fis-gtm-V7.0-005/0000755000032200000250000000000014342376343012356 5ustar librarygtcfis-gtm-V7.0-005/sr_linux/0000755000032200000250000000000014342376327014223 5ustar librarygtcfis-gtm-V7.0-005/sr_linux/arch.gtc0000755000032200000250000000071514342376327015645 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# arch="linux" export arch fis-gtm-V7.0-005/sr_linux/caller_id.c0000644000032200000250000000656014342376327016314 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2008-2020 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. * * * ****************************************************************/ /* caller_id routine called from CRIT_TRACE macro to * return the return address of our caller allowing CRIT_TRACE * (used in various semaphore routines) to determine who was * calling those semaphore routines and for what purpose and * when. This is a system dependent type of operation and is * generally implemented in assembly language. * Presently 32bit linux system has its own implementation in * assembly. Similar implementation will not work on x86_64 * since register rbp is also used as M Frame pointer in its * assembly files. * This particular implementation will work only on Linux x86_64 system * due to its dependency on "backtrace" function call which is not * available on all Unix flovours. */ #include "mdef.h" #include "gtm_stdlib.h" #include "gtm_signal.h" #ifndef __CYGWIN__ #include #endif #include "gt_timer.h" #include "caller_id.h" #define MAX_TRACE_DEPTH 5 /* We need the callers caller of caller_id */ #define RETURN_ADDRESS_DEPTH 2 GBLREF boolean_t blocksig_initialized; GBLREF sigset_t block_sigsent; GBLREF int process_exiting; GBLREF volatile boolean_t timer_in_handler; static boolean_t caller_id_reent = FALSE; /* If ever true, our helper gets a lobotomy */ caddr_t caller_id(unsigned int extra_frames) { void *trace[MAX_TRACE_DEPTH]; int rc, trace_size; sigset_t savemask; /* We cannot let this routine nest itself due to the impolite things that * occur when the exception routines get re-entered so just play dead. */ if (caller_id_reent) return (caddr_t)-1; /* Return 0 if we are already in timer or generic signal signal handler to prevent deadlocks * due to nested mallocs/frees resulting from interrupting in external function calls. */ if (timer_in_handler || process_exiting) return (caddr_t)0; /* When backtrace is processing and a signal occurs, there is the potential for a deadlock -- waiting on a * lock that this process already holds. A work around is to temporarily block signals (SIGINT, SIGQUIT, * SIGTERM, SIGTSTP, SIGCONT, SIGALRM) and then restore them after the backtrace call returns. */ /* It is possible for this routine to be invoked during process startup (as part of err_init()) so * block_sigsent could not be initialized yet. Therefore this does not have an assert(blocksig_initialized) * that similar code in other places (e.g. dollarh.c) has. */ if (blocksig_initialized) SIGPROCMASK(SIG_BLOCK, &block_sigsent, &savemask, rc); caller_id_reent = TRUE; #ifndef __CYGWIN__ trace_size = backtrace(trace, MAX_TRACE_DEPTH); #else /* Cygwin does not support backtrace() */ trace_size = 0; #endif caller_id_reent = FALSE; if (blocksig_initialized) SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc); /* backtrace will return call stack with address.*/ if (RETURN_ADDRESS_DEPTH <= trace_size) return (caddr_t)trace[RETURN_ADDRESS_DEPTH + extra_frames]; else return NULL; } fis-gtm-V7.0-005/sr_linux/gen_threadgbl_asm.cmake0000644000032200000250000000063114342376327020652 0ustar librarygtc file(STRINGS ${gtmthreadgblasmaccess} asmaccesstypes REGEX "^[A-Za-z_]+") foreach(asmaccess ${asmaccesstypes}) string(REGEX REPLACE "^([A-Za-z_]+)[^A-Za-z_].*$" "ggo_\\1" asm "${asmaccess}") file(STRINGS ${gtmthreadgblasmhdr} asmdef REGEX ${asm}) string(REGEX REPLACE "# +define +([A-Za-z_]+) +([0-9]+)" "\\1 = \\2" asmsign "${asmdef}") file(WRITE ${gtmthreadgblasmfile} "${asmsign}\n") endforeach() fis-gtm-V7.0-005/sr_linux/genexport.csh0000755000032200000250000000220714342376327016741 0ustar librarygtc################################################################# # # # 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. # # # ################################################################# ############################################################################################ # # genexport.csh - to generate the linker script *.export to export # all call-in related symbols from libgtmshr.so # Argument # $1 - The pathname of a .exp file that list out all symbols to be exposed # $2 - output verstion script file to be specified with ld --version-script. # # Example output: # { # global: # gtm_ci; # gtm_exit; # gtm_zstatus; # local: # *; # }; ############################################################################################ echo "{" >$2 echo "global:" >>$2 sed 's/\(.*\)/ \1;/g' $1 >>$2 echo "local:" >>$2 echo " *;" >>$2 echo "};" >>$2 fis-gtm-V7.0-005/sr_linux/gtm_env_sp.csh0000755000032200000250000003601114342376327017067 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2021 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. # # # ################################################################# # ########################################################################################## # # gtm_env_sp.csh - environment variable values and aliases specific to Linux # ########################################################################################## # # Once the environment variables have been initialized, be careful not to # re-initialize them for each subshell (i.e., don't undo any explicit changes that # have been made since the last version change). set platform_name = `uname | sed 's/-//g' | tr '[A-Z]' '[a-z]'` set mach_type = `uname -m` ### Sanitize platform_name and match_type # Cygwin adds the Windows version e.g. uname = CYGWIN_NT-5.1 set platform_only = `echo $platform_name | sed 's/_.*//'` # sanitize i386 thru i686 to one option, do not use this to set build optimizations! if ( "linux" == $platform_name ) then set mach_type = `uname -m | sed 's/i[3456]86/ia32/' ` endif # default to 64bit builds when object mode is not set if (!($?OBJECT_MODE)) then setenv OBJECT_MODE 64 endif ### 64bit vs 32bit builds # The only 32 bit targets are cygwin, ia32 and x86_64 with specific settings if ( ( "ia32" == $mach_type ) || ( "cywgin" == $platform_only ) ) then setenv gt_build_type 32 # build 32 bit on x86_64 when $gtm_inc/x86_64.h does not exist with comlist.csh OR when OBJECT_MODE is set to 32 with comlist.mk else if ( "x86_64" == $mach_type && ((! -e $gtm_inc/x86_64.h && "inc" == "${gtm_inc:t}") || "32" == $OBJECT_MODE)) then setenv gt_build_type 32 else setenv gt_build_type 64 setenv gt_ld_m_shl_options "-shared" endif if ( $?gtm_version_change == "1" ) then # Compiler selections: # if !( $?gtm_linux_compiler ) then setenv gtm_linux_compiler gcc endif if ( "ia64" == $mach_type ) then if ( "gcc" != $gtm_linux_compiler ) then if (-r /usr/bin/icc) then setenv gt_cc_compiler "icc -i-static" # name of C compiler endif endif endif # If we are doing a coverage build, use the coverage_cc compiler wrapper if ( $?gcoverage_build == "1" ) then setenv gt_cc_compiler "${gtm_tools}/coverage_cc.sh" # name of C compiler for gcov endif # Archiver definitions: # GNU ar q equals r, S prevents generating the symbol table but # requires ranlib before linking setenv gt_ar_option_create "qSv" # quick, verbose setenv gt_ar_option_update "rv" # replace, verbose setenv gt_ar_option_delete "dv" # delete, verbose setenv gt_ar_use_ranlib "yes" # Assembler definitions: # From before conversion from MASM or get MASM running under DOSEMU or such # setenv gt_as_use_prebuilt "yes" # GNU as if ( "ia64" == $mach_type ) then if ( "icc" == $gtm_linux_compiler ) then setenv gt_cpp_compiler "icc" # name of C preprocessor setenv gt_cpp_options_common "-x assembler-with-cpp" setenv gt_as_assembler "icc" setenv gt_as_options_common "-c -i-static" setenv gt_as_option_optimize "$gt_as_option_optimize -O3" else setenv gt_as_assembler "gcc -c" endif endif if ( "ia64" != $mach_type ) then setenv gt_as_assembler "as" setenv gt_as_option_debug_scan "" if ("s390x" == $mach_type) then setenv gt_as_options_common "-march=z9-109" setenv gt_as_option_debug "--gdwarf-2" else if ( "cygwin" == $platform_only ) then setenv gt_as_options_common "--defsym cygwin=1" setenv gt_as_option_debug "--gdwarf-2" else setenv gt_as_option_debug "--gstabs" setenv gt_as_option_debug_scan "--gdwarf-2 --nocompress-debug-sections" endif if ($?scan_image) setenv gt_as_option_debug "--gdwarf-2 --nocompress-debug-sections" endif # to avoid naming files with .S # smw 1999/12/04 setenv gt_as_options_common "-c -x assembler-with-cpp" setenv gt_as_option_DDEBUG "-defsym DEBUG=1" # C definitions: # generate position independent code setenv gt_cc_shl_fpic "-fPIC" if ( "cygwin" == $platform_only ) then setenv gt_cc_shl_fpic "" endif # setenv gt_cc_options_common "-c -D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64" # For gcc: _BSD_SOURCE for caddr_t, others # _XOPEN_SOURCE=500 should probably define POSIX 199309 and/or # POSIX 199506 but doesnt so... # -fsigned-char for Linux390 but shouldn't hurt x86 # setenv gt_cc_options_common "-c -ansi -D_XOPEN_SOURCE=500 -D_BSD_SOURCE -D_POSIX_C_SOURCE=199506L # setenv gt_cc_options_common "$gt_cc_options_common -D_FILE_OFFSET_BITS=64 -DFULLBLOCKWRITES -fsigned-char" # _GNU_SOURCE includes _XOPEN_SOURCE=400, _BSD_SOURCE, and _POSIX_C_SOURCE-199506L among others # Need _XOPEN_SOURCE=600 for posix_memalign() interface (replaces obsolete memalign) if ( "cygwin" == $platform_only ) then # on Cygwin, -ansi defines __STRICT_ANSI__ which suppresses many prototypes setenv gt_cc_options_common "-c " else setenv gt_cc_options_common "-c -std=c99 -fno-common " endif setenv gt_cc_options_common "$gt_cc_options_common -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 " setenv gt_cc_options_common "$gt_cc_options_common -D_XOPEN_SOURCE=600 -fsigned-char -Wreturn-type -Wpointer-sign " if ( "ia64" != $mach_type ) then if ("$gt_cc_compiler" =~ *icc*) then setenv gt_cc_options_common "$gt_cc_options_common $gt_cc_shl_fpic" else setenv gt_cc_options_common "$gt_cc_options_common $gt_cc_shl_fpic -Wimplicit" endif setenv gt_cc_options_common "$gt_cc_options_common -Wmissing-prototypes -D_LARGEFILE64_SOURCE" endif if ( "cygwin" == $platform_only ) then setenv gt_cc_options_common "$gt_cc_options_common -DNO_SEM_TIME -DNO_SEM_GETPID" endif # 32 bit ICU headers of 32 bit linux are in /emul/ia32-linux/usr/include/ if ( "32" == $gt_build_type ) then if (-d /emul/ia32-linux/usr/include/) setenv gt_cc_option_I "-I/emul/ia32-linux/usr/include/" endif if ( "linux" == $platform_name ) then set lversion=`uname -r` set ltemp_ver=`echo $lversion | sed 's/./& /g'` if ($ltemp_ver[3] == "2" && $ltemp_ver[1] == "2") then setenv gt_cc_options_common "$gt_cc_options_common -DNeedInAddrPort" endif if ( "x86_64" == $mach_type ) then # Add flags for warnings that we want and don't want. set desired_warnings = ( -Wall ) # The following warnings would be desirable, but together can result in megabytes of warning messages. We # should look into how hard they would be to clean up. It is possible that some header changes could # reduce a large number of these. #set desired_warnings = ( $desired_warnings -Wconversion -Wsign-compare ) # # We should also look into how hard these would be to restore. Some of the warnings come from generated # code and macro use, making them harder to deal with. set undesired_warnings = ( -Wunused-result -Wparentheses -Wunused-value -Wunused-variable ) set undesired_warnings = ( $undesired_warnings -Wmaybe-uninitialized -Wchar-subscripts ) set undesired_warnings = ( $undesired_warnings -Wunused-but-set-variable -Wunused-function) if ( { (cc --version |& grep -q -E '4.4.7|4.7.2|4.6.3|SUSE') } ) then set undesired_warnings = ( $undesired_warnings -Wuninitialized ) endif # If our compiler happens not to support --help=warnings, the following will produce junk, but it is # unlikely that the junk will match the warning options of interest, so it shouldn't be a problem. set -f supported_warnings = `cc --help=warnings | & awk '$1 ~ /^-[^=]*$/ {print $1}'` foreach w ($desired_warnings) # Add the warning to a copy of the supported list, discarding duplicates. set -f tmp_warnings = ($supported_warnings $w) if ("$supported_warnings" == "$tmp_warnings") then # Adding the desired warning didn't change the list, so it is supported. setenv gt_cc_options_common "$gt_cc_options_common $w" endif end foreach w ($undesired_warnings) # Add the warning to a copy of the supported list, discarding duplicates. set -f tmp_warnings = ($supported_warnings $w) if ("$supported_warnings" == "$tmp_warnings") then # Adding the desired warning didn't change the list, so it is supported. setenv gt_cc_options_common "$gt_cc_options_common -Wno-${w:s/-W//}" endif end endif endif # TODO: integrate a build option to use these on demand setenv gt_cc_option_DDEBUG_scan "-DBYPASS_MEMCPY_OVERRIDE -DSTATIC_ANALYSIS" if ($?scan_image) setenv gt_cc_option_DDEBUG "$gt_cc_option_DDEBUG $gt_cc_option_DDEBUG_scan" # -fno-defer-pop to prevent problems with assembly/generated code with optimization # -fno-strict-aliasing since we don't comply with the rules # -ffloat-store for consistent results avoiding rounding differences # -fno-omit-frame-pointer so %rbp always gets set up (required by caller_id()). Default changed in gcc 4.6. if ( "ia64" != $mach_type ) then setenv gt_cc_option_optimize "-O2 -fno-defer-pop -fno-strict-aliasing -ffloat-store -fno-omit-frame-pointer" if ( "32" == $gt_build_type ) then # applies to 32bit x86_64, ia32 and cygwin # Compile 32-bit x86 GT.M using 586 instruction set rather than 686 as the new Intel Quark # low power system-on-a-chip uses the 586 instruction set rather than the 686 instruction set setenv gt_cc_option_optimize "$gt_cc_option_optimize -march=i586" endif endif # Debugging options: setenv gt_cc_option_debug "-g " # Override the default debug settings for third party source code scans WARNING: these builds are HUGE # -g3 generate debugging information including macros # -O0 drop optimizations for alternate debuggers # -fno-builtin avoid using compiler built-in functions for alternate debuggers # -gdwarf-2 DWARF version 2 debugging (version 4, was release in 2010) setenv gt_cc_option_debug_scan "-g3 -gdwarf-2 -O0 -fno-builtin " if ( "cygwin" == $platform_only ) then setenv gt_cc_option_debug "$gt_cc_option_debug -gdwarf-2 -fno-inline -fno-inline-functions" setenv gt_cc_option_debug_scan "$gt_cc_option_debug_scan -fno-inline -fno-inline-functions" endif if ($?scan_image) setenv gt_cc_option_debug "$gt_cc_option_debug_scan" # Linker definitions: setenv gt_ld_linker "$gt_cc_compiler" # redefine to use new C compiler definition # -M generate link map onto standard output setenv gt_ld_options_common "-Wl,-M" setenv gt_ld_options_gtmshr "-Wl,-u,accumulate -Wl,-u,is_big_endian -Wl,-u,to_ulong" setenv gt_ld_options_gtmshr "$gt_ld_options_gtmshr -Wl,-u,gtm_filename_to_id -Wl,--version-script,gtmshr_symbols.export" setenv gt_ld_options_all_exe "-rdynamic -Wl,-u,gtm_filename_to_id -Wl,-u,gtm_zstatus" setenv gt_ld_options_all_exe "$gt_ld_options_all_exe -Wl,--version-script,gtmexe_symbols.export" # optimize for all 64bit platforms # # -lrt doesn't work to pull in semaphores with GCC 4.6, so use -lpthread. # Add -lc in front of -lpthread to avoid linking in thread-safe versions # of libc routines from libpthread. setenv gt_ld_syslibs " -lelf -lncurses -lm -ldl -lc -lpthread -lrt" if ( 32 == $gt_build_type ) then # 32bit x86_64 and ia32 - decided at the beginning of the file setenv gt_ld_syslibs " -lncurses -lm -ldl -lc -lpthread -lrt" endif if ( "cygwin" == $platform_only ) then setenv gt_ld_syslibs "-lncurses -lm -lcrypt" endif # -lrt for async I/O in mupip recover/rollback setenv gt_ld_aio_syslib "-lrt" if ( "cygwin" == $platform_only ) then setenv gt_ld_aio_syslib "" endif # Shared library definition overrides: setenv gt_cc_shl_options "-c $gt_cc_shl_fpic" setenv gt_ld_shl_linker "cc" setenv gt_ld_shl_options "-shared" # If we are trying to force a 32 bit build on a 64 bit x86 machine, then we need to explicitly specify a 32 bit # over-ride option. if ( "x86_64" == $mach_type && "32" == $gt_build_type ) then setenv gt_cc_options_common "$gt_cc_options_common -m32" setenv gt_ld_options_gtmshr "$gt_ld_options_gtmshr -m32" setenv gt_cc_shl_options "$gt_cc_shl_options -m32" setenv gt_ld_shl_options "$gt_ld_shl_options -m32" setenv gt_ld_options_common "$gt_ld_options_common -m32" endif # need to re-define these in terms of new gt_ld_options_common: setenv gt_ld_options_bta "$gt_ld_options_common" setenv gt_ld_options_dbg "$gt_ld_options_common" setenv gt_ld_options_pro "$gt_ld_options_common" setenv gt_ld_shl_suffix ".so" # lint definition overrides # setenv gt_lint_linter "" setenv gt_lint_options_library "-x" setenv gt_lint_options_common "" endif # PBO(Profile based optimization) settings for Linux-IA64, intel compiler. set gtm_build_image = `basename $gtm_exe` if ( "ia64" == $mach_type && "pro" == $gtm_build_image && "icc" == $gtm_linux_compiler) then setenv gt_cc_option_optimize "-O3" if ($?gtm_pbo_option) then @ need_mkdir = 0 if !($?gtm_pbo_db) then @ need_mkdir = 1 setenv gtm_pbo_db "$gtm_log/pbo" else if !(-e $gtm_pbo_db) then @ need_mkdir = 1 endif if (1 == $need_mkdir) then mkdir -p $gtm_pbo_db chmod 775 $gtm_pbo_db # Others need ability to write to the pbo files endif if ($gtm_pbo_option == "collect") then setenv gt_cc_option_optimize "-prof-gen -prof-dir $gtm_pbo_db" setenv gt_ld_options_pro "$gt_ld_options_pro -prof-gen -prof-dir $gtm_pbo_db" setenv gt_as_option_optimize "$gt_cc_option_optimize" else if ($gtm_pbo_option == "use") then setenv gt_cc_option_optimize "-O3 -prof-use -prof-dir $gtm_pbo_db" setenv gt_ld_options_pro "$gt_ld_options_pro -prof-dir $gtm_pbo_db" endif endif endif # Assembler definitions: # Note: we need to specify the assembler output file name or it will write it to the source directory. if ( "ia64" == $mach_type ) then alias gt_as_bta 'gt_as $gt_as_option_debug $gt_as_option_nooptimize' alias gt_as_dbg 'gt_as $gt_as_option_DDEBUG $gt_as_option_debug $gt_as_option_nooptimize' alias gt_as_pro 'gt_as $gt_as_option_optimize ' endif if ( "s390x" == $mach_type) then setenv gt_asm_arch_size "-m64" else if ( "64" == $gt_build_type) then setenv gt_asm_arch_size "--64" else setenv gt_asm_arch_size "--32" endif if ( "ia64" != $mach_type ) then alias gt_as_bta \ 'gt_as $gt_as_option_debug $gt_as_option_nooptimize $gt_asm_arch_size -o `basename \!:1 .s`.o \!:1' # If the value of the alias variable extends the 132 character limit, use a temporary variable # (like gtm_as_dbg_var below) and use that in the alias value to stick to the the coding standard. set gt_as_dbg_var = "$gt_as_option_DDEBUG $gt_as_option_debug $gt_as_option_nooptimize $gt_asm_arch_size" alias gt_as_dbg 'gt_as $gt_as_dbg_var -o `basename \!:1 .s`.o \!:1' alias gt_as_pro 'gt_as $gt_as_option_optimize $gt_asm_arch_size -o `basename \!:1 .s`.o \!:1' endif fis-gtm-V7.0-005/sr_linux/gtm_getenv.c0000644000032200000250000000223414342376327016527 0ustar librarygtc/**************************************************************** * * * Copyright 2007, 2014 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. * * * ****************************************************************/ #ifdef __CYGWIN__ /* In Cygwin 1.54-2 and gdb 6.5.50, the Cygwin environment variables * * are not passed to the program being debugged properly. getenv() * * only sees the Windows variables. * */ #include "gtm_common_defs.h" #include "gtm_unistd.h" #include "gtm_stdlib.h" #include "gtm_string.h" extern char **environ; /* array of pointers, last has NULL */ char *gtm_getenv(char *varname) { char *eq, **p; size_t len; if (NULL == environ || NULL == varname) return NULL; len = strlen(varname); for (p = environ; *p; p++) { eq = strchr(*p, '='); if (eq && (*p + len) == eq) { if (!strncasecmp(varname, *p, len)) /* environ names are upcased */ return (eq + 1); } } return NULL; } #endif fis-gtm-V7.0-005/sr_linux/gtm_threadgbl_deftypes_asm.si0000644000032200000250000000116014342376327022124 0ustar librarygtc################################################################# # # # Copyright (c) 2015 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. # # # ################################################################# .ifdef DEBUG .include "gtm_threadgbl_deftypes_asm_dbg.si" .else .include "gtm_threadgbl_deftypes_asm_pro.si" .endif fis-gtm-V7.0-005/sr_linux/inst_flush.c0000755000032200000250000000116514342376327016553 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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. * * * ****************************************************************/ /* STUB FILE only for non-ia64 versions */ #include "mdef.h" #include "inst_flush.h" void inst_flush(void *start, int4 len) { IA64_ONLY(cacheflush(start, len, 0 )); } fis-gtm-V7.0-005/sr_linux/platform.cmake0000644000032200000250000000727114342376327017060 0ustar librarygtc################################################################# # # # Copyright (c) 2013-2020 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. # # # ################################################################# set(srdir "sr_linux") if("${CMAKE_SIZEOF_VOID_P}" EQUAL 4) set(arch "x86") set(bits 32) set(FIND_LIBRARY_USE_LIB64_PATHS FALSE) # Set arch to i586 in order to compile for Galileo set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=i586") set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -Wa,-march=i586") # (Sam): I am not really sure if we need this at all. The Linker has # no issues finding the symbols. If we add _, it now has trouble. # For Cygwin, we need to change the assembly symbols to start with _. # See http://www.drpaulcarter.com/pcasm/faq.php, esp. the examples in the zip files. # UPDATE: Now I figure out that this is needed for 32 bit CYGWIN ONLY. 64 bit keeps the same symbols! if(CYGWIN) list(APPEND CMAKE_ASM_COMPILE_OBJECT "objcopy --prefix-symbols=_ ") endif() else() set(arch "x86_64") set(bits 64) endif() # Platform directories list(APPEND gt_src_list sr_linux) if(${bits} EQUAL 32) list(APPEND gt_src_list sr_i386 sr_x86_regs sr_unix_nsb) else() list(APPEND gt_src_list sr_x86_64 sr_x86_regs) set(gen_xfer_desc 1) endif() # Assembler set(CMAKE_INCLUDE_FLAG_ASM "-Wa,-I") # gcc -I does not make it to "as" # Compiler if(${CYGWIN}) # (VEN/SMH): Looks like we need to add the defsym to tell the assembler to define 'cygwin' set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -Wa,--defsym,cygwin=1") else() # Cygwin must have -ansi undefined (it adds __STRICT_ANSI__ which undefines some important prototypes like fdopen()) # See http://stackoverflow.com/questions/21689124/mkstemp-and-fdopen-in-cygwin-1-7-28 # Cygwin warns if you add -fPIC that the compiled code is already position # independent. So don't add -fPIC set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -fPIC ") endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsigned-char -Wmissing-prototypes -Wreturn-type -Wpointer-sign -fno-omit-frame-pointer") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wimplicit -Wall -Wno-parentheses -Wno-unused-value -Wno-unused-function") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-variable -Wno-char-subscripts") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-but-set-variable -fno-builtin") if(CMAKE_C_COMPILER_VERSION VERSION_GREATER 4.8) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-result -Wno-maybe-uninitialized") endif() add_definitions( #-DNOLIBGTMSHR #gt_cc_option_DBTABLD=-DNOLIBGTMSHR -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_XOPEN_SOURCE=600 -D_LARGEFILE64_SOURCE ) # Linker set(gtm_link "-Wl,-u,gtm_filename_to_id -Wl,-u,gtm_zstatus -Wl,--version-script,\"${GTM_BINARY_DIR}/gtmexe_symbols.export\"") set(gtm_dep "${GTM_BINARY_DIR}/gtmexe_symbols.export") set(libgtmshr_link "-Wl,-u,gtm_ci -Wl,-u,gtm_filename_to_id -Wl,-u,gtm_is_main_thread") set(libgtmshr_link "${libgtmshr_link} -Wl,-u,accumulate -Wl,-u,is_big_endian -Wl,-u,to_ulong") set(libgtmshr_link "${libgtmshr_link} -Wl,--version-script,\"${GTM_BINARY_DIR}/gtmshr_symbols.export\"") set(libgtmshr_dep "${GTM_BINARY_DIR}/gtmexe_symbols.export") if(${bits} EQUAL 32) set(libmumpslibs "-lncurses -lm -ldl -lc -lpthread -lrt") else() set(libmumpslibs "-lelf -lncurses -lm -ldl -lc -lpthread -lrt") endif() if(ENABLE_PROFILING) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fprofile-arcs -ftest-coverage") endif() fis-gtm-V7.0-005/sr_linux/release_name.h0000644000032200000250000000177314342376327017024 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GTM_RELEASE_NAME #ifdef __CYGWIN__ #define GTM_RELEASE_NAME "GT.M V7.0-005 CYGWIN x86" #elif defined(__ia64) #define GTM_RELEASE_NAME "GT.M V7.0-005 Linux IA64" #elif defined(__x86_64__) #define GTM_RELEASE_NAME "GT.M V7.0-005 Linux x86_64" #elif defined(__s390__) #define GTM_RELEASE_NAME "GT.M V7.0-005 Linux S390X" #else #define GTM_RELEASE_NAME "GT.M V7.0-005 Linux x86" #endif #endif #define GTM_PRODUCT "GT.M" #define GTM_VERSION "V7.0" #define GTM_RELEASE_STAMP "20221122 16:34" fis-gtm-V7.0-005/sr_i386/0000755000032200000250000000000014342376333013552 5ustar librarygtcfis-gtm-V7.0-005/sr_i386/mval2bool.s0000755000032200000250000000161214342376327015641 0ustar librarygtc################################################################# # # # Copyright 2001, 2008 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. # # # ################################################################# # PAGE ,132 .title mval2bool.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "mval_def.si" .sbttl mval2bool # PAGE + # -------------------------------- # mval2bool.s # Convert mval to bool # -------------------------------- # edx - src. mval .text .extern s2n .extern underr # PUBLIC mval2bool ENTRY mval2bool mv_force_defined %edx, l1 pushl %edx mv_force_num %edx, skip_conv popl %edx cmpl $0,mval_l_m1(%edx) ret # mval2bool ENDP # END fis-gtm-V7.0-005/sr_i386/mval2mint.s0000755000032200000250000000171414342376327015660 0ustar librarygtc################################################################# # # # Copyright 2001, 2008 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. # # # ################################################################# # PAGE ,132 .title mval2mint.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "mval_def.si" .sbttl mval2mint # PAGE + # -------------------------------- # mval2mint.s # Convert mval to int # -------------------------------- # edx - source mval # eax - destination mval .text .extern mval2i .extern s2n .extern underr # PUBLIC mval2mint ENTRY mval2mint mv_force_defined %edx, l1 pushl %edx # preserve src + push it as arg mv_force_num %edx, skip_conv call mval2i addl $4,%esp ret # mval2mint ENDP # END fis-gtm-V7.0-005/sr_i386/mval2num.s0000755000032200000250000000146414342376327015512 0ustar librarygtc################################################################# # # # Copyright 2001, 2008 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. # # # ################################################################# # PAGE ,132 .title mval2num.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "mval_def.si" .sbttl mval2num # PAGE + .text .extern n2s .extern s2n .extern underr # PUBLIC mval2num ENTRY mval2num mv_force_defined %edx, l0 pushl %edx # save in case call s2n mv_force_num %edx, l1 popl %edx mv_force_str_if_num_approx %edx, l2 ret # mval2num ENDP # END fis-gtm-V7.0-005/sr_i386/mval_def.si0000755000032200000250000001440514342376327015676 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2022 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. # # # ################################################################# .sbttl mval_def.si # PAGE + # ------------------------------------------- # mval_def.si # # This is an include file for # SCO Unix 80386 masm assembler containing # the macros for mval-related assembly routines. # ------------------------------------------- mval_v_nm = 0 mval_v_int = 1 mval_v_str = 2 mval_v_num_approx = 3 mval_v_canonical = 4 mval_v_sym = 5 mval_v_sublit = 6 mval_v_retarg = 7 mval_v_utflen = 8 mval_v_aliascont = 9 mval_m_nm = 0x001 mval_m_int_without_nm = 0x002 mval_m_int = 0x003 mval_m_str = 0x004 mval_m_num_approx = 0x008 mval_m_canonical = 0x010 mval_m_sym = 0x020 mval_m_sublit = 0x040 mval_m_retarg = 0x080 mval_m_utflen = 0x100 mval_m_aliascont = 0x200 # # Length of mval in bytes # mval_byte_len = 32 # # Offsets of type, exp, strlen, stradd, num in mval structure # mval_w_mvtype = 0 mval_b_exp = 2 mval_l_strlen = 24 mval_a_straddr = 28 # # Address offset of number in mval # mvalnm_offs = 12 mval_l_m0 = 12 mval_l_m1 = 16 mval_esign_mask = 0x080 MV_BIAS = 1000 # 10**3 MANT_LO = 100000000 # 10**8 MANT_HI = 1000000000 # 10**9 INT_HI = 1000000 # 10**6 # Stringpool structure offsets base = 0 free = 4 top = 8 # mvals passed to these macros must be registers .sbttl mval_def.si mv_force_defined # --------------------------------------- # mv_force_defined(mval, label) # --------------------------------------- .macro mv_force_defined mval, label testw $(mval_m_str+mval_m_nm),mval_w_mvtype(\mval) jne \label pushl \mval call underr addl $4,%esp movl %eax, \mval \label: .endm .sbttl mval_def.si mv_force_defined_overwrite # --------------------------------------- # mv_force_defined_overwrite(mval, label) # --------------------------------------- .macro mv_force_defined_overwrite mval, label testw $(mval_m_str+mval_m_nm),mval_w_mvtype(\mval) jne \label pushl \mval call underr_overwrite addl $4,%esp \label: .endm .sbttl mval_def.si mv_force_str # --------------------------------------- # mv_force_str(mval, label) # --------------------------------------- .macro mv_force_str mval, label testw $mval_m_str,mval_w_mvtype(\mval) jne \label pushl \mval call n2s addl $4,%esp \label: .endm .sbttl mval_def.si mv_force_num # --------------------------------------- # mv_force_num(mval, label) # --------------------------------------- .macro mv_force_num mval, label testw $mval_m_nm,mval_w_mvtype(\mval) jne \label pushl \mval call s2n addl $4,%esp \label: .endm .sbttl mval_def.si mv_force_str_if_num_approx # --------------------------------------- # mv_force_str_if_num_approx(mval, label) # --------------------------------------- .macro mv_force_str_if_num_approx mval, label testw $mval_m_num_approx,mval_w_mvtype(\mval) je \label pushl \mval call n2s addl $4,%esp \label: .endm .sbttl mval_def.si mv_i2mval # --------------------------------------- # mv_i2mval(int, mval) # --------------------------------------- .macro mv_i2mval int, mval movw $mval_m_int,mval_w_mvtype(\mval) movl \int,%eax imull $MV_BIAS,%eax,%eax movl %eax,mval_l_m1(\mval) .endm .sbttl mval_def.si mv_if_string # --------------------------------------- # mv_if_string(mval,label) # --------------------------------------- .macro mv_if_string mval, label testw $mval_m_str,mval_w_mvtype(\mval) jne \label .endm .sbttl mval_def.si mv_if_number # --------------------------------------- # mv_if_number(mval,label) # --------------------------------------- .macro mv_if_number mval, label testw $mval_m_nm,mval_w_mvtype(\mval) jne \label .endm .sbttl mval_def.si mv_if_int # --------------------------------------- # mv_if_int(mval,label) # --------------------------------------- .macro mv_if_int mval, label testw $mval_m_int_without_nm,mval_w_mvtype(\mval) jne \label .endm .sbttl mval_def.si mv_if_notstring # --------------------------------------- # mv_if_notstring(mval,label) # --------------------------------------- .macro mv_if_notstring mval, label testw $mval_m_str,mval_w_mvtype(\mval) je \label .endm .sbttl mval_def.si mv_if_notnumber # --------------------------------------- # mv_if_notnumber(mval,label) # --------------------------------------- .macro mv_if_notnumber mval, label testw $mval_m_nm,mval_w_mvtype(\mval) je \label .endm .sbttl mval_def.si mv_if_notint # --------------------------------------- # mv_if_notint(mval,label) # --------------------------------------- .macro mv_if_notint mval, label testw $mval_m_int_without_nm,mval_w_mvtype(\mval) je \label .endm .sbttl mval_def.si mv_if_defined # --------------------------------------- # mv_if_defined(mval,label) # --------------------------------------- .macro mv_if_defined mval, label testw $(mval_m_str+mval_m_nm),mval_w_mvtype(\mval) jne \label .endm .sbttl mval_def.si mv_if_notdefined # --------------------------------------- # mv_if_notdefined(mval,label) # --------------------------------------- .macro mv_if_notdefined mval, label testw $(mval_m_str+mval_m_nm),mval_w_mvtype(\mval) je \label .endm .sbttl mval_def.si mv_if_canonical # ------------------------------------------------------------- # WARNING: # Following macro needs to be supplied with 2 extra labels that # are used by local branch instructions, tmp_label1 and tmp_label2 # ------------------------------------------------------------- # mv_if_canonical(mval,mainlabel, tmp_label1, tmp_label2) # ------------------------------------------------------------- .macro mv_if_canonical mval, mainlabel, tmp_label1, tmp_label2 testw $mval_m_nm,mval_w_mvtype(\mval) je \tmp_label1 testw $mval_m_num_approx,mval_w_mvtype(\mval) jne \tmp_label2 jmp \mainlabel \tmp_label1: pushl \mval call val_iscan addl $4,%esp cmpl $0,%eax jne \mainlabel \tmp_label2: .endm fis-gtm-V7.0-005/sr_i386/make_dmode.c0000644000032200000250000000521014342376333016001 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "gtm_string.h" #include "error.h" #include #include "op.h" #include "i386.h" #include "inst_flush.h" #include "dm_setup.h" #include "gtm_text_alloc.h" #define CALL_SIZE 5 #define CODE_SIZE 3*CALL_SIZE #define CODE_LINES 3 rhdtyp *make_dmode(void) { rhdtyp *base_address; lab_tabent *lbl; int *lnr; unsigned char *code; /* dummy code + label entry + line entries */ base_address = (rhdtyp *)GTM_TEXT_ALLOC(SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent) + CODE_LINES * SIZEOF(int4)); memset(base_address,0,SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent) + CODE_LINES*SIZEOF(int4)); base_address->routine_name.len = STR_LIT_LEN(GTM_DMOD); base_address->routine_name.addr = GTM_DMOD; base_address->ptext_ptr = SIZEOF(rhdtyp); base_address->vartab_ptr = base_address->labtab_ptr = SIZEOF(rhdtyp) + CODE_SIZE; /* hdr + code */ base_address->lnrtab_ptr = SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent); base_address->labtab_len = 1; base_address->lnrtab_len = CODE_LINES; code = (unsigned char *) base_address + base_address->ptext_ptr; *code++ = I386_INS_CALL_Jv; *((int4 *)code) = (int4)((unsigned char *)dm_setup - (code + SIZEOF(int4))); code += SIZEOF(int4); *code++ = I386_INS_CALL_Jv; /* this should be a CALL to maintain uniformity between transfer to mum_tstart from baseframe and transfers to mum_tstart from error processing (MUM_TSTART marco in mdb_condition_handler) */ *((int4 *)code) = (int4)((unsigned char *)mum_tstart - (code + SIZEOF(int4))); code += SIZEOF(int4); *code++ = I386_INS_JMP_Jv; *((int4 *)code) = (int4)((unsigned char *)opp_ret - (code + SIZEOF(int4))); code += SIZEOF(int4); lbl = (lab_tabent *)((int) base_address + base_address->labtab_ptr); lbl->lab_ln_ptr = base_address->lnrtab_ptr; lnr = (int *)((int)base_address + base_address->lnrtab_ptr); *lnr++ = base_address->ptext_ptr; *lnr++ = base_address->ptext_ptr; *lnr++ = base_address->ptext_ptr + 2 * CALL_SIZE; assert(code - ((unsigned char *)base_address + base_address->ptext_ptr) == CODE_SIZE); zlput_rname(base_address); inst_flush(base_address, SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent) + CODE_LINES * SIZEOF(int4)); return base_address; } fis-gtm-V7.0-005/sr_i386/op_bkpt.s0000755000032200000250000001073514342376327015410 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2019 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. # # # ################################################################# # PAGE ,132 .title op_bkpt.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_zstepret # PAGE + .DATA .extern frame_pointer .extern zstep_level .text .extern gtm_fetch .extern op_retarg .extern op_zbreak .extern op_zst_break .extern op_zst_over .extern op_zstepret .extern opp_ret # PUBLIC opp_zstepret ENTRY opp_zstepret movl frame_pointer,%eax movw msf_typ_off(%eax),%dx testw $1,%dx je l1 movl zstep_level,%edx cmpl %eax, %edx jg l1 call op_zstepret l1: jmp opp_ret # opp_zstepret ENDP # PUBLIC opp_zstepretarg ENTRY opp_zstepretarg pushl %eax pushl %edx movl frame_pointer,%eax movw msf_typ_off(%eax),%dx testw $1,%dx je l2 movl zstep_level,%edx cmpl %eax, %edx jg l2 call op_zstepret l2: popl %edx popl %eax jmp op_retarg # opp_zstepretarg ENDP # PUBLIC op_zbfetch ENTRY op_zbfetch movl frame_pointer,%edx popl msf_mpc_off(%edx) call gtm_fetch popl %eax # lea esp, [esp][eax*4] leal (%esp,%eax,4),%esp pushl frame_pointer call op_zbreak addl $4,%esp getframe ret # op_zbfetch ENDP # PUBLIC op_zbstart ENTRY op_zbstart movl frame_pointer,%edx popl msf_mpc_off(%edx) pushl %edx call op_zbreak addl $4,%esp getframe ret # op_zbstart ENDP # PUBLIC op_zstepfetch ENTRY op_zstepfetch movl frame_pointer,%edx popl msf_mpc_off(%edx) call gtm_fetch popl %eax # lea esp, [esp][eax*4] leal (%esp,%eax,4),%esp call op_zst_break getframe ret # op_zstepfetch ENDP # PUBLIC op_zstepstart ENTRY op_zstepstart movl frame_pointer,%edx popl msf_mpc_off(%edx) call op_zst_break getframe ret # op_zstepstart ENDP # PUBLIC op_zstzbfetch ENTRY op_zstzbfetch movl frame_pointer,%edx popl msf_mpc_off(%edx) call gtm_fetch popl %eax # lea esp, [esp][eax*4] leal (%esp,%eax,4),%esp pushl frame_pointer call op_zbreak addl $4,%esp call op_zst_break getframe ret # op_zstzbfetch ENDP # PUBLIC op_zstzbstart ENTRY op_zstzbstart movl frame_pointer,%edx popl msf_mpc_off(%edx) pushl %edx call op_zbreak addl $4,%esp call op_zst_break getframe ret # op_zstzbstart ENDP # PUBLIC op_zstzb_fet_over ENTRY op_zstzb_fet_over movl frame_pointer,%edx popl msf_mpc_off(%edx) call gtm_fetch popl %eax # lea esp, [esp][eax*4] leal (%esp,%eax,4),%esp pushl frame_pointer call op_zbreak addl $4,%esp movl zstep_level,%edx cmpl frame_pointer,%edx jle l3 cmpl $0,%eax jne l5 jmp l4 l3: call op_zst_break l4: getframe ret l5: call op_zst_over movl frame_pointer,%edx pushl msf_mpc_off(%edx) ret # op_zstzb_fet_over ENDP # PUBLIC op_zstzb_st_over ENTRY op_zstzb_st_over movl frame_pointer,%edx popl msf_mpc_off(%edx) pushl %edx call op_zbreak addl $4,%esp movl zstep_level,%edx cmpl frame_pointer,%edx jle l6 cmpl $0,%eax jne l8 jmp l7 l6: call op_zst_break l7: getframe ret l8: call op_zst_over movl frame_pointer,%edx pushl msf_mpc_off(%edx) ret # op_zstzb_st_over ENDP # PUBLIC op_zst_fet_over ENTRY op_zst_fet_over movl frame_pointer,%edx popl msf_mpc_off(%edx) call gtm_fetch popl %eax # lea esp, [esp][eax*4] leal (%esp,%eax,4),%esp movl zstep_level,%edx cmpl frame_pointer,%edx jg l9 call op_zst_break getframe ret l9: call op_zst_over movl frame_pointer,%edx pushl msf_mpc_off(%edx) ret # op_zst_fet_over ENDP # PUBLIC op_zst_st_over ENTRY op_zst_st_over movl frame_pointer,%eax popl msf_mpc_off(%eax) movl zstep_level,%edx cmpl %eax,%edx jg l10 call op_zst_break getframe ret l10: call op_zst_over movl frame_pointer,%edx pushl msf_mpc_off(%edx) ret # op_zst_st_over ENDP # PUBLIC opp_zst_over_ret ENTRY opp_zst_over_ret movl frame_pointer,%eax movl zstep_level,%edx movl msf_old_frame_off(%eax),%eax cmpl %eax,%edx jg l11 call op_zstepret l11: jmp opp_ret # opp_zst_over_ret ENDP # PUBLIC opp_zst_over_retarg ENTRY opp_zst_over_retarg pushl %eax pushl %edx movl frame_pointer,%eax movw msf_typ_off(%eax),%dx testw $1,%dx je l12 movl zstep_level,%edx movl msf_old_frame_off(%eax),%eax cmpl %eax,%edx jg l12 call op_zstepret l12: popl %edx popl %eax jmp op_retarg # opp_zst_over_retarg ENDP # END fis-gtm-V7.0-005/sr_i386/op_call.s0000755000032200000250000000201614342376327015354 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title op_call.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_callb # PAGE + .DATA .extern frame_pointer .text .extern copy_stack_frame # PUBLIC op_callb ENTRY op_callb movl frame_pointer,%edx movl (%esp),%eax movl %eax,msf_mpc_off(%edx) addl $2,msf_mpc_off(%edx) call copy_stack_frame ret # op_callb ENDP .sbttl op_callw, op_calll # PUBLIC op_callw, op_calll ENTRY op_calll ENTRY op_callw movl frame_pointer,%edx movl (%esp),%eax movl %eax,msf_mpc_off(%edx) addl $5,msf_mpc_off(%edx) call copy_stack_frame ret # op_calll ENDP # END fis-gtm-V7.0-005/sr_i386/op_callsp.s0000755000032200000250000000250114342376327015716 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2015 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 "linkage.si" .include "g_msf.si" .data .extern dollar_truth .extern frame_pointer .text .extern exfun_frame .extern push_tval # # Note this routine calls exfun_frame() instead of copy_stack_frame() because this routine needs to provide a # separate set of compiler temps for use by the new frame. Particularly when it called on same line with FOR. # ENTRY op_callspb movl frame_pointer,%edx movl (%esp),%eax movl %eax,msf_mpc_off(%edx) addl $2,msf_mpc_off(%edx) # store pc in MUMPS stack frame doit: call exfun_frame pushl dollar_truth call push_tval addl $4,%esp movl frame_pointer,%edx movl msf_temps_ptr_off(%edx),%edi ret ENTRY op_callspw ENTRY op_callspl movl frame_pointer,%edx movl (%esp),%eax movl %eax,msf_mpc_off(%edx) addl $5,msf_mpc_off(%edx) # store pc in MUMPS stack frame jmp doit fis-gtm-V7.0-005/sr_i386/op_contain.s0000755000032200000250000000303314342376327016074 0ustar librarygtc################################################################# # # # Copyright 2001, 2009 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. # # # ################################################################# # PAGE ,132 .title op_contain.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "mval_def.si" .sbttl op_contain # PAGE + .text sav_eax = -4 sav_edx = -8 .extern matchc .extern n2s .extern underr # PUBLIC op_contain ENTRY op_contain enter $8, $0 pushl %edi pushl %esi pushl %ebx movl %edx, sav_edx(%ebp) mv_force_defined %eax, l1 movl %eax, sav_eax(%ebp) mv_force_str %eax, l2 movl sav_edx(%ebp), %edx mv_force_defined %edx, l3 movl %edx, sav_edx(%ebp) mv_force_str %edx, l4 pushl $1 # pieces argument but have to pass its addr movl %esp, %edx subl $4, %esp # returned value movl %esp, %eax pushl %edx # parm 6 pushl %eax # parm 5 movl sav_eax(%ebp), %eax movl sav_edx(%ebp), %edx pushl mval_a_straddr(%eax) # parm 4 movl mval_l_strlen(%eax), %eax pushl %eax # parm 3 pushl mval_a_straddr(%edx) # parm 2 movl mval_l_strlen(%edx), %eax pushl %eax # parm 1 call matchc leal 24(%esp), %esp # remove args popl %eax # return value popl %edx # updated pieces value (ignored) cmpl $0, %eax popl %ebx popl %esi popl %edi leave ret # op_contain ENDP # END fis-gtm-V7.0-005/sr_i386/op_currtn.s0000755000032200000250000000200614342376327015755 0ustar librarygtc################################################################# # # # Copyright 2001, 2006 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. # # # ################################################################# # PAGE ,132 .title op_currtn.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "mval_def.si" .INCLUDE "g_msf.si" .sbttl op_currtn # PAGE + .DATA .extern frame_pointer .text # PUBLIC op_currtn ENTRY op_currtn movw $mval_m_str,mval_w_mvtype(%edx) movl frame_pointer,%eax movl msf_rvector_off(%eax),%eax pushl mrt_rtn_len(%eax) popl mval_l_strlen(%edx) # %edx->str.len = frame_pointer->rvector->routine_name.len movl mrt_rtn_addr(%eax),%eax movl %eax,mval_a_straddr(%edx) # %edx->str.addr = frame_pointer->rvector->routine_name.addr ret # op_currtn ENDP # END fis-gtm-V7.0-005/sr_i386/op_equ.s0000755000032200000250000000124014342376327015231 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title op_equ.s .sbttl op_equ .include "linkage.si" # .386 # .MODEL FLAT, C .text .extern is_equ # PUBLIC op_equ ENTRY op_equ pushl %edx pushl %eax call is_equ addl $8,%esp cmpl $0,%eax ret # op_equ ENDP # END fis-gtm-V7.0-005/sr_i386/op_equnul.s0000755000032200000250000000204514342376327015754 0ustar librarygtc################################################################# # # # Copyright 2001, 2006 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. # # # ################################################################# # PAGE ,132 .title op_equnul.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "mval_def.si" .sbttl op_equnul # PAGE + .DATA .extern undef_inhibit .text .extern underr # PUBLIC op_equnul ENTRY op_equnul mv_if_notdefined %eax, l3 testw $mval_m_str,mval_w_mvtype(%eax) je l2 cmpl $0,mval_l_strlen(%eax) jne l2 l1: movl $1,%eax cmpl $0,%eax ret l2: movl $0,%eax cmpl $0,%eax ret l3: cmpb $0,undef_inhibit # not defined jne l1 # if undef_inhibit, then all undefined # values are equal to null string pushl %eax # really undef call underr addl $4,%esp ret # op_equnul ENDP # END fis-gtm-V7.0-005/sr_i386/op_exfun.s0000755000032200000250000000414214342376327015570 0ustar librarygtc################################################################# # # # Copyright 2001, 2012 Fidelity Information Services, Inc # # # # This source code contains the intellectual property # # of its copyright holder(s), and is made available # # under a license. If you do not know the terms of # # the license, please stop and do not read further. # # # ################################################################# # PAGE ,132 .title op_exfun.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_exfun # PAGE + # call op_exfun with the following stack: # # return PC # ret_value address # mask # actualcnt # actual1 address # actual2 address # ... .DATA .extern ERR_GTMCHECK .extern dollar_truth .extern frame_pointer .text .extern exfun_frame .extern push_parm .extern rts_error JMP_Jb = 0x0eb JMP_Jv = 0x0e9 actual1 = 20 act_cnt = 16 mask_arg = 12 ret_val = 8 rtn_pc = 4 sav_esi = -4 sav_ebx = -8 sav_msf = -12 # PUBLIC op_exfun ENTRY op_exfun pushl %ebp movl %esp,%ebp pushl %esi pushl %ebx leal sav_msf(%ebp),%esp # establish space for locals movl act_cnt(%ebp),%eax addl $3,%eax negl %eax leal (%esp,%eax,4),%esp # establish space for temps movl frame_pointer,%edx movl rtn_pc(%ebp),%eax cmpb $JMP_Jv,(%eax) je long cmpb $JMP_Jb,(%eax) je byte_off error: pushl ERR_GTMCHECK pushl $1 call rts_error addl $8,%esp jmp retlab byte_off: movl %eax,msf_mpc_off(%edx) addl $2,msf_mpc_off(%edx) jmp cont long: movl %eax,msf_mpc_off(%edx) addl $5,msf_mpc_off(%edx) cont: call exfun_frame leal ret_val(%ebp),%esi movl %esp,%edi movl act_cnt(%ebp),%eax movl %eax,%ecx addl $3,%ecx REP movsl movl dollar_truth,%ecx andl $1,%ecx pushl %ecx # push $T addl $4,%eax pushl %eax # push total count call push_parm # push_parm ($T, ret_value, mask, argc [,arg1, arg2, ...]); done: movl frame_pointer,%edx movl msf_temps_ptr_off(%edx),%edi retlab: leal sav_ebx(%ebp),%esp movl rtn_pc(%ebp),%edx movl act_cnt(%ebp),%eax addl $4,%eax popl %ebx popl %esi popl %ebp leal (%esp,%eax,4),%esp pushl %edx ret # op_exfun ENDP # END fis-gtm-V7.0-005/sr_i386/op_extcall.s0000755000032200000250000000315414342376327016101 0ustar librarygtc################################################################# # # # Copyright 2001, 2012 Fidelity Information Services, Inc # # # # This source code contains the intellectual property # # of its copyright holder(s), and is made available # # under a license. If you do not know the terms of # # the license, please stop and do not read further. # # # ################################################################# # PAGE ,132 .title op_extcall.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_extcall # PAGE + .DATA .extern ERR_GTMCHECK .extern ERR_LABELUNKNOWN .extern frame_pointer .text .extern auto_zlink .extern new_stack_frame .extern rts_error # PUBLIC op_extcall ENTRY op_extcall putframe addl $4,%esp # burn return pc popl %edx # routine hdr addr popl %eax # label addr cmpl $0,%eax je l2 l1: movl (%eax),%eax # get the line number offset cmpl $0,%eax je l4 addl mrt_curr_ptr(%edx),%eax addl %edx,%eax # get the line number pointer movl (%eax),%eax # get the line number addl mrt_curr_ptr(%edx),%eax addl %edx,%eax pushl %eax pushl $0 pushl %edx call new_stack_frame addl $12,%esp getframe ret l2: cmpl $0,%edx jne l4 subl $4,%esp pushl %esp movl frame_pointer,%eax pushl msf_mpc_off(%eax) call auto_zlink addl $8,%esp cmpl $0,%eax je l3 movl %eax,%edx popl %eax cmpl $0,%eax jne l1 l3: addl $4,%esp pushl ERR_GTMCHECK pushl $1 call rts_error pushl $1 # in original m68020 code ?? addl $8,%esp getframe ret l4: pushl ERR_LABELUNKNOWN pushl $1 call rts_error addl $8,%esp getframe ret # op_extcall ENDP # END fis-gtm-V7.0-005/sr_i386/op_extexfun.s0000755000032200000250000000545014342376327016314 0ustar librarygtc################################################################# # # # Copyright 2001, 2012 Fidelity Information Services, Inc # # # # This source code contains the intellectual property # # of its copyright holder(s), and is made available # # under a license. If you do not know the terms of # # the license, please stop and do not read further. # # # ################################################################# # PAGE ,132 .title op_extexfun.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_extexfun # PAGE + # call op_extexfun with the following stack: # # return PC # routine # label # ret_value address # mask # actualcnt # actual1 address # actual2 address # ... .DATA .extern ERR_FMLLSTMISSING .extern ERR_GTMCHECK .extern ERR_LABELUNKNOWN .extern dollar_truth .extern frame_pointer .text .extern auto_zlink .extern new_stack_frame .extern push_parm .extern rts_error actual1 = 24 act_cnt = 20 mask_arg = 16 ret_val = 12 label_arg = 8 routine = 4 sav_ebx = -4 sav_msf = -8 # PUBLIC op_extexfun ENTRY op_extexfun putframe addl $4,%esp # burn return PC pushl %ebp movl %esp,%ebp pushl %ebx leal sav_msf(%ebp),%esp # establish space for locals movl act_cnt(%ebp),%eax addl $3,%eax negl %eax leal (%esp,%eax,4),%esp # add space for temps movl routine(%ebp),%edx movl label_arg(%ebp),%eax cmpl $0,%eax je l3 l1: pushl %eax # save labaddr movl (%eax),%eax # get offset to line number entry cmpl $0,%eax je l5 addl mrt_curr_ptr(%edx),%eax addl %edx,%eax # get the pointer to line number entry movl (%eax),%ebx # get line number addl mrt_curr_ptr(%edx),%ebx addl %edx,%ebx popl %eax # restore labaddr cmpl $0,4(%eax) # labaddr += 4, to point to has_parms; then *has_parms je l6 # if has_parms == 0, then issue an error pushl %ebx pushl $0 pushl %edx call new_stack_frame addl $12,%esp leal ret_val(%ebp),%esi movl %esp,%edi movl act_cnt(%ebp),%eax movl %eax,%ecx addl $3,%ecx # include: A(ret_value), mask, argc REP movsl movl dollar_truth,%ecx andl $1,%ecx pushl %ecx addl $4,%eax # include: $T(just pushed) plus other 3 pushl %eax # push total count call push_parm # push_parm ($T, ret_value, mask, argc [,arg1, arg2, ...]); retlab: leal sav_ebx(%ebp),%esp movl act_cnt(%ebp),%eax addl $5,%eax popl %ebx popl %ebp leal (%esp,%eax,4),%esp getframe ret l3: cmpl $0,%edx jne l5 subl $4,%esp movl %esp,%eax pushl %eax movl frame_pointer,%edx pushl msf_mpc_off(%edx) call auto_zlink addl $8,%esp cmpl $0,%eax je l4 movl %eax,%edx popl %eax cmpl $0,%eax jne l1 l4: pushl ERR_GTMCHECK pushl $1 call rts_error jmp retlab l5: pushl ERR_LABELUNKNOWN pushl $1 call rts_error jmp retlab l6: pushl ERR_FMLLSTMISSING pushl $1 call rts_error jmp retlab # op_extexfun ENDP # END fis-gtm-V7.0-005/sr_i386/op_extjmp.s0000755000032200000250000000301414342376327015747 0ustar librarygtc################################################################# # # # Copyright 2001, 2014 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. # # # ################################################################# # PAGE ,132 .title op_extjmp.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_extjmp # PAGE + .DATA .extern ERR_GTMCHECK .extern ERR_LABELNOTFND .extern frame_pointer .text .extern auto_zlink .extern flush_jmp .extern rts_error # PUBLIC op_extjmp ENTRY op_extjmp putframe addl $4,%esp # Burn return pc popl %edx cmpl $0,%edx je l2 popl %eax cmpl $0,%eax je l4 l1: movl (%eax),%eax # get line number offset cmpl $0,%eax je l4 addl mrt_curr_ptr(%edx),%eax addl %edx,%eax # get line number pointer movl (%eax),%eax # get line number addl mrt_curr_ptr(%edx),%eax addl %edx,%eax pushl %eax pushl $0 pushl %edx call flush_jmp addl $12,%esp getframe ret l2: movl %esp,%eax pushl %eax movl frame_pointer,%eax pushl msf_mpc_off(%eax) call auto_zlink addl $8,%esp cmpl $0,%eax je l3 movl %eax,%edx popl %eax cmpl $0,%eax jne l1 jmp l4 l3: addl $4,%esp pushl ERR_GTMCHECK pushl $1 call rts_error addl $8,%esp getframe ret l4: pushl ERR_LABELNOTFND pushl $1 call rts_error addl $8,%esp getframe ret # op_extjmp ENDP # END fis-gtm-V7.0-005/sr_i386/op_fetchintrrpt.s0000755000032200000250000000210514342376327017154 0ustar librarygtc################################################################# # # # Copyright 2001, 2009 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. # # # ################################################################# # PAGE ,132 .title op_fetchintrrpt.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_fetchintrrpt # PAGE + .DATA .extern frame_pointer .extern neterr_pending .text .extern gtm_fetch .extern gvcmz_neterr .extern outofband_clear .extern async_action # PUBLIC op_fetchintrrpt ENTRY op_fetchintrrpt movl frame_pointer,%edx popl msf_mpc_off(%edx) call gtm_fetch popl %eax leal (%esp,%eax,4),%esp cmpb $0,neterr_pending je l1 call outofband_clear pushl $0 call gvcmz_neterr addl $4,%esp l1: pushl $1 call async_action movl frame_pointer,%edx pushl msf_mpc_off(%edx) ret # op_fetchintrrpt ENDP # END fis-gtm-V7.0-005/sr_i386/op_fnget.s0000755000032200000250000000231714342376327015550 0ustar librarygtc################################################################# # # # Copyright 2001, 2009 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. # # # ################################################################# # PAGE ,132 .title op_fnget.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "mval_def.si" .sbttl op_fnget # PAGE + # ------------------------------------ # op_fnget.s # # Mumps $Get function # ------------------------------------ # edx - src. mval # eax - dest. mval .text # PUBLIC op_fnget ENTRY op_fnget cmpl $0,%edx je l5 # if arg = 0, set type and len mv_if_notdefined %edx, l5 # Copy the mval from [edx] to [eax]. pushl %edi pushl %esi movl $mval_byte_len,%ecx movl %edx,%esi movl %eax,%edi REP movsb andw $~mval_m_aliascont, mval_w_mvtype(%eax) # Don't propagate alias container flag popl %esi popl %edi ret l5: movw $mval_m_str,mval_w_mvtype(%eax) # string type movl $0,mval_l_strlen(%eax) # dest. str. len. = 0 ret # op_fnget ENDP # END fis-gtm-V7.0-005/sr_i386/op_fnzextract.s0000755000032200000250000000447114342376327016640 0ustar librarygtc################################################################# # # # Copyright (c) 2006-2022 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. # # # ################################################################# # PAGE ,132 .title op_fnzextract.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "mval_def.si" .sbttl op_fnzextract # PAGE + # ------------------------------------ # op_fnzextract.s # # Mumps $Extract function # ------------------------------------ # -------------------------------- # op_fnzextract (int last, int first, mval *src, mval *dest) # -------------------------------- # esi - src mval # edi - dest mval # ecx - src. str. len. # ebx - resultant str. len. # eax - first index # edx - last index last = 8 first = 12 src = 16 dest = 20 .text .extern n2s .extern underr # PUBLIC op_fnzextract ENTRY op_fnzextract enter $0,$0 pushl %edi pushl %esi pushl %ebx movl src(%ebp),%esi # esi - src. mval mv_force_defined %esi, l00 movl %esi, src(%ebp) # save possibly modified src ptr mv_force_str %esi, l01 movl src(%ebp),%esi # esi - src.mval movl first(%ebp),%eax # eax - first cmpl $0,%eax jg l10 movl $1,%eax # if first < 1, then first = 1 l10: movl last(%ebp),%edx # edx - last movl dest(%ebp),%edi # edi - last movw $mval_m_str,mval_w_mvtype(%edi) # always a string movl mval_l_strlen(%esi),%ecx # ecx - src. str. len. cmpl %eax,%ecx # if left index > str. len, # then null result jl l25 cmpl $0,%edx # if left index < 0, # then null result jl l25 cmpl %edx,%ecx # right index may be at most the len. jge l20 # of the source string movl %ecx,%edx l20: movl %edx,%ebx subl %eax,%ebx # result len. = end - start + 1 addl $1,%ebx jg l30 # if len > 0, then continue l25: movl $0,mval_l_strlen(%edi) jmp retlab l30: movl %ebx,mval_l_strlen(%edi) # dest. str. len. subl $1,%eax # base = src.addr + left ind. - 1 addl mval_a_straddr(%esi),%eax movl %eax,mval_a_straddr(%edi) # string addr. retlab: popl %ebx popl %esi popl %edi leave ret # op_fnzextract ENDP # END fis-gtm-V7.0-005/sr_i386/op_follow.s0000755000032200000250000000226114342376327015745 0ustar librarygtc################################################################# # # # Copyright 2001, 2008 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. # # # ################################################################# # PAGE ,132 .title op_follow.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "mval_def.si" .sbttl op_follow # PAGE + sav_eax = -4 sav_edx = -8 .text .extern memvcmp .extern n2s .extern underr # PUBLIC op_follow ENTRY op_follow enter $8, $0 movl %edx, sav_edx(%ebp) mv_force_defined %eax, l1 movl %eax, sav_eax(%ebp) mv_force_str %eax, l2 movl sav_edx(%ebp), %edx mv_force_defined %edx, l3 movl %edx, sav_edx(%ebp) mv_force_str %edx, l4 movl sav_eax(%ebp),%eax movl sav_edx(%ebp),%edx movl mval_l_strlen(%edx),%ecx pushl %ecx pushl mval_a_straddr(%edx) movl mval_l_strlen(%eax),%ecx pushl %ecx pushl mval_a_straddr(%eax) call memvcmp addl $16,%esp cmpl $0,%eax leave ret # op_follow ENDP # END fis-gtm-V7.0-005/sr_i386/op_forcenum.s0000755000032200000250000000276714342376327016274 0ustar librarygtc################################################################# # # # Copyright 2001, 2008 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. # # # ################################################################# # PAGE ,132 .title op_forcenum.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "mval_def.si" .sbttl op_forcenum # PAGE + .text .extern s2n # edx - source mval # eax - destination mval # PUBLIC op_forcenum ENTRY op_forcenum pushl %eax mv_force_defined %edx, l00 pushl %edx mv_force_num %edx, l10 popl %edx popl %eax testw $mval_m_str,mval_w_mvtype(%edx) je l20 testw $mval_m_num_approx,mval_w_mvtype(%edx) je l40 l20: testw $mval_m_int_without_nm,mval_w_mvtype(%edx) je l30 movw $mval_m_int,mval_w_mvtype(%eax) movl mval_l_m1(%edx),%edx movl %edx,mval_l_m1(%eax) ret l30: pushl %ebx movw $mval_m_nm,mval_w_mvtype(%eax) movb mval_b_exp(%edx),%bl movb %bl,mval_b_exp(%eax) # Copy the only numeric part of Mval from [edx] to [eax]. movl mval_l_m0(%edx),%ebx movl %ebx,mval_l_m0(%eax) movl mval_l_m1(%edx),%ebx movl %ebx,mval_l_m1(%eax) popl %ebx ret l40: # Copy the Mval from [edx] to [eax]. pushl %edi pushl %esi movl %eax,%edi movl %edx,%esi movl $mval_byte_len,%ecx REP movsb popl %esi popl %edi ret # op_forcenum ENDP # END fis-gtm-V7.0-005/sr_i386/op_forchk1.s0000755000032200000250000000114414342376327015777 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title op_forchk1.s .sbttl op_forchk1 # .386 # .MODEL FLAT, C .include "linkage.si" .text # PUBLIC op_forchk1 ENTRY op_forchk1 ret # op_forchk1 ENDP # END fis-gtm-V7.0-005/sr_i386/op_forinit.s0000755000032200000250000000236614342376327016123 0ustar librarygtc################################################################# # # # Copyright 2001, 2008 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. # # # ################################################################# # PAGE ,132 .title op_forinit.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .INCLUDE "mval_def.si" .sbttl op_forinit # PAGE + .DATA .extern frame_pointer .text .extern numcmp .extern s2n .extern underr # PUBLIC op_forinit ENTRY op_forinit movl frame_pointer,%edx popl msf_mpc_off(%edx) movl 4(%esp),%eax mv_force_defined %eax, l0 movl %eax, 4(%esp) mv_force_num %eax, t2 movl 4(%esp),%eax cmpl $0,mval_l_m1(%eax) js l2 mv_if_int %eax, l1 testb $mval_esign_mask,mval_b_exp(%eax) jne l2 l1: movl 8(%esp),%eax movl %eax,4(%esp) call numcmp addl $12,%esp movl frame_pointer,%edx pushl msf_mpc_off(%edx) cmpl $0,%eax ret l2: movl 8(%esp),%eax pushl %eax call numcmp addl $16,%esp movl frame_pointer,%edx pushl msf_mpc_off(%edx) cmpl $0,%eax ret # op_forinit ENDP # END fis-gtm-V7.0-005/sr_i386/op_forintrrpt.s0000755000032200000250000000160014342376327016650 0ustar librarygtc################################################################# # # # 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. # # # ################################################################# # PAGE ,132 .title op_forintrrpt.s .sbttl op_forintrrpt # .386 # .MODEL FLAT, C .include "linkage.si" .DATA .extern neterr_pending .extern restart_pc .text .extern gvcmz_neterr .extern async_action .extern outofband_clear # PUBLIC op_forintrrpt ENTRY op_forintrrpt cmpb $0,neterr_pending je l1 call outofband_clear pushl $0 call gvcmz_neterr addl $4,%esp l1: pushl $0 call async_action addl $4,%esp ret # op_forintrrpt ENDP # END fis-gtm-V7.0-005/sr_i386/op_forlcldo.s0000755000032200000250000000226714342376327016255 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title op_forlcldo.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_forlcldo # PAGE + .DATA .extern frame_pointer .text .extern exfun_frame .sbttl op_forlcldob # PUBLIC op_forlcldob ENTRY op_forlcldob movl frame_pointer,%edx movl (%esp),%eax movl %eax,msf_mpc_off(%edx) addl $2,msf_mpc_off(%edx) # store pc in MUMPS stack frame doit: call exfun_frame movl frame_pointer,%edx movl msf_temps_ptr_off(%edx),%edi ret # op_forlcldob ENDP .sbttl op_forlcldow, op_forlcldol # PUBLIC op_forlcldow, op_forlcldol ENTRY op_forlcldol ENTRY op_forlcldow movl frame_pointer,%edx movl (%esp),%eax movl %eax,msf_mpc_off(%edx) addl $5,msf_mpc_off(%edx) # store pc in MUMPS stack frame jmp doit # op_forlcldol ENDP # END fis-gtm-V7.0-005/sr_i386/op_forloop.s0000755000032200000250000000724214342376327016127 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2022 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. # # # ################################################################# # PAGE ,132 .title op_forloop.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .INCLUDE "mval_def.si" .sbttl op_forloop # PAGE + # Called with the stack contents: # call return # ptr to index mval # ptr to step mval # ptr to terminator mval # loop address .DATA .extern frame_pointer # ten_dd DD 10 ten_dd: .long 10 .text .extern add_mvals .extern numcmp .extern s2n .extern underr term = 12 step = 8 indx = 4 # PUBLIC op_forloop ENTRY op_forloop movl frame_pointer,%edx popl msf_mpc_off(%edx) enter $0, $0 pushl %edi pushl %esi pushl %ebx movl indx(%ebp),%esi mv_force_defined_overwrite %esi, l0 # copy literal_null into control variable if undefined movl %esi, indx(%ebp) mv_force_num %esi, l1 movl indx(%ebp),%esi movl step(%ebp),%edi movw mval_w_mvtype(%esi),%ax movw mval_w_mvtype(%edi),%dx andw %dx,%ax testw $mval_m_int_without_nm,%ax je L66 movl mval_l_m1(%esi),%eax addl mval_l_m1(%edi),%eax cmpl $MANT_HI,%eax jge L68 cmpl $-MANT_HI,%eax jle L67 movw $mval_m_int,mval_w_mvtype(%esi) movl %eax,mval_l_m1(%esi) jmp L63 L67: movb $mval_esign_mask,mval_b_exp(%esi) # set sign bit negl %eax jmp L69 L68: movb $0,mval_b_exp(%esi) # clear sign bit L69: movw $mval_m_nm,mval_w_mvtype(%esi) orb $69,mval_b_exp(%esi) # set exponent field movl %eax,%ebx movl $0,%edx idivl ten_dd,%eax movl %eax,mval_l_m1(%esi) imull $10,%eax,%eax subl %eax,%ebx imull $MANT_LO,%ebx,%ebx movl %ebx,mval_l_m0(%esi) jmp L63 L66: pushl %esi pushl $0 pushl %edi pushl %esi call add_mvals addl $16,%esp movl indx(%ebp),%esi L63: movl step(%ebp),%edi testw $mval_m_int_without_nm,mval_w_mvtype(%edi) jne a cmpb $0,mval_b_exp(%edi) jl b jmp a2 a: cmpl $0,mval_l_m1(%edi) jl b a2: movl term(%ebp),%edi jmp e b: movl %esi,%edi # if step is negative, reverse compare movl term(%ebp),%esi e: # compare indx and term movw mval_w_mvtype(%esi),%ax movw mval_w_mvtype(%edi),%dx andw %dx,%ax testw $2,%ax je ccmp movl mval_l_m1(%esi),%eax subl mval_l_m1(%edi),%eax jmp tcmp ccmp: pushl %edi pushl %esi call numcmp addl $8,%esp cmpl $0,%eax tcmp: jle d movl indx(%ebp),%esi movl step(%ebp),%edi movw mval_w_mvtype(%esi),%ax movw mval_w_mvtype(%edi),%dx andw %dx,%ax testw $mval_m_int_without_nm,%ax je l66 movl mval_l_m1(%esi),%eax subl mval_l_m1(%edi),%eax cmpl $MANT_HI,%eax jge l68 cmpl $-MANT_HI,%eax jle l67 movw $mval_m_int,mval_w_mvtype(%esi) movl %eax,mval_l_m1(%esi) jmp l63 l67: movb $mval_esign_mask,mval_b_exp(%esi) # set sign bit negl %eax jmp l69 l68: movb $0,mval_b_exp(%esi) # clear sign bit l69: movw $mval_m_nm,mval_w_mvtype(%esi) orb $69,mval_b_exp(%esi) # set exponent field movl %eax,%ebx movl $0,%edx idivl ten_dd,%eax movl %eax,mval_l_m1(%esi) imull $10,%eax,%eax subl %eax,%ebx imull $MANT_LO,%ebx,%ebx movl %ebx,mval_l_m0(%esi) jmp l63 l66: pushl %esi pushl $1 pushl %edi pushl %esi call add_mvals addl $16,%esp l63: popl %ebx popl %esi popl %edi leave addl $16,%esp # remove op_forloop arguments movl frame_pointer,%edx pushl msf_mpc_off(%edx) ret d: popl %ebx popl %esi popl %edi leave addl $12,%esp # remove term, step, indx; leave loop addr ret # op_forloop ENDP # END fis-gtm-V7.0-005/sr_i386/op_gettruth.s0000755000032200000250000000162614342376327016315 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title op_gettruth.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "mval_def.si" .sbttl op_gettruth # PAGE + .DATA .extern dollar_truth .extern literal_one .extern literal_zero .text # PUBLIC op_gettruth ENTRY op_gettruth pushl %edi pushl %esi cmpl $0,dollar_truth jne l1 leal literal_zero,%esi jmp doit l1: leal literal_one,%esi doit: movl %edx,%edi movl $mval_byte_len,%ecx REP movsb popl %esi popl %edi ret # op_gettruth ENDP # END fis-gtm-V7.0-005/sr_i386/op_iretmvad.s0000755000032200000250000000154614342376327016263 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title op_iretmvad.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_iretmvad # PAGE + .DATA .extern frame_pointer .text .extern op_unwind # PUBLIC op_iretmvad ENTRY op_iretmvad movl %edx,%ecx # save input parameter from putframe macro putframe addl $4,%esp movl %ecx,%edx pushl %edx call op_unwind popl %eax # return input parameter getframe ret # op_iretmvad ENDP # END fis-gtm-V7.0-005/sr_i386/op_linefetch.s0000755000032200000250000000153314342376327016405 0ustar librarygtc################################################################# # # # Copyright 2001, 2009 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. # # # ################################################################# # PAGE ,132 .title op_linefetch.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_linefetch # PAGE + .DATA .extern frame_pointer .text .extern gtm_fetch # PUBLIC op_linefetch ENTRY op_linefetch movl frame_pointer,%eax popl msf_mpc_off(%eax) call gtm_fetch popl %eax leal (%esp,%eax,4),%esp movl frame_pointer,%eax pushl msf_mpc_off(%eax) ret # op_linefetch ENDP # END fis-gtm-V7.0-005/sr_i386/op_linestart.s0000755000032200000250000000136214342376327016451 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title op_linestart.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_linestart # PAGE + .DATA .extern frame_pointer .text # PUBLIC op_linestart ENTRY op_linestart movl frame_pointer,%edx movl (%esp),%eax movl %eax,msf_mpc_off(%edx) ret # op_linestart ENDP # END fis-gtm-V7.0-005/sr_i386/op_mprofcall.s0000755000032200000250000000211614342376327016421 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title op_mprofcall.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_mprofcallb # PAGE + .DATA .extern frame_pointer .text .extern copy_stack_frame_sp # PUBLIC op_mprofcallb ENTRY op_mprofcallb movl frame_pointer,%edx movl (%esp),%eax movl %eax,msf_mpc_off(%edx) addl $2,msf_mpc_off(%edx) call copy_stack_frame_sp ret # op_callb ENDP .sbttl op_mprofcallw, op_mprofcalll # PUBLIC op_mprofcallw, op_mprofcalll ENTRY op_mprofcalll ENTRY op_mprofcallw movl frame_pointer,%edx movl (%esp),%eax movl %eax,msf_mpc_off(%edx) addl $5,msf_mpc_off(%edx) call copy_stack_frame_sp ret # op_mprofcalll ENDP # END fis-gtm-V7.0-005/sr_i386/op_mprofcallsp.s0000755000032200000250000000253114342376327016765 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title op_mprofcallsp.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_mprofcallsp # PAGE + .DATA .extern dollar_truth .extern frame_pointer .text .extern exfun_frame_push_dummy_frame .extern push_tval .sbttl op_mprofcallspb # PUBLIC op_mprofcallspb ENTRY op_mprofcallspb movl frame_pointer,%edx movl (%esp),%eax movl %eax,msf_mpc_off(%edx) addl $2,msf_mpc_off(%edx) # store pc in MUMPS stack frame doit: call exfun_frame_push_dummy_frame pushl dollar_truth call push_tval addl $4,%esp movl frame_pointer,%edx movl msf_temps_ptr_off(%edx),%edi ret # op_mprofcallspb ENDP .sbttl op_mprofcallspw, op_mprofcallspl # PUBLIC op_mprofcallspw, op_mprofcallspl ENTRY op_mprofcallspw ENTRY op_mprofcallspl movl frame_pointer,%edx movl (%esp),%eax movl %eax,msf_mpc_off(%edx) addl $5,msf_mpc_off(%edx) # store pc in MUMPS stack frame jmp doit # op_mprofcallspw ENDP # END fis-gtm-V7.0-005/sr_i386/op_mprofexfun.s0000755000032200000250000000420614342376327016635 0ustar librarygtc################################################################# # # # Copyright 2001, 2012 Fidelity Information Services, Inc # # # # This source code contains the intellectual property # # of its copyright holder(s), and is made available # # under a license. If you do not know the terms of # # the license, please stop and do not read further. # # # ################################################################# # PAGE ,132 .title op_mprofexfun.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_mprofexfun # PAGE + # call op_mprofexfun with the following stack: # # return PC # ret_value address # mask # actualcnt # actual1 address # actual2 address # ... .DATA .extern ERR_GTMCHECK .extern dollar_truth .extern frame_pointer .text .extern exfun_frame_sp .extern push_parm .extern rts_error JMP_Jb = 0x0eb JMP_Jv = 0x0e9 actual1 = 20 act_cnt = 16 mask_arg = 12 ret_val = 8 rtn_pc = 4 sav_esi = -4 sav_ebx = -8 sav_msf = -12 # PUBLIC op_mprofexfun ENTRY op_mprofexfun pushl %ebp movl %esp,%ebp pushl %esi pushl %ebx leal sav_msf(%ebp),%esp # establish space for locals movl act_cnt(%ebp),%eax addl $3,%eax negl %eax leal (%esp,%eax,4),%esp # establish space for temps movl frame_pointer,%edx movl rtn_pc(%ebp),%eax cmpb $JMP_Jv,(%eax) je long cmpb $JMP_Jb,(%eax) je byte_off error: pushl ERR_GTMCHECK pushl $1 call rts_error addl $8,%esp jmp retlab byte_off: movl %eax,msf_mpc_off(%edx) addl $2,msf_mpc_off(%edx) jmp cont long: movl %eax,msf_mpc_off(%edx) addl $5,msf_mpc_off(%edx) cont: call exfun_frame_sp leal ret_val(%ebp),%esi movl %esp,%edi movl act_cnt(%ebp),%eax movl %eax,%ecx addl $3,%ecx REP movsl movl dollar_truth,%ecx andl $1,%ecx pushl %ecx # push $T addl $4,%eax pushl %eax # push total count call push_parm # push_parm ($T, ret_value, mask, argc [,arg1, arg2, ...]); done: movl frame_pointer,%edx movl msf_temps_ptr_off(%edx),%edi retlab: leal sav_ebx(%ebp),%esp movl rtn_pc(%ebp),%edx movl act_cnt(%ebp),%eax addl $4,%eax popl %ebx popl %esi popl %ebp leal (%esp,%eax,4),%esp pushl %edx ret # op_mprofexfun ENDP # END fis-gtm-V7.0-005/sr_i386/op_mprofextcall.s0000755000032200000250000000321314342376327017141 0ustar librarygtc################################################################# # # # Copyright 2001, 2012 Fidelity Information Services, Inc # # # # This source code contains the intellectual property # # of its copyright holder(s), and is made available # # under a license. If you do not know the terms of # # the license, please stop and do not read further. # # # ################################################################# # PAGE ,132 .title op_mprofextcall.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_mprofextcall # PAGE + .DATA .extern ERR_GTMCHECK .extern ERR_LABELUNKNOWN .extern frame_pointer .text .extern auto_zlink .extern new_stack_frame_sp .extern rts_error # PUBLIC op_mprofextcall ENTRY op_mprofextcall putframe addl $4,%esp # burn return pc popl %edx # routine hdr addr popl %eax # label addr cmpl $0,%eax je l2 l1: movl (%eax),%eax # get the line number offset cmpl $0,%eax je l4 addl mrt_curr_ptr(%edx),%eax addl %edx,%eax # get the line number pointer movl (%eax),%eax # get the line number addl mrt_curr_ptr(%edx),%eax addl %edx,%eax pushl %eax pushl $0 pushl %edx call new_stack_frame_sp addl $12,%esp getframe ret l2: cmpl $0,%edx jne l4 subl $4,%esp pushl %esp movl frame_pointer,%eax pushl msf_mpc_off(%eax) call auto_zlink addl $8,%esp cmpl $0,%eax je l3 movl %eax,%edx popl %eax cmpl $0,%eax jne l1 l3: addl $4,%esp pushl ERR_GTMCHECK pushl $1 call rts_error pushl $1 # in original m68020 code ?? addl $8,%esp getframe ret l4: pushl ERR_LABELUNKNOWN pushl $1 call rts_error addl $8,%esp getframe ret # op_mprofextcall ENDP # END fis-gtm-V7.0-005/sr_i386/op_mprofextexfun.s0000755000032200000250000000551414342376327017361 0ustar librarygtc################################################################# # # # Copyright 2001, 2012 Fidelity Information Services, Inc # # # # This source code contains the intellectual property # # of its copyright holder(s), and is made available # # under a license. If you do not know the terms of # # the license, please stop and do not read further. # # # ################################################################# # PAGE ,132 .title op_mprofextexfun.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_mprofextexfun # PAGE + # call op_mprofextexfun with the following stack: # # return PC # routine # label # ret_value address # mask # actualcnt # actual1 address # actual2 address # ... .DATA .extern ERR_FMLLSTMISSING .extern ERR_GTMCHECK .extern ERR_LABELUNKNOWN .extern dollar_truth .extern frame_pointer .text .extern auto_zlink .extern new_stack_frame_sp .extern push_parm .extern rts_error actual1 = 24 act_cnt = 20 mask_arg = 16 ret_val = 12 label_arg = 8 routine = 4 sav_ebx = -4 sav_msf = -8 # PUBLIC op_mprofextexfun ENTRY op_mprofextexfun putframe addl $4,%esp # burn return PC pushl %ebp movl %esp,%ebp pushl %ebx leal sav_msf(%ebp),%esp # establish space for locals movl act_cnt(%ebp),%eax addl $3,%eax negl %eax leal (%esp,%eax,4),%esp # add space for temps movl routine(%ebp),%edx movl label_arg(%ebp),%eax cmpl $0,%eax je l3 l1: pushl %eax # save labaddr movl (%eax),%eax # get offset to line number entry cmpl $0,%eax je l5 addl mrt_curr_ptr(%edx),%eax addl %edx,%eax # get the pointer to line number entry movl (%eax),%ebx # get line number addl mrt_curr_ptr(%edx),%ebx addl %edx,%ebx popl %eax # restore labaddr cmpl $0,4(%eax) # labaddr += 4, to point to has_parms; then *has_parms je l6 # if has_parms == 0, then issue an error pushl %ebx pushl $0 pushl %edx call new_stack_frame_sp addl $12,%esp leal ret_val(%ebp),%esi movl %esp,%edi movl act_cnt(%ebp),%eax movl %eax,%ecx addl $3,%ecx # include: A(ret_value), mask, argc REP movsl movl dollar_truth,%ecx andl $1,%ecx pushl %ecx addl $4,%eax # include: $T(just pushed) plus other 3 pushl %eax # push total count call push_parm # push_parm ($T, ret_value, mask, argc [,arg1, arg2, ...]); retlab: leal sav_ebx(%ebp),%esp movl act_cnt(%ebp),%eax addl $5,%eax popl %ebx popl %ebp leal (%esp,%eax,4),%esp getframe ret l3: cmpl $0,%edx jne l5 subl $4,%esp movl %esp,%eax pushl %eax movl frame_pointer,%edx pushl msf_mpc_off(%edx) call auto_zlink addl $8,%esp cmpl $0,%eax je l4 movl %eax,%edx popl %eax cmpl $0,%eax jne l1 l4: pushl ERR_GTMCHECK pushl $1 call rts_error jmp retlab l5: pushl ERR_LABELUNKNOWN pushl $1 call rts_error jmp retlab l6: pushl ERR_FMLLSTMISSING pushl $1 call rts_error jmp retlab # op_mprofextexfun ENDP # END fis-gtm-V7.0-005/sr_i386/op_mprofforchk1.s0000644000032200000250000000147414342376327017046 0ustar librarygtc################################################################# # # # Copyright 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. # # # ################################################################# .title op_mprofforchk1.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_mprofforchk1 # Called with the stack contents: # call return .DATA .text .extern forchkhandler # PUBLIC op_mprofforchk1 ENTRY op_mprofforchk1 pushl 0(%esp) # throw the current return address on as an arg call forchkhandler addl $4, %esp ret # op_mprofforchk1 ENDP # END fis-gtm-V7.0-005/sr_i386/op_mprofforlcldo.s0000755000032200000250000000240514342376327017313 0ustar librarygtc################################################################# # # # Copyright 2001, 2012 Fidelity Information Services, Inc # # # # This source code contains the intellectual property # # of its copyright holder(s), and is made available # # under a license. If you do not know the terms of # # the license, please stop and do not read further. # # # ################################################################# # PAGE ,132 .title op_mprofforlcldo.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_mprofforlcldo # PAGE + .DATA .extern frame_pointer .text .extern exfun_frame_sp .sbttl op_mprofforlcldob # PUBLIC op_mprofforlcldob ENTRY op_mprofforlcldob movl frame_pointer,%edx movl (%esp),%eax movl %eax,msf_mpc_off(%edx) addl $2,msf_mpc_off(%edx) # store pc in MUMPS stack frame doit: call exfun_frame_sp movl frame_pointer,%edx movl msf_temps_ptr_off(%edx),%edi ret # op_mprofforlcldob ENDP .sbttl op_mprofforlcldow, op_mprofforlcldol # PUBLIC op_mprofforlcldow, op_mprofforlcldol ENTRY op_mprofforlcldol ENTRY op_mprofforlcldow movl frame_pointer,%edx movl (%esp),%eax movl %eax,msf_mpc_off(%edx) addl $5,msf_mpc_off(%edx) # store pc in MUMPS stack frame jmp doit # op_mprofforlcldol ENDP # END fis-gtm-V7.0-005/sr_i386/op_mproflinefetch.s0000755000032200000250000000177714342376327017463 0ustar librarygtc################################################################# # # # 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. # # # ################################################################# # PAGE ,132 .title op_mproflinefetch.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_mproflinefetch # PAGE + .DATA .extern frame_pointer .text .extern gtm_fetch .extern stack_leak_check .extern pcurrpos # PUBLIC op_mproflinefetch ENTRY op_mproflinefetch movl frame_pointer,%eax popl msf_mpc_off(%eax) call gtm_fetch call pcurrpos popl %eax # popping generated code args off stack before leak check leal (%esp,%eax,4),%esp call stack_leak_check movl frame_pointer,%eax pushl msf_mpc_off(%eax) ret # op_mproflinefetch ENDP # END fis-gtm-V7.0-005/sr_i386/op_mproflinestart.s0000755000032200000250000000146314342376327017517 0ustar librarygtc################################################################# # # # 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. # # # ################################################################# # PAGE ,132 .title op_mproflinestart.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_mproflinestart # PAGE + .DATA .extern frame_pointer .text .extern pcurrpos # PUBLIC op_mproflinestart ENTRY op_mproflinestart movl frame_pointer,%edx movl (%esp),%eax movl %eax,msf_mpc_off(%edx) call pcurrpos ret # op_mproflinestart ENDP # END fis-gtm-V7.0-005/sr_i386/op_neg.s0000755000032200000250000000247714342376327015225 0ustar librarygtc################################################################# # # # Copyright 2001, 2008 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. # # # ################################################################# # PAGE ,132 .title op_neg.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "mval_def.si" .sbttl op_neg # PAGE + .text # # op_neg ( mval *u, mval *v ) : u = -v # # edx - source mval = &v # eax - destination mval = &u .extern s2n .extern underr # PUBLIC op_neg ENTRY op_neg pushl %eax mv_force_defined %edx, isdefined popl %eax mv_if_number %edx, numer pushl %eax pushl %edx call s2n popl %edx popl %eax numer: mv_if_notint %edx, float movw $mval_m_int,mval_w_mvtype(%eax) movl mval_l_m1(%edx),%edx negl %edx movl %edx,mval_l_m1(%eax) ret float: pushl %ebx # need a temp register movw $mval_m_nm,mval_w_mvtype(%eax) movb mval_b_exp(%edx),%bl xorb $mval_esign_mask,%bl # flip the sign bit movb %bl,mval_b_exp(%eax) movl mval_l_m0(%edx),%ebx movl %ebx,mval_l_m0(%eax) movl mval_l_m1(%edx),%ebx movl %ebx,mval_l_m1(%eax) popl %ebx ret # op_neg ENDP #END fis-gtm-V7.0-005/sr_i386/op_numcmp.s0000755000032200000250000000161714342376327015746 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title op_numcmp.s .sbttl op_numcmp # .386 # .MODEL FLAT, C .include "linkage.si" # op_numcmp calls numcmp to compare two mvals # # entry: # eax mval *u # edx mval *v # # exit: # condition codes set according to value of # numcmp (u, v) .text .extern numcmp # PUBLIC op_numcmp ENTRY op_numcmp pushl %edx pushl %eax call numcmp addl $8,%esp # restore stack cmpl $0,%eax # set flags according to result from numcmp ret # op_numcmp ENDP # END fis-gtm-V7.0-005/sr_i386/op_pattern.s0000755000032200000250000000242714342376327016124 0ustar librarygtc################################################################# # # # Copyright 2001, 2002 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title op_pattern.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "mval_def.si" .sbttl op_pattern # PAGE + .text .extern do_patfixed .extern do_pattern # PUBLIC op_pattern ENTRY op_pattern pushl %edx pushl %eax movl mval_a_straddr(%edx),%eax # # This is an array of unaligned ints. If the first word is zero, then call do_pattern # instead of do_patfixed. Only the low order byte is significant and so it is the only # one we need to test. We would do this in assembly because (1) we need the assmembly # routine anyway to save the return value into $TEST and (2) it saves an extra level of # call linkage at the C level to do the decision here. # cmpb $0,(%eax) # little endian compare of low order byte je l1 call do_patfixed jmp l2 l1: call do_pattern l2: addl $8,%esp cmpl $0,%eax ret # op_pattern ENDP # END fis-gtm-V7.0-005/sr_i386/op_restartpc.s0000755000032200000250000000161014342376327016447 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2019 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. # # # ################################################################# # PAGE ,132 .title op_restartpc.s .sbttl op_restartpc # .386 # .MODEL FLAT, C .include "linkage.si" .include "g_msf.si" .DATA .extern frame_pointer .text # PUBLIC op_restartpc ENTRY op_restartpc movl (%esp),%eax subl $6,%eax movl frame_pointer,%edx movl %eax,msf_restart_pc_off(%edx) movl msf_ctxt_off(%edx),%eax movl %eax,msf_restart_ctxt_off(%edx) ret # op_restartpc ENDP # END fis-gtm-V7.0-005/sr_i386/op_retarg.s0000755000032200000250000000145014342376327015726 0ustar librarygtc################################################################# # # # 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. # # # ################################################################# # PAGE ,132 .title op_retarg.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_retarg # PAGE + .DATA .extern frame_pointer .text .extern unw_retarg # PUBLIC op_retarg ENTRY op_retarg movl %edx,(%esp) # Reuse return point on stack (not needed) pushl %eax call unw_retarg addl $8,%esp getframe ret # op_retarg ENDP # END fis-gtm-V7.0-005/sr_i386/op_sorts_after.s0000755000032200000250000000226714342376327017004 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title op_sorts_after.s .sbttl op_sorts_after # .386 # .MODEL FLAT, C .include "linkage.si" # op_sorts_after.s 80386 # # op_sorts_after(mval *mval1, *mval2) # Call sorts_after() to determine whether mval1 comes after mval2 # in sorting order. Use alternate local collation sequence if # present. # # entry: # eax mval *mval1 # edx mval *mval2 # # Sets condition flags and returns in eax: ## 1 mval1 > mval2 ## 0 mval1 = mval2 ## -1 mval1 < mval2 # .text .extern sorts_after # PUBLIC op_sorts_after ENTRY op_sorts_after pushl %edx pushl %eax call sorts_after addl $8,%esp # restore stack cmpl $0,%eax # set flags according to result from ret # sorts_after. # op_sorts_after ENDP # END fis-gtm-V7.0-005/sr_i386/op_startintrrpt.s0000755000032200000250000000177214342376327017231 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title op_startintrrpt.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl op_startintrrpt # PAGE + .DATA .extern frame_pointer .extern neterr_pending .text .extern gvcmz_neterr .extern async_action .extern outofband_clear # PUBLIC op_startintrrpt ENTRY op_startintrrpt putframe cmpb $0,neterr_pending je l1 call outofband_clear pushl $0 call gvcmz_neterr addl $4,%esp l1: pushl $1 call async_action addl $8,%esp # 4 bytes to burn return pc, 4 more to remove arg to async_action getframe ret # op_startintrrpt ENDP # END fis-gtm-V7.0-005/sr_i386/op_sto.s0000755000032200000250000000202514342376327015246 0ustar librarygtc################################################################# # # # Copyright 2001, 2009 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. # # # ################################################################# # PAGE ,132 .title op_sto.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "mval_def.si" .sbttl op_sto # PAGE + .DATA .extern literal_null .extern undef_inhibit .text .extern underr # PUBLIC op_sto ENTRY op_sto mv_if_notdefined %edx, b a: pushl %edi pushl %esi movl $mval_byte_len,%ecx movl %edx,%esi movl %eax,%edi REP movsb andw $~mval_m_aliascont, mval_w_mvtype(%eax) # Don't propagate alias container flag popl %esi popl %edi ret b: cmpb $0,undef_inhibit je clab leal literal_null,%edx jmp a clab: pushl %edx call underr addl $4,%esp ret # op_sto ENDP # END fis-gtm-V7.0-005/sr_i386/op_zhelp.s0000755000032200000250000000135114342376327015564 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title op_zhelp.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" # PAGE + .DATA .extern frame_pointer .text .extern op_zhelp_xfr # PUBLIC op_zhelp ENTRY op_zhelp movl frame_pointer,%edx popl msf_mpc_off(%edx) call op_zhelp_xfr getframe ret # op_zhelp ENDP # END fis-gtm-V7.0-005/sr_i386/opp_break.s0000755000032200000250000000133614342376327015711 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_break.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_break # PAGE + .DATA .extern frame_pointer .text .extern op_break # PUBLIC opp_break ENTRY opp_break putframe addl $4,%esp call op_break getframe ret # opp_break ENDP # END fis-gtm-V7.0-005/sr_i386/opp_commarg.s0000755000032200000250000000137214342376327016252 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_commarg.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_commarg # PAGE + .DATA .extern frame_pointer .text .extern op_commarg # PUBLIC opp_commarg ENTRY opp_commarg putframe addl $4,%esp call op_commarg addl $8,%esp getframe ret # opp_commarg ENDP # END fis-gtm-V7.0-005/sr_i386/opp_dmode.s0000755000032200000250000000133614342376327015715 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_dmode.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_dmode # PAGE + .DATA .extern frame_pointer .text .extern op_dmode # PUBLIC opp_dmode ENTRY opp_dmode putframe addl $4,%esp call op_dmode getframe ret # opp_dmode ENDP # END fis-gtm-V7.0-005/sr_i386/opp_hardret.s0000755000032200000250000000136314342376327016256 0ustar librarygtc################################################################# # # # Copyright 2001, 2007 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. # # # ################################################################# # PAGE ,132 .title opp_hardret.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_hardret # PAGE + .DATA .extern frame_pointer .text .extern op_hardret # PUBLIC opp_hardret ENTRY opp_hardret putframe addl $4,%esp call op_hardret getframe ret # opp_hardret ENDP # END fis-gtm-V7.0-005/sr_i386/opp_inddevparms.s0000755000032200000250000000146614342376327017145 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_inddevparms.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_inddevparms # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_inddevparms # PUBLIC opp_inddevparms ENTRY opp_inddevparms # /* PROC */ putframe addl $4,%esp call op_inddevparms addl $12,%esp getframe ret # opp_inddevparms ENDP # END fis-gtm-V7.0-005/sr_i386/opp_indfnname.s0000755000032200000250000000141114342376327016556 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_indfnname.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_indfnname # PAGE + .DATA .extern frame_pointer .text .extern op_indfnname # PUBLIC opp_indfnname ENTRY opp_indfnname putframe addl $4,%esp call op_indfnname addl $12,%esp getframe ret # opp_indfnname ENDP # END fis-gtm-V7.0-005/sr_i386/opp_indfun.s0000755000032200000250000000142314342376327016105 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_indfun.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_indfun # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_indfun # PUBLIC opp_indfun ENTRY opp_indfun # /* PROC */ putframe addl $4,%esp call op_indfun addl $12,%esp getframe ret # opp_indfun ENDP # END fis-gtm-V7.0-005/sr_i386/opp_indglvn.s0000755000032200000250000000143114342376327016262 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_indglvn.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_indglvn # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_indglvn # PUBLIC opp_indglvn ENTRY opp_indglvn # /* PROC */ putframe addl $4,%esp call op_indglvn addl $8,%esp getframe ret # opp_indglvn ENDP # END fis-gtm-V7.0-005/sr_i386/opp_indincr.s0000755000032200000250000000143214342376327016250 0ustar librarygtc################################################################# # # # Copyright 2004 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_indincr.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_indincr # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_indincr # PUBLIC opp_indincr ENTRY opp_indincr # /* PROC */ putframe addl $4,%esp call op_indincr addl $12,%esp getframe ret # opp_indincr ENDP # END fis-gtm-V7.0-005/sr_i386/opp_indlvadr.s0000755000032200000250000000144014342376327016424 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_indlvadr.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_indlvadr # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_indlvadr # PUBLIC opp_indlvadr ENTRY opp_indlvadr # /* PROC */ putframe addl $4,%esp call op_indlvadr addl $4,%esp getframe ret # opp_indlvadr ENDP # END fis-gtm-V7.0-005/sr_i386/opp_indlvarg.s0000755000032200000250000000144014342376327016427 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_indlvarg.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_indlvarg # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_indlvarg # PUBLIC opp_indlvarg ENTRY opp_indlvarg # /* PROC */ putframe addl $4,%esp call op_indlvarg addl $8,%esp getframe ret # opp_indlvarg ENDP # END fis-gtm-V7.0-005/sr_i386/opp_indlvnamadr.s0000755000032200000250000000146414342376327017126 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_indlvnamadr.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_indlvnamadr # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_indlvnamadr # PUBLIC opp_indlvnamadr ENTRY opp_indlvnamadr # /* PROC */ putframe addl $4,%esp call op_indlvnamadr addl $4,%esp getframe ret # opp_indlvnamadr ENDP # END fis-gtm-V7.0-005/sr_i386/opp_indmerge.s0000755000032200000250000000144014342376327016413 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_indmerge.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_indmerge # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_indmerge # PUBLIC opp_indmerge ENTRY opp_indmerge # /* PROC */ putframe addl $4,%esp call op_indmerge addl $8,%esp getframe ret # opp_indmerge ENDP # END fis-gtm-V7.0-005/sr_i386/opp_indpat.s0000755000032200000250000000142214342376327016100 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_indpat.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_indpat # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_indpat # PUBLIC opp_indpat ENTRY opp_indpat # /* PROC */ putframe addl $4,%esp call op_indpat addl $8,%esp getframe ret # opp_indpat ENDP # END fis-gtm-V7.0-005/sr_i386/opp_indrzshow.s0000755000032200000250000000144714342376327016657 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_indrzshow.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_indrzshow # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_indrzshow # PUBLIC opp_indrzshow ENTRY opp_indrzshow # /* PROC */ putframe addl $4,%esp call op_indrzshow addl $8,%esp getframe ret # opp_indrzshow ENDP # END fis-gtm-V7.0-005/sr_i386/opp_indsavglvn.s0000644000032200000250000000155214342376327016775 0ustar librarygtc################################################################# # # # Copyright 2012 Fidelity Information Services, Inc # # # # This source code contains the intellectual property # # of its copyright holder(s), and is made available # # under a license. If you do not know the terms of # # the license, please stop and do not read further. # # # ################################################################# # PAGE ,132 .title opp_indsavglvn.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_indsavglvn # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_indsavglvn # PUBLIC opp_indsavglvn ENTRY opp_indsavglvn # /* PROC */ putframe addl $4,%esp # /* burn return pc */ call op_indsavglvn addl $12,%esp # /* burn three passed-in args */ getframe ret # opp_indsavglvn ENDP # END fis-gtm-V7.0-005/sr_i386/opp_indsavlvn.s0000644000032200000250000000154114342376327016624 0ustar librarygtc################################################################# # # # Copyright 2012 Fidelity Information Services, Inc # # # # This source code contains the intellectual property # # of its copyright holder(s), and is made available # # under a license. If you do not know the terms of # # the license, please stop and do not read further. # # # ################################################################# # PAGE ,132 .title opp_indsavlvn.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_indsavlvn # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_indsavlvn # PUBLIC opp_indsavlvn ENTRY opp_indsavlvn # /* PROC */ putframe addl $4,%esp # /* burn return pc */ call op_indsavlvn addl $8,%esp # /* burn two passed-in args */ getframe ret # opp_indsavlvn ENDP # END fis-gtm-V7.0-005/sr_i386/opp_indset.s0000755000032200000250000000142214342376327016107 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_indset.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_indset # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_indset # PUBLIC opp_indset ENTRY opp_indset # /* PROC */ putframe addl $4,%esp call op_indset addl $8,%esp getframe ret # opp_indset ENDP # END fis-gtm-V7.0-005/sr_i386/opp_indtext.s0000755000032200000250000000151314342376327016301 0ustar librarygtc################################################################# # # # Copyright 2001, 2002 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_indtext.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_indtext # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_indtext # PUBLIC opp_indtext ENTRY opp_indtext # /* PROC */ putframe addl $4,%esp # burn return PC call op_indtext addl $16,%esp # remove args from stack getframe ret # opp_indtext ENDP # END fis-gtm-V7.0-005/sr_i386/opp_iretmval.s0000755000032200000250000000153614342376327016452 0ustar librarygtc################################################################# # # # Copyright 2001, 2012 Fidelity Information Services, Inc # # # # This source code contains the intellectual property # # of its copyright holder(s), and is made available # # under a license. If you do not know the terms of # # the license, please stop and do not read further. # # # ################################################################# # PAGE ,132 .title opp_iretmval.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_iretmval # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_iretmval # PUBLIC opp_iretmval ENTRY opp_iretmval # /* PROC */ putframe addl $4,%esp # /* burn return pc */ call op_iretmval addl $8,%esp # /* burn two passed-in args */ getframe ret # opp_iretmval ENDP # END fis-gtm-V7.0-005/sr_i386/opp_newintrinsic.s0000755000032200000250000000147214342376327017342 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_newintrinsic.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_newintrinsic # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_newintrinsic # PUBLIC opp_newintrinsic ENTRY opp_newintrinsic # /* PROC */ putframe addl $4,%esp call op_newintrinsic addl $4,%esp getframe ret # opp_newintrinsic ENDP # END fis-gtm-V7.0-005/sr_i386/opp_newvar.s0000755000032200000250000000142014342376327016121 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_newvar.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_newvar # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_newvar # PUBLIC opp_newvar ENTRY opp_newvar # /* PROC */ putframe addl $4,%esp call op_newvar addl $4,%esp getframe ret # opp_newvar ENDP # END fis-gtm-V7.0-005/sr_i386/opp_ret.s0000755000032200000250000000135114342376327015414 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_ret.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_ret # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_unwind # PUBLIC opp_ret ENTRY opp_ret # /* PROC */ addl $4,%esp call op_unwind getframe ret # opp_ret ENDP #END fis-gtm-V7.0-005/sr_i386/opp_rterror.s0000755000032200000250000000151114342376327016317 0ustar librarygtc################################################################# # # # Copyright 2001, 2002 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_rterror.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_rterror # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_rterror # PUBLIC opp_rterror ENTRY opp_rterror # /* PROC */ putframe addl $4,%esp # burn return PC call op_rterror addl $8,%esp # remove args from stack getframe ret # opp_rterror ENDP # END fis-gtm-V7.0-005/sr_i386/opp_svput.s0000755000032200000250000000147314342376327016010 0ustar librarygtc################################################################# # # # Copyright 2001, 2002 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_svput.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_svput # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_svput # PUBLIC opp_svput ENTRY opp_svput # /* PROC */ putframe addl $4,%esp # burn return PC call op_svput addl $8,%esp # remove args from stack getframe ret # opp_svput ENDP # END fis-gtm-V7.0-005/sr_i386/opp_tcommit.s0000755000032200000250000000142114342376327016274 0ustar librarygtc################################################################# # # # Copyright 2001, 2007 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. # # # ################################################################# # PAGE ,132 .title opp_tcommit.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_tcommit # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_tcommit # PUBLIC opp_tcommit ENTRY opp_tcommit # /* PROC */ putframe addl $4,%esp call op_tcommit getframe ret # opp_tcommit ENDP # END fis-gtm-V7.0-005/sr_i386/opp_trestart.s0000755000032200000250000000144514342376327016476 0ustar librarygtc################################################################# # # # Copyright 2001, 2007 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. # # # ################################################################# # PAGE ,132 .title opp_trestart.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_trestart # PAGE + .DATA .extern frame_pointer # /*:DWORD */ .text .extern op_trestart # PUBLIC opp_trestart ENTRY opp_trestart # /* PROC */ putframe addl $4,%esp call op_trestart addl $4,%esp getframe ret # opp_trestart ENDP # END fis-gtm-V7.0-005/sr_i386/opp_trollback.s0000755000032200000250000000145314342376327016602 0ustar librarygtc################################################################# # # # Copyright 2001, 2003 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_trollback.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_trollback # PAGE + .DATA .extern frame_pointer # /*:DWORD */ .text .extern op_trollback # PUBLIC opp_trollback ENTRY opp_trollback # /* PROC */ putframe addl $4,%esp call op_trollback addl $4,%esp getframe ret # opp_trollback ENDP # END fis-gtm-V7.0-005/sr_i386/opp_tstart.s0000755000032200000250000000215114342376327016142 0ustar librarygtc################################################################# # # # 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. # # # ################################################################# # PAGE ,132 .title opp_tstart.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_tstart # PAGE + .DATA .extern frame_pointer # /* :DWORD */ .text .extern op_tstart # PUBLIC opp_tstart ENTRY opp_tstart # /* PROC */ putframe movl $0,%eax # put arg0 over return call address since movl %eax,(%esp) # .. we dont need it: NOT an implicit tstart call op_tstart movl 12(%esp),%eax # get number of variables to preserve cmpl $0,%eax # -1 = not restartable, jge l1 # -2 = preserve all variables movl $0,%eax l1: addl $4,%eax # total args to op_tstart == preservecnt + 4 leal (%esp,%eax,4),%esp getframe ret # opp_tstart ENDP # END fis-gtm-V7.0-005/sr_i386/opp_xnew.s0000755000032200000250000000137314342376327015607 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title opp_xnew.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_xnew # PAGE + .DATA .extern frame_pointer .text .extern op_xnew # PUBLIC opp_xnew ENTRY opp_xnew putframe addl $4,%esp call op_xnew popl %eax leal (%esp,%eax,4),%esp getframe ret # opp_xnew ENDP # END fis-gtm-V7.0-005/sr_i386/opp_zcont.s0000755000032200000250000000134514342376327015762 0ustar librarygtc################################################################# # # # Copyright 2001, 2007 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. # # # ################################################################# # PAGE ,132 .title opp_zcont.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_zcont # PAGE + .DATA .extern frame_pointer .text .extern op_zcont # PUBLIC opp_zcont ENTRY opp_zcont putframe addl $4,%esp call op_zcont getframe ret # opp_zcont ENDP # END fis-gtm-V7.0-005/sr_i386/opp_zg1.s0000644000032200000250000000141114342376327015315 0ustar librarygtc################################################################# # # # Copyright 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. # # # ################################################################# # PAGE ,132 .title opp_zg1.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_zg1 # PAGE + .DATA .extern frame_pointer .text .extern op_zg1 # PUBLIC opp_zg1 ENTRY opp_zg1 putframe addl $4,%esp # burn return pc call op_zg1 addl $4,%esp # burn passed-in arg getframe ret # opp_zg1 ENDP # END fis-gtm-V7.0-005/sr_i386/opp_zgoto.s0000644000032200000250000000144014342376327015760 0ustar librarygtc################################################################# # # # Copyright 2010, 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. # # # ################################################################# # PAGE ,132 .title opp_zgoto.s # .386 # .MODEL FLAT, C .include "linkage.si" .INCLUDE "g_msf.si" .sbttl opp_zgoto # PAGE + .DATA .extern frame_pointer .text .extern op_zgoto # PUBLIC opp_zgoto ENTRY opp_zgoto putframe addl $4,%esp # burn return pc call op_zgoto addl $16,%esp # burn passed in 4 args getframe ret # opp_zgoto ENDP # END fis-gtm-V7.0-005/sr_i386/pseudo_ret.s0000755000032200000250000000117614342376327016122 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title pseudo_ret.s .sbttl pseudo_ret # .386 # .MODEL FLAT, C .include "linkage.si" .text .extern opp_ret # PUBLIC pseudo_ret ENTRY pseudo_ret call opp_ret # pseudo_ret ENDP # END fis-gtm-V7.0-005/sr_i386/zbreaksp.h0000755000032200000250000000242714342376327015557 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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. * * * ****************************************************************/ typedef unsigned char zb_code; #define ZB_CODE_MASK 0xff #define INST_TYPE zb_code /* The ZBreak command operates by finding the generated code for the op_linestart or op_linefetch for the source * line in question and changing the offset in the transfer table load address instruction from the op_linestart or * op_linefetch offset to the appropriate zbreak functionality opcode offset. * In some platforms(IA64 and ZOS) since the INSTRUCTION LAYOUT is complex we need following * macros for instruction manipulation. * EXTRACT_OFFSET_TO_M_OPCODE * FIX_OFFSET_WITH_ZBREAK_OFFSET * EXTRACT_AND_UPDATE_INST * These macros are called only when COMPLEX_INSTRUCTION_UPDATE is defined * If COMPLEX_INSTRUCTION_UPDATE is not defined portable code in the caller of these macros * is invoked. */ #undef COMPLEX_INSTRUCTION_UPDATE fis-gtm-V7.0-005/sr_i386/aswp.s0000755000032200000250000000133114342376327014714 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title aswp.s .sbttl aswp .include "linkage.si" # .386 # .MODEL FLAT, C .text ENTRY aswp movl 4(%esp),%edx # A(latch longword) movl 8(%esp),%eax # replacement value # LOCK xchg (%edx),%eax # return original value lock xchgl (%edx),%eax # return original value ret fis-gtm-V7.0-005/sr_i386/auto_zlink.h0000755000032200000250000000117714342376327016116 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef AUTO_ZLINK_H_INCLUDED #define AUTO_ZLINK_H_INCLUDED rhdtyp *auto_zlink (unsigned char *pc, int4 **line); #endif fis-gtm-V7.0-005/sr_i386/call_dm.s0000755000032200000250000000130214342376327015333 0ustar librarygtc################################################################# # # # Copyright 2001, 2007 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. # # # ################################################################# # PAGE ,132 .title call_dm.s .sbttl call_dm .include "linkage.si" # .386 # .MODEL FLAT, C .DATA .text .extern op_oldvar .extern opp_dmode # PUBLIC call_dm # call_dm PROC ENTRY call_dm l1: call opp_dmode call op_oldvar jmp l1 ret # call_dm ENDP # END fis-gtm-V7.0-005/sr_i386/caller_id.s0000755000032200000250000000177214342376327015671 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2020 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. # # # ################################################################# # PAGE ,132 .title caller_id.s .sbttl caller_id # .386 # .MODEL FLAT, C .include "linkage.si" .text # PUBLIC caller_id ENTRY caller_id movl 4(%esp),%ecx # get extra frames count movl %ebp,%eax # copy frame pointer cmpl $0,%ecx # check for extra frames jle L2 L1: decl %ecx # reduce count of extra frames movl 0(%eax),%eax # get previous stack frame cmpl $0,%ecx # check for additional extra frames jg L1 L2: movl 4(%eax),%eax # address of caller's return address ret # caller_id ENDP # END fis-gtm-V7.0-005/sr_i386/callg.s0000755000032200000250000000273214342376327015032 0ustar librarygtc################################################################# # # # Copyright 2001, 2006 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. # # # ################################################################# # PAGE ,132 .title callg.s .sbttl callg .include "linkage.si" # .386 # .MODEL FLAT, C # Assembler routine to immitate the VAX CALLG instruction, which can redirect # a routine calls argument list to a prepared list in memory. # This implementation has the caveat that the argument list must be integers, # and that it follows our version of the VMS calling conventions - i.e. that # a count of the arguments following is actually the first thing on the stack. routarg = 8 argsarg = 12 cntsav = -4 .text ENTRY callg enter $4,$0 pushl %edi pushl %esi pushl %ebx movl routarg(%ebp),%edx # routine to call movl argsarg(%ebp),%esi # argument list movl (%esi),%ecx movl %ecx,cntsav(%ebp) # save following argument count addl $4,%esi # skip argument count movl %ecx, %eax negl %eax leal (%esp,%eax,4),%esp movl %esp,%edi pushl %ecx rep movsl call *%edx popl %edx # discard possibly modified count movl cntsav(%ebp),%edx # edx to preserve return value in eax leal (%esp,%edx,4),%esp popl %ebx popl %esi popl %edi leave ret fis-gtm-V7.0-005/sr_i386/ci_restart.s0000755000032200000250000000252514342376327016107 0ustar librarygtc################################################################# # # # Copyright 2001, 2003 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title ci_restart.s .sbttl ci_restart # .386 # .MODEL FLAT, C .include "linkage.si" .DATA .extern param_list .text ENTRY ci_restart pushl %ebp # save C frame pointer movl %esp,%ebp # set frame marker for this procedure movl param_list,%eax movl 4(%eax),%eax # argcnt cmpl $0,%eax # if (argcnt > 0) { jle L0 imull $4,%eax,%edx # param_list->args[argcnt] leal 20(%edx),%edx pushl %eax movl param_list,%eax addl %eax,%edx popl %eax L1: pushl 0(%edx) # pushing arguments backwards subl $4,%edx subl $1,%eax cmpl $0,%eax jg L1 # } L0: movl param_list,%eax pushl 4(%eax) #argcnt pushl 20(%eax) #mask pushl 16(%eax) #retaddr pushl 12(%eax) #labaddr pushl 8(%eax) #rtnaddr pushl 4(%ebp) # push the return address to the caller of ci_restart movl 0(%ebp),%ebp # restore previous C frame pointer jmp *(%eax) ret # ci_restart ENDP # END fis-gtm-V7.0-005/sr_i386/compswap.s0000755000032200000250000000150614342376327015577 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title compswap.s .sbttl compswap .include "linkage.si" # .386 # .MODEL FLAT, C .text ENTRY compswap movl 4(%esp),%edx # A(latch longword) movl 8(%esp),%eax # comparison value movl 12(%esp),%ecx # replacement value # LOCK cmpxchgl %ecx,(%edx) lock cmpxchgl %ecx,(%edx) # compare-n-swap jnz fail movl $1,%eax # return TRUE ret fail: xor %eax,%eax # return FALSE ret fis-gtm-V7.0-005/sr_i386/dm_start.s0000755000032200000250000000253614342376327015567 0ustar librarygtc################################################################# # # # 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. # # # ################################################################# # PAGE ,132 .title dm_start.s .sbttl dm_start # .386 # .MODEL FLAT, C .include "linkage.si" .include "error.si" .DATA .extern dollar_truth .extern xfer_table .extern frame_pointer .extern msp .extern mumps_status .extern restart .text .extern mdb_condition_handler .extern op_unwind .ifndef cygwin .type dm_start,@function .endif ENTRY dm_start enter $0,$0 pushl %edi pushl %esi pushl %ebx movl $1,mumps_status leal xfer_table,%ebx movl $1,dollar_truth ESTABLISH mdb_condition_handler, l30 call *restart return: movl mumps_status,%eax popl %ebx popl %esi popl %edi leave ret ENTRY gtm_ret_code REVERT call op_unwind movl msp,%eax movl (%eax),%eax movl %eax,frame_pointer addl $4,msp jmp return # Used by triggers (and eventually call-ins) to return from a nested generated code call # (a call not at the base C stack level). ENTRY gtm_levl_ret_code REVERT jmp return # dm_start ENDP # END fis-gtm-V7.0-005/sr_i386/emit_code.h0000755000032200000250000000130414342376327015657 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef EMIT_CODE_INCLUDED #define EMIT_CODE_INCLUDED void trip_gen(triple *ct); short *emit_vax_inst(short *inst, oprtype **fst_opr, oprtype **lst_opr); void emit_xfer(short xfer); void emit_base_offset (short reg_opcode, short base_reg, int4 offset); #endif fis-gtm-V7.0-005/sr_i386/error.si0000755000032200000250000000312214342376327015244 0ustar librarygtc################################################################# # # # Copyright 2001, 2014 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. # # # ################################################################# .sbttl error.si # PAGE + #----------------------------------------------- # Mumps error condition handler macros #----------------------------------------------- .ifdef cygwin # will need another 8 to save sigmasks chnd_size = 228 .else chnd_size = 176 .endif chnd_save_active = 0 chnd_ch_active = 4 chnd_ch = 12 chnd_jmp = 16 .data .extern ctxt .extern active_ch .text .ifdef cygwin # on cygwin, sigsetjmp is a macro which calls sigprocmask then setjmp .extern _setjmp .else # setjmp is really __sigsetjmp(env,0) .extern __sigsetjmp .extern gtm_asm_establish .endif .sbttl error.si ESTABLISH .macro ESTABLISH x, label call gtm_asm_establish # Bulk of ESTABLISH macro movl ctxt,%eax movl $\x,chnd_ch(%eax) # ctxt->ch = x addl $chnd_jmp,%eax # setjmp(ctxt->jmp) .ifndef cygwin pushl $0 .endif pushl %eax .ifdef cygwin call _setjmp addl $4,%esp .else call __sigsetjmp addl $8,%esp .endif incl %eax jne \label REVERT jmp return \label: .endm .sbttl error.si REVERT .macro REVERT movl ctxt,%eax # active_ch = ctxt->save_active_c movl chnd_save_active(%eax),%eax movl %eax,active_ch subl $chnd_size,ctxt # ctxt-- .endm fis-gtm-V7.0-005/sr_i386/incr_link.c0000644000032200000250000003167714342376333015704 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_unistd.h" #include "gtm_stdio.h" #include "gtm_string.h" #include #include #include "compiler.h" #include "urx.h" #include "objlabel.h" /* needed for masscomp.h */ #include "masscomp.h" #include "gtmio.h" #include "incr_link.h" #include "min_max.h" /* MIDENT_CMP needs MIN */ #include "cmd_qlf.h" /* needed for CQ_UTF8 */ #include "gtm_text_alloc.h" /* INCR_LINK - read and process a mumps object module. Link said module to currently executing image */ LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; static char *code; GBLREF mident_fixed zlink_mname; GBLREF boolean_t gtm_utf8_mode; error_def(ERR_INVOBJFILE); error_def(ERR_LOADRUNNING); error_def(ERR_TEXT); #define RELREAD 50 /* number of relocation entries to buffer */ typedef struct res_list_struct { struct res_list_struct *next, *list; unsigned int addr, symnum; } res_list; void res_free(res_list *root); bool addr_fix(int file, struct exec *fhead, urx_rtnref *urx_lcl, rhdtyp *code); void zl_error(int4 *file, int4 err, int4 len, char *addr, int4 err2, int4 len2, char *addr2); boolean_t incr_link(int *file_desc, zro_ent *dummy, uint4 fname_len, char *fname) { rhdtyp *hdr, *old_rhead; int code_size, save_errno, cnt; int4 rhd_diff, read_size; char *literal_ptr; var_tabent *curvar; char module_name[SIZEOF(mident_fixed)]; lab_tabent *lbt_ent, *lbt_bot, *lbt_top, *olbt_ent, *olbt_bot, *olbt_top, *curlab; urx_rtnref urx_lcl_anchor; int order; struct exec file_hdr; assert(file_desc && (FD_INVALID != *file_desc)); urx_lcl_anchor.len = 0; urx_lcl_anchor.addr = 0; urx_lcl_anchor.lab = 0; urx_lcl_anchor.next = 0; code = NULL; DOREADRL(*file_desc, &file_hdr, SIZEOF(file_hdr), read_size); if (read_size != SIZEOF(file_hdr)) { if (-1 == read_size) { save_errno = errno; zl_error(file_desc, ERR_INVOBJFILE, fname_len, fname, ERR_TEXT, strlen(STRERROR(save_errno)), STRERROR(save_errno)); } else zl_error(file_desc, ERR_INVOBJFILE, fname_len, fname, ERR_TEXT, RTS_ERROR_TEXT("reading file header")); } else if (OMAGIC != file_hdr.a_magic) zl_error(file_desc, ERR_INVOBJFILE, fname_len, fname, ERR_TEXT, RTS_ERROR_TEXT("bad magic")); else if (OBJ_LABEL != file_hdr.a_stamp) return IL_RECOMPILE; /* wrong version */ assert(0 == file_hdr.a_bss); code_size = file_hdr.a_text + file_hdr.a_data; code = GTM_TEXT_ALLOC(code_size); DOREADRL(*file_desc, code, code_size, read_size); if (read_size != code_size) { if (-1 == read_size) { save_errno = errno; zl_error(file_desc, ERR_INVOBJFILE, fname_len, fname, ERR_TEXT, strlen(STRERROR(save_errno)), STRERROR(save_errno)); } else zl_error(file_desc, ERR_INVOBJFILE, fname_len, fname, ERR_TEXT, RTS_ERROR_TEXT("reading code")); } hdr = (rhdtyp *)code; if (memcmp(&hdr->jsb[0], "GTM_CODE", SIZEOF(hdr->jsb))) zl_error(file_desc, ERR_INVOBJFILE, fname_len, fname, ERR_TEXT, RTS_ERROR_TEXT("missing GTM_CODE")); if ((hdr->compiler_qlf & CQ_UTF8) && !gtm_utf8_mode) zl_error(file_desc, ERR_INVOBJFILE, fname_len, fname, ERR_TEXT, RTS_ERROR_TEXT("Object compiled with CHSET=UTF-8 which is different from $ZCHSET")); if (!(hdr->compiler_qlf & CQ_UTF8) && gtm_utf8_mode) zl_error(file_desc, ERR_INVOBJFILE, fname_len, fname, ERR_TEXT, RTS_ERROR_TEXT("Object compiled with CHSET=M which is different from $ZCHSET")); literal_ptr = code + file_hdr.a_text; hdr->routine_source_offset += (uint4)literal_ptr; /* now an absolute pointer, not an offset */ for (cnt = hdr->vartab_len, curvar = VARTAB_ADR(hdr); cnt; --cnt, ++curvar) { /* relocate the variable table */ assert(0 < curvar->var_name.len); curvar->var_name.addr += (uint4)literal_ptr; } for (cnt = hdr->labtab_len, curlab = LABTAB_ADR(hdr); cnt; --cnt, ++curlab) /* relocate the label table */ curlab->lab_name.addr += (uint4)literal_ptr; if (!addr_fix(*file_desc, &file_hdr, &urx_lcl_anchor, hdr)) { urx_free(&urx_lcl_anchor); zl_error(file_desc, ERR_INVOBJFILE, fname_len, fname, ERR_TEXT, RTS_ERROR_TEXT("address fixup failure")); } if (!zlput_rname(hdr)) { urx_free(&urx_lcl_anchor); /* Copy routine name to local variable because zl_error free's it. */ memcpy(&module_name[0], hdr->routine_name.addr, hdr->routine_name.len); zl_error(file_desc, ERR_LOADRUNNING, hdr->routine_name.len, &module_name[0], 0, 0, NULL); } urx_add(&urx_lcl_anchor); old_rhead = (rhdtyp *)hdr->old_rhead_ptr; lbt_bot = (lab_tabent *)((char *)hdr + hdr->labtab_ptr); lbt_top = lbt_bot + hdr->labtab_len; while (old_rhead) { lbt_ent = lbt_bot; olbt_bot = (lab_tabent *)((char *)old_rhead + old_rhead->labtab_ptr); olbt_top = olbt_bot + old_rhead->labtab_len; for (olbt_ent = olbt_bot; olbt_ent < olbt_top; olbt_ent++) { for (; lbt_ent < lbt_top; lbt_ent++) { MIDENT_CMP(&olbt_ent->lab_name, &lbt_ent->lab_name, order); if (order <= 0) break; } if ((lbt_ent < lbt_top) && !order) { olbt_ent->lab_ln_ptr = lbt_ent->lab_ln_ptr; olbt_ent->has_parms = lbt_ent->has_parms; } else olbt_ent->lab_ln_ptr = 0; } rhd_diff = (char *)hdr - (char *)old_rhead; old_rhead->src_full_name = hdr->src_full_name; old_rhead->routine_name = hdr->routine_name; old_rhead->vartab_len = hdr->vartab_len; old_rhead->vartab_ptr = hdr->vartab_ptr + rhd_diff; old_rhead->ptext_ptr = hdr->ptext_ptr + rhd_diff; old_rhead->current_rhead_ptr = rhd_diff; old_rhead->temp_mvals = hdr->temp_mvals; old_rhead->temp_size = hdr->temp_size; old_rhead = (rhdtyp *) old_rhead->old_rhead_ptr; } urx_resolve(hdr, lbt_bot, lbt_top); return IL_DONE; } bool addr_fix(int file, struct exec *fhead, urx_rtnref *urx_lcl, rhdtyp *code) { res_list *res_root, *new_res, *res_temp, *res_temp1; char *symbols, *sym_temp, *sym_temp1, *symtop, *res_addr; struct relocation_info rel[RELREAD]; int numrel, rel_read, i, string_size; unsigned int sym_size; size_t status; mident_fixed rtnid, labid; mstr rtn_str; rhdtyp *rtn; lab_tabent *label, *labtop; bool labsym; urx_rtnref *urx_rp; urx_addr *urx_tmpaddr; res_root = 0; numrel = (fhead->a_trsize + fhead->a_drsize) / SIZEOF(struct relocation_info); if (numrel * SIZEOF(struct relocation_info) != fhead->a_trsize + fhead->a_drsize) return FALSE; for ( ; numrel;) { rel_read = numrel < RELREAD ? numrel : RELREAD; DOREADRC(file, rel, rel_read * SIZEOF(struct relocation_info), status); if (0 != status) { res_free(res_root); return FALSE; } numrel -= rel_read; for (i = 0; i < rel_read; i++) { if (rel[i].r_extern) { new_res = (res_list *)malloc(SIZEOF(*new_res)); new_res->symnum = rel[i].r_symbolnum; new_res->addr = rel[i].r_address; new_res->next = new_res->list = 0; /* Insert the relocation entry in symbol number order on the unresolved chain */ if (!res_root) res_root = new_res; else { res_temp = res_root; res_temp1 = 0; while (res_temp) { if (res_temp->symnum >= new_res->symnum) break; res_temp1 = res_temp; res_temp = res_temp->next; } if (res_temp) { if (res_temp->symnum == new_res->symnum) { new_res->list = res_temp->list; res_temp->list = new_res; } else { if (res_temp1) { new_res->next = res_temp1->next; res_temp1->next = new_res; } else { assert(res_temp == res_root); new_res->next = res_root; res_root = new_res; } } } else res_temp1->next = new_res; } } else *(unsigned int *)(((char *)code) + rel[i].r_address) += (unsigned int)code; } } /* All relocations within the routine should have been done, so copy the routine_name */ assert(code->routine_name.len < SIZEOF(zlink_mname.c)); memcpy(&zlink_mname.c[0], code->routine_name.addr, code->routine_name.len); zlink_mname.c[code->routine_name.len] = 0; if (!res_root) return TRUE; if ((off_t)-1 == lseek(file, (off_t)fhead->a_syms, SEEK_CUR)) { res_free(res_root); return FALSE; } DOREADRC(file, &string_size, SIZEOF(string_size), status); if (0 != status) { res_free(res_root); return FALSE; } string_size -= SIZEOF(string_size); symbols = malloc(string_size); DOREADRC(file, symbols, string_size, status); if (0 != status) { free(symbols); res_free(res_root); return FALSE; } /* Match up unresolved entries with the null terminated symbol name entries from the * symbol text pool we just read in. */ sym_temp = sym_temp1 = symbols; symtop = symbols + string_size; for (i = 0; res_root; i++) { while (i < res_root->symnum) { /* Forward symbol space until our symnum index (i) matches the symbol we are processing in res_root */ while (*sym_temp) { if (sym_temp >= symtop) { free(symbols); res_free(res_root); return FALSE; } sym_temp++; } sym_temp++; sym_temp1 = sym_temp; i++; } assert (i == res_root->symnum); /* Find end of routine name that we care about */ while (('.' != *sym_temp1) && *sym_temp1) { if (sym_temp1 >= symtop) { free(symbols); res_free(res_root); return FALSE; } sym_temp1++; } assert(sym_temp1 >= sym_temp); sym_size = (unsigned int)(sym_temp1 - sym_temp); assert(sym_size <= MAX_MIDENT_LEN); memcpy(&rtnid.c[0], sym_temp, sym_size); rtnid.c[sym_size] = 0; if ('_' == rtnid.c[0]) rtnid.c[0] = '%'; assert((sym_size != mid_len(&zlink_mname)) || (0 != memcmp(&zlink_mname.c[0], &rtnid.c[0], sym_size))); rtn_str.addr = &rtnid.c[0]; rtn_str.len = (mstr_len_t)sym_size; rtn = find_rtn_hdr(&rtn_str); /* Routine already resolved? */ sym_size = 0; labsym = FALSE; if (*sym_temp1 == '.') { /* If symbol is for a label, find the end of the label name */ sym_temp1++; sym_temp = sym_temp1; while (*sym_temp1) { if (sym_temp1 >= symtop) { free(symbols); res_free(res_root); return FALSE; } sym_temp1++; } assert(sym_temp1 >= sym_temp); sym_size = (unsigned int)(sym_temp1 - sym_temp); assert(sym_size <= MAX_MIDENT_LEN); memcpy(&labid.c[0], sym_temp, sym_size); labid.c[sym_size] = 0; if ('_' == labid.c[0]) labid.c[0] = '%'; labsym = TRUE; } sym_temp1++; sym_temp = sym_temp1; if (rtn) { /* The routine part at least is known */ if (labsym) { /* Look our target label up in the routines label table */ label = (lab_tabent *)((char *)rtn + rtn->labtab_ptr); labtop = label + rtn->labtab_len; for (; label < labtop && ((sym_size != label->lab_name.len) || memcmp(&labid.c[0], label->lab_name.addr, sym_size)); label++) ; if (label < labtop) res_addr = (char *)&label->LABENT_LNR_OFFSET; else res_addr = 0; } else res_addr = (char *)rtn; if (res_addr) { /* The external symbol definition is available. Resolve all references to it */ res_temp = res_root->next; while (res_root) { *(uint4 *)(((char *)code) + res_root->addr) = (unsigned int)res_addr; res_temp1 = res_root->list; free(res_root); res_root = res_temp1; } res_root = res_temp; continue; } } /* This symbol is unknown. Put on the (local) unresolved extern chain -- either for labels or routines */ urx_rp = urx_putrtn(rtn_str.addr, rtn_str.len, urx_lcl); res_temp = res_root->next; while (res_root) { if (labsym) urx_putlab(&labid.c[0], sym_size, urx_rp, ((char *)code) + res_root->addr); else { urx_tmpaddr = (urx_addr *)malloc(SIZEOF(urx_addr)); urx_tmpaddr->next = urx_rp->addr; urx_tmpaddr->addr = (INTPTR_T *)(((char *)code) + res_root->addr); urx_rp->addr = urx_tmpaddr; } res_temp1 = res_root->list; free(res_root); res_root = res_temp1; } res_root = res_temp; } free(symbols); return TRUE; } void res_free(res_list *root) { res_list *temp; while (root) { while (root->list) { temp = root->list->list; free(root->list); root->list = temp; } temp = root->next; free(root); root = temp; } } /* ZL_ERROR - perform cleanup and signal errors found in zlinking a mumps object module * err - an error code that accepts no arguments and * err2 - an error code that accepts two arguments (!AD) */ void zl_error(int4 *file, int4 err, int4 len, char *addr, int4 err2, int4 len2, char *addr2) { int rc; if (code) { GTM_TEXT_FREE(code); code = NULL; } if (0 < *file) { CLOSEFILE_RESET(*file, rc); /* resets "*file" to FD_INVALID */ } else *file = FD_INVALID; if (0 != err2) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) err, 2, len, addr, err2, 2, len2, addr2); else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) err, 2, len, addr); } fis-gtm-V7.0-005/sr_i386/follow.s0000755000032200000250000000130514342376327015245 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title follow.s .sbttl follow .include "linkage.si" # .386 # .MODEL FLAT, C .text .extern op_follow # PUBLIC follow ENTRY follow movl 4(%esp),%eax movl 8(%esp),%edx call op_follow jle l1 movl $1,%eax ret l1: movl $0,%eax ret # follow ENDP # END fis-gtm-V7.0-005/sr_i386/g_msf.si0000755000032200000250000000417414342376327015216 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2019 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. # # # ################################################################# .sbttl g_msf.si # PAGE + #----------------------------------------------- # Mumps stack frame manipulation macros # for the GNU gas i386 assembler version #----------------------------------------------- msf_rvector_off = 0 msf_l_symtab_off = 4 msf_mpc_off = 8 msf_ctxt_off = 12 msf_temps_ptr_off = 16 msf_vartab_ptr_off = 20 msf_vartab_len_off = 24 msf_temp_mvals_off = 26 msf_old_frame_off = 28 msf_typ_off = 32 msf_flags_off = 34 msf_for_ctrl_stack = 36 msf_restart_pc_off = 40 msf_restart_ctxt_off = 44 msf_frame_size = 52 SFT_COUNT = 0x01 SFT_DM = 0x02 SFT_REP_OP = 0x04 SFT_ZBRK_ACT = 0x08 SFT_DEV_ACT = 0x10 SFT_ZTRAP = 0x20 SFT_ZTIMEOUT = 0x40 SFT_ZSTEP_ACT = 0x80 SFT_ZINTR = 0x100 SFF_INDCE = 0x01 SFF_ZTRAP_ERR = 0x02 SFF_DEV_ACT_ERR = 0x04 SFF_CI = 0x08 SFF_ETRAP_ERR = 0x10 .sbttl g_msf.si putframe .macro putframe movl frame_pointer,%edx movl %edi,msf_temps_ptr_off(%edx) movl %esi,msf_l_symtab_off(%edx) movl (%esp),%eax movl %eax,msf_mpc_off(%edx) .endm .extern error_return .sbttl g_msf.si getframe .macro getframe movl frame_pointer,%edi movb msf_flags_off(%edi),%dl andb $SFF_ETRAP_ERR,%dl jz lab1\@ call error_return lab1\@: movl frame_pointer, %edx movl msf_temps_ptr_off(%edx),%edi movl msf_l_symtab_off(%edx),%esi pushl msf_mpc_off(%edx) .endm .sbttl g_msf.si mrt_jsb = 0 mrt_src_len = 12 mrt_src_addr = 16 mrt_rtn_len = 24 mrt_rtn_addr = 28 mrt_var_ptr = 32 mrt_var_len = 36 mrt_lab_ptr = 40 mrt_lab_len = 44 mrt_lnr_ptr = 48 mrt_lnr_len = 52 mrt_ptxt_ptr = 56 mrt_checksum = 60 mrt_compiler_qlf = 64 mrt_oldr_ptr = 68 mrt_curr_ptr = 72 mrt_tmp_mv = 76 mrt_tmp_sz = 78 fis-gtm-V7.0-005/sr_i386/gtm_threadgbl_asm_access.txt0000644000032200000250000000120214342376327021275 0ustar librarygtc################################################################# # # # Copyright (c) 2014, 2015 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. # # # ################################################################# # # This is an override file for the version in sr_unix. This platform does not (at this time) use this feature so has no entries. # fis-gtm-V7.0-005/sr_i386/linkage.si0000755000032200000250000000136214342376327015531 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, Inc. # # # # This source code contains the intellectual property # # of its copyright holder(s), and is made available # # under a license. If you do not know the terms of # # the license, please stop and do not read further. # # # ################################################################# # #define SYMBOL_NAME_STR(X) #X # #define SYMBOL_NAME(X) X # #ifdef __STDC__ # #define SYMBOL_NAME_LABEL(X) X##: # #else # #define SYMBOL_NAME_LABEL(X) X/**/: # #endif .macro SYMBOL_NAME_LABEL X \X: .endm .macro ENTRY name # .globl SYMBOL_NAME \name .globl \name .align 16,0x90 SYMBOL_NAME_LABEL \name .endm fis-gtm-V7.0-005/sr_i386/make_cimode.c0000644000032200000250000000664014342376333016161 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "gtm_string.h" #include "error.h" #include #include "op.h" #include "i386.h" #include "inst_flush.h" #include "gtmci.h" #include "gtm_text_alloc.h" #define CALL_SIZE 5 #define CODE_SIZE (3 * CALL_SIZE) #define CODE_LINES 3 /* The code created and returned by make_cimode() is executed in the frame GTM$CI at level 1 of * every nested call-in environment. For every M routine being called-in from C, GTM$CI code * will setup argument registers/stack and executes the M routine. When the M routine returns * from its final QUIT, GTM$CI returns to gtm_ci(). make_cimode generates machine equivalents * for the following operations in that order: * * CALL ci_restart :setup register/stack arguments from 'param_list' and transfer control * to op_extcall/op_extexfun which return only after the M routine finishes and QUITs. * CALL ci_ret_code :transfer control from the M routine back to C (gtm_ci). Never returns. * CALL opp_ret :an implicit QUIT although it is never executed. * * Before GTM$CI executes, it is assumed that the global 'param_list' has been populated with * argument/return mval*. */ rhdtyp *make_cimode(void) { static rhdtyp *base_address = NULL; lab_tabent *lbl; int *lnr; unsigned char *code; if (NULL != base_address) return base_address; base_address = (rhdtyp *)GTM_TEXT_ALLOC(SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent) + CODE_LINES * SIZEOF(int4)); memset(base_address,0,SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent) + CODE_LINES * SIZEOF(int4)); base_address->routine_name.len = STR_LIT_LEN(GTM_CIMOD); base_address->routine_name.addr = GTM_CIMOD; base_address->ptext_ptr = SIZEOF(rhdtyp); base_address->vartab_ptr = base_address->labtab_ptr = SIZEOF(rhdtyp) + CODE_SIZE; /* hdr + code */ base_address->lnrtab_ptr = SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent); base_address->labtab_len = 1; base_address->lnrtab_len = CODE_LINES; code = (unsigned char *) base_address + base_address->ptext_ptr; *code++ = I386_INS_CALL_Jv; *((int4 *)code) = (int4)((unsigned char *)ci_restart - (code + SIZEOF(int4))); code += SIZEOF(int4); *code++ = I386_INS_CALL_Jv; /* a CALL to return control from M to ci_ret_code() which in turn returns to gtm_ci() */ *((int4 *)code) = (int4)((unsigned char *)ci_ret_code - (code + SIZEOF(int4))); code += SIZEOF(int4); *code++ = I386_INS_JMP_Jv; *((int4 *)code) = (int4)((unsigned char *)opp_ret - (code + SIZEOF(int4))); code += SIZEOF(int4); lbl = (lab_tabent *)((int) base_address + base_address->labtab_ptr); lbl->lab_ln_ptr = base_address->lnrtab_ptr; lnr = (int *)((int)base_address + base_address->lnrtab_ptr); *lnr++ = base_address->ptext_ptr; *lnr++ = base_address->ptext_ptr; *lnr++ = base_address->ptext_ptr + 2 * CALL_SIZE; assert(code - ((unsigned char *)base_address + base_address->ptext_ptr) == CODE_SIZE); zlput_rname(base_address); inst_flush(base_address, SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent) + CODE_LINES * SIZEOF(int4)); return base_address; } fis-gtm-V7.0-005/sr_i386/masscomp.h0000755000032200000250000000451514342376327015560 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ struct exec { short a_magic; /* magic number */ short a_stamp; /* version stamp - RTU 2.0+ uses this - see below */ uint4 a_text; /* size of text segment */ uint4 a_data; /* size of initialized data */ uint4 a_bss; /* size of uninitialized data */ uint4 a_syms; /* size of symbol table */ uint4 a_entry; /* entry point */ uint4 a_trsize; /* size of text relocation */ uint4 a_drsize; /* size of data relocation */ }; /* * Format of a relocation datum. */ struct relocation_info { int r_address; /* address which is relocated */ unsigned int r_symbolnum:24, /* local symbol ordinal */ r_pcrel:1, /* was relocated pc relative already */ r_length:2, /* 0=byte, 1=word, 2=int4 */ r_extern:1, /* does not include value of sym referenced */ r_pad:4; /* nothing, yet */ }; struct rel_table { struct rel_table *next, *resolve; struct relocation_info r; }; /* * Format of a symbol table entry; this file is included by * and should be used if you aren't interested the a.out header * or relocation information. */ struct nlist { int4 n_strx; /* index into file string table */ unsigned char n_type; /* type flag, i.e. N_TEXT etc; see below */ char n_other; /* unused */ short n_desc; /* see */ uint4 n_value; /* value of this symbol (or sdb offset) */ }; struct sym_table { struct sym_table *next; struct nlist n; struct rel_table *resolve; unsigned short name_len; unsigned char name[1]; }; /* * Simple values for n_type. */ #define N_UNDF 0x0 /* undefined */ #define N_ABS 0x2 /* absolute */ #define N_TEXT 0x4 /* text */ #define N_DATA 0x6 /* data */ #define N_BSS 0x8 /* bss */ #define N_COMM 0x12 /* common (internal to ld) */ #define N_IPCOMM 0x16 /* initialized private */ #define N_PCOMM 0x18 /* uninitialized private */ #define N_FN 0x1f /* file name symbol */ #define N_EXT 01 /* external bit, or'ed in */ #define N_TYPE 0x1e /* mask for all the type bits */ fis-gtm-V7.0-005/sr_i386/mint2mval.s0000755000032200000250000000150114342376327015652 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # PAGE ,132 .title mint2mval.s # .386 # .MODEL FLAT, C .include "linkage.si" .include "mval_def.si" .sbttl mint2mval # PAGE + .text # -------------------------------- # mint2mval.s # Convert int to mval # -------------------------------- .extern i2mval # PUBLIC mint2mval ENTRY mint2mval pushl %edx leal (%eax),%eax pushl %eax call i2mval addl $8,%esp ret # mint2mval ENDP # END fis-gtm-V7.0-005/sr_i386/mum_tstart.s0000755000032200000250000000151114342376327016141 0ustar librarygtc################################################################# # # # Copyright 2001, 2002 Sanchez Computer Associates, 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. # # # ################################################################# .TITLE mum_tstart.s .include "linkage.si" .include "g_msf.si" #comment perhaps .sbttl mum_tstart .data .extern frame_pointer .extern proc_act_type .text .extern trans_code .extern inst_flush ENTRY mum_tstart addl $4,%esp # back up over return address cmpw $0,proc_act_type je l1 call trans_code l1: getframe leal xfer_table,%ebx call inst_flush # smw 99/11/24 is this needed ret fis-gtm-V7.0-005/sr_i386/obj_file.c0000644000032200000250000002451514342376333015476 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include #include "compiler.h" #include #include "obj_gen.h" #include "cgp.h" #include "mdq.h" #include "cmd_qlf.h" #include "objlabel.h" /* needed for masscomp.h */ #include "masscomp.h" #include "stringpool.h" #include "parse_file.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtmio.h" #include "mmemory.h" #include "obj_file.h" LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; GBLREF mliteral literal_chain; GBLREF char source_file_name[]; GBLREF unsigned short source_name_len; GBLREF command_qualifier cmd_qlf; GBLREF mident routine_name; GBLREF mident module_name; GBLREF boolean_t run_time; GBLREF int4 mlmax, mvmax; GBLREF int4 code_size, lit_addrs, lits_size; GBLREF spdesc stringpool; GBLDEF int4 psect_use_tab[GTM_LASTPSECT]; /* bytes of each psect in this module */ GBLREF char object_file_name[]; GBLREF short object_name_len; GBLREF int object_file_des; static short int current_psect; static char emit_buff[OBJ_EMIT_BUF_SIZE]; /* buffer for emit output */ static short int emit_buff_used; /* number of chars in emit_buff */ GBLREF uint4 txtrel_cnt; static uint4 cdlits; static struct rel_table *data_rel, *data_rel_end; static struct rel_table *text_rel, *text_rel_end; static struct sym_table *symbols; DEBUG_ONLY(static uint4 txtrel_cnt_in_hdr;) error_def(ERR_OBJFILERR); error_def(ERR_STRINGOFLOW); void create_object_file(rhdtyp *rhead) { struct exec hdr; assert(!run_time); init_object_file_name(); /* inputs: cmd_qlf.object_file, module_name; outputs: object_file_name, object_name_len */ object_file_des = mk_tmp_object_file(object_file_name, object_name_len); memcpy(&rhead->jsb[0], "GTM_CODE", SIZEOF(rhead->jsb)); emit_addr((char *)&rhead->src_full_name.addr - (char *)rhead, (int4)rhead->src_full_name.addr, (int4 *)&rhead->src_full_name.addr); emit_addr((char *)&rhead->routine_name.addr - (char *)rhead, (int4)rhead->routine_name.addr, (int4 *)&rhead->routine_name.addr); txtrel_cnt += 2; DEBUG_ONLY(txtrel_cnt_in_hdr = txtrel_cnt;) set_psect(GTM_CODE, 0); hdr.a_magic = OMAGIC; hdr.a_stamp = OBJ_LABEL; hdr.a_entry = 0; hdr.a_bss = 0; hdr.a_text = code_size; assert(0 == PADLEN(lits_size, NATIVE_WSIZE)); hdr.a_data = lits_size; /* and pad to even # */ hdr.a_syms = (mlmax + cdlits) * SIZEOF(struct nlist); hdr.a_trsize = txtrel_cnt * SIZEOF(struct relocation_info); hdr.a_drsize = lit_addrs * SIZEOF(struct relocation_info); emit_immed((char *)&hdr, SIZEOF(hdr)); memset(psect_use_tab, 0, SIZEOF(psect_use_tab)); emit_immed((char *)rhead, SIZEOF(*rhead)); } void finish_object_file(void) { assert(0 == PADLEN(lits_size, NATIVE_WSIZE)); resolve_sym(); output_relocation(); output_symbol(); if (emit_buff_used) buff_emit(); if ((off_t)-1 == lseek(object_file_des, (off_t)0, SEEK_SET)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_OBJFILERR, 2, object_name_len, object_file_name, errno); } void drop_object_file(void) { int rc; if (FD_INVALID != object_file_des) { rc = UNLINK(object_file_name); assert(!rc); CLOSEFILE_RESET(object_file_des, rc); /* resets "object_file_des" to FD_INVALID */ assert(!rc); } } void emit_addr(int4 refaddr, int4 offset, int4 *result) { struct rel_table *newrel; if (run_time) { unsigned char *ptr; ptr = stringpool.free; *result = offset - (int4) ptr; } else { *result = offset + code_size; newrel = (struct rel_table *) mcalloc(SIZEOF(struct rel_table)); newrel->next = (struct rel_table *) 0; newrel->resolve = 0; newrel->r.r_address = refaddr; newrel->r.r_symbolnum = N_DATA; newrel->r.r_pcrel = 0; newrel->r.r_length = 2; newrel->r.r_extern = 0; newrel->r.r_pad = 0; if (!text_rel) text_rel = text_rel_end = newrel; else { text_rel_end->next = newrel; text_rel_end = newrel; } } return; } void emit_pidr(int4 refoffset, int4 data_offset, int4 *result) { struct rel_table *newrel; assert(!run_time); refoffset += code_size; data_offset += code_size; *result = data_offset; newrel = (struct rel_table *) mcalloc(SIZEOF(struct rel_table)); newrel->next = (struct rel_table *)0; newrel->resolve = 0; newrel->r.r_address = refoffset; newrel->r.r_symbolnum = N_DATA; newrel->r.r_pcrel = 0; newrel->r.r_length = 2; newrel->r.r_extern = 0; newrel->r.r_pad = 0; if (!data_rel) data_rel = data_rel_end = newrel; else { data_rel_end->next = newrel; data_rel_end = newrel; } } void emit_reference(uint4 refaddr, mstr *name, uint4 *result) { struct sym_table *sym; struct rel_table *newrel; sym = define_symbol(0, name, 0); assert(sym); if (sym->n.n_type == (N_TEXT | N_EXT)) *result = sym->n.n_value; else { newrel = (struct rel_table *) mcalloc(SIZEOF(struct rel_table)); newrel->next = (struct rel_table *)0; newrel->resolve = 0; newrel->r.r_address = refaddr; newrel->r.r_symbolnum = 0; newrel->r.r_pcrel = 0; newrel->r.r_length = 2; newrel->r.r_extern = 1; newrel->r.r_pad = 0; if (!text_rel) text_rel = text_rel_end = newrel; else { text_rel_end->next = newrel; text_rel_end = newrel; } if (sym->resolve) newrel->resolve = sym->resolve; sym->resolve = newrel; *result = 0; } } /* * emit_immed * * Args: buffer of executable code, and byte count to be output. */ void emit_immed(char *source, uint4 size) { short int write; if (run_time) { if (!IS_STP_SPACE_AVAILABLE(size)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_STRINGOFLOW); memcpy(stringpool.free, source, size); stringpool.free += size; } else { while(size > 0) { write = SIZEOF(emit_buff) - emit_buff_used; write = size < write ? size : write; memcpy(emit_buff + emit_buff_used, source, write); size -= write; source += write; emit_buff_used += write; psect_use_tab[current_psect] += write; if (size) buff_emit(); } } } /* * buff_emit * * Args: buffer pointer, number of bytes to emit */ void buff_emit(void) { uint4 stat; if (-1 == write(object_file_des, emit_buff, emit_buff_used)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_OBJFILERR, 2, object_name_len, object_file_name, errno); emit_buff_used = 0; } void set_psect(unsigned char psect,unsigned char offset) { current_psect = psect; return; } /* * define_symbol * * Args: psect index, symbol name, symbol value. * * Description: Buffers a definition of a global symbol with the * given name and value in the given psect. */ struct sym_table *define_symbol(unsigned char psect, mstr *name, int4 value) { int cmp; struct sym_table *sym, *sym1, *newsym; sym = symbols; sym1 = 0; while(sym) { if ((cmp = memvcmp(name->addr, name->len, &sym->name[0], sym->name_len - 1)) <= 0) break; sym1 = sym; sym = sym->next; } if (cmp || !sym) { newsym = (struct sym_table *) mcalloc(SIZEOF(struct sym_table) + name->len); newsym->name_len = name->len + 1; memcpy(&newsym->name[0], name->addr, name->len); newsym->name[ name->len ] = 0; newsym->n.n_strx = 0; newsym->n.n_type = N_EXT; if (psect == GTM_CODE) newsym->n.n_type |= N_TEXT; /* if symbol is in GTM_CODE, it is defined */ else txtrel_cnt++; newsym->n.n_other = 0; newsym->n.n_desc = 0; newsym->n.n_value = value; newsym->resolve = 0; newsym->next = sym; if (sym1) sym1->next = newsym; else symbols = newsym; cdlits++; return 0; } if (!(sym->n.n_type & N_TEXT)) txtrel_cnt++; return sym; } void resolve_sym(void) { uint4 symnum; struct sym_table *sym; struct rel_table *rel; symnum = 0; sym = symbols; while (sym) { if (sym->resolve) { rel = sym->resolve; while (rel) { rel->r.r_symbolnum = symnum; rel = rel->resolve; } } symnum++; sym = sym->next; } } void output_relocation(void) { struct rel_table *rel; DEBUG_ONLY(int cnt;) DEBUG_ONLY(cnt = 0;) rel = text_rel; while (rel) { emit_immed((char *)&rel->r, SIZEOF(rel->r)); rel = rel->next; DEBUG_ONLY(cnt++;) } assert(cnt == txtrel_cnt_in_hdr); DEBUG_ONLY(cnt = 0;) rel = data_rel; while (rel) { emit_immed((char *)&rel->r, SIZEOF(rel->r)); rel = rel->next; DEBUG_ONLY(cnt++;) } assert(cnt == lit_addrs); } void output_symbol(void) { uint4 string_length; struct sym_table *sym; string_length = SIZEOF(int4); sym = symbols; while (sym) { sym->n.n_strx = string_length; emit_immed((char *)&sym->n, SIZEOF(sym->n)); string_length += sym->name_len; sym = sym->next; } emit_immed((char *)&string_length, SIZEOF(string_length)); sym = symbols; while (sym) { emit_immed((char *)&sym->name[0], sym->name_len); sym = sym->next; } } void obj_init(void) { cdlits = txtrel_cnt = 0; data_rel = text_rel = data_rel_end = text_rel_end = 0; symbols = 0; } void emit_literals(void) { uint4 offset, padsize; mliteral *p; set_psect(GTM_LITERALS, 0); offset = stringpool.free - stringpool.base; emit_immed((char *)stringpool.base, offset); /* comp_lits aligns the start of source path on a NATIVE_WSIZE boundary.*/ padsize = PADLEN(offset, NATIVE_WSIZE); if (padsize) { emit_immed(PADCHARS, padsize); offset += padsize; } emit_immed(source_file_name, source_name_len); offset += source_name_len; /* comp_lits aligns the start of routine_name on a NATIVE_WSIZE boundary.*/ padsize = PADLEN(offset, NATIVE_WSIZE); if (padsize) { emit_immed(PADCHARS, padsize); offset += padsize; } emit_immed(routine_name.addr, routine_name.len); offset += routine_name.len; /* comp_lits aligns the start of the literal area on a NATIVE_WSIZE boundary.*/ padsize = PADLEN(offset, NATIVE_WSIZE); if (padsize) { emit_immed(PADCHARS, padsize); offset += padsize; } dqloop(&literal_chain, que, p) { assert (p->rt_addr == offset); MV_FORCE_NUMD(&p->v); if (p->v.str.len) emit_pidr(p->rt_addr + ((char *) &p->v.str.addr - (char *)&p->v), p->v.str.addr - (char *) stringpool.base, (int4 *)&p->v.str.addr); else p->v.str.addr = 0; emit_immed((char *)&p->v, SIZEOF(p->v)); offset += SIZEOF(p->v); } assert(lits_size == offset); } fis-gtm-V7.0-005/sr_i386/find_line_call.c0000644000032200000250000000522414342376333016643 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "xfer_enum.h" #include "i386.h" #include /* Needed by zbreak.h */ #include "zbreak.h" zb_code *find_line_call(void *addr) { unsigned char *call_addr; union { ModR_M modrm; unsigned char byte; } modrm_byte; call_addr = (unsigned char *)addr; modrm_byte.byte = *(call_addr + 1); if ((I386_INS_Grp5_Prefix == *call_addr) && (I386_INS_CALL_Ev == modrm_byte.modrm.reg_opcode)) { call_addr++; assert(I386_REG_EBX == modrm_byte.modrm.r_m); call_addr++; if (I386_MOD32_BASE_DISP_8 == modrm_byte.modrm.mod) { if ((xf_linestart * SIZEOF(int4) == *call_addr) || (xf_zbstart * SIZEOF(int4) == *call_addr)) return (zb_code *)call_addr; call_addr++; } else { assert (I386_MOD32_BASE_DISP_32 == modrm_byte.modrm.mod); return (zb_code *)addr; } } modrm_byte.byte = *(call_addr + 1); if ((I386_INS_PUSH_Ib == *call_addr) || (I386_INS_PUSH_Iv == *call_addr)) { while ((I386_INS_PUSH_Ib == *call_addr) || (I386_INS_PUSH_Iv == *call_addr)) { if (I386_INS_PUSH_Ib == *call_addr) call_addr += 1 + SIZEOF(unsigned char); else { assert(I386_INS_PUSH_Iv == *call_addr); call_addr += 1 + SIZEOF(int4); } } modrm_byte.byte = *(call_addr + 1); if ((I386_INS_Grp5_Prefix != *call_addr++) || (I386_INS_CALL_Ev != modrm_byte.modrm.reg_opcode)) return (zb_code *)addr; assert((I386_MOD32_BASE_DISP_8 == modrm_byte.modrm.mod) || (I386_MOD32_BASE_DISP_32 == modrm_byte.modrm.mod)); assert(I386_REG_EBX == modrm_byte.modrm.r_m); call_addr++; if (I386_MOD32_BASE_DISP_8 == modrm_byte.modrm.mod) { if ((xf_linefetch * SIZEOF(int4) != *call_addr) && (xf_zbfetch * SIZEOF(int4) != *call_addr)) return (zb_code *)addr; } } else if ((I386_INS_Grp5_Prefix == *call_addr) && (I386_INS_CALL_Ev != modrm_byte.modrm.reg_opcode)) { call_addr++; assert((I386_MOD32_BASE_DISP_8 == modrm_byte.modrm.mod) || (I386_MOD32_BASE_DISP_32 == modrm_byte.modrm.mod)); assert(I386_REG_EBX == modrm_byte.modrm.r_m); call_addr++; if (I386_MOD32_BASE_DISP_8 == modrm_byte.modrm.mod) { if ((xf_linestart * SIZEOF(int4) != *call_addr) && (xf_zbstart * SIZEOF(int4) != *call_addr)) return (zb_code *)addr; } } return (zb_code *)call_addr; } fis-gtm-V7.0-005/sr_i386/auto_zlink.c0000644000032200000250000000557714342376333016113 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "gtm_string.h" #include "i386.h" #include "urx.h" #include #include "op.h" #include #define PEA_SZ 5 #define XFER_BYTE_SZ 3 #define XFER_LONG_SZ 6 #define INST_SZ 1 error_def(ERR_LABELUNKNOWN); error_def(ERR_ROUTINEUNKNOWN); rhdtyp *auto_zlink (unsigned char *pc, int4 **line) { char *adj_pc; /* address of PEA rtnref offset */ mstr rname; mident_fixed rname_local; urx_rtnref *rtnurx; mval rtn; rhdtyp *rhead; union { ModR_M modrm; unsigned char byte; } modrm_byte_byte, modrm_byte_long; /* ASSUMPTION -- The instruction previous to the current mpc is a transfer table jump. * This is either a byte or a int4 displacement off of ebx, instruction * size either 3 or 6 (prefix byte, ModR/M byte, 8- or 32-bit offset). */ modrm_byte_byte.modrm.reg_opcode = I386_INS_CALL_Ev; modrm_byte_byte.modrm.mod = I386_MOD32_BASE_DISP_8; modrm_byte_byte.modrm.r_m = I386_REG_EBX; modrm_byte_long.modrm.reg_opcode = I386_INS_CALL_Ev; modrm_byte_long.modrm.mod = I386_MOD32_BASE_DISP_32; modrm_byte_long.modrm.r_m = I386_REG_EBX; if ((*(pc - XFER_BYTE_SZ) == I386_INS_Grp5_Prefix) && (*(pc - XFER_BYTE_SZ + 1) == modrm_byte_byte.byte)) { assert(*(pc - XFER_BYTE_SZ - PEA_SZ) == I386_INS_PUSH_Iv); adj_pc = (char *)pc - XFER_BYTE_SZ - PEA_SZ; } else if ((*(pc - XFER_LONG_SZ) == I386_INS_Grp5_Prefix) && (*(pc - XFER_LONG_SZ + 1) == modrm_byte_long.byte)) { assert(*(pc - XFER_LONG_SZ - PEA_SZ) == I386_INS_PUSH_Iv); adj_pc = (char *)pc - XFER_LONG_SZ - PEA_SZ; } else GTMASSERT; if (azl_geturxrtn(adj_pc + INST_SZ, &rname, &rtnurx)) { assert((0 <= rname.len) && (MAX_MIDENT_LEN >= rname.len)); assert(rname.addr); /* Copy rname into local storage because azl_geturxrtn sets rname.addr to an address that is * freed during op_zlink and before the call to find_rtn_hdr. */ memcpy(rname_local.c, rname.addr, rname.len); rname.addr = rname_local.c; assert(rtnurx); assert(*(adj_pc - PEA_SZ) == I386_INS_PUSH_Iv); assert(azl_geturxlab(adj_pc - PEA_SZ + INST_SZ, rtnurx)); assert(!find_rtn_hdr(&rname)); rtn.mvtype = MV_STR; rtn.str.len = rname.len; rtn.str.addr = rname.addr; op_zlink (&rtn, 0); if (0 != (rhead = find_rtn_hdr(&rname))) /* note the assignment */ { *line = *(int4 **)(adj_pc - PEA_SZ + INST_SZ); if (!(*line)) rts_error(VARLSTCNT(1) ERR_LABELUNKNOWN); return rhead; } } rts_error(VARLSTCNT(1) ERR_ROUTINEUNKNOWN); return NULL; } fis-gtm-V7.0-005/sr_i386/emit_code.c0000644000032200000250000007576114342376333015666 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "compiler.h" #include #include "stack_frame.h" #include "opcode.h" #include "xfer_enum.h" #include "mdq.h" #include "vxi.h" #include "vxt.h" #include "cgp.h" #include "obj_gen.h" #include "i386.h" #include "obj_file.h" #include #include "hashtab_mname.h" #include "stddef.h" #define BUFFERED_CODE_SIZE 50 #define LONG_JUMP_OFFSET (0x7ffffffc) #define XFER_BYTE_INST_SIZE 3 #define XFER_LONG_INST_SIZE 6 #define BRB_INST_SIZE 2 #define JMP_LONG_INST_SIZE 5 /* index in ttt from start of call[sp] and forlcldo to xfer_table index */ #define CALL_4LCLDO_XFER 2 typedef enum { CLEAR, COMPARE, INCREMENT, JUMP, LOAD, LOAD_ADDRESS, PUSH, PUSH_ADDRESS, STORE, TEST } generic_op; void emit_pcrel(generic_op op, unsigned char use_reg); void emit_trip(generic_op op, oprtype *opr, bool val_output, unsigned char use_reg); void emit_op_base_offset(generic_op op, short base_reg, int offset, short use_reg); void emit_op_alit (generic_op op, unsigned char use_reg); void emit_jmp(short vax_in, short **instp); unsigned char i386_reg(unsigned char vax_reg); union { ModR_M modrm; unsigned char byte; } modrm_byte; union { SIB sib; unsigned char byte; } sib_byte; LITREF octabstruct oc_tab[]; /* op-code table */ LITREF short ttt[]; /* triple templates */ static unsigned char code_buf[BUFFERED_CODE_SIZE]; static unsigned short code_idx; static int4 jmp_offset, code_reference; static int force_32; static int call_4lcldo_variant; /* used in emit_jmp for call[sp] and forlcldo */ GBLREF int4 curr_addr; GBLREF char cg_phase; /* code generation phase */ GBLDEF uint4 txtrel_cnt; /* count of text relocation records */ /* its referenced in ind_code.c */ GBLDEF int calculated_code_size, generated_code_size; error_def(ERR_UNIMPLOP); error_def(ERR_MAXARGCNT); void trip_gen(triple *ct) { oprtype **sopr, *opr; /* triple operand */ oprtype *saved_opr[MAX_ARGS]; uint4 oct; short tp; /* template pointer */ short *tsp; /* template short pointer */ triple *ttp; /* temp triple pointer */ short irep_index; oprtype *irep_opr; short *repl, repcnt; /* temp irep ptr */ int4 off; tp = ttt[ct->opcode]; if (tp <= 0) { stx_error(ERR_UNIMPLOP); return; } code_idx = 0; code_reference = ct->rtaddr; oct = oc_tab[ct->opcode].octype; sopr = &saved_opr[0]; *sopr++ = &ct->destination; for (ttp = ct, opr = ttp->operand ; opr < ARRAYTOP(ttp->operand); ) { if (opr->oprclass) { if (opr->oprclass == TRIP_REF && opr->oprval.tref->opcode == OC_PARAMETER) { ttp = opr->oprval.tref; opr = ttp->operand; continue; } *sopr++ = opr; if (sopr >= ARRAYTOP(saved_opr)) /* user-visible max args is MAX_ARGS - 3 */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_MAXARGCNT, 1, MAX_ARGS - 3); } opr++; } *sopr=0; jmp_offset = 0; call_4lcldo_variant = 0; if (oct & OCT_JUMP || ct->opcode == OC_LDADDR || ct->opcode == OC_FORLOOP) { if (ct->operand[0].oprval.tref->rtaddr == 0) /* forward reference */ { jmp_offset = LONG_JUMP_OFFSET; assert(cg_phase == CGP_APPROX_ADDR); } else jmp_offset = ct->operand[0].oprval.tref->rtaddr - ct->rtaddr; switch (ct->opcode) { case OC_CALL: case OC_FORLCLDO: case OC_CALLSP: /* Changes to emit_xfer, emit_base_offset, or emit_jmp may require changes * here since we try to predict how big the call into the xfer_table * and the following jump will be. * There is also an assumption that both the word and long variants * of the opcode will be followed by a jmp with 32 bit offset while * the -BYTE variants will be followed by a BRB with an 8 bit offset. */ tsp = (short *)&ttt[ttt[tp]]; if (-128 <= tsp[CALL_4LCLDO_XFER] && 127 >= tsp[CALL_4LCLDO_XFER]) off = jmp_offset - XFER_BYTE_INST_SIZE; else off = jmp_offset - XFER_LONG_INST_SIZE; if (-128 <= (off - BRB_INST_SIZE) && 127 >= (off - BRB_INST_SIZE)) call_4lcldo_variant = BRB_INST_SIZE; /* used by emit_jmp */ else { call_4lcldo_variant = JMP_LONG_INST_SIZE; /* used by emit_jmp */ tsp = (short *)&ttt[ttt[tp + 1]]; if (-128 <= tsp[CALL_4LCLDO_XFER] && 127 >= tsp[CALL_4LCLDO_XFER]) off = jmp_offset - XFER_BYTE_INST_SIZE; else off = jmp_offset - XFER_LONG_INST_SIZE; if (-32768 > (off - JMP_LONG_INST_SIZE) && 32767 < (off - JMP_LONG_INST_SIZE)) tsp = (short *)&ttt[ttt[tp + 2]]; } break; case OC_JMP: case OC_JMPEQU: case OC_JMPGEQ: case OC_JMPGTR: case OC_JMPLEQ: case OC_JMPNEQ: case OC_JMPLSS: case OC_JMPTSET: case OC_JMPTCLR: case OC_LDADDR: case OC_FORLOOP: tsp = (short *)&ttt[ttt[tp]]; break; default: assertpro(FALSE && ct->opcode); break; } } else if (oct & OCT_COERCE) { switch (oc_tab[ct->operand[0].oprval.tref->opcode].octype & (OCT_VALUE | OCT_BOOL)) { case OCT_MVAL: tp = ttt[tp]; break; case OCT_MINT: tp = ttt[tp + 3]; break; case OCT_BOOL: tp = ttt[tp + 4]; break; default: assertpro(FALSE && (oc_tab[ct->operand[0].oprval.tref->opcode].octype & (OCT_VALUE | OCT_BOOL))); break; } tsp = (short *)&ttt[tp]; } else tsp = (short *)&ttt[tp]; for (; *tsp != VXT_END; ) { if (*tsp == VXT_IREPAB || *tsp == VXT_IREPL) { repl = tsp; repl += 2; repcnt = *repl++; assert(repcnt != 1); for (irep_index = repcnt, irep_opr = &ct->operand[1]; irep_index > 2; --irep_index) { assert(irep_opr->oprclass == TRIP_REF); irep_opr = &irep_opr->oprval.tref->operand[1]; } if (irep_opr->oprclass == TRIP_REF) { repl = tsp; do { tsp = repl; tsp = emit_vax_inst(tsp, &saved_opr[0], --sopr); } while (sopr > &saved_opr[repcnt]); } else { sopr = &saved_opr[repcnt]; tsp = repl; } } else { assert(*tsp > 0 && *tsp <= 511); tsp = emit_vax_inst(tsp, &saved_opr[0], sopr); }/* else */ }/* for */ } short *emit_vax_inst(short *inst, oprtype **fst_opr, oprtype **lst_opr) /* fst_opr and lst_opr are triple operands */ { short sav_in; bool oc_int; int4 cnt; oprtype *opr; triple *ct; code_idx = 0; force_32 = 0; switch (cg_phase) { case CGP_ADDR_OPT: case CGP_APPROX_ADDR: case CGP_MACHINE: switch ((sav_in = *inst++)) { case VXI_BEQL: case VXI_BGEQ: case VXI_BGTR: case VXI_BLEQ: case VXI_BLSS: case VXI_BNEQ: case VXI_BRB: case VXI_BRW: emit_jmp(sav_in, &inst); break; case VXI_BLBC: case VXI_BLBS: assert(VXT_REG == *inst); inst++; inst++; emit_xfer(4*xf_dt_get); code_buf[code_idx++] = I386_INS_CMP_eAX_Iv; *((int4 *)&code_buf[code_idx]) = 0; code_idx += SIZEOF(int4); if (sav_in == VXI_BLBC) emit_jmp(VXI_BEQL, &inst); else { assert(sav_in == VXI_BLBS); emit_jmp(VXI_BNEQ, &inst); } break; case VXI_BICB2: case VXI_BISB2: assert(VXT_LIT == *inst); inst++; assert(1 == *inst); inst++; assert(VXT_REG == *inst); inst++; inst++; if (sav_in == VXI_BICB2) emit_xfer(4*xf_dt_false); else { assert(sav_in == VXI_BISB2); emit_xfer(4*xf_dt_true); } break; case VXI_CALLS: oc_int = TRUE; if (VXT_LIT == *inst) { inst++; cnt = (int4) *inst++; } else { assert(VXT_VAL == *inst); inst++; opr = *(fst_opr + *inst); assert(opr->oprclass == TRIP_REF); ct = opr->oprval.tref; if (ct->destination.oprclass) { opr = &ct->destination; } if (opr->oprclass == TRIP_REF) { assert(ct->opcode == OC_ILIT); cnt = ct->operand[0].oprval.ilit; if (cnt >= -128 && cnt <= 127) { code_buf[code_idx++] = I386_INS_PUSH_Ib; code_buf[code_idx++] = cnt & 0xff; } else { code_buf[code_idx++] = I386_INS_PUSH_Iv; *((int4 *)&code_buf[code_idx]) = cnt; code_idx += SIZEOF(int4); } cnt++; inst++; } else { assert(opr->oprclass == TINT_REF); oc_int = FALSE; opr = *(fst_opr + *inst++); emit_trip(PUSH, opr, TRUE, 0); } } assert(VXT_XFER == *inst); inst++; emit_xfer(*inst++); if (oc_int) { if (cnt) { code_buf[code_idx++] = I386_INS_LEA_Gv_M; emit_base_offset(I386_REG_ESP, I386_REG_ESP, 4*cnt); } } else { emit_trip(LOAD, opr, TRUE, I386_REG_EDX); code_buf[code_idx++] = I386_INS_LEA_Gv_M; emit_base_offset(I386_REG_ESP, I386_REG_ESP, 4); } break; case VXI_CLRL: assert(VXT_VAL == *inst); inst++; emit_trip(CLEAR, *(fst_opr + *inst++), TRUE, 0); break; case VXI_CMPL: assert(VXT_VAL == *inst); inst++; emit_trip(LOAD, *(fst_opr + *inst++), TRUE, I386_REG_EDX); assert(VXT_VAL == *inst); inst++; emit_trip(COMPARE, *(fst_opr + *inst++), TRUE, I386_REG_EDX); break; case VXI_INCL: assert(VXT_VAL == *inst); inst++; emit_trip(INCREMENT, *(fst_opr + *inst++), TRUE, 0); break; case VXI_JMP: if (VXT_VAL == *inst) { inst++; emit_trip(JUMP, *(fst_opr + *inst++), FALSE, 0); } else { emit_jmp(sav_in, &inst); } break; case VXI_JSB: assert(VXT_XFER == *inst); inst++; emit_xfer(*inst++); break; case VXI_MOVAB: if (VXT_JMP == *inst) { inst += 2; emit_pcrel(LOAD_ADDRESS, I386_REG_EAX); assert(VXT_ADDR == *inst); inst++; emit_trip(STORE, *(fst_opr + *inst++), FALSE, I386_REG_EAX); } else if ((VXT_ADDR == *inst) || (VXT_VAL == *inst)) { bool addr; unsigned char reg; short save_inst; addr = (VXT_VAL == *inst); inst++; save_inst = *inst++; assert(VXT_REG == *inst); inst++; reg = ((*inst++ & 0x01) ? I386_REG_EDX : I386_REG_EAX); /* r0 and r1 are only ones used */ emit_trip(LOAD_ADDRESS, *(fst_opr + save_inst), addr, reg); } else assertpro(FALSE && *inst); break; case VXI_MOVC3: assert(VXT_LIT == *inst); inst += 2; assert(VXT_VAL == *inst); inst++; code_buf[code_idx++] = I386_INS_PUSH_eSI; code_buf[code_idx++] = I386_INS_PUSH_eDI; emit_trip(LOAD_ADDRESS, *(fst_opr + *inst++), TRUE, I386_REG_ECX); assert(VXT_VAL == *inst); inst++; emit_trip(LOAD_ADDRESS, *(fst_opr + *inst++), TRUE, I386_REG_EDI); code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; modrm_byte.modrm.reg_opcode = I386_REG_ESI; modrm_byte.modrm.mod = I386_MOD32_REGISTER; modrm_byte.modrm.r_m = I386_REG_ECX; code_buf[code_idx++] = modrm_byte.byte; code_buf[code_idx++] = I386_INS_MOV_eCX; *((int4 *)&code_buf[code_idx]) = (int4)SIZEOF(mval); code_idx += SIZEOF(int4); code_buf[code_idx++] = I386_INS_REP_E_Prefix; code_buf[code_idx++] = I386_INS_MOVSB_Xb_Yb; code_buf[code_idx++] = I386_INS_POP_eDI; code_buf[code_idx++] = I386_INS_POP_eSI; break; case VXI_MOVL: if (VXT_REG == *inst) { inst++; if (*inst > 0x5f) /* OC_CURRHD */ /* any mode >= 6 (deferred), any register */ { inst++; assert(VXT_ADDR == *inst); inst++; emit_xfer(4*xf_get_msf); emit_op_base_offset(LOAD, I386_REG_EAX, 0, I386_REG_EAX); emit_trip(STORE, *(fst_opr + *inst++), FALSE, I386_REG_EAX); } else { bool addr; assert(0x50 == *inst); /* register mode: R0 */ inst++; if ((VXT_VAL == *inst) || (VXT_ADDR == *inst)) { addr = (VXT_VAL == *inst); inst++; emit_trip(STORE, *(fst_opr + *inst++), addr, I386_REG_EAX); } else if (VXT_REG == *inst) { unsigned char reg; inst++; if ((*inst & 0x0f) == 10) /* VAX $TEST */ { code_buf[code_idx++] = I386_INS_PUSH_eAX; emit_xfer(4*xf_dt_store); code_buf[code_idx++] = I386_INS_POP_eAX; } else { code_buf[code_idx++] = I386_INS_MOV_Ev_Gv; modrm_byte.modrm.reg_opcode = I386_REG_EAX; modrm_byte.modrm.mod = I386_MOD32_REGISTER; modrm_byte.modrm.r_m = i386_reg(*inst); code_buf[code_idx++] = modrm_byte.byte; } inst++; } else assertpro(FALSE && *inst); } } else if (VXT_VAL == *inst) { inst++; emit_trip(LOAD, *(fst_opr + *inst++), TRUE, I386_REG_EDX); assert(VXT_REG == *inst); inst++; assert(0x51 == *inst); /* register mode: R1 */ inst++; } else assertpro(FALSE && *inst); break; case VXT_IREPAB: assert(VXT_VAL == *inst); inst += 2; emit_trip(PUSH_ADDRESS, *lst_opr, TRUE, 0); break; case VXI_PUSHAB: if (VXT_JMP == *inst) { inst += 2; emit_pcrel(PUSH_ADDRESS, 0); } else if (VXT_VAL == *inst) { inst++; emit_trip(PUSH_ADDRESS, *(fst_opr + *inst++), TRUE, 0); } else assertpro(FALSE && *inst); break; case VXT_IREPL: assert(VXT_VAL == *inst); inst += 2; emit_trip(PUSH, *lst_opr, TRUE, 0); break; case VXI_PUSHL: if (VXT_LIT == *inst) { int4 lit; inst++; lit = *inst++; if (lit >= -128 && lit <= 127) { code_buf[code_idx++] = I386_INS_PUSH_Ib; code_buf[code_idx++] = lit & 0xff; } else { code_buf[code_idx++] = I386_INS_PUSH_Iv; *((int4 *)&code_buf[code_idx]) = lit; code_idx += SIZEOF(int4); } } else if (VXT_ADDR == *inst) { inst++; emit_trip(PUSH, *(fst_opr + *inst++), FALSE, 0); } else if (VXT_VAL == *inst) { inst++; emit_trip(PUSH, *(fst_opr + *inst++), TRUE, 0); } else assertpro(FALSE && *inst); break; case VXI_TSTL: if (VXT_VAL == *inst) { inst++; emit_trip(TEST, *(fst_opr + *inst++), TRUE, 0); } else if (VXT_REG == *inst) { inst++; code_buf[code_idx++] = I386_INS_CMP_eAX_Iv; assert(I386_REG_EAX == i386_reg(*inst)); /* VAX R0 */ inst++; *((int4 *)&code_buf[code_idx]) = 0; /* 32 bit immediate 0 */ code_idx += SIZEOF(int4); } else assertpro(FALSE && *inst); break; default: assertpro(FALSE && sav_in); } break; default: assertpro(FALSE && cg_phase); break; } assert(code_idx < BUFFERED_CODE_SIZE); if (cg_phase == CGP_MACHINE) { generated_code_size += code_idx; emit_immed ((char *)&code_buf[0], SIZEOF(unsigned char) * code_idx); } else if (cg_phase != CGP_ASSEMBLY) { if (cg_phase == CGP_APPROX_ADDR) { calculated_code_size += code_idx; } curr_addr += SIZEOF(unsigned char) * code_idx; } code_reference += SIZEOF(unsigned char) * code_idx; jmp_offset -= SIZEOF(unsigned char) * code_idx; return inst; } /* Changes here or emit_xfer may require changes in trip_gen case for OC_CALL[SP] and FORLCLDO */ void emit_jmp(short vax_in, short **instp) { assert(jmp_offset != 0); jmp_offset -= code_idx * SIZEOF(code_buf[0]); /* size of this particular instruction */ assert(**instp == VXT_JMP); *instp += 1; assert(**instp == 1); *instp += 1; if (jmp_offset == 0) { code_buf[code_idx++] = I386_INS_NOP__; } else if ((jmp_offset - 2) >= -128 && (jmp_offset - 2) <= 127 && JMP_LONG_INST_SIZE != call_4lcldo_variant) { jmp_offset -= 2; switch (vax_in) { case VXI_BEQL: code_buf[code_idx++] = I386_INS_JZ_Jb; break; case VXI_BGEQ: code_buf[code_idx++] = I386_INS_JNL_Jb; break; case VXI_BGTR: code_buf[code_idx++] = I386_INS_JNLE_Jb; break; case VXI_BLEQ: code_buf[code_idx++] = I386_INS_JLE_Jb; break; case VXI_BLSS: code_buf[code_idx++] = I386_INS_JL_Jb; break; case VXI_BNEQ: code_buf[code_idx++] = I386_INS_JNZ_Jb; break; case VXI_BRB: case VXI_BRW: case VXI_JMP: assert(0 == call_4lcldo_variant || BRB_INST_SIZE == call_4lcldo_variant); code_buf[code_idx++] = I386_INS_JMP_Jb; break; default: assertpro(FALSE && vax_in); break; } code_buf[code_idx++] = jmp_offset & 0xff; } else { if (vax_in == VXI_BRB || vax_in == VXI_BRW || vax_in == VXI_JMP) { assert(0 == call_4lcldo_variant || JMP_LONG_INST_SIZE == call_4lcldo_variant); jmp_offset -= SIZEOF(int4) + 1; code_buf[code_idx++] = I386_INS_JMP_Jv; } else { jmp_offset -= SIZEOF(int4) + 2; code_buf[code_idx++] = I386_INS_Two_Byte_Escape_Prefix; switch (vax_in) { case VXI_BEQL: code_buf[code_idx++] = I386_INS_JZ_Jv; break; case VXI_BGEQ: code_buf[code_idx++] = I386_INS_JNL_Jv; break; case VXI_BGTR: code_buf[code_idx++] = I386_INS_JNLE_Jv; break; case VXI_BLEQ: code_buf[code_idx++] = I386_INS_JLE_Jv; break; case VXI_BLSS: code_buf[code_idx++] = I386_INS_JL_Jv; break; case VXI_BNEQ: code_buf[code_idx++] = I386_INS_JNZ_Jv; break; default: assertpro(FALSE && vax_in); break; } } *((int4 *)&code_buf[code_idx]) = jmp_offset; code_idx += SIZEOF(int4); } } void emit_pcrel(generic_op op, unsigned char use_reg) { code_buf[code_idx++] = I386_INS_CALL_Jv; *((int4 *)&code_buf[code_idx]) = 0; code_idx += SIZEOF(int4); jmp_offset -= code_idx; code_buf[code_idx++] = I386_INS_POP_eAX; emit_op_base_offset(op, I386_REG_EAX, jmp_offset, use_reg); } GBLREF boolean_t run_time; GBLREF int4 sa_temps_offset[]; GBLREF int4 sa_temps[]; LITREF int4 sa_class_sizes[]; void emit_trip(generic_op op, oprtype *opr, bool val_output, unsigned char use_reg) { unsigned char base_reg, temp_reg; int4 offset, literal; triple *ct; if (opr->oprclass == TRIP_REF) { ct = opr->oprval.tref; if (ct->destination.oprclass) { opr = &ct->destination; } /* else lit or error */ } switch (cg_phase) { case CGP_ADDR_OPT: case CGP_APPROX_ADDR: switch (opr->oprclass) { case TRIP_REF: assert(ct->destination.oprclass == 0); assert(val_output); switch (ct->opcode) { case OC_LIT: if (run_time) { int4 pc_value_idx; switch (op) { case LOAD_ADDRESS: temp_reg = use_reg; break; case PUSH: case PUSH_ADDRESS: temp_reg = I386_REG_ECX; break; default: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); break; } pc_value_idx = code_idx + 5; code_idx += 1 + SIZEOF(int4) + 1; emit_addr(0, (int4)ct->operand[0].oprval.mlit->rt_addr, &offset); offset -= pc_value_idx; force_32 = 1; emit_op_base_offset(op, temp_reg, offset, temp_reg); force_32 = 0; } else { emit_op_alit(op, use_reg); code_idx += SIZEOF(int4); } if (cg_phase == CGP_APPROX_ADDR) txtrel_cnt++; break; case OC_CDLIT: if (cg_phase == CGP_APPROX_ADDR) define_symbol(GTM_LITERALS, ct->operand[0].oprval.cdlt, 0); emit_op_alit(op, use_reg); code_idx += SIZEOF(int4); break; case OC_ILIT: literal = ct->operand[0].oprval.ilit; switch(op) { case COMPARE: /* 1byte(opcode) + 1byte(ModR/M) + 4byte(literal) */ code_idx += 2 + SIZEOF(int4); break; case LOAD: code_idx += 1 + SIZEOF(int4); break; case PUSH: if (literal >= -128 && literal <= 127) code_idx += 2; else code_idx += 1 + SIZEOF(int4); break; default: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); break; } break; default: assertpro(FALSE && ct->opcode); break; } break; case TINT_REF: case TVAL_REF: assert(val_output); offset = sa_temps_offset[opr->oprclass]; offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; emit_op_base_offset(op, I386_REG_EDI, offset, use_reg); break; case TCAD_REF: case TVAD_REF: case TVAR_REF: offset = sa_temps_offset[opr->oprclass]; offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; if (opr->oprclass == TVAR_REF) base_reg = I386_REG_ESI; else base_reg = I386_REG_EDI; switch (op) { case JUMP: if (val_output) { code_idx++; emit_base_offset(I386_REG_EAX, base_reg, offset); } code_idx++; if (val_output) emit_base_offset(I386_INS_JMP_Ev, I386_REG_EAX, 0); else emit_base_offset(I386_INS_JMP_Ev, base_reg, offset); break; case LOAD_ADDRESS: code_idx++; emit_base_offset(use_reg, base_reg, offset); if (opr->oprclass == TVAR_REF) { code_idx++; emit_base_offset(use_reg, use_reg, offsetof(ht_ent_mname, value)); } break; case PUSH: if (!val_output) { code_idx++; emit_base_offset(I386_INS_PUSH_Ev, base_reg, offset); } else { code_idx++; emit_base_offset(I386_REG_ECX, base_reg, offset); code_idx++; emit_base_offset(I386_INS_PUSH_Ev, I386_REG_ECX, 0); } break; case PUSH_ADDRESS: if (val_output) { if (opr->oprclass == TVAR_REF) { code_idx++; emit_base_offset(use_reg, base_reg, offset); code_idx++; emit_base_offset(I386_INS_PUSH_Ev, use_reg, offsetof(ht_ent_mname, value)); } else { code_idx++; emit_base_offset(I386_INS_PUSH_Ev, base_reg, offset); } } else { code_idx++; emit_base_offset(I386_REG_ECX, base_reg, offset); code_idx++; } break; case STORE: if (val_output) { if (use_reg == I386_REG_EAX) temp_reg = I386_REG_EDX; else temp_reg = I386_REG_EAX; code_idx++; emit_base_offset(temp_reg, base_reg, offset); } code_idx++; if (val_output) emit_base_offset(use_reg, temp_reg, 0); else emit_base_offset(use_reg, base_reg, offset); break; default: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); break; } break; } break; case CGP_MACHINE: switch (opr->oprclass) { case TRIP_REF: assert(ct->destination.oprclass == 0); assert(val_output); switch (ct->opcode) { case OC_LIT: assert(ct->operand[0].oprclass == MLIT_REF); if (run_time) { int4 pc_value_idx; switch(op) { case LOAD_ADDRESS: temp_reg = use_reg; break; case PUSH: case PUSH_ADDRESS: temp_reg = I386_REG_ECX; break; default: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); break; } code_buf[code_idx++] = I386_INS_CALL_Jv; *((int4 *)&code_buf[code_idx]) = 0; code_idx += SIZEOF(int4); pc_value_idx = code_idx; code_buf[code_idx++] = I386_INS_POP_eAX + temp_reg; emit_addr(0, (int4)ct->operand[0].oprval.mlit->rt_addr, &offset); offset -= pc_value_idx; force_32 = 1; emit_op_base_offset(op, temp_reg, offset, temp_reg); force_32 = 0; } else { emit_op_alit(op, use_reg); emit_addr(code_reference + (code_idx * SIZEOF(unsigned char)), (int4)ct->operand[0].oprval.mlit->rt_addr, (int4 *)&code_buf[code_idx]); code_idx += SIZEOF(int4); } break; case OC_CDLIT: emit_op_alit(op, use_reg); emit_reference(code_reference + (code_idx * SIZEOF(unsigned char)), ct->operand[0].oprval.cdlt, (uint4 *)&code_buf[code_idx]); code_idx += SIZEOF(int4); break; case OC_ILIT: literal = ct->operand[0].oprval.ilit; switch (op) { case COMPARE: /* cmpl $literal,use_reg - 1byte(opcode) + 1byte(ModR/M) + 4byte(literal) */ code_buf[code_idx++] = I386_INS_Grp1_Ev_Iv_Prefix; modrm_byte.modrm.reg_opcode = I386_INS_CMP__; modrm_byte.modrm.mod = I386_MOD32_REGISTER; modrm_byte.modrm.r_m = use_reg; code_buf[code_idx++] = modrm_byte.byte; *((int4 *)&code_buf[code_idx]) = literal; code_idx += SIZEOF(int4); break; case LOAD: code_buf[code_idx++] = I386_INS_MOV_eAX + use_reg; *((int4 *)&code_buf[code_idx]) = literal; code_idx += SIZEOF(int4); break; case PUSH: if (literal >= -128 && literal <= 127) { code_buf[code_idx++] = I386_INS_PUSH_Ib; code_buf[code_idx++] = literal & 0xff; } else { code_buf[code_idx++] = I386_INS_PUSH_Iv; *((int4 *)&code_buf[code_idx]) = literal; code_idx += SIZEOF(int4); } break; default: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); break; } break; default: assertpro(FALSE && ct->opcode); break; } break; case TINT_REF: case TVAL_REF: assert(val_output); offset = sa_temps_offset[opr->oprclass]; offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; emit_op_base_offset(op, I386_REG_EDI, offset, use_reg); break; case TCAD_REF: case TVAD_REF: case TVAR_REF: offset = sa_temps_offset[opr->oprclass]; offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; if (opr->oprclass == TVAR_REF) base_reg = I386_REG_ESI; else base_reg = I386_REG_EDI; switch (op) { case JUMP: assert(use_reg == 0); if (val_output) { code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; emit_base_offset(I386_REG_EAX, base_reg, offset); } code_buf[code_idx++] = I386_INS_Grp5_Prefix; if (val_output) emit_base_offset(I386_INS_JMP_Ev, I386_REG_EAX, 0); else emit_base_offset(I386_INS_JMP_Ev, base_reg, offset); break; case LOAD_ADDRESS: if (val_output) code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; else code_buf[code_idx++] = I386_INS_LEA_Gv_M; emit_base_offset(use_reg, base_reg, offset); if (opr->oprclass == TVAR_REF) { code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; emit_base_offset(use_reg, use_reg, offsetof(ht_ent_mname, value)); } break; case PUSH: if (val_output) { code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; emit_base_offset(I386_REG_ECX, base_reg, offset); code_buf[code_idx++] = I386_INS_Grp5_Prefix; emit_base_offset(I386_INS_PUSH_Ev, I386_REG_ECX, 0); } else { code_buf[code_idx++] = I386_INS_Grp5_Prefix; emit_base_offset(I386_INS_PUSH_Ev, base_reg, offset); } break; case PUSH_ADDRESS: if (val_output) { if (opr->oprclass == TVAR_REF) { code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; emit_base_offset(use_reg, base_reg, offset); code_buf[code_idx++] = I386_INS_Grp5_Prefix; emit_base_offset(I386_INS_PUSH_Ev, use_reg, offsetof(ht_ent_mname, value)); } else { code_buf[code_idx++] = I386_INS_Grp5_Prefix; emit_base_offset(I386_INS_PUSH_Ev, base_reg, offset); } } else { code_buf[code_idx++] = I386_INS_LEA_Gv_M; emit_base_offset(I386_REG_ECX, base_reg, offset); code_buf[code_idx++] = I386_INS_PUSH_eCX; } break; case STORE: if (val_output) { if (use_reg == I386_REG_EAX) temp_reg = I386_REG_EDX; else temp_reg = I386_REG_EAX; assert(temp_reg != use_reg); code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; emit_base_offset(temp_reg, base_reg, offset); } code_buf[code_idx++] = I386_INS_MOV_Ev_Gv; if (val_output) emit_base_offset(use_reg, temp_reg, 0); else emit_base_offset(use_reg, base_reg, offset); break; default: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); break; } break; default: assertpro(FALSE && opr->oprclass); break; } break; default: assertpro(FALSE && cg_phase); break; } } /* Changes here, emit_base_offset, or emit_jmp may require changes in trip_gen case for OC_CALL[SP] and FORLCLDO */ void emit_xfer(short xfer) { code_buf[code_idx++] = I386_INS_Grp5_Prefix; emit_base_offset(I386_INS_CALL_Ev, I386_REG_EBX, (int4)xfer); } void emit_op_base_offset(generic_op op, short base_reg, int offset, short use_reg) { switch (op) { case CLEAR: code_buf[code_idx++] = I386_INS_MOV_Ev_Iv; emit_base_offset(0, base_reg, offset); *((int4 *)&code_buf[code_idx]) = 0; code_idx += SIZEOF(int4); break; case COMPARE: code_buf[code_idx++] = I386_INS_CMP_Gv_Ev; emit_base_offset(use_reg, base_reg, offset); break; case INCREMENT: code_buf[code_idx++] = I386_INS_Grp5_Prefix; emit_base_offset(I386_INS_INC_Ev, base_reg, offset); break; case LOAD: code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; emit_base_offset(use_reg, base_reg, offset); break; case LOAD_ADDRESS: code_buf[code_idx++] = I386_INS_LEA_Gv_M; emit_base_offset(use_reg, base_reg, offset); break; case PUSH: code_buf[code_idx++] = I386_INS_Grp5_Prefix; emit_base_offset(I386_INS_PUSH_Ev, base_reg, offset); break; case PUSH_ADDRESS: code_buf[code_idx++] = I386_INS_LEA_Gv_M; emit_base_offset(use_reg, base_reg, offset); code_buf[code_idx++] = I386_INS_PUSH_eAX + use_reg; break; case STORE: code_buf[code_idx++] = I386_INS_MOV_Ev_Gv; emit_base_offset(use_reg, base_reg, offset); break; case TEST: code_buf[code_idx++] = I386_INS_Grp1_Ev_Ib_Prefix; emit_base_offset(I386_INS_CMP__, base_reg, offset); code_buf[code_idx++] = 0; break; default: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); break; } } /* Changes here, emit_base_offset, or emit_jmp may require changes in trip_gen case for OC_CALL[SP] and FORLCLDO */ void emit_base_offset (short reg_opcode, short base_reg, int4 offset) { modrm_byte.modrm.reg_opcode = reg_opcode; if (offset == 0) modrm_byte.modrm.mod = I386_MOD32_BASE; else if ((offset >= -128 && offset <= 127) && force_32 == 0) modrm_byte.modrm.mod = I386_MOD32_BASE_DISP_8; else modrm_byte.modrm.mod = I386_MOD32_BASE_DISP_32; if (base_reg == I386_REG_ESP || (base_reg == I386_REG_EBP && offset == 0)) { modrm_byte.modrm.r_m = I386_REG_SIB_FOLLOWS; code_buf[code_idx++] = modrm_byte.byte; sib_byte.sib.base = base_reg; sib_byte.sib.ss = I386_SS_TIMES_1; sib_byte.sib.index = I386_REG_NO_INDEX; code_buf[code_idx++] = sib_byte.byte; } else { modrm_byte.modrm.r_m = base_reg; code_buf[code_idx++] = modrm_byte.byte; } if (offset == 0) ; else if ((offset >= -128 && offset <= 127) && force_32 == 0) code_buf[code_idx++] = offset & 0xff; else { *((int4 *)&code_buf[code_idx]) = offset; code_idx += SIZEOF(int4); } } void emit_op_alit (generic_op op, unsigned char use_reg) { switch (op) { case LOAD_ADDRESS: code_buf[code_idx++] = I386_INS_MOV_eAX + use_reg; break; case PUSH: code_buf[code_idx++] = I386_INS_Grp5_Prefix; modrm_byte.modrm.reg_opcode = I386_INS_PUSH_Ev; modrm_byte.modrm.mod = I386_MOD32_BASE; modrm_byte.modrm.r_m = I386_REG_disp32_NO_BASE; code_buf[code_idx++] = modrm_byte.byte; break; case PUSH_ADDRESS: code_buf[code_idx++] = I386_INS_PUSH_Iv; break; default: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); break; } } unsigned char i386_reg(unsigned char vax_reg) { unsigned char reg; switch (vax_reg & 0xf) /* mask out VAX register mode field */ { case 0: reg = I386_REG_EAX; break; case 1: reg = I386_REG_EDX; break; case 8: reg = I386_REG_ESI; break; case 9: reg = I386_REG_EDI; break; case 11: reg = I386_REG_EBX; break; default: assertpro(FALSE && (vax_reg & 0xf)); break; } return reg; } fis-gtm-V7.0-005/sr_x86_regs/0000755000032200000250000000000014342376327014531 5ustar librarygtcfis-gtm-V7.0-005/sr_x86_regs/i386.h0000644000032200000250000000434114342376327015375 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2007-2016 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. * * * ****************************************************************/ #ifndef I386_H_INCLUDE #define I386_H_INCLUDE #define I386_OP(mnemonic,addressoperand,number) I386_INS_ ## mnemonic ## _ ## addressoperand, enum one_byte_opcode { #include "i386_ops.h" NUM_ONE_BYTE_OPCODES }; enum two_byte_opcode { #include "i386_ops_2b.h" NUM_TWO_BYTE_OPCODES }; enum group1_opcode { #include "i386_ops_g1.h" NUM_GROUP_1_OPCODES }; enum group2_opcode { #include "i386_ops_g2.h" NUM_GROUP_2_OPCODES }; enum group3_opcode { #include "i386_ops_g3.h" NUM_GROUP_3_OPCODES }; enum group4_opcode { #include "i386_ops_g4.h" NUM_GROUP_4_OPCODES }; enum group5_opcode { #include "i386_ops_g5.h" NUM_GROUP_5_OPCODES }; enum group6_opcode { #include "i386_ops_g6.h" NUM_GROUP_6_OPCODES }; enum group7_opcode { #include "i386_ops_g7.h" NUM_GROUP_7_OPCODES }; enum group8_opcode { #include "i386_ops_g8.h" NUM_GROUP_8_OPCODES }; #define I386_MOD(mnemonic,number) I386_MOD16_ ## mnemonic, enum mod16 { #include "i386_mod_16.h" NUM_16_BIT_MODS }; #undef I386_MOD #define I386_MOD(mnemonic,number) I386_MOD32_ ## mnemonic, enum mod32 { #include "i386_mod_32.h" NUM_32_BIT_MODS }; #define SS_CODE(mnemonic,number) I386_SS_ ## mnemonic, enum ssval { #include "i386_ss.h" NUM_SS_CODES }; #define REGDEF(mnemonic,number) I386_REG_ ## mnemonic #ifdef __x86_64__ enum reg64 { #include "i386_reg64.h" NUM_64_BIT_REGISTERS }; #endif /* __x86_64__ */ enum reg32 { #include "i386_reg32.h" NUM_32_BIT_REGISTERS }; enum reg16 { #include "i386_reg16.h" NUM_16_BIT_REGISTERS }; enum reg8 { #include "i386_reg8.h" NUM_8_BIT_REGISTERS }; typedef struct { unsigned char r_m : 3; unsigned char reg_opcode : 3; unsigned char mod : 2; } ModR_M; typedef struct { unsigned char base : 3; unsigned char index : 3; unsigned char ss : 2; } SIB; #endif fis-gtm-V7.0-005/sr_x86_regs/i386_mod_16.h0000644000032200000250000000111214342376327016533 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* Mod field values for 16-bit addressing forms: */ I386_MOD(BASE,0) I386_MOD(BASE_DISP_8,1) I386_MOD(BASE_DISP_16,2) I386_MOD(REGISTER,3) fis-gtm-V7.0-005/sr_x86_regs/i386_mod_32.h0000644000032200000250000000111214342376327016531 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* Mod field values for 32-bit addressing forms: */ I386_MOD(BASE,0) I386_MOD(BASE_DISP_8,1) I386_MOD(BASE_DISP_32,2) I386_MOD(REGISTER,3) fis-gtm-V7.0-005/sr_x86_regs/i386_ops.h0000644000032200000250000001611214342376327016255 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* Opcodes: */ I386_OP(ADD,Eb_Gb,0x00) I386_OP(ADD,Ev_Gv,0x01) I386_OP(ADD,Gb_Eb,0x02) I386_OP(ADD,Gv_Ev,0x03) I386_OP(ADD,AL_Ib,0x04) I386_OP(ADD,eAX_Iv,0x05) I386_OP(PUSH,ES,0x06) I386_OP(POP,ES,0x07) I386_OP(OR,Eb_Gb,0x08) I386_OP(OR,Ev_Gv,0x09) I386_OP(OR,Gb_Eb,0x0a) I386_OP(OR,Gv_Ev,0x0b) I386_OP(OR,AL_Ib,0x0c) I386_OP(OR,eAX_Iv,0x0d) I386_OP(PUSH,CS,0x0e) I386_OP(Two_Byte_Escape,Prefix,0x0f) I386_OP(ADC,Eb_Gb,0x10) I386_OP(ADC,Ev_Gv,0x11) I386_OP(ADC,Gb_Eb,0x12) I386_OP(ADC,Gv_Ev,0x13) I386_OP(ADC,AL_Ib,0x14) I386_OP(ADC,eAX_Iv,0x15) I386_OP(PUSH,SS,0x16) I386_OP(POP,SS,0x17) I386_OP(SBB,Eb_Gb,0x18) I386_OP(SBB,Ev_Gv,0x19) I386_OP(SBB,Gb_Eb,0x1a) I386_OP(SBB,Gv_Ev,0x1b) I386_OP(SBB,AL_Ib,0x1c) I386_OP(SBB,eAX_Iv,0x1d) I386_OP(PUSH,DS,0x1e) I386_OP(POP,DS,0x1f) I386_OP(AND,Eb_Gb,0x20) I386_OP(AND,Ev_Gv,0x21) I386_OP(AND,Gb_Eb,0x22) I386_OP(AND,Gv_Ev,0x23) I386_OP(AND,AL_Ib,0x24) I386_OP(AND,eAX_Iv,0x25) I386_OP(SEG,ES,0x26) I386_OP(DAA,_,0x27) I386_OP(SUB,Eb_Gb,0x28) I386_OP(SUB,Ev_Gv,0x29) I386_OP(SUB,Gb_Eb,0x2a) I386_OP(SUB,Gv_Ev,0x2b) I386_OP(SUB,AL_Ib,0x2c) I386_OP(SUB,eAX_Iv,0x2d) I386_OP(SEG,CS,0x2e) I386_OP(DAS,_,0x2f) I386_OP(XOR,Eb_Gb,0x30) I386_OP(XOR,Ev_Gv,0x31) I386_OP(XOR,Gb_Eb,0x32) I386_OP(XOR,Gv_Ev,0x33) I386_OP(XOR,AL_Ib,0x34) I386_OP(XOR,eAX_Iv,0x35) I386_OP(SEG,SS,0x36) I386_OP(AAA,_,0x37) I386_OP(CMP,Eb_Gb,0x38) I386_OP(CMP,Ev_Gv,0x39) I386_OP(CMP,Gb_Eb,0x3a) I386_OP(CMP,Gv_Ev,0x3b) I386_OP(CMP,AL_Ib,0x3c) I386_OP(CMP,eAX_Iv,0x3d) I386_OP(SEG,DS,0x3e) I386_OP(AAS,_,0x3f) /* On x86-64, these opcodes are used as REX Prefixes.. * * the one byte INC and DEC instructions are not there in the 64 bit mode !!! * */ #ifndef __x86_64 I386_OP(INC,eAX,0x40) I386_OP(INC,eCX,0x41) I386_OP(INC,eDX,0x42) I386_OP(INC,eBX,0x43) I386_OP(INC,eSP,0x44) I386_OP(INC,eBP,0x45) I386_OP(INC,eSI,0x46) I386_OP(INC,eDI,0x47) I386_OP(DEC,eAX,0x48) I386_OP(DEC,eCX,0x49) I386_OP(DEC,eDX,0x4a) I386_OP(DEC,eBX,0x4b) I386_OP(DEC,eSP,0x4c) I386_OP(DEC,eBP,0x4d) I386_OP(DEC,eSI,0x4e) I386_OP(DEC,eDI,0x4f) #else I386_OP(REX_PREFIX,None,0x40) I386_OP(REX_PREFIX,_B,0x41) I386_OP(REX_PREFIX,_X,0x42) I386_OP(REX_PREFIX,_X_B,0x43) I386_OP(REX_PREFIX,_R,0x44) I386_OP(REX_PREFIX,_R_B,0x45) I386_OP(REX_PREFIX,_R_X,0x46) I386_OP(REX_PREFIX,_R_X_B,0x47) I386_OP(REX_PREFIX,_W,0x48) I386_OP(REX_PREFIX,_W_B,0x49) I386_OP(REX_PREFIX,_W_X,0x4a) I386_OP(REX_PREFIX,_W_X_B,0x4b) I386_OP(REX_PREFIX,_W_R,0x4c) I386_OP(REX_PREFIX,_W_R_B,0x4d) I386_OP(REX_PREFIX,_W_R_X,0x4e) I386_OP(REX_PREFIX,_W_R_X_B,0x4f) #endif /* __x86_64 */ I386_OP(PUSH,eAX,0x50) I386_OP(PUSH,eCX,0x51) I386_OP(PUSH,eDX,0x52) I386_OP(PUSH,eBX,0x53) I386_OP(PUSH,eSP,0x54) I386_OP(PUSH,eBP,0x55) I386_OP(PUSH,eSI,0x56) I386_OP(PUSH,eDI,0x57) I386_OP(POP,eAX,0x58) I386_OP(POP,eCX,0x59) I386_OP(POP,eDX,0x5a) I386_OP(POP,eBX,0x5b) I386_OP(POP,eSP,0x5c) I386_OP(POP,eBP,0x5d) I386_OP(POP,eSI,0x5e) I386_OP(POP,eDI,0x5f) I386_OP(PUSHA,_,0x60) I386_OP(POPA,_,0x61) I386_OP(BOUND,Gv_Ma,0x62) #ifdef __i386 I386_OP(ARPL,Ew_Rw,0x63) #else /* __x86_64 */ I386_OP(MOVSXD,Gv_Ev,0x63) #endif /* __i386 */ I386_OP(SEG,FS,0x64) I386_OP(SEG,GS,0x65) I386_OP(Operand_Size,Prefix,0x66) I386_OP(Address_Size,Prefix,0x67) I386_OP(PUSH,Iv,0x68) I386_OP(IMUL,GvEvIv,0x69) I386_OP(PUSH,Ib,0x6a) I386_OP(IMUL,GvEvIb,0x6b) I386_OP(INSB,Yb_DX,0x6c) I386_OP(INSW_D,Yv_DX,0x6d) I386_OP(OUTSB,DX_Xb,0x6e) I386_OP(OUTSW_D,DX_Xv,0x6f) I386_OP(JO,Jb,0x70) I386_OP(JNO,Jb,0x71) I386_OP(JB,Jb,0x72) I386_OP(JNB,Jb,0x73) I386_OP(JZ,Jb,0x74) I386_OP(JNZ,Jb,0x75) I386_OP(JBE,Jb,0x76) I386_OP(JNBE,Jb,0x77) I386_OP(JS,Jb,0x78) I386_OP(JNS,Jb,0x79) I386_OP(JP,Jb,0x7a) I386_OP(JNP,Jb,0x7b) I386_OP(JL,Jb,0x7c) I386_OP(JNL,Jb,0x7d) I386_OP(JLE,Jb,0x7e) I386_OP(JNLE,Jb,0x7f) I386_OP(Grp1,Eb_Ib_Prefix,0x80) I386_OP(Grp1,Ev_Iv_Prefix,0x81) I386_OP(MOVB,AL_imm8,0x82) I386_OP(Grp1,Ev_Ib_Prefix,0x83) I386_OP(TEST,Eb_Gb,0x84) I386_OP(TEST,Ev_Gv,0x85) I386_OP(XCHG,Eb_Gb,0x86) I386_OP(XCHG,Ev_Gv,0x87) I386_OP(MOV,Eb_Gb,0x88) I386_OP(MOV,Ev_Gv,0x89) I386_OP(MOV,Gb_Eb,0x8a) I386_OP(MOV,Gv_Ev,0x8b) I386_OP(MOV,Ew_Sw,0x8c) I386_OP(LEA,Gv_M,0x8d) I386_OP(MOV,Sw_Ew,0x8e) I386_OP(POP,Ev,0x8f) I386_OP(NOP,_,0x90) I386_OP(XCHG,eCX,0x91) I386_OP(XCHG,eDX,0x92) I386_OP(XCHG,eBX,0x93) I386_OP(XCHG,eSP,0x94) I386_OP(XCHG,eBP,0x95) I386_OP(XCHG,eSI,0x96) I386_OP(XCHG,eDI,0x97) I386_OP(CBW,_,0x98) I386_OP(CWD,_,0x99) I386_OP(CALL,Ap,0x9a) I386_OP(WAIT,_,0x9b) I386_OP(PUSHF,Fv,0x9c) I386_OP(POPF,Fv,0x9d) I386_OP(SAHF,_,0x9e) I386_OP(LAHF,_,0x9f) I386_OP(MOV,AL_Ob,0xa0) I386_OP(MOV,eAX_Ov,0xa1) I386_OP(MOV,Ob_AL,0xa2) I386_OP(MOV,Ov_eAX,0xa3) I386_OP(MOVSB,Xb_Yb,0xa4) I386_OP(MOVSW_D,Xv_Yv,0xa5) I386_OP(CMPSB,Xb_Yb,0xa6) I386_OP(CMPSW_D,Xv_Yv,0xa7) I386_OP(TEST,AL_Ib,0xa8) I386_OP(TEST,eAX_Iv,0xa9) I386_OP(STOSB,Yb_AL,0xaa) I386_OP(STOSW_D,Yv_eAX,0xab) I386_OP(LODSB,AL_Xb,0xac) I386_OP(LODSW_D,eAX_Xv,0xad) I386_OP(SCASB,AL_Xb,0xae) I386_OP(SCASW_D,eAX_Xv,0xaf) I386_OP(MOV,AL,0xb0) I386_OP(MOV,CL,0xb1) I386_OP(MOV,DL,0xb2) I386_OP(MOV,BL,0xb3) I386_OP(MOV,AH,0xb4) I386_OP(MOV,CH,0xb5) I386_OP(MOV,DH,0xb6) I386_OP(MOV,BH,0xb7) I386_OP(MOV,eAX,0xb8) I386_OP(MOV,eCX,0xb9) I386_OP(MOV,eDX,0xba) I386_OP(MOV,eBX,0xbb) I386_OP(MOV,eSP,0xbc) I386_OP(MOV,eBP,0xbd) I386_OP(MOV,eSI,0xbe) I386_OP(MOV,eDI,0xbf) I386_OP(Grp2,Eb_Ib_Prefix,0xc0) I386_OP(Grp2,Ev_Iv_Prefix,0xc1) I386_OP(RET,near_Iw,0xc2) I386_OP(RET,near,0xc3) I386_OP(LES,Gv_Mp,0xc4) I386_OP(LDS,Gv_Mp,0xc5) I386_OP(MOV,Eb_Ib,0xc6) I386_OP(MOV,Ev_Iv,0xc7) I386_OP(ENTER,Iw_IB,0xc8) I386_OP(LEAVE,_,0xc9) I386_OP(RET,far_Iw,0xca) I386_OP(RET,far,0xcb) I386_OP(INT,3,0xcc) I386_OP(INT,Ib,0xcd) I386_OP(INTO,_,0xce) I386_OP(IRET,_,0xcf) I386_OP(Grp2,Eb_1_Prefix,0xd0) I386_OP(Grp2,Ev_1_Prefix,0xd1) I386_OP(Grp2,Eb_CL_Prefix,0xd2) I386_OP(Grp2,Ev_CL_Prefix,0xd3) I386_OP(AAM,_,0xd4) I386_OP(AAD,_,0xd5) I386_OP(INVALID_OP,D6,0xd6) I386_OP(XLAT,_,0xd7) I386_OP(ESC,0,0xd8) I386_OP(ESC,1,0xd9) I386_OP(ESC,2,0xda) I386_OP(ESC,3,0xdb) I386_OP(ESC,4,0xdc) I386_OP(ESC,5,0xdd) I386_OP(ESC,6,0xde) I386_OP(ESC,7,0xdf) I386_OP(LOOPNE,Jb,0xe0) I386_OP(LOOPE,Jb,0xe1) I386_OP(LOOP,Jb,0xe2) I386_OP(JCXZ,Jb,0xe3) I386_OP(IN,AL_Ib,0xe4) I386_OP(IN,eAX_Ib,0xe5) I386_OP(OUT,Ib_AL,0xe6) I386_OP(OUT,Ib_eAX,0xe7) I386_OP(CALL,Jv,0xe8) I386_OP(JMP,Jv,0xe9) I386_OP(JMP,Ap,0xea) I386_OP(JMP,Jb,0xeb) I386_OP(IN,AL_DX,0xec) I386_OP(IN,eAX_DX,0xed) I386_OP(OUT,DX_AL,0xee) I386_OP(OUT,DX_eAX,0xef) I386_OP(LOCK,Prefix,0xf0) I386_OP(INVALID_OP,F1,0xf1) I386_OP(REPNE,Prefix,0xf2) I386_OP(REP_E,Prefix,0xf3) I386_OP(HLT,_,0xf4) I386_OP(CMC,_,0xf5) I386_OP(Grp3,Eb_Prefix,0xf6) I386_OP(Grp3,Ev_Prefix,0xf7) I386_OP(CLC,_,0xf8) I386_OP(STC,_,0xf9) I386_OP(CLI,_,0xfa) I386_OP(STI,_,0xfb) I386_OP(CLD,_,0xfc) I386_OP(STD,_,0xfd) I386_OP(Grp4,Prefix,0xfe) I386_OP(Grp5,Prefix,0xff) fis-gtm-V7.0-005/sr_x86_regs/i386_ops_2b.h0000644000032200000250000002072714342376327016647 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* Two-Byte Opcodes: */ I386_OP(Grp6,Prefix,0x00) I386_OP(Grp7,Prefix,0x01) I386_OP(LAR,Gv_Ew,0x02) I386_OP(LSL,Gv_Ew,0x03) I386_OP(INVALID_2_BYTE_OP,04,0x04) I386_OP(INVALID_2_BYTE_OP,05,0x05) I386_OP(CLTS,_,0x06) I386_OP(INVALID_2_BYTE_OP,07,0x07) I386_OP(INVALID_2_BYTE_OP,08,0x08) I386_OP(INVALID_2_BYTE_OP,09,0x09) I386_OP(INVALID_2_BYTE_OP,0A,0x0a) I386_OP(INVALID_2_BYTE_OP,0B,0x0b) I386_OP(INVALID_2_BYTE_OP,0C,0x0c) I386_OP(INVALID_2_BYTE_OP,0D,0x0d) I386_OP(INVALID_2_BYTE_OP,0E,0x0e) I386_OP(INVALID_2_BYTE_OP,0F,0x0f) I386_OP(INVALID_2_BYTE_OP,10,0x10) I386_OP(INVALID_2_BYTE_OP,11,0x11) I386_OP(INVALID_2_BYTE_OP,12,0x12) I386_OP(INVALID_2_BYTE_OP,13,0x13) I386_OP(INVALID_2_BYTE_OP,14,0x14) I386_OP(INVALID_2_BYTE_OP,15,0x15) I386_OP(INVALID_2_BYTE_OP,16,0x16) I386_OP(INVALID_2_BYTE_OP,17,0x17) I386_OP(INVALID_2_BYTE_OP,18,0x18) I386_OP(INVALID_2_BYTE_OP,19,0x19) I386_OP(INVALID_2_BYTE_OP,1A,0x1a) I386_OP(INVALID_2_BYTE_OP,1B,0x1b) I386_OP(INVALID_2_BYTE_OP,1C,0x1c) I386_OP(INVALID_2_BYTE_OP,1D,0x1d) I386_OP(INVALID_2_BYTE_OP,1E,0x1e) I386_OP(INVALID_2_BYTE_OP,1F,0x1f) I386_OP(MOV,Rd_Cd,0x20) I386_OP(MOV,Rd_Dd,0x21) I386_OP(MOV,Cd_Rd,0x22) I386_OP(MOV,Dd_Rd,0x23) I386_OP(MOV,Rd_Td,0x24) I386_OP(INVALID_2_BYTE_OP,25,0x25) I386_OP(MOV,Td_Rd,0x26) I386_OP(INVALID_2_BYTE_OP,27,0x27) I386_OP(INVALID_2_BYTE_OP,28,0x28) I386_OP(INVALID_2_BYTE_OP,29,0x29) I386_OP(INVALID_2_BYTE_OP,2A,0x2a) I386_OP(INVALID_2_BYTE_OP,2B,0x2b) I386_OP(INVALID_2_BYTE_OP,2C,0x2c) I386_OP(INVALID_2_BYTE_OP,2D,0x2d) I386_OP(INVALID_2_BYTE_OP,2E,0x2e) I386_OP(INVALID_2_BYTE_OP,2F,0x2f) I386_OP(INVALID_2_BYTE_OP,30,0x30) I386_OP(INVALID_2_BYTE_OP,31,0x31) I386_OP(INVALID_2_BYTE_OP,32,0x32) I386_OP(INVALID_2_BYTE_OP,33,0x33) I386_OP(INVALID_2_BYTE_OP,34,0x34) I386_OP(INVALID_2_BYTE_OP,35,0x35) I386_OP(INVALID_2_BYTE_OP,36,0x36) I386_OP(INVALID_2_BYTE_OP,37,0x37) I386_OP(INVALID_2_BYTE_OP,38,0x38) I386_OP(INVALID_2_BYTE_OP,39,0x39) I386_OP(INVALID_2_BYTE_OP,3A,0x3a) I386_OP(INVALID_2_BYTE_OP,3B,0x3b) I386_OP(INVALID_2_BYTE_OP,3C,0x3c) I386_OP(INVALID_2_BYTE_OP,3D,0x3d) I386_OP(INVALID_2_BYTE_OP,3E,0x3e) I386_OP(INVALID_2_BYTE_OP,3F,0x3f) I386_OP(INVALID_2_BYTE_OP,40,0x40) I386_OP(INVALID_2_BYTE_OP,41,0x41) I386_OP(INVALID_2_BYTE_OP,42,0x42) I386_OP(INVALID_2_BYTE_OP,43,0x43) I386_OP(INVALID_2_BYTE_OP,44,0x44) I386_OP(INVALID_2_BYTE_OP,45,0x45) I386_OP(INVALID_2_BYTE_OP,46,0x46) I386_OP(INVALID_2_BYTE_OP,47,0x47) I386_OP(INVALID_2_BYTE_OP,48,0x48) I386_OP(INVALID_2_BYTE_OP,49,0x49) I386_OP(INVALID_2_BYTE_OP,4A,0x4a) I386_OP(INVALID_2_BYTE_OP,4B,0x4b) I386_OP(INVALID_2_BYTE_OP,4C,0x4c) I386_OP(INVALID_2_BYTE_OP,4D,0x4d) I386_OP(INVALID_2_BYTE_OP,4E,0x4e) I386_OP(INVALID_2_BYTE_OP,4F,0x4f) I386_OP(INVALID_2_BYTE_OP,50,0x50) I386_OP(INVALID_2_BYTE_OP,51,0x51) I386_OP(INVALID_2_BYTE_OP,52,0x52) I386_OP(INVALID_2_BYTE_OP,53,0x53) I386_OP(INVALID_2_BYTE_OP,54,0x54) I386_OP(INVALID_2_BYTE_OP,55,0x55) I386_OP(INVALID_2_BYTE_OP,56,0x56) I386_OP(INVALID_2_BYTE_OP,57,0x57) I386_OP(INVALID_2_BYTE_OP,58,0x58) I386_OP(INVALID_2_BYTE_OP,59,0x59) I386_OP(INVALID_2_BYTE_OP,5A,0x5a) I386_OP(INVALID_2_BYTE_OP,5B,0x5b) I386_OP(INVALID_2_BYTE_OP,5C,0x5c) I386_OP(INVALID_2_BYTE_OP,5D,0x5d) I386_OP(INVALID_2_BYTE_OP,5E,0x5e) I386_OP(INVALID_2_BYTE_OP,5F,0x5f) I386_OP(INVALID_2_BYTE_OP,60,0x60) I386_OP(INVALID_2_BYTE_OP,61,0x61) I386_OP(INVALID_2_BYTE_OP,62,0x62) I386_OP(INVALID_2_BYTE_OP,63,0x63) I386_OP(INVALID_2_BYTE_OP,64,0x64) I386_OP(INVALID_2_BYTE_OP,65,0x65) I386_OP(INVALID_2_BYTE_OP,66,0x66) I386_OP(INVALID_2_BYTE_OP,67,0x67) I386_OP(INVALID_2_BYTE_OP,68,0x68) I386_OP(INVALID_2_BYTE_OP,69,0x69) I386_OP(INVALID_2_BYTE_OP,6A,0x6a) I386_OP(INVALID_2_BYTE_OP,6B,0x6b) I386_OP(INVALID_2_BYTE_OP,6C,0x6c) I386_OP(INVALID_2_BYTE_OP,6D,0x6d) I386_OP(INVALID_2_BYTE_OP,6E,0x6e) I386_OP(INVALID_2_BYTE_OP,6F,0x6f) I386_OP(INVALID_2_BYTE_OP,70,0x70) I386_OP(INVALID_2_BYTE_OP,71,0x71) I386_OP(INVALID_2_BYTE_OP,72,0x72) I386_OP(INVALID_2_BYTE_OP,73,0x73) I386_OP(INVALID_2_BYTE_OP,74,0x74) I386_OP(INVALID_2_BYTE_OP,75,0x75) I386_OP(INVALID_2_BYTE_OP,76,0x76) I386_OP(INVALID_2_BYTE_OP,77,0x77) I386_OP(INVALID_2_BYTE_OP,78,0x78) I386_OP(INVALID_2_BYTE_OP,79,0x79) I386_OP(INVALID_2_BYTE_OP,7A,0x7a) I386_OP(INVALID_2_BYTE_OP,7B,0x7b) I386_OP(INVALID_2_BYTE_OP,7C,0x7c) I386_OP(INVALID_2_BYTE_OP,7D,0x7d) I386_OP(INVALID_2_BYTE_OP,7E,0x7e) I386_OP(INVALID_2_BYTE_OP,7F,0x7f) I386_OP(JO,Jv,0x80) I386_OP(JNO,Jv,0x81) I386_OP(JB,Jv,0x82) I386_OP(JNB,Jv,0x83) I386_OP(JZ,Jv,0x84) I386_OP(JNZ,Jv,0x85) I386_OP(JBE,Jv,0x86) I386_OP(JNBE,Jv,0x87) I386_OP(JS,Jv,0x88) I386_OP(JNS,Jv,0x89) I386_OP(JP,Jv,0x8a) I386_OP(JNP,Jv,0x8b) I386_OP(JL,Jv,0x8c) I386_OP(JNL,Jv,0x8d) I386_OP(JLE,Jv,0x8e) I386_OP(JNLE,Jv,0x8f) I386_OP(SETO,Eb,0x90) I386_OP(SETNO,Eb,0x91) I386_OP(SETB,Eb,0x92) I386_OP(SETNB,Eb,0x93) I386_OP(SETZ,Eb,0x94) I386_OP(SETNZ,Eb,0x95) I386_OP(SETBE,Eb,0x96) I386_OP(SETNBE,Eb,0x97) I386_OP(SETS,Eb,0x98) I386_OP(SETNS,Eb,0x99) I386_OP(SETP,Eb,0x9a) I386_OP(SETNP,Eb,0x9b) I386_OP(SETL,Eb,0x9c) I386_OP(SETNL,Eb,0x9d) I386_OP(SETLE,Eb,0x9e) I386_OP(SETNLE,Eb,0x9f) I386_OP(PUSH,FS,0xa0) I386_OP(POP,FS,0xa1) I386_OP(INVALID_2_BYTE_OP,A2,0xa2) I386_OP(BT,Ev_Gv,0xa3) I386_OP(SHLD,EvGvIb,0xa4) I386_OP(SHLD,EvGvCL,0xa5) I386_OP(INVALID_2_BYTE_OP,A6,0xa6) I386_OP(INVALID_2_BYTE_OP,A7,0xa7) I386_OP(PUSH,GS,0xa8) I386_OP(POP,GS,0xa9) I386_OP(INVALID_2_BYTE_OP,AA,0xaa) I386_OP(BTS,Ev_Gv,0xab) I386_OP(SHRD,EvGvIb,0xac) I386_OP(SHRD,EvGvCL,0xad) I386_OP(INVALID_2_BYTE_OP,AE,0xae) I386_OP(IMUL,Gv_Ev,0xaf) I386_OP(INVALID_2_BYTE_OP,B0,0xb0) I386_OP(INVALID_2_BYTE_OP,B1,0xb1) I386_OP(LSS,Mp,0xb2) I386_OP(BTR,Ev_Gv,0xb3) I386_OP(LFS,Mp,0xb4) I386_OP(LGS,Mp,0xb5) I386_OP(MOVZX,Gv_Eb,0xb6) I386_OP(MOVZX,Gv_Ew,0xb7) I386_OP(INVALID_2_BYTE_OP,B8,0xb8) I386_OP(INVALID_2_BYTE_OP,B9,0xb9) I386_OP(Grp8,Ev_Ib,0xba) I386_OP(BTC,Ev_Gv,0xbb) I386_OP(BSF,Gv_Ev,0xbc) I386_OP(BSR,Gv_Ev,0xbd) I386_OP(MOVSX,Gv_Eb,0xbe) I386_OP(MOVSX,Gv_Ew,0xbf) I386_OP(INVALID_2_BYTE_OP,C0,0xc0) I386_OP(INVALID_2_BYTE_OP,C1,0xc1) I386_OP(INVALID_2_BYTE_OP,C2,0xc2) I386_OP(INVALID_2_BYTE_OP,C3,0xc3) I386_OP(INVALID_2_BYTE_OP,C4,0xc4) I386_OP(INVALID_2_BYTE_OP,C5,0xc5) I386_OP(INVALID_2_BYTE_OP,C6,0xc6) I386_OP(INVALID_2_BYTE_OP,C7,0xc7) I386_OP(INVALID_2_BYTE_OP,C8,0xc8) I386_OP(INVALID_2_BYTE_OP,C9,0xc9) I386_OP(INVALID_2_BYTE_OP,CA,0xca) I386_OP(INVALID_2_BYTE_OP,CB,0xcb) I386_OP(INVALID_2_BYTE_OP,CC,0xcc) I386_OP(INVALID_2_BYTE_OP,CD,0xcd) I386_OP(INVALID_2_BYTE_OP,CE,0xce) I386_OP(INVALID_2_BYTE_OP,CF,0xcf) I386_OP(INVALID_2_BYTE_OP,D0,0xd0) I386_OP(INVALID_2_BYTE_OP,D1,0xd1) I386_OP(INVALID_2_BYTE_OP,D2,0xd2) I386_OP(INVALID_2_BYTE_OP,D3,0xd3) I386_OP(INVALID_2_BYTE_OP,D4,0xd4) I386_OP(INVALID_2_BYTE_OP,D5,0xd5) I386_OP(INVALID_2_BYTE_OP,D6,0xd6) I386_OP(INVALID_2_BYTE_OP,D7,0xd7) I386_OP(INVALID_2_BYTE_OP,D8,0xd8) I386_OP(INVALID_2_BYTE_OP,D9,0xd9) I386_OP(INVALID_2_BYTE_OP,DA,0xda) I386_OP(INVALID_2_BYTE_OP,DB,0xdb) I386_OP(INVALID_2_BYTE_OP,DC,0xdc) I386_OP(INVALID_2_BYTE_OP,DD,0xdd) I386_OP(INVALID_2_BYTE_OP,DE,0xde) I386_OP(INVALID_2_BYTE_OP,DF,0xdf) I386_OP(INVALID_2_BYTE_OP,E0,0xe0) I386_OP(INVALID_2_BYTE_OP,E1,0xe1) I386_OP(INVALID_2_BYTE_OP,E2,0xe2) I386_OP(INVALID_2_BYTE_OP,E3,0xe3) I386_OP(INVALID_2_BYTE_OP,E4,0xe4) I386_OP(INVALID_2_BYTE_OP,E5,0xe5) I386_OP(INVALID_2_BYTE_OP,E6,0xe6) I386_OP(INVALID_2_BYTE_OP,E7,0xe7) I386_OP(INVALID_2_BYTE_OP,E8,0xe8) I386_OP(INVALID_2_BYTE_OP,E9,0xe9) I386_OP(INVALID_2_BYTE_OP,EA,0xea) I386_OP(INVALID_2_BYTE_OP,EB,0xeb) I386_OP(INVALID_2_BYTE_OP,EC,0xec) I386_OP(INVALID_2_BYTE_OP,ED,0xed) I386_OP(INVALID_2_BYTE_OP,EE,0xee) I386_OP(INVALID_2_BYTE_OP,EF,0xef) I386_OP(INVALID_2_BYTE_OP,F0,0xf0) I386_OP(INVALID_2_BYTE_OP,F1,0xf1) I386_OP(INVALID_2_BYTE_OP,F2,0xf2) I386_OP(INVALID_2_BYTE_OP,F3,0xf3) I386_OP(INVALID_2_BYTE_OP,F4,0xf4) I386_OP(INVALID_2_BYTE_OP,F5,0xf5) I386_OP(INVALID_2_BYTE_OP,F6,0xf6) I386_OP(INVALID_2_BYTE_OP,F7,0xf7) I386_OP(INVALID_2_BYTE_OP,F8,0xf8) I386_OP(INVALID_2_BYTE_OP,F9,0xf9) I386_OP(INVALID_2_BYTE_OP,FA,0xfa) I386_OP(INVALID_2_BYTE_OP,FB,0xfb) I386_OP(INVALID_2_BYTE_OP,FC,0xfc) I386_OP(INVALID_2_BYTE_OP,FD,0xfd) I386_OP(INVALID_2_BYTE_OP,FE,0xfe) I386_OP(INVALID_2_BYTE_OP,FF,0xff) fis-gtm-V7.0-005/sr_x86_regs/i386_ops_g1.h0000644000032200000250000000122414342376327016642 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* Opcodes for group 1 (one-byte) determined by bits 5,4,3 of ModR/M byte: */ I386_OP(ADD,_,0) I386_OP(OR,_,1) I386_OP(ADC,_,2) I386_OP(SBB,_,3) I386_OP(AND,_,4) I386_OP(SUB,_,5) I386_OP(XOR,_,6) I386_OP(CMP,_,7) fis-gtm-V7.0-005/sr_x86_regs/i386_ops_g2.h0000644000032200000250000000124414342376327016645 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* Opcodes for group 2 (one-byte) determined by bits 5,4,3 of ModR/M byte: */ I386_OP(ROL,_,0) I386_OP(ROR,_,1) I386_OP(RCL,_,2) I386_OP(RCR,_,3) I386_OP(SHL,_,4) I386_OP(SHR,_,5) I386_OP(ILLEGAL_GROUP_2_OP,2,2) I386_OP(SAR,_,7) fis-gtm-V7.0-005/sr_x86_regs/i386_ops_g3.h0000644000032200000250000000127714342376327016654 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* Opcodes for group 3 (one-byte) determined by bits 5,4,3 of ModR/M byte: */ I386_OP(TEST,Ib_Iv,0) I386_OP(ILLEGAL_GROUP_3_OP,1,1) I386_OP(NOT,_,2) I386_OP(NEG,_,3) I386_OP(MUL,AL_eAX,4) I386_OP(IMUL,AL_eAX,5) I386_OP(DIV,AL_eAX,6) I386_OP(IDIV,AL_eAX,7) fis-gtm-V7.0-005/sr_x86_regs/i386_ops_g4.h0000644000032200000250000000136114342376327016647 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* Opcodes for group 4 (one-byte) determined by bits 5,4,3 of ModR/M byte: */ I386_OP(INC,Eb,0) I386_OP(DEC,Eb,1) I386_OP(ILLEGAL_GROUP_4_OP,2,2) I386_OP(ILLEGAL_GROUP_4_OP,3,3) I386_OP(ILLEGAL_GROUP_4_OP,4,4) I386_OP(ILLEGAL_GROUP_4_OP,5,5) I386_OP(ILLEGAL_GROUP_4_OP,6,6) I386_OP(ILLEGAL_GROUP_4_OP,7,7) fis-gtm-V7.0-005/sr_x86_regs/i386_ops_g5.h0000644000032200000250000000125614342376327016653 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* Opcodes for group 5 (one-byte) determined by bits 5,4,3 of ModR/M byte: */ I386_OP(INC,Ev,0) I386_OP(DEC,Ev,1) I386_OP(CALL,Ev,2) I386_OP(CALL,Ep,3) I386_OP(JMP,Ev,4) I386_OP(JMP,Ep,5) I386_OP(PUSH,Ev,6) I386_OP(ILLEGAL_GROUP_5_OP,7,7) fis-gtm-V7.0-005/sr_x86_regs/i386_ops_g6.h0000644000032200000250000000127514342376327016655 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* Opcodes for group 6 (two-byte) determined by bits 5,4,3 of ModR/M byte: */ I386_OP(SLDT,Ew,0) I386_OP(STR,Ew,1) I386_OP(LLDT,Ew,2) I386_OP(LTR,Ew,3) I386_OP(VERR,Ew,4) I386_OP(VERW,Ew,5) I386_OP(ILLEGAL_GROUP_6_OP,6,6) I386_OP(ILLEGAL_GROUP_6_OP,7,7) fis-gtm-V7.0-005/sr_x86_regs/i386_ops_g7.h0000644000032200000250000000127714342376327016660 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* Opcodes for group 7 (two-byte) determined by bits 5,4,3 of ModR/M byte: */ I386_OP(SGDT,Ms,0) I386_OP(SIDT,Ms,1) I386_OP(LGDT,Ms,2) I386_OP(LIDT,Ms,3) I386_OP(SMSW,Ew,4) I386_OP(ILLEGAL_GROUP_7_OP,5,5) I386_OP(LMSW,Ew,6) I386_OP(ILLEGAL_GROUP_7_OP,7,7) fis-gtm-V7.0-005/sr_x86_regs/i386_ops_g8.h0000644000032200000250000000132014342376327016646 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* Opcodes for group 8 (two-byte) determined by bits 5,4,3 of ModR/M byte: */ I386_OP(ILLEGAL_GROUP_8_OP,0,0) I386_OP(ILLEGAL_GROUP_8_OP,1,1) I386_OP(ILLEGAL_GROUP_8_OP,2,2) I386_OP(ILLEGAL_GROUP_8_OP,3,3) I386_OP(BT,_,4) I386_OP(BTS,_,5) I386_OP(BTR,_,6) I386_OP(BTC,_,7) fis-gtm-V7.0-005/sr_x86_regs/i386_reg16.h0000644000032200000250000000112314342376327016374 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* 16-bit register definitions: */ REGDEF(AX,0), REGDEF(CX,1), REGDEF(DX,2), REGDEF(BX,3), REGDEF(SP,4), REGDEF(BP,5), REGDEF(SI,6), REGDEF(DI,7), fis-gtm-V7.0-005/sr_x86_regs/i386_reg32.h0000644000032200000250000000137014342376327016376 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* 32-bit register definitions: */ REGDEF(EAX,0), REGDEF(ECX,1), REGDEF(EDX,2), REGDEF(EBX,3), REGDEF(ESP,4), REGDEF(SIB_FOLLOWS,4) = I386_REG_ESP, REGDEF(NO_INDEX,4) = I386_REG_ESP, REGDEF(EBP,5), REGDEF(disp32_NO_BASE,5) = I386_REG_EBP, REGDEF(disp32_FROM_RIP,5) = I386_REG_EBP, REGDEF(ESI,6), REGDEF(EDI,7), fis-gtm-V7.0-005/sr_x86_regs/i386_reg64.h0000644000032200000250000000166214342376327016407 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ REGDEF(RAX,0), REGDEF(RCX,1), REGDEF(RDX,2), REGDEF(RBX,3), REGDEF(RSP,4), REGDEF(RBP,5), REGDEF(RSI,6), REGDEF(RDI,7), REGDEF(R8,8), REGDEF(FIRST_64_BIT,8) = I386_REG_R8, REGDEF(R9,9), REGDEF(R10,10), REGDEF(R11,11), REGDEF(R12,12), REGDEF(R13,13), REGDEF(R14,14), REGDEF(R15,15), fis-gtm-V7.0-005/sr_x86_regs/i386_reg8.h0000644000032200000250000000112214342376327016314 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* 8-bit register definitions: */ REGDEF(AL,0), REGDEF(CL,1), REGDEF(DL,2), REGDEF(BL,3), REGDEF(AH,4), REGDEF(CH,5), REGDEF(DH,6), REGDEF(BH,7), fis-gtm-V7.0-005/sr_x86_regs/i386_ss.h0000644000032200000250000000104614342376327016101 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ /* SIB SS field values: */ SS_CODE(TIMES_1,0) SS_CODE(TIMES_2,1) SS_CODE(TIMES_4,2) SS_CODE(TIMES_8,3) fis-gtm-V7.0-005/sr_unix_nsb/0000755000032200000250000000000014342376335014710 5ustar librarygtcfis-gtm-V7.0-005/sr_unix_nsb/obj_filesp.h0000755000032200000250000000165514342376327017210 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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. * * * ****************************************************************/ #ifndef OBJ_FILESP_INCLUDED #define OBJ_FILESP_INCLUDED void emit_addr(int4 refaddr, int4 offset, int4 *result); void emit_reference(uint4 refaddr, mstr *name, uint4 *result); struct sym_table *define_symbol(unsigned char psect, mstr *name, int4 value); void emit_pidr(int4 refoffset, int4 data_offset, int4 *result); void buff_emit(void); void set_psect(unsigned char psect,unsigned char offset); void resolve_sym(void); void output_relocation(void); void output_symbol(void); #endif fis-gtm-V7.0-005/sr_unix_nsb/opcode_def.h0000755000032200000250000003573114342376327017165 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* start at line 20 to make arithmetic (add 20) easy for those debugging compilation */ OPCODE_DEF(OC_NOOP, (OCT_CGSKIP)) OPCODE_DEF(OC_PARAMETER, (OCT_CGSKIP)) OPCODE_DEF(OC_VAR, (OCT_MVADDR | OCT_MVAL | OCT_CGSKIP | OCT_EXPRLEAF)) OPCODE_DEF(OC_LIT, (OCT_MVAL | OCT_CGSKIP | OCT_EXPRLEAF)) OPCODE_DEF(OC_ADD, (OCT_MVAL | OCT_ARITH)) OPCODE_DEF(OC_SUB, (OCT_MVAL | OCT_ARITH)) OPCODE_DEF(OC_MUL, (OCT_MVAL | OCT_ARITH)) OPCODE_DEF(OC_DIV, (OCT_MVAL | OCT_ARITH)) OPCODE_DEF(OC_IDIV, (OCT_MVAL | OCT_ARITH)) OPCODE_DEF(OC_MOD, (OCT_MVAL | OCT_ARITH)) OPCODE_DEF(OC_NEG, (OCT_MVAL | OCT_UNARY)) OPCODE_DEF(OC_FORCENUM, (OCT_MVAL | OCT_UNARY)) OPCODE_DEF(OC_CAT, (OCT_MVAL)) OPCODE_DEF(OC_SRCHINDX, (OCT_MVADDR | OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_GETINDX, (OCT_MVADDR | OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_PUTINDX, (OCT_MVADDR | OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_GVNAME, (OCT_NULL)) OPCODE_DEF(OC_GVNAKED, (OCT_NULL)) OPCODE_DEF(OC_ZALLOCATE, (OCT_NULL)) OPCODE_DEF(OC_ZDEALLOCATE, (OCT_NULL)) OPCODE_DEF(OC_GVGET, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_GVINCR, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_GVPUT, (OCT_NULL)) OPCODE_DEF(OC_GVKILL, (OCT_NULL)) OPCODE_DEF(OC_GVORDER, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_GVNEXT, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_GVDATA, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNASCII, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZASCII, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNCHAR, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZCHAR, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNDATA, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNEXTRACT, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZEXTRACT, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNFIND, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZFIND, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNFNUMBER, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNGET, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNGVGET, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNINCR, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNJ2, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZJ2, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNJ3, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNLENGTH, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZLENGTH, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNPOPULATION, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZPOPULATION, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNNEXT, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNORDER, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNPIECE, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZPIECE, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNRANDOM, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNTEXT, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZFILE, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZGETDVI, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZGETJPI, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZGETSYI, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZM, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZPARSE, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZPID, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZPRIV, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZSEA, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZSETPRV, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_SVGET, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_SVPUT, (OCT_NULL)) OPCODE_DEF(OC_LT, (OCT_BOOL | OCT_REL)) OPCODE_DEF(OC_NLT, (OCT_BOOL | OCT_REL | OCT_NEGATED)) OPCODE_DEF(OC_GT, (OCT_BOOL | OCT_REL)) OPCODE_DEF(OC_NGT, (OCT_BOOL | OCT_REL | OCT_NEGATED)) OPCODE_DEF(OC_EQU, (OCT_BOOL | OCT_REL)) OPCODE_DEF(OC_NEQU, (OCT_BOOL | OCT_REL | OCT_NEGATED)) OPCODE_DEF(OC_CONTAIN, (OCT_BOOL | OCT_REL)) OPCODE_DEF(OC_NCONTAIN, (OCT_BOOL | OCT_REL | OCT_NEGATED)) OPCODE_DEF(OC_FOLLOW, (OCT_BOOL | OCT_REL)) OPCODE_DEF(OC_NFOLLOW, (OCT_BOOL | OCT_REL | OCT_NEGATED)) OPCODE_DEF(OC_PATTERN, (OCT_BOOL | OCT_REL)) OPCODE_DEF(OC_NPATTERN, (OCT_BOOL | OCT_REL | OCT_NEGATED)) OPCODE_DEF(OC_AND, (OCT_BOOL)) OPCODE_DEF(OC_NAND, (OCT_BOOL | OCT_NEGATED)) OPCODE_DEF(OC_OR, (OCT_BOOL)) OPCODE_DEF(OC_NOR, (OCT_BOOL | OCT_NEGATED)) OPCODE_DEF(OC_COM, (OCT_BOOL | OCT_UNARY)) OPCODE_DEF(OC_BREAK, (OCT_NULL)) OPCODE_DEF(OC_CLOSE, (OCT_NULL)) OPCODE_DEF(OC_HALT, (OCT_NULL)) OPCODE_DEF(OC_HANG, (OCT_NULL)) OPCODE_DEF(OC_JOB, (OCT_NULL)) OPCODE_DEF(OC_KILL, (OCT_NULL)) OPCODE_DEF(OC_KILLALL, (OCT_NULL)) OPCODE_DEF(OC_LKNAME, (OCT_NULL)) OPCODE_DEF(OC_LCKINCR, (OCT_NULL)) OPCODE_DEF(OC_LCKDECR, (OCT_NULL)) OPCODE_DEF(OC_LOCK, (OCT_NULL)) OPCODE_DEF(OC_UNLOCK, (OCT_NULL)) OPCODE_DEF(OC_XNEW, (OCT_NULL)) OPCODE_DEF(OC_NEWVAR, (OCT_NULL)) OPCODE_DEF(OC_CDADDR, (OCT_CDADDR)) OPCODE_DEF(OC_OPEN, (OCT_NULL)) OPCODE_DEF(OC_RET, (OCT_NULL)) OPCODE_DEF(OC_READ, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_RDONE, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_READFL, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_STO, (OCT_NULL)) OPCODE_DEF(OC_SETPIECE, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_SETZPIECE, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_USE, (OCT_NULL)) OPCODE_DEF(OC_VIEW, (OCT_NULL)) OPCODE_DEF(OC_WRITE, (OCT_NULL)) OPCODE_DEF(OC_WTONE, (OCT_NULL)) OPCODE_DEF(OC_WTTAB, (OCT_NULL)) OPCODE_DEF(OC_WTFF, (OCT_NULL)) OPCODE_DEF(OC_WTEOL, (OCT_NULL)) OPCODE_DEF(OC_SETZBRK, (OCT_NULL)) OPCODE_DEF(OC_ZCONT, (OCT_NULL)) OPCODE_DEF(OC_ZEDIT, (OCT_NULL)) OPCODE_DEF(OC_ZGOTO, (OCT_NULL)) OPCODE_DEF(OC_ZLINK, (OCT_NULL)) OPCODE_DEF(OC_ZMESS, (OCT_NULL)) OPCODE_DEF(OC_ZPRINT, (OCT_NULL)) OPCODE_DEF(OC_ZSHOW, (OCT_NULL)) OPCODE_DEF(OC_ZSYSTEM, (OCT_NULL)) OPCODE_DEF(OC_WATCHMOD, (OCT_NULL)) OPCODE_DEF(OC_WATCHREF, (OCT_NULL)) OPCODE_DEF(OC_LVZWRITE, (OCT_NULL)) OPCODE_DEF(OC_GVZWRITE, (OCT_NULL)) OPCODE_DEF(OC_RTERROR, (OCT_NULL)) OPCODE_DEF(OC_CALL, (OCT_JUMP)) OPCODE_DEF(OC_EXTCALL, (OCT_NULL)) OPCODE_DEF(OC_JMP, (OCT_JUMP)) OPCODE_DEF(OC_EXTJMP, (OCT_NULL)) OPCODE_DEF(OC_LABADDR, (OCT_CDADDR)) OPCODE_DEF(OC_RHDADDR, (OCT_CDADDR)) OPCODE_DEF(OC_CURRHD, (OCT_CDADDR)) OPCODE_DEF(OC_CURRTN, (OCT_MVAL)) OPCODE_DEF(OC_LDADDR, (OCT_NULL)) OPCODE_DEF(OC_INDGLVN, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_INDPAT, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_INDFUN, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_COMMARG, (OCT_NULL)) OPCODE_DEF(OC_INDLVADR, (OCT_MVADDR | OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_INDSET, (OCT_NULL)) OPCODE_DEF(OC_GVEXTNAM, (OCT_NULL)) OPCODE_DEF(OC_INDNAME, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_INDTEXT, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_COMVAL, (OCT_MVAL | OCT_COERCE | OCT_UNARY)) OPCODE_DEF(OC_GVSAVTARG, (OCT_MVAL)) OPCODE_DEF(OC_GVRECTARG, (OCT_NULL)) OPCODE_DEF(OC_COMINT, (OCT_MINT | OCT_COERCE)) OPCODE_DEF(OC_FNTRANSLATE, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZTRANSLATE, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_COBOOL, (OCT_BOOL | OCT_COERCE | OCT_UNARY)) OPCODE_DEF(OC_BOOLINIT, (OCT_MINT | OCT_EXPRLEAF)) OPCODE_DEF(OC_BOOLFINI, (OCT_NULL)) OPCODE_DEF(OC_SETTEST, (OCT_NULL)) OPCODE_DEF(OC_CLRTEST, (OCT_NULL)) OPCODE_DEF(OC_FORLOOP, (OCT_NULL)) OPCODE_DEF(OC_JMPTSET, (OCT_JUMP)) OPCODE_DEF(OC_JMPTCLR, (OCT_JUMP)) OPCODE_DEF(OC_NUMCMP, (OCT_BOOL)) OPCODE_DEF(OC_JMPEQU, (OCT_JUMP)) OPCODE_DEF(OC_JMPNEQ, (OCT_JUMP)) OPCODE_DEF(OC_JMPGTR, (OCT_JUMP)) OPCODE_DEF(OC_JMPLEQ, (OCT_JUMP)) OPCODE_DEF(OC_JMPLSS, (OCT_JUMP)) OPCODE_DEF(OC_JMPGEQ, (OCT_JUMP)) OPCODE_DEF(OC_STOTEMP, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_PASSTHRU, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_LVPATWRITE, (OCT_NULL)) OPCODE_DEF(OC_JMPAT, (OCT_NULL)) OPCODE_DEF(OC_IRETMVAL, (OCT_NULL)) OPCODE_DEF(OC_FNZDATE, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_IRETMVAD, (OCT_NULL)) OPCODE_DEF(OC_HARDRET, (OCT_NULL)) OPCODE_DEF(OC_GETTRUTH, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_LINESTART, (OCT_NULL)) OPCODE_DEF(OC_FORINIT, (OCT_NULL)) OPCODE_DEF(OC_FETCH, (OCT_NULL)) OPCODE_DEF(OC_LINEFETCH, (OCT_NULL)) OPCODE_DEF(OC_ILIT, (OCT_MINT | OCT_CGSKIP | OCT_EXPRLEAF)) OPCODE_DEF(OC_CDLIT, (OCT_CDADDR | OCT_CGSKIP | OCT_EXPRLEAF)) OPCODE_DEF(OC_IGETSRC, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_XKILL, (OCT_NULL)) OPCODE_DEF(OC_ZATTACH, (OCT_NULL)) OPCODE_DEF(OC_FNZCALL, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_VXCMPL, (OCT_BOOL)) OPCODE_DEF(OC_INDLVARG, (OCT_MVAL)) OPCODE_DEF(OC_FORCHK1, (OCT_NULL)) OPCODE_DEF(OC_CVTPARM, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_ZPREVIOUS, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZPREVIOUS, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_GVQUERY, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNQUERY, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_BINDPARM, (OCT_NULL)) OPCODE_DEF(OC_RETARG, (OCT_NULL)) OPCODE_DEF(OC_EXFUN, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_EXTEXFUN, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_EXCAL, (OCT_NULL)) OPCODE_DEF(OC_EXTEXCAL, (OCT_NULL)) OPCODE_DEF(OC_ZHELP, (OCT_NULL)) OPCODE_DEF(OC_FNP1, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZP1, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_ZG1, (OCT_NULL)) OPCODE_DEF(OC_NEWINTRINSIC, (OCT_NULL)) OPCODE_DEF(OC_GVZWITHDRAW, (OCT_NULL)) OPCODE_DEF(OC_LVZWITHDRAW, (OCT_NULL)) OPCODE_DEF(OC_NULLEXP, (OCT_MVADDR | OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_EXFUNRET, (OCT_NULL)) OPCODE_DEF(OC_FNLVNAME, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_LKEXTNAME, (OCT_NULL)) OPCODE_DEF(OC_FORLCLDO, (OCT_JUMP)) OPCODE_DEF(OC_INDRZSHOW, (OCT_NULL)) OPCODE_DEF(OC_ZSHOWLOC, (OCT_NULL)) OPCODE_DEF(OC_ZSTEP, (OCT_NULL)) OPCODE_DEF(OC_ZSTEPACT, (OCT_NULL)) OPCODE_DEF(OC_CONUM, (OCT_NULL)) OPCODE_DEF(OC_LKINIT, (OCT_NULL)) OPCODE_DEF(OC_RESTARTPC, (OCT_NULL)) OPCODE_DEF(OC_FNZTRNLNM, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_ZTSTART, (OCT_NULL)) OPCODE_DEF(OC_ZTCOMMIT, (OCT_NULL)) OPCODE_DEF(OC_FNVIEW, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_STOLIT, (OCT_NULL)) OPCODE_DEF(OC_EQUNUL, (OCT_BOOL)) OPCODE_DEF(OC_FNZGETLKI, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZLKID, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_INDLVNAMADR, (OCT_MVADDR | OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_CALLSP, (OCT_JUMP)) OPCODE_DEF(OC_TIMTRU, (OCT_NULL)) OPCODE_DEF(OC_IOCONTROL, (OCT_NULL)) OPCODE_DEF(OC_FGNCAL, (OCT_NULL)) OPCODE_DEF(OC_FNFGNCAL, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_RHDADDR1, (OCT_CDADDR)) OPCODE_DEF(OC_ZCOMPILE, (OCT_NULL)) OPCODE_DEF(OC_TSTART, (OCT_NULL)) OPCODE_DEF(OC_TROLLBACK, (OCT_NULL)) OPCODE_DEF(OC_TRESTART, (OCT_NULL)) OPCODE_DEF(OC_TCOMMIT, (OCT_NULL)) OPCODE_DEF(OC_EXP, (OCT_MVAL | OCT_ARITH)) OPCODE_DEF(OC_FNGET2, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_INDINCR, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNNAME, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_INDFNNAME, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNLVPRVNAME, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_GVO2, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNLVNAMEO2, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNO2, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_INDO2, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZBITSTR, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZBITLEN, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZBITGET, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZBITSET, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZBITCOUN, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZBITFIND, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZBITNOT, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZBITAND, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZBITOR, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZBITXOR, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FGNLOOKUP, (OCT_CDADDR)) OPCODE_DEF(OC_SORTS_AFTER, (OCT_BOOL | OCT_REL)) OPCODE_DEF(OC_NSORTS_AFTER, (OCT_BOOL | OCT_REL | OCT_NEGATED)) OPCODE_DEF(OC_FNGVGET1, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNGET1, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_SETP1, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_SETZP1, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZQGBLMOD, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_SETEXTRACT, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_SETZEXTRACT, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_INDDEVPARMS, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_INDMERGE, (OCT_NULL)) OPCODE_DEF(OC_MERGE, (OCT_NULL)) OPCODE_DEF(OC_MERGE_GVARG, (OCT_NULL)) OPCODE_DEF(OC_MERGE_LVARG, (OCT_MVADDR | OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_M_SRCHINDX, (OCT_MVADDR | OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNSTACK1, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNSTACK2, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNQLENGTH, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNQSUBSCR, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNREVERSE, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_PSVPUT, (OCT_NULL)) OPCODE_DEF(OC_FNZJOBEXAM, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZSIGPROC, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZCONVERT2, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZCONVERT3, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZWIDTH, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZWRITE, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZSUBSTR, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_SETALS2ALS, (OCT_NULL)) OPCODE_DEF(OC_SETALSIN2ALSCT, (OCT_NULL)) OPCODE_DEF(OC_SETALSCTIN2ALS, (OCT_NULL)) OPCODE_DEF(OC_SETALSCT2ALSCT, (OCT_NULL)) OPCODE_DEF(OC_KILLALIAS, (OCT_NULL)) OPCODE_DEF(OC_KILLALIASALL, (OCT_NULL)) OPCODE_DEF(OC_FNZDATA, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_CLRALSVARS, (OCT_NULL)) OPCODE_DEF(OC_FNZAHANDLE, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZTRIGGER, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_EXFUNRETALS, (OCT_NULL)) OPCODE_DEF(OC_SETFNRETIN2ALS, (OCT_NULL)) OPCODE_DEF(OC_SETFNRETIN2ALSCT, (OCT_NULL)) OPCODE_DEF(OC_ZTRIGGER, (OCT_NULL)) OPCODE_DEF(OC_ZWRITESVN, (OCT_EXPRLEAF)) OPCODE_DEF(OC_RFRSHINDX, (OCT_MVADDR | OCT_MVAL)) OPCODE_DEF(OC_SAVPUTINDX, (OCT_MVADDR | OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FORFREEINDX, (OCT_NULL)) OPCODE_DEF(OC_FORNESTLVL, (OCT_NULL)) OPCODE_DEF(OC_ZHALT, (OCT_NULL)) OPCODE_DEF(OC_IGETDST, (OCT_MVADDR | OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_INDGET1, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_GLVNPOP, (OCT_NULL)) OPCODE_DEF(OC_GLVNSLOT, (OCT_MVADDR | OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_INDSAVGLVN, (OCT_MVADDR | OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_INDSAVLVN, (OCT_MVADDR | OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_RFRSHLVN, (OCT_MVADDR | OCT_MVAL)) OPCODE_DEF(OC_SAVGVN, (OCT_MVADDR | OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_SAVLVN, (OCT_MVADDR | OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_SHARESLOT, (OCT_NULL)) OPCODE_DEF(OC_STOGLVN, (OCT_NULL)) OPCODE_DEF(OC_RFRSHGVN, (OCT_NULL)) OPCODE_DEF(OC_INDFNNAME2, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_INDGET2, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_INDMERGE2, (OCT_NULL)) OPCODE_DEF(OC_LITC, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_STOLITC, (OCT_NULL)) OPCODE_DEF(OC_FNZPEEK, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZSYSLOG, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZSOCKET, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZCOLLATE, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZATRANSFORM, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_JMPTRUE, (OCT_JUMP)) OPCODE_DEF(OC_JMPFALSE, (OCT_JUMP)) OPCODE_DEF(OC_FNZTRANSLATE_FAST, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNTRANSLATE_FAST, (OCT_MVAL | OCT_EXPRLEAF)) OPCODE_DEF(OC_FNZAUDITLOG, (OCT_MVAL | OCT_EXPRLEAF)) fis-gtm-V7.0-005/sr_unix_nsb/rtnhdr.h0000755000032200000250000001652114342376327016373 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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. * * * ****************************************************************/ #ifndef RTNHDR_H_INCLUDED #define RTNHDR_H_INCLUDED #include "srcline.h" /* rtnhdr.h - routine header */ /* There are several references to this structure from assembly language; these include: * * From VAX VMS: G_MSF.MAX, * GTM$FGNCALL.MAR, FGNCAL_RTN.MAR * * From Alpha VMS: G_MSF.MAX, * GTM$FGNCAL.M64, FGNCAL_RTN.M64 * * From Unix: g_msf.si * * Any changes to the routine header must be reflected in those files as well. * * Warning: the lists above may not be complete. */ /* rhead_struct is the routine header; it occurs at the beginning of the * object code part of each module. * * The routine header is initialized when a module is first linked into * an executable. If a new version of that module is subsequently ZLINK'ed * into a running image, some of the fields will be updated to describe * the new version of the module so that existing references from other * modules to earlier versions of this module will be re-directed to the * current version. */ /* Macro to determine if given address is inside code segment. Note that even though * the PTEXT_END_ADR macro is the address of end_of_code + 1, we still want a <= check * here because in many cases, the address being tested is the RETURN address from a * call that was done as the last instruction in the code segment. Sometimes this call * is to an error or it could be the implicit quit. On HPUX, the delay slot for the * implicit quit call at the end of the module can also cause the problem. Without * the "=" check also being there, the test will fail when it should succeed. */ #define ADDR_IN_CODE(caddr, rtnhdr) (PTEXT_ADR((rtnhdr)) <= (caddr) && (caddr) <= PTEXT_END_ADR((rtnhdr))) /* Types that are different across the versions */ #define LABENT_LNR_OFFSET lab_ln_ptr /* Variable table entry */ typedef mname_entry var_tabent; /* the actual variable name is stored in the literal text pool */ /* Line number table entry */ typedef int4 lnr_tabent; typedef struct { mident lab_name; /* The name of the label */ int4 lab_ln_ptr; /* Offset of the lnrtab entry from the routine header */ boolean_t has_parms; /* Flag to indicate whether the callee has a formallist */ } lab_tabent; /* Label table entry proxy for run-time linking */ typedef struct { int4 lab_ln_ptr; /* Offset of the lnrtab entry from the routine header */ boolean_t has_parms; /* Flag to indicate whether the callee has a formallist */ } lab_tabent_proxy; #define TABENT_PROXY TREF(lab_proxy) typedef struct rhead_struct { char jsb[RHEAD_JSB_SIZE]; mstr src_full_name; /* (updated) full source name of current module version */ mident routine_name; int4 vartab_off; /* (updated) offset to variable table of current module version */ int4 vartab_len; /* (updated) length of variable table of current module version */ int4 labtab_off; int4 labtab_len; int4 lnrtab_off; int4 lnrtab_len; int4 ptext_off; /* (updated) offset to start of instructions for current module version */ int4 checksum; uint4 compiler_qlf; /* bit flags of compiler qualifiers used (see cmd_qlf.h) */ int4 old_rhead_off; int4 current_rhead_off; /* (updated) offset to routine header of current module version */ int4 temp_mvals; /* (updated) temp_mvals value of current module version */ int4 temp_size; /* (updated) temp_size value of current module version */ # ifdef GTM_TRIGGER void_ptr_t trigr_handle; /* Type is void to avoid needing gv_trigger.h to define gv_trigger_t addr */ # endif unsigned char checksum_128[16]; /* 16-byte MurmurHash3 checksum of routine source code */ routine_source *source_code; /* source code used by $TEXT */ uint4 routine_source_offset; /* (updated) when compiled with EMBED_SOURCE: offset of M source within literal text * pool; becomes absolute address in incr_link */ uint4 routine_source_length; /* if compiled with EMBED_SOURCE: length of source text */ } rhdtyp; /* Routine table entry */ typedef struct { mident rt_name; /* The name of the routine (in the literal text pool) */ rhdtyp *rt_adr; /* Pointer to its routine header */ } rtn_tabent; /* Although the names change from _ptr to _off is politically correct, (they ARE offsets, not pointers), * there is a lot of old code, espcially platform dependent code, that still deals with _ptr that we * do not wish to change at this time. Provide some translations for those entries to the proper ones. */ #define vartab_ptr vartab_off #define labtab_ptr labtab_off #define lnrtab_ptr lnrtab_off #define ptext_ptr ptext_off #define old_rhead_ptr old_rhead_off #define current_rhead_ptr current_rhead_off /* Macros for accessing routine header fields in a portable way */ #define VARTAB_ADR(rtnhdr) ((var_tabent *)((char *)(rtnhdr) + (rtnhdr)->vartab_off)) #define LABTAB_ADR(rtnhdr) ((lab_tabent *)((char *)(rtnhdr) + (rtnhdr)->labtab_off)) #define LNRTAB_ADR(rtnhdr) ((lnr_tabent *)((char *)(rtnhdr) + (rtnhdr)->lnrtab_off)) #define LITERAL_ADR(rtnhdr) ((unsigned char *)(rtnhdr)->literal_ptr) #define LINKAGE_ADR(rtnhdr) ((caddr_t)NULL) #define PTEXT_ADR(rtnhdr) ((unsigned char *)((char *)(rtnhdr) + (rtnhdr)->ptext_off)) #define PTEXT_END_ADR(rtnhdr) ((unsigned char *)((char *)(rtnhdr) + (rtnhdr)->vartab_off)) #define CURRENT_RHEAD_ADR(rtnhdr) ((rhdtyp *)((char *)(rtnhdr) + (rtnhdr)->current_rhead_off)) #define OLD_RHEAD_ADR(rtnhdr) ((rhdtyp *)((char *)(rtnhdr) + (rtnhdr)->old_rhead_off)) #define LINE_NUMBER_ADDR(rtnhdr, lnr_tabent_ptr) ((unsigned char *)((char *)(rtnhdr) + *(lnr_tabent_ptr))) #define LABENT_LNR_ENTRY(rtnhdr, lab_tabent_ptr) ((lnr_tabent *)((char *)(rtnhdr) + (lab_tabent_ptr)->lab_ln_ptr)) #define LABEL_ADDR(rtnhdr, lab_tabent_ptr)(CODE_BASE_ADDR(rtnhdr) + *(LABENT_LNR_ENTRY(rtnhdr, lab_tabent_ptr))) #define CODE_BASE_ADDR(rtnhdr) ((unsigned char *)(rtnhdr)) #define CODE_OFFSET(rtnhdr, addr) ((char *)(addr) - (char *)(rtnhdr)) #define DYNAMIC_LITERALS_ENABLED(rtnhdr) FALSE #define RW_REL_START_ADR(rtnhdr) ((char *)LITERAL_ADR(rtnhdr)) /* Flag values for get_src_line call */ #define VERIFY TRUE #define NOVERIFY FALSE int get_src_line(mval *routine, mval *label, int offset, mstr **srcret, rhdtyp **rtn_vec); void free_src_tbl(rhdtyp *rtn_vector); unsigned char *find_line_start(unsigned char *in_addr, rhdtyp *routine); int4 *find_line_addr(rhdtyp *routine, mstr *label, int4 offset, mident **lent_name); rhdtyp *find_rtn_hdr(mstr *name); boolean_t find_rtn_tabent(rtn_tabent **res, mstr *name); bool zlput_rname(rhdtyp *hdr); rhdtyp *make_dmode(void); void comp_lits(rhdtyp *rhead); rhdtyp *op_rhdaddr(mval *name, rhdtyp *rhd); rhdtyp *op_rhdaddr1(mval *name); lnr_tabent *op_labaddr(rhdtyp *routine, mval *label, int4 offset); void urx_resolve(rhdtyp *rtn, lab_tabent *lbl_tab, lab_tabent *lbl_top); char *rtnlaboff2entryref(char *entryref_buff, mident *rtn, mident *lab, int offset); boolean_t on_stack(rhdtyp *rtnhdr, boolean_t *need_duplicate); #endif /* RTNHDR_H_INCLUDED */ fis-gtm-V7.0-005/sr_unix_nsb/ttt.txt0000755000032200000250000006076414342376327016305 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2001, 2014 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This table defines the intermediate code (vax code) generated by an opcode. ; It drives tttgen.m to generate ttt.c. ; There are multiple versions of this file by platform family and ; changes to one should trigger review and likely changes to the others. ; The format is OC_:list of vax instructions,one to a line. ; The opcode must have a corresponding entry in opcode_def.h ; OC_ADD: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_add OC_BINDPARM: irepl val.2 calls val.1,xfer.xf_bindparm OC_BOOLFINI: incl val.1 OC_BOOLINIT: clrl val.0 OC_BREAK: jsb xfer.xf_break OC_CALL-BYTE: jsb xfer.xf_callb brb jmp.1 OC_CALL-LONG: jsb xfer.xf_calll jmp jmp.1 OC_CALL-WORD: jsb xfer.xf_callw brw jmp.1 OC_CALLSP-BYTE: jsb xfer.xf_callspb brb jmp.1 OC_CALLSP-LONG: jsb xfer.xf_callspl jmp jmp.1 OC_CALLSP-WORD: jsb xfer.xf_callspw brw jmp.1 OC_CAT: irepab val.2 pushab val.0 calls val.1,xfer.xf_cat OC_CLOSE: pushab val.2 pushab val.1 calls #2,xfer.xf_close OC_CLRTEST: bicb2 #1,r10 jsb xfer.xf_dt_false OC_CLRALSVARS: pushab val.1 calls #1,xfer.xf_clralsvars OC_COBOOL-MINT: tstl val.1 OC_COBOOL-MVAL: movab val.1,r1 jsb xfer.xf_mval2bool OC_COMINT-MVAL: movab val.1,r1 jsb xfer.xf_mval2mint movl r0,val.0 OC_COMMARG: pushl val.2 pushab val.1 jsb xfer.xf_commarg OC_COMVAL-MINT: movab val.0,r0 movl val.1,r1 jsb xfer.xf_mint2mval OC_CONUM: movab val.1,r1 jsb xfer.xf_mval2num OC_CONTAIN: movab val.1,r0 movab val.2,r1 jsb xfer.xf_contain OC_CURRHD: movl (ap),addr.0 OC_CURRTN: movab val.0,r1 jsb xfer.xf_currtn OC_CVTPARM: pushab val.0 pushab val.2 pushl val.1 calls #3,xfer.xf_cvtparm OC_DIV: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_div OC_EQU: movab val.2,r1 movab val.1,r0 jsb xfer.xf_equ OC_EQUNUL: movab val.1,r0 jsb xfer.xf_equnul OC_EXCAL: irepab val.4 pushl val.3 pushl val.2 pushl #0 jsb xfer.xf_exfun OC_EXFUN: irepab val.4 pushl val.3 pushl val.2 pushab val.0 jsb xfer.xf_exfun OC_EXFUNRET: pushab val.1 calls #1,xfer.xf_exfunret OC_EXFUNRETALS: pushab val.1 calls #1,xfer.xf_exfunretals OC_EXTCALL: pushab val.2 pushab val.1 jsb xfer.xf_extcall OC_EXTEXCAL: irepab val.5 pushl val.4 pushl val.3 pushl #0 pushab val.2 pushab val.1 jsb xfer.xf_extexfun OC_EXTEXFUN: irepab val.5 pushl val.4 pushl val.3 pushab val.0 pushab val.2 pushab val.1 jsb xfer.xf_extexfun OC_EXTJMP: pushab val.2 pushab val.1 jsb xfer.xf_extjmp OC_EXP: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_exp OC_FETCH: irepl val.2 calls val.1,xfer.xf_fetch OC_FGNCAL: irepab val.6 pushl val.5 pushl val.4 pushab val.3 pushab val.2 pushl #0 calls val.1,xfer.xf_fnfgncal OC_FNASCII: pushab val.0 pushab val.1 pushl val.2 calls #3,xfer.xf_fnascii OC_FNCHAR: irepl val.2 pushab val.0 calls val.1,xfer.xf_fnchar OC_FNZASCII: pushab val.0 pushab val.1 pushl val.2 calls #3,xfer.xf_fnzascii OC_FNZAHANDLE: pushab val.0 pushab val.1 calls #2,xfer.xf_fnzahandle OC_FNZCHAR: irepl val.2 pushab val.0 calls val.1,xfer.xf_fnzchar OC_FNDATA: pushab val.0 pushab val.1 calls #2,xfer.xf_fndata OC_FNEXTRACT: pushab val.0 pushab val.1 pushl val.2 pushl val.3 calls #4,xfer.xf_fnextract OC_FNZEXTRACT: pushab val.0 pushab val.1 pushl val.2 pushl val.3 calls #4,xfer.xf_fnzextract OC_FNFGNCAL: irepab val.6 pushl val.5 pushl val.4 pushab val.3 pushab val.2 pushab val.0 calls val.1,xfer.xf_fnfgncal OC_FNFIND: pushab val.0 pushl val.3 pushab val.2 pushab val.1 calls #4,xfer.xf_fnfind OC_FNZFIND: pushab val.0 pushl val.3 pushab val.2 pushab val.1 calls #4,xfer.xf_fnzfind OC_FNFNUMBER: pushab val.0 pushl val.4 pushl val.3 pushab val.2 pushab val.1 calls #5,xfer.xf_fnfnumber OC_FNGET: movab val.1,r1 movab val.0,r0 jsb xfer.xf_fnget OC_FNGET2: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnget2 OC_FNGVGET: pushab val.0 calls #1,xfer.xf_fngvget OC_FNGVGET1: pushab val.0 calls #1,xfer.xf_fngvget1 OC_FNGET1: pushab val.0 pushab val.1 calls #2,xfer.xf_fnget1 OC_FNINCR: pushab val.0 ; /* result of $INCR */ pushab val.2 ; /* r->operand[1] = increment */ pushab val.1 ; /* r->operand[0] = local-variable to increment */ calls #3,xfer.xf_fnincr OC_FNJ2: pushab val.0 pushl val.2 pushab val.1 calls #3,xfer.xf_fnj2 OC_FNZJ2: pushab val.0 pushl val.2 pushab val.1 calls #3,xfer.xf_fnzj2 OC_FNJ3: pushab val.0 pushl val.3 pushl val.2 pushab val.1 calls #4,xfer.xf_fnj3 OC_FNLENGTH: pushab val.0 pushab val.1 calls #2,xfer.xf_fnlength OC_FNZLENGTH: pushab val.0 pushab val.1 calls #2,xfer.xf_fnzlength OC_FNLVNAME: pushab val.0 pushl val.2 pushab val.1 calls #3,xfer.xf_fnlvname OC_FNLVNAMEO2: pushab val.2 pushab val.0 pushab val.1 calls #3,xfer.xf_fnlvnameo2 OC_FNLVPRVNAME: pushab val.0 pushab val.1 calls #2,xfer.xf_fnlvprvname OC_FNNAME: irepab val.4 pushab val.1 ; /* r->operand[0] */ pushl val.3 pushab val.0 ; /* result of $NAME */ calls val.2,xfer.xf_fnname OC_FNNEXT: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnnext OC_FNO2: pushab val.3 pushab val.0 pushab val.2 pushab val.1 calls #4,xfer.xf_fno2 OC_FNORDER: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnorder OC_FNP1: pushab val.0 pushl val.3 pushl val.2 pushab val.1 calls #4,xfer.xf_fnp1 OC_FNPIECE: pushab val.0 pushl val.4 pushl val.3 pushab val.2 pushab val.1 calls #5,xfer.xf_fnpiece OC_FNZP1: pushab val.0 pushl val.3 pushl val.2 pushab val.1 calls #4,xfer.xf_fnzp1 OC_FNZPIECE: pushab val.0 pushl val.4 pushl val.3 pushab val.2 pushab val.1 calls #5,xfer.xf_fnzpiece OC_FNQLENGTH: pushab val.0 pushab val.1 calls #2,xfer.xf_fnqlength OC_FNQSUBSCR: pushab val.0 pushl val.2 pushab val.1 calls #3,xfer.xf_fnqsubscript OC_FNQUERY: irepab val.3 pushab val.2 pushab val.0 calls val.1,xfer.xf_fnquery OC_FNRANDOM: pushab val.0 pushl val.1 calls #2,xfer.xf_fnrandom OC_FNREVERSE: pushab val.0 pushab val.1 calls #2,xfer.xf_fnreverse OC_FNSTACK1: pushab val.0 pushl val.1 calls #2,xfer.xf_fnstack1 OC_FNSTACK2: pushab val.0 pushab val.2 pushl val.1 calls #3,xfer.xf_fnstack2 OC_FNTEXT: pushab val.0 pushab val.3 pushl val.2 pushab val.1 calls #4,xfer.xf_fntext OC_FNTRANSLATE: pushab val.0 pushab val.3 pushab val.2 pushab val.1 calls #4,xfer.xf_fntranslate OC_FNZTRANSLATE: pushab val.0 pushab val.3 pushab val.2 pushab val.1 calls #4,xfer.xf_fnztranslate OC_FNZTRIGGER: pushab val.0 pushab val.3 pushab val.2 pushab val.1 calls #4,xfer.xf_fnztrigger OC_FNVIEW: irepab val.2 pushab val.0 calls val.1,xfer.xf_fnview OC_FNZBITAND: pushab val.2 pushab val.1 pushab val.0 calls #3,xfer.xf_fnzbitand OC_FNZBITCOUN: pushab val.1 pushab val.0 calls #2,xfer.xf_fnzbitcoun OC_FNZBITFIND: pushl val.3 pushl val.2 pushab val.1 pushab val.0 calls #4,xfer.xf_fnzbitfind OC_FNZBITGET: pushl val.2 pushab val.1 pushab val.0 calls #3,xfer.xf_fnzbitget OC_FNZBITLEN: pushab val.1 pushab val.0 calls #2,xfer.xf_fnzbitlen OC_FNZBITNOT: pushab val.1 pushab val.0 calls #2,xfer.xf_fnzbitnot OC_FNZBITOR: pushab val.2 pushab val.1 pushab val.0 calls #3,xfer.xf_fnzbitor OC_FNZBITSET: pushl val.3 pushl val.2 pushab val.1 pushab val.0 calls #4,xfer.xf_fnzbitset OC_FNZBITSTR: pushl val.2 pushl val.1 pushab val.0 calls #3,xfer.xf_fnzbitstr OC_FNZBITXOR: pushab val.2 pushab val.1 pushab val.0 calls #3,xfer.xf_fnzbitxor OC_FNZCALL: irepab val.2 pushab val.0 calls val.1,xfer.xf_fnzcall OC_FNZDATA: pushab val.0 pushab val.1 calls #2,xfer.xf_fnzdata OC_FNZDATE: pushab val.0 pushab val.4 pushab val.3 pushab val.2 pushab val.1 calls #5,xfer.xf_fnzdate OC_FNZFILE: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnzfile OC_FNZGETDVI: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fngetdvi OC_FNZGETJPI: pushab val.0 pushab val.2 pushl val.1 calls #3,xfer.xf_fngetjpi OC_FNZGETLKI: pushab val.0 pushab val.2 pushl val.1 calls #3,xfer.xf_fngetlki OC_FNZGETSYI: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fngetsyi OC_FNZJOBEXAM: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnzjobexam OC_FNZLKID: pushab val.0 pushl val.1 calls #2,xfer.xf_fnzlkid OC_FNZM: pushab val.0 pushl val.1 calls #2,xfer.xf_fnzm OC_FNZPARSE: pushab val.0 pushab val.5 pushab val.4 pushab val.3 pushab val.2 pushab val.1 calls #6,xfer.xf_fnzparse OC_FNZPID: pushab val.0 pushl val.1 calls #2,xfer.xf_fnzpid OC_FNZPREVIOUS: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnzprevious OC_FNZPRIV: pushab val.0 pushab val.1 calls #2,xfer.xf_fnzpriv OC_FNZQGBLMOD: pushab val.0 calls #1,xfer.xf_fnzqgblmod OC_FNZSEA: pushab val.0 pushl val.3 pushl val.2 pushab val.1 calls #4,xfer.xf_fnzsearch OC_FNZSETPRV: pushab val.0 pushab val.1 calls #2,xfer.xf_fnzsetprv OC_FNZSIGPROC: pushab val.0 pushl val.2 pushl val.1 calls #3,xfer.xf_fnzsigproc OC_FNZSOCKET: irepab val.2 pushab val.0 calls val.1,xfer.xf_fnzsocket OC_FNZTRNLNM: pushab val.0 pushab val.6 pushab val.5 pushab val.4 pushl val.3 pushab val.2 pushab val.1 calls #7,xfer.xf_fnztrnlnm OC_FOLLOW: movab val.1,r0 movab val.2,r1 jsb xfer.xf_follow OC_FORCENUM: movab val.0,r0 movab val.1,r1 jsb xfer.xf_forcenum OC_FORCHK1: jsb xfer.xf_restartpc jsb xfer.xf_forchk1 OC_FORINIT: pushab val.3 pushab val.2 pushab val.1 jsb xfer.xf_forinit OC_FORLCLDO-BYTE: jsb xfer.xf_forlcldob brb jmp.1 OC_FORLCLDO-LONG: jsb xfer.xf_forlcldol jmp jmp.1 OC_FORLCLDO-WORD: jsb xfer.xf_forlcldow brw jmp.1 OC_FORLOOP-BYTE:jsb xfer.xf_restartpc pushab jmp.1 pushab val.4 pushab val.3 pushab val.2 jsb xfer.xf_forloop OC_FORLOOP-LONG:jsb xfer.xf_restartpc pushab jmp.1 pushab val.4 pushab val.3 pushab val.2 jsb xfer.xf_forloop OC_FORLOOP-WORD:jsb xfer.xf_restartpc pushab jmp.1 pushab val.4 pushab val.3 pushab val.2 jsb xfer.xf_forloop OC_GETINDX: irepab val.2 calls val.1,xfer.xf_getindx movl r0,addr.0 OC_GETTRUTH: movab val.0,r1 jsb xfer.xf_gettruth OC_GVDATA: pushab val.0 calls #1,xfer.xf_gvdata OC_GVEXTNAM: irepab val.3 pushl val.2 ; /* hash_code */ calls val.1,xfer.xf_gvextnam OC_GVGET: pushab val.0 calls #1,xfer.xf_gvget OC_GVINCR: pushab val.0 ; /* result of $INCR */ pushab val.2 ; /* r->operand[1] = increment (global-var to increment is gv_currkey so no operand[0]) */ calls #2,xfer.xf_gvincr OC_GVKILL: calls #0,xfer.xf_gvkill OC_GVNAKED: irepab val.3 pushl val.2 ; /* hash_code */ calls val.1,xfer.xf_gvnaked OC_GVNAME: irepab val.3 pushl val.2 ; /* hash_code */ calls val.1,xfer.xf_gvname OC_GVNEXT: pushab val.0 calls #1,xfer.xf_gvnext OC_GVO2: pushab val.1 pushab val.0 calls #2,xfer.xf_gvo2 OC_GVORDER: pushab val.0 calls #1,xfer.xf_gvorder OC_GVPUT: pushab val.1 calls #1,xfer.xf_gvput OC_GVQUERY: pushab val.0 calls #1,xfer.xf_gvquery OC_GVRECTARG: pushab val.1 calls #1,xfer.xf_gvrectarg OC_GVSAVTARG: pushab val.0 calls #1,xfer.xf_gvsavtarg OC_GVZWITHDRAW: calls #0,xfer.xf_gvzwithdraw OC_GVZWRITE: jsb xfer.xf_restartpc irepab val.4 pushl val.3 pushl val.2 calls val.1,xfer.xf_gvzwrite OC_HANG: jsb xfer.xf_restartpc pushab val.1 calls #1,xfer.xf_hang OC_HARDRET: jsb xfer.xf_hardret OC_IDIV: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_idiv OC_IGETSRC: pushab val.0 calls #1,xfer.xf_igetsrc OC_INDDEVPARMS: pushab val.0 pushl val.2 pushab val.1 jsb xfer.xf_inddevparms OC_INDFNNAME: pushab val.2 ; /* r->operand[1] = depth */ pushab val.1 ; /* r->operand[0] = name */ pushab val.0 ; /* r->dst */ jsb xfer.xf_indfnname OC_INDFUN: pushab val.0 pushl val.2 pushab val.1 jsb xfer.xf_indfun OC_INDGLVN: pushab val.0 pushab val.1 jsb xfer.xf_indglvn OC_INDINCR: pushab val.1 ; /* r->operand[0] = indirection expression */ pushab val.2 ; /* r->operand[1] = increment (ILIT) */ pushab val.0 ; /* r->dst */ jsb xfer.xf_indincr OC_INDLVADR: pushab val.1 jsb xfer.xf_indlvadr movl r0,addr.0 OC_INDLVARG: pushab val.0 pushab val.1 jsb xfer.xf_indlvarg OC_INDNAME: pushab val.2 pushab val.1 pushab val.0 calls #3,xfer.xf_indname OC_INDLVNAMADR: pushab val.1 jsb xfer.xf_indlvnamadr movl r0,addr.0 OC_INDO2: pushab val.2 pushab val.1 pushab val.0 calls #3,xfer.xf_indo2 OC_INDPAT: pushab val.0 pushab val.1 jsb xfer.xf_indpat OC_INDRZSHOW: pushab val.2 pushab val.1 jsb xfer.xf_indrzshow OC_INDSET: pushab val.2 pushab val.1 jsb xfer.xf_indset OC_INDTEXT: pushab val.0 pushab val.3 pushl val.2 pushab val.1 jsb xfer.xf_indtext OC_IOCONTROL: jsb xfer.xf_restartpc irepab val.2 calls val.1,xfer.xf_iocontrol OC_IRETMVAD: movab val.1,r1 jsb xfer.xf_iretmvad OC_IRETMVAL: pushab val.2 pushab val.1 jsb xfer.xf_iretmval OC_JMP-BYTE: brb jmp.1 OC_JMP-LONG: jmp jmp.1 OC_JMP-WORD: brw jmp.1 OC_JMPAT: jmp val.1 OC_JMPEQU-BYTE: beql jmp.1 OC_JMPEQU-LONG: bneq #6 jmp jmp.1 OC_JMPEQU-WORD: bneq #3 brw jmp.1 OC_JMPGEQ-BYTE: bgeq jmp.1 OC_JMPGEQ-LONG: blss #6 jmp jmp.1 OC_JMPGEQ-WORD: blss #3 brw jmp.1 OC_JMPGTR-BYTE: bgtr jmp.1 OC_JMPGTR-LONG: bleq #6 jmp jmp.1 OC_JMPGTR-WORD: bleq #3 brw jmp.1 OC_JMPLEQ-BYTE: bleq jmp.1 OC_JMPLEQ-LONG: bgtr #6 jmp jmp.1 OC_JMPLEQ-WORD: bgtr #3 brw jmp.1 OC_JMPLSS-BYTE: blss jmp.1 OC_JMPLSS-LONG: bgeq #6 jmp jmp.1 OC_JMPLSS-WORD: bgeq #3 brw jmp.1 OC_JMPNEQ-BYTE: bneq jmp.1 OC_JMPNEQ-LONG: bneq #6 jmp jmp.1 OC_JMPNEQ-WORD: beql #3 brw jmp.1 OC_JMPTCLR-BYTE: blbc r10,jmp.1 OC_JMPTCLR-LONG: blbs r10,#6 jmp jmp.1 OC_JMPTCLR-WORD: blbs r10,#3 brw jmp.1 OC_JMPTSET-BYTE: blbs r10,jmp.1 OC_JMPTSET-LONG: blbc r10,#6 jmp jmp.1 OC_JMPTSET-WORD: blbc r10,#3 brw jmp.1 OC_JOB: jsb xfer.xf_restartpc irepab val.7 pushab val.6 pushab val.5 pushab val.4 pushl val.3 pushab val.2 calls val.1,xfer.xf_job OC_KILL: pushab val.1 calls #1,xfer.xf_kill OC_KILLALIAS: pushl val.1 calls #1,xfer.xf_killalias OC_KILLALL: calls #0,xfer.xf_killall OC_KILLALIASALL: calls #0,xfer.xf_killaliasall OC_LABADDR: pushl val.2 pushab val.1 pushab val.3 calls #3,xfer.xf_labaddr movl r0,addr.0 OC_LCKDECR: pushab val.1 calls #1,xfer.xf_lckdecr OC_LCKINCR: pushab val.1 calls #1,xfer.xf_lckincr OC_LDADDR-BYTE: movab jmp.1,addr.0 OC_LDADDR-LONG: movab jmp.1,addr.0 OC_LDADDR-WORD: movab jmp.1,addr.0 OC_LINEFETCH: irepl val.2 pushl val.1 jsb xfer.xf_linefetch OC_LINESTART: jsb xfer.xf_linestart OC_LKEXTNAME: irepab val.4 pushab val.3 pushab val.2 calls val.1,xfer.xf_lkname OC_LKINIT: calls #0,xfer.xf_lkinit OC_LKNAME: irepab val.4 pushab val.3 pushl val.2 calls val.1,xfer.xf_lkname OC_LOCK: pushab val.1 calls #1,xfer.xf_lock OC_LVPATWRITE: jsb xfer.xf_restartpc irepab val.3 pushl val.2 calls val.1,xfer.xf_lvpatwrite OC_LVZWITHDRAW: pushab val.1 calls #1,xfer.xf_lvzwithdraw OC_LVZWRITE: jsb xfer.xf_restartpc irepab val.2 calls val.1,xfer.xf_lvzwrite OC_MOD: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_flt_mod OC_MUL: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_mul OC_NEG: movab val.0,r0 movab val.1,r1 jsb xfer.xf_neg OC_NEWINTRINSIC: pushl val.1 jsb xfer.xf_newintrinsic OC_NEWVAR: pushl val.1 jsb xfer.xf_newvar OC_NULLEXP: calls #0,xfer.xf_nullexp movl r0,addr.0 OC_NUMCMP: movab val.1,r0 movab val.2,r1 jsb xfer.xf_numcmp OC_OPEN: jsb xfer.xf_restartpc pushab val.4 pushab val.3 pushab val.2 pushab val.1 calls #4,xfer.xf_open OC_PATTERN: movab val.1,r0 movab val.2,r1 jsb xfer.xf_pattern OC_FNPOPULATION: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnpopulation OC_FNZPOPULATION: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnzpopulation OC_PUTINDX: irepab val.2 calls val.1,xfer.xf_putindx movl r0,addr.0 OC_RDONE: jsb xfer.xf_restartpc pushab val.1 pushab val.0 calls #2,xfer.xf_rdone OC_READ: jsb xfer.xf_restartpc pushab val.1 pushab val.0 calls #2,xfer.xf_read OC_READFL: jsb xfer.xf_restartpc pushab val.2 pushl val.1 pushab val.0 calls #3,xfer.xf_readfl OC_RESTARTPC: jsb xfer.xf_restartpc OC_RET: jsb xfer.xf_ret OC_RETARG: movab val.1,r0 movl val.2,r1 jsb xfer.xf_retarg OC_RHDADDR: pushab val.2 pushab val.1 calls #2,xfer.xf_rhdaddr movl r0,addr.0 OC_RHDADDR1: pushl #0 pushab val.1 calls #2,xfer.xf_rhdaddr movl r0,addr.0 ; ; Note if OC_RTERROR call changes, linetail.c and eval_expr.c will also need ; to change due to it dereferencing the backpoints to get to the opcode. ; OC_RTERROR: pushl val.2 pushl val.1 jsb xfer.xf_rterror OC_SETALS2ALS: pushl val.1 pushab val.2 calls #2,xfer.xf_setals2als OC_SETALSIN2ALSCT: pushab val.1 pushab val.2 calls #2,xfer.xf_setalsin2alsct OC_SETALSCTIN2ALS: pushl val.1 pushab val.2 calls #2,xfer.xf_setalsctin2als OC_SETALSCT2ALSCT: pushab val.1 pushab val.2 calls #2,xfer.xf_setalsct2alsct OC_SETFNRETIN2ALS: pushl val.1 pushab val.2 calls #2,xfer.xf_setfnretin2als OC_SETFNRETIN2ALSCT: pushab val.1 pushab val.2 calls #2,xfer.xf_setfnretin2alsct OC_SETEXTRACT: pushab val.0 pushl val.3 pushl val.2 pushab val.4 pushab val.1 calls #5,xfer.xf_setextract OC_SETP1: pushab val.0 pushl val.3 pushab val.4 pushl val.2 pushab val.1 calls #5,xfer.xf_setp1 OC_SETPIECE: pushab val.0 pushl val.4 pushl val.3 pushab val.5 pushab val.2 pushab val.1 calls #6,xfer.xf_setpiece OC_SETZEXTRACT: pushab val.0 pushl val.3 pushl val.2 pushab val.4 pushab val.1 calls #5,xfer.xf_setzextract OC_SETZP1: pushab val.0 pushl val.3 pushab val.4 pushl val.2 pushab val.1 calls #5,xfer.xf_setzp1 OC_SETZPIECE: pushab val.0 pushl val.4 pushl val.3 pushab val.5 pushab val.2 pushab val.1 calls #6,xfer.xf_setzpiece OC_SETTEST: bisb2 #1,r10 jsb xfer.xf_dt_true OC_SETZBRK: pushl val.5 pushab val.4 pushl val.2 pushab val.1 pushab val.3 calls #5,xfer.xf_setzbrk OC_SORTS_AFTER: movab val.1,r0 movab val.2,r1 jsb xfer.xf_sorts_after OC_SRCHINDX: irepab val.2 calls val.1,xfer.xf_srchindx movl r0,addr.0 OC_STO: movab val.2,r1 movab val.1,r0 jsb xfer.xf_sto OC_STOLIT: movc3 #16,val.2,val.1 OC_STOTEMP: movab val.1,r1 movab val.0,r0 jsb xfer.xf_sto OC_SUB: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_sub OC_SVGET: pushab val.0 pushl val.1 calls #2,xfer.xf_svget OC_PSVPUT: pushab val.2 pushl val.1 jsb xfer.xf_psvput OC_SVPUT: pushab val.2 pushl val.1 calls #2,xfer.xf_svput OC_TIMTRU: movl r0,r10 OC_TCOMMIT: jsb xfer.xf_tcommit OC_TROLLBACK: pushl val.1 jsb xfer.xf_trollback OC_TRESTART: pushl val.1 jsb xfer.xf_trestart OC_TSTART: irepab val.4 pushl val.3 pushab val.2 pushl val.1 jsb xfer.xf_tstart OC_UNLOCK: calls #0,xfer.xf_unlock OC_USE: pushab val.2 pushab val.1 calls #2,xfer.xf_use OC_VIEW: irepab val.2 calls val.1,xfer.xf_view OC_VXCMPL: cmpl val.1,val.2 OC_WRITE: pushab val.1 calls #1,xfer.xf_write OC_WTEOL: pushl val.1 calls #1,xfer.xf_wteol OC_WTFF: calls #0,xfer.xf_wtff OC_WTONE: pushl val.1 calls #1,xfer.xf_wtone OC_WTTAB: pushl val.1 calls #1,xfer.xf_wttab OC_XKILL: irepab val.2 calls val.1,xfer.xf_xkill OC_XNEW: irepab val.2 pushl val.1 jsb xfer.xf_xnew OC_ZALLOCATE: pushab val.1 calls #1,xfer.xf_zallocate OC_ZATTACH: jsb xfer.xf_restartpc pushab val.1 calls #1,xfer.xf_zattach OC_ZCOMPILE: pushl val.2 pushab val.1 calls #2,xfer.xf_zcompile OC_ZCONT: jsb xfer.xf_zcont OC_ZDEALLOCATE: pushab val.1 calls #1,xfer.xf_zdeallocate OC_ZEDIT: jsb xfer.xf_restartpc pushab val.2 pushab val.1 calls #2,xfer.xf_zedit OC_ZG1: pushl val.1 jsb xfer.xf_zg1 OC_ZGOTO: pushl val.1 pushl val.4 pushab val.3 pushab val.2 jsb xfer.xf_zgoto OC_ZHALT: pushl val.1 pushl val.2 calls #2,xfer.xf_zhalt OC_ZHELP: pushab val.2 pushab val.1 calls #2,xfer.xf_zhelp OC_ZLINK: pushab val.2 pushab val.1 calls #2,xfer.xf_zlink OC_ZMESS: irepab val.3 pushl val.2 calls val.1,xfer.xf_zmess OC_ZPREVIOUS: pushab val.0 calls #1,xfer.xf_zprevious OC_ZPRINT: jsb xfer.xf_restartpc pushl val.5 pushab val.4 pushl val.3 pushab val.2 pushab val.1 calls #5,xfer.xf_zprint OC_ZSHOW: jsb xfer.xf_restartpc pushl #0 pushl val.2 pushab val.1 calls #3,xfer.xf_zshow OC_ZSHOWLOC: jsb xfer.xf_restartpc pushab val.3 pushl val.2 pushab val.1 calls #3,xfer.xf_zshow OC_ZSTEP: pushl #0 pushl val.1 calls #2,xfer.xf_zstep jsb xfer.xf_zcont OC_ZSTEPACT: pushab val.2 pushl val.1 calls #2,xfer.xf_zstep jsb xfer.xf_zcont OC_ZSYSTEM: jsb xfer.xf_restartpc pushab val.1 calls #1,xfer.xf_zsystem OC_ZTCOMMIT: pushl val.1 calls #1,xfer.xf_ztcommit OC_ZTSTART: calls #0,xfer.xf_ztstart OC_MERGE: calls #0,xfer.xf_merge OC_MERGE_GVARG: pushl #0 pushl val.1 calls #2,xfer.xf_merge_arg OC_MERGE_LVARG: pushab val.2 pushl val.1 calls #2,xfer.xf_merge_arg OC_INDMERGE: pushab val.1 pushab val.2 jsb xfer.xf_indmerge OC_M_SRCHINDX: irepab val.2 calls val.1,xfer.xf_m_srchindx movl r0,addr.0 OC_FNZATRANSFORM: pushab val.0 pushl val.4 pushl val.3 pushl val.2 pushab val.1 calls #5,xfer.xf_fnzatransform OC_FNZCONVERT2: pushab val.0 ; /* destination mval */ pushab val.2 ; /* "U"/"L"/"T" */ pushab val.1 ; /* string */ calls #3,xfer.xf_fnzconvert2 OC_FNZCONVERT3: pushab val.0 ; /* Destination mval */ pushab val.3 ; /* target chset */ pushab val.2 ; /* src chset */ pushab val.1 ; /* string */ calls #4,xfer.xf_fnzconvert3 OC_FNZCOLLATE: pushab val.0 pushl val.3 pushl val.2 pushab val.1 calls #4,xfer.xf_fnzcollate OC_FNZSUBSTR: pushab val.0 ; /* Destination mval */ pushl val.3 ; /* max byte length */ pushl val.2 ; /* starting character position */ pushab val.1 ; /* string */ calls #4,xfer.xf_fnzsubstr OC_FNZWIDTH: pushab val.0 ; /* destination mval */ pushab val.1 ; /* string */ calls #2,xfer.xf_fnzwidth OC_ZTRIGGER: calls #0,xfer.xf_ztrigger OC_ZWRITESVN: pushl val.1 calls #1,xfer.xf_zwritesvn OC_FNZWRITE: pushab val.0 ; /* destination mval */ pushab val.1 ; /* source (string) mval */ pushl val.2 ; /* conversion direction indicator */ calls #3,xfer.xf_fnzwrite OC_IGETDST: calls #0,xfer.xf_igetdst movl r0,addr.0 OC_INDGET1: pushab val.0 pushab val.1 calls #2,xfer.xf_indget1 OC_GLVNPOP: pushab val.1 calls #1,xfer.xf_glvnpop OC_GLVNSLOT: pushl val.1 calls #1,xfer.xf_glvnslot movl r0,addr.0 OC_INDSAVGLVN: pushl val.3 pushab val.2 pushab val.1 jsb xfer.xf_indsavglvn OC_INDSAVLVN: pushab val.2 pushab val.1 jsb xfer.xf_indsavlvn OC_RFRSHLVN: pushl val.2 pushab val.1 calls #2,xfer.xf_rfrshlvn movl r0,addr.0 OC_SAVGVN: irepab val.3 pushl val.2 ; /* hash_code */ calls val.1,xfer.xf_savgvn OC_SAVLVN: irepab val.2 calls val.1,xfer.xf_savlvn OC_SHARESLOT: pushl val.2 pushab val.1 calls #2,xfer.xf_shareslot OC_STOGLVN: pushab val.2 pushab val.1 calls #2,xfer.xf_stoglvn OC_RFRSHGVN: pushl val.2 pushab val.1 calls #2,xfer.xf_rfrshgvn OC_INDFNNAME2: pushab val.2 pushab val.1 pushab val.0 calls #3,xfer.xf_indfnname2 OC_INDGET2: pushab val.1 pushab val.0 calls #2,xfer.xf_indget2 OC_INDMERGE2: pushab val.1 calls #1,xfer.xf_indmerge2 OC_FNZPEEK: pushab val.0 pushab val.4 pushl val.3 pushl val.2 pushab val.1 calls #5,xfer.xf_fnzpeek OC_FNZSYSLOG: pushab val.0 ; /* destination mval */ pushab val.1 ; /* string */ calls #2,xfer.xf_fnzsyslog OC_FNZTRANSLATE_FAST: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnztranslate_fast OC_FNTRANSLATE_FAST: pushab val.0 pushab val.4 pushab val.3 pushab val.2 pushab val.1 calls #5,xfer.xf_fntranslate_fast OC_FNZAUDITLOG: pushab val.0 ; /* destination mval */ pushab val.1 ; /* string */ calls #2,xfer.xf_fnzauditlog fis-gtm-V7.0-005/sr_unix_nsb/comp_lits.c0000644000032200000250000000261014342376335017044 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "compiler.h" #include #include "mdq.h" #include "stringpool.h" GBLREF mliteral literal_chain; GBLREF spdesc stringpool; GBLREF unsigned short source_name_len; GBLREF mident routine_name; GBLDEF uint4 lits_size, lit_addrs; void comp_lits(rhead) rhdtyp *rhead; { uint4 offset, cnt; uint4 align_pad; mliteral *p; offset = stringpool.free - stringpool.base; offset += PADLEN(offset, NATIVE_WSIZE); rhead->src_full_name.len = source_name_len; rhead->src_full_name.addr = (char *)offset; offset += source_name_len; offset += PADLEN(offset, NATIVE_WSIZE); rhead->routine_name.len = routine_name.len; rhead->routine_name.addr = (char *)offset; offset += routine_name.len; offset += PADLEN(offset, NATIVE_WSIZE); cnt = 0; dqloop(&literal_chain, que, p) if (p->rt_addr < 0) { p->rt_addr = offset; offset += SIZEOF(mval); if (p->v.str.len) cnt++; } lits_size = offset; lit_addrs = cnt; } fis-gtm-V7.0-005/sr_unix_nsb/obj_code.c0000644000032200000250000001463314342376335016627 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_fcntl.h" #include "gtm_stdio.h" #include #include "gtm_stat.h" #include #include "gtm_unistd.h" #include "compiler.h" #include "obj_gen.h" #include #include "cmd_qlf.h" #include "cgp.h" #ifdef UNIX #include "gtmio.h" #include "eintr_wrappers.h" #endif #include "mmemory.h" #include "obj_file.h" #include "alloc_reg.h" #include "jmp_opto.h" #include "mlabel2xtern.h" #include "cg_var.h" #include "gtm_string.h" #include "stringpool.h" #include "rtn_src_chksum.h" #include "have_crit.h" GBLREF boolean_t run_time; GBLREF command_qualifier cmd_qlf; GBLREF int4 mvmax, mlmax, mlitmax, psect_use_tab[], sa_temps[], sa_temps_offset[]; GBLREF mlabel *mlabtab; GBLREF mline mline_root; GBLREF mvar *mvartab; GBLREF mident module_name, int_module_name; GBLREF spdesc stringpool; GBLREF char cg_phase; /* code generation phase */ GBLREF char cg_phase_last; /* previous code generation phase */ GBLREF int4 curr_addr, code_size; GBLREF char object_file_name[]; GBLREF int object_file_des; error_def(ERR_SYSCALL); error_def(ERR_TEXT); void cg_lab (mlabel *l, int4 base); /* The sections of the internal GT.M object are grouped according to their type (R/O, R/W). * Note: Once an object is linked, no section will be released from memory. All sections * will be retained. * * The GT.M object layout on the disk is as follows: * * +---------------+ * | rhead | \ * +---------------+ \ * | generated | | * | code | | * + - - - - - - - + | * | variable tbl | | - R/O * + - - - - - - - + | * | label tbl | | * +---------------+ | * | line num tbl | | * + - - - - - - - + / * | lit text pool | / * +---------------+ * | lit mval tbl |-- R/W * +---------------+ * | relocations | > - relocations for external syms (not kept after link) * +---------------+ * | symbol tbl | > - external symbol table (not kept after link) * +---------------+ * */ void obj_code (uint4 src_lines, void *checksum_ctx) { int status; rhdtyp rhead; mline *mlx, *mly; var_tabent *vptr; int4 lnr_pad_len; intrpt_state_t prev_intrpt_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!run_time); obj_init(); /* Define the routine name global symbol. */ define_symbol(GTM_MODULE_DEF_PSECT, (mstr *)&int_module_name, 0); memset(&rhead, 0, SIZEOF(rhead)); alloc_reg(); jmp_opto(); curr_addr = SIZEOF(rhdtyp); cg_phase = CGP_APPROX_ADDR; cg_phase_last = CGP_NOSTATE; code_gen(); code_size = curr_addr; cg_phase = CGP_ADDR_OPT; shrink_jmps(); comp_lits(&rhead); if ((cmd_qlf.qlf & CQ_MACHINE_CODE)) { cg_phase = CGP_ASSEMBLY; code_gen(); } if (!(cmd_qlf.qlf & CQ_OBJECT)) return; rhead.ptext_ptr = SIZEOF(rhead); set_rtnhdr_checksum(&rhead, (gtm_rtn_src_chksum_ctx *)checksum_ctx); rhead.vartab_ptr = code_size; rhead.vartab_len = mvmax; code_size += mvmax * SIZEOF(var_tabent); rhead.labtab_ptr = code_size; rhead.labtab_len = mlmax; code_size += mlmax * SIZEOF(lab_tabent); rhead.lnrtab_ptr = code_size; rhead.lnrtab_len = src_lines; rhead.compiler_qlf = cmd_qlf.qlf; if (cmd_qlf.qlf & CQ_EMBED_SOURCE) { rhead.routine_source_offset = TREF(routine_source_offset); rhead.routine_source_length = (uint4)(stringpool.free - stringpool.base) - TREF(routine_source_offset); } rhead.temp_mvals = sa_temps[TVAL_REF]; rhead.temp_size = sa_temps_offset[TCAD_REF]; code_size += src_lines * SIZEOF(int4); lnr_pad_len = PADLEN(code_size, SECTION_ALIGN_BOUNDARY); code_size += lnr_pad_len; DEFER_INTERRUPTS(INTRPT_IN_OBJECT_FILE_COMPILE, prev_intrpt_state); create_object_file(&rhead); ENABLE_INTERRUPTS(INTRPT_IN_OBJECT_FILE_COMPILE, prev_intrpt_state); cg_phase = CGP_MACHINE; code_gen(); /* Variable table: */ vptr = (var_tabent *)mcalloc(mvmax * SIZEOF(var_tabent)); if (mvartab) walktree(mvartab, cg_var, (char *)&vptr); else assert(0 == mvmax); emit_immed((char *)vptr, mvmax * SIZEOF(var_tabent)); /* Label table: */ if (mlabtab) walktree((mvar *)mlabtab, cg_lab, (char *)rhead.lnrtab_ptr); else assert(0 == mlmax); /* External entry definitions: */ emit_immed((char *)&(mline_root.externalentry->rtaddr), SIZEOF(mline_root.externalentry->rtaddr)); /* line 0 */ for (mlx = mline_root.child; mlx; mlx = mly) { if (mlx->table) emit_immed((char *)&(mlx->externalentry->rtaddr), SIZEOF(mlx->externalentry->rtaddr)); if (0 == (mly = mlx->child)) /* note assignment */ if (0 == (mly = mlx->sibling)) /* note assignment */ for (mly = mlx; ; ) { if (0 == (mly = mly->parent)) /* note assignment */ break; if (mly->sibling) { mly = mly->sibling; break; } } } if (0 != lnr_pad_len) /* emit padding so literal text pool starts on proper boundary */ emit_immed(PADCHARS, lnr_pad_len); # if !defined(__MVS__) && !defined(__s390__) /* assert not valid for instructions on OS390 */ assert(code_size == psect_use_tab[GTM_CODE]); # endif emit_literals(); DEFER_INTERRUPTS(INTRPT_IN_OBJECT_FILE_COMPILE, prev_intrpt_state); finish_object_file(); ENABLE_INTERRUPTS(INTRPT_IN_OBJECT_FILE_COMPILE, prev_intrpt_state); CLOSE_OBJECT_FILE(object_file_des, status); if (-1 == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("close()"), CALLFROM, errno); /* Ready to make object visible. Rename from tmp name to real routine name */ RENAME_TMP_OBJECT_FILE(object_file_name); } void cg_lab (mlabel *l, int4 base) { mstr glob_name; lab_tabent lent; if (l->ml && l->gbl) { lent.lab_name.len = l->mvname.len; lent.lab_name.addr = (char *)(l->mvname.addr - (char *)stringpool.base); lent.LABENT_LNR_OFFSET = (SIZEOF(lnr_tabent) * l->ml->line_number) + base; lent.has_parms = (NO_FORMALLIST != l->formalcnt); /* Flag to indicate a formallist */ emit_immed((char *)&lent, SIZEOF(lent)); mlabel2xtern(&glob_name, &int_module_name, &l->mvname); define_symbol(GTM_CODE, &glob_name, lent.LABENT_LNR_OFFSET); } } fis-gtm-V7.0-005/sr_unix_nsb/shrink_jmps.c0000644000032200000250000000404714342376335017410 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "mdq.h" #include "cgp.h" #include #include #include "obj_file.h" GBLREF int4 curr_addr, code_size; GBLREF char cg_phase; /* code generation phase */ GBLREF triple t_orig; /* head of triples */ LITREF octabstruct oc_tab[]; /* op-code table */ void shrink_jmps(void) { int new_size, old_size, shrink; triple *ct; /* current triple */ DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(CGP_ADDR_OPT == cg_phase); do { shrink = 0; dqloop(&t_orig, exorder, ct) { if ((oc_tab[ct->opcode].octype & OCT_JUMP) || (OC_LDADDR == ct->opcode) || (OC_FORLOOP == ct->opcode)) { old_size = ct->exorder.fl->rtaddr - ct->rtaddr; curr_addr = 0; if (0 > ct->operand[0].oprval.tref->rtaddr - ct->rtaddr) { ct->rtaddr -= shrink; trip_gen(ct); } else { trip_gen(ct); ct->rtaddr -= shrink; } new_size = curr_addr; /* size of operand 0 */ assert(old_size >= new_size); shrink += old_size - new_size; } else ct->rtaddr -= shrink; }/* dqloop */ code_size -= shrink; } while (shrink != 0); /* Now that the code has been strunk, we may have to adjust the pad length of the code segment. Compute this by now subtracting out the size of the pad length from the code size and recomputing the pad length and readjusting the code size. (see similar computation in code_gen(). */ code_size -= TREF(codegen_padlen); TREF(codegen_padlen) = PADLEN(code_size, SECTION_ALIGN_BOUNDARY); /* Length to pad to align next section */ code_size += TREF(codegen_padlen); } fis-gtm-V7.0-005/sr_unix/0000755000032200000250000000000014342376335014046 5ustar librarygtcfis-gtm-V7.0-005/sr_unix/Makefile.mk0000644000032200000250000002677714342376327016140 0ustar librarygtc################################################################# # # # Copyright (c) 2013-2022 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. # # # ################################################################# # Default configuration parameters. These values can be overridden by passing different values to build different build targets. For # the list of available build targets, see below. image = DEBUG thirdparty = gcrypt algo = AES256CFB # Add options for code scanning service scan = FALSE # If the machine has a libgcrypt version <= 1.4.1, then FIPS mode cannot be turned on. gcrypt_nofips = 0 # Verify that $gtm_dist is defined ifndef gtm_dist $(error $$gtm_dist not defined!) endif DISTDIR = $(gtm_dist) PLUGINDIR = $(DISTDIR)/plugin GTMCRYPTDIR = $(PLUGINDIR)/gtmcrypt CURDIR = `pwd` # Find out whether we are already in $gtm_dist/plugin/gtmcrypt directory. NOT_IN_GTMCRYPTDIR = $(shell [ "$(CURDIR)" = "$(GTMCRYPTDIR)" ] ; echo $$?) # Users may install GT.M without utf8 support HAVE_UTF8 = $(shell [ -d "$(DISTDIR)/utf8" ] ; echo $$?) # Determine machine and OS type. UNAMESTR = $(shell uname -a) MACHTYPE = $(shell uname -m) ifneq (,$(findstring Linux,$(UNAMESTR))) FILEFLAG = -L endif # 64 bit version of GT.M? 0 for yes! BIT64 = $(shell file $(FILEFLAG) $(DISTDIR)/mumps | grep -q -E '64-bit|ELF-64'; echo $$?) # Determine if GPG 2.1+ is installed HAVE_GPG21 = 0 HAVE_GPGCONF = $(shell /bin/sh -c "command -v gpgconf") ifneq ($(HAVE_GPGCONF),) GPGBIN = $(shell gpgconf | grep 'gpg:' | cut -d: -f3) GPGVER = $(shell $(GPGBIN) --version | head -n1 | cut -d' ' -f3) HAVE_GPG21 = $(shell expr $(GPGVER) \>= 2.1.12) endif ifeq ($(HAVE_GPG21),1) USE_LOOPBACK = "-DUSE_GPGME_PINENTRY_MODE_LOOPBACK" endif # Determine if libgcrypt is installed. HAVE_GPGCRYPT = $(shell /bin/sh -c "command -v libgcrypt-config") # Default installation target. This allows for the build system to randomize `thirdparty' and `algo' thereby changing the default # gtmcrypt install link. install_targ = libgtmcrypt_$(thirdparty)_$(algo).so # Setup build type -- debug or production. ifneq ($(image),DEBUG) debug_flag = optimize = -O2 else ifneq ($(scan),TRUE) debug_flag = -g -DDEBUG else debug_flag = -g3 -gdwarf-2 -O0 -fno-builtin -DDEBUG -DBYPASS_MEMCPY_OVERRIDE -DSTATIC_ANALYSIS endif optimize = endif CC = cc # Setup common compiler flags CFLAGS = $(debug_flag) $(optimize) -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES -D_LARGEFILE64_SOURCE=1 $(USE_LOOPBACK) ifneq ($(gcrypt_nofips),0) gcrypt_nofips_flag = -DGCRYPT_NO_FIPS else gcrypt_nofips_flag = endif # Set default extra CFLAGS and LDFLAGS for building maskpass and libgtmcryptutil.so. Both of these use SHA512 for password # obfuscation which must come from either OpenSSL or Libgcrypt. Historically, on AIX we have always made OpenSSL the default # library and on non-AIX platforms, it has been libgcrypt. default_thirdparty_CFLAGS = -DUSE_GCRYPT default_thirdparty_LDFLAGS = -lgcrypt -lgpg-error # Platform specific compiler and linker flags LIBFLAGS = IFLAGS = # Linux ifneq (,$(findstring Linux,$(UNAMESTR))) # -fPIC for Position Independent Code. CFLAGS += -fPIC -std=c99 -D_GNU_SOURCE -D_XOPEN_SOURCE=600 LDFLAGS = # So that dependent libraries are loaded from the parent library's load path at runtime RPATHFLAGS = -Wl,-rpath,'$$ORIGIN' # So that we can build shared library. LDSHR = -shared IFLAGS += -I /usr/local/ssl/include ifeq ($(BIT64),0) LIBFLAGS += -L /usr/local/ssl/lib -L /usr/lib/x86_64-linux-gnu else LIBFLAGS += -L /usr/local/ssl/lib -L /usr/lib/x86-linux-gnu endif icuver_msg = "gtm_icu_version is set to (${gtm_icu_version}). " icuver_msg := $(icuver_msg)"UTF-8 mode library installation may fail if gtm_icu_version is not set" endif # AIX ifneq (,$(findstring AIX,$(UNAMESTR))) # -qchars=signed forces `char' type to be treated as signed chars. # -qsrcmsg enhances error reporting. # -qmaxmem=-1 does not limit the amount of memory used for local tables of specific, # memory-intensive operations (in kilobytes). CFLAGS += -qchars=signed -qsrcmsg -qmaxmem=-1 -D_TPARM_COMPAT # -qro places string literals in read-only storage. # -qroconst places constants in read-only storage. # -q64 forces 64-bit object generation CFLAGS += -qro -qroconst -q64 -qlanglvl=stdc99 # -q64 for 64-bit object generation # -brtl for allowing both '.a' and '.so' to be searched at runtime. # -bhalt:5 is to disable warnings about duplicate symbols that come from # libgtmcryptutil.so and other .so that need pulling the same object # file (/lib/crt0_64.o) LDFLAGS = -q64 -brtl -bhalt:5 RPATHFLAGS = # -G so that we can build shared library # -bexpall exports all symbols from the shared library. # -bnoentry to tell the linker that shared library has no entry point. LDSHR = -Wl,-G -bexpall -bnoentry # On AIX, build maskpass and libgtmcryptutil.so with OpenSSL's libcrypto instead of libgcrypt. default_thirdparty_CFLAGS = -DUSE_OPENSSL default_thirdparty_LDFLAGS = -lcrypto # Set the default library thirdparty = openssl install_targ = libgtmcrypt_$(thirdparty)_$(algo).so endif # Common header and library paths IFLAGS += -I /opt/freeware/include -I /usr/local/include -I /usr/include -I $(gtm_dist) -I $(CURDIR) ifeq ($(BIT64),0) LIBFLAGS += -L /opt/freeware/lib64 -L /opt/freeware/lib LIBFLAGS += -L /usr/local/lib64 LIBFLAGS += -L /usr/local/lib -L /usr/lib64 -L /usr/lib -L /lib64 -L /lib -L `pwd` else LIBFLAGS += -L /opt/freeware/lib32 -L /opt/freeware/lib LIBFLAGS += -L /usr/local/lib32 LIBFLAGS += -L /usr/local/lib -L /usr/lib32 -L /usr/lib -L /lib32 -L /lib -L `pwd` endif CFLAGS += $(IFLAGS) LDFLAGS += $(LIBFLAGS) -o COMMON_LIBS = -lgtmcryptutil -lconfig # Lists of all files needed for building the encryption plugin. crypt_util_srcfiles = gtmcrypt_util.c crypt_util_hdrfiles = gtmcrypt_util.h gtmcrypt_interface.h crypt_srcfiles = gtmcrypt_ref.c gtmcrypt_pk_ref.c gtmcrypt_dbk_ref.c gtmcrypt_sym_ref.c crypt_hrdfiles = gtmcrypt_ref.h gtmcrypt_pk_ref.h gtmcrypt_dbk_ref.h gtmcrypt_sym_ref.h gtmcrypt_interface.h tls_srcfiles = gtm_tls_impl.c gen_tls_hdrfiles = gen_tls_options.h gen_tls_verify_options.h tls_hdrfiles = gtm_tls_impl.h gtm_tls_interface.h ${gen_tls_hdrfiles} # Intentionally clean up these files cleanupfiles = gen_tls_options.tmp gen_tls_verify_options.tmp whichsslh.[chi] whichsslhver all: libgtmcryptutil.so maskpass gcrypt openssl libgtmtls.so # Use the compiler to find the ssl.h header being used. Copy it into PWD and extract the macro definitions # Explanation of the AWK # - /ssl\.h/ match the line with the "ssl.h" header; assumption openssl/ssl.h is the only ssl.h include # - /^[^"]*"/ match everything from the beginning of the line that isn't a quotation mark until it encounters # a quotation mark and substitute that with the null str # - /".*$/ match everything from a quotation mark to the end of the line and substitute that with the null str # - (possibly problematic) With full path to "ssl.h", use AWK's system func to copy the file into PWD # - exit the script so that this action is done only once whichsslh: Makefile printf '#include \n#include \nint main(int argc, char *argv[]){\nprintf("/* %%s */\\n",OPENSSL_VERSION_TEXT);return 0;}' > whichsslh.c $(CC) $(IFLAGS) -E whichsslh.c > whichsslh.i awk '/ssl\.h/{sub(/^[^"]*"/,"");sub(/".*/,"");print;system("cp -f "$$0" whichsslh.h");exit}' whichsslh.i $(CC) $(IFLAGS) whichsslh.c -o whichsslhver # Generate the two header files using an intermediate file step. Commands are always executed ensuring that the plugins # are recompiled only when a material change occurs # NOTE: select between field 2 vs 3 because OpenSSL headers after 1.0.2 do "#define" instead of "#define" gen_tls_options.h: whichsslh awk '/define[ \t]+SSL_OP_[A-Z_v0-9]+[ \t]+/{fld=2;if($$2 ~ /define/)fld=3;print "DEFINE_SSL_OP(" $$(fld) "),"}' whichsslh.h > gen_tls_options.tmp ./whichsslhver >> gen_tls_options.tmp test -e $@ && (cmp -s gen_tls_options.tmp $@ || mv gen_tls_options.tmp $@) || mv gen_tls_options.tmp $@ gen_tls_verify_options.h: whichsslh awk '/define[ \t]+SSL_VERIFY_[A-Z_v0-9]+[ \t]+/{fld=2;if($$2 ~ /define/)fld=3;print "DEFINE_SSL_OP(" $$(fld) "),"}' whichsslh.h > gen_tls_verify_options.tmp ./whichsslhver >> gen_tls_verify_options.tmp test -e $@ && (cmp -s gen_tls_verify_options.tmp $@ || mv gen_tls_verify_options.tmp $@) || mv gen_tls_verify_options.tmp $@ # Reference Encryption Plugin Libraries libgtmcryptutil.so: $(crypt_util_srcfiles) $(crypt_util_hdrfiles) @echo ; echo "Compiling $@..." $(CC) $(CFLAGS) $(default_thirdparty_CFLAGS) $(crypt_util_srcfiles) $(LDSHR) $(LDFLAGS) $@ $(default_thirdparty_LDFLAGS) # Since maskpass is a standalone utility, link it (implicitly) with gtmcrypt_util.o instead of libgtmcryptutil.so. This allows # maskpass to be run without setting LD_LIBRARY_PATH/LIBPATH to load libgtmcryptutil.so. As a standalone utility maskpass should # not depend on functions like `gtm_malloc' and `gtm_free', so we are compiling the executable with -DUSE_SYSLIB_FUNCS. maskpass: maskpass.c $(crypt_util_srcfiles) $(crypt_util_hdrfiles) @echo ; echo "Compiling $@..." $(CC) $(CFLAGS) -DUSE_SYSLIB_FUNCS $(default_thirdparty_CFLAGS) maskpass.c $(crypt_util_srcfiles) \ $(LDFLAGS) $@ $(default_thirdparty_LDFLAGS) gcrypt: libgtmcrypt_gcrypt_AES256CFB.so libgtmcrypt_gcrypt_AES256CFB.so: $(crypt_srcfiles) $(crypt_hdrfiles) libgtmcryptutil.so @echo ; echo "Compiling $@..." $(CC) $(CFLAGS) -DUSE_GCRYPT -DUSE_AES256CFB $(gcrypt_nofips_flag) $(crypt_srcfiles) $(LDSHR) \ $(RPATHFLAGS) $(LDFLAGS) $@ -lgcrypt -lgpgme -lgpg-error $(COMMON_LIBS) openssl: libgtmcrypt_openssl_AES256CFB.so libgtmcrypt_openssl_AES256CFB.so: $(crypt_srcfiles) $(crypt_hdrfiles) libgtmcryptutil.so @echo ; echo "Compiling $@..." $(CC) $(CFLAGS) -DUSE_OPENSSL -DUSE_AES256CFB $(crypt_srcfiles) $(LDSHR) $(RPATHFLAGS) $(LDFLAGS) \ $@ -lcrypto -lgpgme -lgpg-error $(COMMON_LIBS) libgtmtls.so: $(tls_srcfiles) $(tls_hdrfiles) libgtmcryptutil.so @echo ; echo "Compiling $@..." $(CC) $(CFLAGS) $(tls_srcfiles) $(LDSHR) $(RPATHFLAGS) $(LDFLAGS) $@ -lssl $(COMMON_LIBS) install: all @echo ; echo "Installing shared libraries to $(PLUGINDIR) and maskpass to $(PLUGINDIR)/gtmcrypt..." mkdir -p $(PLUGINDIR)/o/utf8 $(PLUGINDIR)/r cp -f *.so $(PLUGINDIR) echo "$(PLUGINDIR)/libgtmcryptutil.so" > $(PLUGINDIR)/gpgagent.tab echo "unmaskpwd: gtm_status_t gc_mask_unmask_passwd(I:gtm_string_t*,O:gtm_string_t*[512])" >> $(PLUGINDIR)/gpgagent.tab ln -fs ./$(install_targ) $(PLUGINDIR)/libgtmcrypt.so cp -pf pinentry.m $(PLUGINDIR)/r (cd $(PLUGINDIR)/o && env gtm_chset=M ${gtm_dist}/mumps $(PLUGINDIR)/r/pinentry.m) ifeq ($(NOT_IN_GTMCRYPTDIR),1) cp -pf *.sh *.m $(GTMCRYPTDIR)/ cp -f maskpass $(GTMCRYPTDIR)/ endif ifeq ($(HAVE_UTF8),0) @echo $(icuver_msg) (cd $(PLUGINDIR)/o/utf8 && env gtm_chset=UTF-8 ${gtm_dist}/mumps $(PLUGINDIR)/r/pinentry.m) endif rm -f ${cleanupfiles} uninstall: @echo ; echo "Uninstalling shared libraries from $(PLUGINDIR) and maskpass from $(PLUGINDIR)/gtmcrypt..." rm -f $(PLUGINDIR)/gpgagent.tab rm -f $(PLUGINDIR)/libgtmcrypt*.so $(PLUGINDIR)/libgtmtls*.so rm -f $(PLUGINDIR)/gtmcrypt/maskpass clean: @echo ; echo "Removing generated files..." rm -f *.so *.o ${cleanupfiles} ${gen_tls_hdrfiles} ifeq ($(NOT_IN_GTMCRYPTDIR),1) rm -f maskpass endif fis-gtm-V7.0-005/sr_unix/add_inter.c0000755000032200000250000000713514342376327016155 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 #include "interlock.h" #include "lockconst.h" #include "add_inter.h" #include "sleep_cnt.h" #include "compswap.h" #include "wcs_sleep.h" #include "rel_quant.h" GBLREF int4 process_id; GBLREF volatile int4 fast_lock_count; /* Used in wcs_stale */ GBLREF int num_additional_processors; /* For those systems without an atomic increment compiler primative we have this flavor of atomic increment (by increment value passed in) based on a compare/exchange algorithm. Previous incarnations have used a lock-increment-unlock algorithm which on some architectures uses substantial numbers of memory barriers during the locking or unlocking process. The compswap approach uses only one set of barriers and those barriers on Itanium especially are fairly loose. This should be a higher performing incremental add than the micro lock version currently required on HPUX-HPPA. */ error_def(ERR_DBCCERR); error_def(ERR_ERRCALL); int4 add_inter(int val, sm_int_ptr_t addr, sm_global_latch_ptr_t latch) { int4 cntrval, newcntrval, spins, maxspins, retries; boolean_t cswpsuccess; sm_int_ptr_t volatile cntrval_p; ++fast_lock_count; maxspins = num_additional_processors ? MAX_LOCK_SPINS(LOCK_SPINS, num_additional_processors) : 1; cntrval_p = addr; /* Need volatile context especially on Itanium */ for (retries = LOCK_TRIES - 1; 0 < retries; retries--) /* - 1 so do rel_quant 3 times first */ { /* seems like a legitinate spin which could take advantage of transactional memory */ for (spins = maxspins; 0 < spins; spins--) { cntrval = *cntrval_p; newcntrval = cntrval + val; /* This is (currently as of 08/2007) the only non-locking usage of compswap in GT.M. We are not passing compswap an actual sm_global_latch_ptr_t addr like its function would normally dictate. However, since the address of the field we want to deal with is the first int in the global_latch_t, we just pass our int address properly cast to the type that compswap is expecting. The assert below verifies that this assumption has not changed (SE 08/2007) */ assert(0 == OFFSETOF(global_latch_t, u.parts.latch_pid)); # if defined(_AIX) cswpsuccess = compswap_unlock(RECAST(sm_global_latch_ptr_t)cntrval_p, cntrval, newcntrval); # elif defined(__linux__) cswpsuccess = compswap((sm_global_latch_ptr_t)cntrval_p, cntrval, newcntrval); # else # error "Don't know how to compswap on this platform" # endif if (cswpsuccess) { --fast_lock_count; assert(0 <= fast_lock_count); return newcntrval; } } if (retries & 0x3) /* On all but every 4th pass, do a simple rel_quant */ rel_quant(); /* Release processor to holder of lock (hopefully) */ else { /* On every 4th pass, we bide for awhile */ wcs_sleep(LOCK_SLEEP); assert(0 == (LOCK_TRIES % 4)); /* assures there are 3 rel_quants prior to first wcs_sleep() */ } } --fast_lock_count; assert(FALSE); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_DBCCERR, 2, LEN_AND_LIT("*unknown*"), ERR_ERRCALL, 3, CALLFROM); return 0; /* To keep the compiler quiet */ } fis-gtm-V7.0-005/sr_unix/aio_shim.c0000644000032200000250000005107514342376327016013 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2021 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 "gtmio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "filestruct.h" #include "dpgbldir.h" #include "gbldirnam.h" #include "sleep_cnt.h" #include "mutex.h" #include "wcs_wt.h" #include "interlock.h" #include "performcaslatchcheck.h" #include "gtm_rel_quant.h" #include "wcs_sleep.h" #include "aio_shim.h" #include "gtm_signal.h" #ifdef USE_LIBAIO #include "gtm_socket.h" #include #include "gtm_time.h" #include #include "gtm_poll.h" #include "gtm_stdlib.h" /* aio_shim.c: serves as a "shim" between both POSIX AIO and Linux AIO * interfaces. Because POSIX AIO is truly asynchronous from the client's * perspective, whereas Linux AIO requires io_getevents() to be called * at appropriate intervals, we have the following approach: * * * A multiplexing thread will service a single Linux kernel context * and exactly one global directory. * * Each global directory has one context and one thread. * * The first global directory to claim the region (via udi->owning_gd) * will be the one to service the IO. * * If a global directory leaves, and another global directory has control * over the same region, we clean up everything so that the next write * into that region will set up the kernel context for the second global * directory. */ GBLREF char *aio_shim_errstr; GBLREF char io_setup_errstr[]; GBLREF int num_additional_processors; GBLREF uint4 process_id; GBLREF boolean_t multi_thread_in_use; #ifdef DEBUG GBLREF boolean_t gtm_jvm_process; GBLREF int process_exiting; #endif GBLREF mstr extnam_str; GBLREF mval dollar_zgbldir; GBLREF boolean_t blocksig_initialized; GBLREF sigset_t block_worker; #define MAX_EVENTS 100 /* An optimization to batch requests -- the * maximum number of completed IO's that come * back to us from io_getevents() at a time. */ #define EVENTFD_SZ 8 /* The number of bytes needed to trigger an eventfd */ #define MAX_WIP_TRIES 3 /* Number of times to try to clear the WIP queue in * event of a wtfini_in_prog */ error_def(ERR_DBFILERR); error_def(ERR_SYSCALL); /* Used by the multiplexing thread to keep track of state and the pipes to * communicate with the main process. */ enum { EXIT_EFD, LAIO_EFD }; /* checks that aiocb and iocb are equivalent */ #define CHECK_OFFSETOF_FLD(x) ((offsetof(struct aiocb, x) == offsetof(struct iocb, x)) \ && (SIZEOF(((struct aiocb *)0)->x) == SIZEOF(((struct iocb *)0)->x))) #define CHECK_STRUCT_AIOCB \ MBSTART { \ assert(CHECK_OFFSETOF_FLD(aio_data) && \ CHECK_OFFSETOF_FLD(aio_key) && \ CHECK_OFFSETOF_FLD(aio_lio_opcode) && \ CHECK_OFFSETOF_FLD(aio_reqprio) && \ CHECK_OFFSETOF_FLD(aio_fildes) && \ CHECK_OFFSETOF_FLD(aio_buf) && \ CHECK_OFFSETOF_FLD(aio_nbytes) && \ CHECK_OFFSETOF_FLD(aio_offset) && \ CHECK_OFFSETOF_FLD(aio_reserved2) && \ CHECK_OFFSETOF_FLD(aio_flags) && \ CHECK_OFFSETOF_FLD(aio_resfd)); \ } MBEND #define CLEANUP_AIO_SHIM_THREAD_INIT(GDI) \ MBSTART { \ int ret, save_errno, local_errno; \ \ save_errno = errno; \ if (FD_INVALID != (GDI).exit_efd) \ { \ CLOSEFILE_RESET((GDI).exit_efd, ret); \ assert(0 == ret); \ } \ if (FD_INVALID != (GDI).laio_efd) \ { \ CLOSEFILE_RESET((GDI).laio_efd, ret); \ assert(0 == ret); \ } \ if (0 != (GDI).ctx) \ { \ ret = io_destroy((GDI).ctx); \ if (0 != ret) \ local_errno = errno; \ assert(0 == ret); \ } \ errno = save_errno; \ } MBEND #define io_setup(nr, ctxp) syscall(SYS_io_setup, nr, ctxp) #define io_destroy(ctx) syscall(SYS_io_destroy, ctx) #define io_submit(ctx, nr, iocbpp) syscall(SYS_io_submit, ctx, nr, iocbpp) #define io_getevents(ctx, min_nr, max_nr, events, timeout) syscall(SYS_io_getevents, ctx, min_nr, max_nr, events, timeout) #define ATOMIC_SUB_FETCH(ptr, val) INTERLOCK_ADD(ptr, NULL,-val) #define ATOMIC_ADD_FETCH(ptr, val) INTERLOCK_ADD(ptr, NULL, val) /* Note that ERROR_LIT MUST be a literal */ #define ISSUE_SYSCALL_RTS_ERROR_WITH_GD(GD, ERROR_LIT, SAVE_ERRNO) \ MBSTART { \ mstr *gldname; \ mstr gld_str_tmp; \ char err_buffer[GTM_PATH_MAX + SIZEOF(ERROR_LIT) + 3]; \ /* save errno in case SNPRINTF modifies errno and SAVE_ERRNO passed \ * in is "errno" in caller. \ */ \ int lcl_save_errno = SAVE_ERRNO; \ \ GET_CURR_GLD_NAME(gldname); \ DEBUG_ONLY({ \ get_first_gdr_name(GD, &gld_str_tmp); \ assert(!strncmp(gld_str_tmp.addr, gldname->addr, gldname->len)); \ }); \ SNPRINTF(err_buffer, ARRAYSIZE(err_buffer), "%s(%*s)", ERROR_LIT, \ gldname->len, gldname->addr); \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, \ LEN_AND_STR(err_buffer), CALLFROM, lcl_save_errno); \ } MBEND STATICFNDCL void *io_getevents_multiplexer(void *arg); STATICFNDCL int io_getevents_internal(aio_context_t ctx); STATICFNDCL void clean_wip_queue(unix_db_info *udi); STATICFNDCL void aio_gld_clean_wip_queue(gd_addr *input_gd, gd_addr *match_gd); STATICFNDCL int aio_shim_setup(aio_context_t *ctx); STATICFNDCL int aio_shim_thread_init(gd_addr *gd); /* Routine performed only by the multiplexing thread. It polls on all file descriptors * and passes messages between the caller and the multiplexing thread to manage file * descriptors, etc. */ /* #GTM_THREAD_SAFE : The below function (io_getevents_multiplexer) is thread-safe */ STATICFNDCL void *io_getevents_multiplexer(void *arg) { struct gd_info *gdi = (struct gd_info *)arg; struct pollfd fds[2]; uint64_t dummy; int ret, num_ios, local_errno; INIT_POLLFD(fds[LAIO_EFD], gdi->laio_efd); INIT_POLLFD(fds[EXIT_EFD], gdi->exit_efd); do { /* we poll on the file descriptors */ while ((-1 == (ret = poll(fds, ARRAYSIZE(fds), -1))) && (EINTR == errno)) ; if (-1 == ret) local_errno = errno; assert(-1 != ret); if (-1 == ret) RECORD_ERROR_IN_WORKER_THREAD_AND_EXIT(gdi, "worker_thread::poll()", local_errno); /* Service the IO's if they completed */ if (EVENTFD_NOTIFIED(fds, LAIO_EFD)) { /* flush the eventfd (though we don't care about the value) */ DOREADRC(fds[LAIO_EFD].fd, &dummy, SIZEOF(dummy), ret); assert(0 == ret); if (0 != ret) RECORD_ERROR_IN_WORKER_THREAD_AND_EXIT(gdi, "worker_thread::read()", ret); /* we subtract from num_ios all the IOs gleaned by * io_getevents_internal(). */ ret = io_getevents_internal(gdi->ctx); if (-1 == ret) RECORD_ERROR_IN_WORKER_THREAD_AND_EXIT(gdi, "worker_thread::io_getevents()", errno); num_ios = ATOMIC_SUB_FETCH(&gdi->num_ios, ret); assert(num_ios >= 0); } /* Exit if we have been notified via the exit eventfd */ if (EVENTFD_NOTIFIED(fds, EXIT_EFD)) { CLOSEFILE_RESET(gdi->laio_efd, ret); assert(0 == ret); CLOSEFILE_RESET(gdi->exit_efd, ret); assert(0 == ret); return NULL; } } while (TRUE); } /* Batches io_getevent() requests until there are no more for this particular context */ /* #GTM_THREAD_SAFE : The below function (io_getevents_internal) is thread-safe */ STATICFNDCL int io_getevents_internal(aio_context_t ctx) { struct timespec timeout = { 0, 0 }; struct io_event event[MAX_EVENTS]; int ret, i, local_errno; struct aiocb *aiocbp; int num_ios = 0; do { /* Loop on EINTR. */ while (-1 == (ret = io_getevents(ctx, 0, MAX_EVENTS, event, &timeout)) && (EINTR == errno)) ; if (0 > ret) local_errno = errno; assert(ret >= 0); if (-1 == ret) return -1; num_ios += ret; for (i = 0; i < ret; ++i) { # ifdef __i386 aiocbp = (struct aiocb *)(unsigned long)event[i].obj; # else aiocbp = (struct aiocb *)event[i].obj; # endif if (0 <= event[i].res) AIOCBP_SET_FLDS(aiocbp, event[i].res, event[i].res2); /* res < 0 means LIBAIO is providing a negated errno through * the "return" (res) value instead of the positive errno via res2. * We place the negated value (which is the actual errno) in * the third parameter of AIOCBP_SET_FLDS() which is where the * errno is expected. In this situation res2 value is also * returned for potential help in debugging. */ else AIOCBP_SET_FLDS(aiocbp, event[i].res2, -event[i].res); } } while (0 < ret); return num_ios; } /* Walks the WIP queue and "cancels" all outstanding IO's that have not yet * completed, and that belong to the current process. */ STATICFNDCL void clean_wip_queue(unix_db_info *udi) { int spins, maxspins, retries, ret; int4 max_sleep_mask; cache_que_head_ptr_t que_head; cache_state_rec_ptr_t cstt; struct aiocb *aiocbp; sgmnt_data_ptr_t csd; sgmnt_addrs *csa; struct gd_info *gdi; node_local_ptr_t cnl; int num_ios, lcnt; int num_tries; boolean_t wtfini_in_prog, done; /* Walk through the WIP queue and mark all processes with our process id and fd as ECANCELED. * Note that this isn't strictly necessary: once we've exited another process doing "wcs_wtfini" will eventually * discover that our process id is not alive anymore and reissue the write. However, doing this here avoids * that salvage/reissue logic in "wcs_wtfini" from kicking in. */ gdi = udi->owning_gd->thread_gdi; assert(gdi->num_ios > 0); /* caller should have ensured this */ csa = &udi->s_addrs; csd = csa->hdr; cnl = csa->nl; max_sleep_mask = -1; /* initialized to -1 to defer memory reference until needed */ maxspins = num_additional_processors ? MAX_LOCK_SPINS(LOCK_SPINS, num_additional_processors) : 1; que_head = &csa->acc_meth.bg.cache_state->cacheq_wip; /* Grab the WIP queue lock, similarly to wcs_get_space.c where we lock the active queue header. */ for (num_tries = 0, done = FALSE; !done && (num_tries < MAX_WIP_TRIES); num_tries++) { if (grab_latch(&que_head->latch, WT_LATCH_TIMEOUT_SEC, WS_23, csa)) { /* walk the WIP queue and strike all csr's that have our epid and file descriptor, * and if aio_shim_error(aiocbp) == EINPROGRESS. */ for (cstt = (cache_state_rec_ptr_t)((sm_uc_ptr_t)que_head + que_head->fl); (cstt != (cache_state_rec_ptr_t)que_head); cstt = (cache_state_rec_ptr_t)((sm_uc_ptr_t)cstt + cstt->state_que.fl)) { if ((cstt->epid == process_id) && ((aiocbp = &cstt->aiocb)->aio_fildes == udi->fd)) { AIO_SHIM_ERROR(aiocbp, ret); if (EINPROGRESS == ret) { AIOCBP_SET_FLDS(aiocbp, -1, ECANCELED); num_ios = ATOMIC_SUB_FETCH(&gdi->num_ios, 1); assert(num_ios >= 0); if (0 == num_ios) break; } } } /* Make sure that a wcs_wtfini() is not currently in progress. It's possible that * wcs_wtfini() has already pulled an element off the WIP queue, and in the meantime * we got the swaplock. This means our view of the WIP queue is missing a cr, and we * must retry to cancel that cr. */ wtfini_in_prog = cnl->wtfini_in_prog; rel_latch(&que_head->latch); if (wtfini_in_prog) { /* Wait 5 seconds, or until wtfini_in_prog becomes 0. */ for (lcnt = 1; (lcnt < SLEEP_FIVE_SEC) && (cnl->wtfini_in_prog); ++lcnt) wcs_sleep(1); if (!cnl->wtfini_in_prog) { /* This forces us to retry on the swaplock, up to MAX_WIP_TRIES */ done = FALSE; } else { /* We waited 5 seconds, and in that time wtfini_in_prog stayed non-zero. * We'll simply exit as our writes will be cleaned up once the process * dies anyway. We don't expect this edge case to occur in testing, so * we leave the assert gd->thread_gdi->num_ios == 0 in aio_shim_destroy() * as-is. */ done = TRUE; } } else done = TRUE; /* A wtfini was not in progress, so our WIP queue is now clean. */ } else done = TRUE; /* Couldn't grab the lock, but we leave as this case will be handled later. */ } } /* Helper method to initialize the AIO kernel context */ STATICFNDCL int aio_shim_setup(aio_context_t *ctx) { int ret; int nr_events; DEBUG_ONLY(int local_errno;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* initialize num_requests from the environment variable or otherwise */ nr_events = TREF(gtm_aio_nr_events); /* We try io_setup() in a loop, and each failed attempt we reduce the amount of space * we're asking for. If there is no space to give, just return -1 EAGAIN */ while (TRUE) { *ctx = 0; ret = io_setup(nr_events, ctx); DEBUG_ONLY(local_errno = errno;) assert((0 == ret) || (EAGAIN == local_errno) || (WBTEST_ENABLED(WBTEST_LOW_MEMORY) && ENOMEM == local_errno)); if (-1 == ret) { nr_events /= 2; if (0 == nr_events) { aio_shim_errstr = io_setup_errstr; return -1; } } else return 0; } } /* Initializes the multiplexing thread and the shutdown eventfd. */ STATICFNDCL int aio_shim_thread_init(gd_addr *gd) { /* Sets up the AIO context, two eventfd's and the multiplexing thread */ int ret, ret2, local_errno; struct gd_info *gdi, tmp_gdi; sigset_t savemask; CHECK_STRUCT_AIOCB; DEBUG_ONLY(aio_shim_errstr = NULL;) /* initialize fields of tmp_gdi */ tmp_gdi.exit_efd = FD_INVALID; tmp_gdi.laio_efd = FD_INVALID; tmp_gdi.ctx = 0; tmp_gdi.num_ios = 0; tmp_gdi.err_syscall = NULL; tmp_gdi.save_errno = 0; /* Sets up the eventfd which notifies the multiplexing thread that it must exit. */ if (-1 != (ret = eventfd(0, 0))) tmp_gdi.exit_efd = ret; else { local_errno = errno; assert(FALSE); aio_shim_errstr = "eventfd(EXIT_EFD)"; return -1; } /* Sets up the eventfd which notifies the multiplexing thread that an AIO completed. */ if (-1 != (ret = eventfd(0, 0))) tmp_gdi.laio_efd = ret; else { local_errno = errno; assert(FALSE); CLEANUP_AIO_SHIM_THREAD_INIT(tmp_gdi); aio_shim_errstr = "eventfd(LAIO_EFD)"; return -1; } /* Sets up the AIO context */ if (-1 == (ret = aio_shim_setup(&tmp_gdi.ctx))) { /* The only "allowed" error is EAGAIN. The errstr should have been set by * aio_shim_setup(). */ local_errno = errno; assert(NULL != aio_shim_errstr); assert(EAGAIN == local_errno || (WBTEST_ENABLED(WBTEST_LOW_MEMORY) && ENOMEM == local_errno)); CLEANUP_AIO_SHIM_THREAD_INIT(tmp_gdi); return -1; } gdi = gtm_malloc(SIZEOF(struct gd_info)); *gdi = tmp_gdi; /* Temporarily block external signals for the worker thread. This enforces that * the multiplexing worker thread will not invoke signal handlers (e.g. timer_handler()) * and manipulate global variables while the main process/thread is concurrently running * and using them. * Note that SIGPROCMASK relies on multi_thread_in_use and so we must set it. * TODO: this code should ideally be merged with gtm_multi_thread.c When gtm_multi_thread() * is enhanced to create a thread in the background. Right now it returns only when the * thread completes. */ multi_thread_in_use = TRUE; assert(TRUE == blocksig_initialized); /* We block all signals, except those which could be generated from within the worker thread. * Every other signal must be externally generated and should drive the signal handler from * the main process and not this worker thread. */ SIGPROCMASK(SIG_BLOCK, &block_worker, &savemask, ret); ret = pthread_create(&gdi->pt, NULL, io_getevents_multiplexer, gdi); if (0 != ret) { /* We don't want to clobber ret so we use ret2. */ SIGPROCMASK(SIG_SETMASK, &savemask, NULL, ret2); multi_thread_in_use = FALSE; assert(EAGAIN == ret); CLEANUP_AIO_SHIM_THREAD_INIT(tmp_gdi); gtm_free(gdi); aio_shim_errstr = "pthread_create()"; errno = ret; /* pthread_create() returns errno in ret. */ return -1; } multi_thread_in_use = FALSE; SIGPROCMASK(SIG_SETMASK, &savemask, NULL, ret); gd->thread_gdi = gdi; return 0; } /* Similar to aio_cancel(), cancels all outstanding IO's by destroying the kernel context * associated with the region. Also destroys the multiplexing thread to clean resources. */ void aio_shim_destroy(gd_addr *gd) { struct gd_info *gdi; int ret, local_errno; char *eventfd_str = "GTMROCKS"; mstr *gldname; gd_addr *addr_ptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; gdi = gd->thread_gdi; if (NULL == gdi) { /* A write didn't happen. */ return; } /* We notify the thread to exit; note we only need to write 8 bytes (exactly) to the fd. */ assert(EVENTFD_SZ == STRLEN(eventfd_str)); DOWRITERC(gdi->exit_efd, eventfd_str, EVENTFD_SZ, ret); assert(0 == ret); if (-1 == ret) ISSUE_SYSCALL_RTS_ERROR_WITH_GD(gd, "aio_shim_destroy::write", errno); /* Wait on the thread exit */ ret = pthread_join(gdi->pt, NULL); assert(0 == ret); if (0 != ret) ISSUE_SYSCALL_RTS_ERROR_WITH_GD(gd, "aio_shim_destroy::pthread_join", errno); /* Destroy the kernel context */ ret = io_destroy(gdi->ctx); if (0 != ret) local_errno = errno; assert(0 == ret); if (-1 == ret) ISSUE_SYSCALL_RTS_ERROR_WITH_GD(gd, "aio_shim_destroy::io_destroy", local_errno); /* If there was at least one region with reg->was_open = TRUE, then it is possible regions in other glds * (different from "gd" have a "udi" with "udi->owning_gd" == "gd". So we would need to look at all regions * across all glds opened by this process. If no was_open region was ever seen by this process, then it is * enough to look at regions in just the current gld ("gd"). */ if (TREF(was_open_reg_seen)) { /* Iterate over all the regions in the global directory and clean their WIP queues */ for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) aio_gld_clean_wip_queue(addr_ptr, gd); } else aio_gld_clean_wip_queue(gd, gd); /* By this point, we must have no more outstanding IOs */ assert(0 == gdi->num_ios); /* Delete the thread_gdi to leave us in a state consistent with no thread existing. */ gtm_free(gdi); gd->thread_gdi = NULL; } void aio_gld_clean_wip_queue(gd_addr *input_gd, gd_addr *match_gd) { unix_db_info *udi; gd_region *reg, *r_top; struct gd_info *gdi; gdi = match_gd->thread_gdi; assert(NULL != gdi); for (reg = input_gd->regions, r_top = reg + input_gd->n_regions; reg < r_top; reg++) { assert(input_gd == reg->owning_gd); if (reg->open && reg->dyn.addr->asyncio && (dba_cm != reg->dyn.addr->acc_meth)) { udi = FILE_INFO(reg); /* We don't call clean_wip_queue() if we don't have any outstanding IOs in the region. * This reduces wip queue header lock contention if lots of processes exit at the same time. * Note that in case multiple regions map to same db file, it is possible udi->owning_gd * points to a gld different from "match_gd". In that case, skip the wip queue clean as we are * interested only in cleaning up aio writes issued from "match_gd". */ if ((udi->owning_gd == match_gd) && (0 < gdi->num_ios)) clean_wip_queue(udi); } } } /* Lazily loads the multiplexing thread and submits an IO. */ int aio_shim_write(gd_region *reg, struct aiocb *aiocbp) { unix_db_info *udi; struct gd_info *gdi; gd_addr *owning_gd; int ret, local_errno; struct iocb *iocbp; struct iocb *cb[1]; udi = FILE_INFO(reg); assert(gtm_is_main_thread() || (gtm_jvm_process && process_exiting)); owning_gd = udi->owning_gd; assert(NULL != owning_gd); if (NULL == (gdi = owning_gd->thread_gdi)) { /* No thread is servicing this global directory -- set it up. */ ret = aio_shim_thread_init(owning_gd); if (0 != ret) local_errno = errno; assert((0 == ret) || (EAGAIN == local_errno)); if (-1 == ret) { /* aio_shim_thread_init() should set aio_shim_errstr */ assert(NULL != aio_shim_errstr); /* Caller will handle -1 case by calling wcs_wterror() which * looks at aio_shim_errstr to report error detail. */ return -1; } gdi = owning_gd->thread_gdi; } assert(NULL != gdi); /* submit the write */ CHECK_ERROR_IN_WORKER_THREAD(reg, udi); aiocbp->status = EINPROGRESS; iocbp = (struct iocb *)aiocbp; iocbp->aio_lio_opcode = IOCB_CMD_PWRITE; iocbp->aio_resfd = gdi->laio_efd; iocbp->aio_flags = IOCB_FLAG_RESFD; cb[0] = iocbp; ATOMIC_ADD_FETCH(&gdi->num_ios, 1); ret = io_submit(gdi->ctx, 1, cb); /* the only acceptable error is EAGAIN in our case */ if (0 > ret) local_errno = errno; assert((1 == ret) || (EAGAIN == errno)); if (1 == ret) return 0; /* we need to rescind the write */ ATOMIC_SUB_FETCH(&gdi->num_ios, 1); aio_shim_errstr = "io_submit()"; return -1; } #endif fis-gtm-V7.0-005/sr_unix/anticipatory_freeze.c0000644000032200000250000003654514342376327020276 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2021 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 #include "gtm_stdio.h" #include "gtm_ctype.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "error.h" #include "repl_sem.h" #include "gtmimagename.h" #include "hashtab_str.h" #include "eintr_wrappers.h" #include "gtmmsg.h" #include "anticipatory_freeze.h" #ifdef DEBUG #include "dpgbldir.h" #include "is_proc_alive.h" #endif #define MAX_TAG_LEN 128 /* Maximum size of an error mnemonic */ #define MAX_READ_SZ 1024 /* Mnemonic + flags shouldn't exceed this limit */ #define COMMENT_DELIMITER ';' #define NEWLINE 0x0A #define EOL_REACHED (char *)(-1) #define EOF_REACHED (char *)(-2) #define EXHAUST_CURRENT_LINE(BUFF, HANDLE, FGETS_RC) \ { \ assert(NEWLINE != BUFF[STRLEN(BUFF) - 1]); \ while (TRUE) \ { \ FGETS_FILE(BUFF, MAX_READ_SZ, HANDLE, FGETS_RC); \ if ((NULL == FGETS_RC) || NEWLINE == BUFF[STRLEN(BUFF) - 1]) \ break; \ } \ } error_def(ERR_ASSERT); error_def(ERR_CUSTERRNOTFND); error_def(ERR_CUSTERRSYNTAX); error_def(ERR_CUSTOMFILOPERR); error_def(ERR_DSKSPCAVAILABLE); error_def(ERR_ENOSPCQIODEFER); error_def(ERR_REPLINSTFREEZECOMMENT); error_def(ERR_REPLINSTFROZEN); error_def(ERR_TEXT); error_def(ERR_INSTFRZDEFER); GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF boolean_t is_src_server; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; #ifdef DEBUG GBLREF uint4 process_id; GBLREF volatile boolean_t timer_in_handler; GBLREF boolean_t multi_thread_in_use; #endif /* Typically prototypes are included in the header file. But, in this case the static function - get_mnemonic_offset - has the * hash_table_str as one of the function parameters which means all the files which includes anticipatory_freeze.h needs to include * hashtab_str.h and since there are lot of such C files, we chose to define static function prototypes in the C file itself. */ STATICFNDCL char *scan_space(FILE *handle, char *buff, char *buffptr, char *buff_top); STATICFNDCL int get_mnemonic_offset(hash_table_str **err_hashtab, char *mnemonic_buf, int mnemonic_len); STATICFNDCL boolean_t is_message_excluded(int msg_id); /* Scan through whitespace in the current buffer (read more if required) */ STATICFNDEF char *scan_space(FILE *handle, char *buff, char *buffptr, char *buff_top) { char *fgets_rc; while (TRUE) { for (; (buffptr < buff_top) && (ISSPACE_ASCII(*buffptr)); buffptr++) ; if (buffptr < buff_top) return buffptr; /* first non-whitespace character */ if (NEWLINE == *(buffptr - 1)) return EOL_REACHED; /* current buffer is exhausted and we haven't seen a newline; read more */ FGETS_FILE(buff, MAX_READ_SZ, handle, fgets_rc); if (NULL == fgets_rc) break; buffptr = buff; buff_top = buffptr + STRLEN(buff); } return EOF_REACHED; } STATICFNDEF int get_mnemonic_offset(hash_table_str **err_hashtab, char *mnemonic_buf, int mnemonic_len) { const err_msg *msg_beg, *msg_top; hash_table_str *tmp_err_hashtab; ht_ent_str *err_htent; stringkey key; err_msg *msg_info; boolean_t added; DEBUG_ONLY(int idx;) msg_beg = merrors_ctl.fst_msg; msg_top = msg_beg + merrors_ctl.msg_cnt; assert((MAX_TAG_LEN > mnemonic_len) && (0 < mnemonic_len)); /* For SCI */ assert('\0' == mnemonic_buf[mnemonic_len]); if (NULL == (tmp_err_hashtab = *err_hashtab)) { /* create and populate hash-table for future lookups */ tmp_err_hashtab = (hash_table_str *)malloc(SIZEOF(hash_table_str)); DEBUG_ONLY(tmp_err_hashtab->base = NULL); init_hashtab_str(tmp_err_hashtab, (msg_top - msg_beg) * (100.0 / HT_LOAD_FACTOR), HASHTAB_NO_COMPACT, HASHTAB_NO_SPARE_TABLE); assert(tmp_err_hashtab->base); for (msg_info = (err_msg *)msg_beg; msg_info < msg_top; msg_info++) { key.str.addr = msg_info->tag; key.str.len = STRLEN(msg_info->tag); COMPUTE_HASH_STR(&key); added = add_hashtab_str(tmp_err_hashtab, &key, msg_info, &err_htent); assert(added); assert(err_htent->value); assert(msg_info->tag == ((err_msg *)(err_htent->value))->tag); } *err_hashtab = tmp_err_hashtab; } assert(NULL != tmp_err_hashtab); /* lookup for the mnemonic */ key.str.addr = mnemonic_buf; key.str.len = mnemonic_len; COMPUTE_HASH_STR(&key); if (NULL == (err_htent = lookup_hashtab_str(tmp_err_hashtab, &key))) return -1; msg_info = (err_msg *)(err_htent->value); assert(msg_info >= msg_beg && msg_info < msg_top); return msg_info - msg_beg; } /* Determine if the given msg_id has been explicitly excluded from being able to effectualte an anticipatory freeze */ STATICFNDCL boolean_t is_message_excluded(int msg_offset) { int cnt; /* See if msg_id is one of the undocumented messages */ for (cnt = 0; cnt < merrors_ctl.undocmsg_cnt; cnt++) /* small list of 25-30 messages */ if (msg_offset == merrors_ctl.undocmsg[cnt]) return TRUE; assert(cnt == merrors_ctl.undocmsg_cnt); return FALSE; } /* Determine whether a given msg_id qualifies for an anticipatory freeze or not */ boolean_t is_anticipatory_freeze_needed(sgmnt_addrs *csa, int msg_id) { const err_ctl *ctl; int idx; jnlpool_addrs_ptr_t local_jnlpool; /* Certain error messages should NOT trigger a freeze even if they are so configured in the custom errors file as they might * result in instance freezes that can be set indefinitely. Currently, we know of at least 3 such messages: * 1. ENOSPCQIODEFER and INSTFRZDEFER : To ensure we don't set anticipatory freeze if we don't/can't hold crit * (due to possible deadlock) * 2. DSKSPCAVAILABLE : To ensure we don't set anticipatory freeze if the disk space becomes available after an initial * lack of space. * These messages have csa == NULL so they are guarranteed to not trigger a freeze. * However, ENOSPCQIODEFER is returned and later passed to rts_error with a non-NULL csa, so return FALSE in that case. */ if (!csa || !csa->nl || !csa->hdr || !csa->hdr->freeze_on_fail || (ERR_ENOSPCQIODEFER == msg_id)) return FALSE; assert((ERR_DSKSPCAVAILABLE != msg_id) && (ERR_INSTFRZDEFER != msg_id)); ctl = err_check(msg_id); if (NULL != ctl) { GET_MSG_IDX(msg_id, ctl, idx); local_jnlpool = csa->jnlpool ? csa->jnlpool : jnlpool; assert(local_jnlpool && local_jnlpool->jnlpool_ctl); assert(idx < ARRAYSIZE(local_jnlpool->jnlpool_ctl->merrors_array)); if (local_jnlpool->jnlpool_ctl->merrors_array[idx] & AFREEZE_MASK) return TRUE; } return FALSE; } /* set the anticipatory freeze in the journal pool */ void set_anticipatory_freeze(sgmnt_addrs *csa, int msg_id) { boolean_t was_crit; sgmnt_addrs *repl_csa; const err_msg *msginfo; jnlpool_addrs_ptr_t save_jnlpool; assert(is_anticipatory_freeze_needed(csa, msg_id)); save_jnlpool = jnlpool; if (csa->jnlpool && (csa->jnlpool != jnlpool)) jnlpool = csa->jnlpool; assert(jnlpool && jnlpool->jnlpool_ctl); assert(jnlpool->jnlpool_dummy_reg); /* other asserts in is anticipatory_freeze_needed */ repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; assert(NULL != repl_csa); was_crit = repl_csa->now_crit; if (!was_crit) { if (csa->now_crit) grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY); else if (FALSE == grab_lock(jnlpool->jnlpool_dummy_reg, FALSE, GRAB_LOCK_ONLY)) { MSGID_TO_ERRMSG(msg_id, msginfo); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_INSTFRZDEFER, 4, LEN_AND_STR(msginfo->tag), REG_LEN_STR(csa->region)); if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; return; } } /* Now that we hold necessary locks, set the freeze and the comment field */ jnlpool->jnlpool_ctl->freeze = TRUE; GENERATE_INST_FROZEN_COMMENT(jnlpool->jnlpool_ctl->freeze_comment, SIZEOF(jnlpool->jnlpool_ctl->freeze_comment), msg_id); /* TODO : Do we need a SHM_WRITE_MEMORY_BARRIER ? */ if (!was_crit) rel_lock(jnlpool->jnlpool_dummy_reg); if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; } /* initialize jnlpool_ctl->merrors_array to set up the list of errors that should trigger anticipatory freeze errors */ boolean_t init_anticipatory_freeze_errors() { int idx, save_errno, status, mnemonic_len, offset, line_no; FILE *handle; char *fgets_rc; char buff[MAX_READ_SZ], mnemonic_buf[MAX_TAG_LEN]; char *buffptr, *buff_top, *errptr, *errptr_top; mstr custom_err_file; hash_table_str *err_hashtab = NULL; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* TODO : Currently, we process errors that belong to merrors[] as those are the ones related to database/journal. Need * to check if cmerrors/cmierrors also need to be included in this list or not. */ assert(IS_MUPIP_IMAGE); /* is_src_server is not initialized at this point */ /* invoke when not previously initialized */ assert(jnlpool && jnlpool->jnlpool_ctl && !jnlpool->jnlpool_ctl->instfreeze_environ_inited); assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); /* should hold journal pool access control semaphore */ /* Now, read the custom errors file and populate the journal pool */ custom_err_file = TREF(gtm_custom_errors); Fopen(handle, custom_err_file.addr, "r"); if (NULL == handle) { save_errno = errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_CUSTOMFILOPERR, 4, LEN_AND_LIT("fopen"), custom_err_file.len, custom_err_file.addr, save_errno); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_CUSTOMFILOPERR, 4, LEN_AND_LIT("fopen"), custom_err_file.len, custom_err_file.addr, save_errno); return FALSE; } line_no = 0; /* The code below parses a custom errors file in the following format. * * file ::= line* * line ::= mnemonic SPACE* comment? EOL | * comment EOL * mnemonic ::= ALNUM+ * comment ::= COMMENT_DELIMITER ANY* * * SPACE ::= any ASCII white space character * COMMENT_DELIMITER ::= ';' * ANY ::= any ASCII character except end of line * EOL ::= ASCII end of line character * ALNUM ::= any ASCII alphanumeric character * * NOTES: * "*" denotes zero-or-more of the previous item * "?" denotes zero-or-one of the previous item * "+" denotes one-or-more of the previous item * "|" denotes multiple alternatives * The mnemonic must match an entry in the GT.M error message list. * Anything between the COMMENT_DELIMITER and EOL is ignored. * Each iteration of the loop parses one line. */ while (TRUE) { FGETS_FILE(buff, MAX_READ_SZ, handle, fgets_rc); line_no++; if (NULL == fgets_rc) break; buffptr = buff; buff_top = buffptr + STRLEN(buff); errptr = &mnemonic_buf[0]; errptr_top = errptr + MAX_TAG_LEN; /* The first character has to be alpha-numeric or a comment */ if (!ISALNUM_ASCII(*buffptr) && (COMMENT_DELIMITER != *buffptr)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_CUSTERRSYNTAX, 3, custom_err_file.len, custom_err_file.addr, line_no, ERR_TEXT, 2, LEN_AND_LIT("First character should be comment (;) or alpha numeric")); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_CUSTERRSYNTAX, 3, custom_err_file.len, custom_err_file.addr, line_no, ERR_TEXT, 2, LEN_AND_LIT("First character should be comment (;) or alpha numeric")); return FALSE; } while (ISALNUM_ASCII(*buffptr)) { *errptr++ = *buffptr++; if (errptr > errptr_top) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_CUSTERRSYNTAX, 3, custom_err_file.len, custom_err_file.addr, line_no, ERR_TEXT, 2, LEN_AND_LIT("Mnemonic too long")); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_CUSTERRSYNTAX, 3, custom_err_file.len, custom_err_file.addr, line_no, ERR_TEXT, 2, LEN_AND_LIT("Mnemonic too long")); return FALSE; } assert(buffptr < buff_top); /* errptr > errptr_top should fail before this */ } *errptr = '\0'; if (0 < (mnemonic_len = (errptr - &mnemonic_buf[0]))) { /* Non-empty error mnemonic found; look it up */ if (-1 == (offset = get_mnemonic_offset(&err_hashtab, mnemonic_buf, mnemonic_len))) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CUSTERRNOTFND, 2, mnemonic_len, mnemonic_buf); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CUSTERRNOTFND, 2, mnemonic_len, mnemonic_buf); return FALSE; } if (!is_message_excluded(offset)) jnlpool->jnlpool_ctl->merrors_array[offset] |= AFREEZE_MASK; /* duplicate entries are not error */ } assert(ISSPACE_ASCII(*buffptr) || (COMMENT_DELIMITER == *buffptr)); if (EOL_REACHED == (buffptr = scan_space(handle, buff, buffptr, buff_top))) continue; else if (EOF_REACHED == buffptr) break; assert(buffptr < buff_top); if (COMMENT_DELIMITER != *buffptr) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_CUSTERRSYNTAX, 3, custom_err_file.len, custom_err_file.addr, line_no, ERR_TEXT, 2, LEN_AND_LIT("Unexpected character found after mnemonic")); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_CUSTERRSYNTAX, 3, custom_err_file.len, custom_err_file.addr, line_no, ERR_TEXT, 2, LEN_AND_LIT("Unexpected character found after mnemonic")); return FALSE; } /* Need to ignore the rest of the current buffer and exhaust the current line */ if (NEWLINE != *(buff_top - 1)) EXHAUST_CURRENT_LINE(buff, handle, fgets_rc); } if (err_hashtab) { free_hashtab_str(err_hashtab); free(err_hashtab); } if (!feof(handle)) { save_errno = errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_CUSTOMFILOPERR, 4, LEN_AND_LIT("fgets"), custom_err_file.len, custom_err_file.addr, save_errno); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_CUSTOMFILOPERR, 4, LEN_AND_LIT("fgets"), custom_err_file.len, custom_err_file.addr, save_errno); return FALSE; } FCLOSE(handle, status); if (SS_NORMAL != status) { save_errno = errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_CUSTOMFILOPERR, 4, LEN_AND_LIT("fclose"), custom_err_file.len, custom_err_file.addr, save_errno); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_CUSTOMFILOPERR, 4, LEN_AND_LIT("fclose"), custom_err_file.len, custom_err_file.addr, save_errno); return FALSE; } jnlpool->jnlpool_ctl->instfreeze_environ_inited = TRUE; return TRUE; } #ifdef DEBUG void clear_fake_enospc_if_master_dead(void) { gd_addr *addr_ptr; gd_region *r_top, *r_local; sgmnt_addrs *csa; assert(!multi_thread_in_use); /* fake-enospc would not have been set if in threaded-code */ assert(jnlpool && jnlpool->jnlpool_ctl); if((jnlpool->jnlpool_ctl->jnlpool_creator_pid != process_id) && !is_proc_alive(jnlpool->jnlpool_ctl->jnlpool_creator_pid, 0)) { for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) { for (r_local = addr_ptr->regions, r_top = r_local + addr_ptr->n_regions; r_local < r_top; r_local++) { if (!IS_REG_BG_OR_MM(r_local)) continue; csa = REG2CSA(r_local); if ((NULL != csa) && (NULL != csa->nl)) if (csa->nl->fake_db_enospc || csa->nl->fake_jnl_enospc) { csa->nl->fake_db_enospc = FALSE; csa->nl->fake_jnl_enospc = FALSE; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_TEXT, 2, DB_LEN_STR(r_local), ERR_TEXT, 2, LEN_AND_LIT("Resetting fake_db_enospc and fake_jnl_enospc because " "fake ENOSPC master is dead")); } } } } } #endif fis-gtm-V7.0-005/sr_unix/arlinkdbg.h0000644000032200000250000000164314342376327016161 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2015 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. * * * ****************************************************************/ /* Header file to control debugging for autorelink */ #ifndef ARLINKDBG_H #define ARLINKDBG_H /* To enable debugging macros (output to console) uncomment the following #define */ /*#define DEBUG_ARLINK*/ #ifdef DEBUG_ARLINK # define DBGARLNK(x) DBGFPF(x) # define DBGARLNK_ONLY(x) x # include "gtm_stdio.h" # include "gtmio.h" # include "io.h" #else # define DBGARLNK(x) # define DBGARLNK_ONLY(x) #endif #endif fis-gtm-V7.0-005/sr_unix/ast.h0000755000032200000250000000074514342376327015020 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, Inc. * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #define ENABLE_AST #define DISABLE_AST fis-gtm-V7.0-005/sr_unix/auto_zlink.h0000755000032200000250000000133214342376327016401 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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. * * * ****************************************************************/ #ifndef AUTO_ZLINK_INCLUDED #define AUTO_ZLINK_INCLUDED void auto_zlink(int rtnhdridx); void auto_relink_check(int rtnhdridx, int lbltblidx); void explicit_relink_check(rhdtyp *rhd, boolean_t setproxy); #endif fis-gtm-V7.0-005/sr_unix/backup_buffer_flush.c0000755000032200000250000001411214342376327020214 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_stat.h" #include #include "gtm_unistd.h" #include "gtm_fcntl.h" #include "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmio.h" #include "util.h" #include "memcoherency.h" #include "shmpool.h" #include "gtmimagename.h" #include "mupipbckup.h" #include "send_msg.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif GBLREF uint4 process_id; error_def(ERR_BKUPTMPFILOPEN); error_def(ERR_BKUPTMPFILWRITE); error_def(ERR_TEXT); ZOS_ONLY(error_def(ERR_BADTAG);) boolean_t backup_buffer_flush(gd_region *reg) { int write_size, fd; int4 status; sgmnt_addrs *csa; shmpool_buff_hdr_ptr_t sbufh_p; shmpool_blk_hdr_ptr_t sblkh_p, next_sblkh_p; DEBUG_ONLY(int flush_cnt;) ZOS_ONLY(int realfiletag;) int muinc_adjust; csa = &FILE_INFO(reg)->s_addrs; sbufh_p = csa->shmpool_buffer; if (!shmpool_lock_hdr_nowait(reg)) { #ifdef DEBUG /* someone else is flushing it right now */ if (!IS_GTM_IMAGE) util_out_print("Process !12UL has the shmpool lock preventing backup buffer flush.", TRUE, sbufh_p->shmpool_crit_latch.u.parts.latch_pid); #endif return FALSE; } if (0 != sbufh_p->backup_errno) { /* Since this is signal/mupip initiated, the proper message will be (or already has been) output on exit. */ shmpool_unlock_hdr(reg); return FALSE; /* Async error state change (perhaps mupip stop) -- nothing to do if backup is dying */ } /* See if there are any buffers needing flushing. Note that we are holding the shmpool lock across * the IO we will be doing. This simplifies the backup logic substantialy. If we released and obtained * the lock for each buffer we dequeue (to allow other processes to proceed while we are doing IO) it * is likely that some of those other processes would get the idea to also run a buffer flush. Then we * would have to manage the task of doing multiple simultaneous IO to the temporary file potentially * resulting in gaps in the file which is something we definitely do not want to do. Besides, if a backup * is going on (and thus causing the flush) we are likely doing this in crit which is holding up all * other processes anyway so we aren't losing much if anything. This is also historically how this * has been done to assure the robustness of the temporary file. SE 1/2005. */ if (0 < sbufh_p->backup_cnt) { /* open the file, write to it at the address and close the file */ OPENFILE(sbufh_p->tempfilename, O_RDWR, fd); if (FD_INVALID == fd) { sbufh_p->failed = process_id; sbufh_p->backup_errno = errno; csa->nl->nbb = BACKUP_NOT_IN_PROGRESS; send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_BKUPTMPFILOPEN, 2, LEN_AND_STR(sbufh_p->tempfilename), sbufh_p->backup_errno); assert(EACCES == sbufh_p->backup_errno); shmpool_unlock_hdr(reg); return FALSE; } #ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(fd, TAG_BINARY, &realfiletag)) TAG_POLICY_SEND_MSG(sbufh_p->tempfilename, errno, realfiletag, TAG_BINARY); #endif /* This adjustment is necessary since the shmpool_blk_hdr had been previously tied to the backup format. * We now have a separate structure for the backup. muinc_adjust is used to translate in a * performance sensitive-way. Currently, the structures only differ by 8 bytes at the front which is * not relevant to the backup and only on 64bit platforms. Asserts are added to assure the design * assumptions are not violated. */ muinc_adjust = SIZEOF(*sblkh_p) - SIZEOF(muinc_blk_hdr_t); NON_GTM64_ONLY(assert(0 == muinc_adjust)); GTM64_ONLY(assert(8 == muinc_adjust)); write_size = SIZEOF(muinc_blk_hdr_t) + sbufh_p->blk_size; DEBUG_ONLY(flush_cnt = 0); for (sblkh_p = SBLKP_REL2ABS(&sbufh_p->que_backup, fl); sblkh_p != (shmpool_blk_hdr_ptr_t)&sbufh_p->que_backup; sblkh_p = next_sblkh_p) { /* Loop through the queued backup blocks */ DEBUG_ONLY(++flush_cnt); VERIFY_QUEUE((que_head_ptr_t)&sbufh_p->que_free); VERIFY_QUEUE((que_head_ptr_t)&sbufh_p->que_backup); next_sblkh_p = SBLKP_REL2ABS(sblkh_p, fl); /* Get next offset now in case remove entry */ /* Need read fence for checking if block has valid data or not since these fields are not set under lock */ SHM_READ_MEMORY_BARRIER; assert(SHMBLK_BACKUP == sblkh_p->blktype); if (!sblkh_p->valid_data) continue; /* This block has valid data. Flush it first, then dequeue it. It won't hurt if this process faile between the time that it starts the IO and it dequeues the block. The worst that would happen is the block would be in the temporary file twice which, while a little annoying is not functionally incorrect. If we dequeue it first though, there is a possibility that the IO could be lost and an invalid block written to the temporary file or missed altogether. */ LSEEKWRITE(fd, sbufh_p->dskaddr, ((char *)sblkh_p + muinc_adjust), write_size, status); if (0 != status) { sbufh_p->failed = process_id; sbufh_p->backup_errno = status; csa->nl->nbb = BACKUP_NOT_IN_PROGRESS; assert(FALSE); send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_BKUPTMPFILWRITE, 2, LEN_AND_STR(sbufh_p->tempfilename), status); break; /* close file, release lock and return now.. */ } /* Update disk addr with record just written */ sbufh_p->dskaddr += write_size; /* Now we can deque this entry from the backup queue safely and release it */ shmpool_blk_free(reg, sblkh_p); } CLOSEFILE_RESET(fd, status); /* resets "fd" to FD_INVALID */ } shmpool_unlock_hdr(reg); return TRUE; } fis-gtm-V7.0-005/sr_unix/bit_op.h0000755000032200000250000000075614342376327015507 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ typedef enum { BIT_SET, BIT_CLEAR } bit_op_t; fis-gtm-V7.0-005/sr_unix/buildaux.csh0000755000032200000250000001510114342376327016364 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2020 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. # # # ################################################################# # ########################################################################################### # # buildaux.csh - Build GT.M auxiliaries: dse, gtmsecshr, lke, mupip. # # Arguments: # $1 - version number or code # $2 - image type (b[ta], d[bg], or p[ro]) # $3 - target directory # $4 - [auxillaries to build] e.g. dse mupip gtcm_pkdisp gtcm_server etc. # Special value "shr" implies build "mumps" and ALL auxillaries # ########################################################################################### set buildaux_status = 0 source $gtm_tools/gtm_env.csh if ( $1 == "" ) then @ buildaux_status++ endif if ( $2 == "" ) then @ buildaux_status++ endif if ( $3 == "" ) then @ buildaux_status++ endif switch ($2) case "[bB]*": set gt_ld_options = "$gt_ld_options_bta" set gt_image = "bta" breaksw case "[dD]*": set gt_ld_options = "$gt_ld_options_dbg" set gt_image = "dbg" breaksw case "[pP]*": set gt_ld_options = "$gt_ld_options_pro" set gt_image = "pro" breaksw default: @ buildaux_status++ breaksw endsw set setactive_parms = ( $1 $2 ) ; source $gtm_tools/setactive.csh if ( $buildaux_status ) then echo "buildaux-I-usage, Usage: buildaux.csh [auxillary]" exit $buildaux_status endif set buildaux_auxillaries = "gde gtmsecshr dse lke mupip gtcm_server gtcm_gnp_server gtmcrypt" set buildaux_utilities = "gtcm_pkdisp gtcm_shmclean gtcm_play" set buildaux_executables = "$buildaux_auxillaries $buildaux_utilities" set buildaux_validexecutable = 0 foreach executable ( $buildaux_executables ) setenv buildaux_$executable 0 end set new_auxillarylist = "" set do_buildshr = 0 set skip_auxillaries = 0 if (4 <= $#) then if (($# == 4) && ("$4" == "shr")) then # "shr" is special value. Handle separately. # build "mumps" and ALL executables set do_buildshr = 1 set argv[4] = "" endif foreach auxillary ( $argv[4-] ) if ( "$auxillary" == "lke") then set new_auxillarylist = "$new_auxillarylist lke gtcm_gnp_server" else if ( "$auxillary" == "gnpclient") then set do_buildshr = 1 else if ( "$auxillary" == "gnpserver") then set new_auxillarylist = "$new_auxillarylist gtcm_gnp_server" else if ( "$auxillary" == "cmisockettcp") then set do_buildshr = 1 set new_auxillarylist = "$new_auxillarylist gtcm_gnp_server" else if ( "$auxillary" == "gtcm") then set new_auxillarylist = "$new_auxillarylist gtcm_server gtcm_play gtcm_shmclean gtcm_pkdisp" else if ( "$auxillary" == "stub") then set new_auxillarylist = "$new_auxillarylist dse mupip gtcm_server gtcm_gnp_server gtcm_play" set new_auxillarylist = "$new_auxillarylist gtcm_pkdisp gtcm_shmclean" else if ("$auxillary" == "mumps") then set do_buildshr = 1 set skip_auxillaries = 1 else set new_auxillarylist = "$new_auxillarylist $auxillary" endif end endif if ( $4 == "" ) then foreach executable ( $buildaux_executables ) setenv buildaux_$executable 1 end else foreach executable ( $buildaux_executables ) foreach auxillary ( $new_auxillarylist ) if ( "$auxillary" == "$executable" ) then set buildaux_validexecutable = 1 setenv buildaux_$auxillary 1 break endif end end if ( $buildaux_validexecutable == 0 && "$new_auxillarylist" != "" ) then echo "buildaux-E-AuxUnknown -- Auxillary, ""$argv[4-]"", is not a valid one" echo "buildaux-I-usage, Usage: buildaux.csh [auxillary-list]" @ buildaux_status++ exit $buildaux_status endif endif unalias ls rm cat # The below 3 env vars are needed by buildaux_*.csh scripts setenv dollar_sign \$ setenv mach_type `uname -m` setenv platform_name `uname | sed 's/-//g' | tr '[A-Z]' '[a-z]'` set cmdfile="$gtm_log/buildaux_$$" rm -f $cmdfile $cmdfile.err set outlist = "" set dollar = '$' echo "alias err_check 'if (${dollar}status) echo BUILDAUX-E-FAIL : Failed from \\!:1 >>&! '$cmdfile'.err'" >> $cmdfile.csh if ($do_buildshr) then set outfile = "${cmdfile}_buildshr.log" set redir=">& $outfile" set outlist = "$outlist $outfile" echo "($gtm_tools/buildshr.csh $1 $2 ${gtm_root}/$1/$2; err_check buildshr.csh) $redir &" >> $cmdfile.csh endif if (! $skip_auxillaries) then if ( $buildaux_gde == 1 ) then set outfile = "${cmdfile}_buildaux_gde.log" set redir=">& $outfile" set outlist = "$outlist $outfile" # Building GDE cannot happen parallely with buildshr as this stage requires "mumps" which is built by "buildshr". # Take that into account when parallelizing. If buildshr is also happening now, then defer buildgde to after that. if ($do_buildshr) then echo "wait" >> $cmdfile.csh endif echo "($gtm_tools/buildaux_gde.csh $gt_image; err_check buildaux_gde.csh) $redir &" >> $cmdfile.csh endif set double_quote = '"' set args3 = "$gt_image ${double_quote}${gt_ld_options}${double_quote} $3" set args3exelist = "gtmsecshr dse lke mupip gtcm_server gtcm_gnp_server gtcm_play gtcm_pkdisp gtcm_shmclean" foreach exe ($args3exelist) set bg = '&' set val = `eval echo '${'buildaux_${exe}'}'` if ($val == 1) then set outfile = "${cmdfile}_buildaux_${exe}.log" set redir=">& $outfile" set outlist = "$outlist $outfile" echo "($gtm_tools/buildaux_${exe}.csh $args3; err_check buildaux_${exe}.csh) $redir $bg" >> $cmdfile.csh endif end # Create the plugin directory, copy the files and set it up so that build.sh can build the needed libraries. if ($buildaux_gtmcrypt == 1) then set outfile = "${cmdfile}_buildaux_gtmcrypt.log" set redir=">& $outfile" set outlist = "$outlist $outfile" echo "($gtm_tools/buildaux_gtmcrypt.csh $gt_image; err_check buildaux_gtmcrypt.csh) $redir &" >> $cmdfile.csh endif endif echo "wait" >> $cmdfile.csh set cmdout="$cmdfile.out" source $cmdfile.csh >& $cmdout set stat = $status cat $outlist rm $outlist if ($stat) then cat $cmdout echo "buildaux-E-$cmdfile.csh : Failed executing $cmdfile.csh" @ buildaux_status++ endif if (-s $cmdfile.err) then cat $cmdfile.err echo "buildaux-E-err : $cmdfile.err found. A script in $cmdfile.csh failed" @ buildaux_status++ else if (-e $cmdfile.err) rm $cmdfile.err rm $cmdfile.csh rm $cmdout endif exit $buildaux_status fis-gtm-V7.0-005/sr_unix/buildaux_dse.csh0000644000032200000250000000310014342376327017210 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2015 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. # # # ################################################################# # # Note: This script only works when called from buildaux.csh # set gt_image = $1 set gt_ld_options = "$2" echo "" echo "############# Linking DSE ###########" echo "" @ buildaux_dse_status = 0 source $gtm_tools/gtm_env.csh set aix_loadmap_option = '' if ( $HOSTOS == "AIX") then set aix_loadmap_option = "-bcalls:$gtm_map/dse.loadmap -bmap:$gtm_map/dse.loadmap -bxref:$gtm_map/dse.loadmap" endif set echo gt_ld $gt_ld_options $aix_loadmap_option ${gt_ld_option_output}$3/dse -L$gtm_obj $gtm_obj/{dse,dse_cmd}.o \ $gt_ld_sysrtns $gt_ld_options_all_exe -ldse -lmumps -lstub \ $gt_ld_extra_libs $gt_ld_syslibs >& $gtm_map/dse.map @ exit_status = $status unset echo if ( $exit_status != 0 || ! -x $3/dse ) then @ buildaux_dse_status++ echo "buildaux-E-linkdse, Failed to link dse (see ${dollar_sign}gtm_map/dse.map)" \ >> $gtm_log/error.${gtm_exe:t}.log else if ( "ia64" == $mach_type && "hpux" == $platform_name ) then if ( "dbg" == $gt_image ) then chatr +dbg enable +as mpas $3/dse else chatr +as mpas $3/dse endif endif exit $buildaux_dse_status fis-gtm-V7.0-005/sr_unix/buildaux_gde.csh0000644000032200000250000000706614342376327017213 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2020 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. # # # ################################################################# # # Note: This script only works when called from buildaux.csh # echo "" echo "############# Linking GDE ###########" echo "" @ buildaux_gde_status = 0 source $gtm_tools/gtm_env.csh set gt_image = $1 pushd $gtm_exe chmod 664 *.m *.o \rm -f *.m *.o # use \rm to avoid rm from asking for confirmation (in case it has been aliased so) cp -p $gtm_pct/*.m . rm -f pinentry.m # avoid problems with concurrent deletion rm -f decomment.m scantypedefs.m # These routines are used only by gengtmdeftypes.csh switch ($gt_image) # potentially all 3 versions could be in $gtm_pct .. we only need one, delete the others case "pro": rm -f GTMDefinedTypesInitBta.m >& /dev/null rm -f GTMDefinedTypesInitDbg.m >& /dev/null mv GTMDefinedTypesInitPro.m GTMDefinedTypesInit.m breaksw case "dbg": rm -f GTMDefinedTypesInitBta.m >& /dev/null rm -f GTMDefinedTypesInitPro.m >& /dev/null mv GTMDefinedTypesInitDbg.m GTMDefinedTypesInit.m breaksw case "bta": rm -f GTMDefinedTypesInitDbg.m >& /dev/null rm -f GTMDefinedTypesInitPro.m >& /dev/null mv GTMDefinedTypesInitBta.m GTMDefinedTypesInit.m breaksw endsw # GDE and the % routines should all be in upper-case. if ( `uname` !~ "CYGWIN*") then ls -1 *.m | awk '! /GTMDefinedTypesInit/ {printf "mv %s %s\n", $1, toupper($1);}' | sed 's/.M$/.m/g' | sh else # unless the mount is "managed", Cygwin is case insensitive but preserving ls -1 *.m | awk '{printf "mv %s %s.tmp;mv %s.tmp %s\n", $1, $1, $1, toupper($1);}' | sed 's/.M$/.m/g' | sh endif # Compile all of the *.m files once so the $gtm_dist directory can remain protected. # Switch to M mode so we are guaranteed the .o files in this directory will be M-mode # (just in case current environment variables are in UTF8 mode) # Not doing so could cause later INVCHSET error if parent environment switches back to M mode. set echo setenv LC_CTYPE C setenv gtm_chset M ./mumps *.m if ($status) then @ buildaux_gde_status++ echo "buildaux-E-compile_M, Failed to compile .m programs in M mode" \ >> $gtm_log/error.${gtm_exe:t}.log endif unset echo source $gtm_tools/set_library_path.csh source $gtm_tools/check_utf8_support.csh if ("TRUE" == "$is_utf8_support") then set icuver = `setenv gtm_dist $PWD ; $gtm_tools/is_icu_symbol_rename.csh` if ("" != "$icuver") setenv gtm_icu_version "$icuver" if (! -e utf8) mkdir utf8 if ( "OS/390" == $HOSTOS ) then setenv gtm_chset_locale $utflocale # LC_CTYPE not picked up right endif set echo setenv LC_CTYPE $utflocale unsetenv LC_ALL setenv gtm_chset UTF-8 # switch to "UTF-8" mode unset echo \rm -f utf8/*.m # use \rm to avoid rm from asking for confirmation (in case it has been aliased so) # get a list of all m files to link setenv mfiles `ls *.m` cd utf8 foreach mfile ($mfiles) ln -s ../$mfile $mfile end set echo ../mumps *.m if ($status) then @ buildaux_gde_status++ echo "buildaux-E-compile_UTF8, Failed to compile .m programs in UTF-8 mode" \ >> $gtm_log/error.${gtm_exe:t}.log endif unset echo cd .. setenv LC_CTYPE C unsetenv gtm_chset # switch back to "M" mode endif popd exit $buildaux_gde_status fis-gtm-V7.0-005/sr_unix/buildaux_gtcm_gnp_server.csh0000644000032200000250000000356314342376327021636 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2015 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. # # # ################################################################# # # Note: This script only works when called from buildaux.csh # set gt_image = $1 set gt_ld_options = "$2" echo "" echo "############# Linking GTCM_GNP_SERVER ###########" echo "" @ buildaux_gtcm_gnp_server_status = 0 source $gtm_tools/gtm_env.csh set aix_loadmap_option = '' if ( $HOSTOS == "AIX") then set aix_loadmap_option = "-bcalls:$gtm_map/gtcm_gnp_server.loadmap" set aix_loadmap_option = "$aix_loadmap_option -bmap:$gtm_map/gtcm_gnp_server.loadmap" set aix_loadmap_option = "$aix_loadmap_option -bxref:$gtm_map/gtcm_gnp_server.loadmap" endif set echo gt_ld $gt_ld_options $aix_loadmap_option ${gt_ld_option_output}$3/gtcm_gnp_server -L$gtm_obj \ $gtm_obj/gtcm_gnp_server.o $gt_ld_sysrtns $gt_ld_options_all_exe \ -lgnpserver -llke -lmumps -lcmisockettcp -lstub \ $gt_ld_extra_libs $gt_ld_syslibs >& $gtm_map/gtcm_gnp_server.map @ exit_status = $status unset echo if ( $exit_status != 0 || ! -x $3/gtcm_gnp_server) then @ buildaux_gtcm_gnp_server_status++ echo "buildaux-E-linkgtcm_gnp_server, Failed to link gtcm_gnp_server" \ "(see ${dollar_sign}gtm_map/gtcm_gnp_server.map)" >> $gtm_log/error.${gtm_exe:t}.log else if ( "ia64" == $mach_type && "hpux" == $platform_name ) then if ( "dbg" == $gt_image ) then chatr +dbg enable +as mpas $3/gtcm_gnp_server else chatr +as mpas $3/gtcm_gnp_server endif endif exit $buildaux_gtcm_gnp_server_status fis-gtm-V7.0-005/sr_unix/buildaux_gtcm_pkdisp.csh0000644000032200000250000000331714342376327020753 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2015 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. # # # ################################################################# # # Note: This script only works when called from buildaux.csh # set gt_image = $1 set gt_ld_options = "$2" echo "" echo "############# Linking GTCM_PKDISP ###########" echo "" @ buildaux_gtcm_pkdisp_status = 0 source $gtm_tools/gtm_env.csh set aix_loadmap_option = '' if ( $HOSTOS == "AIX") then set aix_loadmap_option = "-bcalls:$gtm_map/gtcm_pkdisp.loadmap" set aix_loadmap_option = "$aix_loadmap_option -bmap:$gtm_map/gtcm_pkdisp.loadmap" set aix_loadmap_option = "$aix_loadmap_option -bxref:$gtm_map/gtcm_pkdisp.loadmap" endif set echo gt_ld $gt_ld_options $aix_loadmap_option ${gt_ld_option_output}$3/gtcm_pkdisp -L$gtm_obj $gtm_obj/gtcm_pkdisp.o \ $gt_ld_sysrtns -lgtcm -lmumps -lstub $gt_ld_extra_libs $gt_ld_syslibs \ >& $gtm_map/gtcm_pkdisp.map @ exit_status = $status unset echo if ( $exit_status != 0 || ! -x $3/gtcm_pkdisp) then @ buildaux_gtcm_pkdisp_status++ echo "buildaux-E-linkgtcm_pkdisp, Failed to link gtcm_pkdisp (see ${dollar_sign}gtm_map/gtcm_pkdisp.map)" \ >> $gtm_log/error.${gtm_exe:t}.log else if ( "ia64" == $mach_type && "hpux" == $platform_name ) then if ( "dbg" == $gt_image ) then chatr +dbg enable $3/gtcm_pkdisp endif endif exit $buildaux_gtcm_pkdisp_status fis-gtm-V7.0-005/sr_unix/buildaux_gtcm_play.csh0000644000032200000250000000341314342376327020423 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2015 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. # # # ################################################################# # # Note: This script only works when called from buildaux.csh # set gt_image = $1 set gt_ld_options = "$2" echo "" echo "############# Linking GTCM_PLAY ###########" echo "" @ buildaux_gtcm_play_status = 0 source $gtm_tools/gtm_env.csh set aix_loadmap_option = '' if ( $HOSTOS == "AIX") then set aix_loadmap_option = "-bcalls:$gtm_map/gtcm_play.loadmap" set aix_loadmap_option = "$aix_loadmap_option -bmap:$gtm_map/gtcm_play.loadmap" set aix_loadmap_option = "$aix_loadmap_option -bxref:$gtm_map/gtcm_play.loadmap" endif set echo gt_ld $gt_ld_options $aix_loadmap_option ${gt_ld_option_output}$3/gtcm_play -L$gtm_obj \ $gtm_obj/gtcm_play.o $gtm_obj/omi_sx_play.o $gt_ld_sysrtns $gt_ld_options_all_exe \ -lgtcm -lmumps -lstub $gt_ld_extra_libs $gt_ld_syslibs >& $gtm_map/gtcm_play.map @ exit_status = $status unset echo if ( $exit_status != 0 || ! -x $3/gtcm_play) then @ buildaux_gtcm_play_status++ echo "buildaux-E-linkgtcm_play, Failed to link gtcm_play (see ${dollar_sign}gtm_map/gtcm_play.map)" \ >> $gtm_log/error.${gtm_exe:t}.log else if ( "ia64" == $mach_type && "hpux" == $platform_name ) then if ( "dbg" == $gt_image ) then chatr +dbg enable +as mpas $3/gtcm_play else chatr +as mpas $3/gtcm_play endif endif exit $buildaux_gtcm_play_status fis-gtm-V7.0-005/sr_unix/buildaux_gtcm_server.csh0000644000032200000250000000345314342376327020770 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2015 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. # # # ################################################################# # # Note: This script only works when called from buildaux.csh # set gt_image = $1 set gt_ld_options = "$2" echo "" echo "############# Linking GTCM_SERVER ###########" echo "" @ buildaux_gtcm_server_status = 0 source $gtm_tools/gtm_env.csh set aix_loadmap_option = '' if ( $HOSTOS == "AIX") then set aix_loadmap_option = "-bcalls:$gtm_map/gtcm_server.loadmap" set aix_loadmap_option = "$aix_loadmap_option -bmap:$gtm_map/gtcm_server.loadmap" set aix_loadmap_option = "$aix_loadmap_option -bxref:$gtm_map/gtcm_server.loadmap" endif set echo gt_ld $gt_ld_options $aix_loadmap_option ${gt_ld_option_output}$3/gtcm_server -L$gtm_obj \ $gtm_obj/gtcm_main.o $gtm_obj/omi_srvc_xct.o $gt_ld_sysrtns $gt_ld_options_all_exe \ -lgtcm -lmumps -lstub $gt_ld_extra_libs $gt_ld_syslibs >& $gtm_map/gtcm_server.map @ exit_status = $status unset echo if ( $exit_status != 0 || ! -x $3/gtcm_server ) then @ buildaux_gtcm_server_status++ echo "buildaux-E-linkgtcm_server, Failed to link gtcm_server (see ${dollar_sign}gtm_map/gtcm_server.map)" \ >> $gtm_log/error.${gtm_exe:t}.log else if ( "ia64" == $mach_type && "hpux" == $platform_name ) then if ( "dbg" == $gt_image ) then chatr +dbg enable +as mpas $3/gtcm_server else chatr +as mpas $3/gtcm_server endif endif exit $buildaux_gtcm_server_status fis-gtm-V7.0-005/sr_unix/buildaux_gtcm_shmclean.csh0000644000032200000250000000335514342376327021255 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2015 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. # # # ################################################################# # # Note: This script only works when called from buildaux.csh # set gt_image = $1 set gt_ld_options = "$2" echo "" echo "############# Linking GTCM_SHMCLEAN ###########" echo "" @ buildaux_gtcm_shmclean_status = 0 source $gtm_tools/gtm_env.csh set aix_loadmap_option = '' if ( $HOSTOS == "AIX") then set aix_loadmap_option = "-bcalls:$gtm_map/gtcm_shmclean.loadmap" set aix_loadmap_option = "$aix_loadmap_option -bmap:$gtm_map/gtcm_shmclean.loadmap" set aix_loadmap_option = "$aix_loadmap_option -bxref:$gtm_map/gtcm_shmclean.loadmap" endif set echo gt_ld $gt_ld_options $aix_loadmap_option ${gt_ld_option_output}$3/gtcm_shmclean -L$gtm_obj $gtm_obj/gtcm_shmclean.o \ $gt_ld_sysrtns -lgtcm -lmumps -lstub $gt_ld_extra_libs $gt_ld_syslibs \ >& $gtm_map/gtcm_shmclean.map @ exit_status = $status unset echo if ( $exit_status != 0 || ! -x $3/gtcm_shmclean) then @ buildaux_gtcm_shmclean_status++ echo "buildaux-E-linkgtcm_shmclean, Failed to link gtcm_shmclean (see ${dollar_sign}gtm_map/gtcm_shmclean.map)" \ >> $gtm_log/error.${gtm_exe:t}.log else if ( "ia64" == $mach_type && "hpux" == $platform_name ) then if ( "dbg" == $gt_image ) then chatr +dbg enable $3/gtcm_shmclean endif endif exit $buildaux_gtcm_shmclean_status fis-gtm-V7.0-005/sr_unix/buildaux_gtmcrypt.csh0000755000032200000250000001211014342376327020312 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2021 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. # # # ################################################################# # # Note: This script only works when called from buildaux.csh # # Arguments: # $1 - image type (b[ta], d[bg], or p[ro]) echo "" echo "############# Linking GTMCRYPT ###########" echo "" @ buildaux_gtmcrypt_status = 0 source $gtm_tools/gtm_env.csh set gt_image = "$1" set supported_list = `$gtm_tools/check_encrypt_support.sh mail` if ("ERROR" == "$supported_list") then # This is an error condition. Run check_encrypt_support in debug mode to have some debugging information. echo "buildaux-E-libgtmcrypt, $gtm_tools/check_encrypt_support.sh returned ERROR. Running it in debug mode before exiting" /bin/sh -x $gtm_tools/check_encrypt_support.sh exit 1 else if ("FALSE" == "$supported_list") then # This platform does not support encryption. echo "buildaux-I-libgtmcrypt, encryption is not supported on this platform" exit endif # Remove all lingering gpg-agent processes because they may have cached passphrases. set gpg_agent_pids = `ps -fu $USER | awk '/gpg-agent --homedir \/tmp\/gnupgdir\/'$USER' .*--daemon/ {print $2}'` foreach gpg_agent_pid ($gpg_agent_pids) kill $gpg_agent_pid >&! /dev/null end set plugin_build_type="" set plugin_build_scan="FALSE" switch ($gt_image) case "[bB]*": set plugin_build_type="PRO" breaksw case "[pP]*": set plugin_build_type="PRO" breaksw default: set plugin_build_type="DEBUG" if ($?scan_image) set plugin_build_scan="TRUE" breaksw endsw # First copy all the necessary source and script files to $gtm_dist/plugin/gtmcrypt set helpers = "encrypt_sign_db_key,gen_keypair,gen_sym_hash,gen_sym_key,import_and_sign_key" set helpers = "$helpers,pinentry-gtm,show_install_config" set srcfiles = "gtmcrypt_dbk_ref.c gtmcrypt_pk_ref.c gtmcrypt_sym_ref.c gtmcrypt_ref.c gtm_tls_impl.c maskpass.c" set srcfiles = "$srcfiles gtmcrypt_util.c" set incfiles = "gtmcrypt_interface.h gtmcrypt_dbk_ref.h gtmcrypt_sym_ref.h gtmcrypt_pk_ref.h gtmcrypt_ref.h" set incfiles = "$incfiles gtmcrypt_util.h gtm_tls_impl.h gtm_tls_interface.h" set gtm_dist_plugin = $gtm_dist/plugin rm -rf $gtm_dist_plugin mkdir -p $gtm_dist_plugin/gtmcrypt set srcfile_list = ($srcfiles) eval cp -pf '${srcfile_list:gs||'$gtm_src'/|} $gtm_dist_plugin/gtmcrypt' set incfile_list = ($incfiles) eval cp -pf '${incfile_list:gs||'$gtm_inc'/|} $gtm_dist_plugin/gtmcrypt' cp -pf $gtm_tools/{$helpers}.sh $gtm_dist_plugin/gtmcrypt cp -pf $gtm_pct/pinentry.m $gtm_dist_plugin/gtmcrypt rm -f $gtm_dist/{PINENTRY,pinentry}.[om] cp -pf $gtm_tools/Makefile.mk $gtm_dist_plugin/gtmcrypt/Makefile chmod +x $gtm_dist_plugin/gtmcrypt/*.sh # pushd $gtm_dist_plugin/gtmcrypt set make = "make" if ($gtm_verno =~ V[4-8]*) then # For production builds don't do any randomizations. set algorithm = "AES256CFB" if ($HOSTOS == "AIX") then set encryption_lib = "openssl" else set encryption_lib = "gcrypt" endif else # Randomly choose one configuration based on third-party library and algorithm. set rand = `echo $#supported_list | awk '{srand() ; print 1+int(rand()*$1)}'` set encryption_lib = $supported_list[$rand] if ("gcrypt" == "$encryption_lib") then # Force AES as long as the plugin is linked against libgcrypt set algorithm = "AES256CFB" else # OpenSSL, V9* build. AES256CFB is the only one we we officially support. set algorithm = "AES256CFB" endif endif source $gtm_tools/set_library_path.csh source $gtm_tools/check_utf8_support.csh if ("TRUE" == "$is_utf8_support") then set icuver = `$gtm_tools/is_icu_symbol_rename.csh` if ("" != "$icuver") setenv gtm_icu_version "$icuver" if (! -e $gtm_dist/utf8) mkdir $gtm_dist/utf8 endif # Build and install all encryption libraries and executables. env LC_ALL=$utflocale $make install algo=$algorithm image=$plugin_build_type thirdparty=$encryption_lib scan=$plugin_build_scan if ($status) then @ buildaux_gtmcrypt_status++ echo "buildaux-E-libgtmcrypt, failed to install libgtmcrypt and/or helper scripts" \ >> $gtm_log/error.${gtm_exe:t}.log endif # Remove temporary files. $make clean if ($status) then @ buildaux_gtmcrypt_status++ echo "buildaux-E-libgtmcrypt, failed to clean libgtmcrypt and/or helper scripts" \ >> $gtm_log/error.${gtm_exe:t}.log endif # Remove pinentry routine for GTM-8668 rm -f $gtm_dist_plugin/gtmcrypt/pinentry.m # For now we expect the below plugins to be built. set expected = (libgtmcrypt_gcrypt_AES256CFB.so libgtmcrypt_openssl_AES256CFB.so libgtmcryptutil.so libgtmtls.so) foreach so ($expected) if (! -f $gtm_dist_plugin/$so) then @ buildaux_gtmcrypt_status++ echo "buildaux-E-libgtmcrypt, $so expected but not found" >> $gtm_log/error.${gtm_exe:t}.log endif end popd >&! /dev/null exit $buildaux_gtmcrypt_status fis-gtm-V7.0-005/sr_unix/buildaux_gtmsecshr.csh0000644000032200000250000000532314342376327020445 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2022 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. # # # ################################################################# # # Note: This script only works when called from buildaux.csh # set gt_image = $1 set gt_ld_options = "$2" echo "" echo "############# Linking GTMSECSHR ###########" echo "" @ buildaux_gtmsecshr_status = 0 source $gtm_tools/gtm_env.csh set aix_loadmap_option = '' $gtm_com/IGS $3/gtmsecshr "STOP" # stop any active gtmsecshr processes $gtm_com/IGS $3/gtmsecshr "RMDIR" # remove root-owned gtmsecshr, gtmsecshrdir, gtmsecshrdir/gtmsecshr files/dirs if ( -d $3/utf8/gtmsecshrdir ) then # In case gtmsecshr in utf8/ dir is not a softlink (possible if this is an installation from a GT.M kit) $gtm_com/IGS $3/utf8/gtmsecshr "STOP" # stop any active gtmsecshr processes $gtm_com/IGS $3/utf8/gtmsecshr "RMDIR" # remove root-owned gtmsecshr* files/dirs endif foreach file (gtmsecshr gtmsecshr_wrapper) if ( $HOSTOS == "AIX") then set aix_loadmap_option = "-bcalls:$gtm_map/$file.loadmap" set aix_loadmap_option = "$aix_loadmap_option -bmap:$gtm_map/$file.loadmap" set aix_loadmap_option = "$aix_loadmap_option -bxref:$gtm_map/$file.loadmap" endif set echo gt_ld $gt_ld_options $aix_loadmap_option ${gt_ld_option_output}$3/${file} -L$gtm_obj $gtm_obj/${file}.o \ $gt_ld_sysrtns $gt_ld_extra_libs -lmumps -lstub $gt_ld_syslibs >& $gtm_map/${file}.map @ exit_status = $status unset echo if ( $exit_status != 0 || ! -x $3/${file} ) then @ buildaux_gtmsecshr_status++ echo "buildaux-E-link${file}, Failed to link ${file} (see ${dollar_sign}gtm_map/${file}.map)" \ >> $gtm_log/error.${gtm_exe:t}.log else if ( "ia64" == $mach_type && "hpux" == $platform_name ) then if ( "dbg" == $gt_image ) then chatr +dbg enable +as mpas $3/${file} else chatr +as mpas $3/${file} endif endif end mkdir ../gtmsecshrdir mv ../gtmsecshr ../gtmsecshrdir # move actual gtmsecshr into subdirectory mv ../gtmsecshr_wrapper ../gtmsecshr # rename wrapper to be actual gtmsecshr # add symbolic link to gtmsecshrdir in utf8 if utf8 exists if ( -d $3/utf8 ) then cd $3/utf8 ln -s ../gtmsecshrdir . ln -s ../gtmsecshr . cd - endif $gtm_com/IGS $3/gtmsecshr "CHOWN" # make gtmsecshr, gtmsecshrdir, gtmsecshrdir/gtmsecshr files/dirs root owned if ($status) @ buildaux_gtmsecshr_status++ exit $buildaux_gtmsecshr_status fis-gtm-V7.0-005/sr_unix/buildaux_lke.csh0000644000032200000250000000315014342376327017215 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2015 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. # # # ################################################################# # # Note: This script only works when called from buildaux.csh # set gt_image = $1 set gt_ld_options = "$2" echo "" echo "############# Linking LKE ###########" echo "" @ buildaux_lke_status = 0 source $gtm_tools/gtm_env.csh set aix_loadmap_option = '' if ( $HOSTOS == "AIX") then set aix_loadmap_option = "-bcalls:$gtm_map/lke.loadmap -bmap:$gtm_map/lke.loadmap -bxref:$gtm_map/lke.loadmap" endif set echo gt_ld $gt_ld_options $aix_loadmap_option ${gt_ld_option_output}$3/lke -L$gtm_obj $gtm_obj/{lke,lke_cmd}.o \ $gt_ld_sysrtns $gt_ld_options_all_exe -llke -lmumps -lgnpclient -lmumps -lgnpclient -lcmisockettcp \ $gt_ld_extra_libs $gt_ld_syslibs >& $gtm_map/lke.map @ exit_status = $status unset echo if ( $exit_status != 0 || ! -x $3/lke ) then @ buildaux_lke_status++ echo "buildaux-E-linklke, Failed to link lke (see ${dollar_sign}gtm_map/lke.map)" \ >> $gtm_log/error.${gtm_exe:t}.log else if ( "ia64" == $mach_type && "hpux" == $platform_name ) then if ( "dbg" == $gt_image ) then chatr +dbg enable +as mpas $3/lke else chatr +as mpas $3/lke endif endif exit $buildaux_lke_status fis-gtm-V7.0-005/sr_unix/buildaux_mupip.csh0000644000032200000250000000316414342376327017601 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2015 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. # # # ################################################################# # # Note: This script only works when called from buildaux.csh # set gt_image = $1 set gt_ld_options = "$2" echo "" echo "############# Linking MUPIP ###########" echo "" @ buildaux_mupip_status = 0 source $gtm_tools/gtm_env.csh set aix_loadmap_option = '' if ( $HOSTOS == "AIX") then set aix_loadmap_option = "-bcalls:$gtm_map/mupip.loadmap -bmap:$gtm_map/mupip.loadmap -bxref:$gtm_map/mupip.loadmap" endif set echo gt_ld $gt_ld_options $aix_loadmap_option ${gt_ld_option_output}$3/mupip -L$gtm_obj $gtm_obj/{mupip,mupip_cmd}.o \ $gt_ld_sysrtns $gt_ld_options_all_exe -lmupip -lmumps -lstub \ $gt_ld_extra_libs $gt_ld_aio_syslib $gt_ld_syslibs >& $gtm_map/mupip.map @ exit_status = $status unset echo if ( $exit_status != 0 || ! -x $3/mupip ) then @ buildaux_mupip_status++ echo "buildaux-E-linkmupip, Failed to link mupip (see ${dollar_sign}gtm_map/mupip.map)" \ >> $gtm_log/error.${gtm_exe:t}.log else if ( "ia64" == $mach_type && "hpux" == $platform_name ) then if ( "dbg" == $gt_image ) then chatr +dbg enable +as mpas $3/mupip else chatr +as mpas $3/mupip endif endif exit $buildaux_mupip_status fis-gtm-V7.0-005/sr_unix/buildbdp.csh0000755000032200000250000000246514342376327016345 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2015 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. # # # ################################################################# # ################################################################################# # # buildbdp.csh - Build (link) b (bta), d (dbg), or p (pro) version of GT.M. # # Arguments: # $1 - Version number of release. # $2 - Image type (b[ta], d[bg], or p[ro]). # $3 - Pathname of directory for executables. # ################################################################################# set buildbdp_status = 0 if ( "$1" == "" ) then set buildbdp_status = -1 endif if ( "$2" == "" ) then set buildbdp_status = -1 endif if ( $buildbdp_status != 0 ) then echo "buildbdp-I-needp1orp2, Usage: $shell $gtm_tools/buildbdp.csh " exit $buildbdp_status endif $gtm_tools/buildaux.csh $1 $2 $3 "shr" if ($status != 0) @ buildbdp_status = $status exit $buildbdp_status fis-gtm-V7.0-005/sr_unix/buildbta.csh0000755000032200000250000000171414342376327016342 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2020 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. # # # ################################################################# # ################################################################## # # buildbta.csh - Build bta images. # # Argument: # $1 - Version number or code (i.e., b, d, or p). # ################################################################## if ( $1 == "" ) then echo "buildbta-E-needp1, Usage: $shell buildbta.csh " exit -1 endif set setactive_parms = ( $1 b ) ; source $gtm_tools/setactive.csh $gtm_tools/buildbdp.csh $1 bta $gtm_ver/bta exit $status fis-gtm-V7.0-005/sr_unix/builddbg.csh0000755000032200000250000000171414342376327016330 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2020 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. # # # ################################################################# # ################################################################## # # builddbg.csh - Build dbg images. # # Argument: # $1 - Version number or code (i.e., b, d, or p). # ################################################################## if ( $1 == "" ) then echo "builddbg-E-needp1, Usage: $shell builddbg.csh " exit -1 endif set setactive_parms = ( $1 d ) ; source $gtm_tools/setactive.csh $gtm_tools/buildbdp.csh $1 dbg $gtm_ver/dbg exit $status fis-gtm-V7.0-005/sr_unix/buildpro.csh0000755000032200000250000000313514342376327016373 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2020 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. # # # ################################################################# # ################################################################## # # buildpro.csh - Build pro images. # # Argument: # $1 - Version number or code (i.e., b, d, or p). # ################################################################## if ( $1 == "" ) then echo "buildpro-E-needp1, Usage: $shell buildpro.csh " exit -1 endif set setactive_parms = ( $1 p ) ; source $gtm_tools/setactive.csh $gtm_tools/buildbdp.csh $1 pro $gtm_ver/pro set buildstatus=$status # Extract the debug symbols from each executable if ( "$HOSTOS" == "Linux" ) then set outfile = "strip_debug_symbols.out" rm -f $outfile echo "Stripping debug symbols and generating .debug files. Leaving log at $PWD/$outfile" foreach file (`find ../ -executable -type f`) if ($file:e =~ {sh,csh}) continue echo "Stripping $file" >>&! $outfile objcopy --only-keep-debug $file $file.debug >>&! $outfile strip -g $file >>&! $outfile objcopy --add-gnu-debuglink=$file.debug $file >>&! $outfile end endif # strip removes the restricted permissions of gtmsecshr. Fix it $gtm_com/IGS $gtm_dist/gtmsecshr CHOWN exit $buildstatus fis-gtm-V7.0-005/sr_unix/buildshr.csh0000755000032200000250000001202514342376327016365 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2019 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. # # # ################################################################# # ########################################################### # # buildshr.csh - Build GT.M mumps executable. # # Arguments: # $1 - version number or code # $2 - image type (b[ta], d[bg], or p[ro]) # $3 - target directory # ########################################################### echo "" echo "############# Linking MUMPS ###########" echo "" set buildshr_status = 0 source $gtm_tools/gtm_env.csh set dollar_sign = \$ set mach_type = `uname -m` set platform_name = `uname | sed 's/-//g' | tr '[A-Z]' '[a-z]'` if ( $1 == "" ) then set buildshr_status = `expr $buildshr_status + 1` endif if ( $2 == "" ) then set buildshr_status = `expr $buildshr_status + 1` endif if ( $3 == "" ) then set buildshr_status = `expr $buildshr_status + 1` endif switch ($2) case "[bB]*": set gt_ld_options = "$gt_ld_options_bta" set gt_image = "bta" breaksw case "[dD]*": set gt_ld_options = "$gt_ld_options_dbg" set gt_image = "dbg" breaksw case "[pP]*": set gt_ld_options = "$gt_ld_options_pro" set gt_image = "pro" breaksw default: set buildshr_status = `expr $buildshr_status + 1` breaksw endsw set setactive_parms = ( $1 $2 ) ; source $gtm_tools/setactive.csh if ( $buildshr_status != 0 ) then echo "buildshr-I-usage, Usage: buildshr.csh " exit $buildshr_status endif set gt_ld_linklib_options = "-L$gtm_obj $gtm_obj/gtm_main.o -lmumps -lgnpclient -lcmisockettcp" set nolibgtmshr = "no" # by default build libgtmshr if ($gt_image == "bta") then set nolibgtmshr = "yes" # if bta build, build a static mumps executable endif if ("OS/390" == $HOSTOS) then set exp = "x" else set exp = "export" endif $shell $gtm_tools/genexport.csh $gtm_tools/gtmshr_symbols.exp gtmshr_symbols.$exp # The below is used to generate an export file that is specific to executables. Typically used to export # some symbols from utility progs like mupip, dse, lke etc $shell $gtm_tools/genexport.csh $gtm_tools/gtmexe_symbols.exp gtmexe_symbols.$exp if ($nolibgtmshr == "no") then # do not build libgtmshr.so for bta builds # Building libgtmshr.so shared library set aix_loadmap_option = '' set aix_binitfini_option = '' if ( $HOSTOS == "AIX") then set aix_loadmap_option = \ "-bcalls:$gtm_map/libgtmshr.loadmap -bmap:$gtm_map/libgtmshr.loadmap -bxref:$gtm_map/libgtmshr.loadmap" # Delete old gtmshr since AIX linker fails to overwrite an already loaded shared library. rm -f $3/libgtmshr$gt_ld_shl_suffix # Define gtmci_cleanup as a termination routine for libgtmshr on AIX. set aix_binitfini_option = "-binitfini::gtmci_cleanup" endif set echo gt_ld $gt_ld_options $gt_ld_shl_options $aix_binitfini_option $gt_ld_ci_options $aix_loadmap_option \ ${gt_ld_option_output}$3/libgtmshr$gt_ld_shl_suffix \ ${gt_ld_linklib_options} $gt_ld_extra_libs $gt_ld_syslibs >& $gtm_map/libgtmshr.map @ exit_status = $status unset echo if ( $exit_status != 0 ) then @ buildshr_status++ echo "buildshr-E-linkgtmshr, Failed to link gtmshr (see ${dollar_sign}gtm_map/libgtmshr.map)" \ >> $gtm_log/error.`basename $gtm_exe`.log else if ( ($HOSTOS == "Linux") && (-e /usr/bin/chcon) ) then # Successful build -- for Linux builds use chcon to enable usage of executable (later SELinux platforms) # Note that this command only works on filesystems that support context info so because it may fail, # (and if it does, it is irrelevent) we merrily ignore the output. It either works or it doesn't. chcon -t texrel_shlib_t $3/libgtmshr$gt_ld_shl_suffix >& /dev/null endif if ($HOSTOS == "OS/390") then cp $gtm_obj/gtmshr_symbols.$exp $3/ endif set gt_ld_linklib_options = "-L$gtm_obj" # do not link in mumps whatever is already linked in libgtmshr.so endif # Building mumps executable set aix_loadmap_option = '' if ( $HOSTOS == "AIX") then set aix_loadmap_option = "-bcalls:$gtm_map/mumps.loadmap -bmap:$gtm_map/mumps.loadmap -bxref:$gtm_map/mumps.loadmap" endif set echo gt_ld $gt_ld_options $aix_loadmap_option ${gt_ld_option_output}$3/mumps ${gt_ld_linklib_options} $gtm_obj/gtm.o \ $gt_ld_extra_libs $gt_ld_sysrtns $gt_ld_syslibs >& $gtm_map/mumps.map @ exit_status = $status unset echo if ( $exit_status != 0 || ! -x $3/mumps ) then @ buildshr_status++ echo "buildshr-E-linkmumps, Failed to link mumps (see ${dollar_sign}gtm_map/mumps.map)" \ >> $gtm_log/error.`basename $gtm_exe`.log else if ( "ia64" == $mach_type && "hpux" == $platform_name ) then if ( "dbg" == $gt_image ) then chatr +dbg enable +as mpas $3/mumps else chatr +as mpas $3/mumps endif endif exit $buildshr_status fis-gtm-V7.0-005/sr_unix/buildwarn.awk0000644000032200000250000000225614342376327016547 0ustar librarygtc################################################################# # # # Copyright 2007, 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. # # # ################################################################# BEGIN { state = 0; inwarning = 0; pattern = sprintf("%s/.*", gtm_src); } ($0 == "End of C Compilation") { state = 2;} ($0 == "End of Assembly") { state = 4; } state == 1 { if (NF > 0) { if ((NF == 1) && ($1 ~ pattern)) { prev = $0; inwarning = 0; } else { if (!inwarning) { print prev; inwarning = 1; } print $0; } } } ($0 == "Start of C Compilation") { state = 1; prev = ""; inwarning = 0; } ($0 == "Start of Assembly") { state = 3; inwarning = 0; } state == 3 { if (!inwarning) inwarning = 1; else print $0; } END { if ((state != 2) && (state != 4)) printf "BUILDWARN_AWK-E-ERROR : Did not see Start or End of C compilation/Assembly: state = %d\n", state; } fis-gtm-V7.0-005/sr_unix/buildwarn.csh0000755000032200000250000000337214342376327016545 0ustar librarygtc#!/usr/local/bin/tcsh # ################################################################# # # # Copyright (c) 2004-2019 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. # # # ################################################################# # ################################################################## # # buildwarn.csh - Filter out all the warnings from the build logs # # Argument: # $1 - version number # $2 - image type (b[ta], d[bg], or p[ro]) # ################################################################## set buildwarn_status = 0 if ( $1 == "" ) then @ buildwarn_status++; endif if ( $2 == "" ) then @ buildwarn_status++; endif switch ($2) case "[bB]*": set image = "bta" breaksw case "[dD]*": set image = "dbg" breaksw case "[pP]*": set image = "pro" breaksw default: set image = "" @ buildwarn_status++; breaksw endsw if (! -e $gtm_root/$1/$image) then @ buildwarn_status++; endif if ( $buildwarn_status != 0 ) then echo "buildwarn-E-needp12, Usage: $gtm_tools/buildwarn.csh " exit $buildwarn_status endif set setactive_parms = ( $1 $2 ) ; source $gtm_tools/setactive.csh pushd $gtm_ver/log if (! -e comlist.$image.log) then echo "buildwarn-E-lognotexist, $gtm_ver/log/comlist.$image.log does not exist. Exiting..." exit -1 endif # Generate warning file set buildlog = comlist.$image.log set warnlog = warn.$image.log awk -v gtm_src="$gtm_src" -f $gtm_tools/buildwarn.awk $buildlog > $warnlog exit $status fis-gtm-V7.0-005/sr_unix/callg.c0000644000032200000250000000552214342376327015301 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2015 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 #include "lv_val.h" /* needed for "callg.h" */ #include "callg.h" #define VAR_ARGS4(ar) ar[0], ar[1], ar[2], ar[3] #define VAR_ARGS8(ar) VAR_ARGS4(ar), ar[4], ar[5], ar[6], ar[7] #define VAR_ARGS12(ar) VAR_ARGS8(ar), ar[8], ar[9], ar[10], ar[11] #define VAR_ARGS16(ar) VAR_ARGS12(ar), ar[12], ar[13], ar[14], ar[15] #define VAR_ARGS20(ar) VAR_ARGS16(ar), ar[16], ar[17], ar[18], ar[19] #define VAR_ARGS24(ar) VAR_ARGS20(ar), ar[20], ar[21], ar[22], ar[23] #define VAR_ARGS28(ar) VAR_ARGS24(ar), ar[24], ar[25], ar[26], ar[27] #define VAR_ARGS32(ar) VAR_ARGS28(ar), ar[28], ar[29], ar[30], ar[31] #define VAR_ARGS36(ar) VAR_ARGS32(ar), ar[32], ar[33], ar[34], ar[35] /* Note selection of doing 4 parms per case block is based on the fact that Itanium can do at most 4 parm loads at one time due to instruction bundle restrictions and the fact that most calls made are 4 or fewer parms. NOTE: Although this module can be used on other platforms it is efficient **ONLY** on Itanium in its present form and is not suggested for use on other platforms. */ INTPTR_T callg(callgfnptr fnptr, gparam_list *paramlist) { assert(36 == (SIZEOF(paramlist->arg) / SIZEOF(void *))); switch(paramlist->n) { case 0: return (fnptr)(0); case 1: case 2: case 3: case 4: return (fnptr)(paramlist->n, VAR_ARGS4(paramlist->arg)); case 5: case 6: case 7: case 8: return (fnptr)(paramlist->n, VAR_ARGS8(paramlist->arg)); case 9: case 10: case 11: case 12: return (fnptr)(paramlist->n, VAR_ARGS12(paramlist->arg)); case 13: case 14: case 15: case 16: return (fnptr)(paramlist->n, VAR_ARGS16(paramlist->arg)); case 17: case 18: case 19: case 20: return (fnptr)(paramlist->n, VAR_ARGS20(paramlist->arg)); case 21: case 22: case 23: case 24: return (fnptr)(paramlist->n, VAR_ARGS24(paramlist->arg)); case 25: case 26: case 27: case 28: return (fnptr)(paramlist->n, VAR_ARGS28(paramlist->arg)); case 29: case 30: case 31: case 32: return (fnptr)(paramlist->n, VAR_ARGS32(paramlist->arg)); case 33: case 34: case 35: case 36: assert(fnptr == (callgfnptr)push_parm); /* Only push_parm is aware of this extra space */ return (fnptr)(paramlist->n, VAR_ARGS36(paramlist->arg)); default: assertpro(paramlist->n <= 36); } return 0; } fis-gtm-V7.0-005/sr_unix/callintogtmxfer.c0000644000032200000250000000420014342376327017411 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_stdlib.h" #include #include "callintogtmxfer.h" #include "gt_timer.h" #include "have_crit.h" typedef int (*int_fptr)(); /* Note the malloc and free calls below are turned into gtm_malloc/gtm_free respectively by the #defines for those names * in mdefsp.h. */ GBLDEF int (*callintogtm_vectortable[])()= { (int_fptr)hiber_start, (int_fptr)hiber_start_wait_any, (int_fptr)start_timer, (int_fptr)cancel_timer, (int_fptr)malloc, (int_fptr)free, (int_fptr)-1L }; #ifdef GTM64 #define MAX_ADDR_SIZE 64 #else #define MAX_ADDR_SIZE 32 #endif #define MAX_ADDR_ENV_SIZE 64 #define GTM_CALLIN_START_ENV "GTM_CALLIN_START=" GBLDEF unsigned char gtmvectortable_address[MAX_ADDR_SIZE]; GBLDEF unsigned char gtmvectortable_env[MAX_ADDR_ENV_SIZE]; error_def(ERR_SYSCALL); void init_callin_functable(void) { unsigned char *env_top, *address_top; uint4 address_len; int save_errno, status; address_top = GTM64_ONLY(i2ascl)NON_GTM64_ONLY(i2asc)(gtmvectortable_address, (UINTPTR_T)(&callintogtm_vectortable[0])); *address_top = '\0'; address_len = (uint4)(address_top - >mvectortable_address[0]); env_top = >mvectortable_env[0]; MEMCPY_LIT(env_top, GTM_CALLIN_START_ENV); memcpy((env_top + strlen(GTM_CALLIN_START_ENV)), gtmvectortable_address, address_len); *(env_top + strlen(GTM_CALLIN_START_ENV) + address_len) = '\0'; PUTENV(status, (char *)gtmvectortable_env); if (status) { save_errno = errno; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("putenv"), CALLFROM, save_errno); } } fis-gtm-V7.0-005/sr_unix/callintogtmxfer.h0000755000032200000250000000120214342376327017420 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef INIT_CALLIN_FUNCTABLE_H_INCLUDED #define INIT_CALLIN_FUNCTABLE_H_INCLUDED void init_callin_functable(void); #endif fis-gtm-V7.0-005/sr_unix/ccp_cluster_lock_wake.c0000755000032200000250000000126414342376327020546 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "ccp_cluster_lock_wake.h" void ccp_cluster_lock_wake(gd_region *reg) {assert (FALSE);} fis-gtm-V7.0-005/sr_unix/ccp_fid_msg.c0000755000032200000250000000112214342376327016447 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2004 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" void ccp_fid_msg(void); /* prototype to avoid warning */ void ccp_fid_msg(void) { assert (FALSE); } fis-gtm-V7.0-005/sr_unix/ccp_sendmsg.c0000755000032200000250000000111614342376327016502 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2004 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" void ccp_sendmsg(void); /* prototype to avoid warning */ void ccp_sendmsg() { assert (FALSE); } fis-gtm-V7.0-005/sr_unix/ccp_userwait.c0000755000032200000250000000112414342376327016704 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2004 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" void ccp_userwait(void); /* prototype to avoid warning */ void ccp_userwait(void) { assert (FALSE); } fis-gtm-V7.0-005/sr_unix/ce_init.c0000755000032200000250000000172214342376327015632 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "comp_esc.h" GBLDEF bool ce_init_done = FALSE; GBLDEF struct ce_sentinel_desc *ce_def_list; int ce_init (void) { int4 (*compiler_escape_init)(); mstr filename_logical, filename_translation; char buffer[256]; uint4 status; status = 1; if (!ce_init_done) { ce_def_list = NULL; /* Check for existence of logical name; if present, invoke corresponding entry point. */ /* No Unix code yet -- this is just a stub to initialize ce_def_list */ ce_init_done = TRUE; } return status; } fis-gtm-V7.0-005/sr_unix/ce_substitute.c0000755000032200000250000000177114342376327017106 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "compiler.h" #include "comp_esc.h" GBLREF int source_column; GBLREF struct ce_sentinel_desc *ce_def_list; void ce_substitute(struct ce_sentinel_desc *shp, int4 source_col, int4 *skip_ct) { unsigned char *cp, sub_buffer[MAX_SRCLINE]; short source_length, tail_length; int4 lcl_src_col, skip_count, status; bool run_or_compile; /* Invoke user-supplied routine in order to obtain substitution text at sentinel string. */ /* Just a stub for Unix. */ return; } fis-gtm-V7.0-005/sr_unix/cenable.c0000644000032200000250000000261014342376327015603 0ustar librarygtc/**************************************************************** * * * Copyright 2009 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 "gtm_string.h" #include "iosp.h" #include "io.h" #include "io_params.h" #include "op.h" #include "gtm_logicals.h" #include "logical_truth_value.h" #include "cenable.h" GBLREF io_pair io_std_device; /* standard device */ void cenable(void) { static readonly unsigned char cenable_params_list[2] = { (unsigned char)iop_cenable, (unsigned char)iop_eol }; boolean_t is_defined; mstr valstr; mval pars, val; if (io_std_device.in->type == tt) { valstr.len = SIZEOF(GTM_NOCENABLE) - 1; valstr.addr = GTM_NOCENABLE; if (!logical_truth_value(&valstr, FALSE, &is_defined)) { /* if they don't ask for nocenable, the default is enable */ pars.str.len = SIZEOF(cenable_params_list); pars.str.addr = (char *)cenable_params_list; pars.mvtype = val.mvtype = MV_STR; val.str.len = io_std_device.in->trans_name->len; val.str.addr = io_std_device.in->trans_name->dollar_io; op_use(&val, &pars); } } return; } fis-gtm-V7.0-005/sr_unix/cenable.h0000644000032200000250000000113014342376327015604 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2018 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. * * * ****************************************************************/ #ifndef CENABLE_H_INCLUDED #define CENABLE_H_INCLUDED void cenable(void); #endif fis-gtm-V7.0-005/sr_unix/ch_cond_core.c0000755000032200000250000000444114342376327016626 0ustar librarygtc/**************************************************************** * * * 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. * * * ****************************************************************/ /* Generate a core if we need one and haven't done it yet */ #include "mdef.h" #include "error.h" #include "gtmdbglvl.h" GBLREF boolean_t created_core; GBLREF boolean_t dont_want_core; GBLREF boolean_t need_core; GBLREF uint4 gtmDebugLevel; /* Create our own version of the DUMP macro that does not include stack overflow. This error is handled better inside mdb_condition_handler which should be the top level handler whenever that error is raised. I would add an assert for that but this would force mdb_condition_handler to be included in all the images we build forcing them to be larger than they should be by pulling in the transfer table referenced in mdb_condition_handler. Not doing the dump here does not prevent the core from occuring, it just delays where it would occur should ERR_STACKOFLOW be signaled from a utility routine for some reason. Note that the DUMP macro below is defined in error.h and is expanded as part of the DUMPABLE macro below (10/2000 se). Since ERR_STACKOFLOW has the type of fatal, we must explicitly check that this error is NOT ERR_STACKOFLOW. 1/2001 se. The ERR_MEMORY error now gets same treatment as ERR_STACKOFLOW 2008-01-11 se. */ #undef DUMP #define DUMP ( SIGNAL == (int)ERR_ASSERT \ || SIGNAL == (int)ERR_GTMASSERT \ || SIGNAL == (int)ERR_GTMASSERT2 \ || SIGNAL == (int)ERR_GTMCHECK) /* BYPASSOK */ error_def(ERR_ASSERT); error_def(ERR_GTMASSERT); error_def(ERR_GTMASSERT2); error_def(ERR_GTMCHECK); error_def(ERR_MEMORY); error_def(ERR_OUTOFSPACE); error_def(ERR_STACKOFLOW); void ch_cond_core(void) { boolean_t cond_core_signal; cond_core_signal = (ERR_STACKOFLOW == SIGNAL) || (ERR_MEMORY == SIGNAL); if (DUMPABLE && ((cond_core_signal && (GDL_DumpOnStackOFlow & gtmDebugLevel)) || !cond_core_signal) && !SUPPRESS_DUMP) { need_core = TRUE; gtm_fork_n_core(); } } fis-gtm-V7.0-005/sr_unix/ch_overrun.c0000755000032200000250000000236314342376327016374 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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. * * * ****************************************************************/ /* ch_overrun -- condition handler overrun -- default condition handler for Unix -- no normal return from this module */ #include "mdef.h" #include "gtm_stdlib.h" /* for EXIT() usage in MUMPS_EXIT macro */ #include "error.h" #include "send_msg.h" #include "gtmmsg.h" GBLREF int mumps_status; GBLREF boolean_t exit_handler_active; error_def(ERR_NOCHLEFT); void ch_overrun(void) { PRN_ERROR; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOCHLEFT); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOCHLEFT); /* If exit handler is already active, we will just core and die */ if (exit_handler_active) gtm_dump_core(); else { /* Otherwise, we generate a core and exit to drive the condition handler */ gtm_fork_n_core(); MUMPS_EXIT; } } fis-gtm-V7.0-005/sr_unix/change_fhead_timer.c0000755000032200000250000000260414342376327017774 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "cli.h" #include "iosp.h" #include "timers.h" #define ONE_HOUR (10 * 100 * 60 * 60) /* (decisec / millisec) * (decisec / sec) * (sec /min) * (min / hour) */ void change_fhead_timer(char *timer_name, sm_int_ptr_t timer_address, int default_time, bool zero_is_ok) /* default_time is in milliseconds */ { uint4 status, value; error_def(ERR_TIMRBADVAL); default_time = default_time * TIMER_SCALE; timer_address[1] = 0; status = cli_present((char *)timer_name); if (status == CLI_NEGATED) timer_address[0] = zero_is_ok ? 0 : default_time; else if (status == CLI_PRESENT) { status = cli_get_time((char *)timer_name, &value); if (TRUE == status) { if ((ONE_HOUR < value) || ((0 == value) && (FALSE == zero_is_ok))) rts_error(VARLSTCNT(1) ERR_TIMRBADVAL); else /* the above error is of type GTM-I- */ timer_address[0] = value; } else rts_error(VARLSTCNT(1) ERR_TIMRBADVAL); } return; } fis-gtm-V7.0-005/sr_unix/check_encrypt_support.sh0000755000032200000250000001550514342376327021031 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright (c) 2009-2021 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. # # # ################################################################# ################################################################# # # # check_encrypt_support.sh - Checks if headers, libraries # # and executables required for # # building encryption plugin # # are available # # Returns - If encryption is supported, returns either # # "libgcrypt" or "openssl" or "libgcrypt openssl# # FALSE, otherwise. # ################################################################# ################################# # Helper functions # ################################# check_files() { # Check if a given list of files with a given list of extensions is # present in a given list of search paths srch_path=$1 srch_files=$2 srch_ext=$3 missing="" ret=0 for each_file in $srch_files do flag=0 list=`echo $each_file | tr '|' ' '` for file in $list do for each_path in $srch_path do for each_ext in $srch_ext do if [ -f $each_path/$file$each_ext ]; then flag=1 break ; fi done # The below takes care of files whose extensions are already determined by the caller if [ -f $each_path/$file ]; then flag=1 break; fi done done if [ $flag -eq 0 ]; then missing="$missing $each_file" ret=1 fi done return $ret } send_mail() { msg=$1 echo $encrypt_dist_servers | grep -w $hostname > /dev/null if [ $? -eq 0 ]; then msg="Please setup the required dependencies for this distribution server.\n\n$msg" msg="$msg\nAt least one of Libgcrypt or OpenSSL dependencies must be met." sub="ENCRYPTSUPPORTED-E-ERROR : Distribution server $hostname will not build encryption plugin" fi echo "$encrypt_other_servers $encrypt_desktops" | grep -w $hostname > /dev/null if [ $? -eq 0 ]; then msg="This system supports encryption but does not have the required dependencies setup\n\n.$msg" msg="$msg\nAt least one of Libgcrypt or OpenSSL dependencies must be met." sub="ENCRYPTSUPPORTED-W-WARNING : Server $hostname will not build encryption plugin" fi printf "$msg" | mailx -s "$sub" gglogs } ################################# # Helper functions ends # ################################# hostos=`uname -s` hostname=`uname -n | awk -F. '{print $1}'` machtype=`uname -m` server_list="$btc_tools/server_list.csh" this_host_noencrypt="FALSE" if [ -f $server_list ]; then eval `/usr/local/bin/tcsh -efc " source $server_list; echo non_encrypt_machines=\'\\\$non_encrypt_machines\'; echo encrypt_desktops=\'\\\$encrypt_desktops\'; echo encrypt_dist_servers=\'\\\$encrypt_dist_servers\'; echo encrypt_other_servers=\'\\\$encrypt_other_servers\'; " || echo echo ERROR \; exit 1` echo $non_encrypt_machines | grep -w $hostname > /dev/null if [ $? -eq 0 ]; then this_host_noencrypt="TRUE" fi fi if [ "OSF1" = "$hostos" -o \( "HP-UX" = "$hostos" -a "ia64" != "$machtype" \) -o "TRUE" = "$this_host_noencrypt" ]; then echo "FALSE" exit 0 fi lib_search_path="/usr/local/lib64 /usr/local/lib /usr/lib64 /usr/lib /lib64 /lib /usr/local/ssl/lib /usr/lib/x86_64-linux-gnu" lib_search_path="$lib_search_path /usr/lib/i386-linux-gnu /lib/x86_64-linux-gnu /lib/i386-linux-gnu /opt/openssl/0.9.8/lib/hpux64" lib_search_path="$lib_search_path /opt/freeware/lib64 /opt/freeware/lib" include_search_path="/usr/include /usr/local/include /usr/local/include/gpgme /usr/local/ssl/include /opt/openssl/0.9.8/include" include_search_path="$include_search_path /usr/include/i386-linux-gnu /usr/include/x86_64-linux-gnu" include_search_path="$include_search_path /opt/freeware/include" bin_search_path="/usr/bin /usr/local/bin /bin" mandate_headers="gpgme.h gpg-error.h" mandate_libs="libgpg-error libgpgme" mandate_bins="gpg|gpg2" gcrypt_headers="gcrypt.h" gcrypt_libs="libgcrypt" openssl_headers="openssl/evp.h openssl/sha.h openssl/ssl.h openssl/err.h" openssl_libs="libcrypto libssl" lib_ext=".so" if [ "AIX" = "$hostos" ]; then lib_ext="$lib_ext .a" elif [ "OS/390" = "$hosotos" ]; then lib_ext=".dll" fi # ------------------------------------------------------ # # Mandatory checks # # ------------------------------------------------------ # found_mandates="TRUE" check_files "$include_search_path" "$mandate_headers" "" if [ $? -eq 1 ]; then found_mandates="FALSE" mandate_missing_list="Mandatory header files - $missing - are missing" fi check_files "$lib_search_path" "$mandate_libs" "$lib_ext" if [ $? -eq 1 ]; then found_mandates="FALSE" mandate_missing_list="$mandate_missing_list\nMandatory library files - $missing - are missing" fi check_files "$bin_search_path" "$mandate_bins" "" if [ $? -eq 1 ]; then found_mandates="FALSE" mandate_missing_list="$mandate_missing_list\nMandatory executables - $missing - are missing" fi # ------------------------------------------------------ # # Check for libgcrypt # # ------------------------------------------------------ # found_gcrypt="TRUE" check_files "$include_search_path" "$gcrypt_headers" "" if [ $? -eq 1 ]; then found_gcrypt="FALSE" gcrypt_missing_list="\nLibgcrypt header files - $missing - are missing" fi check_files "$lib_search_path" "$gcrypt_libs" "$lib_ext" if [ $? -eq 1 ]; then found_gcrypt="FALSE" gcrypt_missing_list="$gcrypt_missing_list\nLibgcrypt library files - $missing - are missing" fi # ------------------------------------------------------ # # Check for OpenSSL # # ------------------------------------------------------ # found_openssl="TRUE" check_files "$include_search_path" "$openssl_headers" "" if [ $? -eq 1 ]; then found_openssl="FALSE" openssl_missing_list="\nOpenSSL header files - $missing - are missing" fi check_files "$lib_search_path" "$openssl_libs" "$lib_ext" if [ $? -eq 1 ]; then found_openssl="FALSE" openssl_missing_list="$openssl_missing_list\nOpenSSL library files - $missing - are missing\n" fi # ------------------------------------------------------ # # Figure out supported list # # ------------------------------------------------------ # mail_msg="" supported_list="" if [ "TRUE" = $found_mandates ] ; then if [ "TRUE" = "$found_gcrypt" ]; then supported_list="$supported_list gcrypt" ; fi if [ "TRUE" = "$found_openssl" ]; then supported_list="$supported_list openssl" ; fi if [ "" != "$supported_list" ] ; then echo $supported_list exit 0 fi fi # Some of the dependencies are unmet. mail_msg="$mandate_missing_list \n \n $gcrypt_missing_list \n \n $openssl_missing_list" if [ "mail" = "$1" ]; then if [ -f $server_list ]; then send_mail "$mail_msg" fi fi echo "FALSE" exit 1 fis-gtm-V7.0-005/sr_unix/check_utf8_support.csh0000755000032200000250000000717314342376327020400 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2007-2020 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. # # # ################################################################# # ########################################################################################### # # check_utf8_support.csh - Checks if icu library and utf8 locale is available # setenv is_utf8_support to TRUE/FALSE # Returns : # TRUE - if both icu library and utf8 locale is installed # FALSE - if either of them is not available ########################################################################################### # depending on the list of locales configured, locale -a might be considered a binary output. # grep needs -a option to process the output as text but -a is not supported on the non-linux servers we have. if ("Linux" == "$HOSTOS") then set binaryopt = "-a" else set binaryopt = "" endif set found_icu = 0 set utflocale = `locale -a | grep $binaryopt -iE '\.utf.?8$' | head -n1` # icu-config is deprecated. So try "pkg-config icu-io" first, followed by "icu-config" and "pkg-config icu" set cmd = "echo 0" if ( (-X pkg-config) && ( { pkg-config --exists icu-io } ) ) then set cmd = "pkg-config --modversion icu-io" else if (-X icu-config) then set cmd = "icu-config --version" else if ( (-X pkg-config) && ( { pkg-config --exists icu } ) ) then set cmd = "pkg-config --modversion icu" endif set found_icu = `$cmd |& awk '{ver=+$0;if(ver>=3.6) {print 1} else {print 0}}'` if (0 == $found_icu) then # If ICU is not found using the method above, just try harder by looking for libicuio*.* files in known locations # This could not work on new platforms or newly installed supported platforms. # It should be manually tested using this command : # ssh ls -l {/usr/local,/usr,}/lib{64,,32}/libicuio.{a,so,sl} foreach libdir ( {/usr/local,/usr,}/lib{64,/x86_64-linux-gnu,,32,/i386-linux-gnu}/libicuio.{a,so,sl} ) # 36 is the least version GT.M supports for ICU. We have to get the numeric value from the ICU library. # ICU ships libicuio.so linked to the appropriate versioned library - so using filetype -L works well # The below is the format of the libraries on various platforms: # AIX : libicu. (e.g libicuio42.1.a) # Others : libicu... (e.g libicuio.so.42.1) if ( ! -l $libdir ) continue set icu_versioned_lib = `filetest -L $libdir` set verinfo = ${icu_versioned_lib:s/libicuio//} set parts = ( ${verinfo:as/./ /} ) if ($HOSTOS == "AIX") then # for the above example parts = (42 1 a) set icu_ver = $parts[1] else # for the above example parts = (so 42 1) set icu_ver = $parts[2] endif if ($icu_ver >= "36") then set found_icu = 1 break endif end endif # The calling gtm installation script should sould source this script in order to avoid duplication of 'setenv LD_LIBRARY_PATH' # The gtm-internal test system runs it within `...` in a few places and sets the return value to an env variable # To aid both the cases above, do a 'setenv is_utf8_support' as well as 'echo' of TRUE/FALSE if ($found_icu && $utflocale != "") then setenv is_utf8_support TRUE echo "TRUE" # the system has utf8 support else setenv is_utf8_support FALSE echo "FALSE" # the system doesn't have utf8 support endif fis-gtm-V7.0-005/sr_unix/clear_cache_array.c0000644000032200000250000000626214342376327017630 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2019 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 #include #include "gtm_string.h" #include "gdsroot.h" #include "gdsblk.h" #include "gdsbml.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblkops.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "min_max.h" #include "t_qread.h" #include "dse.h" #include "gtmmsg.h" #include "t_begin.h" #include "t_write_map.h" #include "t_abort.h" #include "t_retry.h" #include "t_end.h" #include "wbox_test_init.h" #include "error.h" #include "t_recycled2free.h" #include "cdb_sc.h" #include "eintr_wrappers.h" #include "gtmimagename.h" #include "clear_cache_array.h" #include "gtmio.h" #include "sleep_cnt.h" #include "wcs_sleep.h" #include "interlock.h" #include "gdsbgtr.h" #include "copy.h" #include "shmpool.h" #include "do_semop.h" #include "gtm_semutils.h" GBLREF uint4 process_id; void clear_cache_array(sgmnt_addrs *csa, sgmnt_data_ptr_t csd, gd_region* reg, block_id new_total, block_id old_total) { char *err_msg; boolean_t got_lock; cache_rec_ptr_t cr; cache_rec_ptr_t cr_lo, cr_top, hash_hdr; bt_rec_ptr_t bt; node_local_ptr_t cnl; unix_db_info *udi; int semval; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* If called from db_init, assure we've grabbed the access semaphor and are the only process attached to the database. * Otherwise, we should have crit when called from wcs_recover or mu_truncate. */ udi = FILE_INFO(reg); assert((udi->grabbed_access_sem && (DB_COUNTER_SEM_INCR == (semval = semctl(udi->semid, DB_COUNTER_SEM, GETVAL)))) || csa->now_crit); hash_hdr = (cache_rec_ptr_t)csa->acc_meth.bg.cache_state->cache_array; cr_lo = hash_hdr + csd->bt_buckets; cr_top = cr_lo + csd->n_bts; cnl = csa->nl; /* Last thing we did was wcs_flu, so no buffers should have been dirtied in the meantime. */ assert(0 == cnl->wcs_active_lvl); for (cr = cr_lo; cr < cr_top; cr++) { assert(0 == cr->dirty); if ((CR_BLKEMPTY != cr->blk) && (new_total <= cr->blk)) { /* Invalidate this cache record. */ cr->cycle++; if (CR_BLKEMPTY != cr->blk) { bt = bt_get(cr->blk); if (NULL != bt) { bt->cache_index = CR_NOTVALID; bt->blk = BT_NOTVALID; } } cr->blk = CR_BLKEMPTY; /* when cr->blk is empty, ensure no bt points to this cache-record */ cr->bt_index = 0; /* offset to bt_rec */ cr->data_invalid = 0; /* process_id */ cr->flushed_dirty_tn = 0; /* value of dirty at the time of flush */ cr->in_tend = 0; /* release of a shmpool reformat block if the current cache record is pointing to it */ SHMPOOL_FREE_CR_RFMT_BLOCK(reg, csa, cr); WRITE_LATCH_VAL(cr) = LATCH_CLEAR; cr->jnl_addr = 0; cr->refer = FALSE; assert(!cr->stopped); } } } fis-gtm-V7.0-005/sr_unix/clear_cache_array.h0000644000032200000250000000134014342376327017625 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2019 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. * * * ****************************************************************/ #ifndef CLEAR_CACHE_ARRAY_DEFINED #define CLEAR_CACHE_ARRAY_DEFINED #include "gdsroot.h" void clear_cache_array(sgmnt_addrs *csa, sgmnt_data_ptr_t csd, gd_region* reg, block_id new_total, block_id old_total); #endif fis-gtm-V7.0-005/sr_unix/cli.c0000755000032200000250000003056014342376327014771 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_ctype.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_limits.h" #include #include "cli.h" #include "util.h" #include "cli_parse.h" #include "min_max.h" #include "gtmimagename.h" #include "gtmmsg.h" error_def(ERR_CLISTRTOOLONG); error_def(ERR_NUMERR); error_def(ERR_NUM64ERR); error_def(ERR_UNUM64ERR); error_def(ERR_HEXERR); error_def(ERR_HEX64ERR); /* * -------------------------------------------------- * Find the qualifier and convert it to hex. * * Return: * TRUE - OK * FALSE - Could not convert to hex * -------------------------------------------------- */ boolean_t cli_get_hex(char *entry, uint4 *dst) { char buf[MAX_LINE + 1]; char local_str[MAX_LINE + 1]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(strlen(entry) > 0); strncpy(local_str, entry, SIZEOF(local_str) - 1); DEBUG_ONLY(TREF(cli_get_str_max_len) = MAX_LINE;) if ((cli_present(local_str) == CLI_PRESENT) && cli_get_value(local_str, buf)) { if (!cli_str_to_hex(buf, dst)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_HEXERR, 2, LEN_AND_STR(buf)); return FALSE; } return TRUE; } return FALSE; } /* * -------------------------------------------------- * Find the qualifier and convert it to 64 bit hex. * * Return: * TRUE - OK * FALSE - Could not convert to hex * -------------------------------------------------- */ boolean_t cli_get_hex64(char *entry, gtm_uint64_t *dst) { char buf[MAX_LINE + 1]; char local_str[MAX_LINE + 1]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(strlen(entry) > 0); strncpy(local_str, entry, SIZEOF(local_str) - 1); DEBUG_ONLY(TREF(cli_get_str_max_len) = MAX_LINE;) if ((cli_present(local_str) == CLI_PRESENT) && cli_get_value(local_str, buf)) { if (!cli_str_to_hex64(buf, dst)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_HEX64ERR, 2, LEN_AND_STR(buf)); return FALSE; } return TRUE; } return FALSE; } /* * -------------------------------------------------- * Find the qualifier and convert it to 64 bit unsigned decimal number. * * Return: * TRUE - OK * FALSE - Could not convert to hex * -------------------------------------------------- */ boolean_t cli_get_uint64(char *entry, gtm_uint64_t *dst) { char buf[MAX_LINE + 1]; char local_str[MAX_LINE + 1]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(strlen(entry) > 0); strncpy(local_str, entry, SIZEOF(local_str) - 1); DEBUG_ONLY(TREF(cli_get_str_max_len) = MAX_LINE;) if ((cli_present(local_str) == CLI_PRESENT) && cli_get_value(local_str, buf)) { if ((!cli_is_hex_explicit(buf) || !cli_str_to_hex64(buf, dst)) && !cli_str_to_uint64(buf, dst)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_UNUM64ERR, 2, LEN_AND_STR(buf)); return FALSE; } return TRUE; } return FALSE; } /* * -------------------------------------------------- * Find the qualifier and convert it to decimal number. * * Return: * TRUE - OK * FALSE - Could not convert to number * -------------------------------------------------- */ boolean_t cli_get_int(char *entry, int4 *dst) { char buf[MAX_LINE + 1]; char local_str[MAX_LINE + 1]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(strlen(entry) > 0); strncpy(local_str, entry, SIZEOF(local_str) - 1); DEBUG_ONLY(TREF(cli_get_str_max_len) = MAX_LINE;) if (cli_present(local_str) == CLI_PRESENT && cli_get_value(local_str, buf)) { if ((!cli_is_dcm(buf) || !cli_str_to_int(buf, dst)) && (!cli_is_hex_explicit(buf) || !cli_str_to_num(buf, dst))) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NUMERR, 2, LEN_AND_STR(buf)); return FALSE; } return TRUE; } return FALSE; } /* * -------------------------------------------------- * Find the qualifier and convert it to 64 bit decimal number. * * Return: * TRUE - OK * FALSE - Could not convert to number * -------------------------------------------------- */ boolean_t cli_get_int64(char *entry, gtm_int64_t *dst) { char buf[MAX_LINE + 1]; char local_str[MAX_LINE + 1]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(strlen(entry) > 0); strncpy(local_str, entry, SIZEOF(local_str) - 1); DEBUG_ONLY(TREF(cli_get_str_max_len) = MAX_LINE;) if (cli_present(local_str) == CLI_PRESENT && cli_get_value(local_str, buf)) { if ((!cli_is_dcm(buf) || !cli_str_to_int64(buf, dst)) && (!cli_is_hex_explicit(buf) || !cli_str_to_num64(buf, dst))) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NUM64ERR, 2, LEN_AND_STR(buf)); return FALSE; } return TRUE; } return FALSE; } /* * -------------------------------------------------- * Find the qualifier and convert it to decimal number * unless 0x prefix. * * Return: * TRUE - OK * FALSE - Could not convert to number * -------------------------------------------------- */ boolean_t cli_get_num(char *entry, int4 *dst) { char buf[MAX_LINE + 1]; char local_str[MAX_LINE + 1]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(strlen(entry) > 0); strncpy(local_str, entry, SIZEOF(local_str) - 1); DEBUG_ONLY(TREF(cli_get_str_max_len) = MAX_LINE;) if (cli_present(local_str) == CLI_PRESENT && cli_get_value(local_str, buf)) { if (!cli_str_to_num(buf, dst)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NUMERR, 2, LEN_AND_STR(buf)); return FALSE; } return TRUE; } return FALSE; } /* * -------------------------------------------------- * Find the qualifier and convert it to 64 bit decimal number * unless 0x prefix. * * Return: * TRUE - OK * FALSE - Could not convert to number * -------------------------------------------------- */ boolean_t cli_get_num64(char *entry, gtm_int64_t *dst) { char buf[MAX_LINE + 1]; char local_str[MAX_LINE + 1]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(strlen(entry) > 0); strncpy(local_str, entry, SIZEOF(local_str) - 1); DEBUG_ONLY(TREF(cli_get_str_max_len) = MAX_LINE;) if (cli_present(local_str) == CLI_PRESENT && cli_get_value(local_str, buf)) { if (!cli_str_to_num64(buf, dst)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NUM64ERR, 2, LEN_AND_STR(buf)); return FALSE; } return TRUE; } return FALSE; } /* * -------------------------------------------------- * Find the qualifier and copy its value to dst buffer. * * Return: * TRUE - OK * FALSE - Could not get string * -------------------------------------------------- */ boolean_t cli_get_str(char *entry, char *dst, unsigned short *max_len) { char buf[MAX_LINE + 1]; char local_str[MAX_LINE + 1]; size_t maxdstlen, maxbuflen, copylen; # ifdef DEBUG DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # endif maxbuflen = SIZEOF(buf); maxdstlen = *max_len; assert(maxdstlen <= maxbuflen); /* Ensure that the callers do not use a MAX_LINE or greater buffer */ assert(maxdstlen > 0); assert(strlen(entry) > 0); SNPRINTF(local_str, MAX_LINE, "%s", entry); DEBUG_ONLY(TREF(cli_get_str_max_len) = maxdstlen;) /* for use inside cli_get_value -> get_parm_entry ... */ if (!((CLI_PRESENT == cli_present(local_str)) && cli_get_value(local_str, buf))) { if (!cli_get_parm(local_str, buf)) { DEBUG_ONLY(TREF(cli_get_str_max_len) = maxdstlen;) /* for use in cli_get_value -> get_parm_entry ... */ return FALSE; } } DEBUG_ONLY(TREF(cli_get_str_max_len) = maxdstlen;) /* for use in cli_get_value -> get_parm_entry ... */ copylen = strlen(buf); if (maxdstlen < copylen) { if (!IS_GTM_IMAGE) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_CLISTRTOOLONG, 3, entry, copylen, maxdstlen); else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_CLISTRTOOLONG, 3, entry, copylen, maxdstlen); return FALSE; } memset(dst, 0, maxdstlen); memcpy(dst, buf, copylen); *max_len = (unsigned short) copylen; return TRUE; } /* * -------------------------------------------------- * Find the qualifier and convert its value to millisecounds and return it in dst * * Return: * TRUE - OK * FALSE - Could not convert to time * -------------------------------------------------- */ boolean_t cli_get_time(char *entry, uint4 *dst) { #define MAXFACTOR (10 * 100 * 60 * 60) /* (decisec / millisec) * (decisec / sec) * (sec /min) * (min / hour) */ char buf[MAX_LINE + 1], *cp, local_str[MAX_LINE + 1]; unsigned int factor; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(strlen(entry) > 0); strncpy(local_str, entry, SIZEOF(local_str) - 1); buf[0] = ':'; DEBUG_ONLY(TREF(cli_get_str_max_len) = MAX_LINE;) if ((cli_present(local_str) == CLI_PRESENT && cli_get_value(local_str, buf + 1))) { for (*dst = 0, factor = 10, cp = buf + strlen(buf) - 1; cp >= buf; cp--) { if (!ISDIGIT_ASCII((int)*cp)) { if (':' == *cp) { if (MAXFACTOR < factor) return FALSE; *dst = *dst + (uint4)(STRTOUL(cp + 1, NULL, 10) * factor); /* #define MAXFACTOR shows the series for factor */ factor = ((10 == factor) ? 100 : 60) * factor; } else return FALSE; } } return TRUE; } return FALSE; } /* * -------------------------------------------------- * Find out if cli entry is either T(rue) F(alse), or * neither. * * Return: * 1 - TRUE * 0 - FALSE * -1 - neither * -------------------------------------------------- */ int4 cli_t_f_n (char *entry) { char buf[MAX_LINE + 1]; char local_str[MAX_LINE + 1]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert (strlen(entry) > 0); strncpy(local_str, entry, SIZEOF(local_str) - 1); local_str[SIZEOF(local_str) - 1] = '\0'; cli_strupper(local_str); DEBUG_ONLY(TREF(cli_get_str_max_len) = MAX_LINE;) if (cli_get_value(local_str, buf)) { if ('t' == TOLOWER(buf[0])) return (1); else if ('f' == TOLOWER(buf[0])) return (0); else { util_out_print("Invalid value !AD specified for qualifier !AD", TRUE, LEN_AND_STR(buf), LEN_AND_STR(local_str)); return (-1); } } else { FPRINTF(stderr, "Error: cannot get value for %s.\n", entry); return (-1); } } /* * -------------------------------------------------- * Find out if cli entry is either T(rue), A(lways), F(alse), N(ever), or * neither. * * Return: * 0 - FALSE/NEVER * 1 - TRUE/ALWAYS * 2 - ALLOWEXISTING * -1 - None of them * -------------------------------------------------- */ int4 cli_n_a_e (char *entry) { char buf[MAX_LINE + 1]; char local_str[MAX_LINE + 1]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert (strlen(entry) > 0); strncpy(local_str, entry, SIZEOF(local_str) - 1); local_str[SIZEOF(local_str) - 1] = '\0'; cli_strupper(local_str); DEBUG_ONLY(TREF(cli_get_str_max_len) = MAX_LINE;) if (cli_get_value(local_str, buf)) { if ('f' == TOLOWER(buf[0]) || 'n' == TOLOWER(buf[0])) return (0); else if ('t' == TOLOWER(buf[0]) || 'a' == TOLOWER(buf[0])) return (1); else if ('e' == TOLOWER(buf[0])) return (2); else { util_out_print("Invalid value !AD specified for qualifier !AD", TRUE, LEN_AND_STR(buf), LEN_AND_STR(local_str)); return (-1); } } else { FPRINTF(stderr, "Error: cannot get value for %s.\n", entry); return (-1); } } boolean_t cli_get_defertime(char *entry, int4 *dst) { char buf[MAX_LINE + 1]; char local_str[MAX_LINE + 1]; int ch_index = 0; int4 prev_value = 0, num = 0; boolean_t neg_num = FALSE; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(strlen(entry) > 0); strncpy(local_str, entry, SIZEOF(local_str) - 1); DEBUG_ONLY(TREF(cli_get_str_max_len) = MAX_LINE;) if (cli_present(local_str) == CLI_PRESENT && cli_get_value(local_str, buf)) { prev_value = 0; if (buf[ch_index] == '-') { neg_num = TRUE; ch_index++; } for (; ((buf[ch_index] >= '0' && buf[ch_index] <= '9') && (buf[ch_index] != '\0')); ch_index++) { num = num * 10 + (buf[ch_index] - '0'); if (num < prev_value) break; prev_value = num; } if (neg_num && buf[ch_index] == '\0') { num = num * -1; } if((-1 > num || num > INT_MAX) || buf[ch_index] != '\0' || (neg_num && !num)) { FPRINTF(stderr, "Error: cannot convert %s value to decimal number.\n", buf); return FALSE; } *dst = num; return TRUE; } return FALSE; } fis-gtm-V7.0-005/sr_unix/cli.h0000755000032200000250000001043014342376327014770 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef CLI_H #define CLI_H /* * ----------------------------------------------------------- * Parser include file * ----------------------------------------------------------- */ #define MAX_PARMS 1024 /* Maximum parameters on command line */ #define MAX_CMD_LEN 25 /* Max Command name string length */ #define MAX_OPT_LEN 25 /* Max Option name string length */ #define MAX_CLI_ERR_STR 256 /* Max error string length */ #define MAX_LINE 32767+256 /* Max line len , maximum record size plus some overhead */ #define PARM_OVHD 32 /* Parameter overhead value */ #define VAL_N_A 0 /* value type not applicable */ #define VAL_STR 1 /* String value type */ #define VAL_NUM 2 /* Number */ #define VAL_TIME 3 /* Time (can never be used on verb) */ #define VAL_LIST 3 /* Value can be a list (only used on verb, applies to last parameter) */ #define VAL_DCM 0 /* Number is Decimal */ #define VAL_HEX 1 /* Number is Hex */ #define VAL_DISALLOWED 0 /* Value Disallowed */ #define VAL_NOT_REQ 1 /* Value not Required, but allowed */ #define VAL_REQ 2 /* Value Required */ #define PARM_NOT_REQ 0 /* Parameter optional */ #define PARM_REQ 1 /* Parameter required */ #define NON_NEG 0 /* Non Negatable */ #define NEG 1 /* Negatable */ #define CLI_ABSENT 0 #define CLI_PRESENT 1 #define CLI_NEGATED 2 #define CLI_DEFAULT 3 /* default present: The present field is only one * bit, therefore, 3 is euqiv. to 1, i.e. CLI_PRESENT * (since there is not CLI_DEFAULT on VMS, * cli_present() should not return CLI_DEFAULT). */ #define DEFA_PRESENT (char *) 1L /* Should be same as CLI_PRESENT - default present */ #define CLI_GET_STR_ALL cli_get_str /* * ------------------------------------------------------ * Here the CLI_PARM structure is used * to give default values to a qualifier * wherever qualifiers dont require values. * Where qualifiers require values, the * CLI_PARM structure is used to prompt for the values. * ------------------------------------------------------ */ typedef struct cmd_parm_struct { char name[MAX_OPT_LEN]; char prompt[MAX_OPT_LEN]; boolean_t parm_required; /* Is this parameter required or optional? */ } CLI_PARM; typedef struct cmd_parm_tag { char name[MAX_OPT_LEN]; /* name string */ void (*func)(void); /* Ptr to worker function */ struct cmd_parm_tag *parms; /* Qualifiers */ struct cmd_parm_struct *parm_values; /* Parameters */ struct cmd_parm_tag *qual_vals; /* Extra Qualifiers */ boolean_t (*disallow_func)(void); /* Ptr to disallow function */ char *dfault_str; unsigned required : 2; /* Value required flag. Values : 0 - disallowed, 1 - optional 2 - required */ unsigned short max_parms; /* Max. # of parameters allowed */ unsigned negatable : 1; /* Negatable flag */ unsigned val_type : 2; /* Value Type VAL_N_A - type not applicable VAL_STR - String value type VAL_NUM - Number VAL_TIME - Time */ unsigned hex_num : 1; /* Number is hex */ unsigned present : 2; /* Arg. is present on command line */ unsigned negated : 1; /* Arg. negated on command line */ char *pval_str; /* Value string */ } CLI_ENTRY; typedef struct { int argc; #ifdef __osf__ #pragma pointer_size (save) #pragma pointer_size (long) #endif char **argv; #ifdef __osf__ #pragma pointer_size (restore) #endif char *tp; /* token pointer */ int buflen; /* length of in_str */ char in_str[]; /* input string buffer. The real length is computed and added to this block */ } IN_PARMS; /* include platform independent prototypes */ #include "cliif.h" #include "gtm_stdio.h" void cli_strlwr(char *sp); int cli_is_id(char *p); void skip_white_space(void); int cli_has_space(char *p); char *cli_fgets(char *buffer, int buffersize, FILE *fp, boolean_t cli_lex_str); #endif fis-gtm-V7.0-005/sr_unix/cli_disallow.c0000755000032200000250000001125514342376327016667 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2002-2021 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 #include "gtm_stdio.h" #include #include "gtm_string.h" #include "gtmmsg.h" #include "cli.h" #include "cli_parse.h" #include "cli_disallow.h" #include "error.h" #include "util.h" #include "mupip_cmd_disallow.h" GBLREF char cli_err_str[]; GBLREF char *cli_err_str_ptr; /* to check lengths and update cli_err_str*/ void cli_err_strcat(char *str) { size_t lencli, lenstr; lencli = strlen(cli_err_str); lenstr = strlen(str); /* No error string should be longer than MAX_CLI_ERR_STR */ assert(MAX_CLI_ERR_STR > (lencli + lenstr + 2)); assert(MAX_CLI_ERR_STR > lencli); /* For SCI on the theory a one var expr is more easily tracked */ memcpy(cli_err_str + lencli," ",1); assert(MAX_CLI_ERR_STR > lencli + 1); /* For SCI on the theory a one var expr is more easily tracked */ memcpy(cli_err_str + lencli + 1, str, lenstr); *(cli_err_str + lencli + lenstr + 1) = '\0'; } /*---------------------------------------------- * d_c_cli_present: * disallow_check_cli_present * * This is a wrapper for cli_present to be used * from disallow functions (i.e. check_disallow * and those it calls). * * Mimicks VMS CLI. * * Do not use other than in *_disallow functions. * * Arguments: * qualifier string * * Return: * TRUE if present, * FALSE otherwise *---------------------------------------------- */ boolean_t d_c_cli_present(char *str) { int val; char *str_ptr; val = cli_present(str); str_ptr = strchr(str,DOT); if (str_ptr) str_ptr++; /* skip the dot */ else str_ptr = str; cli_err_strcat(str_ptr); return(CLI_PRESENT == val); } /*---------------------------------------------- * d_c_cli_negated * disallow_check_cli_negated * * This is a wrapper for cli_negated to be used * from disallow functions (i.e. check_disallow * and those it calls). * * Mimicks NEG operator of VMS CLI. * * Do not use other than in *_disallow functions. * * Arguments: * qualifier string * * Return: * TRUE if negated, * FALSE otherwise *---------------------------------------------- */ boolean_t d_c_cli_negated(char *str) { boolean_t val; char *str_ptr; val = cli_negated(str); str_ptr = strchr(str,DOT); if (str_ptr) str_ptr++; /* skip the dot */ else str_ptr = str; cli_err_strcat(str_ptr); return(val); } /*---------------------------------------------- * cli_check_any2 * * checks if any two of the (many) inputs are * non-zero. * * Mimicks ANY2 operator of VMS CLI. * * Do not use other than in *_disallow functions. * * This function is added for ease of port of * CLD files. * * Arguments: * list of booleans * * Return: * TRUE if any2 condition holds, * FALSE otherwise *---------------------------------------------- */ boolean_t cli_check_any2(int argcnt, ...) { va_list var; int oper, state = 0; VAR_START(var, argcnt); oper = 0; while(argcnt) { if (va_arg(var, VA_ARG_TYPE_BOOL) && 1 < ++state) return TRUE; argcnt--; } va_end(var); return FALSE; } /*------------------------------------------------- * check_disallow * Checks whether the disallow condition is met * It is called for the tables that are involved * in the command being executed (which might be * one or two, whether there are extra qualifiers * or not) * * Return: * TRUE : ok to go on * FALSE: disallowed. * *------------------------------------------------- */ boolean_t check_disallow(CLI_ENTRY *pparm) { static boolean_t (*qual_disallow_func)(void); /* Ptr to disallow function */ boolean_t tmpres; /* parm should be NULL only for the extra qualifiers table */ if (!pparm) return TRUE; qual_disallow_func = pparm->disallow_func; if (NULL == qual_disallow_func) return TRUE; assert(NULL != pparm->parms); /* should never add a line in *_cmd.c with a disallow function and no sub-qualifiers */ /* Copy the error string ahead of time */ SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Missing or illegal combination of command elements - check documentation:"); /* point to the end so that individual disallow functions can fill it */ cli_err_str_ptr = cli_err_str + strlen(cli_err_str); if (qual_disallow_func()) { return FALSE; } /* If there was no error, clear cli_err_str */ *cli_err_str = 0; return TRUE; } fis-gtm-V7.0-005/sr_unix/cli_disallow.h0000755000032200000250000000152414342376327016672 0ustar librarygtc/**************************************************************** * * * Copyright 2002, 2006 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. * * * ****************************************************************/ #ifndef CLI_DISALLOW_H #define CLI_DISALLOW_H void cli_err_strcat(char *str); boolean_t d_c_cli_present(char *str); boolean_t d_c_cli_negated(char *str); boolean_t cli_check_any2(int argcnt, ...); boolean_t check_disallow(CLI_ENTRY *pparm); #define CLI_DIS_CHECK if (disallow_return_value) return TRUE; #define CLI_DIS_CHECK_N_RESET CLI_DIS_CHECK; *cli_err_str_ptr = '\0'; #define DOT '.' #endif fis-gtm-V7.0-005/sr_unix/cli_lex.c0000755000032200000250000004362014342376327015642 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* * ----------------------------------------------------- * Lexical analyzer routines for command line interpreter * ----------------------------------------------------- */ #include "mdef.h" #include "gtm_ctype.h" #include #include "gtm_stdio.h" #include "gtm_string.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" #include "gtm_utf8.h" #endif #include "cli.h" #include "eintr_wrappers.h" #include "min_max.h" GBLDEF char cli_token_buf[MAX_LINE + 1]; /* Token buffer */ GBLREF int cmd_cnt; GBLREF char **cmd_arg; GBLDEF boolean_t gtm_cli_interpret_string = TRUE; GBLDEF IN_PARMS *cli_lex_in_ptr; #ifdef UTF8_SUPPORTED GBLREF boolean_t gtm_utf8_mode; #define CLI_GET_CHAR(PTR, BUFEND, CHAR) (gtm_utf8_mode ? UTF8_MBTOWC(PTR, BUFEND, CHAR) : (CHAR = (wint_t)*(PTR), (PTR) + 1)) #define CLI_PUT_CHAR(PTR, CHAR) (gtm_utf8_mode ? UTF8_WCTOMB(CHAR, PTR) : (*(PTR) = CHAR, (PTR) + 1)) #define CLI_ISSPACE(CHAR) (gtm_utf8_mode ? U_ISSPACE(CHAR) : ISSPACE_ASCII((int)CHAR)) #else #define CLI_GET_CHAR(PTR, BUFEND, CHAR) (CHAR = (int)*(PTR), (PTR) + 1) #define CLI_PUT_CHAR(PTR, CHAR) (*(PTR) = CHAR, (PTR) + 1) #define CLI_ISSPACE(CHAR) ISSPACE_ASCII(CHAR) #endif static int tok_string_extract(void) { int token_len; boolean_t have_quote, first_quote; uchar_ptr_t in_sp, out_sp, in_next, last_in_next, bufend; /* really one past last byte of buffer */ # ifdef UTF8_SUPPORTED wint_t ch; # else int ch; # endif assert(cli_lex_in_ptr); in_sp = (uchar_ptr_t)cli_lex_in_ptr->tp; bufend = (uchar_ptr_t)&cli_lex_in_ptr->in_str[0] + cli_lex_in_ptr->buflen; out_sp = (uchar_ptr_t)cli_token_buf; token_len = 0; have_quote = FALSE; last_in_next = in_next = CLI_GET_CHAR(in_sp, bufend, ch); for ( ; ;) { /* '-' is not a token separator */ while (ch && !CLI_ISSPACE(ch)) { last_in_next = in_next; if (ch == '"') { if (!have_quote) { if (!gtm_cli_interpret_string) { out_sp = CLI_PUT_CHAR(out_sp, ch); token_len++; } have_quote = TRUE; in_next = CLI_GET_CHAR(in_next, bufend, ch); } else { if (!gtm_cli_interpret_string) { out_sp = CLI_PUT_CHAR(out_sp, ch); token_len++; } in_next = CLI_GET_CHAR(in_next, bufend, ch); if (ch == '"') { /* double quote, one goes in string, still have quote */ out_sp = CLI_PUT_CHAR(out_sp, ch); in_next = CLI_GET_CHAR(in_next, bufend, ch); token_len++; } else have_quote = FALSE; } } else { out_sp = CLI_PUT_CHAR(out_sp, ch); in_next = CLI_GET_CHAR(in_next, bufend, ch); token_len++; } } if (ch == '\0') { in_sp = last_in_next; /* Points to start of null char so scan ends next call */ break; } if (have_quote) { out_sp = CLI_PUT_CHAR(out_sp, ch); in_next = CLI_GET_CHAR(in_next, bufend, ch); token_len++; continue; } in_sp = in_next; break; } ch = 0; out_sp = CLI_PUT_CHAR(out_sp, ch); cli_lex_in_ptr->tp = (char *)in_sp; return (token_len); } /* * ------------------------- * Inintialize lexer * ------------------------- */ #ifdef __osf__ /* N.B. If the process is started by mumps, argv passed in from main (in gtm.c) is almost straight from the operating system. * if the process is started externally (call-ins), argc and argv are 0 and NULL respectively */ #pragma pointer_size (save) #pragma pointer_size (long) #endif void cli_lex_setup (int argc, char **argv) { int parmlen, parmindx; char **parmptr; # ifdef __osf__ # pragma pointer_size (restore) # endif # ifdef KEEP_zOS_EBCDIC __argvtoascii_a(argc, argv); # endif cmd_cnt = argc; cmd_arg = (char **)argv; /* Quickly run through the parameters to get a ballpark on the size of the string needed to store them. */ for (parmindx = 0, parmptr = argv, parmlen = PARM_OVHD; parmindx < argc; parmptr++, parmindx++) { if (MAX_LINE < (parmlen + STRLEN(*parmptr) + 1)) { /* Copy as much as possible from the command string */ parmlen = MAX_LINE; break; } parmlen += STRLEN(*parmptr) + 1; } /* call-ins may repeatedly initialize cli_lex_setup for every invocation of gtm_init() */ if (!cli_lex_in_ptr || parmlen > cli_lex_in_ptr->buflen) { /* We have the cure for a missing or unusable buffer */ if (cli_lex_in_ptr) free(cli_lex_in_ptr); cli_lex_in_ptr = (IN_PARMS *)malloc(SIZEOF(IN_PARMS) + parmlen + 1); cli_lex_in_ptr->buflen = parmlen; } cli_lex_in_ptr->argc = argc; cli_lex_in_ptr->argv = argv; cli_lex_in_ptr->in_str[0] = '\0'; cli_lex_in_ptr->tp = NULL; } void cli_str_setup(uint4 addrlen, char *addr) { /* callers trigger_parse and zl_cmd_qlf create command strings with knowledge of their length */ uint4 alloclen; assert(cli_lex_in_ptr); alloclen = ((MAX_LINE <= addrlen) ? MAX_LINE : addrlen) + 1; if (!cli_lex_in_ptr || alloclen > cli_lex_in_ptr->buflen) { /* We have the cure for a missing or unusable buffer */ if (cli_lex_in_ptr) free(cli_lex_in_ptr); cli_lex_in_ptr = (IN_PARMS *)malloc(SIZEOF(IN_PARMS) + alloclen + 1); /* + 1 ensures room for terminator */ cli_lex_in_ptr->buflen = alloclen; } cli_lex_in_ptr->argv = NULL; cli_lex_in_ptr->argc = 0; cli_lex_in_ptr->tp = cli_lex_in_ptr->in_str; addrlen = MIN(addrlen, alloclen - 1); memcpy(cli_lex_in_ptr->in_str, addr, addrlen); (cli_lex_in_ptr->in_str)[addrlen] = '\0'; } /* * --------------------------------------------------------------- * Convert string to upper case. Do it only for ascii characters. * --------------------------------------------------------------- */ void cli_strupper(char *sp) { int c; while (c = *sp) *sp++ = TOUPPER(c); } /* * ------------------------------------------------------- * Check if string is a Hex number prefixed by '0X' * * Return: * TRUE - identifier * FALSE - otherwise * ------------------------------------------------------- */ int cli_is_hex_explicit(char *p) { /* The number is HEX, and it explicitly starts with a '0x' */ if (('+' == *p) || ('-' == *p)) p++; if (('0' == *p) && ('X' == TOUPPER(*(p + 1)))) { p = p + 2; } else return FALSE; while (*p && ISXDIGIT_ASCII(*p)) p++; return ((*p) ? FALSE : TRUE); } /* * ------------------------------------------------------- * Check if string is a Hex number * * Return: * TRUE - identifier * FALSE - otherwise * ------------------------------------------------------- */ int cli_is_hex(char *p) { if (('+' == *p) || ('-' == *p)) p++; if (('0' == *p) && ('X' == TOUPPER(*(p + 1)))) { p = p + 2; } while (*p && ISXDIGIT_ASCII(*p)) p++; return ((*p) ? FALSE : TRUE); } /* * ------------------------------------------------------- * Check if token is a qualifier * * Return: * TRUE - qualifier * FALSE - otherwise * ------------------------------------------------------- */ int cli_is_qualif(char *p) { return (*p == '-'); } /* * ------------------------------------------------------- * Check if token is an assignment symbol * * Return: * TRUE - assignment * FALSE - otherwise * ------------------------------------------------------- */ int cli_is_assign(char *p) { return (*p == '='); } /* ---------------------------------------------- * Routine to skip white space while reading. * Called when a parameter has to be read. * The tok_string_extract () doesnt remove * starting spaces while reading a string. * To make use of that while reading a parameter * this has to be called first. * ---------------------------------------------- */ void skip_white_space(void) { uchar_ptr_t in_sp; # ifdef UTF8_SUPPORTED wint_t ch; uchar_ptr_t next_sp, bufend; # endif assert(cli_lex_in_ptr); in_sp = (uchar_ptr_t)cli_lex_in_ptr->tp; # ifdef UTF8_SUPPORTED if (gtm_utf8_mode) { bufend = (uchar_ptr_t)(cli_lex_in_ptr->in_str + cli_lex_in_ptr->buflen); for ( ; ; ) { next_sp = UTF8_MBTOWC(in_sp, bufend, ch); if (!U_ISSPACE(ch)) break; in_sp = next_sp; } } else #endif while(ISSPACE_ASCII((int)*in_sp)) in_sp++; cli_lex_in_ptr->tp = (char *)in_sp; } /* * -------------------------------------------- * Extract one token from a string. * Token is anything between the separator characters * or separator character itself, if it is '-' or '='. * * Return: * token Length * -------------------------------------------- */ static int tok_extract (void) { int token_len; uchar_ptr_t in_sp, in_next, out_sp, bufend; # ifdef UTF8_SUPPORTED wint_t ch; # else int ch; # endif assert(cli_lex_in_ptr); skip_white_space(); /* Skip leading blanks */ in_sp = (uchar_ptr_t)cli_lex_in_ptr->tp; bufend = (uchar_ptr_t)&cli_lex_in_ptr->in_str[0] + cli_lex_in_ptr->buflen; out_sp = (uchar_ptr_t)cli_token_buf; token_len = 0; in_next = CLI_GET_CHAR(in_sp, bufend, ch); if ('-' == ch || '=' == ch) { out_sp = CLI_PUT_CHAR(out_sp, ch); in_sp = in_next; /* advance one character */ token_len = 1; } else if (ch) /* only if something there */ { /* smw if quotable, need to utf8 isspace (BYPASSOK) */ /* '-' is not a token separator */ while(ch && !CLI_ISSPACE(ch) && ch != '=') { out_sp = CLI_PUT_CHAR(out_sp, ch); in_sp = in_next; in_next = CLI_GET_CHAR(in_next, bufend, ch); token_len++; } } ch = 0; out_sp = CLI_PUT_CHAR(out_sp, ch); cli_lex_in_ptr->tp = (char *)in_sp; return(token_len); } static void cli_lex_in_expand(int in_len) { IN_PARMS *new_cli_lex_in_ptr; new_cli_lex_in_ptr = (IN_PARMS *)malloc(SIZEOF(IN_PARMS) + in_len + 1); new_cli_lex_in_ptr->argc = cli_lex_in_ptr->argc; new_cli_lex_in_ptr->argv = cli_lex_in_ptr->argv; new_cli_lex_in_ptr->buflen = in_len; /* + 1 above accounts for null */ free(cli_lex_in_ptr); cli_lex_in_ptr = new_cli_lex_in_ptr; } char *cli_fgets(char *destbuffer, int buffersize, FILE *fp, boolean_t in_tp) { size_t in_len; char cli_fgets_buffer[MAX_LINE], *retptr = NULL; # ifdef UTF8_SUPPORTED int mbc_len, u16_off; int32_t mbc_dest_len; UErrorCode errorcode; UChar *uc_fgets_ret; UChar32 uc32_cp; UChar cli_fgets_Ubuffer[MAX_LINE]; static UFILE *u_fp; /* Only used in this routine so not using STATICDEF */ static FILE *save_fp; /* Only used in this routine so not using STATICDEF */ # endif assert(MAX_LINE >= buffersize); if (in_tp) cli_lex_in_ptr->tp = NULL; # ifdef UTF8_SUPPORTED if (gtm_utf8_mode) { cli_fgets_Ubuffer[0] = 0; if (NULL == save_fp) save_fp = fp; /* there should be no change in fp as it is currently stdin */ assert(save_fp == fp); /* retain the fact that u_finit has been called once without an intervening * u_fclose so that multiple lines can be read over a pipe on hpux and solaris */ if (NULL == u_fp) u_fp = u_finit(fp, NULL, UTF8_NAME); if (NULL != u_fp) { do { /* no f_ferror */ uc_fgets_ret = u_fgets(cli_fgets_Ubuffer, (int32_t)(SIZEOF(cli_fgets_Ubuffer) / SIZEOF(UChar)) - 1, u_fp); } while (NULL == uc_fgets_ret && !u_feof(u_fp) && ferror(fp) && EINTR == errno); if (NULL == uc_fgets_ret) { u_fclose(u_fp); /* clear u_fp in case we enter again */ u_fp = NULL; save_fp = NULL; return NULL; } in_len = u_strlen(cli_fgets_Ubuffer); in_len = trim_U16_line_term(cli_fgets_Ubuffer, (int)in_len); for (u16_off = 0, mbc_len = 0; u16_off < in_len; ) { U16_NEXT(cli_fgets_Ubuffer, u16_off, in_len, uc32_cp); mbc_len += U8_LENGTH(uc32_cp); } if (mbc_len > cli_lex_in_ptr->buflen) { cli_lex_in_expand(mbc_len); /* for terminating null */ buffersize = cli_lex_in_ptr->buflen + 1; destbuffer = cli_lex_in_ptr->in_str; } errorcode = U_ZERO_ERROR; u_strToUTF8(destbuffer, buffersize, &mbc_dest_len, cli_fgets_Ubuffer, (int4)in_len + 1, &errorcode); if (U_FAILURE(errorcode)) { if (U_BUFFER_OVERFLOW_ERROR == errorcode) { /* truncate so null terminated */ destbuffer[buffersize - 1] = 0; retptr = destbuffer; } else retptr = NULL; } else retptr = destbuffer; /* Repoint to new home */ if (in_tp) cli_lex_in_ptr->tp = retptr; } } else { # endif cli_fgets_buffer[0] = '\0'; FGETS_FILE(cli_fgets_buffer, SIZEOF(cli_fgets_buffer), fp, retptr); if (NULL != retptr) { in_len = strlen(cli_fgets_buffer); assert(in_len <= MAX_LINE); if (cli_lex_in_ptr->buflen < in_len) { cli_lex_in_expand((int)in_len); destbuffer = cli_lex_in_ptr->in_str; buffersize = cli_lex_in_ptr->buflen + 1; } in_len = MIN(in_len, buffersize); if ('\n' == cli_fgets_buffer[in_len - 1]) cli_fgets_buffer[in_len - 1] = '\0'; /* replace NL */ memcpy(destbuffer, cli_fgets_buffer, in_len); retptr = destbuffer; /* return proper buffer */ if (in_tp) cli_lex_in_ptr->tp = retptr; } # ifdef UTF8_SUPPORTED } # endif return retptr; } /* * ------------------------------------------------------- * Get token * * Return: * Token Length * * Side effects: * set eof to <> 0 for EOF condition. * ------------------------------------------------------- */ int cli_gettoken (int *eof) { char *ptr, *ptr_top; int arg_no, print_len, token_len, avail; assert(cli_lex_in_ptr); /* Reading from program argument list */ if ((1 < cli_lex_in_ptr->argc) && (NULL == cli_lex_in_ptr->tp)) { cli_lex_in_ptr->tp = ptr = cli_lex_in_ptr->in_str; /* MUMPS and GT.CM need at least their path length in the command line TODO: why */ avail = cli_lex_in_ptr->buflen - STRLEN(cli_lex_in_ptr->argv[0]); for (ptr_top = ptr + avail, arg_no = 1; (arg_no < cli_lex_in_ptr->argc) && (ptr_top > ptr); arg_no++) { /* Convert arguments into array */ print_len = SNPRINTF(ptr, (ptr_top - ptr), "%s%s", (1 < arg_no) ? " " : "", cli_lex_in_ptr->argv[arg_no]); if ((ptr_top - ptr) <= print_len) break; ptr += print_len; } } if ((NULL == cli_lex_in_ptr->tp) || (1 > strlen(cli_lex_in_ptr->tp))) { cli_token_buf[0] = '\0'; /* cli_fgets can malloc/free cli_lex_in_ptr. Passing in TRUE as last parameter will do the set * to cli_lex_in_ptr->tp within cli_fgets() after any malloc/free, thus avoiding the problem of * writing to freed memory if the set were done here. */ cli_fgets(cli_lex_in_ptr->in_str, cli_lex_in_ptr->buflen + 1, stdin, TRUE); if (NULL != cli_lex_in_ptr->tp) *eof = 0; else { *eof = EOF; return (0); } } token_len = tok_extract(); *eof = (!token_len && (1 < cli_lex_in_ptr->argc)); return token_len; } /* * -------------------------------------------- * Copy next token to the token buffer. * Do not advance the token pointer. * * Return: * Token Length * * Side effects: * set eof to <> 0 for EOF condition. * ------------------------------------------------------- */ int cli_look_next_token(int *eof) { int tok_len; char *old_tp; assert(cli_lex_in_ptr); if (((char *) NULL == cli_lex_in_ptr->tp) || (!strlen(cli_lex_in_ptr->tp))) return(0); old_tp = cli_lex_in_ptr->tp; tok_len = cli_gettoken(eof); cli_lex_in_ptr->tp = old_tp; return(tok_len); } int cli_look_next_string_token(int *eof) { int tok_len; char *old_tp; assert(cli_lex_in_ptr); if (!strlen(cli_lex_in_ptr->tp)) return(0); old_tp = cli_lex_in_ptr->tp; tok_len = cli_get_string_token(eof); cli_lex_in_ptr->tp = old_tp; return(tok_len); } int cli_get_string_token(int *eof) { int arg_no, token_len; char *from, *to, *stop; IN_PARMS *new_cli_lex_in_ptr; assert(cli_lex_in_ptr); /* Reading from program argument list */ if (cli_lex_in_ptr->argc > 1 && cli_lex_in_ptr->tp == 0) { cli_lex_in_ptr->tp = cli_lex_in_ptr->in_str; /* convert arguments into array */ for (arg_no = 1; arg_no < cli_lex_in_ptr->argc; arg_no++) { if (cli_lex_in_ptr->buflen < (STRLEN(cli_lex_in_ptr->in_str) + STRLEN(cli_lex_in_ptr->argv[arg_no]) + 2)) break; /* if too long, just truncate */ if (1 < arg_no) /* 4SCA: strcat adds a space character and a null char */ strcat(cli_lex_in_ptr->in_str, " "); if (cli_has_space(cli_lex_in_ptr->argv[arg_no])) { from = cli_lex_in_ptr->argv[arg_no]; to = cli_lex_in_ptr->in_str + STRLEN(cli_lex_in_ptr->in_str) - 1; stop = cli_lex_in_ptr->in_str + cli_lex_in_ptr->buflen - 2; *to++ = '\"'; while (('\0' != *from) && (to <= stop)) { if ('\"' == *from) *to++ = *from; *to++ = *from++; } *to++ = '\"'; *to = '\0'; } else STRNCAT(cli_lex_in_ptr->in_str, cli_lex_in_ptr->argv[arg_no], cli_lex_in_ptr->buflen - STRLEN(cli_lex_in_ptr->in_str)); } } if ((NULL == cli_lex_in_ptr->tp) || (1 > strlen(cli_lex_in_ptr->tp))) { cli_token_buf[0] = '\0'; /* cli_fgets can malloc/free cli_lex_in_ptr. Passing in TRUE as last parameter will do the set * to cli_lex_in_ptr->tp within cli_fgets() after any malloc/free, thus avoiding the problem of * writing to freed memory if the set were done here. */ cli_fgets(cli_lex_in_ptr->in_str, cli_lex_in_ptr->buflen + 1, stdin, TRUE); if (NULL != cli_lex_in_ptr->tp) *eof = 0; else { *eof = EOF; return (0); } } token_len = tok_string_extract(); *eof = (!token_len && (1 < cli_lex_in_ptr->argc)); return token_len; } /* * ------------------------------------------------------- * Check if string has space in it * * Return: * TRUE - identifier * FALSE - otherwise * ------------------------------------------------------- */ int cli_has_space(char *p) { # ifdef UTF8_SUPPORTED uchar_ptr_t local_p, next_p, bufend; wint_t ch; if (gtm_utf8_mode) { local_p = (uchar_ptr_t)p; bufend = local_p + strlen(p); while (local_p) { next_p = UTF8_MBTOWC(local_p, bufend, ch); if (!ch || U_ISSPACE(ch)) break; local_p = next_p; } p = (char *)local_p; } else # endif while (*p && !ISSPACE_ASCII(*p)) p++; return ((*p) ? (TRUE) : (FALSE)); } fis-gtm-V7.0-005/sr_unix/cli_parse.c0000755000032200000250000007774614342376327016204 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ #if defined(__MVS__) && !defined(_ISOC99_SOURCE) #define _ISOC99_SOURCE #endif #include "mdef.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "cli.h" #include "cli_parse.h" #include "error.h" #include "cli_disallow.h" #include "gtmio.h" #include "op.h" #define NO_STRING "NO" /* Dynamically allocates (that is, grows) enough space for the current array * of parameter strings during the lifetime of the process. Since the number * of array elements is limited, it is better to allocate memory as needed * rather than constantly free and reallocate. */ #define GROW_HEAP_IF_NEEDED(PARM_ARY_INDEX) \ { \ if (TAREF1(parm_str_len, PARM_ARY_INDEX) > TAREF1(parm_ary_len, PARM_ARY_INDEX)) \ { \ if (TAREF1(parm_ary, PARM_ARY_INDEX)) \ free(TAREF1(parm_ary, PARM_ARY_INDEX)); \ \ TAREF1(parm_ary, PARM_ARY_INDEX) = (char *)malloc(TAREF1(parm_str_len, PARM_ARY_INDEX)); \ \ TAREF1(parm_ary_len, PARM_ARY_INDEX) = TAREF1(parm_str_len, PARM_ARY_INDEX); \ } \ assert(NULL != (TAREF1(parm_ary, PARM_ARY_INDEX))); /* 4SCA: parm_ary_len ensure this */ \ } #if MAX_OPT_LEN > MAX_LINE # error MAX_OPT_LEN is greater than MAX_LINE. Fix STRNCMP_STR and STRNCPY_STR usages below. #endif GBLDEF void (*func)(void); /* Function to be dispatched to for this command */ GBLREF char cli_err_str[]; /* Parse Error message buffer */ static CLI_ENTRY *gpqual_root; /* pointer to root of subordinate qualifier table */ static CLI_ENTRY *gpcmd_qual; /* pointer command qualifier table */ static CLI_ENTRY *gpcmd_verb; /* pointer to verb table */ static CLI_PARM *gpcmd_parm_vals; /* pointer to parameters for command */ GBLREF char cli_token_buf[]; GBLREF CLI_ENTRY *cmd_ary; GBLREF IN_PARMS *cli_lex_in_ptr; LITREF mval literal_notimeout; error_def(ERR_CLIERR); error_def(ERR_MUNOACTION); error_def(ERR_MUPCLIERR); /* * ----------------------------------------------- * Clear all parameter values and flags of command * parameter table for a given command. * * Arguments: * cmd_parms - address of parameter array * follow - whether to clear extra value tables or not * TRUE - follow down * FALSE - do not follow down * * Return: * none * ----------------------------------------------- */ void clear_parm_vals(CLI_ENTRY *cmd_parms, boolean_t follow) /* pointer to option's parameter table */ { CLI_ENTRY *root_param; int need_copy; need_copy = (gpcmd_qual != cmd_parms); while (strlen(cmd_parms->name) > 0) { if (cmd_parms->pval_str) free(cmd_parms->pval_str); /* if root table exists, copy over any qualifier values to the new parameter table */ if ((FALSE != follow) && need_copy && (root_param = find_cmd_param(cmd_parms->name, gpcmd_qual, FALSE))) { cmd_parms->pval_str = root_param->pval_str; cmd_parms->negated = root_param->negated; cmd_parms->present = root_param->present; root_param->pval_str = 0; } else { cmd_parms->negated = 0; cmd_parms->present = 0; cmd_parms->pval_str = 0; /* dfault_str also implements default values. 0: it is not present by default, DEFA_PRESENT: it is present by default, no value, str pointer: it is present by default, with the value pointed to */ if (cmd_parms->dfault_str) { cmd_parms->present = CLI_DEFAULT; if (CLI_PRESENT != (INTPTR_T)cmd_parms->dfault_str) cmd_parms->pval_str = cmd_parms->dfault_str; } if ((FALSE != follow) && cmd_parms->qual_vals) clear_parm_vals(cmd_parms->qual_vals, FALSE); } cmd_parms++; } } /* * --------------------------------------------------------- * Find entry in the qualifier table * * Arguments: * str - parameter string * pparm - pointer to parameter table for this command * * Return: * if found, index into command table array, * else -1 * --------------------------------------------------------- */ int find_entry(char *str, CLI_ENTRY *pparm) { int match_ind, res, str_len; boolean_t len_match; int ind; char *sp; ind = 0; match_ind = -1; len_match = FALSE; cli_strupper(str); str_len = strlen(str); while (0 < strlen(sp = (pparm + ind)->name)) { /* As the table is parsed as long as the string in question is lexically smaller than the entry in the table, we go on checking the next entry. If a match is found, the lengths of the two strings (the table entry and the string in question) are compared. When the next entry is checked, if it is not a match, the previous entry is the correct match; if it is a match again (a longer entry), if the first match was a length-match as well, the first entry is the match returned. otherwise an error is returned since we cannot make a decision (does SE match SET or SEP). */ if (0 == (res = STRNCMP_STR(sp, str, str_len))) { if (-1 != match_ind) { if (FALSE == len_match) return (-1); break; } else { if (str_len == strlen(sp)) len_match = TRUE; match_ind = ind; } } else { if (0 < res) break; } ind++; } if (-1 != match_ind && gpqual_root && 0 == STRNCMP_STR(gpqual_root->name, str, MAX_OPT_LEN)) return (-1); return (match_ind); } /* * ----------------------------------------------- * Find command in command table * * Arguments: * str - command string * * Return: * if found, index into command table array, * else -1 * ----------------------------------------------- */ int find_verb(char *str) { return (find_entry(str, cmd_ary)); } /* * --------------------------------------------------------- * Find command parameter in command parameter table * * Arguments: * str - parameter string * pparm - pointer to parameter table for this command * * Return: * if found, pointer to parameter structure * else 0 * --------------------------------------------------------- */ CLI_ENTRY *find_cmd_param(char *str, CLI_ENTRY *pparm, int follow) { CLI_ENTRY *pparm_tmp; int ind, ind_match; char *sp; ind_match = -1; if (NULL == pparm) return NULL; if (0 <= (ind = find_entry(str, pparm))) return pparm + ind; if (FALSE == follow) return NULL; /* if to follow, go through the qual_vals, and check those tables */ for(ind =0; 0 < strlen((pparm + ind)->name); ind++) { pparm_tmp = (pparm + ind)->qual_vals; if (pparm_tmp) ind_match = find_entry(str, pparm_tmp); if (-1 != ind_match) return pparm_tmp + ind_match; } return NULL; } /* * --------------------------------------------------------- * Parse one option. * Read tokens from the input. * Check if it is a valid qualifier or parameter. * If it is a parameter, get it, and save it in the * global parameter array. * If it is a qualifier, get its value and save it in a value table, * corresponding to this option. * * Arguments: * pcmd_parms - pointer to command parameter table * eof - pointer to end of file flag * * Return: * 1 - option parsed OK * -1 - failure to parse option * 0 - no more tokens, in which case * the eof flag is set on end of file. * --------------------------------------------------------- */ int parse_arg(CLI_ENTRY *pcmd_parms, int *eof) { CLI_ENTRY *pparm; char *opt_str, *val_str; int neg_flg; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* ----------------------------------------- * get qualifier marker, or parameter token * ----------------------------------------- */ if (VAL_LIST == gpcmd_verb->val_type && TREF(parms_cnt) == gpcmd_verb->max_parms) return (0); if (!cli_look_next_token(eof)) return (0); /* ------------------------------------------------------------------- * here cli_token_buf is set by the previous cli_look_next_token(eof) * call itself since it in turn calls cli_gettoken() * ------------------------------------------------------------------- */ if (!cli_is_qualif(cli_token_buf) && !cli_is_assign(cli_token_buf)) { /* ---------------------------------------------------- * If token is not a qualifier, it must be a parameter * * No need to check for eof on cli_get_string_token(eof) since * already checked that on the previous cli_look_next_token. * now you have to skip initial white spaces before reading * the string since cli_get_string_token considers a space * as a blank token. hence the need for the skip_white_space() * call. * ------------------------------------------------------------ */ skip_white_space(); cli_get_string_token(eof); if (TREF(parms_cnt) >= gpcmd_verb->max_parms) { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Too many parameters"); return (-1); } TAREF1(parm_str_len, TREF(parms_cnt)) = strlen(cli_token_buf) + 1; GROW_HEAP_IF_NEEDED(TREF(parms_cnt)); memcpy(TAREF1(parm_ary, TREF(parms_cnt)), cli_token_buf, TAREF1(parm_str_len, TREF(parms_cnt))); (TREF(parms_cnt))++; return (1); } /* --------------------------------------------------------------------- * cli_gettoken(eof) need not be checked for return value since earlier * itself we have checked for return value in cli_look_next_token(eof) * --------------------------------------------------------------------- */ cli_gettoken(eof); opt_str = cli_token_buf; if (!pcmd_parms) { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "No qualifiers allowed for this command"); return (-1); } /* ------------------------------------------ * Qualifiers must start with qualifier token * ------------------------------------------ */ if (!cli_is_qualif(cli_token_buf)) { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Qualifier expected instead of : %s ", opt_str); return (-1); } /* ------------------------- * Get the qualifier string * ------------------------- */ if (!cli_look_next_token(eof) || 0 == cli_gettoken(eof)) { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Qualifier string missing %s ", opt_str); return (-1); } /* --------------------------------------- * Fold the qualifier string to upper case * --------------------------------------- */ cli_strupper(opt_str); /* ------------------------- * See if option is negated and update * ------------------------- */ if (-1 == (neg_flg = cli_check_negated(&opt_str, pcmd_parms, &pparm))) return (-1); /* ------------------------------------------------------------- * If value is disallowed for this qualifier, and an assignment * token is encounter, report error, values not allowed for * negated qualifiers * ------------------------------------------------------------- */ if (neg_flg || VAL_DISALLOWED == pparm->required) { if (cli_look_next_token(eof) && cli_is_assign(cli_token_buf)) { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Assignment is not allowed for this option : %s", pparm->name); return (-1); } } else { /* -------------------------------------------------- * Get Value either optional, or required. * In either case, there must be an assignment token * -------------------------------------------------- */ if (!cli_look_next_token(eof) || !cli_is_assign(cli_token_buf)) { if (VAL_REQ == pparm->required) { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Option : %s needs value", pparm->name); return (-1); } else { if (pparm->present) { /* The option was specified before, so clean up that one, * the last one overrides */ if (pparm->pval_str) free(pparm->pval_str); if (pparm->qual_vals) clear_parm_vals(pparm->qual_vals, FALSE); } /* ------------------------------- * Allocate memory and save value * ------------------------------- */ if (pparm->parm_values) { pparm->pval_str = malloc_cpy_str(pparm->parm_values->prompt); if (!cli_get_sub_quals(pparm)) return (-1); } } } else { cli_gettoken(eof); /* --------------------------------- * Get the assignment token + value * --------------------------------- */ if (!cli_is_assign(cli_token_buf)) { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Assignment missing after option : %s", pparm->name); return (-1); } /* -------------------------------------------------------- * get the value token, "=" is NOT a token terminator here * -------------------------------------------------------- */ if (!cli_look_next_string_token(eof) || 0 == cli_get_string_token(eof)) { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Unrecognized option : %s, value expected but not found", pparm->name); cli_lex_in_ptr->tp = 0; return (-1); } val_str = cli_token_buf; if (!cli_numeric_check(pparm, val_str)) { cli_lex_in_ptr->tp = 0; return (-1); } if (pparm->present) { /* The option was specified before, so clean up that one, * the last one overrides */ if (pparm->pval_str) free(pparm->pval_str); if (pparm->qual_vals) clear_parm_vals(pparm->qual_vals, FALSE); } /* ------------------------------- * Allocate memory and save value * ------------------------------- */ pparm->pval_str = malloc_cpy_str(cli_token_buf); if (!cli_get_sub_quals(pparm)) return (-1); } } if (pparm->present) pparm->negated = 0; pparm->negated = neg_flg; pparm->present = 1; if (NULL != pparm->func) func = pparm->func; /* ---------------------------------------------------------------------------------------------------------------------- * If there is another level, update global pointers * Notice that this global pointer updation should be done only at the end of this routine in order to ensure that the * check_disallow() function invoked below sees the currently parsed argument as present (i.e. pparm->present = 1) * ---------------------------------------------------------------------------------------------------------------------- */ if (pparm->parms) { /*------------------------------------------------------------------------------------------- * Check that the disallow conditions for this level are met before switching to next level *------------------------------------------------------------------------------------------- */ if (FALSE == check_disallow(gpcmd_verb)) return (-1); gpqual_root = pparm; clear_parm_vals(pparm->parms, TRUE); gpcmd_qual = pparm->parms; gpcmd_verb = pparm; /* this needs to be done in order for check_disallow() to do the proper disallow check. * an example that will not work otherwise is cli_disallow_mupip_replic_receive() */ } return (1); } /* ----------------------------------------------------- * Check if the value is numeric if it is supposed to be * * Return: * TRUE - It is numeric or val_type is not * numeric anyway * FALSE - Is not numeric * ----------------------------------------------------- */ boolean_t cli_numeric_check(CLI_ENTRY *pparm, char *val_str) { boolean_t retval = TRUE; if (VAL_NUM == pparm->val_type) { if (pparm->hex_num) { /* It can ONLY be a HEX number. */ if (!cli_is_hex(val_str)) { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Unrecognized value: %s, A non-negative hexadecimal integer required", val_str); retval = FALSE; } } else if (!cli_is_dcm(val_str) && !cli_is_hex_explicit(val_str)) { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Unrecognized value: %s, A non-negative decimal or hexadecimal integer required", val_str); retval = FALSE; } } return (retval); } /*--------------------------- * Check if option is negated *--------------------------- */ int cli_check_negated(char **opt_str_ptr, CLI_ENTRY *pcmd_parm_ptr, CLI_ENTRY **pparm_ptr) { int neg_flg; CLI_ENTRY *pcmd_parms; char *opt_str_tmp; pcmd_parms = pcmd_parm_ptr; opt_str_tmp = *opt_str_ptr; if (0 == MEMCMP_LIT(*opt_str_ptr, NO_STRING)) { *opt_str_ptr += SIZEOF(NO_STRING) - 1; neg_flg = 1; } else neg_flg = 0; /* -------------------------------------------- * search qualifier table for qualifier string * -------------------------------------------- */ if (0 == (*pparm_ptr = find_cmd_param(*opt_str_ptr, pcmd_parms, FALSE))) { /* Check that the qualifier does not have the NO prefix */ if (0 == (*pparm_ptr = find_cmd_param(opt_str_tmp, pcmd_parms, FALSE))) { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Unrecognized option : %s", *opt_str_ptr); cli_lex_in_ptr->tp = 0; return (-1); } else { /* It was a valid qualifier with the prefix NO */ *opt_str_ptr = opt_str_tmp; neg_flg = 0; } } /* ---------------------------------------------------- * if option is negated and it is not negatable, error * ---------------------------------------------------- */ if (!(*pparm_ptr)->negatable && neg_flg) { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Option %s may not be negated", *opt_str_ptr); return (-1); } return neg_flg; } /* * -------------------------------------------------- * * Process the qualifier to see if any extra parameters * are possible in the <...> * Return: * TRUE - OK * FALSE - Error * -------------------------------------------------- */ boolean_t cli_get_sub_quals(CLI_ENTRY *pparm) { CLI_ENTRY *pparm_qual, *pparm1; char local_str[MAX_LINE], tmp_str[MAX_LINE + 1], *tmp_str_ptr; char *ptr_next_val, *ptr_prev_val, *ptr_next_comma, *ptr_equal; int neg_flg; boolean_t val_flg, has_a_qual; size_t len_str, ptr_equal_len, neg_adj_len_str; has_a_qual = FALSE; pparm_qual = pparm->qual_vals; if (!pparm_qual) return TRUE; if ((VAL_STR == pparm->val_type) || (VAL_LIST == pparm->val_type)) { len_str = strlen(pparm->pval_str); if (len_str >= sizeof(local_str)) /* BYPASSOK: both size_t */ len_str = sizeof(local_str) - 1; memcpy(local_str, pparm->pval_str, len_str); local_str[len_str] = '\0'; ptr_next_val = local_str; while (NULL != ptr_next_val) { STRNCPY_STR(tmp_str, ptr_next_val, len_str); tmp_str[len_str] = 0; tmp_str_ptr = tmp_str; ptr_next_comma = strchr(tmp_str_ptr, ','); if (NULL == ptr_next_comma) ptr_next_comma = tmp_str_ptr + len_str; else *ptr_next_comma = 0; ptr_equal = strchr(tmp_str_ptr, '='); if (ptr_equal && (ptr_equal < ptr_next_comma) ) *ptr_equal = 0; else ptr_equal = NULL; /* Convert just the qualifier to upper case (e.g. in "enable,on,file=x.mjl" --> "ENABLE,ON,FILE=x.mjl") * Do not convert the values of parameters to upper case as that might result in incorrect translation * if UTF8 characters are present in the command line. The logic below anyways needs only the qualifiers * to be translated. */ cli_strupper(tmp_str); /* ------------------------- * See if option is negated * ------------------------- */ if (-1 == (neg_flg = cli_check_negated( &tmp_str_ptr, pparm_qual, &pparm1))) return FALSE; neg_adj_len_str = len_str - ((1 == neg_flg) ? strlen(NO_STRING) : 0); if ((ptr_equal) && (ptr_equal + 1 < ptr_next_comma)) val_flg = TRUE; else val_flg = FALSE; /* ------------------------------------------------------------- * If value is disallowed for this qualifier, and an assignment * is encountered, report error, values not allowed for * negated qualifiers * ------------------------------------------------------------- */ if (neg_flg || VAL_DISALLOWED == pparm1->required) { if (val_flg) { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Assignment is not allowed for this option : %s", pparm1->name); cli_lex_in_ptr->tp = 0; return FALSE; } } else { if ((!val_flg) && VAL_REQ == pparm1->required) { if (ptr_equal) SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Unrecognized option : %s, value expected but not found", pparm1->name); else SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Option : %s needs value", tmp_str_ptr); cli_lex_in_ptr->tp = 0; return FALSE; } if (!cli_numeric_check(pparm1, ptr_equal + 1)) { cli_lex_in_ptr->tp = 0; return FALSE; } if (pparm1->present) { /* The option was specified before, so clean up that one, * the last one overrides */ if (pparm1->pval_str) free(pparm1->pval_str); } if ((!val_flg) && (VAL_NOT_REQ == pparm1->required) && pparm1->parm_values) pparm1->pval_str = malloc_cpy_str(pparm1->parm_values->prompt); if (val_flg) { ptr_equal_len = strlen(ptr_equal + 1); pparm1->pval_str = malloc(ptr_equal_len + 1); assert(NULL != pparm1->pval_str); STRNCPY_STR(pparm1->pval_str, ptr_next_val + (ptr_equal - tmp_str_ptr) + 1, ptr_equal_len); pparm1->pval_str[ptr_equal_len] = 0; } } if (pparm1->present) pparm->negated = 0; pparm1->negated = neg_flg; pparm1->present = CLI_PRESENT; has_a_qual = TRUE; if ((tmp_str_ptr + neg_adj_len_str) != ptr_next_comma) { ptr_prev_val = ptr_next_val; ptr_next_val = strchr(ptr_next_val, ',') + 1; if (!ptr_next_val || !*ptr_next_val) { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Option expected"); cli_lex_in_ptr->tp = 0; return FALSE; } assert(ptr_next_val > ptr_prev_val); len_str -= ptr_next_val - ptr_prev_val; assert(0 <= len_str); assert(MAX_LINE >= len_str); } else ptr_next_val = NULL; } } /* do one last parse on the qualifier table, if there is any other qualifier present, * the DEFA_PRESENT one is not necessary, otherwise leave it as it is. */ if (has_a_qual) { pparm1 = pparm_qual; while (0 != *pparm1->name) { if (CLI_DEFAULT == pparm1->present) pparm1->present = CLI_ABSENT; pparm1++; } } return TRUE; } /* * ----------------------------------------------------------- * Parse one command. * Get tokens from the input stream. * See if the first token is a command name, as it should be, * and if it is, check if optional arguments that follow, * are legal, and if they are, get their values and * save them in a value table, corresponding to this * option. * If any of these conditions are not met, parse error occures. * * Return: * 0 - command parsed OK * EOF - end of file * <> - failure to parse command * ----------------------------------------------------------- */ int parse_cmd(void) { int res, cmd_ind; char *cmd_str; int opt_cnt; int eof, cmd_err; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; opt_cnt = 0; gpqual_root = NULL; func = 0; cmd_err = 0; TREF(parms_cnt) = 0; /* Parameters count */ *cli_err_str = 0; cmd_str = cli_token_buf; /* ------------------------ * If no more tokens exist * ------------------------ */ if (0 == cli_gettoken(&eof)) { if (eof) return (EOF); return (0); } /* ------------------------------ * Find command in command table * ------------------------------ */ if (-1 != (cmd_ind = find_verb(cmd_str))) { gpcmd_qual = cmd_ary[cmd_ind].parms; gpcmd_parm_vals = cmd_ary[cmd_ind].parm_values; gpcmd_verb = &cmd_ary[cmd_ind]; if (gpcmd_qual) clear_parm_vals(gpcmd_qual, TRUE); func = cmd_ary[cmd_ind].func; /* ---------------------- * Parse command options * ---------------------- */ do { res = parse_arg(gpcmd_qual, &eof); if (1 == res) { opt_cnt++; } } while (1 == res); } else { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Unrecognized command: %s", cmd_str); cli_lex_in_ptr->tp = 0; res = -1; } if ((1 > opt_cnt) && (-1 != res) && (VAL_REQ == cmd_ary[cmd_ind].required)) { SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Command argument expected, but not found"); res = -1; } /*------------------------------------------------------ * Check that the disallow conditions are met (to allow) *------------------------------------------------------ */ if ((-1 != res) && (FALSE == check_disallow(gpcmd_verb))) res = -1; /* ------------------------------------- * If parse error, display error string * ------------------------------------- */ if (-1 == res) { func = 0; eof = 0; } else return (0); /* ------------------------- * If gettoken returned EOF * ------------------------- */ if (eof) return (EOF); else return (ERR_CLIERR); } /* ------------------------------------------------------------ * See if command parameter is present on the command line, * and if it is, return the pointer to its table entry. * * Arguments: * parm_str - parameter string to search for * * Return: * 0 - not present * pointer to parameter entry * ------------------------------------------------------------ */ CLI_ENTRY *get_parm_entry(char *parm_str) { CLI_ENTRY *pparm; boolean_t root_match; char local_str[MAX_LINE], *tmp_ptr; SNPRINTF(local_str, MAX_LINE, "%s", parm_str); root_match = (gpqual_root && !STRNCMP_STR(gpqual_root->name, local_str, MAX_OPT_LEN)); /* --------------------------------------- * search qualifier table for this option * --------------------------------------- */ if (!gpcmd_qual) return (NULL); if (NULL == strchr(local_str,'.')) pparm = find_cmd_param(local_str, gpcmd_qual, TRUE); else { tmp_ptr= strchr(local_str,'.'); /* there should be at least 1 character after the . */ assert (tmp_ptr + 1 < local_str + strlen(local_str)); *tmp_ptr = 0; pparm = find_cmd_param(local_str, gpcmd_qual, FALSE); if (pparm) pparm = find_cmd_param(tmp_ptr+1, pparm->qual_vals, FALSE); } if (root_match && !pparm) return (gpqual_root); else if (pparm) return (pparm); else return (NULL); } /* * ------------------------------------------------------------ * See if command parameter is present on the command line * * Arguments: * entry - parameter string to search for * * Return: * 0 - not present (i.e. CLI_ABSENT) * <> 0 - present (either CLI_PRESENT or CLI_NEGATED) * ------------------------------------------------------------ */ int cli_present(char *entry) { CLI_ENTRY *pparm; char local_str[MAX_LINE]; STRNCPY_STR(local_str, entry, SIZEOF(local_str) - 1); local_str[SIZEOF(local_str) - 1] = '\0'; cli_strupper(local_str); if (pparm = get_parm_entry(local_str)) { if (pparm->negated) return (CLI_NEGATED); if ((CLI_PRESENT == pparm->present) || (CLI_DEFAULT == pparm->present)) return (CLI_PRESENT); } return (CLI_ABSENT); } /* * ------------------------------------------------------------ * Get the command parameter value * * Arguments: * entry - parameter string to search for * val_buf - if parameter is present, it is copied to * this buffer. * * Return: * 0 - not present * <> 0 - ok * ------------------------------------------------------------ */ boolean_t cli_get_value(char *entry, char val_buf[]) { CLI_ENTRY *pparm; char local_str[MAX_LINE]; # ifdef DEBUG int ind, len; CLI_ENTRY *parm_vals; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # endif STRNCPY_STR(local_str, entry, MAX_LINE - 1); local_str[MAX_LINE - 1] = '\0'; cli_strupper(local_str); if (NULL == (pparm = get_parm_entry(local_str))) return (FALSE); if (!pparm->present || NULL == pparm->pval_str) return (FALSE); else { # ifdef DEBUG if (TREF(cli_get_str_max_len) && (NULL != (parm_vals = pparm->qual_vals))) { for (ind = 0; ; ind++) { len = strlen(parm_vals[ind].name); if (!len) break; /* Assert that we have allocated enough space to store all possible values for this parameter. * If this assert fails, fix caller of cli_get_str to allocate enough space. */ assert(len <= TREF(cli_get_str_max_len)); } } # endif STRNCPY_STR(val_buf, pparm->pval_str, MAX_LINE); val_buf[MAX_LINE - 1] = '\0'; } return (TRUE); } /* * -------------------------------------------------- * See if the qualifier given by 'entry' is negated * on the command line * * Return: * TRUE - Negated * FALSE - otherwise * -------------------------------------------------- */ boolean_t cli_negated(char *entry) /* entity */ { CLI_ENTRY *pparm; char local_str[MAX_LINE]; STRNCPY_STR(local_str, entry, SIZEOF(local_str) - 1); local_str[SIZEOF(local_str) - 1] = '\0'; cli_strupper(local_str); if (pparm = get_parm_entry(local_str)) return (pparm->negated); return (FALSE); } boolean_t cli_get_parm(char *entry, char val_buf[]) { char *gets_res, local_str[MAX_LINE], *sp; int eof, ind, match_ind, parm_len, res; mval prompt, dummy, *input_line; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ind = 0; assert(0 != gpcmd_parm_vals); STRNCPY_STR(local_str, entry, SIZEOF(local_str) - 1); local_str[SIZEOF(local_str) - 1] = '\0'; cli_strupper(local_str); match_ind = -1; while (0 < strlen(sp = (gpcmd_parm_vals + ind)->name)) /* implicit assignment intended */ { if (0 == (res = STRNCMP_STR(sp, local_str, MAX_OPT_LEN))) /* implicit assignment intended */ { if (-1 != match_ind) return (FALSE); else match_ind = ind; } else { if (0 < res) break; } ind++; } if (-1 != match_ind) { if (NULL == TAREF1(parm_ary, match_ind)) { if (!((gpcmd_parm_vals + match_ind)->parm_required)) /* Value not required */ return FALSE; /* If no value and required, prompt for it */ dummy.mvtype = dummy.str.len = 0; input_line = push_mval(&dummy); prompt.mvtype = MV_STR; prompt.str.addr = (gpcmd_parm_vals + match_ind)->prompt; for (prompt.str.len = 0, sp = prompt.str.addr; 0 != *sp; sp++, prompt.str.len++) ; op_write(&prompt); op_read(input_line, (mval *)&literal_notimeout); if (input_line->str.len) { parm_len = input_line->str.len; TAREF1(parm_str_len, match_ind) = parm_len + 1; GROW_HEAP_IF_NEEDED(match_ind); if (parm_len) memcpy(TAREF1(parm_ary, match_ind), input_line->str.addr, parm_len); *(TAREF1(parm_ary, match_ind) + parm_len) = '\0'; } else { /* No string was returned so create a real ghost to point to. Note that this should be revisited since this is NOT what should be happening. We should be returning FALSE here but need to instead return a null parm since current behaviors have a dependency on it SE 10/2003 */ TAREF1(parm_str_len, match_ind) = 1; GROW_HEAP_IF_NEEDED(match_ind); *TAREF1(parm_ary, match_ind) = '\0'; } } else if (-1 == *TAREF1(parm_ary, match_ind) && 1 == TAREF1(parm_str_len, match_ind)) return (FALSE); strcpy(val_buf, TAREF1(parm_ary, match_ind)); if (!cli_look_next_token(&eof) || (0 == cli_gettoken(&eof))) { TAREF1(parm_str_len, match_ind) = 1; GROW_HEAP_IF_NEEDED(match_ind); *TAREF1(parm_ary, match_ind) = -1; } else { parm_len = (int)(STRLEN(cli_token_buf) + 1); if (MAX_LINE < parm_len) { PRINTF("Parameter string too long\n"); FFLUSH(stdout); return (FALSE); } TAREF1(parm_str_len, match_ind) = parm_len; GROW_HEAP_IF_NEEDED(match_ind); memcpy(TAREF1(parm_ary, match_ind), cli_token_buf, parm_len); } } else { /* ----------------- * check qualifiers * ----------------- */ if (!cli_get_value(local_str, val_buf)) return (FALSE); } return (TRUE); } #ifdef GTM_TRIGGER int parse_triggerfile_cmd(void) { int res, cmd_ind; int opt_cnt; int eof; char cmd[] = "TRIGGER"; char *ptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; opt_cnt = 0; gpqual_root = 0; func = 0; TREF(parms_cnt) = 0; /* Parameters count */ *cli_err_str = 0; cmd_ind = find_verb(cmd); assert(-1 != cmd_ind); gpcmd_qual = cmd_ary[cmd_ind].qual_vals; gpcmd_parm_vals = NULL; gpcmd_verb = cmd_ary[cmd_ind].qual_vals; if (gpcmd_qual) clear_parm_vals(gpcmd_qual, TRUE); func = cmd_ary[cmd_ind].func; /* ---------------------- * Parse command options * ---------------------- */ do { res = parse_arg(gpcmd_qual, &eof); } while (1 == res); /* ------------------------------------- * If parse error, display error string * ------------------------------------- */ if (-1 == res) func = 0; else return (0); /* ------------------------- * If gettoken returned EOF * ------------------------- */ if (eof) return (EOF); else return (ERR_MUNOACTION); } #endif fis-gtm-V7.0-005/sr_unix/cli_parse.h0000755000032200000250000000427414342376327016173 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #ifndef CLI_PARSE_H_INCLUDED #define CLI_PARSE_H_INCLUDED #include "gtm_stdlib.h" void clear_parm_vals(CLI_ENTRY *cmd_parms, boolean_t follow); int find_entry(char *str, CLI_ENTRY *pparm); int find_verb(char *str); CLI_ENTRY *find_cmd_param(char *str, CLI_ENTRY *pparm, int follow); int parse_arg(CLI_ENTRY *pcmd_parms, int *eof); int parse_cmd(void); CLI_ENTRY *get_parm_entry(char *parm_str); boolean_t cli_get_parm(char *entry, char val_buf[]); boolean_t cli_numeric_check(CLI_ENTRY *pparm, char *val_str); boolean_t cli_get_sub_quals(CLI_ENTRY *pparm); int cli_check_negated(char **opt_str_ptr, CLI_ENTRY *pcmd_parm_ptr, CLI_ENTRY **pparm_ptr); #ifdef GTM_TRIGGER int parse_triggerfile_cmd(void); #endif /** This was originally a macro, but was changed to an inline to make veracode * checking easier. Also, it originally directly set a passed in destination pointer * and it now returns a pointer value instead. * Function to copy a source string to a malloced area that is set to the destination pointer. * Since it is possible that any destination might have multiple pointer dereferences in its usage, we * use a local pointer variable and finally return its value for assignment thereby avoiding duplication of * those pointer dereferences (one for the malloc and one for the strcpy). * * @param src Source string to copy * @return Pointer to the newly malloced copy of the source */ static inline char *malloc_cpy_str(char *src) { char *mcs_ptr; size_t slen; size_t mcs_len; slen = strlen(src); mcs_len = (slen < SIZE_MAX) ? slen + 1 : SIZE_MAX; /* (theoritical) overflow guard */ mcs_ptr = malloc(mcs_len); assert(mcs_ptr); memcpy(mcs_ptr, src, mcs_len); return(mcs_ptr); } #endif fis-gtm-V7.0-005/sr_unix/cmidefsp.h0000755000032200000250000002015514342376327016020 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef CMIDEFSP_H_INCLUDED #define CMIDEFSP_H_INCLUDED #define GTCM_SERVER_NAME "gtcm_gnp_server" /* Service provider - for getservbyname */ #define GTCM_SERVER_PROTOCOL "tcp" /* Service provider - for getservbyname */ #define MAX_CONN_IND 5 /* Max. simultaneous conn. requests for listen */ #define CMM_MIN_PEER_LEVEL "200" /* * Reason codes -> cooresponds to MSG$ codes * * It is anticipated that a VAX implementation would * just defined the CMI_REASON_* to be the MSG$* codes * */ error_def(CMI_REASON_DISCON); error_def(CMI_REASON_ABORT); error_def(CMI_REASON_EXIT); error_def(CMI_REASON_PATHLOST); error_def(CMI_REASON_PROTOCOL); error_def(CMI_REASON_THIRDPARTY); error_def(CMI_REASON_TIMEOUT); error_def(CMI_REASON_NETSHUT); error_def(CMI_REASON_REJECT); error_def(CMI_REASON_OVERRUN); error_def(CMI_REASON_STATUS); error_def(CMI_REASON_CONFIRM); #define CMI_REASON_IODONE 0 #define CMI_REASON_INTMSG 1 #define CMI_REASON_CONNECT 2 #define CMI_IO_WOULDBLOCK(iostatus) (EWOULDBLOCK == (iostatus) || EAGAIN == (iostatus)) typedef que_head relque; typedef int4 cmi_status_t; typedef uint4 cmi_reason_t; typedef int cmi_unit_t; /* For UNIX just use an mstr */ typedef mstr cmi_descriptor; #define CMI_DESC_LENGTH(DESC) ((unsigned)(DESC)->len) #define CMI_DESC_SET_LENGTH(DESC, LEN) (DESC)->len = (LEN) #define CMI_DESC_POINTER(DESC) ((DESC)->addr) #define CMI_DESC_SET_POINTER(DESC,PTR) (DESC)->addr = (PTR) #define CMI_DESC_CONST(DESC,LIT) (DESC)->len = strlen(LEN);(DESC)->addr = (LIT) #define CMI_DESC_DEF(DESC,STR,LEN) (DESC)->len = (LEN);(DESC)->addr = (STR) #include #include "gtm_unistd.h" #include "gtm_netdb.h" #include "gtm_socket.h" /* for sockaddr_storage */ #include "gtm_signal.h" typedef struct { unsigned short xfer_count; /* length of currently processed packet * only valid on read if len_len is 2 */ int len_len; /* used to track length byte I/O */ union { /* buffer for read/write of length */ unsigned short len; /* Total length of the packet. IMPORTANT : length of a packet is always sent in network byte */ char lenbuf[2]; /* order. We maintain len in network byte order until the length bytes are successfully sent. */ } u; /* Thereafter we convert len back to host byte order to correctly track bytes yet to be sent. */ } qio_iosb; typedef struct clb_stat_struct { struct { uint4 msgs,errors,bytes,last_error; } read,write; } clb_stat; /* * Individual link connections doubly linked list */ #include "gtm_inet.h" #ifdef __MVS__ /* need fd_set */ #include #endif struct CLB { que_ent cqe; /* forward/backward links */ struct NTD *ntd; /* back pointer to ntd */ cmi_descriptor nod; /* node */ cmi_descriptor tnd; /* taskname */ struct sockaddr_storage peer_sas; /* peer */ struct addrinfo peer_ai; /* peer */ int mun; /* endpoint's file descriptor */ void *usr; /* client specific storage */ qio_iosb ios; /* used for tracking inprocess I/O */ unsigned short cbl; /* number of bytes read */ unsigned short mbl; /* max size buffer */ unsigned char *mbf; /* pointer to buffer */ unsigned char urgdata; /* buffer for urgent data */ int fd_async; /* TRUE --> fd is in async mode */ int deferred_event; /* TRUE --> deferred event signaled */ cmi_reason_t deferred_reason; /* reason for deferred event */ cmi_status_t deferred_status; /* status for deferred event */ int sta; /* CM_ state */ int prev_sta; /* CM_ state before URG WRITE, to be restored after URG WRITE completes */ void (*err)(struct CLB *); /* link status error callback - not used */ void (*ast)(struct CLB *); /* I/O call back for async read/write */ struct clb_stat_struct stt; }; typedef struct CLB clb_struct; struct NTD { que_ent cqh; /* queue of open CLB's */ que_ent cqh_free; /* free list of clbs */ boolean_t freelist_dirty; /* TRUE --> a clb has been inserted */ /* exception callback */ void (*err)(struct NTD*, struct CLB *, cmi_reason_t); /* link status error callback */ void (*crq)(struct CLB *); /* connect request callback */ bool (*acc)(struct CLB *); /* accept connect callback */ /* callback for urgent data */ void (*urg)(struct CLB *, unsigned char data); void (*trc)(struct CLB *,int, unsigned char *, size_t); int listen_fd; /* Server's listen file descriptor */ fd_set rs,ws,es; /* select I/O sets */ int max_fd; /* largest fd - for select processing*/ VSIG_ATOMIC_T sigio_interrupt; VSIG_ATOMIC_T sigurg_interrupt; /* for mutex processing - use mutex_set set to block. * mutex_set - used in sigprocmask to block * used in sigsuspend, sigprocmask to restore to prior */ sigset_t mutex_set; /* set of signals to block */ size_t pool_size; /* cmi_init specified pool size */ /* This is zero for clients */ size_t free_count; /* # of items on free list */ size_t usr_size; /* usr size from cmi_init */ size_t mbl; /* initial mbf from cmi_init */ }; #include "iosp.h" #define RELQUE2PTR(X) (void_ptr_t)(((unsigned char *) &(X)) + ((int4) (X))) #define CMI_ERROR(s) ((s) != SS_NORMAL) #define CMI_CLB_IOSTATUS(c) ((c)->deferred_status) #define CMI_CLB_ERROR(c) (CMI_ERROR(CMI_CLB_IOSTATUS(c))) #define CMI_MAKE_STATUS(s) (s) #define CMI_MUTEX_DECL(RC) sigset_t cmi_oset_prev; int RC #define CMI_MUTEX_BLOCK(RC) SIGPROCMASK(SIG_BLOCK, &ntd_root->mutex_set, &cmi_oset_prev, RC) #define CMI_MUTEX_RESTORE(RC) SIGPROCMASK(SIG_SETMASK, &cmi_oset_prev, NULL, RC) /* All TCP/IP GNP messages have a 2 byte length before the message itself */ #define CMI_TCP_PREFIX_LEN 2 #define ALIGN_QUAD /**/ void cmi_dprint(char *cs, ...); #ifdef DEBUG GBLREF int cmi_debug_enabled; #define CMI_DPRINT(x) if (cmi_debug_enabled) cmi_dprint x; else; #else #define CMI_DPRINT(x) /**/ #endif #define CM_URGDATA_OFFSET 5 #define CM_URGDATA_LEN 1 #define cmi_write_int(clbp) cmi_write_urg((clbp), *(clbp)->mbf) #define CMI_IDLE(milliseconds) cmi_idle((milliseconds)) /* UNIX specific cmj_ routines */ cmi_status_t cmj_set_async(int fd); int cmj_reset_async(int fd); cmi_status_t cmj_clb_set_async(struct CLB *lnk); cmi_status_t cmj_clb_reset_async(struct CLB *lnk); void cmj_err(struct CLB *lnk, cmi_reason_t reason, cmi_status_t status); void cmj_exception_interrupt(struct CLB *lnk, int signo); void cmj_fini(struct CLB *lnk); int cmj_firstone(fd_set *s, int n); struct CLB *cmj_getdeferred(struct NTD *tsk); void cmj_handler(int signo, siginfo_t *info, void *context); void cmj_housekeeping(void); void cmj_incoming_call(struct NTD *tsk); cmi_status_t cmj_netinit(void); cmi_status_t cmj_postevent(struct CLB *lnk); cmi_status_t cmj_read_start(struct CLB *lnk); void cmj_read_interrupt(struct CLB *lnk, int signo); void cmj_select(int signo); cmi_status_t cmj_setupfd(int fd); struct CLB *cmj_unit2clb(struct NTD *tsk, cmi_unit_t unit); cmi_status_t cmj_write_start(struct CLB *lnk); cmi_status_t cmj_write_urg_start(struct CLB *lnk); void cmj_write_interrupt(struct CLB *lnk, int signo); void cmj_init_clb(struct NTD *tsk, struct CLB *lnk); cmi_status_t cmj_getsockaddr(cmi_descriptor *nod, cmi_descriptor *tnd, struct addrinfo **ai_ptr); cmi_status_t cmi_write_urg(struct CLB *c, unsigned char data); cmi_status_t cmi_init( cmi_descriptor *tnd, unsigned char tnr, void (*err)(struct NTD *ntd, struct CLB *c, cmi_reason_t reason), void (*crq)(struct CLB *c), bool (*acc)(struct CLB *c), void (*urg)(struct CLB *c, unsigned char data), size_t pool_size, size_t usr_size, size_t mbl); void cmi_idle(uint4 hiber); void cmi_free_clb(struct CLB *c); struct CLB *cmi_alloc_clb(void); unsigned char *cmi_realloc_mbf(struct CLB *lnk, size_t new_size); void cmi_peer_info(struct CLB *lnk, char *buf, size_t sz); #endif fis-gtm-V7.0-005/sr_unix/cmistub.c0000755000032200000250000000123114342376327015661 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "cmidef.h" cmi_status_t cmi_read(struct CLB *lnk) { assert(FALSE); return SS_NORMAL; } cmi_status_t cmi_write(struct CLB *lnk) { assert(FALSE); return SS_NORMAL; } fis-gtm-V7.0-005/sr_unix/cms_load.csh0000755000032200000250000003003414342376327016332 0ustar librarygtc#! /usr/local/bin/tcsh ################################################################# # # # Copyright (c) 2001-2020 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. # # # ################################################################# # This file is used by package.csh and must be modified prior # to deploying to a new platform if ("$1" == "-m") then set mods_only=1 shift else set mods_only=0 endif if( $# != 2 ) then echo "" echo " Usage: $0 [-m] " echo "" exit 5 endif set cms_dir = $1 set dst_dir = $2 set dst_top_dir = $dst_dir:h set dst_ver = $dst_dir:t set cms_ver = $cms_dir:t unalias cp chmod mv ls grep alias ls 'echo "ls \!:* 2>/dev/null" | sh' # we want to redirect only stderr to /dev/null; can't do that in tcsh. alias cp 'cp -f \!:* >& /dev/null' # some copies may be null copies. we dont want error messages coming out. alias mv 'mv -f \!:* >& /dev/null' # some moves may be null moves. we dont want error messages coming out. alias chmod 'chmod \!:* >& /dev/null' # some chmods may be null chmods. we dont want error messages coming out. set platform_name = `uname | sed 's/-//g' | sed 's=/==g' | tr '[A-Z]' '[a-z]'` set build_types = "pro dbg bta" set build_dirs = "map obj" set dir_structure = "inc pct src tools log $build_types" ############# Define mapping between file-types and directory-name ################## set gtm_src_types = "c m64 s msg" set gtm_inc_types = "h max mac si" set gtm_pct_types = "mpt m hlp" set gtm_tools_types = "gtc sed awk sh csh list txt exp mk ksh cmake tab in xc" ##################################################################################### if !(-e $cms_dir) then echo "$cms_dir doesn't exist. Exiting..." exit 1 endif if !(-e $dst_top_dir) then echo "$dst_top_dir doesn't exist. Exiting..." exit 1 endif set preserve_time = "-p" # while doing the copy, let us preserve time by default if ("os390" == "$platform_name") then set preserve_time = "-m" # only preserve times endif cd $dst_top_dir if (-e $dst_ver) then foreach image (pro bta dbg) if (-e $gtm_root/$dst_ver/$image/gtmsecshr) then $gtm_com/IGS $gtm_root/$dst_ver/$image/gtmsecshr "STOP" # stop gtmsecshr in case it is running endif if (-d $gtm_root/$dst_ver/$image/utf8/gtmsecshrdir) then $gtm_com/IGS $gtm_root/$dst_ver/$image/utf8/gtmsecshr "STOP" endif end # Verify if anybody is using this version before deleting if ($platform_name == "linux") then set psopt = "-ef --width 300" # to get more screen output have a 300 column screen else set psopt = "-ef" endif /bin/ps $psopt | grep "$dst_top_dir/$dst_ver/" | grep -vE "grep|$0" >& /dev/null if ($status == 0) then # This check does not cover all cases of usage. There is still a window where new processes might start. # But, this is better than not checking at all. echo "Following processes are still using $dst_ver; not deleting $dst_top_dir/$dst_ver" /bin/ps $psopt | grep "$dst_top_dir/$dst_ver/" | grep -vE "grep|$0" exit 1 endif if ($dst_ver =~ V3* || $dst_ver =~ V4* || $dst_ver =~ V5* || $dst_ver =~ V6* || $dst_ver == "V990") then set move_args = "compulsory" endif # to use this script to update released versions, you must # copy this script to your own directtory and change the following IF statement to: (!($mods_only) && $?move_args) # execute the updated script as library with the following command, replacing VER as necessary # doall -server "all" -fg -run '~/cms_load.csh -m $cms_root/${VER} $gtm_root/${VER} |& tee -a ~/logs/${VER}_${HOST}.log' if ($?move_args) then set save_ver = `ls -ld ${gtm_root}/$dst_ver | \ awk '{if (length($7)==1) $7="0"_$7; time=$6"_"$7"_"$8; print toupper(time)}' | sed 's/://g'` echo "# Renaming ${gtm_root}/${dst_ver} to ${gtm_root}/${dst_ver}_${save_ver}" mv ${gtm_root}/$dst_ver ${gtm_root}/${dst_ver}_${save_ver} else if (! $mods_only) then echo "# Deleting existing $dst_dir directory structure" foreach image (pro bta dbg) # remove root-owned gtmsecshr* files/dirs if (-e $gtm_root/$dst_ver/$image/gtmsecshrdir) then $gtm_com/IGS $gtm_root/$dst_ver/$image/gtmsecshr "RMDIR" endif if (-d $gtm_root/$dst_ver/$image/utf8/gtmsecshrdir) then $gtm_com/IGS $gtm_root/$dst_ver/$image/utf8/gtmsecshr "RMDIR" endif end rm -rf $dst_ver if ($status != 0) then exit $status endif else echo "# Updating $dst_dir directory structure" endif endif ############## Create $dst_dir and subdirectories ################## if (! -e $dst_ver) then echo "# Creating -------> $dst_dir Directory Structure ..." mkdir -p $dst_ver if ($status != 0) then exit $status endif cd $dst_ver set gtm_ver = `pwd` if ($status != 0) then exit $status endif mkdir $dir_structure {`echo $build_types | sed 's/ /,/g'`}/{`echo $build_dirs | sed 's/ /,/g'`} if ($status != 0) then exit $status endif else set gtm_ver = ${dst_dir} endif cd $gtm_ver cp $preserve_time $cms_dir/*/gtmsrc.csh . ############### Define platform-specific libraries ################################## # if you add a platform or a platform specific directory below you must modify # tools/btc_tools/cms_cshrc.csh # tools/work_tools/get_lib_dirs.csh # CMakeLists.txt / sr_*/platform.cmake # # platform ordering goes: # platform+OS arch arch_common OS {portable,nsb_portable} # The extra spaces at the end are required for override_libs to work correctly set gtm_s_aix = "sr_port sr_port_cm sr_unix sr_unix_cm sr_unix_gnp sr_rs6000 sr_aix " set gtm_s_osf1 = "sr_port sr_port_cm sr_unix sr_unix_cm sr_unix_gnp sr_alpha sr_dux " set gtm_s_hpux = "sr_port sr_port_cm sr_unix sr_unix_cm sr_unix_gnp sr_hppa sr_hpux " set gtm_s_linux = "sr_port sr_port_cm sr_unix sr_unix_nsb sr_unix_cm sr_unix_gnp sr_x86_regs sr_i386 sr_linux " set gtm_s_linux64 = "sr_port sr_port_cm sr_unix sr_unix_cm sr_unix_gnp sr_x86_regs sr_x86_64 sr_linux " set gtm_s_sunos = "sr_port sr_port_cm sr_unix sr_unix_cm sr_unix_gnp sr_sparc sr_sun " set gtm_s_os390 = "sr_port sr_port_cm sr_unix sr_unix_cm sr_unix_gnp sr_s390 sr_os390 " set gtm_s_l390 = "sr_port sr_port_cm sr_unix sr_unix_cm sr_unix_gnp sr_linux sr_s390 sr_l390 " set gtm_s_hpia = "sr_port sr_port_cm sr_unix sr_unix_cm sr_unix_gnp sr_hpux sr_ia64 " set gtm_s_linuxia = "sr_port sr_port_cm sr_unix sr_unix_cm sr_unix_gnp sr_linux sr_ia64 " set gtm_s_cygwin = "sr_port sr_port_cm sr_unix sr_unix_nsb sr_unix_cm sr_unix_gnp sr_x86_regs sr_i386 sr_linux " set platform_library = "$platform_name" if ( "s390x" == $MACHTYPE && "linux" == $platform_library ) then set platform_library = "l390" endif if ( "z/OS" == $MACHTYPE ) then set platform_library = "os390" endif set mach_type = `uname -m` if ( "ia64" == $mach_type && "hpux" == $platform_library ) then set platform_library = "hpia" endif if ( "ia64" == $mach_type && "linux" == $platform_library ) then set platform_library = "linuxia" endif if ( "x86_64" == $mach_type && "linux" == $platform_library ) then if ( $?OBJECT_MODE ) then if ( $OBJECT_MODE != "32" ) then set platform_library = "linux64" endif else set platform_library = "linux64" endif endif ########### Copy sources from platform-specific directories into appropriate version-subdirectories ############ cd $cms_dir echo "# Copying files from $cms_dir" set ref_libs = `set | grep "^gtm_s_${platform_library}[ ]" | sed 's/^gtm_s_'${platform_library}'[ ][ ]*//g'` foreach ref_library ( $ref_libs ) if ( -d $ref_library ) then set override_libs=`set | grep "^gtm_s_${platform_library}" | sed "s/.*$ref_library //"` # echo "Override_libs for $ref_library are $override_libs" cd $ref_library foreach dir (src inc pct tools) foreach ftype (`set | grep "^gtm_${dir}_types[ ]" | sed 's/^gtm_'$dir'_types[ ][ ]*//g'`) set nfiles = `\ls -1 | grep "\.$ftype"'$' | wc -l | sed 's/^[ ]*//g'` if ($nfiles != 0) then if ($mods_only == 0) then echo "# Copying $nfiles files of type .$ftype from $ref_library to ${gtm_ver}/${dir}" \ls -1 | grep "\.$ftype"'$' | xargs -i cp -f $preserve_time {} $gtm_ver/${dir} else # @ n_modfiles=0 foreach srcfile (*.$ftype) if ("" != "${override_libs}") then set override_exists=0 foreach override_lib ($override_libs) if (-f ../${override_lib}/$srcfile) then # echo "Override for ${ref_library}/${srcfile} found in # ${override_lib}" set override_exists=1 break endif end if ($override_exists) then continue # on to the next file endif endif set dstfile="${srcfile}" if ($srcfile:e == "mpt") then set dstfile="_$srcfile:r.m" # echo "$srcfile is mpt, comparing with $dstfile" endif if ($srcfile == "release_name.h") then # echo "Skipping release_name.h" continue # assume up-to-date release_name.h endif if (! { cmp -s ${srcfile} ${gtm_ver}/${dir}/${dstfile} } ) then echo "# Copying differing $srcfile from $ref_library to $gtm_ver/${dir}" cp -f $preserve_time $srcfile $gtm_ver/${dir} # @ n_modfiles++ endif end # echo "Copied $n_modfiles out of $nfiles files of type .$ftype from $ref_library to # ${gtm_ver}/${dir}" endif endif endif set nfiles=`ls -1 *.${ftype}nix | wc -l | sed 's/^[ ]*//g'` if ($nfiles != 0) then echo "# Restoring $nfiles NIXed files of type .$ftype in directory ${gtm_ver}/${dir}" ls -1 *.${ftype}nix |\ awk '{printf "cp -f $preserve_time %s %s/%s\n", $1, '\"${gtm_ver}/${dir}\"', $1}' |\ sed 's/nix$//g' | sh endif end end cd .. else echo "# Skipping missing library $ref_library" endif end cp sr_unix_cm/makefile* $gtm_ver/tools ########### Copy files from tools repo to $dst_dir/tools ############ if (! $?gtmpcat_dir && -d $ggtools/gtmpcat_tools/gtmpcat) then set gtmpcat_dir=$ggtools/gtmpcat_tools/gtmpcat endif if ($?gtmpcat_dir) then echo "# Copying gtmpcat related files from $gtmpcat_dir" cp $gtmpcat_dir/gtmpcat{{,fldbld}.m,_{field_def,sh}.txt} ${gtm_root}/${dst_ver}/tools endif ######################## Rename .mpt files to _*.m files ####################### if ($mods_only == 0) then echo "# Renaming .mpt files to _*.m in $gtm_ver/pct" cd $gtm_ver/pct ls -1 *.mpt | awk '{printf "mv %s _%s\n", $1, $1}' | sed 's/mpt$/m/g' | sh ######################## Convert EBCDIC files to ASCII ####################### if ("OS/390" == $HOSTOS) then cd .. mv pct pctebc mkdir pct cd pctebc foreach file (*) iconv -T -f IBM-1047 -t ISO8859-1 $file > $gtm_ver/pct/$file if (0 != $status) then echo "# Error converting $file (with iconv) -- return status: $status" endif touch -r $file $gtm_ver/pct/$file end cd $gtm_ver/pct endif ######################## Edit release_name.h #################################### echo "# Modifying release_name.h" $btc_tools/edrelnam.csh $dst_ver # Do we care if this fails? endif ############## Set appropriate permissions on the files. For comments see $gtm_tools/comlist.csh ############## set gtm_verno = $gtm_ver:t switch ($gtm_verno) case "V990": set chmod_protect = 1 breaksw case "V9*": set chmod_protect = 0 breaksw default: set chmod_protect = 1 breaksw endsw if ($chmod_protect == 1) then set chmod_conf = 755 set chmod_src = 444 else set chmod_conf = 775 set chmod_src = 664 endif chmod 755 $gtm_ver cd $gtm_ver chmod $chmod_conf bta dbg pro inc pct src tools gtmsrc.csh chmod 775 log cd $gtm_ver/inc chmod $chmod_src * cd $gtm_ver/pct chmod $chmod_src * cd $gtm_ver/src /bin/ls | xargs -n25 chmod $chmod_src cd $gtm_ver/tools chmod $chmod_src * if ($chmod_protect} == 1 ) then chmod 555 *sh else chmod 775 *sh endif echo "" echo "Done" echo "" exit 0 fis-gtm-V7.0-005/sr_unix/cms_load_pre_v42.csh0000755000032200000250000001634314342376327017702 0ustar librarygtc#!/usr/local/bin/tcsh ################################################################# # # # Copyright (c) 2001-2018 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. # # # ################################################################# if( $# != 2 ) then echo "" echo " Usage: $0 " echo "" exit 5 endif set cms_dir = $1 set dst_dir = $2 set dst_top_dir = $dst_dir:h set dst_ver = $dst_dir:t set cms_ver = $cms_dir:t if (-x $cms_dir/sr_unix/cms_load.csh) then $cms_dir/sr_unix/cms_load.csh $1 $2 exit $status endif set platform_name = `uname | sed 's/-/_/g' | tr '[A-Z]' '[a-z]'` unalias cp chmod mv ls grep alias ls 'echo "ls \!:* 2>/dev/null" | sh' # we want to redirect only stderr to /dev/null; can't do that in tcsh. alias cp 'cp -f \!:* >& /dev/null' # some copies may be null copies. we dont want error messages coming out. alias mv 'mv -f \!:* >& /dev/null' # some moves may be null moves. we dont want error messages coming out. alias chmod 'chmod \!:* >& /dev/null' # some chmods may be null chmods. we dont want error messages coming out. if ($platform_name == "sunos") then alias grep /usr/xpg4/bin/grep # for -E option to work on sparky endif set build_types = "pro dbg bta" set build_dirs = "map obj" set dir_structure = "inc pct src tools log $build_types" set gtcm_dir_structure = "inc src tools log $build_types" # exclude pct for gtcm set s_unix_cm = "sr_port_cm sr_unix_cm" ############# Define mapping between file-types and directory-name ################## set gtm_src_types = "c m64 s msg" set gtm_inc_types = "h max mac si" set gtm_pct_types = "mpt m hlp" set gtm_tools_types = "gtc sed awk sh csh list txt" ##################################################################################### if !(-e $cms_dir) then echo "CMS dir $cms_dir doesn't exist. Exiting..." exit 1 endif if !(-e $dst_top_dir) then echo "Destination dir $dst_top_dir doesn't exist. Exiting..." exit 1 endif cd $dst_top_dir if (-e $dst_ver) then # Confirm deletion of released directories set delete = 1 if ($dst_ver =~ V3* || $dst_ver =~ V4*) then echo "Not deleting $dst_top_dir/$dst_ver. Exiting..." exit 1 endif # Verify if anybody is using this version before deleting if ($platform_name == "linux") then set psopt = "-ef --width 300" # to get more screen output have a 300 column screen else set psopt = "-ef" endif /bin/ps $psopt | grep "$dst_top_dir/$dst_ver" | grep -vE "grep|$0" >& /dev/null if ($status == 0) then # This check does not cover all cases of usage. There is still a window where new processes might start. # But, this is better than not checking at all. echo "Following processes are still using $dst_ver; not deleting $dst_top_dir/$dst_ver" /bin/ps $psopt | grep "$dst_top_dir/$dst_ver" | grep -vE "grep|$0" exit 1 endif echo "Deleting existing $dst_dir directory structure" rm -rf $dst_ver if ($status != 0) then exit $status endif endif ############## Create $dst_dir and subdirectories ################## echo "Creating -------> $dst_dir Directory Structure ..." mkdir $dst_ver if ($status != 0) then exit $status endif cd $dst_ver set gtm_ver = `pwd` mkdir gtcm if ($status != 0) then exit $status endif foreach dir (. gtcm) cd $dir if ($dir == "gtcm") then mkdir $gtcm_dir_structure {`echo $build_types | sed 's/ /,/g'`}/{`echo $build_dirs | sed 's/ /,/g'`} else mkdir $dir_structure {`echo $build_types | sed 's/ /,/g'`}/{`echo $build_dirs | sed 's/ /,/g'`} endif if ($status != 0) then exit $status endif end cd $gtm_ver cp $cms_dir/*/gtmsrc.csh . ############### Define platform-specific libraries ################################## set gtm_s_aix = "sr_port sr_unix sr_rs6000 sr_aix" set gtm_s_osf1 = "sr_port sr_unix sr_alpha sr_dux" set gtm_s_hp_ux = "sr_port sr_unix sr_hppa sr_hpux" set gtm_s_linux = "sr_port sr_unix sr_i386 sr_linux" set gtm_s_sunos = "sr_port sr_unix sr_sparc sr_sun" set gtm_s_os390 = "sr_port sr_unix sr_s390 sr_os390" ########### Copy sources from platform-specific directories into appropriate version-subdirectories ############ cd $cms_dir set ref_libs = `set | grep "^gtm_s_${platform_name}[ ]" | sed 's/^gtm_s_'${platform_name}'[ ][ ]*//g'` set gtm_tver = $gtm_ver foreach ref_library ( $ref_libs $s_unix_cm) if ($ref_library =~ *_cm) then set gtm_ver = $gtm_tver/gtcm endif cd $ref_library foreach dir (src inc pct tools) foreach ftype (`set | grep "^gtm_${dir}_types[ ]" | sed 's/^gtm_'$dir'_types[ ][ ]*//g'`) set nfiles=`ls -1 *.$ftype | wc -l | sed 's/^[ ]*//g'` if ($nfiles != 0) then echo "Copying $nfiles files of type .$ftype from $ref_library to ${gtm_ver}/${dir}" cp *.$ftype $gtm_ver/${dir} endif set nfiles=`ls -1 *.${ftype}nix | wc -l | sed 's/^[ ]*//g'` if ($nfiles != 0) then echo "Restoring $nfiles NIXed files of type .$ftype in directory ${gtm_ver}/${dir}" ls -1 *.${ftype}nix | awk '{printf "cp -f %s %s/%s\n", $1, '\"${gtm_ver}/${dir}\"', $1}' | sed 's/nix$//g' | sh endif end end cd .. end set gtm_ver = $gtm_tver # get gtm_ver back to proper value once gtcm copies are done # ################ Do extra population for GT.CM directories ######################## echo "Copying makefiles for GTCM" cd $cms_dir/sr_unix_cm set gtm_ver = $gtm_ver/gtcm foreach dir ($build_types) cp makefile $gtm_ver/$dir end cp makefile gtcm_gcore $gtm_ver/tools cp makefile.* $gtm_ver/tools set gtm_ver = $gtm_ver:h ######################## Rename .mpt files to _*.m files ####################### echo "Renaming .mpt files to _*.m in $gtm_ver/pct" cd $gtm_ver/pct ls -1 *.mpt | awk '{printf "mv %s _%s\n", $1, $1}' | sed 's/mpt$/m/g' | sh ######################## Edit release_name.h #################################### if ($cms_ver != $dst_ver) then echo "Modifying release_name.h" $btc_tools/edrelnam.csh $dst_ver # Do we care if this fails? endif ############## Set appropriate permissions on the files. For comments see $gtm_tools/comlist.csh ############## set gtm_verno = $gtm_ver:t switch ($gtm_verno) case "V990": set chmod_protect = 1 breaksw case "V9*": set chmod_protect = 0 breaksw default: set chmod_protect = 1 breaksw endsw if ($chmod_protect == 1) then set chmod_conf = 755 set chmod_src = 444 else set chmod_conf = 775 set chmod_src = 664 endif chmod 775 $gtm_ver cd $gtm_ver chmod $chmod_conf bta dbg pro inc pct src tools gtmsrc.csh chmod 775 log cd $gtm_ver/inc chmod $chmod_src * cd $gtm_ver/pct chmod $chmod_src * cd $gtm_ver/src /bin/ls | xargs -n25 chmod $chmod_src cd $gtm_ver/tools chmod $chmod_src * if ($chmod_protect} == 1 ) then chmod 555 *sh else chmod 775 *sh endif if (-d $gtm_ver/gtcm) then chmod $chmod_conf $gtm_ver/gtcm cd $gtm_ver/gtcm chmod $chmod_conf bta dbg pro inc src tools cd $gtm_ver/gtcm/inc chmod $chmod_src * cd $gtm_ver/gtcm/src chmod $chmod_src * cd $gtm_ver/gtcm/tools chmod $chmod_src * endif echo "" echo "Done" echo "" exit 0 fis-gtm-V7.0-005/sr_unix/comimage.csh0000755000032200000250000001006014342376327016327 0ustar librarygtc#!/usr/local/bin/tcsh ################################################################# # # # Copyright (c) 2001-2019 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. # # # ################################################################# #################################################################### # # comimage.csh - submit background task to compile & link GT.M # # comimage.csh is an interactive script to compile & link # (build) a release of GT.M. It prompts the user for 4 # parameters: # version - version to be built (default: current) # images - type of images (default: current) # C compiler options in addition to defaults # assembler options in addition to defaults # #################################################################### # # Get the version number/designation: echo " " echo -n "Enter Version " echo -n "[$gtm_verno]: " set comimage_ver = $< if ( "$comimage_ver" == "" ) then set comimage_ver = $gtm_verno endif echo " " # Get the image type: if ( "$gtm_exe" == "" ) then set comimage_image = "p" else # Convert current image type to single-character prompt set comimage_image = `basename $gtm_exe` switch ( $comimage_image ) case "b*": set comimage_image = "b" breaksw case "d*": set comimage_image = "d" breaksw case "p*": default: set comimage_image = "p" breaksw endsw endif echo -n "Enter Image " echo -n "[$comimage_image]: " set comimage_image_input = $< if ( "$comimage_image_input" != "" ) then set comimage_image = $comimage_image_input endif echo " " # Convert to name and set default compiler and assembler options. # N.B.: These default options must be calculated the same way gtm_env.csh and gtm_env_sp.csh calculate them. switch ( $comimage_image ) case "[Bb]*": set comimage_image = "bta" set comimage_as_options_default = \ "$gt_as_options_common $gt_as_option_I $gt_as_option_DDEBUG $gt_as_option_optimize" set comimage_cc_options_default = \ "$gt_cc_options_common $gt_cc_option_I $gt_cc_option_DDEBUG $gt_cc_option_optimize" breaksw case "[Dd*]": set comimage_image = "dbg" set comimage_as_options_default = \ "$gt_as_options_common $gt_as_option_I $gt_as_option_DDEBUG $gt_as_option_debug $gt_as_option_nooptimize" set comimage_cc_options_default = \ "$gt_cc_options_common $gt_cc_option_I $gt_cc_option_DDEBUG $gt_cc_option_debug $gt_cc_option_nooptimize" breaksw case "[Pp]*": set comimage_image = "pro" set comimage_as_options_default = \ "$gt_as_options_common $gt_as_option_I $gt_as_option_optimize" set comimage_cc_options_default = \ "$gt_cc_options_common $gt_cc_option_I $gt_cc_option_optimize" breaksw endsw # Get assembler options: echo "Enter additional assembler options" echo " [default: $comimage_as_options_default]" echo -n ' --> ' set comimage_as_options_extra = "$<" if ( "$comimage_as_options_extra" != "" ) then echo " [new: $comimage_as_options_default $comimage_as_options_extra]" endif echo " " # Get C compiler options: echo "Enter additional C compiler options" echo " [default: $comimage_cc_options_default]" echo -n ' --> ' set comimage_cc_options_extra = "$<" if ( "$comimage_cc_options_extra" != "" ) then echo " [new: $comimage_cc_options_default $comimage_cc_options_extra]" endif echo " " set setactive_parms = ( $comimage_ver $comimage_image ) ; source $gtm_tools/setactive.csh if ( ! -d $gtm_ver/log ) then mkdir $gtm_ver/log chmod 775 $gtm_ver/log endif nohup /usr/local/bin/tcsh $gtm_tools/comlist.csh \ "$comimage_as_options_extra" "$comimage_cc_options_extra" "gtm_$comimage_image" "$comimage_ver" \ >& $gtm_ver/log/comlist.$comimage_image.log < /dev/null & unset comimage_as_options_default unset comimage_as_options_extra unset comimage_cc_options_default unset comimage_cc_options_extra unset comimage_image unset comimage_image_input unset comimage_ver fis-gtm-V7.0-005/sr_unix/comlist.csh0000755000032200000250000006465314342376327016241 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2021 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. # # # ################################################################# # ##################################################################################### # # comlist.csh - compile all sources, create object libraries, link executables. # Should be kept in sync with similar code in mkutf8dir.csh # # arguments: # $1 - assembler options # $2 - C compiler options # $3 - "gtm_bta" => build bta images ($gtm_ver/bta) # "gtm_dbg" => build dbg images ($gtm_ver/dbg) # "gtm_pro" => build pro images ($gtm_ver/pro) # $4 - version number (without punctuation) or code letter: # e.g., "V123" => version "V1.2-3" or: # "a" => current active (in current process) release # "d" => current development release # "p" => current production release # ##################################################################################### echo "Start of $0 `date`" echo "" echo "Built on $HOST" echo "" echo "arguments: '$1' '$2' '$3' '$4'" echo "" source $gtm_tools/gtm_env.csh if (! $?gtm_curpro) then # The tools and test system relies on $gtm_curpro defined in the environment for greater flexibility # Some day we need to eliminate versions.csh and expect $gtm_curpro to be defined in the environment. # setenv gtm_curpro `source $gtm_tools/versions.csh ; echo $gtm_curpro` echo 'COMLIST-E-CURPRO: gtm_curpro not defined. comlist expects gtm_curpro to be defined and exist in $gtm_root' exit 1 endif echo "-------------------------" echo "locale output at start" echo "-------------------------" locale echo "" echo "Setting locale and LANG to C unconditionally" unsetenv gtm_chset setenv LC_ALL C setenv LANG C echo "-------------------------" echo "locale output after reset" echo "-------------------------" locale echo "" echo "--------------------------------------------" echo "Value at start : gtm_dist=$gtm_dist" echo "Value at start : gtmroutines=""$gtmroutines""" echo "--------------------------------------------" echo "" echo "Setting gtm_dist and gtmroutines to non-UTF8 unconditionally" setenv gtm_dist $gtm_exe setenv gtmroutines ". $gtm_exe" echo "" echo "--------------------------------------------" echo "Value after reset : gtm_dist=$gtm_dist" echo "Value after reset : gtmroutines=""$gtmroutines""" echo "--------------------------------------------" echo "" if ( "$version" =~ *ibm-aix* ) then unsetenv gtm_icu_version endif # Unset gtm_custom_errors to prevent some errors, especially %GTM-E-FTOKERR if $gtm_repl_instance is set in the environment unsetenv gtm_custom_errors # Remove old temporary files and logs. set errorlog = "$gtm_log/error.`basename $gtm_exe`.log" rm -f $errorlog $gtm_log/gt_cc_*.{csh,out} $gtm_log/buildaux_*.{out,err,csh} $gtm_log/tttgen_*.log @ comlist_status = 0 set dollar_sign = \$ alias error_and_exit 'echo \!:1 ; echo \!:1 >> $errorlog ; @ comlist_status++ ; goto comlist.END' unalias ls rm set comlist_start_directory = `pwd` # Verify arguments: set p1 = "$1" set p2 = "$2" setenv gt_as_options "$p1" setenv gt_cc_options "$p2" # Default to building the development version. if ( $4 == "" ) then set p4 = "d" else set p4 = $4 endif # Define image type-specific information. # Use environment variables for the assembler and C compiler because aliases aren't inherited. set p3 = $3 switch ( $p3 ) case "gtm_bta": setenv comlist_gt_as "`alias gt_as_bta` $p1" setenv comlist_gt_cc "`alias gt_cc_bta` $p2" set setactive_parms = ( $p4 b ) ; source $gtm_tools/setactive.csh @ comlist_status = $status set p3 = $gtm_bta set asmtgbltype = "pro" breaksw case "gtm_dbg": setenv comlist_gt_as "`alias gt_as_dbg` $p1" setenv comlist_gt_cc "`alias gt_cc_dbg` $p2" set setactive_parms = ( $p4 d ) ; source $gtm_tools/setactive.csh @ comlist_status = $status set p3 = $gtm_dbg set asmtgbltype = "dbg" breaksw case "gtm_pro": setenv comlist_gt_as "`alias gt_as_pro` $p1" setenv comlist_gt_cc "`alias gt_cc_pro` $p2" set setactive_parms = ( $p4 p ) ; source $gtm_tools/setactive.csh @ comlist_status = $status set p3 = $gtm_pro set asmtgbltype = "pro" breaksw default: error_and_exit "COMLIST-E-BADP3 : Third argument invalid, should be one of: gtm_bta, gtm_dbg, gtm_pro -- no action taken." breaksw endsw if (0 != $comlist_status) then error_and_exit "COMLIST-E-VERSION : version command failed -- aborting build" endif switch ( $gtm_verno ) case "V990": case "V999*": # V990/V999 is designated "most recent on the main line" # and should be protected from inadvertent change. set comlist_chmod_protect = 1 breaksw case "V9*": # Other V9 releases are provided for the "private" use of developers to # modify as they see fit for test purposes and should allow modification. set comlist_chmod_protect = 0 breaksw default: # Anything else should be a release version # and should be protected against inadvertent change. set comlist_chmod_protect = 1 breaksw endsw set mach_type = `uname -m` if ( $comlist_chmod_protect == 1 ) then # Change the permissions on all of the source files to prevent inadvertent # modification by developers. set comlist_chmod_conf = 755 set comlist_chmod_src = 444 else # Change the permissions on all of the source files to allow modification by developers. set comlist_chmod_conf = 775 set comlist_chmod_src = 664 endif if !(-o $gtm_ver) then error_and_exit "COMLIST-E-NOTOWNER : $USER is not owner of $gtm_ver cannot coninue" endif # Change the permissions on the root directory for this version to allow # others in this group (i.e., developers) to create new subdirectories. This # is usually done for running tests or creating readme.txt files, although # there may be other reasons. chmod 775 $gtm_ver # Change the permissions on the configured directories for this version to # prevent inadvetent modification (creation / deletion of files) by developers # or allow modification as appropriate. cd $gtm_ver chmod $comlist_chmod_conf inc pct src tools gtmsrc.csh chmod 775 log if !(-w $gtm_exe) then echo "COMLIST-E-PERMISSON : There is no write permission to $gtm_exe. Exiting" exit endif # Change permisions on all source files to prevent inadvertent modification of # source files corresponding to configured releases and allowing modification # for development and test releases. cd $gtm_inc chmod $comlist_chmod_src * cd $gtm_pct chmod $comlist_chmod_src * # In $gtm_src, use xargs because this directory has so many files that the # command line expansion overflows the command length limits on some Unix # implementations. cd $gtm_src /bin/ls | xargs -n25 chmod $comlist_chmod_src cd $gtm_tools chmod $comlist_chmod_src * if ( $comlist_chmod_protect == 1 ) then chmod 555 *sh # make the shell scripts executable, but protect against inadvertent modification else chmod 775 *sh # make the shell scripts exectuable and allow modification endif echo "" echo "Assembler-related aliases:" echo "" alias | grep gt_as | sort echo "" echo "" echo "Assembler-related environment variables:" echo "" env | grep gt_as | sort echo "" echo "" echo "C compiler-related aliases:" echo "" alias | grep gt_cc | sort echo "" echo "" echo "C compiler-related environment variables:" echo "" env | grep gt_cc | sort echo "" echo "" echo "Linker-related aliases:" echo "" alias | grep gt_ld | sort echo "" echo "" echo "Linker-related environment variables:" echo "" env | grep gt_ld | sort echo "" echo "" echo "Archiver-related aliases:" echo "" alias | grep gt_ar | sort echo "" echo "" echo "Archiver-related environment variables:" echo "" env | grep gt_ar | sort echo "" echo "" set p5 = $5 cd $p3 # Clean slate. chmod +w * # this allows the rm to work without prompting for an override rm * cp $gtm_tools/lowerc_cp.sh lowerc_cp cp $gtm_tools/gtminstall.sh gtminstall chmod +x {lowerc,gtminstall}* cp $gtm_tools/*.xc . cp $gtm_tools/*.gtc . # Fix the copyright year in configure set yyyy = `date +%Y` sed 's/Copyright 2009-YYYY/Copyright 2009-'$yyyy'/' configure.gtc >&! configure rm -f configure.gtc cp $gtm_inc/gtm_common_defs.h . cp $gtm_inc/gtmxc_types.h . cp $gtm_inc/gtm_descript.h . cp $gtm_inc/gtm_sizeof.h . # # headers to support ASCII/EBCDIC independence # cp $gtm_inc/main_pragma.h . cp $gtm_inc/gtm_limits.h . cp $gtm_inc/gtm_stdio.h . cp $gtm_inc/gtm_stdlib.h . cp $gtm_inc/gtm_string.h . cp $gtm_inc/gtm_strings.h . if ( "$HOSTOS" == "OS/390disable" ) then cp $gtm_inc/global_a.h . cp $gtm_inc/gtm_unistd.h . cp $gtm_inc/gtm_netdb.h . cp $gtm_inc/gtm_stat.h . endif # If this is a test version not being built in $gtm_exe # (see value of $3), then make sure ./obj and ./map exist. if ( ! -d ./obj ) then mkdir ./obj endif if ( ! -d ./map ) then mkdir ./map endif cd ./obj # Remove anything that's not a library. We pass -r on Linux to suppress invoking rm with zero args (the xargs default on AIX) set xargs_xtra="" if ("Linux" == $HOSTOS) then set xargs_xtra="-r" endif find . -type f -name '*.a' -prune -o -type f -print | sort | xargs $xargs_xtra -n25 rm set gi = ($gtm_inc) set gs = ($gtm_src) # Irrespective of the gtm_chset value from the user environment, all # M objects generated in $gtm_dist (GDE*.o, _*.o, ttt.o) must be # compiled with gtm_chset="M". unsetenv gtm_chset ############################################################# # # Generate the error message definition files and also ttt.c # ############################################################# if ( -x $gtm_root/$gtm_curpro/pro/mumps ) then set comlist_ZPARSE_error_count = 0 pushd $gtm_src # gtm_startup_chk requires gtm_dist setup set real_gtm_dist = "$gtm_dist" setenv gtm_dist "$gtm_root/$gtm_curpro/pro" set old_gtmroutines = "$gtmroutines" setenv gtmroutines "$gtm_obj($gtm_pct)" set msgstatus = 0 foreach i ( *.msg ) set j = `basename $i .msg` rm -f ${j}_ctl.c ${gtm_inc}${j}_ansi.h # in case an old version is lying around # MSG.m converts a VMS-style error message MSG input file to a # file with information about the messages. # On unix, the C source file includes the message texts. $gtm_root/$gtm_curpro/pro/mumps -run msg $i if (0 != $status) then echo "COMLIST-E-MSG : 'mumps -run msg $i' failed" >> $errorlog @ msgstatus++ endif if ( ! -f ${j}_ctl.c ) then echo "COMLIST-E-MSG : MSG.m failed to produce output file ${j}_ctl.c" >> $errorlog @ msgstatus++ endif if ( -f ${j}_ansi.h ) then mv -f ${j}_ansi.h $gtm_inc endif end # Generate ttt.c $shell -f $gtm_tools/gen_ttt.csh @ msgstatus = $msgstatus + $status setenv gtmroutines "$old_gtmroutines" unset old_gtmroutines setenv gtm_dist "$real_gtm_dist" unset real_gtm_dist popd if ($msgstatus) then error_and_exit "COMLIST-E-MSG : Error running msg.m or gen_ttt.csh" endif else error_and_exit "COMLIST-E-NO_MUMPS : unable to regenerate merrors.c and ttt.c due to missing $gtm_curpro/pro/mumps" endif ############################################################# # # Generate omi_sx_play.c from omi_srvc_xct.c # ############################################################# if (-e $gs[1]/omi_sx_play.c) then chmod a+w $gs[1]/omi_sx_play.c endif cp $gs[1]/omi_srvc_xct.c $gs[1]/omi_sx_play.c chmod a-w $gs[1]/omi_sx_play.c ############################################################# # # Copy over gtcm configuration files to the distribution directory # ############################################################# foreach file (gtcm_slist.gtc gtcm_run.gtc) if ( -e $p3/$file ) then chmod +w $p3/$file endif cp $gtm_tools/$file $p3 chmod a-wx $p3/$file end ############################################## Compilations and Assemblies ################################################### # C compilations first: # For ia64 & x86_64, the file - xfer_desc.i - needs to be generated. if ( "ia64" == $mach_type || "x86_64" == $mach_type ) then pushd $gtm_src $shell -f $gtm_tools/gen_xfer_desc.csh popd endif # For all systems, the file gtm_threadgbl_deftypes.h needs to be generated (no -f as needs startup file) $shell -f $gtm_tools/gen_gtm_threadgbl_deftypes.csh if (0 != $status) then echo "Failed to generate gtm_threadgbl_deftypes.h -- aborting build" exit 1 endif # Setup link from $gtm_obj to the proper assembler include file if this is for a version that doesn't already # have a gtm_threadgbl_deftypes_asm.si in its $gtm_inc directory in order to include the proper variant. if ((! -e ${gtm_inc}/gtm_threadgbl_deftypes_asm.si) && (! -e ${gtm_obj}/gtm_threadgbl_deftypes_asm.si)) then \ln -s ${gtm_inc}/gtm_threadgbl_deftypes_asm_${asmtgbltype}.si ${gtm_obj}/gtm_threadgbl_deftypes_asm.si endif echo "" echo "Start of C Compilation" # Do not change this string. $gtm_tools/buildwarn.awk relies on this to detect warnings. echo "" # List of files that should be excluded from the compilation as they are dealt with separately set TMP_DIR_PREFIX = "/tmp/__${user}__comlist" set TMP_DIR = "${TMP_DIR_PREFIX}__`date +"%y%m%d_%H_%M_%S"`_$$" \mkdir -p ${TMP_DIR} cat << EOF >&! ${TMP_DIR}/exclude.lst omi_sx_play.c gtm_threadgbl_deftypes.c gtmcrypt_pk_ref.c gtmcrypt_dbk_ref.c gtmcrypt_sym_ref.c gtmcrypt_ref.c gtmcrypt_util.c maskpass.c gtm_tls_impl.c EOF find $gs[1] -name '*.c' | grep -v -f ${TMP_DIR}/exclude.lst | sort | xargs -n25 $gtm_tools/gt_cc.csh \rm -rf ${TMP_DIR} set nonomatch ; set compileout=($gtm_log/gt_cc_local_*.out) ; unset nonomatch if ("$gtm_log/gt_cc_local_*.out" != "$compileout") then grep -qE 'COMLIST-E-' $gtm_log/gt_cc_local_*.out if (0 == $status) then error_and_exit "COMLIST-E-COMPILE : Compiling one or more source files failed -- aborting build" endif endif # Special compilation for omi_sx_play.c set comlist_gt_cc_bak = "$comlist_gt_cc" setenv comlist_gt_cc "$comlist_gt_cc -DFILE_TCP" $gtm_tools/gt_cc.csh $gtm_src/omi_sx_play.c setenv comlist_gt_cc "$comlist_gt_cc_bak" echo "" echo "End of C Compilation" # Do not change this string. $gtm_tools/buildwarn.awk relies on this to detect warnings. echo "" if ( $?gt_xargs_insert == 0 ) setenv gt_xargs_insert "-i" # Assembly language assemblies next so they can supersede the C sources by overwriting the object files: echo "Start of Assembly" # Do not change this string. $gtm_tools/buildwarn.awk relies on this to detect warnings. # AS - 2010/07/12 # No longer valid. Probably applied to sr_dux, but gt_as_inc_cvt.csh does # not exist in the current revision or in CVS history #if ( $gt_as_inc_convert == "true" ) then # # Convert assembly language include files to native dialect: # foreach cvt (${gi[1]}/*${gt_as_inc_from_suffix}) # $shell $gtm_tools/gt_as_inc_cvt.csh $cvt # end #endif if ( "$HOSTOS" == "OS/390" ) then $shell -f $gtm_tools/gt_os390_maclib.csh endif # AS - 2010/07/12 this applies to sr_dux only if ( $gt_as_src_convert == "true" ) then # Convert assembly language sources to native dialect in this directory: foreach cvt (${gs[1]}/*${gt_as_src_from_suffix}) $shell -f $gtm_tools/gt_as_src_cvt.csh $cvt end endif if ( $?gt_as_use_prebuilt == 0 ) then # Finally assemble any sources originally in native dialect so they # can supersede any conflicting non-native dialect sources: @ asm_batch_size=25 @ asm_batch_tail = ${asm_batch_size} + 1 set asmlist=(`echo ${gs[1]}/*${gt_as_src_suffix}`) while ($#asmlist) if (${#asmlist} > ${asm_batch_size}) then set asmsublist=(${asmlist[1-${asm_batch_size}]}) set asmlist=(${asmlist[${asm_batch_tail}-]}) else set asmsublist=(${asmlist}) set asmlist=() endif $shell -f $gtm_tools/gt_as.csh ${asmsublist} end else cp -p $gtm_ver/$gt_as_use_prebuilt/*.o . endif if ( $HOSTOS =~ "CYGWIN*" ) then echo "Prefixing _ to .o in $gtm_exe to match Cygwin/Windows naming rules" foreach x (${gs[1]}/*.s) objcopy --prefix-symbols="_" $gtm_exe/obj/$x:r:t.o end endif echo "End of Assembly" # Do not change this string. $gtm_tools/buildwarn.awk relies on this to detect warnings. echo "" ############################################## Archiving object files ################################################### pushd $gtm_tools >& /dev/null set comlist_liblist = `ls *.list | sed 's/.list//' | sed 's/^lib//'` set comlist_liblist = "$comlist_liblist mumps" popd >& /dev/null foreach i ( $comlist_liblist ) if ( -f lib$i.a ) then mv lib$i.a lib$i.a.bak endif if ( -f ar$i.log ) then mv ar$i.log ar$i.log.bak endif echo "Start of $i archive creation: `date`" echo "archiver (ar) options: $gt_ar_option_create" > ar$i.log echo "" >> ar$i.log switch ( $i ) case "mumps": # (Almost) everything else goes into libmumps.a, but the list is too long for a single command line so use xargs. # This case must be executed last in the switch statement (because it picks up "everything else") and, hence, # must appear last in the for statement. #-------------------------------------------------------------------------------------- # While defining exclude files, please append "^" at the beginning of each file name to prevent # other files from being excluded #-------------------------------------------------------------------------------------- # Exclude files that define the same externals # (e.g., "main" and the VMS CLI [command line interpreter] emulator arrays): set exclude = "^gtm\.o|^gtm_main\.o|^lke\.o|^lke_cmd\.o|^dse\.o|^dse_cmd\.o" set exclude = "$exclude|^mupip\.o|^mupip_cmd\.o|^gtmsecshr\.o|^gtmsecshr_wrapper\.o" set exclude = "$exclude|^msg\.o|^gtcm_main\.o|^gtcm_play\.o|^gtcm_pkdisp\.o|^gtcm_shmclean\.o" set exclude = "$exclude|^omi_srvc_xct\.o|^omi_sx_play\.o" set exclude = "$exclude|^gtcm_gnp_server\.o" set exclude = "$exclude|^dummy_gtmci\.o" /bin/ls | egrep '\.o$' | egrep -v "$exclude" | \ xargs -n50 $shell -f $gtm_tools/gt_ar.csh $gt_ar_option_create lib$i.a >>& ar$i.log if ( $status ) then @ comlist_status++ echo "COMLIST-E-ARCHIVE : Error creating lib$i.a archive (see ${dollar_sign}gtm_obj/ar$i.log)" \ >> $errorlog endif breaksw default: gt_ar $gt_ar_option_create lib$i.a `sed -f $gtm_tools/lib_list_ar.sed $gtm_tools/lib$i.list` >>& ar$i.log if ( $status ) then @ comlist_status++ echo "COMLIST-E-ARCHIVE : Error creating lib$i.a archive (see ${dollar_sign}gtm_obj/ar$i.log)" \ >> $errorlog endif rm -f `sed -f $gtm_tools/lib_list_ar.sed $gtm_tools/lib$i.list` breaksw endsw if ( $gt_ar_use_ranlib == "yes" ) then ranlib lib$i.a endif if ( -f lib$i.a.bak ) then rm lib$i.a.bak endif if ( -f ar$i.log.bak) then rm ar$i.log.bak endif echo "" >> ar$i.log echo "End of $i archive creation: `date`" end if (0 != $comlist_status) then error_and_exit "COMLIST-E-ARCHIVE : One of the archive creations above failed -- aborting build" endif /bin/ls | egrep '\.o$' | egrep -v "$exclude" | xargs -n25 rm -f switch ( $3 ) case "gtm_bta": set bldtype = "Bta" set buildscript = "$gtm_tools/buildbta.csh" set buildargs = "$p4" breaksw case "gtm_dbg": set bldtype = "Dbg" set buildscript = "$gtm_tools/builddbg.csh" set buildargs = "$p4" breaksw case "gtm_pro": set bldtype = "Pro" set buildscript = "$gtm_tools/buildpro.csh" set buildargs = "$p4" breaksw endsw $shell -f $buildscript $buildargs if ($status) @ comlist_status++ if (0 != $comlist_status) then error_and_exit "COMLIST-E-BUILD : $buildscript invocation above failed -- aborting build" endif if ( ! -x $gtm_dist/mumps ) then error_and_exit "COMLIST-E-NOMUMPS : ${dollar_sign}gtm_dist/mumps is not executable" endif set mupip_size = `ls -l $gtm_exe/mupip |awk '{print $5}'` set gtmshr_size = `ls -l $gtm_exe/libgtmshr$gt_ld_shl_suffix |awk '{print $5}'` cd $p3 # Generate Special Debug Files (z/OS specific at the moment) if ( -e $gtm_tools/gtm_dbgld.csh ) then $gtm_tools/gtm_dbgld.csh `echo $3 | sed 's/gtm_//'` endif # Build GTMDefinedTypesInit.m file but where to run the generation script from depends on whether our current working # directory has a version of it or not. rm -f obj/gengtmdeftypes.log* rm -f GTMDefinedTypesInit.m echo "Generating GTMDefinedTypesInit.m" $gtm_tools/gengtmdeftypes.csh >& obj/gengtmdeftypes.log @ savestatus = $status if ((0 != $savestatus) || (! -e GTMDefinedTypesInit.m)) then set errmsg = "COMLIST-E-GTMDEFINEDTYPES : gengtmdeftypes.csh failed to create GTMDefinedTypesInit.m " set errmsg = "$errmsg - see log in $gtm_obj/gengtmdeftypes.log" @ comlist_status++ echo $errmsg >> $errorlog endif if (-e struct_padding_warn.out) then mv struct_padding_warn.out $gtm_log/struct_padding_warn.${bldtype:al}.out endif if (-e GTMDefinedTypesInit.m) then # Need a different name for each build type as they can be different cp -f GTMDefinedTypesInit.m $gtm_pct/GTMDefinedTypesInit${bldtype}.m setenv LC_CTYPE C setenv gtm_chset M ./mumps GTMDefinedTypesInit.m if ($status) then set errmsg = "COMLIST-E-GTMDEFINEDTYPES : Failed to compile generated $gtm_exe/GTMDefinedTypes.m" @ comlist_status++ echo "${errmsg}" >> $errorlog endif # If we have a utf8 dir (created by buildaux.csh called from buildbdp.csh above), add a link to it for # GTMDefinedTypesInit.m and compile it in UTF8 mode source $gtm_tools/set_library_path.csh source $gtm_tools/check_utf8_support.csh if (-e $gtm_dist/utf8 && ("TRUE" == "$is_utf8_support")) then set icuver = `setenv gtm_dist $PWD ; $gtm_tools/is_icu_symbol_rename.csh` if ("" != "$icuver") setenv gtm_icu_version "$icuver" if (! -e $gtm_dist/utf8/GTMDefinedTypesInit.m) then ln -s $gtm_dist/GTMDefinedTypesInit.m $gtm_dist/utf8/GTMDefinedTypesInit.m endif pushd utf8 # Switch to UTF8 mode if ( "OS/390" == $HOSTOS ) setenv gtm_chset_locale $utflocale # LC_CTYPE not picked up right setenv LC_CTYPE $utflocale unsetenv LC_ALL setenv gtm_chset UTF-8 # switch to "UTF-8" mode # mumps executable not yet linked to utf8 dir so access it in parent directory ../mumps GTMDefinedTypesInit.m if ($status) then set errmsg = "COMLIST-E-GTMDEFINEDTYPES : Failed to compile generated $gtm_exe/utf8/GTMDefinedTypes.m" @ comlist_status++ echo "${errmsg}" >> $errorlog endif popd setenv LC_CTYPE C unsetenv gtm_chset # switch back to "M" mode if ( "OS/390" == $HOSTOS ) unsetenv gtm_chset_locale endif endif if (0 != $comlist_status) then error_and_exit "COMLIST-E-GTMDEFINEDTYPES : Generating GTMDefinedTypesInit.m failed -- aborting build" endif echo "Generating gtmpcat field build" set old_gtmroutines = "$gtmroutines" setenv gtmroutines "$gtm_obj($gtm_tools)" pushd $gtm_tools chmod +w . rm -f gtmpcat*On*.m gtm_threadgbl_undefs.h $gtm_exe/mumps -r gtmpcatfldbld gtmpcat_field_def.txt ${gtm_verno} if ($status) then error_and_exit "COMLIST-E-GTMPCAT : Failed to generate gtmpcat field build -- aborting build" endif ls -l gtmpcat* popd setenv gtmroutines "$old_gtmroutines" rm $gtm_obj/gtmpcatfldbld.o # Create the GT.M/GDE/MUPIP/DSE/LKE help databases $gtm_tools/generate_help.csh $gtm_pct $errorlog if ($status) then @ comlist_status++ echo "COMLIST-E-HLP : Error generating hlp databases" >> $errorlog endif chmod 775 * # do not check $status here because we know it will be 1 since "gtmsecshr" permissions cannot be changed. # Create a mirror image (using soft links) of $gtm_dist under $gtm_dist/utf8 if it exists. if (-e $gtm_exe/utf8) then # would have been created by buildaux.csh while building GDE pushd $gtm_exe foreach file (*) # Skip utf8 directory if (-d $file && "utf8" == $file) then continue endif # Skip soft linking .o files set extension = $file:e if ($extension == "o") then continue endif # Soft link everything else if (-e utf8/$file) then rm -rf utf8/$file if ($status) then @ comlist_status++ echo "COMLIST-E-RM : Error deleting utf8/$file" >> $errorlog endif endif ln -s ../$file utf8/$file if ($status) then @ comlist_status++ echo "COMLIST-E-LN : Error linking $file" >> $errorlog endif end popd endif # To check the length of path of files even insdie gtmsecshr directory, relax permissions first $gtm_com/IGS $gtm_dist/gtmsecshr UNHIDE set distfiles_log = "dist_files.`basename $gtm_exe`.log" find $gtm_dist -type f >&! $gtm_log/$distfiles_log if ($?scan_image) then tar cvf $gtm_dist/veracode-${gtm_verno}-${HOST:ar}.tar dse gtmsec* gtcm* libgtmshr.so lke mumps mupip tar rvf $gtm_dist/veracode-${gtm_verno}-${HOST:ar}.tar plugin/libgtm* plugin/gtmcrypt/maskpass tar rvf $gtm_dist/veracode-${gtm_verno}-${HOST:ar}.tar /usr/lib64/lib{config,gpgme,gpg-error,crypt,ssl,icu*,z,elf}.so* gzip $gtm_dist/veracode-${gtm_verno}-${HOST:ar}.tar endif $gtm_com/IGS $gtm_dist/gtmsecshr CHOWN awk 'BEGIN {dlen=length(ENVIRON["gtm_dist"]);stat=0} {if ((length($0)-dlen)>50) {stat=1}} END {exit stat}' $gtm_log/$distfiles_log if ($status) then @ comlist_status++ echo "COMLIST-E-PATHLENGTH : The longest path beyond \$gtm_dist exceeds 50 bytes" >> $errorlog awk 'BEGIN {dlen=length(ENVIRON["gtm_dist"]);stat=0} \ {if ((length($0)-dlen)>50) {print $0,"- length :",length($0)-dlen ; stat=1}} \ END {exit stat}' $gtm_log/$distfiles_log >>&! $errorlog endif if ( $comlist_chmod_protect == 1 ) then # If it is release build, protect it from inadvertent modification/rebuild etc chmod -R a-w $gtm_inc $gtm_pct $gtm_src $gtm_tools $gtm_ver/gtmsrc.csh $gtm_exe $gtm_log chmod ug+w $gtm_ver chmod ug+w $gtm_log endif comlist.END: echo "" echo "" if ( -f $errorlog ) then echo "Error summary:" echo "" cat $errorlog else echo "No errors were detected by comlist.csh" endif echo "" # Return to starting directory: cd $comlist_start_directory # Clean up environment variables: unsetenv comlist_gt_as unsetenv comlist_gt_cc # Clean up local shell variables: unset comlist_chmod_conf unset comlist_chmod_protect unset comlist_chmod_src unset comlist_liblist unset comlist_start_directory unset comlist_ZPARSE_error_count unset p1 unset p2 unset p3 unset p4 echo "############# OS and other library versions at the time of build #############" uname -a if ("AIX" == "$HOSTOS") then oslevel -s $gt_cc_compiler -qversion else if ("Linux" == $HOSTOS) then if (-X lsb_release) lsb_release -d $gt_cc_compiler --version |& head -1 endif # gpg and related versions" if (-X gpg2) then gpg2 --version |& grep -E '^gpg |^libgcrypt ' else if (-X gpg) then gpg --version |& grep -E '^gpg |^libgcrypt ' endif if (-X gpg-agent) then gpg-agent --version |& grep -E '^gpg-agent ' endif if (-X openssl) then openssl version endif echo "##############################################################################" echo "" echo "Exit status (should be 0) is : $comlist_status" echo "" echo "End of $0 `date`" exit $comlist_status fis-gtm-V7.0-005/sr_unix/comque.csh0000755000032200000250000001263514342376327016051 0ustar librarygtc#!/usr/local/bin/tcsh ################################################################# # # # Copyright (c) 2001-2019 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. # # # ################################################################# ################################################################## # # comque.csh - submit background task to compile & link GT.M # # comque.csh is an interactive script to compile & link # (build) a release of GT.M. It prompts the user for 4 # parameters: # version - version to be built (default: current) # images - type of images (default: current) # C compiler options in addition to defaults # assembler options in addition to defaults # ################################################################## # # Do not build as root. If you build as root, you'll endu up with incorrectly set permissions on some files. # Incorrectly set permissions fail kitstart.csh if ( 0 == `id -u` ) then echo "You can not build as root." exit 1 endif # Get rid of debug options producing huge amounts of output if set unsetenv gtmdbglvl # Get the version number/designation: echo " " echo -n "Enter Version " echo -n "[$gtm_verno]: " if ( $?comque_batch_mode != 0) then set comque_ver = "" else set comque_ver = $< endif if ( "$comque_ver" == "" ) then set comque_ver = $gtm_verno endif echo " " # Get the image type: if ( "$gtm_exe" == "" ) then set comque_image = "p" else # Convert current image type to single-character prompt set comque_image = `basename $gtm_exe` switch ( $comque_image ) case "b*": set comque_image = "b" breaksw case "d*": set comque_image = "d" breaksw case "p*": default: set comque_image = "p" breaksw endsw endif echo -n "Enter Image " echo -n "[$comque_image]: " if ( $?comque_batch_mode != 0) then set comque_image_input = "" else set comque_image_input = $< endif if ( "$comque_image_input" != "" ) then set comque_image = $comque_image_input endif echo " " # Convert to name and set default compiler and assembler options. # N.B.: These default options must be calculated the same way gtm_env.csh and gtm_env_sp.csh calculate them. switch ( $comque_image ) case "[Bb]*": set comque_image = "bta" set comque_as_options_default = \ "$gt_as_options_common $gt_as_option_I $gt_as_option_DDEBUG $gt_as_option_optimize" set comque_cc_options_default = \ "$gt_cc_options_common $gt_cc_option_I $gt_cc_option_DDEBUG $gt_cc_option_optimize" breaksw case "[Dd]*": if ("$comque_image" =~ "*scan*") then setenv scan_image 1 setenv gt_cc_option_debug "$gt_cc_option_debug_scan" setenv gt_as_option_debug "$gt_as_option_debug_scan" endif set comque_image = "dbg" set comque_as_options_default = \ "$gt_as_options_common $gt_as_option_I $gt_as_option_DDEBUG $gt_as_option_debug $gt_as_option_nooptimize" set comque_cc_options_default = \ "$gt_cc_options_common $gt_cc_option_I $gt_cc_option_DDEBUG $gt_cc_option_debug $gt_cc_option_nooptimize" breaksw case "[Pp]*": set comque_image = "pro" set comque_as_options_default = \ "$gt_as_options_common $gt_as_option_I $gt_as_option_optimize" set comque_cc_options_default = \ "$gt_cc_options_common $gt_cc_option_I $gt_cc_option_optimize" if ( "$HOSTOS" == "Linux" ) then set comque_cc_options_default = "$comque_cc_options_default $gt_cc_option_debug" endif breaksw endsw # Get assembler options: echo "Enter additional assembler options" echo " [default: $comque_as_options_default]" echo -n ' --> ' if ( $?comque_batch_mode != 0) then set comque_as_options_extra = "" else set comque_as_options_extra = "$<" endif if ( "$comque_as_options_extra" != "" ) then echo " [new: $comque_as_options_default $comque_as_options_extra]" endif echo " " # Get C compiler options: echo "Enter additional C compiler options" echo " [default: $comque_cc_options_default]" echo -n ' --> ' if ( $?comque_batch_mode != 0) then set comque_cc_options_extra = "" else set comque_cc_options_extra = "$<" endif if ( "$comque_cc_options_extra" != "" ) then echo " [new: $comque_cc_options_default $comque_cc_options_extra]" endif echo " " set setactive_parms = ( $comque_ver $comque_image ) ; source $gtm_tools/setactive.csh if ( ! -d $gtm_ver/log ) then mkdir $gtm_ver/log chmod 775 $gtm_ver/log endif rm -f $gtm_ver/log/comlist.$comque_image.log if ( $?comque_no_background != 0) then if ( $?comque_batch_mode != 0) then /usr/local/bin/tcsh $gtm_tools/comlist.csh \ "$comque_as_options_extra" "$comque_cc_options_extra" "gtm_$comque_image" "$comque_ver" < /dev/null \ >& $gtm_ver/log/comlist.$comque_image.log else /usr/local/bin/tcsh $gtm_tools/comlist.csh \ "$comque_as_options_extra" "$comque_cc_options_extra" "gtm_$comque_image" "$comque_ver" < /dev/null \ |& tee $gtm_ver/log/comlist.$comque_image.log endif else nohup /usr/local/bin/tcsh $gtm_tools/comlist.csh \ "$comque_as_options_extra" "$comque_cc_options_extra" "gtm_$comque_image" "$comque_ver" \ >& $gtm_ver/log/comlist.$comque_image.log < /dev/null & endif unset comque_as_options_default unset comque_as_options_extra unset comque_cc_options_default unset comque_cc_options_extra unset comque_image unset comque_image_input unset comque_ver fis-gtm-V7.0-005/sr_unix/condstk_expand.c0000644000032200000250000001165614342376327017230 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2015 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_string.h" #include "gtm_stdio.h" #include "gtmio.h" #include "error.h" #include "mv_stent.h" GBLREF unsigned char *stackbase; /* Expands the condition handler stack copying old stack to new expanded stack. * * Note, chnd_end is always set 2 entries from the actual true top of the stack. Consider what can happen * if we go to expand the stack but storage is not available. We will fail but when running things down, * we ALSO need to install handlers. So once process_exiting is set, we are allowed to use the extra handlers. */ void condstk_expand(void) { condition_handler *new_chnd, *new_chnd_end, *ctxt_ent; int new_size, old_len, old_size, cnt; UINTPTR_T delta; mv_stent *mvs; # ifdef DEBUG_ERRHND static int nest_count = 0; nest_count++; assert(1 <= nest_count); /* Prevent reentrance due to condition handler in one of the I/O functions invoked from flush_pio(). */ if (1 == nest_count) DBGEHND((stderr, "condstk_expand: old: chnd: "lvaddr" chnd_end: "lvaddr" ctxt: "lvaddr" active_ch: "lvaddr " chnd_incr: %d\n", chnd, chnd_end, ctxt, active_ch, chnd_incr)); if (ctxt >= (chnd_end + (!process_exiting ? 0 : CONDSTK_RESERVE))) { # endif /* Make sure we are allowed to expand */ old_len = INTCAST((char *)chnd_end - (char *)chnd); old_size = old_len / SIZEOF(condition_handler); new_size = old_size + chnd_incr + CONDSTK_RESERVE; /* New count of entries in cond handlr stack */ /* Nothing known should allow/require stack to get this large */ assertpro(new_size <= CONDSTK_MAX_STACK); new_chnd = malloc(new_size * SIZEOF(condition_handler)); /* Allocate new condition handler stack */ new_chnd_end = &new_chnd[new_size]; delta = (UINTPTR_T)((char *)new_chnd - (char *)chnd); memcpy(new_chnd, chnd, old_len); /* Copy occupied part of old stack */ assert(chnd < chnd_end); /* Modify the address of save_active_ch so points to relevant entry in new condition handler stack. Note first * entry back pointer remains unmodified (as NULL). */ for (cnt = 2, ctxt_ent = new_chnd + 1; cnt <= old_size; cnt++, ctxt_ent++) { assert(ctxt_ent >= new_chnd); assert(ctxt_ent < new_chnd_end); # ifdef DEBUG_ERRHND /* Prevent reentrance due to condition handler in one of the I/O functions invoked from flush_pio(). */ if (1 == nest_count) DBGEHND((stderr, "condstk_expand: cnt: %d, chptr: 0x"lvaddr" save_active_ch from 0x"lvaddr " to 0x"lvaddr"\n", cnt, ctxt_ent, ctxt_ent->save_active_ch, ((char *)ctxt_ent->save_active_ch + delta))); # endif ctxt_ent->save_active_ch = (condition_handler *)((char *)ctxt_ent->save_active_ch + delta); assert(ctxt_ent->save_active_ch >= new_chnd); assert(ctxt_ent->save_active_ch < new_chnd_end); } # ifdef GTM_TRIGGER /* Trigger type mv_stent (MVST_TRIGR) save/restore the value of ctxt so look through the stack to locate those and * fix them up too. */ for (mvs = mv_chain; mvs < (mv_stent *)stackbase; mvs = (mv_stent *)((char *)mvs + mvs->mv_st_next)) { if (MVST_TRIGR != mvs->mv_st_type) continue; # ifdef DEBUG_ERRHND /* Prevent reentrance due to condition handler in one of the I/O functions invoked from flush_pio(). */ if (1 == nest_count) DBGEHND((stderr, "condstk_expand: Trigger saved ctxt modified from 0x"lvaddr" to 0x"lvaddr"\n", mvs->mv_st_cont.mvs_trigr.ctxt_save, (char *)mvs->mv_st_cont.mvs_trigr.ctxt_save + delta)); # endif /* Have a trigger mv_stent - appropriately modify the saved ctxt value (high water mark for condition handlers */ mvs->mv_st_cont.mvs_trigr.ctxt_save = (condition_handler *)((char *)mvs->mv_st_cont.mvs_trigr.ctxt_save + delta); } # endif /* Condition handler stack now reset - modify globals of record accordingly */ free(chnd); /* Old version no longer needed */ chnd = new_chnd; chnd_end = new_chnd_end; if (CONDSTK_MAX_INCR > chnd_incr) chnd_incr = chnd_incr * 2; ctxt = (condition_handler *)((char *)ctxt + delta); active_ch = (condition_handler *)((char *)active_ch + delta); CHECKLOWBOUND(ctxt); CHECKHIGHBOUND(ctxt); CHECKLOWBOUND(active_ch); CHECKHIGHBOUND(active_ch); # ifdef DEBUG_ERRHND } /* Prevent reentrance due to condition handler in one of the I/O functions invoked from flush_pio(). */ if (1 == nest_count) DBGEHND((stderr, "condstk_expand: new: chnd: "lvaddr" chnd_end: "lvaddr" ctxt: "lvaddr" active_ch: "lvaddr " chnd_incr: %d delta: "lvaddr"\n", chnd, chnd_end, ctxt, active_ch, chnd_incr, delta)); nest_count--; assert(0 <= nest_count); # endif } fis-gtm-V7.0-005/sr_unix/configure.gtc0000755000032200000250000005443514342376327016545 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright (c) 2009-2022 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. # # # ################################################################# . ./arch.gtc # Path to the correct echo program # depending on the list of locales configured, locale -a might be considered a binary output. # grep needs -a option to process the output as text but -a is not supported on the non-linux servers we have. if [ $arch = "linux" ]; then echo="/bin/echo -e" binaryopt="-a" else echo=/bin/echo binaryopt="" fi # Options to ps psopts="-ea" # GTCM definitions if [ -f gtcm_server ]; then deliver_gtcm="yes" else deliver_gtcm="no" fi if [ -f gtcm_gnp_server ]; then deliver_gtcm_gnp="yes" else deliver_gtcm_gnp="no" fi # create symbolic links if utf8 directory exists. Ignore the "file exists" errors for the .o files # which exist in both directories. if [ -d utf8 ]; then (cd utf8; ln -s ../* . 2> /dev/null) fi # Native shared library extension. ext=".so" # Flags to build shared libraries of M routines if [ "ibm" = $arch ] ; then ldflags="-brtl -G -bexpfull -bnoentry -b64" ; ldcmd="ld" # AIX elif [ "linux" = $arch ] ; then ldflags="-shared" ; ldcmd="ld" # Linux - all platforms else echo "Shared libary ldflags not set for this platform"; exit 1 fi # Binaries binaries="mupip mumps libgtmshr$ext lke dse" # Normal scripts - executed by anyone nscripts="gtmbase lowerc_cp" # Root scripts - only executed by root rscripts="gtmstart gtmstop" if [ $deliver_gtcm = "yes" ]; then rscripts="$rscripts gtcm_run gtcm_slist" binaries="$binaries gtcm_server gtcm_pkdisp gtcm_play gtcm_shmclean" fi if [ $deliver_gtcm_gnp = "yes" ]; then binaries="$binaries gtcm_gnp_server" fi # Help files hlpfiles="*help.dat *help.gld *.h" # Other files (left here for future use) ofiles="$hlpfiles" # Files that need to have $gtm_dist, $echo, etc. set in them. pathmods="gtmbase.gtc gtmstart.gtc gtmstop.gtc gtmcshrc.gtc gtmprofile.gtc gtm.gtc gtmprofile_preV54000.gtc gdedefaults.gtc" # Files deprecated in various releases. These should explicitly removed if installation is done on an existing directory. deprecatedfiles="geteuid semstat2 ftok" if [ $deliver_gtcm = "yes" ]; then pathmods="$pathmods gtcm_run.gtc gtcm_slist.gtc" fi if [ "`whoami`" != "root" ] ; then $echo "You must run Configure as root." exit fi $echo " GT.M Configuration Script" $echo "Copyright 2009-YYYY Fidelity Information Services, Inc. and/or its subsidiaries." $echo "Use of this software is restricted by the provisions of your license agreement." $echo "" # Native super user and group rootuser=root bingroup=bin defowner=bin # create temporary file to test for valid user and group names touch tmp_owngrp $echo "What user account should own the files? ($defowner) \c" read resp if [ "$resp" = "" ] ; then owner=$defowner else owner=$resp fi chown $owner tmp_owngrp 2> /dev/null if [ 0 != "$?" ] ; then $echo $owner is not a valid user name - exiting! rm tmp_owngrp exit fi $echo "What group should own the files? ($bingroup) \c" read resp if [ "$resp" != "" ] ; then bingroup=$resp fi chgrp $bingroup tmp_owngrp 2> /dev/null if [ 0 != "$?" ] ; then $echo $bingroup is not a valid group name - exiting! rm tmp_owngrp exit 1 fi $echo "Should execution of GT.M be restricted to this group? (y or n) \c" read resp if [ "$resp" = "Y" -o "$resp" = "y" ] ; then # root and bin are invalid groups to be restricted if [ 0 = $bingroup -o "bin" = $bingroup -o "root" = $bingroup ] ; then $echo GTM-E-GROUPNOTVALID - $bingroup not allowed to be a restricted group - exiting! exit 1 fi # for all owners other than root or bin do an owner in group membership check if [ 0 != $owner -a "bin" != $owner -a "root" != $owner -a `groups $owner | grep -w -c $bingroup` -eq 0 ] ; then $echo GTM-E-NOTINGROUP - $owner is not a member of $bingroup - exiting! exit 1 fi group=$bingroup fi rm tmp_owngrp $echo "In what directory should GT.M be installed? \c" read gtmdist # if gtmdist is relative then need to make it absolute if [ `$echo $gtmdist | grep -c '^/'` -eq 0 ] ; then gtmdist=`pwd`/$gtmdist fi # ensure that canonical paths do not exceed PATH_MAX getconf PATH_MAX $gtmdist | \ awk '{max=$0-max;if(max<0)max+=1024;if(length(dist)>max){print dist" exceeds the maximum path length: "max;exit 1}}' \ dist=$gtmdist max=50 || exit $echo "" if [ -d $gtmdist ]; then $echo "Directory $gtmdist exists. If you proceed with this installation then" $echo "some files will be over-written. Is it ok to proceed? (y or n) \c" else $echo "Directory $gtmdist does not exist. Do you wish to create it as part of" $echo "this installation? (y or n) \c" fi read resp if [ "$resp" = "Y" -o "$resp" = "y" ] ; then mkdir -p $gtmdist chmod ug-s,-t $gtmdist mkdir -p $gtmdist/plugin/r $gtmdist/plugin/o chmod 0755 $gtmdist/plugin/r $gtmdist/plugin/o $gtmdist chown $owner $gtmdist/plugin/r $gtmdist/plugin/o $gtmdist chgrp $bingroup $gtmdist/plugin/r $gtmdist/plugin/o $gtmdist remove_deprecated=1 else exit fi if [ ! -w $gtmdist ]; then $echo "Directory $gtmdist is not writeable, so exiting" exit fi server_id=42 $echo "" $echo "Installing GT.M...." $echo "" is64bit_gtm=`file mumps | grep -c 64.bit` if [ -d "utf8" ]; then unset icu_version doutf8=1 # If package has utf8 directory, see if system has libicu and locale $echo "Should UTF-8 support be installed? (y or n) \c" read resp if [ "$resp" = "Y" -o "$resp" = "y" ] ; then if [ -x "$(command -v pkg-config)" -a $(pkg-config --exists icu-io; echo $?) -eq 0 ]; then versioncmd="pkg-config --modversion icu-io" libcmd="pkg-config --variable=libdir icu-io" elif [ -x "$(command -v icu-config)" ] ; then versioncmd="icu-config --version" libcmd="icu-config --libdir" elif [ -x "$(command -v pkg-config)" -a $(pkg-config --exists icu; echo $?) -eq 0 ]; then versioncmd="pkg-config --modversion icu" libcmd="pkg-config --variable=libdir icu" fi if [ -n "$versioncmd" ]; then icu_version=`$versioncmd | awk '{ver=+$0;if(ver>5){ver=ver/10}printf("%.1f\n",ver);exit}'` extendlibpath=`$libcmd` fi if [ "$icu_version" != "" ] ; then maj=`$echo $icu_version | cut -f 1 -d "."` if [ "$maj" -ge "49" ] ; then # Assuming the input is of the form 52.1. As of ICU 49 (aka 4.9), # the version reported by icu-config --version is . and not . majmin=$maj else # Assuming the input is of the form 4.2.1 majmin=`$echo $icu_version | cut -d "." -f 1,2 | sed 's/\.//'` fi if [ "$majmin" -lt "36" ] ; then $echo "ICU version version found : $icu_version" $echo "Install ICU version 3.6 or above to enable UTF-8 support." doutf8=0 fi else $echo "Could not find ICU version in the default location." doutf8=0 fi # Look for locale utflocale=`locale -a | grep $binaryopt -iE '\.utf.?8$' | head -n1` if [ "$utflocale" = "" ] ; then $echo "UTF-8 locale not found." doutf8=0 fi if [ "$doutf8" -eq 0 ] ; then $echo "UTF-8 support was requested, but not all dependencies were satisfied." $echo "Please fix the above and re-run installation." exit 1 fi if [ "$doutf8" -eq 1 -a "linux" = $arch ]; then # We do not recommend setting gtm_icu_version on AIX gtm_icu_version=$icu_version export gtm_icu_version fi else doutf8=0 fi else # If utf8 dir does not exist in package, can't install UTF-8 support doutf8=0 fi # Solaris 10 bourne shell does not support ${var#word} syntax install_dest=$gtmdist if [ X"$gtm_destdir" != X"" ] ; then install_dest=`echo $install_dest | sed "s;${gtm_destdir};;"` fi # Modify the scripts as necessary for target configuration cat << SEDSCRIPT > sedin$$ s|ARCH|$arch|g s|ECHO|"$echo"|g s|GTMDIST|${install_dest}|g s|SERVERID|$server_id|g SEDSCRIPT for i in $pathmods do dest=`basename $i .gtc` sed -f sedin$$ $i > $dest if [ "$doutf8" -ne 0 ]; then cd utf8 if ( test -f "$dest" ) then rm $dest; fi ln -fs ../$dest $dest cd .. fi done rm sedin$$ if [ "$doutf8" -ne 0 ]; then if [ ! -d $gtmdist/utf8 ]; then mkdir -p $gtmdist/utf8 $gtmdist/plugin/o/utf8 chmod 0755 $gtmdist/utf8 $gtmdist/plugin/o/utf8 chown ${owner}:${bingroup} $gtmdist/utf8 $gtmdist/plugin/o/utf8 fi fi # Remove deprecated files first if applicable if [ -n "$remove_deprecated" ]; then $echo "" $echo "Checking for deprecated files in $gtmdist" $echo "" for file in $deprecatedfiles do if [ -f "$gtmdist/$file" -o -f "$gtmdist/utf8/$file" ]; then echo " Removing $file" rm -f $gtmdist/$file $gtmdist/utf8/$file fi done fi # Install COPYING if it is applicable file=COPYING if [ -f $file ]; then cp -p $file $gtmdist if [ "$doutf8" -ne 0 ]; then ln -fs ../$file $gtmdist/utf8/$file fi fi # Install README.txt if it is applicable file=README.txt if [ -f $file ]; then cp -p $file $gtmdist if [ "$doutf8" -ne 0 ]; then ln -fs ../$file $gtmdist/utf8/$file fi fi # Install custom_errors_sample.txt if it is applicable file=custom_errors_sample.txt if [ -f $file ]; then cp -p $file $gtmdist if [ "$doutf8" -ne 0 ]; then ln -fs ../$file $gtmdist/utf8/$file fi fi # Install the .cshrc and .profile files cp -p gdedefaults gtmgblstat.xc gtmprofile gtmprofile_preV54000 gtm gtmcshrc $gtmdist chmod 0444 $gtmdist/gdedefaults chown $owner $gtmdist/gdedefaults chmod 0444 $gtmdist/gtmgblstat.xc chown $owner $gtmdist/gtmgblstat.xc chmod 0755 $gtmdist/gtmprofile chown $owner $gtmdist/gtmprofile chmod 0755 $gtmdist/gtmprofile_preV54000 chown $owner $gtmdist/gtmprofile_preV54000 chmod 0755 $gtmdist/gtm chown $owner $gtmdist/gtm chmod 0755 $gtmdist/gtmcshrc chown $owner $gtmdist/gtmcshrc # Install the normal scripts for i in $nscripts do cp -p $i $gtmdist chmod 0755 $gtmdist/$i chown $owner $gtmdist/$i done # Install the root scripts for i in $rscripts do cp -p $i $gtmdist chmod 0744 $gtmdist/$i chown $rootuser $gtmdist/$i done # Install the normal binaries for i in $binaries do if [ $arch = "linux" ]; then install -g $bingroup -o $owner -m 644 $i $gtmdist elif [ $arch = "ibm" ]; then /usr/bin/install -f $gtmdist -M 644 -O $owner -G $bingroup $i elif [ -x /usr/sbin/install ]; then /usr/sbin/install -f $gtmdist -m 644 -u $owner -g $bingroup $i $gtmdist else install -f $gtmdist -m 644 -u $owner -g $bingroup $i $gtmdist fi # strip $gtmdist/$i >/dev/null 2>&1 done # Install other individual files for i in $ofiles do cp -p $i $gtmdist chown $owner $gtmdist/$i done # For linux systems, attempt to execute the chcon command to allow use of the libgtmshr shared library. This # command is required on many modern SELinux based systems but depends on the filesystem in use (requires context # support). For that reason, we attempt the command and if it works, great. If it doesn't, oh well we tried. if [ -f /usr/bin/chcon ]; then chcon -t texrel_shlib_t $gtmdist/libgtmshr$ext > /dev/null 2>&1 fi # Create $gtmdist/plugin/gtmcrypt directory if this platform supports encryption # Define variables to denote plugin and gtmcrypt directory names plugin="plugin" plugin_gtmcrypt="$plugin/gtmcrypt" # Gtmcrypt scripts gtmcryptscripts="gen_sym_key.sh encrypt_sign_db_key.sh gen_keypair.sh pinentry-gtm.sh" gtmcryptscripts="$gtmcryptscripts import_and_sign_key.sh gen_sym_hash.sh show_install_config.sh" # Gtmcrypt related M file gtmcryptmfile="pinentry.m" # Gtmcrypt source files gtmcryptsrcfiles="Makefile README gtmcrypt_ref.c gtmcrypt_ref.h gtmcrypt_interface.h maskpass.c" gtmcryptsrcfiles="$gtmcryptsrcfiles gtmcrypt_dbk_ref.c gtmcrypt_dbk_ref.h gtmcrypt_pk_ref.c gtmcrypt_pk_ref.h" gtmcryptsrcfiles="$gtmcryptsrcfiles gtmcrypt_sym_ref.h gtmcrypt_sym_ref.c gtm_tls_interface.h gtm_tls_impl.h" gtmcryptsrcfiles="$gtmcryptsrcfiles gtm_tls_impl.c gtmcrypt_util.c gtmcrypt_util.h $gtmcryptmfile" dogtmcrypt=0 if [ -d "$plugin_gtmcrypt" ]; then dogtmcrypt=1 # Create plugin directory and gtmcrypt directory mkdir -p $gtmdist/plugin/gtmcrypt chmod 0755 $gtmdist/plugin chown $owner $gtmdist/plugin chgrp $bingroup $gtmdist/plugin chmod 0755 $gtmdist/plugin/gtmcrypt chown $owner $gtmdist/plugin/gtmcrypt/ chgrp $bingroup $gtmdist/plugin/gtmcrypt # Tar the source files cat > $plugin_gtmcrypt/README << EOF # # WARNING: This file was generated by the GT.M install script at install time. # # Dependency information: # Please install a compiler toolchain and libgcrypt, libgpgme, libconfig, and # libssl development libraries. On Debian you can do: # sudo apt-get install libgcrypt11-dev libgpgme11-dev libconfig-dev libssl-dev # # Installation instructions: # You may need to edit the Makefile to add include (IFLAGS) and library paths # (LIBFLAGS) for your system. # # The commands below to compile, install and set the file permissions on the # encryption plugin libraries make the following assumptions: # - The shell is bourne shell compatible # - The user is root # - The permissions defined for the gtm installation are appropriate for the # plug-in # # You should be able to cut and paste the following commands # gtm_dist="$gtmdist" export gtm_dist make && make install && make clean find \$gtm_dist/plugin -type f -exec chown ${owner}:${bingroup} {} + EOF if [ "$group" != "" ] ; then echo "chmod -R o-rwx \$gtm_dist/plugin" >> $plugin_gtmcrypt/README fi (cd $plugin_gtmcrypt && \ chmod 0644 $gtmcryptsrcfiles && \ chmod 0755 $gtmcryptscripts && \ chown ${owner}:${bingroup} * && \ tar -cvf $gtmdist/$plugin_gtmcrypt/source.tar $gtmcryptsrcfiles $gtmcryptscripts >/dev/null 2>&1) chmod 0644 $gtmdist/$plugin_gtmcrypt/source.tar chown ${owner}:${bingroup} $gtmdist/$plugin_gtmcrypt/source.tar rm $plugin_gtmcrypt/README fi # Install GDE, GTMHELP, and all the percent routines cp -p *.o *.m $gtmdist # Install a mirror image (using soft links) of $gtmdist under $gtmdist/utf8 if this platform can support "UTF-8" mode. if [ "$doutf8" -ne 0 ]; then cd utf8 for file in * do # Skip directories if [ -d "$file" ]; then continue fi # Skip gtmsecshr/dir if [ "$file" = "gtmsecshr" -o "$file" = "gtmsecshrdir" ]; then continue fi # Install .o files base="`basename $file .o`" if [ "$base" != "$file" ]; then cp -p "$file" $gtmdist/utf8 else # Soft link everything else if [ -f $gtmdist/utf8/"$file" ]; then rm -f $gtmdist/utf8/"$file" fi if [ -f $gtmdist/"$file" ]; then ln -fs ../"$file" $gtmdist/utf8/"$file" fi fi done if [ "$dogtmcrypt" -ne 0 ]; then ln -fs ../plugin $gtmdist/utf8/plugin fi cd .. fi $echo "" $echo "All of the GT.M MUMPS routines are distributed with uppercase names." $echo "You can create lowercase copies of these routines if you wish, but" $echo "to avoid problems with compatibility in the future, consider keeping" $echo "only the uppercase versions of the files." $echo "" $echo "Do you want uppercase and lowercase versions of the MUMPS routines? (y or n) \c" read resp if [ "$resp" = "Y" -o "$resp" = "y" ] ; then $echo "" $echo "Creating lowercase versions of the MUMPS routines." (cd $gtmdist; ./lowerc_cp *.m) if [ "$doutf8" -ne 0 ]; then (cd $gtmdist/utf8; ./lowerc_cp *.m) fi fi # Change mode to executable for mumps and libgtmshr to do the compiles chmod 755 $gtmdist/mumps $gtmdist/libgtmshr$ext gtmroutines=$gtmdist gtmgbldir=$gtmdist/mumps.gld gtm_dist=$gtmdist export gtm_dist export gtmroutines export gtmgbldir $echo "" $echo "Compiling all of the MUMPS routines. This may take a moment." $echo "" # Ensure we are NOT in UTF-8 mode gtm_chset="M" export gtm_chset (cd $gtmdist; ./mumps -noignore *.m; $echo $?>compstat ; \ if [ "$is64bit_gtm" -eq 1 -o "linux" != $arch ] ; then $ldcmd $ldflags -o libgtmutil$ext *.o ; fi ) # Now work on UTF-8 mode if [ "$doutf8" -ne 0 ]; then # Enclose UTF-8 operations inside a subshell. This avoids changing the current M mode execution ( # Ensure we ARE in UTF-8 mode utflocale=`locale -a | grep $binaryopt -iE 'en_us\.utf.?8$' | head -n1` if [ "$utflocale" = "" ]; then # If no locale defined, try C.UTF-8 utflocale="C.UTF-8" fi LC_CTYPE=$utflocale export LC_CTYPE unset LC_ALL gtm_chset="UTF-8" export gtm_chset if [ $arch = "ibm" ]; then export LIBPATH=$extendlibpath else LD_LIBRARY_PATH=$extendlibpath export LD_LIBRARY_PATH fi (gtm_dist=$gtmdist/utf8; export gtm_dist; cd $gtm_dist; ./mumps -noignore *.m; $echo $?>>$gtmdist/compstat; \ if [ $is64bit_gtm -eq 1 -o "linux" != $arch ] ; then $ldcmd $ldflags -o libgtmutil$ext *.o ; fi ) ) fi # Change mode to executable for the normal binaries for i in $binaries do chmod 755 $gtmdist/$i done chmod 0644 $gtmdist/*.m chmod 0644 $gtmdist/*.o chown $owner $gtmdist/*.m chown $owner $gtmdist/*.o chown $owner $gtmdist/*.txt chgrp $bingroup $gtmdist/*.m chgrp $bingroup $gtmdist/*.o chgrp $bingroup $gtmdist/*.txt if [ "$doutf8" -ne 0 ]; then chmod 0644 $gtmdist/utf8/*.m chmod 0644 $gtmdist/utf8/*.o chown $owner $gtmdist/utf8 chown $owner $gtmdist/utf8/*.m chown $owner $gtmdist/utf8/*.o chown $owner $gtmdist/utf8/*.txt chgrp $bingroup $gtmdist/utf8/*.m chgrp $bingroup $gtmdist/utf8/*.o chgrp $bingroup $gtmdist/utf8/*.txt fi if [ -f $gtm_dist/libgtmutil$ext ] ; then gtmroutines="$gtm_dist/libgtmutil$ext $gtmdist" else gtmroutines="$gtmdist" fi export gtmroutines other_object_files="" csh_script_files="" # make database files read only chmod 0444 $gtmdist/*.dat chmod 0444 $gtmdist/*.gld # $other_object_files, $csh_script_files should be removed unconditionally savedir=`pwd` # temporarily change to $gtmdist cd $gtmdist \rm -rf $other_object_files $csh_script_files lowerc_cp if [ -d utf8 ]; then cd utf8 \rm -rf $other_object_files $csh_script_files lowerc_cp fi # change back to original directory cd $savedir # Optionally remove .o files if they are in a shared library if [ -f $gtm_dist/libgtmutil$ext ] ; then chown ${owner}:${bingroup} $gtm_dist/libgtmutil$ext chmod 755 $gtm_dist/libgtmutil$ext $echo "" $echo "Object files of M routines placed in shared library $gtm_dist/libgtmutil$ext" $echo "Keep original .o object files (y or n)? \c" read resp if [ "n" = "$resp" -o "N" = "$resp" ] ; then rm -f $gtm_dist/*.o $gtm_dist/utf8/*.o ; fi $echo "" if [ -f $gtm_dist/utf8/libgtmutil$ext ] ; then chown ${owner}:${bingroup} $gtm_dist/utf8/libgtmutil$ext chmod 755 $gtm_dist/utf8/libgtmutil$ext fi fi # change group ownership of all files if group restricted # otherwise change to the default as some files were created with root group if [ "$group" != "" ] ; then chgrp -R $group $gtmdist chmod -R o-rwx $gtmdist else chgrp -R $bingroup $gtmdist fi # Install real gtmsecshr with special permissions in $gtmdist/gtmsecshrdir tgtmsecshrdir=$gtmdist/gtmsecshrdir mkdir -p $tgtmsecshrdir chmod 700 $tgtmsecshrdir chgrp $bingroup $tgtmsecshrdir # Install gtmsecshr and the wrapper with special permissions if [ $arch = "linux" ]; then install -m 4555 -o root -g $bingroup gtmsecshr $gtmdist install -m 4500 -o root -g $bingroup gtmsecshrdir/gtmsecshr $tgtmsecshrdir/gtmsecshr elif [ $arch = "ibm" ]; then /usr/bin/install -f $gtmdist -M 4555 -O root -G $bingroup gtmsecshr /usr/bin/install -f $tgtmsecshrdir -M 4500 -O root -G $bingroup gtmsecshrdir/gtmsecshr elif [ -x /usr/sbin/install ]; then /usr/sbin/install -f $gtmdist -m 4555 -u root -g $bingroup gtmsecshr $gtmdist /usr/sbin/install -f $tgtmsecshrdir -m 4500 -u root -g $bingroup gtmsecshrdir/gtmsecshr $tgtmsecshrdir else install -f $gtmdist -m 4555 -u root -g $bingroup gtmsecshr $gtmdist install -f $tgtmsecshrdir -m 4500 -u root -g $bingroup gtmsecshrdir/gtmsecshr $tgtmsecshrdir fi strip $gtmdist/gtmsecshr > /dev/null 2>&1 strip $tgtmsecshrdir/gtmsecshr > /dev/null 2>&1 if [ -d $gtmdist/utf8 ]; then # Delete the gtmsecshr symlink if [ -f $gtmdist/utf8/gtmsecshr -o -h $gtmdist/utf8/gtmsecshr ]; then rm -f $gtmdist/utf8/gtmsecshr fi ln -f $gtmdist/gtmsecshr $gtmdist/utf8/gtmsecshr || \ echo ln -f $gtmdist/gtmsecshr $gtmdist/utf8/gtmsecshr # Delete the gtmsecshrdir symlink if [ -f $gtmdist/utf8/gtmsecshrdir -o -h $gtmdist/utf8/gtmsecshrdir ]; then rm -f $gtmdist/utf8/gtmsecshrdir fi mkdir -p $gtmdist/utf8/gtmsecshrdir chmod 0500 $gtmdist/utf8/gtmsecshrdir ln -f $gtmdist/gtmsecshrdir/gtmsecshr $gtmdist/utf8/gtmsecshrdir/gtmsecshr || \ echo ln -f $gtmdist/gtmsecshrdir/gtmsecshr $gtmdist/utf8/gtmsecshrdir/gtmsecshr fi # change group ownership of wrapper if group restricted # also remove user privileges for wrapper if group changed if [ "$group" != "" ] ; then chgrp $group $gtmdist/gtmsecshr $echo "" $echo "Removing world permissions from gtmsecshr wrapper since group restricted to \"$group\"" chmod 4550 $gtmdist/gtmsecshr fi # leave nothing writeable chmod -R a-w $gtmdist # if we had a mumps error then remove executable bit recursively and exit # this could include compile and/or library load errors if [ 0 != `grep -c '[1-9]' $gtm_dist/compstat` ]; then $echo "" $echo "GT.M installation FAILED – please review error messages" $echo "" ( cd $gtm_dist && find . -type f -exec chmod a-x {} + ) exit 1 fi rm -f $gtmdist/compstat if [ "$gtm_icu_version" != "" ] ; then icumessage=" Used gtm_icu_version=$gtm_icu_version for UTF-8 installation." fi $echo "" $echo "Installation completed.$icumessage" $echo "Would you like all the temporary files removed from this directory? (y or n) \c" read resp if [ "$resp" = "Y" -o "$resp" = "y" ] ; then \rm -rf $binaries $pathmods $rscripts $nscripts $dirs configure \ *.gtc gtm* gde* GDE*.o _*.m _*.o mumps.dat mumps.gld $other_object_files $csh_script_files lowerc_cp\ *.hlp core *.h *.m *help.dat *help.gld COPYING README.txt \rm -rf GETPASS.o plugin GTMHELP.o custom_errors_sample.txt if [ -d utf8 ]; then \rm -rf utf8 fi fi # Save long listing of files, including permissions and sha256 for future reference find $gtmdist -type f \! -name install_permissions.log -exec ls -l {} \; > $gtmdist/install_permissions.log chmod 0444 $gtmdist/install_permissions.log chown ${owner}:${bingroup} $gtmdist/install_permissions.log OPENSSL=$(command -p command -v openssl) if [ -x "$OPENSSL" ]; then find $gtmdist -type f \! -name install_sha256_checksum.log -exec $OPENSSL sha256 {} \; \ > $gtmdist/install_sha256_checksum.log chmod 0444 $gtmdist/install_sha256_checksum.log chown ${owner}:${bingroup} $gtmdist/install_sha256_checksum.log fi fis-gtm-V7.0-005/sr_unix/continue_handler.c0000755000032200000250000000431414342376327017541 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 #include #ifdef GTM_PTHREAD # include #endif #include "gtm_syslog.h" #include "gtm_limits.h" #include "gtmsiginfo.h" #include "io.h" #include "send_msg.h" #include "setterm.h" #include "continue_handler.h" #include "gtmsecshr.h" GBLREF volatile int suspend_status; GBLREF io_pair io_std_device; GBLREF uint4 process_id; error_def(ERR_REQ2RESUME); void continue_handler(int sig, siginfo_t *info, void *context) { gtmsiginfo_t sig_info; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; FORWARD_SIG_TO_MAIN_THREAD_IF_NEEDED(sig); /* Count how many times we get a continue-process signal (in DEBUG) */ DEBUG_ONLY(DBGGSSHR((LOGFLAGS, "continue_handler: pid %d, continue_proc_cnt bumped from %d to %d\n", process_id, TREF(continue_proc_cnt), TREF(continue_proc_cnt) + 1))); DEBUG_ONLY((TREF(continue_proc_cnt))++); assert(SIGCONT == sig); extract_signal_info(sig, info, context, &sig_info); switch(suspend_status) { case NOW_SUSPEND: /* Don't bother checking if user info is available. If info isn't available, * the value zero will be printed for pid and uid. Note that the following * message was previously put out even when the process was not suspended but * with the changes in spin locks that send continue messages "just in case", * I thought it better to restrict this message to when the process was actually * suspended and being continued. SE 7/01 */ send_msg(VARLSTCNT(4) ERR_REQ2RESUME, 2, sig_info.send_pid, sig_info.send_uid); if (NULL != io_std_device.in && tt == io_std_device.in->type) setterm(io_std_device.in); /* Fall through */ case DEFER_SUSPEND: /* If suspend was deferred, this continue/resume overrides/cancels it */ suspend_status = NO_SUSPEND; break; } } fis-gtm-V7.0-005/sr_unix/continue_handler.h0000755000032200000250000000122614342376327017545 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef CONTINUE_HANDLER_H_INCLUDED #define CONTINUE_HANDLER_H_INCLUDED void continue_handler(int sig, siginfo_t *info, void *context); #endif fis-gtm-V7.0-005/sr_unix/continue_proc.c0000755000032200000250000000274714342376327017077 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 #include #include "gtm_limits.h" #include "io.h" #include "gtmsecshr.h" #include "secshr_client.h" #include "send_msg.h" #include "is_proc_alive.h" #include "wbox_test_init.h" error_def(ERR_NOSUCHPROC); int continue_proc(pid_t pid) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DEBUG_ONLY(if (!TREF(gtm_usesecshr))) /* Cause debug builds to talk to gtmsecshr more often */ { if (WBTEST_ENABLED(WBTEST_HOLD_GTMSOURCE_SRV_LATCH)) { /* Simulate the kill below, but ignore its return status so that we end up invoking gtmsecshr */ kill(pid, SIGCONT); /* Wait until the target quits so that kill() call by gtmsecshr fails with ESRCH */ while (is_proc_alive(pid, 0)) LONG_SLEEP(1); } else if (0 == kill(pid, SIGCONT)) return 0; else if (ESRCH == errno) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_NOSUCHPROC, 3, pid, RTS_ERROR_LITERAL("continue")); return ESRCH; } else assert(EINVAL != errno); } return send_mesg2gtmsecshr(CONTINUE_PROCESS, pid, NULL, 0); } fis-gtm-V7.0-005/sr_unix/coverage_cc.sh0000755000032200000250000001536114342376327016654 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright (c) 2021 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. # # # ################################################################# ## # @file # # @brief Replace the standard GT.M compiler with a coverage enabled one # # Our purpose here in this script is to be a drop-in C compiler (for linux/gcc only) # which can be the object of the gt_cc_compiler environment variable. # # If we are invoked, our only purpose in life is to add the "--coverage" # and supporting flags to compiles, and the "-lgcov" flag to loads, *except* for certain # exceptional files (forex: tmpCFile2.c from scantypedefs.m) # # Since all our gcc versions on hosts participating in a coverage run must be # the same, and since we originally used some advanced coverage features, we standardize # on gcc-10.2.0 for our underlying compiler here. Since this is not the default on # any of our hosts, we use a set-aside directory to make sure we get the correct version. # If we can't find a compliant compiler, we just fall back to an unuseful, but # harmless regular run. ## # @brief Main driver for the compiler script # # We run through the command line here, deciding if we are doing a coverage compile or now. # If we are, and the system supports it, we massage argv so as to add the needed flags to # enable coverage mode in GCC and pull in libgcov at link time. # # @return We will generally exit with the exit status of the underlying compiler # main () { real_compiler="gcc" can_profile=1 # We assume we are run from /usr/library/VXXX/dbg/obj #obj_dir=`pwd` #build=`echo $obj_dir | sed -e 's?/usr/library/??' -e 's?/.*$??'` # First, determine if we are compile or link invocation of the compiler # and if we are doing a DEBUG build. (We only add coverage flags to a debug # build) is_debug_build=0 is_compile=0 is_link=0 is_stop_list=0 # List of C files for which we do not want to turn on --coverage stop_list="tmpCFile.c tmpCFile2.c gtm_threadgbl_deftypes.c" # Peek at our command line arguments to guess what we are doing for arg do if [ $arg = "-DDEBUG" ]; then is_debug_build=1 fi # We are a debug link if we see something like "-L/usr/library/V972/dbg/obj" case $arg in -L*dbg/obj) is_debug_build=1 ;; esac if [ $arg = "-std=c99" ]; then is_compile=1 fi if [ $arg = "-lrt" ]; then is_link=1 fi for stop_file in $stop_list do case $arg in $stop_file) is_stop_list=1 ;; */${stop_file}) is_stop_list=1 ;; esac done done # If we are not linux, not doing a debug build, or if we are on the stop list punt to the original compiler/options if [ \( $OSTYPE != "linux" \) -o \( $is_debug_build -eq 0 \) -o \( $is_stop_list -eq 1 \) ]; then exec $real_compiler $* fi # We are doing a debug build. Can our default compiler profile at the pid-separated level? # Find the gcc version, gcc --version will return something like # gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 # and we always care only about the first line/last field # gcc_version=`$real_compiler --version | head -1 | sed -e 's/^.* //'` # This section used to look for gcc >= 10, but it turns out that the # profiling data is *very* version specific, so we must use gcc-10.2.0. # If that is not the system gcc (and it won't be on any current linux box of ours) # then we have to check to see if we have installed 10.2.0 on the side and # use it. If not, we just skip profiling and do a normal compile, que sera, sera. if [ $gcc_version != "10.2.0" ]; then can_profile=0 if [ -x "/usr/local/cluster/gcc-10.2.0/bin/gcc" ]; then export PATH="/usr/local/cluster/gcc-10.2.0/bin:$PATH" real_compiler=gcc can_profile=1 fi fi # Overlay ourselves and punt to a standard compile. if [ $can_profile -ne 1 ]; then exec $real_compiler $* fi # It is standard practice for automated (ie: not developer driven) test runs to be compiled as # 'library' and run as 'gtmtest'. Therefore, if we are running as library, we want to adjust # things so gcda files land under gtmtest. runtime_user=$USER if [ $USER = "library" ]; then runtime_user="gtmtest" fi # If we get here, we are building DEBUG & can profile # GCOV_PREFIX_STRIP lets us move the GCDA files easily. # (Re)Creating the gcda output dir and its ACL here is not ideal, # but if we don't, it's possible some GCDA files will be created during # build with a non 666 mode and might be unwritable by different userids in # a later test. export GCOV_PREFIX_STRIP=5 # We go ahead and create this directory here because later parts of # the build process run mumps code so we can't really leave it to the # test system. I think the gcc runtime would create it, but would not get the ACL # created, which could leave us with unwritable .gcda files later. # We use a timestamp on the output dir so if the build changes, it doesn't write to the wrong version time_stamp=`ls -ld /usr/library/$gtm_verno | \ gawk '{time=sprintf("%s_%02d_%s",toupper($6),$7,$8); gsub(":","",time); print time}'` profile_dir="/testarea1/${runtime_user}/coverage/${gtm_verno}_${time_stamp}/gcda" if [ ! -d $profile_dir ]; then mkdir -p $profile_dir fi chmod ugo+rx $profile_dir # we need ACLs because tests run as a number of different users. If this fails, punt # to standard compile. (This is our last chance to punt because after this we start fiddling argv) # We trash stderr because there's no point in punting to standard compile if we are going to break # the build with an error message anyway. if setfacl -d -m other::rw -m user::rw -m group::rw $profile_dir 2> /dev/null; then : else exec $real_compiler $* fi # Patch argv to include coverage options. Originally the gcda files were generated # in /usr/library/VXXX/dbg/obj, but that was blowing up /usr/library, so now they # are generated in /testarea1/$runtime_user/coverage/gcda. # The profile-prefix-path keeps the files from being renamed with weird '#' escapes. argv="$*" if [ \( $is_compile -eq 1 \) -a \( $is_debug_build -eq 1 \) -a \( $is_stop_list -eq 0 \) ]; then argv="-fprofile-prefix-path=/usr/library/${gtm_verno}/dbg/obj $argv" argv="-fprofile-dir=${profile_dir} $argv" argv="--coverage $argv" fi # If we are linking, we can always add -lgcov and this will be # harmless if none of the objects need those symbols if [ $is_link -eq 1 ]; then argv="$argv -lgcov" fi # Now that we have edited our command line, run it with the real compiler exec $real_compiler $argv } main $* fis-gtm-V7.0-005/sr_unix/crit_wake.c0000755000032200000250000000324414342376327016171 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include #include #include "gtm_limits.h" #include "io.h" #include "gtmsecshr.h" #include "secshr_client.h" #include "crit_wake.h" #include "send_msg.h" #include "have_crit.h" error_def(ERR_NOSUCHPROC); int crit_wake (sm_uint_ptr_t pid) { if (0 == kill(*pid, SIGALRM)) return 0; else if (ESRCH == errno) { send_msg(VARLSTCNT(5) ERR_NOSUCHPROC, 3, *pid, RTS_ERROR_LITERAL("wake")); return(ESRCH); } else assert(EINVAL != errno); # ifdef NOT_CURRENTLY_USED /* The code segment below basically disabled M LOCK wakeup via gtmsecshr (via send_mesg2gtmsecshr() call below). The * requisite gtmsecshr support is temporarily also being #ifdef'd out until the M LOCK wakeup mechanism used to wakeup * processes with different userids can be run outside of crit. At that point, this code will be re-enabled without * the crit-check below once again allowing improved wakeup times for differing userids without waiting for a 100ms * poll timer to expire. */ /* if you are in crit don't send, the other process's timer will wake it up any way */ if (0 != have_crit(CRIT_HAVE_ANY_REG)) return 0; return send_mesg2gtmsecshr(WAKE_MESSAGE, *pid, (char *)NULL, 0); # else return 0; # endif } fis-gtm-V7.0-005/sr_unix/ctrap_set.c0000755000032200000250000000470314342376327016206 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include "xfer_enum.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "deferred_events.h" #include "fix_xfer_entry.h" #include "op.h" #include "gtmio.h" #include "io.h" #include "gtmimagename.h" /* ------------------------------------------------------------------ * Set flags and transfer table for synchronous handling of ctrap. * Should be called only from set_xfer_handlers. * ------------------------------------------------------------------ */ GBLREF boolean_t ztrap_explicit_null; GBLREF dollar_ecode_type dollar_ecode; GBLREF volatile boolean_t dollar_zininterrupt; GBLREF volatile int4 outofband; GBLREF xfer_entry_t xfer_table[]; void ctrap_set(int4 ob_char) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(INTRPT_IN_EVENT_HANDLING == intrpt_ok_state); if ((((CTRLC == ob_char) ? ctrap : sighup) != outofband) || have_crit(CRIT_HAVE_ANY_REG | CRIT_IN_COMMIT) || dollar_zininterrupt || (jobinterrupt == (TREF(save_xfer_root_ptr))->ev_que.fl->outofband)) { /* not a good time, so save it */ TAREF1(save_xfer_root, ctrap).event_state = queued; SAVE_XFER_QUEUE_ENTRY(ctrap, 0); DBGDFRDEVNT((stderr, "%d %s: ctrap_set - ctrap queued - outofband: %d, trap: %d, intrpt: %d, crit: %d\n", __LINE__, __FILE__, outofband, ((0 < dollar_ecode.index) && (ETRAP_IN_EFFECT)), dollar_zininterrupt, have_crit(CRIT_HAVE_ANY_REG | CRIT_IN_COMMIT))); return; } DBGDFRDEVNT((stderr, "%d %s: ctrap_set - NOT deferred\n", __LINE__, __FILE__)); TAREF1(save_xfer_root, ctrap).param_val = ob_char; outofband = (CTRLC == ob_char) ? ctrap : sighup; DEFER_INTO_XFER_TAB; DBGDFRDEVNT((stderr, "%d %s: ctrap_set - pending xfer entries for ctrap - outofband: %d\n", __LINE__, __FILE__, outofband)); # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_ZTIM_EDGE == gtm_white_box_test_case_number)) DBGFPF((stderr, "# ctrap_set: set the xfer entries for ctrap\n")); # endif } fis-gtm-V7.0-005/sr_unix/ctrlc_handler.c0000755000032200000250000000176414342376327017032 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_signal.h" #ifdef GTM_PTHREAD # include "gtm_pthread.h" #endif #include "ctrlc_handler.h" #include "std_dev_outbndset.h" #include "have_crit.h" #include "deferred_events_queue.h" void ctrlc_handler(int sig) { int4 ob_char; int save_errno; FORWARD_SIG_TO_MAIN_THREAD_IF_NEEDED(sig); save_errno = errno; ob_char = (SIGINT == sig) ? CTRLC : CTRLD; /* borrowing CTRLD for SIGHUP */ std_dev_outbndset(ob_char); errno = save_errno; } fis-gtm-V7.0-005/sr_unix/ctrlc_handler.h0000755000032200000250000000107714342376327017034 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef CTRLC_HANDLER_INCLUDED #define CTRLC_HANDLER_INCLUDED void ctrlc_handler(int sig); #endif /* CTRLC_HANDLER_INCLUDED */ fis-gtm-V7.0-005/sr_unix/ctrlc_set.c0000755000032200000250000000516314342376327016205 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "xfer_enum.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "deferred_events.h" #include "fix_xfer_entry.h" #include "error_trap.h" #include "op.h" #include "gtmio.h" #include "io.h" #include "gtmimagename.h" /* ------------------------------------------------------------------ * Set flags and transfer table for synchronous handling of cntl-C. * Should be called only from set_xfer_handlers. * * Note: dummy parameter is for calling compatibility. * ------------------------------------------------------------------ */ GBLREF boolean_t ztrap_explicit_null; GBLREF dollar_ecode_type dollar_ecode; GBLREF volatile boolean_t ctrlc_on, dollar_zininterrupt; GBLREF volatile int4 outofband; GBLREF xfer_entry_t xfer_table[]; void ctrlc_set(int4 dummy_param) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(INTRPT_IN_EVENT_HANDLING == intrpt_ok_state); if (!(ctrlc_on && IS_MCODE_RUNNING)) { DBGDFRDEVNT((stderr, "%d %s: ctrlc_set - ctrlc outofband not enabled\n", __LINE__, __FILE__)); assert((pending == TAREF1(save_xfer_root, ctrlc).event_state) || ((active == TAREF1(save_xfer_root, ctrlc).event_state))); return; } if ((ctrlc != outofband) || have_crit(CRIT_HAVE_ANY_REG | CRIT_IN_COMMIT)) { /* not a good time, so save it */ TAREF1(save_xfer_root, ctrlc).event_state = queued; SAVE_XFER_QUEUE_ENTRY(ctrlc, 0); DBGDFRDEVNT((stderr, "%d %s: ctrlc_set - ctrlc queued - outofband: %d, trap: %d, intrpt: %d, crit: %d\n", __LINE__, __FILE__, outofband, ((0 < dollar_ecode.index) && (ETRAP_IN_EFFECT)), dollar_zininterrupt, have_crit(CRIT_HAVE_ANY_REG | CRIT_IN_COMMIT))); return; } DBGDFRDEVNT((stderr, "%d %s: ctrlc_set - NOT deferred\n", __LINE__, __FILE__)); TAREF1(save_xfer_root, ctrap).param_val = 0; outofband = ctrlc; DEFER_INTO_XFER_TAB; DBGDFRDEVNT((stderr, "%d %s: ctrlc_set - pending xfer entries for ctrlc\n", __LINE__, __FILE__)); # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_ZTIM_EDGE == gtm_white_box_test_case_number)) DBGFPF((stderr, "# ctrlc_set: set the xfer entries for ctrlc\n")); # endif } fis-gtm-V7.0-005/sr_unix/custom_errors_sample.txt0000644000032200000250000000076414342376327021066 0ustar librarygtcDBBMLCORRUPT DBDANGER DBFSYNCERR DBIOERR DSKNOSPCAVAIL GBLOFLOW GVDATAFAIL GVDATAGETFAIL ; This is currently reported as TPFAIL and invisible to the user. GVGETFAIL GVINCRFAIL GVKILLFAIL GVORDERFAIL GVPUTFAIL GVQUERYFAIL GVQUERYGETFAIL GVZTRIGFAIL ; This is currently reported as TPFAIL and invisible to the user. JNLACCESS JNLCLOSE JNLCLOSED JNLCNTRL JNLEXTEND JNLFILECLOSERR JNLFILEXTERR JNLFILOPN JNLFLUSH JNLFSYNCERR JNLOPNERR JNLRDERR JNLREAD JNLVSIZE JNLWRERR JRTNULLFAIL OUTOFSPACE TRIGDEFBAD fis-gtm-V7.0-005/sr_unix/cvtprot.c0000755000032200000250000000167314342376327015726 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "cvtprot.h" #define READ 4 #define WRITE 2 #define EXECUTE 1 int cvtprot(char *cp, short cnt) { int mask; /* a protection mask consists of 3 bits per access, R(ead), W(rite), and X(excute) */ mask = 0; for (;cnt > 0 ; cnt--, cp++) { switch (*cp) { case 'R': case 'r': mask |= READ; break; case 'W': case 'w': mask |= WRITE; break; case 'X': case 'x': mask |= EXECUTE; break; default: return -1; } } return mask; } fis-gtm-V7.0-005/sr_unix/cvttime.c0000755000032200000250000000105614342376327015673 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "cvttime.h" int4 cvttime(mval *src, int4 *tim) { /* this is a stub */ return 0; } fis-gtm-V7.0-005/sr_unix/daemon_crit.c0000755000032200000250000000106714342376327016506 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2004 Sanchez Computer Associates, 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. * * * ****************************************************************/ void daemon_crit(void); /* prototype to avoid warning */ void daemon_crit(void) { return; /* No longer used */ } fis-gtm-V7.0-005/sr_unix/db_ipcs_reset.c0000644000032200000250000002141214342376327017020 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 #include #include #include #include "gtm_unistd.h" #include "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gtmio.h" #include "gdsfhead.h" #include "db_header_conversion.h" #include "filestruct.h" #include "io.h" #include "iosp.h" #include "eintr_wrappers.h" #include "gtmsecshr.h" #include "secshr_client.h" #include "mu_rndwn_file.h" #include "ftok_sems.h" #include "ipcrmid.h" #include "gtmmsg.h" #include "dbfilop.h" #include "db_ipcs_reset.h" #include "jnl.h" #include "do_semop.h" #include "anticipatory_freeze.h" GBLREF uint4 process_id; GBLREF ipcs_mesg db_ipcs; GBLREF gd_region *gv_cur_region; GBLREF jnl_gbls_t jgbl; GBLREF jnlpool_addrs_ptr_t jnlpool; error_def (ERR_TEXT); error_def (ERR_CRITSEMFAIL); error_def (ERR_DBFILERR); error_def (ERR_FILEPARSE); /* mu_rndwn_file gets a database access control semaphore - this counterpart releases it for one region. This module is invoked * only by those processes that have previously done the mu_rndwn_file and hence guaranteed to hold the access control semaphore. * Because of this, we do not ask for ftok semaphore (ftok_sem_lock) as that would cause an out-of-order request * of locks. */ boolean_t db_ipcs_reset(gd_region *reg) { int status, semval, save_errno; uint4 ustatus; sgmnt_data_ptr_t csd; file_control *fc; unix_db_info *udi; gd_region *temp_region; sgmnt_data old_data; sgmnt_addrs *csa; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(reg); temp_region = gv_cur_region; /* save gv_cur_region wherever there is scope for it to be changed */ gv_cur_region = reg; /* dbfilop needs gv_cur_region */ udi = NULL; if (NULL != reg->dyn.addr->file_cntl) udi = FILE_INFO(reg); if ((NULL == udi) || (INVALID_SEMID == udi->semid)) { assert(!reg->open); gv_cur_region = temp_region; return FALSE; } assert(udi->grabbed_access_sem); csa = &udi->s_addrs; /* Normal callers of this function ensure we have standalone access to the db. An exception is if "mu_rndwn_file" * call did not complete cleanly (e.g. REQROLLBACK error issued there and later mu_int_ch calls this function). * In that case, return right away as we cannot reset standalone access when it was not cleanly obtained. */ if (NULL != csa->nl) { gv_cur_region = temp_region; return FALSE; } fc = reg->dyn.addr->file_cntl; fc->op = FC_OPEN; status = dbfilop(fc); gv_cur_region = temp_region; if (SS_NORMAL != status) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), status); return FALSE; } csd = !udi->fd_opened_with_o_direct ? &old_data : (sgmnt_data_ptr_t)(TREF(dio_buff)).aligned; DB_LSEEKREAD(udi, udi->fd, (off_t)0, csd, SGMNT_HDR_LEN, status); if (0 == memcmp(csd->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_upconv(csd); if (0 != status) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), status); CLOSEFILE_RESET(udi->fd, status); /* resets "udi->fd" to FD_INVALID */ if (0 != status) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), status); return FALSE; } csa->hdr = csd; /* needed for DB_LSEEKWRITE (done later) if/when instance is frozen */ assert((udi->semid == csd->semid) || (INVALID_SEMID == csd->semid)); semval = semctl(udi->semid, DB_COUNTER_SEM, GETVAL); /* Get the counter semaphore's value */ /* Since we have standalone access to the db (caller ensures this) we do not need to worry about whether the * ftok or access counter got halted or not. */ assert(DB_COUNTER_SEM_INCR <= semval); if (DB_COUNTER_SEM_INCR < semval) { assert(jgbl.onlnrlbk); /* everyone else will have total standalone access and hence no one else can be attached */ assert(!reg->read_only); /* ONLINE ROLLBACK must be a read/write process */ if (!reg->read_only) { if (0 != (save_errno = do_semop(udi->semid, DB_COUNTER_SEM, -DB_COUNTER_SEM_INCR, SEM_UNDO))) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_TEXT("db_ipcs_reset - write semaphore release"), save_errno); return FALSE; } assert(1 == (semval = semctl(udi->semid, DB_CONTROL_SEM, GETVAL))); if (0 != (save_errno = do_semop(udi->semid, DB_CONTROL_SEM, -1, SEM_UNDO))) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_TEXT("db_ipcs_reset - access control semaphore release"), save_errno); return FALSE; } } /* Since the semval is at least 2, we should NOT remove the semaphore as the other process that still exists * will encounter an error during its gds_rundown. During the gds_rundown of the other process it will realize * that it is the only attached process and will take care of we_are_last_writer cases in gds_rundown. */ } else { /* We are the only one. Remove the semaphore. Logic in db_init knows to handle when the semaphore is removed * and will retry by creating a new one. But, it is important the semid/shmid fields in the file header is reset * BEFORE removing the semaphore as otherwise the waiting process in db_init will notice the semaphore removal * first and will read the file header and can potentially notice the stale semid/shmid values. */ if (!reg->read_only DEBUG_ONLY(&& !TREF(gtm_usesecshr))) { csd->semid = INVALID_SEMID; csd->shmid = INVALID_SHMID; csd->gt_sem_ctime.ctime = 0; csd->gt_shm_ctime.ctime = 0; if (0 == memcmp(csd->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv(csd); DB_LSEEKWRITE(csa, udi, udi->fn, udi->fd, (off_t)0, csd, SGMNT_HDR_LEN, status); if (0 != status) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), status); CLOSEFILE_RESET(udi->fd, status); /* resets "udi->fd" to FD_INVALID */ if (0 != status) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), status); return FALSE; } } else { db_ipcs.open_fd_with_o_direct = udi->fd_opened_with_o_direct; db_ipcs.semid = INVALID_SEMID; db_ipcs.shmid = INVALID_SHMID; db_ipcs.gt_sem_ctime = 0; db_ipcs.gt_shm_ctime = 0; if (!get_full_path((char *)DB_STR_LEN(reg), db_ipcs.fn, (unsigned int *)&db_ipcs.fn_len, GTM_PATH_MAX, &ustatus)) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_FILEPARSE, 2, DB_LEN_STR(reg), ustatus); return FALSE; } db_ipcs.fn[db_ipcs.fn_len] = 0; WAIT_FOR_REPL_INST_UNFREEZE_SAFE(csa); if (!csa->read_only_fs && !csd->read_only) { status = send_mesg2gtmsecshr(FLUSH_DB_IPCS_INFO, 0, (char *)NULL, 0); if (0 != status) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), status); CLOSEFILE_RESET(udi->fd, status); /* resets "udi->fd" to FD_INVALID */ if (0 != status) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), status); return FALSE; } } } if (0 != sem_rmid(udi->semid)) { save_errno = errno; gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_TEXT("db_ipcs_reset - sem_rmid"), save_errno); return FALSE; } } udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; CLOSEFILE_RESET(udi->fd, status); /* resets "udi->fd" to FD_INVALID */ if (0 != status) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), status); /* Since we created the ftok semaphore in mu_rndwn_file, we release/remove it now. But, since we are exiting, we * do not WAIT for the ftok semaphore if we did not get it in one shot (IPC_NOWAIT). The process that holds the * ftok will eventually release it and so we are guaranteed that when the last process leaves the database, it will * remove the ftok semaphore as well. This means, not able to lock or release the ftok semaphore is not treated * as an error condition. */ if (ftok_sem_lock(reg, TRUE)) /* immediate=TRUE because we don't want to wait while holding access semaphore */ { assert(udi->counter_ftok_incremented || jgbl.onlnrlbk || INST_FREEZE_ON_ERROR_POLICY); ftok_sem_release(reg, udi->counter_ftok_incremented, TRUE); } udi->semid = INVALID_SEMID; udi->shmid = INVALID_SHMID; udi->gt_sem_ctime = 0; udi->gt_shm_ctime = 0; return TRUE; } fis-gtm-V7.0-005/sr_unix/db_ipcs_reset.h0000644000032200000250000000104014342376327017020 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef DB_IPCS_RESET_H #define DB_IPCS_RESET_H boolean_t db_ipcs_reset(gd_region *reg); #endif fis-gtm-V7.0-005/sr_unix/db_read.h0000755000032200000250000000122014342376327015576 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef DB_READ_H_INCLUDED #define DB_READ_H_INCLUDED int4 db_read (int4 fdesc, off_t fptr, sm_uc_ptr_t fbuff, size_t fbuff_len); #endif fis-gtm-V7.0-005/sr_unix/db_snapshot.h0000644000032200000250000001434714342376327016535 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2020 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. * * * ****************************************************************/ #ifndef DB_SNAPSHOT_H #define DB_SNAPSHOT_H #include "gtm_limits.h" #include "gdsroot.h" #define SNAPSHOT_HDR_LABEL "SNAPSHOTV1" #define SET_FAST_INTEG(X) ((X)->proc_property |= 0x0001) #define SET_NORM_INTEG(X) ((X)->proc_property &= 0xfffe) #define FASTINTEG_IN_PROG(X) ((X)->proc_property & 0x0001) /* Possible states when snapshot is being init'ed */ typedef enum { SNAPSHOT_NOT_INITED = 0, SNAPSHOT_SHM_ATTACH_FAIL, SHADOW_FIL_OPEN_FAIL, BEFORE_SHADOW_FIL_CREAT, AFTER_SHADOW_FIL_CREAT, AFTER_SHM_CREAT, SNAPSHOT_INIT_DONE, SS_NUM_PROC_STATUS } ss_proc_status; typedef struct snapshot_info_struct { uint4 ss_pid; /* PID of the process doing the snapshot */ uint4 db_blk_size; /* Database block size */ trans_num snapshot_tn; /* Transaction number at which the snapshot started */ block_id free_blks; /* Free blocks at the time of snapshot */ block_id total_blks; /* Total blocks at the time of snapshot */ char shadow_file[GTM_PATH_MAX]; /* Temporary file that will contain the before images */ char filler[7]; /* so as to make 8-byte allignment explicit */ block_id shadow_vbn; /* Starting VBN of the shadow file */ long ss_shmid; /* Shared memory identifier created by snapshot initiating process */ block_id ss_shmsize; /* Size of the shared memory newly created by snapshot initiating process */ } snapshot_info_t; typedef struct shm_snapshot_struct { snapshot_info_t ss_info; volatile uint4 failure_errno; /* the status code of the failure. */ volatile pid_t failed_pid; /* the process_id that encountered the snapshot failure */ int in_use; boolean_t preserve_snapshot; global_latch_t bitmap_latch; /* latch to be passed on to add_inter while modifying the shadow bitmap */ trans_num ss_tn_count; /* count of transactions after the snapshot started */ sgmnt_data shadow_file_header; } shm_snapshot_t; typedef shm_snapshot_t *shm_snapshot_ptr_t; /* Utilities (MUPIP, DSE, LKE) specific snapshot structure */ typedef struct util_snapshot_struct { unsigned char *master_map; sgmnt_data_ptr_t header; gtm_uint64_t native_size; } util_snapshot_t; typedef struct snapshot_context_struct { int shdw_fd; /* open file descriptor for the shadow file */ long nl_shmid; long attach_shmid; int ss_shmcycle; block_id total_blks; uint4 failure_errno; shm_snapshot_ptr_t ss_shm_ptr; sm_uc_ptr_t start_shmaddr; void *bitmap_addr; block_id shadow_vbn; char shadow_file[GTM_PATH_MAX]; ss_proc_status cur_state; /* the property of process triggering snapshot. At present only last bit is used to indicate property of integ process: * 0x0000: Normal Integ, 0x0001:Fast Integ */ uint4 proc_property; } snapshot_context_t; typedef struct snapshot_filehdr_struct { char label[SIZEOF(SNAPSHOT_HDR_LABEL) - 1]; snapshot_info_t ss_info; int4 shadow_file_len; unsigned char filler[964]; } snapshot_filhdr_t; typedef snapshot_filhdr_t *snapshot_filhdr_ptr_t; typedef util_snapshot_t *util_snapshot_ptr_t; typedef snapshot_context_t *snapshot_context_ptr_t; #define DEFAULT_INIT_SS_CTX(lcl_ss_ctx) \ { \ lcl_ss_ctx->shdw_fd = FD_INVALID; \ lcl_ss_ctx->nl_shmid = INVALID_SHMID; \ lcl_ss_ctx->attach_shmid = INVALID_SHMID; \ lcl_ss_ctx->ss_shmcycle = 0; \ lcl_ss_ctx->total_blks = 0; \ lcl_ss_ctx->failure_errno = 0; \ lcl_ss_ctx->ss_shm_ptr = NULL; \ lcl_ss_ctx->start_shmaddr = lcl_ss_ctx->bitmap_addr = NULL; \ lcl_ss_ctx->shadow_vbn = 0; \ memset(lcl_ss_ctx->shadow_file, 0, GTM_PATH_MAX); \ lcl_ss_ctx->cur_state = SNAPSHOT_NOT_INITED; \ } #define SS_DEFAULT_INIT_INFO(ss_ptr) \ { \ ss_ptr->ss_info.ss_pid = 0; \ ss_ptr->ss_info.snapshot_tn = 0; \ ss_ptr->ss_info.db_blk_size = 0; \ ss_ptr->ss_info.free_blks = 0; \ ss_ptr->ss_info.total_blks = 0; \ memset(ss_ptr->ss_info.shadow_file, 0, GTM_PATH_MAX); \ ss_ptr->ss_info.ss_shmid = INVALID_SHMID; \ ss_ptr->ss_info.ss_shmsize = 0; \ } #define SS_DEFAULT_INIT_POOL(ss_shm_ptr) \ { \ SS_DEFAULT_INIT_INFO(ss_shm_ptr); \ ss_shm_ptr->failure_errno = 0; \ ss_shm_ptr->failed_pid = 0; \ ss_shm_ptr->in_use = 0; \ ss_shm_ptr->preserve_snapshot = FALSE; \ ss_shm_ptr->ss_tn_count = 0; \ } #define MAX_SNAPSHOTS 1 #define SNAPSHOT_HDR_SIZE SIZEOF(snapshot_filhdr_t) #define SINGLE_SHM_SNAPSHOT_SIZE ROUND_UP(SIZEOF(shm_snapshot_t), OS_PAGE_SIZE) #define SS_CTX_CAST(X) ((snapshot_context_ptr_t)(X)) #define BLKS_PER_WORD 32 #define SS_GETSTARTPTR(CSA) (((sm_uc_ptr_t)CSA->shmpool_buffer) + SHMPOOL_BUFFER_SIZE) #define SS_IDX2ABS(CSA, N) ((shm_snapshot_ptr_t)(SS_GETSTARTPTR(CSA) + SNAPSHOT_SECTION_SIZE)) #ifdef DEBUG # define DBG_ENSURE_PTR_WITHIN_SS_BOUNDS(CSA, PTR) \ assert((PTR) >= SS_GETSTARTPTR(CSA) && ((PTR) < SS_GETSTARTPTR(CSA) + SHMPOOL_SECTION_SIZE)) #else # define DBG_ENSURE_PTR_WITHIN_SS_BOUNDS(CSA, PTR) #endif #define SNAPSHOT_SECTION_SIZE (ROUND_UP((MAX_SNAPSHOTS * SINGLE_SHM_SNAPSHOT_SIZE), OS_PAGE_SIZE)) #define SHMPOOL_SECTION_SIZE (ROUND_UP((SHMPOOL_BUFFER_SIZE + SNAPSHOT_SECTION_SIZE), OS_PAGE_SIZE)) boolean_t ss_initiate(gd_region *, util_snapshot_ptr_t, snapshot_context_ptr_t *, boolean_t, char *); void ss_release(snapshot_context_ptr_t *); boolean_t ss_get_block(sgmnt_addrs *, block_id, sm_uc_ptr_t); void ss_read_block(snapshot_context_ptr_t, block_id, sm_uc_ptr_t); boolean_t ss_write_block(sgmnt_addrs *, block_id, cache_rec_ptr_t, sm_uc_ptr_t, snapshot_context_ptr_t); void ss_set_shdw_bitmap(sgmnt_addrs *, snapshot_context_ptr_t, block_id); boolean_t ss_chk_shdw_bitmap(sgmnt_addrs *, snapshot_context_ptr_t, block_id); boolean_t ss_create_context(snapshot_context_ptr_t, int); boolean_t ss_destroy_context(snapshot_context_ptr_t ); void ss_anal_shdw_file(char *, int); void ss_initiate_call_on_signal(void); #endif fis-gtm-V7.0-005/sr_unix/db_write.h0000755000032200000250000000134414342376327016024 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef DB_WRITE_H_INCLUDED #define DB_WRITE_H_INCLUDED int4 db_write (int4 fdesc, off_t fptr, sm_uc_ptr_t fbuff, size_t fbuff_len); int4 db_write_ntp (int4 fdesc, off_t fptr, sm_uc_ptr_t fbuff, size_t fbuff_len); #endif fis-gtm-V7.0-005/sr_unix/db_write_eof_block.c0000644000032200000250000000307614342376327020023 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016 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 "gdsroot.h" #include "gdsblk.h" #include "gdsbml.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblkops.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "db_write_eof_block.h" #include "gtmio.h" #include "anticipatory_freeze.h" /* #GTM_THREAD_SAFE : The below function db_write_eof_block is thread-safe */ int db_write_eof_block(unix_db_info *udi, int fd, int blk_size, off_t offset, dio_buff_t *diobuff) { int status; char *buff; sgmnt_addrs *csa; boolean_t free_needed; if ((NULL != udi) && udi->fd_opened_with_o_direct) { DIO_BUFF_EXPAND_IF_NEEDED(udi, blk_size, diobuff); buff = diobuff->aligned; free_needed = FALSE; } else { buff = (char *)malloc(blk_size); free_needed = TRUE; } memset(buff, 0, blk_size); if (NULL != udi) { csa = &udi->s_addrs; assert(fd == udi->fd); DB_LSEEKWRITE(csa, udi, udi->fn, fd, offset, buff, blk_size, status); } else DB_LSEEKWRITE(NULL, ((unix_db_info *)NULL), NULL, fd, offset, buff, blk_size, status); if (free_needed) free(buff); return status; } fis-gtm-V7.0-005/sr_unix/db_write_eof_block.h0000644000032200000250000000164014342376327020023 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016 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. * * * ****************************************************************/ #ifndef DB_WRITE_EOF_BLOCK_H_INCLUDED #define DB_WRITE_EOF_BLOCK_H_INCLUDED /* Reduction in free blocks after truncating from a to b total blocks: a = old_total (larger), b = new_total */ # define DELTA_FREE_BLOCKS(a, b) ((a - b) - (DIVIDE_ROUND_UP(a, BLKS_PER_LMAP) - DIVIDE_ROUND_UP(b, BLKS_PER_LMAP))) int db_write_eof_block(unix_db_info *udi, int fd, int blk_size, off_t offset, dio_buff_t *diobuff); #endif fis-gtm-V7.0-005/sr_unix/dbfilop.c0000644000032200000250000001305114342376327015632 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include #include #include #ifdef __MVS__ #include "gtm_zos_io.h" #endif #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "db_header_conversion.h" #include "filestruct.h" #include "eintr_wrappers.h" #include "gtmio.h" #include "iosp.h" #include "is_file_identical.h" #include "dbfilop.h" #include "anticipatory_freeze.h" #include "jnl.h" #include "get_fs_block_size.h" GBLREF gd_region *gv_cur_region; error_def(ERR_DBFILERDONLY); error_def(ERR_DBFILOPERR); error_def(ERR_DBNOTGDS); error_def(ERR_DBOPNERR); error_def(ERR_DBPREMATEOF); error_def(ERR_TEXT); uint4 dbfilop(file_control *fc) { unix_db_info *udi; struct stat stat_buf; int4 save_errno; int fstat_res; off_t offset; gd_segment *seg; sgmnt_addrs *csa; ZOS_ONLY(int realfiletag;) udi = FC2UDI(fc); csa = &udi->s_addrs; switch(fc->op) { case FC_READ: assert(fc->op_pos > 0); /* gt.m uses the vms convention of numbering the blocks from 1 */ offset = (off_t)(fc->op_pos - 1) * DISK_BLOCK_SIZE; DB_LSEEKREAD(udi, udi->fd, offset, fc->op_buff, fc->op_len, save_errno); if (0 != save_errno) { if (-1 == save_errno) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBPREMATEOF, 2, LEN_AND_STR(udi->fn)); else RTS_ERROR_CSA_ABT(csa, VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(udi->fn), save_errno); } if (1 == fc->op_pos) { if (0 != memcmp(fc->op_buff, GDS_LABEL, GDS_LABEL_SZ - 3)) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBNOTGDS, 2, LEN_AND_STR(udi->fn)); if ((0 == memcmp(fc->op_buff, GDS_LABEL, GDS_LABEL_SZ - 1)) /* current GDS */ || (0 == memcmp(fc->op_buff, V6_GDS_LABEL, GDS_LABEL_SZ - 1))) /* V6 GDS*/ { if(0 == memcmp(fc->op_buff, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_upconv((sgmnt_data_ptr_t)fc->op_buff); if (offsetof(sgmnt_data, minor_dbver) < fc->op_len) CHECK_DB_ENDIAN((sgmnt_data_ptr_t)fc->op_buff, strlen(udi->fn), udi->fn); /* BYPASSOK */ } } break; case FC_WRITE: assertpro((1 != fc->op_pos) || (((0 == memcmp(fc->op_buff, GDS_LABEL, GDS_LABEL_SZ - 1)) || (0 == memcmp(fc->op_buff, V6_GDS_LABEL, GDS_LABEL_SZ - 1))) && (0 != ((sgmnt_data_ptr_t)fc->op_buff)->acc_meth))); assert((1 != fc->op_pos) || (fc->op_len <= SIZEOF_FILE_HDR(fc->op_buff))); assert(!gv_cur_region->read_only); offset = (off_t)(fc->op_pos - 1) * DISK_BLOCK_SIZE; if (0 == memcmp(fc->op_buff, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv((sgmnt_data_ptr_t)(fc->op_buff)); DB_LSEEKWRITE(csa, udi, udi->fn, udi->fd, offset, fc->op_buff, fc->op_len, save_errno); if (0 != save_errno) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(udi->fn), save_errno); break; case FC_OPEN: gv_cur_region->read_only = FALSE; /* maintain csa->read_write simultaneously */ seg = gv_cur_region->dyn.addr; csa->read_write = TRUE; /* maintain reg->read_only simultaneously */ csa->orig_read_write = TRUE; /* maintain orig_read_write at same time as read_write */ #ifdef DEBUG if (WBTEST_ENABLED(WBTEST_OPENFILE_DB)) { udi->fd = FD_INVALID; errno = gtm_white_box_test_case_count ? gtm_white_box_test_case_count : EPERM; } else #endif OPENFILE_DB((char *)seg->fname, O_RDWR, udi, seg); if (FD_INVALID == udi->fd) { save_errno = errno; OPENFILE_DB((char *)seg->fname, O_RDONLY, udi, seg); if (FD_INVALID == udi->fd) return ERR_DBOPNERR; gv_cur_region->read_only = TRUE; /* maintain csa->read_write simultaneously */ csa->read_write = FALSE; /* maintain reg->read_only simultaneously */ csa->orig_read_write = FALSE; /* maintain orig_read_write at same time as read_write */ } FSTAT_FILE(udi->fd, &stat_buf, fstat_res); if (-1 == fstat_res) return ERR_DBOPNERR; if (gv_cur_region->read_only && !((EPERM == save_errno) || (EACCES == save_errno))) { if (!IS_GTM_IMAGE) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_DBFILERDONLY, 3, LEN_AND_STR(seg->fname), stat_buf.st_mode & 0x1FF, save_errno); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_DBFILERDONLY, 3, LEN_AND_STR(seg->fname), stat_buf.st_mode & 0x1FF, save_errno); } # ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(udi->fd, TAG_BINARY, &realfiletag)) TAG_POLICY_SEND_MSG((char_ptr_t)gv_cur_region->dyn.addr->fname, errno, realfiletag, TAG_BINARY); # endif set_gdid_from_stat(&udi->fileid, &stat_buf); udi->raw = (S_ISCHR(stat_buf.st_mode) || S_ISBLK(stat_buf.st_mode)); udi->fn = (char *)gv_cur_region->dyn.addr->fname; if (seg->full_blkwrt) csa->fullblockwrite_len = get_fs_block_size(udi->fd); break; case FC_CLOSE: CLOSEFILE_RESET(udi->fd, save_errno); /* resets "udi->fd" to FD_INVALID */ if (0 != save_errno) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(udi->fn), save_errno); udi->fd_opened_with_o_direct = FALSE; break; default: assertpro(FALSE && fc->op); } return SS_NORMAL; } fis-gtm-V7.0-005/sr_unix/dbinit_ch.c0000755000032200000250000001243214342376327016143 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_ipc.h" #include #include #include #include "gtm_unistd.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "error.h" #include "filestruct.h" #include "gvcst_protos.h" #include "jnl.h" #include "do_semop.h" #include "mmseg.h" #include "ipcrmid.h" #include "util.h" #include "ftok_sems.h" #include "gtmimagename.h" #include "gtmio.h" #include "have_crit.h" GBLREF gd_region *db_init_region; error_def(ERR_VERMISMATCH); CONDITION_HANDLER(dbinit_ch) { START_CH(TRUE); db_init_err_cleanup(FALSE); NEXTCH } void db_init_err_cleanup(boolean_t retry_dbinit) { unix_db_info *udi; gd_segment *seg; sgmnt_addrs *csa; int rc, lcl_new_dbinit_ipc; boolean_t ftok_counter_halted, access_counter_halted, decrement_ftok_counter; /* Here, we can not rely on the validity of csa->hdr because this function can be triggered anywhere in db_init().Because * we don't have access to file header, we can not know if counters are disabled so we go by our best guess, not disabled, * during cleanup. */ assert(NULL != db_init_region); seg = db_init_region->dyn.addr; udi = NULL; if (NULL != seg->file_cntl) udi = FILE_INFO(db_init_region); if (NULL != udi) { if (FD_INVALID != udi->fd && !retry_dbinit) CLOSEFILE_RESET(udi->fd, rc); /* resets "udi->fd" to FD_INVALID */ assert(FD_INVALID == udi->fd || retry_dbinit); csa = &udi->s_addrs; # ifdef _AIX if ((NULL != csa->hdr) && (dba_mm == db_init_region->dyn.addr->acc_meth)) { assert((NULL != csa->db_addrs[1]) && (csa->db_addrs[1] > csa->db_addrs[0])); munmap((caddr_t)csa->db_addrs[0], (size_t)(csa->db_addrs[1] - csa->db_addrs[0])); } # endif if (NULL != csa->jnl) { free(csa->jnl); csa->jnl = NULL; } /* If shared memory is not available or if this is a VERMISMATCH error situation (where we do not know the exact * position of csa->nl->ftok_counter_halted or if it even exists in the other version), we have to be pessimistic * and assume the counters are halted. This avoids prematurely removing the semaphores. */ if ((NULL != csa->nl) && ((int)ERR_VERMISMATCH != SIGNAL)) { ftok_counter_halted = csa->nl->ftok_counter_halted; access_counter_halted = csa->nl->access_counter_halted; if (csa->now_crit) { /* Ensure that we don't hold crit before detaching shared memory */ assert(!csa->now_crit); rel_crit(db_init_region); } shmdt((caddr_t)csa->nl); csa->nl = (node_local_ptr_t)NULL; } else { ftok_counter_halted = TRUE; access_counter_halted = TRUE; } if (udi->shm_created && (INVALID_SHMID != udi->shmid)) { assert(INVALID_SHMID == csa->mlkhash_shmid); shm_rmid(udi->shmid); udi->shmid = INVALID_SHMID; udi->shm_created = FALSE; } if (udi->sem_created && (INVALID_SEMID != udi->semid)) { sem_rmid(udi->semid); udi->semid = INVALID_SEMID; udi->sem_created = FALSE; udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; } if (udi->counter_acc_incremented && !access_counter_halted) { assert((INVALID_SEMID != udi->semid) && !db_init_region->read_only); /* decrement the read-write sem */ do_semop(udi->semid, DB_COUNTER_SEM, -DB_COUNTER_SEM_INCR, SEM_UNDO | IPC_NOWAIT); udi->counter_acc_incremented = FALSE; } if (udi->grabbed_access_sem) { do_semop(udi->semid, DB_CONTROL_SEM, -1, SEM_UNDO | IPC_NOWAIT); /* release the startup-shutdown sem */ udi->grabbed_access_sem = FALSE; } decrement_ftok_counter = udi->counter_ftok_incremented ? (ftok_counter_halted ? DECR_CNT_SAFE : DECR_CNT_TRUE) : DECR_CNT_FALSE; if (udi->grabbed_ftok_sem) { ftok_sem_release(db_init_region, decrement_ftok_counter, TRUE); assert(FALSE == udi->counter_ftok_incremented); } else if (udi->counter_ftok_incremented) do_semop(udi->ftok_semid, DB_COUNTER_SEM, -DB_COUNTER_SEM_INCR, SEM_UNDO | IPC_NOWAIT); /* Below reset needed for "else if" case above but do it for "if" case too (in pro) just in case */ udi->counter_ftok_incremented = FALSE; udi->grabbed_ftok_sem = FALSE; if (!IS_GTCM_GNP_SERVER_IMAGE && !retry_dbinit) /* gtcm_gnp_server reuses file_cntl */ { free(seg->file_cntl->file_info); free(seg->file_cntl); seg->file_cntl = NULL; } } /* Enable interrupts in case we are here with intrpt_ok_state == INTRPT_IN_GVCST_INIT due to an rts error. * 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 gvcst_init.c (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 (!retry_dbinit) ENABLE_INTERRUPTS(INTRPT_IN_GVCST_INIT, INTRPT_OK_TO_INTERRUPT); } fis-gtm-V7.0-005/sr_unix/dec_err.c0000755000032200000250000000272414342376327015626 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include "fao_parm.h" #include "error.h" #include "msg.h" #include "util.h" #include "util_out_print_vaparm.h" #include "gtmmsg.h" GBLREF bool dec_nofac; void dec_err(uint4 argcnt, ...) { va_list var; uint4 i, j, count, err; const err_ctl *ec; const err_msg *em; char msgbuff[OUT_BUFF_SIZE]; mstr msgstr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; util_out_print(0, RESET, 0); /* reset the buffer */ VAR_START(var, argcnt); assert (argcnt >= 1); err = va_arg(var, uint4); ec = err_check(err); em = NULL; if (ec) GET_MSG_INFO(err, ec, em); msgstr.addr = msgbuff; msgstr.len = SIZEOF(msgbuff); gtm_getmsg(err, &msgstr); if (!em) util_out_print(msgstr.addr, FLUSH, 1, err); else { argcnt--; if (argcnt) { count = va_arg(var, int4); assert (count <= argcnt); } else count = 0; util_out_print_vaparm(msgstr.addr, FLUSH, var, count); va_end(TREF(last_va_list_ptr)); } va_end(var); } fis-gtm-V7.0-005/sr_unix/decomment.m0000644000032200000250000002600714342376327016205 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2010-2018 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Part of gengtmdeftypes. ; ; Routine to remove C comments from a C routine or header file. File to read is specified ; on command line (e.g "$gtm_dist/mumps -run file-to-uncomment.c"). Uncommented ; version of file is written to stdout and can be piped wherever. Note this is sufficient ; to decomment OUR (GT.M source code) code but may need additions to be truly general purpose. ; Set TRUE=1,FALSE=0 ; Set infile=$ZCmdline Do:(""=infile) . Write "Missing parm: file to operate on",! . Halt ; ; Initialization - simple token types ; Set TKEOL=1 ; End of line Set TKEOF=2 ; End of file Set TKDQUOTE=3 ; Double quote '"' Set TKCOMSTRT=4 ; Comment start "/*" Set TKCOMEND=5 ; Comment end "*/" Set TKSLASH=6 ; Potential piece of comment start/end Set TKASTERISK=7 ; Potential piece of comment start/end Set TKBACKSLASH=8 ; Character escape Set TKOTHER=9 ; Everything else than what we care about ; Set CharScn("*")=TKASTERISK Set CharScn("\")=TKBACKSLASH Set CharScn("""")=TKDQUOTE Set CharScn("/")=TKSLASH ; ; Open file make sure exists ; Open infile:Readonly Use infile Set inline=1 Set (CommentMode,QuoteMode,NextTokEol)=FALSE ; ; Prime the tokenizer pump ; Set (inbuf,outbuf,token,dirtoken,tokenval,dirtokenval)="" Do GetToken(FALSE) ; Set tokens - neither of the "fake" tokens initialized earlier need flushing Do GetToken(FALSE) ; Set director token (next) For Quit:TKEOF=token Do . ; . ; If this char is supposed to be escaped because of a previous backslash, make it type TKOTHER so it is otherwise ignored . ; from a parsing standpoint.. . ; . If TKEOL=token Do ; End of line - Next! . . Do GetToken(TRUE) . Else If TKBACKSLASH=token,TKEOL'=dirtoken Do . . Do GetToken('CommentMode) . . ; . . ; We have a new char to inflict on ourselves. But this char has been "escaped" so any special meaning . . ; it would ordinarily have disappears - just set its token type to TKOTHER so it plays nice with everything else. . . ; . . Set:(TKEOF'=token) token=TKOTHER . Else If 'QuoteMode,TKCOMSTRT=token Do ; Entering comment mode (so long as not already in quote mode) . . Do:(CommentMode) Error("INTERROR","F","Comment mode already on when entering comment mode") . . Set comstart=inline,CommentMode=TRUE . . Do GetToken(FALSE) . . For Quit:((TKEOF=token)!(TKCOMEND=token)) Do . . . Do GetToken(FALSE) . . . Do:((TKBACKSLASH=token)&(TKEOL'=dirtoken)) . . . . Do GetToken(FALSE) . . . . Set:((TKEOF'=token)&(TKEOL'=token)) token=TKOTHER . . Do:(TKEOF=token) Error("COMENDNF","E","Hit end of routine while looking for end of comment started at line "_comstart) . . Do GetToken(FALSE) . . Set CommentMode=FALSE . Else If TKCOMEND=token Do Error("FNDCOMNICM","F","Located end-of-comment token in line "_inline_" while not in comment mode") . Else If 'CommentMode,TKDQUOTE=token Do ; Entering quoted text mode (ignored if already in comment mode) . . Do:(QuoteMode) Error("INTERROR","F","Quote mode already on when entering quote mode") . . Set QuoteMode=TRUE . . Do GetToken(TRUE) . . Do:((TKBACKSLASH=token)&(TKEOL'=dirtoken)) . . . Do GetToken(TRUE) . . . Set:((TKEOF'=token)&(TKEOL'=token)) token=TKOTHER . . Set done=FALSE . . ; . . ; On each loop iteration, there are two possibilities if a quote is detected: . . ; 1. If the director char is also a quote, then we do not leave quote mode but do push the scan pointer to . . ; the char following the director quote. . . ; 2. If the director char is NOT a quote, quote mode is done. . . ; . . For Quit:(done!(TKEOF=token)!(TKEOL=token)) Do . . . If TKDQUOTE=token Do . . . . ; . . . . ; We found a potentially quote ending quote. But if the next token is also a quote, then . . . . ; this is an escaped quote and not an ending quote. . . . . ; . . . . If TKDQUOTE=dirtoken Do . . . . . Do GetToken(TRUE) ; Not and ending quote, just eat the second one and continue scan . . . . . Do:((TKBACKSLASH=token)&(TKEOL'=dirtoken)) . . . . . . Do GetToken(TRUE) . . . . . . Set:((TKEOF'=token)&(TKEOL'=token)) token=TKOTHER . . . . Else Do . . . . . Set done=TRUE ; Else, this is an ending quote - set loop terminator . . . . . Set QuoteMode=FALSE ; .. and exit quote mode . . . Else Do GetToken(TRUE) . . Do:(TKEOF=token) Error("CLOSQUOTNF","E","Routine ended while searching for closing quote") . . Do:(TKEOL=token) Error("LINENDQT","E","Line ("_inline_") ended while searching for closing quote") . . Do GetToken(TRUE) . Else Do GetToken(TRUE) ; Do:(""'=outbuf) FlushOutbuf ; Wee bit 'o cleanup Close inbuf,outbuf Quit ; ; Routine to "tokenize" the input file. These are not compiler-tokens but simplistic ; comment-detecting removal tokens so are much coarser grain. Not a lot of care about ; anything except that which allows comments across lines to be removed while dealing ; appropriately with quotes and escaped chars. ; ; Argument tells whether to flush the current token to the output buffer or not before ; it gets replaced. ; GetToken(flush) New done,seenchr,prevtoken Set done=FALSE Quit:(TKEOF=token) TKEOF Do:(flush) ; Buffer output token and if ends line, write it . Set outbuf=outbuf_tokenval . Do:(TKEOL=token) FlushOutbuf Do:(TKEOF=dirtoken) . ; . ; Simple case where we are out of input . ; . Set token=TKEOF . Set tokenval="" . Set done=TRUE Quit:done ; ; Else we need the next token ; Set prevtoken=token Set token=dirtoken Set tokenval=dirtokenval ; ; If our last inbuf reading was ended by endofline (NextTokEol is true) meaning we returned ; whatever we got (if anything) before that, take care of that now. ; Do:NextTokEol . Set dirtoken=TKEOL . Set dirtokenval="" . Set NextTokEol=FALSE . Set done=TRUE Quit:done ; ; Scan to create next director token/val ; Set dirtokenval="" Do:(""=inbuf) . If $ZEof Do ; Oops, at EOF with nothing read . . Set dirtoken=TKEOF . . Set dirtokenval="" . . Set done=TRUE . Else Do ; Read a new line - still might detect EOF but might get lucky too! . . Read inbuf . . If $ZEof Do . . . If ""=dirtokenval Set dirtoken=TKEOF . . . Set done=TRUE . . Set inline=inline+1 ; ; Quick pre-check for simple blank (null) line if not already done ; Do:('done&(""=inbuf)) ; Null line is just a TKEOL return . Set dirtoken=TKEOL . Set dirtokenval="" . Set done=TRUE Quit:done ; Processing already complete - bypass scan ; ; Scan input line for token-ending chars ; For scnp=1:1:$ZLength(inbuf) Quit:done Do . Set chr=$ZExtract(inbuf,scnp) . Do:(0<$Data(CharScn(chr))) . . If TKASTERISK=CharScn(chr) Do ; Possible end of comment . . . Set chr2=$ZExtract(inbuf,scnp+1) . . . Do:(TKSLASH=$Get(CharScn(chr2),TKOTHER)) . . . . ; . . . . ; We have an end-of-comment token. Stop the scan here if we have scanned chars to . . . . ; return the scanned part as TKOTHER. We will return the end of comment token for the . . . . ; next scan. If we have scanned nothing, then we return the end of comment token. . . . . ; . . . . If (1gd_csa is equal to cs_addrs. This could not be true in case we were in mainline code * that was interrupted by the flush timer for a different region which in turn was interrupted by an external signal * that would drive us to exit. Setting the "process_exiting" variable causes those csa checks to pass. */ SET_PROCESS_EXITING_TRUE; # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_DEFERRED_TIMERS == gtm_white_box_test_case_number) && (2 == gtm_white_box_test_case_count)) { DEFER_INTERRUPTS(INTRPT_NO_TIMER_EVENTS, prev_intrpt_state); DBGFPF((stderr, "DEFERRED_SIGNAL_HANDLER: will sleep for 20 seconds\n")); LONG_SLEEP(20); DBGFPF((stderr, "DEFERRED_SIGNAL_HANDLER: done sleeping\n")); ENABLE_INTERRUPTS(INTRPT_NO_TIMER_EVENTS, prev_intrpt_state); } # endif /* If any special routines are registered to be driven on a signal, drive them now */ if ((0 != exi_condition) && (NULL != call_on_signal)) { signal_routine = call_on_signal; call_on_signal = NULL; /* So we don't recursively call ourselves */ (*signal_routine)(); } /* Note, we do not drive create_fatal_error zshow_dmp() in this routine since any deferrable signals are * by definition not fatal. */ EXIT(-exi_condition); } fis-gtm-V7.0-005/sr_unix/deferred_signal_handler.h0000755000032200000250000000114414342376327021035 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef DEFERRED_SIGNAL_HANDLER_INCLUDED #define DEFERRED_SIGNAL_HANDLER_INCLUDED void deferred_signal_handler(void); #endif /* DEFERRED_SIGNAL_HANDLER_INCLUDED */ fis-gtm-V7.0-005/sr_unix/dircompare.m.txt0000644000032200000250000000447514342376327017202 0ustar librarygtcdircompare ; ; This routine is run (for now) only on the development environment file listing. ; The routine adjusts certain file permissions to match with what we expect in an installation ; The final output is expected to match file listing of an installation ; set etrap so we get debug info and exit mumps set $etrap="use $P write $zstatus,!,""Error line: "" zprint @$zposition s x=$zjobexam() halt" set infile=$piece($zcmdline," ",1) set defgroup=$piece($zcmdline," ",2) if ""=$zsearch(infile) write !," file: ",infile," does not exist",! do setspecialperms ; a hard-coded list of files with odd permissions set by configure.gtc open infile:(readonly) use infile:exception="goto eof" for use infile read line do quit:""=line . set perm=$piece(line," ",1) ; get the first piece which is the permission field . set filename=$piece(line," ",2) ; for now there are no space separated file names . set ext=$zparse(filename,"TYPE") . if (".a"=ext)!(".o"=ext)!(".m"=ext)!(".dat"=ext)!(".gld"=ext)!(".h"=ext)!(".xc"=ext) do rmxall(.perm) . if (".gtc"=ext) do zapgtc(.filename) . do fixperms(.perm,filename) . do rmwrite(.perm) . do:""=defgroup zapworld(.perm) ; If it is not "defgroup", then remove all world permissions . use $P . write perm," ",filename,! close infile quit rmwrite(perm) ; do the equivalent of chmod a-w by translating permission w to - set perm=$translate(perm,"w","-") quit rmxall(perm) ; do the equivalent of chmod a-x by translating x to - set perm=$translate(perm,"x","-") quit zapworld(perm) ; get everything up to world field and add --- to tail set perm=$extract(perm,1,7)_"---" quit zapgtc(str) ; This is called when the extension of file is .gtc. ; Return just the name without extension set str=$zparse(str,"NAME") quit setspecialperms ; Permissions for these files are explicity set by configure to specific values ; Though perm755 is set, it is not used, since that is the default permission when other filters are applied for file="gtmstart","gtmstop","gtcm_run","gtcm_slist" set perm744(file)=1 for file="gdedefaults" set perm444(file)=1 for file="gtmprofile","gtmprofile_preV54000","gtm","gtmcshrc" set perm755(file)=1 quit fixperms(perm,file) if $data(perm744(file)) set perm=$extract(perm,1,1)_"rwxr--r--" if $data(perm444(file)) set perm=$extract(perm,1,1)_"r--r--r--" quit fis-gtm-V7.0-005/sr_unix/disk_block_available.c0000755000032200000250000000376514342376327020335 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ /* disk_block_available(int fd, int4 *ret, booleat_t fill_unix_holes) * parameter: * fd: file descriptor of the file that is located * on the disk being examined * *ret: address to put the number of available blocks * fill_unix_holes * TRUE ==> the calculation takes the * hole in this file into consideration * FALSE ==> plain system service * return: * 0: upon successful completion, the number of available * blocks is put to ret. * errno: as to why the request failed. * note that DISK_BLOCK_SIZE is used here instead of GDS_BLOCK_SIZE, * this is due to the intension to make this routine more general */ #include "mdef.h" #include #include "gtm_unistd.h" #include "gtm_stat.h" #include "gtm_statvfs.h" #ifndef __MVS__ #if !defined(sun) && !defined(__CYGWIN__) #include #endif #include #else #define DEV_BSIZE fstat_buf.st_blksize #endif #include "have_crit.h" #include "eintr_wrappers.h" #include "disk_block_available.h" int4 disk_block_available(int fd, gtm_uint64_t *ret, boolean_t fill_unix_holes) { struct stat fstat_buf; struct statvfs fstatvfs_buf; int status; FSTATVFS_FILE(fd, &fstatvfs_buf, status); if (-1 == status) return errno; *ret = (gtm_uint64_t)((fstatvfs_buf.f_frsize / DISK_BLOCK_SIZE) * fstatvfs_buf.f_bavail); if (fill_unix_holes) { FSTAT_FILE(fd, &fstat_buf, status); if (-1 == status) return errno; *ret -= (gtm_uint64_t)(DEV_BSIZE / DISK_BLOCK_SIZE * (DIVIDE_ROUND_UP(fstat_buf.st_size, DEV_BSIZE) - fstat_buf.st_blocks)); } return 0; } fis-gtm-V7.0-005/sr_unix/disk_block_available.h0000755000032200000250000000121714342376327020330 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #ifndef DISK_BLOCK_AVAILABLE_INCLUDED #define DISK_BLOCK_AVAILABLE_INCLUDED int4 disk_block_available(int fd, gtm_uint64_t *ret, boolean_t fill_unix_holes); #endif /* DISK_BLOCK_AVAILABLE_INCLUDED */ fis-gtm-V7.0-005/sr_unix/dm_audit_log.c0000644000032200000250000006625014342376327016653 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018-2022 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 "gtmxc_types.h" #include #include #include #include "gtm_fcntl.h" #include "gtm_limits.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_un.h" #include "gtm_syslog.h" #include "gtm_unistd.h" #include "gtm_socket.h" #include "gtm_string.h" #include "gtm_strings.h" #include "gtm_ipv6.h" #include "gtm_inet.h" #ifdef GTM_TLS #include "gtm_tls.h" #endif #include "have_crit.h" #include "io.h" #include "iottdef.h" #include "util.h" #include "restrict.h" #include "dm_audit_log.h" #include "error.h" #include "send_msg.h" #include "gtmmsg.h" #include "min_max.h" #include "fgncal.h" /* Needed for MAX_ERRSTR_LEN */ #include "eintr_wrappers.h" #include "compiler.h" /* Needed for MAX_SRCLINE */ #define ON 1 /* Used to enable socket options */ #define STRINGIFY(S) #S #define BOUNDED_FMT(LIMIT,TYPE) "%" STRINGIFY(LIMIT) TYPE #define TLSID_FMTSTR BOUNDED_FMT(MAX_TLSID_LEN, "s") #define PORTNUM_FMTSTR BOUNDED_FMT(NI_MAXSERV, "[^:]") #define IPV4_FMTSTR BOUNDED_FMT(SA_MAXLEN, "[^:]") #define IPV6_FMTSTR "[" BOUNDED_FMT(SA_MAXLEN, "[^]]") "]" GBLREF io_pair io_curr_device; GBLREF io_pair io_std_device; GBLREF char dl_err[MAX_ERRSTR_LEN]; /* This is maintained by gtm_tls_loadlibrary() */ GBLREF dm_audit_info audit_conn[]; GBLREF void (*primary_exit_handler)(void); GBLREF boolean_t restrict_initialized; GBLREF enum gtmImageTypes image_type; LITREF gtmImageName gtmImageNames[]; GBLREF mstr sys_input; error_def(ERR_TLSDLLNOOPEN); error_def(ERR_TEXT); error_def(ERR_SOCKINIT); error_def(ERR_TLSINIT); error_def(ERR_SETSOCKOPTERR); error_def(ERR_INVADDRSPEC); error_def(ERR_GETADDRINFO); error_def(ERR_SYSCALL); error_def(ERR_TLSHANDSHAKE); error_def(ERR_TEXT); error_def(ERR_TLSIOERROR); error_def(ERR_TLSCONVSOCK); error_def(ERR_OPENCONN); error_def(ERR_APDINITFAIL); error_def(ERR_APDCONNFAIL); error_def(ERR_APDLOGFAIL); error_def(ERR_AUDINITFAIL); error_def(ERR_AUDCONNFAIL); error_def(ERR_AUDLOGFAIL); error_def(ERR_SOCKCLOSE); error_def(ERR_RESTRICTEDOP); /* Frees all pointers allocated by audit_conn[save_is_zauditlog] */ void free_dm_audit_info_ptrs(void) { intrpt_state_t prev_intrpt_state; gtm_tls_ctx_t *dm_audit_tls_ctx; boolean_t save_is_zauditlog; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; save_is_zauditlog = TREF(is_zauditlog); # ifdef GTM_TLS if (NULL != audit_conn[save_is_zauditlog].tls_sock) { dm_audit_tls_ctx = audit_conn[save_is_zauditlog].tls_sock->gtm_ctx; gtm_tls_session_close((gtm_tls_socket_t **)&audit_conn[save_is_zauditlog].tls_sock); if (NULL != dm_audit_tls_ctx) gtm_tls_fini((gtm_tls_ctx_t **)&dm_audit_tls_ctx); } # endif if (AUDIT_CONN_TCP == audit_conn[save_is_zauditlog].conn_type) { DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); freeaddrinfo(audit_conn[save_is_zauditlog].netaddr.tcp_addr); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); } } /* Establishes a connection to the logger by using the * information contained in the audit_conn global struct. * * returns: * -1 if something went wrong when trying to connect * 0 if connection successfull */ int dm_audit_connect(void) { struct addrinfo *head_ai_ptr = NULL, *remote_ai_ptr = NULL; boolean_t need_socket = TRUE, need_connect = TRUE, save_is_zauditlog; struct linger enable_linger = {1, 3}; char *errptr; int on = ON, save_errno, status, errlen; # ifdef GTM_TLS gtm_tls_ctx_t *dm_audit_tls_ctx; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; save_is_zauditlog = TREF(is_zauditlog); assert((AUDIT_CONN_INVALID != audit_conn[save_is_zauditlog].conn_type) && audit_conn[save_is_zauditlog].initialized); /* If connected previously, close old tls and/or tcp sockets so they can be reused */ if (FD_INVALID != audit_conn[save_is_zauditlog].sock_fd) { if ((AUDIT_CONN_TLS == audit_conn[save_is_zauditlog].conn_type) && (NULL != audit_conn[save_is_zauditlog].tls_sock)) gtm_tls_socket_close(audit_conn[save_is_zauditlog].tls_sock); else assert((AUDIT_CONN_TCP == audit_conn[save_is_zauditlog].conn_type) || (AUDIT_CONN_UN == audit_conn[save_is_zauditlog].conn_type)); close(audit_conn[save_is_zauditlog].sock_fd); audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; } if (AUDIT_CONN_UN != audit_conn[save_is_zauditlog].conn_type) { /* ======================= Do the TCP connection ======================== */ assert(NULL != audit_conn[save_is_zauditlog].netaddr.tcp_addr); head_ai_ptr = audit_conn[save_is_zauditlog].netaddr.tcp_addr; do { /* Test all address families until connected */ if (need_socket && (FD_INVALID != audit_conn[save_is_zauditlog].sock_fd)) { close(audit_conn[save_is_zauditlog].sock_fd); audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; } if (need_socket) { assert(FD_INVALID == audit_conn[save_is_zauditlog].sock_fd); for (remote_ai_ptr = head_ai_ptr; NULL != remote_ai_ptr; remote_ai_ptr = remote_ai_ptr->ai_next) { /* Go through each addrinfo until socket creation with one of them is successful */ audit_conn[save_is_zauditlog].sock_fd = socket(remote_ai_ptr->ai_family, remote_ai_ptr->ai_socktype, remote_ai_ptr->ai_protocol); if (FD_INVALID != audit_conn[save_is_zauditlog].sock_fd) break; } if (FD_INVALID == audit_conn[save_is_zauditlog].sock_fd) { save_errno = errno; errptr = (char *)STRERROR(save_errno); if ((IS_MUPIP_IMAGE) || (IS_DSE_IMAGE) || (IS_LKE_IMAGE) || (save_is_zauditlog)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_AUDCONNFAIL, 1, gtmImageNames[image_type].imageName, ERR_SOCKINIT, 3, save_errno, LEN_AND_STR(errptr)); } else if (IS_MUMPS_IMAGE) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_APDCONNFAIL, 0, ERR_SOCKINIT, 3, save_errno, LEN_AND_STR(errptr)); return -1; } head_ai_ptr = remote_ai_ptr->ai_next; /* Get ready to try next address if connect fails */ need_socket = FALSE; } save_errno = status = 0; if (need_connect) { /* Attempt a connect to logger */ assert((FD_INVALID != audit_conn[save_is_zauditlog].sock_fd) && (NULL != remote_ai_ptr)); if (0 > setsockopt(audit_conn[save_is_zauditlog].sock_fd, SOL_SOCKET, SO_LINGER, (const void *)&enable_linger, SIZEOF(enable_linger))) { save_errno = errno; errptr = (char *)STRERROR(save_errno); errlen = STRLEN(errptr); close(audit_conn[save_is_zauditlog].sock_fd); audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("SO_LINGER"), save_errno, errlen, errptr); } # ifdef TCP_NODELAY if (0 > setsockopt(audit_conn[save_is_zauditlog].sock_fd, IPPROTO_TCP, TCP_NODELAY, &on, SIZEOF(on))) { save_errno = errno; errptr = (char *)STRERROR(save_errno); errlen = STRLEN(errptr); close(audit_conn[save_is_zauditlog].sock_fd); audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("TCP_NODELAY"), save_errno, errlen, errptr); } # endif CONNECT_SOCKET(audit_conn[save_is_zauditlog].sock_fd, remote_ai_ptr->ai_addr, remote_ai_ptr->ai_addrlen, status); save_errno = errno; /* CONNECT_SOCKET should have handled EINTR. Assert that */ assert((0 <= status) || (EINTR != save_errno)); if (0 <= status) { need_connect = FALSE; save_errno = 0; /* Connection successful - need to reset save_errno */ } else { switch (save_errno) { case ETIMEDOUT: /* The other side bound but not listening - fall through */ case ECONNREFUSED: /* Connection refused */ if (NULL != head_ai_ptr) { /* Do loop again - try next address */ need_connect = need_socket = TRUE; save_errno = 0; status = -1; } else { /* We've run out of addresses to try */ need_connect = need_socket = FALSE; status = 0; /* Break out of loop */ } break; default: /* Connect failed */ need_connect = FALSE; status = 0; /* Break out of loop */ break; } } } } while (0 > status); if (save_errno) { /* Failed to connect, throw error */ if (FD_INVALID != audit_conn[save_is_zauditlog].sock_fd) { close(audit_conn[save_is_zauditlog].sock_fd); audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; } if ((IS_MUPIP_IMAGE) || (IS_DSE_IMAGE) || (IS_LKE_IMAGE) || (save_is_zauditlog)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_AUDCONNFAIL, 1, gtmImageNames[image_type].imageName, ERR_OPENCONN, 0, save_errno); } else if (IS_MUMPS_IMAGE) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_APDCONNFAIL, 0, ERR_OPENCONN, 0, save_errno); return -1; } assert(FD_INVALID != audit_conn[save_is_zauditlog].sock_fd); if (AUDIT_CONN_TLS == audit_conn[save_is_zauditlog].conn_type) { /* ================== Do the TLS connection/handshake =================== */ /* If tls socket was not initialized, we need to create one */ if (NULL == audit_conn[save_is_zauditlog].tls_sock) { /* gtm_tls_loadlibrary() must be called before gtm_tls_init() */ if(SS_NORMAL != (status = gtm_tls_loadlibrary())) { close(audit_conn[save_is_zauditlog].sock_fd); audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; /* dl_err is initialized and set in gtm_tls_loadlibrary() when an error occurs */ if ((IS_MUPIP_IMAGE) || (IS_DSE_IMAGE) || (IS_LKE_IMAGE)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_AUDCONNFAIL, 1, gtmImageNames[image_type].imageName, ERR_TLSDLLNOOPEN, 0, ERR_TEXT, 2, LEN_AND_STR(dl_err)); } else if (IS_MUMPS_IMAGE) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_APDCONNFAIL, 0, ERR_TLSDLLNOOPEN, 0, ERR_TEXT, 2, LEN_AND_STR(dl_err)); return -1; } /* Create the SSL/TLS context */ if (NULL == (dm_audit_tls_ctx = gtm_tls_init(GTM_TLS_API_VERSION, GTMTLS_OP_INTERACTIVE_MODE))) { close(audit_conn[save_is_zauditlog].sock_fd); audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; errptr = (char *)gtm_tls_get_error(NULL); if ((IS_MUPIP_IMAGE) || (IS_DSE_IMAGE) || (IS_LKE_IMAGE) || (save_is_zauditlog)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_AUDCONNFAIL, 1, gtmImageNames[image_type].imageName, ERR_TLSINIT, 0, ERR_TEXT, 2, LEN_AND_STR(errptr)); } else if (IS_MUMPS_IMAGE) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_APDCONNFAIL, 0, ERR_TLSINIT, 0, ERR_TEXT, 2, LEN_AND_STR(errptr)); return -1; } if (primary_exit_handler) atexit(primary_exit_handler); } else { /* TLS socket already exists, so just reuse it */ assert(NULL != audit_conn[save_is_zauditlog].tls_sock->gtm_ctx); dm_audit_tls_ctx = audit_conn[save_is_zauditlog].tls_sock->gtm_ctx; } assert(NULL != dm_audit_tls_ctx); /* Create tls socket */ audit_conn[save_is_zauditlog].tls_sock = gtm_tls_socket(dm_audit_tls_ctx, audit_conn[save_is_zauditlog].tls_sock, audit_conn[save_is_zauditlog].sock_fd, audit_conn[save_is_zauditlog].tls_id, GTMTLS_OP_DM_AUDIT | GTMTLS_OP_CLIENT_MODE); if (NULL == audit_conn[save_is_zauditlog].tls_sock) { errptr = (char *)gtm_tls_get_error(NULL); close(audit_conn[save_is_zauditlog].sock_fd); audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; restrict_initialized = TRUE; if ((IS_MUPIP_IMAGE) || (IS_DSE_IMAGE) || (IS_LKE_IMAGE) || (save_is_zauditlog)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_AUDCONNFAIL, 1, gtmImageNames[image_type].imageName, ERR_TLSCONVSOCK, 0, ERR_TEXT, 2, LEN_AND_STR(errptr)); } else if (IS_MUMPS_IMAGE) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_APDCONNFAIL, 0, ERR_TLSCONVSOCK, 0, ERR_TEXT, 2, LEN_AND_STR(errptr)); return -1; } /* Attempt TLS handshake with logger */ if (0 > gtm_tls_connect(audit_conn[save_is_zauditlog].tls_sock)) { errptr = (char *)gtm_tls_get_error(audit_conn[save_is_zauditlog].tls_sock); gtm_tls_socket_close(audit_conn[save_is_zauditlog].tls_sock); close(audit_conn[save_is_zauditlog].sock_fd); audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; restrict_initialized = TRUE; if ((IS_MUPIP_IMAGE) || (IS_DSE_IMAGE) || (IS_LKE_IMAGE) || (save_is_zauditlog)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_AUDCONNFAIL, 1, gtmImageNames[image_type].imageName, ERR_TLSHANDSHAKE, 0, ERR_TEXT, 2, LEN_AND_STR(errptr)); } else if (IS_MUMPS_IMAGE) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_APDCONNFAIL, 0, ERR_TLSHANDSHAKE, 0, ERR_TEXT, 2, LEN_AND_STR(errptr)); return -1; } } } else { /* =============== Do the unix domain socket connection ================ */ assert('\0' != audit_conn[save_is_zauditlog].netaddr.un_addr.sun_path[0]); audit_conn[save_is_zauditlog].sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (FD_INVALID == audit_conn[save_is_zauditlog].sock_fd) { save_errno = errno; errptr = (char *)STRERROR(save_errno); restrict_initialized = TRUE; if ((IS_MUPIP_IMAGE) || (IS_DSE_IMAGE) || (IS_LKE_IMAGE) || (save_is_zauditlog)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_AUDCONNFAIL, 1, gtmImageNames[image_type].imageName, ERR_SOCKINIT, 3, save_errno, LEN_AND_STR(errptr)); } else if (IS_MUMPS_IMAGE) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_APDCONNFAIL, 0, ERR_SOCKINIT, 3, save_errno, LEN_AND_STR(errptr)); return -1; } CONNECT_SOCKET(audit_conn[save_is_zauditlog].sock_fd, (struct sockaddr *)&(audit_conn[save_is_zauditlog].netaddr.un_addr), SIZEOF(struct sockaddr_un), status); if (0 > status) { save_errno = errno; close(audit_conn[save_is_zauditlog].sock_fd); audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; restrict_initialized = TRUE; if ((IS_MUPIP_IMAGE) || (IS_DSE_IMAGE) || (IS_LKE_IMAGE) || (save_is_zauditlog)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_AUDCONNFAIL, 1, gtmImageNames[image_type].imageName, ERR_OPENCONN, 0, save_errno); } else if (IS_MUMPS_IMAGE) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_APDCONNFAIL, 0, ERR_OPENCONN, 0, save_errno); return -1; } } return 0; } /* Initializes the global variable/struct audit_conn[aud_indx] with the logger's (server) * connection information (obtained from restriction file). * * params: * @host_info contains either path to domain socket file or IP+port * Format: * :: * Args: * = Logger's IP address (if enclosed in '[' and ']', * then we assume it's IPv6, otherwise IPv4) * = Port number that the logger listens on * = (Optional) Label of section in the configuration * file containing the TLS options and/or certificate * that GT.M uses when connecting to the logger. * NOTE: If "host_info" does not match the above format, then we * assume it contains path to domain socket file. * @is_tls determines whether we are initializing a tls connection * returns: * -1 if something went wrong when initializing and connecting * 0 if initialization and connection successfull */ int dm_audit_init(char *host_info, boolean_t is_tls) { char host[SA_MAXLEN + 1]; struct addrinfo *remote_ai_head, hints; intrpt_state_t prev_intrpt_state; char port_buffer[NI_MAXSERV + 1]; int host_len, port_len; unsigned int host_info_len; int errcode, real_errno; int fields, port; char tlsid[MAX_TLSID_LEN + 1]; char *errptr; boolean_t save_is_zauditlog; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; save_is_zauditlog = TREF(is_zauditlog); free_dm_audit_info_ptrs(); if ((NULL == host_info) || ('\0' == host_info[0])) { /* No logger (server) information provided */ restrict_initialized = TRUE; // needed for gtm_putmsg_list.c if ((IS_MUPIP_IMAGE) || (IS_DSE_IMAGE) || (IS_LKE_IMAGE) || (save_is_zauditlog)) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_AUDINITFAIL, 1, gtmImageNames[image_type].imageName, ERR_INVADDRSPEC); else if (IS_MUMPS_IMAGE) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_APDINITFAIL, 0, ERR_INVADDRSPEC); return -1; } audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; if ('[' == host_info[0]) /* If first character is '[', then we assume that the IP address is enclosed in brackets and is IPV6 */ fields = SSCANF(host_info, IPV6_FMTSTR " : " PORTNUM_FMTSTR " : " TLSID_FMTSTR, host, port_buffer, tlsid); else /* Otherwise, we assume "host_info" contains an IPV4 address or is a path to file domain socket file */ fields = SSCANF(host_info, IPV4_FMTSTR " : " PORTNUM_FMTSTR " : " TLSID_FMTSTR, host, port_buffer, tlsid); host_len = STRLEN(host); if ((0 == fields) || (0 == host_len)) { restrict_initialized = TRUE; if ((IS_MUPIP_IMAGE) || (IS_DSE_IMAGE) || (IS_LKE_IMAGE)) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_AUDINITFAIL, 1, gtmImageNames[image_type].imageName, ERR_INVADDRSPEC); else if (IS_MUMPS_IMAGE) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_APDINITFAIL, 0, ERR_INVADDRSPEC); return -1; } if ((3 == fields) || (2 == fields)) { /* If we get 2 or 3 fields, then we assume host_info contains IP and port information */ port_len = STRLEN(port_buffer); port_buffer[port_len]='\0'; CLIENT_HINTS(hints); DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); if (0 != (errcode = getaddrinfo(host, port_buffer, &hints, &remote_ai_head))) { real_errno = errno; ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); TEXT_ADDRINFO(errptr, errcode, real_errno); restrict_initialized = TRUE; if ((IS_MUPIP_IMAGE) || (IS_DSE_IMAGE) || (IS_LKE_IMAGE) || (save_is_zauditlog)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_AUDCONNFAIL, 1, gtmImageNames[image_type].imageName, ERR_GETADDRINFO, 0, ERR_TEXT, 2, LEN_AND_STR(errptr)); } else if (IS_MUMPS_IMAGE) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_APDCONNFAIL, 0, ERR_GETADDRINFO, 0, ERR_TEXT, 2, LEN_AND_STR(errptr)); } return -1; } ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); audit_conn[save_is_zauditlog].netaddr.tcp_addr = remote_ai_head; if (!is_tls) audit_conn[save_is_zauditlog].conn_type = AUDIT_CONN_TCP; else { audit_conn[save_is_zauditlog].conn_type = AUDIT_CONN_TLS; audit_conn[save_is_zauditlog].tls_id[0] = '\0'; if ((3 == fields) && ('\0' != tlsid[0])) memcpy(audit_conn[save_is_zauditlog].tls_id, tlsid, MAX_TLSID_LEN + 1); } } else { /* Assume host_info contains path to unix domain socket * if number of fields is 1 or is more than 3. */ host_info_len = strlen(host_info); if (is_tls || (MAX_SOCKADDR_UN_PATH <= host_info_len)) { /* If the "TLS" option is specified but no IP/port info provided or path to socket file is too long */ restrict_initialized = TRUE; if ((IS_MUPIP_IMAGE) || (IS_DSE_IMAGE) || (IS_LKE_IMAGE)) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_AUDINITFAIL, 1, gtmImageNames[image_type].imageName, ERR_INVADDRSPEC); else if (IS_MUMPS_IMAGE) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_APDINITFAIL, 0, ERR_INVADDRSPEC); return -1; } audit_conn[save_is_zauditlog].conn_type = AUDIT_CONN_UN; (audit_conn[save_is_zauditlog].netaddr.un_addr).sun_family = AF_UNIX; memcpy((audit_conn[save_is_zauditlog].netaddr.un_addr).sun_path, host_info, host_info_len); (audit_conn[save_is_zauditlog].netaddr.un_addr).sun_path[host_info_len] = '\0'; } audit_conn[save_is_zauditlog].initialized = TRUE; return 0; } /* Responsible for audit logging. It essentially sends the to-be-logged * command or activity to the listener (logger) for logging. * * params: * @v contains command to be logged * @src integer that identifies the source caller * returns: * FALSE if Direct Mode / Utilities Auditing enabled and logging failed * TRUE if Direct Mode / Utilities Auditing disabled, or if enabled and logging successfull */ int dm_audit_log(mval *v, int src) { unsigned char log_msg_pre[GTM_PATH_MAX + MAX_AUDIT_PROC_INFO_LEN + MAX_SRCLINE + 1], *log_msg; char cmd_pre[MAX_SRCLINE + 1], *cmd, *errptr, ttybuf[TTY_NAME_MAX + 1]; int status, save_errno, log_msg_len, max_log_msg_len; boolean_t need_free = FALSE, save_is_zauditlog; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; save_is_zauditlog = TREF(is_zauditlog); if ((AUDIT_SRC_MUPIP != src) && (AUDIT_SRC_DSE != src) && (AUDIT_SRC_LKE != src) && (AUDIT_SRC_GTM != src)) { if (AUDIT_DISABLE == RESTRICTED(dm_audit_enable)) return TRUE; assert(TREF(dollar_zaudit)); } /* Never log empty commands (useless entries) */ if (v->str.len == 0) return TRUE; /* Skip logging for DMREAD and OPREAD when $IO != $PRINCIPAL */ if (((AUDIT_SRC_DMREAD == src) || (AUDIT_SRC_OPREAD == src)) && (io_curr_device.in != io_std_device.in)) return TRUE; /* Make sure the caller is classified, otherwise set to unknown */ if ((AUDIT_SRC_DMREAD != src) && (AUDIT_SRC_OPREAD != src) && (AUDIT_SRC_MUPIP != src) && (AUDIT_SRC_DSE != src) && (AUDIT_SRC_LKE != src) && (AUDIT_SRC_GTM != src)) src = AUDIT_SRC_UNKNOWN; /* Unknown source caller */ /* Check if audit information was initialized */ if (!audit_conn[save_is_zauditlog].initialized) { restrict_initialized = TRUE; if ((IS_MUPIP_IMAGE)|| (IS_DSE_IMAGE) || (IS_LKE_IMAGE) || (save_is_zauditlog)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_AUDLOGFAIL, 1, gtmImageNames[image_type].imageName, ERR_TEXT, 2, LEN_AND_LIT("Audit information is not initialized")); } else if (IS_MUMPS_IMAGE) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_APDLOGFAIL, 0, ERR_TEXT, 2, LEN_AND_LIT("Audit information is not initialized")); return FALSE; } /* Check if an initial connect is needed */ if ((FD_INVALID == audit_conn[save_is_zauditlog].sock_fd) && (0 > dm_audit_connect())) { restrict_initialized = TRUE; if ((IS_MUPIP_IMAGE) || (IS_DSE_IMAGE) || (IS_LKE_IMAGE) || (save_is_zauditlog)) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_AUDLOGFAIL, 1, gtmImageNames[image_type].imageName); else if (IS_MUMPS_IMAGE) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_APDLOGFAIL, 0, ERR_APDCONNFAIL); return FALSE; } assert(FD_INVALID != audit_conn[save_is_zauditlog].sock_fd); if (MAX_SRCLINE < v->str.len) { max_log_msg_len = GTM_PATH_MAX + MAX_AUDIT_PROC_INFO_LEN + v->str.len; log_msg = (unsigned char *)malloc(max_log_msg_len + 1); cmd = (char *)malloc(v->str.len + 1); need_free = TRUE; } else { max_log_msg_len = GTM_PATH_MAX + MAX_AUDIT_PROC_INFO_LEN + MAX_SRCLINE; log_msg = log_msg_pre; cmd = cmd_pre; } STRNCPY_STR(cmd, v->str.addr, v->str.len); cmd[v->str.len] = '\0'; memset(ttybuf, '\0', TTY_NAME_MAX + 1); memcpy(ttybuf, sys_input.addr, sys_input.len); /* Attach process information to command as one message*/ log_msg_len = SNPRINTF((char *)log_msg, max_log_msg_len + 1, "dist=%s; src=%d; uid=%d; euid=%d; pid=%d; tty=%s; command=%s", getenv("gtm_dist"), src, getuid(), geteuid(), getpid(), ttybuf, cmd); assert((max_log_msg_len + 1) > log_msg_len); log_msg[log_msg_len] = '\0'; if (need_free) free(cmd); if (AUDIT_CONN_TLS == audit_conn[save_is_zauditlog].conn_type) { /* Connection type is TLS */ assert(NULL != audit_conn[save_is_zauditlog].tls_sock); if (0 >= gtm_tls_send(audit_conn[save_is_zauditlog].tls_sock, (char *)log_msg, log_msg_len)) { /* Either invalid socket or sending failed */ if (need_free) free(log_msg); errptr = (char *)gtm_tls_get_error(audit_conn[save_is_zauditlog].tls_sock); gtm_tls_socket_close(audit_conn[save_is_zauditlog].tls_sock); close(audit_conn[save_is_zauditlog].sock_fd); audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; restrict_initialized = TRUE; if ((IS_MUPIP_IMAGE) || (IS_DSE_IMAGE) || (IS_LKE_IMAGE) || (save_is_zauditlog)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(11) ERR_AUDLOGFAIL, 1, gtmImageNames[image_type].imageName, ERR_TLSIOERROR, 2, LEN_AND_LIT("send"), ERR_TEXT, 2, LEN_AND_STR(errptr)); } else if (IS_MUMPS_IMAGE) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_APDLOGFAIL, 0, ERR_TLSIOERROR, 2, LEN_AND_LIT("send"), ERR_TEXT, 2, LEN_AND_STR(errptr)); return FALSE; } } else { /* Connection type is either TCP or unix domain socket */ SEND(audit_conn[save_is_zauditlog].sock_fd, (char *)log_msg, log_msg_len, 0, status); if (0 >= status) { /* Sending failed */ save_errno = errno; if (need_free) free(log_msg); close(audit_conn[save_is_zauditlog].sock_fd); audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; restrict_initialized = TRUE; if ((IS_MUPIP_IMAGE) || (IS_DSE_IMAGE) || (IS_LKE_IMAGE) || (save_is_zauditlog)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(11) ERR_AUDLOGFAIL, 1, gtmImageNames[image_type].imageName, ERR_SYSCALL, 5, LEN_AND_LIT("SEND"), CALLFROM, save_errno); } else if (IS_MUMPS_IMAGE) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_APDLOGFAIL, 0, ERR_SYSCALL, 5, LEN_AND_LIT("SEND"), CALLFROM, save_errno); return FALSE; } } if (need_free) free(log_msg); errno = 0; if (AUDIT_CONN_TLS == audit_conn[save_is_zauditlog].conn_type) gtm_tls_socket_close(audit_conn[save_is_zauditlog].tls_sock); if (AUDIT_CONN_TCP == audit_conn[save_is_zauditlog].conn_type) { if (-1 == shutdown(audit_conn[save_is_zauditlog].sock_fd, SHUT_WR)) { assert(FALSE); audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; return FALSE; } } /* Check for error on normal socket close */ if (-1 == close(audit_conn[save_is_zauditlog].sock_fd)) { save_errno = errno; errptr = (char *)STRERROR(save_errno); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_AUDCONNFAIL, 1,gtmImageNames[image_type].imageName, ERR_SOCKCLOSE, 3, save_errno, LEN_AND_STR(errptr)); audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; return FALSE; } audit_conn[save_is_zauditlog].sock_fd = FD_INVALID; return TRUE; } /* Check for image_type ensures that the code executes only for the specific image and commands log properly. */ int log_cmd_if_needed(char *logcmd) { boolean_t audit_on; mval input_line; int aud_type; switch (image_type) { case DSE_IMAGE: audit_on = RESTRICTED(dse_audit_enable); aud_type = AUDIT_SRC_DSE; break; case LKE_IMAGE: audit_on = RESTRICTED(lke_audit_enable); aud_type = AUDIT_SRC_LKE; break; case MUPIP_IMAGE: audit_on = RESTRICTED(mupip_audit_enable); aud_type = AUDIT_SRC_MUPIP; break; default: return -1; } if (audit_on) { input_line.mvtype = MV_STR; input_line.str.addr = logcmd; input_line.str.len = STRLEN(input_line.str.addr); if (!dm_audit_log(&input_line, aud_type)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) MAKE_MSG_SEVERE(ERR_RESTRICTEDOP), 1, gtmImageNames[image_type].imageName); return 1; } } return 0; } fis-gtm-V7.0-005/sr_unix/dm_audit_log.h0000755000032200000250000000536714342376327016665 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018-2022 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. * * * ****************************************************************/ #ifndef DM_AUDIT_LOG_INCLUDED #define DM_AUDIT_LOG_INCLUDED #include #include #include #ifdef GTM_TLS #include "gtm_tls.h" #endif #define MAX_SOCKADDR_UN_PATH SIZEOF(((struct sockaddr_un *)0)->sun_path) #define MAX_AUDIT_PROC_INFO_LEN 72 + TTY_NAME_MAX /* Safe max length of "src=%d; uid=%d; euid=%d; pid=%d; tty=%s" */ /* For every caller that calls dm_audit_log(), an integer value needs be defined to identify that caller. * Otherwise, caller should use AUDIT_SRC_UNKNOWN (0) to identify itself when calling dm_audit_log(). * If new value defined, the caller must use the defined value as the second argument when calling dm_audit_log(). */ #define AUDIT_SRC_UNKNOWN 0 /* Caller is unknown */ #define AUDIT_SRC_DMREAD 1 /* Caller is dm_read */ #define AUDIT_SRC_OPREAD 2 /* Caller is op_read */ #define AUDIT_SRC_MUPIP 3 /* Caller is mupip */ #define AUDIT_SRC_GTM 4 /* Caller is gtm */ #define AUDIT_SRC_LKE 5 /* Caller is lke */ #define AUDIT_SRC_DSE 6 /* Caller is dse */ /* Connection type when sending log info to logger */ #define AUDIT_CONN_INVALID 0 #define AUDIT_CONN_UN 1 /* UNIX domain (socket) */ #define AUDIT_CONN_TCP 2 /* TCP */ #define AUDIT_CONN_TLS 3 /* TLS */ #define MAX_AUD_CONN 2 /* Maximum number of AUDIT_CONN connections */ /* This structure is used to store information * about the direct mode auditing logger. */ typedef struct { int conn_type; /* Connection type (UNIX-domain/TCP/TLS) */ int sock_fd; /* Socket identifier */ union { struct sockaddr_un un_addr; /* UNIX domain socket address */ struct addrinfo *tcp_addr; /* TCP/IP address */ } netaddr; # ifdef GTM_TLS char tls_id[MAX_TLSID_LEN + 1]; /* Label of section in the GTM TLS configuration * file containing the TLS options and/or certificate * that GT.M uses to connect to the logger. */ gtm_tls_socket_t *tls_sock; /* TLS socket */ # endif boolean_t initialized; /* TRUE if this struct is initialized */ } dm_audit_info; int dm_audit_connect(void); int dm_audit_init(char *host_info, boolean_t is_tls); int dm_audit_log(mval *v, int src); void free_dm_audit_info_ptrs(void); int log_cmd_if_needed(char *logcmd); #endif /* DM_AUDIT_LOG_INCLUDED */ fis-gtm-V7.0-005/sr_unix/dm_read.c0000644000032200000250000010423514342376327015613 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* WARNING: this module contains a mixture of ASCII and EBCDIC on S390*/ #include "mdef.h" #include "gtm_signal.h" #include "gtm_unistd.h" #include "gtm_stdlib.h" #include "gtm_select.h" #include "gtm_string.h" #include #include #include #include "io.h" #include "trmdef.h" #include "iottdef.h" #include "iottdefsp.h" #include "iott_edit.h" #include "stringpool.h" #include "comline.h" #include "gtmio.h" #include "eintr_wrappers.h" #include "cli.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "dm_read.h" #include "gtm_tputs.h" #include "op.h" #include "send_msg.h" #include "svnames.h" #include "util.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" #include "gtm_utf8.h" #endif GBLREF boolean_t dmterm_default, gtm_utf8_mode, prin_in_dev_failure, prin_out_dev_failure; GBLREF char *CLR_EOL, *CURSOR_DOWN, *CURSOR_LEFT, *CURSOR_RIGHT, *CURSOR_UP; GBLREF char *KEY_BACKSPACE, *KEY_DC, *KEY_DOWN, *KEY_INSERT, *KEY_LEFT, *KEY_RIGHT, *KEY_UP; GBLREF char *KEYPAD_LOCAL, *KEYPAD_XMIT; GBLREF int AUTO_RIGHT_MARGIN, EAT_NEWLINE_GLITCH; GBLDEF int comline_index, recall_num; GBLREF int4 exi_condition; GBLREF io_desc *active_device; GBLREF io_pair io_curr_device, io_std_device; GBLREF mstr *comline_base; GBLREF mval dollar_zstatus; GBLREF mv_stent *mv_chain; GBLREF spdesc stringpool; GBLREF stack_frame *frame_pointer; GBLREF unsigned char *msp, *stackbase, *stacktop, *stackwarn; GBLREF volatile int4 outofband; GBLREF volatile boolean_t dollar_zininterrupt, timer_in_handler; LITREF unsigned char lower_to_upper_table[]; #ifdef UTF8_SUPPORTED LITREF UChar32 u32_line_term[]; #endif #define MAX_ERR_MSG_LEN 40 #define REC "rec" #define RECALL "recall" enum RECALL_ERR_CODE { NO_ERROR, ERR_OUT_OF_BOUNDS, ERR_NO_MATCH_STR }; static unsigned char cr = '\r'; static unsigned char recall_error_msg[][MAX_ERR_MSG_LEN] = { "", "Recall Error : Number exceeds limit", "Recall Error : No matching string" }; error_def(ERR_IOEOF); error_def(ERR_NOPRINCIO); error_def(ERR_TERMHANGUP); error_def(ERR_ZINTDIRECT); #define WRITE_GTM_PROMPT \ if (0 < (TREF(gtmprompt)).len) \ { \ write_str((TREF(gtmprompt)).addr, (TREF(gtmprompt)).len, 0, TRUE, TRUE); \ if (utf8_active) \ { /* cannot use "gtm_wcswidth" function as we also want to compute "dx_start" in the loop */ \ dx_start = 0; \ for (ptr = (unsigned char *)(TREF(gtmprompt)).addr, ptrtop = ptr + (TREF(gtmprompt)).len; \ ptr < ptrtop; ptr = ptrnext) \ { \ ptrnext = UTF8_MBTOWC(ptr, ptrtop, codepoint); \ if (WEOF == codepoint) \ UTF8_BADCHAR(0, ptr, ptrtop, 0, NULL); \ GTM_IO_WCWIDTH(codepoint, inchar_width); \ assert(0 <= inchar_width); \ assert(inchar_width <= ioptr_width);/* width cannot be less than a wide character's display width */ \ if (((dx_start % ioptr_width) + inchar_width) > ioptr_width) \ dx_start = ROUND_UP(dx_start, ioptr_width);/* add $X padding for wide char in last column */ \ dx_start += inchar_width; \ } \ assert(0 <= dx_start); \ } else \ dx_start = (TREF(gtmprompt)).len; \ dx = dx_start % ioptr_width; \ } else \ dx = dx_start = 0; #define MOVE_CURSOR_LEFT_ONE_CHAR(dx, instr, dx_instr, dx_start, ioptr_width) \ { \ dx_prev = compute_dx(BUFF_ADDR(0), instr - 1, ioptr_width, dx_start); \ delchar_width = dx_instr - dx_prev; \ move_cursor_left(dx, delchar_width); \ dx = (dx - delchar_width + ioptr_width) % ioptr_width; \ instr--; \ dx_instr -= delchar_width; \ } #define DEL_ONE_CHAR_AT_CURSOR(outlen, dx_outlen, dx, dx_instr, dx_start, ioptr_width) \ { \ assert(instr <= outlen); \ if (instr != outlen) \ { /* First write spaces on all the display columns that the current string occupied. \ * Then overwrite that with the new string. This way we are guaranteed all \ * display columns are clean. \ */ \ STORE_OFF(' ', outlen); \ outlen--; \ IOTT_COMBINED_CHAR_CHECK; \ MOVE_BUFF(instr, BUFF_ADDR(instr + 1), outlen - instr); \ write_str_spaces(dx_outlen - dx_instr, dx, FALSE); \ write_str(BUFF_ADDR(instr), outlen - instr, dx, FALSE, FALSE); \ dx_outlen = compute_dx(BUFF_ADDR(0), outlen, ioptr_width, dx_start); \ } \ } void dm_read (mval *v) { boolean_t buffer_moved, insert_mode, terminator_seen, utf8_active, zint_restart; # ifdef UTF8_SUPPORTED boolean_t matched; char *recptr = RECALL; # endif char *argv[3]; char temp_str[MAX_RECALL_NUMBER_LENGTH + 1]; char *strtokptr; const char delimiter_string[] = " \t"; d_tt_struct *tt_ptr; enum RECALL_ERR_CODE err_recall = NO_ERROR; fd_set input_fd; int backspace, cl, cur_cl, cur_value, delete, down, hist, histidx, index, insert_key, keypad_len, left; int delchar_width; /* display width of deleted char */ int delta_width; /* display width change for replaced char */ int dx, dx_start; /* local dollar X, starting value */ int dx_instr, dx_outlen; /* wcwidth of string to insert point, whole string */ int dx_prev, dx_cur, dx_next;/* wcwidth of string to char BEFORE, AT and AFTER the insert point */ int inchar_width; /* display width of inchar */ int instr; /* insert point in input string */ int ioptr_width; /* display width of the IO device */ int outlen; /* total characters in line so far */ int match_length, msk_in, msk_num, num_chars_left, num_lines_above, right, selstat, status, up, utf8_more; io_desc *io_ptr; io_termmask mask_term; mv_stent *mvc, *mv_zintdev; struct timeval input_timeval; tt_interrupt *tt_state; uint4 mask; unsigned int exp_length, len, length; unsigned char *argv1; unsigned char *buffer_start; /* beginning of non UTF8 buffer */ unsigned char *current_ptr; /* insert next character into buffer here */ unsigned char escape_sequence[ESC_LEN]; unsigned char inbyte, *outptr, *outtop, *ptr, *ptrnext, *ptrtop; unsigned char more_buf[GTM_MB_LEN_MAX + 1], *more_ptr; /* to build up multi byte for character */ unsigned short escape_length = 0; wint_t *buffer_32_start, codepoint, *current_32_ptr, inchar, *ptr32; # ifdef __MVS__ wint_t asc_inchar; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(stringpool.free >= stringpool.base); assert(stringpool.free <= stringpool.top); if ( NULL == comline_base) { comline_base = (mstr *)malloc(MAX_RECALL * SIZEOF(mstr)); memset(comline_base, 0, (MAX_RECALL * SIZEOF(mstr))); } active_device = io_curr_device.in; io_ptr = io_curr_device.in; tt_ptr = (d_tt_struct *)(io_ptr->dev_sp); assert (io_ptr->state == dev_open); if (tt == io_curr_device.out->type) iott_flush(io_curr_device.out); insert_mode = !(TT_NOINSERT & tt_ptr->ext_cap); utf8_active = gtm_utf8_mode ? (CHSET_M != io_ptr->ichset) : FALSE; length = tt_ptr->in_buf_sz + ESC_LEN; /* for possible escape sequence */ exp_length = utf8_active ? (uint4)((SIZEOF(wint_t) * length) + (GTM_MB_LEN_MAX * length) + SIZEOF(gtm_int64_t)) : length; zint_restart = FALSE; if (tt_ptr->mupintr) { /* restore state to before job interrupt */ tt_state = &tt_ptr->tt_state_save; assertpro(ttwhichinvalid != tt_state->who_saved); if (dollar_zininterrupt) { tt_ptr->mupintr = FALSE; tt_state->who_saved = ttwhichinvalid; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZINTDIRECT); } assert(length == tt_state->length); assertpro(dmread == tt_state->who_saved); /* ZINTRECURSEIO should have caught */ mv_zintdev = io_find_mvstent(io_ptr, FALSE); if (mv_zintdev && mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid) { buffer_start = (unsigned char *)mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr; current_ptr = buffer_start; mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; mv_zintdev->mv_st_cont.mvs_zintdev.io_ptr = NULL; if (mv_chain == mv_zintdev) POP_MV_STENT(); /* pop if top of stack */ buffer_moved = (buffer_start != tt_state->buffer_start); if (utf8_active) { /* need to properly align U32 buffer */ assert(exp_length == tt_state->exp_length); buffer_32_start = (wint_t *)ROUND_UP2((INTPTR_T)(buffer_start + (GTM_MB_LEN_MAX * length)), SIZEOF(gtm_int64_t)); if (buffer_moved && (((unsigned char *)buffer_32_start - buffer_start) != ((unsigned char *)tt_state->buffer_32_start - tt_state->buffer_start))) memmove(buffer_32_start, buffer_start + ((unsigned char *)tt_state->buffer_32_start - tt_state->buffer_start), (SIZEOF(wint_t) * length)); current_32_ptr = buffer_32_start; utf8_more = tt_state->utf8_more; more_ptr = tt_state->more_ptr; memcpy(more_buf, tt_state->more_buf, SIZEOF(more_buf)); } instr = tt_state->instr; outlen = tt_state->outlen; dx = tt_state->dx; dx_start = tt_state->dx_start; dx_instr = tt_state->dx_instr; dx_outlen = tt_state->dx_outlen; index = tt_state->index; insert_mode = tt_state->insert_mode; cl = tt_state->cl; escape_length = tt_state->escape_length; memcpy(escape_sequence, tt_state->escape_sequence, ESC_LEN); tt_state->who_saved = ttwhichinvalid; tt_ptr->mupintr = FALSE; zint_restart = TRUE; } } if (!zint_restart) { ENSURE_STP_FREE_SPACE(exp_length); buffer_start = current_ptr = stringpool.free; if (utf8_active) { buffer_32_start = (wint_t *)ROUND_UP2((INTPTR_T)(stringpool.free + (GTM_MB_LEN_MAX * length)), SIZEOF(gtm_int64_t)); current_32_ptr = buffer_32_start; } instr = outlen = 0; dx_instr = dx_outlen = 0; utf8_more = 0; io_ptr->esc_state = START; io_ptr->dollar.za = 0; io_ptr->dollar.zeof = FALSE; dx_start = (int)io_ptr->dollar.x; index = 0; cl = clmod(comline_index - index); } mask = tt_ptr->term_ctrl; if (dmterm_default) { /* $view("DMTERM") or gtm_dmterm is set. Ignore the customized terminators; use the default terinators */ memset(&mask_term.mask[0], 0, SIZEOF(io_termmask)); if (utf8_active) { mask_term.mask[0] = TERM_MSK_UTF8_0; mask_term.mask[4] = TERM_MSK_UTF8_4; } else mask_term.mask[0] = TERM_MSK; } else mask_term = tt_ptr->mask_term; mask_term.mask[ESC / NUM_BITS_IN_INT4] &= ~(1 << ESC); ioptr_width = io_ptr->width; if (!zint_restart) { DOWRITE_A(tt_ptr->fildes, &cr, 1); WRITE_GTM_PROMPT; } /* to turn keypad on if possible */ # ifndef __MVS__ if (!zint_restart && (NULL != KEYPAD_XMIT) && (keypad_len = STRLEN(KEYPAD_XMIT))) /* NOTE assignment */ DOWRITE(tt_ptr->fildes, KEYPAD_XMIT, keypad_len); # endif while (outlen < length) { if (outofband) { if (jobinterrupt == outofband) { /* save state if jobinterrupt */ tt_state = &tt_ptr->tt_state_save; tt_state->who_saved = dmread; tt_state->length = length; tt_state->buffer_start = buffer_start; PUSH_MV_STENT(MVST_ZINTDEV); mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)buffer_start; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = exp_length; mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE; mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr; if (utf8_active) { tt_state->exp_length = exp_length; tt_state->buffer_32_start = buffer_32_start; tt_state->utf8_more = utf8_more; tt_state->more_ptr = more_ptr; memcpy(tt_state->more_buf, more_buf, SIZEOF(more_buf)); } if (IS_AT_END_OF_STRINGPOOL(buffer_start, 0)) stringpool.free += exp_length; /* reserve space */ tt_state->instr = instr; tt_state->outlen = outlen; tt_state->dx = dx; tt_state->dx_start = dx_start; tt_state->dx_instr = dx_instr; tt_state->dx_outlen = dx_outlen; tt_state->index = index; tt_state->insert_mode = insert_mode; tt_state->cl = cl; tt_state->escape_length = escape_length; memcpy(tt_state->escape_sequence, escape_sequence, ESC_LEN); tt_ptr->mupintr = TRUE; } else instr = 0; if (sighup == outofband) exi_condition = -ERR_TERMHANGUP; TAREF1(save_xfer_root, outofband).event_state = pending; async_action(FALSE); break; } assertpro(FD_SETSIZE > tt_ptr->fildes); FD_ZERO(&input_fd); FD_SET(tt_ptr->fildes, &input_fd); assert(0 != FD_ISSET(tt_ptr->fildes, &input_fd)); /* Arbitrarily-chosen timeout value to prevent consumption of resources in tight loop when no input is available. */ input_timeval.tv_sec = 100; input_timeval.tv_usec = 0; /* N.B. On some Unix systems, the documentation for select() is ambiguous with respect to the first argument. * It specifies the number of contiguously-numbered file descriptors to be tested, starting with descriptor zero. * Thus, it should be equal to the highest-numbered file descriptor to test plus one. * (See _UNIX_Network_Programming_ by W. Richard Stevens, Section 6.13, pp. 328-331) */ selstat = select(tt_ptr->fildes + 1, (void *)&input_fd, (void *)NULL, (void *)NULL, &input_timeval); if (0 > selstat) if (EINTR != errno) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); else continue; else if (0 == selstat) continue; /* timeout but still not ready for reading, try again */ /* selstat > 0; try reading something */ else if (0 > (status = (int)read(tt_ptr->fildes, &inbyte, 1))) { /* Error return from read(). */ if (EINTR != errno) { /* If error was EINTR, go to the top of the loop to check for outofband. */ tt_ptr->discard_lf = FALSE; io_ptr->dollar.za = ZA_IO_ERR; rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); } else continue; } else if (0 == status) { /* select() says there's something to read, but read() found zero characters; assume connection dropped. */ ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, FALSE, FALSE); /* FALSE, FALSE: READ tt not socket */ tt_ptr->discard_lf = FALSE; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_IOEOF); } else if (0 < status) { /* select() says it's ready to read and read() found some data */ assert(0 != FD_ISSET(tt_ptr->fildes, &input_fd)); /* set prin_in_dev_failure to FALSE in case it was set to TRUE in the previous read which may have failed */ prin_in_dev_failure = FALSE; # ifdef UTF8_SUPPORTED if (utf8_active) { if (tt_ptr->discard_lf) { /* saw CR last time so ignore following LF */ tt_ptr->discard_lf = FALSE; if (NATIVE_LF == inbyte) continue; } if (utf8_more) { /* needed extra bytes */ *more_ptr++ = inbyte; if (--utf8_more) continue; /* get next byte */ UTF8_MBTOWC(more_buf, more_ptr, inchar); if (WEOF == inchar) { /* invalid char */ io_ptr->dollar.za = ZA_IO_ERR; utf8_badchar((int)(more_ptr - more_buf), more_buf, more_ptr, 0, NULL); break; } } else if (0 < (utf8_more = UTF8_MBFOLLOW(&inbyte))) /* NOTE assignment */ { if (0 > utf8_more) { /* invalid character */ io_ptr->dollar.za = ZA_IO_ERR; more_buf[0] = inbyte; utf8_badchar(1, more_buf, &more_buf[1], 0, NULL); /* ERR_BADCHAR */ break; } else if (GTM_MB_LEN_MAX < utf8_more) { /* too big to be valid */ io_ptr->dollar.za = ZA_IO_ERR; more_buf[0] = inbyte; utf8_badchar(1, more_buf, &more_buf[1], 0, NULL); /* ERR_BADCHAR */ break; } else { more_ptr = more_buf; *more_ptr++ = inbyte; continue; /* get next byte */ } } else { /* single byte */ more_buf[0] = inbyte; more_buf[1] = 0; UTF8_MBTOWC(more_buf, &more_buf[1], inchar); if (WEOF == inchar) { /* invalid char */ io_ptr->dollar.za = ZA_IO_ERR; utf8_badchar(1, more_buf, &more_buf[1], 0, NULL); /* ERR_BADCHAR */ break; } } if (!tt_ptr->done_1st_read) { tt_ptr->done_1st_read = TRUE; if (BOM_CODEPOINT == inchar) continue; } if (mask & TRM_CONVERT) inchar = u_toupper(inchar); GTM_IO_WCWIDTH(inchar, inchar_width); } else { # endif if (mask & TRM_CONVERT) NATIVE_CVT2UPPER(inbyte, inbyte); inchar = inbyte; inchar_width = 1; # ifdef UTF8_SUPPORTED } # endif GETASCII(asc_inchar,inchar); if ((dx >= ioptr_width) && io_ptr->wrap && !(mask & TRM_NOECHO)) { DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */ dx = 0; } terminator_seen = FALSE; if (!utf8_active || (ASCII_MAX >= INPUT_CHAR)) { msk_num = (uint4)INPUT_CHAR / NUM_BITS_IN_INT4; msk_in = (1 << ((uint4)INPUT_CHAR % NUM_BITS_IN_INT4)); if (msk_in & mask_term.mask[msk_num]) terminator_seen = TRUE; } else if (utf8_active && tt_ptr->default_mask_term && ((INPUT_CHAR == u32_line_term[U32_LT_NL]) || (INPUT_CHAR == u32_line_term[U32_LT_LS]) || (INPUT_CHAR == u32_line_term[U32_LT_PS]))) { /* UTF and default terminators and UTF terminators above ASCII_MAX */ terminator_seen = TRUE; } if (terminator_seen) { /* carriage return or other line terminator has been typed */ STORE_OFF(0, outlen); if (utf8_active && (ASCII_CR == INPUT_CHAR)) tt_ptr->discard_lf = TRUE; /* exceeding the maximum length exits the while loop, so it must fit here . */ # ifdef UTF8_SUPPORTED if (utf8_active) { /* recall buffer kept as UTF-8 */ matched = TRUE; for (match_length = 0; (SIZEOF(RECALL) - 1) > match_length && outlen > match_length; match_length++) { if (ASCII_MAX < GET_OFF(match_length) || recptr[match_length] != (char)GET_OFF(match_length)) { matched = FALSE; break; } } if (!matched && (outlen > match_length) && (((SIZEOF(REC) - 1) == match_length) || (SIZEOF(RECALL) == match_length)) && ((' ' == GET_OFF(match_length)) || ('\t' == GET_OFF(match_length)))) matched = TRUE; /* REC or RECALL then space or tab */ else if (matched) if (((SIZEOF(RECALL) - 1) != match_length) && ((SIZEOF(REC) - 1) != match_length)) matched = FALSE; /* wrong size */ else if ((outlen > match_length) && (' ' != GET_OFF(match_length) && ('\t' != GET_OFF(match_length)))) matched = FALSE; /* or RECALL then not eol, space, or tab */ if (!matched) break; /* not RECALL so end of line */ match_length++; /* get past space or tab */ if (outlen <= match_length) argv[1] = NULL; /* nothing after RECALL */ else argv[1] = (char *)buffer_start; for (outptr = buffer_start ; outlen > match_length; match_length++) { inchar = GET_OFF(match_length); outptr = UTF8_WCTOMB(inchar, outptr); if ((ASCII_MAX > GET_OFF(match_length)) && ((' ' == GET_OFF(match_length)) || ('\t' == GET_OFF(match_length)))) break; } *outptr = '\0'; } else { # endif match_length = (uint4)strcspn((const char *)buffer_start, delimiter_string); /* only "rec" and "recall" should be accepted */ if (((strlen(REC) == match_length) || (strlen(RECALL) == match_length)) && (0 == strncmp((const char *)buffer_start, RECALL, match_length))) { for (argv1 = buffer_start + match_length; !(IS_AT_END_OF_STRINGPOOL(argv1, 0)) && (' ' != argv1[0]) && ('\t' != argv1[1]); argv1++); argv[1] = (char *)((IS_AT_END_OF_STRINGPOOL(argv1, 0)) ? NULL : argv1); } else break; /* not RECALL so end of line */ # ifdef UTF8_SUPPORTED } # endif index = 0; DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */ DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */ if (argv[1] == NULL) { /* print out all the history strings */ for (hist = recall_num; hist > 0; hist--) { cur_value = recall_num + 1 - hist; temp_str[MAX_RECALL_NUMBER_LENGTH] = ' '; for (histidx = MAX_RECALL_NUMBER_LENGTH - 1; histidx >= 0; histidx--) { temp_str[histidx] = '0' + cur_value % 10; cur_value = cur_value / 10; if (('0' == temp_str[histidx]) && (0 == cur_value)) temp_str[histidx] = ' '; } cur_cl = clmod(comline_index - hist); DOWRITE_A(tt_ptr->fildes, temp_str, SIZEOF(temp_str)); write_str((unsigned char *)comline_base[cur_cl].addr, comline_base[cur_cl].len, SIZEOF(temp_str), TRUE, TRUE); DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */ } DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */ WRITE_GTM_PROMPT; instr = dx_instr = outlen = dx_outlen = 0; } else { histidx = -1; if (!cli_is_dcm(argv[1])) { /* Not a positive decimal number */ len = STRLEN(argv[1]); for (hist = 1; hist <= recall_num; hist++) { if (0 == strncmp(comline_base[clmod(comline_index - hist)].addr, argv[1] , len)) { histidx = clmod(comline_index - hist); break; } } if (-1 == histidx) err_recall = ERR_NO_MATCH_STR; /* no matching string found */ } else { if (ATOI(argv[1]) > recall_num) err_recall = ERR_OUT_OF_BOUNDS; /* out of bounds error */ else histidx = clmod(comline_index + ATOI(argv[1]) - recall_num - 1); } if (-1 != histidx) { WRITE_GTM_PROMPT; write_str((unsigned char *)comline_base[histidx].addr, comline_base[histidx].len, dx_start, TRUE, TRUE); # ifdef UTF8_SUPPORTED if (utf8_active) { /* note out* variables are used for input here */ len = comline_base[histidx].len; instr = dx_instr = 0; outtop = (unsigned char *)comline_base[histidx].addr + len; for (outptr = (unsigned char *)comline_base[histidx].addr; outptr < outtop; ) { outptr = UTF8_MBTOWC(outptr, outtop, inchar); if (WEOF != inchar) { STORE_OFF(inchar, instr); instr++; GTM_IO_WCWIDTH(inchar, inchar_width); dx_instr += inchar_width; } } outlen = instr; dx_outlen = dx_instr; } else { /* using memcpy since areas definitely don't overlap. */ # endif memcpy(buffer_start, comline_base[histidx].addr, comline_base[histidx].len); instr = outlen = comline_base[histidx].len; dx_instr = dx_outlen = instr; # ifdef UTF8_SUPPORTED } # endif dx = (dx_start + dx_outlen) % ioptr_width; } } if (NO_ERROR != err_recall) { DOWRITE_A(tt_ptr->fildes, recall_error_msg[err_recall], strlen((char *)recall_error_msg[err_recall])); DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */ DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */ WRITE_GTM_PROMPT; instr = dx_instr = outlen = dx_outlen = 0; } continue; /* to allow more input */ } if ((((int)inchar == tt_ptr->ttio_struct->c_cc[VERASE]) || (((NULL != KEY_BACKSPACE) && ('\0' == KEY_BACKSPACE[1]) && (inchar == KEY_BACKSPACE[0])))) && !(mask & TRM_PASTHRU)) { if (0 < instr) { MOVE_CURSOR_LEFT_ONE_CHAR(dx, instr, dx_instr, dx_start, ioptr_width); DEL_ONE_CHAR_AT_CURSOR(outlen, dx_outlen, dx, dx_instr, dx_start, ioptr_width); } } else if (NATIVE_ESC == inchar) { escape_sequence[escape_length++] = (unsigned char)inchar; assert(ESC_LEN > escape_length); io_ptr->esc_state = START; iott_escape(&escape_sequence[escape_length - 1], &escape_sequence[escape_length], io_ptr); } else if (0 != escape_length) { if (utf8_active && (ASCII_MAX < inchar)) continue; /* skip invalid char in escape sequence */ /* If the escape sequence happens to be more than ESC_LEN (allocated buffer length), truncate * the escape sequence but do call "iott_escape" to know when the sequence terminates and the * rest of the command line can be parsed. We do not expect the regular cursor key escape * sequences (UP/DOWN/LEFT/RIGHT/DELETE/INSERT) to be more than ESC_LEN (and hence not suffer * from truncation) and that is what we care about later in this code. */ if (ESC_LEN <= escape_length) escape_length--; escape_sequence[escape_length++] = (unsigned char)inchar; iott_escape(&escape_sequence[escape_length - 1], &escape_sequence[escape_length], io_ptr); } else { switch (inchar) { case EDIT_SOL: { /* ctrl A - start of line */ num_lines_above = (dx_instr + dx_start) / ioptr_width; num_chars_left = dx - dx_start; move_cursor(tt_ptr->fildes, num_lines_above, num_chars_left); instr = dx_instr = 0; dx = dx_start; break; } case EDIT_EOL: { /* ctrl E- end of line */ num_lines_above = (dx_instr + dx_start) / ioptr_width - (dx_outlen + dx_start) / ioptr_width; /* For some reason, a CURSOR_DOWN ("\n") seems to reposition the cursor * at the beginning of the next line rather than maintain the vertical * position. Therefore if we are moving down, we need to calculate * the num_chars_left differently to accommodate this. */ if (0 <= num_lines_above) num_chars_left = dx - (dx_outlen + dx_start) % ioptr_width; else num_chars_left = - ((dx_outlen + dx_start) % ioptr_width); move_cursor(tt_ptr->fildes, num_lines_above, num_chars_left); instr = outlen; dx_instr = dx_outlen; dx = (dx_outlen + dx_start) % ioptr_width; break; } case EDIT_LEFT: { /* ctrl B - left one */ if (0 != instr) { dx_prev = compute_dx(BUFF_ADDR(0), instr - 1, ioptr_width, dx_start); inchar_width = dx_instr - dx_prev; move_cursor_left(dx, inchar_width); instr--; dx = (dx - inchar_width + ioptr_width) % ioptr_width; dx_instr -= inchar_width; } break; } case EDIT_RIGHT: { /* ctrl F - right one */ if (instr != outlen) { dx_next = compute_dx(BUFF_ADDR(0), instr + 1, ioptr_width, dx_start); inchar_width = dx_next - dx_instr; move_cursor_right(dx, inchar_width); instr++; dx = (dx + inchar_width) % ioptr_width; dx_instr += inchar_width; } break; } case EDIT_DEOL: { /* ctrl K - delete to end of line */ write_str_spaces(dx_outlen - dx_instr, dx, FALSE); SET_BUFF(instr, ' ', outlen - instr); outlen = instr; dx_outlen = dx_instr; break; } case EDIT_ERASE: { /* ctrl U - delete whole line */ num_lines_above = (dx_instr + dx_start) / ioptr_width; num_chars_left = dx - dx_start; move_cursor(tt_ptr->fildes, num_lines_above, num_chars_left); SET_BUFF(0, ' ', outlen); write_str_spaces(dx_outlen, dx_start, FALSE); instr = 0; outlen = 0; dx = dx_start; dx_instr = dx_outlen = 0; break; } case EDIT_DELETE: { /* ctrl D - delete char */ if (0 == outlen) { /* line is empty new line and exit - Thanks to Sam Habiel */ op_wteol(1); /* below not restricted because of halt with return code */ op_zhalt(ERR_IOEOF, FALSE); } DEL_ONE_CHAR_AT_CURSOR(outlen, dx_outlen, dx, dx_instr, dx_start, ioptr_width); break; } default: { if (instr == outlen) { /* at end of input */ STORE_OFF(inchar, instr); write_str(BUFF_ADDR(instr), 1, dx, FALSE, FALSE); } else { if (insert_mode) MOVE_BUFF(instr + 1, BUFF_ADDR(instr), outlen - instr) else { GTM_IO_WCWIDTH(GET_OFF(instr), delchar_width); delta_width = inchar_width - delchar_width; } STORE_OFF(inchar, instr); /* First write spaces on all the display columns that the current string * occupied. Then overwrite that with the new string. This way we are * guaranteed all display columns are clean. Note that this space * overwrite is needed even in case of insert mode because due to * differing wide-character alignments before and after the insert, it * is possible that a column might be left empty in the post insert * write of the new string even though it had something displayed before. */ write_str_spaces(dx_outlen - dx_instr, dx, FALSE); write_str(BUFF_ADDR(instr), outlen - instr + (insert_mode ? 1 : 0), dx, FALSE, FALSE); } if (insert_mode || (instr == outlen)) outlen++; instr++; /* Compute value of dollarx at the new cursor position */ dx_cur = compute_dx(BUFF_ADDR(0), instr, ioptr_width, dx_start); inchar_width = dx_cur - dx_instr; move_cursor_right(dx, inchar_width); dx = (dx + inchar_width) % ioptr_width; dx_instr = dx_cur; dx_outlen = compute_dx(BUFF_ADDR(0), outlen, ioptr_width, dx_start); break; } } /* Ensure that the actual display position of the current character matches the computed value */ assert(dx_instr == compute_dx(BUFF_ADDR(0), instr, ioptr_width, dx_start)); assert(dx_outlen == compute_dx(BUFF_ADDR(0), outlen, ioptr_width, dx_start)); } } if ((0 != escape_length) && (FINI <= io_ptr->esc_state)) { /* The arbitrary value -1 signifies inequality in case KEY_* is NULL */ down = (NULL != KEY_DOWN) ? strncmp((const char *)escape_sequence, KEY_DOWN, escape_length) : -1; up = (NULL != KEY_UP) ? strncmp((const char *)escape_sequence, KEY_UP, escape_length) : -1; right = (NULL != KEY_RIGHT) ? strncmp((const char *)escape_sequence, KEY_RIGHT, escape_length) : -1; left = (NULL != KEY_LEFT) ? strncmp((const char *)escape_sequence, KEY_LEFT, escape_length) : -1; backspace = (NULL != KEY_BACKSPACE) ? strncmp((const char *)escape_sequence, KEY_BACKSPACE, escape_length) : -1; delete = (NULL != KEY_DC) ? strncmp((const char *)escape_sequence, KEY_DC, escape_length) : -1; insert_key = ((NULL != KEY_INSERT) && ('\0' != KEY_INSERT[0])) ? strncmp((const char *)escape_sequence, KEY_INSERT, escape_length) : -1; memset(escape_sequence, '\0', escape_length); escape_length = 0; if (BADESC == io_ptr->esc_state) { io_ptr->esc_state = START; break; } if ((0 == delete) || (0 == backspace)) { if ((0 == backspace) && (0 < instr)) MOVE_CURSOR_LEFT_ONE_CHAR(dx, instr, dx_instr, dx_start, ioptr_width) DEL_ONE_CHAR_AT_CURSOR(outlen, dx_outlen, dx, dx_instr, dx_start, ioptr_width); } if (0 == insert_key) insert_mode = !insert_mode; /* toggle */ else if ((0 == up) || (0 == down)) { DOWRITE_A(tt_ptr->fildes, &cr, 1); WRITE_GTM_PROMPT; if (NULL != CLR_EOL) gtm_tputs(CLR_EOL, 1, outc); instr = dx_instr = outlen = dx_outlen = 0; if (0 == up) { if (((MAX_RECALL + 1 != index) && (0 != (*(comline_base + cl)).len) || (0 == index))) index++; } else { assert (0 == down); if (0 != index) --index; } if ((0 < index) && (MAX_RECALL >= index)) { cl = clmod (comline_index - index); instr = outlen = comline_base[cl].len; # ifdef UTF8_SUPPORTED if (utf8_active) { len = comline_base[cl].len; instr = dx_instr = 0; outtop = (unsigned char *)comline_base[cl].addr + len; for (outptr = (unsigned char *)comline_base[cl].addr; outptr < outtop; ) { outptr = UTF8_MBTOWC(outptr, outtop, inchar); if (WEOF != inchar) { STORE_OFF(inchar, instr); instr++; } } outlen = instr; dx_instr = compute_dx(BUFF_ADDR(0), outlen, ioptr_width, dx_start); dx_outlen = dx_instr; } else { # endif if (0 != instr) memcpy(buffer_start, comline_base[cl].addr, outlen); dx_instr = dx_outlen = comline_base[cl].len; # ifdef UTF8_SUPPORTED } # endif dx = (unsigned)(dx_instr + dx_start) % ioptr_width; if (0 != instr) write_str(BUFF_ADDR(0), instr, dx_start, TRUE, FALSE); } } else if (!(mask & TRM_NOECHO) && !((0 == right) && (instr == outlen)) && !((0 == left) && (0 == instr))) { if (0 == right) { dx_next = compute_dx(BUFF_ADDR(0), instr + 1, ioptr_width, dx_start); inchar_width = dx_next - dx_instr; move_cursor_right(dx, inchar_width); instr++; dx = (dx + inchar_width) % ioptr_width; dx_instr += inchar_width; } if (0 == left) { dx_prev = compute_dx(BUFF_ADDR(0), instr - 1, ioptr_width, dx_start); inchar_width = dx_instr - dx_prev; move_cursor_left(dx, inchar_width); instr--; dx = (dx - inchar_width + ioptr_width) % ioptr_width; dx_instr -= inchar_width; } } io_ptr->esc_state = START; } } /* turn keypad off */ # ifndef __MVS__ if ((NULL != KEYPAD_LOCAL) && (keypad_len = STRLEN(KEYPAD_LOCAL))) /* NOTE assignment */ DOWRITE(tt_ptr->fildes, KEYPAD_LOCAL, keypad_len); # endif if (outlen == length) outlen = length - 1; io_ptr->dollar.za = 0; v->mvtype = MV_STR; # ifdef UTF8_SUPPORTED if (utf8_active) { int i; outptr = buffer_start; outtop = ((unsigned char *)buffer_32_start); current_32_ptr = buffer_32_start; for (i = 0; i < outlen && outptr < outtop; i++, current_32_ptr++) outptr = UTF8_WCTOMB(*current_32_ptr, outptr); v->str.len = INTCAST(outptr - buffer_start); } else # endif v->str.len = outlen; v->str.addr = (char *)buffer_start; if (0 != v->str.len) { cl = clmod (comline_index - 1); if ((v->str.len != comline_base[cl].len) || (memcmp(comline_base[cl].addr, buffer_start, v->str.len))) { comline_base[comline_index] = v->str; comline_index = clmod (comline_index + 1); if (MAX_RECALL != recall_num) recall_num ++; } if (IS_AT_END_OF_STRINGPOOL(buffer_start, 0)) stringpool.free += v->str.len; /* otherwise using space from before interrupt */ } if (!(mask & TRM_NOECHO)) { if ((io_ptr->dollar.x += dx_outlen) >= ioptr_width && io_ptr->wrap) { io_ptr->dollar.y += (io_ptr->dollar.x / ioptr_width); if (0 != io_ptr->length) io_ptr->dollar.y %= io_ptr->length; io_ptr->dollar.x %= ioptr_width; if (0 == io_ptr->dollar.x) DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */ } } active_device = 0; return; } fis-gtm-V7.0-005/sr_unix/do_semop.c0000755000032200000250000000261414342376327016026 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 #include #include "gtm_stdlib.h" #include "gtm_ipc.h" #include "gtm_fcntl.h" #include "do_semop.h" #include "gtm_c_stack_trace.h" #include "gtm_c_stack_trace_semop.h" static struct sembuf sop[1]; /* perform one semop, returning errno if it was unsuccessful */ /* maintain in parallel with eintr_wrapper_semop.h */ int do_semop(int sems, int num, int op, int flg) { boolean_t wait_option; int rv = -1; wait_option = ((!(flg & IPC_NOWAIT)) && (0 == op)); sop[0].sem_num = num; sop[0].sem_op = op; sop[0].sem_flg = flg; CHECK_SEMVAL_GRT_SEMOP(sems, num, op); if (wait_option) { rv = try_semop_get_c_stack(sems, sop, 1); /* try with patience and possible stack trace of blocker */ return rv; } while (-1 == (rv = semop(sems, sop, 1)) && ((EINTR == errno))) ; return (-1 == rv) ? errno : 0; /* return errno if not success */ } fis-gtm-V7.0-005/sr_unix/do_semop.h0000755000032200000250000000151214342376327016027 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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. * * * ****************************************************************/ #ifndef DO_SEMOP_INCLUDED #define DO_SEMOP_INCLUDED error_def(ERR_NOMORESEMCNT); int do_semop(int sems, int num, int op, int flg); /* Check whether a counter semaphore is ignored or not */ #define IS_SEM_COUNTER_ONLINE(HDR, COUNTER_HALTED) (!((HDR)->mumps_can_bypass) || !(COUNTER_HALTED)) #endif /* DO_SEMOP_INCLUDED */ fis-gtm-V7.0-005/sr_unix/do_shmat.c0000755000032200000250000000370014342376327016014 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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. * * * ****************************************************************/ /* do_shmat.c UNIX - Attach shared memory (stub) * Note: The TANDEM (NonStop/UX) requires a special version of this routine * so that attaching shared memory does not interfere with malloc. */ #include "mdef.h" #include #include #include #ifndef __MVS__ #include #endif #include "gtm_stdio.h" #include "gtm_ipc.h" #include "do_shmat.h" void *do_shmat(int4 shmid, const void *shm_base, int4 shmflg) { # ifdef __sparc return(shmat((int)shmid, shm_base, shmflg | SHM_SHARE_MMU)); # else return(shmat((int)shmid, shm_base, shmflg)); # endif } /* This is do_shmat + capability to execute code from shared memory. * On platforms that support the SHM_EXEC bit (as of Oct 2014 this is only Linux) we use it. * On those that dont, we use mprotect (additional system call) on top of shmat. * Until SHM_EXEC is not available on all POSIX platforms that GT.M is built/supported on, we need the mprotect code. */ void *do_shmat_exec_perm(int4 shmid, size_t shm_size, int *save_errno) { int4 shmflg; void *addr; # if defined(SHM_EXEC) shmflg = SHM_EXEC; # else shmflg = 0; # endif addr = do_shmat(shmid, NULL, shmflg); if (-1 == (sm_long_t)addr) { *save_errno = errno; return addr; } # if !defined(SHM_EXEC) if (-1 == mprotect(addr, shm_size, PROT_READ | PROT_WRITE | PROT_EXEC)) { *save_errno = errno; assert(FALSE); SHMDT(addr); addr = (void *)-1; } # endif return addr; } fis-gtm-V7.0-005/sr_unix/do_shmat.h0000755000032200000250000000131514342376327016021 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef DO_SHMAT_H_INCLUDED #define DO_SHMAT_H_INCLUDED void *do_shmat(int4 shmid, const void *shm_base, int4 shmflg); void *do_shmat_exec_perm(int4 shmid, size_t shm_size, int *save_errno); #endif fis-gtm-V7.0-005/sr_unix/do_verify.c0000755000032200000250000000145114342376327016205 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* do_verify.c UNIX - call user-supplied collation type and version * verification routine. */ #include "mdef.h" #include "collseq.h" int4 do_verify(collseq *csp, unsigned char type, unsigned char ver) { assert(NULL != csp); return ((*csp->verify)(type,ver) == 0); } fis-gtm-V7.0-005/sr_unix/do_xform.c0000644000032200000250000000722314342376327016034 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "gtm_string.h" #include "mdef.h" #include "gtm_descript.h" #include "min_max.h" #include "collseq.h" #include "do_xform.h" #include "memprot.h" error_def(ERR_COLLARGLONG); error_def(ERR_COLTRANSSTR2LONG); void do_xform(collseq *csp, int fc_type, mstr *input, mstr *output, int *length) { gtm32_descriptor outbuff1, insub1; gtm_descriptor outbuff, insub; int4 status; char *ba, *addr; DEBUG_ONLY(static boolean_t in_do_xform;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!in_do_xform); DO_XFORM_RETURN_IF_NULL_STRING(input, output, length); DEBUG_ONLY(in_do_xform = TRUE;) assert (0 == csp->argtype || 1 == csp->argtype); assert(XFORM == fc_type || XBACK == fc_type); if (0 == csp->argtype) { if (MAX_STRLEN_32K < input->len) { DEBUG_ONLY(in_do_xform = FALSE;) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_COLLARGLONG, 1, csp->act); } insub.type = DSC_K_DTYPE_T; insub.len = input->len; insub.val = input->addr; outbuff.type = DSC_K_DTYPE_T; outbuff.len = MIN(output->len, MAX_STRLEN_32K); memprot(&(TREF(protmem_ba)), outbuff.len); ba = (TREF(protmem_ba)).addr; assert(NULL != ba); outbuff.val = (NULL != ba) ? ba : output->addr; if (XFORM == fc_type) status = (csp->xform)(&insub, 1, &outbuff, length); else status = (csp->xback)(&insub, 1, &outbuff, length); if (*length > output->len) { DEBUG_ONLY(in_do_xform = FALSE;) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_COLTRANSSTR2LONG, 1, csp->act); } /* If collation routine has changed outbuff.val (which it will if it cannot store the transformed * result in the buffer that is passed in), the transformed value is stored in the buffer allocated * externally by the collation routine. In this case, update output->addr before returning. */ addr = (NULL != ba) ? ba : output->addr; if (outbuff.val != addr) output->addr = outbuff.val; else if (NULL != ba) { assert(*length <= outbuff.len); memcpy(output->addr, ba, *length); } } else { insub1.type = DSC_K_DTYPE_T; insub1.len = input->len; insub1.val = input->addr; outbuff1.type = DSC_K_DTYPE_T; outbuff1.len = output->len; memprot(&(TREF(protmem_ba)), outbuff1.len); ba = (TREF(protmem_ba)).addr; assert(NULL != ba); outbuff1.val = (NULL != ba) ? ba : output->addr; if (XFORM == fc_type) status = (csp->xform)(&insub1, 1, &outbuff1, length); else status = (csp->xback)(&insub1, 1, &outbuff1, length); if (*length > output->len) { DEBUG_ONLY(in_do_xform = FALSE;) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_COLTRANSSTR2LONG, 1, csp->act); } /* If collation routine has changed outbuff1.val (which it will if it cannot store the transformed * result in the buffer that is passed in), the transformed value is stored in the buffer allocated * externally by the collation routine. In this case, update output->addr before returning. */ addr = (NULL != ba) ? ba : output->addr; if (outbuff1.val != addr) output->addr = outbuff1.val; else if (NULL != ba) { assert(*length <= outbuff1.len); memcpy(output->addr, ba, *length); } } DEBUG_ONLY(in_do_xform = FALSE;) if (status) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status); } fis-gtm-V7.0-005/sr_unix/dollarh.c0000644000032200000250000000341614342376327015644 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_time.h" #include #include "dollarh.h" #include "have_crit.h" error_def(ERR_WEIRDSYSTIME); long dollarh(time_t intime, uint4 *days, time_t *seconds) { gtm_int8 local_wall_time_in_gmt, seconds_since_m_epoch; int isdst; long offset; struct tm *ttime; GTM_LOCALTIME(ttime, &intime); *seconds = (time_t)(ttime->tm_hour * HOUR) + (time_t)(ttime->tm_min * MINUTE) + (time_t)ttime->tm_sec; # ifdef _BSD_SOURCE /* the BSD structure provides the UTC offset in seconds */ offset = -1L * ttime->tm_gmtoff; /* using 1L here and 1LL below makes 32 bit platforms OK */ # else /* otherwise have to calulate it */ isdst = ttime->tm_isdst; GTM_GMTIME(ttime, &intime); /* recast intime to UTC */ ttime->tm_isdst = isdst; GTM_MKTIME(local_wall_time_in_gmt, ttime); /* turn it back into seconds */ assert(local_wall_time_in_gmt != -1); offset = local_wall_time_in_gmt - intime; /* subtract the original time to determine UTC offset */ # endif seconds_since_m_epoch = (intime - offset) + (1LL * DAYS * ONEDAY); if (seconds_since_m_epoch < 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_WEIRDSYSTIME); *days = (uint4)(seconds_since_m_epoch / ONEDAY); /* after adjusting for UTC we can get the days */ return offset; } fis-gtm-V7.0-005/sr_unix/dollarh.h0000755000032200000250000000122314342376327015646 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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. * * * ****************************************************************/ #ifndef DOLLARH_INCLUDED #define DOLLARH_INCLUDED long dollarh(time_t intime, uint4 *days, time_t *seconds); #endif /* DOLLARH_INCLUDED */ fis-gtm-V7.0-005/sr_unix/dpgbldir_sysops.c0000644000032200000250000001327614342376327017433 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_stat.h" #include #ifdef __MVS__ #include "gtm_zos_io.h" #endif #include "parse_file.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gbldirnam.h" #include "gtmio.h" #include "eintr_wrappers.h" #include "stringpool.h" #include "is_gdid.h" #include "dpgbldir.h" #include "dpgbldir_sysops.h" #include "gtm_logicals.h" char LITDEF gde_labels[GDE_LABEL_NUM][GDE_LABEL_SIZE] = { GDE_LABEL_LITERAL }; GBLREF mval dollar_zgbldir; GBLREF gd_addr *gd_header; error_def(ERR_ZGBLDIRACC); error_def(ERR_IOEOF); #ifdef __MVS__ /* Need the ERR_BADTAG and ERR_TEXT error_defs for the TAG_POLICY macro warning */ error_def(ERR_TEXT); error_def(ERR_BADTAG); #endif /* 30 millisec is an arbitrarily chosen value yielding a wait that seems sufficient, but not too annoying */ #define WAIT_OUT_RENAME_GAP 30 mstr *get_name(mstr *ms) { int4 status; char c[MAX_FN_LEN + 1]; parse_blk pblk; mstr *new; memset(&pblk, 0, SIZEOF(pblk)); pblk.buffer = c; pblk.buff_size = MAX_FN_LEN; pblk.def1_buf = DEF_GDR_EXT; pblk.def1_size = SIZEOF(DEF_GDR_EXT) - 1; status = parse_file(ms,&pblk); if (!(status & 1)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_ZGBLDIRACC, 6, ms->len, ms->addr, LEN_AND_LIT(""), LEN_AND_LIT(""), status); new = (mstr *)malloc(SIZEOF(mstr)); new->len = pblk.b_esl; new->addr = (char *)malloc(pblk.b_esl); memcpy(new->addr, pblk.buffer, pblk.b_esl); return new; } void *open_gd_file(mstr *v) { file_pointer *fp; int4 lcnt; mstr temp; ZOS_ONLY(int realfiletag;) fp = (file_pointer*)malloc(SIZEOF(*fp)); fp->v.len = v->len; fp->v.addr = (char *)malloc(v->len + 1); memcpy(fp->v.addr, v->addr, v->len); *((char*)((char*)fp->v.addr + v->len)) = 0; /* Null terminate string */ for (lcnt = WAIT_OUT_RENAME_GAP; lcnt; lcnt--) { /* try OPEN for enough time to get past any possibility the file has gone missing due to a GDE making a revision */ if (FD_INVALID != (fp->fd = OPEN(fp->v.addr, O_RDONLY))) break; SHORT_SLEEP(lcnt); } if (FD_INVALID == fp->fd) { /* v gets passed down through a few levels, but should be freed */ /* Copy the values into the stringpool so they get cleaned up later */ ENSURE_STP_FREE_SPACE(fp->v.len); memcpy(stringpool.free, fp->v.addr, fp->v.len); temp.addr = (char*)stringpool.free; temp.len = fp->v.len; stringpool.free += fp->v.len; free(v->addr); free(v); free(fp->v.addr); free(fp); if (!dollar_zgbldir.str.len || ((dollar_zgbldir.str.len == temp.len) && !memcmp(dollar_zgbldir.str.addr, temp.addr, temp.len))) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_ZGBLDIRACC, 6, temp.len, temp.addr, LEN_AND_LIT(". Cannot continue"), LEN_AND_LIT(""), errno); assert(FALSE); } RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_ZGBLDIRACC, 6, temp.len, temp.addr, LEN_AND_LIT(". Retaining "), dollar_zgbldir.str.len, dollar_zgbldir.str.addr, errno); } #ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(fp->fd, TAG_BINARY, &realfiletag)) TAG_POLICY_SEND_MSG(fp->v.addr, errno, realfiletag, TAG_BINARY); #endif return (void *)fp; } bool comp_gd_addr(gd_addr *gd_ptr, file_pointer *file_ptr) { int fstat_res; struct stat buf; FSTAT_FILE(file_ptr->fd, &buf, fstat_res); if (-1 == fstat_res) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_ZGBLDIRACC, 6, file_ptr->v.len, file_ptr->v.addr, LEN_AND_LIT(""), LEN_AND_LIT(""), errno); return is_gdid_stat_identical(gd_ptr->id, &buf); } void fill_gd_addr_id(gd_addr *gd_ptr, file_pointer *file_ptr) { int fstat_res; struct stat buf; gd_ptr->id = (gd_id *)malloc(SIZEOF(gd_id)); FSTAT_FILE(file_ptr->fd, &buf, fstat_res); if (-1 == fstat_res) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_ZGBLDIRACC, 6, file_ptr->v.len, file_ptr->v.addr, LEN_AND_LIT(""), LEN_AND_LIT(""), errno); set_gdid_from_stat(gd_ptr->id, &buf); return; } void close_gd_file(file_pointer *file_ptr) { int rc; CLOSEFILE_RESET(file_ptr->fd, rc); /* resets "file_ptr->fd" to FD_INVALID */ free(file_ptr->v.addr); free(file_ptr); return; } void file_read(file_pointer *file_ptr, int4 size, uchar_ptr_t buff, int4 pos) { int4 save_errno; LSEEKREAD(file_ptr->fd, (off_t)(pos - 1 ) * DISK_BLOCK_SIZE, buff, size, save_errno); if (0 != save_errno) if (-1 == save_errno) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_IOEOF); else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_ZGBLDIRACC, 6, file_ptr->v.len, file_ptr->v.addr, LEN_AND_LIT(""), LEN_AND_LIT(""), save_errno); return; } void dpzgbini(void) { mstr temp_mstr; char temp_buff[MAX_FN_LEN + 1]; uint4 status; parse_blk pblk; temp_mstr.addr = GTM_GBLDIR; temp_mstr.len = SIZEOF(GTM_GBLDIR) - 1; memset(&pblk, 0, SIZEOF(pblk)); pblk.buffer = temp_buff; pblk.buff_size = MAX_FN_LEN; pblk.def1_buf = DEF_GDR_EXT; pblk.def1_size = SIZEOF(DEF_GDR_EXT) - 1; status = parse_file(&temp_mstr, &pblk); dollar_zgbldir.mvtype = MV_STR; dollar_zgbldir.str.len = SIZEOF(GTM_GBLDIR) - 1; dollar_zgbldir.str.addr = GTM_GBLDIR; if (status & 1) { dollar_zgbldir.str.len = pblk.b_esl; dollar_zgbldir.str.addr = pblk.buffer; } s2pool(&dollar_zgbldir.str); gd_header = NULL; } fis-gtm-V7.0-005/sr_unix/dpgbldir_sysops.h0000755000032200000250000000156614342376327017442 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef DPGBLDDIR_SYSOPS_H_INCLUDED #define DPGBLDDIR_SYSOPS_H_INCLUDED #include "gtmio.h" bool comp_gd_addr(gd_addr *gd_ptr, file_pointer *file_ptr); void fill_gd_addr_id(gd_addr *gd_ptr, file_pointer *file_ptr); void close_gd_file(file_pointer *file_ptr); void file_read(file_pointer *file_ptr, int4 size, uchar_ptr_t buff, int4 pos); void dpzgbini(void); #endif fis-gtm-V7.0-005/sr_unix/dse.c0000644000032200000250000001377314342376327015001 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "main_pragma.h" #include #include "gtm_inet.h" #include "gtm_signal.h" #include "mlkdef.h" #include "gtm_stdlib.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "stp_parms.h" #include "error.h" #include "interlock.h" #include "gtmimagename.h" #include "stringpool.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdskill.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" #include "tp.h" #include "repl_msg.h" #include "gtmsource.h" #include "util.h" #include "cli.h" #include "op.h" #include "gt_timer.h" #include "io.h" #include "dse.h" #include "compiler.h" #include "patcode.h" #include "lke.h" #include "gtm_startup_chk.h" #include "generic_signal_handler.h" #include "cli_parse.h" #include "getzdir.h" #include "dse_exit.h" #include "getjobname.h" #include "sig_init.h" #include "gtmmsg.h" #include "suspsigs_handler.h" #include "common_startup_init.h" #include "gtm_threadgbl_init.h" #include "wbox_test_init.h" #include "gtmio.h" #include "have_crit.h" #include "gt_timers_add_safe_hndlrs.h" #include "continue_handler.h" #include "restrict.h" #include "dm_audit_log.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" #include "gtm_utf8.h" #include "gtm_conv.h" GBLREF u_casemap_t gtm_strToTitle_ptr; /* Function pointer for gtm_strToTitle */ #endif GBLREF gd_region *gv_cur_region; GBLREF gd_addr *gd_header; GBLREF gd_addr *original_header; GBLREF bool licensed; GBLREF void (*func)(void); GBLREF gv_namehead *gv_target; GBLREF int (*op_open_ptr)(mval *v, mval *p, mval *t, mval *mspace); GBLREF boolean_t dse_running; GBLREF spdesc rts_stringpool, stringpool; GBLREF VSIG_ATOMIC_T util_interrupt; GBLREF char cli_err_str[]; GBLREF boolean_t write_after_image; GBLREF CLI_ENTRY dse_cmd_ary[]; GBLREF ch_ret_type (*stpgc_ch)(); /* Function pointer to stp_gcol_ch */ GBLREF void (*primary_exit_handler)(void); GBLDEF block_id patch_curr_blk; GBLDEF CLI_ENTRY *cmd_ary = &dse_cmd_ary[0]; /* Define cmd_ary to be the DSE specific cmd table */ GBLREF IN_PARMS *cli_lex_in_ptr; static bool dse_process(int argc); static void display_prompt(void); static readonly char prompt[]="DSE> "; error_def(ERR_CTRLC); error_def(ERR_RESTRICTEDOP); int main(int argc, char *argv[]) { DCL_THREADGBL_ACCESS; GTM_THREADGBL_INIT; common_startup_init(DSE_IMAGE); licensed = TRUE; TREF(transform) = TRUE; TREF(no_spangbls) = TRUE; /* dse operates on a per-region basis irrespective of global mapping in gld */ TREF(skip_file_corrupt_check) = TRUE; /* do not let csd->file_corrupt flag cause errors in dse */ op_open_ptr = op_open; INIT_FNPTR_GLOBAL_VARIABLES; patch_curr_blk = get_dir_root(); err_init(util_base_ch); UTF8_ONLY(gtm_strToTitle_ptr = >m_strToTitle); sig_init(generic_signal_handler, dse_ctrlc_handler, suspsigs_handler, continue_handler); atexit(util_exit_handler); primary_exit_handler = util_exit_handler; stp_init(STP_INITSIZE); stpgc_ch = &stp_gcol_ch; rts_stringpool = stringpool; getjobname(); io_init(TRUE); getzdir(); gtm_chk_dist(argv[0]); prealloc_gt_timers(); gt_timers_add_safe_hndlrs(); initialize_pattern_table(); if (RESTRICTED(dse)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_RESTRICTEDOP, 1, "DSE"); gvinit(); region_init(FALSE); util_out_print("!/File !_!AD", TRUE, DB_LEN_STR(gv_cur_region)); util_out_print("Region!_!AD!/", TRUE, REG_LEN_STR(gv_cur_region)); cli_lex_setup(argc, argv); /* Since DSE operates on a region-by-region basis (for the most part), do not use a global directory at all from now on */ original_header = gd_header; gd_header = NULL; OPERATOR_LOG_MSG; # ifdef DEBUG if ((gtm_white_box_test_case_enabled && (WBTEST_SEMTOOLONG_STACK_TRACE == gtm_white_box_test_case_number) )) { sgmnt_addrs * csa; node_local_ptr_t cnl; csa = &FILE_INFO(gv_cur_region)->s_addrs; cnl = csa->nl; cnl->wbox_test_seq_num = 1; /*Signal the first step and wait here*/ /* The signal to the shell. MUPIP must not start BEFORE DSE */ util_out_print("DSE is ready. MUPIP can start. Note: This message is a part of WBTEST_SEMTOOLONG_STACK_TRACE test. " "It will not appear in PRO version.", TRUE); while (2 != cnl->wbox_test_seq_num) /*Wait for another process to get hold of the semaphore and signal next step*/ LONG_SLEEP(1); } # endif if (argc < 2) display_prompt(); while (1) { if (!dse_process(argc)) break; display_prompt(); } dse_exit(); REVERT; return 0; } static void display_prompt(void) { PRINTF("DSE> "); FFLUSH(stdout); } static bool dse_process(int argc) { int res, status; ESTABLISH_RET(util_ch, TRUE); func = 0; util_interrupt = 0; res = parse_cmd(); if (EOF != res) { status = log_cmd_if_needed(cli_lex_in_ptr->in_str); if (status) return FALSE; } if (EOF == res) { if (util_interrupt) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CTRLC); REVERT; return TRUE; } else { REVERT; return FALSE; } } else if (res) { if (1 < argc) { /* Here we need to REVERT since otherwise we stay in dse in a loop * The design of dse needs to be changed to act like VMS (which is: * if there is an error in the dse command (dse dumpoa), go to command * prompt, but UNIX exits */ REVERT; rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) res, 2, LEN_AND_STR(cli_err_str)); } else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) res, 2, LEN_AND_STR(cli_err_str)); } if (func) func(); REVERT; return(1 >= argc); } fis-gtm-V7.0-005/sr_unix/dse_cmd.c0000755000032200000250000006525214342376327015626 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdskill.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" #include "tp.h" #include "repl_msg.h" #include "gtmsource.h" #include "util.h" #include "cli.h" #include "cache.h" #include "op.h" #include "gt_timer.h" #include "io.h" #include "dse.h" #include "compiler.h" #include "dse_exit.h" #include "util_spawn.h" #include "util_help.h" #include "dse_cmd_disallow.h" /************************************************************* * NOTE * This file might have lines longer than 132 characters * since the command tables are being initialized. * * Entries need to be made in sorted order (lexicographic) within * each table. ************************************************************/ static readonly CLI_PARM dse_ftime_parm_values[] = { { "FLUSH_TIME", "Flush Time: ", PARM_REQ}, { "", "" } }; static readonly CLI_ENTRY dse_add_qual[] = { { "BLOCK", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "DATA", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "KEY", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "OFFSET", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "POINTER", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "RECORD", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "STAR", dse_adstar, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "" } }; static readonly CLI_ENTRY dse_cache_qual[] = { { "ALL", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "CHANGE", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "CRIT", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "OFFSET", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "RECOVER", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "SHOW", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "SIZE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "VALUE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "VERIFY", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "" } }; static readonly CLI_ENTRY dse_all_qual[] = { { "ALL", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "BUFFER_FLUSH", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "CLEARCORRUPT", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "CRITINIT", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "DUMP", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "FREEZE", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "OVERRIDE", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "REFERENCE", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "RELEASE", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "RENEW", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "SEIZE", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "WCINIT", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "" } }; static readonly CLI_ENTRY true_false_nochange[] = { { "FALSE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "NOCHANGE", 0, 0, 0, 0, 0, DEFA_PRESENT, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "TRUE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "", 0, 0 } }; static readonly CLI_ENTRY never_always_allowexisting[] = { { "ALWAYS", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_STR, 0 }, { "EXISTING", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_STR, 0 }, { "FALSE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_STR, 0 }, { "NEVER", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_STR, 0 }, { "TRUE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_STR, 0 }, { "", 0, 0 } }; static readonly CLI_ENTRY db_vers[] = { { "V4", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "V6", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "" } }; static readonly CLI_ENTRY dse_cfhead_qual[] = { { "ABANDONED_KILLS", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "AVG_BLKS_READ", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "BLKS_TO_UPGRADE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "BLK_SIZE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "BLOCKS_FREE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "B_BYTESTREAM", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "B_COMPREHENSIVE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "B_DATABASE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "B_INCREMENTAL", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "B_RECORD", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "CERT_DB_VER", 0, 0, 0, db_vers, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "COMMITWAIT_SPIN_COUNT", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "CORRUPT_FILE", 0, 0, 0, true_false_nochange, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "CRIT", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "CURRENT_TN", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "DB_WRITE_FMT", 0, 0, 0, db_vers, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "DECLOCATION", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "DECVALUE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "DEF_COLLATION", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "DUALSITE_RESYNC_SEQNO", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "ENCRYPTION_HASH", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "EPOCHTAPER", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "FLUSH_TIME", 0, 0, dse_ftime_parm_values, 0, 0, 0, VAL_NOT_REQ, 0, NON_NEG, VAL_TIME, 0 }, { "FREEZE", 0, 0, 0, true_false_nochange, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "FULLY_UPGRADED", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "GOT2V5ONCE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "GVSTATSRESET", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "HARD_SPIN_COUNT", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "HEXLOCATION", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "HEXVALUE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "INHIBIT_KILLS", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "INTERRUPTED_RECOV", 0, 0, 0, true_false_nochange, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "JNL_SYNCIO", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "JNL_YIELD_LIMIT", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "KEY_MAX_SIZE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "KILL_IN_PROG", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "LOCATION", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "MACHINE_NAME", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "MAX_TN", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "MBM_SIZE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, /* Maintain MUTEX_* qualifiers for backward compatibility; remove these entries after sufficient time has passed for users to have * made the switch to the synonymn qualifiers that don't have the MUTEX_ prefix. */ { "MUTEX_HARD_SPIN_COUNT", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "MUTEX_SLEEP_SPIN_COUNT", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "MUTEX_SPIN_SLEEP_MASK", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, /* End MUTEX_* qualifiers */ { "NULL_SUBSCRIPTS", 0, 0, 0, never_always_allowexisting, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "ONLINE_NBB", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "OVERRIDE", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "PRE_READ_TRIGGER_FACTOR", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "PROBLKSPLIT", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "QDBRUNDOWN", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "RC_SRV_COUNT", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "RECORD_MAX_SIZE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "REFERENCE_COUNT", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "REG_SEQNO", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "RESERVED_BYTES", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "SIZE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "SLEEP_SPIN_COUNT", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "SPIN_SLEEP_MASK", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "STDNULLCOLL", 0, 0, 0, true_false_nochange, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "STRM_NUM", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "STRM_REG_SEQNO", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "TIMERS_PENDING", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "TOTAL_BLKS", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "TRIGGER_FLUSH", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "UPD_RESERVED_AREA", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "UPD_WRITER_TRIGGER_FACTOR", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "VALUE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "WAIT_DISK", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "WARN_MAX_TN", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "WRITES_PER_FLUSH", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "ZQGBLMOD_SEQNO", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "ZQGBLMOD_TN", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "" } }; static readonly CLI_ENTRY dse_change_qual[] = { { "BLOCK", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "BSIZ", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "CMPC", dse_chng_rhead, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "FILEHEADER", dse_chng_fhead, dse_cfhead_qual, 0, 0, cli_disallow_dse_chng_fhead, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "LEVEL", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "OFFSET", dse_chng_rhead, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "RECORD", dse_chng_rhead, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "RSIZ", dse_chng_rhead, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "TN", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "" } }; static readonly CLI_ENTRY dse_crit_qual[] = { { "ALL", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "CRASH", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "CYCLE", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "INIT", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "OWNER", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "RELEASE", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "REMOVE", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "RESET", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "SEIZE", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "" } }; static readonly CLI_ENTRY dse_rdmp_qual[] = { { "BLOCK", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "COUNT", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "GLO", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "OFFSET", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "RECORD", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "ZWR", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "" } }; static readonly CLI_ENTRY dse_fdmp_qual[] = { { "ALL", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "BACKUP", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "BASIC", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "BG_TRC", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "DB_CSH", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "ENVIRONMENT", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "GVSTATS", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "JOURNAL", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "MIXEDMODE", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "RETRIES", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "SNAPSHOT", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "SUPPLEMENTARY", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "TPBLKMOD", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "TPRETRIES", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "UPDPROC", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "" } }; static readonly CLI_ENTRY dse_dump_qual[] = { { "BLOCK", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "COUNT", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "CRIT", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "FILEHEADER", dse_dmp_fhead, dse_fdmp_qual, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "GLO", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "HEADER", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, #ifdef DEBUG { "IMAGE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, #endif { "OFFSET", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "RECORD", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "ZWR", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "" } }; static readonly CLI_ENTRY dse_eval_qual[] = { { "DECIMAL", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "HEXADECIMAL", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "NUMBER", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "" } }; static readonly CLI_PARM dse_freg_parm_values[] = { { "REGION", "*", PARM_REQ}, { "", "" } }; static readonly CLI_ENTRY dse_find_qual[] = { { "BLOCK", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "CRIT", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "EXHAUSTIVE", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "FREEBLOCK", dse_f_free, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "GBLDIR", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "HINT", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "KEY", dse_f_key, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "REGION", dse_f_reg, 0, dse_freg_parm_values, 0, 0, 0, VAL_NOT_REQ, 0, NON_NEG, VAL_STR, 0 }, { "SIBLINGS", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "STATS", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "" } }; static readonly CLI_ENTRY dse_integrit_qual[] = { { "BLOCK", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "CRIT", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "" } }; static readonly CLI_ENTRY dse_map_qual[] = { { "BLOCK", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "BUSY", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "FREE", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "MASTER", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "RESTORE_ALL", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NON_NEG, 0, 0 }, { "" } }; static readonly CLI_ENTRY dse_open_qual[] = { { "FILE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "OCHSET", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_STR, 0 }, { "" } }; static readonly CLI_ENTRY dse_over_qual[] = { { "BLOCK", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "DATA", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "OFFSET", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "OCHSET", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_NUM, 0 }, { "" } }; static readonly CLI_ENTRY dse_range_qual[] = { { "BUSY", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "CRIT", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "FROM", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "INDEX", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "LOST", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "LOWER", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "STAR", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "TO", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "UPPER", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "" } }; static readonly CLI_ENTRY dse_remove_qual[] = { { "BLOCK", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "COUNT", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "OFFSET", dse_rmrec, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "RECORD", dse_rmrec, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "VERSION", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "" } }; static readonly CLI_ENTRY dse_restore_qual[] = { { "BLOCK", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "FROM", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "REGION", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "VERSION", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_DCM }, { "" } }; static readonly CLI_ENTRY dse_save_qual[] = { { "BLOCK", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "COMMENT", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "CRIT", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, NEG, 0, 0 }, { "LIST", 0, 0, 0, 0, 0, 0, VAL_N_A, 0, 0, 0, 0 }, { "" } }; static readonly CLI_ENTRY dse_shift_qual[] = { { "BACKWARD", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "BLOCK", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "FORWARD", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "OFFSET", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, VAL_HEX }, { "" } }; GBLDEF CLI_ENTRY dse_cmd_ary[] = { { "ADD", dse_adrec, dse_add_qual, 0, 0, cli_disallow_dse_add, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "ALL", dse_all, dse_all_qual, 0, 0, cli_disallow_dse_all, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "BUFFER_FLUSH", dse_flush, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "CACHE", dse_cache, dse_cache_qual, 0, 0, cli_disallow_dse_cache, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "CHANGE", dse_chng_bhead, dse_change_qual, 0, 0, cli_disallow_dse_change, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "CLOSE", dse_close, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "CRITICAL", dse_crit, dse_crit_qual, 0, 0, cli_disallow_dse_crit, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "DUMP", dse_dmp, dse_dump_qual, 0, 0, cli_disallow_dse_dump, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "EVALUATE", dse_eval, dse_eval_qual, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "EXIT", dse_exit, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "FIND", dse_f_blk, dse_find_qual, 0, 0, cli_disallow_dse_find, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "HELP", util_help, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "INTEGRIT", dse_integ, dse_integrit_qual, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "MAPS", dse_maps, dse_map_qual, 0, 0, cli_disallow_dse_maps, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "OPEN", dse_open, dse_open_qual, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "OVERWRITE", dse_over, dse_over_qual, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "PAGE", dse_page, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "QUIT", dse_exit, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "RANGE", dse_range, dse_range_qual, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "REMOVE", dse_rmsb, dse_remove_qual, 0, 0, cli_disallow_dse_remove, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "RESTORE", dse_rest, dse_restore_qual, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "SAVE", dse_save, dse_save_qual, 0, 0, cli_disallow_dse_save, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "SHIFT", dse_shift, dse_shift_qual, 0, 0, cli_disallow_dse_shift, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "SPAWN", util_spawn, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "VERSION", dse_version, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "WCINIT", dse_wcreinit, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "" } }; /* for SPAWN actually value is disallowed, but parameter is allowed. */ fis-gtm-V7.0-005/sr_unix/dse_cmd_disallow.c0000644000032200000250000002437514342376327017522 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2017 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_stdio.h" #include "gtm_string.h" #include "cli.h" #include "cli_parse.h" #include "cli_disallow.h" #include "dse_cmd_disallow.h" GBLREF char *cli_err_str_ptr; boolean_t cli_disallow_dse_add(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; disallow_return_value = d_c_cli_present("STAR") && (d_c_cli_present("DATA") || d_c_cli_present("KEY") || d_c_cli_present("RECORD") || d_c_cli_present("OFFSET")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("DATA") && d_c_cli_present("POINTER"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("RECORD") && d_c_cli_present("OFFSET"); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_dse_all(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; disallow_return_value = d_c_cli_present("WCINIT") && d_c_cli_present("BUFFER_FLUSH"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("RENEW") && (d_c_cli_present("FREEZE") || d_c_cli_present("SEIZE") || d_c_cli_present("RELEASE") || d_c_cli_present("CRITINIT") || d_c_cli_present("BUFFER_FLUSH") || d_c_cli_present("REFERENCE") || d_c_cli_present("WCINIT") || d_c_cli_present("OVERRIDE")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("SEIZE") && d_c_cli_present("RELEASE"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("CRITINIT") && (d_c_cli_present("SEIZE") || d_c_cli_present("RELEASE")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("DUMP") && (d_c_cli_present("BUFFER_FLUSH") || d_c_cli_present("CRITINIT") || d_c_cli_present("FREEZE") || d_c_cli_present("OVERRIDE") || d_c_cli_present("REFERENCE") || d_c_cli_present("RELEASE") || d_c_cli_present("RENEW") || d_c_cli_present("SEIZE") || d_c_cli_present("WCINIT")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("ALL") && !d_c_cli_present("DUMP"); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_dse_cache(void) { int disallow_return_value = 0; boolean_t p1, p2, p3, p4; *cli_err_str_ptr = 0; p1 = d_c_cli_present("CHANGE"); p2 = d_c_cli_present("RECOVER"); p3 = d_c_cli_present("SHOW"); p4 = d_c_cli_present("VERIFY"); /* any DSE CACHE command should contain at LEAST one of the above qualifiers */ disallow_return_value = !(p1 || p2 || p3 || p4); CLI_DIS_CHECK; /* Note CLI_DIS_CHECK_N_RESET is not used as we want to reuse the computed error string (cli_err_str_ptr) * for the next check as well in case it fails. Note that this can be done only if both checks use * exactly the same set of qualifiers (which is TRUE in this case). */ /* any DSE CACHE command should contain at MOST one of the above qualifiers */ disallow_return_value = cli_check_any2(VARLSTCNT(4) p1, p2, p3, p4); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("ALL") && d_c_cli_present("CHANGE"); CLI_DIS_CHECK_N_RESET; disallow_return_value = !(d_c_cli_present("CHANGE") || d_c_cli_present("SHOW")) && (d_c_cli_present("OFFSET") || d_c_cli_present("SIZE") || d_c_cli_present("VALUE")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("SHOW") && d_c_cli_present("VALUE"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("CHANGE") && !d_c_cli_present("OFFSET"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("OFFSET") && !d_c_cli_present("SIZE"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("SIZE") && !d_c_cli_present("OFFSET"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("VALUE") && !d_c_cli_present("OFFSET"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_negated("CRIT") && (d_c_cli_present("CHANGE") || d_c_cli_present("RECOVER") || d_c_cli_present("VERIFY")); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_dse_change(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; disallow_return_value = d_c_cli_present("FILEHEADER") && (d_c_cli_present("BLOCK") || d_c_cli_present("LEVEL") || d_c_cli_present("BSIZ") || d_c_cli_present("RECORD") || d_c_cli_present("OFFSET") || d_c_cli_present("CMPC") || d_c_cli_present("RSIZ") || d_c_cli_present("TN")); CLI_DIS_CHECK_N_RESET; disallow_return_value = ( d_c_cli_present("LEVEL") || d_c_cli_present("BSIZ") || d_c_cli_present("TN")) && ( d_c_cli_present("RECORD") || d_c_cli_present("OFFSET") || d_c_cli_present("CMPC") || d_c_cli_present("RSIZ")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("RECORD") && d_c_cli_present("OFFSET"); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_dse_chng_fhead(void) { int disallow_return_value = 0; disallow_return_value = d_c_cli_present("STRM_NUM") && !d_c_cli_present("STRM_REG_SEQNO"); CLI_DIS_CHECK_N_RESET; disallow_return_value = !d_c_cli_present("STRM_NUM") && d_c_cli_present("STRM_REG_SEQNO"); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_dse_crit(void) { int disallow_return_value = 0; boolean_t p1, p2, p3, p4, p5; *cli_err_str_ptr = 0; p1 = d_c_cli_present("INIT"); p2 = d_c_cli_present("OWNER"); p3 = d_c_cli_present("SEIZE"); p4 = d_c_cli_present("RELEASE"); p5 = d_c_cli_present("REMOVE"); disallow_return_value = cli_check_any2(VARLSTCNT(5) p1, p2, p3, p4, p5); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("CRASH") && (d_c_cli_present("SEIZE") || d_c_cli_present("RELEASE") || d_c_cli_present("OWNER") || d_c_cli_present("RESET")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("RESET") && (d_c_cli_present("SEIZE") || d_c_cli_present("RELEASE") || d_c_cli_present("OWNER")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("CYCLE") && (d_c_cli_present("INIT") || d_c_cli_present("REMOVE") || d_c_cli_present("SEIZE") || d_c_cli_present("RELEASE") || d_c_cli_present("RESET")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("ALL") && (d_c_cli_present("CRASH") || d_c_cli_present("CYCLE") || d_c_cli_present("INIT") || d_c_cli_present("OWNER") || d_c_cli_present("RELEASE") || d_c_cli_present("REMOVE") || d_c_cli_present("RESET") || d_c_cli_present("SEIZE")); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_dse_dump(void) { int disallow_return_value = 0; boolean_t p1, p2, p3; *cli_err_str_ptr = 0; p1 = d_c_cli_present("RECORD"); p2 = d_c_cli_present("OFFSET"); p3 = d_c_cli_present("FILEHEADER"); disallow_return_value = cli_check_any2(VARLSTCNT(3) p1, p2, p3); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("FILEHEADER") && (d_c_cli_present("BLOCK") || d_c_cli_present("HEADER") || d_c_cli_present("COUNT") || d_c_cli_present("GLO") || d_c_cli_present("ZWR")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("GLO") && d_c_cli_present("ZWR"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("GLO") && d_c_cli_present("HEADER"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("ZWR") && d_c_cli_present("HEADER"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("COUNT") && !d_c_cli_present("HEADER") && !(d_c_cli_present("RECORD") || d_c_cli_present("OFFSET")) && !d_c_cli_present("BLOCK"); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_dse_find(void) { int disallow_return_value = 0; boolean_t p1, p2, p3, p4; *cli_err_str_ptr = 0; p1 = d_c_cli_present("BLOCK"); p2 = d_c_cli_present("FREEBLOCK"); p3 = d_c_cli_present("KEY"); p4 = d_c_cli_present("REGION"); disallow_return_value = cli_check_any2(VARLSTCNT(4) p1, p2, p3, p4); CLI_DIS_CHECK_N_RESET; disallow_return_value = ( d_c_cli_present("EXHAUSTIVE") || d_c_cli_present("SIBLINGS")) && ( d_c_cli_present("FREEBLOCK") || d_c_cli_present("KEY") || d_c_cli_present("REGION")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("HINT") && !d_c_cli_present("FREEBLOCK"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("FREEBLOCK") && !d_c_cli_present("HINT"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("STATS") && !d_c_cli_present("REGION"); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_dse_maps(void) { int disallow_return_value = 0; boolean_t p1, p2, p3, p4; *cli_err_str_ptr = 0; p1 = d_c_cli_present("FREE"); p2 = d_c_cli_present("BUSY"); p3 = d_c_cli_present("MASTER"); p4 = d_c_cli_present("RESTORE_ALL"); disallow_return_value = cli_check_any2(VARLSTCNT(4) p1, p2, p3, p4); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("BLOCK") && d_c_cli_present("RESTORE_ALL"); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_dse_remove(void) { int disallow_return_value = 0; boolean_t p1, p2, p3; *cli_err_str_ptr = 0; p1 = d_c_cli_present("RECORD"); p2 = d_c_cli_present("OFFSET"); p3 = d_c_cli_present("VERSION"); disallow_return_value = cli_check_any2(VARLSTCNT(3) p1, p2, p3); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("VERSION") && d_c_cli_present("COUNT"); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_dse_save(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; disallow_return_value = d_c_cli_present("LIST") && d_c_cli_present("COMMENT"); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_dse_shift(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; disallow_return_value = d_c_cli_present("FORWARD") && d_c_cli_present("BACKWARD"); CLI_DIS_CHECK_N_RESET; return FALSE; } fis-gtm-V7.0-005/sr_unix/dse_cmd_disallow.h0000755000032200000250000000206414342376327017521 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2018 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. * * * ****************************************************************/ #ifndef DSE_CMD_DISALLOW_H_INCLUDED #define DSE_CMD_DISALLOW_H_INCLUDED boolean_t cli_disallow_dse_add(void); boolean_t cli_disallow_dse_all(void); boolean_t cli_disallow_dse_cache(void); boolean_t cli_disallow_dse_change(void); boolean_t cli_disallow_dse_chng_fhead(void); boolean_t cli_disallow_dse_crit(void); boolean_t cli_disallow_dse_dump(void); boolean_t cli_disallow_dse_find(void); boolean_t cli_disallow_dse_maps(void); boolean_t cli_disallow_dse_remove(void); boolean_t cli_disallow_dse_save(void); boolean_t cli_disallow_dse_shift(void); #endif fis-gtm-V7.0-005/sr_unix/dse_ctrlc_handler.c0000755000032200000250000000130414342376327017653 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "dse.h" #include GBLREF VSIG_ATOMIC_T util_interrupt; void dse_ctrlc_handler(int sig) { util_interrupt = 1; } fis-gtm-V7.0-005/sr_unix/dse_help.c0000755000032200000250000000272114342376327016003 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "release_name.h" #include "util.h" #include "dse.h" void dse_help(void) { /* This function is a STUB to avoid editting sr_port/dse.h */ } void dse_version(void) { /* * The following assumptions have been made in this function * 1. GTM_RELEASE_NAME is of the form "GTM_PRODUCT VERSION THEREST" * (Refer file release_name.h) * 2. A single blank separates GTM_PRODUCT and VERSION * 3. If THEREST exists, it is separated from VERSION by atleast * one blank */ char gtm_rel_name[] = GTM_RELEASE_NAME; char dse_rel_name[SIZEOF(GTM_RELEASE_NAME) - SIZEOF(GTM_PRODUCT)]; int dse_rel_name_len; char *cptr; for (cptr = gtm_rel_name + SIZEOF(GTM_PRODUCT), dse_rel_name_len = 0; *cptr != ' ' && *cptr != '\0'; dse_rel_name[dse_rel_name_len++] = *cptr++); dse_rel_name[dse_rel_name_len] = '\0'; util_out_print("!AD", TRUE, dse_rel_name_len, dse_rel_name); return; } fis-gtm-V7.0-005/sr_unix/dse_open.c0000755000032200000250000001254614342376327016022 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_string.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include #include "gtm_stat.h" #include "gtm_iconv.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "cli.h" #include "dse.h" #include "gtmio.h" #include "io.h" #include "iosp.h" #include "io_params.h" #include "stringpool.h" #include "util.h" #include "op.h" #include "gtm_utf8.h" #include "gtm_conv.h" #ifdef __osf__ #pragma pointer_size (save) #pragma pointer_size (long) #endif GBLREF int (*op_open_ptr)(mval *v, mval *p, mval *t, mval *mspace); GBLREF spdesc stringpool; GBLREF gtm_chset_t dse_over_chset; LITREF mval literal_zero; #ifdef __osf__ #pragma pointer_size (restore) #endif #define PATCH_FILE_MAX 255 static int patch_fd; static char patch_ofile[PATCH_FILE_MAX + 1]; static short patch_len; static char ch_set_name[MAX_CHSET_NAME + 1]; GBLREF enum dse_fmt dse_dmp_format; void dse_open (void) { unsigned short cli_len; int4 save_errno; mval val; mval open_pars, use_pars; mstr chset_mstr; int cnt; static readonly unsigned char open_params_list[] = { (unsigned char)iop_newversion, (unsigned char)iop_m, (unsigned char)iop_stream, (unsigned char)iop_nowrap, (unsigned char)iop_eol }; /*set iop_width=1MB(1*1024*1024)*/ static readonly unsigned char use_params_list[] = { (unsigned char)iop_width, # ifdef BIGENDIAN (unsigned char)0x0, (unsigned char)0x10, (unsigned char)0x0, (unsigned char)0x0, # else (unsigned char)0x0, (unsigned char)0x0, (unsigned char)0x10, (unsigned char)0x0, # endif (unsigned char)iop_eol }; if (cli_present("FILE") == CLI_PRESENT) { if (CLOSED_FMT != dse_dmp_format) { util_out_print("Error: output file already open.",TRUE); util_out_print("Current output file: !AD", TRUE, strlen(patch_ofile), &patch_ofile[0]); return; } cli_len = PATCH_FILE_MAX; if (!cli_get_str("FILE", patch_ofile, &cli_len)) return; if (0 == cli_len) { util_out_print("Error: must specify a file name.",TRUE); return; } patch_ofile[cli_len] = 0; patch_len = cli_len; val.mvtype = MV_STR; val.str.len = patch_len; val.str.addr = (char *)patch_ofile; open_pars.mvtype = MV_STR; open_pars.str.len = SIZEOF(open_params_list); open_pars.str.addr = (char *)open_params_list; (*op_open_ptr)(&val, &open_pars, (mval *)&literal_zero, NULL); use_pars.mvtype = MV_STR; use_pars.str.len = SIZEOF(use_params_list); use_pars.str.addr = (char *)use_params_list; op_use(&val, &use_pars); if (CLI_PRESENT == cli_present("OCHSET")) { cli_len = MAX_CHSET_NAME; if (cli_get_str("OCHSET", ch_set_name, &cli_len)) { if (0 == cli_len) { util_out_print("Error: must specify a charactor set name.",TRUE); return; } #ifdef KEEP_zOS_EBCDIC ch_set_name[cli_len] = '\0'; ch_set_len = cli_len; if ( (iconv_t)0 != dse_over_cvtcd ) { ICONV_CLOSE_CD(dse_over_cvtcd); } ICONV_OPEN_CD(dse_over_cvtcd, INSIDE_CH_SET, ch_set_name); #else chset_mstr.addr = ch_set_name; chset_mstr.len = cli_len; SET_ENCODING(dse_over_chset, &chset_mstr); #endif } } else #ifdef KEEP_zOS_EBCDIC if ( (iconv_t) 0 == dse_over_cvtcd ) ICONV_OPEN_CD(dse_over_cvtcd, INSIDE_CH_SET, OUTSIDE_CH_SET); #else dse_over_chset = CHSET_M; #endif dse_dmp_format = OPEN_FMT; } else { if (CLOSED_FMT != dse_dmp_format) util_out_print("Current output file: !AD", TRUE, strlen(patch_ofile), &patch_ofile[0]); else util_out_print("No current output file.",TRUE); } return; } boolean_t dse_fdmp_output (void *addr, int4 len) { mval val; static char *buffer = NULL; static int bufsiz = 0; assert(len >= 0); if (len + 1 > bufsiz) { if (buffer) free(buffer); bufsiz = len + 1; buffer = (char *)malloc(bufsiz); } if (len) { assert(buffer); /* 4SCA: Even though len acts as a guard */ memcpy(buffer, addr, len); buffer[len] = 0; val.mvtype = MV_STR; val.str.addr = (char *)buffer; val.str.len = len; op_write(&val); } op_wteol(1); return TRUE; } void dse_close(void) { mval val; mval pars; unsigned char no_param = (unsigned char)iop_eol; if (CLOSED_FMT != dse_dmp_format) { util_out_print("Closing output file: !AD",TRUE,LEN_AND_STR(patch_ofile)); val.mvtype = pars.mvtype = MV_STR; val.str.addr = (char *)patch_ofile; val.str.len = patch_len; pars.str.len = SIZEOF(iop_eol); pars.str.addr = (char *)&no_param; op_close(&val, &pars); dse_dmp_format = CLOSED_FMT; } else util_out_print("Error: no current output file.",TRUE); return; } fis-gtm-V7.0-005/sr_unix/dse_puttime.c0000755000032200000250000000204614342376327016542 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_stdio.h" #include "util.h" #include "dse_puttime.h" #define TIME_SIZE (2 + 1 + 2 + 1 + 2 + 1 + 2) /* hh:mm:ss:dd */ /* display uint4 *, representing a time value in millisec */ void dse_puttime(int_ptr_t time, char *c, bool flush) { char outbuf[TIME_SIZE * 4]; /* Leave room for unexpected values */ SNPRINTF(outbuf, TIME_SIZE * 4, "%2.2d:%2.2d:%2.2d:%2.2d", *time / 3600000, (*time % 3600000) / 60000, (*time % 60000) / 1000, (*time % 1000) / 10); util_out_print(c,flush,TIME_SIZE,outbuf); return; } fis-gtm-V7.0-005/sr_unix/dse_remove.c0000755000032200000250000000150314342376327016345 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "cli.h" #include "dse.h" void dse_remove(void) { if (cli_present("RECORD") == CLI_PRESENT) dse_rmrec(); else if (cli_present("BLOCK") == CLI_PRESENT) dse_rmsb(); return; } fis-gtm-V7.0-005/sr_unix/dsk_read.c0000644000032200000250000003230014342376327015765 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_unistd.h" #include "gtm_string.h" #include "gtm_signal.h" #include #ifdef DEBUG #include "gtm_stdio.h" #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "error.h" #include "gtmio.h" #include "gds_blk_upgrade.h" #include "gdsbml.h" #include "gtmcrypt.h" #include "t_retry.h" #include "gdsdbver.h" #include "min_max.h" #include "gtmimagename.h" #include "memcoherency.h" #include "gdskill.h" #include "gdscc.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "have_crit.h" #include "tp.h" #include "cdb_sc.h" #include "mupip_reorg_encrypt.h" #include "mu_reorg.h" GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF volatile int4 fast_lock_count; GBLREF boolean_t dse_running, is_updhelper; GBLREF boolean_t mu_reorg_upgrd_dwngrd_in_prog; GBLREF unsigned int t_tries; GBLREF uint4 dollar_tlevel; GBLREF sgm_info *sgm_info_ptr; GBLREF sgmnt_addrs *kip_csa; GBLREF jnl_gbls_t jgbl; GBLREF uint4 process_id; GBLREF uint4 mu_reorg_encrypt_in_prog; error_def(ERR_DSEBLKRDFAIL); /* TODO: use more helpful error */ int4 dsk_read (block_id blk, sm_uc_ptr_t buff, enum db_ver *ondsk_blkver, boolean_t blk_free) { boolean_t buff_is_modified_after_lseekread = FALSE, db_is_encrypted, fully_upgraded, use_new_key; block_id blk_num, *blk_ptr, offset; char *in, *out; enum db_ver tmp_ondskblkver; int bsiz, in_len, level, gtmcrypt_errno; int4 save_errno, size; intrpt_state_t prev_intrpt_state; node_local_ptr_t cnl; sm_uc_ptr_t enc_save_buff, recBase; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; /* It is possible that an index block we read in from disk has a block_id that needs adjustment subsequent to enlargement * of the master bit map. The database block scanning routines (gvcst_*search*.c) can deal with V6 or V7 index blocks, * but only dsk_read does the offset adjustment Therefore we do not want to risk reading a potential pre-move index block * directly into the cache and then adjusting it. Instead, we read it into a private buffer, upgrade it there and then * copy it over to the cache. This uses the static variable read_reformat_buffer. We could have as well used the global * variable "reformat_buffer" for this purpose. But that would then prevent dsk_reads and concurrent dsk_writes from * proceeding. We don't want that loss of asynchronocity, hence we keep them separate. Note that while a lot of routines * use "reformat_buffer" only this routine uses "read_reformat_buffer" which is a static rather than a GBLDEF. */ static sm_uc_ptr_t read_reformat_buffer; unix_db_info *udi; unsigned short temp_ushort; # ifdef DEBUG unsigned int effective_t_tries; boolean_t killinprog; static int in_dsk_read; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; save_errno = 0; csa = cs_addrs; csd = csa->hdr; cnl = csa->nl; assert(csd == cs_data); assert(NULL != cnl); assert(0 == in_dsk_read); /* dsk_read should never be nested. the read_reformat_buffer logic below relies on this */ DEBUG_ONLY(in_dsk_read++;) assert(GDSVCURR == GDSV7); /* assert should fail if GDSVCURR changes */ /* Note: Even in snapshots, only INTEG requires dsk_read to read FREE blocks. The assert below should be modified * if we later introduce a scheme where we can figure out as to who started the snapshots and assert accordingly */ assert(!blk_free || SNAPSHOTS_IN_PROG(csa)); /* Only SNAPSHOTS require dsk_read to read a FREE block from the disk */ udi = FILE_INFO(gv_cur_region); assert(csd == cs_data); size = csd->blk_size; tmp_ondskblkver = (enum db_ver)csd->desired_db_format; /* Cache csd->fully_upgraded once so that all uses work the same way. Repeatedly referencing csd->fully_upgraded could * result in different values seen through-out the function resulting in incorrect operation. For example, the code does * not allocate scratch space for the temporary pre-V7 formatted block which is needed later in the function. It is ok if * the value of csd->fully_upgraded changes after we took a copy of it since we have a buffer locked for this particular * block (in BG) so no concurrent process could be changing the format of this block. For MM, there is no such protection. * We have 2 possibilities: * - fully_upgraded is cached as FALSE but becomes TRUE before reading the block from disk. This only results in a little * extra work since the block on disk will already have been upgraded * - fully_upgraded is cached as TRUE but becomes FALSE before reading the block from disk/mmap. There is no problem since * the process performs NO upgrade. Some later process will take the responsiblity for such an upgrade */ fully_upgraded = csd->fully_upgraded; assert(0 == (long)buff % SIZEOF(block_id)); assert(NULL != cnl); INCR_GVSTATS_COUNTER(csa, cnl, n_dsk_read, 1); enc_save_buff = buff; /* The value of MUPIP_REORG_IN_PROG_LOCAL_DSK_READ indicates that this is a direct call from mupip_reorg_encrypt, operating * on a local buffer. */ if (USES_ENCRYPTION(csd->is_encrypted) && (MUPIP_REORG_IN_PROG_LOCAL_DSK_READ != mu_reorg_encrypt_in_prog)) { DBG_ENSURE_PTR_IS_VALID_GLOBUFF(csa, csd, buff); enc_save_buff = GDS_ANY_ENCRYPTGLOBUF(buff, csa); DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(csa, csd, enc_save_buff); } DB_LSEEKREAD(udi, udi->fd, (BLK_ZERO_OFF(csd->start_vbn) + ((off_t)blk * size)), enc_save_buff, size, save_errno); assert((0 == save_errno) || (-1 == save_errno)); WBTEST_ASSIGN_ONLY(WBTEST_PREAD_SYSCALL_FAIL, save_errno, EIO); if ((enc_save_buff != buff) && (0 == save_errno)) { assert(USES_ENCRYPTION(csd->is_encrypted) && (MUPIP_REORG_IN_PROG_LOCAL_DSK_READ != mu_reorg_encrypt_in_prog)); DEFER_INTERRUPTS(INTRPT_IN_CRYPT_RECONFIG, prev_intrpt_state); db_is_encrypted = IS_ENCRYPTED(csd->is_encrypted); assert(NULL != csa->encr_ptr); assert(csa->encr_ptr->reorg_encrypt_cycle == cnl->reorg_encrypt_cycle); /* caller should have ensured this */ use_new_key = NEEDS_NEW_KEY(csd, ((blk_hdr_ptr_t)enc_save_buff)->tn); if (use_new_key || db_is_encrypted) { bsiz = (int)((blk_hdr_ptr_t)enc_save_buff)->bsiz; in_len = MIN(csd->blk_size, bsiz) - SIZEOF(blk_hdr); buff_is_modified_after_lseekread = TRUE; if (IS_BLK_ENCRYPTED(((blk_hdr_ptr_t)enc_save_buff)->levl, in_len)) { /* Due to concurrency conflicts, we are potentially reading a free block even though * blk_free is FALSE. Go ahead and safely "decrypt" such a block, even though it contains no * valid contents. We expect GTMCRYPT_DECRYPT to return success even if it is presented with * garbage data. */ ASSERT_ENCRYPTION_INITIALIZED; memcpy(buff, enc_save_buff, SIZEOF(blk_hdr)); in = (char *)(enc_save_buff + SIZEOF(blk_hdr)); out = (char *)(buff + SIZEOF(blk_hdr)); if (use_new_key) { GTMCRYPT_DECRYPT(csa, TRUE, csa->encr_key_handle2, in, in_len, out, enc_save_buff, SIZEOF(blk_hdr), gtmcrypt_errno); assert(0 == gtmcrypt_errno); } else { GTMCRYPT_DECRYPT(csa, csd->non_null_iv, csa->encr_key_handle, in, in_len, out, enc_save_buff, SIZEOF(blk_hdr), gtmcrypt_errno); assert(0 == gtmcrypt_errno); } save_errno = gtmcrypt_errno; DBG_RECORD_BLOCK_READ(csd, csa, cnl, process_id, blk, ((blk_hdr_ptr_t)enc_save_buff)->tn, 1, use_new_key, enc_save_buff, buff, size, in_len); } else { memcpy(buff, enc_save_buff, size); DBG_RECORD_BLOCK_READ(csd, csa, cnl, process_id, blk, ((blk_hdr_ptr_t)enc_save_buff)->tn, 2, use_new_key, enc_save_buff, buff, size, in_len); } } else { memcpy(buff, enc_save_buff, size); DBG_RECORD_BLOCK_READ(csd, csa, cnl, process_id, blk, ((blk_hdr_ptr_t)enc_save_buff)->tn, 3, use_new_key, enc_save_buff, buff, size, 0); } ENABLE_INTERRUPTS(INTRPT_IN_CRYPT_RECONFIG, prev_intrpt_state); } if (0 == save_errno) /* this bloc is a kissing cousin to code in mm_read and the 2should be maintained in parallel */ { /* see if block needs to be converted to current version */ if ((GDSV6p == (tmp_ondskblkver = ((blk_hdr_ptr_t)buff)->bver)) && (GDSMV70000 == csd->creation_mdb_ver)) { /* adjust for shift of GDSV7 id from 2 to 4 */ buff_is_modified_after_lseekread = TRUE; tmp_ondskblkver = ((blk_hdr_ptr_t)buff)->bver = GDSV7; } if (blk_free || (GDSV4 == tmp_ondskblkver)) { /* but might be uninitialed */ buff_is_modified_after_lseekread = TRUE; # ifdef DEBUG if (!blk_free && !is_updhelper && !dse_running && !mu_reorg_encrypt_in_prog && !mu_reorg_upgrd_dwngrd_in_prog) TREF(donot_commit) = DONOTCOMMIT_DSK_READ_EMPTY_BUT_NOT_FREE; /* expected data, but got empty */ # endif /* might not be correct, but any writer would correct it before it goes to a DB file */ tmp_ondskblkver = ((blk_hdr_ptr_t)buff)->bver = csd->desired_db_format; } else assert((GDSV7 == tmp_ondskblkver) || (GDSV6 == tmp_ondskblkver) /* vanilla cases */ || ((GDSV7m == tmp_ondskblkver) && IS_64_BLK_ID(buff)) /* block upgrade complete from V6 */ || (!fully_upgraded && (GDSV6p == tmp_ondskblkver))); /* shuffled & adjusted but still 4 byte ID */ if (!fully_upgraded && !blk_free && (GDSV7m != tmp_ondskblkver)) /* !fully_upgraded only during V6 -> V7 upgrade */ { /* block in need of attention */ buff_is_modified_after_lseekread = TRUE; /* data blocks & local bit maps just get a version update */ if ((0 == (level = (int)((blk_hdr_ptr_t)buff)->levl)) || (LCL_MAP_LEVL == level)) /* WARNING assignment */ tmp_ondskblkver = level ? GDSV7m : GDSV6; else if ((csd->offset) && (GDSV6 == tmp_ondskblkver)) { /* This is a pre-V7 index block needing its offset adjusted */ assert(MEMCMP_LIT(csd->label, GDS_LABEL)); blk_ptr_adjust(buff, csd->offset); tmp_ondskblkver = ((blk_hdr_ptr_t)buff)->bver = GDSV6p; /* 4 byte block_id with offset applied */ } else assert(GDSV6p == tmp_ondskblkver); } /* V7 block with V7 DB intent or V6 with V6 DB intent or V6 in transition to V7 */ assert(!fully_upgraded ? (GDSV7 != tmp_ondskblkver) /* note: GDSV6p cannot exist when fully_upgraded is TRUE */ : (((GDSV7 == tmp_ondskblkver) || (GDSV7m == tmp_ondskblkver) && MEMCMP_LIT(csd->label, GDS_LABEL)) || ((GDSV6 == tmp_ondskblkver) && (!MEMCMP_LIT(csd->label, V6_GDS_LABEL))))); assert((GDSV4 != tmp_ondskblkver) && (NULL != ondsk_blkver)); /* REORG encrypt does not pass ondsk_blkver */ *ondsk_blkver = tmp_ondskblkver; } if (buff_is_modified_after_lseekread) { /* Normally the disk read (done in LSEEKREAD macro) would do the necessary write memory barrier to make the * updated shared memory global buffer contents visible to all other processes as long as they see any later * updates done to shared memory by the reader. But in case of a V4 -> V5 upgrade or reading of an encrypted * block, the actual disk read would have happened into a different buffer. That would then be used as a * source for the upgrade or decryption before placing the final contents in the input global buffer. * We now need a write memory barrier before returning from this function to publish this shared memory * update to other processes waiting on this read. Note: it is possible in rare cases (e.g. mupip reorg upgrade) * that the input buffer is NOT a shared memory buffer in which case the write memory barrier is not necessary * but it is not easily possible to identify that and we want to save if checks on the fast path and so do * the memory barrier in all cases. */ SHM_WRITE_MEMORY_BARRIER; } # ifdef DEBUG in_dsk_read--; assert(0 == in_dsk_read); /* Expect t_tries to be 3 if we have crit. Exceptions: gvcst_redo_root_search (where t_tries is temporarily reset * for the duration of the redo_root_search and so we should look at the real t_tries in redo_rootsrch_ctxt), * gvcst_expand_free_subtree, REORG UPGRADE/DOWNGRADE, DSE (where we grab crit before doing the t_qread irrespective * of t_tries), forward recovery (where we grab crit before doing everything), MUPIP TRIGGER -UPGRADE (where we * grab crit before doing the entire ^#t upgrade TP transaction) OR bm_getfree (where we did a preemptive crit grab * before doing a file extension). */ effective_t_tries = UNIX_ONLY( (TREF(in_gvcst_redo_root_search)) ? (TREF(redo_rootsrch_ctxt)).t_tries : ) t_tries; effective_t_tries = MAX(effective_t_tries, t_tries); killinprog = (NULL != ((dollar_tlevel) ? sgm_info_ptr->kip_csa : kip_csa)); assert(dse_running || killinprog || jgbl.forw_phase_recovery || mu_reorg_upgrd_dwngrd_in_prog || mu_reorg_encrypt_in_prog GTMTRIG_ONLY(|| TREF(in_trigger_upgrade)) || TREF(in_bm_getfree_gdsfilext) || (csa->now_crit != (CDB_STAGNATE > effective_t_tries))); # endif return save_errno; } fis-gtm-V7.0-005/sr_unix/dsk_write_nocache.c0000755000032200000250000001623514342376327017700 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2021 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_fcntl.h" /* needed for silly aix's expansion of open to open64 */ #include "gtm_unistd.h" #include "gtm_stdio.h" #include "gtm_signal.h" #include #include #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdsblk.h" #include "iosp.h" #include "gtmio.h" #include "gds_blk_downgrade.h" #include "add_inter.h" #include "anticipatory_freeze.h" #include "gtmcrypt.h" #include "min_max.h" #include "jnl.h" GBLREF sm_uc_ptr_t reformat_buffer; GBLREF int reformat_buffer_len; GBLREF volatile int reformat_buffer_in_use; /* used only in DEBUG mode */ GBLREF volatile int4 fast_lock_count; /* * 1) We write direct from the given buffer to a block in the database file on disk rather than from a cache record's buffer. * 2) This routine takes care of the maint of blks_to_upgrd in the file-header for these non-cached writes. */ int dsk_write_nocache(gd_region *reg, block_id blk, sm_uc_ptr_t buff, enum db_ver ondsk_blkver) { unix_db_info *udi; int4 size, save_errno; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; int in_len, this_blk_size, gtmcrypt_errno; char *in; gd_segment *seg; boolean_t use_new_key; # ifdef DEBUG sm_uc_ptr_t save_buff; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; udi = FILE_INFO(reg); csa = &udi->s_addrs; csd = csa->hdr; assert(NULL != csd); assert(((blk_hdr_ptr_t)buff)->bver); /* GDSV4 (0) version uses this field as a block length so should always be > 0 */ assert(0 == fast_lock_count); /* ensure the static reformat buffer is not being used currently */ ++fast_lock_count; /* Prevents interrupt from using reformat buffer while we have it */ /* reformat_buffer_in_use should always be incremented only AFTER incrementing fast_lock_count * as it is the latter that prevents interrupts from using the reformat buffer. Similarly * the decrement of fast_lock_count should be done AFTER decrementing reformat_buffer_in_use. */ assert(0 == reformat_buffer_in_use); DEBUG_ONLY(reformat_buffer_in_use++;) /*if (IS_GDS_BLK_DOWNGRADE_NEEDED(ondsk_blkver)) { * Need to downgrade/reformat this block back to the previous format * DEBUG_DYNGRD_ONLY(PRINTF("DSK_WRITE_NOCACHE: Block %d being dynamically downgraded on write\n", blk)); if (csd->blk_size > reformat_buffer_len) { * Buffer not big enough (or does not exist) .. get a new one releasing old if it exists * if (reformat_buffer) free(reformat_buffer); * Different blksized databases in use .. keep only largest one * reformat_buffer = malloc(csd->blk_size); reformat_buffer_len = csd->blk_size; } gds_blk_downgrade((v15_blk_hdr_ptr_t)reformat_buffer, (blk_hdr_ptr_t)buff); buff = reformat_buffer; size = (((v15_blk_hdr_ptr_t)buff)->bsiz + 1) & ~1; * Represents a block state change from V5 -> V4 * INCR_BLKS_TO_UPGRD(csa, csd, 1); assert(SIZEOF(v15_blk_hdr) <= size); } else */ if (GDSV6 == ondsk_blkver) { size = (((blk_hdr_ptr_t)buff)->bsiz + 1) & ~1; assert(SIZEOF(blk_hdr) <= size); /*INCR_BLKS_TO_UPGRD(csa, csd, 1);*/ } else DEBUG_ONLY(if ((GDSV7 == ondsk_blkver) || (GDSV7m == ondsk_blkver))) { assert(GDSVCURR == ondsk_blkver); size = (((blk_hdr_ptr_t)buff)->bsiz + 1) & ~1; assert(SIZEOF(blk_hdr) <= size); /* no adjustment to blks_to_upgrd counter is needed since the format we are going to write is GDSVCURR */ } # ifdef DEBUG else assert(((GDSV7 == ondsk_blkver) && (!MEMCMP_LIT(csd->label, GDS_LABEL))) || ((GDSV7m == ondsk_blkver) && (!MEMCMP_LIT(csd->label, GDS_LABEL))) || ((GDSV6 == ondsk_blkver) && (!MEMCMP_LIT(csd->label, V6_GDS_LABEL)))); # endif if (csd->write_fullblk) /* See similiar logic in wcs_wtstart.c */ size = (int)ROUND_UP(size, (FULL_DATABASE_WRITE == csd->write_fullblk) ? csd->blk_size : csa->fullblockwrite_len); assert(size <= csd->blk_size); assert(FALSE == reg->read_only); /* This function is called by "bml_init" which in turn can be called by "mucregini" or "gdsfilext". The former is * a case where the region is not yet open. csa is usable to a limited extent in the former case but since shared * memory is not set up yet, most of it (e.g. csa->nl etc.) are not usable in the former case. But it is usable * completely in the latter case. Therefore take care using "csa" below. Hence the "reg->open" usages before csa access. * The reg->open check was not necessary until statsdb support because the only caller of "mucregini" was MUPIP CREATE * which would not anyways open the journal pool. But now that GT.M can call "mucregini" to create a statsdb while * it has other basedbs open, it could have the jnlpool open which is why we need this safety check. */ assert(!reg->open || !csa->acc_meth.bg.cache_state->cache_array || buff != (sm_uc_ptr_t)csd); assert(size <= csd->blk_size); if (udi->raw) size = ROUND_UP(size, DISK_BLOCK_SIZE); /* raw I/O must be a multiple of DISK_BLOCK_SIZE */ use_new_key = USES_NEW_KEY(csd); if (IS_ENCRYPTED(csd->is_encrypted) || use_new_key) { this_blk_size = ((blk_hdr_ptr_t)buff)->bsiz; assert((this_blk_size <= csd->blk_size) && (this_blk_size >= SIZEOF(blk_hdr))); in_len = MIN(csd->blk_size, this_blk_size) - SIZEOF(blk_hdr); /* Make sure we do not end up encrypting a zero-length record */ if (BLK_NEEDS_ENCRYPTION(((blk_hdr_ptr_t)buff)->levl, in_len)) { ASSERT_ENCRYPTION_INITIALIZED; in = (char *)(buff + SIZEOF(blk_hdr)); GTMCRYPT_ENCRYPT(csa, (use_new_key ? TRUE : csd->non_null_iv), (use_new_key ? csa->encr_key_handle2 : csa->encr_key_handle), in, in_len, NULL, buff, SIZEOF(blk_hdr), gtmcrypt_errno); if (0 != gtmcrypt_errno) { seg = reg->dyn.addr; GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, rts_error, seg->fname_len, seg->fname); } } } if (udi->fd_opened_with_o_direct) { assert(reg->open); /* should be coming in through "gdsfilext" after having done a "gvcst_init" */ /* This means dio_buff.aligned would already have been allocated to hold at least one GDS block. Use it. */ size = ROUND_UP2(size, DIO_ALIGNSIZE(udi)); assert(DIO_BUFF_NO_OVERFLOW((TREF(dio_buff)), size)); assert(size <= csd->blk_size); memcpy((TREF(dio_buff)).aligned, buff, size); DEBUG_ONLY(save_buff = buff;) /* for DBG purposes */ buff = (sm_uc_ptr_t)(TREF(dio_buff)).aligned; } DB_LSEEKWRITE(reg->open ? csa : NULL, udi, udi->fn, udi->fd, (BLK_ZERO_OFF(csd->start_vbn) + (off_t)blk * csd->blk_size), buff, size, save_errno); DEBUG_ONLY(reformat_buffer_in_use--;) assert(0 == reformat_buffer_in_use); --fast_lock_count; /* reformat buffer is no longer necessary */ assert(0 == fast_lock_count); if (0 != save_errno) /* If it didn't work for whatever reason.. */ return -1; return SS_NORMAL; } fis-gtm-V7.0-005/sr_unix/eintr_wrapper_semop.h0000644000032200000250000000275314342376327020313 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2021 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. * * * ****************************************************************/ #ifndef EINTR_WRP_SEMOP_INCLUDED #define EINTR_WRP_SEMOP_INCLUDED #define NO_WAIT 0 #define FORCED_WAIT 1 #include #include #include "gtm_c_stack_trace_semop.h" #ifdef DEBUG #define SEMVALMAX 32767 #endif /* maintain in parallel with do_semop.c */ #define SEMOP(SEMID, SOPS, NSOPS, RC, TO_WAIT) \ { \ int numsems; \ \ assert(sizeof(SOPS) >= NSOPS); \ for (numsems = NSOPS - 1; numsems >= 0; --numsems) \ CHECK_SEMVAL_GRT_SEMOP(SEMID, SOPS[numsems].sem_num, SOPS[numsems].sem_op); \ if (FORCED_WAIT == TO_WAIT) \ { \ RC = try_semop_get_c_stack(SEMID, SOPS, NSOPS); /* try with patience */ \ if (0 != RC) \ { \ errno = RC; \ RC = -1; \ } \ } else \ { \ assert(NO_WAIT == TO_WAIT); \ while (-1 == (RC = semop(SEMID, SOPS, NSOPS)) && ((EINTR == errno))) \ ; \ } \ } #endif fis-gtm-V7.0-005/sr_unix/encrypt_sign_db_key.sh0000644000032200000250000000611114342376327020423 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright (c) 2010-2021 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. # # # ################################################################# ############################################################################################# # # encrypt_db_key.sh - encrypts the symmetric database key with the recipient's public key. # The file is signed by the key provider. # # Arguments: # $1 - Input file with symmetric encryption key protected with user's private key. # $2 - Path of the output file. # $3 - Email address of the public key's owner. # Rest of line is comment for the output file # ############################################################################################# hostos=`uname -s` # echo and options ECHO=/bin/echo ECHO_OPTIONS="" #Linux honors escape sequence only when run with -e if [ "Linux" = "$hostos" ] ; then ECHO_OPTIONS="-e" ; fi # Input file, output file and recipient e-mail id are mandatory if [ $# -lt 3 ]; then $ECHO "Usage: `basename $0` input_key_file output_key_file recipient_id" ; exit 1 fi # Identify GnuPG - it is required gpg=`command -v gpg2` if [ -z "$gpg" ] ; then gpg=`command -v gpg` ; fi if [ -z "$gpg" ] ; then $ECHO "Unable to find gpg2 or gpg. Exiting" ; exit 1 ; fi # Confirm existence of and ability to read input file if [ ! -r "$1" ] ; then $ECHO $1 does not exist or is not readable ; exit 1 ; fi input_file=$1 # Confirm ability to create output file output_dir=`dirname $2` ; if [ -z "$output_dir" ] ; then output_dir=$PWD ; fi if [ ! -w $output_dir ] ; then $ECHO $output_dir does not exist or is not writable ; exit 1 ; fi if [ -f $2 ] ; then if [ ! -w $2 ] ; then $ECHO Unable to overwrite output file $2 ; exit 1 ; fi fi output_file=$2 recipient=$3 # Get comment for key, create default if none provided on command line shift 3 comment="$*" ; if [ -z "$comment" ] ; then comment="$output_file created from $input_file for $recipient by $USER `date -u`" ; fi # Get passphrase for GnuPG keyring $ECHO $ECHO_OPTIONS Passphrase for keyring: \\c ; stty -echo ; read passphrase ; stty echo ; $ECHO "" # Yes, providing the passphrase on the command line to the second gpg command is not ideal, but that # is the best we can do with this reference implementation. Otherwise it must prompt twice. echo $passphrase | $gpg --batch --passphrase-fd 0 --quiet --decrypt $input_file | \ $gpg --encrypt --armor --sign --output $output_file --comment "$comment" --recipient $recipient --batch --passphrase "$passphrase" fis-gtm-V7.0-005/sr_unix/err_init.c0000755000032200000250000001222414342376327016032 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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 #include #include #include "gtm_string.h" #include "unistd.h" #include "gtm_stdlib.h" #include "gtm_stdio.h" #include "invocation_mode.h" #include "gtmimagename.h" #include "error.h" #include "send_msg.h" #include "have_crit.h" #include "eintr_wrappers.h" #define COREDUMPFILTERFN "/proc/%i/coredump_filter" #define FILTERPARMSIZE (8 + 2 + 1) /* 8 bytes for number (normally 7), 2 bytes for 0x, and 1 null terminator */ #define FILTERENABLEBITS 0x0000073 /* Bits 0, 1, 4, 5, 6 - 32-bit can only handle 10 bytes total */ GBLREF enum gtmImageTypes image_type; GBLDEF char **gtmenvp; error_def(ERR_SYSCALL); /* 1. Allocate a basic initial condition handler stack that can be expanded later if necessary. * 2. On Linux, make sure bits 0,1, 4, 5, and 6 are set in /proc/PID/coredump_filter so dumps the sections that GT.M * cores need to have in them. */ void err_init(void (*x)()) { # ifdef __linux__ int rc; unsigned int filterbits; char procfn[SIZEOF(COREDUMPFILTERFN) + MAX_DIGITS_IN_INT]; /* File name of file to update */ char filter[FILTERPARMSIZE], *filterend; /* Value read in & written out */ char *rcc, *bytes_buf; FILE *filterstrm; /* filter stream file block */ # endif chnd = (condition_handler *)malloc((CONDSTK_INITIAL_INCR + CONDSTK_RESERVE) * SIZEOF(condition_handler)); chnd[0].ch_active = FALSE; chnd[0].save_active_ch = NULL; active_ch = ctxt = &chnd[0]; ctxt->ch = x; chnd_end = &chnd[CONDSTK_INITIAL_INCR]; /* chnd_end is the end of the condition handler stack */ chnd_incr = CONDSTK_INITIAL_INCR * 2; # ifdef __linux__ /* Read the coredump_filter value from /proc for this process, update the value if necessary so we have the proper * flags set to get the info we (and gtmpcat) need to properly process a core file. Note any errors we encounter just * send a message to the operator log and return as nothing here should prevent GT.M from running. * * Note "man 5 core" on x86-64 Linux (Ubuntu 12.04) notes that the /proc/PID/coredump_filter file is only provided when * the Linux kernel is built with the CONFIG_ELF_CORE configuration option. This *seems* to control whether or not the * kernel supports the ELF loader or not. To date, all Linux flavors GT.M supports use ELF so we regard this as largely * mandatory though in the future it may happen that GT.M works yet runs with something other than ELF. In that case, * we'd need to change the below to avoid the operator log messages every time GT.M initializes. * * Note use simple basic methods since this early in initialization not everything is necessarily setup to * be able to properly use the *print*() wrapper functions. * */ bytes_buf = GETENV("gtm_coredump_filter"); if ((NULL == bytes_buf) || (0 != strncmp("-1", bytes_buf, 3))) { rc = snprintf(procfn, SIZEOF(procfn), COREDUMPFILTERFN, getpid()); if (0 > rc) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("snprintf()"), CALLFROM, rc); return; } if (NULL == bytes_buf) { /* If $gtm_coredump_filter is not defined, set the filter to provide a full dump including huge pages */ Fopen(filterstrm, procfn, "r"); if (NULL == filterstrm) { rc = errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fopen()"), CALLFROM, rc); return; } rcc = fgets(filter, SIZEOF(filter), filterstrm); if (NULL == rcc) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fgets()"), CALLFROM, rc); return; } FCLOSE(filterstrm, rc); if (0 > rc) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fclose()"), CALLFROM, rc); return; } filterend = filter + SIZEOF(filter); filterbits = (unsigned int)strtol(filter, &filterend, 16); if (FILTERENABLEBITS != (filterbits & FILTERENABLEBITS)) { /* At least one flag was missing - reset them */ filterbits = filterbits | FILTERENABLEBITS; } snprintf(filter, FILTERPARMSIZE, "0x%07x", filterbits); bytes_buf = filter; } Fopen(filterstrm, procfn, "w"); if (NULL == filterstrm) { rc = errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fopen()"), CALLFROM, rc); return; } rc = fprintf(filterstrm, "%s", bytes_buf); if (0 > rc) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fprintf"), CALLFROM, rc); return; } FCLOSE(filterstrm, rc); if (0 > rc) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fclose()"), CALLFROM, rc); return; } } # endif } fis-gtm-V7.0-005/sr_unix/exi_ch.c0000644000032200000250000000262314342376327015455 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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 "error.h" #include "util.h" GBLREF int4 exi_condition; GBLREF boolean_t created_core; GBLREF boolean_t dont_want_core; DEBUG_ONLY(GBLREF boolean_t ok_to_UNWIND_in_exit_handling;) /* This condition handler is currently only established by gtm_exit_handler. The latter does various types of * rundowns (gds_rundown, io_rundown etc.). And wants any error in one particular type of rundown to stop processing * that and move on to the next type of rundown. To effect that, this condition handler basically does an UNWIND to * return to gtm_exit_handler so it can move on to the next type of rundown. */ CONDITION_HANDLER(exi_ch) { START_CH(TRUE); ESTABLISH(terminate_ch); exi_condition = SIGNAL; if (DUMPABLE) { PRN_ERROR; if (!SUPPRESS_DUMP) DUMP_CORE; PROCDIE(exi_condition); } REVERT; DEBUG_ONLY(ok_to_UNWIND_in_exit_handling = TRUE); UNWIND(NULL, NULL); } fis-gtm-V7.0-005/sr_unix/extract_signal_info.c0000755000032200000250000001413614342376327020245 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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. * * * ****************************************************************/ /* For the various Unix/Posix flavors, extract and fill in the appropriate information */ #include "mdef.h" #include "gtm_string.h" #include #ifndef __MVS__ # include #endif #include "gtm_inet.h" #include "gtm_stdlib.h" #include "gtm_stdio.h" #include "gtm_unistd.h" #if defined(__ia64) && defined(__hpux) #include #include #endif /* __ia64 */ #include "gtm_signal.h" #include "gtmsiginfo.h" /* GCC on HPPA does not have SI_USER defined */ #if !defined(SI_USER) && defined(__GNUC__) # define SI_USER 0 #endif /* OS/390 (R7) does not define SI_USER but for code expansions purposes, define the value it uses in its place */ #if !defined(SI_USER) && defined(__MVS__) # define SI_USER 0 #endif error_def(ERR_SIGACCERR); error_def(ERR_SIGADRALN); error_def(ERR_SIGADRERR); error_def(ERR_SIGBADSTK); error_def(ERR_SIGCOPROC); error_def(ERR_SIGFLTDIV); error_def(ERR_SIGFLTINV); error_def(ERR_SIGFLTOVF); error_def(ERR_SIGFLTRES); error_def(ERR_SIGFLTUND); error_def(ERR_SIGILLADR); error_def(ERR_SIGILLOPC); error_def(ERR_SIGILLOPN); error_def(ERR_SIGILLTRP); error_def(ERR_SIGINTDIV); error_def(ERR_SIGINTOVF); error_def(ERR_SIGMAPERR); error_def(ERR_SIGOBJERR); error_def(ERR_SIGPRVOPC); error_def(ERR_SIGPRVREG); void extract_signal_info(int sig, siginfo_t *info, gtm_sigcontext_t *context, gtmsiginfo_t *gtmsi) { memset(gtmsi, 0, SIZEOF(*gtmsi)); gtmsi->signal = sig; if (NULL != info) { switch(info->si_code) { case SI_USER: gtmsi->send_pid = info->si_pid; gtmsi->send_uid = info->si_uid; gtmsi->infotype |= GTMSIGINFO_USER; break; default: # if defined(__linux__) gtmsi->subcode = info->si_code; gtmsi->bad_vadr = info->si_addr; gtmsi->infotype |= GTMSIGINFO_BADR; if (NULL != context) { # if defined(__i386) # ifndef REG_EIP # define REG_EIP EIP # endif gtmsi->int_iadr = (caddr_t)context->uc_mcontext.gregs[REG_EIP]; # elif defined(__x86_64__) # ifndef REG_RIP # define REG_RIP EIP # endif gtmsi->int_iadr = (caddr_t)context->uc_mcontext.gregs[REG_RIP]; # elif defined(__s390__) gtmsi->int_iadr = (caddr_t)context->uc_mcontext.psw.addr; # else # error "Unsupported Linux Platform" # endif gtmsi->infotype |= GTMSIGINFO_ILOC; } break; # elif defined(__CYGWIN__) gtmsi->subcode = info->si_code; gtmsi->bad_vadr = info->si_addr; gtmsi->infotype |= GTMSIGINFO_BADR; break; # elif defined(__MVS__) if (0 > info->si_code) { /* sent from another process */ gtmsi->send_pid = info->si_pid; gtmsi->send_uid = info->si_uid; gtmsi->infotype |= GTMSIGINFO_USER; } else { gtmsi->subcode = info->si_code; if ((SIGBUS == sig) || (SIGFPE == sig) || (SIGILL == sig) || (SIGSEGV == sig)) { /* address of faulting instruction */ gtmsi->int_iadr = info->si_addr; gtmsi->infotype |= GTMSIGINFO_ILOC; } /* we don't know the format of the mcontext structure yet if (context != NULL) { gtmsi->bad_vadr = (caddr_t)context->uc_mcontext[0]; gtmsi->infotype |= GTMSIGINFO_BADR; } */ } break; # elif defined(_AIX) gtmsi->subcode = info->si_code; gtmsi->bad_vadr = info->si_addr; gtmsi->infotype |= GTMSIGINFO_BADR; if (context != NULL) { gtmsi->int_iadr = (caddr_t)context->sc_context.iar; gtmsi->infotype |= GTMSIGINFO_ILOC; } break; # else # error "Unsupported Platform" # endif } /* See if additional information can be gleaned from the subcode */ if (0 != gtmsi->subcode) { switch (sig) { case SIGILL : switch (gtmsi->subcode) { case ILL_ILLOPC : gtmsi->sig_err = ERR_SIGILLOPC; break; case ILL_ILLOPN : gtmsi->sig_err = ERR_SIGILLOPN; break; case ILL_ILLADR : gtmsi->sig_err = ERR_SIGILLADR; break; case ILL_ILLTRP : gtmsi->sig_err = ERR_SIGILLTRP; break; case ILL_PRVOPC : gtmsi->sig_err = ERR_SIGPRVOPC; break; case ILL_PRVREG : gtmsi->sig_err = ERR_SIGPRVREG; break; case ILL_COPROC : gtmsi->sig_err = ERR_SIGCOPROC; break; case ILL_BADSTK : gtmsi->sig_err = ERR_SIGBADSTK; break; } break; case SIGBUS : switch (gtmsi->subcode) { case BUS_ADRALN : gtmsi->sig_err = ERR_SIGADRALN; break; case BUS_ADRERR : gtmsi->sig_err = ERR_SIGADRERR; break; case BUS_OBJERR : gtmsi->sig_err = ERR_SIGOBJERR; break; } break; case SIGFPE : switch (gtmsi->subcode) { case FPE_INTDIV : gtmsi->sig_err = ERR_SIGINTDIV; break; case FPE_INTOVF : gtmsi->sig_err = ERR_SIGINTOVF; break; case FPE_FLTDIV : gtmsi->sig_err = ERR_SIGFLTDIV; break; case FPE_FLTOVF : gtmsi->sig_err = ERR_SIGFLTOVF; break; case FPE_FLTUND : gtmsi->sig_err = ERR_SIGFLTUND; break; case FPE_FLTRES : gtmsi->sig_err = ERR_SIGFLTRES; break; case FPE_FLTINV : gtmsi->sig_err = ERR_SIGFLTINV; break; } break; case SIGSEGV : switch (gtmsi->subcode) { case SEGV_MAPERR : gtmsi->sig_err = ERR_SIGMAPERR; break; case SEGV_ACCERR : gtmsi->sig_err = ERR_SIGACCERR; break; } break; } } } } fis-gtm-V7.0-005/sr_unix/exttime.c0000755000032200000250000000205614342376327015700 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2008 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 "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "dollarh.h" int exttime (uint4 time, char *buffer, int extract_len) { unsigned char *ptr; uint4 days; time_t seconds; dollarh((time_t)time, &days, &seconds); /* Convert time to $Horolog format */ ptr = i2asc((unsigned char *)(buffer + extract_len), days); *ptr++ = ','; ptr = i2asc(ptr, (uint4)seconds); *ptr++ = '\\'; /* The use of this fn is only once, that too only as offset.. */ return (int)((char *)ptr - buffer); } fis-gtm-V7.0-005/sr_unix/f_char.c0000755000032200000250000000545514342376327015451 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2018 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "stringpool.h" #include "advancewindow.h" #include "gtm_utf8.h" GBLREF spdesc stringpool; GBLREF boolean_t badchar_inhibit; GBLREF boolean_t gtm_utf8_mode; error_def(ERR_FCHARMAXARGS); error_def(ERR_INVDLRCVAL); int f_char(oprtype *a, opctype op) { boolean_t all_lits; unsigned char *base, *outptr, *tmpptr; int argc, ch, char_len, size; mval v; oprtype *argp, argv[CHARMAXARGS]; triple *curr, *last, *root; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* If we are not in UTF8 mode, we need to reroute to the $ZCHAR function to handle things correctly */ if (!gtm_utf8_mode) return f_zchar(a, op); all_lits = TRUE; argp = &argv[0]; argc = 0; for (;;) { if (EXPR_FAIL == expr(argp, MUMPS_INT)) return FALSE; assert(TRIP_REF == argp->oprclass); if (OC_ILIT != argp->oprval.tref->opcode) all_lits = FALSE; argc++; argp++; if (TK_COMMA != TREF(window_token)) break; advancewindow(); if (CHARMAXARGS <= argc) { stx_error(ERR_FCHARMAXARGS); return FALSE; } } if (all_lits) { /* All literals, build the function inline */ size = argc * GTM_MB_LEN_MAX; ENSURE_STP_FREE_SPACE(size); base = stringpool.free; argp = &argv[0]; for (outptr = base, char_len = 0; argc > 0; --argc, argp++) { /* For each wide char value, convert to UTF chars in stringpool buffer */ ch = argp->oprval.tref->operand[0].oprval.ilit; if (0 <= ch) { /* As per the M standard, negative code points should map to no characters */ tmpptr = UTF8_WCTOMB(ch, outptr); assert(tmpptr - outptr <= 4); if (tmpptr != outptr) ++char_len; /* yet another valid character. update the character length */ else if (!badchar_inhibit) stx_error(ERR_INVDLRCVAL, 1, ch); outptr = tmpptr; } } stringpool.free = outptr; MV_INIT_STRING(&v, outptr - base, base); v.str.char_len = char_len; v.mvtype |= MV_UTF_LEN; CLEAR_MVAL_BITS(&v); *a = put_lit(&v); return TRUE; } root = maketriple(op); root->operand[0] = put_ilit(argc + 1); last = root; argp = &argv[0]; for (; argc > 0 ;argc--, argp++) { curr = newtriple(OC_PARAMETER); curr->operand[0] = *argp; last->operand[1] = put_tref(curr); last = curr; } ins_triple(root); *a = put_tref(root); return TRUE; } fis-gtm-V7.0-005/sr_unix/f_piece.c0000755000032200000250000001676514342376327015627 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2015 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_string.h" #include "compiler.h" #include "mmemory.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" #include "fnpc.h" #include "gtm_utf8.h" #include "stringpool.h" #include "op.h" GBLREF boolean_t gtm_utf8_mode; error_def(ERR_COMMA); /* * Given a input (op) indicating whether we are using $ZPIECE or $PIECE, create the appropriate triple for runtime execution * or run $[Z]PIECE if all inputs are literals. There is also a possibility of a OC_FNZP1 being generated if appropriate. * @input[out] a A pointer that will be set to the the result of the expression; in some cases a triple to be evaluated, or * the string literal representing the result of the $PIECE fnction * @returns An integer flag of; TRUE if the function completed successfully, or FALSE if there was an error * @par Side effects * - Calls advance window multiple times, and consumes tokens accordingly * - Calls expr multiple times, which (most notably) adds literals to a hash table * - Calls ins_triple, which adds triples to the execution chain * - Calls st2pool, which inserts strings into the string pool */ int f_piece(oprtype *a, opctype op) { delimfmt unichar; mval *delim_mval, tmp_mval; oprtype x, *newop; triple *delimiter, *first, *last, *r; static mstr scratch_space = {0, 0, 0}; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) return FALSE; if (TK_COMMA != TREF(window_token)) { stx_error(ERR_COMMA); return FALSE; } advancewindow(); delimiter = newtriple(OC_PARAMETER); r->operand[1] = put_tref(delimiter); first = newtriple(OC_PARAMETER); delimiter->operand[1] = put_tref(first); if (EXPR_FAIL == expr(&x, MUMPS_STR)) return FALSE; if (TK_COMMA != TREF(window_token)) first->operand[0] = put_ilit(1); else { advancewindow(); if (EXPR_FAIL == expr(&(first->operand[0]), MUMPS_INT)) return FALSE; } assert(TRIP_REF == x.oprclass); if ((TK_COMMA != TREF(window_token)) && (OC_LIT == x.oprval.tref->opcode) && (1 == ((gtm_utf8_mode && (OC_FNZPIECE != op)) ? MV_FORCE_LEN_DEC(&x.oprval.tref->operand[0].oprval.mlit->v) : x.oprval.tref->operand[0].oprval.mlit->v.str.len))) { /* Potential shortcut to op_fnzp1 or op_fnp1. Make some further checks */ delim_mval = &x.oprval.tref->operand[0].oprval.mlit->v; /* Both valid chars of char_len 1 and invalid chars of byte length 1 get the fast path */ unichar.unichar_val = 0; if (!gtm_utf8_mode || OC_FNZPIECE == op) { /* Single byte delimiter */ r->opcode = OC_FNZP1; unichar.unibytes_val[0] = *delim_mval->str.addr; } else { /* Potentially multiple bytes in one int */ r->opcode = OC_FNP1; assert(SIZEOF(int) >= delim_mval->str.len); memcpy(unichar.unibytes_val, delim_mval->str.addr, delim_mval->str.len); } delimiter->operand[0] = put_ilit(unichar.unichar_val); /* If we have all literals, run at compile time and return the result. To maintain backwards compatibility, * we should emit a warning if there is an invalid UTF8 character, but continue compilation anyaway. */ if ((OC_LIT == r->operand[0].oprval.tref->opcode) && (OC_ILIT == delimiter->operand[0].oprval.tref->opcode) && (OC_ILIT == first->operand[0].oprval.tref->opcode) && (!gtm_utf8_mode || (valid_utf_string(&r->operand[0].oprval.tref->operand[0].oprval.mlit->v.str) && valid_utf_string(&x.oprval.tref->operand[0].oprval.mlit->v.str)))) { /* We don't know how much space we will use; but we know it will be <= the size of the current string */ if (scratch_space.len < r->operand[0].oprval.tref->operand[0].oprval.mlit->v.str.len) { if (NULL != scratch_space.addr) free(scratch_space.addr); scratch_space.addr = malloc(r->operand[0].oprval.tref->operand[0].oprval.mlit->v.str.len); scratch_space.len = r->operand[0].oprval.tref->operand[0].oprval.mlit->v.str.len; } tmp_mval.str.addr = scratch_space.addr; if (OC_FNZP1 == r->opcode) { op_fnzp1(&r->operand[0].oprval.tref->operand[0].oprval.mlit->v, /* First string */ delimiter->operand[0].oprval.tref->operand[0].oprval.ilit, first->operand[0].oprval.tref->operand[0].oprval.ilit, &tmp_mval); } else { op_fnp1(&r->operand[0].oprval.tref->operand[0].oprval.mlit->v, /* First string */ delimiter->operand[0].oprval.tref->operand[0].oprval.ilit, first->operand[0].oprval.tref->operand[0].oprval.ilit, &tmp_mval); } s2pool(&tmp_mval.str); newop = (oprtype *)mcalloc(SIZEOF(oprtype)); *newop = put_lit(&tmp_mval); /* Copies mval so stack var tmp_mval not an issue */ assert(TRIP_REF == newop->oprclass); newop->oprval.tref->src = r->src; *a = put_tref(newop->oprval.tref); return TRUE; } ins_triple(r); *a = put_tref(r); return TRUE; } /* Fall into here if (1) have multi-char delimiter or (2) an invalid utf8 sequence of bytelen > 1 * This generates the longer form call to op_fnpiece/op_fnzpiece. */ delimiter->operand[0] = x; last = newtriple(OC_PARAMETER); first->operand[1] = put_tref(last); if (TK_COMMA != TREF(window_token)) last->operand[0] = first->operand[0]; else { advancewindow(); if (EXPR_FAIL == expr(&(last->operand[0]), MUMPS_INT)) return FALSE; } /* If we have all literals, run at compile time and return the result */ if ((OC_LIT == r->operand[0].oprval.tref->opcode) && (OC_LIT == x.oprval.tref->opcode) && (OC_ILIT == first->operand[0].oprval.tref->opcode) && (OC_ILIT == last->operand[0].oprval.tref->opcode) && (!gtm_utf8_mode || (valid_utf_string(&r->operand[0].oprval.tref->operand[0].oprval.mlit->v.str) && valid_utf_string(&x.oprval.tref->operand[0].oprval.mlit->v.str)))) { /* We don't know how much space we will use; but we know it will be <= the size of the current string */ if (scratch_space.len < r->operand[0].oprval.tref->operand[0].oprval.mlit->v.str.len) { if (NULL != scratch_space.addr) free(scratch_space.addr); scratch_space.addr = malloc(r->operand[0].oprval.tref->operand[0].oprval.mlit->v.str.len); scratch_space.len = r->operand[0].oprval.tref->operand[0].oprval.mlit->v.str.len; } tmp_mval.str.addr = scratch_space.addr; if (!gtm_utf8_mode || (OC_FNZPIECE == op)) { op_fnzpiece(&r->operand[0].oprval.tref->operand[0].oprval.mlit->v, &x.oprval.tref->operand[0].oprval.mlit->v, first->operand[0].oprval.tref->operand[0].oprval.ilit, last->operand[0].oprval.tref->operand[0].oprval.ilit, &tmp_mval); } else { op_fnpiece(&r->operand[0].oprval.tref->operand[0].oprval.mlit->v, &x.oprval.tref->operand[0].oprval.mlit->v, first->operand[0].oprval.tref->operand[0].oprval.ilit, last->operand[0].oprval.tref->operand[0].oprval.ilit, &tmp_mval); } s2pool(&tmp_mval.str); newop = (oprtype *)mcalloc(SIZEOF(oprtype)); *newop = put_lit(&tmp_mval); /* Copies mval so stack var tmp_mval not an issue */ assert(TRIP_REF == newop->oprclass); newop->oprval.tref->src = r->src; *a = put_tref(newop->oprval.tref); return TRUE; } ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_unix/fake_enospc.c0000644000032200000250000001625714342376327016503 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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. * * * ****************************************************************/ #ifdef DEBUG /* The #ifdef DEBUG game is to basically leave a return statement, so picky compilers are satisfied */ #include "mdef.h" #include "gtm_stat.h" #include "gtm_fcntl.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "gtmio.h" /* for CLOSEFILE used by the F_CLOSE macro in JNL_FD_CLOSE */ #include "repl_sp.h" /* for F_CLOSE used by the JNL_FD_CLOSE macro */ #include "iosp.h" /* for SS_NORMAL used by the JNL_FD_CLOSE macro */ #include "gt_timer.h" #include "gtmimagename.h" #include "dpgbldir.h" #include "have_crit.h" #include "anticipatory_freeze.h" #endif #include "fake_enospc.h" #ifdef DEBUG GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF volatile int4 gtmMallocDepth; STATICDEF uint4 syslog_deferred = 0; error_def(ERR_TEXT); error_def(ERR_FAKENOSPCLEARED); #define ENOSPC_FROZEN_DURATION (16 * MILLISECS_IN_SEC) #define ENOSPC_UNFROZEN_DURATION (16 * MILLISECS_IN_SEC * ((rand() % 120) + 1)) /* 16 seconds to 32 minutes */ #define ENOSPC_GDWAIT_INTERVAL (2 * MILLISECS_IN_SEC) #define ENOSPC_RETRY_INTERVAL MILLISECS_IN_SEC #define DEFERRED_SYSLOG_INTERVAL MILLISECS_IN_SEC #define MAX_REGIONS 50 #define NONE 0 /* 1/2 probablility */ #define DB_ON 1 /* 1/6 probablility */ #define JNL_ON 2 /* 1/6 probablility */ #define DB_AND_JNL_ON 3 /* 1/6 probablility */ #define MAX_ENOSPC_TARGET 4 #endif /* This is a debug-only timer routine which is run within the source server that creates the journal pool. * It randomly sets flags indicating that writes to database files and/or journal files within the instance * should return an ENOSPC error, expecting that the custom error retry processing will handle it seamlessly * when it later clears the flags. * The routine logs its activities to the syslog. If the timer fires in a context in which it is not safe * to send a message to the syslog, it starts a handle_deferred_syslog timer (below) to attempt to send the * syslog message at a future time. */ void fake_enospc(void) { # if defined (DEBUG) && !defined (STATIC_ANALYSIS) boolean_t ok_to_interrupt; uint4 deferred_count; char enospc_enable_list[MAX_REGIONS]; const char *syslog_msg; gd_addr *addr_ptr; gd_region *r_local, *r_top; int i; sgmnt_addrs *csa; jnlpool_addrs_ptr_t local_jnlpool; /* needed by INST_FREEZE_ON_NOSPC_ENABLED */ DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ok_to_interrupt = (INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) && (0 == gtmMallocDepth); if (syslog_deferred && ok_to_interrupt) { cancel_timer((TID)&syslog_deferred); deferred_count = syslog_deferred; syslog_deferred = 0; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_FAKENOSPCLEARED, 1, deferred_count); } /* If ok_to_interrupt is FALSE and intrpt_ok_state == INTRPT_IN_SHMDT, it is possible we have detached * from the shared memory (i.e. in the middle of the shmdt()) when the timer interrupt occurs and so * we cannot do a IS_REPL_INST_FROZEN check which looks at a field inside jnlpool_ctl. Account for that below. */ if (syslog_deferred || (!ok_to_interrupt && ((INTRPT_IN_SHMDT == intrpt_ok_state) || !IS_REPL_INST_FROZEN)) || !CUSTOM_ERRORS_LOADED) { /* We have to skip this because we have just fallen into deferred zone or we are currently in it */ /* Try again in a second */ start_timer((TID)fake_enospc, ENOSPC_RETRY_INTERVAL, fake_enospc, 0, NULL); return; } assert(0 == syslog_deferred); addr_ptr = get_next_gdr(NULL); if (NULL == addr_ptr) /* Ensure that there is a global directory to operate on. */ { start_timer((TID)fake_enospc, ENOSPC_GDWAIT_INTERVAL, fake_enospc, 0, NULL); return; } assert(NULL == get_next_gdr(addr_ptr)); /* Randomly simulate ENOSPC or free space. NO more than 50 regions are allowed to avoid unnecessary * malloc/frees in debug-only code */ assert(MAX_REGIONS >= addr_ptr->n_regions); if (!IS_REPL_INST_FROZEN) { /* We are in an UNFROZEN state, and about to be FROZEN due to ENOSPC */ assert(MAX_REGIONS >= addr_ptr->n_regions); for (i = 0; i < addr_ptr->n_regions; i++) { if (rand() % 2) /* Should we trigger fake ENOSPC on this region */ enospc_enable_list[i] = rand() % (MAX_ENOSPC_TARGET - 1) + 1; /* What kind? */ else enospc_enable_list[i] = NONE; } start_timer((TID)fake_enospc, ENOSPC_FROZEN_DURATION, fake_enospc, 0, NULL); } else { /* We are in a FROZEN state, and about to be UNFROZEN due to free space */ memset(enospc_enable_list, 0, MAX_REGIONS); if (!ok_to_interrupt) { syslog_deferred = 1; start_timer((TID)&syslog_deferred, DEFERRED_SYSLOG_INTERVAL, handle_deferred_syslog, 0, NULL); } start_timer((TID)fake_enospc, ENOSPC_UNFROZEN_DURATION, fake_enospc, 0, NULL); } for (r_local = addr_ptr->regions, r_top = r_local + addr_ptr->n_regions, i = 0; r_local < r_top; r_local++, i++) { if (!IS_REG_BG_OR_MM(r_local)) continue; csa = REG2CSA(r_local); if ((NULL != csa) && (NULL != csa->nl) && INST_FREEZE_ON_NOSPC_ENABLED(csa, local_jnlpool)) { syslog_msg = NULL; switch(enospc_enable_list[i]) { case NONE: if (csa->nl->fake_db_enospc || csa->nl->fake_jnl_enospc) { syslog_msg = "Turning off fake ENOSPC for both database and journal file."; csa->nl->fake_db_enospc = FALSE; csa->nl->fake_jnl_enospc = FALSE; } break; case DB_ON: syslog_msg = "Turning on fake ENOSPC only for database file."; csa->nl->fake_db_enospc = TRUE; csa->nl->fake_jnl_enospc = FALSE; break; case JNL_ON: syslog_msg = "Turning on fake ENOSPC only for journal file."; csa->nl->fake_db_enospc = FALSE; csa->nl->fake_jnl_enospc = TRUE; break; case DB_AND_JNL_ON: syslog_msg = "Turning on fake ENOSPC for both database and journal file."; csa->nl->fake_db_enospc = TRUE; csa->nl->fake_jnl_enospc = TRUE; break; default: assert(FALSE); break; /* NOTREACHED */ } if (ok_to_interrupt && (NULL != syslog_msg)) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_TEXT, 2, DB_LEN_STR(r_local), ERR_TEXT, 2, LEN_AND_STR(syslog_msg)); } } # endif return; } /* This is a timer routine used by fake_enospc, above. */ void handle_deferred_syslog(void) { # if defined (DEBUG) && !defined (STATIC_ANALYSIS) boolean_t ok_to_interrupt; uint4 deferred_count; if (syslog_deferred) { ok_to_interrupt = (INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) && (0 == gtmMallocDepth); if (ok_to_interrupt) { deferred_count = syslog_deferred; syslog_deferred = 0; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_FAKENOSPCLEARED, 1, deferred_count); } else { syslog_deferred++; start_timer((TID)&syslog_deferred, DEFERRED_SYSLOG_INTERVAL, handle_deferred_syslog, 0, NULL); } } # endif return; } fis-gtm-V7.0-005/sr_unix/fake_enospc.h0000644000032200000250000000127314342376327016500 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef FAKE_ENOSPC_H_INCLUDED #define FAKE_ENOSPC_H_INCLUDED #define ENOSPC_INIT_DURATION (8 * MILLISECS_IN_SEC) void fake_enospc(void); void handle_deferred_syslog(void); #endif fis-gtm-V7.0-005/sr_unix/file_head_read.c0000755000032200000250000000747214342376327017123 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 #include "gtm_unistd.h" #include "gtm_fcntl.h" #include "gtm_stat.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "db_header_conversion.h" #include "filestruct.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtmio.h" #include "iosp.h" #include "eintr_wrappers.h" #include "file_head_read.h" #include "gtmmsg.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif error_def(ERR_DBFILOPERR); error_def(ERR_DBNOTGDS); error_def(ERR_TEXT); /* * This is a plain way to read file header. * User needs to take care of concurrency issue etc. * Parameters : * fn : full name of a database file. * header: Pointer to database file header structure (may not be in shared memory) * len: size of header (may be just SGMNT_HDR_LEN or SIZEOF_FILE_HDR_MAX) */ boolean_t file_head_read(char *fn, sgmnt_data_ptr_t header, int4 len) { int save_errno, fd, header_size; struct stat stat_buf; ZOS_ONLY(int realfiletag;) header_size = SIZEOF(sgmnt_data); OPENFILE(fn, O_RDONLY, fd); /* udi not available so OPENFILE_DB not used */ if (FD_INVALID == fd) { save_errno = errno; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); return FALSE; } # ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(fd, TAG_BINARY, &realfiletag)) TAG_POLICY_GTM_PUTMSG(fn, errno, realfiletag, TAG_BINARY); # endif FSTAT_FILE(fd, &stat_buf, save_errno); if (-1 == save_errno) { save_errno = errno; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); CLOSEFILE_RESET(fd, save_errno); /* resets "fd" to FD_INVALID */ return FALSE; } if (!S_ISREG(stat_buf.st_mode) || stat_buf.st_size < header_size) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_DBNOTGDS, 2, LEN_AND_STR(fn)); CLOSEFILE_RESET(fd, save_errno); /* resets "fd" to FD_INVALID */ return FALSE; } LSEEKREAD(fd, 0, header, header_size, save_errno); if (0 == memcmp(header->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_upconv(header); if (0 != save_errno) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); CLOSEFILE_RESET(fd, save_errno); /* resets "fd" to FD_INVALID */ return FALSE; } if (memcmp(header->label, GDS_LABEL, GDS_LABEL_SZ - 1) && memcmp(header->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_DBNOTGDS, 2, LEN_AND_STR(fn)); CLOSEFILE_RESET(fd, save_errno); /* resets "fd" to FD_INVALID */ return FALSE; } CHECK_DB_ENDIAN(header, strlen(fn), fn); /* BYPASSOK */ assert(MASTER_MAP_SIZE_MAX >= MASTER_MAP_SIZE(header)); assert(SGMNT_HDR_LEN == len || SIZEOF_FILE_HDR(header) <= len); if (SIZEOF_FILE_HDR(header) <= len) { LSEEKREAD(fd, ROUND_UP(SGMNT_HDR_LEN + 1, DISK_BLOCK_SIZE), MM_ADDR(header), MASTER_MAP_SIZE(header), save_errno); if (0 != save_errno) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); CLOSEFILE_RESET(fd, save_errno); /* resets "fd" to FD_INVALID */ return FALSE; } } CLOSEFILE_RESET(fd, save_errno); /* resets "fd" to FD_INVALID */ if (0 != save_errno) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); return FALSE; } return TRUE; } fis-gtm-V7.0-005/sr_unix/file_head_write.c0000755000032200000250000000502214342376327017327 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 #include "gtm_unistd.h" #include "gtm_fcntl.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "db_header_conversion.h" #include "filestruct.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtmio.h" #include "iosp.h" #include "eintr_wrappers.h" #include "file_head_write.h" #include "gtmmsg.h" #include "jnl.h" #include "anticipatory_freeze.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif error_def(ERR_DBFILOPERR); error_def(ERR_DBNOTGDS); error_def(ERR_TEXT); /* This is a plain way to write file header to database. * User needs to take care of concurrency issue etc. * Parameters : * fn : full name of a database file. * header: Pointer to database file header structure (may not be in shared memory) * len: length of header to write (should be either SGMNT_HDR_LEN or SIZEOF_FILE_HDR(header)) */ boolean_t file_head_write(char *fn, sgmnt_data_ptr_t header, int4 len) { int save_errno, fd; ZOS_ONLY(int realfiletag;) assert(SGMNT_HDR_LEN == len || SIZEOF_FILE_HDR(header) == len); OPENFILE(fn, O_RDWR, fd); /* udi not available so OPENFILE_DB not used */ if (FD_INVALID == fd) { save_errno = errno; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); return FALSE; } # ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(fd, TAG_BINARY, &realfiletag)) TAG_POLICY_GTM_PUTMSG(fn, errno, realfiletag, TAG_BINARY); # endif if (0 == memcmp(header->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv(header); DB_LSEEKWRITE(NULL, ((unix_db_info *)NULL), NULL, fd, 0, header, len, save_errno); if (0 != save_errno) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); return FALSE; } CLOSEFILE_RESET(fd, save_errno); /* resets "fd" to FD_INVALID */ if (0 != save_errno) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); return FALSE; } return TRUE; } fis-gtm-V7.0-005/sr_unix/file_input.c0000644000032200000250000002107314342376327016354 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2021 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_fcntl.h" #include "gtm_string.h" #include "gtm_unistd.h" #include #include "gtm_stdio.h" #include "io.h" #include "iormdef.h" #include "iosp.h" #include "copy.h" #include "error.h" #include "gtmio.h" #include "io_params.h" #include "gtm_stat.h" #include "op.h" #include "file_input.h" #include "iotimer.h" #include "min_max.h" #define BUFF_SIZE 65535 GBLREF int (*op_open_ptr)(mval *v, mval *p, mval *t, mval *mspace); GBLREF uint4 dollar_tlevel; GBLREF io_pair io_curr_device; LITREF mval literal_notimeout; LITREF mval literal_zero; error_def(ERR_LOADFILERR); error_def(ERR_FILEOPENFAIL); error_def(ERR_PREMATEOF); static char buff1[BUFF_SIZE]; static char *buff1_end; static char *buff1_ptr; static off_t buff1_ptr_file_offset; static char *load_fn_ptr; static int load_fn_len; static unsigned char open_params_list_default[] = { (unsigned char)iop_recordsize, /* 64K enough to hold MAX_BLK_SZ */ # ifdef BIGENDIAN (unsigned char)0, (unsigned char)0, (unsigned char)255, (unsigned char)255, # else (unsigned char)255, (unsigned char)255, (unsigned char)0, (unsigned char)0, # endif (unsigned char)iop_readonly, (unsigned char)iop_m, /* iop_stream not included since it is necessary only if we are opening file for write (which is not the case here) */ (unsigned char)iop_nowrap, (unsigned char)iop_eol }; static unsigned char open_params_list_rewind[] = { (unsigned char)iop_recordsize, /* 64K enough to hold MAX_BLK_SZ */ # ifdef BIGENDIAN (unsigned char)0, (unsigned char)0, (unsigned char)255, (unsigned char)255, # else (unsigned char)255, (unsigned char)255, (unsigned char)0, (unsigned char)0, # endif (unsigned char)iop_readonly, (unsigned char)iop_rewind, (unsigned char)iop_m, /* iop_stream not included since it is necessary only if we are opening file for write (which is not the case here) */ (unsigned char)iop_nowrap, (unsigned char)iop_eol }; void file_input_init(char *fn, short fn_len, open_params_flags params_flag) { int status; mval pars, val; unsigned char no_param = (unsigned char)iop_eol; unsigned char* open_params; ESTABLISH(mupip_load_ch); pars.mvtype = MV_STR; if (params_flag & IOP_REWIND) { pars.str.len = SIZEOF(open_params_list_rewind); pars.str.addr = (char *)open_params_list_rewind; } else { /* IOP_EOL */ pars.str.len = SIZEOF(open_params_list_default); pars.str.addr = (char *)open_params_list_default; } val.mvtype = MV_STR; val.str.len = fn_len; val.str.addr = (char *)fn; /* The mode will be set to M for reads */ status = (*op_open_ptr)(&val, &pars, (mval *)&literal_zero, NULL); if (!status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_FILEOPENFAIL, 2, fn_len, fn); pars.str.len = SIZEOF(iop_eol); pars.str.addr = (char *)&no_param; op_use(&val, &pars); load_fn_ptr = fn; load_fn_len = fn_len; buff1_ptr = buff1; buff1_ptr_file_offset = 0; buff1_end = buff1; REVERT; return; } void file_input_close(void) { mval pars, val; unsigned char no_param = (unsigned char)iop_eol; val.mvtype = pars.mvtype = MV_STR; val.str.addr = (char *)load_fn_ptr; val.str.len = load_fn_len; pars.str.len = SIZEOF(iop_eol); pars.str.addr = (char *)&no_param; op_close(&val, &pars); } void file_input_bin_init(char *line1_ptr, int line1_len) { assert(buff1_ptr == buff1); assert((0 < line1_len) && (line1_len < BUFF_SIZE)); assert(buff1_end == buff1_ptr); assert(0 == buff1_ptr_file_offset); line1_len = MIN(line1_len, (BUFF_SIZE - 1)); memcpy(buff1_ptr, line1_ptr, line1_len); assert(line1_len < BUFF_SIZE); /* For SCI */ buff1_end += line1_len; } int file_input_bin_get(char **in_ptr, off_t *file_offset, char **buff_base, boolean_t do_rts_error) { char *ptr; int rd_cnt, rd_len, ret, s1; unsigned short s1s; ESTABLISH_RET(mupip_load_ch, 0); if (SIZEOF(short) > (buff1_end - buff1_ptr)) { if (0 >= (rd_len = file_input_bin_read())) /* NOTE assignment */ { ret = 0; if (buff1_end != buff1_ptr) { if (do_rts_error) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_PREMATEOF); ret = ERR_PREMATEOF; } else if (-1 == rd_len) { if (do_rts_error) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_LOADFILERR, 2, load_fn_len, load_fn_ptr); ret = ERR_LOADFILERR; } if (!do_rts_error || 0 == ret) { REVERT; return -ret; } } buff1_end += rd_len; } GET_USHORT(s1s, buff1_ptr); buff1_ptr += SIZEOF(short); s1 = s1s; assert(0 < s1); assert(BUFF_SIZE >= s1); if ((buff1_end - buff1_ptr) < s1) { /* not enough data in buffer, read additional bytes */ rd_len = file_input_bin_read(); if ((rd_len + buff1_end - buff1_ptr) < s1) { if (!do_rts_error) REVERT; if (-1 == rd_len) { if (do_rts_error) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_LOADFILERR, 2, load_fn_len, load_fn_ptr); return -ERR_LOADFILERR; } if (do_rts_error) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_PREMATEOF); return -ERR_PREMATEOF; } buff1_end += rd_len; } *in_ptr = buff1_ptr; buff1_ptr += s1; *file_offset = buff1_ptr_file_offset; *buff_base = buff1; REVERT; return s1; } int file_input_bin_read(void) { d_rm_struct *d_rm; int s1, rdlen; io_desc *iod; s1 = (int)(buff1_end - buff1_ptr); memmove(buff1, buff1_ptr, s1); iod = io_curr_device.in; d_rm = (d_rm_struct *)iod->dev_sp; assert(NULL != d_rm); assert(BUFF_SIZE - s1); buff1_end = buff1 + s1; buff1_ptr_file_offset += (buff1_ptr - buff1); buff1_ptr = buff1; DOREADRL(d_rm->fildes, buff1_end, BUFF_SIZE - s1, rdlen); # ifdef DEBUG_FO_BIN PRINTF("int file_input_bin_read:\t\tread(%d, %x, %d) = %d\n", d_rm->fildes, buff1, BUFF_SIZE, s1); if (BUFF_SIZE - s1 > rdlen) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); # endif return rdlen; } int file_input_get_xchar(char *in_ptr, int max_chars_to_read) /* uses opreadfl with a buffered getc call to read file content */ { int rd_len; mval val; op_readfl(&val, max_chars_to_read, (mval *)(dollar_tlevel ? &literal_zero : &literal_notimeout)); rd_len = val.str.len; if ((0 == rd_len) && io_curr_device.in->dollar.zeof) return -1; memcpy(in_ptr, val.str.addr, rd_len); return rd_len; } int file_input_read_xchar(char *in_ptr, int max_chars_to_read) /* uses DOREADRL with read system api to read file content */ { int rd_len; io_desc *iod; d_rm_struct *d_rm; iod = io_curr_device.in; d_rm = (d_rm_struct *)iod->dev_sp; assert(NULL != d_rm); DOREADRL(d_rm->fildes, in_ptr, max_chars_to_read, rd_len); return rd_len; } /* Returns * -1 (FILE_INPUT_GET_ERROR) in case of errors, * -2 (FILE_INPUT_GET_LINE2LONG) in case line length of read becomes > input max_len (assuming max_len is non-zero) */ int file_input_get(char **in_ptr, unsigned int max_len) { char *ptr, *tmp_ptr; int rd_len, s1; mval val; static char *mbuff = buff1; static unsigned int mbuff_len = BUFF_SIZE; unsigned int new_mbuff_len, ret_len; ESTABLISH_RET(mupip_load_ch, 0); ret_len = 0; for (;;) { /* one-time only reads if in TP to avoid TPNOTACID, otherwise use untimed reads */ op_read(&val, (mval *)(dollar_tlevel ? &literal_zero: &literal_notimeout)); assert(0 <= val.str.len); rd_len = (unsigned int)val.str.len; if ((0 == rd_len) && io_curr_device.in->dollar.zeof) { REVERT; if (io_curr_device.in->dollar.x) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_PREMATEOF); return FILE_INPUT_GET_ERROR; } if (max_len && ((ret_len + rd_len) > max_len)) { REVERT; return FILE_INPUT_GET_LINE2LONG; } if (mbuff_len < (ret_len + rd_len)) { new_mbuff_len = MAX((ret_len + rd_len), (2 * mbuff_len)); tmp_ptr = (char *)malloc(new_mbuff_len); if (NULL == tmp_ptr) { REVERT; return FILE_INPUT_GET_ERROR; } assert(new_mbuff_len >= ret_len); /* For SCI */ memcpy(tmp_ptr, mbuff, ret_len); if (mbuff != buff1) /* do not free static array, free all later expansions (malloc buffers) */ free(mbuff); mbuff = tmp_ptr; mbuff_len = new_mbuff_len; } assert(mbuff_len >= (ret_len + rd_len)); memcpy((unsigned char *)(mbuff + ret_len), val.str.addr, rd_len); ret_len += rd_len; if ( !(io_curr_device.in->dollar.x) ) { *in_ptr = mbuff; REVERT; return ret_len; } } } fis-gtm-V7.0-005/sr_unix/file_input.h0000644000032200000250000000241714342376327016362 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2021 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. * * * ****************************************************************/ #ifndef FILE_INPUT_INPUT_H_INCLUDED #define FILE_INPUT_INPUT_H_INCLUDED #define FILE_INPUT_GET_ERROR -1 #define FILE_INPUT_GET_LINE2LONG -2 #define DO_RTS_ERROR_TRUE TRUE #define DO_RTS_ERROR_FALSE FALSE typedef enum { IOP_EOL = 0x00, IOP_REWIND = 0x01 } open_params_flags; void file_input_init(char *fn, short fn_len, open_params_flags params_flag); void file_input_close(void); void file_input_bin_init(char *line1_ptr, int line1_len); int file_input_bin_get(char **in_ptr, off_t *file_offset, char **buff_base, boolean_t do_rts_error); int file_input_bin_read(void); int file_input_get_xchar(char *in_ptr, int max_chars_to_read); int file_input_read_xchar(char *in_ptr, int max_chars_to_read); int file_input_get(char **in_ptr, unsigned int max_len); #endif fis-gtm-V7.0-005/sr_unix/filestruct.h0000644000032200000250000001437614342376327016417 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef FILESTRUCT_H #define FILESTRUCT_H #ifndef dev_t #include #endif #include "gdsdbver.h" #define GDS_LABEL_GENERIC "GDSDYNUNX" #define GDS_LABEL GDS_LABEL_GENERIC GDS_CURR_NO_PAREN /* This string must be of length GDS_LABEL_SZ */ #define V6_GDS_LABEL GDS_LABEL_GENERIC GDS_V50 /* Any changes must be copied to the definition in v6_gdsfhead.h */ #define GDS_RPL_LABEL "GDSRPLUNX04" /* format of journal pool and receive pool (must be of length GDS_LABEL_SZ) */ /* Check O_DIRECT alignment requirements for each supported platform */ #define DIO_ALIGNSIZE(udi) ((udi)->db_fs_block_size) #define BUFF_IS_DIO_ALIGNED(udi, buff) (0 == (((UINTPTR_T)(buff)) % DIO_ALIGNSIZE(udi))) #define OFFSET_IS_DIO_ALIGNED(udi, off) (0 == ((off) % DIO_ALIGNSIZE(udi))) #define SIZE_IS_DIO_ALIGNED(udi, size) (0 == ((size) % DIO_ALIGNSIZE(udi))) #define DIO_BUFF_NO_OVERFLOW(DIO_BUFF, SIZE) \ (((UINTPTR_T)(DIO_BUFF).aligned + (SIZE)) <= ((UINTPTR_T)(DIO_BUFF).unaligned + (DIO_BUFF).unaligned_size)) /* DIO buffer alignment is needed in almost all cases of writing to the database file. The only exception is if * we are about to create the database file AND we should not have opened the fd with O_DIRECT. Take that into account. */ #define ASSERT_NO_DIO_ALIGN_NEEDED(UDI) assert(in_mu_cre_file && !(UDI)->fd_opened_with_o_direct) #define DBG_CHECK_DIO_ALIGNMENT(udi, offset, buff, size) \ MBSTART { \ DEBUG_ONLY(DCL_THREADGBL_ACCESS); \ \ DEBUG_ONLY(SETUP_THREADGBL_ACCESS); \ assert((NULL == (udi)) || !(udi)->fd_opened_with_o_direct || (NULL != (TREF(dio_buff)).aligned)); \ assert((NULL == (udi)) || !(udi)->fd_opened_with_o_direct || OFFSET_IS_DIO_ALIGNED(udi, offset)); \ assert((NULL == (udi)) || !(udi)->fd_opened_with_o_direct || BUFF_IS_DIO_ALIGNED(udi, buff)); \ assert((NULL == (udi)) || !(udi)->fd_opened_with_o_direct || SIZE_IS_DIO_ALIGNED(udi, size)); \ /* If we are using the global variable "dio_buff.aligned", then we better not be executing in timer \ * code or in threaded code (as we have only ONE buffer to use). Assert that. \ */ \ assert(((TREF(dio_buff)).aligned != (char *)(buff)) || (!timer_in_handler && !multi_thread_in_use)); \ /* Assert that we are not exceeding allocated buffer bounds in case of "dio_buff.aligned" */ \ assert(((TREF(dio_buff)).aligned != (char *)(buff)) || DIO_BUFF_NO_OVERFLOW((TREF(dio_buff)), size)); \ } MBEND typedef struct { int unaligned_size; /* size of allocated (and potentially) unaligned buffer */ char *unaligned; /* pointer to start of allocated buffer */ char *aligned; /* pointer to start of aligned buffer (i.e. aligned >= unaligned) */ } dio_buff_t; #ifdef DEBUG GBLREF boolean_t in_mu_cre_file; GBLREF volatile boolean_t timer_in_handler; GBLREF boolean_t multi_thread_in_use; #endif /* Returns a buffer that has space to hold at least SIZE bytes where the buffer start is OS_PAGE_SIZE aligned. */ #define DIO_BUFF_EXPAND_IF_NEEDED(UDI, SIZE, DIO_BUFF) \ MBSTART { \ assert(UDI->fd_opened_with_o_direct); \ assert(DIO_ALIGNSIZE(UDI)); \ DIO_BUFF_EXPAND_IF_NEEDED_NO_UDI(SIZE, DIO_ALIGNSIZE(UDI), DIO_BUFF); \ } MBEND #define DIO_BUFF_EXPAND_IF_NEEDED_NO_UDI(SIZE, ALIGNSIZE, DIO_BUFF) \ MBSTART { \ int needed; \ \ needed = ROUND_UP2(SIZE, ALIGNSIZE) + OS_PAGE_SIZE; \ if (needed > (DIO_BUFF)->unaligned_size) \ { \ if (NULL != (DIO_BUFF)->unaligned) \ { \ free((DIO_BUFF)->unaligned); \ (DIO_BUFF)->unaligned = NULL; \ } \ (DIO_BUFF)->unaligned = (char *)malloc(needed); \ (DIO_BUFF)->aligned = (char *)ROUND_UP2((UINTPTR_T)(DIO_BUFF)->unaligned, OS_PAGE_SIZE);\ (DIO_BUFF)->unaligned_size = needed; \ } \ } MBEND typedef struct unix_db_info_struct { sgmnt_addrs s_addrs; char *fn; int fd; gd_addr *owning_gd; /* Used by the linux kernel interface for AIO. As long as owning_gd * is NULL we can assume no writes to this database file on disk * have happened yet. */ gd_id fileid; int semid; time_t gt_sem_ctime; int shmid; time_t gt_shm_ctime; int ftok_semid; key_t key; boolean_t raw; uint4 db_fs_block_size; unsigned int shm_created : 1; unsigned int shm_deleted : 1; unsigned int sem_created : 1; unsigned int sem_deleted : 1; unsigned int grabbed_ftok_sem : 1; unsigned int grabbed_access_sem : 1; unsigned int counter_acc_incremented : 1; unsigned int counter_ftok_incremented : 1; unsigned int fd_opened_with_o_direct : 1; /* If dbfilop(FC_OPEN) happened with O_DIRECT */ } unix_db_info; typedef struct unix_file_info_struct { int file; int fn_len; char *fn; } unix_file_info; #define FC2UDI(FILE_CNTL) ((unix_db_info *)(FILE_CNTL->file_info)) #define FILE_CNTL(reg) (reg)->dyn.addr->file_cntl #define FILE_INFO(reg) ((unix_db_info *)(reg)->dyn.addr->file_cntl->file_info) #define FILE_ID(reg) ((unix_db_info *)(reg)->dyn.addr->file_cntl->file_info)->fileid #define GDS_INFO unix_db_info #define FI_FN(file_info) ((unix_file_info *)(file_info))->fn #define FI_FN_LEN(file_info) ((unix_file_info *)(file_info))->fn_len #define REG_EQUAL(fileinfo,reg) ((fileinfo)->fileid.inode == FILE_INFO(reg)->fileid.inode \ && (fileinfo)->fileid.device == FILE_INFO(reg)->fileid.device) #define CSD2UDI(CSD, UDI) \ MBSTART { \ UDI->shmid = CSD->shmid; \ UDI->semid = CSD->semid; \ UDI->gt_sem_ctime = CSD->gt_sem_ctime.ctime; \ UDI->gt_shm_ctime = CSD->gt_shm_ctime.ctime; \ } MBEND #define UDI2CSD(UDI, CSD) \ MBSTART { \ CSD->shmid = UDI->shmid; \ CSD->semid = UDI->semid; \ CSD->gt_sem_ctime.ctime = UDI->gt_sem_ctime; \ CSD->gt_shm_ctime.ctime = UDI->gt_shm_ctime; \ } MBEND #endif /* FILESTRUCT_H */ fis-gtm-V7.0-005/sr_unix/fix_pages.c0000755000032200000250000000102114342376327016155 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "fix_pages.h" void fix_pages(unsigned char *bot, unsigned char *top) { } fis-gtm-V7.0-005/sr_unix/forced_exit_err_display.c0000644000032200000250000000613614342376327021111 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016 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_signal.h" /* needed for gtmsiginfo.h */ #include "gtmmsg.h" #include "send_msg.h" #include "gtmsiginfo.h" #include "gtmimagename.h" #include "forced_exit_err_display.h" GBLREF int forced_exit_err; GBLREF gtmsiginfo_t signal_info; GBLREF enum gtmImageTypes image_type; GBLREF uint4 process_id; LITREF gtmImageName gtmImageNames[]; error_def(ERR_KILLBYSIG); error_def(ERR_KILLBYSIGUINFO); error_def(ERR_KILLBYSIGSINFO1); error_def(ERR_KILLBYSIGSINFO2); error_def(ERR_KILLBYSIGSINFO3); void forced_exit_err_display(void) { assert(forced_exit_err); /* note can't use switch here because ERR_xxx are not defined as constants */ if (ERR_KILLBYSIG == forced_exit_err) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_KILLBYSIG, 4, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_KILLBYSIG, 4, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal); } else if (ERR_KILLBYSIGUINFO == forced_exit_err) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_KILLBYSIGUINFO, 6, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.send_pid, signal_info.send_uid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_KILLBYSIGUINFO, 6, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.send_pid, signal_info.send_uid); } else if (ERR_KILLBYSIGSINFO1 == forced_exit_err) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_KILLBYSIGSINFO1, 6, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.int_iadr, signal_info.bad_vadr); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_KILLBYSIGSINFO1, 6, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.int_iadr, signal_info.bad_vadr); } else if (ERR_KILLBYSIGSINFO2 == forced_exit_err) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_KILLBYSIGSINFO2, 5, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.int_iadr); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_KILLBYSIGSINFO2, 5, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.int_iadr); } else if (ERR_KILLBYSIGSINFO3 == forced_exit_err) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_KILLBYSIGSINFO3, 5, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.bad_vadr); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_KILLBYSIGSINFO3, 5, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.bad_vadr); } else { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(1) forced_exit_err); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) forced_exit_err); } } fis-gtm-V7.0-005/sr_unix/forced_exit_err_display.h0000644000032200000250000000124714342376327021114 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016 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. * * * ****************************************************************/ #ifndef FORCED_EXIT_ERR_DISPLAY_INCLUDED #define FORCED_EXIT_ERR_DISPLAY_INCLUDED void forced_exit_err_display(void); #endif /* FORCED_EXIT_ERR_DISPLAY_INCLUDED */ fis-gtm-V7.0-005/sr_unix/fork_init.h0000644000032200000250000000333714342376327016212 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2018 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. * * * ****************************************************************/ #ifndef FORK_INIT_H_INCLUDED #define FORK_INIT_H_INCLUDED #include "have_crit.h" /* This macro takes care of safely clearing the timer queue and resetting the timer-related globals when we need * to fork-off a process. Note that it is necessary to use this macro EVEN WHEN the fork is immediately followed * by an exec (of any flavor), because of the ENABLE_INTERRUPTS macro usage right after the "fork" call. * If this call is done in the child process (0 == pid), and timer variables are not cleared, it is possible * (if the right conditions are met) that "have_crit" gets invoked as part of the ENABLE_INTERRUPTS macro in * the child process and ends up with a GTMASSERT -- BYPASSOK -- (GTM-8050). If timers have not been initialized at all, * then "clear_timers" does no system calls so the only overhead is a function call invocation which is okay considering * we anyways have invoked the "fork()" system call just now. */ #define FORK(pid) \ { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_FORK_OR_SYSTEM, prev_intrpt_state) \ pid = fork(); \ if (0 == pid) \ clear_timers(); \ ENABLE_INTERRUPTS(INTRPT_IN_FORK_OR_SYSTEM, prev_intrpt_state) \ } #endif fis-gtm-V7.0-005/sr_unix/ftok_sem_get_common.c0000755000032200000250000001762414342376327020246 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_ipc.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "gtm_stdlib.h" #include "gtm_fcntl.h" #include "gtm_stdio.h" #include "gtm_stat.h" #include "gtm_signal.h" /* for kill(), SIGTERM, SIGQUIT */ #include #include #include #include #include #include "gtm_sem.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtm_c_stack_trace.h" #include "eintr_wrapper_semop.h" #include "eintr_wrappers.h" #include "mu_rndwn_file.h" #include "error.h" #include "io.h" #include "gt_timer.h" #include "iosp.h" #include "gtmio.h" #include "gtmimagename.h" #include "do_semop.h" #include "ipcrmid.h" #include "gtmmsg.h" #include "util.h" #include "semwt2long_handler.h" #include "repl_sem.h" #include "jnl.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" #include "gtm_semutils.h" #include "ftok_sems.h" #include "wbox_test_init.h" GBLREF gd_region *ftok_sem_reg; error_def(ERR_CRITSEMFAIL); error_def(ERR_FTOKERR); error_def(ERR_MAXSEMGETRETRY); error_def(ERR_SEMWT2LONG); #define MAX_SEM_DSE_WT (MILLISECS_IN_SEC * (30 / 2)) /* Actually 30 seconds before giving up - two semops with 15 second */ #define MAX_SEM_WT (MILLISECS_IN_SEC * (60 / 2)) /* Actually 60 seconds before giving up - two semops with 30 second */ /* If running in-house we want to debug live semop hangs. So, we will be continuing to hang until we get a successful semop with * stack traces taken every MAX_SEM_DSE_WT/MAX_SEM_WT seconds. */ #define MAX_SEMOP_TRYCNT 2 /* effective wait time - 30 seconds for DSE and 1 minute for other images */ #define MAX_SEMOP_DBG_TRYCNT 604800 /* effective wait time - 3.5 days for DSE and 1 week for other images */ #define OLD_VERSION_SEM_PER_SET 2 #define ISSUE_CRITSEMFAIL_AND_RETURN(REG, FAILED_OP, ERRNO) \ { \ gtm_putmsg_csa(CSA_ARG(REG2CSA(REG)) VARLSTCNT(4) ERR_CRITSEMFAIL, 2, DB_LEN_STR(REG)); \ gtm_putmsg_csa(CSA_ARG(REG2CSA(REG)) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL(FAILED_OP), CALLFROM, ERRNO); \ return FALSE; \ } #define CANCEL_TIMER_AND_RETURN_SUCCESS(REG) \ { \ cancel_timer((TID)semwt2long_handler); \ RETURN_SUCCESS(REG); \ } #define RETURN_SUCCESS(REG) \ { \ ftok_sem_reg = REG; \ udi->grabbed_ftok_sem = TRUE; \ return TRUE; \ } boolean_t ftok_sem_get_common(gd_region *reg, boolean_t incr_cnt, int project_id, boolean_t immediate, boolean_t *stacktrace_time, boolean_t *timedout, semwait_status_t *retstat, boolean_t *bypass, boolean_t *ftok_counter_halted) { int status = SS_NORMAL, save_errno; int ftok_sopcnt, sem_pid; uint4 lcnt, loopcnt; unix_db_info *udi; union semun semarg; sgmnt_addrs *csa; node_local_ptr_t cnl; boolean_t shared_mem_available, sem_known_removed; int4 lcl_ftok_ops_index; key_t ftokid; struct sembuf ftok_sop[3]; char *msgstr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; udi = FILE_INFO(reg); assert(!udi->grabbed_ftok_sem && !udi->grabbed_access_sem); assert(NULL == ftok_sem_reg); if (-1 == (udi->key = FTOK(udi->fn, project_id))) RETURN_SEMWAIT_FAILURE(retstat, errno, op_ftok, 0, ERR_FTOKERR, 0); /* First try is always IPC_NOWAIT */ SET_GTM_SOP_ARRAY(ftok_sop, ftok_sopcnt, incr_cnt, (SEM_UNDO | IPC_NOWAIT)); /* The following loop deals with the possibility that the semaphores can be deleted by someone else AFTER a successful * semget but BEFORE semop locks it, in which case we should retry. */ *ftok_counter_halted = FALSE; sem_known_removed = FALSE; for (lcnt = 0; MAX_SEMGET_RETRIES > lcnt; lcnt++) { /* Try to find an existing sem if we haven't already discovered that there isn't one. */ if (!sem_known_removed && (INVALID_SEMID == (ftokid = udi->ftok_semid = semget(udi->key, FTOK_SEM_PER_ID, RWDALL)))) { save_errno = errno; if (ENOENT == save_errno) sem_known_removed = TRUE; else RETURN_SEMWAIT_FAILURE(retstat, save_errno, op_semget, 0, ERR_CRITSEMFAIL, 0); } /* If we found there is no sem, create and initialize one. */ if (sem_known_removed) { if (INVALID_SEMID == (ftokid = udi->ftok_semid = semget(udi->key, FTOK_SEM_PER_ID, RWDALL | IPC_CREAT))) { save_errno = errno; RETURN_SEMWAIT_FAILURE(retstat, save_errno, op_semget, 0, ERR_CRITSEMFAIL, 0); } sem_known_removed = FALSE; SET_GTM_ID_SEM(ftokid, status); /* Set 3rd semaphore's value to GTM_ID = 43 */ if (-1 == status) { save_errno = errno; if (SEM_REMOVED(save_errno)) { /* start afresh for next iteration of for loop with new semid and initial operations */ *ftok_counter_halted = FALSE; SET_GTM_SOP_ARRAY(ftok_sop, ftok_sopcnt, incr_cnt, (SEM_UNDO | IPC_NOWAIT)); sem_known_removed = TRUE; continue; } RETURN_SEMWAIT_FAILURE(retstat, save_errno, op_semctl, 0, ERR_CRITSEMFAIL, 0); } } /* First try is always non-blocking */ SEMOP(ftokid, ftok_sop, ftok_sopcnt, status, NO_WAIT); if (-1 != status) { udi->counter_ftok_incremented = (FTOK_SOPCNT_NO_INCR_COUNTER != ftok_sopcnt); /* Input parameter *bypass could be OK_TO_BYPASS_FALSE or OK_TO_BYPASS_TRUE (for "do_blocking_semop" call). * But if we are returning without going that path, reset "*bypass" to reflect no bypass happened. */ *bypass = FALSE; RETURN_SUCCESS(reg); } save_errno = errno; assert(EINTR != save_errno); if (ERANGE == save_errno) { /* We have no access to file header to check so just assume qdbrundown is set in the file header. * If it turns out to be FALSE, after we read the file header, we will issue an error */ assert(!*ftok_counter_halted); *ftok_counter_halted = TRUE; ftok_sopcnt = FTOK_SOPCNT_NO_INCR_COUNTER; /* Ignore increment operation */ lcnt--; /* Do not count this attempt */ continue; } if (immediate) RETURN_SEMWAIT_FAILURE(retstat, save_errno, op_semop, 0, ERR_CRITSEMFAIL, 0); if (EAGAIN == save_errno) { /* someone else is holding it */ if (NO_SEMWAIT_ON_EAGAIN == TREF(dbinit_max_delta_secs)) { sem_pid = semctl(ftokid, DB_CONTROL_SEM, GETPID); if (-1 != sem_pid) RETURN_SEMWAIT_FAILURE(retstat, 0, op_invalid_sem_syscall, ERR_SEMWT2LONG, 0, sem_pid); save_errno = errno; /* fall-through */ } else if (do_blocking_semop(ftokid, gtm_ftok_sem, stacktrace_time, timedout, retstat, reg, bypass, ftok_counter_halted, incr_cnt)) { /* ftok_counter_halted and bypass set by "do_blocking_semop" */ udi->counter_ftok_incremented = incr_cnt && !(*ftok_counter_halted); if (*bypass) return TRUE; else RETURN_SUCCESS(reg); } else if (!SEM_REMOVED(retstat->save_errno)) return FALSE; /* retstat will already have the necessary error information */ save_errno = retstat->save_errno; /* some other error. Fall-through */ } if (SEM_REMOVED(save_errno)) { /* start afresh for next iteration of for loop with new semid and ftok_sopcnt */ *ftok_counter_halted = FALSE; SET_GTM_SOP_ARRAY(ftok_sop, ftok_sopcnt, incr_cnt, (SEM_UNDO | IPC_NOWAIT)); sem_known_removed = TRUE; continue; } assert(EINTR != save_errno); RETURN_SEMWAIT_FAILURE(retstat, save_errno, op_semctl_or_semop, 0, ERR_CRITSEMFAIL, 0); } assert(FALSE); RETURN_SEMWAIT_FAILURE(retstat, 0, op_invalid_sem_syscall, 0, ERR_MAXSEMGETRETRY, 0); } fis-gtm-V7.0-005/sr_unix/ftok_sem_incrcnt.c0000644000032200000250000001035714342376327017550 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2017 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 #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_instance.h" #include "repl_inst_ftok_counter_halted.h" #include "ftok_sem_incrcnt.h" #include "gtm_semutils.h" #include "eintr_wrapper_semop.h" #include "gtmmsg.h" GBLREF gd_region *ftok_sem_reg; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF boolean_t is_src_server; GBLREF boolean_t is_rcvr_server; error_def(ERR_CRITSEMFAIL); /* * Description: * Assumes that ftok semaphore id already exists. Increment only the COUNTER SEMAPHORE in that semaphore set. * Parameters: * reg : Regions structure * Return Value: TRUE, if succsessful. *ftok_counter_halted contains whether counter increment happened or not * FALSE, if fails. */ boolean_t ftok_sem_incrcnt(gd_region *reg, const char *file_type_str, boolean_t *ftok_counter_halted) { int save_errno, status; unix_db_info *udi; sgmnt_addrs *csa; struct sembuf ftok_sop; repl_inst_hdr repl_instance; boolean_t issue_error; assert(NULL != reg); assert(NULL == ftok_sem_reg); /* assert that we never hold more than one FTOK semaphore at any point in time */ /* For now, the only callers to "ftok_sem_incrcnt" are for the replication instance file and not for the database. * Assert this as it is relied upon by the "ERANGE" code below. */ assert(!MEMCMP_LIT(file_type_str, FILE_TYPE_REPLINST)); assert((NULL != jnlpool) && (reg == jnlpool->jnlpool_dummy_reg)); /* this is assumed by the code below */ udi = FILE_INFO(reg); csa = &udi->s_addrs; assert(!csa->now_crit); assert(INVALID_SEMID != udi->ftok_semid); ftok_sop.sem_num = DB_COUNTER_SEM; ftok_sop.sem_op = DB_COUNTER_SEM_INCR; /* increment counter */ ftok_sop.sem_flg = SEM_UNDO; SEMOP(udi->ftok_semid, (&ftok_sop), 1, status, NO_WAIT); if (-1 == status) /* We couldn't increment it in one shot -- see if we already have it */ { save_errno = errno; udi->counter_ftok_incremented = FALSE; issue_error = TRUE; if (ERANGE == save_errno) { /* "repl_inst_read" and "repl_inst_write" (invoked from "repl_inst_ftok_counter_halted") * rely on the caller holding the ftok semaphore. Although we dont hold it in this case, * if we are the source server (is_src_server) our caller (gtmsource.c) would have ensured * there is a parent pid that is holding the ftok and waiting for us to finish this counter * increment. Therefore steal the ftok semaphore temporarily for the assert. If we are the * receiver server (is_rcvr_server) our caller (gtmrecv.c) does not ensure this so grab the * ftok in that case. Those are the only two possibilities for the caller as asserted below. */ assert(is_src_server || is_rcvr_server); assert(!udi->grabbed_ftok_sem); DEBUG_ONLY(if (is_src_server) udi->grabbed_ftok_sem = TRUE;) if (is_rcvr_server) repl_inst_ftok_sem_lock(); repl_inst_read(udi->fn, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr)); if (repl_instance.qdbrundown) { issue_error = FALSE; if (!jnlpool->jnlpool_ctl->ftok_counter_halted) repl_inst_ftok_counter_halted(udi); } if (is_rcvr_server) repl_inst_ftok_sem_release(); DEBUG_ONLY(if (is_src_server) udi->grabbed_ftok_sem = FALSE;) } if (issue_error) { gtm_putmsg_csa(CSA_ARG(REG2CSA(reg)) VARLSTCNT(4) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg)); gtm_putmsg_csa(CSA_ARG(REG2CSA(reg)) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("semop"), CALLFROM, save_errno); *ftok_counter_halted = FALSE; return FALSE; } } else udi->counter_ftok_incremented = TRUE; *ftok_counter_halted = !udi->counter_ftok_incremented; return TRUE; } fis-gtm-V7.0-005/sr_unix/ftok_sem_incrcnt.h0000644000032200000250000000133114342376327017545 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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. * * * ****************************************************************/ #ifndef FTOK_SEM_INCRCNT_INCLUDED #define FTOK_SEM_INCRCNT_INCLUDED boolean_t ftok_sem_incrcnt(gd_region *reg, const char *file_type_str, boolean_t *ftok_counter_halted); #endif /* FTOK_SEM_INCRCNT_INCLUDED */ fis-gtm-V7.0-005/sr_unix/ftok_sems.c0000644000032200000250000003213314342376327016207 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_ipc.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "gtm_stdlib.h" #include "gtm_fcntl.h" #include "gtm_stdio.h" #include "gtm_stat.h" #include "gtm_signal.h" /* for kill(), SIGTERM, SIGQUIT */ #include #include #include #include #include #include "gtm_sem.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtm_c_stack_trace.h" #include "eintr_wrapper_semop.h" #include "eintr_wrappers.h" #include "mu_rndwn_file.h" #include "error.h" #include "io.h" #include "gt_timer.h" #include "iosp.h" #include "gtmio.h" #include "gtmimagename.h" #include "do_semop.h" #include "ipcrmid.h" #include "gtmmsg.h" #include "util.h" #include "semwt2long_handler.h" #include "repl_sem.h" #include "jnl.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" #include "gtm_semutils.h" #include "ftok_sems.h" #include "wbox_test_init.h" GBLREF uint4 process_id; GBLREF gd_region *ftok_sem_reg; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnl_gbls_t jgbl; error_def(ERR_CRITSEMFAIL); error_def(ERR_FTOKERR); error_def(ERR_MAXSEMGETRETRY); error_def(ERR_SEMKEYINUSE); error_def(ERR_SEMWT2LONG); error_def(ERR_SYSCALL); #define MAX_SEM_DSE_WT (MILLISECS_IN_SEC * (30 / 2)) /* Actually 30 seconds before giving up - two semops with 15 second */ #define MAX_SEM_WT (MILLISECS_IN_SEC * (60 / 2)) /* Actually 60 seconds before giving up - two semops with 30 second */ /* If running in-house we want to debug live semop hangs. So, we will be continuing to hang until we get a successful semop with * stack traces taken every MAX_SEM_DSE_WT/MAX_SEM_WT seconds. */ #define MAX_SEMOP_TRYCNT 2 /* effective wait time - 30 seconds for DSE and 1 minute for other images */ #define MAX_SEMOP_DBG_TRYCNT 604800 /* effective wait time - 3.5 days for DSE and 1 week for other images */ #define OLD_VERSION_SEM_PER_SET 2 #define ISSUE_CRITSEMFAIL_AND_RETURN(REG, FAILED_OP, ERRNO) \ { \ gtm_putmsg_csa(CSA_ARG(REG2CSA(REG)) VARLSTCNT(4) ERR_CRITSEMFAIL, 2, DB_LEN_STR(REG)); \ gtm_putmsg_csa(CSA_ARG(REG2CSA(REG)) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL(FAILED_OP), CALLFROM, ERRNO); \ return FALSE; \ } #define CANCEL_TIMER_AND_RETURN_SUCCESS(REG) \ { \ cancel_timer((TID)semwt2long_handler); \ RETURN_SUCCESS(REG); \ } #define RETURN_SUCCESS(REG) \ MBSTART { \ ftok_sem_reg = REG; \ udi->grabbed_ftok_sem = TRUE; \ return TRUE; \ } MBEND boolean_t ftok_sem_get2(gd_region *reg, boolean_t *stacktrace_time, boolean_t *timedout, semwait_status_t *retstat, boolean_t *bypass, boolean_t *ftok_counter_halted, boolean_t incr_cnt) { boolean_t immediate = FALSE; int project_id = GTM_ID; return ftok_sem_get_common(reg, incr_cnt, project_id, immediate, stacktrace_time, timedout, retstat, bypass, ftok_counter_halted); } /* * Description: * Using project_id this will find FTOK of FILE_INFO(reg)->fn. * Create semaphore set of id "ftok_semid" using that project_id, if it does not exist. * Then it will lock ftok_semid. * Parameters: * reg : Regions structure * incr_cnt : IF incr_cnt == TRUE, it will increment counter semaphore. * project_id : Project id for ftok call. * immediate : IF immediate == TRUE, it will use IPC_NOWAIT flag. * Return Value: TRUE, if succsessful * FALSE, if fails. */ boolean_t ftok_sem_get(gd_region *reg, boolean_t incr_cnt, int project_id, boolean_t immediate, boolean_t *ftok_counter_halted) { uint4 semop_wait_time; unix_db_info *udi; boolean_t stacktrace_time = FALSE, sem_timeout, bypass = FALSE, result; semwait_status_t retstat; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(reg); *ftok_counter_halted = FALSE; /* The ftok semaphore should never be requested on the replication instance file while already holding the * journal pool access semaphore as it can lead to deadlocks (the right order is get ftok semaphore first * and then get the access semaphore). The only exception is MUPIP JOURNAL -ROLLBACK -BACKWARD due to an issue * that is documented in C9F10-002759. Assert that below. */ assert(((NULL == jnlpool) || (reg != jnlpool->jnlpool_dummy_reg)) || (jgbl.mur_rollback && !jgbl.mur_options_forward) || !holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); udi = FILE_INFO(reg); assert(!udi->grabbed_ftok_sem && !udi->grabbed_access_sem); assert(NULL == ftok_sem_reg); semop_wait_time = !IS_DSE_IMAGE ? MAX_SEM_WT : MAX_SEM_DSE_WT; TIMEOUT_INIT(sem_timeout, semop_wait_time); result = ftok_sem_get_common(reg, incr_cnt, project_id, immediate, &stacktrace_time, &sem_timeout, &retstat, &bypass, ftok_counter_halted); TIMEOUT_DONE(sem_timeout); assert(!bypass); if (!result) { PRINT_SEMWAIT_ERROR(&retstat, reg, udi, "ftok"); return FALSE; } else RETURN_SUCCESS(reg); } /* * Description: * Assumes that ftok semaphore already exists. Just lock it. * Parameters: * reg : Regions structure * immediate : IF immediate == TRUE, it will use IPC_NOWAIT flag. * Return Value: TRUE, if succsessful * FALSE, if fails. */ boolean_t ftok_sem_lock(gd_region *reg, boolean_t immediate) { int semflag, save_errno, status; unix_db_info *udi; sgmnt_addrs *csa; struct sembuf ftok_sop[3]; int ftok_sopcnt; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(reg); /* The ftok semaphore should never be requested on the replication instance file while already holding the * journal pool access semaphore as it can lead to deadlocks (the right order is get ftok semaphore first * and then get the access semaphore). The only exception is MUPIP JOURNAL -ROLLBACK -BACKWARD due to an issue * that is documented in C9F10-002759. Assert that below. */ assert(((NULL == jnlpool) || (reg != jnlpool->jnlpool_dummy_reg)) || (jgbl.mur_rollback && !jgbl.mur_options_forward) || !holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); udi = FILE_INFO(reg); csa = &udi->s_addrs; /* Requests for the ftok lock on a db should always comes before requests for crit on the same db. * This is needed to avoid deadlocks. So we should never hold crit on this db while requesting the ftok lock. Assert that. */ assert(!csa->now_crit); /* The following two asserts are to ensure we never hold more than one FTOK semaphore at any point in time. The only * exception is if we were MUPIP STOPped (or kill -3ed) while having ftok_sem lock on one region and we came to rundown code * that invoked ftok_sem_lock() on a different region. Hence the process_exiting check below. In the pro version, we will * do the right thing by returning TRUE right away if udi->grabbed_ftok_sem is TRUE. */ assert(!udi->grabbed_ftok_sem || (FALSE != process_exiting)); assert((NULL == ftok_sem_reg) || (FALSE != process_exiting)); assert(INVALID_SEMID != udi->ftok_semid); ftok_sopcnt = 0; if (!udi->grabbed_ftok_sem) { /* Guarantee no one else accesses database file header while we update semid/shmid fields in the file header */ ftok_sop[0].sem_num = DB_CONTROL_SEM; ftok_sop[0].sem_op = 0; /* Wait for 0 (unlocked) */ ftok_sop[1].sem_num = DB_CONTROL_SEM; ftok_sop[1].sem_op = 1; /* Then lock it */ ftok_sopcnt = FTOK_SOPCNT_NO_INCR_COUNTER; } else return TRUE; ftok_sop[0].sem_flg = ftok_sop[1].sem_flg = ftok_sop[2].sem_flg = SEM_UNDO | IPC_NOWAIT; SEMOP(udi->ftok_semid, ftok_sop, ftok_sopcnt, status, NO_WAIT); if (-1 == status) /* We couldn't get it in one shot -- see if we already have it */ { save_errno = errno; if (EAGAIN == save_errno) { assert(process_id != semctl(udi->ftok_semid, 0, GETPID)); /* ensure that we don't hold the ftok semaphore */ if (immediate) { /* Only db_ipcs_reset passes immediate=TRUE for ftok_sem_lock. If we couldn't get the lock, return * FALSE without doing a gtm_putmsg as the process that does hold the lock will release it. */ return FALSE; } ftok_sop[0].sem_flg = ftok_sop[1].sem_flg = ftok_sop[2].sem_flg = SEM_UNDO; SEMOP(udi->ftok_semid, ftok_sop, ftok_sopcnt, status, FORCED_WAIT) if (-1 == status) /* We couldn't get it at all.. */ { save_errno = errno; GTM_SEM_CHECK_EINVAL(TREF(gtm_environment_init), save_errno, udi); ISSUE_CRITSEMFAIL_AND_RETURN(reg, "semop()/semctl()", save_errno); } } else { GTM_SEM_CHECK_EINVAL(TREF(gtm_environment_init), save_errno, udi); ISSUE_CRITSEMFAIL_AND_RETURN(reg, "semop()", save_errno); } } udi->grabbed_ftok_sem = TRUE; RETURN_SUCCESS(reg); } /* * Description: * Assumes that ftok semaphore was already locked. Now release it. * Parameters: * reg : Regions structure * IF decr_cnt == DECR_CNT_TRUE (1), it will decrement counter semaphore & remove it if needed. * IF decr_cnt == DECR_CNT_SAFE (2), it will decrement counter semaphore but not remove the semaphore. * IF immediate == TRUE, it will use IPC_NOWAIT flag. * Return Value: TRUE, if succsessful * FALSE, if fails. * NOTE: The parameter "immediate" may not be necessary. Here we remove the semaphore * or decrement the counter. We are already holding the control semaphore. * So never we need to pass IPC_NOWAIT. But we need to analyze before we change code. */ boolean_t ftok_sem_release(gd_region *reg, boolean_t decr_cnt, boolean_t immediate) { int ftok_semval, semflag, save_errno; unix_db_info *udi; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(NULL != reg); assert(DECR_CNT_SAFE != DECR_CNT_TRUE); assert(DECR_CNT_SAFE != DECR_CNT_FALSE); /* The following assert is to ensure we never hold more than one FTOK semaphore at any point in time. The only exception is * if we were MUPIP STOPped (or kill -3ed) while having ftok_sem lock on one region and we came to rundown code that invoked * ftok_sem_lock() on a different region. Hence the process_exiting check below. */ assert(reg == ftok_sem_reg || (FALSE != process_exiting)); udi = FILE_INFO(reg); assert(udi->grabbed_ftok_sem); assert(udi && INVALID_SEMID != udi->ftok_semid); /* if we dont have the ftok semaphore, return true even if decr_cnt was requested */ if (!udi->grabbed_ftok_sem) return TRUE; semflag = SEM_UNDO | (immediate ? IPC_NOWAIT : 0); if (decr_cnt) { assert(udi->counter_ftok_incremented); if (DECR_CNT_SAFE != decr_cnt) { if ((-1 == (ftok_semval = semctl(udi->ftok_semid, DB_COUNTER_SEM, GETVAL))) /* WARNING: assign */ && !((EINVAL == (save_errno = errno) || (EIDRM == save_errno)) /* WARNING: assign */ && !udi->shm_created)) /* stand-in for read_only */ { /* an mm read_only database file maps process-private, so we don't care if the semaphore is gone */ GTM_SEM_CHECK_EINVAL(TREF(gtm_environment_init), save_errno, udi); ISSUE_CRITSEMFAIL_AND_RETURN(reg, "semop()", save_errno); } /* Below checks against 0, in case already we decremented semaphore number 1 */ if (DB_COUNTER_SEM_INCR >= ftok_semval) { if (0 != sem_rmid(udi->ftok_semid)) { save_errno = errno; GTM_SEM_CHECK_EINVAL(TREF(gtm_environment_init), save_errno, udi); ISSUE_CRITSEMFAIL_AND_RETURN(reg, "sem_rmid()", save_errno); } udi->ftok_semid = INVALID_SEMID; ftok_sem_reg = NULL; udi->grabbed_ftok_sem = FALSE; udi->counter_ftok_incremented = FALSE; return TRUE; } } /* Always set IPC_NOWAIT for counter decrement. In the rare case where the counter is already zero, is it better * to handle the error than it is to wait indefinitely for another process to wake us up. */ if (0 != (save_errno = do_semop(udi->ftok_semid, DB_COUNTER_SEM, -DB_COUNTER_SEM_INCR, (SEM_UNDO | IPC_NOWAIT)))) { GTM_SEM_CHECK_EINVAL(TREF(gtm_environment_init), save_errno, udi); ISSUE_CRITSEMFAIL_AND_RETURN(reg, "semop()", save_errno); } udi->counter_ftok_incremented = FALSE; } while (TRUE) { save_errno = do_semop(udi->ftok_semid, DB_CONTROL_SEM, -1, semflag); if ((0 == save_errno) || (!udi->shm_created && ((EINVAL == save_errno) || (EIDRM == save_errno)))) break; /* either success or read_only db and indications it's already gone, in which case we don't care */ if ((NULL != jnlpool) && (reg == jnlpool->jnlpool_dummy_reg) && (EAGAIN == save_errno)) { /* journal pool ftok, in which case keep trying with more patience */ while (EAGAIN == (save_errno = do_semop(udi->ftok_semid, DB_CONTROL_SEM, -1, SEM_UNDO))) ; if (0 == save_errno) break; } GTM_SEM_CHECK_EINVAL(TREF(gtm_environment_init), save_errno, udi); ISSUE_CRITSEMFAIL_AND_RETURN(reg, "semop()", save_errno); } udi->grabbed_ftok_sem = FALSE; ftok_sem_reg = NULL; return TRUE; } fis-gtm-V7.0-005/sr_unix/ftok_sems.h0000755000032200000250000000266414342376327016225 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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. * * * ****************************************************************/ #ifndef FTOK_SEMS_INCLUDED #define FTOK_SEMS_INCLUDED #include "gtm_semutils.h" #define DECR_CNT_FALSE FALSE #define DECR_CNT_TRUE TRUE #define DECR_CNT_SAFE 2 boolean_t ftok_sem_get2(gd_region *reg, boolean_t *stacktrace_time, boolean_t *timedout, semwait_status_t *retstat, boolean_t *bypass, boolean_t *ftok_counter_halted, boolean_t incr_cnt); boolean_t ftok_sem_get(gd_region *reg, boolean_t incr_cnt, int project_id, boolean_t immediate, boolean_t *ftok_counter_halted); boolean_t ftok_sem_get_common(gd_region *reg, boolean_t incr_cnt, int project_id, boolean_t immediate, boolean_t *stacktrace_time, boolean_t *timedout, semwait_status_t *retstat, boolean_t *bypass, boolean_t *ftok_counter_halted); boolean_t ftok_sem_lock(gd_region *reg, boolean_t immediate); boolean_t ftok_sem_release(gd_region *reg, boolean_t decr_cnt, boolean_t immediate); #define MAX_SEMGET_RETRIES 100 #endif /* FTOK_SEMS_INCLUDED */ fis-gtm-V7.0-005/sr_unix/gbldirnam.h0000644000032200000250000000163114342376327016160 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #define GDE_LABEL_SIZE 13 #define GDE_LABEL_NUM 1 /* Note, GDE_LABEL_LITERAL must be maintained in gdeinit.m if changes are made here */ /* The reference file for 64bittn/vermismatch expects this value so must be kept in sync */ /* Also change at least the assert in create_dummy_gbldir.c */ #define GDE_LABEL_LITERAL GTM64_ONLY("GTCGBDUNX115") NON_GTM64_ONLY("GTCGBDUNX015") #define DEF_GDR_EXT "*.gld" fis-gtm-V7.0-005/sr_unix/gcall.h0000755000032200000250000000154614342376327015313 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GCALL_ARGS_H_INCLUDED #define GCALL_ARGS_H_INCLUDED #include "mdef.h" typedef struct gcall_args_struct { intszofptr_t callargs; intszofptr_t truth; intszofptr_t retval; intszofptr_t mask; intszofptr_t argcnt; mval *argval[MAX_ACTUALS]; } gcall_args; void ojchildparms(job_params_type *jparms, gcall_args *g_args, mval *arglst); #endif fis-gtm-V7.0-005/sr_unix/gdedefaults.gtc0000644000032200000250000000105214342376327017033 0ustar librarygtctemplate -segment -access_method=mm -block_size=4096 -allocation=5000 -extension=10000 -global_buffer_count=1000 template -segment -access_method=bg -block_size=4096 -allocation=5000 -extension=10000 -global_buffer_count=1000 template -region -stdnull -key_size=255 -record_size=4080 -journal=before change -segment DEFAULT -block_size=4096 -allocation=5000 -extension=10000 -global_buffer_count=1000 -file_name=$gtmdir/$gtmver/g/gtm.dat change -region DEFAULT -stdnull -key_size=255 -record_size=4080 -journal=(before,file="$gtmdir/$gtmver/g/gtm.mjl") fis-gtm-V7.0-005/sr_unix/gdeget.m0000755000032200000250000010424214342376327015472 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2006-2020 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; gdeget: ;read in an existing GD or create a default LOAD n abs,contents,rel,xregs,xsegs,reglist,map,$et,ptrsize i debug s $et="b" e s $et="g ABORT^GDE:($p($p($zs,"","",3),""-"")'=""%GDE"") u io w !,$p($zs,"","",3,999),! d GETOUT^GDEEXIT zg 0" ; if zchset is UTF-8 open in raw mode to avoid BADCHAR errors ; For OS390 aka z/OS, use BINARY mode s abs=1,update=0,chset=$SELECT($ZV["OS390":"BINARY",$ZCHSET="UTF-8":"M",1:"") o file:(exc="g badfile^"_$t(+0):rewind:recordsize=SIZEOF("dsk_blk"):readonly:fixed:blocksize=SIZEOF("dsk_blk"):ichset=chset) u file r rec i debug u @useio ; header s label=$ze(rec,1,12) s olabel=label s mach=$p($zver," ",4) n gldfmt i ($ze(label,1,9)'="GTCGBDUNX") zm gdeerr("GDUNKNFMT"):file,gdeerr("INPINTEG") s gldfmt=+$ze(label,11,12) ; 10th byte could be 1 indicating 64-bit platform so ignore that for format check s seghasencrflag=FALSE i gldfmt>5 s seghasencrflag=TRUE s reghasv550fields=FALSE i gldfmt>6 s reghasv550fields=TRUE s reghasv600fields=FALSE i gldfmt>7 s reghasv600fields=TRUE s v6312=0 if (label="GTCGBDUNX014")!(label="GTCGBDUNX114") set label=hdrlab,v6312=1,update=1 ;autoconvert if (v6312=1) new SIZEOF do v6312init s v6312=0 s v6311=0 if (label="GTCGBDUNX013")!(label="GTCGBDUNX113") set label=hdrlab,v6311=1,update=1 ;autoconvert if (v6311=1) new SIZEOF do v6312init s v6311=0 s v6310=0 if (label="GTCGBDUNX012")!(label="GTCGBDUNX112") set label=hdrlab,v6310=1,update=1 ;autoconvert if (v6310=1) new SIZEOF do v6310init set v631=0 i (label="GTCGBDUNX011")!(label="GTCGBDUNX111") s label=hdrlab,v631=1,update=1 ;autoconvert i (v631=1) n SIZEOF d v631init s v63a=0 i (label="GTCGBDUNX010")!(label="GTCGBDUNX110") s label=hdrlab,v63a=1,update=1 ;autoconvert i (v63a=1) n SIZEOF d v63ainit s v621=0 i (label="GTCGBDUNX009")!(label="GTCGBDUNX109") s label=hdrlab,v621=1,update=1 ;autoconvert i (v621=1) n SIZEOF d v621init s v600=0 i (label="GTCGBDUNX008")!(label="GTCGBDUNX108") s label=hdrlab,v600=1,update=1 ;autoconvert i (v600=1) n SIZEOF d v600init s v550=0 i (label="GTCGBDUNX007")!(label="GTCGBDUNX107") s label=hdrlab,v550=1,update=1 ;autoconvert i (v550=1) n SIZEOF d v550init s v542=0 i (label="GTCGBLDIR009")!(label="GTCGBDUNX006")!(label="GTCGBDUNX106") s label=hdrlab,v542=1,update=1 ;autoconvert i (v542=1) n SIZEOF d v542init s v534=0 i (label="GTCGBDUNX105")!((label="GTCGBDUNX005")&(mach="IA64")) s label=hdrlab,v534=1,update=1 ;autoconvert i (v534=1) n SIZEOF d v534init s v533=0 i (gtm64=TRUE),(label="GTCGBDUNX006") s label=hdrlab,v533=1,update=1 ;autoconvert i (v533=1) n SIZEOF d v533init s v532=0 i (label="GTCGBLDIR008")!(label="GTCGBDUNX005") s label=hdrlab,v532=1,update=1 ;autoconvert i (v532=1),(mach'="IA64") n gtm64 s gtm64=FALSE i (v532=1),(mach'="IA64") n SIZEOF d v532init s v5ft1=0 i (label="GTCGBLDIR008")!(label="GTCGBDUNX004") s label=hdrlab,v5ft1=1,update=1 ;autoconvert i v5ft1=1 n gtm64 s gtm64=FALSE i v5ft1=1 n SIZEOF d v5ft1init s v44=0 i (label="GTCGBLDIR007")!(label="GTCGBDUNX003") s label=hdrlab,v44=1,update=1 ;autoconvert i v44=1 n gtm64 s gtm64=FALSE i v44=1 n MAXNAMLN,MAXSEGLN,MAXREGLN,SIZEOF d v44init s v30=0 i (label="GTCGBLDIR006")!(label="GTCGBDUNX002") s label=hdrlab,v30=4,update=1 ;autoconvert i v30=4 n gtm64 s gtm64=FALSE i label'=hdrlab zm gdeerr("GDUNKNFMT"):file,gdeerr("INPINTEG") s filesize=$$bin2num($ze(rec,13,16)) s abs=abs+SIZEOF("gd_header") ; contents s ptrsize=$s((gtm64=TRUE):8,1:4) i $ze(rec,abs,abs+ptrsize-1)'=$tr($j("",ptrsize)," ",ZERO) zm gdeerr("INPINTEG") ; gd_addr.local_locks s abs=abs+ptrsize s contents("maxrecsize")=$$bin2num($ze(rec,abs,abs+3)),abs=abs+4 n cntsize s cntsize=$s((gldfmt>8):4,1:2) ; counters are 4-bytes since V6.1 s contents("mapcnt")=$$bin2num($ze(rec,abs,abs+cntsize-1)),abs=abs+cntsize s contents("regioncnt")=$$bin2num($ze(rec,abs,abs+cntsize-1)),abs=abs+cntsize s contents("segmentcnt")=$$bin2num($ze(rec,abs,abs+cntsize-1)),abs=abs+cntsize i '(gldfmt>8) d . i $ze(rec,abs,abs+cntsize-1)'=$tr($j("",cntsize)," ",ZERO) zm gdeerr("INPINTEG") ; filler . s abs=abs+cntsize . i gtm64=TRUE s abs=abs+4 ; including 4-byte padding e d . s contents("gblnamecnt")=$$bin2num($ze(rec,abs,abs+cntsize-1)),abs=abs+cntsize . s contents("varmapslen")=$$bin2num($ze(rec,abs,abs+cntsize-1)),abs=abs+cntsize s contents("maps")=$$bin2num($ze(rec,abs,abs+ptrsize-1)),abs=abs+ptrsize s contents("regions")=$$bin2num($ze(rec,abs,abs+ptrsize-1)),abs=abs+ptrsize s contents("segments")=$$bin2num($ze(rec,abs,abs+ptrsize-1)),abs=abs+ptrsize i (gldfmt>8) s contents("gblnames")=$$bin2num($ze(rec,abs,abs+ptrsize-1)),abs=abs+ptrsize i (gldfmt>11) s contents("inst")=$$bin2num($ze(rec,abs,abs+ptrsize-1)),abs=abs+ptrsize s abs=abs+(3*ptrsize) ;skip link, tab_ptr and id pointers s contents("end")=$$bin2num($ze(rec,abs,abs+ptrsize-1)),abs=abs+ptrsize i (gldfmt>8) s abs=abs+16 ; reserved for runtime fillers i contents("regioncnt")'=contents("segmentcnt") zm gdeerr("INPINTEG") i contents("regioncnt")-1>contents("mapcnt") zm gdeerr("INPINTEG") ; verify offsets i abs'=(SIZEOF("gd_header")+SIZEOF("gd_contents")+1) zm gdeerr("INPINTEG") s x=contents("maps") i x+1'=(abs-SIZEOF("gd_header")) zm gdeerr("INPINTEG") s x=x+(contents("mapcnt")*SIZEOF("gd_map")) i (gldfmt>8) s x=x+contents("varmapslen") ; add variable maps section too if available i x'=contents("regions") zm gdeerr("INPINTEG") s x=x+(contents("regioncnt")*SIZEOF("gd_region")) i x'=contents("segments") zm gdeerr("INPINTEG") s x=x+(contents("segmentcnt")*(SIZEOF("gd_segment")-v30)) i (gldfmt>8) d . i x'=contents("gblnames") zm gdeerr("INPINTEG") . s x=x+(contents("gblnamecnt")*(SIZEOF("gd_gblname"))) i (gldfmt>11),(contents("inst")>0) d . i x'=contents("inst") zm gdeerr("INPINTEG") . s x=x+(SIZEOF("gd_inst_info")) i x'=contents("end") zm gdeerr("INPINTEG") s rel=abs ; maps - verify that mapped regions and regions are 1-to-1 k reglist i '(gldfmt>8) d . f i=1:1:contents("mapcnt") d mapPreV61 e d . n maparray,tmpabs,newabs . f i=1:1:contents("mapcnt") d mapfixed(i) ; read through fixed section of MAPS . s tmpabs=abs . f i=1:1:contents("mapcnt") d mapvariable(i) ; read through variable section of MAPS . s newabs=(((abs-1)+(ptrsize-1))\ptrsize*ptrsize)+1 ; make "abs" 8-byte aligned for 64bit platforms (and 4-byte for 32-bit) . s rel=rel+(newabs-abs) ; adjust "rel" to take "abs"-alignment-adjustment into account . s abs=newabs . i (abs-tmpabs)'=contents("varmapslen") zm gdeerr("INPINTEG") s s="" f i=1:1:contents("regioncnt") s s=$o(reglist(s)) i $zl($o(reglist(s))) zm gdeerr("INPINTEG") i i'=contents("regioncnt") zm gdeerr("INPINTEG") ; regions k regs,xregs s regs=0 f i=1:1:contents("regioncnt") d region i regs'=contents("regioncnt") zm gdeerr("INPINTEG") ; segments k segs,xsegs s segs=0 f i=1:1:contents("segmentcnt") d segment i segs'=contents("segmentcnt") zm gdeerr("INPINTEG") ; gblnames i (gldfmt>8) d . k gnams s gnams=0 . f i=1:1:contents("gblnamecnt") d gblname(i) e s gnams=0 ; wait until "gnams" is setup before checking maps, as "gnams" is used in case of subscripted gvns in map entries zm:'$$MAP2NAM^GDEMAP(.map) gdeerr("INPINTEG") i (gldfmt'>10) do . ; remove any %Y* name mappings in old gld (unsubscripted OR subscripted) as documented . n s s s="" f s s=$o(nams(s)) q:s="" i $ze(s,1,2)="%Y" k nams(s) i $incr(nams,-1) ; instance i (gldfmt>11) d . k inst s inst=0 . i contents("inst")>0 d inst e s inst=0 ; template access method s tmpacc=$$gderead(4) i accmeth'[("\"_tmpacc) zm gdeerr("INPINTEG") ; templates k tmpreg,tmpseg d cretmps i (reghasv550fields=TRUE) d tmpreg("ALIGNSIZE") s s="ALLOCATION" d tmpreg(s) if (gldfmt>10) d tmpreg("AUTODB") s tmpreg("STATS")='(tmpreg("AUTODB")\2#2),tmpreg("AUTODB")=tmpreg("AUTODB")#2 i (reghasv550fields=TRUE) d tmpreg("AUTOSWITCHLIMIT") f s="BEFORE_IMAGE","BUFFER_SIZE" d tmpreg(s) i tmpreg("BUFFER_SIZE")9) d tmpreg("EPOCHTAPER") i (reghasv550fields=TRUE) d tmpreg("EPOCH_INTERVAL") f s="EXTENSION","FILE_NAME" d tmpreg(s) i (reghasv600fields=TRUE) d tmpreg("INST_FREEZE_ON_ERROR") f s="JOURNAL","KEY_SIZE" d tmpreg(s) if (gldfmt>10) d tmpreg("LOCK_CRIT") s tmpreg("LOCK_CRIT")='tmpreg("LOCK_CRIT") f s="NULL_SUBSCRIPTS" d tmpreg(s) i (reghasv600fields=TRUE) d tmpreg("QDBRUNDOWN") f s="RECORD_SIZE" d tmpreg(s) ; need to handle versioning i 'v44&'v30 d tmpreg("STDNULLCOLL") i (reghasv550fields=TRUE) d tmpreg("SYNC_IO") i (reghasv550fields=TRUE) d tmpreg("YIELD_LIMIT") ; minimum allocation/extension was changed in V54001; check if default current value is lower and if so adjust it i tmpreg("ALLOCATION")10) d tmpseg(am,"ASYNCIO") . f s="BLOCK_SIZE","BUCKET_SIZE","DEFER" d tmpseg(am,s) . i (gldfmt>9) d tmpseg(am,"DEFER_ALLOCATE") . i (seghasencrflag=TRUE) d tmpseg(am,"ENCRYPTION_FLAG") . f s="EXTENSION_COUNT","FILE_TYPE" d tmpseg(am,s) . i (gldfmt>13) d tmpseg(am,"FULLBLKWRT") . f s="GLOBAL_BUFFER_COUNT","LOCK_SPACE" d tmpseg(am,s) . i (gldfmt>8) d tmpseg(am,"MUTEX_SLOTS") . i 'v30 d tmpseg(am,"RESERVED_BYTES") ;autoconvert, can be condensed someday . d tmpseg(am,"WINDOW_SIZE") . c file ; resolve s s="" f s s=$o(nams(s)) q:'$zl(s) zm:'$d(xregs(nams(s))) gdeerr("INPINTEG") s nams(s)=xregs(nams(s)) d:nams(s)?1L.E . i $i(nams,-1),$d(regs(nams(s))),$i(regs,-1),$i(segs,-1) k regs(nams(s)),segs(nams(s)) . k nams(s) f s s=$o(regs(s)) q:'$zl(s) zm:'$d(xsegs(regs(s,"DYNAMIC_SEGMENT"))) gdeerr("INPINTEG") d . s regs(s,"DYNAMIC_SEGMENT")=xsegs(regs(s,"DYNAMIC_SEGMENT")) f s s=$o(segs(s)) q:'$zl(s) s am=segs(s,"ACCESS_METHOD") d . s x="" . f s x=$o(segs(s,x)) q:x="" d . . i x'="FILE_NAME",'$zl(tmpseg(am,x)) zm:segs(s,x) gdeerr("INPINTEG") s segs(s,x)="" ; fall through ! verify: s x=$$ALL^GDEVERIF i 'x zm gdeerr("INPINTEG") q ;---------------------------------------------------------------------------------------------------------------------------------- badfile ;file access failed s:'debug $et="" u file:exc="" s abortzs=$zs zm gdeerr("GDREADERR"):file,+abortzs d GETOUT^GDEEXIT h ; bin2num:(bin) ; binary number -> number n num,i s num=0 i endian=TRUE f i=$zl(bin):-1:1 s num=$za(bin,i)*HEX($zl(bin)-i*2)+num e f i=1:1:$zl(bin) s num=$za(bin,i)*HEX(i-1*2)+num q num ; ;---------------------------------------------------------------------------------------------------------------------------------- regoffchk:(x) ; check region offset s reglist(x)="",x=x-contents("regions") i x#SIZEOF("gd_region") zm gdeerr("INPINTEG") i x\SIZEOF("gd_region")'contents("regions") zm gdeerr("INPINTEG") f q:(($zl(rec)-(rel-1))'14) s regs(s,"BUFFER_SIZE")=$$bin2num($zextract(rec,rel,rel+3)),rel=rel+4 if (gldfmt<=14) s regs(s,"BUFFER_SIZE")=$$bin2num($ze(rec,rel,rel+1)),rel=rel+2 i regs(s,"BUFFER_SIZE")14) do . if $$bin2num($zextract(rec,rel,rel+1))'=0 zm gdeerr("INPINTEG") . set rel=rel+2 ; align i $ze(rec,rel,rel+7)'=$tr($j("",8)," ",ZERO) zm gdeerr("INPINTEG") ; reserved s rel=rel+8 ; reserved i (gldfmt>8) s rel=rel+4 ; "gd_region.is_spanned" not needed by GDE i (gldfmt>10) s rel=rel+4 ; "gd_region.statsDB_reg_index" never used by GDE (generated by gdeput if needed) s regs(s,"EPOCHTAPER")=1 s regs(s,"AUTODB")=0,regs(s,"STATS")=1,regs(s,"LOCK_CRIT")=0 i (gldfmt>9) do . s regs(s,"EPOCHTAPER")=$$bin2num($ze(rec,rel)),rel=rel+1 . i (gldfmt>10) do . . s x=$$bin2num($ze(rec,rel)),regs(s,"AUTODB")=x#2,regs(s,"STATS")='(x\2#2),rel=rel+1 . . s regs(s,"LOCK_CRIT")='$$bin2num($ze(rec,rel)),rel=rel+1 . . s rel=rel+34 ; reserved for runtime fillers . s rel=rel+11 ; reserved for runtime fillers e i (gldfmt=9) s rel=rel+12 ; reserved for runtime fillers s rel=rel+SIZEOF("gd_region_padding") ; padding s abs=abs+SIZEOF("gd_region") q segment: i $zl(rec)-(rel-1)<(SIZEOF("gd_segment")-v30) d nextrec ; autoconvert s segs=segs+1 s x=$$bin2num($ze(rec,rel+SIZEOF("am_offset")-v30,rel+SIZEOF("am_offset")-v30+3)) ; autoconvert s am=$s(x=1:"BG",x=2:"MM",x=4:"USER",1:"ERROR") i am="ERROR" zm gdeerr("INPINTEG") s l=$$bin2num($ze(rec,rel,rel+1)),rel=rel+2 s s=$ze(rec,rel,rel+l-1),rel=rel+MAXSEGLN,xsegs(abs-1-SIZEOF("gd_header"))=s s segs(s,"ACCESS_METHOD")=am s l=$$bin2num($ze(rec,rel,rel+1)),rel=rel+2 s segs(s,"FILE_NAME")=$ze(rec,rel,rel+l-1),rel=rel+SIZEOF("file_spec") s segs(s,"BLOCK_SIZE")=$$bin2num($ze(rec,rel,rel+1)),rel=rel+2 s segs(s,"FULLBLKWRT")=0 if (gldfmt>12) do . if gldfmt=13 do . . if $$bin2num($zextract(rec,rel,rel+1))'=0 zm gdeerr("INPINTEG") . . else set rel=rel+2 . else set segs(s,"FULLBLKWRT")=$$bin2num($zextract(rec,rel,rel+1)),rel=rel+2 . set segs(s,"EXTENSION_COUNT")=$$bin2num($zextract(rec,rel,rel+3)),rel=rel+4 if (gldfmt<=12) set segs(s,"EXTENSION_COUNT")=$$bin2num($zextract(rec,rel,rel+1)),rel=rel+2 s segs(s,"ALLOCATION")=$$bin2num($ze(rec,rel,rel+3)),rel=rel+4 i $ze(rec,rel,rel+ptrsize-1)'=$tr($j("",ptrsize)," ",ZERO) zm gdeerr("INPINTEG") ; reserved for clb if (gldfmt>12) set rel=rel+$select((gtm64=TRUE):8,1:4) ; padding else set rel=rel+$select((gtm64=TRUE):12,1:4) i $ze(rec,rel,rel+3)'=".DAT" zm gdeerr("INPINTEG") s rel=rel+4 s segs(s,"DEFER")=$$bin2num($ze(rec,rel)) s rel=rel+1 s x=$$bin2num($ze(rec,rel)),rel=rel+1 s segs(s,"FILE_TYPE")=$s(x=0:"DYNAMIC",1:"ERROR") i segs(s,"FILE_TYPE")="ERROR" zm gdeerr("INPINTEG") s segs(s,"BUCKET_SIZE")=$$bin2num($ze(rec,rel)) s rel=rel+1 s segs(s,"WINDOW_SIZE")=$$bin2num($ze(rec,rel)) s rel=rel+1 s segs(s,"LOCK_SPACE")=$$bin2num($ze(rec,rel,rel+3)),rel=rel+4 s segs(s,"GLOBAL_BUFFER_COUNT")=$$bin2num($ze(rec,rel,rel+3)),rel=rel+4 i 'v30 d . s segs(s,"RESERVED_BYTES")=$$bin2num($ze(rec,rel,rel+3)),rel=rel+4 ;autoconvert e s segs(s,"RESERVED_BYTES")=0 i (gldfmt>8) s segs(s,"MUTEX_SLOTS")=$$bin2num($ze(rec,rel,rel+3)),rel=rel+4 e s segs(s,"MUTEX_SLOTS")=defseg("MUTEX_SLOTS") i (gldfmt>9) s segs(s,"DEFER_ALLOCATE")=$$bin2num($ze(rec,rel,rel+3)),rel=rel+4 e s segs(s,"DEFER_ALLOCATE")=defseg("DEFER_ALLOCATE") s rel=rel+4 ; access method already processed i (gldfmt=9)&(gtm64=TRUE) s rel=rel+4 ; 4-byte filler i $ze(rec,rel,rel+ptrsize-1)'=$tr($j("",ptrsize)," ",ZERO) zm gdeerr("INPINTEG") ; file_cntl pointer s rel=rel+ptrsize i $ze(rec,rel,rel+ptrsize-1)'=$tr($j("",ptrsize)," ",ZERO) zm gdeerr("INPINTEG") ; repl_list pointer s rel=rel+ptrsize ; If the gld file has the encrytion flag, read it. Also read only if ; the current platform is encrpytion enabled. Otherwise default to 0 s segs(s,"ENCRYPTION_FLAG")=$S(((encsupportedplat=TRUE)&(seghasencrflag=TRUE)):$$bin2num($ze(rec,rel,rel+3)),1:0) i (seghasencrflag=TRUE) s rel=rel+4 i (gldfmt>10) s segs(s,"ASYNCIO")=$$bin2num($ze(rec,rel,rel+3)),rel=rel+4 e do . s segs(s,"ASYNCIO")=defseg("ASYNCIO") . if (seghasencrflag=TRUE)&(gtm64=TRUE) s rel=rel+4 ; Padding bytes for 64 bit platforms if (gldfmt>8) set rel=rel+16 ; reserved for runtime fillers s abs=abs+SIZEOF("gd_segment")-v30 q gblname:(i) n x,y i $zl(rec)-(rel-1)maxgnam("COLLATION") zm gdeerr("INPINTEG") ; collation # should be <= 255 s y=$$bin2num($ze(rec,rel,rel+3)),rel=rel+4 ; read 4 bytes d chkcoll^GDEPARSE(x,s,y) s gnams(s,"COLLATION")=x s gnams(s,"COLLVER")=y s abs=abs+SIZEOF("gd_gblname") q inst: n x,y i $zl(rec)-(rel-1)max zm gdeerr("INPINTEG") i $zl(rec)-(rel-1) size of the "gd_inst_info" structure (defined in gdsfhead.h) s SIZEOF("max_str")=1048576 s SIZEOF("mident")=32 s SIZEOF("reg_jnl_deq")=4 s SIZEOF("rec_hdr")=4 d gvstats^GDEINIT s MAXNAMLN=SIZEOF("mident")-1,MAXREGLN=32,MAXSEGLN=32 ; maximum name length allowed is 31 characters s PARNAMLN=31,PARREGLN=31,PARSEGLN=31 q v6312init: if ((olabel="GTCGBDUNX014")!(olabel="GTCGBDUNX013")) do . s SIZEOF("am_offset")=336 . s SIZEOF("file_spec")=256 . s SIZEOF("gd_contents")=80 . s SIZEOF("gd_header")=16 . s SIZEOF("gd_map")=16 . s SIZEOF("gd_region")=412 . s SIZEOF("gd_region_padding")=0 . s SIZEOF("gd_segment")=372 else do . s SIZEOF("am_offset")=340 . s SIZEOF("file_spec")=256 . s SIZEOF("gd_contents")=120 . s SIZEOF("gd_header")=16 . s SIZEOF("gd_map")=24 . s SIZEOF("gd_region")=424 . s SIZEOF("gd_region_padding")=4 . s SIZEOF("gd_segment")=384 s SIZEOF("blk_hdr")=16 s SIZEOF("dsk_blk")=512 s SIZEOF("gd_gblname")=40 s SIZEOF("gd_inst_info")=SIZEOF("file_spec") ; --> size of the "gd_inst_info" structure (defined in gdsfhead.h) s SIZEOF("max_str")=1048576 s SIZEOF("mident")=32 s SIZEOF("reg_jnl_deq")=4 s SIZEOF("rec_hdr")=4 d gvstats^GDEINIT s MAXNAMLN=SIZEOF("mident")-1,MAXREGLN=32,MAXSEGLN=32 ; maximum name length allowed is 31 characters s PARNAMLN=31,PARREGLN=31,PARSEGLN=31 q fis-gtm-V7.0-005/sr_unix/gdeput.m0000644000032200000250000003651714342376327015531 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2006-2021 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; gdeput: ;output the result of the session to the global directory file GDEPUT() n rec,gds,cregs,csegs,cregcnt,csegcnt,maxrecsize,mapcnt,map,hasSpanGbls,isSpanned,curMapSpanning,prevMapSpanning n varmapslen,vargblnamelen,tmplen,ptrsize,varmapoff,gnamcnt,gblnamelen,filler16byte,filler12byte,filler45byte d gblstatmap,CREATEGLDMAP^GDEMAP s ptrsize=$s((gtm64=TRUE):8,1:4) s s="",gdeputzs="",varmapslen=0,mapcnt=0,hasSpanGbls=0 f s s=$o(map(s)),tmplen=$zl(s) q:'tmplen d . s cregs(map(s))="" . s varmapslen=(varmapslen+tmplen+2) ; varmapslen needs to account 2 null terminating bytes . s varmapslen($incr(mapcnt))=tmplen . s gblnamelen=$zf(s,ZERO) . i gblnamelen'=0 s hasSpanGbls=1 . s gblnamelen=$s(gblnamelen=0:tmplen,1:gblnamelen-2) . s vargblnamelen(mapcnt)=gblnamelen s varmapslen=(varmapslen+ptrsize-1)\ptrsize*ptrsize ; do 8-byte or 4-byte rounding up as appropriate s maxrecsize=0 f cregcnt=0:1 s s=$o(cregs(s)) q:'$l(s) d . s csegs(regs(s,"DYNAMIC_SEGMENT"))=s i maxrecsize0 d . s rec=rec_$$num2bin(ptrsize,filesize) ; instptr . s filesize=filesize+SIZEOF("gd_inst_info") e s rec=rec_$tr($j("",ptrsize)," ",ZERO) ; no inst info s rec=rec_$tr($j("",(3*ptrsize))," ",ZERO) ; reserved s rec=rec_$$num2bin(ptrsize,filesize) ; end s rec=rec_$$num2bin(4,hasSpanGbls) ; has_span_gbls s rec=rec_filler12byte ; for runtime filler s rec=hdrlab_$$num2bin(4,$l(hdrlab)+4+filesize)_rec i create zm gdeerr("GDCREATE"):file e zm gdeerr("GDUPDATE"):file s gdexcept="s gdeputzs=$zs zgoto "_$zl_":writeerr^GDEPUT" s tempfile=file_"inprogress" ;if zchset is UTF-8 open in raw mode to avoid BADCHAR errors ; For OS390 aka z/OS, use BINARY mode s chset=$SELECT($ZV["OS390":"BINARY",$ZCHSET="UTF-8":"M",1:"") o tempfile:(rewind:noreadonly:newversion:recordsize=512:fixed:blocksize=512:exception=gdexcept:ochset=chset) ; maps s s="",curMapSpanning=0,prevMapSpanning=0 f i=1:1 s s=$o(map(s)) q:'$zl(s) d mapfixed(i,s) s s="" f i=1:1 s s=$o(map(s)) q:'$zl(s) d mapvariable(i,s) ; write padding if not 8-byte (on 64-bit) or 4-byte (on 32-bit platform) aligned after variable maps section s tmplen=varmapoff#ptrsize i tmplen d . s tmplen=ptrsize-tmplen . s rec=rec_$tr($j("",tmplen)," ",ZERO) ; cregs f s s=$o(cregs(s)) q:'$l(s) d cregion ; csegs f s s=$o(csegs(s)) q:'$l(s) d csegment ; cgnams f s s=$o(gnams(s)) q:'$l(s) d cgblname(s) ; cinst i inst>0 d cinst ; template access method i accmeth'[("\"_tmpacc) d error1 s rec=rec_$tr($j($l(tmpacc),3)," ",0) s rec=rec_tmpacc ; templates new x s x=tmpreg("STATS"),tmpreg("AUTODB")='x*2+tmpreg("AUTODB") k tmpreg("STATS") ;combine items for bit mask - restore below s tmpreg("LOCK_CRIT")='tmpreg("LOCK_CRIT") ; again an adjustment before and after f s s=$o(tmpreg(s)) q:'$l(s) s rec=rec_$tr($j($l(tmpreg(s)),3)," ",0) s rec=rec_tmpreg(s) s tmpreg("STATS")=x,tmpreg("AUTODB")=tmpreg("AUTODB")#2 ; save/restore cheaper than checking in loop s tmpreg("LOCK_CRIT")='tmpreg("LOCK_CRIT") ; restore GDE representation f i=2:1:$l(accmeth,"\") s am=$p(accmeth,"\",i) s s="" d . f s s=$o(tmpseg(am,s)) q:'$l(s) s rec=rec_$tr($j($l(tmpseg(am,s)),3)," ",0),rec=rec_tmpseg(am,s) u tempfile f s record=$ze(rec,1,512),rec=$ze(rec,513,MAXSTRLEN) q:'$zl(record) w record,! u @useio c tempfile:(replace=file) ; overwrite before rename cuts delete gap but not currently documented i debug,$$MAP2NAM^GDEMAP(.map),$$ALL^GDEVERIF q 1 ;----------------------------------------------------------------------------------------------------------------------------------- gblstatmap: n s,ysr,cnt,xrefstatsreg,xrefcnt,ynam,blksiz s s="" s maxs=$o(regs(s),-1) ; to ensure STDNULLCOLL for the statsDB mapping, last lower-case reg gets all of %YGS ; It cannot be the first lower-case region as that would cause problems for the runtime ; (since the map entries for %YGS and %YGS(FIRSTREG) would coalesce). Choosing the last ; lower-case region avoids this coalesce issue and ensure there is a separate map entry ; for just %YGS. This is needed by runtime (see "ygs_map_entry_changed" usage) s ysr=$zconvert(maxs,"L") ; Map ^%Y* to a lower-case region s ynam="%Y*",ynam("NSUBS")=0,ynam("SUBS",0)=ynam,ynam("TYPE")="STAR" m nams(ynam)=ynam s nams(ynam)=ysr i $incr(nams) s blksiz=$$BLKSIZ($tr($j("",MAXREGLN)," ","x")) ; use maximum length region name for computation s s=maxs d s s="" f s s=$o(regs(s)) q:(maxs=s) i (s?1U.e) s ysr=$zconvert(s,"L") d . d add2nams^GDEMAP("^%YGS("""_s_""")",ysr,"POINT") i $incr(nams) . s xrefstatsreg(ysr)=s,xrefstatsreg(s)=ysr . m regs(ysr)=regs(s) ; copy & then partially override region settings for statsDB regions . s regs(ysr,"AUTODB")=1 . s regs(ysr,"BEFORE_IMAGE")=0 . s regs(ysr,"DYNAMIC_SEGMENT")=ysr . s regs(ysr,"JOURNAL")=0 . s regs(ysr,"KEY_SIZE")=dflreg("KEY_SIZE") . s regs(ysr,"QDBRUNDOWN")=1 ; have it always enabled on statsdbs as MUPIP SET can enable this only on basedb later . s regs(ysr,"STATS")=0 . s regs(ysr,"STDNULLCOLL")=1 . m segs(ysr)=segs(regs(s,"DYNAMIC_SEGMENT")) ; copy & then partially override segment settings for statsDB segments . s segs(ysr,"ACCESS_METHOD")="MM" . s segs(ysr,"ALLOCATION")=2050 ; about enough for 2000 processes . s segs(ysr,"ASYNCIO")=0 . s segs(ysr,"BLOCK_SIZE")=blksiz,regs(ysr,"RECORD_SIZE")=segs(ysr,"BLOCK_SIZE")-SIZEOF("blk_hdr") . s segs(ysr,"DEFER")=0 ; setting defer=0 on an MM database implies NO flush timer on statsdb regions . s segs(ysr,"DEFER_ALLOCATE")=1 . s segs(ysr,"ENCRYPTION_FLAG")=0 . s segs(ysr,"EXTENSION_COUNT")=2050 ; and a 2000 more at a time . ; Corresponding unique .gst file name for statsdb is determined at runtime when basedb is first opened. . ; For now just keep the name as the basedb name + ".gst" . s segs(ysr,"FILE_NAME")=segs(ysr,"FILE_NAME")_".gst" . s segs(ysr,"LOCK_SPACE")=defseg("LOCK_SPACE") . s segs(ysr,"RESERVED_BYTES")=0 . i $i(nams),$i(regs),$i(segs) ; increment the counts ; Determine gd_region.statsDB_reg_index for runtime k sreg s cnt=0,s="" for set s=$o(regs(s)) quit:s="" s xrefcnt(s)=cnt i $incr(cnt) for set s=$o(xrefstatsreg(s)) quit:s="" s sreg(s,"STATSDB_REG_INDEX")=xrefcnt(xrefstatsreg(s)) q BLKSIZ(regnm) ; Calculate minimum block size for ^%YGS(regnm) ; Note that this function is always called with regnm set to a string of 32 'x' chars new bhdsz,helpgld,maxksz,maxkpad,maxpid,minksz,minkpad,minreq,rhdsz,statsz,tmp ; The key thing here is that the AIX max has one more digit than the Linux max set tmp=$piece($zversion," ",3),maxpid=$select("AIX"=tmp:2**26-2,"Linux"=tmp:2**22-1,1:2**16) set maxksz=$zlength($zcollate("%YGS("""_regnm_""","""_maxpid_""")",0)) ; max key size for statistics record set minksz=$zlength($zcollate("%YGS("""_regnm_""",1)",0)) ; min key size for statistics record set maxkpad=maxksz#8,maxkpad=$select(maxkpad:8-maxkpad,1:0) ; padding to align statistics of largest record set minkpad=minksz#8,minkpad=$select(minkpad:8-minkpad,1:0) ; padding to align statistics of smallest record set tmp=maxksz+maxkpad set:minksz+minkpad>tmp tmp=minksz+minkpad set minreq=SIZEOF("blk_hdr")+SIZEOF("rec_hdr")+tmp+SIZEOF("gvstats") quit $select(minreq#512:minreq\512+1*512,1:minreq) ; actual block size must round up to multiple of 512 fdatum: s x=segs(s,"ACCESS_METHOD") s filetype=$s((x="BG")!(x="MM"):"GDS",x="USER":"USER",1:"ERROR") i filetype="ERROR" d error1 q mapfixed:(i,key) n tmpmaplen,tmpnamelen,reg d writerec s tmpmaplen=varmapslen(i) s rec=rec_$$num2bin(4,varmapoff) ; gvkey.offset i (gtm64=TRUE) s rec=rec_$$num2bin(4,0) ; add padding s reg=map(key) s rec=rec_$$num2bin(4,cregs(reg,"offset")) ; reg.offset i (gtm64=TRUE) s rec=rec_$$num2bin(4,0) ; add padding s tmpnamelen=vargblnamelen(i) s rec=rec_$$num2bin(4,tmpnamelen) ; gvname_len s rec=rec_$$num2bin(4,tmpmaplen+1) ; gvkey_len s varmapoff=varmapoff+tmpmaplen+2 s curMapSpanning=(tmpnamelen'=tmpmaplen) i (curMapSpanning!prevMapSpanning) s isSpanned(reg)=1 s prevMapSpanning=curMapSpanning q mapvariable:(i,key) d writerec s rec=rec_key_ZERO_ZERO q cregion: d writerec s rec=rec_$$num2bin(2,$l(s)) s rec=rec_s_$tr($j("",MAXREGLN-$l(s))," ",ZERO) s rec=rec_$$num2bin(2,regs(s,"KEY_SIZE")) s rec=rec_$$num2bin(4,regs(s,"RECORD_SIZE")) s rec=rec_$$num2bin(4,csegs(regs(s,"DYNAMIC_SEGMENT"),"offset")) i (gtm64=TRUE) s rec=rec_$$num2bin(4,0) ; padding s rec=rec_$$num2bin(ptrsize,0) s rec=rec_ZERO ; OPEN state s rec=rec_ZERO ; LOCK_WRITE s rec=rec_$c(regs(s,"NULL_SUBSCRIPTS")) s rec=rec_$c(regs(s,"JOURNAL")) s rec=rec_$$num2bin(4,regs(s,"ALLOCATION")) s rec=rec_$$num2bin(4,regs(s,"EXTENSION")) s rec=rec_$$num2bin(4,regs(s,"AUTOSWITCHLIMIT")) s rec=rec_$$num2bin(4,regs(s,"ALIGNSIZE")) s rec=rec_$$num2bin(4,regs(s,"EPOCH_INTERVAL")) s rec=rec_$$num2bin(4,regs(s,"SYNC_IO")) s rec=rec_$$num2bin(4,regs(s,"YIELD_LIMIT")) s rec=rec_$$num2bin(4,regs(s,"BUFFER_SIZE")) s rec=rec_$c(regs(s,"BEFORE_IMAGE")) s rec=rec_$tr($j("",4)," ",ZERO) ;filler s rec=rec_$$num2bin(1,regs(s,"COLLATION_DEFAULT")) s rec=rec_$$num2bin(1,regs(s,"STDNULLCOLL")) s rec=rec_$$num2bin(1,regs(s,"INST_FREEZE_ON_ERROR")) s rec=rec_$$num2bin(1,regs(s,"QDBRUNDOWN")) s rec=rec_$$num2bin(1,$zl(regs(s,"FILE_NAME"))) s rec=rec_regs(s,"FILE_NAME")_$tr($j("",SIZEOF("file_spec")-$zl(regs(s,"FILE_NAME")))," ",ZERO) s rec=rec_$tr($j("",2)," ",ZERO) ;alignment s rec=rec_$tr($j("",8)," ",ZERO) ; reserved s rec=rec_$$num2bin(4,isSpanned(s)) ; is_spanned n maxregindex s maxregindex=$get(sreg(s,"STATSDB_REG_INDEX"),TWO(32)-1) s rec=rec_$$num2bin(4,maxregindex) ; statsDB_reg_index s rec=rec_$$num2bin(1,regs(s,"EPOCHTAPER")) ; epoch tapering s rec=rec_$$num2bin(1,((s?1L.E)*4)+(('regs(s,"STATS"))*2)+regs(s,"AUTODB")) ; type of reserved DB s rec=rec_$$num2bin(1,'regs(s,"LOCK_CRIT")) ; LOCK crit with DB (1) or not (0) s rec=rec_filler45byte ; runtime filler s rec=rec_$tr($j("",SIZEOF("gd_region_padding"))," ",ZERO) ; padding q csegment: d writerec s ref=$zl(rec) s am=segs(s,"ACCESS_METHOD") s rec=rec_$$num2bin(2,$l(s)) s rec=rec_s_$tr($j("",MAXSEGLN-$l(s))," ",ZERO) s rec=rec_$$num2bin(2,$zl(segs(s,"FILE_NAME"))) s rec=rec_segs(s,"FILE_NAME")_$tr($j("",SIZEOF("file_spec")-$zl(segs(s,"FILE_NAME")))," ",ZERO) s rec=rec_$$num2bin(2,segs(s,"BLOCK_SIZE")) set rec=rec_$$num2bin(2,segs(s,"FULLBLKWRT")) set rec=rec_$$num2bin(4,segs(s,"EXTENSION_COUNT")) s rec=rec_$$num2bin(4,segs(s,"ALLOCATION")) if (gtm64=TRUE) s rec=rec_$tr($j("",8)," ",ZERO) ;reserved for clb + padding e s rec=rec_$tr($j("",4)," ",ZERO) ;reserved for clb s rec=rec_".DAT" s rec=rec_$c(+segs(s,"DEFER")) s rec=rec_ZERO ;DYNAMIC segment s rec=rec_$$num2bin(1,segs(s,"BUCKET_SIZE")) s rec=rec_$$num2bin(1,segs(s,"WINDOW_SIZE")) s rec=rec_$$num2bin(4,segs(s,"LOCK_SPACE")) s rec=rec_$$num2bin(4,segs(s,"GLOBAL_BUFFER_COUNT")) s rec=rec_$$num2bin(4,segs(s,"RESERVED_BYTES")) s rec=rec_$$num2bin(4,segs(s,"MUTEX_SLOTS")) s rec=rec_$$num2bin(4,segs(s,"DEFER_ALLOCATE")) s x=$s(am="BG":1,am="MM":2,am="USER":4,1:-1) i x=-1 d error1 s rec=rec_$$num2bin(4,x) s rec=rec_$$num2bin(ptrsize,0) ; file_cntl ptr s rec=rec_$$num2bin(ptrsize,0) ; repl_list ptr ; Only for platforms that support encryption, we write this value. Others it will ; always be 0 (ie encryption is off) i (encsupportedplat=TRUE) s rec=rec_$$num2bin(4,segs(s,"ENCRYPTION_FLAG")) e s rec=rec_$$num2bin(4,0) s rec=rec_$$num2bin(4,segs(s,"ASYNCIO")) set rec=rec_filler16byte ; runtime filler (read_only + filler) q cgblname:(s) n len,coll,ver d writerec s len=$zl(s) s rec=rec_s_$tr($j("",SIZEOF("mident")-len)," ",ZERO) s coll=gnams(s,"COLLATION") s rec=rec_$$num2bin(4,coll) s ver=$view("YCOLLATE",coll) s rec=rec_$$num2bin(4,ver) q cinst: n s,len,n d writerec s s=inst("FILE_NAME") s len=$zl(s) s rec=rec_s_$tr($j("",SIZEOF("gd_inst_info")-len)," ",ZERO) q ;----------------------------------------------------------------------------------------------------------------------------------- num2bin:(l,n) i (gtm64=TRUE) q $s(l=1:$$num2tiny(+n),l=2:$$num2shrt(+n),l=4:$$num2int(+n),l=8:$$num2long(+n),1:$$num2error) q $s(l=1:$$num2tiny(+n),l=2:$$num2shrt(+n),l=4:$$num2int(+n),1:$$num2error) ; num2tiny:(num) i (num<0)!(num'<256) d error1 q $zch(num) ; num2shrt:(num) i (num<0)!(num'segment mappings n refdyns s s="" f s s=$o(regs(s)) q:'$l(s) d:$d(refdyns(regs(s,"DYNAMIC_SEGMENT"))) dupseg s refdyns(regs(s,"DYNAMIC_SEGMENT"),s)="" ; No duplicate segment->file mappings n reffils f s s=$o(segs(s)) q:'$l(s) d:$d(reffils(segs(s,"FILE_NAME"))) dupfile s reffils(segs(s,"FILE_NAME"),s)="" q NAME i '$d(nams(NAME)) k verified d q . zm $$info(gdeerr("OBJNOTFND")):"Name":$s(NAME'="#":$$namedisp^GDESHOW(NAME,0),1:"Local Locks") name1: i '$d(regs(nams(NAME))) d . s verified=0 . zm gdeerr("MAPBAD"):"Region":nams(NAME):"Name":$s(NAME'="#":$$namedisp^GDESHOW(NAME,0),1:"Local Locks") q GBLNAME i '$d(gnams(GBLNAME)) k verified zm $$info(gdeerr("OBJNOTFND")):"Global Name":GBLNAME q gblname1: n s,sval,errissued s s="" f s s=$o(gnams(GBLNAME,s)) q:""=s s sval=gnams(GBLNAME,s) d . s errissued=0 . i $d(mingnam(s)),mingnam(s)>sval s errissued=1 zm gdeerr("VALTOOSMALL"):sval:mingnam(s):s . i $d(maxgnam(s)),maxgnam(s)rquals(s) zmessage gdeerr("VALTOOSMALL"):rquals(s):minreg(s):s else if $data(maxreg(s)),maxreg(s)squals(s) zmessage gdeerr("VALTOOSMALL"):squals(s):minseg(am,s):s else if $data(maxseg(am,s)),maxseg(am,s)y set verified=0 zmessage gdeerr("KEYSIZIS"):ks,gdeerr("KEYFORBLK"):bs:f:y,gdeerr("REGIS"):REGION quit buf2blk: i REGION="TEMPLATE" q i "USER"[am s verified=0 zm gdeerr("NOJNL"):am,gdeerr("REGIS"):REGION,gdeerr("SEGIS"):am:SEGMENT q mmbichk: i REGION="TEMPLATE",am="MM",tmpacc'="MM" q i am="MM" s verified=0 zm gdeerr("MMNOBEFORIMG"),gdeerr("REGIS"):REGION,gdeerr("SEGIS"):am:SEGMENT q allocchk(rquals) n ext,alloc,asl,qn s qn="EXTENSION",ext=$s($d(rquals(qn)):rquals(qn),$d(regs(REGION,qn)):regs(REGION,qn),1:tmpreg(qn)) s qn="ALLOCATION",alloc=$s($d(rquals(qn)):rquals(qn),$d(regs(REGION,qn)):regs(REGION,qn),1:tmpreg(qn)) s qn="AUTOSWITCHLIMIT",asl=$s($d(rquals(qn)):rquals(qn),$d(regs(REGION,qn)):regs(REGION,qn),1:tmpreg(qn)) i alloc>asl s verified=0 zm gdeerr("VALTOOBIG"):alloc:asl_" (AUTOSWITCHLIMIT)":"ALLOCATION" q i alloc'=asl,ext+alloc>asl d . s rquals("ALLOCATION")=asl . zm gdeerr("JNLALLOCGROW"):alloc:asl:"region":REGION q ;----------------------------------------------------------------------------------------------------------------------------------- ; called from GDEADD.M and GDECHANG.M RQUALS(rquals) if '$data(verified) new verified set verified=1 set s="" for set s=$order(rquals(s)) quit:'$length(s) do regelm quit:'verified verified if $data(rquals("FILE_NAME")),$zlength(rquals("FILE_NAME"))>(SIZEOF("file_spec")-1) set verified=0 if do . zmessage $$info(gdeerr("VALTOOLONG")):rquals("FILE_NAME"):SIZEOF("file_spec")-1:"Journal filename",gdeerr("REGIS"):REGION set ks="KEY_SIZE",ks=$select($data(rquals(ks)):rquals(ks),$data(regs(REGION,ks)):regs(REGION,ks),1:tmpreg(ks)) set x="RECORD_SIZE",x=$select($data(rquals(x)):rquals(x),$data(regs(REGION,x)):regs(REGION,x),1:tmpreg(x)) do allocchk(.rquals) if REGION="TEMPLATE" set bs=tmpseg(tmpacc,"BLOCK_SIZE"),f=tmpseg(tmpacc,"RESERVED_BYTES") ; note "else" used in two consecutive lines intentionally (instead of using a do block inside one else). ; this is because we want the QUIT to quit out of RQUALS and the NEW of SEGMENT,am to happen at the RQUALS level. else set s="DYNAMIC_SEGMENT",s=$select($data(rquals(s)):rquals(s),$data(regs(REGION,s)):regs(REGION,s),1:0) else quit:'$data(segs(s)) verified new SEGMENT,am do . set SEGMENT=s,am=segs(s,"ACCESS_METHOD"),bs=$get(segs(s,"BLOCK_SIZE")),f=$get(segs(s,"RESERVED_BYTES")) do:((minreg("KEY_SIZE")'>ks)&(maxreg("KEY_SIZE")'squals(bs))&(maxseg(am,"BLOCK_SIZE")'bs)&(maxseg(am,"BLOCK_SIZE")' #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtm_stat.h" #include "eintr_wrappers.h" error_def(ERR_DBFILOPERR); gtm_uint64_t gds_file_size(file_control *fc) { unix_db_info *udi; int fstat_res; struct stat stat_buf; udi = (unix_db_info *)fc->file_info; FSTAT_FILE(udi->fd, &stat_buf, fstat_res); if (-1 == fstat_res) rts_error(VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(udi->fn), errno); assert(0 == stat_buf.st_size % DISK_BLOCK_SIZE); return (gtm_uint64_t)(stat_buf.st_size / DISK_BLOCK_SIZE); } fis-gtm-V7.0-005/sr_unix/gds_rundown.c0000644000032200000250000013445414342376327016557 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_ipc.h" #include "gtm_inet.h" #include "gtm_fcntl.h" #include "gtm_string.h" #include "gtm_unistd.h" #include "gtm_time.h" #include "gtm_signal.h" /* for VSIG_ATOMIC_T type */ #include #include #include #include #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdsblk.h" #include "gt_timer.h" #include "jnl.h" #include "error.h" #include "iosp.h" #include "gdsbgtr.h" #include "repl_msg.h" #include "gtmsource.h" #include "aswp.h" #include "gtm_c_stack_trace.h" #include "eintr_wrapper_semop.h" #include "util.h" #include "send_msg.h" #include "change_reg.h" #include "compswap.h" #include "mutex.h" #include "gds_rundown.h" #include "gvusr.h" #include "do_semop.h" #include "mmseg.h" #include "ipcrmid.h" #include "gtmmsg.h" #include "wcs_recover.h" #include "wcs_mm_recover.h" #include "tp_change_reg.h" #include "wcs_flu.h" #include "add_inter.h" #include "io.h" #include "gtmsecshr.h" #include "secshr_client.h" #include "ftok_sems.h" #include "gtmimagename.h" #include "gtmio.h" #include "have_crit.h" #include "is_proc_alive.h" #include "shmpool.h" #include "db_snapshot.h" #include "ss_lock_facility.h" #include "anticipatory_freeze.h" #include "wcs_clean_dbsync.h" #include "interlock.h" #include "gds_rundown_err_cleanup.h" #include "wcs_wt.h" #include "wcs_sleep.h" #include "aio_shim.h" #include "gvcst_protos.h" #include "targ_alloc.h" #include "gdskill.h" /* needed for tp.h */ #include "gdscc.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "hashtab_int8.h" /* needed for tp.h and gds_rundown.c itself */ #include "tp.h" #include "mlkdef.h" #include "mlk_ops.h" GBLREF VSIG_ATOMIC_T forced_exit; GBLREF boolean_t mupip_jnl_recover; GBLREF boolean_t is_src_server, is_updproc; GBLREF gd_region *ftok_sem_reg, *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF uint4 process_id; GBLREF ipcs_mesg db_ipcs; GBLREF jnl_process_vector *prc_vec; GBLREF jnl_process_vector *originator_prc_vec; GBLREF jnl_gbls_t jgbl; GBLREF boolean_t dse_running; GBLREF int num_additional_processors; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF int process_exiting; GBLREF boolean_t ok_to_UNWIND_in_exit_handling; GBLREF gv_namehead *gv_target_list; LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; LITREF gtmImageName gtmImageNames[]; error_def(ERR_AIOCANCELTIMEOUT); error_def(ERR_ASSERT); error_def(ERR_CRITSEMFAIL); error_def(ERR_DBFILERR); error_def(ERR_DBRNDWN); error_def(ERR_DBRNDWNWRN); error_def(ERR_GTMASSERT); error_def(ERR_GTMASSERT2); error_def(ERR_GTMCHECK); error_def(ERR_IPCNOTDEL); error_def(ERR_JNLFLUSH); error_def(ERR_LASTWRITERBYPAS); error_def(ERR_MEMORY); error_def(ERR_OUTOFSPACE); error_def(ERR_RESRCINTRLCKBYPAS); error_def(ERR_RNDWNSEMFAIL); error_def(ERR_RNDWNSKIPCNT); error_def(ERR_STACKOFLOW); error_def(ERR_STACKOFLOW); error_def(ERR_TEXT); error_def(ERR_WCBLOCKED); int4 gds_rundown(boolean_t cleanup_udi) { boolean_t canceled_dbsync_timer, do_jnlwait, ok_to_write_pfin, wrote_pfin; boolean_t have_standalone_access, ipc_deleted, err_caught, aiocancel_timedout; boolean_t is_cur_process_ss_initiator, remove_shm, vermismatch, we_are_last_user, we_are_last_writer, is_mm; boolean_t unsafe_last_writer; boolean_t db_needs_flushing; char time_str[CTIME_BEFORE_NL + 2]; /* for GET_CUR_TIME macro */ gd_region *reg, *statsDBreg; gd_segment *seg; int save_errno, status, rc; int4 semval, ftok_semval, sopcnt, ftok_sopcnt; short crash_count; sm_long_t munmap_len; void *munmap_start; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; node_local_ptr_t cnl; struct shmid_ds shm_buf; struct sembuf sop[2], ftok_sop[2]; uint4 jnl_status; unix_db_info *udi; jnl_private_control *jpc; jnl_buffer_ptr_t jbp; shm_snapshot_t *ss_shm_ptr; uint4 ss_pid, onln_rlbk_pid, holder_pid; boolean_t is_statsDB, was_crit; boolean_t safe_mode; /* Do not flush or take down shared memory. */ boolean_t bypassed_ftok = FALSE, bypassed_access = FALSE, may_bypass_ftok, inst_is_frozen; boolean_t ftok_counter_halted, access_counter_halted; int secshrstat; intrpt_state_t prev_intrpt_state; gv_namehead *currgvt; gd_region *baseDBreg; sgmnt_addrs *baseDBcsa; node_local_ptr_t baseDBnl; sgm_info *si; DEBUG_ONLY(boolean_t orig_we_are_last_writer = FALSE); jnl_status = 0; reg = gv_cur_region; /* Local copy */ /* Early out for cluster regions * to avoid tripping the assert below. * Note: * This early out is consistent with VMS. It has been * noted that all of the gtcm assignments * to gv_cur_region should use the TP_CHANGE_REG * macro. This would also avoid the assert problem * and should be done eventually. */ seg = reg->dyn.addr; if (dba_cm == seg->acc_meth) return EXIT_NRM; udi = FILE_INFO(reg); csa = &udi->s_addrs; csd = csa->hdr; assert((csa == cs_addrs) && (csd == cs_data)); /* relied upon by "jnl_ensure_open" calls below */ if ((reg->open) && (dba_usr == csd->acc_meth)) { change_reg(); gvusr_rundown(); return EXIT_NRM; } /* If this region has a corresponding statsdb region that is open, close that first. This is needed to ensure * that the statsdb can safely be deleted at basedb rundown time if we happen to be the last one to rundown the basedb. */ is_statsDB = IS_STATSDB_REG(reg); if (is_statsDB) { /* This is a statsdb. Fix reg->read_only & csa->read_write based on csa->orig_read_write. * This is so it reflects real permissions this process has on the statsdb. */ reg->read_only = !csa->orig_read_write; csa->read_write = csa->orig_read_write; /* Maintain read_only/read_write in parallel */ } else { /* Note that even if the baseDB has RDBF_NOSTATS set, we could have opened the statsDB region * (for example, if statsDB has read-only permissions, we would have opened it and found it is * read-only when we tried to add the ^%YGS node and would have disabled stats in the baseDB * all the while leaving the statsDB open. So we need to check if it is open and if so run it down * without checking the RDBF_NOSTATS bit in the baseDB. */ BASEDBREG_TO_STATSDBREG(reg, statsDBreg); if (statsDBreg->open) { /* Now we're running down statsdb, reset basedb stats back to private memory in case it was * pointing to statsdb shared/mapped memory. Note that the following reset of the stats * pointer back to the internal stats buffer located is sgmnt_data is normally taken care * of by the statsdb unlink processing in gvcst_remove_statsDB_linkage() but we keep this * reset here also to be sure it gets done in case of a statsDB rundown issue. */ csa->gvstats_rec_p = &csa->gvstats_rec; gv_cur_region = statsDBreg; /* Switch "gv_cur_region" to do rundown of statsDB */ tp_change_reg(); gds_rundown(cleanup_udi); /* Ignore errors in statsdb rundown. Continue with baseDB rundown. */ gv_cur_region = reg; /* Restore "gv_cur_region" back to continue rundown of baseDB */ tp_change_reg(); } } /* If this is a read-only database, simply return */ if (csd->read_only) { if (INVALID_SEMID != udi->semid) semctl(udi->semid, 0, IPC_RMID); udi->sem_deleted = TRUE; /* Note that we deleted the semaphore */ udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; assert(FALSE == udi->grabbed_ftok_sem); if (INVALID_SEMID != udi->ftok_semid) semctl(udi->ftok_semid, 0, IPC_RMID); udi->counter_ftok_incremented = FALSE; udi->semid = udi->ftok_semid = INVALID_SEMID; return EXIT_NRM; } csa->regcnt--; if (csa->regcnt) { /* There is at least one more region pointing to the same db file as this region. * Defer rundown of this "csa" until the last region corresponding to this csa is called for rundown. */ reg->open = FALSE; return EXIT_NRM; } /* If the process has standalone access, it has udi->grabbed_access_sem set to TRUE at this point. Note that down in a local * variable as the udi->grabbed_access_sem is set to TRUE even for non-standalone access below and hence we can't rely on * that later to determine if the process had standalone access or not when it entered this function. We need to guarantee * that none else access database file header when semid/shmid fields are reset. We already have created ftok semaphore in * db_init or, mu_rndwn_file and did not remove it. So just lock it. We do it in blocking mode. */ have_standalone_access = udi->grabbed_access_sem; /* process holds standalone access */ DEFER_INTERRUPTS(INTRPT_IN_GDS_RUNDOWN, prev_intrpt_state); ESTABLISH_NORET(gds_rundown_ch, err_caught); if (err_caught) { REVERT; WITH_CH(gds_rundown_ch, gds_rundown_err_cleanup(have_standalone_access), 0); ENABLE_INTERRUPTS(INTRPT_IN_GDS_RUNDOWN, prev_intrpt_state); DEBUG_ONLY(ok_to_UNWIND_in_exit_handling = FALSE); return EXIT_ERR; } assert(reg->open); /* if we failed to open, dbinit_ch should have taken care of proper clean up */ assert(!reg->opening); /* see comment above */ assert(IS_CSD_BG_OR_MM(csd)); is_mm = (dba_bg != csd->acc_meth); assert(!csa->hold_onto_crit || (csa->now_crit && jgbl.onlnrlbk)); /* If we are online rollback, we should already be holding crit and should release it only at the end of this module. This * is usually done by noting down csa->now_crit in a local variable (was_crit) and using it whenever we are about to * grab_crit. But, there are instances (like mupip_set_journal.c) where we grab_crit but invoke "gds_rundown" without any * preceeding rel_crit. Such code relies on the fact that "gds_rundown" does rel_crit unconditionally (to get locks to a * known state). So, augment csa->now_crit with jgbl.onlnrlbk to track if we can rel_crit unconditionally or not in * "gds_rundown". */ was_crit = (csa->now_crit && jgbl.onlnrlbk); /* Cancel any pending flush timer for this region by this process */ canceled_dbsync_timer = FALSE; CANCEL_DB_TIMERS(reg, csa, canceled_dbsync_timer); we_are_last_user = FALSE; inst_is_frozen = IS_REPL_INST_FROZEN && REPL_ALLOWED(csa->hdr); if (FREEZE_LATCH_HELD(csa)) rel_latch(&cnl->freeze_latch); if (!csa->persistent_freeze) region_freeze(reg, FALSE, FALSE, FALSE, FALSE, FALSE); if (!csa->lock_crit_with_db && LOCK_CRIT_HELD(csa)) { mlk_pvtctl pctl; MLK_PVTCTL_INIT(pctl, reg); REL_LOCK_CRIT(pctl, FALSE); } if (!was_crit) { rel_crit(reg); /* get locks to known state */ mutex_cleanup(reg); } /* The only process that can invoke "gds_rundown" while holding access control semaphore is RECOVER/ROLLBACK. All the * others (like MUPIP SET -FILE/MUPIP EXTEND would have invoked db_ipcs_reset() before invoking "gds_rundown" (from * mupip_exit_handler). The only exception is when these processes encounter a terminate signal and they reach * mupip_exit_handler while holding access control semaphore. Assert accordingly. */ assert(!have_standalone_access || mupip_jnl_recover || process_exiting); /* If we have standalone access, then ensure that a concurrent online rollback cannot be running at the same time as it * needs the access control lock as well. The only expection is we are online rollback and currently running down. */ cnl = csa->nl; onln_rlbk_pid = cnl->onln_rlbk_pid; assert(!have_standalone_access || mupip_jnl_recover || !onln_rlbk_pid || !is_proc_alive(onln_rlbk_pid, 0)); if (!have_standalone_access) { if (-1 == (ftok_semval = semctl(udi->ftok_semid, DB_COUNTER_SEM, GETVAL))) /* Check # of procs counted on FTOK */ { save_errno = errno; assert(FALSE); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(12) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, RTS_ERROR_TEXT("gds_rundown SEMCTL failed to get ftok_semval"), CALLFROM, errno); } may_bypass_ftok = CAN_BYPASS(ftok_semval, csa, inst_is_frozen); /* Do we need a blocking wait? */ /* We need to guarantee that no one else access database file header when semid/shmid fields are reset. * We already have created ftok semaphore in db_init or mu_rndwn_file and did not remove it. So just lock it. */ if (!ftok_sem_lock(reg, may_bypass_ftok)) { if (may_bypass_ftok) { /* We did a non-blocking wait. It's ok to proceed without locking */ bypassed_ftok = TRUE; holder_pid = semctl(udi->ftok_semid, DB_CONTROL_SEM, GETPID); if ((uint4)-1 == holder_pid) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(12) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, RTS_ERROR_TEXT("gds_rundown SEMCTL failed to get holder_pid"), CALLFROM, errno); if (!IS_GTM_IMAGE) /* MUMPS processes should not flood syslog with bypass messages. */ { send_msg_csa(CSA_ARG(csa) VARLSTCNT(12) ERR_RESRCINTRLCKBYPAS, 10, LEN_AND_STR(gtmImageNames[image_type].imageName), process_id, LEN_AND_LIT("FTOK"), REG_LEN_STR(reg), DB_LEN_STR(reg), holder_pid); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_LIT("FTOK bypassed at rundown")); } } else { /* We did a blocking wait but something bad happened. */ FTOK_TRACE(csa, csa->ti->curr_tn, ftok_ops_lock, process_id); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBFILERR, 2, DB_LEN_STR(reg)); } } sop[0].sem_num = DB_CONTROL_SEM; sop[0].sem_op = 0; /* Wait for 0 */ sop[1].sem_num = DB_CONTROL_SEM; sop[1].sem_op = 1; /* Lock */ sopcnt = 2; sop[0].sem_flg = sop[1].sem_flg = SEM_UNDO | IPC_NOWAIT; /* Don't wait the first time thru */ SEMOP(udi->semid, sop, sopcnt, status, NO_WAIT); if (0 != status) { save_errno = errno; /* Check # of processes counted on access sem. */ if (-1 == (semval = semctl(udi->semid, DB_COUNTER_SEM, GETVAL))) { save_errno = errno; if ((EINVAL == save_errno) && (cleanup_udi)) { REVERT; return EXIT_NRM; } assert(FALSE); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(12) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, RTS_ERROR_TEXT("gds_rundown SEMCTL failed to get semval"), CALLFROM, save_errno); } bypassed_access = CAN_BYPASS(semval, csa, inst_is_frozen) || onln_rlbk_pid || csd->file_corrupt; /* Before attempting again in the blocking mode, see if the holding process is an online rollback. * If so, it is likely we won't get the access control semaphore anytime soon. In that case, we * are better off skipping rundown and continuing with sanity cleanup and exit. */ holder_pid = semctl(udi->semid, DB_CONTROL_SEM, GETPID); if ((uint4)-1 == holder_pid) { save_errno = errno; assert(FALSE); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(12) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, RTS_ERROR_TEXT("gds_rundown SEMCTL failed to get holder_pid"), CALLFROM, save_errno); } if (!bypassed_access) { /* We couldn't get it in one shot-- see if we already have it */ if (holder_pid == process_id) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) MAKE_MSG_INFO(ERR_CRITSEMFAIL), 2, DB_LEN_STR(reg), ERR_RNDWNSEMFAIL); REVERT; ENABLE_INTERRUPTS(INTRPT_IN_GDS_RUNDOWN, prev_intrpt_state); assert(FALSE); return EXIT_ERR; } if (EAGAIN != save_errno) { assert(FALSE); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(12) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, RTS_ERROR_TEXT("gds_rundown SEMOP on access control semaphore"), CALLFROM, save_errno); } sop[0].sem_flg = sop[1].sem_flg = SEM_UNDO; /* Try again - blocking this time */ SEMOP(udi->semid, sop, 2, status, FORCED_WAIT); if (-1 == status) /* We couldn't get it at all.. */ RTS_ERROR_CSA_ABT(csa, VARLSTCNT(12) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, RTS_ERROR_TEXT("gds_rundown SEMOP on access control semaphore"), CALLFROM, errno); } else if (!IS_GTM_IMAGE) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(12) ERR_RESRCINTRLCKBYPAS, 10, LEN_AND_STR(gtmImageNames[image_type].imageName), process_id, LEN_AND_LIT("access control"), REG_LEN_STR(reg), DB_LEN_STR(reg), holder_pid); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_LIT("Access control bypassed at rundown")); } udi->grabbed_access_sem = !bypassed_access; } } /* Else we we hold the access control semaphore and therefore have standalone access. We do not release it now - we * release it later in mupip_exit_handler.c. Since we already hold the access control semaphore, we don't need the * ftok semaphore and trying it could cause deadlock */ /* Note that in the case of online rollback, "udi->grabbed_access_sem" (and in turn "have_standalone_access") is TRUE. * But there could be other processes still having the database open so we cannot safely reset the halted fields. */ if (have_standalone_access && !jgbl.onlnrlbk) cnl->ftok_counter_halted = cnl->access_counter_halted = FALSE; ftok_counter_halted = cnl->ftok_counter_halted; access_counter_halted = cnl->access_counter_halted; /* If we bypassed any of the semaphores, activate safe mode. * Also, if the replication instance is frozen and this db has replication turned on (which means * no flushes of dirty buffers to this db can happen while the instance is frozen) activate safe mode. * Similarly, if there is an online freeze in place, we need to avoid writing to the file, so we need * to keep shared memory around. * Or if an online freeze has been autoreleased, we need to keep shared memory around so that it can be * reported and cleaned up by a subsequent MUPIP FREEZE -OFF. */ ok_to_write_pfin = !(bypassed_access || bypassed_ftok || inst_is_frozen); safe_mode = !ok_to_write_pfin || ftok_counter_halted || access_counter_halted || FROZEN_CHILLED(csa) || CHILLED_AUTORELEASE(csa); /* At this point we are guaranteed no one else is doing a db_init/rundown as we hold the access control semaphore */ assert(csa->ref_cnt); /* decrement private ref_cnt before shared ref_cnt decrement. */ csa->ref_cnt--; assert(!csa->ref_cnt); /* Note that the below value is normally incremented/decremented under control of the init/rundown semaphore in * "db_init" and "gds_rundown" but if QDBRUNDOWN is turned ON it could be manipulated without the semaphore in * both callers. Therefore use interlocked INCR_CNT/DECR_CNT. */ DECR_CNT(&cnl->ref_cnt, &cnl->wc_var_lock); if (memcmp(cnl->now_running, gtm_release_name, gtm_release_name_len + 1)) { /* VERMISMATCH condition. Possible only if DSE */ assert(dse_running); vermismatch = TRUE; } else vermismatch = FALSE; if (-1 == shmctl(udi->shmid, IPC_STAT, &shm_buf)) { save_errno = errno; RTS_ERROR_CSA_ABT(csa, VARLSTCNT(12) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, RTS_ERROR_TEXT("gds_rundown shmctl"), CALLFROM, save_errno); } else we_are_last_user = (1 == shm_buf.shm_nattch) && !vermismatch && !safe_mode; /* recover => one user except ONLINE ROLLBACK, or standalone with frozen instance */ assert(!have_standalone_access || we_are_last_user || jgbl.onlnrlbk || inst_is_frozen); if (-1 == (semval = semctl(udi->semid, DB_COUNTER_SEM, GETVAL))) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(12) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, RTS_ERROR_TEXT("gds_rundown SEMCTL failed to get semval"), CALLFROM, errno); /* There's one writer left and I am it */ assert(reg->read_only || semval >= 0); unsafe_last_writer = (DB_COUNTER_SEM_INCR == semval) && (FALSE == reg->read_only) && !vermismatch; we_are_last_writer = unsafe_last_writer && !safe_mode; assert(!we_are_last_writer || !safe_mode); assert(!we_are_last_user || !safe_mode); /* recover + R/W region => one writer except ONLINE ROLLBACK, or standalone with frozen instance, leading to safe_mode */ assert(!(have_standalone_access && !reg->read_only) || we_are_last_writer || jgbl.onlnrlbk || inst_is_frozen); GTM_WHITE_BOX_TEST(WBTEST_ANTIFREEZE_JNLCLOSE, we_are_last_writer, 1); /* Assume we are the last writer to invoke wcs_flu */ if (!have_standalone_access && (-1 == (ftok_semval = semctl(udi->ftok_semid, DB_COUNTER_SEM, GETVAL)))) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(12) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, RTS_ERROR_TEXT("gds_rundown SEMCTL failed to get ftok_semval"), CALLFROM, errno); if (NULL != csa->ss_ctx) { ss_destroy_context(csa->ss_ctx); free(csa->ss_ctx); csa->ss_ctx = NULL; } /* SS_MULTI: If multiple snapshots are supported, then we have to run through each of the snapshots */ assert(1 == MAX_SNAPSHOTS); ss_shm_ptr = (shm_snapshot_ptr_t)SS_GETSTARTPTR(csa); ss_pid = ss_shm_ptr->ss_info.ss_pid; is_cur_process_ss_initiator = (process_id == ss_pid); if (ss_pid && (is_cur_process_ss_initiator || we_are_last_user)) { /* Try getting snapshot crit latch. If we don't get latch, we won't hang for eternity and will skip * doing the orphaned snapshot cleanup. It will be cleaned up eventually either by subsequent MUPIP * INTEG or by a MUPIP RUNDOWN. */ if (ss_get_lock_nowait(reg) && (ss_pid == ss_shm_ptr->ss_info.ss_pid) && (is_cur_process_ss_initiator || !is_proc_alive(ss_pid, 0))) { ss_release(NULL); ss_release_lock(reg); } } /* If cnl->donotflush_dbjnl is set, it means mupip recover/rollback was interrupted and therefore we need not flush * shared memory contents to disk as they might be in an inconsistent state. Moreover, any more flushing will only cause * future rollback to undo more journal records (PBLKs). In this case, we will go ahead and remove shared memory (without * flushing the contents) in this routine. A reissue of the recover/rollback command will restore the database to a * consistent state. * Or if we have an Online Freeze, skip flushing, as that will be handled when the freeze is removed. */ if (!cnl->donotflush_dbjnl && !reg->read_only && !vermismatch) { /* If we had an orphaned block and were interrupted, set wc_blocked so we can invoke wcs_recover. Do it ONLY * if there is NO concurrent online rollback running (as we need crit to set wc_blocked) */ if (csa->wbuf_dqd && !is_mm) { /* If we had an orphaned block and were interrupted, mupip_exit_handler will invoke secshr_db_clnup which * will clear this field and so we should never come to "gds_rundown" with a non-zero wbuf_dqd. The only * exception is if we are recover/rollback in which case "gds_rundown" (from mur_close_files) is invoked * BEFORE secshr_db_clnup in mur_close_files. * Note: It is NOT possible for online rollback to reach here with wbuf_dqd being non-zero. This is because * the moment we apply the first PBLK, we stop all interrupts and hence can never be interrupted in * wcs_wtstart or wcs_get_space. Assert accordingly. */ assert(mupip_jnl_recover && !jgbl.onlnrlbk && !safe_mode); if (!was_crit) grab_crit(reg, WS_40); SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER); BG_TRACE_PRO_ANY(csa, wcb_gds_rundown1); send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_WCBLOCKED, 6, LEN_AND_LIT("wcb_gds_rundown1"), process_id, &csa->ti->curr_tn, DB_LEN_STR(reg)); csa->wbuf_dqd = 0; wcs_recover(reg); BG_TRACE_PRO_ANY(csa, lost_block_recovery); if (!was_crit) rel_crit(reg); } if (JNL_ENABLED(csd) && IS_GTCM_GNP_SERVER_IMAGE) originator_prc_vec = NULL; /* If we are the last writing user, then everything must be flushed */ if (we_are_last_writer) { /* Time to flush out all of our buffers */ assert(!safe_mode); if (is_mm) { MM_DBFILEXT_REMAP_IF_NEEDED(csa, reg); cnl->remove_shm = TRUE; } if (WC_BLOCK_RECOVER == cnl->wc_blocked && jgbl.onlnrlbk) { /* if the last update done by online rollback was not committed in the normal code-path but * was completed by secshr_db_clnup, wc_blocked will be set to TRUE. But, since online * rollback never invokes grab_crit (since csa->hold_onto_crit is set to TRUE), wcs_recover * is never invoked. This could result in the last update never getting flushed to the disk * and if online rollback happened to be the last writer then the shared memory will be * flushed and removed and the last update will be lost. So, force wcs_recover if we find * ourselves in such a situation. But, wc_blocked is possible only if phase1 or phase2 * errors are induced using white box test cases */ assert(WB_COMMIT_ERR_ENABLED); wcs_recover(reg); } /* Note WCSFLU_SYNC_EPOCH ensures the epoch is synced to the journal and indirectly * also ensures that the db is fsynced. We don't want to use it in the calls to * "wcs_flu" from "t_end" and "tp_tend" since we can defer it to out-of-crit there. * In this case, since we are running down, we don't have any such option. * If we are in safe_mode, we won't get here, so no need to check for online freeze. */ if (!FROZEN_CHILLED(csa)) cnl->remove_shm = wcs_flu(WCSFLU_FLUSH_HDR | WCSFLU_WRITE_EPOCH | WCSFLU_SYNC_EPOCH); else { jnl_wait(reg); cnl->remove_shm = FALSE; } if (!cnl->remove_shm) { /* If "wcs_flu" call fails, then we should not remove shm or reset anything in the db fileheader. * So reset "we_are_last_writer" variable itself as that makes it more safer to fall through to * the cleanup code below. Before doing so, take a copy for debugging purposes. */ DEBUG_ONLY(orig_we_are_last_writer = TRUE;) we_are_last_writer = FALSE; /* Since "wcs_flu" failed, set wc_blocked to TRUE if not already set. */ if (!cnl->wc_blocked) { SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER); BG_TRACE_PRO_ANY(csa, wcb_gds_rundown2); send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_WCBLOCKED, 6, LEN_AND_LIT("wcb_gds_rundown2"), process_id, &csa->ti->curr_tn, DB_LEN_STR(reg)); } } /* "wcs_flu" performs writes asynchronously, which might spawn up a thread. We close it here. Since * the thread belongs to the global directory, we assume no one else is doing the same for this global * directory. */ IF_LIBAIO(aio_shim_destroy(udi->owning_gd);) /* Since we_are_last_writer, we should be guaranteed that "wcs_flu" did not change csd, (in * case of MM for potential file extension), even if it did a grab_crit(). Therefore, make * sure that's true. */ assert(csd == csa->hdr); assert((0 == memcmp(csd->label, GDS_LABEL, GDS_LABEL_SZ - 1)) || (0 == memcmp(csd->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1))); } else if (((csa->canceled_flush_timer && (0 > cnl->wcs_timers)) || canceled_dbsync_timer) && !inst_is_frozen) { /* If we canceled a pending dbsync timer in "gds_rundown" OR canceled a db flush timer in "gds_rundown" * or sometime in the past (e.g. because we found a JNL_FILE_SWITCHED situation in wcs_stale etc.) * AND there are no other active pending flush timers, it is possible we have unflushed buffers in * the db/jnl so call wcs_flu to flush EPOCH to disk in a timely fashion. * But before that, check if a wcs_flu is really necessary. If not, skip the heavyweight call. */ db_needs_flushing = (cnl->last_wcsflu_tn < csa->ti->curr_tn); if (db_needs_flushing) if (!FROZEN_CHILLED(csa)) wcs_flu(WCSFLU_FLUSH_HDR | WCSFLU_WRITE_EPOCH | WCSFLU_SYNC_EPOCH); else jnl_wait(reg); /* Same as above "wcs_flu" */ IF_LIBAIO(aio_shim_destroy(udi->owning_gd);) assert(is_mm || (csd == cs_data)); csd = cs_data; /* In case this is MM and "wcs_flu" remapped an extended database, reset csd */ } /* Do rundown journal processing after buffer flushes since they require jnl to be open */ if (JNL_ENABLED(csd)) { jpc = csa->jnl; jbp = jpc->jnl_buff; if (jbp->fsync_in_prog_latch.u.parts.latch_pid == process_id) { assert(FALSE); COMPSWAP_UNLOCK(&jbp->fsync_in_prog_latch, process_id, 0, LOCK_AVAILABLE, 0); } if (jbp->io_in_prog_latch.u.parts.latch_pid == process_id) { assert(FALSE); COMPSWAP_UNLOCK(&jbp->io_in_prog_latch, process_id, 0, LOCK_AVAILABLE, 0); } /* If we are last writer, it is possible cnl->remove_shm is set to FALSE from the "wcs_flu" call * above (e.g. we are source server and "wcs_flu" noticed a phase2 commit that need to be cleaned up * which needs a "wcs_recover" call but that is a no-op for the source server). So check that * additionally. Thankfully "we_are_last_writer" would have already factored that into account above * ("we_are_last_writer && cnl->remove_shm" code block above). So no additional check needed below. */ if (ok_to_write_pfin && !FROZEN_CHILLED(csa) && (((NOJNL != jpc->channel) && !JNL_FILE_SWITCHED(jpc)) || (we_are_last_writer && (0 != cnl->jnl_file.u.inode)))) { /* We need to close the journal file cleanly if we have the latest generation journal file open * or if we are the last writer and the journal file is open in shared memory (not necessarily * by ourselves e.g. the only process that opened the journal got shot abnormally) * Note: we should not infer anything from the shared memory value of cnl->jnl_file.u.inode * if we are not the last writer as it can be concurrently updated. */ do_jnlwait = FALSE; if (!was_crit) grab_crit(reg, WS_41); if (JNL_ENABLED(csd)) { SET_GBL_JREC_TIME; /* jnl_ensure_open/jnl_write_pini/pfin/jnl_file_close all need it */ /* Before writing to jnlfile, adjust jgbl.gbl_jrec_time if needed to maintain time order * of jnl records. This needs to be done BEFORE the jnl_ensure_open as that could write * journal records (if it decides to switch to a new journal file). */ ADJUST_GBL_JREC_TIME(jgbl, jbp); jnl_status = jnl_ensure_open(reg, csa); if (0 == jnl_status) { /* If we_are_last_writer, we would have already done a "wcs_flu" which would * have written an epoch record and we are guaranteed no further updates * since we are the last writer. So, just close the journal. * If the freeaddr == post_epoch_freeaddr, wcs_flu may have skipped writing * a pini, so allow for that. */ assert(!jbp->before_images || is_mm || !we_are_last_writer || (0 != jpc->pini_addr) || jgbl.mur_extract || ((jbp->freeaddr == jbp->post_epoch_freeaddr) && (jbp->freeaddr == jbp->rsrv_freeaddr))); /* If we haven't written a pini, let jnl_file_close write the pini/pfin. */ if (!jgbl.mur_extract && (0 != jpc->pini_addr)) { jnl_write_pfin(csa); wrote_pfin = TRUE; } else wrote_pfin = FALSE; /* If not the last writer and no pending flush timer left, do jnl flush now. * But if we wrote a PFIN record, then we can instead do a "jnl_wait" which is * done outside CRIT and is equivalent to "jnl_flush". */ if (!we_are_last_writer && (0 > cnl->wcs_timers)) { if (wrote_pfin) do_jnlwait = TRUE; else if (SS_NORMAL == (jnl_status = jnl_flush(reg))) { assert(jbp->freeaddr == jbp->dskaddr); assert(jbp->freeaddr == jbp->rsrv_freeaddr); jnl_fsync(reg, jbp->dskaddr); assert(jbp->fsync_dskaddr == jbp->dskaddr); } else { send_msg_csa(CSA_ARG(csa) VARLSTCNT(9) ERR_JNLFLUSH, 2, JNL_LEN_STR(csd), ERR_TEXT, 2, RTS_ERROR_TEXT("Error with journal flush in gds_rundown"), jnl_status); assert(NOJNL == jpc->channel);/* jnl file lost has been triggered */ /* In this routine, all code that follows from here on does not * assume anything about the journaling characteristics of this * database so it is safe to continue execution even though * journaling got closed in the middle. */ } } if (!do_jnlwait) jnl_file_close(reg, we_are_last_writer, FALSE); } else send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg)); } if (!was_crit) rel_crit(reg); if (do_jnlwait) { jnl_wait(reg); /* Try to do db fsync outside crit */ jnl_file_close(reg, we_are_last_writer, FALSE); } } } if (we_are_last_writer && !FROZEN_CHILLED(csa)) { /* Flush the fileheader last and harden the file to disk */ if (!was_crit) grab_crit(reg, WS_42); /* To satisfy crit requirement in fileheader_sync() */ memset(csd->machine_name, 0, MAX_MCNAMELEN); /* clear the machine_name field */ if (we_are_last_user && !CHILLED_AUTORELEASE(csa)) { csd->shmid = INVALID_SHMID; csd->gt_shm_ctime.ctime = 0; if (!have_standalone_access) { /* "mupip_exit_handler" will delete semid later in "mur_close_file"-->"db_ipcs_reset" */ csd->semid = INVALID_SEMID; csd->gt_sem_ctime.ctime = 0; } } fileheader_sync(reg); if (!was_crit) rel_crit(reg); if (!is_mm) { GTM_DB_FSYNC(csa, udi->fd, rc); /* Sync it all */ if (-1 == rc) { RTS_ERROR_CSA_ABT(csa, VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_TEXT("Error during file sync at close"), errno); } } else { /* Now do final MM file sync before exit */ assert(csa->ti->total_blks == csa->total_blks); # ifdef _AIX GTM_DB_FSYNC(csa, udi->fd, rc); if (-1 == rc) # else if (-1 == MSYNC((caddr_t)csa->db_addrs[0], (caddr_t)csa->db_addrs[1])) # endif { RTS_ERROR_CSA_ABT(csa, VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_TEXT("Error during file sync at close"), errno); } } } else if (unsafe_last_writer && !cnl->lastwriterbypas_msg_issued) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_LASTWRITERBYPAS, 2, DB_LEN_STR(reg)); cnl->lastwriterbypas_msg_issued = TRUE; } } /* end if (!reg->read_only && !cnl->donotflush_dbjnl) */ /* We had canceled all db timers at start of rundown. In case as part of rundown (wcs_flu above), we had started * any timers, cancel them BEFORE setting reg->open to FALSE (assert in wcs_clean_dbsync relies on this). */ CANCEL_DB_TIMERS(reg, csa, canceled_dbsync_timer); if (reg->read_only && we_are_last_user && cnl->remove_shm) { /* mupip_exit_handler will do this after mur_close_file */ db_ipcs.open_fd_with_o_direct = udi->fd_opened_with_o_direct; db_ipcs.shmid = INVALID_SHMID; db_ipcs.gt_shm_ctime = 0; if (!have_standalone_access) { /* "mupip_exit_handler" will delete semid later in "mur_close_file"-->"db_ipcs_reset" */ db_ipcs.semid = INVALID_SEMID; db_ipcs.gt_sem_ctime = 0; } db_ipcs.fn_len = seg->fname_len; memcpy(db_ipcs.fn, seg->fname, seg->fname_len); db_ipcs.fn[seg->fname_len] = 0; /* request gtmsecshr to flush. read_only cannot flush itself */ WAIT_FOR_REPL_INST_UNFREEZE_SAFE(csa); if (!csa->read_only_fs && !csd->read_only) { secshrstat = send_mesg2gtmsecshr(FLUSH_DB_IPCS_INFO, 0, (char *)NULL, 0); if (0 != secshrstat) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(8) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_TEXT("gtmsecshr failed to update database file header")); } } if (!is_mm && csd->asyncio) { /* Cancel ALL pending async ios for this region by this process. Need to do this BEFORE detaching from database * shared memory OR closing the file descriptor (udi->fd) as the in-progress asyncio buffers/fd point there. */ # ifndef USE_LIBAIO WAIT_FOR_AIO_TO_BE_DONE(udi->fd, aiocancel_timedout); if (aiocancel_timedout) send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_AIOCANCELTIMEOUT, 3, process_id, DB_LEN_STR(reg)); # else /* Here, aio_shim_destroy() will destroy the thread and the kernel context associated with the entire global * directory -- so this effectively cancels all in-progress IOs to all subsequent regions that are about to * be rundown. This is necessary, however, because we need to cancel all in-progress IOs before the below * CLOSEFILE_RESET(). * * IOs canceled for subsequent regions will be reissued when we go to "gds_rundown" next and the * "wcs_flu"/wcs_wtstart()/aio_shim_write() happens, which will reopen the kernel context and multiplexing thread * as necessary. */ aio_shim_destroy(udi->owning_gd); # endif } /* If "reg" is a statsdb, it is possible that the basedb has asyncio turned on and has called for the statsdb to be * rundown first. A statsdb never has asyncio turned on. So it is possible that thread_gdi is non-NULL and will be * cleaned up as part of the "aio_shim_destroy" done in "gds_rundown" of the basedb but that will happen only after * the statsdb rundown completes. So account for that in the below assert. */ assert((NULL == udi->owning_gd->thread_gdi) || is_statsDB); udi->owning_gd = NULL; /* Done with file now, close it */ CLOSEFILE_RESET(udi->fd, rc); /* resets "udi->fd" to FD_INVALID */ if (-1 == rc) { RTS_ERROR_CSA_ABT(csa, VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("Error during file close"), errno); } if (NULL != csa->db_addrs[0]) { /* Unmap storage if mm mode */ if (is_mm) { assert(csa->db_addrs[1] > csa->db_addrs[0]); # if !defined(_AIX) munmap_len = (sm_long_t)(1 + csa->db_addrs[1] - csa->db_addrs[0]); /* Note: 1 + to compensate for * -1 done in "db_init". */ assert(0 < munmap_len); status = munmap((caddr_t)(csa->db_addrs[0]), (size_t)(munmap_len)); assert(0 == status); # endif } /* If this is a BASEDB and we are the last one out, prepare to unlink/remove the corresponding STATSDB */ if (we_are_last_user) UNLINK_STATSDB_AT_BASEDB_RUNDOWN(cnl); } /* Detach our shared memory while still under lock so reference counts will be correct for the next process to run down * this region. In the process also get the remove_shm status from node_local before detaching. * If cnl->donotflush_dbjnl is TRUE, it means we can safely remove shared memory without compromising data * integrity as a reissue of recover will restore the database to a consistent state. */ remove_shm = !vermismatch && (cnl->remove_shm || cnl->donotflush_dbjnl) && !CHILLED_AUTORELEASE(csa); /* We are done with online rollback on this region. Indicate to other processes by setting the onln_rlbk_pid to 0. * Do it before releasing crit (t_end relies on this ordering when accessing cnl->onln_rlbk_pid). */ if (jgbl.onlnrlbk) cnl->onln_rlbk_pid = 0; rel_crit(reg); /* Since we are about to detach from the shared memory, release crit and reset onln_rlbk_pid */ /* If we had skipped flushing journal and database buffers due to a concurrent online rollback, increment the counter * indicating that in the shared memory so that online rollback can report the # of such processes when it shuts down. * The same thing is done for both FTOK and access control semaphores when there are too many MUMPS processes. */ if (safe_mode) /* indicates flushing was skipped */ { if (bypassed_access) cnl->dbrndwn_access_skip++; /* Access semaphore can be bypassed during online rollback */ if (bypassed_ftok) cnl->dbrndwn_ftok_skip++; } if (MLK_CTL_BLKHASH_EXT == csa->mlkctl->blkhash) { csa->mlkhash_shmid = csa->mlkctl->hash_shmid; if (NULL != csa->mlkhash) SHMDT(csa->mlkhash); } else csa->mlkhash_shmid = INVALID_SHMID; if (jgbl.onlnrlbk) csa->hold_onto_crit = FALSE; GTM_WHITE_BOX_TEST(WBTEST_HOLD_SEM_BYPASS, cnl->wbox_test_seq_num, 0); if (csa->now_crit) { /* Ensure that we don't hold crit before detaching shared memory */ assert(!csa->now_crit); rel_crit(reg); } /* Dereferencing nl or hdr+friends after detach is not right; Nullify ahead of the detach operation so that concurrent * code, e.g. signal handlers, can test before a dereference the occurs in the middle of a detach. */ csa->nl = NULL; cs_data = csd = csa->hdr = NULL; status = SHMDT((caddr_t)cnl); /* Note that although csa->nl is NULL, we use CSA_ARG(csa) below (not CSA_ARG(NULL)) to be consistent with similar * usages before csa->nl became NULL. The "is_anticipatory_freeze_needed" function (which is in turn called by the * CHECK_IF_FREEZE_ON_ERROR_NEEDED macro) does a check of csa->nl before dereferencing shared memory contents so * we are safe passing "csa". */ if (-1 == status) send_msg_csa(CSA_ARG(csa) VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("Error during shmdt"), errno); REMOVE_CSA_FROM_CSADDRSLIST(csa); /* remove "csa" from list of open regions (cs_addrs_list) */ reg->open = FALSE; assert(!is_statsDB || process_exiting || IS_GTCM_GNP_SERVER_IMAGE); /* If file is still not in good shape, die here and now before we get rid of our storage */ assertpro(0 == csa->wbuf_dqd); ipc_deleted = FALSE; /* If we are the very last user, remove shared storage id and the semaphores */ if (we_are_last_user) { /* remove shared storage, only if last writer to rundown did a successful "wcs_flu" */ assert(!vermismatch); if (remove_shm) { ipc_deleted = TRUE; if (INVALID_SHMID != csa->mlkhash_shmid) { if (0 != shm_rmid(csa->mlkhash_shmid)) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(8) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_TEXT("Unable to remove lock shared memory")); } if (0 != shm_rmid(udi->shmid)) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(8) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_TEXT("Unable to remove shared memory")); /* Note that this process deleted shared memory. Currently only used by rollback. */ udi->shm_deleted = TRUE; /* mupip recover/rollback don't release the semaphore here, but do it later in "db_ipcs_reset" * (invoked from "mur_close_files") */ if (!have_standalone_access) { if (0 != sem_rmid(udi->semid)) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_TEXT("Unable to remove semaphore"), errno); udi->sem_deleted = TRUE; /* Note that we deleted the semaphore */ udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; } if (is_statsDB) { STATSDBREG_TO_BASEDBREG(reg, baseDBreg); baseDBcsa = &FILE_INFO(baseDBreg)->s_addrs; baseDBnl = baseDBcsa->nl; baseDBnl->statsdb_rundown_clean = TRUE; } } else if (is_src_server || is_updproc) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_DBRNDWNWRN, 4, DB_LEN_STR(reg), process_id, process_id); send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_DBRNDWNWRN, 4, DB_LEN_STR(reg), process_id, process_id); } else send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_DBRNDWNWRN, 4, DB_LEN_STR(reg), process_id, process_id); } else { assert(!have_standalone_access || jgbl.onlnrlbk || safe_mode); if (!jgbl.onlnrlbk && !have_standalone_access) { /* If we were writing, get rid of our writer access count semaphore */ if (!reg->read_only) { if (!access_counter_halted) { save_errno = do_semop(udi->semid, DB_COUNTER_SEM, -DB_COUNTER_SEM_INCR, SEM_UNDO); if (0 != save_errno) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(12) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, RTS_ERROR_TEXT("gds_rundown access control semaphore decrement"), CALLFROM, save_errno); } udi->counter_acc_incremented = FALSE; } assert(safe_mode || !bypassed_access); /* Now remove the rundown lock */ if (!bypassed_access) { if (0 != (save_errno = do_semop(udi->semid, DB_CONTROL_SEM, -1, SEM_UNDO))) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(12) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, RTS_ERROR_TEXT("gds_rundown access control semaphore release"), CALLFROM, save_errno); udi->grabbed_access_sem = FALSE; } } /* else access control semaphore will be released in db_ipcs_reset */ } if (!have_standalone_access) { if (bypassed_ftok) { if (!ftok_counter_halted) if (0 != (save_errno = do_semop(udi->ftok_semid, DB_COUNTER_SEM, -DB_COUNTER_SEM_INCR, SEM_UNDO))) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBFILERR, 2, DB_LEN_STR(reg)); } else if (!ftok_sem_release(reg, !ftok_counter_halted, FALSE)) { FTOK_TRACE(csa, csa->ti->curr_tn, ftok_ops_release, process_id); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBFILERR, 2, DB_LEN_STR(reg)); } udi->grabbed_ftok_sem = FALSE; udi->counter_ftok_incremented = FALSE; } ENABLE_INTERRUPTS(INTRPT_IN_GDS_RUNDOWN, prev_intrpt_state); if (!ipc_deleted) { GET_CUR_TIME(time_str); if (is_src_server) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_IPCNOTDEL, 6, CTIME_BEFORE_NL, time_str, LEN_AND_LIT("Source server"), REG_LEN_STR(reg)); if (is_updproc) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_IPCNOTDEL, 6, CTIME_BEFORE_NL, time_str, LEN_AND_LIT("Update process"), REG_LEN_STR(reg)); if (mupip_jnl_recover && (!jgbl.onlnrlbk || !we_are_last_user)) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_IPCNOTDEL, 6, CTIME_BEFORE_NL, time_str, LEN_AND_LIT("Mupip journal process"), REG_LEN_STR(reg)); send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_IPCNOTDEL, 6, CTIME_BEFORE_NL, time_str, LEN_AND_LIT("Mupip journal process"), REG_LEN_STR(reg)); } } REVERT; /* Now that "gds_rundown" is done, free up the memory associated with the region as long as the caller is okay with it */ if (cleanup_udi) { if (NULL != csa->dir_tree) FREE_CSA_DIR_TREE(csa); if (csa->sgm_info_ptr) { si = csa->sgm_info_ptr; /* It is possible we got interrupted before initializing all fields of "si" * completely so account for NULL values while freeing/releasing those fields. */ assert((si->tp_csa == csa) || (NULL == si->tp_csa)); if (si->jnl_tail) { PROBE_FREEUP_BUDDY_LIST(si->format_buff_list); PROBE_FREEUP_BUDDY_LIST(si->jnl_list); FREE_JBUF_RSRV_STRUCT(si->jbuf_rsrv_ptr); } PROBE_FREEUP_BUDDY_LIST(si->recompute_list); PROBE_FREEUP_BUDDY_LIST(si->new_buff_list); PROBE_FREEUP_BUDDY_LIST(si->tlvl_info_list); PROBE_FREEUP_BUDDY_LIST(si->tlvl_cw_set_list); PROBE_FREEUP_BUDDY_LIST(si->cw_set_list); if (NULL != si->blks_in_use) { free_hashtab_int8(si->blks_in_use); free(si->blks_in_use); si->blks_in_use = NULL; } if (si->cr_array_size) { assert(NULL != si->cr_array); if (NULL != si->cr_array) free(si->cr_array); } if (NULL != si->first_tp_hist) free(si->first_tp_hist); free(si); } if (csa->jnl) { assert(&FILE_INFO(csa->jnl->region)->s_addrs == csa); free(csa->jnl); } assert(seg->file_cntl->file_info); free(seg->file_cntl->file_info); free(seg->file_cntl); assert(NULL == ftok_sem_reg); ftok_sem_reg = NULL; /* always assigning this is less burden than an if mirroring the above assert */ seg->file_cntl = NULL; } return EXIT_NRM; } fis-gtm-V7.0-005/sr_unix/gds_rundown_ch.c0000644000032200000250000000207214342376327017217 0ustar librarygtc/**************************************************************** * * * Copyright 2013, 2014 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 "error.h" GBLREF boolean_t created_core, need_core, dont_want_core; #ifdef DEBUG #include "have_crit.h" GBLREF boolean_t ok_to_UNWIND_in_exit_handling; #endif CONDITION_HANDLER(gds_rundown_ch) { START_CH(TRUE); /* To get as virgin a state as possible in the core, take the core now if we * would be doing so anyway. This will set created_core so it doesn't happen again. */ if (DUMPABLE && !SUPPRESS_DUMP) { need_core = TRUE; gtm_fork_n_core(); } assert(INTRPT_IN_GDS_RUNDOWN == intrpt_ok_state); PRN_ERROR; DEBUG_ONLY(ok_to_UNWIND_in_exit_handling = TRUE); UNWIND(NULL, NULL); } fis-gtm-V7.0-005/sr_unix/gds_rundown_err_cleanup.c0000644000032200000250000000733314342376327021131 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2016 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gds_rundown.h" #include "jnl.h" #include "gtm_semutils.h" #include "do_semop.h" #include "add_inter.h" #include "ftok_sems.h" #include #include "wcs_clean_dbsync.h" #include "interlock.h" #include "wbox_test_init.h" #include "gds_rundown_err_cleanup.h" GBLREF gd_region *gv_cur_region; GBLREF jnl_gbls_t jgbl; GBLREF uint4 process_id; error_def(ERR_TEXT); error_def(ERR_DBRNDWN); void gds_rundown_err_cleanup(boolean_t have_standalone_access) { pid_t sem_pid; int semop_res; unix_db_info *udi; sgmnt_addrs *csa; boolean_t cancelled_dbsync_timer; /* Here, we can not rely on the validity of csa->hdr because this function can be triggered anywhere in * gds_rundown().Because we don't have access to file header, we can not know if counters are disabled so we go by our best * guess, not disabled, during cleanup. */ udi = FILE_INFO(gv_cur_region); csa = &udi->s_addrs; /* We got here on an error and are going to close the region. Cancel any pending flush timer for this region by this task*/ CANCEL_DB_TIMERS(gv_cur_region, csa, cancelled_dbsync_timer); if (csa->now_crit) /* Might hold crit if wcs_flu or other failure */ { assert(!csa->hold_onto_crit || jgbl.onlnrlbk); if (NULL != csa->nl) rel_crit(gv_cur_region); /* also sets csa->now_crit to FALSE */ else csa->now_crit = FALSE; } if (!have_standalone_access) { if (udi->counter_acc_incremented) { if (0 != (semop_res = do_semop(udi->semid, DB_COUNTER_SEM, -DB_COUNTER_SEM_INCR, SEM_UNDO | IPC_NOWAIT))) send_msg_csa(CSA_ARG(csa) VARLSTCNT(9) ERR_CRITSEMFAIL, 2, DB_LEN_STR(gv_cur_region), ERR_TEXT, 2, RTS_ERROR_TEXT("Error decreasing access semaphore counter"), semop_res); udi->counter_acc_incremented = FALSE; } if (udi->grabbed_access_sem) { /* release the access control semaphore, if you hold it */ sem_pid = semctl(udi->semid, 0, GETPID); assert(sem_pid == process_id); if (0 != (semop_res = do_semop(udi->semid, DB_CONTROL_SEM, -1, SEM_UNDO | IPC_NOWAIT))) send_msg_csa(CSA_ARG(csa) VARLSTCNT(9) ERR_CRITSEMFAIL, 2, DB_LEN_STR(gv_cur_region), ERR_TEXT, 2, RTS_ERROR_TEXT("Error releasing access semaphore"), semop_res); udi->grabbed_access_sem = FALSE; } } if (udi->grabbed_ftok_sem) { /* Decrease counter and release ftok */ assert(!have_standalone_access); /* See gv_rundown.c comment for why ftok_sem_release 2nd parameter is FALSE below */ ftok_sem_release(gv_cur_region, FALSE, TRUE); } else if (udi->counter_ftok_incremented) /* Just decrease ftok counter */ { if (0 != (semop_res = do_semop(udi->ftok_semid, DB_COUNTER_SEM, -DB_COUNTER_SEM_INCR, SEM_UNDO | IPC_NOWAIT))) send_msg_csa(CSA_ARG(csa) VARLSTCNT(9) ERR_CRITSEMFAIL, 2, DB_LEN_STR(gv_cur_region), ERR_TEXT, 2, RTS_ERROR_TEXT("Error decreasing ftok semaphore counter"), semop_res); udi->counter_ftok_incremented = FALSE; } gv_cur_region->open = FALSE; csa->nl = NULL; REMOVE_CSA_FROM_CSADDRSLIST(csa); /* remove "csa" from list of open regions (cs_addrs_list) */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_DBRNDWN, 2, REG_LEN_STR(gv_cur_region)); } fis-gtm-V7.0-005/sr_unix/gds_rundown_err_cleanup.h0000644000032200000250000000113714342376327021132 0ustar librarygtc/**************************************************************** * * * Copyright 2013, 2014 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. * * * ****************************************************************/ #ifndef GDS_RUNDOWN_ERR_CLEANUP_INCLUDED #define GDS_RUNDOWN_ERR_CLEANUP_INCLUDED void gds_rundown_err_cleanup(boolean_t have_standalone_access); #endif fis-gtm-V7.0-005/sr_unix/gdsfheadsp.h0000755000032200000250000000133014342376330016322 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GDSFHEADSP_H_INCLUDED #define GDSFHEADSP_H_INCLUDED int dsk_write_nocache(gd_region *r, block_id blk, sm_uc_ptr_t buff, enum db_ver ondsk_blkver); void wcs_clean_dbsync_timer(sgmnt_addrs *csa); #endif fis-gtm-V7.0-005/sr_unix/gdsfilext.c0000644000032200000250000005702014342376330016202 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "gtm_time.h" #include #ifdef _AIX #include #endif #include #include "gtm_unistd.h" #include "gtm_signal.h" #include "buddy_list.h" #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbml.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdscc.h" #include "filestruct.h" #include "gtmio.h" #include "iosp.h" #include "jnl.h" #include "tp.h" #include "eintr_wrappers.h" #include "send_msg.h" #include "gt_timer.h" #include "mmseg.h" #include "gdsblk.h" /* needed for gds_blk_downgrade.h */ #include "gds_blk_downgrade.h" /* for IS_GDS_BLK_DOWNGRADE_NEEDED macro */ #include "wbox_test_init.h" #include "anticipatory_freeze.h" /* Include prototypes */ #include "bit_set.h" #include "disk_block_available.h" #include "gds_map_moved.h" #include "gtmmsg.h" #include "gdsfilext.h" #include "bm_getfree.h" #include "gtmimagename.h" #include "gtmdbglvl.h" #include "min_max.h" #include "repl_msg.h" #include "gtmsource.h" #include "error.h" #include "wcs_backoff.h" #include "wcs_wt.h" #include "db_write_eof_block.h" #include "interlock.h" #include "warn_db_sz.h" #define GDSFILEXT_CLNUP \ MBSTART { \ if (!was_crit) \ rel_crit(gv_cur_region); \ } MBEND #define ISSUE_WAITDSKSPACE(TO_WAIT, WAIT_PERIOD, MECHANISM) \ MBSTART { \ uint4 seconds; \ \ seconds = TO_WAIT + (CDB_STAGNATE - t_tries) * WAIT_PERIOD; \ MECHANISM(CSA_ARG(cs_addrs) VARLSTCNT(11) ERR_WAITDSKSPACE, 4, process_id, seconds, DB_LEN_STR(gv_cur_region), ERR_TEXT,\ 2, LEN_AND_LIT("Please make more disk space available or shutdown GT.M to avoid data loss"), ENOSPC); \ } MBEND #define SUSPICIOUS_EXTEND (2 * (dollar_tlevel ? sgm_info_ptr->cw_set_depth : cw_set_depth) < cs_addrs->ti->free_blocks) GBLREF sigset_t blockalrm; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF unsigned char cw_set_depth; GBLREF uint4 dollar_tlevel; GBLREF gd_region *gv_cur_region; GBLREF inctn_opcode_t inctn_opcode; GBLREF boolean_t mu_reorg_process; GBLREF uint4 process_id; GBLREF sgm_info *sgm_info_ptr; GBLREF unsigned int t_tries; GBLREF jnl_gbls_t jgbl; GBLREF inctn_detail_t inctn_detail; /* holds detail to fill in to inctn jnl record */ GBLREF boolean_t gtm_dbfilext_syslog_disable; /* control whether db file extension message is logged or not */ GBLREF uint4 gtmDebugLevel; GBLREF jnlpool_addrs_ptr_t jnlpool; error_def(ERR_DBFILERR); error_def(ERR_DBFILEXT); error_def(ERR_DSKSPACEFLOW); error_def(ERR_JNLFLUSH); error_def(ERR_NOSPACEEXT); error_def(ERR_PREALLOCATEFAIL); error_def(ERR_SYSCALL); error_def(ERR_TEXT); error_def(ERR_TOTALBLKMAX); error_def(ERR_WAITDSKSPACE); OS_PAGE_SIZE_DECLARE STATICFNDCL int extend_wait_for_write(unix_db_info *udi, int blk_size, off_t new_eof); STATICFNDEF int extend_wait_for_write(unix_db_info *udi, int blk_size, off_t new_eof) { int to_wait, to_msg, wait_period, save_errno; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Attempt to write every second, and send message to operator every 1/20 of cs_data->wait_disk_space */ wait_period = to_wait = DIVIDE_ROUND_UP(cs_data->wait_disk_space, CDB_STAGNATE + 1); to_msg = (to_wait / 8) ? (to_wait / 8) : 1; /* send around 8 messages during 1 wait_period */ do { if ((to_wait == cs_data->wait_disk_space) || (to_wait % to_msg == 0)) ISSUE_WAITDSKSPACE(to_wait, wait_period, send_msg_csa); hiber_start(1000); to_wait--; save_errno = db_write_eof_block(udi, udi->fd, blk_size, new_eof, &(TREF(dio_buff))); } while ((to_wait > 0) && (ENOSPC == save_errno)); return save_errno; } int4 gdsfilext(block_id blocks, block_id filesize, boolean_t trans_in_prog) { sm_uc_ptr_t old_base[2], mmap_retaddr; boolean_t was_crit, is_mm; int fd, result, save_errno, status, to_msg, to_wait, wait_period; DEBUG_ONLY(int first_save_errno); block_id new_bit_maps, bplmap, map, new_blocks, new_total, max_tot_blks, old_total, temp_blk; uint4 jnl_status; gtm_uint64_t avail_blocks, mmap_sz; off_t new_eof, new_size, old_size; trans_num curr_tn; unix_db_info *udi; inctn_opcode_t save_inctn_opcode; block_id prev_extend_blks_to_upgrd; jnl_private_control *jpc; jnl_buffer_ptr_t jbp; cache_rec_ptr_t cr; jnlpool_addrs_ptr_t local_jnlpool; /* needed by INST_FREEZE_ON_NOSPC_ENABLED */ char *db_file_name = ""; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!IS_DSE_IMAGE); assert((cs_addrs->nl == NULL) || (process_id != cs_addrs->nl->trunc_pid)); /* mu_truncate shouldn't extend file... */ assert(!process_exiting); DEBUG_ONLY(old_base[0] = old_base[1] = NULL); assert(!gv_cur_region->read_only); udi = FILE_INFO(gv_cur_region); is_mm = (dba_mm == cs_addrs->hdr->acc_meth); # if !defined(MM_FILE_EXT_OK) if (!udi->grabbed_access_sem && is_mm) return NO_FREE_SPACE; /* should this be changed to show extension not allowed ? */ # endif /* Make sure that there is enough space left in the database to add the requested blocks */ assert((blocks <= (MAXTOTALBLKS(cs_data) - cs_data->trans_hist.total_blks)) || WBTEST_ENABLED(WBTEST_FILE_EXTEND_ERROR)); if (!blocks && (cs_data->defer_allocate || (TRANS_IN_PROG_TRUE == trans_in_prog))) return NO_FREE_SPACE; /* should this be changed to show extension not enabled ? */ bplmap = cs_data->bplmap; /* New total of non-bitmap blocks will be number of current, non-bitmap blocks, plus new blocks desired * There are (bplmap - 1) non-bitmap blocks per bitmap, so add (bplmap - 2) to number of non-bitmap blocks * and divide by (bplmap - 1) to get total number of bitmaps for expanded database. (must round up in this * manner as every non-bitmap block must have an associated bitmap) * Current number of bitmaps is (total number of current blocks + bplmap - 1) / bplmap. * Subtract current number of bitmaps from number needed for expanded database to get number of new bitmaps needed. */ new_bit_maps = DIVIDE_ROUND_UP(cs_data->trans_hist.total_blks - DIVIDE_ROUND_UP(cs_data->trans_hist.total_blks, bplmap) + blocks, bplmap - 1) - DIVIDE_ROUND_UP(cs_data->trans_hist.total_blks, bplmap); new_blocks = blocks + new_bit_maps; assert((0 < (int)new_blocks) || (!cs_data->defer_allocate && (0 == new_blocks))); if (new_blocks + cs_data->trans_hist.total_blks > MAXTOTALBLKS(cs_data)) { assert(WBTEST_ENABLED(WBTEST_FILE_EXTEND_ERROR)); send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) ERR_TOTALBLKMAX); return NO_FREE_SPACE; } if (0 != (save_errno = disk_block_available(udi->fd, &avail_blocks, FALSE))) { send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(gv_cur_region), save_errno); RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(gv_cur_region), save_errno); } else { if (!(gtmDebugLevel & GDL_IgnoreAvailSpace)) { /* Bypass this space check if debug flag above is on. Allows us to create a large sparce DB * in space it could never fit it if wasn't sparse. Needed for some tests. */ avail_blocks = avail_blocks / (cs_data->blk_size / DISK_BLOCK_SIZE); if ((blocks * EXTEND_WARNING_FACTOR) > avail_blocks) { if (blocks > avail_blocks) { if (!INST_FREEZE_ON_NOSPC_ENABLED(cs_addrs, local_jnlpool)) return NO_FREE_SPACE; else send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(6) MAKE_MSG_WARNING(ERR_NOSPACEEXT), 4, DB_LEN_STR(gv_cur_region), &new_blocks, &avail_blocks); } else { temp_blk = avail_blocks - ((new_blocks <= avail_blocks) ? new_blocks : 0); send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_DSKSPACEFLOW, 3, DB_LEN_STR(gv_cur_region), &temp_blk); } } } } # ifdef DEBUG if (((WBTEST_ENABLED(WBTEST_MM_CONCURRENT_FILE_EXTEND) && dollar_tlevel && !MEMCMP_LIT(gv_cur_region->rname, "DEFAULT")) || (WBTEST_ENABLED(WBTEST_WSSTATS_PAUSE) && (10 == gtm_white_box_test_case_count) && !MEMCMP_LIT(gv_cur_region->rname, "DEFAULT"))) && !cs_addrs->now_crit) { /* Sync with copy in bm_getfree() */ /* Not clear to me why the WBTEST_MM_CONCURRENT_FILE_EXTEND doesn't need something similar, but we don't want our * child to come here. Unsetting the env shouldn't affect the parent, it reads env just once at process startup*/ if(WBTEST_ENABLED(WBTEST_WSSTATS_PAUSE)) unsetenv("gtm_white_box_test_case_enable"); SYSTEM("$gtm_dist/mumps -run $gtm_wbox_mrtn"); assert(1 == cs_addrs->nl->wbox_test_seq_num); /* should have been set by mubfilcpy */ cs_addrs->nl->wbox_test_seq_num = 2; /* signal mupip backup to stop sleeping in mubfilcpy */ } # endif /* From here on, we need to use GDSFILEXT_CLNUP before returning to the caller */ was_crit = cs_addrs->now_crit; assert(!cs_addrs->hold_onto_crit || was_crit); /* If we are coming from mupip_extend (which gets crit itself) we better have waited for any unfreezes to occur. * If we are coming from online rollback (when that feature is available), we will come in holding crit and in * the final retry. In that case too, we expect to have waited for unfreezes to occur in the caller itself. * Therefore if we are coming in holding crit from MUPIP, we expect the db to be unfrozen so no need to wait for * freeze. * If we are coming from GT.M and final retry (in which case we come in holding crit) we expect to have waited * for any unfreezes (by invoking tp_crit_all_regions) to occur (TP or non-TP) before coming into this * function. However, there is one exception. In the final retry, if tp_crit_all_regions notices that * at least one of the participating regions did ONLY READs, it will not wait for any freeze on THAT region * to complete before grabbing crit. Later, in the final retry, if THAT region did an update which caused * op_tcommit to invoke bm_getfree->gdsfilext, then we would have come here with a frozen region on which * we hold crit. */ assert(!was_crit || !FROZEN_HARD(cs_addrs) || (dollar_tlevel && (CDB_STAGNATE <= t_tries))); /* * If we are in the final retry and already hold crit, it is possible that csa->nl->wc_blocked is also set to TRUE * (by a concurrent process in phase2 which encountered an error in the midst of commit and secshr_db_clnup * finished the job for it). In this case we do NOT want to invoke wcs_recover as that will update the "bt" * transaction numbers without correspondingly updating the history transaction numbers (effectively causing * a cdb_sc_blkmod type of restart). Therefore do NOT call grab_crit (which unconditionally invokes wcs_recover) * if we already hold crit. */ if (!was_crit) { for ( ; ; ) { grab_crit(gv_cur_region, WS_12); if (FROZEN_CHILLED(cs_addrs)) DO_CHILLED_AUTORELEASE(cs_addrs, cs_data); assert(FROZEN(cs_data) || !cs_addrs->jnlpool || (cs_addrs->jnlpool == jnlpool)); if (!FROZEN(cs_data) && !IS_REPL_INST_FROZEN) break; rel_crit(gv_cur_region); while (FROZEN(cs_data) || IS_REPL_INST_FROZEN) { hiber_start(1000); if (FROZEN_CHILLED(cs_addrs) && CHILLED_AUTORELEASE(cs_addrs)) break; } } } else if (FROZEN_HARD(cs_addrs) && dollar_tlevel) { /* We don't want to continue with file extension as explained above. Hence return with an error code which * op_tcommit will recognize (as a cdb_sc_needcrit/cdb_sc_instancefreeze type of restart) and restart accordingly. */ assert(CDB_STAGNATE <= t_tries); GDSFILEXT_CLNUP; return FINAL_RETRY_FREEZE_PROG; } else WAIT_FOR_REGION_TO_UNCHILL(cs_addrs, cs_data); assert(!cs_addrs->jnlpool || (cs_addrs->jnlpool == jnlpool)); if (IS_REPL_INST_FROZEN && trans_in_prog) { assert((CDB_STAGNATE <= t_tries) || TREF(in_bm_getfree_gdsfilext)); GDSFILEXT_CLNUP; return FINAL_RETRY_INST_FREEZE; } assert(cs_addrs->ti->total_blks == cs_data->trans_hist.total_blks); old_total = cs_data->trans_hist.total_blks; if (old_total != filesize) { /* Somebody else has already extended it, since we are in crit, this is trust-worthy. However, in case of MM, * we still need to remap the database */ assert((old_total > filesize) || !is_mm); /* For BG, someone else could have truncated or extended - we have no idea */ GDSFILEXT_CLNUP; return SS_NORMAL; } if (trans_in_prog && SUSPICIOUS_EXTEND) { assertpro(is_free_blks_ctr_ok()); if (!was_crit) { GDSFILEXT_CLNUP; return EXTEND_SUSPECT; } } if (JNL_ENABLED(cs_data)) { if (!jgbl.dont_reset_gbl_jrec_time) SET_GBL_JREC_TIME; /* needed before jnl_ensure_open as that can write jnl records */ jpc = cs_addrs->jnl; jbp = jpc->jnl_buff; /* Before writing to jnlfile, adjust jgbl.gbl_jrec_time if needed to maintain time order * of jnl records. This needs to be done BEFORE the jnl_ensure_open as that could write * journal records (if it decides to switch to a new journal file). */ ADJUST_GBL_JREC_TIME(jgbl, jbp); jnl_status = jnl_ensure_open(gv_cur_region, cs_addrs); if (jnl_status) { GDSFILEXT_CLNUP; send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(6) jnl_status, 4, JNL_LEN_STR(cs_data), DB_LEN_STR(gv_cur_region)); return NO_FREE_SPACE; /* should have better return status */ } } if (is_mm) { cs_addrs->nl->mm_extender_pid = process_id; status = wcs_wtstart(gv_cur_region, 0, NULL, NULL); cs_addrs->nl->mm_extender_pid = 0; assertpro(SS_NORMAL == status); old_base[0] = cs_addrs->db_addrs[0]; old_base[1] = cs_addrs->db_addrs[1]; cs_addrs->db_addrs[0] = NULL; /* don't rely on it until the mmap below */ # ifdef _AIX status = shmdt(old_base[0] - BLK_ZERO_OFF(cs_data->start_vbn)); # else status = munmap((caddr_t)old_base[0], (size_t)(old_base[1] - old_base[0])); # endif if (0 != status) { save_errno = errno; GDSFILEXT_CLNUP; send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(12) ERR_DBFILERR, 2, DB_LEN_STR(gv_cur_region), ERR_SYSCALL, 5, LEN_AND_STR(MEM_UNMAP_SYSCALL), CALLFROM, save_errno); return NO_FREE_SPACE; } } else { /* Due to concurrency issues, it is possible some process had issued a disk read of the GDS block# corresponding * to "old_total" right after a truncate wrote a GDS-block of zeros on disk (to signal end of the db file). * If so, the global buffer containing this block needs to be invalidated now as part of the extend. If not, it is * possible the EOF block on disk is now going to be overwritten by a properly initialized bitmap block (as part * of the gdsfilext below) while the global buffer continues to have an incorrect copy of that bitmap block and * this in turn would cause XXXX failures due to a bad bitmap block in shared memory. (GTM-7519) */ cr = db_csh_get(old_total); if ((NULL != cr) && ((cache_rec_ptr_t)CR_NOTVALID != cr)) { assert((0 == cr->dirty) && (0 == cr->bt_index) && !cr->stopped); cr->cycle++; cr->blk = CR_BLKEMPTY; } } CHECK_TN(cs_addrs, cs_data, cs_data->trans_hist.curr_tn); /* can issue rts_error TNTOOLARGE */ new_total = old_total + new_blocks; new_eof = BLK_ZERO_OFF(cs_data->start_vbn) + ((off_t)new_total * cs_data->blk_size); if (!cs_data->defer_allocate) { new_size = new_eof + cs_data->blk_size; old_size = new_blocks ? BLK_ZERO_OFF(cs_data->start_vbn) + ((off_t)old_total * cs_data->blk_size) : 0; fd = udi->fd; /* There seems to be a discrepancy between the manpage for posix_fallocate and the actual usage; * if you try to reserve space, it checks "current_size + len", versus "offset + len". This means that if * the file is more than half the size of the partition, the next posix_fallocate will fail * for a 0 increase use the documented way */ POSIX_FALLOCATE(fd, old_size, new_size - old_size, save_errno); DEBUG_ONLY(first_save_errno = save_errno); if ((ENOSPC == save_errno) && IS_GTM_IMAGE) { /* Attempt to fallocate every second, and send message to operator every 1/20 of cs_data->wait_disk_space */ wait_period = to_wait = DIVIDE_ROUND_UP(cs_data->wait_disk_space, CDB_STAGNATE + 1); to_msg = (to_wait / 8) ? (to_wait / 8) : 1; /* send around 8 messages during 1 wait_period */ do { if ((to_wait == cs_data->wait_disk_space) || (to_wait % to_msg == 0)) ISSUE_WAITDSKSPACE(to_wait, wait_period, send_msg_csa); hiber_start(1000); to_wait--; POSIX_FALLOCATE(fd, old_size, new_size - old_size, save_errno); } while ((to_wait > 0) && (ENOSPC == save_errno)); } if (0 != save_errno) { GDSFILEXT_CLNUP; assert(ENOSPC == save_errno); if (ENOSPC != save_errno) send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_PREALLOCATEFAIL, 2, DB_LEN_STR(gv_cur_region), save_errno); return NO_FREE_SPACE; } } save_errno = db_write_eof_block(udi, udi->fd, cs_data->blk_size, new_eof, &(TREF(dio_buff))); if ((ENOSPC == save_errno) && IS_GTM_IMAGE) save_errno = extend_wait_for_write(udi, cs_data->blk_size, new_eof); if (0 != save_errno) { GDSFILEXT_CLNUP; if (ENOSPC != save_errno) send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(gv_cur_region), save_errno); return NO_FREE_SPACE; } if (WBTEST_ENABLED(WBTEST_FILE_EXTEND_INTERRUPT_1)) { LONG_SLEEP(600); assert(FALSE); } /* Ensure the EOF and metadata get to disk BEFORE any bitmap writes. Otherwise, the file size could no longer reflect * a proper extent and subsequent invocations of gdsfilext could corrupt the database. */ if (!IS_STATSDB_CSA(cs_addrs)) { GTM_DB_FSYNC(cs_addrs, udi->fd, status); assert(0 == status); if (0 != status) { GDSFILEXT_CLNUP; send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(8) ERR_DBFILERR, 5, RTS_ERROR_LITERAL("fsync1()"), CALLFROM, status); return NO_FREE_SPACE; } } if (WBTEST_ENABLED(WBTEST_FILE_EXTEND_INTERRUPT_2)) { LONG_SLEEP(600); assert(FALSE); /* Should be killed before that */ } DEBUG_ONLY(prev_extend_blks_to_upgrd = cs_data->blks_to_upgrd;) /* inctn_detail.blks_to_upgrd_delta holds the increase in "csd->blks_to_upgrd" due to the file extension */ inctn_detail.blks2upgrd_struct.blks_to_upgrd_delta = (IS_GDS_BLK_DOWNGRADE_NEEDED(cs_data->desired_db_format) ? new_bit_maps : 0); if (JNL_ENABLED(cs_data)) { save_inctn_opcode = inctn_opcode; if (mu_reorg_process) inctn_opcode = inctn_gdsfilext_mu_reorg; else inctn_opcode = inctn_gdsfilext_gtm; if (0 == jpc->pini_addr) jnl_write_pini(cs_addrs); jnl_write_inctn_rec(cs_addrs); inctn_opcode = save_inctn_opcode; /* Harden INCTN to disk before updating/flushing database. This will ensure that any positive adjustment to the * blks_to_upgrd counter (stored in the inctn record) is seen by recovery before a V4 format bitmap block is. */ jnl_status = jnl_flush(gv_cur_region); if (SS_NORMAL != jnl_status) { send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(9) ERR_JNLFLUSH, 2, JNL_LEN_STR(cs_data), ERR_TEXT, 2, RTS_ERROR_TEXT("Error with journal flush during gdsfilext"), jnl_status); assert(NOJNL == jpc->channel); /* jnl file lost has been triggered */ /* In this routine, all code that follows from here on does not assume anything about the * journaling characteristics of this database so it is safe to continue execution even though * journaling got closed in the middle. Let the caller deal with this situation. */ } } if (new_bit_maps) { for (map = ROUND_UP(old_total, bplmap); map < new_total; map += bplmap) { DEBUG_ONLY(new_bit_maps--;) if (SS_NORMAL != (status = bml_init(map))) { GDSFILEXT_CLNUP; send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(gv_cur_region), status); return NO_FREE_SPACE; } } assert(0 == new_bit_maps); } /* Ensures that if at all the file header write goes to disk before the crash, the bitmap blocks are all * guaranteed to be initialized and synced to disk as well. */ if (WBTEST_ENABLED(WBTEST_FILE_EXTEND_INTERRUPT_3)) { LONG_SLEEP(600); assert(FALSE); /* Should be killed before that */ } if (!IS_STATSDB_CSA(cs_addrs)) { GTM_DB_FSYNC(cs_addrs, udi->fd, status); assert(0 == status); if (0 != status) { GDSFILEXT_CLNUP; send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(8) ERR_DBFILERR, 5, RTS_ERROR_LITERAL("fsync2()"), CALLFROM, status); return NO_FREE_SPACE; } } if (WBTEST_ENABLED(WBTEST_FILE_EXTEND_INTERRUPT_4)) { LONG_SLEEP(600); assert(FALSE); /* Should be killed before that */ } assert(cs_data->blks_to_upgrd == (inctn_detail.blks2upgrd_struct.blks_to_upgrd_delta + prev_extend_blks_to_upgrd)); assert((0 < blocks) || (!cs_data->defer_allocate && (0 == new_blocks))); assert(0 < (cs_addrs->ti->free_blocks + blocks)); cs_addrs->ti->free_blocks += blocks; cs_addrs->total_blks = cs_addrs->ti->total_blks = new_total; blocks = old_total; if (blocks / bplmap * bplmap != blocks) { bit_set(blocks / bplmap, MM_ADDR(cs_data)); /* Mark old last local map as having space */ if (blocks > cs_addrs->nl->highest_lbm_blk_changed) cs_addrs->nl->highest_lbm_blk_changed = blocks; } curr_tn = cs_addrs->ti->curr_tn; assert(cs_addrs->ti->early_tn == cs_addrs->ti->curr_tn); /* do not increment transaction number for forward recovery */ if (!jgbl.forw_phase_recovery || JNL_ENABLED(cs_data)) { cs_data->trans_hist.early_tn = cs_data->trans_hist.curr_tn + 1; INCREMENT_CURR_TN(cs_data); } /* white box test for interrupted file extension */ if (WBTEST_ENABLED(WBTEST_FILE_EXTEND_INTERRUPT_5)) { LONG_SLEEP(600); assert(FALSE); /* Should be killed before that */ } fileheader_sync(gv_cur_region); /* white box test for interrupted file extension */ if (WBTEST_ENABLED(WBTEST_FILE_EXTEND_INTERRUPT_6)) { LONG_SLEEP(600); assert(FALSE); /* Should be killed before that */ } if (is_mm) { assert((NULL != old_base[0]) && (NULL != old_base[1])); mmap_sz = new_eof - BLK_ZERO_OFF(cs_data->start_vbn); /* Don't mmap the file header and master map */ CHECK_LARGEFILE_MMAP(gv_cur_region, mmap_sz); /* can issue rts_error MMFILETOOLARGE */ # ifdef _AIX status = (sm_long_t)(mmap_retaddr = (sm_uc_ptr_t)shmat(udi->fd, (void *)NULL,SHM_MAP)); # else status = (sm_long_t)(mmap_retaddr = (sm_uc_ptr_t)MMAP_FD(udi->fd, mmap_sz, BLK_ZERO_OFF(cs_data->start_vbn), FALSE)); # endif GTM_WHITE_BOX_TEST(WBTEST_MEM_MAP_SYSCALL_FAIL, status, -1); if (-1 == status) { save_errno = errno; WBTEST_ASSIGN_ONLY(WBTEST_MEM_MAP_SYSCALL_FAIL, save_errno, ENOMEM); GDSFILEXT_CLNUP; send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(12) ERR_DBFILERR, 2, DB_LEN_STR(gv_cur_region), ERR_SYSCALL, 5, LEN_AND_STR(MEM_MAP_SYSCALL), CALLFROM, save_errno); return NO_FREE_SPACE; } /* In addition to updating the internal map values, gds_map_moved sets cs_data to point to the remapped file */ # if defined(_AIX) mmap_retaddr = (sm_uc_ptr_t)mmap_retaddr + BLK_ZERO_OFF(cs_data->start_vbn); # endif gds_map_moved(mmap_retaddr, old_base[0], old_base[1], mmap_sz); /* updates cs_addrs->db_addrs[1] */ cs_addrs->db_addrs[0] = mmap_retaddr; assert(cs_addrs->db_addrs[0] < cs_addrs->db_addrs[1]); } GDSFILEXT_CLNUP; INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_db_extends, 1); if (!gtm_dbfilext_syslog_disable) { send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(7) ERR_DBFILEXT, 5, DB_LEN_STR(gv_cur_region), &blocks, &new_total, &curr_tn); if ((NULL != gv_cur_region) && (NULL != gv_cur_region->dyn.addr) && (0 != gv_cur_region->dyn.addr->fname_len)) db_file_name = (char *)gv_cur_region->dyn.addr->fname; else db_file_name = ""; warn_db_sz(db_file_name, blocks, new_total, MAXTOTALBLKS(cs_data)); } return SS_NORMAL; } fis-gtm-V7.0-005/sr_unix/gdsfilext_nojnl.c0000644000032200000250000001032014342376330017372 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2020 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 #include "gtm_string.h" #include "gtm_multi_thread.h" #include "gdsroot.h" #include "gdsblk.h" #include "gdsbml.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblkops.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "min_max.h" #include "t_qread.h" #include "dse.h" #include "gtmmsg.h" #include "t_begin.h" #include "t_write_map.h" #include "t_abort.h" #include "t_retry.h" #include "t_end.h" #include "wbox_test_init.h" #include "error.h" #include "t_recycled2free.h" #include "cdb_sc.h" #include "eintr_wrappers.h" #include "gtmimagename.h" #include "gdsfilext_nojnl.h" #include "gtmio.h" #include "anticipatory_freeze.h" #include "sleep_cnt.h" #include "wcs_sleep.h" #include "interlock.h" #include "gdsbgtr.h" #include "copy.h" #include "shmpool.h" #include "db_write_eof_block.h" #include "buddy_list.h" #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "hashtab_mname.h" /* needed for muprec.h */ #include "muprec.h" GBLREF jnl_gbls_t jgbl; error_def(ERR_DBFILERR); /* Minimal file extend. Called (at the moment) from mur_back_process.c when processing JRT_TRUNC record. * We want to avoid jnl and other interferences of gdsfilext. */ /* #GTM_THREAD_SAFE : The below function (gdsfilext_nojnl) is thread-safe */ int gdsfilext_nojnl(gd_region* reg, block_id new_total, block_id old_total) { sgmnt_addrs *csa; sgmnt_data_ptr_t csd; int blk_size, status; off_t offset; char *newmap, *aligned_buff; block_id ii; unix_db_info *udi; reg_ctl_list *rctl; dio_buff_t *diobuff; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; udi = FILE_INFO(reg); csa = &udi->s_addrs; csd = csa->hdr; assert(old_total < new_total); assert(new_total <= MAXTOTALBLKS(csd)); blk_size = csd->blk_size; offset = (off_t)BLK_ZERO_OFF(csd->start_vbn) + (off_t)new_total * blk_size; if (udi->fd_opened_with_o_direct) { if (multi_thread_in_use) { /* If multiple threads are running, we cannot use the global variable "dio_buff". Fortunately though, * the only caller of this function which can have "multi_thread_in_use" set is "mur_back_process" * which is invoked by a MUPIP JOURNAL command. Assert that. Given that, we can safely get at "rctl" * from csa->miscptr in this case and use "rctl->dio_buff" safely inside threaded code since each thread * operates on one "rctl". */ assert(jgbl.in_mupjnl); rctl = (reg_ctl_list *)csa->miscptr; assert(csa == rctl->csa); assert(csd == rctl->csd); DIO_BUFF_EXPAND_IF_NEEDED(udi, blk_size, &rctl->dio_buff); diobuff = &rctl->dio_buff; } else diobuff = &(TREF(dio_buff)); } status = db_write_eof_block(udi, udi->fd, blk_size, offset, diobuff); if (0 != status) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), status); return status; } /* initialize the bitmap tn's to 0. */ newmap = (char *)malloc(blk_size); bml_newmap((blk_hdr *)newmap, BM_SIZE(BLKS_PER_LMAP), 0, csd->desired_db_format); if (udi->fd_opened_with_o_direct) { aligned_buff = diobuff->aligned; memcpy(aligned_buff, newmap, blk_size); } else aligned_buff = newmap; /* initialize bitmaps, if any new ones are added */ for (ii = ROUND_UP(old_total, BLKS_PER_LMAP); ii < new_total; ii += BLKS_PER_LMAP) { offset = (off_t)BLK_ZERO_OFF(csd->start_vbn) + (off_t)ii * blk_size; DB_LSEEKWRITE(csa, udi, udi->fn, udi->fd, offset, aligned_buff, blk_size, status); if (0 != status) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), status); free(newmap); return status; } } csa->ti->free_blocks += DELTA_FREE_BLOCKS(new_total, old_total); csa->ti->total_blks = new_total; free(newmap); return status; } fis-gtm-V7.0-005/sr_unix/gdsfilext_nojnl.h0000644000032200000250000000125714342376330017410 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2019 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. * * * ****************************************************************/ #ifndef GDSFILEX_NOJNL_DEFINED #define GDSFILEX_NOJNL_DEFINED #include "gdsroot.h" int gdsfilext_nojnl(gd_region* reg, block_id new_total, block_id old_total); #endif fis-gtm-V7.0-005/sr_unix/gen_gtm_threadgbl_deftypes.csh0000644000032200000250000002052514342376330022103 0ustar librarygtc################################################################# # # # Copyright (c) 2010-2020 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. # # # ################################################################# # # Generate gtm_threadgbl_deftypes.h from gtm_threadgbl_deftypes.c. The result is a list of # #defines for the (real) types of the neutral-typed vars in the gtm_threadgbl structure. The # vars are neutral so not all types have to be defined in all modules. This mechanism allows # us to type only the vars used in the module. # # # We need to build this table twice - once for pro and once for dbg because the elements in the # table could be different sizes. So setup the necessary #ifdefs so the proper set is selected # when the module is built. One complication is that gtm_malloc_src.h gets built both as PRO and # DBG in the same module for a pro build. The DEBUG flag is overridden and the PRO build is # selected is PRO_BUILD is defined (set by gtm_malloc_dbg.c). # # Makefile builds don't have a bunch of stuff source $gtm_tools/gtm_env.csh setenv includge "" if ($#argv > 1) then setenv gtm_ver $1 shift setenv gtm_src $gtm_ver/$1 shift setenv gtm_obj $gtm_ver/$1 shift # need -I for this to work correctly foreach dir ($*) setenv includge "$includge -I${gtm_ver}/${dir}" end setenv includge "$includge -I${gtm_obj}" # aliases are not defined in the TCSH subshell source $gtm_ver/sr_unix/gtm_env.csh endif echo "Entering $0:t to build gtm_threadgbl_deftypes.h" pushd $gtm_obj \rm gtm_threadgbl_deftypes.h >& /dev/null if (-e gtm_threadgbl_deftypes.h) then echo "$0:t-E-BUILD: Error Unable to delete old $gtm_obj/gtm_threadgbl_deftypes.h - FAIL" exit 1 endif if (! -w $gtm_obj) then echo "$0:t-E-BUILD: Error Unable to write to $gtm_obj/gtm_threadgbl_deftypes.h - FAIL" exit 1 endif # # Now do pro build/run. Override the optimization setting to no-optimize since we don't need to spend time # optimizing this one time run (takes longer to optimize than to run unoptimized code). # gt_cc_pro -O0 $gtm_src/gtm_threadgbl_deftypes.c $includge >& gtm_threadgbl_deftypes_comp.log if (0 != $status) then echo "$0:t-E-BUILD: Error in pro build of $gtm_obj/gtm_threadgbl_deftypes, see $gtm_obj/gtm_threadgbl_deftypes_comp.log" popd exit 1 endif gt_ld -o gtm_threadgbl_deftypes_pro $gt_ld_options_pro -L$gtm_obj $gt_ld_sysrtns $gt_ld_syslibs \ gtm_threadgbl_deftypes.o >& gtm_threadgbl_deftypes_linkmap.txt if (0 != $status) then echo "$0:t-E-BUILD: Error in pro link of $gtm_obj/gtm_threadgbl_deftypes, see $gtm_obj/gtm_threadgbl_deftypes_linkmap.txt" popd exit 1 endif # # Do debug build # gt_cc_dbg $gtm_src/gtm_threadgbl_deftypes.c $includge >& gtm_threadgbl_deftypes_comp_dbg.log if (0 != $status) then echo "$0:t-E-BUILD: Error in dbg build of $gtm_obj/gtm_threadgbl_deftypes, see $gtm_obj/gtm_threadgbl_deftypes_comp_dbg.log" popd exit 1 endif gt_ld -o gtm_threadgbl_deftypes_dbg $gt_ld_options_dbg -L$gtm_obj $gt_ld_sysrtns $gt_ld_syslibs \ gtm_threadgbl_deftypes.o >& gtm_threadgbl_deftypes_linkmap_dbg.txt if (0 != $status) then echo "$0:t-E-BUILD: Error in dbg link of $gtm_obj/gtm_threadgbl_deftypes, see $gtm_obj/gtm_threadgbl_deftypes_linkmap_dbg.txt" popd exit 1 endif # # Create gtm_threadgbl_deftypes.h file - create in $gtm_obj first so we don't replace the $gtm_inc version # until/unless we know it is replaceable. # set year = `date +%Y` set ofile = "$gtm_obj/gtm_threadgbl_deftypes.h" echo "/****************************************************************" > $ofile sed 's/XXXX/2010/;s/YYYY/'$year'/;s/^/ */;s/$/*/' $gtm_tools/copyright.txt >> $ofile echo " ****************************************************************/" >> $ofile cat >> $ofile <& gtm_threadgbl_deftypes.h.diff if (0 == $status) then set keepold = 1 # if no diff, keep the old file else # # Do some more checking to see if there is "much" of a diff. Specifically, if only the version changed # in the comment, we will still replace the file but reset the creation date to what it was before so # a runall doesn't cause a full rebuild. # set diffcnt = `grep -E "<|>" gtm_threadgbl_deftypes.h.diff | grep -v "/* Generated by" | wc -l` if (0 == $diffcnt) then touch -r $gtm_inc/gtm_threadgbl_deftypes.h gtm_threadgbl_deftypes.h set keepold = 1 # not much diff (comments only) endif endif endif if (! $keepold) then if (-e $gtm_inc/gtm_threadgbl_deftypes.h) then \chmod 666 $gtm_inc/gtm_threadgbl_deftypes.h if (0 != $status) then echo "$0:t-E-BUILD: Error Unable to reset permissions to allow us to replace $gtm_inc/gtm_threadgbl_deftypes.h" popd exit 1 endif endif echo "Replacing $gtm_inc/gtm_threadgbl_deftypes.h" \mv -f gtm_threadgbl_deftypes.h $gtm_inc # replace if needed if (0 != $status) then echo "$0:t-E-BUILD: Error Unable to replace $gtm_inc/gtm_threadgbl_deftypes.h" popd exit 1 endif else echo "$gtm_inc/gtm_threadgbl_deftypes.h is current - not replaced" endif # # Now create the assembler based include files. Create one for each of pro/dbg. Similar to gtm_threadgbl_deftypes.h, # only replace them if they have changed. # set accesstxt = $gtm_tools/gtm_threadgbl_asm_access.txt set currprofull = $gtm_root/$gtm_curpro/pro \cp $gtm_pct/gtmthreadgblasm.m ./ set savestatus = "$status" if ("0" != "$savestatus") then echo "$0:t-E-BUILD: Error Unable to copy $gtm_tools/gtmthreadgblasm.m to `pwd`/gtmthreadgblasm.m" popd exit 1 endif foreach image (pro dbg) \rm -f gtm_threadgbl_deftypes_asm_${image}.fail ./gtm_threadgbl_deftypes_${image} > gtm_threadgbl_deftypes_asm_${image}.in env gtm_dist=$currprofull gtm_chset="M" gtmroutines=". $currprofull" \ $currprofull/mumps -run gtmthreadgblasm ${accesstxt} gtm_threadgbl_deftypes_asm_${image}.in \ gtm_threadgbl_deftypes_asm_${image}.si || touch gtm_threadgbl_deftypes_asm_${image}.fail if ( -e gtm_threadgbl_deftypes_asm_${image}.fail) then echo "$0:t-E-BUILD: Error Failed to create gtm_threadgbl_deftypes_asm_${image}.si" popd exit 1 endif set keepold = 0 if (-e $gtm_inc/gtm_threadgbl_deftypes_asm_${image}.si) then \rm -f gtm_threadgbl_deftypes_asm_${image}.si.diff \diff $gtm_inc/gtm_threadgbl_deftypes_asm_${image}.si gtm_threadgbl_deftypes_asm_${image}.si \ >& gtm_threadgbl_deftypes_asm_${image}.si.diff if (0 == $status) then set keepold = 1 # if no diff, keep the old file endif endif if (! $keepold) then \cp -p gtm_threadgbl_deftypes_asm_${image}.si $gtm_inc set savestatus = $status if (0 != $savestatus) then echo "$0:t-E-BUILD: Error Unable to copy gtm_threadgbl_deftypes_asm_${image}.si to $gtm_inc" popd exit 1 else echo "$gtm_inc/gtm_threadgbl_deftypes_asm_${image}.si replaced" endif else echo "$gtm_inc/gtm_threadgbl_deftypes_asm_${image}.si is current - not replaced" endif \rm -f gtm_threadgbl_deftypes_asm_${image}.in end # # Get rid of program stuff unless requested to keep it # if (! $?KEEP_THREADGBL) then \rm gtm_threadgbl_deftypes{_pro,_dbg,.o} >& /dev/null endif popd echo "Exiting $0:t" exit 0 fis-gtm-V7.0-005/sr_unix/gen_keypair.sh0000644000032200000250000001147614342376330016703 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright (c) 2010-2021 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. # # # ################################################################# ############################################################################################# # # gen_keypair.sh - Generates a new public/private key pair for the current user. # The email address identifies the user. It is an error if the gpg keyring exists. # # Arguments: # $1 - Email ID of the current user # $2 - Optional; Any residual text on line is full name of user # ############################################################################################# # GnuPG uses $GNUPGHOME, if set, for GNU Privacy Guard keyring # Script uses $gtm_pubkey, if set for file containing exported ASCII armored public key hostos=`uname -s` # temporary file if [ -x "$(command -v mktemp)" ] ; then tmp_file=`mktemp` else tmp_file=/tmp/`basename $0`_$$.tmp ; fi touch $tmp_file chmod go-rwx $tmp_file trap 'rm -rf $tmp_file ; stty sane ; exit 1' HUP INT QUIT TERM TRAP # echo and options ECHO=/bin/echo ECHO_OPTIONS="" #Linux honors escape sequence only when run with -e if [ "Linux" = "$hostos" ] ; then ECHO_OPTIONS="-e" ; fi # e-mail address is a mandatory parameter for GnuPG and cannot be null. if [ $# -lt 1 ] ; then $ECHO "Usage: `basename $0` email_address [Real name]" ; exit 1 fi email=$1 ; shift # Identify GnuPG - it is required gpg=`command -v gpg2` if [ -z "$gpg" ] ; then gpg=`command -v gpg` ; fi if [ -z "$gpg" ] ; then $ECHO "Unable to find gpg2 or gpg. Exiting" ; exit 1 ; fi # Default file for exported public key, if not specified if [ -z "$gtm_pubkey" ] ; then gtm_pubkey="${email}_pubkey.txt" ; fi # If nothing on the command line for the user name, use userid if [ $# -ge 1 ] ; then gtm_user="$*" ; else gtm_user=$USER ; fi # If GNUPGHOME is already defined, then use this as the place to store the keys. If undefined, # use $HOME/.gnupg (default for GnuPG) if [ -z "$GNUPGHOME" ]; then gtm_gpghome="$HOME/.gnupg" else gtm_gpghome="$GNUPGHOME" fi if [ -d "$gtm_gpghome" -o -f "$gtm_gpghome" ] ; then $ECHO "$gtm_gpghome already exists; cannot create a new directory" ; exit 1 fi mkdir -p $gtm_gpghome if [ ! -d $gtm_gpghome ] ; then $ECHO "Unable to create directory $gtm_gpghome" ; exit 1 fi trap 'rm -rf $tmp_file $gtm_gpghome ; stty sane ; exit 1' HUP INT QUIT TERM TRAP chmod go-rwx $gtm_gpghome # Get passphrase for new GnuPG keyring unset passphrase while [ -z "$passphrase" ] ; do $ECHO $ECHO_OPTIONS Passphrase for new keyring: \\c ; stty -echo ; read passphrase ; stty echo ; $ECHO "" $ECHO $ECHO_OPTIONS Verify passphrase: \\c ; stty -echo ; read tmp ; stty echo ; $ECHO "" if [ "$passphrase" != "$tmp" ] ; then $ECHO Verification does not match passphrase. Try again. ; unset passphrase fi unset tmp done # Fill out the unattended key generation details including the passphrase and email address key_info="Key-Type: DSA\n Key-Length: 1024\n Subkey-Type: RSA\n Subkey-Length: 2048\n Name-Real: $gtm_user\n" key_info=$key_info" Name-Email: $email\n Expire-Date: 0\n Passphrase: $passphrase\n %commit\n %echo Generated\n" # Run the unattended GnuPG key generation. Any errors will be output to gen_key.log # which will later be removed. $ECHO Key ring will be created in $gtm_gpghome $ECHO Key generation might take some time. Do something that will create entropy, $ECHO like moving the mouse or typing in another session. $ECHO $ECHO_OPTIONS $key_info | $gpg --homedir $gtm_gpghome --no-tty --batch --gen-key 2> $tmp_file # Set up pinentry-gtm so that GnuPG version 2 pinentry will return password from # obfuscated password. dir should be an absolute path name. dir=`dirname $0` if [ -z "$dir" ] ; then dir=$PWD else case $dir in /*) ;; .) dir=$PWD ;; *) dir=$PWD/$dir ;; esac fi $ECHO "pinentry-program $dir/pinentry-gtm.sh" >$gtm_gpghome/gpg-agent.conf $gpg --homedir $gtm_gpghome --list-keys | grep "$email" >> $tmp_file if [ $? -eq 0 ]; then $gpg --homedir $gtm_gpghome --export --armor -o $gtm_pubkey $gpg --homedir $gtm_gpghome --list-keys --fingerprint $ECHO "Key pair created and public key exported in ASCII to $gtm_pubkey" else $ECHO "Error creating public key/private key pairs." cat $tmp_file fi rm -f $tmp_file fis-gtm-V7.0-005/sr_unix/gen_sym_hash.sh0000644000032200000250000000507714342376330017052 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright (c) 2010-2021 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. # # # ################################################################# ################################################################################################ # # gen_sym_hash.sh - generates SHA512 hash output of the given key + Encryption parameters # $1 - encrypted symmetric key # ################################################################################################ # echo and options # Linux honors escape sequence only when run with -e # gtmcrypt_ref.h and gen_sym_hash.sh NEED to use the same value for # the encryption parameter string(defined below). # This is currently determined by the OS type. If this changes, # please verify that UNIQ_ENC_PARAM_STRING in gtmcrypt_ref.h # and encr_param_string in this module match. if [ $# -lt 1 ]; then $ECHO "Usage: $0 " ; exit 1 fi hostos=`uname -s` basedir=`dirname $0` if [ ! -x $basedir/show_install_config.sh ]; then echo "Cannot find show_install_config.sh in $basedir. Exiting" exit 1 fi algorithm=`$basedir/show_install_config.sh | awk '/^ALGORITHM/ {print $NF}'` # temporary file if [ -x "$(command -v mktemp)" ] ; then tmp_file=`mktemp` else tmp_file=/tmp/`basename $0`_$$.tmp ; fi touch $tmp_file chmod go-rwx $tmp_file trap 'rm -rf $tmp_file ; stty sane ; exit 1' HUP INT QUIT TERM TRAP ECHO=/bin/echo ECHO_OPTIONS="" if [ "Linux" = $hostos ] ; then ECHO_OPTIONS="-e" ; fi; encrypted_key_file="$1" $ECHO $ECHO_OPTIONS $algorithm\\c >$tmp_file # Identify GnuPG - it is required gpg=`command -v gpg2` if [ -z "$gpg" ] ; then gpg=`command -v gpg` ; fi if [ -z "$gpg" ] ; then $ECHO "Unable to find gpg2 or gpg. Exiting" ; exit 1 ; fi # Get passphrase for GnuPG keyring $ECHO $ECHO_OPTIONS Passphrase for keyring: \\c ; stty -echo ; read passphrase ; stty echo ; $ECHO "" $ECHO $passphrase | $gpg --no-tty --batch --passphrase-fd 0 -d $encrypted_key_file | \ cat - $tmp_file | $gpg --print-md SHA512 | tr -d ' \n' $ECHO rm -f $tmp_file fis-gtm-V7.0-005/sr_unix/gen_sym_key.sh0000644000032200000250000000637214342376330016716 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright (c) 2010-2021 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. # # # ################################################################# ############################################################################################# # # gen_sym_key.sh - generates a 32 byte random key using gpg --gen-random. # The generated key is stored in the output file encrypted with the user's public key. # # $1 - Strength of the random bytes (0 - least; 2 - greatest) # $2 - Output file name for generated key encrypted with user's public key # $3- Rest of line is treated as a comment # ############################################################################################# SYM_KEY_LEN=32 hostos=`uname -s` # echo and options ECHO=/bin/echo ECHO_OPTIONS="" #Linux honors escape sequence only when run with -e if [ "Linux" = "$hostos" ] ; then ECHO_OPTIONS="-e" ; fi if [ $# -lt 2 ] ; then $ECHO Usage: "`basename $0` key_strength[0-2] output_file" ; exit 1 ; fi # Identify GnuPG - it is required gpg=`command -v gpg2` if [ -z "$gpg" ] ; then gpg=`command -v gpg` ; fi if [ -z "$gpg" ] ; then $ECHO "Unable to find gpg2 or gpg. Exiting" ; exit 1 ; fi # Confirm ability to create output file output_dir=`dirname $2` ; if [ -z "$output_dir" ] ; then output_dir=$PWD ; fi if [ ! -w $output_dir ] ; then $ECHO "$output_dir does not exist or is not writable" ; exit 1 ; fi if [ -f $2 ] ; then if [ ! -w $2 ] ; then $ECHO "Unable to overwrite existing output file $2" ; exit 1 ; fi fi output_file=$2 random_strength=3 case $1 in [0-2]) random_strength=$1 ;; esac while [ $random_strength -lt 0 -o $random_strength -gt 2 ] ; do $ECHO $ECHO_OPTIONS "Please enter a preferred strength for the key (0/1/2/[?]):" \\c read random_strength case "$random_strength" in [0-2]) ;; *) random_strength=3 $ECHO $ECHO "Choose a key strength 0 (weakest - for testing) through 2 (strongest - for production)." $ECHO "Since 2 may use up all available entropy on your system and/or take some time" $ECHO "it is recommended that you choose 2 only on desktop systems where you can" $ECHO "more easily generate entropy by moving the mouse and typing." $ECHO ;; esac done # Get comment for key shift 2 comment="$*" ; if [ -z "$comment" ] ; then comment="Key in $output_file created by $USER `date -u`" ; fi dir_path=`dirname $0` ; if [ -z "$dir_path" ] ; then dir_path=$PWD ; fi # If $gtm_encrypt_notty is defined, we want to use it. notty=$gtm_encrypt_notty # Generate random key and save the output encrypted and signed $gpg $notty --gen-random $random_strength $SYM_KEY_LEN | \ $gpg $notty --armor --encrypt --default-recipient-self --comment "$comment" --output $output_file fis-gtm-V7.0-005/sr_unix/gen_ttt.csh0000644000032200000250000000410514342376330016204 0ustar librarygtc################################################################# # # # Copyright 2008 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. # # # ################################################################# set save_gtm_dist = "$gtm_dist" setenv gtm_dist "$gtm_root/$gtm_curpro/pro" set save_gtmroutines = "$gtmroutines" setenv gtmroutines "$gtm_obj($gtm_pct)" # Generate ttt.c from $gtm_tools/ttt.txt, $gtm_inc/opcode_def.h, and $gtm_inc/vxi.h, if needed if (-e ttt.c && $gtm_verno !~ V9*) then echo "GTN_TTT-I-EXIST : ttt.c already exists for production version $gtm_verno. Not recreating." else if ((-e ttt.c) && ((-M $gtm_tools/ttt.txt) <= (-M $gtm_src/ttt.c))) then echo "GEN_TTT-I-EXIST : ttt.c already exists for development version $gtm_verno. Recreating." else echo "GEN_TTT-I-NOTEXIST : ttt.c out of date or missing. Recreating." endif if (-e ttt.c) then chmod +w ttt.c rm -f ttt.c endif cd $gtm_exe/obj cp $gtm_inc/opcode_def.h $gtm_inc/vxi.h $gtm_tools/ttt.txt . set timestamp = `date +%m%d_%H%M%S` $gtm_root/$gtm_curpro/pro/mumps -direct <& $gtm_log/tttgen_$timestamp.log Set \$ZROUTINES=". $gtmroutines" Do ^tttgen ZContinue Halt GTM_in_tttgen cp ttt.c $gtm_src chmod 444 $gtm_src/ttt.c # clean up the files that we just copied here and the generated ttt.c chmod +w ttt.c opcode_def.h vxi.h ttt.txt rm -f ttt.c opcode_def.h vxi.h ttt.txt # remove the .o-s we just created so they're not put into libraries rm -f chk2lev.o chkop.o gendash.o genout.o loadop.o loadvx.o tttgen.o tttscan.o endif setenv gtmroutines "$save_gtmroutines" unset save_gtmroutines setenv gtm_dist "$save_gtm_dist" unset save_gtm_dist fis-gtm-V7.0-005/sr_unix/gen_vms_ttt.csh0000644000032200000250000000353614342376330017100 0ustar librarygtc################################################################# # # # Copyright 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. # # # ################################################################# # Generate ttt.c from $gtm_tools/ttt.txt, $gtm_inc/opcode_def.h, and $gtm_inc/vxi.h, if needed if (-e $work_dir/vvms/new) then cd $work_dir/port/new/ foreach cmpnt (opcode_def.h vxi.h) if (-e $cmpnt) then echo "Using existing $cmpnt" else $work_tools/workfetch.csh $cmpnt endif end cd $work_dir/vvms/new/ foreach cmpnt (ttt.txt ttt.c) if (-e $cmpnt) then echo "Using existing $cmpnt" else $work_tools/workfetch.csh $cmpnt endif end set save_gtm_dist = "$gtm_dist" setenv gtm_dist "$gtm_root/$gtm_curpro/pro" set save_gtmroutines = "$gtmroutines" setenv gtmroutines ". $gtm_obj($gtm_pct)" if (-e ttt.c) then chmod +w ttt.c rm -f ttt.c endif set timestamp = `date +%m%d_%H%M%S` $gtm_root/$gtm_curpro/pro/mumps -run tttgen "ttt.txt $work_dir/port/new/opcode_def.h $work_dir/port/new/vxi.h" # remove the .o-s we just created so they're not put into libraries rm -f chk2lev.o chkop.o gendash.o genout.o loadop.o loadvx.o tttgen.o tttscan.o setenv gtmroutines "$save_gtmroutines" unset save_gtmroutines setenv gtm_dist "$save_gtm_dist" unset save_gtm_dist cd ../../ echo "workclean may be needed to clear out implicitly fetched modules" else echo "No vvms/new so No action" endif fis-gtm-V7.0-005/sr_unix/gen_xfer_desc.cmake0000644000032200000250000001005314342376330017635 0ustar librarygtc################################################################# # # # Copyright 2013 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. # # # ################################################################# set(I "") foreach(i ${includes}) list(APPEND I "-I${i}") endforeach() file(WRITE tmp_xfer_1.c " /* We have not yet created gtm_threadgbl_deftypes.h and don't need it, signal gtm_threadgbl.h to avoid including it */ #define NO_THREADGBL_DEFTYPES #include \"mdef.h\" #define XFER(a,b) MY_XF,b #include \"xfer.h\" ") execute_process( COMMAND ${CMAKE_C_COMPILER} ${I} -E tmp_xfer_1.c -o tmp_xfer_2.c RESULT_VARIABLE failed ) if(failed) message(FATAL_ERROR "Preprocessing with ${CMAKE_C_COMPILER} failed") endif() file(STRINGS tmp_xfer_2.c lines REGEX "MY_XF") string(REGEX REPLACE "(MY_XF|,)" "" names "${lines}") file(REMOVE tmp_xfer_1.c tmp_xfer_2.c) if("${arch}" MATCHES "ia64") # Guess what! Its possible for xfer_table to be initialized by functions others than # then the ones in xfer.h. So append those names explicitly here list(APPEND names op_fnzreverse op_zst_st_over op_zst_fet_over op_zstzb_fet_over op_zstzb_st_over opp_zstepret opp_zstepretarg op_zstepfetch op_zstepstart op_zstzbfetch op_zstzbstart opp_zst_over_ret opp_zst_over_retarg op_fetchintrrpt op_startintrrpt op_forintrrpt op_mprofextexfun op_mprofextcall op_mprofcalll op_mprofcallb op_mprofcallw op_mprofcallspl op_mprofcallspb op_mprofcallspw op_mprofexfun op_mprofforlcldow op_mprofforlcldol op_mprofforlcldob op_mprofforchk1 op_mproflinefetch op_mproflinestart ) endif() file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/sources.list" sources) # On X86_64, the calling convention for variable length functions defines that the register $RAX # (ie the lower 8 bytes of this register actually) will contain the # of floating point arguments passed to that function. # $RAX is not an argument register normally and so is typically unused (and also is not expected to be preserved across calls). # since none of the XFER functions actually take double/float arguments, but some are variable length functions, # the generated code should set the $RAX register to the value '0'. # Theoretically this register can be set to 0 all the time during a function call. # But to optimize the number of generated instructions, identify which XFER function is actually a var arg function # And if the logic to identify it falls thru, blindly assume its a VAR_ARG function. # Hence the logic below might incorrectly mark a few functions like op_sub, op_fnzascii as C_VAR_ARGS. But that is okay. set(ftypes "") set(defines "\n/* Defines used in resetting xfer_table_desc on transfer table changes */\n") foreach(name ${names}) set(ftype "") if(";${sources};" MATCHES ";([^;]*/${name}\\.s);") set(ftype GTM_ASM_RTN) elseif(";${sources};" MATCHES ";([^;]*/${name}\\.c);") file(STRINGS "${CMAKE_MATCH_1}" sig REGEX "${name}.*\\.\\.\\.") if(sig) set(ftype "GTM_C_VAR_ARGS_RTN") else() set(ftype "GTM_C_RTN") endif() endif() if(NOT ftype) set(ftype "GTM_C_VAR_ARGS_RTN") foreach(src ${sources}) if("${src}" MATCHES "\\.s$") file(STRINGS "${src}" sig REGEX "^${name}") if(sig) set(ftype "GTM_ASM_RTN") break() endif() endif() endforeach() endif() set(ftypes "${ftypes}${ftype}, /* ${name} abc */ \\\n") set(defines "${defines}#define ${name}_FUNCTYPE ${ftype}\n") endforeach() if("${arch}" MATCHES "ia64") else() # Not IA64? reset defines set(defines "") endif() file(WRITE xfer_desc.i "/* Generated by gen_xfer_desc.cmake */ #define GTM_C_RTN 1 #define GTM_ASM_RTN 2 #define GTM_C_VAR_ARGS_RTN 3 #define DEFINE_XFER_TABLE_DESC char xfer_table_desc[] = \\ { \\ ${ftypes}0}\n ${defines}") fis-gtm-V7.0-005/sr_unix/gen_xfer_desc.csh0000644000032200000250000001621714342376330017342 0ustar librarygtc################################################################# # # # Copyright 2008, 2014 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. # # # ################################################################# # Most of the logic in this script is similar to its counter-part on sr_ia64 # Any changes or bugfixes in this files should be updated in its couterpart on sr_ia64 # This script is called from two places. If it is called from comlist.mk,it takes source directories as arguments. # else it is called from comlist.csh with no arguments. if ( $#argv != 0 ) then set builds=$buildtypes set numbuilds=$#builds if ( 1 != $numbuilds ) then echo "sr_x86_64/gen_xfer_desc.csh-E-2many: only one buildtype at a time allowed" exit 2 endif cd $gtm_ver/$buildtypes/obj set lib_count=$#argv set ref_libs="" while ( $lib_count != 0 ) set ref_libs="$ref_libs $argv[$lib_count]" @ lib_count-- end set gtm_src_types = "c m64 s msg" set gtm_inc_types = "h max mac si" set xfer_dir=`pwd` if (-e src) then \rm -rf src endif if (-e inc ) then \rm -rf inc endif # Create temporary directories called src and inc mkdir src inc pushd $gtm_ver # Following "foreach" logic comes from cms_load.csh foreach ref_library ( $ref_libs ) cd $ref_library foreach dir (src inc) foreach ftype (`set | grep "^gtm_${dir}_t" | sed 's/^gtm_'$dir'_types[ ][ ]*//g'`) set nfiles = `\ls -1 | grep "\.$ftype"'$' | wc -l | sed 's/^[ ]*//g'` if ($nfiles != 0) then #creates the links for all specific files in src and inc directory. \ls -1 | grep "\.$ftype"'$' | xargs -i ln -f -s "$PWD/{}" $xfer_dir/${dir} endif end end cd .. end popd setenv gtm_src `pwd`/src setenv gtm_inc `pwd`/inc setenv gt_cc_option_I "$gt_cc_option_I -I$gtm_inc" rm -rf $xfer_dir/xfer_desc.i else # set xfer_desc.i path to $gtm_inc in normal build set xfer_dir=$gtm_inc # If this is a non-developmental version and the current image is "dbg" and xfer_desc.i already exists, do not # recreate xfer_desc.i. The assumption is that a "pro" build had already created xfer_desc.i so we should not # change whatever it had relied upon. For development versions, we dont care so we unconditionally recreate this file. if (-e $xfer_dir/xfer_desc.i) then if ($gtm_verno !~ V9*) then echo "GENXFERDESC-I-EXIST : xfer_desc.i already exists for production version $gtm_verno. Not recreating." exit 0 else echo "GENXFERDESC-I-EXIST : xfer_desc.i already exists for development version $gtm_verno. Recreating." chmod +w $xfer_dir/xfer_desc.i # in case previous build had reset permissions to be read-only rm -f $xfer_dir/xfer_desc.i endif endif endif cd $gtm_src \rm -f temp_xyz_ia.* >&! /dev/null cat << TEST >! temp_xyz_ia.c /* We have not yet created gtm_threadgbl_deftypes.h and don't need it, signal gtm_threadgbl.h to avoid including it */ #define NO_THREADGBL_DEFTYPES #include "mdef.h" #include "xfer_enum.h" #define XFER(a,b) MY_XF,b #include "xfer.h" TEST $gt_cc_compiler $gt_cc_option_I -E temp_xyz_ia.c >! temp_xyz_ia.1 awk -F , '/MY_XF/ {print $2}' temp_xyz_ia.1 >! temp_xyz_ia.2 cat >> $xfer_dir/xfer_desc.i << EOF /* Generated by gen_xfer_desc.csh */ #define GTM_C_RTN 1 #define GTM_ASM_RTN 2 #define GTM_C_VAR_ARGS_RTN 3 #define DEFINE_XFER_TABLE_DESC char xfer_table_desc[] = \\ { \\ EOF # On X86_64, the calling convention for variable length functions defines that the register $RAX # (ie the lower 8 bytes of this register actually) will contain the # of floating point arguments passed to that function. # $RAX is not an argument register normally and so is typically unused (and also is not expected to be preserved across calls). # since none of the XFER functions actually take double/float arguments, but some are variable length functions, # the generated code should set the $RAX register to the value '0'. # Theoritically this register can be set to 0 all the time during a function call. # But to optimize the number of generated instructions, identify which XFER function is actually a var arg function # And if the logic to identify it falls thro, blindly assume its a VAR_ARG function. # Hence the logic below might incorrectly mark a few functions like op_sub, op_fnzascii as C_VAR_ARGS. But that is okay. foreach name (`cat temp_xyz_ia.2`) set name2 = `grep "^$name" *.s` if (-r ${name}.s) then set ftype = "GTM_ASM_RTN" else if (-r ${name}.c) then grep $name $gtm_src/${name}.c | grep "\.\.\." >> /dev/null if ( $? == 0 ) then set ftype = "GTM_C_VAR_ARGS_RTN" else set ftype = "GTM_C_RTN" endif else if ("${name2}" != "") then set ftype = "GTM_ASM_RTN" else if ("${name2}" == "") then set ftype = "GTM_C_VAR_ARGS_RTN" endif echo "$ftype, /* $name */ \\" >> $xfer_dir/xfer_desc.i # print the #defines in a temp file to append to $xfer_dir/xfer_desc.i later. # This is done to avoid the whole loop once again. echo "#define ${name}_FUNCTYPE $ftype" >> temp_xyz_ia.defines end echo "0}" >> $xfer_dir/xfer_desc.i echo " " >> $xfer_dir/xfer_desc.i # The defines used in resetting xfer_table_desc needs to be generated only for ia64 set mach_type = `uname -m` if ( "ia64" == "$mach_type") then echo "/* Defines used in resetting xfer_table_desc on transfer table changes */" >> $xfer_dir/xfer_desc.i # Guess what! Its possible for xfer_table to be intialized by funtions others than # then the ones in xfer.h. So append those names explicitly here cat >> temp_xyz_ia.3 << EOF op_fnzreverse op_zst_st_over op_zst_fet_over op_zstzb_fet_over op_zstzb_st_over opp_zstepret opp_zstepretarg op_zstepfetch op_zstepstart op_zstzbfetch op_zstzbstart opp_zst_over_ret opp_zst_over_retarg op_fetchintrrpt op_startintrrpt op_forintrrpt op_mprofextexfun op_mprofextcall op_mprofcalll op_mprofcallb op_mprofcallw op_mprofcallspl op_mprofcallspb op_mprofcallspw op_mprofexfun op_mprofforlcldow op_mprofforlcldol op_mprofforlcldob op_mprofforchk1 op_mproflinefetch op_mproflinestart EOF foreach name (`cat temp_xyz_ia.3`) set name2 = `grep "^$name" *.s` if (-r ${name}.s) then set ftype = "GTM_ASM_RTN" else if (-r ${name}.c) then grep $name $gtm_inc/* | grep "\.\.\." >> /dev/null if ( $? == 0 ) then set ftype = "GTM_C_VAR_ARGS_RTN" else set ftype = "GTM_C_RTN" endif else if ("${name2}" != "") then set ftype = "GTM_ASM_RTN" else if ("${name2}" == "") then set ftype = "GTM_C_VAR_ARGS_RTN" endif echo "#define ${name}_FUNCTYPE $ftype" >> temp_xyz_ia.defines end # Append the defines to the end of $xfer_dir/xfer_desc.i cat temp_xyz_ia.defines >> $xfer_dir/xfer_desc.i echo endif \rm temp_xyz_ia.* if ($#argv != 0) then cd $xfer_dir \rm -rf src \rm -rf inc endif exit 0 fis-gtm-V7.0-005/sr_unix/generate_help.csh0000755000032200000250000000627714342376330017361 0ustar librarygtc################################################################# # # # Copyright (c) 2014-2018 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. # # # ################################################################# # # (re)Generate GT.M and Utility Help global directories and files on demand # # Parameters: # HLP file location (defaults to $gtm_pct) # Error log file (used to redirect output to error file in comlist.csh) set hlpdir = $1 if ("" == "${hlpdir}") then if (0 == $?gtm_pct) then echo "HLP file location was not supplied and \$gtm_pct is not defined" exit -1 endif set hlpdir = ${gtm_pct} if (! -e ${hlpdir}) then echo "HLP file location does not exist" exit -2 endif endif alias do_log '\!:*' if ("" != "${2}") alias do_log '\!:* >>& '$2'' # Need write permissions to $gtm_dist if (! -w ${gtm_dist}) then set restorePerms = `filetest -P $gtm_dist` chmod ugo+w ${gtm_dist} if ($status) then echo "User does not have sufficient privileges to get write access to $gtm_dist, cannot update help" exit -3 endif endif set script_stat = 0 foreach hlp (${hlpdir}/*.hlp) # Extract the HLP file name and fix-up the mumps to gtm set prefix=${hlp:t:r:s/mumps/gtm/} # If the HLP files are newer than the help database create a new one, otherwise skip it if ( -C ${hlp} > -C $gtm_dist/${prefix}help.dat ) then \rm -f ${gtm_dist}/${prefix}help.gld ${gtm_dist}/${prefix}help.dat else continue endif echo "Generating ${prefix}help.gld and ${prefix}help.dat" # Either help info does not exist or needs to be regenerated # Define the global directory with the same prefix as the HLP file and # use ${gtm_dist} in the file name to ensure dynamic lookup of the DAT # for help information setenv gtmgbldir ${gtm_dist}/${prefix}help.gld ${gtm_dist}/mumps -run GDE <& /dev/null chmod ugo-x ${gtm_dist}/${prefix}help.{gld,dat} end # Restore read-only status if ($?restorePerms) then chmod ${restorePerms} ${gtm_dist} endif exit ${script_stat} fis-gtm-V7.0-005/sr_unix/generic_signal_handler.c0000644000032200000250000003623714342376330020666 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* Standard signal processor * * If we are nesting our handlers in an improper way, this routine will * not return but will immediately invoke core/termination processing. * * Returns if some condition makes it inadvisable to exit now else invokes the system EXIT() system call. * For GTMSECSHR it unconditionally returns to gtmsecshr_signal_handler() which later invokes gtmsecshr_exit(). */ #include "mdef.h" #include "gtm_string.h" #include "gtm_unistd.h" #include "gtm_stdlib.h" /* for EXIT() */ #include "gtm_inet.h" #include "gtm_signal.h" #include "gtm_stdio.h" #include "gtm_multi_thread.h" #include "error.h" #include "gtmsiginfo.h" #include "gtmimagename.h" #include "gt_timer.h" #include "send_msg.h" #include "generic_signal_handler.h" #include "gtmmsg.h" #include "gtmio.h" #include "have_crit.h" #include "util.h" // For gd_region #include "gdsroot.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #define DEFER_EXIT_PROCESSING ((EXIT_PENDING_TOLERANT >= exit_state) \ && (exit_handler_active || multi_thread_in_use \ || multi_proc_in_use || !OK_TO_INTERRUPT)) /* Combine send_msg and gtm_putmsg into one macro to conserve space. */ #define SEND_AND_PUT_MSG(...) \ { \ if (OK_TO_SEND_MSG) \ send_msg_csa(CSA_ARG(NULL) __VA_ARGS__); \ gtm_putmsg_csa(CSA_ARG(NULL) __VA_ARGS__); \ } /* These fields are defined as globals not because they are used globally but * so they will be easily retrievable even in 'pro' cores. */ GBLDEF siginfo_t exi_siginfo; GBLDEF gtm_sigcontext_t exi_context; GBLREF int4 forced_exit_err; GBLREF int4 exi_condition; GBLREF boolean_t dont_want_core; GBLREF boolean_t created_core; GBLREF boolean_t need_core; GBLREF uint4 process_id; GBLREF volatile int4 exit_state; GBLREF volatile unsigned int core_in_progress; GBLREF gtmsiginfo_t signal_info; GBLREF boolean_t exit_handler_active; GBLREF void (*call_on_signal)(); GBLREF boolean_t gtm_quiet_halt; GBLREF volatile int4 gtmMallocDepth; /* Recursion indicator */ GBLREF volatile boolean_t timer_active; GBLREF sigset_t block_sigsent; GBLREF gd_region *gv_cur_region; GBLREF boolean_t blocksig_initialized; GBLREF boolean_t mu_reorg_process; GBLREF sgmnt_data_ptr_t cs_data; #ifdef DEBUG GBLREF boolean_t in_nondeferrable_signal_handler; #endif LITREF gtmImageName gtmImageNames[]; error_def(ERR_FORCEDHALT); error_def(ERR_GTMSECSHRSHUTDN); error_def(ERR_KILLBYSIG); error_def(ERR_KILLBYSIGSINFO1); error_def(ERR_KILLBYSIGSINFO2); error_def(ERR_KILLBYSIGSINFO3); error_def(ERR_KILLBYSIGUINFO); error_def(ERR_KRNLKILL); error_def(ERR_STATSDBMEMERR); static inline void check_for_statsdb_memerr() { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TREF(gvcst_statsDB_open_ch_active)) { /* Case where we've tried to create a stats db block * Issue an rts error and let the statsDB condition handler * do the clean up */ TREF(statsdb_memerr) = TRUE; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_STATSDBMEMERR, 2, gv_cur_region->dyn.addr->fname_len, gv_cur_region->dyn.addr->fname); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_STATSDBMEMERR, 2, gv_cur_region->dyn.addr->fname_len, gv_cur_region->dyn.addr->fname); } } void generic_signal_handler(int sig, siginfo_t *info, void *context) { gtm_sigcontext_t *context_ptr; void (*signal_routine)(); intrpt_state_t prev_intrpt_state; # ifdef DEBUG boolean_t save_in_nondeferrable_signal_handler; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!thread_block_sigsent || blocksig_initialized); /* If "thread_block_sigsent" is TRUE, it means the threads do not want the master thread to honor external signals * anymore until the threads complete. Achieve that effect by returning right away from the signal handler. */ if (thread_block_sigsent && sigismember(&block_sigsent, sig)) return; FORWARD_SIG_TO_MAIN_THREAD_IF_NEEDED(sig); # ifdef DEBUG /* Note that it is possible "in_nondeferrable_signal_handler" is non-zero if we first went into timer_handler * and then came here due to a nested signal (e.g. SIG-15). So save current value of global and restore it at * end of this function even though we will most often not return to the caller (process will exit mostly). */ save_in_nondeferrable_signal_handler = in_nondeferrable_signal_handler; # endif /* Save parameter value in global variables for easy access in core */ dont_want_core = FALSE; /* (re)set in case we recurse */ created_core = FALSE; /* we can deal with a second core if needbe */ exi_condition = sig; if (NULL != info) exi_siginfo = *info; else memset(&exi_siginfo, 0, SIZEOF(*info)); # if defined(__ia64) && defined(__hpux) context_ptr = (gtm_sigcontext_t *)context; /* no way to make a copy of the context */ memset(&exi_context, 0, SIZEOF(exi_context)); # else if (NULL != context) exi_context = *(gtm_sigcontext_t *)context; else memset(&exi_context, 0, SIZEOF(exi_context)); context_ptr = &exi_context; # endif /* Check if we are fielding nested immediate shutdown signals */ if (EXIT_IMMED <= exit_state) { switch(sig) { /* If we are dealing with one of these three dangerous signals which we have * already hit while attempting to shutdown once, die with core now. */ case SIGSEGV: case SIGBUS: case SIGILL: DEBUG_ONLY(in_nondeferrable_signal_handler = IN_GENERIC_SIGNAL_HANDLER;) if (core_in_progress) { if (exit_handler_active) UNDERSCORE_EXIT(sig); else EXIT(sig); } ++core_in_progress; DUMP_CORE; assertpro(!((SIGSEGV == sig) || (SIGBUS == sig) || (SIGKILL == sig))); default: ; } } switch(sig) { case SIGTERM: if (!IS_GTMSECSHR_IMAGE) { forced_exit_err = ERR_FORCEDHALT; /* If nothing pending AND we have crit or in wcs_wtstart() or already in exit processing, wait to * invoke shutdown. wcs_wtstart() manipulates the active queue that a concurrent process in crit * in bt_put() might be waiting for. interrupting it can cause deadlocks (see C9C11-002178). */ if (mu_reorg_process && OK_TO_INTERRUPT && cs_data && cs_data->kill_in_prog) DEFER_INTERRUPTS(INTRPT_IN_KILL_CLEANUP, prev_intrpt_state); /* avoid ABANDONEDKILL */ if (DEFER_EXIT_PROCESSING) { SET_FORCED_EXIT_STATE; exit_state++; /* Make exit pending, may still be tolerant though */ assert(!IS_GTMSECSHR_IMAGE); if (exit_handler_active && !gtm_quiet_halt) SEND_AND_PUT_MSG(VARLSTCNT(1) forced_exit_err); return; } DEBUG_ONLY(in_nondeferrable_signal_handler = IN_GENERIC_SIGNAL_HANDLER;) exit_state = EXIT_IMMED; SET_PROCESS_EXITING_TRUE; /* Set this BEFORE cancelling timers as wcs_phase2_commit_wait * relies on this. */ if (ERR_FORCEDHALT != forced_exit_err || !gtm_quiet_halt) SEND_AND_PUT_MSG(VARLSTCNT(1) forced_exit_err); } else { /* Special case for gtmsecshr - no deferral just exit */ DEBUG_ONLY(in_nondeferrable_signal_handler = IN_GENERIC_SIGNAL_HANDLER;) forced_exit_err = ERR_GTMSECSHRSHUTDN; if (OK_TO_SEND_MSG) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(1) forced_exit_err); } dont_want_core = TRUE; break; case SIGQUIT: /* Handle SIGQUIT specially which we ALWAYS want to defer if possible as it is always sent */ dont_want_core = TRUE; extract_signal_info(sig, &exi_siginfo, context_ptr, &signal_info); switch(signal_info.infotype) { case GTMSIGINFO_NONE: forced_exit_err = ERR_KILLBYSIG; break; case GTMSIGINFO_USER: forced_exit_err = ERR_KILLBYSIGUINFO; break; case GTMSIGINFO_ILOC + GTMSIGINFO_BADR: forced_exit_err = ERR_KILLBYSIGSINFO1; break; case GTMSIGINFO_ILOC: forced_exit_err = ERR_KILLBYSIGSINFO2; break; case GTMSIGINFO_BADR: forced_exit_err = ERR_KILLBYSIGSINFO3; break; default: exit_state = EXIT_IMMED; assertpro(FALSE && signal_info.infotype); /* show signal_info if there's a failure */ } /* If nothing pending AND we have crit or already in exit processing, wait to invoke shutdown */ if (DEFER_EXIT_PROCESSING) { SET_FORCED_EXIT_STATE; exit_state++; /* Make exit pending, may still be tolerant though */ assert(!IS_GTMSECSHR_IMAGE); return; } DEBUG_ONLY(in_nondeferrable_signal_handler = IN_GENERIC_SIGNAL_HANDLER;) exit_state = EXIT_IMMED; SET_PROCESS_EXITING_TRUE; switch(signal_info.infotype) { case GTMSIGINFO_NONE: SEND_AND_PUT_MSG(VARLSTCNT(6) ERR_KILLBYSIG, 4, GTMIMAGENAMETXT(image_type), process_id, sig); break; case GTMSIGINFO_USER: SEND_AND_PUT_MSG(VARLSTCNT(8) ERR_KILLBYSIGUINFO, 6, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.send_pid, signal_info.send_uid); break; case GTMSIGINFO_ILOC + GTMSIGINFO_BADR: SEND_AND_PUT_MSG(VARLSTCNT(8) ERR_KILLBYSIGSINFO1, 6, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.int_iadr, signal_info.bad_vadr); break; case GTMSIGINFO_ILOC: SEND_AND_PUT_MSG(VARLSTCNT(7) ERR_KILLBYSIGSINFO2, 5, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.int_iadr); break; case GTMSIGINFO_BADR: SEND_AND_PUT_MSG(VARLSTCNT(7) ERR_KILLBYSIGSINFO3, 5, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.bad_vadr); break; } break; # ifdef _AIX case SIGDANGER: forced_exit_err = ERR_KRNLKILL; /* If nothing pending AND we have crit or already in exit processing, wait to invoke shutdown */ if (DEFER_EXIT_PROCESSING) { SET_FORCED_EXIT_STATE; exit_state++; /* Make exit pending, may still be tolerant though */ assert(!IS_GTMSECSHR_IMAGE); return; } DEBUG_ONLY(in_nondeferrable_signal_handler = IN_GENERIC_SIGNAL_HANDLER;) exit_state = EXIT_IMMED; SET_PROCESS_EXITING_TRUE; SEND_AND_PUT_MSG(VARLSTCNT(1) forced_exit_err); dont_want_core = TRUE; break; # endif default: extract_signal_info(sig, &exi_siginfo, context_ptr, &signal_info); switch(signal_info.infotype) { case GTMSIGINFO_NONE: DEBUG_ONLY(in_nondeferrable_signal_handler = IN_GENERIC_SIGNAL_HANDLER;) exit_state = EXIT_IMMED; SET_PROCESS_EXITING_TRUE; SEND_AND_PUT_MSG(VARLSTCNT(6) ERR_KILLBYSIG, 4, GTMIMAGENAMETXT(image_type), process_id, sig); break; case GTMSIGINFO_USER: /* This signal was SENT to us so it can wait until we are out of crit to cause an exit */ forced_exit_err = ERR_KILLBYSIGUINFO; /* If nothing pending AND we have crit or already exiting, wait to invoke shutdown */ if (DEFER_EXIT_PROCESSING) { assert(!IS_GTMSECSHR_IMAGE); SET_FORCED_EXIT_STATE; exit_state++; /* Make exit pending, may still be tolerant though */ need_core = TRUE; gtm_fork_n_core(); /* Generate "virgin" core while we can */ return; } DEBUG_ONLY(in_nondeferrable_signal_handler = IN_GENERIC_SIGNAL_HANDLER;) exit_state = EXIT_IMMED; SET_PROCESS_EXITING_TRUE; SEND_AND_PUT_MSG(VARLSTCNT(8) ERR_KILLBYSIGUINFO, 6, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.send_pid, signal_info.send_uid); break; case GTMSIGINFO_ILOC + GTMSIGINFO_BADR: check_for_statsdb_memerr(); DEBUG_ONLY(in_nondeferrable_signal_handler = IN_GENERIC_SIGNAL_HANDLER;) exit_state = EXIT_IMMED; SET_PROCESS_EXITING_TRUE; /* SIGABRT is usually delivered when memory corruption is detected by glibc * e.g. *** glibc detected *** mupip: double free or corruption (!prev): 0x0983f180 *** * We want to detect that right when it happens so assert fail in that case. */ assert(SIGABRT != sig); SEND_AND_PUT_MSG(VARLSTCNT(8) ERR_KILLBYSIGSINFO1, 6, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.int_iadr, signal_info.bad_vadr); break; case GTMSIGINFO_ILOC: DEBUG_ONLY(in_nondeferrable_signal_handler = IN_GENERIC_SIGNAL_HANDLER;) exit_state = EXIT_IMMED; SET_PROCESS_EXITING_TRUE; SEND_AND_PUT_MSG(VARLSTCNT(7) ERR_KILLBYSIGSINFO2, 5, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.int_iadr); break; case GTMSIGINFO_BADR: check_for_statsdb_memerr(); DEBUG_ONLY(in_nondeferrable_signal_handler = IN_GENERIC_SIGNAL_HANDLER;) exit_state = EXIT_IMMED; SET_PROCESS_EXITING_TRUE; SEND_AND_PUT_MSG(VARLSTCNT(7) ERR_KILLBYSIGSINFO3, 5, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.bad_vadr); break; default: DEBUG_ONLY(in_nondeferrable_signal_handler = IN_GENERIC_SIGNAL_HANDLER;) exit_state = EXIT_IMMED; SET_PROCESS_EXITING_TRUE; assertpro(FALSE && signal_info.infotype);; /* show signal_info if there's a failure */ } if (0 != signal_info.sig_err) { SEND_AND_PUT_MSG(VARLSTCNT(1) signal_info.sig_err); } break; } /* switch (sig) */ /* Stop the timers but do not cancel them. This allows the timer structures to appear in the core where gtmpcat can * extract them allowing us to see what was going on. */ assert(in_nondeferrable_signal_handler); if (timer_active) sys_canc_timer(); FFLUSH(stdout); if (!dont_want_core) { need_core = TRUE; gtm_fork_n_core(); } /* If any special routines are registered to be driven on a signal, drive them now */ if (0 != exi_condition) { /* If this is a GTM-ish process in runtime mode, call the routine to generate the zshow dump. This separate * routine necessary because (1) generic_signal_handler() no longer calls condition handlers and (2) we couldn't * use call_on_signal because it would already be in use in updproc() where it is also possible this routine * needs to be called. Bypass this code if we didn't create a core since that means it is not a GTM * issue that forced its demise (and since this is an uncaring interrupt, we could be in any number of * situations that would cause a ZSHOW dump to explode). Better for user to use jobexam to cause a dump prior * to terminating the process in a deferrable fashion. */ if (!dont_want_core && IS_MCODE_RUNNING && (NULL != (signal_routine = RFPTR(create_fatal_error_zshow_dmp_fptr)))) { /* note assignment of signal_routine above */ SFPTR(create_fatal_error_zshow_dmp_fptr, NULL); (*signal_routine)(exi_condition); } /* Some mupip functions define an entry point to drive on signals. Make sure to do this AFTER we create the * dump file above as it may detach things (like the recvpool) we need to create the above dump. */ if (NULL != (signal_routine = call_on_signal)) /* Note assignment */ { call_on_signal = NULL; /* So we don't recursively call ourselves */ (*signal_routine)(); } } if (!IS_GTMSECSHR_IMAGE) { assert((EXIT_IMMED <= exit_state) || !exit_handler_active); EXIT(-exi_condition); } else { DEBUG_ONLY(in_nondeferrable_signal_handler = save_in_nondeferrable_signal_handler;) return; } } fis-gtm-V7.0-005/sr_unix/generic_signal_handler.h0000755000032200000250000000120314342376330020657 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GENERIC_SIGNAL_HANDLER_INCLUDED #define GENERIC_SIGNAL_HANDLER_INCLUDED void generic_signal_handler(int sig, siginfo_t *info, void *context); #endif /* GENERIC_SIGNAL_HANDLER_INCLUDED */ fis-gtm-V7.0-005/sr_unix/gengtmdeftypes.csh0000755000032200000250000001634614342376330017602 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2011-2021 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. # # # ################################################################# # # Create GTMDefinedTypesInit.m (currently) used by offset.m and gtmpcat.m # # Unset gtmcompile to prevent possible issues with production version # unsetenv gtmcompile setenv pushdsilent set gtmver = "" set gtmtyp = "" set ourdir = `pwd` # # Parse arguments # @ argc1 = 1 foreach arg ($argv) @ argc1 = $argc1 + 1 if ($?skip) then unset skip else switch($arg) case "-help": case "-?": echo " " echo "Command format is:" echo " " echo "$0 | d>" echo " " echo "Where:" echo " " echo " - gtmver is version name - e.g. V51000 or V54002. Only one version can be specified." echo " - Indicate pro or debug version. Default is whatever is current. They generate somewhat" echo " different files." echo " " echo "The GTM defined types file (GTMDefinedTypesInit.m) is created in the current working directory." echo " " exit 0 default: if ("" == "$gtmver") then set gtmver = "$arg" else if ("" == "$gtmtyp") then if ("p" == "$arg" || "pro" == "$arg") then set gtmtyp = "p" else if ("d" == "$arg" || "dbg" == "$arg") then set gtmtyp = "d" else echo "GENGTMDEFTYPES-E-INVTYP Invalid type specified - should be {p|pro} for pro version or" echo " {d|dbg} for a debug version" exit 1 endif else echo "GENGTMDEFTYPES-E-ONEVER Only one GT.M version can be specified" exit 1 endif endsw endif end if ("" == "$gtmver") set gtmver = ${gtm_dist:h:t} if ("" == "$gtmtyp") set gtmtyp = ${gtm_dist:t:s/bg//:s/ro//:s/ta//} if ("b" == "$gtmtyp") set gtmtyp = "p" # Switch BTA to PRO echo echo "Starting generation of GTM defined types initialization routine for version $gtmver ($gtmtyp)" echo # # Create a temporary directory to put the object and other temporary file(s) in. Solves the # problems of cluttering up directories with remnants and also HPUX's issues with creating a # GT.M object file on an NFS directory. # set tmpdir = "/tmp/gengtmdeftypes.${USER}.$$" if (! -e $tmpdir) then mkdir $tmpdir if (0 != $status) then echo "GENGTMDEFTYPES-E-OBJDIRFAIL Failed to create object directory -- terminating -- tmpdir $tmpdir not removed" exit 1 endif endif # # Set the version we will run with - note $gtmroutines not reset by version (setactive*.csh) # if ($?usecurpro) then set setactive_parms=(p $gtmtyp); source $gtm_tools/setactive.csh set proddist=$gtm_dist setenv gtmroutines "$tmpdir $gtm_dist" endif pushd $tmpdir # # Run in normal M mode since this build has no UTF-8 dependencies but has some complications # due to how it switches versions around. # unsetenv gtm_chset # set setactive_parms=($gtmver $gtmtyp); source $gtm_tools/setactive.csh if ($?proddist) then setenv gtm_dist $proddist endif # If gengtmdeftypes.csh is invoked for old versions, point cpfrom_tools/cpfrom_pct to the location of # the helper files, typically $gtm_tools/$gtm_pct of gtm_curpro if (! $?cpfrom_tools) set cpfrom_tools = $gtm_tools if (! $?cpfrom_pct) set cpfrom_pct = $gtm_pct cp $cpfrom_pct/{decomment.m,scantypedefs.m} . cp $cpfrom_tools/{gtmexcludetypelist.txt,stripmine.awk,xtrgtmtypes.awk} . # # Create a list of types defined by GTM by reading the header files for types defined there. This allows us to only # pay attention to these GTM defined structures later when we process compiler pre-processor output where system header # files will contribute lots of irrelevant definitions. The steps takes are - for each include file: # 1. Run it through decomment.m which strips comments from the module to ease parsing. # 2. Run through xtrgtmtypes.awk script which locates typedef statements and records their types (topname and bottom name) # recording them in the output file. # 3. Sort the generated names and eliminate duplicates. # rm -f gtmtypelist.txt >& /dev/null rm -f gtmtypelist.tmp >& /dev/null ls -1 $gtm_inc/*.h | awk '{x="$gtm_dist/mumps -run decomment "$1" | awk -f xtrgtmtypes.awk >> gtmtypelist.tmp"; system(x);}' sort gtmtypelist.tmp | uniq > gtmtypelist.txt rm -f gtmtypelist.tmp >& /dev/null if (! -e gtmtypelist.txt) then echo "GENGTMDEFTYPES-E-CANTCONT Cannot continue due to failure of earlier commands -- tmpdir $tmpdir not removed" popd exit 1 endif # # Drive the big kahuna (scantypedefs) to: # 1. Read the list of GTM types we want to pay attention to created in the last step. # 2. Read a list of ignored types which we don't pay attention to because they are irrelevant for our purposes # or they cause errors, etc. # 3. Create a C program with every GT.M include (with some exceptions listed in scantypedefs.m) and # run it through the C pre-processor. # 4. Process the pre-processor output with the awk script stripmine.awk which performs the following actions: # a. Eliminates all statements other than "typedef" statements. # b. Puts spaces around all tokens, all special chars. # c. Eliminates multiple adjacent white space chars to make easy parsing for $ZPiece(). # 5. Reads the created input file, parsing the typedef statements into a structure. # 6. Resolves types to their simplest types (undoing typedefs) # 7. Imbedded structures are expanded so each field in a structure is resolved to the maximum point # we can de-evolve it to using GTM types and base types. # 8. Create another C program generating macros to extract information about each field within a struct or union # and print it. Information includes name, type, offset, length, and dimension plus overal length and type # of the struct. # 9. Compile and link the created program. # 10. Run the program. # 11. Parse the output and fill in the length/offset fields of the structure. # 12. Write out GTMDefinedTypesInit.m which initializes gtmtypes, gtmstructs, and gtmunions arrays for use by # gtmpcat, offset, and eventually perhaps other utilities. # rm -f GTMDefinedTypesInit.m scantypedefs-fail.zshowdmp-*.txt >& /dev/null rm -f scantypedefs-DEBUG-zshowdump.txt >& /dev/null if (`uname -s` == "HP-UX" && `uname -m` != "ia64") setenv gt_cc_options_common "$gt_cc_options_common +W 454" # Suppress -O0 warning $gtm_dist/mumps -run scantypedefs $gtmver ls scantypedefs-fail.zshowdmp-*.txt >& /dev/null if (0 == $status) then echo echo "*** $0 halted due to failures -- tmpdir $tmpdir not removed" echo popd exit 1 endif if (-e scantypedefs-DEBUG-zshowdump.txt) mv -f scantypedefs-DEBUG-zshowdump.txt $ourdir if ( ! -e GTMDefinedTypesInit.m ) then echo "*** $0 halted due to absence of GTMDefinedTypesInit.m -- tmpdir $tmpdir not removed" popd exit 1 endif mv -f GTMDefinedTypesInit.m $ourdir if (-e struct_padding_warn.out) then cp struct_padding_warn.out $ourdir endif popd rm -fr $tmpdir >& /dev/null echo "gengtmdeftypes.csh complete" exit 0 fis-gtm-V7.0-005/sr_unix/genpat.c0000755000032200000250000000417214342376330015472 0ustar librarygtc/**************************************************************** * * * 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 "gtm_string.h" #include "patcode.h" #include "compiler.h" /* This routine translates system specific wildcarded strings into MUMPS patterns, and generates the internal pattern matching literal string into a local buffer by calling the PATSTR() hook into the compiler. The result can be passed directly to DO_PATTERN() to match input. This routine uses the UNIX pattern matching characters of * and ? for multiple and single character wildcarding. */ void genpat(mstr *input, mval *patbuf) { uint4 ecount, status; bool patopen; char source_buffer[MAX_SRCLINE]; char *top, *fpat, *pat_str; ptstr pat_ptstr; mstr instr; pat_str = source_buffer; fpat = input->addr; top = fpat + input->len; patopen = FALSE; while (fpat < top) { if (*fpat == '?') { for (ecount = 0; fpat < top && *fpat == '?'; ecount++) fpat++; if (patopen) { *pat_str++ = '"'; patopen = FALSE; } pat_str = (char *)i2asc((uchar_ptr_t)pat_str, ecount); *pat_str++ = 'E'; } else if (*fpat == '*') { while(fpat < top && *fpat == '*') fpat++; if (patopen) { *pat_str++ = '"'; patopen = FALSE; } *pat_str++ = '.'; *pat_str++ = 'E'; } else { patopen = TRUE; *pat_str++ = '1'; *pat_str++ = '"'; while(fpat < top && *fpat != '*' && *fpat != '?') *pat_str++ = *fpat++; } } if (patopen) *pat_str++ = '"'; instr.addr = source_buffer; instr.len = INTCAST(pat_str - source_buffer); if (status = patstr(&instr, &pat_ptstr, NULL)) rts_error(VARLSTCNT(1) status); assert(pat_ptstr.len <= MAX_PATOBJ_LENGTH); patbuf->str.len = pat_ptstr.len * SIZEOF(uint4); memcpy(patbuf->str.addr, &pat_ptstr.buff[0], patbuf->str.len); } fis-gtm-V7.0-005/sr_unix/get_command_line.c0000755000032200000250000000537614342376330017507 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_ctype.h" #include "stringpool.h" #include "get_command_line.h" #include "restrict.h" GBLREF spdesc stringpool; GBLREF int cmd_cnt; #ifdef __osf__ #pragma pointer_size (save) #pragma pointer_size (long) #endif GBLREF char **cmd_arg; #ifdef __osf__ #pragma pointer_size (restore) #endif void get_command_line(mval *result, boolean_t zcmd_line) { int first_item, len, nlen, word_cnt; size_t ulen; unsigned char *cp, *cp_top; if (RESTRICTED(zcmdline)) { result->mvtype = MV_STR; result->str.len = result->str.char_len = 0; return; } result->mvtype = 0; /* so stp_gcol, if invoked below, can free up space currently occupied by this to-be-overwritten mval */ len = -1; /* to compensate for no space at the end */ if (cmd_cnt > 1) { first_item = 1; if (zcmd_line) { /* $ZCMDLINE returns the processed command line. Remove "-direct" and/or "-run " from cmd line */ if (!memcmp(cmd_arg[1], "-", STRLEN(cmd_arg[1]))) { first_item += 2; cp = (unsigned char *)cmd_arg[2]; } else { cp = (unsigned char *)cmd_arg[1]; if ('-' == *cp++) first_item++; } if ((1 < first_item) && ('r' == TOLOWER(*cp))) first_item++; } for (word_cnt = first_item; word_cnt < cmd_cnt; word_cnt++) { nlen = len + (int)STRLEN(cmd_arg[word_cnt]) + 1; /* include space between arguments */ assert(len < nlen); len = nlen; assert(0 <= len); } } if (0 >= len) { result->str.len = 0; result->mvtype = MV_STR; /* initialize mvtype now that mval has been otherwise completely set up */ return; } ENSURE_STP_FREE_SPACE(len); cp_top = cp = stringpool.free; cp_top += len; stringpool.free += len; result->str.addr = (char *)cp; result->str.len = len; result->mvtype = MV_STR; /* initialize mvtype now that mval has been otherwise completely set up */ for (word_cnt = first_item; cp <= cp_top; *cp++ = ' ') { ulen = strlen(cmd_arg[word_cnt]); DEBUG_ONLY(len = (int)ulen); /* For IS_AT_END_OF_STRINGPOOL below */ assert(cp_top >= (cp + ulen)); memcpy(cp, cmd_arg[word_cnt], ulen); if (++word_cnt == cmd_cnt) break; cp += ulen; /* Do not advance cp for IS_AT_END_OF_STRINGPOOL below */ } assert(IS_AT_END_OF_STRINGPOOL(cp, len)); return; } fis-gtm-V7.0-005/sr_unix/get_full_path.c0000755000032200000250000000576414342376330017041 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "rmv_mul_slsh.h" #include #include #include "gtm_unistd.h" #include "gtm_string.h" #include "gtm_limits.h" #include "gdsroot.h" #include "gdsbt.h" #include "have_crit.h" error_def(ERR_FILENAMETOOLONG); /* Gets the full path name for a given file name. Prepends the CWD, even if the file does not exist. */ boolean_t get_full_path(char *orig_fn, unsigned int orig_len, char *full_fn, unsigned int *full_len, unsigned int max_len, uint4 *status) { char *cptr, *c1; char cwdbuf[GTM_PATH_MAX]; unsigned int cwd_len, dir_len, newfn_len, trim_len; int i; char *getcwd_res; unsigned int length; if ('/' == *orig_fn) { /* The original path is already complete */ if (max_len < orig_len) { *status = ERR_FILENAMETOOLONG; return FALSE; } length = orig_len; memcpy(full_fn, orig_fn, length); } else { GETCWD(cwdbuf, SIZEOF(cwdbuf), getcwd_res); if (NULL == getcwd_res) { *status = errno; return FALSE; } cwd_len = strlen(cwdbuf); cptr = orig_fn; if (('.' == *cptr) && ('.' == *(cptr + 1))) { for (i = 1; ; ++i) { cptr += 2; if (('.' != *(cptr + 1)) || ('.' != *(cptr + 2))) break; ++cptr; } for (c1 = &cwdbuf[cwd_len - 1]; i > 0; --i) while ('/' != *c1) --c1; assert(c1 >= cwdbuf); dir_len = (unsigned int)(c1 - cwdbuf); assert(cptr >= orig_fn); assert((0 <= (unsigned int)(cptr - orig_fn)) && (max_len > (unsigned int)(cptr - orig_fn))); trim_len = (unsigned int)(cptr - orig_fn); assert(orig_len >= trim_len); newfn_len = orig_len - trim_len; length = dir_len + newfn_len; if (max_len < (length + 1)) { *status = ERR_FILENAMETOOLONG; return FALSE; } memcpy(full_fn, cwdbuf, dir_len); memcpy(full_fn + dir_len, cptr, newfn_len); } else { if ('.' == *cptr && '/' == (*(cptr + 1))) cptr += 2; assert(cptr >= orig_fn); assert((0 <= (unsigned int)(cptr - orig_fn)) && (max_len > (unsigned int)(cptr - orig_fn))); trim_len = (unsigned int)(cptr - orig_fn); assert(orig_len >= trim_len); newfn_len = orig_len - trim_len; length = cwd_len + 1 + newfn_len; if (max_len < (length + 1)) { *status = ERR_FILENAMETOOLONG; return FALSE; } memcpy(full_fn, cwdbuf, cwd_len); full_fn[cwd_len++] = '/'; memcpy(full_fn + cwd_len, cptr, newfn_len); } } *full_len = length; /*Remove multiple slash occurances*/ *full_len = rmv_mul_slsh(full_fn, *full_len); full_fn[*full_len] = '\0'; return TRUE; } fis-gtm-V7.0-005/sr_unix/get_page_size.c0000755000032200000250000000300114342376330017007 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_unistd.h" #include "get_page_size.h" #include "gtm_sizeof.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtmio.h" GBLDEF int4 gtm_os_page_size; GBLDEF long gtm_os_hugepage_size = -1; GBLREF bool hugetlb_shm_enabled; void get_page_size(void) { gtm_os_page_size = getpagesize(); return; } void get_hugepage_size(void) { FILE *info; int hps, res; char line[1024]; char *fgets_res; gtm_os_hugepage_size = gtm_os_page_size; /* Default to regular page size */ if (hugetlb_shm_enabled) { Fopen(info, "/proc/meminfo", "r"); if (NULL != info) { FEOF(info, res); while (!res) { res = FSCANF(info, "Hugepagesize: %d kB", &hps); if (EOF == res || ferror(info)) break; else if (1 == res && 0 < hps) { gtm_os_hugepage_size = (long)(1024*hps); break; } else if (0 != res) break; FGETS(line, SIZEOF(line), info, fgets_res); /* Consume the line */ FEOF(info, res); } FCLOSE(info, res); } } return; } fis-gtm-V7.0-005/sr_unix/get_ztimeout.c0000644000032200000250000000542414342376330016731 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018-2021 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_stdio.h" #include "gtm_string.h" #include "stringpool.h" #include "time.h" #include "gt_timer.h" #include "ztimeout_routines.h" #include "deferred_events.h" #include "min_max.h" #include "compiler.h" GBLREF int process_exiting; GBLREF spdesc stringpool; /* "%ld.%ld" ":" \0 */ #define ZTIMEOUTSTRLEN ((2 * MAX_DIGITS_IN_INT8) + 1 + 1 + 1) int get_ztimeout(mval *result) { unsigned char *cp; char *ztimeout_vector_ptr; int ztimeout_vector_len; char full_ztimeout[ZTIMEOUTSTRLEN]; int req_len, time_len; long int ms = 0; ABS_TIME cur_time; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ztimeout_vector_ptr = (TREF(dollar_ztimeout)).ztimeout_vector.str.addr; ztimeout_vector_len = (TREF(dollar_ztimeout)).ztimeout_vector.str.len; if ((((TREF(dollar_ztimeout)).ztimeout_seconds.m[1] / MV_BIAS) == -1)) time_len = SNPRINTF(full_ztimeout, ZTIMEOUTSTRLEN, "%s", !ztimeout_vector_len ? "-1" : "-1:"); else { sys_get_curr_time(&cur_time); cur_time = sub_abs_time(&(TREF(dollar_ztimeout)).end_time, &cur_time); if (0 <= cur_time.at_sec) { ms = DIVIDE_ROUND_DOWN(cur_time.at_usec, MICROSECS_IN_MSEC); time_len = SNPRINTF(full_ztimeout, ZTIMEOUTSTRLEN, (!ztimeout_vector_len ? "%ld.%ld" : "%ld.%ld:"), cur_time.at_sec, ms); } else time_len = SNPRINTF(full_ztimeout, ZTIMEOUTSTRLEN, (!ztimeout_vector_len ? "%ld" : "%ld:"), ms); } assert((0 < time_len) && (time_len <= ZTIMEOUTSTRLEN)); assert(((0 == ztimeout_vector_len) && (NULL == ztimeout_vector_ptr)) || ((0 < ztimeout_vector_len) && (NULL != ztimeout_vector_ptr))); DBGDFRDEVNT((stderr,"%d %s: $ZTIMEOUT = %s%s\n", __LINE__, __FILE__, full_ztimeout, ztimeout_vector_ptr)); req_len = time_len + ztimeout_vector_len; if ((process_exiting) && !(IS_STP_SPACE_AVAILABLE_PRO(req_len))) return -1; /* Process is exiting, avoid adding to the memory pressure */ ENSURE_STP_FREE_SPACE(req_len); cp = stringpool.free; stringpool.free += req_len; result->str.addr = (char *)cp; result->str.len = req_len; result->mvtype = MV_STR; memcpy(cp, full_ztimeout, time_len); if ((0 < ztimeout_vector_len) && (NULL != ztimeout_vector_ptr)) memcpy(cp + time_len, ztimeout_vector_ptr, ztimeout_vector_len); assert(IS_AT_END_OF_STRINGPOOL(cp, req_len)); return 0; } fis-gtm-V7.0-005/sr_unix/getcaps.c0000755000032200000250000001615014342376330015641 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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. * * * ****************************************************************/ /* ------------------------------------------------------ * mdef.h not included because the definition of bool * conflicts with that in curses.h in the AIX platform * also mdef.h is not required here except for the GBLDEF and assert * ------------------------------------------------------ */ #include "gtm_stdlib.h" #include /* must be before term.h */ #include "gtm_term.h" #include "getcaps.h" #include "gtm_sizeof.h" #ifdef DEBUG #include #endif #if defined(__MVS__) && __CHARSET_LIB==1 /* -qascii */ #include "ebc_xlat.h" #endif #ifndef assert #define assert(x) #endif #define GBLDEF #undef KEY_UP #undef KEY_DOWN #undef KEY_RIGHT #undef KEY_LEFT #undef KEY_BACKSPACE #undef KEY_DC #define MAXCOLS 132 #define BEL '\007' GBLDEF int AUTO_RIGHT_MARGIN; /* auto margins */ GBLDEF char *CLR_EOS; /* clear to end of display */ GBLDEF char *CLR_EOL; /* clear to end of line */ GBLDEF int COLUMNS; /* number of columns */ GBLDEF char *CURSOR_ADDRESS; /* cursor motion */ GBLDEF char *CURSOR_DOWN; /* cursor down */ GBLDEF char *CURSOR_LEFT; /* cursor left */ GBLDEF char *CURSOR_RIGHT; /* cursor right */ GBLDEF char *CURSOR_UP; /* cursor up */ GBLDEF int EAT_NEWLINE_GLITCH; /* newline glitch */ GBLDEF char *KEY_BACKSPACE; /* backspace key */ GBLDEF char *KEY_DC; /* delete key */ GBLDEF char *KEY_DOWN; /* down arrow key */ GBLDEF char *KEY_LEFT; /* left arrow key */ GBLDEF char *KEY_RIGHT; /* right arrow key */ GBLDEF char *KEY_UP; /* up arrow key */ GBLDEF char *KEY_INSERT; /* insert key aka KEY_IC */ GBLDEF char *KEYPAD_LOCAL; /* turn keypad off */ GBLDEF char *KEYPAD_XMIT; /* turn keypad on */ GBLDEF int GTM_LINES; /* number of rows */ #ifdef KEEP_zOS_EBCDIC #pragma convlit(suspend) #endif static int gtm_auto_right_margin = 0; static char gtm_clr_eos[] = "\033[J"; static char gtm_clr_eol[] = "\033[K"; static int gtm_columns = 80; static char gtm_cursor_address[] = "\033[%i%p1%d;%p2%dH"; static char gtm_cursor_down[] = "\012"; /* */ static char gtm_cursor_left[] = "\010"; static char gtm_cursor_right[] = "\033[C"; static char gtm_cursor_up[] = "\033[A"; static int gtm_eat_newline_glitch = 1; static char gtm_key_backspace[] = "\010"; static char gtm_key_dc[] = "\033[3~"; static char gtm_key_down[] = "\033OB"; static char gtm_key_left[] = "\033OD"; static char gtm_key_right[] = "\033OC"; static char gtm_key_up[] = "\033OA"; static char gtm_key_insert[] = ""; static char gtm_keypad_local[] = "\033[?1l"; static char gtm_keypad_xmit[] = "\033[?1h"; static int gtm_lines = 24; #if defined(__MVS__) && __CHARSET_LIB==1 /* -qascii */ static char gtm_cap_ascii[16 * 16]; /* ESC_LEN from io.h times number of tigetstr values */ #define CAP2ASCII(CAP) \ { \ ebc_len = strlen(CAP) + 1; \ assert(SIZEOF(gtm_cap_ascii) > (gtm_cap_index + ebc_len)); \ ebc_to_asc((unsigned char *)>m_cap_ascii[gtm_cap_index], (unsigned char *)CAP, ebc_len); \ CAP = >m_cap_ascii[gtm_cap_index]; \ gtm_cap_index += ebc_len; \ } #else #define CAP2ASCII(CAP) #endif #ifdef KEEP_zOS_EBCDIC #pragma convlit(resume) #endif /* extern unsigned char leftkey[], rightkey[], upkey[], downkey[]; */ /* -------------------------------------------------------------- * returns the status whether successful or not. * status = 1 if successful * 0 if TERM is not present in terminfo * -1 if terminfo database could not be opened * -------------------------------------------------------------- */ int getcaps(int fildes) { char *cap; #if defined(__MVS__) && __CHARSET_LIB==1 /* -qascii */ char cap_ebcdic[128]; /* more than enough for terminal name */ int ebc_len, gtm_cap_index = 0; #endif int status; cap = GETENV("TERM"); if (!cap) cap = "unknown"; #if defined(__MVS__) && __CHARSET_LIB==1 /* -qascii */ ebc_len = strlen(cap); if (SIZEOF(cap_ebcdic) < ebc_len) ebc_len = SIZEOF(cap_ebcdic) - 1; asc_to_ebc((unsigned char *)cap_ebcdic, (unsigned char *)cap, ebc_len); cap_ebcdic[ebc_len] = '\0'; cap = cap_ebcdic; #endif SETUPTERM(cap, fildes, &status); if (1 == status) { #ifdef KEEP_zOS_EBCDIC #pragma convlit(suspend) #endif #if defined(__MVS__) && __CHARSET_LIB==1 /* -qascii */ /* assumes source is EBCDIC and curses/terminfo entries expect EBCDIC */ #pragma convert(source) #endif AUTO_RIGHT_MARGIN = tigetflag("am"); CLR_EOS = tigetstr("ed"); CLR_EOL = tigetstr("el"); COLUMNS = tigetnum("cols"); CURSOR_ADDRESS = tigetstr("cup"); CURSOR_DOWN = tigetstr("cud1"); CURSOR_LEFT = tigetstr("cub1"); CURSOR_RIGHT = tigetstr("cuf1"); CURSOR_UP = tigetstr("cuu1"); EAT_NEWLINE_GLITCH = tigetflag("xenl"); KEY_BACKSPACE = tigetstr("kbs"); KEY_DC = tigetstr("kdch1"); KEY_DOWN = tigetstr("kcud1"); KEY_LEFT = tigetstr("kcub1"); KEY_RIGHT = tigetstr("kcuf1"); KEY_UP = tigetstr("kcuu1"); KEY_INSERT = tigetstr("kich1"); KEYPAD_LOCAL = tigetstr("rmkx"); KEYPAD_XMIT = tigetstr("smkx"); GTM_LINES = tigetnum("lines"); #if defined(__MVS__) && __CHARSET_LIB==1 /* -qascii */ #pragma convert(pop) #endif #ifdef KEEP_zOS_EBCDIC #pragma convlit(resume) #endif assert(-1 != AUTO_RIGHT_MARGIN); assert((char *)-1 != CLR_EOS); CAP2ASCII(CLR_EOS); assert((char *)-1 != CLR_EOL); CAP2ASCII(CLR_EOL); assert(-2 != COLUMNS); assert((char *)-1 != CURSOR_ADDRESS); CAP2ASCII(CURSOR_ADDRESS); assert((char *)-1 != CURSOR_DOWN); CAP2ASCII(CURSOR_DOWN); assert((char *)-1 != CURSOR_LEFT); CAP2ASCII(CURSOR_LEFT); assert((char *)-1 != CURSOR_RIGHT); CAP2ASCII(CURSOR_RIGHT); assert((char *)-1 != CURSOR_UP); CAP2ASCII(CURSOR_UP); assert((char *)-1 != KEY_BACKSPACE); CAP2ASCII(KEY_BACKSPACE); assert((char *)-1 != KEY_DC); CAP2ASCII(KEY_DC); assert((char *)-1 != KEY_DOWN); CAP2ASCII(KEY_DOWN); assert((char *)-1 != KEY_LEFT); CAP2ASCII(KEY_LEFT); assert((char *)-1 != KEY_RIGHT); CAP2ASCII(KEY_RIGHT); assert((char *)-1 != KEY_UP); CAP2ASCII(KEY_UP); assert((char *)-1 != KEY_INSERT); CAP2ASCII(KEY_INSERT); assert((char *)-1 != KEYPAD_LOCAL); CAP2ASCII(KEYPAD_LOCAL); assert((char *)-1 != KEYPAD_XMIT); CAP2ASCII(KEYPAD_XMIT); assert(-2 != GTM_LINES); } else { AUTO_RIGHT_MARGIN = gtm_auto_right_margin; CLR_EOS = gtm_clr_eos; CLR_EOL = gtm_clr_eol; COLUMNS = gtm_columns; CURSOR_ADDRESS = gtm_cursor_address; CURSOR_DOWN = gtm_cursor_down; CURSOR_LEFT = gtm_cursor_left; CURSOR_RIGHT = gtm_cursor_right; CURSOR_UP = gtm_cursor_up; EAT_NEWLINE_GLITCH = gtm_eat_newline_glitch; KEY_BACKSPACE = gtm_key_backspace; KEY_DC = gtm_key_dc; KEY_DOWN = gtm_key_down; KEY_LEFT = gtm_key_left; KEY_RIGHT = gtm_key_right; KEY_UP = gtm_key_up; KEY_INSERT = gtm_key_insert; KEYPAD_LOCAL = gtm_keypad_local; KEYPAD_XMIT = gtm_keypad_xmit; GTM_LINES = gtm_lines; } return status; } fis-gtm-V7.0-005/sr_unix/getcaps.h0000755000032200000250000000113514342376330015643 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GETCAPS_H_INCLUDED #define GETCAPS_H_INCLUDED int getcaps(int fildes); #endif fis-gtm-V7.0-005/sr_unix/geteditor.c0000755000032200000250000000302114342376330016172 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_string.h" #include "gtm_stdlib.h" #include "gtm_unistd.h" #include "gtm_limits.h" #include "eintr_wrappers.h" #include "geteditor.h" #include "wbox_test_init.h" GBLDEF mstr editor; void geteditor(void) { char *edt, **pedt; size_t len; int iter; char *editor_list[] = { "/usr/bin/vi", "/usr/ucb/vi", "/bin/vi", /* Linux */ 0 /* this array should be terminated by a 0 */ }; edt = GETENV("EDITOR"); pedt = &editor_list[0]; do { if (0 == ACCESS(edt, (F_OK|X_OK))) /* File exists and is executable, use it */ break; edt = *pedt++; } while (edt); WBTEST_ASSIGN_ONLY(WBTEST_BADEDITOR_GETEDITOR, edt, 0); if (edt) { len = strlen(edt); if (PATH_MAX < len) /* 4SCA: access() ensures path is < PATH_MAX */ len = PATH_MAX; editor.len = (mstr_len_t)len; if (PATH_MAX >= len) /* 4SCA: Increment behind a guard */ len++; editor.addr = (char*) malloc(len); /* must be null terminated */ memcpy(editor.addr, edt, len); } else editor.len = 0; } fis-gtm-V7.0-005/sr_unix/geteditor.h0000755000032200000250000000105414342376330016203 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GETEDITOR_INCLUDED #define GETEDITOR_INCLUDED void geteditor(void); #endif /* GETEDITOR_INCLUDED */ fis-gtm-V7.0-005/sr_unix/getjobnum.c0000755000032200000250000000136714342376330016211 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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_unistd.h" #include "getjobnum.h" GBLREF uint4 process_id; GBLREF uid_t user_id; void getjobnum(void) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; process_id = getpid(); user_id = (uint4)getuid(); } fis-gtm-V7.0-005/sr_unix/getmaxfds.c0000755000032200000250000000133514342376330016174 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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. * * * ****************************************************************/ /* * * This function returns the maximum number * of file descriptors (channels) per process. * */ #include #include "getmaxfds.h" int getmaxfds(void) { struct rlimit rlp; if (getrlimit(RLIMIT_NOFILE, &rlp) < 0) return -1; return (int)rlp.rlim_cur; } fis-gtm-V7.0-005/sr_unix/getmaxfds.h0000755000032200000250000000113514342376330016177 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GETMAXFDS_H_INCLUDED #define GETMAXFDS_H_INCLUDED int getmaxfds(void); #endif fis-gtm-V7.0-005/sr_unix/getstorage.c0000644000032200000250000000310114342376330016344 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include #include #include "gtm_unistd.h" #include "getstorage.h" #define ERRSTR "getrlimit()" error_def(ERR_SYSCALL); int4 getstorage(void) { struct rlimit rl; int save_errno; UINTPTR_T cur_sbrk; rlim_t size; if (0 != getrlimit(RLIMIT_DATA,&rl)) { save_errno = errno; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL(ERRSTR), CALLFROM, save_errno); } #if !defined(__MVS__) cur_sbrk = (UINTPTR_T)sbrk(0); /* Two step conversion to eliminate warnings */ #else cur_sbrk = 0; /* smw until something better */ #endif size = rl.rlim_cur - cur_sbrk; /* #if !defined(GTM64) && defined(INT8_SUPPORTED) */ if (MAXPOSINT4 < size) size = MAXPOSINT4; else if (0 > size) size = 0; /* Temporarily, all platform return a diminished potential storage value until stack alignment issues on x86_64 * are fixed allowing floats again or a better fix is made. * * #elif defined(GTM64) * if (MAX_LONG_IN_DOUBLE < size) * size = MAX_LONG_IN_DOUBLE; * #endif */ return size; } fis-gtm-V7.0-005/sr_unix/getzmode.c0000755000032200000250000000114414342376330016026 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "getzmode.h" void getzmode(void) { /* doesn't need to do anything in Unix. jobchild_init() sets dollar_zmode appropriately */ return; } fis-gtm-V7.0-005/sr_unix/getzprocess.c0000755000032200000250000000123314342376330016557 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2002 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "getzprocess.h" GBLDEF mval dollar_zproc; void getzprocess(void) { dollar_zproc.mvtype = MV_STR; dollar_zproc.str.addr = ""; dollar_zproc.str.len = 0; return; } fis-gtm-V7.0-005/sr_unix/goq_load.c0000755000032200000250000000110514342376330015772 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "load.h" void goq_load(void) { error_def(ERR_UNIMPLOP); rts_error(VARLSTCNT(1) ERR_UNIMPLOP); return; } fis-gtm-V7.0-005/sr_unix/gpgagent.tab.in0000644000032200000250000000021114342376330016724 0ustar librarygtc@GTM_TOP@/plugin/libgtmcrypt.so unmaskpwd: xc_status_t gc_pk_mask_unmask_passwd_interlude(I:xc_string_t*,O:xc_string_t*[512],I:xc_int_t) fis-gtm-V7.0-005/sr_unix/grab_crit.c0000644000032200000250000001324014342376330016141 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_signal.h" /* for VSIG_ATOMIC_T type */ #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsbgtr.h" #include "filestruct.h" #include "send_msg.h" #include "mutex.h" #include "wcs_recover.h" #include "deferred_signal_handler.h" #include "caller_id.h" #include "is_proc_alive.h" #ifdef DEBUG #include "gtm_stdio.h" #include "gt_timer.h" #include "wbox_test_init.h" #include "repl_msg.h" #include "gtmsource.h" #include "jnl.h" #endif #include "gtmimagename.h" #include "error.h" #include "anticipatory_freeze.h" GBLREF volatile int4 crit_count; GBLREF short crash_count; GBLREF uint4 process_id; GBLREF node_local_ptr_t locknl; GBLREF jnlpool_addrs_ptr_t jnlpool; #ifdef DEBUG GBLREF jnl_gbls_t jgbl; #endif GBLREF boolean_t mupip_jnl_recover; error_def(ERR_CRITRESET); error_def(ERR_DBCCERR); error_def(ERR_DBFLCORRP); void grab_crit(gd_region *reg, wait_state state) { unix_db_info *udi; sgmnt_addrs *csa; node_local_ptr_t cnl; sgmnt_data_ptr_t csd; enum cdb_sc status; mutex_spin_parms_ptr_t mutex_spin_parms; intrpt_state_t prev_intrpt_state; # ifdef DEBUG sgmnt_addrs *jnlpool_csa; jnlpool_addrs_ptr_t local_jnlpool, save_jnlpool; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; udi = FILE_INFO(reg); csa = &udi->s_addrs; csd = csa->hdr; cnl = csa->nl; # ifdef DEBUG save_jnlpool = jnlpool; if (csa->jnlpool && (csa->jnlpool != jnlpool)) jnlpool = csa->jnlpool; if (gtm_white_box_test_case_enabled && (WBTEST_SENDTO_EPERM == gtm_white_box_test_case_number) && (0 == cnl->wbox_test_seq_num)) { FPRINTF(stderr, "MUPIP BACKUP entered grab_crit\n"); cnl->wbox_test_seq_num = 1; while (2 != cnl->wbox_test_seq_num) LONG_SLEEP(1); FPRINTF(stderr, "MUPIP BACKUP resumed in grab_crit\n"); cnl->wbox_test_seq_num = 3; } # endif # ifdef underconstruction if (TREF(in_mupip_integ) && IS_REPL_INST_FROZEN && !TREF(integ_cannotskip_crit)) { TREF(instance_frozen_crit_skipped) = TRUE; return; } # endif assert(!csa->hold_onto_crit); if (!csa->now_crit) { # ifdef DEBUG local_jnlpool = csa->jnlpool; assert((NULL == local_jnlpool) || (local_jnlpool == jnlpool)); if ((NULL != local_jnlpool) && (NULL != local_jnlpool->jnlpool_ctl)) { /* We should never request crit on a database region while already holding the lock on the journal pool. * Not following the protocol (obtaining lock on journal pool AFTER obtaining crit on database region), * can lead to potential deadlocks */ jnlpool_csa = &FILE_INFO(local_jnlpool->jnlpool_dummy_reg)->s_addrs; assert(!jnlpool_csa->now_crit); } # endif DEFER_INTERRUPTS(INTRPT_IN_CRIT_FUNCTION, prev_intrpt_state); TREF(grabbing_crit) = reg; DEBUG_ONLY(locknl = cnl;) /* for DEBUG_ONLY LOCK_HIST macro */ mutex_spin_parms = (mutex_spin_parms_ptr_t)&csd->mutex_spin_parms; status = gtm_mutex_lock(reg, mutex_spin_parms, crash_count, MUTEX_LOCK_WRITE, state); assert((NULL == local_jnlpool) || (local_jnlpool == jnlpool)); # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_SENDTO_EPERM == gtm_white_box_test_case_number) && (1 == cnl->wbox_test_seq_num)) { FPRINTF(stderr, "MUPIP SET entered grab_crit\n"); cnl->wbox_test_seq_num = 2; while (3 != cnl->wbox_test_seq_num) LONG_SLEEP(1); FPRINTF(stderr, "MUPIP SET resumed in grab_crit\n"); } # endif DEBUG_ONLY(locknl = NULL;) /* restore "locknl" to default value */ if (status != cdb_sc_normal) { # ifdef DEBUG if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; # endif ENABLE_INTERRUPTS(INTRPT_IN_CRIT_FUNCTION, prev_intrpt_state); TREF(grabbing_crit) = NULL; switch(status) { case cdb_sc_critreset: RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_CRITRESET, 2, REG_LEN_STR(reg)); case cdb_sc_dbccerr: RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBCCERR, 2, REG_LEN_STR(reg)); default: assertpro((cdb_sc_critreset == status) || (cdb_sc_dbccerr == status)); } return; } /* There is only one case we know of when cnl->in_crit can be non-zero and that is when a process holding * crit gets kill -9ed and another process ends up invoking "secshr_db_clnup" which in turn clears the * crit semaphore (making it available for waiters) but does not also clear cnl->in_crit since it does not * hold crit at that point. But in that case, the pid reported in cnl->in_crit should be dead. Check that. */ assert((0 == cnl->in_crit) || (FALSE == is_proc_alive(cnl->in_crit, 0))); cnl->in_crit = process_id; CRIT_TRACE(csa, crit_ops_gw); /* see gdsbt.h for comment on placement */ TREF(grabbing_crit) = NULL; ENABLE_INTERRUPTS(INTRPT_IN_CRIT_FUNCTION, prev_intrpt_state); } /* Commands/Utilties that plays with the file_corrupt flags (DSE/MUPIP SET -PARTIAL_RECOV_BYPASS/RECOVER/ROLLBACK) should * NOT issue DBFLCORRP. Use skip_file_corrupt_check global variable for this purpose */ if (csd->file_corrupt && !TREF(skip_file_corrupt_check)) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBFLCORRP, 2, DB_LEN_STR(reg)); if (WC_BLOCK_RECOVER == cnl->wc_blocked) wcs_recover(reg); # ifdef DEBUG if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; # endif return; } fis-gtm-V7.0-005/sr_unix/grab_crit_encr_cycle_sync.c0000644000032200000250000000632414342376330021370 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2020 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_string.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "process_reorg_encrypt_restart.h" GBLREF sgmnt_addrs *reorg_encrypt_restart_csa; GBLREF uint4 mu_reorg_encrypt_in_prog; /* non-zero if MUPIP REORG ENCRYPT is in progress */ GBLREF uint4 process_id; #ifdef DEBUG GBLREF uint4 dollar_tlevel; #endif /* This is a version of "grab_crit" that returns with crit held but also ensures * "csa->nl->reorg_encrypt_cycle == csa->encr_ptr->reorg_encrypt_cycle" and that the encryption * sync happens without holding crit (as that can take a lot of time involving external access to gpg). * This should be used by * 1) Callers of "t_qread" who expect that holding crit and doing t_qread is guaranteed to not return a NULL value * (NULL is possible if the encryption cycles are different). Examples of such callers are * a) All dse*.c modules * b) mu_reorg_upgrd_dwngrd.c when it does a t_qread of a bitmap block inside crit * c) mupip_reorg_encrypt.c when it does a t_qread of a bitmap block inside crit * d) gvcst_expand_free_subtree.c when it does a t_qread of a killed index block to find its descendants to kill. * 2) Functions like "t_retry" and "tp_restart" which grab_crit to enter into the final retry. It is best to do * the heavyweight new-encryption-handle opening operation while outside of crit. * * Returns: TRUE if new-encryption-handles were opened (i.e. "process_reorg_encrypt_restart" was invoked) at least once. * FALSE otherwise. */ boolean_t grab_crit_encr_cycle_sync(gd_region *reg, wait_state state) { boolean_t sync_needed; enc_info_t *encr_ptr; enum cdb_sc status; node_local_ptr_t cnl; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; unix_db_info *udi; udi = FILE_INFO(reg); csa = &udi->s_addrs; grab_crit(reg, state); encr_ptr = csa->encr_ptr; sync_needed = FALSE; if (NULL != encr_ptr) { csd = csa->hdr; cnl = csa->nl; assert(NULL == reorg_encrypt_restart_csa); while (cnl->reorg_encrypt_cycle != encr_ptr->reorg_encrypt_cycle) { sync_needed = TRUE; /* Cycles mismatch. Fix the cycles. Take copy of shared memory while holding crit but do the * "process_encrypt_restart_csa" after releasing crit as that is a heavyweight operation * (involving access to external "gpg"). */ SIGNAL_REORG_ENCRYPT_RESTART(mu_reorg_encrypt_in_prog, reorg_encrypt_restart_csa, cnl, csa, csd, status, process_id); rel_crit(reg); assert(csa == reorg_encrypt_restart_csa); process_reorg_encrypt_restart(); assert(NULL == reorg_encrypt_restart_csa); grab_crit(reg, state); } } return sync_needed; } fis-gtm-V7.0-005/sr_unix/grab_crit_immediate.c0000644000032200000250000001107314342376330020161 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_signal.h" /* for VSIG_ATOMIC_T type */ #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsbgtr.h" #include "filestruct.h" #include "send_msg.h" #include "mutex.h" #include "deferred_signal_handler.h" #include "caller_id.h" #include "is_proc_alive.h" #include "gtmimagename.h" #include "error.h" #include "wcs_recover.h" GBLREF short crash_count; GBLREF volatile int4 crit_count; GBLREF uint4 process_id; GBLREF node_local_ptr_t locknl; #ifdef DEBUG GBLREF boolean_t mupip_jnl_recover; #endif error_def(ERR_CRITRESET); error_def(ERR_DBCCERR); error_def(ERR_DBFLCORRP); boolean_t grab_crit_immediate(gd_region *reg, boolean_t ok_for_wcs_recover, wait_state state) { unix_db_info *udi; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; node_local_ptr_t cnl; enum cdb_sc status; mutex_spin_parms_ptr_t mutex_spin_parms; intrpt_state_t prev_intrpt_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; udi = FILE_INFO(reg); csa = &udi->s_addrs; csd = csa->hdr; cnl = csa->nl; if (!csa->now_crit) { DEFER_INTERRUPTS(INTRPT_IN_CRIT_FUNCTION, prev_intrpt_state); DEBUG_ONLY(locknl = cnl;) /* for DEBUG_ONLY LOCK_HIST macro */ mutex_spin_parms = (mutex_spin_parms_ptr_t)&csd->mutex_spin_parms; status = gtm_mutex_lock(reg, mutex_spin_parms, crash_count, MUTEX_LOCK_WRITE_IMMEDIATE, state); DEBUG_ONLY(locknl = NULL;) /* restore "locknl" to default value */ if (status != cdb_sc_normal) { ENABLE_INTERRUPTS(INTRPT_IN_CRIT_FUNCTION, prev_intrpt_state); switch (status) { case cdb_sc_nolock: return(FALSE); case cdb_sc_critreset: RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_CRITRESET, 2, REG_LEN_STR(reg)); break; /* Keep syntax checkers happy */ case cdb_sc_dbccerr: RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBCCERR, 2, REG_LEN_STR(reg)); break; /* Keep syntax checkers happy */ default: /* An out-of-design return value. assertpro(FALSE) but spell out the failing condition * details instead of just FALSE. Hence the complex structure below. */ assertpro((cdb_sc_nolock != status) && (cdb_sc_critreset != status) && (cdb_sc_dbccerr != status) && FALSE); } return(FALSE); } /* There is only one case we know of when cnl->in_crit can be non-zero and that is when a process holding * crit gets kill -9ed and another process ends up invoking "secshr_db_clnup" which in turn clears the * crit semaphore (making it available for waiters) but does not also clear cnl->in_crit since it does not * hold crit at that point. But in that case, the pid reported in cnl->in_crit should be dead. Check that. */ assert((0 == cnl->in_crit) || (FALSE == is_proc_alive(cnl->in_crit, 0))); cnl->in_crit = process_id; CRIT_TRACE(csa, crit_ops_gw); /* see gdsbt.h for comment on placement */ ENABLE_INTERRUPTS(INTRPT_IN_CRIT_FUNCTION, prev_intrpt_state); } else assert(FALSE); /* Commands/Utilties that plays with the file_corrupt flags (DSE/MUPIP SET -PARTIAL_RECOV_BYPASS/RECOVER/ROLLBACK) should * NOT issue DBFLCORRP. Use skip_file_corrupt_check global variable for this purpose. Ideally we need this check only * in grab_crit.c and not grab_crit_immediate.c as all the above commands/utilities only go to grab_crit and do not come * here but we keep it the same for consistency. */ /* Assert that MUPIP RECOVER/ROLLBACK has TREF(skip_file_corrupt_check)=TRUE and so does not issue DBFLCORRP error */ assert(!mupip_jnl_recover || TREF(skip_file_corrupt_check)); if (csd->file_corrupt && !TREF(skip_file_corrupt_check)) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBFLCORRP, 2, DB_LEN_STR(reg)); if (WC_BLOCK_RECOVER == cnl->wc_blocked) { if (ok_for_wcs_recover) wcs_recover(reg); else { /* Caller says it is not okay to call "wcs_recover" so return as if crit could not be obtained right away. * These callers are periodic/cyclical and interrupt-based so they will retry later if needed * (e.g. wcs_wtstart and wcs_clean_dbsync). */ rel_crit(reg); return(FALSE); } } return(TRUE); } fis-gtm-V7.0-005/sr_unix/grab_latch.c0000644000032200000250000000614514342376330016301 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2021 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" #ifdef DEBUG #include "gtm_unistd.h" /* for "getpid" */ #endif #include "gtm_facility.h" #include "gdsroot.h" #include "fileinfo.h" #include "gdsbt.h" #include "interlock.h" #include "performcaslatchcheck.h" #include "gtm_rel_quant.h" #include "sleep_cnt.h" #include "wcs_sleep.h" #include "min_max.h" #include "gt_timer.h" GBLREF int num_additional_processors; GBLREF uint4 process_id; GBLREF volatile int4 fast_lock_count; /* Stop interrupts while we have our parts exposed */ /* Grab a latch. If cannot get it in the approximate time requested, return FALSE, else TRUE. * originally intended to protect verify_queue which only runs in debug * but adopted by some auto-relink code - not clear that's appropriate */ boolean_t grab_latch(sm_global_latch_ptr_t latch, int max_timeout_in_secs, wait_state state, sgmnt_addrs *csa) { ABS_TIME cur_time, end_time, remain_time; int maxspins, retries, spins; int4 max_sleep_mask; boolean_t skip_time_calc; assert(process_id == getpid()); /* Make sure "process_id" global variable is reliable (used below in an assert) */ if (process_id == latch->u.parts.latch_pid) { /* Already have lock */ assert(FALSE); /* Don't expect caller to call us if we hold the lock already. in pro be safe and return */ return TRUE; } skip_time_calc = !max_timeout_in_secs || (GRAB_LATCH_INDEFINITE_WAIT == max_timeout_in_secs); if (!skip_time_calc) { sys_get_curr_time(&cur_time); add_int_to_abs_time(&cur_time, max_timeout_in_secs * 1000, &end_time); remain_time.at_sec = 0; /* ensure one try */ } /* Define number of hard-spins the inner loop does */ maxspins = num_additional_processors ? MAX_LOCK_SPINS(LOCK_SPINS, num_additional_processors) : 1; UPDATE_CRIT_COUNTER(csa, state); for (retries = 1; ; retries++) { ++fast_lock_count; /* Disable interrupts (i.e. wcs_stale) for duration to avoid potential deadlocks */ for (spins = maxspins; spins > 0 ; spins--) { /* We better not hold it if trying to get it */ assert(latch->u.parts.latch_pid != process_id); if (GET_SWAPLOCK(latch)) { --fast_lock_count; assert(0 <= fast_lock_count); return TRUE; } } --fast_lock_count; if (!max_timeout_in_secs) break; if (!skip_time_calc) { REST_FOR_LATCH(latch, USEC_IN_NSEC_MASK, retries); sys_get_curr_time(&cur_time); remain_time = sub_abs_time(&end_time, &cur_time); if (0 > remain_time.at_sec) break; } else { /* Indefinite wait for lock. Periodically check if latch is held by dead pid. If so get it back. */ SLEEP_FOR_LATCH(latch, retries); } } assert(0 <= fast_lock_count); assert(FALSE); return FALSE; } fis-gtm-V7.0-005/sr_unix/grab_lock.c0000644000032200000250000001642514342376330016140 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_signal.h" /* for VSIG_ATOMIC_T type */ #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "mutex.h" #include "deferred_signal_handler.h" #include "caller_id.h" #include "is_proc_alive.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_instance.h" #include "jnl.h" #include "gtmimagename.h" /* for IS_GTCM_GNP_SERVER_IMAGE */ #include "anticipatory_freeze.h" #include "util.h" /* for OUT_BUFF_SIZE */ GBLREF volatile int4 crit_count; GBLREF uint4 process_id; GBLREF node_local_ptr_t locknl; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnl_gbls_t jgbl; GBLREF boolean_t is_src_server; GBLREF boolean_t jnlpool_init_needed; error_def(ERR_DBCCERR); error_def(ERR_CRITRESET); error_def(ERR_REPLREQROLLBACK); error_def(ERR_TEXT); /* Note about usage of this function : Create dummy gd_region, gd_segment, file_control, * unix_db_info, sgmnt_addrs, and allocate mutex_struct (and NUM_CRIT_ENTRY * mutex_que_entry), * mutex_spin_parms_struct, and node_local in shared memory. Initialize the fields as in * jnlpool_init(). Pass the address of the dummy region as argument to this function. */ boolean_t grab_lock(gd_region *reg, boolean_t is_blocking_wait, uint4 onln_rlbk_action) { unix_db_info *udi; sgmnt_addrs *csa; enum cdb_sc status; mutex_spin_parms_ptr_t mutex_spin_parms; intrpt_state_t prev_intrpt_state; char scndry_msg[OUT_BUFF_SIZE]; # ifdef DEBUG DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # endif udi = FILE_INFO(reg); csa = &udi->s_addrs; assert(!csa->hold_onto_crit); assert(!csa->now_crit); if (!csa->now_crit) { DEFER_INTERRUPTS(INTRPT_IN_CRIT_FUNCTION, prev_intrpt_state); DEBUG_ONLY(locknl = csa->nl); /* for DEBUG_ONLY LOCK_HIST macro */ assert(jnlpool && jnlpool->jnlpool_ctl); /* pool_init not yet set when called from jnlpool_init */ assert(reg == jnlpool->jnlpool_dummy_reg); mutex_spin_parms = (mutex_spin_parms_ptr_t)((sm_uc_ptr_t)csa->critical + JNLPOOL_CRIT_SPACE); /* This assumes that mutex_spin_parms_t is located immediately after the crit structures */ /* As of 10/07/98, crashcnt field in mutex_struct is not changed by any function for the dummy region */ if (is_blocking_wait) status = gtm_mutex_lock(reg, mutex_spin_parms, 0, MUTEX_LOCK_WRITE, NOT_APPLICABLE); else status = gtm_mutex_lock(reg, mutex_spin_parms, 0, MUTEX_LOCK_WRITE_IMMEDIATE, NOT_APPLICABLE); DEBUG_ONLY(locknl = NULL); /* restore "locknl" to default value */ if (status != cdb_sc_normal) { ENABLE_INTERRUPTS(INTRPT_IN_CRIT_FUNCTION, prev_intrpt_state); switch(status) { case cdb_sc_critreset: /* As of 10/07/98, this return value is not possible */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_CRITRESET, 2, REG_LEN_STR(reg)); case cdb_sc_dbccerr: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_DBCCERR, 2, REG_LEN_STR(reg)); case cdb_sc_nolock: return FALSE; default: assertpro(FALSE && status); } return FALSE; } /* There is only one case we know of when csa->nl->in_crit can be non-zero and that is when a process holding * crit gets kill -9ed and another process ends up invoking "secshr_db_clnup" which in turn clears the * crit semaphore (making it available for waiters) but does not also clear csa->nl->in_crit since it does not * hold crit at that point. But in that case, the pid reported in csa->nl->in_crit should be dead. Check that. */ assert((0 == csa->nl->in_crit) || (FALSE == is_proc_alive(csa->nl->in_crit, 0))); csa->nl->in_crit = process_id; CRIT_TRACE(csa, crit_ops_gw); /* see gdsbt.h for comment on placement */ ENABLE_INTERRUPTS(INTRPT_IN_CRIT_FUNCTION, prev_intrpt_state); if (jnlpool && jnlpool->repl_inst_filehdr && jnlpool->repl_inst_filehdr->file_corrupt && !jgbl.onlnrlbk) { /* Journal pool indicates an abnormally terminated online rollback. Cannot continue until the rollback * command is re-run to bring the journal pool/file and instance file to a consistent state. */ SNPRINTF(scndry_msg, OUT_BUFF_SIZE, "Instance file header has file_corrupt field set to TRUE"); /* No need to do rel_lock before rts_error (mupip_exit_handler will do it for us) - BYPASSOK rts_error */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_REPLREQROLLBACK, 2, LEN_AND_STR(udi->fn), ERR_TEXT, 2, LEN_AND_STR(scndry_msg)); } /* If ASSERT_NO_ONLINE_ROLLBACK, then no concurrent online rollbacks can happen at this point. So, the jnlpool * should be in in sync. There are two exceptions. If this is GT.CM GNP Server and the last client disconnected, the * server invokes gtcmd_rundown which in-turn invokes gds_rundown thereby running down all active databases at this * point but leaves the journal pool up and running. Now, if an online rollback is attempted, it increments the * onln_rlbk_cycle in the journal pool, but csa->onln_rlbk_cycle is not synced yet. So, the grab_crit done in t_end * will NOT detect a concurrent online rollback and it doesn't need to because the rollback happened AFTER the * rundown. Assert that this is the only case we know of for the cycles to be out-of-sync. In PRO * jnlpool_ctl->onln_rlbk_cycle is used only by the replication servers (which GT.CM is not) and so even if it * continues with an out-of-sync csa->onln_rlbk_cycle, t_end logic does the right thing. The other exception is if * GT.M initialized journal pool while opening database (belonging to a different instance) in gvcst_init (for * anticipatory freeze) followed by an online rollback which increments the jnlpool_ctl->onln_rlbk_cycle but leaves * the repl_csa->onln_rlbk_cycle out-of-sync. At this point, if a replicated database is open for the first time, * we'll reach t_end to commit the update but will end up failing the below assert due to the out-of-sync * onln_rlbk_cycle. So, assert accordingly. Note : even though the cycles are out-of-sync they are not an issue for * GT.M because it always relies on the onln_rlbk_cycle from csa->nl and not from repl_csa. But, we don't remove the * assert as it is valuable for replication servers (Source, Receiver and Update Process). */ assert((ASSERT_NO_ONLINE_ROLLBACK != onln_rlbk_action) || (jnlpool_init_needed && INST_FREEZE_ON_ERROR_POLICY) || (jnlpool && jnlpool->jnlpool_ctl && csa && (csa->onln_rlbk_cycle == jnlpool->jnlpool_ctl->onln_rlbk_cycle)) || IS_GTCM_GNP_SERVER_IMAGE); if ((HANDLE_CONCUR_ONLINE_ROLLBACK == onln_rlbk_action) && (jnlpool && jnlpool->jnlpool_ctl && (csa->onln_rlbk_cycle != jnlpool->jnlpool_ctl->onln_rlbk_cycle))) { assert(is_src_server); SYNC_ONLN_RLBK_CYCLES; gtmsource_onln_rlbk_clnup(); /* side-effect : sets gtmsource_state */ rel_lock(reg); /* caller knows to disconnect and re-establish the connection */ } } return TRUE; } fis-gtm-V7.0-005/sr_unix/gse.mpt0000755000032200000250000000567014342376330015354 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2006-2018 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %GSE ;GT.M %GSE utility - global search ; n c,i,g,gn,gtmvt,m,n,p,rl,s,sl,sx,tics,x,%ZD,%ZG i ('$d(%ZQ)) n %ZQ s %ZQ=0 i '$d(%zdebug) n $et s $et="zg "_$zl_":ERR^%GSE" u $p:(ctrap=$c(3):exc="zg "_$zl_":LOOP^%GSE") w !,"Global Search for Every Occurrence",! d base q base f d q:$l(%ZD) . r !,"Output device: : ",%ZD,! . i '$l(%ZD) s %ZD=$p q . i %ZD="^" q . i %ZD="?" d q . . w !!,"Select the device you want for output" . . w !,"If you wish to exit enter a carat (^)",! . . s %ZD="" . i $zparse(%ZD)="" w " no such device" s %ZD="" q . o %ZD:(newversion:block=2048:record=2044:exception="g noopen"):0 . i '$t w !,%ZD," is not available" s %ZD="" q . q noopen . w !,$p($ZS,",",2,999),! c %ZD s %ZD="" q:%ZD="^" i %ZD'=$p u %ZD w $zd($h,"DD-MON-YEAR 24:60:SS"),!,"Global Search for Every occurrence",! u $p f d main q:'%ZG c:%ZD'=$p %ZD u $p:(ctrap="":exc="") q main k %ZG d CALL^%GSEL i '%ZG q s gn="" r "Find string: ",s,!! i s="" w !,"No string to find - no search done.",! s %ZG=0 q i s?.E1C.E w !,"The Find string contains control characters" u %ZD s gtmvt=$$GTMVT i gtmvt s sx=$c(27)_"[7m"_s_$c(27)_"[0m" e d . s sx=s . n si,sc . set sl=0 . f si=1:1:$length(s) set sc=$e(s,si) d . . i $a(sc)<128 set sl=sl+1 . . e set sl=sl+$zwidth(sc) . s tics=$tr($j("",sl)," ","^") f s gn=$o(%ZG(gn)) q:gn="" d search q search n wrotegn s g=gn,(m,n)=0 i %ZQ s wrotegn=0 e s wrotegn=1 u %ZD w:$x>70 ! w gn,?$x\10+1*10 i ($d(@g)#10=1) s n=1 d:@g[s show f s g=$q(@g) q:g="" s n=n+1 d:@g[s show i m w !!,"Total ",m," matches found in ",n," nodes.",! e i ((m>0)!('%ZQ)) w !,"No matches found in ",n," nodes.",! u $p q show n gl s gl=$s($l(g)>16:$l(g),1:16) s x=@g,c=$l(x,s),m=m+c-1,rl=$j("",gl) i ('wrotegn) s wrotegn=1 u %ZD w:$x>70 ! w gn,?$x\10+1*10 w !,g,?gl f i=1:1:c-1 s p=$tr($p(x,s,i),$c(9)," ") w p,sx i 'gtmvt s rl=rl_$j(tics,$zwidth(p)+sl) w $p(@g,s,c) i 'gtmvt w !,rl q ; GTMVT() ; true if a video i $zver'["VMS" n d d q +d ;if should be more precise . s d="" . i $i'=$p q . zsh "d":d . f s d=$o(d("D",d)) q:d="" i $p(d("D",d)," ")=$p s d=$p(d("D",d)," ",3) q . i d["TERMINAL" s d=$tr($ztrnlnm("TERM"),"ANSIVT","ansivt") i $l(d),d["ansi"!(d["vt") s d=1 i @"$zgetdvi($zparse($zio,""DEVICE""),""TRM"")",@"$zgetdvi($zio,""DECCRT"")" q $t ; ERR i $d(%ZD),%ZD'=$p c %ZD u $p w !,$p($zs,",",2,99),! u $p:(ctrap="":exc="") s $ec="" q LOOP i $d(%ZD),%ZD'=$p c %ZD d base q QUIET n %ZQ s %ZQ=1 d %GSE q fis-gtm-V7.0-005/sr_unix/gt_ar.csh0000755000032200000250000000202514342376330015636 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2015 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. # # # ################################################################# # ########################################################################## # # gt_ar.csh - Invoke library archiver. # This script is intended to be used with the xargs command, because # xargs requires any command it executes to be specified by full # pathname or to be on $PATH and will not handle an alias properly. # # Arguments # Options and arguments to the archiver. # # Aliases # gt_ar invoke native archiver # ########################################################################## source $gtm_tools/gtm_env.csh gt_ar $* fis-gtm-V7.0-005/sr_unix/gt_as.csh0000755000032200000250000000504414342376330015643 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2018 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. # # # ################################################################# # ################################################################################################# # # gt_as.csh - Assemble Unix assembly language program using GT.M assembly language options. # Note: this procedure only assembles one source file at a time. # # Argument # The pathname of a single assembly language program in native dialect. # # Output # Object code in file with suffix ".o". # # Environment # comlist_gt_as current assembler command set by comlist.csh for appropriate # version and image type # ################################################################################################# source $gtm_tools/gtm_env.csh if ( $?comlist_gt_as == "0" ) then echo "gt_as-E-nocomlist: gt_as.csh should only be invoked from comlist.csh" exit 1 endif alias gt_as_local "$comlist_gt_as" set os = `uname` set platform_name = ${os:gs/-//:s,/,,:al} set mach_type = `uname -m` set asmlist=($*) set cmdfile="$gtm_log/gt_as_$$__batch.csh" set background="&" if ($HOST:r:r:r =~ {strato}) set background="" echo 'alias gt_as_local "$comlist_gt_as"' >> $cmdfile foreach asm ($asmlist) set outfile="$gtm_log/gt_as_$$_${asm:t:r}.out" set redir=">& $outfile" if ( "ia64" == $mach_type && "linux" == $platform_name ) then set file=$asm:t:r:r set sfile="${gtm_obj}/${file}_cpp.s" set ofile="${gtm_obj}/${file}.o" echo "(eval 'gt_cpp -E $asm' > $sfile ; eval 'gt_as_local $sfile -o $ofile' ; /bin/rm $sfile)" \ "$redir $background" >> $cmdfile else if ( "os390" == $platform_name ) then set file=$asm:t:r set dbgfile="${gtm_obj}/${file}.dbg" echo "(eval 'gt_as_local $asm' ; if ( -e $dbgfile ) chmod ugo+r $dbgfile) $redir $background" >> $cmdfile else echo "eval 'gt_as_local $asm' $redir $background" >> $cmdfile endif end echo "wait" >> $cmdfile set cmdout="$gtm_log/gt_as_$$__batch.out" source $cmdfile >& $cmdout set stat=$status foreach asm ($asmlist) set outfile="$gtm_log/gt_as_$$_${asm:t:r}.out" /bin/cat $outfile /bin/rm $outfile end if ($stat) then /bin/cat $cmdout else /bin/rm $cmdfile /bin/rm $cmdout endif exit 0 fis-gtm-V7.0-005/sr_unix/gt_cc.csh0000755000032200000250000000404514342376330015625 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2021 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. # # # ################################################################# # ########################################################################################### # # gt_cc.csh - Compile C programs from $gtm_src # # Arguments # A list of C source file names (minus directory paths). # # Output # Object code in file with suffix ".o". # # Environment # comlist_gt_cc current C compiler alias set by comlist.csh for appropriate # version and image type # ########################################################################################### if ( $?comlist_gt_cc == "0" ) then echo "gt_cc-E-nocomlist: gt_cc.csh should only be invoked from within comlist.csh" exit 1 endif source $gtm_tools/gtm_env.csh alias gt_cc_local "comlist_gt_cc" set cfilelist=($*) set cmdfile="$gtm_log/gt_cc_$$_batch.csh" set background="&" if ($HOST:r:r:r =~ {snail,turtle}) set background="" echo 'alias gt_cc_local "$comlist_gt_cc"' >> $cmdfile foreach cfile ($cfilelist) set outfile="$gtm_log/gt_cc_local_$$_${cfile:t:r}.out" set redir=">& $outfile" set errmsg = "COMLIST-E-COMPILE : compiling $cfile failed" echo "(echo $cfile ; eval 'gt_cc_local $cfile' || echo '$errmsg') $redir $background" >> $cmdfile end echo "wait" >> $cmdfile set cmdout="$gtm_log/gt_cc_$$_batch.out" source $cmdfile >& $cmdout set compilestat = 0 foreach cfile ($cfilelist) set outfile="$gtm_log/gt_cc_local_$$_${cfile:t:r}.out" /bin/cat $outfile grep -qE 'COMLIST-E-.' $outfile if (0 == $status) then set compilestat = 1 else /bin/rm $outfile endif end if ($compilestat) then exit 1 else /bin/rm $cmdfile /bin/rm $cmdout endif fis-gtm-V7.0-005/sr_unix/gt_timer.h0000755000032200000250000002253014342376330016031 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #ifndef GT_TIMER_H #define GT_TIMER_H /* System dependent include file for gtm timer package */ #include typedef INTPTR_T TID; /* Timer ID type */ typedef void (*timer_hndlr)(); /* Timer handler type */ /* Gtm timer package uses ABS_TIME structure to carry * the time information in operating system independent * manner. The time in this structure is stored as * an absolute time - elapsed time since some major historic * event, or some other fixed date in the past. Different * operating systems have different time reference points. * * The time is converted from the OS time format to * the ABS_TIME format, and from then on, all * timer related code uses this format. */ typedef struct tag_abs_time { #ifndef __osf__ long at_sec; /* seconds */ long at_usec; /* and microseconds */ #else /* avoid 8 byte alignment issues */ intszofptr_t at_sec; /* seconds */ intszofptr_t at_usec; /* and microseconds */ #endif } ABS_TIME; #include /* Type that corresponds to the tv_usec field in a timeval struct. Valid across all platforms */ #if defined(__linux__) || defined(__ia64) || defined(__sparc) || defined(_AIX) || defined(__MVS__) \ || defined(__CYGWIN__) typedef suseconds_t gtm_tv_usec_t; #elif defined(__hppa) typedef long gtm_tv_usec_t; #elif defined(__osf__) typedef int gtm_tv_usec_t; #elif !defined(VMS) # error unsupported platform #endif /* All timer requests are placed into a linked list, or * a queue of pending requests in a time order. * The first timer in this queue is the currently * active timer, and expires first. */ #define GT_TIMER_INIT_DATA_LEN 8 typedef struct tag_ts { ABS_TIME expir_time; /* Absolute time when timer expires */ ABS_TIME start_time; /* Time when the timer is added */ void (*handler)(); /* Pointer to handler routine */ struct tag_ts *next; /* Pointer to next */ TID tid; /* Timer id */ int4 safe; /* Indicates if handler can be delivered while we are in * a deferred mode */ int4 block_int; /* Set to intrpt_state_t value if timer handling was blocked * by an uninterruptible state. For use in debugging, but not * conditional on DEBUG to keep the structure stable. */ int4 hd_len_max; /* Max length this blk can hold */ int4 hd_len; /* Handler data length */ char hd_data[GT_TIMER_INIT_DATA_LEN]; /* Handler data */ } GT_TIMER; /* Struct to track timefree block allocations */ typedef struct st_timer_alloc { void *addr; struct st_timer_alloc *next; } st_timer_alloc; #define MAX_SAFE_TIMER_HNDLRS 16 /* Max # of safe timer handlers */ #define GT_WAKE #define CANCEL_TIMERS cancel_unsafe_timers() /* Set a timeout timer, using a local variable as the timeout indicator. * Note: This macro establishes a condition handler which cancels the timer in case of an rts_error. * The TIMEOUT_DONE() macro must be used on all normal routine exit paths in order to REVERT the handler. */ #define TIMEOUT_INIT(TIMEOUT_VAR, TIMEOUT_MILLISECS) \ MBSTART { \ boolean_t *ptr_to_##TIMEOUT_VAR = &(TIMEOUT_VAR); \ boolean_t got_error; \ \ TIMEOUT_VAR = FALSE; \ start_timer((TID)ptr_to_##TIMEOUT_VAR, TIMEOUT_MILLISECS, simple_timeout_timer, \ SIZEOF(ptr_to_##TIMEOUT_VAR), &ptr_to_##TIMEOUT_VAR); \ ESTABLISH_NORET(timer_cancel_ch, got_error); \ if (got_error) \ { \ TIMEOUT_DONE(TIMEOUT_VAR); \ DRIVECH(error_condition); \ } \ } MBEND #define TIMEOUT_DONE(TIMEOUT_VAR) \ MBSTART { \ boolean_t *ptr_to_##TIMEOUT_VAR = &(TIMEOUT_VAR); \ \ assert(ctxt->ch == timer_cancel_ch); \ REVERT; \ cancel_timer((TID)ptr_to_##TIMEOUT_VAR); \ } MBEND /* Lighter versions with no condition handler, for when it is safe to have the timer hang around after it is no longer relevant. */ #define TIMEOUT_INIT_NOCH(TIMEOUT_VAR, TIMEOUT_MILLISECS) \ MBSTART { \ boolean_t *ptr_to_##TIMEOUT_VAR = &(TIMEOUT_VAR); \ \ TIMEOUT_VAR = FALSE; \ start_timer((TID)ptr_to_##TIMEOUT_VAR, TIMEOUT_MILLISECS, simple_timeout_timer, \ SIZEOF(ptr_to_##TIMEOUT_VAR), &ptr_to_##TIMEOUT_VAR); \ } MBEND #define TIMEOUT_DONE_NOCH(TIMEOUT_VAR) \ MBSTART { \ boolean_t *ptr_to_##TIMEOUT_VAR = &(TIMEOUT_VAR); \ \ cancel_timer((TID)ptr_to_##TIMEOUT_VAR); \ } MBEND /* Uncomment the below #define if you want to print the status of the key timer-related variables as well as the entire timer queue * when operations such as addition, cancellation, or handling of a timer occur. * * #define TIMER_DEBUGGING */ #define MAX_UNKNOWN_LEN 20 + 1 #ifdef TIMER_DEBUGGING # define DUMP_TIMER_INFO(LOCATION) \ { \ extern void (*fake_enospc_ptr)(); \ extern void (*simple_timeout_timer_ptr)(); \ int i; \ GT_TIMER *cur_timer; \ char *s_jnl_file_close_timer = "jnl_file_close_timer"; \ char *s_wcs_clean_dbsync = "wcs_clean_dbsync"; \ char *s_wcs_stale = "wcs_stale"; \ char *s_fake_enospc = "fake_enospc"; \ char *s_simple_timeout_timer = "simple_timeout_timer"; \ char s_unknown[MAX_UNKNOWN_LEN]; \ char *handler; \ \ cur_timer = (GT_TIMER *)timeroot; \ FPRINTF(stderr, "------------------------------------------------------\n" \ "%s\n---------------------------------\n" \ "Timer Info:\n" \ " system timer active: %d\n" \ " timer in handler: %d\n" \ " timer stack count: %d\n" \ " oldjnlclose started: %d\n", \ LOCATION, timer_active, timer_in_handler, \ timer_stack_count, oldjnlclose_started); \ FFLUSH(stderr); \ i = 0; \ while (cur_timer) \ { \ if ((void (*)())jnl_file_close_timer_ptr == cur_timer->handler) \ handler = s_jnl_file_close_timer; \ else if ((void (*)())wcs_clean_dbsync_fptr == cur_timer->handler) \ handler = s_wcs_clean_dbsync; \ else if ((void (*)())wcs_stale_fptr == cur_timer->handler) \ handler = s_wcs_stale; \ else if ((void (*)())fake_enospc_ptr == cur_timer->handler) \ handler = s_fake_enospc; \ else if ((void (*)())simple_timeout_timer_ptr == cur_timer->handler) \ handler = s_simple_timeout_timer; \ else \ { \ SNPRINTF(s_unknown, MAX_UNKNOWN_LEN, "%p", (void *)handler); \ handler = s_unknown; \ } \ FPRINTF(stderr, " - timer #%d:\n" \ " handler: %s\n" \ " safe: %d\n" \ " start_time: [at_sec: %ld; at_usec: %ld]\n" \ " expir_time: [at_sec: %ld; at_usec: %ld]\n", \ i, handler, cur_timer->safe, \ cur_timer->start_time.at_sec, cur_timer->start_time.at_usec, \ cur_timer->expir_time.at_sec, cur_timer->expir_time.at_usec); \ FFLUSH(stderr); \ cur_timer = cur_timer->next; \ i++; \ } \ FPRINTF(stderr, "------------------------------------------------------\n"); \ FFLUSH(stderr); \ } #else # define DUMP_TIMER_INFO(LOCATION) #endif int4 abs_time_comp(ABS_TIME *atp1, ABS_TIME *atp2); void add_int_to_abs_time(ABS_TIME *atps, int4 ival, ABS_TIME *atpd); void cancel_timer(TID tid); void cancel_unsafe_timers(void); void clear_timers(void); void hiber_start(uint4 milliseconds); void hiber_start_wall_time(uint4 milliseconds); void hiber_start_wait_any(uint4 milliseconds); void gtm_start_timer(TID tid, int4 time_to_expir, void(* handler)(), int4 data_length, void *handler_data); void start_timer(TID tid, int4 time_to_expir, void(* handler)(), int4 data_length, void *handler_data); ABS_TIME sub_abs_time(ABS_TIME *atp1, ABS_TIME *atp2); void sys_get_curr_time(ABS_TIME *tp); void sys_get_wall_time(ABS_TIME *atp); void prealloc_gt_timers(void); void set_blocksig(void); void check_for_timer_pops(boolean_t sig_handler_changed); GT_TIMER *find_timer_intr_safe(TID tid, GT_TIMER **tprev); void check_for_deferred_timers(void); void add_safe_timer_handler(int safetmr_cnt, ...); void sys_canc_timer(void); void simple_timeout_timer(TID tid, int4 hd_len, boolean_t **timedout); STATICFNDCL void gt_timers_alloc(void); STATICFNDCL void start_timer_int(TID tid, int4 time_to_expir, void (*handler)(), int4 hdata_len, void *hdata, boolean_t safe_timer); STATICFNDCL void sys_settimer (TID tid, ABS_TIME *time_to_expir); STATICFNDCL void start_first_timer(ABS_TIME *curr_time); STATICFNDCL void timer_handler(int why); STATICFNDCL GT_TIMER *find_timer(TID tid, GT_TIMER **tprev); STATICFNDCL GT_TIMER *add_timer(ABS_TIME *atp, TID tid, int4 time_to_expir, void (*handler)(), int4 hdata_len, void *hdata, boolean_t safe_timer); STATICFNDCL void remove_timer(TID tid); STATICFNDCL void init_timers(void); #endif fis-gtm-V7.0-005/sr_unix/gt_timers.c0000755000032200000250000012515314342376330016214 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* This file contains a general purpose timer package. Simultaneous multiple timers are supported. * All outstanding timers are contained in a queue of pending requests. New timer is added to the * queue in an expiration time order. The first timer in a queue expires first, and the last one * expires last. When the timer expires, the signal is generated and the process is awakened. This * timer is then removed from the queue, and the first timer in a queue is started again, and so on. * Starting a timer with the timer id equal to one of the existing timers in a chain will remove the * existing timer from the chain and add a new one instead. * * It is a responsibility of the user to go to hibernation mode by executing appropriate system call * if the user needs to wait for the timer expiration. * * Additionally, certain timers, designated by "safe" flag, can be processed---and, if necessary, out * of order---while we are deferred on interrupts. All regular timers that pop within the deferred * zone, will be handler in order as soon as we reenable interrupt processing. * * Following are top-level user-callable routines of this package: * * void sys_get_curr_time(ABS_TIME *atp) * fetch absolute time into stucture * * void hiber_start(uint4 hiber) * used to sleep for hiber milliseconds * * void start_timer(TID tid, int4 time_to_expir, void (*handler)(), int4 dlen, char *data) * Used to start a new timer. * * void cancel_timer(TID tid) * Cancel an existing timer. * Cancelling timer with tid = 0, cancels all timers. */ #include "mdef.h" #include "gtm_signal.h" #include "gtm_time.h" #include "gtm_string.h" #include "gtmimagename.h" #include #include #include #if (defined(__ia64) && defined(__linux__)) || defined(__MVS__) # include "gtm_unistd.h" #endif /* __ia64 && __linux__ or __MVS__ */ #include "gt_timer.h" #include "wake_alarm.h" #ifdef DEBUG # include "wbox_test_init.h" # include "io.h" #endif #if defined(mips) && !defined(_SYSTYPE_SVR4) # include #else # include #endif #ifndef __MVS__ # include #endif #include "send_msg.h" #include "gtmio.h" #include "have_crit.h" #include "util.h" #include "sleep.h" #include "error.h" #include "gtm_multi_thread.h" #include "gtmxc_types.h" /*#define DEBUG_SIGSAFE*/ #ifdef DEBUG_SIGSAFE # define DBGSIGSAFEFPF(x) DBGFPF(x) /*#include "gtmio.h"*/ #include "io.h" #else # define DBGSIGSAFEFPF(x) #endif #ifdef ITIMER_REAL # define USER_HZ 1000 #endif #define TIMER_BLOCK_SIZE 64 /* # of timer entries allocated initially as well as at every expansion */ #define GT_TIMER_EXPAND_TRIGGER 8 /* if the # of timer entries in the free queue goes below this, allocate more */ #define MAX_TIMER_POP_TRACE_SZ 32 #define ADD_SAFE_HNDLR(HNDLR) \ { \ assert((ARRAYSIZE(safe_handlers) - 1) > safe_handlers_cnt); \ assert(NULL != (void *)HNDLR); /* void * to avoid warnings of always true */ \ safe_handlers[safe_handlers_cnt++] = HNDLR; \ } #define REPORT_SETITIMER_ERROR(TIMER_TYPE, SYS_TIMER, FATAL, ERRNO) \ { \ char s[512]; \ \ SNPRINTF(s, 512, "Timer: %s; timer_active: %d; " \ "sys_timer.it_value: [tv_sec: %ld; tv_usec: %ld]; " \ "sys_timer.it_interval: [tv_sec: %ld; tv_usec: %ld]", \ TIMER_TYPE, timer_active, \ SYS_TIMER.it_value.tv_sec, SYS_TIMER.it_value.tv_usec, \ SYS_TIMER.it_interval.tv_sec, SYS_TIMER.it_interval.tv_usec); \ if (FATAL) \ { \ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) \ ERR_SETITIMERFAILED, 1, ERRNO, ERR_TEXT, 2, LEN_AND_STR(s)); \ in_setitimer_error = TRUE; \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SETITIMERFAILED, 1, ERRNO); \ } else \ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) MAKE_MSG_WARNING(ERR_SETITIMERFAILED), \ 1, ERRNO, ERR_TEXT, 2, LEN_AND_STR(s)); \ } #define SYS_SETTIMER(TIMER, DELTA) \ MBSTART { \ sys_timer_at = (TIMER)->expir_time; \ sys_settimer((TIMER)->tid, DELTA); \ } MBEND STATICDEF struct itimerval sys_timer, old_sys_timer; STATICDEF ABS_TIME sys_timer_at; /* Absolute time associated with sys_timer */ STATICDEF boolean_t in_setitimer_error; #define DUMMY_SIG_NUM 0 /* following can be used to see why timer_handler was called */ #define SAFE_FOR_ANY_TIMER ((INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) && (FALSE == process_exiting) && !fast_lock_count) /* In case threads are running, we dont want any unsafe timers to be handled during a timer handler pop. This is because we * dont know if the threads will modify the same global variable that the unsafe timer modifies concurrently. * But it is okay for timers to be started by individual threads. For example the iott_flush_timer will be started inside * thread code only while holding a mutex lock (e.g. inside gtm_putmsg_list or so) and even though a "setitimer" call is done * inside one thread, the SIGALRM pop will happen only in the parent process because all threads have SIGALRM disabled in their * signal mask. Define SAFE_FOR_TIMER_POP and SAFE_FOR_TIMER_START variables accordingly. */ #define SAFE_FOR_TIMER_POP (SAFE_FOR_ANY_TIMER && !multi_thread_in_use) #define SAFE_FOR_TIMER_START (SAFE_FOR_ANY_TIMER) STATICDEF volatile GT_TIMER *timeroot = NULL; /* chain of pending timer requests in time order */ STATICDEF boolean_t first_timeset = TRUE; STATICDEF struct sigaction prev_alrm_handler; /* save previous SIGALRM handler, if any */ /* Chain of unused timer request blocks */ STATICDEF volatile GT_TIMER *timefree = NULL; STATICDEF volatile int4 num_timers_free; /* # of timers in the unused queue */ STATICDEF volatile st_timer_alloc *timer_allocs = NULL; STATICDEF int safe_timer_cnt, timer_pop_cnt; /* Number of safe timers in queue/popped */ STATICDEF TID *deferred_tids; STATICDEF timer_hndlr safe_handlers[MAX_SAFE_TIMER_HNDLRS + 1]; /* +1 for NULL to terminate list */ STATICDEF int safe_handlers_cnt; STATICDEF boolean_t stolen_timer = FALSE; /* only complain once, used in check_for_timer_pops() */ STATICDEF boolean_t stopped_timer = FALSE; /* only complain once, used in check_for_timer_pops() */ STATICDEF char *whenstolen[] = {"check_for_timer_pops", "check_for_timer_pops first time"}; /* for check_for_timer_pops */ #ifdef DEBUG STATICDEF int trc_timerpop_idx; STATICDEF GT_TIMER trc_timerpop_array[MAX_TIMER_POP_TRACE_SZ]; # define TRACE_TIMER_POP(TIMER_INFO) \ { \ memcpy(&trc_timerpop_array[trc_timerpop_idx], TIMER_INFO, SIZEOF(GT_TIMER)); \ trc_timerpop_idx = (trc_timerpop_idx + 1) % MAX_TIMER_POP_TRACE_SZ; \ } #endif /* Get current clock time from the target clock when using clock_gettime() * Arguments: atp - pointer to absolute structure of time * clockid - one of CLOCK_REALTIME or CLOCK_MONOTONIC */ #define GET_TIME_CORE(ATP, CLOCKID) \ MBSTART { \ struct timespec ts; \ \ /* Note: This code is called from timer_handler and so needs to be async-signal safe. \ * POSIX defines "clock_gettime" as safe but not "gettimeofday" so dont use the latter. \ */ \ clock_gettime(CLOCKID, &ts); \ atp->at_sec = (int4)ts.tv_sec; \ atp->at_usec = (int4)ts.tv_nsec / 1000; \ } MBEND /* Sleep for MS milliseconds of "clockid" time unless interrupted by RESTART processing */ #ifdef _AIX /* Because of unreliability, AIX uses plain old nanosleep() */ #define HIBER_START_CORE(MS, CLOCKID, RESTART) SLEEP_USEC((MS * 1000), RESTART) #else #define HIBER_START_CORE(MS, CLOCKID, RESTART) \ MBSTART { \ time_t seconds, nanoseconds; \ \ seconds = MS / 1000; \ nanoseconds = (MS % 1000) * E_6; \ CLOCK_NANOSLEEP(CLOCKID, seconds, nanoseconds, (RESTART)); \ } MBEND #endif /* Flag signifying timer is active. Especially useful when the timer handlers get nested. This has not been moved to a * threaded framework because we do not know how timers will be used with threads. */ GBLDEF volatile boolean_t timer_active = FALSE; GBLDEF volatile int4 timer_stack_count = 0; GBLDEF volatile boolean_t timer_in_handler = FALSE; GBLDEF void (*wcs_clean_dbsync_fptr)(); /* Reference to wcs_clean_dbsync() to be used in gt_timers.c. */ GBLDEF void (*wcs_stale_fptr)(); /* Reference to wcs_stale() to be used in gt_timers.c. */ GBLDEF boolean_t deferred_timers_check_needed; /* Indicator whether check_for_deferred_timers() should be called * upon leaving deferred zone. */ GBLREF boolean_t blocksig_initialized; /* Set to TRUE when blockalrm, block_ttinout, and block_sigsent are * initialized. */ GBLREF boolean_t mu_reorg_process, oldjnlclose_started; GBLREF int process_exiting; GBLREF int4 error_condition; GBLREF sigset_t blockalrm; GBLREF sigset_t block_sigsent; GBLREF sigset_t block_ttinout; GBLREF sigset_t block_worker; GBLREF void (*jnl_file_close_timer_ptr)(void); /* Initialized only in gtm_startup(). */ GBLREF volatile int4 fast_lock_count, outofband; #ifdef DEBUG GBLREF boolean_t in_nondeferrable_signal_handler; GBLREF boolean_t gtm_jvm_process; #endif error_def(ERR_SETITIMERFAILED); error_def(ERR_TEXT); error_def(ERR_TIMERHANDLER); /* Preallocate some memory for timers. */ void gt_timers_alloc(void) { int4 gt_timer_cnt; GT_TIMER *timeblk, *timeblks; st_timer_alloc *new_alloc; /* Allocate timer blocks putting each timer on the free queue */ assert(1 > timer_stack_count); # ifdef DEBUG /* Allocate space for deferred timer tracking. We don't expect to need more than the initial TIMER_BLOCK_SIZE entries */ if (WBTEST_ENABLED(WBTEST_DEFERRED_TIMERS)) { deferred_tids = (TID *)malloc(sizeof(TID) * TIMER_BLOCK_SIZE); memset((char *)deferred_tids, (char)0xff, sizeof(TID) * TIMER_BLOCK_SIZE); } # endif timeblk = timeblks = (GT_TIMER *)malloc((SIZEOF(GT_TIMER)) * TIMER_BLOCK_SIZE); new_alloc = (st_timer_alloc *)malloc(SIZEOF(st_timer_alloc)); new_alloc->addr = timeblk; new_alloc->next = (st_timer_alloc *)timer_allocs; timer_allocs = new_alloc; for (gt_timer_cnt = TIMER_BLOCK_SIZE; 0 < gt_timer_cnt; --gt_timer_cnt) { timeblk->hd_len_max = GT_TIMER_INIT_DATA_LEN; /* Set amount it can store */ timeblk->next = (GT_TIMER *)timefree; /* Put on free queue */ timefree = timeblk; timeblk = (GT_TIMER *)((char *)timeblk + SIZEOF(GT_TIMER)); /* Next! */ } assert(((char *)timeblk - (char *)timeblks) == (SIZEOF(GT_TIMER)) * TIMER_BLOCK_SIZE); num_timers_free += TIMER_BLOCK_SIZE; } void add_safe_timer_handler(int safetmr_cnt, ...) { int i; va_list var; timer_hndlr tmrhndlr; VAR_START(var, safetmr_cnt); for (i = 1; i <= safetmr_cnt; i++) { tmrhndlr = va_arg(var, timer_hndlr); ADD_SAFE_HNDLR(tmrhndlr); } va_end(var); } /* Do the initialization of blockalrm, block_ttinout and block_sigsent, and set blocksig_initialized to TRUE, so * that we can later block signals when there is a need. This function should be called very early * in the main() routines of modules that wish to do their own interrupt handling. */ void set_blocksig(void) { sigemptyset(&blockalrm); sigaddset(&blockalrm, SIGALRM); sigemptyset(&block_ttinout); sigaddset(&block_ttinout, SIGTTIN); sigaddset(&block_ttinout, SIGTTOU); sigemptyset(&block_sigsent); sigaddset(&block_sigsent, SIGINT); sigaddset(&block_sigsent, SIGQUIT); sigaddset(&block_sigsent, SIGTERM); sigaddset(&block_sigsent, SIGTSTP); sigaddset(&block_sigsent, SIGCONT); sigaddset(&block_sigsent, SIGALRM); sigfillset(&block_worker); sigdelset(&block_worker, SIGSEGV); sigdelset(&block_worker, SIGKILL); sigdelset(&block_worker, SIGFPE); sigdelset(&block_worker, SIGBUS); sigaddset(&block_worker, SIGTERM); blocksig_initialized = TRUE; /* note the fact that blockalrm and block_sigsent are initialized */ } /* Initialize group of timer blocks */ void prealloc_gt_timers(void) { /* Preallocate some timer blocks. This will be all the timer blocks we hope to need. * Allocate them with 8 bytes of possible data each. * If more timer blocks are needed, we will allocate them as needed. */ gt_timers_alloc(); /* Now initialize the safe timers. Must be done dynamically to avoid the situation where this module always references all * possible safe timers thus pulling extra stuff into executables that don't need or want it. * * First step, fill in the safe timers contained within this module which are always available. * NOTE: Use gt_timers_add_safe_hndlrs to add safe timer handlers not visible to gtmsecshr */ ADD_SAFE_HNDLR(&wake_alarm); /* Standalone module containing only one global reference */ } /* Get current monotonic clock time. Fill-in the structure with the monotonic time of system clock */ void sys_get_curr_time(ABS_TIME *atp) { GET_TIME_CORE(atp, CLOCK_MONOTONIC); } /* Get current "wall" clock time. Fill-in the structure with the absolute time of system clock. * WARNING: only op_hang uses this to ensure the HANG duration matches $[Z]Horlog/$ZUT time */ void sys_get_wall_time(ABS_TIME *atp) { GET_TIME_CORE(atp, CLOCK_REALTIME); } /* Sleep for milliseconds of monotonic time unless interrupted by out-of-band processing */ void hiber_start(uint4 milliseconds) { /* WARNING: negation of outofband is intentional */ HIBER_START_CORE(milliseconds, CLOCK_MONOTONIC, !outofband); } /* Sleep for milliseconds of "wall clock" time unless interrupted by out-of-band processing * WARNING: only op_hang uses this to ensure the HANG duration matches $[Z]Horlog/$ZUT time */ void hiber_start_wall_time(uint4 milliseconds) { /* WARNING: negation of outofband is intentional */ HIBER_START_CORE(milliseconds, CLOCK_REALTIME, !outofband); } /* Sleep for milliseconds of time unless EINTRrupted */ void hiber_start_wait_any(uint4 milliseconds) { HIBER_START_CORE(milliseconds, CLOCK_MONOTONIC, FALSE); } /* Wrapper function for start_timer() that is exposed for outside use. The function ensures that time_to_expir is positive. If * negative value or 0 is passed, set time_to_expir to 0 and invoke start_timer(). The reason we have not merged this functionality * with start_timer() is because there is no easy way to determine whether the function is invoked from inside GT.M or by an * external routine. * Arguments: tid - timer id * time_to_expir - time to expiration in msecs * handler - pointer to handler routine * hdata_len - length of handler data next arg * hdata - data to pass to handler (if any) */ void gtm_start_timer(TID tid, int4 time_to_expir, void (*handler)(), int4 hdata_len, void *hdata) { if (0 >= time_to_expir) time_to_expir = 0; start_timer(tid, time_to_expir, handler, hdata_len, hdata); } /* Start the timer. If timer chain is empty or this is the first timer to expire, actually start the system timer. * Arguments: tid - timer id * time_to_expir - time to expiration in msecs * handler - pointer to handler routine * hdata_len - length of handler data next arg * hdata - data to pass to handler (if any) */ void start_timer(TID tid, int4 time_to_expir, void (*handler)(), int4 hdata_len, void *hdata) { sigset_t savemask; boolean_t safe_timer = FALSE, safe_to_add = FALSE; int i, rc; #ifdef DEBUG struct itimerval curtimer; #endif assertpro(0 <= time_to_expir); /* Callers should verify non-zero time */ DUMP_TIMER_INFO("At the start of start_timer()"); if (NULL == handler) { safe_to_add = TRUE; safe_timer = TRUE; } else if (wcs_clean_dbsync_fptr == handler) { /* Account for known instances of the above function being called from within a deferred zone. */ assert((INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) || (INTRPT_IN_WCS_WTSTART == intrpt_ok_state) || (INTRPT_IN_GDS_RUNDOWN == intrpt_ok_state) || (INTRPT_IN_DB_CSH_GETN == intrpt_ok_state) || (mu_reorg_process && (INTRPT_IN_KILL_CLEANUP == intrpt_ok_state))); safe_to_add = TRUE; } else if (wcs_stale_fptr == handler) { /* Account for known instances of the above function being called from within a deferred zone. */ assert((INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) || (INTRPT_IN_DB_CSH_GETN == intrpt_ok_state) || (INTRPT_IN_TRIGGER_NOMANS_LAND == intrpt_ok_state) || (mu_reorg_process && (INTRPT_IN_KILL_CLEANUP == intrpt_ok_state))); safe_to_add = TRUE; } else { for (i = 0; NULL != safe_handlers[i]; i++) { if (safe_handlers[i] == handler) { safe_to_add = TRUE; safe_timer = TRUE; break; } } } if (!safe_to_add && !SAFE_FOR_TIMER_START) { assert(FALSE); return; } if (1 > timer_stack_count) { SIGPROCMASK(SIG_BLOCK, &blockalrm, &savemask, rc); /* block SIGALRM signal */ #ifdef DEBUG if (TRUE == timer_active) /* There had better be an active timer */ assert(0 == getitimer(ITIMER_REAL, &curtimer)); #endif } start_timer_int(tid, time_to_expir, handler, hdata_len, hdata, safe_timer); if (1 > timer_stack_count) SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc); /* reset signal handlers */ DUMP_TIMER_INFO("At the end of start_timer()"); } /* Internal version of start_timer that does not protect itself, assuming this has already been done. * Otherwise does as explained above in start_timer. */ STATICFNDEF void start_timer_int(TID tid, int4 time_to_expir, void (*handler)(), int4 hdata_len, void *hdata, boolean_t safe_timer) { ABS_TIME at; GT_TIMER *newt; assert(0 <= time_to_expir); /* there is abuse of this api - hdata is a pointer, so hd_len, if supplied, should be the size of a pointer, but callers * sometimes use the size of the pointed-to data */ sys_get_curr_time(&at); if (first_timeset) { init_timers(); first_timeset = FALSE; } /* We expect no timer with id= to exist in the timer queue currently. This is asserted in "add_timer" call below. * In pro though, we'll be safe and remove any tids that exist before adding a new entry with the same tid - 2009/10. * If a few years pass without the assert failing, it might be safe then to remove the PRO_ONLY code below. */ # ifndef DEBUG remove_timer(tid); /* Remove timer from chain */ # endif /* Check if # of free timer slots is less than minimum threshold. If so, allocate more of those while it is safe to do so */ if ((GT_TIMER_EXPAND_TRIGGER > num_timers_free) && (1 > timer_stack_count)) gt_timers_alloc(); DUMP_TIMER_INFO("Before invoking add_timer()"); newt = add_timer(&at, tid, time_to_expir, handler, hdata_len, hdata, safe_timer); /* Put new timer in the queue. */ DUMP_TIMER_INFO("After invoking add_timer()"); if ((timeroot->tid == tid) || !timer_active || (timer_active && ((newt->expir_time.at_sec < sys_timer_at.at_sec) || ((newt->expir_time.at_sec == sys_timer_at.at_sec) && ((gtm_tv_usec_t)newt->expir_time.at_usec < sys_timer_at.at_usec))))) start_first_timer(&at); } /* Cancel timer. * Arguments: tid - timer id */ void cancel_timer(TID tid) { ABS_TIME at; sigset_t savemask; boolean_t first_timer; int rc; if (1 > timer_stack_count) SIGPROCMASK(SIG_BLOCK, &blockalrm, &savemask, rc); /* block SIGALRM signal */ DUMP_TIMER_INFO("At the start of cancel_timer()"); sys_get_curr_time(&at); first_timer = (timeroot && (timeroot->tid == tid)); remove_timer(tid); /* remove it from the chain */ if (first_timer) { if (timeroot) start_first_timer(&at); /* start the first timer in the chain */ else if (timer_active) sys_canc_timer(); } if (1 > timer_stack_count) SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc); DUMP_TIMER_INFO("At the end of cancel_timer()"); } /* Clear the timers' state for the forked-off process. */ void clear_timers(void) { sigset_t savemask; int rc; DUMP_TIMER_INFO("At the start of clear_timers()"); if (NULL == timeroot) { /* If no timers have been initialized in this process, take fast path (avoid system call) */ /* If the only timer popped, and we got a SIGTERM while its handler was active, the timeroot * would be NULL and timer_in_handler would be TRUE, but that should be safe for the fast path, * so allow this case if the process is exiting. */ assert((FALSE == timer_in_handler) || process_exiting); assert(FALSE == timer_active); assert(FALSE == oldjnlclose_started); assert(FALSE == deferred_timers_check_needed); return; } if (1 > timer_stack_count) SIGPROCMASK(SIG_BLOCK, &blockalrm, &savemask, rc); /* block SIGALRM signal */ while (timeroot) remove_timer(timeroot->tid); timer_in_handler = FALSE; timer_active = FALSE; oldjnlclose_started = FALSE; deferred_timers_check_needed = FALSE; if (1 > timer_stack_count) SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc); DUMP_TIMER_INFO("After invoking clear_timers()"); return; } /* System call to set timer. Time is given im msecs. * Arguments: tid - timer id * time_to_expir - time to expiration */ STATICFNDEF void sys_settimer(TID tid, ABS_TIME *time_to_expir) { if (in_setitimer_error) return; sys_timer.it_value.tv_sec = time_to_expir->at_sec; sys_timer.it_value.tv_usec = (gtm_tv_usec_t)time_to_expir->at_usec; sys_timer.it_interval.tv_sec = sys_timer.it_interval.tv_usec = 0; assert(1000000 > sys_timer.it_value.tv_usec); if ((-1 == setitimer(ITIMER_REAL, &sys_timer, &old_sys_timer)) || WBTEST_ENABLED(WBTEST_SETITIMER_ERROR)) { REPORT_SETITIMER_ERROR("ITIMER_REAL", sys_timer, TRUE, errno); } # ifdef TIMER_DEBUGGING FPRINTF(stderr, "------------------------------------------------------\n" "SETITIMER\n---------------------------------\n"); FPRINTF(stderr, "System timer :\n expir_time: [at_sec: %ld; at_usec: %ld]\n", sys_timer.it_value.tv_sec, sys_timer.it_value.tv_usec); FPRINTF(stderr, "Old System timer :\n expir_time: [at_sec: %ld; at_usec: %ld]\n", old_sys_timer.it_value.tv_sec, old_sys_timer.it_value.tv_usec); FFLUSH(stderr); # endif timer_active = TRUE; } /* Start the first timer in the timer chain * Arguments: curr_time - current time assumed within the function */ STATICFNDEF void start_first_timer(ABS_TIME *curr_time) { ABS_TIME eltime; GT_TIMER *tpop; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DUMP_TIMER_INFO("At the start of start_first_timer()"); deferred_timers_check_needed = FALSE; if ((1 < timer_stack_count) || (TRUE == timer_in_handler)) return; for (tpop = (GT_TIMER *)timeroot ; tpop ; tpop = tpop->next) { eltime = sub_abs_time((ABS_TIME *)&tpop->expir_time, curr_time); if ((0 > eltime.at_sec) || ((0 == eltime.at_sec) && (0 == eltime.at_usec))) { /* Timer has expired. Handle safe timers, defer unsafe timers. */ if (tpop->safe || (SAFE_FOR_TIMER_START && (1 > timer_stack_count) && !(TREF(in_ext_call) && (wcs_stale_fptr == tpop->handler)))) { timer_handler(DUMMY_SIG_NUM); /* At this point all timers should have been handled, including a recursive call to * start_first_timer(), if needed, and deferred_timers_check_needed set to the appropriate * value, so we are done. */ break; } else { deferred_timers_check_needed = TRUE; tpop->block_int = intrpt_ok_state; } } else { /* Set system timer to wake on unexpired timer. */ SYS_SETTIMER(tpop, &eltime); break; /* System timer will handle subsequent timers, so we are done. */ } assert(deferred_timers_check_needed); } assert(timeroot || !deferred_timers_check_needed); DUMP_TIMER_INFO("At the end of start_first_timer()"); } /* Timer handler. This is the main handler routine that is being called by the kernel upon receipt * of timer signal. It dispatches to the user handler routine, and removes first timer in a timer * queue. If the queue is not empty, it starts the first timer in the queue. The why parameter is a * no-op in our case, but is required to maintain compatibility with the system type of __sighandler_t, * which is (void*)(int). */ STATICFNDEF void timer_handler(int why) { int4 cmp, save_error_condition; GT_TIMER *tpop, *tpop_prev = NULL; ABS_TIME at; int save_errno, timer_defer_cnt; TID *deferred_tid; boolean_t tid_found; char *save_util_outptr; va_list save_last_va_list_ptr; boolean_t util_copy_saved = FALSE, safe_for_timer_pop; # ifdef DEBUG boolean_t save_in_nondeferrable_signal_handler; ABS_TIME rel_time, old_at, late_time; static int last_continue_proc_cnt = -1; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(gtm_is_main_thread() || gtm_jvm_process); DUMP_TIMER_INFO("At the start of timer_handler()"); if (SIGALRM == why) { /* If why is 0, we know that timer_handler() was called directly, so no need * to check if the signal needs to be forwarded to appropriate thread. */ FORWARD_SIG_TO_MAIN_THREAD_IF_NEEDED(SIGALRM); } # ifdef DEBUG /* Note that it is possible "in_nondeferrable_signal_handler" is non-zero if we first went into generic_signal_handler * (say to handle sig-3) and then had a timer handler pop while inside there (possible for example in receiver server). * So save current value of global and restore it at end of this function. */ save_in_nondeferrable_signal_handler = in_nondeferrable_signal_handler; # endif /* timer_handler() may or may not be protected from signals. * If why is SIGALRM, the OS typically blocks SIGALRM while this handler is executing. * If why is DUMMY_SIG_NUM, SIGALRM is not blocked, so make sure that a concurrent SIGALRM bails out at this point. * All other routines which manipulate the timer data structure block SIGALRM (using SIGPROCMASK), so timer_handler() * can't conflict with them. As long as those routines can't be invoked asynchronously while timer_handler (or another * of those routines) is running, there can be no conflict, and the timer structures are safe from concurrent manipulation. */ if (1 < INTERLOCK_ADD(&timer_stack_count, UNUSED, 1)) { deferred_timers_check_needed = TRUE; INTERLOCK_ADD(&timer_stack_count, UNUSED, -1); return; } deferred_timers_check_needed = FALSE; save_errno = errno; save_error_condition = error_condition; /* aka SIGNAL */ timer_active = FALSE; /* timer has popped; system timer not active anymore */ sys_get_curr_time(&at); tpop = (GT_TIMER *)timeroot; timer_defer_cnt = 0; /* reset the deferred timer count, since we are in timer_handler */ safe_for_timer_pop = SAFE_FOR_TIMER_POP; /* If "multi_thread_in_use" is TRUE, it is possible util_out* buffers are concurrently being manipulated by the running * threads. So do not use SAVE/RESTORE_UTIL_OUT_BUFFER macros. Thankfully in this case, "safe_for_timer_pop" will * be FALSE (asserted below) and so only safe timer handlers will be driven. We expect the safe timer handlers to * not play with the util_out* buffers. So it is actually okay to not do the SAVE/RESTORE_UTIL_OUT_BUFFER. */ assert(!multi_thread_in_use || !safe_for_timer_pop); if (safe_for_timer_pop) SAVE_UTIL_OUT_BUFFER(save_util_outptr, save_last_va_list_ptr, util_copy_saved); # ifdef DEBUG if (safe_for_timer_pop) in_nondeferrable_signal_handler = IN_TIMER_HANDLER; /* Allow a base 50 seconds of lateness for safe timers */ late_time.at_sec = 50; late_time.at_usec = 0; # endif while (tpop) /* fire all handlers that expired */ { cmp = abs_time_comp(&at, (ABS_TIME *)&tpop->expir_time); if (cmp < 0) break; # if defined(DEBUG) && !defined(_AIX) if (tpop->safe && (TREF(continue_proc_cnt) == last_continue_proc_cnt) && !(gtm_white_box_test_case_enabled && ((WBTEST_SIGTSTP_IN_JNL_OUTPUT_SP == gtm_white_box_test_case_number) || (WBTEST_EXPECT_IO_HANG == gtm_white_box_test_case_number) || (WBTEST_OINTEG_WAIT_ON_START == gtm_white_box_test_case_number)))) { /* Check if the timer is extremely overdue, with the following exceptions: * - Unsafe timers can be delayed indefinitely. * - AIX systems tend to arbitrarily delay processes when loaded. * - WBTEST_SIGTSTP_IN_JNL_OUTPUT_SP stops the process from running. * - Some other mechanism causes a SIGSTOP/SIGCONT, bumping continue_proc_cnt. */ rel_time = sub_abs_time(&at, (ABS_TIME *)&tpop->expir_time); if (abs_time_comp(&late_time, &rel_time) <= 0) gtm_fork_n_core(); /* Dump core, but keep going. */ } last_continue_proc_cnt = TREF(continue_proc_cnt); # endif /* A timer might pop while we are in the non-zero intrpt_ok_state zone, which could cause collisions. Instead, * we will defer timer events and drive them once the deferral is removed, unless the timer is safe. * Handle wcs_stale timers during external calls similarly. */ if ((safe_for_timer_pop && !(TREF(in_ext_call) && (wcs_stale_fptr == tpop->handler))) || tpop->safe) { if (NULL != tpop_prev) tpop_prev->next = tpop->next; else timeroot = tpop->next; if (tpop->safe) { safe_timer_cnt--; assert(0 <= safe_timer_cnt); } if (NULL != tpop->handler) /* if there is a handler, call it */ { # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_DEFERRED_TIMERS) && ((void *)tpop->handler != (void*)jnl_file_close_timer_ptr)) { DBGFPF((stderr, "TIMER_HANDLER: handled a timer\n")); timer_pop_cnt++; } # endif timer_in_handler = TRUE; (*tpop->handler)(tpop->tid, tpop->hd_len, tpop->hd_data); timer_in_handler = FALSE; if (!tpop->safe) /* if safe, avoid a system call */ { DEBUG_ONLY(old_at = at); sys_get_curr_time(&at); /* refresh current time if called a handler */ # ifdef DEBUG /* Include the time it took to handle the unsafe timer in the allowed late time. * Otherwise, a hung unsafe timer could cause a subsequent safe timer to be overdue. */ rel_time = sub_abs_time(&at, &old_at); late_time.at_sec += rel_time.at_sec; late_time.at_usec += rel_time.at_usec; if (late_time.at_usec > MICROSECS_IN_SEC) { late_time.at_sec++; late_time.at_usec -= MICROSECS_IN_SEC; } # endif } DEBUG_ONLY(TRACE_TIMER_POP(tpop)); } tpop->next = (GT_TIMER *)timefree; /* put timer block on the free chain */ timefree = tpop; if (NULL != tpop_prev) tpop = tpop_prev->next; else tpop = (GT_TIMER *)timeroot; num_timers_free++; assert(0 < num_timers_free); } else { timer_defer_cnt++; # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_DEFERRED_TIMERS)) { tid_found = FALSE; deferred_tid = deferred_tids; while (-1 != *deferred_tid) { if (*deferred_tid == tpop->tid) { tid_found = TRUE; break; } deferred_tid++; /* WBTEST_DEFERRED_TIMERS tests do not need more than TIMER_BLOCK_SIZE entries, right? */ assert(TIMER_BLOCK_SIZE >= (deferred_tid - deferred_tids)); } if (!tid_found) { *deferred_tid = tpop->tid; DBGFPF((stderr, "TIMER_HANDLER: deferred a timer\n")); } } # endif tpop->block_int = intrpt_ok_state; tpop_prev = tpop; tpop = tpop->next; if ((0 == safe_timer_cnt) && !(TREF(in_ext_call) && (wcs_stale_fptr == tpop_prev->handler))) break; /* no more safe timers left, and not special case, so quit */ } } if (safe_for_timer_pop) RESTORE_UTIL_OUT_BUFFER(save_util_outptr, save_last_va_list_ptr, util_copy_saved); if (safe_for_timer_pop || (0 < safe_timer_cnt)) start_first_timer(&at); else if ((NULL != timeroot) || (0 < timer_defer_cnt)) deferred_timers_check_needed = TRUE; /* Restore mainline error_condition global variable. This way any gtm_putmsg or rts_errors that occurred inside interrupt * code do not affect the error_condition global variable that mainline code was relying on. For example, not doing this * restore caused the update process (in updproc_ch) to issue a GTMASSERT (GTM-7526). BYPASSOK. */ SET_ERROR_CONDITION(save_error_condition); /* restore error_condition & severity */ errno = save_errno; /* restore mainline errno by similar reasoning as mainline error_condition */ INTERLOCK_ADD(&timer_stack_count, UNUSED, -1); # ifdef DEBUG if (safe_for_timer_pop) in_nondeferrable_signal_handler = save_in_nondeferrable_signal_handler; # endif DUMP_TIMER_INFO("At the end of timer_handler()"); } /* Find a timer given by tid in the timer chain. * Arguments: tid - timer id * tprev - address of pointer to previous node * Return: pointer to timer in the chain, or 0 if timer is not found * Note: tprev is set to the link previous to the tid link */ STATICFNDEF GT_TIMER *find_timer(TID tid, GT_TIMER **tprev) { GT_TIMER *tc; tc = (GT_TIMER *)timeroot; *tprev = NULL; while (tc) { if (tc->tid == tid) return tc; *tprev = tc; tc = tc->next; } return 0; } /* Add timer to timer chain. Allocate a new link for a timer. Convert time to expiration into absolute time. * Insert new link into chain in timer order. * Arguments: tid - timer id * time_to_expir - elapsed time to expiration * handler - pointer to handler routine * hdata_len - length of data to follow * hdata - data to pass to timer rtn if any * safe_timer - timer's handler is in safe_handlers array */ STATICFNDEF GT_TIMER *add_timer(ABS_TIME *atp, TID tid, int4 time_to_expir, void (*handler)(), int4 hdata_len, void *hdata, boolean_t safe_timer) { GT_TIMER *tp, *tpp, *ntp, *lastntp; int4 cmp, i; st_timer_alloc *new_alloc; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* assert that no timer entry with the same "tid" exists in the timer chain */ assert(NULL == find_timer(tid, &tpp)); assert(GT_TIMER_INIT_DATA_LEN >= hdata_len); /* obtain a new timer block */ ntp = (GT_TIMER *)timefree; lastntp = NULL; for ( ; NULL != ntp; ) { /* we expect all callers of timer functions to not require more than 8 bytes of data; any violations * of this assumption need to be caught---hence the assert below */ assert(GT_TIMER_INIT_DATA_LEN == ntp->hd_len_max); assert(ntp->hd_len_max >= hdata_len); if (ntp->hd_len_max >= hdata_len) /* found one that can hold our data */ { /* dequeue block */ if (NULL == lastntp) /* first one on queue */ timefree = ntp->next; /* dequeue 1st element */ else /* is not 1st on queue -- use simple dequeue */ lastntp->next = ntp->next; assert(0 < num_timers_free); num_timers_free--; break; } lastntp = ntp; /* still looking, try next block */ ntp = ntp->next; } /* if didn't find one, fail if dbg; else malloc a new one */ if (NULL == ntp) { assert(FALSE); /* if dbg, we should have enough already */ ntp = (GT_TIMER *)malloc(SIZEOF(GT_TIMER)); /* if we are in a timer, malloc may error out */ new_alloc = (st_timer_alloc *)malloc(SIZEOF(st_timer_alloc)); /* insert in front of the list */ new_alloc->addr = ntp; new_alloc->next = (st_timer_alloc *)timer_allocs; timer_allocs = new_alloc; assert(GT_TIMER_INIT_DATA_LEN == hdata_len); ntp->hd_len_max = hdata_len; } ntp->tid = tid; ntp->handler = handler; if (safe_timer) { ntp->safe = TRUE; safe_timer_cnt++; assert(0 < safe_timer_cnt); } else ntp->safe = FALSE; ntp->block_int = INTRPT_OK_TO_INTERRUPT; ntp->hd_len = hdata_len; if (0 < hdata_len) { assert(GT_TIMER_INIT_DATA_LEN >= hdata_len); memcpy(ntp->hd_data, hdata, hdata_len); } add_int_to_abs_time(atp, time_to_expir, &ntp->expir_time); ntp->start_time.at_sec = atp->at_sec; ntp->start_time.at_usec = atp->at_usec; tp = (GT_TIMER *)timeroot; tpp = NULL; while (tp) { cmp = abs_time_comp(&tp->expir_time, &ntp->expir_time); if (cmp >= 0) break; tpp = tp; tp = tp->next; } ntp->next = tp; if (NULL == tpp) timeroot = ntp; else tpp->next = ntp; return ntp; } /* Remove timer from the timer chain. */ STATICFNDEF void remove_timer(TID tid) { GT_TIMER *tprev, *tp, *tpp; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DUMP_TIMER_INFO("At the start of remove_timer()"); if (tp = find_timer(tid, &tprev)) /* Warning: assignment */ { if (tprev) tprev->next = tp->next; else { timeroot = tp->next; if (NULL == timeroot) deferred_timers_check_needed = FALSE; /* assert in fast path of "clear_timers" relies on this */ } if (tp->safe) safe_timer_cnt--; tp->next = (GT_TIMER *)timefree; /* place element on free queue */ timefree = tp; num_timers_free++; assert(0 < num_timers_free); /* assert that no duplicate timer entry with the same "tid" exists in the timer chain */ assert((NULL == find_timer(tid, &tpp))); } DUMP_TIMER_INFO("After invoking remove_timer()"); } /* System call to cancel timer. Not static because can be called from generic_signal_handler() to stop timers * from popping yet preserve the blocks so gtmpcat can pick them out of the core. Note that once we exit, * timers are cleared at the top of the exit handler. */ void sys_canc_timer() { struct itimerval zero; memset(&zero, 0, SIZEOF(struct itimerval)); assert(timer_active); /* In case of canceling the system timer, we do not care if we succeed. Consider the two scenarios: * 1) The process is exiting, so all timers must have been removed anyway, and regardless of whether the system * timer got unset or not, no handlers would be processed (even in the event of a pop). * 2) Some timer is being canceled as part of the runtime logic. If the system is experiencing problems, then the * following attempt to schedule a new timer (remember that we at the very least have the heartbeat timer once * database access has been established) would fail; if no other timer is scheduled, then the canceled entry * must have been removed off the queue anyway, so no processing would occur on a pop. */ if (-1 == setitimer(ITIMER_REAL, &zero, &old_sys_timer)) { REPORT_SETITIMER_ERROR("ITIMER_REAL", zero, FALSE, errno); } timer_active = FALSE; /* no timer is active now */ } /* Cancel all unsafe timers. */ void cancel_unsafe_timers(void) { ABS_TIME at; sigset_t savemask; GT_TIMER *active, *curr, *next; int rc; DEBUG_ONLY(int4 cnt = 0;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DUMP_TIMER_INFO("At the start of cancel_unsafe_timers()"); if (1 > timer_stack_count) SIGPROCMASK(SIG_BLOCK, &blockalrm, &savemask, rc); /* block SIGALRM signal */ active = curr = (GT_TIMER *)timeroot; while (curr) { /* If the timer is unsafe, remove it from the chain. */ next = curr->next; if (!curr->safe) remove_timer(curr->tid); curr = next; DEBUG_ONLY(cnt++;) } assert((NULL == timeroot) || (0 < safe_timer_cnt)); if (timeroot) { /* If the head of the queue has changed, or the system timer was not running, start the current first timer. */ if ((active != timeroot) || (!timer_active)) { sys_get_curr_time(&at); start_first_timer(&at); } } else { deferred_timers_check_needed = FALSE; /* There are no timers left, but the system timer was active, so cancel it. */ if (timer_active) sys_canc_timer(); } # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_DEFERRED_TIMERS)) { DBGFPF((stderr, "CANCEL_ALL_TIMERS:\n")); DBGFPF((stderr, " Timer pops handled: %d\n", timer_pop_cnt)); DBGFPF((stderr, " Timers canceled: %d\n", cnt)); } # endif if (1 > timer_stack_count) SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc); DUMP_TIMER_INFO("After invoking cancel_unsafe_timers()"); } /* Initialize timers. */ STATICFNDEF void init_timers() { struct sigaction act; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = timer_handler; sigaction(SIGALRM, &act, &prev_alrm_handler); if (first_timeset && /* not from timer_handler to prevent dup message */ (SIG_IGN != prev_alrm_handler.sa_handler) && /* as set by sig_init */ (SIG_DFL != prev_alrm_handler.sa_handler)) /* utils, compile */ { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_TIMERHANDLER, 3, prev_alrm_handler.sa_handler, LEN_AND_LIT("init_timers")); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_TIMERHANDLER, 3, prev_alrm_handler.sa_handler, LEN_AND_LIT("init_timers")); assert(FALSE); } } /* Check for deferred timers. Drive any timers that have been deferred. In case the system timer is * disabled, launch it for the next scheduled event. This function should be called upon leaving the * interrupt-deferred zone. */ void check_for_deferred_timers(void) { sigset_t savemask; int rc; char *rname; assert(!INSIDE_THREADED_CODE(rname)); /* below code is not thread safe as it does SIGPROCMASK() etc. */ deferred_timers_check_needed = FALSE; timer_handler(DUMMY_SIG_NUM); } /* Check for timer pops. If any timers are on the queue, pretend a sigalrm occurred, and we have to * check everything. This is mainly for use after external calls until such time as external calls * can use this timing facility. Current problem is that external calls are doing their own catching * of sigalarms that should be ours, so we end up hung. */ void check_for_timer_pops(boolean_t sig_handler_changed) { int rc, stolenwhen = 0; /* 0 = no, 1 = not first, 2 = first time */ sigset_t savemask; struct sigaction current_sa; int save_errno = 0; DBGSIGSAFEFPF((stderr, "check_for_timer_pops: sig_handler_changed=%d, first_timeset=%d, timer_active=%d\n", sig_handler_changed, first_timeset, timer_active)); if (sig_handler_changed) { sigaction(SIGALRM, NULL, ¤t_sa); /* get current info */ DBGSIGSAFEFPF((stderr, "check_for_timer_pops: current_sa.sa_handler=%p\n", current_sa.sa_handler)); if (!first_timeset) { if (timer_handler != current_sa.sa_handler) /* check if what we expected */ { init_timers(); if (!stolen_timer) { stolen_timer = TRUE; stolenwhen = 1; } } } else /* we haven't set so should be ... */ { if ((SIG_IGN != current_sa.sa_handler) && /* as set by sig_init */ (SIG_DFL != current_sa.sa_handler)) /* utils, compile */ { if (!stolen_timer) { stolen_timer = TRUE; stolenwhen = 2; } } } DBGSIGSAFEFPF((stderr, "check_for_timer_pops: stolenwhen=%d\n", stolenwhen)); /* Check for an established timer */ deferred_timers_check_needed = TRUE; /* Invoke timer_handler because the ext call could swallow a signal */ } if (timeroot && (1 > timer_stack_count)) DEFERRED_EXIT_HANDLING_CHECK; /* Check for deferred timers */ /* Now that timer handling is done, issue errors as needed */ if (stolenwhen) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_TIMERHANDLER, 3, current_sa.sa_handler, LEN_AND_STR(whenstolen[stolenwhen - 1]), save_errno); } /* Externally exposed routine that does a find_timer and is SIGALRM interrupt safe. */ GT_TIMER *find_timer_intr_safe(TID tid, GT_TIMER **tprev) { sigset_t savemask; GT_TIMER *tcur; int rc; /* Before scanning timer queues, block SIGALRM signal as otherwise that signal could cause an interrupt * timer routine to be driven which could in turn modify the timer queues while this mainline code is * examining the very same queue. This could cause all sorts of invalid returns (of tcur and tprev) * from the find_timer call below. */ if (1 > timer_stack_count) SIGPROCMASK(SIG_BLOCK, &blockalrm, &savemask, rc); tcur = find_timer(tid, tprev); if (1 > timer_stack_count) SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc); return tcur; } fis-gtm-V7.0-005/sr_unix/gt_timers_add_safe_hndlrs.c0000644000032200000250000000302414342376330021361 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2016 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 "gt_timer.h" #include "gt_timers_add_safe_hndlrs.h" #include "semwt2long_handler.h" #include "secshr_client.h" #include "jnl_file_close_timer.h" #ifdef DEBUG #include "fake_enospc.h" #include "wbox_test_init.h" #include "ztimeout_routines.h" #endif /* This optional routine adds entries to the safe_handlers[] array. It is separate because while most executables need * these timers listed, there is one executable (gtmsecshr) that decidedly does not - gtmsecshr. If these routines are * part of gtmsecshr, they cause large numbers of other routines that should definitely not be part of a root privileged * executable to be pulled in. */ void gt_timers_add_safe_hndlrs(void) { add_safe_timer_handler(4, semwt2long_handler, client_timer_handler, simple_timeout_timer, jnl_file_close_timer); # ifdef DEBUG add_safe_timer_handler(2, fake_enospc, handle_deferred_syslog); if (WBTEST_ENABLED(WBTEST_DEFERRED_TIMERS)) add_safe_timer_handler(1, ztimeout_expired); /* Give $ztimeout a pass */ # endif } fis-gtm-V7.0-005/sr_unix/gt_timers_add_safe_hndlrs.h0000644000032200000250000000110414342376330021363 0ustar librarygtc/**************************************************************** * * * Copyright 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #ifndef GT_TIMERS_ADD_SAFE_HNDLRS_INCLUDED #define GT_TIMERS_ADD_SAFE_HNDLRS_INCLUDED void gt_timers_add_safe_hndlrs(void); #endif fis-gtm-V7.0-005/sr_unix/gtcmstub.c0000644000032200000250000000340614342376330016040 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "parse_file.h" #include "gvcmy_open.h" #include "gvcmy_rundown.h" #include "cmidef.h" #include "hashtab_mname.h" /* needed for cmmdef.h */ #include "cmmdef.h" #include "gvcmx.h" #include "gvcmz.h" #include "mvalconv.h" error_def(ERR_TEXT); error_def(ERR_UNIMPLOP); void gvcmy_rundown(void) { return; } void gvcmy_open(gd_region *reg, parse_blk *nb) { RTS_ERROR_ABT(VARLSTCNT(6) ERR_UNIMPLOP, 0, ERR_TEXT, 2, LEN_AND_LIT("This utility does not support remote database operations")); } mint gvcmx_data(void) { assert(FALSE); return -1; } bool gvcmx_get(mval *v) { assert (FALSE); return(-1); } void gvcmx_kill(bool do_subtree) { assert (FALSE); } bool gvcmx_order(void) { assert (FALSE); return(-1); } void gvcmx_put(mval *v) { assert(FALSE); } bool gvcmx_query(mval *val) { assert (FALSE); return(-1); } void gvcmz_clrlkreq(void) { assert (FALSE); } void gvcmx_unlock(unsigned char rmv_locks, bool specific, char incr) { assert (FALSE); } bool gvcmx_zprevious(void) { assert (FALSE); return(-1); } void gvcmx_increment(mval *increment, mval *result) { assert(FALSE); return; } fis-gtm-V7.0-005/sr_unix/gtcmtrstub.c0000755000032200000250000000153614342376330016413 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2004 Sanchez Computer Associates, 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 "cmidef.h" #include "hashtab_mname.h" /* needed for cmmdef.h */ #include "cmmdef.h" #include "gtcmtr_protos.h" bool gtcmtr_lke_clearreq(struct CLB *lnk, char rnum, bool all, bool interactive, int4 pid, mstr *node) { assert(FALSE); return(FALSE); } bool gtcmtr_lke_showreq(struct CLB *lnk, char rnum, bool all, bool wait, int4 pid, mstr *node) { assert(FALSE); return(FALSE); } fis-gtm-V7.0-005/sr_unix/gtm.c0000755000032200000250000001056114342376330015002 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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" #define BYPASS_MEMCPY_OVERRIDE /* Signals gtm_string.h to not override memcpy(). This causes linking problems when libmumps.a * is not available. */ #include "main_pragma.h" #undef UNIX /* Causes non-GTM-runtime routines (libgtmshr) to be used since libgtmshr is not yet available */ #include "gtm_stdio.h" #define UNIX #include "gtm_string.h" #include "gtm_strings.h" #include "gtm_stdlib.h" #include "gtm_limits.h" #include #ifdef __osf__ /* On OSF/1 (Digital Unix), pointers are 64 bits wide; the only exception to this is C programs for which one may * specify compiler and link editor options in order to use (and allocate) 32-bit pointers. However, since C is * the only exception and, in particular because the operating system does not support such an exception, the argv * array passed to the main program is an array of 64-bit pointers. Thus the C program needs to declare argv[] * as an array of 64-bit pointers and needs to do the same for any pointer it sets to an element of argv[]. */ #pragma pointer_size (save) #pragma pointer_size (long) #endif #ifndef NOLIBGTMSHR /* for bta builds we link gtm_main() statically so we do not need to open the shared library */ typedef int (*gtm_main_t)(int argc, char **argv, char **envp); #ifdef __CYGWIN__ /* private copy of gtm_getenv needed since real one is in libgtmshr */ #include "gtm_unistd.h" #ifdef GETENV #undef GETENV #endif #define GETENV private_getenv extern char **environ; /* array of pointers, last has NULL */ char *private_getenv(const char *varname); char *private_getenv(const char *varname) { char *eq, **p; size_t len; if (NULL == environ || NULL == varname) return NULL; len = strlen(varname); for (p = environ; *p; p++) { eq = strchr(*p, '='); if (eq && (*p + len) == eq) { if (!strncasecmp(varname, *p, len)) /* gdb upcases names */ return (eq + 1); } } return NULL; } #endif /* __CYGWIN__ */ #endif int main (int argc, char **argv, char **envp) #ifdef __osf__ #pragma pointer_size (restore) #endif { int status; #ifndef NOLIBGTMSHR char gtmshr_file[GTM_PATH_MAX]; char_ptr_t fptr; unsigned int dir_len; void_ptr_t handle; gtm_main_t gtm_main; /* We fake the output of the following messages to appear as if they were reported by GT.M error * handlers (gtm_putmsg/rts_error) by prefixing every message with %GTM-E-* */ int ERR_GTMDISTUNDEF = 150377714; int ERR_DISTPATHMAX = 150377682; int ERR_DLLNOOPEN = 150379250; int ERR_DLLNORTN = 150379258; if (!(fptr = (char_ptr_t)GETENV(GTM_DIST))) { FPRINTF(stderr, "%%GTM-E-GTMDISTUNDEF, Environment variable $gtm_dist is not defined\n"); return ERR_GTMDISTUNDEF; } dir_len = strlen(fptr); if (GTM_DIST_PATH_MAX <= dir_len) { FPRINTF(stderr, "%%GTM-E-DISTPATHMAX, $gtm_dist path is greater than maximum (%d)\n", GTM_DIST_PATH_MAX); return ERR_DISTPATHMAX; } memcpy(>mshr_file[0], fptr, dir_len); gtmshr_file[dir_len] = DIR_SEPARATOR; memcpy(>mshr_file[dir_len+1], GTMSHR_IMAGE_NAME, STR_LIT_LEN(GTMSHR_IMAGE_NAME)); gtmshr_file[dir_len + STR_LIT_LEN(GTMSHR_IMAGE_NAME) + 1] = 0; /* RTLD_NOW - resolve immediately so we know errors sooner than later * RTLD_GLOBAL - make all exported symbols from gtmshr available for subsequent dlopen */ handle = dlopen(>mshr_file[0], (RTLD_NOW | RTLD_GLOBAL)); if (NULL == handle) { FPRINTF(stderr, "%%GTM-E-DLLNOOPEN, Failed to load external dynamic library %s\n", gtmshr_file); if ((fptr = dlerror()) != NULL) FPRINTF(stderr, "%%GTM-E-TEXT, %s\n", fptr); return ERR_DLLNOOPEN; } gtm_main = (gtm_main_t)dlsym(handle, GTM_MAIN_FUNC); if (NULL == gtm_main) { FPRINTF(stderr, "%%GTM-E-DLLNORTN, Failed to look up the location of the symbol %s\n", GTM_MAIN_FUNC); if ((fptr = dlerror()) != NULL) FPRINTF(stderr, "%%GTM-E-TEXT, %s\n", fptr); return ERR_DLLNORTN; } #endif status = gtm_main(argc, argv, envp); return status; } fis-gtm-V7.0-005/sr_unix/gtm.gtc0000644000032200000250000000324614342376330015334 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright 2010,2013 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. # # # ################################################################# if [ ! -f "GTMDIST"/gtmprofile ] ; then echo Cannot find file "GTMDIST"/gtmprofile to source else . "GTMDIST"/gtmprofile timestamp=`date -u +%Y%m%d%H%M%S`"UTC" ( cd `dirname $gtmgbldir` ; \ $gtm_dist/mupip journal -recover -backward "*" 2>$gtm_tmp/"$USER"_$timestamp"_mupip_recover" && \ $gtm_dist/mupip set -journal="on,before" -region "*" 2>$gtm_tmp/"$USER"_$timestamp"_mupip_set" && \ find . -name \*.mjl_\* -mtime +$gtm_retention -exec rm -vf {} \; ) if [ 0 = $# ] ; then $gtm_dist/mumps -direct elif [ "-help" = "$1" -o "-h" = "$1" -o "-?" = "$1" ] ; then echo "gtm -dir[ect] to enter direct mode (halt returns to shell)" echo "gtm -run to start executing at an entryref" echo "gtm -help / gtm -h / gtm -? to display this text" else $gtm_dist/mumps $* fi ( cd `dirname $gtmgbldir` \ $gtm_dist/mupip rundown -region "*" 2>$gtm_tmp/"$USER"_$timestamp"-"`date -u +%Y%m%d%H%M%S`"UTC_mupip_rundown" ) find $gtm_tmp -name "$USER"_\* -mtime +$gtm_retention -exec rm -f {} \; fi fis-gtm-V7.0-005/sr_unix/gtm_aio.h0000644000032200000250000000620714342376330015636 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2017 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. * * * ****************************************************************/ #ifndef GTM_AIO_H #define GTM_AIO_H #if defined(MUR_USE_AIO) #ifndef __CYGWIN__ #include #endif #if defined(__CYGWIN__) && !defined(AIO_CANCELED) /* minimal just to satisfy mur_init.c and mur_read_file.h. * More would be needed if MUR_USE_AIO were defined */ struct aiocb { int aio_fildes; volatile void *aio_buf; size_t aio_nbytes; off_t aio_offset; size_t aio_bytesread; int aio_errno; }; #endif /* __CYGWIN__ empty aio.h */ #define AIO_POLL_SLEEP_TIME 10 /* 10 msec */ #if defined(_AIX) #define AIO_READ(FD, AIOCBP, STATUS1, STATUS2) \ { \ STATUS2 = SS_NORMAL; \ (AIOCBP)->aio_whence = SEEK_SET; \ do \ { \ STATUS1 = aio_read(FD, AIOCBP); \ } while(-1 == STATUS1 && EAGAIN == errno); \ if (-1 == STATUS1) \ STATUS1 = errno; \ } #define AIO_ERROR(AIOCBP, STATUS) \ MBSTART { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_AIO_ERROR, prev_intrpt_state); \ STATUS = aio_error((AIOCBP)->aio_handle); \ while(STATUS == EINPROGRESS) \ { \ ENABLE_INTERRUPTS(INTRPT_IN_AIO_ERROR, prev_intrpt_state); \ SHORT_SLEEP(AIO_POLL_SLEEP_TIME); \ DEFER_INTERRUPTS(INTRPT_IN_AIO_ERROR, prev_intrpt_state); \ STATUS = aio_error((AIOCBP)->aio_handle); \ } \ ENABLE_INTERRUPTS(INTRPT_IN_AIO_ERROR, prev_intrpt_state); \ } MBEND #define AIO_RETURN(AIOCBP, STATUS) \ { \ STATUS = aio_return((AIOCBP)->aio_handle); \ } #else /* Non-AIX */ #define AIO_READ(FD, AIOCBP, STATUS1, STATUS2) \ { \ STATUS2 = SS_NORMAL; \ AIOCBP->aio_fildes = FD; \ do \ { \ STATUS1 = aio_read(AIOCBP); \ } while(-1 == STATUS1 && EAGAIN == errno); \ if (-1 == STATUS1) \ STATUS1 = errno; \ } #define AIO_ERROR(AIOCBP, STATUS) \ MBSTART { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_AIO_ERROR, prev_intrpt_state); \ STATUS = aio_error(AIOCBP); \ while(STATUS == EINPROGRESS) \ { \ ENABLE_INTERRUPTS(prev_intrpt_state, INTRPT_IN_AIO_ERROR); \ SHORT_SLEEP(AIO_POLL_SLEEP_TIME); \ DEFER_INTERRUPTS(INTRPT_IN_AIO_ERROR, prev_intrpt_state); \ STATUS = aio_error(AIOCBP); \ } \ ENABLE_INTERRUPTS(prev_intrpt_state, INTRPT_IN_AIO_ERROR); \ } MBEND #define AIO_RETURN(AIOCBP, STATUS) \ { \ STATUS = aio_return(AIOCBP); \ } #endif #define AIO_CANCEL(FD, AIOCBP, STATUS) \ { \ STATUS = aio_cancel(FD, AIOCBP); \ } #endif #endif fis-gtm-V7.0-005/sr_unix/gtm_asm_establish.c0000644000032200000250000000162414342376330017675 0ustar librarygtc/**************************************************************** * * * Copyright 2012, 2013 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 "error.h" /* Declares all needed globals */ void gtm_asm_establish(void); /* Only needs to be declared here as is only called from assembler routines */ /* This routine is called from assembler routines (basically dm_start) who need to do an ESTABLISH. We do all of the ESTABLISH * here except for the actual setjmp() call which needs to be in the assembler macro itself. */ void gtm_asm_establish(void) { GTM_ASM_ESTABLISH; } fis-gtm-V7.0-005/sr_unix/gtm_bintim.c0000755000032200000250000001017514342376330016345 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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. * * * ****************************************************************/ /* int gtm_bintim(char *toscan, jnl_proc_time *timep) * * Converts an absolute or relative time to the UNIX internal format (seconds * past 1970) * * Input: * toscan ASCII string containing an absolute or relative time * specification (see below). * * timep pointer to a variable which will hold the absolute or * relative time. *timep's value should be interpreted * as follows: * *timep > 0 absolute time * *timep <= 0 relative time * * Returns: 0 = success * -1 = failure * * ASCII time format: * dd-mmm-yyyy [hh:mm[:ss[:cc]]] absolute time * -- hh:mm[:ss[:cc]] absolute time (w/today's date) * * dd hh:mm[:ss[:cc]] relative time * * */ #include "mdef.h" #include "gtm_time.h" #include "gtm_ctype.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" /* jnl_proc_time needs this. jnl.h needs some of the above */ #include "have_crit.h" #include "gtm_bintim.h" #define monthalphas "abcdefgjlmnoprstuvyABCDEFGJLMNOPRSTUVY" static int getmon(char *month); int gtm_bintim(char *toscan, jnl_proc_time *timep) { time_t now, mktime_ret; struct tm time_tm, *now_tm; int num, sec, min, hour, day, year; int len = STRLEN(toscan), matched = 0; char month[256]; num = SSCANF(toscan, "%d %d", &day, &hour); if (2 == num) { /* delta time format. note: this is the same code as in VMS gtm_bintim.c */ num = SSCANF(toscan, "%d %d:%d:%d%n", &day, &hour, &min, &sec, &matched); if (matched < len) { num = SSCANF(toscan, "%d %d:%d:%d:%*d%n", &day, &hour, &min, &sec, &matched); if (matched < len) { sec = 0; num = SSCANF(toscan, "%d %d:%d%n", &day, &hour, &min, &matched); if (matched < len) return -1; } } *timep = -((day * 86400) + (hour * 3600) + (min * 60) + sec); return 0; } else /* absolute time format */ { *month = '\0'; now = time((time_t *) 0); GTM_LOCALTIME(now_tm, &now); num = SSCANF(toscan, "%d-%[" monthalphas "]-%d %d:%d:%d%n", &day, month, &year, &hour, &min, &sec, &matched); if (matched < len) { num = SSCANF(toscan, "%d-%[" monthalphas "]-%d %d:%d:%d:%*d%n", &day, month, &year, &hour, &min, &sec, &matched); if (matched < len) { sec = 0; num = SSCANF(toscan, "%d-%[" monthalphas "]-%d %d:%d%n", &day, month, &year, &hour, &min, &matched); if (matched < len) { hour = min = sec = 0; num = SSCANF(toscan, "%d-%[" monthalphas "]-%d%n", &day, month, &year, &matched); if (matched < len) { day = now_tm->tm_mday; year = now_tm->tm_year + 1900; num = SSCANF(toscan, "-- %d:%d:%d%n", &hour, &min, &sec, &matched); if (matched < len) { num = SSCANF(toscan, "-- %d:%d:%d:%*d%n", &hour, &min, &sec, &matched); if (matched < len) { sec = 0; num = SSCANF(toscan, "-- %d:%d%n", &hour, &min, &matched); if (matched < len) return -1; } } } } } } time_tm.tm_sec = sec; time_tm.tm_min = min; time_tm.tm_hour = hour; if (*month) time_tm.tm_mon = getmon(month); else time_tm.tm_mon = now_tm->tm_mon; time_tm.tm_mday = day; time_tm.tm_year = year-1900; time_tm.tm_isdst = -1; GTM_MKTIME(mktime_ret, &time_tm); if ((time_t)-1 == mktime_ret) return -1; *timep = (jnl_proc_time)mktime_ret; return 0; } } static int getmon(char *month) { char *p; int i; static char *m[] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; for (p = month; *p; p++) *p = TOLOWER(*p); for (i = 0; i < 12; i++) if (!strcmp(month,m[i])) return i; return -1; } fis-gtm-V7.0-005/sr_unix/gtm_c_stack_trace.c0000644000032200000250000001300114342376330017634 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2022 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 #include #include "gtm_un.h" #include "gtm_signal.h" #include "gtm_string.h" #include "send_msg.h" #include "wbox_test_init.h" #include "gt_timer.h" #include "gtm_logicals.h" #include "trans_log_name.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gtm_c_stack_trace.h" #include "jobsp.h" /* for MAX_PIDSTR_LEN */ #include "gtm_limits.h" error_def(ERR_EXITSTATUS); error_def(ERR_STUCKACT); error_def(ERR_SYSCALL); error_def(ERR_TEXT); /* This looks up the environment variable gtm_procstuckexec, adds the calling information to it, passes it to a SYSTEM call * and checks the returns from both the system and the invoked shell command */ void gtm_c_stack_trace(char *message, pid_t waiting_pid, pid_t blocking_pid, uint4 count) { size_t messagelen; uint4 arr_len; char *command; char *currpos; int save_errno; mstr envvar_logical, trans; char buf[GTM_PATH_MAX]; int status; # ifdef _BSD union wait wait_stat; # else int4 wait_stat; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; messagelen = strnlen(message, MAX_STRLEN); assert(SIZEOF(count) <= SIZEOF(pid_t)); arr_len = GTM_MAX_DIR_LEN + messagelen + (3 * MAX_PIDSTR_LEN) + 5; /* 4 spaces and a terminator */ if (!(TREF(gtm_waitstuck_script)).len) { /* uninitialized buffer - translate logical and move it to the buffer */ envvar_logical.addr = GTM_PROCSTUCKEXEC; envvar_logical.len = SIZEOF(GTM_PROCSTUCKEXEC) - 1; if (SS_NORMAL == (status = TRANS_LOG_NAME(&envvar_logical, &trans, buf, SIZEOF(buf), do_sendmsg_on_log2long))) { /* the environmental variable is defined */ assert(SIZEOF(buf) > trans.len); if (0 != trans.len) { /* and it has a value - stick the length of the translation in char_len of mstr */ (TREF(gtm_waitstuck_script)).len = trans.len + arr_len; (TREF(gtm_waitstuck_script)).addr = (char *)malloc((TREF(gtm_waitstuck_script)).len); memcpy((TREF(gtm_waitstuck_script)).addr, trans.addr, trans.len); *(char *)((TREF(gtm_waitstuck_script)).addr + trans.len) = ' '; trans.len += 1; (TREF(gtm_waitstuck_script)).char_len = trans.len; /* abuse of mstr to hold second length */ } } } else { /* already have a pointer to the shell command get its length */ trans.len = (TREF(gtm_waitstuck_script)).char_len; assert(0 < trans.len); if ((trans.len + arr_len) > (TREF(gtm_waitstuck_script)).len) { /* new message doesn't fit - malloc fresh space and free the old */ assert(0 <= arr_len); /* For SCI so it can't in theory subtract from trans.len */ (TREF(gtm_waitstuck_script)).len = trans.len + arr_len; trans.addr = (char *)malloc(trans.len + arr_len); assert(NULL != trans.addr); memcpy(trans.addr, (TREF(gtm_waitstuck_script)).addr, trans.len); free((TREF(gtm_waitstuck_script)).addr); (TREF(gtm_waitstuck_script)).addr = trans.addr; } } if (0 != (TREF(gtm_waitstuck_script)).len) { /* have a command and a message */ command = (TREF(gtm_waitstuck_script)).addr; currpos = command + trans.len; memcpy(currpos, message, messagelen); currpos += messagelen; *currpos++ = ' '; currpos = (char *)i2asc((unsigned char*)currpos, (unsigned int)waiting_pid); *currpos++ = ' '; currpos = (char *)i2asc((unsigned char*)currpos, (unsigned int)blocking_pid); *currpos++ = ' '; currpos = (char *)i2asc((unsigned char*)currpos, (unsigned int)count); *currpos++ = 0; assert(currpos - (TREF(gtm_waitstuck_script)).addr <= (TREF(gtm_waitstuck_script)).len); status = SYSTEM(command); /* command has been "enhanced" but still starts at the same address */ if (-1 == status) { /* SYSTEM failed */ save_errno = errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_STUCKACT, 4, LEN_AND_LIT("FAILURE"), LEN_AND_STR(command)); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("system"), CALLFROM, save_errno); } else { /* check on how the command did */ assert(SIZEOF(wait_stat) == SIZEOF(int4)); # ifdef _BSD wait_stat.w_status = status; # else wait_stat = status; # endif if (WIFEXITED(wait_stat)) { status = WEXITSTATUS(wait_stat); if (!status) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_STUCKACT, 4, LEN_AND_LIT("SUCCESS"), LEN_AND_STR(command)); else { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_STUCKACT, 4, LEN_AND_LIT("FAILURE"), LEN_AND_STR(command)); if (WIFSIGNALED(wait_stat)) { status = WTERMSIG(wait_stat); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_EXITSTATUS, 6, LEN_AND_LIT("PROCSTUCK terminated by signal"), status, CALLFROM); } else send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_EXITSTATUS, 6, LEN_AND_LIT("PROCSTUCK"), status, CALLFROM); } } else { /* it's gone rogue' */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_STUCKACT, 4, LEN_AND_LIT("FAILURE"), LEN_AND_STR(command)); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_EXITSTATUS, 6, LEN_AND_LIT("PROCSTUCK did not report status"), status, CALLFROM); assert(FALSE); } } } } fis-gtm-V7.0-005/sr_unix/gtm_c_stack_trace_semop.c0000644000032200000250000000611014342376330021042 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2020 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_ipc.h" #include #include #include "gtm_c_stack_trace.h" #include "gtm_c_stack_trace_semop.h" #include "semwt2long_handler.h" #include "wbox_test_init.h" #include "gt_timer.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" GBLREF uint4 process_id; #ifdef DEBUG GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; #endif int try_semop_get_c_stack(int semid, struct sembuf sops[], int nsops) { int stuckcnt, loopcount; int semop_pid, save_errno; int last_sem_trace, rc; # ifdef DEBUG node_local_ptr_t cnl = NULL; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # ifdef DEBUG if ((NULL != gv_cur_region) && gv_cur_region->open && (NULL != cs_addrs)) cnl = cs_addrs->nl; # endif stuckcnt = 0; loopcount = -1; save_errno = 0; last_sem_trace = -1; TREF(semwait2long) = TRUE; do { if (TREF(semwait2long)) { /* 1st time or semwait2long timer pop mean we need to (re)start timer */ TREF(semwait2long) = FALSE; start_timer((TID)semwt2long_handler,(int4)MAX_SEM_WAIT_TIME, semwt2long_handler, 0, NULL); } rc = semop(semid, sops, nsops); if (-1 == rc) { /* didn't work */ save_errno = errno; if ((EINTR == save_errno) && TREF(semwait2long)) { /* semwait2long timer popped, get C-stack trace */ stuckcnt++; last_sem_trace = -1; for (loopcount = 0; loopcount < nsops; loopcount++) { /* for does 1 pass of each semop in set */ if ((last_sem_trace != sops[loopcount].sem_num) && (0 == sops[loopcount].sem_op)) { /* Do not take trace of the same process again, in a point of time */ last_sem_trace = sops[loopcount].sem_num; semop_pid = semctl(semid, sops[loopcount].sem_num, GETPID); if (-1 == semop_pid) { /* no owner to trace leave the for loop with the errno from semctl */ save_errno = errno; rc = -1; break; } if (semop_pid != process_id) { /* not good to try tracing own process */ GET_C_STACK_FROM_SCRIPT("SEMOP_INFO", process_id, semop_pid, stuckcnt); /* Got stack trace signal the first process to continue */ # ifdef DEBUG if (cnl) GTM_WHITE_BOX_TEST(WBTEST_SEMTOOLONG_STACK_TRACE, cnl->wbox_test_seq_num, 3); # endif } } } /* for */ } /* our timer pop */ } /* error return from semop */ } while ((-1 == rc) && (EINTR == save_errno)); /* keep trying as long as error is an EINTR */ cancel_timer((TID)semwt2long_handler); return (-1 == rc) ? save_errno : 0; } fis-gtm-V7.0-005/sr_unix/gtm_c_stack_trace_semop.h0000644000032200000250000000331014342376330021046 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2016 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. * * * ****************************************************************/ #ifndef GTM_C_STACK_TRACE_SEMOP_INCLUDED #define GTM_C_STACK_TRACE_SEMOP_INCLUDED #include #include "semwt2long_handler.h" #ifdef DEBUG #include "gdsroot.h" /* needed for gdsfhead.h */ #include "gtm_facility.h" /* needed for gdsfhead.h */ #include "gdskill.h" /* needed for gdsfhead.h */ #include "fileinfo.h" /* needed for gdsfhead.h */ #include "gdsbt.h" /* needed for gdsfhead.h */ #include "gdsblk.h" /* needed for gdsfhead.h */ #include "gdsfhead.h" /* needed for gtm_semutils.h */ #include "gtm_semutils.h" #include "wbox_test_init.h" #define CHECK_SEMVAL_GRT_SEMOP(SEMID, SEMNUM, SEM_OP) \ { \ int sems_val; \ if (0 > (SEM_OP)) \ { \ sems_val = semctl(SEMID, SEMNUM, GETVAL); \ if (-1 != sems_val) \ assert((sems_val >= abs(SEM_OP)) || (gtm_white_box_test_case_enabled && \ (WBTEST_MUR_ABNORMAL_EXIT_EXPECTED == gtm_white_box_test_case_number))); \ } \ } #else #define CHECK_SEMVAL_GRT_SEMOP(SEMID, SEMNUM, SEMOP) {} #endif #define MAX_SEM_WAIT_TIME (1000 * 60) /* 60 seconds */ int try_semop_get_c_stack(int semid, struct sembuf sops[], int nsops); #endif /*GTM_C_STACK_TRACE_SEMOP_INCLUDED*/ fis-gtm-V7.0-005/sr_unix/gtm_compare_dir.csh0000755000032200000250000000341414342376330017700 0ustar librarygtc#!/usr/local/bin/tcsh ################################################################# # # # Copyright (c) 2011-2020 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. # # # ################################################################# # create the output files from the build and install directory listings and compare them # returns 1 if the comparison fails and 0 if the comparison passes source $1/pro/gtmcshrc mkdir $2/dircompare cd $2/dircompare cp $gtm_tools/dircompare.m.txt ./dircompare.m echo "setenv gtm_dist $gtm_dist" >&! repeat echo "setenv gtmroutines '$gtmroutines'" >>& repeat echo "$gtm_dist/mumps -r dircompare $2/build.dir '$defgroup'" >>& repeat echo "cp $2/install.dir install.out" >>& repeat $gtm_dist/mumps -r dircompare $2/build.dir "$defgroup" > dev_orig.out cp $2/install.dir install.out # The names of the files are modified from build.dir -> dev.out (.gtc extension removed) # So re-sort the file listing. And do not sort the last line which is output of pro/gtmsecsrdir directory # "head -n -1" is not available on AIX and hence the workaround of doing "head - dev_orig.out" set totallines = `wc -l dev_orig.out` @ prolines = $totallines[1] - 1 head -$prolines dev_orig.out | sort -k2 > dev.out tail -1 dev_orig.out >> dev.out diff dev.out install.out > diff.out if ($status) then echo "DIR-E-COMPARE : dev.out (extracted from $2/build.dir) and install.out (extracted from $2/install.dir) differs" echo "Check $PWD/diff.out" exit 1 endif exit 0 fis-gtm-V7.0-005/sr_unix/gtm_compile.h0000755000032200000250000000106314342376330016514 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GTM_COMPILE_INCLUDED #define GTM_COMPILE_INCLUDED int gtm_compile(void); #endif /* GTM_COMPILE_INCLUDED */ fis-gtm-V7.0-005/sr_unix/gtm_confstr.c0000644000032200000250000000544214342376330016537 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2017-2022 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_string.h" #include "gtm_stdlib.h" #include "gtm_common_defs.h" #include "gtm_unistd.h" #include "gtm_stat.h" #include "eintr_wrappers.h" #include "gtmmsg.h" error_def(ERR_SYSUTILCONF); GBLREF volatile boolean_t timer_in_handler; /* Get the path to system utilities and prepend it to the command. Caller sends the max size of the command buffer */ int gtm_confstr(char *command, unsigned int maxsize) { char pathbuf[MAX_FN_LEN]; char *cmd_path, *path_tok, *path_tokptr, *cmd_ptr; size_t n, tok_len, cmdlen; int status, i; struct stat sb; n = confstr(_CS_PATH, NULL, (size_t) 0); assert((MAX_FN_LEN >= n) && (MAX_FN_LEN >= maxsize)); if ((n > maxsize) || (0 == n)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SYSUTILCONF, 2, LEN_AND_LIT("Path for system utilities not defined")); return -1; } confstr(_CS_PATH, pathbuf, n); i = 0; cmdlen = strnlen(command, MAX_FN_LEN); assert(0 < cmdlen); /* Internal commands should never be null */ path_tok = STRTOK_R(pathbuf, ":", &path_tokptr); while (path_tok != NULL) { tok_len = strlen(path_tok); n = (tok_len + cmdlen + 2); assert(n < maxsize); if (maxsize <= n) /* Path + command will exceed command buffer size */ break; cmd_ptr = cmd_path = (char *)malloc(n); assert(NULL != cmd_path); memcpy(cmd_path, path_tok, tok_len); /* For SCI: Use cmd_path for memcpy, since it was the name malloced */ cmd_ptr += tok_len; *cmd_ptr = '/'; cmd_ptr++; memcpy(cmd_ptr, command, cmdlen - 1); /* Don't copy the trailing space in the command */ cmd_ptr += cmdlen - 1; *cmd_ptr = '\0'; STAT_FILE(cmd_path, &sb, status); if (!status && (S_IXUSR & sb.st_mode)) /* File is present and an executable */ { cmdlen = strlen(cmd_path); assert(maxsize > (cmdlen + 1)); if (maxsize <= (cmdlen + 1)) /* Path + command will exceed command buffer size */ { free(cmd_path); break; } assert(MAX_FN_LEN > cmdlen); /* For SCI */ memcpy(command, cmd_path, cmdlen); command[cmdlen] = ' '; command[cmdlen + 1] = '\0'; free(cmd_path); return 0; } free(cmd_path); path_tok = STRTOK_R(NULL, ":", &path_tokptr); } gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SYSUTILCONF, 2, LEN_AND_LIT("System utilities not found at the specified path")); return -1; } fis-gtm-V7.0-005/sr_unix/gtm_conv.c0000644000032200000250000000732014342376330016023 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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_string.h" #include "stringpool.h" #include "gtm_caseconv.h" #include "gtm_icu_api.h" #include "gtm_utf8.h" #include "gtm_conv.h" #include "wbox_test_init.h" GBLREF spdesc stringpool; GBLREF UConverter *chset_desc[CHSET_MAX_IDX]; GBLREF casemap_t casemaps[MAX_CASE_IDX]; LITREF mstr chset_names[CHSET_MAX_IDX_ALL]; LITREF unsigned char lower_to_upper_table[]; error_def(ERR_MAXSTRLEN); /* Routine to verify given parameter against supported case conversion codes. * Valid arguments (case-insensitive): * "U", "L" and "T" * Returns * -1 (if invalid argument) or * index to an entry of casemaps[] (if valid) */ int verify_case(const mstr *parm) { unsigned char c; int index; if (1 == parm->len) { c = lower_to_upper_table[*(uchar_ptr_t)parm->addr]; for (index = 0; index < MAX_CASE_IDX; ++index) { if (c == casemaps[index].code[0]) return index; } } return -1; } int32_t gtm_strToTitle(UChar *dst, int32_t dstlen, const UChar *src, int32_t srclen, const char *locale, UErrorCode *status) { # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_OPFNZCONVERT_FILE_ACCESS_ERROR == gtm_white_box_test_case_number)) { *status = U_FILE_ACCESS_ERROR; return -1; } # endif return u_strToTitle(dst, dstlen, src, srclen, NULL, locale, status); } int gtm_conv(UConverter* from, UConverter* to, mstr *src, char* dstbuff, int* bufflen) { char *dstptr, *dstbase, *srcptr; const char *ichset; int dstlen, src_charlen, srclen; UErrorCode status, status1; if (0 == src->len) return 0; if (NULL == dstbuff) { /* Compute the stringpool buffer space needed for conversion given that source * is encoded in the ichset representation. The ICU functions ucnv_getMinCharSize() * and ucnv_getMaxCharSize() are used to compute the minimum and maximum number of * bytes required per UChar if converted from/to ichset/ochset respectively */ src_charlen = (src->len / ucnv_getMinCharSize(from)) + 1; /* number of UChar's from ichset */ dstlen = UCNV_GET_MAX_BYTES_FOR_STRING(src_charlen, ucnv_getMaxCharSize(to)); dstlen = (dstlen > MAX_STRLEN) ? MAX_STRLEN : dstlen; ENSURE_STP_FREE_SPACE(dstlen); dstbase = (char *)stringpool.free; } else { dstbase = dstbuff; dstlen = *bufflen; } srcptr = src->addr; srclen = (int)src->len; dstptr = dstbase; status = U_ZERO_ERROR; /* initialization to "success" is required by ICU */ ucnv_convertEx(to, from, &dstptr, dstptr + dstlen, (const char**)&srcptr, srcptr + srclen, NULL, NULL, NULL, NULL, TRUE, TRUE, &status); if (U_FAILURE(status)) { if (U_BUFFER_OVERFLOW_ERROR == status) { /* translation requires more space than the maximum allowed GT.M string size */ if (NULL == dstbuff) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXSTRLEN); else { /* Insufficient buffer passed. Return the required buffer length */ src_charlen = (srclen / ucnv_getMinCharSize(from)) + 1; *bufflen = UCNV_GET_MAX_BYTES_FOR_STRING(src_charlen, ucnv_getMaxCharSize(to)); return -1; } } status1 = U_ZERO_ERROR; ichset = ucnv_getName(from, &status1); assert(U_SUCCESS(status1)); UTF8_BADCHAR(1,(unsigned char *) (srcptr - 1), NULL,STRLEN(ichset), ichset); } return (int) (dstptr - dstbase); } fis-gtm-V7.0-005/sr_unix/gtm_conv.h0000755000032200000250000000424114342376330016032 0ustar librarygtc/**************************************************************** * * * Copyright 2006, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #ifndef GTM_CONV_H #define GTM_CONV_H #include "gtm_icu_api.h" #define MAX_CASE_IDX 3 /* maximum number of case conversions supported */ #define MAX_ZCONVBUFF (8 * 1024) /* temporary buffer size used in case conversion */ #define MIN_CHSET_LEN 1 /* minimum length of CHSET names */ #define MAX_CHSET_LEN 8 /* maximum length of CHSET names */ int verify_chset(const mstr *parm); int verify_case(const mstr *parm); UConverter* get_chset_desc(const mstr *chset); int gtm_conv(UConverter* from, UConverter* to, mstr* src, char* dstbuff, int* bufflen); typedef void (*m_casemap_t)(uchar_ptr_t, uchar_ptr_t, int4); typedef int32_t (*u_casemap_t)(UChar *dest, int32_t destCapacity, const UChar *src, int32_t srcLength, const char *locale, UErrorCode *pErrorCode); /* An interlude routine for title case to have the same signature as u_strToUpper/u_strToLower */ int32_t gtm_strToTitle(UChar *dest, int32_t destCapacity, const UChar *src, int32_t srcLength, const char *locale, UErrorCode *pErrorCode); /* descriptor for case mapping */ typedef struct { const char* code; /* case conversion code - "U","L","T" */ m_casemap_t m; /* conversion routine for "M" mode */ u_casemap_t u; /* conversion routine for "UTF-8" mode */ } casemap_t; /* Call back function for ICU to stop at illegal/invalid characters and return with error */ void callback_stop(const void* context, UConverterToUnicodeArgs *args, const char *codeUnits, int32_t length, UConverterCallbackReason reason, UErrorCode *pErrorCode); #endif /* GTM_CONV_H */ fis-gtm-V7.0-005/sr_unix/gtm_conv_init.c0000644000032200000250000000672514342376330017056 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2021 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_string.h" #include "gtm_caseconv.h" #include "gtm_utf8.h" #include "gtm_conv.h" GBLREF UConverter *chset_desc[CHSET_MAX_IDX]; GBLREF casemap_t casemaps[MAX_CASE_IDX]; GBLREF u_casemap_t gtm_strToTitle_ptr; /* Function pointer for gtm_strToTitle */ LITREF mstr chset_names[CHSET_MAX_IDX_ALL]; /* Note these routines are separated from gtm_conv.c to avoid pulling into gtmsecshr all the stuff the conversion modules use */ error_def(ERR_ICUERROR); /* Startup initializations of conversion data */ void gtm_conv_init(void) { assert(gtm_utf8_mode); /* Implicitly created CHSET descriptor for UTF-8 */ get_chset_desc(&chset_names[CHSET_UTF8]); assert(NULL != chset_desc[CHSET_UTF8]); /* initialize the case conversion disposal functions */ casemaps[0].u = u_strToUpper; casemaps[1].u = u_strToLower; casemaps[2].u = gtm_strToTitle_ptr; } UConverter* get_chset_desc(const mstr* chset) { int chset_indx; UErrorCode status; if ((0 >= (chset_indx = verify_chset(chset))) || (CHSET_MAX_IDX <= chset_indx)) return NULL; if (NULL == chset_desc[chset_indx]) { status = U_ZERO_ERROR; chset_desc[chset_indx] = ucnv_open(chset_names[chset_indx].addr, &status); if (U_FAILURE(status)) RTS_ERROR_ABT(VARLSTCNT(3) ERR_ICUERROR, 1, status); /* strange and unexpected ICU unhappiness */ /* Initialize the callback for illegal/invalid characters, so that conversion * stops at the first illegal character rather than continuing with replacement */ status = U_ZERO_ERROR; ucnv_setToUCallBack(chset_desc[chset_indx], &callback_stop, NULL, NULL, NULL, &status); if (U_FAILURE(status)) RTS_ERROR_ABT(VARLSTCNT(3) ERR_ICUERROR, 1, status); /* strange and unexpected ICU unhappiness */ } return chset_desc[chset_indx]; } /* Routine to verify given parameter against supported CHSETs. * Valid arguments (case-insensitive): * "M", "UTF-8", "UTF-16", "UTF-16LE" and "UTF-16BE" * Returns * -1 (if invalid argument) or * 0 (if "M") or * non-zero index to an entry of chset_names[] (if valid) */ int verify_chset(const mstr *parm) { const mstr *vptr, *vptr_top; char mode[MAX_CHSET_LEN]; if ((MIN_CHSET_LEN > parm->len) || (MAX_CHSET_LEN < parm->len)) return -1; /* Parameter is smaller or larger than any possible CHSET */ /* Make a translated copy of the parm */ lower_to_upper((unsigned char *)mode, (unsigned char *)parm->addr, parm->len); /* See if any of our possibilities match */ for (vptr = chset_names, vptr_top = vptr + CHSET_MAX_IDX_ALL; vptr < vptr_top; ++vptr) { if (parm->len == vptr->len && 0 == memcmp(mode, vptr->addr, vptr->len)) return (int)(vptr - chset_names); /* return the index */ } return -1; } void callback_stop(const void* context, UConverterToUnicodeArgs *args, const char *codeUnits, int32_t length, UConverterCallbackReason reason, UErrorCode *pErrorCode) { /* EMPTY BODY: * By not resetting the pErrorCode, this routine returns to ICU routine directing * it to stop and return immediately */ } fis-gtm-V7.0-005/sr_unix/gtm_cshrc.csh0000755000032200000250000000506314342376330016520 0ustar librarygtc################################################################# # # # Copyright 2001, 2014 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. # # # ################################################################# # ##################################################################################### # # # gtm_cshrc.csh - csh/tcsh startup script # # gtm_cshrc.csh sets up the GT.M development root directory, verifies the shell # script version consistency, and invokes other scripts to complete the # environment setup. # ##################################################################################### # Make sure the tcsh environment variables are set properly (even if not running tcsh): if ( $?HOSTOS == "0" ) setenv HOSTOS `uname -s` # operating system if ( $?MACHTYPE == "0" ) setenv MACHTYPE `uname -m` # hardware type # Be careful not to re-initialize all of the environment variables for each subshell # (i.e., don't undo what the last invocation of the version command set up). set gtm_cshrc_first_time = "false" if ( $?gtm_environment_init == "0" ) then set gtm_cshrc_first_time = "true" # for gtm_env.csh (see below) setenv gtm_environment_init "GT.M environment initialized at `date`" if ( $HOSTOS != "OS/390" ) then setenv gtm_root '/usr/library' else setenv gtm_root '/gtm/library' endif setenv gtm_com $gtm_root/com if ( ! -f $gtm_com/gtm_cshrc.csh ) then # This is highly unlikely because that's where this file lives and where it must be executed! echo "gtm_cshrc-E-nogtm_cshrc, There is no gtm_cshrc.csh in $gtm_com" # I am not here! endif if ( -f $gtm_com/gtmdef.csh ) then source $gtm_com/gtmdef.csh # initialize non-version-specific GT.M environment variables else echo "gtm_cshrc-E-nogtmdef, There is no gtmdef.csh in $gtm_com" endif if (! -f $gtm_com/versions.csh ) then echo "gtm_cshrc-E-noversions, There is no versions.csh in $gtm_com" endif endif if ( $gtm_cshrc_first_time == "true" ) then # If it's the first time, we need to initialize all of the environment variables. # Otherwise, we just need to re-specify the aliases. setenv gtm_version_change `date` endif if ( -f $gtm_tools/gtm_env.csh ) then source $gtm_tools/gtm_env.csh # version-controlled definitions and aliases endif if ( $gtm_cshrc_first_time == "true" ) then unsetenv gtm_version_change endif unset gtm_cshrc_first_time fis-gtm-V7.0-005/sr_unix/gtm_dbjnl_dupfd_check.c0000644000032200000250000001255614342376330020475 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2017 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" #ifdef GTM_FD_TRACE #include "gtm_stat.h" #include "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "jnl.h" #include "eintr_wrappers.h" #include "dpgbldir.h" #include "gtm_dbjnl_dupfd_check.h" #include "error.h" #include "send_msg.h" #include "is_file_identical.h" #define MAX_FD_FOR_FASTCHECK 256 GBLDEF gd_region *dupfd_check_reg; /* for debugging purposes */ GBLDEF int dupfd_check_fd; /* for debugging purposes */ GBLDEF fdinfo_t *dupfd_check_openfdarray; /* for debugging purposes */ error_def(ERR_GVFAILCORE); /* Before fixing corrupt jnl fd take a core dump and send syslog message to ensure it gets analyzed */ #define FIX_CORRUPT_JNLFD(REG) \ { \ jnl_private_control *jpc; \ \ assert(FALSE); \ gtm_fork_n_core(); \ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_GVFAILCORE); \ jpc = FILE_INFO(REG)->s_addrs.jnl; \ jpc->channel = NOJNL; \ jpc->cycle--; \ jpc->pini_addr = 0; \ } boolean_t gtm_check_fd_is_valid(gd_region *reg, boolean_t is_db, int fd) { struct stat stat_buf; sgmnt_addrs *csa; int fstat_res; FSTAT_FILE(fd, &stat_buf, fstat_res); assertpro(-1 != fstat_res); assert(reg->open); if (is_db) assertpro(is_gdid_stat_identical(&FILE_ID(reg), &stat_buf)); /* db fd does not corespond back to itself */ else { csa = &FILE_INFO(reg)->s_addrs; /* If fd does not point back to journal file, it could be because of a concurrent journal switch. * Check that. If that fails as well, go ahead and fix the journal file descriptor. */ if (!is_gdid_stat_identical(JNL_GDID_PTR(csa), &stat_buf) && !JNL_FILE_SWITCHED(csa->jnl)) { FIX_CORRUPT_JNLFD(reg); /* Journal file fd is corrupt. Fix it. */ return FALSE; } } return TRUE; } void gtm_dupfd_check_specific(gd_region *reg, fdinfo_t *open_fdarray, int fd, boolean_t is_db) { gd_region *db_reg, *jnl_reg; int fstat_res; struct stat stat_buf; /* Record key local variables in globals in case we take an assertpro and need to analyze the pro core */ dupfd_check_fd = fd; dupfd_check_reg = reg; dupfd_check_openfdarray = open_fdarray; assertpro(0 <= fd); if (MAX_FD_FOR_FASTCHECK > fd) { /* fd is within fastcheck range. We assume the first fd that fills the array is valid and skip the * heavyweight fstat check. For dbg builds though, we do this check just so that code is exercised as well. */ assert((NULL != open_fdarray[fd].reg) || gtm_check_fd_is_valid(reg, is_db, fd)); if (NULL != open_fdarray[fd].reg) { assertpro(!(is_db && open_fdarray[fd].is_db)); /* Cannot do much to recover from 2 DBs with SAME the fd */ /* The fds of one region's database and another region's journal collide. * Check if db fd is indeed valid and if so close the journal's fd. * If db fd is not valid, then cannot do much to recover from this situation. */ FSTAT_FILE(fd, &stat_buf, fstat_res); assertpro(-1 != fstat_res); if (is_db) { db_reg = reg; jnl_reg = open_fdarray[fd].reg; } else { db_reg = open_fdarray[fd].reg; jnl_reg = reg; } assertpro(is_gdid_stat_identical(&FILE_ID(db_reg), &stat_buf)); /* fd doesn't lead back to DB; corrupted! */ /* fd corresponds back to the database which means the jnl file structure is corrupt which can be fixed */ FIX_CORRUPT_JNLFD(jnl_reg); if (!is_db) /* Entry in open_fdarray[fd] is correct. So return without updating it (to the wrong value) */ return; } open_fdarray[fd].reg = reg; open_fdarray[fd].is_db = is_db; } else { /* fd is outside the fast check range. no other go but check that fd is indeed valid (using heavyweight fstat) */ gtm_check_fd_is_valid(reg, is_db, fd); } } /* This routine is a debugging tool written to detect the symptom of D9I11-002714 before any damage to the database occurs. * It checks all open db and jnl file descriptors and identifies any duplicates and if so creates a core file for analysis. */ void gtm_dbjnl_dupfd_check(void) { fdinfo_t open_fdarray[MAX_FD_FOR_FASTCHECK]; gd_addr *addr_ptr; gd_region *r_top, *reg; gd_segment *seg; int fd; sgmnt_addrs *csa; unix_db_info *udi; memset(open_fdarray, 0, SIZEOF(open_fdarray)); for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) { for (reg = addr_ptr->regions, r_top = reg + addr_ptr->n_regions; reg < r_top; reg++) { seg = reg->dyn.addr; if (!IS_ACC_METH_BG_OR_MM(seg->acc_meth)) continue; if (!reg->open || reg->was_open) continue; udi = FILE_INFO(reg); /* Check DB first */ fd = udi->fd; gtm_dupfd_check_specific(reg, open_fdarray, fd, TRUE); /* Check JNL next */ csa = &udi->s_addrs; if (JNL_ALLOWED(csa)) { fd = csa->jnl->channel; if (NOJNL != fd) gtm_dupfd_check_specific(reg, open_fdarray, fd, FALSE); } } } } #endif fis-gtm-V7.0-005/sr_unix/gtm_dbjnl_dupfd_check.h0000644000032200000250000000153414342376330020474 0ustar librarygtc/**************************************************************** * * * Copyright 2009 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. * * * ****************************************************************/ #ifndef GTM_DBJNL_DUPFD_CHECK_H_INCLUDED #define GTM_DBJNL_DUPFD_CHECK_H_INCLUDED typedef struct { gd_region *reg; boolean_t is_db; /* TRUE implies db file, FALSE implies journal file */ } fdinfo_t; boolean_t gtm_check_fd_is_valid(gd_region *reg, boolean_t is_db, int fd); void gtm_dupfd_check_specific(gd_region *reg, fdinfo_t *open_fd, int fd, boolean_t is_db); void gtm_dbjnl_dupfd_check(void); #endif fis-gtm-V7.0-005/sr_unix/gtm_descript.h0000755000032200000250000000337214342376330016706 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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. * * * ****************************************************************/ #ifndef GTM_DESCRIPT_INCLUDED #define GTM_DESCRIPT_INCLUDED #include "gtm_sizeof.h" int mumps_call(); typedef struct { short len; short type; void *val; } gtm_descriptor; typedef struct { unsigned int len; unsigned int type; void *val; } gtm32_descriptor; /* legal types */ #define DSC_K_DTYPE_T 1 #define GTM_ARRAY_OF_CHARS DSC_K_DTYPE_T #define DSC_K_DTYPE_D 2 #define GTM_DOUBLE DSC_K_DTYPE_D #define DSC_K_DTYPE_B 3 #define GTM_CHAR DSC_K_DTYPE_B #define DSC_K_DTYPE_BU 4 #define GTM_UNSIGNED_CHAR DSC_K_DTYPE_BU #define DSC_K_DTYPE_W 5 #define GTM_SHORT DSC_K_DTYPE_W #define DSC_K_DTYPE_WU 6 #define GTM_UNSIGNED_SHORT DSC_K_DTYPE_WU #define DSC_K_DTYPE_L 7 #define GTM_LONG DSC_K_DTYPE_L #define DSC_K_DTYPE_LU 8 #define GTM_UNSIGNED_LONG DSC_K_DTYPE_LU #define DSC_K_DTYPE_F 9 #define GTM_FLOAT DSC_K_DTYPE_F #define GTM_MODE 10 #define GTM_DELIMITER 10 #define DESCRIPTOR(x,y) {x.type = GTM_ARRAY_OF_CHARS; x.len = SIZEOF(y) - 1; x.val = y;} #define DESC_MODE(x,y) {x.type=GTM_MODE; x.len=SIZEOF(y); x.val=(void*)&y;} #define DESC_CHAR(x,y) {x.type=GTM_ARRAY_OF_CHARS; x.len=SIZEOF(y)-1; x.val=y;} #define DESC_ZERO(x) {x.type=0; x.len=0;} #define DESC_LONG(x,y) {x.type=GTM_LONG; x.len=SIZEOF(y); x.val=&y;} #define DESC_DELIM(x,y) {x.type=GTM_DELIMITER;x.len=SIZEOF(y);x.val=(void *)&y;} #endif fis-gtm-V7.0-005/sr_unix/gtm_dump.c0000755000032200000250000000102014342376330016015 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "error.h" void gtm_dump(void) {} fis-gtm-V7.0-005/sr_unix/gtm_dump_core.c0000755000032200000250000000520314342376330017034 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_stat.h" #include "gtm_stdio.h" #include "gtm_unistd.h" #include "gtm_signal.h" /* for SIGPROCMASK */ #include "error.h" #include "gtmcrypt.h" #define CORE_NAME_LEN 20 + 1 void gtm_dump_core(void) { struct sigaction act; char newname[CORE_NAME_LEN]; int rc, suffix, status; struct stat fs1; sigset_t unblock_sigquit; /* Scrub any encryption related information before taking a core dump */ # ifndef DEBUG_NOSCRUB GTMCRYPT_CLOSE; # endif sigemptyset(&act.sa_mask); # ifdef _AIX act.sa_flags = SA_FULLDUMP; # else act.sa_flags = 0; # endif act.sa_handler = SIG_DFL; sigaction(SIGQUIT, &act, 0); /* We are about to generate a core file. If one already exists on the disk, make a simplistic attempt to rename it so we can get the most useful info possible. */ if (0 == Stat("core", &fs1)) /* If core exists (and stat command works) */ { status = -1; for (suffix = 1; 0 != status && suffix < 100; ++suffix) { SNPRINTF(&newname[0], CORE_NAME_LEN, "core%d", suffix); /* Make new file name */ status = Stat(&newname[0], &fs1); /* This file exist ? */ if (0 != status) status = RENAME("core", &newname[0]); /* No, attempt the rename */ else status = -1; /* Yes, reset status for another iteration */ } } /* Even if signals are disabled at this point (for instance online rollback), the SIGQUIT below will be useless. So, * unblock SIGQUIT unconditionally as we are anyways about to die. */ sigemptyset(&unblock_sigquit); sigaddset(&unblock_sigquit, SIGQUIT); SIGPROCMASK(SIG_UNBLOCK, &unblock_sigquit, NULL, rc); kill(getpid(), SIGQUIT); /* The below sleep function should NOT be converted to LONG_SLEEP() despite what ftpput says. This sleep is just * waiting for the preceding signal to take effect so it should not run hiber_start() since this is NOT the main * process but a fork-inspired facsimile spawned for the sole purpose of generating an appropriate core. */ sleep(60); /* BYPASSOK */ UNDERSCORE_EXIT(EXIT_FAILURE); } fis-gtm-V7.0-005/sr_unix/gtm_env.csh0000755000032200000250000002271614342376330016212 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2019 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. # # # ################################################################# # ################################################################################## # # gtm_env.csh - establish GT.M environment # # This script defines reasonable default values for environment variables # and aliases generally compatible with as many Unix platforms as possible. # # Each environment variable or alias used in any non-platform-specific Unix # shell script should have a default value defined in here. In addition, # this script can contain definitions of other environment variables and # aliases considered to be generally useful. # # To accomodate platform-specific peculiarities, this script also invokes # gtm_env_sp.csh (if one exists) to provide over-riding definitions for any # of the environment variables or aliases defined here. # # Naming conventions are designed so an alphabetically-sorted list of # environment variables or aliases will cause related definitions to appear # together. Common abbreviations: # # gt Greystone Technology # gt_ar archiver (library maintenance) # gt_as assembler # gt_cc C compiler # gt_ld linker # gt_lint C de-lint utility # gtm GT.M-specific (as opposed to GT.SQL-specific, for example) # shl shared-library # ################################################################################## # Once the environment variables have been initialized, be careful not to # re-initialize them for each subshell (i.e., don't undo any explicit changes that # have been made since the last version change). if ( $?gtm_version_change == "1" ) then # Generic archiver information: setenv gt_ar_archiver "ar" # name of archiver utility setenv gt_ar_option_create "qlv" # quick, use local directory for temp files, verbose setenv gt_ar_option_update "rlv" # replace, use local directory for temp files, verbose setenv gt_ar_option_delete "dlv" # delete, use local directory for temp files, verbose setenv gt_ar_use_ranlib "no" # don't run ranlib over final archive -- not necessary # Generic assembler information: setenv gt_as_assembler "as" # name of assembler setenv gt_as_inc_convert "false" # if true, convert non-native assembly language header files # to native dialect setenv gt_as_src_convert "false" # if true, convert non-native assembly language source files # to native dialect setenv gt_as_src_suffix ".s" # filename suffix for assembly language source files setenv gt_as_option_DDEBUG "-DDEBUG" # define DEBUG compilation-/assembly-time macro setenv gt_as_option_I "" # specify header (include) file directory # (set by gtmsrc.csh during version command) setenv gt_as_option_debug "" # generate debugger information setenv gt_as_option_nooptimize "" # don't optimize generated code setenv gt_as_option_optimize "" # optimize generated code (if applicable) setenv gt_as_options_common "" # generally-required assembler option(s) # Generic C compiler information: setenv gt_cc_compiler "cc" # name of C compiler setenv gt_cpp_compiler "cpp" # name of C preprocessor setenv gt_cc_option_DBTABLD "-DNOLIBGTMSHR" # define NOLIBGTMSHR macro to statically link mumps for bta image setenv gt_cc_option_DDEBUG "-DDEBUG" # define DEBUG compilation-time macro setenv gt_cc_option_I "-I/usr/local/include" # specify ICU header (include) file directory setenv gt_cc_option_I " $gt_cc_option_I -I/usr/include/libelf" # Libelf is under different directory in SUSE # (set by gtmsrc.csh during version command) setenv gt_cc_option_debug "-g" # generate debugger information setenv gt_cc_option_nooptimize "" # don't optimize generated code setenv gt_cc_option_optimize "-O" # optimize generated code setenv gt_cc_options_common "-c" # suppress link phase; force .o for each .c file even if only one setenv gt_cpp_options_common "" # Options for C preprocessor # Generic linker information: setenv gt_ld_linker "$gt_cc_compiler" # name of link editor; use cc instead of ld to ensure correct # startup routines and C runtime libraries setenv gt_ld_option_output "-o " # option to specify where linker should write output # (some linkers do not allow a space; some require it) setenv gt_ld_options_common "" # generally-required linker options setenv gt_ld_options_bta "$gt_ld_options_common" setenv gt_ld_options_dbg "$gt_ld_options_common" setenv gt_ld_options_pro "$gt_ld_options_common" setenv gt_ld_options_gtmshr "" # force the linker to retain gtmci.o & dependent modules even if not referenced. setenv gt_ld_ci_u_option "-Wl,-u,gtm_ci -Wl,-u,gtm_is_main_thread" setenv gt_ld_extra_libs "" # platform specific GT.M libraries setenv gt_ld_syslibs "-lcurses -lm" # system libraries needed for link (in addition to defaults) setenv gt_ld_sysrtns "" # system routines needed for link (in addition to defaults) setenv gt_ld_aio_syslib "" # system libraries needed for async I/O routines # Linker options to create shared libraries from GT.M generated objects setenv gt_ld_m_shl_linker "ld" setenv gt_ld_m_shl_options "" # Generic shared library information: # setenv gt_cc_shl_options "" # there is no good default for this; leave uninitialized here, # forcing initialization in gtm_env_sp.csh setenv gt_ld_shl_linker "$gt_ld_linker" # setenv gt_ld_shl_options "" # there is no good default for this; leave uninitialized here, # forcing initialization in gtm_env_sp.csh setenv gt_ld_shl_suffix ".sl" # Generic lint information: setenv gt_lint_linter "lint" # name of linter; parameterized to allow use of non-default setenv gt_lint_option_output "-o " # option to specify where lint should write output library # (some lints do not allow a space; some require it) setenv gt_lint_options_common "" # generally-required lint options setenv gt_lint_options_library "" # lint options specific to lint library generation # (e.g., ignore undefined externals because libraries typically # only # contain a subset of sources for a program) setenv gt_lint_options_bta "$gt_cc_option_DDEBUG" setenv gt_lint_options_dbg "$gt_cc_option_DDEBUG" setenv gt_lint_options_pro "" setenv gt_lint_syslibs "" # system libraries against which to check for compatibility endif # Alias definitions. In some Unix implementations, alias definitions are not inherited # by subshells, so they need to be re-initialized each time. Aliases should not be # redefined by users; they should be defined to depend on environment variables which # can be changed by users. # Generic archiver invocations: alias gt_ar '$gt_ar_archiver' # Generic assembler invocations: alias gt_as '$gt_as_assembler $gt_as_options_common $gt_as_option_I' alias gt_as_bta 'gt_as $gt_as_option_debug $gt_as_option_nooptimize' alias gt_as_dbg 'gt_as $gt_as_option_DDEBUG $gt_as_option_debug $gt_as_option_nooptimize' alias gt_as_pro 'gt_as $gt_as_option_optimize' # Generic C compiler invocations: alias gt_cpp '$gt_cpp_compiler $gt_cpp_options_common $gt_cc_option_I' alias gt_cc '$gt_cc_compiler $gt_cc_options_common $gt_cc_option_I' setenv gt_cc 'eval $gt_cc_compiler $gt_cc_options_common $gt_cc_option_I' alias gt_cc_bta 'gt_cc $gt_cc_option_DBTABLD $gt_cc_option_debug $gt_cc_option_nooptimize' setenv gt_cc_bta 'eval $gt_cc $gt_cc_option_DBTABLD $gt_cc_option_debug $gt_cc_option_nooptimize' alias gt_cc_dbg 'gt_cc $gt_cc_option_DDEBUG $gt_cc_option_debug $gt_cc_option_nooptimize' setenv gt_cc_dbg 'eval $gt_cc $gt_cc_option_DDEBUG $gt_cc_option_debug $gt_cc_option_nooptimize' if ( "$HOSTOS" == "Linux" ) then alias gt_cc_pro 'gt_cc $gt_cc_option_optimize $gt_cc_option_debug' setenv gt_cc_pro 'eval $gt_cc $gt_cc_option_optimize $gt_cc_option_debug' else alias gt_cc_pro 'gt_cc $gt_cc_option_optimize' setenv gt_cc_pro 'eval $gt_cc $gt_cc_option_optimize' endif # Generic linker invocation: alias gt_ld '$gt_ld_linker' alias gt_ld_shl '$gt_ld_shl_linker' # Generic lint invocation: alias gt_lint '$gt_lint_linter' # GT.M commands and utilities: alias dse '$gtm_exe/dse' alias gde 'mumps -run GDE' alias gtm 'mumps -direct' alias lke '$gtm_exe/lke' alias mumps '$gtm_exe/mumps' alias mupip '$gtm_exe/mupip' # Note: on VMS, this is defined as the symbol VER*SION, allowing abbreviation by # omitting trailing characters as long as the first three ("VER") are specified. alias version 'set setactive_parms=(\!*); source $gtm_tools/setactive.csh' if ( $?prompt == "1" ) then # On Unix, we have to define all of the shorter forms as individual symbols, although # it's unlikely any forms other than "ver" or "version" are ever used. Note we only # do this for login (interactive) processes; shell scripts should not need to abbreviate. alias ver version alias vers version alias versi version alias versio version endif # Platform-specific overrides, if any: if ( -f $gtm_tools/gtm_env_sp.csh ) then source $gtm_tools/gtm_env_sp.csh endif # Allow platform specific gt_ld_ci related symbol changes # force the linker to retain gtmci.o & dependent modules even if not referenced. setenv gt_ld_ci_options "$gt_ld_ci_u_option $gt_ld_options_gtmshr" fis-gtm-V7.0-005/sr_unix/gtm_env_init_sp.c0000755000032200000250000005611314342376330017402 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2004-2022 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" #ifdef __MVS__ #include #endif #include #include #include #include #include "gtm_stat.h" #include "gtm_string.h" #include "gtm_strings.h" #include "gtm_ctype.h" #include "gtm_unistd.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtmio.h" #include "gtmimagename.h" #include "gtm_logicals.h" #include "trans_numeric.h" #include "trans_log_name.h" #include "logical_truth_value.h" #include "iosp.h" /* for SS_ */ #include "nametabtyp.h" /* for namelook */ #include "namelook.h" #include "io.h" #include "iottdef.h" #include "gtm_env_init.h" /* for gtm_env_init() and gtm_env_init_sp() prototype */ #include "gtm_utf8.h" /* UTF8_NAME */ #include "gtm_zlib.h" #include "error.h" #include "gtm_limits.h" #include "compiler.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "replgbl.h" #include "gtm_semutils.h" #include "gtmlink.h" #include "send_msg.h" #include "eintr_wrappers.h" #include "utfcgr.h" #include "gtm_reservedDB.h" #define DEFAULT_NON_BLOCKED_WRITE_RETRIES 10 /* default number of retries */ #ifdef __MVS__ # define PUTENV_BPXK_MDUMP_PREFIX "_BPXK_MDUMP=" #endif #ifdef DEBUG /* Note the var below is NOT located in gtm_logicals because it is DEBUG-only which would screw-up * regresion test v53003/D9I10002703. */ # define GTM_USESECSHR "$gtm_usesecshr" /* GTM_TEST_FAKE_ENOSPC is used only in debug code so it does not have to go in gtm_logicals.h */ # define GTM_TEST_FAKE_ENOSPC "$gtm_test_fake_enospc" /* GTM_TEST_AUTORELINK_ALWAYS is used only in debug code so it does not have to go in gtm_logicals.h */ # define GTM_TEST_AUTORELINK_ALWAYS "$gtm_test_autorelink_always" /* GTM_DB_COUNTER_SEM_INCR is used only in debug code so it does not have to go in gtm_logicals.h */ # define GTM_DB_COUNTER_SEM_INCR "$gtm_db_counter_sem_incr" /* GTM_TEST_JNLPOOL_SYNC is used only in debug code so it does not have to go in gtm_logicals.h */ # define GTM_TEST_JNLPOOL_SYNC "$gtm_test_jnlpool_sync" #endif /* Remove trailing '/' from path (unless only '/') */ #define REMOVE_TRAILING_SLASH_FROM_MSTR(TRANS) \ { \ while ((1 < TRANS.len) && ('/' == TRANS.addr[TRANS.len - 1])) \ TRANS.len--; \ } GBLREF boolean_t badchar_inhibit, dmterm_default, hup_on, gtm_quiet_halt, is_gtm_chset_utf8, utf8_patnumeric; GBLREF boolean_t ipv4_only; /* If TRUE, only use AF_INET. */ GBLREF char *gtm_core_file; GBLREF char *gtm_core_putenv; GBLREF int gtm_non_blocked_write_retries; /* number for retries for non_blocked write to pipe */ GBLREF uint4 gtm_principal_editing_defaults; /* ext_cap flags if tt */ GBLREF enum db_ver gtm_db_create_ver; /* database creation version */ ZOS_ONLY(GBLREF char *gtm_utf8_locale_object;) ZOS_ONLY(GBLREF boolean_t gtm_tag_utf8_as_ascii;) GBLREF volatile boolean_t timer_in_handler; #ifdef USE_LIBAIO GBLREF char io_setup_errstr[IO_SETUP_ERRSTR_ARRAYSIZE]; #endif LITREF mstr relink_allowed_mstr[]; static readonly nametabent editing_params[] = { {7, "EDITING"}, {7, "EMPTERM"}, {6, "INSERT"}, {9, "NOEDITING"}, {9, "NOEMPTERM"}, {8, "NOINSERT"} }; static readonly unsigned char editing_index[27] = { 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }; error_def(ERR_INVLOCALE); error_def(ERR_INVLINKTMPDIR); error_def(ERR_INVTMPDIR); error_def(ERR_ARCTLMAXHIGH); error_def(ERR_ARCTLMAXLOW); void gtm_env_init_sp(void) { /* Unix only environment initializations */ mstr val, trans; int4 status, index, len, hrtbt_cntr_delta, stat_res; size_t cwdlen; boolean_t ret, is_defined, novalidate; char buf[MAX_SRCLINE + 1], *token, cwd[GTM_PATH_MAX]; char *cwdptr, *c, *end, *strtokptr; struct stat outbuf; int gtm_autorelink_shm_min; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # ifdef __MVS__ /* For now OS/390 only. Eventually, this could be added to all UNIX platforms along with the * capability to specify the desired directory to put a core file in. Needs to be setup before * much of anything else is. */ if (NULL == gtm_core_file) { token = getcwd(cwd, SIZEOF(cwd)); if (NULL != token) { cwdlen = strlen(cwd); gtm_core_putenv = malloc(cwdlen + ('/' == cwd[cwdlen - 1] ? 0 : 1) + SIZEOF(GTMCORENAME) + strlen(PUTENV_BPXK_MDUMP_PREFIX)); MEMCPY_LIT(gtm_core_putenv, PUTENV_BPXK_MDUMP_PREFIX); gtm_core_file = cwdptr = gtm_core_putenv + strlen(PUTENV_BPXK_MDUMP_PREFIX); memcpy(cwdptr, &cwd, cwdlen); cwdptr += cwdlen; if ('/' != cwd[cwdlen - 1]) *cwdptr++ = '/'; memcpy(cwdptr, GTMCORENAME, SIZEOF(GTMCORENAME)); /* Also copys in trailing null */ } /* else gtm_core_file/gtm_core_putenv remain null and we likely cannot generate proper core files */ } # endif assert(GTM_PATH_MAX <= MAX_SRCLINE); /* Validate $gtm_tmp if specified, else that default is available */ val.addr = GTM_TMP_ENV; val.len = SIZEOF(GTM_TMP_ENV) - 1; if ((SS_NORMAL != (status = TRANS_LOG_NAME(&val, &trans, buf, GTM_PATH_MAX, do_sendmsg_on_log2long))) || (0 == trans.len)) { /* Nothing for $gtm_tmp either - use DEFAULT_GTM_TMP which is already a string */ MEMCPY_LIT(buf, DEFAULT_GTM_TMP); trans.addr = buf; trans.len = SIZEOF(DEFAULT_GTM_TMP) - 1; } assert(GTM_PATH_MAX > trans.len); REMOVE_TRAILING_SLASH_FROM_MSTR(trans); /* Remove trailing '/' from trans.addr */ trans.addr[trans.len] = '\0'; STAT_FILE(trans.addr, &outbuf, stat_res); if ((-1 == stat_res) || !S_ISDIR(outbuf.st_mode)) { /* Either the directory doesn't exist or the specified or defaulted entity is not a directory */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_INVTMPDIR, 2, trans.len, trans.addr); } (TREF(gtm_tmpdir)).addr = malloc(trans.len + 1); /* +1 for '\0'; This memory is never freed */ (TREF(gtm_tmpdir)).len = trans.len; memcpy((TREF(gtm_tmpdir)).addr, trans.addr, trans.len + 1); /* Check for and and setup gtm_quiet_halt if specified */ val.addr = GTM_QUIET_HALT; val.len = SIZEOF(GTM_QUIET_HALT) - 1; ret = logical_truth_value(&val, FALSE, &is_defined); if (is_defined) gtm_quiet_halt = ret; /* Initialize local variable null subscripts allowed flag */ val.addr = GTM_LVNULLSUBS; val.len = SIZEOF(GTM_LVNULLSUBS) - 1; ret = trans_numeric(&val, &is_defined, TRUE); /* Not initialized enuf for errors yet so silent rejection of invalid vals */ TREF(lv_null_subs) = ((is_defined && (LVNULLSUBS_FIRST < ret) && (LVNULLSUBS_LAST > ret)) ? ret : LVNULLSUBS_OK); /* ZLIB library compression level */ val.addr = GTM_ZLIB_CMP_LEVEL; val.len = SIZEOF(GTM_ZLIB_CMP_LEVEL) - 1; gtm_zlib_cmp_level = trans_numeric(&val, &is_defined, TRUE); if (GTM_CMPLVL_OUT_OF_RANGE(gtm_zlib_cmp_level)) gtm_zlib_cmp_level = ZLIB_CMPLVL_MIN; /* no compression in this case */ /* Check for and and setup gtm_hupenable if specified */ val.addr = GTM_HUPENABLE; val.len = SIZEOF(GTM_HUPENABLE) - 1; ret = logical_truth_value(&val, FALSE, &is_defined); if (is_defined) hup_on = ret; gtm_principal_editing_defaults = 0; val.addr = GTM_PRINCIPAL_EDITING; val.len = SIZEOF(GTM_PRINCIPAL_EDITING) - 1; if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &trans, buf, GTM_PATH_MAX, do_sendmsg_on_log2long))) { assert(trans.len < GTM_PATH_MAX); trans.addr[trans.len] = '\0'; token = STRTOK_R(trans.addr, ":", &strtokptr); while (NULL != token) { if (ISALPHA_ASCII(token[0])) index = namelook(editing_index, editing_params, STR_AND_LEN(token)); else index = -1; /* ignore this token */ if (0 <= index) { switch (index) { case 0: /* EDITING */ gtm_principal_editing_defaults |= TT_EDITING; break; case 1: /* EMPTERM */ gtm_principal_editing_defaults |= TT_EMPTERM; break; case 2: /* INSERT */ gtm_principal_editing_defaults &= ~TT_NOINSERT; break; case 3: /* NOEDITING */ gtm_principal_editing_defaults &= ~TT_EDITING; break; case 4: /* NOEMPTERM */ gtm_principal_editing_defaults &= ~TT_EMPTERM; break; case 5: /* NOINSERT */ gtm_principal_editing_defaults |= TT_NOINSERT; break; } } token = STRTOK_R(NULL, ":", &strtokptr); } } val.addr = GTM_CHSET_ENV; val.len = STR_LIT_LEN(GTM_CHSET_ENV); if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &trans, buf, GTM_PATH_MAX, do_sendmsg_on_log2long)) && STR_LIT_LEN(UTF8_NAME) == trans.len) { if (!strncasecmp(buf, UTF8_NAME, STR_LIT_LEN(UTF8_NAME))) { is_gtm_chset_utf8 = TRUE; # ifdef __MVS__ val.addr = GTM_CHSET_LOCALE_ENV; val.len = STR_LIT_LEN(GTM_CHSET_LOCALE_ENV); if ((SS_NORMAL == (status = TRANS_LOG_NAME(&val, &trans, buf, GTM_PATH_MAX, do_sendmsg_on_log2long))) && (0 < trans.len)) { /* full path to 64 bit ASCII UTF-8 locale object */ gtm_utf8_locale_object = malloc(trans.len + 1); STRNCPY_STR(gtm_utf8_locale_object, buf, trans.len); gtm_utf8_locale_object[trans.len] = '\0'; } val.addr = GTM_TAG_UTF8_AS_ASCII; val.len = STR_LIT_LEN(GTM_TAG_UTF8_AS_ASCII); if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &trans, buf, GTM_PATH_MAX, do_sendmsg_on_log2long))) { /* We to tag UTF8 files as ASCII so we can read them, this var disables that */ if (status = logical_truth_value(&val, FALSE, &is_defined) && is_defined) gtm_tag_utf8_as_ascii = FALSE; } # endif /* Initialize $ZPATNUMERIC only if $ZCHSET is "UTF-8" */ val.addr = GTM_PATNUMERIC_ENV; val.len = STR_LIT_LEN(GTM_PATNUMERIC_ENV); if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &trans, buf, GTM_PATH_MAX, do_sendmsg_on_log2long)) && STR_LIT_LEN(UTF8_NAME) == trans.len && !strncasecmp(buf, UTF8_NAME, STR_LIT_LEN(UTF8_NAME))) { utf8_patnumeric = TRUE; } val.addr = GTM_BADCHAR_ENV; val.len = STR_LIT_LEN(GTM_BADCHAR_ENV); status = logical_truth_value(&val, TRUE, &is_defined); if (is_defined) badchar_inhibit = status ? TRUE : FALSE; } } /* Initialize variable that controls number of retries for non-blocked writes to a pipe on unix */ val.addr = GTM_NON_BLOCKED_WRITE_RETRIES; val.len = SIZEOF(GTM_NON_BLOCKED_WRITE_RETRIES) - 1; gtm_non_blocked_write_retries = trans_numeric(&val, &is_defined, TRUE); if (!is_defined) gtm_non_blocked_write_retries = DEFAULT_NON_BLOCKED_WRITE_RETRIES; /* Initialize variable that controls the behavior on journal error */ val.addr = GTM_ERROR_ON_JNL_FILE_LOST; val.len = SIZEOF(GTM_ERROR_ON_JNL_FILE_LOST) - 1; TREF(error_on_jnl_file_lost) = trans_numeric(&val, &is_defined, FALSE); if (MAX_JNL_FILE_LOST_OPT < TREF(error_on_jnl_file_lost)) TREF(error_on_jnl_file_lost) = JNL_FILE_LOST_TURN_OFF; /* default behavior */ /* Initialize variable that controls jnl release timeout */ val.addr = GTM_JNL_RELEASE_TIMEOUT; val.len = SIZEOF(GTM_JNL_RELEASE_TIMEOUT) - 1; (TREF(replgbl)).jnl_release_timeout = trans_numeric(&val, &is_defined, TRUE); if (!is_defined) (TREF(replgbl)).jnl_release_timeout = DEFAULT_JNL_RELEASE_TIMEOUT; else if (0 > (TREF(replgbl)).jnl_release_timeout) /* consider negative timeout value as zero */ (TREF(replgbl)).jnl_release_timeout = 0; else if ((MAXPOSINT4 / MILLISECS_IN_SEC) < (TREF(replgbl)).jnl_release_timeout) /* max value supported for timers */ (TREF(replgbl)).jnl_release_timeout = MAXPOSINT4 / MILLISECS_IN_SEC; /* Initialize variable that controls the maximum time that a process should spend while waiting for semaphores in db_init */ val.addr = GTM_DB_STARTUP_MAX_WAIT; val.len = SIZEOF(GTM_DB_STARTUP_MAX_WAIT) - 1; hrtbt_cntr_delta = trans_numeric(&val, &is_defined, FALSE); if (!is_defined) TREF(dbinit_max_delta_secs) = DEFAULT_DBINIT_MAX_DELTA_SECS; else TREF(dbinit_max_delta_secs) = hrtbt_cntr_delta; /* Initialize database file creation version */ gtm_db_create_ver = GDSVCURR; /* Default to the current version */ val.addr = GTM_DB_CREATE_VER; val.len = SIZEOF(GTM_DB_CREATE_VER) - 1; if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &trans, buf, GTM_PATH_MAX, do_sendmsg_on_log2long))) if (('6' == buf[0]) || (('V' == toupper(buf[0])) && ('6' == buf[1]))) gtm_db_create_ver = GDSV6; /* Create prior version: GDSV6 */ /* Initialize variable that controls the location of GT.M custom errors file (used for anticipatory freeze) */ val.addr = GTM_CUSTOM_ERRORS; val.len = SIZEOF(GTM_CUSTOM_ERRORS) - 1; if ((SS_NORMAL == (status = TRANS_LOG_NAME(&val, &trans, buf, GTM_PATH_MAX, do_sendmsg_on_log2long))) && (0 < trans.len)) { assert(GTM_PATH_MAX > trans.len); (TREF(gtm_custom_errors)).addr = malloc(trans.len + 1); /* +1 for '\0'; This memory is never freed */ (TREF(gtm_custom_errors)).len = trans.len; /* For now, we assume that if the environment variable is defined to NULL, anticipatory freeze is NOT in effect */ if (0 < trans.len) { memcpy((TREF(gtm_custom_errors)).addr, buf, trans.len); ((TREF(gtm_custom_errors)).addr)[trans.len] = '\0'; } } /* See if gtm_link is set */ val.addr = GTM_LINK; val.len = SIZEOF(GTM_LINK) - 1; TREF(relink_allowed) = LINK_NORECURSIVE; /* default */ if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &trans, buf, GTM_PATH_MAX, do_sendmsg_on_log2long))) { init_relink_allowed(&trans); /* set TREF(relink_allowed) */ } # ifdef AUTORELINK_SUPPORTED if (!IS_GTMSECSHR_IMAGE) { /* Set default or supplied value for $gtm_linktmpdir */ val.addr = GTM_LINKTMPDIR; val.len = SIZEOF(GTM_LINKTMPDIR) - 1; if (SS_NORMAL != (status = TRANS_LOG_NAME(&val, &trans, buf, GTM_PATH_MAX, do_sendmsg_on_log2long))) { /* Else use default $gtm_tmp value or its default */ val.addr = GTM_TMP_ENV; val.len = SIZEOF(GTM_TMP_ENV) - 1; if ((SS_NORMAL != (status = TRANS_LOG_NAME(&val, &trans, buf, GTM_PATH_MAX, do_sendmsg_on_log2long))) || (0 < trans.len)) { /* Nothing for $gtm_tmp either - use DEFAULT_GTM_TMP which is already a string */ trans.addr = DEFAULT_GTM_TMP; trans.len = SIZEOF(DEFAULT_GTM_TMP) - 1; } novalidate = TRUE; /* Don't validate gtm_linktmpdir if is defaulting to $gtm_tmp */ } else novalidate = FALSE; assert((GTM_PATH_MAX > trans.len) && (0 < trans.len)); REMOVE_TRAILING_SLASH_FROM_MSTR(trans); /* Remove trailing '/' from trans.addr */ (TREF(gtm_linktmpdir)).addr = malloc(trans.len + 1); /* +1 for '\0'; This memory is never freed */ (TREF(gtm_linktmpdir)).len = trans.len; /* For now, we assume that if the environment variable is defined to NULL, anticipatory freeze is NOT in effect */ if (0 < trans.len) memcpy((TREF(gtm_linktmpdir)).addr, trans.addr, trans.len); ((TREF(gtm_linktmpdir)).addr)[trans.len] = '\0'; if (!novalidate) { /* $gtm_linktmpdir was specified - validate it (bypass if using $gtm_tmp or its default as that was * already checked earlier. */ STAT_FILE((TREF(gtm_linktmpdir)).addr, &outbuf, stat_res); if ((-1 == stat_res) || !S_ISDIR(outbuf.st_mode)) { /* Either the directory doesn't exist or the entity is not a directory */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_INVLINKTMPDIR, 2, (TREF(gtm_linktmpdir)).len, (TREF(gtm_linktmpdir)).addr); free((TREF(gtm_linktmpdir)).addr); trans.len = SIZEOF(DEFAULT_GTM_TMP) - 1; trans.addr = DEFAULT_GTM_TMP; REMOVE_TRAILING_SLASH_FROM_MSTR(trans); /* Remove trailing '/' from trans.addr */ (TREF(gtm_linktmpdir)) = trans; } } } /* See if gtm_autorelink_shm is set */ val.addr = GTM_AUTORELINK_SHM; val.len = SIZEOF(GTM_AUTORELINK_SHM) - 1; gtm_autorelink_shm_min = trans_numeric(&val, &is_defined, TRUE); if (!is_defined || !gtm_autorelink_shm_min) TREF(relinkctl_shm_min_index) = 0; else { gtm_autorelink_shm_min = (2 <= gtm_autorelink_shm_min) ? ceil_log2_32bit(gtm_autorelink_shm_min) : 0; TREF(relinkctl_shm_min_index) = gtm_autorelink_shm_min; } /* See if gtm_autorelink_keeprtn is set */ val.addr = GTM_AUTORELINK_KEEPRTN; val.len = SIZEOF(GTM_AUTORELINK_KEEPRTN) - 1; TREF(gtm_autorelink_keeprtn) = logical_truth_value(&val, FALSE, &is_defined); if (!is_defined) TREF(gtm_autorelink_keeprtn) = FALSE; /* See if gtm_autorelink_ctlmax is set */ val.addr = GTM_AUTORELINK_CTLMAX; val.len = SIZEOF(GTM_AUTORELINK_CTLMAX) - 1; TREF(gtm_autorelink_ctlmax) = trans_numeric(&val, &is_defined, TRUE); if (!is_defined) TREF(gtm_autorelink_ctlmax) = RELINKCTL_DEFAULT_ENTRIES; else if (TREF(gtm_autorelink_ctlmax) > RELINKCTL_MAX_ENTRIES) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_ARCTLMAXHIGH, 4, LEN_AND_LIT(GTM_AUTORELINK_CTLMAX), TREF(gtm_autorelink_ctlmax), RELINKCTL_MAX_ENTRIES); TREF(gtm_autorelink_ctlmax) = RELINKCTL_MAX_ENTRIES; } else if (TREF(gtm_autorelink_ctlmax) < RELINKCTL_MIN_ENTRIES) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_ARCTLMAXLOW, 4, LEN_AND_LIT(GTM_AUTORELINK_CTLMAX), TREF(gtm_autorelink_ctlmax), RELINKCTL_MIN_ENTRIES); TREF(gtm_autorelink_ctlmax) = RELINKCTL_MIN_ENTRIES; } # endif /* AUTORELINK_SUPPORTED */ # ifdef DEBUG /* DEBUG-only option to bypass 'easy' methods of things and always use gtmsecshr for IPC cleanups, wakeups, file removal, * etc. Basically use gtmsecshr for anything where it is an option - helps with testing gtmsecshr for proper operation. */ val.addr = GTM_USESECSHR; val.len = SIZEOF(GTM_USESECSHR) - 1; TREF(gtm_usesecshr) = logical_truth_value(&val, FALSE, &is_defined); if (!is_defined) TREF(gtm_usesecshr) = FALSE; /* DEBUG-only option to enable/disable anticipatory freeze fake ENOSPC testing */ val.addr = GTM_TEST_FAKE_ENOSPC; val.len = SIZEOF(GTM_TEST_FAKE_ENOSPC) - 1; TREF(gtm_test_fake_enospc) = logical_truth_value(&val, FALSE, &is_defined); if (!is_defined) TREF(gtm_test_fake_enospc) = FALSE; /* DEBUG-only option to enable autorelink on all directories in $zroutines (except for shlib directories) */ val.addr = GTM_TEST_AUTORELINK_ALWAYS; val.len = SIZEOF(GTM_TEST_AUTORELINK_ALWAYS) - 1; TREF(gtm_test_autorelink_always) = logical_truth_value(&val, FALSE, &is_defined); if (!is_defined) TREF(gtm_test_autorelink_always) = FALSE; /* DEBUG-only option to enable counter semaphore to be incremented by more than the default value of 1 */ val.addr = GTM_DB_COUNTER_SEM_INCR; val.len = SIZEOF(GTM_DB_COUNTER_SEM_INCR) - 1; gtm_db_counter_sem_incr = trans_numeric(&val, &is_defined, TRUE); if (!is_defined || !gtm_db_counter_sem_incr) gtm_db_counter_sem_incr = DEFAULT_DB_COUNTER_SEM_INCR; /* DEBUG-only option to force the journal pool accounting out of sync every n transactions. */ val.addr = GTM_TEST_JNLPOOL_SYNC; val.len = SIZEOF(GTM_TEST_JNLPOOL_SYNC) - 1; TREF(gtm_test_jnlpool_sync) = trans_numeric(&val, &is_defined, TRUE); if (!is_defined) TREF(gtm_test_jnlpool_sync) = 0; # endif # ifdef GTMDBGFLAGS_ENABLED val.addr = GTMDBGFLAGS; val.len = SIZEOF(GTMDBGFLAGS) - 1; TREF(gtmdbgflags) = trans_numeric(&val, &is_defined, TRUE); val.addr = GTMDBGFLAGS_FREQ; val.len = SIZEOF(GTMDBGFLAGS_FREQ) - 1; TREF(gtmdbgflags_freq) = trans_numeric(&val, &is_defined, TRUE); # endif /* See if gtm_ipv4_only is set */ val.addr = GTM_IPV4_ONLY; val.len = SIZEOF(GTM_IPV4_ONLY) - 1; ipv4_only = logical_truth_value(&val, FALSE, NULL); /* See if gtm_dmterm is set */ val.addr = GTM_DMTERM; val.len = SIZEOF(GTM_DMTERM) - 1; dmterm_default = logical_truth_value(&val, FALSE, NULL); /* Set values for gtm_utfcgr_strings and gtm_utfcgr_string_groups */ val.addr = GTM_UTFCGR_STRINGS; val.len = SIZEOF(GTM_UTFCGR_STRINGS) - 1; TREF(gtm_utfcgr_strings) = trans_numeric(&val, &is_defined, TRUE); if (!is_defined) { assert(GTM_UTFCGR_STRINGS_DEFAULT <= GTM_UTFCGR_STRINGS_MAX); TREF(gtm_utfcgr_strings) = GTM_UTFCGR_STRINGS_DEFAULT; } else if (GTM_UTFCGR_STRINGS_MAX < TREF(gtm_utfcgr_strings)) TREF(gtm_utfcgr_strings) = GTM_UTFCGR_STRINGS_MAX; val.addr = GTM_UTFCGR_STRING_GROUPS; val.len = SIZEOF(GTM_UTFCGR_STRING_GROUPS) - 1; TREF(gtm_utfcgr_string_groups) = trans_numeric(&val, &is_defined, TRUE); if (!is_defined) TREF(gtm_utfcgr_string_groups) = GTM_UTFCGR_STRING_GROUPS_DEFAULT; /* If gtm_locale is defined, reset the locale for this process - but only for UTF8 mode */ if (is_gtm_chset_utf8) { val.addr = GTM_LOCALE; val.len = SIZEOF(GTM_LOCALE) - 1; if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &trans, buf, GTM_PATH_MAX, do_sendmsg_on_log2long))) { if ((0 < trans.len) && (GTM_PATH_MAX > trans.len)) { /* Something was specified - need to clear LC_ALL and set LC_CTYPE but need room in buf[] * for string-ending null. */ putenv("LC_ALL"); /* Clear LC_ALL before LC_CTYPE can take effect */ buf[trans.len] = '\0'; status = setenv("LC_CTYPE", buf, TRUE); if (0 != status) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_INVLOCALE, 2, val.len, val.addr, status); } } } # ifdef USE_LIBAIO /* Initialize variable that controls the nr_events parameter to io_setup() for linux AIO. */ val.addr = GTM_AIO_NR_EVENTS; val.len = SIZEOF(GTM_AIO_NR_EVENTS) - 1; TREF(gtm_aio_nr_events) = trans_numeric(&val, &is_defined, FALSE); if (!is_defined || (TREF(gtm_aio_nr_events) == 0)) TREF(gtm_aio_nr_events) = GTM_AIO_NR_EVENTS_DEFAULT; /* Populate the io_setup() error string. */ SNPRINTF(io_setup_errstr, ARRAYSIZE(io_setup_errstr), IO_SETUP_FMT, TREF(gtm_aio_nr_events)); # endif /* Check if gtm_statshare is enabled */ val.addr = GTM_STATSHARE; val.len = SIZEOF(GTM_STATSHARE) - 1; ret = logical_truth_value(&val, FALSE, &is_defined); TREF(statshare_opted_in) = (!is_defined) ? NO_STATS_OPTIN : ret ? ALL_STATS_OPTIN : NO_STATS_OPTIN; /* Pull in specified gtm_statsdir if specified, else default to gtm_tmp or its default. Note we don't validate the directory * here. It need not exist until a database is opened. If gtm_statsdir does not exist, find an appropriate default and * set it so it is always resolvable. */ val.addr = GTM_STATSDIR; val.len = SIZEOF(GTM_STATSDIR) - 1; /* Using MAX_FN_LEN below instead of GTM_PATH_MAX because csa->nl->statsdb_fname[] size is MAX_FN_LEN + 1 */ if ((SS_NORMAL != (status = TRANS_LOG_NAME(&val, &trans, buf, MAX_STATSDIR_LEN, do_sendmsg_on_log2long))) || (0 == trans.len)) { /* Either no translation for $gtm_statsdir or the current and/or expanded value of $gtm_statsdir exceeds the * max path length. For either case $gtm_statsdir needs to be (re)set so try to use $gtm_tmp instead - note * from here down we'll (re)set $gtm_statsdir so it ALWAYS has a (valid) value for mu_cre_file() to later use. */ val.addr = GTM_TMP_ENV; val.len = SIZEOF(GTM_TMP_ENV) - 1; if ((SS_NORMAL != (status = TRANS_LOG_NAME(&val, &trans, buf, MAX_STATSDIR_LEN, do_sendmsg_on_log2long))) || (0 == trans.len)) { /* Nothing for $gtm_tmp - use DEFAULT_GTM_TMP instead */ trans.addr = DEFAULT_GTM_TMP; trans.len = SIZEOF(DEFAULT_GTM_TMP) - 1; } /* In the setenv() call below, trans.addr always points to a double quoted string so has a NULL terminator */ c = GTM_STATSDIR; c++; /* Bump past the '$' to get to the actual envvar name needed by setenv */ status = setenv(c, trans.addr, 1); assert(0 == status); } assert(MAX_STATSDIR_LEN >= trans.len); } fis-gtm-V7.0-005/sr_unix/gtm_env_translate.c0000644000032200000250000000520414342376330017722 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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_string.h" #include "gtm_limits.h" /* needed for PATH_MAX */ #include "error.h" #include "gtmxc_types.h" /* needed for xc_string_t */ #include "lv_val.h" /* needed for "fgncal.h" */ #include "fgncal.h" #include "gtm_env_xlate_init.h" GBLREF mstr env_gtm_env_xlate; GBLREF mval dollar_zdir; error_def(ERR_TEXT); error_def(ERR_XTRNRETSTR); error_def(ERR_XTRNRETVAL); error_def(ERR_XTRNTRANSDLL); error_def(ERR_XTRNTRANSERR); mval* gtm_env_translate(mval* val1, mval* val2, mval* val_xlated) { xc_string_t in1, in2, in3, out; int ret_gtm_env_xlate; char pakname[PATH_MAX + 1]; void_ptr_t pakhandle; MSTR_CONST(routine_name, GTM_ENV_XLATE_ROUTINE_NAME); DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (0 != env_gtm_env_xlate.len) { MV_FORCE_STR(val2); if (NULL == RFPTR(gtm_env_xlate_entry)) { memcpy(pakname, env_gtm_env_xlate.addr, env_gtm_env_xlate.len); pakname[env_gtm_env_xlate.len]='\0'; pakhandle = fgn_getpak(pakname, ERROR); SFPTR(gtm_env_xlate_entry, (fgnfnc)fgn_getrtn(pakhandle, &routine_name, ERROR)); /* With UTF8 mstr changes, xc_string_t is no longer compatible with mstr * so explicit copy of len/addr fields required */ } in1.length = val1->str.len; in1.address = val1->str.addr; in2.length = val2->str.len; in2.address = val2->str.addr; in3.length = dollar_zdir.str.len; in3.address = dollar_zdir.str.addr; out.address = NULL; ret_gtm_env_xlate = IVFPTR(gtm_env_xlate_entry)(&in1, &in2, &in3, &out); val_xlated->str.len = (mstr_len_t)out.length; val_xlated->str.addr = out.address; if (MAX_DBSTRLEN < val_xlated->str.len) RTS_ERROR_ABT(VARLSTCNT(4) ERR_XTRNRETVAL, 2, val_xlated->str.len, MAX_DBSTRLEN); if (0 != ret_gtm_env_xlate) { if ((val_xlated->str.len) && (val_xlated->str.addr)) RTS_ERROR_ABT(VARLSTCNT(6) ERR_XTRNTRANSERR, 0, ERR_TEXT, 2, val_xlated->str.len, val_xlated->str.addr); else RTS_ERROR_ABT(VARLSTCNT(1) ERR_XTRNTRANSERR); } if ((NULL == val_xlated->str.addr) && (0 != val_xlated->str.len)) RTS_ERROR_ABT(VARLSTCNT(1)ERR_XTRNRETSTR); val_xlated->mvtype = MV_STR; val1 = val_xlated; } return val1; } fis-gtm-V7.0-005/sr_unix/gtm_exit_handler.c0000755000032200000250000001612614342376330017533 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_unistd.h" #include "gtm_inet.h" #include "gtm_signal.h" #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "io.h" #include "iosp.h" #include "iotimer.h" #include "error.h" #include "gtm_stdio.h" #include "repl_msg.h" #include "gtmsource.h" #include "gt_timer.h" #include "mutex.h" #include "fgncalsp.h" #include "zcall_package.h" #include "gtm_exit_handler.h" #include "gv_rundown.h" #include "mprof.h" #include "print_exit_stats.h" #include "invocation_mode.h" #include "secshr_db_clnup.h" #include "gtmcrypt.h" #include "relinkctl.h" #include "gvcst_protos.h" #include "op.h" GBLREF int4 exi_condition; GBLREF uint4 dollar_tlevel; GBLREF boolean_t need_core; /* Core file should be created */ GBLREF boolean_t created_core; /* Core file was created */ GBLREF unsigned int core_in_progress; GBLREF boolean_t dont_want_core; GBLREF boolean_t exit_handler_active; GBLREF volatile int4 fast_lock_count; GBLREF boolean_t skip_exit_handler; GBLREF boolean_t is_tracing_on; #ifdef DEBUG GBLREF boolean_t stringpool_unusable; GBLREF boolean_t stringpool_unexpandable; #endif enum rundown_state { rundown_state_lock, rundown_state_mprof, rundown_state_statsdb, rundown_state_db, rundown_state_io, rundown_state_last }; static enum rundown_state attempting; #ifdef DEBUG GBLREF int process_exiting; #endif LITREF mval literal_notimeout; /* This macro is a framework to help perform ONE type of rundown (e.g. db or lock or io rundown etc.). * "gtm_exit_handler" invokes this macro for each type of rundown that is needed and passes appropriate * parameters to indicate the detail needed for each rundown. * Note: This macro uses local variables "attempting", "error_seen" and "actual_exi_condition". */ #define RUNDOWN_STEP(THISSTATE, NEXTSTATE, ERRCODE, STMT) \ { \ if (THISSTATE == attempting) \ { \ if (!error_seen) \ { \ STMT; \ } else \ { \ if (!actual_exi_condition) \ actual_exi_condition = exi_condition; \ if (0 != ERRCODE) \ { \ PRN_ERROR; \ dec_err(VARLSTCNT(1) ERRCODE); \ } \ } \ error_seen = FALSE; \ attempting++; \ } \ assert(NEXTSTATE == (THISSTATE + 1)); \ } #define MPROF_RUNDOWN_MACRO \ { \ if (is_tracing_on) \ turn_tracing_off(NULL); \ } #define LOCK_RUNDOWN_MACRO \ { \ SET_PROCESS_EXITING_TRUE; \ CANCEL_TIMERS; /* Cancel all unsafe timers - No unpleasant surprises */ \ /* Note we call secshr_db_clnup() with the flag NORMAL_TERMINATION even in an error condition \ * here because we know at this point that we aren't in the middle of a transaction commit but \ * crit may be held in one or more regions and/or other odds/ends to cleanup. \ */ \ secshr_db_clnup(NORMAL_TERMINATION); \ zcall_halt(); \ op_unlock(); \ op_zdeallocate((mval *)&literal_notimeout); \ } #define IO_RUNDOWN_MACRO \ { \ /* Invoke cleanup routines for all the shared libraries loaded during external call initialisation. \ * The cleanup routines are not mandatory routines, but if defined, will be invoked before \ * closing the shared library. \ */ \ for (package_ptr = TREF(extcall_package_root); package_ptr; package_ptr = package_ptr->next_package) \ { \ if (package_ptr->package_clnup_rtn) \ package_ptr->package_clnup_rtn(); \ fgn_closepak(package_ptr->package_handle, INFO); \ } \ relinkctl_rundown(TRUE, TRUE); /* decrement relinkctl-attach & rtnobj-reference counts */ \ assert(process_exiting); \ if (MUMPS_CALLIN & invocation_mode) \ { \ flush_pio(); \ io_rundown(RUNDOWN_EXCEPT_STD); \ } else \ io_rundown(NORMAL_RUNDOWN); \ GTMCRYPT_CLOSE; \ } error_def(ERR_GVRUNDOWN); error_def(ERR_LKRUNDOWN); error_def(ERR_MPROFRUNDOWN); /* Function that is invoked at process exit time to do cleanup. * The general flow here is to do various types of rundowns (e.g. db rundown, lock rundown, io rundown etc.). * If one type of rundown encounters an error midway, we want to just move on to the next type of rundown. * This way we do as much cleanup as possible before the process exists. Towards this, we use "exi_ch" as a * condition handler and the RUNDOWN_STEP macro to take care of each type of rundown. */ void gtm_exit_handler(void) { struct sigaction act; struct extcall_package_list *package_ptr; boolean_t error_seen; int4 actual_exi_condition; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (exit_handler_active || skip_exit_handler) /* Skip exit handling if specified or if exit handler already active */ return; exit_handler_active = TRUE; attempting = rundown_state_lock; actual_exi_condition = 0; ESTABLISH_NORET(exi_ch, error_seen); /* "error_seen" is initialized inside this macro */ #ifdef DEBUG if (WBTEST_ENABLED(WBTEST_CRASH_SHUTDOWN_EXPECTED) && (NO_STATS_OPTIN != TREF(statshare_opted_in))) { /* Forced to FALSE when killing processes and we may need to rundown statsdbs */ stringpool_unusable = FALSE; stringpool_unexpandable = FALSE; fast_lock_count = 0; } #endif RUNDOWN_STEP(rundown_state_lock, rundown_state_mprof, ERR_LKRUNDOWN, LOCK_RUNDOWN_MACRO); RUNDOWN_STEP(rundown_state_mprof, rundown_state_statsdb, ERR_MPROFRUNDOWN, MPROF_RUNDOWN_MACRO); /* The condition handler used in the gvcst_remove_statsDB_linkage_all() path takes care of sending errors */ RUNDOWN_STEP(rundown_state_statsdb, rundown_state_db, 0, gvcst_remove_statsDB_linkage_all()); RUNDOWN_STEP(rundown_state_db, rundown_state_io, ERR_GVRUNDOWN, gv_rundown()); /* We pass 0 (not ERR_IORUNDOWN) below to avoid displaying any error if io_rundown fails. One reason we have * seen an external filter M program fail is with a "SYSTEM-E-ENO32, Broken pipe" error if the source or receiver * server (that is communicating with it through a pipe device) closes its end of the pipe and we do not want that * to be treated as an error in rundown (it is how a pipe close happens normally). */ RUNDOWN_STEP(rundown_state_io, rundown_state_last, 0, IO_RUNDOWN_MACRO); REVERT; print_exit_stats(); if (need_core && !created_core && !dont_want_core) /* We needed to core */ { ++core_in_progress; DUMP_CORE; /* This will not return */ } if (actual_exi_condition && !(MUMPS_CALLIN & invocation_mode)) PROCDIE(actual_exi_condition); } fis-gtm-V7.0-005/sr_unix/gtm_exit_handler.h0000755000032200000250000000111014342376330017523 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GTM_EXIT_HANDLER_INCLUDED #define GTM_EXIT_HANDLER_INCLUDED void gtm_exit_handler(void); #endif /* GTM_EXIT_HANDLER_INCLUDED */ fis-gtm-V7.0-005/sr_unix/gtm_fd_trace.c0000644000032200000250000001156514342376330016633 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2020 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" #ifdef GTM_FD_TRACE #include #include /* Before including gtm_fcntl.h, make sure we do not redefine open/creat/close etc. as this module * is where we are defining the interlude functions gtm_open/gtm_creat etc. to use the real system version. * Note that if the system include file redefined open/creat etc. (e.g. HPUX redefines open to __open64 to allow * for large file support) we want to make sure we use the system redefined versions here. So it is necessary * to disable any redefining of these functions that GT.M otherwise does. */ #undef GTM_FD_TRACE #include "gtm_fcntl.h" #include "gtm_stat.h" #include "gtm_unistd.h" #include "gtm_socket.h" #include "caller_id.h" #include "iosp.h" #include "error.h" #include "gtm_string.h" #include "send_msg.h" #include "gtmio.h" /* This is a GT.M wrapper module for all system calls that open/close file descriptors. * This is needed to trace all files that were opened by GT.M (D9I11-002714) */ enum fd_ops { fd_ops_open = 1, /* open */ fd_ops_open3, /* open */ fd_ops_creat, /* creat */ fd_ops_dup, /* dup */ fd_ops_dup2, /* dup2 */ fd_ops_pipe0, /* pipe */ fd_ops_pipe1, /* pipe */ fd_ops_socket, /* socket */ fd_ops_close /* close */ }; typedef struct { caddr_t call_from; int fd; enum fd_ops fd_act; int4 status; /* valid only if fd_act is fd_ops_close or fd_ops_pipe{0,1} */ } fd_trace; #define FD_OPS_ARRAY_SIZE 512 GBLDEF int4 fd_ops_array_index = -1; GBLDEF int4 fd_ops_array_num_wraps = 0; /* to get an idea how many total files were opened/closed */ GBLDEF fd_trace fd_ops_array[FD_OPS_ARRAY_SIZE]; /* space for FD_TRACE macro to record info */ error_def(ERR_CLOSEFAIL); error_def(ERR_CALLERID); /* Determine on what platforms and build types we want to get caller_id information. In pro builds, invoke caller_id only on * those platforms where caller_id is lightweight (i.e. caller_id.s exists). Thankfully AIX (necessary for D9I11-002714) * falls in this category. For debug builds, invoke caller_id unconditionally since performance is not a big concern. */ #if (defined(DEBUG) || defined(_AIX) || defined(__sparc) || defined(__MVS__) || defined(Linux390) \ || (defined(__linux__) && (defined(__i386))) || defined(__osf__)) # define GET_CALLER_ID caller_id(0) #else # define GET_CALLER_ID 0 #endif #define FD_TRACE(OPS, FD, STATUS) \ { \ ++fd_ops_array_index; \ if (FD_OPS_ARRAY_SIZE <= fd_ops_array_index) \ { \ fd_ops_array_num_wraps++; \ fd_ops_array_index = 0; \ } \ fd_ops_array[fd_ops_array_index].call_from = (caddr_t)GET_CALLER_ID; \ fd_ops_array[fd_ops_array_index].fd_act = OPS; \ fd_ops_array[fd_ops_array_index].fd = FD; \ fd_ops_array[fd_ops_array_index].status = (int4)STATUS; \ } int gtm_open(const char *pathname, int flags) { int fd; fd = open(pathname, flags); FD_TRACE(fd_ops_open, fd, 0); return fd; } int gtm_open3(const char *pathname, int flags, mode_t mode) { int fd; fd = open(pathname, flags, mode); FD_TRACE(fd_ops_open3, fd, 0); return fd; } int gtm_creat(const char *pathname, mode_t mode) { int fd; fd = creat(pathname, mode); FD_TRACE(fd_ops_creat, fd, 0); return fd; } int gtm_dup(int oldfd) { int newfd; newfd = dup(oldfd); FD_TRACE(fd_ops_dup, newfd, oldfd); assert(-1 != newfd); return newfd; } int gtm_dup2(int oldfd, int newfd) { int status; status = dup2(oldfd, newfd); assert((-1 == status) || (newfd == status)); FD_TRACE(fd_ops_dup2, newfd, (-1 == status) ? status : oldfd); assert(-1 != newfd); return newfd; } int gtm_pipe1(int pipefd[2]) { int status; status = pipe(pipefd); FD_TRACE(fd_ops_pipe0, pipefd[0], status); FD_TRACE(fd_ops_pipe1, pipefd[1], status); assert(-1 != status); return status; } int gtm_socket(int family, int type, int protocol) { int fd; fd = socket(family, SETSOCKCLOEXEC(type), protocol); if (-1 != fd) { SETFDCLOEXEC(fd); FD_TRACE(fd_ops_socket, fd, 0); } /* it is possible that fd will be -1 if the address family is not supported */ return fd; } int gtm_close(int fd) { int status; int save_errno; status = close(fd); save_errno = errno; FD_TRACE(fd_ops_close, fd, status); if ((-1 == status) && (EINTR != save_errno)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CLOSEFAIL, 1, fd, save_errno); SEND_CALLERID("gtm_close()"); assert(FALSE); } return status; } #endif fis-gtm-V7.0-005/sr_unix/gtm_file_remove.c0000755000032200000250000000141014342376330017347 0ustar librarygtc/**************************************************************** * * * Copyright 2003 Sanchez Computer Associates, 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 #include "gtm_unistd.h" #include "eintr_wrappers.h" #include "gtm_file_remove.h" #include "iosp.h" int4 gtm_file_remove(char *fn, int fn_len, uint4 *ustatus) { assert(0== fn[fn_len]); *ustatus = SS_NORMAL; /* used in VMS only */ if (-1 == UNLINK(fn)) return errno; return SS_NORMAL; } fis-gtm-V7.0-005/sr_unix/gtm_file_stat.c0000755000032200000250000000544214342376330017036 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "errno.h" #include "eintr_wrappers.h" #include "gtm_string.h" #include "gtm_stat.h" #include "gtm_unistd.h" #include "gdsroot.h" #include "gdsbt.h" #include "parse_file.h" #include "gtm_file_stat.h" #include "gtmmsg.h" #define DOT '.' error_def(ERR_FILEPATHTOOLONG); /* Checks the status of a file. * Output Parameter * uint4 *status : error number * Returns: * FILE_NOT_FOUND: if file is not presnt * FILE_PRESENT: if file is present * FILE_READONLY|FILE_PRESENT: if file is readonly * FILE_STAT_ERROR: if error happens during this module. * Side Effect: Except for FILE_STAT_ERROR passed "ret" will have file name with full path. */ int gtm_file_stat(mstr *file, mstr *def, mstr *ret, boolean_t check_prv, uint4 *status) { int stat_res; struct stat stat_buf; char fbuff[MAX_FN_LEN + 1]; parse_blk pblk; mstr tmp_str, *tmpfile; boolean_t file_not_found = FALSE; tmpfile = &tmp_str; if (NULL != def) { memset(&pblk, 0, SIZEOF(pblk)); pblk.buffer = fbuff; pblk.buff_size = MAX_FN_LEN; pblk.fop = (F_SYNTAXO | F_PARNODE); memcpy(fbuff, file->addr, file->len); *(fbuff + file->len) = '\0'; if (NULL != def) { pblk.def1_buf = def->addr; pblk.def1_size = def->len; } *status = parse_file(file, &pblk); if (!(*status & 1)) { file_not_found = TRUE; if (ERR_FILEPATHTOOLONG == *status) return FILE_STAT_ERROR; } pblk.buffer[pblk.b_esl] = 0; tmpfile->addr = pblk.buffer; tmpfile->len = pblk.b_esl; } else { tmpfile = file; tmpfile->addr[tmpfile->len] = 0; } if (!file_not_found) { STAT_FILE((char *)tmpfile->addr, &stat_buf, stat_res); if (0 != stat_res) { *status = errno; if (ENOENT == errno) file_not_found = TRUE; else return FILE_STAT_ERROR; } } if (NULL != ret) { assert((0 < ret->len) && (0 < tmpfile->len)); /* ret->len has the max buffer allocated length, pass that to get_full_path */ if (!get_full_path((char *)tmpfile->addr, (unsigned int)tmpfile->len, ret->addr, (unsigned int *)&ret->len, (unsigned int)ret->len, status)) return FILE_STAT_ERROR; } if (file_not_found) return FILE_NOT_FOUND; /* Now we know file is present */ if (check_prv && 0 != ACCESS((char *)tmpfile->addr, W_OK)) return (FILE_PRESENT | FILE_READONLY); else return FILE_PRESENT; } fis-gtm-V7.0-005/sr_unix/gtm_filter_command.h0000644000032200000250000000117414342376330020047 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018 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 "gtmxc_types.h" #include "gtm_common_defs.h" gtm_string_t gtm_filter_command(char * command, char * caller_name); fis-gtm-V7.0-005/sr_unix/gtm_fork_n_core.c0000755000032200000250000001463714342376330017360 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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" /* This routine may do a malloc() to get space for the MM file header which usually will not be * available in a core file. There is no benefit to running gtm_malloc() here instead of the * system malloc and doing so would actually produce potential error handling implications so * use the system malloc() here (since the process is micro-milli-bleems from dying with a * core anyway to avoid error handling issues. Use system malloc by un-defining the redefinition. */ #undef malloc #include "gtm_signal.h" #include #include #include "gtm_stdio.h" #include "gtm_stat.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "error.h" #include "eintr_wrappers.h" #ifdef DEBUG #include #include #include "gtmio.h" #endif #include "gtmmsg.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "dpgbldir.h" GBLREF boolean_t created_core; /* core file was created */ GBLREF unsigned int core_in_progress; GBLREF int4 exi_condition; GBLREF sigset_t blockalrm; #ifdef DEBUG GBLREF sgmnt_addrs *cs_addrs; #endif error_def(ERR_COREINPROGRESS); error_def(ERR_NOFORKCORE); #define MM_MALLOC_ALREADY_TRIED (sgmnt_data_ptr_t)-1 #ifdef AIX_SYSTRACE_ENABLE # ifndef _AIX # error "Unsupported platform for SYSTRACE_ENABLE" # endif /* Maximum number of trace files to save/rename if SYSTRACE_ENABLE is specified */ #define SYSTRACE_MAX 10 #endif void gtm_fork_n_core(void) { struct sigaction act, intr; pid_t childid, waitrc; int rc, status, save_errno; #ifdef AIX_SYSTRACE_ENABLE struct stat fs1; char oldname[1024], newname[1024], *trcpath, *trcsuffix; unsigned char *p; #endif sigset_t savemask; sgmnt_addrs *csa; sgmnt_data_ptr_t csd, tmp_csd; gd_region *reg, *r_top; intrpt_state_t prev_intrpt_state; DEBUG_ONLY(struct rlimit rlim;) DEBUG_ONLY( getrlimit(RLIMIT_CORE, &rlim); if ( rlim.rlim_cur != rlim.rlim_max) { if (RLIM_INFINITY == rlim.rlim_max) rlim.rlim_cur = RLIM_INFINITY; else { if (rlim.rlim_cur < rlim.rlim_max) { rlim.rlim_cur = rlim.rlim_max; } } setrlimit(RLIMIT_CORE, &rlim); } ) #ifdef AIX_SYSTRACE_ENABLE /* Stop the current trace (aix), rename trace file and restart it. To use this facility: 1. The $gtm_trace environment variable must be set to the directory where the trace files are to reside. 2. The system trace must be started with the following command PRIOR to execution of the mumps executable: trace -a -L 8000000 -o $gtm_trace/systrace This will generate an 8Mb file for each of SYSTRACE_MAX system trace files (total files is SYSTRACE_MAX + 1). Make sure the directory has the room for it. In its present incarnation, we only stop/rename the trace file on a sig-11. Note this code has not been modified to use politically correct wrapper macros since it is intended ONLY for use on AIX. */ if (SIGSEGV == exi_condition) { system("trcstop"); trcpath = getenv("gtm_trace"); if (trcpath) { strcpy(oldname, trcpath); /* copy path name */ strcat(oldname, "/systrace"); /* add file name */ strcpy(newname, oldname); /* copy 'to' file */ /* Verify that the trace file we think was just created actually was */ status = stat(oldname, &fs1); if (0 == status) { /* We have the file. See if we can rename it */ trcsuffix = newname + strlen(newname); /* point to null at end of line */ status = -1; for (suffix = 1; 0 != status && suffix <= SYSTRACE_MAX; ++suffix) { p = i2asc(trcsuffix, suffix); *p = 0; status = stat(newname, &fs1); /* This file exist ? */ if (0 != status) status = RENAME(oldname, newname); /* No, attempt the rename */ else status = -1; /* Yes, reset status for another iteration */ } } strcpy(newname, "trace -a -L 8000000 -o "); strcat(newname, oldname); system(newname); } } #endif if (core_in_progress++) { if (1 == core_in_progress) { /* only report once */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_COREINPROGRESS, 0); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_COREINPROGRESS, 0); } return; } /* ignore interrupts */ sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = SIG_IGN; sigaction(SIGINT, &act, &intr); /* block SIGALRM signal */ SIGPROCMASK(SIG_BLOCK, &blockalrm, &savemask, rc); /* FORK() clears timers in the child, which shouldn't be necessary here since we have SIGALRM blocked, * and it disrupts diagnosis of any timer related issues, so inline the parts we want here. */ DEFER_INTERRUPTS(INTRPT_IN_FORK_OR_SYSTEM, prev_intrpt_state); childid = fork(); /* BYPASSOK */ /* Only ENABLE_INTERRUPTS() in the parent, below, as we don't want any deferred handlers firing in the child. */ if (childid) { ENABLE_INTERRUPTS(INTRPT_IN_FORK_OR_SYSTEM, prev_intrpt_state); if (-1 == childid) { /* restore interrupt handler */ sigaction(SIGINT, &intr, 0); SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_NOFORKCORE, 0, errno); return; /* Fork failed, no core done */ } if (NULL != cs_addrs && NULL != cs_addrs->nl) { DBG_PRINT_BLOCK_INFOS(cs_addrs->nl); } WAITPID(childid, &status, 0, waitrc); save_errno = errno; /* restore interrupt handler */ sigaction(SIGINT, &intr, 0); SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc); --core_in_progress; if (-1 == waitrc) { /* If got error from waitpid, core may or may not have been taken. Assume worst & don't set flag */ errno = save_errno; PERROR("GTM-E-FORKCOREWAIT"); } else created_core = TRUE; } else { intrpt_ok_state = prev_intrpt_state; /* Restore from DEFER_INTERRUPTS */ DUMP_CORE; /* This will (should) not return */ UNDERSCORE_EXIT(-1); /* Protection to kill fork'd process with no rundown by exit handler(s) */ } } fis-gtm-V7.0-005/sr_unix/gtm_ftok.c0000644000032200000250000000247214342376330016024 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2020 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_ipc.h" #include "mmrhash.h" #include #include #include key_t gtm_ftok(const char *path, int id) { int rc; struct stat statbuf; uint32_t key = 0; STAT_FILE(path, &statbuf, rc); if (rc < 0) { return (key_t)-1; } MurmurHash3_x86_32(&statbuf.st_dev, sizeof statbuf.st_dev, key, &key); MurmurHash3_x86_32(&statbuf.st_ino, sizeof statbuf.st_ino, key, &key); /* substitute the id for the top 8 bits of the hash */ key &= 0x00ffffff; key |= (id & 0xff) << 24; return (key_t)key; } fis-gtm-V7.0-005/sr_unix/gtm_getmsg.c0000755000032200000250000000673014342376330016353 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #ifdef SYS_ERRLIST_INCLUDE #include SYS_ERRLIST_INCLUDE #endif #include "error.h" #include "gtmmsg.h" GBLREF bool dec_nofac; #define ERR_TAG "ENO" void gtm_getmsg(int4 msgnum, mstr *msgbuf) { short int severity; char *cp; const char *top, *msgp, *fac, *base; char outbuf[32]; char_ptr_t tag; const err_msg *msg; const err_ctl *ctl; size_t cp_len, faclen, m_len, taglen; assert(0 < msgbuf->len); /* All callers pass in a large buffer */ ctl = err_check(msgnum); if (NULL != ctl) { GET_MSG_INFO(msgnum, ctl, msg); msgp = msg->msg; tag = (char_ptr_t)msg->tag; fac = (const char *)ctl->facname; severity = (short int)SEVMASK(msgnum); } else { severity = ERROR; tag = (char_ptr_t)outbuf; if ((MAX_SYSERR > msgnum) && (msgnum > 0)) { assert(NULL != STRERROR(1)); /* OSF/1 check; can happen with 64-bit pointers and bad declaration */ cp = (char *)tag; MEMCPY_LIT(cp, ERR_TAG); cp += STR_LIT_LEN(ERR_TAG); cp = (char *)i2asc((uchar_ptr_t)cp, msgnum); *cp = '\0'; msgp = STRERROR(msgnum); } else { tag = "UNKNOWN"; msgp = "Unknown system error !SL"; } fac = "SYSTEM"; } m_len = strlen(msgp); if (!dec_nofac) { m_len += (faclen = strlen(fac)); m_len += 4; /* %-- */ m_len += (taglen = strlen((const char *)tag)); m_len += 2; /* , */ } /* Subtract the max length by 1 to leave space for terminating null */ m_len = (m_len > (msgbuf->len - 1)) ? (msgbuf->len - 1) : m_len; base = (const char *)msgbuf->addr; cp = (char *)base; top = (const char *)(cp + m_len); if (!dec_nofac) { /* Use a loop construct to fill the message buffer with as much * information as possible. At any point in the loop, if the * buffer becomes full, a break statement will exit the loop, * terminating the the buffer filling. */ do { if (cp >= top) break; *cp++ = '%'; cp_len = (size_t)((faclen > (top - cp)) ? (top - cp) : faclen); if (cp_len) { memcpy(cp, fac, cp_len); cp += cp_len; } if (cp >= top) break; *cp++ = '-'; if (cp >= top) break; switch(severity) { case SUCCESS: *cp++ = 'S'; break; case INFO: *cp++ = 'I'; break; case WARNING: *cp++ = 'W'; break; case ERROR: *cp++ = 'E'; break; case SEVERE: *cp++ = 'F'; break; default: *cp++ = 'U'; break; } if (cp >= top) break; *cp++ = '-'; cp_len = (size_t)((taglen > (top - cp)) ? (top - cp) : taglen); if (cp_len) { memcpy(cp, tag, cp_len); cp += cp_len; } if (cp >= top) break; *cp++ = ','; if (cp >= top) break; *cp++ = ' '; break; } while (0); } assert((top - cp) <= (m_len - (cp - base))); assert(0 <= (top - cp)); cp_len = (size_t)(top - cp); memcpy(cp, msgp, cp_len); cp += cp_len; /* The buffer size calculation always leaves space for a null character */ assert((int)(cp - base) < msgbuf->len); *cp = 0; msgbuf->len = (int)m_len; } fis-gtm-V7.0-005/sr_unix/gtm_getpwuid.c0000644000032200000250000000563414342376330016714 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2008-2018 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_unistd.h" #include "gtm_pwd.h" #include "gtm_signal.h" /* for SIGPROCMASK */ #include "gtm_string.h" #include "wbox_test_init.h" #undef getpwuid /* since we are going to use the system level "getpwuid" function, undef the alias to "gtm_getpwuid" */ GBLREF boolean_t blocksig_initialized; GBLREF sigset_t block_sigsent; GBLREF struct passwd getpwuid_struct; /* cached copy of "getpwuid" to try avoid future system calls for the same "uid" */ /* This is a wrapper for the system "getpwuid" and is needed to prevent signal interrupts from occurring in the middle * of getpwuid since that is not signal-safe (i.e. could hold system library related locks that might prevent a signal * handler from running other system library calls which use the same lock). */ struct passwd *gtm_getpwuid(uid_t uid) { struct passwd temp_passwd; struct passwd *retval; sigset_t savemask; char *buff; size_t buff_size; int rc; DEBUG_ONLY(static boolean_t first_time = TRUE;) assert(!first_time || (INVALID_UID == getpwuid_struct.pw_uid)); /* assert we do the INVALID_UID init in gbldefs.c */ if (uid != getpwuid_struct.pw_uid) /* if we did not do a "getpwuid" call for this "uid", do it else return cached value */ { assert(blocksig_initialized); /* the set of blocking signals should be initialized at process startup */ if (blocksig_initialized) /* In pro, dont take chances and handle case where it is not initialized */ SIGPROCMASK(SIG_BLOCK, &block_sigsent, &savemask, rc); buff_size = sysconf(_SC_GETPW_R_SIZE_MAX); if (-1 == buff_size) buff_size = 2048; buff = malloc(buff_size); if (NULL == buff) { return NULL; } getpwuid_r(uid, &temp_passwd, buff, buff_size, &retval); if (blocksig_initialized) SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc); if (NULL == retval) return NULL; /* error or "uid" record not found */ getpwuid_struct = *retval; /* Cache return from "getpwuid" call and avoid future calls to this function */ #ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_GETPWUID_CHECK_OVERWRITE == gtm_white_box_test_case_number)) { /* White box test case for the issue GTM-8415. getpwuid_struct should not overwrite by calling getpwuid */ /* uid 1 is a daemon process id */ retval = getpwuid((uid_t)1); assert(retval && STRCMP(getpwuid_struct.pw_name, retval->pw_name)); } first_time = FALSE; #endif } return &getpwuid_struct; } fis-gtm-V7.0-005/sr_unix/gtm_icu.c0000644000032200000250000005255214342376330015645 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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. * * * ****************************************************************/ /* NOTE: THIS MODULE MUST NEVER INCLUDE HEADER fILE gtm_icu_api.h */ #include "mdef.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_strings.h" #include "gtm_stdlib.h" /* needed for realpath */ #include /* needed for GTM_PATH_MAX */ #include "gtm_stat.h" #include #include #include /* needed for setlocale() */ #include /* needed for nl_langinfo() */ #ifdef _AIX #include /* needed for loadquery */ #include /* needed for basename */ #include #endif #define GTMIO_MINIMAL /* If gtmio.h is pulled in for tracing, don't include stuff that causes redefined errors */ #include "error.h" #include "io.h" #include "iosp.h" #include "gtm_logicals.h" /* needed for GTM_ICU_VERSION */ #include "trans_log_name.h" #include "real_len.h" /* for COPY_DLERR_MSG */ #include "fgncal.h" /* needed for COPY_DLLERR_MSG() */ #include "restrict.h" /* Needed for restrictions */ #include "have_crit.h" /* Defer interrupts */ #ifdef _AIX # define DELIM ":" # define MAX_SEARCH_PATH_LEN GTM_PATH_MAX * 4 /* Give enough room for loadquery to give the search paths in the first time */ # define ICU_LIBNAME_LEN GTM_PATH_MAX # define ICU_NOT_FOUND_ERR "Cannot find ICU library in the standard system library path (/usr/lib, /usr/lib64 etc.) or LIBPATH" #endif ZOS_ONLY(GBLREF char *gtm_utf8_locale_object;) GBLREF boolean_t gtm_dist_ok_to_use; GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF volatile boolean_t timer_in_handler; typedef void (*icu_func_t)(); /* a generic pointer type to the ICU function */ /* For now the minimum ICU version supported is 3.6 */ #define ICU_MINIMUM_SUPPORTED_VER "3.6" #define IS_ICU_VER_GREATER_THAN_MIN_VER(major_ver, minor_ver) ((3 < major_ver) || ((3 == major_ver) && (6 <= minor_ver))) /* ICU function to query for the ICU version. Used only if GTM_ICU_VERSION is not set. */ #define GET_ICU_VERSION_FNAME "u_getVersion" /* Indicates the maximum size of the array that is given as input to u_getVersion. The corresponding MACRO * in unicode/uversion.h is U_MAX_VERSION_LENGTH and is tagged as @stable 2.4. */ #define MAX_ICU_VERSION_LENGTH 4 /* Indicates the maximum length of the complete ICU version in string form. The corresponding MACRO in * unicode/uversion.h is U_MAX_VERSION_STRING_LENGTH and is tagged as @stable 2.4. */ #define MAX_ICU_VERSION_STRLEN 20 /* Maintain max length of all the ICU function names that GT.M uses. This is currently 20 so we keep max at 24 to be safe. * In case a new function that has a name longer than 24 chars gets added, an assert below will fail so we are protected. */ #define MAX_ICU_FNAME_LEN 24 /* The function u_getVersion which queries for the version number of installed ICU library expects * UVersionInfo which is typefed to uint8_t. To avoid including unicode/uversion.h that defines UVersionInfo * do explicit typedef of uint8_t to UVersionInfo here. We don't expect UVersionInfo type to change as it is * tagged as @stable ICU 2.4 and GT.M will only support ICU versions >= 3.6 */ typedef uint8_t UVersionInfo[MAX_ICU_VERSION_LENGTH]; /* The first parameter to ICUVERLT36 can be either ICU_LIBNAME or $gtm_icu_version. Depending on the choice * of the first parameter, different suffix is chosen. */ #define ICU_LIBNAME_SUFFIX " has version" #define GTM_ICU_VERSION_SUFFIX " is" /* Declare enumerators for all functions */ #define ICU_DEF(x) x##_, enum { #include "gtm_icu.h" /* BYPASSOK */ icu_func_n /* total number of ICU functions used */ }; #undef ICU_DEF /* Initialize the table of symbol names to be used in dlsym() */ #define ICU_DEF(x) #x, LITDEF char* icu_fname[] = { #include "gtm_icu.h" /* BYPASSOK */ NULL }; #undef ICU_DEF #define ICU_DEF(x) GBLDEF icu_func_t x##_ptr; #include "gtm_icu.h" #undef ICU_DEF /* initialize the table of locations of function pointers that are set by dlsym() */ #define ICU_DEF(x) &x##_ptr, GBLDEF icu_func_t *icu_fptr[] = { #include "gtm_icu.h" /* BYPASSOK */ NULL }; #undef ICU_DEF /* duplicated prototypes needed to avoid including header file gtm_icu_api.h */ void gtm_icu_init(void); void gtm_conv_init(void); error_def(ERR_DLLNOOPEN); error_def(ERR_ICUSYMNOTFOUND); error_def(ERR_ICUVERLT36); error_def(ERR_NONUTF8LOCALE); error_def(ERR_RESTRICTEDOP); error_def(ERR_TEXT); /* * The ICU project has used two different formats to specify the version. These format are visible to us * through icu-config, the library name and renamed symbols. * * icu-config reported the version in the following format from ICU 3.6 until ICU 4.8 * . . * As of ICU 49 (aka 4.9), the format returned is: * . * * * The version numbers used in the ICU library file names from ICU 3.6 until ICU 4.8 were * . . (symlinked to the below name) * . * * As of ICU 49 (aka 4.9) the version used in the library file names is (note the missing dot between major and minor) * . (symlinked to the below name) * * * * The version number extension for renamed symbols from ICU 3.6 until ICU 4.2 was (confirmed on * http://icu-project.org/apiref/icu4c/uvernum_8h.html, search for U_ICU_ENTRY_POINT_RENAME) * _ _ * As of ICU 4.4, the version number extension changed to (note the missing underscore) * _ * * The function below parses gtm_icu_version to determine the strings used in the library and symbol versions */ static boolean_t parse_gtm_icu_version(char *icu_ver_buf, int len, char *icusymver, char *iculibver) { char *ptr; char tmp_errstr[SIZEOF(GTM_ICU_VERSION) + STR_LIT_LEN(GTM_ICU_VERSION_SUFFIX)]; /* "$gtm_icu_version is" */ int4 major_ver, minor_ver; int i; if ((NULL == icu_ver_buf) || (0 == len)) return FALSE; /* empty string */ /* Deconstruct the two known forms of gtm_icu_version "[0-9].[0-9]" and "[0-9][0-9]" ignoring trailing values */ ptr = icu_ver_buf; if (-1 == (major_ver = asc2i((uchar_ptr_t)ptr++, 1))) return FALSE; if ('.' == *ptr) ptr++; if (-1 == (minor_ver = asc2i((uchar_ptr_t)ptr, 1))) return FALSE; /* Generate the ICU symbol renaming string */ i = 0; icusymver[i++] = '_'; icusymver[i++] = *icu_ver_buf; if ( 44 > ((major_ver * 10) + minor_ver)) icusymver[i++] = '_'; icusymver[i++] = *ptr; icusymver[i++] = '\0'; /* Generate the ICU library name string */ i = 0; iculibver[i++] = *icu_ver_buf; iculibver[i++] = *ptr; iculibver[i++] = '\0'; /* Check if the formatted version is greater than or equal to 3.6 */ if (!(IS_ICU_VER_GREATER_THAN_MIN_VER(major_ver, minor_ver))) { /* Construct the first part of the ICUVERLT36 error message. */ SNPRINTF(tmp_errstr, SIZEOF(GTM_ICU_VERSION) + STR_LIT_LEN(GTM_ICU_VERSION_SUFFIX), "%s%s", GTM_ICU_VERSION, GTM_ICU_VERSION_SUFFIX); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_ICUVERLT36, 4, LEN_AND_STR(tmp_errstr), major_ver, minor_ver); } return TRUE; } void gtm_icu_init(void) { char *locale, *chset, *libname, err_msg[MAX_ERRSTR_LEN]; char icu_final_fname[MAX_ICU_FNAME_LEN + 1 + MAX_ICU_VERSION_STRLEN]; /* 1 for '_' in between */ char icu_ver_buf[MAX_ICU_VERSION_STRLEN], icusymver[MAX_ICU_VERSION_STRLEN], iculibver[MAX_ICU_VERSION_STRLEN]; char tmp_errstr[SIZEOF(ICU_LIBNAME) + STR_LIT_LEN(ICU_LIBNAME_SUFFIX)]; /* "libicuio.so has version" */ char icu_libname[SIZEOF(ICU_LIBNAME) + MAX_ICU_VERSION_STRLEN]; char *strtokptr; const char *cur_icu_fname; int icu_final_fname_len, icu_libname_len, len, save_fname_len, icusymver_len, iculibver_len; void_ptr_t handle; char_ptr_t err_str; icu_func_t fptr; int findx, ver; boolean_t icu_getversion_found = FALSE, gtm_icu_ver_defined, symbols_renamed; UVersionInfo icu_version; mstr icu_ver, trans; int iculdflags = ICU_LIBFLAGS; struct stat libpath_stat; char real_path[GTM_PATH_MAX], librarypath[GTM_PATH_MAX]; char *pieceptr, *cptr; # ifdef _AIX int buflen, prev_dyn_size; char buf[ICU_LIBNAME_LEN], temp_path[GTM_PATH_MAX], search_paths[MAX_SEARCH_PATH_LEN]; char *ptr, *each_libpath, *dyn_search_paths = NULL, *search_path_ptr; struct stat real_path_stat; /* To see if the resolved real_path exists or not */ # endif intrpt_state_t prev_intrpt_state; assert(!gtm_utf8_mode); # ifdef __MVS__ if (gtm_utf8_locale_object) locale = setlocale(LC_CTYPE, gtm_utf8_locale_object); else # endif # ifdef _AIX iculdflags |= RTLD_MEMBER; # endif DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); locale = setlocale(LC_CTYPE, ""); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); chset = nl_langinfo(CODESET); if (NULL == chset) /* 4SCA : null return nl_langinfo not possible */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_NONUTF8LOCALE, 2, LEN_AND_LIT("")); if ((NULL == locale) || (0 != strcasecmp(chset, "utf-8")) && (0 != strcasecmp(chset, "utf8"))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_NONUTF8LOCALE, 2, LEN_AND_STR(chset)); /* By default, GT.M will henceforth expect that ICU has been built with symbol renaming disabled. If that is not the case, * GT.M can be notified of this through an environment variable (macro GTM_ICU_VERSION). The variable should contain the * icu version number formatted as ".". Example would be "3.6" to indicate ICU 3.6. * Given the above environment variable, GT.M will try to dlopen the version specific icu library directly * (i.e. libicuio.so.36 instead of libicuio.so). In that case, it will try both renamed and non-renamed symbols and * whichever works it will use that. But if the environment variable is not specified, GT.M will assume that ICU has * been built with no renaming of symbols. Any value of the environment variable not conforming to the above formatting * will be treated as if this environment variable was not set at all and the default behavior (which is to query for * symbols without appended version numbers) will be used. */ icu_ver.addr = GTM_ICU_VERSION; icu_ver.len = STR_LIT_LEN(GTM_ICU_VERSION); gtm_icu_ver_defined = FALSE; if (SS_NORMAL == TRANS_LOG_NAME(&icu_ver, &trans, icu_ver_buf, SIZEOF(icu_ver_buf), do_sendmsg_on_log2long)) { /* GTM_ICU_VERSION is defined. Do edit check on the value before considering it really defined */ gtm_icu_ver_defined = parse_gtm_icu_version(trans.addr, trans.len, icusymver, iculibver); } if (gtm_icu_ver_defined) { /* User explicitly specified an ICU version. So load version specific icu file (e.g. libicuio.so.36) */ icu_libname_len = 0; iculibver_len = STRLEN(iculibver); icusymver_len = STRLEN(icusymver); # if defined(_AIX) || defined(__MVS__) || defined(__CYGWIN__) /* Transform (e.g. libicuio.a -> libicuio36.a ) */ len = STR_LIT_LEN(ICU_LIBNAME_ROOT); memcpy(&icu_libname[icu_libname_len], ICU_LIBNAME_ROOT, len); icu_libname_len += len; memcpy(&icu_libname[icu_libname_len], iculibver, iculibver_len); icu_libname_len += iculibver_len; icu_libname[icu_libname_len++] = '.'; len = STR_LIT_LEN(ICU_LIBNAME_EXT); memcpy(&icu_libname[icu_libname_len], ICU_LIBNAME_EXT, len); icu_libname_len += len; # else /* Transform (e.g. libicuio.so -> libicuio.so.36) */ len = STR_LIT_LEN(ICU_LIBNAME); memcpy(&icu_libname[icu_libname_len], ICU_LIBNAME, len); icu_libname_len += len; icu_libname[icu_libname_len++] = '.'; memcpy(&icu_libname[icu_libname_len], iculibver, iculibver_len); icu_libname_len += iculibver_len; # endif icu_libname[icu_libname_len] = '\0'; assert(SIZEOF(icu_libname) > icu_libname_len); libname = icu_libname; } else libname = ICU_LIBNAME; /* go with default name */ DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); if (RESTRICTED(library_load_path)) { /* Try the version named symlink */ if (gtm_icu_ver_defined) { SNPRINTF(librarypath, LIBRARY_PATH_MAX, GTM_PLUGIN_FMT_FULL, gtm_dist, libname); if (0 != Stat(librarypath, &libpath_stat)) /* Try the default named symlink */ SNPRINTF(librarypath, LIBRARY_PATH_MAX, GTM_PLUGIN_FMT_SHORT ICU_LIBNAME, gtm_dist); } else /* Try the default named symlink */ SNPRINTF(librarypath, LIBRARY_PATH_MAX, GTM_PLUGIN_FMT_SHORT ICU_LIBNAME, gtm_dist); # ifdef _AIX STRNCAT(librarypath, AIX_SHR_64, SIZEOF(AIX_SHR_64)); /* Append "(shr_64.o)" to library path */ # endif libname = librarypath; if (NULL == (handle = dlopen(libname, iculdflags))) { ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); SNPRINTF(err_msg, MAX_ERRSTR_LEN, "dlopen(%s)", libname); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_RESTRICTEDOP, 1, err_msg); } # ifndef _AIX if (NULL == realpath(librarypath, real_path)) assert(FALSE); /* Just opened that library */ pieceptr = STRTOK_R(real_path, DOT_CHAR, &strtokptr); while (NULL != pieceptr) { if ((2 == strlen(pieceptr)) && (('0' <= pieceptr[0]) && ('9' >= pieceptr[0])) && (('0' <= pieceptr[1]) && ('9' >= pieceptr[1]))) { icusymver_len = ver = 0; cptr = pieceptr; A2I(cptr, pieceptr+2, ver); assert(0 <= ver); /* Already validated above */ /* Generate the ICU symbol renaming string */ icusymver[icusymver_len++] = '_'; icusymver[icusymver_len++] = pieceptr[0]; if (44 > ver) icusymver[icusymver_len++] = '_'; icusymver[icusymver_len++] = pieceptr[1]; icusymver[icusymver_len++] = '\0'; gtm_icu_ver_defined = TRUE; break; } pieceptr = STRTOK_R(NULL, DOT_CHAR, &strtokptr); } # endif } else /* Note: the "else" clause spans an ifdef which calls dlopen() twice on AIX */ # ifdef _AIX if (gtm_icu_ver_defined || /* Use the AIX system default when no ICU version specified */ NULL == (handle = dlopen(ICU_LIBNAME_DEF, iculdflags))) { /* AIX has a unique packaging convention in that shared objects are conventionally * archived into a static (.a) library. To resolve the shared library name at runtime * in a version independent way we use loadquery to fetch the paths that might contain * the ICU shared library. By running through each of these paths, we see if any of them * contains the libicuio.a. If so, we use realpath to find the versioned ICU library * that is symbolically linked from libicuio.a. This realpath can then be used to construct * the fully qualified archive + member combination that will be finally dlopen'ed. */ prev_dyn_size = MAX_SEARCH_PATH_LEN; search_path_ptr = search_paths; while(-1 == loadquery(L_GETLIBPATH, search_path_ptr, prev_dyn_size)) { /* We don't expect loadquery to fail for reason other than ENOMEM */ assertpro(ENOMEM == errno); /* If the previous call to loadquery fails and if it's because the input buffer's length was not * enough for loadquery to fill the library search paths, then do a malloc equal to double the prior * size and call loadquery again. It's relatively unlikely that this condition would be reached */ if (NULL != dyn_search_paths) free(dyn_search_paths); prev_dyn_size *= 2; dyn_search_paths = (char *)malloc(prev_dyn_size); search_path_ptr = dyn_search_paths; } /* At this point we have all the library search paths pointed by search_path_ptr seperated by ":". */ each_libpath = STRTOK_R(search_path_ptr, DELIM, &strtokptr); while (NULL != each_libpath) { SNPRINTF(temp_path, GTM_PATH_MAX, "%s/%s", each_libpath, libname); if (NULL == realpath(temp_path, real_path) && (0 != Stat(real_path, &real_path_stat))) { each_libpath = STRTOK_R(NULL, DELIM, &strtokptr); continue; } /* At this point we would have in real_path the fully qualified path to the version'ed libicuio archive. * ICU_LIBNAME - libicuio.a would have resulted in libicuio36.0.a * Now, we need to construct the archive library name along with it's shared object member. This is done * below. */ buflen = 0; /* real_path = /usr/local/lib64/libicuio36.0.a */ ptr = basename(real_path); /* buf = /usr/local/lib64/libicuio36.0.a(libicuio36.0.a */ SNPRINTF(buf, ICU_LIBNAME_LEN, "%s(%s", real_path, ptr); buflen += (STRLEN(real_path) + STRLEN(ptr) + 1); ptr = strrchr(buf, '.'); strcpy(ptr, ".so)"); /* buf = /usr/local/lib64/libicuio36.0.a(libicuio36.0.so) */ buflen += STR_LIT_LEN(".so)"); buf[buflen] = '\0'; /* NULL termination */ break; } if (NULL != dyn_search_paths) free(dyn_search_paths); /* If each_libpath is NULL then we were not able to look for libicuio.a in the loader search path */ if (NULL == each_libpath) { ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_DLLNOOPEN, 2, LEN_AND_STR(libname), ERR_TEXT, 2, LEN_AND_LIT(ICU_NOT_FOUND_ERR)); } libname = buf; handle = dlopen(libname, iculdflags); } # else handle = dlopen(libname, iculdflags); # endif if (NULL == handle) { COPY_DLLERR_MSG(err_str, err_msg); # ifdef _AIX /* On AIX, ICU is sometimes packaged differently where the archived shared library is named as libicuio.so * instead of libicuio36.0.so. Try dlopen with this new naming scheme as well. * Below SNPRINTF converts /usr/local/lib64/libicuio36.0.a to /usr/local/lib64/libicuio36.0.a(libicuio.so) */ SNPRINTF(temp_path, ICU_LIBNAME_LEN, "%s(%s.so)", real_path, ICU_LIBNAME_ROOT); libname = temp_path; handle = dlopen(libname, iculdflags); if (NULL == handle) { ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); libname = buf; # endif RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_DLLNOOPEN, 2, LEN_AND_STR(libname), ERR_TEXT, 2, LEN_AND_STR(err_msg)); # ifdef _AIX } # endif } DEBUG_ONLY(symbols_renamed = -1;) for (findx = 0; findx < icu_func_n; ++findx) { cur_icu_fname = icu_fname[findx]; icu_final_fname_len = 0; len = STRLEN(cur_icu_fname); assert(MAX_ICU_FNAME_LEN > len); /* ensure we have enough space to hold the icu function */ memcpy(&icu_final_fname[icu_final_fname_len], cur_icu_fname, len); icu_final_fname_len += len; icu_final_fname[icu_final_fname_len] = '\0'; assert(SIZEOF(icu_final_fname) > icu_final_fname_len); fptr = NULL; assert((0 != findx) || (-1 == symbols_renamed)); assert((0 == findx) || (FALSE == symbols_renamed) || (TRUE == symbols_renamed)); if ((0 == findx) || !symbols_renamed) #ifdef __CYGWIN__ /* Don't ask why... I have no idea how all the funcs are just in the global space in Cygwin */ fptr = (icu_func_t)dlsym(NULL, icu_final_fname); #else fptr = (icu_func_t)dlsym(handle, icu_final_fname); #endif if (NULL == fptr) { /* If gtm_icu_version is defined to a proper value, then try function name with _ */ if (gtm_icu_ver_defined && ((0 == findx) || symbols_renamed)) { memcpy(&icu_final_fname[icu_final_fname_len], icusymver, icusymver_len); icu_final_fname_len += icusymver_len; icu_final_fname[icu_final_fname_len] = '\0'; assert(SIZEOF(icu_final_fname) > icu_final_fname_len); #ifdef __CYGWIN__ fptr = (icu_func_t)dlsym(NULL, icu_final_fname); #else fptr = (icu_func_t)dlsym(handle, icu_final_fname); #endif } if (NULL == fptr) { COPY_DLLERR_MSG(err_str, err_msg); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ICUSYMNOTFOUND, 2, LEN_AND_STR(cur_icu_fname), ERR_TEXT, 2, LEN_AND_STR(err_msg)); } if (0 == findx) /* record the fact that the symbols ARE renamed */ symbols_renamed = TRUE; } else if (0 == findx) /* record the fact that the symbols are NOT renamed */ symbols_renamed = FALSE; assert((0 == findx) || icu_getversion_found || gtm_icu_ver_defined); /* u_getVersion should have been dlsym'ed */ *icu_fptr[findx] = fptr; /* If the current function that is dlsym'ed is u_getVersion, then we use fptr to query for the library's ICU * version. If it is less than the least ICU version that GT.M supports we issue an error. If not, we continue * dlsym the rest of the functions. To facilitate issuing wrong version error early, the ICU function getVersion * should be the first function in gtm_icu.h. This way dlsym on u_getVersion will happen as the first thing in * this loop. But do all this only if gtm_icu_version is not defined in the environment. If it's defined to an * an appropriate value in the environment then the version check would have happened before and there isn't any * need to repeat it again. */ if (!gtm_icu_ver_defined && (FALSE == icu_getversion_found) && (0 == strcmp(cur_icu_fname, GET_ICU_VERSION_FNAME))) { icu_getversion_found = TRUE; memset(icu_version, 0, MAX_ICU_VERSION_LENGTH); fptr(icu_version); if (!(IS_ICU_VER_GREATER_THAN_MIN_VER(icu_version[0], icu_version[1]))) { /* Construct the first part of the ICUVERLT36 error message. */ SNPRINTF(tmp_errstr, SIZEOF(ICU_LIBNAME) + STR_LIT_LEN(ICU_LIBNAME_SUFFIX), "%s%s", ICU_LIBNAME, ICU_LIBNAME_SUFFIX); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_ICUVERLT36, 4, LEN_AND_STR(tmp_errstr), icu_version[0], icu_version[1]); } } } ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); gtm_utf8_mode = TRUE; /* gtm_wcswidth()/U_ISPRINT() in util_format() can henceforth be safely called now that ICU initialization is complete */ gtm_conv_init(); } fis-gtm-V7.0-005/sr_unix/gtm_icu.h0000755000032200000250000000211714342376330015645 0ustar librarygtc/**************************************************************** * * * Copyright 2006, 2009 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. * * * ****************************************************************/ ICU_DEF(u_getVersion) ICU_DEF(u_isalpha) ICU_DEF(u_islower) ICU_DEF(u_isupper) ICU_DEF(u_istitle) ICU_DEF(u_iscntrl) ICU_DEF(u_ispunct) ICU_DEF(u_isdigit) ICU_DEF(u_isspace) ICU_DEF(u_isblank) ICU_DEF(u_isprint) ICU_DEF(u_getIntPropertyValue) ICU_DEF(u_strFromUTF8) ICU_DEF(u_strToUTF8) ICU_DEF(u_strToLower) ICU_DEF(u_strToUpper) ICU_DEF(u_strToTitle) ICU_DEF(u_strlen) ICU_DEF(u_toupper) ICU_DEF(u_finit) ICU_DEF(u_fgets) ICU_DEF(u_fclose) ICU_DEF(u_feof) ICU_DEF(ucnv_open) ICU_DEF(ucnv_close) ICU_DEF(ucnv_convertEx) ICU_DEF(ucnv_getMaxCharSize) ICU_DEF(ucnv_getMinCharSize) ICU_DEF(ucnv_setToUCallBack) ICU_DEF(ucnv_getName) ICU_DEF(u_charType) fis-gtm-V7.0-005/sr_unix/gtm_icu_api.h0000755000032200000250000000410414342376330016474 0ustar librarygtc/**************************************************************** * * * Copyright 2006, 2008 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. * * * ****************************************************************/ #ifndef ICU_API_H #define ICU_API_H #define U_DISABLE_RENAMING 1 /* required by ICU to disable renaming */ #define u_isalpha (*u_isalpha_ptr) #define u_islower (*u_islower_ptr) #define u_isupper (*u_isupper_ptr) #define u_istitle (*u_istitle_ptr) #define u_iscntrl (*u_iscntrl_ptr) #define u_ispunct (*u_ispunct_ptr) #define u_isdigit (*u_isdigit_ptr) #define u_isspace (*u_isspace_ptr) #define u_isblank (*u_isblank_ptr) #define u_isprint (*u_isprint_ptr) #define u_getIntPropertyValue (*u_getIntPropertyValue_ptr) #define u_strFromUTF8 (*u_strFromUTF8_ptr) #define u_strToUTF8 (*u_strToUTF8_ptr) #define u_strToLower (*u_strToLower_ptr) #define u_strToUpper (*u_strToUpper_ptr) #define u_strToTitle (*u_strToTitle_ptr) #define u_strlen (*u_strlen_ptr) #define u_toupper (*u_toupper_ptr) #define u_finit (*u_finit_ptr) #define u_fgets (*u_fgets_ptr) #define u_fclose (*u_fclose_ptr) #define u_feof (*u_feof_ptr) #define ucnv_open (*ucnv_open_ptr) #define ucnv_close (*ucnv_close_ptr) #define ucnv_convertEx (*ucnv_convertEx_ptr) #define ucnv_getMaxCharSize (*ucnv_getMaxCharSize_ptr) #define ucnv_getMinCharSize (*ucnv_getMinCharSize_ptr) #define ucnv_setToUCallBack (*ucnv_setToUCallBack_ptr) #define ucnv_getName (*ucnv_getName_ptr) #define u_charType (*u_charType_ptr) #include #include #include #include LITREF UChar32 u32_line_term[]; void gtm_icu_init(void); void gtm_conv_init(void); GBLREF boolean_t is_gtm_chset_utf8; #define GTM_ICU_INIT_IF_NEEDED if (is_gtm_chset_utf8) gtm_icu_init(); #endif /* ICU_API_H */ fis-gtm-V7.0-005/sr_unix/gtm_image_exit.c0000644000032200000250000000142714342376330017173 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015 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_multi_thread.h" /* needed for below EXIT macro */ void gtm_image_exit(int status) { \ char *rname; \ \ if (!INSIDE_THREADED_CODE(rname)) \ exit(status); \ else \ GTM_PTHREAD_EXIT(status); \ } fis-gtm-V7.0-005/sr_unix/gtm_ipc.h0000644000032200000250000000446314342376330015643 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ /* gtm_ipc.h - interlude to system header file. */ #ifndef GTM_IPCH #define GTM_IPCH #include #include "gtm_fcntl.h" /* Needed for AIX's silly open to open64 translations (open used in JNLPOOL_CLEAR_FIELDS macro) */ #ifdef __MVS__ /* For shmget with __IPC_MEGA or _LP64 */ #define MEGA_BOUND (1024 * 1024) #endif #define FTOK gtm_ftok #define FTOK_OLD ftok #define JNLPOOL_CLEAR_FIELDS(JNLPOOL) \ { \ GBLREF int pool_init; \ \ assert(NULL != JNLPOOL); \ JNLPOOL->jnlpool_ctl = NULL; \ JNLPOOL->gtmsrc_lcl_array = NULL; \ JNLPOOL->gtmsource_local_array = NULL; \ JNLPOOL->jnldata_base = NULL; \ JNLPOOL->repl_inst_filehdr = NULL; \ JNLPOOL->jnlpool_dummy_reg->open = FALSE; \ JNLPOOL->gd_ptr = NULL; \ if (JNLPOOL->pool_init && (0 < pool_init)) \ pool_init--; \ JNLPOOL->pool_init = JNLPOOL->recv_pool = FALSE; \ } #define JNLPOOL_SHMDT(JNLPOOL, RC, SAVE_ERRNO) \ { \ jnlpool_ctl_ptr_t save_jnlpool_ctl; \ intrpt_state_t prev_intrpt_state; \ \ SAVE_ERRNO = 0; /* clear any left-over value */ \ assert(NULL != JNLPOOL); \ assert(NULL != JNLPOOL->jnlpool_ctl); \ assert((NULL == JNLPOOL->jnlpool_dummy_reg) \ || !REG2CSA(JNLPOOL->jnlpool_dummy_reg)->now_crit); \ DEFER_INTERRUPTS(INTRPT_IN_SHMDT, prev_intrpt_state); \ save_jnlpool_ctl = JNLPOOL->jnlpool_ctl; \ JNLPOOL->jnlpool_ctl = NULL; \ RC = SHMDT(save_jnlpool_ctl); \ SAVE_ERRNO = errno; \ JNLPOOL_CLEAR_FIELDS(JNLPOOL); \ ENABLE_INTERRUPTS(INTRPT_IN_SHMDT, prev_intrpt_state); \ } key_t gtm_ftok(const char *path, int id); #define IPC_REMOVED(ERRNO) ((EINVAL == ERRNO) || (EIDRM == ERRNO)) /* EIDRM is only on Linux */ #define SEM_REMOVED(ERRNO) IPC_REMOVED(ERRNO) #define SHM_REMOVED(ERRNO) IPC_REMOVED(ERRNO) #endif fis-gtm-V7.0-005/sr_unix/gtm_is_main_thread.c0000644000032200000250000000163014342376330020022 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016 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 "gtmxc_types.h" #ifdef GTM_PTHREAD # include "gtm_pthread.h" #endif #ifdef GTM_PTHREAD GBLREF pthread_t gtm_main_thread_id; GBLREF boolean_t gtm_main_thread_id_set; #endif int gtm_is_main_thread() { # ifdef GTM_PTHREAD if (!gtm_main_thread_id_set) return -1; if (pthread_equal(gtm_main_thread_id, pthread_self())) return 1; return 0; # else return -1; # endif } fis-gtm-V7.0-005/sr_unix/gtm_isanlp.c0000755000032200000250000000246514342376330016354 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "gtm_isanlp.h" /* gtm_isanlp returns 1 if the indicated file is associated with a line printer and 0 if it isn't. */ /* This is a stub version that always returns 0. */ /* gtm_isanlp is intended to be called whenever a tty-specific ioctl call fails; if gtm_isanlp returns true (1), then the device is assumed to be a line printer and the tty-specific ioctl failure will be ignored. So far, systems that don't allow tty-specific ioctl calls for line printer devices have some other way to determine whether the device is a line printer; those systems have specific gtm_isanlp functions. This version is for those Unix systems that do allow tty- specific ioctl calls for line printers, so this function returns 0 to ensure the ioctl error will not be ignored. */ int gtm_isanlp (int file_des) { return (0); } fis-gtm-V7.0-005/sr_unix/gtm_isanlp.h0000755000032200000250000000115014342376330016347 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GTM_ISANLP_H_INCLUDED #define GTM_ISANLP_H_INCLUDED int gtm_isanlp(int file_des); #endif fis-gtm-V7.0-005/sr_unix/gtm_logicals.h0000644000032200000250000001457414342376330016671 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "gtm_libaio.h" /* gtm_logicals.h - Environment variables used by GT.M. */ /* within each group, the entries are in alpha order of the third column */ /* Warning: A consideration is that reordering these values reorders the output in v53003/D9I10002703 as well */ /* -------------------------- Common to UNIX and VMS -------------------------- */ #define GTM_DIST_LOG "$gtm_dist" /* Database */ #define GTM_BLKUPGRADE_FLAG "$gtm_blkupgrade_flag" #define GTM_DBFILEXT_SYSLOG_DISABLE "$gtm_dbfilext_syslog_disable" #define GTM_ENV_XLATE "$gtm_env_translate" #define GTM_GDSCERT "$gtm_gdscert" #define GTM_GVDUPSETNOOP "$gtm_gvdupsetnoop" #define GTM_GBLDIR "$gtmgbldir" #define GTM_GVUNDEF_FATAL "$gtm_gvundef_fatal" #define GTM_POOLLIMIT "$gtm_poollimit" #define GTM_TP_ALLOCATION_CLUE "$gtm_tp_allocation_clue" #define GTM_TPNOTACIDTIME "$gtm_tpnotacidtime" #define GTM_TPRESTART_LOG_DELTA "$gtm_tprestart_log_delta" #define GTM_TPRESTART_LOG_FIRST "$gtm_tprestart_log_first" #define GTM_NONTPRESTART_LOG_DELTA "$gtm_nontprestart_log_delta" #define GTM_NONTPRESTART_LOG_FIRST "$gtm_nontprestart_log_first" #define GTM_ZMAXTPTIME "$gtm_zmaxtptime" /* GTM_DIRTREE_COLLHDR_ALWAYS "$gtm_dirtree_collhdr_always" dbg-only use in gvcst_put hence no #define for it or else the D9I10002703 subtest will need changes for this. */ /* White-box testing */ #define GTM_WHITE_BOX_TEST_CASE_COUNT "$gtm_white_box_test_case_count" #define GTM_WHITE_BOX_TEST_CASE_ENABLE "$gtm_white_box_test_case_enable" #define GTM_WHITE_BOX_TEST_CASE_NUMBER "$gtm_white_box_test_case_number" /* Indirection-cache */ #define GTM_MAX_INDRCACHE_COUNT "$gtm_max_indrcache_count" #define GTM_MAX_INDRCACHE_MEMORY "$gtm_max_indrcache_memory" /* MUPIP BACKUP */ #define GTM_BAK_TEMPDIR_LOG_NAME "$gtm_baktmpdir" #define GTM_BAK_TEMPDIR_LOG_NAME_UC "$GTM_BAKTMPDIR" /* Pattern match operator */ #define PAT_FILE "$gtm_pattern_file" #define PAT_TABLE "$gtm_pattern_table" /* Alternative Collation */ #define CT_PREFIX "$gtm_collate_" #define LCT_PREFIX "$gtm_local_collate" #define LCT_STDNULL "$gtm_lct_stdnull" /* GTM processing versus M standard */ /* (see gtm_local_collate above) */ #define GTM_STDXKILL "$gtm_stdxkill" /* Miscellaneous */ #define ZCOMPILE "$gtmcompile" #define GTM_DEBUG_LEVEL_ENVLOG "$gtmdbglvl" #define GTM_ZROUTINES "$gtmroutines" #define GTM_BOOLEAN "$gtm_boolean" #define DISABLE_ALIGN_STRINGS "$gtm_disable_alignstr" #define GTM_HUGETLB_SHM "$gtm_hugetlb_shm" #define GTM_MAX_SOCKETS "$gtm_max_sockets" #define GTM_MEMORY_RESERVE "$gtm_memory_reserve" #define GTM_NOFFLF "$gtm_nofflf" #define GTM_NOUNDEF "$gtm_noundef" #define GTM_PINSHM "$gtm_pinshm" #define GTM_PRINCIPAL "$gtm_principal" #define GTM_PROMPT "$gtm_prompt" #define GTM_SIDE_EFFECT "$gtm_side_effects" #define GTM_SOCKET_KEEPALIVE_IDLE "$gtm_socket_keepalive_idle" #define SYSID "$gtm_sysid" #define GTM_MPROF_TESTING "$gtm_trace_gbl_name" #define GTM_TRACE_GROUPS "$gtm_trace_groups" #define GTM_TRACE_TABLE_SIZE "$gtm_trace_table_size" #define ZDATE_FORM "$gtm_zdate_form" #define GTM_ZINTERRUPT "$gtm_zinterrupt" #define GTM_ZQUIT_ANYWAY "$gtm_zquit_anyway" #define ZTRAP_FORM "$gtm_ztrap_form" #define ZTRAP_NEW "$gtm_ztrap_new" #define ZYERROR "$gtm_zyerror" #define GTM_ZSTEP "$gtm_zstep" /* -------------------------- UNIX only -------------------------- */ /* Database */ #define GTM_TMP_ENV "$gtm_tmp" #define GTM_TRIGGER_ETRAP "$gtm_trigger_etrap" #define GTM_SNAPTMPDIR "$gtm_snaptmpdir" #define GTM_DB_STARTUP_MAX_WAIT "$gtm_db_startup_max_wait" #ifdef USE_LIBAIO #define GTM_AIO_NR_EVENTS "$gtm_aio_nr_events" #endif #define GTM_DB_CREATE_VER "$gtm_db_create_ver" /* Replication */ #define GTM_REPL_INSTANCE "$gtm_repl_instance" #define GTM_REPL_INSTNAME "$gtm_repl_instname" #define GTM_REPL_INSTSECONDARY "$gtm_repl_instsecondary" #define GTM_ZLIB_CMP_LEVEL "$gtm_zlib_cmp_level" #define GTM_JNL_RELEASE_TIMEOUT "$gtm_jnl_release_timeout" #define GTM_CUSTOM_ERRORS "$gtm_custom_errors" /* UTF8 */ #define GTM_CHSET_ENV "$gtm_chset" #ifdef __MVS__ #define GTM_CHSET_LOCALE_ENV "$gtm_chset_locale" #define GTM_TAG_UTF8_AS_ASCII "$gtm_dont_tag_UTF8_ASCII" #endif #define GTM_PATNUMERIC_ENV "$gtm_patnumeric" #define GTM_BADCHAR_ENV "$gtm_badchar" #define GTM_ICU_VERSION "$gtm_icu_version" /* [Auto]Relink related */ #define GTM_LINK "$gtm_link" #define GTM_LINKTMPDIR "$gtm_linktmpdir" #define GTM_AUTORELINK_SHM "$gtm_autorelink_shm" #define GTM_AUTORELINK_KEEPRTN "$gtm_autorelink_keeprtn" /* do not let go of objects in rtnobj shm */ #define GTM_AUTORELINK_CTLMAX "$gtm_autorelink_ctlmax" /* Global shared stats related */ #define GTM_STATSHARE "$gtm_statshare" #define GTM_STATSDIR "$gtm_statsdir" #define GTM_JNL_STATSDB "gtm_jnl_statsdb" /* Only seen/used in debug mode */ /* Miscellaneous */ #define GTM_ERROR_ON_JNL_FILE_LOST "$gtm_error_on_jnl_file_lost" #define GTM_ETRAP "$gtm_etrap" #define GTM_LOG_ENV "$gtm_log" #define GTM_LVNULLSUBS "$gtm_lvnullsubs" #define GTM_NOCENABLE "$gtm_nocenable" #define GTM_NON_BLOCKED_WRITE_RETRIES "$gtm_non_blocked_write_retries" #define GTM_PRINCIPAL_EDITING "$gtm_principal_editing" #define GTM_PROCSTUCKEXEC "$gtm_procstuckexec" #define GTM_QUIET_HALT "$gtm_quiet_halt" #define GTM_EXTRACT_NOCOL "$gtm_extract_nocol" #define GTMDBGFLAGS "$gtmdbgflags" #define GTMDBGFLAGS_FREQ "$gtmdbgflags_freq" #define GTM_MALLOC_LIMIT "$gtm_malloc_limit" #define GTM_MSTACK_SIZE "$gtm_mstack_size" #define GTM_MSTACK_CRIT_THRESH "$gtm_mstack_crit_threshold" #define GTM_IPV4_ONLY "$gtm_ipv4_only" #define GTM_DMTERM "$gtm_dmterm" #define GTM_MUPJNL_PARALLEL "$gtm_mupjnl_parallel" #define GTM_LOCALE "$gtm_locale" #define GTM_UTFCGR_STRINGS "$gtm_utfcgr_strings" #define GTM_UTFCGR_STRING_GROUPS "$gtm_utfcgr_string_groups" #define GTM_STRPLLIM "$gtm_string_pool_limit" #define GTM_HUPENABLE "$gtm_hupenable" fis-gtm-V7.0-005/sr_unix/gtm_main.h0000755000032200000250000000233714342376330016015 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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. * * * ****************************************************************/ #ifndef GTM_MAINH #define GTM_MAINH #ifdef __osf__ /* On OSF/1 (Digital Unix), pointers are 64 bits wide; the only exception to this is C programs for which one may * specify compiler and link editor options in order to use (and allocate) 32-bit pointers. However, since C is * the only exception and, in particular because the operating system does not support such an exception, the argv * array passed to the main program is an array of 64-bit pointers. Thus the C program needs to declare argv[] * as an array of 64-bit pointers and needs to do the same for any pointer it sets to an element of argv[]. */ #pragma pointer_size (save) #pragma pointer_size (long) #endif int gtm_main(int argc, char **argv, char **envp); #ifdef __osf__ #pragma pointer_size (restore) #endif #endif fis-gtm-V7.0-005/sr_unix/gtm_multi_proc.c0000644000032200000250000003667314342376330017250 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2022 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_string.h" #include "gtm_unistd.h" #include "gtm_stdlib.h" #include #include #include #include #include "error.h" #include "gtm_ipc.h" #include "gtm_multi_proc.h" #include "do_shmat.h" #include "ipcrmid.h" #include "gtmmsg.h" #include "iosp.h" #include "fork_init.h" #include "getjobnum.h" #include "eintr_wrappers.h" #include "interlock.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "gdscc.h" #include "filestruct.h" #include "gdskill.h" #include "buddy_list.h" #include "jnl.h" #include "tp.h" #include "repl_msg.h" #include "gtmsource.h" #include "mutex.h" #ifdef DEBUG #include "is_proc_alive.h" #endif #ifdef DEBUG /* Below are needed in case MUR_DEBUG is defined */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "hashtab_mname.h" /* needed for muprec.h */ #include "muprec.h" #endif GBLREF VSIG_ATOMIC_T forced_exit; GBLREF uint4 process_id; GBLREF boolean_t skip_exit_handler; GBLREF uint4 mutex_per_process_init_pid; error_def(ERR_FORCEDHALT); error_def(ERR_SYSCALL); error_def(ERR_TEXT); /* This function invokes "fnptr" with the argument "&parm_array[i]" where "i" ranges from 0 thru "ntasks - 1". * At most "max_procs" processes will run parallely at any given point in time with two special exceptions. * max_procs = 0 implies one process runs parallely per region. * max_procs = 1 implies no process-parallelization are used. * * Returns 0 (SS_NORMAL) if "ntasks" tasks were successfully created and completed (with or without multiple processes). * "ret_array[]" contains individual task invocation exit status in this case. * Returns non-zero otherwise. In this case, it waits for all/any created processes to die down before returning. * Also, "ret_array[]" is filled with return status of each "fnptr" task invocation as appropriate. * Caller needs to look at the function return value and "ret_array[]" and issue appropriate error messages. * Note: Although ret_array[i] is of type "void *", if the function "fnptr" returns a pointer, it has to point * to memory that is visible to both the parent and the forked-off children (e.g. cannot point to heap * which is child-specific memory). Safest would be for "fnptr" to return a non-pointer return type. * * Additionally, one can specify * --> "extra_shm_size" to indicate caller-specific extra space to allocate in the shared memory segment that * "gtm_multi_proc" anyways creates. * --> "init_fnptr" to indicate a caller-specific initialization function to invoke after shared memory creation. * Note: This function is NOT called in case no parallel processes are started. * --> "finish_fnptr" to indicate a caller-specific finish function that is invoked once all parallel processes return * and before "gtm_multi_proc" returns to caller. Note that this invocation happens even if no parallel * processes are invoked internally. As long as "init_fnptr" was invoked, "finish_fnptr" will be invoked. */ int gtm_multi_proc(gtm_multi_proc_fnptr_t fnptr, int ntasks, int max_procs, void **ret_array, void *parm_array, int parmElemSize, size_t extra_shm_size, gtm_multi_proc_fnptr_t init_fnptr, gtm_multi_proc_fnptr_t finish_fnptr) { int final_ret, rc, rc2, tasknum, shmid, save_errno; char errstr[256]; size_t shm_size; pid_t child_pid; void **ret_ptr; uchar_ptr_t parm_ptr; multi_proc_shm_hdr_t *mp_hdr; /* Pointer to "multi_proc_shm_hdr_t" structure in shared memory */ intrpt_state_t prev_intrpt_state; assert(!multi_proc_in_use); if (!max_procs || (max_procs > ntasks)) max_procs = ntasks; final_ret = 0; ret_ptr = &ret_array[0]; memset(ret_ptr, 0, SIZEOF(void *) * ntasks); /* initialize return status to SS_NORMAL/0 */ parm_ptr = (uchar_ptr_t)parm_array; if (1 == max_procs) { /* Simplest case. No parallelization. Finish and return */ for (tasknum = 0; tasknum < ntasks; tasknum++, parm_ptr += parmElemSize, ret_ptr++) { if (!final_ret) { rc = (INTPTR_T)(*fnptr)(parm_ptr); if (rc) final_ret = rc; } else rc = 0; *ret_ptr = (void *)(INTPTR_T)rc; } if (NULL != finish_fnptr) { rc = (INTPTR_T)(*finish_fnptr)(NULL); if (rc) { assert(FALSE); if (!final_ret) final_ret = rc; } } return final_ret; } if (MULTI_PROC_MAX_PROCS <= max_procs) { SNPRINTF(errstr, SIZEOF(errstr), "gtm_multi_proc : Cannot fork() more than %d processes : %d processes requested", MULTI_PROC_MAX_PROCS - 1, max_procs); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) MAKE_MSG_TYPE(ERR_TEXT, ERROR), 2, LEN_AND_STR(errstr)); return -1; } shm_size = SIZEOF(multi_proc_shm_hdr_t); /* Allocate space for return array in shared memory. This will be later copied back to "ret_array" for caller */ shm_size += (SIZEOF(void *) * ntasks); shm_size += extra_shm_size; shmid = gtm_shmget(IPC_PRIVATE, shm_size, 0600 | IPC_CREAT, TRUE); if (-1 == shmid) { save_errno = errno; SNPRINTF(errstr, SIZEOF(errstr), "shmget() : shmsize=0x%llx", shm_size); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_STR(errstr), CALLFROM, save_errno); return -1; } multi_proc_shm_hdr = (multi_proc_shm_hdr_t *)do_shmat(shmid, 0, 0); if (-1 == (sm_long_t)(multi_proc_shm_hdr)) { save_errno = errno; SNPRINTF(errstr, SIZEOF(errstr), "shmat() : shmid=%d shmsize=0x%llx", shmid, shm_size); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_STR(errstr), CALLFROM, save_errno); return -1; } /* Initialize shm hdr */ mp_hdr = multi_proc_shm_hdr; memset(mp_hdr, 0, shm_size); mp_hdr->shmid = shmid; mp_hdr->parent_pid = process_id; mp_hdr->fnptr = fnptr; mp_hdr->pvt_ret_array = ret_array; mp_hdr->shm_ret_array = (void **)(mp_hdr + 1); mp_hdr->parm_array = parm_array; mp_hdr->ntasks = ntasks; mp_hdr->max_procs = max_procs; mp_hdr->parmElemSize = parmElemSize; /* Defer interrupts (SIG-15 etc.) while processes are being forked off. Note that the interrupt will invoke * "generic_signal_handler" and cause SET_FORCED_MULTI_PROC_EXIT to be invoked (through the SET_FORCED_EXIT_STATE * macro) which will cause the forked off children to die at a logical point as soon as possible. So we do handle * the external signal even though it is slightly deferred. */ DEFER_INTERRUPTS(INTRPT_IN_GTM_MULTI_PROC, prev_intrpt_state); multi_proc_in_use = TRUE; assert(NULL == multi_proc_key); rc = (INTPTR_T)(*init_fnptr)((uchar_ptr_t)parm_array); /* Invoke caller-specific initialization function first */ if (0 == rc) { /* Fork off all processes next */ for (tasknum = 0; tasknum < max_procs; tasknum++) { if (forced_exit) { /* We got an external signal that wants us to terminate as soon as possible. */ SET_FORCED_MULTI_PROC_EXIT; /* signal any forked off children to finish at a logical point */ gtm_multi_proc_finish(finish_fnptr); /* wait for forked off pids to finish */ multi_proc_in_use = FALSE; ENABLE_INTERRUPTS(INTRPT_IN_GTM_MULTI_PROC, prev_intrpt_state); return -1; } FORK(child_pid); if (-1 == child_pid) { save_errno = errno; SNPRINTF(errstr, SIZEOF(errstr), "fork() : tasknum=%d out of %d total processes", tasknum, max_procs); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_STR(errstr), CALLFROM, save_errno); SET_FORCED_MULTI_PROC_EXIT; /* signal any forked off children to finish at a logical point */ gtm_multi_proc_finish(finish_fnptr); /* wait for forked off pids to finish */ multi_proc_in_use = FALSE; ENABLE_INTERRUPTS(INTRPT_IN_GTM_MULTI_PROC, prev_intrpt_state); multi_proc_in_use = TRUE; return -1; } if (0 == child_pid) { /* The child process should operate as a regular process so re-enable interrupts. * But before that, set "process_id" to a value different from the parent * as the ENABLE_INTERRUPTS macro could end up calling * deferred_signal_handler -> forced_exit_err_display -> gtm_putmsg_csa -> grab_latch * and grab_latch would fail an assert otherwise. */ getjobnum(); /* set "process_id" to a value different from parent */ /* Skip exit handler, as otherwise we would reduce reference counts in database * shared memory etc. for each forked off process when they go to gds_rundown when * actually they did not do any db_init (they inherited the db from the parent). * Do it here instead of in the helper in case enabling interrupts causes us to exit. */ skip_exit_handler = TRUE; DEBUG_ONLY(multi_proc_key_exception = TRUE); /* Allow error messages without a key */ ENABLE_INTERRUPTS(INTRPT_IN_GTM_MULTI_PROC, prev_intrpt_state); DEBUG_ONLY(multi_proc_key_exception = FALSE); gtm_multi_proc_helper(); /* Note: does not return */ } mp_hdr->pid[tasknum] = child_pid; mp_hdr->procs_created = tasknum + 1; } } rc2 = gtm_multi_proc_finish(finish_fnptr); /* wait for all forked off processes to finish */ final_ret = rc ? rc : rc2; multi_proc_in_use = FALSE; ENABLE_INTERRUPTS(INTRPT_IN_GTM_MULTI_PROC, prev_intrpt_state); return final_ret; } void gtm_multi_proc_helper(void) { int nexttask, ntasks, rc; multi_proc_shm_hdr_t *mp_hdr; /* Pointer to "multi_proc_shm_hdr_t" structure in shared memory */ void *parm_array; void **ret_array, **ret_ptr; int parmElemSize; uchar_ptr_t parm_ptr; gtm_multi_proc_fnptr_t fnptr; boolean_t release_latch; # ifdef MUR_DEBUG fprintf(stderr, "pid = %d : Started\n", process_id); # endif /* Do process-level reinitialization of a few things (see gtmrecv.c, gtmsource.c for example usage) */ /* Re-initialize mutex socket, memory semaphore etc. with child's pid if already done by parent */ if (mutex_per_process_init_pid) { assert(mutex_per_process_init_pid != process_id); mutex_per_process_init(); } /* process-level reinitialization is done */ mp_hdr = multi_proc_shm_hdr; ntasks = mp_hdr->ntasks; parm_array = mp_hdr->parm_array; parmElemSize = mp_hdr->parmElemSize; ret_array = mp_hdr->shm_ret_array; fnptr = mp_hdr->fnptr; rc = 0; while (TRUE) { GRAB_MULTI_PROC_LATCH_IF_NEEDED(release_latch); assert(release_latch); nexttask = mp_hdr->next_task; if (nexttask < ntasks) mp_hdr->next_task = nexttask + 1; REL_MULTI_PROC_LATCH_IF_NEEDED(release_latch); if (nexttask >= ntasks) break; parm_ptr = (uchar_ptr_t)parm_array + (parmElemSize * nexttask); ret_ptr = &ret_array[nexttask]; assert(0 == *ret_ptr); /* should have been initialized at entry into "gtm_multi_proc" */ if (IS_FORCED_MULTI_PROC_EXIT(mp_hdr)) { /* Either parent or sibling parallel process has encountered a signal/error. So stop at logical point */ rc = ERR_FORCEDHALT; *ret_ptr = (void *)(INTPTR_T)rc; break; } rc = (INTPTR_T)(*fnptr)(parm_ptr); *ret_ptr = (void *)(INTPTR_T)rc; if (0 != rc) { /* Stop the already running threads */ SET_FORCED_MULTI_PROC_EXIT; /* signal any forked off children to finish at a logical point */ break; } nexttask++; } # ifdef MUR_DEBUG fprintf(stderr, "pid = %d : Completed\n", process_id); # endif EXIT(rc); } int gtm_multi_proc_finish(gtm_multi_proc_fnptr_t finish_fnptr) { int max_procs, tasknum, num_pids_to_wait, num_pids_waited, save_errno; int shmid; int stat; /* child exit status */ # ifdef _BSD union wait wait_stat; # else int wait_stat; # endif pid_t ret_pid; /* return value from waitpid */ int ret2, final_ret; char errstr[256]; pid_t pid; multi_proc_shm_hdr_t *mp_hdr; /* Pointer to "multi_proc_shm_hdr_t" structure in shared memory */ assert(multi_proc_in_use); mp_hdr = multi_proc_shm_hdr; assert(process_id == mp_hdr->parent_pid); /* assert this function is not invoked by child processes */ max_procs = mp_hdr->procs_created; final_ret = 0; num_pids_to_wait = 0; for (tasknum = 0; tasknum < max_procs; tasknum++) { pid = mp_hdr->pid[tasknum]; if (0 == pid) continue; num_pids_to_wait++; } assert(num_pids_to_wait == max_procs); /* It is possible the child pids terminate in an arbitrary order. In that case, we don't want to be * stuck doing a WAITPID of the first pid when the second pid has finished since it is possible the * second pid terminated abnormally (e.g. holding a latch) and until we do the WAITPID for that pid * it would be a defunct pid and "is_proc_alive" calls from the first pid will return the second pid * as alive (which is incorrect) potentially causing the first pid to hang eternally waiting for the * same latch. Therefore do WAITPID for an arbitrary child. */ for (num_pids_waited = 0; num_pids_waited < num_pids_to_wait; ) { WAITPID((pid_t)-1, &stat, 0, ret_pid); if (-1 == ret_pid) { assert(FALSE); save_errno = errno; SNPRINTF(errstr, SIZEOF(errstr), "waitpid()"); /* BYPASSOK("waitpid") */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_STR(errstr), CALLFROM, save_errno); /* Note: In this case we do not copy mp_hdr->shm_ret_array to mp_hdr->pvt_ret_array because we * do not know what state the latter is in (due to the abrupt error return from waitpid). */ return -1; } for (tasknum = 0; tasknum < max_procs; tasknum++) { pid = mp_hdr->pid[tasknum]; if (0 == pid) continue; if (pid == ret_pid) { mp_hdr->pid[tasknum] = 0; /* so we do not wait again for this pid */ mp_hdr->orig_pid[tasknum] = ret_pid; /* for debugging purposes */ break; } } assert(FALSE == is_proc_alive(ret_pid, 0)); if (tasknum == max_procs) { /* This is a child pid that we did not fork off in "gtm_multi_proc". * Skip this and continue waiting for the child pids we did fork off. */ continue; } mp_hdr->wait_stat[tasknum] = stat; /* for debugging purposes */ # ifdef _BSD assert(SIZEOF(wait_stat) == SIZEOF(int4)); wait_stat.w_status = stat; # else wait_stat = stat; # endif if (WIFEXITED(wait_stat)) ret2 = WEXITSTATUS(wait_stat); else if (WIFSIGNALED(wait_stat)) ret2 = WTERMSIG(wait_stat); else ret2 = 0; if (ret2 && !final_ret) { final_ret = ret2; SET_FORCED_MULTI_PROC_EXIT; /* Signal any currently-running forked off children * to finish at a logical point. */ } num_pids_waited++; } mp_hdr->wait_done = TRUE; # ifdef DEBUG for (tasknum = 0; tasknum < max_procs; tasknum++) { pid = mp_hdr->pid[tasknum]; assert(0 == pid); } # endif /* Copy return status of each task from shared memory to private memory (needed by caller of "gtm_multi_proc") */ memcpy(mp_hdr->pvt_ret_array, mp_hdr->shm_ret_array, (SIZEOF(void *) * mp_hdr->ntasks)); if (NULL != finish_fnptr) { ret2 = (INTPTR_T)(*finish_fnptr)(NULL); if (ret2 && !final_ret) final_ret = ret2; } shmid = mp_hdr->shmid; ret2 = shm_rmid(shmid); if (0 != ret2) { assert(FALSE); save_errno = errno; SNPRINTF(errstr, SIZEOF(errstr), "shm_rmid() : shmid=%d", shmid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_STR(errstr), CALLFROM, save_errno); if (!final_ret) { final_ret = ret2; assert(FALSE); } } return final_ret; } fis-gtm-V7.0-005/sr_unix/gtm_multi_proc.h0000644000032200000250000001063514342376330017243 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2020 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. * * * ****************************************************************/ #ifndef GTM_MULTI_PROC_H #define GTM_MULTI_PROC_H typedef void *(*gtm_multi_proc_fnptr_t)(void *parm); int gtm_multi_proc(gtm_multi_proc_fnptr_t fnptr, int ntasks, int max_procs, void **ret_array, void *parm_array, int parmElemSize, size_t extra_shm_size, gtm_multi_proc_fnptr_t init_fnptr, gtm_multi_proc_fnptr_t finish_fnptr); void gtm_multi_proc_helper(void); int gtm_multi_proc_finish(gtm_multi_proc_fnptr_t finish_fnptr); GBLREF boolean_t multi_proc_in_use; /* TRUE => parallel processes active ("gtm_multi_proc"). False otherwise */ GBLREF unsigned char *multi_proc_key; /* NULL for parent process; Non-NULL for forked off child processes */ #ifdef DEBUG GBLREF boolean_t multi_proc_key_exception; #endif error_def(ERR_MULTIPROCLATCH); \ #define MULTI_PROC_MAX_PROCS 1000 /* We expect max # of tasks to execute to be in the hundreds, not thousands */ #define MULTI_PROC_LATCH_TIMEOUT_SEC (4 * 60) /* Define latch timeout as being 4 mins */ #define MULTI_PROC_ONE_PER_REG 0 /* One process runs parallely per region */ #define MULTI_PROC_NO_PARALLEL 1 /* No process-parallelization are used */ #define GRAB_MULTI_PROC_LATCH_IF_NEEDED(RELEASE_LATCH) \ { \ GBLREF uint4 process_id; \ \ if (multi_proc_in_use) \ { \ RELEASE_LATCH = FALSE; \ if (process_id != multi_proc_shm_hdr->multi_proc_latch.u.parts.latch_pid) \ { \ if (!grab_latch(&multi_proc_shm_hdr->multi_proc_latch, MULTI_PROC_LATCH_TIMEOUT_SEC, NOT_APPLICABLE,NULL)) \ { \ assert(FALSE); \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) \ ERR_MULTIPROCLATCH, 2, LEN_AND_LIT("GRAB_MULTI_PROC_LATCH_IF_NEEDED")); \ } \ RELEASE_LATCH = TRUE; \ } \ } \ } #define REL_MULTI_PROC_LATCH_IF_NEEDED(RELEASE_LATCH) \ { \ GBLREF uint4 process_id; \ \ if (multi_proc_in_use) \ { \ assert(process_id == multi_proc_shm_hdr->multi_proc_latch.u.parts.latch_pid); \ if (RELEASE_LATCH) \ rel_latch(&multi_proc_shm_hdr->multi_proc_latch); \ } \ } #define SET_FORCED_MULTI_PROC_EXIT \ { \ if (multi_proc_in_use) \ multi_proc_shm_hdr->forced_multi_proc_exit = TRUE; \ } #define IS_FORCED_MULTI_PROC_EXIT(mp_hdr) (mp_hdr->forced_multi_proc_exit) /* Structure in shared memory used for inter-process communications amongst the multiple processes forked off by "gtm_multi_proc" */ typedef struct { global_latch_t multi_proc_latch; /* latch used to obtain critical section by multiple processes */ boolean_t forced_multi_proc_exit; /* flag to signal multiple processes to exit at a logical point */ boolean_t wait_done; /* WAITPID of all children processes completed by parent */ int shmid; /* id of the shared memory segment created by "gtm_multi_proc" */ int procs_created; /* # of processes that have been forked off by "gtm_multi_proc" */ int parent_pid; /* Store pid of parent process */ int next_task; /* next task available to be picked up by a free process */ pid_t pid[MULTI_PROC_MAX_PROCS]; /* Store pid of forked off processes. Cleared when pid dies */ pid_t orig_pid[MULTI_PROC_MAX_PROCS]; /* Copy of pid of forked off processes. For debugging purposes. */ int4 wait_stat[MULTI_PROC_MAX_PROCS];/* Status of WAITPID call. For debugging purposes. */ /* Below are parameters from "gtm_multi_proc" invocation */ gtm_multi_proc_fnptr_t fnptr; void **pvt_ret_array; /* array of return values passed in by caller (points to private memory) */ void **shm_ret_array; /* array of return values (points to shared memory) */ void *parm_array; int ntasks; int max_procs; int parmElemSize; } multi_proc_shm_hdr_t; GBLREF multi_proc_shm_hdr_t *multi_proc_shm_hdr; /* Pointer to "multi_proc_shm_hdr_t" structure in shared memory */ #endif fis-gtm-V7.0-005/sr_unix/gtm_multi_thread.c0000644000032200000250000002166614342376330017550 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2021 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_signal.h" /* for SIGPROCMASK */ #include "gtm_string.h" #include "gtm_pthread.h" #include #include "gtm_multi_thread.h" #include "iosp.h" /* for SS_NORMAL */ #include "have_crit.h" /* for DEFERRED_EXIT_HANDLING_CHECK */ #include "gtmimagename.h" #ifdef DEBUG #include "wbox_test_init.h" #endif #include "gtmmsg.h" /* for gtm_putmsg_csa prototype */ GBLREF pthread_t gtm_main_thread_id; GBLREF boolean_t gtm_main_thread_id_set; GBLREF boolean_t multi_thread_in_use; /* TRUE => threads are in use. FALSE => not in use */ GBLREF boolean_t thread_mutex_initialized; /* TRUE => "thread_mutex" variable is initialized */ GBLREF pthread_mutex_t thread_mutex; /* mutex structure used to ensure serialization amongst threads */ GBLREF boolean_t blocksig_initialized; GBLREF sigset_t block_sigsent; GBLREF int next_task_index; /* "next" task index waiting for a thread to be assigned */ error_def(ERR_SYSCALL); /* This function invokes "fnptr" with the argument "&parm_array[i]" where "i" ranges from 0 thru "ntasks - 1". * At most "max_threads" threads will run at any given point in time with two special exceptions. * max_threads = 0 implies one thread runs per region. * max_threads = 1 implies no threads are used. * If threads are used, this function uses pthread_t structure from "thr_array[i]" to create the needed threads. * Elements of "parm_array[]" are specific to the thread, so access to them does not need to be protected by a mutex, * (unless they contain pointers to shared data, in which case some protection may be necessary.) * On platforms where GTM_PTHREAD is not defined (currently HPUX IA64), this function does not use pthreads but * instead serially executes the function "fnptr" for each of the "parm_array[i]" parameters available. * Effectively assumes "max_threads == 1" even if set to a different value at function entry. * * Returns 0 (SS_NORMAL) if "ntasks" tasks were successfully created and completed (with or without threads). * "ret_array[]" contains individual thread exit status in this case. * Returns non-zero otherwise. In this case, it waits for all/any created threads to die down before returning. * Also, "ret_array[]" is filled with return status of each task invocation as appropriate. * Caller needs to look at the function return value and "ret_array[]" and issue appropriate error messages. */ int gtm_multi_thread(gtm_pthread_fnptr_t fnptr, int ntasks, int max_threads, pthread_t *thr_array, void **ret_array, void *parm_array, int parmElemSize) { int final_ret, rc, rc1, error_line; void **ret_ptr, *ret; pthread_t *thr_ptr, *thr_start, *thr_top; uchar_ptr_t parm_ptr; pthread_attr_t attr; sigset_t savemask; # ifdef GTM_PTHREAD thread_parm_t tparm; # endif assert(!multi_thread_in_use); assert(0 < ntasks); # ifdef GTM_PTHREAD if (!max_threads || (max_threads > ntasks)) max_threads = ntasks; # else max_threads = 1; /* do not use threads on thread-unsupported platform */ # endif thr_start = &thr_array[0]; thr_top = thr_start + ntasks; parm_ptr = (uchar_ptr_t)parm_array; ret_ptr = &ret_array[0]; memset(&ret_array[0], 0, SIZEOF(void *) * ntasks); /* initialize return status to SS_NORMAL/0 */ final_ret = 0; if (1 == max_threads) { /* Simplest case. No thread usage. Finish and return */ assert(0 == SS_NORMAL); for (thr_ptr = &thr_array[0]; thr_ptr < thr_top; thr_ptr++, parm_ptr += parmElemSize, ret_ptr++) { if (!final_ret) { rc1 = (INTPTR_T)(*fnptr)(parm_ptr); if (rc1) final_ret = rc1; } else rc1 = 0; *ret_ptr = (void *)(INTPTR_T)rc1; } return final_ret; } # ifdef GTM_PTHREAD /* Initialize thread-mutex variables if not already done */ if (!thread_mutex_initialized) { rc = pthread_mutex_init(&thread_mutex, NULL); if (rc) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("pthread_mutex_init()"), CALLFROM, rc); thread_mutex_initialized = TRUE; } /* Initialize and set thread-is-joinable attribute */ rc = pthread_attr_init(&attr); if (rc) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("pthread_attr_init()"), CALLFROM, rc); rc = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); if (rc) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("pthread_attr_setdetachstate()"), CALLFROM, rc); RESET_FORCED_THREAD_EXIT; /* reset "forced_thread_exit" from a previous call to "gtm_multi_thread" */ multi_thread_in_use = TRUE; /* Temporarily block external signals. That way the threads we create inherit a signal-mask that has those * signals blocked. This way any SIG-15 (for example) that gets sent while threads are running gets sent * to the current process and not any of the threads that it is about to create. * In addition, the pthread_* functions are not async-signal-safe so it is better to block those signals * when we use those functions below. */ assert(blocksig_initialized); SIGPROCMASK(SIG_BLOCK, &block_sigsent, &savemask, rc); DEBUG_ONLY(error_line = 0;) tparm.ntasks = ntasks; tparm.fnptr = fnptr; tparm.ret_array = ret_array; tparm.parm_array = parm_array; tparm.parmElemSize = parmElemSize; thr_top = thr_start + max_threads; next_task_index = 0; for (thr_ptr = thr_start; thr_ptr < thr_top; thr_ptr++) { rc1 = pthread_create(thr_ptr, &attr, (gtm_pthread_fnptr_t)>m_multi_thread_helper, (void *)&tparm); if (rc1) { /* Thread creation failed */ DEBUG_ONLY(if (!error_line) error_line = __LINE__); SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc); if ((0 != rc1) && !IS_GTM_IMAGE) /* Display error if mupip/dse etc. but not for mumps */ { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("pthread_create()"), CALLFROM, rc1); } final_ret = rc1; /* Stop the already started threads */ SET_FORCED_THREAD_EXIT; /* this signals spawned-off threads to stop execution at a logical point */ break; } } if (!rc1) SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc); /* Wait for started threads to complete */ for ( ; thr_start < thr_ptr; thr_start++) { rc1 = pthread_join(*thr_start, &ret); if (rc1) { DEBUG_ONLY(if (!error_line) error_line = __LINE__); if (!final_ret) final_ret = rc1; } else { ret; /* "ret" would have been set by "pthread_join" */ if (ret && !final_ret) { DEBUG_ONLY(if (!error_line) error_line = __LINE__;) final_ret = (INTPTR_T)ret; } } } multi_thread_in_use = FALSE; DEFERRED_EXIT_HANDLING_CHECK; /* Now that all threads have terminated, check for need of deferred signal/exit handling */ rc = pthread_attr_destroy(&attr); /* Free attribute */ if (rc) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("pthread_attr_destroy()"), CALLFROM, rc); /* Now that there are no thread usages in this process, ideally we should be doing the following. * pthread_mutex_destroy(&thread_mutex); * But it is possible the process uses threads again (e.g. journal rollback uses threads in various stages of the process). * We dont want to be initializing and destroying the mutex everytime. So we avoid this step. */ # endif return final_ret; } #ifdef GTM_PTHREAD int gtm_multi_thread_helper(thread_parm_t *tparm) { boolean_t was_holder; gtm_pthread_fnptr_t fnptr; int ntasks, nexttask; int parmElemSize, rc1; pthread_t *thr_array; uchar_ptr_t parm_ptr; void **ret_array; void *parm_array; void **ret_ptr; ntasks = tparm->ntasks; fnptr = tparm->fnptr; ret_array = tparm->ret_array; parm_array = tparm->parm_array; parmElemSize = tparm->parmElemSize; rc1 = SS_NORMAL; while (TRUE) { PTHREAD_MUTEX_LOCK_IF_NEEDED(was_holder); assert(!was_holder); nexttask = next_task_index; if (nexttask < ntasks) next_task_index++; PTHREAD_MUTEX_UNLOCK_IF_NEEDED(was_holder); if (nexttask >= ntasks) break; parm_ptr = (uchar_ptr_t)parm_array + (parmElemSize * nexttask); ret_ptr = &ret_array[nexttask]; /* The below invocation of *fnptr() can exit this thread without returning back to us * (e.g. if PTHREAD_EXIT_IF_FORCED_EXIT is invoked etc.). In that case, we want the return * value to reflect the correct value. Initialize accordingly. */ *ret_ptr = PTHREAD_CANCELED; rc1 = (INTPTR_T)(*fnptr)(parm_ptr); *ret_ptr = (void *)(INTPTR_T)rc1; if (SS_NORMAL != rc1) { /* Stop the already running threads */ SET_FORCED_THREAD_EXIT; /* this signals spawned-off threads to stop execution at a logical point */ break; } nexttask++; } return rc1; } #endif fis-gtm-V7.0-005/sr_unix/gtm_multi_thread.h0000644000032200000250000002236214342376330017547 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2018 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. * * * ****************************************************************/ #ifndef GTM_MULTI_THREAD_H #define GTM_MULTI_THREAD_H #include "gtm_pthread.h" typedef void *(*gtm_pthread_fnptr_t)(void *parm); int gtm_multi_thread(gtm_pthread_fnptr_t fnptr, int ntasks, int max_threads, pthread_t *thr_array, void **ret_array, void *parmarray, int parmElemSize); GBLREF boolean_t multi_thread_in_use; /* TRUE => threads are in use. FALSE => not in use */ GBLREF boolean_t thread_mutex_initialized; /* TRUE => "thread_mutex" variable is initialized */ GBLREF pthread_mutex_t thread_mutex; /* mutex structure used to ensure serialization amongst threads */ GBLREF pthread_t thread_mutex_holder; /* pid/tid of the thread that has "thread_mutex" currently locked */ GBLREF pthread_key_t thread_gtm_putmsg_rname_key; /* points to region name corresponding to each running thread */ GBLREF boolean_t thread_block_sigsent; /* TRUE => block external signals SIGINT/SIGQUIT/SIGTERM/SIGTSTP/SIGCONT */ GBLREF int gtm_mupjnl_parallel; /* Maximum # of concurrent threads or procs to use in "gtm_multi_thread" */ #ifdef DEBUG GBLREF boolean_t in_nondeferrable_signal_handler; # define IN_GENERIC_SIGNAL_HANDLER 1 # define IN_TIMER_HANDLER 2 #endif GBLREF boolean_t forced_thread_exit; error_def(ERR_SYSCALL); /* Some pthread operations don't do well when interrupted by signals, so provide deferred interrupt wrappers for them. */ #define PTHREAD_COND_SIGNAL(COND, RVAL) \ MBSTART { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_PTHREAD_NB, prev_intrpt_state); \ (RVAL) = pthread_cond_signal(COND); \ ENABLE_INTERRUPTS(INTRPT_IN_PTHREAD_NB, prev_intrpt_state); \ } MBEND #define PTHREAD_COND_TIMEDWAIT(COND, MUTEX, TIMEOUT, RVAL) \ MBSTART { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_PTHREAD_NB, prev_intrpt_state); \ (RVAL) = pthread_cond_timedwait(COND, MUTEX, TIMEOUT); \ ENABLE_INTERRUPTS(INTRPT_IN_PTHREAD_NB, prev_intrpt_state); \ } MBEND #ifdef GTM_PTHREAD /* For use with gtm_multi_thread() */ typedef struct { int ntasks; gtm_pthread_fnptr_t fnptr; void **ret_array; void *parm_array; int parmElemSize; } thread_parm_t; int gtm_multi_thread_helper(thread_parm_t *tparm); #define IS_LIBPTHREAD_MUTEX_LOCK_HOLDER (pthread_self() == thread_mutex_holder) #define PTHREAD_MUTEX_LOCK_IF_NEEDED(WAS_HOLDER) \ { \ int rc; \ \ if (multi_thread_in_use) \ { /* gtm_malloc/gtm_free is not thread safe. So use locks to serialize */ \ assert(thread_mutex_initialized); \ /* We should never use pthread_* calls inside a signal/timer handler. Assert that */ \ assert(!in_nondeferrable_signal_handler); \ /* Allow for self to already own the lock (due to nested codepaths that need the lock. */ \ if (!IS_LIBPTHREAD_MUTEX_LOCK_HOLDER) \ { \ rc = pthread_mutex_lock(&thread_mutex); \ if (rc) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, \ RTS_ERROR_LITERAL("pthread_mutex_lock()"), CALLFROM, rc); \ thread_mutex_holder = pthread_self(); \ WAS_HOLDER = FALSE; \ } else \ WAS_HOLDER = TRUE; \ } else \ assert(!thread_mutex_holder); \ } #define PTHREAD_MUTEX_UNLOCK_IF_NEEDED(WAS_HOLDER) \ { \ int rc; \ \ if (multi_thread_in_use) \ { \ assert(thread_mutex_initialized); \ /* We should never use pthread_* calls inside a signal/timer handler. Assert that */ \ assert(!in_nondeferrable_signal_handler); \ /* assert self does own the lock */ \ assert(pthread_self() == thread_mutex_holder); \ if (!WAS_HOLDER) \ { \ thread_mutex_holder = 0; \ rc = pthread_mutex_unlock(&thread_mutex); \ if (rc) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, \ RTS_ERROR_LITERAL("pthread_mutex_unlock()"), CALLFROM, rc); \ } \ } else \ assert(!thread_mutex_holder); \ } /* Below macro identifies if the caller is inside threaded code. A quick check of this is using the global * "multi_thread_in_use". If that is FALSE, we are guaranteed not to be in threaded code. If it is TRUE though, it is * still possible the caller is the master process (that spawned off the threads) and not the threads themselves. * The only way to distinguish between the master and the threads is to get the rname_key. It is NULL for the * master and non-NULL for the threads. * Note: In the future, if more threads are implemented with no region context, we might be better off checking * "gtm_main_thread_id" against "pthread_self()". Right now, the former is not maintained in all cases and is * hence not used. */ #define INSIDE_THREADED_CODE(rname) (multi_thread_in_use && (NULL != (rname = pthread_getspecific(thread_gtm_putmsg_rname_key)))) #else #define IS_LIBPTHREAD_MUTEX_LOCK_HOLDER FALSE #define ASSERT_NO_THREAD_USAGE assert(!multi_thread_in_use && !thread_mutex_holder) #define PTHREAD_MUTEX_LOCK_IF_NEEDED(WAS_HOLDER) ASSERT_NO_THREAD_USAGE #define PTHREAD_MUTEX_UNLOCK_IF_NEEDED(WAS_HOLDER) ASSERT_NO_THREAD_USAGE #define INSIDE_THREADED_CODE(rname) FALSE #endif /* Returns FALSE if threads are in use and we dont own the libpthread mutex lock. Returns TRUE otherwise */ #define IS_PTHREAD_LOCKED_AND_HOLDER (!multi_thread_in_use || IS_LIBPTHREAD_MUTEX_LOCK_HOLDER) /* Below macro is invoked just before we read or write global variables that are also updated inside threaded code. * Global variables currently in this list (of those that are updated inside threaded code) are * TREF(util_outbuff_ptr) * TREF(util_outptr) * Any time any variable in the above list is read or written, the below macro needs to be added before the reference. */ #define ASSERT_SAFE_TO_UPDATE_THREAD_GBLS assert(IS_PTHREAD_LOCKED_AND_HOLDER) /* Note: Below macro can be safely used even in threaded code since this variable only transitions from 0 to 1 inside threaded code. * Also, it is okay for other threads to read a stale value of this since they keep checking this at logical points in their * execution (using the PTHREAD_EXIT_IF_FORCED_EXIT macro). Reading a stale value will only delay thread exit a little. * The 1 to 0 transition happens only in non-threaded code. Assert accordingly. */ #define SET_FORCED_THREAD_EXIT forced_thread_exit = 1 #define RESET_FORCED_THREAD_EXIT \ { \ char *rname; \ \ assert(!INSIDE_THREADED_CODE(rname)); \ forced_thread_exit = 0; \ } /* If a process with multiple threads receives a signal (e.g. SIGTERM) asking it to terminate, * the master process sets forced_exit to a non-zero value. But does not initiate exit handling * right then. Instead it goes back to what it was doing (most likely "pthread_join" inside * "gtm_multi_thread") and continues waiting for the threads to die. The threads are supposed to * check for "forced_exit" periodically (when they reach a logical stage in their processing) and * exit as soon as possible thereby letting the master process initiate exit handling processing. * The below macro helps with that. */ #define PTHREAD_EXIT_IF_FORCED_EXIT \ { \ char *rname; \ \ GTM_PTHREAD_ONLY(assert(INSIDE_THREADED_CODE(rname))); \ NON_GTM_PTHREAD_ONLY(assert(FALSE)); \ if (forced_thread_exit) \ GTM_PTHREAD_EXIT(PTHREAD_CANCELED); \ } /* Exit the thread with status "STATUS". But before that release any mutex locks you hold (possible for example * if the exiting thread had done a "rts_error" call which would have grabbed the pthread_mutex_t lock but not * released it anywhere until thread exit time (which is here). The release is needed to prevent other threads * from deadlocking. */ #define GTM_PTHREAD_EXIT(STATUS) \ { \ char *rname; \ \ GTM_PTHREAD_ONLY(assert(INSIDE_THREADED_CODE(rname));) \ NON_GTM_PTHREAD_ONLY(assert(FALSE)); \ /* If thread is exiting with abnormal status, signal other threads to stop execution \ * at a logical point since the parent is anyways going to return with a non-zero exit status. \ * This is necessary so only ONE thread modifies the condition-handler stack and other global variables \ * as part of error-handling. If multiple threads encounter errors, the first thread will go \ * through the condition handler scheme, the second thread onwards will exit in DRIVECH without \ * going through any condition handler invocations. \ */ \ if (0 != STATUS) \ SET_FORCED_THREAD_EXIT; \ if (IS_LIBPTHREAD_MUTEX_LOCK_HOLDER) \ PTHREAD_MUTEX_UNLOCK_IF_NEEDED(FALSE); \ pthread_exit((void *)(INTPTR_T)STATUS); \ } #endif fis-gtm-V7.0-005/sr_unix/gtm_permissions.c0000755000032200000250000003157314342376330017443 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2018 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_string.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_limits.h" #include "gtm_unistd.h" #include "gtm_fcntl.h" #include "gtm_stat.h" #include "gtm_pwd.h" #include #include "gtm_permissions.h" #include "send_msg.h" #include "eintr_wrappers.h" GBLDEF gid_t *gid_list = NULL; GBLDEF int gid_list_len = 0; GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF boolean_t gtm_dist_ok_to_use; #if defined(__MVS__) # define LIBGTMSHR "%s/libgtmshr.dll" #else # define LIBGTMSHR "%s/libgtmshr.so" #endif /* Get the process's group list and stash the information to avoid repeated calls */ void gtm_init_gid_list(void) { int tmp_gid_list_len; assert(NULL == gid_list); tmp_gid_list_len = getgroups(0, NULL); assert(0 <= tmp_gid_list_len); if (0 < tmp_gid_list_len) { gid_list = malloc(tmp_gid_list_len * SIZEOF(gid_t)); gid_list_len = getgroups(tmp_gid_list_len, gid_list); assert(gid_list_len == tmp_gid_list_len); if (gid_list_len != tmp_gid_list_len) { gid_list_len = 0; free(gid_list); gid_list = NULL; } } } /* Search through the supplementary gid list for a match */ boolean_t gtm_gid_in_gid_list(gid_t gid) { int i; assert(NULL != gid_list); if (NULL == gid_list) return FALSE; for (i = 0; i < gid_list_len; i++) if (gid == gid_list[i]) return TRUE; return FALSE; } /* Return the group id of the distribution based on libgtmshr.xx[x]. If there is some * problem accessing that file then return INVALID_GID which signals no change to group. Otherwise, * the pointer to the stat buffer will contain the result of the call to STAT_FILE. */ gid_t gtm_get_group_id(struct stat *stat_buff) { int ret_stat; char temp[GTM_PATH_MAX]; static boolean_t first_time = TRUE; static struct stat st_buff; if (!first_time) { *stat_buff = st_buff; return st_buff.st_gid; } if (gtm_dist_ok_to_use) { /* build a path to libgtmshr.so or .sl on hpux or .dll on zos */ SNPRINTF(temp, SIZEOF(temp), LIBGTMSHR, gtm_dist); STAT_FILE(temp, stat_buff, ret_stat); if (0 == ret_stat) { first_time = FALSE; st_buff = *stat_buff; return(stat_buff->st_gid); } } /* return INVALID_GID if STAT_FILE returned a -1 or gtm_dist has not been validated */ return (INVALID_GID); } /* Return TRUE if the "uid" parameter is a member of the "gid" group parameter. * Return FALSE if it is not. */ boolean_t gtm_member_group_id(uid_t uid, gid_t gid, struct perm_diag_data *pdd) { struct group *grp; struct passwd *pwd; # ifdef DEBUG if (WBTEST_HELPOUT_FAILGROUPCHECK == gtm_white_box_test_case_number) { if (NULL == gid_list) gtm_init_gid_list(); return FALSE; } # endif /* Effective user is uid ?*/ if (GETEUID() == uid) { /* Effective group is gid? */ if (GETEGID() == gid) return(TRUE); /* EUID == uid but check if gid is in any groups associated with the process */ return GID_IN_GID_LIST(gid); } /* Get "uid" details */ pwd = getpwuid(uid); if (NULL == pwd) return(FALSE); /* If user id not found then assume uid not a member */ /* If the gid of the file is the same as the gid for the process uid we are done */ if (gid == pwd->pw_gid) return(TRUE); /* Else get "gid" details */ grp = getgrgid(gid); if (NULL == grp) return(FALSE); /* If group id not found then assume uid not a member */ /* Else we have to compare the name stored in pwd struct with the names of the group members in the group struct */ while (NULL != *(grp->gr_mem)) { if (!strcmp(pwd->pw_name, *(grp->gr_mem++))) return(TRUE); } return(FALSE); } /* Based on security rules in this routine, set * a) *group_id to the group to be used for shared resources (journals, temp files etc.). * If no change, will be set to INVALID_GID. * b) *perm to the permissions to be used. * c) *user_id to the user id to be used. * If the user is root, *user_id may be set to a target uid if needed. Otherwise, it will be set to INVALID_UID. */ boolean_t gtm_permissions(struct stat *stat_buff, int *user_id, int *group_id, int *perm, enum perm_target_types target_type, struct perm_diag_data *pdd) { uid_t this_uid, file_uid; gid_t this_gid, file_gid; gid_t gtm_dist_gid = INVALID_GID; struct stat dist_stat_buff; int this_uid_is_file_owner; int this_uid_is_root; int this_uid_in_file_group; int owner_in_file_group; int gtm_group_restricted; int file_owner_perms, file_group_perms, file_other_perms; int new_owner_perms, new_group_perms, new_other_perms; char *strnow, *strtop; int i, len; mode_t st_mode, dir_mode = 0; /* get this_uid/gid */ this_uid = GETEUID(); this_gid = GETEGID(); file_uid = stat_buff->st_uid; /* get owning file uid */ file_gid = stat_buff->st_gid; /* get owning file gid */ /* set variables for permission logic */ this_uid_is_file_owner = (this_uid == file_uid); this_uid_is_root = (0 == this_uid); this_uid_in_file_group = gtm_member_group_id(this_uid, file_gid, pdd); owner_in_file_group = gtm_member_group_id(file_uid, file_gid, pdd); *user_id = INVALID_UID; /* set default uid */ *group_id = INVALID_GID; /* set default gid */ assert((PERM_FILE & target_type) || (PERM_IPC & target_type)); /* code below relies on this */ st_mode = stat_buff->st_mode; /* General rule for permissions below. If target is a FILE (not an IPC, i.e. "(target_type & PERM_FILE) == TRUE", * and if "this_uid_is_file_owner" is FALSE but "this_uid_in_file_group" is TRUE, copy over group permissions of * source file into owner permissions of the target file. This ensures that the target file is accessible by the * new owner (who is governed by the owner permissions) as well as other members of the group owning the source * file (and in turn the target file). */ file_owner_perms = (0600 & st_mode); file_group_perms = (0060 & st_mode); if (file_owner_perms && !(0066 & st_mode)) { /* File is only user accessible */ /* Use default group */ assert(this_uid_is_file_owner || this_uid_is_root); if (this_uid_is_root) { *user_id = file_uid; *group_id = file_gid; } /* Else: use default uid/gid */ new_owner_perms = ((PERM_FILE & target_type) ? file_owner_perms : 0600); *perm = new_owner_perms; /* read and/or write for ONLY user and none for group/other */ } else if (file_group_perms && !(0606 & st_mode)) { /* File is only group accessible */ if (this_uid_is_root) { *user_id = file_uid; new_group_perms = ((PERM_FILE & target_type)) ? file_group_perms : 0060; *perm = new_group_perms; } else { /* No need to initialize *user_id. Use default uid instead. */ if (PERM_FILE & target_type) { new_owner_perms = (file_group_perms << 3); new_group_perms = file_group_perms; } else { new_owner_perms = 0600; new_group_perms = 0060; } *perm = new_owner_perms | new_group_perms; } *group_id = file_gid; /* use file group */ } else { /* File is other accessible OR accessible to user and group but not other */ file_other_perms = (0006 & st_mode); if (PERM_IPC & target_type) { new_owner_perms = file_owner_perms ? 0600 : 0000; new_group_perms = file_group_perms ? 0060 : 0000; new_other_perms = file_other_perms ? 0006 : 0000; } else { new_owner_perms = file_owner_perms; new_group_perms = file_group_perms; new_other_perms = file_other_perms; } /* Find restricted group, if any */ gtm_dist_gid = gtm_get_group_id(&dist_stat_buff); dir_mode = (INVALID_GID != gtm_dist_gid) ? dist_stat_buff.st_mode : 0; /* 4SCA: Assigned value is garbage */ gtm_group_restricted = ((INVALID_GID != gtm_dist_gid) && !(dir_mode & 01)); /* not other executable */ if ((this_uid_is_file_owner && this_uid_in_file_group) || this_uid_is_root) { if (this_uid_is_root) /* otherwise, use default uid */ *user_id = file_uid; *group_id = file_gid; /* use file group */ *perm = new_owner_perms | new_group_perms | new_other_perms; } else if (this_uid_is_file_owner) { /* This uid has access via file owner membership but is not a member of the file group */ assert(!this_uid_in_file_group); /* Since there could be userids that are members of the file group as well as the new group, * one needs to keep the existing group permissions while setting up the new group permissions. */ new_group_perms = (new_group_perms | (new_other_perms << 3)); if (gtm_group_restricted) { assert((WBTEST_HELPOUT_FAILGROUPCHECK == gtm_white_box_test_case_number) || gtm_member_group_id(this_uid, gtm_dist_gid, pdd)); *group_id = gtm_dist_gid; /* use restricted group */ *perm = new_owner_perms | new_group_perms; } else { /* No need to set *group_id. Use default group. * But because file owner is not part of the file group, transfer group permissions of * file to "other" when creating the new file so members of the file group (that are not * part of the default group of the file owner) can still access the new file with same * permissions as the source file. And transfer other permissions of file to "group" when * creating the new file for similar reasons. Note that it is possible for userids to exist * that are part of the file group and the default group of the file owner. For those userids, * we need to ensure the group permissions of the new file stay same as source file. Therefore * we need the new group and other permissions to be a bitwise OR of the old group and other. */ assert(file_owner_perms); new_other_perms = new_group_perms >> 3; *perm = new_owner_perms | new_group_perms | new_other_perms; } } else { /* This uid has access via file group OR file other membership */ /* Use default uid in all cases below */ if ((this_uid_in_file_group && owner_in_file_group) || this_uid_is_root) { *group_id = file_gid; /* use file group */ assert(file_group_perms); new_owner_perms = new_group_perms << 3; *perm = new_owner_perms | new_group_perms | new_other_perms; } else if (gtm_group_restricted) { assert((WBTEST_HELPOUT_FAILGROUPCHECK == gtm_white_box_test_case_number) || gtm_member_group_id(this_uid, gtm_dist_gid, pdd)); *group_id = gtm_dist_gid; /* use restricted group */ new_group_perms = (new_group_perms | (new_other_perms << 3)); /* "owner" gets group permissions if in group, otherwise "other" permissions */ new_owner_perms = this_uid_in_file_group ? (new_group_perms << 3) : (new_other_perms << 6); *perm = new_owner_perms | new_group_perms; } else { if (this_uid_in_file_group) { *group_id = file_gid; /* use file group */ /* Since owner_in_file_group is FALSE, we need to make sure owner permissions * are transferred to "other" in the new file. */ new_other_perms |= (new_owner_perms >> 6); new_owner_perms = new_group_perms << 3; } else { /* Use default group; Use 'other' permissions for owner */ new_owner_perms = (new_other_perms << 6); new_group_perms = (new_group_perms | (new_other_perms << 3)); new_other_perms = new_group_perms >> 3; } *perm = new_owner_perms | new_group_perms | new_other_perms; } } } if (target_type & PERM_EXEC) *perm |= ((*perm & 0444) >> 2); /* Grab the read bits, shift them to the exec bit position, and add them back in. */ assert(*perm); if (!(this_uid_is_root) && (!(*perm & 0600) || (!(this_uid_is_file_owner) && !(this_uid_in_file_group) && !(0006 & st_mode)))) { /* Either permissions don't include owner or could not ascertain group membership */ pdd->this_uid = this_uid; pdd->this_gid = this_gid; pdd->file_uid = file_uid; /* get owning file uid */ pdd->file_gid = file_gid; /* get owning file gid */ pdd->lib_gid = gtm_dist_gid; SNPRINTF(pdd->file_perm, SIZEOF(pdd->file_perm), "%04o", st_mode & PERMALL); SNPRINTF(pdd->lib_perm, SIZEOF(pdd->lib_perm), "%04o", dir_mode & PERMALL); if (gid_list_len > 0) { strtop = pdd->print_gid_list + MAX_PRINT_GID_LEN; strnow = pdd->print_gid_list; for (i = 0; (strnow < strtop) && (i < gid_list_len); i++) if (this_gid != gid_list[i]) /* Avoid printing the primary GID */ strnow += SNPRINTF(strnow, (strtop - strnow), ",%d", (int) gid_list[i]); if (strnow >= strtop) { /* It's too long, terminate with an ellipsis*/ strnow = pdd->print_gid_list + MAX_PRINT_GID_LEN - GID_ELLIPSIS_LEN; SNPRINTF(strnow, GID_ELLIPSIS_LEN, GID_ELLIPSIS); } pdd->print_gid_list_len = strlen(pdd->print_gid_list); assert(pdd->print_gid_list_len < MAX_PRINT_GID_LEN); } else pdd->print_gid_list_len = 0; return FALSE; } return TRUE; } fis-gtm-V7.0-005/sr_unix/gtm_pipe.c0000755000032200000250000000413514342376330016017 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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. * * * ****************************************************************/ /* gtm_pipe.c Parameters -- command : the command to be executed. pt : type of the pipe (input or output) Returns -- -1 : fork error -2 : pipe() error other : the file descriptor serving as one end of the pipe */ #include "mdef.h" #include #include "gtm_fcntl.h" #include "gtm_stat.h" #include "gtm_stdio.h" #include "gtm_unistd.h" #include "gtm_stdlib.h" #include "gtm_pipe.h" #include "eintr_wrappers.h" #include "gtmio.h" #include "fork_init.h" GBLDEF uint4 pipe_child; int gtm_pipe(char *command, pipe_type pt) { int pfd[2], child, parent; int dup2_res, rc; pid_t child_pid; parent = (int)pt; child = 1 - parent; if (0 > pipe(pfd)) { PERROR("pipe : "); return -2; } FORK(child_pid); if (-1 == child_pid) { PERROR("fork : "); return -1; } else if (0 == child_pid) { /* child process */ CLOSEFILE_RESET(pfd[parent], rc); /* resets "pfd[parent]" to FD_INVALID */ DUP2(pfd[child], child, dup2_res); CLOSEFILE_RESET(pfd[child], rc); /* resets "pfd[child]" to FD_INVALID */ /* We should have used exec instead of SYSTEM. Earlier it was followed by EXIT(EXIT_SUCCESS), which calls * exit_handler. So both child and parent will do exit handling. This can make ref_cnt < 0, or, it can release * semaphores, which we should not release until parent exists. So we just call UNDERSCORE_EXIT(EXIT_SUCCESS). */ rc = SYSTEM(command); UNDERSCORE_EXIT(EXIT_SUCCESS); /* just exit from here */ } else { /* parent process */ pipe_child = child_pid; CLOSEFILE_RESET(pfd[child], rc); /* resets "pfd[child]" to FD_INVALID */ return pfd[parent]; } } fis-gtm-V7.0-005/sr_unix/gtm_pipe.h0000755000032200000250000000107514342376330016024 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /* gtm_pipe.h */ typedef enum { input_from_comm = 0, output_to_comm } pipe_type; int gtm_pipe(char *command, pipe_type pt); fis-gtm-V7.0-005/sr_unix/gtm_poll.h0000644000032200000250000000142214342376330016026 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016 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. * * * ****************************************************************/ #ifndef GTM_POLL_H_INCLUDED #define GTM_POLL_H_INCLUDED #include #define EVENTFD_NOTIFIED(fds, n) (0 != (fds[n].revents & POLLIN)) #define INIT_POLLFD(pollfd, efd) \ { \ pollfd.fd = efd; \ pollfd.revents = 0; \ pollfd.events = POLLIN; \ } #endif fis-gtm-V7.0-005/sr_unix/gtm_post_startup_check_init.c0000644000032200000250000000241014342376330022000 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018-2022 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_post_startup_check_init.h" #include "gtm_icu_api.h" #ifdef __linux__ #include "hugetlbfs_overrides.h" #include "get_page_size.h" #endif /* This function exists to perform all initialization that can only be done * after $gtm_dist has been validated by gtm_startup_chk. The reason for this * is two fold: * 1. err_init() is performed before this point so that a minimal * environment exists to report an error. This is a requirement for * gtm_startup_chk. * 2. Initializations require a verified $gtm_dist to enforce * restrictions. AKA restrictions will be initialized now. */ void gtm_post_startup_check_init(void) { # ifdef HUGETLB_SUPPORTED get_page_size(); get_hugepage_size(); # endif GTM_ICU_INIT_IF_NEEDED; } fis-gtm-V7.0-005/sr_unix/gtm_post_startup_check_init.h0000644000032200000250000000120614342376330022007 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018 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. * * * ****************************************************************/ #ifndef __GTM_POST_STARTUP_CHECK_INIT_H__ #define __GTM_POST_STARTUP_CHECK_INIT_H__ void gtm_post_startup_check_init(void); #endif fis-gtm-V7.0-005/sr_unix/gtm_pthread.h0000644000032200000250000000154714342376330016517 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2018 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. * * * ****************************************************************/ /* gtm_pthread.h - interlude to system header file. */ #ifndef GTM_PTHREAD_H #define GTM_PTHREAD_H #include /* BYPASSOK(gtm_pthread.h) */ #define PTHREAD_MUTEX_ROBUST_SUPPORTED ((_POSIX_C_SOURCE >= 200809L) && !defined(_AIX)) #define PTHREAD_MUTEX_CONSISTENT_SUPPORTED ((_POSIX_C_SOURCE >= 200809L) && !defined(_AIX)) #endif fis-gtm-V7.0-005/sr_unix/gtm_pthread_init_key.c0000644000032200000250000000424714342376330020405 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2017 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 "gdsroot.h" #include "gtm_facility.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gtm_multi_thread.h" #include "gtm_pthread_init_key.h" error_def(ERR_SYSCALL); /* The following function needs to be invoked first thing inside a mur_* thread function. * Returns 0 on success. Non-zero otherwise. */ uint4 gtm_pthread_init_key(gd_region *reg) { int status; # ifdef GTM_PTHREAD unsigned char *ptr; if (!multi_thread_in_use) return 0; /* Initialize thread_gtm_putmsg_rname_key at start of thread */ assert('\0' == reg->rname[reg->rname_len]); /* ensure region name is null terminated */ /* In rare cases (e.g. "mur_db_files_from_jnllist"), rname is not initialized, but fname is. Use it then. */ if (!reg->owning_gd->is_dummy_gbldir) { assert(reg->rname_len); ptr = ®->rname[0]; assert('\0' == ptr[reg->rname_len]); } else { assert(!memcmp(reg->rname, "DEFAULT", reg->rname_len)); ptr = ®->dyn.addr->fname[0]; assert('\0' == ptr[reg->dyn.addr->fname_len]); } assert('\0' != *ptr); status = pthread_setspecific(thread_gtm_putmsg_rname_key, ptr); assert(0 == status); if ((0 != status) && !IS_GTM_IMAGE) /* Display error if mupip/dse etc. but not for mumps */ { /* Note: We got an error while setting the rname_key so gtm_putmsg_csa will not print the * region-name prefix for this message like it usually does for gtm_putmsg calls inside threaded code. * Not having the region name in this rare case is considered okay for now. */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("pthread_setspecific()"), CALLFROM, status); } # else status = 0; # endif return status; } fis-gtm-V7.0-005/sr_unix/gtm_pthread_init_key.h0000644000032200000250000000105614342376330020405 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015 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. * * * ****************************************************************/ uint4 gtm_pthread_init_key(gd_region *reg); fis-gtm-V7.0-005/sr_unix/gtm_putmsg.c0000644000032200000250000001000214342376330016364 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 #include "gtm_multi_thread.h" #include "util.h" #include "gtmmsg.h" #include "gtm_putmsg_list.h" #include "gtmimagename.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "anticipatory_freeze.h" #include "gtm_multi_proc.h" #include "interlock.h" GBLREF gd_region *gv_cur_region; GBLREF jnlpool_addrs_ptr_t jnlpool; /* WARNING: For chained error messages, all messages MUST be followed by an fao count; * ======= zero MUST be specified if there are no parameters. */ /* #GTM_THREAD_SAFE : The below function (gtm_putmsg) is thread-safe */ void gtm_putmsg(int argcnt, ...) { boolean_t was_holder; sgmnt_addrs *csa; jnlpool_addrs_ptr_t local_jnlpool; /* needed by PTHREAD_CSA_FROM_GV_CUR_REGION */ boolean_t release_latch; va_list var; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; PTHREAD_CSA_FROM_GV_CUR_REGION(csa, local_jnlpool); VAR_START(var, argcnt); PTHREAD_MUTEX_LOCK_IF_NEEDED(was_holder); /* get thread lock in case threads are in use */ GRAB_MULTI_PROC_LATCH_IF_NEEDED(release_latch); /* get multi-process lock if needed */ gtm_putmsg_list(csa, argcnt, var); va_end(var); util_out_print("",TRUE); REL_MULTI_PROC_LATCH_IF_NEEDED(release_latch); /* release multi-process lock if needed */ PTHREAD_MUTEX_UNLOCK_IF_NEEDED(was_holder); /* release exclusive thread lock if needed */ } /* #GTM_THREAD_SAFE : The below function (gtm_putmsg_csa) is thread-safe */ void gtm_putmsg_csa(void *csa, int argcnt, ...) { boolean_t was_holder; boolean_t release_latch; va_list var; VAR_START(var, argcnt); PTHREAD_MUTEX_LOCK_IF_NEEDED(was_holder); /* get thread lock in case threads are in use */ GRAB_MULTI_PROC_LATCH_IF_NEEDED(release_latch); /* get multi-process lock if needed */ gtm_putmsg_list(csa, argcnt, var); va_end(var); util_out_print("",TRUE); REL_MULTI_PROC_LATCH_IF_NEEDED(release_latch); /* release multi-process lock if needed */ PTHREAD_MUTEX_UNLOCK_IF_NEEDED(was_holder); /* release exclusive thread lock if needed */ } /* #GTM_THREAD_SAFE : The below function (gtm_putmsg_noflush) is thread-safe */ void gtm_putmsg_noflush(int argcnt, ...) { boolean_t was_holder; sgmnt_addrs *csa; jnlpool_addrs_ptr_t local_jnlpool; /* needed by PTHREAD_CSA_FROM_GV_CUR_REGION */ boolean_t release_latch; va_list var; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; PTHREAD_CSA_FROM_GV_CUR_REGION(csa, local_jnlpool); VAR_START(var, argcnt); PTHREAD_MUTEX_LOCK_IF_NEEDED(was_holder); /* get thread lock in case threads are in use */ GRAB_MULTI_PROC_LATCH_IF_NEEDED(release_latch); /* get multi-process lock if needed */ gtm_putmsg_list(csa, argcnt, var); va_end(var); REL_MULTI_PROC_LATCH_IF_NEEDED(release_latch); /* release multi-process lock if needed */ PTHREAD_MUTEX_UNLOCK_IF_NEEDED(was_holder); /* release exclusive thread lock if needed */ } /* #GTM_THREAD_SAFE : The below function (gtm_putmsg_noflush_csa) is thread-safe */ void gtm_putmsg_noflush_csa(void *csa, int argcnt, ...) { boolean_t was_holder; boolean_t release_latch; va_list var; VAR_START(var, argcnt); PTHREAD_MUTEX_LOCK_IF_NEEDED(was_holder); /* get thread lock in case threads are in use */ GRAB_MULTI_PROC_LATCH_IF_NEEDED(release_latch); /* get multi-process lock if needed */ gtm_putmsg_list(csa, argcnt, var); va_end(var); REL_MULTI_PROC_LATCH_IF_NEEDED(release_latch); /* release multi-process lock if needed */ PTHREAD_MUTEX_UNLOCK_IF_NEEDED(was_holder); /* release exclusive thread lock if needed */ } fis-gtm-V7.0-005/sr_unix/gtm_putmsg_list.c0000644000032200000250000001303614342376330017431 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include "gtm_stdio.h" #include "gtm_multi_thread.h" #include "fao_parm.h" #include "error.h" #include "util.h" #include "util_format.h" #include "util_out_print_vaparm.h" #include "gtmmsg.h" #include "gtm_putmsg_list.h" #include "gtmimagename.h" /* needed for IS_GTM_IMAGE macro */ #include "gtmio.h" /* needed for FFLUSH macro */ #include "io.h" /* database/replication related includes due to anticipatory freeze */ #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "anticipatory_freeze.h" /* for SET_ANTICIPATORY_FREEZE_IF_NEEDED */ #include "restrict.h" /* For GTM-7759 logging control */ #define COLON_SEPARATOR " : " /* * ---------------------------------------------------------------------------------------- * WARNING: For chained error messages, all messages MUST be followed by an fao count; * ======= zero MUST be specified if there are no parameters. * ---------------------------------------------------------------------------------------- */ /* #GTM_THREAD_SAFE : The below function (gtm_putmsg_list) is thread-safe because caller ensures serialization with locks */ void gtm_putmsg_list(void *csa, int arg_count, va_list var) { GBLREF boolean_t gtm_dist_ok_to_use; int i, msg_id, fao_actual, fao_count, dummy, freeze_msg_id; char msg_buffer[PUT_BUFF_SIZE]; mstr msg_string; const err_msg *msg; const err_ctl *ctl; boolean_t freeze_needed = FALSE; char *rname; jnlpool_addrs_ptr_t local_jnlpool; /* used by CHECK_IF_FREEZE_ON_ERROR_NEEDED and FREEZE_INSTANCE_IF_NEEDED */ boolean_t mustlog; va_list var_sav; char fmt_buf[2048]; char *fmt_ptr; int f_actual, f_count; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (!IS_GTMSECSHR_IMAGE) { /* Note gtmsecshr does no stdout/stderr IO - everything goes to operator log so this doesn't apply */ util_out_print(NULL, RESET); } assert(0 < arg_count); assert(IS_PTHREAD_LOCKED_AND_HOLDER); # ifdef GTM_PTHREAD if (INSIDE_THREADED_CODE(rname)) /* Note: "rname" is not initialized if macro returns FALSE */ { /* If running with threads, identify which thread is generating this message with a prefix using * thread-specific-key (which points to the region name for mur* related threads). This helps decipher * the output of a mupip journal command where the region specific output might be intermixed. */ assert((NULL != rname) && ('\0' != rname[0])); util_out_print(rname, NOFLUSH_OUT); util_out_print(COLON_SEPARATOR, NOFLUSH_OUT); } # endif for (; ; ) { msg_id = va_arg(var, int); mustlog = ( gtm_dist_ok_to_use && (!(RESTRICTED(logdenials))) && (MSGFLAG(msg_id) & MSGMUSTLOG) ); /* GTM-7759 logging unless restricted */ CHECK_IF_FREEZE_ON_ERROR_NEEDED(csa, msg_id, freeze_needed, freeze_msg_id, local_jnlpool); --arg_count; if (NULL == (ctl = err_check(msg_id))) msg = NULL; else GET_MSG_INFO(msg_id, ctl, msg); msg_string.addr = msg_buffer; msg_string.len = SIZEOF(msg_buffer); gtm_getmsg(msg_id, &msg_string); if (mustlog) { /* If this message must be sysloged for GTM-7759, do it here */ va_copy(var_sav, var); fmt_ptr = &fmt_buf[0]; if ((0 < arg_count) && msg) { /* We don't consume arg_count here */ f_actual = va_arg(var_sav, int); f_count = f_actual < msg->parm_count ? f_actual : msg->parm_count; if (MAX_FAO_PARMS < f_count) f_count = MAX_FAO_PARMS; } else f_actual = f_count = 0; fmt_ptr = util_format(msg_string.addr, var_sav, fmt_ptr, SIZEOF(fmt_buf) - 1, f_count); *fmt_ptr = '\0'; send_msg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_STR(fmt_buf)); va_end(var_sav); } if (NULL == msg) { util_out_print(msg_string.addr, NOFLUSH_OUT, msg_id); if (0 < arg_count) { /* -------------------------- * Print the message to date * -------------------------- */ util_out_print(NULL, FLUSH); /* --------------------------------------- * Chained error; scan off the fao count * (it should be zero) * --------------------------------------- */ i = va_arg(var, int); --arg_count; assert(0 == i); } } else { if (0 < arg_count) { fao_actual = va_arg(var, int); --arg_count; fao_count = fao_actual < msg->parm_count ? fao_actual : msg->parm_count; if (MAX_FAO_PARMS < fao_count) fao_count = MAX_FAO_PARMS; } else fao_actual = fao_count = 0; util_out_print_vaparm(msg_string.addr, NOFLUSH_OUT, var, fao_count); va_end(var); /* needed before used as dest in copy */ VAR_COPY(var, TREF(last_va_list_ptr)); /* How much we unwound */ arg_count -= fao_count; /* ------------------------------ * Skim off any extra parameters * ------------------------------ */ for (i = fao_count; i < fao_actual; ++i) { dummy = va_arg(var, int); --arg_count; } va_end(TREF(last_va_list_ptr)); } if (0 == arg_count) break; if (!IS_GTMSECSHR_IMAGE) util_out_print("!/", NOFLUSH_OUT); } FREEZE_INSTANCE_IF_NEEDED(csa, freeze_needed, freeze_msg_id, local_jnlpool); } fis-gtm-V7.0-005/sr_unix/gtm_rel_quant.h0000644000032200000250000000650714342376330017063 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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. * * * ****************************************************************/ #ifndef GTM_REL_QUANT_INCLUDED #define GTM_REL_QUANT_INCLUDED #include #include #include #include #include "gtm_unistd.h" #include "sleep.h" GBLREF uint4 process_id; /* yield processor macro - if argument is 0 or the (pseudo-)random value whose limit the argument defines is 0 just yield * otherwise do a microsleep */ #if defined(_AIX) /* For pSeries the "yield" system call seems a better match for * yields to ALLprocesses instead of just those on the local processor queue. */ void yield(void); /* AIX doesn't have this in a header, so use prototype from the man page. */ #define RELQUANT yield() #else #define RELQUANT sched_yield() /* avoiding pthread_yield() avoids unnecessary linking with libpthreads */ #endif #define USEC_IN_NSEC_MASK 0x3FF #define GTM_REL_QUANT(MAX_TIME_MASK) \ MBSTART { \ int NANO_SLEEP_TIME; \ static uint4 TIME_ADJ = 0; \ \ /* process_id provides cheap pseudo-random across processes, but add in a timestamp/counter \ * so that the number varies a bit. \ */ \ if (0 == TIME_ADJ) \ TIME_ADJ = (uint4) time(NULL); \ if (MAX_TIME_MASK) \ { /* To get a value that moves a bit, xor with a timestamp/counter. */ \ NANO_SLEEP_TIME = (process_id ^ (TIME_ADJ++)) & (MAX_TIME_MASK); \ if (!NANO_SLEEP_TIME) \ NANO_SLEEP_TIME = MAX_TIME_MASK; \ assert((NANO_SLEEP_TIME < E_9) && (NANO_SLEEP_TIME > 0)); \ NANOSLEEP(NANO_SLEEP_TIME, FALSE); \ } else \ RELQUANT; \ } MBEND /* Sleep/rel_quant <= 1 micro-second every 4 iterations and also perform caslatch check every ~4 seconds */ #define REST_FOR_LATCH(LATCH, MAX_SLEEP_MASK, RETRIES) \ MBSTART { \ if (0 == (RETRIES & LOCK_SPIN_HARD_MASK)) /* On every so many passes, sleep rather than spinning */ \ { \ GTM_REL_QUANT((MAX_SLEEP_MASK)); /* Release processor to holder of lock (hopefully) */ \ /* Check if we're due to check for lock abandonment check or holder wakeup */ \ if (0 == (RETRIES & (LOCK_CASLATCH_CHKINTVL_USEC - 1))) \ performCASLatchCheck(LATCH, TRUE); \ } \ } MBEND /* Sleep 1 micro-second every 4 iterations and also perform caslatch check every ~4 seconds */ #define SLEEP_FOR_LATCH(LATCH, RETRIES) \ MBSTART { \ if (0 == (RETRIES & LOCK_SPIN_HARD_MASK)) /* On every so many passes, sleep rather than spinning */ \ { \ SLEEP_USEC(1, FALSE); /* Release processor to holder of lock (hopefully) */ \ /* Check if we're due to check for lock abandonment check or holder wakeup */ \ if (0 == (RETRIES & (LOCK_CASLATCH_CHKINTVL_USEC - 1))) \ performCASLatchCheck(LATCH, TRUE); \ } \ } MBEND #endif /* GTM_REL_QUANT_INCLUDED */ fis-gtm-V7.0-005/sr_unix/gtm_rename.c0000755000032200000250000000152014342376330016324 0ustar librarygtc/**************************************************************** * * * Copyright 2003, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include #include "gtm_stdio.h" #include "gtm_rename.h" #include "iosp.h" uint4 gtm_rename(char *org_fn, int org_fn_len, char *rename_fn, int rename_len, uint4 *ustatus) { *ustatus = SS_NORMAL; /* used in VMS only */ assert(0 == org_fn[org_fn_len]); assert(0 == rename_fn[rename_len]); if (-1 == RENAME(org_fn, rename_fn)) return errno; return SS_NORMAL; } fis-gtm-V7.0-005/sr_unix/gtm_repl_multi_inst.h0000644000032200000250000000377314342376330020304 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2017 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. * * * ****************************************************************/ #ifndef GTM_REPL_MULTI_INST_H #define GTM_REPL_MULTI_INST_H error_def(ERR_REPLMULTINSTUPDATE); /* Issue REPLMULTINSTUPDATE when in TP and touching more than one region */ #define DISALLOW_MULTIINST_UPDATE_IN_TP(TLEVEL, JPLHEAD, CSA, SGMFIRST, DOJNLPOOLINIT) \ MBSTART { \ sgm_info *si; \ jnlpool_addrs_ptr_t local_jnlpool; \ \ if (IS_MUMPS_IMAGE && TLEVEL && JPLHEAD && JPLHEAD->next) \ { /* in transaction and multiple replication instances */ \ if (DOJNLPOOLINIT) /* trigger updates may not have initialized the journalpool yet */ \ JNLPOOL_INIT_IF_NEEDED(CSA, CSA->hdr, CSA->nl, SCNDDBNOUPD_CHECK_TRUE); /* may set CSA->jnlpool */ \ for (si = SGMFIRST; si && CSA->jnlpool; si = si->next_sgm_info) \ { /* CSA is connected to a jnlpool, check all regions in the transaction with updates use same jnlpool */ \ if (si->update_trans && si->tp_csa->jnlpool && (si->tp_csa->jnlpool != CSA->jnlpool)) \ { \ local_jnlpool = si->tp_csa->jnlpool; \ assert(local_jnlpool); \ assert(CSA->jnlpool == jnlpool); \ assert(jnlpool); \ assert(WBTEST_ENABLED(WBTEST_NO_REPLINSTMULTI_FAIL)); \ rts_error_csa(CSA_ARG(CSA) VARLSTCNT(8) ERR_REPLMULTINSTUPDATE, 6, \ DB_LEN_STR(local_jnlpool->jnlpool_dummy_reg), DB_LEN_STR(gv_cur_region), \ DB_LEN_STR(jnlpool->jnlpool_dummy_reg)); \ } \ } \ } \ } MBEND; #endif fis-gtm-V7.0-005/sr_unix/gtm_sem.h0000755000032200000250000000341114342376330015647 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef GTM_SEM_INCLUDED #define GTM_SEM_INCLUDED #define FTOK_SEM_PER_ID 3 union semun { int val; struct semid_ds *buf; u_short *array; #if defined(__linux__) && (__ia64) struct seminfo *__buf; /* buffer for IPC_INFO */ void *__pad; #endif }; #endif /* GTM_SEM_INCLUDED */ #define GTM_SEM_CHECK_EINVAL(gtm_environment_init, save_errno, udi) \ { \ assert(EINVAL != save_errno); \ assert(0 <= udi->ftok_semid); \ /* We want a core in case of EINVAL errno only if running in-house */ \ if (gtm_environment_init && (EINVAL == save_errno)) \ { \ util_out_print("udi->ftok_semid is: !UL", TRUE, udi->ftok_semid); \ util_out_print("save_errno is : !UL", TRUE, save_errno); \ assertpro(EINVAL != save_errno); \ } \ } fis-gtm-V7.0-005/sr_unix/gtm_semaphore.h0000644000032200000250000000254014342376330017045 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015 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. * * * ****************************************************************/ #ifndef GTM_SEMAPHORE_H #define GTM_SEMAPHORE_H #include /* "sem_init" does not return EINTR so no do/while loop needed like for "sem_wait" */ #define GTM_SEM_INIT(POSIX_SEM, PSHARED, VALUE, RC) \ { \ RC = sem_init(POSIX_SEM, PSHARED, VALUE); \ assert((-1 != RC) || (EINTR != errno)); \ } #define GTM_SEM_WAIT(POSIX_SEM, RC) \ { \ do \ { \ RC = sem_wait(POSIX_SEM); \ } while ((-1 == RC) && (EINTR == errno)); \ } #define GTM_SEM_TRYWAIT(POSIX_SEM, RC) \ { \ do \ { \ RC = sem_trywait(POSIX_SEM); \ } while ((-1 == RC) && (EINTR == errno)); \ } /* "sem_post" does not return EINTR so no do/while loop needed like for "sem_wait" */ #define GTM_SEM_POST(POSIX_SEM, RC) \ { \ RC = sem_post(POSIX_SEM); \ assert((-1 != RC) || (EINTR != errno)); \ } #endif fis-gtm-V7.0-005/sr_unix/gtm_semutils.c0000644000032200000250000002254014342376330016724 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2018 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_ipc.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "gtm_stdlib.h" #include "gtm_fcntl.h" #include "gtm_stdio.h" #include "gtm_stat.h" #include "gtm_signal.h" /* for kill(), SIGTERM, SIGQUIT */ #include "iosp.h" #include #include #include #include #include #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "eintr_wrapper_semop.h" #include "gtm_sem.h" #include "gtm_c_stack_trace.h" #include "gtm_semutils.h" #include "gtmimagename.h" #include "do_semop.h" #include "filestruct.h" GBLREF uint4 process_id; GBLREF bool in_mupip_freeze; error_def(ERR_CRITSEMFAIL); error_def(ERR_SEMWT2LONG); error_def(ERR_RESRCWAIT); error_def(ERR_RESRCINTRLCKBYPAS); error_def(ERR_TEXT); #define FULLTIME(MAX_HRTBT_DELTA, START_HRTBT_CNTR) (MAX_HRTBT_DELTA - 1 == (heartbeat_counter - START_HRTBT_CNTR)) #define HALFTIME(MAX_HRTBT_DELTA, START_HRTBT_CNTR) (MAX_HRTBT_DELTA / 2 == (heartbeat_counter - START_HRTBT_CNTR)) #define STACKTRACE_TIME(MAX_HRTBT_DELTA, START_HRTBT_CNTR) (HALFTIME(MAX_HRTBT_DELTA, START_HRTBT_CNTR) || \ FULLTIME(MAX_HRTBT_DELTA, START_HRTBT_CNTR)) #define USER_SPECIFIED_TIME_EXPIRED(MAX_HRTBT_DELTA, START_HRTBT_CNTR) (MAX_HRTBT_DELTA <= (heartbeat_counter - START_HRTBT_CNTR)) #define IS_ACCESS_SEM (gtm_access_sem == semtype) #define IS_FTOK_SEM (gtm_ftok_sem == semtype) boolean_t do_blocking_semop(int semid, enum gtm_semtype semtype, boolean_t *stacktrace_time, boolean_t *timedout, semwait_status_t *retstat, gd_region *reg, boolean_t *bypass, boolean_t *sem_halted, boolean_t incr_cnt) { boolean_t need_stacktrace, indefinite_wait; char *msgstr; int status = SS_NORMAL, save_errno, sem_pid = 0, semval, i, sopcnt; uint4 loopcnt = 0, max_hrtbt_delta, lcl_hrtbt_cntr, stuck_cnt = 0; boolean_t stacktrace_issued = FALSE, ok_to_bypass; struct sembuf sop[3]; char *sem_names[2] = {"FTOK", "access control"}; /* based on gtm_semtype enum order */ DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(IS_FTOK_SEM || IS_ACCESS_SEM); assert(NO_SEMWAIT_ON_EAGAIN != TREF(dbinit_max_delta_secs)); assert(!((NULL == timedout) ^ (NULL == stacktrace_time))); *sem_halted = FALSE; /* Access control semaphore should not be increased when the process is readonly */ SET_GTM_SOP_ARRAY(sop, sopcnt, (incr_cnt && (IS_FTOK_SEM || !reg->read_only)), (SEM_UNDO | IPC_NOWAIT)); /* If DSE or LKE or MUPIP FREEZE -ONLINE, it is okay to bypass but only if input "*bypass" is TRUE. * If "*bypass" is FALSE, that overrides anything else. */ ok_to_bypass = *bypass && (IS_DSE_IMAGE || IS_LKE_IMAGE || (IS_MUPIP_IMAGE && in_mupip_freeze)); *bypass = FALSE; /* set default value of "*bypass" first. Can be overridden later if needed */ indefinite_wait = (NULL == timedout); need_stacktrace = (DEFAULT_DBINIT_MAX_DELTA_SECS <= TREF(dbinit_max_delta_secs)) && !ok_to_bypass; if (!need_stacktrace) { /* Since the user specified wait time is less than the default wait, wait that time without any stack trace */ if (ok_to_bypass) { /* ok_to_bypass == TRUE means we can bypass after 3 seconds of wait. IPC_NOWAIT semop every second. * The semaphore value must be at least 2 to make sure the shared memeory is already created. */ if (-1 == (semval = semctl(semid, DB_COUNTER_SEM, GETVAL))) /* semval = number of process attached */ RETURN_SEMWAIT_FAILURE(retstat, errno, op_semctl, ERR_CRITSEMFAIL, 0, 0); if (DB_COUNTER_SEM_INCR < semval) { if (-1 == (sem_pid = semctl(semid, 0, GETPID))) RETURN_SEMWAIT_FAILURE(retstat, errno, op_semctl, ERR_CRITSEMFAIL, 0, 0); i = 0; do { SEMOP(semid, sop, sopcnt, status, NO_WAIT); save_errno = errno; if ((-1 == status) && (ERANGE == save_errno)) { if (!(*sem_halted)) { sopcnt = 2; /* ignore the increment operation */ *sem_halted = TRUE; continue; /* Try again */ } } else { if (0 == i) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_RESRCWAIT, 8, LEN_AND_STR(sem_names[semtype]), REG_LEN_STR(reg), DB_LEN_STR(reg), sem_pid, semid); } LONG_SLEEP(1); i++; } } while ((-1 == status) && ((EAGAIN == save_errno) || (ERANGE == save_errno)) && (i < MAX_BYPASS_WAIT_SEC)); if (-1 != status) return TRUE; assert(EINTR != save_errno); if (EAGAIN == save_errno) { *bypass = TRUE; save_errno = 0; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_RESRCINTRLCKBYPAS, 10, LEN_AND_STR((IS_LKE_IMAGE ? "LKE" : "DSE")), process_id, LEN_AND_STR(sem_names[semtype]), REG_LEN_STR(reg), DB_LEN_STR(reg), sem_pid); /* If this is a readonly access, we don't increment access semaphore's counter. See * SET_GTM_SOP_ARRAY definition in gtm_semutils.h and how it is called from db_init(). */ if (!(*sem_halted) && incr_cnt && (IS_FTOK_SEM || !reg->read_only)) { /* Increase the counter semaphore. */ save_errno = do_semop(semid, DB_COUNTER_SEM, DB_COUNTER_SEM_INCR, SEM_UNDO); if (save_errno == 0) return TRUE; else if (ERANGE == save_errno) { *sem_halted = TRUE; return TRUE; } *bypass = FALSE; /* Semaphore removed when attempting to bypass. Abort bypass. */ } else return TRUE; } } } if (!ok_to_bypass || (semval <= DB_COUNTER_SEM_INCR)) { /* Do not bypass. (We are not LKE/DSE OR) OR (There are less than 2 processes inside) */ sop[0].sem_flg = sop[1].sem_flg = sop[2].sem_flg = SEM_UNDO; /* Enable blocking wait. */ do { status = semop(semid, sop, sopcnt); save_errno = errno; if ((-1 == status) && (ERANGE == save_errno)) { if (!(*sem_halted)) { sopcnt = 2; /* ignore the increment operation */ *sem_halted = TRUE; continue; /* Try again */ } } } while ((-1 == status) && ((EINTR == save_errno) || (ERANGE == save_errno)) && (indefinite_wait || !*timedout)); if (-1 != status) return TRUE; /* someone else is holding it and we are done waiting */ sem_pid = semctl(semid, 0, GETPID); if (-1 != sem_pid) RETURN_SEMWAIT_FAILURE(retstat, 0, op_invalid_sem_syscall, ERR_SEMWT2LONG, 0, sem_pid); } } else { sop[0].sem_flg = sop[1].sem_flg = sop[2].sem_flg = SEM_UNDO; /* Enable blocking wait. */ do { loopcnt++; status = semop(semid, sop, sopcnt); if (-1 != status) { SENDMSG_SEMOP_SUCCESS_IF_NEEDED(stacktrace_issued, semtype); return TRUE; } save_errno = errno; if ((ERANGE == save_errno) && !(*sem_halted)) { sopcnt = 2; /* ignore the increment operation */ *sem_halted = TRUE; loopcnt--; /* do not count this attempt */ continue; /* retry semop */ } else if (EINTR != save_errno) break; sem_pid = semctl(semid, 0, GETPID); if (-1 != sem_pid) { if (!indefinite_wait && (*stacktrace_time || *timedout)) { if (*stacktrace_time) *stacktrace_time = FALSE; /* Prevent re-issuing midpoint trace */ stuck_cnt++; if (IS_FTOK_SEM) msgstr = (1 == stuck_cnt) ? "SEMWT2LONG_FTOK_INFO" : "SEMWT2LONG_FTOK"; else msgstr = (1 == stuck_cnt) ? "SEMWT2LONG_ACCSEM_INFO" : "SEMWT2LONG_ACCSEM"; if ((0 != sem_pid) && (sem_pid != process_id)) { GET_C_STACK_FROM_SCRIPT(msgstr, process_id, sem_pid, stuck_cnt); if (TREF(gtm_environment_init)) stacktrace_issued = TRUE; } } continue; } save_errno = errno; /* for the failed semctl, fall-through */ break; } while (indefinite_wait || !*timedout); if ((0 == loopcnt) || (EINTR == save_errno)) { /* the timer has expired */ if (!indefinite_wait && !TREF(gtm_environment_init)) RETURN_SEMWAIT_FAILURE(retstat, 0, op_invalid_sem_syscall, ERR_SEMWT2LONG, 0, sem_pid); SEMOP(semid, sop, sopcnt, status, NO_WAIT); /* ignore EINTR if asked for indefinite wait or run in-house */ if (-1 != status) { SENDMSG_SEMOP_SUCCESS_IF_NEEDED(stacktrace_issued, semtype); return TRUE; } save_errno = errno; if (TREF(gtm_environment_init) && SEM_REMOVED(save_errno)) { /* If the semaphore is removed (possible by a concurrent gds_rundown), the caller will retry * by doing semget once again. In most cases this will succeed and the process will get hold * of the semaphore and so the SEM_REMOVED condition can be treated as if the semop succeeded * So, log a SEMOP success message in the syslog. */ SENDMSG_SEMOP_SUCCESS_IF_NEEDED(stacktrace_issued, semtype); } else if ((!*sem_halted) && (ERANGE == save_errno)) { *sem_halted = TRUE; RETURN_SEMWAIT_FAILURE(retstat, 0, op_invalid_sem_syscall, ERR_SEMWT2LONG, 0, sem_pid); } /* else some other error occurred; fall-through */ } /* else some other error occurred; fall-through */ } assert(0 != save_errno); RETURN_SEMWAIT_FAILURE(retstat, save_errno, op_semctl_or_semop, ERR_CRITSEMFAIL, 0, 0); } fis-gtm-V7.0-005/sr_unix/gtm_semutils.h0000644000032200000250000002002514342376330016725 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2017 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. * * * ****************************************************************/ #ifndef GTM_SEMUTILS_H #define GTM_SEMUTILS_H #include /* Database startup wait related macros */ #define DEFAULT_DBINIT_MAX_DELTA_SECS 96 #define NO_SEMWAIT_ON_EAGAIN 0 #define INDEFINITE_WAIT_ON_EAGAIN (uint4) -1 #define MAX_BYPASS_WAIT_SEC 3 #define MAX_C_STACK_TRACES_FOR_SEMWAIT 2 #define DB_CONTROL_SEM 0 #define DB_COUNTER_SEM 1 #define DEFAULT_DB_COUNTER_SEM_INCR 1 #ifdef DEBUG GBLREF int gtm_db_counter_sem_incr; # define DB_COUNTER_SEM_INCR gtm_db_counter_sem_incr #else #define DB_COUNTER_SEM_INCR DEFAULT_DB_COUNTER_SEM_INCR #endif error_def(ERR_CRITSEMFAIL); error_def(ERR_DBFILERR); error_def(ERR_FTOKERR); error_def(ERR_MAXSEMGETRETRY); error_def(ERR_SEMKEYINUSE); error_def(ERR_SEMWT2LONG); error_def(ERR_SYSCALL); /* Possible semaphore functions that can fail */ enum sem_syscalls { op_invalid_sem_syscall, op_ftok, op_semget, op_semop, op_semctl, op_semctl_or_semop }; enum gtm_semtype { gtm_ftok_sem, gtm_access_sem }; typedef struct semwait_status_struct { int line_no; int save_errno; /* This value must be checked/assigned only errors. May not be 0 on success. */ int status1; int status2; int sem_pid; const char *module; enum sem_syscalls op; } semwait_status_t; boolean_t do_blocking_semop(int semid, enum gtm_semtype semtype, boolean_t *stacktrace_time, boolean_t *timedout, semwait_status_t *status, gd_region *reg, boolean_t *bypass, boolean_t *sem_halted, boolean_t incr_sem); #define SENDMSG_SEMOP_SUCCESS_IF_NEEDED(STACKTRACE_ISSUED, SEMTYPE) \ { \ if (TREF(gtm_environment_init) && STACKTRACE_ISSUED) \ { \ const char *lcl_msgstr = NULL; \ \ lcl_msgstr = (gtm_ftok_sem == SEMTYPE) ? "SEMWT2LONG_FTOK_SUCCEEDED: semop for the ftok semaphore succeeded" \ : "SEMWT2LONG_ACCSEM_SUCCEEDED: semop for the access semaphore succeeded";\ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_STR(lcl_msgstr)); \ } \ } #define DBFILERR_PARAMS(REG) ERR_DBFILERR, 2, DB_LEN_STR(REG) #define CRITSEMFAIL_PARAMS(REG) ERR_CRITSEMFAIL,2, DB_LEN_STR(REG) #define SYSCALL_PARAMS(RETSTAT, OP) ERR_SYSCALL, 5, LEN_AND_STR(OP), LEN_AND_STR((RETSTAT)->module), (RETSTAT)->line_no #define SEMKEYINUSE_PARAMS(UDI) ERR_SEMKEYINUSE, 1, UDI->key #define SEMWT2LONG_PARAMS(REG, RETSTAT, GTM_SEMTYPE, TOT_WAIT_TIME) \ ERR_SEMWT2LONG, 7, process_id, TOT_WAIT_TIME, \ LEN_AND_LIT(GTM_SEMTYPE), DB_LEN_STR(REG), (RETSTAT)->sem_pid #define GET_OP_STR(RETSTAT, OP) \ { \ switch ((RETSTAT)->op) \ { \ case op_semget: \ OP = "semget()"; \ break; \ case op_semop: \ OP = "semop()"; \ break; \ case op_semctl: \ OP = "semctl()"; \ break; \ case op_semctl_or_semop: \ OP = "semctl()/semop()"; \ break; \ default: \ OP = ""; \ assert(FALSE); \ } \ } #define ISSUE_SEMWAIT_ERROR(RETSTAT, REG, UDI, GTM_SEMTYPE) SEMWAIT_ERROR_COMMON(RETSTAT, REG, UDI, GTM_SEMTYPE, rts_error_csa) #define PRINT_SEMWAIT_ERROR(RETSTAT, REG, UDI, GTM_SEMTYPE) SEMWAIT_ERROR_COMMON(RETSTAT, REG, UDI, GTM_SEMTYPE, gtm_putmsg_csa) #define SEMWAIT_ERROR_COMMON(RETSTAT, REG, UDI, GTM_SEMTYPE, REPORT_FN) \ MBSTART { \ const char *op; \ uint4 tot_wait_time; \ \ GBLREF uint4 process_id; \ \ if (ERR_CRITSEMFAIL == (RETSTAT)->status2) \ { \ if (0 == (RETSTAT)->status1) \ { \ GET_OP_STR(RETSTAT, op); \ REPORT_FN(CSA_ARG(NULL) VARLSTCNT(16) DBFILERR_PARAMS(REG), \ CRITSEMFAIL_PARAMS(REG), SYSCALL_PARAMS(RETSTAT, op), \ (RETSTAT)->save_errno); \ } else if (ERR_SEMKEYINUSE == (RETSTAT)->status1) \ { \ REPORT_FN(CSA_ARG(NULL) VARLSTCNT(11) DBFILERR_PARAMS(REG), CRITSEMFAIL_PARAMS(REG), \ SEMKEYINUSE_PARAMS(UDI)); \ } else \ assertpro(FALSE); \ } else if (ERR_MAXSEMGETRETRY == (RETSTAT)->status2) \ { \ REPORT_FN(CSA_ARG(NULL) VARLSTCNT(7) DBFILERR_PARAMS(REG), ERR_MAXSEMGETRETRY, 1, \ MAX_SEMGET_RETRIES); \ } else if (ERR_FTOKERR == (RETSTAT)->status2) \ { \ REPORT_FN(CSA_ARG(NULL) VARLSTCNT(9) DBFILERR_PARAMS(REG), ERR_FTOKERR, 2, DB_LEN_STR(REG), \ (RETSTAT)->save_errno); \ } else if (0 == (RETSTAT)->status2) \ { \ assert(ERR_SEMWT2LONG == (RETSTAT)->status1); \ assert((RETSTAT)->sem_pid && (-1 != (RETSTAT)->sem_pid)); \ tot_wait_time = TREF(dbinit_max_delta_secs); \ REPORT_FN(CSA_ARG(NULL) VARLSTCNT(13) DBFILERR_PARAMS(REG), \ SEMWT2LONG_PARAMS(REG, RETSTAT, GTM_SEMTYPE, tot_wait_time)); \ } else \ assertpro(FALSE); \ } MBEND /* Set the value of semaphore number 2 ( = FTOK_SEM_PER_ID - 1) as GTM_ID. This way, in case of an orphaned * semaphore (say, kill -9), MUPIP RUNDOWN will be able to identify GT.M semaphore from the value and will * remove it. */ #define SET_GTM_ID_SEM(SEMID, RC) \ { \ union semun semarg; \ \ semarg.val = GTM_ID; \ RC = semctl(SEMID, FTOK_SEM_PER_ID - 1, SETVAL, semarg); \ } #define FTOK_SOPCNT_NO_INCR_COUNTER 2 #define FTOK_SOPCNT_INCR_COUNTER 3 /* Set up typical GT.M semaphore (access control semaphore and/or ftok semaphore) */ #define SET_GTM_SOP_ARRAY(SOP, SOPCNT, INCR_CNT, SEMFLG) \ { \ /* Typically, multiple statements are not specified in a single line. However, each of the 2 lines below represent \ * "one" semaphore operation and hence an acceptible exception to the coding guidelines. \ */ \ SOP[0].sem_num = DB_CONTROL_SEM; SOP[0].sem_op = 0; /* Wait for 0 (unlocked) */ \ SOP[1].sem_num = DB_CONTROL_SEM; SOP[1].sem_op = 1; /* Then lock it */ \ if (INCR_CNT) \ { \ assert(2 == FTOK_SOPCNT_NO_INCR_COUNTER); \ SOP[2].sem_num = DB_COUNTER_SEM; SOP[2].sem_op = DB_COUNTER_SEM_INCR;/* Increment counter semaphore */ \ SOPCNT = FTOK_SOPCNT_INCR_COUNTER; \ } else \ SOPCNT = FTOK_SOPCNT_NO_INCR_COUNTER; \ SOP[0].sem_flg = SOP[1].sem_flg = SOP[2].sem_flg = SEMFLG; \ } #define SET_SOP_ARRAY_FOR_DECR_CNT(SOP, SOPCNT, SEMFLG) \ { \ /* Typically, multiple statements are not specified in a single line. However, each of the 2 lines below represent \ * "one" semaphore operation and hence an acceptible exception to the coding guidelines. \ */ \ SOP[0].sem_num = DB_COUNTER_SEM; SOP[0].sem_op = -DB_COUNTER_SEM_INCR; /* Decrement counter semaphore */ \ SOPCNT = 1; \ SOP[0].sem_flg = SEMFLG; \ } #define SET_SEMWAIT_FAILURE_RETSTAT(RETSTAT, ERRNO, OP, STATUS1, STATUS2, SEMPID) \ { \ (RETSTAT)->line_no = __LINE__; \ (RETSTAT)->save_errno = ERRNO; \ (RETSTAT)->op = OP; \ (RETSTAT)->status1 = STATUS1; \ (RETSTAT)->status2 = STATUS2; \ (RETSTAT)->sem_pid = SEMPID; \ (RETSTAT)->module = __FILE__; \ } #define RETURN_SEMWAIT_FAILURE(RETSTAT, ERRNO, OP, STATUS1, STATUS2, SEMPID) \ { \ SET_SEMWAIT_FAILURE_RETSTAT(RETSTAT, ERRNO, OP, STATUS1, STATUS2, SEMPID); \ return FALSE; \ } #endif /* GTM_SEMUTILS_H */ fis-gtm-V7.0-005/sr_unix/gtm_signal.h0000644000032200000250000000220014342376330016330 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015 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. * * * ****************************************************************/ #ifndef GTM_SIGNAL_H #define GTM_SIGNAL_H #include /* BYPASSOK(gtm_signal.h) */ #include "gtm_multi_thread.h" /* for INSIDE_THREADED_CODE macro */ #define SIGPROCMASK(FUNC, NEWSET, OLDSET, RC) \ MBSTART { \ GBLREF boolean_t multi_thread_in_use; \ \ char *rname; \ \ /* Use the right system call based on threads are in use or not */ \ if (!INSIDE_THREADED_CODE(rname)) \ RC = sigprocmask(FUNC, NEWSET, OLDSET); /* BYPASSOK(sigprocmask) */ \ else \ RC = pthread_sigmask(FUNC, NEWSET, OLDSET); \ assert(0 == RC); \ } MBEND #endif fis-gtm-V7.0-005/sr_unix/gtm_startup.h0000755000032200000250000000142114342376330016564 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2006 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. * * * ****************************************************************/ #ifndef GTM_STARTUP_INCLUDED #define GTM_STARTUP_INCLUDED void gtm_startup(struct startup_vector *svec); void init_gtm(void); void gtm_init_env(rhdtyp* base_addr, unsigned char* transfer_addr); void lref_parse(unsigned char* lref, mstr* rtn, mstr* lab, int* offset); void gtm_utf8_init(void); #endif /* GTM_STARTUP_INCLUDED */ fis-gtm-V7.0-005/sr_unix/gtm_startup_chk.c0000644000032200000250000001222714342376330017407 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /************************************************************************************ * Current GT.M operation on Unix depends on $gtm_dist environment variable * being set correctly or it cannot communicate with gtmsecshr. If this * communication fails, it can cause many problems. * This is the startup checking including the following: * gtm_chk_dist() * * 1. $gtm_dist is defined * * 2. Ensure that the executable resides in $gtm_dist. This provides enough * assurance that $gtm_dist is suitable for use. * * This comparison is done using the inode of the executable in $gtm_dist * and the discovered executable path, either from /proc or other OS * function. Using argv[0] wasn't possible when $gtm_dist is in the $PATH. * * The GT.CM servers, OMI and GNP, always defer issuing an error for an * unverified $gtm_dist. All other executables, MUMPS, MUPIP, DSE, and LKE * issue the error messages as soon as possible. * * 3. Checking that $gtm_dist/gtmsecshr was relocated to secshr_client() ************************************************************************************/ #include "mdef.h" #include #include "gtm_string.h" #include "gtm_stat.h" #include "gtm_stdlib.h" #include "gtm_unistd.h" #include "gtm_limits.h" #include "gdsroot.h" #include "error.h" #include "parse_file.h" #include "is_file_identical.h" #include "gtm_startup_chk.h" #include "gtmimagename.h" #include "have_crit.h" #include "gtm_post_startup_check_init.h" GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF unsigned int gtm_dist_len; GBLREF boolean_t gtm_dist_ok_to_use; LITREF gtmImageName gtmImageNames[]; GBLREF uint4 process_id; error_def(ERR_DISTPATHMAX); error_def(ERR_SYSCALL); error_def(ERR_GTMDISTUNDEF); error_def(ERR_GTMDISTUNVERIF); error_def(ERR_FILEPARSE); error_def(ERR_MAXGTMPATH); error_def(ERR_IMAGENAME); error_def(ERR_TEXT); int gtm_chk_dist(char *image) { char *real_dist; char *imagepath; char *exename; int exename_len; int path_len; boolean_t status; boolean_t is_gtcm_image; char image_real_path[GTM_PATH_MAX]; char real_gtm_dist_path[GTM_PATH_MAX]; char comparison[GTM_PATH_MAX]; unsigned int real_dist_len; is_gtcm_image = (IS_GTCM_GNP_SERVER_IMAGE || IS_GTCM_SERVER_IMAGE); /* GT.CM servers defer issuing errors until startup */ /* Use the real path while checking the path length. If not valid, let it fail when checking is_file_identical */ real_dist = realpath(gtm_dist, real_gtm_dist_path); if (real_dist) { STRNLEN(real_dist, GTM_PATH_MAX, real_dist_len); } else { # ifdef DEBUG STRNLEN(gtm_dist, GTM_PATH_MAX, real_dist_len); /* Set real_dist_len */ assert(real_dist_len == gtm_dist_len); # endif real_dist_len = gtm_dist_len; } if (real_dist_len) { assert(IS_VALID_IMAGE && (n_image_types > image_type)); /* assert image_type is initialized */ if (GTM_DIST_PATH_MAX <= real_dist_len) { if (is_gtcm_image) return 0; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_DISTPATHMAX, 1, GTM_DIST_PATH_MAX); } } else { if (is_gtcm_image) return 0; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_GTMDISTUNDEF); } /* Get the executable name and length */ exename = strrchr(image, '/'); if (!exename) /* no slash found, then image is just the exe's name */ { exename = image; } else /* slash found in the path, advance the pointer by one to get the name */ exename++; STRNLEN(exename, GTM_PATH_MAX, exename_len); status = gtm_image_path(image_real_path); if (status) /* On HP-UX it's possible for the underlying system call to return an error. Fall back to realpath() */ { /* Use the return of realpath(). If it returns null for argv[0], just use image and let the check fail */ imagepath = realpath(image, image_real_path); if (NULL == imagepath) imagepath = image; } else imagepath = image_real_path; /* create the comparison path (gtm_dist + '/' + exename + '\0') and compare it to imagepath */ SNPRINTF(comparison, GTM_PATH_MAX, "%s/%s", gtm_dist, exename); status = is_file_identical(imagepath, comparison); if (status) gtm_dist_ok_to_use = TRUE; else { if (is_gtcm_image) return 0; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_GTMDISTUNVERIF, 4, LEN_AND_STR(gtm_dist), LEN_AND_STR(image)); } if (IS_GTM_IMAGE && memcmp(exename, GTM_IMAGE_NAME, GTM_IMAGE_NAMELEN)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_IMAGENAME, 4, LEN_AND_LIT(GTM_IMAGE_NAME), LEN_AND_STR(exename)); gtm_post_startup_check_init(); return 0; } int gtm_image_path(char *realpath) { #if defined(__linux__) || defined(__sparc) || defined(_AIX) || defined(__CYGWIN__) SNPRINTF(realpath, GTM_PATH_MAX, PROCSELF, process_id); #else # error "Unsupported platform : no way to determine the true exe path" #endif return 0; } fis-gtm-V7.0-005/sr_unix/gtm_startup_chk.h0000755000032200000250000000155114342376330017415 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GTM_STARTUP_CHK_H_INCLUDED #define GTM_STARTUP_CHK_H_INCLUDED #if defined(__linux__) || defined(__CYGWIN__) #define PROCSELF "/proc/self/exe" #elif defined(__sparc) #define PROCSELF "/proc/%d/path/a.out" #elif defined(_AIX) #define PROCSELF "/proc/%d/object/a.out" #endif int gtm_chk_dist(char *image); int gtm_image_path(char *realpath); #endif fis-gtm-V7.0-005/sr_unix/gtm_statvfs.h0000755000032200000250000000201714342376330016556 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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. * * * ****************************************************************/ /* gtm_statvfs.h - interlude to system header file. */ #ifndef GTM_STATVFSH #define GTM_STATVFSH #include #define STATVFS(pathname,fsinfo,statvfs_res) (statvfs_res = statvfs(pathname, fsinfo)) #define FSTATVFS(filedesc,fstatvfsinfo,fstatvfs_res) \ { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_FSTAT, prev_intrpt_state); \ fstatvfs_res = fstatvfs(filedesc, fstatvfsinfo); \ ENABLE_INTERRUPTS(INTRPT_IN_FSTAT, prev_intrpt_state); \ } #endif fis-gtm-V7.0-005/sr_unix/gtm_stdio.c0000644000032200000250000000703414342376330016202 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2020 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 "have_crit.h" #include "eintr_wrappers.h" #include "gtm_stdio.h" #include "gtm_signal.h" #include #include /* Collection of eintr wrapper routines for gtm_stdio.h declared routines. Since all of the routines have variable * length parameter lists, it is not possible to do the wrappers using macros. Some routines may also have additional * signal blocking implemented to prevent the routines from being re-entered due to signals (known to cause issues on * some platforms. * * Note for the routines that utilize signal blocks, blocksig_initialized may not be set at this point for a variety of * cases such one as the following: * 1) At startup of GTCM_SERVER or GTCM_GNP_SERVER * 2) At startup of GT.M (e.g. bad command line "mumps -bad") * Because of this, we dont have an assert(blocksig_initialized) that similar code in dollarh.c has. * * Routines implemented: * gtm_printf() * gtm_fprintf() * gtm_sprintf() * gtm_snprintf() * gtm_scanf() * gtm_fscanf() * gtm_sscanf() */ GBLREF boolean_t blocksig_initialized; GBLREF sigset_t block_sigsent; /* Wrapper for printf() */ int gtm_printf(const char *format, ...) { va_list printargs; int retval; va_start(printargs, format); VPRINTF(format, printargs, retval); retval = (-1 == retval) ? errno : 0; va_end(printargs); return retval; } /* Wrapper for fprintf() */ int gtm_fprintf(FILE *stream, const char *format, ...) { va_list printargs, pa_copy; size_t retval, buflen; sigset_t savemask; intrpt_state_t prev_intrpt_state; retval = 0; va_start(printargs, format); VAR_COPY(pa_copy, printargs); VSNPRINTF(NULL, 0, format, pa_copy, buflen); /* C99: NULL string just returns size */ va_end(pa_copy); { char buf[buflen + 1]; /* C99: Variable Length Array, avoids malloc. */ VSNPRINTF(buf, SIZEOF(buf), format, printargs, buflen); GTM_FWRITE(buf, buflen, 1, stream, buflen, retval); } va_end(printargs); assert(-1 != retval); return retval; } /* Wrapper for sprintf() - unneeded / unwanted as it is not safe */ /* Wrapper for snprintf() */ int gtm_snprintf(char *str, size_t size, const char *format, ...) { va_list printargs; int retval; va_start(printargs, format); VSNPRINTF(str, size, format, printargs, retval); va_end(printargs); assert(-1 != retval); return retval; } /* Note these functions do not exist on TRU64 (OSF1) */ #ifndef __osf__ /* Wrapper for scanf() */ int gtm_scanf(const char *format, ...) { va_list scanargs; int retval; va_start(scanargs, format); VSCANF(format, scanargs, retval); va_end(scanargs); return retval; } /* Wrapper for fscanf() */ int gtm_fscanf(FILE *stream, const char *format, ...) { va_list scanargs; int retval; va_start(scanargs, format); VFSCANF(stream, format, scanargs, retval); va_end(scanargs); return retval; } /* Wrapper for sscanf() */ int gtm_sscanf(char *str, const char *format, ...) { va_list scanargs; int retval; va_start(scanargs, format); VSSCANF(str, format, scanargs, retval); va_end(scanargs); return retval; } #endif fis-gtm-V7.0-005/sr_unix/gtm_stdio.h0000755000032200000250000001260714342376330016214 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2018 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. * * * ****************************************************************/ /* gtm_stdio.h - gtm interface to stdio.h */ #ifndef GTM_STDIOH #define GTM_STDIOH /* This header is split between sr_unix and sr_vvms because there are several test system and standalone modules * that do not #define UNIX or VMS for us to know which defines to proceed with. So now this split makes * that determination unnecessary. Note we still use the definition of UNIX or not in THIS header to indicate the * compilation of a GTM source file or a standalone file not needing (or able to get to) libgtmshr wrappers. */ #include #ifdef UNIX /* If interrupted, the following functions have previously caused hangs, so defer interrupts for their duration to be safe. However, * since gtm_stdio.h may be included in a non-GT.M compilation, we define the macros differently based on the UNIX compiler switch, * which should only be defined within GT.M. */ # define FDOPEN(VAR, FILE_DES, MODE) \ { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_FDOPEN, prev_intrpt_state); \ VAR = fdopen(FILE_DES, MODE); \ ENABLE_INTERRUPTS(INTRPT_IN_FDOPEN, prev_intrpt_state); \ } /* Fopen() is not fully capitalized because there is an FOPEN() macro on AIX. */ # define Fopen(VAR, PATH, MODE) \ { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); \ VAR = fopen(PATH, MODE); \ ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); \ } #else # define FDOPEN(VAR, FILE_DES, MODE) \ { \ VAR = fdopen(FILD_DES, MODE); \ } # define Fopen(VAR, PATH, MODE) \ { \ VAR = fopen(PATH, MODE); \ } #endif #define FGETS(strg, n, strm, fgets_res) (fgets_res = fgets(strg,n,strm)) #define GETS(buffer, gets_res) syntax error #define PERROR perror #define POPEN popen #define TEMPNAM tempnam #ifndef P_tmpdir #define P_tmpdir "/tmp" #endif #define DEFAULT_GTM_TMP P_tmpdir #define RENAME rename #define SETVBUF setvbuf #ifdef UNIX /* We are compiling a GTM source module if UNIX is defined */ # define FPRINTF gtm_fprintf # define PRINTF gtm_printf # define SPRINTF(STR, ...) assert(FALSE); /* don't want to use invocation prone to buffer overruns */ # define SNPRINTF gtm_snprintf int gtm_printf(const char *format, ...); int gtm_fprintf(FILE *stream, const char *format, ...); int gtm_snprintf(char *str, size_t size, const char *format, ...); #else /* We are compiling a standalone or test system module so no override (This is NOT VMS) */ # define FPRINTF fprintf # define PRINTF printf # define SPRINTF sprintf # define SNPRINTF snprintf #endif /* Similar to above for *scanf invocations. Note however that TRU64 does NOT have * the v*scanf functions used by the wrappers so always use the non-wrapper versions. */ #if defined(UNIX) && !defined(__osf__) # define SCANF gtm_scanf # define SSCANF gtm_sscanf # define FSCANF gtm_fscanf int gtm_scanf(const char *format, ...); int gtm_fscanf(FILE *stream, const char *format, ...); int gtm_sscanf(char *str, const char *format, ...); #else # define SCANF scanf # define SSCANF sscanf # define FSCANF fscanf #endif #define VPRINTF(FORMAT, VALUE, RC) \ { \ do \ { \ RC = vprintf(FORMAT, VALUE); \ } while(-1 == RC && EINTR == errno); \ } #define VFPRINTF(STREAM, FORMAT, VALUE, RC) \ { \ do \ { \ RC = vfprintf(STREAM, FORMAT, VALUE); \ } while(-1 == RC && EINTR == errno); \ } #define VSPRINTF(STRING, FORMAT, VALUE, RC) \ { \ do \ { \ RC = vsprintf(STRING, FORMAT, VALUE); \ } while(-1 == RC && EINTR == errno); \ } #define VSNPRINTF(STRING, SIZE, FORMAT, VALUE, RC) \ { \ do \ { \ RC = vsnprintf(STRING, SIZE, FORMAT, VALUE); \ } while(-1 == RC && EINTR == errno); \ } /* Note TRU64 does not have these v*scanf() functions so they will generate errors if used */ #define VSCANF(FORMAT, POINTER, RC) \ { \ do \ { \ RC = vscanf(FORMAT, POINTER); \ } while(-1 == RC && EINTR == errno); \ } #define VSSCANF(STRING, FORMAT, POINTER, RC) \ { \ do \ { \ RC = vsscanf(STRING, FORMAT, POINTER); \ } while(-1 == RC && EINTR == errno); \ } #define VFSCANF(STREAM, FORMAT, POINTER, RC) \ { \ do \ { \ RC = vfscanf(STREAM, FORMAT, POINTER); \ } while(-1 == RC && EINTR == errno); \ } #define SNPRINTF_ENV_NUM(BUFF, LEN, ENV_VAR, ENV_VAL, ENV_IND) \ { \ assert(NULL == strchr(ENV_VAR, '=')); /* strchr() done in ojstartchild() relies on this */ \ SNPRINTF(BUFF, LEN, "%s=%d", ENV_VAR, ENV_VAL); *ENV_IND++ = BUFF; \ } #define SNPRINTF_ENV_STR(BUFF, LEN, ENV_VAR, ENV_VAL, ENV_IND) \ { \ assert(NULL == strchr(ENV_VAR, '=')); /* strchr() done in ojstartchild() relies on this */ \ SNPRINTF(BUFF, LEN, "%s=%s", ENV_VAR, ENV_VAL); *ENV_IND++ = BUFF; \ } #endif fis-gtm-V7.0-005/sr_unix/gtm_syslog.h0000755000032200000250000000122714342376330016406 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ /* gtm_syslog.h - interlude to system header file. */ #ifndef GTM_SYSLOGH #define GTM_SYSLOGH #include #define OPENLOG openlog #define SYSLOG syslog #define CLOSELOG closelog #endif fis-gtm-V7.0-005/sr_unix/gtm_system.c0000644000032200000250000000561714342376330016411 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2018 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_unistd.h" #include "gtm_stdlib.h" #include "gtm_signal.h" #include "gtm_string.h" #include #include #include "have_crit.h" #include "fork_init.h" #include "eintr_wrappers.h" #define RESTOREMASK(RC) \ { \ sigaction(SIGINT, &old_intrpt, NULL); \ sigaction(SIGQUIT, &old_quit, NULL); \ SIGPROCMASK(SIG_SETMASK, &savemask, NULL, RC); \ } error_def(ERR_INVSTRLEN); int gtm_system(const char *cmdline) { return gtm_system_internal(NULL, NULL, NULL, cmdline); } int gtm_system_internal(const char *sh, const char *opt, const char *rtn, const char *cmdline) { struct sigaction ignore, old_intrpt, old_quit; sigset_t mask, savemask; pid_t pid; int stat; /* child exit status */ int rc, ret; /* return value from waitpid */ int len, shlen; intrpt_state_t prev_intrpt_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DEFER_INTERRUPTS(INTRPT_IN_FORK_OR_SYSTEM, prev_intrpt_state); sigemptyset(&ignore.sa_mask); ignore.sa_handler = SIG_IGN; ignore.sa_flags = 0; if (sigaction(SIGINT, &ignore, &old_intrpt)) { ENABLE_INTERRUPTS(INTRPT_IN_FORK_OR_SYSTEM, prev_intrpt_state); return -1; } if (sigaction(SIGQUIT, &ignore, &old_quit)) { sigaction(SIGINT, &old_intrpt, NULL); ENABLE_INTERRUPTS(INTRPT_IN_FORK_OR_SYSTEM, prev_intrpt_state); return -1; } sigemptyset(&mask); sigaddset(&mask, SIGCHLD); SIGPROCMASK(SIG_BLOCK, &mask, &savemask, rc); if (rc) { sigaction(SIGINT, &old_intrpt, NULL); sigaction(SIGQUIT, &old_quit, NULL); ENABLE_INTERRUPTS(INTRPT_IN_FORK_OR_SYSTEM, prev_intrpt_state); return -1; } /* Shell and command options */ if (NULL == opt) opt = "-c"; if (NULL == sh) sh = GETENV("SHELL"); sh = (NULL == sh) || ('\0' == *sh) ? "/bin/sh" : sh; /* Below FORK is not used as interrupts are already disabled at the * beginning of this function */ pid = fork(); /* BYPASSOK */ if (0 > pid) { RESTOREMASK(rc); ENABLE_INTERRUPTS(INTRPT_IN_FORK_OR_SYSTEM, prev_intrpt_state); return -1; } else if (0 == pid) { RESTOREMASK(rc); assert('\0' != *cmdline); if (NULL == rtn) execl(sh, sh, opt, cmdline, NULL); else execl(sh, sh, opt, rtn, cmdline, NULL); UNDERSCORE_EXIT(127); } else { ENABLE_INTERRUPTS(INTRPT_IN_FORK_OR_SYSTEM, prev_intrpt_state); WAITPID(pid, &stat, 0, ret); if ((-1 == ret) && (EINTR != errno)) stat = -1; RESTOREMASK(rc); return stat; } } fis-gtm-V7.0-005/sr_unix/gtm_term.h0000755000032200000250000000123014342376330016027 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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. * * * ****************************************************************/ /* gtm_term.h - interlude to system header file. */ #ifndef GTM_TERMH #define GTM_TERMH #ifdef __CYGWIN__ #include #else #include #endif #define SETUPTERM setupterm #endif fis-gtm-V7.0-005/sr_unix/gtm_termios.h0000755000032200000250000000100714342376330016544 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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. * * * ****************************************************************/ #ifdef __MVS__ #define _POSIX_SOURCE #endif #include fis-gtm-V7.0-005/sr_unix/gtm_test_install.csh0000755000032200000250000002175414342376330020130 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2011-2020 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. # # # ################################################################# echo "" echo "------------------------------------------------------------------------" set arch = `grep arch $gtm_ver/pro/arch.gtc | awk -F= '{print $2}' | tr -d '\"'` cd $1 # Source the installed GT.M's configuration source ./gtmcshrc setenv gtmgbldir mumps.gld gde exit mupip create gtm << \EOF >&! gtm_test_install.out set ^X=1 set x=2 write $ORDER(^%),! zwrite zwrite ^X halt \EOF gtm << EOF >>&! gtm_test_install.out zhelp zwrite ^X halt EOF mkdir test_gtm cd test_gtm # create sim.m cat > sim.m <>&! $save_gtm_dist/gtm_test_install.out grep ZCHSET gtm.out >>&! $save_gtm_dist/gtm_test_install.out #get version number setenv gtmver `ls -d V*` # get journal output cd $gtmver/g setenv gtm_dist $save_gtm_dist $gtm_dist/mupip journal -extract -forward gtm.mjl >& mupip.out env gtmgbldir=$gtm_dist/test_gtm/mumps.gld $gtm_dist/mupip integ -reg "*" >& integ.out set mupip_status = $status # output the change lines echo "" >>&! $gtm_dist/gtm_test_install.out echo "Global changes in the gtm.mjl file:" >>&! $gtm_dist/gtm_test_install.out grep = gtm.mjf | awk -F\\ '{print ($NF)}' >>&! $gtm_dist/gtm_test_install.out if (0 != $mupip_status) then echo "Error: mupip integ returned $status status instead of 0" >>&! $gtm_dist/gtm_test_install.out else echo "mupip integ returned a 0 status - as expected" >>&! $gtm_dist/gtm_test_install.out endif cd $gtm_dist # If gtm_icu_version is empty, consider it equivalent to not set if ($?gtm_icu_version) then if ("" != $gtm_icu_version) then unsetenv gtm_icu_version endif endif set icu_version = "" set libpath = "" if (! $?gtm_icu_version) then # icu-config is deprecated. So try "pkg-config icu-io" first, followed by "icu-config" and "pkg-config icu" if ( (-X pkg-config) && ( { pkg-config --exists icu-io } ) ) then set versioncmd = "pkg-config --modversion icu-io" set libcmd="pkg-config --variable=libdir icu-io" else if (-X icu-config) then set versioncmd = "icu-config --version" set libcmd="icu-config --libdir" else if ( (-X pkg-config) && ( { pkg-config --exists icu } ) ) then set versioncmd = "pkg-config --modversion icu" set libcmd="pkg-config --variable=libdir icu" endif if ($?versioncmd) then # pkg-config/icu-config can report versions like 4.2.1 but we want just the 4.2 part for GT.M set icu_version = `$versioncmd | awk '{ver=+$0;if(ver>5){ver=ver/10}printf("%.1f\n",ver);exit}'` set libpath=`$libcmd` if ( "linux" == "$arch" ) then # We do not recommend setting gtm_icu_version on AIX setenv gtm_icu_version "$icu_version" endif endif endif # No files are supposed to have write permissions enabled. However part of the kit install # test leaves a few files writeable. The list of known writeable files is: # .: # -rw-r--r-- 1 root root 1387 Jul 10 11:54 gtm_test_install.out # -rw-rw-rw- 1 root root 366080 Jul 10 11:54 mumps.dat # -rw-r--r-- 1 root root 1536 Jul 10 11:54 mumps.gld # test_gtm: # -rw-r--r-- 1 root root 670 Jul 10 11:54 gtm.out # -rw-r--r-- 1 root root 1536 Jul 10 11:54 mumps.gld # -rw-r--r-- 1 root root 88 Jul 10 11:54 sim.m # -rw-r--r-- 1 root root 869 Jul 10 11:54 sim.o set msg2 = `ls -al * | grep -v \> | grep w- | wc -l` @ msg2 = $msg2 - 7 if ( $msg2 ) then echo "Number of writeable lines = $msg2" >>&! gtm_test_install.out ls -al * | grep -v \> | grep w- >>&! gtm_test_install.out endif setenv gtm_dist . gtm << EOF >>&! gtm_test_install.out write \$zchset EOF setenv gtm_dist utf8 setenv LD_LIBRARY_PATH $libpath setenv LIBPATH $libpath setenv gtm_chset utf-8 # depending on the list of locales configured, locale -a might be considered a binary output. # grep needs -a option to process the output as text but -a is not supported on the non-linux servers we have. if ( "linux" == "$arch" ) then set binaryopt = "-a" else set binaryopt = "" endif set utflocale = `locale -a | grep $binaryopt -iE '\.utf.?8$' | head -n1` setenv LC_ALL $utflocale gtm << EOF >>&! gtm_test_install.out write \$zchset EOF setenv gtm_dist $save_gtm_dist/utf8 setenv gtmroutines ". $save_gtm_dist/utf8" mkdir test_gtm_utf8 cd test_gtm_utf8 cp ../test_gtm/sim.m . # set to test directory setenv gtmdir $save_gtm_dist/test_gtm_utf8 # make gtm set the utf locale unsetenv LC_CTYPE if ($?gtm_icu_version) then # unset gtm_icu_version to test gtmprofile.gtc setting it setenv save_icu $gtm_icu_version unsetenv gtm_icu_version endif # NOTE: this is not the alias gtm, but the script sr_unix/gtm.gtc ../gtm -r sim >& gtm.out # get $ZCHSET echo "" >>&! $save_gtm_dist/gtm_test_install.out grep ZCHSET gtm.out >>&! $save_gtm_dist/gtm_test_install.out if ($?save_icu) then # restore saved gtm_icu_version setenv gtm_icu_version $save_icu endif # test gtmsecshr with an alternate user set XCMD='do ^GTMHELP("",$ztrnlnm("gtm_dist")_"/gtmhelp.gld")' if ($?gtm_icu_version) then set icuver = "gtm_icu_version=$gtm_icu_version" else set icuver = "" endif su - gtmtest -c "env LD_LIBRARY_PATH=$libpath LC_ALL=$LC_ALL gtm_chset=UTF-8 $icuver gtm_dist=$gtm_dist gtmroutines='$gtmroutines' $gtm_dist/mumps -run %XCMD '${XCMD:q}' < /dev/null" > gtmtest.out #BYPASSOKLENGTH # if we see the 'Topic? ' prompt, all is well grep -q '^Topic. $' gtmtest.out if ( 0 == $status ) cat gtmtest.out >>&! $save_gtm_dist/gtm_test_install.out # get journal output cd V*/g $save_gtm_dist/mupip journal -extract -forward gtm.mjl >& mupip.out env gtmgbldir=$save_gtm_dist/test_gtm/mumps.gld $save_gtm_dist/mupip integ -reg "*" >& integ.out set mupip_status = $status # output the change lines echo "" >>&! $save_gtm_dist/gtm_test_install.out echo "Global changes in the gtm.mjl file:" >>&! $save_gtm_dist/gtm_test_install.out awk -F\\ '/=/{print ($NF)}' gtm.mjf >>&! $save_gtm_dist/gtm_test_install.out if (0 != $mupip_status) then echo "Error: mupip integ returned $status status instead of 0" >>&! $save_gtm_dist/gtm_test_install.out else echo "mupip integ returned a 0 status - as expected" >>&! $save_gtm_dist/gtm_test_install.out endif $gtm_dist/gtmsecshr >>&! $save_gtm_dist/gtm_test_install.out $gtm_com/IGS $save_gtm_dist/gtmsecshr "STOP" # gtm_dist points to utf8/ and gtmsecshr is a softlink to $save_gtm_dist/gtmsecshr echo "Build test the encryption plugin" >>&! $save_gtm_dist/gtm_test_install.out #BYPASSOKLENGTH mkdir -p $save_gtm_dist/plugin/buildcrypt cd $save_gtm_dist/plugin/buildcrypt tar xf $save_gtm_dist/plugin/gtmcrypt/source.tar env LD_LIBRARY_PATH=$libpath LC_ALL=$LC_ALL GTMXC_gpgagent=$save_gtm_dist/plugin/gpgagent.tab sh -v README >& $save_gtm_dist/encr_plugin_build_test.out #BYPASSOKLENGTH if ($status) echo "Encryption plugin build failure" grep "GTM.[WIFE]" $save_gtm_dist/encr_plugin_build_test.out setenv gtmroutines ". $gtm_dist/plugin/o($gtm_dist/plugin/r) $gtm_dist" setenv gtm_passwd "`echo gtmrocks | env gtm_dist=$gtm_dist $gtm_dist/plugin/gtmcrypt/maskpass | cut -f3 -d ' '`" setenv GTMXC_gpgagent $gtm_dist/plugin/gpgagent.tab setenv gtm_pinentry_log $gtm_dist/encr_plugin_build_test.out echo "Default gtmroutines" >>&! $save_gtm_dist/gtm_test_install.out #BYPASSOKLENGTH echo GETPIN | env LD_LIBRARY_PATH=$libpath LC_ALL=$LC_ALL $gtm_dist/plugin/gtmcrypt/pinentry-gtm.sh >>&! $save_gtm_dist/gtm_test_install.out #BYPASSOKLENGTH echo "Null gtmroutines" >>&! $save_gtm_dist/gtm_test_install.out #BYPASSOKLENGTH unsetenv gtmroutines echo GETPIN | env LD_LIBRARY_PATH=$libpath LC_ALL=$LC_ALL $gtm_dist/plugin/gtmcrypt/pinentry-gtm.sh >>&! $save_gtm_dist/gtm_test_install.out #BYPASSOKLENGTH cd $save_gtm_dist # strip off the copyright lines tail -n +11 $gtm_tools/gtm_test_install.txt > gtm_test_install.txt # remove white space at end of created output sed 's/[ ][ ]*$//' gtm_test_install.out >&! gtm_test_install.out_stripspaces cmp gtm_test_install.txt gtm_test_install.out_stripspaces if ($status) then echo "---------" echo "GTM_TEST_INSTALL-E-ERROR the output is not as expected. Check $PWD" diff gtm_test_install.txt gtm_test_install.out_stripspaces set exitstat = 1 echo "---------" else echo "The test succeeded, the output is in $PWD/gtm_test_install.out" set exitstat = 0 endif echo "------------------------------------------------------------------------" exit $exitstat fis-gtm-V7.0-005/sr_unix/gtm_test_install.txt0000644000032200000250000000274414342376330020165 0ustar librarygtc################################################################# # # # Copyright 2011, 2013 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. # # # ################################################################# GTM> GTM> GTM> ^X GTM> x=2 GTM> ^X=1 GTM> GTM> Additional information available: About_GT.M Commands Err_Processing Functions Integrate_External Internationalization IO_Processing ISV Language_Extensions M_Lang_Features Opr_Dbg_Dir_Mode Program_Cycle Triggers Utility_Routines Topic? GTM> ^X=1 GTM> ZCHSET= M Global changes in the gtm.mjl file: ^a="1" ^b="2" mupip integ returned a 0 status - as expected GTM> M GTM> GTM> UTF-8 GTM> ZCHSET= UTF-8 Additional information available: About_GT.M Commands Err_Processing Functions Integrate_External Internationalization IO_Processing ISV Language_Extensions M_Lang_Features Opr_Dbg_Dir_Mode Program_Cycle Triggers Utility_Routines Topic? Global changes in the gtm.mjl file: ^a="1" ^b="2" mupip integ returned a 0 status - as expected Build test the encryption plugin Default gtmroutines OK Your orders please D gtmrocks OK Null gtmroutines OK Your orders please D gtmrocks OK fis-gtm-V7.0-005/sr_unix/gtm_threadgbl_asm_access.txt0000644000032200000250000000130214342376330021562 0ustar librarygtc################################################################# # # # Copyright (c) 2014, 2015 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. # # # ################################################################# # # List of threadgbl variables needing access in assembler routines. One routine per line - no white space prefix. # lnk_proxy # pseudo-linkage table used by dynamic code when label index is < 0 fis-gtm-V7.0-005/sr_unix/gtm_threadgbl_deftypes_asm.si0000644000032200000250000000117414342376330021747 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2015 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. * * * ****************************************************************/ #ifndef DEBUG # include "gtm_threadgbl_deftypes_asm_pro.si" #else # include "gtm_threadgbl_deftypes_asm_dbg.si" #endif fis-gtm-V7.0-005/sr_unix/gtm_times.h0000755000032200000250000000072514342376330016211 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 fis-gtm-V7.0-005/sr_unix/gtm_tls.c0000644000032200000250000001530414342376330015661 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2022 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_tls.h" #include "have_crit.h" /* This file defines wrapper functions that defer interrupts, invoke the SSL/TLS function and enable interrupts. This guarantees * that system calls invoked by the SSL/TLS library (or OpenSSL) are not interrupted by internal (like SIGALRM) or external * signals (like SIGTERM) thereby avoiding any deadlocks. Note that this file has to be maintained in sync with gtm_tls_interface.h. */ const char *intrsafe_gtm_tls_get_error(gtm_tls_socket_t *tlssocket) { const char *rv; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); rv = (*gtm_tls_get_error_fptr)(tlssocket); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); return rv; } int intrsafe_gtm_tls_errno(void) { int rv; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); rv = (*gtm_tls_errno_fptr)(); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); return rv; } gtm_tls_ctx_t *intrsafe_gtm_tls_init(int version, int flags) { gtm_tls_ctx_t *rv; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); rv = (*gtm_tls_init_fptr)(version, flags); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); return rv; } void intrsafe_gtm_tls_prefetch_passwd(gtm_tls_ctx_t *tls_ctx, char *env_name) { intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); (*gtm_tls_prefetch_passwd_fptr)(tls_ctx, env_name); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); } gtm_tls_socket_t *intrsafe_gtm_tls_socket(gtm_tls_ctx_t *ctx, gtm_tls_socket_t *prev_socket, int sockfd, char *id, int flags) { gtm_tls_socket_t *rv; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); rv = (*gtm_tls_socket_fptr)(ctx, prev_socket, sockfd, id, flags); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); return rv; } int intrsafe_gtm_tls_connect(gtm_tls_socket_t *socket) { int rv; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); rv = (*gtm_tls_connect_fptr)(socket); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); return rv; } int intrsafe_gtm_tls_accept(gtm_tls_socket_t *socket) { int rv; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); rv = (*gtm_tls_accept_fptr)(socket); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); return rv; } int intrsafe_gtm_tls_renegotiate(gtm_tls_socket_t *socket) { int rv; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); rv = (*gtm_tls_renegotiate_fptr)(socket); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); return rv; } int intrsafe_gtm_tls_get_conn_info(gtm_tls_socket_t *socket, gtm_tls_conn_info *conn_info) { int rv; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); rv = (*gtm_tls_get_conn_info_fptr)(socket, conn_info); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); return rv; } int intrsafe_gtm_tls_send(gtm_tls_socket_t *socket, char *buf, int send_len) { int rv; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); rv = (*gtm_tls_send_fptr)(socket, buf, send_len); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); return rv; } int intrsafe_gtm_tls_recv(gtm_tls_socket_t *socket, char *buf, int recv_len) { int rv; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); rv = (*gtm_tls_recv_fptr)(socket, buf, recv_len); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); return rv; } int intrsafe_gtm_tls_cachedbytes(gtm_tls_socket_t *socket) { int rv; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); rv = (*gtm_tls_cachedbytes_fptr)(socket); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); return rv; } void intrsafe_gtm_tls_socket_close(gtm_tls_socket_t *socket) { intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); (*gtm_tls_socket_close_fptr)(socket); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); } void intrsafe_gtm_tls_session_close(gtm_tls_socket_t **socket) { intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); (*gtm_tls_session_close_fptr)(socket); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); } void intrsafe_gtm_tls_fini(gtm_tls_ctx_t **ctx) { intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); (*gtm_tls_fini_fptr)(ctx); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); } int intrsafe_gtm_tls_store_passwd(gtm_tls_ctx_t *tls_ctx, const char *tlsid, const char *obs_passwd) { int rv; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); rv = (*gtm_tls_store_passwd_fptr)(tls_ctx, tlsid, obs_passwd); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); return rv; } int intrsafe_gtm_tls_add_config(gtm_tls_ctx_t *tls_ctx, const char *idstr, const char *configstr) { int rv; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); rv = (*gtm_tls_add_config_fptr)(tls_ctx, idstr, configstr); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); return rv; } int intrsafe_gtm_tls_renegotiate_options(gtm_tls_socket_t *socket, int msec_timeout, char *idstr, char *configstr, int tlsid_present) { int rv; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); rv = (*gtm_tls_renegotiate_options_fptr)(socket, msec_timeout, idstr, configstr, tlsid_present); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); return rv; } int intrsafe_gtm_tls_version(int caller_version) { int rv; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); rv = (*gtm_tls_version_fptr)(caller_version); ENABLE_INTERRUPTS(INTRPT_IN_TLS_FUNCTION, prev_intrpt_state); return rv; } fis-gtm-V7.0-005/sr_unix/gtm_tls.h0000644000032200000250000000753514342376330015675 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2021 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. * * * ****************************************************************/ #ifndef GTM_TLS_H #define GTM_TLS_H #define gtm_tls_get_error (*gtm_tls_get_error_fptr) #define gtm_tls_errno (*gtm_tls_errno_fptr) #define gtm_tls_init (*gtm_tls_init_fptr) #define gtm_tls_prefetch_passwd (*gtm_tls_prefetch_passwd_fptr) #define gtm_tls_socket (*gtm_tls_socket_fptr) #define gtm_tls_connect (*gtm_tls_connect_fptr) #define gtm_tls_accept (*gtm_tls_accept_fptr) #define gtm_tls_renegotiate (*gtm_tls_renegotiate_fptr) #define gtm_tls_get_conn_info (*gtm_tls_get_conn_info_fptr) #define gtm_tls_send (*gtm_tls_send_fptr) #define gtm_tls_recv (*gtm_tls_recv_fptr) #define gtm_tls_cachedbytes (*gtm_tls_cachedbytes_fptr) #define gtm_tls_socket_close (*gtm_tls_socket_close_fptr) #define gtm_tls_session_close (*gtm_tls_session_close_fptr) #define gtm_tls_fini (*gtm_tls_fini_fptr) #define gtm_tls_store_passwd (*gtm_tls_store_passwd_fptr) #define gtm_tls_add_config (*gtm_tls_add_config_fptr) #define gtm_tls_renegotiate_options (*gtm_tls_renegotiate_options_fptr) #define gtm_tls_version (*gtm_tls_version_fptr) /* It's important that the "gtm_tls_interface.h" include should be *after* the above macro definitions. This way, the function * prototypes defined in the header file will automatically be expanded to function pointers saving us the trouble of explicitly * defining them once again. */ #include "gtm_tls_interface.h" #undef gtm_tls_get_error #undef gtm_tls_errno #undef gtm_tls_init #undef gtm_tls_prefetch_passwd #undef gtm_tls_socket #undef gtm_tls_connect #undef gtm_tls_accept #undef gtm_tls_renegotiate #undef gtm_tls_get_conn_info #undef gtm_tls_send #undef gtm_tls_recv #undef gtm_tls_cachedbytes #undef gtm_tls_socket_close #undef gtm_tls_session_close #undef gtm_tls_fini #undef gtm_tls_store_passwd #undef gtm_tls_add_config #undef gtm_tls_renegotiate_options #undef gtm_tls_version /* Now, we need to define prototypes for wrapper functions that will be defined in GT.M to defer interrupts before invoking the * corresponding TLS function. But, to avoid redefining the prototypes, include the gtm_tls_interface.h once again to automatically * generate the prototypes. */ #define gtm_tls_get_error intrsafe_gtm_tls_get_error #define gtm_tls_errno intrsafe_gtm_tls_errno #define gtm_tls_init intrsafe_gtm_tls_init #define gtm_tls_prefetch_passwd intrsafe_gtm_tls_prefetch_passwd #define gtm_tls_socket intrsafe_gtm_tls_socket #define gtm_tls_connect intrsafe_gtm_tls_connect #define gtm_tls_accept intrsafe_gtm_tls_accept #define gtm_tls_renegotiate intrsafe_gtm_tls_renegotiate #define gtm_tls_get_conn_info intrsafe_gtm_tls_get_conn_info #define gtm_tls_send intrsafe_gtm_tls_send #define gtm_tls_recv intrsafe_gtm_tls_recv #define gtm_tls_cachedbytes intrsafe_gtm_tls_cachedbytes #define gtm_tls_socket_close intrsafe_gtm_tls_socket_close #define gtm_tls_session_close intrsafe_gtm_tls_session_close #define gtm_tls_fini intrsafe_gtm_tls_fini #define gtm_tls_store_passwd intrsafe_gtm_tls_store_passwd #define gtm_tls_add_config intrsafe_gtm_tls_add_config #define gtm_tls_renegotiate_options intrsafe_gtm_tls_renegotiate_options #define gtm_tls_version intrsafe_gtm_tls_version #undef GTM_TLS_INTERFACE_H /* Allows us to include gtm_tls_interface.h twice. */ #include "gtm_tls_interface.h" /* BYPASSOK : intentional duplicate include. */ GBLREF gtm_tls_ctx_t *tls_ctx; int gtm_tls_loadlibrary(void); #endif fis-gtm-V7.0-005/sr_unix/gtm_tls_funclist.h0000644000032200000250000000251214342376330017572 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2021 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. * * * ****************************************************************/ /* Any time a new function gets added to the GT.M SSL/TLS interface, add the corresponding entry here. This file is included in * `gtm_tls_loadlibrary', by appropriately, defining TLS_DEF, to generate necessary string literals and function pointers which * is used to `dlsym' symbols from the SSL/TLS shared library. */ TLS_DEF(gtm_tls_get_error) TLS_DEF(gtm_tls_errno) TLS_DEF(gtm_tls_init) TLS_DEF(gtm_tls_prefetch_passwd) TLS_DEF(gtm_tls_socket) TLS_DEF(gtm_tls_connect) TLS_DEF(gtm_tls_accept) TLS_DEF(gtm_tls_renegotiate) TLS_DEF(gtm_tls_get_conn_info) TLS_DEF(gtm_tls_send) TLS_DEF(gtm_tls_recv) TLS_DEF(gtm_tls_cachedbytes) TLS_DEF(gtm_tls_socket_close) TLS_DEF(gtm_tls_session_close) TLS_DEF(gtm_tls_fini) TLS_DEF(gtm_tls_store_passwd) TLS_DEF(gtm_tls_add_config) TLS_DEF(gtm_tls_renegotiate_options) TLS_DEF(gtm_tls_version) fis-gtm-V7.0-005/sr_unix/gtm_tls_impl.c0000644000032200000250000021241714342376330016706 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2022 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gtmxc_types.h" #include "gtmcrypt_util.h" #include "gtm_tls_interface.h" #include "gtm_tls_impl.h" #ifdef DEBUG #include #endif GBLDEF int tls_errno; GBLDEF gtmtls_passwd_list_t *gtmtls_passwd_listhead; STATICDEF config_t gtm_tls_cfg; #if OPENSSL_VERSION_NUMBER < 0x30000000L STATICDEF DH *dh512, *dh1024; /* Diffie-Hellman structures for Ephemeral Diffie-Hellman key exchange. */ #endif #ifdef DEBUG STATICDEF char *wbox_enable = NULL, *wbox_tls_check = NULL, *wbox_count = NULL, *wbox_test_count = NULL; STATICDEF int wbox_count_val = 0, wbox_test_count_val = 0, wbox_enable_val = 0, wbox_tls_check_val = 0; #define TRUE 1 #define FALSE 0 #define WBTEST_REPL_TLS_RECONN 169 #endif #define MAX_CONFIG_LOOKUP_PATHLEN 64 /* Older, but still commonly used, OpenSSL versions don't have macros for TLSv1.1 and TLSv1.2 versions. * They are hard coded to 0x0302 and 0x0303 respectively. So, define them here for use in gtm_tls_get_conn_info. */ #ifndef TLS1_1_VERSION #define TLS1_1_VERSION 0x0302 #endif #ifndef TLS1_2_VERSION #define TLS1_2_VERSION 0x0303 #endif #ifndef TLS1_3_VERSION #define TLS1_3_VERSION 0x0304 #endif /* Below template translates to: Arrange ciphers in increasing order of strength after excluding the following: * ADH: Anonymous Diffie-Hellman Key Exchange (Since we want both encryption and authentication and ADH provides only former). * LOW: Low strength ciphers. * EXP: Export Ciphers. * MD5 : MD5 message digest. */ #define REPL_CIPHER_LIST "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" #define GTM_DEFAULT_CIPHER_LIST "DEFAULT:!SRP" #define OPTIONEND ':' #define OPTIONENDSTR ":" #define OPTIONNOT '!' #define DEFINE_SSL_OP(OP_DEF) { #OP_DEF , OP_DEF } #define SET_AND_APPEND_OPENSSL_ERROR(...) \ { \ char *errptr, *end; \ int rv; \ \ rv = gtm_tls_set_error(NULL, __VA_ARGS__); \ end = errptr = (char *)gtm_tls_get_error(NULL); \ end += MAX_GTMCRYPT_ERR_STRLEN; \ errptr += rv; \ if (end > errptr) \ { \ rv = snprintf(errptr, end - errptr, "%s", " Reason: "); \ if (0 <= rv) \ errptr += rv; \ } \ if (end > errptr) \ { \ rv = ERR_get_error(); \ ERR_error_string_n(rv, errptr, end - errptr); \ } \ } struct gtm_ssl_options { const char *opt_str; long opt_val; }; STATICDEF struct gtm_ssl_options gtm_ssl_verify_level_list[]= { { "CHECK", GTMTLS_OP_VERIFY_LEVEL_CHECK }, {NULL, 0} }; STATICDEF struct gtm_ssl_options gtm_ssl_verify_mode_list[] = { #include "gen_tls_verify_options.h" {NULL, 0} }; STATICDEF struct gtm_ssl_options gtm_ssl_options_list[] = { #include "gen_tls_options.h" #ifndef SSL_OP_NO_TLSv1_3 /* Special case: define this option so that pre OpenSSL 1.1.1 recognize it */ # define SSL_OP_NO_TLSv1_3 0x0 DEFINE_SSL_OP(SSL_OP_NO_TLSv1_3), #endif {NULL, 0} }; #ifdef SSL_OP_NO_TLSv1 #define GTM_NO_TLSv1 | SSL_OP_NO_TLSv1 #else #define GTM_NO_TLSv1 #endif #ifdef SSL_OP_NO_TLSv1_1 #define GTM_NO_TLSv1_1 | SSL_OP_NO_TLSv1_1 #else #define GTM_NO_TLSv1_1 #endif /* Deprecate all SSL/TLS Protocols before TLS v1.2 */ #define DEPRECATED_SSLTLS_PROTOCOLS (SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 GTM_NO_TLSv1 GTM_NO_TLSv1_1) /* Static function definitions */ STATICFNDEF char *parse_SSL_options(struct gtm_ssl_options *opt_table, size_t opt_table_size, const char *options, long *current, long *clear); STATICFNDEF int gtm_tls_set_error(gtm_tls_socket_t *tlssocket, const char *format, ...); STATICFNDEF char *parse_SSL_options(struct gtm_ssl_options *opt_table, size_t opt_table_size, const char *options, long *current, long *clear) { int negate; size_t num_options, index, optionlen; long bitmask; const char *charptr, *optionend; if (NULL == options) return 0; negate = 0; bitmask = *current; num_options = opt_table_size/SIZEOF(struct gtm_ssl_options); for (charptr = options; *charptr; charptr = optionend) { if (OPTIONEND == *charptr) if ('\0' == *++charptr) break; optionend = strstr((const char *)charptr, OPTIONENDSTR); if (NULL == optionend) optionend = charptr + strlen(charptr); if (OPTIONNOT == *charptr) { negate = TRUE; charptr++; } else negate = FALSE; optionlen = optionend - charptr; for (index = 0; num_options > index ; index++) { if (NULL == opt_table[index].opt_str) break; if ((optionlen == strlen(opt_table[index].opt_str)) && (0 == strncmp(opt_table[index].opt_str, charptr, optionlen))) { if (negate) { bitmask &= ~opt_table[index].opt_val; if (NULL != clear) *clear |= opt_table[index].opt_val; } else bitmask |= opt_table[index].opt_val; break; } } if ((num_options - 1) <= index) /* last option is NULL */ return (char *)charptr; /* option not known */ } *current = bitmask; return NULL; } #ifdef DEBUG_SSL #define SSL_DPRINT(FP, ...) {fprintf(FP, __VA_ARGS__); fflush(FP);} /* BYPASSOK -- cannot use FFLUSH. */ #else #define SSL_DPRINT(FP, ...) #endif /* OpenSSL doesn't provide an easy way to convert an ASN1 time to a string format unless BIOs are used. So, use a memory BIO to * write the string representation of ASN1_TIME in the said buffer. */ STATICFNDEF int format_ASN1_TIME(ASN1_TIME *tm, char *buf, int maxlen) { BIO *memfp; int len; if (NULL == (memfp = BIO_new(BIO_s_mem()))) return -1; ASN1_TIME_print(memfp, tm); len = BIO_pending(memfp); len = len < maxlen ? len : maxlen; if (0 >= BIO_read(memfp, buf, len)) return -1; if (0 >= BIO_free(memfp)) return -1; buf[len] = '\0'; return 0; } STATICFNDEF int ssl_error(gtm_tls_socket_t *tls_sock, int err, long verify_result) { int ssl_error_code, reason_code, err_lib; unsigned long error_code; char *errptr, *end; SSL *ssl; #ifdef DEBUG int is_wb = FALSE; #endif ssl = tls_sock->ssl; ssl_error_code = SSL_get_error(ssl, err); /* generic error code */ #ifdef DEBUG /* Change the error code to induce error in case of * WBTEST_REPL_TLS_RECONN white box. */ if ((1 == wbox_enable_val) && (WBTEST_REPL_TLS_RECONN == wbox_tls_check_val) && (wbox_test_count_val == wbox_count_val)) { SSL_DPRINT(stderr, "Prev code: %d setting code to: %d\n", ssl_error_code,SSL_ERROR_SSL); is_wb = TRUE; ssl_error_code = SSL_ERROR_SSL; } #endif switch (ssl_error_code) { case SSL_ERROR_ZERO_RETURN: /* SSL/TLS connection has been closed gracefully. The underlying TCP/IP connection is not yet closed. The * caller should take necessary action to close the underlying transport */ tls_errno = ECONNRESET; break; case SSL_ERROR_SYSCALL: tls_errno = errno; if (0 == tls_errno) /* If no error at underlying socket, consisder a connecton reset */ tls_errno = ECONNRESET; tls_sock->flags |= GTMTLS_OP_NOSHUTDOWN; break; case SSL_ERROR_WANT_WRITE: return GTMTLS_WANT_WRITE; case SSL_ERROR_WANT_READ: return GTMTLS_WANT_READ; case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_CONNECT: assert(FALSE); case SSL_ERROR_SSL: case SSL_ERROR_NONE: errptr = (char *)gtm_tls_get_error(tls_sock); assert(errptr != NULL); end = errptr + MAX_GTMCRYPT_ERR_STRLEN; tls_errno = -1; if ((GTMTLS_OP_VERIFY_LEVEL_CHECK & tls_sock->flags) && (X509_V_OK != verify_result)) { gtm_tls_set_error(tls_sock, "certificate verification error: %s", X509_verify_cert_error_string(verify_result)); return -1; } else if (SSL_ERROR_NONE == ssl_error_code) { /* we are ignoring verify result and no other error */ tls_errno = 0; /* Return the orginal return value from the prev SSL call, * since SSL_ERROR_NONE is only returned when ret > 0 */ return err; } else if (SSL_ERROR_SSL == ssl_error_code) { SSL_DPRINT(stderr, "Resetting the err code\n"); /* Check the first error in the queue */ error_code = ERR_peek_error(); #ifdef DEBUG assert(error_code || ((1 == wbox_enable_val) && (WBTEST_REPL_TLS_RECONN == wbox_tls_check_val))); #endif err_lib = ERR_GET_LIB(error_code); reason_code = ERR_GET_REASON(error_code); #ifdef DEBUG if (is_wb) { reason_code = SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC; err_lib = ERR_LIB_SSL; } #endif /* Change the returned error only for replication * and if the error comes from SSL library */ if (!(tls_sock->flags & GTMTLS_OP_SOCKET_DEV) && !(tls_sock->flags & GTMTLS_OP_DM_AUDIT) && (ERR_LIB_SSL == err_lib) && ((SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC == reason_code) || (SSL_R_SSLV3_ALERT_BAD_RECORD_MAC == reason_code))) tls_errno = ECONNRESET; tls_sock->flags |= GTMTLS_OP_NOSHUTDOWN; } do { error_code = ERR_get_error(); if (0 == error_code) { if (errptr == tls_sock->errstr) { /* Very first call to ERR_get_error returned 0. This is very unlikely. Nevertheless * handle this by updating the error string with a generic error. */ gtm_tls_set_error(tls_sock, "Unknown SSL/TLS protocol error."); return -1; } break; } else if ((errptr < end) && (errptr != tls_sock->errstr)) *errptr++ = ';'; if (errptr >= end) continue; /* We could break here, but we want to clear the OpenSSL error stack. */ ERR_error_string_n(error_code, errptr, end - errptr); errptr += STRLEN(errptr); } while (TRUE); break; default: tls_errno = -1; gtm_tls_set_error(tls_sock, "Unknown error: %d returned by `SSL_get_error'", ssl_error_code); assert(FALSE); break; } return -1; } /* This callback function is called whenever OpenSSL wants to decrypt the private key (e.g., SSL_CTX_check_private_key()). More * importantly, when OpenSSL calls this callback function, it passes a userdata which provides the callback a hint as to which * private key this callback function corresponds to. In our case, this hint is equivalent to the TLSID passed to the plugin when * gtm_tls_socket is called. */ STATICFNDEF int passwd_callback(char *buf, int size, int rwflag, void *userdata) { passwd_entry_t *pwent; int len; pwent = (passwd_entry_t *)userdata; assert(NULL != pwent); assert(NULL != pwent->passwd); len = STRLEN(pwent->passwd); strncpy(buf, pwent->passwd, size); if (len >= size) { buf[size] = '\0'; len = size - 1; } return len; } /* The below callback is invoked whenever OpenSSL creates a new session (either because the old session expired or because of a * renegotiation). Store the session back into the API's socket structure. */ STATICFNDEF int new_session_callback(SSL *ssl, SSL_SESSION *session) { gtm_tls_socket_t *socket; /* See SSL_set_app_data for details on why we need to use the compatibility interface: SSL_get_app_data/SSL_set_app_data. */ socket = SSL_get_app_data(ssl); assert(NULL != socket); /* Free up the old session. */ SSL_DPRINT(stderr, "new_session_callback: references=%d\n", session->references); if (socket->session) SSL_SESSION_free(socket->session); /* Add the new session to the `socket' structure. */ socket->session = session; return 1; } #if OPENSSL_VERSION_NUMBER < 0x30000000L STATICFNDEF DH *read_dhparams(const char *dh_fn) { BIO *bio; DH *dh; if (NULL == (bio = BIO_new_file(dh_fn, "r"))) { SET_AND_APPEND_OPENSSL_ERROR("Unable to load Diffie-Hellman parameter file: %s.", dh_fn); return NULL; } if (NULL == (dh = (PEM_read_bio_DHparams(bio, NULL, NULL, NULL)))) { SET_AND_APPEND_OPENSSL_ERROR("Unable to load Diffie-Hellman parameter file: %s.", dh_fn); return NULL; } return dh; } STATICFNDEF int init_dhparams(void) { int rv1, rv2; const char *dh512_fn, *dh1024_fn; if (dh1024) return 0; /* already have */ rv1 = config_lookup_string(>m_tls_cfg, "tls.dh512", &dh512_fn); rv2 = config_lookup_string(>m_tls_cfg, "tls.dh1024", &dh1024_fn); if (!rv1 && !rv2) return 0; /* No Diffie-Hellman parameters specified in the config file. */ if (!rv1) { gtm_tls_set_error(NULL, "Configuration parameter `tls.dh512' not specified."); return -1; } if (!rv2) { gtm_tls_set_error(NULL, "Configuration parameter `tls.dh1024' not specified."); return -1; } if (NULL == (dh512 = read_dhparams(dh512_fn))) return -1; if (NULL == (dh1024 = read_dhparams(dh1024_fn))) return -1; return 0; } STATICFNDEF DH *tmp_dh_callback(SSL *ssl, int is_export, int keylength) { assert(dh512 && dh1024 && ((512 == keylength) || (1024 == keylength))); return (512 == keylength) ? dh512 : dh1024; } #endif int gtm_tls_errno(void) { return tls_errno; } /* Interlude to snprintf that uses the appropriate buffer. Function can return bytes written for callers that care */ STATICFNDEF int gtm_tls_set_error(gtm_tls_socket_t *tlssocket, const char *format, ...) { char *errstr = NULL; va_list var; int ret = 0; assert(NULL != gtmtls_err_string); if (NULL != tlssocket) { /* Separate DB and network error strings */ if (NULL == tlssocket->errstr) errstr = tlssocket->errstr = malloc(MAX_GTMCRYPT_ERR_STRLEN); assert(NULL != tlssocket->errstr); } if (NULL == errstr) errstr = (NULL != gtmtls_err_string)? gtmtls_err_string : gtmcrypt_err_string; va_start(var, format); ret = vsnprintf(errstr, MAX_GTMCRYPT_ERR_STRLEN, format, var); va_end(var); return ret; } const char *gtm_tls_get_error(gtm_tls_socket_t *tlssocket) { if ((NULL != tlssocket) && (NULL != tlssocket->errstr)) return tlssocket->errstr; /* socket specific error string present */ if (gtmtls_err_string) return gtmtls_err_string; /* TLS implementation's general error string */ return gtmcrypt_err_string; /* DB encryption error string */ } int gtm_tls_version(int caller_version) { return GTM_TLS_API_VERSION; } gtm_tls_ctx_t *gtm_tls_init(int version, int flags) { const char *CAfile = NULL, *CApath = NULL, *crl, *CAptr, *cipher_list, *options_string, *verify_mode_string; const char *verify_level_string; char *config_env, *parse_ptr, *optionendptr; int rv, rv1, rv2, fips_requested, fips_enabled, verify_mode, parse_len, verify_level; # if (((LIBCONFIG_VER_MAJOR == 1) && (LIBCONFIG_VER_MINOR >= 4)) || (LIBCONFIG_VER_MAJOR > 1)) int verify_depth, session_timeout; # else long int verify_depth, session_timeout; # endif long options_mask, options_current, options_clear, verify_long, level_long, level_clear; SSL_CTX *ctx; X509_STORE *store; X509_LOOKUP *lookup; config_t *cfg; # if OPENSSL_VERSION_NUMBER < 0x009080dfL unsigned int cfg_line; const char *cfg_file; config_setting_t *cfg_setting; # endif gtm_tls_ctx_t *gtm_tls_ctx; assert(GTM_TLS_API_VERSION >= version); /* Make sure the caller is using the right API version */ if (NULL == (gtmtls_err_string = malloc(MAX_GTMCRYPT_ERR_STRLEN + 1))) { gtm_tls_set_error(NULL, "Unable to allocate error buffer for libgtmtls.so plugin"); return NULL; } if (GTM_TLS_API_VERSION < version) { gtm_tls_set_error(NULL, "Version of libgtmtls.so plugin (%d) older than needed by caller (%d).", GTM_TLS_API_VERSION, version); return NULL; } /* Setup function pointers to symbols exported by libgtmshr.so. */ if (0 != gc_load_gtmshr_symbols()) return NULL; /* Initialize OpenSSL library */ #if OPENSSL_VERSION_NUMBER >= 0x10100000L /* The following options are initialzied by default: * OPENSSL_INIT_LOAD_CRYPTO_STRINGS * OPENSSL_INIT_ADD_ALL_CIPHERS * OPENSSL_INIT_ADD_ALL_DIGESTS * OPENSSL_INIT_LOAD_CONFIG as of 1.1.1 - second parameter is path to file; NULL means OS default * OPENSSL_INIT_ASYNC * * The following are manually added: * OPENSSL_INIT_NO_ATEXIT - suppress OpenSSL's "atexit" handler (new OpenSSL 3.0) */ # ifndef OPENSSL_INIT_NO_ATEXIT # define OPENSSL_INIT_NO_ATEXIT 0 # endif # define GTMTLS_STARTUP_OPTIONS (OPENSSL_INIT_NO_ATEXIT) if (!OPENSSL_init_ssl(GTMTLS_STARTUP_OPTIONS, NULL)) { /* Can't use SET_AND_APPEND_OPENSSL_ERROR which needs the above to initialize error reporting functions */ gtm_tls_set_error(NULL, "OpenSSL library initialization failed"); return NULL; } #else /* Initialize the SSL/TLS library, the algorithms/cipher suite and error strings. */ SSL_library_init(); SSL_load_error_strings(); ERR_load_BIO_strings(); OpenSSL_add_all_algorithms(); #endif /* Turn on FIPS mode if requested. */ fips_enabled = FALSE; /* most common case. */ IS_FIPS_MODE_REQUESTED(fips_requested); if (fips_requested) { ENABLE_FIPS_MODE(rv, fips_enabled); if (-1 == rv) return NULL; /* Relevant error detail populated in the above macro. */ } /* Setup a SSL context that allows TLSv1.x but no SSLv[23] (which is deprecated due to a great number of security * vulnerabilities). */ #if OPENSSL_VERSION_NUMBER >= 0x10100000L if (NULL == (ctx = SSL_CTX_new(TLS_method()))) #else if (NULL == (ctx = SSL_CTX_new(SSLv23_method()))) #endif { SET_AND_APPEND_OPENSSL_ERROR("Failed to create an SSL context."); return NULL; } SSL_CTX_set_options(ctx, DEPRECATED_SSLTLS_PROTOCOLS); /* Read the configuration file for more configuration parameters. */ cfg = >m_tls_cfg; config_init(cfg); if (NULL == (config_env = getenv("gtmcrypt_config"))) { if (!(GTMTLS_OP_INTERACTIVE_MODE & flags)) { /* allow no config file if interactive for simple client usage */ gtm_tls_set_error(NULL, ENV_UNDEF_ERROR, "gtmcrypt_config"); SSL_CTX_free(ctx); return NULL; } else flags |= GTMTLS_OP_ABSENT_CONFIG; } else if (!config_read_file(cfg, config_env)) { gtm_tls_set_error(NULL, "Failed to read config file: %s. At line: %d, %s.", config_env, config_error_line(cfg), config_error_text(cfg)); SSL_CTX_free(ctx); config_destroy(cfg); return NULL; } if (!(GTMTLS_OP_ABSENT_CONFIG & flags)) { /* check for tls section */ if (NULL == config_lookup(cfg, "tls")) if ((GTMTLS_OP_INTERACTIVE_MODE & flags)) flags |= GTMTLS_OP_ABSENT_CONFIG; else { gtm_tls_set_error(NULL, "No tls: section in config file: %s", config_env); SSL_CTX_free(ctx); config_destroy(cfg); return NULL; } } /* Get global SSL configuration parameters */ if (config_lookup_int(cfg, "tls.verify-depth", &verify_depth)) SSL_CTX_set_verify_depth(ctx, verify_depth); if (CONFIG_TRUE == config_lookup_string(cfg, "tls.verify-mode", &verify_mode_string)) { verify_long = 0; parse_ptr = parse_SSL_options(>m_ssl_verify_mode_list[0], SIZEOF(gtm_ssl_verify_mode_list), verify_mode_string, &verify_long, NULL); if (NULL != parse_ptr) { optionendptr = strstr((const char *)parse_ptr, OPTIONENDSTR); if (NULL == optionendptr) parse_len = strlen(parse_ptr); else parse_len = optionendptr - parse_ptr; gtm_tls_set_error(NULL, "Unknown verify-mode option: %.*s", parse_len, parse_ptr); SSL_CTX_free(ctx); config_destroy(cfg); return NULL; } verify_mode = (int)verify_long; SSL_CTX_set_verify(ctx, verify_mode, NULL); } else flags |= GTMTLS_OP_ABSENT_VERIFYMODE; if (CONFIG_TRUE == config_lookup_string(cfg, "tls.verify-level", &verify_level_string)) { level_long = GTMTLS_OP_VERIFY_LEVEL_MASK & flags; level_clear = 0; parse_ptr = parse_SSL_options(>m_ssl_verify_level_list[0], SIZEOF(gtm_ssl_verify_level_list), verify_level_string, &level_long, &level_clear); if (NULL != parse_ptr) { optionendptr = strstr((const char *)parse_ptr, OPTIONENDSTR); if (NULL == optionendptr) parse_len = strlen(parse_ptr); else parse_len = optionendptr - parse_ptr; gtm_tls_set_error(NULL, "Unknown verify-level option: %.*s", parse_len, parse_ptr); return NULL; } if (0 != level_clear) { verify_level = (int)level_clear; flags &= ~verify_level; } verify_level = (int)level_long; flags = (GTMTLS_OP_VERIFY_LEVEL_CMPLMNT & flags) | verify_level; } else flags |= GTMTLS_OP_VERIFY_LEVEL_DEFAULT; rv1 = config_lookup_string(cfg, "tls.CAfile", &CAfile); rv2 = config_lookup_string(cfg, "tls.CApath", &CApath); /* Setup trust locations for peer verifications. This adds on to any trust locations that was previously loaded. */ if ((rv1 || rv2) && !SSL_CTX_load_verify_locations(ctx, CAfile, CApath)) { if (rv1 && rv2) { SET_AND_APPEND_OPENSSL_ERROR("Failed to load CA verification locations (CAfile = %s; CApath = %s).", CAfile, CApath); } else { CAptr = rv1 ? CAfile : CApath; SET_AND_APPEND_OPENSSL_ERROR("Failed to load CA verification location: %s.", CAptr); } SSL_CTX_free(ctx); config_destroy(cfg); return NULL; } if (rv1 || rv2) flags |= GTMTLS_OP_CA_LOADED; /* Load the default verification paths as well. On most Unix distributions, the default path is set to /etc/ssl/certs. */ if (!SSL_CTX_set_default_verify_paths(ctx)) { SET_AND_APPEND_OPENSSL_ERROR("Failed to load default CA verification locations."); SSL_CTX_free(ctx); config_destroy(cfg); return NULL; } /* If a CRL is specified in the configuration file, add it to the cert store. */ if (config_lookup_string(cfg, "tls.crl", &crl)) { if (NULL == (store = SSL_CTX_get_cert_store(ctx))) { SET_AND_APPEND_OPENSSL_ERROR("Failed to get handle to internal certificate store."); SSL_CTX_free(ctx); config_destroy(cfg); return NULL; } if (NULL == (lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()))) { SET_AND_APPEND_OPENSSL_ERROR("Failed to get handle to internal certificate store."); SSL_CTX_free(ctx); config_destroy(cfg); return NULL; } if (0 == X509_LOOKUP_load_file(lookup, (char *)crl, X509_FILETYPE_PEM)) { SET_AND_APPEND_OPENSSL_ERROR("Failed to add Certificate Revocation List %s to internal certificate store.", crl); SSL_CTX_free(ctx); config_destroy(cfg); return NULL; } X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); } SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH); /* Set callbacks to called whenever a SSL session is added. */ SSL_CTX_sess_set_new_cb(ctx, new_session_callback); /* Set session timeout value (in seconds). If the current time is greater than the session creation time + session * timeout, the session is not reused. This is useful only on the server. */ if (config_lookup_int(cfg, "tls.session-timeout", &session_timeout)) { if (0 >= session_timeout) SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); else SSL_CTX_set_timeout(ctx, session_timeout); } else SSL_CTX_set_timeout(ctx, DEFAULT_SESSION_TIMEOUT); if (CONFIG_FALSE == config_lookup_string(cfg, "tls.cipher-list", &cipher_list)) cipher_list = NULL; else if (('\0' != cipher_list[0]) && (0 >= SSL_CTX_set_cipher_list(ctx, cipher_list)) #if OPENSSL_VERSION_NUMBER >= 0x10101000L && (0 >= SSL_CTX_set_ciphersuites(ctx, cipher_list)) #endif ) { SET_AND_APPEND_OPENSSL_ERROR("Failed to add Cipher-List command string: %s.", cipher_list); SSL_CTX_free(ctx); config_destroy(cfg); return NULL; } /* use OpenSSL default */ if (CONFIG_TRUE == config_lookup_string(cfg, "tls.ssl-options", &options_string)) { options_mask = options_current = SSL_CTX_get_options(ctx); options_clear = 0; parse_ptr = parse_SSL_options(>m_ssl_options_list[0], SIZEOF(gtm_ssl_options_list), options_string, &options_mask, &options_clear); if (NULL != parse_ptr) { optionendptr = strstr((const char *)parse_ptr, OPTIONENDSTR); if (NULL == optionendptr) parse_len = strlen(parse_ptr); else parse_len = optionendptr - parse_ptr; gtm_tls_set_error(NULL, "Unknown ssl-options option: %.*s", parse_len, parse_ptr); SSL_CTX_free(ctx); config_destroy(cfg); return NULL; } if (options_current != options_mask) options_mask = SSL_CTX_set_options(ctx, options_mask); if (0 != options_clear) # if OPENSSL_VERSION_NUMBER >= 0x009080dfL options_mask = SSL_CTX_clear_options(ctx, options_clear); # else { /* clear_options first in OpenSSL 0.9.8m */ cfg_setting = config_lookup(cfg, "tls.ssl-options"); cfg_file = config_setting_source_file(cfg_setting); cfg_line = config_setting_source_line(cfg_setting); gtm_tls_set_error(NULL, "Unable to negate values in %s - need OpenSSL 0.9.8m or newer in %s line %d", "tls.ssl-options", cfg_file, cfg_line); SSL_CTX_free(ctx); config_destroy(cfg); return NULL; } # endif } #if OPENSSL_VERSION_NUMBER >= 0x10002000L /* Support for ECDHE Ciphers was added in OpenSSL 1.0.2 with the below. * OpenSSL 1.1.0 made this the default and made the function a no-op */ SSL_CTX_set_ecdh_auto(ctx, 1); #endif gtm_tls_ctx = MALLOC(SIZEOF(gtm_tls_ctx_t)); gtm_tls_ctx->ctx = ctx; if (NULL == cipher_list) flags |= GTMTLS_OP_ABSENT_CIPHER; else if ('\0' == cipher_list[0]) flags |= GTMTLS_OP_DEFAULT_CIPHER; gtm_tls_ctx->flags = flags; gtm_tls_ctx->fips_mode = fips_enabled; gtm_tls_ctx->compile_time_version = OPENSSL_VERSION_NUMBER; gtm_tls_ctx->runtime_version = SSLeay(); gtm_tls_ctx->version = version; /* GTM_TLS_API_VERSION of caller */ if (GTM_TLS_API_VERSION_NONBLOCK <= version) gtm_tls_ctx->plugin_version = GTM_TLS_API_VERSION; return gtm_tls_ctx; } STATICFNDEF gtmtls_passwd_list_t *gtm_tls_find_pwent(const char *input_env_name) { gtmtls_passwd_list_t *pwent_node; char *env_name_ptr; int len, inputlen; inputlen = STRLEN(input_env_name); for (pwent_node = gtmtls_passwd_listhead; NULL != pwent_node; pwent_node = pwent_node->next) { /* Lookup to see if we already have a password for the tlsid. */ env_name_ptr = pwent_node->pwent->env_name; len = STRLEN(env_name_ptr); assert(len < PASSPHRASE_ENVNAME_MAX); assert(len > SIZEOF(GTMTLS_PASSWD_ENV_PREFIX) - 1); if ((len == inputlen) && (0 == strcmp(input_env_name, env_name_ptr))) break; /* We already have a password for the tlsid. */ } return pwent_node; } int gtm_tls_store_passwd(gtm_tls_ctx_t *tls_ctx, const char *tlsid, const char *obs_passwd) { char env_name[PASSPHRASE_ENVNAME_MAX]; size_t env_name_idx; size_t env_name_len, obs_len; gtmtls_passwd_list_t *pwent_node; passwd_entry_t *pwent; assert(tls_ctx); if (!(GTMTLS_OP_INTERACTIVE_MODE & tls_ctx->flags)) return 0; /* Not running in an interactive mode. */ assert(NULL != tlsid); env_name_len = strnlen(tlsid, (PASSPHRASE_ENVNAME_MAX - sizeof(GTMTLS_PASSWD_ENV_PREFIX))); /* Includes null */ env_name_idx = (sizeof(GTMTLS_PASSWD_ENV_PREFIX) - 1); memcpy(env_name, GTMTLS_PASSWD_ENV_PREFIX, env_name_idx); memcpy(&env_name[env_name_idx], tlsid, env_name_len); env_name_idx += env_name_len; assert(PASSPHRASE_ENVNAME_MAX > env_name_idx); env_name[env_name_idx] = '\0'; obs_len = strlen(obs_passwd); pwent_node = gtm_tls_find_pwent(env_name); if (NULL != pwent_node) { pwent = pwent_node->pwent; if ((obs_len == (size_t)(pwent->passwd_len * 2)) && (0 == strncmp(obs_passwd, pwent->passwd, obs_len))) return 1; /* already on the list */ } /* Either no entry for tlsid or need to replace with new value */ pwent = MALLOC(sizeof(passwd_entry_t)); assert(NULL != pwent); strcpy(pwent->env_name, env_name); pwent->env_value = MALLOC(obs_len + 1); memcpy(pwent->env_value, obs_passwd, obs_len + 1); /* include null */ pwent->passwd = NULL; pwent->passwd_len = 0; if (0 == gc_update_passwd(pwent->env_name, &pwent, NULL, GTMTLS_OP_NOPWDENVVAR)) { pwent_node = MALLOC(SIZEOF(gtmtls_passwd_list_t)); pwent_node->next = gtmtls_passwd_listhead; pwent_node->pwent = pwent; gtmtls_passwd_listhead = pwent_node; } else { if (gtmtls_err_string) { /* gc_update_passwd() uses gtmcrypt_err_string for error messages */ memcpy(gtmtls_err_string, gtmcrypt_err_string, MAX_GTMCRYPT_ERR_STRLEN); gtmtls_err_string[MAX_GTMCRYPT_ERR_STRLEN] = '\0'; } return -1; /* gc_update_passwd freed pwent */ } return 1; } void gtm_tls_prefetch_passwd(gtm_tls_ctx_t *tls_ctx, char *env_name) { char *env_name_ptr, *env_value, prompt[GTM_PASSPHRASE_MAX_ASCII + 1]; gtmtls_passwd_list_t *pwent_node; passwd_entry_t *pwent; assert((NULL != (env_value = getenv(env_name))) && (0 == STRLEN(env_value))); if (!(GTMTLS_OP_INTERACTIVE_MODE & tls_ctx->flags)) return; /* Not running in an interactive mode. Cannot prompt for password. */ env_name_ptr = (char *)env_name; assert(PASSPHRASE_ENVNAME_MAX > STRLEN(env_name_ptr)); assert(SIZEOF(GTMTLS_PASSWD_ENV_PREFIX) - 1 < STRLEN(env_name_ptr)); env_name_ptr += (SIZEOF(GTMTLS_PASSWD_ENV_PREFIX) - 1); SNPRINTF(prompt, GTM_PASSPHRASE_MAX_ASCII, "Enter passphrase for TLSID %s: ", env_name_ptr); pwent = NULL; if (0 == gc_update_passwd(env_name, &pwent, prompt, TRUE)) { pwent_node = MALLOC(SIZEOF(gtmtls_passwd_list_t)); pwent_node->next = gtmtls_passwd_listhead; pwent_node->pwent = pwent; gtmtls_passwd_listhead = pwent_node; } /* else, something went wrong while acquiring the password. Don't report it now. Later, `gtm_tls_socket' makes another * attempt to acquire the password. */ } static int copy_tlsid_elem(const config_t *tmpcfg, config_t *cfg, config_setting_t *tlsid, const char *idstr, const char *elemname, int type); static int copy_tlsid_elem(const config_t *tmpcfg, config_t *cfg, config_setting_t *tlsid, const char *idstr, const char *elemname, int type) { config_setting_t *srcelem, *elem; char cfg_path[MAX_CONFIG_LOOKUP_PATHLEN]; SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.%s", idstr, elemname); if (srcelem = config_lookup(tmpcfg, cfg_path)) { elem = config_lookup(cfg, cfg_path); if (NULL == elem) { /* option not currently in tls.idstr so create */ elem = config_setting_add(tlsid, elemname, type); if (NULL == elem) { gtm_tls_set_error(NULL, "Failed to add TLSID: %s item %s to config file: %s", idstr, elemname, config_error_text(cfg)); return -1; } } if (CONFIG_TYPE_STRING == type) config_setting_set_string(elem, config_setting_get_string(srcelem)); else if (CONFIG_TYPE_INT == type) { config_setting_set_int(elem, config_setting_get_int(srcelem)); config_setting_set_format(elem, config_setting_get_format(srcelem)); } else { gtm_tls_set_error(NULL, "gtm_tls_impl.c/copy_tlsid_elem: Unexpected CONFIG_TYPE %d for item %s", type, elemname); return -1; } } return 0; } int gtm_tls_add_config(gtm_tls_ctx_t *tls_ctx, const char *idstr, const char *configstr) { # ifndef LIBCONFIG_VER_MAJOR gtm_tls_set_error(NULL, "TLSID: %s: libconfig 1.4.x is needed to support adding config information", idstr); return -1; # else config_t *cfg, tmpcfg; config_setting_t *tlsid, *tlssect; char cfg_path[MAX_CONFIG_LOOKUP_PATHLEN]; config_init(&tmpcfg); if (CONFIG_FALSE == config_read_string(&tmpcfg, configstr)) { gtm_tls_set_error(NULL, "Failed to add config information: %s in line %d:\n%s", config_error_text(&tmpcfg), config_error_line(&tmpcfg), configstr); return -1; } cfg = >m_tls_cfg; strncpy(cfg_path, "tls", SIZEOF("tls")); tlssect = config_lookup(cfg, cfg_path); if (NULL == tlssect) { /* need to add tls section */ tlssect = config_setting_add(config_root_setting(cfg), "tls", CONFIG_TYPE_GROUP); if (NULL == tlssect) { gtm_tls_set_error(NULL, "Failed to add tls section to config file: %s", config_error_text(cfg)); return -1; } } SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s", idstr); tlsid = config_lookup(cfg, cfg_path); if (NULL == tlsid) { /* add new section named tls.idstr */ tlsid = config_setting_add(tlssect, idstr, CONFIG_TYPE_GROUP); if (NULL == tlsid) { gtm_tls_set_error(NULL, "Failed to add TLSID: %s section to config file: %s", idstr, config_error_text(cfg)); return -1; } } /* add any new gtm_tls_socket level options below */ if (-1 == copy_tlsid_elem(&tmpcfg, cfg, tlsid, idstr, "verify-mode", CONFIG_TYPE_STRING)) return -1; if (-1 == copy_tlsid_elem(&tmpcfg, cfg, tlsid, idstr, "cipher-list", CONFIG_TYPE_STRING)) return -1; if (-1 == copy_tlsid_elem(&tmpcfg, cfg, tlsid, idstr, "cert", CONFIG_TYPE_STRING)) return -1; if (-1 == copy_tlsid_elem(&tmpcfg, cfg, tlsid, idstr, "key", CONFIG_TYPE_STRING)) return -1; if (-1 == copy_tlsid_elem(&tmpcfg, cfg, tlsid, idstr, "format", CONFIG_TYPE_STRING)) return -1; if (-1 == copy_tlsid_elem(&tmpcfg, cfg, tlsid, idstr, "ssl-options", CONFIG_TYPE_STRING)) return -1; if (-1 == copy_tlsid_elem(&tmpcfg, cfg, tlsid, idstr, "verify-depth", CONFIG_TYPE_INT)) return -1; if (-1 == copy_tlsid_elem(&tmpcfg, cfg, tlsid, idstr, "verify-level", CONFIG_TYPE_STRING)) return -1; if (-1 == copy_tlsid_elem(&tmpcfg, cfg, tlsid, idstr, "session-id-hex", CONFIG_TYPE_STRING)) return -1; if (-1 == copy_tlsid_elem(&tmpcfg, cfg, tlsid, idstr, "CAfile", CONFIG_TYPE_STRING)) return -1; config_destroy(&tmpcfg); return 0; # endif } gtm_tls_socket_t *gtm_tls_socket(gtm_tls_ctx_t *tls_ctx, gtm_tls_socket_t *prev_socket, int sockfd, char *id, int flags) { int len, verify_mode, verify_mode_set, nocert, nopkey, parse_len, verify_level, verify_level_set; int tlscafile; int session_id_len; long options_mask, options_current, options_clear, verify_long, level_long, level_clear; char *optionendptr, *parse_ptr; # if (((LIBCONFIG_VER_MAJOR == 1) && (LIBCONFIG_VER_MINOR >= 4)) || (LIBCONFIG_VER_MAJOR > 1)) int verify_depth, session_timeout; # else long int verify_depth, session_timeout; # endif char cfg_path[MAX_CONFIG_LOOKUP_PATHLEN], input_env_name[PASSPHRASE_ENVNAME_MAX + 1], *env_name_ptr; char prompt[GTM_PASSPHRASE_MAX_ASCII + 1]; const char *cert, *private_key, *format, *cipher_list, *options_string, *verify_mode_string; const char *verify_level_string, *session_id_hex, *CAfile = NULL; unsigned char session_id_string[SSL_MAX_SSL_SESSION_ID_LENGTH]; FILE *fp; SSL *ssl; SSL_CTX *ctx; EVP_PKEY *evp_pkey = NULL; config_t *cfg; config_setting_t *cfg_setting; # if OPENSSL_VERSION_NUMBER < 0x009080dfL unsigned int cfg_line; const char *cfg_file; # endif gtmtls_passwd_list_t *pwent_node; passwd_entry_t *pwent; gtm_tls_socket_t *socket; STACK_OF(X509_NAME) *CAcerts; # ifndef SSL_OP_NO_COMPRESSION STACK_OF(SSL_COMP)* compression; # endif ctx = tls_ctx->ctx; cfg = >m_tls_cfg; /* Create a SSL object. This object will be used for the actual I/O: recv/send */ if (NULL == (ssl = SSL_new(ctx))) { SET_AND_APPEND_OPENSSL_ERROR("Failed to obtain a new SSL/TLS object."); return NULL; } if ('\0' != id[0]) { SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s", id); cfg_setting = config_lookup(cfg, cfg_path); if (NULL == cfg_setting) { gtm_tls_set_error(NULL, "TLSID %s not found in configuration file.", id); SSL_free(ssl); return NULL; } SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.verify-mode", id); if (CONFIG_TRUE == config_lookup_string(cfg, cfg_path, &verify_mode_string)) { verify_long = 0; parse_ptr = parse_SSL_options(>m_ssl_verify_mode_list[0], SIZEOF(gtm_ssl_verify_mode_list), verify_mode_string, &verify_long, NULL); if (NULL != parse_ptr) { optionendptr = strstr((const char *)parse_ptr, OPTIONENDSTR); if (NULL == optionendptr) parse_len = strlen(parse_ptr); else parse_len = optionendptr - parse_ptr; gtm_tls_set_error(NULL, "In TLSID: %s - unknown verify-mode option: %.*s", id, parse_len, parse_ptr); SSL_free(ssl); return NULL; } verify_mode = (int)verify_long; if (SSL_VERIFY_PEER & verify_mode) flags |= GTMTLS_OP_VERIFY_PEER; verify_mode_set = TRUE; } else if (GTMTLS_OP_ABSENT_VERIFYMODE & tls_ctx->flags) { verify_mode = SSL_VERIFY_PEER; verify_mode_set = TRUE; } else verify_mode_set = FALSE; SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.verify-level", id); if (CONFIG_TRUE == config_lookup_string(cfg, cfg_path, &verify_level_string)) { level_long = GTMTLS_OP_VERIFY_LEVEL_MASK & tls_ctx->flags; level_clear = 0; parse_ptr = parse_SSL_options(>m_ssl_verify_level_list[0], SIZEOF(gtm_ssl_verify_level_list), verify_level_string, &level_long, &level_clear); if (NULL != parse_ptr) { optionendptr = strstr((const char *)parse_ptr, OPTIONENDSTR); if (NULL == optionendptr) parse_len = strlen(parse_ptr); else parse_len = optionendptr - parse_ptr; gtm_tls_set_error(NULL, "In TLSID: %s - unknown verify-level option: %.*s", id, parse_len, parse_ptr); SSL_free(ssl); return NULL; } if (0 != level_clear) level_long &= ~level_clear; verify_level = (int)level_long; verify_level_set = TRUE; } else verify_level_set = FALSE; SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.cipher-list", id); if (CONFIG_TRUE == config_lookup_string(cfg, cfg_path, &cipher_list)) { if ('\0' == cipher_list[0]) /* use default instead of tls.cipher-list if empty string */ cipher_list = (GTMTLS_OP_SOCKET_DEV & flags) ? GTM_DEFAULT_CIPHER_LIST : REPL_CIPHER_LIST; } else cipher_list = NULL; } else if (!CLIENT_MODE(flags)) { /* server mode needs certificate and thus tlsid */ gtm_tls_set_error(NULL, "Server mode requires a certificate but no TLSID specified"); SSL_free(ssl); return NULL; } else { assert(GTMTLS_OP_SOCKET_DEV & flags); cipher_list = NULL; if (GTMTLS_OP_ABSENT_VERIFYMODE & tls_ctx->flags) { verify_mode = SSL_VERIFY_PEER; verify_mode_set = TRUE; } else verify_mode_set = FALSE; verify_level_set = FALSE; } if (verify_mode_set) SSL_set_verify(ssl, verify_mode, NULL); if (verify_level_set) flags = (GTMTLS_OP_VERIFY_LEVEL_CMPLMNT & flags) | verify_level; else flags = (GTMTLS_OP_VERIFY_LEVEL_CMPLMNT & flags) | (GTMTLS_OP_VERIFY_LEVEL_MASK & tls_ctx->flags); if (NULL == cipher_list) { /* no cipher-list in labelled section or no section */ if (0 != ((GTMTLS_OP_ABSENT_CIPHER | GTMTLS_OP_DEFAULT_CIPHER) & tls_ctx->flags)) { /* no or default cipher specified top level */ cipher_list = (GTMTLS_OP_SOCKET_DEV & flags) ? GTM_DEFAULT_CIPHER_LIST : REPL_CIPHER_LIST; } } if ((NULL != cipher_list) && (0 >= SSL_set_cipher_list(ssl, cipher_list)) #if OPENSSL_VERSION_NUMBER >= 0x10101000L && (0 >= SSL_set_ciphersuites(ssl, cipher_list)) #endif ) { SET_AND_APPEND_OPENSSL_ERROR("Failed to add Cipher-List command string: %s.", cipher_list); SSL_free(ssl); return NULL; } if ('\0' != id[0]) { /* First lookup the certificate and private key associated with the provided id in the configuration file. */ SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.cert", id); if (!config_lookup_string(cfg, cfg_path, &cert)) { if (!CLIENT_MODE(flags)) { if (NULL == config_root_setting(cfg)) { /* not sure this is possible */ gtm_tls_set_error(NULL, "Certificate required for TLSID: %s" " but no configuration information available.", id); } else { gtm_tls_set_error(NULL, "Certificate corresponding to TLSID: %s" " not found in configuration file.", id); } SSL_free(ssl); return NULL; } else nocert = TRUE; } else nocert = FALSE; SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.key", id); if (config_lookup_string(cfg, cfg_path, &private_key)) { if (nocert) { gtm_tls_set_error(NULL, "Private key but no certificate corresponding to TLSID:" " %s in configuration file.", id); SSL_free(ssl); return NULL; } } else if (!nocert) private_key = cert; /* assume both in one file */ /* Verify that the format, if specified, is of PEM type as that's the only kind we support now. */ SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.format", id); if (config_lookup_string(cfg, cfg_path, &format)) { if (nocert) { gtm_tls_set_error(NULL, "Format but no certificate corresponding to TLSID: %s" " in configuration file.", id); SSL_free(ssl); return NULL; } if (((SIZEOF("PEM") - 1) != strlen(format)) || (format[0] != 'P') || (format[1] != 'E') || (format[2] != 'M')) { gtm_tls_set_error(NULL, "Unsupported format type %s found for TLSID: %s.", format, id); SSL_free(ssl); return NULL; } } if (!nocert) { /* Setup the certificate to be used for this connection */ if (!SSL_use_certificate_file(ssl, cert, SSL_FILETYPE_PEM)) { SET_AND_APPEND_OPENSSL_ERROR("Failed to add certificate %s.", cert); SSL_free(ssl); return NULL; } /* Before setting up the private key, check-up on the password for the private key. */ SNPRINTF(input_env_name, PASSPHRASE_ENVNAME_MAX, GTMTLS_PASSWD_ENV_PREFIX "%s", id); /* Lookup to see if we have already prefetched the password. */ pwent_node = gtm_tls_find_pwent(input_env_name); if (NULL == pwent_node) { /* Lookup failed. Create a new entry for the given id. */ pwent = NULL; SNPRINTF(prompt, GTM_PASSPHRASE_MAX_ASCII, "Enter passphrase for TLSID %s:", id); if (0 != gc_update_passwd(input_env_name, &pwent, prompt, 0)) { SSL_free(ssl); return NULL; } pwent_node = MALLOC(SIZEOF(gtmtls_passwd_list_t)); pwent_node->next = gtmtls_passwd_listhead; pwent_node->pwent = pwent; gtmtls_passwd_listhead = pwent_node; } else pwent = pwent_node->pwent; assert((NULL != pwent) && (NULL != pwent_node)); /* Setup the private key corresponding to the certificate and the callback function to obtain * the password for the key. We cannot use the much simpler SSL_use_PrivateKey file to load * the private key file because we want fine grained control on the password callback mechanism. * For this purpose, use the PEM_read_PrivateKey function which * supports callbacks for individual private keys. */ fp = fopen(private_key, "r"); if (NULL != fp) { evp_pkey = PEM_read_PrivateKey(fp, &evp_pkey, &passwd_callback, pwent); fclose(fp); } else evp_pkey = NULL; if (NULL == evp_pkey) { if (NULL == fp) { gtm_tls_set_error(NULL, "Private Key corresponding to TLSID:" " %s - error opening file %s: %s.", id, private_key, strerror(errno)); } else if (ERR_GET_REASON(ERR_peek_error()) == PEM_R_NO_START_LINE) { /* give clearer error if only cert given but it doesn't have the key */ gtm_tls_set_error(NULL, "Private Key corresponding to TLSID:" " %s not found in configuration file.", id); } else { SET_AND_APPEND_OPENSSL_ERROR("Failed to read private key %s.", private_key); } SSL_free(ssl); return NULL; } if (!SSL_use_PrivateKey(ssl, evp_pkey)) { SET_AND_APPEND_OPENSSL_ERROR("Failed to use private key %s.", private_key); SSL_free(ssl); return NULL; } /* Verify that private key matches the certificate */ if (!SSL_check_private_key(ssl)) { SET_AND_APPEND_OPENSSL_ERROR("Consistency check failed for private key: %s and certificate: %s\n", private_key, cert); SSL_free(ssl); return NULL; } } } # ifdef SSL_OP_ALLOW_CLIENT_RENEGOTIATION /* The Receiver Server does this to let the Source Server initiate renegotiation. OpenSSL 3.0 requires the * server side to explicitly allow client side initiated renegotiations. This was done to mitigate the * possibility of a denial-of-service attack caused by a client repeatedly requesting renegotiation. This should * not be a problem for replication since the Source and Receiver Servers negotiate a number of parameters * before starting TLS (protection by protocol). */ if (flags & GTMTLS_OP_RENEGOTIATE_REQUESTED) { assert(!CLIENT_MODE(flags)); SSL_set_options(ssl, SSL_OP_ALLOW_CLIENT_RENEGOTIATION); } # endif /* OpenSSL does not recommend enabling compression as the current state of the SSL/TLS protocol does not specify identifiers * for compression libraries thereby allowing for incompatibilities when different SSL/TLS implementations are used in the * client and the server. So, disable compression. */ # ifdef SSL_OP_NO_COMPRESSION SSL_set_options(ssl, SSL_OP_NO_COMPRESSION); # else /* OpenSSL versions <= 0.9.8 enabled compression by default and did not provide an easy way to turn-off compression. Below * is a work-around that zeroes out all the compression methods explicitly in the compression structures effectively * disabling compression. */ compression = SSL_COMP_get_compression_methods(); sk_SSL_COMP_zero(compression); # endif /* When SSL_MODE_AUTO_RETRY is set, SSL_read, SSL_write, and others will not return when interrupted. It can also cause * hangs when used with select()/poll() (see SSL_set_mode man page.) */ /* While SSL_clear_mode was't documented until OpenSSL 1.1.1, it has been defined in openssl/ssl.h since at least 1.0.1d */ # ifdef SSL_clear_mode SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY); /* on by default since 1.1.1 */ # endif if (GTMTLS_OP_NONBLOCK_WRITE & flags) { SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); } if ('\0' != id[0]) { SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.ssl-options", id); if (CONFIG_TRUE == config_lookup_string(cfg, cfg_path, &options_string)) { options_mask = options_current = SSL_get_options(ssl); options_clear = 0; parse_ptr = parse_SSL_options(>m_ssl_options_list[0], SIZEOF(gtm_ssl_options_list), options_string, &options_mask, &options_clear); if (NULL != parse_ptr) { optionendptr = strstr((const char *)parse_ptr, OPTIONENDSTR); if (NULL == optionendptr) parse_len = strlen(parse_ptr); else parse_len = optionendptr - parse_ptr; gtm_tls_set_error(NULL, "In TLSID: %s - unknown ssl-options option: %.*s", id, parse_len, parse_ptr); SSL_free(ssl); return NULL; } if (options_current != options_mask) options_mask = SSL_set_options(ssl, options_mask); if (0 != options_clear) # if OPENSSL_VERSION_NUMBER >= 0x009080dfL options_mask = SSL_clear_options(ssl, options_clear); # else { /* clear_options first in OpenSSL 0.9.8m */ cfg_setting = config_lookup(cfg, cfg_path); cfg_file = config_setting_source_file(cfg_setting); cfg_line = config_setting_source_line(cfg_setting); gtm_tls_set_error(NULL, "Unable to negate values in %s - need OpenSSL 0.9.8m or" " newer in %s line %d", cfg_path, cfg_file, cfg_line); SSL_free(ssl); return NULL; } # endif } SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.verify-depth", id); if (CONFIG_TRUE == config_lookup_int(cfg, cfg_path, &verify_depth)) SSL_set_verify_depth(ssl, verify_depth); } if (!CLIENT_MODE(flags)) { /* Socket created for server mode operation. Set a session ID context for session resumption at the time of * reconnection. */ SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.session-id-hex", id); if (CONFIG_TRUE == config_lookup_string(cfg, cfg_path, &session_id_hex)) { /* convert hex to char and set len */ session_id_len = (int)strnlen(session_id_hex, (size_t)MAX_SESSION_ID_LEN); assert((0 < session_id_len) && (MAX_SESSION_ID_LEN >= session_id_len)); GC_UNHEX(session_id_hex, session_id_string, session_id_len); if (-1 == session_id_len) { gtm_tls_set_error(NULL, "In TLSID: %s - invalid session-id-hex value: %s", id, session_id_hex); tls_errno = -1; SSL_free(ssl); return NULL; } session_id_len = session_id_len / 2; /* bytes */ } else { session_id_len = (int)strnlen(id, (size_t)(SSL_MAX_SSL_SESSION_ID_LENGTH - 1)); assert((0 < session_id_len) && (SSL_MAX_SSL_SESSION_ID_LENGTH > session_id_len)); memcpy((char *)session_id_string, id, (size_t)session_id_len); /* default to tlsid */ session_id_string[session_id_len] = '\0'; } if (0 >= SSL_set_session_id_context(ssl, (const unsigned char *)session_id_string, (unsigned int)session_id_len)) { SET_AND_APPEND_OPENSSL_ERROR("Failed to set Session-ID context to enable session resumption."); SSL_free(ssl); return NULL; } #if OPENSSL_VERSION_NUMBER < 0x30000000L /* Set up Ephemeral Diffie-Hellman key exchange callback. This callback is invoked whenever, during the connection * time, OpenSSL requires Diffie-Hellman key parameters. The SSL_OP_SINGLE_DH_USE is turned on so that the same * private key is not used for each session. This means a little extra computation during the time of handshake, but * is recommended by the OpenSSL community. */ if (-1 == init_dhparams()) { SSL_free(ssl); return NULL; } if (dh512 && dh1024) { SSL_set_options(ssl, SSL_OP_SINGLE_DH_USE); SSL_set_tmp_dh_callback(ssl, tmp_dh_callback); } #else SSL_set_dh_auto(ssl, 1); #endif } tlscafile = config_lookup_string(cfg, "tls.CAfile", &CAfile); SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.CAfile", id); config_lookup_string(cfg, cfg_path, &CAfile); /* if absent CAfile retains value */ if (NULL != CAfile) { if (!(GTMTLS_OP_CA_LOADED & tls_ctx->flags)) { /* no CAfile or CApath before so do now */ if (!SSL_CTX_load_verify_locations(tls_ctx->ctx, CAfile, NULL)) { SET_AND_APPEND_OPENSSL_ERROR("Failed to load CA verification location: %s.", CAfile); SSL_free(ssl); return NULL; } tls_ctx->flags |= GTMTLS_OP_CA_LOADED; } if (!CLIENT_MODE(flags)) { /* these SSL calls are server side only */ CAcerts = SSL_load_client_CA_file(CAfile); if (NULL == CAcerts) { SET_AND_APPEND_OPENSSL_ERROR("Failed to load client CA file %s", CAfile); SSL_free(ssl); return NULL; } SSL_set_client_CA_list(ssl, CAcerts); flags |= GTMTLS_OP_CLIENT_CA; } } /* Finally, wrap the Unix TCP/IP socket into SSL/TLS object */ if (0 >= SSL_set_fd(ssl, sockfd)) { SET_AND_APPEND_OPENSSL_ERROR("Failed to associate TCP/IP socket descriptor %d with an SSL/TLS descriptor", sockfd); SSL_free(ssl); return NULL; } if (NULL == prev_socket) { socket = MALLOC(SIZEOF(gtm_tls_socket_t)); socket->session = NULL; } else socket = prev_socket; socket->flags = flags; socket->ssl = ssl; socket->gtm_ctx = tls_ctx; session_id_len = (int)strnlen(id, (size_t)MAX_TLSID_LEN); strncpy(socket->tlsid, (const char *)id, MAX_TLSID_LEN); socket->tlsid[session_id_len] = '\0'; socket->errstr = NULL; /* Now, store the `socket' structure in the `SSL' structure so that we can get it back in a callback that receives an * `SSL' structure. Ideally, we should be using SSL_set_ex_data/SSL_get_ex_data family of functions. But, these functions * operate on a specific index (obtained by calling SSL_get_ex_new_index). But, since the library should potentially * support multiple sockets, we need to be able to associate the index returned by OpenSSL with a structure which is not * straightforward. So, use OpenSSL's compatibility interface which lets the application store *one* piece of application * data in the SSL structure for later retrieval. It does so by reserving index '0' for the compatibility interface. */ SSL_set_app_data(ssl, socket); /* This way, we can get back the `socket' structure in a callback that receives `SSL'. */ return socket; } int gtm_tls_connect(gtm_tls_socket_t *socket) { int rv; long verify_result; assert(CLIENT_MODE(socket->flags)); if (NULL != socket->session) { /* Old session available. Reuse it. */ SSL_DPRINT(stderr, "gtm_tls_connect(1): references=%d\n", ((SSL_SESSION *)(socket->session))->references); if (0 >= (rv = SSL_set_session(socket->ssl, socket->session))) return ssl_error(socket, rv, X509_V_OK); SSL_DPRINT(stderr, "gtm_tls_connect(2): references=%d\n", ((SSL_SESSION *)(socket->session))->references); } if (0 < (rv = SSL_connect(socket->ssl))) { if (NULL != socket->session) SSL_DPRINT(stderr, "gtm_tls_connect(3): references=%d\n", ((SSL_SESSION *)(socket->session))->references); verify_result = SSL_get_verify_result(socket->ssl); if (X509_V_OK == verify_result) return 0; } else verify_result = SSL_get_verify_result(socket->ssl); return ssl_error(socket, rv, verify_result); } int gtm_tls_accept(gtm_tls_socket_t *socket) { int rv; long verify_result; rv = SSL_accept(socket->ssl); verify_result = SSL_get_verify_result(socket->ssl); if ((0 < rv) && (X509_V_OK == verify_result)) return 0; return ssl_error(socket, rv, verify_result); } int gtm_tls_renegotiate(gtm_tls_socket_t *socket) { int rv; long vresult; #if OPENSSL_VERSION_NUMBER >= 0x10101000L if (TLS1_3_VERSION == SSL_version(socket->ssl)) { if (0 >= (rv = SSL_key_update(socket->ssl, SSL_KEY_UPDATE_REQUESTED))) return ssl_error(socket, rv, SSL_get_verify_result(socket->ssl)); } else /* TLS1_2_VERSION */ #endif { if (0 >= (rv = SSL_renegotiate(socket->ssl))) return ssl_error(socket, rv, SSL_get_verify_result(socket->ssl)); } do { rv = SSL_do_handshake(socket->ssl); vresult = SSL_get_verify_result(socket->ssl); if ((0 < rv) && (X509_V_OK == vresult)) return 0; /* On a blocking socket, SSL_do_handshake returns ONLY after successful completion. However, if the system call * is interrupted (say, by a SIGALRM), it can return with a WANT_READ or WANT_WRITE. Handle it by retrying. * Ideally, we should return back to the caller and let it handle WANT_READ/WANT_WRITE and call us again, but * since renegotiation is done seldomly and returning the control back to the caller causes interface issues, we * handle GTMTLS_WANT_READ or GTMTLS_WANT_WRITE by retrying. */ rv = ssl_error(socket, rv, vresult); } while ((GTMTLS_WANT_READ == rv) || (GTMTLS_WANT_WRITE == rv)); return rv; } # if (((LIBCONFIG_VER_MAJOR == 1) && (LIBCONFIG_VER_MINOR >= 4)) || (LIBCONFIG_VER_MAJOR > 1)) #define VERIFY_DEPTH_TYPE int # else #define VERIFY_DEPTH_TYPE long int #endif STATICFNDEF int gtm_tls_renegotiate_options_config(gtm_tls_socket_t *socket, char *idstr, int flags, config_t *cfg, VERIFY_DEPTH_TYPE *verify_depth, int *verify_depth_set, int *verify_mode, int *verify_mode_set, int *verify_level, int *verify_level_set, int *session_id_len, unsigned char *session_id_string, const char **CAfile); STATICFNDEF int gtm_tls_renegotiate_options_config(gtm_tls_socket_t *socket, char *idstr, int flags, config_t *cfg, VERIFY_DEPTH_TYPE *verify_depth, int *verify_depth_set, int *verify_mode, int *verify_mode_set, int *verify_level, int *verify_level_set, int *session_id_len, unsigned char *session_id_string, const char **CAfile) { int rv, parse_len; config_setting_t *tlsid, *tlssect, *cfg_setting; char cfg_path[MAX_CONFIG_LOOKUP_PATHLEN]; long options_mask, options_current, options_clear, verify_long, level_long, level_clear; char *optionendptr, *parse_ptr; const char *verify_mode_string, *verify_level_string, *session_id_hex; STACK_OF(X509_NAME) *CAcerts; SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.verify-depth", idstr); if (CONFIG_TRUE == config_lookup_int(cfg, cfg_path, verify_depth)) *verify_depth_set = TRUE; SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.verify-mode", idstr); if (CONFIG_TRUE == config_lookup_string(cfg, cfg_path, &verify_mode_string)) { verify_long = 0; parse_ptr = parse_SSL_options(>m_ssl_verify_mode_list[0], SIZEOF(gtm_ssl_verify_mode_list), verify_mode_string, &verify_long, NULL); if (NULL != parse_ptr) { optionendptr = strstr((const char *)parse_ptr, OPTIONENDSTR); if (NULL == optionendptr) parse_len = strlen(parse_ptr); else parse_len = optionendptr - parse_ptr; gtm_tls_set_error(socket, "In TLSID: %s - unknown verify-mode option: %.*s", idstr, parse_len, parse_ptr); tls_errno = -1; return -1; } *verify_mode = (int)verify_long; *verify_mode_set = TRUE; } SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.verify-level", idstr); if (CONFIG_TRUE == config_lookup_string(cfg, cfg_path, &verify_level_string)) { level_long = GTMTLS_OP_VERIFY_LEVEL_MASK & flags; level_clear = 0; parse_ptr = parse_SSL_options(>m_ssl_verify_level_list[0], SIZEOF(gtm_ssl_verify_level_list), verify_level_string, &level_long, &level_clear); if (NULL != parse_ptr) { optionendptr = strstr((const char *)parse_ptr, OPTIONENDSTR); if (NULL == optionendptr) parse_len = strlen(parse_ptr); else parse_len = optionendptr - parse_ptr; gtm_tls_set_error(socket, "In TLSID: %s - unknown verify-level option: %.*s", idstr, parse_len, parse_ptr); tls_errno = -1; return -1; } if (0 != level_clear) level_long &= ~level_clear; *verify_level = (int)level_long; *verify_level_set = TRUE; } SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.CAfile", idstr); rv = config_lookup_string(cfg, cfg_path, CAfile); SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.session-id-hex", idstr); if (CONFIG_TRUE == config_lookup_string(cfg, cfg_path, &session_id_hex)) { /* convert hex to char and set len */ *session_id_len = STRLEN(session_id_hex); if (MAX_SESSION_ID_LEN < *session_id_len) *session_id_len = MAX_SESSION_ID_LEN; /* avoid overrun */ GC_UNHEX(session_id_hex, session_id_string, *session_id_len); if (-1 == *session_id_len) { gtm_tls_set_error(socket, "In TLSID: %s - invalid session-id-hex value: %s", idstr, session_id_hex); tls_errno = -1; return -1; } *session_id_len = *session_id_len / 2; /* bytes */ } return 0; } int gtm_tls_renegotiate_options(gtm_tls_socket_t *socket, int msec_timeout, char *idstr, char *configstr, int tlsid_present) { int rv; config_t *cfg, tmpcfg; config_setting_t *tlsid, *tlssect, *cfg_setting; char cfg_path[MAX_CONFIG_LOOKUP_PATHLEN]; int verify_mode, parse_len, verify_level; int verify_mode_set, verify_level_set, verify_depth_set, flags; int session_id_len; long options_mask, options_current, options_clear, verify_long, level_long, level_clear; char *optionendptr, *parse_ptr; const char *verify_mode_string, *verify_level_string; const char *CAfile = NULL, *session_id_hex; unsigned char session_id_string[SSL_MAX_SSL_SESSION_ID_LENGTH]; # if (((LIBCONFIG_VER_MAJOR == 1) && (LIBCONFIG_VER_MINOR >= 4)) || (LIBCONFIG_VER_MAJOR > 1)) int verify_depth; # else long int verify_depth; # endif SSL *ssl; STACK_OF(X509_NAME) *CAcerts; ssl = socket->ssl; flags = socket->flags; gtm_tls_get_error(socket); verify_mode_set = verify_level_set = verify_depth_set = FALSE; if ('\0' != idstr[0]) { /* process options from config file and/or options */ cfg = >m_tls_cfg; session_id_len = 0; if (tlsid_present) { /* process config file first if real tlsid */ SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s", idstr); cfg_setting = config_lookup(cfg, cfg_path); if (NULL == cfg_setting) { gtm_tls_set_error(socket, "TLSID %s not found in configuration file.", idstr); tls_errno = -1; return -1; } if (0 != gtm_tls_renegotiate_options_config(socket, idstr, flags, cfg, &verify_depth, &verify_depth_set, &verify_mode, &verify_mode_set, &verify_level, &verify_level_set, &session_id_len, session_id_string, &CAfile)) return -1; } if (NULL != configstr) { /* now process any options given on WRITE /TLS */ # ifndef LIBCONFIG_VER_MAJOR gtm_tls_set_error(socket, "TLSID: %s: libconfig 1.4.x is needed to support providing options on WRITE /TLS", idstr); tls_errno = -1; return -1; # else config_init(&tmpcfg); if (CONFIG_FALSE == config_read_string(&tmpcfg, configstr)) { gtm_tls_set_error(socket, "Failed to process options: %s in line %d:\n%s", config_error_text(&tmpcfg), config_error_line(&tmpcfg), configstr); tls_errno = -1; return -1; } if (0 != gtm_tls_renegotiate_options_config(socket, idstr, flags, &tmpcfg, &verify_depth, &verify_depth_set, &verify_mode, &verify_mode_set, &verify_level, &verify_level_set, &session_id_len, session_id_string, &CAfile)) return -1; # endif } /* now really process verify-* and CAfile options */ if (verify_depth_set) SSL_set_verify_depth(ssl, verify_depth); if (verify_mode_set) SSL_set_verify(ssl, verify_mode, NULL); if (verify_level_set) flags = (GTMTLS_OP_VERIFY_LEVEL_CMPLMNT & flags) | verify_level; if ((NULL == CAfile) && !(GTMTLS_OP_CLIENT_CA & flags)) { /* check for tlsid.CAfile or tls.CAfile */ SNPRINTF(cfg_path, MAX_CONFIG_LOOKUP_PATHLEN, "tls.%s.CAfile", socket->tlsid); rv = config_lookup_string(cfg, cfg_path, &CAfile); if (CONFIG_FALSE == rv) { rv = config_lookup_string(cfg, "tls.CAfile", &CAfile); } } if (NULL != CAfile) { CAcerts = SSL_load_client_CA_file(CAfile); if (NULL == CAcerts) { SET_AND_APPEND_OPENSSL_ERROR("Failed to load client CA file %s", CAfile); tls_errno = -1; return -1; } SSL_set_client_CA_list(ssl, CAcerts); flags |= GTMTLS_OP_CLIENT_CA; } if ((0 < session_id_len) && (0 >= SSL_set_session_id_context(ssl, (const unsigned char *)session_id_string, (unsigned int)session_id_len))) { SET_AND_APPEND_OPENSSL_ERROR("Failed to set Session-ID context to enable session resumption."); tls_errno = -1; return -1; } } socket->flags = flags; rv = gtm_tls_renegotiate(socket); return rv; } int gtm_tls_get_conn_info(gtm_tls_socket_t *socket, gtm_tls_conn_info *conn_info) { long verify_result, timeout, creation_time; int session_id_length; unsigned int ssl_version; const SSL_CIPHER *cipher; const COMP_METHOD *compression_method; char *ssl_version_ptr, *session_id_ptr; gtm_tls_ctx_t *tls_ctx; X509 *peer; SSL *ssl; EVP_PKEY *pubkey; SSL_SESSION *session; ssl = socket->ssl; tls_ctx = socket->gtm_ctx; peer = SSL_get_peer_certificate(ssl); if ((NULL != peer) || (GTMTLS_OP_SOCKET_DEV & socket->flags)) { /* if socket device and no certificate from peer still provide info */ verify_result = SSL_get_verify_result(ssl); if ((X509_V_OK == verify_result) || (GTMTLS_OP_ABSENT_CONFIG & tls_ctx->flags)) { /* return information for Socket clients even without a config file */ /* SSL-Session Protocol */ switch (ssl_version = SSL_version(ssl)) { case SSL2_VERSION: ssl_version_ptr = "SSLv2"; break; case SSL3_VERSION: ssl_version_ptr = "SSLv3"; break; case TLS1_VERSION: ssl_version_ptr = "TLSv1"; break; case TLS1_1_VERSION: ssl_version_ptr = "TLSv1.1"; break; case TLS1_2_VERSION: ssl_version_ptr = "TLSv1.2"; break; #if OPENSSL_VERSION_NUMBER >= 0x10101000L case TLS1_3_VERSION: ssl_version_ptr = "TLSv1.3"; break; #endif default: ssl_version_ptr = NULL; SNPRINTF(conn_info->protocol, SIZEOF(conn_info->protocol), "unknown protocol 0x%X", ssl_version); break; } if (NULL != ssl_version_ptr) strncpy(conn_info->protocol, ssl_version_ptr, MAX_ALGORITHM_LEN); /* SSL-Session Cipher Algorithm */ cipher = SSL_get_current_cipher(ssl); SNPRINTF(conn_info->session_algo, SIZEOF(conn_info->session_algo), "%s", SSL_CIPHER_get_name(cipher)); /* Remote Certificate Asymmetric Algorithm */ if (NULL != peer) { pubkey = X509_get_pubkey(peer); # if OPENSSL_VERSION_NUMBER >= 0x10000001L SNPRINTF(conn_info->cert_algo, SIZEOF(conn_info->cert_algo), "%s", OBJ_nid2ln(EVP_PKEY_base_id(pubkey))); # else SNPRINTF(conn_info->cert_algo, SIZEOF(conn_info->cert_algo), "%s", OBJ_nid2ln(pubkey->type)); # endif } else conn_info->cert_algo[0] = '\0'; /* Is Secure Renegotiation Supported? */ # if OPENSSL_VERSION_NUMBER >= 0x009080dfL /* SSL_get_secure_renegotiation_support function was introduced in OpenSSL version >= "0.9.8m". */ conn_info->secure_renegotiation = SSL_get_secure_renegotiation_support(ssl); # else conn_info->secure_renegotiation = FALSE; # endif /* Is the session reused? */ conn_info->reused = SSL_session_reused(ssl); /* Negotiated Session-ID. */ if (NULL == (session = SSL_get1_session(ssl))) /* `get1' version is used to increment reference count. */ { gtm_tls_set_error(socket, "Failed to obtain the handle to negotiated SSL/TLS session"); return -1; } session_id_ptr = (char *)SSL_SESSION_get_id(session, (unsigned int *)&session_id_length); assert(session_id_length <= (MAX_SESSION_ID_LEN / 2)); assert(MAX_SESSION_ID_LEN >= (SSL_MAX_SSL_SESSION_ID_LENGTH * 2)); if ((MAX_SESSION_ID_LEN / 2) < session_id_length) session_id_length = MAX_SESSION_ID_LEN / 2; /* avoid overrun */ GC_HEX(session_id_ptr, conn_info->session_id, session_id_length * 2); conn_info->session_id[session_id_length * 2] = '\0'; /* Session expiry timeout. */ if (0 >= (timeout = SSL_SESSION_get_timeout(session))) conn_info->session_expiry_timeout = -1; else { creation_time = SSL_SESSION_get_time(session); if (0 == creation_time) conn_info->session_expiry_timeout = -1; else conn_info->session_expiry_timeout = creation_time + timeout; } SSL_SESSION_free(session); /* Is compression supported? */ # ifndef OPENSSL_NO_COMP compression_method = SSL_get_current_compression(ssl); conn_info->compression = compression_method ? (char *)SSL_COMP_get_name(compression_method) : "NONE"; # else conn_info->compression = "NONE"; # endif if (NULL != peer) { /* Remote Certificate Asymmetric Algorithm Strength */ conn_info->cert_nbits = EVP_PKEY_bits(pubkey); /* Remote Certificate Subject */ X509_NAME_oneline(X509_get_subject_name(peer), conn_info->subject, MAX_X509_LEN); /* Remote Certificate Issuer */ X509_NAME_oneline(X509_get_issuer_name(peer), conn_info->issuer, MAX_X509_LEN); /* Certificate Expiry */ if (-1 == format_ASN1_TIME(X509_get_notBefore(peer), conn_info->not_before, MAX_TIME_STRLEN)) SNPRINTF(conn_info->not_before, MAX_TIME_STRLEN, "Bad certificate date"); if (-1 == format_ASN1_TIME(X509_get_notAfter(peer), conn_info->not_after, MAX_TIME_STRLEN)) SNPRINTF(conn_info->not_after, MAX_TIME_STRLEN, "Bad certificate date"); X509_free(peer); } else { conn_info->cert_nbits = 0; conn_info->subject[0] = conn_info->issuer[0] = '\0'; conn_info->not_before[0] = conn_info->not_after[0] = '\0'; } if (GTM_TLS_API_VERSION_SOCK <= socket->gtm_ctx->version) conn_info->options = SSL_get_options(ssl); if (GTM_TLS_API_VERSION_RENEGOPT <= socket->gtm_ctx->version) { conn_info->total_renegotiations = SSL_total_renegotiations(ssl); conn_info->verify_mode = SSL_get_verify_mode(ssl); } return 0; } else { gtm_tls_set_error(socket, "Peer certificate invalid: %s", X509_verify_cert_error_string(verify_result)); X509_free(peer); return -1; } } else gtm_tls_set_error(socket, "No certificate sent from the remote side"); return -1; } int gtm_tls_send(gtm_tls_socket_t *socket, char *buf, int send_len) { int rv; long verify_result; if (0 < (rv = SSL_write(socket->ssl, buf, send_len))) { assert(SSL_ERROR_NONE == SSL_get_error(socket->ssl, rv)); verify_result = SSL_get_verify_result(socket->ssl); if (X509_V_OK == verify_result) return rv; } else verify_result = SSL_get_verify_result(socket->ssl); return ssl_error(socket, rv, verify_result); } int gtm_tls_recv(gtm_tls_socket_t * socket, char *buf, int recv_len) { int rv; long verify_result; rv = SSL_read(socket->ssl, buf, recv_len); #ifdef DEBUG /* Emulate an error condition in case of WBTEST_REPL_TLS_RECONN white box*/ if ((NULL != (wbox_enable = getenv("gtm_white_box_test_case_enable"))) && (NULL != (wbox_tls_check = getenv("gtm_white_box_test_case_number"))) && (NULL != (wbox_count = getenv("wbox_count"))) && (NULL != (wbox_test_count= getenv("gtm_white_box_test_case_count"))) && (1 == (wbox_enable_val = atoi(wbox_enable))) && (WBTEST_REPL_TLS_RECONN == (wbox_tls_check_val = atoi(wbox_tls_check))) && (wbox_test_count_val = atoi(wbox_test_count)) && (wbox_count_val = atoi(wbox_count)) && (wbox_test_count_val == wbox_count_val)) rv = -1; #endif if (0 < rv) { assert(SSL_ERROR_NONE == SSL_get_error(socket->ssl, rv)); verify_result = SSL_get_verify_result(socket->ssl); if (X509_V_OK == verify_result) return rv; } else verify_result = SSL_get_verify_result(socket->ssl); return ssl_error(socket, rv, verify_result); } int gtm_tls_cachedbytes(gtm_tls_socket_t *socket) { return SSL_pending(socket->ssl); } void gtm_tls_socket_close(gtm_tls_socket_t *socket) { if ((NULL == socket) || (NULL == socket->ssl)) { tls_errno = 0; return; } /* Invoke SSL_shutdown to close the SSL/TLS connection. Although the protocol (and the OpenSSL library) supports * bidirectional shutdown (which waits for the peer's "close notify" alert as well), we intend to only send the * "close notify" alert and be done with it. This is because the process is done with the connection when it calls * this function and we don't want to consume additional time waiting for a "close notify" acknowledge signal from the * other side. */ if (!(GTMTLS_OP_NOSHUTDOWN & socket->flags)) { tls_errno = 0; SSL_shutdown(socket->ssl); } tls_errno = 0; SSL_free(socket->ssl); socket->ssl = NULL; } void gtm_tls_session_close(gtm_tls_socket_t **socket) { SSL_SESSION *session; gtm_tls_socket_t *sock; sock = *socket; if (NULL == sock) return; if (NULL != sock->ssl) gtm_tls_socket_close(sock); if (NULL != (session = sock->session)) { SSL_DPRINT(stderr, "gtm_tls_session_close: references=%d\n", session->references); SSL_SESSION_free(session); } sock->session = NULL; FREE(*socket); *socket = NULL; } void gtm_tls_fini(gtm_tls_ctx_t **tls_ctx) { gtmtls_passwd_list_t *node, *prev_node; /* Free up the SSL/TLS context */ assert(*tls_ctx); SSL_CTX_free((*tls_ctx)->ctx); FREE(*tls_ctx); *tls_ctx = NULL; /* Free up the libconfig context */ config_destroy(>m_tls_cfg); /* Relinquish all the memory allocated by libconfig */ /* Free up the gtmtls_passwd_list_t linked list */ node = gtmtls_passwd_listhead; while (NULL != node) { gc_freeup_pwent(node->pwent); prev_node = node; node = node->next; FREE(prev_node); } } fis-gtm-V7.0-005/sr_unix/gtm_tls_impl.h0000644000032200000250000000324014342376330016703 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2021 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. * * * ****************************************************************/ #ifndef GTM_TLS_IMPL_H #define GTM_TLS_IMPL_H STATICFNDEF int format_ASN1_TIME(ASN1_TIME *tm, char *buf, int maxlen); STATICFNDEF int ssl_generic_vfy_callback(int preverify_ok, X509_STORE_CTX *ctx); STATICFNDEF int passwd_callback(char *buf, int size, int rwflag, void *userdata); STATICFNDEF int new_session_callback(SSL *ssl, SSL_SESSION *session); STATICFNDEF void remove_session_callback(SSL_CTX *ctx, SSL_SESSION *session); STATICFNDEF DH *read_dhparams(const char *dh_fn); STATICFNDEF int init_dhparams(void); STATICFNDEF DH *tmp_dh_callback(SSL *ssl, int is_export, int keylength); STATICFNDEF int ssl_error(gtm_tls_socket_t *tls_sock, int err, long verify_result); typedef struct gtmtls_passwd_list_struct { struct gtmtls_passwd_list_struct *next; passwd_entry_t *pwent; } gtmtls_passwd_list_t; STATICFNDEF gtmtls_passwd_list_t *gtm_tls_find_pwent(const char *input_env_name); #define GET_SOCKFD(TLS) SSL_get_fd((SSL *)TLS) #define VERIFY_PEER(FLAGS) (FLAGS & GTMTLS_OP_VERIFY_PEER) #define CLIENT_MODE(FLAGS) (FLAGS & GTMTLS_OP_CLIENT_MODE) #define DEFAULT_SESSION_TIMEOUT 3600 /* Old sessions can be reused upto 1 hour since the creation time. */ #endif fis-gtm-V7.0-005/sr_unix/gtm_tls_interface.h0000644000032200000250000004713314342376330017713 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2022 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. * * * ****************************************************************/ #ifndef GTM_TLS_INTERFACE_H #define GTM_TLS_INTERFACE_H #ifndef GTM_TLS_INTERFACE_DEFINITIONS_INCLUDED #define GTM_TLS_INTERFACE_DEFINITIONS_INCLUDED #define GTM_TLS_API_VERSION 0x00000006 #define GTM_TLS_API_VERSION_SOCK 0x00000002 /* when TLS sockets added */ #define GTM_TLS_API_VERSION_RENEGOPT 0x00000004 /* WRITE /TLS renegotiate with options */ #define GTM_TLS_API_VERSION_NONBLOCK 0x00000005 /* WRITE /BLOCK("OFF") */ #define GTM_TLS_API_VERSION_GET_ERROR 0x00000006 /* gtm_tls_get_error() socket ptr for errbuf */ #define MAX_X509_LEN 256 #define MAX_ALGORITHM_LEN 64 #define MAX_TIME_STRLEN 32 #define MAX_TLSID_LEN 32 #define MAX_SESSION_ID_LEN 64 /* twice SSL_MAX_SSL_SESSION_ID_LENGTH since in hex */ #define INVALID_TLS_CONTEXT NULL #define INVALID_TLS_SOCKET NULL /* SSL/TLS protocol allows for renegoitations (change in cipher spec, etc.) to take place at any point during the communication. So, * it is likely for a call to `gtm_tls_recv' or `gtm_tls_send' to initiate a renegoitation sequence in which case a `gtm_tls_recv' * might have to 'send' data and `gtm_tls_send' to 'recv' data. In such cases, the SSL/TLS reference implementation returns one of * the following two return codes. They do not indicate an error situation. Instead, they indicate that the reference implementation * could not successfully complete the operation without reading or writing more data to the underlying TCP/IP connection and gives * a chance to the application to wait for the underlying TCP/IP pipe to become ready for a read (if GTMTLS_WANT_READ is returned) * or write (if GTMTLS_WANT_WRITE is returned). */ #define GTMTLS_WANT_READ -2 #define GTMTLS_WANT_WRITE -3 #define GTMTLS_PASSWD_ENV_PREFIX "gtmtls_passwd_" /* below also defined in gtmcrypt_util.h so prevent redefinition in gtm_tls_impl.h */ #ifndef GTM_PASSPHRASE_MAX #define GTM_PASSPHRASE_MAX 512 /* obfuscated */ #define GTM_PASSPHRASE_MAX_ASCII (GTM_PASSPHRASE_MAX / 2) #elif GTM_PASSPHRASE_MAX != 512 #error "GTM-E-GTMTLSINTERFACE different values for GTM_PASSPHRASE_MAX" #endif /* Note these flags may be in either the ctx or ssl structures but not all * may have meaning in both. */ /* GTMTLS_OP_INTERACTIVE_MODE and GTMTLS_OP_NOPWDENVVAR must match definition in gtmcrypt_interface,h */ /* Whether the library is loaded in an interactive environment so that password prompting can happen if needed. */ #define GTMTLS_OP_INTERACTIVE_MODE 0x00000001 /* Turn-on compression for SSL/TLS protocol. */ #define GTMTLS_OP_ENABLE_COMPRESSION 0x00000002 /* Socket created for a client-mode operation. */ #define GTMTLS_OP_CLIENT_MODE 0x00000004 /* Peer verification is needed. */ #define GTMTLS_OP_VERIFY_PEER 0x00000008 /* Socket device */ #define GTMTLS_OP_SOCKET_DEV 0x00000010 /* Socket (client) created for direct mode auditing (APD). Temporarily defined with GTMTLS_OP_SOCKET_DEV's value */ #define GTMTLS_OP_DM_AUDIT 0x00000010 /* No cipher list specifed at top tls level */ #define GTMTLS_OP_ABSENT_CIPHER 0x00000020 /* Default cipher list used */ #define GTMTLS_OP_DEFAULT_CIPHER 0x00000040 /* REPL_CIPHER_LIST used */ #define GTMTLS_OP_REPL_CIPHER 0x00000080 /* No verify mode specifed at top tls level */ #define GTMTLS_OP_ABSENT_VERIFYMODE 0x00000100 /* This is SSL_OP_ALLOW_CLIENT_RENEGOTIATION; Previously set between V62001-V62002A, but not used */ #define GTMTLS_OP_RENEGOTIATE_REQUESTED 0x00000200 /* No gtmcrypt_config or tls in config needed for client only use */ #define GTMTLS_OP_ABSENT_CONFIG 0x00000400 /* No environment variable for password - used by gc_update_passwd so must be same in gtmcrypt_interface.h */ #define GTMTLS_OP_NOPWDENVVAR 0x00000800 /* Bit mask for VERIFY_LEVEL options - one now and one planned but allow two more */ #define GTMTLS_OP_VERIFY_LEVEL_MASK 0x0000F000 #define GTMTLS_OP_VERIFY_LEVEL_CMPLMNT 0x7FFF0FFF /* 4SCA: 32bit complement */ /* Check SSL_get_verify_result after SSL_connect and other useful places */ #define GTMTLS_OP_VERIFY_LEVEL_CHECK 0x00001000 /* Default VERIFY_LEVEL options */ #define GTMTLS_OP_VERIFY_LEVEL_DEFAULT GTMTLS_OP_VERIFY_LEVEL_CHECK /* For socket level only - explicit SSL_set_client_CA_list has been done */ #define GTMTLS_OP_CLIENT_CA 0x00010000 /* CAfile or CApath processed */ #define GTMTLS_OP_CA_LOADED 0x00020000 #define GTMTLS_OP_NOSHUTDOWN 0x00040000 /* NON BLOCKing WRITE so set SSL_MODE_ENABLE_PARTIAL_WRITE */ #define GTMTLS_OP_NONBLOCK_WRITE 0x00080000 #define GTMTLS_IS_FIPS_MODE(CTX) (TRUE == CTX->fips_mode) #define GTMTLS_RUNTIME_LIB_VERSION(CTX) (CTX->runtime_version) typedef struct gtm_tls_conn_info_struct { /* SSL Session Information */ char protocol[MAX_ALGORITHM_LEN]; /* Descriptive name of the negoitiated protocol (for instance, TLSv1). */ char session_algo[MAX_ALGORITHM_LEN];/* Algorithm negoitated by the SSL/TLS session. */ char session_id[MAX_SESSION_ID_LEN + 1]; /* Hexadecimal representation of the negotiated Session-ID. */ char *compression; /* Compression method used for the SSL/TLS session. */ int secure_renegotiation; /* 1 if SSL/TLS renegotiation is supported and 0 otherwise. */ int reused; /* Is the session reused? */ long session_expiry_timeout; /* Time at which this session expires (-1 if doesn't expire). */ /* Remote Certificate Information */ char cert_algo[MAX_ALGORITHM_LEN]; /* Certificate's asymmetric cryptography algorithm. */ int cert_nbits; /* Strength of the certificate's asymmetric cryptography. */ char subject[MAX_X509_LEN]; /* To whom the certificate belongs? */ char issuer[MAX_X509_LEN]; /* CA who issued the certificate. */ char not_before[MAX_TIME_STRLEN]; /* Date before which the certificate is not valid. */ char not_after[MAX_TIME_STRLEN]; /* Date after which the certificate is not valid. */ /* items after this added for GTM_TLS_API_VERSION_SOCK */ long options; /* bitmask of SSL options */ int renegotiation_pending; /* no handshake yet */ /* items after this added for GTM_TLS_API_VERSION_RENEGOPT */ int total_renegotiations; /* SSL_total_renegotiations */ int verify_mode; /* SSL_get_verify_mode */ } gtm_tls_conn_info; typedef struct gtm_tls_ctx_struct { int flags; int fips_mode; unsigned long compile_time_version; /* OpenSSL version that this library is compiled with. */ unsigned long runtime_version; /* OpenSSL version that this library is currently running with. */ void *ctx; int version; /* caller GTM_TLS_API_VERSION */ int plugin_version; /* added with GTM_TLS_API_VERSION_NONBLOCK */ } gtm_tls_ctx_t; typedef struct gtm_tls_session_struct { int flags; void *ssl; void *session; char tlsid[MAX_TLSID_LEN + 1]; gtm_tls_ctx_t *gtm_ctx; char *errstr; } gtm_tls_socket_t; #endif /* GTM_TLS_INTERFACE_DEFINITIONS_INCLUDED */ /* Note: The below function prototypes should be kept in sync with the corresponding declarations/definitions in sr_unix/gtm_tls.h, * sr_unix/gtm_tls.c, and sr_unix/gtm_tls_funclist.h. */ /* Returns the most recent error (null-terminated) related to the workings of the SSL/TLS reference implementation. */ extern const char *gtm_tls_get_error(gtm_tls_socket_t *); /* If the most recent invocation of the SSL/TLS reference implementation resulted in a system call error, `gtm_tls_errno' returns * the value of `errno'. Otherwise, -1 is returned in which case `gtm_tls_get_error' provides more information. */ extern int gtm_tls_errno(void); /* Initializes the SSL/TLS context for a process. Typically invoked only once (unless the previous attempt failed). Attributes * necessary to initialize the SSL/TLS context are obtained from the configuration file pointed to by `$gtmcrypt_config'. * * Arguments: * `version' : The API version that the caller understands. Current version is 0x5. * `flags' : Initialization flags as a bitmask. The first one the API understands is GTMTLS_OP_INTERACTIVE_MODE. * Set this bitmask if the process doing the SSL/TLS initialization is run in an interactive mode. This lets * the API decide if it can prompt for a password if a need arises while decrypting the private key. * * Returns a value, of type gtm_tls_ctx_t, representing the initialized SSL/TLS context. This context can be used, later by the * application, to create as many SSL/TLS aware sockets as needed. In case of an error, INVALID_TLS_CONTEXT is returned in which * case gtm_tls_get_error() provides the necessary error detail. */ extern gtm_tls_ctx_t *gtm_tls_init(int version, int flags); /* Stores a M program provided password for later use. * * Arguments: * `tls_ctx' : The SSL/TLS context corresponding to this process. * `tlsid' : identifier of config file section to select the private key corresponding to this password. * `obs_passwd' : obfuscated password in the same format as a gtmtls_passwd_ environment variable's value. * * Returns: * 1 Success * 0 Not an interactive context * -1 Failure - use gtm_tls_get_error() to get reason */ extern int gtm_tls_store_passwd(gtm_tls_ctx_t *tls_ctx, const char *tlsid, const char *obs_passwd); /* Provides additional information to merge with config file * * Arguments: * `tls_ctx' : The SSL/TLS context corresponding to this process. * `configstr': to be used by config_read_str. * * Returns: * 0 Success * -1 Failure - use gtm_tls_get_error() to get reason */ extern int gtm_tls_add_config(gtm_tls_ctx_t *tls_ctx, const char *idstr, const char *configstr); /* Prefetches the password corresponding to a private key. * * Arguments: * `tls_ctx' : The SSL/TLS context corresponding to this process. * `env_name' : The name of the environment variable that corresponds to the private key in question. The SSL/TLS API identifies * a private key by a name that is specified in the configuration file. The name of the environment variable is then * obtained by prefixing "gtmtls_passwd_" to the identifier. So, if the identifier is `PRODUCTION', then the name * of the environment variable is "gtmtls_passwd_PRODUCTION". * * No return value. Since this is only an attempt to prefetch the password, no error is reported. Another attempt will be made, * later to acquire the password when actually decrypting the private key. * * Note 1: This function is typically invoked whenever the application detects that an environment variable of the form * "gtmtls_passwd_" is present in the environment but doesn't have any assoicated value. In such a case the below * function prompts the user to provide the password before continuing any further. The password is not validated, but is stored * for future use. * * Note 2: The function honors the GTMTLS_OP_INTERACTIVE_MODE flag passed to the `gtm_tls_init' function. If the application has * initialized the SSL/TLS API in a non-interactive mode, the API does not prompt the user for password. */ extern void gtm_tls_prefetch_passwd(gtm_tls_ctx_t *tls_ctx, char *env_name); /* Converts a Unix TCP/IP socket into a SSL/TLS aware socket. * * Arguments: * `ctx' : The SSL/TLS context corresponding to this process. * `prev_socket` : Pointer to an existing `gtm_tls_socket_t' structure. A non-null value indicates reuse. * `sockfd' : The Unix TCP/IP socket identifier. * `tls_id' : Identifier corresponding to the private key and certificate pair that should be used for future communication. * The plugin searches for the identifier in the configuration file pointed to by `$gtmcrypt_config' to get other * information corresponding to this connection (like, path to the private key, certificate and the format of the * private key). * `flags' : Additional configuration options. See GTMTLS_OP* macros for more details. * * Returns a value, of type gtm_tls_socket_t *, representing the initialized SSL/TLS aware socket. This value can be used for actual * communication to provide security. In case of an error, INVALID_TLS_SOCKET is returned in which case gtm_tls_get_error() * provides the necessary error detail. If `prev_socket' is non-NULL, then that storage area is used for setting up the new socket * instead of creating a new storage area. * * Note 1: If the password corresponding to the `tls_id' has not yet been prefetched by the SSL/TLS API, then the API attempts to * read the password from the environment. * * Note 2: The function honors the GTMTLS_OP_INTERACTIVE_MODE flag passed to the `gtm_tls_init' function. If the application has * initialized the SSL/TLS API in a non-interactive mode, this function does not prompt the user for password. */ extern gtm_tls_socket_t *gtm_tls_socket(gtm_tls_ctx_t *ctx, gtm_tls_socket_t *prev_socket, int sockfd, char *id, int flags); /* Connects using SSL/TLS aware socket. Assumes the other transport endpoint understands SSL/TLS. * * Arguments: * `socket' : The SSL/TLS socket (initialized using `gtm_tls_socket'). * * Returns 0 if the connection is successful. Otherwise, one of -1, GTMTLS_WANT_READ or GTMTLS_WANT_WRITE is returned. In case of * -1, `gtm_tls_errno' and `gtm_tls_get_error' can be used to obtain the necessary error detail. * * Note: The function makes use of an existing SSL session (if one is available). */ extern int gtm_tls_connect(gtm_tls_socket_t *socket); /* Accepts an incoming connection using SSL/TLS aware socket. Assumes the other transport endpoint understands SSL/TLS. * * Arguments: * `socket' : The SSL/TLS socket (initialized using `gtm_tls_socket'). * * Returns 0 if the connection is successful. Otherwise, one of -1, GTMTLS_WANT_READ or GTMTLS_WANT_WRITE is returned. In case of * -1, `gtm_tls_errno' and `gtm_tls_get_error' can be used to obtain the necessary error detail. * */ extern int gtm_tls_accept(gtm_tls_socket_t *socket); /* Renegotiates an active SSL/TLS connection. Note: This function does the renegotiation in a blocking fashion and more importantly * handles EINTR internally by retrying the renegotiation. * * Arguments: * `socket' : The SSL/TLS socket (initialized using `gtm_tls_socket'). * * Return value: none. */ extern int gtm_tls_renegotiate(gtm_tls_socket_t *socket); /* Process configuration file options for WRITE /TLS("renegotiate") and then calls gtm_tls_renegotiate * * Arguments: * `socket' : The SSL/TLS socket (initialized using `gtm_tls_socket'). * * Return value: none. */ extern int gtm_tls_renegotiate_options(gtm_tls_socket_t *socket, int msec_timeout, char *idstr, char *configstr, int tlsid_present); /* Obtains additional SSL/TLS related information on the peer. This function is typically invoked to log information for diagnostic * purposes. * * Arguments: * `socket' : The SSL/TLS socket (initialized using `gtm_tls_socket'). * `conn_info' : A pointer to the `gtm_tls_conn_info' structure. * * Returns 0 if the connection is successful (and conn_info structure is filled). Otherwise, one of -1, GTMTLS_WANT_READ or * GTMTLS_WANT_WRITE is returned. In case of -1, `gtm_tls_errno' and `gtm_tls_get_error' can be used to obtain the necessary error * detail. */ extern int gtm_tls_get_conn_info(gtm_tls_socket_t *socket, gtm_tls_conn_info *conn_info); /* Transmits message securely to the transport endpoint. This function should be invoked ONLY after successful invocations of either * `gtm_tls_connect' or `gtm_tls_accept'. * * Arguments: * `socket' : The SSL/TLS socket (initialized using `gtm_tls_socket'). * `buf' : Buffer containing the message that has to be transmitted. * `send_len' : Length of the message that has to be transmitted. * * If successful, returns the number of bytes (> 0) actually sent through the SSL/TLS connection. Otherwise, one of -1, * GTMTLS_WANT_READ or GTMTLS_WANT_WRITE is returned. In case of -1, `gtm_tls_errno' and `gtm_tls_get_error' can be used to obtain * the necessary error detail. */ extern int gtm_tls_send(gtm_tls_socket_t *socket, char *buf, int send_len); /* Receives message securely from the transport endpoint. This function should be invoked ONLY after successful invocations of * either `gtm_tls_connect' or `gtm_tls_accept'. * * Arguments: * `socket' : The SSL/TLS socket (initialized using `gtm_tls_socket'). * `buf' : Buffer in which the received data (after having decrypted) has to be placed. * `recv_len' : Upper bound on the amount of data that can be received. * become ready for a `send' or `recv'. * * If successful, returns the number of bytes (> 0) actually received from the SSL/TLS connection. Otherwise, one of -1, * GTMTLS_WANT_READ or GTMTLS_WANT_WRITE is returned. In case of -1, `gtm_tls_errno' and `gtm_tls_get_error' can be used to obtain * the necessary error detail. */ extern int gtm_tls_recv(gtm_tls_socket_t *socket, char *buf, int recv_len); /* Returns the number of bytes cached in the SSL/TLS layer and is ready for immediate retrieval with the `gtm_tls_recv'. * * Arguments: * `socket' : The SSL/TLS socket (initialized using `gtm_tls_socket'). * * Note: Data from the peer is received in blocks. Therefore, data can be buffered inside the SSL/TLS layer and is ready for * immediate retrieval with `gtm_tls_recv'. Given this, it is important for the application to call this function, if `select' * or `poll' on the underlying TCP/IP socket indicates that the subsequent `recv' will block, and check if there are any bytes * readily available. */ extern int gtm_tls_cachedbytes(gtm_tls_socket_t *socket); /* Close the SSL/TLS socket connection. * * Arguments: * `socket' : Pointer to the SSL/TLS socket (initialized using `gtm_tls_socket'). * * Note: This function merely shuts down the active SSL/TLS connection. The session is still preserved in the SSL/TLS internal * structures for reuse when a connection is made with the same server at a later point. * * Returns 0 if successful and -1 otherwise. In case of an error, `gtm_tls_errno' and `gtm_tls_get_error' can be used to obtain * necessary error detail. * */ extern void gtm_tls_socket_close(gtm_tls_socket_t *socket); /* Closes an active SSL/TLS session. This frees up the session and thus makes the session not resuable for a future connection. * Any subsequent connection will create a new session. * * Note: The function takes a pointer to the gtm_tls_socket_t structure. This is because it forces the actual `socket' value to be * INVALID_TLS_SOCKET. */ extern void gtm_tls_session_close(gtm_tls_socket_t **socket); /* Frees up any memory allocated by the SSL/TLS context. This function should typically be invoked at process exit. * * Arguments: * `ctx' : Pointer to the SSL/TLS context (initialized using `gtm_tls_init'). * * No return value. * * Note: The function takes a pointer to the gtm_tls_ctx_t structure. This is because it forces the actual `ctx' value to be * INVALID_TLS_CONTEXT. */ extern void gtm_tls_fini(gtm_tls_ctx_t **ctx); /* Return the value of GTM_TLS_API_VERSION for the gtm tls plugin. This can be * used to check if the plugin supports features needed by the caller. * * Argument: * caller_version : GTM_TLS_API_VERSION of caller - not used currently. * * Returns the GTM_TLS_API_VERSION of the plugin. */ extern int gtm_tls_version(int caller_version); #endif fis-gtm-V7.0-005/sr_unix/gtm_tls_loadlibrary.c0000644000032200000250000000774214342376330020254 0ustar librarygtc/**************************************************************** * * * Copyright 2013, 2014 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 #include #ifdef _AIX #include #endif #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_limits.h" #include "lv_val.h" #include "real_len.h" #include "fgncal.h" /* needed for COPY_DLLERR_MSG() */ #include "gtmmsg.h" #include "gtmcrypt.h" typedef void (*gtm_tls_func_t)(); /* A generic pointer type to the TLS functions exposed by the plugin */ #define TLS_DEF(x) x##_, enum { #include "gtm_tls_funclist.h" /* BYPASSOK */ gtm_tls_func_n /* total number of TLS functions that needs to be dlsym()ed */ }; #undef TLS_DEF #define TLS_DEF(x) GBLDEF gtm_tls_func_t x##_fptr; #include "gtm_tls_funclist.h" #undef TLS_DEF #define GTM_TLS_LIBNAME "libgtmtls.so" GBLREF char dl_err[MAX_ERRSTR_LEN]; GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF boolean_t gtm_dist_ok_to_use; error_def(ERR_GTMDISTUNVERIF); /* Cannot include gtm_tls.h in this module due to conflicting GBLDEF/GBLREFs. So, redefine the function prototype here to silent * the compiler. */ int gtm_tls_loadlibrary(void); int gtm_tls_loadlibrary() { /* Initialize the table of symbol names to be used in dlsym() */ # define TLS_DEF(x) #x, char *gtm_tls_fname[] = { # include "gtm_tls_funclist.h" NULL }; # undef TLS_DEF /* Initialize the table of locations of function pointers that are set by dlsym() */ gtm_tls_func_t fptr; # define TLS_DEF(x) &x##_fptr, gtm_tls_func_t *gtm_tls_fptr[] = { # include "gtm_tls_funclist.h" NULL }; # undef TLS_DEF void_ptr_t *handle; char *err_str, libpath[GTM_PATH_MAX], util_libpath[GTM_PATH_MAX]; int findx; # ifdef _AIX char new_libpath_env[GTM_PATH_MAX], *save_libpath_ptr, plugin_dir_path[GTM_PATH_MAX]; char save_libpath[GTM_PATH_MAX]; # endif if(!gtm_dist_ok_to_use) { SNPRINTF(dl_err, MAX_ERRSTR_LEN, "%%GTM-E-GTMDISTUNVERIF, Environment variable $gtm_dist (%s) " "could not be verified against the executables path", gtm_dist); return -1; } # ifdef _AIX SNPRINTF(plugin_dir_path, GTM_PATH_MAX, "%s/%s", gtm_dist, GTMCRYPT_PLUGIN_DIR_NAME); SNPRINTF(libpath, GTM_PATH_MAX, "%s/%s/%s", gtm_dist, GTMCRYPT_PLUGIN_DIR_NAME, GTM_TLS_LIBNAME); /* Prefix LIBPATH with "$gtm_dist/plugin" so that dlopen can find the helper library (libgtmcryptutil.so). */ if (NULL == (save_libpath_ptr = getenv(LIBPATH_ENV))) SNPRINTF(new_libpath_env, GTM_PATH_MAX, "%s", plugin_dir_path); else { /* Since the setenv below can potentially thrash the save_libpath_ptr, take a copy of it for later restore. */ strncpy(save_libpath, save_libpath_ptr, SIZEOF(save_libpath)); save_libpath[SIZEOF(save_libpath) - 1] = '\0'; SNPRINTF(new_libpath_env, GTM_PATH_MAX, "%s:%s", plugin_dir_path, save_libpath); } setenv(LIBPATH_ENV, new_libpath_env, TRUE); # else SNPRINTF(libpath, GTM_PATH_MAX, "%s/%s/%s", gtm_dist, GTMCRYPT_PLUGIN_DIR_NAME, GTM_TLS_LIBNAME); # endif if (NULL == (handle = dlopen(libpath, RTLD_GLOBAL | RTLD_NOW))) { COPY_DLLERR_MSG(err_str, dl_err); return -1; } # ifdef _AIX /* Restore old LIBPATH. */ if (NULL == save_libpath_ptr) unsetenv(LIBPATH_ENV); else setenv(LIBPATH_ENV, save_libpath, TRUE); /* Now verify that "libgtmcryptutil.so" was really loaded from "$gtm_dist/plugin". */ if (!verify_lib_loadpath(GTMCRYPT_UTIL_LIBNAME, plugin_dir_path)) return -1; # endif for (findx = 0; findx < gtm_tls_func_n; ++findx) { fptr = (gtm_tls_func_t)dlsym(handle, gtm_tls_fname[findx]); if (NULL == fptr) { COPY_DLLERR_MSG(err_str, dl_err); return -1; } *gtm_tls_fptr[findx] = fptr; } return 0; } fis-gtm-V7.0-005/sr_unix/gtm_tparm.c0000755000032200000250000000344314342376330016206 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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. * * * ****************************************************************/ /* * This file wraps the curses tparm function to avoid a conflict with * the bool typedef in mdef.h and curses.h. Since the only use * appears to be in sr_unix/iott_use.c, this is a reasonable detour. * The alternative would be to excise bool from all unix files, insuring * that persistent structures were correctly sized. */ #include "gtm_tparm.h" #include #include "gtm_term.h" #include "gtm_sizeof.h" #if defined(__MVS__) && __CHARSET_LIB==1 /* -qascii */ #include "ebc_xlat.h" static char gtm_tparm_buf[64]; /* static so it can be returned, room for expanded escape sequence */ #endif char *gtm_tparm(char *c, int p1, int p2) { #if !(defined(__MVS__) && __CHARSET_LIB==1) /* -qascii */ return tparm(c, p1, p2, 0, 0, 0, 0, 0, 0, 0); #else int len; char ebcdicbuf[32]; /* ESC_LEN in io.h is 16, allow extra */ char *retptr; len = strlen(c) + 1; if (SIZEOF(ebcdicbuf) <= len) len = SIZEOF(ebcdicbuf) - 1; asc_to_ebc((unsigned char *)ebcdicbuf, (unsigned char *)c, len); ebcdicbuf[len] = '\0'; /* ensure null terminated */ retptr = tparm(ebcdicbuf, p1, p2, 0, 0, 0, 0, 0, 0, 0); len = strlen(retptr) + 1; if (SIZEOF(gtm_tparm_buf) <= len) len = SIZEOF(gtm_tparm_buf) - 1; ebc_to_asc((unsigned char *)gtm_tparm_buf, (unsigned char *)retptr, len); gtm_tparm_buf[len] = '\0'; /* ensure null terminated */ return gtm_tparm_buf; #endif } fis-gtm-V7.0-005/sr_unix/gtm_tparm.h0000755000032200000250000000116114342376330016206 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GTM_TPARM_H_INCLUDED #define GTM_TPARM_H_INCLUDED char *gtm_tparm(char *, int p1, int p2); #endif fis-gtm-V7.0-005/sr_unix/gtm_tputs.c0000755000032200000250000000125314342376330016237 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 #include "gtm_term.h" #include "gtm_tputs.h" #ifdef __sparc int gtm_tputs(char *c, int la, int (*putcfunc)(char)) #else int gtm_tputs(char *c, int la, int (*putcfunc)(int)) #endif { return tputs(c, la, putcfunc); } fis-gtm-V7.0-005/sr_unix/gtm_tputs.h0000755000032200000250000000127114342376330016244 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GTM_TPUTS_H_INCLUDED #define GTM_TPUTS_H_INCLUDED #ifdef __sparc int gtm_tputs(char *, int, int (*)(char)); #else int gtm_tputs(char *, int, int (*)(int)); #endif #endif fis-gtm-V7.0-005/sr_unix/gtm_trigger.h0000644000032200000250000001161614342376330016531 0ustar librarygtc/**************************************************************** * * * Copyright 2010, 2014 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. * * * ****************************************************************/ #ifndef GTM_TRIGGER_H #define GTM_TRIGGER_H #include "gtm_trigger_trc.h" #define TRIGGER_NAME_RESERVED_SPACE 2 /* Reserved space in trigger name to make name unique if needbe */ /* This macro is used where it is possible for the top frame to be a trigger base frame if we are in * "trigger-no-mans-land" which is characterized by us having created the trigger base frame but no * trigger execution frame on the stack that would have incremented gtm_trigger_depth (gtm_trigger_depth * is only incremented for the duration of the trigger execution frame which also means gtm_trigger and * dm_start are also on the stack). To be in this state at this point, we must have driven at least one of * a parallel trigger set, returned back to gvtr_match_n_invoke, then, while retrieving the trigger source * for the next parallel trigger, we hit a restart condition. There are a few different handlers than can * catch this condition depending on where this macro is used but they all share one thing in common: * In a normal restart condition while the trigger is running, gtm_trigger itself will unwind the trigger * base frame during restarts but in this condition where we are in "trigger-no-mans-land", we need to * unwind the trigger. We use an interuptable state flag that contains this nomansland state to determine * if we are eligible for this unwind. If not, this is an out-of-design condition we need to protect * against. */ #define TRIGGER_BASE_FRAME_UNWIND_IF_NOMANSLAND \ { \ if (SFT_TRIGR & frame_pointer->type) \ { \ assertpro(INTRPT_IN_TRIGGER_NOMANS_LAND == intrpt_ok_state); \ /* Remove this errant frame and continue to restart */ \ DBGTRIGR((stderr, "%s: trigger-no-mans-land situation - removing trigger " \ "base frame\n", __FILE__)); \ gtm_trigger_fini(FALSE, FALSE); \ } \ } typedef enum { /* Trigger rethrow types */ trigr_rethrow_nil, trigr_rethrow_zg1, /* Rethrow level-only ZGOTO (op_zg1) */ trigr_rethrow_zgoto, /* Rethrow level/entryref ZGOTO (op_zgoto) */ trigr_rethrow_goerrorfrm /* Rethrow goerrorframe() unwind */ } trigr_rethrow_t; /* Second parm to gtm_trigger() containing non-static type items for a given trigger */ typedef struct { mval *ztoldval_new; /* mval to set into $ZTOLDVAL. * Points to stringpool at "gtm_trigger" entry - is NOT updated by the function */ mval *ztvalue_new; /* mval for current value to set into $ZTVALUE. * Points to stringpool at "gtm_trigger" entry. */ const mval *ztdata_new; /* $Data status of trigger value being Set/Killed - points to a constant mval * in mtables.c */ const mval *ztdelim_new; /* mval to set into $ZTDELIM - points to a string pool entry */ const mval *ztriggerop_new; /* Opcode type invoking the trigger - points to a constant mval in mtables.c */ mval *ztupdate_new; /* mval to set into $ZTUPDATE. * Points to stringpool at "gtm_trigger" entry - is NOT updated by the function */ mval **lvvalarray; /* Value array for lvs named in "lvnamearray" member of gv_trigger_t structure. * Values are derived from subscripts of matching key at runtime. * Note that this points to an array of "mval *" each of which in turn point to * mvals in the M-stack whose strings point to the stringpool. * For performance reasons, we keep this an array of trigdsc->numsubs entries * (not trigdsc->numlvsubs entries). So "gtm_trigger" has to look at * trigdsc->lvindexarray and figure out the actual indices which correspond to * local variables of interest to that trigdsc and obtain the corresponding * indices from this array to get the actual local variable values. * Points to stringpool at "gtm_trigger" entry - is NOT updated by the function */ boolean_t ztvalue_changed; /* Set to TRUE if $ZTVALUE was changed inside the trigger. "ztvalue_new" member * is updated to point to the new value (in the stringpool) in this case. */ /* All above "mval *" fields that point to the stringpool have their corresponding mvals pushed onto the M-stack * so they are safe from stp_gcol already. No special care needs to be taken by the function "gtm_trigger" for this. */ } gtm_trigger_parms; int gtm_trigger(gv_trigger_t *trigdsc, gtm_trigger_parms *trigprm); void gtm_trigger_fini(boolean_t forced_unwind, boolean_t fromzgoto); void gtm_trigger_cleanup(gv_trigger_t *trigdsc); int gtm_trigger_complink(gv_trigger_t *trigdsc, boolean_t dolink); #endif fis-gtm-V7.0-005/sr_unix/gtm_trigger_trc.h0000644000032200000250000000236414342376330017401 0ustar librarygtc/**************************************************************** * * * Copyright 2010, 2014 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. * * * ****************************************************************/ #ifndef GTM_TRIGGER_TRC_H #define GTM_TRIGGER_TRC_H /* Provide some tracing for triggers but due to the somewhat heavy requirements for include * gtm_trigger.h, these have been moved out to this supplemental header file with no * pre-reqs. * Debugging macros for triggers. In open code so are always defined (at least as null). * Uncomment below include and define to enable debugging macros (and set GTM_TRIGGER of course) */ /* #include "have_crit.h" for DBGFPF/FFLUSH/INTRPT_IN_FFLUSH */ /* #define DEBUG_TRIGR */ #if defined(DEBUG_TRIGR) && defined(GTM_TRIGGER) # define DBGIFTRIGR(x) if (is_trigger) DBGFPF(x) # define DBGTRIGR(x) DBGFPF(x) # define DBGTRIGR_ONLY(x) x # include "gtm_stdio.h" # include "gtmio.h" #else # define DBGIFTRIGR(x) # define DBGTRIGR(x) # define DBGTRIGR_ONLY(x) #endif #endif fis-gtm-V7.0-005/sr_unix/gtm_ulimit.h0000755000032200000250000000115614342376330016372 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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. * * * ****************************************************************/ /* Interlude to */ #if !defined(__CYGWIN__) && !(defined(sun) && !defined(__SVR4)) /* no on Cygwin or SunOS 4.1.x */ # include #endif fis-gtm-V7.0-005/sr_unix/gtm_unique_file_util.c0000755000032200000250000000354314342376330020426 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2021 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 "gtm_stdlib.h" #include "mdef.h" #include "gtmxc_types.h" #include "gdsroot.h" #include "is_file_identical.h" #include "is_gdid.h" #include "iosp.h" /* for SS_NORMAL */ /* Checks whether the two fileids passed are identical */ xc_status_t gtm_is_file_identical(xc_fileid_ptr_t fileid1, xc_fileid_ptr_t fileid2) { if (!fileid1 || (!fileid2)) return FALSE; return is_gdid_identical((gd_id_ptr_t) fileid1, (gd_id_ptr_t) fileid2); } /* Converts given filename to unique file id. Uses filename_to_id internally for getting the unique id. Note that, * the allocation of the fileid structure is done here and the caller needs to worry only about free'ing the * allocated pointer via gtm_xcfileid_free. */ xc_status_t gtm_filename_to_id(xc_string_t *filename, xc_fileid_ptr_t *fileid) { boolean_t status; gd_id_ptr_t tmp_fileid; if (!filename) return FALSE; assert(fileid && !*fileid); tmp_fileid = (gd_id_ptr_t)malloc(SIZEOF(gd_id)); status = (SS_NORMAL == filename_to_id(tmp_fileid, filename->address)); *fileid = (xc_fileid_ptr_t)tmp_fileid; return status; } /* Allocation of xc_fileid_ptr_t happens in gtm_filename_to_id. During the close time, encryption library needs to free these * externally allocated resources and this is done through this function. */ void gtm_xcfileid_free(xc_fileid_ptr_t fileid) { if (NULL != fileid) free(fileid); } fis-gtm-V7.0-005/sr_unix/gtm_unlink_all.h0000644000032200000250000000104614342376330017212 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef GTM_UNLINK_ALL_H_INCLUDED #define GTM_UNLINK_ALL_H_INCLUDED void gtm_unlink_all(void); #endif fis-gtm-V7.0-005/sr_unix/gtm_utf8.c0000644000032200000250000003463714342376330015757 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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 #include "gtm_string.h" #include "gtm_stdlib.h" #include "error.h" #include "util.h" #include "gtm_icu_api.h" #include "gtm_utf8.h" #include "cmd_qlf.h" GBLREF command_qualifier cmd_qlf; GBLREF boolean_t badchar_inhibit; GBLREF boolean_t gtm_utf8_mode; GBLREF void (*stx_error_fptr)(int in_error, ...); /* Function pointer for stx_error() so gtm_utf8.c can avoid * pulling in stx_error() in gtmsecshr. */ GBLREF void (*show_source_line_fptr)(boolean_t warn); /* Func ptr for show_source_line() same reason as above */ LITREF mval literal_null; error_def(ERR_BADCHAR); /* Return UTF8 length of mstr string in UTF8 characters */ int utf8_len(mstr* str) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; return utf8_len_real(TREF(compile_time) ? err_stx : err_rts, str); } /* This is the same as "utf8_len" except that it invokes UTF8_BADCHAR_STX macro which does a stx_error instead of rts_error * when an invalid UTF8 character is detected in the string (and badchar_inhibit is not enabled). * If UTF8_BADCHAR_STX is invoked, this function returns a -1 signalling a parse error. */ int utf8_len_stx(mstr* str) { return utf8_len_real(err_stx, str); } /* This is the same as "utf8_len" except that it invokes UTF8_BADCHAR_DEC macro which does a dec_err instead of rts_error. * Note only one "error" is raised for any given string and we return the length as best we can with the broken string. */ int utf8_len_dec(mstr* str) { return utf8_len_real(err_dec, str); } /* This is the same as "utf8_len" except that it always returns a length and does not report errors. */ int utf8_len_silent(mstr* str) { return utf8_len_real(err_ignore, str); } /* The routine that does the actual work of determining the length and responding appropriately in the event an invalid * UTF8 character is detected. */ STATICFNDEF int utf8_len_real(utf8_err_type err_type, mstr* str) { int charlen, bytelen; char *ptrtop, *ptr; boolean_t err_raised; assert(gtm_utf8_mode); if (&literal_null.str == str) { assert((0 == str->char_len) && (0 == str->len)); return 0; } assert((0 <= str->len) && (MAX_STRLEN >= str->len)); ptr = str->addr; ptrtop = ptr + str->len; charlen = 0; err_raised = FALSE; if (!badchar_inhibit) { for (; ptr < ptrtop; charlen++, ptr += bytelen) { if (!UTF8_VALID(ptr, ptrtop, bytelen)) { switch(err_type) { case err_rts: UTF8_BADCHAR(0, ptr, ptrtop, 0, NULL); break; /* Never get here but keeps compiler happy */ case err_stx: UTF8_BADCHAR_STX(0, ptr, ptrtop, 0, NULL); return -1; case err_dec: if (!err_raised) { UTF8_BADCHAR_DEC(0, ptr, ptrtop, 0, NULL); err_raised = TRUE; } case err_ignore: /* WARNING fall through */ bytelen = 1; /* Assume only one char is broken */ break; default: assertpro(FALSE /* Invalid error type */); } } } } else { for (; ptr < ptrtop; charlen++) ptr = (char *)UTF8_MBNEXT(ptr, ptrtop); } assert(!charlen || (ptr == ptrtop)); str->char_len = charlen; return charlen; } /* Similar to utf8_len() except it operates on a given string instead of an mval and does not observe badchar_inhibit. * String must be valid or error is raised. */ int utf8_len_strict(unsigned char* ptr, int len) { int charlen, bytelen; unsigned char *ptrtop; ptrtop = ptr + len; for (charlen = 0; ptr < ptrtop; charlen++, ptr += bytelen) { if (!UTF8_VALID(ptr, ptrtop, bytelen)) UTF8_BADCHAR(0, ptr, ptrtop, 0, NULL); } assert(ptr == ptrtop); return charlen; } /* Returns the total display column width of a UTF-8 string given its address and byte length. * The third parameter (strict) is used to specify how both illegal characters should be handled. * The fourth parameter (nonprintwidth) is to specify what width to give for unprintable characters. * It is currently 0 if coming through $ZWIDTH and 1 if coming through util_output (for historical reasons). * If strict is TRUE, this routine * - triggers BADCHAR error if it encounters any illegal characters irrespective of VIEW BADCHAR setting. * If strict is FALSE, this routine * - does NOT do BADCHAR check. * - treats illegal characters as unprintable characters (for width). */ int gtm_wcswidth(unsigned char* ptr, int len, boolean_t strict, int nonprintwidth) { int strwidth, cwidth; uint4 ch; unsigned char *ptrtop, *ptrnext; assert(gtm_utf8_mode); ptrtop = ptr + len; for (strwidth = 0; ptr < ptrtop; ptr = ptrnext) { ptrnext = UTF8_MBTOWC(ptr, ptrtop, ch); if (WEOF != ch && -1 != (cwidth = UTF8_WCWIDTH(ch))) strwidth += cwidth; else if (strict && (WEOF == ch)) UTF8_BADCHAR(0, ptr, ptrtop, 0, NULL); else strwidth += nonprintwidth; } assert(ptr == ptrtop); return strwidth; } /* Returns the display column width of a character given its code point. This function * returns -1 for control characters and 0 for non-spacing (combining) characters. * * NOTEs: * We are not using libc's wcwidth() due to its inconsistent behavior across different * platforms and its incorrect behavior for several characters (even on Linux). * * ICU does not provide a direct API for display width, however, it does provide API * for the property "East Asian Width" specified in UAX#11 (http://unicode.org/reports/tr11/) * which provides guidelines to determine the width for the entire repertoire. * * Using "East Asian Width" and "General Category" properties. gtm_wcwidth() determines * the column width as below: * - SOFT-HYPHEN is a special format control character with a width of 1. * - Non-spacing combining marks and Enclosing combining marks (general * category codes 'Mn' and 'Me') have a column width of 0. Note that Combing spacing * marks (General Category 'Mc') occupy a column width of 1. * - Conjoining Hangul Jamos (i.e. vowels and trailing consonants between U+1160 - * U+11FF) have a column with of 0. They are like the combining marks in that they * attach to their previous characters (although they categorized as letters). * - All wide characters (East Asian Width - Wide (W) and Full-Width (F)) have a * column width of 2 and all narrow characters (East Asian Width - Narrow (Na) * and Half-Width (H)) have a column width of 1. * - All characters (with East Asian Width - Neutral (N) and Ambiguous (A)) have a * column width of 1. * - All other non-printable (control characters) and unassigned code points (empty blocks) * have a width -1. */ int gtm_wcwidth(wint_t code) { UCharCategory gc; /* General category as defined by the standard */ UEastAsianWidth ea; UHangulSyllableType hst; assert(gtm_utf8_mode); if (0x00ad == code) /* SOFT-HYPHEN, a special format control character */ return 1; gc = (UCharCategory)u_getIntPropertyValue((UChar32)code, UCHAR_GENERAL_CATEGORY); if (U_NON_SPACING_MARK == gc || U_ENCLOSING_MARK == gc || /* combining marks (Mn, Me) */ U_FORMAT_CHAR == gc || /* all other format control (Cf) characters */ U_HST_VOWEL_JAMO == (hst = (UHangulSyllableType)u_getIntPropertyValue((UChar32)code, UCHAR_HANGUL_SYLLABLE_TYPE)) || U_HST_TRAILING_JAMO == hst) /* conjoining hangul jamos (in Korean) */ { return 0; } if (U_ISPRINT((UChar32)code)) { ea = (UEastAsianWidth)u_getIntPropertyValue((UChar32)code, UCHAR_EAST_ASIAN_WIDTH); return (U_EA_FULLWIDTH == ea || U_EA_WIDE == ea) ? 2 : 1; } return -1; } /* This function issues a BADCHAR error and prints the sequences of bytes that comprise the bad multi-byte character. * If "len" is 0, the function determines how many bytes this multi-byte character is comprised of and prints all of it. * If "len" is non-zero, the function prints "len" number of bytes from "str" in the error message. */ void utf8_badchar(int len, unsigned char *str, unsigned char *strtop, int chset_len, unsigned char *chset) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; utf8_badchar_real(TREF(compile_time) ? err_stx : err_rts, len, str, strtop, chset_len, chset); return; } /* This function is the same as "utf8_badchar" except that it does a "stx_error" instead of "rts_error". This helps * to identify the line in the M program that has the compile time error. */ void utf8_badchar_stx(int len, unsigned char *str, unsigned char *strtop, int chset_len, unsigned char *chset) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(TREF(compile_time)); utf8_badchar_real(err_stx, len, str, strtop, chset_len, chset); return; } /* This function is the same as "utf8_badchar" except that it does a "dec_err()" instead of "rts_error". This helps * to identify the line in the M program that has the compile time error but unlike stx_error(), it does not remove * the relevant generated code and replace it with a runtime OC_RTERROR triple. This is because the runtime code does * the same level of checking as the compiler does so can detect the error "normally" on its own - no need for the * complication with trying to cut out the right set of triples. We can just put out the compiler message and let it * be. */ void utf8_badchar_dec(int len, unsigned char *str, unsigned char *strtop, int chset_len, unsigned char *chset) { utf8_badchar_real(err_dec, len, str, strtop, chset_len, chset); return; } /* This function issues a BADCHAR error and prints the sequences of bytes that comprise the bad multi-byte character. * If "len" is 0, the function determines how many bytes this multi-byte character is comprised of and prints all of it. * If "len" is non-zero, the function prints "len" number of bytes from "str" in the error message. * This is the work-horse routine for the 3 above variants of utf8_badchar*(). The differences are in how the error * is delivered and what happens afterwards. For the 3 options: * * err_rts - uses rts_error() to raise the error * err_stx - uses stx_error to raise the error * err_dec - uses dec_err to raise the error */ STATICFNDEF void utf8_badchar_real(utf8_err_type err_type, int len, unsigned char *str, unsigned char *strtop, int chset_len, unsigned char *chset) { unsigned char *strptr, *strend, *outstr; unsigned char errtxt[OUT_BUFF_SIZE]; int tmplen; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(gtm_utf8_mode); if (0 == len) { /* Determine the maximal length (upto 4 bytes) of the invalid byte sequence */ for (strend = str; len <= 4 && strend < strtop; ++strend, ++len) { if (UTF8_VALID(strend, strtop, tmplen)) break; } } else strend = str + len; strptr = str; outstr = &errtxt[0]; for (; strptr < strend; ++strptr, ++outstr) { outstr = (unsigned char*)i2asc((uchar_ptr_t)outstr, *strptr); *outstr = ','; } if (str < strptr) /* do not include the last comma */ outstr--; if (err_dec == err_type) { assert(NULL != show_source_line_fptr); if (!(TREF(compile_time) && !(cmd_qlf.qlf & CQ_WARNINGS))) (*show_source_line_fptr)(TRUE); /* Print errant src line and pointer to where parsing detected the error */ } if (0 < chset_len) { switch(err_type) { case err_rts: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_BADCHAR, 4, (outstr - &errtxt[0]), &errtxt[0], chset_len, chset); break; /* Never get here but keeps compiler happy */ case err_stx: assert(NULL != stx_error_fptr); (*stx_error_fptr)(ERR_BADCHAR, 4, (outstr - &errtxt[0]), &errtxt[0], chset_len, chset); break; case err_dec: if (!(TREF(compile_time) && !(cmd_qlf.qlf & CQ_WARNINGS))) dec_err(VARLSTCNT(6) (TREF(compile_time) ? MAKE_MSG_TYPE(ERR_BADCHAR, WARNING) : ERR_BADCHAR), 4, (outstr - &errtxt[0]), &errtxt[0], chset_len, chset); break; default: assertpro(FALSE /* Invalid error type */); } } else { switch(err_type) { case err_rts: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_BADCHAR, 4, (outstr - &errtxt[0]), &errtxt[0], LEN_AND_LIT(UTF8_NAME)); break; /* Never get here but keeps compiler happy */ case err_stx: assert(NULL != stx_error_fptr); (*stx_error_fptr)(ERR_BADCHAR, 4, (outstr - &errtxt[0]), &errtxt[0], LEN_AND_LIT(UTF8_NAME)); break; case err_dec: if (!(TREF(compile_time) && !(cmd_qlf.qlf & CQ_WARNINGS))) dec_err(VARLSTCNT(6) (TREF(compile_time) ? MAKE_MSG_TYPE(ERR_BADCHAR, WARNING): ERR_BADCHAR), 4, (outstr - &errtxt[0]), &errtxt[0], LEN_AND_LIT(UTF8_NAME)); break; default: assertpro(FALSE /* Invalid error type */); } } } /* This function scans the string from the beginning and stops the moment it finds an invalid byte sequence. * It null-terminates the string from then onwards until the end. */ unsigned char *gtm_utf8_trim_invalid_tail(unsigned char *str, int len) { unsigned char *ptrend, *ptr; int bytelen; ptr = str; ptrend = str + len; while (ptr < ptrend) { if (UTF8_VALID(ptr, ptrend, bytelen)) ptr += bytelen; else break; } for ( ; ptr < ptrend; ptr++) *ptr = '\0'; return ptr; } /* Remove the trailing line terminator from buffer. * buffer line in ICU UChar format * len length of line as number of UChar characters * as given by u_strlen() * Returns number of characters after removing line terminator */ int trim_U16_line_term(UChar *buffer, int len) { int lt_index; UChar32 uc32_cp; if (0 == len) return 0; /* zero length string */ U16_GET(buffer, 0, len - 1, len, uc32_cp); for (lt_index = 0; u32_line_term[lt_index]; lt_index++) if (uc32_cp == u32_line_term[lt_index]) break; if ((U32_LT_LF == lt_index) && (1 < len)) { U16_GET(buffer, 0, len - 2, len, uc32_cp); if (u32_line_term[U32_LT_CR] == uc32_cp) len--; /* trim both CR and LF */ } if (U32_LT_LAST >= lt_index) { buffer[len - 1] = 0; return (len - 1); } return len; /* no line terminator so return it all */ } boolean_t valid_utf_string(const mstr *str) { int charlen, bytelen; char *ptrtop, *ptr; ptr = str->addr; ptrtop = ptr + str->len; charlen = 0; for (; ptr < ptrtop; charlen++, ptr += bytelen) { if (!UTF8_VALID(ptr, ptrtop, bytelen)) { /* Emit a warning if there is an issue*/ UTF8_BADCHAR_DEC(0, ptr, ptrtop, 0, NULL); return FALSE; } } return TRUE; } fis-gtm-V7.0-005/sr_unix/gtm_utf8.h0000755000032200000250000013373714342376330015770 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2022 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. * * * ****************************************************************/ #ifndef GTM_UTF8_H #define GTM_UTF8_H #include #include /* *======================================================================================================================= * UTF-8 BIT DISTRIBUTION: *======================================================================================================================= * Code range Scalar value UTF-8 Notes * hexadecimal binary binary * ----------------------------------------------------------------------------------------------------------------------- * 000000-00007F 0xxxxxxx 0xxxxxxx ASCII equivalence range; * seven x seven x byte begins with zero * * 000080-0007FF 00000xxx xxxxxxxx 110xxxxx 10xxxxxx first byte begins with 110, * three x, eight x five x, six x the following byte begins with 10. * * 000800-00FFFF xxxxxxxx xxxxxxxx 1110xxxx 10xxxxxx 10xxxxxx first byte begins with 1110, * eight x, eight x four x, six x, six x the following bytes begin with 10. * * 010000-10FFFF 000xxxxx xxxxxxxx xxxxxxxx 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx First byte begins with 11110, * five x, eight x, eight x three x, six x, six x, six x the following bytes begin with 10 * * ==================================================================================================================== * Codepoint Codepoint binary UTF-8 binary UTF-8 hex * hex * -------------------------------------------------------------------------------------------------------------------- * 000000 0 0 00 # 1-byte UTF-8 encoding BEGIN * 00007F 1111111 1111111 7F # 1-byte UTF-8 encoding END * * 000080 00010000000 11000010 10000000 C2 80 # 2-byte UTF-8 encoding BEGIN * 0007FF 11111111111 11011111 10111111 DF BF # 2-byte UTF-8 encoding END * * 000800 0000100000000000 11100000 10100000 10000000 E0 A0 80 # 3-byte UTF-8 encoding BEGIN * 00D7FF 1101011111111111 11101101 10011111 10111111 ED 9F BF # 3-byte UTF-8 encoding PAUSE * * 00D800 1101100000000000 11101101 10100000 10000000 ED A0 80 # surrogate invalid range BEGIN * 00DFFF 1101111111111111 11101101 10111111 10111111 ED BF BF # surrogate invalid range END * * 00E000 1110000000000000 11101110 10000000 10000000 EE 80 80 # 3-byte UTF-8 encoding RESUME * 00FFFF 1111111111111111 11101111 10111111 10111111 EF BF BF # 3-byte UTF-8 encoding END * * 010000 000010000000000000000 11110000 10010000 10000000 10000000 F0 90 80 80 # 4-byte UTF-8 encoding BEGIN * 10FFFF 100001111111111111111 11110100 10001111 10111111 10111111 F4 8F BF BF # 4-byte UTF-8 encoding END * ==================================================================================================================== * *====================================================================================================================== * UTF-16 BIT DISTRIBUTION: *====================================================================================================================== * Code range Scalar value UTF-16 Notes * hexadecimal binary binary * -------------------------------------------------------------------------------------------------------------------- * 000000-00FFFF xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx All code points in the Basic * sixteen x sixteen x Multi-lingual Plane (BMP) * * 010000-10FFFF 000uuuuuxxxxxxxxxxxxxxxx 110110wwwwxxxxxx 110111xxxxxxxxxx All code points in the * five u, sixteen x four w, six x, ten x Suplementary Plane (non-BMP) * * where wwww = uuuuu - 1 *======================================================================================================================= */ #define UTF8_NAME "UTF-8" #define CHSET_M_STR "M" #define CHSET_UTF8_STR UTF8_NAME #define UTF8_1BYTE_MAX (unsigned)(wint_t)ASCII_MAX #define UTF8_2BYTE_MAX (unsigned)(wint_t)0x7FF #define UTF8_3BYTE_MAX (unsigned)(wint_t)0xFFFF #define UTF8_4BYTE_MAX (unsigned)(wint_t)0x10FFFF #define U_BMP_MAX (unsigned)(wint_t)0xFFFF #define U_SURROGATE_BEGIN (unsigned)(wint_t)0xD800 #define U_HIGH_SURROGATE_BEGIN (unsigned)(wint_t)0xD800 #define U_LOW_SURROGATE_BEGIN (unsigned)(wint_t)0xDC00 #define U_SURROGATE_END (unsigned)(wint_t)0xDFFF #define UTF8_LEAD_2BYTEMASK 0x1F /* extract out the x-s in 110xxxxx */ #define UTF8_LEAD_3BYTEMASK 0x0F /* extract out the x-s in 1110xxxx */ #define UTF8_LEAD_4BYTEMASK 0x07 /* extract out the x-s in 11110xxx */ #define UTF8_NONLEAD_BYTEMASK 0x3F /* extract out the x-s in 10xxxxxx */ #define UTF8_LEAD_2BYTE_PREFIX 0xC0 /* 110xxxxx where x is replaced with 0 */ #define UTF8_LEAD_3BYTE_PREFIX 0xE0 /* 1110xxxx where x is replaced with 0 */ #define UTF8_LEAD_4BYTE_PREFIX 0xF0 /* 11110xxx where x is replaced with 0 */ #define UTF8_NONLEAD_BYTE_PREFIX 0x80 /* 10xxxxxx where x is replaced with 0 */ #define UTF8_LEAD_2BYTE_BITLEN 5 /* number of bits extracted from the leading byte of a 2-byte UTF-8 encoding */ #define UTF8_LEAD_3BYTE_BITLEN 4 /* number of bits extracted from the leading byte of a 3-byte UTF-8 encoding */ #define UTF8_LEAD_4BYTE_BITLEN 3 /* number of bits extracted from the leading byte of a 4-byte UTF-8 encoding */ #define UTF8_NONLEAD_BITLEN 6 /* number of bits extracted from the nonleading byte of a UTF-8 encoding */ #define UTF_LINE_SEPARATOR 0x2028 #define UTF_PARA_SEPARATOR 0x2029 #define UTF8_SURROGATE_BYTELEN 3 #define GTM_MB_LEN_MAX 4 /* maximum bytes we support for multi-byte char */ #define GTM_MB_DISP_LEN_MAX 2 /* maximum number of display columns for a multi-byte char. * all characters we know fit in a display width of 2 columns */ /* This macro checks if a byte is a possible valid non-leading byte in a UTF-8 byte stream */ #define UTF8_IS_VALID_TRAILING(x) (((unsigned char)(x) & 0xc0) == 0x80) /* This macro checks if a byte is a possible valid leading byte in a UTF-8 byte stream */ #define UTF8_IS_VALID_LEADING(x) (-1 != (int)utf8_followlen[(unsigned char)(x)]) /* boolean_t U_IS_SURROGATE_CODE(wint_t c) * Returns TRUE if the code point (c) of a character falls in the surrogate range. * Returns 0 otherwise. */ #define U_IS_SURROGATE_CODE(codepoint) \ (((unsigned)(codepoint) >= U_SURROGATE_BEGIN) \ && ((unsigned)(codepoint) <= U_SURROGATE_END)) /* boolean_t U_IS_SURROGATE_HIGH(wint_t c) * Returns TRUE if the code point (c) of a character is a leading (high) surrogate. * Returns 0 otherwise. */ #define U_IS_SURROGATE_HIGH(codepoint) \ (((unsigned)(codepoint) >= U_SURROGATE_BEGIN) \ && ((unsigned)(codepoint) < U_LOW_SURROGATE_BEGIN)) /* boolean_t U_IS_SURROGATE_LOW(wint_t c) * Returns TRUE if the code point (c) of a character is a trailing (low) surrogate. * Returns 0 otherwise. */ #define U_IS_SURROGATE_LOW(codepoint) \ (((unsigned)(codepoint) >= U_LOW_SURROGATE_BEGIN) \ && ((unsigned)(codepoint) <= U_SURROGATE_END)) /* If mbptr points to a valid 2-byte UTF-8 encoding this macro returns TRUE. * If not a valid 2-byte UTF-8 encoding, this macro returns FALSE, resets bytelen to 1. * Assumes "mbptr" is of type "uchar_ptr_t". */ #define UTF8_VALID_2BYTE(mbptr, bytelen) \ ((UTF8_IS_VALID_TRAILING(mbptr[1])) \ ? TRUE \ : (bytelen = 1, FALSE)) /* boolean_t UTF8_NONCHAR_CODE_3BYTE(wint_t c) * boolean_t UTF8_NONCHAR_CODE_4BYTE(wint_t c) * Each of these macros returns TRUE if the code point (c) of a 3-byte or 4-byte wide-character is noncharacter, or * FALSE otherwise. Noncharacters are the code points that do not have valid character * assignment. This set includes: * U+FDD0 - U+FDEF (32 code points, all of which are 3-byte encoded) * All U+nFFFE and U+nFFFF, for each n from 0x0 to 0x10 (total of 34 code points, * of which U+FFFE and U+FFFF are 3-byte encoded and rest are 4-byte encoded) */ #define UTF8_NONCHAR_CODE_3BYTE(codepoint) \ ((unsigned)(codepoint) >= 0xFDD0 \ && ((unsigned)(codepoint) <= 0xFDEF || ((unsigned)(codepoint) & 0xFFFE) == 0xFFFE)) #define UTF8_NONCHAR_CODE_4BYTE(codepoint) \ (((unsigned)(codepoint) & 0xFFFE) == 0xFFFE) /* boolean_t UTF8_NONCHAR_CODEPOINT(wint_t c) * Returns TRUE if the codepoint (c) of ANY multi-byte character is a noncharacter or FALSE otherwise. * It assumes that UTF8_NONCHAR_CODE_3BYTE macro returns the correct values for any codepoint < UTF8_3BYTE_MAX * (including 1-byte and 2-byte codepoints). */ #define UTF8_NONCHAR_CODEPOINT(codepoint) \ (((unsigned)(codepoint) <= UTF8_3BYTE_MAX) && (UTF8_NONCHAR_CODE_3BYTE(codepoint)) \ || UTF8_NONCHAR_CODE_4BYTE(codepoint)) /* boolean_t UTF8_NONCHAR_3BYTE(char* mbptr) * boolean_t UTF8_NONCHAR_4BYTE(char* mbptr) * Each of these macros returns TRUE if mbptr points to a noncharacter as described above * (or FALSE otherwise), except that the checks are performed on the UTF-8 byte stream * instead of the code points. Below are the equivalent byte patterns: * U+FDD0 - U+FDEF: * 0xEF 0xB7 0x90 - 0xEF 0xB7 0xAF (32 code points) * All U+nFFFE and U+nFFFF with the last two bytes having the byte patterns: * 0xEF 0xBF 0xBE and 0xEF 0xBF 0xBF (U+FFFE, U+FFFF) * 0xF0 0x9F 0xBF 0xBE and 0xF0 0x9F 0xBF 0xBF (U+1FFFE, U+1FFFF) * 0xF0 0xAF 0xBF 0xBE and 0xF0 0xAF 0xBF 0xBF (U+2FFFE, U+2FFFF) * .... * .... * 0xF4 0x8F 0xBF 0xBE and 0xF4 0x8F 0xBF 0xBF (U+10FFFE, U+10FFFF) */ #define UTF8_NONCHAR_3BYTE(mbptr) \ (mbptr[0] == 0xEF && ((mbptr[1] == 0xB7 && ((unsigned)(mbptr[2] - 0x90) < 32)) \ || ((mbptr[1] == 0xBF) && (mbptr[2] & 0xBE) == 0xBE))) #define UTF8_NONCHAR_4BYTE(mbptr) \ (((mbptr[1] & 0x0F) == 0x0F) && (mbptr[2] == 0xBF) && (mbptr[3] & 0xBE) == 0xBE) /* If mbptr points to a valid 3-byte UTF-8 encoding this macro returns TRUE. * If not a valid 3-byte UTF-8 encoding, this macro returns FALSE, resets bytelen to 1. * Assumes "mbptr" is of type "uchar_ptr_t". */ #define UTF8_VALID_3BYTE(mbptr, bytelen) \ ((UTF8_IS_VALID_TRAILING(mbptr[1]) && UTF8_IS_VALID_TRAILING(mbptr[2]) \ && ((mbptr[0] != 0xE0) || mbptr[1] >= 0xA0) /* ensure bytestream is above 3-byte UTF-8 BEGIN */ \ && ((mbptr[0] != 0xED) || mbptr[1] <= 0x9F) /* ensure bytestream is NOT a 3-byte UTF-8 surrogate */ \ && !UTF8_NONCHAR_3BYTE(mbptr)) /* ensure bytestream is NOT a 3-byte noncharacter */ \ ? TRUE \ : (bytelen = 1, FALSE)) /* If mbptr points to a valid 4-byte UTF-8 encoding this macro returns TRUE. * If not a valid 4-byte UTF-8 encoding, this macro returns FALSE, resets bytelen to 1. * Assumes "mbptr" is of type "uchar_ptr_t". */ #define UTF8_VALID_4BYTE(mbptr, bytelen) \ ((UTF8_IS_VALID_TRAILING(mbptr[1]) && UTF8_IS_VALID_TRAILING(mbptr[2]) && UTF8_IS_VALID_TRAILING(mbptr[3]) \ && ((mbptr[0] != 0xF0) || mbptr[1] >= 0x90) /* ensure bytestream is above 4-byte UTF-8 BEGIN */ \ && ((mbptr[0] != 0xF4) || mbptr[1] <= 0x8F) /* ensure bytestream is below 4-byte UTF-8 END */ \ && !UTF8_NONCHAR_4BYTE(mbptr)) /* ensure bytestream is NOT a 4-byte noncharacter */ \ ? TRUE \ : (bytelen = 1, FALSE)) /* If mbptr points to a valid 2-byte UTF-8 encoding this macro returns (mbptr + 2) and sets codepoint appropriately. * If not a valid 2-byte UTF-8 encoding, this macro returns (mbptr + 1), and sets codepoint to WEOF. * Assumes "mbptr" is of type "uchar_ptr_t" and that "codepoint" is already set to "mbptr[0]". */ #define UTF8_MBTOWC_2BYTE(mbptr, codepoint) \ ((UTF8_IS_VALID_TRAILING(mbptr[1])) \ ? ((codepoint = ((codepoint & UTF8_LEAD_2BYTEMASK) << UTF8_NONLEAD_BITLEN) \ | (mbptr[1] & UTF8_NONLEAD_BYTEMASK)) \ , (mbptr + 2)) \ : (codepoint = (wint_t)WEOF, (mbptr + 1))) /* If mbptr points to a valid 3-byte UTF-8 encoding this macro returns (mbptr + 3) and sets codepoint appropriately. * If not a valid 3-byte UTF-8 encoding, this macro returns (mbptr + 1), and sets codepoint to WEOF. * Assumes "mbptr" is of type "uchar_ptr_t" and that "codepoint" is already set to "mbptr[0]". */ #define UTF8_MBTOWC_3BYTE(mbptr, codepoint) \ ((UTF8_IS_VALID_TRAILING(mbptr[1]) && UTF8_IS_VALID_TRAILING(mbptr[2]) \ && ((mbptr[0] != 0xE0) || mbptr[1] >= 0xA0) /* ensure bytestream is above 3-byte UTF-8 BEGIN */ \ && ((mbptr[0] != 0xED) || mbptr[1] <= 0x9F) /* ensure bytestream is NOT a 3-byte UTF-8 surrogate */ \ && !UTF8_NONCHAR_3BYTE(mbptr)) /* ensure bytestream is NOT a 3-byte noncharacter */ \ ? ((codepoint = ((((codepoint & UTF8_LEAD_3BYTEMASK) \ << UTF8_NONLEAD_BITLEN) | (mbptr[1] & UTF8_NONLEAD_BYTEMASK)) \ << UTF8_NONLEAD_BITLEN) | (mbptr[2] & UTF8_NONLEAD_BYTEMASK)) \ , (mbptr + 3)) \ : (codepoint = (wint_t)WEOF, (mbptr + 1))) /* If mbptr points to a valid 4-byte UTF-8 encoding this macro returns (mbptr + 4) and sets codepoint appropriately. * If not a valid 4-byte UTF-8 encoding, this macro returns (mbptr + 1), and sets codepoint to WEOF. * Assumes "mbptr" is of type "uchar_ptr_t" and that "codepoint" is already set to "mbptr[0]". */ #define UTF8_MBTOWC_4BYTE(mbptr, codepoint) \ ((UTF8_IS_VALID_TRAILING(mbptr[1]) && UTF8_IS_VALID_TRAILING(mbptr[2]) && UTF8_IS_VALID_TRAILING(mbptr[3]) \ && ((mbptr[0] != 0xF0) || mbptr[1] >= 0x90) /* ensure bytestream is above 4-byte UTF-8 BEGIN */ \ && ((mbptr[0] != 0xF4) || mbptr[1] <= 0x8F) /* ensure bytestream is below 4-byte UTF-8 END */ \ && !UTF8_NONCHAR_4BYTE(mbptr)) /* ensure bytestream is NOT a 4-byte noncharacter */ \ ? ((codepoint = ((((((codepoint & UTF8_LEAD_4BYTEMASK) \ << UTF8_NONLEAD_BITLEN) | (mbptr[1] & UTF8_NONLEAD_BYTEMASK)) \ << UTF8_NONLEAD_BITLEN) | (mbptr[2] & UTF8_NONLEAD_BYTEMASK)) \ << UTF8_NONLEAD_BITLEN) | (mbptr[3] & UTF8_NONLEAD_BYTEMASK)) \ , (mbptr + 4)) \ : (codepoint = (wint_t)WEOF, (mbptr + 1))) /* If mbptr points to a valid 2-byte UTF-8 encoding this macro returns (mbptr + 2). * If not a valid 2-byte UTF-8 encoding, this macro returns (mbptr + 1). * Assumes "mbptr" is of type "uchar_ptr_t". */ #define UTF8_MBNEXT_2BYTE(mbptr) \ (UTF8_IS_VALID_TRAILING(mbptr[1]) \ ? (mbptr + 2) \ : (mbptr + 1)) /* If mbptr points to a valid 3-byte UTF-8 encoding this macro returns (mbptr + 3). * If not a valid 3-byte UTF-8 encoding, this macro returns (mbptr + 1). * Assumes "mbptr" is of type "uchar_ptr_t". */ #define UTF8_MBNEXT_3BYTE(mbptr) \ ((UTF8_IS_VALID_TRAILING(mbptr[1]) && UTF8_IS_VALID_TRAILING(mbptr[2]) \ && ((mbptr[0] != 0xE0) || mbptr[1] >= 0xA0) /* ensure bytestream is above 3-byte UTF-8 BEGIN */ \ && ((mbptr[0] != 0xED) || mbptr[1] <= 0x9F) /* ensure bytestream is NOT a 3-byte UTF-8 surrogate */ \ && !UTF8_NONCHAR_3BYTE(mbptr)) /* ensure bytestream is NOT a 3-byte noncharacter */ \ ? (mbptr + 3) \ : (mbptr + 1)) /* If mbptr points to a valid 4-byte UTF-8 encoding this macro returns (mbptr + 4). * If not a valid 4-byte UTF-8 encoding, this macro returns (mbptr + 1). * Assumes "mbptr" is of type "uchar_ptr_t". */ #define UTF8_MBNEXT_4BYTE(mbptr) \ ((UTF8_IS_VALID_TRAILING(mbptr[1]) && UTF8_IS_VALID_TRAILING(mbptr[2]) && UTF8_IS_VALID_TRAILING(mbptr[3]) \ && ((mbptr[0] != 0xF0) || mbptr[1] >= 0x90) /* ensure bytestream is above 4-byte UTF-8 BEGIN */ \ && ((mbptr[0] != 0xF4) || mbptr[1] <= 0x8F) /* ensure bytestream is below 4-byte UTF-8 END */ \ && !UTF8_NONCHAR_4BYTE(mbptr)) /* ensure bytestream is NOT a 4-byte noncharacter */ \ ? (mbptr + 4) \ : (mbptr + 1)) LITREF unsigned int utf8_bytelen[]; /* boolean_t UTF8_VALID(char *ptr, char *ptrend, unsigned int bytelen) * Inspects bytes of the multi-byte UTF-8 string "ptr" upto "ptrend" and Returns TRUE if the * byte sequence beginning at s forms a welformed and complete UTF-8 character, or FALSE * otherwise. Sets "bytelen" to the byte length of the UTF-8 character if returning TRUE and * to 1 if returning FALSE. A well-formed UTF-8 codepoint that is either a surrogate (in the * range D800 - DFFF) or a noncharacter is considered invalid. This macro assumes that * "ptrend" is at least "ptr+1" and does not do any checks on this. */ #define UTF8_VALID(mbptr, ptrend, bytelen) \ ((bytelen) = utf8_bytelen[((uchar_ptr_t)(mbptr))[0]], \ (((((uchar_ptr_t)(mbptr))[0]) <= ASCII_MAX) ? TRUE /* ASCII. Do simplest check first. */ \ : (((bytelen) == 1) ? FALSE /* Invalid leading byte */ \ : (((int4)(bytelen) > (int4)(((uchar_ptr_t)(ptrend)) - ((uchar_ptr_t)(mbptr)))) \ ? (bytelen = 1, FALSE) /* Not enough length in input string */ \ : ((bytelen) == 2 ? UTF8_VALID_2BYTE(((uchar_ptr_t)(mbptr)), (bytelen)) \ : ((bytelen) == 3 ? UTF8_VALID_3BYTE(((uchar_ptr_t)(mbptr)), (bytelen)) \ : /* bytelen == 4 */UTF8_VALID_4BYTE(((uchar_ptr_t)(mbptr)), (bytelen)) \ )))))) /* boolean_t U_VALID_CODE(wint_t codepoint) * Returns * TRUE if the code point of a character is a valid utf code point * FALSE otherwise. * Invalid code points include: * All surrogate code points * All noncharacter code points * All code points greater than U+10FFFF */ #define U_VALID_CODE(codepoint) \ (((unsigned)(codepoint) <= UTF8_4BYTE_MAX) \ && !U_IS_SURROGATE_CODE(codepoint) \ && !UTF8_NONCHAR_CODEPOINT(codepoint)) LITREF signed int utf8_followlen[]; /* int UTF8_MBFOLLOW(char *s) * Inspects only the first byte of a multi-byte (or even an incomplete) UTF-8 string * pointed at s, and returns the numbers of bytes to follow in order to form a complete * character. The possible return values by this macro are 0, 1, 2 or 3. * If the byte stored at s does not form a legal first-byte of UTF-8 character, * it returns -1. */ #define UTF8_MBFOLLOW(mbptr) (utf8_followlen[((uchar_ptr_t)(mbptr))[0]]) /* int UTF16BE_MBFOLLOW(char *mbptr, char *ptrend) * Inspects up to two bytes of a multi-byte (or even an incomplete) UTF-16BE string * pointed at mbptr, and returns the numbers of bytes to follow the byte at mbptr in order * to form a complete UTF-16 character in BIG-ENDIAN format. The valid return values by * this macro are 1 and 3. If the number of bytes between [mbptr, ptrend) is less than 2, * the macro returns -1. */ #define UTF16BE_MBFOLLOW(mbptr, ptrend) \ ((ptrend - mbptr >= 2) ? (UTF16BE_HIGH_SURROGATE(mbptr) ? 3 : 1) : -1) /* int UTF16LE_MBFOLLOW(char *mbptr, char *ptrend) * Inspects up to two bytes of a multi-byte (or even an incomplete) UTF-16LE string * pointed at mbptr, and returns the numbers of bytes to follow the byte at mbptr in order * to form a complete UTF-16 character in LITTLE-ENDIAN format. The valid return values by * this macro are 1 and 3. If the number of bytes between [mbptr, ptrend) is less than 2, * the macro returns -1. */ #define UTF16LE_MBFOLLOW(mbptr, ptrend) \ ((ptrend - mbptr >= 2) ? (UTF16LE_HIGH_SURROGATE(mbptr) ? 3 : 1) : -1) /* boolean_t UTF16BE_VALID(char *ptr, char *ptrend, unsigned int bytelen) * Inspects 2 or 4 bytes of the UTF-16BE string "ptr" upto "ptrend" and Returns TRUE * if the byte sequence beginning at ptr forms a welformed and complete UTF-16 character * in big-endian format, or FALSE otherwise. This macro also sets "bytelen" to 2 (for * BMP characters) or 4 (for surrogate pair). * NOTES: * "bytelen" is always set irrespective of the validity of the code point (eg. * it can be set to 4 for surrogate pair for which the macro returns FALSE * because its code point is not valid (non-character). * * "ptrend" is asummed to be at least "ptr+2" */ #define UTF16BE_VALID(mbptr, ptrend, bytelen) \ (UTF16BE_HIGH_SURROGATE(mbptr) /* compute the code point first */ \ ? (((ptrend - mbptr) >= 4 && UTF16BE_LOW_SURROGATE(mbptr + 2)) \ ? ((UTF16BE_LOAD_SURROGATE(mbptr, bytelen), U_VALID_CODE(bytelen)) \ ? (bytelen = 4, TRUE) : (bytelen = 4, FALSE)) \ : (bytelen = 2, FALSE)) \ : (((bytelen = UTF16BE_GET_UNIT(mbptr)), U_VALID_CODE(bytelen)) \ ? (bytelen = 2, TRUE) : (bytelen = 2, FALSE))) /* boolean_t UTF16LE_VALID(char *ptr, char *ptrend, unsigned int bytelen) * Inspects 2 or 4 bytes of the UTF-16BE string "ptr" upto "ptrend" and Returns TRUE * if the byte sequence beginning at ptr forms a welformed and complete UTF-16 character * in little-endian format, or FALSE otherwise. This macro also sets "bytelen" to 2 (for * BMP characters) or 4 (for surrogate pair). * NOTES: * "bytelen" is always set irrespective of the validity of the code point (eg. * it can be set to 4 for surrogate pair for which the macro returns FALSE * because its code point is not valid (non-character). * * "ptrend" is asummed to be at least "ptr+2" */ #define UTF16LE_VALID(mbptr, ptrend, bytelen) \ (UTF16LE_HIGH_SURROGATE(mbptr) /* compute the code point first */ \ ? (((ptrend - mbptr) >= 4 && UTF16LE_LOW_SURROGATE(mbptr + 2)) \ ? ((UTF16LE_LOAD_SURROGATE(mbptr, bytelen), U_VALID_CODE(bytelen)) \ ? (bytelen = 4, TRUE) : (bytelen = 4, FALSE)) \ : (bytelen = 2, FALSE)) \ : (((bytelen = UTF16LE_GET_UNIT(mbptr)), U_VALID_CODE(bytelen)) \ ? (bytelen = 2, TRUE) : (bytelen = 2, FALSE))) /* unsigned char *UTF8_MBTOWC(char *mbptr, char *ptrend, wint_t codepoint) * Inspects bytes of the UTF-8 string upto ptrend and sets "codepoint" to the code point of * the next character in the string. If the bytes starting from mbptr do not form a complete * wellformed UTF-8 character, it sets "codepoint" to WEOF. Returns (mbptr+len) where "len" * is the byte length of the UTF-8 character found. If "codepoint" is set to WEOF, the return * value is (mbptr+1). */ #define UTF8_MBTOWC(mbptr, ptrend, codepoint) \ ((codepoint) = (wint_t)(((uchar_ptr_t)(mbptr))[0]), \ (((codepoint) <= ASCII_MAX) ? ((uchar_ptr_t)mbptr + 1) /* ASCII. Do simplest check first. */ \ : ((utf8_bytelen[(codepoint)] == 1) /* Invalid leading byte */ \ ? ((codepoint) = (wint_t)WEOF, ((uchar_ptr_t)mbptr + 1)) \ : (((int4)utf8_bytelen[(codepoint)] /* Not enough length in input string */ \ > (int4)(((uchar_ptr_t)(ptrend)) - ((uchar_ptr_t)(mbptr)))) \ ? ((codepoint) = (wint_t)WEOF, ((uchar_ptr_t)mbptr + 1)) \ : (utf8_bytelen[(codepoint)] == 2 ? UTF8_MBTOWC_2BYTE(((uchar_ptr_t)(mbptr)), (codepoint)) \ : (utf8_bytelen[(codepoint)] == 3 ? UTF8_MBTOWC_3BYTE(((uchar_ptr_t)(mbptr)), (codepoint)) \ : /* utf8_bytelen[codepoint] == 4 */UTF8_MBTOWC_4BYTE(((uchar_ptr_t)(mbptr)), (codepoint)) \ )))))) /* unsigned char* UTF8_MBNEXT(char *ptr, char *ptrend) * Assuming that the string pointed at ptr is wellformed, it inspects bytes upto ptrend * and advances the pointer by the number of bytes used by the character pointed at ptr. * It returns the pointer to the beginning of the following character. If the bytes * starting from s do not form a welformed character within the limits defined * by ptrend, it returns the pointer to the next byte (i.e. s+1). */ #define UTF8_MBNEXT(mbptr, ptrend) \ (((((uchar_ptr_t)(mbptr))[0]) <= ASCII_MAX) ? ((uchar_ptr_t)mbptr + 1) /* ASCII. Do simplest check first. */ \ : ((utf8_bytelen[(((uchar_ptr_t)(mbptr))[0])] == 1) /* Invalid leading byte */ \ ? ((uchar_ptr_t)mbptr + 1) \ : (((int4)utf8_bytelen[(((uchar_ptr_t)(mbptr))[0])] > (int4)((uchar_ptr_t)ptrend - (uchar_ptr_t)mbptr)) \ ? ((uchar_ptr_t)mbptr + 1) /* Not enough length in input string */ \ : (utf8_bytelen[(((uchar_ptr_t)(mbptr))[0])] == 2 ? UTF8_MBNEXT_2BYTE(((uchar_ptr_t)(mbptr))) \ : (utf8_bytelen[(((uchar_ptr_t)(mbptr))[0])] == 3 ? UTF8_MBNEXT_3BYTE(((uchar_ptr_t)(mbptr))) \ : /* utf8_bytelen[(((uchar_ptr_t)mbptr)[0])] == 4 */UTF8_MBNEXT_4BYTE(((uchar_ptr_t)(mbptr))) \ ))))) /* unsigned char* UTF8_WCTOMB(wint_t c, char *s) * Converts the code point of a character (c) to a sequence of bytes and stores * the result (of 1 to 4 bytes long) at the beginning of the character array pointed * to by s. It returns the pointer advanced by the number of bytes required for c. * For invalid code points no conversion is done and and the macro returns s. */ #define UTF8_WCTOMB(codepoint, mbptr) \ (((unsigned)(codepoint) <= UTF8_1BYTE_MAX) /* 1-byte UTF-8 encoding */ \ ? (*((uchar_ptr_t)mbptr) = (unsigned char)(codepoint), ((uchar_ptr_t)mbptr) + 1) \ : (((unsigned)(codepoint) <= UTF8_2BYTE_MAX) /* 2-byte UTF-8 encoding */ \ ? (*(((uchar_ptr_t)mbptr) + 1) \ = (unsigned char)(((codepoint) & UTF8_NONLEAD_BYTEMASK) | UTF8_NONLEAD_BYTE_PREFIX), \ *((uchar_ptr_t)mbptr) = (unsigned char)(((codepoint) >> UTF8_NONLEAD_BITLEN) | UTF8_LEAD_2BYTE_PREFIX), \ ((uchar_ptr_t)mbptr) + 2) \ : (((unsigned)(codepoint) <= UTF8_3BYTE_MAX) /* 3-byte UTF-8 encoding */ \ ? ((U_IS_SURROGATE_CODE(codepoint) || UTF8_NONCHAR_CODE_3BYTE(codepoint)) \ ? ((uchar_ptr_t)mbptr) /* Surrogate or noncharacter (3-byte case) */ \ : (*(((uchar_ptr_t)mbptr) + 2) /* Non-surrogate 3-byte case */ \ = (unsigned char)(((codepoint) & UTF8_NONLEAD_BYTEMASK) | UTF8_NONLEAD_BYTE_PREFIX), \ *(((uchar_ptr_t)mbptr) + 1) \ = (unsigned char)((((codepoint) >> UTF8_NONLEAD_BITLEN) & UTF8_NONLEAD_BYTEMASK) \ | UTF8_NONLEAD_BYTE_PREFIX), \ *((uchar_ptr_t)mbptr) = (unsigned char)(((codepoint) >> (2 * UTF8_NONLEAD_BITLEN)) \ | UTF8_LEAD_3BYTE_PREFIX), \ ((uchar_ptr_t)mbptr) + 3)) \ : ((((unsigned)(codepoint) <= UTF8_4BYTE_MAX) && !UTF8_NONCHAR_CODE_4BYTE(codepoint)) /* 4-byte UTF-8 encoding */ \ ? (*(((uchar_ptr_t)mbptr) + 3) \ = (unsigned char)(((codepoint) & UTF8_NONLEAD_BYTEMASK) | UTF8_NONLEAD_BYTE_PREFIX), \ *(((uchar_ptr_t)mbptr) + 2) \ = (unsigned char)((((codepoint) >> UTF8_NONLEAD_BITLEN) & UTF8_NONLEAD_BYTEMASK) \ | UTF8_NONLEAD_BYTE_PREFIX), \ *(((uchar_ptr_t)mbptr) + 1) \ = (unsigned char)((((codepoint) >> (2 * UTF8_NONLEAD_BITLEN)) & UTF8_NONLEAD_BYTEMASK) \ | UTF8_NONLEAD_BYTE_PREFIX), \ *((uchar_ptr_t)mbptr) = (unsigned char)(((codepoint) >> (3 * UTF8_NONLEAD_BITLEN)) \ | UTF8_LEAD_4BYTE_PREFIX), \ ((uchar_ptr_t)mbptr) + 4) \ : ((uchar_ptr_t)mbptr))))) /* boolean_t UTF8_SURROGATE(char* s, char *ptrend) * Inspects bytes of the multi-byte UTF-8 string upto ptrend and Returns TRUE if the * byte sequence beginning at s forms a welformed UTF-8 character and an * isolated surrogate character (either lower surrogate or upper surrogate). * It returns FALSE, otherwise. */ #define UTF8_SURROGATE(mbptr, ptrend) \ (((UTF8_SURROGATE_BYTELEN /* maxlen should be at least 3-bytes */ \ <= ((int4)((uchar_ptr_t)ptrend - (uchar_ptr_t)mbptr))) \ && (((uchar_ptr_t)mbptr)[0] == 0xED) /* leading byte should be 0xED for surrogate UTF-8 */ \ && (((uchar_ptr_t)mbptr)[1] >= 0xA0) /* first non-leading byte should be at least 0xA0 */ \ && (((uchar_ptr_t)mbptr)[1] <= 0xBF) /* first non-leading byte should be at most 0xBF */ \ && (UTF8_IS_VALID_TRAILING(((uchar_ptr_t)mbptr)[2]))) /* second non-leading byte should be valid */ \ ? TRUE : FALSE) /* void UTF8_LEADING_BYTE(char* mbptr, char* baseptr, char* leadptr) * Sets leadptr to point to the leading byte of the UTF-8 character containing the byte * pointed by mbptr. If the byte pointed by mbptr is not part of a valid UTF-8 character, * this macro sets leadptr to mbptr. * NOTE: mbptr and leadptr must not be the same variable. */ #define UTF8_LEADING_BYTE(mbptr, baseptr, leadptr) \ { \ leadptr = mbptr; \ while (leadptr >= baseptr && UTF8_IS_VALID_TRAILING(*(uchar_ptr_t)leadptr)) \ --leadptr; \ if (leadptr < baseptr || !UTF8_IS_VALID_LEADING(*(uchar_ptr_t)leadptr) || \ (mbptr - leadptr) > utf8_followlen[*(uchar_ptr_t)leadptr]) \ leadptr = mbptr; \ } /* Macros to return the UTF-16 (16-bit) code units from a given code point in the supplementary plane. * Note: these macros must be called only for the supplementary code points (> U_BMP_MAX) that are <= UTF8_4BYTE_MAX */ #define UTF16_HIGH_SURROGATE(codepoint) \ (U_HIGH_SURROGATE_BEGIN | ((((codepoint) >> 16) - 1) << 6) | (((codepoint) >> 10) & 0x3F)) #define UTF16_LOW_SURROGATE(codepoint) \ (U_LOW_SURROGATE_BEGIN | ((codepoint) & 0x3FF)) /* Composes a surrogate pair and returns the code point in the supplementary plane */ #define UTF16_COMPOSE_SURROGATES(high, low) \ ((((((high) >> 6) & 0xF) + 1) << 16) | (((high) & 0x3F) << 10) | ((low) & 0x3FF)) /* Macros to convert a UTF-16 (16-bit) code unit into a 2-byte sequence in the appropriate endianness. * The codeunits passed must be less than or equal to U_BMP_MAX */ #define UTF16BE_STORE_UNIT(mbptr, codeunit) \ ((((uchar_ptr_t)mbptr)[0] = ((codeunit) >> 8)), (((uchar_ptr_t)mbptr)[1] = ((codeunit) & 0x00FF))) #define UTF16LE_STORE_UNIT(mbptr, codeunit) \ ((((uchar_ptr_t)mbptr)[1] = ((codeunit) >> 8)), (((uchar_ptr_t)mbptr)[0] = ((codeunit) & 0x00FF))) /* macros to return a single UTF-16 (16-bit) codeunit given a 2-byte sequence */ #define UTF16BE_GET_UNIT(mbptr) \ ((((uchar_ptr_t)mbptr)[0] << 8) | ((uchar_ptr_t)mbptr)[1]) #define UTF16LE_GET_UNIT(mbptr) \ ((((uchar_ptr_t)mbptr)[1] << 8) | ((uchar_ptr_t)mbptr)[0]) /* macros to load UTF-16 surrogate codeunit pairs and return the code point in the supplementary plane. * Note: mbptr must point to a valid 4-byte sequence of high and low surrogates */ #define UTF16BE_LOAD_SURROGATE(mbptr, codepoint) \ (codepoint = UTF16BE_GET_UNIT(mbptr), \ codepoint = UTF16_COMPOSE_SURROGATES(codepoint, UTF16BE_GET_UNIT(mbptr+2))) #define UTF16LE_LOAD_SURROGATE(mbptr, codepoint) \ (codepoint = UTF16LE_GET_UNIT(mbptr), \ codepoint = UTF16_COMPOSE_SURROGATES(codepoint, UTF16LE_GET_UNIT(mbptr+2))) /* char* UTF16BE_WCTOMB(wint_t codepoint, char *mbptr) * Converts the code point of a character (codepoint) in to big-endian UTF-16 bytes * and stores the result (of 2 or 4 bytes long) at the beginning of the character * array pointed to by mbptr. It returns the pointer advanced by the number of bytes * required for codepoint. For invalid code points, no conversion is done and and * the macro returns mbptr. */ #define UTF16BE_WCTOMB(codepoint, mbptr) \ (U_VALID_CODE(codepoint) \ ? ((codepoint) <= U_BMP_MAX \ ? (UTF16BE_STORE_UNIT(mbptr, (codepoint)), mbptr + 2) /* code points in BMP */ \ : (UTF16BE_STORE_UNIT(mbptr, UTF16_HIGH_SURROGATE(codepoint)), /* supplementary plane */\ UTF16BE_STORE_UNIT(mbptr + 2, UTF16_LOW_SURROGATE(codepoint)), mbptr + 4)) \ : mbptr) /* char* UTF16LE_WCTOMB(wint_t codepoint, char *mbptr) * Converts the code point of a character (codepoint) in to little-endian UTF-16 bytes * and stores the result (of 2 or 4 bytes long) at the beginning of the character * array pointed to by mbptr. It returns the pointer advanced by the number of bytes * required for codepoint. For invalid code points, no conversion is done and and * the macro returns mbptr. */ #define UTF16LE_WCTOMB(codepoint, mbptr) \ (U_VALID_CODE(codepoint) \ ? ((codepoint) <= U_BMP_MAX /* 16-bit characters */ \ ? (UTF16LE_STORE_UNIT(mbptr, (codepoint)), mbptr + 2) /* code points in BMP */ \ : (UTF16LE_STORE_UNIT(mbptr, UTF16_HIGH_SURROGATE(codepoint)), /* supplementary plane */\ UTF16LE_STORE_UNIT(mbptr + 2, UTF16_LOW_SURROGATE(codepoint)), mbptr + 4)) \ : mbptr) /* char *UTF16BE_MBTOWC(char *mbptr, char *ptrend, wint_t codepoint) * Inspects 2 bytes (or 4 bytes if surrogates) of the UTF-16 string in big-endian and * sets "codepoint" to the code point of the next character in the string. Returns * (mbptr + len) where "len" is the byte length of the UTF-16BE character found. If * the bytes starting from mbptr do not form a complete welformed UTF-16BE character, * it sets codepoint to WEOF and return mbptr. */ #define UTF16BE_MBTOWC(mbptr, ptrend, codepoint) \ ((UTF16BE_HIGH_SURROGATE(mbptr) /* compute the code point first */ \ ? (((ptrend - mbptr) >= 4 && UTF16BE_LOW_SURROGATE(mbptr + 2)) \ ? UTF16BE_LOAD_SURROGATE(mbptr, codepoint) : (codepoint = WEOF)) \ : (codepoint = UTF16BE_GET_UNIT(mbptr))), \ (U_VALID_CODE(codepoint) /* validate the code point */ \ ? ((codepoint) <= U_BMP_MAX ? (mbptr + 2) : (mbptr + 4)) \ : (((codepoint) = WEOF), mbptr))) /* char *UTF16LE_MBTOWC(char *mbptr, char *ptrend, wint_t codepoint) * Inspects 2 bytes (or 4 bytes if surrogates) of the UTF-16 string in little-endian and * sets "codepoint" to the code point of the next character in the string. Returns * (mbptr + len) where "len" is the byte length of the UTF-16BE character found. If * the bytes starting from mbptr do not form a complete welformed UTF-16LE character, * it sets codepoint to WEOF and return mbptr. */ #define UTF16LE_MBTOWC(mbptr, ptrend, codepoint) \ ((UTF16LE_HIGH_SURROGATE(mbptr) /* compute the code point first */ \ ? (((ptrend - mbptr) >= 4 && UTF16LE_LOW_SURROGATE(mbptr + 2)) \ ? UTF16LE_LOAD_SURROGATE(mbptr, codepoint) : (codepoint = WEOF)) \ : (codepoint = UTF16LE_GET_UNIT(mbptr))), \ (U_VALID_CODE(codepoint) /* validate the code point */ \ ? ((codepoint) <= U_BMP_MAX ? (mbptr + 2) : (mbptr + 4)) \ : (((codepoint) = WEOF), mbptr))) /* boolean_t UTF16BE_HIGH_SURROGATE(char* mbptr) * Inspects at most 2 bytes in the UTF-16 string and Returns TRUE if the byte sequence * beginning at mbptr forms a welformed UTF-16BE high surrogate character (U+D800 - U+DBFF) * and FALSE otherwise. */ #define UTF16BE_HIGH_SURROGATE(mbptr) \ (U_IS_SURROGATE_HIGH(UTF16BE_GET_UNIT(mbptr))) /* boolean_t UTF16LE_HIGH_SURROGATE(char* mbptr) * Inspects at most 2 bytes in the UTF-16 string and Returns TRUE if the byte sequence * beginning at mbptr forms a welformed UTF-16LE high surrogate character (U+D800 - U+DBFF) * and FALSE otherwise. */ #define UTF16LE_HIGH_SURROGATE(mbptr) \ (U_IS_SURROGATE_HIGH(UTF16LE_GET_UNIT(mbptr))) /* boolean_t UTF16BE_LOW_SURROGATE(char* mbptr) * Inspects at most 2 bytes in the UTF-16 string and Returns TRUE if the byte sequence * beginning at mbptr forms a welformed UTF-16BE low surrogate character (U+DC00 - U+DFFF) * and FALSE otherwise. */ #define UTF16BE_LOW_SURROGATE(mbptr) \ (U_IS_SURROGATE_LOW(UTF16BE_GET_UNIT(mbptr))) /* boolean_t UTF16LE_LOW_SURROGATE(char* mbptr) * Inspects at most 2 bytes in the UTF-16 string and Returns TRUE if the byte sequence * beginning at mbptr forms a welformed UTF-16LE low surrogate character (U+DC00 - U+DFFF) * and FALSE otherwise. */ #define UTF16LE_LOW_SURROGATE(mbptr) \ (U_IS_SURROGATE_LOW(UTF16LE_GET_UNIT(mbptr))) /* The following macros provide the character classification for utf characters given their code points */ #define U_ISLOWER(c) u_islower(c) #define U_ISUPPER(c) u_isupper(c) #define U_ISALPHA(c) u_isalpha(c) #define U_ISCNTRL(c) u_iscntrl(c) #define U_ISDIGIT(c) u_isdigit(c) #define U_ISPUNCT(c) u_ispunct(c) #define U_ISSPACE(c) u_isspace(c) #define U_ISBLANK(c) u_isblank(c) #define U_ISGRAPH(c) u_isgraph(c) #define U_ISPRINT(c) GTM_U_ISPRINT(c) /* see macro definition for why redirection needed */ #define U_ISTITLE(c) u_istitle(c) #define U_CHARTYPE(c) u_charType(c) /* uint4 CTYPEMASK(wint_t c) * * This macro assumes that "c" is a valid utf codepoint. * * Returns a patcode from a code point (wide character wint_t) paralleling the way ICU library functions classify codepoints. * u_isalpha (for A) * u_isdigit (for N) * u_ispunct (for P) * u_iscntrl (for C) * But with the following adjustments. * 1) If $ZPATNUMERIC is not "UTF-8", non-ASCII decimal digits are classified as A. * 2) Non-decimal digits (Nl and No) are classified as A. Note: u_isdigit only matches decimal digits. * 3) Anything left is classified via u_isprint into either P or C. Note: u_isprint only matches non-control characters. * Note that the ISV $ZPATN[UMERIC] dictates how the pattern class N used in the pattern match operator is interpreted. * If $ZPATNUMERIC is "UTF-8", the pattern class N matches any decimal numeric character as defined by the standard. * If $ZPATNUMERIC is "M", GT.M restricts the pattern class N to match only ASCII digits 0-9 (i.e. ASCII 48-57). * The variable "utf8_patnumeric" is TRUE if $ZPATNUMERIC is "UTF-8". * * The above rules result in the following mapping * -------------------------------------------------- * general category GT.M patcode class * -------------------------------------------------- * L* (all letters) -> A * M* (all marks) -> P * Nd (decimal numbers) -> N (if decimal digit is ASCII or $ZPATNUMERIC is "UTF-8", otherwise -> A) * Nl (letter numbers) -> A (examples of Nl are Roman numerals) * No (other numbers) -> A (examples of No are fractions) * P* (all punctuation) -> P * S* (all symbols) -> P * Zs (spaces) -> P * Zl (line separators) -> C * Zp (paragraph separators) -> C * C* (all control codepoints) -> C * * For a description of the general categories see http://unicode.org/versions/Unicode4.0.0/ch04.pdf (section 4.5) * * E = A + P + N + C and the classifications A, P, N, and C are mutually exclusive. * * This means that PATM_UTF8_NONBASIC does not currently have any codepoints mapped to it. * It is being retained in case it is needed in the future. */ /* our mask to map non-decimal digits into the PATM_A */ #define GTM_NA_MASK (U_GC_NL_MASK | U_GC_NO_MASK) #define CTYPEMASK(c) \ (U_ISALPHA(c) ? /* alphabet */ \ (U_ISLOWER(c) ? PATM_L /* lower-case */ \ : (U_ISUPPER(c) ? PATM_U /* upper-case */ \ : PATM_UTF8_ALPHABET)) /* utf alphabet that is neither lower nor upper case */ \ : (U_ISDIGIT(c) /* ascii or non-ascii decimal digit */ \ ? ((utf8_patnumeric || IS_ASCII(c)) /* check $ZPATNUMERIC setting */ \ ? PATM_N /* Ascii digit OR $ZPATNUMERIC set to "UTF-8" */ \ : PATM_UTF8_ALPHABET) /* $ZPATNUMERIC set to "M" and non-ascii decimal digit */ \ : ((U_MASK(U_CHARTYPE(c)) & GTM_NA_MASK)/* put non-decimal digits in */ \ ? PATM_UTF8_ALPHABET /* PATM_UTF8_ALPHABET */ \ : (U_ISPUNCT(c) ? PATM_P /* punctuation */ \ :(U_ISCNTRL(c) ? PATM_C /* control */ \ /* utf character that is not part of any basic class */ \ :(U_ISPRINT(c) /* if printable */ \ ? PATM_P /* punctuation */ \ : PATM_C)))))) /* otherwise, control */ /* uint4 TYPEMASK(char *ptr, char *ptrend, char *ptrnext, wint_t codepoint) * Inspects bytes of a character (in UTF-8 format) starting at "ptr" upto "ptrend", and returns its patcode. * This macro is a replacment to the existing typemask[] table that works in both UTF-8 and non-UTF8 mode. * This macro should only be used by the compiler. This macro assumes that "gtm_utf8_mode" is TRUE. * The parameter "codepoint" is set to the codepoint which is the same thing that gets returned by the macro. */ #define TYPEMASK(ptr, ptrend, ptrnext, codepoint) \ (IS_ASCII(*(ptr)) \ ? ((ptrnext = ptr + 1), (codepoint) = *(ptr), typemask[*(ptr)]) \ : (ptrnext = UTF8_MBTOWC(ptr, ptrend, codepoint), CTYPEMASK(codepoint))) /* uint4 PATTERN_TYPEMASK(char *ptr, char *ptrend, char *ptrnext, wint_t codepoint) * Inspects bytes of a character (in UTF-8 format) starting at "ptr" upto "ptrend", and returns its patcode. * This macro is a replacment to the existing pattern_typemask[] table that works in both UTF-8 and non-UTF8 mode. * This macro should only be used by the runtime. This macro assumes that "gtm_utf8_mode" is TRUE. * The parameter "codepoint" is set to the codepoint if it is a multi-byte UTF8 character. */ #define PATTERN_TYPEMASK(ptr, ptrend, ptrnext, codepoint) \ (IS_ASCII(*(ptr)) \ ? ((ptrnext = ptr + 1), (codepoint) = *(ptr), pattern_typemask[*(ptr)]) \ : (ptrnext = UTF8_MBTOWC(ptr, ptrend, codepoint), CTYPEMASK(codepoint))) /* Returns the display column width of a character given its code point. This macro * returns -1 for control characters and 0 for non-spacing (combining) characters */ #define UTF8_WCWIDTH(c) gtm_wcwidth((wint_t)(c)) /* The following macro is same as UTF8_WCWIDTH except that it returns 0 for unprintable valid characters as well. * It is primarily used by the IO code. */ #ifdef UTF8_SUPPORTED #define GTM_IO_WCWIDTH(CHAR,RET) \ if (utf8_active) \ { \ RET = UTF8_WCWIDTH(CHAR); \ RET = (0 > RET ? 0 : RET); \ } else \ RET = 1 #else #define GTM_IO_WCWIDTH(CHAR,RET) RET = 1 #endif /* Offsets for use with u32_line_term[] */ #define U32_LT_LF 0 #define U32_LT_CR 1 #define U32_LT_NL 2 #define U32_LT_FF 3 #define U32_LT_LS 4 #define U32_LT_PS 5 #define U32_LT_LAST 5 /* not counting null sentinel */ #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" int trim_U16_line_term(UChar *buffer, int len); #endif /* There could be integral promotion/sign extension issues if short, int (or an integral type) is used for comparison. Avoid * such issues by definining BOM as a string */ #define UTF16BE_BOM "\xFE\xFF" /* Big Endian BYTE ORDER MARKER */ #define UTF16BE_BOM_LEN STR_LIT_LEN(UTF16BE_BOM) #define UTF16LE_BOM "\xFF\xFE" /* Little Endian BYTE ORDER MARKER */ #define UTF16LE_BOM_LEN STR_LIT_LEN(UTF16LE_BOM) #define UTF8_BOM "\xEF\xBB\xBF" /* No relevance to endian-ness, a UTF8 MARKER similar to UTF16_BOM */ #define UTF8_BOM_LEN STR_LIT_LEN(UTF8_BOM) #define UTF32BE_BOM "\x00\x00\xFE\xFF" /* Big Endian BYTE ORDER MARKER */ #define UTF32BE_BOM_LEN STR_LIT_LEN(UTF32BE_BOM) #define UTF32LE_BOM "\xFF\xFE\x00\x00" /* Little Endian BYTE ORDER MARKER */ #define UTF32LE_BOM_LEN STR_LIT_LEN(UTF32LE_BOM) #define BOM_CODEPOINT 0xFEFF #define UTF8_BADCHAR(len, str, strtop, chset_len, chset) \ utf8_badchar((len), (unsigned char *)(str), (unsigned char *)(strtop), (chset_len), (unsigned char *)(chset)) #define UTF8_BADCHAR_DEC(len, str, strtop, chset_len, chset) \ utf8_badchar_dec((len), (unsigned char *)(str), (unsigned char *)(strtop), (chset_len), (unsigned char *)(chset)) #define UTF8_BADCHAR_STX(len, str, strtop, chset_len, chset) \ utf8_badchar_stx((len), (unsigned char *)(str), (unsigned char *)(strtop), (chset_len), (unsigned char *)(chset)) #define UTF8_LEN_STRICT(ptr, len) \ utf8_len_strict((unsigned char *)(ptr), (len)) #define UTF8_LEN_SILENT(ptr, len) \ utf8_len_silent((unsigned char *)(ptr), (len)) /* This macro is needed to to ensure all utf line terminators are considered non-printable. As of this * writing, ICU's u_isprint returns TRUE for LS/PS (Line/Paragraph separator; codepoints 0x2028, 0x2029) * and this causes problems in extracting and loading data which contains these codepoints (in UTF8 mode). * Ideally, one should go through all the line terminators in u32_line_term[] array and check them. * But since this routine is performance intensive (called from mupip extract which can take hours for * huge databases) we avoid a loop and just check for LS/PS which we know dont work right with "u_isprint" */ #define GTM_U_ISPRINT(code) \ ((((UChar32)UTF_LINE_SEPARATOR == (UChar32)(code)) || ((UChar32)UTF_PARA_SEPARATOR == (UChar32)(code))) \ ? FALSE \ : u_isprint(code)) static void inline copy_character(uint4 copy_length, sm_uc_ptr_t dstptr, char *rcur) { switch (copy_length) { /* Rather than calling memcpy, do this dance so we can save some instructions */ case 4: *((int4*)dstptr) = *((int4*)rcur); break; case 2: *((short*)dstptr) = *((short*)rcur); break; case 3: *((short*)(dstptr+1)) = *((short*)(rcur+1)); /* FALLTHROUGH */ case 1: *dstptr = *rcur; break; default: assert(FALSE); } } /* The utf8_len[_{stx,dec)] routines do slightly different error reporting. This enum defines which type is desired */ typedef enum { err_rts, /* Use rts_error() */ err_stx, /* Use stx_error() */ err_dec, /* Use dec_error() */ err_ignore /* Get length as best possible without complaint */ } utf8_err_type; GBLREF boolean_t utf8_patnumeric; int utf8_len(mstr* str); int utf8_len_dec(mstr* str); int utf8_len_stx(mstr* str); int utf8_len_silent(mstr* str); int utf8_len_strict(unsigned char* ptr, int len); STATICFNDCL int utf8_len_real(utf8_err_type err_type, mstr* str); int gtm_wcwidth(wint_t code); int gtm_wcswidth(unsigned char* ptr, int len, boolean_t strict, int nonprintwidth); void utf8_badchar(int len, unsigned char* str, unsigned char *strtop, int chset_len, unsigned char* chset); void utf8_badchar_dec(int len, unsigned char* str, unsigned char *strtop, int chset_len, unsigned char* chset); void utf8_badchar_stx(int len, unsigned char* str, unsigned char *strtop, int chset_len, unsigned char* chset); STATICFNDCL void utf8_badchar_real(utf8_err_type err_type, int len, unsigned char* str, unsigned char *strtop, int chset_len, unsigned char* chset); unsigned char *gtm_utf8_trim_invalid_tail(unsigned char *str, int len); boolean_t valid_utf_string(const mstr *str); /* To prevent GTMSECSHR from pulling in the function "gtm_wcswidth" (used in util_output.c) and in turn the entire utf codebase, * we define a function-pointer variable and initialize it at startup to NULL only in GTMSECSHR and thereby avoid pulling * in all the unneeded / unwanted executables. */ typedef int (*gtm_wcswidth_fnptr_t)(unsigned char* ptr, int len, boolean_t strict, int nonprintwidth); GBLREF gtm_wcswidth_fnptr_t gtm_wcswidth_fnptr; /* see comment above about this typedef */ #endif /* GTM_UTF8_H */ fis-gtm-V7.0-005/sr_unix/gtm_version_dirname.csh0000755000032200000250000000175214342376330020603 0ustar librarygtc################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# # ################################################################################################### # # # gtm_version_dirname.csh - convert version name to corresponding development directory name. # # argument - GT.M version name (e.g., "v314") # # The net effect of gtm_version_dirname.csh is to write to stdout the name of the directory # corresponding to the GT.M version name passed as an argument. # # ################################################################################################### echo "$1" | sed -f $gtm_tools/gtm_version_dirname.sed fis-gtm-V7.0-005/sr_unix/gtm_version_dirname.sed0000755000032200000250000000353514342376330020602 0ustar librarygtc################################################################# # # # Copyright 2002, 2003 Sanchez Computer Associates, 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. # # # ################################################################# # ############################################################################################################################## # # # gtm_version_dirname.sed - sed script to convert a version name to the name of the corresponding development directory. # # This script is intended to be used in-line by gtm_version_dirname.csh # which does some pre-formatting by converting the version name to # lower-case. # # ############################################################################################################################## # All characters should be lower-case on entry; convert leading "v" # and remove embedded decimal points (.) and minus signs (-). s/^v/V/ s/\.//g s/-//g # V9.9-BL99* Base-line release: V99bl99* => V99BL99* /^V[0-9][0-9][bB][lL][0-9][0-9]*$/s/[bB][lL]/BL/ /^V[0-9][0-9]BL[0-9][0-9]*$/q # V9.9-FT99* Field-test release: V99ft99* => V99FT99* /^V[0-9][0-9][fF][tT][0-9][0-9]*$/s/[fF][tT]/FT/ /^V[0-9][0-9]FT[0-9][0-9]*$/q # V9.9-FT99*x Field-test incremental release: V99ft99*x => V99FT99*x x == any lower-case alphabetic character /^V[0-9][0-9][fF][tT][0-9][0-9]*[A-Za-z]$/s/[fF][tT]/FT/ /^V[0-9][0-9]FT[0-9][0-9]*[A-Za-z]*$/q # V9.9-99* Relatively major release: V9999* => V9999* 9* == any (possibly empty) sequence of numeric digits /^V[0-9][0-9][0-9][0-9]*$/q # V9.9-99*x Incremental release: V9999*x => V9999*x x == any lower-case alphabetic character /^V[0-9][0-9][0-9][0-9]*[A-Za-z]$/q fis-gtm-V7.0-005/sr_unix/gtm_wake.c0000755000032200000250000000107114342376330016005 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "gtm_wake.h" void gtm_wake(int4 *p1,char *p2) { return; /* stub for the time being */ } fis-gtm-V7.0-005/sr_unix/gtm_zlib.c0000644000032200000250000000726514342376330016026 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2008-2021 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 #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_limits.h" #include "real_len.h" /* for COPY_DLERR_MSG */ #include "lv_val.h" /* needed for "fgncal.h" */ #include "fgncal.h" /* needed for COPY_DLLERR_MSG() */ #include "gtm_zlib.h" #include "gtmmsg.h" #include "send_msg.h" #include "restrict.h" /* Needed for restrictions */ #include "have_crit.h" /* Needed for defer interrupts */ error_def(ERR_DLLNOOPEN); error_def(ERR_DLLNORTN); error_def(ERR_RESTRICTEDOP); error_def(ERR_TEXT); GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF boolean_t gtm_dist_ok_to_use; void gtm_zlib_init(void) { char err_msg[MAX_ERRSTR_LEN]; #ifdef _AIX char aix_err_msg[MAX_ERRSTR_LEN]; #endif void_ptr_t handle; char_ptr_t err_str; char *zlib_fname[] = { ZLIB_CMP_FNAME, ZLIB_UNCMP_FNAME, }; void **zlib_fptr[] = { (void **)&zlib_compress_fnptr, (void **)&zlib_uncompress_fnptr, }; int findx; void *fptr; char librarypath[GTM_PATH_MAX], *lpath = NULL; intrpt_state_t prev_intrpt_state; assert(gtm_zlib_cmp_level); #ifdef _AIX if (RESTRICTED(library_load_path)) { lpath = librarypath; assert(gtm_dist_ok_to_use); SNPRINTF(librarypath, GTM_PATH_MAX, GTM_PLUGIN_FMT_SHORT ZLIB_AIXLIBNAME, gtm_dist); } else lpath = ZLIB_AIXLIBNAME; /* Attempt to load the AIX packaged zlib first */ DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); handle = dlopen( lpath, ZLIB_LIBFLAGS | RTLD_MEMBER); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); if (NULL == handle) { COPY_DLLERR_MSG(err_str, aix_err_msg); #endif if (RESTRICTED(library_load_path)) { lpath = librarypath; SNPRINTF(librarypath, GTM_PATH_MAX, GTM_PLUGIN_FMT_SHORT ZLIB_LIBNAME, gtm_dist); } else lpath = ZLIB_LIBNAME; DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); handle = dlopen(lpath, ZLIB_LIBFLAGS); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); if (NULL == handle) { if (RESTRICTED(library_load_path)) { SNPRINTF(err_msg, MAX_ERRSTR_LEN, "dlopen(%s)", lpath); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_RESTRICTEDOP, 1, err_msg); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_RESTRICTEDOP, 1, err_msg); gtm_zlib_cmp_level = ZLIB_CMPLVL_NONE; /* dont use compression */ return; } COPY_DLLERR_MSG(err_str, err_msg); # ifdef _AIX gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_DLLNOOPEN, 2, LEN_AND_STR(lpath), ERR_TEXT, 2, LEN_AND_STR(err_msg), ERR_TEXT, 2, LEN_AND_STR(aix_err_msg)); # else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_DLLNOOPEN, 2, LEN_AND_STR(lpath), ERR_TEXT, 2, LEN_AND_STR(err_msg)); # endif gtm_zlib_cmp_level = ZLIB_CMPLVL_NONE; /* dont use compression */ return; } #ifdef _AIX } #endif for (findx = 0; findx < ZLIB_NUM_DLSYMS; ++findx) { fptr = (void *)dlsym(handle, zlib_fname[findx]); if (NULL == fptr) { COPY_DLLERR_MSG(err_str, err_msg); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_DLLNORTN, 2, LEN_AND_STR(zlib_fname[findx]), ERR_TEXT, 2, LEN_AND_STR(err_msg)); gtm_zlib_cmp_level = ZLIB_CMPLVL_NONE; /* dont use compression */ return; } *zlib_fptr[findx] = fptr; } return; } fis-gtm-V7.0-005/sr_unix/gtm_zlib.h0000644000032200000250000001074514342376330016030 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2008-2016 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. * * * ****************************************************************/ #ifndef GTM_ZLIB_H_INCLUDED #define GTM_ZLIB_H_INCLUDED #if (defined(__osf__) && defined(__alpha)) || (defined(__MVS__)) /* For some reason, zconf.h (included by zlib.h) on Tru64 seems to undefine const if STDC is not defined. * The GT.M build time options currently dont define __STDC__ on Tru64 (which is what leads zconf.h to define STDC) * so define STDC temporarily. In any case check if it is defined and only if not defined, do the overriding define. */ # if (!defined(STDC)) # define GTM_ZLIB_STDC_DEFINE # define STDC # endif #endif #include #if (defined(__osf__) && defined(__alpha)) || (defined(__MVS__)) /* Undefine STDC in case it was defined just above */ # if (defined(GTM_ZLIB_STDC_DEFINE)) # undef STDC # endif #endif typedef int (*zlib_cmp_func_t)(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level); typedef int (*zlib_uncmp_func_t)(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen); typedef uLong (*zlib_cmpbound_func_t)(uLong sourceLen); GBLREF zlib_cmp_func_t zlib_compress_fnptr; GBLREF zlib_uncmp_func_t zlib_uncompress_fnptr; /* The standard shared library suffix for HPUX on HPPA is .sl. * On HPUX/IA64, the standard suffix was changed to .so (to match other Unixes) but for * the sake of compatibility, they still accept, and look for, .sl if .so is not present. * Nevertheless, we use the standard suffix on all platforms. */ #if (defined(__hpux) && defined(__hppa)) # define ZLIB_LIBNAME "libz.sl" #else # define ZLIB_LIBNAME "libz.so" #ifdef _AIX # define ZLIB_AIXLIBNAME "libz.a(libz.so.1)" #endif #endif #define ZLIB_LIBFLAGS (RTLD_NOW) /* RTLD_NOW - resolve immediately so we know errors sooner than later */ #define ZLIB_CMP_FNAME "compress2" #define ZLIB_UNCMP_FNAME "uncompress" #define ZLIB_NUM_DLSYMS 2 /* number of function names that we need to dlsym (compress2 and uncompress) */ GBLREF int4 gtm_zlib_cmp_level; /* zlib compression level specified at process startup */ GBLREF int4 repl_zlib_cmp_level; /* zlib compression level currently in use in replication pipe */ #define ZLIB_CMPLVL_MIN 0 #define ZLIB_CMPLVL_MAX 9 /* although currently known max zlib compression level is 9, it could be higher in * future versions of zlib so we dont do any edit checks on this value inside of GT.M */ #define ZLIB_CMPLVL_NONE ZLIB_CMPLVL_MIN #define GTM_CMPLVL_OUT_OF_RANGE(x) (ZLIB_CMPLVL_MIN > x) void gtm_zlib_init(void); /* Macros for zlib compress2 and uncompress function calls. Since 'malloc' or 'free' inside zlib library does NOT go * through gtm_malloc or gtm_free respectively, defer signals (MUPIP STOP for instance) until the corresponding zlib * call is completed so as to avoid deadlocks involving nested 'malloc' or 'free' each waiting for the other's * completion */ #define ZLIB_COMPRESS(CMPBUFF_PTR, CMPLEN, UNCMPBUFF_PTR, UNCMPLEN, ZLIB_CMP_LEVEL, RC) \ { \ GBLREF zlib_cmp_func_t zlib_compress_fnptr; \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_ZLIB_CMP_UNCMP, prev_intrpt_state); \ assert(0 < (signed)(CMPLEN)); \ assert(NULL != zlib_compress_fnptr); \ RC = (*zlib_compress_fnptr)(((Bytef *)(CMPBUFF_PTR)), (uLongf *)&(CMPLEN), (const Bytef *)(UNCMPBUFF_PTR), \ (uLong)(UNCMPLEN), ZLIB_CMP_LEVEL); \ ENABLE_INTERRUPTS(INTRPT_IN_ZLIB_CMP_UNCMP, prev_intrpt_state); \ } #define ZLIB_UNCOMPRESS(UNCMPBUFF_PTR, UNCMPLEN, CMPBUFF_PTR, CMPLEN, RC) \ { \ GBLREF zlib_uncmp_func_t zlib_uncompress_fnptr; \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_ZLIB_CMP_UNCMP, prev_intrpt_state); \ assert(0 < (signed)(UNCMPLEN)); \ assert(NULL != zlib_uncompress_fnptr); \ RC = (*zlib_uncompress_fnptr)(((Bytef *)(UNCMPBUFF_PTR)), (uLongf *)&(UNCMPLEN), (const Bytef *)(CMPBUFF_PTR), \ (uLong)(CMPLEN)); \ ENABLE_INTERRUPTS(INTRPT_IN_ZLIB_CMP_UNCMP, prev_intrpt_state); \ } #endif fis-gtm-V7.0-005/sr_unix/gtmbase.gtc0000755000032200000250000000176414342376330016175 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright 2001 Sanchez Computer Associates, 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. # # # ################################################################# gtm_dist=GTMDIST echo=ECHO gtmgbldir="./mumps.gld" export gtmgbldir export gtm_dist $echo "Updating your login files to support GT.M. You must log out" $echo "and log back in for these changes to take effect." $echo "" if [ -f $HOME/.cshrc ]; then grep "source $gtm_dist" $HOME/.cshrc > /dev/null 2>&1 if [ $? = 1 ]; then $echo "source $gtm_dist/gtmcshrc" >> $HOME/.cshrc fi fi if [ -f $HOME/.profile ]; then grep ". $gtm_dist" $HOME/.profile > /dev/null 2>&1 if [ $? = 1 ]; then $echo ". $gtm_dist/gtmprofile" >> $HOME/.profile fi fi fis-gtm-V7.0-005/sr_unix/gtmci.h0000755000032200000250000000426014342376330015322 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GTMCI_H #define GTMCI_H #include #include "mdef.h" #define GTM_CIMOD "GTM$CI" /* base call-in frame at level 1 */ /* Allow this many nested callin levels before generating an error. Previously, the code * allowed a number of condition handlers per call-in invocation, but when we implemented * triggers they count as an invocation and caused varying behavior that made testing the limit * problematic, so we picked an arbitrary limit (10) that seems generous. */ #define CALLIN_MAX_LEVEL 10 #define CALLIN_HASHTAB_SIZE 32 #define SET_CI_ENV(g) \ { \ frame_pointer->flags = SFF_CI; \ frame_pointer->old_frame_pointer->ctxt = GTM_CONTEXT(g); \ IA64_ONLY(frame_pointer->old_frame_pointer->mpc = CODE_ADDRESS_C(g);) \ NON_IA64_ONLY(frame_pointer->old_frame_pointer->mpc = CODE_ADDRESS(g);) \ } /* Macro that allows temp_fgncal_stack to override fgncal_stack. This is used in gtm_init (in gtmci.c) * during creation of a new level to solve chicken and egg problem that old fgncal_stack needs to be * saved but cannot be put into an mv_stent until after the level is actually created. This temp serves * that purpose so only has a non-NULL value for the duration of level creation. */ #define FGNCAL_STACK ((NULL == TREF(temp_fgncal_stack)) ? fgncal_stack : TREF(temp_fgncal_stack)) void ci_restart(void); void ci_ret_code(void); void ci_ret_code_exit(void); void ci_ret_code_quit(void); void gtmci_isv_save(void); void gtmci_isv_restore(void); rhdtyp *make_cimode(void); int gtm_ci_exec(const char *c_rtn_name, void *callin_handle, int populate_handle, va_list var, boolean_t internal_use); #ifdef _AIX void gtmci_cleanup(void); #endif #endif fis-gtm-V7.0-005/sr_unix/gtmsource_onln_rlbk_clnup.c0000644000032200000250000001037514342376334021470 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2019 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 "gdsroot.h" #include "gdsblk.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "repl_msg.h" /* for gtmsource.h */ #include "gtmsource.h" /* for jnlpool_addrs structure definition */ #include "gvcst_protos.h" /* for gvcst_root_search prototype */ #include "repl_instance.h" #include "gtmrecv.h" #include "gtmimagename.h" #include "have_crit.h" #include "tp_frame.h" GBLREF gtmsource_state_t gtmsource_state; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF seq_num gtmsource_save_read_jnl_seqno; GBLREF uint4 process_id; void gtmsource_onln_rlbk_clnup() { gtmsource_local_ptr_t gtmsource_local; boolean_t was_crit; sgmnt_addrs *repl_csa; gtmsource_local = jnlpool->gtmsource_local; repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; was_crit = repl_csa->now_crit; assert(!repl_csa->hold_onto_crit); assert(was_crit || (process_id == gtmsource_local->gtmsource_srv_latch.u.parts.latch_pid) || (0 != have_crit(CRIT_HAVE_ANY_REG))); /* Reset source server context to indicate a fresh connection that is about to take place */ assert(NULL != gtmsource_local); if (NULL != gtmsource_local) { /* If ROLLBACK has not taken the instance past the source server's read_jnl_seqno, then the source server should * just continue from where it currently is and start sending the journal records from that point onwards. But, this * is non-trivial. The reason is because, when the source server detected the online rollback, it could be in the * READ_POOL state. But, since the instance has been rolled back, the journal pool cannot be relied upon in its * entirety. To illustrate this -- consider that the journal pool contains the data from 1-100 and the source server * is currently sending sequence number 30 and is reading from the pool. Assume an online rollback happens that * takes the instance from sequence number 100 to sequence number 80 and leaves the journal pool write_addr and * rsrv_write_addr untouched. Now, lets say GT.M process comes in after this and does a few more updates. All of * these updates will be written in the journal pool right after the "old-rolled-back" sequence number 100. If the * source server continues to read from the pool, it will send the valid data until sequence number 80. After that, * it will start sending the "old-rolled-back" sequence numbers 81-100 which is not right. To avoid this, rollback * should set the write_addr and rsrv_write_addr by searching in the journal pool for sequence number 81. This is * currently not done, but is something that we can think about when it comes to optimization. Until then, force * rollback to read only from the file until the current seqno (using gtmsource_save_read_jnl_seqno). * READ_FILE mode. */ gtmsource_local->read_state = READ_FILE; /* Set the state which gets bubbled up the call chain to gtmsource_process at which point we will close and * re-establish the connection with the other end. */ gtmsource_local->gtmsource_state = gtmsource_state = GTMSOURCE_HANDLE_ONLN_RLBK; if (!was_crit) grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); /* We have to let the read files logic know that until we have sent data "upto" the current journal sequence number * at this point, we cannot rely on the journal pool. Indicate this through the gtmsource_save_read_jnl_seqno global * variable */ gtmsource_save_read_jnl_seqno = jnlpool->jnlpool_ctl->jnl_seqno; GTMSOURCE_SET_READ_ADDR(gtmsource_local, jnlpool); if (!was_crit) rel_lock(jnlpool->jnlpool_dummy_reg); } return; } fis-gtm-V7.0-005/sr_unix/gtmci_signals.c0000755000032200000250000000641114342376330017035 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "gtm_unistd.h" #include "gtmci_signals.h" #include "gtmsiginfo.h" #include "invocation_mode.h" /* These routines implement signal handling protocol between gtm and external user (if C is base the application): + by switching the active signal handlers between gtm and external user + if signal received in gtm context, it is the responsibility of gtm to execute external handlers. + similary, external user is responsible to save and execute the gtm signal handlers if signalled in external context. */ /* info of the last signal handled by generic_signal_handler */ GBLREF int4 exi_condition; GBLREF siginfo_t exi_siginfo; GBLDEF gtm_sigcontext_t exi_context; static struct sigaction *gtm_sig_h; /* storage for GT.M handlers */ static struct sigaction *ext_sig_h; /* handlers defined in external application */ static boolean_t sig_gtm_ctxt = FALSE; /* if current signal context is of GT.M */ /* initialize all sig handlers to 'act' and save external handlers if C is the base routine in call-ins. Otherwise, if M is the base routine, don't retain the old handlers */ void sig_save_ext(struct sigaction* act) { int i; if (MUMPS_CALLIN & invocation_mode) { /* C is the base */ gtm_sig_h = (struct sigaction*)malloc(NSIG * SIZEOF(struct sigaction)); ext_sig_h = (struct sigaction*)malloc(NSIG * SIZEOF(struct sigaction)); for (i = 1; i <= NSIG; i++) sigaction(i, act, &ext_sig_h[i-1]); } else { /* GT.M is the base */ for (i = 1; i <= NSIG; i++) sigaction(i, act, 0); } } void sig_save_gtm(void) { int i; if (MUMPS_CALLIN & invocation_mode) { for (i = 1; i <= NSIG; i++) sigaction(i, 0, >m_sig_h[i-1]); sig_gtm_ctxt = TRUE; } } void sig_switch_gtm(void) /* switch to GT.M signal context */ { int i; if ((MUMPS_CALLIN & invocation_mode) && !sig_gtm_ctxt) { for (i = 1; i <= NSIG; i++) sigaction(i, >m_sig_h[i-1], &ext_sig_h[i-1]); sig_gtm_ctxt = TRUE; } } void sig_switch_ext(void) /* switch to external signal context */ { int i; if ((MUMPS_CALLIN & invocation_mode) && sig_gtm_ctxt) { for (i = 1; i <= NSIG; i++) sigaction(i, &ext_sig_h[i-1], >m_sig_h[i-1]); sig_gtm_ctxt = FALSE; } } /* identify any signal received in GT.M context, and handle them in the context of external user */ void gtmci_exit_handler(void) { static boolean_t handler_active = FALSE; struct sigaction *act, ignore; if ((0 >= exi_condition && NSIG < exi_condition) || !(MUMPS_CALLIN & invocation_mode) || !(MUMPS_GTMCI & invocation_mode) || handler_active) return; handler_active = TRUE; sig_switch_ext(); act = &ext_sig_h[exi_condition-1]; if (SIG_IGN == act->sa_handler || SIG_DFL == act->sa_handler) return; if (act->sa_flags & SA_SIGINFO) (*act->sa_sigaction)(exi_condition, &exi_siginfo, &exi_context); else (*act->sa_handler)(exi_condition); } fis-gtm-V7.0-005/sr_unix/gtmci_signals.h0000755000032200000250000000124614342376330017043 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 #ifndef GTMCI_SIGNALS_H #define GTMCI_SIGNALS_H void sig_save_ext(struct sigaction* act); void sig_save_gtm(void); void sig_switch_gtm(void); void sig_switch_ext(void); void gtmci_exit_handler(void); #endif fis-gtm-V7.0-005/sr_unix/gtmcrypt.h0000644000032200000250000006111214342376330016064 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2022 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. * * * ****************************************************************/ #ifndef GTMCRYPT_H #define GTMCRYPT_H #include "gtmxc_types.h" #include "gtmimagename.h" #include "have_crit.h" #include "deferred_signal_handler.h" #include "wbox_test_init.h" #include "gtmmsg.h" #include "error.h" /* for MAKE_MSG_WARNING macro */ #define gtmcrypt_close (*gtmcrypt_close_fnptr) #define gtmcrypt_encrypt_decrypt (*gtmcrypt_encrypt_decrypt_fnptr) #define gtmcrypt_init (*gtmcrypt_init_fnptr) #define gtmcrypt_init_db_cipher_context_by_hash (*gtmcrypt_init_db_cipher_context_by_hash_fnptr) #define gtmcrypt_init_device_cipher_context_by_keyname (*gtmcrypt_init_device_cipher_context_by_keyname_fnptr) #define gtmcrypt_obtain_db_key_hash_by_keyname (*gtmcrypt_obtain_db_key_hash_by_keyname_fnptr) #define gtmcrypt_release_cipher_context (*gtmcrypt_release_cipher_context_fnptr) #define gtmcrypt_same_key (*gtmcrypt_same_key_fnptr) #define gtmcrypt_strerror (*gtmcrypt_strerror_fnptr) /* It's important that the "gtmcrypt_interface.h" include should be *after* the above macro definitions. This way, the function * prototypes defined in the header file will automatically be expanded to function pointers saving us the trouble of explicitly * defining them once again. */ #include "gtmcrypt_interface.h" #define GTM_MAX_IV_LEN 16 typedef struct enc_handles_struct { gtmcrypt_key_t encr_key_handle; gtmcrypt_key_t encr_key_handle2; } enc_handles; GBLREF boolean_t gtmcrypt_initialized; GBLREF mstr pvt_crypt_buf; GBLREF char dl_err[]; GBLREF char *gtmcrypt_badhash_size_msg; GBLREF void (*primary_exit_handler)(void); LITREF char gtmcrypt_repeat_msg[]; LITREF gtm_string_t null_iv; error_def(ERR_CRYPTDLNOOPEN); error_def(ERR_CRYPTDLNOOPEN2); error_def(ERR_CRYPTHASHGENFAILED); error_def(ERR_CRYPTINIT); error_def(ERR_CRYPTKEYFETCHFAILED); error_def(ERR_CRYPTOPFAILED); /* =====================================================================================================* * Error Reporting Macros * * =====================================================================================================*/ #define CRYPTERR_MASK 0x10000000 #define REPEAT_MSG_MASK 0x20000000 #define IS_CRYPTERR_MASK(ERRID) ((ERRID) & CRYPTERR_MASK) #define IS_REPEAT_MSG_MASK(ERRID) ((ERRID) & REPEAT_MSG_MASK) #define SET_CRYPTERR_MASK(ERRID) ((ERRID) | CRYPTERR_MASK) #define SET_REPEAT_MSG_MASK(ERRID) ((ERRID) | REPEAT_MSG_MASK) #define CLEAR_CRYPTERR_MASK(ERRID) (ERRID = ((ERRID) & ~CRYPTERR_MASK)) #define CLEAR_REPEAT_MSG_MASK(ERRID) (ERRID = ((ERRID) & ~REPEAT_MSG_MASK)) #define REALLOC_CRYPTBUF_IF_NEEDED(LEN) \ { \ if (!pvt_crypt_buf.addr || (pvt_crypt_buf.len < LEN)) \ { \ if (pvt_crypt_buf.addr) \ free(pvt_crypt_buf.addr); \ pvt_crypt_buf.addr = (char *)malloc(LEN); \ pvt_crypt_buf.len = LEN; \ } \ } #define GTMCRYPT_REPORT_ERROR(ERRID, MECHANISM, LEN, PTR) \ { \ int errid; \ const char *errptr; \ intrpt_state_t prev_intrpt_state; \ \ errid = ERRID; \ assert(IS_CRYPTERR_MASK(errid)); \ CLEAR_CRYPTERR_MASK(errid); \ if (IS_REPEAT_MSG_MASK(errid)) \ errptr = >mcrypt_repeat_msg[0]; \ else if ((ERR_CRYPTDLNOOPEN == errid) || (ERR_CRYPTDLNOOPEN2 == errid) \ || (MAKE_MSG_WARNING(ERR_CRYPTDLNOOPEN2) == errid) || (MAKE_MSG_WARNING(ERR_CRYPTDLNOOPEN) == errid)) \ errptr = (const char *)&dl_err[0]; \ else if (ERR_CRYPTHASHGENFAILED == errid) \ errptr = (const char *)gtmcrypt_badhash_size_msg; \ else \ { \ DEFER_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ errptr = (const char *)gtmcrypt_strerror(); \ ENABLE_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ } \ CLEAR_REPEAT_MSG_MASK(errid); \ MECHANISM(VARLSTCNT(6) errid, 4, LEN, PTR, LEN_AND_STR(errptr)); \ } #define CORE_ON_CRYPTOPFAILED \ MBSTART { \ /* Except for white box testing capture CRYPTOPFAILED cores for analysis */ \ if (!ENCR_WBOX_ENABLED) \ gtm_fork_n_core(); \ } MBEND /* =====================================================================================================*/ /* GT.M Related Macros */ /* =====================================================================================================*/ #define IS_ENCRYPTED_BIT 1 #define TO_BE_ENCRYPTED_BIT 2 #define UNSTARTED -1 #define EMPTY_GTMCRYPT_HASH16 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" #define EMPTY_GTMCRYPT_HASH32 EMPTY_GTMCRYPT_HASH16 EMPTY_GTMCRYPT_HASH16 #define EMPTY_GTMCRYPT_HASH EMPTY_GTMCRYPT_HASH32 EMPTY_GTMCRYPT_HASH32 /* Below macros accept any other field in place of CSD and CSA as long they contain the requisite fields. */ #define IS_ENCRYPTED(IS_ENCRYPTED_FIELD) (IS_ENCRYPTED_FIELD & IS_ENCRYPTED_BIT) #define TO_BE_ENCRYPTED(IS_ENCRYPTED_FIELD) (IS_ENCRYPTED_FIELD & TO_BE_ENCRYPTED_BIT) #define USES_ENCRYPTION(IS_ENCRYPTED_FIELD) (IS_ENCRYPTED_FIELD != 0) #define MARK_AS_ENCRYPTED(IS_ENCRYPTED_FIELD) IS_ENCRYPTED_FIELD |= IS_ENCRYPTED_BIT #define MARK_AS_TO_BE_ENCRYPTED(IS_ENCRYPTED_FIELD) IS_ENCRYPTED_FIELD |= TO_BE_ENCRYPTED_BIT #define SET_AS_ENCRYPTED(IS_ENCRYPTED_FIELD) IS_ENCRYPTED_FIELD = IS_ENCRYPTED_BIT #define SET_AS_UNENCRYPTED(IS_ENCRYPTED_FIELD) IS_ENCRYPTED_FIELD = 0 #define USES_NEW_KEY(CSD) (TO_BE_ENCRYPTED((CSD)->is_encrypted) \ && (UNSTARTED < (CSD)->encryption_hash_cutoff)) #define USES_ANY_KEY(CSD) (IS_ENCRYPTED((CSD)->is_encrypted) \ || (TO_BE_ENCRYPTED((CSD)->is_encrypted) \ && (UNSTARTED < (CSD)->encryption_hash_cutoff))) #define NEEDS_NEW_KEY(CSD, TN) (TO_BE_ENCRYPTED((CSD)->is_encrypted) \ && (UNSTARTED < (CSD)->encryption_hash_cutoff) \ && ((CSD)->encryption_hash2_start_tn <= TN)) #define NEEDS_ANY_KEY(CSD, TN) (IS_ENCRYPTED((CSD)->is_encrypted) \ || (TO_BE_ENCRYPTED((CSD)->is_encrypted) \ && (UNSTARTED < (CSD)->encryption_hash_cutoff) \ && ((CSD)->encryption_hash2_start_tn <= TN))) #define IS_BLK_ENCRYPTED(LEVL, BSIZ) ((0 <= ((char)LEVL)) && (0 < BSIZ)) #define BLK_NEEDS_ENCRYPTION(LEVL, BSIZ) IS_BLK_ENCRYPTED(LEVL, BSIZ) #define BLK_NEEDS_ENCRYPTION3(FLAG, LEVL, BSIZ) (FLAG && IS_BLK_ENCRYPTED(LEVL, BSIZ)) #define ENCR_INITIALIZED gtmcrypt_initialized #define ENCR_WBOX_ENABLED (gtm_white_box_test_case_enabled \ && (WBTEST_ENCRYPT_INIT_ERROR == gtm_white_box_test_case_number \ || WBTEST_LOW_MEMORY == gtm_white_box_test_case_number)) #define ASSERT_ENCRYPTION_INITIALIZED assert(ENCR_INITIALIZED || ENCR_WBOX_ENABLED) #define IS_INTERACTIVE_MODE (IS_MUMPS_IMAGE) #define GTMCRYPT_COPY_ENCRYPT_SETTINGS(SRC, DST) \ { \ (DST)->is_encrypted = (SRC)->is_encrypted; \ memcpy((DST)->encryption_hash, (SRC)->encryption_hash, GTMCRYPT_HASH_LEN); \ memcpy((DST)->encryption_hash2, (SRC)->encryption_hash2, GTMCRYPT_HASH_LEN); \ (DST)->non_null_iv = (SRC)->non_null_iv; \ (DST)->encryption_hash_cutoff = (SRC)->encryption_hash_cutoff; \ (DST)->encryption_hash2_start_tn = (SRC)->encryption_hash2_start_tn; \ } \ #define SAME_ENCRYPTION_SETTINGS(SRC1, SRC2) \ (((SRC1)->is_encrypted == (SRC2)->is_encrypted) \ && (!IS_ENCRYPTED((SRC1)->is_encrypted) \ || (!memcmp((SRC1)->encryption_hash, (SRC2)->encryption_hash, GTMCRYPT_HASH_LEN) \ && ((SRC1)->non_null_iv == (SRC2)->non_null_iv))) \ && ((SRC1)->encryption_hash_cutoff == (SRC2)->encryption_hash_cutoff) \ && ((UNSTARTED == (SRC1)->encryption_hash_cutoff) \ || (!memcmp((SRC1)->encryption_hash2, (SRC2)->encryption_hash2, GTMCRYPT_HASH_LEN) \ && ((SRC1)->encryption_hash2_start_tn == (SRC2)->encryption_hash2_start_tn)))) /* General Note: All macros below (except GTMCRYPT_CLOSE) take CSA as their first parameter. Currently, most macros do not use CSA, * but include a reference to CSA in case a need arises in the future. */ /* Database specific initialization - gets the encryption key corresponding to the HASH (SHA-512 currently) found in the database * file header and allocates a buffer large enough to encrypt/decrypt database block sizes. */ #define INIT_DB_OR_JNL_ENCRYPTION(CSA, CSD, FILENAME_LEN, FILENAME, RC) \ { \ RC = 0; \ (CSA)->encr_key_handle = (void *)GTMCRYPT_INVALID_KEY_HANDLE; \ (CSA)->encr_key_handle2 = (void *)GTMCRYPT_INVALID_KEY_HANDLE; \ if (IS_ENCRYPTED((CSD)->is_encrypted)) \ { \ GTMCRYPT_INIT_BOTH_CIPHER_CONTEXTS(CSA, (CSD)->encryption_hash, \ FILENAME_LEN, FILENAME, (CSA)->encr_key_handle, RC); \ } \ if ((0 == RC) && USES_NEW_KEY(CSD)) \ { \ GTMCRYPT_INIT_BOTH_CIPHER_CONTEXTS(CSA, (CSD)->encryption_hash2, \ FILENAME_LEN, FILENAME, (CSA)->encr_key_handle2, RC); \ } \ } /* Process specific initialization - dlopen libgtmcrypt.so and invoke gtmcrypt_init() */ #define INIT_PROC_ENCRYPTION(RC) \ { \ intrpt_state_t prev_intrpt_state; \ \ RC = 0; \ if (!gtmcrypt_initialized) \ { \ if (0 == (RC = gtmcrypt_entry())) \ { /* dlopen succeeded */ \ DEFER_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ if (0 != gtmcrypt_init(IS_INTERACTIVE_MODE ? GTMCRYPT_OP_INTERACTIVE_MODE : 0)) \ RC = SET_CRYPTERR_MASK(ERR_CRYPTINIT); \ else \ gtmcrypt_initialized = TRUE; /* Intialization is done for this process. */ \ ENABLE_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ if (gtmcrypt_initialized && primary_exit_handler) \ atexit(primary_exit_handler); \ } else \ RC = SET_CRYPTERR_MASK(RC); \ } \ } /* Given a cryptographic hash (currently SHA-512), the below macro retrieves a handle to the symmetric key corresponding to * the hash. This macro is always called before attempting an encrypt or decrypt operation. */ #define GTMCRYPT_INIT_BOTH_CIPHER_CONTEXTS(CSA, HASH, DB_PATH_LENGTH, DB_PATH, ENCRYPT_KEY_HANDLE, RC) \ { \ gtm_string_t hash_string, db_path_string; \ intrpt_state_t prev_intrpt_state; \ \ ENCRYPT_KEY_HANDLE = GTMCRYPT_INVALID_KEY_HANDLE; \ if (gtmcrypt_initialized) \ { \ assert(NULL != HASH); \ hash_string.length = (gtm_long_t)GTMCRYPT_HASH_LEN; \ hash_string.address = (gtm_char_t *)(HASH); \ db_path_string.length = (gtm_long_t)DB_PATH_LENGTH; \ db_path_string.address = (gtm_char_t *)(DB_PATH); \ assert(0 <= db_path_string.length); \ DEFER_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ if (0 != gtmcrypt_init_db_cipher_context_by_hash(&(ENCRYPT_KEY_HANDLE), hash_string, db_path_string, null_iv)) \ RC = SET_CRYPTERR_MASK(ERR_CRYPTKEYFETCHFAILED); \ else \ RC = 0; \ ENABLE_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ } else \ { \ RC = SET_REPEAT_MSG_MASK((SET_CRYPTERR_MASK(ERR_CRYPTOPFAILED))); \ DEBUG_ONLY(CORE_ON_CRYPTOPFAILED); \ } \ if (gtmcrypt_initialized && primary_exit_handler) \ atexit(primary_exit_handler); \ } /* Ensure that the symmetric key corresponding to the specified hash exists and that a handle is created. */ #define GTMCRYPT_HASH_CHK(CSA, HASH, DB_PATH_LENGTH, DB_PATH, RC) \ { \ gtmcrypt_key_t handle; \ \ GTMCRYPT_INIT_BOTH_CIPHER_CONTEXTS(CSA, HASH, DB_PATH_LENGTH, DB_PATH, handle, RC); \ } /* The below macro retrieves a handle to the symmetric key corresponding to the provided key name as specified in the * configuration file. */ #define GTMCRYPT_INIT_CIPHER_CONTEXT(KEYNAME_LENGTH, KEYNAME, IV_LENGTH, IV, KEY_HANDLE, OPERATION, RC) \ { \ gtm_string_t keyname, iv; \ intrpt_state_t prev_intrpt_state; \ \ KEY_HANDLE = GTMCRYPT_INVALID_KEY_HANDLE; \ if (gtmcrypt_initialized) \ { \ assert(NULL != KEYNAME); \ assert(NULL != IV); \ keyname.length = (gtm_long_t)KEYNAME_LENGTH; \ keyname.address = (gtm_char_t *)(KEYNAME); \ iv.length = (gtm_long_t)IV_LENGTH; \ iv.address = (gtm_char_t *)(IV); \ DEFER_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ if (0 != gtmcrypt_init_device_cipher_context_by_keyname(&(KEY_HANDLE), keyname, iv, OPERATION)) \ RC = SET_CRYPTERR_MASK(ERR_CRYPTKEYFETCHFAILED); \ else \ RC = 0; \ ENABLE_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ } else \ { \ RC = SET_REPEAT_MSG_MASK((SET_CRYPTERR_MASK(ERR_CRYPTOPFAILED))); \ DEBUG_ONLY(CORE_ON_CRYPTOPFAILED); \ } \ } /* Safely remove the specified handle to a particular symmetric key. */ #define GTMCRYPT_REMOVE_CIPHER_CONTEXT(KEY_HANDLE, RC) \ { \ intrpt_state_t prev_intrpt_state; \ \ RC = 0; \ if (gtmcrypt_initialized && (GTMCRYPT_INVALID_KEY_HANDLE != KEY_HANDLE)) \ { \ DEFER_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ if (0 != gtmcrypt_release_cipher_context(KEY_HANDLE)) \ { \ RC = SET_CRYPTERR_MASK(ERR_CRYPTKEYFETCHFAILED); \ } else \ { \ KEY_HANDLE = GTMCRYPT_INVALID_KEY_HANDLE; \ RC = 0; \ } \ ENABLE_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ } \ } /* Based on the database name (used as a key name), the below macro looks up the corresponding symmetric key and copies its hash * into the passed buffer storage. */ #define GTMCRYPT_HASH_GEN(CSA, FILENAME_LENGTH, FILENAME, KEY_PATH_LENGTH, KEY_PATH, HASH, RC) \ { \ gtm_string_t filename_string, hash_string, key_path_string; \ intrpt_state_t prev_intrpt_state; \ \ if (gtmcrypt_initialized) \ { \ assert(NULL != FILENAME); \ assert(NULL != HASH); \ filename_string.length = (gtm_long_t)FILENAME_LENGTH; \ filename_string.address = (gtm_char_t *)(FILENAME); \ assert(0 <= filename_string.length); \ key_path_string.length = (gtm_long_t)KEY_PATH_LENGTH; \ key_path_string.address = (gtm_char_t *)(KEY_PATH); \ assert(0 <= key_path_string.length); \ DEFER_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ if (0 != gtmcrypt_obtain_db_key_hash_by_keyname(filename_string, key_path_string, &hash_string)) \ RC = SET_CRYPTERR_MASK(ERR_CRYPTKEYFETCHFAILED); \ else \ { \ if (hash_string.length != GTMCRYPT_HASH_LEN) \ { /* Populate the message about the bad hash size, allocating the buffer, if necessary. */ \ if (NULL == gtmcrypt_badhash_size_msg) \ gtmcrypt_badhash_size_msg = (char *)malloc(1024); \ SNPRINTF(gtmcrypt_badhash_size_msg, 1023, "Specified symmetric key hash has " \ "length %d, which is different from the expected hash length %d", \ hash_string.length, GTMCRYPT_HASH_LEN); \ RC = SET_CRYPTERR_MASK(ERR_CRYPTHASHGENFAILED); \ } else \ { /* Note that the copy is not NULL-terminated. */ \ memcpy(HASH, hash_string.address, hash_string.length); \ RC = 0; \ } \ } \ ENABLE_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ } else \ { \ RC = SET_REPEAT_MSG_MASK((SET_CRYPTERR_MASK(ERR_CRYPTOPFAILED))); \ DEBUG_ONLY(CORE_ON_CRYPTOPFAILED); \ } \ } /* Encrypt data with either a null IV or set to the specified value prior to the operation. */ #define GTMCRYPT_ENCRYPT(CSA, USE_NON_NULL_IV, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, IV_ADDR, IV_LEN, RC) \ { \ gtm_string_t iv_macro; \ \ if (!(USE_NON_NULL_IV)) \ { \ GTMCRYPT_ENCRYPT_DECRYPT_WITH_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, \ GTMCRYPT_OP_ENCRYPT, GTMCRYPT_IV_RESET, null_iv, RC); \ } else \ { \ assert(IV_LEN <= GTM_MAX_IV_LEN); \ iv_macro.address = (gtm_char_t *)IV_ADDR; \ iv_macro.length = (gtm_long_t)IV_LEN; \ GTMCRYPT_ENCRYPT_DECRYPT_WITH_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, \ GTMCRYPT_OP_ENCRYPT, GTMCRYPT_IV_SET, iv_macro, RC); \ } \ } /* Decrypt data with either a null IV or set to the specified value prior to the operation. */ #define GTMCRYPT_DECRYPT(CSA, USE_NON_NULL_IV, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, IV_ADDR, IV_LEN, RC) \ { \ gtm_string_t iv_macro; \ \ if (!(USE_NON_NULL_IV)) \ { \ GTMCRYPT_ENCRYPT_DECRYPT_WITH_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, \ GTMCRYPT_OP_DECRYPT, GTMCRYPT_IV_RESET, null_iv, RC); \ } else \ { \ assert(IV_LEN <= GTM_MAX_IV_LEN); \ iv_macro.address = (gtm_char_t *)IV_ADDR; \ iv_macro.length = (gtm_long_t)IV_LEN; \ GTMCRYPT_ENCRYPT_DECRYPT_WITH_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, \ GTMCRYPT_OP_DECRYPT, GTMCRYPT_IV_SET, iv_macro, RC); \ } \ } /* Encrypt data with the IV reset to all-NULL initial value prior to the operation. */ #define GTMCRYPT_ENCRYPT_NO_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, RC) \ GTMCRYPT_ENCRYPT_DECRYPT_WITH_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, \ GTMCRYPT_OP_ENCRYPT, GTMCRYPT_IV_RESET, null_iv, RC) /* Decrypt data with the IV reset to all-NULL initial value prior to the operation. */ #define GTMCRYPT_DECRYPT_NO_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, RC) \ GTMCRYPT_ENCRYPT_DECRYPT_WITH_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, \ GTMCRYPT_OP_DECRYPT, GTMCRYPT_IV_RESET, null_iv, RC) /* Encrypt data with the IV set to the specified value prior to the operation. */ #define GTMCRYPT_ENCRYPT_WITH_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, IV_ADDR, IV_LEN, RC) \ { \ gtm_string_t iv_macro; \ \ assert(IV_LEN <= GTM_MAX_IV_LEN); \ iv_macro.address = (gtm_char_t *)IV_ADDR; \ iv_macro.length = (gtm_long_t)IV_LEN; \ GTMCRYPT_ENCRYPT_DECRYPT_WITH_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, \ GTMCRYPT_OP_ENCRYPT, GTMCRYPT_IV_SET, iv_macro, RC); \ } /* Decrypt data with the IV set to the specified value prior to the operation. */ #define GTMCRYPT_DECRYPT_WITH_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, IV_ADDR, IV_LEN, RC) \ { \ gtm_string_t iv_macro; \ \ assert(IV_LEN <= GTM_MAX_IV_LEN); \ iv_macro.address = (gtm_char_t *)IV_ADDR; \ iv_macro.length = (gtm_long_t)IV_LEN; \ GTMCRYPT_ENCRYPT_DECRYPT_WITH_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, \ GTMCRYPT_OP_DECRYPT, GTMCRYPT_IV_SET, iv_macro, RC); \ } /* Encrypt data without touching the IV prior to the operation. */ #define GTMCRYPT_ENCRYPT_CONT_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, RC) \ GTMCRYPT_ENCRYPT_DECRYPT_WITH_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, \ GTMCRYPT_OP_ENCRYPT, GTMCRYPT_IV_CONTINUE, null_iv, RC) /* Use of null_iv as argument is irrelevant. */ /* Decrypt data without touching the IV prior to the operation. */ #define GTMCRYPT_DECRYPT_CONT_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, RC) \ GTMCRYPT_ENCRYPT_DECRYPT_WITH_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, \ GTMCRYPT_OP_DECRYPT, GTMCRYPT_IV_CONTINUE, null_iv, RC) /* Use of null_iv as argument is irrelevant. */ /* Encrypt or decrypt data with the IV optionally set to a specified, or reset to the initial, value prior to the operation. */ #ifdef GTM_CRYPT_ENCRYPT_DECRYPT_LOG # define GTMCRYPT_ENCRYPT_DECRYPT_WITH_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, OPERATION, IV_MODE, IV, RC) \ { \ int i; \ unsigned char c; \ intrpt_state_t prev_intrpt_state; \ \ assert(INBUF); \ if (gtmcrypt_initialized && (GTMCRYPT_INVALID_KEY_HANDLE != KEY_HANDLE)) \ { \ DEFER_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ FPRINTF(stderr, (OPERATION == GTMCRYPT_OP_ENCRYPT ? "Going to ENCRYPT\n" : "Going to DECRYPT\n")); \ for (i = 0; i < INBUF_LEN; i++) \ { \ c = ((char *)INBUF)[i]; \ FPRINTF(stderr, "%c(%02x) ", 31 < c && 127 > c ? c : '.', c); \ } \ FPRINTF(stderr, "\n IV:\n "); \ for (i = 0; i < (IV).length; i++) \ { \ c = ((char *)(IV).address)[i]; \ FPRINTF(stderr, "%c(%02x) ", 31 < c && 127 > c ? c : '.', c); \ } \ FPRINTF(stderr, "\n"); \ FFLUSH(stderr); \ if (0 == gtmcrypt_encrypt_decrypt(KEY_HANDLE, (char *)(INBUF), INBUF_LEN, (char *)(OUTBUF), \ OPERATION, IV_MODE, IV)) \ { \ FPRINTF(stderr, " Result:\n "); \ for (i = 0; i < INBUF_LEN; i++) \ { \ c = ((OUTBUF == NULL) ? (char *)INBUF : (char *)(OUTBUF))[i]; \ FPRINTF(stderr, "%c(%02x) ", 31 < c && 127 > c ? c : '.', c); \ } \ FPRINTF(stderr, "\n---------------------------------------------------------\n"); \ FFLUSH(stderr); \ RC = 0; \ } else \ RC = SET_CRYPTERR_MASK(ERR_CRYPTOPFAILED); \ ENABLE_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ } else \ { \ RC = SET_REPEAT_MSG_MASK((SET_CRYPTERR_MASK(ERR_CRYPTOPFAILED))); \ DEBUG_ONLY(CORE_ON_CRYPTOPFAILED); \ } \ } #else # define GTMCRYPT_ENCRYPT_DECRYPT_WITH_IV(CSA, KEY_HANDLE, INBUF, INBUF_LEN, OUTBUF, OPERATION, IV_MODE, IV, RC) \ { \ intrpt_state_t prev_intrpt_state; \ char *gcedwiv_inbuf = (char *)(INBUF); \ \ assert(gcedwiv_inbuf); \ if (gtmcrypt_initialized && (GTMCRYPT_INVALID_KEY_HANDLE != KEY_HANDLE)) \ { \ DEFER_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ if (0 == gtmcrypt_encrypt_decrypt(KEY_HANDLE, gcedwiv_inbuf, INBUF_LEN, (char *)(OUTBUF), \ OPERATION, IV_MODE, IV)) \ RC = 0; \ else \ { \ RC = SET_CRYPTERR_MASK(ERR_CRYPTOPFAILED); \ DEBUG_ONLY(CORE_ON_CRYPTOPFAILED); \ } \ ENABLE_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ } else \ { \ RC = SET_REPEAT_MSG_MASK((SET_CRYPTERR_MASK(ERR_CRYPTOPFAILED))); \ DEBUG_ONLY(CORE_ON_CRYPTOPFAILED); \ } \ } #endif /* Check whether the specified symmetric key handles belong to the same key. */ #define GTMCRYPT_SAME_KEY(KEY_HANDLE1, KEY_HANDLE2) \ gtmcrypt_same_key(KEY_HANDLE1, KEY_HANDLE2) /* Shut down the encryption for this process. */ #define GTMCRYPT_CLOSE \ { \ intrpt_state_t prev_intrpt_state; \ \ if (gtmcrypt_initialized) \ { \ DEFER_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ gtmcrypt_close(); \ gtmcrypt_initialized = FALSE; \ ENABLE_INTERRUPTS(INTRPT_IN_CRYPT_SECTION, prev_intrpt_state); \ } \ } uint4 gtmcrypt_entry(void); boolean_t verify_lib_loadpath(const char *libname, char *loadpath); #endif /* GTMCRYPT_H */ fis-gtm-V7.0-005/sr_unix/gtmcrypt_dbk_ref.c0000644000032200000250000012036214342376330017536 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2022 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 #include #include #include #include #include #include #include #include #include /* gpgme functions */ #include /* gcry*_err_t */ #include #include "gtmxc_types.h" #include "gtmcrypt_util.h" #include "gtmcrypt_interface.h" /* Function prototypes for gtmcrypt*.* functions */ #include "gtmcrypt_ref.h" #include "gtmcrypt_dbk_ref.h" #include "gtmcrypt_sym_ref.h" #include "gtmcrypt_pk_ref.h" #define UNRES_KEY_FILE 0 /* Key is for device encryption. */ #define UNRES_KEY_UNRES_DB 1 /* Key is for a database that does not yet exist. */ #define UNRES_KEY_RES_DB 2 /* Key is for a database that already exists. */ #define SEARCH_BY_KEYNAME 0 /* Searching for an unresolved key by name. */ #define SEARCH_BY_KEYPATH 1 /* Searching for an unresolved key by path. */ #define SEARCH_BY_HASH 2 /* Searching for an unresolved key by hash. */ #define CONFIG_FILE_UNREAD ('\0' == gc_config_filename[0]) #define GPG_MESSAGE "Verify encrypted key file and your GNUPGHOME settings" #define NON_GPG_MESSAGE "Verify encryption key in configuration file pointed to by $gtmcrypt_config" /* On certain platforms the st_mtime field of the stat structure got replaced by a timespec st_mtim field, which in turn has tv_sec * and tv_nsec fields. For compatibility reasons, those platforms define an st_mtime macro which points to st_mtim.tv_sec. Whenever * we detect such a situation, we define a nanosecond flavor of that macro to point to st_mtim.tv_nsec. On HPUX Itanium and older * AIX boxes the stat structure simply has additional fields with the nanoseconds value, yet the names of those field are different * on those two architectures, so we choose our mapping accordingly. */ #if defined st_mtime # define st_nmtime st_mtim.tv_nsec #elif defined(_AIX) # define st_nmtime st_mtime_n #elif defined(__hpux) && defined(__ia64) # define st_nmtime st_nmtime #endif /* Insert a new gtm_keystore_xxx_link_t element in a respective tree. */ #define INSERT_KEY_LINK(ROOT, LINK, TYPE, FIELD, VALUE, LENGTH, FILL_LEN, FIXED, DUPL) \ { \ int diff; \ TYPE *cur_node, **target_node; \ \ target_node = &ROOT; \ while (cur_node = *target_node) /* NOTE: Assignment!!! */ \ { \ diff = FIXED \ ? memcmp(cur_node->FIELD, VALUE, LENGTH) \ : strcmp((char *)cur_node->FIELD, (char *)VALUE); \ assert(DUPL || (0 != diff)); \ if (0 >= diff) \ target_node = &cur_node->left; \ else \ target_node = &cur_node->right; \ } \ /* Allocate and initialize a gtm_keystore_xxx_link_t element. */ \ *target_node = (TYPE *)MALLOC(SIZEOF(TYPE)); \ (*target_node)->left = (*target_node)->right = NULL; \ (*target_node)->link = LINK; \ memset((*target_node)->FIELD, 0, FILL_LEN); \ memcpy((*target_node)->FIELD, VALUE, LENGTH); \ } /* Remove a link from the unresolved list (because it is now resolved or is a duplicate). */ #define REMOVE_UNRESOLVED_LINK(CUR, PREV) \ { \ gtm_keystore_unres_key_link_t *next; \ \ next = (CUR)->next; \ if (NULL != PREV) \ (PREV)->next = next; \ else \ { \ assert(CUR == keystore_by_unres_key_head); \ keystore_by_unres_key_head = next; \ } \ FREE(CUR); \ CUR = next; \ } STATICDEF int n_keys; /* Count of how many keys were loaded. */ STATICDEF char gc_config_filename[GTM_PATH_MAX]; /* Path to the configuration file. */ STATICDEF gtm_keystore_hash_link_t *keystore_by_hash_head = NULL; /* Root of the binary search tree to look * keys up by hash. */ STATICDEF gtm_keystore_keyname_link_t *keystore_by_keyname_head = NULL; /* Root of the binary search tree to look * keys up by name. */ STATICDEF gtm_keystore_keypath_link_t *keystore_by_keypath_head = NULL; /* Root of the binary search tree to look * keys up by path. */ STATICDEF gtm_keystore_unres_key_link_t *keystore_by_unres_key_head = NULL; /* Head of the linked list holding keys of * DBs with presently unresolved paths. */ STATICDEF config_t gtmcrypt_cfg; /* Encryption configuration. */ STATICDEF char path_array[GTM_PATH_MAX]; /* Array for temporary storage of keys or * DBs' real path information. */ STATICDEF unsigned char key_hash_array[GTMCRYPT_HASH_LEN]; /* Array for temporary storage of keys' * hashes. */ GBLREF passwd_entry_t *gtmcrypt_pwent; GBLREF int gtmcrypt_init_flags; /* * Find the key based on its name. * * Arguments: key_name Name of the key. * key_path Path to the key (optional). * entry Address where to place the pointer to the found key. * database Flag indicating whether a database (or device) key is being searched. * * Returns: 0 if the key with the specified name is found; -1 otherwise. */ int gtmcrypt_getkey_by_keyname(char *key_name, char *key_path, gtm_keystore_t **entry, int database) { int error; if (NULL != key_path) *entry = keystore_lookup_by_keyname_plus(key_name, key_path, SEARCH_BY_KEYPATH); else *entry = keystore_lookup_by_keyname(key_name); if (NULL == *entry) { /* No matches in the binary tree; trying the unresolved key list. */ if (0 != keystore_refresh()) return -1; error = 0; if (NULL == (*entry = keystore_lookup_by_unres_key(key_name, SEARCH_BY_KEYNAME, key_path, SEARCH_BY_KEYPATH, database, &error))) { if (!error) { if (NULL == key_path) { UPDATE_ERROR_STRING("%s " STR_ARG " missing in configuration file or does not exist", (database ? "Database file" : "Keyname"), ELLIPSIZE(key_name)); } else { UPDATE_ERROR_STRING("%s " STR_ARG " missing in configuration file, does not exist, or is " "not associated with key " STR_ARG, (database ? "Database file" : "Keyname"), ELLIPSIZE(key_name), ELLIPSIZE(key_path)); } } return -1; } } assert(NULL != *entry); return 0; } /* * Find the key based on its hash. * * Arguments: hash Hash of the key. * db_path Path to the key (optional). * entry Address where to place the pointer to the found key. * * Returns: 0 if the key with the specified name is found; -1 otherwise. */ int gtmcrypt_getkey_by_hash(unsigned char *hash, char *db_path, gtm_keystore_t **entry) { int err_caused_by_gpg, error, errorlen; char save_err[MAX_GTMCRYPT_ERR_STRLEN + 1], hex_buff[GTMCRYPT_HASH_HEX_LEN + 1]; char *alert_msg, *errptr; if (NULL != db_path) *entry = keystore_lookup_by_keyname_plus(db_path, (char *)hash, SEARCH_BY_HASH); else *entry = keystore_lookup_by_hash(hash); if (NULL == *entry) { /* No matches in the binary tree; trying the unresolved key list. */ if (0 != keystore_refresh()) return -1; error = 0; if (NULL == (*entry = keystore_lookup_by_unres_key((char *)hash, SEARCH_BY_HASH, db_path, SEARCH_BY_KEYNAME, TRUE, &error))) { if (!error) { /* Be specific in the error as to what hash we were trying to find. */ errptr = gtmcrypt_strerror(); err_caused_by_gpg = ('\0' != errptr[0]); alert_msg = err_caused_by_gpg ? GPG_MESSAGE : NON_GPG_MESSAGE; GC_HEX(hash, hex_buff, GTMCRYPT_HASH_HEX_LEN); if (err_caused_by_gpg) { errorlen = STRLEN(errptr); if (MAX_GTMCRYPT_ERR_STRLEN < errorlen) errorlen = MAX_GTMCRYPT_ERR_STRLEN; memcpy(save_err, errptr, errorlen); save_err[errorlen] = '\0'; UPDATE_ERROR_STRING("Expected hash - " STR_ARG " - %s. %s", ELLIPSIZE(hex_buff), save_err, alert_msg); } else UPDATE_ERROR_STRING("Expected hash - " STR_ARG ". %s", ELLIPSIZE(hex_buff), alert_msg); } return -1; } } assert(NULL != *entry); return 0; } /* * Helper function to perform the actual binary search of the key by its hash. * * Arguments: hash Hash of the key. * * Returns: Pointer to the key, if found; NULL otherwise. */ STATICFNDEF gtm_keystore_t *keystore_lookup_by_hash(unsigned char *hash) { int diff; gtm_keystore_hash_link_t *cur_node; cur_node = keystore_by_hash_head; while (cur_node) { diff = memcmp(cur_node->link->key_hash, hash, GTMCRYPT_HASH_LEN); if (0 < diff) cur_node = cur_node->right; else if (0 == diff) return cur_node->link; else cur_node = cur_node->left; } return NULL; } /* * Helper function to perform the actual binary search of the key by its path. * * Arguments: keypath Path to the key. * * Returns: Pointer to the key, if found; NULL otherwise. */ STATICFNDEF gtm_keystore_t *keystore_lookup_by_keypath(char *keypath) { int diff; gtm_keystore_keypath_link_t *cur_node; cur_node = keystore_by_keypath_head; while (cur_node) { diff = strcmp(cur_node->link->key_path, keypath); if (0 < diff) cur_node = cur_node->right; else if (0 == diff) return cur_node->link; else cur_node = cur_node->left; } return NULL; } /* * Helper function to perform the actual binary search of the key by its name. * * Arguments: keyname Name of the key. * * Returns: Pointer to the key, if found; NULL otherwise. */ STATICFNDEF gtm_keystore_t *keystore_lookup_by_keyname(char *keyname) { int diff; gtm_keystore_keyname_link_t *cur_node; cur_node = keystore_by_keyname_head; while (cur_node) { diff = strcmp(cur_node->key_name, keyname); if (0 < diff) cur_node = cur_node->right; else if (0 == diff) return cur_node->link; else cur_node = cur_node->left; } return NULL; } /* * Helper function to perform the actual binary search of the key by its name. * * Arguments: keyname Name of the key. * search_field Value of the seconds search criterion besides the key name. * search_type Type of the second search criterion, either a key path or hash. * * Returns: Pointer to the key, if found; NULL otherwise. */ STATICFNDEF gtm_keystore_t *keystore_lookup_by_keyname_plus(char *keyname, char *search_field, int search_type) { int diff, match; gtm_keystore_keyname_link_t *cur_node; char *ynew_ext; char lcl_keyname[GTM_PATH_MAX]; unsigned int keynamelen, cplen; assert((SEARCH_BY_KEYPATH == search_type) || (SEARCH_BY_HASH == search_type)); assert(NULL != search_field); assert(keyname); /* Strip off EXT_NEW from autodb paths so that the key lookup works correctly */ keynamelen = strlen(keyname); if (GTM_PATH_MAX < keynamelen) keynamelen = GTM_PATH_MAX; ynew_ext = keyname + keynamelen - strlen(EXT_NEW); if ((ynew_ext >= keyname) && (0 == strcmp(ynew_ext, EXT_NEW))) { /* This is an autodb, fixup the path */ assert(strlen(EXT_NEW) <= keynamelen); cplen = (strlen(EXT_NEW) <= keynamelen) ? (keynamelen - strlen(EXT_NEW)) : 0; assert(GTM_PATH_MAX > cplen); memcpy(lcl_keyname, keyname, cplen); lcl_keyname[cplen] = '\0'; keyname = lcl_keyname; } cur_node = keystore_by_keyname_head; while (cur_node) { diff = strcmp(cur_node->key_name, keyname); if (0 < diff) cur_node = cur_node->right; else if (0 == diff) { if (SEARCH_BY_KEYPATH == search_type) match = (0 == strcmp(cur_node->link->key_path, search_field)); else match = (0 == memcmp(cur_node->link->key_hash, search_field, GTMCRYPT_HASH_LEN)); if (match) return cur_node->link; else if (NULL == cur_node->left) return NULL; else cur_node = cur_node->left; } else cur_node = cur_node->left; } return NULL; } /* * Helper function to perform a linear search of the key by its name or hash in the unresolved keys list. It attempts to resolve the * real path of a keyname in case it corresponds to a previously unresolved database name. If the path is resolved, the node's entry * is used to create (as needed) new key node as well as hash-, keyname-, and keypath-based links to it, and the unresolved entry is * removed from the list. * * Arguments: search_field1 Value of the first search criterion for unresolved keys. * search_field1_type Type of the first search criterion, either the key name or hash. * search_field2 Value of the second search criterion for unresolved keys. * search_field2_type Type of the second search criterion, either the key path or name. * database Flag indicating whether the search is for a database or device encryption key. * error Address where to set the flag indicating whether an error was encountered. * * Returns: Pointer to the key, if found; NULL otherwise. */ STATICFNDEF gtm_keystore_t *keystore_lookup_by_unres_key(char *search_field1, int search_field1_type, char *search_field2, int search_field2_type, int database, int *error) { gtm_keystore_unres_key_link_t *curr, *prev; gtm_keystore_t *node; int name_length, path_length, search_fail; char *name_search_field_ptr, *path_search_field_ptr, *ynew_ext; char *lcl_key_name, lcl_key_name_buff[GTM_PATH_MAX]; char name_search_field_buff[GTM_PATH_MAX]; unsigned int search_field_len, cplen; int isautodb; assert(NULL != search_field1); assert((SEARCH_BY_KEYNAME == search_field1_type) || (SEARCH_BY_HASH == search_field1_type)); assert((SEARCH_BY_KEYPATH == search_field2_type) || (SEARCH_BY_KEYNAME == search_field2_type)); assert(((SEARCH_BY_KEYNAME == search_field1_type) && (SEARCH_BY_KEYPATH == search_field2_type)) || ((SEARCH_BY_HASH == search_field1_type) && (SEARCH_BY_KEYNAME == search_field2_type))); /* Prepare the character array pointers to use for searching by key name or path. */ path_search_field_ptr = NULL; isautodb = FALSE; if (SEARCH_BY_KEYNAME == search_field1_type) { name_search_field_ptr = search_field1; if (database && (NULL == search_field2)) { /* Newly created AutoDBs have EXT_NEW appended to them, but the crypt cfg doesn't have those keys */ search_field_len = strlen(search_field1); if (GTM_PATH_MAX < search_field_len) search_field_len = GTM_PATH_MAX; ynew_ext = search_field1 + search_field_len - strlen(EXT_NEW); if (0 == strcmp(ynew_ext, EXT_NEW)) { /* Strip EXT_NEW off the path string for comparison later. Note that this path, minus EXT_NEW, * is a fully resolved path. */ isautodb = TRUE; assert(strlen(EXT_NEW) <= search_field_len); cplen = (strlen(EXT_NEW) <= search_field_len) ? (search_field_len - strlen(EXT_NEW)) : 0; assert(GTM_PATH_MAX > cplen); memcpy(name_search_field_buff, search_field1, cplen); name_search_field_buff[cplen] = '\0'; name_search_field_ptr = name_search_field_buff; } } if ((NULL != search_field2) && (SEARCH_BY_KEYPATH == search_field2_type)) path_search_field_ptr = search_field2; } else if ((NULL != search_field2) && (SEARCH_BY_KEYNAME == search_field2_type)) name_search_field_ptr = search_field2; else name_search_field_ptr = NULL; /* Start the main search loop. */ prev = NULL; curr = keystore_by_unres_key_head; while (curr) { /* Skip entries whose type does not match the one we are searching for. */ if ((database && (UNRES_KEY_FILE != curr->status)) || (!database && (UNRES_KEY_FILE == curr->status))) { /* If the database file has not been resolved yet, try resolving it. */ search_fail = 0; if (UNRES_KEY_UNRES_DB == curr->status) { if (isautodb) { /* Append EXT_NEW to see if this a matching AutoDB */ SNPRINTF(lcl_key_name_buff, GTM_PATH_MAX, "%s%s", curr->key_name, EXT_NEW); lcl_key_name = lcl_key_name_buff; } else lcl_key_name = curr->key_name; if (NULL == realpath(lcl_key_name, path_array)) { if (ENAMETOOLONG == errno) { *error = TRUE; UPDATE_ERROR_STRING("Real path, or a component of the path, of the database " STR_ARG " is too long", ELLIPSIZE(curr->key_name)); return NULL; } else if (ENOENT != errno) { *error = TRUE; UPDATE_ERROR_STRING("Could not obtain the real path of the database " STR_ARG ". %s", ELLIPSIZE(curr->key_name), strerror(errno)); return NULL; } /* If we are looking by a keyname, and the database is missing, skip the entry. Otherwise, * give a chance to find the key by hash. */ if (SEARCH_BY_KEYNAME == search_field1_type) search_fail = 1; } else { /* Once the path has been resolved, save it to avoid future realpath()s. * If this isn't an autodb, use the path as is. If it is an autodb ensure that the target * path and the existing db match before storing it. */ if (!isautodb || (!strcmp(path_array, search_field1))) { if (isautodb) /* Save the resolved path minus EXT_NEW */ strcpy(curr->key_name, name_search_field_buff); else strcpy(curr->key_name, path_array); curr->status = UNRES_KEY_RES_DB; } else continue; } } /* Do not proceed examining the current item if the file we are looking for is missing. */ if (!search_fail) { /* Next, see if the current item is a legitimate or illegitimate duplicate. */ if (UNRES_KEY_UNRES_DB != curr->status) { /* It is possible that a newly resolved realpath points to a previously seen database file, * in which case we should first check whether that database has already been inserted into * the tree with the same key to avoid inserting a duplicate. Alternatively, it may be a * duplicate device keyname, which, unlike a database one, cannot be associated with * multiple keys. */ name_length = strlen(curr->key_name); assert(name_length < GTM_PATH_MAX); if (database) node = keystore_lookup_by_keyname_plus(curr->key_name, curr->key_path, SEARCH_BY_KEYPATH); else node = keystore_lookup_by_keyname(curr->key_name); if (NULL != node) { if (!database && strcmp(node->key_path, curr->key_path)) { *error = TRUE; UPDATE_ERROR_STRING("In config file " STR_ARG ", keyname in entry #%d " "corresponding to 'files' has already been seen but specifies " "a different key", ELLIPSIZE(gc_config_filename), curr->index); return NULL; } else { /* This key is already found in our search trees, so simply remove it from * the unresolved list. */ REMOVE_UNRESOLVED_LINK(curr, prev); continue; } } } else { /* Name is unresolved; we better be searching by hash. */ assert(SEARCH_BY_HASH == search_field1_type); name_length = -1; } /* If the key name and path search criteria yield a match, proceed to decrypt the key. */ if (((NULL == name_search_field_ptr) || (!strcmp(curr->key_name, name_search_field_ptr))) && ((NULL == path_search_field_ptr) || (!strcmp(curr->key_path, path_search_field_ptr)))) { path_length = strlen(curr->key_path); node = gtmcrypt_decrypt_key(curr->key_path, path_length, curr->key_name, name_length); if (NULL == node) { *error = TRUE; return NULL; } else { /* Remove the key from the unresolved list and see if matches by its hash. */ REMOVE_UNRESOLVED_LINK(curr, prev); if ((NULL != search_field1) && (SEARCH_BY_HASH == search_field1_type) && memcmp(node->key_hash, search_field1, GTMCRYPT_HASH_LEN)) continue; return node; } } } } prev = curr; curr = curr->next; } return NULL; } /* * Helper function to decrypt a symmetric key, produce its hash, and store it for future discovery by key name, path, or hash. * * Arguments: key_path Path to the key. * path_length Length of the keypath. * key_name Name of the key. * name_length Length of the keyname. * * Returns: Pointer to the key, if created; NULL otherwise. */ STATICFNDEF gtm_keystore_t *gtmcrypt_decrypt_key(char *key_path, int path_length, char *key_name, int name_length) { gtm_keystore_t *node; unsigned char raw_key[SYMMETRIC_KEY_MAX]; int raw_key_length; int gpgerr, gpg_attempt; /* If we have seen a key with the same path, do not re-read it. */ if (NULL == (node = keystore_lookup_by_keypath(key_path))) { /* Now that we have the name of the symmetric key file, try to decrypt it. If gc_pk_get_decrypted_key returns a * non-zero status, it should have already populated the error string. */ gpg_attempt = 2; /* Retry the libgpgme decryption request once */ do { gpgerr = gc_pk_get_decrypted_key(key_path, raw_key, &raw_key_length); if (GPG_ERR_DECRYPT_FAILED == gpgerr) /* Cipher is not valid, which cannot be the case. */ gpg_attempt--; /* Assume it's a gpg bug and retry */ else gpg_attempt = 0; } while (gpg_attempt); if (0 != gpgerr) return NULL; if (0 == raw_key_length) { UPDATE_ERROR_STRING("Symmetric key " STR_ARG " found to be empty", ELLIPSIZE(key_path)); return NULL; } /* We expect a symmetric key within a certain length. */ assert(SYMMETRIC_KEY_MAX >= raw_key_length); GC_PK_COMPUTE_HASH(key_hash_array, raw_key); /* It is possible that while no key has been specified under this name, the same key has already been loaded * for a different database or device, so look up the key by hash to avoid duplicates. */ if (NULL == (node = keystore_lookup_by_hash(key_hash_array))) { /* Allocate a gtm_keystore_t element. */ node = MALLOC(SIZEOF(gtm_keystore_t)); node->cipher_head = NULL; node->db_cipher_entry = NULL; /* WARNING: Not doing a memset here because raw_key comes padded with NULLs from gc_pk_get_decrypted_key. */ memcpy(node->key, raw_key, SYMMETRIC_KEY_MAX); /* This should take care of assigning key_hash to the node itself. */ INSERT_KEY_LINK(keystore_by_hash_head, node, gtm_keystore_hash_link_t, link->key_hash, key_hash_array, GTMCRYPT_HASH_LEN, GTMCRYPT_HASH_LEN, TRUE, FALSE); } INSERT_KEY_LINK(keystore_by_keypath_head, node, gtm_keystore_keypath_link_t, link->key_path, key_path, path_length + 1, GTM_PATH_MAX, FALSE, FALSE); } if (-1 != name_length) { /* Only inserting a keyname-based link if the keyname was passed. */ INSERT_KEY_LINK(keystore_by_keyname_head, node, gtm_keystore_keyname_link_t, key_name, key_name, name_length + 1, GTM_PATH_MAX, FALSE, TRUE); } return node; } /* * Re-read the configuration file, if necessary, and store it in memory. * * Returns: 0 if succeeded re-reading the configuration file; -1 otherwise. */ STATICFNDEF int keystore_refresh(void) { int n_mappings, status, just_read; size_t envvar_len; char *config_env; struct stat stat_info; static long last_modified_s, last_modified_ns; just_read = FALSE; /* Check and update the value of gtm_passwd if it has changed since we last checked. This way, if the user had originally * entered a wrong password, but later changed the value (possible in MUMPS using external call), we read the up-to-date * value instead of issuing an error. */ if (0 != gc_update_passwd(GTM_PASSWD_ENV, >mcrypt_pwent, GTMCRYPT_DEFAULT_PASSWD_PROMPT, GTMCRYPT_OP_INTERACTIVE_MODE & gtmcrypt_init_flags)) return -1; if (CONFIG_FILE_UNREAD) { /* First, make sure we have a proper environment varible and a regular configuration file. */ if (NULL != (config_env = getenv("gtmcrypt_config"))) { if (0 == (envvar_len = strlen(config_env))) /* inline assignment */ { UPDATE_ERROR_STRING(ENV_EMPTY_ERROR, "gtmcrypt_config"); return -1; } if (GTM_PATH_MAX <= envvar_len) { UPDATE_ERROR_STRING(ENV_TOOLONG_ERROR, "gtmcrypt_config", status); return -1; } if (0 != stat(config_env, &stat_info)) { UPDATE_ERROR_STRING("Cannot stat configuration file: " STR_ARG ". %s", ELLIPSIZE(config_env), strerror(errno)); return -1; } if (!S_ISREG(stat_info.st_mode)) { UPDATE_ERROR_STRING("Configuration file " STR_ARG " is not a regular file", ELLIPSIZE(config_env)); return -1; } } else { UPDATE_ERROR_STRING(ENV_UNDEF_ERROR, "gtmcrypt_config"); return -1; } /* The gtmcrypt_config variable is defined and accessible. Copy it to a global for future references. */ memcpy(gc_config_filename, config_env, envvar_len); gc_config_filename[envvar_len] = '\0'; just_read = TRUE; } assert(!CONFIG_FILE_UNREAD); /* Stat the file if not done already, so that we can get the last modified date. */ if ((!just_read) && (0 != stat(gc_config_filename, &stat_info))) { UPDATE_ERROR_STRING("Cannot stat configuration file " STR_ARG ". %s", ELLIPSIZE(gc_config_filename), strerror(errno)); return -1; } /* If the config file has not been modified since the last time we checked, return right away. */ if ((last_modified_s > (long)stat_info.st_mtime) || ((last_modified_s == (long)stat_info.st_mtime) && (last_modified_ns >= (long)stat_info.st_nmtime))) return 0; /* File has been modified, so re-read it. */ if (!config_read_file(>mcrypt_cfg, gc_config_filename)) { UPDATE_ERROR_STRING("Cannot read config file " STR_ARG ". At line# %d - %s", ELLIPSIZE(gc_config_filename), config_error_line(>mcrypt_cfg), config_error_text(>mcrypt_cfg)) return -1; } /* Clear the entire unresolved keys list because it will be rebuilt. */ gtm_keystore_cleanup_unres_key_list(); n_keys = 0; if (-1 == (status = read_database_section(>mcrypt_cfg))) return -1; n_keys += status; if (-1 == (status = read_files_section(>mcrypt_cfg))) return -1; n_keys += status; last_modified_s = (long)stat_info.st_mtime; last_modified_ns = (long)stat_info.st_nmtime; if (0 == n_keys) { UPDATE_ERROR_STRING("Configuration file " STR_ARG " contains neither 'database.keys' section nor 'files' section, " "or both sections are empty.", ELLIPSIZE(gc_config_filename)); return -1; } return 0; } /* * Read the 'files' section of the configuration file, storing any previously unseen key in the unresolved list. * * Arguments: cfgp Pointer to the configuration object as populated by libconfig. * * Returns: 0 if successfully processed the 'files' section; -1 otherwise. */ STATICFNDEF int read_files_section(config_t *cfgp) { int i, name_length, lcl_n_maps; config_setting_t *setting, *elem; gtm_keystore_t *node; char *key_name, *key_path; if (NULL == (setting = config_lookup(cfgp, "files"))) return 0; lcl_n_maps = config_setting_length(setting); for (i = 0; i < lcl_n_maps; i++) { elem = config_setting_get_elem(setting, i); assert(NULL != elem); if (CONFIG_TYPE_STRING != config_setting_type(elem)) { UPDATE_ERROR_STRING("In config file " STR_ARG ", entry #%d corresponding to 'files' is not a string", ELLIPSIZE(gc_config_filename), i + 1); return -1; } if (NULL == (key_name = config_setting_name(elem))) { UPDATE_ERROR_STRING("In config file " STR_ARG ", entry #%d corresponding to 'files' does not have a " "key attribute", ELLIPSIZE(gc_config_filename), i + 1); return -1; } /* Length should be under GTM_PATH_MAX because that is the size of the array where the name of a key is stored. */ name_length = strlen(key_name) + 1; if (GTM_PATH_MAX <= name_length) { UPDATE_ERROR_STRING("In config file " STR_ARG ", 'files' entry #%d's field length exceeds %d", ELLIPSIZE(gc_config_filename), i + 1, GTM_PATH_MAX - 1); return -1; } if (NULL == (key_path = (char *)config_setting_get_string(elem))) { UPDATE_ERROR_STRING("In config file " STR_ARG ", cannot find the value corresponding to 'files.%s'", ELLIPSIZE(gc_config_filename), key_name); return -1; } /* Key path needs to be fully resolved before we can reliably use it, hence realpath-ing. */ if (NULL == realpath(key_path, path_array)) { UPDATE_ERROR_STRING("In config file " STR_ARG ", could not obtain the real path of 'files' " "entry #%d's key. %s", ELLIPSIZE(gc_config_filename), i + 1, strerror(errno)); return -1; } /* Duplicate names with different keys are prohibited for files, though they are allowed for databases. */ if (NULL != (node = keystore_lookup_by_keyname(key_name))) { if (strcmp(node->key_path, path_array)) { UPDATE_ERROR_STRING("In config file " STR_ARG ", keyname in entry #%d corresponding to 'files' " "has already been seen but specifies a different key", ELLIPSIZE(gc_config_filename), i + 1); return -1; } else continue; } insert_unresolved_key_link(key_name, path_array, i + 1, UNRES_KEY_FILE); } return lcl_n_maps; } /* * Process the 'database' section of the configuration file, storing any previously unseen key in the unresolved list. * * Arguments: cfgp Pointer to the configuration object as populated by libconfig. * * Returns: 0 if successfully processed the 'database' section; -1 otherwise. */ STATICFNDEF int read_database_section(config_t *cfgp) { int i, name_length, lcl_n_maps; config_setting_t *setting, *elem; gtm_keystore_t *node; char *key_name, *key_path; if (NULL == (setting = config_lookup(cfgp, "database.keys"))) return 0; lcl_n_maps = config_setting_length(setting); /* The following code makes sure that having an empty last entry in the database section is not required and does not cause * errors, as GTM-7948's original implementation would have it. */ if (lcl_n_maps > 1) { elem = config_setting_get_elem(setting, lcl_n_maps - 1); if (0 == config_setting_length(elem)) { config_setting_remove_elem(setting, lcl_n_maps - 1); lcl_n_maps--; } } for (i = 0; i < lcl_n_maps; i++) { elem = config_setting_get_elem(setting, i); assert(NULL != elem); if (!config_setting_lookup_string(elem, "dat", (const char **)&key_name)) { UPDATE_ERROR_STRING("In config file " STR_ARG ", entry #%d corresponding to " "'database.keys' does not have a 'dat' item", ELLIPSIZE(gc_config_filename), i + 1); return -1; } /* Length should be under GTM_PATH_MAX because that is the size of the array where the name of a key is stored. */ name_length = strlen(key_name) + 1 + STR_LIT_LEN(EXT_NEW); if (GTM_PATH_MAX <= name_length) { UPDATE_ERROR_STRING("In config file " STR_ARG ", in entry #%d corresponding to 'database.keys' " "file name exceeds %d", ELLIPSIZE(gc_config_filename), i + 1, (int)(GTM_PATH_MAX - STR_LIT_LEN(EXT_NEW) - 1)); return -1; } if (!config_setting_lookup_string(elem, "key", (const char **)&key_path)) { UPDATE_ERROR_STRING("In config file " STR_ARG ", entry #%d corresponding to " "'database.keys' does not have a 'key' item", ELLIPSIZE(gc_config_filename), i + 1); return -1; } /* Key path needs to be fully resolved before we can reliably use it, hence realpath-ing. */ if (NULL == realpath(key_path, path_array)) { UPDATE_ERROR_STRING("In config file " STR_ARG ", could not obtain the real path of 'database.keys' " "entry #%d's key. %s", ELLIPSIZE(gc_config_filename), i + 1, strerror(errno)); return -1; } /* Duplicate names with different keys are allowed for databases, though they are prohibited for files. */ if (NULL != (node = keystore_lookup_by_keyname_plus(key_name, path_array, SEARCH_BY_KEYPATH))) continue; insert_unresolved_key_link(key_name, path_array, i + 1, UNRES_KEY_UNRES_DB); } return lcl_n_maps; } /* * Create new encryption / decryption state object with the specified IV. * * Arguments: entry Pointer to the key structure to which the encryption / decryption state object will be assigned. * iv Initialization vector to use. * length Length of the initialization vector. * action 1 for encryption, 0 for decryption. * * Returns: 0 if successfully created a new encryption / decryption state object; -1 otherwise. */ int keystore_new_cipher_ctx(gtm_keystore_t *entry, char *iv, unsigned int length, int action) { int rv; crypt_key_t handle = NULL; gtm_cipher_ctx_t *ctx; unsigned char iv_array[GTMCRYPT_IV_LEN]; assert(GTMCRYPT_IV_LEN >= length); /* sanity check incoming length -- should never happen */ memset(iv_array, 0, GTMCRYPT_IV_LEN); memcpy(iv_array, iv, length); if (0 != (rv = gc_sym_create_cipher_handle(entry->key, iv_array, &handle, action, FALSE))) return rv; ctx = MALLOC(SIZEOF(gtm_cipher_ctx_t)); ctx->store = entry; ctx->handle = handle; memcpy(ctx->iv, iv_array, GTMCRYPT_IV_LEN); if (NULL == entry->cipher_head) { ctx->next = ctx->prev = NULL; } else { ctx->next = entry->cipher_head; ctx->prev = NULL; entry->cipher_head->prev = ctx; } entry->cipher_head = ctx; return 0; } /* * Remove an encryption / decryption state object. * * Arguments: ctx Pointer to the encryption / decryption state object to remove. * * Returns: 0 if successfully removed the cipher context; -1 otherwise. */ int keystore_remove_cipher_ctx(gtm_cipher_ctx_t *ctx) { gtm_cipher_ctx_t *next, *prev; int status; assert(NULL != ctx); status = 0; gc_sym_destroy_cipher_handle(ctx->handle); next = ctx->next; prev = ctx->prev; if (NULL != prev) prev->next = next; if (NULL != next) next->prev = prev; if (ctx->store->cipher_head == ctx) ctx->store->cipher_head = next; if (ctx->store->db_cipher_entry == ctx) ctx->store->db_cipher_entry = NULL; FREE(ctx); return status; } /* Insert a new gtm_keystore_unres_key_link_t element in the unresolved keys list. */ STATICFNDEF void insert_unresolved_key_link(char *keyname, char *keypath, int index, int status) { gtm_keystore_unres_key_link_t *node; node = (gtm_keystore_unres_key_link_t *)MALLOC(SIZEOF(gtm_keystore_unres_key_link_t)); memset(node->key_name, 0, GTM_PATH_MAX); strncpy(node->key_name, keyname, GTM_PATH_MAX - 1); /* Callers verified that the name fits in GTM_PATH_MAX */ memset(node->key_path, 0, GTM_PATH_MAX); strncpy(node->key_path, keypath, GTM_PATH_MAX - 1); /* Callers verified that the path fits in GTM_PATH_MAX */ node->next = keystore_by_unres_key_head; node->index = index; node->status = status; keystore_by_unres_key_head = node; } /* * Clean up all key and encryption / decryption state contexts. * * Returns: 0 if successfully cleaned up all encryption handle lists and trees; -1 otherwise. */ int gtm_keystore_cleanup_all(void) { int status; status = 0; if (NULL != keystore_by_hash_head) { if (-1 == gtm_keystore_cleanup_hash_tree(keystore_by_hash_head)) status = -1; keystore_by_hash_head = NULL; } if (NULL != keystore_by_keyname_head) { gtm_keystore_cleanup_keyname_tree(keystore_by_keyname_head); keystore_by_keyname_head = NULL; } if (NULL != keystore_by_keypath_head) { gtm_keystore_cleanup_keypath_tree(keystore_by_keypath_head); keystore_by_keypath_head = NULL; } gtm_keystore_cleanup_unres_key_list(); return status; } /* * Clean up a particular key object and all its encryption / decryption state objects. * * Arguments: node Key object to clean. * * Returns: 0 if successfully cleaned up the keystore node; -1 otherwise. */ STATICFNDEF int gtm_keystore_cleanup_node(gtm_keystore_t *node) { gtm_cipher_ctx_t *curr, *temp; int status; status = 0; curr = node->cipher_head; while (NULL != curr) { temp = curr->next; gc_sym_destroy_cipher_handle(curr->handle); FREE(curr); curr = temp; } memset(node->key, 0, SYMMETRIC_KEY_MAX); memset(node->key_hash, 0, GTMCRYPT_HASH_LEN); FREE(node); return status; } /* * Clean up (recursively) a binary search tree for looking up keys by their hashes. * * Arguments: entry Pointer to the node from which to descend for cleaning. * * Returns: 0 if successfully cleaned up the hash tree; -1 otherwise. */ STATICFNDEF int gtm_keystore_cleanup_hash_tree(gtm_keystore_hash_link_t *entry) { gtm_keystore_hash_link_t *curr; int status; status = 0; while (TRUE) { if (NULL != entry->left) gtm_keystore_cleanup_hash_tree(entry->left); if (-1 == gtm_keystore_cleanup_node(entry->link)) status = -1; curr = entry; if (NULL != entry->right) entry = entry->right; else break; FREE(curr); } return status; } /* * Clean up (recursively) a binary search tree for looking up keys by their paths. * * Arguments: entry Pointer to the node from which to descend for cleaning. * * Returns: 0 if successfully cleaned up the path tree; -1 otherwise. */ STATICFNDEF void gtm_keystore_cleanup_keypath_tree(gtm_keystore_keypath_link_t *entry) { gtm_keystore_keypath_link_t *curr; while (TRUE) { if (NULL != entry->left) gtm_keystore_cleanup_keypath_tree(entry->left); curr = entry; if (NULL != entry->right) entry = entry->right; else break; FREE(curr); } } /* * Clean up (recursively) a binary search tree for looking up keys by their names. * * Arguments: entry Pointer to the node from which to descend for cleaning. */ STATICFNDEF void gtm_keystore_cleanup_keyname_tree(gtm_keystore_keyname_link_t *entry) { gtm_keystore_keyname_link_t *curr; while (TRUE) { if (NULL != entry->left) gtm_keystore_cleanup_keyname_tree(entry->left); curr = entry; if (NULL != entry->right) entry = entry->right; else break; FREE(curr); } } /* * Clean up (linearly) an unresolved keys list. */ STATICFNDEF void gtm_keystore_cleanup_unres_key_list(void) { gtm_keystore_unres_key_link_t *temp, *curr; curr = keystore_by_unres_key_head; while (NULL != curr) { temp = curr; curr = curr->next; FREE(temp); } keystore_by_unres_key_head = NULL; } #ifdef GTM_CRYPT_KEYS_LOG /* Following are debugging functions for printing the state of the keystores. */ /* * Print the relevant fields of the passed-in node of one of the trees we use for looking up keys. * * Arguments: node Binary tree node to print. * type Type of the node, depending on what tree it came from. * child1 Pointer to save the pointer to the node's left child in. * child2 Pointer to save the pointer to the node's right child in. */ STATICFNDEF void print_node(void *node, int type, void **child1, void **child2) { gtm_keystore_keyname_link_t *name_node; gtm_keystore_keypath_link_t *path_node; gtm_keystore_hash_link_t *hash_node; gtm_keystore_t *keystore; gtm_cipher_ctx_t *cipher; switch (type) { case 0: /* Hash-based tree */ hash_node = (gtm_keystore_hash_link_t *)node; *child1 = (void *)hash_node->left; *child2 = (void *)hash_node->right; keystore = hash_node->link; break; case 1: /* Path-based tree */ path_node = (gtm_keystore_keypath_link_t *)node; *child1 = (void *)path_node->left; *child2 = (void *)path_node->right; keystore = path_node->link; break; case 2: /* Name-based tree */ name_node = (gtm_keystore_keyname_link_t *)node; *child1 = (void *)name_node->left; *child2 = (void *)name_node->right; keystore = name_node->link; fprintf(stderr, "Name: %s; ", name_node->key_name); break; } fprintf(stderr, "Path: %s; [", keystore->key_path); cipher = keystore->cipher_head; while (NULL != cipher) { if (cipher == keystore->db_cipher_entry) fprintf(stderr, "*"); fprintf(stderr, "X"); cipher = cipher->next; } fprintf(stderr, "]\n"); } /* * Recursively print all the nodes of the passed-in tree. * * Arguments: node Root node of the current subtree to print. * left Flag indicating whether the current root is a left child of its parent. * level Level of the current node in the entire tree. * type Type of the tree we are dealing with. */ STATICFNDEF void print_tree(void *node, int left, int level, int type) { int i; void *child1, *child2; fprintf(stderr, " "); for (i = 0; i < level; i++) { if (i == level - 1) { fprintf(stderr, left ? "|" : "`"); fprintf(stderr, "-"); } else fprintf(stderr, "| "); } if (NULL == node) fprintf(stderr, "\n"); else { print_node(node, type, &child1, &child2); print_tree(child1, 1, level + 1, type); print_tree(child2, 0, level + 1, type); } } /* * Print all nodes of the unresolved keys list. */ STATICFNDEF void print_unres_list(void) { gtm_keystore_unres_key_link_t *node; node = keystore_by_unres_key_head; while (NULL != node) { fprintf(stderr, " Name: %s; Path: %s; Index: %d; Status: %d\n", node->key_name, node->key_path, node->index, node->status); node = node->next; } } /* Print out the contents of the unresolved keys list as well as all binary trees we use for key searches. */ STATICFNDEF void print_debug(void) { fprintf(stderr, " Hash-based tree:\n"); print_tree((void *)keystore_by_hash_head, 0, 0, 0); fprintf(stderr, "\n Keypath-based tree:\n"); print_tree((void *)keystore_by_keypath_head, 0, 0, 1); fprintf(stderr, "\n Keyname-based tree:\n"); print_tree((void *)keystore_by_keyname_head, 0, 0, 2); fprintf(stderr, "\n Unresolved keys list:\n"); print_unres_list(); fprintf(stderr, "\n"); fflush(stderr); /* BYPASSOK */ } #endif fis-gtm-V7.0-005/sr_unix/gtmcrypt_dbk_ref.h0000644000032200000250000002777314342376330017557 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2021 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. * * * ****************************************************************/ #ifndef GTMCRYPT_DBK_REF_H #define GTMCRYPT_DBK_REF_H /* * This file defines several structures that store information about every symmetric key that we load as well the encryption / * decryption context for any database or device that uses that particular key. The information about the key includes its raw * content and its hash; those are stored in gtm_keystore_t->typed objects malloced invdividually. To assist lookups based on the * key's hash, name, or path, we have tree structures consisting of gtm_keystore_hash_link_t-, gtm_keystore_keyname_link_t-, and * gtm_keystore_keypath_linkt_t-typed nodes, respectively. Since we expect hashes to be unique along with the path of each key, the * hash and path information is already stored in gtm_keystore_t nodes, and the hash- and path- based trees' nodes contain no * additional information and have a one-to-one and onto relationship with the gtm_keystore_t nodes. The keyname-based tree's nodes, * on the other hand, also contain the key name information, which may correspond to the database's name or a user-chosen string in * case of device encryption. Additionally, several databases or devices may use the same key, in which case one gtm_keystore_t * element may be referenced by multiple keyname tree's nodes. An example is given below: * * keystore_by_hash_head keystore_by_keyname_head / keystore_by_keypath_head * (gtm_keystore_hash_link_t *) (gtm_keystore_t *) (gtm_keystore_keyname_link_t * / gtm_keystore_keypath_link_t *) * | ________ | * ______|_____ | | ______|_____ * | link ---|-------------> | key #1 | <-----. .------------------|--- link | * | left right | |________| \ / | left right | * |_/_______\__| \/ |_/_______\__| * / \ ________ /\ / \ * _______/____ \ | | / \ ______/_____ \ * | link ---|-------\-------------> | key #2 | <-----` `-----------|--- link | \ * | left right | \ |________| | left right | \ * |_/_______\__| \ |_/_______\__| \ * ... ... \ ________ / ... \ * _____\______ | | / _____\______ * | link ---|--> | key #3 | <-.-------------------/------------------|--- link | * | left right | |________| \ / | left right | * |_/_______\__| \ ______/_____ |_/_______\__| * ... ... `-------|--- link | ... ... * | left right | * |_/_______\__| * ... ... * * Because we do not want to decrypt every key from the configuration file just as we read it, we first store whatever information * we processed in an unresolved key list. The list is singly-linked and consists of gtm_keystore_keyname_link_t-typed elements; * each element contains information about the key name, path, index in the configuration file, and whether it is a key for a device * encryption, database whose address we already resolved, or the one that awaits resolution. (Certain database files referenced in * the configuration file may not actually exist, such as during MUPIP CREATE time or when loading an extract.) * * As for gtm_keystore_t elements, in addition to the key data they host a pointer to the head of the encryption / decryption state * list as well as to the one element of that list that is specific to database encryption. (Because database encryption does not * preserve its state beyond one block, we only ever create one database encryption and one database decryption state object, which * gets reused continuously.) Unlike devices, which can activate encryption and decryption individually, databases have to have both * encryption and decryption enabled at once. For that reason, if there is a database encryption context entry in one key's contexts * list, there is a database decryption context right after. * * The encryption / decryption contexts list is doubly-linked and consists of gtm_cipher_ctx_t-typed elements. Each element contains * the algorithm-specific (such as OpenSSL or GCRYPT) encryption or decryption state object, initialization vector (used only when * initializing the encryption / decryption state) and a pointer back to its respective key structure. An example is given below: * * ___________________________________________________ * | cipher_head db_cipher_entry | * .-->| (gtm_cipher_ctx_t *) (gtm_cipher_ctx_t *) | * | |________|_______________________________|__________| * | | | * | | (DB ENCR) ----> (DB DECR) * | | | * | ____v____ _________ ____v____ _________ * | | prev |<----|- prev |<----|- prev |<----|- prev | * | | next --|---->| next --|---->| next --|---->| next | * | | store | | store | | store | | store | * | |____|____| |____|____| |____|____| |____|____| * | | | | | * `-------------'---------------'---------------'---------------' * * For the actual implementation of the above design please refer to gtmcrypt_dbk_ref.c. */ /* Principal structure for storing key information required to perform encryption / decryption. */ typedef struct gtm_keystore_struct { unsigned char key[SYMMETRIC_KEY_MAX]; /* Raw symmetric key contents. */ unsigned char key_hash[GTMCRYPT_HASH_LEN]; /* SHA-512 hash of symmetric key. */ char key_path[GTM_PATH_MAX]; /* Path to the key file. */ struct gtm_cipher_ctx_struct *cipher_head; /* Linked list of cipher handles for * either encryption or decryption. A list * is needed because multiple devices or * databases can map to the same key, but * the internal encryption / decryption * state cannot be shared. */ struct gtm_cipher_ctx_struct *db_cipher_entry; /* Direct pointer to the (only) DB * encryption cipher entry (followed by the * DB decryption entry). */ } gtm_keystore_t; /* Structure for storing the encryption / decryption state for one device or any DB using the key pointed to by the store field. */ typedef struct gtm_cipher_ctx_struct { crypt_key_t handle; /* Encryption / decryption state. */ unsigned char iv[GTMCRYPT_IV_LEN]; /* Initialization vector. */ gtm_keystore_t *store; /* Pointer to master key object. */ struct gtm_cipher_ctx_struct *prev; /* Pointer to previous element. */ struct gtm_cipher_ctx_struct *next; /* Pointer to next element. */ } gtm_cipher_ctx_t; /* Structure to organize references to the key object by the key name, in a binary search tree fashion. */ typedef struct gtm_keystore_keyname_link_struct { gtm_keystore_t *link; /* Link to respective key object. */ char key_name[GTM_PATH_MAX]; /* Logical entity that the symmetric key * maps to. For databases it is the name of * the database file. For devices it is a * user-chosen string. */ struct gtm_keystore_keyname_link_struct *left; /* Pointer to left child. */ struct gtm_keystore_keyname_link_struct *right; /* Pointer to right child. */ } gtm_keystore_keyname_link_t; /* Structure to organize references to the key object by the key path, in a binary search tree fashion. */ typedef struct gtm_keystore_keypath_link_struct { gtm_keystore_t *link; /* Link to respective key object. */ struct gtm_keystore_keypath_link_struct *left; /* Pointer to left child. */ struct gtm_keystore_keypath_link_struct *right; /* Pointer to right child. */ } gtm_keystore_keypath_link_t; /* Structure to organize references to the key object by the key hash, in a binary search tree fashion. */ typedef struct gtm_keystore_hash_link_struct { gtm_keystore_t *link; /* Link to respective key object. */ struct gtm_keystore_hash_link_struct *left; /* Pointer to left child. */ struct gtm_keystore_hash_link_struct *right; /* Pointer to right child. */ } gtm_keystore_hash_link_t; /* Structure to temporarily store key information if the real path of the respective database file name could not be obtained. */ typedef struct gtm_keystore_unres_key_link_struct { char key_name[GTM_PATH_MAX]; /* Logical entity that the symmetric key * maps to. For databases it is the name of * the database file. For devices it is a * user-chosen string. */ char key_path[GTM_PATH_MAX]; /* Path to the key file. */ int index; /* Index in the configuration file */ int status; /* Indication of whether it is for a device, * unresolved, or resolved database. */ struct gtm_keystore_unres_key_link_struct *next; /* Pointer to next element. */ } gtm_keystore_unres_key_link_t; STATICFNDEF int keystore_refresh(void); STATICFNDEF int read_files_section(config_t *cfgp); STATICFNDEF int read_database_section(config_t *cfgp); STATICFNDEF int gtm_keystore_cleanup_node(gtm_keystore_t *); int gtm_keystore_cleanup_all(void); STATICFNDEF int gtm_keystore_cleanup_hash_tree(gtm_keystore_hash_link_t *entry); STATICFNDEF void gtm_keystore_cleanup_keyname_tree(gtm_keystore_keyname_link_t *entry); STATICFNDEF void gtm_keystore_cleanup_keypath_tree(gtm_keystore_keypath_link_t *entry); STATICFNDEF void gtm_keystore_cleanup_unres_key_list(void); int gtmcrypt_getkey_by_keyname(char *keyname, char *keypath, gtm_keystore_t **entry, int database); int gtmcrypt_getkey_by_hash(unsigned char *hash, char *dbpath, gtm_keystore_t **entry); STATICFNDEF gtm_keystore_t *gtmcrypt_decrypt_key(char *key_path, int path_length, char *key_name, int name_length); STATICFNDEF void insert_unresolved_key_link(char *keyname, char *keypath, int index, int status); STATICFNDEF gtm_keystore_t *keystore_lookup_by_hash(unsigned char *hash); STATICFNDEF gtm_keystore_t *keystore_lookup_by_keyname(char *keyname); STATICFNDEF gtm_keystore_t *keystore_lookup_by_keyname_plus(char *keyname, char *search_field, int search_type); STATICFNDEF gtm_keystore_t *keystore_lookup_by_keypath(char *keypath); STATICFNDEF gtm_keystore_t *keystore_lookup_by_unres_key(char *search_field1, int search_field1_type, char *search_field2, int search_field2_type, int database, int *error); int keystore_new_cipher_ctx(gtm_keystore_t *entry, char *iv, unsigned int length, int action); int keystore_remove_cipher_ctx(gtm_cipher_ctx_t *ctx); STATICFNDEF void print_debug(void); #endif /* GTMCRYPT_DBK_REF_H */ fis-gtm-V7.0-005/sr_unix/gtmcrypt_entry.c0000755000032200000250000001641514342376330017311 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2021 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 #include #ifdef _AIX #include #endif #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_limits.h" /* for GTM_PATH_MAX */ #include "lv_val.h" /* needed for "fgncal.h" */ #include "real_len.h" #include "fgncal.h" /* needed for COPY_DLLERR_MSG() */ #include "gtmmsg.h" #include "iosp.h" /* for SS_NORMAL */ #include "trans_log_name.h" #include "gdsroot.h" #include "is_file_identical.h" #define GTMCRYPT_LIBNAME "libgtmcrypt.so" #define MAX_GTMCRYPT_PLUGIN_STR_LEN (SIZEOF(GTMCRYPT_LIBNAME) * 4) #define GTM_CRYPT_PLUGIN "$gtm_crypt_plugin" typedef void (*gtmcrypt_func_t)(); /* A generic pointer type to the gtmcrypt functions exposed by the plugin */ #define GTMCRYPT_DEF(x) x##_, enum { #include "gtmcrypt_funclist.h" /* BYPASSOK */ gtmcrypt_func_n /* total number of gtmcrypt functions that needs to be dlsym()ed */ }; #undef GTMCRYPT_DEF #define GTMCRYPT_DEF(x) GBLDEF gtmcrypt_func_t x##_fnptr; #include "gtmcrypt_funclist.h" #undef GTMCRYPT_DEF GBLREF char dl_err[MAX_ERRSTR_LEN]; GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF boolean_t gtm_dist_ok_to_use; error_def(ERR_CRYPTDLNOOPEN); /* Including gtmcrypt.h in this module results in conflicting GBLDEF/GBLREFs. So, re-define the function prototype here to * silent the compiler. */ int4 gtmcrypt_entry(void); boolean_t verify_lib_loadpath(const char *libname, char *loadpath); #ifdef _AIX /* On AIX, there is no known way to specify that dependent libraries (in this case "libgtmcryptutil.so") should also be searched in * the same directory from which the parent library is loaded ($ORIGIN on Linux, HP-UX and Solaris). To work-around that, we * explicitly prefix LIBPATH with "$gtm_dist/plugin" before invoking dlopen. But, to ensure that "libgtmcryptutil.so" was indeed * loaded from "$gtm_dist/plugin", we use loadquery to get the list of loaded modules along with the path from which they are loaded * from and verify against it. */ boolean_t verify_lib_loadpath(const char *libname, char *loadpath) { struct ld_xinfo *ldxinfo; char *bufptr, *ptr, cmpptr[GTM_PATH_MAX], buf[GTM_PATH_MAX]; int ret, cmplen, buflen, save_errno; buflen = GTM_PATH_MAX; bufptr = &buf[0]; while (-1 == loadquery(L_GETXINFO, bufptr, buflen)) { save_errno = errno; if (ENOMEM != save_errno) { assert(FALSE); SNPRINTF(dl_err, MAX_ERRSTR_LEN, "System call `loadquery' failed. %s", STRERROR(save_errno)); return FALSE; } if (bufptr != &buf[0]) free(bufptr); buflen *= 2; bufptr = malloc(buflen); } ldxinfo = (struct ld_xinfo *)bufptr; ret = FALSE; SNPRINTF(cmpptr, GTM_PATH_MAX, "%s/%s", loadpath, libname); while (ldxinfo->ldinfo_next) { /* Point to the offset at which the path of the loaded module is present. */ ptr = (char *)ldxinfo + ldxinfo->ldinfo_filename; if (is_file_identical(cmpptr, ptr)) { ret = TRUE; break; } ldxinfo = (struct ld_xinfo *)((sm_long_t)ldxinfo + ldxinfo->ldinfo_next); } if (bufptr != &buf[0]) free(bufptr); if (!ret) SNPRINTF(dl_err, MAX_ERRSTR_LEN, "Dependent shared library %s was not loaded relative to %s.", libname, loadpath); return ret; } #endif int4 gtmcrypt_entry() { /* Initialize the table of symbol names to be used in dlsym() */ # define GTMCRYPT_DEF(x) #x, char *gtmcrypt_fname[] = { # include "gtmcrypt_funclist.h" NULL }; # undef GTMCRYPT_DEF /* Initialize the table of locations of function pointers that are set by dlsym() */ # define GTMCRYPT_DEF(x) &x##_fnptr, gtmcrypt_func_t *gtmcrypt_fnptr[] = { # include "gtmcrypt_funclist.h" NULL }; # undef GTMCRYPT_DEF void_ptr_t handle; char_ptr_t err_str, libname_ptr; gtmcrypt_func_t fptr; int findx, plugin_dir_len, save_errno; # ifdef _AIX char new_libpath_env[GTM_PATH_MAX], *save_libpath_ptr, save_libpath[GTM_PATH_MAX]; # endif char libpath[GTM_PATH_MAX], buf[MAX_GTMCRYPT_PLUGIN_STR_LEN], plugin_dir_path[GTM_PATH_MAX]; char resolved_libpath[GTM_PATH_MAX], resolved_plugin_dir_path[GTM_PATH_MAX]; mstr trans, env_var = {0, LEN_AND_LIT(GTM_CRYPT_PLUGIN)}; if(!gtm_dist_ok_to_use) { SNPRINTF(dl_err, MAX_ERRSTR_LEN, "%%GTM-E-GTMDISTUNVERIF, Environment variable $gtm_dist (%s) " "could not be verified against the executables path", gtm_dist); return ERR_CRYPTDLNOOPEN; } SNPRINTF(plugin_dir_path, GTM_PATH_MAX, "%s/%s", gtm_dist, GTMCRYPT_PLUGIN_DIR_NAME); if (NULL == realpath(plugin_dir_path, resolved_plugin_dir_path)) { save_errno = errno; SNPRINTF(dl_err, MAX_ERRSTR_LEN, "Failed to find symbolic link for %s. %s", plugin_dir_path, STRERROR(save_errno)); return ERR_CRYPTDLNOOPEN; } plugin_dir_len = STRLEN(resolved_plugin_dir_path); if ((SS_NORMAL != TRANS_LOG_NAME(&env_var, &trans, buf, SIZEOF(buf), do_sendmsg_on_log2long)) || (0 >= trans.len)) { /* Either $gtm_crypt_plugin is not defined in the environment variable OR it is set to null-string. Fall-back to * using libgtmcrypt.so */ libname_ptr = GTMCRYPT_LIBNAME; } else libname_ptr = &buf[0]; /* value of $gtm_crypt_plugin */ SNPRINTF(libpath, GTM_PATH_MAX, "%s/%s", plugin_dir_path, libname_ptr); if (NULL == realpath(libpath, resolved_libpath)) { save_errno = errno; SNPRINTF(dl_err, MAX_ERRSTR_LEN, "Failed to find symbolic link for %s. %s", libpath, STRERROR(save_errno)); return ERR_CRYPTDLNOOPEN; } /* Symbolic link found. dlopen resolved_libpath */ if (0 != memcmp(resolved_libpath, resolved_plugin_dir_path, plugin_dir_len)) { /* resolved_path doesn't contain $gtm_dist/plugin as the prefix */ SNPRINTF(dl_err, MAX_ERRSTR_LEN, "Resolved path for %s must be relative to the resolved path for %s", libpath, plugin_dir_path); return ERR_CRYPTDLNOOPEN; } # ifdef _AIX if (NULL == (save_libpath_ptr = getenv(LIBPATH_ENV))) SNPRINTF(new_libpath_env, GTM_PATH_MAX, "%s", plugin_dir_path); else { /* Since the setenv below can potentially thrash the save_libpath_ptr, take a copy of it for later restore. */ strncpy(save_libpath, save_libpath_ptr, SIZEOF(save_libpath)); save_libpath[SIZEOF(save_libpath) - 1] = '\0'; SNPRINTF(new_libpath_env, GTM_PATH_MAX, "%s:%s", plugin_dir_path, save_libpath_ptr); } setenv(LIBPATH_ENV, new_libpath_env, TRUE); # endif handle = dlopen(&resolved_libpath[0], RTLD_NOW | RTLD_GLOBAL); if (NULL == handle) { COPY_DLLERR_MSG(err_str, dl_err); return ERR_CRYPTDLNOOPEN; } # ifdef _AIX if (NULL == save_libpath_ptr) unsetenv(LIBPATH_ENV); else setenv(LIBPATH_ENV, save_libpath, TRUE); if (!verify_lib_loadpath(GTMCRYPT_UTIL_LIBNAME, plugin_dir_path)) return ERR_CRYPTDLNOOPEN; # endif for (findx = 0; findx < gtmcrypt_func_n; ++findx) { fptr = (gtmcrypt_func_t)dlsym(handle, gtmcrypt_fname[findx]); if (NULL == fptr) { COPY_DLLERR_MSG(err_str, dl_err); return ERR_CRYPTDLNOOPEN; } *gtmcrypt_fnptr[findx] = fptr; } return 0; } fis-gtm-V7.0-005/sr_unix/gtmcrypt_funclist.h0000644000032200000250000000231014342376330017766 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2016 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. * * * ****************************************************************/ /* Any time a new function gets added to the GT.M encryption interface, add the corresponding entry here. This file is included in * 'gtmcrypt_entry' by appropriately defining GTMCRYPT_DEF, to generate necessary string literals and function pointers which are * used to 'dlsym' symbols from the GT.M encryption shared library. */ GTMCRYPT_DEF(gtmcrypt_init) GTMCRYPT_DEF(gtmcrypt_strerror) GTMCRYPT_DEF(gtmcrypt_init_db_cipher_context_by_hash) GTMCRYPT_DEF(gtmcrypt_init_device_cipher_context_by_keyname) GTMCRYPT_DEF(gtmcrypt_obtain_db_key_hash_by_keyname) GTMCRYPT_DEF(gtmcrypt_release_cipher_context) GTMCRYPT_DEF(gtmcrypt_encrypt_decrypt) GTMCRYPT_DEF(gtmcrypt_same_key) GTMCRYPT_DEF(gtmcrypt_close) fis-gtm-V7.0-005/sr_unix/gtmcrypt_interface.h0000644000032200000250000002202014342376330020077 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2020 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 * * * ****************************************************************/ /* WARNING: Include gtm_limits.h prior to including this file. */ #ifndef GTMCRYPT_INTERFACE_H #define GTMCRYPT_INTERFACE_H /* This module defines the interface for a GT.M encryption plug-in implementation. Refer to the function comments for details. */ /* Environment variable containing the obfuscated password for the key ring with which the symmetric keys are encrypted. */ #define GTM_PASSWD_ENV "gtm_passwd" /* Length of the plain and in-hex hash string for a symmetric key. We are currently using SHA-512, so the length is 64 bytes. */ #define GTMCRYPT_HASH_LEN 64 #define GTMCRYPT_HASH_HEX_LEN GTMCRYPT_HASH_LEN * 2 /* Length of the key name (which for databases is an absolute path to the file). */ #define GTMCRYPT_MAX_KEYNAME_LEN GTM_PATH_MAX /* Definitions must match those in gtm_tls_interface.h */ /* Flag to be used whenever password can be obtained interactively. */ #define GTMCRYPT_OP_INTERACTIVE_MODE 0x00000001 /* No environment variable for password - used by gc_update_passwd so must be same in gtmcrypt_interface.h */ #define GTMCRYPT_OP_NOPWDENVVAR 0x00000800 /* Special value that indicates invalid / uninitialized encryption state object. */ #define GTMCRYPT_INVALID_KEY_HANDLE NULL /* Constants to indicate what IV-handling mode is desired for a particular encryption or decryption operation. */ #define GTMCRYPT_IV_CONTINUE 0 #define GTMCRYPT_IV_SET 1 #define GTMCRYPT_IV_RESET -1 /* Constants to indicate whether a particular operation is of encryption or decryption type. */ #define GTMCRYPT_OP_ENCRYPT 1 #define GTMCRYPT_OP_DECRYPT 0 typedef void * gtmcrypt_key_t; /* * Initialize encryption if not yet initialized. Use this function to load necessary libraries and set appropriate configuration * options. Upon a successful return this function is never invoked again. * * Arguments: flags Encryption flags to use. * * Returns: 0 if encryption was initialized successfully; -1 otherwise. */ extern gtm_status_t gtmcrypt_init(gtm_int_t flags); /* * Return the error string. Use this function to provide the current error status. The function is normally invoked following a * non-zero return from one of the other functions defined in the interface, which means that each of them should start by clearing * the error buffer. * * Returns: The error string constructed so far. */ extern gtm_char_t *gtmcrypt_strerror(void); /* * Find the key by hash and database path and set up database encryption and decryption state objects, if not created yet. Use this * function to locate a particular key by its hash and, if found, initialize the objects for subsequent encryption and decryption * operations on any database that will use this key, unless already initialized. If the db_path argument specifies a non-null * string, then the key should additionally correspond to that database in the configuration file. * * The reason that any database relying on the same key may use the same encryption and decryption state objects is this: Every * database's encryption and decryption handles are initialized with a null IV, and every block is processed using either a null IV * or IV corresponding to the block number. So, for every encryption and decryption operation the IV is always preset to the * "correct" value, effectively making it suitable for every database using the same hash. * * Arguments: handle Pointer to the database encryption state object supplied by the caller and filled in by this * routine. * key_hash Hash of the key. * db_path Path to the database file that should be associated with the sought key. Can be an empty string. * iv Initialization vector to use for encryption or decryption. * * Returns: 0 if the routine found the key, and either found existing database encryption and decryption state objects or * initialized them; -1 otherwise. */ extern gtm_status_t gtmcrypt_init_db_cipher_context_by_hash(gtmcrypt_key_t *handle, gtm_string_t key_hash, gtm_string_t db_path, gtm_string_t iv); /* * Find the key by its name and set up device encryption or decryption state object. Use this function to locate a particular key by * its name (as specified in the configuration file) and, if found, initialize an object for subsequent encryption or decryption * operations (depending on the 'encrypt' parameter) with one device using this key. Note that, unlike databases, different devices * relying on the same key require individual encryption and decryption state objects as their states evolve with each encryption or * decryption operation. * * Arguments: handle Pointer to the database encryption state object supplied by the caller and filled in by this * routine. * key_name Name of the key. * iv Initialization vector to use for encryption or decryption. * operation Flag indicating whether encryption or decryption is desired; use GTMCRYPT_OP_ENCRYPT or * GTMCRYPT_OP_DECRYPT, respectively. * * Returns: 0 if the routine found the key, and either found existing database encryption and decryption state objects or * initialized them; -1 otherwise. */ extern gtm_status_t gtmcrypt_init_device_cipher_context_by_keyname(gtmcrypt_key_t *handle, gtm_string_t key_name, gtm_string_t iv, gtm_int_t operation); /* * Find the key by the path of the database it corresponds to as well as its own path, and obtain its hash. Use this function to * locate a particular key by the path of the database that is associated with the key in the configuration file and calculate (or * copy, if precalculated) its hash to the 'hash_dest' address. If the key_path argument specifies a non-null string, then the key * should have the corresponding path; otherwise, the *last* of all keys associated with the specified database in the configuration * file is used. * * Arguments: db_path Path to the database file that should be associated with the sought key. * key_path Path to the key. Can be an empty string. * hash_dest Pointer to the location for this routine to copy the key's hash. * * Returns: 0 if the routine found the key and copied its hash to the specified location; -1 otherwise. */ extern gtm_status_t gtmcrypt_obtain_db_key_hash_by_keyname(gtm_string_t db_path, gtm_string_t key_path, gtm_string_t *hash_dest); /* * Release the specified encryption or decryption state object, also releasing the decryption state if database encryption state is * specified. * * Arguments: handle Pointer to the encryption or decryption state object to release. * * Returns: 0 if the operation succeeded; -1 otherwise. */ extern gtm_status_t gtmcrypt_release_cipher_context(gtmcrypt_key_t handle); /* * Perform encryption or decryption of the provided data based on the specified encryption / decryption state. If the target buffer * pointer is NULL, the operation is done in-place. It is also possible to set the initialization vector (IV) to a particular value, * or reset it to the original value, before attempting the operation. Note that the changes are persistent. * * Arguments: handle Encryption state object. * unencr_block Block of unencrypted data. * unencr_block_len Length of the unencrypted and encrypted data blocks. * encr_block Block of encrypted data. * operation Flag indicating whether to perform encryption or decryption; use GTMCRYPT_OP_ENCRYPT or * GTMCRYPT_OP_DECRYPT, respectively. * iv_mode Flag indicating whether to change the initialization vector (IV) prior to the operation; use * GTMCRYPT_IV_CONTINUE to proceed without changing the IV, GTMCRYPT_IV_SET to set the IV the * value supplied in the iv argument, and GTMCRYPT_IV_RESET to reset the IV to the value * specified at initialization. * iv Initialization vector for the encryption state to take when iv_mode is GTMCRYPT_IV_SET. * * Returns: 0 if the operation succeeded; -1 otherwise. */ extern gtm_status_t gtmcrypt_encrypt_decrypt(gtmcrypt_key_t handle, gtm_char_t *src_block, gtm_int_t src_block_len, gtm_char_t *dest_block, gtm_int_t operation, gtm_int_t iv_mode, gtm_string_t iv); /* * Compare the keys associated with two encryption or decryption state objects. * * Arguments: handle1 First encryption or decryption state object. * handle2 Second encryption or decryption state object. * * Returns: 1 if both encryption or decryption state objects use the same key; 0 otherwise. */ extern gtm_int_t gtmcrypt_same_key(gtmcrypt_key_t handle1, gtmcrypt_key_t handle2); /* * Disable encryption and discard any sensitive data in memory. * * Returns: 0 if the operation succeeded; -1 otherwise. */ extern gtm_status_t gtmcrypt_close(void); #endif fis-gtm-V7.0-005/sr_unix/gtmcrypt_pk_ref.c0000644000032200000250000001674714342376330017423 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2021 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. * * * ****************************************************************/ #define _FILE_OFFSET_BITS 64 /* Needed to compile gpgme client progs also with large file support */ #include #include #include #include #include #include #include #include #include #include #include /* BYPASSOK -- see above */ #include #include /* gpgme functions */ #include /* gcry*_err_t */ #include #include "gtmxc_types.h" /* xc_string, xc_status_t and other callin interfaces xc_fileid */ #include "gtmcrypt_util.h" #include "gtmcrypt_interface.h" /* Function prototypes for gtmcrypt*.* functions */ #include "gtmcrypt_ref.h" #include "gtmcrypt_dbk_ref.h" #include "gtmcrypt_sym_ref.h" #include "gtmcrypt_pk_ref.h" GBLDEF passwd_entry_t *gtmcrypt_pwent; GBLDEF gpgme_ctx_t pk_crypt_ctx; void gc_pk_scrub_passwd() { /* Nullify the key strings, so that any generated cores will not contain the unencrypted keys */ gc_freeup_pwent(gtmcrypt_pwent); /* Finally release the gpgme context */ if (NULL != pk_crypt_ctx) gpgme_release(pk_crypt_ctx); } /* This function is called whenever gpg needs the passphrase with which the secret key is encrypted. In this case, the passphrase * is obtained from the ENVIRONMENT VARIABLE - $gtm_passwd or by invoking the mumps engine during the "gtmcrypt_init()". * In either ways, it's guaranteed that when this function is called, the passphrase is already set in the global variable. */ int gc_pk_crypt_passphrase_callback(void *opaque, const char *uid_hint, const char *passphrase_info, int last_was_bad, int fd) { int write_ret, len; assert(0 != fd); assert(NULL != gtmcrypt_pwent->passwd); len = STRLEN(gtmcrypt_pwent->passwd); write_ret = write(fd, gtmcrypt_pwent->passwd, len); if (len == write_ret) { write_ret = write(fd, "\n", 1); if (1 == write_ret) return 0; } /* Problem with one of the writes so let gpgme know */ return -1; } /* Given the structure that holds the plain data, this function reads through the structure and retrieves the plain text. We * also return the number of bytes actually read from the structure. */ int gc_pk_crypt_retrieve_plain_text(gpgme_data_t plain_data, unsigned char *plain_text) { int ret; assert(NULL != plain_text); /* Clear the temporary buffer */ memset(plain_text, 0, SYMMETRIC_KEY_MAX); gpgme_data_seek(plain_data, 0, SEEK_SET); ret = (int)gpgme_data_read(plain_data, plain_text, SYMMETRIC_KEY_MAX); assert(ret || (NULL != getenv("gtm_white_box_test_case_enable"))); /* || needed for "encryption/key_file_enc" subtest */ return ret; } /* This is currently necessary to work around what seems to be a gpgme issue in not clearing the plaintext keys * from the C stack (shows up in a core dump otherwise). When gpgme is fixed, this code can be removed. * The size of lclarray (8K) is determined purely from experimentation on all platforms. */ int gc_pk_scrub_plaintext_keys_from_c_stack() { char lclarray[8192]; memset(lclarray, 0, SIZEOF(lclarray)); return 0; } /* This function tries to decrypt the cipher file (the file containing the symmetric key with which the database is encrypted). * It's assumed that the context is initialized and is set with the appropriate passphrase callback. The cipher_file * should contain the fully qualified path of the encrypted database key file. Also, plain text is supposed to be allocated with * sufficient space to hold the decrypted text. */ gpgme_error_t gc_pk_get_decrypted_key(const char *cipher_file, unsigned char *plain_text, int *plain_text_length) { gpgme_error_t err; gpgme_data_t cipher_data = NULL, plain_data = NULL; gpg_err_code_t ecode; char null_buffer[SYMMETRIC_KEY_MAX]; assert(NULL != cipher_file); assert(NULL != plain_text); assert(NULL != pk_crypt_ctx); assert(0 != STRLEN(cipher_file)); /* Convert the cipher content in the cipher file into in-memory content. * This in-memory content is stored in gpgme_data_t structure. */ err = gpgme_data_new_from_file(&cipher_data, cipher_file, 1); if (!err) { err = gpgme_data_new(&plain_data); if (!err) { /* Try decrypting the cipher content with the context. * The decrypted content will also be stored in gpgme_data_t structure. */ err = gpgme_op_decrypt(pk_crypt_ctx, cipher_data, plain_data); if (!err) /* Once decrypted, the plain text has to be obtained from the plain_data structure. */ *plain_text_length = gc_pk_crypt_retrieve_plain_text(plain_data, plain_text); gc_pk_scrub_plaintext_keys_from_c_stack(); } } ecode = gpgme_err_code(err); if (0 != ecode) { switch (ecode) { case GPG_ERR_BAD_PASSPHRASE: UPDATE_ERROR_STRING("Incorrect password or error while obtaining password"); break; case GPG_ERR_ENOENT: UPDATE_ERROR_STRING("Encryption key file " STR_ARG " not found", ELLIPSIZE(cipher_file)); break; case GPG_ERR_EACCES: UPDATE_ERROR_STRING("Encryption key file " STR_ARG " not accessible", ELLIPSIZE(cipher_file)); break; case GPG_ERR_ENAMETOOLONG: UPDATE_ERROR_STRING("Path, or a component of the path, to encryption key file " STR_ARG " is too long", ELLIPSIZE(cipher_file)); break; default: UPDATE_ERROR_STRING("Error while accessing key file " STR_ARG ": %s", ELLIPSIZE(cipher_file), gpgme_strerror(err)); break; } } else { assert((0 == plain_text_length) || (NULL != plain_data)); } if (NULL != plain_data) { /* Scrub plaintext data before releasing it */ assert(SYMMETRIC_KEY_MAX == SIZEOF(null_buffer)); memset(null_buffer, 0, SYMMETRIC_KEY_MAX); assert((0 != ecode) || (0 != plain_text_length) || memcmp(plain_text, null_buffer, SYMMETRIC_KEY_MAX)); gpgme_data_write(plain_data, null_buffer, SYMMETRIC_KEY_MAX); gpgme_data_release(plain_data); } if (NULL != cipher_data) gpgme_data_release(cipher_data); return ecode; } int gc_pk_gpghome_has_permissions() { char pathname[GTM_PATH_MAX], *ptr; int gnupghome_set, perms; size_t pathlen; /* See if GNUPGHOME is set in the environment */ if (!(ptr = getenv(GNUPGHOME))) { /* $GNUPGHOME is not set, use $HOME/.gnupg as the GPG home directory */ gnupghome_set = FALSE; if (!(ptr = getenv(HOME))) { UPDATE_ERROR_STRING(ENV_UNDEF_ERROR, HOME); return -1; } SNPRINTF(pathname, GTM_PATH_MAX, "%s/%s", ptr, DOT_GNUPG); } else { pathlen = strlen(ptr); if (0 == pathlen) { UPDATE_ERROR_STRING(ENV_EMPTY_ERROR, GNUPGHOME); return -1; } if (GTM_PATH_MAX <= pathlen) { UPDATE_ERROR_STRING("$GNUPGHOME is too long -" STR_ARG, ELLIPSIZE(pathname)); return -1; } gnupghome_set = TRUE; memcpy(pathname, ptr, pathlen); pathname[pathlen] = '\0'; } if (-1 != (perms = access(pathname, R_OK | X_OK))) return 0; else if (EACCES == errno) { if (gnupghome_set) { UPDATE_ERROR_STRING("No read permissions on $%s", GNUPGHOME); } else UPDATE_ERROR_STRING("No read permissions on $%s/%s", HOME, DOT_GNUPG); } else /* Some other error */ UPDATE_ERROR_STRING("Cannot stat on " STR_ARG " - %d", ELLIPSIZE(pathname), errno); return -1; } fis-gtm-V7.0-005/sr_unix/gtmcrypt_pk_ref.h0000644000032200000250000001354014342376330017414 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2017 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. * * * ****************************************************************/ #ifndef GTMCRYPT_PK_REF_H #define GTMCRYPT_PK_REF_H int gc_pk_mask_unmask_passwd(char *in, char *out, int len); int gc_pk_mask_unmask_passwd_interlude(int nparm, gtm_string_t *in, gtm_string_t *out, int len); void gc_pk_scrub_passwd(); void gc_pk_crypt_load_gtmci_env(); xc_status_t gc_pk_update_passwd(); int gc_pk_crypt_passphrase_callback(void *opaque, const char *uid_hint, const char *passphrase_info, int last_was_bad, int fd); int gc_pk_crypt_retrieve_plain_text(gpgme_data_t plain_data, unsigned char *plain_text); gpgme_error_t gc_pk_get_decrypted_key(const char *cipher_file, unsigned char *plain_text, int *plain_text_length); int gc_pk_mask_unmask_passwd(char *in, char *out, int len); void gc_pk_scrub_passwd(void); void gc_pk_crypt_load_gtmci_env(void); int gc_pk_scrub_plaintext_keys_from_c_stack(void); int gc_pk_gpghome_has_permissions(void); #ifdef USE_GPGME_PINENTRY_MODE_LOOPBACK /* Tell GPGME to set the pinentry mode to LOOPBACK. Note that turning this on implies that the * installed gpg/gpg2 supports the command line argument. If it does not, gpg will error out * leading to confusion. Rely on the Makefile to detect GPG 2.1.12+ under the assumption that * an upgrade will not break this option. * We could use gpgme_set_global_flag("require-gnupg","2.1.0"), but that causes a failure in * GPGME where we want to operate in a compatible mode. * Attempting to use the GPGME library version does not work because distributions have * packaged GPGME versions ahead of GnuPG binaries (this was true at least on Debian). * https://lists.gt.net/gnupg/devel/72103#72103 */ #define GPG_MAJOR_PINENTRY_LOOPBACK 2 #define GPG_SEPERATOR_PINENTRY_LOOPBACK '.' #define GPG_MINOR_PINENTRY_LOOPBACK 1 #define GPG_SUBMINOR_PINENTRY_LOOPBACK 12 #define GC_PK_INIT_PINENTRY_MODE \ { \ gpgme_engine_info_t info; \ int i, len, major, minor, sub; \ \ GC_PK_INIT_DO_OR_ERR(gpgme_get_engine_info(&info)); \ /* Find the right protocol entry for the version check */ \ while (info && (info->protocol != GPGME_PROTOCOL_OpenPGP)) \ info = info->next; \ /* Check the version before using */ \ if (info && info->version) \ { \ len = strlen(info->version); \ for (major = i = 0; (i <= len) && (('0' < info->version[i]) && ('9' >= info->version[i])); i++) \ major = (major * 10) + (info->version[i] - '0'); \ if (GPG_SEPERATOR_PINENTRY_LOOPBACK == info->version[i]) i++; \ for (minor = 0; (i <= len) && (('0' < info->version[i]) && ('9' >= info->version[i])); i++) \ minor = (minor * 10) + (info->version[i] - '0'); \ if (GPG_SEPERATOR_PINENTRY_LOOPBACK == info->version[i]) i++; \ for (sub = 0; (i <= len) && (('0' < info->version[i]) && ('9' >= info->version[i])); i++) \ sub = (sub * 10) + (info->version[i] - '0'); \ if ((GPG_MAJOR_PINENTRY_LOOPBACK < major) || \ ((GPG_MAJOR_PINENTRY_LOOPBACK == major) && \ ((GPG_MINOR_PINENTRY_LOOPBACK < minor) || \ ((GPG_MINOR_PINENTRY_LOOPBACK == minor) && (GPG_SUBMINOR_PINENTRY_LOOPBACK <= sub))))) \ { \ GC_PK_INIT_DO_OR_ERR(gpgme_set_pinentry_mode(pk_crypt_ctx, GPGME_PINENTRY_MODE_LOOPBACK)); \ } \ } \ } #else #define GC_PK_INIT_PINENTRY_MODE #endif #define GC_PK_INIT_DO_OR_ERR(LINE) \ { \ err = LINE; \ if (err) \ { \ pk_crypt_ctx = NULL; \ UPDATE_ERROR_STRING("Error initializing GpgME: %s/%s", gpgme_strsource(err), gpgme_strerror(err)); \ return -1; \ } \ } /* Public key cryptography related macros */ #define GC_PK_INIT \ { \ gpgme_error_t err; \ GBLREF gpgme_ctx_t pk_crypt_ctx; \ \ gpgme_check_version(NULL); /* This initializes the gpgme engine. */ \ GC_PK_INIT_DO_OR_ERR(gpgme_new(&pk_crypt_ctx)); \ GC_PK_INIT_DO_OR_ERR(gpgme_set_protocol(pk_crypt_ctx, GPGME_PROTOCOL_OpenPGP)); \ GC_PK_INIT_PINENTRY_MODE; \ /* Attempt to set a callback to retrieve the password. Works with GPG classic and GPG 2.1+ */ \ gpgme_set_passphrase_cb(pk_crypt_ctx, \ (gpgme_passphrase_cb_t) gc_pk_crypt_passphrase_callback, NULL); \ } #define GC_PK_APPEND_UNIQ_STRING(in_buff, symmetric_key) \ { \ memcpy(in_buff, symmetric_key, SYMMETRIC_KEY_MAX); \ memcpy(in_buff + SYMMETRIC_KEY_MAX, UNIQ_ENC_PARAM_STRING, UNIQ_ENC_PARAM_LEN); \ } #ifdef USE_OPENSSL #define GC_PK_COMPUTE_HASH(symmetric_key_hash, symmetric_key) \ { \ unsigned char in_buff[HASH_INPUT_BUFF_LEN]; \ \ GC_PK_APPEND_UNIQ_STRING(in_buff, symmetric_key); \ EVP_Digest(in_buff, HASH_INPUT_BUFF_LEN, symmetric_key_hash, NULL, EVP_sha512(), NULL); \ memset(in_buff, 0, HASH_INPUT_BUFF_LEN); \ } #else #define GC_PK_COMPUTE_HASH(symmetric_key_hash, symmetric_key) \ { \ unsigned char in_buff[HASH_INPUT_BUFF_LEN]; \ \ GC_PK_APPEND_UNIQ_STRING(in_buff, symmetric_key); \ gcry_md_hash_buffer(GCRY_MD_SHA512, symmetric_key_hash, in_buff, HASH_INPUT_BUFF_LEN); \ memset(in_buff, 0, HASH_INPUT_BUFF_LEN); \ } #endif #endif /* GTMCRYPT_PK_REF_H */ fis-gtm-V7.0-005/sr_unix/gtmcrypt_ref.c0000644000032200000250000003504714342376330016723 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2021 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. * * * ****************************************************************/ #define _FILE_OFFSET_BITS 64 /* Needed to compile gpgme client progs also with large file support */ #include #include #include #include #include #include #include #include #include #include /* gpgme functions */ #include /* gcry*_err_t */ #include #include #include "gtmxc_types.h" /* gtm_string, gtm_status_t and other callin interfaces gtm_fileid */ #include "gtmcrypt_util.h" #include "gtmcrypt_interface.h" /* Function prototypes for gtmcrypt*.* functions */ #include "gtmcrypt_ref.h" #include "gtmcrypt_dbk_ref.h" #include "gtmcrypt_sym_ref.h" #include "gtmcrypt_pk_ref.h" #ifdef __MVS__ #define GTMSHR_IMAGENAME "libgtmshr.dll" #endif #define CHECK_IV_LENGTH(IV) \ { \ if ((GTMCRYPT_IV_LEN < IV.length) || (0 > IV.length)) \ { \ UPDATE_ERROR_STRING("Specified IVEC has length %ld, which is greater than " \ "the maximum allowed IVEC length %d", IV.length, GTMCRYPT_IV_LEN); \ return -1; \ } \ } #ifdef GTM_CRYPT_KEYS_LOG # define PRINT_KEYSTORE_STATE(MESSAGE, ...) \ { \ fprintf(stderr, "----------------------------------\n" MESSAGE \ "----------------------------------\n", __VA_ARGS__); \ print_debug(); \ } #else # define PRINT_KEYSTORE_STATE(MESSAGE, ...) #endif GBLDEF int gtmcrypt_inited; GBLDEF int gtmcrypt_init_flags; GBLREF passwd_entry_t *gtmcrypt_pwent; /* * Initialize encryption if not yet initialized. * * Arguments: flags Encryption flags to use. * * Returns: 0 if encryption was initialized successfully; -1 otherwise. */ gtm_status_t gtmcrypt_init(gtm_int_t flags) { int fips_requested, fips_enabled, rv; if (gtmcrypt_inited) return 0; if (0 != gc_load_gtmshr_symbols()) return -1; # ifdef USE_GCRYPT gcry_set_log_handler(gtm_gcry_log_handler, NULL); # endif IS_FIPS_MODE_REQUESTED(fips_requested); if (fips_requested) { # ifdef USE_GCRYPT # ifndef GCRYPT_NO_FIPS if (0 != (rv = gcry_control(GCRYCTL_FORCE_FIPS_MODE))) { GC_APPEND_GCRY_ERROR(rv, "Failed to initialize FIPS mode."); return -1; } # endif # else ENABLE_FIPS_MODE(rv, fips_enabled); /* Relevant error detail populated in the above macro. */ if (-1 == rv) return -1; # endif } # ifdef USE_GCRYPT if (0 != gc_sym_init()) return -1; # endif GC_PK_INIT; /* Update $gtm_passwd for future invocation */ if (0 != gc_update_passwd(GTM_PASSWD_ENV, >mcrypt_pwent, GTMCRYPT_DEFAULT_PASSWD_PROMPT, GTMCRYPT_OP_INTERACTIVE_MODE & flags)) { return -1; } gtmcrypt_inited = TRUE; gtmcrypt_init_flags = flags; gtmcrypt_err_string[0] = '\0'; if (0 != gc_pk_gpghome_has_permissions()) return -1; return 0; } /* * Return the error string. * * Returns: The error string constructed so far. */ char* gtmcrypt_strerror(void) { return gtmcrypt_err_string; } /* * Find the key by hash and database path and set up database encryption and decryption state objects, if not created yet. * * Arguments: handle Pointer to the database encryption state object supplied by the caller and filled in by this * routine. * key_hash Hash of the key. * db_path Path to the database file that should be associated with the sought key. Can be an empty string. * iv Initialization vector to use for encryption or decryption. * * Returns: 0 if the key was found and database encryption and decryption state objects were initialized or existed already; -1 * otherwise. */ gtm_status_t gtmcrypt_init_db_cipher_context_by_hash(gtmcrypt_key_t *handle, gtm_string_t key_hash, gtm_string_t db_path, gtm_string_t iv) { gtm_keystore_t *entry; char filename[GTM_PATH_MAX], real_db_path[GTM_PATH_MAX]; char *db_path_ptr; gtm_cipher_ctx_t **ctx; GC_VERIFY_INITED; /* Discard any previously recorded error messages. */ gtmcrypt_err_string[0] = '\0'; assert((GTM_PATH_MAX > db_path.length) && (0 <= db_path.length)); CHECK_IV_LENGTH(iv); if (key_hash.length != GTMCRYPT_HASH_LEN) { UPDATE_ERROR_STRING("Specified symmetric key hash has length %ld, which is different from " "the expected hash length %d", key_hash.length, GTMCRYPT_HASH_LEN); return -1; } if (0 < db_path.length) { memcpy(filename, db_path.address, db_path.length); filename[db_path.length] = '\0'; /* Database path needs to be fully resolved before we can reliably use it, hence realpath-ing. */ if (NULL == realpath(filename, real_db_path)) { UPDATE_ERROR_STRING("Could not obtain the real path of the database " STR_ARG ". %s", ELLIPSIZE(filename), strerror(errno)); return -1; } db_path_ptr = real_db_path; } else db_path_ptr = NULL; if (0 != gtmcrypt_getkey_by_hash((unsigned char *)key_hash.address, db_path_ptr, &entry)) return -1; assert(NULL != entry); if (NULL == entry->db_cipher_entry) { /* This cipher context is for decryption; iv is a static global. */ if (0 != keystore_new_cipher_ctx(entry, iv.address, (unsigned int)iv.length, GTMCRYPT_OP_DECRYPT)) return -1; /* And this cipher context (inserted ahead of the first one) is for encryption. */ if (0 != keystore_new_cipher_ctx(entry, iv.address, (unsigned int)iv.length, GTMCRYPT_OP_ENCRYPT)) return -1; entry->db_cipher_entry = entry->cipher_head; } else assert(NULL != entry->cipher_head); ctx = (gtm_cipher_ctx_t **)handle; *ctx = entry->db_cipher_entry; assert(NULL != (*ctx)->next); PRINT_KEYSTORE_STATE("Returning from gtmcrypt_init_db_cipher_context_by_hash(db_path=%s):\n (pointing to %s)\n", filename, entry->key_path); return 0; } /* * Find the key by its name and set up device encryption or decryption state object. * * Arguments: handle Pointer which should get pointed to the device encryption or decryption state object. * key_name Name of the key. * iv Initialization vector to use for encryption or decryption. * operation Flag indicating whether encryption or decryption is desired; use GTMCRYPT_OP_ENCRYPT or * GTMCRYPT_OP_DECRYPT, respectively. * * Returns: 0 if the key was found and device encryption or decryption state object was initialized; -1 otherwise. */ gtm_status_t gtmcrypt_init_device_cipher_context_by_keyname(gtmcrypt_key_t *handle, gtm_string_t key_name, gtm_string_t iv, gtm_int_t operation) { gtm_keystore_t *entry; char keyname[GTM_PATH_MAX]; gtm_cipher_ctx_t **ctx; GC_VERIFY_INITED; /* Discard any previously recorded error messages. */ gtmcrypt_err_string[0] = '\0'; CHECK_IV_LENGTH(iv); /* NULL-terminating to ensure correct lookups. */ memcpy(keyname, key_name.address, key_name.length); memset(keyname + key_name.length, 0, GTM_PATH_MAX - key_name.length); if (0 != gtmcrypt_getkey_by_keyname(keyname, NULL, &entry, FALSE)) return -1; assert(NULL != entry); if (0 != keystore_new_cipher_ctx(entry, iv.address, (unsigned int)iv.length, operation)) return -1; ctx = (gtm_cipher_ctx_t **)handle; *ctx = entry->cipher_head; PRINT_KEYSTORE_STATE("Returning from gtmcrypt_init_device_cipher_context_by_keyname(key_name=%s):\n (pointing to %s)\n", keyname, entry->key_path); return 0; } /* * Find the key by the path of the database it corresponds to as well as its own path, and obtain its hash. * * Arguments: db_path Path to the database file that should be associated with the sought key. * key_path Path to the key. Can be an empty string. * hash_dest Pointer to the location for this routine to copy the key's hash. * * Returns: 0 if the key was found and key's hash was copied to the specified location; -1 otherwise. */ gtm_status_t gtmcrypt_obtain_db_key_hash_by_keyname(gtm_string_t db_path, gtm_string_t key_path, gtm_string_t *hash_dest) { gtm_keystore_t *entry; char filename[GTM_PATH_MAX], real_db_path[GTM_PATH_MAX], real_key_path[GTM_PATH_MAX]; char *key_path_ptr; GC_VERIFY_INITED; /* Discard any previously recorded error messages. */ gtmcrypt_err_string[0] = '\0'; assert((GTM_PATH_MAX > db_path.length) && (0 < db_path.length)); assert((GTM_PATH_MAX > key_path.length) && (0 <= key_path.length)); memcpy(filename, db_path.address, db_path.length); filename[db_path.length] = '\0'; /* Database path needs to be fully resolved before we can reliably use it, hence realpath-ing. */ if (NULL == realpath(filename, real_db_path)) { UPDATE_ERROR_STRING("Could not obtain the real path of the database " STR_ARG ". %s", ELLIPSIZE(filename), strerror(errno)); return -1; } if (0 < key_path.length) { memcpy(filename, key_path.address, key_path.length); filename[key_path.length] = '\0'; /* Key path also needs to be fully resolved before we can reliably use it, hence realpath-ing. */ if (NULL == realpath(filename, real_key_path)) { UPDATE_ERROR_STRING("Could not obtain the real path of the key " STR_ARG ". %s", ELLIPSIZE(filename), strerror(errno)); return -1; } key_path_ptr = real_key_path; } else key_path_ptr = NULL; if (0 != gtmcrypt_getkey_by_keyname(real_db_path, key_path_ptr, &entry, TRUE)) return -1; assert(NULL != entry); hash_dest->length = GTMCRYPT_HASH_LEN; hash_dest->address = (char *)entry->key_hash; PRINT_KEYSTORE_STATE("Returning from gtmcrypt_obtain_db_key_hash_by_keyname(db_path=%s, key_path=%s):\n (pointing to %s)\n", real_db_path, (key_path_ptr ? key_path_ptr : ""), entry->key_path); return 0; } /* * Release the specified encryption or decryption state object, also releasing the decryption state if database encryption state is * specified. * * Arguments: handle Encryption or decryption state object to release. * * Returns: 0 if the operation was successful; -1 otherwise. */ gtm_status_t gtmcrypt_release_cipher_context(gtmcrypt_key_t handle) { gtm_cipher_ctx_t *ctx; GC_VERIFY_INITED; assert(GTMCRYPT_INVALID_KEY_HANDLE != handle); /* Discard any previously recorded error messages. */ gtmcrypt_err_string[0] = '\0'; ctx = (gtm_cipher_ctx_t *)handle; /* In case a database encryption state object is specified, we want to make sure that the respective database decryption * state object is also removed. */ if (ctx->store->db_cipher_entry == ctx) { assert(NULL != ctx->next); if (-1 == keystore_remove_cipher_ctx(ctx->next)) return -1; } if (-1 == keystore_remove_cipher_ctx(ctx)) return -1; return 0; } /* * Perform encryption or decryption of the provided data based on the specified encryption / decryption state. If the target buffer * pointer is NULL, the operation is done in-place. It is also possible to set the initialization vector (IV) to a particular value, * or reset it to the original value, before attempting the operation. Note that the changes are persistent. * * Arguments: handle Encryption state object to use. * unencr_block Block where unencrypted data is read from. * unencr_block_len Length of the unencrypted (and encrypted) data block. * encr_block Block where encrypted data is put into. * operation Flag indicating whether encryption or decryption is desired; use GTMCRYPT_OP_ENCRYPT or * GTMCRYPT_OP_DECRYPT, respectively. * iv_mode Flag indicating whether the initialization vector (IV) should be changed prior to the * operation; use GTMCRYPT_IV_CONTINUE to proceed without changing the IV, GTMCRYPT_IV_SET to * set the IV the value supplied in the iv argument, and GTMCRYPT_IV_RESET to reset the IV to * the value specified at initialization. * iv Initialization vector to set the encryption state to when iv_mode is GTMCRYPT_IV_SET. * * Returns: 0 if the operation succeeded; -1 otherwise. */ gtm_status_t gtmcrypt_encrypt_decrypt(gtmcrypt_key_t handle, gtm_char_t *src_block, gtm_int_t src_block_len, gtm_char_t *dest_block, gtm_int_t operation, gtm_int_t iv_mode, gtm_string_t iv) { gtm_cipher_ctx_t *ctx; unsigned char iv_array[GTMCRYPT_IV_LEN]; GC_VERIFY_INITED; assert(GTMCRYPT_INVALID_KEY_HANDLE != handle); /* Discard any previously recorded error messages. */ gtmcrypt_err_string[0] = '\0'; ctx = (gtm_cipher_ctx_t *)handle; assert(NULL != ctx); switch (iv_mode) { case GTMCRYPT_IV_SET: CHECK_IV_LENGTH(iv); memcpy(iv_array, iv.address, iv.length); if (GTMCRYPT_IV_LEN != iv.length) memset(iv_array + iv.length, 0, GTMCRYPT_IV_LEN - iv.length); case GTMCRYPT_IV_RESET: /* CAUTION: Fall-through. */ if (GTMCRYPT_OP_DECRYPT == operation) { /* We expect the IV to be set on a particular operation only for databases, which is why we obtain * the correct crypt_key_t object in case of decryption. */ ctx = ctx->next; assert(NULL != ctx); } if (gc_sym_create_cipher_handle(ctx->store->key, (GTMCRYPT_IV_SET == iv_mode) ? iv_array : ctx->iv, &ctx->handle, operation, TRUE)) return -1; break; case GTMCRYPT_IV_CONTINUE: /* For devices, encryption and decryption state should be maintained right from the first byte, and for this * purpose the IV should not be touched. */ break; default: assert(FALSE); } if (0 != gc_sym_encrypt_decrypt(&ctx->handle, (unsigned char *)src_block, src_block_len, (unsigned char *)dest_block, operation)) return -1; return 0; } /* * Compare the keys associated with two encryption or decryption state objects. * * Arguments: handle1 First ecryption or decryption state object to use. * handle2 Second ecryption or decryption state object to use. * * Returns: 1 if both encryption or decryption state objects use the same key; 0 otherwise. */ gtm_int_t gtmcrypt_same_key(gtmcrypt_key_t handle1, gtmcrypt_key_t handle2) { gtm_cipher_ctx_t *ctx1, *ctx2; assert((GTMCRYPT_INVALID_KEY_HANDLE != handle1) && (GTMCRYPT_INVALID_KEY_HANDLE != handle2)); ctx1 = (gtm_cipher_ctx_t *)handle1; ctx2 = (gtm_cipher_ctx_t *)handle2; if (ctx1 == NULL) return (ctx2 == NULL); if (ctx2 == NULL) return 0; return (ctx1->store == ctx2->store); } /* * Disable encryption and discard any sensitive data in memory. * * Returns: 0 if the operation succeeded; -1 otherwise. */ gtm_status_t gtmcrypt_close(void) { int status; GC_VERIFY_INITED; /* Discard any previously recorded error messages. */ gtmcrypt_err_string[0] = '\0'; gc_pk_scrub_passwd(); status = gtm_keystore_cleanup_all(); gtmcrypt_inited = FALSE; return status; } fis-gtm-V7.0-005/sr_unix/gtmcrypt_ref.h0000755000032200000250000000270014342376330016721 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2016 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. * * * ****************************************************************/ #ifndef GTMCRYPT_REF_H #define GTMCRYPT_REF_H #ifdef USE_OPENSSL # include # include # include typedef EVP_CIPHER_CTX *crypt_key_t; #else # include typedef gcry_cipher_hd_t crypt_key_t; #endif #define DOT_GNUPG ".gnupg" #define SYMMETRIC_KEY_MAX 32 #define GTMCRYPT_IV_LEN 16 #define GC_MIN_STATIC_BLOCK_SIZE 4096 /* Have a good size block, so that we dont keep reallocating */ /* Some environment variables that encryption plugin cares about */ #define GNUPGHOME "GNUPGHOME" #define HOME "HOME" /* Following makes sure that at no point we are in the encryption library without a prior call to gtmcrypt_init. */ #define GC_VERIFY_INITED \ { \ if (!gtmcrypt_inited) \ { \ UPDATE_ERROR_STRING("Encryption library has not been initialized"); \ return -1; \ } \ } #endif /* GTMCRYPT_REF_H */ fis-gtm-V7.0-005/sr_unix/gtmcrypt_sym_ref.c0000644000032200000250000001315614342376330017610 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2016 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 #include #include #include #include #include #include #include #include #include /* gpgme functions */ #include /* gcry*_err_t */ #include #include "gtmxc_types.h" #include "gtmcrypt_util.h" #include "gtmcrypt_interface.h" /* Function prototypes for gtmcrypt*.* functions */ #include "gtmcrypt_ref.h" #include "gtmcrypt_dbk_ref.h" #include "gtmcrypt_sym_ref.h" #include "gtmcrypt_pk_ref.h" #ifndef USE_OPENSSL /* * Initialize encryption state if libgcrypt is used. * * Returns: 0 if the initialization succeeded; -1 otherwise. */ int gc_sym_init(void) { gcry_error_t rv; if (!gcry_check_version(GCRYPT_VERSION)) { UPDATE_ERROR_STRING("libgcrypt version mismatch. %s or higher is required", GCRYPT_VERSION); return -1; } if (0 != (rv = gcry_control(GCRYCTL_DISABLE_SECMEM, 0))) { GC_APPEND_GCRY_ERROR(rv, "Failed to disable secure memory."); return -1; } if (0 != (rv = gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0))) { GC_APPEND_GCRY_ERROR(rv, "Failed to finish encryption initialization.") return -1; } return 0; } #endif /* * Destroy the specified encryption / decryption state object. * * Arguments: handle Encryption / decryption state object to destroy. * * Returns: N/A neither OpenSSL nor GCrypt destructors return a status. */ void gc_sym_destroy_cipher_handle(crypt_key_t handle) { if (NULL != handle) #ifdef USE_OPENSSL EVP_CIPHER_CTX_free(handle); #elif defined(USE_GCRYPT) gcry_cipher_close(handle); #else error Encryption library not defined, please use either -DUSE_OPENSSL or -DUSE_GCRYPT #endif } /* * Create an encryption / decryption state object based on the specified key and IV and assign it to the passed pointer. If the * reuse flag is TRUE, then the passed cipher state is not recreated but reinitialized with the specified IV. Note that for a * successful reuse of the state object with OpenSSL the *same* raw key needs to be provided as during its creation. * * Arguments: raw_key Raw contents of the symmetric key to use. * iv Initialization vector to use. * handle Pointer to assign the newly created encryption / decryption state object to. * direction Indicator of whether encryption or decryption state object is to be constructed. * reuse Indicator of whether the state object should be reused or recreated. * * Returns: 0 if the state object was successfully constructed; -1 otherwise. */ int gc_sym_create_cipher_handle(unsigned char *raw_key, unsigned char *iv, crypt_key_t *handle, int direction, int reuse) { int rv, plain_text_length; # ifdef USE_OPENSSL if (NULL == *handle) *handle = EVP_CIPHER_CTX_new(); else if (!reuse) { EVP_CIPHER_CTX_init(*handle); } if (!EVP_CipherInit_ex(*handle, ALGO, NULL, raw_key, iv, direction)) { GC_APPEND_OPENSSL_ERROR("Failed to initialize encryption key handle."); return -1; } # else if (!reuse) { if (0 != (rv = gcry_cipher_open(handle, ALGO, MODE, 0))) { GC_APPEND_GCRY_ERROR(rv, "Failed to initialize encryption key handle ('gcry_cipher_open')."); return -1; } if (0 != (rv = gcry_cipher_setkey(*handle, raw_key, SYMMETRIC_KEY_MAX))) { GC_APPEND_GCRY_ERROR(rv, "Failed to initialize encryption key handle ('gcry_cipher_setkey')."); return -1; } } gcry_cipher_setiv(*handle, iv, GTMCRYPT_IV_LEN); # endif return 0; } /* * Perform an encryption or decryption operation using the specified state object and buffers (or buffer, if in-place). * * Arguments: key Pointer to the encryption / decryption state object. * in_block Block from which to take the input data for the operation. * in_block_len Length of the block from which to take the input data for the operation; it should match the length * of the block for the output data, if not NULL. * out_block Block where to place the output data from the operation. * flag Indicator of whether encryption or decryption is to be performed. * * Returns: 0 if the operation went successfully; -1 otherwise. */ int gc_sym_encrypt_decrypt(crypt_key_t *key, unsigned char *in_block, int in_block_len, unsigned char *out_block, int flag) { int rv, tmp_len, out_block_len; assert(in_block); assert(0 < in_block_len); if (NULL == out_block) out_block = in_block; out_block_len = in_block_len; # ifdef USE_GCRYPT if (out_block == in_block) { in_block = NULL; in_block_len = 0; } rv = (GTMCRYPT_OP_ENCRYPT == flag) ? gcry_cipher_encrypt(*key, out_block, out_block_len, in_block, in_block_len) : gcry_cipher_decrypt(*key, out_block, out_block_len, in_block, in_block_len); if (0 != rv) { GC_APPEND_GCRY_ERROR(rv, "Libgcrypt function 'gcry_cipher_encrypt' or 'gcry_cipher_decrypt' failed."); return -1; } # endif # ifdef USE_OPENSSL if (!EVP_CipherUpdate(*key, out_block, &out_block_len, in_block, in_block_len)) { GC_APPEND_OPENSSL_ERROR("OpenSSL function 'EVP_CipherUpdate' failed.") return -1; } if (!EVP_CipherFinal_ex(*key, out_block + out_block_len, &tmp_len)) { GC_APPEND_OPENSSL_ERROR("OpenSSL function 'EVP_CipherFinal_ex' failed.") return -1; } # endif return 0; } fis-gtm-V7.0-005/sr_unix/gtmcrypt_sym_ref.h0000644000032200000250000000330114342376330017604 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2020 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. * * * ****************************************************************/ #ifndef GTMCRYPT_SYM_REF_H #define GTMCRYPT_SYM_REF_H #ifdef USE_OPENSSL # if defined(USE_AES256CFB) # define ALGO EVP_aes_256_cfb128() # define UNIQ_ENC_PARAM_STRING "AES256CFB" # else # error "Unsupported Algorithm for OpenSSL" # endif #elif defined USE_GCRYPT # if defined(USE_AES256CFB) # define ALGO GCRY_CIPHER_AES256 # define MODE GCRY_CIPHER_MODE_CFB # define UNIQ_ENC_PARAM_STRING "AES256CFB" # define FLAGS 0 # else # error "Unsupported Algorithm for Libgcrypt" # endif #else # error "Unsupported encryption library. Reference implementation currently supports OpenSSL and Libgcrypt" #endif /* USE_GCRYPT */ #define UNIQ_ENC_PARAM_LEN SIZEOF(UNIQ_ENC_PARAM_STRING) - 1 #define HASH_INPUT_BUFF_LEN UNIQ_ENC_PARAM_LEN + SYMMETRIC_KEY_MAX #ifndef USE_OPENSSL int gc_sym_init(void); #endif int gc_sym_destroy_key_handles(gtm_keystore_t *entry); int gc_sym_create_cipher_handle(unsigned char *raw_key, unsigned char *iv, crypt_key_t *handle, int direction, int reuse); void gc_sym_destroy_cipher_handle(crypt_key_t handle); int gc_sym_encrypt_decrypt(crypt_key_t *key, unsigned char *in_block, int in_block_len, unsigned char *out_block, int flag); #endif /* GTMCRYPT_SYM_REF_H */ fis-gtm-V7.0-005/sr_unix/gtmcrypt_util.c0000644000032200000250000003335214342376330017121 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2022 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gtmxc_types.h" #include "gtmcrypt_util.h" #include "gtmcrypt_interface.h" /* Define the global error string here. Since this module gets linked into libgtmcryptutil.so, the error string global gets * into the shared library automatically and is shared across all different libraries that comprise the encryption reference * implementation. */ GBLDEF char gtmcrypt_err_string[MAX_GTMCRYPT_ERR_STRLEN + 1]; GBLDEF char *gtmtls_err_string = NULL; #ifndef USE_SYSLIB_FUNCS GBLDEF gtm_malloc_fnptr_t gtm_malloc_fnptr; GBLDEF gtm_free_fnptr_t gtm_free_fnptr; #endif #define SIGPROCMASK(FUNC, NEWSET, OLDSET, RC) \ { \ do \ { \ RC = sigprocmask(FUNC, NEWSET, OLDSET); /* BYPASSOK(sigprocmask) */ \ } while ((-1 == RC) && (EINTR == errno)); \ } #define Tcsetattr(FDESC, WHEN, TERMPTR, RC, ERRNO) \ { \ sigset_t block_ttinout; \ sigset_t oldset; \ int rc; \ \ sigemptyset(&block_ttinout); \ sigaddset(&block_ttinout, SIGTTIN); \ sigaddset(&block_ttinout, SIGTTOU); \ SIGPROCMASK(SIG_BLOCK, &block_ttinout, &oldset, rc); \ do \ { \ RC = tcsetattr(FDESC, WHEN, TERMPTR); \ } while(-1 == RC && EINTR == errno); \ ERRNO = errno; \ SIGPROCMASK(SIG_SETMASK, &oldset, NULL, rc); \ } #ifdef USE_OPENSSL #define SHA512(INBUF, INBUF_LEN, OUTBUF, RC) \ { \ RC = EVP_Digest(INBUF, INBUF_LEN, (unsigned char *)OUTBUF, NULL, EVP_sha512(), NULL); \ if (0 >= RC) \ { \ RC = -1; \ GC_APPEND_OPENSSL_ERROR("OpenSSL function 'EVP_sha512' failed."); \ } else \ RC = 0; \ } #else #define SHA512(INBUF, INBUF_LEN, OUTBUF, RC) \ { \ /* First initialize the libgcrypt library */ \ RC = 0; \ if (NULL == gcry_check_version(GCRYPT_VERSION)) \ { \ UPDATE_ERROR_STRING("libgcrypt version mismatch. %s or higher is required", GCRYPT_VERSION); \ RC = -1; \ } else \ gcry_md_hash_buffer(GCRY_MD_SHA512, OUTBUF, INBUF, INBUF_LEN); \ } #endif int gc_load_gtmshr_symbols() { /* CYGWIN TODO: This is to fix a linker error. Undo when it is fixed. */ # if !defined(USE_SYSLIB_FUNCS) && !defined(__CYGWIN__) gtm_malloc_fnptr = >m_malloc; gtm_free_fnptr = >m_free; # endif return 0; } /* Libgcrypt doesn't do a good job of handling error messages and simply dumps them onto the console if * no handler is set. One such example is when libgcrypt is doing a select on /dev/random and when it * is interrupted due to a SIGALRM, libgcrypt dumps an error message onto the console. To avoid this, * setup a dummy handler and swallow libgcrypt messages as long as they are not FATAL or BUG. */ void gtm_gcry_log_handler(void *opaque, int level, const char *fmt, va_list arg_ptr) { assert((GCRY_LOG_FATAL != level) && (GCRY_LOG_BUG != level)); return; } int gc_read_passwd(char *prompt, char *buf, int maxlen, void *tty) { struct termios new_tty, old_tty, *tty_copy; int fd, status, save_errno, istty, i, rv; char c; /* Display the prompt */ printf("%s", prompt); fflush(stdout); /* BYPASSOK -- cannot use FFLUSH */ /* Determine if the process has a terminal device associated with it */ fd = fileno(stdin); if (FALSE != (istty = isatty(fd))) { /* Turn off terminal echo. */ status = tcgetattr(fd, &old_tty); if (0 != status) { UPDATE_ERROR_STRING("Unable to set up terminal for safe password entry. Will not request passphrase. %s", strerror(errno)); return -1; } if (NULL != tty) { /* In case a pointer was passed for the current terminal state, avoid a race condition with a potential * interrupt by first assigning a pointer for the allocated space to a local variable and only then * updating the passed-in pointer. */ tty_copy = (struct termios *)MALLOC(SIZEOF(struct termios)); assert(NULL != tty_copy); memcpy(tty_copy, &old_tty, SIZEOF(struct termios)); *((struct termios **)tty) = tty_copy; } new_tty = old_tty; new_tty.c_lflag &= ~ECHO; /* GT.M terminal settings has ICANON and ICRNL turned-off. This causes the terminal to be treated * in non-canonical mode and carriage-return to not take effect. Re-enable them so that input can be * read from the user. */ new_tty.c_lflag |= ICANON; new_tty.c_iflag |= ICRNL; Tcsetattr(fd, TCSAFLUSH, &new_tty, status, save_errno); if (-1 == status) { UPDATE_ERROR_STRING("Unable to set up terminal for safe password entry. Will not request passphrase. %s", strerror(save_errno)); return -1; } } /* Read the password. Note: we cannot use fgets() here as that would cause mixing of streams (buffered vs non-buffered). */ i = rv = 0; do { while ((-1 == (status = read(fd, &c, 1))) && (EINTR == errno)) ; if (-1 == status) { save_errno = errno; UPDATE_ERROR_STRING("Failed to obtain passphrase. %s", strerror(save_errno)); rv = -1; break; } else if (0 == status) { UPDATE_ERROR_STRING("Failed to obtain passphrase. Encountered premature EOF while reading from terminal."); rv = -1; break; } buf[i++] = c; } while (('\n' != c) && (i < maxlen)); if (i == maxlen) { UPDATE_ERROR_STRING("Password too long. Maximum allowed password length is %d characters.", maxlen); rv = -1; } if (-1 != rv) { assert('\n' == buf[i - 1]); buf[i - 1] = '\0'; /* strip off the trailing \n */ } if (istty) { /* Reset terminal settings to how it was at the function entry. */ Tcsetattr(fd, TCSAFLUSH, &old_tty, status, save_errno); if (-1 == status) { UPDATE_ERROR_STRING("Unable to restore terminal settings. %s", strerror(save_errno)); return -1; } } return rv; } /* Given a stream of characters representing the obfuscated/unobfuscated password, convert to the other form. The process requires * an intermediate XOR_MASK. * * XOR MASK: * -------- * If the gtm_obfuscation_key exists and points to a file that has readable contents, the XOR mask is the SHA-512 hash of the * contents of that file. * Otherwise, within a pre-zero'ed buffer (of size equal to the length of the password), the value of $USER (from the environment) * is left-justified and the decimal representation of the inode of the mumps executable is right-justified. The XOR mask is the * SHA-512 hash of the contents of this buffer. * * USER0000INODE => SHA-512 => XOR mask * * MASKING/UNMASKING: * ------------------ * The input character pointer (in->address) is XOR'ed with the above XOR mask and copied into out->address. * * The 'nparm' value is unused when called from C and is there only so that this function can be used as an external call entry- * point for M. */ int gc_mask_unmask_passwd(int nparm, gtm_string_t *in, gtm_string_t *out) { char tmp[GTM_PASSPHRASE_MAX], mumps_exe[GTM_PATH_MAX], hash_in[GTM_PASSPHRASE_MAX], hash[GTMCRYPT_HASH_LEN]; char *ptr, *distptr, *mmap_addrs; int passwd_len, len, i, save_errno, fd, have_hash, status, hash_off; struct stat stat_info; have_hash = FALSE; passwd_len = in->length < GTM_PASSPHRASE_MAX ? in->length : GTM_PASSPHRASE_MAX; if (NULL != (ptr = getenv(GTM_OBFUSCATION_KEY))) { if (-1 != (fd = open(ptr, O_RDONLY))) { if ((-1 != fstat(fd, &stat_info)) && S_ISREG(stat_info.st_mode)) { /* File pointed by $gtm_obfuscation_key exists and is a regular file */ mmap_addrs = mmap(0, stat_info.st_size, PROT_READ, MAP_SHARED, fd, 0); if (MAP_FAILED != mmap_addrs) { SHA512(mmap_addrs, stat_info.st_size, hash, status); if (0 != status) return -1; have_hash = TRUE; munmap(mmap_addrs, stat_info.st_size); } } close(fd); } } if (!have_hash) { if (!(distptr = getenv(GTM_DIST_ENV))) { UPDATE_ERROR_STRING(ENV_UNDEF_ERROR, GTM_DIST_ENV); return -1; } SNPRINTF(mumps_exe, GTM_PATH_MAX, "%s/%s", distptr, "mumps"); if (0 != stat(mumps_exe, &stat_info)) { save_errno = errno; UPDATE_ERROR_STRING("Cannot find MUMPS executable in %s - %s", ptr, strerror(save_errno)); return -1; } if (!(ptr = getenv(USER_ENV))) { UPDATE_ERROR_STRING(ENV_UNDEF_ERROR, USER_ENV); return -1; } memset(hash_in, 0, SIZEOF(hash_in)); SNPRINTF(hash_in, passwd_len, "%s", ptr); SNPRINTF(tmp, SIZEOF(tmp), "%ld", (long)stat_info.st_ino); len = (int)STRLEN(tmp); hash_off = 0; if (len < passwd_len) hash_off = passwd_len - len; else len = passwd_len; memcpy(hash_in + hash_off, tmp, len); SHA512(hash_in, passwd_len, hash, status); if (0 != status) return -1; have_hash = TRUE; } assert(have_hash); for (i = 0; i < passwd_len; i++) out->address[i] = in->address[i] ^ hash[i % GTMCRYPT_HASH_LEN]; out->length = passwd_len; return 0; } void gc_freeup_pwent(passwd_entry_t *pwent) { assert(NULL != pwent); memset(pwent->passwd, 0, pwent->passwd_len); if (NULL != pwent->passwd) FREE(pwent->passwd); if (NULL != pwent->env_value) FREE(pwent->env_value); FREE(pwent); } int gc_update_passwd(char *name, passwd_entry_t **ppwent, char *prompt, int interactive) { char *env_name, *env_value, *passwd, *lpasswd, tmp_passwd[GTM_PASSPHRASE_MAX]; int len, status; gtm_string_t passwd_str, tmp_passwd_str; passwd_entry_t *pwent; pwent = *ppwent; if (!(GTMCRYPT_OP_NOPWDENVVAR & interactive)) { if (!(lpasswd = getenv(name))) { UPDATE_ERROR_STRING(ENV_UNDEF_ERROR, name); return -1; } if ((NULL != pwent) && (0 == strcmp(pwent->env_value, lpasswd))) return 0; /* No change in the environment value. Nothing more to do. */ } else { if ((NULL != pwent) && (NULL != pwent->env_value)) lpasswd = pwent->env_value; else { UPDATE_ERROR_STRING("No passphrase provided for %s", name); if (NULL != pwent) gc_freeup_pwent(pwent); return -1; } } len = STRLEN(lpasswd); if (0 != (len % 2)) { UPDATE_ERROR_STRING("Environment variable %s must be a valid hexadecimal string of even length less than %d. " "Length is odd", name, GTM_PASSPHRASE_MAX); if (NULL != pwent) gc_freeup_pwent(pwent); return -1; } if ((GTM_PASSPHRASE_MAX * 2) <= len) { UPDATE_ERROR_STRING("Environment variable %s must be a valid hexadecimal string of even length less than %d. " "Length is %d", name, GTM_PASSPHRASE_MAX, len); if (NULL != pwent) gc_freeup_pwent(pwent); return -1; } if (!(GTMCRYPT_OP_NOPWDENVVAR & interactive)) { if (NULL != pwent) gc_freeup_pwent(pwent); pwent = MALLOC(SIZEOF(passwd_entry_t)); assert(NULL != pwent); pwent->env_value = MALLOC(len ? len + 1 : GTM_PASSPHRASE_MAX * 2 + 1); assert(NULL != pwent->env_value); env_name = pwent->env_name; strncpy(env_name, name, SIZEOF(pwent->env_name)); env_name[SIZEOF(pwent->env_name) - 1] = '\0'; } else env_name = pwent->env_name; pwent->passwd_len = len ? len / 2 + 1 : GTM_PASSPHRASE_MAX + 1; pwent->passwd = MALLOC(pwent->passwd_len); assert(NULL != pwent->passwd); env_value = pwent->env_value; passwd = pwent->passwd; if (0 < len) { /* First, convert from hexadecimal representation to regular representation */ GC_UNHEX(lpasswd, passwd, len); if (len < 0) { UPDATE_ERROR_STRING("Environment variable %s must be a valid hexadecimal string of even length " "less than %d. '%c' is not a valid digit (0-9, a-f, or A-F)", name, GTM_PASSPHRASE_MAX, passwd[0]); if (NULL != pwent) gc_freeup_pwent(pwent); return -1; } /* Now, unobfuscate to get the real password */ passwd_str.address = passwd; passwd_str.length = len / 2; if (0 == (status = gc_mask_unmask_passwd(2, &passwd_str, &passwd_str))) { if (env_value != lpasswd) { /* env_value was malloc()ed for len + 1 bytes */ strncpy(env_value, lpasswd, len); /* Store the hexadecimal representation in environment */ env_value[len] = '\0'; } passwd[len / 2] = '\0'; /* null-terminate the password string */ *ppwent = pwent; } else gc_freeup_pwent(pwent); return status; } /* Environment variable is set to an empty string. Prompt for password. But, first check if we are running in interactive * mode. If not, return with an error. */ if (!(GTMCRYPT_OP_INTERACTIVE_MODE & interactive)) { UPDATE_ERROR_STRING("Environment variable %s set to empty string. " "Cannot prompt for password in this mode of operation.", env_name); gc_freeup_pwent(pwent); return -1; } if (-1 == gc_read_passwd(prompt, passwd, GTM_PASSPHRASE_MAX, NULL)) { gc_freeup_pwent(pwent); return -1; } /* Obfuscate the read password and set it in the environment. */ passwd_str.address = passwd; passwd_str.length = (int)STRLEN(passwd); tmp_passwd_str.address = &tmp_passwd[0]; if (0 != gc_mask_unmask_passwd(2, &passwd_str, &tmp_passwd_str)) { gc_freeup_pwent(pwent); return -1; } /* Since the obfuscated password might contain un-printable characters, represent them as hexadecimal digits. */ GC_HEX(tmp_passwd, env_value, tmp_passwd_str.length * 2); setenv(env_name, env_value, TRUE); *ppwent = pwent; return 0; } fis-gtm-V7.0-005/sr_unix/gtmcrypt_util.h0000644000032200000250000002043714342376330017126 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2022 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. * * * ****************************************************************/ #ifndef GTMCRYPT_UTIL_H #define GTMCRYPT_UTIL_H #if !defined(DEBUG) && defined(assert) # undef assert # define assert(x) #endif #include "gtm_sizeof.h" /* For SIZEOF. */ #include "gtm_limits.h" /* For GTM_PATH_MAX. */ #include "gtm_common_defs.h" /* To import common macros implemented by GT.M */ #define GTM_DIST_ENV "gtm_dist" #define USER_ENV "USER" #define ENV_UNDEF_ERROR "Environment variable %s not set" #define ENV_EMPTY_ERROR "Environment variable %s set to empty string" #define ENV_TOOLONG_ERROR "Environment variable %s is too long (%d)" #define MAX_GTMCRYPT_STR_ARG_LEN 256 #define MAX_GTMCRYPT_ERR_STRLEN 2048 /* next two defines also in gtm_tls_interface.h and should be kept in sync */ #define GTM_PASSPHRASE_MAX 512 /* obfuscated */ #define GTM_PASSPHRASE_MAX_ASCII (GTM_PASSPHRASE_MAX / 2) #define PASSPHRASE_ENVNAME_MAX 64 #define GTMCRYPT_DEFAULT_PASSWD_PROMPT "Enter Passphrase: " #define GTM_OBFUSCATION_KEY "gtm_obfuscation_key" #define GTMCRYPT_FIPS_ENV "gtmcrypt_FIPS" #define GC_H2D(C, RES, MULT) \ { \ if ((C >= 'A') && (C <= 'F')) \ RES = ((C - 'A') + 10) * MULT; \ else if ((C >= 'a') && (C <= 'f')) \ RES = ((C - 'a') + 10) * MULT; \ else if ((C >= '0') && (C <= '9')) \ RES = (C - '0') * MULT; \ else \ RES = -1; \ } /* Convert SOURCE, sequence of hexadecimal characters, into decimal representation. LEN denotes the length of the SOURCE string. * NOTE: Hexadecimal characters, presented in SOURCE string, has to be in upper case. */ #define GC_UNHEX(SOURCE, TARGET, LEN) \ { \ int i, res1, res2; \ char c; \ \ for (i = 0; i < LEN; i += 2) \ { \ c = SOURCE[i]; \ GC_H2D(c, res1, 16); \ if (res1 < 0) \ { \ LEN = -1; \ TARGET[0] = (char)c; \ break; \ } \ c = SOURCE[i + 1]; \ GC_H2D(c, res2, 1); \ if (res2 < 0) \ { \ LEN = -1; \ TARGET[0] = (char)c; \ break; \ } \ TARGET[i / 2] = (unsigned char)(res1 + res2); \ } \ } /* Convert SOURCE, sequence of decimal characters, into hexadecimal representation. LEN denotes the length of the TARGET string. */ #define GC_HEX(SOURCE, TARGET, LEN) \ MBSTART { \ int i; \ \ for (i = 0; i < LEN; i += 2) \ SNPRINTF(TARGET + i, LEN + 1 - i, "%02X", (unsigned char)SOURCE[i / 2]); \ } MBEND /* Using this macro implies that truncation errors are caught by some downstream check */ #define SNPRINTF(SRC, LEN, ...) \ { \ int rc; \ \ do \ { \ rc = snprintf(SRC, LEN, __VA_ARGS__); /* BYPASSOK */ \ } while ((-1 == rc) && (EINTR == errno)); /* EINTR-safe */ \ } #define SPRINTF(SRC, ...) assert(FALSE); /* don't want use of function subject to overruns */ \ /* Some helper error reporting macros. */ #define UPDATE_ERROR_STRING(...) \ { \ SNPRINTF(gtmcrypt_err_string, MAX_GTMCRYPT_ERR_STRLEN, __VA_ARGS__); \ } #define STR_QUOT(X) #X #define STR_WRAP(X) STR_QUOT(X) #define STR_ARG "%." STR_WRAP(MAX_GTMCRYPT_STR_ARG_LEN) "s%s" #define ELLIPSIZE(STR) STR, (strlen(STR) > MAX_GTMCRYPT_STR_ARG_LEN ? "..." : "") #define IS_FIPS_MODE_REQUESTED(RV) \ { \ char *ptr; \ \ RV = FALSE; \ if (NULL != (ptr = getenv(GTMCRYPT_FIPS_ENV))) \ { \ if ((0 == strcasecmp(ptr, "YES")) \ || (0 == strcasecmp(ptr, "TRUE")) \ || (*ptr == '1') \ || (*ptr == 'Y') \ || (*ptr == 'y')) \ { \ RV = TRUE; \ } \ } \ } /* In OpenSSL versions prior to 1.0.1, "FIPS_mode_set", the function that enables/disables FIPS mode, is available only * in a FIPS capable OpenSSL (that is, OpenSSL built from source with "fips" option). From 1.0.1, the function was moved * to "crypto/o_fips.c" and made as a wrapper to the actual FIPS Object Module and so was available on OpenSSL versions * built with or without the "fips" flag. Since the plugin would be run in an environment having OpenSSL with or without * FIPS capability, we first do a dlopen with a special NULL parameter to get access to the global namespace and then * do a dlsym to see if the "FIPS_mode_set" function is available. If not (or "FIPS_mode_set" returns 0), we raise an error. */ #define ENABLE_FIPS_MODE(RV, FIPS_ENABLED) \ { \ void *handle; \ int (*FIPS_mode_set_fnptr)(int); \ \ handle = dlopen(NULL, (RTLD_NOW | RTLD_GLOBAL)); \ RV = 0; \ FIPS_ENABLED = FALSE; \ if (NULL == handle) \ { \ UPDATE_ERROR_STRING("Failed to dlopen the global namespace. Reason: %s.", dlerror()); \ assert(FALSE); \ RV = -1; \ } else \ { \ FIPS_mode_set_fnptr = (int(*)(int))dlsym(handle, "FIPS_mode_set"); \ if (NULL != FIPS_mode_set_fnptr) \ { /* Symbol available. Invoke it to enable FIPS mode. */ \ if (FIPS_mode_set_fnptr(1)) \ FIPS_ENABLED = TRUE; \ else \ { \ GC_APPEND_OPENSSL_ERROR("Failed to initialize FIPS mode."); \ RV = -1; \ } \ } else \ { \ UPDATE_ERROR_STRING("Failed to initialize FIPS mode. Reason: cannot find OpenSSL FIPS " \ "functions in the runtime system."); \ RV = -1; \ } \ } \ } /* OpenSSL specific error handling. */ #define GC_APPEND_OPENSSL_ERROR(...) \ { \ char *errptr, *end; \ int rv; \ \ errptr = gtmcrypt_err_string; \ end = errptr + MAX_GTMCRYPT_ERR_STRLEN; \ SNPRINTF(errptr, MAX_GTMCRYPT_ERR_STRLEN, __VA_ARGS__); \ errptr += STRLEN(errptr); \ SNPRINTF(errptr, end - errptr, "%s", " Reason: "); \ errptr += STRLEN(errptr); \ rv = ERR_get_error(); \ ERR_error_string_n(rv, errptr, end - errptr); \ } /* Libgcrypt specific error handling. */ #define GC_APPEND_GCRY_ERROR(ERR, ...) \ { \ char *errptr, *end; \ \ errptr = gtmcrypt_err_string; \ end = errptr + MAX_GTMCRYPT_ERR_STRLEN; \ SNPRINTF(errptr, MAX_GTMCRYPT_ERR_STRLEN, __VA_ARGS__); \ errptr += STRLEN(errptr); \ SNPRINTF(errptr, end - errptr, "%s", " Reason: "); \ errptr += STRLEN(errptr); \ SNPRINTF(errptr, end - errptr, "%s", gcry_strerror(ERR)); \ } /* CYGWIN TODO: This is to fix a linker error. Undo when it is fixed. */ #if !defined(USE_SYSLIB_FUNCS) && !defined(__CYGWIN__) #define MALLOC (*gtm_malloc_fnptr) #define FREE (*gtm_free_fnptr) #else #define MALLOC malloc #define FREE free #endif typedef struct { char env_name[PASSPHRASE_ENVNAME_MAX]; char *env_value; /* Obfuscated version of the password stored in the environment (as hex) */ char *passwd; /* Password in clear text. */ int passwd_len; /* Length of the password that's allocated. */ } passwd_entry_t; typedef void * (*gtm_malloc_fnptr_t)(size_t); typedef void (*gtm_free_fnptr_t)(void *); GBLREF gtm_malloc_fnptr_t gtm_malloc_fnptr; GBLREF gtm_free_fnptr_t gtm_free_fnptr; GBLREF char gtmcrypt_err_string[MAX_GTMCRYPT_ERR_STRLEN + 1]; GBLREF char *gtmtls_err_string; int gc_load_gtmshr_symbols(void); void gtm_gcry_log_handler(void *opaque, int level, const char *fmt, va_list arg_ptr); int gc_read_passwd(char *prompt, char *buf, int maxlen, void *tty); int gc_mask_unmask_passwd(int nparm, gtm_string_t *in, gtm_string_t *out); void gc_freeup_pwent(passwd_entry_t *pwent); int gc_update_passwd(char *name, passwd_entry_t **ppwent, char *prompt, int interactive); #endif fis-gtm-V7.0-005/sr_unix/gtmcshrc.gtc0000755000032200000250000000154114342376330016356 0ustar librarygtc################################################################# # # # Copyright 2001, 2009 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. # # # ################################################################# setenv gtm_dist 'GTMDIST' setenv gtmgbldir 'mumps.gld' if ($?gtm_chset) then if (($gtm_chset == "UTF-8" || $gtm_chset == "utf-8") && (-e $gtm_dist/utf8)) then setenv gtm_dist $gtm_dist/utf8 endif endif setenv gtmroutines ". $gtm_dist" alias gtm '$gtm_dist/mumps -direct' alias mupip '$gtm_dist/mupip' alias lke '$gtm_dist/lke' alias gde '$gtm_dist/mumps -r ^GDE' alias dse '$gtm_dist/dse' set path = ($path $gtm_dist) fis-gtm-V7.0-005/sr_unix/gtmdbgflags.h0000644000032200000250000000314214342376330016473 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2018 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. * * * ****************************************************************/ #ifndef GTMDBGFLAGS_H_INCLUDED #define GTMDBGFLAGS_H_INCLUDED #ifdef GTMDBGFLAGS_ENABLED # define GTMDBGFLAGS_MASK_SET(MASK) (TREF(gtmdbgflags) & MASK) # define GTMDBGFLAGS_ONLY(MASK, ...) \ { \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ if (GTMDBGFLAGS_MASK_SET(MASK)) \ { \ (TREF(gtmdbgflags_freq_cntr))++; \ if (TREF(gtmdbgflags_freq) == TREF(gtmdbgflags_freq_cntr)) \ { \ __VA_ARGS__; \ TREF(gtmdbgflags_freq_cntr) = 0; \ } \ } \ } # define GTMDBGFLAGS_NOFREQ_ONLY(MASK, ...) \ { \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ if (GTMDBGFLAGS_MASK_SET(MASK)) \ { \ __VA_ARGS__; \ } \ } # define GTMSOURCE_FORCE_READ_FILE_MODE 0x00000001 #else # define GTMDBGFLAGS_MASK_SET(MASK) FALSE # define GTMDBGFLAGS_ONLY(MASK, FREQ, ...) # define GTMDBGFLAGS_NOFREQ_ONLY(MASK, ...) #endif #endif fis-gtm-V7.0-005/sr_unix/gtmdef.csh0000755000032200000250000000435714342376330016022 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2020 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. # # # ################################################################# # ############################################################## # # # gtmdef.csh - set up initial GT.M environment variables # # This shell script corresponds to the VMS script # gtmdef.com which defines all of the system globals for # the Greystone GT.M development environment. I.e., # each of the following 'setenv' commands is intended to # be roughly equivalent to the VMS 'define/system' # command. # # To duplicate this effect as much as possible, this # script should be invoked as soon as possible after # shell startup (see gtm_cshrc.csh). # # ############################################################## # The below are defined for backward compatibility. Can be removed once V63011 and prior versions are gone setenv gtm_gtmdev "/usr" setenv gtm_topdir library ############################### if (! $?gtm_root) setenv gtm_root /usr/library # location of development directory tree setenv gtm_com $gtm_root/com # location of GT.M csh scripts source $gtm_com/versions.csh # establish the environment variables: # gtm_curpro (current production release name) # gtm_verno (current active release name) set vernam=$gtm_root/$gtm_verno setenv gtm_ver $vernam setenv gtm_vrt $gtm_ver # Set only because prior versions need it. Should not be used post V63011 setenv gtm_tools $gtm_ver/tools setenv gtm_inc $gtm_ver/inc setenv gtm_pro $gtm_ver/pro # production version setenv gtm_bta $gtm_ver/bta # beta test version setenv gtm_dbg $gtm_ver/dbg # debug version setenv gtm_tools $gtm_ver/tools # version-controlled GT.M csh scripts setenv gtm_exe $gtm_dbg # the active version (initially debug) setenv gtmsrc_last_exe $gtm_exe # Set only because prior versions need it. Should not be needed post V63011 setenv gtmroutines ". $gtm_exe" fis-gtm-V7.0-005/sr_unix/gtmdefinedtypestodb.m0000644000032200000250000000154414342376330020267 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2015-2017 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This is called by generate_help.csh to put offset, length, type and format info into the gtmhelp database GTMDEFINEDTYPESTODB set $etrap="use $principal write $zstatus,! set $etrap=""zgoto 0"" zhalt 1" do Init^GTMDefinedTypesInit merge ^gtmtypfldindx=gtmtypfldindx merge ^gtmstructs=gtmstructs merge ^gtmunions=gtmunions merge ^gtmtypes=gtmtypes quit fis-gtm-V7.0-005/sr_unix/gtmexcludetypelist.txt0000644000032200000250000000274614342376330020552 0ustar librarygtc# # Part of gengtmdeftypes. # # List of GT.M defined structures that gengtmdeftypes.csh will ignore. These types are # unused in the GT.M runtime and/or cause issues when we try to process them. Most of them # are in usused components of GT.M or are in associated plugins, etc. # ModR_M SIB backup_blk_ptr_t backup_buff_ptr_t blk_ident ccp_action_aux_value ccp_action_code ccp_action_record ccp_db_header ccp_iosb ccp_que_entry ccp_relque ccp_wait ccp_wait_head db_key_map gtm_char_t gtm_ci_fptr_t gtm_double_t gtm_fileid_ptr_t gtm_filename_to_id_fnptr_t gtm_filename_to_id_fptr_t gtm_float_t gtm_free_fnptr_t gtm_free_fptr_t gtm_int_t gtm_int8 gtm_is_file_identical_fnptr_t gtm_is_file_identical_fptr_t gtm_jbig_decimal_t gtm_jboolean_t gtm_jbyte_array_t gtm_jdouble_t gtm_jfloat_t gtm_jint_t gtm_jlong_t gtm_jstring_t gtm_long_t gtm_pointertofunc_t gtm_status_t gtm_string_t gtm_timet gtm_uint_t gtm_uint8 gtm_ulong_t gtm_xcfileid_free_fnptr_t gtm_xcfileid_free_fptr_t gtm_zstatus_fptr_t gtmcrypt_decrypt_t gtmcrypt_encrypt_t gv_orig_key_array int2 jrec_suffix last_dfa_row_t lockhist_ptr_t mach_inst off_chain sub_num uint2 v15_bg_trc_rec v15_blk_hdr v15_blk_hdr_ptr_t v15_db_csh_acct_rec v15_gd_region v15_gd_seg_addr v15_gd_segment v15_jrec_prefix v15_sgmnt_data v15_sgmnt_data_ptr_t v15_th_index v15_th_index_ptr_t v15_time_t v15_trans_num v3_sgmnt_data_ptr_t v50v51_mstr xc_char_t xc_double_t xc_fileid_ptr_t xc_float_t xc_int_t xc_long_t xc_pointertofunc_t xc_status_t xc_string_t xc_uint_t xc_ulong_t fis-gtm-V7.0-005/sr_unix/gtmexe_symbols.exp0000755000032200000250000000014214342376330017620 0ustar librarygtcgtm_ci gtm_zstatus gtm_malloc gtm_free gtm_filename_to_id gtm_is_file_identical gtm_xcfileid_free fis-gtm-V7.0-005/sr_unix/gtmgblstat.c0000644000032200000250000000252114342376330016355 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2017 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. * * * ****************************************************************/ /* inc_rand() is not thread-safe; others are believed to be thread-safe. */ #include "mdef.h" #include "gtm_stdlib.h" #include "gtmxc_types.h" #include "gtmgblstat.h" /* Functions to support %YGBLSTAT */ gtm_status_t accumulate(int argc, gtm_string_t *acc, gtm_string_t *incr) { unsigned long long *acc1, *acc2, *incr1; acc1 = (unsigned long long *)acc->address; acc2 = (unsigned long long *)(acc->address + (acc->length > incr->length ? incr->length : acc->length)); incr1 = (unsigned long long *)incr->address; while ( acc1 < acc2 ) *acc1++ += *incr1++ ; return 0; } gtm_status_t is_big_endian(int argc, gtm_uint_t *endian) { #if __BIG_ENDIAN__ *endian = 1; #else *endian = 0; #endif return 0; } gtm_status_t to_ulong(int argc, gtm_ulong_t *value, gtm_string_t *bytestr) { *value = *(gtm_ulong_t *)bytestr->address; return 0; } fis-gtm-V7.0-005/sr_unix/gtmgblstat.h0000644000032200000250000000147714342376330016373 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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. * * * ****************************************************************/ /* Functions to support %YGBLSTAT */ #ifndef GTMGBLSTAT_H #define GTMGBLSTAT_H gtm_status_t accumulate(int argc, gtm_string_t *acc, gtm_string_t *incr); gtm_status_t is_big_endian(int argc, gtm_uint_t *endian); gtm_status_t to_ulong(int argc, gtm_ulong_t *value, gtm_string_t *bytestr); #endif /* GTMGBLSTAT_H */ fis-gtm-V7.0-005/sr_unix/gtmgblstat.xc0000644000032200000250000000037114342376330016546 0ustar librarygtc$gtm_dist/libgtmshr.so accumulate: gtm_status_t accumulate(IO:gtm_string_t*, I:gtm_string_t*) : SIGSAFE isbigendian: gtm_status_t is_big_endian(O:gtm_uint_t*) : SIGSAFE toulong: gtm_status_t to_ulong(O:gtm_ulong_t*, I:gtm_string_t*) : SIGSAFE fis-gtm-V7.0-005/sr_unix/gtmhelp.m0000755000032200000250000001630614342376330015670 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2002-2019 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; gtmhelp(subtopic,gbldir) ; new (gbldir,subtopic) new $zgbldir ; New $zgbldir so it is restored on exit zshow "d":zshow ; to capture original $P state - assumes $P is always subscript 1 set IO(1)=$io ; to capture the original $IO use $principal:nocenable ; do as soon as there's hope of undoing it, ie, after 2 prior lines set pio=$zwrite($io) ; in case of an early error new $etrap ; also inportant to get in place set $etrap="zgoto "_$zlevel_":error^GTMHELP" ; the code below sets up pio to restore the original $P state set:$io'=IO(1) IO(0)=$io ; now capture $P state - IO(0) means there' a device other than $P ; Capture the existing $principal's state to restore on exit set tmp="$principal:("_$select(zshow("D",1)["NOCENE":"no",1:"")_"cenable" set tmp=tmp_":ctrap="_$select(zshow("D",1)["CTRA=":$piece($piece(zshow("D",1),"CTRA=",2)," "),1:"""""") set exception=$zwrite($select(zshow("D",1)["EXCE=""":$piece(zshow("D",1),"EXCE=",2),1:"""""")) ; assumes EXCE last set tmp=tmp_":exception="_$extract(exception,3,$length(exception)-2) set pio=tmp_")" ; Override the exception handler if zshow("D",1)["TERMINAL" do . use $principal:(ctrap=$char(3):exception="zgoto "_$zlevel_":again:$zstatus[""CTRAP"","_$zlevel_":error^GTMHELP") else use $principal:(exception="zgoto "_$zlevel_":error^GTMHELP") set $zgbldir=gbldir again set:$data(COUNT) subtopic="" ; comes back to here and clears any topic kill (%gbldir,IO,pio,subtopic) ; X-NEW is evil, but performance is not an issue here set COUNT=0,IO="",NOTFOUND=0 ; some initialization do parse(subtopic) ; check for topic passed in for do display quit:COUNT<0 ; drive the real work use @pio,IO(1) ; restore $P state and original $IO quit ; parse(subtopic) ; organize space-delimited input memes into a topic hierarchy new (pio,subtopic,NEW,COUNT,TOPIC) set NEW=0 for i=1:1:$length(subtopic," ") set x=$piece(subtopic," ",i) if x'="" do . set COUNT=COUNT+1,NEW=NEW+1 . set TOPIC(COUNT)=$zconvert(x,"U") . quit quit ; display ; do the real work new (pio,COUNT,IO,MATCH,NEW,NOTFOUND,PROMPT,TOPIC) if $get(TOPIC(COUNT))="?" set COUNT=COUNT-1 ; refresh choices on the same topic (leve) write # if $$MATCH do ; look up the topic . if NOTFOUND do . . write !!,"Sorry, no Documentation on " . . for i=COUNT+1:1:NEW+COUNT write TOPIC(i)," " . . set NOTFOUND=0 . . quit . for set IO=$order(IO(IO),-1) quit:""=IO do ; if juggling devices, send content to both; do $P 2nd . . use IO(IO) . . for i=1:1:MATCH do print(MATCH(i),i) ; drive out lines of text using print . . quit . if $data(@MATCH(MATCH)@("s"))>1&(MATCH=1) do ; if descendent topics show the choices only on $P . . write $$FORMAT(4) . . write !!,"Additional information available: ",!! . . set x="" . . set subref=$name(@MATCH(MATCH)@("s")) . . for set x=$order(@subref@(x)) quit:x="" do ; use a "tabbed" list display . . . write $$FORMAT(0) . . . write @subref@(x) . . . write $$COLUMNS(subref,x) . . . do qualifiers($name(@subref@(x))) . . . quit . . quit . else set COUNT=COUNT-NEW ; otherwise, reposition to the start of the current level . if $zeof write # set COUNT=COUNT-1 quit ; No more input, peel back out write # could cause an error . write $$PROMPT . read subtopic,! . if subtopic="" set COUNT=COUNT-1 quit:0>COUNT ; no answer means peal back a level . else do parse(subtopic) ; check out the response . quit else do ; look up failed . set NOTFOUND=1 ; flag the next call . set COUNT=COUNT-NEW ; reset to the last working level . quit quit ; print(ref,i); ; text output function new (pio,ref,i,MATCH,COUNT) write !,@ref set y="" for set y=$order(@ref@(y)) quit:(y="s")!(y="") do . write $$FORMAT(1) . write !,@ref@(y) . quit if $data(@ref)>1 do . set subref=$name(@ref@("s")),x="" . for set x=$order(@subref@(x)) quit:x="" do:($extract(^(x))="-") ; do lines at this level . . set MATCH(i)=$name(MATCH(i),COUNT-1*2) . . write $$FORMAT(1) . . write !,@subref@(x) . . set z="" . . for set z=$order(@subref@(x,z)) quit:z="" do ; and its descendents . . . write !,@subref@(x,z) . . . quit . . quit . quit quit ; recursiv(ref,level) ; new (pio,COUNT,TOPIC,ref,MATCH,level,PROMPT,FLAG) set level=level+1 if ($extract(TOPIC(level))="-")&($get(FLAG)'=1) do . set FLAG=1 . for i=COUNT:-1:level set TOPIC(i+1)=TOPIC(i) . set COUNT=COUNT+1 . set TOPIC(level)="*" . quit set ref=$name(@ref@("s",TOPIC(level))) if TOPIC(level)'="" do:$data(@ref) . if level=COUNT do . . set PROMPT(level)=TOPIC(level) . . set MATCH=MATCH+1 . . set MATCH(MATCH)=ref . . quit . if level'=COUNT do recursiv(ref,level) . quit if TOPIC(level)="*" set TOPIC(level)="" set x="" for set x=$order(@ref) quit:(x="")!("\"_x'[("\"_TOPIC(level))) do . set ref=$name(@ref,(level*2)-1) . set ref=$name(@ref@(x)) . set (TOPIC(level),PROMPT(level))=@$name(@ref,level*2) . if level=COUNT do . . set MATCH=MATCH+1 . . set MATCH(MATCH)=ref . . quit . if level'=COUNT do recursiv(ref,level) . quit quit qualifiers(ref) ; qualifier lister new (pio,ref) if $data(@ref)>1 do . set ref=$name(@ref@("s")),x="-" . for set x=$order(@ref@(x)) quit:x=""!($extract(x)'="-") do:($extract(^(x))="-") . . set count=$get(count)+1 . . if count=1 write ! . . write ^(x) . . write $$COLUMNS(ref,x) . . quit . quit if $get(count)>0 write !! quit ; error ; Error handler called by $etrap set $ecode="" ; caller loses error trace, but generally called by direct mode quit:($zstatus["IOEOF")&($principal=$io) ; EOF is not a "real" error use @pio,IO(1) ; restore $P state and original $IO write !,"Error in GT.M help utility - look at ",$zjobexam("gtmhelpdmp")," for additional information",! quit MATCH() ; return array MATCH containing all global references that match ; the TOPIC array. new (pio,TOPIC,COUNT,MATCH,PROMPT) set QUALIFIERS=0 if COUNT=0 set MATCH=1 set MATCH(1)="^HELP" if COUNT'=0 do . set level=0 . set MATCH=0 . set ref="^HELP" . do recursiv(ref,level) . quit if $get(FLAG)=1 set COUNT=COUNT-1 quit MATCH ; WIDTH() quit 80 ; Width of the current device PAGE() quit 24 ; Page length of the current device FORMAT(newlines) if $y>($$PAGE-newlines-3) do . if '$zeof read !!,"Press RETURN to continue ...",dummy . write # . quit quit "" COLUMNS(subref,x) if $x+12'>$$WIDTH write ?$x\12+1*12 if $x+$l($order(@subref@(x)))>$$WIDTH write ! if $x+12>$$WIDTH write ! quit "" PROMPT() new (pio,COUNT,TOPIC,PROMPT) write !! set ref="^HELP" for i=1:1:COUNT do . set TOPIC(i)=$zconvert($select($data(PROMPT(i)):PROMPT(i),1:TOPIC(i)),"U") . set ref=$name(@ref@("s",TOPIC(i))) . write @ref," " . quit if COUNT=0 kill PROMPT write "Topic? " if COUNT>0 write "Subtopic? " quit "" fis-gtm-V7.0-005/sr_unix/gtmhlpld.m0000755000032200000250000000327014342376330016037 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2002-2017 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; gtmload ; new (%zdebug) set %level=$zlevel set $ztrap=$select($data(%zdebug):"b",1:"zg %level:err") f read "Help file > ",file s x=$zsearch(file) q:file=""!(x'="") d . w !,"File does not exist, to quit.",! . quit if file="" quit open file:(readonly) use file set ref="^HELP",incr=1,count=0,level=0,oldlevel=0,^HELP="" for read line quit:$zeof do . set first=$extract(line) . if first?1N!(first="-") do .. if first?1N set level=first,oldlevel=first .. else set level=oldlevel+1 .. if level'>count set ref=$name(@ref,(level-1)*2) .. set count=level .. set:first?1N subtopic=$piece(line," ",2) .. set:first="-" subtopic=$piece(line," ",1) .. set ref=$name(@ref@("s")) .. set ref=$name(@ref@($$UCASE(subtopic))) .. set @ref=subtopic .. set incr=1 .. quit . else do .. set @ref@(incr)=line .. set incr=incr+1 .. quit . quit close file quit err ; set $ztrap="" u 0 w !,"Error in GT.M help load utility." set file="gtmhlpld.dmp" open file:newversion use file zshow "*" close file set $etrap="zgoto 0" zhalt 1 ; UCASE(string) set lo="abcdefghijklmnopqrstuvwxyz" set up="ABCDEFGHIJKLMNOPQRSTUVWXYZ" quit $translate(string,lo,up) fis-gtm-V7.0-005/sr_unix/gtminstall.sh0000755000032200000250000006041414342376330016563 0ustar librarygtc#!/bin/sh - ################################################################# # # # Copyright (c) 2014-2021 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. # # # ################################################################# # This script automates the installation of GT.M as much as possible, # to the extent of attempting to download the distribution file. # Current limitation is GNU/Linux on x86 (32- & 64-bit) architectures # and root installation, but it is intended to relax this in the future. # NOTE: This script requires the GNU wget program to download # distribution files that are not on the local file system. # Revision history # # 2011-02-15 0.01 K.S. Bhaskar - Initial version for internal use # 2011-02-20 0.02 K.S. Bhaskar - Mostly usable with enough features for first Beta test # 2011-02-21 0.03 K.S. Bhaskar - Deal with case of no group bin, bug fixes, download from FTP site, other platforms # 2011-02-28 0.04 K.S. Bhaskar - Use which to get locations of id and grep, more bug fixes # 2011-03-05 0.05 K.S. Bhaskar - Through V5.4-001 group only needed if execution restricted to a group # 2011-03-08 0.06 K.S. Bhaskar - Make it work when bundled with GT.M V5.4-002 # 2011-03-10 0.10 K.S. Bhaskar - Incorporate review comments to bundle with V5.4-002 distribution # 2011-05-03 0.11 K.S. Bhaskar - Allow for letter suffix releases # 2011-10-25 0.12 K.S. Bhaskar - Support option to delete .o files on shared library platforms # 2014-08-13 0.13 K.S. Bhaskar - Add verbosity around getting latest version and tarball, if requested # 2015-10-13 0.14 GT.M Staff - Fix a few minor bugs # 2017-06-05 0.15 GT.M Staff - GT.M install script should set RemoveIPC no in systemd linux distributions # 2017-09-19 0.16 GT.M Staff - Treat i686 as i586; Fix an interaction bug in gtminstall resulting from the above change # 2019-07-14 0.17 GT.M Staff - Add an option (--filename) to pass the file name of GT.M distribution tarball # Turn on debugging if set if [ "Y" = "$gtm_debug" ] ; then set -x ; fi # Initialization timestamp=`date +%Y%m%d%H%M%S` gtm_id=`command -v id` gtm_grep=`command -v grep` if [ -z "$USER" ] ; then USER=`$gtm_id -un` ; fi # Functions dump_info() { set +x if [ -n "$gtm_arch" ] ; then echo gtm_arch " : " $gtm_arch ; fi if [ -n "$gtm_buildtype" ] ; then echo gtm_buildtype " : " $gtm_buildtype ; fi if [ -n "$gtm_configure_in" ] ; then echo gtm_configure_in " : " $gtm_configure_in ; fi if [ -n "$gtm_copyenv" ] ; then echo gtm_copyenv " : " $gtm_copyenv ; fi if [ -n "$gtm_copyexec" ] ; then echo gtm_copyexec " : " $gtm_copyexec ; fi if [ -n "$gtm_debug" ] ; then echo gtm_debug " : " $gtm_debug ; fi if [ -n "$gtm_dist" ] ; then echo gtm_dist " : " $gtm_dist ; fi if [ -n "$gtm_distrib" ] ; then echo gtm_distrib " : " $gtm_distrib ; fi if [ -n "$gtm_dryrun" ] ; then echo gtm_dryrun " : " $gtm_dryrun ; fi if [ -n "$gtm_filename" ] ; then echo gtm_filename " : " $gtm_filename ; fi if [ -n "$gtm_flavor" ] ; then echo gtm_flavor " : " $gtm_flavor ; fi if [ -n "$gtm_ftp_dirname" ] ; then echo gtm_ftp_dirname " : " $gtm_ftp_dirname ; fi if [ -n "$gtm_group" ] ; then echo gtm_group " : " $gtm_group ; fi if [ -n "$gtm_group_already" ] ; then echo gtm_group_already " : " $gtm_group_already ; fi if [ -n "$gtm_group_restriction" ] ; then echo gtm_group_restriction " : " $gtm_group_restriction ; fi if [ -n "$gtm_hostos" ] ; then echo gtm_hostos " : " $gtm_hostos ; fi if [ -n "$gtm_install_utf8" ] ; then echo gtm_install_utf8 " : " $gtm_install_utf8 ; fi if [ -n "$gtm_install_flavor" ] ; then echo gtm_install_flavor " : " $gtm_install_flavor ; fi if [ -n "$gtm_installdir" ] ; then echo gtm_installdir " : " $gtm_installdir ; fi if [ -n "$gtm_keep_obj" ] ; then echo gtm_keep_obj " : " $gtm_keep_obj ; fi if [ -n "$gtm_lcase_utils" ] ; then echo gtm_lcase_utils " : " $gtm_lcase_utils ; fi if [ -n "$gtm_linkenv" ] ; then echo gtm_linkenv " : " $gtm_linkenv ; fi if [ -n "$gtm_linkexec" ] ; then echo gtm_linkexec " : " $gtm_linkexec ; fi if [ -n "$gtm_overwrite_existing" ] ; then echo gtm_overwrite_existing " : " $gtm_overwrite_existing ; fi if [ -n "$gtm_prompt_for_group" ] ; then echo gtm_prompt_for_group " : " $gtm_prompt_for_group ; fi if [ -n "$gtm_prompt_for_sys_reconfig" ] ; then echo gtm_prompt_for_sys_reconfig " : " $gtm_prompt_for_sys_reconfig ; fi if [ -n "$gtm_sf_dirname" ] ; then echo gtm_sf_dirname " : " $gtm_sf_dirname ; fi if [ -n "$gtm_tmp" ] ; then echo gtm_tmp " : " $gtm_tmp ; fi if [ -n "$gtm_user" ] ; then echo gtm_user " : " $gtm_user ; fi if [ -n "$gtm_verbose" ] ; then echo gtm_verbose " : " $gtm_verbose ; fi if [ -n "$gtm_version" ] ; then echo gtm_version " : " $gtm_version ; fi if [ -n "$gtmroutines" ] ; then echo gtmroutines " : " $gtmroutines ; fi if [ -n "$timestamp" ] ; then echo timestamp " : " $timestamp ; fi if [ "Y" = "$gtm_debug" ] ; then set -x ; fi } err_exit() { set +x echo "gtminstall [option] ... [version]" echo "Options are:" echo "--build-type buildtype - * type of GT.M build, default is pro" echo "--copyenv dirname - copy gtmprofile and gtmcshrc files to dirname; incompatible with linkenv" echo "--copyexec dirname - copy gtm script to dirname; incompatible with linkexec" echo "--debug - * turn on debugging with set -x" echo "--distrib dirname or URL - source directory for GT.M distribution tarball, local or remote" echo "--filename filename - name of GT.M distribution tarball" echo "--dry-run - do everything short of installing GT.M, including downloading the distribution" echo "--group group - group that should own the GT.M installation" echo "--group-restriction - limit execution to a group; defaults to unlimited if not specified" echo "--help - print this usage information" echo "--installdir dirname - directory where GT.M is to be installed; defaults to /usr/lib/fis-gtm/version_platform" m1="--keep-obj - keep .o files" m1="$m1"" of M routines (normally deleted on platforms with GT.M support for routines in shared libraries)" echo "$m1" echo "--linkenv dirname - create link in dirname to gtmprofile and gtmcshrc files; incompatible with copyenv" echo "--linkexec dirname - create link in dirname to gtm script; incompatible with copyexec" echo "--noprompt-for-sys-cfg - do not prompt to adjust OS configuration files" echo "--overwrite-existing - install into an existing directory, overwriting contents; defaults to requiring new directory" m1="--prompt-for-group - * GT.M installation " m1="$m1""script will prompt for group; default is yes for production releases V5.4-002 or later, no for all others" echo "$m1" echo "--ucaseonly-utils -- install only upper case utility program names; defaults to both if not specified" echo "--user username - user who should own GT.M installation; default is root" echo "--utf8 - install UTF-8 support" echo "--verbose - * output diagnostic information as the script executes; default is to run quietly" echo "options that take a value (e.g, --group) can be specified as either --option=value or --option value" echo "options marked with * are likely to be of interest primarily to GT.M developers" echo "version is defaulted from mumps file if one exists in the same directory as the installer" echo "This version must run as root." exit 1 } mktmpdir() { tmpdirname="/tmp/gtminstall_${timestamp}" mkdir $tmpdirname mkdirstatus=$? while [ 0 -ne $mkdirstatus ] ; do let i++ tmpdirname="/tmp/gtminstall_${timestamp}_$i" mkdir $tmpdirname mkdirstatus=$? done echo $tmpdirname } read_yes_no() { read resp response=`echo $resp | tr '[a-z]' '[A-Z]'` if [ "Y" = "$response" -o "YES" = "$response" ] ; then echo "yes" else echo "no" fi } # Defaults that can be over-ridden by command line options to follow if [ -z "$gtm_buildtype" ] ; then gtm_buildtype="pro" ; fi if [ -z "$gtm_keep_obj" ] ; then gtm_keep_obj="N" ; fi if [ -z "$gtm_dryrun" ] ; then gtm_dryrun="N" ; fi if [ -z "$gtm_group_restriction" ] ; then gtm_group_restriction="N" ; fi if [ -z "$gtm_lcase_utils" ] ; then gtm_lcase_utils="Y" ; fi if [ -z "$gtm_overwrite_existing" ] ; then gtm_overwrite_existing="N" ; fi if [ -z "$gtm_prompt_for_group" ] ; then gtm_prompt_for_group="N" ; fi if [ -z "$gtm_prompt_for_sys_reconfig" ] ; then gtm_prompt_for_sys_reconfig="Y" ; fi if [ -z "$gtm_verbose" ] ; then gtm_verbose="N" ; fi # Initializing internal flags gtm_group_already="N" # Process command line while [ $# -gt 0 ] ; do case "$1" in --build-type*) tmp=`echo $1 | cut -s -d = -f 2-` if [ -n "$tmp" ] ; then gtm_buildtype=$tmp else if [ 1 -lt "$#" ] ; then gtm_buildtype=$2 ; shift else echo "--buildtype needs a value" ; err_exit fi fi shift ;; --copyenv*) tmp=`echo $1 | cut -s -d = -f 2-` if [ -n "$tmp" ] ; then gtm_copyenv=$tmp else if [ 1 -lt "$#" ] ; then gtm_copyenv=$2 ; shift else echo "--copyenv needs a value" ; err_exit fi fi unset gtm_linkenv shift ;; --copyexec*) tmp=`echo $1 | cut -s -d = -f 2-` if [ -n "$tmp" ] ; then gtm_copyexec=$tmp else if [ 1 -lt "$#" ] ; then gtm_copyexec=$2 ; shift else echo "--copyexec needs a value" ; err_exit fi fi unset gtm_linkexec shift ;; --debug) gtm_debug="Y" ; set -x ; shift ;; --distrib*) tmp=`echo $1 | cut -s -d = -f 2-` if [ -n "$tmp" ] ; then gtm_distrib=$tmp else if [ 1 -lt "$#" ] ; then gtm_distrib=$2 ; shift else echo "--distrib needs a value" ; err_exit fi fi shift ;; --filename) tmp=`echo $1 | cut -s -d = -f 2-` if [ -n "$tmp" ] ; then gtm_filename=$tmp else if [ 1 -lt "$#" ] ; then gtm_filename=$2 ; shift else echo "--filename needs a value" ; err_exit fi fi shift ;; --dry-run) gtm_dryrun="Y" ; shift ;; --group-restriction) gtm_group_restriction="Y" ; shift ;; # must come before group* --group*) tmp=`echo $1 | cut -s -d = -f 2-` if [ -n "$tmp" ] ; then gtm_group=$tmp else if [ 1 -lt "$#" ] ; then gtm_group=$2 ; shift else echo "--group needs a value" ; err_exit fi fi shift ;; --help) err_exit ;; --installdir*) tmp=`echo $1 | cut -s -d = -f 2-` if [ -n "$tmp" ] ; then gtm_installdir=$tmp else if [ 1 -lt "$#" ] ; then gtm_installdir=$2 ; shift else echo "--installdir needs a value" ; err_exit fi fi shift ;; --keep-obj) gtm_keep_obj="Y" ; shift ;; --linkenv*) tmp=`echo $1 | cut -s -d = -f 2-` if [ -n "$tmp" ] ; then gtm_linkenv=$tmp else if [ 1 -lt "$#" ] ; then gtm_linkenv=$2 ; shift else echo "--linkenv needs a value" ; err_exit fi fi unset gtm_copyenv shift ;; --linkexec*) tmp=`echo $1 | cut -s -d = -f 2-` if [ -n "$tmp" ] ; then gtm_linkexec=$tmp else if [ 1 -lt "$#" ] ; then gtm_linkexec=$2 ; shift else echo "--linkexec needs a value" ; err_exit fi fi unset gtm_copyexec shift ;; --noprompt-for-sys-cfg) gtm_prompt_for_sys_reconfig="N" ; shift ;; --overwrite-existing) gtm_overwrite_existing="Y" ; shift ;; --prompt-for-group) gtm_prompt_for_group="Y" ; shift ;; --ucaseonly-utils) gtm_lcase_utils="N" ; shift ;; --user*) tmp=`echo $1 | cut -s -d = -f 2-` if [ -n "$tmp" ] ; then gtm_user=$tmp else if [ 1 -lt "$#" ] ; then gtm_user=$2 ; shift else echo "--user needs a value" ; err_exit fi fi shift ;; --utf8*) gtm_install_utf8="Y" ; shift ;; --verbose) gtm_verbose="Y" ; shift ;; -*) echo Unrecognized option "$1" ; err_exit ;; *) if [ -n "$gtm_version" ] ; then echo Nothing must follow the GT.M version ; err_exit else gtm_version=$1 ; shift ; fi esac done if [ "Y" = "$gtm_verbose" ] ; then echo Processed command line ; dump_info ; fi # Set environment variables according to machine architecture gtm_arch=`uname -m | tr -d _` case $gtm_arch in sun*) gtm_arch="sparc" ;; esac gtm_hostos=`uname -s | tr A-Z a-z` case $gtm_hostos in gnu/linux) gtm_hostos="linux" ;; hp-ux) gtm_hostos="hpux" ;; sun*) gtm_hostos="solaris" ;; esac gtm_shlib_support="Y" case ${gtm_hostos}_${gtm_arch} in aix*) # no Source Forge dirname gtm_arch="rs6000" # uname -m is not useful on AIX gtm_ftp_dirname="aix" gtm_flavor="rs6000" gtm_install_flavor="RS6000" ;; hpux_ia64) # no Source Forge dirname gtm_ftp_dirname="hpux_ia64" gtm_flavor="ia64" gtm_install_flavor="IA64" ;; linux_i586) gtm_sf_dirname="GT.M-x86-Linux" gtm_ftp_dirname="linux" gtm_flavor="i586" gtm_install_flavor="x86" gtm_shlib_support="N" ;; linux_i686) gtm_sf_dirname="GT.M-x86-Linux" gtm_ftp_dirname="linux" gtm_flavor="i586" gtm_install_flavor="x86" gtm_shlib_support="N" ;; linux_ia64) # no Source Forge dirname gtm_ftp_dirname="linux_ia64" gtm_flavor="ia64" gtm_install_flavor="IA" ;; linux_s390x) # no Source Forge dirname gtm_ftp_dirname="linux_s390x" gtm_flavor="s390x" gtm_install_flavor="S390X" ;; linux_x8664) gtm_sf_dirname="GT.M-amd64-Linux" gtm_ftp_dirname="linux_x8664" gtm_flavor="x8664" gtm_install_flavor="x86_64" ;; solaris_sparc) # no Source Forge dirname gtm_ftp_dirname="sun" gtm_flavor="sparc" gtm_install_flavor="SPARC" ;; default) echo Architecture `uname -o` on `uname -m` not supported by this script ; err_exit ;; esac # GT.M version is required - first see if gtminstall and mumps are bundled if [ -z "$gtm_version" ] ; then tmp=`dirname $0` if [ -e "$tmp/mumps" -a -e "$tmp/_XCMD.m" ] ; then gtm_distrib=$tmp gtm_dist=$tmp ; export gtm_dist chmod +x $gtm_dist/mumps tmp=`mktmpdir` gtmroutines="$tmp($gtm_dist)" ; export gtmroutines gtm_version=`$gtm_dist/mumps -run %XCMD 'write $piece($zversion," ",2)'` rm -rf $tmp fi fi if [ "Y" = "$gtm_verbose" ] ; then echo Determined architecture, OS and GT.M version ; dump_info wget_flags="-P" else wget_flags="-qP" fi # See if GT.M version can be determined from meta data if [ -z "$gtm_distrib" ] ; then gtm_distrib=http://sourceforge.net/projects/fis-gtm fi gtm_tmp=`mktmpdir` mkdir $gtm_tmp/tmp if [ -z "$gtm_version" -o "latest" = "`echo "$gtm_version" | tr LATES lates`" ] ; then case $gtm_distrib in http://sourceforge.net/projects/fis-gtm | https://sourceforge.net/projects/fis-gtm) if [ "Y" = "$gtm_verbose" ] ; then echo wget ${gtm_distrib}/files/${gtm_sf_dirname}/latest to determine latest version echo Check proxy settings if wget hangs fi if { wget $wget_flags $gtm_tmp ${gtm_distrib}/files/${gtm_sf_dirname}/latest 2>&1 1>${gtm_tmp}/wget_latest.log ; } ; then gtm_version=`cat ${gtm_tmp}/latest` else echo Unable to determine GT.M version ; err_exit fi ;; ftp://*) if [ "Y" = "$gtm_verbose" ] ; then echo wget $gtm_tmp ${gtm_distrib}/${gtm_ftp_dirname}/latest to determine latest version echo Check proxy settings if wget hangs fi if { wget $wget_flags $gtm_tmp ${gtm_distrib}/${gtm_ftp_dirname}/latest 2>&1 1>${gtm_tmp}/wget_latest.log ; } ; then gtm_version=`cat ${gtm_tmp}/latest` else echo Unable to determine GT.M version ; err_exit fi ;; *) if [ -f ${gtm_distrib}/latest ] ; then gtm_version=`cat ${gtm_distrib}/latest` if [ "Y" = "$gtm_verbose" ] ; then echo Version is $gtm_version ; fi else echo Unable to determine GT.M version ; err_exit fi ;; esac fi if [ -z "$gtm_version" ] ; then echo GT.M version to install is required ; err_exit fi # Get GT.M distribution if gtminstall is not bundled with distribution if [ -f "${gtm_distrib}/mumps" ] ; then gtm_tmp=$gtm_distrib else tmp=`echo $gtm_version | tr -d .-` if [ -z "$gtm_filename" ] ; then gtm_filename=gtm_${tmp}_${gtm_hostos}_${gtm_flavor}_${gtm_buildtype}.tar.gz fi case $gtm_distrib in http://sourceforge.net/projects/fis-gtm | https://sourceforge.net/projects/fis-gtm) if [ "Y" = "$gtm_verbose" ] ; then echo wget ${gtm_distrib}/files/${gtm_sf_dirname}/${gtm_version}/${gtm_filename} to download tarball echo Check proxy settings if wget hangs fi if { ! wget $wget_flags $gtm_tmp ${gtm_distrib}/files/${gtm_sf_dirname}/${gtm_version}/${gtm_filename} \ 2>&1 1>${gtm_tmp}/wget_dist.log ; } ; then echo Unable to download GT.M distribution $gtm_filename ; err_exit fi ;; ftp://*) if [ "Y" = "$gtm_verbose" ] ; then echo wget ${gtm_distrib}/${gtm_ftp_dirname}/${tmp}/${gtm_filename} to download tarball echo Check proxy settings if wget hangs fi if { ! wget $wget_flags $gtm_tmp ${gtm_distrib}/${gtm_ftp_dirname}/${tmp}/${gtm_filename} \ 2>&1 1>${gtm_tmp}/wget_dist.log ; } ; then echo Unable to download GT.M distribution $gtm_filename ; err_exit fi ;; *) if [ -f ${gtm_distrib}/${gtm_filename} ] ; then if [ "Y" = "$gtm_verbose" ] ; then echo tarball is ${gtm_distrib}/${gtm_filename} ; fi ln -s ${gtm_distrib}/${gtm_filename} $gtm_tmp else echo Unable to locate GT.M distribution file ${gtm_distrib}/${gtm_filename} ; err_exit fi ;; esac ( cd $gtm_tmp/tmp ; gzip -d < ${gtm_tmp}/${gtm_filename} | tar xf - 2>&1 1>${gtm_tmp}/tar.log ) fi if [ "Y" = "$gtm_verbose" ] ; then echo Downloaded and unpacked GT.M distribution ; dump_info ; fi # Check installation settings & provide defaults as needed tmp=`$gtm_id -un` if [ -z "$gtm_user" ] ; then gtm_user=$tmp else if [ "$gtm_user" != "`$gtm_id -un $gtm_user`" ] ; then echo $gtm_user is a non-existent user ; err_exit fi fi if [ "root" = $tmp ] ; then if [ -z "$gtm_group" ] ; then gtm_group=`$gtm_id -gn` else if [ "root" != "$gtm_user" -a "$gtm_group" != "`$gtm_id -Gn $gtm_user | xargs -n 1 | $gtm_grep $gtm_group`" ] ; then echo $gtm_user is not a member of $gtm_group ; err_exit fi fi else echo Non-root installations not currently supported if [ "N" = "$gtm_dryrun" ] ; then err_exit else echo "Continuing because --dry-run selected" fi fi if [ -z "$gtm_installdir" ] ; then gtm_installdir=/usr/lib/fis-gtm/${gtm_version}_${gtm_install_flavor} ; fi if [ -d "$gtm_installdir" -a "Y" != "$gtm_overwrite_existing" ] ; then echo $gtm_installdir exists and --overwrite-existing not specified ; err_exit fi issystemd=`command -v systemctl` if [ -n "$issystemd" ] ; then # It is a systemd installation logindconf="/etc/systemd/logind.conf" removeipcopt=`awk -F = '/^RemoveIPC/ {opt=$2} END{print opt}' $logindconf` if [ "no" != "$removeipcopt" ] ; then # RemoveIPC=no is NOT the final settings in the file ipcissue1="If RemoveIPC=yes is configured for systemd, ipcs (database shm & sem)" ipcissue1="$ipcissue1 are removed for a non-system user's processes when that user logs out." ipcissue2="That can cause database operations to fail with mysterious errors." ipcline1="# GT.M : Override systemd default of RemoveIPC=yes to prevent automatic ipc removal of" ipcline1="$ipcline1 Shared Memory Segments and Semaphore Arrays of orphaned databases" ipcline2="RemoveIPC=no" ipccmd="systemctl restart systemd-logind" echo "$ipcissue1" echo "$ipcissue2" echo "The installation would like to add the below two lines to $logindconf" echo " $ipcline1" echo " $ipcline2" echo "And issue the below command to restart systemd-logind" echo " $ipccmd" if [ "N" != "$gtm_prompt_for_sys_reconfig" ]; then echo -n "Do you wish to proceed (Y/N)? " answer=`read_yes_no` if [ "yes" != "$answer" ] ; then echo "Will abort installation" echo $ipcissue1 echo $ipcissue2 echo "Please add the below two lines to $logindconf" echo " $ipcline1" echo " $ipcline2" echo "and restart systemd-logind using the below command, for example or by rebooting the system" echo " $ipccmd" echo "and retry GT.M installation" exit 1 fi echo $ipcline1 >> $logindconf echo $ipcline2 >> $logindconf $ipccmd else echo "===============================================================" echo "Installation directed to not change systemd configuration files" echo "===============================================================" fi fi fi if [ "Y" = "$gtm_verbose" ] ; then echo Finished checking options and assigning defaults ; dump_info ; fi # Prepare input to GT.M configure script gtm_configure_in=${gtm_tmp}/configure_${timestamp}.in if { ! $gtm_id -gn bin 2>/dev/null 1>/dev/null ; } then if [ "N" = "$gtm_prompt_for_group" -o 54002 -gt `echo $gtm_version | cut -s -d V -f 2- | tr -d A-Za-z.-` ] ; then echo y >>$gtm_configure_in echo root >>$gtm_configure_in echo $gtm_group_restriction >>$gtm_configure_in gtm_group_already="Y" fi fi echo $gtm_user >>$gtm_configure_in if [ "Y" = "$gtm_prompt_for_group" -o 54002 -le `echo $gtm_version | cut -s -d V -f 2- | tr -d A-Za-z.-` ] ; then echo $gtm_group >>$gtm_configure_in fi if [ "N" = "$gtm_group_already" ] ; then echo $gtm_group_restriction >>$gtm_configure_in fi echo $gtm_installdir >>$gtm_configure_in echo y >>$gtm_configure_in if [ -z "$gtm_install_utf8" ] ; then echo n >>$gtm_configure_in else echo y >>$gtm_configure_in fi echo $gtm_lcase_utils >>$gtm_configure_in if [ "Y" = $gtm_shlib_support ] ; then echo $gtm_keep_obj >>$gtm_configure_in ; fi echo y >>$gtm_configure_in if [ "Y" = "$gtm_verbose" ] ; then echo Prepared configuration file ; cat $gtm_configure_in ; dump_info ; fi # Run the GT.M configure script if [ "$gtm_distrib" != "$gtm_tmp" ] ; then chmod +w $gtm_tmp/tmp cd $gtm_tmp/tmp fi tmp=`head -1 configure | cut -f 1` if [ "#!/bin/sh" != "$tmp" ] ; then echo "#!/bin/sh" >configure.sh fi cat configure >>configure.sh chmod +x configure.sh # Stop here if this is a dry run if [ "Y" = "$gtm_dryrun" ] ; then echo Installation prepared in $gtm_tmp ; exit ; fi ./configure.sh <$gtm_configure_in 1> $gtm_tmp/configure_${timestamp}.out 2>$gtm_tmp/configure_${timestamp}.err if [ $? -gt 0 ] ; then cat $gtm_tmp/configure_${timestamp}.out $gtm_tmp/configure_${timestamp}.err ; fi if [ "Y" = "$gtm_verbose" ] ; then echo Installation complete ; ls -l $gtm_installdir ; fi # Create copies of environment scripts and gtm executable if [ -d "$gtm_linkenv" ] ; then ( cd $gtm_linkenv ; ln -s $gtm_installdir/gtmprofile $gtm_installdir/gtmcshrc ./ ) if [ "Y" = "$gtm_verbose" ] ; then echo Linked env ; ls -l $gtm_linkenv ; fi else if [ -d "$gtm_copyenv" ] ; then ( cd $gtm_copyenv ; cp $gtm_installdir/gtmprofile $gtm_installdir/gtmcshrc ./ ) if [ "Y" = "$gtm_verbose" ] ; then echo Copied env ; ls -l $gtm_copyenv ; fi fi fi if [ -d "$gtm_linkexec" ] ; then ( cd $gtm_linkexec ; ln -s $gtm_installdir/gtm ./ ) if [ "Y" = "$gtm_verbose" ] ; then echo Linked exec ; ls -l $gtm_linkexec ; fi else if [ -d "$gtm_copyexec" ] ; then ( cd $gtm_copyexec ; cp $gtm_installdir/gtm ./ ) if [ "Y" = "$gtm_verbose" ] ; then echo Copied exec ; ls -l $gtm_copyexec ; fi fi fi fis-gtm-V7.0-005/sr_unix/gtmio.h0000644000032200000250000007277714342376330015355 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ /* Define macros to do our IO and restart as appropriate * * IOCTL Loop until ioctl call succeeds or fails with other than EINTR. * OPENFILE Loop until open succeeds or fails with other than EINTR. * OPENFILE_SYNC * Loop until open succeeds or fails with other than EINTR. * Opens with O_DSYNC/O_SYNC in direct mode where possible. * Else opens only with O_DSYNC or O_SYNC. * OPEN_OBJECT_FILE Opens the object file. * CLOSE_OBJECT_FILE Close the object file. * CLOSEFILE Loop until close succeeds for fails with other than EINTR. * CLOSEFILE_RESET * Loop until close succeeds for fails with other than EINTR. * At end reset channel to FD_INVALID unconditionally (even if close was not successful). * CONVERT_OBJECT_LOCK - convert type of lock held on object file * LSEEKREAD Performs either pread() or an lseek()/ read() combination. In * the latter case, sets global variable to warn off async IO routines. * LSEEKREAD_AVAILABLE Same as LSEEKREAD except it has an extra parameter where the number of bytes * ACTUALLY READ are stored irrespective of whether all REQUESTED BYTES were read or not. * LSEEKWRITE Same as LSEEKREAD but for WRITE. * DOREADRC Performs read, returns code 0 if okay, otherwise returns errno. * DOREADRL Performs read but returns length read or -1 if errno is set. * DOREADRLTO2 Same as DOREADRL but has a timeout flag to poll on interrupts. * DOWRITE Performs write with no error checking/return. * DOWRITERC Performs write, returns code 0 if okay, otherwise returns errno. * DOWRITERL Performs write but returns length written or -1 if errno is set. * WRITEPIPE Performs write to FIFO/pipe but individual writes (within loop) limited to system pipe buffer size */ #ifndef GTMIO_Included #define GTMIO_Included /* Depends on aio_shim.h if using the POSIX or linux ASYNC IO #defines. This is due to aio_shim.h * requiring some header files with no include guards, and so we don't want to pull it in multiple * times by accident. */ #ifndef GTMIO_MINIMAL /* Avoid pulling in includes that make gtm_icu.c uncompilable */ # include # include "gtm_stat.h" # include "gtm_unistd.h" # include "gtm_fcntl.h" # include "eintr_wrappers.h" # include "min_max.h" # include "wbox_test_init.h" # include "get_fs_block_size.h" # include "min_max.h" #endif #include "stdio.h" #if defined(__linux__) || defined(__CYGWIN__) #include #include "is_fstype_nfs.h" #endif error_def(ERR_PREMATEOF); #ifdef KEEP_zOS_EBCDIC #define DOWRITE_A __write_a #define DOREAD_A __read_a #define DOWRITERL_A #error need to create as part of z/OS port and make __write_a return status and good errno #else #define DOWRITE_A DOWRITE #define DOREAD_A read #define DOWRITERL_A DOWRITERL #endif #define MAX_FILE_OPEN_TRIES 20 /* polling count */ #define WAIT_FOR_FILE_TIME 100 /* msec */ #define WAIT_FOR_BLOCK_TIME 100 /* msec */ #define IOCTL(FDESC, REQUEST, ARG, RC) \ MBSTART { \ do \ { \ RC = ioctl(FDESC, REQUEST, ARG); \ } while (-1 == RC && EINTR == errno); \ if (-1 != RC) \ RC = 0; \ else \ RC = errno; \ } MBEND #define OPENFILE(FNAME, FFLAGS, FDESC) \ MBSTART { \ do \ { \ FDESC = OPEN(FNAME, FFLAGS); \ } while (-1 == FDESC && EINTR == errno); \ } MBEND #define OPENFILE3(FNAME, FFLAGS, FMODE, FDESC) \ MBSTART { \ do \ { \ FDESC = OPEN3(FNAME, FFLAGS, FMODE); \ } while (-1 == FDESC && EINTR == errno); \ } MBEND /* OPENFILE4 not needed - io_open_try handles interrupts */ /* This macro is used when we need to set FD_CLOEXEC whether or not the platform supports O_CLOEXEC. * This is used for system calls, e.g, pipe, that do not support O_CLOEXEC. */ #define SETFDCLOEXECALWAYS(FDESC) \ MBSTART { \ int flags, fcntl_res; \ if (-1 != FDESC) \ { \ flags = 0; \ FCNTL2(FDESC, F_GETFD, flags); \ if (-1 != flags) \ FCNTL3(FDESC, F_SETFD, (flags | FD_CLOEXEC), fcntl_res);\ } \ } MBEND /* Versions of the OPEN* macros that cause the file descriptor to be closed before an EXEC. */ /* If the platform really supports O_CLOEXEC use it in the OPEN */ #if defined(O_CLOEXEC) && !defined(_AIX) && !defined(__sparc) #define OPEN_CLOEXEC(FNAME, FFLAGS, FDESC) \ MBSTART { \ FDESC = OPEN(FNAME, FFLAGS | O_CLOEXEC);\ } MBEND #define OPEN3_CLOEXEC(FNAME, FFLAGS, FMODE, FDESC) \ MBSTART { \ do \ { \ FDESC = OPEN3(FNAME, FFLAGS | O_CLOEXEC, FMODE); \ } while (-1 == FDESC && EINTR == errno); \ } MBEND #define OPENFILE_CLOEXEC(FNAME, FFLAGS, FDESC) OPENFILE(FNAME, FFLAGS | O_CLOEXEC, FDESC); #define OPENFILE_SYNC_CLOEXEC(FNAME, FFLAGS, FDESC) OPENFILE_SYNC(FNAME, FFLAGS | O_CLOEXEC, FDESC); #define OPENFILE3_CLOEXEC(FNAME, FFLAGS, FMODE, FDESC) OPENFILE3(FNAME, FFLAGS | O_CLOEXEC, FMODE, FDESC); /* The next two macros are used when the open command needs to return a value and is used as part of control statement. * See comment in io_open_try.c. Between the two macros (see the else case versions) either O_CLOEXEC or FD_CLOEXEC is * used (depending on whether the platform supports O_CLOEXEC (preferred)). */ #define SETOCLOEXEC(FFLAGS) (FFLAGS | O_CLOEXEC) #define SETSOCKCLOEXEC(FFLAGS) (FFLAGS | SOCK_CLOEXEC) /* Since the platform support O_CLOEXEC via the OPEN* no need for FD_CLOEXEC */ #define SETFDCLOEXEC(FDESC) #else /* If the platform does not support O_CLOEXEC, use fcntl with FD_CLOEXEC */ #define OPEN_CLOEXEC(FNAME, FFLAGS, FDESC) \ MBSTART { \ int flags, fcntl_res; \ FDESC = OPEN(FNAME, FFLAGS); \ if (-1 != FDESC) \ { \ flags = 0; \ FCNTL2(FDESC, F_GETFD, flags); \ if (-1 != flags) \ FCNTL3(FDESC, F_SETFD, (flags | FD_CLOEXEC), fcntl_res);\ } \ } MBEND #define OPEN3_CLOEXEC(FNAME, FFLAGS, FMODE, FDESC) \ MBSTART { \ int flags, fcntl_res; \ FDESC = OPEN3(FNAME, FFLAGS, FMODE); \ if (-1 != FDESC) \ { \ flags = 0; \ FCNTL2(FDESC, F_GETFD, flags); \ if (-1 != flags) \ FCNTL3(FDESC, F_SETFD, (flags | FD_CLOEXEC), fcntl_res);\ } \ } MBEND #define OPENFILE_CLOEXEC(FNAME, FFLAGS, FDESC) \ MBSTART { \ int flags, fcntl_res; \ OPENFILE(FNAME, FFLAGS, FDESC); \ if (-1 != FDESC) \ { \ flags = 0; \ FCNTL2(FDESC, F_GETFD, flags); \ if (-1 != flags) \ FCNTL3(FDESC, F_SETFD, (flags | FD_CLOEXEC), fcntl_res);\ } \ } MBEND #define OPENFILE_SYNC_CLOEXEC(FNAME, FFLAGS, FDESC) \ MBSTART { \ int flags, fcntl_res; \ OPENFILE_SYNC(FNAME, FFLAGS, FDESC); \ if (-1 != FDESC) \ { \ flags = 0; \ FCNTL2(FDESC, F_GETFD, flags); \ if (-1 != flags) \ FCNTL3(FDESC, F_SETFD, (flags | FD_CLOEXEC), fcntl_res);\ } \ } MBEND #define OPENFILE3_CLOEXEC(FNAME, FFLAGS, FMODE, FDESC) \ MBSTART { \ int flags, fcntl_res; \ OPENFILE3(FNAME, FFLAGS, FMODE, FDESC); \ if (-1 != FDESC) \ { \ flags = 0; \ FCNTL2(FDESC, F_GETFD, flags); \ if (-1 != flags) \ FCNTL3(FDESC, F_SETFD, (flags | FD_CLOEXEC), fcntl_res);\ } \ } MBEND #define SETOCLOEXEC(FFLAGS) (FFLAGS) #define SETSOCKCLOEXEC(FFLAGS) (FFLAGS) #define SETFDCLOEXEC(FDESC) \ MBSTART { \ SETFDCLOEXECALWAYS(FDESC); \ } MBEND #endif #define OPENFILE_DB(FNAME, FFLAGS, UDI, SEG) \ MBSTART { \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ if (!IS_AIO_ON_SEG(SEG)) \ { \ OPENFILE_CLOEXEC(FNAME, FFLAGS, UDI->fd); \ UDI->fd_opened_with_o_direct = FALSE; \ } else \ { \ OPENFILE_SYNC_CLOEXEC(FNAME, FFLAGS, UDI->fd); \ if (FD_INVALID != udi->fd) \ { \ UDI->fd_opened_with_o_direct = TRUE; \ /* Get filesystem block size and use that to align future reads/writes */ \ UDI->db_fs_block_size = get_fs_block_size(UDI->fd); \ /* Until we read the db file header, we do not know the blocksize so allocate space \ * initially to read the db file header. Later we can expand this to fit a GDS block \ * in case that turns out to be bigger than the file header. The global variable \ * "dio_buff" is filled in with the aligned/unaligned buffer details. \ */ \ DIO_BUFF_EXPAND_IF_NEEDED(UDI, SGMNT_HDR_LEN, &(TREF(dio_buff))); \ } \ } \ } MBEND #define FSTYPE_ADVFS "advfs" #define FSTYPE_UFS "ufs" #if defined(__MVS__) # define O_DIRECT_FLAGS (O_SYNC) #else # define O_DIRECT_FLAGS (O_DIRECT | O_DSYNC) #endif #define OPENFILE_SYNC(FNAME, FFLAGS, FDESC) OPENFILE(FNAME, FFLAGS | O_DIRECT_FLAGS, FDESC); #if defined( __linux__) || defined(__CYGWIN__) /* A special handling was needed for linux due to its inability to lock * over NFS. The only difference in code is an added check for NFS file * thru fstatfs */ #define LOCK_IS_ALLOWED(FDESC, STATUS) \ MBSTART { \ struct statfs buf; \ STATUS = ((-1 != fstatfs(FDESC, &buf)) && (NFS_SUPER_MAGIC != buf.f_type)) ? 0 : -2; \ } MBEND #else #define LOCK_IS_ALLOWED(FDESC, STATUS) STATUS = 0 #endif #define OPEN_OBJECT_FILE(FNAME, FFLAG, FDESC) OPENFILE(FNAME, FFLAG, FDESC) #define CLOSE_OBJECT_FILE(FDESC, RC) CLOSEFILE_RESET(FDESC, RC) #define CLOSEFILE(FDESC, RC) \ MBSTART { \ do \ { \ RC = close(FDESC); \ } while (-1 == RC && EINTR == errno); \ if (-1 == RC) /* Had legitimate error - return it */ \ RC = errno; \ } MBEND #define CLOSEFILE_RESET(FDESC, RC) \ MBSTART { \ CLOSEFILE(FDESC, RC); \ FDESC = FD_INVALID; \ } MBEND /* Close file only if we have it open. Use FCNTL to check if we have it open */ #define CLOSEFILE_IF_OPEN(FDESC, RC) \ MBSTART { \ int flags; \ \ FCNTL2(FDESC, F_GETFL, flags); \ if ((-1 != flags) || (EBADF != errno)) \ CLOSEFILE(FDESC, RC); /* file is a valid descriptor. Close it */ \ } MBEND #define LSEEKREAD(FDESC, FPTR, FBUFF, FBUFF_LEN, RC) \ MBSTART { \ ssize_t gtmioStatus; \ size_t gtmioBuffLen; \ off_t gtmioPtr; \ sm_uc_ptr_t gtmioBuff; \ gtmioBuffLen = FBUFF_LEN; \ gtmioBuff = (sm_uc_ptr_t)(FBUFF); \ gtmioPtr = (off_t)(FPTR); \ for (;;) \ { \ if (-1 != (gtmioStatus = pread(FDESC, gtmioBuff, gtmioBuffLen, gtmioPtr))) \ { \ gtmioBuffLen -= gtmioStatus; \ if (0 == gtmioBuffLen || 0 == gtmioStatus) \ break; \ gtmioBuff += gtmioStatus; \ gtmioPtr += gtmioStatus; \ continue; \ } \ if (EINTR != errno) \ break; \ } \ if (0 == gtmioBuffLen) \ RC = 0; \ else if (-1 == gtmioStatus) /* Had legitimate error - return it */ \ RC = errno; \ else \ RC = -1; /* Something kept us from reading what we wanted */ \ } MBEND #define DB_LSEEKREAD(UDI, FD, OFFSET, BUFF, SIZE, STATUS) \ MBSTART { \ DBG_CHECK_DIO_ALIGNMENT(UDI, OFFSET, BUFF, SIZE); \ LSEEKREAD(FD, OFFSET, BUFF, SIZE, STATUS); \ } MBEND /* The below macro is almost the same as LSEEKREAD except it has an extra parameter where the number of * bytes ACTUALLY READ are stored irrespective of whether all REQUESTED BYTES were read or not. */ #define LSEEKREAD_AVAILABLE(FDESC, FPTR, FBUFF, FBUFF_LEN, ACTUAL_READLEN, RC) \ MBSTART { \ ssize_t gtmioStatus; \ size_t gtmioBuffLen; \ off_t gtmioPtr; \ sm_uc_ptr_t gtmioBuff; \ \ gtmioBuffLen = (FBUFF_LEN); \ gtmioBuff = (sm_uc_ptr_t)(FBUFF); \ gtmioPtr = (off_t)(FPTR); \ for (;;) \ { \ if (-1 != (gtmioStatus = pread(FDESC, gtmioBuff, gtmioBuffLen, gtmioPtr))) \ { \ gtmioBuffLen -= gtmioStatus; \ if (0 == gtmioBuffLen || 0 == gtmioStatus) \ break; \ gtmioBuff += gtmioStatus; \ gtmioPtr += gtmioStatus; \ continue; \ } \ if (EINTR != errno) \ break; \ } \ (ACTUAL_READLEN) = (FBUFF_LEN) - gtmioBuffLen; \ if (0 == gtmioBuffLen) \ RC = 0; \ else if (-1 == gtmioStatus) /* Had legitimate error - return it */ \ RC = errno; \ else \ RC = -1; /* Something kept us from reading what we wanted */ \ } MBEND #define LSEEKWRITEASYNCSTART(CSA, FDESC, FPTR, FBUFF, FBUFF_LEN, CR, RC) \ MBSTART { \ memset(&CR->aiocb, 0, SIZEOF(struct aiocb)); \ CR->aiocb.aio_nbytes = (size_t) FBUFF_LEN; \ CR->aiocb.aio_offset = (off_t) FPTR; \ LSEEKWRITEASYNCRESTART(CSA, FDESC, FBUFF, CR, RC); \ } MBEND #define LSEEKWRITEASYNCRESTART(CSA, FDESC, FBUFF, CR, RC) \ MBSTART { \ GBLREF boolean_t async_restart_got_eagain; \ ssize_t gtmioStatus; \ \ CR->aiocb.aio_buf = IF_LIBAIO((unsigned long)) FBUFF; \ CR->aiocb.aio_fildes = FDESC; \ assert(0 < CR->aiocb.aio_nbytes); \ assert(0 < CR->aiocb.aio_offset); \ AIO_SHIM_WRITE(CSA->region, &(CR->aiocb), gtmioStatus); \ if (0 == gtmioStatus) \ RC = 0; \ else if (-1 == gtmioStatus) /* Had legitimate error - return it */ \ RC = errno; \ else \ { \ assert(FALSE); \ RC = -1; /* Something kept us from initiating the write */ \ } \ assert((-1 < gtmioStatus) IF_LIBAIO(|| (EAGAIN == RC))); \ if (EAGAIN == RC) \ BG_TRACE_PRO_ANY(CSA, async_restart_eagain); \ } MBEND #define LSEEKWRITE(FDESC, FPTR, FBUFF, FBUFF_LEN, RC) \ MBSTART { \ ssize_t gtmioStatus; \ size_t gtmioBuffLen; \ off_t gtmioPtr; \ sm_uc_ptr_t gtmioBuff; \ \ gtmioBuffLen = FBUFF_LEN; \ gtmioBuff = (sm_uc_ptr_t)(FBUFF); \ gtmioPtr = (off_t)(FPTR); \ for (;;) \ { \ if (-1 != (gtmioStatus = pwrite(FDESC, gtmioBuff, gtmioBuffLen, gtmioPtr))) \ { \ gtmioBuffLen -= gtmioStatus; \ if (0 == gtmioBuffLen) \ break; \ gtmioBuff += gtmioStatus; \ gtmioPtr += gtmioStatus; \ continue; \ } \ if (EINTR != errno) \ break; \ } \ if (0 == gtmioBuffLen) \ RC = 0; \ else if (-1 == gtmioStatus) /* Had legitimate error - return it */ \ RC = errno; \ else \ RC = -1; /* Something kept us from writing what we wanted */ \ } MBEND #define DOREADRC(FDESC, FBUFF, FBUFF_LEN, RC) \ MBSTART { \ ssize_t gtmioStatus; \ size_t gtmioBuffLen; \ sm_uc_ptr_t gtmioBuff; \ gtmioBuffLen = FBUFF_LEN; \ gtmioBuff = (sm_uc_ptr_t)(FBUFF); \ for (;;) \ { \ if (-1 != (gtmioStatus = read(FDESC, gtmioBuff, gtmioBuffLen))) \ { \ gtmioBuffLen -= gtmioStatus; \ if (0 == gtmioBuffLen || 0 == gtmioStatus) \ break; \ gtmioBuff += gtmioStatus; \ } \ else if (EINTR != errno) \ break; \ } \ if (-1 == gtmioStatus) /* Had legitimate error - return it */ \ RC = errno; \ else if (0 == gtmioBuffLen) \ RC = 0; \ else \ RC = -1; /* Something kept us from reading what we wanted */ \ } MBEND #define DOREADRL(FDESC, FBUFF, FBUFF_LEN, RLEN) \ MBSTART { \ ssize_t gtmioStatus; \ size_t gtmioBuffLen; \ sm_uc_ptr_t gtmioBuff; \ gtmioBuffLen = FBUFF_LEN; \ gtmioBuff = (sm_uc_ptr_t)(FBUFF); \ for (;;) \ { \ if (-1 != (gtmioStatus = read(FDESC, gtmioBuff, gtmioBuffLen))) \ { \ gtmioBuffLen -= gtmioStatus; \ if (0 == gtmioBuffLen || 0 == gtmioStatus) \ break; \ gtmioBuff += gtmioStatus; \ } \ else if (EINTR != errno) \ break; \ } \ if (-1 != gtmioStatus) \ RLEN = (int)(FBUFF_LEN - gtmioBuffLen); /* Return length actually read */ \ else /* Had legitimate error - return it */ \ RLEN = -1; \ } MBEND #define DOREADRLTO2(FDESC, FBUFF, FBUFF_LEN, TOFLAG, BLOCKED_IN, ISPIPE, FLAGS, RLEN, \ TOT_BYTES_READ, TIMER_ID, MSEC_TIMEOUT, PIPE_ZERO_TIMEOUT, UTF_VAR_PF, PIPE_OR_FIFO) \ MBSTART { \ ssize_t gtmioStatus; \ int skip_read = FALSE; \ int tfcntl_res; \ size_t gtmioBuffLen; \ sm_uc_ptr_t gtmioBuff; \ gtmioBuffLen = (size_t)FBUFF_LEN; \ gtmioBuff = (sm_uc_ptr_t)(FBUFF); \ for (;;) \ { \ /* if it is a read x:0 on a pipe and it is not blocked (always the case when starting a read) \ then try and read one char. If it succeeds then turn on blocked io and read the rest \ of the line.*/ \ if (ISPIPE && (FALSE == *BLOCKED_IN)) \ { \ for (;;) \ { \ if (-1 != (gtmioStatus = read(FDESC, gtmioBuff, 1))) \ { \ if (0 == gtmioStatus) /* end of file */ \ { \ skip_read = TRUE; \ break; \ } \ FCNTL3(FDESC, F_SETFL, FLAGS, tfcntl_res); \ if (0 > tfcntl_res) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, \ 5, LEN_AND_LIT("fcntl"), CALLFROM, errno); \ *BLOCKED_IN = TRUE; \ if (PIPE_ZERO_TIMEOUT) \ { \ TOFLAG = FALSE; \ /* Set a timer for 1 sec so atomic read x:0 will still work on \ loaded systems but timeout on incomplete reads. \ Any characters \ read to this point will be returned. */ \ *MSEC_TIMEOUT = 1 * MILLISECS_IN_SEC; \ start_timer(TIMER_ID, *MSEC_TIMEOUT, wake_alarm, 0, NULL); \ } \ gtmioBuffLen -= gtmioStatus; \ gtmioBuff += gtmioStatus; \ /* if only asked to read 1 character then skip additional read */ \ if (0 == gtmioBuffLen) \ skip_read = TRUE; \ break; \ } else if (EINTR != errno || TOFLAG) \ { \ skip_read = TRUE; \ break; \ } \ } \ } \ /* if we didn't read 1 character or it's an error don't read anymore now */ \ if (skip_read) \ break; \ if (-1 != (gtmioStatus = read(FDESC, gtmioBuff, gtmioBuffLen))) \ { \ gtmioBuffLen -= gtmioStatus; \ if (0 == gtmioBuffLen || 0 == gtmioStatus) \ break; \ gtmioBuff += gtmioStatus; \ /* If it is pipe or fifo, read data that is currently available. If pipe contains data less than \ the CHUNK_SIZE, no need to read once again since it is in BLOCKING mode, in which case it will return -1. \ So in the first read itself (after a successful read) break from the infinite loop. This variable is TRUE \ if DOREADRLTO2 macro is called for a CHUNK_SIZE read. In other places (eg: iorm_get) it will be FALSE. \ */ \ if (UTF_VAR_PF) \ break; \ } else if (EINTR != errno || TOFLAG) \ break; \ if (PIPE_OR_FIFO && outofband) \ break; \ } \ if (-1 != gtmioStatus) \ RLEN = (int)(FBUFF_LEN - gtmioBuffLen); /* Return length actually read */ \ else /* Had legitimate error - return it */ \ { \ /* Store the number of bytes read in this invocation before we error out */ \ *TOT_BYTES_READ = (int)(FBUFF_LEN - gtmioBuffLen); \ RLEN = -1; \ } \ } MBEND #define DOWRITE(FDESC, FBUFF, FBUFF_LEN) \ MBSTART { \ ssize_t gtmioStatus; \ size_t gtmioBuffLen; \ sm_uc_ptr_t gtmioBuff; \ gtmioBuffLen = FBUFF_LEN; \ gtmioBuff = (sm_uc_ptr_t)(FBUFF); \ assert(0 != gtmioBuffLen); \ for (;;) \ { \ if (-1 != (gtmioStatus = write(FDESC, gtmioBuff, gtmioBuffLen))) \ { \ gtmioBuffLen -= gtmioStatus; \ if (0 == gtmioBuffLen) \ break; \ gtmioBuff += gtmioStatus; \ } else if (EINTR != errno) \ break; \ } \ /* assertpro(FALSE)? */ \ } MBEND #define DOWRITERC_RM(RM, FBUFF, FBUFF_LEN, RC) \ MBSTART { \ if (0 == RM->fsblock_buffer_size) \ DOWRITERC(RM->fildes, FBUFF, FBUFF_LEN, RC); \ else \ { \ GBLREF int gtm_non_blocked_write_retries; \ ssize_t gtmioStatus; \ size_t gtmioBuffLen; \ sm_uc_ptr_t gtmioBuff; \ int block_cnt = 0; \ intrpt_state_t prev_intrpt_state; \ \ gtmioBuffLen = FBUFF_LEN; \ gtmioBuff = (sm_uc_ptr_t)(FBUFF); \ for (;;) \ { \ DEFER_INTERRUPTS(INTRPT_IN_IO_WRITE, prev_intrpt_state); \ gtmioStatus = fwrite(gtmioBuff, 1, gtmioBuffLen, RM->filstr); /* BYPASSOK("fwrite") */ \ ENABLE_INTERRUPTS(INTRPT_IN_IO_WRITE, prev_intrpt_state); \ if (gtmioBuffLen >= gtmioStatus) \ { \ gtmioBuffLen -= gtmioStatus; \ if (0 == gtmioBuffLen) \ break; \ gtmioBuff += gtmioStatus; \ } \ else if (EINTR != errno && EAGAIN != errno) \ break; \ else if (EAGAIN == errno) \ { \ if (gtm_non_blocked_write_retries <= block_cnt) \ break; \ SHORT_SLEEP(WAIT_FOR_BLOCK_TIME); \ block_cnt++; \ } \ } \ if (0 == gtmioBuffLen) \ RC = 0; \ else \ RC = errno; /* Something kept us from writing what we wanted */ \ } \ } MBEND #define DOWRITERC(FDESC, FBUFF, FBUFF_LEN, RC) \ MBSTART { \ GBLREF int gtm_non_blocked_write_retries; \ ssize_t gtmioStatus; \ size_t gtmioBuffLen; \ sm_uc_ptr_t gtmioBuff; \ int block_cnt = 0; \ gtmioBuffLen = FBUFF_LEN; \ gtmioBuff = (sm_uc_ptr_t)(FBUFF); \ for (;;) \ { \ if (-1 != (gtmioStatus = write(FDESC, gtmioBuff, gtmioBuffLen))) \ { \ gtmioBuffLen -= gtmioStatus; \ if (0 == gtmioBuffLen) \ break; \ gtmioBuff += gtmioStatus; \ } \ else if (EINTR != errno && EAGAIN != errno) \ break; \ else if (EAGAIN == errno) \ { \ if (gtm_non_blocked_write_retries <= block_cnt) \ break; \ SHORT_SLEEP(WAIT_FOR_BLOCK_TIME); \ block_cnt++; \ } \ } \ if (-1 == gtmioStatus) /* Had legitimate error - return it */ \ RC = errno; \ else if (0 == gtmioBuffLen) \ RC = 0; \ else \ RC = -1; /* Something kept us from writing what we wanted */ \ } MBEND #define DOWRITERL_RM(RM, FBUFF, FBUFF_LEN, RLEN) \ MBSTART { \ if (0 == RM->fsblock_buffer_size) \ DOWRITERL(RM->fildes, FBUFF, FBUFF_LEN, RLEN); \ else \ { \ ssize_t gtmioStatus; \ size_t gtmioBuffLen; \ sm_uc_ptr_t gtmioBuff; \ int block_cnt = 0; \ intrpt_state_t prev_intrpt_state; \ \ GBLREF int gtm_non_blocked_write_retries; \ \ gtmioBuffLen = FBUFF_LEN; \ gtmioBuff = (sm_uc_ptr_t)(FBUFF); \ for (;;) \ { \ DEFER_INTERRUPTS(INTRPT_IN_IO_WRITE, prev_intrpt_state); \ gtmioStatus = fwrite(gtmioBuff, 1, gtmioBuffLen, RM->filstr); /* BYPASSOK("fwrite") */ \ ENABLE_INTERRUPTS(INTRPT_IN_IO_WRITE, prev_intrpt_state); \ if (gtmioBuffLen >= gtmioStatus) \ { \ gtmioBuffLen -= gtmioStatus; \ if (0 == gtmioBuffLen) \ break; \ gtmioBuff += gtmioStatus; \ } \ else if (EINTR != errno && EAGAIN != errno) \ break; \ else if (EAGAIN == errno) \ { \ if (gtm_non_blocked_write_retries <= block_cnt) \ break; \ SHORT_SLEEP(WAIT_FOR_BLOCK_TIME); \ block_cnt++; \ } \ } \ if (0 < gtmioStatus) \ RLEN = (int)(FBUFF_LEN - gtmioBuffLen); /* Return length actually written */ \ else /* Had legitimate error - return it */ \ RLEN = -1; \ } \ } MBEND #define DOWRITERL(FDESC, FBUFF, FBUFF_LEN, RLEN) \ MBSTART { \ ssize_t gtmioStatus; \ size_t gtmioBuffLen; \ sm_uc_ptr_t gtmioBuff; \ int block_cnt = 0; \ \ GBLREF int gtm_non_blocked_write_retries; \ \ gtmioBuffLen = FBUFF_LEN; \ gtmioBuff = (sm_uc_ptr_t)(FBUFF); \ for (;;) \ { \ if (-1 != (gtmioStatus = write(FDESC, gtmioBuff, gtmioBuffLen))) \ { \ gtmioBuffLen -= gtmioStatus; \ if (0 == gtmioBuffLen) \ break; \ gtmioBuff += gtmioStatus; \ } \ else if (EINTR != errno && EAGAIN != errno) \ break; \ else if (EAGAIN == errno) \ { \ if (gtm_non_blocked_write_retries <= block_cnt) \ break; \ SHORT_SLEEP(WAIT_FOR_BLOCK_TIME); \ block_cnt++; \ } \ } \ if (-1 != gtmioStatus) \ RLEN = (int)(FBUFF_LEN - gtmioBuffLen); /* Return length actually written */ \ else /* Had legitimate error - return it */ \ RLEN = -1; \ } MBEND #define DO_FILE_READ(CHANNEL, OFFSET, READBUFF, LEN, STATUS1, STATUS2) \ MBSTART { \ STATUS2 = SS_NORMAL; \ LSEEKREAD(CHANNEL, OFFSET, READBUFF, LEN, STATUS1); \ if (-1 == STATUS1) \ STATUS1 = ERR_PREMATEOF; \ } MBEND #define DB_DO_FILE_WRITE(CHANNEL, OFFSET, WRITEBUFF, LEN, STATUS1, STATUS2) \ MBSTART { \ STATUS2 = SS_NORMAL; \ DB_LSEEKWRITE(NULL, ((unix_db_info *)NULL), NULL, CHANNEL, OFFSET, WRITEBUFF, LEN, STATUS1); \ if (-1 == STATUS1) \ STATUS1 = ERR_PREMATEOF; \ } MBEND #define JNL_DO_FILE_WRITE(CSA, JNL_FN, CHANNEL, OFFSET, WRITEBUFF, LEN, STATUS1, STATUS2) \ MBSTART { \ STATUS2 = SS_NORMAL; \ JNL_LSEEKWRITE(CSA, JNL_FN, CHANNEL, OFFSET, WRITEBUFF, LEN, STATUS1); \ if (-1 == STATUS1) \ STATUS1 = ERR_PREMATEOF; \ } MBEND typedef struct { int fd; mstr v; } file_pointer; /* WRITEPIPE is a work-around for a problem found in z/OS where the kernel doesn't seem to * break up writes into small enough pieces, requiring that we do it ourselves. The fix is * applied to all Unix platforms, even though all except z/OS seem to work with monster writes. */ #define WRITEPIPE(FDESC, PIPESZ, FBUFF, FBUFF_LEN, RC) \ MBSTART { \ GBLREF int gtm_non_blocked_write_retries; \ ssize_t gtmioStatus; \ size_t gtmioBuffLen; \ size_t shortBuffLen; \ sm_uc_ptr_t gtmioBuff; \ int block_cnt = 0; \ gtmioBuffLen = FBUFF_LEN; \ gtmioBuff = (sm_uc_ptr_t)(FBUFF); \ for (;;) \ { \ shortBuffLen = MIN(PIPESZ, gtmioBuffLen); \ if (-1 != (gtmioStatus = write(FDESC, gtmioBuff, shortBuffLen))) \ { \ gtmioBuffLen -= gtmioStatus; \ if (0 == gtmioBuffLen) \ break; \ gtmioBuff += gtmioStatus; \ } \ else if (EINTR != errno && EAGAIN != errno) \ break; \ else if (EAGAIN == errno) \ { \ if (gtm_non_blocked_write_retries <= block_cnt) \ break; \ SHORT_SLEEP(WAIT_FOR_BLOCK_TIME); \ block_cnt++; \ } \ } \ if (-1 == gtmioStatus) /* Had legitimate error - return it */ \ RC = errno; \ else if (0 == gtmioBuffLen) \ RC = 0; \ else \ RC = -1; /* Something kept us from writing what we wanted */ \ } MBEND #define FFLUSH(STREAM) \ MBSTART { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_FFLUSH, prev_intrpt_state); \ fflush(STREAM); \ ENABLE_INTERRUPTS(INTRPT_IN_FFLUSH, prev_intrpt_state); \ } MBEND /* Macros to deal with calls which are not async-signal-safe */ #define GETC(STREAM, RC) \ MBSTART { \ GBLREF boolean_t multi_thread_in_use; \ char *rname; \ intrpt_state_t prev_intrpt_state; \ /* Use the right system call based on threads are in use or not */ \ DEFER_INTERRUPTS(INTRPT_IN_GETC, prev_intrpt_state); \ if (!INSIDE_THREADED_CODE(rname)) \ RC = getc_unlocked(STREAM); \ else \ RC = getc(STREAM); \ ENABLE_INTERRUPTS(INTRPT_IN_GETC, prev_intrpt_state); \ } MBEND #define CLEARERR(STREAM) \ MBSTART { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_IO_READ, prev_intrpt_state); \ clearerr(STREAM); \ ENABLE_INTERRUPTS(INTRPT_IN_IO_READ, prev_intrpt_state); \ } MBEND #define FEOF(STREAM, RC) \ MBSTART { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_IO_READ, prev_intrpt_state); \ RC = feof(STREAM); \ ENABLE_INTERRUPTS(INTRPT_IN_IO_READ, prev_intrpt_state); \ } MBEND #endif fis-gtm-V7.0-005/sr_unix/gtmio_ch.c0000644000032200000250000000131714342376330016000 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2019 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 "io.h" #include "error.h" GBLREF boolean_t in_prin_gtmio, prin_dm_io; CONDITION_HANDLER(gtmio_ch) { START_CH(TRUE); in_prin_gtmio = FALSE; prin_dm_io = FALSE; NEXTCH; } fis-gtm-V7.0-005/sr_unix/gtmlink.c0000644000032200000250000000326214342376330015655 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2017 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_caseconv.h" #include "gtm_string.h" #include "gtmlink.h" LITDEF mstr relink_allowed_mstr[] = { {0, LEN_AND_LIT("NORECURSIVE")}, {0, LEN_AND_LIT("RECURSIVE")}, /* if env var $gtm_link = "RECURSIVE", then recursive relink is enabled */ {0, LEN_AND_LIT("")} }; #define MAX_KEYWORD_LEN 16 void init_relink_allowed(mstr *keyword) { mstr trans; char buf[MAX_KEYWORD_LEN]; int i; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DEBUG_ONLY(check_max_keyword_len()); if ((keyword->len >= MAX_KEYWORD_LEN) || (0 == keyword->len)) { /* unrecognized keyword - use default */ TREF(relink_allowed) = LINK_NORECURSIVE; return; } lower_to_upper((unsigned char *)&buf[0], (unsigned char *)keyword->addr, keyword->len); trans.len = keyword->len; trans.addr = &buf[0]; for (i = 0; i < LINK_MAXTYPE; i++) { if (MSTR_EQ(&trans, &relink_allowed_mstr[i])) { TREF(relink_allowed) = i; return; } } /* unrecognized keyword - use default */ TREF(relink_allowed) = LINK_NORECURSIVE; return; } #ifdef DEBUG void check_max_keyword_len(void) { int i; for (i = 0; i < LINK_MAXTYPE; i++) assert(MAX_KEYWORD_LEN > relink_allowed_mstr[i].len); } #endif fis-gtm-V7.0-005/sr_unix/gtmlink.h0000644000032200000250000000131214342376330015654 0ustar librarygtc/**************************************************************** * * * Copyright 2013, 2014 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. * * * ****************************************************************/ #ifndef GTMLINK_H_INCLUDED #define GTMLINK_H_INCLUDED enum gtm_link_type { LINK_NORECURSIVE = 0, LINK_RECURSIVE, LINK_MAXTYPE }; void init_relink_allowed(mstr *keyword); #ifdef DEBUG void check_max_keyword_len(void); #endif #endif /* GTMLINK_H_INCLUDED */ fis-gtm-V7.0-005/sr_unix/gtmpcat_sh.txt0000644000032200000250000000127614342376330016741 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright (c) 2017 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. # # # ################################################################# gtm_chset=M gtm_boolean=0 gtm_side_effects=0 gtm_max_indrcache_count=2048 gtm_max_indrcache_memory=8192 \ gtmroutines="$gtm_dist/tools/gtmpcat $gtmroutines" $gtm_dist/mumps -r gtmpcat "$@" fis-gtm-V7.0-005/sr_unix/gtmprofile.gtc0000755000032200000250000003544614342376330016727 0ustar librarygtc################################################################# # # # Copyright (c) 2010-2021 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. # # # ################################################################# # Usage: source "GTMDIST"/gtmprofile # Sets up environment for single-user use of GT.M with reasonable defaults. # Explicitly specify gtm_chset=UTF-8 before running this for UTF-8 mode. # Explicitly specify the gtmdir=directory before sourcing this for simple multi-user environments. # System administrators should adapt as appropriate for more complex multi-user and production environments. # Expects the following environment variables to be set: # $gtm_chset - set to "UTF-8" if GT.M is to run in UTF8 mode # $gtm_passwd and $gtm_dbkeys if database encryption is used # Attempts to set the following environment variables to reasonable values: # $gtm_dist $gtmgbldir $gtm_icu_version $gtm_log $gtm_principal_editing $gtm_prompt $gtm_retention $gtmroutines $gtm_tmp $gtmver # The following shell variables are used and left unset: # $old_gtm_dist $old_gtmroutines $old_gtmver $tmp_gtm_shlib $tmp_gtm_tmp $tmp_passwd # The following shell variables are used and left set: # $arch # ; is used as a separator for sed as being the character most unlikely to be in file names # Set gtm_arch - GT.M installation script sets the actual architecture & shared library extension for use in this script arch="ARCH" # Save gtmroutines if it exists because we need to temporarily override it if [ -n "$gtmroutines" ] ; then old_gtmroutines="$gtmroutines" else unset old_gtmroutines fi # Save gtm_passwd if defined because we don't want to mess with prompting for a password within this script # Need to distinguish between $gtm_passwd not set at all vs. set to null string if [ -z "${gtm_passwd+isset}" ] ; then unset tmp_passwd else tmp_passwd=$gtm_passwd ; unset gtm_passwd fi # If an existing GT.M is defined in $gtm_dist, capture it to change version number & directory in environment variables # Since %XCMD is new, cannot assume that old GT.M version has it if [ -n "$gtm_dist" ] ; then old_gtm_dist=$gtm_dist if [ -f $gtm_dist/_XCMD.m ] ; then tmp_gtm_shlib=`basename $gtm_dist/libgtmshr.* | cut -d . -f 2` ; export tmp_gtm_shlib if [ -f "$gtm_dist/libgtmutil.$tmp_gtm_shlib" ] ; then gtmroutines="$gtm_dist/libgtmutil.$tmp_gtm_shlib" else gtmroutines=$gtm_dist ; fi export gtmroutines old_gtmver=`$gtm_dist/mumps -run %XCMD 'Write $Piece($ZVersion," ",2),"_",$Piece($ZVersion," ",4)'` else unset old_gtmver fi else unset old_gtm_dist old_gtmver fi # Set gtm_dist - GT.M installation script sets the actual directory name gtm_dist="GTMDIST" ; export gtm_dist # Determine name of shared library file name extension on this platform tmp_gtm_shlib=`basename $gtm_dist/libgtmshr.* | cut -d . -f 2` ; export tmp_gtm_shlib # Idempotence says change the environment only if a new GT.M directory is being pointed to # otherwise just restore $gtmroutines and exit if [ $gtm_dist != "$old_gtm_dist" ] ; then # Force $gtmroutines to guarantee access to %XCMD for now; repaired later if [ -f "$gtm_dist/libgtmutil.$tmp_gtm_shlib" ] ; then gtmroutines="$gtm_dist/libgtmutil.$tmp_gtm_shlib $gtm_dist" else gtmroutines=$gtm_dist ; fi export gtmroutines # Set $gtm_icu_version - should be set before checking for UTF-8 mode if [ -z "$gtm_icu_version" ] ; then if [ -x "$(command -v pkg-config)" -a $(pkg-config --exists icu-io; echo $?) -eq 0 ]; then versioncmd="pkg-config --modversion icu-io" libcmd="pkg-config --variable=libdir icu-io" elif [ -x "$(command -v icu-config)" ] ; then versioncmd="icu-config --version" libcmd="icu-config --libdir" elif [ -x "$(command -v pkg-config)" -a $(pkg-config --exists icu; echo $?) -eq 0 ]; then versioncmd="pkg-config --modversion icu" libcmd="pkg-config --variable=libdir icu" fi if [ -n "$versioncmd" ]; then gtm_icu_version=`$versioncmd | gtm_chset=M $gtm_dist/mumps -run %XCMD 'Read x Write $FNumber(x*$Select(+x>5:.1,1:1),"",1)'` extendlibpath=`$libcmd` export gtm_icu_version # Add library path to LD_LIBRARY_PATH if [ "" = "$LD_LIBRARY_PATH" ]; then LD_LIBRARY_PATH="$extendlibpath"; else LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$extendlibpath";fi; export LD_LIBRARY_PATH if [ "ibm" = "$arch" ]; then if [ "" = "$LIBPATH" ]; then LIBPATH="$extendlibpath"; else LIBPATH="$LIBPATH:$extendlibpath";fi; export LIBPATH; unset gtm_icu_version fi fi fi # If UTF-8 is selected, and UTF-8 mode works, set $gtm_dist to utf8 subdirectory # Note that if UTF-8 mode cannot be properly set, GT.M remains configured for M mode # default $LC_CTYPE if not set # depending on the list of locales configured, locale -a might be considered a binary output. # grep needs -a option to process the output as text but -a is not supported on the non-linux servers we have. os=`uname -s` if [ $os = "Linux" ]; then binaryopt="-a" else binaryopt="" fi if [ "`echo $gtm_chset|tr utf UTF`" = "UTF-8" ] ; then if [ ! -d $gtm_dist/utf8 ] ; then echo "$gtm_dist/utf8 does not exist. Re-install GT.M with UTF-8 support to proceed." echo "Alternatively, unset gtm_chset environment variable to run gtmprofile without UTF-8 support." return 1 fi if [ -z "$LC_CTYPE" ] ; then LC_CTYPE=`locale -a | grep $binaryopt -iE '\.utf.?8$' | head -n1` ; export LC_CTYPE echo $LC_CTYPE | grep -iq .utf if [ $? -ne 0 ] ; then echo "No UTF-8 locales available. Please install a UTF-8 locale to proceed." echo "Alternatively, unset gtm_chset environment variable to run gtmprofile without UTF-8 support." return 1 fi else echo $LC_CTYPE | grep -iq .utf if [ $? -ne 0 ] ; then echo "LC_CTYPE=C is set to a non-UTF-8 locale. Set the environment variable LC_CTYPE or LC_ALL to any valid UTF-8 locale." echo "Alternatively, unset LC_CTYPE so that gtmprofile can automatically set it to an available UTF-8 locale." return 1 fi fi if [ "`echo Halt | gtm_prompt='GTM>' $gtm_dist/utf8/mumps -dir | tail -n 1`" = 'GTM>' ] ; then gtm_dist=$gtm_dist/utf8 if [ -f "$gtm_dist/libgtmutil.$tmp_gtm_shlib" ] ; then gtmroutines="$gtm_dist/libgtmutil.$tmp_gtm_shlib $gtm_dist" else gtmroutines=$gtm_dist ; fi else echo "Error while executing $gtm_dist/utf8/mumps -dir. Cannot set gtm_dist and gtmroutines to $gtm_dist/utf8." echo HALT | $gtm_dist/utf8/mumps -dir return 1 fi fi # Set $gtmver - must be set before $gtmroutines and $gtm_prompt are set gtmver=`$gtm_dist/mumps -run %XCMD 'Write $Piece($ZVersion," ",2),"_",$Piece($ZVersion," ",4)'` ; export gtmver # Set $gtm_prompt - must be set after checking for UTF-8 mode since prompt may be based on gtm_dist if [ -z "$gtm_prompt" ] ; then gtm_prompt="GTM>" else if [ -n "$old_gtm_dist" ] ; then gtm_prompt=`echo $gtm_prompt | sed "s;$old_gtm_dist;$gtm_dist;"` fi if [ -n "$old_gtmver" ] ; then gtm_prompt=`echo $gtm_prompt | sed "s;$old_gtmver;$gtmver;"` fi fi export gtm_prompt # Set $gtmdir and create default directories if they don't exist if [ -n "$gtmdir" ] ; then if [ -n "$old_gtm_dist" ] ; then gtmdir=`echo $gtmdir | sed "s;$old_gtm_dist;$gtm_dist;"` fi if [ -n "$old_gtmver" ] ; then gtmdir=`echo $gtmdir | sed "s;$old_gtmver;$gtmver;"` fi else gtmdir=$HOME/.fis-gtm fi export gtmdir mkdir -p $gtmdir/r $gtmdir/$gtmver/g $gtmdir/$gtmver/o $gtmdir/$gtmver/r $gtmdir/$gtmver/o/utf8 # Set $gtmroutines - edit for new GT.M if previously set, otherwise use default if [ -n "$old_gtmroutines" ] ; then gtmroutines="$old_gtmroutines" if [ -n "$old_gtm_dist" ] ; then gtmroutines=`echo $gtmroutines | sed "s;$old_gtm_dist;$gtm_dist;"` fi if [ -n "$old_gtmver" ] ; then gtmroutines=`echo $gtmroutines | sed "s;$old_gtmver;$gtmver;"` fi else if [ -f "$gtm_dist/libgtmutil.$tmp_gtm_shlib" ] ; then gtmroutines="$gtm_dist/libgtmutil.$tmp_gtm_shlib $gtm_dist" else gtmroutines=$gtm_dist ; fi if [ "utf8" = `basename $gtm_dist` ] ; then if [ -d $gtm_dist/plugin/o/utf8 ] ; then gtmroutines=`$gtm_dist/mumps -run %XCMD 'set x=$ztrnlnm("gtm_dist")_"/plugin/o/utf8/*."_$ztrnlnm("tmp_gtm_shlib") for set y=$zsearch(x) quit:""=y write y," "'`"$gtm_dist/plugin/o/utf8($gtm_dist/plugin/r) $gtmroutines" fi gtmroutines=`$gtm_dist/mumps -run %XCMD 'set x=$ztrnlnm("gtmdir")_"/"_$ztrnlnm("gtmver")_"/o/utf8/*."_$ztrnlnm("tmp_gtm_shlib") for set y=$zsearch(x) quit:""=y write y," "'`"$gtmdir/$gtmver/o/utf8*($gtmdir/$gtmver/r $gtmdir/r) $gtmroutines" else if [ -d $gtm_dist/plugin/o ] ; then gtmroutines=`$gtm_dist/mumps -run %XCMD 'set x=$ztrnlnm("gtm_dist")_"/plugin/o/*."_$ztrnlnm("tmp_gtm_shlib") for set y=$zsearch(x) quit:""=y write y," "'`"$gtm_dist/plugin/o($gtm_dist/plugin/r) $gtmroutines" fi gtmroutines=`$gtm_dist/mumps -run %XCMD 'set x=$ztrnlnm("gtmdir")_"/"_$ztrnlnm("gtmver")_"/o/*."_$ztrnlnm("tmp_gtm_shlib") for set y=$zsearch(x) quit:""=y write y," "'`"$gtmdir/$gtmver/o*($gtmdir/$gtmver/r $gtmdir/r) $gtmroutines" fi fi export gtmroutines # Set $gtmgbldir - edit for new GT.M if previously set, otherwise use default and create if needed if [ -n "$gtmgbldir" ] ; then if [ -n "$old_gtm_dist" ] ; then gtmgbldir=`echo $gtmgbldir | sed "s;$old_gtm_dist;$gtm_dist;"` fi if [ -n "$old_gtmver" ] ; then gtmgbldir=`echo $gtmgbldir | sed "s;$old_gtmver;$gtmver;"` fi else gtmgbldir=$gtmdir/$gtmver/g/gtm.gld fi export gtmgbldir # Set $gtm_tmp tmp_gtm_tmp=`uname -s` if [ "HP-UX" = $tmp_gtm_tmp -o "SunOS" = $tmp_gtm_tmp ] ; then tmp_gtm_tmp=/var/tmp/fis-gtm else tmp_gtm_tmp=/tmp/fis-gtm fi if [ ! -d "$tmp_gtm_tmp" ] ; then mkdir $tmp_gtm_tmp ; chmod a+rw,+t $tmp_gtm_tmp fi tmp_gtm_tmp=$tmp_gtm_tmp/$gtmver if [ -d "$tmp_gtm_tmp" ] ; then gtm_tmp=$tmp_gtm_tmp else if [ -n "$old_gtm_dist" ] ; then gtm_tmp=`echo $gtm_tmp | sed "s;$old_gtm_dist;$gtm_dist;"` fi if [ -n "$old_gtmver" ] ; then gtm_tmp=`echo $gtm_tmp | sed "s;$old_gtmver;$gtmver;"` fi if [ ! -d "$gtm_tmp" ] ; then mkdir -p $tmp_gtm_tmp ; chmod a+rw,+t $tmp_gtm_tmp ; gtm_tmp=$tmp_gtm_tmp fi fi export gtm_tmp # Set $gtm_log - use recommended directory if it exists, otherwise modify old value if it exists # Don't need to worry about non-existent directory because gtmsecshr will create it # $gtm_tmp will be used if $gtm_log not defined if [ -d /var/log/fis-gtm/$gtmver ] ; then gtm_log=/var/log/fis-gtm/$gtmver elif [ -n "$gtm_log" ] ; then if [ -n "$old_gtm_dist" ] ; then gtm_log=`echo $gtm_log | sed "s;$old_gtm_dist;$gtm_dist;"` fi if [ -n "$old_gtmver" ] ; then gtm_log=`echo $gtm_log | sed "s;$old_gtmver;$gtmver;"` fi else gtm_log=$gtm_tmp fi export gtm_log # Set $PATH if [ -n "$old_gtm_dist" ] ; then PATH=`echo $PATH | sed "s;$old_gtm_dist;$gtm_dist;"` fi if [ -n "$old_gtmver" ] ; then PATH=`echo $PATH | sed "s;$old_gtmver;$gtmver;"` fi if [ -z "`echo $PATH | grep -E \(:\|\^\)$gtm_dist\(:\|$\)`" ] ; then PATH=$PATH:$gtm_dist fi # Other useful environment variables # System administrators - add, delete or modify GT.M environment variables here as appropriate for your installation if [ -z "$gtm_etrap" ] ; then gtm_etrap='Write:(0=$STACK) "Error occurred: ",$ZStatus,!' ; export gtm_etrap fi if [ -z "$gtm_principal_editing" ] ; then gtm_principal_editing="EDITING" ; export gtm_principal_editing fi if [ -z "$gtm_repl_instance" ] ; then gtm_repl_instance=$gtmdir/$gtmver/g/gtm.repl ; export gtm_repl_instance elif [ -n "$old_gtmver" ] ; then gtm_repl_instance=`echo $gtm_repl_instance | sed "s;$old_gtmver;$gtmver;"` fi if [ -z "$gtm_retention" ] ; then gtm_retention=42 ; export gtm_retention fi # Set & uncomment these as required # Refer to the GT.M Administration & Operations Guide for complete list of GT.M environment variables # export gtm_autorelink_keeprtn=1 # leave auto-relinked object code in the shared memory repositories # export gtm_linktmpdir= # put relinkctl files in location other than $gtm_tmp # export gtm_baktmpdir= # location for MUPIP BACKUP temporary snapshot files # export gtm_snaptmpdir= # location for MUPIP INTEG temporary snapshot files # Set environment variables for plugins in $gtm_dist/plugin tmp_gtm_tmp=`uname -s` if [ "HP-UX" = $tmp_gtm_tmp -o "SunOS" = $tmp_gtm_tmp ] ; then tmp_gtm_tmp=/var/tmp/ else tmp_gtm_tmp=/tmp/ fi tmp_gtm_tmp=$tmp_gtm_tmp`$gtm_dist/mumps -run %XCMD 'write $$%RANDSTR^%RANDSTR(12)'`.tmp $gtm_dist/mumps -run %XCMD 'set x=$ztrnlnm("gtm_dist")_"/plugin/*.xc" for set y=$zsearch(x,213) quit:""=y set z=$zparse(y,"NAME") write "GTMXC_",z,"=",y," ; export GTMXC_",z,!' >$tmp_gtm_tmp if [ -s $tmp_gtm_tmp ] ; then . $tmp_gtm_tmp ; fi rm $tmp_gtm_tmp # Define aliases alias dse="$gtm_dist/dse" alias gde="$gtm_dist/mumps -run GDE" alias gtm="$gtm_dist/gtm" alias lke="$gtm_dist/lke" alias mupip="$gtm_dist/mupip" # Create default global directory and database if they don't exist # Must handle case where database file path is relative to global directory if [ ! -f $gtmgbldir ] ; then if [ -f $gtm_dist/gdedefaults ] ; then $gtm_dist/mumps -run %XCMD 'Write "@","'$gtm_dist'/gdedefaults",!,"exit",!' | $gtm_dist/mumps -run GDE; stty sane else echo exit | $gtm_dist/mumps -run GDE fi fi if [ ! -f "`dirname $gtmgbldir`/`basename $gtmgbldir gld`dat" ] ; then ( cd `dirname $gtmgbldir` ; mupip create ; mupip set -journal="on,before" -region "*" ) fi # If $old_gtm_dist and $gtm_dist are the same, need to restore $gtmroutines else gtmroutines="$old_gtmroutines" ; export gtmroutines fi # Restore gtm_passwd if it was set if [ ! -z "${tmp_passwd+isset}" ] ; then gtm_passwd=$tmp_passwd ; export gtm_passwd fi unset old_gtm_dist old_gtmver old_gtmroutines tmp_gtm_shlib tmp_gtm_tmp tmp_passwd fis-gtm-V7.0-005/sr_unix/gtmprofile_preV54000.gtc0000644000032200000250000000165614342376330020305 0ustar librarygtc################################################################# # # # Copyright 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. # # # ################################################################# gtm_dist="GTMDIST"; export gtm_dist gtmgbldir="mumps.gld"; export gtmgbldir if [ "$gtm_chset" = "UTF-8" -o "$gtm_chset" = "utf-8" ] ; then if [ -e $gtm_dist/utf8 ] ; then gtm_dist="$gtm_dist/utf8"; export gtm_dist fi fi gtmroutines=". $gtm_dist"; export gtmroutines gtm="$gtm_dist/mumps -direct"; export gtm mupip="$gtm_dist/mupip"; export mupip lke="$gtm_dist/lke"; export lke gde="$gtm_dist/mumps -r ^GDE"; export gde dse="$gtm_dist/dse"; export dse PATH=$PATH:$gtm_dist fis-gtm-V7.0-005/sr_unix/gtmrecv.c0000644000032200000250000006554314342376330015671 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2022 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_unistd.h" #include "gtm_fcntl.h" #include "gtm_inet.h" #include "gtm_ipc.h" #include #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdskill.h" #include "gdscc.h" #include "filestruct.h" #include "iosp.h" #include "repl_shutdcode.h" #include "gtmrecv.h" #include "repl_log.h" #include "repl_dbg.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "repl_errno.h" #include "repl_sem.h" #include "repl_sp.h" #include "cli.h" #include "jnl.h" #include "buddy_list.h" #include "tp.h" #include "repl_filter.h" #include "error.h" #include "eintr_wrappers.h" #include "util.h" #include "is_proc_alive.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_instance.h" #include "gtmmsg.h" #include "sgtm_putmsg.h" #include "gt_timer.h" #include "ftok_sem_incrcnt.h" #include "mutex.h" #include "fork_init.h" #include "io.h" #include "gtmio.h" GBLDEF boolean_t gtmrecv_fetchreysnc; GBLDEF boolean_t gtmrecv_logstats = FALSE; GBLDEF int gtmrecv_filter = NO_FILTER; GBLREF void (*call_on_signal)(); GBLREF uint4 process_id; GBLREF recvpool_addrs recvpool; GBLREF int recvpool_shmid; GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF boolean_t gtm_dist_ok_to_use; GBLREF gtmrecv_options_t gtmrecv_options; GBLREF int gtmrecv_log_fd; GBLREF FILE *gtmrecv_log_fp; GBLREF boolean_t is_rcvr_server; GBLREF boolean_t first_syslog; GBLREF int gtmrecv_srv_count; GBLREF uint4 log_interval; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF IN_PARMS *cli_lex_in_ptr; GBLREF uint4 mutex_per_process_init_pid; LITREF gtmImageName gtmImageNames[]; error_def(ERR_GTMDISTUNVERIF); error_def(ERR_INITORRESUME); error_def(ERR_MUPCLIERR); error_def(ERR_NORESYNCSUPPLONLY); error_def(ERR_NORESYNCUPDATERONLY); error_def(ERR_RECVPOOLSETUP); error_def(ERR_REPLERR); error_def(ERR_REPLINFO); error_def(ERR_REPLINSTFMT); error_def(ERR_REPLINSTOPEN); error_def(ERR_RESUMESTRMNUM); error_def(ERR_REUSEINSTNAME); error_def(ERR_REUSESLOTZERO); error_def(ERR_SYSCALL); error_def(ERR_TEXT); error_def(ERR_UPDSYNC2MTINS); error_def(ERR_UPDSYNCINSTFILE); int gtmrecv(void) { recvpool_ctl_ptr_t recvpool_ctl; upd_proc_local_ptr_t upd_proc_local; gtmrecv_local_ptr_t gtmrecv_local; upd_helper_ctl_ptr_t upd_helper_ctl; repl_inst_hdr updresync_inst_hdr; uint4 gtmrecv_pid; int idx, semval, status, save_upd_status, upd_start_status, upd_start_attempts; struct stat stat_buf; char print_msg[REPL_MSG_SIZE], tmpmsg[REPL_MSG_SIZE]; pid_t pid, procgp; int exit_status, waitpid_res, save_errno; int log_init_status; int updresync_instfile_fd; /* fd of the instance file name specified in -UPDATERESYNC= */ boolean_t cross_endian, dummy_ftok_counter_halted; int null_fd, rc; call_on_signal = gtmrecv_sigstop; ESTABLISH_RET(gtmrecv_ch, SS_NORMAL); memset((uchar_ptr_t)&recvpool, 0, SIZEOF(recvpool)); if (-1 == gtmrecv_get_opt()) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MUPCLIERR); if (!gtm_dist_ok_to_use) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_GTMDISTUNVERIF, 4, STRLEN(gtm_dist), gtm_dist, gtmImageNames[image_type].imageNameLen, gtmImageNames[image_type].imageName); if (gtmrecv_options.start || gtmrecv_options.shut_down) { jnlpool_init(GTMRECEIVE, (boolean_t)FALSE, (boolean_t *)NULL, NULL); assert(NULL != jnlpool); assert(NULL != jnlpool->repl_inst_filehdr); assert(NULL != jnlpool->jnlpool_ctl); /* If -UPDATERESYNC was specified without an empty instance file, error out. The only exception is if * the receiver is a root primary (updates enabled) supplementary instance and the source is non-supplementary. * In this case, since the non-supplementary stream is being ADDED to an existing instance file, there is no * requirement that the current instance file be empty. In fact, in that case we expect it to be non-empty * as one history record would have been written by the source server that brought up the root primary instance. */ if (gtmrecv_options.updateresync && jnlpool->repl_inst_filehdr->num_histinfo && !(jnlpool->repl_inst_filehdr->is_supplementary && !jnlpool->jnlpool_ctl->upd_disabled)) /* replication instance file is NOT empty. Issue error */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UPDSYNC2MTINS); if (gtmrecv_options.noresync) { /* If -NORESYNC was specified on a non-supplementary receiver instance, issue an error */ if (!jnlpool->repl_inst_filehdr->is_supplementary) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_NORESYNCSUPPLONLY); /* If -NORESYNC was specified on a receiver instance where updates are disabled, issue an error */ if (jnlpool->jnlpool_ctl->upd_disabled) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_NORESYNCUPDATERONLY); } } if (gtmrecv_options.shut_down) { /* Wait till shutdown time nears even before going to "recvpool_init". This is because the latter will return * with the ftok semaphore, access and options semaphore held and we do not want to be holding those locks (while * waiting for the user specified timeout to expire) as that will affect new GTM processes and/or other * MUPIP REPLIC commands that need these locks for their function. */ if (0 < gtmrecv_options.shutdown_time) { repl_log(stdout, TRUE, TRUE, "Waiting for %d second(s) before forcing shutdown\n", gtmrecv_options.shutdown_time); LONG_SLEEP(gtmrecv_options.shutdown_time); } else repl_log(stdout, TRUE, TRUE, "Forcing immediate shutdown\n"); } recvpool_init(GTMRECV, gtmrecv_options.start && 0 != gtmrecv_options.listen_port); /* * When gtmrecv_options.start is TRUE, shm field recvpool.recvpool_ctl->fresh_start is updated in "recvpool_init" * recvpool.recvpool_ctl->fresh_start == TRUE ==> fresh start, and * recvpool.recvpool_ctl->fresh_start == FALSE ==> start after a crash */ recvpool_ctl = recvpool.recvpool_ctl; upd_proc_local = recvpool.upd_proc_local; gtmrecv_local = recvpool.gtmrecv_local; upd_helper_ctl = recvpool.upd_helper_ctl; if (gtmrecv_options.start) { if (0 == gtmrecv_options.listen_port /* implies (updateonly || helpers only) */ || !recvpool_ctl->fresh_start) { if (SRV_ALIVE == (status = is_recv_srv_alive()) && 0 != gtmrecv_options.listen_port) { rel_sem(RECV, RECV_SERV_OPTIONS_SEM); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Receiver Server already exists")); } else if (SRV_DEAD == status && 0 == gtmrecv_options.listen_port) { rel_sem(RECV, RECV_SERV_OPTIONS_SEM); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Receiver server does not exist. Start it first")); } else if (SRV_ERR == status) { status = errno; rel_sem(RECV, RECV_SERV_OPTIONS_SEM); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Receiver server semaphore error"), status); } if (gtmrecv_options.updateonly) { status = gtmrecv_start_updonly() - UPDPROC_STARTED; rel_sem(RECV, RECV_SERV_OPTIONS_SEM); gtmrecv_exit(status); } if (gtmrecv_options.helpers && 0 == gtmrecv_options.listen_port) { /* start helpers only */ status = gtmrecv_start_helpers(gtmrecv_options.n_readers, gtmrecv_options.n_writers); rel_sem(RECV, RECV_SERV_OPTIONS_SEM); gtmrecv_exit(status - NORMAL_SHUTDOWN); } } if (gtmrecv_options.updateresync && ('\0' != gtmrecv_options.updresync_instfilename[0])) { /* -UPDATERESYNC= was specified. * Note: -UPDATERESYNC without a value is treated as -UPDATERESYNC="" hence the above check. */ OPENFILE_CLOEXEC(gtmrecv_options.updresync_instfilename, O_RDONLY, updresync_instfile_fd); if (FD_INVALID == updresync_instfile_fd) { rel_sem(RECV, RECV_SERV_OPTIONS_SEM); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_REPLINSTOPEN, 2, LEN_AND_STR(gtmrecv_options.updresync_instfilename), errno); } LSEEKREAD(updresync_instfile_fd, 0, &updresync_inst_hdr, SIZEOF(updresync_inst_hdr), status); if (0 != status) { /* Encountered an error reading the full file header */ rel_sem(RECV, RECV_SERV_OPTIONS_SEM); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Input file does not even have a full instance file header")); } /* Check if it is the right version */ if (memcmp(updresync_inst_hdr.label, GDS_REPL_INST_LABEL, GDS_REPL_INST_LABEL_SZ - 1)) { rel_sem(RECV, RECV_SERV_OPTIONS_SEM); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(10) ERR_UPDSYNCINSTFILE, 0, ERR_REPLINSTFMT, 6, LEN_AND_STR(gtmrecv_options.updresync_instfilename), GDS_REPL_INST_LABEL_SZ - 1, GDS_REPL_INST_LABEL, GDS_REPL_INST_LABEL_SZ - 1, updresync_inst_hdr.label); } /* Check endianness match. If not, fields have to be endian converted before examining them. */ cross_endian = (GTM_IS_LITTLE_ENDIAN != updresync_inst_hdr.is_little_endian); /* At the time of this writing, the only minor version supported is 1. * Whenever this gets updated, we need to add code to do the online upgrade. * Add an assert as a reminder to do this. */ assert(1 == SIZEOF(updresync_inst_hdr.replinst_minorver)); /* so no endian conversion needed */ assert(1 == updresync_inst_hdr.replinst_minorver); /* Check if cleanly shutdown */ if (cross_endian) { assert(4 == SIZEOF(updresync_inst_hdr.crash)); /* so need to use GTM_BYTESWAP_32 */ updresync_inst_hdr.crash = GTM_BYTESWAP_32(updresync_inst_hdr.crash); } if (updresync_inst_hdr.crash) { /* The instance file cannot be used for updateresync if it was not cleanly shutdown */ rel_sem(RECV, RECV_SERV_OPTIONS_SEM); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Input instance file was not cleanly shutdown")); } if (cross_endian) { assert(4 == SIZEOF(updresync_inst_hdr.is_supplementary)); /* so need GTM_BYTESWAP_32 */ updresync_inst_hdr.is_supplementary = GTM_BYTESWAP_32(updresync_inst_hdr.is_supplementary); assert(8 == SIZEOF(updresync_inst_hdr.jnl_seqno)); /* so need to use GTM_BYTESWAP_64 */ updresync_inst_hdr.jnl_seqno = GTM_BYTESWAP_64(updresync_inst_hdr.jnl_seqno); assert(4 == SIZEOF(updresync_inst_hdr.num_histinfo)); /* so need to use GTM_BYTESWAP_32 */ updresync_inst_hdr.num_histinfo = GTM_BYTESWAP_32(updresync_inst_hdr.num_histinfo); } if (jnlpool->repl_inst_filehdr->is_supplementary && !updresync_inst_hdr.is_supplementary) { /* Do one check for non-supplementary -> supplementary connection using -updateresync. * This is because this use of -updateresync is different than the other connection usages. */ if (!updresync_inst_hdr.jnl_seqno) { /* The instance file cannot be used for updateresync if it has a ZERO seqno */ rel_sem(RECV, RECV_SERV_OPTIONS_SEM); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Non-supplementary input instance file cannot be used" " on supplementary instance when it is empty (has seqno of 0)")); } } /* else similar checks of the jnl_seqno and num_histinfo for other types of connections (i.e. * non-supplementary -> non-supplementary, supplementary -> supplementary) will be done later in * "repl_inst_get_updresync_histinfo" only when there is a need to scan the input instance file * for any history record. */ updresync_inst_hdr.num_histinfo--; /* needed to get at the last history record */ if (cross_endian) ENDIAN_CONVERT_REPL_INST_UUID(&updresync_inst_hdr.lms_group_info); if (IS_REPL_INST_UUID_NULL(updresync_inst_hdr.lms_group_info)) { /* The input instance has a NULL LMS group. Cannot be used to fill in current instance */ rel_sem(RECV, RECV_SERV_OPTIONS_SEM); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Input instance file has NULL LMS group")); } if (updresync_inst_hdr.is_supplementary) { /* The input instance file is supplementary. Allow it only if the current instance is * supplementary and is not a root primary. */ if (!jnlpool->repl_inst_filehdr->is_supplementary) { rel_sem(RECV, RECV_SERV_OPTIONS_SEM); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2, LEN_AND_LIT("Input instance file must be non-supplementary" " to match current instance")); assert(FALSE); } if (!jnlpool->jnlpool_ctl->upd_disabled) { rel_sem(RECV, RECV_SERV_OPTIONS_SEM); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2, LEN_AND_LIT("Input instance file must be non-supplementary" " as current instance is a supplementary root primary")); } } else if (jnlpool->repl_inst_filehdr->is_supplementary) { if (jnlpool->jnlpool_ctl->upd_disabled) { /* The input instance file is non-supplementary. Allow it only if the current * instance is non-supplementary or if it is a supplementary root primary. */ rel_sem(RECV, RECV_SERV_OPTIONS_SEM); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2, LEN_AND_LIT("Input instance file must be supplementary" " to match current instance")); } if (!gtmrecv_options.resume_specified && !gtmrecv_options.initialize_specified) { rel_sem(RECV, RECV_SERV_OPTIONS_SEM); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_INITORRESUME); } } if (!jnlpool->repl_inst_filehdr->is_supplementary || jnlpool->jnlpool_ctl->upd_disabled) { if (gtmrecv_options.resume_specified) { rel_sem(RECV, RECV_SERV_OPTIONS_SEM); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_RESUMESTRMNUM, 0, ERR_TEXT, 2, LEN_AND_LIT("RESUME allowed only on root primary supplementary instance")); } if (gtmrecv_options.reuse_specified) { rel_sem(RECV, RECV_SERV_OPTIONS_SEM); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REUSEINSTNAME, 0, ERR_TEXT, 2, LEN_AND_LIT("REUSE allowed only on root primary supplementary instance")); } } } # ifndef REPL_DEBUG_NOBACKGROUND FORK(pid); if (0 < pid) { REPL_DPRINT2("Waiting for receiver child process %d to startup\n", pid); while (0 == (semval = get_sem_info(RECV, RECV_SERV_COUNT_SEM, SEM_INFO_VAL)) && is_proc_alive(pid, 0)) { /* To take care of reassignment of PIDs, the while condition should be && with the * condition (PPID of pid == process_id) */ REPL_DPRINT2("Waiting for receiver child process %d to startup\n", pid); SHORT_SLEEP(GTMRECV_WAIT_FOR_SRV_START); WAITPID(pid, &exit_status, WNOHANG, waitpid_res); /* Release defunct child if dead */ } if (0 <= semval) rel_sem(RECV, RECV_SERV_OPTIONS_SEM); gtmrecv_exit(1 == semval ? SRV_ALIVE : SRV_DEAD); } else if (0 > pid) { status = errno; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to fork"), status); } # endif } else if (gtmrecv_options.shut_down) { if (gtmrecv_options.updateonly) gtmrecv_exit(gtmrecv_endupd() - NORMAL_SHUTDOWN); if (gtmrecv_options.helpers) gtmrecv_exit(gtmrecv_end_helpers(FALSE) - NORMAL_SHUTDOWN); gtmrecv_exit(gtmrecv_shutdown(FALSE, NORMAL_SHUTDOWN) - NORMAL_SHUTDOWN); } else if (gtmrecv_options.changelog) gtmrecv_exit(gtmrecv_changelog() - NORMAL_SHUTDOWN); else if (gtmrecv_options.checkhealth) gtmrecv_exit(gtmrecv_checkhealth() - NORMAL_SHUTDOWN); else if (gtmrecv_options.showbacklog) gtmrecv_exit(gtmrecv_showbacklog() - NORMAL_SHUTDOWN); else if (gtmrecv_options.stopreceiverfilter) gtmrecv_exit(gtmrecv_stopfilter() - NORMAL_SHUTDOWN); else gtmrecv_exit(gtmrecv_statslog() - NORMAL_SHUTDOWN); assert(!holds_sem[RECV][RECV_POOL_ACCESS_SEM]); assert(holds_sem[RECV][RECV_SERV_OPTIONS_SEM]); /* The process might have sent a syslog message already, so set first_syslog here to force setting image type * with the new value of is_rcvr_server. */ is_rcvr_server = first_syslog = TRUE; process_id = getpid(); OPERATOR_LOG_MSG; /* Initialize mutex socket, memory semaphore etc. before any "grab_lock" is done by this process on the journal pool. * Note that the initialization would already have been done by the parent receiver startup command but we need to * redo the initialization with the child process id. */ assert(mutex_per_process_init_pid && mutex_per_process_init_pid != process_id); mutex_per_process_init(); /* Take a copy of the fact whether an -UPDATERESYNC was specified or not. Note this down in shared memory. * We will clear the shared memory copy as soon as the first history record gets written to the instance file * after the first time this receiver connects to a source. Future connects of this same receiver with the same * or different source servers should NOT use the -UPDATERESYNC but instead treat it as a non-updateresync connect. */ gtmrecv_local->updateresync = gtmrecv_options.updateresync; /* Now that this receiver server has started up fine without issues, one can safely move the instance file descriptor * (and other instance file header information) from private memory to gtmrecv_local (shared memory). */ if (gtmrecv_options.updateresync && ('\0' != gtmrecv_options.updresync_instfilename[0])) { gtmrecv_local->updresync_instfile_fd = updresync_instfile_fd; assert(cross_endian == (GTM_IS_LITTLE_ENDIAN != updresync_inst_hdr.is_little_endian)); gtmrecv_local->updresync_cross_endian = cross_endian; gtmrecv_local->updresync_num_histinfo = updresync_inst_hdr.num_histinfo; /* already endian converted */ /* In case of a supplementary input instance file, we also want the last history record number * for each available non-supplementary stream. Endian convert it if needed. */ for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) { assert(4 == SIZEOF(updresync_inst_hdr.last_histinfo_num[idx])); /* so need to use GTM_BYTESWAP_32 */ gtmrecv_local->updresync_num_histinfo_strm[idx] = cross_endian ? GTM_BYTESWAP_32(updresync_inst_hdr.last_histinfo_num[idx]) : updresync_inst_hdr.last_histinfo_num[idx]; assert(gtmrecv_local->updresync_num_histinfo >= gtmrecv_local->updresync_num_histinfo_strm[idx]); assert((0 <= gtmrecv_local->updresync_num_histinfo_strm[idx]) || (INVALID_HISTINFO_NUM == gtmrecv_local->updresync_num_histinfo_strm[idx])); } gtmrecv_local->updresync_lms_group = updresync_inst_hdr.lms_group_info; gtmrecv_local->updresync_jnl_seqno = updresync_inst_hdr.jnl_seqno; } else { gtmrecv_local->updresync_instfile_fd = FD_INVALID; /* No need to initialize the below since they will be used only if updresync_instfile_fd is not FD_INVALID * gtmrecv_local->updresync_cross_endian = cross_endian; * gtmrecv_local->updresync_num_histinfo = updresync_inst_hdr.num_histinfo; * gtmrecv_local->updresync_num_histinfo_strm[] = updresync_inst_hdr.last_histinfo_num[]; * gtmrecv_local->updresync_lms_group = updresync_inst_hdr.lms_group_info; * gtmrecv_local->updresync_jnl_seqno = updresync_inst_hdr.jnl_seqno; */ } /* Take a copy of the fact whether a -NORESYNC was specified or not. Note this down in shared memory. * We will clear the shared memory copy as soon as the first history record gets written to the instance file * after the first time this receiver connects to a source. Future connects of this same receiver with the same * or different source servers should NOT use the -NORESYNC but instead treat it as a regular (no -noresync) connect. */ gtmrecv_local->noresync = gtmrecv_options.noresync; STRCPY(gtmrecv_local->log_file, gtmrecv_options.log_file); gtmrecv_local->log_interval = log_interval = gtmrecv_options.rcvr_log_interval; upd_proc_local->log_interval = gtmrecv_options.upd_log_interval; upd_helper_ctl->start_helpers = FALSE; upd_helper_ctl->start_n_readers = upd_helper_ctl->start_n_writers = 0; log_init_status = repl_log_init(REPL_GENERAL_LOG, >mrecv_log_fd, gtmrecv_options.log_file); assert(SS_NORMAL == log_init_status); repl_log_fd2fp(>mrecv_log_fp, gtmrecv_log_fd); if (-1 == (procgp = setsid())) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Receiver server error in setsid"), errno); /* Point stdin to /dev/null */ OPENFILE(DEVNULL, O_RDONLY, null_fd); if (0 > null_fd) rts_error_csa(CSA_ARG(NULL) ERR_REPLERR, RTS_ERROR_LITERAL("Failed to open /dev/null for read"), errno, 0); assert(null_fd > 2); /* Detached from the initiating process, now detach from the starting IO */ io_rundown(NORMAL_RUNDOWN); FSTAT_FILE(gtmrecv_log_fd, &stat_buf, log_init_status); assertpro(!log_init_status); /* io_rundown should not affect the log file */ DUP2(null_fd, 0, rc); if (0 > rc) RTS_ERROR_CSA_ABT(NULL, ERR_REPLERR, RTS_ERROR_LITERAL("Failed to set stdin to /dev/null"), errno, 0); /* Re-init IO now that we have opened the log file and set stdin to /dev/null */ io_init(IS_MUPIP_IMAGE); CLOSEFILE(null_fd, rc); if (0 > rc) RTS_ERROR_CSA_ABT(NULL, ERR_REPLERR, RTS_ERROR_LITERAL("Failed to close /dev/null"), errno, 0); gtmrecv_local->recv_serv_pid = process_id; assert((NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl)); jnlpool->jnlpool_ctl->gtmrecv_pid = process_id; gtmrecv_local->listen_port = gtmrecv_options.listen_port; /* Log receiver server startup command line first */ repl_log(gtmrecv_log_fp, TRUE, TRUE, "%s %s\n", cli_lex_in_ptr->argv[0], cli_lex_in_ptr->in_str); assert((NULL != jnlpool) && (NULL != jnlpool->repl_inst_filehdr)); SNPRINTF(tmpmsg, REPL_MSG_SIZE, "GTM Replication Receiver Server with Pid [%d] started on replication instance [%s]", process_id, jnlpool->repl_inst_filehdr->inst_info.this_instname); sgtm_putmsg(print_msg, REPL_MSG_SIZE, VARLSTCNT(4) ERR_REPLINFO, 2, LEN_AND_STR(tmpmsg)); repl_log(gtmrecv_log_fp, TRUE, TRUE, print_msg); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Attached to existing jnlpool with shmid = [%d] and semid = [%d]\n", jnlpool->repl_inst_filehdr->jnlpool_shmid, jnlpool->repl_inst_filehdr->jnlpool_semid); if (recvpool_ctl->fresh_start) { QWASSIGNDW(recvpool_ctl->jnl_seqno, 0); /* Update process will initialize this to a non-zero value */ repl_log(gtmrecv_log_fp, TRUE, TRUE, "Created recvpool with shmid = [%d] and semid = [%d]\n", jnlpool->repl_inst_filehdr->recvpool_shmid, jnlpool->repl_inst_filehdr->recvpool_semid); } else { /* Coming up after a crash, reset Update process read. This is done by setting gtmrecv_local->restart. * This will trigger update process to reset recvpool_ctl->jnl_seqno too. */ gtmrecv_local->restart = GTMRECV_RCVR_RESTARTED; /* Signal the update process to check for the update. */ PTHREAD_COND_SIGNAL(&recvpool.recvpool_ctl->write_updated, status); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_cond_signal"), CALLFROM, status, 0); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Attached to existing recvpool with shmid = [%d] and semid = [%d]\n", jnlpool->repl_inst_filehdr->recvpool_shmid, jnlpool->repl_inst_filehdr->recvpool_semid); } save_upd_status = upd_proc_local->upd_proc_shutdown; for (upd_start_attempts = 0; UPDPROC_START_ERR == (upd_start_status = gtmrecv_upd_proc_init(recvpool_ctl->fresh_start)) && GTMRECV_MAX_UPDSTART_ATTEMPTS > upd_start_attempts; upd_start_attempts++) { if (EREPL_UPDSTART_SEMCTL == repl_errno || EREPL_UPDSTART_BADPATH == repl_errno) { gtmrecv_exit(ABNORMAL_SHUTDOWN); } else if (EREPL_UPDSTART_FORK == repl_errno) { /* Couldn't start up update now, can try later */ LONG_SLEEP(GTMRECV_WAIT_FOR_PROC_SLOTS); continue; } else if (EREPL_UPDSTART_EXEC == repl_errno) { /* In forked child, could not exec, should exit */ upd_proc_local->upd_proc_shutdown = save_upd_status; gtmrecv_exit(ABNORMAL_SHUTDOWN); } } if ((UPDPROC_EXISTS == upd_start_status && recvpool_ctl->fresh_start) || (UPDPROC_START_ERR == upd_start_status && GTMRECV_MAX_UPDSTART_ATTEMPTS <= upd_start_attempts)) { sgtm_putmsg(print_msg, REPL_MSG_SIZE, VARLSTCNT(4) ERR_REPLERR, RTS_ERROR_LITERAL( (UPDPROC_EXISTS == upd_start_status) ? "Runaway Update Process. Aborting..." : "Too many failed attempts to fork Update Process. Aborting...")); repl_log(gtmrecv_log_fp, TRUE, TRUE, print_msg); gtmrecv_exit(ABNORMAL_SHUTDOWN); } upd_proc_local->start_upd = UPDPROC_STARTED; if (!recvpool_ctl->fresh_start) { while ((GTMRECV_RCVR_RESTARTED == gtmrecv_local->restart) && (SRV_ALIVE == is_updproc_alive())) { REPL_DPRINT1("Rcvr waiting for update to restart\n"); SHORT_SLEEP(GTMRECV_WAIT_FOR_SRV_START); } upd_proc_local->bad_trans = FALSE; recvpool_ctl->write_wrap = recvpool_ctl->recvpool_size; recvpool_ctl->write = 0; recvpool_ctl->wrapped = FALSE; upd_proc_local->changelog = TRUE; gtmrecv_local->restart = GTMRECV_NO_RESTART; /* release the update process wait */ /* Signal the update process to check for the update. */ PTHREAD_COND_SIGNAL(&recvpool.recvpool_ctl->write_updated, status); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_cond_signal"), CALLFROM, status, 0); } if (gtmrecv_options.helpers) gtmrecv_helpers_init(gtmrecv_options.n_readers, gtmrecv_options.n_writers); /* It is necessary for every process that is using the ftok semaphore to increment the counter by 1. This is used * by the last process that shuts down to delete the ftok semaphore when it notices the counter to be 0. * Note that the parent receiver server startup command would have done an increment of the ftok counter semaphore * for the replication instance file. But the receiver server process (the child) that comes here would not have done * that. Do that while the parent is still waiting for our okay. */ if (!ftok_sem_incrcnt(recvpool.recvpool_dummy_reg, FILE_TYPE_REPLINST, &dummy_ftok_counter_halted)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_RECVPOOLSETUP); /* Lock the receiver server count semaphore. Its value should be atmost 1. */ if (0 > grab_sem_immediate(RECV, RECV_SERV_COUNT_SEM)) { save_errno = errno; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Receive pool semop failure"), save_errno); } # ifdef REPL_DEBUG_NOBACKGROUND rel_sem(RECV, RECV_SERV_OPTIONS_SEM); # endif gtmrecv_srv_count++; gtmrecv_filter = NO_FILTER; if ('\0' != gtmrecv_local->filter_cmd[0]) { if (SS_NORMAL == (status = repl_filter_init(gtmrecv_local->filter_cmd))) gtmrecv_filter |= EXTERNAL_FILTER; else gtmrecv_exit(ABNORMAL_SHUTDOWN); } gtmrecv_process(!recvpool_ctl->fresh_start); return (SS_NORMAL); } fis-gtm-V7.0-005/sr_unix/gtmrecv.h0000644000032200000250000004274014342376330015670 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2020 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. * * * ****************************************************************/ #ifndef GTMRECV_H #define GTMRECV_H /* Needs mdef.h, gdsfhead.h and its dependencies, and iosp.h */ #define DEFAULT_SHUTDOWN_TIMEOUT 30 /* seconds */ #define MAX_FILTER_CMD_LEN 512 /* characters */ #define UPD_HELPERS_DELIM ',' #define MAX_UPD_HELPERS 128 /* Max helper process (incl. readers and writers) one instance can support */ #define MIN_UPD_HELPERS 1 /* Minimum number of helper processes, one for reading or writing */ #define DEFAULT_UPD_HELPERS 8 /* If value for -HELPERS is not specified, start these many helpers. Change * DEFAULT_UPD_HELPERS_STR if you change DEFAULT_UPD_HELPERS */ #define DEFAULT_UPD_HELP_READERS 5 /* If -HELPERS is not specified, or specified as -HELPERS=,n start these many * readers. Change DEFAULT_UPD_HELPERS_STR if you change DEFAULT_UPD_HELP_READERS */ #define DEFAULT_UPD_HELPERS_STR "8,5" /* Built as "DEFAULT_UPD_HELPERS,DEFAULT_UPD_HELP_READERS". Maintain DEFAULT for * /helpers in vvms:mupip_cmd.cld in sync with DEFAULT_UPD_HELPERS_STR */ #ifdef VMS #define MAX_GSEC_KEY_LEN 32 /* 31 is allowed + 1 for NULL terminator */ #endif typedef enum { GTMRECV_DUMMY_STATE = 0, GTMRECV_START, GTMRECV_WAITING_FOR_CONNECTION, GTMRECV_RECEIVING_MSGS, GTMRECV_WAITING_FOR_UPD_CRASH_RESTART, GTMRECV_WAITING_FOR_UPD_SHUT_RESTART } gtmrecv_state_t; enum { UPDPROC_STARTED, UPDPROC_START, UPDPROC_EXISTS, UPDPROC_START_ERR }; enum { GTMRECV_NO_RESTART, GTMRECV_RCVR_RESTARTED, GTMRECV_UPD_RESTARTED }; enum { HELPER_REAP_NONE = 0, HELPER_REAP_NOWAIT, HELPER_REAP_WAIT }; #define GTMRECV_WAIT_FOR_PROC_SLOTS 1 /* s */ #define GTMRECV_WAIT_FOR_UPDSTART (1000 - 1) /* ms */ #define GTMRECV_WAIT_FOR_UPD_SHUTDOWN 10 /* ms */ #define GTMRECV_MAX_UPDSTART_ATTEMPTS 16 #define GTMRECV_WAIT_FOR_RECVSTART (1000 - 1) /* ms */ #define GTMRECV_WAIT_FOR_SRV_START 10 /* ms */ #define GTMRECV_REAP_HELPERS_INTERVAL 300 /* s */ #define SRV_ALIVE 0x0 #define SRV_DEAD 0x1 #define SRV_ERR 0x2 /* The exit status of checkhealth is BIT-OR of the Receiver status and the * Update status */ #define RECEIVER_SRV_ALIVE 0x00 #define RECEIVER_SRV_DEAD 0x01 #define RECEIVER_CHECKHEALTH_ERR 0x02 #define UPDATE_PROC_ALIVE 0x00 #define UPDATE_PROC_DEAD 0x04 #define UPDATE_CHECKHEALTH_ERR 0x08 #define RECVPOOL_SEGMENT 'R' #define DEFAULT_RECVPOOL_SIZE (2 << 25) /* 64MiB */ #define MIN_RECVPOOL_SIZE (2 << 19) /* 1MiB */ #define MAX_RECVPOOL_SIZE (0x1000000000LL) /* 64GB */ #define GTMRECV_MIN_TCP_SEND_BUFSIZE (512) /* anything less than this, issue a warning */ #define GTMRECV_TCP_SEND_BUFSIZE (1024) /* not much outbound traffic, we can live with a low limit */ #define GTMRECV_MIN_TCP_RECV_BUFSIZE (16 * 1024) /* anything less than this, issue a warning */ #define GTMRECV_TCP_RECV_BUFSIZE_INCR (32 * 1024) /* attempt to get a larger buffer with this increment */ #define GTMRECV_TCP_RECV_BUFSIZE (1024 * 1024) /* desirable to set the buffer size to be able to receive large chunks */ #define IS_RCVR_SRVR_FALSE FALSE #define IS_RCVR_SRVR_TRUE TRUE #include /* Note: fields shared between the receiver and update processes * really need to have memory barriers or other appropriate * synchronization constructs to ensure changes by one * process are actually seen by the other process. Cache * line spacing should also be taken into account. * Adding volatile is only a start at this. */ typedef struct { replpool_identifier recvpool_id; /* Shared memory identification */ volatile seq_num jnl_seqno; /* Sequence number of the next transaction expected to be received from source * server. Updated by Receiver Server */ seq_num old_jnl_seqno; /* Stores the value of jnl_seqno before it is set to 0 when upd crash/shut */ repl_conn_info_t this_side; /* Replication connection details of this side/instance */ gtm_uint64_t recvdata_base_off; /* Receive pool offset from where journal data starts */ gtm_uint64_t recvpool_size; /* Available space for journal data in bytes */ volatile gtm_uint64_t write; /* Relative offset from recvdata_base_off for for the next journal record to be * written. Updated by Receiver Server */ volatile gtm_uint64_t write_wrap; /* Relative offset from recvdata_base_off where write was wrapped by recvr srvr */ volatile uint4 wrapped; /* Boolean, set by Receiver Server when it wraps. Reset by Update Process when it * wraps. Used for detecting space used in the receive pool */ uint4 initialized; /* Boolean, has receive pool been initialized? */ uint4 fresh_start; /* Boolean, fresh_start or crash_start? */ repl_histinfo last_rcvd_histinfo; /* history from the last received REPL_HISTREC message */ repl_histinfo last_valid_histinfo; /* history corresponding to last logical record written * into receive pool by the receiver. This is almost always * the same as last_rcvd_histinfo except in the window * between receiving a new REPL_HISTREC and the first * logical records corresponding to it. */ repl_histinfo last_rcvd_strm_histinfo[MAX_SUPPL_STRMS]; /* same as last_rcvd_histinfo but for each * non-supplementary stream. Used only in a * propagating supplementary instance. */ repl_histinfo last_valid_strm_histinfo[MAX_SUPPL_STRMS]; /* same as last_valid_histinfo but for each * non-supplementary stream. Used only in a * propagating supplementary instance. */ boolean_t is_valid_strm_histinfo[MAX_SUPPL_STRMS]; /* TRUE if corresponding entry in * "last_rcvd_strm_histinfo[]" array is valid */ uint4 max_strm_histinfo; /* maximum valid index in "is_valid_strm_histinfo" array */ boolean_t insert_strm_histinfo; /* true for a supplementary propagating primary if the receiver * has to insert REPL_HISTREC records for each valid stream * into the receive pool. */ pthread_mutex_t write_updated_ctl; pthread_cond_t write_updated; } recvpool_ctl_struct; #define INSERT_STRM_HISTINFO_FALSE FALSE #define INSERT_STRM_HISTINFO_TRUE TRUE #define GTMRECV_CLEAR_CACHED_HISTINFO(RECVPOOL_CTL, JNLPOOL, INSERT_STRM_HISTINFO) \ { \ memset(&RECVPOOL_CTL->last_rcvd_histinfo, 0, SIZEOF(RECVPOOL_CTL->last_rcvd_histinfo)); \ memset(&RECVPOOL_CTL->last_valid_histinfo, 0, SIZEOF(RECVPOOL_CTL->last_valid_histinfo)); \ assert((NULL != JNLPOOL) && (NULL != JNLPOOL->repl_inst_filehdr)); \ assert(NULL != JNLPOOL->jnlpool_ctl); \ if (JNLPOOL->repl_inst_filehdr->is_supplementary && JNLPOOL->jnlpool_ctl->upd_disabled) \ { /* The below fields are used only in case of a supplementary instance where updates are disabled. \ * So avoid initializing them in any other case. \ */ \ memset(&RECVPOOL_CTL->last_rcvd_strm_histinfo[0], 0, SIZEOF(RECVPOOL_CTL->last_rcvd_strm_histinfo)); \ memset(&RECVPOOL_CTL->last_valid_strm_histinfo[0], 0, SIZEOF(RECVPOOL_CTL->last_valid_strm_histinfo)); \ memset(&RECVPOOL_CTL->is_valid_strm_histinfo[0], 0, SIZEOF(RECVPOOL_CTL->is_valid_strm_histinfo)); \ RECVPOOL_CTL->max_strm_histinfo = 0; \ assert((0 == RECVPOOL_CTL->jnl_seqno) \ || (0 < RECVPOOL_CTL->jnl_seqno) && (RECVPOOL_CTL->jnl_seqno >= JNLPOOL->jnlpool_ctl->jnl_seqno)); \ assert(0 < JNLPOOL->jnlpool_ctl->jnl_seqno); \ RECVPOOL_CTL->insert_strm_histinfo = INSERT_STRM_HISTINFO; \ } \ } /* * The following structure contains Update Process related data items. * Maintaining this structure in the Receive pool provides for * persistence across instantiations of the Update Process (across crashes, * the receive pool is preserved) */ typedef struct { uint4 upd_proc_pid; /* Process identification of update server */ uint4 upd_proc_pid_prev; /* Save for reporting old pid if we fail */ volatile seq_num read_jnl_seqno; /* Next jnl_seqno to be read; keep aligned at 8 byte boundary for performance */ volatile gtm_uint64_t read; /* Relative offset from recvdata_base_off of the next journal record to be * read from the receive pool */ volatile uint4 upd_proc_shutdown; /* Used to communicate shutdown related values between Receiver and Update */ volatile int4 upd_proc_shutdown_time; /* Time allowed for update process to shut down */ volatile uint4 bad_trans; /* Boolean, set by Update Process that it received a bad transaction record */ volatile uint4 changelog; /* Boolean - change the log file */ int4 start_upd; /* Used to communicate upd only startup values */ volatile uint4 log_interval; /* Interval (in seqnos) at which update process logs its progress */ char log_file[MAX_FN_LEN + 1]; volatile uint4 onln_rlbk_flg; /* Set to TRUE every time update process sees an online rollback. Set to FALSE ONLY * by receiver server */ } upd_proc_local_struct; /* * The following structure contains data items local to the Receiver Server, * but are in the Receive Pool to provide for persistence across instantiations * of the Receiver Server (across Receiver Server crashes, the Receive * Pool is preserved). */ typedef struct { uint4 recv_serv_pid; /* Process identification of receiver server */ int4 lastrecvd_time; /* unused */ /* Data items used in communicating action qualifiers (show statistics, shutdown) and * qualifier values (log file, shutdown time, etc). */ volatile uint4 statslog; /* Boolean - detailed log on/off? */ volatile uint4 shutdown; /* Used to communicate shutdown related values between process initiating shutdown * and Receiver Server */ int4 shutdown_time; /* Time allowed for shutdown in seconds */ int4 listen_port; /* Port at which the Receiver Server is listening */ volatile uint4 restart; /* Used by receiver server to coordinate crash restart with update process */ volatile uint4 changelog; /* Boolean - change the log file */ volatile uint4 log_interval; /* Interval (in seqnos) at which receiver logs its progress */ char filter_cmd[MAX_FILTER_CMD_LEN]; /* Receiver filters incoming records using this process */ char log_file[MAX_FN_LEN + 1]; /* File to log receiver progress */ char statslog_file[MAX_FN_LEN + 1]; /* File to log statistics */ repl_conn_info_t remote_side; /* Details of the remote side/instance of the connection */ int4 strm_index; boolean_t updateresync; /* Copy of gtmrecv_options.updateresync; This is cleared once first history * record gets applied on the receiver after the first connect with a source. */ boolean_t noresync; /* Copy of gtmrecv_options.noresync; This is cleared once first history * record gets applied on the receiver after the first connect with a source. */ int updresync_instfile_fd; /* fd of the instance file name specified in -UPDATERESYNC= */ int4 updresync_num_histinfo; /* "num_histinfo" member of instance file header from -UPDATERESYNC= */ boolean_t updresync_cross_endian; /* is the -updateresync instance file cross endian relative to current instance */ int4 updresync_num_histinfo_strm[MAX_SUPPL_STRMS]; /* "last_histinfo_num[]" member of instance file header * from -UPDATERESYNC= */ repl_inst_uuid updresync_lms_group; /* "lms_group_info" member of instance file header from -UPDATERESYNC= */ seq_num updresync_jnl_seqno; /* "jnl_seqno" member of instance file header from -UPDATERESYNC= */ repl_inst_uuid remote_lms_group; /* "lms_group_info" member of remote instance file header. * Initialized only if the receiving instance is a supplementary root primary. */ } gtmrecv_local_struct; #ifdef VMS typedef struct { char name[MAX_GSEC_KEY_LEN]; struct dsc$descriptor_s desc; char filler[3]; } vms_shm_key; #endif /* * The following structure contains data items local to the Update Helpers, * but are in the Receive Pool to provide for persistence across instantiations * of the Helpers (the Receive Pool is preserved across helper crashes). */ typedef struct { uint4 helper_pid; /* Owner of this entry. Non-zero indicates entry occupied */ uint4 helper_pid_prev;/* Copy of helper_pid, used to recognize helpers that are now gone and salvage entries */ uint4 helper_type; /* READER or WRITER */ volatile uint4 helper_shutdown;/* used to communicate to the helpers to shut down */ } upd_helper_entry_struct; typedef struct { global_latch_t pre_read_lock; /* operated by pre-readers. Used to control access to next_read_offset */ volatile uint4 pre_read_offset; /* updated by updproc, read-only by pre-readers */ volatile boolean_t first_done; /* pre-readers use this to elect ONE that computes where to begin/resume */ volatile uint4 next_read_offset; /* offset in recvpool of the next record to be pre-read by pre-readers */ uint4 start_helpers; /* TRUE: receiver to start helpers, FALSE: receiver finished helper start */ uint4 start_n_readers; /* start/started these many readers */ uint4 start_n_writers; /* start/started these many writers */ uint4 reap_helpers; /* receiver to salvage slots vacated by dead helpers */ upd_helper_entry_struct helper_list[MAX_UPD_HELPERS]; /* helper information */ } upd_helper_ctl_struct; /* * Receive pool shared memory layout - * * recvpool_ctl_struct * upd_proc_local_struct * gtmrecv_local_struct * upd_helper_ctl_struct * zero or more journal records */ #define RECVPOOL_CTL_SIZE ROUND_UP(SIZEOF(recvpool_ctl_struct), CACHELINE_SIZE) #define UPD_PROC_LOCAL_SIZE ROUND_UP(SIZEOF(upd_proc_local_struct), CACHELINE_SIZE) #define GTMRECV_LOCAL_SIZE ROUND_UP(SIZEOF(gtmrecv_local_struct), CACHELINE_SIZE) #define UPD_HELPER_CTL_SIZE ROUND_UP(SIZEOF(upd_helper_ctl_struct), CACHELINE_SIZE) #define RECVDATA_BASE_OFF ROUND_UP(RECVPOOL_CTL_SIZE + UPD_HELPER_CTL_SIZE + GTMRECV_LOCAL_SIZE + UPD_HELPER_CTL_SIZE, \ JNL_REC_START_BNDRY) #if defined(__osf__) && defined(__alpha) # pragma pointer_size(save) # pragma pointer_size(long) #endif typedef recvpool_ctl_struct *recvpool_ctl_ptr_t; typedef upd_proc_local_struct *upd_proc_local_ptr_t; typedef gtmrecv_local_struct *gtmrecv_local_ptr_t; typedef upd_helper_entry_struct *upd_helper_entry_ptr_t; typedef upd_helper_ctl_struct *upd_helper_ctl_ptr_t; #if defined(__osf__) && defined(__alpha) # pragma pointer_size(restore) #endif typedef struct { recvpool_ctl_ptr_t recvpool_ctl; upd_proc_local_ptr_t upd_proc_local; gtmrecv_local_ptr_t gtmrecv_local; upd_helper_ctl_ptr_t upd_helper_ctl; sm_uc_ptr_t recvdata_base; #ifdef UNIX gd_region *recvpool_dummy_reg; #elif VMS int4 shm_range[2]; int4 shm_lockid; vms_shm_key vms_recvpool_key; #endif } recvpool_addrs; typedef enum { UPDPROC, UPD_HELPER_READER, UPD_HELPER_WRITER, GTMRECV, # ifdef UNIX GTMZPEEK # endif # ifdef VMS GTMRECV_CHILD # endif } recvpool_user; typedef struct { boolean_t start; boolean_t shut_down; boolean_t checkhealth; boolean_t statslog; boolean_t showbacklog; boolean_t updateonly; boolean_t stopsourcefilter; boolean_t changelog; gtm_uint64_t buffsize; int4 shutdown_time; int4 listen_port; boolean_t updateresync; boolean_t noresync; uint4 rcvr_log_interval; uint4 upd_log_interval; boolean_t helpers; boolean_t reuse_specified; boolean_t resume_specified; boolean_t initialize_specified; int4 resume_strm_num; int4 n_readers; int4 n_writers; int4 cmplvl; char log_file[MAX_FN_LEN + 1]; char updresync_instfilename[MAX_FN_LEN + 1]; char filter_cmd[MAX_FILTER_CMD_LEN]; char reuse_instname[MAX_INSTNAME_LEN]; boolean_t autorollback; boolean_t autorollback_verbose; boolean_t stopreceiverfilter; } gtmrecv_options_t; #include "gtm_inet.h" /********** Receiver server function prototypes **********/ int gtmrecv(void); int gtmrecv_changelog(void); int gtmrecv_checkhealth(void); int gtmrecv_comm_init(in_port_t port); int gtmrecv_end1(boolean_t auto_shutdown); int gtmrecv_endupd(void); void gtmrecv_end(void); int gtmrecv_get_opt(void); int gtmrecv_poll_actions1(int *pending_data_len, int *buff_unprocessed, unsigned char *buffp); int gtmrecv_poll_actions(int pending_data_len, int buff_unprocessed, unsigned char *buffp); void gtmrecv_process(boolean_t crash_restart); int gtmrecv_showbacklog(void); int gtmrecv_shutdown(boolean_t auto_shutdown, int exit_status); void gtmrecv_sigstop(void); void gtmrecv_autoshutdown(void); int gtmrecv_statslog(void); int gtmrecv_ipc_cleanup(boolean_t auto_shutdown, int *exit_status); int gtmrecv_start_updonly(void); int gtmrecv_upd_proc_init(boolean_t fresh_start); int gtmrecv_wait_for_detach(void); void gtmrecv_exit(int exit_status); int gtmrecv_alloc_msgbuff(void); void gtmrecv_free_msgbuff(void); int gtmrecv_alloc_filter_buff(int bufsiz); void gtmrecv_free_filter_buff(void); int is_updproc_alive(void); int is_srv_alive(int srv_type); int is_recv_srv_alive(void); void recvpool_init(recvpool_user pool_user, boolean_t gtmrecv_startup); void gtmrecv_reinit_logseqno(void); int gtmrecv_helpers_init(int n_readers, int n_writers); int gtmrecv_start_helpers(int n_readers, int n_writers); void gtmrecv_reap_helpers(boolean_t wait); int gtmrecv_end_helpers(boolean_t is_rcvr_srvr); void gtmrecv_onln_rlbk_clnup(void); int gtmrecv_stopfilter(void); #endif fis-gtm-V7.0-005/sr_unix/gtmrecv_end.c0000644000032200000250000001741514342376330016512 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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_unistd.h" /* for close */ #include "gtm_string.h" #include "gtm_ipc.h" #include #include #include #include #include "gtm_inet.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "repl_shutdcode.h" #include "gtmrecv.h" #include "repl_dbg.h" #include "gtm_stdio.h" #include "eintr_wrappers.h" #include "jnl.h" #include "repl_filter.h" #include "repl_msg.h" #include "repl_sem.h" #include "repl_log.h" #include "is_proc_alive.h" #include "gtmsource.h" #include "gtmio.h" #include "have_crit.h" #include "repl_comm.h" #include "anticipatory_freeze.h" #ifdef GTM_TLS #include "gtm_repl.h" #endif GBLREF uint4 process_id; GBLREF recvpool_addrs recvpool; GBLREF int gtmrecv_filter; GBLREF boolean_t gtmrecv_logstats; GBLREF int gtmrecv_listen_sock_fd; GBLREF int gtmrecv_sock_fd; GBLREF int gtmrecv_log_fd; GBLREF FILE *gtmrecv_log_fp; GBLREF qw_num repl_recv_data_recvd; GBLREF qw_num repl_recv_data_processed; GBLREF repl_msg_ptr_t gtmrecv_msgp; GBLREF uchar_ptr_t repl_filter_buff; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF int pool_init; error_def(ERR_SYSCALL); int gtmrecv_endupd(void) { pid_t savepid; int exit_status, status, save_errno; pid_t waitpid_res; repl_log(stdout, TRUE, TRUE, "Initiating shut down of Update Process\n"); recvpool.upd_proc_local->upd_proc_shutdown = SHUTDOWN; /* Signal the update process to check for the update. */ PTHREAD_COND_SIGNAL(&recvpool.recvpool_ctl->write_updated, status); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_cond_signal"), CALLFROM, status, 0); /* Wait for update process to shut down */ while((SHUTDOWN == recvpool.upd_proc_local->upd_proc_shutdown) && (0 < (savepid = (pid_t)recvpool.upd_proc_local->upd_proc_pid)) && is_proc_alive(savepid, 0)) { SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_SHUTDOWN); WAITPID(savepid, &exit_status, WNOHANG, waitpid_res); /* Release defunct update process if dead */ } exit_status = recvpool.upd_proc_local->upd_proc_shutdown; if (SHUTDOWN == exit_status) { if (0 == savepid) /* No Update Process */ exit_status = NORMAL_SHUTDOWN; else /* Update Process Crashed */ { repl_log(stderr, TRUE, TRUE, "Update Process exited abnormally, INTEGRITY CHECK might be warranted\n"); exit_status = ABNORMAL_SHUTDOWN; } } /* Wait for the Update Process to detach */ if (0 == grab_sem(RECV, UPD_PROC_COUNT_SEM)) { if (0 != (status = rel_sem(RECV, UPD_PROC_COUNT_SEM))) { save_errno = errno; repl_log(stderr, TRUE, TRUE, "Error releasing the Update Process Count semaphore : %s\n", STRERROR(save_errno)); } repl_log(stdout, TRUE, TRUE, "Update Process exited\n"); } else { save_errno = errno; repl_log(stderr, TRUE, TRUE, "Error in update proc count semaphore : %s\n", STRERROR(save_errno)); exit_status = ABNORMAL_SHUTDOWN; } return (exit_status); } int gtmrecv_end1(boolean_t auto_shutdown) { int4 strm_idx; int exit_status, idx, status, save_errno; int fclose_res, rc; seq_num log_seqno, log_seqno1, jnlpool_seqno, jnlpool_strm_seqno[MAX_SUPPL_STRMS]; uint4 savepid; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; exit_status = gtmrecv_end_helpers(TRUE); exit_status = gtmrecv_endupd(); log_seqno = recvpool.recvpool_ctl->jnl_seqno; log_seqno1 = recvpool.upd_proc_local->read_jnl_seqno; strm_idx = recvpool.gtmrecv_local->strm_index; /* Detach from receive pool */ recvpool.gtmrecv_local->shutdown = exit_status; recvpool.gtmrecv_local->recv_serv_pid = 0; if (0 > SHMDT(recvpool.recvpool_ctl)) { save_errno = errno; repl_log(stderr, TRUE, TRUE, "Error detaching from Receive Pool : %s\n", STRERROR(save_errno)); } recvpool.recvpool_ctl = NULL; assert((NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl)); if ((NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl)) { /* Reset fields that might have been initialized by the receiver server after connecting to the primary. * It is ok not to hold the journal pool lock while updating jnlpool_ctl fields since this will be the * only process updating those fields. */ jnlpool->jnlpool_ctl->primary_instname[0] = '\0'; jnlpool->jnlpool_ctl->gtmrecv_pid = 0; jnlpool_seqno = jnlpool->jnlpool_ctl->jnl_seqno; for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) jnlpool_strm_seqno[idx] = jnlpool->jnlpool_ctl->strm_seqno[idx]; /* Also take this opportunity to detach from the journal pool except in the auto_shutdown case. This is because * the fields "jnlpool_ctl->repl_inst_filehdr->recvpool_semid" and "jnlpool_ctl->repl_inst_filehdr->recvpool_shmid" * need to be reset by "gtmrecv_jnlpool_reset" (called from "gtmrecv_shutdown") which is invoked a little later. * However, if IFOE is configured we need the journal pool attached so that we can check for instance freeze in * database rundown, so skip the detach. * In that case, the detach will happen automatically when the process terminates. */ if (!auto_shutdown && !INST_FREEZE_ON_ERROR_POLICY) { JNLPOOL_SHMDT(jnlpool, status, save_errno); if (0 > status) repl_log(stderr, TRUE, TRUE, "Error detaching from Journal Pool : %s\n", STRERROR(save_errno)); } } else jnlpool_seqno = 0; gtmrecv_free_msgbuff(); gtmrecv_free_filter_buff(); recvpool.recvpool_ctl = NULL; /* Close the connection with the Receiver */ if (FD_INVALID != gtmrecv_listen_sock_fd) CLOSEFILE_RESET(gtmrecv_listen_sock_fd, rc); /* resets "gtmrecv_listen_sock_fd" to FD_INVALID */ repl_close(>mrecv_sock_fd); # ifdef GTM_TLS /* Free up the SSL/TLS socket structures. */ if (NULL != repl_tls.sock) gtm_tls_session_close(&repl_tls.sock); assert(NULL == repl_tls.sock); /* Free up the SSL/TLS context now that we are shutting down. */ if (NULL != tls_ctx) gtm_tls_fini(&tls_ctx); assert(NULL == tls_ctx); # endif repl_log(gtmrecv_log_fp, TRUE, FALSE, "REPL INFO - Current Jnlpool Seqno : %llu\n", jnlpool_seqno); for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) { if (jnlpool_strm_seqno[idx]) repl_log(gtmrecv_log_fp, TRUE, FALSE, "REPL INFO - Stream # %d : Current Jnlpool Stream Seqno : %llu\n", idx, jnlpool_strm_seqno[idx]); } if (0 < strm_idx) repl_log(gtmrecv_log_fp, TRUE, FALSE, "REPL INFO - Receiver server has Stream # %d\n", strm_idx); repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL INFO - Current Update process Read Seqno : %llu\n", log_seqno1); repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL INFO - Current Receive Pool Seqno : %llu\n", log_seqno); /* If log_seqno/log_seqno1 is 0, then do not decrement it as that will be interpreted as a huge positive seqno. Keep it 0 */ if (log_seqno) log_seqno--; if (log_seqno1) log_seqno1--; repl_log(gtmrecv_log_fp, TRUE, FALSE, "REPL INFO - Last Recvd Seqno : %llu Jnl Total : %llu Msg Total : %llu\n", log_seqno, repl_recv_data_processed, repl_recv_data_recvd); repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL INFO - Last Seqno processed by update process : %llu\n", log_seqno1); if (gtmrecv_filter & EXTERNAL_FILTER) repl_stop_filter(); if (auto_shutdown) return (exit_status); else gtmrecv_exit(exit_status - NORMAL_SHUTDOWN); return -1; /* This will never get executed, added to make compiler happy */ } void gtmrecv_end(void) { gtmrecv_end1(FALSE); } fis-gtm-V7.0-005/sr_unix/gtmrecv_fetchresync.c0000644000032200000250000005356514342376330020267 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_ipc.h" #include "gtm_socket.h" #include "gtm_string.h" #include "gtm_inet.h" #include "gtm_stdio.h" #include "gtm_select.h" #include "gtm_un.h" #include "gtm_time.h" /* needed for difftime() definition; if this file is not included, difftime returns bad values on AIX */ #include #include #include "gtm_fcntl.h" #include "gtm_unistd.h" #include #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" /* needed for muprec.h */ #include "hashtab_mname.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "repl_sem.h" #include "muprec.h" #include "error.h" #include "iosp.h" #include "gtmrecv.h" #include "repl_comm.h" #include "repl_msg.h" #include "repl_errno.h" #include "repl_dbg.h" #include "gtm_logicals.h" #include "eintr_wrappers.h" #include "repl_sp.h" #include "repl_log.h" #include "io.h" #include "trans_log_name.h" #include "util.h" #include "gtmsource.h" #include "repl_instance.h" #include "gtmio.h" #include "replgbl.h" #define MAX_ATTEMPTS_FOR_FETCH_RESYNC 60 /* max-wait in seconds for source server response after connection is established */ #define MAX_WAIT_FOR_FETCHRESYNC_CONN 60 /* max-wait in seconds to establish connection with the source server */ #define FETCHRESYNC_PRIMARY_POLL (MICROSECS_IN_SEC - 1) /* micro seconds, almost 1 second */ #define CHECK_SEND_RECV_LOOP_ERROR(STATUS, WAIT_COUNT, MESSAGE) \ { \ if (SS_NORMAL != STATUS) \ { \ if (EREPL_RECV == repl_errno) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, \ LEN_AND_LIT("Error in recv() for " MESSAGE), STATUS); /* BYPASSOK(recv) */ \ else if (EREPL_SEND == repl_errno) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, \ LEN_AND_LIT("Error in send() for " MESSAGE), STATUS); /* BYPASSOK(send) */ \ else if (EREPL_SELECT == repl_errno) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, \ LEN_AND_LIT("Error in select() for " MESSAGE), STATUS); \ } \ if (0 >= WAIT_COUNT) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, \ LEN_AND_LIT("Waited too long to get/send" MESSAGE " from primary. Check if primary is alive."));\ } #define CHECK_CONN_RETRY(STATUS) \ { \ if ((EREPL_RECV == repl_errno) && (REPL_CONN_RESET(STATUS))) \ { \ if (WBTEST_ENABLED(WBTEST_FETCHCOMM_ERR) || WBTEST_ENABLED(WBTEST_FETCHCOMM_HISTINFO)) \ repl_log(stdout, TRUE, TRUE, "Closing socket and re-establishing connection\n"); \ repl_close(>mrecv_sock_fd); \ gtmrecv_fetchresync_connect(port); \ repl_connection_reset = FALSE; \ reconnect = TRUE; \ } \ } #define SEND_REPL_FETCH_RESYNC_MSG(RESYNC_MSG,MAX_REG_SEQNO) \ { \ /* Send REPL_FETCH_RESYNC message */ \ memset(&RESYNC_MSG, 0, SIZEOF(RESYNC_MSG)); \ RESYNC_MSG.resync_seqno = MAX_REG_SEQNO; \ assert(RESYNC_MSG.resync_seqno); \ RESYNC_MSG.proto_ver = REPL_PROTO_VER_THIS; \ RESYNC_MSG.node_endianness = NODE_ENDIANNESS; \ RESYNC_MSG.is_supplementary = jnlpool->repl_inst_filehdr->is_supplementary; \ remote_side->endianness_known = FALSE; \ remote_side->cross_endian = FALSE; \ gtmrecv_repl_send((repl_msg_ptr_t)&RESYNC_MSG, REPL_FETCH_RESYNC, MIN_REPL_MSGLEN, \ "REPL_FETCH_RESYNC", RESYNC_MSG.resync_seqno); \ if (repl_connection_reset) \ { /* Connection got reset during the above send */ \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_REPLCOMM); \ return ERR_REPLCOMM; \ } \ } GBLREF uint4 process_id; GBLREF int recvpool_shmid; GBLREF int gtmrecv_listen_sock_fd, gtmrecv_sock_fd; GBLREF seq_num seq_num_zero; GBLREF jnl_gbls_t jgbl; GBLREF int repl_max_send_buffsize, repl_max_recv_buffsize; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF boolean_t repl_connection_reset; GBLREF mur_gbls_t murgbl; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; GBLREF repl_conn_info_t *remote_side; GBLREF int4 strm_index; error_def(ERR_PRIMARYNOTROOT); error_def(ERR_RECVPOOLSETUP); error_def(ERR_REPL2OLD); error_def(ERR_REPLCOMM); error_def(ERR_REPLINSTNOHIST); error_def(ERR_TEXT); CONDITION_HANDLER(gtmrecv_fetchresync_ch) { int rc; START_CH(TRUE); if (FD_INVALID != gtmrecv_listen_sock_fd) CLOSEFILE_RESET(gtmrecv_listen_sock_fd, rc); /* resets "gtmrecv_listen_sock_fd" to FD_INVALID */ if (FD_INVALID != gtmrecv_sock_fd) CLOSEFILE_RESET(gtmrecv_sock_fd, rc); /* resets "gtmrecv_sock_fd" to FD_INVALID */ PRN_ERROR; NEXTCH; } void gtmrecv_fetchresync_connect(int port) { int status; struct addrinfo primary_ai; struct sockaddr_storage primary_sas; struct timeval repl_poll_wait; fd_set input_fds; time_t t1, t2; int save_errno; gtmrecv_comm_init(port); primary_ai.ai_addr = (sockaddr_ptr)&primary_sas; primary_ai.ai_addrlen = SIZEOF(primary_sas); remote_side->proto_ver = REPL_PROTO_VER_UNINITIALIZED; repl_log(stdout, TRUE, TRUE, "Waiting for a connection...\n"); assertpro(FD_SETSIZE > gtmrecv_listen_sock_fd); FD_ZERO(&input_fds); FD_SET(gtmrecv_listen_sock_fd, &input_fds); while (TRUE) { t1 = time(NULL); repl_poll_wait.tv_sec = MAX_WAIT_FOR_FETCHRESYNC_CONN; repl_poll_wait.tv_usec = 0; while (0 > (status = select(gtmrecv_listen_sock_fd + 1, &input_fds, NULL, NULL, &repl_poll_wait))) { if ((EINTR == errno) || (EAGAIN == errno)) { t2 = time(NULL); if (0 >= (int)(repl_poll_wait.tv_sec = (MAX_WAIT_FOR_FETCHRESYNC_CONN - (int)difftime(t2, t1)))) { status = 0; break; } repl_poll_wait.tv_usec = 0; FD_SET(gtmrecv_listen_sock_fd, &input_fds); continue; } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Error in select on listen socket"), errno); } if (status == 0) { repl_log(stdout, TRUE, TRUE, "Waited about %d seconds for connection from primary source server\n", MAX_WAIT_FOR_FETCHRESYNC_CONN); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Waited too long to get a connection request. Check if primary is alive.")); } ACCEPT_SOCKET(gtmrecv_listen_sock_fd, primary_ai.ai_addr, (GTM_SOCKLEN_TYPE *)&primary_ai.ai_addrlen, gtmrecv_sock_fd); if (FD_INVALID != gtmrecv_sock_fd) break; save_errno = errno; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Error accepting connection from Source Server"), save_errno); } /* Connection established */ repl_close(>mrecv_listen_sock_fd); /* Close the listener socket */ repl_connection_reset = FALSE; return; } int gtmrecv_fetchresync(int port, seq_num *resync_seqno, seq_num max_reg_seqno) { repl_resync_msg_t resync_msg; repl_msg_t msg; uchar_ptr_t msgp; unsigned char *msg_ptr; /* needed for REPL_{SEND,RECV}_LOOP */ int tosend_len, sent_len, sent_this_iter; /* needed for REPL_SEND_LOOP */ int torecv_len, recvd_len, recvd_this_iter; /* needed for REPL_RECV_LOOP */ int status, poll_dir; /* needed for REPL_{SEND,RECV}_LOOP */ int wait_count, save_errno; char seq_num_str[32], *seq_num_ptr; pid_t rollback_pid; int rollback_status; int wait_status; repl_old_instinfo_msg_t old_instinfo_msg; repl_old_needinst_msg_ptr_t old_need_instinfo_msg; repl_needinst_msg_t need_instinfo_msg; repl_needhistinfo_msg_ptr_t need_histinfo_msg; repl_histinfo histinfo; seq_num histinfo_seqno; short retry_num; repl_inst_hdr_ptr_t inst_hdr; repl_logfile_info_msg_t logfile_msg; uint4 len; boolean_t reconnect = FALSE; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; repl_log(stdout, TRUE, TRUE, "Assuming primary supports multisite functionality. Connecting using multisite communication protocol.\n"); ESTABLISH_RET(gtmrecv_fetchresync_ch, (!SS_NORMAL)); QWASSIGN(*resync_seqno, seq_num_zero); gtmrecv_fetchresync_connect(port); if (0 != (status = get_send_sock_buff_size(gtmrecv_sock_fd, &repl_max_send_buffsize)) || 0 != (status = get_recv_sock_buff_size(gtmrecv_sock_fd, &repl_max_recv_buffsize))) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Error getting socket send/recv buffsizes"), status); } repl_log(stdout, TRUE, TRUE, "Connection established, using TCP send buffer size %d receive buffer size %d\n", repl_max_send_buffsize, repl_max_recv_buffsize); repl_log_conn_info(gtmrecv_sock_fd, stdout, FALSE); SEND_REPL_FETCH_RESYNC_MSG(resync_msg,max_reg_seqno); /* Wait for REPL_RESYNC_SEQNO (if dual-site primary) or REPL_OLD_NEED_INSTANCE_INFO (if multi-site primary) * or REPL_NEED_INSTINFO (if multi-site primary with supplementary instance support) message */ do { wait_count = MAX_ATTEMPTS_FOR_FETCH_RESYNC; assert(SIZEOF(msg) == MIN_REPL_MSGLEN); REPL_RECV_LOOP_FETCHRESYNC(gtmrecv_sock_fd, &msg, MIN_REPL_MSGLEN, REPL_POLL_WAIT) { if (SS_NORMAL == status) { recvd_len += recvd_this_iter; torecv_len -= recvd_this_iter; if (torecv_len <= 0) break; } /* Else connection reset || torecv_len > 0*/ if (0 >= wait_count) break; repl_log(stdout, TRUE, TRUE, "Waiting for REPL_RESYNC_SEQNO or REPL_OLD_NEED_INSTANCE_INFO " " or REPL_NEED_INSTINFO or REPL_NEED_HISTINFO\n"); wait_count--; CHECK_CONN_RETRY(status); if (reconnect) /* There was connection reset and we re-connected */ { if (0 != (status = get_send_sock_buff_size(gtmrecv_sock_fd, &repl_max_send_buffsize)) || 0 != (status = get_recv_sock_buff_size(gtmrecv_sock_fd, &repl_max_recv_buffsize))) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Error getting socket send/recv buffsizes"), status); } repl_log(stdout, TRUE, TRUE, "Connection established, using TCP send buffer size %d receive buffer size %d\n", repl_max_send_buffsize, repl_max_recv_buffsize); repl_log_conn_info(gtmrecv_sock_fd, stdout, FALSE); SEND_REPL_FETCH_RESYNC_MSG(resync_msg,max_reg_seqno); reconnect = FALSE; if (WBTEST_ENABLED(WBTEST_FETCHCOMM_ERR)) gtm_wbox_input_test_case_count = 6; /* Do not go to white box again*/ } } CHECK_SEND_RECV_LOOP_ERROR(status, wait_count, "RESYNC JNLSEQNO"); if (!remote_side->endianness_known) { remote_side->endianness_known = TRUE; if ((REPL_MSGTYPE_LAST < msg.type) && (REPL_MSGTYPE_LAST > GTM_BYTESWAP_32(msg.type))) { remote_side->cross_endian = TRUE; repl_log(stdout, TRUE, TRUE, "Source and Receiver sides have opposite endianness\n"); } else { remote_side->cross_endian = FALSE; repl_log(stdout, TRUE, TRUE, "Source and Receiver sides have same endianness\n"); } } if (remote_side->cross_endian) { msg.type = GTM_BYTESWAP_32(msg.type); msg.len = GTM_BYTESWAP_32(msg.len); } switch(msg.type) { case REPL_OLD_NEED_INSTANCE_INFO: assert((NULL != jnlpool) && (NULL != jnlpool->repl_inst_filehdr)); old_need_instinfo_msg = (repl_old_needinst_msg_ptr_t)&msg; repl_log(stdout, TRUE, TRUE, "Received REPL_OLD_NEED_INSTANCE_INFO message from primary " "instance [%s]\n", old_need_instinfo_msg->instname); if (jnlpool->repl_inst_filehdr->is_supplementary) { /* Issue REPL2OLD error because this is a supplementary instance and remote side runs * on a GT.M version that does not understand the supplementary protocol */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPL2OLD, 4, LEN_AND_STR(old_need_instinfo_msg->instname), LEN_AND_STR(jnlpool->repl_inst_filehdr->inst_info.this_instname)); } remote_side->proto_ver = old_need_instinfo_msg->proto_ver; assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver); assert(REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver); memset(&old_instinfo_msg, 0, SIZEOF(old_instinfo_msg)); memcpy(old_instinfo_msg.instname, jnlpool->repl_inst_filehdr->inst_info.this_instname, MAX_INSTNAME_LEN - 1); old_instinfo_msg.was_rootprimary = (unsigned char)repl_inst_was_rootprimary(); murgbl.was_rootprimary = old_instinfo_msg.was_rootprimary; assert(MIN_REPL_MSGLEN == SIZEOF(old_instinfo_msg)); gtmrecv_repl_send((repl_msg_ptr_t)&old_instinfo_msg, REPL_OLD_INSTANCE_INFO, MIN_REPL_MSGLEN, "REPL_OLD_INSTANCE_INFO", MAX_SEQNO); if (old_instinfo_msg.was_rootprimary && !old_need_instinfo_msg->is_rootprimary) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_PRIMARYNOTROOT, 2, LEN_AND_STR((char *)old_need_instinfo_msg->instname)); break; case REPL_NEED_INSTINFO: /* We got only a part of this message. Get the remaining part as well */ assert(SIZEOF(need_instinfo_msg) > MIN_REPL_MSGLEN); memcpy(&need_instinfo_msg, &msg, MIN_REPL_MSGLEN); msgp = (uchar_ptr_t)&need_instinfo_msg + MIN_REPL_MSGLEN; REPL_RECV_LOOP(gtmrecv_sock_fd, msgp, SIZEOF(need_instinfo_msg) - MIN_REPL_MSGLEN, REPL_POLL_WAIT) { if (0 >= wait_count) break; repl_log(stdout, TRUE, TRUE, "Waiting for REPL_NEED_INSTINFO\n"); wait_count--; } CHECK_SEND_RECV_LOOP_ERROR(status, wait_count, "REPL_NEED_INSTINFO"); gtmrecv_check_and_send_instinfo(&need_instinfo_msg, IS_RCVR_SRVR_FALSE); break; case REPL_NEED_HISTINFO: /* case REPL_NEED_TRIPLE_INFO: too but that message has been renamed to REPL_NEED_HISTINFO */ need_histinfo_msg = RECAST(repl_needhistinfo_msg_ptr_t)&msg; /* In case of fetchresync rollback, the REPL_NEED_HISTINFO message asks for a seqno to be found. * Not a specific histinfo number. The latter is valid only in the receiver. Assert this. * Versions older than V55000 dont set this histinfo number so relax the assert accordingly. */ assert((INVALID_HISTINFO_NUM == need_histinfo_msg->histinfo_num) || ((0 == need_histinfo_msg->histinfo_num) && (REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver))); assert(remote_side->endianness_known); /* only then is remote_side->cross_endian reliable */ if (!remote_side->cross_endian) histinfo_seqno = need_histinfo_msg->seqno; else histinfo_seqno = GTM_BYTESWAP_64(need_histinfo_msg->seqno); assert((INVALID_SUPPL_STRM == strm_index) || ((0 <= strm_index) && (MAX_SUPPL_STRMS > strm_index))); if (0 < strm_index) { /* Source side is a non-supplementary instance. */ repl_log(stdout, TRUE, TRUE, "Received REPL_NEED_HISTINFO message for " "Stream %d : Seqno %llu [0x%llx]\n", strm_index, histinfo_seqno, histinfo_seqno); } else repl_log(stdout, TRUE, TRUE, "Received REPL_NEED_HISTINFO message for " "Seqno %llu [0x%llx]\n", histinfo_seqno, histinfo_seqno); assert(REPL_PROTO_VER_UNINITIALIZED != remote_side->proto_ver); assert((NULL != jnlpool) && (NULL != jnlpool->jnlpool_dummy_reg)); /* repl_inst_wrapper_triple_find_seqno needs the ftok lock on the replication instance file. But, * But, ROLLBACK already holds JNLPOOL and RECVPOOL access semaphores and so asking for ftok lock * can result in potential deadlocks. Since we hold the JNLPOOL and RECVPOOL semaphores, no one * else can startup until we are done. So, assert that we hold the access control semaphores and * proceed. */ ASSERT_HOLD_REPLPOOL_SEMS; status = repl_inst_wrapper_histinfo_find_seqno(histinfo_seqno, strm_index, &histinfo); if (0 != status) { /* Close the connection. The function call above would have issued the error. */ assert(ERR_REPLINSTNOHIST == status); repl_log(stdout, TRUE, TRUE, "Connection reset due to REPLINSTNOHIST error\n"); repl_connection_reset = TRUE; repl_close(>mrecv_sock_fd); return status; } if (0 < strm_index) { /* About to send to a non-supplementary instance. It does not understand strm_seqnos. * So convert it back to a format it understands. */ CONVERT_SUPPL2NONSUPPL_HISTINFO(histinfo); } assert(histinfo.start_seqno < histinfo_seqno); gtmrecv_send_histinfo(&histinfo); break; case REPL_INST_NOHIST: repl_log(stdout, TRUE, TRUE, "Originating instance encountered a REPLINSTNOHIST error." " JNL_SEQNO of this replicating instance precedes the current history in the " "originating instance file. Rollback exiting.\n"); status = ERR_REPLINSTNOHIST; repl_log(stdout, TRUE, TRUE, "Connection reset due to REPLINSTNOHIST error on primary\n"); repl_connection_reset = TRUE; repl_close(>mrecv_sock_fd); return status; break; case REPL_RESYNC_SEQNO: repl_log(stdout, TRUE, TRUE, "Received REPL_RESYNC_SEQNO message\n"); if (REPL_PROTO_VER_UNINITIALIZED == remote_side->proto_ver) { /* Issue REPL2OLD error because primary is dual-site */ assert((NULL != jnlpool) && (NULL != jnlpool->repl_inst_filehdr)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPL2OLD, 4, LEN_AND_STR(UNKNOWN_INSTNAME), LEN_AND_STR(jnlpool->repl_inst_filehdr->inst_info.this_instname)); } assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver); /* The following fields dont need to be initialized since they are needed (for internal filter * transformations) only if we send journal records across. Besides we are going to close * the connection now and proceed with the rollback. * remote_side->jnl_ver = ... * remote_side->is_std_null_coll = ... * remote_side->trigger_supported = ... * remote_side->null_subs_xform = ... */ break; case REPL_LOGFILE_INFO: /* We got only a part of this message. Get the remaining part as well */ assert(SIZEOF(logfile_msg) > MIN_REPL_MSGLEN); assert(MIN_REPL_MSGLEN < msg.len); assert(remote_side->endianness_known); msgp = (uchar_ptr_t)&logfile_msg; memcpy(msgp, &msg, MIN_REPL_MSGLEN); REPL_RECV_LOOP(gtmrecv_sock_fd, msgp + MIN_REPL_MSGLEN, msg.len - MIN_REPL_MSGLEN, REPL_POLL_WAIT) { if (0 >= wait_count) break; wait_count--; } CHECK_SEND_RECV_LOOP_ERROR(status, wait_count, "REPL_LOGFILE_INFO"); assert(REPL_PROTO_VER_REMOTE_LOGPATH <= logfile_msg.proto_ver); if (remote_side->cross_endian) { logfile_msg.fullpath_len = GTM_BYTESWAP_32(logfile_msg.fullpath_len); logfile_msg.pid = GTM_BYTESWAP_32(logfile_msg.pid); } assert('\0' == logfile_msg.fullpath[logfile_msg.fullpath_len - 1]); repl_log(stdout, TRUE, TRUE, "Remote side source log file path is %s; Source Server PID = %d\n", logfile_msg.fullpath, logfile_msg.pid); /* Now send our logfile path to the source side. Since the rollback doesn't have a logfile per-se, * send just the $PWD */ len = repl_logfileinfo_get(NULL, &logfile_msg, remote_side->cross_endian, (FILE *)stdout); REPL_SEND_LOOP(gtmrecv_sock_fd, &logfile_msg, len, REPL_POLL_WAIT) { if (0 >= wait_count) break; wait_count--; } CHECK_SEND_RECV_LOOP_ERROR(status, wait_count, "REPL_LOGFILE_INFO"); break; default: repl_log(stdout, TRUE, TRUE, "Message of unknown type (%d) received\n", msg.type); assert(FALSE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_REPLCOMM); break; } } while (!repl_connection_reset && (REPL_RESYNC_SEQNO != msg.type)); if (repl_connection_reset) { /* Connection got reset during the above send */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_REPLCOMM); return ERR_REPLCOMM; } assert(remote_side->endianness_known); /* only then is remote_side->cross_endian reliable */ if (!remote_side->cross_endian) QWASSIGN(*resync_seqno, *(seq_num *)&msg.msg[0]); else QWASSIGN(*resync_seqno, GTM_BYTESWAP_64(*(seq_num *)&msg.msg[0])); /* Wait till connection is broken or REPL_CONN_CLOSE is received */ REPL_RECV_LOOP(gtmrecv_sock_fd, &msg, MIN_REPL_MSGLEN, REPL_POLL_WAIT) { REPL_DPRINT1("FETCH_RESYNC : Waiting for source to send CLOSE_CONN or connection breakage\n"); } repl_close(>mrecv_sock_fd); REVERT; repl_log(stdout, TRUE, TRUE, "Received RESYNC SEQNO is %llu [0x%llx]\n", *resync_seqno, *resync_seqno); /* We expect the resync_seqno to be less than or equal to the instance jnl_seqno (which is the maximum reg_seqno * across all regions). The only exception is if this is a supplementary instance and the source side is a * non-supplementary instance in which case the resync_seqno corresponds to a strm_seqno whereas max_reg_seqno * corresponds to the unified jnl_seqno (across all streams) and those are two different dimensions and so cannot * be compared directly. It is possible that the strm_seqno is GREATER than the unified jnl_seqno. Take below example. * * Say in instance A (from A->P terminology) I keep updating the same node ^a=1 a million times. * And do say 100 local updates on P. And then do a mupip load of A's extract onto P and initialize -updateresync * replication between A->P. Then the strm_seqno of strm # 1 would be 1 million + 1 but the jnl_seqno of P would be * only 100 + 1. So the unified jnl_seqno 101 is a lot less than the strm_seqno of 1,000,001. This is a valid scenario * and there is nothing in the code that otherwise requires this ordering for their correct operation. */ assert((*resync_seqno <= max_reg_seqno) || jnlpool->repl_inst_filehdr->is_supplementary); return SS_NORMAL; } fis-gtm-V7.0-005/sr_unix/gtmrecv_poll_actions.c0000644000032200000250000006161414342376330020432 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2008-2021 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_stdio.h" #include "gtm_string.h" #include "gtm_unistd.h" #include "gtm_time.h" #include #include #include "gtm_inet.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_shutdcode.h" #include "gtmrecv.h" #include "repl_comm.h" #include "repl_msg.h" #include "repl_dbg.h" #include "repl_log.h" #include "repl_errno.h" #include "iosp.h" #include "eintr_wrappers.h" #include "gt_timer.h" #include "gtmio.h" #include "util.h" #include "tp_change_reg.h" #include "memcoherency.h" #include "replgbl.h" #include "gtmsource.h" #ifdef GTM_TLS #include "gtm_repl.h" #endif #include "jnl.h" #include "repl_filter.h" GBLREF repl_msg_ptr_t gtmrecv_msgp; GBLREF int gtmrecv_max_repl_msglen; GBLREF int gtmrecv_listen_sock_fd; GBLREF int gtmrecv_sock_fd; GBLREF boolean_t repl_connection_reset; GBLREF recvpool_addrs recvpool; GBLREF int gtmrecv_log_fd; GBLREF FILE *gtmrecv_log_fp; GBLREF boolean_t gtmrecv_logstats; GBLREF boolean_t gtmrecv_wait_for_jnl_seqno; GBLREF boolean_t gtmrecv_bad_trans_sent; GBLREF uint4 log_interval; GBLREF volatile time_t gtmrecv_now; GBLREF boolean_t gtmrecv_send_cmp2uncmp; GBLREF repl_conn_info_t *remote_side; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF int gtmrecv_filter; error_def(ERR_RECVPOOLSETUP); error_def(ERR_REPLCOMM); error_def(ERR_TEXT); #ifdef INT8_SUPPORTED static seq_num last_ack_seqno = 0; #else static seq_num last_ack_seqno = {0, 0}; #endif #define GTMRECV_NEXT_REPORT_FACTOR 2 enum { CONTINUE_POLL, STOP_POLL }; int gtmrecv_poll_actions1(int *pending_data_len, int *buff_unprocessed, unsigned char *buffp) { static int report_cnt = 1; static int next_report_at = 1; static boolean_t send_xoff = FALSE; static boolean_t xoff_sent = FALSE; static seq_num send_seqno; static boolean_t log_draining_msg = FALSE; static boolean_t send_badtrans = FALSE; static boolean_t send_cmp2uncmp = FALSE; static boolean_t upd_shut_too_early_logged = FALSE; static time_t last_reap_time = 0; repl_msg_t xoff_msg; repl_badtrans_msg_t bad_trans_msg; boolean_t alert = FALSE, info = FALSE; int return_status; gd_region *region_top; unsigned char *msg_ptr; /* needed for REPL_{SEND,RECV}_LOOP */ int tosend_len, sent_len, sent_this_iter; /* needed for REPL_SEND_LOOP */ int torecv_len, recvd_len, recvd_this_iter; /* needed for REPL_RECV_LOOP */ int status, poll_dir; /* needed for REPL_{SEND,RECV}_LOOP */ int temp_len, pending_msg_size; int upd_start_status, upd_start_attempts; int buffered_data_len; int upd_exit_status; seq_num temp_send_seqno; boolean_t bad_trans_detected = FALSE, onln_rlbk_flg_set = FALSE; recvpool_ctl_ptr_t recvpool_ctl; upd_proc_local_ptr_t upd_proc_local; gtmrecv_local_ptr_t gtmrecv_local; upd_helper_ctl_ptr_t upd_helper_ctl; pid_t waitpid_res; int4 msg_type, msg_len; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; recvpool_ctl = recvpool.recvpool_ctl; upd_proc_local = recvpool.upd_proc_local; gtmrecv_local = recvpool.gtmrecv_local; upd_helper_ctl = recvpool.upd_helper_ctl; if (SHUTDOWN == gtmrecv_local->shutdown) { repl_log(gtmrecv_log_fp, TRUE, TRUE, "Shutdown signalled\n"); gtmrecv_end(); /* Won't return */ } # ifdef GTM_TLS /* If we sent a REPL_RENEG_ACK, then we cannot afford to send anymore asynchronous messages (like XOFF_ACK_ME) until we * receive a REPL_RENEG_COMPLETE from the source server. This ensures that while the source server attempts to do a SSL/TLS * renegotiation, it doesn't have any application data (like XOFF_ACK_ME) sitting in the pipe. */ if (REPLTLS_WAITING_FOR_RENEG_COMPLETE == repl_tls.renegotiate_state) return STOP_POLL; # endif /* Reset report_cnt and next_report_at to 1 when a new upd proc is forked */ if ((1 == report_cnt) || (report_cnt == next_report_at)) { /* A comment on the usage of NO_SHUTDOWN below for the alert variable. Since upd_proc_local->upd_proc_shutdown is * a shared memory field (and could be concurrently changed by either the receiver server or the update process), * we want to make sure it is the same value BEFORE and AFTER checking whether the update process is alive or not. * If it is not NO_SHUTDOWN (i.e. is SHUTDOWN or NORMAL_SHUTDOWN or ABNORMAL_SHUTDOWN) it has shut down due to * an external request so we do want to send out a false update-process-is-not-alive alert. */ if ((alert = ((NO_SHUTDOWN == upd_proc_local->upd_proc_shutdown) && (SRV_DEAD == is_updproc_alive()) && (NO_SHUTDOWN == upd_proc_local->upd_proc_shutdown))) || (info = (((NORMAL_SHUTDOWN == upd_proc_local->upd_proc_shutdown) || (ABNORMAL_SHUTDOWN == upd_proc_local->upd_proc_shutdown)) && (SRV_DEAD == is_updproc_alive())))) { if (alert) repl_log(gtmrecv_log_fp, TRUE, TRUE, "ALERT : Receiver Server detected that Update Process is not ALIVE\n"); else repl_log(gtmrecv_log_fp, TRUE, TRUE, "INFO : Update process not running due to user initiated shutdown\n"); if (1 == report_cnt) { send_xoff = TRUE; recvpool_ctl->old_jnl_seqno = recvpool_ctl->jnl_seqno; recvpool_ctl->jnl_seqno = 0; /* Even though we have identified that the update process is NOT alive, a waitpid on the update * process PID is necessary so that the system doesn't leave any zombie process lying around. * This is possible since any child process that dies without the parent doing a waitpid on it * will be defunct unless the parent dies at which point the "init" process takes the role of * the parent and invokes waitpid to remove the zombies. * NOTE: It is possible that the update process was killed before the receiver server got a * chance to record it's PID in the recvpool.upd_proc_local structure. In such a case, don't * invoke waitpid as that will block us (receiver server) if this instance of the receiver * server was started with helper processes. */ if (0 < upd_proc_local->upd_proc_pid) { WAITPID(upd_proc_local->upd_proc_pid, &upd_exit_status, 0, waitpid_res); /* Since the update process as part of its shutdown does NOT reset the upd_proc_pid, reset * it here ONLY if the update process was NOT kill -9ed. This is needed because receiver * server as part of its shutdown relies on this field (upd_proc_pid) to determine if the * update process was cleanly shutdown or was kill -9ed. */ if (!alert) upd_proc_local->upd_proc_pid = 0; } upd_proc_local->bad_trans = FALSE; /* No point in doing bad transaction processing */ upd_proc_local->onln_rlbk_flg = FALSE; /* No point handling online rollback */ } gtmrecv_wait_for_jnl_seqno = TRUE; REPL_DPRINT1( "gtmrecv_poll_actions : Setting gtmrecv_wait_for_jnl_seqno to TRUE because of upd crash/shutdown\n"); next_report_at *= GTMRECV_NEXT_REPORT_FACTOR; report_cnt++; } } else report_cnt++; /* Check if REPL_CMP2UNCMP or REPL_BADTRANS message needs to be sent */ if (upd_proc_local->onln_rlbk_flg) { /* Update process detected an online rollback and is requesting us to restart the connection. But before that, send * REPL_XOFF source side and drain the replication pipe */ onln_rlbk_flg_set = TRUE; send_xoff = TRUE; } else if (!send_cmp2uncmp && gtmrecv_send_cmp2uncmp) { send_xoff = TRUE; send_seqno = recvpool_ctl->jnl_seqno; send_cmp2uncmp = TRUE; } else if (!send_badtrans && upd_proc_local->bad_trans) { send_xoff = TRUE; send_seqno = upd_proc_local->read_jnl_seqno; send_badtrans = TRUE; bad_trans_detected = TRUE; } else if (!upd_proc_local->bad_trans && send_badtrans && 1 != report_cnt) { send_badtrans = FALSE; bad_trans_detected = FALSE; } if (send_xoff && !xoff_sent) { /* Send XOFF_ACK_ME if the receiver has a connection to the source. Do not attempt to send it if we dont even * know the endianness of the remote side. In that case, we are guaranteed no initial handshake occurred and * so no point sending the XOFF too. This saves us lots of trouble in case of cross-endian replication connections. */ assert((FD_INVALID != gtmrecv_sock_fd) || repl_connection_reset); if ((FD_INVALID != gtmrecv_sock_fd) && remote_side->endianness_known) { send_seqno = upd_proc_local->read_jnl_seqno; if (!remote_side->cross_endian) { xoff_msg.type = REPL_XOFF_ACK_ME; xoff_msg.len = MIN_REPL_MSGLEN; memcpy((uchar_ptr_t)&xoff_msg.msg[0], (uchar_ptr_t)&send_seqno, SIZEOF(seq_num)); } else { xoff_msg.type = GTM_BYTESWAP_32(REPL_XOFF_ACK_ME); xoff_msg.len = GTM_BYTESWAP_32(MIN_REPL_MSGLEN); temp_send_seqno = GTM_BYTESWAP_64(send_seqno); memcpy((uchar_ptr_t)&xoff_msg.msg[0], (uchar_ptr_t)&temp_send_seqno, SIZEOF(seq_num)); } REPL_SEND_LOOP(gtmrecv_sock_fd, &xoff_msg, MIN_REPL_MSGLEN, REPL_POLL_NOWAIT) ; /* Empty Body */ if (SS_NORMAL != status) { if (REPL_CONN_RESET(status) && EREPL_SEND == repl_errno) { repl_log(gtmrecv_log_fp, TRUE, TRUE, "Connection reset while sending XOFF_ACK_ME. " "Status = %d ; %s\n", status, STRERROR(status)); repl_close(>mrecv_sock_fd); repl_connection_reset = TRUE; xoff_sent = FALSE; send_badtrans = FALSE; } else if (EREPL_SEND == repl_errno) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Error sending XOFF msg due to BAD_TRANS or UPD crash/shutdown. " "Error in send"), status); else { assert(EREPL_SELECT == repl_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Error sending XOFF msg due to BAD_TRANS or UPD crash/shutdown. " "Error in select"), status); } } else { xoff_sent = TRUE; log_draining_msg = TRUE; } repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL_XOFF_ACK_ME sent due to upd shutdown/crash or bad trans " "or ONLINE_ROLLBACK\n"); send_xoff = FALSE; } else { /* Connection has been lost OR initial handshake needs to happen again, so no point sending XOFF/BADTRANS */ send_xoff = FALSE; send_badtrans = FALSE; } } /* Drain pipe */ if (xoff_sent) { if (log_draining_msg) { /* avoid multiple logs per instance */ repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL INFO - Draining replication pipe due to %s\n", send_cmp2uncmp ? "CMP2UNCMP" : (send_badtrans ? "BAD_TRANS" : (onln_rlbk_flg_set ? "ONLINE_ROLLBACK" : "UPD shutdown/crash"))); log_draining_msg = FALSE; } if (0 != *buff_unprocessed) { /* Throw away the current contents of the buffer */ buffered_data_len = ((*pending_data_len <= *buff_unprocessed) ? *pending_data_len : *buff_unprocessed); *buff_unprocessed -= buffered_data_len; buffp += buffered_data_len; *pending_data_len -= buffered_data_len; REPL_DPRINT2("gtmrecv_poll_actions : (1) Throwing away %d bytes from old buffer while draining\n", buffered_data_len); assert(remote_side->endianness_known); /* only then is remote_side->cross_endian reliable */ while (REPL_MSG_HDRLEN <= *buff_unprocessed) { assert(0 == (((unsigned long)buffp) % REPL_MSG_ALIGN)); msg_len = ((repl_msg_ptr_t)buffp)->len; msg_type = ((repl_msg_ptr_t)buffp)->type; if (remote_side->cross_endian) { msg_len = GTM_BYTESWAP_32(msg_len); msg_type = GTM_BYTESWAP_32(msg_type); } msg_type = (msg_type & REPL_TR_CMP_MSG_TYPE_MASK); assert((REPL_TR_CMP_JNL_RECS == msg_type) || (0 == (msg_len % REPL_MSG_ALIGN))); *pending_data_len = ROUND_UP2(msg_len, REPL_MSG_ALIGN); buffered_data_len = ((*pending_data_len <= *buff_unprocessed) ? *pending_data_len : *buff_unprocessed); *buff_unprocessed -= buffered_data_len; buffp += buffered_data_len; *pending_data_len -= buffered_data_len; REPL_DPRINT3("gtmrecv_poll_actions : (1) Throwing away message of " "type %d and length %d from old buffer while draining\n", msg_type, buffered_data_len); } if (0 < *buff_unprocessed) { memmove((unsigned char *)gtmrecv_msgp, buffp, *buff_unprocessed); REPL_DPRINT2("gtmrecv_poll_actions : Incomplete header of length %d while draining\n", *buff_unprocessed); } } status = SS_NORMAL; if (0 != *buff_unprocessed || 0 == *pending_data_len) { /* Receive the header of a message */ assert(REPL_MSG_HDRLEN > *buff_unprocessed); /* so we dont pass negative length in REPL_RECV_LOOP */ REPL_RECV_LOOP(gtmrecv_sock_fd, ((unsigned char *)gtmrecv_msgp) + *buff_unprocessed, (REPL_MSG_HDRLEN - *buff_unprocessed), REPL_POLL_WAIT) ; /* Empty Body */ if (SS_NORMAL == status) { assert(remote_side->endianness_known); /* only then is remote_side->cross_endian reliable */ if (!remote_side->cross_endian) { msg_len = gtmrecv_msgp->len; msg_type = gtmrecv_msgp->type; } else { msg_len = GTM_BYTESWAP_32(gtmrecv_msgp->len); msg_type = GTM_BYTESWAP_32(gtmrecv_msgp->type); } msg_type = (msg_type & REPL_TR_CMP_MSG_TYPE_MASK); assert((REPL_TR_CMP_JNL_RECS == msg_type) || (0 == (msg_len % REPL_MSG_ALIGN))); msg_len = ROUND_UP2(msg_len, REPL_MSG_ALIGN); REPL_DPRINT3("gtmrecv_poll_actions : Received message of type %d and length %d while draining\n", msg_type, msg_len); } } if ((SS_NORMAL == status) && (0 != *buff_unprocessed || 0 == *pending_data_len) && (REPL_XOFF_ACK == msg_type)) { /* Receive the rest of the XOFF_ACK msg and signal the drain as complete */ REPL_RECV_LOOP(gtmrecv_sock_fd, gtmrecv_msgp, (MIN_REPL_MSGLEN - REPL_MSG_HDRLEN), REPL_POLL_WAIT) ; /* Empty Body */ if (SS_NORMAL == status) { repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL INFO - XOFF_ACK received. Drained replication pipe completely\n"); upd_shut_too_early_logged = FALSE; xoff_sent = FALSE; return_status = STOP_POLL; } } else if (SS_NORMAL == status) { /* Drain the rest of the message */ if (0 < *pending_data_len) { pending_msg_size = *pending_data_len; REPL_DPRINT2("gtmrecv_poll_actions : (2) Throwing away %d bytes from pipe\n", pending_msg_size); } else { pending_msg_size = msg_len - REPL_MSG_HDRLEN; REPL_DPRINT3("gtmrecv_poll_actions : (2) Throwing away message of " "type %d and length %d from pipe\n", msg_type, msg_len); } for ( ; SS_NORMAL == status && 0 < pending_msg_size; pending_msg_size -= gtmrecv_max_repl_msglen) { temp_len = (pending_msg_size < gtmrecv_max_repl_msglen)? pending_msg_size : gtmrecv_max_repl_msglen; REPL_RECV_LOOP(gtmrecv_sock_fd, gtmrecv_msgp, temp_len, REPL_POLL_WAIT) ; /* Empty Body */ } *buff_unprocessed = 0; *pending_data_len = 0; if (SS_NORMAL == status && info && !upd_shut_too_early_logged) { repl_log(gtmrecv_log_fp, TRUE, TRUE, "ALERT : User initiated shutdown of Update Process done " "when there was data in the replication pipe\n"); upd_shut_too_early_logged = TRUE; } return_status = CONTINUE_POLL; } if (SS_NORMAL != status) { if (EREPL_RECV == repl_errno) { if (REPL_CONN_RESET(status)) { repl_log(gtmrecv_log_fp, TRUE, TRUE, "Connection reset while receiving XOFF_ACK. " "Status = %d ; %s\n", status, STRERROR(status)); repl_close(>mrecv_sock_fd); repl_connection_reset = TRUE; xoff_sent = FALSE; send_badtrans = FALSE; return_status = STOP_POLL; } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Error while draining replication pipe. Error in recv"), status); } else { assert(EREPL_SELECT == repl_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Error while draining replication pipe. Error in select"), status); } } } else return_status = STOP_POLL; /* Like was done before for the XOFF_ACK_ME message, send a BADTRANS/CMP2UNCMP message only if we know * the endianness of the other side. If not, no point in sending one anyways and saves us trouble in * case of cross-endian replication connections. */ if ((STOP_POLL == return_status) && (send_badtrans || send_cmp2uncmp) && (FD_INVALID != gtmrecv_sock_fd) && remote_side->endianness_known) { /* Send REPL_BADTRANS or REPL_CMP2UNCMP message */ if (!remote_side->cross_endian) { bad_trans_msg.type = send_cmp2uncmp ? REPL_CMP2UNCMP : REPL_BADTRANS; bad_trans_msg.len = MIN_REPL_MSGLEN; bad_trans_msg.start_seqno = send_seqno; } else { bad_trans_msg.type = send_cmp2uncmp ? GTM_BYTESWAP_32(REPL_CMP2UNCMP) : GTM_BYTESWAP_32(REPL_BADTRANS); bad_trans_msg.len = GTM_BYTESWAP_32(MIN_REPL_MSGLEN); bad_trans_msg.start_seqno = GTM_BYTESWAP_64(send_seqno); } REPL_SEND_LOOP(gtmrecv_sock_fd, &bad_trans_msg, MIN_REPL_MSGLEN, REPL_POLL_NOWAIT) ; /* Empty Body */ if (SS_NORMAL == status) { if (send_cmp2uncmp) repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL_CMP2UNCMP message sent with seqno %llu\n", send_seqno); else repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL_BADTRANS message sent with seqno %llu\n", send_seqno); } else { if (REPL_CONN_RESET(status) && EREPL_SEND == repl_errno) { if (send_cmp2uncmp) { repl_log(gtmrecv_log_fp, TRUE, TRUE, "Connection reset while sending REPL_CMP2UNCMP. " "Status = %d ; %s\n", status, STRERROR(status)); } else { repl_log(gtmrecv_log_fp, TRUE, TRUE, "Connection reset while sending REPL_BADTRANS. " "Status = %d ; %s\n", status, STRERROR(status)); } repl_close(>mrecv_sock_fd); repl_connection_reset = TRUE; return_status = STOP_POLL; } else if (EREPL_SEND == repl_errno) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Error sending REPL_BADTRANS/REPL_CMP2UNCMP. Error in send"), status); else { assert(EREPL_SELECT == repl_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Error sending REPL_BADTRANS/REPL_CMP2UNCMP. Error in select"), status); } } send_badtrans = FALSE; if (send_cmp2uncmp) { REPL_DPRINT1("gtmrecv_poll_actions : Setting gtmrecv_wait_for_jnl_seqno to TRUE because this receiver" "server requested a fall-back from compressed to uncompressed operation\n"); gtmrecv_wait_for_jnl_seqno = TRUE;/* set this to TRUE to break out and go back to a fresh "do_main_loop" */ gtmrecv_bad_trans_sent = TRUE; gtmrecv_send_cmp2uncmp = FALSE; send_cmp2uncmp = FALSE; } } if ((upd_proc_local->bad_trans && bad_trans_detected) || onln_rlbk_flg_set || (UPDPROC_START == upd_proc_local->start_upd) && (1 != report_cnt)) { if (UPDPROC_START == upd_proc_local->start_upd) { assert(is_updproc_alive() != SRV_ALIVE); upd_proc_local->upd_proc_shutdown = NO_SHUTDOWN; } recvpool_ctl->wrapped = FALSE; recvpool_ctl->write_wrap = recvpool_ctl->recvpool_size; recvpool_ctl->write = 0; /* Reset last_rcvd_histinfo, last_valid_histinfo etc. as they reflect context from unprocessed data * in the receive pool and those are no longer valid because we have drained the receive pool. */ GTMRECV_CLEAR_CACHED_HISTINFO(recvpool.recvpool_ctl, jnlpool, INSERT_STRM_HISTINFO_FALSE); if (UPDPROC_START == upd_proc_local->start_upd) { /* Attempt starting the update process */ for (upd_start_attempts = 0; UPDPROC_START_ERR == (upd_start_status = gtmrecv_upd_proc_init(FALSE)) && GTMRECV_MAX_UPDSTART_ATTEMPTS > upd_start_attempts; upd_start_attempts++) { if (EREPL_UPDSTART_SEMCTL == repl_errno || EREPL_UPDSTART_BADPATH == repl_errno) { gtmrecv_autoshutdown(); } else if (EREPL_UPDSTART_FORK == repl_errno) { /* Couldn't start up update now, can try later */ LONG_SLEEP(GTMRECV_WAIT_FOR_PROC_SLOTS); continue; } else if (EREPL_UPDSTART_EXEC == repl_errno) { /* In forked child, could not exec, should exit */ gtmrecv_exit(ABNORMAL_SHUTDOWN); } } if (UPDPROC_STARTED == (upd_proc_local->start_upd = upd_start_status)) { REPL_DPRINT1("gtmrecv_poll_actions : Setting gtmrecv_wait_for_jnl_seqno to TRUE because of " "upd restart\n"); gtmrecv_wait_for_jnl_seqno = TRUE; report_cnt = next_report_at = 1; if (send_xoff && (FD_INVALID == gtmrecv_sock_fd)) { /* Update start command was issued before connection was established, * no point in sending XOFF. */ send_xoff = FALSE; } } else { repl_log(gtmrecv_log_fp, TRUE, TRUE, "%d failed attempts to fork update process. Try later\n", upd_start_attempts); } } else { gtmrecv_wait_for_jnl_seqno = TRUE;/* set this to TRUE to break out and go back to a fresh "do_main_loop" */ if (onln_rlbk_flg_set) { assert(jnlpool && (NULL != jnlpool->jnlpool_ctl)); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Closing connection due to ONLINE ROLLBACK\n"); repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL INFO - Current Jnlpool Seqno : %llu\n", jnlpool->jnlpool_ctl->jnl_seqno); repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL INFO - Current Receive Pool Seqno : %llu\n", recvpool_ctl->jnl_seqno); repl_close(>mrecv_sock_fd); repl_connection_reset = TRUE; xoff_sent = FALSE; send_badtrans = FALSE; upd_proc_local->onln_rlbk_flg = FALSE; /* Before restarting afresh, sync the online rollback cycles. This way any future grab_lock that * we do after restarting should not realize an unhandled online rollback. For receiver, it is * just syncing the journal pool cycles as the databases are not opened. But, to be safe, grab * the lock and sync the cycles. */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY); SYNC_ONLN_RLBK_CYCLES; rel_lock(jnlpool->jnlpool_dummy_reg); return_status = STOP_POLL; recvpool_ctl->jnl_seqno = 0; } else { REPL_DPRINT1("gtmrecv_poll_actions : Setting gtmrecv_wait_for_jnl_seqno to TRUE because bad trans" "sent\n"); gtmrecv_bad_trans_sent = TRUE; upd_proc_local->bad_trans = FALSE; recvpool_ctl->jnl_seqno = upd_proc_local->read_jnl_seqno; } } } if ((0 == *pending_data_len) && (0 != gtmrecv_local->changelog)) { if (gtmrecv_local->changelog & REPLIC_CHANGE_LOGINTERVAL) { repl_log(gtmrecv_log_fp, TRUE, TRUE, "Changing log interval from %u to %u\n", log_interval, gtmrecv_local->log_interval); log_interval = gtmrecv_local->log_interval; gtmrecv_reinit_logseqno(); /* will force a LOG on the first recv following the interval change */ } if (gtmrecv_local->changelog & REPLIC_CHANGE_LOGFILE) { repl_log(gtmrecv_log_fp, TRUE, TRUE, "Changing log file to %s\n", gtmrecv_local->log_file); repl_log_init(REPL_GENERAL_LOG, >mrecv_log_fd, gtmrecv_local->log_file); repl_log_fd2fp(>mrecv_log_fp, gtmrecv_log_fd); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Change log to %s successful\n",gtmrecv_local->log_file); } /* NOTE: update process and receiver each ignore any setting specific to the other (REPLIC_CHANGE_UPD_LOGINTERVAL, * REPLIC_CHANGE_LOGINTERVAL) */ if (REPLIC_CHANGE_LOGINTERVAL == gtmrecv_local->changelog) upd_proc_local->changelog = 0; else upd_proc_local->changelog = gtmrecv_local->changelog; /* Pass changelog request to the update process */ gtmrecv_local->changelog = 0; } if (0 == *pending_data_len && !gtmrecv_logstats && gtmrecv_local->statslog) { gtmrecv_logstats = TRUE; repl_log(gtmrecv_log_fp, TRUE, TRUE, "Begin statistics logging\n"); } else if (0 == *pending_data_len && gtmrecv_logstats && !gtmrecv_local->statslog) { gtmrecv_logstats = FALSE; /* Force all data out to the file before closing the file */ repl_log(gtmrecv_log_fp, TRUE, TRUE, "End statistics logging\n"); } if ((0 == *pending_data_len) && (gtmrecv_filter & EXTERNAL_FILTER) && ('\0' == gtmrecv_local->filter_cmd[0])) { repl_log(gtmrecv_log_fp, TRUE, TRUE, "Stopping filter\n"); repl_stop_filter(); gtmrecv_filter &= ~EXTERNAL_FILTER; } if (0 == *pending_data_len) { if (upd_helper_ctl->start_helpers) { gtmrecv_helpers_init(upd_helper_ctl->start_n_readers, upd_helper_ctl->start_n_writers); upd_helper_ctl->start_helpers = FALSE; } if (HELPER_REAP_NONE != (status = upd_helper_ctl->reap_helpers) || (double)GTMRECV_REAP_HELPERS_INTERVAL <= difftime(gtmrecv_now, last_reap_time)) { gtmrecv_reap_helpers(HELPER_REAP_WAIT == status); last_reap_time = gtmrecv_now; } } return (return_status); } int gtmrecv_poll_actions(int pending_data_len, int buff_unprocessed, unsigned char *buffp) { while (CONTINUE_POLL == gtmrecv_poll_actions1(&pending_data_len, &buff_unprocessed, buffp)) ; return (SS_NORMAL); } fis-gtm-V7.0-005/sr_unix/gtmrecv_process.c0000644000032200000250000054373414342376330017432 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2022 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. * * * ****************************************************************/ #if defined(__MVS__) && !defined(_ISOC99_SOURCE) #define _ISOC99_SOURCE #endif #include "mdef.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_netdb.h" #include "gtm_time.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "gtm_stdio.h" /* for FILE * in repl_comm.h */ #include #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmrecv.h" #include "repl_comm.h" #include "repl_msg.h" #include "repl_dbg.h" #include "repl_errno.h" #include "io.h" #include "iosp.h" #include "eintr_wrappers.h" #include "jnl.h" #include "repl_sp.h" #include "repl_filter.h" #include "repl_log.h" #include "gtmsource.h" #include "sgtm_putmsg.h" #include "gt_timer.h" #include "min_max.h" #include "error.h" #include "copy.h" #include "repl_instance.h" #include "ftok_sems.h" #include "buddy_list.h" /* needed for muprec.h */ #include "hashtab_mname.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "muprec.h" #include "gtmmsg.h" #include "is_proc_alive.h" #include "jnl_typedef.h" #include "memcoherency.h" #include "have_crit.h" /* needed for ZLIB_UNCOMPRESS */ #include "deferred_signal_handler.h" /* needed for ZLIB_UNCOMPRESS */ #include "gtm_zlib.h" #include "wbox_test_init.h" #ifdef GTM_TRIGGER #include "repl_sort_tr_buff.h" #endif #include "replgbl.h" #include "gtmio.h" #include "repl_inst_dump.h" /* for "repl_dump_histinfo" prototype */ #include "gv_trigger_common.h" #include "anticipatory_freeze.h" #ifdef GTM_TLS #include "gtm_repl.h" #endif #define GTM_ZLIB_UNCMP_ERR_STR "error from zlib uncompress function " #define GTM_ZLIB_Z_MEM_ERROR_STR "Out-of-memory " GTM_ZLIB_UNCMP_ERR_STR #define GTM_ZLIB_Z_BUF_ERROR_STR "Insufficient output buffer " GTM_ZLIB_UNCMP_ERR_STR #define GTM_ZLIB_Z_DATA_ERROR_STR "Input-data-incomplete-or-corrupt " GTM_ZLIB_UNCMP_ERR_STR #define GTM_ZLIB_UNCMPLEN_ERROR_STR "Decompressed message data length %d is not equal to precompressed length %d " #define GTM_ZLIB_UNCMP_ERR_SEQNO_STR "at seqno "INT8_FMT" "INT8_FMTX"\n" #define GTM_ZLIB_UNCMP_ERR_SOLVE_STR "before sending REPL_CMP_SOLVE message\n" #define GTM_ZLIB_UNCMPTRANSITION_STR "Defaulting to NO decompression\n" #define RECVBUFF_REPLMSGLEN_FACTOR 8 #define GTMRECV_WAIT_FOR_STARTJNLSEQNO 100 /* ms */ #define GTMRECV_WAIT_FOR_UPD_PROGRESS 100 /* ms */ #define PROC_RECVOPS_PRINT_MSG_LEN 2048 /* bytes*/ /* By having different high and low watermarks, we can reduce the # of XOFF/XON exchanges */ #define RECVPOOL_HIGH_WATERMARK_PCTG 90 /* Send XOFF when %age of receive pool space occupied goes beyond this */ #define RECVPOOL_LOW_WATERMARK_PCTG 80 /* Send XON when %age of receive pool space occupied falls below this */ #define RECVPOOL_XON_TRIGGER_SIZE (1 * 1024 * 1024) /* Keep the low water mark within this amount of high water mark * so that we don't wait too long to send XON */ #define GTMRECV_XOFF_LOG_CNT 100 #define GTMRECV_HEARTBEAT_PERIOD 10 /* seconds, timer that goes off every this period is the time keeper for * receiver server; used to reduce calls to time related systemc calls */ #define ONLN_RLBK_CMD_MAXLEN 1024 #define MUPIP_DIST_STR "$gtm_dist/mupip " #define ONLN_RLBK_CMD "journal " #define ONLN_RLBK_VERBOSE "-verbose " #define ONLN_RLBK_QUALIFIERS "-online -rollback -backward \"*\" -fetchresync=" /* port# will be filled later */ #if defined(__hpux) && !defined(__hppa) || defined(_AIX) #define KEEPALIVE_PROTO_LEVEL IPPROTO_TCP #define KEEPALIVE_TIME 5 #define KEEPALIVE_INTVL 5 #define KEEPALIVE_PROBES 5 #elif defined(__linux__) #define KEEPALIVE_PROTO_LEVEL SOL_TCP #define KEEPALIVE_TIME 5 #define KEEPALIVE_INTVL 5 #define KEEPALIVE_PROBES 5 #endif GBLDEF repl_msg_ptr_t gtmrecv_msgp; GBLDEF int gtmrecv_max_repl_msglen; GBLDEF int gtmrecv_sock_fd = FD_INVALID; GBLDEF boolean_t repl_connection_reset = TRUE; GBLDEF boolean_t gtmrecv_wait_for_jnl_seqno = FALSE; GBLDEF boolean_t gtmrecv_bad_trans_sent = FALSE; GBLDEF boolean_t gtmrecv_send_cmp2uncmp = FALSE; GBLDEF qw_num repl_recv_data_recvd = 0; GBLDEF qw_num repl_recv_data_processed = 0; GBLDEF qw_num repl_recv_postfltr_data_procd = 0; GBLDEF qw_num repl_recv_lastlog_data_recvd = 0; GBLDEF qw_num repl_recv_lastlog_data_procd = 0; GBLDEF time_t repl_recv_prev_log_time; GBLDEF time_t repl_recv_this_log_time; GBLDEF volatile time_t gtmrecv_now = 0; STATICDEF uchar_ptr_t gtmrecv_cmpmsgp; STATICDEF int gtmrecv_cur_cmpmsglen; STATICDEF int gtmrecv_max_repl_cmpmsglen; STATICDEF uchar_ptr_t gtmrecv_uncmpmsgp; STATICDEF int gtmrecv_max_repl_uncmpmsglen; STATICDEF int gtmrecv_repl_cmpmsglen; STATICDEF int gtmrecv_repl_uncmpmsglen; STATICFNDCL void gtmrecv_repl_send_loop_error(int status, char *msgtypestr); STATICFNDCL int repl_tr_endian_convert(unsigned char remote_jnl_ver, uchar_ptr_t jnl_buff, uint4 jnl_len); STATICFNDCL void do_flow_control(gtm_uint64_t write_pos); STATICFNDCL int gtmrecv_est_conn(void); STATICFNDCL int gtmrecv_start_onln_rlbk(void); STATICFNDCL void prepare_recvpool_for_write(gtm_uint64_t datalen, gtm_uint64_t pre_filter_write_len); STATICFNDCL void copy_to_recvpool(uchar_ptr_t databuff, gtm_uint64_t datalen); STATICFNDCL void wait_for_updproc_to_clear_backlog(void); STATICFNDCL void process_tr_buff(int msg_type); STATICFNDCL void gtmrecv_updresync_histinfo_find_seqno(seq_num input_seqno, int4 strm_num, repl_histinfo *histinfo); STATICFNDCL void gtmrecv_updresync_histinfo_get(int4 index, repl_histinfo *histinfo); STATICFNDCL void gtmrecv_process_need_strminfo_msg(repl_needstrminfo_msg_ptr_t need_strminfo_msg); STATICFNDCL void gtmrecv_process_need_histinfo_msg(repl_needhistinfo_msg_ptr_t need_histinfo_msg, repl_histinfo *histinfo); STATICFNDCL void do_main_loop(boolean_t crash_restart); STATICFNDCL void gtmrecv_heartbeat_timer(TID tid, int4 interval_len, int *interval_ptr); STATICFNDCL void gtmrecv_main_loop(boolean_t crash_restart); #ifdef GTM_TLS STATICFNDCL boolean_t gtmrecv_exchange_tls_info(void); #endif GBLREF gtmrecv_options_t gtmrecv_options; GBLREF int gtmrecv_listen_sock_fd; GBLREF recvpool_addrs recvpool; GBLREF boolean_t gtmrecv_logstats; GBLREF int gtmrecv_filter; GBLREF int gtmrecv_log_fd; GBLREF FILE *gtmrecv_log_fp; GBLREF seq_num seq_num_zero, seq_num_one, seq_num_minus_one; GBLREF unsigned char *repl_filter_buff; GBLREF int repl_filter_bufsiz; GBLREF unsigned int jnl_source_datalen, jnl_dest_maxdatalen; GBLREF unsigned char jnl_source_rectype, jnl_dest_maxrectype; GBLREF int repl_max_send_buffsize, repl_max_recv_buffsize; GBLREF seq_num lastlog_seqno; GBLREF uint4 log_interval; GBLREF qw_num trans_recvd_cnt, last_log_tr_recvd_cnt; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnl_gbls_t jgbl; GBLREF mur_opt_struct mur_options; GBLREF mur_gbls_t murgbl; GBLREF repl_conn_info_t *this_side, *remote_side; GBLREF int4 strm_index; error_def(ERR_INSNOTJOINED); error_def(ERR_INSROLECHANGE); error_def(ERR_INSUNKNOWN); error_def(ERR_JNLNEWREC); error_def(ERR_JNLSETDATA2LONG); error_def(ERR_NOSUPPLSUPPL); error_def(ERR_PRIMARYNOTROOT); error_def(ERR_RCVRMANYSTRMS); error_def(ERR_REPL2OLD); error_def(ERR_REPLCOMM); error_def(ERR_REPLINSTNOHIST); error_def(ERR_REPLINSTREAD); error_def(ERR_REPLNOTLS); error_def(ERR_REPLTRANS2BIG); error_def(ERR_REPLXENDIANFAIL); error_def(ERR_RESUMESTRMNUM); error_def(ERR_REUSEINSTNAME); error_def(ERR_REPLAHEAD); error_def(ERR_STRMNUMIS); error_def(ERR_SUPRCVRNEEDSSUPSRC); error_def(ERR_SYSCALL); error_def(ERR_TEXT); error_def(ERR_TLSCONVSOCK); error_def(ERR_TLSHANDSHAKE); error_def(ERR_UNIMPLOP); error_def(ERR_UPDSYNCINSTFILE); typedef enum { GTM_RECV_POOL, GTM_RECV_CMPBUFF } gtmrecv_buff_t; static unsigned char *buffp, *buff_start, *msgbuff; static gtm_uint64_t buff_unprocessed; static gtm_uint64_t buffered_data_len; static gtm_uint64_t max_recv_bufsiz; static gtm_uint64_t data_len; static gtm_uint64_t exp_data_len; static boolean_t xoff_sent; static repl_msg_t xon_msg, xoff_msg; static int xoff_msg_log_cnt = 0; static long recvpool_high_watermark, recvpool_low_watermark; static gtm_uint64_t write_loc, write_wrap; static gtm_uint64_t write_off; static double time_elapsed; static gtm_uint64_t recvpool_size; static int heartbeat_period; #ifdef REPL_CMP_SOLVE_TESTING static boolean_t repl_cmp_solve_timer_set; #endif #define ISSUE_REPLCOMM_ERROR(REASON, SAVE_ERRNO) \ { \ SEND_SYSMSG_REPLCOMM(LEN_AND_LIT(REASON)); \ if (0 != SAVE_ERRNO) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT(REASON), SAVE_ERRNO);\ else \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT(REASON)); \ } #define GTMRECV_EXPAND_CMPBUFF_IF_NEEDED(cmpmsglen) \ { \ int lclcmpmsglen; \ \ lclcmpmsglen = ROUND_UP2(cmpmsglen, REPL_MSG_ALIGN); \ if (lclcmpmsglen > gtmrecv_max_repl_cmpmsglen) \ { \ if (NULL != gtmrecv_cmpmsgp) \ free(gtmrecv_cmpmsgp); \ gtmrecv_cmpmsgp = (uchar_ptr_t)malloc(lclcmpmsglen); \ gtmrecv_max_repl_cmpmsglen = (lclcmpmsglen); \ } \ assert(0 == (gtmrecv_max_repl_cmpmsglen % REPL_MSG_ALIGN)); \ } #define GTMRECV_EXPAND_UNCMPBUFF_IF_NEEDED(uncmpmsglen) \ { \ if (uncmpmsglen > gtmrecv_max_repl_uncmpmsglen) \ { \ if (NULL != gtmrecv_uncmpmsgp) \ free(gtmrecv_uncmpmsgp); \ gtmrecv_uncmpmsgp = (uchar_ptr_t)malloc(uncmpmsglen); \ gtmrecv_max_repl_uncmpmsglen = (uncmpmsglen); \ } \ assert(0 == (gtmrecv_max_repl_uncmpmsglen % REPL_MSG_ALIGN)); \ } /* Set an alternate buffer as the target to hold the incoming compressed journal records. * This will then be uncompressed into yet another buffer from where the records will be * transferred to the receive pool one transaction at a time. */ #define GTMRECV_SET_BUFF_TARGET_CMPBUFF(cmplen, uncmplen, curcmplen) \ { \ GTMRECV_EXPAND_CMPBUFF_IF_NEEDED(cmplen); \ GTMRECV_EXPAND_UNCMPBUFF_IF_NEEDED(uncmplen); \ curcmplen = 0; \ } /* Wrap the "prepare_recvpool_for_write", "copy_to_recvpool", "do_flow_control" and "gtmrecv_poll_actions" functions in macros. * This is needed because every invocation of these functions (except for a few gtmrecv_poll_actions invocations) should check * a few global variables to determine if there was an error and in that case return from the caller. * All callers of these functions should use the macro and not invoke the function directly. * This avoids duplication of the error check logic. */ #define PREPARE_RECVPOOL_FOR_WRITE(curdatalen, prefilterdatalen) \ { \ prepare_recvpool_for_write(curdatalen, prefilterdatalen); \ /* could update "recvpool_ctl->write" and "write_loc" */ \ if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) \ return; \ } #define COPY_TO_RECVPOOL(ptr, datalen) \ { \ copy_to_recvpool(ptr, datalen); /* uses and updates "write_loc" */ \ if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) \ return; \ } #if defined(GTM_REPLIC_FLOW_CONTROL_ENABLED) #define DO_FLOW_CONTROL(write_loc) \ { \ do_flow_control(write_loc); \ if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) \ return; \ } #else #define DO_FLOW_CONTROL(write_loc) /* nothing */ #endif /* Wrapper for gtmrecv_poll_actions to handle connection reset. The arguments passed in are typically either the static variables * `data_len', `buff_unprocessed' and `buffp'. But, these values are relevant only when this is being called from do_main_loop as * they indicate how much of the received buffer is still to be processed and the pointer to the beginning of the unprocessed * buffer. Outside do_main_loop (for instance, in gtmrecv_est_conn), gtmrecv_poll_actions is invoked with 0,0, NULL respectively. */ #define GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp) \ { \ gtmrecv_poll_actions(data_len, buff_unprocessed, buffp); \ if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) \ return; \ } #define WACKY_MESSAGE(MSG, SOCK_FD, TYPE) \ MBSTART { /* If the header is wacky, assume it's a rogue transmission and reset the connection */ \ repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received UNKNOWN message (type = %d / %d). " \ "Discarding it and resetting connection.\n", MSG & REPL_TR_CMP_MSG_TYPE_MASK, TYPE); \ repl_connection_reset = TRUE; \ repl_close(SOCK_FD); \ return; \ } MBEND /* The below macro is used (within this module) to check for errors (and issue appropriate message) after REPL_SEND_LOOP * returns. * NOTE: This macro, in its current form, cannot be used in all REPL_SEND_LOOP usages due to specific post-error processing * done in a few places. There is scope to: * (a) Improve the macro to adjust to post-error processing * (b) Provide similar macro to be used for REPL_RECV_LOOP usages */ #define CHECK_REPL_SEND_LOOP_ERROR(status, msgstr) \ { \ if (SS_NORMAL != status) \ { \ gtmrecv_repl_send_loop_error(status, msgstr); \ if (repl_connection_reset) \ return; \ } \ } #define GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED \ { \ sgmnt_addrs *repl_csa; \ \ repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; \ assert(repl_csa->now_crit); \ assert(NULL != jnlpool->jnlpool_ctl); \ if (repl_csa->onln_rlbk_cycle != jnlpool->jnlpool_ctl->onln_rlbk_cycle) \ { \ SYNC_ONLN_RLBK_CYCLES; \ rel_lock(jnlpool->jnlpool_dummy_reg); \ gtmrecv_onln_rlbk_clnup(); \ if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) \ return; \ } \ } /* For cross-endian conversion to happen on the receiving side, the receiver must understand the layout of the journal * records. To keep the endian conversion logic on both primary and secondary simple, the following scheme is used: * (a) If primary < secondary, endian conversion will happen on primary. * (b) If primary >= secondary, primary will apply internal filters to convert the records to secondary's format. The * secondary on receiving them will do the necessary endian conversion before letting the update process see them. * * However, the above logic will cause the older versions (< V5.4-002) to NOT replicate to V5.4-002 as the endian-conversion * by-primary is introduced only from V5.4-002 and above. Hence, allow secondary to do endian conversion for this special * case when the primary is a GT.M version running V5.3-003 (V18_JNL_VER) to V5.4-001 (V20_JNL_VER). The lower limit is * chosen to be V5.3-003 since that was the first version where cross-endian conversion was supported. * * There is one other exception. V5.5 source server (V22_JNL_VER) had a bug wherein a history record is endian-converted * when the replication is NOT cross-endian and vice versa. In either case, do an endian conversion of the history record. * * The below macro takes all the above conditions into consideration to determine if the receiver server needs to do endian * converison or not. */ #define ENDIAN_CONVERSION_NEEDED(IS_NEW_HISTREC, THIS_JNL_VER, REMOTE_JNL_VER, X_ENDIAN) \ ((IS_NEW_HISTREC && (V22_JNL_VER == REMOTE_JNL_VER)) \ || (X_ENDIAN && ((REMOTE_JNL_VER >= THIS_JNL_VER) || (V21_JNL_VER > REMOTE_JNL_VER)))) STATICFNDEF void gtmrecv_repl_send_loop_error(int status, char *msgtypestr) { char print_msg[1024]; assert((EREPL_SEND == repl_errno) || (EREPL_SELECT == repl_errno)); if (REPL_CONN_RESET(status) && EREPL_SEND == repl_errno) { SNPRINTF(print_msg, SIZEOF(print_msg), "Connection got reset while sending %s message. Status = %d ; %s\n", msgtypestr, status, STRERROR(status)); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) MAKE_MSG_INFO(ERR_REPLCOMM), 0, ERR_TEXT, 2, LEN_AND_STR(print_msg)); repl_connection_reset = TRUE; repl_close(>mrecv_sock_fd); SNPRINTF(print_msg, SIZEOF(print_msg), "Closing connection on receiver side\n"); repl_log(gtmrecv_log_fp, TRUE, TRUE, print_msg); return; } else if (EREPL_SEND == repl_errno) { SNPRINTF(print_msg, SIZEOF(print_msg), "Error sending %s message. Error in send : %s", msgtypestr, STRERROR(status)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_STR(print_msg)); } else if (EREPL_SELECT == repl_errno) { SNPRINTF(print_msg, SIZEOF(print_msg), "Error sending %s message. Error in select : %s", msgtypestr, STRERROR(status)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_STR(print_msg)); } } /* convert endianness of transaction */ STATICFNDEF int repl_tr_endian_convert(unsigned char remote_jnl_ver, uchar_ptr_t jnl_buff, uint4 jnl_len) { unsigned char *jb, *jstart, *ptr; enum jnl_record_type rectype; int status, reclen; uint4 jlen; jrec_prefix *prefix; jnl_record *rec; jnl_string *keystr; mstr_len_t *vallen_ptr; mstr_len_t temp_val; repl_old_triple_jnl_ptr_t oldtriple; repl_histinfo *histinfo; jnl_str_len_t nodeflags_keylen; uint4 update_num, num_participants_4bytes; unsigned short num_participants_2bytes; # ifdef DEBUG DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # endif assert((remote_jnl_ver >= this_side->jnl_ver) || (V21_JNL_VER > remote_jnl_ver) || (V22_JNL_VER == remote_jnl_ver)); jb = jnl_buff; status = SS_NORMAL; jlen = jnl_len; assert(0 == ((UINTPTR_T)jb % SIZEOF(UINTPTR_T))); while (JREC_PREFIX_SIZE <= jlen) { assert(0 == ((UINTPTR_T)jb % SIZEOF(UINTPTR_T))); rec = (jnl_record *) jb; rectype = (enum jnl_record_type)rec->prefix.jrec_type; reclen = rec->prefix.forwptr = GTM_BYTESWAP_24(rec->prefix.forwptr); if (!IS_REPLICATED(rectype) || (0 == reclen) || (reclen > jlen)) { /* Bad OR Incomplete record */ assert(FALSE); status = -1; break; } assert(!IS_ZTP(rectype)); assert((JRT_HISTREC == rectype) || (JRT_TRIPLE == rectype) || IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(rectype) || (JRT_TCOM == rectype) || (JRT_NULL == rectype)); DEBUG_ONLY(jstart = jb;) if (JRT_HISTREC == rectype) { histinfo = &(((repl_histrec_jnl_ptr_t)rec)->histcontent); ENDIAN_CONVERT_REPL_HISTINFO(histinfo); } else if (JRT_TRIPLE == rectype) { oldtriple = (repl_old_triple_jnl_ptr_t)rec; oldtriple->cycle = GTM_BYTESWAP_32(oldtriple->cycle); oldtriple->start_seqno = GTM_BYTESWAP_64(oldtriple->start_seqno); } else { /* pini_addr, time, checksum and tn field of the journal records created by the source server are * irrelevant to the receiver server and hence no point doing the endian conversion for them. */ ((jrec_suffix *)((unsigned char *)rec + reclen - JREC_SUFFIX_SIZE))->backptr = reclen; rec->jrec_null.jnl_seqno = GTM_BYTESWAP_64(rec->jrec_null.jnl_seqno); /* Starting jnl ver V22, we have a "strm_seqno" field in the journal record so endian convert that */ if (V22_JNL_VER <= remote_jnl_ver) { /* At this point, we could have a TCOM or NULL or SET/KILL/ZKILL/ZTRIG type of record. * Assert that all of them have "strm_seqno" at the exact same offset so we can avoid * an if/then/else check on the record types in order to endian convert "strm_seqno". */ assert(&rec->jrec_null.strm_seqno == &rec->jrec_set_kill.strm_seqno); assert(&rec->jrec_null.strm_seqno == &rec->jrec_tcom.strm_seqno); rec->jrec_null.strm_seqno = GTM_BYTESWAP_64(rec->jrec_null.strm_seqno); } if (IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(rectype)) { /* This code will need changes in case the jnl-ver changes from V28 to V29 so add an assert to * alert to that possibility. Once the code is fixed for the new jnl format, change the assert * to reflect the new latest jnl-ver. */ assert(JNL_VER_THIS == V28_JNL_VER); /* To better understand the logic below (particularly the use of hardcoded offsets), see comment * in repl_filter.c (search for "struct_jrec_upd layout" for the various jnl versions we support). */ if (V22_JNL_VER <= remote_jnl_ver) { /* byte-swap update_num */ assert(&rec->jrec_set_kill.update_num == &rec->jrec_ztworm.update_num); assert(&rec->jrec_set_kill.update_num == &rec->jrec_lgtrig.update_num); rec->jrec_set_kill.update_num = GTM_BYTESWAP_32(rec->jrec_set_kill.update_num); /* No need to byte-swap num_participants as it is not used by the update process */ /* Get pointer to mumps_node */ keystr = (jnl_string *)&rec->jrec_set_kill.mumps_node; assert(keystr == (jnl_string *)&rec->jrec_ztworm.ztworm_str); assert(keystr == (jnl_string *)&rec->jrec_lgtrig.lgtrig_str); } else if (V19_JNL_VER <= remote_jnl_ver) { /* byte-swap update_num */ ptr = (unsigned char *)rec + 32; /* is offset of update_num in V19 struct_jrec_upd */ update_num = *(uint4 *)ptr; *(uint4 *)ptr = GTM_BYTESWAP_32(update_num); /* No need to byte-swap num_participants as it is not used by the update process */ /* Get pointer to mumps_node */ keystr = (jnl_string *)((unsigned char *)rec + 40); /* is offset of mumps_node */ } else { assert(V17_JNL_VER <= remote_jnl_ver); /* Note: In V17, there is no update_num or num_participants like V19 so no endian convert */ /* Get pointer to mumps_node */ keystr = (jnl_string *)((unsigned char *)rec + 32); /* is offset of mumps_node */ } /* In V18, the jnl_string contained a 32 bit length field followed by mumps_node * In V19, the "length" field is divided into 8 bit "nodeflags" and 24 bit "length" fields. * Byteswap the entire 32 bit value */ nodeflags_keylen = *(jnl_str_len_t *)keystr; *(jnl_str_len_t *)keystr = GTM_BYTESWAP_32(nodeflags_keylen); if (IS_SET(rectype)) { assert(!IS_ZTWORM(rectype)); assert(!IS_LGTRIG(rectype)); /* SET records have a 'value' part which needs to be endian converted */ vallen_ptr = (mstr_len_t *)&keystr->text[keystr->length]; GET_MSTR_LEN(temp_val, vallen_ptr); temp_val = GTM_BYTESWAP_32(temp_val); PUT_MSTR_LEN(vallen_ptr, temp_val); /* The actual 'value' itself is a character array and hence needs no endian conversion */ } } else if (JRT_TCOM == rectype) { /* byte-swap num_participants as this is relied upon by "repl_sort_tr_buff". * The offset and size of this field are different for older versions. Endian convert accordingly. * V22 struct_jrec_tcom layout is as follows. * offset = 0042 [0x002a] size = 0002 [0x0002] ----> num_participants * V19/V20/V21 struct_jrec_tcom layout is as follows. * offset = 0034 [0x0022] size = 0002 [0x0002] ----> num_participants * V17/V18 struct_jrec_tcom layout is as follows. * offset = 0040 [0x0028] size = 0004 [0x0004] ----> participants */ if (V22_JNL_VER <= remote_jnl_ver) { assert(42 == ((INTPTR_T)&rec->jrec_tcom.num_participants - (INTPTR_T)rec)); assert(SIZEOF(num_participants_2bytes) == SIZEOF(rec->jrec_tcom.num_participants)); num_participants_2bytes = rec->jrec_tcom.num_participants; rec->jrec_tcom.num_participants = GTM_BYTESWAP_16(num_participants_2bytes); } else if (V19_JNL_VER <= remote_jnl_ver) { ptr = (unsigned char *)rec + 34; /* is offset of update_num in V19 struct_jrec_upd */ assert(SIZEOF(num_participants_2bytes) == SIZEOF(unsigned short)); num_participants_2bytes = *(unsigned short *)ptr; rec->jrec_tcom.num_participants = GTM_BYTESWAP_16(num_participants_2bytes); } else { assert(V17_JNL_VER <= remote_jnl_ver); ptr = (unsigned char *)rec + 40; /* is offset of update_num in V19 struct_jrec_upd */ assert(SIZEOF(num_participants_4bytes) == SIZEOF(uint4)); num_participants_4bytes = *(uint4 *)ptr; rec->jrec_tcom.num_participants = GTM_BYTESWAP_32(num_participants_4bytes); } assert(rec->jrec_tcom.num_participants); /* token_seq.jnl_seqno & strm_seqno have already been endian converted. */ } /* else if (JRT_NULL == rectype) * token_seq.jnl_seqno & strm_seqno have already been endian converted. */ assert(reclen == REC_LEN_FROM_SUFFIX(jb, reclen)); } jb = jb + reclen; assert(jb == jstart + reclen); jlen -= reclen; } if ((-1 != status) && (0 != jlen)) { /* Incomplete record */ assert(FALSE); status = -1; } return status; } STATICFNDEF void do_flow_control(gtm_uint64_t write_pos) { /* Check for overflow before writing */ recvpool_ctl_ptr_t recvpool_ctl; upd_proc_local_ptr_t upd_proc_local; gtmrecv_local_ptr_t gtmrecv_local; long space_used; unsigned char *msg_ptr; /* needed for REPL_{SEND,RECV}_LOOP */ int tosend_len, sent_len, sent_this_iter; /* needed for REPL_SEND_LOOP */ int torecv_len, recvd_len, recvd_this_iter; /* needed for REPL_RECV_LOOP */ int status, poll_dir; /* needed for REPL_{SEND,RECV}_LOOP */ gtm_uint64_t read_pos; seq_num temp_seq_num; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; recvpool_ctl = recvpool.recvpool_ctl; upd_proc_local = recvpool.upd_proc_local; gtmrecv_local = recvpool.gtmrecv_local; space_used = 0; if (recvpool_ctl->wrapped) space_used = write_pos + recvpool_size - (read_pos = upd_proc_local->read); if (!recvpool_ctl->wrapped || space_used > recvpool_size) space_used = write_pos - (read_pos = upd_proc_local->read); assert(remote_side->endianness_known); /* only then is remote_side->cross_endian reliable */ if (space_used >= recvpool_high_watermark && !xoff_sent) { /* Send XOFF message */ if (!remote_side->cross_endian) { xoff_msg.type = REPL_XOFF; memcpy((uchar_ptr_t)&xoff_msg.msg[0], (uchar_ptr_t)&upd_proc_local->read_jnl_seqno, SIZEOF(seq_num)); xoff_msg.len = MIN_REPL_MSGLEN; } else { xoff_msg.type = GTM_BYTESWAP_32(REPL_XOFF); temp_seq_num = GTM_BYTESWAP_64(upd_proc_local->read_jnl_seqno); memcpy((uchar_ptr_t)&xoff_msg.msg[0], (uchar_ptr_t)&temp_seq_num, SIZEOF(seq_num)); xoff_msg.len = GTM_BYTESWAP_32(MIN_REPL_MSGLEN); } REPL_SEND_LOOP(gtmrecv_sock_fd, &xoff_msg, MIN_REPL_MSGLEN, REPL_POLL_NOWAIT) { GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp); } CHECK_REPL_SEND_LOOP_ERROR(status, "REPL_XOFF"); if (gtmrecv_logstats) repl_log(gtmrecv_log_fp, TRUE, TRUE, "Space used = %ld, High water mark = %ld Low water mark = %ld, " "Updproc Read = %ld, Recv Write = %ld, Sent XOFF\n", space_used, recvpool_high_watermark, recvpool_low_watermark, read_pos, write_pos); repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL_XOFF sent as receive pool has %ld bytes transaction data yet to be " "processed\n", space_used); xoff_sent = TRUE; xoff_msg_log_cnt = 1; } else if (space_used < recvpool_low_watermark && xoff_sent) { if (!remote_side->cross_endian) { xon_msg.type = REPL_XON; memcpy((uchar_ptr_t)&xon_msg.msg[0], (uchar_ptr_t)&upd_proc_local->read_jnl_seqno, SIZEOF(seq_num)); xon_msg.len = MIN_REPL_MSGLEN; } else { xon_msg.type = GTM_BYTESWAP_32(REPL_XON); temp_seq_num = GTM_BYTESWAP_64(upd_proc_local->read_jnl_seqno); memcpy((uchar_ptr_t)&xon_msg.msg[0], (uchar_ptr_t)&temp_seq_num, SIZEOF(seq_num)); xon_msg.len = GTM_BYTESWAP_32(MIN_REPL_MSGLEN); } REPL_SEND_LOOP(gtmrecv_sock_fd, &xon_msg, MIN_REPL_MSGLEN, REPL_POLL_NOWAIT) { GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp); } CHECK_REPL_SEND_LOOP_ERROR(status, "REPL_XON"); if (gtmrecv_logstats) repl_log(gtmrecv_log_fp, TRUE, TRUE, "Space used now = %ld, High water mark = %ld, " "Low water mark = %ld, Updproc Read = %ld, Recv Write = %ld, Sent XON\n", space_used, recvpool_high_watermark, recvpool_low_watermark, read_pos, write_pos); repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL_XON sent as receive pool has %ld bytes free space to buffer transaction " "data\n", recvpool_size - space_used); xoff_sent = FALSE; xoff_msg_log_cnt = 0; } return; } STATICFNDEF int gtmrecv_est_conn(void) { recvpool_ctl_ptr_t recvpool_ctl; upd_proc_local_ptr_t upd_proc_local; gtmrecv_local_ptr_t gtmrecv_local; boolean_t keepalive; GTM_SOCKLEN_TYPE optlen; int status, keepalive_opt, optval, save_errno; int send_buffsize, recv_buffsize, tcp_r_bufsize; struct linger disable_linger = {0, 0}; char print_msg[1024]; struct addrinfo primary_ai; struct sockaddr_storage primary_sas; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Wait for a connection from a Source Server. The Receiver Server is an iterative server. */ recvpool_ctl = recvpool.recvpool_ctl; upd_proc_local = recvpool.upd_proc_local; gtmrecv_local = recvpool.gtmrecv_local; /* Create a listen socket */ gtmrecv_comm_init((in_port_t)gtmrecv_local->listen_port); primary_ai.ai_addr = (sockaddr_ptr)&primary_sas; primary_ai.ai_addrlen = SIZEOF(primary_sas); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Waiting for a connection...\n"); /* Null initialize fields that need to be initialized only after connecting to the primary. * It is ok not to hold a lock on the journal pool while updating jnlpool_ctl fields since this will be the only * process updating those fields. */ assert(remote_side == >mrecv_local->remote_side); remote_side->proto_ver = REPL_PROTO_VER_UNINITIALIZED; jnlpool->jnlpool_ctl->primary_instname[0] = '\0'; while (TRUE) { while (TRUE) { if (0 < (status = fd_ioready(gtmrecv_listen_sock_fd, REPL_POLLIN, REPL_POLL_WAIT))) break; if (-1 == status) { save_errno = errno; assert((EAGAIN != save_errno) && (EINTR != save_errno)); ISSUE_REPLCOMM_ERROR("Error in select on listen socket", save_errno); } else if (0 == status) /* timeout */ gtmrecv_poll_actions(0, 0, NULL); } ACCEPT_SOCKET(gtmrecv_listen_sock_fd, primary_ai.ai_addr, (GTM_SOCKLEN_TYPE *)&primary_ai.ai_addrlen, gtmrecv_sock_fd); if (FD_INVALID == gtmrecv_sock_fd) { save_errno = errno; # ifdef __hpux if (ENOBUFS == save_errno) { gtmrecv_poll_actions(0, 0, NULL); continue; } # endif ISSUE_REPLCOMM_ERROR("Error accepting connection from Source Server", save_errno); } break; } /* Connection established */ repl_close(>mrecv_listen_sock_fd); /* Close the listener socket */ repl_connection_reset = FALSE; if (-1 == setsockopt(gtmrecv_sock_fd, SOL_SOCKET, SO_LINGER, (const void *)&disable_linger, SIZEOF(disable_linger))) ISSUE_REPLCOMM_ERROR("Error with receiver server socket disable linger", errno); # ifdef REPL_DISABLE_KEEPALIVE keepalive = FALSE; # else keepalive = TRUE; # endif if (-1 == setsockopt(gtmrecv_sock_fd, SOL_SOCKET, SO_KEEPALIVE, (const void *)&keepalive, SIZEOF(keepalive))) { ISSUE_REPLCOMM_ERROR("Error with receiver server socket enable keepalive", errno); } /* Set up the keepalive parameters * TCP_KEEPCNT : overrides tcp_keepalive_probes * TCP_KEEPIDLE: overrides tcp_keepalive_time * TCP_KEEPINTVL: overrides tcp_keepalive_intvl */ # if defined(KEEPALIVE_PROTO_LEVEL) keepalive_opt = KEEPALIVE_PROBES; if (-1 == setsockopt(gtmrecv_sock_fd, KEEPALIVE_PROTO_LEVEL, TCP_KEEPCNT, (void*)&keepalive_opt, SIZEOF(keepalive_opt))) ISSUE_REPLCOMM_ERROR("Error with receiver server socket setting tcp_keepalive_probes", errno); keepalive_opt = KEEPALIVE_TIME; if (-1 == setsockopt(gtmrecv_sock_fd, KEEPALIVE_PROTO_LEVEL, TCP_KEEPIDLE, (void*)&keepalive_opt, SIZEOF(keepalive_opt))) ISSUE_REPLCOMM_ERROR("Error with receiver server socket setting tcp_keepalive_time", errno); keepalive_opt = KEEPALIVE_INTVL; if (-1 == setsockopt(gtmrecv_sock_fd, KEEPALIVE_PROTO_LEVEL, TCP_KEEPINTVL, (void*)&keepalive_opt, SIZEOF(keepalive_opt))) ISSUE_REPLCOMM_ERROR("Error with receiver server socket setting tcp_keepalive_intvl", errno); # endif optlen = SIZEOF(optval); if ( -1 == getsockopt(gtmrecv_sock_fd, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen)) ISSUE_REPLCOMM_ERROR("Error with receiver server socket checking keepalive enabled or not", errno) # if !defined(KEEPALIVE_PROTO_LEVEL) repl_log(gtmrecv_log_fp, TRUE, TRUE, "SO_KEEPALIVE is %s\n", (optval ? "ON" : "OFF")); # else repl_log(gtmrecv_log_fp, TRUE, TRUE, "SO_KEEPALIVE is %s. ", (optval ? "ON" : "OFF")); if (-1 == getsockopt(gtmrecv_sock_fd, KEEPALIVE_PROTO_LEVEL, TCP_KEEPCNT, &optval, &optlen)) ISSUE_REPLCOMM_ERROR("Error with receiver server socket getting tcp_keepalive_probes", errno); if (optval) repl_log(gtmrecv_log_fp, FALSE, TRUE, "TCP_KEEPCNT is %d, ", optval); if (-1 == getsockopt(gtmrecv_sock_fd, KEEPALIVE_PROTO_LEVEL, TCP_KEEPIDLE, &optval, &optlen)) ISSUE_REPLCOMM_ERROR("Error with receiver server socket getting tcp_keepalive_time", errno); if (optval) repl_log(gtmrecv_log_fp, FALSE, TRUE, "TCP_KEEPIDLE is %d, ", optval); if (-1 == getsockopt(gtmrecv_sock_fd, KEEPALIVE_PROTO_LEVEL, TCP_KEEPINTVL, &optval, &optlen)) ISSUE_REPLCOMM_ERROR("Error with receiver server socket getting tcp_keepalive_intvl", errno); if (optval) repl_log(gtmrecv_log_fp, FALSE, TRUE, "TCP_KEEPINTVL is %d.\n", optval); # endif if (0 != (status = get_send_sock_buff_size(gtmrecv_sock_fd, &send_buffsize))) ISSUE_REPLCOMM_ERROR("Error getting socket send buffsize", errno); if (send_buffsize < GTMRECV_TCP_SEND_BUFSIZE) { if (0 != (status = set_send_sock_buff_size(gtmrecv_sock_fd, GTMRECV_TCP_SEND_BUFSIZE))) { if (send_buffsize < GTMRECV_MIN_TCP_SEND_BUFSIZE) { SNPRINTF(print_msg, SIZEOF(print_msg), "Could not set TCP send buffer size to %d : %s", GTMRECV_MIN_TCP_SEND_BUFSIZE, STRERROR(status)); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) MAKE_MSG_INFO(ERR_REPLCOMM), 0, ERR_TEXT, 2, LEN_AND_STR(print_msg)); } } } if (0 != (status = get_send_sock_buff_size(gtmrecv_sock_fd, &repl_max_send_buffsize))) /* may have changed */ ISSUE_REPLCOMM_ERROR("Error getting socket send buffsize", errno); if (0 != (status = get_recv_sock_buff_size(gtmrecv_sock_fd, &recv_buffsize))) ISSUE_REPLCOMM_ERROR("Error getting socket recv buffsize", errno); if (recv_buffsize < GTMRECV_TCP_RECV_BUFSIZE) { for (tcp_r_bufsize = GTMRECV_TCP_RECV_BUFSIZE; tcp_r_bufsize >= MAX(recv_buffsize, GTMRECV_MIN_TCP_RECV_BUFSIZE) && 0 != (status = set_recv_sock_buff_size(gtmrecv_sock_fd, tcp_r_bufsize)); tcp_r_bufsize -= GTMRECV_TCP_RECV_BUFSIZE_INCR) ; if (tcp_r_bufsize < GTMRECV_MIN_TCP_RECV_BUFSIZE) { SNPRINTF(print_msg, SIZEOF(print_msg), "Could not set TCP receive buffer size in range [%d, %d], last " "known error : %s", GTMRECV_MIN_TCP_RECV_BUFSIZE, GTMRECV_TCP_RECV_BUFSIZE, STRERROR(status)); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) MAKE_MSG_INFO(ERR_REPLCOMM), 0, ERR_TEXT, 2, LEN_AND_STR(print_msg)); } } if (0 != (status = get_recv_sock_buff_size(gtmrecv_sock_fd, &repl_max_recv_buffsize))) /* may have changed */ ISSUE_REPLCOMM_ERROR("Error getting socket recv buffsize", errno); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Connection established, using TCP send buffer size %d receive buffer size %d\n", repl_max_send_buffsize, repl_max_recv_buffsize); repl_log_conn_info(gtmrecv_sock_fd, gtmrecv_log_fp, FALSE); /* re-determine endianness of other side */ remote_side->endianness_known = FALSE; /* re-determine journal format of other side */ remote_side->jnl_ver = 0; /* re-determine compression level on the replication pipe after every connection establishment */ repl_zlib_cmp_level = ZLIB_CMPLVL_NONE; /* Reset prior connection related state variables (see ) */ xoff_sent = FALSE; xoff_msg_log_cnt = 0; # ifdef GTM_TLS assert(!repl_tls.enabled); /* We either did not create a TLS/SSL aware socket or the SSL object off of the TLS/SSL aware socket should be NULL since * we haven't yet connected to the source server. Assert that. */ assert((NULL == repl_tls.sock) || (NULL == repl_tls.sock->ssl)); # endif /* Note that even though we are reopening a fresh connection, we should NOT reset the cached information * last_rcvd_histinfo, last_valid_histinfo etc. in this case as we might just resume processing from where * the previous connection left off in which case all the cached information is still valid. If we don't * resume from where we left off, the receiver will anyways error out asking for a rollback to be done so * the cached information never gets used if it is invalid. */ return (SS_NORMAL); } int gtmrecv_alloc_filter_buff(int bufsiz) { unsigned char *old_filter_buff; bufsiz = ROUND_UP2(bufsiz, OS_PAGE_SIZE); if ((NO_FILTER != gtmrecv_filter) && (repl_filter_bufsiz < bufsiz)) { REPL_DPRINT3("Expanding filter buff from %d to %d\n", repl_filter_bufsiz, bufsiz); old_filter_buff = repl_filter_buff; repl_filter_buff = malloc(bufsiz); if (NULL != old_filter_buff) { memcpy(repl_filter_buff, old_filter_buff, repl_filter_bufsiz); free(repl_filter_buff); } repl_filter_bufsiz = bufsiz; } return (SS_NORMAL); } void gtmrecv_free_filter_buff(void) { if (NULL != repl_filter_buff) { free(repl_filter_buff); repl_filter_buff = NULL; repl_filter_bufsiz = 0; } } int gtmrecv_alloc_msgbuff(void) { gtmrecv_max_repl_msglen = MAX_REPL_MSGLEN; assert(NULL == gtmrecv_msgp); /* first time initialization. The receiver server doesn't need to re-allocate */ msgbuff = (unsigned char *)malloc(gtmrecv_max_repl_msglen + OS_PAGE_SIZE); gtmrecv_msgp = (repl_msg_ptr_t)ROUND_UP2((unsigned long)msgbuff, OS_PAGE_SIZE); gtmrecv_alloc_filter_buff(gtmrecv_max_repl_msglen); return (SS_NORMAL); } void gtmrecv_free_msgbuff(void) { if (NULL != msgbuff) { assert(NULL != gtmrecv_msgp); free(msgbuff); msgbuff = NULL; gtmrecv_msgp = NULL; } } /* This function can be used to only send fixed-size message types across the replication pipe. * This in turn uses REPL_SEND* macros but also does error checks and sets the global variables * "repl_connection_reset" or "gtmrecv_wait_for_jnl_seqno" accordingly. * It also does the endian conversion of the 'type' and 'len' fields of the repl_msg_t structure being sent. * * msg = Pointer to the message buffer to send * type = One of the various message types listed in repl_msg.h * len = Length of the message to be sent * msgtypestr = Message name as a string to display meaningful error messages * optional_seqno = Optional seqno that needs to be printed along with the message name */ void gtmrecv_repl_send(repl_msg_ptr_t msgp, int4 type, int4 len, char *msgtypestr, seq_num optional_seqno) { unsigned char *msg_ptr; /* needed for REPL_SEND_LOOP */ int tosend_len, sent_len, sent_this_iter; /* needed for REPL_SEND_LOOP */ int status, poll_dir; /* needed for REPL_{SEND,RECV}_LOOP */ FILE *log_fp; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); /* ROLLBACK -FORWARD should not call this function */ assert(!mur_options.rollback || (NULL == recvpool.gtmrecv_local)); assert(mur_options.rollback || (NULL != recvpool.gtmrecv_local)); assert((REPL_MULTISITE_MSG_START > type) || (REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver)); log_fp = (NULL == gtmrecv_log_fp) ? stdout : gtmrecv_log_fp; if (MAX_SEQNO != optional_seqno) { repl_log(log_fp, TRUE, TRUE, "Sending %s message with seqno "INT8_FMT" "INT8_FMTX"\n", msgtypestr, optional_seqno, optional_seqno); } else repl_log(log_fp, TRUE, TRUE, "Sending %s message\n", msgtypestr); /* Assert that if we don't know the endianness of the remote side, we assume it is the same endianness */ assert(remote_side->endianness_known || !remote_side->cross_endian); if (!remote_side->cross_endian) { msgp->type = type; msgp->len = len; } else { msgp->type = GTM_BYTESWAP_32(type); msgp->len = GTM_BYTESWAP_32(len); } REPL_SEND_LOOP(gtmrecv_sock_fd, msgp, len, REPL_POLL_NOWAIT) { GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp); } CHECK_REPL_SEND_LOOP_ERROR(status, msgtypestr); assert(SS_NORMAL == status); } /* This function is invoked on receipt of a REPL_NEED_INSTINFO message. * After doing some checks, it sends back a REPL_INSTINFO message containing the instance information. * If any of the checks fail it issues the appropriate error right away. * There can be TWO callers. One is the receiver server and another fetchresync rollback. * is_rcvr_srvr is 1 for the former and 0 for the latter. */ void gtmrecv_check_and_send_instinfo(repl_needinst_msg_ptr_t need_instinfo_msg, boolean_t is_rcvr_srvr) { boolean_t remote_side_is_supplementary, grab_lock_needed, lms_group_different; repl_inst_hdr_ptr_t inst_hdr; repl_instinfo_msg_t instinfo_msg; repl_inst_uuid *strm_start, *strm_top, *strm_info; FILE *log_fp; int reuse_slot, first_usable_slot; uint4 creator_pid; seq_num strm_jnl_seqno; sgmnt_addrs *repl_csa; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); /* ROLLBACK -FORWARD should not call this function */ repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; grab_lock_needed = is_rcvr_srvr || (jnlpool && (NULL != jnlpool->jnlpool_ctl) && !repl_csa->hold_onto_crit); remote_side_is_supplementary = need_instinfo_msg->is_supplementary; remote_side->is_supplementary = remote_side_is_supplementary; assert(remote_side->endianness_known); /* ensure remote_side->cross_endian is reliable */ /* If the remote source server is <= V62000 it would have endian converted "need_instinfo_msg" ONLY IF * src_jnl_ver < rcv_jnl_ver. If remote source server is > V62000 it would have endian converted unconditionally. * So we don't need to do the endian conversion in either of those cases. The need_instinfo_msg->proto_ver will let * us distinguish the > V62000 vs <= V62000 case. But if src version is <= V62000, we cannot easily distinguish what * the other side's jnl_ver is so we don't know if the source server would have endian converted or not. We work around * it by assuming that pids are limited to a max of 2**24 and so the most significant byte of the 4-byte pid should be 0. * This trick might not work always in case an OS supports > 2**24 pid number (I don't know of any now) OR if both the * most and least significant bytes of the 4-byte pid are 0, but if it does not, the worst we will see is a) an incorrect * INSNOTJOINED error issued by the receiver server OR b) an incorrect lms_group_info copied over into the instance file * (which will soon elicit some other related error). We can address (a) by trying out endian converting again below to * see if that prevents the error and if so use that instead but we cannot address (b) with this trick. * We can live with since the fix for (b) is to upgrade the receiver side to GTM V62001 or higher. */ if (remote_side->cross_endian && (REPL_PROTO_VER_XENDIANFIXES > need_instinfo_msg->proto_ver)) { creator_pid = need_instinfo_msg->lms_group_info.creator_pid; if (!(creator_pid & 0xff) && (creator_pid & 0xff000000)) ENDIAN_CONVERT_REPL_INST_UUID(&need_instinfo_msg->lms_group_info); } assert(is_rcvr_srvr && (NULL != gtmrecv_log_fp) || !is_rcvr_srvr && (NULL == gtmrecv_log_fp)); assert(!is_rcvr_srvr || !repl_csa->hold_onto_crit); assert(is_rcvr_srvr || !jgbl.onlnrlbk || repl_csa->hold_onto_crit); log_fp = !is_rcvr_srvr ? stdout : gtmrecv_log_fp; repl_log(log_fp, TRUE, TRUE, "Received REPL_NEED_INSTINFO message from primary instance [%s]\n", need_instinfo_msg->instname); inst_hdr = jnlpool->repl_inst_filehdr; assert(NULL != inst_hdr); /* The fact that we came here (REPL_NEED_INSTINFO message) implies the source server understands the * supplementary protocol. In that case, make sure -UPDATERESYNC if specified at receiver server startup * had a value as well. If not, issue error. */ if (is_rcvr_srvr && recvpool.gtmrecv_local->updateresync && (FD_INVALID == recvpool.gtmrecv_local->updresync_instfile_fd)) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2, LEN_AND_LIT("Source side is >= V5.5-000 implies -UPDATERESYNC needs a value specified")); assert(FALSE); /* we don't expect the rts_error to return control */ } /* We usually expect the LMS group info to be non-NULL on both primary and secondary. An exception is if * both of them are being brought up for the first time using a GT.M version that supports supplementary instances. * In this case, if the primary was brought up as a root primary, then it should still have the group info filled. * Assert that. */ assert(inst_hdr->lms_group_info.created_time || need_instinfo_msg->lms_group_info.created_time || !need_instinfo_msg->is_rootprimary); assert((is_rcvr_srvr && (NULL != jnlpool->jnlpool_ctl)) || (!is_rcvr_srvr && (NULL == jnlpool->jnlpool_ctl)) || jgbl.onlnrlbk || (jgbl.mur_rollback && INST_FREEZE_ON_ERROR_POLICY)); /* If this instance is supplementary and the journal pool exists (to indicate whether updates are enabled or not * which in turn helps us know whether this is an originating instance or not) do some additional checks. */ if (is_rcvr_srvr && inst_hdr->is_supplementary) { assert(NULL != jnlpool->jnlpool_ctl); if (!jnlpool->jnlpool_ctl->upd_disabled) { /* this supplementary instance was started with -UPDOK. Issue error if source is also supplementary */ if (need_instinfo_msg->is_supplementary) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_NOSUPPLSUPPL, 4, LEN_AND_STR((char *)inst_hdr->inst_info.this_instname), LEN_AND_STR((char *)need_instinfo_msg->instname)); assert(FALSE); /* we don't expect the rts_error to return control */ } } else { /* this supplementary instance was started with -UPDNOTOK. Issue error if source is not supplementary */ if (!need_instinfo_msg->is_supplementary) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_SUPRCVRNEEDSSUPSRC, 4, LEN_AND_STR((char *)inst_hdr->inst_info.this_instname), LEN_AND_STR((char *)need_instinfo_msg->instname)); assert(FALSE); /* we don't expect the rts_error to return control */ } } } /* Assert that if the receiver side is a root primary (i.e. has updates enabled), it better be a * supplementary instance and have the LMS group info filled in. Exception is if this a FETCHRESYNC * ROLLBACK run on an instance which was once primary (not necessarily a supplementary one). */ assert((NULL == jnlpool->jnlpool_ctl) || jnlpool->jnlpool_ctl->upd_disabled || inst_hdr->is_supplementary && IS_REPL_INST_UUID_NON_NULL(inst_hdr->lms_group_info) || jgbl.onlnrlbk || (jgbl.mur_rollback && INST_FREEZE_ON_ERROR_POLICY)); /* Check if primary and secondary are in same LMS group. Otherwise issue error. An exception is if the group info has * not yet been filled in after instance file creation. In that case, copy the info from primary and skip the error check. */ if (IS_REPL_INST_UUID_NON_NULL(inst_hdr->lms_group_info)) { /* LMS Group info has been initialized. Compare with that of the source side */ if (memcmp(&need_instinfo_msg->lms_group_info, &inst_hdr->lms_group_info, SIZEOF(inst_hdr->lms_group_info))) { /* Source and Receiver are part of DIFFERENT LMS Groups. If this instance is supplementary and remote * side is not supplementary then we expect them to be different. Otherwise issue error. * Note that because of an issue with endian-conversion (see big comment at start of this function), * it is possible this lms_group_info would be good (i.e. passed the memcmp) if endian-converted. So * try that once and if it still fails, go ahead with issuing an error. */ lms_group_different = TRUE; if (remote_side->cross_endian && (REPL_PROTO_VER_XENDIANFIXES > need_instinfo_msg->proto_ver)) { ENDIAN_CONVERT_REPL_INST_UUID(&need_instinfo_msg->lms_group_info); if (!memcmp(&need_instinfo_msg->lms_group_info, &inst_hdr->lms_group_info, SIZEOF(inst_hdr->lms_group_info))) lms_group_different = FALSE; /* stay with converted lms_group_info as it is good */ else { /* neither works so return to the original */ ENDIAN_CONVERT_REPL_INST_UUID(&need_instinfo_msg->lms_group_info); } } } else lms_group_different = FALSE; if (lms_group_different) { if (!inst_hdr->is_supplementary || remote_side_is_supplementary) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_INSNOTJOINED, 4, LEN_AND_STR((char *)inst_hdr->inst_info.this_instname), LEN_AND_STR((char *)need_instinfo_msg->instname)); assert(FALSE); /* we don't expect the rts_error to return control */ } } else { /* Primary and Secondary are part of SAME LMS Group. If this instance is supplementary and remote * side is not supplementary then we expect them to be different. Issue error in that case. */ if (inst_hdr->is_supplementary && !remote_side_is_supplementary) { assert(!need_instinfo_msg->is_supplementary); /* else NOSUPPLSUPPL error must have been issued */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_INSROLECHANGE, 4, LEN_AND_STR((char *)inst_hdr->inst_info.this_instname), LEN_AND_STR((char *)need_instinfo_msg->instname)); assert(FALSE); /* we don't expect the rts_error to return control */ } } if (is_rcvr_srvr && recvpool.gtmrecv_local->updateresync && (FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd)) { /* Caller is receiver server and -UPDATERESYNC= was specified. Check if the lms_group_info * of that file matches that of the source side. If not issue error. */ if (memcmp(&recvpool.gtmrecv_local->updresync_lms_group, &need_instinfo_msg->lms_group_info, SIZEOF(inst_hdr->lms_group_info))) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2, LEN_AND_LIT("Specified input instance file does not have same " "LMS Group information as source server instance")); } } } else if (IS_REPL_INST_UUID_NON_NULL(need_instinfo_msg->lms_group_info)) { /* LMS Group info is NULL in instance file header. Initialize it in journal pool from the value * on the primary side AND flush the changes to disk. Get lock before manipulating it. * If caller is rollback, no other process can be touching the instance file until we are done so * no need of the lock in that case. */ if (grab_lock_needed) { grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY); if (is_rcvr_srvr) GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED; } inst_hdr->lms_group_info = need_instinfo_msg->lms_group_info; repl_inst_flush_filehdr(); if (grab_lock_needed) rel_lock(jnlpool->jnlpool_dummy_reg); } /* If this instance is supplementary and remote side is not, then find out which stream # the non-supplementary source * corresponds to. Issue error if the source LMS group is unknown in the instance file. If this is non-supplementary, * the stream index is 0. */ if (inst_hdr->is_supplementary && !remote_side_is_supplementary) { assert(ARRAYSIZE(inst_hdr->strm_group_info) == (MAX_SUPPL_STRMS - 1)); assert(SIZEOF(repl_inst_uuid) == SIZEOF(inst_hdr->strm_group_info[0])); strm_start = &inst_hdr->strm_group_info[0]; strm_top = strm_start + ARRAYSIZE(inst_hdr->strm_group_info); /* Do this check under a lock as another receiver server started with -UPDATERESYNC= (not supported * now but will be in the future) could be changing the "strm_group_info" array concurrently. * If a matching stream is found, update gtmrecv_local->strm_index to reflect this while still holding lock. * This field is checked by the -UPDATERESYNC to see if a receiver has a given stream # actively in use. */ if (grab_lock_needed) { grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY); if (is_rcvr_srvr) GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED; } reuse_slot = 0; if (is_rcvr_srvr && recvpool.gtmrecv_local->updateresync && gtmrecv_options.reuse_specified) { /* If -REUSE was specified, check if instance name specified matches any existing slot. * If slot has already been found, don't search any more. */ for (strm_info = strm_start; strm_info < strm_top; strm_info++) { if (IS_REPL_INST_UUID_NULL(*strm_info)) continue; if (!STRCMP(gtmrecv_options.reuse_instname, strm_info->this_instname)) { reuse_slot = (strm_info - strm_start) + 1; break; } } if (strm_info == strm_top) { /* -REUSE specified an instance name that is not present in any of the 15 strm_group slots */ rel_lock(jnlpool->jnlpool_dummy_reg); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REUSEINSTNAME, 0, ERR_TEXT, 2, LEN_AND_LIT("Instance name in REUSE does not match any of 15 slots in instance file")); } assert(reuse_slot); } first_usable_slot = 0; strm_index = 0; for (strm_info = strm_start; strm_info < strm_top; strm_info++) { if (IS_REPL_INST_UUID_NULL(*strm_info)) { if (!first_usable_slot) first_usable_slot = (strm_info - strm_start) + 1; continue; } if (!memcmp(&need_instinfo_msg->lms_group_info, strm_info, SIZEOF(repl_inst_uuid))) { /* Found the stream corresponding to the source side */ strm_index = (strm_info - strm_start) + 1; break; } } if (strm_info == strm_top) { /* Non-supplementary source is unknown to this supplementary instance */ if (reuse_slot) /* -REUSE specified and did locate a reusable slot. Use it. */ { assert(is_rcvr_srvr); strm_index = reuse_slot; /* having reused the slot as specified, the qualifier has no further use & may be disruptive */ gtmrecv_options.reuse_specified = FALSE; } else if (gtmrecv_options.resume_specified) { assert(is_rcvr_srvr); strm_index = gtmrecv_options.resume_strm_num; } else if (is_rcvr_srvr && recvpool.gtmrecv_local->updateresync) { if (first_usable_slot) strm_index = first_usable_slot; else { if (grab_lock_needed) rel_lock(jnlpool->jnlpool_dummy_reg); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2, LEN_AND_LIT("No empty slot found. Specify REUSE to choose one for reuse")); } } else { if (grab_lock_needed) rel_lock(jnlpool->jnlpool_dummy_reg); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_INSUNKNOWN, 4, LEN_AND_STR((char *)inst_hdr->inst_info.this_instname), LEN_AND_STR((char *)need_instinfo_msg->instname)); assert(FALSE); /* we don't expect the rts_error to return control */ } /* Since we did not find the stream in the existing instance file but did find a slot, fill that slot * while we have the lock on the instance file. This way another -updateresync startup of a receiver * server (when we have multiple receiver server support) will see this slot taken when it gets the lock. */ assert(is_rcvr_srvr && recvpool.gtmrecv_local->updateresync && (FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd)); assert(!memcmp(&recvpool.gtmrecv_local->updresync_lms_group, &need_instinfo_msg->lms_group_info, SIZEOF(inst_hdr->lms_group_info))); assert(0 < strm_index); strm_info = &strm_start[strm_index - 1]; assert(strm_info < strm_top); assert(IS_REPL_INST_UUID_NON_NULL(need_instinfo_msg->lms_group_info)); *strm_info = need_instinfo_msg->lms_group_info; repl_inst_flush_filehdr(); } else if ((gtmrecv_options.resume_specified) && (gtmrecv_options.resume_strm_num != strm_index)) { /* If -RESUME was specified, then the slot it matched must be same as slot found without its use */ assert(is_rcvr_srvr); rel_lock(jnlpool->jnlpool_dummy_reg); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_RESUMESTRMNUM, 0, ERR_TEXT, 2, LEN_AND_LIT("Source side LMS " "group is found in instance file but RESUME specifies different stream number")); } else if (reuse_slot && (reuse_slot != strm_index)) { /* If -REUSE was specified, then the slot it matched must be same as slot found without its use */ assert(is_rcvr_srvr); rel_lock(jnlpool->jnlpool_dummy_reg); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REUSEINSTNAME, 0, ERR_TEXT, 2, LEN_AND_LIT("Source side LMS " "group is found in instance file but REUSE specifies different instance name")); } assert(INVALID_SUPPL_STRM != strm_index); assert(0 < strm_index); assert(MAX_SUPPL_STRMS > strm_index); /* Maintain stream slot # in shared memory as well so another -UPDATERESYNC= * can see which stream# is actively in use by this receiver server. */ if (is_rcvr_srvr) { if (recvpool.gtmrecv_local->strm_index && (strm_index != recvpool.gtmrecv_local->strm_index)) { /* This receiver server has already connected to a source server with a different stream #. * Since mixing of multiple stream journal records in the same receive pool confuses the * update process, issue error. Note: This limitation "might" be removed in the future. */ rel_lock(jnlpool->jnlpool_dummy_reg); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_RCVRMANYSTRMS, 2, strm_index, recvpool.gtmrecv_local->strm_index); } recvpool.gtmrecv_local->strm_index = strm_index; repl_log(gtmrecv_log_fp, TRUE, TRUE, "Determined non-supplementary source Stream # = %d\n", strm_index); assert(IS_REPL_INST_UUID_NON_NULL(need_instinfo_msg->lms_group_info)); recvpool.gtmrecv_local->remote_lms_group = need_instinfo_msg->lms_group_info; rel_lock(jnlpool->jnlpool_dummy_reg); } else repl_log(stdout, TRUE, TRUE, "Determined non-supplementary source Stream # = %d\n", strm_index); /* Compute non-zero strm_jnl_seqno to send across in the REPL_INSTINFO message. * For receiver server * If updateresync startup and this is the first connection to a source server * If -RESUME is not specified : * --> gtmrecv_local->updresync_jnl_seqno * If -RESUME is specified : * --> jnlpool_ctl->strm_seqno[gtmrecv_options.resume_strm_num] * If updateresync startup and this is not the first connection to a source server * --> recvpool_ctl->jnl_seqno * If normal startup and this is the first connection to a source server * --> inst_hdr->strm_seqno[strm_index] where strm_index > 0 * If this is not the first connection to a source server * --> recvpool_ctl->jnl_seqno * In case of instance crash * --> No need to worry about this case as we would have otherwise issued a * --> REPLREQROLLBACK error when the source server for this instance started up after the crash. * For rollback * If normal startup after a clean shutdown of the instance file * --> inst_hdr->strm_seqno[strm_index] where strm_index > 0 * In case of crash shutdown of instance file * --> Take max of cs_data->strm_seqno[strm_index] across all databases and use this (strm_index > 0) */ if (is_rcvr_srvr) { if (recvpool.upd_proc_local->read_jnl_seqno) strm_jnl_seqno = recvpool.recvpool_ctl->jnl_seqno; else { if (recvpool.gtmrecv_local->updateresync) { assert(FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd); if (!gtmrecv_options.resume_specified) strm_jnl_seqno = recvpool.gtmrecv_local->updresync_jnl_seqno; else { strm_jnl_seqno = jnlpool->jnlpool_ctl->strm_seqno[gtmrecv_options.resume_strm_num]; /* It is possible for the strm_seqno to be 0. This implies the stream has * had no updates. In that case, ideally this value should have been 1. * But because we want to differentiate a stream that has had updates from * stream numbers where there is no interest, we follow this convention. * Therefore, in this case, reset the 0 back to 1 so we never send a zero * seqno to the other side. */ if (!strm_jnl_seqno) strm_jnl_seqno = 1; } } else strm_jnl_seqno = jnlpool->jnlpool_ctl->strm_seqno[strm_index]; assert(0 == GET_STRM_INDEX(strm_jnl_seqno)); } repl_log(gtmrecv_log_fp, TRUE, TRUE, "Sending Stream Seqno = "INT8_FMT" "INT8_FMTX"\n", strm_jnl_seqno, strm_jnl_seqno); } else { if (jnlpool->repl_inst_filehdr->crash) strm_jnl_seqno = mur_get_max_strm_reg_seqno(strm_index); else { assert((NULL == jnlpool->jnlpool_ctl) || jgbl.onlnrlbk); assert((NULL == jnlpool->jnlpool_ctl) || (jnlpool->jnlpool_ctl->strm_seqno[strm_index] == inst_hdr->strm_seqno[strm_index])); strm_jnl_seqno = jnlpool->repl_inst_filehdr->strm_seqno[strm_index]; } repl_log(stdout, TRUE, TRUE, "Sending Stream Seqno = "INT8_FMT" "INT8_FMTX"\n", strm_jnl_seqno, strm_jnl_seqno); } } else { strm_jnl_seqno = 0; /* actually no need to initialize this since source server will not look at this * field in this case but still be safe */ if (inst_hdr->is_supplementary) { /* In case caller is receiver server, "strm_index" would have been already set to 0 in jnlpool_init.c. * But in case caller is rollback, "strm_index" would still be set to -1. In this case, set it to 0. */ assert(!is_rcvr_srvr || (0 == strm_index)); strm_index = 0; } DEBUG_ONLY( if (is_rcvr_srvr) NULL_INITIALIZE_REPL_INST_UUID(recvpool.gtmrecv_local->remote_lms_group); ) } assert(!is_rcvr_srvr || (INVALID_SUPPL_STRM == recvpool.gtmrecv_local->strm_index) || ((0 <= recvpool.gtmrecv_local->strm_index) && (MAX_SUPPL_STRMS > recvpool.gtmrecv_local->strm_index))); /* Initialize the remote side protocol version from "proto_ver" field of this msg */ assert(REPL_PROTO_VER_SUPPLEMENTARY <= need_instinfo_msg->proto_ver); remote_side->proto_ver = need_instinfo_msg->proto_ver; /*************** Send REPL_INSTINFO message ***************/ memset(&instinfo_msg, 0, SIZEOF(instinfo_msg)); memcpy(instinfo_msg.instname, inst_hdr->inst_info.this_instname, MAX_INSTNAME_LEN - 1); if (grab_lock_needed) { grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY); if (is_rcvr_srvr) GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED; } instinfo_msg.was_rootprimary = (unsigned char)repl_inst_was_rootprimary(); if (grab_lock_needed) rel_lock(jnlpool->jnlpool_dummy_reg); if (!is_rcvr_srvr) murgbl.was_rootprimary = instinfo_msg.was_rootprimary; instinfo_msg.strm_jnl_seqno = strm_jnl_seqno; /* strm_jnl_seqno is not expected to be zero unless this is a non-supplementary instance (like A->B) in which case * strm_seqno is not maintained OR the remote side is supplementary (like P->Q) in which case the two instances do * not communicate in-terms of strm_seqno (once the handshake is established) */ assert(strm_jnl_seqno || !inst_hdr->is_supplementary || remote_side_is_supplementary); instinfo_msg.lms_group_info = inst_hdr->lms_group_info; assert(remote_side->endianness_known); /* only then is remote_side->cross_endian reliable */ if (remote_side->cross_endian) { ENDIAN_CONVERT_REPL_INST_UUID(&instinfo_msg.lms_group_info); instinfo_msg.strm_jnl_seqno = GTM_BYTESWAP_64(instinfo_msg.strm_jnl_seqno); } gtmrecv_repl_send((repl_msg_ptr_t)&instinfo_msg, REPL_INSTINFO, SIZEOF(repl_instinfo_msg_t), "REPL_INSTINFO", MAX_SEQNO); if (repl_connection_reset || is_rcvr_srvr && gtmrecv_wait_for_jnl_seqno) return; /* Do not allow an instance which was formerly a root primary or which still * has a non-zero value of "zqgblmod_seqno" to start up as a tertiary. The only exception is * if this is P (supplementary instance) receiving from a non-supplementary instance. * In that case, P can never be a root primary of the non-supplementary group and therefore * cannot be affected by lost transactions being applied from the non-supplementary group. */ if ((!inst_hdr->is_supplementary || remote_side_is_supplementary) && !need_instinfo_msg->is_rootprimary && (instinfo_msg.was_rootprimary || (is_rcvr_srvr && jnlpool->jnlpool_ctl->max_zqgblmod_seqno))) { if (is_rcvr_srvr) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_PRIMARYNOTROOT, 2, LEN_AND_STR((char *) need_instinfo_msg->instname)); gtmrecv_autoshutdown(); /* should not return */ } else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_PRIMARYNOTROOT, 2, LEN_AND_STR((char *) need_instinfo_msg->instname)); assert(FALSE); } if (is_rcvr_srvr) memcpy(jnlpool->jnlpool_ctl->primary_instname, need_instinfo_msg->instname, MAX_INSTNAME_LEN - 1); } STATICFNDEF int gtmrecv_start_onln_rlbk(void) { char command[ONLN_RLBK_CMD_MAXLEN + 1], *errptr; int status, save_errno, cmdlen; gtmrecv_local_ptr_t gtmrecv_local; assert(!have_crit(CRIT_HAVE_ANY_REG)); gtmrecv_local = recvpool.gtmrecv_local; MEMCPY_LIT(command, MUPIP_DIST_STR); cmdlen = STR_LIT_LEN(MUPIP_DIST_STR); MEMCPY_LIT(&command[cmdlen], ONLN_RLBK_CMD); cmdlen += STR_LIT_LEN(ONLN_RLBK_CMD); if (gtmrecv_options.autorollback_verbose) { MEMCPY_LIT(&command[cmdlen], ONLN_RLBK_VERBOSE); cmdlen += STR_LIT_LEN(ONLN_RLBK_VERBOSE); } MEMCPY_LIT(&command[cmdlen], ONLN_RLBK_QUALIFIERS); cmdlen += STR_LIT_LEN(ONLN_RLBK_QUALIFIERS); assert(0 < gtmrecv_local->listen_port); SNPRINTF(&command[cmdlen], ONLN_RLBK_CMD_MAXLEN - cmdlen, "%d", gtmrecv_local->listen_port); /* will add '\0' at the end */ repl_log(gtmrecv_log_fp, TRUE, TRUE, "Executing %s\n", command); status = SYSTEM(((char *)command)); if (0 != status) { if (-1 == status) { save_errno = errno; errptr = (char *)STRERROR(save_errno); repl_log(gtmrecv_log_fp, TRUE, TRUE, "SYSTEM command failed: %s\n", errptr); } else { /* ONLINE ROLLBACK returned a non-zero status */ repl_log(gtmrecv_log_fp, TRUE, TRUE, "ONLINE FETCHRESYNC ROLLBACK exited with code - %d\n", status); } repl_log(gtmrecv_log_fp, TRUE, TRUE, "Could not complete ONLINE FETCHRESYNC ROLLBACK due to the above errors\n"); } else { repl_log(gtmrecv_log_fp, TRUE, TRUE, "ONLINE FETCHRESYNC ROLLBACK completed successfully\n"); } return status; } /* We are here because a grab_lock or grab_crit saw a concurrent online rollback and we need to break the connection and * re-establish with the new sequence number (the rolled back one). Wait for the update process to let us know the new sequence * number from where we should start requesting transactions from. * Ideally this function should have been a part of onln_rlbk.c. However, the function calls into gtmrecv_poll_actions which * is strictly in the libmupip archive. Moving the function into onln_rlbk.c means it will try to pull in gtmrecv_poll_actions * and all the modules that it calls which is something that we don't desire. So, keep this function isolated from onln_rlbk.c */ void gtmrecv_onln_rlbk_clnup(void) { boolean_t connection_already_reset; assert(NULL != gtmrecv_log_fp); repl_log(gtmrecv_log_fp, TRUE, TRUE, "---> ONLINE ROLLBACK. Current Jnlpool Seqno : "INT8_FMT"\n", jnlpool->jnlpool_ctl->jnl_seqno); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Waiting for update process to set recvpool_ctl->onln_rlbk_flag\n"); connection_already_reset = repl_connection_reset; assert(!gtmrecv_wait_for_jnl_seqno); while (TRUE) { SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_PROGRESS); gtmrecv_poll_actions(data_len, buff_unprocessed, buffp); /* If connection was already closed before we came here (possible if receiver server closed the connection in * response to a REPL_ROLLBACK_FIRST message) after spawning off an online rollback, then we should NOT check for * repl_connection_reset. This way, we avoid breaking prematurely from this loop without waiting for the update * process to acknowledge the online rollback. In this case, wait for gtmrecv_wait_for_jnl_seqno to be set to TRUE * by gtmrecv_poll_actions. */ if ((!connection_already_reset && repl_connection_reset) || gtmrecv_wait_for_jnl_seqno) break; } return; } /* This function is invoked on receipt of a REPL_NEED_HISTINFO message. * This in turn sends a REPL_HISTINFO message containing the history information. */ void gtmrecv_send_histinfo(repl_histinfo *cur_histinfo) { repl_histinfo1_msg_t histinfo1_msg; repl_histinfo2_msg_t histinfo2_msg; repl_histinfo_msg_t histinfo_msg; FILE *log_fp; char remote_proto_ver; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* If sending history from a supplementary to a non-supplementary version, assert that the history record * particularly the "strm_seqno" is 0 as a non-zero value is not understood by a non-supplementary instance. */ assert((NULL == this_side) || (this_side->is_supplementary == jnlpool->repl_inst_filehdr->is_supplementary)); assert(!jnlpool->repl_inst_filehdr->is_supplementary || remote_side->is_supplementary || !cur_histinfo->strm_seqno); remote_proto_ver = remote_side->proto_ver; assert(REPL_PROTO_VER_MULTISITE <= remote_proto_ver); assert(remote_side->endianness_known); /* only then is remote_side->cross_endian reliable */ if (REPL_PROTO_VER_SUPPLEMENTARY > remote_proto_ver) { /* Remote side does not support supplementary protocol. Send OLDER histinfo messages */ /*************** Send REPL_OLD_TRIPLEINFO1 message ***************/ histinfo1_msg.start_seqno = !remote_side->cross_endian ? cur_histinfo->start_seqno : GTM_BYTESWAP_64(cur_histinfo->start_seqno); memcpy(histinfo1_msg.instname, cur_histinfo->root_primary_instname, MAX_INSTNAME_LEN - 1); histinfo1_msg.instname[MAX_INSTNAME_LEN - 1] = '\0'; gtmrecv_repl_send((repl_msg_ptr_t)&histinfo1_msg, REPL_OLD_TRIPLEINFO1, MIN_REPL_MSGLEN, "REPL_OLD_TRIPLEINFO1", cur_histinfo->start_seqno); if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) return; /*************** Send REPL_OLD_TRIPLEINFO2 message ***************/ if (!remote_side->cross_endian) { histinfo2_msg.start_seqno = cur_histinfo->start_seqno; histinfo2_msg.cycle = cur_histinfo->root_primary_cycle; histinfo2_msg.histinfo_num = cur_histinfo->histinfo_num; } else { histinfo2_msg.start_seqno = GTM_BYTESWAP_64(cur_histinfo->start_seqno); histinfo2_msg.cycle = GTM_BYTESWAP_32(cur_histinfo->root_primary_cycle); histinfo2_msg.histinfo_num = GTM_BYTESWAP_32(cur_histinfo->histinfo_num); } gtmrecv_repl_send((repl_msg_ptr_t)&histinfo2_msg, REPL_OLD_TRIPLEINFO2, MIN_REPL_MSGLEN, "REPL_OLD_TRIPLEINFO2", cur_histinfo->start_seqno); if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) return; } else { /* Remote side does support supplementary protocol. Send NEWER histinfo message. */ histinfo_msg.history = *cur_histinfo; histinfo_msg.history.root_primary_instname[MAX_INSTNAME_LEN - 1] = '\0'; /* just in case */ if (remote_side->cross_endian) ENDIAN_CONVERT_REPL_HISTINFO(&histinfo_msg.history); gtmrecv_repl_send((repl_msg_ptr_t)&histinfo_msg, REPL_HISTINFO, SIZEOF(repl_histinfo_msg_t), "REPL_HISTINFO", cur_histinfo->start_seqno); } log_fp = (NULL == gtmrecv_log_fp) ? stdout : gtmrecv_log_fp; repl_dump_histinfo(log_fp, TRUE, FALSE, "History sent", cur_histinfo); } STATICFNDEF void prepare_recvpool_for_write(gtm_uint64_t datalen, gtm_uint64_t pre_filter_write_len) { recvpool_ctl_ptr_t recvpool_ctl; recvpool_ctl = recvpool.recvpool_ctl; if (datalen > recvpool_size) { /* Too large a transaction to be accommodated in the Receive Pool */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLTRANS2BIG, 5, &recvpool_ctl->jnl_seqno, &datalen, &pre_filter_write_len, LEN_AND_LIT("Receive")); } if (write_loc > (recvpool_size - datalen)) { REPL_DEBUG_ONLY( if (recvpool_ctl->wrapped) REPL_DPRINT1("Update Process too slow. Waiting for it to free up space and wrap\n"); ) while (recvpool_ctl->wrapped) { /* Wait till the updproc wraps */ SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_PROGRESS); GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp); } assert(recvpool_ctl->wrapped == FALSE); recvpool_ctl->write_wrap = write_wrap = write_loc; /* The update process reads (a) "recvpool_ctl->write" first. If "write" is not equal to * "upd_proc_local->read", it then reads (b) "recvpool_ctl->write_wrap" and assumes that * "write_wrap" holds a non-stale value. This is in turn used to compare "temp_read" and * "write_wrap" to determine how much of unprocessed data there is in the receive pool. If * it so happens that the receiver server sets "write_wrap" in the above line to a value * that is lesser than its previous value (possible if in the previous wrap of the pool, * transactions used more portions of the pool than in the current wrap), it is important * that the update process sees the updated value of "write_wrap" as long as it sees the * corresponding update to "write". This is because it will otherwise end up processing * the tail section of the receive pool (starting from the uptodate value of "write" to the * stale value of "write_wrap") that does not contain valid journal data. For this read order * dependency to hold good, the receiver server needs to do a write memory barrier * after updating "write_wrap" but before updating "write". The update process * will do a read memory barrier after reading "wrapped" but before reading "write". */ SHM_WRITE_MEMORY_BARRIER; /* The update process reads (a) "recvpool_ctl->wrapped" first and then reads (b) * "recvpool_ctl->write". If "wrapped" is TRUE, it assumes that "write" will never hold a stale * value that reflects a corresponding previous state of "wrapped" (i.e. "write" will point to * the beginning of the receive pool, either 0 or a small non-zero value instead of pointing * to the end of the receive pool). For this to hold good, the receiver server needs to do * a write memory barrier after updating "write" but before updating "wrapped". The update * process will do a read memory barrier after reading "wrapped" but before reading "write". */ recvpool_ctl->write = write_loc = 0; SHM_WRITE_MEMORY_BARRIER; recvpool_ctl->wrapped = TRUE; } DO_FLOW_CONTROL(write_loc); } STATICFNDEF void copy_to_recvpool(uchar_ptr_t databuff, gtm_uint64_t datalen) { gtm_uint64_t upd_read; gtm_uint64_t future_write; upd_proc_local_ptr_t upd_proc_local; recvpool_ctl_ptr_t recvpool_ctl; recvpool_ctl = recvpool.recvpool_ctl; upd_proc_local = recvpool.upd_proc_local; future_write = write_loc + datalen; assertpro(future_write >= write_loc); /* prepare_recvpool_for_write should have handled this */ upd_read = upd_proc_local->read; REPL_DEBUG_ONLY( if (recvpool_ctl->wrapped && (upd_read <= future_write)) { REPL_DPRINT1("Update Process too slow. Waiting for it to free up space\n"); } ) while (recvpool_ctl->wrapped && (upd_read <= future_write)) { /* Write will cause overflow. Wait till there is more space available */ SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_PROGRESS); GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp); upd_read = upd_proc_local->read; } memcpy(recvpool.recvdata_base + write_loc, databuff, datalen); write_loc = future_write; if (write_loc > write_wrap) write_wrap = write_loc; } STATICFNDEF void wait_for_updproc_to_clear_backlog(void) { upd_proc_local_ptr_t upd_proc_local; recvpool_ctl_ptr_t recvpool_ctl; recvpool_ctl = recvpool.recvpool_ctl; upd_proc_local = recvpool.upd_proc_local; while (upd_proc_local->read != recvpool_ctl->write) { SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_PROGRESS); GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp); } } STATICFNDEF void process_tr_buff(int msg_type) { recvpool_ctl_ptr_t recvpool_ctl; seq_num log_seqno, recv_jnl_seqno, upd_seqno, diff_seqno; uint4 in_size, out_size, upd_read, max_strm_histinfo; boolean_t filter_pass = FALSE, is_new_histrec, is_repl_cmpc; uchar_ptr_t save_buffp, save_filter_buff, in_buff; int idx, status, num_strm_histinfo; qw_num msg_total; repl_old_triple_jnl_t old_triple_content; uLongf destlen; int cmpret, cur_data_len, rc; repl_msg_ptr_t msgp, msgp_top; int4 histinfo_strm_num; uint4 write_len, pre_filter_write_len, pre_filter_write; boolean_t uncmpfail; repl_histinfo *cur_histinfo, *pool_histinfo, tmp_histinfo; repl_histrec_jnl_t *pool_histrec, tmp_histjrec, rcvd_strm_histjrec[MAX_SUPPL_STRMS]; static boolean_t first_histrec = TRUE; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; recvpool_ctl = recvpool.recvpool_ctl; is_repl_cmpc = ((REPL_TR_CMP_JNL_RECS == msg_type) || (REPL_TR_CMP_JNL_RECS2 == msg_type)); is_new_histrec = ((REPL_OLD_TRIPLE == msg_type) || (REPL_HISTREC == msg_type)); assert(!is_repl_cmpc || !is_new_histrec); /* HISTINFO records should not be compressed in the pipe */ if (is_repl_cmpc) { assert(gtmrecv_max_repl_uncmpmsglen); destlen = gtmrecv_max_repl_uncmpmsglen; if (ZLIB_CMPLVL_NONE == gtm_zlib_cmp_level) { /* Receiver does not have compression enabled in the first place but yet source server has sent * compressed records. Stop source server from sending compressed records. */ uncmpfail = TRUE; } else { ZLIB_UNCOMPRESS(gtmrecv_uncmpmsgp, destlen, gtmrecv_cmpmsgp, gtmrecv_repl_cmpmsglen, cmpret); GTM_WHITE_BOX_TEST(WBTEST_REPL_TR_UNCMP_ERROR, cmpret, Z_DATA_ERROR); recv_jnl_seqno = recvpool_ctl->jnl_seqno; switch(cmpret) { case Z_MEM_ERROR: assert(FALSE); repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_Z_MEM_ERROR_STR GTM_ZLIB_UNCMP_ERR_SEQNO_STR, recv_jnl_seqno, recv_jnl_seqno); break; case Z_BUF_ERROR: assert(FALSE); repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_Z_BUF_ERROR_STR GTM_ZLIB_UNCMP_ERR_SEQNO_STR, recv_jnl_seqno, recv_jnl_seqno); break; case Z_DATA_ERROR: assert(gtm_white_box_test_case_enabled && (WBTEST_REPL_TR_UNCMP_ERROR == gtm_white_box_test_case_number)); repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_Z_DATA_ERROR_STR GTM_ZLIB_UNCMP_ERR_SEQNO_STR, recv_jnl_seqno, recv_jnl_seqno); break; } uncmpfail = (Z_OK != cmpret); if (!uncmpfail) { GTM_WHITE_BOX_TEST(WBTEST_REPL_TR_UNCMP_ERROR, destlen, gtmrecv_repl_uncmpmsglen - 1); if (destlen != gtmrecv_repl_uncmpmsglen) { /* decompression did not yield precompressed data length */ assert(gtm_white_box_test_case_enabled && (WBTEST_REPL_TR_UNCMP_ERROR == gtm_white_box_test_case_number)); repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_UNCMPLEN_ERROR_STR GTM_ZLIB_UNCMP_ERR_SEQNO_STR, destlen, gtmrecv_repl_uncmpmsglen, recv_jnl_seqno, recv_jnl_seqno); uncmpfail = TRUE; } } } if (uncmpfail) { /* Since uncompression failed, default to NO compression. Send a REPL_CMP2UNCMP message accordingly */ repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_UNCMPTRANSITION_STR); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Waiting for update process to clear the backlog first\n"); wait_for_updproc_to_clear_backlog(); if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) return; repl_log(gtmrecv_log_fp, TRUE, TRUE, "Update process has successfully cleared the backlog\n"); gtmrecv_send_cmp2uncmp = TRUE; /* trigger REPL_CMP2UNCMP message processing */ gtmrecv_poll_actions(data_len, buff_unprocessed, buffp); assert(repl_connection_reset || (!gtmrecv_send_cmp2uncmp && gtmrecv_wait_for_jnl_seqno)); return; } assert(0 == destlen % REPL_MSG_ALIGN); msgp = (repl_msg_ptr_t)gtmrecv_uncmpmsgp; msgp_top = (repl_msg_ptr_t)(gtmrecv_uncmpmsgp + destlen); } do { assert(remote_side->endianness_known); /* only then is remote_side->cross_endian reliable */ if (is_repl_cmpc) { if (msgp >= msgp_top) { assert(msgp == msgp_top); break; } /* If primary is of different endianness, endian convert the UNCOMPRESSED message header * before using the type and len fields (the compressed message header was already endian * converted as part of receiving the message in do_main_loop()) */ if (remote_side->cross_endian) { msgp->type = GTM_BYTESWAP_32(msgp->type); msgp->len = GTM_BYTESWAP_32(msgp->len); } assert(REPL_TR_JNL_RECS == msgp->type); cur_data_len = msgp->len - REPL_MSG_HDRLEN; assert(0 < cur_data_len); assert(0 == (cur_data_len % REPL_MSG_ALIGN)); PREPARE_RECVPOOL_FOR_WRITE(cur_data_len, 0); /* could update "recvpool_ctl->write" and "write_loc" */ COPY_TO_RECVPOOL((uchar_ptr_t)msgp + REPL_MSG_HDRLEN, cur_data_len);/* uses and updates "write_loc" */ msgp = (repl_msg_ptr_t)((uchar_ptr_t)msgp + cur_data_len + REPL_MSG_HDRLEN); } write_off = recvpool_ctl->write; write_len = (write_loc - write_off); assert((write_off != write_wrap) || (0 == write_off)); assert(remote_side->jnl_ver); assert(!remote_side->cross_endian || (V18_JNL_VER <= remote_side->jnl_ver)); if (ENDIAN_CONVERSION_NEEDED(is_new_histrec, this_side->jnl_ver, remote_side->jnl_ver, remote_side->cross_endian)) { if (SS_NORMAL != (status = repl_tr_endian_convert(remote_side->jnl_ver, recvpool.recvdata_base + write_off, write_len))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_REPLXENDIANFAIL, 3, LEN_AND_LIT("Replicating"), &recvpool.upd_proc_local->read_jnl_seqno); } if (!is_new_histrec) { if (NO_FILTER != gtmrecv_filter) { /* Need to pass through filter */ pre_filter_write = write_off; pre_filter_write_len = write_len; /* If input transaction cannot fit in currently allocated buffer, expand buffer * directly to that needed length hoping this should be enough (minimizes the # of * 50% expansions we do). If this is still not enough (because the filter function * results in bigger sized jnl records) we will then do geometric expansion of this * buffer in the while loop below as much as needed. */ if (write_len > repl_filter_bufsiz) { gtmrecv_free_filter_buff(); gtmrecv_alloc_filter_buff(write_len); } if (gtmrecv_filter & INTERNAL_FILTER) { in_buff = recvpool.recvdata_base + write_off; in_size = write_len; while (SS_NORMAL != (status = repl_filter_old2cur[remote_side->jnl_ver - JNL_VER_EARLIEST_REPL]( in_buff, &in_size, repl_filter_buff, &out_size, repl_filter_bufsiz)) && (EREPL_INTLFILTER_NOSPC == repl_errno)) { /* We ran out of space in current buffer. We can try and use the transformed * records as is and resume transformation from where the space-issue occurred * but the transformation function might rely on seeing the entire transaction * context in one shot so better not to take a risk. So free old buffer and * allocate new buffer and redo transformation from scratch. */ gtmrecv_free_filter_buff(); in_size = pre_filter_write_len; /* just in case in_size was modified */ write_len = write_len + (write_len >> 1); /* increase the buffer size by half */ gtmrecv_alloc_filter_buff(write_len); } if (SS_NORMAL == status) write_len = out_size; else { if (EREPL_INTLFILTER_DATA2LONG == repl_errno) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_JNLSETDATA2LONG, 2, jnl_source_datalen, jnl_dest_maxdatalen); else if (EREPL_INTLFILTER_NEWREC == repl_errno) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_JNLNEWREC, 2, (unsigned int)jnl_source_rectype, (unsigned int)jnl_dest_maxrectype); else { INT_FILTER_RTS_ERROR(recvpool_ctl->jnl_seqno, repl_errno); /* no return */ } } } else memcpy(repl_filter_buff, recvpool.recvdata_base + write_off, write_len); assert(write_len <= repl_filter_bufsiz); GTMTRIG_ONLY( if ((unsigned char)V19_JNL_VER <= remote_side->jnl_ver) { repl_sort_tr_buff(repl_filter_buff, write_len); DBG_VERIFY_TR_BUFF_SORTED(repl_filter_buff, write_len); } ) if ((gtmrecv_filter & EXTERNAL_FILTER) && (SS_NORMAL != (status = repl_filter(recvpool_ctl->jnl_seqno, &repl_filter_buff, (int*)&write_len, &repl_filter_bufsiz)))) repl_filter_error(recvpool_ctl->jnl_seqno, status); GTMTRIG_ONLY( /* Ensure that the external filter has not disturbed the sorted sequence of the * update_num */ DEBUG_ONLY( if ((unsigned char)V19_JNL_VER <= remote_side->jnl_ver) DBG_VERIFY_TR_BUFF_SORTED(repl_filter_buff, write_len); ) ) assert(write_len <= repl_filter_bufsiz); write_loc = write_off; /* reset "write_loc" */ PREPARE_RECVPOOL_FOR_WRITE(write_len, pre_filter_write_len); /* could update "->write" * and "write_loc" */ COPY_TO_RECVPOOL((uchar_ptr_t)repl_filter_buff, write_len);/* uses and updates "write_loc" */ write_off = recvpool_ctl->write; repl_recv_postfltr_data_procd += (qw_num)write_len; filter_pass = TRUE; } else { GTMTRIG_ONLY( if ((unsigned char)V19_JNL_VER <= remote_side->jnl_ver) { repl_sort_tr_buff((uchar_ptr_t)(recvpool.recvdata_base + write_off), write_len); DBG_VERIFY_TR_BUFF_SORTED((recvpool.recvdata_base + write_off), write_len); } ) } } if (recvpool_ctl->jnl_seqno - lastlog_seqno >= log_interval) { log_seqno = recvpool_ctl->jnl_seqno; upd_seqno = recvpool.upd_proc_local->read_jnl_seqno; assert(log_seqno >= upd_seqno); diff_seqno = (log_seqno - upd_seqno); trans_recvd_cnt += (log_seqno - lastlog_seqno); msg_total = repl_recv_data_recvd - buff_unprocessed; /* Don't include data not yet processed, we'll include that count in a later log */ if (NO_FILTER == gtmrecv_filter) { repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL INFO - Seqno : "INT8_FMT" "INT8_FMTX " Jnl Total : "INT8_FMT" "INT8_FMTX" Msg Total : "INT8_FMT" "INT8_FMTX " Current backlog : "INT8_FMT" "INT8_FMTX"\n", log_seqno, log_seqno, repl_recv_data_processed, repl_recv_data_processed, msg_total, msg_total, diff_seqno, diff_seqno); } else { repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL INFO - Seqno : "INT8_FMT" "INT8_FMTX " Pre filter total : "INT8_FMT" "INT8_FMTX" Post filter total : " INT8_FMT" "INT8_FMTX" Msg Total : "INT8_FMT" "INT8_FMTX " Current backlog : "INT8_FMT" "INT8_FMTX"\n", log_seqno, log_seqno, repl_recv_data_processed, repl_recv_data_processed, repl_recv_postfltr_data_procd, repl_recv_postfltr_data_procd, msg_total, msg_total, diff_seqno, diff_seqno); } /* Approximate time with an error not more than GTMRECV_HEARTBEAT_PERIOD. We use this * instead of calling time(), and expensive system call, especially on VMS. The * consequence of this choice is that we may defer logging when we may have logged. We * can live with that. Currently, the logging interval is not changeable by users. * When/if we provide means of choosing log interval, this code may have to be re-examined. * - Vinaya 2003/09/08. */ assert(0 != gtmrecv_now); repl_recv_this_log_time = gtmrecv_now; assert(repl_recv_this_log_time >= repl_recv_prev_log_time); time_elapsed = difftime(repl_recv_this_log_time, repl_recv_prev_log_time); if ((double)GTMRECV_LOGSTATS_INTERVAL <= time_elapsed) { repl_log(gtmrecv_log_fp, TRUE, FALSE, "REPL INFO since last log : Time elapsed : " "%00.f Tr recvd : "INT8_FMT" Tr bytes : "INT8_FMT" Msg bytes : "INT8_FMT"\n", time_elapsed, trans_recvd_cnt - last_log_tr_recvd_cnt, repl_recv_data_processed - repl_recv_lastlog_data_procd, msg_total - repl_recv_lastlog_data_recvd); repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL INFO since last log : Time elapsed : " "%00.f Tr recvd/s : %f Tr bytes/s : %f Msg bytes/s : %f\n", time_elapsed, (float)(trans_recvd_cnt - last_log_tr_recvd_cnt)/time_elapsed, (float)(repl_recv_data_processed - repl_recv_lastlog_data_procd)/time_elapsed, (float)(msg_total - repl_recv_lastlog_data_recvd)/time_elapsed); repl_recv_lastlog_data_procd = repl_recv_data_processed; repl_recv_lastlog_data_recvd = msg_total; last_log_tr_recvd_cnt = trans_recvd_cnt; repl_recv_prev_log_time = repl_recv_this_log_time; } lastlog_seqno = log_seqno; } if (gtmrecv_logstats) { if (!filter_pass) { repl_log(gtmrecv_log_fp, FALSE, FALSE, "Tr : "INT8_FMT" Size : %d Write : %ld " "Total : "INT8_FMT"\n", recvpool_ctl->jnl_seqno, write_len, write_off, repl_recv_data_processed); } else { assert(!is_new_histrec); repl_log(gtmrecv_log_fp, FALSE, FALSE, "Tr : "INT8_FMT" Pre filter Size : %d " "Post filter Size : %d Pre filter Write : %d Post filter Write : %ld " "Pre filter Total : "INT8_FMT" Post filter Total : "INT8_FMT"\n", recvpool_ctl->jnl_seqno, pre_filter_write_len, write_len, pre_filter_write, write_off, repl_recv_data_processed, repl_recv_postfltr_data_procd); } } recvpool_ctl->write_wrap = write_wrap; if (recvpool.gtmrecv_local->noresync) { /* With -NORESYNC, we could take the strm_seqno further back with multiple connects of the same receiver * server with different source servers at different seqnos. So avoid any confusion with asserts * that check for increasing seqnos using last_valid_histinfo and last_rcvd_histinfo. */ memset(&recvpool.recvpool_ctl->last_valid_histinfo, 0, SIZEOF(recvpool.recvpool_ctl->last_valid_histinfo)); memset(&recvpool.recvpool_ctl->last_rcvd_histinfo, 0, SIZEOF(recvpool.recvpool_ctl->last_rcvd_histinfo)); /* In the case of -NORESYNC, we are a root primary supplementary instance (not a propagating primary) * and therefore recvpool.recvpool_ctl->last_valid_strm_histinfo[] and * recvpool.recvpool_ctl->last_rcvd_strm_histinfo[] are unused and hence no need to reset them. */ assert(!remote_side->is_supplementary); /* assert that we are not a propagating primary supplementary */ assert(jnlpool->repl_inst_filehdr->is_supplementary && !jnlpool->jnlpool_ctl->upd_disabled); } if (is_new_histrec) { /* Note: The REPL_OLD_TRIPLE or REPL_HISTREC messages are endian converted by the source server * in case the receiver is running with a this_side->jnl_ver higher than the source. If not, the call to * function "repl_tr_endian_convert" (done already in the current function) will take care of * the endian conversion. So no more endian conversion needed at this point. */ if (REPL_OLD_TRIPLE == msg_type) { assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver); assert(REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver); assert(SIZEOF(old_triple_content) == write_len); memcpy((sm_uc_ptr_t)&old_triple_content, (recvpool.recvdata_base + write_off), SIZEOF(old_triple_content)); assert(JRT_TRIPLE == old_triple_content.jrec_type); assert(old_triple_content.forwptr == SIZEOF(old_triple_content)); assert(old_triple_content.start_seqno == recvpool_ctl->jnl_seqno); assert(old_triple_content.start_seqno >= recvpool.upd_proc_local->read_jnl_seqno); assert((old_triple_content.start_seqno > recvpool_ctl->last_valid_histinfo.start_seqno) || ((old_triple_content.start_seqno == recvpool_ctl->last_valid_histinfo.start_seqno) && gtm_white_box_test_case_enabled && (WBTEST_UPD_PROCESS_ERROR == gtm_white_box_test_case_number))); cur_histinfo = &tmp_histjrec.histcontent; memcpy(cur_histinfo->root_primary_instname, old_triple_content.instname, MAX_INSTNAME_LEN - 1); cur_histinfo->root_primary_instname[MAX_INSTNAME_LEN - 1] = '\0'; cur_histinfo->start_seqno = old_triple_content.start_seqno; cur_histinfo->strm_seqno = 0; cur_histinfo->root_primary_cycle = old_triple_content.cycle; cur_histinfo->creator_pid = 0; cur_histinfo->created_time = 0; /* No need to initialize the following fields as they are reinitialized later * when the history record gets added to the instance file (in "repl_inst_histinfo_add"). * cur_histinfo->histinfo_num = INVALID_HISTINFO_NUM; * cur_histinfo->prev_histinfo_num = INVALID_HISTINFO_NUM; * cur_histinfo->last_histinfo_num[] = INVALID_HISTINFO_NUM; */ cur_histinfo->strm_index = 0; cur_histinfo->history_type = HISTINFO_TYPE_NORMAL; NULL_INITIALIZE_REPL_INST_UUID(cur_histinfo->lms_group); tmp_histjrec.jrec_type = JRT_HISTREC; tmp_histjrec.forwptr = SIZEOF(repl_histrec_jnl_t); /* We now have to upgrade a REPL_OLD_TRIPLE format history record to a REPL_HISTREC record. * The latter is a much bigger record so make room for this in the receive pool. */ write_loc = write_off; /* reset "write_loc" */ write_len = tmp_histjrec.forwptr; PREPARE_RECVPOOL_FOR_WRITE(write_len, SIZEOF(old_triple_content)); /* above macro could update "recvpool_ctl->write" and "write_loc" */ COPY_TO_RECVPOOL((uchar_ptr_t)&tmp_histjrec, write_len);/* uses and updates "write_loc" */ write_off = recvpool_ctl->write; /* Copy relevant fields from received histinfo message to "last_rcvd_histinfo" if applicable */ cur_histinfo = &recvpool_ctl->last_rcvd_histinfo; assert(old_triple_content.start_seqno >= cur_histinfo->start_seqno); *cur_histinfo = tmp_histjrec.histcontent; assert(!remote_side->is_supplementary); /* so no need to maintain last_rcvd_strm_histinfo[] */ } else { assert(REPL_HISTREC == msg_type); assert(REPL_PROTO_VER_SUPPLEMENTARY <= remote_side->proto_ver); assert(SIZEOF(repl_histrec_jnl_t) == write_len); pool_histrec = (repl_histrec_jnl_t *)((sm_uc_ptr_t)recvpool.recvdata_base + write_off); assert(JRT_HISTREC == pool_histrec->jrec_type); assert(pool_histrec->forwptr == SIZEOF(repl_histrec_jnl_t)); pool_histinfo = &pool_histrec->histcontent; assert(pool_histinfo->start_seqno == recvpool_ctl->jnl_seqno); assert(pool_histinfo->start_seqno >= recvpool.upd_proc_local->read_jnl_seqno); if (jnlpool->repl_inst_filehdr->is_supplementary && !jnlpool->jnlpool_ctl->upd_disabled) { /* Modify the history record to reflect the stream # */ assert((0 <= recvpool.gtmrecv_local->strm_index) && (MAX_SUPPL_STRMS > recvpool.gtmrecv_local->strm_index)); assert(strm_index == recvpool.gtmrecv_local->strm_index); pool_histinfo->strm_index = recvpool.gtmrecv_local->strm_index; assert(0 == pool_histinfo->strm_seqno); assert(IS_REPL_INST_UUID_NON_NULL(recvpool.gtmrecv_local->remote_lms_group)); pool_histinfo->lms_group = recvpool.gtmrecv_local->remote_lms_group; if (recvpool.gtmrecv_local->updateresync) { assert(FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd); /* Make it known that this is an updateresync type history record */ pool_histinfo->history_type = HISTINFO_TYPE_UPDRESYNC; } /* We want to do a similar check for -noresync but we cannot use gtmrecv_local->noresync * since that is reset the moment a REPL_WILL_RESTART_WITH_INFO message is seen (much * before coming here). So use a static variable to determine if this is the first * history record that is being received by this receiver server and if -noresync was * specified in the command line (indicated by gtmrecv_options.noresync), set the * history_type of the incoming history record to reflect the noresync type. */ assert(!recvpool.gtmrecv_local->noresync); if (first_histrec) { if (gtmrecv_options.noresync) pool_histinfo->history_type = HISTINFO_TYPE_NORESYNC; first_histrec = FALSE; } } assert((INVALID_SUPPL_STRM != strm_index) || (0 == pool_histinfo->strm_index)); cur_histinfo = &recvpool_ctl->last_rcvd_histinfo; *cur_histinfo = *pool_histinfo; if ((INVALID_SUPPL_STRM == strm_index) || (strm_index == pool_histinfo->strm_index)) { assert((pool_histinfo->start_seqno > recvpool_ctl->last_valid_histinfo.start_seqno) || ((pool_histinfo->start_seqno == recvpool_ctl->last_valid_histinfo.start_seqno) && gtm_white_box_test_case_enabled && (WBTEST_UPD_PROCESS_ERROR == gtm_white_box_test_case_number))); assert(pool_histinfo->start_seqno >= cur_histinfo->start_seqno); } else { assert(!recvpool_ctl->insert_strm_histinfo); cur_histinfo = pool_histinfo; } } /* If supplementary instance with updates disabled, initialize last_rcvd_strm_histinfo as well */ if (remote_side->is_supplementary) { /* Check if "insert_strm_histinfo" is set. If so and if REPL_STRMINFO messages were * exchanged between the source and receiver, we need to potentially insert history * records for valid stream #s > 0. This is to ensure that even in case of a * -updateresync startup, we record knowledge of all streams that exist on the source side * (which we previously received as part of the same connection) on the receiver side as well. * Examples of when REPL_STRMINFO messages are not exchanged are if receiver side is at * jnl_seqno = 1, or if the receiver side is at the exact same jnl_seqno as the source side * and no prior history is available. */ if (recvpool_ctl->insert_strm_histinfo && (max_strm_histinfo = recvpool_ctl->max_strm_histinfo)) /* caution: assignment */ { /* Make room for the new records in the receive pool first. * At this point "cur_histinfo" holds the first received history record. * Add the other stream history records onto it. */ assert(0 == cur_histinfo->strm_index); /* Determine how many history records need to be added */ num_strm_histinfo = 0; /* Copy 0th stream history record into receive pool first */ rcvd_strm_histjrec[num_strm_histinfo].jrec_type = JRT_HISTREC; rcvd_strm_histjrec[num_strm_histinfo].forwptr = SIZEOF(repl_histrec_jnl_t); cur_histinfo->strm_seqno = jnlpool->jnlpool_ctl->strm_seqno[0]; rcvd_strm_histjrec[num_strm_histinfo++].histcontent = *cur_histinfo; /* Copy non-zero stream history records if they exist */ for (idx = 1; idx < max_strm_histinfo; idx++) { if (recvpool_ctl->is_valid_strm_histinfo[idx]) { DEBUG_ONLY(tmp_histinfo = recvpool_ctl->last_rcvd_strm_histinfo[idx]); assert(IS_REPL_INST_UUID_NON_NULL(tmp_histinfo.lms_group)); rcvd_strm_histjrec[num_strm_histinfo].jrec_type = JRT_HISTREC; rcvd_strm_histjrec[num_strm_histinfo].forwptr = SIZEOF(repl_histrec_jnl_t); rcvd_strm_histjrec[num_strm_histinfo].histcontent = recvpool_ctl->last_rcvd_strm_histinfo[idx]; /* Fix the "start_seqno" & "strm_seqno" fields of the history record */ rcvd_strm_histjrec[num_strm_histinfo].histcontent.start_seqno = cur_histinfo->start_seqno; /* Note that jnlpool_ctl->strm_seqno[idx] could be 0 if we have not yet * seen any updates in this stream. But since we do have a history record * for this stream and its strm_seqno cannot be 0 (has to be non-zero), * set it to 1 in that case. */ rcvd_strm_histjrec[num_strm_histinfo].histcontent.strm_seqno = jnlpool->jnlpool_ctl->strm_seqno[idx] ? jnlpool->jnlpool_ctl->strm_seqno[idx] : 1; num_strm_histinfo++; /* Do not reset recvpool_ctl->is_valid_strm_histinfo[idx] to FALSE * as this reflects reality and staying this way helps later * communication with the source in cases of reconnects/pipe-drains. */ } } write_loc = write_off; /* reset "write_loc" */ write_len = num_strm_histinfo * SIZEOF(repl_histrec_jnl_t); PREPARE_RECVPOOL_FOR_WRITE(write_len, 0); /* could update "recvpool_ctl->write" * and "write_loc" */ COPY_TO_RECVPOOL((uchar_ptr_t)rcvd_strm_histjrec, write_len); /* uses and updates "write_loc" */ write_off = recvpool_ctl->write; /* Note: "last_rcvd_strm_histinfo" is automatically maintained in this case */ /* Now that we have inserted the stream specific history records, reset flag */ recvpool_ctl->insert_strm_histinfo = FALSE; } else { /* Maintain last_rcvd_strm_histinfo[] as well. * In addition, if insert_strm_histinfo is TRUE, it means no REPL_STRMINFO messages * were exchanged at startup. In that case, no need of inserting stream specific * history information in the receive pool so reset variable to FALSE. */ if (recvpool_ctl->insert_strm_histinfo) recvpool_ctl->insert_strm_histinfo = FALSE; histinfo_strm_num = cur_histinfo->strm_index; assert((0 <= histinfo_strm_num) && (MAX_SUPPL_STRMS > histinfo_strm_num)); memcpy(&recvpool_ctl->last_rcvd_strm_histinfo[histinfo_strm_num], cur_histinfo, SIZEOF(repl_histinfo)); recvpool_ctl->is_valid_strm_histinfo[histinfo_strm_num] = TRUE; if (recvpool_ctl->max_strm_histinfo <= histinfo_strm_num) recvpool_ctl->max_strm_histinfo = histinfo_strm_num + 1; } } repl_dump_histinfo(gtmrecv_log_fp, TRUE, TRUE, "New History Content", cur_histinfo); } else { /* Note: In case of a propagating primary supplementary instance, the below if implies that we will * change last_rcvd_histinfo to last_valid_histinfo even if the logical update corresponded to a * different stream than 0 (the stream corresponding to the history of interest on this receiver). * But there should not be any issues due to this as it is an update nevertheless and is going to * bump up the recvpool_ctl->jnl_seqno to one more than last_rcvd_histinfo.start_seqno. */ if (recvpool_ctl->jnl_seqno == recvpool_ctl->last_rcvd_histinfo.start_seqno) { /* Move over stuff from "last_rcvd_histinfo" to "last_valid_histinfo" */ memcpy(&recvpool_ctl->last_valid_histinfo, &recvpool_ctl->last_rcvd_histinfo, SIZEOF(repl_histinfo)); if (remote_side->is_supplementary) { /* Propagating primary supplementary instance. Maintain last_valid_strm_histinfo too. */ max_strm_histinfo = recvpool_ctl->max_strm_histinfo; assert(max_strm_histinfo); for (idx = 0; idx < max_strm_histinfo; idx++) { if (recvpool_ctl->is_valid_strm_histinfo[idx]) { DEBUG_ONLY(tmp_histinfo = recvpool_ctl->last_rcvd_strm_histinfo[idx]); assert((0 == idx) || IS_REPL_INST_UUID_NON_NULL(tmp_histinfo.lms_group)); assert((0 != idx) || IS_REPL_INST_UUID_NULL(tmp_histinfo.lms_group)); memcpy(&recvpool_ctl->last_valid_strm_histinfo[idx], &recvpool_ctl->last_rcvd_strm_histinfo[idx], SIZEOF(repl_histinfo)); recvpool_ctl->is_valid_strm_histinfo[idx] = FALSE; } } recvpool_ctl->max_strm_histinfo = 0; /* Now that last_valid_strm_histinfo[0] is initialized, it is safe to reset the below */ if (recvpool_ctl->insert_strm_histinfo) recvpool_ctl->insert_strm_histinfo = FALSE; } /* Now that at least one history record has been written into the receive pool and is guaranteed * to be written to the instance file (when this gets processed by the update process), don't use * -updateresync or -noresync for future handshakes in case the current connection gets reset. */ if (recvpool.gtmrecv_local->updateresync) { recvpool.gtmrecv_local->updateresync = FALSE; /* Close fd of the input instance file name used for the -updateresync */ if (FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd) CLOSEFILE_RESET(recvpool.gtmrecv_local->updresync_instfile_fd, rc); } assert(!recvpool.gtmrecv_local->noresync); /* should have been reset already */ } QWINCRBYDW(recvpool_ctl->jnl_seqno, 1); assert(recvpool_ctl->last_valid_histinfo.start_seqno < recvpool_ctl->jnl_seqno); } /* The update process looks at "recvpool_ctl->write" first and then reads (a) "recvpool_ctl->write_wrap" * AND (b) all journal data in the receive pool upto this offset. It assumes that (a) and (b) will never * hold stale values corresponding to a previous state of "recvpool_ctl->write". In order for this * assumption to hold good, the receiver server needs to do a write memory barrier after updating the * receive pool data and "write_wrap" but before updating "write". The update process will do a read * memory barrier after reading "write" but before reading "write_wrap" or the receive pool data. Not * enforcing the read order will result in the update process attempting to read/process invalid data * from the receive pool (which could end up in db out of sync situation between primary and secondary). */ SHM_WRITE_MEMORY_BARRIER; recvpool_ctl->write = write_loc; /* Signal the update process to check for the update. */ PTHREAD_COND_SIGNAL(&recvpool_ctl->write_updated, status); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_cond_signal"), CALLFROM, status, 0); } while (is_repl_cmpc); return; } /* Retrieve the history record corresponding to "input_seqno" in the instance file used with the -UPDATERESYNC qualifier. * Do special processing in case of P->Q type of connection (where primary and secondary are both supplementary instances) * which is to search only in the 0th stream. * * Note: This function is similar to "repl_inst_histinfo_find_seqno" except that this operates on the input instance file * provided with the -updateresync qualifier. Two reasons why we need this code duplication is * a) The "repl_inst_histinfo_find_seqno" function currently is coded to use only the replication instance file. * b) The input instance file for -updateresync could be cross endian. * If "repl_inst_histinfo_find_seqno" is enhanced to fix these limitations, then we can avoid this code duplication. */ STATICFNDEF void gtmrecv_updresync_histinfo_find_seqno(seq_num input_seqno, int4 strm_num, repl_histinfo *histinfo) { char print_msg[1024]; int fd, status; int4 histinfo_num; off_t offset; fd = recvpool.gtmrecv_local->updresync_instfile_fd; assert(FD_INVALID != fd); /* If remote side is non-supplementary then its instance file (which is given as input to the -updateresync * command) knows only strem 0, so reset strm_num to 0 unconditionally. */ if (!remote_side->is_supplementary) strm_num = 0; if (INVALID_SUPPL_STRM == strm_num) histinfo_num = recvpool.gtmrecv_local->updresync_num_histinfo; else histinfo_num = recvpool.gtmrecv_local->updresync_num_histinfo_strm[strm_num]; if (INVALID_HISTINFO_NUM == histinfo_num) { /* The instance file cannot be used for updateresync if it has NO history records. */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_UPDSYNCINSTFILE, 0, ERR_STRMNUMIS, 1, strm_num, ERR_TEXT, 2, LEN_AND_LIT("Input instance file has NO history records")); } assert(0 <= histinfo_num); if (!recvpool.gtmrecv_local->updresync_jnl_seqno) { /* The instance file cannot be used for updateresync if it has a ZERO seqno */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_UPDSYNCINSTFILE, 0, ERR_STRMNUMIS, 1, strm_num, ERR_TEXT, 2, LEN_AND_LIT("Input instance file has jnl_seqno of 0")); } if (input_seqno > recvpool.gtmrecv_local->updresync_jnl_seqno) { /* Input seqno is greater than the max seqno in the updateresync input instance file. So can never be found. */ SNPRINTF(print_msg, SIZEOF(print_msg), "Seqno "INT8_FMT" "INT8_FMTX" cannot be found in input instance file " " which has a max seqno of "INT8_FMT" "INT8_FMTX"\n", input_seqno, input_seqno, recvpool.gtmrecv_local->updresync_jnl_seqno, recvpool.gtmrecv_local->updresync_jnl_seqno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_UPDSYNCINSTFILE, 0, ERR_STRMNUMIS, 1, strm_num, ERR_TEXT, 2, LEN_AND_STR(print_msg)); } histinfo->start_seqno = 0; do { offset = REPL_INST_HISTINFO_START + ((histinfo_num) * SIZEOF(repl_histinfo)); LSEEKREAD(fd, offset, histinfo, SIZEOF(repl_histinfo), status); if (0 != status) { /* At this point, we don't have the name of the input instance file used in the -updateresync qualifier. * So we use a value of "" instead. The fact that the REPLINSTREAD message is preceded by a UPDSYNCINSTFILE * error indicates to the user it is the -updateresync qualifier where the issue is so it is not a big loss. */ if (-1 == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(15) ERR_UPDSYNCINSTFILE, 0, ERR_STRMNUMIS, 1, strm_num, ERR_TEXT, 2, LEN_AND_LIT("Error reading history record"), ERR_REPLINSTREAD, 4, SIZEOF(repl_histinfo), (qw_off_t *)&offset, LEN_AND_LIT("")); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(16) ERR_UPDSYNCINSTFILE, 0, ERR_STRMNUMIS, 1, strm_num, ERR_TEXT, 2, LEN_AND_LIT("Error reading history record"), ERR_REPLINSTREAD, 4, SIZEOF(repl_histinfo), (qw_off_t *)&offset, LEN_AND_LIT(""), status); } if (recvpool.gtmrecv_local->updresync_cross_endian) ENDIAN_CONVERT_REPL_HISTINFO(histinfo); if (input_seqno > histinfo->start_seqno) return; /* found history record corresponding to input_seqno. return right away */ histinfo_num = (INVALID_SUPPL_STRM == strm_num) ? (histinfo_num - 1) : histinfo->prev_histinfo_num; } while (INVALID_HISTINFO_NUM != histinfo_num); /* Could not find history record in -updateresync= input instance file */ SNPRINTF(print_msg, SIZEOF(print_msg), "Receiver side instance seqno "INT8_FMT" "INT8_FMTX" is less than" " any history record found in instance file", input_seqno, input_seqno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_UPDSYNCINSTFILE, 0, ERR_STRMNUMIS, 1, strm_num, ERR_TEXT, 2, LEN_AND_STR(print_msg)); } /* Retrieve the "index"'th history record in the instance file specified using the -UPDATERESYNC qualifier. * * This function is similar to "repl_inst_histinfo_get" except that this operates on the input instance file provided * with the -updateresync qualifier. Two reasons why we need this code duplication is * a) The "repl_inst_histinfo_get" function currently is coded to use only the replication instance file. * b) The input instance file for -updateresync could be cross endian. * If "repl_inst_histinfo_get" is enhanced to fix these limitations, then we can avoid this code duplication. */ STATICFNDEF void gtmrecv_updresync_histinfo_get(int4 index, repl_histinfo *histinfo) { int fd, status; off_t offset; fd = recvpool.gtmrecv_local->updresync_instfile_fd; assert(FD_INVALID != fd); assert(0 <= index); offset = REPL_INST_HISTINFO_START + ((index) * SIZEOF(repl_histinfo)); LSEEKREAD(fd, offset, histinfo, SIZEOF(repl_histinfo), status); if (0 != status) { /* At this point, we don't have the name of the input instance file used in the -updateresync qualifier. * So we use a value of "" instead. The fact that the REPLINSTREAD message is preceded by a UPDSYNCINSTFILE * error indicates to the user it is the -updateresync qualifier where the issue is so it is not a big loss. */ if (-1 == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(12) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2, LEN_AND_LIT("Error reading history record"), ERR_REPLINSTREAD, 4, SIZEOF(repl_histinfo), (qw_off_t *)&offset, LEN_AND_LIT("")); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(13) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2, LEN_AND_LIT("Error reading history record"), ERR_REPLINSTREAD, 4, SIZEOF(repl_histinfo), (qw_off_t *)&offset, LEN_AND_LIT(""), status); } if (recvpool.gtmrecv_local->updresync_cross_endian) ENDIAN_CONVERT_REPL_HISTINFO(histinfo); } /* This function is invoked on receipt of a REPL_NEED_STRMINFO message. In case of an error, it returns if either * repl_connection_reset OR gtmrecv_wait_for_jnl_seqno are set so the caller should check for this and return as well. */ STATICFNDEF void gtmrecv_process_need_strminfo_msg(repl_needstrminfo_msg_ptr_t need_strminfo_msg) { int4 status; int idx; repl_histinfo histinfo, *previously_rcvd_histinfo; repl_strminfo_msg_t strminfo_msg; seq_num need_strminfo_seqno, last_valid_histinfo_seqno; assert(remote_side->is_supplementary); /* STRMINFO messages are sent only in case source and receiver are supplementary */ assert(0 == strm_index); assert(remote_side->endianness_known); /* ensure remote_side->cross_endian is reliable */ if (!remote_side->cross_endian) need_strminfo_seqno = need_strminfo_msg->seqno; else need_strminfo_seqno = GTM_BYTESWAP_64(need_strminfo_msg->seqno); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_NEED_STRMINFO message for seqno "INT8_FMT" "INT8_FMTX"\n", need_strminfo_seqno, need_strminfo_seqno); if (recvpool.gtmrecv_local->updateresync && (FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd)) { /* The stream information that is being requested needs to be found in the -updateresync input instance file * (not the receiver side instance file). */ repl_log(gtmrecv_log_fp, TRUE, TRUE, "Searching for desired stream info in -updateresync input instance file\n"); gtmrecv_updresync_histinfo_find_seqno(need_strminfo_seqno, INVALID_SUPPL_STRM, &histinfo); for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) strminfo_msg.last_histinfo_num[idx] = histinfo.last_histinfo_num[idx]; assert(recvpool.recvpool_ctl->insert_strm_histinfo); } else { /* The history record needs to be found in the receiver side instance file or in the receive pool. * If last_valid_strm_histinfo[0] has non-default content, then because this is a supplementary instance * and a propagating primary, we can rest assured that last_valid_strm_histinfo[1] thru [15] reflect * the latest history records for each stream if the stream exists on this instance. And so no need * to go to the instance file at all. If [0] does not have any content, then it means the cached history * is empty for not just the 0th stream but for every other stream as well i.e. the receive pool is empty * and the receiver is connecting with a source for the first time. So go to the instance file in that case. */ last_valid_histinfo_seqno = recvpool.recvpool_ctl->last_valid_strm_histinfo[0].start_seqno; if (last_valid_histinfo_seqno) { assert(need_strminfo_seqno > last_valid_histinfo_seqno); assert(!recvpool.gtmrecv_local->updateresync); assert(!recvpool.gtmrecv_local->noresync); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Searching for the desired history in the receive pool\n"); for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) { previously_rcvd_histinfo = &recvpool.recvpool_ctl->last_valid_strm_histinfo[idx]; assert((0 != idx) || IS_REPL_INST_UUID_NULL(previously_rcvd_histinfo->lms_group)); if ((0 == idx) || IS_REPL_INST_UUID_NON_NULL(previously_rcvd_histinfo->lms_group)) strminfo_msg.last_histinfo_num[idx] = UNKNOWN_HISTINFO_NUM; else { assert(0 != idx); strminfo_msg.last_histinfo_num[idx] = INVALID_HISTINFO_NUM; } } assert(!recvpool.recvpool_ctl->insert_strm_histinfo); histinfo.strm_index = 0; /* so "0 < histinfo.strm_index" if block is skipped below for this case */ } else { repl_log(gtmrecv_log_fp, TRUE, TRUE, "Searching for the desired history in the replication instance file\n"); grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY); GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED; /* above macro will "return" if repl_connection_reset OR gtmrecv_wait_for_jnl_seqno is set */ status = repl_inst_wrapper_histinfo_find_seqno(need_strminfo_seqno, INVALID_SUPPL_STRM, &histinfo); rel_lock(jnlpool->jnlpool_dummy_reg); if (0 != status) { /* Close the connection */ assert(ERR_REPLINSTNOHIST == status); assert(FALSE); gtmrecv_autoshutdown(); /* should not return */ } for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) strminfo_msg.last_histinfo_num[idx] = histinfo.last_histinfo_num[idx]; } } if (0 < histinfo.strm_index) { assert(histinfo.last_histinfo_num[histinfo.strm_index] < histinfo.histinfo_num); strminfo_msg.last_histinfo_num[histinfo.strm_index] = histinfo.histinfo_num; } if (remote_side->cross_endian) { for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) { assert(4 == SIZEOF(strminfo_msg.last_histinfo_num[idx])); /* so GTM_BYTESWAP_32 can be used below */ strminfo_msg.last_histinfo_num[idx] = GTM_BYTESWAP_32(strminfo_msg.last_histinfo_num[idx]); } } gtmrecv_repl_send((repl_msg_ptr_t)&strminfo_msg, REPL_STRMINFO, SIZEOF(repl_strminfo_msg_t), "REPL_STRMINFO", MAX_SEQNO); return; } /* This function is invoked on receipt of a REPL_NEED_HISTINFO message. In case of an error, it returns if either * repl_connection_reset OR gtmrecv_wait_for_jnl_seqno are set so the caller should check for this and return as well. */ STATICFNDEF void gtmrecv_process_need_histinfo_msg(repl_needhistinfo_msg_ptr_t need_histinfo_msg, repl_histinfo *histinfo) { boolean_t maintain_rcvd_strm_histinfo, suppl_propagate_primary; int4 need_histinfo_num, need_histinfo_strm_num, status; recvpool_ctl_ptr_t recvpool_ctl; seq_num first_unprocessed_seqno, last_unprocessed_histinfo_seqno; seq_num need_histinfo_seqno, last_valid_histinfo_seqno; assert(remote_side->endianness_known); /* ensure remote_side->cross_endian is reliable */ if (!remote_side->cross_endian) { need_histinfo_seqno = need_histinfo_msg->seqno; need_histinfo_num = need_histinfo_msg->histinfo_num; need_histinfo_strm_num = need_histinfo_msg->strm_num; } else { need_histinfo_seqno = GTM_BYTESWAP_64(need_histinfo_msg->seqno); need_histinfo_num = GTM_BYTESWAP_32(need_histinfo_msg->histinfo_num); need_histinfo_strm_num = GTM_BYTESWAP_32(need_histinfo_msg->strm_num); } assert((INVALID_SUPPL_STRM == strm_index) || ((0 <= strm_index) && (MAX_SUPPL_STRMS > strm_index))); if (INVALID_SUPPL_STRM == strm_index) { /* Both receiver and source sides are non-supplementary instances */ if (REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver) { /* needhistinfo_msg.strm_num & histinfo_num are uninitialized in this case. Fix it */ need_histinfo_strm_num = INVALID_SUPPL_STRM; need_histinfo_num = INVALID_HISTINFO_NUM; } else { assert(INVALID_SUPPL_STRM == need_histinfo_strm_num); assert(INVALID_HISTINFO_NUM == need_histinfo_num); } repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_NEED_HISTINFO message for seqno "INT8_FMT" "INT8_FMTX"\n", need_histinfo_seqno, need_histinfo_seqno); } else if (0 < strm_index) { /* Receiver side is supplementary but Source side is a non-supplementary instance */ assert(INVALID_SUPPL_STRM == need_histinfo_strm_num); assert(INVALID_HISTINFO_NUM == need_histinfo_num); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_NEED_HISTINFO message for Stream %d : Seqno " INT8_FMT" "INT8_FMTX"\n", strm_index, need_histinfo_seqno, need_histinfo_seqno); need_histinfo_strm_num = strm_index; } else { /* Both receiver and source sides are supplementary instances */ /* strm_index is 0 at this point (already asserted above) */ assert(INVALID_SUPPL_STRM != need_histinfo_strm_num); if ((INVALID_HISTINFO_NUM == need_histinfo_num) || (UNKNOWN_HISTINFO_NUM == need_histinfo_num)) repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_NEED_HISTINFO message for Stream %d : " "Seqno "INT8_FMT" "INT8_FMTX"\n", need_histinfo_strm_num, need_histinfo_seqno, need_histinfo_seqno); else { assert(0 <= need_histinfo_num); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_NEED_HISTINFO message for History Number %d\n", need_histinfo_num); } } /* The only two histinfo_num values that have special meaning are negative. So we can check for a valid value * by checking for positive. Assert that below before doing the positive check. */ assert((0 > INVALID_HISTINFO_NUM) && (0 > UNKNOWN_HISTINFO_NUM)); recvpool_ctl = recvpool.recvpool_ctl; if (0 <= need_histinfo_num) { /* Handle simplest case first. Get the "need_histinfo_num"'th history record directly from the instance file */ if (recvpool.gtmrecv_local->updateresync && (FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd)) { /* The history record that is being requested needs to be found in the -updateresync input instance file * (not the receiver side instance file). */ repl_log(gtmrecv_log_fp, TRUE, TRUE, "Searching for desired history in the -updateresync input instance file\n"); gtmrecv_updresync_histinfo_get(need_histinfo_num, histinfo); } else { /* The history record needs to be found in the receiver side instance file */ repl_log(gtmrecv_log_fp, TRUE, TRUE, "Searching for the desired history in the replication instance file\n"); grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY); GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED; /* above macro will "return" if repl_connection_reset OR gtmrecv_wait_for_jnl_seqno is set */ status = repl_inst_histinfo_get(need_histinfo_num, histinfo); rel_lock(jnlpool->jnlpool_dummy_reg); if (0 != status) { /* Close the connection */ assert(ERR_REPLINSTNOHIST == status); assert(FALSE); gtmrecv_autoshutdown(); /* should not return */ } } maintain_rcvd_strm_histinfo = TRUE; } else if (UNKNOWN_HISTINFO_NUM == need_histinfo_num) { /* This value was sent in a previous REPL_STRMINFO message for a particular non-zero stream # because * we had not yet played this history record in the instance file. Return history record directly from * where the receiver server saved a copy of the last unprocessed history record for this stream number. */ assert(need_histinfo_strm_num); assert(remote_side->is_supplementary); assert((0 < need_histinfo_strm_num) && (MAX_SUPPL_STRMS > need_histinfo_strm_num)); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Searching for desired history in the receive pool\n"); *histinfo = recvpool_ctl->last_valid_strm_histinfo[need_histinfo_strm_num]; maintain_rcvd_strm_histinfo = FALSE; } else { first_unprocessed_seqno = recvpool.upd_proc_local->read_jnl_seqno; repl_log(gtmrecv_log_fp, TRUE, FALSE, "Update process has processed upto seqno "INT8_FMT" "INT8_FMTX"\n", first_unprocessed_seqno, first_unprocessed_seqno); suppl_propagate_primary = remote_side->is_supplementary; if (!suppl_propagate_primary) last_valid_histinfo_seqno = recvpool_ctl->last_valid_histinfo.start_seqno; else last_valid_histinfo_seqno = recvpool_ctl->last_valid_strm_histinfo[0].start_seqno; repl_log(gtmrecv_log_fp, TRUE, TRUE, "Starting seqno of the last valid history in the receive pool is "INT8_FMT" "INT8_FMTX"\n", last_valid_histinfo_seqno, last_valid_histinfo_seqno); if (last_valid_histinfo_seqno >= first_unprocessed_seqno) last_unprocessed_histinfo_seqno = last_valid_histinfo_seqno; else last_unprocessed_histinfo_seqno = MAX_SEQNO; if (last_unprocessed_histinfo_seqno && (need_histinfo_seqno > last_unprocessed_histinfo_seqno)) { /* NOTE0: The source server is requesting histinfo information for a seqno whose corresponding * histinfo has also not yet been processed by the update process (and hence not present in the * instance file). Find latest histinfo information that is stored in receive pool. * NOTE1: Even though there could be more than one unprocessed history record in the receive pool, * the source should request only the last one for comparison. If the last history record on the * receiver matches on the source side too, replication can resume from there. If not, the receiver * side should do a rollback (REPL_ROLLBACK_FIRST message). That is why it is enough to maintain * recvpool_ctl->last_valid_histinfo and not pointers to all the unprocessed histories. * NOTE2: There is one exception to this and that is if the receiver server had already connected * to a source server and placed records in the receiver pool and then lost the connection and * reestablished connection (with the same or a different source server) AND had been started with * the -noresync option. In this case, it will not see a REPL_ROLLBACK_FIRST message but instead * will go back in history to find the first matching history. But in this case, the -noresync is * valid only for the FIRST connection and is cleared for future connections. So reconnections would * assume as if -noresync was not specified and hence will fall into the same REPL_ROLLBACK_FIRST * category as described in NOTE0. Assert it below. * NOTE3: In the case of a propagating primary supplementary instance, we need to not just store the * last received history record but also one for each stream possible. */ assert(!recvpool.gtmrecv_local->updateresync); assert(!recvpool.gtmrecv_local->noresync); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Searching for the desired history in the receive pool\n"); if (!suppl_propagate_primary) memcpy(histinfo, &recvpool_ctl->last_valid_histinfo, SIZEOF(repl_histinfo)); else { assert(!need_histinfo_strm_num); memcpy(histinfo, &recvpool_ctl->last_valid_strm_histinfo[0], SIZEOF(repl_histinfo)); } assert(!recvpool_ctl->insert_strm_histinfo); } else if (recvpool.gtmrecv_local->updateresync && (FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd)) { /* The receiver was started with -UPDATERESYNC=. The history record that is being requested * needs to be found in the input instance file (not the receiver side instance file). * Read the history record corresponding to need_histinfo_seqno. */ repl_log(gtmrecv_log_fp, TRUE, TRUE, "Searching for desired history in the -updateresync input instance file\n"); gtmrecv_updresync_histinfo_find_seqno(need_histinfo_seqno, need_histinfo_strm_num, histinfo); assert(histinfo->start_seqno); } else { /* The seqno has been processed by the update process. Hence the histinfo * for this will be found in the instance file. Search there. */ assert(NULL != jnlpool->jnlpool_dummy_reg); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Searching for the desired history in the replication instance file\n"); grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY); GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED; status = repl_inst_wrapper_histinfo_find_seqno(need_histinfo_seqno, need_histinfo_strm_num, histinfo); rel_lock(jnlpool->jnlpool_dummy_reg); if (0 != status) { /* Close the connection */ assert(ERR_REPLINSTNOHIST == status); gtmrecv_autoshutdown(); /* should not return */ } assert((histinfo->histinfo_num != (jnlpool->repl_inst_filehdr->num_histinfo - 1)) || (histinfo->start_seqno == jnlpool->jnlpool_ctl->last_histinfo_seqno)); if (0 < need_histinfo_strm_num) { /* About to send to a non-supplementary instance. It does not understand strm_seqnos. * So convert it back to a format it understands. */ CONVERT_SUPPL2NONSUPPL_HISTINFO(*histinfo); } assert(histinfo->start_seqno); assert(histinfo->start_seqno < need_histinfo_seqno); } maintain_rcvd_strm_histinfo = (0 == histinfo->strm_index); } if (maintain_rcvd_strm_histinfo && recvpool_ctl->insert_strm_histinfo) { assert(remote_side->is_supplementary); need_histinfo_strm_num = histinfo->strm_index; assert((0 <= need_histinfo_strm_num) && (MAX_SUPPL_STRMS > need_histinfo_strm_num)); assert(IS_REPL_INST_UUID_NULL(recvpool_ctl->last_rcvd_strm_histinfo[need_histinfo_strm_num].lms_group)); assert((FALSE == recvpool_ctl->is_valid_strm_histinfo[need_histinfo_strm_num]) || !memcmp(&recvpool_ctl->last_rcvd_strm_histinfo[need_histinfo_strm_num], histinfo, SIZEOF(repl_histinfo))); memcpy(&recvpool_ctl->last_rcvd_strm_histinfo[need_histinfo_strm_num], histinfo, SIZEOF(repl_histinfo)); recvpool_ctl->is_valid_strm_histinfo[need_histinfo_strm_num] = TRUE; if (recvpool_ctl->max_strm_histinfo <= need_histinfo_strm_num) recvpool_ctl->max_strm_histinfo = need_histinfo_strm_num + 1; } return; } #ifdef GTM_TLS /* The below logic is very similar to `gtmsource_exchange_tls_info' but is kept separate because of the following reasons: * (a) `gtmrecv_poll_actions' needs to be invoked as opposed to `gtmsource_poll_actions'. * (b) The arguments passed to `gtmrecv_poll_actions' are defined as static. * (c) The action taken after `gtmsource_poll_actions' and `gtmrecv_poll_actions' are different. */ STATICFNDEF boolean_t gtmrecv_exchange_tls_info(void) { int poll_dir, status; char *errp; repl_tlsinfo_msg_t reply; reply.type = REPL_TLS_INFO; reply.len = MIN_REPL_MSGLEN; reply.API_version = GTM_TLS_API_VERSION; reply.library_version = (uint4)tls_ctx->runtime_version; if (remote_side->cross_endian) { reply.API_version = GTM_BYTESWAP_32(reply.API_version); reply.library_version = GTM_BYTESWAP_32(reply.library_version); } gtmrecv_repl_send((repl_msg_ptr_t)&reply, REPL_TLS_INFO, SIZEOF(repl_tlsinfo_msg_t), "REPL_TLS_INFO", MAX_SEQNO); if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) return FALSE; /* At this point, the both sides are ready for a TLS/SSL handshake. Create a TLS/SSL aware socket. */ if (NULL == (repl_tls.sock = gtm_tls_socket(tls_ctx, repl_tls.sock, gtmrecv_sock_fd, repl_tls.id, GTMTLS_OP_VERIFY_PEER | GTMTLS_OP_RENEGOTIATE_REQUESTED))) { if (!PLAINTEXT_FALLBACK) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TLSCONVSOCK, 0, ERR_TEXT, 2, LEN_AND_STR(gtm_tls_get_error(NULL))); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) MAKE_MSG_WARNING(ERR_TLSCONVSOCK), 0, ERR_TEXT, 2, LEN_AND_STR(gtm_tls_get_error(NULL))); } else { /* Do the actual handshake. */ poll_dir = REPL_INVALID_POLL_DIRECTION; do { status = repl_do_tls_handshake(gtmrecv_log_fp, gtmrecv_sock_fd, TRUE, &poll_dir); assert(0 == data_len); gtmrecv_poll_actions(data_len, buff_unprocessed, buffp); if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) return FALSE; } while ((GTMTLS_WANT_READ == status) || (GTMTLS_WANT_WRITE == status)); if (SS_NORMAL == status) return TRUE; else if (REPL_CONN_RESET(status)) { repl_log(gtmrecv_log_fp, TRUE, TRUE, "Attempt to connect() with TLS/SSL protocol failed. " "Status = %d; %s\n", status, STRERROR(status)); repl_close(>mrecv_sock_fd); repl_connection_reset = TRUE; return FALSE; } errp = (-1 == status) ? (char *)gtm_tls_get_error(NULL) : STRERROR(status); if (!PLAINTEXT_FALLBACK) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TLSHANDSHAKE, 0, ERR_TEXT, 2, LEN_AND_STR(errp)); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) MAKE_MSG_WARNING(ERR_TLSHANDSHAKE), 0, ERR_TEXT, 2, LEN_AND_STR(errp)); } repl_log(gtmrecv_log_fp, TRUE, TRUE, "Plaintext fallback enabled. Closing and reconnecting without TLS/SSL.\n"); repl_close(>mrecv_sock_fd); repl_connection_reset = TRUE; CLEAR_REPL_TLS_REQUESTED; /* As if -tlsid qualifier was never specified. */ return FALSE; } #endif STATICFNDEF void do_main_loop(boolean_t crash_restart) { /* The work-horse of the Receiver Server */ boolean_t dont_reply_to_heartbeat = FALSE, is_repl_cmpc; boolean_t uncmpfail, send_cross_endian, recvpool_prepared, copied_to_recvpool; gtmrecv_local_ptr_t gtmrecv_local; gtm_time4_t ack_time; char print_msg[PROC_RECVOPS_PRINT_MSG_LEN]; char print_msg_t[PROC_RECVOPS_PRINT_MSG_LEN]; int4 msghdrlen, strm_num, processed_hdrlen; int4 need_histinfo_num; int cmpret; int msg_type, msg_len, hdr_msg_type, hdr_msg_len; int torecv_len, recvd_len, recvd_this_iter; /* needed for REPL_RECV_LOOP */ int tosend_len, sent_len, sent_this_iter; /* needed for REPL_SEND_LOOP */ int status, poll_dir; /* needed for REPL_{SEND,RECV}_LOOP */ recvpool_ctl_ptr_t recvpool_ctl; repl_cmpinfo_msg_ptr_t cmptest_msg; repl_cmpinfo_msg_t cmpsolve_msg; repl_cmpmsg_ptr_t cmpmsgp; repl_heartbeat_msg_t heartbeat; repl_histinfo histinfo; repl_needhistinfo_msg_ptr_t need_histinfo_msg; repl_needinst_msg_ptr_t need_instinfo_msg; repl_needstrminfo_msg_ptr_t need_strminfo_msg; repl_old_instinfo_msg_t old_instinfo_msg; repl_old_needinst_msg_ptr_t old_need_instinfo_msg; repl_start_msg_ptr_t msgp; repl_start_reply_msg_t *start_msg; seq_num ack_seqno, temp_ack_seqno; seq_num request_from, recvd_jnl_seqno; sgmnt_addrs *repl_csa; uchar_ptr_t old_buffp, buffp_start; uint4 recvd_start_flags, len; uLong cmplen; uLongf destlen; unsigned char *msg_ptr; /* needed for REPL_{SEND,RECV}_LOOP */ unsigned char remote_jnl_ver; upd_proc_local_ptr_t upd_proc_local; repl_logfile_info_msg_t *logfile_msgp, logfile_msg; # ifdef GTM_TLS repl_msg_t renegotiate_msg; boolean_t reneg_ack_sent; repl_tlsinfo_msg_t *need_tlsinfo_msgp; uint4 remote_lib_ver, remote_API_ver; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; recvpool_ctl = recvpool.recvpool_ctl; upd_proc_local = recvpool.upd_proc_local; gtmrecv_local = recvpool.gtmrecv_local; gtmrecv_wait_for_jnl_seqno = FALSE; assert((NULL != jnlpool->jnlpool_dummy_reg) && jnlpool->jnlpool_dummy_reg->open); repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; # ifdef DEBUG assert(!repl_csa->hold_onto_crit); ASSERT_VALID_JNLPOOL(repl_csa); # endif /* If BAD_TRANS was written by the update process, it would have updated recvpool_ctl->jnl_seqno accordingly. * Only otherwise, do we need to wait for it to write "recvpool_ctl->jnl_seqno". */ if (!gtmrecv_bad_trans_sent) { repl_log(gtmrecv_log_fp, TRUE, TRUE, "Waiting for Update Process to write jnl_seqno\n"); while (QWEQ(recvpool_ctl->jnl_seqno, seq_num_zero)) { SHORT_SLEEP(GTMRECV_WAIT_FOR_STARTJNLSEQNO); GTMRECV_POLL_ACTIONS(0, 0, NULL); } /* The call to "gtmrecv_poll_actions" above might have set the variable "gtmrecv_wait_for_jnl_seqno" to TRUE. * In that case, we need to reset it to FALSE here as we are now going to wait for the jnl_seqno below. * Not doing so will cause us to wait for jnl_seqno TWICE (once now and once when we later enter this function). */ gtmrecv_wait_for_jnl_seqno = FALSE; /* Since remote primary is multisite capable (otherwise we would have issued an error), we need to send the * journal seqno of this instance for comparison. If the receiver has received more seqnos than have been * processed by the update process, we should be sending the last received seqno across to avoid receiving * duplicate and out-of-order seqnos. This is maintained in "recvpool_ctl->jnl_seqno" and is guaranteed to * be greater than or equal to the journal seqno of this instance. */ request_from = recvpool_ctl->jnl_seqno; /* If this is the first time the update process initialized "recvpool_ctl->jnl_seqno", it should be * equal to "jnlpool_ctl->jnl_seqno". But if the receiver had already connected and received a bunch * of seqnos and if the update process did not process all of them and if the receiver disconnects * and re-establishes the connection, the value of "recvpool_ctl->jnl_seqno" could be greater than * "jnlpool_ctl->jnl_seqno" if there is non-zero backlog on the secondary. Assert accordingly. * There is one exception to this and that is if this is a root primary supplementary instance. * In that case, the receive pool talks about the non-supplementary instance stream jnl_seqnos whereas * the jnlpool talks about the merged stream of jnl_seqnos (including any local updates). Therefore we * cannot compare the two at all. */ assert((!jnlpool->jnlpool_ctl->upd_disabled && jnlpool->repl_inst_filehdr->is_supplementary) || (recvpool_ctl->jnl_seqno >= jnlpool->jnlpool_ctl->jnl_seqno)); assert(request_from); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Requesting transactions from JNL_SEQNO "INT8_FMT" "INT8_FMTX"\n", request_from, request_from); /* Send (re)start JNL_SEQNO to Source Server. * Note that even though we might know the endianness of the source side at this time, we still send this * message in the native endian format. This keeps the logic on the source server side simple. This is the only * exception to the general rule that the receiver server does all endian conversion for messages except the * first one in the connection handshake. The source side knows to endian convert this (instead of expecting it * to be in the source endian format) if this EPL_START_JNL_SEQNO message is not the first one in the connection. * The only exception is if the source side is pre-V55000 AND we have already determined the endianness of the * source side (i.e. the REPL_START_JNL_SEQNO message about to be sent is not the first one for this connection) * AND the source is cross-endian. In that case, the pre-V55000 source does not know to handle a * receiver-side-native-endian format message. So endian convert the message only in this case. */ repl_log(gtmrecv_log_fp, TRUE, TRUE, "Sending REPL_START_JNL_SEQNO message with seqno "INT8_FMT" "INT8_FMTX"\n", request_from, request_from); send_cross_endian = (remote_side->endianness_known && remote_side->cross_endian && (REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver)); msgp = (repl_start_msg_ptr_t)gtmrecv_msgp; memset(msgp, 0, SIZEOF(*msgp)); /* Since REPL_START_JNL_SEQNO is 0, there is no endian conversion necessary but for completeness we do it */ msgp->type = send_cross_endian ? GTM_BYTESWAP_32(REPL_START_JNL_SEQNO) : REPL_START_JNL_SEQNO; if (send_cross_endian) *((seq_num *)msgp->start_seqno) = GTM_BYTESWAP_64(request_from); else *((seq_num *)msgp->start_seqno) = request_from; msgp->start_flags = START_FLAG_NONE; msgp->start_flags |= (gtmrecv_options.stopsourcefilter ? START_FLAG_STOPSRCFILTER : 0); /* If -UPDATERESYNC is specified then let pre-V55000 source server know so it does not ask for history record * exchange. In case of post-V55000 source server, we expect a value to the -updateresync qualifier (the instance * file name) and that is used for the history record exchange. In that case the source server ignores the * START_FLAG_UPDATERESYNC bit and requests a history exchange anyways. */ msgp->start_flags |= (gtmrecv_local->updateresync ? START_FLAG_UPDATERESYNC : 0); /* Let source server know if -NORESYNC was specified in receiver server startup */ msgp->start_flags |= (gtmrecv_local->noresync ? START_FLAG_NORESYNC : 0); msgp->start_flags |= START_FLAG_HASINFO; if (this_side->is_std_null_coll) msgp->start_flags |= START_FLAG_COLL_M; msgp->start_flags |= START_FLAG_VERSION_INFO; GTMTRIG_ONLY(msgp->start_flags |= START_FLAG_TRIGGER_SUPPORT;) # ifdef GTM_TLS if (REPL_TLS_REQUESTED) { assert(NULL != tls_ctx); /* gtm_tls_init() must have already happened. */ msgp->start_flags |= START_FLAG_ENABLE_TLS; } # endif if (send_cross_endian) msgp->start_flags = GTM_BYTESWAP_32(msgp->start_flags); msgp->jnl_ver = this_side->jnl_ver; msgp->proto_ver = REPL_PROTO_VER_THIS; msgp->node_endianness = NODE_ENDIANNESS; msgp->is_supplementary = jnlpool->repl_inst_filehdr->is_supplementary; msgp->len = send_cross_endian ? GTM_BYTESWAP_32(MIN_REPL_MSGLEN) : MIN_REPL_MSGLEN; msg_len = MIN_REPL_MSGLEN; REPL_SEND_LOOP(gtmrecv_sock_fd, msgp, msg_len, REPL_POLL_NOWAIT) { GTMRECV_POLL_ACTIONS(0, 0, NULL); } CHECK_REPL_SEND_LOOP_ERROR(status, "REPL_START_JNL_SEQNO"); } gtmrecv_bad_trans_sent = FALSE; request_from = recvpool_ctl->jnl_seqno; assert(request_from >= seq_num_one); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Waiting for REPL_WILL_RESTART_WITH_INFO or REPL_ROLLBACK_FIRST message\n"); /* Receive journal data and place it in the Receive Pool */ buff_start = (unsigned char *)gtmrecv_msgp; buffp = buff_start; buff_unprocessed = 0; data_len = 0; write_loc = recvpool_ctl->write; write_wrap = recvpool_ctl->write_wrap; repl_recv_data_recvd = 0; repl_recv_data_processed = 0; repl_recv_postfltr_data_procd = 0; repl_recv_lastlog_data_recvd = 0; repl_recv_lastlog_data_procd = 0; msghdrlen = REPL_MSG_HDRLEN; GTMTLS_ONLY(DEBUG_ONLY(reneg_ack_sent = FALSE)); GTMTLS_ONLY(repl_tls.renegotiate_state = REPLTLS_RENEG_STATE_NONE); while (TRUE) { recvd_len = gtmrecv_max_repl_msglen - buff_unprocessed; GTMTLS_ONLY(poll_dir = REPL_INVALID_POLL_DIRECTION); while ((SS_NORMAL == (status = repl_recv(gtmrecv_sock_fd, (buffp + buff_unprocessed), &recvd_len, REPL_POLL_WAIT, &poll_dir))) && (0 == recvd_len)) { recvd_len = gtmrecv_max_repl_msglen - buff_unprocessed; if (xoff_sent) { DO_FLOW_CONTROL(write_loc); } if (xoff_sent && GTMRECV_XOFF_LOG_CNT <= xoff_msg_log_cnt) { /* Update process is still running slow; force wait before logging any message. */ SHORT_SLEEP(MILLISECS_IN_SEC - 1); /* Sleep for one second. */ REPL_DPRINT1("Waiting for Update Process to clear recvpool space\n"); xoff_msg_log_cnt = 0; } else if (xoff_sent) xoff_msg_log_cnt++; GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp); } if (SS_NORMAL != status) { if (EREPL_RECV == repl_errno) { if (REPL_CONN_RESET(status)) { repl_log(gtmrecv_log_fp, TRUE, TRUE, "Connection reset. Status = %d ; %s\n", status, STRERROR(status)); repl_log_conn_info(gtmrecv_sock_fd, gtmrecv_log_fp, TRUE); SEND_SYSMSG_REPLCOMM(LEN_AND_LIT("Error in receiving from source. Error in recv")); } } else if (EREPL_SELECT == repl_errno) { repl_log(gtmrecv_log_fp, TRUE, TRUE, "Error in receiving from source." " Error in select: %d ; %s\n", status, STRERROR(status)); ISSUE_REPLCOMM_ERROR("Error in receiving from source. Error in select", status); } repl_connection_reset = TRUE; repl_close(>mrecv_sock_fd); return; } if (repl_connection_reset) return; # ifdef REPL_CMP_SOLVE_TESTING /* Received communication from the source server, so we can cancel the timer */ if (TREF(gtm_environment_init) && repl_cmp_solve_timer_set) { cancel_timer((TID)repl_cmp_solve_rcv_timeout); repl_cmp_solve_timer_set = FALSE; } # endif /* Something on the replication pipe - read it */ REPL_DPRINT3("Pending data len : %d Prev buff unprocessed : %ld\n", data_len, buff_unprocessed); buff_unprocessed += recvd_len; repl_recv_data_recvd += (qw_num)recvd_len; if (gtmrecv_logstats) repl_log(gtmrecv_log_fp, FALSE, FALSE, "Recvd : %d Total : %ld\n", recvd_len, repl_recv_data_recvd); while (msghdrlen <= buff_unprocessed) { buffp_start = buffp; if (0 == data_len) { assert(0 == ((unsigned long)buffp % REPL_MSG_ALIGN)); DEBUG_ONLY(recvpool_prepared = FALSE); if (!remote_side->endianness_known) { remote_side->endianness_known = TRUE; msg_type = ((repl_msg_ptr_t)buffp)->type; if (!((REPL_MSGTYPE_LAST > msg_type) || (REPL_MSGTYPE_LAST > GTM_BYTESWAP_32(msg_type)))) WACKY_MESSAGE(msg_type, >mrecv_sock_fd, 1); /* impossible message type */ if ((REPL_MSGTYPE_LAST < msg_type) && (REPL_MSGTYPE_LAST > GTM_BYTESWAP_32(msg_type))) { remote_side->cross_endian = TRUE; repl_log(gtmrecv_log_fp, TRUE, TRUE, "Source and Receiver sides have opposite " "endianness\n"); } else { remote_side->cross_endian = FALSE; repl_log(gtmrecv_log_fp, TRUE, TRUE, "Source and Receiver sides have same " "endianness\n"); } } if (remote_side->cross_endian) { hdr_msg_type = GTM_BYTESWAP_32(((repl_msg_ptr_t)buffp)->type); hdr_msg_len = GTM_BYTESWAP_32(((repl_msg_ptr_t)buffp)->len); } else { hdr_msg_type = ((repl_msg_ptr_t)buffp)->type; hdr_msg_len = ((repl_msg_ptr_t)buffp)->len; } if (0 >= hdr_msg_len) WACKY_MESSAGE(hdr_msg_type, >mrecv_sock_fd, 2); /* invalid length - overflow */ msg_type = hdr_msg_type & REPL_TR_CMP_MSG_TYPE_MASK; if (REPL_TR_CMP_JNL_RECS == msg_type) { processed_hdrlen = REPL_MSG_HDRLEN; msg_len = hdr_msg_len - processed_hdrlen; gtmrecv_repl_cmpmsglen = msg_len; gtmrecv_repl_uncmpmsglen = (hdr_msg_type >> REPL_TR_CMP_MSG_TYPE_BITS); assert(0 < gtmrecv_repl_uncmpmsglen); assert(REPL_TR_CMP_THRESHOLD > gtmrecv_repl_uncmpmsglen); /* Since msg_len is compressed length, it need not be 8-byte aligned. Make it so * since 8-byte aligned length would have been sent by the source server anyways. */ msg_len = ROUND_UP(msg_len, REPL_MSG_ALIGN); buffp += processed_hdrlen; exp_data_len = gtmrecv_repl_uncmpmsglen; buff_unprocessed -= processed_hdrlen; GTMRECV_SET_BUFF_TARGET_CMPBUFF(gtmrecv_repl_cmpmsglen, gtmrecv_repl_uncmpmsglen, gtmrecv_cur_cmpmsglen); } else if (REPL_TR_CMP_JNL_RECS2 == msg_type) { /* A REPL_TR_CMP_JNL_RECS2 message is special in that it has a bigger message header. * So check if unprocessed length is greater than the header. If not need to read more. */ msghdrlen = REPL_MSG_HDRLEN2; if (msghdrlen > buff_unprocessed) /* Did not receive the full-header. */ break; /* Break out of here and read more data first. */ msghdrlen = REPL_MSG_HDRLEN; /* reset to regular msg hdr length for future messages */ processed_hdrlen = REPL_MSG_HDRLEN2; cmpmsgp = (repl_cmpmsg_ptr_t)buffp; if (remote_side->cross_endian) { cmpmsgp->cmplen = GTM_BYTESWAP_32(cmpmsgp->cmplen); cmpmsgp->uncmplen = GTM_BYTESWAP_32(cmpmsgp->uncmplen); } gtmrecv_repl_cmpmsglen = cmpmsgp->cmplen; gtmrecv_repl_uncmpmsglen = cmpmsgp->uncmplen; assert(0 < gtmrecv_repl_uncmpmsglen); assert(REPL_TR_CMP_THRESHOLD <= gtmrecv_repl_uncmpmsglen); msg_len = hdr_msg_len - processed_hdrlen; /* Unlike REPL_TR_CMP_JNL_RECS message, msg_len is guaranteed to be 8-byte aligned here */ buffp += processed_hdrlen; exp_data_len = gtmrecv_repl_uncmpmsglen; buff_unprocessed -= processed_hdrlen; GTMRECV_SET_BUFF_TARGET_CMPBUFF(gtmrecv_repl_cmpmsglen, gtmrecv_repl_uncmpmsglen, gtmrecv_cur_cmpmsglen); } else { processed_hdrlen = REPL_MSG_HDRLEN; msg_len = hdr_msg_len - processed_hdrlen; exp_data_len = msg_len; if (REPL_TR_JNL_RECS == msg_type || REPL_OLD_TRIPLE == msg_type || REPL_HISTREC == msg_type) { /* Target buffer is the receive pool. Prepare the receive pool for write (also * checks if the transaction will fit in). * Note: this is a special case where PREPARE_RECVPOOL_FOR_WRITE is not invoked * right before COPY_TO_RECVPOOL because we want to prepare the receive pool just * once even though the actual data (coming from the other end) might come in * different pieces. */ PREPARE_RECVPOOL_FOR_WRITE(exp_data_len, 0); DEBUG_ONLY(recvpool_prepared = TRUE); } /* for REPL_TR_CMP_JNL_RECS{2}, receive pool is prepared after uncompression */ buffp += processed_hdrlen; buff_unprocessed -= processed_hdrlen; } assert(0 <= buff_unprocessed); assert(0 == (msg_len % REPL_MSG_ALIGN)); data_len = msg_len; assert(0 == (exp_data_len % REPL_MSG_ALIGN)); } assert(0 == (data_len % REPL_MSG_ALIGN)); buffered_data_len = ((data_len <= buff_unprocessed) ? data_len : buff_unprocessed); buffered_data_len = ROUND_DOWN2(buffered_data_len, REPL_MSG_ALIGN); old_buffp = buffp; buffp += buffered_data_len; buff_unprocessed -= buffered_data_len; assert(0 <= buff_unprocessed); data_len -= buffered_data_len; /* Once we have sent a REPL_RENEG_ACK, the only message we should get is the REPL_RENEG_COMPLETE. */ assert(buffp > buff_start); assert(buffp <= (buff_start + gtmrecv_max_repl_msglen)); assert(!reneg_ack_sent || (REPL_RENEG_COMPLETE == msg_type)); copied_to_recvpool = FALSE; switch(msg_type) { case REPL_TR_JNL_RECS: case REPL_TR_CMP_JNL_RECS: case REPL_TR_CMP_JNL_RECS2: case REPL_OLD_TRIPLE: case REPL_HISTREC: is_repl_cmpc = ((REPL_TR_CMP_JNL_RECS == msg_type) || (REPL_TR_CMP_JNL_RECS2 == msg_type)); if (!is_repl_cmpc) { assert(recvpool_prepared); COPY_TO_RECVPOOL(old_buffp, buffered_data_len); /* uses and updates "write_loc" */ } else { memcpy(gtmrecv_cmpmsgp + gtmrecv_cur_cmpmsglen, old_buffp, buffered_data_len); gtmrecv_cur_cmpmsglen += buffered_data_len; assert(gtmrecv_cur_cmpmsglen <= gtmrecv_max_repl_cmpmsglen); } /* The partial/complete message is copied either to the receive pool or the private * compression buffer space. Set copied_to_recvpool to TRUE so that we can safely * set buffp = buff_start if this is the last message in the current received buffer. */ copied_to_recvpool = TRUE; repl_recv_data_processed += (qw_num)buffered_data_len; if (0 == data_len) { process_tr_buff(msg_type); if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) return; } break; case REPL_LOSTTNCOMPLETE: if (0 == data_len) { assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_LOSTTNCOMPLETE message\n"); status = repl_inst_reset_zqgblmod_seqno_and_tn(); assert(-1 == status || EXIT_ERR == status || SS_NORMAL == status); if (-1 == status) { /* only reason we know currently for the above function to return -1 is due * to a concurrent online rollback. In this case, we cannot continue * and need to start afresh. */ gtmrecv_onln_rlbk_clnup(); if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) return; } } break; case REPL_HEARTBEAT: if (0 == data_len) { /* Heartbeat msg contents start from buffp - msg_len */ GTM_WHITE_BOX_TEST(WBTEST_REPL_HEARTBEAT_NO_ACK, dont_reply_to_heartbeat, TRUE); if (dont_reply_to_heartbeat) { dont_reply_to_heartbeat = FALSE; break; } memcpy(heartbeat.ack_seqno, buffp - msg_len, msg_len); assert(remote_side->endianness_known); /* only then is remote_side->cross_endian * reliable */ if (!remote_side->cross_endian) { ack_time = *(gtm_time4_t *)&heartbeat.ack_time[0]; memcpy((uchar_ptr_t)&ack_seqno, (uchar_ptr_t)&heartbeat.ack_seqno[0], SIZEOF(seq_num)); } else { ack_time = GTM_BYTESWAP_32(*(gtm_time4_t *)&heartbeat.ack_time[0]); memcpy((uchar_ptr_t)&temp_ack_seqno, (uchar_ptr_t)&heartbeat.ack_seqno[0], SIZEOF(seq_num)); ack_seqno = GTM_BYTESWAP_64(temp_ack_seqno); } REPL_DPRINT4("HEARTBEAT received with time %ld SEQNO "INT8_FMT" at %ld\n", ack_time, ack_seqno, time(NULL)); ack_seqno = upd_proc_local->read_jnl_seqno; if (!remote_side->cross_endian) { heartbeat.type = REPL_HEARTBEAT; heartbeat.len = MIN_REPL_MSGLEN; memcpy((uchar_ptr_t)&heartbeat.ack_seqno[0], (uchar_ptr_t)&ack_seqno, SIZEOF(seq_num)); } else { heartbeat.type = GTM_BYTESWAP_32(REPL_HEARTBEAT); heartbeat.len = GTM_BYTESWAP_32(MIN_REPL_MSGLEN); temp_ack_seqno = GTM_BYTESWAP_64(ack_seqno); memcpy((uchar_ptr_t)&heartbeat.ack_seqno[0], (uchar_ptr_t)&temp_ack_seqno, SIZEOF(seq_num)); } REPL_SEND_LOOP(gtmrecv_sock_fd, &heartbeat, MIN_REPL_MSGLEN, REPL_POLL_NOWAIT) { GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp); } CHECK_REPL_SEND_LOOP_ERROR(status, "REPL_HEARTBEAT"); REPL_DPRINT4("HEARTBEAT sent with time %ld SEQNO "INT8_FMT" at %ld\n", ack_time, ack_seqno, time(NULL)); } break; case REPL_OLD_NEED_INSTANCE_INFO: if (0 == data_len) { assert(msg_len == MIN_REPL_MSGLEN - REPL_MSG_HDRLEN); old_need_instinfo_msg = (repl_old_needinst_msg_ptr_t)(buffp - msg_len - REPL_MSG_HDRLEN); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_OLD_NEED_INSTANCE_INFO message" " from primary instance [%s]\n", old_need_instinfo_msg->instname); if (jnlpool->repl_inst_filehdr->is_supplementary) { /* Issue REPL2OLD error because this is a supplementary instance and remote * side runs a GT.M version that does not know the supplementary protocol */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPL2OLD, 4, LEN_AND_STR(old_need_instinfo_msg->instname), LEN_AND_STR(jnlpool->repl_inst_filehdr->inst_info.this_instname)); } /* The source server does not understand the supplementary protocol. * So make sure -UPDATERESYNC if specified at receiver server startup * had no value. If not, issue error. */ if (gtmrecv_local->updateresync && (FD_INVALID != gtmrecv_local->updresync_instfile_fd)) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2, LEN_AND_LIT("Source side is non-supplementary implies " "-UPDATERESYNC needs no value specified")); } /* Initialize the remote side protocol version from "proto_ver" * field of this msg */ remote_side->proto_ver = old_need_instinfo_msg->proto_ver; assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver); assert(REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver); /*************** Send REPL_OLD_INSTANCE_INFO message ***************/ memset(&old_instinfo_msg, 0, SIZEOF(old_instinfo_msg)); memcpy(old_instinfo_msg.instname, jnlpool->repl_inst_filehdr->inst_info.this_instname, MAX_INSTNAME_LEN - 1); grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY); GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED; old_instinfo_msg.was_rootprimary = (unsigned char)repl_inst_was_rootprimary(); rel_lock(jnlpool->jnlpool_dummy_reg); gtmrecv_repl_send((repl_msg_ptr_t)&old_instinfo_msg, REPL_OLD_INSTANCE_INFO, MIN_REPL_MSGLEN, "REPL_OLD_INSTANCE_INFO", MAX_SEQNO); if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) return; /* Do not allow an instance which was formerly a root primary or which still * has a non-zero value of "zqgblmod_seqno" to start up as a tertiary. */ if ((old_instinfo_msg.was_rootprimary || jnlpool->jnlpool_ctl->max_zqgblmod_seqno) && !old_need_instinfo_msg->is_rootprimary) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_PRIMARYNOTROOT, 2, LEN_AND_STR((char *) old_need_instinfo_msg->instname)); gtmrecv_autoshutdown(); /* should not return */ assert(FALSE); } memcpy(jnlpool->jnlpool_ctl->primary_instname, old_need_instinfo_msg->instname, MAX_INSTNAME_LEN - 1); } break; case REPL_NEED_INSTINFO: if (0 == data_len) { assert(msg_len == SIZEOF(repl_needinst_msg_t) - REPL_MSG_HDRLEN); need_instinfo_msg = (repl_needinst_msg_ptr_t)(buffp - msg_len - REPL_MSG_HDRLEN); gtmrecv_check_and_send_instinfo(need_instinfo_msg, IS_RCVR_SRVR_TRUE); if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) return; } break; case REPL_CMP_TEST: if (0 == data_len) { repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_CMP_TEST message\n"); uncmpfail = FALSE; if (ZLIB_CMPLVL_NONE == gtm_zlib_cmp_level) { /* Receiver does not have compression enabled in the first place. * Send dummy REPL_CMP_SOLVE response message. */ repl_log(gtmrecv_log_fp, TRUE, TRUE, "Environment variable " "gtm_zlib_cmp_level specifies NO decompression (set to %d)\n", gtm_zlib_cmp_level); uncmpfail = TRUE; } assert(remote_side->endianness_known); /* ensure remote_side->cross_endian * is reliable */ if (!uncmpfail) { assert(msg_len == REPL_MSG_CMPINFOLEN - REPL_MSG_HDRLEN); cmptest_msg = (repl_cmpinfo_msg_ptr_t)(buffp - msg_len - REPL_MSG_HDRLEN); if (!remote_side->cross_endian) cmplen = cmptest_msg->datalen; else cmplen = GTM_BYTESWAP_32(cmptest_msg->datalen); if (REPL_MSG_CMPEXPDATALEN < cmplen) { assert(FALSE); /* since src srvr should not have sent such a msg */ repl_log(gtmrecv_log_fp, TRUE, TRUE, "Compression test message " "has compressed data length (%d) greater than receiver " "allocated length (%d)\n", (int)cmplen, REPL_MSG_CMPEXPDATALEN); uncmpfail = TRUE; } } if (!uncmpfail) { destlen = REPL_MSG_CMPEXPDATALEN; /* initialize available * decompressed buffer space */ ZLIB_UNCOMPRESS(&cmpsolve_msg.data[0], destlen, &cmptest_msg->data[0], cmplen, cmpret); GTM_WHITE_BOX_TEST(WBTEST_REPL_TEST_UNCMP_ERROR, cmpret, Z_DATA_ERROR); switch(cmpret) { case Z_MEM_ERROR: assert(FALSE); repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_Z_MEM_ERROR_STR GTM_ZLIB_UNCMP_ERR_SOLVE_STR); break; case Z_BUF_ERROR: assert(FALSE); repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_Z_BUF_ERROR_STR GTM_ZLIB_UNCMP_ERR_SOLVE_STR); break; case Z_DATA_ERROR: assert(gtm_white_box_test_case_enabled && (WBTEST_REPL_TEST_UNCMP_ERROR == gtm_white_box_test_case_number)); repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_Z_DATA_ERROR_STR GTM_ZLIB_UNCMP_ERR_SOLVE_STR); break; } if (Z_OK != cmpret) uncmpfail = TRUE; } if (!uncmpfail) { cmpsolve_msg.datalen = (int4)destlen; GTM_WHITE_BOX_TEST(WBTEST_REPL_TEST_UNCMP_ERROR, cmpsolve_msg.datalen, REPL_MSG_CMPDATALEN - 1); if (REPL_MSG_CMPDATALEN != cmpsolve_msg.datalen) { /* decompression did not yield precompressed data length */ assert(gtm_white_box_test_case_enabled && (WBTEST_REPL_TEST_UNCMP_ERROR == gtm_white_box_test_case_number)); repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_UNCMPLEN_ERROR_STR "\n", cmpsolve_msg.datalen, REPL_MSG_CMPDATALEN); uncmpfail = TRUE; } } if (uncmpfail) { cmpsolve_msg.datalen = REPL_RCVR_CMP_TEST_FAIL; repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_UNCMPTRANSITION_STR); } if (remote_side->cross_endian) cmpsolve_msg.datalen = GTM_BYTESWAP_32(cmpsolve_msg.datalen); cmpsolve_msg.proto_ver = REPL_PROTO_VER_THIS; # ifdef REPL_CMP_SOLVE_TESTING if (TREF(gtm_environment_init)) { start_timer((TID)repl_cmp_solve_rcv_timeout, 15 * 60 * 1000, repl_cmp_solve_rcv_timeout, 0, NULL); repl_cmp_solve_timer_set = TRUE; } # endif gtmrecv_repl_send((repl_msg_ptr_t)&cmpsolve_msg, REPL_CMP_SOLVE, REPL_MSG_CMPINFOLEN, "REPL_CMP_SOLVE", MAX_SEQNO); if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) return; if (!uncmpfail) repl_zlib_cmp_level = gtm_zlib_cmp_level; } break; case REPL_NEED_STRMINFO: if (0 == data_len) { assert(REPL_PROTO_VER_SUPPLEMENTARY <= remote_side->proto_ver); assert(remote_side->is_supplementary); assert(msg_len == MIN_REPL_MSGLEN - REPL_MSG_HDRLEN); need_strminfo_msg = (repl_needstrminfo_msg_ptr_t)(buffp - msg_len - REPL_MSG_HDRLEN); gtmrecv_process_need_strminfo_msg(need_strminfo_msg); /* Check for error return from above function call */ if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) return; } break; case REPL_NEED_HISTINFO: /* case REPL_NEED_TRIPLE_INFO: too but that message has been renamed to REPL_NEED_HISTINFO */ if (0 == data_len) { assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver); assert(msg_len == MIN_REPL_MSGLEN - REPL_MSG_HDRLEN); need_histinfo_msg = (repl_needhistinfo_msg_ptr_t)(buffp - msg_len - REPL_MSG_HDRLEN); gtmrecv_process_need_histinfo_msg(need_histinfo_msg, &histinfo); /* Check for error return from above function call */ if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) return; /* Send the histinfo */ gtmrecv_send_histinfo(&histinfo); /* Check for error return from above function call as well */ if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) return; } break; case REPL_WILL_RESTART_WITH_INFO: case REPL_ROLLBACK_FIRST: if (0 != data_len) break; /* Have received a REPL_WILL_RESTART_WITH_INFO or REPL_ROLLBACK_FIRST message. If * have not yet received a REPL_OLD_NEED_INSTANCE_INFO or REPL_NEED_INSTINFO message * (which would have initialized "remote_side->proto_ver"), it * means the remote side does not understand multi-site replication communication * protocol. Note that down. */ if (REPL_PROTO_VER_UNINITIALIZED == remote_side->proto_ver) { /* Issue REPL2OLD error because primary is dual-site */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPL2OLD, 4, LEN_AND_STR(UNKNOWN_INSTNAME), LEN_AND_STR(jnlpool->repl_inst_filehdr->inst_info.this_instname)); } /* Assert that endianness_known and cross_endian have already been initialized. * This ensures that remote_side->cross_endian is reliable */ assert(remote_side->endianness_known); assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver); assert(msg_len == MIN_REPL_MSGLEN - REPL_MSG_HDRLEN); start_msg = (repl_start_reply_msg_ptr_t)(buffp - msg_len - REPL_MSG_HDRLEN); assert((unsigned long)start_msg % SIZEOF(seq_num) == 0); /* alignment check */ memcpy((uchar_ptr_t)&recvd_jnl_seqno, (uchar_ptr_t)start_msg->start_seqno, SIZEOF(seq_num)); /* Assert that "node_endianness" field reflects our cross-endian understanding */ BIGENDIAN_ONLY(assert((REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver) || (remote_side->cross_endian && (LITTLE_ENDIAN_MARKER == start_msg->node_endianness)) || (!remote_side->cross_endian && (BIG_ENDIAN_MARKER == start_msg->node_endianness)))); LITTLEENDIAN_ONLY(assert((REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver) || (remote_side->cross_endian && (BIG_ENDIAN_MARKER == start_msg->node_endianness)) || (!remote_side->cross_endian && (LITTLE_ENDIAN_MARKER == start_msg->node_endianness)))); if (remote_side->cross_endian) recvd_jnl_seqno = GTM_BYTESWAP_64(recvd_jnl_seqno); /* Handle REPL_ROLLBACK_FIRST case (easy one) first */ if (REPL_ROLLBACK_FIRST == msg_type) { REPL_DPRINT1("Received REPL_ROLLBACK_FIRST message"); sgtm_putmsg(print_msg, PROC_RECVOPS_PRINT_MSG_LEN, VARLSTCNT(4) ERR_REPLAHEAD, 2, LEN_AND_LIT("")); repl_log(gtmrecv_log_fp, TRUE, TRUE, print_msg); SNPRINTF(print_msg_t, SIZEOF(print_msg_t), "Replicating instance : "INT8_FMT" " "Originating instance : "INT8_FMT". ", request_from, recvd_jnl_seqno); sgtm_putmsg(print_msg, PROC_RECVOPS_PRINT_MSG_LEN, VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_STR(print_msg_t)); repl_log(gtmrecv_log_fp, TRUE, TRUE, print_msg); if (gtmrecv_options.autorollback) { sgtm_putmsg(print_msg, PROC_RECVOPS_PRINT_MSG_LEN, VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_LIT("Initiating automatic rollback to match the replicating" " instance with a prior in-sync originating instance state.")); repl_log(gtmrecv_log_fp, TRUE, TRUE, print_msg); repl_connection_reset = TRUE; repl_close(>mrecv_sock_fd); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Closing connection before starting " "ROLLBACK\n"); if (SS_NORMAL != gtmrecv_start_onln_rlbk()) { /* gtmrecv_start_onln_rlbk() would have issued the appropriate * error message. */ assert(FALSE); gtmrecv_autoshutdown(); /* should not return */ assert(FALSE); } grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY); GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED; /* The ONLINE ROLLBACK did not change the physical or the logical state. * Otherwise the above macro would have returned to the caller. Since we * have already disconnected the connection by now, we cannot resume the * flow from this point on. Return to the calling function to continue * reconnecting. */ rel_lock(jnlpool->jnlpool_dummy_reg); return; } else { sgtm_putmsg(print_msg, PROC_RECVOPS_PRINT_MSG_LEN, VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_LIT(" The receiver server was not started with " "-AUTOROLLBACK. Perform MUPIP JOURNAL -ROLLBACK on the replicating" " instance to match the originating instance. Check the Source " "Server log to determine the last in-sync sequence number.")); repl_log(gtmrecv_log_fp, TRUE, TRUE, print_msg); } gtmrecv_autoshutdown(); /* should not return */ assert(FALSE); break; } /* Handle REPL_WILL_RESTART_WITH_INFO case now */ if (jnlpool->repl_inst_filehdr->was_rootprimary && jnlpool->jnlpool_ctl->upd_disabled) { /* This is the first time an instance that was formerly a root primary * is brought up as an immediate secondary of the new root primary. Once * fetchresync rollback has happened and the receiver and source server * have communicated successfully, the instance file header field that * indicates this was a root primary can be reset to FALSE as the zero * or non-zeroness of the "zqgblmod_seqno" field in the respective * database file headers henceforth controls whether this instance can * be brought up as a tertiary or not. Flush changes to file on disk. */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY); GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED; jnlpool->repl_inst_filehdr->was_rootprimary = FALSE; repl_inst_flush_filehdr(); rel_lock(jnlpool->jnlpool_dummy_reg); } assert(REPL_WILL_RESTART_WITH_INFO == msg_type); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_WILL_RESTART_WITH_INFO message" " with seqno "INT8_FMT" "INT8_FMTX"\n", recvd_jnl_seqno, recvd_jnl_seqno); remote_side->jnl_ver = start_msg->jnl_ver; remote_jnl_ver = remote_side->jnl_ver; REPL_DPRINT3("Local jnl ver is octal %o, remote jnl ver is octal %o\n", this_side->jnl_ver, remote_jnl_ver); repl_check_jnlver_compat(!remote_side->cross_endian); /* older versions zero filler that was in place of start_msg->start_flags, * so we are okay fetching start_msg->start_flags unconditionally. */ GET_ULONG(recvd_start_flags, start_msg->start_flags); if (remote_side->cross_endian) recvd_start_flags = GTM_BYTESWAP_32(recvd_start_flags); assert(remote_jnl_ver > V15_JNL_VER || 0 == recvd_start_flags); if (remote_jnl_ver <= V15_JNL_VER) /* safety in pro */ recvd_start_flags = 0; remote_side->is_std_null_coll = (recvd_start_flags & START_FLAG_COLL_M) ? TRUE : FALSE; if (remote_side->is_std_null_coll != this_side->is_std_null_coll) remote_side->null_subs_xform = (remote_side->is_std_null_coll ? STDNULL_TO_GTMNULL_COLL : GTMNULL_TO_STDNULL_COLL); else remote_side->null_subs_xform = FALSE; /* this sets null_subs_xform regardless of remote_jnl_ver */ remote_side->is_supplementary = start_msg->is_supplementary; remote_side->trigger_supported = (recvd_start_flags & START_FLAG_TRIGGER_SUPPORT) ? TRUE : FALSE; # ifdef GTM_TLS remote_side->tls_requested = (recvd_start_flags & START_FLAG_ENABLE_TLS) ? TRUE : FALSE; if (REPL_TLS_REQUESTED && !remote_side->tls_requested) { ISSUE_REPLNOTLS(ERR_REPLNOTLS, "Receiver side", "Source side"); CLEAR_REPL_TLS_REQUESTED; /* As if -tlsid qualifier was never specified. */ } /* If we have not requested TLS/SSL and the originating side requested TLS/SSL, the latter * reports the REPLNOTLS error and so we need not do anything. Hence, the below assert. */ assert(REPL_TLS_REQUESTED || !remote_side->tls_requested); # endif if (this_side->jnl_ver > remote_jnl_ver) { assert(JNL_VER_EARLIEST_REPL <= remote_jnl_ver); assert((remote_jnl_ver - JNL_VER_EARLIEST_REPL) < ARRAYSIZE(repl_filter_old2cur)); assert((remote_jnl_ver - JNL_VER_EARLIEST_REPL) < ARRAYSIZE(repl_filter_cur2old)); assert(IF_NONE != repl_filter_old2cur[remote_jnl_ver - JNL_VER_EARLIEST_REPL]); assert(IF_INVALID != repl_filter_old2cur[remote_jnl_ver - JNL_VER_EARLIEST_REPL]); /* reverse transformation should exist */ assert(IF_INVALID != repl_filter_cur2old[remote_jnl_ver - JNL_VER_EARLIEST_REPL]); assert(IF_NONE != repl_filter_cur2old[remote_jnl_ver - JNL_VER_EARLIEST_REPL]); gtmrecv_filter |= INTERNAL_FILTER; gtmrecv_alloc_filter_buff(gtmrecv_max_repl_msglen); } else { gtmrecv_filter &= ~INTERNAL_FILTER; if (NO_FILTER == gtmrecv_filter) gtmrecv_free_filter_buff(); } /* Don't send any more stopsourcefilter message */ gtmrecv_options.stopsourcefilter = FALSE; assert(QWEQ(recvd_jnl_seqno, request_from) || (jnlpool->repl_inst_filehdr->is_supplementary && !jnlpool->jnlpool_ctl->upd_disabled && strm_index)); assert(this_side->is_supplementary == jnlpool->repl_inst_filehdr->is_supplementary); if (this_side->is_supplementary && !remote_side->is_supplementary) { /* For the non-supplementary -> supplementary replication connection that happens * for the very first time during the lifetime of this receiver server, * (i.e. when upd_proc_local->read_jnl_seqno is 0), until this point in time, * we cannot be sure what the starting point of the transmission is (partly due * to -UPDATERESYNC but primarily due to -NORESYNC). Update process will be * waiting for receiver to set read_jnl_seqno to a non-zero value. Finish it now. * In case this is a reconnect, recvpool.upd_proc_local->read_jnl_seqno already * reflects how much the update process has processed so that should be untouched * by the receiver server as it will otherwise confuse the concurrently running * update process (see ). * In addition do not touch recvpool_ctl->jnl_seqno in this case as we need to * resume filling in the receive pool from where the previous connection stopped. */ assert(0 == GET_STRM_INDEX(recvd_jnl_seqno)); if (0 == recvpool.upd_proc_local->read_jnl_seqno) { /* Set read_jnl_seqno after jnl_seqno. Update process reads it in opposite * order. Have memory barriers in between to ensure no out-of-order reads. */ recvpool.recvpool_ctl->jnl_seqno = recvd_jnl_seqno; SHM_WRITE_MEMORY_BARRIER; recvpool.upd_proc_local->read_jnl_seqno = recvd_jnl_seqno; repl_log(gtmrecv_log_fp, TRUE, TRUE, "Wrote upd_proc_local->read_jnl_seqno" " : "INT8_FMT" "INT8_FMTX"\n", recvd_jnl_seqno, recvd_jnl_seqno); /* If -NORESYNC was used in receiver startup, we don't want to use it * anymore as future such connections could cause recvpool_ctl->jnl_seqno * to be reset further backwards and will confuse the update process. */ if (recvpool.gtmrecv_local->noresync) recvpool.gtmrecv_local->noresync = FALSE; } else { assert(recvpool.recvpool_ctl->jnl_seqno >= recvpool.upd_proc_local->read_jnl_seqno); assert(!recvpool.gtmrecv_local->noresync); } } /* Now that recvpool.recvpool_ctl->jnl_seqno has been determined for sure (for * non-supplementary or non-root-primary instances it is determined even before but * otherwise it takes until now). Force a log on the first recv. This function uses * recvpool.recvpool_ctl->jnl_seqno hence the placement here. */ gtmrecv_reinit_logseqno(); break; case REPL_INST_NOHIST: if (0 == data_len) { assert(msg_len == MIN_REPL_MSGLEN - REPL_MSG_HDRLEN); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Originating instance encountered a " "REPLINSTNOHIST error. JNL_SEQNO of this replicating instance precedes" " the current history in the originating instance file. " "Receiver server exiting.\n"); gtmrecv_autoshutdown(); /* should not return */ assert(FALSE); } break; case REPL_LOGFILE_INFO: if (0 == data_len) { logfile_msgp = (repl_logfile_info_msg_t *)(buffp - msg_len - REPL_MSG_HDRLEN); assert(REPL_PROTO_VER_REMOTE_LOGPATH <= logfile_msgp->proto_ver); if (remote_side->cross_endian) { logfile_msgp->fullpath_len = GTM_BYTESWAP_32(logfile_msgp->fullpath_len); logfile_msgp->pid = GTM_BYTESWAP_32(logfile_msgp->pid); } assert('\0' == logfile_msgp->fullpath[logfile_msgp->fullpath_len - 1]); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Remote side source log file path is %s; " "Source Server PID = %d\n", logfile_msgp->fullpath, logfile_msgp->pid); /* Now, send our logfile path to the source side */ assert(remote_side->endianness_known); len = repl_logfileinfo_get(recvpool.gtmrecv_local->log_file, &logfile_msg, remote_side->cross_endian, gtmrecv_log_fp); REPL_SEND_LOOP(gtmrecv_sock_fd, &logfile_msg, len, REPL_POLL_NOWAIT) { GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp); } CHECK_REPL_SEND_LOOP_ERROR(status, "REPL_LOGFILE_INFO"); } break; # ifdef GTM_TLS case REPL_NEED_TLS_INFO: if (0 != data_len) break; assert(REPL_PROTO_VER_TLS_SUPPORT <= REPL_PROTO_VER_THIS); assert(REPL_PROTO_VER_TLS_SUPPORT <= remote_side->proto_ver); assert(REPL_TLS_REQUESTED); assert(NULL != tls_ctx); assert(!repl_tls.enabled); need_tlsinfo_msgp = (repl_tlsinfo_msg_t *)(buffp - msg_len - REPL_MSG_HDRLEN); remote_API_ver = need_tlsinfo_msgp->API_version; remote_lib_ver = need_tlsinfo_msgp->library_version; if (remote_side->cross_endian) { remote_API_ver = GTM_BYTESWAP_32(remote_API_ver); remote_lib_ver = (uint4)GTM_BYTESWAP_32(remote_lib_ver); } repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_NEED_TLS_INFO message\n"); repl_log(gtmrecv_log_fp, TRUE, TRUE, " Remote side API version: 0x%08x\n", remote_API_ver); repl_log(gtmrecv_log_fp, TRUE, TRUE, " Remote side Library version: 0x%08x\n", remote_lib_ver); if (!gtmrecv_exchange_tls_info()) { if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) return; assert(PLAINTEXT_FALLBACK); assert(!repl_tls.enabled); } else repl_tls.enabled = TRUE; /* From here on, all communications are secured with SSL */ break; case REPL_RENEG_ACK_ME: if (0 != data_len) break; assert(!reneg_ack_sent); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_RENEG_ACK_ME message\n"); gtmrecv_repl_send(&renegotiate_msg, REPL_RENEG_ACK, MIN_REPL_MSGLEN, "REPL_RENEG_ACK", MAX_SEQNO); DEBUG_ONLY(reneg_ack_sent = TRUE); repl_tls.renegotiate_state = REPLTLS_WAITING_FOR_RENEG_COMPLETE; break; case REPL_RENEG_COMPLETE: if (0 != data_len) break; assert(reneg_ack_sent); repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_RENEG_COMPLETE message." " TLS/SSL connection successfully renegotiated.\n"); DEBUG_ONLY(reneg_ack_sent = FALSE); repl_log_tls_info(gtmrecv_log_fp, repl_tls.sock); repl_tls.renegotiate_state = REPLTLS_RENEG_STATE_NONE; break; # endif default: WACKY_MESSAGE(msg_type, >mrecv_sock_fd, 3); /* undefined message type */ } if (repl_connection_reset) return; } assert(0 == ((unsigned long)(buffp) % REPL_MSG_ALIGN)); if (buff_start != buffp) { /* We are at the tail of the current received buffer. */ if ((0 != data_len) && !copied_to_recvpool) { /* We have a complete header for a control message, but not the complete message. Move the message * (including the header) to the beginning of the allocated buffer space. Restore the remaining * length to buff_unprocessed and reset data_len so that the header is read again. */ data_len = 0; buff_unprocessed += (buffered_data_len + processed_hdrlen); assert((0 <= buff_unprocessed) && (buff_unprocessed <= recvpool_size)); if (buffp_start != buff_start) memmove(buff_start, buffp_start, buff_unprocessed); } else if (0 != buff_unprocessed) { /* We have an incomplete header. Move it to the beginning of the allocated buffer space. */ memmove(buff_start, buffp, buff_unprocessed); } buffp = buff_start; } GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp); } } void repl_cmp_solve_rcv_timeout(void) { assertpro(FALSE); } STATICFNDEF void gtmrecv_heartbeat_timer(TID tid, int4 interval_len, int *interval_ptr) { assert(0 != gtmrecv_now); UNIX_ONLY(assert(*interval_ptr == heartbeat_period);) /* interval_len and interval_ptr are dummies on VMS */ gtmrecv_now += heartbeat_period; REPL_DPRINT2("Starting heartbeat timer with %d s\n", heartbeat_period); start_timer((TID)gtmrecv_heartbeat_timer, heartbeat_period * 1000, gtmrecv_heartbeat_timer, SIZEOF(heartbeat_period), &heartbeat_period); /* start_timer expects time interval in milli seconds, heartbeat_period is in seconds */ } STATICFNDEF void gtmrecv_main_loop(boolean_t crash_restart) { assert(FD_INVALID == gtmrecv_sock_fd); gtmrecv_poll_actions(0, 0, NULL); /* Clear any pending bad trans */ gtmrecv_est_conn(); gtmrecv_bad_trans_sent = FALSE; /* this assignment should be after gtmrecv_est_conn since gtmrecv_est_conn can * potentially call gtmrecv_poll_actions. If the timing is right, * gtmrecv_poll_actions might set this variable to TRUE if the update process sets * bad_trans in the recvpool. When we are (re)establishing connection with the * source server, there is no point in doing bad trans processing. Also, we have * to send START_JNL_SEQNO message to the source server. If not, there will be a * deadlock with the source and receiver servers waiting for each other to send * a message. */ repl_recv_prev_log_time = gtmrecv_now; while (!repl_connection_reset) do_main_loop(crash_restart); return; } void gtmrecv_process(boolean_t crash_restart) { recvpool_ctl_ptr_t recvpool_ctl; upd_proc_local_ptr_t upd_proc_local; gtmrecv_local_ptr_t gtmrecv_local; if (ZLIB_CMPLVL_NONE != gtm_zlib_cmp_level) gtm_zlib_init(); /* Open zlib shared library for compression/decompression */ # ifdef GTM_TLS if (REPL_TLS_REQUESTED) { repl_do_tls_init(gtmrecv_log_fp); assert(REPL_TLS_REQUESTED || PLAINTEXT_FALLBACK); } # endif recvpool_ctl = recvpool.recvpool_ctl; upd_proc_local = recvpool.upd_proc_local; gtmrecv_local = recvpool.gtmrecv_local; /* Check all message sizes are the same size (32 bytes = MIN_REPL_MSGLEN) except for the REPL_OLD_TRIPLE message * (repl_histrec_msg_t structure) which is 8 bytes more. Pre-supplementary, the receiver server knew to handle * different sized messages only for a few messages types REPL_TR_JNL_RECS, REPL_OLD_TRIPLE and REPL_CMP_SOLVE. * But post-supplementary it knows to handle different sized messages for various additional message types * (including REPL_NEED_INSTINFO, REPL_INSTINFO, REPL_HISTREC). */ assert(MIN_REPL_MSGLEN == SIZEOF(repl_start_msg_t)); assert(MIN_REPL_MSGLEN == SIZEOF(repl_start_reply_msg_t)); assert(MIN_REPL_MSGLEN == SIZEOF(repl_resync_msg_t)); assert(MIN_REPL_MSGLEN == SIZEOF(repl_old_needinst_msg_t)); assert(MIN_REPL_MSGLEN < SIZEOF(repl_needinst_msg_t)); assert(MIN_REPL_MSGLEN == SIZEOF(repl_needhistinfo_msg_t)); assert(MIN_REPL_MSGLEN == SIZEOF(repl_old_instinfo_msg_t)); assert(MIN_REPL_MSGLEN < SIZEOF(repl_instinfo_msg_t)); assert(MIN_REPL_MSGLEN == SIZEOF(repl_histinfo1_msg_t)); assert(MIN_REPL_MSGLEN == SIZEOF(repl_histinfo2_msg_t)); assert(MIN_REPL_MSGLEN < SIZEOF(repl_histinfo_msg_t)); assert(MIN_REPL_MSGLEN < SIZEOF(repl_old_triple_msg_t)); assert(MIN_REPL_MSGLEN < SIZEOF(repl_histrec_msg_t)); assert(MIN_REPL_MSGLEN == SIZEOF(repl_heartbeat_msg_t)); assert(REPL_POLL_WAIT < MILLISECS_IN_SEC); recvpool_size = recvpool_ctl->recvpool_size; recvpool_high_watermark = (long)((float)RECVPOOL_HIGH_WATERMARK_PCTG / 100 * recvpool_size); recvpool_low_watermark = (long)((float)RECVPOOL_LOW_WATERMARK_PCTG / 100 * recvpool_size); if ((long)((float)(RECVPOOL_HIGH_WATERMARK_PCTG - RECVPOOL_LOW_WATERMARK_PCTG) / 100 * recvpool_size) >= RECVPOOL_XON_TRIGGER_SIZE) { /* for large receive pools, the difference between high and low watermarks as computed above may be too large that * we may not send XON quickly enough. Limit the difference to RECVPOOL_XON_TRIGGER_SIZE */ recvpool_low_watermark = recvpool_high_watermark - RECVPOOL_XON_TRIGGER_SIZE; } REPL_DPRINT4("RECVPOOL HIGH WATERMARK is %ld, LOW WATERMARK is %ld, Receive pool size is %ld\n", recvpool_high_watermark, recvpool_low_watermark, recvpool_size); gtmrecv_alloc_msgbuff(); gtmrecv_now = time(NULL); heartbeat_period = GTMRECV_HEARTBEAT_PERIOD; /* time keeper, well sorta */ start_timer((TID)gtmrecv_heartbeat_timer, heartbeat_period * 1000, gtmrecv_heartbeat_timer, SIZEOF(heartbeat_period), &heartbeat_period); /* start_timer expects time interval in milli seconds, heartbeat_period is in seconds */ do { gtmrecv_main_loop(crash_restart); } while (repl_connection_reset); assertpro(FALSE); /* shouldn't reach here */ return; } fis-gtm-V7.0-005/sr_unix/gtmrecv_shutdown.c0000755000032200000250000002021214342376330017607 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2017 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_socket.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_inet.h" #include "gtm_string.h" #include #include #include #include "repl_instance.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "gtmrecv.h" #include "repl_dbg.h" #include "gtm_stdio.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "is_proc_alive.h" #include "repl_log.h" #include "gt_timer.h" #include "ftok_sems.h" #include "gtmmsg.h" #include "repl_msg.h" #include "gtmsource.h" #ifdef DEBUG #include "wbox_test_init.h" #include "gtmio.h" #endif #define GTMRECV_WAIT_FOR_SHUTDOWN (1000 - 1) /* ms, almost 1s */ GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF uint4 process_id; GBLREF recvpool_addrs recvpool; GBLREF int recvpool_shmid; GBLREF gtmrecv_options_t gtmrecv_options; GBLREF boolean_t is_rcvr_server; GBLREF int gtmrecv_srv_count; GBLREF void (*call_on_signal)(); GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; error_def(ERR_RECVPOOLSETUP); error_def(ERR_TEXT); int gtmrecv_shutdown(boolean_t auto_shutdown, int exit_status) { uint4 savepid; boolean_t shut_upd_too = FALSE, was_crit; int status, save_errno; unix_db_info *udi; udi = (unix_db_info *)FILE_INFO(recvpool.recvpool_dummy_reg); repl_log(stdout, TRUE, TRUE, "Initiating shut down\n"); call_on_signal = NULL; /* So we don't reenter on error */ /* assert that auto shutdown should be invoked only if the current process is a receiver server */ assert(!auto_shutdown || gtmrecv_srv_count); if (auto_shutdown) { /* grab the ftok semaphore and recvpool access control lock IN THAT ORDER (to avoid deadlocks) */ repl_inst_ftok_sem_lock(); status = grab_sem(RECV, RECV_POOL_ACCESS_SEM); if (0 > status) { save_errno = errno; repl_log(stderr, TRUE, TRUE, "Error grabbing receive pool control semaphore : %s. " "Shutdown not complete\n", STRERROR(save_errno)); repl_inst_ftok_sem_release(); return ABNORMAL_SHUTDOWN; } } else { /* ftok semaphore and recvpool access semaphore should already be held from the previous call to "recvpool_init" */ assert(udi->grabbed_ftok_sem); assert(holds_sem[RECV][RECV_POOL_ACCESS_SEM]); /* We do not want to hold the options semaphore to avoid deadlocks with receiver server startup (C9F12-002766) */ assert(!holds_sem[RECV][RECV_SERV_OPTIONS_SEM]); recvpool.gtmrecv_local->shutdown = SHUTDOWN; /* Wait for receiver server to die. But before that release ftok semaphore and receive pool access control * semaphore. This way, other processes (either in this environment or a different one) don't encounter startup * issues. However, to ensure that a concurrent argument-less rundown doesn't remove these semaphores (in case they * are orphaned), increment the counter semaphore. */ if (0 != (status = incr_sem(RECV, RECV_SERV_COUNT_SEM))) { save_errno = errno; repl_log(stderr, TRUE, TRUE, "Could not acquire Receive Pool counter semaphore : %s. " "Shutdown did not complete\n", STRERROR(save_errno)); /* Even though we hold the FTOK and RECV_POOL_ACCESS_SEM before entering this function (as ensured by * asserts above), it is safe to release them in case of a premature error (like this one). The caller * doesn't rely on the semaphores being held and this function is designed to release these semaphores * eventually anyways (after gtmrecv_ipc_cleanup()) */ repl_inst_ftok_sem_release(); status = rel_sem(RECV, RECV_POOL_ACCESS_SEM); assert(0 == status); return ABNORMAL_SHUTDOWN; } if (0 != (status = rel_sem(RECV, RECV_POOL_ACCESS_SEM))) { save_errno = errno; repl_log(stderr, TRUE, TRUE, "Could not release Receive Pool access control semaphore : %s. " "Shutdown did not complete\n", STRERROR(save_errno)); repl_inst_ftok_sem_release(); /* see comment above for why this is okay */ status = decr_sem(RECV, RECV_SERV_COUNT_SEM); assert(0 == status); return ABNORMAL_SHUTDOWN; } repl_inst_ftok_sem_release(); while((SHUTDOWN == recvpool.gtmrecv_local->shutdown) && (0 < (savepid = recvpool.gtmrecv_local->recv_serv_pid)) && is_proc_alive(savepid, 0)) SHORT_SLEEP(GTMRECV_WAIT_FOR_SHUTDOWN); exit_status = recvpool.gtmrecv_local->shutdown; if (SHUTDOWN == exit_status) { if (0 == savepid) /* No Receiver Process */ exit_status = NORMAL_SHUTDOWN; else /* Receiver Server Crashed */ { repl_log(stderr, FALSE, TRUE, "Receiver Server exited abnormally\n"); exit_status = ABNORMAL_SHUTDOWN; shut_upd_too = TRUE; } } /* (Re)Grab the ftok semaphore and recvpool access control semaphore IN THAT ORDER (to avoid deadlocks) */ repl_inst_ftok_sem_lock(); # ifdef DEBUG /* Sleep for a few seconds to test for concurrent argument-less RUNDOWN to ensure that the latter doesn't remove * the RECV_POOL_ACCESS_SEM under the assumption that it is orphaned. */ if (gtm_white_box_test_case_enabled && (WBTEST_LONGSLEEP_IN_REPL_SHUTDOWN == gtm_white_box_test_case_number)) { DBGFPF((stderr, "GTMRECV_SHUTDOWN is about to start long sleep\n")); LONG_SLEEP(10); } # endif if (0 != (status = grab_sem(RECV, RECV_POOL_ACCESS_SEM))) { save_errno = errno; repl_log(stderr, TRUE, TRUE, "Could not acquire Receive Pool access control semaphore : %s. " "Shutdown did not complete\n", STRERROR(save_errno)); repl_inst_ftok_sem_release(); status = decr_sem(RECV, RECV_SERV_COUNT_SEM); assert(0 == status); return ABNORMAL_SHUTDOWN; } /* Now that semaphores are acquired, decrement the counter semaphore */ if (0 != (status = decr_sem(RECV, RECV_SERV_COUNT_SEM))) { save_errno = errno; repl_log(stderr, TRUE, TRUE, "Could not release Receive Pool counter semaphore : %s. " "Shutdown did not complete\n", STRERROR(save_errno)); repl_inst_ftok_sem_release(); status = rel_sem(RECV, RECV_POOL_ACCESS_SEM); assert(0 == status); return ABNORMAL_SHUTDOWN; } } if (shut_upd_too) { gtmrecv_end_helpers(FALSE); gtmrecv_endupd(); } /* gtmrecv_ipc_cleanup will not be successful unless receiver server has completely exited. * It relies on RECV_SERV_COUNT_SEM value. */ if (FALSE == gtmrecv_ipc_cleanup(auto_shutdown, &exit_status)) { /* Release all semaphores */ if (!auto_shutdown) { decr_sem(RECV, UPD_PROC_COUNT_SEM); decr_sem(RECV, RECV_SERV_COUNT_SEM); } rel_sem_immediate( RECV, RECV_POOL_ACCESS_SEM); } else { /* Receive Pool and Access Control Semaphores removed. Invalidate corresponding fields in file header */ assert(!udi->s_addrs.hold_onto_crit); was_crit = udi->s_addrs.now_crit; /* repl_inst_recvpool_reset inturn invokes repl_inst_flush_filehdr which expects the caller to grab journal pool * lock if journal pool is available. */ if ((NULL != jnlpool->jnlpool_ctl) && !was_crit) grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY); repl_inst_recvpool_reset(); if ((NULL != jnlpool->jnlpool_ctl) && !was_crit) rel_lock(jnlpool->jnlpool_dummy_reg); } assert(NULL != jnlpool->jnlpool_ctl); if (!ftok_sem_release(recvpool.recvpool_dummy_reg, !jnlpool->jnlpool_ctl->ftok_counter_halted && udi->counter_ftok_incremented, FALSE)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_RECVPOOLSETUP); return (exit_status); } static void gtmrecv_stop(boolean_t exit) { int status; status = gtmrecv_shutdown(TRUE, gtmrecv_end1(TRUE)) - NORMAL_SHUTDOWN; if (exit) gtmrecv_exit(status); return; } void gtmrecv_sigstop(void) { if (is_rcvr_server) gtmrecv_stop(FALSE); return; } void gtmrecv_autoshutdown(void) { gtmrecv_stop(TRUE); return; } fis-gtm-V7.0-005/sr_unix/gtmrecv_stopfilter.c0000644000032200000250000000267114342376330020135 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2019 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_unistd.h" #include "gtm_stdio.h" #include "gtm_fcntl.h" #include "gtm_inet.h" #include "gtm_ipc.h" #include #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdskill.h" #include "gdscc.h" #include "filestruct.h" #include "iosp.h" #include "repl_shutdcode.h" #include "repl_log.h" #include "util.h" #include GBLREF recvpool_addrs recvpool; int gtmrecv_stopfilter(void) { repl_log(stderr, TRUE, TRUE, "Initiating STOPRECEIVERFILTER operation on receiver server pid [%d]\n", recvpool.gtmrecv_local->recv_serv_pid); if ('\0' == recvpool.gtmrecv_local->filter_cmd[0]) { util_out_print("No filter currently active", TRUE); return (ABNORMAL_SHUTDOWN); } recvpool.gtmrecv_local->filter_cmd[0] = '\0'; util_out_print("Stop filter initiated", TRUE); return (NORMAL_SHUTDOWN); } fis-gtm-V7.0-005/sr_unix/gtmsecshr.c0000644000032200000250000012570314342376330016214 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /******************************************************************************************** * W A R N I N G --- W A R N I N G --- W A R N I N G --- W A R N I N G --- W A R N I N G * * * * This routine (gtmsecshr) runs as setuid to root to perform various functions on behalf * * of GT.M processes. Extreme care must be taken to prevent all forms of deceptive access, * * linking with unauthorized libraries, etc. Same applies to anything it calls. * * * * W A R N I N G --- W A R N I N G --- W A R N I N G --- W A R N I N G --- W A R N I N G * ********************************************************************************************/ #include "mdef.h" #include "main_pragma.h" #include #include #include #include "gtm_stat.h" #include "gtm_socket.h" #include #if !defined(_AIX) && !defined(__linux__) && !defined(__hpux) && !defined(__CYGWIN__) && !defined(__MVS__) # include #endif #include "gtm_syslog.h" #include "gtm_limits.h" #include "gtm_stdlib.h" #include "gtm_signal.h" #include "gtm_sem.h" #include "gtm_string.h" #include "gtm_un.h" #include "gtm_fcntl.h" #include #include "gtm_time.h" #include "gtm_unistd.h" #include "gtm_stdio.h" #include "gtm_permissions.h" #include "gtm_select.h" #if defined(__MVS__) # include "gtm_zos_io.h" #endif #include "cli.h" #include "error.h" #include "gtm_logicals.h" #include "io.h" #include "gtmsecshr.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "db_header_conversion.h" #include "filestruct.h" #include "mutex.h" #include "iosp.h" #include "gt_timer.h" #include "gtm_c_stack_trace.h" #include "eintr_wrappers.h" #include "eintr_wrapper_semop.h" #include "gtmimagename.h" #include "util.h" #include "send_msg.h" #include "generic_signal_handler.h" #include "gtmmsg.h" #include "have_crit.h" #include "sig_init.h" #include "gtmio.h" #include "common_startup_init.h" #include "gtm_threadgbl_init.h" #include "hashtab.h" #include "fork_init.h" #include "jnl.h" #include "repl_msg.h" /* needed by gtmsource.h */ #include "gtmsource.h" /* for anticipatory_freeze.h */ #include "anticipatory_freeze.h" #ifdef UTF8_SUPPORTED # include "gtm_icu_api.h" # include "gtm_utf8.h" #endif #include "getjobnum.h" #define execname "gtmsecshr" /* this is what this executable is supposed to be called. A different name is verboten */ #define intent_open "for open" /* FLUSH_DB_IPCS_INFO types */ #define intent_close "for close" GBLDEF CLI_ENTRY *cmd_ary = NULL; /* GTMSECSHR does not have any command tables so initialize command array to NULL */ GBLREF int gtmsecshr_sockfd; GBLREF struct sockaddr_un gtmsecshr_sock_name; GBLREF int gtmsecshr_sockpath_len; GBLREF key_t gtmsecshr_key; GBLREF uint4 process_id; GBLREF boolean_t need_core; GBLREF boolean_t first_syslog; /* Defined in util_output.c */ GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF unsigned int gtm_dist_len; GBLREF boolean_t gtm_dist_ok_to_use; LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; static volatile int gtmsecshr_timer_popped; static int gtmsecshr_socket_dir_len; void clean_client_sockets(char *path); void gtmsecshr_exit (int exit_code, boolean_t dump); void gtmsecshr_init(char_ptr_t argv[], char **rundir, int *rundir_len); void gtmsecshr_timer_handler(void); void gtmsecshr_signal_handler(int sig, siginfo_t *info, void *context); ZOS_ONLY(boolean_t gtm_tag_error(char *filename, int realtag, int desiredtag);) error_def(ERR_ASSERT); error_def(ERR_BADTAG); error_def(ERR_DBFILOPERR); error_def(ERR_DBNOTGDS); error_def(ERR_GTMASSERT); error_def(ERR_GTMASSERT2); error_def(ERR_GTMCHECK); error_def(ERR_GTMSECSHR); error_def(ERR_GTMSECSHRBADDIR); error_def(ERR_GTMSECSHRCHDIRF); error_def(ERR_GTMSECSHRDMNSTARTED); error_def(ERR_GTMSECSHRFORKF); error_def(ERR_GTMSECSHRGETSEMFAIL); error_def(ERR_GTMSECSHRISNOT); error_def(ERR_GTMSECSHRNOARG0); error_def(ERR_GTMSECSHROPCMP); error_def(ERR_GTMSECSHRRECVF); error_def(ERR_GTMSECSHRREMFILE); error_def(ERR_GTMSECSHRREMSEM); error_def(ERR_GTMSECSHRREMSEMFAIL); error_def(ERR_GTMSECSHRREMSHM); error_def(ERR_GTMSECSHRSCKSEL); error_def(ERR_GTMSECSHRSEMGET); error_def(ERR_GTMSECSHRSENDF); error_def(ERR_GTMSECSHRSHMCONCPROC); error_def(ERR_GTMSECSHRSOCKET); error_def(ERR_GTMSECSHRSRVFID); error_def(ERR_GTMSECSHRSRVFIL); error_def(ERR_GTMSECSHRSSIDF); error_def(ERR_GTMSECSHRSTART); error_def(ERR_GTMSECSHRSUIDF); error_def(ERR_GTMSECSHRTMOUT); error_def(ERR_GTMSECSHRTMPPATH); error_def(ERR_GTMSECSHRUPDDBHDR); error_def(ERR_MEMORY); error_def(ERR_OUTOFSPACE); error_def(ERR_STACKOFLOW); error_def(ERR_SYSCALL); error_def(ERR_TEXT); /* Note that this condition handler is not really properly setup as a condition handler * in that it has none of the required condition handler macros in it. It's job is just * to perform shutdown logic when it is called. No further handlers are called hence * the streamlined nature. */ CONDITION_HANDLER(gtmsecshr_cond_hndlr) { START_CH(TRUE); gtmsecshr_exit(arg, DUMPABLE ? TRUE : FALSE); } /* If there was a leftover socket, the client will append a lower case letter * which we take as a flag to delete all sockets for the current client pid */ void clean_client_sockets(char *path) { char last, suffix; int len; len = STRLEN(path); last = path[len - 1]; for (suffix = 'a'; last > suffix; suffix++) { path[len - 1] = suffix; /* OK since main loop is done with this */ UNLINK(path); /* We could care less about errors */ } path[len - 1] = '\0'; /* The normal name for the pid */ UNLINK(path); return; } /* For gtmsecshr, override this routine since crit doesn't exist here */ uint4 have_crit(uint4 crit_state) { return 0; } /* For gtmsecshr, no forking but still allow to create a core */ void gtm_fork_n_core(void) { /* Should we switch to an otherwise unpriv'd id of some sort and chdir to create the core * in say $gtm_dist ? */ DUMP_CORE; } /* Main gtmsecshr entry point. * * Note the functions in util_output know the caller is gtmsecshr so ALL messages flushed through it * automatically go to syslog. There is no flat-file logging. */ int main(int argc, char_ptr_t argv[]) { int selstat; int save_errno; int recv_complete, send_complete; int num_chars_recd, num_chars_sent, rundir_len; TID timer_id; GTM_SOCKLEN_TYPE client_addr_len; char *recv_ptr, *send_ptr, *rundir; fd_set wait_on_fd; struct sockaddr_un client_addr; struct timeval input_timeval; gtmsecshr_mesg mesg; DCL_THREADGBL_ACCESS; GTM_THREADGBL_INIT; assert((0 <= GTMSECSHR_MESG_TIMEOUT) && (MAXPOSINT4 >= GTMSECSHR_MESG_TIMEOUT)); common_startup_init(GTMSECSHR_IMAGE); /* Side-effect : Sets skip_dbtriggers = TRUE if platorm lacks trigger support */ err_init(gtmsecshr_cond_hndlr); gtmsecshr_init(argv, &rundir, &rundir_len); timer_id = (TID)main; while (TRUE) { input_timeval.tv_sec = MAX_TIMEOUT_VALUE; /* Restart timeout each interation for platforms that save * unexpired time when select exits. */ input_timeval.tv_usec = 0; assertpro(FD_SETSIZE > gtmsecshr_sockfd); FD_ZERO(&wait_on_fd); FD_SET(gtmsecshr_sockfd, &wait_on_fd); gtmsecshr_timer_popped = FALSE; SELECT(gtmsecshr_sockfd + 1, (void *)&wait_on_fd, NULL, NULL, &input_timeval, selstat); if (0 > selstat) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_GTMSECSHR, 1, process_id, ERR_GTMSECSHRSCKSEL, 0, errno); else if (0 == selstat) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_GTMSECSHRTMOUT); gtmsecshr_exit(0, 0); /* Doesn't return */ } recv_ptr = (char *)&mesg; client_addr_len = SIZEOF(struct sockaddr_un); DBGGSSHR((LOGFLAGS, "gtmsecshr: Select rc = %d message timeout = %d\n", selstat, GTMSECSHR_MESG_TIMEOUT)); start_timer(timer_id, GTMSECSHR_MESG_TIMEOUT, gtmsecshr_timer_handler, 0, NULL); recv_complete = FALSE; do { /* Note RECVFROM does not loop on EINTR return codes so must be handled */ num_chars_recd = (int)(RECVFROM(gtmsecshr_sockfd, (void *)recv_ptr, SIZEOF(mesg), 0, (struct sockaddr *)&client_addr, &client_addr_len)); save_errno = errno; DBGGSSHR((LOGFLAGS, "gtmsecshr: timer-popped: %d RECVFROM rc = %d errno = %d (only relevant if rc = " "-1)\n", gtmsecshr_timer_popped, num_chars_recd, save_errno)); if ((0 >= num_chars_recd) && (gtmsecshr_timer_popped || EINTR != save_errno)) /* Note error includes 0 return from UDP read - should never be possible with UDP */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_GTMSECSHR, 1, process_id, ERR_GTMSECSHRRECVF, 0, save_errno); if (0 < num_chars_recd) recv_complete = TRUE; /* Only complete messages received via UDP datagram */ } while (!recv_complete); cancel_timer(timer_id); assert(0 < num_chars_recd); service_request(&mesg, num_chars_recd, rundir, rundir_len); DBGGSSHR((LOGFLAGS, "gtmsecshr: service request complete - return code: %d\n", mesg.code)); if (INVALID_COMMAND != mesg.code) { /* Reply if code not overridden to mean no acknowledgement required */ send_ptr = (char *)&mesg; start_timer(timer_id, GTMSECSHR_MESG_TIMEOUT, gtmsecshr_timer_handler, 0, NULL); send_complete = FALSE; do { num_chars_sent = (int)(SENDTO(gtmsecshr_sockfd, send_ptr, GTM_MESG_HDR_SIZE, 0, (struct sockaddr *)&client_addr, (GTM_SOCKLEN_TYPE)client_addr_len)); save_errno = errno; DBGGSSHR((LOGFLAGS, "gtmsecshr: timer-popped: %d SENDTO rc = %d errno = %d (only relevant if " "rc = -1)\n", gtmsecshr_timer_popped, num_chars_recd, save_errno)); if ((0 >= num_chars_sent) && (gtmsecshr_timer_popped || save_errno != EINTR)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_GTMSECSHR, 1, process_id, ERR_GTMSECSHRSENDF, 0, save_errno); if (0 < num_chars_sent) send_complete = TRUE; } while (!send_complete); cancel_timer(timer_id); } else DBGGSSHR((LOGFLAGS, "gtmsecshr: SENDTO reply skipped due to mesg.code = INVALID_COMMAND\n")); assert(('a' > 'F') && ('a' > '9')); if ('a' <= client_addr.sun_path[strlen(client_addr.sun_path) - 1]) clean_client_sockets(client_addr.sun_path); } } void gtmsecshr_init(char_ptr_t argv[], char **rundir, int *rundir_len) { int file_des, save_errno, len = 0; int create_attempts = 0; int secshr_sem; int semop_res, rndirln, modlen; char *name_ptr, *rndir, *cp, realpathbef[GTM_PATH_MAX], *realpathaft, gtmdist[GTM_PATH_MAX]; char *path, *chrrv; pid_t pid; struct sembuf sop[4]; gtmsecshr_mesg mesg; struct stat stat_buf; char *gtm_tmp_ptr; int status; int lib_gid; struct stat dist_stat_buff; intrpt_state_t prev_intrpt_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Before priv escalation need to understand how/where we were invoked in terms of module path and name. * * Steps: * * 1. Verify we are "gtmsecshr" and not a module renamed for some other purpose. * 2. Verify current working dir is $gtm_dist/gtmsecshrdir. * 3. Verify we are loaded from $gtm_dist (a pretense setup by the wrapper). * * Note when/if this module merges with its wrapper, step 2 should be modified to that extent. Note our dependence * on argv[0] for correct startup directory may preclude getting rid of the wrapper. The execl*() functions allow * an invocation dir to be specified that has little to nothing to do with the actual invocation. */ /* Step 1 */ rndir = argv[0]; rndirln = STRLEN(rndir); if (0 == rndirln) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_GTMSECSHRSTART, 3, RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRNOARG0); gtmsecshr_exit(UNABLETODETERMINEPATH, FALSE); } for (cp = rndir + rndirln - 1; cp >= rndir; cp--) { if ('/' == *cp) break; } cp++; /* Bump back past the '/' */ modlen = (rndir + rndirln) - cp; if ((STRLEN(execname) != modlen) || (0 != memcmp(execname, cp, modlen))) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_GTMSECSHRSTART, 3, RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRISNOT, 2, modlen, cp); gtmsecshr_exit(NOTGTMSECSHR, FALSE); } rndirln = rndirln - modlen - 1; /* Step 2 */ realpathaft = malloc(GTM_PATH_MAX); /* Malloc space as this dir is used later during message validations */ memcpy(realpathbef, rndir, rndirln); /* Need to copy before we modify it */ realpathbef[rndirln] = '\0'; /* Terminate directory string (executable/dir name already checked) */ chrrv = realpath(realpathbef, realpathaft); /* Normalize dir name - eliminate all symlinks */ if (NULL == chrrv) { save_errno = errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_GTMSECSHRSTART, 3, RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRISNOT, 0, save_errno); gtmsecshr_exit(NOTGTMSECSHR, FALSE); } rndirln = STRLEN(realpathaft); /* Record (new) length now that realpath has normalized the path */ /* Temporaryily add gtmsecshrdir to exec path for verification purposes */ memcpy(realpathaft + rndirln, GTMSECSHR_DIR_SUFFIX, SIZEOF(GTMSECSHR_DIR_SUFFIX)); /* includes null terminator */ chrrv = getcwd(gtmdist, GTM_PATH_MAX); /* Use gtmdist 'cause it's convenient */ if ((NULL == chrrv) || (0 != strncmp(gtmdist, realpathaft, GTM_PATH_MAX))) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_GTMSECSHRSTART, 3, RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRBADDIR); gtmsecshr_exit(BADGTMDISTDIR, FALSE); } realpathaft[rndirln] = '\0'; /* Remove gtmsecshrdir part - Put it back to supplied path ($gtm_dist) */ *rundir = realpathaft; /* This value for $gtm_dist is used in later validations */ *rundir_len = rndirln; /* .. and can be used either with this len or NULL char terminator */ /* Step 3 */ path = gtm_dist; chrrv = realpath(path, gtmdist); if ((NULL == chrrv) || (0 != strncmp(realpathaft, gtmdist, GTM_PATH_MAX))) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_GTMSECSHRSTART, 3, RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRBADDIR); gtmsecshr_exit(BADGTMDISTDIR, FALSE); } /* **** With invocation validated, begin our priviledge escalation **** */ if (-1 == setuid(ROOTUID)) { save_errno = errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) MAKE_MSG_WARNING(ERR_GTMSECSHRSTART), 3, RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRSUIDF, 0, ERR_GTMSECSHROPCMP, 0, save_errno); gtmsecshr_exit(SETUIDROOT, FALSE); } /* Before we fork, close the system log because when the facility name disappears in this middle-process, * the logging capability disappears on some systems too - On others, it takes the executable name instead. * Either one causes our tests to fail. */ DEFER_INTERRUPTS(INTRPT_IN_LOG_FUNCTION, prev_intrpt_state); CLOSELOG(); ENABLE_INTERRUPTS(INTRPT_IN_LOG_FUNCTION, prev_intrpt_state); first_syslog = TRUE; FORK(pid); if (0 > pid) { /* Fork failed */ save_errno = errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_GTMSECSHRSTART, 3, RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRFORKF, 0, save_errno); EXIT(GNDCHLDFORKFLD); } else if (0 < pid) /* This is the original process - it dies quietly (no exit handler of any sort) to isolate us */ UNDERSCORE_EXIT(EXIT_SUCCESS); /****** We are now in the (isolated) child process ******/ getjobnum(); pid = getsid(process_id); gtm_dist_ok_to_use = TRUE; gtm_dist_len = strlen(gtm_dist); if ((pid != process_id) && ((pid_t)-1 == setsid())) { save_errno = errno; DEBUG_ONLY(util_out_print("expected sid !UL but have !UL", OPER, process_id, pid)); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) MAKE_MSG_WARNING(ERR_GTMSECSHRSTART), 3, RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRSSIDF, 0, save_errno); } /* Close standard IO devices - we don't need/want them as all IO goes to operator log. Else we would have IO devices * tied to whatever process started gtmsecshr up which we definitely don't want. */ CLOSEFILE(0, status); /* No stdin */ CLOSEFILE(1, status); /* No stdout */ CLOSEFILE(2, status); /* No stderr */ /* Init signal handling which works slightly different than in other utilities - gtmsecshr has its own handler which * *calls* generic_signal_handler (which always returns for gtmsecshr) - we then drive our normal exit handling. */ sig_init(gtmsecshr_signal_handler, NULL, NULL, NULL); file_des = sysconf(_SC_OPEN_MAX); for (file_des = file_des - 1; file_des >= 3; file_des--) { /* Close the file only if we have it open. This is to avoid a CLOSEFAIL error in case of * trying to close an invalid file descriptor. */ CLOSEFILE_IF_OPEN(file_des, status); } if (-1 == CHDIR(P_tmpdir)) /* Switch to temporary directory as CWD */ { save_errno = errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) MAKE_MSG_SEVERE(ERR_GTMSECSHRSTART), 3, RTS_ERROR_LITERAL("Server"), process_id, ERR_GTMSECSHRCHDIRF, 2, LEN_AND_STR(P_tmpdir), save_errno); EXIT(UNABLETOCHDIR); } umask(0); if (0 != gtmsecshr_pathname_init(SERVER, *rundir, *rundir_len)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) MAKE_MSG_SEVERE(ERR_GTMSECSHRSOCKET), 3, RTS_ERROR_LITERAL("Server path"), process_id); if (-1 == (secshr_sem = semget(gtmsecshr_key, FTOK_SEM_PER_ID, RWDALL | IPC_NOWAIT))) { secshr_sem = INVALID_SEMID; if ((ENOENT != errno) || /* Below will fail otherwise */ (-1 == (secshr_sem = semget(gtmsecshr_key, FTOK_SEM_PER_ID, RWDALL | IPC_CREAT | IPC_NOWAIT | IPC_EXCL)))) { secshr_sem = INVALID_SEMID; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_GTMSECSHRSEMGET, 1, errno); gtmsecshr_exit(SEMGETERROR, FALSE); } } sop[0].sem_num = 0; sop[0].sem_op = 0; sop[0].sem_flg = IPC_NOWAIT; sop[1].sem_num = 0; sop[1].sem_op = 1; sop[1].sem_flg = IPC_NOWAIT | SEM_UNDO; SEMOP(secshr_sem, sop, 2, semop_res, NO_WAIT); if (0 > semop_res) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) MAKE_MSG_WARNING(ERR_GTMSECSHRSTART), 3, RTS_ERROR_LITERAL("Server"), process_id, ERR_TEXT, 2, RTS_ERROR_LITERAL("server already running"), errno); /* If gtm_tmp is not defined, show default path */ if (gtm_tmp_ptr = GETENV("gtm_tmp")) /* Warning - assignment */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_GTMSECSHRTMPPATH, 2, RTS_ERROR_TEXT(gtm_tmp_ptr), ERR_TEXT, 2, RTS_ERROR_TEXT("(from $gtm_tmp)")); else send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_GTMSECSHRTMPPATH, 2, RTS_ERROR_TEXT("/tmp")); gtmsecshr_exit(SEMAPHORETAKEN, FALSE); } if (0 != gtmsecshr_sock_init(SERVER)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) MAKE_MSG_SEVERE(ERR_GTMSECSHRSOCKET), 3, RTS_ERROR_LITERAL("Server"), process_id); if (-1 == Stat(gtmsecshr_sock_name.sun_path, &stat_buf)) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) MAKE_MSG_WARNING(ERR_GTMSECSHRSTART), 3, RTS_ERROR_LITERAL("Server"), process_id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to get status of socket file"), errno); /* Get the distribution group */ lib_gid = gtm_get_group_id(&dist_stat_buff); /* If it is world accessible then make mode 666 */ if ((-1 != lib_gid) && (dist_stat_buff.st_mode & 04)) lib_gid = -1; if (-1 == lib_gid) stat_buf.st_mode = 0666; else { /* Change group if different from current user group */ if (lib_gid != GETGID() && (-1 == CHOWN(gtmsecshr_sock_name.sun_path, -1, lib_gid))) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) MAKE_MSG_WARNING(ERR_GTMSECSHRSTART), 3, RTS_ERROR_LITERAL("Server"), process_id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to change socket file group"), errno); stat_buf.st_mode = 0660; } if (-1 == CHMOD(gtmsecshr_sock_name.sun_path, stat_buf.st_mode)) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) MAKE_MSG_WARNING(ERR_GTMSECSHRSTART), 3, RTS_ERROR_LITERAL("Server"), process_id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to change socket file permisions"), errno); name_ptr = strrchr(gtmsecshr_sock_name.sun_path, '/'); while (*name_ptr == '/') /* back off in case of double-slash */ name_ptr--; gtmsecshr_socket_dir_len = (int)(name_ptr - gtmsecshr_sock_name.sun_path + 1); /* Preallocate some timer blocks. */ prealloc_gt_timers(); /* Note we do NOT call gt_timers_add_safe_hndlrs here - don't need them or their baggage */ /* Create communication key used in all gtmsecshr messages. Key's purpose is to eliminate cross-version * communication issues. */ STR_HASH((char *)gtm_release_name, gtm_release_name_len, TREF(gtmsecshr_comkey), 0); /* Initialization complete */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_GTMSECSHRDMNSTARTED, 5, gtmsecshr_key, gtm_release_name_len, gtm_release_name, *rundir_len, *rundir); return; } void gtmsecshr_exit(int exit_code, boolean_t dump) { int gtmsecshr_sem; if (dump) DUMP_CORE; gtmsecshr_sock_cleanup(SERVER); gtmsecshr_sockfd = FD_INVALID; if (SEMAPHORETAKEN != exit_code) { /* remove semaphore */ if (-1 == (gtmsecshr_sem = semget(gtmsecshr_key, FTOK_SEM_PER_ID, RWDALL | IPC_NOWAIT))) { gtmsecshr_sem = INVALID_SEMID; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_GTMSECSHRGETSEMFAIL, 1, errno); } if (-1 == semctl(gtmsecshr_sem, 0, IPC_RMID, 0)) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_GTMSECSHRREMSEMFAIL, 1, errno); } /* Note shutdown message taken care of by generic_signal_handler */ EXIT(exit_code); } void gtmsecshr_timer_handler(void) { gtmsecshr_timer_popped = TRUE; } void gtmsecshr_signal_handler(int sig, siginfo_t *info, void *context) { /* Do standard signal handling */ (void)generic_signal_handler(sig, info, context); /* Note that we are not letting process_signal run gtm_fork_n_core. In testing, * bad things occurred when the root process ran into trouble trying to fork * so our object is to avoid the problem entirely and core here if we need to * and if the OS will do it (Linux seems not to core if root process). */ gtmsecshr_exit(sig, need_core); } void service_request(gtmsecshr_mesg *buf, int msglen, char *rundir, int rundir_len) { int flags, fn_len, index, basind, save_errno, save_code; int stat_res, fd; char *basnam, *fn; struct shmid_ds temp_shmctl_buf; struct stat statbuf; sgmnt_data header, *csd; endian32_struct check_endian; char *intent, *buff; boolean_t fd_opened_with_o_direct; uint4 fsb_size; ZOS_ONLY(int realfiletag;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; save_code = buf->code; /* First up, validate the communication key for this gtmsecshr version */ if (TREF(gtmsecshr_comkey) != buf->comkey) { buf->code = INVALID_COMKEY; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Comkey not correct for this gtmsecshr version")); return; } /* Process code (with code specific valications) */ switch(buf->code) { # ifdef NOT_CURRENTLY_USED /* Currently, M LOCK wakeup logic is disabled in the client and here. This code was not being used due to a * requirement (and check) that crit was not being held. Since mlk_unlock()->mlk_wake_pending->crit_wake() * runs in crit, this prevented gtmsecshr from EVER being called with this message. This code is disabled until * the next gtmsecshr improvement phase which will modify M unLOCK logic to drive the wakeups only after the * process has released crit. This will restore fast M LOCK wakeups for processes with a different userid than * the current process which currently waits until a 100ms poll timer expires to retry the lock. */ case WAKE_MESSAGE: /* if (0 != validate_receiver(buf, rundir, rundir_len, save_code)) return; / * buf->code already set - to be re-enabled when routine is completed */ buf->code = 0; if ((-1 == kill((pid_t)buf->mesg.id, SIGALRM)) && (ESRCH != errno)) { save_errno = errno; buf->code = save_errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to wake up process"), save_errno); } # ifdef DEBUG else /* Output msg only in DEBUG mode as these could otherwise be frequent */ util_out_print("[client pid !UL] Process !UL was awakened", OPER, buf->pid, buf->mesg.id); # endif break; # endif case CONTINUE_PROCESS: /* if (0 != validate_receiver(buf, rundir, rundir_len, save_code)) return; / * buf->code already set - to be re-enabled when routine is completed */ buf->code = 0; if ((-1 == kill((pid_t)buf->mesg.id, SIGCONT)) && (ESRCH != errno)) { save_errno = errno; buf->code = save_errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to request process to resume processing"), save_errno); } # ifdef DEBUG else /* Output msg only in DEBUG mode as these could otherwise be frequent */ util_out_print("[client pid !UL] Process !UL was requested to resume processing", OPER, buf->pid, buf->mesg.id); # endif break ; case REMOVE_SEM: buf->code = (-1 == semctl((int)buf->mesg.id, 0, IPC_RMID, 0)) ? errno : 0; if (!buf->code) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_GTMSECSHRREMSEM, 2, buf->pid, buf->mesg.id); else { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to remove semaphore"), buf->code); } break; case REMOVE_SHM: buf->code = (-1 == shmctl((int)buf->mesg.id, IPC_STAT, &temp_shmctl_buf)) ? errno : 0; if (buf->code) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to get shared memory statistics"), buf->code); break; } else if (1 < temp_shmctl_buf.shm_nattch) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_GTMSECSHRSHMCONCPROC, 2, buf->mesg.id, temp_shmctl_buf.shm_nattch); buf->code = EBUSY; break; } buf->code = (-1 == shmctl((int)buf->mesg.id, IPC_RMID, 0)) ? errno : 0; if (!buf->code) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_GTMSECSHRREMSHM, 3, buf->pid, buf->mesg.id, temp_shmctl_buf.shm_nattch); else { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to remove shared memory segment"), buf->code); } break; # ifndef MUTEX_MSEM_WAKE case REMOVE_FILE : /* This case only exists for plastforms using socket based long sleep when waiting for the * critical section. The memory semaphore based sleeps do not use so cannot orphan this socket. */ for (index = 0; index < SIZEOF(buf->mesg.path); index++) { /* Verify has null terminator within the message */ if ('\0' == buf->mesg.path[index]) break; } if ((0 == index) || (index >= SIZEOF(buf->mesg.path)) || (index != (msglen - GTM_MESG_HDR_SIZE - 1))) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFIL, 7, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, buf->code, index >= SIZEOF(buf->mesg.path) ? SIZEOF(buf->mesg.path) - 1 : index, buf->mesg.path, ERR_TEXT, 2, RTS_ERROR_LITERAL("no file name or length too long or invalid")); buf->code = EINVAL; } else { if (('/' == buf->mesg.path[index - 1]) && (1 < index)) { /* remove trailing slash unless only slash */ buf->mesg.path[index - 1] = '\0'; index--; } for (basind = index - 1; 0 <= basind; basind--) { if ('/' == buf->mesg.path[basind]) break; } if ((0 < basind) || ((0 == basind) && ('/' == buf->mesg.path[basind]))) basnam = &buf->mesg.path[basind + 1]; else basnam = &buf->mesg.path[0]; /* Verify: * * 1. File exists. * 2. File is a socket. * 3. Has a name prefix GT.M would use * 4. File is resident in expected directory ($gtm_tmp or /tmp). * */ STAT_FILE(buf->mesg.path, &statbuf, stat_res); if (-1 == stat_res) { # ifdef DEBUG if (buf->usesecshr && (ENOENT == errno)) /* ALL unlinks for the mutex socket come thru here in this mode so if socket does * not exist, just let it go. */ buf->code = 0; else # endif { save_errno = errno; buf->code = save_errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(14) ERR_GTMSECSHRSRVFIL, 7, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, buf->code, index, buf->mesg.path, ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to get file status"), save_errno); } } else if ((!S_ISSOCK(statbuf.st_mode)) ZOS_ONLY(|| (S_ISCHR(statbuf.st_mode)))) { buf->code = EINVAL; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFIL, 7, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, buf->code, index, buf->mesg.path, ERR_TEXT, 2, RTS_ERROR_LITERAL("File is not a GTM mutex socket file")); } else if (0 != MEMCMP_LIT(basnam, MUTEX_SOCK_FILE_PREFIX)) { buf->code = EINVAL; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFIL, 7, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, buf->code, index, buf->mesg.path, ERR_TEXT, 2, RTS_ERROR_LITERAL("File name does not match the naming convention for a GT.M " "mutex socket file")); } else if (0 != memcmp(gtmsecshr_sock_name.sun_path, buf->mesg.path, gtmsecshr_socket_dir_len)) { buf->code = EINVAL; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFIL, 7, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, buf->code, index, buf->mesg.path, ERR_TEXT, 2, RTS_ERROR_LITERAL("File does not reside in the normal directory for a GT.M " "mutex socket file")); } else if (buf->code = (-1 == UNLINK(buf->mesg.path)) ? errno : 0) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(14) ERR_GTMSECSHRSRVFIL, 7, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, RTS_ERROR_STRING(buf->mesg.path), ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to remove file"), buf->code); } else send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_GTMSECSHRREMFILE, 3, buf->pid, RTS_ERROR_STRING(buf->mesg.path)); } break; # endif case FLUSH_DB_IPCS_INFO: /* Most of this code came from file_head_read/write.c but those routines don't follow our rules * (no stdout/stderr IO) so we streamline it here. */ fn = buf->mesg.db_ipcs.fn; fn_len = buf->mesg.db_ipcs.fn_len; if ((GTM_PATH_MAX < fn_len) || ('\0' != *(fn + fn_len)) || (fn_len != (msglen - GTM_MESG_HDR_SIZE - offsetof(ipcs_mesg, fn[0]) - 1))) { buf->code = EINVAL; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2, RTS_ERROR_LITERAL("invalid file name argument")); break; } /* First open and read-in the fileheader */ flags = O_RDWR; if (buf->mesg.db_ipcs.open_fd_with_o_direct) { fd_opened_with_o_direct = TRUE; flags = flags | O_DIRECT_FLAGS; } else fd_opened_with_o_direct = FALSE; OPENFILE(fn, flags, fd); /* udi not available so OPENFILE_DB not used */ if (FD_INVALID == fd) { save_errno = buf->code = errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_DBFILOPERR, 2, fn_len, fn, save_errno); break; } # ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(fd, TAG_BINARY, &realfiletag)) TAG_POLICY_SEND_MSG(fn, errno, realfiletag, TAG_BINARY); # endif /* Verify is a GT.M database - validations: * * 1. Is a regular file (not a special type). * 2. Is of a size at least as large as the file header (doesn't get fancy with this) * 3. Able to read the file. * 4. Verify tag at top of file denotes a GT.M database of the correct version. * 5. Validate fields being set into database file (see details below). * 6. Set the fields into the database. * 7. Write updated database header and close the database. */ FSTAT_FILE(fd, &statbuf, save_errno); if (-1 == save_errno) { buf->code = save_errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); CLOSEFILE_RESET(fd, save_errno); /* resets "fd" to FD_INVALID */ break; } if (!S_ISREG(statbuf.st_mode) || (SIZEOF(header) > statbuf.st_size)) { buf->code = ERR_DBNOTGDS; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_DBNOTGDS, 2, LEN_AND_STR(fn)); CLOSEFILE_RESET(fd, save_errno); /* resets "fd" to FD_INVALID */ break; } if (fd_opened_with_o_direct) { fsb_size = get_fs_block_size(fd); DIO_BUFF_EXPAND_IF_NEEDED_NO_UDI(SGMNT_HDR_LEN, fsb_size, &(TREF(dio_buff))); csd = (sgmnt_data *)(TREF(dio_buff)).aligned; } else csd = &header; DB_LSEEKREAD(((unix_db_info *)NULL), fd, 0, csd, SGMNT_HDR_LEN, save_errno); if (0 == memcmp(csd->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_upconv(csd); if (0 != save_errno) { buf->code = save_errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); CLOSEFILE_RESET(fd, save_errno); /* resets "fd" to FD_INVALID */ break; } if (memcmp(csd->label, GDS_LABEL, GDS_LABEL_SZ - 1) && memcmp(csd->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) { /* Verify is GT.M database file */ buf->code = ERR_DBNOTGDS; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_DBNOTGDS, 2, LEN_AND_STR(fn)); CLOSEFILE_RESET(fd, save_errno); /* resets "fd" to FD_INVALID */ break; } /* It would be easier to use the CHECK_DB_ENDIAN macro here but we'd prefer it didn't raise rts_error */ check_endian.word32 = csd->minor_dbver; if (!check_endian.shorts.ENDIANCHECKTHIS) { buf->code = ERR_DBENDIAN; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_DBENDIAN, 4, fn_len, fn, ENDIANOTHER, ENDIANTHIS); CLOSEFILE_RESET(fd, save_errno); break; } /* Verify the fields to update make sense. * * 1. If new semid is INVALID_SEMID, then (file close): * a. New shmid should be INVALID_SHMID. * b. Both ctime fields should be zero. * c. If current value of semid in header is INVALID_SEMID, verify the other settings are also correct * for a closed database, close file. * d. If current value of semid is not INVALID_SEMID, check its value - should be <= 1 else error. * e. The current shmid should exist and have 1 attachment. * 2. If new semid is *not* INVALID_SEMID then (file open): * a. Initial impulse is to check that new shmid is NOT INVALID_SHMID, but in one case which we can't * detect (standalone access of R/O DB by MUPIP INTEG and perhaps others), only the semaphore id * and ctime are modified. The shm fields are left as is. * b. If previous semid is not INVALID_SEMID, verify is same as we've been requested to update and close * with an already open message. * c. If previous semid is INVALID_SEMID, check the new semid and query its value. Should be <= 1 or * raise error. * d. Ditto for shmid and its attach value should also be <= 1. * * Note - Items 1c - 1e and items 2b - 2d not yet implemented (phase 3). */ if (INVALID_SEMID == buf->mesg.db_ipcs.semid) { /* Intent is to close the given database */ intent = intent_close; if ((INVALID_SHMID != buf->mesg.db_ipcs.shmid) || (0 != buf->mesg.db_ipcs.gt_sem_ctime) || (0 != buf->mesg.db_ipcs.gt_shm_ctime)) { buf->code = EINVAL; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Invalid header value combination")); CLOSEFILE_RESET(fd, save_errno); break; } } else { /* Intent is to open the given database - Note if this is a standalone access of a read-only * DB by such as MUPIP INTEG, only the semaphore is set, not shared memory so we must relax our * checks a bit and allow the shm id and ctime to remain in "closed" state. But if shmid is * specified, do validate that ctime was specified and vice-versa. */ intent = intent_open; if ((0 == buf->mesg.db_ipcs.gt_sem_ctime) || ((INVALID_SEMID == buf->mesg.db_ipcs.shmid) && (0 != buf->mesg.db_ipcs.gt_shm_ctime)) || ((INVALID_SEMID != buf->mesg.db_ipcs.shmid) && (0 == buf->mesg.db_ipcs.gt_shm_ctime))) { buf->code = EINVAL; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Invalid header value combination")); CLOSEFILE_RESET(fd, save_errno); break; } } /* Update file header fields */ csd->semid = buf->mesg.db_ipcs.semid; csd->shmid = buf->mesg.db_ipcs.shmid; csd->gt_sem_ctime.ctime = buf->mesg.db_ipcs.gt_sem_ctime; csd->gt_shm_ctime.ctime = buf->mesg.db_ipcs.gt_shm_ctime; /* And flush the changes back out. */ if (0 == memcmp(csd->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv(csd); GTMSECSHR_DB_LSEEKWRITE(((unix_db_info *)NULL), fd, 0, csd, SGMNT_HDR_LEN, save_errno); if (0 != save_errno) { buf->code = save_errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to write database file header"), save_errno); CLOSEFILE_RESET(fd, save_errno); break; } send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_GTMSECSHRUPDDBHDR, 5, buf->pid, fn_len, fn, RTS_ERROR_STRING(intent)); buf->code = 0; CLOSEFILE_RESET(fd, save_errno); break; default: send_msg_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, buf->code, buf->mesg.id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Invalid Service Request")); buf->code = 0x8000; /* Flag for no-ack required - invalid commands get no response */ } return; } /* Service request asks gtmsecshr to send a signal to a given process. Verify (as best we can) this process is running something * related to GT.M. Two potential methods are used: * * 1. Target process has an execution directory the same as ours ($gtm_dist). Note it is possible a target process is doing * a call-in so this test is not always TRUE but is the faster of the two tests. * 2. Target process has libgtmshr.{suffix} (aka GTMSHR_IMAGE_NAME from mdefsa.h) loaded which we can tell by examining the * open files of the target process. * * Note - this routine is currently NOT USED as it is incomplete and not yet implemented for all platforms. We leave it in here * for now and plan to complete it in an upcoming version. */ int validate_receiver(gtmsecshr_mesg *buf, char *rundir, int rundir_len, int save_code) { int save_errno; # ifdef __linux__ # define PROCPATH_PREFIX "/proc/" # define PROCPATH_CMDLSUFFIX "/cmdline" # define PROCPATH_MAPSSUFFIX "/maps" int lnln, clrv, cmdbufln; FILE *procstrm; char procpath[GTM_PATH_MAX], cmdbuf[GTM_PATH_MAX], rpcmdbuf[GTM_PATH_MAX]; char *ppptr, *ppptr_save, *csrv, *cptr; /* Check #1 - open /proc//cmdline, read to first NULL - this is the command name */ ppptr = procpath; MEMCPY_LIT(procpath, PROCPATH_PREFIX); ppptr += STRLEN(PROCPATH_PREFIX); ppptr = (char*)i2asc((uchar_ptr_t)ppptr, buf->mesg.id); ppptr_save = ppptr; /* Save where adding cmdline so can replace if need to move to check #2 */ memcpy(ppptr, PROCPATH_CMDLSUFFIX, SIZEOF(PROCPATH_CMDLSUFFIX)); /* Copy includes terminating null of literal */ Fopen(procstrm, procpath, "r"); if (NULL == procstrm) { save_errno = errno; buf->code = save_errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Could not open /proc//cmdline"), save_errno); return save_errno; } FGETS(cmdbuf, GTM_PATH_MAX, procstrm, csrv); if (NULL == csrv) { save_errno = errno; buf->code = save_errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Could not read /proc//cmdline"), save_errno); return save_errno; } FCLOSE(procstrm, clrv); if (-1 == clrv) /* Not a functional issue so just warn about it in op-log */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) MAKE_MSG_WARNING(ERR_SYSCALL), 5, LEN_AND_LIT("fclose()"), CALLFROM, errno); lnln = STRLEN(cmdbuf); /* Look from the end backwards to find the last '/' to isolate the directory */ for (cptr = cmdbuf + lnln - 1; (cptr >= cmdbuf) && ('/' != *cptr); cptr--) ; cmdbufln = INTCAST(cptr - cmdbuf); if (0 < cmdbufln) { /* Normalize the directory via realpath so comparison possible */ cmdbuf[cmdbufln] = rpcmdbuf[0] = '\0'; csrv = realpath(cmdbuf, rpcmdbuf); cmdbufln = STRLEN(rpcmdbuf); if ((cmdbufln == rundir_len) && (0 == memcmp(rundir, rpcmdbuf, cmdbufln))) { buf->code = 0; /* Successful validation */ DEBUG_ONLY(util_out_print("Successful validation of target processid !UL", OPER, buf->pid)); return 0; } } /* Check #1 failed - attempt check #2 - read /proc//maps to see if libgtmshr is there */ memcpy(ppptr_save, PROCPATH_MAPSSUFFIX, SIZEOF(PROCPATH_MAPSSUFFIX)); /* Copy includes terminating null of literal */ Fopen(procstrm, procpath, "r"); if (NULL == procstrm) { save_errno = errno; buf->code = save_errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_LITERAL("Server"), process_id, buf->pid, save_code, buf->mesg.id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Could not open /proc//cmdline"), save_errno); return save_errno; } /* Insert map reading code TODO */ FCLOSE(procstrm, clrv); if (-1 == clrv) /* Not a functional issue so just warn about it in op-log */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) MAKE_MSG_WARNING(ERR_SYSCALL), 5, LEN_AND_LIT("fclose()"), CALLFROM, errno); # endif return 0; } #ifdef __MVS__ boolean_t gtm_tag_error(char *filename, int realtag, int desiredtag) { char *errmsg; errmsg = STRERROR(errno); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_BADTAG, 4, LEN_AND_STR(filename), realtag, desiredtag, ERR_TEXT, 2, RTS_ERROR_STRING(errmsg)); return FALSE; } #endif fis-gtm-V7.0-005/sr_unix/gtmsecshr.h0000755000032200000250000001124414342376330016216 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GTMSECSHR_INCLUDED #define GTMSECSHR_INCLUDED /* To enable debugging of gtmsecshr, uncomment #define immediately below */ /* #define DEBUG_GTMSECSHR */ #ifdef DEBUG_GTMSECSHR # define LOGFLAGS (LOG_USER | LOG_INFO) # define DBGGSSHR(x) syslog x #else # define DBGGSSHR(x) #endif #define ABSOLUTE_PATH(X) ('/' == X[0]) #define GTMSECSHR_MESG_TIMEOUT 30 * MILLISECS_IN_SEC #define GTMSECSHR_PERMS 0666 /* Exit codes from gtmsecshr - note matching text entries are in message table in secshr_client.c */ #define NORMALEXIT 0 #define SETUIDROOT 1 #define INVTRANSGTMSECSHR 2 #define UNABLETOEXECGTMSECSHR 3 #define GNDCHLDFORKFLD 4 #define SEMGETERROR 5 #define SEMAPHORETAKEN 6 #define SYSLOGHASERRORDETAIL 7 #define UNABLETOCHDIR 8 #define UNABLETODETERMINEPATH 9 #define NOTGTMSECSHR 10 #define BADGTMDISTDIR 11 #define LASTEXITCODE 11 /* Should have same value as last error code */ /* return codes with gtmsecshr*/ #define INVLOGNAME 20 #define BINDERR 21 #define SOCKETERR 22 #define UNLINKERR 23 #define FTOKERR 24 /* special flag from gtmsecshr_sock_init for client if could not get normal socket name */ #define ONETIMESOCKET -1 /* arguments for gtmsecshr_sock_init */ #define SERVER 0 #define CLIENT 1 #define GTMSECSHR_SOCK_DIR GTM_TMP_ENV #define DEFAULT_GTMSECSHR_SOCK_DIR DEFAULT_GTM_TMP #define GTMSECSHR_SOCK_PREFIX "gtm_secshr" #define GTMSECSHR_DIR_SUFFIX "/gtmsecshrdir" #define GTMSECSHR_EXECUTABLE "gtmsecshr" #define ROOTUID 0 #ifdef SHORT_GTMSECSHR_TIMEOUT # define MAX_TIMEOUT_VALUE 30 #else # ifdef DEBUG # define MAX_TIMEOUT_VALUE 60 /* Give secshr timeout/startup some excercise in DEBUG mode */ # else # define MAX_TIMEOUT_VALUE 6000 # endif #endif #define MAX_ID_LEN 8 #define MAX_MESG 2048 #define MAX_SECSHR_SOCKFILE_NAME_LEN (SIZEOF(GTMSECSHR_SOCK_PREFIX) + MAX_DIGITS_IN_INT) typedef struct ipcs_mesg_struct { boolean_t open_fd_with_o_direct; /* if TRUE, gtmsecshr will open db file with O_DIRECT */ int semid; int shmid; time_t gt_sem_ctime; time_t gt_shm_ctime; unsigned int fn_len; char fn[GTM_PATH_MAX]; } ipcs_mesg; typedef struct gtmsecshr_mesg_struct { int code; /* To gtmsecshr: requested gtmsecshr_mesg_type function code. * From gtmsecshr: return code (0 or errno). */ unsigned int comkey; /* Unique key per version keeps from having cross-version issues */ boolean_t usesecshr; /* Copy of client's gtm_usesecshr flag. Only used in debug build but always kept * for alignment. */ pid_t pid; /* Process id of sender */ unsigned long seqno; /* Used only by client to validate response is for message sent */ union { int4 id; /* Can be pid, semid or shmid */ char path[GTM_PATH_MAX]; ipcs_mesg db_ipcs; } mesg; } gtmsecshr_mesg; /* include for offsetof() */ #define GTM_MESG_HDR_SIZE offsetof(gtmsecshr_mesg, mesg.id) /* Version V6.0-000 largely re-built the interface between gtmsecshr client and server. Later versions should strive to * not change the order or placement of the message codes below. If a message becomes obsolete, rename the code to be * prefixed with "UNUSED_". This is so for future versions, if a security bug is found, we can take the source, compile * it for the relevant version and refresh just this module (assuming the client doesn't have issues). */ enum gtmsecshr_mesg_type { /* Starting here, these are request codes put in mesg.code. They are returned unchanged except in case of error */ WAKE_MESSAGE = 1, REMOVE_SEM, REMOVE_SHM, REMOVE_FILE, CONTINUE_PROCESS, FLUSH_DB_IPCS_INFO, /* From here down are response codes. These codes are never processed but all except INVALID_COMMAND (for which there is * no response) can be returned to client. */ INVALID_COMMAND = 0x8000, /* No response given */ INVALID_COMKEY }; int validate_receiver(gtmsecshr_mesg *buf, char *rundir, int rundir_len, int save_code); void service_request(gtmsecshr_mesg *buf, int msglen, char *rundir, int rundir_len); int4 gtmsecshr_sock_init(int caller); void gtmsecshr_sock_cleanup(int); int4 gtmsecshr_pathname_init(int caller, char *execpath, int execpathln); int continue_proc(pid_t pid); #endif fis-gtm-V7.0-005/sr_unix/gtmsecshr_sock_cleanup.c0000755000032200000250000000335114342376330020737 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include #include #include "gtm_socket.h" #include "gtm_stdio.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "gtm_limits.h" #include "io.h" #include "gtmsecshr.h" #include "error.h" #include "send_msg.h" #include "gtmio.h" GBLREF int gtmsecshr_sockfd; GBLREF struct sockaddr_un gtmsecshr_sock_name; GBLREF struct sockaddr_un gtmsecshr_cli_sock_name; GBLREF uint4 process_id; GBLREF boolean_t gtmsecshr_sock_init_done; error_def(ERR_GTMSECSHR); error_def(ERR_TEXT); void gtmsecshr_sock_cleanup(int caller) { int save_errno; struct sockaddr_un *sock_ptr; int rc; /* Close the secshr client socket */ if (FD_INVALID != gtmsecshr_sockfd) CLOSEFILE_RESET(gtmsecshr_sockfd, rc); /* resets "gtmsecshr_sockfd" to FD_INVALID */ /* do the unlink */ sock_ptr = (CLIENT == caller) ? >msecshr_cli_sock_name : >msecshr_sock_name; if (('\0' != sock_ptr->sun_path[0]) && (-1 == UNLINK(sock_ptr->sun_path)) && (ENOENT != errno)) { save_errno = errno; send_msg(VARLSTCNT(12) ERR_GTMSECSHR, 1, process_id, ERR_TEXT, 2, RTS_ERROR_TEXT("unlinking socket"), ERR_TEXT, 2, RTS_ERROR_STRING(sock_ptr->sun_path), save_errno); } sock_ptr->sun_path[0] = '\0'; /* Even if error unlinking since it is useless now */ gtmsecshr_sock_init_done = FALSE; } fis-gtm-V7.0-005/sr_unix/gtmsecshr_sock_init.c0000644000032200000250000002763014342376330020256 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_ipc.h" #include "gtm_stat.h" #include "gtm_un.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_socket.h" #include "gtm_limits.h" #include "gtm_logicals.h" #include "io.h" #include "error.h" #include "gtmsecshr.h" #include "gtmimagename.h" #include "iosp.h" #include "send_msg.h" #include "getjobnum.h" #include "gtmmsg.h" #include "trans_log_name.h" #include "eintr_wrappers.h" #include "gtm_permissions.h" GBLREF struct sockaddr_un gtmsecshr_sock_name; GBLREF struct sockaddr_un gtmsecshr_cli_sock_name; GBLREF key_t gtmsecshr_key; GBLREF int gtmsecshr_sockpath_len; GBLREF int gtmsecshr_cli_sockpath_len; GBLREF mstr gtmsecshr_pathname; GBLREF boolean_t gtmsecshr_sock_init_done; GBLREF uint4 process_id; GBLREF int gtmsecshr_sockfd; GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF boolean_t gtm_dist_ok_to_use; LITREF gtmImageName gtmImageNames[]; static char gtmsecshr_sockpath[GTM_PATH_MAX]; static char gtmsecshr_path[GTM_PATH_MAX]; static char hex_table[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; unsigned char *mypid2ascx(unsigned char *, pid_t); #ifndef SUN_LEN # define SUN_LEN(x) SIZEOF(*x) #else # define EXACT_SIZE_SOCKNAME #endif error_def(ERR_GTMDISTUNVERIF); error_def(ERR_GTMSECSHRSOCKET); error_def(ERR_LOGTOOLONG); error_def(ERR_TEXT); int4 gtmsecshr_pathname_init(int caller, char *execpath, int execpathln) { int ret_status = 0, status, len; char *dir_error_mesg, *error_mesg; mstr secshrsock_lognam, secshrsock_transnam; struct stat buf; int4 max_sock_path_len; if (!process_id) getjobnum(); if (!gtm_dist_ok_to_use) if (SERVER == caller) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_GTMDISTUNVERIF, 4, STRLEN(gtm_dist), gtm_dist, gtmImageNames[image_type].imageNameLen, gtmImageNames[image_type].imageName); else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_GTMDISTUNVERIF, 4, STRLEN(gtm_dist), gtm_dist, gtmImageNames[image_type].imageNameLen, gtmImageNames[image_type].imageName); secshrsock_lognam.addr = GTMSECSHR_SOCK_DIR; secshrsock_lognam.len = SIZEOF(GTMSECSHR_SOCK_DIR) - 1; /* Get the maximum size of the path excluding the socket filename */ max_sock_path_len = SIZEOF(gtmsecshr_sock_name.sun_path) - MAX_SECSHR_SOCKFILE_NAME_LEN; /* Make sure this length is atmost equal to the size of the buffer that will hold the socket path */ if (GTM_PATH_MAX < max_sock_path_len) max_sock_path_len = GTM_PATH_MAX - MAX_SECSHR_SOCKFILE_NAME_LEN; /* Get the value of the GTMSECSHR_SOCK_DIR logical from the environment. status will be SS_LOG2LONG if * the value is greater than max_sock_path_len */ status = TRANS_LOG_NAME(&secshrsock_lognam, &secshrsock_transnam, gtmsecshr_sockpath, max_sock_path_len, do_sendmsg_on_log2long); if ((SS_NORMAL != status) || !ABSOLUTE_PATH(gtmsecshr_sockpath)) { if (SS_LOG2LONG == status) { if (SERVER == caller) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_LOGTOOLONG, 3, secshrsock_lognam.len, secshrsock_lognam.addr, max_sock_path_len); else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_LOGTOOLONG, 3, secshrsock_lognam.len, secshrsock_lognam.addr, max_sock_path_len); } ret_status = INVLOGNAME; strcpy(gtmsecshr_sockpath, DEFAULT_GTMSECSHR_SOCK_DIR); gtmsecshr_sockpath_len = SIZEOF(DEFAULT_GTMSECSHR_SOCK_DIR) - 1; } else gtmsecshr_sockpath_len = secshrsock_transnam.len; if ((-1 == Stat(gtmsecshr_sockpath, &buf)) || !S_ISDIR(buf.st_mode) ) { if (ret_status) { dir_error_mesg = NULL; error_mesg = "Unable to locate default tmp directory"; } else { dir_error_mesg = malloc(GTM_PATH_MAX); SNPRINTF(dir_error_mesg, GTM_PATH_MAX, "$gtm_tmp (%s) is not a directory", gtmsecshr_sockpath); error_mesg = dir_error_mesg; } if (SERVER == caller) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) MAKE_MSG_SEVERE(ERR_GTMSECSHRSOCKET), 3, RTS_ERROR_STRING((SERVER == caller) ? "Server" : "Caller"), process_id, ERR_TEXT, 2, RTS_ERROR_STRING(error_mesg)); else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) MAKE_MSG_SEVERE(ERR_GTMSECSHRSOCKET), 3, RTS_ERROR_STRING((SERVER == caller) ? "Server" : "Caller"), process_id, ERR_TEXT, 2, RTS_ERROR_STRING(error_mesg)); if (dir_error_mesg) free(dir_error_mesg); return INVLOGNAME; } ret_status = 0; if ('/' != gtmsecshr_sockpath[gtmsecshr_sockpath_len - 1]) gtmsecshr_sockpath[gtmsecshr_sockpath_len++] = '/'; gtmsecshr_sockpath[gtmsecshr_sockpath_len] = '\0'; strcpy(gtmsecshr_sockpath + gtmsecshr_sockpath_len , GTMSECSHR_SOCK_PREFIX); gtmsecshr_sockpath_len += (SIZEOF(GTMSECSHR_SOCK_PREFIX) - 1); /* Servers have already determined the executable name; clients use path name discovered by gtmsecshr_init(). */ gtmsecshr_pathname.len = SNPRINTF(gtmsecshr_path, GTM_PATH_MAX, "%s/%s", (SERVER == caller) ? execpath : gtm_dist, GTMSECSHR_EXECUTABLE); gtmsecshr_pathname.addr = gtmsecshr_path; /* We have different project id here. This guarantees to avoid deadlock, if only one gtm installation is there */ if (-1 == (gtmsecshr_key = FTOK(gtmsecshr_path, GTMSECSHR_ID))) { ret_status = FTOKERR; if (SERVER == caller) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(14) ERR_GTMSECSHRSOCKET, 3, RTS_ERROR_STRING((SERVER == caller) ? "Server" : "Caller"), process_id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with gtmsecshr ftok :"), ERR_TEXT, 2, RTS_ERROR_STRING(gtmsecshr_path), errno); else send_msg_csa(CSA_ARG(NULL) VARLSTCNT(14) ERR_GTMSECSHRSOCKET, 3, RTS_ERROR_STRING((SERVER == caller) ? "Server" : "Caller"), process_id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with gtmsecshr ftok :"), ERR_TEXT, 2, RTS_ERROR_STRING(gtmsecshr_path), errno); } return ret_status; } /* Note - only the server passes in the executable name/len - ignore for client */ int4 gtmsecshr_sock_init(int caller) { int ret_status = 0; int save_errno; int id_str_len; int4 init_pathname_status; unsigned int gtmsecshr_cli_sockpath_end; unsigned char id_str[MAX_ID_LEN+1], suffix; unsigned char pid_str[2 * SIZEOF(pid_t) + 1]; int i2hex_nofill(int , uchar_ptr_t, int); int stat_res; struct stat stat_buf; struct stat dist_stat_buff; int lib_gid; assert(FALSE == gtmsecshr_sock_init_done); if (!process_id) getjobnum(); if (CLIENT == caller) { if (0 != (init_pathname_status = gtmsecshr_pathname_init(CLIENT, NULL, 0))) return init_pathname_status; gtmsecshr_cli_sock_name.sun_family = AF_UNIX; memcpy(gtmsecshr_cli_sock_name.sun_path, gtmsecshr_sockpath, gtmsecshr_sockpath_len); strcpy(gtmsecshr_cli_sock_name.sun_path + gtmsecshr_sockpath_len, (char *)mypid2ascx(pid_str, process_id)); gtmsecshr_cli_sockpath_len = (int)(SUN_LEN(>msecshr_cli_sock_name)); } id_str[i2hex_nofill((unsigned int)gtmsecshr_key, (uchar_ptr_t )id_str, MAX_ID_LEN)] = 0; id_str_len = STRLEN((char *)id_str); memcpy(gtmsecshr_sockpath + gtmsecshr_sockpath_len, (char *)id_str, id_str_len); gtmsecshr_sockpath_len += id_str_len; gtmsecshr_sock_name.sun_family = AF_UNIX; memcpy(gtmsecshr_sock_name.sun_path, gtmsecshr_sockpath, gtmsecshr_sockpath_len); gtmsecshr_sockpath_len = (int)(SUN_LEN(>msecshr_sock_name)); if (FD_INVALID == (gtmsecshr_sockfd = socket(AF_UNIX, SOCK_DGRAM, 0))) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(10) ERR_GTMSECSHRSOCKET, 3, RTS_ERROR_STRING((SERVER == caller) ? "Server" : "Caller"), process_id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with gtmsecshr socket create"), errno); ret_status = SOCKETERR; } if (SERVER == caller) { if (!ret_status) { if (-1 == UNLINK(gtmsecshr_sock_name.sun_path)) { if (ENOENT != errno) { save_errno = errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_GTMSECSHRSOCKET, 3, RTS_ERROR_STRING((SERVER == caller) ? "Server" : "Caller"), process_id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error unlinking leftover gtmsecshr socket"), save_errno); ret_status = UNLINKERR; } } } if (!ret_status) { if (0 > BIND(gtmsecshr_sockfd, (struct sockaddr *)>msecshr_sock_name, gtmsecshr_sockpath_len)) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(10) ERR_GTMSECSHRSOCKET, 3, RTS_ERROR_STRING((SERVER == caller) ? "Server" : "Caller"), process_id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with gtmsecshr socket bind"), errno); ret_status = BINDERR; } } } else /* CLIENT */ { for (suffix = '\0'; !ret_status && 'z' >= suffix; ) { if (-1 == UNLINK(gtmsecshr_cli_sock_name.sun_path)) { if (EPERM == errno || EACCES == errno) { if (!suffix) { suffix = 'a'; gtmsecshr_cli_sockpath_end = strnlen(gtmsecshr_cli_sock_name.sun_path, sizeof(gtmsecshr_cli_sock_name.sun_path)); assert(sizeof(gtmsecshr_cli_sock_name.sun_path) > (1 + gtmsecshr_cli_sockpath_end)); gtmsecshr_cli_sock_name.sun_path[gtmsecshr_cli_sockpath_end + 1] = '\0'; # ifdef EXACT_SIZE_SOCKNAME gtmsecshr_cli_sockpath_len++; /* Account for socket name growth (suffix) */ # endif } else suffix++; gtmsecshr_cli_sock_name.sun_path[gtmsecshr_cli_sockpath_end] = suffix; continue; } else if (ENOENT != errno) { save_errno = errno; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_GTMSECSHRSOCKET, 3, RTS_ERROR_STRING((SERVER == caller) ? "Server" : "Caller"), process_id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error unlinking leftover gtmsecshr_cli socket"), save_errno); ret_status = UNLINKERR; } else break; } else break; } if ( 'z' < suffix) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_GTMSECSHRSOCKET, 3, RTS_ERROR_LITERAL("Client"), process_id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Too many left over gtmsecshr_cli sockets")); ret_status = UNLINKERR; } if (!ret_status) { if (0 > BIND(gtmsecshr_sockfd, (struct sockaddr *)>msecshr_cli_sock_name, gtmsecshr_cli_sockpath_len)) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(10) ERR_GTMSECSHRSOCKET, 3, RTS_ERROR_STRING((SERVER == caller) ? "Server" : "Caller"), process_id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with gtmsecshr_cli socket bind"), errno); ret_status = BINDERR; } else if ('\0' != suffix) ret_status = ONETIMESOCKET; /* If ret_status is zero do the following checks if $gtm_dist/libgtmshr.so is not world accessible * then set mode to 0660 and change the gid to the gid of $gtm_dist/libgtmshr.so if different from * current user. */ if (!ret_status) { lib_gid = gtm_get_group_id(&dist_stat_buff); if ((-1 != lib_gid) && (dist_stat_buff.st_mode & 04)) lib_gid = -1; /* don't change it */ if ((-1 != lib_gid) /* 4SCA: TOCTOU cannot use fchmod/fchown on socket files */ && (-1 == CHMOD(gtmsecshr_cli_sock_name.sun_path, 0660) || ((lib_gid != GETGID()) && (-1 == CHOWN(gtmsecshr_cli_sock_name.sun_path, -1, lib_gid))))) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(10) ERR_GTMSECSHRSOCKET, 3, RTS_ERROR_STRING("Caller"), process_id, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error changing socket permissions/group"), errno); } } } } gtmsecshr_sock_init_done = TRUE; return ret_status; } unsigned char *mypid2ascx(unsigned char *pid_str, pid_t pid) { /* pid_str should accommodate at least 2 * SIZEOF(pid_t) + 1 characters */ register unsigned char *cp; cp = &pid_str[2 * SIZEOF(pid_t)]; *cp = '\0'; /* Null terminate the string */ while (cp > pid_str) { *--cp = hex_table[pid & 0xF]; pid >>= 4; } return pid_str; } fis-gtm-V7.0-005/sr_unix/gtmsecshr_wrapper.c0000644000032200000250000004353514342376330017756 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2008-2018 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. * * * ****************************************************************/ /******************************************************************************************** * W A R N I N G --- W A R N I N G --- W A R N I N G --- W A R N I N G --- W A R N I N G * * * * This routine (gtmsecshr_wrapper) runs as setuid to root to perform "environmental * * cleanup" prior to invoking gtmsecshr proper. Extreme care must be taken to prevent all * * forms of deceptive access, linking with unauthorized libraries, etc. Same applies to * * anything it calls. * * * * W A R N I N G --- W A R N I N G --- W A R N I N G --- W A R N I N G --- W A R N I N G * ********************************************************************************************/ #include "mdef.h" #define BYPASS_MEMCPY_OVERRIDE /* Signals gtm_string.h to not override memcpy(). This causes linking problems when libmumps.a * is not available. */ /* We want system malloc, not gtm_malloc (which comes from mdef.h --> mdefsp.h). Since gtmsecshr_wrapper runs as root, * using the system malloc will increase security over using gtm_malloc. Additionally, by not using gtm_malloc, we * are reducing code bloat. */ #undef malloc #undef free #include "gtm_unistd.h" #include "gtm_stat.h" #include "gtm_stdlib.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_syslog.h" #include "main_pragma.h" #include "gtm_signal.h" #ifndef __MVS__ # include #endif #include #define WBOX_GBLDEF /* Causes whitebox global vars to be GBLDEF'd instead of GBLREF'd thus keeping gbldefs.c out * of gtmsecshr_wrapper the executable. */ #include "wbox_test_init.h" #include "gtm_limits.h" #define ROOTUID 0 #define ROOTGID 0 #define MAX_ENV_VAR_VAL_LEN 1024 #define MAX_ALLOWABLE_LEN 256 #define ASCIICTLMAX 32 /* space character */ #define ASCIICTLMIN 0 /* NULL character */ #define GTM_TMP "gtm_tmp" #define GTM_DIST "gtm_dist" #define SUB_PATH_TO_GTMSECSHRDIR "/gtmsecshrdir" #define REL_PATH_TO_CURDIR "." #define REL_PATH_TO_GTMSECSHR "./gtmsecshr" #define GTMSECSHR_BASENAME "/gtmsecshr" #define MAX_ENV_NAME_LEN 2048 #define PERMALL 07777 /* Build up some defines for use with AIX and reading /etc/environment to pick up a default TZ value plus some defines * for testing AIX errors in this wrapper with white box test cases. */ #ifdef _AIX #define GTM_TZ "TZ" #define TZLOCATOR "TZ=" #define NEWLINE 0x0a #define GTM_WHITE_BOX_TEST_CASE_ENABLE "gtm_white_box_test_case_enable" #define GTM_WHITE_BOX_TEST_CASE_NUMBER "gtm_white_box_test_case_number" #define GTMETCDIRPATH "/etc" #define BADGTMETCDIRPATH "/bogusdirnotetc" #define GTMENVIRONFILE "environment" #define BADGTMENVIRONFILE "bogusfilenotenvironment" #endif typedef char **environptr_t; extern char **environ; /* Since gtmsecshr_wrapper.c is a stand-alone module, we cannot use error_def-style definitions, so use simple macro defines to * initialize all error types used along with their respective mnemonics. Note that we need two '%' to ensure the '%' prefix * in the syslog. */ #define ERR_SECSHRCLEARENVFAILED \ "%%GTM-E-SECSHRCLEARENVFAILED, clearenv failed. gtmsecshr will not be started\n" #define ERR_SECSHRCHDIRFAILED1 \ "%%GTM-E-SECSHRCHDIRFAILED1, chdir failed on %s, errno %d. gtmsecshr will not be started\n" #define ERR_SECSHRCHDIRFAILED2 \ "%%GTM-W-SECSHRCHDIRFAILED2, chdir failed on %s, errno %d. gtmsecshr will be started with GMT timezone\n" #define ERR_SECSHREXECLFAILED \ "%%GTM-E-SECSHREXECLFAILED, execl of %s failed\n" #define ERR_SECSHRGTMDIST2LONG \ "%%GTM-E-SECSHRGTMDIST2LONG, gtm_dist env var too long. gtmsecshr will not be started\n" #define ERR_SECSHRGTMTMP2LONG \ "%%GTM-E-SECSHRGTMTMP2LONG, gtm_tmp env var too long. gtmsecshr will not be started\n" #define ERR_SECSHRNOGTMDIST \ "%%GTM-E-SECSHRNOGTMDIST, gtm_dist env var does not exist. gtmsecshr will not be started\n" #define ERR_SECSHRNOTOWNEDBYROOT \ "%%GTM-E-SECSHRNOTOWNEDBYROOT, %s not owned by root. gtmsecshr will not be started\n" #define ERR_SECSHRNOTSETUID \ "%%GTM-E-SECSHRNOTSETUID, %s not set-uid. gtmsecshr will not be started\n" #define ERR_SECSHRPERMINCRCT \ "%%GTM-E-SECSHRPERMINCRCT, %s permissions incorrect (%04o). gtmsecshr will not be started\n" #define ERR_SECSHRSETGTMDISTFAILED \ "%%GTM-E-SECSHRSETGTMDISTFAILED, setenv for gtm_dist failed. gtmsecshr will not be started\n" #define ERR_SECSHRSETGTMTMPFAILED \ "%%GTM-E-SECSHRSETGTMTMPFAILED, setenv for gtm_tmp failed. gtmsecshr will not be started\n" #define ERR_SECSHRSETUIDFAILED \ "%%GTM-E-SECSHRSETUIDFAILED, setuid failed. gtmsecshr will not be started\n" #define ERR_SECSHRSTATFAILED \ "%%GTM-E-SECSHRSTATFAILED, stat failed on %s, errno %d. gtmsecshr will not be started\n" #define ERR_SECSHRTZFAIL \ "%%GTM-W-SECSHRTZFAIL, %s %d. gtmsecshr will start with TZ set to GMT\n" #define ERR_SECSHRWRITABLE \ "%%GTM-E-SECSHRWRITABLE, %s writable. gtmsecshr will not be started\n" /* Make sure these are synced with the above. We need this comment for the InfoHub tools to generate message information for gtmsecshr_wrapper.c, since it does not have a stand-alone .msg file. .FACILITY GTMSECSHRINIT,251/PREFIX=ERR_ SECSHRCHDIRFAILED1 /error/fao=3!/ansi=0 SECSHRCHDIRFAILED2 /warning/fao=3!/ansi=0 SECSHRCLEARENVFAILED /error/fao=0!/ansi=0 SECSHREXECLFAILED /error/fao=2!/ansi=0 SECSHRGTMDIST2LONG /error/fao=0!/ansi=0 SECSHRGTMTMP2LONG /error/fao=0!/ansi=0 SECSHRNOGTMDIST /error/fao=0!/ansi=0 SECSHRNOTOWNEDBYROOT /error/fao=3!/ansi=0 SECSHRNOTSETUID /error/fao=2!/ansi=0 SECSHRPERMINCRCT /error/fao=3!/ansi=0 SECSHRSETGTMDISTFAILED /error/fao=0!/ansi=0 SECSHRSETGTMTMPFAILED /error/fao=0!/ansi=0 SECSHRSETUIDFAILED /error/fao=0!/ansi=0 SECSHRSTATFAILED /error/fao=3!/ansi=0 SECSHRTZFAIL /warning/fao=3!/ansi=0 SECSHRWRITABLE /error/fao=2!/ansi=0 ! the following line stops getmsginfo.m .end */ void strsanitize(char *src, char *dst); void strsanitize(char *src, char *dst) { int i, srclen; /* The calling function already validates the string length. */ srclen = strlen(src); for (i = 0; (i <= srclen) && (MAX_ENV_VAR_VAL_LEN > i); i++) { /* Convert all control characters to '*'. */ if ((ASCIICTLMAX > (int)src[i]) && (ASCIICTLMIN < (int)src[i])) dst[i] = '*'; else dst[i] = src[i]; } } int main() { int ret, status; char *env_var_ptr; struct stat gtm_secshrdir_stat; struct stat gtm_secshr_stat; char gtm_dist_val[MAX_ENV_VAR_VAL_LEN]; char gtm_tmp_val[MAX_ENV_VAR_VAL_LEN]; char gtm_secshrdir_path[MAX_ENV_VAR_VAL_LEN]; char gtm_secshrdir_path_display[MAX_ENV_VAR_VAL_LEN]; char gtm_secshr_path[MAX_ENV_VAR_VAL_LEN]; char gtm_secshr_path_display[MAX_ENV_VAR_VAL_LEN]; char gtm_secshr_orig_path[MAX_ENV_VAR_VAL_LEN]; boolean_t gtm_tmp_exists = FALSE; int rc; sigset_t mask; # ifdef _AIX FILE *envfile; int recnum, reclen, save_errno; char *fgets_rc; boolean_t gtm_TZ_found; char gtm_TZ_val[MAX_ENV_VAR_VAL_LEN + 1], *gtm_TZ_val_ptr; char *etcdirpath, *environfile; # endif /* Reset the signal mask (since the one inherited from the invoking process might have signals such as SIGALRM or SIGTERM * blocked) to let gtmsecshr manage its own signals using sig_init. */ sigemptyset(&mask); sigprocmask(SIG_SETMASK, &mask, NULL); /* BYPASSOK(sigprocmask) */ OPENLOG("GTMSECSHRINIT", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_USER); # ifdef _AIX # ifdef DEBUG /* Use some very simplistic processing to obtain values for $gtm_white_box_test_case_enable/number since we are basically * standalone in this routine without the ability to call into other mumps routines. We fetch the value and convert it * numerically as best as possible. For the boolean enable flag, If it's non-zero - it's true else false. No errors raised * here for conversions. */ env_var_ptr = getenv(GTM_WHITE_BOX_TEST_CASE_ENABLE); if (NULL != env_var_ptr) { gtm_white_box_test_case_enabled = atoi(env_var_ptr); if (gtm_white_box_test_case_enabled) { env_var_ptr = getenv(GTM_WHITE_BOX_TEST_CASE_NUMBER); if (NULL != env_var_ptr) gtm_white_box_test_case_number = atoi(env_var_ptr); } } # endif /* DEBUG */ etcdirpath = !(WBTEST_ENABLED(WBTEST_SECSHRWRAP_NOETC)) ? GTMETCDIRPATH : BADGTMETCDIRPATH; environfile = !(WBTEST_ENABLED(WBTEST_SECSHRWRAP_NOENVIRON)) ? GTMENVIRONFILE : BADGTMENVIRONFILE; /* Note syslog timestamps are handled somewhat differently on AIX. If one undefines the TZ environment variable (such as * our wrapper does to prevent reporting time in the timezone of the process that started gtmsecshr, process time reverts * to GMT. So to prevent that, lookup the default timezone in /etc/environment and use it to prevent whacky time reporting * by gtmsecshr in the operator log. Secure file read method is to switch dir first, then do relative open. */ gtm_TZ_found = FALSE; if (-1 == CHDIR(etcdirpath)) /* Note chdir is changed again below so this is only temporary */ SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRCHDIRFAILED2, etcdirpath, errno); else { envfile = fopen(environfile, "r"); if (NULL == envfile) SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRTZFAIL, "Unable to open /etc/environment errno:", errno); else { /* /etc/environments is open, locate TZ= record */ for (recnum = 0; ; recnum++) { FGETS(gtm_TZ_val, MAX_ENV_VAR_VAL_LEN, envfile, fgets_rc); if (NULL == fgets_rc) break; if (STRLEN(TZLOCATOR) >= (reclen = STRLEN(gtm_TZ_val))) /* Note assignment */ continue; if (0 == memcmp(TZLOCATOR, gtm_TZ_val, STRLEN(TZLOCATOR))) { gtm_TZ_found = TRUE; break; } } if (!gtm_TZ_found DEBUG_ONLY(|| WBTEST_ENABLED(WBTEST_SECSHRWRAP_NOTZREC_READERR) || WBTEST_ENABLED(WBTEST_SECSHRWRAP_NOTZREC_EOF))) { /* We didn't find the TZ record - report it depending if we just hit EOF or something more * severe. */ save_errno = errno; # if DEBUG /* Since we may have come here via the white box tests, let's ensure gtm_TZ_found is FALSE */ gtm_TZ_found = FALSE; /* Separately test for some white box conditions before testing the real return codes */ if (WBTEST_ENABLED(WBTEST_SECSHRWRAP_NOTZREC_READERR)) SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRTZFAIL, "FGETS() failure reading /etc/environment " "errno:", 999); else if (WBTEST_ENABLED(WBTEST_SECSHRWRAP_NOTZREC_EOF)) SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRTZFAIL, "File /etc/environment has no default TZ ", 0); /* This puts a spurious 0 out with the message but this condition should * be so rare as to be irrelevant and not worth a separate message. */ else # endif if (!feof(envfile)) SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRTZFAIL, "FGETS() failure reading /etc/environment " "errno:", save_errno); else /* Have EOF - didn't find TZ */ SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRTZFAIL, "File /etc/environment has no default TZ ", 0); /* This puts a spurious 0 out with the message but this condition should * be so rare as to be irrelevant and not worth a separate message. */ } else { /* TZ record acquired - isolate TZ value. Note we don't allocate storage for it like we would if * this were a long(er) running process but this process's sole job is to call the real gtmsecshr * so the stack storage flavor of the buffer is fine. */ if (NEWLINE == gtm_TZ_val[reclen - 1]) gtm_TZ_val[--reclen] = '\0'; /* Overwrite nl with null and decr length */ /* Set pointer to point past the TZ= part of the value pair as we use setenv() to set this value */ gtm_TZ_val_ptr = gtm_TZ_val + STRLEN(TZLOCATOR); /* In case this default TZ is different from the process that started gtmsecshr, go ahead and * establish this version now for any remaining messages that happen before we do a clearenv(). * We will re-establish the value again after the clear. Until we establish the default value * here, gtmsecshr wrapper messages will have the timezone of the invoking process. */ status = setenv(GTM_TZ, gtm_TZ_val_ptr, TRUE); if ((0 != status) || WBTEST_ENABLED(WBTEST_SECSHRWRAP_SETENVFAIL1)) { save_errno = errno; # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_SECSHRWRAP_SETENVFAIL1)) save_errno = 999; # endif SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRTZFAIL, "TZ reset with setenv() failed (1) errno:", save_errno); } } fclose(envfile); } } # endif /* _AIX */ ret = 0; /* start positive */ /* get the ones we need */ if (env_var_ptr = getenv(GTM_DIST)) /* Warning - assignment */ { if (MAX_ALLOWABLE_LEN < (strlen(env_var_ptr) + STR_LIT_LEN(SUB_PATH_TO_GTMSECSHRDIR) + STR_LIT_LEN(GTMSECSHR_BASENAME))) { SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRGTMDIST2LONG); ret = -1; } else { snprintf(gtm_dist_val, MAX_ENV_VAR_VAL_LEN, "%s", env_var_ptr); /* point the path to the real gtmsecshr - for display purposes only */ snprintf(gtm_secshr_path, MAX_ENV_VAR_VAL_LEN, "%s%s%s", env_var_ptr, SUB_PATH_TO_GTMSECSHRDIR, GTMSECSHR_BASENAME); strsanitize(gtm_secshr_path, gtm_secshr_path_display); /* point the path to the real gtmsecshrdir */ snprintf(gtm_secshrdir_path, MAX_ENV_VAR_VAL_LEN, "%s%s", env_var_ptr, SUB_PATH_TO_GTMSECSHRDIR); strsanitize(gtm_secshrdir_path, gtm_secshrdir_path_display); /* the path to gtmsecshr wrapper that we want to display in a PS listing */ snprintf(gtm_secshr_orig_path, MAX_ENV_VAR_VAL_LEN, "%s%s", env_var_ptr, GTMSECSHR_BASENAME); } } else { SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRNOGTMDIST); ret = -1; } if (env_var_ptr = getenv(GTM_TMP)) /* Warning - assignment */ { if (MAX_ALLOWABLE_LEN < strlen(env_var_ptr)) { SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRGTMTMP2LONG); ret = -1; } else { gtm_tmp_exists = TRUE; strcpy(gtm_tmp_val, env_var_ptr); } } if (!ret) { /* clear all */ # if defined(SUNOS) || defined(__CYGWIN__) environ = NULL; status = 0; # else status = clearenv(); # endif if (status) { SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRCLEARENVFAILED); ret = -1; } /* add the ones we need */ status = setenv(GTM_DIST, gtm_dist_val, TRUE); if (status) { SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRSETGTMDISTFAILED); ret = -1; } if (gtm_tmp_exists) { status = setenv(GTM_TMP, gtm_tmp_val, TRUE); if (status) { SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRSETGTMTMPFAILED); ret = -1; } } # ifdef _AIX if (gtm_TZ_found DEBUG_ONLY(|| WBTEST_ENABLED(WBTEST_SECSHRWRAP_SETENVFAIL2))) { # ifdef DEBUG if (gtm_TZ_found) /* Want to run setenv even if WBOX if TZ was found so we can find log entries */ status = setenv(GTM_TZ, gtm_TZ_val_ptr, TRUE); if (WBTEST_ENABLED(WBTEST_SECSHRWRAP_SETENVFAIL2)) status = -1; # else status = setenv(GTM_TZ, gtm_TZ_val_ptr, TRUE); # endif if (0 != status) { save_errno = errno; # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_SECSHRWRAP_SETENVFAIL2)) save_errno = 999; # endif SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRTZFAIL, "TZ reset with setenv() failed (2) errno:", save_errno); } } # endif } if (!ret) { /* go to root */ if (-1 == CHDIR(gtm_secshrdir_path)) SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRCHDIRFAILED1, gtm_secshrdir_path_display, errno); else if (-1 == Stat(REL_PATH_TO_CURDIR, >m_secshrdir_stat)) SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRSTATFAILED, gtm_secshrdir_path_display, errno); else if (ROOTUID != gtm_secshrdir_stat.st_uid) SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRNOTOWNEDBYROOT, gtm_secshrdir_path_display); else if (gtm_secshrdir_stat.st_mode & 0277) SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRPERMINCRCT, gtm_secshrdir_path_display, gtm_secshrdir_stat.st_mode & PERMALL); else if (-1 == Stat(REL_PATH_TO_GTMSECSHR, >m_secshr_stat)) SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRSTATFAILED, gtm_secshr_path_display, errno); else if (ROOTUID != gtm_secshr_stat.st_uid) SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRNOTOWNEDBYROOT, gtm_secshr_path_display); else if (gtm_secshr_stat.st_mode & 022) SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRWRITABLE, gtm_secshr_path_display); else if (!(gtm_secshr_stat.st_mode & 04000)) SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRNOTSETUID, gtm_secshr_path_display); else if (-1 == setuid(ROOTUID)) SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHRSETUIDFAILED); else { /* call the real gtmsecshr, but have ps display the original gtmsecshr location */ ret = execl(REL_PATH_TO_GTMSECSHR, gtm_secshr_orig_path, NULL); if (-1 == ret) SYSLOG(LOG_USER | LOG_INFO, ERR_SECSHREXECLFAILED, gtm_secshr_path_display); } } CLOSELOG(); return ret; } fis-gtm-V7.0-005/sr_unix/gtmshr_symbols.exp0000755000032200000250000000050314342376330017634 0ustar librarygtcgtm_main gtm_init gtm_jinit gtm_ci gtm_cij gtm_cip gtm_exit gtm_zstatus gtm_hiber_start gtm_hiber_start_wait_any gtm_start_timer gtm_cancel_timer gtm_malloc gtm_free gtm_filename_to_id gtm_is_file_identical gtm_xcfileid_free gtm_is_main_thread accumulate to_ulong is_big_endian op_read dm_read io_curr_device io_std_device fis-gtm-V7.0-005/sr_unix/gtmsiginfo.h0000755000032200000250000000520714342376330016367 0ustar librarygtc/**************************************************************** * * * 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. * * * ****************************************************************/ #ifndef GTM_SIGNAL_INFO_H #define GTM_SIGNAL_INFO_H /* Define signal information structures and routines */ #ifdef __sparc # include #endif # include typedef struct { caddr_t int_iadr; /* Interrupted instruction address */ caddr_t bad_vadr; /* Failing virtual address */ int sig_err; /* Error message with signal reason clarification */ pid_t send_pid; /* Process that sent signal */ uid_t send_uid; /* Userid that sent signal */ int subcode; /* Subcode used to create message */ int infotype; /* mask of what types of information are available */ int signal; /* Actual signal number */ } gtmsiginfo_t; /* Info types that are available (mask) */ #define GTMSIGINFO_NONE 0 /* No further information is available about the signals */ #define GTMSIGINFO_ILOC 0x1 /* Interrupt location information is available */ #define GTMSIGINFO_BADR 0x2 /* Bad virtual address information is available */ #define GTMSIGINFO_USER 0x4 /* User information is available */ #if defined(__osf__) typedef struct sigcontext gtm_sigcontext_t; #elif defined(_AIX) typedef struct sigcontext64 gtm_sigcontext_t; #else typedef ucontext_t gtm_sigcontext_t; #endif void extract_signal_info(int sig, siginfo_t *info, gtm_sigcontext_t *context, gtmsiginfo_t *gtmsi); #define NO_SUSPEND 0 /* Suspend not pending */ #define DEFER_SUSPEND 1 /* Suspend deferred */ #define NOW_SUSPEND 2 /* Suspend now; will indicate that we "suspended" ourselves when woken up */ /* States of exit_state * * Normal state is no exit is pending. When we receive a signal, we are either going * to go into a pending state (wait until out of crit) or an immediate state (going * to exit now). There are two different pending states. In the first, we can tolerate * another (2nd) signal which will bump us to final pending state. If a 3rd signal * comes in, we will go to the immediate exit state [tolerant pending state added at the * request of Roger 4/2000]. If one of the terminal signals is received (i.e. not * sent by another user, we will go to the immediate exit state. */ #define EXIT_NOTPENDING 0 #define EXIT_PENDING_TOLERANT 1 #define EXIT_PENDING 2 #define EXIT_IMMED 3 #endif /* ifndef GTM_SIGNAL_INFO_H */ fis-gtm-V7.0-005/sr_unix/gtmsource.c0000644000032200000250000006317114342376330016225 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_inet.h" #include #include "gtm_fcntl.h" #include "gtm_unistd.h" #include #include #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdskill.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" #include "tp.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_dbg.h" #include "error.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "cli.h" #include "iosp.h" #include "repl_log.h" #include "repl_errno.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "repl_filter.h" #include "eintr_wrappers.h" #include "send_msg.h" #include "is_proc_alive.h" #include "gtmmsg.h" #include "sgtm_putmsg.h" #include "repl_comm.h" #include "repl_instance.h" #include "ftok_sems.h" #include "ftok_sem_incrcnt.h" #include "gt_timer.h" /* for LONG_SLEEP macro (hiber_start function prototype) and add_safe_timer_handler */ #include "gtmsource_heartbeat.h" /* for gtmsource_heartbeat_timer */ #include "mutex.h" #include "gtm_zlib.h" #include "fork_init.h" #include "io.h" #include "gtmio.h" #include "util.h" #include "mu_outofband_setup.h" #include "mupip_exit.h" #ifdef GTM_TLS #include "gtm_repl.h" #endif #ifdef DEBUG #include "anticipatory_freeze.h" #include "fake_enospc.h" #endif GBLDEF boolean_t gtmsource_logstats = FALSE, gtmsource_pool2file_transition = FALSE; GBLDEF int gtmsource_filter = NO_FILTER; GBLDEF boolean_t update_disable = FALSE; GBLDEF boolean_t last_seen_freeze_flag = FALSE; GBLREF gtmsource_options_t gtmsource_options; GBLREF gtmsource_state_t gtmsource_state; GBLREF boolean_t is_src_server; GBLREF boolean_t first_syslog; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF uint4 process_id; GBLREF int gtmsource_sock_fd; GBLREF int gtmsource_log_fd; GBLREF FILE *gtmsource_log_fp; GBLREF gd_addr *gd_header; GBLREF void (*call_on_signal)(); GBLREF seq_num gtmsource_save_read_jnl_seqno, seq_num_zero; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF repl_msg_ptr_t gtmsource_msgp; GBLREF int gtmsource_msgbufsiz; GBLREF unsigned char *gtmsource_tcombuff_start; GBLREF boolean_t is_jnlpool_creator; GBLREF uchar_ptr_t repl_filter_buff; GBLREF int repl_filter_bufsiz; GBLREF int gtmsource_srv_count; GBLREF gd_region *ftok_sem_reg; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; GBLREF IN_PARMS *cli_lex_in_ptr; GBLREF uint4 mutex_per_process_init_pid; GBLREF bool mu_ctrlc_occurred; error_def(ERR_JNLPOOLSETUP); error_def(ERR_MUPCLIERR); error_def(ERR_REPLNORESP); error_def(ERR_NOTALLDBOPN); error_def(ERR_NULLCOLLDIFF); error_def(ERR_REPL0BACKLOG); error_def(ERR_REPLBACKLOG); error_def(ERR_REPLCOMM); error_def(ERR_REPLERR); error_def(ERR_REPLINFO); error_def(ERR_REPLINSTFREEZECOMMENT); error_def(ERR_REPLINSTFROZEN); error_def(ERR_REPLOFFJNLON); error_def(ERR_TEXT); error_def(ERR_SHUT2QUICK); error_def(ERR_CTRLC); error_def(ERR_MUNOFINISH); int gtmsource() { int status, log_init_status, waitpid_res, save_errno; struct stat stat_buf; char print_msg[OUT_BUFF_SIZE - 1], tmpmsg[REPL_MSG_SIZE]; gd_region *reg, *region_top; sgmnt_addrs *csa, *repl_csa; boolean_t all_files_open, isalive, ftok_counter_halted; pid_t pid, ppid, procgp; seq_num chkbklogresult, read_jnl_seqno, jnl_seqno; unix_db_info *udi; gtmsource_local_ptr_t gtmsource_local; boolean_t hrtbt_recvd = FALSE, iszerobacklog = FALSE, this_side_std_null_coll, wasshut2quick = FALSE; int null_fd, rc; uint4 gtmsource_pid; int shutdowntime = 0; int4 index; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; call_on_signal = gtmsource_sigstop; ESTABLISH_RET(gtmsource_ch, SS_NORMAL); if (-1 == gtmsource_get_opt()) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MUPCLIERR); if (gtmsource_options.shut_down) { mu_outofband_setup(); shutdowntime = gtmsource_options.shutdown_time; if (gtmsource_options.zerobacklog) { if (0 < shutdowntime) { repl_log(stdout, TRUE, TRUE, "Initiating ZEROBACKLOG shutdown operation. " "Waiting for up to %d second(s) for backlog to clear\n", shutdowntime); jnlpool_init(GTMSOURCE, gtmsource_options.start, &is_jnlpool_creator, NULL); if (NULL != jnlpool->gtmsource_local) gtmsource_local = jnlpool->gtmsource_local; /* if -INST is specified */ else gtmsource_local = &jnlpool->gtmsource_local_array[0];/* if -INST is not specified */ /* Loop across all Source Server(s) or the selected Source Server and set hrtbt_recvd to FALSE */ for (index = 0; index < NUM_GTMSRC_LCL; index++, gtmsource_local++) { if ('\0' == gtmsource_local->secondary_instname[0]) { assert(NULL == jnlpool->gtmsource_local); continue; } gtmsource_pid = gtmsource_local->gtmsource_pid; /* gtmsource_pid is 0 for Source Servers that were shut down */ if ((NULL == jnlpool->gtmsource_local) && (0 == gtmsource_pid)) continue; /* Set hrtbt_recvd to FALSE. If it turns TRUE later, we have a heartbeat confirmation */ gtmsource_local->hrtbt_recvd = FALSE; if (NULL != jnlpool->gtmsource_local) break; } chkbklogresult = 1; for (; shutdowntime; shutdowntime--) { LONG_SLEEP(1); if (mu_ctrlc_occurred) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CTRLC); mupip_exit(ERR_MUNOFINISH); } chkbklogresult = gtmsource_checkforbacklog(); if (0 == chkbklogresult) { /* There is no backlog. Stop waiting and proceed with the shut down */ iszerobacklog = TRUE; break; } } /* Check and report those Source Servers where there was no heartbeat detected */ if (!iszerobacklog) { if (NULL != jnlpool->gtmsource_local) gtmsource_local = jnlpool->gtmsource_local; else gtmsource_local = &jnlpool->gtmsource_local_array[0]; for (index = 0; index < NUM_GTMSRC_LCL; index++, gtmsource_local++) { if ('\0' == gtmsource_local->secondary_instname[0]) { assert(NULL == jnlpool->gtmsource_local); continue; } gtmsource_pid = gtmsource_local->gtmsource_pid; if ((NULL == jnlpool->gtmsource_local) && (0 == gtmsource_pid)) continue; if (!gtmsource_local->hrtbt_recvd) { sgtm_putmsg(print_msg, OUT_BUFF_SIZE - 1, VARLSTCNT(6) ERR_REPLNORESP, 4, LEN_AND_STR(gtmsource_local->secondary_instname), gtmsource_options.shutdown_time); repl_log(stdout, TRUE, TRUE, print_msg); if (gtmsource_local->connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD] > gtmsource_options.shutdown_time) { sgtm_putmsg(print_msg, OUT_BUFF_SIZE - 1, VARLSTCNT(6) ERR_SHUT2QUICK, 4, gtmsource_options.shutdown_time, gtmsource_local-> connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD], LEN_AND_STR(gtmsource_local->secondary_instname)); repl_log(stdout, TRUE, TRUE, print_msg); } } /* If there is only one instance to process, break for loop in the first iteration */ if (NULL != jnlpool->gtmsource_local) break; } sgtm_putmsg(print_msg, OUT_BUFF_SIZE - 1, VARLSTCNT(1) ERR_REPLBACKLOG); repl_log(stdout, TRUE, TRUE, print_msg); } else { sgtm_putmsg(print_msg, OUT_BUFF_SIZE - 1, VARLSTCNT(1) ERR_REPL0BACKLOG); repl_log(stdout, TRUE, TRUE, print_msg); } } } else if (0 < gtmsource_options.shutdown_time) { repl_log(stdout, TRUE, TRUE, "Waiting for %d second(s) before forcing shutdown\n", gtmsource_options.shutdown_time); for (; shutdowntime; shutdowntime--) { LONG_SLEEP(1); if (mu_ctrlc_occurred) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CTRLC); mupip_exit(ERR_MUNOFINISH); } } } else repl_log(stdout, TRUE, TRUE, "Forcing immediate shutdown\n"); } else if (gtmsource_options.start) { repl_log(stdout, TRUE, TRUE, "Initiating START of source server for secondary instance [%s]\n", gtmsource_options.secondary_instname); } if (gtmsource_options.activate && (ROOTPRIMARY_SPECIFIED == gtmsource_options.rootprimary)) { /* MUPIP REPLIC -SOURCE -ACTIVATE -UPDOK has been specified. We need to open the gld and db regions now * in case this is a secondary -> primary transition. This is so we can later switch journal files in all * journaled regions when the transition actually happens inside "gtmsource_rootprimary_init". But since * we have not yet done a "jnlpool_init", we don't know if updates are disabled in it or not. Although we * need to do the gld/db open only if updates are currently disabled in the jnlpool, we do this always * because once we do a jnlpool_init, we will come back with the ftok on the jnlpool held and that has * issues with later db open since we will try to hold the db ftok as part of db open and the ftok logic * currently has assumptions that a process holds only one ftok at any point in time. */ assert(NULL == gd_header); gvinit(); all_files_open = region_init(FALSE); if (!all_files_open) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOTALLDBOPN); gtmsource_exit(ABNORMAL_SHUTDOWN); } } if (!holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]) { jnlpool_init(GTMSOURCE, gtmsource_options.start, &is_jnlpool_creator, NULL); /* is_jnlpool_creator == TRUE ==> this process created the journal pool * is_jnlpool_creator == FALSE ==> journal pool already existed and this process simply attached to it. */ } assert(jnlpool && jnlpool->pool_init); if (gtmsource_options.shut_down) gtmsource_exit(gtmsource_shutdown(FALSE, NORMAL_SHUTDOWN) - NORMAL_SHUTDOWN); else if (gtmsource_options.activate) gtmsource_exit(gtmsource_mode_change(GTMSOURCE_MODE_ACTIVE_REQUESTED) - NORMAL_SHUTDOWN); else if (gtmsource_options.deactivate) gtmsource_exit(gtmsource_mode_change(GTMSOURCE_MODE_PASSIVE_REQUESTED) - NORMAL_SHUTDOWN); else if (gtmsource_options.checkhealth) gtmsource_exit(gtmsource_checkhealth() - NORMAL_SHUTDOWN); else if (gtmsource_options.changelog) gtmsource_exit(gtmsource_changelog() - NORMAL_SHUTDOWN); else if (gtmsource_options.showbacklog) gtmsource_exit(gtmsource_showbacklog() - NORMAL_SHUTDOWN); else if (gtmsource_options.stopsourcefilter) gtmsource_exit(gtmsource_stopfilter() - NORMAL_SHUTDOWN); else if (gtmsource_options.jnlpool) gtmsource_exit(gtmsource_jnlpool() - NORMAL_SHUTDOWN); else if (gtmsource_options.losttncomplete) gtmsource_exit(gtmsource_losttncomplete() - NORMAL_SHUTDOWN); else if (gtmsource_options.needrestart) gtmsource_exit(gtmsource_needrestart() - NORMAL_SHUTDOWN); else if (gtmsource_options.showfreeze) gtmsource_exit(gtmsource_showfreeze() - NORMAL_SHUTDOWN); else if (gtmsource_options.setfreeze) gtmsource_exit(gtmsource_setfreeze() - NORMAL_SHUTDOWN); else if (!gtmsource_options.start) { assert(CLI_PRESENT == cli_present("STATSLOG")); gtmsource_exit(gtmsource_statslog() - NORMAL_SHUTDOWN); } assert(gtmsource_options.start); # ifndef REPL_DEBUG_NOBACKGROUND /* Set "child_server_running" to FALSE before forking off child. Wait for it to be set to TRUE by the child. */ gtmsource_local = jnlpool->gtmsource_local; gtmsource_local->child_server_running = FALSE; udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); FORK(pid); if (0 > pid) { save_errno = errno; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Could not fork source server"), save_errno); } else if (0 < pid) { /* Parent. Wait until child sets "child_server_running" to FALSE. That is an indication that the child * source server has completed its initialization phase and is all set so the parent command can return. */ while (isalive = is_proc_alive(pid, 0)) /* note : intended assignment */ { if (gtmsource_local->child_server_running) break; /* To take care of reassignment of PIDs, the while condition should be && with the condition * (PPID of pid == process_id) */ SHORT_SLEEP(GTMSOURCE_WAIT_FOR_SRV_START); WAITPID(pid, &status, WNOHANG, waitpid_res); /* Release defunct child if dead */ } if (isalive) { /* Child process is alive and started with no issues */ if (0 != (save_errno = rel_sem(SOURCE, JNL_POOL_ACCESS_SEM))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error in rel_sem"), save_errno); /* If the child source server process got a ftok counter overflow, it would have recorded that in * jnlpool->jnlpool_ctl->ftok_counter_halted. Decrement the ftok counter only if neither we nor the * child process got a counter overflow. */ ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented && !jnlpool->jnlpool_ctl->ftok_counter_halted, TRUE); } else { /* Child source server process errored out at startup and is no longer alive. * If we were the one who created the journal pool, let us clean it up. */ repl_log(stdout, TRUE, TRUE, "Source server startup failed. See source server log file\n"); if (is_jnlpool_creator) status = gtmsource_shutdown(TRUE, NORMAL_SHUTDOWN); } /* If the parent is killed (or crashes) between the fork and exit, checkhealth may not detect that startup * is in progress - parent forks and dies, the system will release sem 0 and 1, checkhealth might test the * value of sem 1 before the child grabs sem 1. */ gtmsource_exit(isalive ? SRV_ALIVE : SRV_ERR); } /* The parent process (source server startup command) will be holding the ftok semaphore and jnlpool access semaphore * at this point. The variables that indicate this would have been copied over to the child during the fork. This will * make the child think it is actually holding them as well when actually it is not. Reset those variables in the child * to ensure they do not misrepresent the holder of those semaphores. */ ftok_sem_reg = NULL; assert(udi->grabbed_ftok_sem); udi->grabbed_ftok_sem = FALSE; assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); holds_sem[SOURCE][JNL_POOL_ACCESS_SEM] = FALSE; assert(!holds_sem[SOURCE][SRC_SERV_COUNT_SEM]); /* Start child source server initialization. The process might have sent a syslog message already, so set first_syslog * here to force setting image type with the new value of is_src_server. */ is_src_server = first_syslog = TRUE; TREF(error_on_jnl_file_lost) = JNL_FILE_LOST_ERRORS; /* source server should never switch journal files even on errors */ OPERATOR_LOG_MSG; process_id = getpid(); /* Initialize mutex socket, memory semaphore etc. before any "grab_lock" is done by this process on the journal pool. * Note that the initialization would already have been done by the parent receiver startup command but we need to * redo the initialization with the child process id. */ assert(mutex_per_process_init_pid && (mutex_per_process_init_pid != process_id)); mutex_per_process_init(); ppid = getppid(); log_init_status = repl_log_init(REPL_GENERAL_LOG, >msource_log_fd, gtmsource_options.log_file); assert(SS_NORMAL == log_init_status); repl_log_fd2fp(>msource_log_fp, gtmsource_log_fd); if (-1 == (procgp = setsid())) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Source server error in setsid"), errno); /* Point stdin to /dev/null */ OPENFILE(DEVNULL, O_RDONLY, null_fd); if (0 > null_fd) rts_error_csa(CSA_ARG(NULL) ERR_REPLERR, RTS_ERROR_LITERAL("Failed to open /dev/null for read"), errno, 0); assert(null_fd > 2); /* Detached from the initiating process, now detach from the starting IO */ io_rundown(NORMAL_RUNDOWN); FSTAT_FILE(gtmsource_log_fd, &stat_buf, log_init_status); assertpro(!log_init_status); /* io_rundown should not affect the log file */ DUP2(null_fd, 0, rc); if (0 > rc) RTS_ERROR_CSA_ABT(NULL, ERR_REPLERR, RTS_ERROR_LITERAL("Failed to set stdin to /dev/null"), errno, 0); /* Re-init IO now that we have opened the log file and set stdin to /dev/null */ io_init(IS_MUPIP_IMAGE); CLOSEFILE(null_fd, rc); if (0 > rc) RTS_ERROR_CSA_ABT(NULL, ERR_REPLERR, RTS_ERROR_LITERAL("Failed to close /dev/null"), errno, 0); # endif /* REPL_DEBUG_NOBACKGROUND */ if (ZLIB_CMPLVL_NONE != gtm_zlib_cmp_level) gtm_zlib_init(); /* Open zlib shared library for compression/decompression */ REPL_DPRINT1("Setting up regions\n"); gvinit(); /* We use the same code dse uses to open all regions but we must make sure they are all open before proceeding. */ all_files_open = region_init(FALSE); if (!all_files_open) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOTALLDBOPN); gtmsource_exit(ABNORMAL_SHUTDOWN); } /* Determine primary side null subscripts collation order */ /* Also check whether all regions have same null collation order */ this_side_std_null_coll = -1; for (reg = gd_header->regions, region_top = gd_header->regions + gd_header->n_regions; reg < region_top; reg++) { assert(reg->open); csa = &FILE_INFO(reg)->s_addrs; if (this_side_std_null_coll != csa->hdr->std_null_coll) { if (-1 == this_side_std_null_coll) this_side_std_null_coll = csa->hdr->std_null_coll; else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NULLCOLLDIFF); gtmsource_exit(ABNORMAL_SHUTDOWN); } } if (!REPL_ALLOWED(csa) && JNL_ALLOWED(csa)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_REPLOFFJNLON, 2, DB_LEN_STR(reg)); gtmsource_exit(ABNORMAL_SHUTDOWN); } if (reg->read_only && REPL_ALLOWED(csa)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Source Server does not have write permissions to one or " "more database files that are replicated")); gtmsource_exit(ABNORMAL_SHUTDOWN); } if ((NULL == csa->jnlpool) && REPL_ALLOWED(csa) && jnlpool->pool_init) csa->jnlpool = jnlpool; } /* Initialize source server alive/dead state related fields in "gtmsource_local" before the ftok semaphore is released */ gtmsource_local->gtmsource_pid = process_id; gtmsource_local->gtmsource_state = GTMSOURCE_START; if (is_jnlpool_creator) { assert(jnlpool && jnlpool->jnlpool_ctl); DEBUG_ONLY(jnlpool->jnlpool_ctl->jnlpool_creator_pid = process_id); gtmsource_seqno_init(this_side_std_null_coll); if (ROOTPRIMARY_SPECIFIED == gtmsource_options.rootprimary) { /* Created the journal pool as a root primary. Append a history record to the replication instance file. * Invoke the function "gtmsource_rootprimary_init" to do that. */ gtmsource_rootprimary_init(jnlpool->jnlpool_ctl->jnl_seqno); } } /* after this point we can no longer have the case where all the regions are unreplicated/non-journaled. */ # ifndef REPL_DEBUG_NOBACKGROUND /* It is necessary for every process that is using the ftok semaphore to increment the counter by 1. This is used * by the last process that shuts down to delete the ftok semaphore when it notices the counter to be 0. * Note that the parent source server startup command would have done an increment of the ftok counter semaphore * for the replication instance file. But the source server process (the child) that comes here would not have done * that. Do that while the parent is still holding on to the ftok semaphore waiting for our okay. */ assert(jnlpool && jnlpool->pool_init); if (!ftok_sem_incrcnt(jnlpool->jnlpool_dummy_reg, FILE_TYPE_REPLINST, &ftok_counter_halted)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_JNLPOOLSETUP); /* Increment the source server count semaphore */ status = incr_sem(SOURCE, SRC_SERV_COUNT_SEM); if (0 != status) { save_errno = errno; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Counter semaphore increment failure in child source server"), save_errno); } # else if (0 != (save_errno = rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM))) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error in rel_sem_immediate"), save_errno); } # endif /* REPL_DEBUG_NOBACKGROUND */ gtmsource_srv_count++; gtmsource_local->child_server_running = TRUE; /* At this point, the parent startup command will stop waiting for child */ /* Log source server startup command line first */ SNPRINTF(tmpmsg, REPL_MSG_SIZE, "%s %s\n", cli_lex_in_ptr->argv[0], cli_lex_in_ptr->in_str); repl_log(gtmsource_log_fp, TRUE, TRUE, tmpmsg); SNPRINTF(tmpmsg, REPL_MSG_SIZE, "GTM Replication Source Server with Pid [%d] started for Secondary Instance [%s]", process_id, gtmsource_local->secondary_instname); sgtm_putmsg(print_msg, OUT_BUFF_SIZE - 1, VARLSTCNT(4) ERR_REPLINFO, 2, LEN_AND_STR(tmpmsg)); repl_log(gtmsource_log_fp, TRUE, TRUE, print_msg); if (is_jnlpool_creator) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Created jnlpool with shmid = [%d] and semid = [%d]\n", jnlpool->repl_inst_filehdr->jnlpool_shmid, jnlpool->repl_inst_filehdr->jnlpool_semid); } else repl_log(gtmsource_log_fp, TRUE, TRUE, "Attached to existing jnlpool with shmid = [%d] and semid = [%d]\n", jnlpool->repl_inst_filehdr->jnlpool_shmid, jnlpool->repl_inst_filehdr->jnlpool_semid); # ifdef GTM_TLS if (REPL_TLS_REQUESTED) { repl_do_tls_init(gtmsource_log_fp); assert(REPL_TLS_REQUESTED || PLAINTEXT_FALLBACK); } # endif if (jnlpool->jnlpool_ctl->freeze) { last_seen_freeze_flag = jnlpool->jnlpool_ctl->freeze; sgtm_putmsg(print_msg, OUT_BUFF_SIZE - 1, VARLSTCNT(3) ERR_REPLINSTFROZEN, 1, jnlpool->repl_inst_filehdr->inst_info.this_instname); repl_log(gtmsource_log_fp, TRUE, FALSE, print_msg); sgtm_putmsg(print_msg, OUT_BUFF_SIZE - 1, VARLSTCNT(3) ERR_REPLINSTFREEZECOMMENT, 1, jnlpool->jnlpool_ctl->freeze_comment); repl_log(gtmsource_log_fp, TRUE, TRUE, print_msg); } add_safe_timer_handler(1, gtmsource_heartbeat_timer); # ifdef DEBUG if (is_jnlpool_creator) { if (TREF(gtm_test_fake_enospc) && CUSTOM_ERRORS_LOADED) { /* Only the journal pool creator drives fake_enospc */ srand(time(NULL)); start_timer((TID)fake_enospc, ENOSPC_INIT_DURATION, fake_enospc, 0, NULL); } } # endif gtmsource_local->jnlfileonly = gtmsource_options.jnlfileonly; do { /* If mode is passive, go to sleep. Wakeup every now and then and check to see if I have to become active. */ gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_START; if ((gtmsource_local->mode == GTMSOURCE_MODE_PASSIVE) && (gtmsource_local->shutdown == NO_SHUTDOWN)) { gtmsource_poll_actions(FALSE); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_MODE_CHANGE); continue; } if (GTMSOURCE_MODE_PASSIVE == gtmsource_local->mode) { /* Shutdown initiated */ assert(gtmsource_local->shutdown == SHUTDOWN); sgtm_putmsg(print_msg, OUT_BUFF_SIZE - 1, VARLSTCNT(4) ERR_REPLINFO, 2, RTS_ERROR_LITERAL("GTM Replication Source Server Shutdown signalled")); repl_log(gtmsource_log_fp, TRUE, TRUE, print_msg); break; } gtmsource_poll_actions(FALSE); if (GTMSOURCE_CHANGING_MODE == gtmsource_state) continue; if (GTMSOURCE_MODE_ACTIVE_REQUESTED == gtmsource_local->mode) gtmsource_local->mode = GTMSOURCE_MODE_ACTIVE; SNPRINTF(tmpmsg, REPL_MSG_SIZE, "GTM Replication Source Server now in ACTIVE mode using port %d", gtmsource_local->secondary_port); sgtm_putmsg(print_msg, OUT_BUFF_SIZE - 1, VARLSTCNT(4) ERR_REPLINFO, 2, LEN_AND_STR(tmpmsg)); repl_log(gtmsource_log_fp, TRUE, TRUE, print_msg); DEBUG_ONLY(repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs;) assert(!repl_csa->hold_onto_crit); /* so it is ok to invoke "grab_lock" and "rel_lock" unconditionally */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, HANDLE_CONCUR_ONLINE_ROLLBACK); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Starting afresh due to ONLINE ROLLBACK\n"); repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL INFO - Current Jnlpool Seqno : %llu\n", jnlpool->jnlpool_ctl->jnl_seqno); continue; } GTMSOURCE_SET_READ_ADDR(gtmsource_local, jnlpool); gtmsource_local->read_state = gtmsource_local->jnlfileonly ? READ_FILE : READ_POOL; read_jnl_seqno = gtmsource_local->read_jnl_seqno; assert(read_jnl_seqno <= jnlpool->jnlpool_ctl->jnl_seqno); if (read_jnl_seqno < jnlpool->jnlpool_ctl->jnl_seqno) { gtmsource_local->read_state = READ_FILE; QWASSIGN(gtmsource_save_read_jnl_seqno, jnlpool->jnlpool_ctl->jnl_seqno); gtmsource_pool2file_transition = TRUE; /* so that we read the latest gener jnl files */ } rel_lock(jnlpool->jnlpool_dummy_reg); if (SS_NORMAL != (status = gtmsource_alloc_tcombuff())) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error allocating initial tcom buffer space. Malloc error"), status); gtmsource_filter = NO_FILTER; if ('\0' != gtmsource_local->filter_cmd[0]) { if (SS_NORMAL == (status = repl_filter_init(gtmsource_local->filter_cmd))) gtmsource_filter |= EXTERNAL_FILTER; else gtmsource_exit(ABNORMAL_SHUTDOWN); } gtmsource_process(); /* gtmsource_process returns only when mode needs to be changed to PASSIVE */ assert(gtmsource_state == GTMSOURCE_CHANGING_MODE); gtmsource_ctl_close(); gtmsource_free_msgbuff(); gtmsource_free_tcombuff(); gtmsource_free_filter_buff(); gtmsource_stop_heartbeat(); if (FD_INVALID != gtmsource_sock_fd) repl_close(>msource_sock_fd); if (gtmsource_filter & EXTERNAL_FILTER) repl_stop_filter(); } while (TRUE); gtmsource_end(); return(SS_NORMAL); } fis-gtm-V7.0-005/sr_unix/gtmsource.h0000644000032200000250000011311614342376330016225 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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. * * * ****************************************************************/ #ifndef GTMSOURCE_H #define GTMSOURCE_H /* for in_addr_t typedef on Linux */ #include "gtm_inet.h" #include "min_max.h" #include "mdef.h" #include "gt_timer.h" #include "gtm_ipv6.h" /* for union gtm_sockaddr_in46 */ #include "sleep.h" /* Needs mdef.h, gdsfhead.h and its dependencies */ #define JNLPOOL_DUMMY_REG_NAME "JNLPOOL_REG" #define MAX_TLSKEY_LEN 32 #define MAX_FILTER_CMD_LEN 512 #define DEFAULT_JNLPOOL_SIZE (2 << 25) /* 64MiB */ #define MIN_JNLPOOL_SIZE (2 << 19) /* 1MiB */ #define MAX_JNLPOOL_SIZE 0x1000000000LL /* 64GB - This should be a multiple of (~JNL_WRT_END_MASK + 1) */ #define MAX_FREEZE_COMMENT_LEN 1024 /* We need space in the journal pool to let other processes know which error messages should trigger anticipatory freeze. * Instead of storing them as a list, allocate one byte for each error message. Currently, the only piece of information * associated with each error message is whether it can trigger anticipatory freeze or not. */ #define MERRORS_ARRAY_SZ (2 * 1024) /* 2k should be enough for a while */ enum { READ_POOL, READ_FILE }; enum { GTMSOURCE_MODE_PASSIVE, GTMSOURCE_MODE_ACTIVE, GTMSOURCE_MODE_PASSIVE_REQUESTED, GTMSOURCE_MODE_ACTIVE_REQUESTED }; enum { ROOTPRIMARY_UNSPECIFIED = -1, /* if neither -PROPAGATEPRIMARY or -ROOTPRIMARY is explicitly or implicitly specified */ PROPAGATEPRIMARY_SPECIFIED = 0, /* if -PROPAGATEPRIMARY or -UPDNOTOK is explicitly or implicitly specified */ ROOTPRIMARY_SPECIFIED = 1 /* if -ROOTPRIMARY or -UPDOK is explicitly or implicitly specified */ }; #define SRV_ALIVE 0x0 #define SRV_DEAD 0x1 #define SRV_ERR 0x2 /* The source server can be in any one of the following states. */ typedef enum { GTMSOURCE_DUMMY_STATE = 0, /* Default state when no source server is up */ GTMSOURCE_START, /* Set at source server startup (in gtmsource.c) */ GTMSOURCE_WAITING_FOR_CONNECTION, /* Set when waiting for receiver to connect e.g. connection got reset etc. */ GTMSOURCE_WAITING_FOR_RESTART, /* Set when previous state is GTMSOURCE_WAITING_FOR_CONNECTION and * connection gets established with the receiver server. */ GTMSOURCE_SEARCHING_FOR_RESTART, /* Set when source server scans the jnl files and determines the resend point */ GTMSOURCE_SENDING_JNLRECS, /* Set when source server is sending journal records across */ GTMSOURCE_WAITING_FOR_XON, /* Set when source server gets an XOFF from the receiver server */ GTMSOURCE_CHANGING_MODE, /* Set when gtmsource_local->mode gets set to PASSIVE (i.e. ACTIVE --> PASSIVE) */ GTMSOURCE_SEND_NEW_HISTINFO, /* Set when the source server detects it has to send a REPL_HISTREC message * to the secondary before sending the next seqno. */ GTMSOURCE_HANDLE_ONLN_RLBK, /* Set when the source server detects an online rollback and hence has close and * restart the connection */ GTMSOURCE_NUM_STATES } gtmsource_state_t; #define GTMSOURCE_WAIT_FOR_RECEIVER_TO_QUIT 5 /* seconds */ #define GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN (1000 - 1) /* ms */ #define GTMSOURCE_WAIT_FOR_JNLOPEN 10 /* ms */ #define LOG_WAIT_FOR_JNLOPEN_PERIOD (50 * 1000) /* ms */ #define GTMSOURCE_WAIT_FOR_JNL_RECS 1 /* ms */ #define LOG_WAIT_FOR_JNL_RECS_PERIOD (50 * 1000) /* ms */ #define LOG_WAIT_FOR_JNL_FLUSH_PERIOD (8 * 1000) /* ms */ #define GTMSOURCE_WAIT_FOR_SRV_START 10 /* ms */ #define GTMSOURCE_WAIT_FOR_MODE_CHANGE (1000 - 1) /* ms, almost 1 sec */ #define GTMSOURCE_WAIT_FOR_SHUTDOWN (1000 - 1) /* ms, almost 1 sec */ #define GTMSOURCE_WAIT_FOR_SOURCESTART (1000 - 1) /* ms, almost 1 sec */ #define GTMSOURCE_WAIT_FOR_FIRSTHISTINFO (1000 - 1) /* ms, almost 1 sec */ #define LOG_WAIT_FOR_JNLOPEN_TIMES 5 /* Number of times the source logs wait_for_jnlopen */ /* Wait for a max of 2 minutes on a single region database as all the source server shutdown * timeouts seen so far have been on a single region database. For multi-region databases, wait * for a max of one and a half minute per region. If ever we see timeouts with multi-region * databases, this value needs to be bumped as well. */ #define GTMSOURCE_MAX_SHUTDOWN_WAITLOOP(gdheader) (MAX(120, (gdheader->n_regions) * 90)) #define GTMSOURCE_SHUTDOWN_PAD_TIME 5 /* seconds */ #define GTMSOURCE_SENT_THRESHOLD_FOR_RECV (1 * 1024 * 1024) #define BACKLOG_BYTES_THRESHOLD (1 * 1024 * 1024) #define BACKLOG_COUNT_THRESHOLD (10 * 1024) #define GTMSOURCE_MIN_TCP_SEND_BUFSIZE (16 * 1024) /* anything less than this, issue a warning */ #define GTMSOURCE_TCP_SEND_BUFSIZE_INCR (32 * 1024) /* attempt to get a larger buffer with this increment */ #define GTMSOURCE_TCP_SEND_BUFSIZE (1024 * 1024) /* desirable to set the buffer size to be able to send large chunks */ #define GTMSOURCE_MIN_TCP_RECV_BUFSIZE (512) /* anything less than this, issue a warning */ #define GTMSOURCE_TCP_RECV_BUFSIZE (1024) /* not much inbound traffic, we can live with a low limit */ #define GTMSOURCE_FH_FLUSH_INTERVAL 60 /* seconds, if required, flush file header(s) every these many seconds */ #define JPL_PHASE2_COMMIT_ARRAY_SIZE 16384 /* Max # of jnlpool commits that can still be in phase2. * Note that even if a phase2 commit is complete, the slot it occupies * cannot be reused by anyone else until all phase2 commit slots that * started before this slot are also done. */ /* Individual structure describing an active phase2 jnlpool commit in jnlpool shared memory */ typedef struct { seq_num jnl_seqno; seq_num strm_seqno; qw_off_t start_write_addr; /* jpl->rsrv_write_addr at start of this commit */ uint4 process_id; uint4 tot_jrec_len; /* total length of jnl records corresponding to this seqno */ uint4 prev_jrec_len; /* total length of jnl records corresponding to the previous seqno */ boolean_t write_complete; /* TRUE if this pid is done writing jnl records to jnlbuff */ } jpl_phase2_in_prog_t; /* Structure recording a particular type of event in this jnlpool */ typedef struct { gtm_uint64_t cntr; /* # of times this event was encountered in the life of this jnlpool */ seq_num seqno; /* seqno when this event was last encountered */ } jpl_trc_rec_t; #define JPL_TRACE_PRO(JPL, TRC) { JPL->TRC.cntr++; JPL->TRC.seqno = JPL->jnl_seqno; } #define TAB_JPL_TRC_REC(A,B) B, enum jpl_trc_rec_type { #include "tab_jpl_trc_rec.h" n_jpl_trc_rec_types }; #undef TAB_JPL_TRC_REC typedef struct { /* IMPORTANT : all fields that are used by the source server reading from pool logic must be defined VOLATILE to avoid * compiler optimization, forcing fresh load on every access. */ replpool_identifier jnlpool_id; sm_off_t critical_off; /* Offset from the start of this structure to "csa->critical" in jnlpool */ sm_off_t filehdr_off; /* Offset to "repl_inst_filehdr" section in jnlpool */ sm_off_t srclcl_array_off; /* Offset to "gtmsrc_lcl_array" section in jnlpool */ sm_off_t sourcelocal_array_off; /* Offset to "gtmsource_local_array" section in jnlpool */ /* The offsets of the above fields in this structure should not change as "mu_rndwn_replpool" relies on that. * If they change, the macro GDS_RPL_LABEL needs to be changed as well (to effect a journal pool format change). */ seq_num start_jnl_seqno; /* The sequence number with which operations started. * Needs to be on 8 byte boundary */ gtm_uint64_t jnldata_base_off; /* Journal pool offset from where journal data starts */ gtm_uint64_t jnlpool_size; /* Available space for journal data in bytes */ volatile seq_num jnl_seqno; /* Sequence number for transactions. Updated by GTM process */ volatile seq_num last_histinfo_seqno; /* Starting seqno of the last histinfo in the instance file. * Set to 0 if there are no histinfo records in the instance file. */ seq_num max_zqgblmod_seqno; /* The current value of max of zqgblmod_seqno of all databases. * Initialized at jnlpool creation time by "gtmsource_seqno_init". * Set to 0 by receiver server when it receives LOSTTNCOMPLETE notification * or by a MUPIP REPLIC -SOURCE -LOSTTNCOMPLETE command on this instance. * Updated by "gtmsource_update_zqgblmod_seqno_and_tn" whenever it resets * the database file header "zqgblmod_seqno" fields on receipt of a * fetchresync rollback message. This field is always updated while * holding the journal pool lock */ seq_num strm_seqno[MAX_SUPPL_STRMS]; /* the current jnl seqno of each stream */ repl_conn_info_t this_side; /* Replication connection details of this side/instance */ volatile qw_off_t write_addr; /* Virtual address of the next journal record to be written in the merged * journal file. Note that the merged journal may not exist. * Updated by GTM process. */ volatile qw_off_t rsrv_write_addr; /* Similar to "write_addr" but space is reserved in phase1 of commit * and actual copy to jnlpool happens in phase2 outside of jnlpool crit. * If no commits are active, "rsrv_write_addr == write_addr". Else * "rsrv_write_addr > write_addr". */ boolean_t upd_disabled; /* Identify whether updates are disabled or not on the secondary */ volatile uint4 lastwrite_len; /* The length of the last transaction written into the journal pool. * Copied to jnldata_hdr.prev_jnldata_len before writing into the pool. * Updated by GTM process. */ boolean_t send_losttn_complete; /* Set to TRUE by MUPIP REPLIC -SOURCE -LOSTTNCOMPLETE command. Set to * FALSE whenever a secondary that was a former root primary does a * -fetchresync rollback. This value is copied over to each gtmsource_local * structure whenever that slot gets used and the corresponding source * server connects to the receiver. */ unsigned char primary_instname[MAX_INSTNAME_LEN];/* Name of the primary instance this secondary instance is * connected to. Set to NULL at journal pool creation time. * Initialized when receiver server connects to primary. * Stays NULL and has no meaning if this is a root primary. */ uint4 gtmrecv_pid; /* Process identification of receiver server. Copy of that which is stored * in the receive pool, but needed for those processes that do not * attach to the receive pool (e.g. source server) but yet need this info. */ volatile jnl_tm_t prev_jnlseqno_time; /* To ensure that time never decreases across successive jnl records * across ALL replicated regions attached to this journal pool. */ boolean_t pool_initialized; /* Set to TRUE only after completely finished with initialization. * Anyone who does a "jnlpool_init" before this will issue a error. */ uint4 jnlpool_creator_pid; /* DEBUG-ONLY field used for fake ENOSPC testing */ volatile uint4 onln_rlbk_pid; /* process ID of currently running ONLINE ROLLBACK. 0 if none. */ volatile uint4 onln_rlbk_cycle; /* incremented everytime an ONLINE ROLLBACK ends */ boolean_t freeze; /* Freeze all regions in this instance. */ char freeze_comment[MAX_FREEZE_COMMENT_LEN]; /* Text explaining reason for freeze */ boolean_t instfreeze_environ_inited; unsigned char merrors_array[MERRORS_ARRAY_SZ]; boolean_t ftok_counter_halted; uint4 phase2_commit_index1; uint4 phase2_commit_index2; char filler_16bytealign1[16]; /************* JPL_TRC_REC RELATED FIELDS -- begin -- ***********/ # define TAB_JPL_TRC_REC(A,B) jpl_trc_rec_t B; # include "tab_jpl_trc_rec.h" # undef TAB_JPL_TRC_REC /************* JPL_TRC_REC RELATED FIELDS -- end -- ***********/ jpl_phase2_in_prog_t phase2_commit_array[JPL_PHASE2_COMMIT_ARRAY_SIZE]; CACHELINE_PAD(SIZEOF(global_latch_t), 0) /* start next latch at a different cacheline than previous fields */ global_latch_t phase2_commit_latch; /* Used by "repl_phase2_complete" to update "phase2_commit_index1" */ } jnlpool_ctl_struct; #if defined(__osf__) && defined(__alpha) # pragma pointer_size(save) # pragma pointer_size(long) #endif typedef jnlpool_ctl_struct *jnlpool_ctl_ptr_t; typedef struct repl_inst_hdr_struct *repl_inst_hdr_ptr_t; typedef struct gtmsrc_lcl_struct *gtmsrc_lcl_ptr_t; #if defined(__osf__) && defined(__alpha) # pragma pointer_size(restore) #endif /* * When a GTM process writes journal data into the journal pool, it uses the * following layout at location write * * struct jnlpool_trans_struct * { * jnldata_hdr_struct jnldata_hdr; - jnldata_hdr.jnldata_len * is the length of journal * data of a transaction * uchar jnldata[jnldata_len]; - transaction journal * data * }; */ /********************** VERY IMPORTANT ******************************** * Keep SIZEOF(jnldata_hdr_struct) == ~JNL_WRT_END_MASK + 1 and * jnlpool_size should be a multiple of (~JNL_WRT_END_MASK + 1). * This is to avoid jnldata_hdr_struct from wrapping, so that fields * remain contiguous. **********************************************************************/ typedef struct { uint4 jnldata_len; /* length of the journal data of a transaction in bytes */ uint4 prev_jnldata_len; /* length of the journal data of the previous transaction in the * journal pool (in bytes) */ } jnldata_hdr_struct; #if defined(__osf__) && defined(__alpha) # pragma pointer_size(save) # pragma pointer_size(long) #endif typedef jnldata_hdr_struct *jnldata_hdr_ptr_t; #if defined(__osf__) && defined(__alpha) # pragma pointer_size(restore) #endif #define REPL_CONN_HARD_TRIES_COUNT 5 /* Default number of connection hard tries */ #define REPL_CONN_HARD_TRIES_PERIOD 500 /* msec Default connection hard try period */ #define REPL_CONN_SOFT_TRIES_PERIOD 5 /* sec Default connection soft try period*/ #define REPL_CONN_ALERT_ALERT_PERIOD 0 /* sec Default no logging of the REPLALERT message */ #define REPL_CONN_HEARTBEAT_PERIOD 15 /* sec Default heartbeat period */ #define REPL_CONN_HEARTBEAT_MAX_WAIT 300 /* sec Default heartbeat maximum waiting period */ #define REPL_MAX_LOG_PERIOD 150 /* sec Maximum logging period */ enum { GTMSOURCE_CONN_HARD_TRIES_COUNT = 0, GTMSOURCE_CONN_HARD_TRIES_PERIOD, GTMSOURCE_CONN_SOFT_TRIES_PERIOD, GTMSOURCE_CONN_ALERT_PERIOD, GTMSOURCE_CONN_HEARTBEAT_PERIOD, GTMSOURCE_CONN_HEARTBEAT_MAX_WAIT, GTMSOURCE_CONN_PARMS_COUNT }; #define GTMSOURCE_CONN_PARMS_DELIM "," #define JNLPOOL_SEGMENT 'J' /*************** Macro to send a REPL_HISTREC message, given an histinfo type of record ***************/ /* Note that HSTINFO.start_seqno is modified by this macro */ #define GTMSOURCE_SEND_REPL_HISTREC(HSTINFO, GTMSRCLCL, RCVR_CROSS_ENDIAN) \ { \ repl_histrec_msg_t histrec_msg; \ \ memset(&histrec_msg, 0, SIZEOF(repl_histrec_msg_t)); \ histrec_msg.type = REPL_HISTREC; \ histrec_msg.len = SIZEOF(repl_histrec_msg_t); \ histrec_msg.histjrec.jrec_type = JRT_HISTREC; \ /* Update history record's start_seqno to reflect the starting point of transmission */ \ HSTINFO.start_seqno = GTMSRCLCL->read_jnl_seqno; \ histrec_msg.histjrec.histcontent = HSTINFO; \ if (RCVR_CROSS_ENDIAN && (this_side->jnl_ver < remote_side->jnl_ver)) \ { \ histrec_msg.histjrec.forwptr = GTM_BYTESWAP_24(SIZEOF(repl_histrec_jnl_t)); \ ENDIAN_CONVERT_REPL_HISTINFO(&histrec_msg.histjrec.histcontent); \ } else \ histrec_msg.histjrec.forwptr = SIZEOF(repl_histrec_jnl_t); \ gtmsource_repl_send((repl_msg_ptr_t)&histrec_msg, "REPL_HISTREC", GTMSRCLCL->read_jnl_seqno, INVALID_SUPPL_STRM); \ } /* Macro for use before calling a routine (gtmsource_recv_ctl_nowait(), gtmsource_poll_actions()), * directly or indirectly, which may cause a switch to a different state. For now we only care about * occurrences which affect call paths through gtmsource_readfiles. * We treat GTMSOURCE_SEARCHING_FOR_RESTART differently than other states, as there is some special * handling to do. However, we only enter this state in gtmsource_recv_ctl() (which is called by * gtmsource_recv_ctl_nowait()) when the prior state was GTMSOURCE_WAITING_FOR_XON, as is asserted * there. Since we don't send transactions while in GTMSOURCE_WAITING_FOR_XON, we won't be in that * state in gtmsource_readfiles, so it will never be the prior state. We assert this fact before * saving the prior state here. Since GTMSOURCE_WAITING_FOR_XON was not the prior state at the * points where we save it, we can skip checking for the GTMSOURCE_SEARCHING_FOR_RESTART state below. * Similarly, repl_tls.renegotiate_state should not be REPLTLS_WAITING_FOR_RENEG_ACK while sending * transactions, so assert the fact and skip saving this state. */ #define GTMSOURCE_SAVE_STATE(STATEVAR) \ MBSTART { \ assert(GTMSOURCE_WAITING_FOR_XON != gtmsource_state); \ assert(REPLTLS_WAITING_FOR_RENEG_ACK != repl_tls.renegotiate_state); \ STATEVAR = gtmsource_state; \ } MBEND /* Macros for use in a test after calling a routine (gtmsource_recv_ctl_nowait(), gtmsource_poll_actions()), * directly or indirectly, which may cause a switch to a different state. * For now we only care about occurrences which affect call paths through gtmsource_readfiles. * See above comments about GTMSOURCE_SEARCHING_FOR_RESTART and REPLTLS_WAITING_FOR_RENEG_ACK. */ #define GTMSOURCE_CHANGED_STATE(STATEVAR) (((STATEVAR) != gtmsource_state) && ((STATEVAR) != GTMSOURCE_DUMMY_STATE)) /* By "transitional" state here we mean a state which may result from the above mentioned * routines when called while sending journal records which cause us to stop sending journal * records. */ #define GTMSOURCE_IS_TRANSITIONAL_STATE() \ ((GTMSOURCE_CHANGING_MODE == gtmsource_state) \ || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) \ || (GTMSOURCE_WAITING_FOR_XON == gtmsource_state)) #define GTMSOURCE_NOW_TRANSITIONAL(STATEVAR) \ ((GTMSOURCE_CHANGED_STATE(STATEVAR) && GTMSOURCE_IS_TRANSITIONAL_STATE()) \ GTMTLS_ONLY(|| (REPLTLS_WAITING_FOR_RENEG_ACK == repl_tls.renegotiate_state)) \ || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) #define GTMSOURCE_SET_READ_ADDR(GTMSOURCE_LOCAL, JNLPOOL) \ MBSTART { \ qw_off_t rsrv_write_addr; \ \ assert(JNLPOOL && JNLPOOL->jnlpool_dummy_reg && JNLPOOL->jnlpool_ctl && GTMSOURCE_LOCAL); \ assert((&FILE_INFO(JNLPOOL->jnlpool_dummy_reg)->s_addrs)->now_crit); \ rsrv_write_addr = JNLPOOL->jnlpool_ctl->rsrv_write_addr; \ GTMSOURCE_LOCAL->read_addr = rsrv_write_addr; \ GTMSOURCE_LOCAL->read = rsrv_write_addr % JNLPOOL->jnlpool_ctl->jnlpool_size; \ } MBEND #define SET_JPL_WRITE_ADDR(JPL, NEW_WRITE_ADDR) \ { \ /* For systems with UNORDERED memory access (example, ALPHA, POWER4, PA-RISC 2.0), on a \ * multi processor system, it is possible that the source server notices the change in \ * rsrv_write_addr before seeing the change to jnlheader->jnldata_len, leading it to read an \ * invalid transaction length. To avoid such conditions, we should commit the order of \ * shared memory updates before we update rsrv_write_addr. This ensures that the source server \ * sees all shared memory updates related to a transaction before the change in rsrv_write_addr \ */ \ SHM_WRITE_MEMORY_BARRIER; \ JPL->write_addr = NEW_WRITE_ADDR; \ } #define SET_JNL_SEQNO(JPL, TEMP_JNL_SEQNO, SUPPLEMENTARY, STRM_SEQNO, STRM_INDEX, NEXT_STRM_SEQNO) \ { \ GBLREF jnl_gbls_t jgbl; \ \ assert(!jgbl.forw_phase_recovery); \ TEMP_JNL_SEQNO++; \ JPL->jnl_seqno = TEMP_JNL_SEQNO; \ if (SUPPLEMENTARY) \ { \ NEXT_STRM_SEQNO = STRM_SEQNO + 1; \ JPL->strm_seqno[STRM_INDEX] = NEXT_STRM_SEQNO; \ } \ } /* The following structure contains data items local to one Source Server. * It is maintained in the journal pool to provide for persistence across * instantiations of the Source Server (due to crashes). */ typedef struct { unsigned char secondary_instname[MAX_INSTNAME_LEN];/* Name of the secondary instance that is connected */ uint4 gtmsource_pid; /* Process identification of source server */ int4 mode; /* Active, or Passive */ gtmsource_state_t gtmsource_state;/* Current state of the source server. This shared memory flag is maintained in * sync with the source server specific private global variable "gtmsource_state" */ int4 gtmsrc_lcl_array_index; /* Index of THIS struct in the array of gtmsource_local_struct in jnlpool */ int4 repl_zlib_cmp_level; /* zlib compression level currently used across the replication pipe */ unsigned char filler1_align_8[3]; int4 read_state; /* From where to read - pool or the file(s)? */ qw_off_t read; /* Offset relative to jnldata_base_off of the next journal record from the pool */ repl_conn_info_t remote_side; /* Details of the remote side connection */ qw_off_t read_addr; /* Virtual address of the next journal record in the merged journal file to read */ seq_num read_jnl_seqno; /* Next jnl_seqno to be read (corresponds to "gtmsrc_lcl->resync_seqno") */ seq_num connect_jnl_seqno; /* jnl_seqno of the instance when last connection with secondary was made. * Used to determine least recently used slot for reuse. Is maintained in * parallel with the same field in the corresponding "gtmsrc_lcl" structure */ seq_num heartbeat_jnl_seqno; /* ack_seqno from the receiver server */ boolean_t hrtbt_recvd; /* whether heartbeat was received from the receiver server */ int4 num_histinfo; /* Index of the last histinfo in the instance file as known to THIS source * server. Usually kept in sync with "repl_inst_filehdr->num_histinfo". * Note that whenever a histinfo record gets added to the instance file, * the file header field gets updated right away but this gtmsource_local * field gets updated in a deferred fashion whenever the source server * detects (as part of sending records across) that the instance file has * changed (has more histinfo records) since it last knew. */ int4 next_histinfo_num; /* Index of the histinfo record whose "start_seqno" is "next_histinfo_seqno" * Initialized when the source server connects to the receiver.*/ seq_num next_histinfo_seqno; /* "start_seqno" of the histinfo record that follows the one corresponding * to the the current "gtmsource_local->read_jnl_seqno". Initialized when * the source server connects to the receiver. */ seq_num last_flush_resync_seqno;/* Value of "gtmsource_local->read_jnl_seqno" (or corresponding field * "gtmsrc_lcl->resync_seqno") when corresponding gtmsrc_lcl was last * flushed to disk. Helps avoid redundant flushes. */ boolean_t send_new_histrec; /* TRUE if REPL_HISTREC needs to be sent before next send */ boolean_t send_losttn_complete; /* TRUE if a REPL_LOSTTN_COMPLETE message needs to be sent across * (set by a MUPIP REPLIC -SOURCE -LOSTTNCOMPLETE command). Set to * FALSE after the message gets sent. */ char secondary_host[MAX_HOST_NAME_LEN]; /* hostname of the secondary */ union gtm_sockaddr_in46 secondary_inet_addr; /* IP address of the secondary */ int secondary_af; /* address family of the seconary */ int secondary_addrlen; /* length of the secondary address */ uint4 secondary_port; /* Port at which Receiver is listening */ boolean_t child_server_running; /* Set to FALSE before starting a source server; * Set to TRUE by the source server process after its initialization. * Signal to parent startup command to stop waiting for child. */ uint4 log_interval; /* seqno count interval at which src server prints messages about replic traffic */ char log_file[MAX_FN_LEN + 1]; /* log file name */ uint4 changelog; /* Change the log file or log interval */ uint4 statslog; /* Boolean - detailed log on/off? */ char statslog_file[MAX_FN_LEN + 1]; /* stats log file name */ int4 connect_parms[GTMSOURCE_CONN_PARMS_COUNT]; /* Connect failure tries parms. Add fillers (if needed) * based on GTMSOURCE_CONN_PARMS_COUNT */ uint4 shutdown; /* Use to communicate shutdown related values */ int4 shutdown_time; /* Time allowed for shutdown in seconds */ char filter_cmd[MAX_FILTER_CMD_LEN]; /* command to run to invoke the external filter (if needed) */ global_latch_t gtmsource_srv_latch; boolean_t jnlfileonly; /* Current source server is only reading from journal files */ # ifdef GTM_TLS uint4 next_renegotiate_time; /* Time (in future) at which the next SSL/TLS renegotiation happens. */ int4 num_renegotiations; /* Number of SSL/TLS renegotiations that happened so far. */ # endif int4 filler_8byte_align; /* Keep size % 8 == 0 */ } gtmsource_local_struct; #if defined(__osf__) && defined(__alpha) # pragma pointer_size(save) # pragma pointer_size(long) #endif typedef gtmsource_local_struct *gtmsource_local_ptr_t; #if defined(__osf__) && defined(__alpha) # pragma pointer_size(restore) #endif /* * Journal pool shared memory layout - * * jnlpool_ctl_struct * mutex structure (mutex_struct) [JNLPOOL lock control] * NUM_CRIT_ENTRY * mutex_que_entry [JNLPOOL lock control] * mutex spin parms structure (mutex_spin_parms_struct) [JNLPOOL lock control] * node local structure (node_local) [JNLPOOL lock control] * repl_inst_hdr * gtmsrc_lcl[0-15] * gtmsource_local_struct[0-15] * zero or more jnlpool_trans_struct */ /* Push the jnldata_base_off to be aligned to (~JNL_WRT_END_MASK + 1)-byte boundary */ #define JNLPOOL_CTL_SIZE ROUND_UP(SIZEOF(jnlpool_ctl_struct), CACHELINE_SIZE) /* align crit semaphore at cache line */ #define JNLPOOL_CRIT_SIZE (JNLPOOL_CRIT_SPACE + SIZEOF(mutex_spin_parms_struct) + SIZEOF(node_local)) #define JNLDATA_BASE_OFF (JNLPOOL_CTL_SIZE + JNLPOOL_CRIT_SIZE + REPL_INST_HDR_SIZE + GTMSRC_LCL_SIZE + GTMSOURCE_LOCAL_SIZE) #define REPLCSA2JPL(CSA) (jnlpool_ctl_ptr_t)((sm_uc_ptr_t)CSA->critical - JNLPOOL_CTL_SIZE) /* see "jnlpool_init" for * relationship between critical and jpl. */ /* The below is a structure capturing the details of space reserved in the journal pool corresponding to a transaction. * Space for the transaction in the jnlpool is reserved in phase1 ("jnl_write_reserve"0 while holding crit and actual * memcpy of journal records into jnlpool happens in phase2 outside of crit ("jnl_write_phase2"). */ typedef struct { qw_off_t start_write_addr; /* jpl->write_addr at which this transaction started */ qw_off_t cur_write_addr; /* current value of jpl->write_addr as we progress writing journal records * into jnlpool in phase2 of commit. */ uint4 tot_jrec_len; /* Total length (in bytes) of jnl records we expect to be written for this seqno */ uint4 write_total; /* Total length of jnl records actually used up for this seqno */ boolean_t memcpy_skipped; /* we skipped "memcpy" at least once in "jnl_pool_write" */ uint4 phase2_commit_index; /* index into jpl->phase2_commit_array[] corresponding to this commit */ uint4 num_tcoms; /* If curr tn is a TP tn, then # of tcom records seen till now */ char filler_8byte_align[4]; } jpl_rsrv_struct_t; /* The below structure has various fields that point to different sections of the journal pool * and a few fields that point to private memory. */ typedef struct jnlpool_addrs_struct { jnlpool_ctl_ptr_t jnlpool_ctl; /* pointer to the journal pool control structure */ gd_region *jnlpool_dummy_reg; /* some functions need gd_region */ gtmsource_local_ptr_t gtmsource_local; /* pointer to the gtmsource_local structure for this source server */ gtmsource_local_ptr_t gtmsource_local_array; /* pointer to the gtmsource_local array section in the journal pool */ repl_inst_hdr_ptr_t repl_inst_filehdr; /* pointer to the instance file header section in the journal pool */ gtmsrc_lcl_ptr_t gtmsrc_lcl_array; /* pointer to the gtmsrc_lcl array section in the journal pool */ sm_uc_ptr_t jnldata_base; /* pointer to the start of the actual journal record data */ jpl_rsrv_struct_t jrs; boolean_t pool_init; /* this jnlpool_addrs is active */ boolean_t recv_pool; /* this jnlpool is the same instance as recvpool */ boolean_t relaxed; /* created with jnlpool_user GTMRELAXED */ struct jnlpool_addrs_struct *next; gd_inst_info *gd_instinfo; /* global directory not gtm_repl_instance */ gd_addr *gd_ptr; /* pointer to global directory */ } jnlpool_addrs; #define JNLPOOL_FROM(CSA) (!IS_GTM_IMAGE ? jnlpool : (!(CSA) ? NULL : ((CSA)->jnlpool ? (CSA)->jnlpool : jnlpool))) #define UPDATE_JPL_RSRV_WRITE_ADDR(JPL, JNLPOOL, TN_JRECLEN) \ MBSTART { \ qw_off_t rsrv_write_addr; \ int nextIndex, endIndex; \ jpl_phase2_in_prog_t *phs2cmt; \ \ GBLREF uint4 process_id; \ \ assert((NULL != JNLPOOL) && (NULL != JNLPOOL->jnlpool_dummy_reg)); \ assert(JPL == JNLPOOL->jnlpool_ctl); \ assert((&FILE_INFO(JNLPOOL->jnlpool_dummy_reg)->s_addrs)->now_crit); \ /* Allocate a slot. But before that, check if the slot array is full. \ * endIndex + 1 == first_index implies full. \ * endIndex == first_index implies empty. \ */ \ endIndex = JPL->phase2_commit_index2; \ nextIndex = endIndex; \ INCR_PHASE2_COMMIT_INDEX(nextIndex, JPL_PHASE2_COMMIT_ARRAY_SIZE); \ if (nextIndex == JPL->phase2_commit_index1) \ { /* Slot array is full. Wait for phase2 to finish. */ \ do \ { \ repl_phase2_cleanup(JNLPOOL); \ if (nextIndex != JPL->phase2_commit_index1) \ break; \ JPL_TRACE_PRO(JPL, jnl_pool_write_sleep); \ SLEEP_USEC(1, FALSE); \ } while (nextIndex == JPL->phase2_commit_index1); \ } \ ASSERT_JNL_PHASE2_COMMIT_INDEX_IS_VALID(endIndex, JPL_PHASE2_COMMIT_ARRAY_SIZE); \ phs2cmt = &JPL->phase2_commit_array[endIndex]; \ assert(JPL->jnl_seqno == jnl_fence_ctl.token); \ phs2cmt->jnl_seqno = jnl_fence_ctl.token; \ phs2cmt->strm_seqno = jnl_fence_ctl.strm_seqno; \ phs2cmt->process_id = process_id; \ rsrv_write_addr = JPL->rsrv_write_addr; \ phs2cmt->start_write_addr = rsrv_write_addr; \ assert(TN_JRECLEN); \ assert(0 == (TN_JRECLEN % JNL_REC_START_BNDRY)); \ assert(NULL_RECLEN <= TN_JRECLEN); /* see "repl_phase2_salvage" for why this is needed */ \ phs2cmt->tot_jrec_len = TN_JRECLEN; \ phs2cmt->prev_jrec_len = JPL->lastwrite_len; \ phs2cmt->write_complete = FALSE; \ JNLPOOL->jrs.start_write_addr = rsrv_write_addr; \ JNLPOOL->jrs.cur_write_addr = rsrv_write_addr + SIZEOF(jnldata_hdr_struct); \ JNLPOOL->jrs.tot_jrec_len = TN_JRECLEN; \ JNLPOOL->jrs.write_total = SIZEOF(jnldata_hdr_struct); /* will be incremented as we copy \ * each jnlrec into jnlpool in phase2 \ */ \ JNLPOOL->jrs.memcpy_skipped = FALSE; \ JNLPOOL->jrs.phase2_commit_index = endIndex; \ JNLPOOL->jrs.num_tcoms = 0; \ /* Note: "mutex_salvage" and "repl_phase2_cleanup" rely on the below order of sets */ \ JPL->lastwrite_len = TN_JRECLEN; \ JPL->rsrv_write_addr = rsrv_write_addr + TN_JRECLEN; \ SHM_WRITE_MEMORY_BARRIER; /* see corresponding SHM_READ_MEMORY_BARRIER in "repl_phase2_cleanup" */ \ JPL->phase2_commit_index2 = nextIndex; \ } MBEND error_def(ERR_JNLPOOLRECOVERY); #define JPL_PHASE2_WRITE_COMPLETE(JNLPOOL) \ jpl_phase2_write_complete(JNLPOOL) #if defined(__osf__) && defined(__alpha) # pragma pointer_size(save) # pragma pointer_size(long) #endif typedef jnlpool_addrs *jnlpool_addrs_ptr_t; #if defined(__osf__) && defined(__alpha) # pragma pointer_size(restore) #endif /* Types of processes that can do jnlpool_init */ typedef enum { GTMPROC, /* For GT.M and Update Process */ GTMSOURCE, /* For Source Server */ GTMRECEIVE, /* For Receiver Server. Note this name should be different from GTMRECV which is defined to serve * a similar purpose in gtmrecv.h for processes that do recvpool_init. */ GTMRELAXED, /* For processes which want to a attach to an existing journal pool without the usual validations (currently * NOJNLPOOL is the only validation that is skipped) */ } jnlpool_user; typedef struct { boolean_t start; boolean_t shut_down; boolean_t activate; boolean_t changelog; boolean_t checkhealth; boolean_t deactivate; boolean_t jnlpool; boolean_t showbacklog; boolean_t statslog; boolean_t stopsourcefilter; int4 rootprimary; /* ROOTPRIMARY_SPECIFIED if -ROOTPRIMARY or -UPDOK is explicitly or implicitly specified, * PROPAGATEPRIMARY_SPECIFIED if -PROPAGATEPRIMARY or -UPDNOTOK is explicitly or * implicitly specified. * ROOTPRIMARY_UNSPECIFIED otherwise */ boolean_t instsecondary; /* TRUE if -INSTSECONDARY is explicitly or implicitly specified, FALSE otherwise */ boolean_t needrestart; /* TRUE if -NEEDRESTART was specified, FALSE otherwise */ boolean_t losttncomplete; /* TRUE if -LOSTTNCOMPLETE was specified, FALSE otherwise */ boolean_t showfreeze; /* TRUE if -FREEZE was specified with no value, FALSE otherwise */ boolean_t setfreeze; /* TRUE if -FREEZE was specified with a value, FALSE otherwise */ boolean_t freezeval; /* TRUE for -FREEZE=ON, FALSE for -FREEZE=OFF */ boolean_t setcomment; /* TRUE if -COMMENT was specified, FALSE otherwise */ boolean_t jnlfileonly; /* TRUE if -JNLFILEONLY was specified, FALSE otherwise */ boolean_t zerobacklog; /* TRUE if -ZEROBACKLOG was specified, FALSE otherwise */ int4 cmplvl; int4 shutdown_time; gtm_uint64_t buffsize; int4 mode; int4 secondary_port; uint4 src_log_interval; int4 connect_parms[GTMSOURCE_CONN_PARMS_COUNT]; char filter_cmd[MAX_FILTER_CMD_LEN]; char secondary_host[MAX_HOST_NAME_LEN]; char log_file[MAX_FN_LEN + 1]; char secondary_instname[MAX_INSTNAME_LEN]; /* instance name specified in -INSTSECONDARY qualifier */ char freeze_comment[MAX_FREEZE_COMMENT_LEN]; # ifdef GTM_TLS char tlsid[MAX_TLSKEY_LEN]; int4 renegotiate_interval; # endif } gtmsource_options_t; /********** Source server function prototypes **********/ int gtmsource(void); boolean_t gtmsource_is_heartbeat_overdue(time_t *now, repl_heartbeat_msg_ptr_t overdue_heartbeat); int gtmsource_alloc_filter_buff(int bufsiz); int gtmsource_alloc_msgbuff(int maxbuffsize, boolean_t discard_oldbuff); int gtmsource_alloc_tcombuff(void); void gtmsource_free_filter_buff(void); void gtmsource_free_msgbuff(void); void gtmsource_free_tcombuff(void); int gtmsource_changelog(void); int gtmsource_checkhealth(void); int gtmsource_comm_init(boolean_t print_addresolve_error); int gtmsource_ctl_close(void); int gtmsource_ctl_init(void); int gtmsource_jnlpool(void); int gtmsource_end1(boolean_t auto_shutdown); int gtmsource_est_conn(void); int gtmsource_get_jnlrecs(uchar_ptr_t buff, int *data_len, int maxbufflen, boolean_t read_multiple); int gtmsource_get_opt(void); int gtmsource_ipc_cleanup(boolean_t auto_shutdown, int *exit_status, int4 *num_src_servers_running); int gtmsource_mode_change(int to_mode); int gtmsource_poll_actions(boolean_t poll_secondary); int gtmsource_process(void); void gtmsource_recv_ctl(void); boolean_t gtmsource_recv_ctl_nowait(void); int gtmsource_readfiles(uchar_ptr_t buff, int *data_len, int maxbufflen, boolean_t read_multiple); int gtmsource_readpool(uchar_ptr_t buff, int *data_len, int maxbufflen, boolean_t read_multiple, qw_num stop_read_at); int gtmsource_recv_restart(seq_num *recvd_jnl_seqno, int *msg_type, int *start_flags); int gtmsource_set_lookback(void); int gtmsource_showbacklog(void); int gtmsource_shutdown(boolean_t auto_shutdown, int exit_status); int gtmsource_srch_restart(seq_num recvd_jnl_seqno, int recvd_start_flags); int gtmsource_statslog(void); int gtmsource_stopfilter(void); int gtmsource_update_zqgblmod_seqno_and_tn(seq_num resync_seqno); void gtmsource_end(void); void gtmsource_exit(int exit_status); void gtmsource_seqno_init(boolean_t this_side_std_null_coll); void gtmsource_stop(boolean_t exit); void gtmsource_sigstop(void); boolean_t jnlpool_hasnt_overflowed(jnlpool_ctl_ptr_t jctl, gtm_uint64_t jnlpool_size, qw_num read_addr); void jnlpool_detach(void); void jnlpool_init(jnlpool_user pool_user, boolean_t gtmsource_startup, boolean_t *jnlpool_creator, gd_addr *gd_ptr); int gtmsource_init_heartbeat(void); int gtmsource_process_heartbeat(repl_heartbeat_msg_ptr_t heartbeat_msg); int gtmsource_send_heartbeat(time_t *now); int gtmsource_stop_heartbeat(void); void gtmsource_flush_fh(seq_num resync_seqno); void gtmsource_reinit_logseqno(void); void gtmsource_rootprimary_init(seq_num start_seqno); int gtmsource_needrestart(void); int gtmsource_losttncomplete(void); void gtmsource_jnl_release_timer(TID tid, int4 interval_len, int *interval_ptr); int gtmsource_start_jnl_release_timer(void); int gtmsource_stop_jnl_release_timer(void); void gtmsource_onln_rlbk_clnup(void); int gtmsource_showfreeze(void); int gtmsource_setfreeze(void); seq_num gtmsource_checkforbacklog(void); #ifdef GTM_TLS boolean_t gtmsource_exchange_tls_info(void); #endif /********** Miscellaneous prototypes **********/ void repl_phase2_cleanup(jnlpool_addrs *jpa); void repl_phase2_salvage(jnlpool_addrs *jpa, jnlpool_ctl_ptr_t jpl, jpl_phase2_in_prog_t *deadCmt); #endif /* GTMSOURCE_H */ fis-gtm-V7.0-005/sr_unix/gtmsource_changelog.c0000644000032200000250000001056214342376330020230 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2017 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_fcntl.h" #include "gtmio.h" #include "repl_sp.h" #include "gtm_time.h" #include "gtm_string.h" #include "gtm_inet.h" /* Required for gtmsource.h */ #include #ifdef VMS #include /* Required for gtmsource.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "repl_shutdcode.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_dbg.h" #include "repl_sem.h" #include "util.h" #include "repl_log.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF gtmsource_options_t gtmsource_options; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; error_def(ERR_REPLLOGOPN); error_def(ERR_CHANGELOGINTERVAL); int gtmsource_changelog(void) { uint4 changelog_accepted = 0; int log_fd = 0; /*used to indicate whether the new specified log file is writable*/ int close_status = 0; /*used to indicate if log file is successfully closed*/ char* err_code; int save_errno = 0; int retry_count = 5; assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); repl_log(stderr, TRUE, TRUE, "Initiating CHANGELOG operation on source server pid [%d] for secondary instance [%s]\n", jnlpool->gtmsource_local->gtmsource_pid, jnlpool->gtmsource_local->secondary_instname); if (0 != jnlpool->gtmsource_local->changelog) { retry_count = 5; while (0 != retry_count--) { LONG_SLEEP(5); if (!jnlpool->gtmsource_local->changelog) break; } } if (0 != jnlpool->gtmsource_local->changelog) { util_out_print("Change log is already in progress. Not initiating change in log file or log interval", TRUE); return (ABNORMAL_SHUTDOWN); } if ('\0' != gtmsource_options.log_file[0]) /* trigger change in log file */ { if (0 != STRCMP(jnlpool->gtmsource_local->log_file, gtmsource_options.log_file)) { /*check if the new log file is writable*/ OPENFILE3_CLOEXEC(gtmsource_options.log_file, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, log_fd); if (log_fd < 0) { save_errno = ERRNO; err_code = STRERROR(save_errno); if ('\0' != jnlpool->gtmsource_local->log_file[0]) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_REPLLOGOPN, 6, LEN_AND_STR(gtmsource_options.log_file), LEN_AND_STR(err_code), LEN_AND_STR(jnlpool->gtmsource_local->log_file)); else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_REPLLOGOPN, 6, LEN_AND_STR(gtmsource_options.log_file), LEN_AND_STR(err_code), LEN_AND_STR(NULL_DEVICE)); } else { CLOSEFILE_IF_OPEN(log_fd, close_status); assert(close_status==0); changelog_accepted |= REPLIC_CHANGE_LOGFILE; STRCPY(jnlpool->gtmsource_local->log_file, gtmsource_options.log_file); util_out_print("Change log initiated with file !AD", TRUE, LEN_AND_STR(gtmsource_options.log_file)); } } else util_out_print("Log file is already !AD. Not initiating change in log file", TRUE, LEN_AND_STR(gtmsource_options.log_file)); } if (0 != gtmsource_options.src_log_interval) /* trigger change in log interval */ { if (gtmsource_options.src_log_interval != jnlpool->gtmsource_local->log_interval) { changelog_accepted |= REPLIC_CHANGE_LOGINTERVAL; jnlpool->gtmsource_local->log_interval = gtmsource_options.src_log_interval; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_CHANGELOGINTERVAL, 5, LEN_AND_LIT("Source"), LEN_AND_STR(jnlpool->gtmsource_local->log_file), gtmsource_options.src_log_interval); } else util_out_print("Log interval is already !UL. Not initiating change in log interval", TRUE, gtmsource_options.src_log_interval); } if (0 != changelog_accepted) jnlpool->gtmsource_local->changelog = changelog_accepted; else util_out_print("No change to log file or log interval", TRUE); return (0 != save_errno) ? ABNORMAL_SHUTDOWN : NORMAL_SHUTDOWN; } fis-gtm-V7.0-005/sr_unix/gtmsource_checkforbacklog.c0000644000032200000250000000530614342376330021410 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2020 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_string.h" #include "gtm_stdio.h" #if !defined(__MVS__) && !defined(VMS) #include #endif #include #include "gtm_inet.h" #include #ifdef VMS #include /* Required for gtmsource.h */ #endif #include "error.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_dbg.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "util.h" #include "repl_log.h" #include "is_proc_alive.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF gtmsource_options_t gtmsource_options; seq_num gtmsource_checkforbacklog(void) { seq_num seq_num, heartbeat_jnl_seqno, jnl_seqno, read_jnl_seqno, backlog_count = 0; gtmsource_local_ptr_t gtmsourcelocal_ptr; int4 index; boolean_t srv_alive; uint4 gtmsource_pid; jnl_seqno = jnlpool->jnlpool_ctl->jnl_seqno; if (NULL != jnlpool->gtmsource_local) gtmsourcelocal_ptr = jnlpool->gtmsource_local; else gtmsourcelocal_ptr = &jnlpool->gtmsource_local_array[0]; for (index = 0; index < NUM_GTMSRC_LCL; index++, gtmsourcelocal_ptr++) { if ('\0' == gtmsourcelocal_ptr->secondary_instname[0]) { assert(NULL == jnlpool->gtmsource_local); continue; } gtmsource_pid = gtmsourcelocal_ptr->gtmsource_pid; /* 0 == gtmsource_pid evaluates to TRUE when a Source Server has been shut down */ if ((NULL == jnlpool->gtmsource_local) && (0 == gtmsource_pid)) continue; read_jnl_seqno = gtmsourcelocal_ptr->read_jnl_seqno; heartbeat_jnl_seqno = gtmsourcelocal_ptr->heartbeat_jnl_seqno; /* jnl_seqno >= read_jnl_seqno is the most common case; see gtmsource_readpool() for when the rare case can occur */ seq_num = (jnl_seqno >= read_jnl_seqno) ? jnl_seqno - read_jnl_seqno : 0; /* read_jnl_seqno is 1 for empty databases */ if ((seq_num == 0) && (read_jnl_seqno != 1)) seq_num = (read_jnl_seqno > heartbeat_jnl_seqno) ? 1 : 0; backlog_count += seq_num; if (NULL != jnlpool->gtmsource_local) break; } return backlog_count; /* backlog_count is non-zero when any one Source Server has a backlog and -INST is not specified. */ } fis-gtm-V7.0-005/sr_unix/gtmsource_checkhealth.c0000755000032200000250000001400614342376330020544 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2019 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_string.h" #include "gtm_inet.h" #include #include #ifdef UNIX #include #endif #ifdef VMS #include /* Required for gtmsource.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_dbg.h" #include "gtm_stdio.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "repl_sp.h" #include "repl_log.h" #include "is_proc_alive.h" #include "gtmmsg.h" #include "sgtm_putmsg.h" #include "util.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF gtmsource_options_t gtmsource_options; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; GBLREF gd_addr *gd_header; error_def(ERR_NOTALLDBOPN); error_def(ERR_REPLJNLCLOSED); error_def(ERR_SRCSRVNOTEXIST); int gtmsource_checkhealth(void) { uint4 gtmsource_pid; int status, semval, save_errno; boolean_t srv_alive, all_files_open; gtmsource_local_ptr_t gtmsourcelocal_ptr; int4 index, num_servers; seq_num reg_seqno, jnlseqno; gd_region *reg, *region_top; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; char errtxt[OUT_BUFF_SIZE]; char *modestr; assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); if (NULL != jnlpool->gtmsource_local) /* Check health of a specific source server */ gtmsourcelocal_ptr = jnlpool->gtmsource_local; else gtmsourcelocal_ptr = &jnlpool->gtmsource_local_array[0]; num_servers = 0; status = SRV_ALIVE; for (index = 0; index < NUM_GTMSRC_LCL; index++, gtmsourcelocal_ptr++) { if ('\0' == gtmsourcelocal_ptr->secondary_instname[0]) { assert(NULL == jnlpool->gtmsource_local); continue; } gtmsource_pid = gtmsourcelocal_ptr->gtmsource_pid; /* If CHECKHEALTH on a specific secondary instance is requested, print the health information irrespective * of whether a source server for that instance is alive or not. For CHECKHEALTH on ALL secondary instances * print health information only for those instances that have an active or passive source server alive. */ if ((NULL == jnlpool->gtmsource_local) && (0 == gtmsource_pid)) continue; repl_log(stdout, TRUE, TRUE, "Initiating CHECKHEALTH operation on source server pid [%d] for secondary instance" " name [%s]\n", gtmsource_pid, gtmsourcelocal_ptr->secondary_instname); srv_alive = (0 == gtmsource_pid) ? FALSE : is_proc_alive(gtmsource_pid, 0); if (srv_alive) { if (GTMSOURCE_MODE_ACTIVE == gtmsourcelocal_ptr->mode) modestr = "ACTIVE"; else if (GTMSOURCE_MODE_ACTIVE_REQUESTED == gtmsourcelocal_ptr->mode) modestr = "ACTIVE REQUESTED"; else if (GTMSOURCE_MODE_PASSIVE == gtmsourcelocal_ptr->mode) modestr = "PASSIVE"; else if (GTMSOURCE_MODE_PASSIVE_REQUESTED == gtmsourcelocal_ptr->mode) modestr = "PASSIVE REQUESTED"; else { assert(gtmsourcelocal_ptr->mode != gtmsourcelocal_ptr->mode); modestr = "UNKNOWN"; } repl_log(stderr, FALSE, TRUE, FORMAT_STR1, gtmsource_pid, "Source server", "", modestr); status |= SRV_ALIVE; num_servers++; } else { repl_log(stderr, FALSE, TRUE, FORMAT_STR, gtmsource_pid, "Source server", " NOT"); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SRCSRVNOTEXIST, 2, LEN_AND_STR(gtmsourcelocal_ptr->secondary_instname)); status |= SRV_DEAD; } if (NULL != jnlpool->gtmsource_local) break; } if (NULL == jnlpool->gtmsource_local) { /* Compare number of servers that were found alive with the current value of the COUNT semaphore. * If they are not equal, report the discrepancy. */ semval = get_sem_info(SOURCE, SRC_SERV_COUNT_SEM, SEM_INFO_VAL); if (-1 == semval) { save_errno = errno; repl_log(stderr, FALSE, TRUE, "Error fetching source server count semaphore value : %s\n", STRERROR(save_errno)); status |= SRV_ERR; } else if (semval != num_servers) { repl_log(stderr, FALSE, FALSE, "Error : Expected %d source server(s) to be alive but found %d actually alive\n", semval, num_servers); repl_log(stderr, FALSE, TRUE, "Error : Check if any pid reported above is NOT a source server process\n"); status |= SRV_ERR; } } /* Check that there are no regions with replication state = WAS_ON (i.e. repl_was_open). If so report that. * But to determine that, we need to attach to all the database regions. */ gvinit(); /* We use the same code dse uses to open all regions but we must make sure they are all open before proceeding. */ all_files_open = region_init(FALSE); if (!all_files_open) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOTALLDBOPN); status |= SRV_ERR; } else { for (reg = gd_header->regions, region_top = gd_header->regions + gd_header->n_regions; reg < region_top; reg++) { csa = &FILE_INFO(reg)->s_addrs; csd = csa->hdr; if (REPL_WAS_ENABLED(csd)) { assert(!JNL_ENABLED(csd) || REPL_ENABLED(csd)); /* || is for turning replication on concurrently */ reg_seqno = csd->reg_seqno; jnlseqno = (NULL != jnlpool->jnlpool_ctl) ? jnlpool->jnlpool_ctl->jnl_seqno : MAX_SEQNO; sgtm_putmsg(errtxt, OUT_BUFF_SIZE, VARLSTCNT(8) ERR_REPLJNLCLOSED, 6, DB_LEN_STR(reg), ®_seqno, ®_seqno, &jnlseqno, &jnlseqno); repl_log(stderr, FALSE, TRUE, errtxt); status |= SRV_ERR; } } } if (jnlpool->jnlpool_ctl->freeze) { repl_log(stderr, FALSE, FALSE, "Warning: Instance Freeze is ON\n"); repl_log(stderr, FALSE, TRUE, " Freeze Comment: %s\n", jnlpool->jnlpool_ctl->freeze_comment); status |= SRV_ERR; } return (status + NORMAL_SHUTDOWN); } fis-gtm-V7.0-005/sr_unix/gtmsource_end.c0000644000032200000250000001172114342376330017045 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2018 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_unistd.h" /* for close() */ #include "gtm_ipc.h" #include #include #include #include "gtm_inet.h" #include "gtm_string.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_dbg.h" #include "gtm_stdio.h" #include "repl_shutdcode.h" #include "eintr_wrappers.h" #include "jnl.h" #include "repl_filter.h" #include "repl_sem.h" #include "mutex.h" #include "repl_log.h" #include "repl_comm.h" #include "have_crit.h" #include "anticipatory_freeze.h" #ifdef GTM_TLS #include "gtm_repl.h" #endif GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF uint4 process_id; GBLREF int gtmsource_sock_fd; GBLREF int gtmsource_log_fd; GBLREF FILE *gtmsource_log_fp; GBLREF int gtmsource_filter; GBLREF boolean_t gtmsource_logstats; GBLREF int gtmsource_statslog_fd; GBLREF FILE *gtmsource_statslog_fp; GBLREF unsigned char *gtmsource_tcombuff_start; GBLREF qw_num repl_source_cmp_sent; GBLREF qw_num repl_source_data_sent; GBLREF qw_num repl_source_msg_sent; GBLREF seq_num seq_num_zero; GBLREF repl_msg_ptr_t gtmsource_msgp; GBLREF uchar_ptr_t repl_filter_buff; GBLREF int pool_init; int gtmsource_end1(boolean_t auto_shutdown) { int exit_status, idx, status, save_errno; seq_num read_jnl_seqno, jnlpool_seqno, diff_seqno, jnlpool_strm_seqno[MAX_SUPPL_STRMS]; int fclose_res; sgmnt_addrs *repl_csa; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; gtmsource_ctl_close(); DEBUG_ONLY(repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs;) assert(!repl_csa->hold_onto_crit); /* so it is ok to invoke and "rel_lock" unconditionally */ rel_lock(jnlpool->jnlpool_dummy_reg); mutex_cleanup(jnlpool->jnlpool_dummy_reg); exit_status = NORMAL_SHUTDOWN; if (!auto_shutdown) jnlpool->gtmsource_local->shutdown = NORMAL_SHUTDOWN; read_jnl_seqno = jnlpool->gtmsource_local->read_jnl_seqno; jnlpool_seqno = jnlpool->jnlpool_ctl->jnl_seqno; for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) jnlpool_strm_seqno[idx] = jnlpool->jnlpool_ctl->strm_seqno[idx]; jnlpool->gtmsource_local->gtmsource_pid = 0; jnlpool->gtmsource_local->gtmsource_state = GTMSOURCE_DUMMY_STATE; /* Detach from journal pool, except if IFOE is configured, in which case we need the journal pool attached * so that we can check for instance freeze in database rundown, or if auto_shutdown is set. * In those cases, the detach will happen automatically when the process terminates. */ if (!auto_shutdown && !INST_FREEZE_ON_ERROR_POLICY) { JNLPOOL_SHMDT(jnlpool, status, save_errno); if (0 > status) repl_log(gtmsource_log_fp, FALSE, TRUE, "Error detaching from journal pool : %s\n", STRERROR(save_errno)); } gtmsource_free_msgbuff(); gtmsource_free_tcombuff(); gtmsource_free_filter_buff(); gtmsource_stop_heartbeat(); repl_close(>msource_sock_fd); # ifdef GTM_TLS /* Free up the SSL/TLS socket structures. */ if (NULL != repl_tls.sock) gtm_tls_session_close(&repl_tls.sock); assert(NULL == repl_tls.sock); /* Free up the SSL/TLS context now that we are shutting down. */ if (NULL != tls_ctx) gtm_tls_fini(&tls_ctx); assert(NULL == tls_ctx); # endif if (jnlpool_seqno) { repl_log(gtmsource_log_fp, TRUE, FALSE, "REPL INFO - Current Jnlpool Seqno : %llu\n", jnlpool_seqno); for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) { if (jnlpool_strm_seqno[idx]) repl_log(gtmsource_log_fp, TRUE, FALSE, "REPL INFO - Stream # %d : Current Jnlpool Stream Seqno " ": %llu\n", idx, jnlpool_strm_seqno[idx]); } jnlpool_seqno--; } if (read_jnl_seqno) read_jnl_seqno--; diff_seqno = jnlpool_seqno - read_jnl_seqno; repl_log(gtmsource_log_fp, TRUE, FALSE, "REPL INFO - Last Seqno written in jnlpool : %llu", jnlpool_seqno); repl_log(gtmsource_log_fp, FALSE, FALSE, " Last Seqno sent : %llu", read_jnl_seqno); repl_log(gtmsource_log_fp, FALSE, TRUE, " Number of unsent Seqno : %llu\n", 0 < diff_seqno ? diff_seqno : 0); repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL INFO - Jnl Total : %llu Msg Total : %llu CmpMsg Total : %llu\n", repl_source_data_sent, repl_source_msg_sent, repl_source_cmp_sent); if (gtmsource_filter & EXTERNAL_FILTER) repl_stop_filter(); if (auto_shutdown) return (exit_status); else gtmsource_exit(exit_status - NORMAL_SHUTDOWN); return -1; /* This will never get executed, added to make compiler happy */ } void gtmsource_end(void) { gtmsource_end1(FALSE); } fis-gtm-V7.0-005/sr_unix/gtmsource_flush_fh.c0000755000032200000250000000361214342376330020100 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2018 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_instance.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF time_t gtmsource_last_flush_time; GBLREF volatile time_t gtmsource_now; GBLREF gtmsource_state_t gtmsource_state; /* Flush the source server's current resync_seqno into the corresponding slot in the replication instance file header */ void gtmsource_flush_fh(seq_num resync_seqno) { sgmnt_addrs *repl_csa; assert((NULL != jnlpool) && (NULL != jnlpool->jnlpool_dummy_reg) && jnlpool->jnlpool_dummy_reg->open); # ifdef DEBUG repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; assert(!repl_csa->hold_onto_crit); ASSERT_VALID_JNLPOOL(repl_csa); # endif jnlpool->gtmsource_local->read_jnl_seqno = resync_seqno; gtmsource_last_flush_time = gtmsource_now; if (jnlpool->gtmsource_local->last_flush_resync_seqno == resync_seqno) return; /* need to flush resync_seqno to instance file. Grab the journal pool lock before flushing */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, HANDLE_CONCUR_ONLINE_ROLLBACK); /* sets gtmsource_state */ if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) return; repl_inst_flush_gtmsrc_lcl(); /* this requires the ftok semaphore to be held */ rel_lock(jnlpool->jnlpool_dummy_reg); return; } fis-gtm-V7.0-005/sr_unix/gtmsource_freeze.c0000644000032200000250000000450714342376330017563 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2017 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 "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_sem.h" #include "repl_shutdcode.h" #include "filestruct.h" #include "util.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF gtmsource_options_t gtmsource_options; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; error_def(ERR_REPLINSTFREEZECOMMENT); error_def(ERR_REPLINSTFROZEN); error_def(ERR_REPLINSTUNFROZEN); int gtmsource_showfreeze(void) { boolean_t instance_frozen; assert(!holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); instance_frozen = jnlpool->jnlpool_ctl->freeze; util_out_print("Instance Freeze: !AZ", TRUE, instance_frozen ? "ON" : "OFF"); if (jnlpool->jnlpool_ctl->freeze) util_out_print(" Freeze Comment: !AZ", TRUE, jnlpool->jnlpool_ctl->freeze_comment); return (instance_frozen ? (SRV_ERR + NORMAL_SHUTDOWN) : NORMAL_SHUTDOWN); } int gtmsource_setfreeze(void) { if (gtmsource_options.freezeval) { assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); /* sets gtmsource_state */ } else assert(!holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); jnlpool->jnlpool_ctl->freeze = gtmsource_options.freezeval; if (gtmsource_options.setcomment) STRNCPY_STR(jnlpool->jnlpool_ctl->freeze_comment, gtmsource_options.freeze_comment, SIZEOF(jnlpool->jnlpool_ctl->freeze_comment)); if (gtmsource_options.freezeval) { send_msg_csa(NULL, VARLSTCNT(3) ERR_REPLINSTFROZEN, 1, jnlpool->repl_inst_filehdr->inst_info.this_instname); send_msg_csa(NULL, VARLSTCNT(3) ERR_REPLINSTFREEZECOMMENT, 1, jnlpool->jnlpool_ctl->freeze_comment); rel_lock(jnlpool->jnlpool_dummy_reg); } else { send_msg_csa(NULL, VARLSTCNT(3) ERR_REPLINSTUNFROZEN, 1, jnlpool->repl_inst_filehdr->inst_info.this_instname); } return (NORMAL_SHUTDOWN); } fis-gtm-V7.0-005/sr_unix/gtmsource_get_opt.c0000755000032200000250000005413214342376330017746 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2022 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_netdb.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_string.h" #include "gtm_ctype.h" #if !defined(__MVS__) && !defined(__CYGWIN__) && (!defined(__GNUC__) && defined(__hpux)) #include #endif #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "jnl.h" #include "gtmsource.h" #include "cli.h" #include "gtm_stdio.h" #include "util.h" #include "repl_log.h" #include "gtmmsg.h" /* for "gtm_putmsg" prototype */ #include "gtm_logicals.h" /* for GTM_REPL_INSTSECONDARY */ #include "trans_log_name.h" #include "iosp.h" /* for SS_NORMAL */ #include "gtm_zlib.h" #ifdef GTM_TLS #include "gtm_repl.h" #endif #define MAX_SECONDARY_LEN (MAX_HOST_NAME_LEN + 11) /* +11 for ':' and port number */ #define DEFAULT_SHUTDOWN_TIMEOUT 120 #define MAX_SHUTDOWN_TIMEOUT 3600 /* 1 hour */ #define GTMSOURCE_CONN_PARMS_LEN ((10 + 1) * GTMSOURCE_CONN_PARMS_COUNT - 1) GBLREF gtmsource_options_t gtmsource_options; #ifdef GTM_TLS GBLREF repl_tls_info_t repl_tls; #endif GBLREF volatile boolean_t timer_in_handler; GBLREF gd_addr *gd_header; error_def(ERR_BADCONNECTPARAM); error_def(ERR_BADPARAMCOUNT); error_def(ERR_LOGTOOLONG); error_def(ERR_REPLINSTSECLEN); error_def(ERR_REPLINSTSECUNDF); error_def(ERR_INVSHUTDOWN); int gtmsource_get_opt(void) { boolean_t connect_parms_badval, connect_parm_digit, dotted_notation, log, log_interval_specified; boolean_t plaintext_fallback, secondary; char *c, *connect_parm, *connect_parms_str, *connect_parm_token_str; char tmp_connect_parms_str[GTMSOURCE_CONN_PARMS_LEN + 1]; char freeze_comment[SIZEOF(gtmsource_options.freeze_comment)]; char freeze_val[SIZEOF("OFF")]; /* "ON" or "OFF", including null terminator */ char inst_name[MAX_FN_LEN + 1], *ip_end, *strtokptr; char secondary_sys[MAX_SECONDARY_LEN]; char statslog_val[SIZEOF("OFF")]; /* "ON" or "OFF" */ char update_val[SIZEOF("DISABLE")]; /* "ENABLE" or "DISABLE" */ gtm_int64_t buffsize; unsigned short connect_parms_index, counter; int index = 0, port_len, renegotiate_interval, status; int timeout_status; mstr log_nam, trans_name; struct hostent *sec_hostentry; unsigned short connect_parms_str_len, filter_cmd_len, freeze_comment_len, freeze_val_len, inst_name_len, log_file_len; unsigned short secondary_len, statslog_val_len, tlsid_len, update_val_len; memset((char *)>msource_options, 0, SIZEOF(gtmsource_options)); gtmsource_options.start = (CLI_PRESENT == cli_present("START")); gtmsource_options.shut_down = (CLI_PRESENT == cli_present("SHUTDOWN")); gtmsource_options.zerobacklog = (CLI_PRESENT == cli_present("ZEROBACKLOG")); gtmsource_options.activate = (CLI_PRESENT == cli_present("ACTIVATE")); gtmsource_options.deactivate = (CLI_PRESENT == cli_present("DEACTIVATE")); gtmsource_options.checkhealth = (CLI_PRESENT == cli_present("CHECKHEALTH")); gtmsource_options.statslog = (CLI_PRESENT == cli_present("STATSLOG")); gtmsource_options.showbacklog = (CLI_PRESENT == cli_present("SHOWBACKLOG")); gtmsource_options.changelog = (CLI_PRESENT == cli_present("CHANGELOG")); gtmsource_options.stopsourcefilter = (CLI_PRESENT == cli_present("STOPSOURCEFILTER")); gtmsource_options.needrestart = (CLI_PRESENT == cli_present("NEEDRESTART")); gtmsource_options.losttncomplete = (CLI_PRESENT == cli_present("LOSTTNCOMPLETE")); gtmsource_options.jnlpool = (CLI_PRESENT == cli_present("JNLPOOL")); secondary = (CLI_PRESENT == cli_present("SECONDARY")); gtmsource_options.rootprimary = ROOTPRIMARY_UNSPECIFIED; /* to indicate unspecified state */ if ((CLI_PRESENT == cli_present("ROOTPRIMARY")) || (CLI_PRESENT == cli_present("UPDOK"))) gtmsource_options.rootprimary = ROOTPRIMARY_SPECIFIED; else if ((CLI_PRESENT == cli_present("PROPAGATEPRIMARY")) || (CLI_PRESENT == cli_present("UPDNOTOK"))) gtmsource_options.rootprimary = PROPAGATEPRIMARY_SPECIFIED; else { /* Neither ROOTPRIMARY (or UPDOK) nor PROPAGATEPRIMARY (or UPDNOTOK) specified. Assume default values. * Assume ROOTPRIMARY for -START -SECONDARY (active source server start) and -ACTIVATE commands. * Assume PROPAGATEPRIMARY for -START -PASSIVE (passive source server start) and -DEACTIVATE commands. */ if ((gtmsource_options.start && secondary) || gtmsource_options.activate) gtmsource_options.rootprimary = ROOTPRIMARY_SPECIFIED; if ((gtmsource_options.start && !secondary) || gtmsource_options.deactivate) gtmsource_options.rootprimary = PROPAGATEPRIMARY_SPECIFIED; } gtmsource_options.instsecondary = (CLI_PRESENT == cli_present("INSTSECONDARY")); if (gtmsource_options.instsecondary) { /* -INSTSECONDARY is specified in the command line. */ inst_name_len = SIZEOF(inst_name);; if (!cli_get_str("INSTSECONDARY", &inst_name[0], &inst_name_len)) { util_out_print("Error parsing INSTSECONDARY qualifier", TRUE); return(-1); } } else { /* Check if environment variable "gtm_repl_instsecondary" is defined. * Do that only if any of the following qualifiers is present as these are the only ones that honour it. * Mandatory : START, ACTIVATE, DEACTIVATE, STOPSOURCEFILTER, CHANGELOG, STATSLOG, NEEDRESTART, * Optional : CHECKHEALTH, SHOWBACKLOG or SHUTDOWN */ inst_name_len = 0; /* 4SCA: Even though this is guarded by gtmsource_options.instsecondary */ if (gtmsource_options.start || gtmsource_options.activate || gtmsource_options.deactivate || gtmsource_options.stopsourcefilter || gtmsource_options.changelog || gtmsource_options.statslog || gtmsource_options.needrestart || gtmsource_options.checkhealth || gtmsource_options.showbacklog || gtmsource_options.shut_down) { log_nam.addr = GTM_REPL_INSTSECONDARY; log_nam.len = SIZEOF(GTM_REPL_INSTSECONDARY) - 1; trans_name.addr = &inst_name[0]; if (SS_NORMAL == (status = TRANS_LOG_NAME(&log_nam, &trans_name, inst_name, SIZEOF(inst_name), do_sendmsg_on_log2long))) { gtmsource_options.instsecondary = TRUE; inst_name_len = trans_name.len; } else if (!gtmsource_options.checkhealth && !gtmsource_options.showbacklog && !gtmsource_options.shut_down) { if (SS_LOG2LONG == status) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_LOGTOOLONG, 3, log_nam.len, log_nam.addr, SIZEOF(inst_name) - 1); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_REPLINSTSECUNDF); return (-1); } } } if (gtmsource_options.instsecondary) { /* Secondary instance name specified either through -INSTSECONDARY or "gtm_repl_instsecondary" */ inst_name[inst_name_len] = '\0'; if ((MAX_INSTNAME_LEN <= inst_name_len) || (0 == inst_name_len)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_REPLINSTSECLEN, 2, inst_name_len, inst_name); return (-1); } assert((inst_name_len + 1) <= MAX_INSTNAME_LEN); memcpy(gtmsource_options.secondary_instname, inst_name, inst_name_len + 1); /* copy terminating '\0' as well */ } if (gtmsource_options.start || gtmsource_options.activate) { if (secondary) { secondary_len = MAX_SECONDARY_LEN; if (!cli_get_str("SECONDARY", secondary_sys, &secondary_len)) { util_out_print("Error parsing SECONDARY qualifier", TRUE); return(-1); } /* Parse secondary_sys into secondary_host * and secondary_port */ c = secondary_sys; dotted_notation = TRUE; if ('[' == *c) { ip_end = strchr(++c, ']'); if (NULL == ip_end || 0 == (index = ip_end - c)) { util_out_print("Invalid IP address !AD", TRUE, LEN_AND_STR(secondary_sys)); return(-1); } memcpy(gtmsource_options.secondary_host, c, index); gtmsource_options.secondary_host[index] = '\0'; c = ip_end + 1; } else { while(*c && (':' != *c)) gtmsource_options.secondary_host[index++] = *c++; gtmsource_options.secondary_host[index] = '\0'; } if (':' != *c) { util_out_print("Secondary port number should be specified", TRUE); return(-1); } port_len = strlen(++c); errno = 0; if (((0 == (gtmsource_options.secondary_port = ATOI(c))) && (0 != errno)) || (0 >= gtmsource_options.secondary_port)) { util_out_print("Error parsing secondary port number !AD", TRUE, LEN_AND_STR(c)); return(-1); } } if (CLI_PRESENT == cli_present("CONNECTPARAMS")) { gtmsource_options.connect_parms[GTMSOURCE_CONN_HARD_TRIES_COUNT] = REPL_CONN_HARD_TRIES_COUNT; gtmsource_options.connect_parms[GTMSOURCE_CONN_HARD_TRIES_PERIOD] = REPL_CONN_HARD_TRIES_PERIOD; gtmsource_options.connect_parms[GTMSOURCE_CONN_SOFT_TRIES_PERIOD] = REPL_CONN_SOFT_TRIES_PERIOD; gtmsource_options.connect_parms[GTMSOURCE_CONN_ALERT_PERIOD] = REPL_CONN_ALERT_ALERT_PERIOD; gtmsource_options.connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD] = REPL_CONN_HEARTBEAT_PERIOD; gtmsource_options.connect_parms[GTMSOURCE_CONN_HEARTBEAT_MAX_WAIT] = REPL_CONN_HEARTBEAT_MAX_WAIT; connect_parms_str_len = GTMSOURCE_CONN_PARMS_LEN + 1; if (!cli_get_str("CONNECTPARAMS", tmp_connect_parms_str, &connect_parms_str_len)) { util_out_print("Error parsing CONNECTPARAMS qualifier", TRUE); return -1; } connect_parms_str = &tmp_connect_parms_str[0]; for (connect_parms_index = 0, connect_parms_badval = FALSE, connect_parm_token_str = connect_parms_str; !connect_parms_badval && connect_parms_index <= GTMSOURCE_CONN_PARMS_COUNT && /* inline assignment */ (NULL != (connect_parm = STRTOK_R(connect_parm_token_str, GTMSOURCE_CONN_PARMS_DELIM, &strtokptr))); connect_parms_index++, connect_parm_token_str = NULL) { if (GTMSOURCE_CONN_PARMS_COUNT > connect_parms_index) { errno = 0; if ((0 == (gtmsource_options.connect_parms[connect_parms_index] = ATOI(connect_parm)) && (0 != errno)) /* WARNING assignment above */ || (0 >= gtmsource_options.connect_parms[connect_parms_index])) connect_parms_badval = TRUE; } connect_parm_digit = TRUE; for (counter = 0; counter < strlen(connect_parm); counter++) if (!ISDIGIT_ASCII(connect_parm[counter])) { connect_parm_digit = FALSE; connect_parms_badval = TRUE; break; } if (connect_parms_badval) { switch(connect_parms_index) {/*Addition validation and error reporting for each param in -CONNECTPARAM */ case GTMSOURCE_CONN_HARD_TRIES_COUNT: if ((connect_parm_digit) && (0 == ATOI(connect_parm))) { connect_parms_badval = FALSE; break; } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT (8) ERR_BADCONNECTPARAM, 2, LEN_AND_LIT("number of hard connection attempts"), ERR_TEXT, 2, LEN_AND_LIT("Specify either the number of hard connection" " attempts or 0 to disable hard connection attempts")) ; return -1; } case GTMSOURCE_CONN_HARD_TRIES_PERIOD: gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT (8) ERR_BADCONNECTPARAM, 2, LEN_AND_LIT("wait time for hard connection attempts"), ERR_TEXT, 2, LEN_AND_LIT("Specify the wait time in milliseconds")); return -1; case GTMSOURCE_CONN_SOFT_TRIES_PERIOD: gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT (8) ERR_BADCONNECTPARAM, 2, LEN_AND_LIT("wait time for soft connection attempts"), ERR_TEXT, 2, LEN_AND_LIT("Specify the wait time in seconds")); return -1; case GTMSOURCE_CONN_ALERT_PERIOD: if ((connect_parm_digit) && (0 == ATOI(connect_parm))) { connect_parms_badval = FALSE; break; } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT (8) ERR_BADCONNECTPARAM, 2, LEN_AND_LIT("alert period for REPLALERT messages"), ERR_TEXT, 2, LEN_AND_LIT("Specify the approximate alert period (in " "seconds) for REPLALERT messages or 0 to disable " "REPLALERT messages")); return -1; } case GTMSOURCE_CONN_HEARTBEAT_PERIOD: gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT (8) ERR_BADCONNECTPARAM, 2, LEN_AND_LIT("heartbeat interval"), ERR_TEXT, 2, LEN_AND_LIT("Specify the interval (in seconds) between " "heartbeats to the Receiver Server")); return -1; case GTMSOURCE_CONN_HEARTBEAT_MAX_WAIT: gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT (8) ERR_BADCONNECTPARAM, 2, LEN_AND_LIT("maximum heartbeat wait period"), ERR_TEXT, 2, LEN_AND_LIT("Specify the maximum period (in seconds) for " "which the Source Server should wait to receive a heartbeat " "response from the Receiver Server")); return -1; } } } if (GTMSOURCE_CONN_PARMS_COUNT < connect_parms_index) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT (1) ERR_BADPARAMCOUNT); return -1; } if ((0 != gtmsource_options.connect_parms[GTMSOURCE_CONN_ALERT_PERIOD]) && (gtmsource_options.connect_parms[GTMSOURCE_CONN_ALERT_PERIOD]< gtmsource_options.connect_parms[GTMSOURCE_CONN_SOFT_TRIES_PERIOD])) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT (8) ERR_BADCONNECTPARAM, 2, LEN_AND_LIT("alert period for REPLALERT messages"), ERR_TEXT, 2, LEN_AND_LIT("Alert period cannot be less than soft connection attempts period")); return -1; } if (gtmsource_options.connect_parms[GTMSOURCE_CONN_HEARTBEAT_MAX_WAIT] < gtmsource_options.connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD]) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT (8) ERR_BADCONNECTPARAM, 2, LEN_AND_LIT("maximum heartbeat wait period"), ERR_TEXT, 2, LEN_AND_LIT("Maximum heartbeat wait period cannot be less than heartbeat interval")); return -1; } } else { gtmsource_options.connect_parms[GTMSOURCE_CONN_HARD_TRIES_COUNT] = REPL_CONN_HARD_TRIES_COUNT; gtmsource_options.connect_parms[GTMSOURCE_CONN_HARD_TRIES_PERIOD] = REPL_CONN_HARD_TRIES_PERIOD; gtmsource_options.connect_parms[GTMSOURCE_CONN_SOFT_TRIES_PERIOD] = REPL_CONN_SOFT_TRIES_PERIOD; gtmsource_options.connect_parms[GTMSOURCE_CONN_ALERT_PERIOD] = REPL_CONN_ALERT_ALERT_PERIOD; gtmsource_options.connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD] = REPL_CONN_HEARTBEAT_PERIOD; gtmsource_options.connect_parms[GTMSOURCE_CONN_HEARTBEAT_MAX_WAIT] = REPL_CONN_HEARTBEAT_MAX_WAIT; } } /* End of -START || -ACTIVATE */ if (gtmsource_options.start || gtmsource_options.statslog || gtmsource_options.changelog || gtmsource_options.activate) { log = (cli_present("LOG") == CLI_PRESENT); log_interval_specified = (CLI_PRESENT == cli_present("LOG_INTERVAL")); if (log) { log_file_len = MAX_FN_LEN + 1; if (!cli_get_str("LOG", gtmsource_options.log_file, &log_file_len)) { util_out_print("Error parsing LOG qualifier", TRUE); return(-1); } } else gtmsource_options.log_file[0] = '\0'; gtmsource_options.src_log_interval = 0; if (log_interval_specified) { if (!cli_get_num("LOG_INTERVAL", (int4 *)>msource_options.src_log_interval)) { util_out_print("Error parsing LOG_INTERVAL qualifier", TRUE); return (-1); } } if (gtmsource_options.start && 0 == gtmsource_options.src_log_interval) gtmsource_options.src_log_interval = LOGTRNUM_INTERVAL; /* For changelog/activate, interval == 0 implies don't change log interval already established */ /* We ignore interval specification for statslog, Vinaya 2005/02/07 */ } if (gtmsource_options.start) { assert(secondary || CLI_PRESENT == cli_present("PASSIVE")); gtmsource_options.mode = ((secondary) ? GTMSOURCE_MODE_ACTIVE : GTMSOURCE_MODE_PASSIVE); if (CLI_PRESENT == cli_present("BUFFSIZE")) { /* use a big conversion so we have a signed number for comparison */ if (!cli_get_int64("BUFFSIZE", &buffsize)) { util_out_print("Error parsing BUFFSIZE qualifier", TRUE); return(-1); } if (MIN_JNLPOOL_SIZE > buffsize) gtmsource_options.buffsize = MIN_JNLPOOL_SIZE; else if ((gtm_int64_t)MAX_JNLPOOL_SIZE < buffsize) gtmsource_options.buffsize = (gtm_uint64_t)MAX_JNLPOOL_SIZE; else gtmsource_options.buffsize = (gtm_uint64_t)buffsize; } else gtmsource_options.buffsize = DEFAULT_JNLPOOL_SIZE; /* Round up buffsize to the nearest (~JNL_WRT_END_MASK + 1) multiple */ gtmsource_options.buffsize = ((gtmsource_options.buffsize + ~JNL_WRT_END_MASK) & JNL_WRT_END_MASK); if (CLI_PRESENT == cli_present("FILTER")) { filter_cmd_len = MAX_FILTER_CMD_LEN; if (!cli_get_str("FILTER", gtmsource_options.filter_cmd, &filter_cmd_len)) { util_out_print("Error parsing FILTER qualifier", TRUE); return(-1); } } else gtmsource_options.filter_cmd[0] = '\0'; /* Check if compression level is specified */ if (CLI_PRESENT == cli_present("CMPLVL")) { if (!cli_get_int("CMPLVL", >msource_options.cmplvl)) { util_out_print("Error parsing CMPLVL qualifier", TRUE); return(-1); } if (GTM_CMPLVL_OUT_OF_RANGE(gtmsource_options.cmplvl)) gtmsource_options.cmplvl = ZLIB_CMPLVL_MIN; /* no compression in this case */ /* CMPLVL qualifier should override any value specified in the environment variable gtm_zlib_cmp_level */ gtm_zlib_cmp_level = gtmsource_options.cmplvl; } else gtmsource_options.cmplvl = ZLIB_CMPLVL_MIN; /* no compression in this case */ /* Check if SSL/TLS secure communication is requested. */ # ifdef GTM_TLS if (CLI_PRESENT == cli_present("TLSID")) { tlsid_len = MAX_TLSID_LEN; if (!cli_get_str("TLSID", repl_tls.id, &tlsid_len)) { util_out_print("Error parsing TLSID qualifier", TRUE); return -1; } assert(0 < tlsid_len); if (CLI_PRESENT == cli_present("RENEGOTIATE_INTERVAL")) { if (!cli_get_int("RENEGOTIATE_INTERVAL", &renegotiate_interval)) { util_out_print("Error parsing RENEGOTIATE_INTERVAL qualifier", TRUE); return -1; } if (0 > renegotiate_interval) { util_out_print("Negative values are not allowed for RENEGOTIATE_INTERVAL qualifier", TRUE); return -1; } else if ((0 < renegotiate_interval) && (renegotiate_interval < MIN_RENEGOTIATE_TIMEOUT)) renegotiate_interval = MIN_RENEGOTIATE_TIMEOUT; if (gtmsource_options.connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD] > renegotiate_interval * 60) { /* MUPIPERR when -CONNECTPARAMS having HEARTBEAT_PERIOD > renegotiate_interval */ util_out_print("RENEGOTIATE_INTERVAL [!UL] (in minutes) cannot be less than " "HEARTBEAT_PERIOD [!UL]", TRUE, renegotiate_interval, gtmsource_options.connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD]); return -1; } } else renegotiate_interval = DEFAULT_RENEGOTIATE_TIMEOUT * 60; /* Convert to seconds. */ gtmsource_options.renegotiate_interval = renegotiate_interval * 60; /* Check if plaintext-fallback mode is specified. Default option is NOPLAINTEXTFALLBACK. */ if (CLI_PRESENT == (plaintext_fallback = cli_present("PLAINTEXTFALLBACK"))) repl_tls.plaintext_fallback = (plaintext_fallback != CLI_NEGATED); else repl_tls.plaintext_fallback = FALSE; } # endif } if (gtmsource_options.shut_down) { if ((timeout_status = cli_present("TIMEOUT")) == CLI_PRESENT) { if (!cli_get_int("TIMEOUT", >msource_options.shutdown_time)) { util_out_print("Error parsing TIMEOUT qualifier", TRUE); return(-1); } if (MAX_SHUTDOWN_TIMEOUT < gtmsource_options.shutdown_time || 0 > gtmsource_options.shutdown_time) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_INVSHUTDOWN); return (-1); } } else if (CLI_NEGATED == timeout_status) gtmsource_options.shutdown_time = -1; else /* TIMEOUT not specified */ gtmsource_options.shutdown_time = DEFAULT_SHUTDOWN_TIMEOUT; } if (gtmsource_options.statslog) { statslog_val_len = SIZEOF(statslog_val) - 1; if (!cli_get_str("STATSLOG", statslog_val, &statslog_val_len)) { util_out_print("Error parsing STATSLOG qualifier", TRUE); return(-1); } statslog_val[statslog_val_len] = '\0'; cli_strupper(statslog_val); if (0 == STRCMP(statslog_val, "ON")) gtmsource_options.statslog = TRUE; else if (0 == STRCMP(statslog_val, "OFF")) gtmsource_options.statslog = FALSE; else { util_out_print("Invalid value for STATSLOG qualifier, must be either ON or OFF", TRUE); return(-1); } } if (cli_present("FREEZE") == CLI_PRESENT) { freeze_val_len = SIZEOF(freeze_val) - 1; if (cli_get_str("FREEZE", freeze_val, &freeze_val_len)) { gtmsource_options.setfreeze = TRUE; freeze_val[freeze_val_len] = '\0'; cli_strupper(freeze_val); if (0 == STRCMP(freeze_val, "ON")) gtmsource_options.freezeval = TRUE; else if (0 == STRCMP(freeze_val, "OFF")) gtmsource_options.freezeval = FALSE; else { util_out_print("Invalid value for FREEZE qualifier, must be either ON or OFF", TRUE); return -1; } if (cli_present("COMMENT") == CLI_PRESENT) { if (!gtmsource_options.freezeval) { util_out_print("Invalid qualifier combination, cannot specify FREEZE=OFF with COMMENT", TRUE); return -1; } freeze_comment_len = SIZEOF(freeze_comment); if (!cli_get_str("COMMENT", freeze_comment, &freeze_comment_len)) { util_out_print("Error parsing COMMENT qualifier", TRUE); return -1; } gtmsource_options.setcomment = TRUE; STRNCPY_STR(gtmsource_options.freeze_comment, freeze_comment, SIZEOF(gtmsource_options.freeze_comment) - 1); gtmsource_options.freeze_comment[SIZEOF(gtmsource_options.freeze_comment) - 1] = '\0'; } else if (cli_present("COMMENT") == CLI_NEGATED) { gtmsource_options.setcomment = TRUE; gtmsource_options.freeze_comment[0] = '\0'; } else if (gtmsource_options.freezeval) { util_out_print("Missing qualifier, must specify either COMMENT or NOCOMMENT with FREEZE=ON", TRUE); return -1; } } else gtmsource_options.showfreeze = TRUE; } gtmsource_options.jnlfileonly = (CLI_PRESENT == cli_present("JNLFILEONLY")); return(0); } fis-gtm-V7.0-005/sr_unix/gtmsource_heartbeat.c0000644000032200000250000002452414342376330020243 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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_string.h" #include "gtm_time.h" #include "gtm_inet.h" /* Required for gtmsource.h */ #include "gtm_stdio.h" #include #include #ifdef VMS #include /* Required for gtmsource.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_comm.h" #include "repl_dbg.h" #include "repl_log.h" #include "repl_errno.h" #include "iosp.h" #include "gt_timer.h" #include "gtmsource_heartbeat.h" #include "relqop.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF int gtmsource_sock_fd; GBLREF boolean_t gtmsource_logstats; GBLREF int gtmsource_log_fd; GBLREF FILE *gtmsource_log_fp; GBLREF gtmsource_state_t gtmsource_state; GBLDEF boolean_t heartbeat_stalled = TRUE; GBLDEF repl_heartbeat_que_entry_t *repl_heartbeat_que_head = NULL; GBLDEF repl_heartbeat_que_entry_t *repl_heartbeat_free_head = NULL; GBLDEF volatile time_t gtmsource_now; GBLDEF time_t last_sent_time, earliest_sent_time; error_def(ERR_REPLCOMM); error_def(ERR_TEXT); static int heartbeat_period, heartbeat_max_wait; void gtmsource_heartbeat_timer(TID tid, int4 interval_len, int *interval_ptr) { assert(0 != gtmsource_now); UNIX_ONLY(assert(*interval_ptr == heartbeat_period);) /* interval_len and interval_ptr are dummies on VMS */ gtmsource_now += heartbeat_period; /* cannot use *interval_ptr on VMS */ REPL_DPRINT4("Repeating heartbeat timer with %d s\tSource now is %ld\tTime now is %ld\n", heartbeat_period, gtmsource_now, time(NULL)); start_timer((TID)gtmsource_heartbeat_timer, heartbeat_period * 1000, gtmsource_heartbeat_timer, SIZEOF(heartbeat_period), &heartbeat_period); /* start_timer expects time interval in milli seconds, heartbeat_period is in seconds */ } int gtmsource_init_heartbeat(void) { int num_q_entries; repl_heartbeat_que_entry_t *heartbeat_element; assert(NULL == repl_heartbeat_que_head); heartbeat_period = jnlpool->gtmsource_local->connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD]; heartbeat_max_wait = jnlpool->gtmsource_local->connect_parms[GTMSOURCE_CONN_HEARTBEAT_MAX_WAIT]; num_q_entries = DIVIDE_ROUND_UP(heartbeat_max_wait, heartbeat_period) + 2; REPL_DPRINT4("Initialized heartbeat, heartbeat_period = %d s, heartbeat_max_wait = %d s, num_q_entries = %d\n", heartbeat_period, heartbeat_max_wait, num_q_entries); if (!(repl_heartbeat_que_head = (repl_heartbeat_que_entry_t *)malloc(num_q_entries * SIZEOF(repl_heartbeat_que_entry_t)))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Error in allocating heartbeat queue"), errno); memset(repl_heartbeat_que_head, 0, num_q_entries * SIZEOF(repl_heartbeat_que_entry_t)); repl_heartbeat_free_head = repl_heartbeat_que_head + 1; *(gtm_time4_t *)&repl_heartbeat_que_head->heartbeat.ack_time[0] = 0; *(gtm_time4_t *)&repl_heartbeat_free_head->heartbeat.ack_time[0] = 0; for (heartbeat_element = repl_heartbeat_free_head + 1, num_q_entries -= 2; num_q_entries > 0; num_q_entries--, heartbeat_element++) { insqt((que_ent_ptr_t)heartbeat_element, (que_ent_ptr_t)repl_heartbeat_free_head); } last_sent_time = gtmsource_now = time(NULL); /* Ideally, we should use the Greatest Common Factor of heartbeat_period and GTMSOURCE_LOGSTATS_INTERVAL as the time keeper * interval. As it stands now, we may not honor GTMSOURCE_LOGSTATS_INTERVAL if user specifies a heartbeat value * larger than GTMSOURCE_LOGSTATS_INTERVAL. When we make GTMSOURCE_LOGSTATS_INTERVAL a user configurable parameter, * this code may have to be revisited. Also, modify the check in gtmsource_process (prev_now != (save_now = gtmsource_now)) * to be something like (hearbeat_period < difftime((save_now = gtmsource_now), prev_now)). Vinaya 2003, Sep 08 */ start_timer((TID)gtmsource_heartbeat_timer, heartbeat_period * 1000, gtmsource_heartbeat_timer, SIZEOF(heartbeat_period), &heartbeat_period); /* start_timer expects time interval in milli seconds, heartbeat_period is in seconds */ REPL_DPRINT4("Started heartbeat timer with %d s\tSource now is %ld\tTime now is %ld\n", heartbeat_period, gtmsource_now, time(NULL)); heartbeat_stalled = FALSE; earliest_sent_time = 0; return (SS_NORMAL); } int gtmsource_stop_heartbeat(void) { REPL_DPRINT4("Stopping heartbeat timer with %d s\tSource now is %ld\tTime now is %ld\n", heartbeat_period, gtmsource_now, time(NULL)); cancel_timer((TID)gtmsource_heartbeat_timer); if (NULL != repl_heartbeat_que_head) free(repl_heartbeat_que_head); repl_heartbeat_que_head = NULL; repl_heartbeat_free_head = NULL; last_sent_time = 0; earliest_sent_time = 0; gtmsource_now = 0; heartbeat_stalled = TRUE; REPL_DPRINT2("Stopped heartbeat (%d)\n", intrpt_ok_state); return (SS_NORMAL); } boolean_t gtmsource_is_heartbeat_overdue(time_t *now, repl_heartbeat_msg_ptr_t overdue_heartbeat) { repl_heartbeat_que_entry_t *heartbeat_element; double time_elapsed; unsigned char seq_num_str[32], *seq_num_ptr; #ifndef REPL_DISABLE_HEARTBEAT if (0 == earliest_sent_time || (time_elapsed = difftime(*now, earliest_sent_time)) <= (double)heartbeat_max_wait) return (FALSE); heartbeat_element = (repl_heartbeat_que_entry_t *)remqh((que_ent_ptr_t)repl_heartbeat_que_head); if (NULL == heartbeat_element) { assert(FALSE); return (FALSE); } memcpy(overdue_heartbeat, &heartbeat_element->heartbeat, SIZEOF(repl_heartbeat_msg_t)); REPL_DPRINT5("Overdue heartbeat - SEQNO : "INT8_FMT" time : %ld now : %ld difftime : %00.f\n", INT8_PRINT(*(seq_num *)&overdue_heartbeat->ack_seqno[0]), *(gtm_time4_t *)&overdue_heartbeat->ack_time[0], *now, time_elapsed); insqt((que_ent_ptr_t)heartbeat_element, (que_ent_ptr_t)repl_heartbeat_free_head); return (TRUE); #else return (FALSE); #endif } int gtmsource_send_heartbeat(time_t *now) { repl_heartbeat_que_entry_t *heartbeat_element; unsigned char *msg_ptr; /* needed for REPL_{SEND,RECV}_LOOP */ int tosend_len, sent_len, sent_this_iter; /* needed for REPL_SEND_LOOP */ int status, poll_dir; /* needed for REPL_{SEND,RECV}_LOOP */ unsigned char seq_num_str[32], *seq_num_ptr; gtmsource_local_ptr_t gtmsource_local; heartbeat_element = (repl_heartbeat_que_entry_t *)remqh((que_ent_ptr_t)repl_heartbeat_free_head); if (NULL == heartbeat_element) /* Too many pending heartbeats, send later */ return (SS_NORMAL); QWASSIGN(*(seq_num *)&heartbeat_element->heartbeat.ack_seqno[0], jnlpool->jnlpool_ctl->jnl_seqno); *(gtm_time4_t *)&heartbeat_element->heartbeat.ack_time[0] = (gtm_time4_t)(*now); heartbeat_element->heartbeat.type = REPL_HEARTBEAT; heartbeat_element->heartbeat.len = MIN_REPL_MSGLEN; REPL_SEND_LOOP(gtmsource_sock_fd, &heartbeat_element->heartbeat, MIN_REPL_MSGLEN, REPL_POLL_NOWAIT) { gtmsource_poll_actions(FALSE); /* Recursive call */ if ((GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) || (GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) return (SS_NORMAL); } if (SS_NORMAL == status) { insqt((que_ent_ptr_t)heartbeat_element, (que_ent_ptr_t)repl_heartbeat_que_head); last_sent_time = *now; if (0 == earliest_sent_time) earliest_sent_time = last_sent_time; REPL_DPRINT4("HEARTBEAT sent with time %ld SEQNO "INT8_FMT" at %ld\n", *(gtm_time4_t *)&heartbeat_element->heartbeat.ack_time[0], INT8_PRINT(*(seq_num *)&heartbeat_element->heartbeat.ack_seqno[0]), time(NULL)); return (SS_NORMAL); } if (EREPL_SEND == repl_errno && REPL_CONN_RESET(status)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Connection reset while attempting to send heartbeat. Status = %d ; %s\n", status, STRERROR(status)); repl_close(>msource_sock_fd); gtmsource_state = jnlpool->gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; return (SS_NORMAL); } if (EREPL_SEND == repl_errno) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Error sending HEARTBEAT message. Error in send"), status); if (EREPL_SELECT == repl_errno) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Error sending HEARTBEAT message. Error in select"), status); assertpro((SS_NORMAL == status)); return -1; /* This will never get executed, added to make compiler happy */ } int gtmsource_process_heartbeat(repl_heartbeat_msg_ptr_t heartbeat_msg) { repl_heartbeat_que_entry_t *heartbeat_element; seq_num ack_seqno; gd_region *reg, *region_top; sgmnt_addrs *csa; unsigned char seq_num_str[32], *seq_num_ptr; gtmsource_local_ptr_t gtmsource_local; if (NULL != jnlpool->gtmsource_local) /* For use with -SHOWBACKLOG and -ZEROBACKLOG*/ gtmsource_local = jnlpool->gtmsource_local; else gtmsource_local = &jnlpool->gtmsource_local_array[0]; QWASSIGN(ack_seqno, *(seq_num *)&heartbeat_msg->ack_seqno[0]); gtmsource_local->heartbeat_jnl_seqno = ack_seqno; gtmsource_local->hrtbt_recvd = TRUE; REPL_DPRINT4("HEARTBEAT received with time %ld SEQNO "INT8_FMT" at %ld\n", *(gtm_time4_t *)&heartbeat_msg->ack_time[0], INT8_PRINT(ack_seqno), time(NULL)); for (heartbeat_element = (repl_heartbeat_que_entry_t *)remqh((que_ent_ptr_t)repl_heartbeat_que_head); NULL != heartbeat_element&& *(gtm_time4_t *)&heartbeat_msg->ack_time[0] >= (gtm_time4_t)earliest_sent_time; heartbeat_element = (repl_heartbeat_que_entry_t *)remqh((que_ent_ptr_t)repl_heartbeat_que_head)) { insqt((que_ent_ptr_t)heartbeat_element, (que_ent_ptr_t)repl_heartbeat_free_head); earliest_sent_time = (time_t) *(gtm_time4_t *)&((repl_heartbeat_que_entry_t *) ((unsigned char *)repl_heartbeat_que_head + repl_heartbeat_que_head->que.fl))->heartbeat.ack_time[0]; } if (NULL != heartbeat_element) insqh((que_ent_ptr_t)heartbeat_element, (que_ent_ptr_t)repl_heartbeat_que_head); return (SS_NORMAL); } fis-gtm-V7.0-005/sr_unix/gtmsource_inline.h0000644000032200000250000001135514342376330017565 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2017-2018 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 "jnl.h" #include "memcoherency.h" #include "gtmsource.h" static inline void jpl_phase2_write_complete(struct jnlpool_addrs_struct *jnlpool) { int index; jpl_phase2_in_prog_t *phs2cmt; jnldata_hdr_ptr_t jnl_header; jrec_prefix *prefix; uint4 tot_jrec_len; GBLREF uint4 process_id; GBLREF jnl_gbls_t jgbl; GBLREF void repl_phase2_cleanup(jnlpool_addrs *jpa); DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; tot_jrec_len = jnlpool->jrs.tot_jrec_len; assert(tot_jrec_len); index = jnlpool->jrs.phase2_commit_index; ASSERT_JNL_PHASE2_COMMIT_INDEX_IS_VALID(index, JPL_PHASE2_COMMIT_ARRAY_SIZE); phs2cmt = &jnlpool->jnlpool_ctl->phase2_commit_array[index]; assert(phs2cmt->process_id == process_id); assert(FALSE == phs2cmt->write_complete); assert(phs2cmt->tot_jrec_len == tot_jrec_len); assert(jgbl.cumul_index == jgbl.cu_jnl_index); if (!jnlpool->jrs.memcpy_skipped) { assert(jnlpool->jrs.start_write_addr >= jnlpool->jnlpool_ctl->write_addr); assert(jnlpool->jrs.start_write_addr < jnlpool->jnlpool_ctl->rsrv_write_addr); jnl_header = (jnldata_hdr_ptr_t)(jnlpool->jnldata_base + (jnlpool->jrs.start_write_addr % jnlpool->jnlpool_ctl->jnlpool_size)); jnl_header->jnldata_len = tot_jrec_len; assert(0 == (phs2cmt->prev_jrec_len % JNL_REC_START_BNDRY)); jnl_header->prev_jnldata_len = phs2cmt->prev_jrec_len; DEBUG_ONLY(prefix = (jrec_prefix *)(jnlpool->jnldata_base + (jnlpool->jrs.start_write_addr + SIZEOF(jnldata_hdr_struct)) % jnlpool->jnlpool_ctl->jnlpool_size)); assert(JRT_BAD != prefix->jrec_type); if ((jnlpool->jrs.write_total != tot_jrec_len) DEBUG_ONLY(|| ((0 != TREF(gtm_test_jnlpool_sync)) && (0 == (phs2cmt->jnl_seqno % TREF(gtm_test_jnlpool_sync)))))) { /* This is an out-of-sync situation. "tot_jrec_len" (computed in phase1) is not equal * to "write_total" (computed in phase2). Not sure how this can happen but recover * from this situation by replacing the first record in the reserved space with a * JRT_BAD rectype. That way the source server knows this is a transaction that it * has to read from the jnlfiles and not the jnlpool. */ assert((0 != TREF(gtm_test_jnlpool_sync)) && (0 == (phs2cmt->jnl_seqno % TREF(gtm_test_jnlpool_sync)))); assert(tot_jrec_len >= (SIZEOF(jnldata_hdr_struct) + SIZEOF(jrec_prefix))); /* Note that it is possible jnl_header is 8 bytes shy of the jnlpool end in which case * "prefix" below would end up going outside the jnlpool range hence a simple * (jnl_header + 1) would not work to set prefix (and instead the % needed below). */ prefix = (jrec_prefix *)(jnlpool->jnldata_base + (jnlpool->jrs.start_write_addr + SIZEOF(jnldata_hdr_struct)) % jnlpool->jnlpool_ctl->jnlpool_size); prefix->jrec_type = JRT_BAD; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_JNLPOOLRECOVERY, 4, tot_jrec_len, jnlpool->jrs.write_total, &phs2cmt->jnl_seqno, jnlpool->jnlpool_ctl->jnlpool_id.instfilename); /* Now that JRT_BAD is set, fix cur_write_addr so it is set back in sync * (so later assert can succeed). */ DEBUG_ONLY(jnlpool->jrs.cur_write_addr = (jnlpool->jrs.start_write_addr + tot_jrec_len)); } /* Need to make sure the writes of jnl_header->jnldata_len & jnl_header->prev_jnldata_len * happen BEFORE the write of phs2cmt->write_complete in that order. Hence need the write * memory barrier. Not doing this could cause another process in "repl_phase2_cleanup" to * see phs2cmt->write_complete as TRUE and update jnlpool_ctl->write_addr to reflect this * particular seqno even though the jnl_header write has not still happened. This could cause * a concurrently running source server to decide to read this seqno in "gtmsource_readpool" * and read garbage lengths in the jnl_header section. */ SHM_WRITE_MEMORY_BARRIER; } assert((jnlpool->jrs.start_write_addr + tot_jrec_len) == jnlpool->jrs.cur_write_addr); phs2cmt->write_complete = TRUE; jnlpool->jrs.tot_jrec_len = 0; /* reset needed to prevent duplicate calls (e.g. "secshr_db_clnup") */ /* Invoke "repl_phase2_cleanup" sparingly as it calls "grab_latch". So we do it twice. * Once at half-way mark and once when a wrap occurs. */ if (!index || ((JPL_PHASE2_COMMIT_ARRAY_SIZE / 2) == index)) repl_phase2_cleanup(jnlpool); } fis-gtm-V7.0-005/sr_unix/gtmsource_jnl_release_timer.c0000755000032200000250000000251614342376330021767 0ustar librarygtc/**************************************************************** * * * Copyright 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 "gt_timer.h" #include "replgbl.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtm_threadgbl.h" void gtmsource_jnl_release_timer(TID tid, int4 interval_len, int *interval_ptr) { gtmsource_ctl_close(); } int gtmsource_start_jnl_release_timer(void) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* start_timer expects > 0 time interval in milli seconds, idle_timeout is in seconds */ if ((TREF(replgbl)).jnl_release_timeout) start_timer((TID)gtmsource_jnl_release_timer, (TREF(replgbl)).jnl_release_timeout * MILLISECS_IN_SEC, gtmsource_jnl_release_timer, 0, NULL); return (SS_NORMAL); } int gtmsource_stop_jnl_release_timer(void) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TREF(replgbl)).jnl_release_timeout) cancel_timer((TID)gtmsource_jnl_release_timer); return (SS_NORMAL); } fis-gtm-V7.0-005/sr_unix/gtmsource_jnlpool.c0000644000032200000250000000564114342376330017760 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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" #if !defined(__MVS__) && !defined(VMS) #include #endif #include #include "gtm_inet.h" #include #include "gtm_string.h" #ifdef VMS #include /* Required for gtmsource.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_dbg.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "util.h" #include "repl_inst_dump.h" #include "cli.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; GBLREF boolean_t detail_specified; /* set to TRUE if -DETAIL is specified */ GBLREF uint4 section_offset; /* Used by PRINT_OFFSET_PREFIX macro in repl_inst_dump.c */ error_def(ERR_MUPCLIERR); int gtmsource_jnlpool(void) { uint4 offset, size; gtm_uint64_t value; boolean_t value_present; assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); assert((NULL != jnlpool) && (NULL == jnlpool->gtmsource_local)); if (CLI_PRESENT == cli_present("NAME")) { util_out_print("Error: NAME cannot be used with JNLPOOL", TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MUPCLIERR); } if (CLI_PRESENT == cli_present("SHOW")) { detail_specified = (CLI_PRESENT == cli_present("DETAIL")); section_offset = 0; repl_inst_dump_jnlpoolctl(jnlpool->jnlpool_ctl); section_offset = (uint4)((sm_uc_ptr_t)jnlpool->repl_inst_filehdr - (sm_uc_ptr_t)jnlpool->jnlpool_ctl); repl_inst_dump_filehdr(jnlpool->repl_inst_filehdr); section_offset = (uint4)((sm_uc_ptr_t)jnlpool->gtmsrc_lcl_array - (sm_uc_ptr_t)jnlpool->jnlpool_ctl); repl_inst_dump_gtmsrclcl(jnlpool->gtmsrc_lcl_array); section_offset = (uint4)((sm_uc_ptr_t)jnlpool->gtmsource_local_array - (sm_uc_ptr_t)jnlpool->jnlpool_ctl); repl_inst_dump_gtmsourcelocal(jnlpool->gtmsource_local_array); } if (CLI_PRESENT == cli_present("CHANGE")) { mupcli_get_offset_size_value(&offset, &size, &value, &value_present); if (size > jnlpool->jnlpool_ctl->jnlpool_size) { util_out_print("Error: SIZE specified [0x!XL] is greater than size of journal pool [0x!XL]", TRUE, size, jnlpool->jnlpool_ctl->jnlpool_size); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MUPCLIERR); } mupcli_edit_offset_size_value(&((sm_uc_ptr_t)jnlpool->jnlpool_ctl)[offset], offset, size, value, value_present); } return (NORMAL_SHUTDOWN); } fis-gtm-V7.0-005/sr_unix/gtmsource_losttncomplete.c0000755000032200000250000000602614342376330021360 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2017 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_string.h" #include "gtm_stdio.h" #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_instance.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "util.h" #include "cli.h" #include "repl_log.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; int gtmsource_losttncomplete(void) { int idx; gtmsource_local_ptr_t gtmsourcelocal_ptr; sgmnt_addrs *repl_csa; uint4 exit_status; assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); assert((NULL != jnlpool) && (NULL == jnlpool->gtmsource_local)); repl_log(stderr, TRUE, TRUE, "Initiating LOSTTNCOMPLETE operation on instance [%s]\n", jnlpool->repl_inst_filehdr->inst_info.this_instname); /* If this is a root primary instance, propagate this information to secondaries as well so they reset zqgblmod_seqno to 0. * If propagating primary, no need to send this to tertiaries as the receiver on the tertiary cannot have started with * non-zero "zqgblmod_seqno" to begin with (PRIMARYNOTROOT error would have been issued). */ if (!jnlpool->jnlpool_ctl->upd_disabled) { DEBUG_ONLY(repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs;) assert(!repl_csa->hold_onto_crit); /* so it is ok to invoke "grab_lock" and "rel_lock" unconditionally */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); jnlpool->jnlpool_ctl->send_losttn_complete = TRUE; gtmsourcelocal_ptr = jnlpool->gtmsource_local_array; for (idx = 0; idx < NUM_GTMSRC_LCL; idx++, gtmsourcelocal_ptr++) { if (('\0' == gtmsourcelocal_ptr->secondary_instname[0]) && (0 == gtmsourcelocal_ptr->read_jnl_seqno) && (0 == gtmsourcelocal_ptr->connect_jnl_seqno)) continue; gtmsourcelocal_ptr->send_losttn_complete = TRUE; } rel_lock(jnlpool->jnlpool_dummy_reg); } /* Reset zqgblmod_seqno and zqgblmod_tn to 0 in this instance as well */ exit_status = repl_inst_reset_zqgblmod_seqno_and_tn(); if (SS_NORMAL != exit_status) { /* Only reason we know of the above function returning -1 is in case of an online rollback. But, that's not * possible because we hold the access control semaphore at this point which online rollback needs at startup. * Also in case of gds_rundown failure the function returns EXIT_ERR which results in an assert failure here. */ assert(FALSE); } return (exit_status + NORMAL_SHUTDOWN); } fis-gtm-V7.0-005/sr_unix/gtmsource_mode_change.c0000755000032200000250000001342514342376330020536 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2017 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_time.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_inet.h" #include "gtm_string.h" #include "gtmio.h" #include "repl_sp.h" #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_dbg.h" #include "gtm_stdio.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "repl_log.h" #include "repl_instance.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF gtmsource_options_t gtmsource_options; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; error_def(ERR_REPLLOGOPN); int gtmsource_mode_change(int to_mode) { uint4 savepid; int exit_status; int status, detach_status, remove_status; int log_fd = 0, close_status = 0; char* err_code; int save_errno; sgmnt_addrs *repl_csa; assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); repl_log(stdout, TRUE, TRUE, "Initiating %s operation on source server pid [%d] for secondary instance [%s]\n", (GTMSOURCE_MODE_ACTIVE_REQUESTED == to_mode) ? "ACTIVATE" : "DEACTIVATE", jnlpool->gtmsource_local->gtmsource_pid, jnlpool->gtmsource_local->secondary_instname); if ((jnlpool->gtmsource_local->mode == GTMSOURCE_MODE_ACTIVE_REQUESTED) || (jnlpool->gtmsource_local->mode == GTMSOURCE_MODE_PASSIVE_REQUESTED)) { repl_log(stderr, FALSE, TRUE, "Source Server %s already requested, not changing mode\n", (to_mode == GTMSOURCE_MODE_ACTIVE_REQUESTED) ? "ACTIVATE" : "DEACTIVATE"); return (ABNORMAL_SHUTDOWN); } if (((GTMSOURCE_MODE_ACTIVE == jnlpool->gtmsource_local->mode) && (GTMSOURCE_MODE_ACTIVE_REQUESTED == to_mode)) || ((GTMSOURCE_MODE_PASSIVE == jnlpool->gtmsource_local->mode) && (GTMSOURCE_MODE_PASSIVE_REQUESTED == to_mode))) { repl_log(stderr, FALSE, TRUE, "Source Server already %s, not changing mode\n", (to_mode == GTMSOURCE_MODE_ACTIVE_REQUESTED) ? "ACTIVE" : "PASSIVE"); return (ABNORMAL_SHUTDOWN); } assert(ROOTPRIMARY_UNSPECIFIED != gtmsource_options.rootprimary); /*check if the new log file is writable*/ if ('\0' != gtmsource_options.log_file[0] && 0 != STRCMP(jnlpool->gtmsource_local->log_file, gtmsource_options.log_file)) { OPENFILE3_CLOEXEC(gtmsource_options.log_file, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, log_fd); if (log_fd < 0) { save_errno = ERRNO; err_code = STRERROR(save_errno); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_REPLLOGOPN, 6, LEN_AND_STR(gtmsource_options.log_file), LEN_AND_STR(err_code), LEN_AND_STR(NULL_DEVICE)); return (ABNORMAL_SHUTDOWN); } CLOSEFILE_IF_OPEN(log_fd, close_status); assert(close_status==0); } if ((GTMSOURCE_MODE_ACTIVE_REQUESTED == to_mode) && (ROOTPRIMARY_SPECIFIED == gtmsource_options.rootprimary) && jnlpool->jnlpool_ctl->upd_disabled) { /* ACTIVATE is specified with ROOTPRIMARY on a journal pool that was created with PROPAGATEPRIMARY. This is a * case of transition from propagating primary to root primary. Enable updates in this journal pool and append * a histinfo record to the replication instance file. The function "gtmsource_rootprimary_init" does just that. */ gtmsource_rootprimary_init(jnlpool->jnlpool_ctl->jnl_seqno); } DEBUG_ONLY(repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs;) assert(!repl_csa->hold_onto_crit); /* so it is ok to invoke "grab_lock" and "rel_lock" unconditionally */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); /* Any ACTIVATE/DEACTIVATE versus ROOTPRIMARY/PROPAGATE incompatibilities have already been checked in the * function "jnlpool_init" so go ahead and document the impending activation/deactivation and return. * This flag will be eventually detected by the concurrently running source server which will then change mode. */ if (GTMSOURCE_MODE_ACTIVE_REQUESTED == to_mode) { jnlpool->gtmsource_local->secondary_port = gtmsource_options.secondary_port; STRCPY(jnlpool->gtmsource_local->secondary_host, gtmsource_options.secondary_host); jnlpool->gtmsource_local->secondary_port = gtmsource_options.secondary_port; memcpy(&jnlpool->gtmsource_local->connect_parms[0], >msource_options.connect_parms[0], SIZEOF(gtmsource_options.connect_parms)); } if ('\0' != gtmsource_options.log_file[0] && 0 != STRCMP(jnlpool->gtmsource_local->log_file, gtmsource_options.log_file)) { repl_log(stdout, FALSE, TRUE, "Signaling change in log file from %s to %s\n", jnlpool->gtmsource_local->log_file, gtmsource_options.log_file); STRCPY(jnlpool->gtmsource_local->log_file, gtmsource_options.log_file); jnlpool->gtmsource_local->changelog |= REPLIC_CHANGE_LOGFILE; } if (0 != gtmsource_options.src_log_interval && jnlpool->gtmsource_local->log_interval != gtmsource_options.src_log_interval) { repl_log(stdout, FALSE, TRUE, "Signaling change in log interval from %u to %u\n", jnlpool->gtmsource_local->log_interval, gtmsource_options.src_log_interval); jnlpool->gtmsource_local->log_interval = gtmsource_options.src_log_interval; jnlpool->gtmsource_local->changelog |= REPLIC_CHANGE_LOGINTERVAL; } jnlpool->gtmsource_local->mode = to_mode; rel_lock(jnlpool->jnlpool_dummy_reg); REPL_DPRINT1("Change mode signalled\n"); return (NORMAL_SHUTDOWN); } fis-gtm-V7.0-005/sr_unix/gtmsource_needrestart.c0000755000032200000250000000460014342376330020620 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2017 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_string.h" #include "gtm_stdio.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "util.h" #include "cli.h" #include "repl_log.h" #include "repl_instance.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; GBLREF gtmsource_options_t gtmsource_options; error_def(ERR_MUPCLIERR); int gtmsource_needrestart(void) { gtmsource_local_ptr_t gtmsource_local; sgmnt_addrs *repl_csa; assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); gtmsource_local = jnlpool->gtmsource_local; if (NULL != gtmsource_local) { assert(!STRCMP(gtmsource_options.secondary_instname, gtmsource_local->secondary_instname)); repl_log(stderr, TRUE, TRUE, "Initiating NEEDRESTART operation on source server pid [%d] for secondary instance [%s]\n", gtmsource_local->gtmsource_pid, gtmsource_options.secondary_instname); } else repl_log(stderr, TRUE, TRUE, "Initiating NEEDRESTART operation for secondary instance [%s]\n", gtmsource_options.secondary_instname); DEBUG_ONLY(repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs;) assert(!repl_csa->hold_onto_crit); /* so it is ok to invoke "grab_lock" and "rel_lock" unconditionally */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); if ((NULL != gtmsource_local) && (gtmsource_local->connect_jnl_seqno >= jnlpool->jnlpool_ctl->start_jnl_seqno)) util_out_print("Secondary Instance [!AZ] DOES NOT NEED to be restarted", TRUE, gtmsource_local->secondary_instname); else util_out_print("Secondary Instance [!AZ] NEEDS to be restarted first", TRUE, gtmsource_options.secondary_instname); rel_lock(jnlpool->jnlpool_dummy_reg); return (NORMAL_SHUTDOWN); } fis-gtm-V7.0-005/sr_unix/gtmsource_process.c0000644000032200000250000026202714342376330017764 0ustar librarygtc /*************************************************************** * * * Copyright (c) 2006-2022 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. * * * ****************************************************************/ #if defined(__MVS__) && !defined(_ISOC99_SOURCE) #define _ISOC99_SOURCE #endif #include "mdef.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_socket.h" #include "gtm_netdb.h" #include "gtm_inet.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_time.h" #include "gtm_stat.h" #include "gtm_signal.h" #include #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "sgtm_putmsg.h" #include "repl_comm.h" #include "jnl.h" #include "hashtab_mname.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "buddy_list.h" #include "muprec.h" #include "repl_ctl.h" #include "repl_errno.h" #include "repl_dbg.h" #include "repl_shutdcode.h" #include "iosp.h" #include "gt_timer.h" #include "gtmsource_heartbeat.h" #include "repl_filter.h" #include "repl_log.h" #include "min_max.h" #include "copy.h" #include "ftok_sems.h" #include "repl_instance.h" #include "gtmmsg.h" #include "repl_sem.h" #include "have_crit.h" /* needed for ZLIB_COMPRESS */ #include "deferred_signal_handler.h" /* needed for ZLIB_COMPRESS */ #include "gtm_zlib.h" #include "repl_sort_tr_buff.h" #include "replgbl.h" #include "gtmsource_srv_latch.h" #include "gv_trigger_common.h" #include "wbox_test_init.h" #ifdef GTM_TLS #include "gtm_repl.h" #endif #define MAX_HEXDUMP_CHARS_PER_LINE 26 /* 2 characters per byte + space, 80 column assumed */ #define BREAK_IF_CMP_ERROR(CMPRET, SEND_TR_LEN) \ { \ switch(CMPRET) \ { \ case Z_MEM_ERROR: \ repl_log(gtmsource_log_fp, TRUE, FALSE, "Out-of-memory error from compress function " \ "while compressing %d bytes\n", SEND_TR_LEN); \ assert(FALSE); \ break; \ case Z_BUF_ERROR: \ repl_log(gtmsource_log_fp, TRUE, FALSE, "Insufficient output buffer error from compress function " \ "while compressing %d bytes\n", SEND_TR_LEN); \ assert(FALSE); \ break; \ case Z_STREAM_ERROR: \ repl_log(gtmsource_log_fp, TRUE, FALSE, "Compression level %d invalid error from compress function " \ "while compressing %d bytes\n", repl_zlib_cmp_level, SEND_TR_LEN); \ assert(FALSE); \ break; \ } \ } #define SET_8BYTE_CMP_MSGHDR(SEND_MSGP, SEND_TR_LEN, CMPBUFLEN, MSGHDRLEN) \ { \ SEND_MSGP->type = (SEND_TR_LEN << REPL_TR_CMP_MSG_TYPE_BITS) | REPL_TR_CMP_JNL_RECS; \ SEND_MSGP->len = (int4)cmpbuflen + msghdrlen; \ /* Note that a compressed message need not be 8-byte aligned even though the input message was. So round it up to \ * the nearest align boundary. The actual message will contain the unaligned length which is what the receiver will \ * receive. But the # of bytes transmitted across will be the aligned length. \ */ \ SEND_TR_LEN = ROUND_UP(SEND_MSGP->len, REPL_MSG_ALIGN); \ } #define SET_16BYTE_CMP_MSGHDR(SEND_MSGP, SEND_TR_LEN, CMPBUFLEN, MSGHDRLEN) \ { \ repl_cmpmsg_ptr_t send_cmpmsgp; \ \ send_cmpmsgp = (repl_cmpmsg_ptr_t)SEND_MSGP; \ assert(&send_cmpmsgp->type == &SEND_MSGP->type); \ assert(&send_cmpmsgp->len == &SEND_MSGP->len); \ send_cmpmsgp->type = REPL_TR_CMP_JNL_RECS2; \ /* Note that a compressed message need not be 8-byte aligned even though the input message was. So round it up to \ * the nearest align boundary. The actual message will contain the unaligned length which is what the receiver will \ * receive. But the # of bytes transmitted across will be the aligned length. \ */ \ send_cmpmsgp->len = (int4)(ROUND_UP(CMPBUFLEN + MSGHDRLEN, REPL_MSG_ALIGN)); \ send_cmpmsgp->uncmplen = SEND_TR_LEN; \ send_cmpmsgp->cmplen = (int4)CMPBUFLEN; \ SEND_TR_LEN = SEND_MSGP->len; \ } #ifdef GTM_TRIGGER #define ISSUE_TRIG2NOTRIG_IF_NEEDED \ { \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ if (!(TREF(replgbl)).trig_replic_warning_issued && (TREF(replgbl)).trig_replic_suspect_seqno \ && !remote_side->trigger_supported) \ { /* Note: The below repl_log text is copied from TRIG2NOTRIG error message content from merrors.msg. Change \ * to one should be reflected in another \ */ \ repl_log(gtmsource_log_fp, TRUE, TRUE, "Warning: Sending transaction sequence number %d which used " \ "triggers to a replicator that does not support triggers\n", (TREF(replgbl)).trig_replic_suspect_seqno);\ (TREF(replgbl)).trig_replic_warning_issued = TRUE; /* No more warnings till restart */ \ (TREF(replgbl)).trig_replic_suspect_seqno = seq_num_zero; \ } \ } #endif #ifdef GTM_TLS #define REPLTLS_RENEGOTIATE(SOCK, STATUS) \ { \ char *errp; \ int save_errno; \ \ if (0 != (STATUS = gtm_tls_renegotiate(SOCK))) \ { \ assert(-1 == STATUS); \ save_errno = gtm_tls_errno(); \ if (REPL_CONN_RESET(save_errno)) \ { \ repl_log(gtmsource_log_fp, TRUE, TRUE, "Connection reset while attempting to renegotiate" \ " TLS/SSL connection.\n"); \ gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; \ repl_close(>msource_sock_fd); \ } else \ { \ errp = (-1 == save_errno) ? (char *)gtm_tls_get_error(NULL) : STRERROR(save_errno); \ assert(FALSE); \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_TLSRENEGOTIATE, 0, ERR_TEXT, 2, LEN_AND_STR(errp)); \ } \ } else \ gtmsource_local->num_renegotiations++; \ } #endif #define PHASE2_COMMIT_WAIT_CNT 8 /* 8 iterations of each 1 msec sleep before we decide to invoke "repl_phase2_cleanup" */ GBLDEF repl_msg_ptr_t gtmsource_msgp = NULL; GBLDEF int gtmsource_msgbufsiz = 0; GBLDEF repl_msg_ptr_t gtmsource_cmpmsgp = NULL; GBLDEF int gtmsource_cmpmsgbufsiz = 0; GBLDEF boolean_t gtmsource_received_cmp2uncmp_msg; GBLDEF qw_num repl_source_data_sent = 0; GBLDEF qw_num repl_source_msg_sent = 0; GBLDEF qw_num repl_source_cmp_sent = 0; GBLDEF qw_num repl_source_lastlog_data_sent = 0; GBLDEF qw_num repl_source_lastlog_msg_sent = 0; GBLDEF time_t repl_source_prev_log_time; GBLDEF time_t repl_source_this_log_time; GBLDEF time_t gtmsource_last_flush_time; GBLREF gtmsource_state_t gtmsource_state; GBLREF uchar_ptr_t repl_filter_buff; GBLREF int repl_filter_bufsiz; GBLREF volatile time_t gtmsource_now; GBLREF int gtmsource_sock_fd; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF gd_region *gv_cur_region; GBLREF repl_ctl_element *repl_ctl_list; GBLREF gtmsource_options_t gtmsource_options; GBLREF int gtmsource_log_fd; GBLREF FILE *gtmsource_log_fp; GBLREF boolean_t gtmsource_logstats; GBLREF int gtmsource_filter; GBLREF gd_addr *gd_header; GBLREF seq_num seq_num_zero, seq_num_minus_one, seq_num_one; GBLREF unsigned int jnl_source_datalen, jnl_dest_maxdatalen; GBLREF unsigned char jnl_source_rectype, jnl_dest_maxrectype; GBLREF int repl_max_send_buffsize, repl_max_recv_buffsize; GBLREF seq_num lastlog_seqno; GBLREF uint4 log_interval; GBLREF qw_num trans_sent_cnt, last_log_tr_sent_cnt; GBLREF repl_conn_info_t *this_side, *remote_side; GBLREF int4 strm_index; GBLREF uint4 process_id; GBLREF seq_num gtmsource_save_read_jnl_seqno; STATICDEF boolean_t xon_wait_logged = FALSE; STATICDEF boolean_t already_communicated = FALSE; STATICDEF seq_num recvd_seqno = 0; STATICDEF int recvd_start_flags = START_FLAG_NONE; STATICDEF int poll_time = REPL_POLL_NOWAIT; #ifdef GTM_TLS STATICDEF boolean_t next_renegotiate_hrtbt = FALSE; STATICDEF int4 hrtbt_cnt = 0; STATICDEF int4 renegotiate_factor = 0; #ifdef DEBUG STATICDEF boolean_t renegotiation_pending = FALSE; #endif #endif #define OUT_LINE 256 + 1 #define PROC_SRCOPS_PRINT_MSG_LEN 2048 error_def(ERR_JNLNEWREC); error_def(ERR_JNLSETDATA2LONG); error_def(ERR_REPLCOMM); error_def(ERR_REPLFTOKSEM); error_def(ERR_REPLINSTNOHIST); error_def(ERR_REPLNOTLS); error_def(ERR_REPLXENDIANFAIL); error_def(ERR_REPLAHEAD); error_def(ERR_TRIG2NOTRIG); error_def(ERR_TLSIOERROR); error_def(ERR_TLSRENEGOTIATE); error_def(ERR_TEXT); /* Endian converts the given set of journal records (possibly multiple sequence numbers) so that the secondary can consume them * as-is. This is done only in the case when the primary is running on a GT.M version less than the GT.M version on secondary * side. Otherwise, the secondary takes the responsibility of doing the endian conversion. Note that the endian conversion happens * in-place. The below function is based on gtmrecv_process.c/repl_tr_endian_convert() */ static void repl_tr_endian_convert(repl_msg_ptr_t send_msgp, int send_tr_len, seq_num pre_read_seqno) { uchar_ptr_t buffp, jb; DEBUG_ONLY(uchar_ptr_t jstart;) int buflen, remaining_len, jlen, reclen, status, nodeflags_keylen, temp_val, keylen; jnl_record *rec; enum jnl_record_type rectype; jrec_suffix *suffixp; jnl_string *keystr; mstr_len_t *vallen_ptr; /* seq_num good_seqno; */ buffp = send_msgp->msg; buflen = send_msgp->len - REPL_MSG_HDRLEN; remaining_len = send_tr_len; /* QWASSIGN(good_seqno, seq_num_zero); */ status = 0; while (0 < remaining_len) { jlen = buflen; jb = buffp; while (JREC_PREFIX_SIZE <= jlen) { DEBUG_ONLY(jstart = jb); rec = (jnl_record *)(jb); /* endian convert the prefix fields. Not all of the prefix fields are used by the secondary. Only rectype * and forwptr are needed. */ rectype = (enum jnl_record_type)rec->prefix.jrec_type; reclen = rec->prefix.forwptr; rec->prefix.forwptr = GTM_BYTESWAP_24(reclen); if (!IS_REPLICATED(rectype) || (0 == reclen) || (reclen > jlen) || (reclen > MAX_LOGI_JNL_REC_SIZE)) { assert(FALSE); status = -1; break; } assert(!IS_ZTP(rectype)); assert(IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(rectype) || (JRT_TCOM == rectype) || (JRT_NULL == rectype)); /* endian convert the suffix fields. Only backptr needs endian conversion as the other field - suffix_code * is 8 bit. */ suffixp = ((jrec_suffix *)((unsigned char *)rec + reclen - JREC_SUFFIX_SIZE)); suffixp->backptr = GTM_BYTESWAP_24(suffixp->backptr); /* QWASSIGN(good_seqno, rec->jrec_null.jnl_seqno); */ /* update good_seqno */ rec->jrec_null.jnl_seqno = GTM_BYTESWAP_64(rec->jrec_null.jnl_seqno); /* At this point, we could have a TCOM or NULL or SET/KILL/ZKILL/ZTRIG type of record. * Assert that all of them have "strm_seqno" at the exact same offset so we can avoid * an if/then/else check on the record types in order to endian convert "strm_seqno". */ assert(&rec->jrec_null.strm_seqno == &rec->jrec_set_kill.strm_seqno); assert(&rec->jrec_null.strm_seqno == &rec->jrec_tcom.strm_seqno); rec->jrec_null.strm_seqno = GTM_BYTESWAP_64(rec->jrec_null.strm_seqno); if (IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(rectype)) { keystr = (jnl_string *)&rec->jrec_set_kill.mumps_node; assert(keystr == (jnl_string *)&rec->jrec_ztworm.ztworm_str); assert(keystr == (jnl_string *)&rec->jrec_lgtrig.lgtrig_str); assert(&rec->jrec_set_kill.update_num == &rec->jrec_ztworm.update_num); assert(&rec->jrec_set_kill.update_num == &rec->jrec_lgtrig.update_num); rec->jrec_set_kill.update_num = GTM_BYTESWAP_32(rec->jrec_set_kill.update_num); /* From V19 onwards, the 'length' field is divided into 8 bit 'nodeflags' and 24 bit 'length' * fields. */ keylen = keystr->length; nodeflags_keylen = *(jnl_str_len_t *)keystr; *(jnl_str_len_t *)keystr = GTM_BYTESWAP_32(nodeflags_keylen); if (IS_SET(rectype) || IS_ZTWORM(rectype) || IS_LGTRIG(rectype)) { /* SET/ZTWORM/LGTRIG records have a 'key/value' part whose length needs endian conversion */ vallen_ptr = (mstr_len_t *)&keystr->text[keylen]; GET_MSTR_LEN(temp_val, vallen_ptr); temp_val = GTM_BYTESWAP_32(temp_val); PUT_MSTR_LEN(vallen_ptr, temp_val); /* The 'key/value' itself is a character array and hence needs no endian conversion */ } } else if (JRT_TCOM == rectype) { assert((unsigned char *)&rec->jrec_tcom.token_seq + SIZEOF(token_seq_t) == (unsigned char *)&rec->jrec_tcom.filler_short); /* endian convert num_participants */ rec->jrec_tcom.num_participants = GTM_BYTESWAP_16(rec->jrec_tcom.num_participants); } /* else records can only be JRT_NULL. The only relevant field in JRT_NULL is the sequence number which is * already endian converted. */ assert(jstart == jb); /* endian conversion should always happen in-place. */ jlen -= reclen; jb += reclen; } if ((-1 == status) || (0 != jlen)) { assert(FALSE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_REPLXENDIANFAIL, 3, LEN_AND_LIT("Originating"), &pre_read_seqno); } /* move on to the next transaction */ remaining_len -= (buflen + REPL_MSG_HDRLEN); buffp += buflen; assert((REPL_TR_JNL_RECS == ((repl_msg_ptr_t)(buffp))->type) || (0 == remaining_len)); buflen = ((repl_msg_ptr_t)(buffp))->len - REPL_MSG_HDRLEN; buffp += REPL_MSG_HDRLEN; } if (0 != remaining_len) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_REPLXENDIANFAIL, 3, LEN_AND_LIT("Originating"), &pre_read_seqno); assert(FALSE); } } /* Returns TRUE if the state changed to a transitional state while handling control messages */ boolean_t gtmsource_recv_ctl_nowait(void) { gtmsource_state_t gtmsource_state_sav; int poll_time_sav; GTMSOURCE_SAVE_STATE(gtmsource_state_sav); poll_time_sav = poll_time; poll_time = REPL_POLL_NOWAIT; gtmsource_recv_ctl(); /* If we changed state, keep the poll_time associated with the new state. * However, TLS messaging changes poll_time without changing state, so restore poll_time to match the prior state. */ if (!GTMSOURCE_CHANGED_STATE(gtmsource_state_sav)) poll_time = poll_time_sav; return (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)); } void gtmsource_recv_ctl(void) { gtmsource_local_ptr_t gtmsource_local; repl_msg_t renegotiate_msg; repl_msg_t xoff_ack; repl_heartbeat_msg_ptr_t heartbeat_msg; repl_msg_t recv_msg, *recv_msgp; /* gtmsource_msgp may be in use; use this instead */ unsigned char *msg_ptr; /* needed for REPL_{SEND,RECV}_LOOP */ int torecv_len, recvd_len, recvd_this_iter; /* needed for REPL_RECV_LOOP */ int status, poll_dir; /* needed for REPL_{SEND,RECV}_LOOP */ boolean_t msg_is_cross_endian; seq_num tmp_seqno; gtm_time4_t tmp_time4; int index; char err_string[1024]; /* Check if receiver sent us any control message. Typically, the traffic from receiver to source is very * low compared to traffic in the other direction. More often than not, there will be nothing on the pipe * to receive. Ideally, we should let TCP notify us when there is data on the pipe (async I/O on Unix and * VMS). But, we are not there yet. Since we do a select() before a recv(), we won't block if there is * nothing in the pipe. So, it shouldn't be an expensive operation even if done before every send. Also, * in doing so, we react to an XOFF sooner than later. */ /* Make sure we don't sleep for an extended period of time if there is something to be sent across */ assert((GTMSOURCE_SENDING_JNLRECS != gtmsource_state) || ((0 == poll_time) || (GTMSOURCE_IDLE_POLL_WAIT == poll_time)) GTMTLS_ONLY(DEBUG_ONLY(|| renegotiation_pending))); if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return; if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) return; # ifdef GTM_TLS if (repl_tls.enabled && (REPLTLS_WAITING_FOR_RENEG_TIMEOUT == repl_tls.renegotiate_state) && next_renegotiate_hrtbt) { /* Time to renegotiate the TLS/SSL parameters. */ heartbeat_stalled = TRUE; /* Defer heartbeats until renegotiation is done. */ DEBUG_ONLY(renegotiation_pending = TRUE); /* Send REPL_RENEG_ACK_ME message to the receiver. */ renegotiate_msg.type = REPL_RENEG_ACK_ME; renegotiate_msg.len = MIN_REPL_MSGLEN; gtmsource_repl_send((repl_msg_ptr_t)&renegotiate_msg, "REPL_RENEG_ACK_ME", MAX_SEQNO, INVALID_SUPPL_STRM); if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return; if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) return; /* We now have to wait for REPL_RENEG_ACK from the receiver. Until then we defer sending journal * records to the other side. This way, we don't end up having outbound data in the TCP/IP pipe * during the time of renegotiation. TLS/SSL protocol doesn't handle application data when it is * in the middle of renegotiation. Similarly, the receiver on receipt of the REPL_RENEG_ACK_ME * message will defer sending any more messages to us until the renegotiation is completed. */ repl_tls.renegotiate_state = REPLTLS_WAITING_FOR_RENEG_ACK; repl_log(gtmsource_log_fp, TRUE, TRUE, "Waiting for REPL_RENEG_ACK\n"); poll_time = REPL_POLL_WAIT; /* because we are waiting for a REPL_RENEG_ACK */ } # endif recv_msgp = &recv_msg; REPL_RECV_LOOP(gtmsource_sock_fd, recv_msgp, MIN_REPL_MSGLEN, poll_time) { if (0 == recvd_len) /* nothing received in the first attempt, let's try again later */ break; gtmsource_poll_actions(TRUE); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) { poll_time = REPL_POLL_NOWAIT; return; } else if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) break; } gtmsource_local = jnlpool->gtmsource_local; if ((SS_NORMAL == status) && (0 != recvd_len)) { /* Process the received control message */ assert(MIN_REPL_MSGLEN == recvd_len); REPL_DPRINT3("gtmsource_process: %d bytes received, type is %d\n", recvd_len, recv_msgp->type); /* One is not always guaranteed the received message is in source native endian format. * See endianness related comments in gtmsource_recv_restart for why. So be safe and handle * it just like how gtmsource_recv_restart does. The below check works as all messages we * expect at this point have a fixed length of MIN_REPL_MSGLEN. */ msg_is_cross_endian = (((unsigned)MIN_REPL_MSGLEN < (unsigned)recv_msgp->len) && ((unsigned)MIN_REPL_MSGLEN == GTM_BYTESWAP_32((unsigned)recv_msgp->len))); if (msg_is_cross_endian) { recv_msgp->type = GTM_BYTESWAP_32(recv_msgp->type); recv_msgp->len = GTM_BYTESWAP_32(recv_msgp->len); } assert(MIN_REPL_MSGLEN == recv_msgp->len); assert(remote_side->endianness_known); switch(recv_msgp->type) { case REPL_XOFF: case REPL_XOFF_ACK_ME: gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_XON; poll_time = REPL_POLL_WAIT; /* because we are waiting for a REPL_XON */ repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL_XOFF/REPL_XOFF_ACK_ME received. Send stalled...\n"); xon_wait_logged = FALSE; if (REPL_XOFF_ACK_ME == recv_msgp->type) { xoff_ack.type = REPL_XOFF_ACK; tmp_seqno = *(seq_num *)&recv_msgp->msg[0]; if (msg_is_cross_endian) tmp_seqno = GTM_BYTESWAP_64(tmp_seqno); *(seq_num *)&xoff_ack.msg[0] = tmp_seqno; xoff_ack.len = MIN_REPL_MSGLEN; gtmsource_repl_send((repl_msg_ptr_t)&xoff_ack, "REPL_XOFF_ACK", MAX_SEQNO, INVALID_SUPPL_STRM); if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return; /* "gtmsource_repl_send" did not complete */ if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) break; /* "gtmsource_repl_send" did not complete */ /* REPL_XOFF_ACK_ME is always followed by either a REPL_START_JNL_SEQNO, * REPL_CMP2UNCMP or REPL_BADTRANS. We don't want to be doing TLS/SSL * renegotiation in the middle of these messages as the logic on the * receiver side is complicated enough to include TLS/SSL renegotiation. * In all three cases, we go break out of this loop and redo the replication * handshake. So, set the state to skip renegotiation in the mean time. */ # ifdef GTM_TLS if (repl_tls.enabled) repl_tls.renegotiate_state = REPLTLS_SKIP_RENEGOTIATION; # endif } break; case REPL_XON: gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_SENDING_JNLRECS; poll_time = REPL_POLL_NOWAIT; /* because we received XON and data ready for send */ repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL_XON received\n"); GTMTLS_ONLY(if (REPLTLS_WAITING_FOR_RENEG_ACK != repl_tls.renegotiate_state)) heartbeat_stalled = FALSE; REPL_DPRINT1("Restarting HEARTBEAT\n"); break; case REPL_BADTRANS: case REPL_CMP2UNCMP: case REPL_START_JNL_SEQNO: /* A REPL_XOFF_ACK_ME must have been sent before. Ensure by asserting that we are * waiting for an XON. */ assert(GTMSOURCE_WAITING_FOR_XON == gtmsource_state); QWASSIGN(recvd_seqno, *(seq_num *)&recv_msgp->msg[0]); if (msg_is_cross_endian) recvd_seqno = GTM_BYTESWAP_64(recvd_seqno); gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_SEARCHING_FOR_RESTART; if ((REPL_BADTRANS == recv_msgp->type) || (REPL_CMP2UNCMP == recv_msgp->type)) { already_communicated = TRUE; recvd_start_flags = START_FLAG_NONE; if (REPL_BADTRANS == recv_msgp->type) repl_log(gtmsource_log_fp, TRUE, TRUE, "Received REPL_BADTRANS " "message with SEQNO "INT8_FMT" "INT8_FMTX"\n", recvd_seqno, recvd_seqno); else { repl_log(gtmsource_log_fp, TRUE, TRUE, "Received REPL_CMP2UNCMP " "message with SEQNO "INT8_FMT" "INT8_FMTX"\n", recvd_seqno, recvd_seqno); repl_log(gtmsource_log_fp, TRUE, FALSE, "Defaulting to NO compression for this connection\n"); gtmsource_received_cmp2uncmp_msg = TRUE; } } else { recvd_start_flags = ((repl_start_msg_ptr_t)recv_msgp)->start_flags; if (msg_is_cross_endian) recvd_start_flags = GTM_BYTESWAP_32(recvd_start_flags); already_communicated = FALSE; repl_log(gtmsource_log_fp, TRUE, TRUE, "Received REPL_START_JNL_SEQNO message with SEQNO "INT8_FMT" " INT8_FMTX". Possible crash of recvr/update process\n", recvd_seqno, recvd_seqno); } break; case REPL_HEARTBEAT: if (msg_is_cross_endian) { heartbeat_msg = (repl_heartbeat_msg_ptr_t)recv_msgp; tmp_seqno = *(seq_num *)&heartbeat_msg->ack_seqno[0]; tmp_seqno = GTM_BYTESWAP_64(tmp_seqno); *(seq_num *)&heartbeat_msg->ack_seqno[0] = tmp_seqno; tmp_time4 = *(gtm_time4_t *)&heartbeat_msg->ack_time[0]; tmp_time4 = GTM_BYTESWAP_32(tmp_time4); *(gtm_time4_t *)&heartbeat_msg->ack_time[0] = tmp_time4; } if ((!gtmsource_is_heartbeat_stalled) && (SHUTDOWN != gtmsource_local->shutdown)) gtmsource_process_heartbeat((repl_heartbeat_msg_ptr_t)recv_msgp); # ifdef GTM_TLS hrtbt_cnt++; /* Check whether it is time for renegotiation */ if ((0 < renegotiate_factor) && (0 == (hrtbt_cnt % renegotiate_factor)) && (SHUTDOWN != gtmsource_local->shutdown)) { switch(repl_tls.renegotiate_state) { case REPLTLS_RENEG_STATE_NONE: case REPLTLS_WAITING_FOR_RENEG_TIMEOUT: next_renegotiate_hrtbt = TRUE; repl_tls.renegotiate_state = REPLTLS_WAITING_FOR_RENEG_TIMEOUT; poll_time = REPL_POLL_WAIT; break; case REPLTLS_WAITING_FOR_RENEG_ACK: /* On slower systems, heartbeat responses may arrive late. In such a case, defer renegotiation */ break; default: assert(FALSE); break; } } # endif break; # ifdef GTM_TLS case REPL_RENEG_ACK: repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL_RENEG_ACK received\n"); REPLTLS_RENEGOTIATE(repl_tls.sock, status); poll_time = REPL_POLL_NOWAIT; /* because we are back to sending data */ if (0 != status) { assert(GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state); break; } /* Send the REPL_RENEG_COMPLETE message. */ repl_log(gtmsource_log_fp, TRUE, TRUE, "Sending REPL_RENEG_COMPLETE\n"); renegotiate_msg.type = REPL_RENEG_COMPLETE; renegotiate_msg.len = MIN_REPL_MSGLEN; gtmsource_repl_send((repl_msg_ptr_t)&renegotiate_msg, "REPL_RENEG_COMPLETE", MAX_SEQNO, INVALID_SUPPL_STRM); if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return; if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) break; repl_log(gtmsource_log_fp, TRUE, TRUE, "Sent REPL_RENEG_COMPLETE message." " TLS/SSL connection successfully renegotiated.\n"); assert(heartbeat_stalled); if (GTMSOURCE_WAITING_FOR_XON != gtmsource_state) heartbeat_stalled = FALSE; /* else, heartbeat_stalled will be set back to FALSE when REPL_XON is received. */ DEBUG_ONLY(renegotiation_pending = FALSE); repl_tls.renegotiate_state = REPLTLS_RENEG_STATE_NONE; next_renegotiate_hrtbt = FALSE; hrtbt_cnt = 0; repl_log_tls_info(gtmsource_log_fp, repl_tls.sock); break; # endif default: repl_log(gtmsource_log_fp, TRUE, TRUE, "Message of unknown type %d of length %d " "bytes received; hex dump follows\n", recv_msgp->type, recvd_len); for (index = 0; index < MIN(recvd_len, gtmsource_msgbufsiz - REPL_MSG_HDRLEN); ) { repl_log(gtmsource_log_fp, FALSE, FALSE, "%.2x ", recv_msgp->msg[index]); if ((++index) % MAX_HEXDUMP_CHARS_PER_LINE == 0) repl_log(gtmsource_log_fp, FALSE, TRUE, "\n"); } repl_log(gtmsource_log_fp, FALSE, TRUE, "\n"); /* flush BEFORE the assert */ assert(FALSE); break; } # ifdef GTM_TLS /* On receipt of a REPL_XOFF_ACK_ME, we should no longer wait-for/attempt TLS/SSL * renegotiation. */ assert((REPL_XOFF_ACK_ME != recv_msgp->type) || !repl_tls.enabled || (REPLTLS_SKIP_RENEGOTIATION == repl_tls.renegotiate_state)); # endif } else if (SS_NORMAL != status) { if (EREPL_RECV == repl_errno) { if (REPL_CONN_RESET(status)) { /* Connection reset */ repl_log(gtmsource_log_fp, TRUE, TRUE, "Connection reset while attempting to receive from secondary." " Status = %d ; %s\n", status, STRERROR(status)); repl_log_conn_info(gtmsource_sock_fd, gtmsource_log_fp, TRUE); SEND_SYSMSG_REPLCOMM(LEN_AND_LIT("Error receiving Control message from Receiver. Error in recv.")); } repl_close(>msource_sock_fd); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; return; } else if (EREPL_SELECT == repl_errno) { SNPRINTF(err_string, SIZEOF(err_string), "Error receiving Control message from Receiver. Error in select : %s", STRERROR(status)); SEND_SYSMSG_REPLCOMM(LEN_AND_STR(err_string)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_STR(err_string)); } } } /* The work-horse of the Source Server */ int gtmsource_process(void) { gtmsource_local_ptr_t gtmsource_local; jnlpool_ctl_ptr_t jctl; seq_num sav_read_jnl_seqno; seq_num recvd_jnl_seqno, tmp_read_jnl_seqno; int data_len, srch_status; unsigned char *msg_ptr; /* needed for REPL_{SEND,RECV}_LOOP */ int tosend_len, sent_len, sent_this_iter; /* needed for REPL_SEND_LOOP */ int status, poll_dir; /* needed for REPL_{SEND,RECV}_LOOP */ int tot_tr_len, send_tr_len, remaining_len, pre_cmpmsglen; int recvd_msg_type; uchar_ptr_t in_buff, out_buff, out_buffmsg; uint4 in_buflen, out_buflen, out_bufsiz; seq_num log_seqno, diff_seqno, pre_read_seqno, post_read_seqno, jnl_seqno; char err_string[1024]; double time_elapsed; seq_num resync_seqno, zqgblmod_seqno, filter_seqno; gd_region *reg, *region_top; sgmnt_addrs *csa, *repl_csa; qw_num delta_sent_cnt, delta_data_sent, delta_msg_sent; time_t prev_now; int index; uint4 temp_ulong; unix_db_info *udi; repl_histinfo remote_histinfo, local_histinfo; int4 num_histinfo, max_epoch_interval; seq_num local_jnl_seqno, tmp_seqno; repl_msg_t instnohist_msg, losttncomplete_msg; repl_msg_ptr_t send_msgp; repl_cmpmsg_ptr_t send_cmpmsgp; repl_start_reply_msg_ptr_t reply_msgp; boolean_t rollback_first, secondary_ahead, secondary_was_rootprimary; boolean_t intfilter_error, skip_last_histinfo_check, msg_is_cross_endian, retval; int semval, cmpret; uLongf cmpbuflen; int4 msghdrlen; Bytef *cmpbufptr; char histdetail[OUT_LINE]; sm_global_latch_ptr_t gtmsource_srv_latch; gtmsource_state_t gtmsource_state_sav; uint4 phase2_commit_index1; int phase2_commit_wait_cnt; boolean_t close_retry = FALSE; DEBUG_ONLY(uchar_ptr_t save_inbuff;) DEBUG_ONLY(uchar_ptr_t save_outbuff;) DCL_THREADGBL_ACCESS; char print_msg_src[PROC_SRCOPS_PRINT_MSG_LEN]; char print_msg_t[PROC_SRCOPS_PRINT_MSG_LEN]; SETUP_THREADGBL_ACCESS; assert((NULL != jnlpool) && (NULL != jnlpool->jnlpool_dummy_reg) && jnlpool->jnlpool_dummy_reg->open); # ifdef DEBUG repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; assert(!repl_csa->hold_onto_crit); /* so we can do unconditional grab_lock/rel_lock */ ASSERT_VALID_JNLPOOL(repl_csa); # endif assert(REPL_MSG_HDRLEN == SIZEOF(jnldata_hdr_struct)); /* necessary for reading multiple transactions from jnlpool in * a single attempt */ jctl = jnlpool->jnlpool_ctl; gtmsource_local = jnlpool->gtmsource_local; gtmsource_msgp = NULL; gtmsource_msgbufsiz = MAX_REPL_MSGLEN; if (ZLIB_CMPLVL_NONE != gtm_zlib_cmp_level) gtmsource_cmpmsgp = NULL; assert(REPL_POLL_WAIT < MILLISECS_IN_SEC); assert(GTMSOURCE_IDLE_POLL_WAIT < REPL_POLL_WAIT); if (0 < gtmsource_options.renegotiate_interval) { /* When heartbeat period is a multiple of renegotiate_interval, perform renegotiation every renegotiate_interval/heartbeat_period. */ renegotiate_factor = (int) DIVIDE_ROUND_DOWN(gtmsource_options.renegotiate_interval, gtmsource_local->connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD]); /* When heartbeat period is about as high as the renegotiate interval (1 == renegotiate_factor), perform renegotiation every other heartbeat.*/ if (1 == renegotiate_factor) renegotiate_factor++; } gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; gtmsource_srv_latch = >msource_local->gtmsource_srv_latch; /* Below is a simplistic representation of the state diagram of a source server. * * ------------------------------ * GTMSOURCE_START * ------------------------------ * | * | (startup state) * v * ------------------------------ * GTMSOURCE_WAITING_FOR_CONNECTION * ------------------------------ * | * | (gtmsource_est_conn) * v * ------------------------------ * GTMSOURCE_WAITING_FOR_RESTART * ------------------------------ * | * | (gtmsource_recv_restart) * v * ------------------------------ * GTMSOURCE_SEARCHING_FOR_RESTART * ------------------------------ * | * | (gtmsource_srch_restart) * v * ------------------------------ * GTMSOURCE_SENDING_JNLRECS <---------\ * ------------------------------ | * | | * | (receive REPL_XOFF) | * | (receive REPL_XOFF_ACK_ME) | * v | * ------------------------------ ^ * GTMSOURCE_WAITING_FOR_XON | * ------------------------------ | * | | * v (receive REPL_XON) | * | | * \--------------------->---------/ */ udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); /* Before entering the loop find the max EPOCH interval (for use in lock waits) */ max_epoch_interval = 0; for (reg = gd_header->regions, region_top = gd_header->regions + gd_header->n_regions; reg < region_top; reg++) { csa = &FILE_INFO(reg)->s_addrs; if ((max_epoch_interval < csa->hdr->epoch_interval) && (MAX_EPOCH_INTERVAL >= csa->hdr->epoch_interval)) max_epoch_interval = csa->hdr->epoch_interval; } /* Since we want to wait at least a couple of minutes before timing out on the latch, ensure max_epoch_interval * is at least 1 minute even if the individual file header epoch intervals are not that big. */ max_epoch_interval = MAX(60, max_epoch_interval); while (TRUE) { assert(!udi->grabbed_ftok_sem); gtmsource_stop_heartbeat(); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; /* Ensure we don't hold any locks at this moment */ assert(process_id != gtmsource_local->gtmsource_srv_latch.u.parts.latch_pid); assert(!have_crit(CRIT_HAVE_ANY_REG)); /* checks both journal pool lock and database crit lock */ assert(FD_INVALID != gtmsource_sock_fd); if (FD_INVALID != gtmsource_sock_fd) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Closing connection due to ONLINE ROLLBACK\n"); repl_close(>msource_sock_fd); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); } jnl_seqno = jnlpool->jnlpool_ctl->jnl_seqno; repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL INFO - Current Jnlpool Seqno : "INT8_FMT"\n", jnl_seqno); repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL INFO - Last Seqno sent : "INT8_FMT"\n", gtmsource_local->read_jnl_seqno - 1); /* gtmsource_save_read_jnl_seqno is kept uptodate with gtmsource_local->read_addr and gtmsource_local->read * fields in gtmsource_onln_rlbk_clnup. But, gtmsource_local->read_jnl_seqno is still pointing to the last * sequence number that we sent to the receiver (which could have been rolled back now). We don't want to * continue with a stale value of read_jnl_seqno. So, set it to gtmsource_save_read_jnl_seqno which itself * is taken from the jnlpool_ctl->jnl_seqno right when we detected the online rollback. We could have set * this right when we set gtmsource_save_read_jnl_seqno but we don't do that because we want to print the * old value in the log file but we can't use repl_log/gtmsource_log_fp in gtmsource_onln_rlbk_clnup() as * it is bundled up as part of libgtmshr.so whereas repl_log is bundled in libmupip.a. */ gtmsource_local->read_jnl_seqno = gtmsource_save_read_jnl_seqno; repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL INFO - Source Server Read Seqno is now set to : "INT8_FMT"\n", gtmsource_local->read_jnl_seqno); gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; assert(READ_FILE == gtmsource_local->read_state); gtmsource_ctl_close(); /* can't rely on the journal files anymore since rollback could have touched them */ } if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) { gtmsource_start_jnl_release_timer(); gtmsource_est_conn(); gtmsource_stop_jnl_release_timer(); if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return (SS_NORMAL); repl_source_data_sent = repl_source_msg_sent = repl_source_cmp_sent = 0; repl_source_lastlog_data_sent = 0; repl_source_lastlog_msg_sent = 0; gtmsource_alloc_msgbuff(MAX_REPL_MSGLEN, TRUE); gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_RESTART; recvd_start_flags = START_FLAG_NONE; repl_source_prev_log_time = time(NULL); } if (GTMSOURCE_WAITING_FOR_RESTART == gtmsource_state && SS_NORMAL != (status = gtmsource_recv_restart(&recvd_seqno, &recvd_msg_type, &recvd_start_flags))) { if (EREPL_RECV == repl_errno) { if (REPL_CONN_RESET(status)) { /* Connection reset */ repl_log(gtmsource_log_fp, TRUE, TRUE, "Connection reset while receiving restart SEQNO. Status = %d ; %s\n", status, STRERROR(status)); repl_close(>msource_sock_fd); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; continue; } else { SNPRINTF(err_string, SIZEOF(err_string), "Error receiving RESTART SEQNO. Error in recv : %s", STRERROR(status)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_STR(err_string)); } } else if (EREPL_SEND == repl_errno) { if (REPL_CONN_RESET(status)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Connection reset while sending XOFF_ACK due to possible update process shutdown. " "Status = %d ; %s\n", status, STRERROR(status)); repl_log_conn_info(gtmsource_sock_fd, gtmsource_log_fp, TRUE); close_retry = TRUE; } if (close_retry) { repl_close(>msource_sock_fd); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; close_retry = FALSE; continue; } else { SNPRINTF(err_string, SIZEOF(err_string), "Error sending XOFF_ACK_ME message. Error in send : %s\n", STRERROR(status)); SEND_SYSMSG_REPLCOMM(LEN_AND_STR(err_string)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_STR(err_string)); } } else if (EREPL_SELECT == repl_errno) { SNPRINTF(err_string, SIZEOF(err_string), "Error receiving RESTART SEQNO/sending XOFF_ACK_ME. " "Error in select : %s", STRERROR(status)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_STR(err_string)); } } if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return (SS_NORMAL); /* Connection might have been closed if "gtmsource_recv_restart" got an unexpected message. In that case * re-establish the same by continuing to the beginning of this loop. */ if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) continue; assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver); assert((GTMSOURCE_SEARCHING_FOR_RESTART == gtmsource_state) || (GTMSOURCE_WAITING_FOR_RESTART == gtmsource_state)); /* Receiver runs on a version of GT.M that supports multi-site capability */ /* If gtmsource_state == GTMSOURCE_SEARCHING_FOR_RESTART, we have already communicated with the * receiver and hence checked the instance info so no need to do it again. */ if (GTMSOURCE_WAITING_FOR_RESTART == gtmsource_state) { /* Get replication instance info */ DEBUG_ONLY(secondary_was_rootprimary = -1;) /* Note: As part of the REPL_INSTINFO message, the receiver could be sending a non-zero "strm_jnl_seqno" * in some cases. If so, it will override "recvd_seqno" we saw before in the REPL_START_JNL_SEQNO message. */ if (!gtmsource_get_instance_info(&secondary_was_rootprimary, &recvd_seqno)) { if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return (SS_NORMAL); else if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) continue; else { /* Got a REPL_XOFF_ACK_ME from the receiver. Restart the initial handshake */ assert(GTMSOURCE_WAITING_FOR_RESTART == gtmsource_state); continue; } } assert((FALSE == secondary_was_rootprimary) || (TRUE == secondary_was_rootprimary)); } rollback_first = FALSE; secondary_ahead = FALSE; grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, HANDLE_CONCUR_ONLINE_ROLLBACK); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; continue; } local_jnl_seqno = jctl->jnl_seqno; rel_lock(jnlpool->jnlpool_dummy_reg); /* Take care to set the flush parameter in repl_log calls below to FALSE until at least the first message * gets sent back. This is so the fetchresync rollback on the other side does not timeout before receiving * a response. */ poll_time = REPL_POLL_NOWAIT; assert(0 == GET_STRM_INDEX(recvd_seqno)); assert(0 == GET_STRM_INDEX(local_jnl_seqno)); if (recvd_seqno > local_jnl_seqno) { /* Secondary journal seqno is greater than that of the Primary. We know it is ahead of the primary. */ secondary_ahead = TRUE; REPL_DPRINT5("Secondary instance journal seqno "INT8_FMT" "INT8_FMTX" is greater than Primary " "instance journal seqno "INT8_FMT" "INT8_FMTX"", recvd_seqno, recvd_seqno, local_jnl_seqno, local_jnl_seqno); sgtm_putmsg(print_msg_src, PROC_SRCOPS_PRINT_MSG_LEN, VARLSTCNT(4) ERR_REPLAHEAD, 2, LEN_AND_LIT("")); repl_log(gtmsource_log_fp, TRUE, TRUE, print_msg_src); SNPRINTF(print_msg_t, SIZEOF(print_msg_t), "Replicating instance : "INT8_FMT", Originating instance : " ""INT8_FMT". ", recvd_seqno, local_jnl_seqno); sgtm_putmsg(print_msg_src, PROC_SRCOPS_PRINT_MSG_LEN, VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_STR(print_msg_t)); repl_log(gtmsource_log_fp, TRUE, TRUE, print_msg_src); /* Since the secondary is at least multi-site, the determination of the rollback seqno involves comparing * the histinfo records between the primary and secondary starting down from "local_jnl_seqno-1" * (done below). In either case, the secondary has to roll back to at most "local_jnl_seqno". * Reset "recvd_seqno" to this number given that we have already recorded that the secondary is * ahead of the primary. */ recvd_seqno = local_jnl_seqno; }else { repl_log(gtmsource_log_fp, TRUE, FALSE, "Current Journal Seqno of the instance is "INT8_FMT" "INT8_FMTX"\n", local_jnl_seqno, local_jnl_seqno); } /* Before setting "next_histinfo_seqno", check if we have at least one histinfo record in the replication instance * file. The only case when there can be no histinfo records is if this instance is a propagating primary. Assert * that. In this case, wait for this instance's primary to send the first histinfo record before setting the * next_histinfo_seqno. Note that we are fetching the value of "num_histinfo" without holding a lock on the instance * file but that is ok since all we care about is if it is 0 or not. We do not rely on the actual value. */ num_histinfo = jnlpool->repl_inst_filehdr->num_histinfo; assert(0 <= num_histinfo); assert(num_histinfo || jctl->upd_disabled); gtmsource_local->next_histinfo_num = -1;/* Initial value. Reset by the call to "gtmsource_set_next_histinfo_seqno" * invoked in turn by "gtmsource_send_new_histrec" down below */ if (jctl->upd_disabled && !num_histinfo) { /* Wait for corresponding primary to send a new histinfo record and the receiver server on this instance * to write that to the replication instance file. */ assert(-1 == gtmsource_local->next_histinfo_num); repl_log(gtmsource_log_fp, TRUE, TRUE, "Source server waiting for first history record to be written by " "update process\n"); do { SHORT_SLEEP(GTMSOURCE_WAIT_FOR_FIRSTHISTINFO); gtmsource_poll_actions(FALSE); if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return (SS_NORMAL); else if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; break; /* Break this loop */ } num_histinfo = jnlpool->repl_inst_filehdr->num_histinfo; if (num_histinfo) /* Number of histinfos is non-zero */ break; } while (TRUE); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; continue; /* Restart the outer loop */ } repl_log(gtmsource_log_fp, TRUE, TRUE, "First history record written by update process. Source server proceeding.\n"); } /* Now get the latest histinfo record from the secondary. There are a few exceptions though. * 1) If we came here because of a BAD_TRANS or CMP2UNCMP message from the receiver server. * In this case, we have already been communicating with the receiver so no need to * compare the histinfo record between primary and secondary. * 2) If receiver server was started with -UPDATERESYNC and receiver is running pre-V55000. * In this case there is no history record on the receiver side to compare against. * In case the receiver is post-V55000, the -UPDATERESYNC would have required an instance * file name as the value which would be used towards history record verification. * 3) If receiver server was started with -UPDATERESYNC and receiver is >= V55000 and at a seqno * which is EQUAL to the earliest seqno for which we have a history record on the primary. * We have no way of verifying histories since we definitely don't have the history record * for the receiver side seqno. Since -updateresync was used, assume they are in sync and * start replicating from the earliest seqno for which we have a history record on the primary. * 4) If recvd_seqno is 1. In this case, the receiver instance has been created afresh so its instance * file is empty and we are guaranteed there is nothing to compare. So no point requesting it. * Besides, this is a very common situation in practice that requiring -updateresync in this * case seems user-unfriendly so we will let this one go by without a -updateresync particularly * because there is no harm that can happen by allowing two such instances to connect/replicate. */ assert(0 != recvd_seqno); if (1 == recvd_seqno) skip_last_histinfo_check = TRUE; else { if ((GTMSOURCE_WAITING_FOR_RESTART != gtmsource_state) && already_communicated) skip_last_histinfo_check = TRUE; else if (START_FLAG_UPDATERESYNC & recvd_start_flags) { repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL_START_JNL_SEQNO message has " "START_FLAG_UPDATERESYNC bit set\n"); if (REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver) skip_last_histinfo_check = TRUE; else { assert(jnlpool->repl_inst_filehdr->num_histinfo); /* should be at least 1 history record */ /* If -updateresync is specified and receiver instance seqno is exactly equal to the * start_seqno of the earliest history record in the instance file, then skip last * histinfo check. Note that in case both source and receiver instances are supplementary, * we should be looking at the 0th stream only. Even in that case, we are guaranteed that * the 0th history record in the instance file corresponds to the 0th stream. So it is * safe to look at the start_seqno of just the 0th history record in all cases. */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, HANDLE_CONCUR_ONLINE_ROLLBACK); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; continue; } status = repl_inst_histinfo_get(0, &local_histinfo); assert(0 == status); /* Since we pass histinfo_num of 0 which is >=0 and < num_histinfo */ rel_lock(jnlpool->jnlpool_dummy_reg); if (local_histinfo.start_seqno == recvd_seqno) skip_last_histinfo_check = TRUE; else skip_last_histinfo_check = FALSE; } } else skip_last_histinfo_check = FALSE; } if (!skip_last_histinfo_check) { /* Find histinfo record in the local instance file corresponding to seqno "recvd_seqno-1" */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, HANDLE_CONCUR_ONLINE_ROLLBACK); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; continue; } assert(recvd_seqno <= local_jnl_seqno); assert(recvd_seqno <= jctl->jnl_seqno); assert((INVALID_SUPPL_STRM == strm_index) || (0 == strm_index)); status = repl_inst_histinfo_find_seqno(recvd_seqno, strm_index, &local_histinfo); rel_lock(jnlpool->jnlpool_dummy_reg); assert((0 != status) || (local_histinfo.start_seqno < recvd_seqno)); if (0 != status) { /* If recvd_seqno is the earliest history record's start_seqno and -udpateresync was * specified, assume the two instances are in sync. Otherwise issue error and close connection. * Send this error status to the receiver server before closing the connection. * This way the receiver will know to shut down rather than loop back trying to * reconnect. This avoids an infinite loop of connection open and closes * between the source server and receiver server. */ assert(ERR_REPLINSTNOHIST == status); /* only error returned by "repl_inst_histinfo_find_seqno" */ assert((INVALID_HISTINFO_NUM == local_histinfo.histinfo_num) || (local_histinfo.start_seqno >= recvd_seqno)); if (!(START_FLAG_UPDATERESYNC & recvd_start_flags) || (INVALID_HISTINFO_NUM == local_histinfo.histinfo_num) || (local_histinfo.start_seqno > recvd_seqno)) { /* recvd_seqno is PRIOR to the starting seqno of the instance file. * In that case, issue error and close the connection. */ SNPRINTF(histdetail, OUT_LINE, "seqno "INT8_FMT" "INT8_FMTX, recvd_seqno - 1, recvd_seqno - 1); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_REPLINSTNOHIST, 4, LEN_AND_STR(histdetail), LEN_AND_STR(udi->fn)); instnohist_msg.type = REPL_INST_NOHIST; instnohist_msg.len = MIN_REPL_MSGLEN; memset(&instnohist_msg.msg[0], 0, SIZEOF(instnohist_msg.msg)); gtmsource_repl_send((repl_msg_ptr_t)&instnohist_msg, "REPL_INST_NOHIST", MAX_SEQNO, INVALID_SUPPL_STRM); repl_log(gtmsource_log_fp, TRUE, TRUE, "Connection reset due to above REPLINSTNOHIST error\n"); repl_close(>msource_sock_fd); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; continue; } assert((0 == local_histinfo.histinfo_num) && (local_histinfo.start_seqno == recvd_seqno)); } if (local_histinfo.start_seqno < recvd_seqno) { /* Find histinfo record in the remote instance file corresponding to seqno "recvd_seqno-1" */ retval = gtmsource_get_remote_histinfo(recvd_seqno, &remote_histinfo); if (retval) { assert(remote_histinfo.start_seqno < recvd_seqno); /* Check if primary and secondary have same histinfo for "recvd_seqno-1" */ rollback_first = !gtmsource_is_histinfo_identical(&remote_histinfo, &local_histinfo, recvd_seqno, OK_TO_LOG_TRUE); /* If local and remote sides are supplementary (i.e. P->Q replication), verify each * stream level history as well. Do this only if the remote side is a receiver server * (i.e. not rollback) and if we still intend on sending a REPL_WILL_RESTART_WITH_INFO * message. */ assert(this_side->is_supplementary == jnlpool->repl_inst_filehdr->is_supplementary); if (this_side->is_supplementary && remote_side->is_supplementary && (REPL_START_JNL_SEQNO == recvd_msg_type) && !rollback_first && !secondary_ahead) retval = gtmsource_check_remote_strm_histinfo(recvd_seqno, &rollback_first); } if (!retval) { if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return (SS_NORMAL); else if ((GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) { poll_time = REPL_POLL_NOWAIT; continue; } else { /* Got a REPL_XOFF_ACK_ME from receiver. Restart the initial handshake */ assert(GTMSOURCE_WAITING_FOR_RESTART == gtmsource_state); continue; } } } } QWASSIGN(sav_read_jnl_seqno, gtmsource_local->read_jnl_seqno); reply_msgp = (repl_start_reply_msg_ptr_t)gtmsource_msgp; memset(reply_msgp, 0, SIZEOF(*reply_msgp)); /* to identify older releases in the future */ reply_msgp->len = MIN_REPL_MSGLEN; reply_msgp->proto_ver = REPL_PROTO_VER_THIS; reply_msgp->node_endianness = NODE_ENDIANNESS; reply_msgp->is_supplementary = jnlpool->repl_inst_filehdr->is_supplementary; assert((1 != recvd_seqno) || !rollback_first); if ((GTMSOURCE_SEARCHING_FOR_RESTART == gtmsource_state) || (REPL_START_JNL_SEQNO == recvd_msg_type)) { gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_SEARCHING_FOR_RESTART; /* If the last histinfo record in both instances are NOT the same ("rollback_first" is TRUE) * (possible only if the secondary is multi-site), or if secondary is ahead of the primary * ("secondary_ahead" is TRUE) we do want the secondary to rollback first. Issue message to * do rollback fetchresync. There is one exception though. And that is if -NORESYNC was * specified on the receiver side. In this case, determine the resync/common point by comparing * local and remote histinfo records from the tail of the instance file until we reach * one seqno whose histinfo information is identical in both. * Use this as the common point to send a REPL_WILL_RESTART_WITH_INFO message. */ poll_time = REPL_POLL_NOWAIT; if (!rollback_first && !secondary_ahead) resync_seqno = recvd_seqno; else if (START_FLAG_NORESYNC & recvd_start_flags) { repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL_START_JNL_SEQNO message has " "START_FLAG_NORESYNC bit set\n"); assert(!skip_last_histinfo_check); assert(1 != recvd_seqno); if (!rollback_first) { assert(secondary_ahead); assert(recvd_seqno == local_jnl_seqno); /* The primary and secondary are in sync as of "recvd_seqno" the jnl seqno of the * primary. So that is the common point. Send it across. */ resync_seqno = recvd_seqno; } else { resync_seqno = gtmsource_find_resync_seqno(&local_histinfo, &remote_histinfo); assert((MAX_SEQNO != resync_seqno) || (GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)); rollback_first = FALSE; } } else { /* Ask secondary to issue a fetchresync rollback */ REPL_DPRINT1("Secondary instance needs to first do MUPIP JOURNAL ROLLBACK FETCHRESYNC\n"); resync_seqno = local_jnl_seqno; poll_time = REPL_POLL_NOWAIT; rollback_first = TRUE; } if (MAX_SEQNO != resync_seqno) { QWASSIGN(*(seq_num *)&reply_msgp->start_seqno[0], resync_seqno); if (!rollback_first) { assert(NULL != gd_header); assert(0 < gd_header->n_regions); grab_gtmsource_srv_latch(gtmsource_srv_latch, 2 * gd_header->n_regions * max_epoch_interval, HANDLE_CONCUR_ONLINE_ROLLBACK); # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_HOLD_GTMSOURCE_SRV_LATCH)) while (0 == TREF(continue_proc_cnt)) LONG_SLEEP(1); # endif if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; continue; } srch_status = gtmsource_srch_restart(resync_seqno, recvd_start_flags); rel_gtmsource_srv_latch(>msource_local->gtmsource_srv_latch); assert(resync_seqno == gtmsource_local->read_jnl_seqno); assert(SS_NORMAL == srch_status); reply_msgp->type = REPL_WILL_RESTART_WITH_INFO; reply_msgp->jnl_ver = this_side->jnl_ver; temp_ulong = (0 == this_side->is_std_null_coll) ? START_FLAG_NONE : START_FLAG_COLL_M; GTMTRIG_ONLY( assert(this_side->trigger_supported); temp_ulong |= START_FLAG_TRIGGER_SUPPORT; ) # ifdef GTM_TLS if (REPL_TLS_REQUESTED) { if (!remote_side->tls_requested) { ISSUE_REPLNOTLS(ERR_REPLNOTLS, "Source side", "Receiver side"); CLEAR_REPL_TLS_REQUESTED; /* As if -tlsid qualifier was never specified. */ } else temp_ulong |= START_FLAG_ENABLE_TLS; } # endif PUT_ULONG(reply_msgp->start_flags, temp_ulong); recvd_start_flags = START_FLAG_NONE; gtmsource_repl_send((repl_msg_ptr_t)reply_msgp, "REPL_WILL_RESTART_WITH_INFO", resync_seqno, INVALID_SUPPL_STRM); } else { /* Secondary needs to first do FETCHRESYNC rollback to synchronize with primary */ reply_msgp->type = REPL_ROLLBACK_FIRST; poll_time = REPL_POLL_NOWAIT; gtmsource_repl_send((repl_msg_ptr_t)reply_msgp, "REPL_ROLLBACK_FIRST", resync_seqno, INVALID_SUPPL_STRM); } } } else { /* REPL_FETCH_RESYNC received and state is WAITING_FOR_RESTART */ if (rollback_first || secondary_ahead) { /* Primary and Secondary are currently not in sync */ if (!rollback_first) { /* We know the secondary is ahead of the primary in terms of journal seqno but the last * histinfo records are identical. This means that the secondary is in sync with the * primary until the primary's journal seqno ("local_jnl_seqno") which should be the new * resync seqno. */ resync_seqno = local_jnl_seqno; } else { /* Determine the resync seqno between this primary and secondary by comparing * local and remote histinfo records from the tail of the instance file until we reach * one seqno whose histinfo information is identical in both. */ assert(1 != recvd_seqno); resync_seqno = gtmsource_find_resync_seqno(&local_histinfo, &remote_histinfo); assert((MAX_SEQNO != resync_seqno) || (GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)); } } else { /* Primary and Secondary are in sync upto "recvd_seqno". Send it back as the new resync seqno. */ resync_seqno = recvd_seqno; } if (MAX_SEQNO != resync_seqno) { assert(GTMSOURCE_WAITING_FOR_RESTART == gtmsource_state && REPL_FETCH_RESYNC == recvd_msg_type); reply_msgp->type = REPL_RESYNC_SEQNO; QWASSIGN(*(seq_num *)&reply_msgp->start_seqno[0], resync_seqno); gtmsource_repl_send((repl_msg_ptr_t)reply_msgp, "REPL_RESYNC_SEQNO", resync_seqno, INVALID_SUPPL_STRM); } } if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return (SS_NORMAL); /* "gtmsource_repl_send" or "gtmsource_find_resync_seqno" did not complete */ if ((GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) { poll_time = REPL_POLL_NOWAIT; continue; /* "gtmsource_repl_send" or "gtmsource_find_resync_seqno" did not complete */ } assert(MAX_SEQNO != resync_seqno); /* After having established connection, initialize a few fields in the gtmsource_local * structure and flush those changes to the instance file on disk. */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, HANDLE_CONCUR_ONLINE_ROLLBACK); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; continue; } gtmsource_local->connect_jnl_seqno = jctl->jnl_seqno; gtmsource_local->send_losttn_complete = jctl->send_losttn_complete; /* Now that "connect_jnl_seqno" has been updated, flush it to corresponding gtmsrc_lcl on disk */ repl_inst_flush_gtmsrc_lcl(); /* this requires the jnlpool lock to be held */ rel_lock(jnlpool->jnlpool_dummy_reg); if (REPL_WILL_RESTART_WITH_INFO != reply_msgp->type) { assert(reply_msgp->type == REPL_RESYNC_SEQNO || reply_msgp->type == REPL_ROLLBACK_FIRST); if ((REPL_RESYNC_SEQNO == reply_msgp->type) && secondary_was_rootprimary) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Sent REPL_RESYNC_SEQNO message with SEQNO " INT8_FMT" "INT8_FMTX"\n", (*(seq_num *)&reply_msgp->start_seqno[0]), (*(seq_num *)&reply_msgp->start_seqno[0])); region_top = gd_header->regions + gd_header->n_regions; assert(NULL != jnlpool->jnlpool_dummy_reg); grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, HANDLE_CONCUR_ONLINE_ROLLBACK); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; continue; } zqgblmod_seqno = jctl->max_zqgblmod_seqno; if (0 == zqgblmod_seqno) { /* If zqgblmod_seqno in all file headers is 0, it implies that this is the first * FETCHRESYNC rollback after the most recent MUPIP REPLIC -LOSTTNCOMPLETE command. * Therefore reset zqgblmod_seqno to the rollback seqno. If not the first rollback, * then zqgblmod_seqno will be reset only if the new rollback seqno is lesser * than the current value. */ zqgblmod_seqno = MAX_SEQNO; /* actually 0xFFFFFFFFFFFFFFFF (max possible seqno) */ /* Reset any pending MUPIP REPLIC -SOURCE -LOSTTNCOMPLETE */ jctl->send_losttn_complete = FALSE; gtmsource_local->send_losttn_complete = jctl->send_losttn_complete; poll_time = REPL_POLL_NOWAIT; } rel_lock(jnlpool->jnlpool_dummy_reg); REPL_DPRINT2("BEFORE FINDING RESYNC - zqgblmod_seqno is "INT8_FMT, zqgblmod_seqno); REPL_DPRINT2(", curr_seqno is "INT8_FMT"\n", jctl->jnl_seqno); if (zqgblmod_seqno > resync_seqno) { /* reset "zqgblmod_seqno" and "zqgblmod_tn" in all fileheaders to "resync_seqno" */ GTMSOURCE_SAVE_STATE(gtmsource_state_sav); if (SS_NORMAL != gtmsource_update_zqgblmod_seqno_and_tn(resync_seqno)) { assert(GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; continue; } } if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return (SS_NORMAL); if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) continue; } } /* Could send a REPL_CLOSE_CONN message here */ /* It is expected that on receiving this msg, the Receiver Server will break the connection and exit. */ repl_close(>msource_sock_fd); LONG_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_TO_QUIT); /* may not be needed after REPL_CLOSE_CONN is sent */ gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; continue; } /* Now that REPL_WILL_RESTART_WITH_INFO message has been sent, if compression of the replication stream is * requested, check if the receiver server supports ability to decompress. Don't do this if this receiver has * previously sent a REPL_CMP2UNCMP message. */ gtmsource_local->repl_zlib_cmp_level = repl_zlib_cmp_level = ZLIB_CMPLVL_NONE; /* no compression by default */ if (!gtmsource_received_cmp2uncmp_msg && (ZLIB_CMPLVL_NONE != gtm_zlib_cmp_level)) { if (REPL_PROTO_VER_MULTISITE_CMP <= remote_side->proto_ver) { /* Receiver server is running a version of GT.M that supports compression of replication stream. * Send test message with compressed data to check if it is able to decompress properly. If so, * enable compression on the replication pipe. Compression level set in repl_zlib_cmp_level. */ if (!gtmsource_get_cmp_info(&repl_zlib_cmp_level)) { if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return (SS_NORMAL); else if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) continue; else { /* Got a REPL_XOFF_ACK_ME from receiver. Restart the initial handshake */ assert(GTMSOURCE_WAITING_FOR_RESTART == gtmsource_state); continue; } } /* Note down replication cmp_level in this source-server specific structure in journal pool */ gtmsource_local->repl_zlib_cmp_level = repl_zlib_cmp_level; } else { repl_log(gtmsource_log_fp, TRUE, FALSE, "Receiver server does not support compressed data on the replication pipe\n"); repl_log(gtmsource_log_fp, TRUE, FALSE, "Defaulting to NO compression\n"); } } # ifdef GTM_TLS if (!repl_tls.enabled && REPL_TLS_REQUESTED && remote_side->tls_requested) { /* Now that START_FLAGS have been exchanged and both sides request TLS/SSL, do the handshake and establish * an TLS/SSL connection. Even though the TCP connection is established much earlier, we want to do the * TLS/SSL handshake at a point that is close to its purpose which is to send the journal records. */ assert(REPL_PROTO_VER_TLS_SUPPORT <= REPL_PROTO_VER_THIS); assert(REPL_PROTO_VER_TLS_SUPPORT <= remote_side->proto_ver); if (!gtmsource_exchange_tls_info()) { switch (gtmsource_state) { case GTMSOURCE_CHANGING_MODE: /* ACTIVE->PASSIVE mode change in poll actions. */ return SS_NORMAL; case GTMSOURCE_WAITING_FOR_CONNECTION: /* Disconnect during gtmsource_repl_{send,recv} */ case GTMSOURCE_WAITING_FOR_RESTART: /* Got a REPL_XOFF_ACK_ME from receiver. Restart. */ continue; default: assert(FALSE); } } else { repl_tls.enabled = TRUE; /* From here on, all communications are secured with TLS/SSL. */ repl_tls.renegotiate_state = REPLTLS_RENEG_STATE_NONE; next_renegotiate_hrtbt = FALSE; hrtbt_cnt = 0; } } # endif if (NULL != repl_ctl_list) { /* The journal files may have been positioned * a) Ahead of the read_jnl_seqno for the next read in which case indicate that they have to be * repositioned into the past OR * b) Behind the read_jnl_seqno for the next read and ctl->lookback might have been set when * the disconnect of the previous connection occured and the newer connection happens * with a receiver database that is much more ahead in time than when it disconnected. * In this case fix ctl->lookback. (GTM-8547) */ assert(READ_FILE == gtmsource_local->read_state); gtmsource_set_lookback(); /* In case we read ahead, enable looking back. */ } /* The variable poll_time indicates if we should wait for the receive pipe to be I/O ready and should be set to * a non-zero value ONLY if the source server has nothing to send. At this point we have data to send and so * set poll_time to no-wait. */ poll_time = REPL_POLL_NOWAIT; gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_SENDING_JNLRECS; assert(1 <= gtmsource_local->read_jnl_seqno); /* Now that "gtmsource_local->read_jnl_seqno" is initialized, ensure the first send gets logged. */ gtmsource_reinit_logseqno(); gtmsource_init_heartbeat(); /* Internal filters are needed as long as the filter format of the originating side is greater or equal to the * filter format of the secondary side */ if ((this_side->jnl_ver >= remote_side->jnl_ver) && (IF_NONE != repl_filter_cur2old[remote_side->jnl_ver - JNL_VER_EARLIEST_REPL])) { assert(IF_INVALID != repl_filter_cur2old[remote_side->jnl_ver - JNL_VER_EARLIEST_REPL]); assert(IF_INVALID != repl_filter_old2cur[remote_side->jnl_ver - JNL_VER_EARLIEST_REPL]); /* reverse transformation should exist */ assert(IF_NONE != repl_filter_old2cur[remote_side->jnl_ver - JNL_VER_EARLIEST_REPL]); if (this_side->is_std_null_coll != remote_side->is_std_null_coll) remote_side->null_subs_xform = (this_side->is_std_null_coll ? STDNULL_TO_GTMNULL_COLL : GTMNULL_TO_STDNULL_COLL); else remote_side->null_subs_xform = FALSE; gtmsource_filter |= INTERNAL_FILTER; gtmsource_alloc_filter_buff(gtmsource_msgbufsiz); } else { gtmsource_filter &= ~INTERNAL_FILTER; if (NO_FILTER == gtmsource_filter) gtmsource_free_filter_buff(); } /* Reset some variables to their initial value before entering the while loop (to safeguard against stale values) */ xon_wait_logged = FALSE; heartbeat_stalled = FALSE; # ifdef GTM_TLS DEBUG_ONLY(renegotiation_pending = FALSE); if (repl_tls.enabled && (0 < gtmsource_options.renegotiate_interval)) repl_tls.renegotiate_state = REPLTLS_WAITING_FOR_RENEG_TIMEOUT; # endif /* Flush "gtmsource_local->read_jnl_seqno" to disk right now. This will serve as a reference point for next timed * flush to occur. */ gtmsource_flush_fh(gtmsource_local->read_jnl_seqno); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; continue; } gtmsource_local->send_new_histrec = TRUE; /* Send new histinfo unconditionally at start of connection */ gtmsource_local->next_histinfo_seqno = MAX_SEQNO; /* Initial value. Reset by "gtmsource_send_new_histrec" below */ assert(-1 == gtmsource_local->next_histinfo_num); prev_now = gtmsource_now; phase2_commit_index1 = 0; phase2_commit_wait_cnt = 0; while (TRUE) { gtmsource_poll_actions(TRUE); if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return (SS_NORMAL); else if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; break; /* The outerloop will continue */ } if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) break; if (gtmsource_local->send_losttn_complete) { /* Send LOSTTNCOMPLETE across to the secondary and reset flag to FALSE */ losttncomplete_msg.type = REPL_LOSTTNCOMPLETE; losttncomplete_msg.len = MIN_REPL_MSGLEN; gtmsource_repl_send((repl_msg_ptr_t)&losttncomplete_msg, "REPL_LOSTTNCOMPLETE", MAX_SEQNO, INVALID_SUPPL_STRM); grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, HANDLE_CONCUR_ONLINE_ROLLBACK); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; break; /* the outerloop will continue */ } gtmsource_local->send_losttn_complete = FALSE; rel_lock(jnlpool->jnlpool_dummy_reg); } if (gtmsource_local->send_new_histrec) { /* We are at the beginning of a new histinfo record boundary. Send a REPL_HISTREC message * before sending journal records for seqnos corresponding to this histinfo. */ assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver); /* Remote version supports multi-site functionality. Send REPL_HISTREC and friends */ gtmsource_send_new_histrec(); if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return (SS_NORMAL); if ((GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) break; assert(FALSE == gtmsource_local->send_new_histrec); } if (prev_now != gtmsource_now) { prev_now = gtmsource_now; if (gtmsource_msgbufsiz - MAX_REPL_MSGLEN > 2 * OS_PAGE_SIZE) { /* We have expanded the buffer by too much (could have been avoided had we sent one * transaction at a time while reading from journal files); let's revert back to our * initial buffer size. If we don't reduce our buffer, it is possible that the buffer keeps * growing (while reading * from journal file) thus making the size of sends while reading * from journal pool very large (> 1 MB). */ gtmsource_free_filter_buff(); gtmsource_free_msgbuff(); gtmsource_alloc_msgbuff(MAX_REPL_MSGLEN, TRUE); /* will also allocate filter buffer */ } } # if defined(DEBUG) && defined(GTM_TLS) if (repl_tls.enabled && (WBTEST_ENABLED(WBTEST_INDUCE_TLSIOERR))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_TLSIOERROR, 2, LEN_AND_LIT("WBTEST_INDUCE_TLSIOERR")); # endif /* GTMSOURCE_SAVE_STATE() and GTMSOURCE_NOW_TRANSITIONAL() check are not needed * here as the existing logic handles transitions. */ gtmsource_recv_ctl(); if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return SS_NORMAL; if ((GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) { poll_time = REPL_POLL_NOWAIT; break; } # ifdef GTM_TLS /* If we are waiting for a REPL_RENEG_ACK from the receiver, don't send any more messages (even journal * records) before completing the renegotiation. */ if (REPLTLS_WAITING_FOR_RENEG_ACK == repl_tls.renegotiate_state) continue; # endif if (GTMSOURCE_WAITING_FOR_XON == gtmsource_state) { if (!xon_wait_logged) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Waiting to receive XON\n"); heartbeat_stalled = TRUE; REPL_DPRINT1("Stalling HEARTBEAT\n"); xon_wait_logged = TRUE; } continue; } if ((GTMSOURCE_SEARCHING_FOR_RESTART == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state)) break; assert(gtmsource_state == GTMSOURCE_SENDING_JNLRECS); pre_read_seqno = gtmsource_local->read_jnl_seqno; GTMTLS_ONLY(assert(!renegotiation_pending)); grab_gtmsource_srv_latch(gtmsource_srv_latch, 2 * gd_header->n_regions * max_epoch_interval, HANDLE_CONCUR_ONLINE_ROLLBACK); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; break; /* the outerloop will continue */ } GTMSOURCE_SAVE_STATE(gtmsource_state_sav); tot_tr_len = gtmsource_get_jnlrecs(>msource_msgp->msg[0], &data_len, gtmsource_msgbufsiz - REPL_MSG_HDRLEN, !(gtmsource_filter & EXTERNAL_FILTER)); rel_gtmsource_srv_latch(>msource_local->gtmsource_srv_latch); /* It is safe to send the journal records as we are guaranteed NO online rollback happened in between * and so we won't be sending garbage */ if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return (SS_NORMAL); if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) break; if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) continue; if (GTMSOURCE_SEND_NEW_HISTINFO == gtmsource_state) { /* This is a signal from "gtmsource_get_jnlrecs" to send a REPL_HISTREC message first * before sending any more seqnos across. Set "gtmsource_local->send_new_histrec" to TRUE. */ assert(0 == tot_tr_len); gtmsource_local->send_new_histrec = TRUE; /* Will cause a new histinfo record to be sent first */ gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_SENDING_JNLRECS; poll_time = REPL_POLL_NOWAIT; continue; /* Send a REPL_HISTREC message first and then send journal records */ } post_read_seqno = gtmsource_local->read_jnl_seqno; if (0 <= tot_tr_len) { if (0 < data_len) { APPLY_EXT_FILTER_IF_NEEDED(gtmsource_filter, gtmsource_msgp, data_len, tot_tr_len); gtmsource_msgp->type = REPL_TR_JNL_RECS; gtmsource_msgp->len = data_len + REPL_MSG_HDRLEN; send_msgp = gtmsource_msgp; send_tr_len = tot_tr_len; intfilter_error = FALSE; if (gtmsource_filter & INTERNAL_FILTER) { in_buff = gtmsource_msgp->msg; in_buflen = data_len; /* jrec size of the FIRST SEQNO in the converted buffer */ out_buffmsg = repl_filter_buff; out_buff = out_buffmsg + REPL_MSG_HDRLEN; out_bufsiz = repl_filter_bufsiz - REPL_MSG_HDRLEN; remaining_len = tot_tr_len; /* jrec size of ALL SEQNOs ( >= 1) in buffer */ while (JREC_PREFIX_SIZE <= remaining_len) { filter_seqno = ((struct_jrec_null *)(in_buff))->jnl_seqno; DEBUG_ONLY( save_inbuff = in_buff; save_outbuff = out_buff; ) APPLY_INT_FILTER(in_buff, in_buflen, out_buff, out_buflen, out_bufsiz, status); /* Internal filters should not modify the incoming pointers. Assert that. */ assert((save_inbuff == in_buff) && (save_outbuff == out_buff)); if (SS_NORMAL == status) { /* adjust various pointers and book-keeping values to move to next * record. */ ((repl_msg_ptr_t)(out_buffmsg))->type = REPL_TR_JNL_RECS; ((repl_msg_ptr_t)(out_buffmsg))->len = out_buflen + REPL_MSG_HDRLEN; out_buffmsg = (out_buff + out_buflen); remaining_len -= (in_buflen + REPL_MSG_HDRLEN); assert(0 <= remaining_len); if (0 >= remaining_len) break; in_buff += in_buflen; in_buflen = ((repl_msg_ptr_t)(in_buff))->len - REPL_MSG_HDRLEN; in_buff += REPL_MSG_HDRLEN; out_buff = (out_buffmsg + REPL_MSG_HDRLEN); out_bufsiz -= (out_buflen + REPL_MSG_HDRLEN); assert(0 <= (int)out_bufsiz); } else if (EREPL_INTLFILTER_NOSPC == repl_errno) { REALLOCATE_INT_FILTER_BUFF(out_buff, out_buffmsg, out_bufsiz); /* note that in_buff and in_buflen is not changed so that we can * start from where we left */ } else /* fatal error from the internal filter */ { intfilter_error = TRUE; break; } } assert((0 == remaining_len) || intfilter_error); GTMTRIG_ONLY(ISSUE_TRIG2NOTRIG_IF_NEEDED;) send_msgp = (repl_msg_ptr_t)repl_filter_buff; send_tr_len = out_buffmsg - repl_filter_buff; if (0 == send_tr_len) { /* This is possible ONLY if the first transaction in the buffer read from * journal pool or disk encountered error while doing internal filter * conversion. Issue rts_error right away as there is nothing much we can * do at this point. */ assert(intfilter_error); assert(filter_seqno == pre_read_seqno); INT_FILTER_RTS_ERROR(filter_seqno, repl_errno); /* no return */ } } assert(send_tr_len && (0 == (send_tr_len % REPL_MSG_ALIGN))); /* ensure that the head of the buffer has the correct type and len */ assert((REPL_TR_JNL_RECS == send_msgp->type) && (0 == (send_msgp->len % JNL_REC_START_BNDRY))); /* At this point send_msgp is the buffer to be sent and send_tr_len is the send size */ assert(remote_side->endianness_known); if (remote_side->cross_endian && (this_side->jnl_ver < remote_side->jnl_ver)) { /* Cross-endian replication with GT.M version on primary being lesser than that * the secondary. Do the endian conversion in the primary so that the secondary * can consume it as-is. * No return if the below call to repl_tr_endian_convert fails. */ repl_tr_endian_convert(send_msgp, send_tr_len, pre_read_seqno); } pre_cmpmsglen = send_tr_len; /* send_tr_len will be updated below */ if (ZLIB_CMPLVL_NONE != repl_zlib_cmp_level) { /* Compress the journal records before replicating them across the pipe. * Depending on whether the total data length to be sent is within a threshold * or not (see repl_msg.h before REPL_TR_CMP_THRESHOLD #define for why), send * either a REPL_TR_CMP_JNL_RECS or REPL_TR_CMP_JNL_RECS2 message */ msghdrlen = (REPL_TR_CMP_THRESHOLD > send_tr_len) ? REPL_MSG_HDRLEN : REPL_MSG_HDRLEN2; cmpbuflen = gtmsource_cmpmsgbufsiz - msghdrlen; cmpbufptr = ((Bytef *)gtmsource_cmpmsgp) + msghdrlen; ZLIB_COMPRESS(cmpbufptr, cmpbuflen, send_msgp, send_tr_len, repl_zlib_cmp_level, cmpret); BREAK_IF_CMP_ERROR(cmpret, send_tr_len); /* Note: break stmt. inside the macro */ if (Z_OK == cmpret) { /* Send compressed buffer */ send_msgp = gtmsource_cmpmsgp; if (REPL_TR_CMP_THRESHOLD > send_tr_len) { /* Send REPL_TR_CMP_JNL_RECS message with 8-byte header */ SET_8BYTE_CMP_MSGHDR(send_msgp, send_tr_len, cmpbuflen, msghdrlen); } else { /* Send REPL_TR_CMP_JNL_RECS2 message with 16-byte header */ SET_16BYTE_CMP_MSGHDR(send_msgp, send_tr_len, cmpbuflen, msghdrlen); } } else { /* Send normal buffer */ repl_log(gtmsource_log_fp, TRUE, FALSE, "Defaulting to NO compression\n"); repl_zlib_cmp_level = ZLIB_CMPLVL_NONE; /* no compression */ gtmsource_local->repl_zlib_cmp_level = repl_zlib_cmp_level; } } assert((send_tr_len == pre_cmpmsglen) || (ZLIB_CMPLVL_NONE != repl_zlib_cmp_level)); assert(0 == (send_tr_len % REPL_MSG_ALIGN)); /* The following loop tries to send multiple seqnos in one shot. resync_seqno gets * updated once the send is completely successful. If an error occurs in the middle * of the send, it is possible that we successfully sent a few seqnos to the other side. * In this case resync_seqno should be updated to reflect those seqnos. Not doing so * might cause the secondary to get ahead of the primary in terms of resync_seqno. * Although it is possible to determine the exact seqno where the send partially failed, * we update resync_seqno as if all seqnos were successfully sent (It is ok for the * resync_seqno on the primary side to be a little more than the actual value as long as * the secondary side has an accurate value of resync_seqno. This is because the * resync_seqno of the system is the minimum of the resync_seqno of both primary * and secondary). This is done by the call to gtmsource_flush_fh() done within * gtmsource_poll_actions() as well as in the (SS_NORMAL != status) if condition below. * Note that all of this is applicable only in a dualsite replication scenario. In * case of a multisite scenario, it is always the receiver server that tells the * sequence number from where the source server should start sending. So, even if * the source server notes down a higher value of journal sequence number in * jnlpool->gtmsource_local->read_jnl_seqno, it is not a problem since the receiver * server will communicate the appropriate sequence number as part of the histinfo * exchange. */ REPL_SEND_LOOP(gtmsource_sock_fd, send_msgp, send_tr_len, REPL_POLL_WAIT) { gtmsource_poll_actions(FALSE); if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return (SS_NORMAL); else if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; break; } } if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; break; /* The outerloop will continue */ } if (SS_NORMAL != status) { gtmsource_flush_fh(post_read_seqno); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; break; /* the outerloop will continue */ } if (EREPL_SEND == repl_errno) { if (REPL_CONN_RESET(status)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Connection reset while sending seqno data from " INT8_FMT" "INT8_FMTX" to "INT8_FMT" "INT8_FMTX ". Status = %d ; %s\n", pre_read_seqno, pre_read_seqno, post_read_seqno, post_read_seqno, status, STRERROR(status)); repl_log_conn_info(gtmsource_sock_fd, gtmsource_log_fp, TRUE); close_retry = TRUE; } if (close_retry) { repl_close(>msource_sock_fd); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; close_retry = FALSE; break; } else { SNPRINTF(err_string, SIZEOF(err_string), "Error sending DATA. Error in send : %s", STRERROR(status)); SEND_SYSMSG_REPLCOMM(LEN_AND_STR(err_string)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_STR(err_string)); } } if (EREPL_SELECT == repl_errno) { SNPRINTF(err_string, SIZEOF(err_string), "Error sending DATA. Error in select : %s", STRERROR(status)); SEND_SYSMSG_REPLCOMM(LEN_AND_STR(err_string)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_STR(err_string)); } } if (intfilter_error) { /* Now that we are done sending whatever buffer was filter converted, issue * the error. This will bring down the source server (due to the rts_error). * At this point, jnlpool->gtmsource_local->read_jnl_seqno could effectively * be behind the receiver server's journal sequence number. But, that is * okay since as part of reconnection (when the source server comes back up), * the receiver server will communicate the appropriate sequence number as * part of the histinfo exchange. */ assert(filter_seqno <= post_read_seqno); INT_FILTER_RTS_ERROR(filter_seqno, repl_errno); /* no return */ } jnlpool->gtmsource_local->read_jnl_seqno = post_read_seqno; repl_source_cmp_sent += (qw_num)send_tr_len; repl_source_msg_sent += (qw_num)pre_cmpmsglen; repl_source_data_sent += (qw_num)(pre_cmpmsglen) - (post_read_seqno - pre_read_seqno) * REPL_MSG_HDRLEN; log_seqno = post_read_seqno - 1; /* post_read_seqno is the "next" seqno to be sent, * not the last one we sent */ if (gtmsource_logstats || (log_seqno - lastlog_seqno >= log_interval)) { /* print always when STATSLOG is ON, or when the log interval has passed */ trans_sent_cnt += (log_seqno - lastlog_seqno); /* jctl->jnl_seqno >= post_read_seqno is the most common case; * see gtmsource_readpool() for when the rare case can occur */ jnl_seqno = jctl->jnl_seqno; if (jnl_seqno >= post_read_seqno - 1) { diff_seqno = (jnl_seqno >= post_read_seqno) ? (jnl_seqno - post_read_seqno) : 0; repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL INFO - Seqno : "INT8_FMT" " INT8_FMTX" Jnl Total : "INT8_FMT" "INT8_FMTX " Msg Total : "INT8_FMT" "INT8_FMTX" CmpMsg Total : " INT8_FMT" "INT8_FMTX" Current backlog : "INT8_FMT" "INT8_FMTX"\n", log_seqno, log_seqno, repl_source_data_sent, repl_source_data_sent, repl_source_msg_sent, repl_source_msg_sent, repl_source_cmp_sent, repl_source_cmp_sent, diff_seqno, diff_seqno); /* gtmsource_now is updated by the heartbeat protocol every heartbeat * interval. To cut down on calls to time(), we use gtmsource_now as the * time to figure out if we have to log statistics. This works well as the * logging interval generally is larger than the heartbeat interval, and * that the heartbeat protocol is running when we are sending data. The * consequence although is that we may defer logging when we would have * logged. We can live with that given the benefit of not calling time * related system calls. Currently, the logging interval is not changeable * by users. When/if we provide means of choosing log interval, this code * may have to be re-examined. Vinaya 2003, Sep 08 */ assert(0 != gtmsource_now); /* must hold if we are sending data */ repl_source_this_log_time = gtmsource_now; /* approximate time, in the * worst case, behind by * heartbeat interval */ assert(repl_source_this_log_time >= repl_source_prev_log_time); time_elapsed = difftime(repl_source_this_log_time, repl_source_prev_log_time); if ((double)GTMSOURCE_LOGSTATS_INTERVAL <= time_elapsed) { delta_sent_cnt = trans_sent_cnt - last_log_tr_sent_cnt; delta_data_sent = repl_source_data_sent - repl_source_lastlog_data_sent; delta_msg_sent = repl_source_msg_sent - repl_source_lastlog_msg_sent; repl_log(gtmsource_log_fp, TRUE, FALSE, "REPL INFO since last log : " "Time elapsed : %00.f Tr sent : "INT8_FMT" "INT8_FMTX" " "Tr bytes : "INT8_FMT" "INT8_FMTX " Msg bytes : "INT8_FMT" "INT8_FMTX"\n", time_elapsed, delta_sent_cnt, delta_sent_cnt, delta_data_sent, delta_data_sent, delta_msg_sent, delta_msg_sent); repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL INFO since last log : " "Time elapsed : %00.f Tr sent/s : %f Tr bytes/s : %f " "Msg bytes/s : %f\n", time_elapsed, (float)delta_sent_cnt / time_elapsed, (float)delta_data_sent / time_elapsed, (float)delta_msg_sent / time_elapsed); repl_source_lastlog_data_sent = repl_source_data_sent; repl_source_lastlog_msg_sent = repl_source_msg_sent; last_log_tr_sent_cnt = trans_sent_cnt; repl_source_prev_log_time = repl_source_this_log_time; } lastlog_seqno = log_seqno; } /* else an online rollback occurred. Fall-through and the subsequent iteration * will take care of re-establishing the connection */ } /* Because we sent data to the other side and there might be more to be sent across, don't * wait for the receive pipe to be ready. */ poll_time = REPL_POLL_NOWAIT; } else /* data_len == 0 */ { /* nothing to send */ gtmsource_flush_fh(post_read_seqno); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { poll_time = REPL_POLL_NOWAIT; break; /* the outerloop will continue */ } if ((READ_POOL == gtmsource_local->read_state) && (jctl->write_addr != jctl->rsrv_write_addr)) { /* We found nothing to send in the journal pool but there is some phase2 commit * in progress. Check if it is dead and needs to be cleaned up by noting its * progress across various iterations of this "while" loop. */ if (phase2_commit_index1 == jctl->phase2_commit_index1) { /* We have been through one iteration of this "while" loop with * an intervening sleep and no changes happening. Do check. */ phase2_commit_wait_cnt++; if (PHASE2_COMMIT_WAIT_CNT <= phase2_commit_wait_cnt) { /* Need to get "grab_lock" in order for "repl_phase2_cleanup" * to invoke "repl_phase2_salvage" logic. So try getting it * in immediate mode (second parameter FALSE below). If not * available, then try again next iteration. */ if (grab_lock(jnlpool->jnlpool_dummy_reg, FALSE, GRAB_LOCK_ONLY)) { repl_phase2_cleanup(jnlpool); rel_lock(jnlpool->jnlpool_dummy_reg); phase2_commit_wait_cnt = 0; } else phase2_commit_wait_cnt--; /* Try again next iteration */ } } else { phase2_commit_wait_cnt = 0; /* there is progress since we last took note */ phase2_commit_index1 = jctl->phase2_commit_index1; } } /* Sleep for a while (as part of the next REPL_RECV_LOOP) to avoid spinning when there is no * data to be sent */ poll_time = GTMSOURCE_IDLE_POLL_WAIT; } } else /* else tot_tr_len < 0, error */ { assertpro(0 < data_len); /* Else major problems */ /* Insufficient buffer space, increase the buffer space */ gtmsource_alloc_msgbuff(data_len + REPL_MSG_HDRLEN, TRUE); } } } } fis-gtm-V7.0-005/sr_unix/gtmsource_process_ops.c0000644000032200000250000032323614342376330020645 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2022 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. * * * ****************************************************************/ #if defined(__MVS__) && !defined(_ISOC99_SOURCE) #define _ISOC99_SOURCE #endif #include "mdef.h" #include "gtm_stdio.h" /* for FILE * in repl_comm.h */ #include "gtm_socket.h" #include "gtm_netdb.h" #include "gtm_inet.h" #include #include #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_stat.h" #include "gtm_string.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_comm.h" #include "repl_shutdcode.h" #include "jnl.h" #include "hashtab_mname.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "buddy_list.h" #include "muprec.h" #include "repl_ctl.h" #include "repl_errno.h" #include "gtmio.h" /* for REPL_DPRINT* macros */ #include "repl_dbg.h" #include "iosp.h" #include "gt_timer.h" #include "eintr_wrappers.h" #include "repl_sp.h" #include "repl_filter.h" #include "repl_log.h" #include "sgtm_putmsg.h" #include "min_max.h" #include "error.h" #include "repl_instance.h" #include "ftok_sems.h" #include "gtmmsg.h" #include "wbox_test_init.h" #include "have_crit.h" /* needed for ZLIB_COMPRESS */ #include "deferred_signal_handler.h" /* needed for ZLIB_COMPRESS */ #include "gtm_zlib.h" #include "replgbl.h" #include "repl_inst_dump.h" /* for "repl_dump_histinfo" prototype */ #include "gtmdbgflags.h" #include "lockconst.h" #ifdef GTM_TLS #include "gtm_repl.h" #endif #define SEND_REPL_LOGFILE_INFO(LOGFILE, LOGFILE_MSG) \ { \ int len; \ \ len = repl_logfileinfo_get(LOGFILE, &LOGFILE_MSG, FALSE, gtmsource_log_fp); \ REPL_SEND_LOOP(gtmsource_sock_fd, &LOGFILE_MSG, len, REPL_POLL_NOWAIT) \ { \ gtmsource_poll_actions(FALSE); \ if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) \ || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) \ return SS_NORMAL; \ } \ } #define PROC_OPS_PRINT_MSG_LEN 1024 char print_msg_src[PROC_OPS_PRINT_MSG_LEN]; char print_msg_t[PROC_OPS_PRINT_MSG_LEN]; GBLREF boolean_t gtmsource_logstats; GBLREF boolean_t gtmsource_pool2file_transition; GBLREF boolean_t gtmsource_received_cmp2uncmp_msg; GBLREF boolean_t secondary_side_std_null_coll; GBLREF FILE *gtmsource_log_fp; GBLREF gd_addr *gd_header; GBLREF gtmsource_state_t gtmsource_state; GBLREF int4 strm_index; GBLREF int gtmsource_cmpmsgbufsiz; GBLREF int gtmsource_filter; GBLREF int gtmsource_log_fd; GBLREF int gtmsource_msgbufsiz; GBLREF int gtmsource_sock_fd; GBLREF int repl_filter_bufsiz; GBLREF int repl_max_send_buffsize, repl_max_recv_buffsize; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF repl_conn_info_t *this_side, *remote_side; GBLREF repl_ctl_element *repl_ctl_list; GBLREF repl_msg_ptr_t gtmsource_cmpmsgp; GBLREF repl_msg_ptr_t gtmsource_msgp; GBLREF seq_num gtmsource_save_read_jnl_seqno; GBLREF seq_num seq_num_zero; GBLREF uchar_ptr_t repl_filter_buff; GBLREF uint4 process_id; GBLREF unsigned char *gtmsource_tcombuffp; GBLREF unsigned char *gtmsource_tcombuff_start; #ifdef GTM_TLS GBLREF gtmsource_options_t gtmsource_options; #endif error_def(ERR_REPLALERT); error_def(ERR_REPL2OLD); error_def(ERR_REPLCOMM); error_def(ERR_REPLFTOKSEM); error_def(ERR_REPLINSTNOHIST); error_def(ERR_REPLINSTSECMTCH); error_def(ERR_REPLNOXENDIAN); error_def(ERR_SECNOTSUPPLEMENTARY); error_def(ERR_STRMNUMMISMTCH1); error_def(ERR_STRMNUMMISMTCH2); error_def(ERR_TEXT); error_def(ERR_TLSCONVSOCK); error_def(ERR_TLSHANDSHAKE); int gtmsource_est_conn() { char print_msg[PROC_OPS_PRINT_MSG_LEN], msg_str[1024], *errmsg; int connection_attempts, max_heartbeat_wait, save_errno, comminit_retval, status; int send_buffsize, recv_buffsize, tcp_s_bufsize; int logging_period, logging_interval; /* logging period = soft_tries_period*logging_interval */ int alert_period, hardtries_count, hardtries_period; int max_shutdown_wait, max_sleep, soft_tries_period; int secondary_addrlen; time_t alert_time_start = 0, noconnection_time = 0; /* For logging the REPLALERT message */ boolean_t throw_errors = TRUE; sockaddr_ptr secondary_sa; gtmsource_local_ptr_t gtmsource_local; gtmsource_local = jnlpool->gtmsource_local; # ifdef GTM_TLS assert(!repl_tls.enabled); /* Set after REPL_NEED_TLS_INFO/REPL_TLS_INFO messages are exchanged. */ assert(REPLTLS_RENEG_STATE_NONE == repl_tls.renegotiate_state); /* We either did not create a TLS/SSL aware socket or the SSL object off of the TLS/SSL aware socket should be NULL since * we haven't yet connected to the receiver server. Assert that. */ assert((NULL == repl_tls.sock) || (NULL == repl_tls.sock->ssl)); /* Since this is a new connection, reset next renegotiation time. */ gtmsource_local->next_renegotiate_time = 0; /* Set to correct value after TLS/SSL handshake is established. */ # endif assert(remote_side == >msource_local->remote_side); remote_side->proto_ver = REPL_PROTO_VER_UNINITIALIZED; remote_side->endianness_known = FALSE; /* Connect to the secondary - use hard tries, soft tries ... */ connection_attempts = 0; comminit_retval = gtmsource_comm_init(throw_errors); /* set up gtmsource_local.secondary_ai */ max_shutdown_wait = GTMSOURCE_MAX_SHUTDOWN_WAITLOOP(gd_header); soft_tries_period = gtmsource_local->connect_parms[GTMSOURCE_CONN_SOFT_TRIES_PERIOD]; hardtries_period = gtmsource_local->connect_parms[GTMSOURCE_CONN_HARD_TRIES_PERIOD]; hardtries_count = gtmsource_local->connect_parms[GTMSOURCE_CONN_HARD_TRIES_COUNT]; alert_period = gtmsource_local->connect_parms[GTMSOURCE_CONN_ALERT_PERIOD]; max_heartbeat_wait = gtmsource_local->connect_parms[GTMSOURCE_CONN_HEARTBEAT_MAX_WAIT]; max_sleep = DIVIDE_ROUND_UP(max_shutdown_wait, 2); if (hardtries_period > (max_sleep * 1000)) { hardtries_period = max_sleep * 1000; repl_log(gtmsource_log_fp, TRUE, TRUE, "Hard tries period cannot be more than half of maximum shutdown wait time of %d seconds. " "Reducing hard tries period to %d milliseconds \n", max_shutdown_wait, hardtries_period); } repl_log(gtmsource_log_fp, TRUE, TRUE, "Connect hard tries count = %d, Connect hard tries period = %d milliseconds\n", hardtries_count, hardtries_period); while (++connection_attempts < (hardtries_count + 1)) { if (0 == noconnection_time) time(&noconnection_time); if ((FD_INVALID == gtmsource_sock_fd) || (0 != comminit_retval)) { /* gtmsource_comm_init failed to initialize the socket. Report error and retry gtmsource_comm_init. */ repl_log(gtmsource_log_fp, TRUE, TRUE, "%d hard connection attempt failed : Error with source server connection initiation\n", connection_attempts); } else { secondary_sa = (sockaddr_ptr)(>msource_local->secondary_inet_addr); secondary_addrlen = gtmsource_local->secondary_addrlen; CONNECT_SOCKET(gtmsource_sock_fd, secondary_sa, secondary_addrlen, status); if (0 == status) { noconnection_time = 0; break; } repl_log(gtmsource_log_fp, TRUE, TRUE, "%d hard connection attempt failed : %s\n", connection_attempts, STRERROR(errno)); repl_close(>msource_sock_fd); } if (MILLISECS_IN_SEC > hardtries_period) { SHORT_SLEEP(hardtries_period); } else LONG_SLEEP_MSEC(hardtries_period); gtmsource_poll_actions(FALSE); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) return (SS_NORMAL); /* Check for network resolution issues if the socket is invalid */ if (FD_INVALID == gtmsource_sock_fd) comminit_retval = gtmsource_comm_init(throw_errors); } /* end of while*/ gtmsource_poll_actions(FALSE); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) return (SS_NORMAL); if (hardtries_count <= connection_attempts) { /*Initialize variables to gradually reduce soft_try_attempts period logging*/ logging_period = soft_tries_period; logging_interval = 1; if (soft_tries_period > max_sleep) { soft_tries_period = max_sleep; repl_log(gtmsource_log_fp, TRUE, TRUE, "Soft tries period cannot be more than half of the maximum shutdown wait time of %d seconds. " "Reducing soft tries period to %d seconds \n", max_shutdown_wait, soft_tries_period); } repl_log(gtmsource_log_fp, TRUE, TRUE, "Soft tries period = %d seconds, REPLALERT message period = %d seconds\n", soft_tries_period, alert_period); connection_attempts = 0; do { if ((FD_INVALID == gtmsource_sock_fd) || (0 != comminit_retval)) { /* gtmsource_comm_init failed to initialize the socket. Report error and retry gtmsource_comm_init. */ if (0 == alert_time_start) time(&alert_time_start); if (0 == noconnection_time) noconnection_time = alert_time_start; if (0 == (connection_attempts + 1) % logging_interval) { repl_log(gtmsource_log_fp, TRUE, TRUE, "%d soft connection attempt failed : Error with source server connection initiation\n", connection_attempts + 1); throw_errors = TRUE; } else /* Decrease the frequency of showing the connection failure error messages */ throw_errors = FALSE; } else { secondary_sa = (sockaddr_ptr)(>msource_local->secondary_inet_addr); secondary_addrlen = gtmsource_local->secondary_addrlen; CONNECT_SOCKET(gtmsource_sock_fd, secondary_sa, secondary_addrlen, status); if (0 == status) { noconnection_time = 0; break; } if (0 == alert_time_start) time(&alert_time_start); if (0 == noconnection_time) noconnection_time = alert_time_start; save_errno = errno; repl_close(>msource_sock_fd); if (0 == (connection_attempts + 1) % logging_interval) { repl_log(gtmsource_log_fp, TRUE, TRUE, "%d soft connection attempt failed : %s\n", connection_attempts + 1, STRERROR(save_errno)); throw_errors = TRUE; } else /* Decrease the frequency of showing the connection failure error messages */ throw_errors = FALSE; } LONG_SLEEP(soft_tries_period); gtmsource_poll_actions(FALSE); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) return (SS_NORMAL); /* Check for network resolution issues if the socket is invalid */ if (FD_INVALID == gtmsource_sock_fd) comminit_retval = gtmsource_comm_init(throw_errors); connection_attempts++; if ((0 != alert_period) && ((double)alert_period <= difftime(time(NULL), alert_time_start))) { /* Log the REPLALERT message */ sgtm_putmsg(print_msg, PROC_OPS_PRINT_MSG_LEN, VARLSTCNT(4) ERR_REPLALERT, 2, jnlpool->gtmsource_local->secondary_instname, (UINTPTR_T)difftime(time(NULL), noconnection_time)); repl_log(gtmsource_log_fp, TRUE, TRUE, print_msg); alert_time_start = 0; } if (logging_period <= REPL_MAX_LOG_PERIOD) { /*the maximum real_period can reach 2*REPL_MAX_LOG_PERIOD)*/ if (0 == connection_attempts % logging_interval) { /* Double the logging period after every logging attempt*/ logging_interval = logging_interval << 1; logging_period = logging_period << 1; } } } while (TRUE); } if (0 != (status = get_send_sock_buff_size(gtmsource_sock_fd, &send_buffsize))) { SNPRINTF(msg_str, SIZEOF(msg_str), "Error getting socket send buffsize : %s", STRERROR(status)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_STR(msg_str)); } if (send_buffsize < GTMSOURCE_TCP_SEND_BUFSIZE) { for (tcp_s_bufsize = GTMSOURCE_TCP_SEND_BUFSIZE; tcp_s_bufsize >= MAX(send_buffsize, GTMSOURCE_MIN_TCP_SEND_BUFSIZE) && 0 != (status = set_send_sock_buff_size(gtmsource_sock_fd, tcp_s_bufsize)); tcp_s_bufsize -= GTMSOURCE_TCP_SEND_BUFSIZE_INCR) ; if (tcp_s_bufsize < GTMSOURCE_MIN_TCP_SEND_BUFSIZE) { SNPRINTF(msg_str, SIZEOF(msg_str), "Could not set TCP send buffer size in range [%d, %d], last " "known error : %s", GTMSOURCE_MIN_TCP_SEND_BUFSIZE, GTMSOURCE_TCP_SEND_BUFSIZE, STRERROR(status)); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) MAKE_MSG_INFO(ERR_REPLCOMM), 0, ERR_TEXT, 2, LEN_AND_STR(msg_str)); } } if (0 != (status = get_send_sock_buff_size(gtmsource_sock_fd, &repl_max_send_buffsize))) /* may have changed */ { SNPRINTF(msg_str, SIZEOF(msg_str), "Error getting socket send buffsize : %s", STRERROR(status)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_STR(msg_str)); } if (0 != (status = get_recv_sock_buff_size(gtmsource_sock_fd, &recv_buffsize))) { errmsg = STRERROR(status); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(10) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Error getting socket recv buffsize"), ERR_TEXT, 2, LEN_AND_STR(errmsg)); } if (recv_buffsize < GTMSOURCE_TCP_RECV_BUFSIZE) { if (0 != (status = set_recv_sock_buff_size(gtmsource_sock_fd, GTMSOURCE_TCP_RECV_BUFSIZE))) { if (recv_buffsize < GTMSOURCE_MIN_TCP_RECV_BUFSIZE) { SNPRINTF(msg_str, SIZEOF(msg_str), "Could not set TCP recv buffer size to %d : %s", GTMSOURCE_MIN_TCP_RECV_BUFSIZE, STRERROR(status)); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) MAKE_MSG_INFO(ERR_REPLCOMM), 0, ERR_TEXT, 2, LEN_AND_STR(msg_str)); } } } if (0 != (status = get_recv_sock_buff_size(gtmsource_sock_fd, &repl_max_recv_buffsize))) /* may have changed */ { SNPRINTF(msg_str, SIZEOF(msg_str), "Error getting socket recv buffsize : %s", STRERROR(status)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_STR(msg_str)); } repl_log(gtmsource_log_fp, TRUE, TRUE, "Connected to secondary, using TCP send buffer size %d receive buffer size %d\n", repl_max_send_buffsize, repl_max_recv_buffsize); repl_log_conn_info(gtmsource_sock_fd, gtmsource_log_fp, FALSE); /* re-determine compression level on the replication pipe after every connection establishment */ gtmsource_local->repl_zlib_cmp_level = repl_zlib_cmp_level = ZLIB_CMPLVL_NONE; /* reset any CMP2UNCMP messages received in prior connections. Once a connection encounters a REPL_CMP2UNCMP message * all further replication on that connection will be uncompressed. */ gtmsource_received_cmp2uncmp_msg = FALSE; return (SS_NORMAL); } int gtmsource_alloc_tcombuff(void) { /* Allocate buffer for TCOM, ZTCOM records */ if (NULL == gtmsource_tcombuff_start) { assert(NULL == gtmsource_tcombuff_start); gtmsource_tcombuff_start = (unsigned char *)malloc(gd_header->n_regions * TCOM_RECLEN); } return (SS_NORMAL); } void gtmsource_free_tcombuff(void) { if (NULL != gtmsource_tcombuff_start) { free(gtmsource_tcombuff_start); gtmsource_tcombuff_start = NULL; } return; } int gtmsource_alloc_filter_buff(int bufsiz) { unsigned char *old_filter_buff; if ((NO_FILTER != gtmsource_filter) && (bufsiz > repl_filter_bufsiz)) { REPL_DPRINT3("Expanding filter buff from %d to %d\n", repl_filter_bufsiz, bufsiz); old_filter_buff = repl_filter_buff; repl_filter_buff = (unsigned char *)malloc(bufsiz); if (NULL != old_filter_buff) { assert(NULL != old_filter_buff); memcpy(repl_filter_buff, old_filter_buff, repl_filter_bufsiz); free(old_filter_buff); } repl_filter_bufsiz = bufsiz; } return (SS_NORMAL); } void gtmsource_free_filter_buff(void) { if (NULL != repl_filter_buff) { assert(NULL != repl_filter_buff); free(repl_filter_buff); repl_filter_buff = NULL; repl_filter_bufsiz = 0; } } int gtmsource_alloc_msgbuff(int maxbuffsize, boolean_t discard_oldbuff) { /* Allocate message buffer */ repl_msg_ptr_t oldmsgp; assert(MIN_REPL_MSGLEN < maxbuffsize); if ((maxbuffsize > gtmsource_msgbufsiz) || (NULL == gtmsource_msgp)) { REPL_DPRINT3("Expanding message buff from %d to %d\n", gtmsource_msgbufsiz, maxbuffsize); oldmsgp = gtmsource_msgp; gtmsource_msgp = (repl_msg_ptr_t)malloc(maxbuffsize); if (NULL != oldmsgp) { /* Copy existing data */ if (!discard_oldbuff) memcpy((unsigned char *)gtmsource_msgp, (unsigned char *)oldmsgp, gtmsource_msgbufsiz); free(oldmsgp); } gtmsource_msgbufsiz = maxbuffsize; if (ZLIB_CMPLVL_NONE != gtm_zlib_cmp_level) { /* Compression is enabled. Allocate parallel buffers to hold compressed journal records. * Allocate extra space just in case compression actually expands the data (needed only in rare cases). */ oldmsgp = gtmsource_cmpmsgp; gtmsource_cmpmsgp = (repl_msg_ptr_t)malloc(maxbuffsize * MAX_CMP_EXPAND_FACTOR); if (NULL != oldmsgp) free(oldmsgp); gtmsource_cmpmsgbufsiz = (maxbuffsize * MAX_CMP_EXPAND_FACTOR); } gtmsource_alloc_filter_buff(gtmsource_msgbufsiz); } return (SS_NORMAL); } void gtmsource_free_msgbuff(void) { if (NULL != gtmsource_msgp) { free(gtmsource_msgp); gtmsource_msgp = NULL; gtmsource_msgbufsiz = 0; if (ZLIB_CMPLVL_NONE != gtm_zlib_cmp_level) { /* Compression is enabled. Free up compression buffer as well. */ assert(NULL != gtmsource_cmpmsgp); free(gtmsource_cmpmsgp); gtmsource_cmpmsgp = NULL; gtmsource_cmpmsgbufsiz = 0; } } } /* Receive starting jnl_seqno for (re)starting transmission */ int gtmsource_recv_restart(seq_num *recvd_jnl_seqno, int *msg_type, int *start_flags) { boolean_t msg_is_cross_endian, log_waitmsg; fd_set input_fds; repl_msg_t msg; repl_logfile_info_msg_t logfile_msg; unsigned char *msg_ptr; /* needed for REPL_{SEND,RECV}_LOOP */ int tosend_len, sent_len, sent_this_iter; /* needed for REPL_SEND_LOOP */ int torecv_len, recvd_len, recvd_this_iter; /* needed for REPL_RECV_LOOP */ int status, poll_dir; /* needed for REPL_{SEND,RECV}_LOOP */ uint4 remaining_len, len; unsigned char seq_num_str[32], *seq_num_ptr, *buffp; repl_msg_t xoff_ack; # ifdef DEBUG boolean_t remote_side_endianness_known; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; status = SS_NORMAL; assert(remote_side == &jnlpool->gtmsource_local->remote_side); DEBUG_ONLY(*msg_type = -1); for (log_waitmsg = TRUE; SS_NORMAL == status; ) { if (log_waitmsg) repl_log(gtmsource_log_fp, TRUE, TRUE, "Waiting for REPL_START_JNL_SEQNO or REPL_FETCH_RESYNC message\n"); REPL_RECV_LOOP(gtmsource_sock_fd, &msg, MIN_REPL_MSGLEN, REPL_POLL_WAIT) { gtmsource_poll_actions(FALSE); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) return (SS_NORMAL); } DEBUG_ONLY(remote_side_endianness_known = remote_side->endianness_known); if (SS_NORMAL != status) break; if (REPL_LOGFILE_INFO == msg.type) /* No need to endian convert as the receiver converts this to our native fmt */ { /* We received a REPL_START_JNL_SEQNO/REPL_FETCH_RESYNC and coming through the loop again * to receive REPL_LOGFILE_INFO. At this point, we should have already established the endianness * of the remote side and even if the remote side is of different endianness, we are going to interpret the * message without endian conversion because the Receiver Server, from REPL_PROTO_VER_SUPPLEMENTARY * onwards, always endian converts the message intended for the Source Server */ assert(remote_side->endianness_known); assert(REPL_PROTO_VER_REMOTE_LOGPATH <= remote_side->proto_ver); assert(-1 != *msg_type); buffp = (unsigned char *)&logfile_msg; /* First copy what we already received */ memcpy(buffp, &msg, MIN_REPL_MSGLEN); assert((logfile_msg.fullpath_len > 0) && (logfile_msg.fullpath_len < REPL_LOGFILE_PATH_MAX)); /* Now receive the rest of the message */ buffp += MIN_REPL_MSGLEN; remaining_len = msg.len - MIN_REPL_MSGLEN; REPL_RECV_LOOP(gtmsource_sock_fd, buffp, remaining_len, REPL_POLL_WAIT) { gtmsource_poll_actions(FALSE); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) return SS_NORMAL; } if (SS_NORMAL != status) return status; assert(REPL_PROTO_VER_REMOTE_LOGPATH <= logfile_msg.proto_ver); assert(logfile_msg.proto_ver == remote_side->proto_ver); assert('\0' == logfile_msg.fullpath[logfile_msg.fullpath_len - 1]); if (REPL_FETCH_RESYNC == *msg_type) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Remote side rollback path is %s; Rollback PID = %d\n", logfile_msg.fullpath, logfile_msg.pid); } else { assert(REPL_START_JNL_SEQNO == *msg_type); repl_log(gtmsource_log_fp, TRUE, TRUE, "Remote side receiver log file path is %s; " "Receiver Server PID = %d\n", logfile_msg.fullpath, logfile_msg.pid); } /* Now that we've received REPL_LOGFILE_INFO message from the other side, handshake is complete. */ return SS_NORMAL; } /* If endianness of other side is not yet known, determine that now by seeing if the msg.len is * greater than expected. If it is, convert it and see if it is now what we expect. If it is, * then the other system is of opposite endianness. Note: We would normally use msg.type since * it is effectively an enum and we control by adding new messages. But, REPL_START_JNL_SEQNO * is lucky number zero which means it is identical on systems of either endianness. * * If endianness of other side is not yet known, determine that from the message length as we * expect it to be MIN_REPL_MSGLEN. There is one exception though. If a pre-V55000 receiver sends * a REPL_XOFF_ACK_ME message, it could send it in the receiver's native-endian or cross-endian * format (depending on how its global variable "src_node_same_endianness" is initialized). This * bug in the receiver server logic is fixed V55000 onwards (proto_ver is REPL_PROTO_VER_SUPPLEMENTARY). * Therefore, in this case, we cannot use the endianness of the REPL_XOFF_ACK_ME message to determine * the endianness of the connection. In this case, wait for the next non-REPL_XOFF_ACK_ME message * to determine the connection endianness. Handle this exception case correctly. * * If on the other hand, we know the endianness of the other side, we still cannot guarantee which * endianness a REPL_XOFF_ACK_ME message is sent in (in pre-V55000 versions for example in V53004A where * it is sent in receiver native endian format whereas in V54002B it is sent in source native * endian format). So better be safe on the source side and handle those cases like we did when * we did not know the endianness of the remote side. * * The below check works as all messages we expect at this point have a fixed length of MIN_REPL_MSGLEN. */ msg_is_cross_endian = (((unsigned)MIN_REPL_MSGLEN < (unsigned)msg.len) && ((unsigned)MIN_REPL_MSGLEN == GTM_BYTESWAP_32((unsigned)msg.len))); if (msg_is_cross_endian) { msg.type = GTM_BYTESWAP_32(msg.type); msg.len = GTM_BYTESWAP_32(msg.len); } assert(msg.type == REPL_START_JNL_SEQNO || msg.type == REPL_FETCH_RESYNC || msg.type == REPL_XOFF_ACK_ME); assert(MIN_REPL_MSGLEN == msg.len); /* If we don't yet know the endianness of the other side and the input message is not a REPL_XOFF_ACK_ME * we can decide the endianness of the receiver side by the endianness of the input message. * REPL_XOFF_ACK_ME is an exception due to its handling by pre-V5500 versions (described in comments above). */ if (!remote_side->endianness_known && (REPL_XOFF_ACK_ME != msg.type)) { remote_side->endianness_known = TRUE; remote_side->cross_endian = msg_is_cross_endian; if (remote_side->cross_endian) repl_log(gtmsource_log_fp, TRUE, TRUE, "Source and Receiver sides have opposite " "endianness\n"); else repl_log(gtmsource_log_fp, TRUE, TRUE, "Source and Receiver sides have same endianness\n"); } /* We only expect REPL_START_JNL_SEQNO, REPL_LOGFILE_INFO and REPL_XOFF_ACK_ME messages to be sent once the * endianness of the remote side has been determined. We don't expect the REPL_FETCH_RESYNC message to be * ever sent in the middle of a handshake (i.e. after the remote side endianness has been determined). * Assert that. The logic that sets "msg_is_cross_endian" relies on this. If this assert fails, the logic * has to change. */ assert((REPL_FETCH_RESYNC != msg.type) || !remote_side_endianness_known); *msg_type = msg.type; *start_flags = START_FLAG_NONE; memcpy((uchar_ptr_t)recvd_jnl_seqno, (uchar_ptr_t)&msg.msg[0], SIZEOF(seq_num)); if (REPL_START_JNL_SEQNO == msg.type) { if (msg_is_cross_endian) *recvd_jnl_seqno = GTM_BYTESWAP_64(*recvd_jnl_seqno); repl_log(gtmsource_log_fp, TRUE, TRUE, "Received REPL_START_JNL_SEQNO message with SEQNO " "%llu [0x%llx]\n", INT8_PRINT(*recvd_jnl_seqno), INT8_PRINT(*recvd_jnl_seqno)); if (msg_is_cross_endian) ((repl_start_msg_ptr_t)&msg)->start_flags = GTM_BYTESWAP_32(((repl_start_msg_ptr_t)&msg)->start_flags); *start_flags = ((repl_start_msg_ptr_t)&msg)->start_flags; assert(!msg_is_cross_endian || (NODE_ENDIANNESS != ((repl_start_msg_ptr_t)&msg)->node_endianness)); if (*start_flags & START_FLAG_STOPSRCFILTER) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Start JNL_SEQNO msg tagged with STOP SOURCE FILTER\n"); if (gtmsource_filter & EXTERNAL_FILTER) { repl_stop_filter(); gtmsource_filter &= ~EXTERNAL_FILTER; } else repl_log(gtmsource_log_fp, TRUE, TRUE, "Filter is not active, ignoring STOP SOURCE FILTER msg\n"); } /* Determine the protocol version of the receiver side. That information is encoded in the * "proto_ver" field of the message from V51 onwards but to differentiate V50 vs V51 we need * to check if the START_FLAG_VERSION_INFO bit is set in start_flags. If not the protocol is V50. * V51 implies support for multi-site while V50 implies dual-site configuration only. */ if (*start_flags & START_FLAG_VERSION_INFO) { assert(REPL_PROTO_VER_DUALSITE != ((repl_start_msg_ptr_t)&msg)->proto_ver); remote_side->is_supplementary = ((repl_start_msg_ptr_t)&msg)->is_supplementary; remote_side->proto_ver = ((repl_start_msg_ptr_t)&msg)->proto_ver; } else { /* Issue REPL2OLD error because receiver is dual-site */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPL2OLD, 4, LEN_AND_STR(UNKNOWN_INSTNAME), LEN_AND_STR(jnlpool->repl_inst_filehdr->inst_info.this_instname)); } assert(*start_flags & START_FLAG_HASINFO); /* V4.2+ versions have jnl ver in the start msg */ remote_side->jnl_ver = ((repl_start_msg_ptr_t)&msg)->jnl_ver; REPL_DPRINT3("Local jnl ver is octal %o, remote jnl ver is octal %o\n", this_side->jnl_ver, remote_side->jnl_ver); repl_check_jnlver_compat(!remote_side->cross_endian); assert(remote_side->jnl_ver > V15_JNL_VER || 0 == (*start_flags & START_FLAG_COLL_M)); if (remote_side->jnl_ver <= V15_JNL_VER) *start_flags &= ~START_FLAG_COLL_M; /* zap it for pro, just in case */ remote_side->is_std_null_coll = (*start_flags & START_FLAG_COLL_M) ? TRUE : FALSE; assert((remote_side->jnl_ver >= V19_JNL_VER) || (0 == (*start_flags & START_FLAG_TRIGGER_SUPPORT))); if (remote_side->jnl_ver < V19_JNL_VER) *start_flags &= ~START_FLAG_TRIGGER_SUPPORT; /* zap it for pro, just in case */ remote_side->trigger_supported = (*start_flags & START_FLAG_TRIGGER_SUPPORT) ? TRUE : FALSE; # ifdef GTM_TRIGGER if (!remote_side->trigger_supported) repl_log(gtmsource_log_fp, TRUE, TRUE, "Warning : Secondary does not support GT.M " "database triggers. #t updates on primary will not be replicated\n"); # endif /* remote_side->null_subs_xform is initialized later in function "gtmsource_process" */ (TREF(replgbl)).trig_replic_warning_issued = FALSE; remote_side->tls_requested = (*start_flags & START_FLAG_ENABLE_TLS) ? TRUE : FALSE; if (REPL_PROTO_VER_REMOTE_LOGPATH > remote_side->proto_ver) return SS_NORMAL; /* Remote side doesn't support REPL_LOGFILE_INFO message */ SEND_REPL_LOGFILE_INFO(jnlpool->gtmsource_local->log_file, logfile_msg); log_waitmsg = FALSE; } else if (REPL_FETCH_RESYNC == msg.type) { /* Determine the protocol version of the receiver side. * Take care to set the flush parameter in repl_log calls below to FALSE until at least the * first message gets sent back. This is so the fetchresync rollback on the other side does * not timeout before receiving a response. */ remote_side->proto_ver = (RECAST(repl_resync_msg_ptr_t)&msg)->proto_ver; remote_side->is_supplementary = (RECAST(repl_resync_msg_ptr_t)&msg)->is_supplementary; /* The following fields don't need to be initialized since they are needed (for internal filter * transformations) only if we send journal records across. REPL_FETCH_RESYNC causes only * protocol messages to be exchanged (no journal records). * remote_side->jnl_ver = ... * remote_side->is_std_null_coll = ... * remote_side->trigger_supported = ... * remote_side->null_subs_xform = ... */ if (msg_is_cross_endian) *recvd_jnl_seqno = GTM_BYTESWAP_64(*recvd_jnl_seqno); repl_log(gtmsource_log_fp, TRUE, FALSE, "Received REPL_FETCH_RESYNC message with SEQNO " "%llu [0x%llx]\n", INT8_PRINT(*recvd_jnl_seqno), INT8_PRINT(*recvd_jnl_seqno)); if (REPL_PROTO_VER_REMOTE_LOGPATH > remote_side->proto_ver) return SS_NORMAL; /* Remote side doesn't support REPL_LOGFILE_INFO message */ SEND_REPL_LOGFILE_INFO(jnlpool->gtmsource_local->log_file, logfile_msg); log_waitmsg = FALSE; } else if (REPL_XOFF_ACK_ME == msg.type) { repl_log(gtmsource_log_fp, TRUE, FALSE, "Received REPL_XOFF_ACK_ME message. " "Possible crash/shutdown of update process\n"); /* Send XOFF_ACK */ xoff_ack.type = REPL_XOFF_ACK; if (msg_is_cross_endian) *recvd_jnl_seqno = GTM_BYTESWAP_64(*recvd_jnl_seqno); memcpy((uchar_ptr_t)&xoff_ack.msg[0], (uchar_ptr_t)recvd_jnl_seqno, SIZEOF(seq_num)); xoff_ack.len = MIN_REPL_MSGLEN; repl_log(gtmsource_log_fp, TRUE, TRUE, "Sending REPL_XOFF_ACK message\n"); REPL_SEND_LOOP(gtmsource_sock_fd, &xoff_ack, xoff_ack.len, REPL_POLL_NOWAIT) { gtmsource_poll_actions(FALSE); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) return (SS_NORMAL); } log_waitmsg = TRUE; /* Wait for REPL_START_JNL_SEQNO or REPL_FETCH_RESYNC */ } else { /* If unknown message is received, close connection. Caller will reopen the same. */ repl_log(gtmsource_log_fp, TRUE, TRUE, "Received UNKNOWN message (type = %d). " "Closing connection.\n", msg.type); assert(FALSE); repl_close(>msource_sock_fd); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); gtmsource_state = jnlpool->gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; return (SS_NORMAL); } } return (status); } int gtmsource_srch_restart(seq_num recvd_jnl_seqno, int recvd_start_flags) { seq_num cur_read_jnl_seqno; qw_off_t cur_read_addr; gtm_uint64_t cur_read, prev_read, prev_tr_size, jnlpool_size; int save_lastwrite_len; unsigned char seq_num_str[32], *seq_num_ptr; jnlpool_ctl_ptr_t jctl; gtmsource_local_ptr_t gtmsource_local; gd_region *reg, *region_top; sgmnt_addrs *csa, *repl_csa; jctl = jnlpool->jnlpool_ctl; jnlpool_size = jctl->jnlpool_size; gtmsource_local = jnlpool->gtmsource_local; assert(recvd_jnl_seqno <= jctl->jnl_seqno); cur_read_jnl_seqno = gtmsource_local->read_jnl_seqno; if (recvd_jnl_seqno > cur_read_jnl_seqno) { /* The secondary is requesting a seqno higher than what we last remember having sent but yet it is in sync with us * upto seqno "recvd_jnl_seqno" as otherwise the caller would have determined it is out of sync and not even call * us. To illustrate an example of how this could happen, consider an INSTA->INSTB replication and INSTA->INSTC * replication going on. Lets say INSTA's journal sequence number is at 100. INSTB is at 60 and INSTC is at 30. * This means, last sequence number sent by INSTA to INSTB is 60 and to INSTC is 30. Now, lets say INSTA is shutdown * and INSTB comes up as the primary to INSTC and starts replicating the 30 updates thereby bringing both INSTB and * INSTC sequence number to 60. Now, if INSTA comes backup again as primary against INSTC, we will have a case where * gtmsource_local->read_jnl_seqno as 30, but recvd_jnl_seqno as 60. This means that we are going to bump * "gtmsource_local->read_jnl_seqno" up to the received seqno (in the later call to "gtmsource_flush_fh") without * knowing how many bytes of transaction data that meant (to correspondingly bump up "gtmsource_local->read_addr"). * The only case where we don't care to maintain "read_addr" is if we are in READ_FILE mode AND the current * read_jnl_seqno and the received seqno is both lesser than or equal to "gtmsource_save_read_jnl_seqno". Except * for that case, we need to reset "gtmsource_save_read_jnl_seqno" to correspond to the current jnl seqno. */ if ((READ_FILE != gtmsource_local->read_state) || (recvd_jnl_seqno > gtmsource_save_read_jnl_seqno)) { grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); gtmsource_local->read_state = READ_FILE; gtmsource_save_read_jnl_seqno = jctl->jnl_seqno; GTMSOURCE_SET_READ_ADDR(gtmsource_local, jnlpool); rel_lock(jnlpool->jnlpool_dummy_reg); } } else if (READ_POOL == gtmsource_local->read_state) { /* Follow the back-chain in the Journal Pool to find whether or not recvd_jnl_seqno is in the Pool */ /* The implementation for searching through the back chain has several inefficiences. We are deferring addressing * them to keep code changes for V4.4-003 to a minimum. We should address these in an upcoming release. * Vinaya 2003, Oct 02 */ QWASSIGN(cur_read_addr, gtmsource_local->read_addr); cur_read = gtmsource_local->read; if (jnlpool_hasnt_overflowed(jctl, jnlpool_size, cur_read_addr) && QWGT(cur_read_jnl_seqno, recvd_jnl_seqno) && QWGT(cur_read_jnl_seqno, jctl->start_jnl_seqno)) { if (QWGE(jctl->rsrv_write_addr, cur_read_addr)) { /* If there is no more input to be read, the previous transaction size should not be read from the * journal pool since the read pointers point to the next read. In such a case, we can find the * size of the transcation cur_read_jnl_seqno from jctl->lastwrite_len. We should access * lastwrite_len after a memory barrier to avoid reading a stale value. We rely on the memory * barrier done in jnlpool_hasnt_overflowed */ save_lastwrite_len = jctl->lastwrite_len; if (QWEQ(jctl->rsrv_write_addr, cur_read_addr)) { /* GT.M is not writing any transaction, safe to rely on jctl->lastwrite_len. Note, * GT.M could not have been writing transaction cur_read_jnl_seqno if we are here. Also, * lastwrite_len cannot be in the future w.r.t rsrv_write_addr because of the memory * barriers we do in t{p}_{t}end.c. It can be behind by atmost one transaction * (cur_read_jnl_seqno). Well, we want the length of transaction cur_read_jnl_seqno, * not cur_read_jnl_seqno + 1. */ QWDECRBYDW(cur_read_addr, save_lastwrite_len); QWDECRBYDW(cur_read_jnl_seqno, 1); prev_read = cur_read; if (cur_read > save_lastwrite_len) cur_read -= save_lastwrite_len; else { cur_read = cur_read - (save_lastwrite_len % jnlpool_size); if (cur_read >= prev_read) cur_read += jnlpool_size; } assert(cur_read == QWMODDW(cur_read_addr, jnlpool_size)); REPL_DPRINT2("Srch restart : No more input in jnlpool, backing off to read_jnl_seqno : " INT8_FMT, INT8_PRINT(cur_read_jnl_seqno)); REPL_DPRINT3(" read_addr : "INT8_FMT" read : %ld\n", INT8_PRINT(cur_read_addr), cur_read); } } if (QWEQ(cur_read_addr, jctl->write_addr)) { /* Check if there are any pending phase2 commits that can be cleaned up. * That will bring jctl->write_addr more uptodate. And then redo the read_addr/write_addr check. */ if (jctl->write_addr != jctl->rsrv_write_addr) repl_phase2_cleanup(jnlpool); if (QWEQ(cur_read_addr, jctl->write_addr)) { /* We caught a GTM process writing cur_read_jnl_seqno + 1, * we cannot rely on lastwrite_len as it may or may not have changed. * Wait until the GTM process finishes writing this transaction. */ repl_log(gtmsource_log_fp, TRUE, FALSE, "SEARCHING RESYNC POINT IN POOL : " "Waiting for GTM process to finish writing journal records to the pool\n"); while (QWEQ(cur_read_addr, jctl->write_addr)) { SHORT_SLEEP(GTMSOURCE_WAIT_FOR_JNL_RECS); gtmsource_poll_actions(FALSE); /* Check if there are any pending phase2 commits that can be cleaned up. * That will bring jctl->write_addr more uptodate. */ if (jctl->write_addr != jctl->rsrv_write_addr) repl_phase2_cleanup(jnlpool); } repl_log(gtmsource_log_fp, TRUE, FALSE, "SEARCHING RESYNC POINT IN POOL : " "GTM process finished writing journal records to the pool\n"); } } } while (jnlpool_hasnt_overflowed(jctl, jnlpool_size, cur_read_addr) && QWGT(cur_read_jnl_seqno, recvd_jnl_seqno) && QWGT(cur_read_jnl_seqno, jctl->start_jnl_seqno)) { assert(cur_read + SIZEOF(jnldata_hdr_struct) <= jnlpool_size); prev_tr_size = ((jnldata_hdr_ptr_t)(jnlpool->jnldata_base + cur_read))->prev_jnldata_len; if ((prev_tr_size <= cur_read_addr) && jnlpool_hasnt_overflowed(jctl, jnlpool_size, cur_read_addr - prev_tr_size)) { QWDECRBYDW(cur_read_addr, prev_tr_size); prev_read = cur_read; cur_read -= prev_tr_size; if (cur_read >= prev_read) cur_read += jnlpool_size; assert(cur_read == QWMODDW(cur_read_addr, jnlpool_size)); QWDECRBYDW(cur_read_jnl_seqno, 1); REPL_DPRINT2("Srch restart : No overflow yet, backing off to read_jnl_seqno : "INT8_FMT, INT8_PRINT(cur_read_jnl_seqno)); REPL_DPRINT3(" read_addr : "INT8_FMT" read : %ld\n", INT8_PRINT(cur_read_addr), cur_read); continue; } break; } QWASSIGN(gtmsource_local->read_addr, cur_read_addr); gtmsource_local->read = cur_read; if (jnlpool_hasnt_overflowed(jctl, jnlpool_size, cur_read_addr) && QWEQ(cur_read_jnl_seqno, recvd_jnl_seqno) && QWGE(cur_read_jnl_seqno, jctl->start_jnl_seqno)) { REPL_DPRINT2("Srch restart : Now in READ_POOL state read_jnl_seqno : "INT8_FMT, INT8_PRINT(cur_read_jnl_seqno)); REPL_DPRINT3(" read_addr : "INT8_FMT" read : %ld\n",INT8_PRINT(cur_read_addr), cur_read); } else { /* Overflow, or requested seqno too far back to be in pool */ REPL_DPRINT2("Srch restart : Now in READ_FILE state. Changing sync point to read_jnl_seqno : "INT8_FMT, INT8_PRINT(cur_read_jnl_seqno)); REPL_DPRINT3(" read_addr : "INT8_FMT" read : %ld ", INT8_PRINT(cur_read_addr), cur_read); REPL_DPRINT2("save_read_jnl_seqno : "INT8_FMT"\n", INT8_PRINT(gtmsource_save_read_jnl_seqno)); QWASSIGN(gtmsource_save_read_jnl_seqno, cur_read_jnl_seqno); if (QWLT(gtmsource_save_read_jnl_seqno, jctl->start_jnl_seqno)) { QWASSIGN(gtmsource_save_read_jnl_seqno, jctl->start_jnl_seqno); assert(QWEQ(gtmsource_local->read_addr, seq_num_zero)); assert(gtmsource_local->read == 0); /* For pro version, force zero assignment */ QWASSIGN(gtmsource_local->read_addr, seq_num_zero); gtmsource_local->read = 0; REPL_DPRINT2("Srch restart : Sync point "INT8_FMT, INT8_PRINT(gtmsource_save_read_jnl_seqno)); REPL_DPRINT2(" beyond start_seqno : "INT8_FMT, INT8_PRINT(jctl->start_jnl_seqno)); REPL_DPRINT3(", sync point set to read_addr : "INT8_FMT" read : %d\n", INT8_PRINT(gtmsource_local->read_addr), gtmsource_local->read); } gtmsource_local->read_state = READ_FILE; repl_log(gtmsource_log_fp, TRUE, FALSE, "Source server now reading from journal files; journal pool " "does not contain transaction %llu [0x%llx]\n", recvd_jnl_seqno, recvd_jnl_seqno); gtmsource_pool2file_transition = TRUE; } } else /* read_state is READ_FILE and requesting a sequence number less than or equal to read_jnl_seqno */ { assert(cur_read_jnl_seqno == gtmsource_local->read_jnl_seqno); if (cur_read_jnl_seqno > gtmsource_save_read_jnl_seqno) gtmsource_save_read_jnl_seqno = cur_read_jnl_seqno; REPL_DPRINT2("Srch restart : Continuing in READ_FILE state. Retaining sync point for read_jnl_seqno : "INT8_FMT, INT8_PRINT(cur_read_jnl_seqno)); REPL_DPRINT2(" at read_addr : "INT8_FMT, INT8_PRINT(gtmsource_local->read_addr)); REPL_DPRINT3(" read : %d corresponding to save_read_jnl_seqno : "INT8_FMT"\n", gtmsource_local->read, INT8_PRINT(gtmsource_save_read_jnl_seqno)); repl_log(gtmsource_log_fp, TRUE, FALSE, "Source server continuing to read from journal files at seqno " "%llu [0x%llx]\n", recvd_jnl_seqno, recvd_jnl_seqno); } REPL_DPRINT2("Setting resync_seqno to "INT8_FMT"\n", INT8_PRINT(recvd_jnl_seqno)); repl_log(gtmsource_log_fp, TRUE, FALSE, "Source server last sent seqno %llu [0x%llx]\n", cur_read_jnl_seqno, cur_read_jnl_seqno); repl_log(gtmsource_log_fp, TRUE, FALSE, "Source server will start sending from seqno %llu [0x%llx]\n", recvd_jnl_seqno, recvd_jnl_seqno); if (READ_POOL == gtmsource_local->read_state) repl_log(gtmsource_log_fp, TRUE, TRUE, "Source server now reading from journal pool\n"); else repl_log(gtmsource_log_fp, TRUE, TRUE, "Source server now reading from journal files\n"); /* Finally set "gtmsource_local->read_jnl_seqno" to be "recvd_jnl_seqno" and flush changes to instance file on disk */ gtmsource_flush_fh(recvd_jnl_seqno); assert(GTMSOURCE_HANDLE_ONLN_RLBK != gtmsource_state); return (SS_NORMAL); } int gtmsource_get_jnlrecs(uchar_ptr_t buff, int *data_len, int maxbufflen, boolean_t read_multiple) { int total_tr_len; unsigned char seq_num_str[32], *seq_num_ptr; jnlpool_ctl_ptr_t jctl; gtmsource_local_ptr_t gtmsource_local; seq_num jnl_seqno, read_jnl_seqno; qw_num write_addr, read_addr; gtmsource_state_t gtmsource_state_sav; int index1; # ifdef DEBUG DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # endif jctl = jnlpool->jnlpool_ctl; gtmsource_local = jnlpool->gtmsource_local; jnl_seqno = jctl->jnl_seqno; read_jnl_seqno = gtmsource_local->read_jnl_seqno; read_addr = gtmsource_local->read_addr; GTMDBGFLAGS_NOFREQ_ONLY(GTMSOURCE_FORCE_READ_FILE_MODE, gtmsource_local->read_state = READ_FILE); switch(gtmsource_local->read_state) { case READ_POOL: /* Check if there are any pending phase2 commits that can be cleaned up. * That will bring jctl->write_addr more uptodate. */ index1 = jctl->phase2_commit_index1; assert((0 <= index1) && (JPL_PHASE2_COMMIT_ARRAY_SIZE > index1)); if ((index1 != jctl->phase2_commit_index2) && jctl->phase2_commit_array[index1].write_complete && (LOCK_AVAILABLE == jctl->phase2_commit_latch.u.parts.latch_pid)) repl_phase2_cleanup(jnlpool); /* Now that write_addr is uptodate, go ahead and read jnl records */ write_addr = jctl->write_addr; assert((0 != write_addr) || (read_jnl_seqno <= jctl->start_jnl_seqno)); assert(read_addr <= write_addr); if (read_addr == write_addr) { /* Nothing to read. While reading pool, the comparison of read_addr against write_addr is * the only reliable indicator if there are any transactions to be read. This is due to * the placement of memory barriers in t_end/tp_tend.c. Also, since we do not issue memory * barrier here, we may be reading a stale value of write_addr in which case we may conclude * that there is nothing to read. But, this will not continue forever as the source server * eventually (decided by architecture's implementation) will see the change to write_addr. */ *data_len = 0; return (0); } total_tr_len = gtmsource_readpool(buff, data_len, maxbufflen, read_multiple, write_addr); if (GTMSOURCE_SEND_NEW_HISTINFO == gtmsource_state) return (0); /* need to send REPL_HISTREC message before sending any more seqnos */ if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) return (0); /* Connection got reset in call to "gtmsource_readpool" */ if (0 < total_tr_len) { /* Found the entire seqno in the jnlpool. Check if the first journal record of this seqno * is of type JRT_BAD. This indicates a ERR_JNLPOOLRECOVERY situation (see * JPL_PHASE2_WRITE_COMPLETE macro for details). If so, switch to reading from files. */ assert(total_tr_len >= (SIZEOF(jnldata_hdr_struct) + SIZEOF(jrec_prefix))); if (JRT_BAD != ((jrec_prefix *)buff)->jrec_type) return (total_tr_len); assert((0 != TREF(gtm_test_jnlpool_sync)) && (0 == (read_jnl_seqno % TREF(gtm_test_jnlpool_sync)))); *data_len = -1; /* so we do fall through to READ_FILE code below */ } if (0 < *data_len) return (-1); /* Overflow, switch to READ_FILE */ gtmsource_local->read_state = READ_FILE; QWASSIGN(gtmsource_save_read_jnl_seqno, read_jnl_seqno); gtmsource_pool2file_transition = TRUE; /* so that we read the latest gener jnl files */ repl_log(gtmsource_log_fp, TRUE, TRUE, "Source server now reading from journal files; journal pool " "overflow detected at seqno %llu [0x%llx]\n", gtmsource_save_read_jnl_seqno, gtmsource_save_read_jnl_seqno); /* CAUTION : FALL THROUGH */ case READ_FILE: /* Note that while reading from journal files, it is possible the source server sees the journal records * for a transaction in the journal files BEFORE the transaction is marked as complete in the journal * pool (because FINISH_JNL_PHASE2_IN_JNLPOOL_IF_NEEDED is called AFTER FINISH_JNL_PHASE2_IN_JNLBUFF * in NONTP_FINISH_JNL_PHASE2_IN_JNLBUFF_AND_JNLPOOL and TP_FINISH_JNL_PHASE2_IN_JNLBUFF_AND_JNLPOOL). * Therefore it is possible read_addr (which is derived from the journal file records) could be greater * than write_addr (which is derived from the journal pool). And so "assert(read_addr <= write_addr)" * cannot be done here like is done for the READ_POOL case. */ if (read_jnl_seqno >= jnl_seqno) { /* Nothing to read. While reading from files, source server does not use write_addr to decide * how much to read. Also, it is possible that read_addr and write_addr are the same if the * source server came up after a crash and syncs with the latest state in jnlpool (see * gtmsource()). These reasons preclude the comparison of read_addr and write_addr (as we did for * READ_POOL case) to decide whether there are any unsent transactions. We use jnl_seqno instead. * Note though, the source server's view of jnl_seqno may be stale, and we may conclude that * we don't have anything to read as we do not do memory barrier here to fetch the latest * value of jnl_seqno. But, this will not continue forever as the source server eventually * (decided by architecture's implementation) will see the change to jnl_seqno. * * On systems that allow unordered memory access, it is possible that the value of jnl_seqno * as seen by source server is in the past compared to read_jnl_seqno - source server in * keeping up with updaters reads (from pool) and sends upto write_addr, the last transaction * of which could be jnl_seqno + 1. To cover the case, we use >= in the above comparison. * Given this, we may return with "nothing to read" even though we fell through from the * READ_POOL case. */ *data_len = 0; return 0; } if (gtmsource_pool2file_transition /* read_pool -> read_file transition */ || NULL == repl_ctl_list) /* files not opened */ { /* Close all the file read related structures and start afresh. The idea here is that most of the * file read info might be stale 'cos there is usually a long gap between pool to file read * transitions (in production environments). So, start afresh with the latest generation journal * files. This will also prevent opening previous generations that may not be required. */ REPL_DPRINT1("Pool to File read transition. Closing all the stale file read info and starting " "afresh\n"); gtmsource_ctl_close(); gtmsource_ctl_init(); gtmsource_pool2file_transition = FALSE; } GTMSOURCE_SAVE_STATE(gtmsource_state_sav); total_tr_len = gtmsource_readfiles(buff, data_len, maxbufflen, read_multiple); if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) return (0); /* Control message triggered mode change while reading files */ if (GTMSOURCE_SEND_NEW_HISTINFO == gtmsource_state) return (0); /* need to send REPL_HISTREC message before sending any more seqnos */ if (0 < total_tr_len) return (total_tr_len); assertpro(0 < *data_len); return (-1); } return (-1); /* This should never get executed, added to make compiler happy */ } /* This function can be used to only send fixed-size message types across the replication pipe. * This in turn uses REPL_SEND* macros but also does error checks and sets the global variable "gtmsource_state" accordingly. * * msg = Pointer to the message buffer to send * msgtypestr = Message name as a string to display meaningful error messages * optional_seqno = Optional seqno that needs to be printed along with the message name */ void gtmsource_repl_send(repl_msg_ptr_t msg, char *msgtypestr, seq_num optional_seqno, int4 optional_strm_num) { unsigned char *msg_ptr; /* needed for REPL_SEND_LOOP */ int tosend_len, sent_len, sent_this_iter; /* needed for REPL_SEND_LOOP */ int status, poll_dir; /* needed for REPL_{SEND,RECV}_LOOP */ char err_string[1024]; boolean_t close_retry = FALSE; assert((REPL_MULTISITE_MSG_START > msg->type) || (REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver)); if (MAX_SEQNO != optional_seqno) { if (INVALID_SUPPL_STRM == optional_strm_num) { if (REPL_ROLLBACK_FIRST != msg->type) { repl_log(gtmsource_log_fp, TRUE, FALSE, "Sending %s message with seqno %llu [0x%llx]\n",msgtypestr, optional_seqno, optional_seqno); } else if (REPL_ROLLBACK_FIRST == msg->type) { REPL_DPRINT1("Received REPL_ROLLBACK_FIRST message"); } } else repl_log(gtmsource_log_fp, TRUE, FALSE, "Sending %s message with seqno %llu [0x%llx] for Stream # %2d\n", msgtypestr, optional_seqno, optional_seqno, optional_strm_num); } else repl_log(gtmsource_log_fp, TRUE, FALSE, "Sending %s message\n", msgtypestr); REPL_SEND_LOOP(gtmsource_sock_fd, msg, msg->len, REPL_POLL_NOWAIT) { gtmsource_poll_actions(FALSE); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state)) return; } /* Check for error status from the REPL_SEND */ if (SS_NORMAL != status) { if (EREPL_SEND == repl_errno) { if (REPL_CONN_RESET(status)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Connection reset while sending %s. Status = %d ; %s\n", msgtypestr, status, STRERROR(status)); repl_log_conn_info(gtmsource_sock_fd, gtmsource_log_fp, TRUE); if (WBTEST_ENABLED(WBTEST_REPLCOMM_SEND_SRC)) gtm_wbox_input_test_case_count = 6; /*Do not got into white box case again */ close_retry = TRUE; } if (close_retry) { repl_close(>msource_sock_fd); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); gtmsource_state = jnlpool->gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; return; } else { SNPRINTF(err_string, SIZEOF(err_string), "Error sending %s message. " "Error in send : %s", msgtypestr, STRERROR(status)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_STR(err_string)); } } if (EREPL_SELECT == repl_errno) { SNPRINTF(err_string, SIZEOF(err_string), "Error sending %s message. " "Error in select : %s", msgtypestr, STRERROR(status)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_STR(err_string)); } } } /* This function can be used to only receive fixed-size message types across the replication pipe. * This in turn uses REPL_RECV* macros but also does error checks and sets the global variable "gtmsource_state" accordingly. * While waiting for the input message type, this function also recognizes * a) a REPL_XOFF_ACK_ME message in which case invokes "gtmsource_repl_send" to send a REPL_XOFF_ACK message type. * b) a REPL_XOFF or REPL_XON message in which case ignores them as we are still in the initial handshake stage * and these messages from the receiver are not relevant. * * msg = Pointer to the message buffer where received message will be stored * msglen = Length of the message to receive * msgtype = Expected type of the message (if received message is not of this type, the connection is closed) * msgtypestr = Message name as a string to display meaningful error messages */ static boolean_t gtmsource_repl_recv(repl_msg_ptr_t msg, int4 msglen, int4 msgtype, char *msgtypestr) { unsigned char *msg_ptr; /* needed for REPL_RECV_LOOP */ int torecv_len, recvd_len, recvd_this_iter; /* needed for REPL_RECV_LOOP */ int status, poll_dir; /* needed for REPL_{SEND,RECV}_LOOP */ repl_msg_t xoff_ack; char err_string[1024]; unsigned char *buff; int4 bufflen; repl_log(gtmsource_log_fp, TRUE, FALSE, "Waiting for %s message\n", msgtypestr); assert((REPL_XOFF != msgtype) && (REPL_XON != msgtype) && (REPL_XOFF_ACK_ME != msgtype)); buff = (unsigned char *)msg; bufflen = msglen; do { /* Note that "bufflen" could potentially be > 32-byte (MIN_REPL_MSGLEN) the length of most replication * messages, in case we are expecting to receive a REPL_CMP_SOLVE message. In that case, it is possible * that while waiting for the 512 byte or so REPL_CMP_SOLVE message, we get a 32-byte REPL_XOFF_ACK_ME * message. In this case, we should break out of the REPL_RECV_LOOP as otherwise we would be eternally * waiting for a never-to-come REPL_CMP_SOLVE message. This is in fact a general issue with any REPL_RECV_LOOP * code that passes a 3rd parameter > MIN_REPL_MSGLEN. All such usages need a check inside the body of the * loop to account for a REPL_XOFF_ACK_ME and if so break. */ REPL_RECV_LOOP(gtmsource_sock_fd, buff, bufflen, REPL_POLL_WAIT) { gtmsource_poll_actions(TRUE); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state)) return FALSE; /* Check if we received an XOFF_ACK_ME message completely. If yes, we can safely break out of the * loop without receiving the originally intended message (as the receiver is going to drain away all * the stuff in the replication pipe anyways and reinitiate a fresh handshake). This way we don't hang * eternally waiting for a never-arriving originally intended message. */ if ((MIN_REPL_MSGLEN <= recvd_len) && (REPL_XOFF_ACK_ME == msg->type)) break; } if (SS_NORMAL != status) { if (EREPL_RECV == repl_errno) { if (REPL_CONN_RESET(status)) { /* Connection reset */ repl_log(gtmsource_log_fp, TRUE, TRUE, "Connection reset while attempting to receive %s message. Status = %d ; %s\n", msgtypestr, status, STRERROR(status)); repl_close(>msource_sock_fd); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); gtmsource_state = jnlpool->gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; return FALSE; } else { SNPRINTF(err_string, SIZEOF(err_string), "Error receiving %s message from Receiver. Error in recv : %s", msgtypestr, STRERROR(status)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_STR(err_string)); } } else if (EREPL_SELECT == repl_errno) { SNPRINTF(err_string, SIZEOF(err_string), "Error receiving %s message from Receiver. Error in select : %s", msgtypestr, STRERROR(status)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_STR(err_string)); } } assert(SS_NORMAL == status); if ((REPL_XON != msg->type) && (REPL_XOFF != msg->type)) break; /* Strip the "msg" buffer of the XON or XOFF message and redo the loop until "msglen" bytes of the expected * msgtype are obtained. If "msglen" matches the XON or XOFF message length, then we can discard the ENTIRE message * and redo the loop without having to strip a "partial" msg buffer. If "msglen" is LESSER than a complete XON or * XOFF message, then we have to finish reading the entire XON/XOFF message and then redo the loop. But we * expect all callers to set "msglen" to at least the XON/XOFF message length. Assert accordingly. * Note that the below logic works even if more than one XON/XOFF messages get sent before the expected message. * For every XON/XOFF message, we will do one memmove and go back in the loop to read MIN_REPL_MSGLEN-more bytes. */ assert(MIN_REPL_MSGLEN == msg->len); assert(MIN_REPL_MSGLEN <= msglen); bufflen = msg->len; if (msglen != bufflen) { memmove(msg, (uchar_ptr_t)msg + bufflen, msglen - bufflen); buff = (uchar_ptr_t)msg + msglen - bufflen; } } while (TRUE); /* Check if message received is indeed of expected type */ assert(remote_side->endianness_known); /* so we ensure msg->type we read below is accurate and not cross-endian format */ if (REPL_XOFF_ACK_ME == msg->type) { /* Send XOFF_ACK. Anything sent before this in the replication pipe will be drained and therefore * return to the caller so it can reissue the message exchange sequence right from the beginning. */ repl_log(gtmsource_log_fp, TRUE, FALSE, "Received REPL_XOFF_ACK_ME message\n", msgtypestr); xoff_ack.type = REPL_XOFF_ACK; memcpy((uchar_ptr_t)&xoff_ack.msg[0], (uchar_ptr_t)>msource_msgp->msg[0], SIZEOF(seq_num)); xoff_ack.len = MIN_REPL_MSGLEN; gtmsource_repl_send((repl_msg_ptr_t)&xoff_ack, "REPL_XOFF_ACK", MAX_SEQNO, INVALID_SUPPL_STRM); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state)) return FALSE; /* "gtmsource_repl_send" did not complete */ gtmsource_state = jnlpool->gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_RESTART; return FALSE; } else if (msgtype != msg->type) { repl_log(gtmsource_log_fp, TRUE, FALSE, "UNKNOWN msg (type = %d) received when waiting for msg (type = %d)" ". Closing connection.\n", msg->type, msgtype); repl_close(>msource_sock_fd); assert(FALSE); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); gtmsource_state = jnlpool->gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; return FALSE; } else { repl_log(gtmsource_log_fp, TRUE, FALSE, "Received %s message\n", msgtypestr); return TRUE; } } /* Given that the source server was started with compression enabled, this function checks if the receiver server was * also started with the decompression enabled and if so sends a compressed test message. The receiver server responds back * whether it is successfully able to decompress that or not. If yes, compression is enabled on the replication pipe and * the input parameter "*repl_zlib_cmp_level_ptr" is set to the compression level used. */ boolean_t gtmsource_get_cmp_info(int4 *repl_zlib_cmp_level_ptr) { repl_cmpinfo_msg_t test_msg, solve_msg; char inputdata[REPL_MSG_CMPDATALEN], cmpbuf[REPL_MSG_CMPEXPDATALEN]; int index, cmpret, start; boolean_t cmpfail; uLongf cmplen; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(gtm_zlib_cmp_level); /*************** Send REPL_CMP_TEST message ***************/ memset(&test_msg, 0, SIZEOF(test_msg)); test_msg.type = REPL_CMP_TEST; test_msg.len = REPL_MSG_CMPINFOLEN; test_msg.proto_ver = REPL_PROTO_VER_THIS; /* Fill in test data with random data. The data will be a sequence of bytes from 0 to 255. The start point though * is randomly chosen using the process_id. If it is 253, the resulting sequence would be 253, 254, 255, 0, 1, 2, ... */ for (start = (process_id & REPL_MSG_CMPDATAMASK), index = 0; index < REPL_MSG_CMPDATALEN; index++) inputdata[index] = (start + index) % REPL_MSG_CMPDATALEN; /* Compress the data */ cmplen = SIZEOF(cmpbuf); /* initialize it to the available compressed buffer space */ ZLIB_COMPRESS(&cmpbuf[0], cmplen, inputdata, REPL_MSG_CMPDATALEN, gtm_zlib_cmp_level, cmpret); switch(cmpret) { case Z_MEM_ERROR: assert(FALSE); repl_log(gtmsource_log_fp, TRUE, FALSE, "Out-of-memory; Error from zlib compress function before sending REPL_CMP_TEST message\n"); break; case Z_BUF_ERROR: assert(FALSE); repl_log(gtmsource_log_fp, TRUE, FALSE, "Insufficient output buffer; Error from zlib compress " "function before sending REPL_CMP_TEST message\n"); break; case Z_STREAM_ERROR: /* level was incorrectly specified. Default to NO compression. */ repl_log(gtmsource_log_fp, TRUE, FALSE, "Compression level %d invalid; Error from compress function" " before sending REPL_CMP_TEST message\n", gtm_zlib_cmp_level); break; } if (Z_OK != cmpret) { repl_log(gtmsource_log_fp, TRUE, FALSE, "Defaulting to NO compression\n"); *repl_zlib_cmp_level_ptr = ZLIB_CMPLVL_NONE; /* no compression */ return TRUE; } if (REPL_MSG_CMPEXPDATALEN < cmplen) { /* The zlib compression library expanded data more than we had allocated for. But handle code for pro */ assert(FALSE); repl_log(gtmsource_log_fp, TRUE, FALSE, "Compression of %d bytes of test data resulted in %d bytes which is" " more than allocated space of %d bytes\n", REPL_MSG_CMPDATALEN, cmplen, REPL_MSG_CMPEXPDATALEN); repl_log(gtmsource_log_fp, TRUE, FALSE, "Defaulting to NO compression\n"); *repl_zlib_cmp_level_ptr = ZLIB_CMPLVL_NONE; /* no compression */ return TRUE; } test_msg.datalen = (int4)cmplen; memcpy(test_msg.data, cmpbuf, test_msg.datalen); gtmsource_repl_send((repl_msg_ptr_t)&test_msg, "REPL_CMP_TEST", MAX_SEQNO, INVALID_SUPPL_STRM); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state)) return FALSE; /* send did not succeed */ /* for in-house testing, set up a timer that would cause assert failure if REPL_CMP_SOLVE is not received * within 1 or 15 minutes, depending on whether this is a white-box test or not */ # ifdef REPL_CMP_SOLVE_TESTING if (TREF(gtm_environment_init)) start_timer((TID)repl_cmp_solve_src_timeout, 15 * 60 * 1000, repl_cmp_solve_src_timeout, 0, NULL); # endif /*************** Receive REPL_CMP_SOLVE message ***************/ if (!gtmsource_repl_recv((repl_msg_ptr_t)&solve_msg, REPL_MSG_CMPINFOLEN, REPL_CMP_SOLVE, "REPL_CMP_SOLVE")) { # ifdef REPL_CMP_SOLVE_TESTING if (TREF(gtm_environment_init)) cancel_timer((TID)repl_cmp_solve_src_timeout); # endif return FALSE; /* recv did not succeed */ } # ifdef REPL_CMP_SOLVE_TESTING if (TREF(gtm_environment_init)) cancel_timer((TID)repl_cmp_solve_src_timeout); # endif assert(REPL_CMP_SOLVE == solve_msg.type); cmpfail = FALSE; if (REPL_MSG_CMPDATALEN != solve_msg.datalen) { assert(REPL_RCVR_CMP_TEST_FAIL == solve_msg.datalen); cmpfail = TRUE; } else { /* Check that receiver side decompression was correct */ for (index = 0; index < REPL_MSG_CMPDATALEN; index++) { if (inputdata[index] != solve_msg.data[index]) { cmpfail = FALSE; break; } } } if (!cmpfail) { assert(solve_msg.proto_ver == remote_side->proto_ver); assert(REPL_PROTO_VER_MULTISITE_CMP <= solve_msg.proto_ver); repl_log(gtmsource_log_fp, TRUE, FALSE, "Receiver server was able to decompress successfully\n"); *repl_zlib_cmp_level_ptr = gtm_zlib_cmp_level; repl_log(gtmsource_log_fp, TRUE, FALSE, "Using zlib compression level %d for replication\n", gtm_zlib_cmp_level); } else { repl_log(gtmsource_log_fp, TRUE, FALSE, "Receiver server could not decompress successfully\n"); repl_log(gtmsource_log_fp, TRUE, FALSE, "Defaulting to NO compression\n"); *repl_zlib_cmp_level_ptr = ZLIB_CMPLVL_NONE; } return TRUE; } void repl_cmp_solve_src_timeout(void) { assertpro(FALSE); } #ifdef GTM_TLS boolean_t gtmsource_exchange_tls_info(void) { int poll_dir, status, flags; char *errp; repl_tlsinfo_msg_t reply, response; assert(NULL != tls_ctx); assert(REPL_TLS_REQUESTED); assert(!repl_tls.enabled); /* Send REPL_NEED_TLS_INFO message. We pass our GT.M TLS API version as part of this message. */ reply.type = REPL_NEED_TLS_INFO; reply.len = MIN_REPL_MSGLEN; reply.API_version = GTM_TLS_API_VERSION; reply.library_version = (uint4)tls_ctx->runtime_version; gtmsource_repl_send((repl_msg_ptr_t)&reply, "REPL_NEED_TLS_INFO", MAX_SEQNO, INVALID_SUPPL_STRM); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state)) return FALSE; /* send did not succeed */ /* Receive REPL_TLS_INFO message. The GT.M TLS API version of the receiver is sent as part of the message. */ if (!gtmsource_repl_recv((repl_msg_ptr_t)&response, MIN_REPL_MSGLEN, REPL_TLS_INFO, "REPL_TLS_INFO")) return FALSE; /* recv did not succeed */ repl_log(gtmsource_log_fp, TRUE, TRUE, " Remote side API version: 0x%08x\n", response.API_version); repl_log(gtmsource_log_fp, TRUE, TRUE, " Remote side Library version: 0x%08x\n", response.library_version); flags = GTMTLS_OP_VERIFY_PEER | GTMTLS_OP_CLIENT_MODE; /* At this point, both sides are ready for a TLS/SSL handshake. Create a TLS/SSL aware socket. */ if (NULL == (repl_tls.sock = gtm_tls_socket(tls_ctx, repl_tls.sock, gtmsource_sock_fd, repl_tls.id, flags))) { if (!PLAINTEXT_FALLBACK) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TLSCONVSOCK, 0, ERR_TEXT, 2, LEN_AND_STR(gtm_tls_get_error(NULL))); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) MAKE_MSG_WARNING(ERR_TLSCONVSOCK), 0, ERR_TEXT, 2, LEN_AND_STR(gtm_tls_get_error(NULL))); } else { /* Do the actual handshake. */ poll_dir = REPL_INVALID_POLL_DIRECTION; do { status = repl_do_tls_handshake(gtmsource_log_fp, gtmsource_sock_fd, FALSE, &poll_dir); gtmsource_poll_actions(FALSE); if (GTMSOURCE_CHANGING_MODE == gtmsource_state) return FALSE; } while ((GTMTLS_WANT_READ == status) || (GTMTLS_WANT_WRITE == status)); if (SS_NORMAL == status) return TRUE; else if (REPL_CONN_RESET(status)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Attempt to connect() with TLS/SSL protocol failed. " "Status = %d; %s\n", status, STRERROR(status)); repl_close(>msource_sock_fd); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); gtmsource_state = jnlpool->gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; return FALSE; } errp = (-1 == status) ? (char *)gtm_tls_get_error(NULL) : STRERROR(status); if (!PLAINTEXT_FALLBACK) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TLSHANDSHAKE, 0, ERR_TEXT, 2, LEN_AND_STR(errp)); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) MAKE_MSG_WARNING(ERR_TLSHANDSHAKE), 0, ERR_TEXT, 2, LEN_AND_STR(errp)); } repl_log(gtmsource_log_fp, TRUE, TRUE, "Plaintext fallback enabled. Closing and reconnecting without TLS/SSL.\n"); repl_close(>msource_sock_fd); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); gtmsource_state = jnlpool->gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; CLEAR_REPL_TLS_REQUESTED; /* As if -tlsid qualifier was never specified. */ return FALSE; } #endif /* Note: Do NOT reset input parameter "strm_jnl_seqno" unless receiver instance is supplementary and root primary */ boolean_t gtmsource_get_instance_info(boolean_t *secondary_was_rootprimary, seq_num *strm_jnl_seqno) { char print_msg[1024]; gtmsource_local_ptr_t gtmsource_local; int status; repl_instinfo_msg_t instinfo_msg; repl_needinst_msg_t needinst_msg; repl_old_instinfo_msg_t old_instinfo_msg; repl_old_needinst_msg_t old_needinst_msg; assert((NULL != jnlpool) && (NULL != jnlpool->repl_inst_filehdr)); /* journal pool should be set up */ gtmsource_local = jnlpool->gtmsource_local; assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver); if (REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver) { /* Use pre-supplementary protocol to communicate */ /*************** Send REPL_OLD_NEED_INSTANCE_INFO message ***************/ memset(&old_needinst_msg, 0, SIZEOF(old_needinst_msg)); old_needinst_msg.type = REPL_OLD_NEED_INSTANCE_INFO; old_needinst_msg.len = MIN_REPL_MSGLEN; memcpy(old_needinst_msg.instname, jnlpool->repl_inst_filehdr->inst_info.this_instname, MAX_INSTNAME_LEN - 1); old_needinst_msg.proto_ver = REPL_PROTO_VER_THIS; old_needinst_msg.node_endianness = NODE_ENDIANNESS; old_needinst_msg.is_rootprimary = !(jnlpool->jnlpool_ctl->upd_disabled); gtmsource_repl_send((repl_msg_ptr_t)&old_needinst_msg, "REPL_OLD_NEED_INSTANCE_INFO", MAX_SEQNO, INVALID_SUPPL_STRM); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state)) return FALSE; /* send did not succeed */ /*************** Receive REPL_OLD_INSTANCE_INFO message ***************/ if (!gtmsource_repl_recv((repl_msg_ptr_t)&old_instinfo_msg, MIN_REPL_MSGLEN, REPL_OLD_INSTANCE_INFO, "REPL_OLD_INSTANCE_INFO")) return FALSE; /* recv did not succeed */ assert(REPL_OLD_INSTANCE_INFO == old_instinfo_msg.type); repl_log(gtmsource_log_fp, TRUE, FALSE, "Received secondary instance name is [%s]\n", old_instinfo_msg.instname); if (jnlpool->repl_inst_filehdr->is_supplementary) { /* Issue REPL2OLD error because this is a supplementary instance and remote side runs * on a GT.M version that does not understand the supplementary protocol */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPL2OLD, 4, LEN_AND_STR(old_instinfo_msg.instname), LEN_AND_STR(jnlpool->repl_inst_filehdr->inst_info.this_instname)); } /* Check if instance name in the REPL_OLD_INSTANCE_INFO message matches that in the source server command line */ if (STRCMP(old_instinfo_msg.instname, jnlpool->gtmsource_local->secondary_instname)) { /* Instance name obtained from the receiver does not match what was specified in the * source server command line. Issue error. */ sgtm_putmsg(print_msg, PROC_OPS_PRINT_MSG_LEN, VARLSTCNT(6) ERR_REPLINSTSECMTCH, 4, LEN_AND_STR(old_instinfo_msg.instname), LEN_AND_STR(jnlpool->gtmsource_local->secondary_instname)); repl_log(gtmsource_log_fp, TRUE, TRUE, print_msg); status = gtmsource_shutdown(TRUE, NORMAL_SHUTDOWN) - NORMAL_SHUTDOWN; gtmsource_exit(status); } *secondary_was_rootprimary = (boolean_t)old_instinfo_msg.was_rootprimary; } else { /* Use supplementary protocol to communicate */ /*************** Send REPL_NEED_INSTINFO message ***************/ memset(&needinst_msg, 0, SIZEOF(needinst_msg)); needinst_msg.type = REPL_NEED_INSTINFO; needinst_msg.len = SIZEOF(needinst_msg); memcpy(needinst_msg.instname, jnlpool->repl_inst_filehdr->inst_info.this_instname, MAX_INSTNAME_LEN - 1); needinst_msg.lms_group_info = jnlpool->repl_inst_filehdr->lms_group_info; /* Need to byteswap a few multi-byte fields to take into account the receiver endianness */ assert(remote_side->endianness_known); /* only then is remote_side->cross_endian reliable */ /* Starting GT.M V62001, the receiver server expects an endian converted lms_group_info. So endian * convert in that case. Pre-V62001, the receiver unconditionally did the endian conversion. So skip * endian conversion in the source side in that case. */ if (remote_side->cross_endian && (REPL_PROTO_VER_XENDIANFIXES <= remote_side->proto_ver)) ENDIAN_CONVERT_REPL_INST_UUID(&needinst_msg.lms_group_info); needinst_msg.proto_ver = REPL_PROTO_VER_THIS; needinst_msg.is_rootprimary = !(jnlpool->jnlpool_ctl->upd_disabled); needinst_msg.is_supplementary = jnlpool->repl_inst_filehdr->is_supplementary; needinst_msg.jnl_ver = JNL_VER_THIS; gtmsource_repl_send((repl_msg_ptr_t)&needinst_msg, "REPL_NEED_INSTINFO", MAX_SEQNO, INVALID_SUPPL_STRM); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state)) return FALSE; /* send did not succeed */ /*************** Receive REPL_INSTINFO message ***************/ if (!gtmsource_repl_recv((repl_msg_ptr_t)&instinfo_msg, SIZEOF(repl_instinfo_msg_t), REPL_INSTINFO, "REPL_INSTINFO")) return FALSE; /* recv did not succeed */ assert(REPL_INSTINFO == instinfo_msg.type); repl_log(gtmsource_log_fp, TRUE, FALSE, "Received secondary instance name is [%s]\n", instinfo_msg.instname); if (!remote_side->is_supplementary) { /* Remote side is non-supplementary */ if (jnlpool->repl_inst_filehdr->is_supplementary) { /* Issue SECNOTSUPPLEMENTARY error because this is a supplementary primary and secondary * is not a supplementary instance. */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_SECNOTSUPPLEMENTARY, 4, LEN_AND_STR(jnlpool->repl_inst_filehdr->inst_info.this_instname), LEN_AND_STR(instinfo_msg.instname)); } } else if (!jnlpool->repl_inst_filehdr->is_supplementary) { /* Remote side is supplementary and Local side is non-supplementary. * The REPL_INSTINFO message would have a non-zero "strm_jnl_seqno" field. * Pass it back on to the caller. */ assert(instinfo_msg.strm_jnl_seqno); assert(0 == GET_STRM_INDEX(instinfo_msg.strm_jnl_seqno)); repl_log(gtmsource_log_fp, TRUE, TRUE, "Received Stream Seqno = %llu [0x%llx]\n", instinfo_msg.strm_jnl_seqno, instinfo_msg.strm_jnl_seqno); *strm_jnl_seqno = instinfo_msg.strm_jnl_seqno; } /* Check if instance name in the REPL_INSTINFO message matches that in the source server command line */ if (STRCMP(instinfo_msg.instname, jnlpool->gtmsource_local->secondary_instname)) { /* Instance name obtained from the receiver does not match what was specified in the * source server command line. Issue error. */ sgtm_putmsg(print_msg, PROC_OPS_PRINT_MSG_LEN, VARLSTCNT(6) ERR_REPLINSTSECMTCH, 4, LEN_AND_STR(instinfo_msg.instname), LEN_AND_STR(jnlpool->gtmsource_local->secondary_instname)); repl_log(gtmsource_log_fp, TRUE, TRUE, print_msg); status = gtmsource_shutdown(TRUE, NORMAL_SHUTDOWN) - NORMAL_SHUTDOWN; gtmsource_exit(status); } *secondary_was_rootprimary = (boolean_t)instinfo_msg.was_rootprimary; } return TRUE; } /* Given an input "seqno", this function locates the histinfo record from the receiver that corresponds to "seqno - 1" * * seqno --> The journal seqno that is to be searched in the instance file history. * histinfo --> Pointer to the histinfo structure that is filled in with what was received. */ boolean_t gtmsource_get_remote_histinfo(seq_num seqno, repl_histinfo *histinfo) { char err_string[1024]; repl_histinfo1_msg_t histinfo1_msg; repl_histinfo2_msg_t histinfo2_msg; repl_histinfo_msg_t histinfo_msg; repl_needhistinfo_msg_t needhistinfo_msg; /*************** Send REPL_NEED_HISTINFO (formerly REPL_NEED_TRIPLE_INFO) message ***************/ memset(&needhistinfo_msg, 0, SIZEOF(needhistinfo_msg)); assert(SIZEOF(needhistinfo_msg) == MIN_REPL_MSGLEN); needhistinfo_msg.type = REPL_NEED_HISTINFO; needhistinfo_msg.len = MIN_REPL_MSGLEN; needhistinfo_msg.seqno = seqno; needhistinfo_msg.strm_num = strm_index; needhistinfo_msg.histinfo_num = INVALID_HISTINFO_NUM; gtmsource_repl_send((repl_msg_ptr_t)&needhistinfo_msg, "REPL_NEED_HISTINFO", seqno, needhistinfo_msg.strm_num); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state)) return FALSE; /* send did not succeed */ if (REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver) { /* Remote side does not support supplementary protocol. Use older protocol messages to communicate. */ assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver); /*************** Receive REPL_OLD_TRIPLEINFO1 message ***************/ if (!gtmsource_repl_recv((repl_msg_ptr_t)&histinfo1_msg, MIN_REPL_MSGLEN, REPL_OLD_TRIPLEINFO1, "REPL_OLD_TRIPLEINFO1")) return FALSE; /* recv did not succeed */ assert(REPL_OLD_TRIPLEINFO1 == histinfo1_msg.type); /*************** Receive REPL_OLD_TRIPLEINFO2 message ***************/ if (!gtmsource_repl_recv((repl_msg_ptr_t)&histinfo2_msg, MIN_REPL_MSGLEN, REPL_OLD_TRIPLEINFO2, "REPL_OLD_TRIPLEINFO2")) return FALSE; /* recv did not succeed */ assert(REPL_OLD_TRIPLEINFO2 == histinfo2_msg.type); /* Check if start_seqno in HISTINFO1 and HISTINFO2 message is the same. If not something is wrong */ if (histinfo1_msg.start_seqno != histinfo2_msg.start_seqno) { assert(FALSE); repl_log(gtmsource_log_fp, TRUE, FALSE, "REPL_OLD_TRIPLEINFO1 msg has start_seqno %llu [0x%llx] while " "corresponding REPL_OLD_TRIPLEINFO2 msg has a different start_seqno %llu [0x%llx]. " "Closing connection.\n", histinfo1_msg.start_seqno, histinfo1_msg.start_seqno, histinfo2_msg.start_seqno, histinfo2_msg.start_seqno); repl_close(>msource_sock_fd); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); gtmsource_state = jnlpool->gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; return FALSE; } memset(histinfo, 0, SIZEOF(*histinfo)); memcpy(histinfo->root_primary_instname, histinfo1_msg.instname, MAX_INSTNAME_LEN - 1); histinfo->start_seqno = histinfo1_msg.start_seqno; histinfo->root_primary_cycle = histinfo2_msg.cycle; histinfo->histinfo_num = histinfo2_msg.histinfo_num; } else { /* Remote side does support supplementary protocol. Use newer protocol messages to communicate. */ /*************** Receive REPL_HISTINFO message ***************/ if (!gtmsource_repl_recv((repl_msg_ptr_t)&histinfo_msg, SIZEOF(repl_histinfo_msg_t), REPL_HISTINFO, "REPL_HISTINFO")) return FALSE; /* recv did not succeed */ assert(REPL_HISTINFO == histinfo_msg.type); *histinfo = histinfo_msg.history; } assert(0 <= histinfo->histinfo_num); return TRUE; } /* Given an input "seqno", this function determines how many non-supplementary streams are known to the receiver server * as of instance jnl_seqno = "seqno". It then compares if the source side list of known streams are identical. For each * such stream, exchange and verify the stream-specific history record corresponding to "seqno" is the same. * * seqno --> The journal seqno that is to be searched in the instance file history. * *rollback_first --> Set to TRUE if we find some stream history record not matching between the source & receiver side. */ boolean_t gtmsource_check_remote_strm_histinfo(seq_num seqno, boolean_t *rollback_first) { boolean_t lcl_strm_valid, remote_strm_valid; int4 lcl_histinfo_num; char err_string[1024]; int idx, status; repl_histinfo local_histinfo; repl_histinfo_msg_t histinfo_msg; repl_needhistinfo_msg_t needhistinfo_msg; repl_needstrminfo_msg_t needstrminfo_msg; repl_strminfo_msg_t strminfo_msg; assert(remote_side->is_supplementary); assert(REPL_PROTO_VER_SUPPLEMENTARY <= remote_side->proto_ver); assert(0 == strm_index); assert(FALSE == *rollback_first); /*************** Send REPL_NEED_STRMINFO message ***************/ memset(&needstrminfo_msg, 0, SIZEOF(needstrminfo_msg)); assert(SIZEOF(needstrminfo_msg) == MIN_REPL_MSGLEN); needstrminfo_msg.type = REPL_NEED_STRMINFO; needstrminfo_msg.len = MIN_REPL_MSGLEN; needstrminfo_msg.seqno = seqno; gtmsource_repl_send((repl_msg_ptr_t)&needstrminfo_msg, "REPL_NEED_STRMINFO", seqno, INVALID_SUPPL_STRM); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state)) return FALSE; /* send did not succeed */ /*************** Receive REPL_STRMINFO message ***************/ if (!gtmsource_repl_recv((repl_msg_ptr_t)&strminfo_msg, SIZEOF(repl_strminfo_msg_t), REPL_STRMINFO, "REPL_STRMINFO")) return FALSE; /* recv did not succeed */ assert(REPL_STRMINFO == strminfo_msg.type); /* Verify that the list of known streams is identical on both sides */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, HANDLE_CONCUR_ONLINE_ROLLBACK); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) return FALSE; /* concurrent online rollback happened */ status = repl_inst_histinfo_find_seqno(seqno, INVALID_SUPPL_STRM, &local_histinfo); rel_lock(jnlpool->jnlpool_dummy_reg); assert(0 == status); /* we are guaranteed to find this since we have already verified 0th stream matches */ /* Fix last_histinfo_num[] in local side to include "local_histinfo" too (which could have strm_index > 0) */ if (0 < local_histinfo.strm_index) { assert(local_histinfo.last_histinfo_num[local_histinfo.strm_index] < local_histinfo.histinfo_num); local_histinfo.last_histinfo_num[local_histinfo.strm_index] = local_histinfo.histinfo_num; } /* Skip 0th stream as it has already been verified */ for (idx = 1; idx < MAX_SUPPL_STRMS; idx++) { lcl_strm_valid = (INVALID_HISTINFO_NUM != local_histinfo.last_histinfo_num[idx]); remote_strm_valid = (INVALID_HISTINFO_NUM != strminfo_msg.last_histinfo_num[idx]); if (!lcl_strm_valid && remote_strm_valid) { assert(FALSE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_STRMNUMMISMTCH1, 1, idx); } else if (lcl_strm_valid && !remote_strm_valid) { assert(FALSE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_STRMNUMMISMTCH2, 1, idx); } } /* Now that we know both sides have the exact set of known streams, verify history record for each stream matches */ /* Send REPL_NEED_HISTINFO message for each stream. Do common initialization outside loop */ memset(&needhistinfo_msg, 0, SIZEOF(needhistinfo_msg)); assert(SIZEOF(needhistinfo_msg) == MIN_REPL_MSGLEN); needhistinfo_msg.type = REPL_NEED_HISTINFO; needhistinfo_msg.len = MIN_REPL_MSGLEN; needhistinfo_msg.seqno = seqno; /* this is not essential but helps debugging the handshake as this gets logged in * the receiver log so initialize it */ for (idx = 1; idx < MAX_SUPPL_STRMS; idx++) { lcl_histinfo_num = local_histinfo.last_histinfo_num[idx]; if (INVALID_HISTINFO_NUM == lcl_histinfo_num) continue; /* Find corresponding history record on REMOTE side */ assert(INVALID_HISTINFO_NUM != strminfo_msg.last_histinfo_num[idx]); needhistinfo_msg.histinfo_num = strminfo_msg.last_histinfo_num[idx]; needhistinfo_msg.strm_num = idx; /*************** Send REPL_NEED_HISTINFO message ***************/ gtmsource_repl_send((repl_msg_ptr_t)&needhistinfo_msg, "REPL_NEED_HISTINFO", seqno, needhistinfo_msg.strm_num); if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state)) return FALSE; /* send did not succeed */ /*************** Receive REPL_HISTINFO message ***************/ if (!gtmsource_repl_recv((repl_msg_ptr_t)&histinfo_msg, SIZEOF(repl_histinfo_msg_t), REPL_HISTINFO, "REPL_HISTINFO")) return FALSE; /* recv did not succeed */ assert(REPL_HISTINFO == histinfo_msg.type); /* Find corresponding history record on LOCAL side */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, HANDLE_CONCUR_ONLINE_ROLLBACK); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) return FALSE; /* concurrent online rollback happened */ status = repl_inst_histinfo_get(lcl_histinfo_num, &local_histinfo); assert(0 == status); /* Since we pass histinfo_num of 0 which is >=0 and < num_histinfo */ rel_lock(jnlpool->jnlpool_dummy_reg); /* Compare the two history records. If they are not identical for even one stream, signal rollback on receiver */ if (!gtmsource_is_histinfo_identical(&histinfo_msg.history, &local_histinfo, seqno, OK_TO_LOG_TRUE)) *rollback_first = TRUE; } return TRUE; } /* This function finds the 'n'th histinfo record in the replication instance file of this instance. * This is a wrapper on top of "repl_inst_histinfo_get" which additionally does error checking. * This closes the connection if it detects a REPLINSTNOHIST error. */ void gtmsource_histinfo_get(int4 index, repl_histinfo *histinfo) { unix_db_info *udi; char histdetail[MAX_REPL_OPMSG_LEN]; int4 status; repl_msg_t instnohist_msg; assert((NULL != jnlpool) && (NULL != jnlpool->repl_inst_filehdr)); /* journal pool should be set up */ udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); assert(udi->s_addrs.now_crit); status = repl_inst_histinfo_get(index, histinfo); assert((0 == status) || (INVALID_HISTINFO_NUM == index)); assert((0 != status) || (index == histinfo->histinfo_num)); if (0 != status) { assert(ERR_REPLINSTNOHIST == status); /* currently the only error returned by "repl_inst_histinfo_get" */ SNPRINTF(histdetail, MAX_REPL_OPMSG_LEN, "record number %d [0x%x]", index, index); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_REPLINSTNOHIST, 4, LEN_AND_STR(histdetail), LEN_AND_STR(udi->fn)); /* Send this error status to the receiver server before closing the connection. This way the receiver * will know to shut down rather than loop back trying to reconnect. This avoids an infinite loop of * connection open and closes between the source server and receiver server. */ instnohist_msg.type = REPL_INST_NOHIST; instnohist_msg.len = MIN_REPL_MSGLEN; memset(&instnohist_msg.msg[0], 0, SIZEOF(instnohist_msg.msg)); gtmsource_repl_send((repl_msg_ptr_t)&instnohist_msg, "REPL_INST_NOHIST", MAX_SEQNO, INVALID_SUPPL_STRM); /* Close the connection */ repl_log(gtmsource_log_fp, TRUE, TRUE, "Connection reset due to above REPLINSTNOHIST error\n"); repl_close(>msource_sock_fd); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); gtmsource_state = jnlpool->gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; } } /* Given two histinfo records (one each from the primary and secondary), this function compares if the history information * in both histinfo records are the same. If so, it returns TRUE; otherwise it returns FALSE. The jnl_seqno is only used for * logging and should refer to the resync seqno we are checking. Logging is only performed if ok_to_log is TRUE. */ boolean_t gtmsource_is_histinfo_identical(repl_histinfo *remote_histinfo, repl_histinfo *local_histinfo, seq_num jnl_seqno, boolean_t ok_to_log) { if (ok_to_log) { repl_log(gtmsource_log_fp, FALSE, FALSE, "On Secondary, seqno %llu [0x%llx] generated by instance name [%s] " "with cycle [0x%x] (%d), pid = %d : time = %d [0x%x]\n", jnl_seqno - 1, jnl_seqno - 1, remote_histinfo->root_primary_instname, remote_histinfo->root_primary_cycle, remote_histinfo->root_primary_cycle, remote_histinfo->creator_pid, remote_histinfo->created_time, remote_histinfo->created_time); repl_log(gtmsource_log_fp, FALSE, FALSE, "On Primary, seqno %llu [0x%llx] generated by instance name [%s] " "with cycle [0x%x] (%d), pid = %d : time = %d [0x%x]\n", jnl_seqno - 1, jnl_seqno - 1, local_histinfo->root_primary_instname, local_histinfo->root_primary_cycle, local_histinfo->root_primary_cycle, local_histinfo->creator_pid, local_histinfo->created_time, local_histinfo->created_time); } /* Starting with the version of GT.M that supports supplementary instances, we check for a lot more pieces of the history. * But if remote side is an older version that does not support the supplementary protocol, check only those pieces * which were checked previously. */ if (STRCMP(local_histinfo->root_primary_instname, remote_histinfo->root_primary_instname) || (local_histinfo->root_primary_cycle != remote_histinfo->root_primary_cycle) || ((REPL_PROTO_VER_SUPPLEMENTARY <= remote_side->proto_ver) && ((local_histinfo->creator_pid != remote_histinfo->creator_pid) || (local_histinfo->created_time != remote_histinfo->created_time)))) { /* either the root primary instance name or the cycle did not match */ if (ok_to_log) { repl_log(gtmsource_log_fp, FALSE, FALSE, "Primary and Secondary have DIFFERING history records for " "seqno %llu [0x%llx]\n", jnl_seqno - 1, jnl_seqno - 1); SNPRINTF(print_msg_t, SIZEOF(print_msg_t), "Originating instance and replicating instances are" " out-of-sync after sequence number : "INT8_FMT" ", jnl_seqno - 1); sgtm_putmsg(print_msg_src, PROC_OPS_PRINT_MSG_LEN, VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_STR(print_msg_t)); repl_log(gtmsource_log_fp, TRUE, TRUE, print_msg_src); } return FALSE; } else { if (ok_to_log) repl_log(gtmsource_log_fp, FALSE, FALSE, "Primary and Secondary have IDENTICAL history records for " "seqno %llu [0x%llx]\n", jnl_seqno - 1, jnl_seqno - 1); return TRUE; } } /* Determine the resync seqno between primary and secondary by comparing local and remote histinfo records from the tail of the * respective instance files until we reach a seqno whose histinfo record information is identical in both. The resync seqno * is the first seqno whose histinfo record information was NOT identical in both. The histinfo records on the secondary are * obtained through successive calls to the function "gtmsource_get_remote_histinfo". * * If the connection gets reset while exchanging histinfo records with secondary, this function returns a seqno of MAX_SEQNO. * The global variable "gtmsource_state" will be set to GTMSOURCE_CHANGING_MODE or GTMSOURCE_WAITING_FOR_CONNECTION and the * caller of this function should accordingly check for that immediately on return. */ seq_num gtmsource_find_resync_seqno(repl_histinfo *local_histinfo, repl_histinfo *remote_histinfo) { seq_num max_start_seqno, local_start_seqno, remote_start_seqno; int4 local_histinfo_num; # ifdef DEBUG int4 prev_remote_histinfo_num; sgmnt_addrs *csa; seq_num min_start_seqno; # endif assert((NULL != jnlpool) && (NULL != jnlpool->jnlpool_dummy_reg) && jnlpool->jnlpool_dummy_reg->open); # ifdef DEBUG csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; ASSERT_VALID_JNLPOOL(csa); prev_remote_histinfo_num = remote_histinfo->prev_histinfo_num; # endif do { local_start_seqno = local_histinfo->start_seqno; remote_start_seqno = remote_histinfo->start_seqno; assert(local_start_seqno); assert(remote_start_seqno); max_start_seqno = MAX(local_start_seqno, remote_start_seqno); /* "max_start_seqno" is the largest yet known seqno whose histinfo does NOT match between primary and secondary. * Therefore determine the histinfo(s) for "max_start_seqno-1" in primary and/or secondary and compare them. */ if (1 == max_start_seqno) { /* The earliest possible seqno in the primary is out of sync with that of the secondary. Stop the histinfo * search right away and return with 1 (the smallest possible seqno) as the resync seqno. */ assert(local_start_seqno == max_start_seqno); assert(remote_start_seqno == max_start_seqno); assert(INVALID_HISTINFO_NUM == local_histinfo->prev_histinfo_num); assert(INVALID_HISTINFO_NUM == remote_histinfo->prev_histinfo_num); break; } DEBUG_ONLY(min_start_seqno = MIN(local_start_seqno, remote_start_seqno);) assert(!gtmsource_is_histinfo_identical(remote_histinfo, local_histinfo, min_start_seqno, OK_TO_LOG_FALSE)); if (local_start_seqno == max_start_seqno) { /* Need to get the previous histinfo record on the primary */ local_histinfo_num = local_histinfo->prev_histinfo_num; assert(0 <= local_histinfo->histinfo_num); grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, HANDLE_CONCUR_ONLINE_ROLLBACK); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) return MAX_SEQNO; gtmsource_histinfo_get(local_histinfo_num, local_histinfo); rel_lock(jnlpool->jnlpool_dummy_reg); if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) return MAX_SEQNO; /* Connection got reset in "gtmsource_histinfo_get" due to REPLINSTNOHIST */ } if (remote_start_seqno == max_start_seqno) { /* Need to get the previous histinfo record on the secondary */ assert(0 <= prev_remote_histinfo_num); if (!gtmsource_get_remote_histinfo(remote_start_seqno, remote_histinfo)) { assert((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state)); /* Connection got reset while exchanging histinfo history information */ return MAX_SEQNO; } assert(remote_histinfo->histinfo_num == prev_remote_histinfo_num); DEBUG_ONLY(prev_remote_histinfo_num = remote_histinfo->prev_histinfo_num); } assert(local_histinfo->start_seqno < max_start_seqno); assert(remote_histinfo->start_seqno < max_start_seqno); } while (!gtmsource_is_histinfo_identical(remote_histinfo, local_histinfo, max_start_seqno, OK_TO_LOG_TRUE)); repl_log(gtmsource_log_fp, TRUE, FALSE, "Resync Seqno determined is %llu [0x%llx]\n", max_start_seqno, max_start_seqno); /* "max_start_seqno-1" has same histinfo info in both primary and secondary. Hence "max_start_seqno" is the resync seqno. */ return max_start_seqno; } /* This routine sends a REPL_HISTREC message for the histinfo record corresponding to seqno "gtmsource_local->read_jnl_seqno". * It positions the send to that histinfo record which corresponds to "gtmsource_local->read_jnl_seqno". This is done by * a call to the function "gtmsource_set_next_histinfo_seqno". * On return from this routine, the caller should check the value of the global variable "gtmsource_state" to see if it is * either of GTMSOURCE_CHANGING_MODE or GTMSOURCE_WAITING_FOR_CONNECTION and if so take appropriate action. */ void gtmsource_send_new_histrec() { repl_histinfo histinfo, zero_histinfo; repl_old_triple_msg_t oldtriple_msg; gtmsource_local_ptr_t gtmsource_local; boolean_t first_histrec_send; int4 zero_histinfo_num; DEBUG_ONLY(sgmnt_addrs *csa;) assert((NULL != jnlpool) && (NULL != jnlpool->jnlpool_dummy_reg) && jnlpool->jnlpool_dummy_reg->open); # ifdef DEBUG csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; ASSERT_VALID_JNLPOOL(csa); # endif gtmsource_local = jnlpool->gtmsource_local; assert(gtmsource_local->send_new_histrec); assert(gtmsource_local->read_jnl_seqno <= gtmsource_local->next_histinfo_seqno); first_histrec_send = (-1 == gtmsource_local->next_histinfo_num); gtmsource_set_next_histinfo_seqno(FALSE); /* sets gtmsource_local->next_histinfo_seqno & next_histinfo_num */ if ((GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) return; /* "gtmsource_set_next_histinfo_seqno" encountered REPLINSTNOHIST or concurrent online rollback occurred */ /*************** Read histinfo (to send) from instance file first ***************/ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, HANDLE_CONCUR_ONLINE_ROLLBACK); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) return; assert(1 <= gtmsource_local->next_histinfo_num); /* With non-supplementary instances, we are guaranteed, consecutive history records increase in start_seqno. * But with supplementary instances, it is possible for two consecutive history records to have the same start_seqno * (if those came from different root primary instances at the same time). Take care of that in the assert below. */ assert(!this_side->is_supplementary && (gtmsource_local->read_jnl_seqno < gtmsource_local->next_histinfo_seqno) || this_side->is_supplementary && (gtmsource_local->read_jnl_seqno <= gtmsource_local->next_histinfo_seqno)); gtmsource_histinfo_get(gtmsource_local->next_histinfo_num - 1, &histinfo); if ((GTMSOURCE_WAITING_FOR_CONNECTION != gtmsource_state) && this_side->is_supplementary && first_histrec_send && (0 < histinfo.strm_index)) { /* Supplementary source side sending to a supplementary receiver. And this is the FIRST history record * being sent for this replication connection. It is possible that the closest history record prior to * "gtmsource_local->read_jnl_seqno" has a non-zero "strm_index". In that case, we might be sending a * non-zero strm_index history record across and it is possible that the secondary has an empty instance * file (in case of an -updateresync startup). This would lead to issues on the receiving supplementary * instance since it will now have a non-zero stream history record as the first history record in its * instance file. This is an out-of-design situation since to establish the resync point between two * supplementary instances, we need to examine the 0th stream and the assumption is that if the instance * file has at least one history record, there is at least one 0th stream history record. Therefore we * need to prevent this out-of-design situation. Towards that, find out the most recent 0th stream * history record in this instance and send that across BEFORE sending the history record corresponding * to "gtmsource_local->read_jnl_seqno". */ zero_histinfo_num = histinfo.last_histinfo_num[0]; assert(INVALID_HISTINFO_NUM != zero_histinfo_num); assert(0 <= zero_histinfo_num); gtmsource_histinfo_get(zero_histinfo_num, &zero_histinfo); } else zero_histinfo_num = INVALID_HISTINFO_NUM; rel_lock(jnlpool->jnlpool_dummy_reg); if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) { assert(FALSE); return; /* Connection got reset in "gtmsource_histinfo_get" due to REPLINSTNOHIST */ } assert(gtmsource_local->read_jnl_seqno >= histinfo.start_seqno); assert(remote_side->endianness_known); /* only then is remote_side->cross_endian reliable */ if (REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver) { /* Remote side does not support supplementary protocol. Use older protocol messages to communicate. */ assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver); assert(!this_side->is_supplementary); /*************** Send REPL_OLD_TRIPLE message ***************/ memset(&oldtriple_msg, 0, SIZEOF(oldtriple_msg)); oldtriple_msg.type = REPL_OLD_TRIPLE; oldtriple_msg.len = SIZEOF(oldtriple_msg); oldtriple_msg.triplecontent.jrec_type = JRT_TRIPLE; if (remote_side->cross_endian && (this_side->jnl_ver < remote_side->jnl_ver)) { oldtriple_msg.triplecontent.forwptr = GTM_BYTESWAP_24(SIZEOF(repl_old_triple_jnl_t)); oldtriple_msg.triplecontent.start_seqno = GTM_BYTESWAP_64(gtmsource_local->read_jnl_seqno); oldtriple_msg.triplecontent.cycle = GTM_BYTESWAP_32(histinfo.root_primary_cycle); } else { oldtriple_msg.triplecontent.forwptr = SIZEOF(repl_old_triple_jnl_t); oldtriple_msg.triplecontent.start_seqno = gtmsource_local->read_jnl_seqno; oldtriple_msg.triplecontent.cycle = histinfo.root_primary_cycle; } memcpy(oldtriple_msg.triplecontent.instname, histinfo.root_primary_instname, MAX_INSTNAME_LEN - 1); gtmsource_repl_send((repl_msg_ptr_t)&oldtriple_msg, "REPL_OLD_TRIPLE", gtmsource_local->read_jnl_seqno, INVALID_SUPPL_STRM); } else { /* Remote side supports supplementary protocol. Communicate using new message protocol */ if (INVALID_HISTINFO_NUM != zero_histinfo_num) { /* Send REPL_HISTREC message corresponding to the 0th stream history record */ assert(gtmsource_local->read_jnl_seqno >= zero_histinfo.start_seqno); GTMSOURCE_SEND_REPL_HISTREC(zero_histinfo, gtmsource_local, remote_side->cross_endian); /* the above macro would have invoked "gtmsource_repl_send" so check for "gtmsource_state" */ if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state)) return; /* send did not succeed */ /* If we have a non-zero stream history record, then check if its "start_seqno" is lesser than * gtmsource_local->read_jnl_seqno. If so, we don't even need to send this history record. If it is * equal though, we do need to send this across. */ assert(gtmsource_local->read_jnl_seqno >= histinfo.start_seqno); if (gtmsource_local->read_jnl_seqno == histinfo.start_seqno) GTMSOURCE_SEND_REPL_HISTREC(histinfo, gtmsource_local, remote_side->cross_endian); } else GTMSOURCE_SEND_REPL_HISTREC(histinfo, gtmsource_local, remote_side->cross_endian); } if ((GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state)) return; /* send did not succeed */ repl_dump_histinfo(gtmsource_log_fp, TRUE, TRUE, "New History Content", &histinfo); gtmsource_local->send_new_histrec = FALSE; } /* This function is invoked once for each histinfo record that the source server goes past while sending journal records across. * This function sets the boundary seqno field "next_histinfo_seqno" to be the "start_seqno" of the next histinfo record so the * source server does not send any seqnos corresponding to the next histinfo record before sending a REPL_HISTREC message. * It will set "gtmsource_local->next_histinfo_seqno" and "gtmsource_local->next_histinfo_num" to correspond to the next histinfo * record and set the private copy "gtmsource_local->num_histinfo" to a copy of what is currently present in * "repl_inst_filehdr->num_histinfo". * * The input variable "detect_new_histinfo" is set to TRUE if this function is called from "gtmsource_readfiles" or * "gtmsource_readpool" the moment they detect that the instance file has had a new histinfo record since the last time this * source server took a copy of it in its private "gtmsource_local->num_histinfo". In this case, the only objective * is to find the start_seqno of the next histinfo record and note that down as "gtmsource_local->next_histinfo_seqno". * * If the input variable "detect_new_histinfo" is set to FALSE, "next_histinfo_seqno" is set to the starting seqno of the * histinfo record immediately after that corresponding to "gtmsource_local->read_jnl_seqno". * * This can end up closing the connection if the call to "gtmsource_histinfo_get" or "repl_inst_histinfo_find_seqno" fails. */ void gtmsource_set_next_histinfo_seqno(boolean_t detect_new_histinfo) { unix_db_info *udi; int4 status, next_histinfo_num, num_histinfo; repl_histinfo next_histinfo, prev_histinfo; gtmsource_local_ptr_t gtmsource_local; repl_msg_t instnohist_msg; char histdetail[256]; seq_num read_seqno; DEBUG_ONLY(sgmnt_addrs *csa;) assert((NULL != jnlpool) && (NULL != jnlpool->jnlpool_dummy_reg) && jnlpool->jnlpool_dummy_reg->open); # ifdef DEBUG csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; ASSERT_VALID_JNLPOOL(csa); # endif grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, HANDLE_CONCUR_ONLINE_ROLLBACK); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) return; assert(NULL != jnlpool->repl_inst_filehdr); /* journal pool should be set up */ gtmsource_local = jnlpool->gtmsource_local; next_histinfo_num = gtmsource_local->next_histinfo_num; /* assert((-1 == next_histinfo_num) || (gtmsource_local->next_histinfo_seqno >= gtmsource_local->read_jnl_seqno)); */ read_seqno = gtmsource_local->read_jnl_seqno; assert(gtmsource_local->next_histinfo_seqno >= read_seqno); num_histinfo = jnlpool->repl_inst_filehdr->num_histinfo; if (!detect_new_histinfo) { if (-1 == next_histinfo_num) { /* This is the first invocation of this function after this connection with a receiver was established. * Find the first histinfo record in the local instance file corresponding to the next seqno to be sent * across i.e. "gtmsource_local->read_jnl_seqno". The below function will return the history record * just BEFORE read_jnl_seqno. So fetch the immediately next history record to get the desired record. */ assert(read_seqno <= jnlpool->jnlpool_ctl->jnl_seqno); status = repl_inst_histinfo_find_seqno(read_seqno, INVALID_SUPPL_STRM, &prev_histinfo); if (0 != status) { assert(ERR_REPLINSTNOHIST == status); /* only error returned by "repl_inst_histinfo_find_seqno" */ assert((INVALID_HISTINFO_NUM == prev_histinfo.histinfo_num) || (prev_histinfo.start_seqno >= read_seqno)); if ((INVALID_HISTINFO_NUM == prev_histinfo.histinfo_num) || (prev_histinfo.start_seqno > read_seqno)) { /* The read seqno is PRIOR to the starting seqno of the instance file. * In that case, issue error and close the connection. */ NON_GTM64_ONLY(SNPRINTF(histdetail, MAX_REPL_OPMSG_LEN, "seqno [0x%llx]", read_seqno - 1)); GTM64_ONLY(SNPRINTF(histdetail, MAX_REPL_OPMSG_LEN, "seqno [0x%lx]", read_seqno - 1)); udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_REPLINSTNOHIST, 4, LEN_AND_STR(histdetail), LEN_AND_STR(udi->fn)); /* Send error status to the receiver server before closing the connection. This way the * receiver will know to shut down rather than loop back trying to reconnect. This avoids * an infinite loop of connection open/closes between the source and receiver servers. */ instnohist_msg.type = REPL_INST_NOHIST; instnohist_msg.len = MIN_REPL_MSGLEN; memset(&instnohist_msg.msg[0], 0, SIZEOF(instnohist_msg.msg)); gtmsource_repl_send((repl_msg_ptr_t)&instnohist_msg, "REPL_INST_NOHIST", MAX_SEQNO, INVALID_SUPPL_STRM); rel_lock(jnlpool->jnlpool_dummy_reg); repl_log(gtmsource_log_fp, TRUE, TRUE, "Connection reset due to above REPLINSTNOHIST error\n"); repl_close(>msource_sock_fd); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; return; } next_histinfo_num = prev_histinfo.histinfo_num; } else { assert(prev_histinfo.start_seqno < read_seqno); next_histinfo_num = prev_histinfo.histinfo_num; if ((next_histinfo_num + 1) < num_histinfo) { gtmsource_histinfo_get(next_histinfo_num + 1, &next_histinfo); if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) { assert(FALSE); rel_lock(jnlpool->jnlpool_dummy_reg); return; /* Connection got reset in "gtmsource_histinfo_get" due to REPLINSTNOHIST */ } assert(next_histinfo.start_seqno >= read_seqno); if (next_histinfo.start_seqno == read_seqno) { next_histinfo_num++; assert(next_histinfo_num == next_histinfo.histinfo_num); } } } } /* else: next_histinfo_num was already found. So just move on to the NEXT history record. */ assert(0 <= next_histinfo_num); assert(next_histinfo_num < num_histinfo); assert((gtmsource_local->next_histinfo_num != next_histinfo_num) || (read_seqno == gtmsource_local->next_histinfo_seqno) || (MAX_SEQNO == gtmsource_local->next_histinfo_seqno)); next_histinfo_num++; } else { /* A new histinfo record got added to the instance file since we knew last. * Set "next_histinfo_seqno" for our current histinfo down from its current value of MAX_SEQNO. */ assert(gtmsource_local->next_histinfo_seqno == MAX_SEQNO); assert(next_histinfo_num < num_histinfo); if (READ_FILE == gtmsource_local->read_state) { /* It is possible that we have already read the journal records for the next * read_jnl_seqno before detecting that a histinfo has to be sent first. In that case, * the journal files may have been positioned ahead of the read_jnl_seqno for the * next read. Indicate that they have to be repositioned into the past. */ gtmsource_set_lookback(); } } if (num_histinfo > next_histinfo_num) { /* Read the next histinfo record to determine its "start_seqno" */ gtmsource_histinfo_get(next_histinfo_num, &next_histinfo); if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) { assert(FALSE); rel_lock(jnlpool->jnlpool_dummy_reg); return; /* Connection got reset in "gtmsource_histinfo_get" due to REPLINSTNOHIST */ } assert(next_histinfo.start_seqno >= read_seqno); gtmsource_local->next_histinfo_seqno = next_histinfo.start_seqno; } else gtmsource_local->next_histinfo_seqno = MAX_SEQNO; gtmsource_local->next_histinfo_num = next_histinfo_num; gtmsource_local->num_histinfo = num_histinfo; rel_lock(jnlpool->jnlpool_dummy_reg); } fis-gtm-V7.0-005/sr_unix/gtmsource_readfiles.c0000644000032200000250000026646614342376330020257 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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_string.h" #include /* for offsetof macro */ #include "gtm_ipc.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_un.h" #include #include #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_stat.h" #include "gtmio.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtm_repl.h" #include "repl_msg.h" #include "gtmsource.h" #include "jnl.h" #include "buddy_list.h" #include "hashtab_mname.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "muprec.h" #include "repl_ctl.h" #include "repl_errno.h" #include "repl_dbg.h" #include "iosp.h" #include "gtm_stdio.h" #include "copy.h" #include "eintr_wrappers.h" #include "repl_sp.h" #include "is_file_identical.h" #include "repl_log.h" #include "min_max.h" #include "error.h" #include "repl_tr_good.h" #include "repl_instance.h" #include "wbox_test_init.h" #include "gtmcrypt.h" #include "gtmdbgflags.h" #include "anticipatory_freeze.h" #define LSEEK_ERR_STR "Error in lseek" #define READ_ERR_STR "Error in read" #define UNKNOWN_ERR_STR "Error unknown" /* Get journal end of data, adjusted if file not virtually truncated by recover/rollback */ #define REAL_END_OF_DATA(FC) (FC->jfh->prev_recov_end_of_data ? FC->jfh->end_of_data : FC->jfh->end_of_data + EOF_RECLEN) #define DO_EOF_ADDR_CHECK FALSE #define SKIP_EOF_ADDR_CHECK TRUE /* Callers of this macro ensure that the maximum seqno which can be found until offset MAX_SEQNO_EOF_ADDR is MAX_SEQNO. */ #define CTL_SET_MAX_SEQNO(CTL, MAX_SEQNO, MAX_SEQNO_ADDR, MAX_SEQNO_EOF_ADDR, SKIP_EOF_ADDR_CHECK) \ MBSTART { \ assert(MAX_SEQNO_ADDR <= MAX_SEQNO_EOF_ADDR); \ assert(SKIP_EOF_ADDR_CHECK || (MAX_SEQNO_EOF_ADDR <= CTL->repl_buff->fc->eof_addr)); \ assert(ctl->max_seqno <= MAX_SEQNO); \ ctl->max_seqno = MAX_SEQNO; \ assert(ctl->max_seqno_dskaddr <= MAX_SEQNO_ADDR); \ ctl->max_seqno_dskaddr = MAX_SEQNO_ADDR; \ assert(MAX_SEQNO_EOF_ADDR >= MAX_SEQNO_ADDR); \ assert(ctl->max_seqno_eof_addr <= MAX_SEQNO_EOF_ADDR); \ ctl->max_seqno_eof_addr = MAX_SEQNO_EOF_ADDR; \ } MBEND #define BUNCHING_TIME (8 * MILLISECS_IN_SEC) #define GTMSRC_DO_JNL_FLUSH_IF_POSSIBLE(CTL, CSA) \ MBSTART { \ boolean_t flush_done; \ uint4 dskaddr, freeaddr, rsrv_freeaddr, saved_jpc_cycle; \ jnl_private_control *jpc; \ int rc; \ \ jpc = CSA->jnl; \ saved_jpc_cycle = jpc->cycle; /* remember current cycle */ \ DO_JNL_FLUSH_IF_POSSIBLE(CTL->reg, CSA, flush_done, dskaddr, freeaddr, rsrv_freeaddr); \ if (flush_done) \ { /* Source server did a flush. Log that event for debugging purposes */ \ repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL_INFO : Source server did flush of journal file %s " \ "state %s : seqno %llu [0x%llx]. [dskaddr 0x%x freeaddr 0x%x rsrv_freeaddr 0x%x]\n", \ CTL->jnl_fn, jnl_file_state_lit[CTL->file_state], CTL->seqno, CTL->seqno, \ dskaddr, freeaddr, rsrv_freeaddr); \ /* Since "flush_done" is TRUE, it means the source server has done a "jnl_ensure_open" inside the \ * DO_JNL_FLUSH_IF_POSSIBLE macro and has opened the journal file. It already has a copy of the fd in \ * ctl->repl_buff->fc->fd which it uses to read the journal file. Now that the jnl_flush (rare event) \ * is done, close the jpc->channel and continue to use the ctl's fd. \ */ \ assert(NOJNL != jpc->channel); \ JNL_FD_CLOSE(jpc->channel, rc); /* sets jpc->channel to NOJNL */ \ assert(NOJNL == jpc->channel); \ jpc->cycle = saved_jpc_cycle; /* do not want cycle to be changed by our flushing so restore it */ \ } \ } MBEND GBLREF unsigned char *gtmsource_tcombuff_start; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF repl_ctl_element *repl_ctl_list; GBLREF repl_rctl_elem_t *repl_rctl_list; GBLREF seq_num gtmsource_save_read_jnl_seqno; GBLREF repl_msg_ptr_t gtmsource_msgp; GBLREF int gtmsource_msgbufsiz; GBLREF seq_num seq_num_zero, seq_num_one; GBLREF gd_region *gv_cur_region; GBLREF FILE *gtmsource_log_fp; GBLREF gtmsource_state_t gtmsource_state; GBLREF uint4 process_id; LITREF char *jnl_file_state_lit[]; error_def(ERR_JNLBADRECFMT); error_def(ERR_JNLEMPTY); error_def(ERR_JNLFILRDOPN); error_def(ERR_JNLRECINCMPL); error_def(ERR_NOPREVLINK); error_def(ERR_REPLBRKNTRANS); error_def(ERR_REPLCOMM); error_def(ERR_REPLFILIOERR); error_def(ERR_TEXT); static int4 num_tcom = -1; static boolean_t trans_read = FALSE; static int tot_tcom_len = 0; static int total_wait_for_jnl_recs = 0; static int total_wait_for_jnlopen = 0; static unsigned char *tcombuffp = NULL; static int adjust_buff_leaving_hdr(repl_buff_t *rb); static tr_search_state_t position_read(repl_ctl_element*, seq_num); static int read_regions( unsigned char **buff, int *buff_avail, boolean_t attempt_open_oldnew, boolean_t *brkn_trans, seq_num read_jnl_seqno); static int first_read(repl_ctl_element*); static int update_max_seqno_info(repl_ctl_element *ctl); #if 0 /* Not used for now */ static int scavenge_closed_jnl_files(seq_num ack_seqno); #endif /* 0 */ static int update_eof_addr(repl_ctl_element *ctl, int *eof_change); static int repl_read_file(repl_buff_t *rb) { repl_buff_desc *b; repl_file_control_t *fc; ssize_t nb; sgmnt_addrs *csa; uint4 dskaddr; uint4 read_less, status; int eof_change; uint4 start_addr; uint4 end_addr; fc = rb->fc; b = &rb->buff[rb->buffindex]; csa = &FILE_INFO(rb->backctl->reg)->s_addrs; read_less = 0; assert(b->readaddr >= b->recaddr); assert(0 < b->buffremaining); dskaddr = csa->jnl->jnl_buff->dskaddr; if (!is_gdid_gdid_identical(&fc->id, JNL_GDID_PTR(csa))) { if (!rb->backctl->eof_addr_final) update_eof_addr(rb->backctl, &eof_change); /* update possible change in end_of_data, re-read file header */ assert(!fc->jfh->crash); dskaddr = REAL_END_OF_DATA(fc); } # ifdef DEBUG b->save_readaddr = b->readaddr; b->save_dskaddr = dskaddr; b->save_buffremaining = b->buffremaining; # endif /* Make sure we do not read beyond end of data in the journal file */ /* Note : This logic is always needed when journal file is pre-allocated. * With no pre-allocation, this logic is needed only when repl_read_file is called from * update_max_seqno_info -> repl_next. Specifically, this logic is needed till the existing * JRT_EOF record is completely overwritten and the file grows beyond its existing size. */ assert(b->readaddr <= dskaddr); if (b->buffremaining > (dskaddr - b->readaddr)) /* note the ordering of the operands to take care of 4G overflow */ { if (b->readaddr == dskaddr) { REPL_DPRINT3("READ FILE : Jnl file %s yet to grow from (or ends at) offset %u\n", rb->backctl->jnl_fn, b->readaddr); return (SS_NORMAL); } read_less = b->buffremaining - (dskaddr - b->readaddr); /* explicit ordering to take care of 4G overflow */ REPL_DPRINT5("READ FILE : Racing with jnl file %s avoided. Read size reduced from %u to %u at offset %u\n", rb->backctl->jnl_fn, b->buffremaining, b->buffremaining - read_less, b->readaddr); } start_addr = ROUND_DOWN2(b->readaddr, fc->fs_block_size); end_addr = ROUND_UP2(b->readaddr + b->buffremaining - read_less, fc->fs_block_size); if ((off_t)-1 == lseek(fc->fd, (off_t)start_addr, SEEK_SET)) { repl_errno = EREPL_JNLFILESEEK; return (errno); } READ_FILE(fc->fd, b->base + REPL_BLKSIZE(rb) - b->buffremaining - (b->readaddr - start_addr), end_addr - start_addr, nb); status = errno; if (nb < (b->readaddr - start_addr)) { /* This case means that we didn't read enough bytes to get from the alignment point in the disk file * to the start of the actual desired read (the offset we did the lseek to above). This can't happen * and represents an out of design situation and we must return an error. */ assert(FALSE); nb = -1; } else { nb = nb - (b->readaddr - start_addr); if (nb > (b->buffremaining - read_less)) nb = b->buffremaining - read_less; } if (0 < nb) { b->buffremaining -= (uint4)nb; b->readaddr += (uint4)nb; return (SS_NORMAL); } else if ((0 == nb) && (end_addr != start_addr)) return (SS_NORMAL); repl_errno = EREPL_JNLFILEREAD; return (status); } static int repl_next(repl_buff_t *rb) { repl_buff_desc *b; int4 reclen; jrec_suffix *suffix; uint4 maxreclen; int status, sav_buffremaining; char err_string[BUFSIZ]; repl_ctl_element *backctl; int gtmcrypt_errno; enum jnl_record_type rectype; jnl_record *rec; jnl_string *keystr; jnl_file_header *jfh; boolean_t use_new_key; b = &rb->buff[rb->buffindex]; b->recbuff += b->reclen; /* The next record */ b->recaddr += b->reclen; backctl = rb->backctl; if (b->recaddr == b->readaddr && b->buffremaining == 0) { /* Everything in this buffer processed */ b->recbuff = b->base; b->reclen = 0; b->buffremaining = REPL_BLKSIZE(rb); } if (b->recaddr == b->readaddr || b->reclen == 0 || (backctl->eof_addr_final && (b->readaddr != REAL_END_OF_DATA(rb->fc)) && b->buffremaining)) { sav_buffremaining = b->buffremaining; if ((status = repl_read_file(rb)) == SS_NORMAL) { if (sav_buffremaining == b->buffremaining) { /* Even though we are returning with EREPL_JNLRECINCMPL, this is not a case of an incomplete record * if the journal file is a previous generation journal file. It actually indicates that the end * of file of the previous generation journal file is now reached. */ b->reclen = 0; repl_errno = EREPL_JNLRECINCMPL; return (repl_errno); } } else { if (repl_errno == EREPL_JNLFILESEEK) MEMCPY_LIT(err_string, LSEEK_ERR_STR); else if (repl_errno == EREPL_JNLFILEREAD) MEMCPY_LIT(err_string, READ_ERR_STR); else MEMCPY_LIT(err_string, UNKNOWN_ERR_STR); RTS_ERROR_CSA_ABT(&FILE_INFO(backctl->reg)->s_addrs, VARLSTCNT(9) ERR_REPLFILIOERR, 2, backctl->jnl_fn_len, backctl->jnl_fn, ERR_TEXT, 2, LEN_AND_STR(err_string), status); } } maxreclen = (uint4)(((b->base + REPL_BLKSIZE(rb)) - b->recbuff) - b->buffremaining); assert(maxreclen > 0); jfh = rb->fc->jfh; if (maxreclen > JREC_PREFIX_UPTO_LEN_SIZE && (reclen = ((jrec_prefix *)b->recbuff)->forwptr) <= maxreclen && IS_VALID_JNLREC((jnl_record *)b->recbuff, jfh)) { if (USES_ANY_KEY(jfh)) { rec = ((jnl_record *)(b->recbuff)); rectype = (enum jnl_record_type)rec->prefix.jrec_type; if (IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(rectype)) { assert(!IS_ZTP(rectype)); keystr = (jnl_string *)&rec->jrec_set_kill.mumps_node; /* Assert that ZTWORMHOLE and LGTRIG type record too has same layout as KILL/SET */ assert((sm_uc_ptr_t)keystr == (sm_uc_ptr_t)&rec->jrec_ztworm.ztworm_str); assert((sm_uc_ptr_t)keystr == (sm_uc_ptr_t)&rec->jrec_lgtrig.lgtrig_str); use_new_key = USES_NEW_KEY(jfh); assert(NEEDS_NEW_KEY(jfh, ((jrec_prefix *)b->recbuff)->tn) == use_new_key); MUR_DECRYPT_LOGICAL_RECS( keystr, (use_new_key ? TRUE : jfh->non_null_iv), rec->prefix.forwptr, (use_new_key ? backctl->encr_key_handle2 : backctl->encr_key_handle), gtmcrypt_errno); if (0 != gtmcrypt_errno) GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, rts_error, backctl->jnl_fn_len, backctl->jnl_fn); } } b->reclen = reclen; return SS_NORMAL; } repl_errno = (maxreclen > JREC_PREFIX_SIZE && reclen <= maxreclen) ? EREPL_JNLRECFMT : EREPL_JNLRECINCMPL; if (EREPL_JNLRECFMT == repl_errno) { /* not sure how this can happen. take a dump (fork_n_core) to gather more information */ assert(FALSE); gtm_fork_n_core(); } b->reclen = 0; return repl_errno; } static int open_prev_gener(repl_ctl_element **old_ctl, repl_ctl_element *ctl, seq_num read_seqno) { gtmsource_state_t gtmsource_state_sav; repl_ctl_element *prev_ctl; repl_rctl_elem_t *repl_rctl; if (0 == ctl->repl_buff->fc->jfh->prev_jnl_file_name_length || QWLE(ctl->repl_buff->fc->jfh->start_seqno, read_seqno)) { /* No need to open previous generation, or no previous * generation */ REPL_DPRINT2("No need to open prev gener of %s or no prev gener\n", ctl->jnl_fn); return (0); } repl_ctl_create(old_ctl, ctl->reg, ctl->repl_buff->fc->jfh->prev_jnl_file_name_length, (char *)ctl->repl_buff->fc->jfh->prev_jnl_file_name, FALSE); REPL_DPRINT2("Prev gener file %s opened\n", ctl->repl_buff->fc->jfh->prev_jnl_file_name); prev_ctl = *old_ctl; assert(prev_ctl->reg == ctl->reg); prev_ctl->prev = ctl->prev; prev_ctl->next = ctl; prev_ctl->prev->next = prev_ctl; ctl->prev = prev_ctl; repl_rctl = ctl->repl_rctl; prev_ctl->repl_rctl = repl_rctl; repl_rctl->ctl_start = prev_ctl; GTMSOURCE_SAVE_STATE(gtmsource_state_sav); first_read(prev_ctl); if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Abandoning open_prev_gener\n"); return (0); } if (JNL_FILE_OPEN == prev_ctl->file_state) { prev_ctl->file_state = JNL_FILE_CLOSED; REPL_DPRINT2("open_prev_gener : %s jnl file marked closed\n", prev_ctl->jnl_fn); } else { MARK_CTL_AS_EMPTY(prev_ctl); REPL_DPRINT2("open_prev_gener : %s jnl file marked empty\n", prev_ctl->jnl_fn); } return (1); } static int open_newer_gener_jnlfiles(gd_region *reg, repl_ctl_element *reg_ctl_end) { sgmnt_addrs *csa; repl_ctl_element *new_ctl, *ctl; int jnl_fn_len; char jnl_fn[JNL_NAME_SIZE]; int nopen, n; boolean_t do_jnl_ensure_open; gd_id_ptr_t reg_ctl_end_id; gtmsource_state_t gtmsource_state_sav; repl_rctl_elem_t *repl_rctl; jnl_private_control *jpc; /* Attempt to open newer generation journal files. Return the number of new files opened. Create new * ctl element(s) for each newer generation and attach at reg_ctl_end. Work backwards from the current journal file. */ nopen = 0; csa = &FILE_INFO(reg)->s_addrs; /* If no journal file switch happened since the last time we did a "jnl_ensure_open" on this region, do a "cycle" * check (lightweight) and avoid the heavyweight "jnl_ensure_open" call. */ jpc = csa->jnl; if (!JNL_FILE_SWITCHED(jpc)) return nopen; reg_ctl_end_id = ®_ctl_end->repl_buff->fc->id; /* Note that at this point, journaling might have been turned OFF (e.g. REPL_WAS_ON state) in which case * JNL_GDID_PTR(csa) would have been nullified by jnl_file_lost. Therefore comparing with that is not a good idea * to use the "id" to check if the journal file remains same (this was done previously). Instead use the ID of * the current reg_ctl_end and the NAME of the newly opened journal file. Because we don't have crit, we cannot * safely read the journal file name from the file header therefore we invoke repl_ctl_create unconditionally * (that has safety protections for crit) and use the new_ctl that it returns to access the journal file name * returned and use that for the ID to NAME comparison. */ jnl_fn_len = 0; jnl_fn[0] = '\0'; for (do_jnl_ensure_open = TRUE; ; do_jnl_ensure_open = FALSE) { repl_ctl_create(&new_ctl, reg, jnl_fn_len, jnl_fn, do_jnl_ensure_open); if (do_jnl_ensure_open && is_gdid_gdid_identical(reg_ctl_end_id, &new_ctl->repl_buff->fc->id)) { /* Current journal file in db file header has been opened ALREADY by source server. Return right away */ assert(0 == nopen); repl_ctl_close(new_ctl); return nopen; } nopen++; REPL_DPRINT2("Newer generation file %s opened\n", new_ctl->jnl_fn); new_ctl->prev = reg_ctl_end; new_ctl->next = reg_ctl_end->next; if (NULL != new_ctl->next) new_ctl->next->prev = new_ctl; reg_ctl_end->next = new_ctl; repl_rctl = reg_ctl_end->repl_rctl; new_ctl->repl_rctl = repl_rctl; jnl_fn_len = new_ctl->repl_buff->fc->jfh->prev_jnl_file_name_length; memcpy(jnl_fn, new_ctl->repl_buff->fc->jfh->prev_jnl_file_name, jnl_fn_len); jnl_fn[jnl_fn_len] = '\0'; if ('\0' == jnl_fn[0]) { /* prev link has been cut, can't follow path back from latest generation jnlfile to the latest we had opened */ RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_NOPREVLINK, 2, new_ctl->jnl_fn_len, new_ctl->jnl_fn); } if (is_gdid_file_identical(reg_ctl_end_id, jnl_fn, jnl_fn_len)) break; } /* Name of the journal file corresponding to reg_ctl_end might have changed. Update the name. * Since inode info doesn't change when a file is renamed, it is not necessary to close and reopen the file. */ reg_ctl_end->jnl_fn[reg_ctl_end->jnl_fn_len] = '\0'; /* For safety */ jnl_fn[jnl_fn_len] = '\0'; if (STRCMP(reg_ctl_end->jnl_fn, jnl_fn) != 0) /* Name has changed */ { REPL_DPRINT3("Detected name change of %s to %s\n", reg_ctl_end->jnl_fn, jnl_fn); reg_ctl_end->jnl_fn_len = reg_ctl_end->reg->jnl_file_len = jnl_fn_len; memcpy(reg_ctl_end->jnl_fn, jnl_fn, jnl_fn_len); memcpy(reg_ctl_end->reg->jnl_file_name, jnl_fn, jnl_fn_len); } /* Except the latest generation, mark the newly opened future generations CLOSED, or EMPTY. * We assume that when a new file is opened, the previous generation has been flushed to disk fully. */ for (ctl = reg_ctl_end, n = nopen; n; n--, ctl = ctl->next) { if (JNL_FILE_UNREAD == ctl->file_state) { GTMSOURCE_SAVE_STATE(gtmsource_state_sav); first_read(ctl); if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Abandoning open_newer_gener_jnlfiles (first_read)\n"); return 0; } } else { assert(JNL_FILE_OPEN == ctl->file_state); GTMSOURCE_SAVE_STATE(gtmsource_state_sav); if (update_max_seqno_info(ctl) != SS_NORMAL) { assert(repl_errno == EREPL_JNLEARLYEOF); assertpro(FALSE); /* Program bug */ } if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Abandoning open_newer_gener_jnlfiles (update_max)\n"); return 0; } } if (JNL_FILE_UNREAD == ctl->file_state) { MARK_CTL_AS_EMPTY(ctl); REPL_DPRINT2("Open_newer_gener_files : %s marked empty\n", ctl->jnl_fn); } else { assert(JNL_FILE_OPEN == ctl->file_state); ctl->file_state = JNL_FILE_CLOSED; REPL_DPRINT2("Open_newer_gener_files : %s marked closed\n", ctl->jnl_fn); } } return nopen; } static int update_eof_addr(repl_ctl_element *ctl, int *eof_change) { repl_file_control_t *fc; uint4 prev_eof_addr, new_eof_addr; int status; sgmnt_addrs *csa; repl_buff_t *rb; assert(!ctl->eof_addr_final); /* The caller should invoke us ONLY if ctl->eof_addr_final is FALSE */ csa = &FILE_INFO(ctl->reg)->s_addrs; rb = ctl->repl_buff; fc = rb->fc; prev_eof_addr = fc->eof_addr; *eof_change = 0; if (is_gdid_gdid_identical(&fc->id, JNL_GDID_PTR(csa))) { new_eof_addr = csa->jnl->jnl_buff->dskaddr; REPL_DPRINT3("Update EOF : New EOF addr from SHM for %s is %u\n", ctl->jnl_fn, new_eof_addr); } else { REPL_DPRINT2("Update EOF : New EOF addr will be found from jnl file hdr for %s\n", ctl->jnl_fn); REPL_DPRINT3("Update EOF : FC ID IS %u %d\n", fc->id.inode, fc->id.device); REPL_DPRINT3("Update EOF : csa->nl->jnl_file.u (unreliable) is %u %d\n", csa->nl->jnl_file.u.inode, csa->nl->jnl_file.u.device); if (!ctl->eof_addr_final) { F_READ_BLK_ALIGNED(fc->fd, 0, fc->jfh, ROUND_UP2(REAL_JNL_HDR_LEN, fc->fs_block_size), status); if (SS_NORMAL != status) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(9) ERR_REPLFILIOERR, 2, ctl->jnl_fn_len, ctl->jnl_fn, ERR_TEXT, 2, LEN_AND_LIT("Error reading journal fileheader to update EOF address"), status); REPL_DPRINT2("Update EOF : Jnl file hdr refreshed from file for %s\n", ctl->jnl_fn); ctl->eof_addr_final = TRUE; /* No more updates to fc->eof_addr for this journal file */ } new_eof_addr = REAL_END_OF_DATA(fc); REPL_DPRINT3("Update EOF : New EOF addr from jfh for %s is %u\n", ctl->jnl_fn, new_eof_addr); } /* ensure that new_eof_addr is not less than prev_eof_addr. * the only scenario where this need not be TRUE is the following case * (i) if new_eof_addr == (journal file header's end_of_data + size of eof record) and * (ii) if prev_eof_addr == the next REPL_BLKSIZE boundary (since source server reads in chunks of REPL_BLKSIZE) * in the above case, source server's read of REPL_BLKSIZE bytes while trying to read at offset end_of_data would * have succeeded since the whole REPL_BLKSIZE block has been allocated in the journal file both in Unix and VMS. * but only the first EOF_RECLEN bytes of that block is valid data. * For the case new_eof_addr < prev_eof_addr, the assert could have been * DIVIDE_ROUND_UP(prev_eof_addr, REPL_BLKSIZE(rb)) == DIVIDE_ROUND_UP(new_eof_addr, REPL_BLKSIZE(rb)), but we use * DIVIDE_ROUND_DOWN and account for prev_eof_addr being an exact multiple of REPL_BLKSIZE(rb) to avoid 4G overflow */ assert((new_eof_addr >= prev_eof_addr) || (DIVIDE_ROUND_DOWN(prev_eof_addr, REPL_BLKSIZE(rb)) - ((0 == prev_eof_addr % REPL_BLKSIZE(rb)) ? 1 : 0) == DIVIDE_ROUND_DOWN(new_eof_addr, REPL_BLKSIZE(rb)))); fc->eof_addr = new_eof_addr; /* eof_change calculated below is not used anywhere. In case it needs to be used, the below calculation * has to be reexamined in light of the above assert involving new_eof_addr and prev_eof_addr * although the code below is dead for now, it can be used for future performance enhancements. */ *eof_change = new_eof_addr > prev_eof_addr ? (int4)(new_eof_addr - prev_eof_addr) : -(int4)(prev_eof_addr - new_eof_addr); /* Above computation done that way because the variables involved are unsigned */ return (SS_NORMAL); } static int force_file_read(repl_ctl_element *ctl) { /* The journal file may have grown since we last read it. A previously read EOF record may have been over-written on disk. * Reposition read pointers so that a file read is forced if the current read pointers are positioned at a EOF record. */ repl_buff_t *rb; repl_buff_desc *b; repl_file_control_t *fc; enum jnl_record_type rectype; rb = ctl->repl_buff; b = &rb->buff[REPL_MAINBUFF]; fc = rb->fc; if (b->reclen == 0 || b->recaddr == b->readaddr || b->buffremaining == 0 || b->buffremaining == REPL_BLKSIZE(rb)) { /* A file read will be forced anyway */ return (SS_NORMAL); } /* b->recbuff points to valid record */ rectype = (enum jnl_record_type)((jrec_prefix *)b->recbuff)->jrec_type; assert(rectype > JRT_BAD && rectype <= JRT_RECTYPES); if (rectype != JRT_EOF) /* Can't be stale */ return (SS_NORMAL); assert(b->reclen == EOF_RECLEN); assert(b->readaddr - b->recaddr >= EOF_RECLEN); b->buffremaining += (b->readaddr - b->recaddr); b->readaddr = b->recaddr; b->reclen = 0; return (SS_NORMAL); } static int update_max_seqno_info(repl_ctl_element *ctl) { /* The information in ctl is outdated. The journal file has grown. Update max_seqno and its dskaddr */ int eof_change; repl_buff_t *rb; repl_buff_desc *b; repl_file_control_t *fc; uint4 dskread, max_seqno_eof_addr, stop_at; boolean_t eof_addr_final, max_seqno_found; uint4 max_seqno_addr; seq_num max_seqno, reg_seqno; int status; enum jnl_record_type rectype; gd_region *reg; sgmnt_addrs *csa; int wait_for_jnl = 0; gtmsource_state_t gtmsource_state_sav; assert(ctl->file_state == JNL_FILE_OPEN); rb = ctl->repl_buff; fc = rb->fc; reg = ctl->reg; csa = &FILE_INFO(reg)->s_addrs; if (!ctl->eof_addr_final) update_eof_addr(ctl, &eof_change); # ifdef GTMSOURCE_SKIP_DSKREAD_WHEN_NO_EOF_CHANGE /* This piece of code needs some more testing - Vinaya 03/11/98 */ if ((0 == eof_change) && ctl->first_read_done) { REPL_DPRINT2("UPDATE MAX SEQNO INFO: No change in EOF addr. Skipping disk read for %s\n", ctl->jnl_fn); return (SS_NORMAL); } # endif /* Store/cache fc->eof_addr in local variable. It is possible the "repl_next" calls done below invoke "repl_read_file" * which in turn invokes "update_eof_addr" and modify fc->eof_addr. But we limit our max_seqno search to * the stored value of fc->eof_addr. Any changes to fc->eof_addr inside this function cause corresponding * changes to ctl->max_seqno_eof_addr in subsequent calls to "update_max_seqno_info". Store copy of ctl->eof_eof_addr_final * at the same time since ctl->eof_addr_final and ctl->eof_addr are maintained in sync. */ max_seqno_eof_addr = fc->eof_addr; eof_addr_final = ctl->eof_addr_final; if (JNL_FILE_FIRST_RECORD == max_seqno_eof_addr) { /* Journal file only has the journal header, no journal records. That case should have been caught earlier * when the journal file was first open. */ assert(FALSE); repl_errno = EREPL_JNLEARLYEOF; return (repl_errno); } QWASSIGN(reg_seqno, csa->hdr->reg_seqno); QWDECRBYDW(reg_seqno, 1); assert(!ctl->max_seqno_final || eof_addr_final); if ((ctl->max_seqno >= reg_seqno) || (ctl->max_seqno_eof_addr == max_seqno_eof_addr) || (ctl->max_seqno_final && ctl->first_read_done)) { /* have searched already */ REPL_DPRINT4("UPDATE MAX SEQNO INFO : not reading file %s; max_seqno = "INT8_FMT", reg_seqno = "INT8_FMT"\n", ctl->jnl_fn, INT8_PRINT(ctl->max_seqno), INT8_PRINT(reg_seqno)); return (SS_NORMAL); } rb->buffindex = REPL_SCRATCHBUFF; /* temporarily set to scratch buffer */ b = &rb->buff[rb->buffindex]; dskread = ROUND_DOWN(max_seqno_eof_addr, REPL_BLKSIZE(rb)); if (dskread == max_seqno_eof_addr) dskread -= REPL_BLKSIZE(rb); max_seqno = 0; max_seqno_addr = 0; max_seqno_found = FALSE; do { /* Ignore the existing contents of scratch buffer */ b->buffremaining = REPL_BLKSIZE(rb); b->recbuff = b->base; b->reclen = 0; b->readaddr = b->recaddr = JNL_BLK_DSKADDR(dskread, REPL_BLKSIZE(rb)); if (JNL_FILE_FIRST_RECORD == b->readaddr && SS_NORMAL != adjust_buff_leaving_hdr(rb)) { assert(repl_errno == EREPL_BUFFNOTFRESH); assertpro(FALSE); /* Program bug */ } stop_at = dskread + MIN(REPL_BLKSIZE(rb), max_seqno_eof_addr - dskread); /* Limit search to this block */ /* If we don't limit the search, we may end up re-reading blocks that follow this block. The consequence of * limiting the search is that we may not find the maximum close to the current state, but some time in the past * for a file that is growing. We can live with that as we will redo the max search if necessary */ assert(stop_at > dskread); while (b->reclen < stop_at - b->recaddr) { if (SS_NORMAL == (status = repl_next(rb))) { rectype = (enum jnl_record_type)((jrec_prefix *)b->recbuff)->jrec_type; if (IS_REPLICATED(rectype)) { max_seqno = GET_JNL_SEQNO(b->recbuff); max_seqno_addr = b->recaddr; } else if (JRT_EOF == rectype) break; } else if (EREPL_JNLRECINCMPL == status) { /* It is possible to get this return value if jb->dskaddr is in the middle of a journal record. Log * warning message for every certain number of attempts. There might have been a crash and the file * might have been corrupted. The file possibly might never grow. Such cases have to be detected * and attempt to read such files aborted. If the journal file is a previous generation, waste no * time waiting for journal records to be written, but error out right away. That should never * happen, hence the assert and gtm_fork_n_core. */ if (ctl->eof_addr_final) { /* EREPL_JNLRECINCMPL is also possible if there is no more records left in the journal * file. If this happens with a previous generation journal file, there is no point in * retrying the read from this block as the file will never grow. Add asserts to ensure * to ensure this (that we have reached the end of previous generation journal file) and * create a core otherwise (for diagnostic purposes). */ if ((b->readaddr != b->recaddr) || (b->recaddr != REAL_END_OF_DATA(fc))) { assert(FALSE); gtm_fork_n_core(); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(6) ERR_JNLRECINCMPL, 4, b->recaddr, ctl->jnl_fn_len, ctl->jnl_fn, &ctl->seqno); } break; } assert(GTMSOURCE_WAITING_FOR_XON != gtmsource_state); if (gtmsource_recv_ctl_nowait()) { repl_log(gtmsource_log_fp, TRUE, TRUE, "State change detected in update_max_seqno_info\n"); rb->buffindex = REPL_MAINBUFF; /* reset back to the main buffer */ gtmsource_set_lookback(); /* In case we read ahead, enable looking back. */ return (SS_NORMAL); } GTMSOURCE_SAVE_STATE(gtmsource_state_sav); gtmsource_poll_actions(TRUE); if(GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "State change detected in update_max_seqno_info (poll)\n"); rb->buffindex = REPL_MAINBUFF; /* reset back to the main buffer */ gtmsource_set_lookback(); /* In case we read ahead, enable looking back. */ return (SS_NORMAL); } SHORT_SLEEP(GTMSOURCE_WAIT_FOR_JNL_RECS); wait_for_jnl += GTMSOURCE_WAIT_FOR_JNL_RECS; if (0 == (wait_for_jnl % LOG_WAIT_FOR_JNL_FLUSH_PERIOD)) GTMSRC_DO_JNL_FLUSH_IF_POSSIBLE(ctl, csa); /* See if a "jnl_flush" might help nudge */ if (0 == (wait_for_jnl % LOG_WAIT_FOR_JNL_RECS_PERIOD)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL_WARN : Source server waited %u seconds for" " journal record(s) to be written to journal file %s while attempting to read " "seqno %llu [0x%llx]. [dskaddr 0x%x freeaddr 0x%x rsrv_freeaddr 0x%x]." " Check for problems with journaling\n", wait_for_jnl / 1000, ctl->jnl_fn, ctl->seqno, ctl->seqno, csa->jnl->jnl_buff->dskaddr, csa->jnl->jnl_buff->freeaddr, csa->jnl->jnl_buff->rsrv_freeaddr); } } else { assertpro(EREPL_JNLRECFMT == status); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(5) ERR_JNLBADRECFMT, 3, ctl->jnl_fn_len, ctl->jnl_fn, b->recaddr); } } if ((max_seqno_found = (0 != max_seqno)) || (0 == dskread)) break; assert(REPL_BLKSIZE(rb) <= dskread); dskread -= REPL_BLKSIZE(rb); } while (TRUE); rb->buffindex = REPL_MAINBUFF; /* reset back to the main buffer */ if (max_seqno_found) { /* Assert that there is some progress in this call to "update_max_seqno_info" compared to the previous call */ assert((max_seqno_eof_addr > ctl->max_seqno_eof_addr) || (max_seqno_addr > ctl->max_seqno_dskaddr) || (max_seqno > ctl->max_seqno)); CTL_SET_MAX_SEQNO(ctl, max_seqno, max_seqno_addr, max_seqno_eof_addr, DO_EOF_ADDR_CHECK); if (eof_addr_final) { /* Do not use ctl->eof_addr_final in the check above since it could have changed inside this function. * We want to set all max_seqno* fields based on fc->eof_addr after update_eof_addr * done at beginning of this function. This keeps all of them in sync with each other. */ ctl->max_seqno_final = TRUE; /* No more max_seqno updates as this journal file has switched */ } return (SS_NORMAL); } /* Two possibilities to get here : * 1- No replicatable journal record found. Shouldn't happen since it should have already looked for min seq. number. * 2- Max seq. number found is 0. This is possible only if the journal file has been created with replication turned OFF. * That should have been detected earlier when opening the journal file. */ assert(FALSE); repl_errno = EREPL_JNLEARLYEOF; return (repl_errno); } static int adjust_buff_leaving_hdr(repl_buff_t *rb) { /* The first alignsize bytes to be read from jnl file. Adjust fields in rb->buff[rb->buffindex] to skip jnl file header */ repl_buff_desc *b; if (REPL_BLKSIZE(rb) <= JNL_FILE_FIRST_RECORD) return (SS_NORMAL); b = &rb->buff[rb->buffindex]; if (b->buffremaining < REPL_BLKSIZE(rb)) { repl_errno = EREPL_BUFFNOTFRESH; return (repl_errno); } memset(b->base, 0, JNL_FILE_FIRST_RECORD); b->recbuff = b->base + JNL_FILE_FIRST_RECORD; b->reclen = 0; b->recaddr = b->readaddr = JNL_FILE_FIRST_RECORD; b->buffremaining -= JNL_FILE_FIRST_RECORD; return (SS_NORMAL); } static int first_read(repl_ctl_element *ctl) { /* The first read from this generation of the journal file. Find the min_seqno, max_seqno. * If EOF is found while searching for min_seqno, or, nothing could be read from this file, * this is an empty journal file, probably will be written to later. * Position the next read at min_seqno. Update the max_seqno_info. */ int status, eof_change; enum jnl_record_type rectype; repl_buff_t *rb; repl_buff_desc *b; repl_file_control_t *fc; boolean_t min_seqno_found; unsigned char seq_num_str[32], *seq_num_ptr; /* INT8_PRINT */ gtmsource_state_t gtmsource_state_sav; assert(JNL_FILE_UNREAD == ctl->file_state); rb = ctl->repl_buff; assert(rb->buffindex == REPL_MAINBUFF); b = &rb->buff[rb->buffindex]; fc = rb->fc; /* Ignore the existing contents of the buffer */ b->buffremaining = REPL_BLKSIZE(rb); b->recbuff = b->base; b->reclen = 0; b->readaddr = JNL_FILE_FIRST_RECORD; b->recaddr = JNL_FILE_FIRST_RECORD; if (adjust_buff_leaving_hdr(rb) != SS_NORMAL) { assert(repl_errno == EREPL_BUFFNOTFRESH); assertpro(FALSE); /* Program bug */ } min_seqno_found = FALSE; /* Since this is first time we are reading from this journal file, initialize fields cared for in CTL_SET_MAX_SEQNO macro */ DEBUG_ONLY(ctl->max_seqno = ctl->max_seqno_dskaddr = ctl->max_seqno_eof_addr = 0;) while (!min_seqno_found) { if ((status = repl_next(rb)) == SS_NORMAL) { rectype = (enum jnl_record_type)((jrec_prefix *)b->recbuff)->jrec_type; if (IS_REPLICATED(rectype)) { ctl->min_seqno = GET_JNL_SEQNO(b->recbuff); ctl->min_seqno_dskaddr = b->recaddr; ctl->seqno = ctl->min_seqno; ctl->tn = ((jrec_prefix *)b->recbuff)->tn; /* Since update_eof_addr has not yet been called (will be done as part of "update_max_seqno_info" * at the end of this function), skip the fc->eof_addr check inside the below macro. */ CTL_SET_MAX_SEQNO(ctl, ctl->min_seqno, b->recaddr, b->recaddr, SKIP_EOF_ADDR_CHECK); ctl->file_state = JNL_FILE_OPEN; min_seqno_found = TRUE; } else if (rectype == JRT_EOF) { ctl->first_read_done = TRUE; REPL_DPRINT2("JRT_EOF read when looking for first replication record. Empty file %s\n", ctl->jnl_fn); return (SS_NORMAL); } } else if (status == EREPL_JNLRECINCMPL) /* Nothing read */ { ctl->first_read_done = TRUE; REPL_DPRINT2("Nothing read when looking for first replication record. Empty file %s\n", ctl->jnl_fn); return (SS_NORMAL); } else { if (status == EREPL_JNLRECFMT) RTS_ERROR_CSA_ABT(&FILE_INFO(ctl->reg)->s_addrs, VARLSTCNT(5) ERR_JNLBADRECFMT, 3, ctl->jnl_fn_len, ctl->jnl_fn, b->recaddr); } } REPL_DPRINT5("FIRST READ of %s - Min seqno "INT8_FMT" min_seqno_dskaddr %u EOF addr %u\n", ctl->jnl_fn, INT8_PRINT(ctl->min_seqno), ctl->min_seqno_dskaddr, ctl->repl_buff->fc->eof_addr); GTMSOURCE_SAVE_STATE(gtmsource_state_sav); if (update_max_seqno_info(ctl) != SS_NORMAL) { assert(repl_errno == EREPL_JNLEARLYEOF); assertpro(FALSE); /* Program bug */ } if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Abandoning first_read\n"); /* file_state was set to JNL_FILE_OPEN above; reset it here. */ ctl->file_state = JNL_FILE_UNREAD; return (SS_NORMAL); } REPL_DPRINT5("FIRST READ of %s - Max seqno "INT8_FMT" max_seqno_dskaddr %u EOF addr %d\n", ctl->jnl_fn, INT8_PRINT(ctl->max_seqno), ctl->max_seqno_dskaddr, ctl->repl_buff->fc->eof_addr); ctl->first_read_done = TRUE; return (SS_NORMAL); } static void increase_buffer(unsigned char **buff, int *buflen, int buffer_needed) { int alloc_status, expandsize, newbuffsize; unsigned char *old_msgp; /* The tr size is not known apriori. Hence, a good guess of 1.5 times the current buffer space is used */ expandsize = (gtmsource_msgbufsiz >> 1); if (expandsize < buffer_needed) expandsize = buffer_needed; newbuffsize = gtmsource_msgbufsiz + expandsize; REPL_DPRINT3("Buff space shortage. Attempting to increase buff space. Curr buff space %d. Attempt increase to atleast %d\n", gtmsource_msgbufsiz, newbuffsize); old_msgp = (unsigned char *)gtmsource_msgp; if ((alloc_status = gtmsource_alloc_msgbuff(newbuffsize, FALSE)) != SS_NORMAL) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, LEN_AND_LIT("Error extending buffer space while reading files. Malloc error"), alloc_status); } REPL_DPRINT3("Old gtmsource_msgp = 0x%llx; New gtmsource_msgp = 0x%llx\n", old_msgp, gtmsource_msgp); REPL_DPRINT2("Old *buff = 0x%llx\n", *buff); *buff = (unsigned char *)gtmsource_msgp + (*buff - old_msgp); REPL_DPRINT2("New *buff = 0x%llx\n", *buff); *buflen =(int)(gtmsource_msgbufsiz - (*buff - (unsigned char *)gtmsource_msgp)); REPL_DPRINT2("New remaining len = %ld\n", *buflen); return; } static int read_transaction(repl_ctl_element *ctl, unsigned char **buff, int *bufsiz, seq_num read_jnl_seqno) { /* Read the transaction ctl->seqno into buff. Position the next read at the next seqno in the journal file. * Update max_seqno if necessary. If read of the next seqno blocks, leave the read buffers as is. * The next time when this journal file is accessed the read will move forward. */ repl_buff_t *rb; repl_buff_desc *b; repl_file_control_t *fc; int readlen; seq_num rec_jnl_seqno; enum jnl_record_type rectype; int status; seq_num read_seqno; unsigned char *seq_num_ptr, seq_num_str[32]; /* INT8_PRINT */ sgmnt_addrs *csa; gtmsource_state_t gtmsource_state_sav; repl_rctl_elem_t *repl_rctl; rb = ctl->repl_buff; assert(rb->buffindex == REPL_MAINBUFF); b = &rb->buff[rb->buffindex]; fc = rb->fc; repl_rctl = ctl->repl_rctl; assert(FALSE == repl_rctl->read_complete); csa = &FILE_INFO(ctl->reg)->s_addrs; readlen = 0; assert(0 != b->reclen); if (b->reclen > *bufsiz) increase_buffer(buff, bufsiz, b->reclen); assert(b->reclen <= *bufsiz); memcpy(*buff, b->recbuff, b->reclen); *buff += b->reclen; readlen += b->reclen; assert(readlen % JNL_WRT_END_MODULUS == 0); *bufsiz -= b->reclen; rectype = (enum jnl_record_type)((jrec_prefix *)b->recbuff)->jrec_type; assert(IS_REPLICATED(rectype)); rec_jnl_seqno = GET_REPL_JNL_SEQNO(b->recbuff); assert(QWEQ(rec_jnl_seqno, ctl->seqno)); if (rec_jnl_seqno > ctl->max_seqno) CTL_SET_MAX_SEQNO(ctl, rec_jnl_seqno, b->recaddr, b->recaddr, DO_EOF_ADDR_CHECK); ctl->tn = ((jrec_prefix *)b->recbuff)->tn; if (!IS_FENCED(rectype) || JRT_NULL == rectype) { /* Entire transaction done */ repl_rctl->read_complete = TRUE; trans_read = TRUE; } else { assert(IS_TUPD(rectype) || IS_FUPD(rectype)); /* The first record should be the beginning of a transaction */ } /* Suggested optimisation : Instead of waiting for all records pertaining to this transaction to * be written to the journal file, read those available, mark this file BLOCKED, read other journal * files, and come back to this journal file later. */ while (!repl_rctl->read_complete) /* Read the rest of the transaction */ { if ((status = repl_next(rb)) == SS_NORMAL) { rectype = (enum jnl_record_type)((jrec_prefix *)b->recbuff)->jrec_type; if (IS_REPLICATED(rectype)) { if (b->reclen > *bufsiz) increase_buffer(buff, bufsiz, b->reclen); assert(b->reclen <= *bufsiz); if (rectype != JRT_TCOM && rectype != JRT_ZTCOM) { memcpy(*buff, b->recbuff, b->reclen); *buff += b->reclen; readlen += b->reclen; assert(readlen % JNL_WRT_END_MODULUS == 0); *bufsiz -= b->reclen; } else { memcpy(tcombuffp, b->recbuff, b->reclen); tcombuffp += b->reclen; tot_tcom_len += b->reclen; /* End of transaction in this file */ repl_rctl->read_complete = TRUE; if (num_tcom == -1) num_tcom = ((jnl_record *)b->recbuff)->jrec_tcom.num_participants; num_tcom--; if (num_tcom == 0) /* Read the whole trans */ trans_read = TRUE; } rec_jnl_seqno = GET_JNL_SEQNO(b->recbuff); assert(rec_jnl_seqno == ctl->seqno); if (rec_jnl_seqno > ctl->max_seqno) CTL_SET_MAX_SEQNO(ctl, rec_jnl_seqno, b->recaddr, b->recaddr, DO_EOF_ADDR_CHECK); ctl->tn = ((jrec_prefix *)b->recbuff)->tn; } else if (rectype == JRT_EOF) { assert(FALSE); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(7) ERR_REPLBRKNTRANS, 1, &read_jnl_seqno, ERR_TEXT, 2, LEN_AND_LIT("Early EOF found")); } } else if (status == EREPL_JNLRECINCMPL) { /* Log warning message for every certain number of attempts. There might have been a crash and the file * might have been corrupted. The file possibly might never grow. Such cases have to be detected and * attempt to read such files aborted. If the journal file is a previous generation, waste no time waiting * for journal records to be written, but error out right away. That should never happen, hence the assert * and gtm_fork_n_core. */ if (ctl->eof_addr_final) { assert(FALSE); gtm_fork_n_core(); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(6) ERR_JNLRECINCMPL, 4, b->recaddr, ctl->jnl_fn_len, ctl->jnl_fn, &ctl->seqno); } if (gtmsource_recv_ctl_nowait()) { repl_log(gtmsource_log_fp, TRUE, TRUE, "State change detected in read_transaction\n"); gtmsource_set_lookback(); /* In case we read ahead, enable looking back. */ return 0; } GTMSOURCE_SAVE_STATE(gtmsource_state_sav); gtmsource_poll_actions(TRUE); if(GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "State change detected in read_transaction (poll)\n"); gtmsource_set_lookback(); /* In case we read ahead, enable looking back. */ return 0; } SHORT_SLEEP(GTMSOURCE_WAIT_FOR_JNL_RECS); total_wait_for_jnl_recs += GTMSOURCE_WAIT_FOR_JNL_RECS; if (0 == (total_wait_for_jnl_recs % LOG_WAIT_FOR_JNL_FLUSH_PERIOD)) GTMSRC_DO_JNL_FLUSH_IF_POSSIBLE(ctl, csa); /* See if a "jnl_flush" might help nudge */ if (0 == (total_wait_for_jnl_recs % LOG_WAIT_FOR_JNL_RECS_PERIOD)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL_WARN : Source server waited %u seconds for journal " "record(s) to be written to journal file %s while attempting to read seqno %llu [0x%llx]. " "[dskaddr 0x%x freeaddr 0x%x rsrv_freeaddr 0x%x]. Check for problems with journaling\n", total_wait_for_jnl_recs / 1000, ctl->jnl_fn, ctl->seqno, ctl->seqno, csa->jnl->jnl_buff->dskaddr, csa->jnl->jnl_buff->freeaddr, csa->jnl->jnl_buff->rsrv_freeaddr); } } else { assertpro(status == EREPL_JNLRECFMT); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(5) ERR_JNLBADRECFMT, 3, ctl->jnl_fn_len, ctl->jnl_fn, b->recaddr); } } /* Try positioning next read to the next seqno. Leave it as is if operation blocks (has to wait for records) */ QWADD(read_seqno, ctl->seqno, seq_num_one); position_read(ctl, read_seqno); return (readlen); } static tr_search_state_t do_linear_search(repl_ctl_element *ctl, uint4 lo_addr, uint4 max_readaddr, seq_num read_seqno, tr_search_status_t *srch_status) { repl_buff_t *rb; repl_buff_desc *b; seq_num rec_jnl_seqno; enum jnl_record_type rectype; tr_search_state_t found; int status; REPL_DPRINT5("do_linear_search: file : %s lo_addr : %u max_readaddr : %u read_seqno : %llu\n", ctl->jnl_fn, lo_addr, max_readaddr, read_seqno); assert(lo_addr < max_readaddr); rb = ctl->repl_buff; assert(rb->buffindex == REPL_MAINBUFF); b = &rb->buff[rb->buffindex]; if (lo_addr != b->recaddr) { /* Initiate a fresh read */ lo_addr = ROUND_DOWN(lo_addr, REPL_BLKSIZE(rb)); b->recaddr = b->readaddr = JNL_BLK_DSKADDR(lo_addr, REPL_BLKSIZE(rb)); b->recbuff = b->base; b->reclen = 0; b->buffremaining = REPL_BLKSIZE(rb); if (b->readaddr == JNL_FILE_FIRST_RECORD && adjust_buff_leaving_hdr(rb) != SS_NORMAL) { assert(repl_errno == EREPL_BUFFNOTFRESH); assertpro(FALSE); /* Program bug */ } REPL_DPRINT1("do_linear_search: initiating fresh read\n"); } else { /* use what has been read already */ assert(read_seqno != ctl->seqno); /* if not, we'll skip to the next transaction and declare read_seqno not found */ } found = TR_NOT_FOUND; srch_status->prev_seqno = srch_status->seqno = 0; while (found == TR_NOT_FOUND && b->reclen < max_readaddr - b->recaddr) { if ((status = repl_next(rb)) == SS_NORMAL) { rectype = (enum jnl_record_type)((jrec_prefix *)b->recbuff)->jrec_type; if (IS_REPLICATED(rectype)) { rec_jnl_seqno = GET_JNL_SEQNO(b->recbuff); if (srch_status->seqno == 0 || srch_status->seqno != rec_jnl_seqno) { /* change srch_status only when records of different transactions are encountered */ srch_status->prev_seqno = srch_status->seqno; srch_status->seqno = rec_jnl_seqno; } if (ctl->max_seqno < rec_jnl_seqno) CTL_SET_MAX_SEQNO(ctl, rec_jnl_seqno, b->recaddr, b->recaddr, SKIP_EOF_ADDR_CHECK); QWASSIGN(ctl->seqno, rec_jnl_seqno); ctl->tn = ((jrec_prefix *)b->recbuff)->tn; if (QWEQ(rec_jnl_seqno, read_seqno)) found = TR_FOUND; else if (QWGT(rec_jnl_seqno, read_seqno)) found = TR_WILL_NOT_BE_FOUND; } else if (rectype == JRT_EOF) found = TR_WILL_NOT_BE_FOUND; } else if (status == EREPL_JNLRECINCMPL) found = TR_FIND_WOULD_BLOCK; else if (status == EREPL_JNLRECFMT) RTS_ERROR_CSA_ABT(&FILE_INFO(ctl->reg)->s_addrs, VARLSTCNT(5) ERR_JNLBADRECFMT, 3, ctl->jnl_fn_len, ctl->jnl_fn, b->recaddr); } REPL_DPRINT2("do_linear_search: returning %s\n", (found == TR_NOT_FOUND) ? "TR_NOT_FOUND" : (found == TR_FOUND) ? "TR_FOUND" : (found == TR_WILL_NOT_BE_FOUND) ? "TR_WILL_NOT_BE_FOUND" : (found == TR_FIND_WOULD_BLOCK) ? "TR_FIND_WOULD_BLOCK" : "*UNKNOWN RETURN CODE*"); return (found); } static tr_search_state_t do_binary_search(repl_ctl_element *ctl, uint4 lo_addr, uint4 hi_addr, seq_num read_seqno, tr_search_status_t *srch_status) { repl_buff_t *rb; repl_buff_desc *b; repl_file_control_t *fc; tr_search_state_t found; uint4 low, high, mid, new_mid, mid_further, stop_at; uint4 srch_half_lo_addr, srch_half_hi_addr, othr_half_lo_addr, othr_half_hi_addr; uint4 hi_addr_mod, hi_addr_diff, mid_mod, mid_diff; uint4 willnotbefound_addr = 0, willnotbefound_stop_at = 0; int iter, max_iter; enum jnl_record_type rectype; boolean_t search_complete = FALSE; REPL_DPRINT5("do_binary_search: file : %s lo_addr : %u hi_addr : %u read_seqno : %llu\n", ctl->jnl_fn, lo_addr, hi_addr, read_seqno); if (lo_addr > hi_addr) { REPL_DPRINT1("do_binary_search: lower limit is larger than upper limit, search not initiated\n"); return TR_NOT_FOUND; } rb = ctl->repl_buff; assert(rb->buffindex == REPL_MAINBUFF); b = &rb->buff[rb->buffindex]; fc = rb->fc; assert(lo_addr < fc->eof_addr); assert(hi_addr <= fc->eof_addr); assert(lo_addr <= hi_addr); low = ROUND_DOWN(lo_addr, REPL_BLKSIZE(rb)); if (0 == (hi_addr_mod = hi_addr % REPL_BLKSIZE(rb))) hi_addr_mod = REPL_BLKSIZE(rb); /* avoid including additional block if already aligned */ hi_addr_diff = fc->eof_addr - hi_addr; high = hi_addr + MIN(REPL_BLKSIZE(rb) - hi_addr_mod, hi_addr_diff); for (found = TR_NOT_FOUND, mid = ROUND_DOWN((low >> 1) + (high >> 1), REPL_BLKSIZE(rb)); ; ) { mid_mod = mid % REPL_BLKSIZE(rb); mid_diff = fc->eof_addr - mid; assert(0 != mid_diff); stop_at = mid + MIN(REPL_BLKSIZE(rb) - mid_mod, mid_diff); /* Note that for high values of journal-alignsize (which is what REPL_BLKSIZE macro expands to), it is * possible that stop_at is GREATER than high. Since it is not necessary to search any further than high, * limit it accordingly before calling the linear search. */ found = do_linear_search(ctl, mid, MIN(stop_at, high), read_seqno, srch_status); assert(srch_status->seqno == 0 || srch_status->seqno == ctl->seqno); switch (found) { case TR_FOUND: rectype = (enum jnl_record_type)((jrec_prefix *)b->recbuff)->jrec_type; if (!IS_FENCED(rectype) || IS_TUPD(rectype) || IS_FUPD(rectype) || JRT_NULL == rectype) { REPL_DPRINT4("do_binary_search: found %llu at %u in %s\n", read_seqno, b->recaddr, ctl->jnl_fn); return found; } assert(b->recaddr >= REPL_BLKSIZE(rb)); /* cannot have trailing records of a tr in first block */ low = ((b->recaddr > REPL_BLKSIZE(rb)) ? ROUND_DOWN(b->recaddr - REPL_BLKSIZE(rb), REPL_BLKSIZE(rb)) : 0); high = ROUND_DOWN(b->recaddr, REPL_BLKSIZE(rb)); REPL_DPRINT5("do_binary_search: found record of type %d with seqno %llu at %u in %s\n", rectype, read_seqno, b->recaddr, ctl->jnl_fn); REPL_DPRINT3("do_binary_search: will back off and search for beginning of transaction, changing " "low to %u high to %u\n", low, high); break; case TR_NOT_FOUND: assert(b->readaddr == stop_at); if (srch_status->seqno != 0) { /* at least one replicated record found in block */ assert(read_seqno > srch_status->seqno); low = stop_at; /* search in the upper half */ REPL_DPRINT5("do_binary_search: could not find %llu in block %u, last seen replicated " "record has seqno %llu changing low to %u\n", read_seqno, mid, srch_status->seqno, low); } else { /* no replicated record found in block */ assert(srch_status->prev_seqno == 0); /* must hold if no replicated record found */ REPL_DPRINT3("do_binary_search: could not find %llu, no replicated record in block %u, " "searching lower half", read_seqno, mid); if (low < mid) /* seach lower half recursively */ found = do_binary_search(ctl, low, mid, read_seqno, srch_status); else { REPL_DPRINT4("do_binary_search: all blocks searched in lower half search, " "seqno %llu not found, low %u high %u\n", read_seqno, low, mid); } if (found == TR_NOT_FOUND && stop_at < high) /* search upper half recursively */ found = do_binary_search(ctl, stop_at, high, read_seqno, srch_status); else { REPL_DEBUG_ONLY( if (found == TR_NOT_FOUND) { REPL_DPRINT4("do_binary_search: all blocks searched in upper half" " search, seqno %llu not found, low %u high %u\n", read_seqno, stop_at, high); } ) } if (found != TR_NOT_FOUND) return found; search_complete = TRUE; } break; case TR_WILL_NOT_BE_FOUND: if (srch_status->seqno != 0 && read_seqno < srch_status->seqno) { if (srch_status->prev_seqno == 0) { /* first replicated record in block has larger seqno than read_seqno */ REPL_DPRINT5("do_binary_search: will not find %llu, first replicated record in " "block %u has seqno %llu, changing high to %u\n", read_seqno, mid, srch_status->seqno, mid); willnotbefound_addr = mid; willnotbefound_stop_at = stop_at; high = mid; /* search in the lower half */ } else { /* at least two replicated records found, and the read_seqno is between the seqno * numbers of two records */ REPL_DPRINT5("do_binary_search: will not find %llu in block %u, last two seqnos are" " %llu and %llu, return WILL_NOT_BE_FOUND\n", read_seqno, mid, srch_status->prev_seqno, srch_status->seqno); assert(srch_status->prev_seqno < read_seqno); return found; /* read_seqno is not in this journal file */ } } else { /* found EOF record, read_seqno is not in this journal file */ REPL_DPRINT3("do_binary_search: will not find %llu in block %u, EOF found\n", read_seqno, mid); return found; } break; case TR_FIND_WOULD_BLOCK: assert(mid == ROUND_DOWN(high, REPL_BLKSIZE(rb))); /* must be the last block for this retval */ assert(ctl->file_state == JNL_FILE_OPEN || /* file that is yet to grow, or truncated file */ ctl->file_state == JNL_FILE_CLOSED && 0 != fc->jfh->prev_recov_end_of_data); if (srch_status->seqno != 0) { /* journal flush yet to be done, or truncated file's end_of_data reached, can't locate seqno */ assert(read_seqno > srch_status->seqno); REPL_DPRINT4("do_binary_search: find of %llu would block, last seqno %llu found in block " "%u\n", read_seqno, srch_status->seqno, mid); return (ctl->file_state == JNL_FILE_OPEN ? found : TR_WILL_NOT_BE_FOUND); } /* no replicated record found in the block, search in previous block(s) */ high = (high > REPL_BLKSIZE(rb)) ? high - REPL_BLKSIZE(rb) : 0; REPL_DPRINT4("do_binary_search: find of %llu would block, no seqno found in block %u, " "change high to %u\n", read_seqno, mid, high); break; default: /* Why didn't we cover all cases? */ assertpro(FALSE); } /* end switch */ if (!search_complete && low < high) { new_mid = ROUND_DOWN((low >> 1) + (high >> 1), REPL_BLKSIZE(rb)); mid_further = (new_mid != mid) ? 0 : REPL_BLKSIZE(rb); /* if necessary, move further to avoid repeat */ if (high - new_mid > mid_further) { mid = new_mid + mid_further; continue; } } REPL_DPRINT6("do_binary_search: all blocks searched, seqno %llu not found, low %u high %u mid %u mid_further %u\n", read_seqno, low, high, mid, mid_further); assert(found != TR_FOUND); /* done searching all blocks between lo_addr and hi_addr */ if (found == TR_NOT_FOUND && 0 != willnotbefound_addr) { /* There is a block that contains a seqno larger than read_seqno; leave ctl positioned at that seqno. * If we don't do this, we may repeat binary search (wastefully) for all seqnos between read_seqno and * the least seqno larger than read_seqno in this file. */ found = do_linear_search(ctl, willnotbefound_addr, willnotbefound_stop_at, read_seqno, srch_status); REPL_DPRINT4("do_binary_search: position at seqno %llu in block [%u, %u)\n", srch_status->seqno, willnotbefound_addr, willnotbefound_stop_at); assert(found == TR_WILL_NOT_BE_FOUND); assert(read_seqno < ctl->seqno); } return found; } /* end for */ } static tr_search_state_t position_read(repl_ctl_element *ctl, seq_num read_seqno) { repl_buff_t *rb; repl_buff_desc *b; uint4 lo_addr, hi_addr; tr_search_state_t found, (*srch_func)(); tr_search_status_t srch_status; uint4 willnotbefound_addr = 0, willnotbefound_stop_at = 0; int eof_change; DEBUG_ONLY(jnl_record *jrec;) DEBUG_ONLY(enum jnl_record_type rectype;) /* Position read pointers so that the next read should get the first journal record with JNL_SEQNO atleast read_seqno. * Do a search between min_seqno and seqno if read_seqno < ctl->seqno; else search from ctl->seqno onwards. * If ctl->seqno > ctl->max_seqno update max_seqno as you move, else search between ctl->seqno and ctl->max_seqno. * We want to do a binary search here. */ /* If the receiver disconnects while we were previously in the read-file mode and later reconnects requesting a * particular sequence number, it is possible that we can have an out-of-date fc->eof_addr as fc->eof_addr is not * frequently updated. But, now that we will be searching for the sequence number (either with binary or linear * search) update the fc->eof_addr unconditionally. This is considered okay since the update_eof_addr function * is a almost always a light weight function (with the only exception being the case when the jnl file source * server is interested in is not the latest generation file then it could end up reading the journal file header * from disk but that too it does only once per older generation journal file so it is okay) */ if (!ctl->eof_addr_final) update_eof_addr(ctl, &eof_change); /* Validate the range; if called from read_transaction(), it is possible that we are looking out of range, but * we clearly can identify such a case */ assert(ctl->min_seqno <= read_seqno); assert(read_seqno <= ctl->max_seqno || read_seqno == ctl->seqno + 1); rb = ctl->repl_buff; assert(REPL_MAINBUFF == rb->buffindex); b = &rb->buff[rb->buffindex]; /* looking up something we read already? */ assert(read_seqno != ctl->seqno || read_seqno == ctl->min_seqno || read_seqno == ctl->max_seqno || ctl->lookback); srch_func = do_binary_search; if (read_seqno > ctl->seqno) { if (read_seqno == ctl->seqno + 1) { /* trying to position to next seqno or the max, better to do linear search */ srch_func = do_linear_search; lo_addr = b->recaddr; hi_addr = MAXUINT4; } else if (read_seqno < ctl->max_seqno) { lo_addr = b->recaddr; hi_addr = ctl->max_seqno_dskaddr; /* Since we know that read_seqno is LESSER than ctl->max_seqno, we know for sure we should not return * this function with found = TR_NOT_FOUND. If ever the binary/linear search invocation at the end of * this function returns with TR_NOT_FOUND, we have to change that to TR_WILL_NOT_BE_FOUND and also * adjust ctl->seqno to point to ctl->max_seqno that way we don't repeat binary search (wastefully) for * all seqnos between read_seqno and the least seqno larger than read_seqno in this file. */ willnotbefound_addr = hi_addr; assert(ctl->max_seqno_dskaddr < rb->fc->eof_addr); willnotbefound_stop_at = rb->fc->eof_addr; if (lo_addr == hi_addr) { /* Since hi_addr corresponds to ctl->max_seqno which is in turn > read_seqno, the above * "if" check succeeding implies we can never find read_seqno here. So skip calling * "do_binary_search" in that case as that and "do_linear_search" have asserts that expect * callers to never invoke them with the same value of lo_addr and hi_addr. */ found = TR_NOT_FOUND; srch_func = NULL; } } else if (read_seqno == ctl->max_seqno) { /* For read_seqno == ctl->max_seqno, do not use linear search. Remember, max_seqno_dskaddr may be the * the address of the TCOM record of a transaction, and this TCOM record may be in a different block * than the block containing the first record of the transcation. To get it right, we may have to * back off to previous blocks. Well, do_binary_search() handles this condition. So, better we use binary * search. */ lo_addr = b->recaddr; assert(ctl->max_seqno_dskaddr < rb->fc->eof_addr); hi_addr = rb->fc->eof_addr; } else /* read_seqno > ctl->max_seqno */ { lo_addr = ctl->max_seqno_dskaddr; hi_addr = rb->fc->eof_addr; } } else /* (read_seqno <= ctl->seqno) */ { if (read_seqno != ctl->min_seqno) { lo_addr = ctl->min_seqno_dskaddr; hi_addr = b->recaddr + b->reclen; } else { /* trying to locate min, better to do linear search */ srch_func = do_linear_search; lo_addr = ctl->min_seqno_dskaddr; hi_addr = MAXUINT4; /* read_seqno == ctl->seqno == ctl->min_seqno is a special case. But, don't know how that can happen without * lookback set and hence the assert below. */ assert((read_seqno != ctl->seqno) || ctl->lookback); if ((read_seqno == ctl->seqno) && (lo_addr == b->recaddr)) { /* we are positioned where we want to be, no need for a read */ assert(MIN_JNLREC_SIZE <= b->reclen); DEBUG_ONLY(jrec = (jnl_record *)b->recbuff); DEBUG_ONLY(rectype = (enum jnl_record_type)jrec->prefix.jrec_type); assert(b->reclen == jrec->prefix.forwptr); assert(IS_VALID_JNLREC(jrec, rb->fc->jfh)); assert(IS_REPLICATED(rectype)); assert(!IS_FENCED(rectype) || IS_TUPD(rectype) || IS_FUPD(rectype) || JRT_NULL == rectype); REPL_DPRINT3("position_read: special case, read %llu is same as min in %s, returning TR_FOUND\n", read_seqno, ctl->jnl_fn); return TR_FOUND; } } } # if defined(GTMSOURCE_READFILES_LINEAR_SEARCH_TEST) srch_func = do_linear_search; hi_addr = MAXUINT4; # elif defined(GTMSOURCE_READFILES_BINARY_SEARCH_TEST) srch_func = do_binary_search; hi_addr = rb->fc->eof_addr; # endif REPL_DPRINT6("position_read: Using %s search to locate %llu in %s between %u and %u\n", (srch_func == do_linear_search) ? "linear" : "binary", read_seqno, ctl->jnl_fn, lo_addr, hi_addr); if (NULL != srch_func) found = srch_func(ctl, lo_addr, hi_addr, read_seqno, &srch_status); if ((TR_NOT_FOUND == found) && (0 != willnotbefound_addr)) { /* There is a block that contains a seqno larger than read_seqno; leave ctl positioned at this higher seqno. * If we don't do this, we could end up in an infinite loop if the caller of this function is "read_regions". * That caller redoes the "position_read" function call assuming there would have been some progress in the * previous call to the same function. So not resetting ctl->seqno here will result in an infinite loop. */ found = do_linear_search(ctl, willnotbefound_addr, willnotbefound_stop_at, read_seqno, &srch_status); REPL_DPRINT4("do_binary_search: position at seqno %llu in block [%u, %u)\n", srch_status.seqno, willnotbefound_addr, willnotbefound_stop_at); assert(found == TR_WILL_NOT_BE_FOUND); assert(read_seqno < ctl->seqno); } assert(found != TR_NOT_FOUND); return (found); } static int read_and_merge(unsigned char *buff, int maxbufflen, seq_num read_jnl_seqno) { int buff_avail, total_read, read_len, pass; boolean_t brkn_trans; unsigned char *seq_num_ptr, seq_num_str[32]; /* INT8_PRINT */ repl_ctl_element *ctl; int wait_for_jnlopen_log_num = -1; sgmnt_addrs *csa; gtmsource_state_t gtmsource_state_sav; repl_rctl_elem_t *repl_rctl; trans_read = FALSE; num_tcom = -1; tot_tcom_len = 0; total_read = 0; total_wait_for_jnl_recs = 0; total_wait_for_jnlopen = 0; tcombuffp = gtmsource_tcombuff_start; buff_avail = maxbufflen; /* ensure that buff is always within gtmsource_msgp bounds (especially in case the buffer got expanded in the last call) */ assert((buff >= (uchar_ptr_t)gtmsource_msgp + REPL_MSG_HDRLEN) && (buff <= (uchar_ptr_t)gtmsource_msgp + gtmsource_msgbufsiz)); for (repl_rctl = repl_rctl_list; NULL != repl_rctl; repl_rctl = repl_rctl->next) repl_rctl->read_complete = FALSE; for (pass = 1; !trans_read; pass++) { if (1 < pass) { if (gtmsource_recv_ctl_nowait()) { repl_log(gtmsource_log_fp, TRUE, TRUE, "State change detected in read_and_merge\n"); gtmsource_set_lookback(); /* In case we read ahead, enable looking back. */ return 0; } GTMSOURCE_SAVE_STATE(gtmsource_state_sav); gtmsource_poll_actions(TRUE); if(GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "State change detected in read_and_merge (poll)\n"); gtmsource_set_lookback(); /* In case we read ahead, enable looking back. */ return 0; } SHORT_SLEEP(GTMSOURCE_WAIT_FOR_JNLOPEN); /* sleep for 10 msec between each iteration */ total_wait_for_jnlopen += GTMSOURCE_WAIT_FOR_JNLOPEN; if (0 == (total_wait_for_jnlopen % LOG_WAIT_FOR_JNL_FLUSH_PERIOD)) { for (ctl = repl_ctl_list->next; NULL != ctl; ctl = ctl->next) { csa = &FILE_INFO(ctl->reg)->s_addrs; GTMSRC_DO_JNL_FLUSH_IF_POSSIBLE(ctl, csa); /* See if a "jnl_flush" might help nudge */ } } if (0 == (total_wait_for_jnlopen % LOG_WAIT_FOR_JNLOPEN_PERIOD)) { /* We have waited for 5000 intervals of 10 msec each, for a total of 50 seconds sleep. * Issue alert every 50 seconds. */ repl_log(gtmsource_log_fp, TRUE, TRUE, "REPL_WARN : Source server waited %u seconds for journal" " file(s) to be opened, or updated while attempting to read seqno %llu [0x%llx]. Check" " for problems with journaling\n", total_wait_for_jnlopen / 1000, read_jnl_seqno, read_jnl_seqno); for (ctl = repl_ctl_list->next; NULL != ctl; ctl = ctl->next) { csa = &FILE_INFO(ctl->reg)->s_addrs; repl_log(gtmsource_log_fp, TRUE, TRUE, "DBG_INFO: Journal File: %s for" " Database File: %s; State: %s. Timer PIDs(%d): (%u %u %u)\n", ctl->jnl_fn, ctl->reg->dyn.addr->fname, jnl_file_state_lit[ctl->file_state], csa->nl->wcs_timers + 1, csa->nl->wt_pid_array[0], csa->nl->wt_pid_array[1], csa->nl->wt_pid_array[2]); repl_log(gtmsource_log_fp, TRUE, TRUE, " " "ctl->seqno = %llu [0x%llx]. [dskaddr = 0x%x,freeaddr = 0x%x,rsrv_freeaddr = 0x%x]." " ctl->repl_rctl->read_complete = %d\n", ctl->seqno, ctl->seqno, csa->jnl->jnl_buff->dskaddr, csa->jnl->jnl_buff->freeaddr, csa->jnl->jnl_buff->rsrv_freeaddr, ctl->repl_rctl->read_complete); } } } GTMSOURCE_SAVE_STATE(gtmsource_state_sav); read_len = read_regions(&buff, &buff_avail, pass > 1, &brkn_trans, read_jnl_seqno); if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) return 0; if (brkn_trans) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_REPLBRKNTRANS, 1, &read_jnl_seqno); total_read += read_len; assert(total_read % JNL_WRT_END_MODULUS == 0); } if (tot_tcom_len > 0) { /* Copy all the TCOM records to the end of the buffer */ if (tot_tcom_len > buff_avail) increase_buffer(&buff, &buff_avail, tot_tcom_len); assert(buff + tot_tcom_len <= ((unsigned char *)gtmsource_msgp + gtmsource_msgbufsiz)); memcpy(buff, gtmsource_tcombuff_start, tot_tcom_len); total_read += tot_tcom_len; assert(total_read % JNL_WRT_END_MODULUS == 0); } return (total_read); } static int read_regions(unsigned char **buff, int *buff_avail, boolean_t attempt_open_oldnew, boolean_t *brkn_trans, seq_num read_jnl_seqno) { repl_ctl_element *ctl, *prev_ctl, *next_ctl, *old_ctl; gd_region *region; tr_search_state_t found; int read_len, cumul_read; int nopen; unsigned char seq_num_str[32], *seq_num_ptr; /* INT8_PRINT */ DEBUG_ONLY(static int loopcnt;) sgmnt_addrs *csa; jnlpool_ctl_ptr_t jctl; uint4 freeaddr; gtmsource_state_t gtmsource_state_sav; repl_rctl_elem_t *repl_rctl; boolean_t ctl_close; seq_num next_ctl_min_seqno; cumul_read = 0; *brkn_trans = TRUE; assert(repl_ctl_list->next != NULL); assert(NULL != jnlpool); jctl = jnlpool->jnlpool_ctl; /* For each region */ assert(repl_ctl_list->next == repl_rctl_list->ctl_start); for (repl_rctl = repl_rctl_list; (NULL != repl_rctl) && !trans_read; repl_rctl = repl_rctl->next) { ctl = repl_rctl->ctl_start; prev_ctl = ctl->prev; assert(NULL != prev_ctl); found = TR_NOT_FOUND; region = ctl->reg; DEBUG_ONLY(loopcnt = 0;) do { /* Find the generation of the journal file which has read_jnl_seqno */ for ( ; ; ) { if ((NULL == ctl) || (ctl->reg != region)) break; if ((JNL_FILE_OPEN == ctl->file_state) || (JNL_FILE_UNREAD == ctl->file_state)) break; assert((JNL_FILE_CLOSED == ctl->file_state) || (JNL_FILE_EMPTY == ctl->file_state)); assert(ctl->first_read_done); if (read_jnl_seqno <= ctl->max_seqno) break; next_ctl = ctl->next; /* "ctl" is no longer needed for any future seqnos (until a reconnection occurs) * so close it and free up associated "fd" and "memory" with one exception. * If the max-seqno of "ctl" is lesser than the min-seqno of "next_ctl". * In this case, keep "ctl" open until "read_jnl_seqno becomes >= next_ctl->min_seqno". */ assert(next_ctl->reg == ctl->reg); assert(next_ctl->repl_rctl == repl_rctl); if (!next_ctl->first_read_done) { GTMSOURCE_SAVE_STATE(gtmsource_state_sav); first_read(next_ctl); if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Abandoning read_regions (first_read CLOSED/EMPTY)\n"); return 0; } assert(next_ctl->first_read_done); } assert(ctl->max_seqno || ((JNL_FILE_EMPTY == ctl->file_state) && (1 == ctl->repl_buff->fc->jfh->start_seqno))); next_ctl_min_seqno = next_ctl->min_seqno; if (!next_ctl->min_seqno) { assert((JNL_FILE_UNREAD == next_ctl->file_state) || (JNL_FILE_EMPTY == next_ctl->file_state)); next_ctl_min_seqno = next_ctl->repl_buff->fc->jfh->start_seqno; } assert(next_ctl_min_seqno > ctl->max_seqno); ctl_close = (read_jnl_seqno >= next_ctl_min_seqno); if (ctl_close) { prev_ctl->next = next_ctl; next_ctl->prev = prev_ctl; repl_rctl = ctl->repl_rctl; if (repl_rctl->ctl_start == ctl) repl_rctl->ctl_start = next_ctl; repl_ctl_close(ctl); } ctl = next_ctl; } if ((NULL == ctl) || (ctl->reg != region)) { /* Hit the end of generation list for journal file */ if (!attempt_open_oldnew) { /* Reposition to skip prev_ctl */ REPL_DPRINT2("First pass...not opening newer gener file...skipping %s\n", prev_ctl->jnl_fn); ctl = prev_ctl; prev_ctl = ctl->prev; found = TR_FIND_WOULD_BLOCK; continue; } /* We come here ONLY if we have searched from the current generation journal file (as known to the * source server) backwards till the oldest generation journal files of this region and failed to * find read_jnl_seqno. Open newer generation journal files (if any) to see if they contain * read_jnl_seqno */ GTMSOURCE_SAVE_STATE(gtmsource_state_sav); nopen = open_newer_gener_jnlfiles(region, prev_ctl); if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) return 0; if (nopen > 0) /* Newer gener files opened */ { if (prev_ctl->file_state == JNL_FILE_CLOSED) { /* Recently updated journal file, search this */ ctl = prev_ctl; prev_ctl = ctl->prev; REPL_DPRINT3("Attempt search in %s. Next gener is %s\n", ctl->jnl_fn, ctl->next->jnl_fn); } else { /* Search next gener onwards */ ctl = prev_ctl->next; prev_ctl = ctl->prev; REPL_DPRINT3("Skipping empty gener %s. Moving to gener %s\n", prev_ctl->jnl_fn, ctl->jnl_fn); } } else if (nopen == 0) { /* None opened, the journal file hasn't been written into. * Reposition to skip the prev_ctl. */ ctl = prev_ctl; prev_ctl = ctl->prev; if (QWLT(read_jnl_seqno, jctl->jnl_seqno)) { csa = &FILE_INFO(ctl->reg)->s_addrs; freeaddr = csa->jnl->jnl_buff->rsrv_freeaddr; if ((ctl->repl_buff->fc->eof_addr == freeaddr) || (!JNL_ENABLED(csa->hdr))) { /* No more pending updates in the journal file. Next update to the * journal file will take the seqno jctl->jnl_seqno which will be * greater than read_jnl_seqno */ found = TR_WILL_NOT_BE_FOUND; continue; } } found = TR_FIND_WOULD_BLOCK; } } else if (ctl->file_state == JNL_FILE_UNREAD) { if (!ctl->first_read_done || attempt_open_oldnew) { GTMSOURCE_SAVE_STATE(gtmsource_state_sav); first_read(ctl); if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Abandoning read_regions (first_read UNREAD)\n"); return 0; } } if (ctl->file_state == JNL_FILE_UNREAD) { REPL_DPRINT2("First read of %s. Nothing yet written to this file\n", ctl->jnl_fn); if (ctl->prev->reg == ctl->reg && QWGT(ctl->repl_buff->fc->jfh->start_seqno, read_jnl_seqno)) { /* Prev gener opened already. Looking for read_jnl_seqno in this gener, * but the first possible seqno in this gener is > read_jnl_seqno. */ found = TR_WILL_NOT_BE_FOUND; continue; } if (ctl->prev->reg == ctl->reg) { /* Nothing yet written to the file. Attempt opening next gener. */ assert(QWLE(ctl->prev->repl_buff->fc->jfh->start_seqno, ctl->repl_buff->fc->jfh->start_seqno)); prev_ctl = ctl; ctl = ctl->next; } else if (attempt_open_oldnew) { GTMSOURCE_SAVE_STATE(gtmsource_state_sav); if (open_prev_gener(&old_ctl, ctl, read_jnl_seqno) == 0) { if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Abandoning read_regions (open_prev_gener UNREAD)\n"); return 0; } if (QWGT(ctl->repl_buff->fc->jfh->start_seqno, read_jnl_seqno)) { found = TR_WILL_NOT_BE_FOUND; continue; } /* Nothing yet written to this file. Attempt opening next gener */ prev_ctl = ctl; ctl = ctl->next; } else { assert(old_ctl->file_state == JNL_FILE_CLOSED || old_ctl->file_state == JNL_FILE_EMPTY); if (old_ctl->file_state == JNL_FILE_EMPTY || QWGT(old_ctl->min_seqno, read_jnl_seqno)) { /* Give the other regions a chance to * open their previous generations. */ REPL_DPRINT2("Skipping to other regions. Not reading from " "previous generation file %s\n", old_ctl->jnl_fn); found = TR_FIND_WOULD_BLOCK; continue; } /* Search the prev gener and backwards */ ctl = old_ctl; prev_ctl = old_ctl->prev; REPL_DPRINT2("Attempt searching in %s and backwards\n", ctl->jnl_fn); } } else { REPL_DPRINT2("First pass...skipping UNREAD file %s\n", ctl->jnl_fn); found = TR_FIND_WOULD_BLOCK; continue; } } } else if (ctl->file_state == JNL_FILE_EMPTY || QWGT(ctl->min_seqno, read_jnl_seqno)) { /* May be in prev gener */ if (ctl->prev->reg == ctl->reg && ctl->file_state != JNL_FILE_EMPTY) { /* If prev gener is already open, and we come here, we are looking for * a seqno between prev gener's max_seqno and this gener's min_seqno. * This region is not part of the transaction. Skip to the next region. */ REPL_DPRINT3("Gap between %s (max seqno "INT8_FMT, ctl->prev->jnl_fn, INT8_PRINT(ctl->prev->max_seqno)); REPL_DPRINT3(") and %s (min seqno "INT8_FMT, ctl->jnl_fn, INT8_PRINT(ctl->min_seqno)); REPL_DPRINT2(") found while looking for "INT8_FMT"\n", INT8_PRINT(read_jnl_seqno)); assert((JNL_FILE_CLOSED == ctl->prev->file_state) && (ctl->prev->max_seqno < read_jnl_seqno) || (JNL_FILE_EMPTY == ctl->prev->file_state)); found = TR_WILL_NOT_BE_FOUND; continue; } if (ctl->prev->reg == ctl->reg) { /* Skip the empty gener */ REPL_DPRINT2("Skipping empty journal file %s\n", ctl->jnl_fn); prev_ctl = ctl; ctl = ctl->next; continue; } if (!attempt_open_oldnew) { REPL_DPRINT2("First pass...not opening prev gener file...skipping %s\n", ctl->jnl_fn); found = TR_FIND_WOULD_BLOCK; continue; } /* Need to open prev gener */ GTMSOURCE_SAVE_STATE(gtmsource_state_sav); if (open_prev_gener(&old_ctl, ctl, read_jnl_seqno) == 0) { if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Abandoning read_regions (open_prev_gener EMPTY)\n"); return 0; } if (ctl->file_state != JNL_FILE_EMPTY) found = TR_WILL_NOT_BE_FOUND; else { /* Skip the empty generation */ REPL_DPRINT2("Skipping empty journal file %s\n", ctl->jnl_fn); prev_ctl = ctl; ctl = ctl->next; } continue; } assert(old_ctl->file_state == JNL_FILE_CLOSED || old_ctl->file_state == JNL_FILE_EMPTY); if (old_ctl->file_state == JNL_FILE_EMPTY || QWGT(old_ctl->min_seqno, read_jnl_seqno)) { /* Give the other regions a chance to open their previous generations */ found = TR_FIND_WOULD_BLOCK; REPL_DPRINT2("Skipping to other regions. Not reading from previous generation file %s\n", old_ctl->jnl_fn); continue; } /* Search the prev gener and backwards */ ctl = old_ctl; prev_ctl = old_ctl->prev; REPL_DPRINT2("Attempt searching in %s and backwards\n", ctl->jnl_fn); } else { assert((ctl->file_state == JNL_FILE_OPEN || read_jnl_seqno <= ctl->max_seqno) && (ctl->min_seqno <= read_jnl_seqno)); if (ctl->lookback) { assert(QWLE(read_jnl_seqno, ctl->seqno)); assert(ctl->file_state == JNL_FILE_OPEN || ctl->file_state == JNL_FILE_CLOSED); REPL_DPRINT4("Looking back and attempting to position read for %s at " INT8_FMT". File state is %s\n", ctl->jnl_fn, INT8_PRINT(read_jnl_seqno), (ctl->file_state == JNL_FILE_OPEN) ? "OPEN" : "CLOSED"); position_read(ctl, read_jnl_seqno); ctl->lookback = FALSE; } if (QWEQ(read_jnl_seqno, ctl->seqno)) { /* Found it */ if (!ctl->repl_rctl->read_complete) { GTMSOURCE_SAVE_STATE(gtmsource_state_sav); if ((read_len = read_transaction(ctl, buff, buff_avail, read_jnl_seqno)) < 0) assert(repl_errno == EREPL_JNLEARLYEOF); if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) return 0; cumul_read += read_len; assert(cumul_read % JNL_WRT_END_MODULUS == 0); } found = TR_FOUND; } else if (read_jnl_seqno < ctl->seqno) { /* This region is not involved in transaction read_jnl_seqno */ found = TR_WILL_NOT_BE_FOUND; } else /* QWGT(read_jnl_seqno, ctl->seqno) */ { /* Detect infinite loop of calls to position_read() by limiting # of calls to 1024 in dbg */ DEBUG_ONLY(loopcnt++;) assert(1024 > loopcnt); if (ctl->file_state == JNL_FILE_OPEN) { /* State change from READ_FILE->READ_POOL-> READ_FILE might cause this. * The journal files have grown since the transition from READ_FILE to READ_POOL * was made. Update ctl info. */ GTMSOURCE_SAVE_STATE(gtmsource_state_sav); if (update_max_seqno_info(ctl) != SS_NORMAL) { assert(repl_errno == EREPL_JNLEARLYEOF); assertpro(FALSE); /* Program bug */ } if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Abandoning read_regions (update_max file/pool/file)\n"); return 0; } if (read_jnl_seqno <= ctl->max_seqno) { /* May be found in this journal file, * attempt to position next read to read_jnl_seqno */ force_file_read(ctl); /* Buffer might be stale with an EOF record */ position_read(ctl, read_jnl_seqno); } else { /* Will possibly be found in next gener */ prev_ctl = ctl; ctl = ctl->next; } } else if (ctl->file_state == JNL_FILE_CLOSED) { /* May be found in this jnl file, attempt to position next read to read_jnl_seqno */ position_read(ctl, read_jnl_seqno); } else { /* Program bug - ctl->seqno should never be greater than ctl->max_seqno */ assertpro(FALSE); } } } } while (TR_NOT_FOUND == found); /* Move to the next region, now that the tr has been found or will not be found */ *brkn_trans = (*brkn_trans && (TR_WILL_NOT_BE_FOUND == found)); } assert(!*brkn_trans || (gtm_white_box_test_case_enabled && ((WBTEST_REPLBRKNTRANS == gtm_white_box_test_case_number) || (WBTEST_MURUNDOWN_KILLCMT06 == gtm_white_box_test_case_number) || (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number)))); return (cumul_read); } int gtmsource_readfiles(unsigned char *buff, int *data_len, int maxbufflen, boolean_t read_multiple) { size_t read_size, read_state, first_tr_len, tot_tr_len, loopcnt; unsigned char *orig_msgp, seq_num_str[32], *seq_num_ptr; /* INT8_PRINT */ jnlpool_ctl_ptr_t jctl; gtmsource_local_ptr_t gtmsource_local; seq_num read_jnl_seqno, max_read_seqno; qw_num read_addr; gtm_uint64_t jnlpool_size; boolean_t file2pool; unsigned int start_heartbeat; boolean_t stop_bunching; gtmsource_state_t gtmsource_state_sav; jctl = jnlpool->jnlpool_ctl; gtmsource_local = jnlpool->gtmsource_local; jnlpool_size = jctl->jnlpool_size; max_read_seqno = jctl->jnl_seqno; /* Note that we are fetching the value of "jctl->jnl_seqno" without a lock on the journal pool. This means we could * get an inconsistent 8-byte value (i.e. neither the pre-update nor the post-update value) which is possible if a * concurrent GT.M process updates this 8-byte field in a sequence of two 4-byte operations instead of one * atomic operation (possible in architectures where 8-byte operations are not native) AND if the pre-update and * post-update value differ in their most significant 4-bytes. Since that is considered a virtually impossible * rare occurrence and since we want to avoid the overhead of doing a "grab_lock", we don't do that here. */ assert(REPL_MSG_HDRLEN == SIZEOF(jnldata_hdr_struct)); DEBUG_ONLY(loopcnt = 0;) do { assert(maxbufflen == gtmsource_msgbufsiz - REPL_MSG_HDRLEN); DEBUG_ONLY(loopcnt++); file2pool = FALSE; if (max_read_seqno > gtmsource_local->next_histinfo_seqno) max_read_seqno = gtmsource_local->next_histinfo_seqno; /* Do not read more than next histinfo boundary */ assert(MAX_SEQNO != max_read_seqno); read_jnl_seqno = gtmsource_local->read_jnl_seqno; assert(read_jnl_seqno <= max_read_seqno); if (read_jnl_seqno == gtmsource_local->next_histinfo_seqno) { /* Request a REPL_HISTREC message be sent first before sending any more seqnos across */ gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_SEND_NEW_HISTINFO; REPL_DPRINT1("REPL_HISTREC message first needs to be sent before any more seqnos can be sent across\n"); return 0; } TIMEOUT_INIT(stop_bunching, BUNCHING_TIME); read_addr = gtmsource_local->read_addr; GTMSOURCE_SAVE_STATE(gtmsource_state_sav); first_tr_len = read_size = read_and_merge(buff, maxbufflen, read_jnl_seqno++) + REPL_MSG_HDRLEN; if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { TIMEOUT_DONE(stop_bunching); return 0; } tot_tr_len = 0; do { tot_tr_len += read_size; REPL_DPRINT5("File read seqno : %llu Tr len : %d Total tr len : %d Maxbufflen : %d\n", read_jnl_seqno - 1, read_size - REPL_MSG_HDRLEN, tot_tr_len, maxbufflen); if (gtmsource_save_read_jnl_seqno < read_jnl_seqno) { read_addr += read_size; if (jnlpool_size >= (jctl->rsrv_write_addr - read_addr)) { /* No more overflow, switch to READ_POOL. To avoid the expense of memory barrier * in jnlpool_hasnt_overflowed(), we use a possibly stale value of rsrv_write_addr * to check if we can switch back to pool. The consequence is that we may switch * back and forth between file and pool read if we are in a situation wherein a GTM * process races with source server, writing transactions into the pool right when * the source server concludes that it can read from pool. We think this condition * is rare enough that the expense of re-opening the files (due to the transition) * and re-positioning read pointers is considered liveable when compared with the * cost of a memory barrier. We can reduce the expense by not clearing the file * information for every transition back to pool. We can wait for a certain period * of time (say 15 minutes) before we close all files. */ file2pool = TRUE; break; } REPL_DPRINT3("Readfiles : after sync with pool read_seqno: %llu read_addr: %llu\n", read_jnl_seqno, read_addr); } /* If reading multiple transactions in one shot, make sure we stop the bunching if at least 8 seconds * (a heartbeat period) has elapsed during the bunching. This way we send whatever we have now rather * than accumulating transactions in our huge internal buffer and avoid risking the user perception * of no-progress. In the worst case we could be unresponsive for 8 seconds (1 heartbeat period) * with this approach. */ read_multiple = read_multiple && !stop_bunching; if (read_multiple) { /* Ok to read multiple transactions. Limit the multiple reads until there is no more to be read * OR total read size reaches a fixed value MAX_TR_BUFFSIZE. This strikes a fine balance between * reducing the # of "send()" system calls done by the source server versus being responsive to * the user (in case of shutdown requests). Time spent reading multiple transactions is where * the source server is not responsive to outside requests and is hence better minimized. */ /* Note: Recompute buff and maxbufflen below buffer may have expanded during read_and_merge */ buff = (unsigned char *)gtmsource_msgp + tot_tr_len + REPL_MSG_HDRLEN; maxbufflen = gtmsource_msgbufsiz - tot_tr_len - REPL_MSG_HDRLEN; if ((tot_tr_len < MAX_TR_BUFFSIZE) && (read_jnl_seqno < max_read_seqno)) { assert(0 < maxbufflen); GTMSOURCE_SAVE_STATE(gtmsource_state_sav); read_size = read_and_merge(buff, maxbufflen, read_jnl_seqno++) + REPL_MSG_HDRLEN; if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { /* Control message triggered state change */ TIMEOUT_DONE(stop_bunching); return 0; } /* Don't use buff to assign type and len as buffer may have expanded. * Use gtmsource_msgp instead */ ((repl_msg_ptr_t)((unsigned char *)gtmsource_msgp + tot_tr_len))->type = REPL_TR_JNL_RECS; ((repl_msg_ptr_t)((unsigned char *)gtmsource_msgp + tot_tr_len))->len = read_size; continue; } REPL_DPRINT5("Readfiles : tot_tr_len %d read_jnl_seqno %llu max_read_seqno %llu " "gtmsource_msgbufsize : %d; stop multiple reads\n", tot_tr_len, read_jnl_seqno, max_read_seqno, gtmsource_msgbufsiz); } break; } while (TRUE); TIMEOUT_DONE(stop_bunching); assert(read_jnl_seqno <= max_read_seqno); if ((gtmsource_local->next_histinfo_num < gtmsource_local->num_histinfo) || (gtmsource_local->num_histinfo == jnlpool->repl_inst_filehdr->num_histinfo)) { /* We are either sending seqnos of a histinfo that is not the last one in the instance file OR * we are sending seqnos of the last histinfo (that is open-ended) but there has been no more histinfo * records concurrently added to this instance file compared to what is in our private memory. In either * case, it is safe to send these seqnos without worrying about whether a new histinfo record needs to * be sent first. */ break; } else { /* Set the next histinfo record's start_seqno and redo the read with the new * "gtmsource_local->next_histinfo_seqno" */ assert(MAX_SEQNO == gtmsource_local->next_histinfo_seqno); gtmsource_set_next_histinfo_seqno(TRUE); if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) return 0; /* Connection got reset in "gtmsource_set_next_histinfo_seqno" */ /* Since the buffer may have expanded, reposition buff to the beginning and set maxbufflen to the maximum * available size (as if this is the first time we came into the while loop) */ buff = (unsigned char *)gtmsource_msgp + REPL_MSG_HDRLEN; maxbufflen = gtmsource_msgbufsiz - REPL_MSG_HDRLEN; } } while (TRUE); if (file2pool && !gtmsource_local->jnlfileonly) { /* Ahead of the transition to pool, force repl_phase2_cleanup() when write_addr is behind read_addr. This * condition happens frequently with replicating instances and instances with infrequent updates. */ if (jctl->write_addr < read_addr) repl_phase2_cleanup(jnlpool); assert(jctl->write_addr >= read_addr); gtmsource_local->read = read_addr % jnlpool_size; gtmsource_local->read_state = read_state = READ_POOL; } else read_state = gtmsource_local->read_state; gtmsource_local->read_addr = read_addr; assert(read_jnl_seqno <= gtmsource_local->next_histinfo_seqno); gtmsource_local->read_jnl_seqno = read_jnl_seqno; GTMDBGFLAGS_NOFREQ_ONLY(GTMSOURCE_FORCE_READ_FILE_MODE, gtmsource_local->read_state = read_state = READ_FILE); if (read_state == READ_POOL) { gtmsource_ctl_close(); /* no need to keep files open now that we are going to read from pool */ repl_log(gtmsource_log_fp, TRUE, TRUE, "Source server now reading from journal pool at seqno %llu [0x%llx]\n", read_jnl_seqno, read_jnl_seqno); REPL_DPRINT3("Readfiles : after switch to pool, read_addr : "INT8_FMT" read : %u\n", INT8_PRINT(read_addr), gtmsource_local->read); } *data_len = (first_tr_len - REPL_MSG_HDRLEN); return (tot_tr_len); } /* This function resets "zqgblmod_seqno" and "zqgblmod_tn" in all replicated database file headers to correspond to the * "resync_seqno" passed in as input. This shares some of its code with the function "repl_inst_reset_zqgblmod_seqno_and_tn". * Any changes there might need to be reflected here. */ int gtmsource_update_zqgblmod_seqno_and_tn(seq_num resync_seqno) { repl_ctl_element *ctl, *next_ctl, *old_ctl; gd_region *region; sgmnt_addrs *csa; unsigned char seq_num_str[32], *seq_num_ptr; /* INT8_PRINT */ boolean_t was_crit; seq_num start_seqno, max_zqgblmod_seqno; trans_num bov_tn; gtmsource_state_t gtmsource_state_sav; gtmsource_ctl_close(); gtmsource_ctl_init(); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { /* Possible if grab_crit done from gtmsource_ctl_init -> repl_ctl_create detected an Online Rollback as part * of grab_crit */ return -1; /* gtmsource_ctl_close will be done by gtmsource_process */ } max_zqgblmod_seqno = 0; for (ctl = repl_ctl_list->next; ctl != NULL; ctl = next_ctl) { next_ctl = ctl->next; assert((NULL == next_ctl) || (ctl->reg != next_ctl->reg)); repl_log(gtmsource_log_fp, TRUE, FALSE, "Updating ZQGBLMOD SEQNO and TN for Region [%s]\n", ctl->reg->rname); GTMSOURCE_SAVE_STATE(gtmsource_state_sav); first_read(ctl); if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Abandoning gtmsource_update_zqgblmod_seqno_and_tn (first_read)\n"); return (SS_NORMAL); } do { assert(ctl->first_read_done); start_seqno = ctl->repl_buff->fc->jfh->start_seqno; repl_log(gtmsource_log_fp, TRUE, FALSE, "Region [%s] : Journal file [%s] : Start Seqno : [0x%llx]\n", ctl->reg->rname, ctl->jnl_fn, start_seqno); if (start_seqno <= resync_seqno) break; GTMSOURCE_SAVE_STATE(gtmsource_state_sav); if (0 == open_prev_gener(&old_ctl, ctl, resync_seqno)) /* this automatically does a "first_read" */ { /* Previous journal file link was NULL. Issue error. */ if (GTMSOURCE_NOW_TRANSITIONAL(gtmsource_state_sav)) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Abandoning gtmsource_update_zqgblmod_seqno_and_tn (open_prev_gener)\n"); return (SS_NORMAL); } RTS_ERROR_CSA_ABT(&FILE_INFO(ctl->reg)->s_addrs, VARLSTCNT(4) ERR_NOPREVLINK, 2, ctl->jnl_fn_len, ctl->jnl_fn); } assert(old_ctl->next == ctl); assert(ctl->prev == old_ctl); ctl = old_ctl; } while (TRUE); assert(NULL != ctl); csa = &FILE_INFO(ctl->reg)->s_addrs; assert(REPL_ALLOWED(csa->hdr)); bov_tn = ctl->repl_buff->fc->jfh->bov_tn; repl_log(gtmsource_log_fp, TRUE, TRUE, "Assigning Region [%s] : Resync Seqno [0x%llx] : ZQGBLMOD SEQNO [0x%llx] " ": ZQGBLMOD TN : [0x%llx]\n", ctl->reg->rname, resync_seqno, start_seqno, bov_tn); /* csa->hdr->zqgblmod_seqno is modified ONLY by the source server OR online rollback (both of these hold the * database crit while doing so). It is also read by fileheader_sync() which does so while holding crit. * To avoid the latter from reading an inconsistent value (i.e neither the pre-update nor the post-update * value, which is possible if the 8-byte operation is not atomic but a sequence of two 4-byte operations * AND if the pre-update and post-update value differ in their most significant 4-bytes) we grab_crit. We * could have used QWCHANGE_IS_READER_CONSISTENT macro (which checks for most significant 4-byte difference) * instead to determine if it is really necessary to grab crit. But, since the update to zqgblmod_seqno is a * rare operation, we decided to play it safe. */ assert(!csa->hold_onto_crit); if (FALSE == (was_crit = csa->now_crit)) grab_crit(ctl->reg, WS_34); if (csa->onln_rlbk_cycle != csa->nl->onln_rlbk_cycle) { assert(process_id != jnlpool->gtmsource_local->gtmsource_srv_latch.u.parts.latch_pid); SYNC_ONLN_RLBK_CYCLES; gtmsource_onln_rlbk_clnup(); /* would have set gtmsource_state accordingly */ if (!was_crit) rel_crit(ctl->reg); return -1; /* gtmsource_ctl_close will be done by gtmsource_process */ } csa->hdr->zqgblmod_seqno = start_seqno; csa->hdr->zqgblmod_tn = bov_tn; if (FALSE == was_crit) rel_crit(ctl->reg); if (max_zqgblmod_seqno < start_seqno) max_zqgblmod_seqno = start_seqno; } assert(!jnlpool->jnlpool_ctl->max_zqgblmod_seqno || jnlpool->jnlpool_ctl->max_zqgblmod_seqno > resync_seqno); assert(0 < max_zqgblmod_seqno); assert(resync_seqno >= max_zqgblmod_seqno); assert(!(FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs.now_crit)); grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, HANDLE_CONCUR_ONLINE_ROLLBACK); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) { assert(process_id != jnlpool->gtmsource_local->gtmsource_srv_latch.u.parts.latch_pid); return -1; /* gtmsource_ctl_close will be done by gtmsource_process */ } jnlpool->jnlpool_ctl->max_zqgblmod_seqno = max_zqgblmod_seqno; rel_lock(jnlpool->jnlpool_dummy_reg); gtmsource_ctl_close(); /* close all structures now that we are done; if we have to read from journal files; we'll open * the structures again */ return (SS_NORMAL); } fis-gtm-V7.0-005/sr_unix/gtmsource_readpool.c0000644000032200000250000003147714342376330020116 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2022 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_string.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_stat.h" #include #include #ifdef VMS #include /* Required for gtmsource.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "jnl.h" #include "buddy_list.h" #include "hashtab_mname.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "muprec.h" #include "repl_ctl.h" #include "repl_errno.h" #include "repl_dbg.h" #include "memcoherency.h" #include "repl_tr_good.h" #include "min_max.h" #include "repl_instance.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF gtmsource_state_t gtmsource_state; int gtmsource_readpool(uchar_ptr_t buff, int *data_len, int maxbufflen, boolean_t read_multiple, qw_num stop_read_at) { size_t jnldata_len, read_size, avail_data; gtm_uint64_t jnlpool_size; uint4 first_tr_len, num_tr_read, orig_first_tr_len, orig_tr_len, tr_len; int64_t read, wrap_size; /* This can be negative. Must handle a signed 4G value */ uchar_ptr_t buf_top, tr_p; jnlpool_ctl_ptr_t jctl; gtmsource_local_ptr_t gtmsource_local; sm_uc_ptr_t jnldata_base; jnldata_hdr_ptr_t jnl_header; qw_num read_addr, avail_data_qw; seq_num read_jnl_seqno, jnl_seqno, next_read_seqno, next_histinfo_seqno; jnl_record *jnlrec; boolean_t trim_this_seqno; uchar_ptr_t trim_buff; jctl = jnlpool->jnlpool_ctl; jnlpool_size = jctl->jnlpool_size; DEBUG_ONLY(jnl_seqno = jctl->jnl_seqno;) /* jnl_seqno is used in an assert below. jnl_seqno is a local variable for * debugging purposes since shared memory can change from the time the assert * fails to the time the core gets created */ jnldata_base = jnlpool->jnldata_base; gtmsource_local = jnlpool->gtmsource_local; do { read = gtmsource_local->read; read_addr = gtmsource_local->read_addr; assert(stop_read_at > read_addr); /* there should be data to be read, if not how did we end up here? */ read_jnl_seqno = gtmsource_local->read_jnl_seqno; assert(read_jnl_seqno <= gtmsource_local->next_histinfo_seqno); if (read_jnl_seqno == gtmsource_local->next_histinfo_seqno) { /* Request a REPL_HISTREC message be sent first before sending any more seqnos across */ gtmsource_state = gtmsource_local->gtmsource_state = GTMSOURCE_SEND_NEW_HISTINFO; return 0; } next_histinfo_seqno = gtmsource_local->next_histinfo_seqno; next_read_seqno = read_jnl_seqno; if (!jnlpool_hasnt_overflowed(jctl, jnlpool_size, read_addr)) break; /* overflow happened, or about to happen */ /* No overflow yet. Before we read the content (including the jnldata_len read below), we have to ensure * we read up-to-date content. We rely on the memory barrier done in jnlpool_hasnt_overflowed for this. */ assert(read + SIZEOF(jnldata_hdr_struct) <= jnlpool_size); jnl_header = (jnldata_hdr_ptr_t)(jnldata_base + read); first_tr_len = jnldata_len = jnl_header->jnldata_len; if (read_multiple) { assert(stop_read_at >= read_addr); avail_data_qw = stop_read_at - read_addr; /* To catch the case of change in type of maxbufflen */ assert((0 <= maxbufflen) && (maxbufflen <= MAXPOSINT4)); avail_data = (uint4)MIN(avail_data_qw, (qw_num)maxbufflen); assert(next_read_seqno < next_histinfo_seqno); read_multiple = (first_tr_len < avail_data) && ((next_read_seqno + 1) < next_histinfo_seqno); if (read_multiple) jnldata_len = avail_data; } if (SIZEOF(jnldata_hdr_struct) < jnldata_len && jnldata_len <= jnlpool_size) { read_size = jnldata_len - SIZEOF(jnldata_hdr_struct); if (0 < read_size && read_size <= maxbufflen) { if (0 < (wrap_size = ((int64_t)read - (jnlpool_size - jnldata_len)))) read_size -= wrap_size; memcpy(buff, (sm_uc_ptr_t)jnl_header + SIZEOF(jnldata_hdr_struct), read_size); if (0 < wrap_size) memcpy(buff + read_size, jnldata_base, wrap_size); jnlrec = (jnl_record *)buff; /* Now that we have read the content, we have to ensure that we haven't read content * that may have been overwritten. We rely on the memory barrier done in * "jnlpool_hasnt_overflowed" for this. In addition, check for JRT_BAD (this is indication * of a ERR_JNLPOOLRECOVERY situation) and if so switch to READ_FILE. */ if ((jnlpool_hasnt_overflowed(jctl, jnlpool_size, read_addr)) && (JRT_BAD != jnlrec->prefix.jrec_type)) { /* No overflow. Only now are we guaranteed a good value of "first_tr_len". */ /* Check if this seqno was processed by "repl_phase2_salvage". In that case, it * would contain just one NULL record so copy just that and ignore the rest of * the reserved space (if any). Note that if the rest of the reserved space is * 0 bytes long, then we don't need any special processing. */ first_tr_len -= SIZEOF(jnldata_hdr_struct); assert(0 == (first_tr_len % JNL_WRT_END_MODULUS)); assert(first_tr_len >= NULL_RECLEN); orig_first_tr_len = first_tr_len; trim_buff = NULL; if ((JRT_NULL == jnlrec->prefix.jrec_type) && (NULL_RECLEN != first_tr_len)) { first_tr_len = NULL_RECLEN; trim_buff = buff + NULL_RECLEN; } # ifdef REPL_DEBUG assert(repl_tr_good(buff, first_tr_len, read_jnl_seqno)); num_tr_read = 1; # endif next_read_seqno++; assert(next_read_seqno <= next_histinfo_seqno); if (read_multiple) { /* Although stop_read_at - read_addr contains no partial transaction, it * is possible that stop_read_at - read_addr is more than maxbufflen, and * hence we read fewer bytes than stop_read_at - read_addr; scan what we * read to figure out if the tail is an incomplete transaction. */ assert((orig_first_tr_len + SIZEOF(jnldata_hdr_struct)) < jnldata_len); /* above must hold if multiple transactions were read */ tr_p = buff + orig_first_tr_len; buf_top = buff + jnldata_len - SIZEOF(jnldata_hdr_struct); while (SIZEOF(jnldata_hdr_struct) < (buf_top - tr_p)) { /* more than hdr available */ orig_tr_len = tr_len = ((jnldata_hdr_ptr_t)tr_p)->jnldata_len; assert(0 == (tr_len % JNL_WRT_END_MODULUS)); assert(0 < tr_len); assert(tr_len <= jnlpool_size); if (tr_len <= (buf_top - tr_p)) /* transaction completely read */ { jnlrec = (jnl_record *)(tr_p + SIZEOF(jnldata_hdr_struct)); if (JRT_BAD == jnlrec->prefix.jrec_type) { /* This is indication of a ERR_JNLPOOLRECOVERY situation * and so we need to switch to READ_FILE for this seqno. * Do that by stopping the READ_POOL until the seqno before * this one. So break out of loop that reads more seqnos. */ break; } if ((JRT_NULL == jnlrec->prefix.jrec_type) && ((NULL_RECLEN + SIZEOF(jnldata_hdr_struct)) != tr_len)) { trim_this_seqno = TRUE; tr_len = NULL_RECLEN + REPL_MSG_HDRLEN; } else trim_this_seqno = FALSE; /* The message type and len assignments are a violation of * layering; ideally, this should be done in * gtmsource_process(), but we choose to do it here for * performance reasons. If we have to do it in * gtmsource_process(), we have to scan the buffer again. */ ((repl_msg_ptr_t)tr_p)->type = REPL_TR_JNL_RECS; ((repl_msg_ptr_t)tr_p)->len = tr_len; # ifdef REPL_DEBUG assert(repl_tr_good(tr_p + REPL_MSG_HDRLEN, tr_len - REPL_MSG_HDRLEN, read_jnl_seqno + num_tr_read)); num_tr_read++; # endif if (NULL != trim_buff) { memmove(trim_buff, tr_p, tr_len); trim_buff += tr_len; } else if (trim_this_seqno) trim_buff = tr_p + tr_len; next_read_seqno++; tr_p += orig_tr_len; if (next_read_seqno >= next_histinfo_seqno) { /* Don't read more than boundary of next histinfo */ assert(next_read_seqno == next_histinfo_seqno); break; } } else { REPL_DPRINT5("Partial transaction read since jnldata_len" " %llu larger than maxbufflen %d, tr_len %d, " "remaining buffer %d\n", avail_data_qw, maxbufflen, tr_len, buf_top - tr_p); break; } } # ifdef REPL_DEBUG if (0 != (buf_top - tr_p)) { REPL_DPRINT4("Partial tr header read since jnldata_len " "%llu larger than maxbufflen %d, incomplete header" " length %d\n", avail_data_qw, maxbufflen, buf_top - tr_p); } # endif jnldata_len = (uint4)((tr_p - buff) + SIZEOF(jnldata_hdr_struct)); assert(0 == (jnldata_len % JNL_WRT_END_MODULUS)); wrap_size = ((int64_t)read - (jnlpool_size - jnldata_len)); } REPL_DPRINT4("Pool read seqno : "INT8_FMT" Num Tr read : %d Total Tr len : %d\n", INT8_PRINT(read_jnl_seqno), num_tr_read, jnldata_len); REPL_DPRINT4("Read %u : Next read : %ld : %s\n", read, (0 > wrap_size) ? read + jnldata_len : wrap_size, (0 > wrap_size) ? "" : " READ WRAPPED"); assert(next_read_seqno <= next_histinfo_seqno); /* Before sending the seqnos, check if a new histinfo got concurrently written */ assert(gtmsource_local->next_histinfo_num <= gtmsource_local->num_histinfo); if ((gtmsource_local->next_histinfo_num == gtmsource_local->num_histinfo) && (gtmsource_local->num_histinfo != jnlpool->repl_inst_filehdr->num_histinfo)) { /* We are sending seqnos of the last histinfo (that is open-ended) and * there has been at least one histinfo concurrently added to this instance * file compared to what is in our private memory. Set the next histinfo's * start_seqno and redo the read with the new "next_histinfo_seqno". */ assert(MAX_SEQNO == gtmsource_local->next_histinfo_seqno); gtmsource_set_next_histinfo_seqno(TRUE); /* Set the next histinfo's start_seqno and redo the read */ if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) { /* Connection reset in "gtmsource_set_next_histinfo_seqno" */ return 0; } continue; } read = ((0 > wrap_size) ? read + jnldata_len : wrap_size); read_addr += jnldata_len; read_jnl_seqno = next_read_seqno; assert(read_jnl_seqno <= gtmsource_local->next_histinfo_seqno); assert(stop_read_at >= read_addr); assert(jnl_seqno >= read_jnl_seqno - 1); /* In the rare case when we read the transaction read_jnl_seqno just as * it becomes available and before the GTM process that wrote it updates * jctl->jnl_seqno in t_end/tp_tend, we may return from this function * with read_jnl_seqno one more than jctl->jnl_seqno. This is such a rare * case that we don't want to add a wait loop for jctl->jnl_seqno to become * equal to read_jnl_seqno. We expect that by the time we send the just read * transaction(s) using socket I/O, jctl->jnl_seqno would have been updated. * In any case, we prevent ourselves from misinterpreting this condition when * read_jnl_seqno is compared against jctl->jnl_seqno in gtmsource_process(), * gtmsource_get_jnlrecs() and gtmsource_showbacklog(). */ assert(read == read_addr % jnlpool_size); gtmsource_local->read = read; gtmsource_local->read_addr = read_addr; gtmsource_local->read_jnl_seqno = read_jnl_seqno; *data_len = first_tr_len; if (NULL != trim_buff) jnldata_len = (uint4)((trim_buff - buff) + SIZEOF(jnldata_hdr_struct)); return (jnldata_len); } } else if (0 < read_size && jnlpool_hasnt_overflowed(jctl, jnlpool_size, read_addr)) { /* Buffer cannot accommodate data */ *data_len = read_size; return (-1); } /* else * We read a corrupt (overwritten) large value, or read_size == 0, both of which imply overflow. * read_size == 0 => overflow because every transaction generates non-zero bytes of jnl data */ } /* else * We read a corrupt (overwritten) large value, or read 0, both of which imply overflow. * jnldata_len == 0 => overflow because every transaction generates non-zero bytes of jnl data */ break; } while (TRUE); *data_len = -1; return (-1); /* Error indication */ } fis-gtm-V7.0-005/sr_unix/gtmsource_rootprimary_init.c0000644000032200000250000001534714342376330021721 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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_string.h" #include "gtm_time.h" #include "gtm_unistd.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_instance.h" #include "jnl.h" #include "change_reg.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF uint4 process_id; GBLREF jnl_gbls_t jgbl; GBLREF gd_addr *gd_header; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; error_def(ERR_JNLEXTEND); /* This function is called primarily to append a new histinfo record to the replication instance file by one of the following * 1) MUPIP REPLIC -SOURCE -START -ROOTPRIMARY command (after forking the child source server) if it created the journal pool. * 2) MUPIP REPLIC -SOURCE -ACTIVATE -ROOTPRIMARY command if this is a propagating primary to root primary transition. * In addition, this function also initializes the "lms_group_info" field in the instance file (from the "inst_info" field) * if the current value is NULL. */ void gtmsource_rootprimary_init(seq_num start_seqno) { unix_db_info *udi; repl_histinfo histinfo; boolean_t was_crit, switch_jnl; gd_region *reg, *region_top; jnl_private_control *jpc; jnl_buffer_ptr_t jbp; uint4 jnl_status; assert(NULL != jnlpool); udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); assert(NULL != jnlpool->repl_inst_filehdr); /* Update journal pool fields to reflect this is a root primary startup and updates are enabled */ assert(!udi->s_addrs.hold_onto_crit || jgbl.onlnrlbk); was_crit = udi->s_addrs.now_crit; if (!was_crit) grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); jnlpool->repl_inst_filehdr->root_primary_cycle++; /* If this instance is transitioning from a non-rootprimary to rootprimary, switch journal files. * This helps with maintaining accurate value of csd->zqgblmod_tn when the former primary connects * to the current primary through a fetchresync-rollback or receiver-server-autorollback.. */ switch_jnl = (!jnlpool->repl_inst_filehdr->was_rootprimary && (0 < jnlpool->repl_inst_filehdr->num_histinfo)); jnlpool->repl_inst_filehdr->was_rootprimary = TRUE; assert(start_seqno >= jnlpool->jnlpool_ctl->start_jnl_seqno); assert(start_seqno == jnlpool->jnlpool_ctl->jnl_seqno); jnlpool->repl_inst_filehdr->jnl_seqno = start_seqno; assert(jgbl.onlnrlbk || jnlpool->jnlpool_ctl->upd_disabled); if (!jgbl.onlnrlbk) jnlpool->jnlpool_ctl->upd_disabled = FALSE; if (IS_REPL_INST_UUID_NULL(jnlpool->repl_inst_filehdr->lms_group_info)) { /* This is the first time this instance is being brought up either as a root primary or as a propagating * primary. Initialize the "lms_group_info" fields in the instance file header in journal pool shared memory. * They will be flushed to the instance file as part of the "repl_inst_histinfo_add -> repl_inst_flush_filehdr" * function invocation below. */ assert('\0' == jnlpool->repl_inst_filehdr->lms_group_info.created_nodename[0]); assert('\0' == jnlpool->repl_inst_filehdr->lms_group_info.this_instname[0]); assert(!jnlpool->repl_inst_filehdr->lms_group_info.creator_pid); jnlpool->repl_inst_filehdr->lms_group_info = jnlpool->repl_inst_filehdr->inst_info; assert('\0' != jnlpool->repl_inst_filehdr->lms_group_info.created_nodename[0]); DBG_CHECK_CREATED_NODENAME(jnlpool->repl_inst_filehdr->lms_group_info.created_nodename); assert('\0' != jnlpool->repl_inst_filehdr->lms_group_info.this_instname[0]); assert(jnlpool->repl_inst_filehdr->lms_group_info.created_time); assert(jnlpool->repl_inst_filehdr->lms_group_info.creator_pid); } /* Initialize histinfo fields */ memcpy(histinfo.root_primary_instname, jnlpool->repl_inst_filehdr->inst_info.this_instname, MAX_INSTNAME_LEN - 1); histinfo.root_primary_instname[MAX_INSTNAME_LEN - 1] = '\0'; assert('\0' != histinfo.root_primary_instname[0]); histinfo.start_seqno = start_seqno; assert(jnlpool->jnlpool_ctl->strm_seqno[0] == jnlpool->repl_inst_filehdr->strm_seqno[0]); assert(jnlpool->repl_inst_filehdr->is_supplementary || (0 == jnlpool->jnlpool_ctl->strm_seqno[0])); histinfo.strm_seqno = (!jnlpool->repl_inst_filehdr->is_supplementary) ? 0 : jnlpool->jnlpool_ctl->strm_seqno[0]; histinfo.root_primary_cycle = jnlpool->repl_inst_filehdr->root_primary_cycle; assert(process_id == getpid()); histinfo.creator_pid = process_id; JNL_SHORT_TIME(histinfo.created_time); histinfo.strm_index = 0; histinfo.history_type = HISTINFO_TYPE_NORMAL; NULL_INITIALIZE_REPL_INST_UUID(histinfo.lms_group); /* The following fields will be initialized in the "repl_inst_histinfo_add" function call below. * histinfo.histinfo_num * histinfo.prev_histinfo_num * histinfo.last_histinfo_num[] */ /* Add the histinfo record to the instance file and flush the changes in the journal pool to the file header */ repl_inst_histinfo_add(&histinfo); if (!was_crit) rel_lock(jnlpool->jnlpool_dummy_reg); if (switch_jnl) { SET_GBL_JREC_TIME; /* jnl_ensure_open/jnl_file_extend and its callees assume jgbl.gbl_jrec_time is set */ for (reg = gd_header->regions, region_top = gd_header->regions + gd_header->n_regions; reg < region_top; reg++) { gv_cur_region = reg; change_reg(); /* sets cs_addrs/cs_data (needed by jnl_ensure_open) */ if (!JNL_ENABLED(cs_addrs)) continue; grab_crit(gv_cur_region, WS_84); jpc = cs_addrs->jnl; /* Before writing to jnlfile, adjust jgbl.gbl_jrec_time if needed to maintain time order of jnl * records. This needs to be done BEFORE the jnl_ensure_open as that could write journal records * (if it decides to switch to a new journal file) */ jbp = jpc->jnl_buff; ADJUST_GBL_JREC_TIME(jgbl, jbp); jnl_status = jnl_ensure_open(gv_cur_region, cs_addrs); if (0 == jnl_status) { if (EXIT_ERR == SWITCH_JNL_FILE(jpc)) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_JNLEXTEND, 2, JNL_LEN_STR(cs_data)); } else { if (SS_NORMAL != jpc->status) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(7) jnl_status, 4, JNL_LEN_STR(cs_data), DB_LEN_STR(gv_cur_region), jpc->status); else rts_error_csa(CSA_ARG(cs_addrs) VARLSTCNT(6) jnl_status, 4, JNL_LEN_STR(cs_data), DB_LEN_STR(gv_cur_region)); } rel_crit(gv_cur_region); } } } fis-gtm-V7.0-005/sr_unix/gtmsource_seqno_init.c0000644000032200000250000002055214342376330020451 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2017 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_string.h" #include "gtm_inet.h" #include "gtm_fcntl.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "error.h" #include "repl_msg.h" #include "repl_shutdcode.h" #include "gtmsource.h" #include "jnl.h" #include "gtmmsg.h" #include "repl_instance.h" GBLREF gd_addr *gd_header; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF repl_conn_info_t *this_side; error_def(ERR_NOREPLCTDREG); error_def(ERR_REPLINSTDBMATCH); error_def(ERR_REPLINSTDBSTRM); /* Find the start_jnl_seqno */ void gtmsource_seqno_init(boolean_t this_side_std_null_coll) { boolean_t is_supplementary; gd_region *region_top, *reg; int4 idx; sgmnt_addrs *csa, *repl_csa; sgmnt_data_ptr_t csd; seq_num db_seqno, replinst_seqno, strm_db_seqno[MAX_SUPPL_STRMS], strm_inst_seqno, zqgblmod_seqno; sm_uc_ptr_t gld_fn; unix_db_info *udi; /* Unix and VMS have different field names for now, but will both be soon changed to instfilename instead of gtmgbldir */ gld_fn = (sm_uc_ptr_t)jnlpool->jnlpool_ctl->jnlpool_id.instfilename; zqgblmod_seqno = 0; region_top = gd_header->regions + gd_header->n_regions; db_seqno = 0; replinst_seqno = jnlpool->repl_inst_filehdr->jnl_seqno; /* The stream specific jnl seqnos are valid only if this is a supplementary instance. */ is_supplementary = jnlpool->repl_inst_filehdr->is_supplementary; if (is_supplementary) { /* Since this is a supplementary instance, the 0th stream should be at least 1 (even if the db file header * still says 0). The first update to that replicated database will set the strm_reg_seqno to a non-zero value. * See repl_inst_create.c for similar code and comment on why this is needed for a supplementary instance. * By a similar argument, streams 1 thru 15 also need to have seqno of at least 1 as that will be the seqno * assigned for the next update which happens on that stream. This adjustment of seqno 0 to 1 avoids spurious * ERR_REPLINSTDBSTRM errors further down in this module. */ for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) strm_db_seqno[idx] = 1; } for (reg = gd_header->regions; reg < region_top; reg++) { assert(reg->open); csa = &FILE_INFO(reg)->s_addrs; csd = csa->hdr; if (REPL_ALLOWED(csd)) { if (db_seqno < csd->reg_seqno) db_seqno = csd->reg_seqno; if (is_supplementary) { for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) { if (strm_db_seqno[idx] < csd->strm_reg_seqno[idx]) strm_db_seqno[idx] = csd->strm_reg_seqno[idx]; } } if (zqgblmod_seqno < csd->zqgblmod_seqno) zqgblmod_seqno = csd->zqgblmod_seqno; } } if (0 == db_seqno) { /* No replicated region, or databases created with older * version of GTM */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_NOREPLCTDREG, 3, LEN_AND_LIT("instance file"), gld_fn); /* Error, has to shutdown all regions 'cos mupip needs exclusive access to turn replication on */ gtmsource_exit(ABNORMAL_SHUTDOWN); } /* Assert that the jnl seqno of the instance is greater than or equal to the start_seqno of the last histinfo record in the * instance file. If this was not the case, a REPLINSTSEQORD error would have been issued in "jnlpool_init" */ assert(!jnlpool->jnlpool_ctl->last_histinfo_seqno || (replinst_seqno >= jnlpool->jnlpool_ctl->last_histinfo_seqno)); /* Check if jnl seqno in db and instance file match */ if (0 != replinst_seqno) { if (db_seqno != replinst_seqno) { /* Journal seqno from the databases does NOT match that stored in the replication instance file header. */ udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_REPLINSTDBMATCH, 4, LEN_AND_STR(udi->fn), &replinst_seqno, &db_seqno); gtmsource_exit(ABNORMAL_SHUTDOWN); } if (is_supplementary) { /* Check that each of the potentially 16 stream seqnos are also identical between db and instance file */ for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) { strm_inst_seqno = jnlpool->repl_inst_filehdr->strm_seqno[idx]; if (strm_inst_seqno && (strm_db_seqno[idx] != strm_inst_seqno)) { udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_REPLINSTDBSTRM, 5, LEN_AND_STR(udi->fn), &strm_inst_seqno, idx, &strm_db_seqno[idx]); assert(FALSE); gtmsource_exit(ABNORMAL_SHUTDOWN); } } } } else { /* Instance file header has no seqno values. Initialize it from the db file header. */ jnlpool->repl_inst_filehdr->jnl_seqno = db_seqno; if (is_supplementary) { /* Initialize each of the potentially 16 stream seqnos from the db */ idx = 0; jnlpool->repl_inst_filehdr->strm_seqno[idx] = strm_db_seqno[idx]; idx++; /* For streams 1 thru 15, if the db seqno is at 1, it means that stream * has no updates yet in this instance. In that case, keep the instance file * header at seqno of 0 to avoid showing unused streams as being used. * For stream 0 though, we use it always in case of a supplementary * instance so initialize it even if it is to the seqno 1. */ for ( ; idx < MAX_SUPPL_STRMS; idx++) { assert(0 < strm_db_seqno[idx]); jnlpool->repl_inst_filehdr->strm_seqno[idx] = (1 < strm_db_seqno[idx]) ? strm_db_seqno[idx] : 0; } } } /* At this point, we are guaranteed there is no other process attached to the journal pool (since our parent source * server command is still waiting with the ftok lock for the pool to be initialized by this child). Even then it * does not hurt to get the lock on the journal pool before updating fields in there. */ DEBUG_ONLY(repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs;) assert(!repl_csa->hold_onto_crit); /* so it is ok to invoke "grab_lock" and "rel_lock" unconditionally */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); jnlpool->jnlpool_ctl->start_jnl_seqno = db_seqno; jnlpool->jnlpool_ctl->jnl_seqno = db_seqno; jnlpool->jnlpool_ctl->max_zqgblmod_seqno = zqgblmod_seqno; jnlpool->jnlpool_ctl->prev_jnlseqno_time = 0; if (is_supplementary) { /* Copy stream jnl seqno info from instance file header to jnlpool. * From this point onwards, only the jnlpool will have uptodate values for strm_seqno. * Therefore only that should be used by whoever wants to find out the current strm_seqno. */ for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) jnlpool->jnlpool_ctl->strm_seqno[idx] = jnlpool->repl_inst_filehdr->strm_seqno[idx]; } /* Initialize details for this side of the replication connection. Do it while we still have the jnlpool lock. */ assert(this_side == &jnlpool->jnlpool_ctl->this_side); this_side->proto_ver = REPL_PROTO_VER_THIS; this_side->jnl_ver = JNL_VER_THIS; this_side->is_std_null_coll = this_side_std_null_coll; this_side->trigger_supported = GTMTRIG_ONLY(TRUE) NON_GTMTRIG_ONLY(FALSE); /* The following 3 members make sense only if the other side of a replication connection is also known. Since * this_side talks about the properties of this instance, these 3 dont make sense in this context. When a connection * to the other side is made, each source server's gtmsource_local->remote_side will have these fields appropriately set. */ this_side->cross_endian = FALSE; this_side->endianness_known = FALSE; this_side->null_subs_xform = FALSE; this_side->is_supplementary = is_supplementary; rel_lock(jnlpool->jnlpool_dummy_reg); DEBUG_ONLY( /* Assert that seqno fields in "gtmsrc_lcl" array are within the instance journal seqno. * This is taken care of in "mur_close_files". */ for (idx = 0; NUM_GTMSRC_LCL > idx; idx++) { if ('\0' != jnlpool->gtmsrc_lcl_array[idx].secondary_instname[0]) { assert(jnlpool->gtmsrc_lcl_array[idx].resync_seqno <= db_seqno); assert(jnlpool->gtmsrc_lcl_array[idx].connect_jnl_seqno <= db_seqno); } } ) jnlpool->jnlpool_ctl->pool_initialized = TRUE; /* It is only now that the journal pool is completely initialized */ } fis-gtm-V7.0-005/sr_unix/gtmsource_showbacklog.c0000755000032200000250000001201614342376330020603 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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_string.h" #include "gtm_stdio.h" #if !defined(__MVS__) && !defined(VMS) #include #endif #include #include "gtm_inet.h" #include #ifdef VMS #include /* Required for gtmsource.h */ #endif #include "error.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_dbg.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "util.h" #include "repl_log.h" #include "is_proc_alive.h" #include "min_max.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF gtmsource_options_t gtmsource_options; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; error_def(ERR_LASTTRANS); error_def(ERR_SRCSRVNOTEXIST); error_def(ERR_SRCBACKLOGSTATUS); int gtmsource_showbacklog(void) { seq_num heartbeat_jnl_seqno, jnl_seqno, read_jnl_seqno, src_backlog; gtmsource_local_ptr_t gtmsourcelocal_ptr; char * lasttrans[] = {"posted ","sent ","acknowledged "}; char * syncstate[] = {"is behind by", "has not acknowledged", "is ahead by"}; int4 index, syncstateindex = 0; boolean_t srv_alive; assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); if (NULL != jnlpool->gtmsource_local) /* Show backlog for a specific source server */ gtmsourcelocal_ptr = jnlpool->gtmsource_local; else gtmsourcelocal_ptr = &jnlpool->gtmsource_local_array[0]; for (index = 0; index < NUM_GTMSRC_LCL; index++, gtmsourcelocal_ptr++) { if ('\0' == gtmsourcelocal_ptr->secondary_instname[0]) { assert(NULL == jnlpool->gtmsource_local); continue; } /* If SHOWBACKLOG on a specific secondary instance is requested, print the backlog information irrespective * of whether a source server for that instance is alive or not. For SHOWBACKLOG on ALL secondary instances * print backlog information only for those instances that have an active or passive source server alive. */ heartbeat_jnl_seqno = gtmsourcelocal_ptr->heartbeat_jnl_seqno; if ((NULL == jnlpool->gtmsource_local) && (0 == gtmsourcelocal_ptr->gtmsource_pid)) continue; repl_log(stderr, TRUE, TRUE, "Initiating SHOWBACKLOG operation on source server pid [%d] for secondary instance [%s]\n", gtmsourcelocal_ptr->gtmsource_pid, gtmsourcelocal_ptr->secondary_instname); /* jnlpool->jnlpool_ctl->jnl_seqno >= gtmsourcelocal_ptr->read_jnl_seqno is the most common case; * see gtmsource_readpool() for when the rare case can occur * Within a Source Server, jnlpool->jnlpool_ctl->jnl_seqno and gtmsourcelocal_ptr->read_jnl_seqno * counters start with 1 and cannot be 0 or less. heartbeat_jnl_seqno is 0 whenever the Source Server * restarts or we have an empty database. * Use local variables for arithmetic computation to prevent adjustments to the memory structure. */ if (0 < jnlpool->jnlpool_ctl->jnl_seqno) jnl_seqno = jnlpool->jnlpool_ctl->jnl_seqno - 1; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_LASTTRANS, 3, LEN_AND_STR(lasttrans[0]), &jnl_seqno); if (0 < gtmsourcelocal_ptr->read_jnl_seqno) read_jnl_seqno = gtmsourcelocal_ptr->read_jnl_seqno - 1; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_LASTTRANS, 3, LEN_AND_STR(lasttrans[1]), &read_jnl_seqno); if (0 != heartbeat_jnl_seqno) heartbeat_jnl_seqno--; assert(0 <= heartbeat_jnl_seqno); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_LASTTRANS, 3, LEN_AND_STR(lasttrans[2]), &heartbeat_jnl_seqno); src_backlog = MAX(jnl_seqno, read_jnl_seqno) - heartbeat_jnl_seqno; if (0 == heartbeat_jnl_seqno) syncstateindex = 1; if (heartbeat_jnl_seqno > MIN(jnl_seqno, read_jnl_seqno)) { src_backlog = heartbeat_jnl_seqno - MIN(jnl_seqno, read_jnl_seqno); syncstateindex = 2; } gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SRCBACKLOGSTATUS, 5, LEN_AND_STR(gtmsourcelocal_ptr->secondary_instname), LEN_AND_STR(syncstate[syncstateindex]), &src_backlog); srv_alive = (0 == gtmsourcelocal_ptr->gtmsource_pid) ? FALSE : is_proc_alive(gtmsourcelocal_ptr->gtmsource_pid, 0); if (!srv_alive) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) MAKE_MSG_WARNING(ERR_SRCSRVNOTEXIST), 2, LEN_AND_STR(gtmsourcelocal_ptr->secondary_instname)); else if ((gtmsourcelocal_ptr->mode == GTMSOURCE_MODE_PASSIVE) || (gtmsourcelocal_ptr->mode == GTMSOURCE_MODE_ACTIVE_REQUESTED)) util_out_print("WARNING - Source Server is in passive mode, transactions are not being replicated", TRUE); if (NULL != jnlpool->gtmsource_local) break; } return (NORMAL_SHUTDOWN); } fis-gtm-V7.0-005/sr_unix/gtmsource_shutdown.c0000644000032200000250000003234214342376330020154 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2017 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_unistd.h" #include "gtm_string.h" #include "gtm_inet.h" #include "gtm_fcntl.h" #include "gtm_socket.h" #include #include #include #include #include #include "repl_instance.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_dbg.h" #include "gtm_stdio.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "is_proc_alive.h" #include "repl_comm.h" #include "repl_log.h" #include "ftok_sems.h" #include "gtm_c_stack_trace.h" #ifdef DEBUG #include "wbox_test_init.h" #include "gtmio.h" #include "anticipatory_freeze.h" #include "gtm_threadgbl.h" #endif GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF uint4 process_id; GBLREF int gtmsource_srv_count; GBLREF gtmsource_options_t gtmsource_options; GBLREF boolean_t is_src_server; GBLREF void (*call_on_signal)(); GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; GBLREF int pool_init; GBLREF gd_addr *gd_header; error_def(ERR_JNLPOOLSETUP); error_def(ERR_REPLFTOKSEM); error_def(ERR_TEXT); int gtmsource_shutdown(boolean_t auto_shutdown, int exit_status) { boolean_t all_dead, first_time, ftok_counter_halted, regrab_lock, sem_incremented; uint4 savepid[NUM_GTMSRC_LCL]; int status, shutdown_status, save_errno, max_loopcnt; int4 index, maxindex, lcnt, num_src_servers_running; unix_db_info *udi; gtmsource_local_ptr_t gtmsourcelocal_ptr; #ifdef DEBUG DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; #endif /* Significance of shutdown field in gtmsource_local: * This field is initially set to NO_SHUTDOWN. When a command to shut down the source server is issued, * the process initiating the shutdown sets this field to SHUTDOWN. The Source Server on sensing * that it has to shut down (reads SHUTDOWN in the shutdown field), flushes the database regions, writes * (NORMAL_SHUTDOWN + its exit value) into this field and exits. On seeing a non SHUTDOWN value * in this field, the process which initiated the shutdown removes the ipcs and exits with the exit value * which is a combination of gtmsource_local->shutdown and its own exit value. * * Note : Exit values should be positive for error indication, zero for normal exit. */ call_on_signal = NULL; /* Don't reenter on error */ assert(pool_init); /* should have attached to the journal pool before coming here */ assert(NULL != jnlpool); udi = (unix_db_info *)FILE_INFO(jnlpool->jnlpool_dummy_reg); if (!auto_shutdown) { /* ftok semaphore and jnlpool access semaphore should already be held from the previous call to "jnlpool_init" */ assert(udi->grabbed_ftok_sem); assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); if (NULL != jnlpool->gtmsource_local) { /* Shutdown source server for the secondary instance specified in the command line */ savepid[0] = jnlpool->gtmsource_local->gtmsource_pid; /* Set flag to signal concurrently running source server to shutdown */ jnlpool->gtmsource_local->shutdown = SHUTDOWN; repl_log(stdout, TRUE, TRUE, "Initiating SHUTDOWN operation on source server pid [%d] for secondary" " instance [%s]\n", savepid[0], jnlpool->gtmsource_local->secondary_instname); maxindex = 1; /* Only one process id to check */ } else { /* Shutdown ALL source servers that are up and running */ gtmsourcelocal_ptr = &jnlpool->gtmsource_local_array[0]; for (maxindex = 0, index = 0; index < NUM_GTMSRC_LCL; index++, gtmsourcelocal_ptr++) { savepid[index] = gtmsourcelocal_ptr->gtmsource_pid; if (0 < savepid[index]) { gtmsourcelocal_ptr->shutdown = SHUTDOWN; repl_log(stdout, TRUE, TRUE, "Initiating SHUTDOWN operation on source server pid [%d] " "for secondary instance [%s]\n", savepid[index], gtmsourcelocal_ptr->secondary_instname); maxindex = index + 1; /* Check at least until pid corresponding to "index" */ } } } /* Wait for source server(s) to die. But before that release ftok semaphore and jnlpool access control semaphore. * This way, other processes (either in this environment or a different one) don't encounter startup issues. * However, to ensure that a concurrent argument-less rundown doesn't remove these semaphores (in case they * are orphaned), increment the counter semaphore. */ if (0 != incr_sem(SOURCE, SRC_SERV_COUNT_SEM)) { save_errno = errno; repl_log(stderr, TRUE, TRUE, "Could not increment Journal Pool counter semaphore : %s. " "Shutdown did not complete\n", STRERROR(save_errno)); /* Even though we hold the FTOK and JNL_POOL_ACCESS_SEM before entering this function (as ensured by * asserts above), it is safe to release them in case of a premature error (like this one). The caller * doesn't rely on the semaphores being held and this function is designed to release these semaphores * eventually anyways (after gtmsource_ipc_cleanup()) */ repl_inst_ftok_sem_release(); status = rel_sem(SOURCE, JNL_POOL_ACCESS_SEM); assert(0 == status); return ABNORMAL_SHUTDOWN; } if (0 != rel_sem(SOURCE, JNL_POOL_ACCESS_SEM)) { save_errno = errno; repl_log(stderr, TRUE, TRUE, "Could not release Journal Pool access control semaphore : %s. " "Shutdown did not complete\n", STRERROR(save_errno)); repl_inst_ftok_sem_release(); /* see comment above for why this is okay */ status = decr_sem(SOURCE, SRC_SERV_COUNT_SEM); assert(0 == status); return ABNORMAL_SHUTDOWN; } repl_inst_ftok_sem_release(); regrab_lock = sem_incremented = TRUE; gvinit(); /* Get the gd header*/ /* Wait for ONE particular or ALL source servers to die */ max_loopcnt = GTMSOURCE_MAX_SHUTDOWN_WAITLOOP(gd_header); repl_log(stdout, TRUE, TRUE, "Waiting for upto [%d] seconds for the source server to shutdown\n", max_loopcnt); for (lcnt = 1; max_loopcnt >= lcnt; lcnt++) { all_dead = TRUE; for (index = 0; index < maxindex; index++) { if ((0 < savepid[index]) && is_proc_alive(savepid[index], 0)) { all_dead = FALSE; # ifdef DEBUG if (!(lcnt % 60)) GET_C_STACK_FROM_SCRIPT("ERR_SHUTDOWN_INFO", process_id, savepid[index], lcnt); # endif } } if (!all_dead) SHORT_SLEEP(GTMSOURCE_WAIT_FOR_SHUTDOWN) else break; } if (max_loopcnt < lcnt) { /* Max timeout over, take stack trace of all the source server(s) which are still running. * Display the list of pids that wont die along with the secondary instances they correspond to. * Users need to kill these pids and reissue the shutdown command for the journal pool to be cleaned up. */ repl_log(stderr, TRUE, TRUE, "Error : Timed out waiting for following source server process(es) to die\n"); for (lcnt = 0, index = 0; index < maxindex; index++) { if ((0 < savepid[index]) && is_proc_alive(savepid[index], 0)) { lcnt++; GET_C_STACK_FROM_SCRIPT("ERR_SHUTDOWN", process_id, savepid[index], lcnt); if (NULL != jnlpool->gtmsource_local) { assert(0 == index); gtmsourcelocal_ptr = jnlpool->gtmsource_local; } else gtmsourcelocal_ptr = &jnlpool->gtmsource_local_array[index]; repl_log(stderr, FALSE, FALSE, " ---> Source server pid [%d] for secondary instance [%s] is still alive\n", savepid[index], gtmsourcelocal_ptr->secondary_instname); } } repl_log(stderr, FALSE, TRUE, "Shutdown cannot proceed. Stop the above processes and reissue " "the shutdown command.\n"); status = decr_sem(SOURCE, SRC_SERV_COUNT_SEM); assert(0 == status); return ABNORMAL_SHUTDOWN; } } else { sem_incremented = FALSE; if (gtmsource_srv_count) { repl_log(stdout, TRUE, TRUE, "Initiating shut down\n"); /* A non-zero gtmsource_srv_count indicates we are the spawned off child source server. That means we * are not holding any semaphores. More importantly, none of the source server's mainline code holds * the ftok or the access control semaphore anymore. So, even if we reach here due to an external signal * we are guaranteed that we don't hold any semaphores. Assert that. */ assert(!udi->grabbed_ftok_sem); assert(!holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); regrab_lock = TRUE; } else { assert(udi->grabbed_ftok_sem); assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); /* Do not release lock as this is a case of the source server startup command coming here after the * forked off child source server errored out at startup itself. Just in case the jnlpool has not * yet been initialized (possible if this process created the journal pool) we do not want to * release the lock and let someone else sneak in and see uninitialized data. Better to remove the * journal pool before anyone can come in. Hence hold on to the lock. */ regrab_lock = FALSE; } } if (regrab_lock) { /* Now that the source servers are shutdown, regrab the FTOK and access control semaphore (IN THAT ORDER to avoid * deadlocks) */ repl_inst_ftok_sem_lock(); # ifdef DEBUG /* Sleep for a few seconds to test for concurrent argument-less RUNDOWN to ensure that the latter doesn't remove * the JNL_POOL_ACCESS_SEM under the assumption that it is orphaned. */ if (gtm_white_box_test_case_enabled && (WBTEST_LONGSLEEP_IN_REPL_SHUTDOWN == gtm_white_box_test_case_number)) { DBGFPF((stderr, "GTMSOURCE_SHUTDOWN is about to start long sleep\n")); LONG_SLEEP(10); } # endif if (0 > (status = grab_sem(SOURCE, JNL_POOL_ACCESS_SEM))) { save_errno = errno; repl_log(stderr, TRUE, TRUE, "Could not acquire Journal Pool access control semaphore : %s. " "Shutdown not complete\n", STRERROR(save_errno)); repl_inst_ftok_sem_release(); status = decr_sem(SOURCE, SRC_SERV_COUNT_SEM); assert(0 == status); return ABNORMAL_SHUTDOWN; } /* Now that the locks are re-acquired, decrease the counter sempahore */ if (sem_incremented && (0 > (status = decr_sem(SOURCE, SRC_SERV_COUNT_SEM)))) { save_errno = errno; repl_log(stderr, TRUE, TRUE, "Could not decrement Journal Pool counter semaphore : %s." "Shutdown not complete\n", STRERROR(save_errno)); repl_inst_ftok_sem_release(); status = rel_sem(SOURCE, JNL_POOL_ACCESS_SEM); assert(0 == status); return ABNORMAL_SHUTDOWN; } } if (!auto_shutdown) { first_time = TRUE; for (index = 0; index < maxindex; index++) { if (NULL != jnlpool->gtmsource_local) { assert(0 == index); gtmsourcelocal_ptr = jnlpool->gtmsource_local; } else gtmsourcelocal_ptr = &jnlpool->gtmsource_local_array[index]; exit_status = gtmsourcelocal_ptr->shutdown; if (SHUTDOWN == exit_status) { if (0 == savepid[index]) /* No source server */ exit_status = NORMAL_SHUTDOWN; else /* Source Server crashed */ { repl_log(stderr, first_time, TRUE, "Source Server pid [%d] (secondary instance [%s])" " exited abnormally. MUPIP RUNDOWN might be warranted\n", savepid[index], gtmsourcelocal_ptr->secondary_instname); first_time = FALSE; } } } if (!first_time) /* At least one source server did not exit normally. Reset "exit_status" */ exit_status = ABNORMAL_SHUTDOWN; } shutdown_status = exit_status; /* gtmsource_ipc_cleanup will not be successful unless source server has completely exited. * It relies on SRC_SERV_COUNT_SEM value. One thing to note here is that if shutdown of a specific source server * is requested and that is successfully shutdown we should return NORMAL_SHUTDOWN if other source servers * are running (currently returned as an ABNORMAL_SHUTDOWN "exit_status" in "gtmsource_ipc_cleanup". But if any * other error occurs in that function causing it to return ABNORMAL_SHUTDOWN, then we should return ABNORMAL_SHUTDOWN * from this function as well. */ ftok_counter_halted = jnlpool->jnlpool_ctl->ftok_counter_halted; /* Copy before jnlpool->jnlpool_ctl is NULLed */ if (FALSE == gtmsource_ipc_cleanup(auto_shutdown, &exit_status, &num_src_servers_running)) rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); else { /* Journal Pool and Access Control Semaphores removed. Invalidate corresponding fields in file header */ repl_inst_jnlpool_reset(); } if (!ftok_sem_release(jnlpool->jnlpool_dummy_reg, !ftok_counter_halted && udi->counter_ftok_incremented, FALSE)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_JNLPOOLSETUP); assert(!num_src_servers_running || (ABNORMAL_SHUTDOWN == exit_status)); return (((1 == maxindex) && num_src_servers_running) ? shutdown_status : exit_status); } void gtmsource_stop(boolean_t exit) { int status; assert(gtmsource_srv_count || is_src_server); status = gtmsource_end1(TRUE); status = gtmsource_shutdown(TRUE, status) - NORMAL_SHUTDOWN; if (exit) gtmsource_exit(status); return; } void gtmsource_sigstop(void) { if (is_src_server) gtmsource_stop(FALSE); return; } fis-gtm-V7.0-005/sr_unix/gtmsource_srv_latch.c0000644000032200000250000001215114342376330020262 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2021 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 #include "aswp.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "copy.h" #include "interlock.h" #include "performcaslatchcheck.h" #include "relqop.h" #include "wcs_sleep.h" #include "caller_id.h" #include "rel_quant.h" #include "sleep_cnt.h" #include "gtmsource_srv_latch.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_instance.h" #include "have_crit.h" #include "util.h" /* For OUT_BUFF_SIZE */ GBLREF int4 process_id; GBLREF int num_additional_processors; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnl_gbls_t jgbl; #ifdef DEBUG GBLREF node_local_ptr_t locknl; GBLREF gd_region *gv_cur_region; GBLREF boolean_t is_src_server; #endif error_def(ERR_REPLREQROLLBACK); error_def(ERR_TEXT); /* Note we don't increment fast_lock_count as part of getting the latch and decrement it when releasing it because ROLLBACK * can hold onto this latch for a long while and can do updates in this duration and we should NOT have a non-zero fast_lock_count * as many places like t_begin/dsk_read have asserts to this effect. It is okay to NOT increment fast_lock_count as ROLLBACK * anyways have logic to disable interrupts the moment it starts doing database updates. */ boolean_t grab_gtmsource_srv_latch(sm_global_latch_ptr_t latch, uint4 max_timeout_in_secs, uint4 onln_rlbk_action) { uint4 spins, maxspins, retries, max_retries; unix_db_info *udi; sgmnt_addrs *repl_csa; boolean_t cycle_mismatch; char scndry_msg[OUT_BUFF_SIZE]; assert(!have_crit(CRIT_HAVE_ANY_REG)); udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); repl_csa = &udi->s_addrs; maxspins = num_additional_processors ? MAX_LOCK_SPINS(LOCK_SPINS, num_additional_processors) : 1; /* outer-loop : X minutes, 1 loop in 4 is sleep of 1 ms */ max_retries = (max_timeout_in_secs < (UINT32_MAX / 4 / 1000)) ? (max_timeout_in_secs * 4 * 1000) : UINT32_MAX; for (retries = max_retries - 1; 0 < retries; retries--) { /* seems like it should be a mutex */ for (spins = maxspins; 0 < spins; spins--) { assert(latch->u.parts.latch_pid != process_id); /* We better not hold it if trying to get it */ if (GET_SWAPLOCK(latch)) { DEBUG_ONLY(locknl = repl_csa->nl); /* Use the journal pool to maintain lock history */ LOCK_HIST("OBTN", latch, process_id, retries); DEBUG_ONLY(locknl = NULL); if (jnlpool->repl_inst_filehdr->file_corrupt && !jgbl.onlnrlbk) { /* Journal pool indicates an abnormally terminated online rollback. Cannot continue until * the rollback command is re-run to bring the journal pool/file and instance file to a * consistent state. */ SNPRINTF(scndry_msg, OUT_BUFF_SIZE, "Instance file header has file_corrupt field set to " "TRUE"); /* No need to release the latch before rts_error_csa (mupip_exit_handler will do it for * us). */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_REPLREQROLLBACK, 2, LEN_AND_STR(udi->fn), ERR_TEXT, 2, LEN_AND_STR(scndry_msg)); } cycle_mismatch = (repl_csa->onln_rlbk_cycle != jnlpool->jnlpool_ctl->onln_rlbk_cycle); assert((ASSERT_NO_ONLINE_ROLLBACK != onln_rlbk_action) || !cycle_mismatch); if ((HANDLE_CONCUR_ONLINE_ROLLBACK == onln_rlbk_action) && cycle_mismatch) { assert(is_src_server); SYNC_ONLN_RLBK_CYCLES; gtmsource_onln_rlbk_clnup(); /* side-effect : sets gtmsource_state */ rel_gtmsource_srv_latch(latch); } return TRUE; } } if (retries & 0x3) { /* On all but every 4th pass, do a simple rel_quant */ rel_quant(); } else { /* On every 4th pass, we bide for awhile */ wcs_sleep(LOCK_SLEEP); /* Check if we're due to check for lock abandonment check or holder wakeup */ if (0 == (retries & (LOCK_CASLATCH_CHKINTVL - 1))) performCASLatchCheck(latch, TRUE); } } DUMP_LOCKHIST(); assert(FALSE); assert(jnlpool->gtmsource_local && jnlpool->gtmsource_local->gtmsource_pid); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_SRVLCKWT2LNG, 2, jnlpool->gtmsource_local->gtmsource_pid, max_timeout_in_secs); return FALSE; /* to keep the compiler happy */ } boolean_t rel_gtmsource_srv_latch(sm_global_latch_ptr_t latch) { sgmnt_addrs *repl_csa; repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; DEBUG_ONLY(locknl = repl_csa->nl); LOCK_HIST("RLSE", latch, process_id, 0); DEBUG_ONLY(locknl = NULL); assert(process_id == latch->u.parts.latch_pid); RELEASE_SWAPLOCK(latch); return TRUE; } boolean_t gtmsource_srv_latch_held_by_us() { return (process_id == jnlpool->gtmsource_local->gtmsource_srv_latch.u.parts.latch_pid); } fis-gtm-V7.0-005/sr_unix/gtmsource_srv_latch.h0000644000032200000250000000147614342376330020277 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2018 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. * * * ****************************************************************/ #ifndef GTMSOURCE_SRV_LATCH_INCLUDED #define GTMSOURCE_SRV_LATCH_INCLUDED boolean_t grab_gtmsource_srv_latch(sm_global_latch_ptr_t latch, uint4 max_timeout_in_secs, uint4 onln_rlbk_action); boolean_t rel_gtmsource_srv_latch(sm_global_latch_ptr_t latch); boolean_t gtmsource_srv_latch_held_by_us(void); #endif fis-gtm-V7.0-005/sr_unix/gtmsource_statslog.c0000755000032200000250000000436214342376330020145 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2017 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_string.h" #include "gtm_inet.h" #include "gtm_stdio.h" #include "gtm_fcntl.h" #include "gtmio.h" #if !defined(__MVS__) && !defined(VMS) #include #endif #include #include #ifdef UNIX #include #endif #ifdef VMS #include /* Required for gtmsource.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "repl_sp.h" #include "gtmsource.h" #include "repl_dbg.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "util.h" #include "repl_log.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF gtmsource_options_t gtmsource_options; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; error_def(ERR_REPLLOGOPN); int gtmsource_statslog(void) { assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); repl_log(stderr, TRUE, TRUE, "Initiating STATSLOG operation on source server pid [%d] for secondary instance [%s]\n", jnlpool->gtmsource_local->gtmsource_pid, jnlpool->gtmsource_local->secondary_instname); if (gtmsource_options.statslog == jnlpool->gtmsource_local->statslog) { util_out_print("STATSLOG is already !AD. Not initiating change in stats log", TRUE, gtmsource_options.statslog ? strlen("ON") : strlen("OFF"), gtmsource_options.statslog ? "ON" : "OFF"); return (ABNORMAL_SHUTDOWN); } if (!gtmsource_options.statslog) { jnlpool->gtmsource_local->statslog = FALSE; jnlpool->gtmsource_local->statslog_file[0] = '\0'; util_out_print("STATSLOG turned OFF", TRUE); return (NORMAL_SHUTDOWN); } jnlpool->gtmsource_local->statslog = TRUE; util_out_print("Stats log turned on", TRUE); return (NORMAL_SHUTDOWN); } fis-gtm-V7.0-005/sr_unix/gtmsource_stopfilter.c0000755000032200000250000000347214342376330020501 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2017 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_string.h" #include "gtm_inet.h" #include "gtm_stdio.h" #if !defined(__MVS__) && !defined(VMS) #include #endif #include #include #ifdef UNIX #include #endif #ifdef VMS #include /* Required for gtmsource.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_dbg.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "util.h" #include "repl_log.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF gtmsource_options_t gtmsource_options; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; int gtmsource_stopfilter(void) { assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); repl_log(stderr, TRUE, TRUE, "Initiating STOPSOURCEFILTER operation on source server pid [%d] for secondary instance [%s]\n", jnlpool->gtmsource_local->gtmsource_pid, jnlpool->gtmsource_local->secondary_instname); if ('\0' == jnlpool->gtmsource_local->filter_cmd[0]) { util_out_print("No filter currently active", TRUE); return (ABNORMAL_SHUTDOWN); } jnlpool->gtmsource_local->filter_cmd[0] = '\0'; util_out_print("Stop filter initiated", TRUE); return (NORMAL_SHUTDOWN); } fis-gtm-V7.0-005/sr_unix/gtmsrc.csh0000755000032200000250000000731314342376330016046 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2020 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. # # # ################################################################# # ########################################################################################### # # gtmsrc.csh - release-specific definitions # # gtmsrc.csh is an auxiliary shell script invoked by setactive.csh to define release- # specific environment variables and aliases for this release. (The original version # of this file just defined release-specific source directories, hence the name.) # For most releases, this is straightforward, but incremental releases require the # specification of the release(s) upon which the current increment is based. # # This script defines the following environment variables: # # gtmroutines - pathname for GT.M to lookup M sources and object files # # gt_as_option_I - assembler option(s) specifying the location(s) of the # assembly language header files (usually *.si) # # gt_cc_option_I - C compiler option(s) specifying the location(s) of the C # header files (*.h) # ########################################################################################### # # This file is copied to $gtm_root// only because V63011 and prior versions source it. # Copying to $gtm_root// can be avoided if there are no V63011 and prior versions. # These env vars are set in setactive.csh. They are duplicated here only because V63011 and prior versions need it. # These lines can be removed once there are no V63011 and prior versions. setenv gtm_inc "$gtm_ver/inc" setenv gtm_pct "$gtm_ver/pct" setenv gtm_src "$gtm_ver/src" setenv gtm_tools "$gtm_ver/tools" #################################### if !($?gtmroutines) then setenv gtmroutines "" endif # Check for utf8 mode two ways, 1) by gtm_chset and 2) by active routines set utf = "" if ($?gtm_chset) then if ("UTF-8" == "$gtm_chset") set utf="/utf8" else if ("utf8" == "$gtm_exe:t") then set utf="/utf8" endif # The only place where gtmsrc.csh is sourced is setactive.csh. The current setactive.csh simply sets $gtmroutines to "." # But this "rebuild" of gtmroutines is necessary when sourcing setactive.csh of versions V63011 and earlier, which do # not set gtmroutines to ".". This section can be removed once there are no V63011 and prior versions. set rtns = ($gtmroutines:x) if (0 < $#rtns) then @ rtncnt = $#rtns # Strip off "$gtm_exe/plugin/o($gtm_exe/plugin/r)" if present; assumption, it's at the end if ("$rtns[$rtncnt]" =~ "*/plugin/o*(*/plugin/r)") @ rtncnt-- # Strip off "$gtm_exe"; assumption, it's next to last or the last if ($?gtmsrc_last_exe) then if ("${rtns[$rtncnt]:s;/utf8;;:s;*;;}" == "${gtmsrc_last_exe:s;/utf8;;:s;*;;}") @ rtncnt-- endif setenv gtmroutines "$rtns[-$rtncnt]" unset rtncnt else setenv gtmroutines "." endif if (-d $gtm_exe/plugin/o && -d $gtm_exe/plugin/r) then setenv gtmroutines "$gtmroutines $gtm_exe$utf $gtm_exe/plugin/o$utf($gtm_exe/plugin/r)" else setenv gtmroutines "$gtmroutines $gtm_exe$utf" endif setenv gtmsrc_last_exe $gtm_exe unset rtns utf8 setenv gtm_version_change `date` source $gtm_tools/gtm_env.csh unsetenv gtm_version_change if !($?gt_as_option_I) then setenv gt_as_option_I "-I$gtm_inc" else setenv gt_as_option_I "$gt_as_option_I -I$gtm_inc" endif if !($?gt_cc_option_I) then setenv gt_cc_option_I "-I$gtm_inc" else setenv gt_cc_option_I "$gt_cc_option_I -I$gtm_inc" endif fis-gtm-V7.0-005/sr_unix/gtmstart.gtc0000755000032200000250000000632014342376330016411 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright 2001, 2012 Fidelity Information Services, Inc # # # # This source code contains the intellectual property # # of its copyright holder(s), and is made available # # under a license. If you do not know the terms of # # the license, please stop and do not read further. # # # ################################################################# arch=ARCH gtm_dist=GTMDIST echo=ECHO logdir=$gtm_dist/log multiservers=0 server_list= export gtm_dist while [ $# -ne 0 ] do case $1 in -autorestart) GTCM_RESTART=1 export GTCM_RESTART ;; *) multiservers=1 server_list="$server_list $1" ;; esac shift done if [ ! -d $logdir ] then $echo "GT.M/GT.CM logging directory ($logdir) does not exist, creating it..." mkdir $logdir fi if [ ! -f $gtm_dist/gtcm_server ] then exit 0 fi if [ $arch = "sun" ]; then ps -ax | fgrep gtcm_s | fgrep -v grep > /usr/tmp/tmp$$ else ps -ea | fgrep gtcm_s > /usr/tmp/tmp$$ fi if [ $? -eq 0 ] then $echo "The following server(s) are running:" cat /usr/tmp/tmp$$ fi rm /usr/tmp/tmp$$ $echo "Do you want to start GT.CM? (Y or N)\c" read resp $echo if [ "$resp" = "Y" -o "$resp" = "y" ] ; then if [ $gtmgbldir ] ; then $echo > /dev/null else $echo "The environment variable gtmgbldir is not defined. gtcm_server will" $echo "not run correctly without this variable. Please do the following" $echo "to start GT.M up correctly: " $echo " 1) Define gtmgbldir. (Please see the manual for instructions.)" $echo " 2) Rerun this script." exit fi if [ "$GTCM_RESTART" -eq 1 ] then $echo "Starting the GT.CM server(s) in auto-restart mode." else $echo "Starting the GT.CM server(s)." fi if [ $multiservers -eq 1 ]; then for i in $server_list do while read service id options do if [ "$service" != "$i" ] ; then continue fi echo "Starting GT.CM (${service}, ${id})..." if [ ! -d $logdir/$service ] then $echo "logging directory (${logdir}/${service}) does not exist, creating it..." mkdir $logdir/$service fi nohup $gtm_dist/gtcm_run -service $service -id ${id} -log - $options \ >> $logdir/${service}/session.log 2>&1 < /dev/null & if [ $? != 0 ]; then $echo "The GT.CM server (${service}) failed to start." fi sleep 1 done < $gtm_dist/gtcm_slist done else while read service id options do if [ "$service" = "#" -o -z "$id" ] ; then continue fi echo "Starting GT.CM (${service}, ${id})..." if [ ! -d $logdir/$service ] then $echo "logging directory (${logdir}/${service}) does not exist, creating it..." mkdir $logdir/$service fi nohup $gtm_dist/gtcm_run -service $service -id ${id} -log - $options \ >> $logdir/${service}/session.log 2>&1 < /dev/null & if [ $? != 0 ]; then $echo "The GT.CM server (${service}) failed to start." fi sleep 1 done < $gtm_dist/gtcm_slist fi else exit fi fis-gtm-V7.0-005/sr_unix/gtmstop.gtc0000755000032200000250000000510614342376330016242 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright (c) 2001-2019 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. # # # ################################################################# gtm_dist=GTMDIST echo=ECHO arch=ARCH tmpfile="/tmp/tmp$$" if [ "`whoami`" != "root" ] ; then $echo "If you want to perform a rundown on anyone" $echo "else's MUMPS process, you must run this program as root." $echo \\n fi $echo "Signalling all MUMPS processes to proceed with rundown." $echo \\n ps -ef | grep mumps | grep -v grep | awk '{ print "kill -15", $2, " ; sleep 1" }' | sh > /dev/null 2>&1 $echo "Waiting for MUMPS processes to rundown." $echo \\n sleep 10 $echo "Resignalling processes." $echo \\n ps -ef | grep mumps | grep -v grep | awk '{ print "kill -15", $2, " ; sleep 1" }' | sh > /dev/null 2>&1 ps -ef | fgrep mumps | fgrep -v grep > $tmpfile if [ $? -eq 0 ] then $echo "The following mumps process(es) are still running:" cat $tmpfile fi rm -f $tmpfile if [ "`whoami`" != "root" ] ; then $echo "You are not root. Unable to terminate some GT.M processes" else $echo "Do you want to stop GT.M server(s) if present? (y or n)\c" read resp if [ "$resp" = "Y" -o "$resp" = "y" ] ; then $echo \\n if [ -f $gtm_dist/gtcm_server ]; then $echo "Stopping GT.CM server(s) ...." ps -ef | grep gtcm_run | grep -v grep | awk '{ print "kill -15", $2 }' | sh > /dev/null 2>&1 ps -ef | grep gtcm_s | grep -v grep | awk '{ print "kill -15", $2, " ; sleep 1" }' | sh > /dev/null 2>&1 sleep 10 ps -ef | grep gtcm_s | grep -v grep > $tmpfile if [ $? -eq 0 ] then $echo "server(s) did not respond to kill -15." $echo "sending TRAP signal to the gtcm_server(s)." ps -ef | grep gtcm_s | grep -v grep | awk '{ print "kill -5", $2, " ; sleep 2" }' | sh > /dev/null 2>&1 sleep 4 fi fi if [ -f $gtm_dist/shmclean ]; then $echo "Cleaning up shared memory segments and semaphores..." # # Greystone suggests adding mupip rundown -region for each region # in your active databases. This ensures flushing and deletion # of the appropriate shared memory segments and semaphores. # # example: $gtm_dist/mupip rundown -region DEFAULT # $gtm_dist/shmclean -q -d $gtm_dist/shmclean -q -s fi $echo "Shutdown complete." fi fi fis-gtm-V7.0-005/sr_unix/gtmthreadgblasm.m0000644000032200000250000001502514342376330017367 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2014-2017 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Routine to read gtm_threadgbl_deftypes.h and for the given platform and the threadgbl variables defined in the input ; file gtm_threadgbl_asm_access.txt, generate the proper #define type declarations to enable assembler routine access ; to those variables. ; ; Argument: Takes three file names as arguments ; $gtm_tools/gtm_threadgbl_asm_access.txt ; ; ; ; Output: This routine writes the following symbol defininitions for each field defined in gtm_threadgbl_asm_access.txt ; ; - ggo_ - Defines the variable's offset within the threadgbl structure. ; - ggl_ - Defines the variable's data length (in bytes) ; set accesstxt=$piece($zcmdline," ",1) set defsin=$piece($zcmdline," ",2) set defsout=$piece($zcmdline," ",3) set TAB=$char(9) set months="January,February,March,April,May,June,July,August,September,October,November,December" ; ; Prefixes (for the desired variable name) we want to collect. All must be the same length ; set varprefixcnt=2 ; Count of variables below set varprefix("# define ggo_")=1 ; Defines variable offset set varprefix("# define ggl_")=1 ; Defines variable length set varprefixlen=$zlength($order(varprefix(""))) ; ; See which platform we are running on so we know how to generate records for variables ; set platform("AIX","RS6000")="AIXOnPSeries" set platform("HP-UX","IA64")="HPUXOnIA64" set platform("Linux","x86")="LinuxOnX8632" set platform("Linux","x86_64")="LinuxOnX8664" set platform("Solaris","SPARC")="SolarisOnSPARC" ; ; Note AIX comments handled separated as their comments are the same as C. ; set commentchar("HPUXOnIA64")="//" set commentchar("LinuxOnX8632")="#" set commentchar("LinuxOnX8664")="#" set commentchar("SolarisOnSPARC")="!" set gtmzv=$ZVersion set gtmos=$ZPiece(gtmzv," ",3) set gtmhdwr=$ZPiece(gtmzv," ",4) if (0=$get(platform(gtmos,gtmhdwr))) do . write "gtmthreadgblasm: Not running on a recognized platform",! . set $etrap="zgoto 0" . zhalt 1 set platform=platform(gtmos,gtmhdwr) set fmtrtn="formatrecfor"_platform ; ; Check argument ; if ($zlength($zcmdline," ")'=3) do . write "gtmthreadgblasm: Invalid arguments ",! . write "gtmthreadgblasm: $gtm_tools/gtm_threadgbl_asm_access.txt .in .si",! . set $etrap="zgoto 0" . zhalt 1 ; ; Read in variable list to make available to assembler routines ; set infile=accesstxt open infile:readonly use infile set maxvarlen=0 for i=1:1 do quit:$zeof . read line . quit:$zeof . quit:(("#"=$extract(line,1))!(""=line)) . do verify(.line) . set globacc(line)=0 . set globvarlen=$zlength(line) . if (maxvarlen /* For intptr_t */ #include "inttypes.h" /* .. ditto (defined different places in different platforms) .. */ #ifdef __osf__ /* Ensure 32-bit pointers for compatibility with GT.M internal representations. */ #pragma pointer_size (save) #pragma pointer_size (short) #endif typedef int gtm_status_t; typedef int gtm_int_t; typedef unsigned int gtm_uint_t; #if defined(__osf__) typedef int gtm_long_t; typedef unsigned int gtm_ulong_t; #else typedef long gtm_long_t; typedef unsigned long gtm_ulong_t; #endif typedef float gtm_float_t; typedef double gtm_double_t; typedef char gtm_char_t; typedef int (*gtm_pointertofunc_t)(); /* Structure for passing (non-NULL-terminated) character arrays whose length corresponds to the value of the * 'length' field. Note that for output-only gtm_string_t * arguments the 'length' field is set to the * preallocation size of the buffer pointed to by 'address', while the first character of the buffer is '\0'. */ typedef struct { gtm_long_t length; gtm_char_t *address; } gtm_string_t; #ifdef __osf__ #pragma pointer_size (restore) #endif struct extcall_string { long len; char *addr; }; #if !defined(__alpha) typedef intptr_t gtm_tid_t; #else typedef int gtm_tid_t; #endif typedef void *gtm_fileid_ptr_t; typedef struct { gtm_string_t rtn_name; void* handle; } ci_name_descriptor; /* Define deprecated types for backward compatibility. */ typedef gtm_status_t xc_status_t; typedef gtm_int_t xc_int_t; typedef gtm_uint_t xc_uint_t; typedef gtm_long_t xc_long_t; typedef gtm_ulong_t xc_ulong_t; typedef gtm_float_t xc_float_t; typedef gtm_double_t xc_double_t; typedef gtm_char_t xc_char_t; typedef gtm_string_t xc_string_t; typedef gtm_pointertofunc_t xc_pointertofunc_t; typedef gtm_fileid_ptr_t xc_fileid_ptr_t; /* Java types with special names for clarity. */ typedef gtm_int_t gtm_jboolean_t; typedef gtm_int_t gtm_jint_t; typedef gtm_long_t gtm_jlong_t; typedef gtm_float_t gtm_jfloat_t; typedef gtm_double_t gtm_jdouble_t; typedef gtm_char_t gtm_jstring_t; typedef gtm_char_t gtm_jbyte_array_t; typedef gtm_char_t gtm_jbig_decimal_t; /* Call-in interface. */ gtm_status_t gtm_ci(const char *c_rtn_name, ...); gtm_status_t gtm_ci_filter(const char *c_rtn_name, ...); gtm_status_t gtm_cip(ci_name_descriptor *ci_info, ...); gtm_status_t gtm_init(void); #ifdef GTM_PTHREAD gtm_status_t gtm_jinit(void); #endif gtm_status_t gtm_exit(void); gtm_status_t gtm_cij(const char *c_rtn_name, char **arg_blob, int count, int *arg_types, unsigned int *io_vars_mask, unsigned int *has_ret_value); void gtm_zstatus(char* msg, int len); /* Other entry points accessable in libgtmshr. */ gtm_status_t gtm_filename_to_id(gtm_string_t *filename, gtm_fileid_ptr_t *fileid); void gtm_hiber_start(gtm_uint_t mssleep); void gtm_hiber_start_wait_any(gtm_uint_t mssleep); void gtm_start_timer(gtm_tid_t tid, gtm_int_t time_to_expir, void (*handler)(), gtm_int_t hdata_len, void *hdata); void gtm_cancel_timer(gtm_tid_t tid); gtm_status_t gtm_is_file_identical(gtm_fileid_ptr_t fileid1, gtm_fileid_ptr_t fileid2); void gtm_xcfileid_free(gtm_fileid_ptr_t fileid); int gtm_is_main_thread(void); void *gtm_malloc(size_t); void gtm_free(void *); #endif /* GTMXC_TYPES_H */ fis-gtm-V7.0-005/sr_unix/gv_trig_cmd_table.h0000644000032200000250000000144214342376330017646 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ GV_TRIG_CMD_ENTRY(GVTR_CMDTYPE_SET, "S", 0x01) /* for SET */ GV_TRIG_CMD_ENTRY(GVTR_CMDTYPE_KILL, "K", 0x02) /* for KILL */ GV_TRIG_CMD_ENTRY(GVTR_CMDTYPE_ZKILL, "ZK", 0x04) /* for ZKILL or ZWITHDRAW */ GV_TRIG_CMD_ENTRY(GVTR_CMDTYPE_ZTKILL, "ZTK", 0x08) /* for ZTKILL */ GV_TRIG_CMD_ENTRY(GVTR_CMDTYPE_ZTRIGGER, "ZTR", 0x10) /* For ZTRIGGER */ fis-gtm-V7.0-005/sr_unix/gv_trigger.h0000644000032200000250000010473214342376330016360 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2021 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. * * * ****************************************************************/ #ifndef GV_TRIGGER_H_INCLUDED #define GV_TRIGGER_H_INCLUDED #include "gv_trigger_common.h" /* ^#t related macros (common to both Unix and VMS) */ #include "hashtab_mname.h" /* for COMPUTE_HASH_MNAME macro */ error_def(ERR_GVIS); error_def(ERR_GVZTRIGFAIL); error_def(ERR_TRIGREPLSTATE); #define HASHT_OPT_ISOLATION "I" #define HASHT_OPT_NOISOLATION "NOI" #define HASHT_OPT_CONSISTENCY "C" #define HASHT_OPT_NOCONSISTENCY "NOC" typedef enum { #define GV_TRIG_CMD_ENTRY(cmdtype, cmdlit, cmdmaskval) cmdtype, #include "gv_trig_cmd_table.h" #undef GV_TRIG_CMD_ENTRY GVTR_CMDTYPES /* Total number of command types related to triggers */ } gvtr_cmd_type_t; #define GVTR_SUBS_STAR 1 /* Allow ANY value for this subscript */ #define GVTR_SUBS_POINT 2 /* Allow a FIXED value for this subscript */ #define GVTR_SUBS_RANGE 3 /* Allow a RANGE of values for this subscript */ #define GVTR_SUBS_PATTERN 4 /* Allow all values for subscript that match a specified pattern */ #define GVTR_RANGE_OPEN_LEN (uint4)-1 /* length assigned to open side of a range (e.g. "a": has an open right side); * is considered an impossible value for the length of any valid subscript */ #define GVTR_LIST_ELE_SIZE 8 /* size of each element in gv_trig_list buddy list (see comment in gv_trigger.c) */ #define GV_TRIG_LIST_INIT_ALLOC 256 /* we anticipate 256 bytes to be used by each trigger so start the buddy list there */ #define MAX_TRIG_UTIL_LEN 40 /* needed for storing the trigger index and the property (CMD, TRIGNAME.. etc.) * in util_buff to be passed as the last parameter for TRIGDEFBAD error message. * Both the index and the property name are guaranteed to be less than 20 * and hence MAX_TRIG_UTIL_LEN set to 40 should be enough */ /* Miscellaneous structures needed to build the global variable trigger superstructures : gv_trigger_t and gvt_trigger_t */ typedef struct gvtr_subs_star_struct { uint4 gvtr_subs_type; uint4 filler_8byte_align; union gvtr_subs_struct *next_range; } gvtr_subs_star_t; typedef struct gvtr_subs_point_struct { uint4 gvtr_subs_type; uint4 len; union gvtr_subs_struct *next_range; char *subs_key; } gvtr_subs_point_t; typedef struct gvtr_subs_range_struct { uint4 gvtr_subs_type; uint4 len1; union gvtr_subs_struct *next_range; char *subs_key1; uint4 len2; char *subs_key2; } gvtr_subs_range_t; typedef struct gvtr_subs_pattern_struct { uint4 gvtr_subs_type; uint4 filler_8byte_align; union gvtr_subs_struct *next_range; mval pat_mval; } gvtr_subs_pattern_t; typedef union gvtr_subs_struct { uint4 gvtr_subs_type; gvtr_subs_star_t gvtr_subs_star; gvtr_subs_point_t gvtr_subs_point; gvtr_subs_range_t gvtr_subs_range; gvtr_subs_pattern_t gvtr_subs_pattern; } gvtr_subs_t; typedef gtm_num_range_t gvtr_piece_t; /* gv_trigger_t is the structure describing the static components of ONE trigger for a given global variable name. At the time of * trigger invocation, there is some process-context-specific information that needs to be passed in which is done in a separate * gtm_trigger_parms structure (defined in gtm_trigger.h). The below fields are arranged in order to ensure minimal structure * padding added by the compiler. This might not seem logical at first (for e.g. the related fields is_zdelim & delimiter are in * different sections). */ typedef struct gv_trigger_struct { struct gv_trigger_struct /* 3 chains - one for each command type since a given trigger could be on all 3. */ *next_set, /* Next SET type trigger for this global */ *next_kill, /* Next KILL type trigger for this global */ *next_ztrig; /* Next ZTRIGGER type trigger for this global */ uint4 cmdmask; /* bitwise OR of all commands defining this trigger in ^#t(,,"CMD"). * e.g. if ^#t(..,"CMD") is "S,K", cmdmask will be "GVTR_OPER_SET | GVTR_OPER_KILL" */ uint4 numsubs; /* # of comma-separated subscripts specified for this particular trigger */ uint4 numlvsubs; /* # of subscripts for which the trigger requested a local variable name to be bound. * i.e. numlvsubs <= numsubs is always true. */ uint4 numpieces; /* # of contiguous piece ranges specified in the trigger */ gvtr_subs_t *subsarray; /* pointer to an array of "numsubs" number of gvtr_subs_t structures. * NULL if no subscript specified in the trigger (i.e. numsubs = 0) and is very unusual. */ uint4 *lvindexarray; /* pointer to an array of "numlvsubs" number of uint4 type fields which contain the index * in "subsarray" of the subscript whose local variable name binding this corresponds to. */ mname_entry *lvnamearray; /* pointer to an array of "numlvsubs" number of mname_entry structures that contain the * local variable names which need to be bound to each subscript at trigger invocation. * If no subscripts have lvns specified, this is NULL. * e.g. if the trigger specified "^GBL(lvn1=:,1,lvn3=:) ..." in this case, * numsubs = 3, numlvsubs = 2, lvindexarray[0] = 0, lvindexarray[1] = 2, * lvnamearray[0].var_name = "lvn1", lvnamearray[1].var_name = "lvn3", */ gtm_num_range_t *piecearray; /* pointer to an array of "numpieces" ranges (range is the closed interval [min,max]). * e.g, if the piece string specified in ^#t(,,"PIECES") is "4:6;8" then * numpieces = 2 * piecearray[0].min=4, piecearray[0].max=6 * piecearray[1].min=8, piecearray[1].max=8 */ rtn_tabent rtn_desc; /* Compiled routine name/objcode; Inited by gvtr_db_read_hasht() */ boolean_t is_zdelim; /* TRUE if "ZDELIM" was specified; FALSE if "DELIM" was specified */ mval delimiter; /* is a copy of ^#t(,,"DELIM") or ^#t(..."ZDELIM") whichever is defined */ mstr options; /* is a copy of ^#t(,,"OPTIONS") */ mval xecute_str; /* Trigger code to execute */ struct gvt_trigger_struct /* top of gvt_trigger block this trigger belongs to. Used in src lookup when we know */ *gvt_trigger; /* .. gv_trigger_t but not owning region or trigger#. Allows us to get both with no * additional lookup */ } gv_trigger_t; /* Structure describing ALL triggers for a given global variable name */ typedef struct gvt_trigger_struct { uint4 gv_trigger_cycle; /* copy of ^#t(,"#CYCLE") */ uint4 num_gv_triggers; /* copy of ^#t(,"#COUNT") */ struct gv_trigger_struct *set_triglist; /* -> circular list of SET triggers (using next_set link) */ struct gv_trigger_struct *kill_triglist; /* -> circular list of KILL type triggers (using next_kill link) */ struct gv_trigger_struct *ztrig_triglist; /* -> circular list of ZTRIG triggers (using next_ztrig link) */ gv_namehead *gv_target; /* gv_target that owns these triggers - used in trigr src lkup */ gv_trigger_t *gv_trig_top; /* top of the array of triggers */ struct buddy_list_struct *gv_trig_list; /* buddy list that maintains mallocs done inside gv_trig_array */ gv_trigger_t *gv_trig_array; /* array of triggers read in from ^#t(,...) */ } gvt_trigger_t; /* Structure describing parameters passed (from gvcst_put/gvcst_kill) to trigger invocation routine */ typedef struct gvtr_invoke_parms_struct { gvt_trigger_t *gvt_trigger; /* Input parameter */ gvtr_cmd_type_t gvtr_cmd; /* Input parameter */ int num_triggers_invoked; /* Output parameter : # of triggers invoked by an update */ } gvtr_invoke_parms_t; /* Requires #include of op.h, tp_set_sgm.h, t_begin.h in the caller of this macro */ #define GVTR_INIT_AND_TPWRAP_IF_NEEDED(CSA, CSD, GVT, GVT_TRGR, LCL_TSTART, IS_TPWRAP, T_ERR) \ { \ GBLREF boolean_t skip_dbtriggers; /* see gbldefs.c for description of this global */ \ GBLREF sgmnt_data_ptr_t cs_data; \ GBLREF uint4 dollar_tlevel; \ GBLREF jnl_gbls_t jgbl; \ GBLREF uint4 t_err; \ GBLREF trans_num local_tn; \ GBLREF uint4 update_trans; \ \ DEBUG_ONLY(GBLREF gv_namehead *reset_gv_target;) \ DEBUG_ONLY(GBLREF boolean_t donot_INVOKE_MUMTSTART;) \ DEBUG_ONLY(GBLREF tp_frame *tp_pointer;) \ DEBUG_ONLY(GBLREF stack_frame *frame_pointer;) \ DEBUG_ONLY(GBLREF mv_stent *mv_chain;) \ DEBUG_ONLY(GBLREF unsigned char *msp;) \ DEBUG_ONLY(GBLREF int tprestart_state;) \ DEBUG_ONLY(GBLREF sgm_info *sgm_info_ptr;) \ DEBUG_ONLY(gv_namehead *save_reset_gv_target;) \ DEBUG_ONLY(boolean_t was_nontp;) \ \ LITREF mval literal_batch; \ uint4 cycle; \ boolean_t set_upd_trans_t_err, cycle_mismatch, db_trigger_cycle_mismatch, ztrig_cycle_mismatch; \ DBGTRIGR_ONLY(char gvnamestr[MAX_MIDENT_LEN + 1];) \ \ assert(TPRESTART_STATE_NORMAL == tprestart_state); \ assert(!skip_dbtriggers); \ /* If start of transaction, read in GVT's triggers from ^#t global if not already done. If restart and if it was due to \ * GVT's triggers being out-of-date re-read them. Note theoretically either CSD or CSA can be used to get the cycle for \ * comparison with GVT but while using CSD can relatively reduce the # of times "gvtr_init" is invoked, it can also \ * cause an issue where a nested trigger for the same global can cause a restart which unloads a running trigger \ * causing problems (see trigthrash subtest in triggers test suite specifically trigthrash3.m). For that reason, we \ * stick wish CSA. \ */ \ cycle = CSA->db_trigger_cycle; \ assert(CSD == cs_data); \ /* triggers can be invoked only by updates currently */ \ assert(!dollar_tlevel || sgm_info_ptr); \ assert((sgm_info_ptr && (dollar_tlevel && sgm_info_ptr->update_trans)) || (!dollar_tlevel && update_trans)); \ set_upd_trans_t_err = FALSE; \ ztrig_cycle_mismatch = (CSA->db_dztrigger_cycle && (GVT->db_dztrigger_cycle != CSA->db_dztrigger_cycle)); \ db_trigger_cycle_mismatch = (GVT->db_trigger_cycle != cycle); \ cycle_mismatch = (db_trigger_cycle_mismatch || ztrig_cycle_mismatch); \ DBGTRIGR_ONLY(memcpy(gvnamestr, GVT->gvname.var_name.addr, GVT->gvname.var_name.len);) \ DBGTRIGR_ONLY(gvnamestr[GVT->gvname.var_name.len]='\0';) \ DBGTRIGR((stderr, "GVTR_INIT_AND_TPWRAP_IF_NEEDED: CSA=%s GVT=%s\n", CSA->region->rname, gvnamestr)); \ DBGTRIGR((stderr, "GVTR_INIT_AND_TPWRAP_IF_NEEDED: CSA->db_trigger_cycle=%d\n", cycle)); \ DBGTRIGR((stderr, "GVTR_INIT_AND_TPWRAP_IF_NEEDED: GVT->db_trigger_cycle=%d\n", GVT->db_trigger_cycle)); \ DBGTRIGR((stderr, "GVTR_INIT_AND_TPWRAP_IF_NEEDED: CSA->db_dztrigger_cycle=%d\n", CSA->db_dztrigger_cycle)); \ DBGTRIGR((stderr, "GVTR_INIT_AND_TPWRAP_IF_NEEDED: GVT->db_dztrigger_cycle=%d\n", GVT->db_dztrigger_cycle)); \ DBGTRIGR((stderr, "GVTR_INIT_AND_TPWRAP_IF_NEEDED: local_tn=%d\n", local_tn)); \ DBGTRIGR((stderr, "GVTR_INIT_AND_TPWRAP_IF_NEEDED: GVT->trig_local_tn=%d\n", GVT->trig_local_tn)); \ DBGTRIGR((stderr, "GVTR_INIT_AND_TPWRAP_IF_NEEDED: ztrig_cycle_mismatch=%d\n", ztrig_cycle_mismatch)); \ DBGTRIGR((stderr, "GVTR_INIT_AND_TPWRAP_IF_NEEDED: db_trigger_cycle_mismatch=%d\n", db_trigger_cycle_mismatch)); \ DBGTRIGR((stderr, "GVTR_INIT_AND_TPWRAP_IF_NEEDED: cycle_mismatch=%d t_tries=%d\n", cycle_mismatch, t_tries)); \ /* Set up wrapper even if no triggers if this is for ZTRIGGER command */ \ if (cycle_mismatch || (NULL != GVT->gvt_trigger) || (ERR_GVZTRIGFAIL == T_ERR)) \ { /* Create TP wrap if needed */ \ if (!dollar_tlevel) \ { /* need to create implicit TP wrap */ \ DBGTRIGR((stderr, "GVTR_INIT_AND_TPWRAP_IF_NEEDED: creating TP wrapper\n")); \ DEBUG_ONLY(was_nontp = TRUE;) \ assert(!LCL_TSTART); \ /* Set a debug-only global variable to indicate that from now onwards, until the completion of this \ * tp-wrapped non-tp update, we dont expect "t_retry" to be called while this gvcst_put is in the \ * C-call-stack. This is because we have not set up the TP transaction using opp_tstart (like what M \ * code does) and so there is no point to go back to generated code (mdb_condition_handler invoked from \ * t_retry does this transfer of control using the MUM_TSTART macro). We instead expect to handle \ * retries internally in gvcst_put. We also expect any restarts occurring in nested trigger code to \ * eventually end up as a RESTART return code from "gtm_trigger" so we get to choose how to handle the \ * restart for this implicit TSTART. \ */ \ assert(!donot_INVOKE_MUMTSTART); \ DEBUG_ONLY(donot_INVOKE_MUMTSTART = TRUE;) \ /* With journal recovery, we expect it to play non-TP journal records as non-TP transactions and ZTP \ * journal records as ZTP transactions so we dont expect an implicit TP wrap to be done inside recovery \ * due to a trigger (as this means GT.M and recovery have different values for GVT->gvt_trigger which \ * is not possible). Assert that. \ */ \ assert(!jgbl.forw_phase_recovery); \ LCL_TSTART = TRUE; \ /* 0 ==> save no locals but RESTART OK */ \ op_tstart((IMPLICIT_TSTART + IMPLICIT_TRIGGER_TSTART), TRUE, &literal_batch, 0); \ /* Ensure that the op_tstart done above has set up the TP frame and that the first entry is \ * of MVST_TPHOLD type. \ */ \ assert((tp_pointer->fp == frame_pointer) && (MVST_TPHOLD == mv_chain->mv_st_type) \ && (msp == (unsigned char *)mv_chain)); \ IS_TPWRAP = TRUE; \ assert(!sgm_info_ptr || (!CSA->sgm_info_ptr->tp_set_sgm_done && !CSA->sgm_info_ptr->update_trans)); \ tp_set_sgm(); \ /* tp_set_sgm above could modify CSA->db_trigger_cycle (from CSD->db_trigger_cycle). Set local variable \ * cycle to match CSA->db_trigger_cycle so as to pass the updated value to gvtr_init. Also in that \ * case recompute cycle_mismatch (and related variables) now that CSA->db_trigger_cycle changed. \ */ \ if (cycle != CSA->db_trigger_cycle) \ { \ cycle = CSA->db_trigger_cycle; \ /* Assert that if db_trigger_cycle mismatch was TRUE above, \ * it better be TRUE after 'cycle' update as well. \ */ \ assert(!db_trigger_cycle_mismatch || (GVT->db_trigger_cycle != cycle)); \ db_trigger_cycle_mismatch = (GVT->db_trigger_cycle != cycle); \ cycle_mismatch = (db_trigger_cycle_mismatch || ztrig_cycle_mismatch); \ } \ /* An implicit TP wrap is created for an explicit TP update. tp_set_sgm call done above will initalize \ * sgm_info_ptr for this TP transaction and will have sgm_info_ptr->update_trans set to zero. If the \ * op_tstart done above is for ^#t read, then set_upd_trans_t_err set to TRUE just after gvtr_init will \ * take care of resetting si->update_trans and t_err at macro exit. However, if this op_tstart is for \ * the actual transaction (for eg., triggers for this global is already read for this process and an \ * explicit non-tp update is in progress) then we need to set si->update_trans and t_err to correct \ * values before the macro exit. \ */ \ set_upd_trans_t_err = TRUE; \ assert(((0 < dollar_tlevel) || (ERR_GVZTRIGFAIL != T_ERR)) && (1)) ; /* &&(1) idents assert */ \ } else \ { \ /* Already in TP */ \ DEBUG_ONLY(was_nontp = FALSE;) \ assert(sgm_info_ptr == CSA->sgm_info_ptr); \ } \ if (cycle_mismatch) \ { /* Process' trigger view changed. Re-read triggers */ \ assert(GVT->gd_csa == CSA); \ if ((local_tn == GVT->trig_local_tn) && db_trigger_cycle_mismatch) \ { /* Already dispatched trigger for this gvn in this transaction - must restart. But do so ONLY \ * if the process' trigger view changed because of a concurrent trigger load/unload and NOT \ * because of $ZTRIGGER as part of this transaction as that could cause unintended restarts. \ */ \ assert(!LCL_TSTART); \ assert(UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])); \ DBGTRIGR((stderr, "GVTR_INIT_AND_TPWRAP_IF_NEEDED: throwing TP restart\n")); \ t_retry(cdb_sc_triggermod); \ } \ DEBUG_ONLY(save_reset_gv_target = reset_gv_target); \ gvtr_init(GVT, cycle, LCL_TSTART, T_ERR); \ /* ^#t reads done via gvtr_init will cause t_err to be set to GVGETFAIL which needs to be reset to \ * T_ERR (incoming parameter to this macro) before leaving the macro. Also, in case of an implicit \ * tstart (LCL_TSTART = TRUE), if a restart happens while reading ^#t global, gvtr_tpwrap_ch will be \ * invoked which inturn will invoke tp_restart that would reset sgm_info_ptr->update_trans to zero. The \ * caller of the macro (gvcst_put and gvcst_kill) relies on sgm_info_ptr->update_trans being non-zero. \ */ \ assert(((0 < dollar_tlevel) || (ERR_GVZTRIGFAIL != T_ERR)) && (2)) ; /* &&(2) idents assert */ \ set_upd_trans_t_err = TRUE; \ /* Check that gvtr_init does not play with "reset_gv_target" a global variable that callers of this \ * function (e.g. gvcst_put) might have set to a non-default value. \ */ \ assert(reset_gv_target == save_reset_gv_target); \ CSD = cs_data; /* if MM and db extension occurred, reset CSD to cs_data to avoid stale value */ \ } \ } \ GVT_TRGR = GVT->gvt_trigger; \ assert((NULL == GVT_TRGR) || dollar_tlevel); \ if ((NULL == GVT_TRGR) && IS_TPWRAP && !dollar_tlevel) \ { \ assert(set_upd_trans_t_err); \ assert(was_nontp); \ assert(0 == t_tries); \ /* We came in as a non-tp update and initiated op_tstart to do the ^#t reads. However, no triggers were defined \ * because of which we did the corresponding op_tcommit in gvtr_db_tpwrap_helper. Now the TP wrap is complete \ * restore any global variables the wrap messed with. \ */ \ IS_TPWRAP = LCL_TSTART = FALSE; \ DEBUG_ONLY(donot_INVOKE_MUMTSTART = FALSE;) \ } \ if (set_upd_trans_t_err) /* Reset update_trans/si->update_trans and t_err */ \ { \ if (!dollar_tlevel) \ { /* In t_begin we expect update_trans to always be 0. */ \ update_trans = 0; \ } \ /* If non-tp, the below macro will invoke t_begin which will set up the necessary structures for the Non-TP \ * update \ */ \ T_BEGIN_SETORKILL_NONTP_OR_TP(T_ERR); \ } \ } #define GVTR_OP_TCOMMIT(STATUS) \ { \ GBLREF boolean_t skip_INVOKE_RESTART; \ \ DEBUG_ONLY(GBLREF boolean_t donot_INVOKE_MUMTSTART;) \ DEBUG_ONLY(GBLREF tp_frame *tp_pointer;) \ DEBUG_ONLY(GBLREF stack_frame *frame_pointer;) \ DEBUG_ONLY(GBLREF mv_stent *mv_chain;) \ DEBUG_ONLY(GBLREF unsigned char *msp;) \ DEBUG_ONLY(GBLREF uint4 dollar_tlevel;) \ \ assert(dollar_tlevel); \ assert((tp_pointer->fp == frame_pointer) && (MVST_TPHOLD == mv_chain->mv_st_type) \ && (msp == (unsigned char *)mv_chain)); \ assert(!skip_INVOKE_RESTART); \ skip_INVOKE_RESTART = TRUE; /* causes op_tcommit to return code if restart situation */ \ STATUS = op_tcommit(); \ assert(!skip_INVOKE_RESTART); /* should have been reset by op_tcommit at very beginning */ \ DEBUG_ONLY(if (cdb_sc_normal == STATUS) donot_INVOKE_MUMTSTART = FALSE;) \ } /* Protect MVALs from garabage collection */ #define INCR_AND_PUSH_MV_STENT(VAR) \ { \ PUSH_MV_STENT(MVST_MVAL); \ VAR = &mv_chain->mv_st_cont.mvs_mval; \ VAR->mvtype = 0; \ trig_protected_mval_push_count++; \ } #define DECR_AND_POP_MV_STENT() \ { \ while (0 < trig_protected_mval_push_count--) \ POP_MV_STENT(); \ } #define RETURN_AND_POP_MVALS(RET) \ { /* Convenience helper macro to avoid repeated retun & pop */ \ DECR_AND_POP_MV_STENT(); \ return RET; \ } #define PUSH_ZTOLDMVAL_ON_M_STACK(ZTOLD_MVAL, SAVE_MSP, SAVE_MV_CHAIN) \ { \ GBLREF mv_stent *mv_chain; \ GBLREF unsigned char *msp; \ \ /* Create mval on the M-stack (thereby it is known to stp_gcol (BYPASSOK)) to save \ * pre-gvcst_put/gvcst_kill value. The memory needed to save the actual value will be obtained from the \ * stringpool later. \ */ \ assert(NULL == ZTOLD_MVAL); \ SAVE_MSP = msp; /* Save current msp & mv_chain to restore finally */ \ SAVE_MV_CHAIN = mv_chain; \ PUSH_MV_STENT(MVST_MVAL); /* protect $ztoldval from stp_gcol (BYPASSOK) */ \ ZTOLD_MVAL = &mv_chain->mv_st_cont.mvs_mval; \ ZTOLD_MVAL->mvtype = 0; /* make sure mval is setup enough to protect stp_gcol (BYPASSOK)(if invoked \ * below) from incorrectly reading its contents until it is fully initialized \ * later. */ \ } /* The POP_MVALS_FROM_M_STACK_IF_NEEDED macro below pops some mvals pushed on the stack but pops them in an unusual way for * performance reasons (not really popping them but just restoring previous pointers). The debug version of that macro will use the * slightly longer but verifying form of unwind defined below to make sure we aren't popping something we need in an invisible and * tough to track fashion. */ #ifdef DEBUG #define UNW_MV_STENT_TO(prev_msp, prev_mv_chain) \ { \ mv_stent *mvc; \ mvc = mv_chain; \ while (mvc < prev_mv_chain) \ { \ assert(MVST_MVAL == mvc->mv_st_type); \ mvc = (mv_stent *)(mvc->mv_st_next + (char *)mvc); \ } \ assert(prev_mv_chain == mvc); \ assert(prev_msp <= (unsigned char *)mvc); \ msp = prev_msp; \ mv_chain = mvc; \ } #else #define UNW_MV_STENT_TO(prev_msp, prev_mv_chain) \ { \ msp = prev_msp; \ mv_chain = prev_mv_chain; \ } #endif #define POP_MVALS_FROM_M_STACK_IF_NEEDED(ZTOLD_MVAL, SAVE_MSP, SAVE_MV_CHAIN) \ { \ GBLREF boolean_t skip_dbtriggers; \ GBLREF mv_stent *mv_chain; \ GBLREF unsigned char *msp; \ \ if (NULL != ZTOLD_MVAL) \ { /* ZTOLD_MVAL & potentially a few other mvals have been pushed onto the \ * M-stack. Pop them all in one restore of the M-stack. \ */ \ assert(!skip_dbtriggers); \ assert(SAVE_MSP > msp); \ if (SAVE_MSP > msp) \ UNW_MV_STENT_TO(SAVE_MSP, SAVE_MV_CHAIN); \ ZTOLD_MVAL = NULL; \ } \ } #define SET_GVTARGET_TO_HASHT_GBL(CSA) \ { \ mname_entry gvname; \ gv_namehead *hasht_tree; \ \ GBLREF gv_namehead *gv_target; \ \ hasht_tree = CSA->hasht_tree; \ if (NULL == hasht_tree) \ { /* Allocate gv_target like structure for "^#t" global in this database file */ \ gvname.var_name.addr = HASHT_GBLNAME; \ gvname.var_name.len = HASHT_GBLNAME_LEN; \ gvname.marked = FALSE; \ COMPUTE_HASH_MNAME(&gvname); \ hasht_tree = targ_alloc(CSA->hdr->max_key_size, &gvname, NULL); \ hasht_tree->gd_csa = CSA; \ CSA->hasht_tree = hasht_tree; \ } \ gv_target = hasht_tree; \ } #define INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED \ { \ unsigned char *key; \ \ GBLREF gv_key *gv_currkey; \ GBLREF gd_region *gv_cur_region; \ GBLREF sgmnt_data_ptr_t cs_data; \ \ key = &gv_currkey->base[0]; \ assert(gv_currkey->base + gv_currkey->end + HASHT_GBLNAME_FULL_LEN <= ((gv_currkey->base) + gv_currkey->top)); \ memcpy(key, HASHT_GBLNAME, HASHT_GBLNAME_FULL_LEN); /* including terminating '\0' subscript */ \ key += HASHT_GBLNAME_FULL_LEN; \ *key++ = '\0'; /* double '\0' for terminating key */ \ gv_currkey->end = HASHT_GBLNAME_FULL_LEN; \ /* Determine root block of ^#t global in this database file. Need to use gvcst_root_search for this. It expects \ * gv_currkey, gv_target, gv_cur_region, cs_addrs & cs_data to be set up appropriately. gv_currkey & gv_target \ * are already set up. The remaining should be set up which is asserted below. \ */ \ assert(&FILE_INFO(gv_cur_region)->s_addrs == cs_addrs); \ assert(cs_data == cs_addrs->hdr); \ /* Do the actual search for ^#t global in the directory tree */ \ GVCST_ROOT_SEARCH; \ } /* Caller has to check for "NULL" value of "cs_addrs" and act accordingly */ #define GVTR_SWITCH_REG_AND_HASHT_BIND_NAME(reg) \ { \ GBLREF gv_namehead *gv_target; \ GBLREF gd_region *gv_cur_region; \ GBLREF gv_key *gv_currkey; \ \ assert(!IS_STATSDB_REGNAME(reg)); /* caller should have ensured this */ \ if (!reg->open) \ gv_init_reg(reg, NULL); \ gv_cur_region = reg; \ change_reg(); /* TP_CHANGE_REG wont work as we need to set sgm_info_ptr */ \ if (NULL != cs_addrs) \ { \ SET_GVTARGET_TO_HASHT_GBL(cs_addrs); /* sets up gv_target */ \ assert(NULL != gv_target); \ INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED; /* sets up gv_currkey */ \ } else \ { \ DEBUG_ONLY(gv_target = NULL;) /* to keep cs_addrs/gv_target in sync */ \ DEBUG_ONLY(gv_currkey->base[0] = '\0';) /* to keep gv_target/gv_currkey in sync */ \ } \ } GBLREF uint4 dollar_tlevel; GBLREF int4 gtm_trigger_depth; GBLREF int4 tstart_trigger_depth; /* This macro returns if the current update is an EXPLICIT update or not. Any update done as part of a trigger invocation is not * considered an explicit update. Note that it is possible to do a TROLLBACK while inside trigger code. In this case, any updates * done after the trollback while still inside the trigger code are considered explicit updates. Hence the seemingly complicated * check below. There is a version without the asserts for use ONLY WITH the IS_OK_TO_INVOKE_GVCST_KILL macro where nested asserts * dont work well with the C preprocessor. */ #define IS_EXPLICIT_UPDATE (DBG_ASSERT(!dollar_tlevel || (tstart_trigger_depth <= gtm_trigger_depth)) \ IS_EXPLICIT_UPDATE_NOASSERT) #define IS_EXPLICIT_UPDATE_NOASSERT (!dollar_tlevel || (tstart_trigger_depth == gtm_trigger_depth)) /* Check if update is inside trigger (implicit update) and to a replicated database. If so check that corresponding triggering * update (explicit update) also occurred in a replicated database. If not this is an out-of-design situation as the replicating * secondary will see no journal records for this TP transaction (since the triggering update did not get replicated) and so cannot * keep the secondary in sync with the primary. In this case, issue an error. */ #define TRIG_CHECK_REPLSTATE_MATCHES_EXPLICIT_UPDATE(REG, CSA) \ { \ GBLREF boolean_t explicit_update_repl_state; \ GBLREF gv_key *gv_currkey; \ \ if (!IS_EXPLICIT_UPDATE && !explicit_update_repl_state && REPL_ALLOWED(CSA)) \ { \ unsigned char buff[MAX_ZWR_KEY_SZ], *end; \ \ if (0 == (end = format_targ_key(buff, MAX_ZWR_KEY_SZ, gv_currkey, TRUE))) \ end = &buff[MAX_ZWR_KEY_SZ - 1]; \ rts_error_csa(CSA_ARG(CSA) VARLSTCNT(8) ERR_TRIGREPLSTATE, 2, DB_LEN_STR(REG), ERR_GVIS, 2, \ end - buff, buff); \ } \ } #define TRIG_PROCESS_JNL_STR_NODEFLAGS(NODEFLAGS) \ { \ GBLREF boolean_t skip_dbtriggers; \ GBLREF mval dollar_ztwormhole; \ \ assert(!(JS_NOT_REPLICATED_MASK & NODEFLAGS)); \ skip_dbtriggers = (NODEFLAGS & JS_SKIP_TRIGGERS_MASK); \ if (NODEFLAGS & JS_NULL_ZTWORM_MASK) \ dollar_ztwormhole.str.len = 0; \ } #define JNL_FORMAT_ZTWORM_IF_NEEDED(CSA, WRITE_LOGICAL_JNLRECS, JNL_OP, KEY, VAL, ZTWORM_JFB, JFB, JNL_FORMAT_DONE) \ { \ GBLREF mval dollar_ztwormhole; \ GBLREF int4 gtm_trigger_depth; \ GBLREF int4 tstart_trigger_depth; \ GBLREF boolean_t skip_dbtriggers; \ GBLREF boolean_t explicit_update_repl_state; \ GBLREF uint4 dollar_tlevel; \ GBLREF jnl_gbls_t jgbl; \ \ uint4 nodeflags; \ \ /* No need to write ZTWORMHOLE journal records for updates inside trigger since those records are not \ * replicated anyway. \ */ \ assert(dollar_tlevel); /* tstart_trigger_depth is not usable otherwise */ \ assert(tstart_trigger_depth <= gtm_trigger_depth); \ assert(!skip_dbtriggers); /* we ignore the JS_SKIP_TRIGGERS_MASK bit in nodeflags below because of this */ \ ZTWORM_JFB = NULL; \ if (tstart_trigger_depth == gtm_trigger_depth) \ { /* explicit update so need to write ztwormhole records */ \ assert(WRITE_LOGICAL_JNLRECS == JNL_WRITE_LOGICAL_RECS(CSA)); \ nodeflags = 0; \ explicit_update_repl_state = REPL_ALLOWED(CSA); \ /* Write ZTWORMHOLE records only if replicating since secondary is the only one that cares about it. */ \ if (explicit_update_repl_state && dollar_ztwormhole.str.len) \ { /* $ZTWORMHOLE is non-NULL. Journal that BEFORE the corresponding SET record. If it is found \ * that the trigger invocation did not REFERENCE it, we will remove this from the list of \ * formatted journal records. \ */ \ ZTWORM_JFB = jnl_format(JNL_ZTWORM, NULL, &dollar_ztwormhole, 0); \ /* Note : ztworm_jfb could be NULL if it was determined that this ZTWORMHOLE record is not \ * needed i.e. if the exact same value of ZTWORMHOLE was already written as part of the \ * previous update in this TP transaction. \ */ \ } \ } else \ nodeflags = JS_NOT_REPLICATED_MASK; \ assert(!JNL_FORMAT_DONE); \ /* Need to write logical SET or KILL journal records irrespective of trigger depth */ \ if (WRITE_LOGICAL_JNLRECS) \ { \ nodeflags |= JS_HAS_TRIGGER_MASK; /* gvt_trigger is non-NULL */ \ if (!dollar_ztwormhole.str.len) \ { \ /* Set jgbl.prev_ztworm_ptr to NULL. This is needed so that any subsequent non-null ztwormhole \ * assignment which matches with the ztwormhole value before the NULL ztwormhole SHOULD write \ * ZTWORM records in the journal file. \ */ \ jgbl.save_ztworm_ptr = jgbl.prev_ztworm_ptr; \ jgbl.prev_ztworm_ptr = NULL; \ nodeflags |= JS_NULL_ZTWORM_MASK; \ } \ /* Insert SET journal record now that ZTWORMHOLE (if any) has been inserted */ \ JFB = jnl_format(JNL_OP, KEY, VAL, nodeflags); \ assert(NULL != JFB); \ JNL_FORMAT_DONE = TRUE; \ } \ } #define REMOVE_ZTWORM_JFB_IF_NEEDED(ZTWORM_JFB, JFB, SI) \ { \ GBLREF boolean_t ztwormhole_used; /* TRUE if $ztwormhole was used by trigger code */ \ GBLREF jnl_gbls_t jgbl; \ \ jnl_format_buffer *tmpjfb; \ \ if ((NULL != ZTWORM_JFB) && !ztwormhole_used) \ { /* $ZTWORMHOLE was non-zero before the trigger invocation and was never used inside the trigger. We \ * need to remove the corresponding formatted journal record. We dont free up the memory occupied by \ * ZTWORM_JFB as it is not easy to free up memory in the middle of a buddy list. This memory will \ * anyway be freed up eventually at tp_clean_up time. \ * NOTE: Trigger code that does NOT use the $ZTWORMHOLE is equivalent to a trigger code that has \ * $ZTWORMHOLE set to NULL and hence should have the JS_NULL_ZTWORM_MASK set in the nodeflags. But for \ * that to happen, checksum of the SET/KILL/ZTRIG records need to be recomputed. Since this is a costly \ * operation, we don't touch the nodeflags as there is no known correctness issue. \ */ \ assert(NULL != JFB); \ tmpjfb = ZTWORM_JFB->prev; \ if (NULL != tmpjfb) \ { \ tmpjfb->next = JFB; \ JFB->prev = tmpjfb; \ } else \ { \ assert(SI->jnl_head == ZTWORM_JFB); \ SI->jnl_head = JFB; \ assert(IS_UUPD(JFB->rectype)); \ assert(((jnl_record *)JFB->buff)->prefix.jrec_type == JFB->rectype); \ JFB->rectype--; \ assert(IS_TUPD(JFB->rectype)); \ ((jnl_record *)JFB->buff)->prefix.jrec_type = JFB->rectype; \ } \ jgbl.prev_ztworm_ptr = jgbl.save_ztworm_ptr; \ assert(0 < jgbl.cumul_index); \ DEBUG_ONLY(jgbl.cumul_index--;) \ jgbl.cumul_jnl_rec_len -= ZTWORM_JFB->record_size; \ } \ jgbl.save_ztworm_ptr = NULL; \ } #define SET_PARAM_STRING(UTIL_BUFF, UTIL_LEN, TRIGIDX, PARAM) \ { \ uchar_ptr_t util_ptr; \ \ util_ptr = i2asc(&UTIL_BUFF[0], TRIGIDX); /* UTIL_BUFF = 1 */ \ assert(MAX_TRIG_UTIL_LEN >= STR_LIT_LEN(PARAM)); \ MEMCPY_LIT(util_ptr, PARAM); /* UTIL_BUFF = 1,"CMD" */ \ util_ptr += STR_LIT_LEN(PARAM); \ UTIL_LEN = UINTCAST(util_ptr - &UTIL_BUFF[0]); \ assert(MAX_TRIG_UTIL_LEN >= UTIL_LEN); \ } /* This error macro is used for all definition errors where the target is ^#t(GVN,,) */ #define HASHT_GVN_DEFINITION_RETRY_OR_ERROR(INDEX,SUBSCRIPT,CSA) \ { \ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) \ t_retry(cdb_sc_triggermod); \ else \ { \ assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); \ /* format "INDEX,SUBSCRIPT" of ^#t(GVN,INDEX,SUBSCRIPT) in the error message */ \ SET_PARAM_STRING(util_buff, util_len, INDEX, SUBSCRIPT); \ rts_error_csa(CSA_ARG(CSA) VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, \ trigvn_len, trigvn, util_len, util_buff); \ } \ } #endif fis-gtm-V7.0-005/sr_unix/gv_trigger_protos.h0000644000032200000250000000275214342376330017765 0ustar librarygtc/**************************************************************** * * * Copyright 2010, 2014 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. * * * ****************************************************************/ #ifndef GV_TRIGGER_PROTOS_H_INCLUDED #define GV_TRIGGER_PROTOS_H_INCLUDED STATICFNDCL void gvtr_db_tpwrap_helper(sgmnt_addrs *csa, int err_code, boolean_t root_srch_needed); STATICFNDCL uint4 gvtr_process_range(gv_namehead *gvt, gvtr_subs_t *subsdsc, int type, char *start, char *end); STATICFNDCL uint4 gvtr_process_pattern(char *ptr, uint4 len, gvtr_subs_t *subsdsc, gvt_trigger_t *gvt_trigger); STATICFNDCL uint4 gvtr_process_gvsubs(char *start, char *end, gvtr_subs_t *subsdsc, boolean_t colon_imbalance, gv_namehead *gvt); STATICFNDCL boolean_t gvtr_is_key_a_match(char *keysub_start[], gv_trigger_t *trigdsc, mval *lvvalarray[]); STATICFNDCL boolean_t gvtr_is_value_a_match(mval *val, gv_trigger_t *trigdsc); boolean_t gvtr_get_hasht_gblsubs(mval *subs_mval, mval *ret_mval); void gvtr_db_read_hasht(sgmnt_addrs *csa); void gvtr_free(gv_namehead *gvt); void gvtr_init(gv_namehead *gvt, uint4 cycle, boolean_t tp_is_implicit, int err_code); int gvtr_match_n_invoke(gtm_trigger_parms *trigparms, gvtr_invoke_parms_t *gvtr_parms); #endif fis-gtm-V7.0-005/sr_unix/gvcmx_canremlk_stub.c0000755000032200000250000000104514342376330020245 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "gvcmx.h" void gvcmx_canremlk(void) {assert (FALSE);} fis-gtm-V7.0-005/sr_unix/gvcmx_reqremlk_stub.c0000755000032200000250000000111514342376330020271 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "gvcmx.h" bool gvcmx_reqremlk(unsigned char c, int4 timeout) { assert(FALSE); return(-1); } fis-gtm-V7.0-005/sr_unix/gvcmx_resremlk_stub.c0000755000032200000250000000107714342376330020302 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "gvcmx.h" bool gvcmx_resremlk(unsigned char c) { assert(FALSE); return(-1); } fis-gtm-V7.0-005/sr_unix/gvcmx_susremlk_stub.c0000755000032200000250000000107314342376330020317 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "gvcmx.h" void gvcmx_susremlk(unsigned char rmv_locks) { assert (FALSE); } fis-gtm-V7.0-005/sr_unix/gvcmy_open.h0000755000032200000250000000117214342376330016364 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GVCMY_OPEN_H_INCLUDED #define GVCMY_OPEN_H_INCLUDED void gvcmy_open(gd_region *reg, parse_blk *nb); #endif fis-gtm-V7.0-005/sr_unix/gvcmz_bunch_stub.c0000644000032200000250000000137414342376330017554 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "cmidef.h" #include "gvcmz.h" GBLDEF bool zdefactive; GBLDEF unsigned short zdefbufsiz; error_def(ERR_UNIMPLOP); void gvcmz_bunch(mval *v) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); } fis-gtm-V7.0-005/sr_unix/gvcmz_error_stub.c0000755000032200000250000000122114342376330017600 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "cmidef.h" #include "gvcmz.h" void gvcmz_error(char code, uint4 status) { assert (FALSE); } fis-gtm-V7.0-005/sr_unix/gvcmz_neterr_stub.c0000755000032200000250000000125714342376330017757 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "cmidef.h" #include "hashtab_mname.h" /* needed for cmmdef.h */ #include "cmmdef.h" #include "gvcmz.h" GBLDEF bool neterr_pending; void gvcmz_neterr(INTPTR_T *err) { assert(FALSE); } fis-gtm-V7.0-005/sr_unix/gvcmz_zflush_stub.c0000644000032200000250000000127714342376330017772 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "cmidef.h" #include "gvcmz.h" error_def(ERR_UNIMPLOP); void gvcmz_zflush(void) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); } fis-gtm-V7.0-005/sr_unix/gvcst_init_sysops.c0000644000032200000250000024006014342376330020000 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #ifndef __MVS__ #include #endif #include #include #include #include #include "gtm_stdlib.h" #include "gtm_ipc.h" #include "gtm_un.h" #include "gtm_socket.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "gtm_sem.h" #include "gtm_statvfs.h" #include "hugetlbfs_overrides.h" /* for the ADJUST_SHM_SIZE_FOR_HUGEPAGES macro */ #include "gt_timer.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "db_header_conversion.h" #include "gdsblk.h" #include "gdscc.h" #include "min_max.h" #include "gdsblkops.h" #include "filestruct.h" #include "parse_file.h" #include "jnl.h" #include "interlock.h" #include "io.h" #include "iosp.h" #include "error.h" #include "mutex.h" #include "gtmio.h" #include "mupipbckup.h" #include "gtmimagename.h" #include "mmseg.h" #include "gtmsecshr.h" #include "secshr_client.h" #include "ftok_sems.h" #include "repl_msg.h" #include "gtmsource.h" #include "anticipatory_freeze.h" /* Include prototypes */ #include "mlk_shr_init.h" #include "gtm_c_stack_trace.h" #include "eintr_wrappers.h" #include "eintr_wrapper_semop.h" #include "is_file_identical.h" #include "repl_instance.h" #include "util.h" #include "dbfilop.h" #include "gvcst_protos.h" #include "is_raw_dev.h" #include "gv_match.h" #include "do_semop.h" #include "gvcmy_open.h" #include "wcs_sleep.h" #include "do_shmat.h" #include "send_msg.h" #include "gtmmsg.h" #include "shmpool.h" #include "gtm_permissions.h" #include "wbox_test_init.h" #include "gtmcrypt.h" #include "have_crit.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif #include "db_snapshot.h" #include "lockconst.h" /* for LOCK_AVAILABLE */ #include "recover_truncate.h" #include "get_fs_block_size.h" #include "add_inter.h" #include "dpgbldir.h" #include "tp_change_reg.h" #include "mu_gv_cur_reg_init.h" #include "mlkdef.h" #define REQRUNDOWN_TEXT "semid is invalid but shmid is valid or at least one of sem_ctime or shm_ctime are non-zero" #define MAX_ACCESS_SEM_RETRIES 2 /* see comment below where this macro is used for why it needs to be 2 */ #define RTS_ERROR(...) rts_error_csa(CSA_ARG(csa) __VA_ARGS__) #define SEND_MSG(...) send_msg_csa(CSA_ARG(csa) __VA_ARGS__) #define SS_INFO_INIT(CSA) \ MBSTART { \ shm_snapshot_ptr_t ss_shm_ptr; \ node_local_ptr_t lcl_cnl; \ \ lcl_cnl = CSA->nl; \ lcl_cnl->ss_shmid = INVALID_SHMID; \ lcl_cnl->ss_shmcycle = 0; \ CLEAR_SNAPSHOTS_IN_PROG(lcl_cnl); \ lcl_cnl->num_snapshots_in_effect = 0; \ SET_LATCH_GLOBAL(&lcl_cnl->snapshot_crit_latch, LOCK_AVAILABLE); \ assert(1 == MAX_SNAPSHOTS); /* To ensure that we revisit this whenever multiple snapshots is implemented */ \ ss_shm_ptr = (shm_snapshot_ptr_t)(SS_GETSTARTPTR(CSA)); \ SS_DEFAULT_INIT_POOL(ss_shm_ptr); \ } MBEND /* If a current value doesn't match the saved value used in the original calculation revert to the original */ #define DSE_VERIFY_AND_RESTORE(CSA, TSD, VAR) \ MBSTART { \ assert(IS_DSE_IMAGE); \ if (CSA->nl->saved_##VAR != TSD->VAR) \ { \ gtm_putmsg_csa(CSA_ARG(CSA) VARLSTCNT(6) ERR_NLRESTORE, 4, \ LEN_AND_LIT(#VAR), TSD->VAR, CSA->nl->saved_##VAR); \ TSD->VAR = CSA->nl->saved_##VAR; \ } \ } MBEND #define GTM_ATTACH_CHECK_ERROR \ MBSTART { \ if (-1 == status_l) \ { \ RTS_ERROR(VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), \ ERR_TEXT, 2, LEN_AND_LIT("Error attaching to database shared memory"), errno); \ } \ } MBEND #define GTM_ATTACH_SHM \ MBSTART { \ status_l = (sm_long_t)(csa->db_addrs[0] = (sm_uc_ptr_t)do_shmat(udi->shmid, 0, SHM_RND)); \ GTM_ATTACH_CHECK_ERROR; \ csa->nl = (node_local_ptr_t)csa->db_addrs[0]; \ } MBEND /* These values may potentially be used by DSE to calculate various offsets into the shared memory layout. * Note that blk_size does not affect the offset calculation for MM. */ #define GTM_CACHE_INTO_SHM(CSA, TSD) \ MBSTART { \ CSA->nl->saved_acc_meth = TSD->acc_meth; \ CSA->nl->saved_lock_space_size = TSD->lock_space_size; \ CSA->nl->saved_blk_size = TSD->blk_size; \ CSA->nl->saved_jnl_buffer_size = TSD->jnl_buffer_size; \ } MBEND #define GTM_ATTACH_SHM_AND_CHECK_VERS(VERMISMATCH, SHM_SETUP_OK) \ MBSTART { \ GTM_ATTACH_SHM; \ /* The following checks for GDS_LABEL_GENERIC and gtm_release_name ensure that the shared memory under consideration \ * is valid. If shared memory is already initialized, do VERMISMATCH check BEFORE referencing any other fields in \ * shared memory. \ */ \ VERMISMATCH = FALSE; \ SHM_SETUP_OK = FALSE; \ if (!MEMCMP_LIT(csa->nl->label, GDS_LABEL_GENERIC)) \ { \ if (memcmp(csa->nl->now_running, gtm_release_name, gtm_release_name_len + 1)) \ { /* Copy csa->nl->now_running into a local variable before passing to rts_error due to the following \ * issue: \ * In VMS, a call to rts_error copies only the error message and its arguments (as pointers) and \ * transfers control to the topmost condition handler which is dbinit_ch() in this case. dbinit_ch() \ * does a PRN_ERROR only for SUCCESS/INFO (VERMISMATCH is neither of them) and in addition \ * nullifies csa->nl as part of its condition handling. It then transfers control to the next level \ * condition handler which does a PRN_ERROR but at that point in time, the parameter \ * csa->nl->now_running is no longer accessible and hence no \parameter substitution occurs (i.e. the \ * error message gets displayed with plain !ADs). \ * In UNIX, this is not an issue since the first call to rts_error does the error message \ * construction before handing control to the topmost condition handler. But it does not hurt to do \ * the copy. \ */ \ assert(strlen(csa->nl->now_running) < SIZEOF(now_running)); \ memcpy(now_running, csa->nl->now_running, SIZEOF(now_running)); \ now_running[SIZEOF(now_running) - 1] = '\0'; /* protection against bad csa->nl->now_running values */ \ VERMISMATCH = TRUE; \ } else \ SHM_SETUP_OK = TRUE; \ } \ } MBEND #define GTM_VERMISMATCH_ERROR \ MBSTART { \ if (!vermismatch_already_printed) \ { \ vermismatch_already_printed = TRUE; \ RTS_ERROR(VARLSTCNT(8) ERR_VERMISMATCH, 6, DB_LEN_STR(reg), gtm_release_name_len, gtm_release_name, \ LEN_AND_STR(now_running)); \ } \ } MBEND #define INIT_DB_ENCRYPTION_IF_NEEDED(DO_CRYPT_INIT, INIT_STATUS, REG, CSA, TSD, CRYPT_WARNING) \ MBSTART { \ int fn_len; \ char *fn; \ \ if (DO_CRYPT_INIT) \ { \ fn = (char *)(REG->dyn.addr->fname); \ fn_len = REG->dyn.addr->fname_len; \ if (0 == INIT_STATUS) \ INIT_DB_OR_JNL_ENCRYPTION(CSA, TSD, fn_len, fn, INIT_STATUS); \ } \ DO_ERR_PROC_ENCRYPTION_IF_NEEDED(REG, DO_CRYPT_INIT, INIT_STATUS, CRYPT_WARNING); \ } MBEND #define INIT_PROC_ENCRYPTION_IF_NEEDED(DO_CRYPT_INIT, INIT_STATUS) \ MBSTART { \ if (DO_CRYPT_INIT) \ INIT_PROC_ENCRYPTION(INIT_STATUS); \ } MBEND #define DO_ERR_PROC_ENCRYPTION_IF_NEEDED(REG, DO_CRYPT_INIT, INIT_STATUS, CRYPT_WARNING) \ MBSTART { \ int fn_len; \ char *fn; \ \ if (DO_CRYPT_INIT && (0 != INIT_STATUS) && !(CRYPT_WARNING)) \ { \ fn = (char *)(REG->dyn.addr->fname); \ fn_len = REG->dyn.addr->fname_len; \ if (IS_GTM_IMAGE || mu_reorg_encrypt_in_prog) \ { \ GTMCRYPT_REPORT_ERROR(INIT_STATUS, rts_error, fn_len, fn); \ } else \ { \ GTMCRYPT_REPORT_ERROR(MAKE_MSG_WARNING(INIT_STATUS), gtm_putmsg, fn_len, fn); \ CRYPT_WARNING = TRUE; \ } \ } \ } MBEND #define READ_DB_FILE_HEADER(REG, TSD, ERR_RET) \ MBSTART { \ file_control *fc; \ gd_segment *seg; \ mstr *gld_str; \ unix_db_info *udi; \ uint4 fsb_size; \ gd_region *baseDBreg; \ \ seg = REG->dyn.addr; \ fc = seg->file_cntl; \ fc->op = FC_READ; \ fc->op_buff = (sm_uc_ptr_t)TSD; \ fc->op_pos = 1; \ fc->op_len = SGMNT_HDR_LEN; \ dbfilop(fc); \ ERR_RET = 0; \ if (IS_AIO_DBGLDMISMATCH(seg, TSD)) \ { /* Copy tsd setting for asyncio into seg and retry "dbfilopn"/"db_init" \ * sequence in caller "gvcst_init" by returning with an error. \ */ \ COPY_AIO_SETTINGS(seg, TSD); /* copies from TSD to seg */ \ ERR_RET = ERR_DBGLDMISMATCH; \ } \ if (IS_RDBF_STATSDB(TSD) && !IS_STATSDB_REGNAME(REG)) \ { /* This is a case where the file header says it is a statsDB but the region \ * name is not lower case as it should be for a statsDB. Not supported. \ */ \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_STATSDBNOTSUPP, 2, DB_LEN_STR(REG)); \ } \ if (!IS_RDBF_STATSDB(TSD) && IS_STATSDB_REGNAME(REG)) \ { /* This is a case where the file header does NOT say it is a statsDB but the region \ * name is lower case as it should be for a statsDB. This is also an illegal \ * combination so give an appropriate error. \ */ \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_INVSTATSDB, 4, DB_LEN_STR(REG), \ REG_LEN_STR(REG)); \ } \ if (TSD->asyncio) \ { /* AIO = ON, implies we need to use O_DIRECT. \ * Check for db vs fs blksize alignment issues. \ */ \ udi = FC2UDI(fc); \ fsb_size = get_fs_block_size(udi->fd); \ if (0 != (TSD->blk_size % fsb_size)) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_DBBLKSIZEALIGN, 4, \ DB_LEN_STR(REG), TSD->blk_size, fsb_size); \ } \ } MBEND #define READ_DB_FILE_MASTERMAP(REG, CSD) \ MBSTART { \ file_control *fc; \ \ fc = REG->dyn.addr->file_cntl; \ fc->op = FC_READ; \ fc->op_buff = MM_ADDR(CSD); \ fc->op_len = MASTER_MAP_SIZE(CSD); \ fc->op_pos = MM_BLOCK; \ dbfilop(fc); \ } MBEND /* Depending on whether journaling and/or replication was enabled at the time of the crash, * print REQRUNDOWN, REQRECOV, or REQROLLBACK error message. */ #define PRINT_CRASH_MESSAGE(CNT, ARG, ...) \ MBSTART { \ if (JNL_ENABLED(tsd)) \ { \ if (REPL_ENABLED(tsd) && tsd->jnl_before_image) \ RTS_ERROR(VARLSTCNT(10 + CNT) ERR_REQROLLBACK, 4, DB_LEN_STR(reg), \ LEN_AND_STR((ARG)->machine_name), __VA_ARGS__); \ else \ RTS_ERROR(VARLSTCNT(10 + CNT) ERR_REQRECOV, 4, DB_LEN_STR(reg), \ LEN_AND_STR((ARG)->machine_name), __VA_ARGS__); \ } else \ RTS_ERROR(VARLSTCNT(10 + CNT) ERR_REQRUNDOWN, 4, DB_LEN_STR(reg), \ LEN_AND_STR((ARG)->machine_name), __VA_ARGS__); \ } MBEND #define START_SEM_TIMERS(SEM_STACKTRACE_TIME, SEM_TIMEDOUT, MAX_HRTBT_DELTA) \ MBSTART { \ TIMEOUT_INIT(SEM_TIMEDOUT, MAX_HRTBT_DELTA * MILLISECS_IN_SEC); \ TIMEOUT_INIT(SEM_STACKTRACE_TIME, MAX_HRTBT_DELTA * MILLISECS_IN_SEC / 2); \ } MBEND #define CANCEL_SEM_TIMERS(SEM_STACKTRACE_TIME, SEM_TIMEDOUT) \ MBSTART { \ TIMEOUT_DONE(SEM_STACKTRACE_TIME); \ TIMEOUT_DONE(SEM_TIMEDOUT); \ } MBEND #define RETURN_IF_BYPASSED(BYPASSED_FTOK, INDEFINITE_WAIT, SEM_STACKTRACE_TIME, SEM_TIMEDOUT) \ RETURN_IF_ERROR(BYPASSED_FTOK, -1, INDEFINITE_WAIT, SEM_STACKTRACE_TIME, SEM_TIMEDOUT) #define RETURN_IF_ERROR(COND, RETVAL, INDEFINITE_WAIT, SEM_STACKTRACE_TIME, SEM_TIMEDOUT) \ MBSTART { \ if (COND) \ { \ if (!INDEFINITE_WAIT) \ CANCEL_SEM_TIMERS(SEM_STACKTRACE_TIME, SEM_TIMEDOUT); \ REVERT; \ return RETVAL; /* "gvcst_init" does cleanup and retries the "db_init" call */ \ } \ } MBEND #define MU_GV_CUR_REG_FREE(TMP_REG, SAVE_REG) \ { \ GBLREF gd_region *gv_cur_region; \ \ SAVE_REG = gv_cur_region; \ gv_cur_region = TMP_REG; \ mu_gv_cur_reg_free(); \ gv_cur_region = SAVE_REG; \ } GBLREF boolean_t is_src_server; GBLREF boolean_t mupip_jnl_recover; GBLREF gd_region *gv_cur_region, *db_init_region; GBLREF ipcs_mesg db_ipcs; GBLREF gd_addr *gd_header; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnlpool_addrs_ptr_t jnlpool_head; GBLREF node_local_ptr_t locknl; GBLREF uint4 mutex_per_process_init_pid; GBLREF uint4 process_id; GBLREF jnl_gbls_t jgbl; GBLREF uint4 mu_reorg_encrypt_in_prog; GBLREF int pool_init; GBLREF boolean_t jnlpool_init_needed; GBLREF mstr extnam_str; GBLREF mval dollar_zgbldir; #ifndef MUTEX_MSEM_WAKE GBLREF int mutex_sock_fd; #endif LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; OS_PAGE_SIZE_DECLARE error_def(ERR_DBFILNOFULLWRT); error_def(ERR_BADDBVER); error_def(ERR_CRITSEMFAIL); error_def(ERR_DBBLKSIZEALIGN); error_def(ERR_DBCREINCOMP); error_def(ERR_DBFILERDONLY); error_def(ERR_DBFILERR); error_def(ERR_DBFLCORRP); error_def(ERR_DBGLDMISMATCH); error_def(ERR_DBIDMISMATCH); error_def(ERR_DBNAMEMISMATCH); error_def(ERR_DBNOTGDS); error_def(ERR_DBSHMNAMEDIFF); error_def(ERR_FILENOTFND); error_def(ERR_HOSTCONFLICT); error_def(ERR_INVOPNSTATSDB); error_def(ERR_INVSTATSDB); error_def(ERR_JNLBUFFREGUPD); error_def(ERR_MMNODYNUPGRD); error_def(ERR_NLMISMATCHCALC); error_def(ERR_NLRESTORE); error_def(ERR_REQRECOV); error_def(ERR_REQROLLBACK); error_def(ERR_REQRUNDOWN); error_def(ERR_STATSDBNOTSUPP); error_def(ERR_SYSCALL); error_def(ERR_TEXT); error_def(ERR_VERMISMATCH); gd_region *dbfilopn(gd_region *reg) { unix_db_info *tmp_udi, *udi; parse_blk pblk; mstr file; char *fnptr, fbuff[MAX_FN_LEN + 1], tmpbuff[MAX_FN_LEN + 1]; struct stat buf; gd_region *prev_reg, *save_gv_cur_region, *tmp_reg; gd_segment *seg; int status; boolean_t raw; boolean_t open_read_only; int stat_res, rc, save_errno; sgmnt_addrs *csa; sgmnt_data tsdbuff; sgmnt_data_ptr_t tsd; file_control *fc; unsigned char cstatus; boolean_t ftok_counter_halted; ZOS_ONLY(int realfiletag;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; db_init_region = reg; seg = reg->dyn.addr; assert(IS_ACC_METH_BG_OR_MM(seg->acc_meth)); FILE_CNTL_INIT_IF_NULL(seg); udi = FILE_INFO(reg); csa = &udi->s_addrs; file.addr = (char *)seg->fname; file.len = seg->fname_len; memset(&pblk, 0, SIZEOF(pblk)); pblk.buffer = fbuff; pblk.buff_size = MAX_FN_LEN; pblk.fop = (F_SYNTAXO | F_PARNODE); memcpy(fbuff, file.addr, file.len); *(fbuff + file.len) = '\0'; if (is_raw_dev(fbuff)) { raw = TRUE; pblk.def1_buf = DEF_NODBEXT; pblk.def1_size = SIZEOF(DEF_NODBEXT) - 1; } else { raw = FALSE; pblk.def1_buf = DEF_DBEXT; pblk.def1_size = SIZEOF(DEF_DBEXT) - 1; } status = parse_file(&file, &pblk); /* When opening a reservedDB type database file, it is possible that it does not (yet) exist. In that case, * make a call out to create it. */ if (!(status & 1)) { if (!IS_GTCM_GNP_SERVER_IMAGE) { free(seg->file_cntl->file_info); free(seg->file_cntl); seg->file_cntl = NULL; } RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), status); } assert(((int)pblk.b_esl + 1) <= SIZEOF(seg->fname)); memcpy(seg->fname, pblk.buffer, pblk.b_esl); pblk.buffer[pblk.b_esl] = 0; seg->fname[pblk.b_esl] = 0; seg->fname_len = pblk.b_esl; if (pblk.fnb & F_HAS_NODE) { /* Remote node specification given */ assert(pblk.b_node && pblk.l_node[pblk.b_node - 1] == ':'); gvcmy_open(reg, &pblk); return (gd_region *)-1L; } fnptr = (char *)seg->fname + pblk.b_node; udi->raw = raw; udi->fn = (char *)fnptr; /* If MUPIP JOURNAL -EXTRACT, then open db in read-only mode or else we could incorrectly reset crash field * in journal file header as part of gds_rundown because we have the db open in read_write mode (GTM-8483). */ open_read_only = (jgbl.mur_extract && !jgbl.mur_update); for ( ; ; ) { if (!open_read_only) { #ifdef DEBUG if (WBTEST_ENABLED(WBTEST_OPENFILE_DB)) { udi->fd = FD_INVALID; errno = gtm_white_box_test_case_count ? gtm_white_box_test_case_count : EPERM; } else #endif OPENFILE_DB(fnptr, O_RDWR, udi, seg); save_errno = errno; /* When opening an auto-create type of reservedDB database file, it is possible * that it does not (yet) exist. In that case, make a call out to create it. Note that * a statsdb (which is also an autodb) gets created in "gvcst_init" so skip that here. */ if ((FD_INVALID == udi->fd) && (ENOENT == errno) && IS_AUTODB_REG(reg) && !IS_STATSDB_REG(reg)) { /* We want to get a lock to ensure only one process does the create. But the lock needs to be * based on an existing file ("ftok_sem_get" relies on that). So use "/" directory. That is * guaranteed to exist on any system and will serve as a sync point for ANY/ALL autodb creates. */ /* First check if we have enough space to do ".new" suffix addition (done a little later) */ if (ARRAYSIZE(seg->fname) <= seg->fname_len + STR_LIT_LEN(EXT_NEW)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_DBFILERR, 2, DB_LEN_STR(reg)); save_gv_cur_region = gv_cur_region; mu_gv_cur_reg_init(); tmp_reg = gv_cur_region; gv_cur_region = save_gv_cur_region; tmp_reg->dyn.addr->fname[0] = '/'; tmp_reg->dyn.addr->fname[1] = '\0'; tmp_reg->dyn.addr->fname_len = 1; tmp_udi = FILE_INFO(tmp_reg); tmp_udi->fn = (char *)tmp_reg->dyn.addr->fname; if (!ftok_sem_get(tmp_reg, FALSE, GTM_ID, FALSE, &ftok_counter_halted)) { MU_GV_CUR_REG_FREE(tmp_reg, save_gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_DBFILERR, 2, DB_LEN_STR(reg)); } assert(!ftok_counter_halted); /* since we specified FALSE as the 2nd parameter ("incr_cnt") */ /* Now that we got the ftok lock, check if the file exists. If so skip the create. */ OPENFILE_DB(fnptr, O_RDWR, udi, seg); if ((FD_INVALID == udi->fd) && (ENOENT == errno)) { /* File still does not exist. Create it. */ /* If autodb file name is say "tmp.dat", point region to a temporary filename * "tmp.dat_%YGTM" (suffix EXT_NEW). This is similar to the logic used in "cre_jnl_file" * with ".mjl" and ".mjl_%YGTM" files. Create database "tmp.dat_%YGTM" and initialize it. * And finally rename it to "tmp.dat". If we instead create "tmp.dat" and then fill it * with GT.M db file header content, it is possible while this is happening, other * processes see the file exist (OPENFILE_DB returns TRUE above) and proceed assuming the * file is a valid GT.M database file when actually the creation is not yet done. */ MEMCPY_LIT(seg->fname + seg->fname_len, EXT_NEW); seg->fname_len += STR_LIT_LEN(EXT_NEW); seg->fname[seg->fname_len] = '\0'; cstatus = gvcst_cre_autoDB(reg); if (EXIT_NRM != cstatus) { ftok_sem_release(tmp_reg, FALSE, FALSE); MU_GV_CUR_REG_FREE(tmp_reg, save_gv_cur_region); save_errno = TREF(mu_cre_file_openrc); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_DBFILERR, 2, DB_LEN_STR(reg)); } memcpy(tmpbuff, seg->fname, seg->fname_len + 1); /* + 1 to include terminating '\0' */ seg->fname_len -= STR_LIT_LEN(EXT_NEW); seg->fname[seg->fname_len] = '\0'; /* Rename "tmp.dat_%YGTM" to "tmp.dat" */ if (-1 == RENAME(tmpbuff, (char *)seg->fname)) { save_errno = errno; ftok_sem_release(tmp_reg, FALSE, FALSE); MU_GV_CUR_REG_FREE(tmp_reg, save_gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), save_errno); } } ftok_sem_release(tmp_reg, FALSE, FALSE); MU_GV_CUR_REG_FREE(tmp_reg, save_gv_cur_region); /* DB file exists now - retry the open */ OPENFILE_DB(fnptr, O_RDWR, udi, seg); } if (!udi->grabbed_access_sem) { /* If the process already has standalone access, these fields are initialized in mu_rndwn_file */ udi->ftok_semid = INVALID_SEMID; udi->semid = INVALID_SEMID; udi->shmid = INVALID_SHMID; udi->gt_sem_ctime = 0; udi->gt_shm_ctime = 0; } reg->read_only = FALSE; /* maintain csa->read_write simultaneously */ csa->read_write = TRUE; /* maintain reg->read_only simultaneously */ csa->orig_read_write = TRUE; } if (open_read_only || (FD_INVALID == udi->fd)) { OPENFILE_DB(fnptr, O_RDONLY, udi, seg); if (FD_INVALID == udi->fd) { save_errno = errno; if (!IS_GTCM_GNP_SERVER_IMAGE) { free(seg->file_cntl->file_info); free(seg->file_cntl); seg->file_cntl = NULL; } RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), save_errno); } reg->read_only = TRUE; /* maintain csa->read_write simultaneously */ csa->read_write = FALSE; /* maintain reg->read_only simultaneously */ csa->orig_read_write = FALSE; if (!open_read_only && !((EPERM == save_errno) || (EACCES == save_errno))) { if (!IS_GTM_IMAGE) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_DBFILERDONLY, 3, DB_LEN_STR(reg), (int)0, save_errno); send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_DBFILERDONLY, 3, DB_LEN_STR(reg), (int)0, save_errno); } } if (!reg->owning_gd->is_dummy_gbldir && (pool_init || !jnlpool_init_needed || !CUSTOM_ERRORS_AVAILABLE)) break; /* Caller created a dummy region (not a region from a gld). So determine asyncio & acc_meth setting * from db file header and copy that to segment to avoid DBGLDMISMATCH error later in "db_init". * Or need to check replication state to decide if an early call to jnlpool_init is needed in gvcst_init */ tsd = udi->fd_opened_with_o_direct ? (sgmnt_data_ptr_t)(TREF(dio_buff)).aligned : &tsdbuff; /* If O_DIRECT, use aligned buffer */ fc = seg->file_cntl; fc->op = FC_READ; fc->op_buff = (sm_uc_ptr_t)tsd; fc->op_pos = 1; fc->op_len = SGMNT_HDR_LEN; dbfilop(fc); if (!pool_init && jnlpool_init_needed && CUSTOM_ERRORS_AVAILABLE) csa->repl_state = tsd->repl_state; /* needed in gvcst_init */ if (!reg->owning_gd->is_dummy_gbldir) break; if (!IS_AIO_DBGLDMISMATCH(seg, tsd)) break; CLOSEFILE_RESET(udi->fd, rc); /* close file and reopen it with correct asyncio setting */ COPY_AIO_SETTINGS(seg, tsd); /* copies from tsd to seg */ } # ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(udi->fd, TAG_BINARY, &realfiletag)) TAG_POLICY_SEND_MSG(fnptr, errno, realfiletag, TAG_BINARY); # endif /* Now that the file has been opened, use FSTAT on the fd instead of STAT on the file name. The latter is risky for AUTODB * since it could be concurrently deleted whereas the fd would still be accessible (delete-pending). */ FSTAT_FILE(udi->fd, &buf, stat_res); if (-1 == stat_res) { save_errno = errno; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), save_errno); } set_gdid_from_stat(&udi->fileid, &buf); if (prev_reg = gv_match(reg)) { CLOSEFILE_RESET(udi->fd, rc); /* resets "udi->fd" to FD_INVALID */ free(seg->file_cntl->file_info); free(seg->file_cntl); seg->file_cntl = NULL; return prev_reg; } SYNC_OWNING_GD(reg); return reg; } void dbsecspc(gd_region *reg, sgmnt_data_ptr_t csd, gtm_uint64_t *sec_size) { gtm_uint64_t tmp_sec_size; /* Ensure that all the various sections that the shared memory contains are actually * aligned at the OS_PAGE_SIZE boundary */ INIT_NUM_CRIT_ENTRY_IF_NEEDED(csd); assert(MIN_NODE_LOCAL_SPACE <= NODE_LOCAL_SPACE(csd)); assert(0 == NODE_LOCAL_SPACE(csd) % OS_PAGE_SIZE); assert(0 == LOCK_SPACE_SIZE(csd) % OS_PAGE_SIZE); assert(0 == JNL_SHARE_SIZE(csd) % OS_PAGE_SIZE); assert(0 == SHMPOOL_SECTION_SIZE % OS_PAGE_SIZE); assert(0 == CACHE_CONTROL_SIZE(csd) % OS_PAGE_SIZE); /* First compute the size based on sections common to both MM and BG */ csd->free_space = (BLK_ZERO_OFF(csd->start_vbn) - SIZEOF_FILE_HDR(csd)); tmp_sec_size = NODE_LOCAL_SPACE(csd) + JNL_SHARE_SIZE(csd) + LOCK_SPACE_SIZE(csd) + SHMPOOL_SECTION_SIZE + SIZEOF_FILE_HDR(csd); /* Now, add sections specific to MM and BG */ if (dba_bg == reg->dyn.addr->acc_meth) tmp_sec_size += CACHE_CONTROL_SIZE(csd) + (DIVIDE_ROUND_UP(BT_SIZE(csd), OS_PAGE_SIZE) * OS_PAGE_SIZE); ADJUST_SHM_SIZE_FOR_HUGEPAGES(tmp_sec_size, *sec_size); /* *sec_size is adjusted size */ return; } int db_init(gd_region *reg, boolean_t ok_to_bypass) { boolean_t is_bg, read_only, need_stacktrace, have_standalone_access; boolean_t shm_setup_ok = FALSE, vermismatch = FALSE, vermismatch_already_printed = FALSE; boolean_t new_shm_ipc, replinst_mismatch, need_shmctl, need_semctl; boolean_t gld_do_crypt_init, db_do_crypt_init, semop_success, statsdb_off; char machine_name[MAX_MCNAMELEN]; int gethostname_res, stat_res, user_id, group_id, perm, save_udi_semid; int4 status, dblksize, save_errno, wait_time, loopcnt, sem_pid; sm_long_t status_l; sgmnt_addrs *csa; node_local_ptr_t cnl; sgmnt_data tsdbuff; sgmnt_data_ptr_t csd, tsd; jnlpool_addrs_ptr_t save_jnlpool, local_jnlpool; replpool_identifier replpool_id; unsigned int full_len; /* for REPL_INST_AVAILABLE */ gd_id replfile_gdid, *tmp_gdid; boolean_t need_jnlpool_setup; struct sembuf sop[3]; struct stat stat_buf; union semun semarg; struct semid_ds semstat; struct shmid_ds shmstat; struct statvfs dbvfs; uint4 fbwsize, sopcnt; uint4 max_hrtbt_delta; boolean_t sem_timedout, *sem_timedoutp = NULL, sem_stacktrace_time, *sem_stacktrace_timep = NULL, indefinite_wait = TRUE; unix_db_info *udi; char now_running[MAX_REL_NAME]; int err_ret, init_status; gtm_uint64_t sec_size, mmap_sz; semwait_status_t retstat; struct perm_diag_data pdd; boolean_t bypassed_ftok = FALSE, bypassed_access = FALSE, dummy_ftok_counter_halted, ftok_counter_halted, access_counter_halted, incr_cnt; int jnl_buffer_size; char s[JNLBUFFUPDAPNDX_SIZE]; /* JNLBUFFUPDAPNDX_SIZE is defined in jnl.h */ char *syscall; void *mmapaddr; int ret, secshrstat; boolean_t crypt_warning; gd_region *baseDBreg; sgmnt_addrs *baseDBcsa; node_local_ptr_t baseDBnl; int slp_cnt = 0; boolean_t is_logged = FALSE; # ifdef DEBUG int i; char *ptr; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ESTABLISH_NOUNWIND(dbinit_ch); assert(INTRPT_IN_GVCST_INIT == intrpt_ok_state); /* we better be called from gvcst_init */ read_only = reg->read_only; udi = FILE_INFO(reg); tsd = udi->fd_opened_with_o_direct ? (sgmnt_data_ptr_t)(TREF(dio_buff)).aligned : &tsdbuff; /* If O_DIRECT, use aligned buffer */ memset(machine_name, 0, SIZEOF(machine_name)); csa = &udi->s_addrs; assert(!mutex_per_process_init_pid || mutex_per_process_init_pid == process_id); if (!mutex_per_process_init_pid) mutex_per_process_init(); if (GETHOSTNAME(machine_name, MAX_MCNAMELEN, gethostname_res)) RTS_ERROR(VARLSTCNT(5) ERR_TEXT, 2, LEN_AND_LIT("Unable to get the hostname"), errno); if (WBTEST_ENABLED(WBTEST_TAMPER_HOSTNAME)) STRCPY(machine_name, "s_i_l_l_y"); assert(strlen(machine_name) < MAX_MCNAMELEN); assert(NULL == csa->hdr); /* dbinit_ch relies on this to unmap the db (if mm) */ assert((NULL == csa->db_addrs[0]) && (NULL == csa->db_addrs[1])); assert((NULL == csa->mlkctl) && (0 == csa->mlkctl_len)); reg->opening = TRUE; assert(0 <= udi->fd); /* database file must have been already opened by "dbfilopn" done from "gvcst_init" */ FSTAT_FILE(udi->fd, &stat_buf, stat_res); /* get the stats for the database file */ if (-1 == stat_res) RTS_ERROR(VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), errno); /* Setup new group and permissions if indicated by the security rules. */ if (!gtm_permissions(&stat_buf, &user_id, &group_id, &perm, PERM_IPC, &pdd)) { SEND_MSG(VARLSTCNT(6 + PERMGENDIAG_ARG_COUNT) ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("ipc resources"), RTS_ERROR_STRING(udi->fn), PERMGENDIAG_ARGS(pdd)); RTS_ERROR(VARLSTCNT(6 + PERMGENDIAG_ARG_COUNT) ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("ipc resources"), RTS_ERROR_STRING(udi->fn), PERMGENDIAG_ARGS(pdd)); } /* if the process has standalone access, it will have udi->grabbed_access_sem set to TRUE at * this point. Note that down in a local variable as the udi->grabbed_access_sem will be set * to TRUE even for non-standalone access below and hence we can't rely on that later to determine if the process had * standalone access or not when it entered this function. */ have_standalone_access = udi->grabbed_access_sem; init_status = 0; crypt_warning = TREF(mu_set_file_noencryptable); udi->shm_deleted = udi->sem_deleted = FALSE; if (!have_standalone_access) { gld_do_crypt_init = (IS_ENCRYPTED(reg->dyn.addr->is_encrypted) && !IS_LKE_IMAGE); assert(!TO_BE_ENCRYPTED(reg->dyn.addr->is_encrypted)); INIT_PROC_ENCRYPTION_IF_NEEDED(gld_do_crypt_init, init_status); /* heavyweight so do it before ftok */ max_hrtbt_delta = TREF(dbinit_max_delta_secs); indefinite_wait = (INDEFINITE_WAIT_ON_EAGAIN == max_hrtbt_delta); if (!indefinite_wait) { START_SEM_TIMERS(sem_stacktrace_time, sem_timedout, max_hrtbt_delta); sem_timedoutp = &sem_timedout; sem_stacktrace_timep = &sem_stacktrace_time; } /* If the header is uninitalized we assume that mumps_can_bypass is TRUE. After we read the header to confirm our * assumption. If we turn out to be wrong, we error out. */ bypassed_ftok = ok_to_bypass; if (!ftok_sem_get2(reg, sem_stacktrace_timep, sem_timedoutp, &retstat, &bypassed_ftok, &ftok_counter_halted, TRUE)) ISSUE_SEMWAIT_ERROR((&retstat), reg, udi, "ftok"); assert(ok_to_bypass || !bypassed_ftok); assert(udi->grabbed_ftok_sem || bypassed_ftok); if (bypassed_ftok) SEND_MSG(VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_LIT("FTOK bypassed at database initialization")); /* At this point we have ftok_semid semaphore based on ftok key (exception is if "bypassed_ftok" is TRUE). * Any ftok conflicted region will block at this point. For example, if a.dat and b.dat both have same ftok * and process A tries to open or close a.dat and process B tries to open or close b.dat, even though the * database accesses don't conflict, the first one to control the ftok semaphore blocks (makes wait) the other(s). */ READ_DB_FILE_HEADER(reg, tsd, err_ret); /* file already opened by "dbfilopn" done from "gvcst_init" */ RETURN_IF_ERROR(err_ret, err_ret, indefinite_wait, sem_stacktrace_time, sem_timedout); DO_BADDBVER_CHK(reg, tsd); /* need to do BADDBVER check before de-referencing shmid and semid from file header * as they could be at different offsets if the database is V4-format */ db_do_crypt_init = (USES_ENCRYPTION(tsd->is_encrypted) && !IS_LKE_IMAGE); if (gld_do_crypt_init != db_do_crypt_init) { /* Encryption setting different between global directory and database file header */ if (db_do_crypt_init) { /* Encryption is turned on in the file header. Need to do encryption initialization. Release ftok * as initialization is heavy-weight. Decrement counter so later increment is correct. */ assert(udi->counter_ftok_incremented == !ftok_counter_halted); /* If we are going to do a "ftok_sem_release", do not decrement the counter that was * potentially incremented in the previous call to "ftok_sem_get2". Hence the 2nd parameter FALSE. */ if (!bypassed_ftok && !ftok_sem_release(reg, FALSE, FALSE)) RTS_ERROR(VARLSTCNT(4) ERR_DBFILERR, 2, DB_LEN_STR(reg)); INIT_PROC_ENCRYPTION_IF_NEEDED(db_do_crypt_init, init_status); /* redo initialization */ bypassed_ftok = ok_to_bypass; if (!indefinite_wait) { /* restart timer to reflect time lost in encryption initialization */ CANCEL_SEM_TIMERS(sem_stacktrace_time, sem_timedout); START_SEM_TIMERS(sem_stacktrace_time, sem_timedout, max_hrtbt_delta); } /* Since this is the second call to "ftok_sem_get2", do not increment counter. * Hence FALSE as last parameter. */ if (!ftok_sem_get2(reg, sem_stacktrace_timep, sem_timedoutp, &retstat, &bypassed_ftok, &dummy_ftok_counter_halted, FALSE)) ISSUE_SEMWAIT_ERROR((&retstat), reg, udi, "ftok"); assert(ok_to_bypass || !bypassed_ftok); assert(!udi->counter_ftok_incremented); udi->counter_ftok_incremented = !ftok_counter_halted; /* restore to first "ftok_sem_get2" call */ assert(udi->grabbed_ftok_sem || bypassed_ftok); if (bypassed_ftok) SEND_MSG(VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_LIT("bypassed at database encryption initialization")); /* Re-read now possibly stale file header and redo the above based on the new header info */ READ_DB_FILE_HEADER(reg, tsd, err_ret); RETURN_IF_ERROR(err_ret, err_ret, indefinite_wait, sem_stacktrace_time, sem_timedout); DO_BADDBVER_CHK(reg, tsd); /* need to do BADDBVER check before de-referencing shmid and semid from * file header as they could be at different offsets if the database is * V4-format */ db_do_crypt_init = (USES_ENCRYPTION(tsd->is_encrypted) && !IS_LKE_IMAGE); } /* else encryption is turned off in the file header. Continue as-is. Any encryption initialization done * before is discarded */ } if (mu_reorg_encrypt_in_prog) INIT_DB_ENCRYPTION_IF_NEEDED(db_do_crypt_init, init_status, reg, csa, tsd, crypt_warning); else DO_ERR_PROC_ENCRYPTION_IF_NEEDED(reg, db_do_crypt_init, init_status, crypt_warning); if (WBTEST_ENABLED(WBTEST_HOLD_ONTO_FTOKSEM_IN_DBINIT)) { DBGFPF((stderr, "Holding the ftok semaphore.. Sleeping for 30 seconds\n")); LONG_SLEEP(30); DBGFPF((stderr, "30 second sleep exhausted.. continuing with rest of db_init..\n")); } access_counter_halted = FALSE; /* This loop is primarily to take care of the case where udi->semid is non-zero in the file header and did exist * when we read the file header but was concurrently deleted by a process holding standalone access on the database * file (that does not hold the ftok semaphore) e.g. "db_ipcs_reset". We do not expect the semaphore to be deleted * more than once because for another delete to happen, the deleting process must first open the database and * for that it needs to go through "mu_rndwn_file"/"db_init" which needs the ftok lock that we currently hold. * Note that it is possible we don't hold the ftok lock (e.g. DSE/LKE) but in that case, any errors cause us * to retry in the caller ("gvcst_init") where the final retry is always with holding the ftok lock. */ for (loopcnt = 0; MAX_ACCESS_SEM_RETRIES > loopcnt; loopcnt++) { CSD2UDI(tsd, udi); /* sets udi->semid/shmid/sem_ctime/shm_ctime from file header */ /* We did not create a new ipc resource */ udi->sem_created = udi->shm_created = FALSE; /* In the below code, use RETURN_IF_BYPASSED macro to retry the "db_init" (with "ok_to_bypass" == FALSE) * if ever we are about to issue an error or create a sem/shm, all of which should happen only when * someone has the ftok lock (i.e. have not bypassed that part above). */ if (INVALID_SEMID == udi->semid) { /* Access control semaphore does not exist. Create one. But if we have bypassed the ftok lock, * then return (sem/shm can only be created by someone with the ftok lock). */ RETURN_IF_BYPASSED(bypassed_ftok, indefinite_wait, sem_stacktrace_time, sem_timedout); if (!bypassed_ftok) INIT_DB_ENCRYPTION_IF_NEEDED(db_do_crypt_init, init_status, reg, csa, tsd, crypt_warning); if (0 != udi->gt_sem_ctime || INVALID_SHMID != udi->shmid || 0 != udi->gt_shm_ctime) { /* We must have somthing wrong in protocol or, code, if this happens. */ assert(FALSE); PRINT_CRASH_MESSAGE(0, tsd, ERR_TEXT, 2, LEN_AND_STR(REQRUNDOWN_TEXT)); } /* Create new semaphore using IPC_PRIVATE. System guarantees a unique id. */ if (-1 == (udi->semid = semget(IPC_PRIVATE, FTOK_SEM_PER_ID, RWDALL | IPC_CREAT))) { udi->semid = INVALID_SEMID; RTS_ERROR(VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("Error with database control semget"), errno); } udi->shmid = INVALID_SHMID; /* reset shmid so dbinit_ch does not get confused in case we go there */ udi->sem_created = TRUE; udi->shm_created = !tsd->read_only; /* change group and permissions */ semarg.buf = &semstat; if (-1 == semctl(udi->semid, FTOK_SEM_PER_ID - 1, IPC_STAT, semarg)) RTS_ERROR(VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("Error with database control semctl IPC_STAT1"), errno); if ((INVALID_UID != user_id) && (user_id != semstat.sem_perm.uid)) { semstat.sem_perm.uid = user_id; need_semctl = TRUE; } if ((INVALID_GID != group_id) && (group_id != semstat.sem_perm.gid)) { semstat.sem_perm.gid = group_id; need_semctl = TRUE; } if (semstat.sem_perm.mode != perm) { semstat.sem_perm.mode = perm; need_semctl = TRUE; } if (need_semctl && (-1 == semctl(udi->semid, FTOK_SEM_PER_ID - 1, IPC_SET, semarg))) RTS_ERROR(VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("Error with database control semctl IPC_SET"), errno); SET_GTM_ID_SEM(udi->semid, status); if (-1 == status) RTS_ERROR(VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("Error with database control semctl SETVAL"), errno); /* WARNING: Because SETVAL changes sem_ctime, we must NOT do any SETVAL after this one; code here * and elsewhere uses IPC_STAT to get sem_ctime and relies on sem_ctime as the creation time of the * semaphore. */ semarg.buf = &semstat; if (-1 == semctl(udi->semid, FTOK_SEM_PER_ID - 1, IPC_STAT, semarg)) RTS_ERROR(VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("Error with database control semctl IPC_STAT2"), errno); tsd->gt_sem_ctime.ctime = udi->gt_sem_ctime = semarg.buf->sem_ctime; } else { /* "semid" already exists. Need to lock it. Before that do sanity check on "semid" and "shmid" */ if (INVALID_SHMID != udi->shmid) { if (WBTEST_ENABLED(WBTEST_HOLD_FTOK_UNTIL_BYPASS)) { if ((4 * DB_COUNTER_SEM_INCR) == semctl(udi->ftok_semid, DB_COUNTER_SEM, GETVAL)) { /* We are bypasser */ DBGFPF((stderr, "Waiting for all processes to quit.\n")); while (1 < semctl(udi->ftok_semid, DB_COUNTER_SEM, GETVAL)) LONG_SLEEP(1); } } if (-1 == shmctl(udi->shmid, IPC_STAT, &shmstat)) { RETURN_IF_BYPASSED(bypassed_ftok, indefinite_wait, sem_stacktrace_time, \ sem_timedout); if (((MAX_ACCESS_SEM_RETRIES - 1) == loopcnt) || !(SHM_REMOVED(errno))) PRINT_CRASH_MESSAGE(1, tsd, ERR_TEXT, 2, LEN_AND_LIT("Error with database control shmctl"), errno); /* else */ READ_DB_FILE_HEADER(reg, tsd, err_ret); RETURN_IF_ERROR(err_ret, err_ret, indefinite_wait, sem_stacktrace_time, sem_timedout); continue; } else if (shmstat.shm_ctime != tsd->gt_shm_ctime.ctime) { RETURN_IF_BYPASSED(bypassed_ftok, indefinite_wait, sem_stacktrace_time, \ sem_timedout); GTM_ATTACH_SHM_AND_CHECK_VERS(vermismatch, shm_setup_ok); if (vermismatch) { GTM_VERMISMATCH_ERROR; } else { PRINT_CRASH_MESSAGE(0, tsd, ERR_TEXT, 2, LEN_AND_LIT("IPC creation time indicates a probable prior crash")); } } semarg.buf = &semstat; if (-1 == semctl(udi->semid, DB_CONTROL_SEM, IPC_STAT, semarg)) { /* file header has valid semid but semaphore does not exist */ RETURN_IF_BYPASSED(bypassed_ftok, indefinite_wait, sem_stacktrace_time, \ sem_timedout); PRINT_CRASH_MESSAGE(1, tsd, ERR_TEXT, 2, LEN_AND_LIT("Error with database control semaphore (IPC_STAT)"), errno); } else if (semarg.buf->sem_ctime != tsd->gt_sem_ctime.ctime) { RETURN_IF_BYPASSED(bypassed_ftok, indefinite_wait, sem_stacktrace_time, \ sem_timedout); GTM_ATTACH_SHM_AND_CHECK_VERS(vermismatch, shm_setup_ok); if (vermismatch) { GTM_VERMISMATCH_ERROR; } else { PRINT_CRASH_MESSAGE(0, tsd, ERR_TEXT, 2, LEN_AND_LIT("IPC creation time indicates a probable prior crash")); } } } else { /* else "shmid" is NOT valid. This is possible if - * (a) Another process is holding the access control semaphore for a longer duration of time * but does NOT have the shared memory setup (MUPIP INTEG -FILE or MUPIP RESTORE). * * (b) If a process (like in (a)) were kill -15ed or -9ed and hence did not get a chance to * do db_ipcs_reset which resets "semid"/"shmid" field in the file header to INVALID. * * In either case, try grabbing the semaphore. If not, wait (depending on the user specified * wait time). Eventually, we will either get hold of the semaphore OR will error out. */ RETURN_IF_BYPASSED(bypassed_ftok, indefinite_wait, sem_stacktrace_time, sem_timedout); udi->shm_created = !tsd->read_only; /* Need to create shared memory */ } } incr_cnt = !read_only; /* We already have ftok semaphore of this region, so all we need is the access control semaphore */ SET_GTM_SOP_ARRAY(sop, sopcnt, incr_cnt, (SEM_UNDO | IPC_NOWAIT)); SEMOP(udi->semid, sop, sopcnt, status, NO_WAIT); if (-1 != status) break; save_errno = errno; assert(!udi->sem_created); /* If we created the semaphore, we should be able to do the semop */ if ((EAGAIN == save_errno) || (ERANGE == save_errno)) { if ((EAGAIN == save_errno) && (NO_SEMWAIT_ON_EAGAIN == TREF(dbinit_max_delta_secs))) { sem_pid = semctl(udi->semid, DB_CONTROL_SEM, GETPID); if (-1 != sem_pid) { RETURN_IF_BYPASSED(bypassed_ftok, indefinite_wait, sem_stacktrace_time, \ sem_timedout); RTS_ERROR(VARLSTCNT(13) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_SEMWT2LONG, 7, process_id, 0, LEN_AND_LIT("access control"), DB_LEN_STR(reg), sem_pid); } else { save_errno = errno; RETURN_IF_BYPASSED(bypassed_ftok, indefinite_wait, sem_stacktrace_time, \ sem_timedout); if (!SEM_REMOVED(save_errno)) RTS_ERROR(VARLSTCNT(12) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, RTS_ERROR_LITERAL("semop()"), CALLFROM, save_errno); } } else { bypassed_access = ok_to_bypass; semop_success = do_blocking_semop(udi->semid, gtm_access_sem, sem_stacktrace_timep, sem_timedoutp, &retstat, reg, &bypassed_access, &access_counter_halted, TRUE); assert(ok_to_bypass || !bypassed_access); if (!semop_success) { RETURN_IF_BYPASSED(bypassed_ftok, indefinite_wait, sem_stacktrace_time, \ sem_timedout); if (!SEM_REMOVED(retstat.save_errno)) ISSUE_SEMWAIT_ERROR((&retstat), reg, udi, "access control"); save_errno = retstat.save_errno; } else { if (bypassed_access) SEND_MSG(VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_LIT("Access control bypassed at init")); save_errno = status = SS_NORMAL; incr_cnt = (incr_cnt && !access_counter_halted); break; } } } else if (!SEM_REMOVED(save_errno)) { RETURN_IF_BYPASSED(bypassed_ftok, indefinite_wait, sem_stacktrace_time, sem_timedout); RTS_ERROR(VARLSTCNT(12) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, RTS_ERROR_LITERAL("semop()"), CALLFROM, save_errno); } assert(SEM_REMOVED(save_errno)); if ((MAX_ACCESS_SEM_RETRIES - 1) == loopcnt) RTS_ERROR(VARLSTCNT(12) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, RTS_ERROR_LITERAL("semop()"), CALLFROM, save_errno); READ_DB_FILE_HEADER(reg, tsd, err_ret); RETURN_IF_ERROR(err_ret, err_ret, indefinite_wait, sem_stacktrace_time, sem_timedout); } assert((-1 != status) || bypassed_access); if (!indefinite_wait) CANCEL_SEM_TIMERS(sem_stacktrace_time, sem_timedout); if (!bypassed_access) { udi->grabbed_access_sem = TRUE; /* Now that we have the access control semaphore, re-read the file header so we have uptodate information * in case some of the fields (like access method) were modified concurrently by MUPIP SET -FILE. */ READ_DB_FILE_HEADER(reg, tsd, err_ret); RETURN_IF_ERROR(err_ret, err_ret, indefinite_wait, sem_stacktrace_time, sem_timedout); UDI2CSD(udi, tsd); /* Since we read the file header again, tsd->semid/shmid and corresponding ctime fields * will not be uptodate. Refresh it with udi copies as they are more uptodate. */ } udi->counter_acc_incremented = incr_cnt; } else { /* for have_standalone_access we were already in "mu_rndwn_file" and got "semid" semaphore. Since mu_rndwn_file * would have gotten "ftok" semaphore before acquiring the access control semaphore, no need to get the "ftok" * semaphore as well. */ incr_cnt = !read_only; ftok_counter_halted = FALSE; access_counter_halted = FALSE; READ_DB_FILE_HEADER(reg, tsd, err_ret); /* file already opened by "dbfilopn" done from "gvcst_init" */ RETURN_IF_ERROR(err_ret, err_ret, indefinite_wait, sem_stacktrace_time, sem_timedout); db_do_crypt_init = (USES_ENCRYPTION(tsd->is_encrypted) && !IS_LKE_IMAGE); INIT_PROC_ENCRYPTION_IF_NEEDED(db_do_crypt_init, init_status); INIT_DB_ENCRYPTION_IF_NEEDED(db_do_crypt_init, init_status, reg, csa, tsd, crypt_warning); CSD2UDI(tsd, udi); /* Make sure "mu_rndwn_file" has created semaphore for standalone access */ assertpro((INVALID_SEMID != udi->semid) && (0 != udi->gt_sem_ctime)); /* Make sure "mu_rndwn_file" has reset shared memory. In pro, just clear it and proceed. */ assert((INVALID_SHMID == udi->shmid) && (0 == udi->gt_shm_ctime)); /* In pro, just clear it and proceed */ udi->shmid = INVALID_SHMID; /* reset shmid so dbinit_ch does not get confused in case we go there */ udi->sem_created = TRUE; udi->shm_created = !tsd->read_only; } assert(udi->grabbed_access_sem || bypassed_access); DO_DB_HDR_CHECK(reg, tsd); /* Basic sanity check on the file header fields */ if (udi->fd_opened_with_o_direct) { /* "tsd" points to dio_buff.aligned, a global variable buffer that will likely be reused by other functions * inside "db_init" (e.g. "recover_truncate" invoking "db_write_eof_block"). Point it back to tsdbuff * after copying the contents as we need access to "tsd" even after the "recover_truncate" invocation. */ assert(tsd == (sgmnt_data_ptr_t)(TREF(dio_buff)).aligned); memcpy(&tsdbuff, (sgmnt_data_ptr_t)(TREF(dio_buff)).aligned, SGMNT_HDR_LEN); tsd = &tsdbuff; } if (WBTEST_ENABLED(WBTEST_HOLD_ONTO_ACCSEM_IN_DBINIT)) { DBGFPF((stderr, "Holding the access control semaphore.. Sleeping for 30 seconds\n")); LONG_SLEEP(30); DBGFPF((stderr, "30 second sleep exhausted.. continuing with rest of db_init..\n")); } if (WBTEST_ENABLED(WBTEST_HOLD_FTOK_UNTIL_BYPASS)) { if ((3 * DB_COUNTER_SEM_INCR) == semctl(udi->ftok_semid, DB_COUNTER_SEM, GETVAL)) { /* We are ftok semaphore holder */ DBGFPF((stderr, "Holding the ftok semaphore until a new process comes along.\n")); while (3 == semctl(udi->ftok_semid, DB_COUNTER_SEM, GETVAL)) LONG_SLEEP(1); } } /* Now that the access control lock is obtained and file header passed all sanity checks, update the acc_meth of the * region from the one in the file header (in case they are different). This way, any later code that relies on the * acc_meth dereferenced from the region will work correctly. Instead of checking if they are different, do the assignment * unconditionally */ reg->dyn.addr->acc_meth = tsd->acc_meth; reg->dyn.addr->read_only = tsd->read_only; reg->dyn.addr->full_blkwrt = tsd->write_fullblk; COPY_AIO_SETTINGS(reg->dyn.addr, tsd); /* copy "asyncio" from tsd to reg->dyn.addr */ new_shm_ipc = udi->shm_created; if (new_shm_ipc && !tsd->read_only) { /* Bypassers are not allowed to create shared memory so we don't end up with conflicting shared memories */ assert(!bypassed_ftok && !bypassed_access); /* Since we are about to allocate new shared memory, if necessary, adjust the journal buffer size right now. * Note that if the process setting up shared memory is a read-only process, then we might not flush updated * jnl_buffer_size to the file header, which is fine because the value in shared memory is what all processes * are looking at. If necessary, the next process to initialize shared memory will repeat the process of * adjusting the jnl_buffer_size value. */ jnl_buffer_size = tsd->jnl_buffer_size; if ((0 != jnl_buffer_size) && (jnl_buffer_size < JNL_BUFFER_MIN)) { ROUND_UP_MIN_JNL_BUFF_SIZE(tsd->jnl_buffer_size, tsd); SNPRINTF(s, JNLBUFFUPDAPNDX_SIZE, JNLBUFFUPDAPNDX, JNL_BUFF_PORT_MIN(tsd), JNL_BUFFER_MAX); SEND_MSG(VARLSTCNT(10) ERR_JNLBUFFREGUPD, 4, REG_LEN_STR(reg), jnl_buffer_size, tsd->jnl_buffer_size, ERR_TEXT, 2, LEN_AND_STR(s)); } dbsecspc(reg, tsd, &sec_size); /* Find db segment size */ /* Create new shared memory using IPC_PRIVATE. System guarantees a unique id */ GTM_WHITE_BOX_TEST(WBTEST_FAIL_ON_SHMGET, sec_size, GTM_UINT64_MAX); if (-1 == (status_l = udi->shmid = gtm_shmget(IPC_PRIVATE, sec_size, RWDALL | IPC_CREAT, TRUE))) { udi->shmid = (int)INVALID_SHMID; status_l = INVALID_SHMID; RTS_ERROR(VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("Error with database shmget"), errno); } tsd->shmid = udi->shmid; if (-1 == shmctl(udi->shmid, IPC_STAT, &shmstat)) RTS_ERROR(VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("Error with database control shmctl IPC_STAT1"), errno); /* change uid, group-id and permissions if needed */ need_shmctl = FALSE; if ((INVALID_UID != user_id) && (user_id != shmstat.shm_perm.uid)) { shmstat.shm_perm.uid = user_id; need_shmctl = TRUE; } if ((INVALID_GID != group_id) && (group_id != shmstat.shm_perm.gid)) { shmstat.shm_perm.gid = group_id; need_shmctl = TRUE; } if (shmstat.shm_perm.mode != perm) { shmstat.shm_perm.mode = perm; need_shmctl = TRUE; } if (need_shmctl && (-1 == shmctl(udi->shmid, IPC_SET, &shmstat))) RTS_ERROR(VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("Error with database control shmctl IPC_SET"), errno); /* Warning: We must read the shm_ctime using IPC_STAT after IPC_SET, which changes it. * We must NOT do any more IPC_SET or SETVAL after this. Our design is to use * shm_ctime as creation time of shared memory and store it in file header. */ if (-1 == shmctl(udi->shmid, IPC_STAT, &shmstat)) RTS_ERROR(VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("Error with database control shmctl IPC_STAT2"), errno); tsd->gt_shm_ctime.ctime = udi->gt_shm_ctime = shmstat.shm_ctime; GTM_ATTACH_SHM; shm_setup_ok = TRUE; } else if (tsd->read_only) { dbsecspc(reg, tsd, &sec_size); /* Find db segment size */ csa->db_addrs[0] = malloc(ROUND_UP2(sec_size, OS_PAGE_SIZE) + OS_PAGE_SIZE); /* Init the space to zero; the system assumes shared memory gets init'd to zero, funny errors without this */ memset(csa->db_addrs[0], 0, ROUND_UP2(sec_size, OS_PAGE_SIZE) + OS_PAGE_SIZE); /* Move the pointer above so it falls on a cacheline boundary; * since this segment will be needed during all of process execution * we don't need to record the original place for cleanup */ csa->db_addrs[0] = (sm_uc_ptr_t)((UINTPTR_T)csa->db_addrs[0] + OS_PAGE_SIZE) - ((UINTPTR_T)csa->db_addrs[0] % OS_PAGE_SIZE); csa->nl = (node_local_ptr_t)csa->db_addrs[0]; read_only = TRUE; reg->read_only = TRUE; csa->read_write = FALSE; shm_setup_ok = TRUE; incr_cnt = 0; } else { GTM_ATTACH_SHM_AND_CHECK_VERS(vermismatch, shm_setup_ok); if (vermismatch) { GTM_VERMISMATCH_ERROR; } else if (!shm_setup_ok) { PRINT_CRASH_MESSAGE(0, tsd, ERR_TEXT, 2, LEN_AND_LIT("shared memory is invalid")); } /* Compare file header fields against cached values. Restore as necessary to fix shared memory layout calculation */ if (IS_DSE_IMAGE) { DSE_VERIFY_AND_RESTORE(csa, tsd, acc_meth); DSE_VERIFY_AND_RESTORE(csa, tsd, lock_space_size); DSE_VERIFY_AND_RESTORE(csa, tsd, jnl_buffer_size); DSE_VERIFY_AND_RESTORE(csa, tsd, blk_size); } } csa->critical = (CRIT_PTR_T)(csa->db_addrs[0] + NODE_LOCAL_SIZE); assert(((INTPTR_T)csa->critical & 0xf) == 0); /* critical should be 16-byte aligned */ # ifdef CACHELINE_SIZE assert(0 == ((INTPTR_T)csa->critical & (CACHELINE_SIZE - 1))); # endif /* Note: Here we check jnl_state from database file; its value cannot change without stand-alone access. * The jnl_buff should be initialized irrespective of read/write process */ JNL_INIT(csa, reg, tsd); csa->shmpool_buffer = (shmpool_buff_hdr_ptr_t)(csa->db_addrs[0] + NODE_LOCAL_SPACE(tsd) + JNL_SHARE_SIZE(tsd)); /* Initialize memory for snapshot context */ if (NULL == csa->ss_ctx) /* May have been allocated if opened/closed/opened */ csa->ss_ctx = malloc(SIZEOF(snapshot_context_t)); DEFAULT_INIT_SS_CTX((SS_CTX_CAST(csa->ss_ctx))); csa->mlkctl = (struct mlk_ctldata_struct *) ((sm_uc_ptr_t)csa->shmpool_buffer + SHMPOOL_SECTION_SIZE); csa->mlkctl_len = LOCK_SPACE_SIZE(tsd); csa->total_blks = tsd->trans_hist.total_blks; /* For test to see if file has extended */ cnl = csa->nl; if (new_shm_ipc) { # ifdef DEBUG /* We allocated shared storage -- "shmget" ensures it is null initialized. Assert that. */ ptr = (char *)cnl; for (i = 0; i < SIZEOF(*cnl); i++) assert('\0' == ptr[i]); # endif cnl->sec_size = sec_size; /* Set the shared memory size */ if (JNL_ALLOWED(csa)) { /* initialize jb->cycle to a value different from initial value of jpc->cycle (0). although this is not * necessary right now, in the future, the plan is to change "jnl_ensure_open" to only do a cycle mismatch * check in order to determine whether to call jnl_file_open() or not. this is in preparation for that. */ csa->jnl->jnl_buff->cycle = 1; } GTM_CACHE_INTO_SHM(csa, tsd); } is_bg = (dba_bg == tsd->acc_meth); if (is_bg) csd = csa->hdr = (sgmnt_data_ptr_t)((sm_uc_ptr_t)csa->mlkctl + csa->mlkctl_len + CACHE_CONTROL_SIZE(tsd)); else { FSTAT_FILE(udi->fd, &stat_buf, stat_res); if (-1 == stat_res) RTS_ERROR(VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), errno); csd = csa->hdr = (sgmnt_data_ptr_t)((sm_uc_ptr_t)csa->mlkctl + csa->mlkctl_len); mmap_sz = stat_buf.st_size - BLK_ZERO_OFF(tsd->start_vbn) - csd->free_space; assert(0 < mmap_sz); CHECK_LARGEFILE_MMAP(reg, mmap_sz); /* can issue rts_error MMFILETOOLARGE */ # ifdef _AIX mmapaddr = shmat(udi->fd, 0, (read_only ? (SHM_MAP|SHM_RDONLY) : SHM_MAP)); # else mmapaddr = MMAP_FD(udi->fd, mmap_sz, BLK_ZERO_OFF(tsd->start_vbn), read_only); # endif if (-1 == (INTPTR_T)mmapaddr) { RTS_ERROR(VARLSTCNT(12) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, LEN_AND_STR(MEM_MAP_SYSCALL), CALLFROM, errno); } # ifdef _AIX csa->db_addrs[0] = (sm_uc_ptr_t)((sm_uc_ptr_t)mmapaddr + BLK_ZERO_OFF(tsd->start_vbn)); # else csa->db_addrs[0] = mmapaddr; # endif csa->db_addrs[1] = (sm_uc_ptr_t)((sm_uc_ptr_t)csa->db_addrs[0] + mmap_sz - 1); /* '- 1' due to 0-based indexing */ assert(csa->db_addrs[1] > csa->db_addrs[0]); } /* At this point, shm_setup_ok is TRUE so we are guaranteed that vermismatch is FALSE. Therefore, we can safely * dereference cnl->glob_sec_init without worrying about whether or not it could be at a different offset than * the current version. The only exception is DSE which can continue even after the VERMISMATCH error and hence * can have shm_setup_ok set to FALSE at this point. */ if (shm_setup_ok && !cnl->glob_sec_init && !(bypassed_ftok || bypassed_access)) { assert(udi->shm_created || tsd->read_only); assert(new_shm_ipc || tsd->read_only); assert(!vermismatch); memcpy(csd, tsd, SIZEOF(sgmnt_data)); READ_DB_FILE_MASTERMAP(reg, csd); if (csd->machine_name[0]) /* crash occurred */ { if (0 != STRNCMP_STR(csd->machine_name, machine_name, MAX_MCNAMELEN)) /* crashed on some other node */ RTS_ERROR(VARLSTCNT(8) ERR_HOSTCONFLICT, 6, LEN_AND_STR(machine_name), DB_LEN_STR(reg), LEN_AND_STR(csd->machine_name)); else { PRINT_CRASH_MESSAGE(0, csd, ERR_TEXT, 2, LEN_AND_LIT("machine name in file header is non-null implying possible crash")); } } if (is_bg) { cnl->cache_off = -CACHE_CONTROL_SIZE(csd); db_csh_ini(csa); bt_malloc(csa); } db_csh_ref(csa, TRUE); shmpool_buff_init(reg); SS_INFO_INIT(csa); STRNCPY_STR(cnl->machine_name, machine_name, MAX_MCNAMELEN); /* machine name */ assert(MAX_REL_NAME > gtm_release_name_len); memcpy(cnl->now_running, gtm_release_name, gtm_release_name_len + 1); /* GT.M release name */ memcpy(cnl->label, GDS_LABEL, GDS_LABEL_SZ - 1); /* GDS label */ memcpy(cnl->fname, reg->dyn.addr->fname, reg->dyn.addr->fname_len); /* database filename */ cnl->creation_date_time4 = csd->creation_time4; cnl->highest_lbm_blk_changed = GDS_CREATE_BLK_MAX; /* set to invalid block number */ cnl->wcs_timers = -1; cnl->nbb = BACKUP_NOT_IN_PROGRESS; cnl->unique_id.uid = FILE_INFO(reg)->fileid; /* save what file we initialized this storage for */ /* save pointers in csa to access shared memory */ cnl->critical = (sm_off_t)((sm_uc_ptr_t)csa->critical - (sm_uc_ptr_t)cnl); if (JNL_ALLOWED(csa)) cnl->jnl_buff = (sm_off_t)((sm_uc_ptr_t)csa->jnl->jnl_buff - (sm_uc_ptr_t)cnl); cnl->shmpool_buffer = (sm_off_t)((sm_uc_ptr_t)csa->shmpool_buffer - (sm_uc_ptr_t)cnl); if (is_bg) /* Field is sm_off_t (4 bytes) so only in BG mode is this assurred to be 4 byte capable */ cnl->hdr = (sm_off_t)((sm_uc_ptr_t)csd - (sm_uc_ptr_t)cnl); cnl->lock_addrs = (sm_off_t)((sm_uc_ptr_t)csa->mlkctl - (sm_uc_ptr_t)cnl); if (!read_only || is_bg) { csd->trans_hist.early_tn = csd->trans_hist.curr_tn; csd->max_update_array_size = csd->max_non_bm_update_array_size = (int4)(ROUND_UP2(MAX_NON_BITMAP_UPDATE_ARRAY_SIZE(csd), UPDATE_ARRAY_ALIGN_SIZE)); csd->max_update_array_size += (int4)(ROUND_UP2(MAX_BITMAP_UPDATE_ARRAY_SIZE(csd), UPDATE_ARRAY_ALIGN_SIZE)); /* add current db_csh counters into the cumulative counters and reset the current counters */ # define TAB_DB_CSH_ACCT_REC(COUNTER, DUMMY1, DUMMY2) \ csd->COUNTER.cumul_count += csd->COUNTER.curr_count; \ csd->COUNTER.curr_count = 0; # include "tab_db_csh_acct_rec.h" # undef TAB_DB_CSH_ACCT_REC } SET_TRACEABLE_VAR(cnl->wc_blocked, WC_UNBLOCK); /* Since we are creating shared memory, reset wc_blocked to 0 */ gvstats_rec_csd2cnl(csa); /* should be called before "db_auto_upgrade" */ reg->dyn.addr->ext_blk_count = csd->extension_size; mlk_shr_init((sm_uc_ptr_t)csa->mlkctl, csd->lock_space_size, csa, (FALSE == read_only)); /* should be called before "gtm_mutex_init" to ensure NUM_CRIT_ENTRY is nonzero */ if (!MEMCMP_LIT(csd->label, GDS_LABEL)) db_auto_upgrade(reg); else if(!MEMCMP_LIT(csd->label, V6_GDS_LABEL)) v6_db_auto_upgrade(reg); DEBUG_ONLY(locknl = cnl;) /* for DEBUG_ONLY LOCK_HIST macro */ gtm_mutex_init(reg, NUM_CRIT_ENTRY(csd), FALSE); DEBUG_ONLY(locknl = NULL;) /* restore "locknl" to default value */ if (read_only) cnl->remove_shm = TRUE; /* gds_rundown can remove shmem if first process has read-only access */ if (FALSE == csd->multi_site_open) { /* first time database is opened after upgrading to a GTM version that supports multi-site * replication */ csd->zqgblmod_seqno = 0; csd->zqgblmod_tn = 0; if (csd->pre_multisite_resync_seqno > csd->reg_seqno) csd->pre_multisite_resync_seqno = csd->reg_seqno; csd->multi_site_open = TRUE; } cnl->glob_sec_init = TRUE; STAT_FILE((char *)cnl->fname, &stat_buf, stat_res); if (-1 == stat_res) { save_errno = errno; RTS_ERROR(VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), save_errno); } set_gdid_from_stat(&cnl->unique_id.uid, &stat_buf); recover_truncate(csa, csd, reg); cnl->jnlpool_shmid = INVALID_SHMID; assert(0 == cnl->statsdb_fname_len); assert(0 == cnl->statsdb_cur_error); assert(0 == cnl->statsdb_error_cycle); cnl->statsdb_created = FALSE; cnl->statsdb_rundown_clean = FALSE; if (IS_STATSDB_REGNAME(reg)) { /* Note that in case this region is a statsdb, its baseDBnl->statsdb_rundown_clean would have been set to * FALSE at baseDB "db_init" time but it is possible the statsdb has been through "gds_rundown" and we * are back in "db_init" for the same statsDB (e.g. VIEW "NOSTATSHARE" VIEW "STATSHARE" sequence). In that * case, we need to reset the flag to FALSE. Hence the code below. */ STATSDBREG_TO_BASEDBREG(reg, baseDBreg); assert(baseDBreg->open); baseDBcsa = &FILE_INFO(baseDBreg)->s_addrs; baseDBnl = baseDBcsa->nl; baseDBnl->statsdb_rundown_clean = FALSE; assert(csd->basedb_fname_len); /* should have been initialized for statsdb at "mucregini" time */ } } else { if (STRNCMP_STR(cnl->machine_name, machine_name, MAX_MCNAMELEN)) /* machine names do not match */ { if (cnl->machine_name[0]) RTS_ERROR(VARLSTCNT(8) ERR_HOSTCONFLICT, 6, LEN_AND_STR(machine_name), DB_LEN_STR(reg), LEN_AND_STR(cnl->machine_name)); else { PRINT_CRASH_MESSAGE(0, csd, ERR_TEXT, 2, LEN_AND_LIT("machine name in shared memory is non-null implying possible crash")); } } /* Since nl is memset to 0 initially and then fname is copied over from gv_cur_region and since "fname" is * guaranteed to not exceed MAX_FN_LEN, we should have a terminating '\0' at least at cnl->fname[MAX_FN_LEN] */ assert(cnl->fname[MAX_FN_LEN] == '\0'); /* Note: the first '\0' in cnl->fname can be much earlier */ /* Check whether cnl->fname exists. If not, then it is a serious condition. Error out. */ STAT_FILE((char *)cnl->fname, &stat_buf, stat_res); if (-1 == stat_res) { save_errno = errno; SEND_MSG(VARLSTCNT(13) ERR_REQRUNDOWN, 4, DB_LEN_STR(reg), LEN_AND_STR(cnl->machine_name), ERR_DBNAMEMISMATCH, 4, DB_LEN_STR(reg), udi->shmid, cnl->fname, save_errno); PRINT_CRASH_MESSAGE(3, cnl, ERR_DBNAMEMISMATCH, 4, DB_LEN_STR(reg), udi->shmid, cnl->fname, save_errno); } /* Check whether cnl->fname and cnl->unique_id.uid are in sync. If not error out. */ if (FALSE == is_gdid_stat_identical(&cnl->unique_id.uid, &stat_buf)) { SEND_MSG(VARLSTCNT(12) ERR_REQRUNDOWN, 4, DB_LEN_STR(reg), LEN_AND_STR(cnl->machine_name), ERR_DBIDMISMATCH, 4, cnl->fname, DB_LEN_STR(reg), udi->shmid); PRINT_CRASH_MESSAGE(2, cnl, ERR_DBIDMISMATCH, 4, cnl->fname, DB_LEN_STR(reg), udi->shmid); } /* Previously, we used to check for cnl->creation_date_time4 vs csd->creation_time4 and treat it as * an id mismatch situation as well. But later it was determined that as long as the filename and the fileid * match between the database file header and the copy in shared memory, there is no more matching that needs * to be done. It is not possible for the user to create a situation where the filename/fileid matches but * the creation time does not. The only way for this to happen is shared memory corruption in which case we * have a much bigger problem to deal with -- 2011/03/30 --- nars. */ if (FALSE == is_gdid_gdid_identical(&FILE_INFO(reg)->fileid, &cnl->unique_id.uid)) { SEND_MSG(VARLSTCNT(12) ERR_REQRUNDOWN, 4, DB_LEN_STR(reg), LEN_AND_STR(cnl->machine_name), ERR_DBSHMNAMEDIFF, 4, DB_LEN_STR(reg), udi->shmid, cnl->fname); PRINT_CRASH_MESSAGE(2, cnl, ERR_DBSHMNAMEDIFF, 4, DB_LEN_STR(reg), udi->shmid, cnl->fname); } /* If a regular Recover/Rollback created the shared memory and died (because of a user error or runtime error), * any process that comes up after that should NOT touch the shared memory or database. The user should reissue * Rollback/Recover command that will fix the state of the shared memory and bring the database back to a consistent * state. Note that the reissue of a regular Rollback/Recover command will NOT hit this condition because it invokes * mu_rndwn_file (STANDALONE) that removes the shared memory. The only case in which mu_rndwn_file does NOT remove * shared memory is if it was invoked by an Online Rollback in which case the below check should be bypassed * However, if there is an online rollback active (that is not us) this should be a transient condition * and if we wait a little bit, it should go away. */ if (cnl->donotflush_dbjnl && !jgbl.onlnrlbk) { /* If we can see online rollback is out there, spin a bit */ while (cnl->donotflush_dbjnl && !jgbl.onlnrlbk && cnl->onln_rlbk_pid) { if (!(slp_cnt % 100)) is_logged = FALSE; if (! is_logged) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_LIT("Spinning for clean DB header")); is_logged = TRUE; } wcs_sleep(++slp_cnt); if (1000 < slp_cnt) break; } if (cnl->donotflush_dbjnl && !jgbl.onlnrlbk) PRINT_CRASH_MESSAGE(0, cnl, ERR_TEXT, 2, LEN_AND_LIT("mupip recover/rollback created shared memory. Needs MUPIP RUNDOWN")); } /* verify pointers from our calculation vs. the copy in shared memory */ if (cnl->critical != (sm_off_t)((sm_uc_ptr_t)csa->critical - (sm_uc_ptr_t)cnl)) { PRINT_CRASH_MESSAGE(2, cnl, ERR_NLMISMATCHCALC, 4, LEN_AND_LIT("critical"), (uint4)((sm_uc_ptr_t)csa->critical - (sm_uc_ptr_t)cnl), (uint4)cnl->critical); } if ((JNL_ALLOWED(csa)) && (cnl->jnl_buff != (sm_off_t)((sm_uc_ptr_t)csa->jnl->jnl_buff - (sm_uc_ptr_t)cnl))) { PRINT_CRASH_MESSAGE(2, cnl, ERR_NLMISMATCHCALC, 4, LEN_AND_LIT("journal buffer"), (uint4)((sm_uc_ptr_t)csa->jnl->jnl_buff - (sm_uc_ptr_t)cnl), (uint4)cnl->jnl_buff); } if (cnl->shmpool_buffer != (sm_off_t)((sm_uc_ptr_t)csa->shmpool_buffer - (sm_uc_ptr_t)cnl)) { PRINT_CRASH_MESSAGE(2, cnl, ERR_NLMISMATCHCALC, 4, LEN_AND_LIT("backup buffer"), (uint4)((sm_uc_ptr_t)csa->shmpool_buffer - (sm_uc_ptr_t)cnl), (uint4)cnl->shmpool_buffer); } if ((is_bg) && (cnl->hdr != (sm_off_t)((sm_uc_ptr_t)csd - (sm_uc_ptr_t)cnl))) { PRINT_CRASH_MESSAGE(2, cnl, ERR_NLMISMATCHCALC, 4, LEN_AND_LIT("file header"), (uint4)((sm_uc_ptr_t)csd - (sm_uc_ptr_t)cnl), (uint4)cnl->hdr); } if (cnl->lock_addrs != (sm_off_t)((sm_uc_ptr_t)csa->mlkctl - (sm_uc_ptr_t)cnl)) { PRINT_CRASH_MESSAGE(2, cnl, ERR_NLMISMATCHCALC, 4, LEN_AND_LIT("lock address"), (uint4)((sm_uc_ptr_t)csa->mlkctl - (sm_uc_ptr_t)cnl), (uint4)cnl->lock_addrs); } assert(!udi->shm_created); if (is_bg) db_csh_ini(csa); } /* If this is a source server, bind this database to the journal pool shmid & instance file name that the * source server started with. Assert that jnlpool_init has already been done by the source server before * it does db_init. * In addition, if this is not a source server, but has done a "jnlpool_init" (in caller function "gvcst_init"), * and the source server had shut down just before we got the access control lock in db_init(), we would have * had to create db shm afresh. In that case, the source server would have left the jnlpool intact but db shm * would have lost the jnlpool initialization that the source server did. So do it on behalf of the source * server even though this is not a source server. */ need_jnlpool_setup = REPL_ALLOWED(csd) && is_src_server; save_jnlpool = jnlpool; if (need_jnlpool_setup) assert(jnlpool && jnlpool->pool_init); /* only one jnlpool for source server */ else if (!is_src_server && pool_init && REPL_ALLOWED(csd) && gd_header && udi->shm_created && REPL_INST_AVAILABLE(csa->gd_ptr)) { /* not source server but db shm created so check proper jnlpool */ status = filename_to_id(&replfile_gdid, replpool_id.instfilename); /* set by REPL_INST_AVAILABLE */ assertpro(SS_NORMAL == status); if (jnlpool && jnlpool->pool_init) { tmp_gdid = &FILE_ID(jnlpool->jnlpool_dummy_reg); if (!gdid_cmp(tmp_gdid, &replfile_gdid)) need_jnlpool_setup = TRUE; /* current jnlpool is for this region */ } if (!need_jnlpool_setup) { /* need to find right jnlpool */ for (local_jnlpool = jnlpool_head; local_jnlpool; local_jnlpool = local_jnlpool->next) { if (local_jnlpool->pool_init) { tmp_gdid = &FILE_ID(jnlpool->jnlpool_dummy_reg); if (!gdid_cmp(tmp_gdid, &replfile_gdid)) { jnlpool = local_jnlpool; need_jnlpool_setup = TRUE; break; } } } } } if (need_jnlpool_setup) { assert((NULL != jnlpool) && (NULL != jnlpool->repl_inst_filehdr)); /* Note: cnl->replinstfilename is changed under control of the init/rundown semaphore only. */ assert('\0' != jnlpool->jnlpool_ctl->jnlpool_id.instfilename[0]); replinst_mismatch = FALSE; if ('\0' == cnl->replinstfilename[0]) STRCPY(cnl->replinstfilename, jnlpool->jnlpool_ctl->jnlpool_id.instfilename); else if (STRCMP(cnl->replinstfilename, jnlpool->jnlpool_ctl->jnlpool_id.instfilename)) { assert(!(jnlpool->pool_init && udi->shm_created)); replinst_mismatch = TRUE; } /* Note: cnl->jnlpool_shmid is changed under control of the init/rundown semaphore only. */ assert(INVALID_SHMID != jnlpool->repl_inst_filehdr->jnlpool_shmid); if (INVALID_SHMID == cnl->jnlpool_shmid) cnl->jnlpool_shmid = jnlpool->repl_inst_filehdr->jnlpool_shmid; else if (cnl->jnlpool_shmid != jnlpool->repl_inst_filehdr->jnlpool_shmid) { /* shmid mismatch. Check if the shmid noted down in db filehdr is out-of-date. * Possible if the jnlpool has since been deleted. If so, note the new one down. * If not, then issue an error. */ assert(!(jnlpool->pool_init && udi->shm_created)); if (-1 == shmctl(cnl->jnlpool_shmid, IPC_STAT, &shmstat)) { save_errno = errno; if (SHM_REMOVED(save_errno)) { replinst_mismatch = FALSE; cnl->jnlpool_shmid = jnlpool->repl_inst_filehdr->jnlpool_shmid; } else replinst_mismatch = TRUE; } else replinst_mismatch = TRUE; } /* Replication instance file or jnlpool id mismatch. Issue error. */ if (replinst_mismatch) { assert(!(jnlpool->pool_init && udi->shm_created)); if (INVALID_SHMID == cnl->jnlpool_shmid) RTS_ERROR(VARLSTCNT(4) ERR_REPLINSTNOSHM, 2, DB_LEN_STR(reg)); else RTS_ERROR(VARLSTCNT(10) ERR_REPLINSTMISMTCH, 8, LEN_AND_STR(jnlpool->jnlpool_ctl->jnlpool_id.instfilename), jnlpool->repl_inst_filehdr->jnlpool_shmid, DB_LEN_STR(reg), LEN_AND_STR(cnl->replinstfilename), cnl->jnlpool_shmid); } } csa->root_search_cycle = cnl->root_search_cycle; csa->onln_rlbk_cycle = cnl->onln_rlbk_cycle; /* take local copy of the current Online Rollback cycle */ csa->db_onln_rlbkd_cycle = cnl->db_onln_rlbkd_cycle; /* take local copy of the current Online Rollback mod cycle */ SYNC_RESERVEDDBFLAGS_REG_CSA_CSD(reg, csa, csd, cnl); INITIALIZE_CSA_ENCR_PTR(csa, csd, udi, db_do_crypt_init, crypt_warning, bypassed_ftok); /* sets csa->encr_ptr */ /* Record ftok information as soon as shared memory set up is done */ if (!have_standalone_access && !bypassed_ftok) FTOK_TRACE(csa, csd->trans_hist.curr_tn, ftok_ops_lock, process_id); assert(!incr_cnt || !read_only); if (incr_cnt) { if (!cnl->first_writer_seen) { cnl->first_writer_seen = TRUE; cnl->remove_shm = FALSE; } if (!cnl->first_nonbypas_writer_seen && !bypassed_ftok && !bypassed_access && !FROZEN_CHILLED(csa) && !tsd->read_only) { /* For read-write process flush file header to write machine_name, * semaphore, shared memory id and semaphore creation time to disk. * Note: If first process to open db was read-only, then sem/shm info would have already been flushed * using gtmsecshr but machine_name is flushed only now. It is possible the first writer bypassed * the ftok/access in which case we will not write the machine_name then (because we do not hold a * lock on the database to safely flush the file header) but it is considered acceptable to wait * until the first non-bypassing-writer process attaches since those bypassing processes would be * DSE/LKE only. */ cnl->first_nonbypas_writer_seen = TRUE; STRNCPY_STR(csd->machine_name, machine_name, MAX_MCNAMELEN); assert(csd->shmid == tsd->shmid); /* csd already has uptodate sem/shm info from the UDI2CSD call above */ assert(csd->semid == tsd->semid); assert(!memcmp(&csd->gt_sem_ctime, &tsd->gt_sem_ctime, SIZEOF(tsd->gt_sem_ctime))); assert(!memcmp(&csd->gt_shm_ctime, &tsd->gt_shm_ctime, SIZEOF(tsd->gt_shm_ctime))); if (0 == memcmp(csd->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv(csd); DB_LSEEKWRITE(csa, udi, udi->fn, udi->fd, (off_t)0, (sm_uc_ptr_t)csd, SGMNT_HDR_LEN, save_errno); if (0 != save_errno) { if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; RTS_ERROR(VARLSTCNT(9) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("Error with database header flush"), save_errno); } } } else if (read_only && new_shm_ipc) { /* For read-only process if shared memory and semaphore created for first time, * semaphore and shared memory id, and semaphore creation time are written to disk. */ db_ipcs.open_fd_with_o_direct = udi->fd_opened_with_o_direct; db_ipcs.semid = tsd->semid; /* use tsd instead of csd in order for MM to work too */ db_ipcs.shmid = tsd->shmid; db_ipcs.gt_sem_ctime = tsd->gt_sem_ctime.ctime; db_ipcs.gt_shm_ctime = tsd->gt_shm_ctime.ctime; db_ipcs.fn_len = reg->dyn.addr->fname_len; memcpy(db_ipcs.fn, reg->dyn.addr->fname, reg->dyn.addr->fname_len); db_ipcs.fn[reg->dyn.addr->fname_len] = 0; WAIT_FOR_REPL_INST_UNFREEZE_SAFE(csa); if(!tsd->read_only) { secshrstat = send_mesg2gtmsecshr(FLUSH_DB_IPCS_INFO, 0, (char *)NULL, 0); csa->read_only_fs = (EROFS == secshrstat); } if ((0 != secshrstat) && !csa->read_only_fs && !tsd->read_only) { if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; RTS_ERROR(VARLSTCNT(8) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("gtmsecshr failed to update database file header")); } } if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; if (ftok_counter_halted || access_counter_halted) { if (!csd->mumps_can_bypass) { /* We skipped ftok/access counter increment operation, but after reading the file header, we found out we * are not allowed to do that. Abort. */ SET_SEMWAIT_FAILURE_RETSTAT(&retstat, ERANGE, op_semctl_or_semop, 0, ERR_CRITSEMFAIL, 0); ISSUE_SEMWAIT_ERROR((&retstat), reg, udi, (ftok_counter_halted ? "ftok" : "access control")); } if (ftok_counter_halted && !cnl->ftok_counter_halted) { cnl->ftok_counter_halted = TRUE; send_msg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_NOMORESEMCNT, 5, LEN_AND_LIT("ftok"), FILE_TYPE_DB, DB_LEN_STR(reg)); } if (access_counter_halted && !cnl->access_counter_halted) { cnl->access_counter_halted = TRUE; send_msg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_NOMORESEMCNT, 5, LEN_AND_LIT("access"), FILE_TYPE_DB, DB_LEN_STR(reg)); } } if (udi->counter_acc_incremented && cnl->access_counter_halted) { /* Shared access counter had overflown a while back but is not right now. Undo the counter bump that we * did as otherwise we will later have problems (for example if this is a MUPIP SET -JOURNAL command that * needs standalone access and does a "gds_rundown" followed by a "mu_rndwn_file" later. The "mu_rndwn_file" * call will wait for the counter to become 0 which it never will as we would have bumped it here. */ SET_SOP_ARRAY_FOR_DECR_CNT(sop, sopcnt, (SEM_UNDO | IPC_NOWAIT)); SEMOP(udi->semid, sop, sopcnt, status, NO_WAIT); udi->counter_acc_incremented = FALSE; assert(-1 != status); /* since we hold the access control lock, we do not expect any errors */ } if (udi->counter_ftok_incremented && cnl->ftok_counter_halted) { /* Do similar cleanup for ftok like we did for access semaphore above */ SET_SOP_ARRAY_FOR_DECR_CNT(sop, sopcnt, (SEM_UNDO | IPC_NOWAIT)); SEMOP(udi->ftok_semid, sop, sopcnt, status, NO_WAIT); udi->counter_ftok_incremented = FALSE; assert(-1 != status); /* since we hold the access control lock, we do not expect any errors */ } if (csd->write_fullblk) { /* We have been asked to do FULL BLOCK WRITES for this database. On *NIX, attempt to get the filesystem * blocksize from statvfs. This allows a full write of a blockwithout the OS having to fetch the old * block for a read/update operation. We will round the IOs to the next filesystem blocksize if the * following criteria are met: * * 1) Database blocksize must be a whole multiple of the filesystem blocksize for the above * mentioned reason. * * 2) Filesystem blocksize must be a factor of the location of the first data block * given by the start_vbn. * * The saved length (if the feature is enabled) will be the filesystem blocksize and will be the * length that a database IO is rounded up to prior to initiation of the IO. */ fbwsize = get_fs_block_size(udi->fd); dblksize = csd->blk_size; if (0 == fbwsize || (0 != dblksize % fbwsize) || (0 != (BLK_ZERO_OFF(csd->start_vbn)) % fbwsize)) { if (!IS_STATSDB_REGNAME(reg)) { if (!fbwsize) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_DBFILNOFULLWRT, 5, LEN_AND_LIT("Could not get native filesize"), LEN_AND_LIT("File size extracted: "), fbwsize); else if (0 != dblksize % fbwsize) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_DBFILNOFULLWRT, 5, LEN_AND_LIT("Database block size not a multiple of file system block size\n"), LEN_AND_LIT("DB blocks size: "), dblksize); } csd->write_fullblk = 0; /* This region is not fullblockwrite enabled */ } /* Report this length in DSE even if not enabled */ csa->fullblockwrite_len = fbwsize; /* Length for rounding fullblockwrite */ } /* Note that the below value is normally incremented/decremented under control of the init/rundown semaphore in * "db_init" and "gds_rundown" but if QDBRUNDOWN is turned ON it could be manipulated without the semaphore in * both callers. Therefore use interlocked INCR_CNT/DECR_CNT. */ INCR_CNT(&cnl->ref_cnt, &cnl->wc_var_lock); assert(!csa->ref_cnt); /* Increment shared ref_cnt before private ref_cnt increment. */ csa->ref_cnt++; /* Currently journaling logic in gds_rundown() in VMS relies on this order to detect last writer */ if (WBTEST_ENABLED(WBTEST_HOLD_SEM_BYPASS) && !IS_GTM_IMAGE) { if (0 == cnl->wbox_test_seq_num) { cnl->wbox_test_seq_num = 1; DBGFPF((stderr, "Holding semaphores...\n")); while (1 == cnl->wbox_test_seq_num) LONG_SLEEP(1); } } if (!have_standalone_access && !jgbl.onlnrlbk && !bypassed_access) { /* Release control lockout now that it is init'd */ if (0 != (save_errno = do_semop(udi->semid, DB_CONTROL_SEM, -1, SEM_UNDO))) { save_errno = errno; RTS_ERROR(VARLSTCNT(12) ERR_CRITSEMFAIL, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, \ RTS_ERROR_LITERAL("semop()"), CALLFROM, save_errno); } udi->grabbed_access_sem = FALSE; } if (WBTEST_ENABLED(WBTEST_SEMTOOLONG_STACK_TRACE) && (1 == cnl->wbox_test_seq_num)) { cnl->wbox_test_seq_num = 2; /* Wait till the other process has got some stack traces */ while (cnl->wbox_test_seq_num != 3) LONG_SLEEP(1); } /* In case of REORG -ENCRYPT the ftok will be released after it has incremented cnl->reorg_encrypt_cycle. */ if (!have_standalone_access && !bypassed_ftok && !mu_reorg_encrypt_in_prog) { /* Release ftok semaphore lock so that any other ftok conflicted database can continue now */ if (!ftok_sem_release(reg, FALSE, FALSE)) RTS_ERROR(VARLSTCNT(4) ERR_DBFILERR, 2, DB_LEN_STR(reg)); FTOK_TRACE(csa, csd->trans_hist.curr_tn, ftok_ops_release, process_id); udi->grabbed_ftok_sem = FALSE; } if (udi->fd_opened_with_o_direct) { /* When we opened the database file we allocated an aligned buffer to hold SGMNT_HDR_LEN bytes. * Now that we have read the db file header (as part of READ_DB_FILE_HEADER above) check if the * database block size is bigger than SGMNT_HDR_LEN. If so, allocate more aligned space in the * global variable "dio_buff" as that will be later used to write either the file header or a GDS block. */ DIO_BUFF_EXPAND_IF_NEEDED(udi, csd->blk_size, &(TREF(dio_buff))); } REVERT; return 0; } fis-gtm-V7.0-005/sr_unix/gvcst_spr_data.c0000644000032200000250000001164514342376330017217 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_stdio.h" #include "gtmio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ #include "error.h" #include "io.h" #include "gvcst_protos.h" #include "change_reg.h" #include "op.h" #include "op_tcommit.h" #include "tp_frame.h" #include "tp_restart.h" #include "targ_alloc.h" #include "stack_frame.h" #include "gtmimagename.h" #include "gvt_inline.h" LITREF mval literal_batch; GBLREF gv_key *gv_currkey; GBLREF gd_region *gv_cur_region; GBLREF uint4 dollar_tlevel; DEFINE_NSB_CONDITION_HANDLER(gvcst_spr_data_ch) mint gvcst_spr_data(void) { boolean_t spr_tpwrapped; boolean_t est_first_pass; mint val, cumul_val; int reg_index; gd_binding *start_map, *end_map, *map; gd_region *reg, *gd_reg_start; gd_addr *addr; gv_namehead *start_map_gvt; gvnh_reg_t *gvnh_reg; trans_num gd_targ_tn, *tn_array; # ifdef DEBUG int save_dollar_tlevel; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; start_map = TREF(gd_targ_map); /* set up by op_gvname/op_gvnaked/op_gvextnam done just before invoking op_gvdata */ start_map_gvt = gv_target; /* save gv_target corresponding to start_map so we can restore at end */ /* Find out if the next (in terms of $order) key maps to same map as currkey. If so, no spanning activity needed */ GVKEY_INCREMENT_ORDER(gv_currkey); end_map = gv_srch_map_linear(start_map, (char *)&gv_currkey->base[0], gv_currkey->end - 1); BACK_OFF_ONE_MAP_ENTRY_IF_EDGECASE(gv_currkey->base, gv_currkey->end - 1, end_map); GVKEY_UNDO_INCREMENT_ORDER(gv_currkey); val = 0; if (start_map == end_map) { assert(gv_target == start_map_gvt); if (start_map_gvt->root) val = gvcst_data(); return val; } /* Do any initialization that is independent of retries BEFORE the op_tstart */ addr = TREF(gd_targ_addr); assert(NULL != addr); gd_reg_start = &addr->regions[0]; tn_array = TREF(gd_targ_reg_array); gvnh_reg = TREF(gd_targ_gvnh_reg); assert(NULL != gvnh_reg); assert(NULL != gvnh_reg->gvspan); /* Now that we know the keyrange maps to more than one region, go through each of them and do the $data. * Since multiple regions are potentially involved, need a TP fence. */ DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); if (!dollar_tlevel) { spr_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0); ESTABLISH_NORET(gvcst_spr_data_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); } else spr_tpwrapped = FALSE; assert(gv_cur_region == start_map->reg.addr); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); /* Do any initialization that is dependent on retries AFTER the op_tstart */ map = start_map; cumul_val = 0; INCREMENT_GD_TARG_TN(gd_targ_tn); /* takes a copy of incremented "TREF(gd_targ_tn)" into local variable "gd_targ_tn" */ /* Verify that initializations that happened before op_tstart are still unchanged */ assert(addr == TREF(gd_targ_addr)); assert(tn_array == TREF(gd_targ_reg_array)); assert(gvnh_reg == TREF(gd_targ_gvnh_reg)); for ( ; map <= end_map; map++) { ASSERT_BASEREG_OPEN_IF_STATSREG(map); /* "gv_srch_map_linear" call above should have ensured that */ reg = map->reg.addr; GET_REG_INDEX(addr, gd_reg_start, reg, reg_index); /* sets "reg_index" */ assert((map != start_map) || (tn_array[reg_index] != gd_targ_tn)); assert(TREF(gd_targ_reg_array_size) > reg_index); if (tn_array[reg_index] == gd_targ_tn) continue; if (map != start_map) GV_BIND_SUBSREG(addr, reg, gvnh_reg); /* sets gv_target/gv_cur_region/cs_addrs */ assert(reg->open); if (gv_target->root) { val = gvcst_data(); cumul_val = cumul_val | val; if (cumul_val >= 10) break; /* we cannot get better than 10 or 11 so stop going through regions once we get there */ } tn_array[reg_index] = gd_targ_tn; } if (gv_target != start_map_gvt) { /* Restore gv_cur_region/gv_target etc. */ gv_target = start_map_gvt; gv_cur_region = start_map->reg.addr; change_reg(); } DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); if (spr_tpwrapped) { op_tcommit(); REVERT; /* remove our condition handler */ } assert(save_dollar_tlevel == dollar_tlevel); return cumul_val; } fis-gtm-V7.0-005/sr_unix/gvcst_spr_order.c0000644000032200000250000002624314342376330017421 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_stdio.h" #include "gtmio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ #include "io.h" #include "gvcst_protos.h" #include "change_reg.h" #include "op.h" #include "op_tcommit.h" #include "tp_frame.h" #include "tp_restart.h" #include "targ_alloc.h" #include "error.h" #include "stack_frame.h" #include "gtmimagename.h" #include "gvt_inline.h" LITREF mval literal_batch; GBLREF gv_key *gv_currkey, *gv_altkey; GBLREF gd_region *gv_cur_region; GBLREF uint4 dollar_tlevel; DEFINE_NSB_CONDITION_HANDLER(gvcst_spr_order_ch) boolean_t gvcst_spr_order(void) { boolean_t spr_tpwrapped; boolean_t est_first_pass; boolean_t found, result_found; boolean_t do_atorder_search; boolean_t currkey_orig_saved = FALSE; int reg_index; int prev; gd_binding *start_map, *first_map, *stop_map, *end_map, *map, *rmap; gd_region *reg, *gd_reg_start; gd_addr *addr; gv_namehead *start_map_gvt; gv_key *matchkey = NULL; gv_key_buf currkey_save_buf = { .key.top = DBKEYSIZE(MAX_KEY_SZ), .key.end = 0, .key.prev = 0 }; gv_key_buf currkey_orig_buf = { .key.top = DBKEYSIZE(MAX_KEY_SZ), .key.end = 0, .key.prev = 0 }; gv_key_buf altkey_save_buf = { .key.top = DBKEYSIZE(MAX_KEY_SZ), .key.end = 0, .key.prev = 0 }; gvnh_reg_t *gvnh_reg; srch_blk_status *bh; trans_num gd_targ_tn, *tn_array; # ifdef DEBUG int save_dollar_tlevel; gd_binding *prev_end_map; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Ensure gv_cur_region/gv_target/cs_addrs at function return are identical to values at function entry * (this is not currently required now that op_gvname fast-path optimization has been removed in GTM-2168 * but might be better to hold onto just in case that optimization is resurrected for non-spanning-globals or so) */ start_map = TREF(gd_targ_map); /* set up by op_gvname/op_gvnaked/op_gvextnam done just before invoking op_gvorder */ start_map_gvt = gv_target; /* gv_currkey has gone through a GVKEY_INCREMENT_ORDER in op_gvorder. Recompute start_map just in case this happens * to skip a few more regions than the current value of "start_map" (which was computed at op_gvname time). */ map = gv_srch_map_linear(start_map, (char *)&gv_currkey->base[0], gv_currkey->end - 1); addr = TREF(gd_targ_addr); assert(NULL != addr); gvnh_reg = TREF(gd_targ_gvnh_reg); assert(NULL != gvnh_reg); assert(NULL != gvnh_reg->gvspan); first_map = map; if ((map != start_map) || (gv_target->gd_csa != cs_addrs) || (gv_cur_region != start_map->reg.addr)) { /* set global variables to point to region corresponding to "map" */ reg = map->reg.addr; GV_BIND_SUBSREG(addr, reg, gvnh_reg); /* sets gv_target/gv_cur_region/cs_addrs to new first_map */ } /* Find out if the next (in terms of $order) key at the PREVIOUS subscript level maps to same map as currkey. * If so, no spanning activity needed. */ GVKEY_INCREMENT_PREVSUBS_ORDER(gv_currkey); prev = gv_currkey->prev; stop_map = gv_srch_map_linear(first_map, (char *)&gv_currkey->base[0], prev); BACK_OFF_ONE_MAP_ENTRY_IF_EDGECASE(gv_currkey->base, gv_currkey->prev, stop_map); GVKEY_UNDO_INCREMENT_PREVSUBS_ORDER(gv_currkey); found = FALSE; if (first_map == stop_map) { /* At this point, gv_target could be different from start_map_gvt. * Hence cannot use latter like is used in other gvcst_spr_* modules. */ if (gv_target->root) found = gvcst_order(); if (gv_target != start_map_gvt) { /* Restore gv_cur_region/gv_target etc. */ gv_target = start_map_gvt; gv_cur_region = start_map->reg.addr; change_reg(); } return found; } /* Do any initialization that is independent of retries BEFORE the op_tstart */ gd_reg_start = &addr->regions[0]; tn_array = TREF(gd_targ_reg_array); /* Now that we know the keyrange maps to more than one region, go through each of them and do the $order * Since multiple regions are potentially involved, need a TP fence. */ DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); if (!dollar_tlevel) { spr_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0); ESTABLISH_NORET(gvcst_spr_order_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); } else spr_tpwrapped = FALSE; assert(gv_cur_region == first_map->reg.addr); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); /* Do any initialization that is dependent on retries AFTER the op_tstart */ map = first_map; end_map = stop_map; result_found = FALSE; do_atorder_search = FALSE; INCREMENT_GD_TARG_TN(gd_targ_tn); /* takes a copy of incremented "TREF(gd_targ_tn)" into local variable "gd_targ_tn" */ /* Verify that initializations that happened before op_tstart are still unchanged */ assert(addr == TREF(gd_targ_addr)); assert(tn_array == TREF(gd_targ_reg_array)); assert(gvnh_reg == TREF(gd_targ_gvnh_reg)); assert(KEY_DELIMITER == gv_currkey->base[gv_currkey->end]); for ( ; map <= end_map; map++) { /* Scan through each map in the gld and do gvcst_order calls in each map's region. * Note down the smallest key found across the scanned regions until we find a key that belongs to the * same map (in the gld) as the currently scanned "map". At which point, the region-spanning order is done. */ ASSERT_BASEREG_OPEN_IF_STATSREG(map); /* "gv_srch_map_linear" call above should have ensured that */ reg = map->reg.addr; GET_REG_INDEX(addr, gd_reg_start, reg, reg_index); /* sets "reg_index" */ assert(TREF(gd_targ_reg_array_size) > reg_index); if (map != first_map) GV_BIND_SUBSREG(addr, reg, gvnh_reg); /* sets gv_target/gv_cur_region/cs_addrs */ assert(reg->open); if (gv_target->root) { matchkey = gv_altkey; found = gvcst_order(); assert(!found || !memcmp(&gv_altkey->base[0], &gv_currkey->base[0], prev)); if (do_atorder_search) { bh = gv_target->hist.h; if (gv_currkey->end <= bh->curr_rec.match) { /* The history indicates that the gvcst_search() performed by gvcst_order() actually found * the key marking the end of a previous map, so try that first. */ matchkey = gv_currkey; found = TRUE; } } if (found) { /* For accurate results, we need to see if the node found by gvcst_order() has data or that its * first data child lies in the same map. Otherwise, the node should be excluded. * To do this, take the proposed key and do a gvcst_query() on it. * If the resulting key matches the request and is in the current map, use it to derive the result. * Otherwise, put things back to where they were after the search and continue. */ if (!currkey_orig_saved) { COPY_KEY(&currkey_orig_buf.key, gv_currkey); currkey_orig_saved = TRUE; } COPY_KEY(&currkey_save_buf.key, gv_currkey); if (matchkey == gv_altkey) { COPY_KEY(gv_currkey, gv_altkey); gv_currkey->base[gv_currkey->end] = KEY_DELIMITER; } else { assert(matchkey == gv_currkey); COPY_KEY(&altkey_save_buf.key, gv_altkey); altkey_save_buf.split.base[altkey_save_buf.key.end] = KEY_DELIMITER; /* The key came from the prior map's endpoint, and we need the query to skip * anything prior, so search from the full prior map's endpoint. */ assert(map > first_map); assert(0 == memcmp(gv_currkey->base, map[-1].gvkey.addr, gv_currkey->end)); memcpy(gv_currkey->base, map[-1].gvkey.addr, map[-1].gvkey_len); gv_currkey->end = map[-1].gvkey_len; gv_currkey->base[gv_currkey->end] = KEY_DELIMITER; } do { found = gvcst_query2(); /* The query result is only useful if it shares the same base as the currkey * and has subscripts beyond that. */ if (found) { assert(gv_altkey->end >= prev); assert(0 == memcmp(gv_currkey->base, gv_altkey->base, prev)); rmap = gv_srch_map_linear(first_map, (char *)gv_altkey->base, gv_altkey->end - 1); if (map == rmap) { result_found = TRUE; /* Use altkey from query, but only up to the subscript we care about. */ for (gv_altkey->end = prev; gv_altkey->base[gv_altkey->end]; gv_altkey->end++) ; gv_altkey->base[++gv_altkey->end] = KEY_DELIMITER; /* gv_currkey will be restored from currkey_orig on the way out, * so no need to restore it here. */ matchkey = gv_altkey; break; } } if (matchkey == gv_altkey) { /* The saved gv_altkey is a miss, so don't bother restoring it. */ COPY_KEY(gv_currkey, &currkey_save_buf.key); matchkey = NULL; } else { /* The currkey check failed, so try the altkey. */ COPY_KEY(gv_currkey, &altkey_save_buf.key); assert(KEY_DELIMITER == gv_currkey->base[gv_currkey->end]); matchkey = gv_altkey; } } while (matchkey); if (matchkey) { /* Only non-NULL if we did a break out of the above loop. */ break; } } } tn_array[reg_index] = gd_targ_tn; if (map != end_map) { /* Since we know that any subsequent match would lie beyond the current map, replace the last subscript * in the search with the corresponding one from the map endpoint. * This keeps us from getting false hits which would have been associated with earlier maps. */ assert(!memcmp(map->gvkey.addr, &gv_currkey->base[0], prev)); assert(0 <= memcmp(map->gvkey.addr + prev, gv_currkey->base + prev, gv_currkey->end - prev)); if (!currkey_orig_saved) { COPY_KEY(&currkey_orig_buf.key, gv_currkey); currkey_orig_saved = TRUE; } for (gv_currkey->end = prev; map->gvkey.addr[gv_currkey->end]; gv_currkey->end++) { gv_currkey->base[gv_currkey->end] = map->gvkey.addr[gv_currkey->end]; } gv_currkey->base[gv_currkey->end++] = KEY_DELIMITER; gv_currkey->base[gv_currkey->end] = KEY_DELIMITER; do_atorder_search = TRUE; } else do_atorder_search = FALSE; } if (currkey_orig_saved || (gv_target != start_map_gvt)) { /* Restore gv_cur_region/gv_target etc. */ gv_target = start_map_gvt; gv_cur_region = start_map->reg.addr; change_reg(); } assert(gv_cur_region == start_map->reg.addr); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); if (spr_tpwrapped) { op_tcommit(); REVERT; /* remove our condition handler */ } assert(save_dollar_tlevel == dollar_tlevel); if (result_found) { /* Restore accumulated minimal key into gv_altkey */ assert(matchkey); if (gv_altkey != matchkey) COPY_KEY(gv_altkey, matchkey); } if (currkey_orig_saved) COPY_KEY(gv_currkey, &currkey_orig_buf.key); return result_found; } fis-gtm-V7.0-005/sr_unix/gvcst_spr_query.c0000644000032200000250000001576414342376330017461 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_stdio.h" #include "gtmio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ #include "io.h" #include "gvcst_protos.h" #include "change_reg.h" #include "op.h" #include "op_tcommit.h" #include "tp_frame.h" #include "tp_restart.h" #include "targ_alloc.h" #include "error.h" #include "stack_frame.h" #include "gtmimagename.h" #include "gvt_inline.h" LITREF mval literal_batch; GBLREF gv_key *gv_currkey, *gv_altkey; GBLREF gd_region *gv_cur_region; GBLREF uint4 dollar_tlevel; DEFINE_NSB_CONDITION_HANDLER(gvcst_spr_query_ch) boolean_t gvcst_spr_query(void) { boolean_t spr_tpwrapped; boolean_t est_first_pass; boolean_t found, result_found; boolean_t do_atorder_search = FALSE; boolean_t currkey_saved = FALSE; int reg_index; gd_binding *start_map, *end_map, *map, *prev_end_map, *stop_map, *rmap; gd_region *reg, *gd_reg_start; gd_addr *addr; gv_namehead *start_map_gvt; gv_key *matchkey = NULL; gv_key_buf currkey_save_buf = { .key.top = DBKEYSIZE(MAX_KEY_SZ), .key.end = 0, .key.prev = 0 }; gvnh_reg_t *gvnh_reg; mname_entry gvname; srch_blk_status *bh; trans_num gd_targ_tn, *tn_array; # ifdef DEBUG int save_dollar_tlevel; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; start_map = TREF(gd_targ_map); /* set up by op_gvname/op_gvnaked/op_gvextnam done just before invoking op_gvquery */ start_map_gvt = gv_target; /* save gv_target corresponding to start_map so we can restore at end */ /* Do any initialization that is independent of retries BEFORE the op_tstart */ addr = TREF(gd_targ_addr); assert(NULL != addr); gd_reg_start = &addr->regions[0]; tn_array = TREF(gd_targ_reg_array); gvnh_reg = TREF(gd_targ_gvnh_reg); assert(NULL != gvnh_reg); assert(NULL != gvnh_reg->gvspan); /* Now that we know the keyrange maps to more than one region, go through each of them and do the $query * Since multiple regions are potentially involved, need a TP fence. But before that, open any statsDBs pointed * to by map entries from "start_map" to "stop_map" (as their open will be deferred once we go into TP). Not * opening them before the TP can produce incomplete results from the $query operation. */ assert(0 < gvnh_reg->gvspan->end_map_index); assert(gvnh_reg->gvspan->end_map_index < addr->n_maps); stop_map = &addr->maps[gvnh_reg->gvspan->end_map_index]; for (map = start_map; map <= stop_map; map++) OPEN_BASEREG_IF_STATSREG(map); DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); if (!dollar_tlevel) { spr_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0); ESTABLISH_NORET(gvcst_spr_query_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); } else spr_tpwrapped = FALSE; assert(gv_cur_region == start_map->reg.addr); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); /* Do any initialization that is dependent on retries AFTER the op_tstart */ map = start_map; result_found = FALSE; do_atorder_search = FALSE; INCREMENT_GD_TARG_TN(gd_targ_tn); /* takes a copy of incremented "TREF(gd_targ_tn)" into local variable "gd_targ_tn" */ end_map = stop_map; /* Verify that initializations that happened before op_tstart are still unchanged */ assert(addr == TREF(gd_targ_addr)); assert(tn_array == TREF(gd_targ_reg_array)); assert(gvnh_reg == TREF(gd_targ_gvnh_reg)); for ( ; map <= end_map; map++) { /* Scan through each map in the gld and do gvcst_query calls in each map's region. * Note down the smallest key found across the scanned regions until we find a key that belongs to the * same map (in the gld) as the currently scanned "map". At which point, the region-spanning query is done. */ ASSERT_BASEREG_OPEN_IF_STATSREG(map); /* "OPEN_BASEREG_IF_STATSREG" call above should have ensured that */ reg = map->reg.addr; GET_REG_INDEX(addr, gd_reg_start, reg, reg_index); /* sets "reg_index" */ assert((map != start_map) || (tn_array[reg_index] != gd_targ_tn)); assert(TREF(gd_targ_reg_array_size) > reg_index); if ((map != start_map) || (gv_target->gd_csa != cs_addrs) || (gv_cur_region != start_map->reg.addr)) GV_BIND_SUBSREG(addr, reg, gvnh_reg); /* sets gv_target/gv_cur_region/cs_addrs */ assert(reg->open); if (gv_target->root) { matchkey = gv_altkey; found = gvcst_query(); if (do_atorder_search) { bh = gv_target->hist.h; if ((gv_currkey->end <= bh->curr_rec.match) && (1 == (gvcst_data2() % 10))) { /* The history indicates that the gvcst_search() performed by gvcst_query() actually found * the key marking the end of a previous map, so use that. */ matchkey = gv_currkey; found = TRUE; } } if (found) { rmap = gv_srch_map_linear(map, (char *)&matchkey->base[0], matchkey->end - 1); if (map == rmap) { assert(matchkey->end); assert(KEY_DELIMITER == matchkey->base[matchkey->end]); result_found = TRUE; break; } } } tn_array[reg_index] = gd_targ_tn; /* Since we know that any subsequent match would lie beyond the current map, replace the last subscript * in the search with the corresponding one from the map endpoint. * This keeps us from getting false hits which would have been associated with earlier maps. */ if (map != end_map) { if (!currkey_saved) { COPY_KEY(&currkey_save_buf.key, gv_currkey); currkey_saved = TRUE; } assert(!strcmp((char *)gv_currkey->base, map->gvkey.addr)); memcpy(gv_currkey->base, map->gvkey.addr, map->gvkey_len); gv_currkey->end = map->gvkey_len; gv_currkey->base[gv_currkey->end] = KEY_DELIMITER; gv_currkey->base[gv_currkey->end + 1] = KEY_DELIMITER; do_atorder_search = TRUE; } } if (result_found) { assert(matchkey); if (gv_altkey != matchkey) COPY_KEY(gv_altkey, matchkey); } if (currkey_saved) COPY_KEY(gv_currkey, &currkey_save_buf.key); if (currkey_saved || (gv_target != start_map_gvt)) { /* Restore gv_cur_region/gv_target etc. */ gv_target = start_map_gvt; gv_cur_region = start_map->reg.addr; change_reg(); } assert(gv_cur_region == start_map->reg.addr); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); if (spr_tpwrapped) { op_tcommit(); REVERT; /* remove our condition handler */ } assert(save_dollar_tlevel == dollar_tlevel); return result_found; } fis-gtm-V7.0-005/sr_unix/gvcst_spr_queryget.c0000644000032200000250000001575114342376330020155 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_stdio.h" #include "gtmio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ #include "mv_stent.h" #include "io.h" #include "gvcst_protos.h" #include "change_reg.h" #include "op.h" #include "op_tcommit.h" #include "tp_frame.h" #include "tp_restart.h" #include "targ_alloc.h" #include "gtmimagename.h" #include "gvt_inline.h" LITREF mval literal_batch; GBLREF gv_key *gv_currkey, *gv_altkey; GBLREF gd_region *gv_cur_region; GBLREF uint4 dollar_tlevel; DEFINE_NSB_CONDITION_HANDLER(gvcst_spr_queryget_ch) boolean_t gvcst_spr_queryget(mval *result_val) { boolean_t spr_tpwrapped; boolean_t est_first_pass; boolean_t found, result_found; boolean_t currkey_saved = FALSE; int reg_index; gd_binding *start_map, *end_map, *map, *prev_end_map, *stop_map, *rmap; gd_region *reg, *gd_reg_start; gd_addr *addr; gv_namehead *start_map_gvt; gv_key_buf currkey_save_buf = { .key.top = DBKEYSIZE(MAX_KEY_SZ), .key.end = 0, .key.prev = 0 }; gvnh_reg_t *gvnh_reg; mname_entry gvname; mval *val; srch_blk_status *bh; trans_num gd_targ_tn, *tn_array; # ifdef DEBUG int save_dollar_tlevel; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; start_map = TREF(gd_targ_map); /* set up by op_gvname/op_gvnaked/op_gvextnam done just before invoking op_gvqueryget */ start_map_gvt = gv_target; /* save gv_target corresponding to start_map so we can restore at end */ /* Do any initialization that is independent of retries BEFORE the op_tstart */ PUSH_MV_STENT(MVST_MVAL); /* Need to protect value returned by gvcst_queryget from stpgcol */ /* "val" protection might not be necessary specifically for the non-spanning-node gvcst_queryget version. * And most likely not needed for the spanning-node gvcst_queryget version. But it is not easy to be sure and * besides it does not hurt that much since only a M-stack entry gets pushed. So we err on the side of caution. */ val = &mv_chain->mv_st_cont.mvs_mval; val->mvtype = 0; /* initialize mval in M-stack in case stp_gcol gets called before mkey gets initialized below */ addr = TREF(gd_targ_addr); assert(NULL != addr); gd_reg_start = &addr->regions[0]; tn_array = TREF(gd_targ_reg_array); gvnh_reg = TREF(gd_targ_gvnh_reg); assert(NULL != gvnh_reg); assert(NULL != gvnh_reg->gvspan); /* Now that we know the keyrange maps to more than one region, go through each of them and do the $queryget * Since multiple regions are potentially involved, need a TP fence. But before that, open any statsDBs pointed * to by map entries from "start_map" to "stop_map" (as their open will be deferred once we go into TP). Not * opening them before the TP can produce incomplete results from the $query operation. */ assert(0 < gvnh_reg->gvspan->end_map_index); assert(gvnh_reg->gvspan->end_map_index < addr->n_maps); stop_map = &addr->maps[gvnh_reg->gvspan->end_map_index]; for (map = start_map; map <= stop_map; map++) OPEN_BASEREG_IF_STATSREG(map); DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); if (!dollar_tlevel) { spr_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0); ESTABLISH_NORET(gvcst_spr_queryget_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); } else spr_tpwrapped = FALSE; assert(gv_cur_region == start_map->reg.addr); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); /* Do any initialization that is dependent on retries AFTER the op_tstart */ map = start_map; result_found = FALSE; INCREMENT_GD_TARG_TN(gd_targ_tn); /* takes a copy of incremented "TREF(gd_targ_tn)" into local variable "gd_targ_tn" */ end_map = stop_map; /* Verify that initializations that happened before op_tstart are still unchanged */ assert(addr == TREF(gd_targ_addr)); assert(tn_array == TREF(gd_targ_reg_array)); assert(gvnh_reg == TREF(gd_targ_gvnh_reg)); for ( ; map <= end_map; map++) { /* Scan through each map in the gld and do gvcst_queryget calls in each map's region. * Note down the smallest key found across the scanned regions until we find a key that belongs to the * same map (in the gld) as the currently scanned "map". At which point, the region-spanning queryget is done. */ ASSERT_BASEREG_OPEN_IF_STATSREG(map); /* "OPEN_BASEREG_IF_STATSREG" call above should have ensured that */ reg = map->reg.addr; GET_REG_INDEX(addr, gd_reg_start, reg, reg_index); /* sets "reg_index" */ assert((map != start_map) || (tn_array[reg_index] != gd_targ_tn)); assert(TREF(gd_targ_reg_array_size) > reg_index); if ((map != start_map) || (gv_target->gd_csa != cs_addrs) || (gv_cur_region != start_map->reg.addr)) GV_BIND_SUBSREG(addr, reg, gvnh_reg); /* sets gv_target/gv_cur_region/cs_addrs */ assert(reg->open); if (gv_target->root) { found = gvcst_queryget(val); if (found) { rmap = gv_srch_map_linear(map, (char *)&gv_altkey->base[0], gv_altkey->end - 1); if (map == rmap) { assert(gv_altkey->end); assert(KEY_DELIMITER == gv_altkey->base[gv_altkey->end]); result_found = TRUE; *result_val = *val; break; } } } tn_array[reg_index] = gd_targ_tn; /* Since we know that any subsequent match would lie beyond the current map, replace the last subscript * in the search with the corresponding one from the map endpoint. * This keeps us from getting false hits which would have been associated with earlier maps. */ if (map != end_map) { if (!currkey_saved) { COPY_KEY(&currkey_save_buf.key, gv_currkey); currkey_saved = TRUE; } memcpy(gv_currkey->base, map->gvkey.addr, map->gvkey_len); gv_currkey->end = map->gvkey_len; gv_currkey->base[gv_currkey->end] = KEY_DELIMITER; gv_currkey->base[gv_currkey->end + 1] = KEY_DELIMITER; } } if (currkey_saved) COPY_KEY(gv_currkey, &currkey_save_buf.key); if (currkey_saved || (gv_target != start_map_gvt)) { /* Restore gv_cur_region/gv_target etc. */ gv_target = start_map_gvt; gv_cur_region = start_map->reg.addr; change_reg(); } assert(gv_cur_region == start_map->reg.addr); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); if (spr_tpwrapped) { op_tcommit(); REVERT; /* remove our condition handler */ } POP_MV_STENT(); /* "val" */ assert(save_dollar_tlevel == dollar_tlevel); return result_found; } fis-gtm-V7.0-005/sr_unix/gvcst_spr_zprevious.c0000644000032200000250000002662214342376330020355 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_stdio.h" #include "gtmio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ #include "io.h" #include "gvcst_protos.h" #include "change_reg.h" #include "op.h" #include "op_tcommit.h" #include "tp_frame.h" #include "tp_restart.h" #include "targ_alloc.h" #include "error.h" #include "stack_frame.h" #include "gtmimagename.h" #include "gvt_inline.h" LITREF mval literal_batch; GBLREF gv_key *gv_currkey, *gv_altkey; GBLREF gd_region *gv_cur_region; GBLREF uint4 dollar_tlevel; DEFINE_NSB_CONDITION_HANDLER(gvcst_spr_zprevious_ch) boolean_t gvcst_spr_zprevious(void) { boolean_t spr_tpwrapped; boolean_t est_first_pass; boolean_t found, result_found; boolean_t do_atorder_search; boolean_t currkey_orig_saved = FALSE; char savech; int reg_index; int prev; gd_binding *start_map, *first_map, *stop_map, *end_map, *map, *prev_end_map, *rmap; gd_region *reg, *gd_reg_start; gd_addr *addr; gv_namehead *start_map_gvt; gv_key *matchkey = NULL; gv_key_buf currkey_save_buf = { .key.top = DBKEYSIZE(MAX_KEY_SZ), .key.end = 0, .key.prev = 0 }; gv_key_buf currkey_orig_buf = { .key.top = DBKEYSIZE(MAX_KEY_SZ), .key.end = 0, .key.prev = 0 }; gv_key_buf altkey_save_buf = { .key.top = DBKEYSIZE(MAX_KEY_SZ), .key.end = 0, .key.prev = 0 }; gvnh_reg_t *gvnh_reg; srch_blk_status *bh; trans_num gd_targ_tn, *tn_array; # ifdef DEBUG int save_dollar_tlevel; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Ensure gv_cur_region/gv_target/cs_addrs at function return are identical to values at function entry * (this is not currently required now that op_gvname fast-path optimization has been removed in GTM-2168 * but might be better to hold onto just in case that optimization is resurrected for non-spanning-globals or so) */ start_map = TREF(gd_targ_map); /* set up by op_gvname/op_gvnaked/op_gvextnam done just before invoking op_zprevious */ start_map_gvt = gv_target; addr = TREF(gd_targ_addr); assert(NULL != addr); gvnh_reg = TREF(gd_targ_gvnh_reg); assert(NULL != gvnh_reg); assert(NULL != gvnh_reg->gvspan); if (TREF(gv_last_subsc_null)) { /* last subscript is "". gv_currkey would have been recomputed in op_zprevious to have a 0xFF byte sequence * reflecting the maximum possible subscript value. Since this global spans multiple regions, recompute * the map corresponding to this gv_currkey. */ first_map = gv_srch_map_linear(start_map, (char *)&gv_currkey->base[0], gv_currkey->end - 1); } else { first_map = start_map; BACK_OFF_ONE_MAP_ENTRY_IF_EDGECASE(gv_currkey->base, gv_currkey->end - 1, first_map); if ((start_map != first_map) || (gv_target->gd_csa != cs_addrs) || (gv_cur_region != start_map->reg.addr)) { /* set global variables to point to new first_map region */ reg = first_map->reg.addr; GV_BIND_SUBSREG(addr, reg, gvnh_reg); /* sets gv_target/gv_cur_region/cs_addrs to new first_map */ } } /* Check if the previous key at SAME subscript level maps to same map as currkey. If so, no spanning activity needed */ GVKEY_SET_SUBS_ZPREVIOUS(gv_currkey, savech); prev = gv_currkey->prev; stop_map = gv_srch_map_linear_backward(first_map, (char *)&gv_currkey->base[0], prev + 1); assert(stop_map <= first_map); GVKEY_UNDO_SET_SUBS_ZPREVIOUS(gv_currkey, savech); found = FALSE; if (first_map == stop_map) { /* At this point, gv_target could be different from start_map_gvt. * Hence cannot use latter like is used in other gvcst_spr_* modules. */ if (gv_target->root) found = gvcst_zprevious(); if (gv_target != start_map_gvt) { /* Restore gv_cur_region/gv_target etc. */ gv_target = start_map_gvt; gv_cur_region = start_map->reg.addr; change_reg(); } return found; } /* Do any initialization that is independent of retries BEFORE the op_tstart */ gd_reg_start = &addr->regions[0]; tn_array = TREF(gd_targ_reg_array); /* Now that we know the keyrange maps to more than one region, go through each of them and do the $zprevious * Since multiple regions are potentially involved, need a TP fence. */ DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); if (!dollar_tlevel) { spr_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0); ESTABLISH_NORET(gvcst_spr_zprevious_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); } else spr_tpwrapped = FALSE; assert((first_map != start_map) || (gv_cur_region == first_map->reg.addr)); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); /* Do any initialization that is dependent on retries AFTER the op_tstart */ map = first_map; end_map = stop_map; result_found = FALSE; do_atorder_search = FALSE; INCREMENT_GD_TARG_TN(gd_targ_tn); /* takes a copy of incremented "TREF(gd_targ_tn)" into local variable "gd_targ_tn" */ /* Verify that initializations that happened before op_tstart are still unchanged */ assert(addr == TREF(gd_targ_addr)); assert(tn_array == TREF(gd_targ_reg_array)); assert(gvnh_reg == TREF(gd_targ_gvnh_reg)); for ( ; map >= end_map; map--) { /* Backward scan through each map in the gld and do gvcst_zprevious calls in each map's region. * Note down the largest key found across the scanned regions until we find a key that belongs to the * same map (in the gld) as the currently scanned "map". At which point, the region-spanning zprevious is done. */ ASSERT_BASEREG_OPEN_IF_STATSREG(map); /* "gv_srch_map_linear" and "gv_srch_map_linear_backward" call above * should have ensured that. */ reg = map->reg.addr; GET_REG_INDEX(addr, gd_reg_start, reg, reg_index); /* sets "reg_index" */ assert(TREF(gd_targ_reg_array_size) > reg_index); GV_BIND_SUBSREG(addr, reg, gvnh_reg); /* sets gv_target/gv_cur_region/cs_addrs */ assert(reg->open); if (gv_target->root) { matchkey = gv_altkey; found = gvcst_zprevious(); assert(!found || !memcmp(&gv_altkey->base[0], &gv_currkey->base[0], prev)); if (do_atorder_search) { bh = gv_target->hist.h; if (gv_currkey->end <= bh->curr_rec.match) { /* The history indicates that the gvcst_search() performed by gvcst_order() actually found * the key marking the end of a previous map, so use that. */ matchkey = gv_currkey; found = TRUE; } } if (found) { /* For accurate results, we need to see if the node found by gvcst_zprevious() has data or that its * first data child lies in the same map. Otherwise, the node should be excluded. * To do this, take the proposed key and do a gvcst_query() on it. * If the resulting key matches the request and is in the current map, use it to derive the result. * Otherwise, put things back to where they were after the search and continue. */ if (!currkey_orig_saved) { COPY_KEY(&currkey_orig_buf.key, gv_currkey); currkey_orig_saved = TRUE; } COPY_KEY(&currkey_save_buf.key, gv_currkey); if (matchkey == gv_altkey) { COPY_KEY(gv_currkey, gv_altkey); gv_currkey->base[gv_currkey->end] = KEY_DELIMITER; } else { assert(matchkey == gv_currkey); COPY_KEY(&altkey_save_buf.key, gv_altkey); altkey_save_buf.split.base[altkey_save_buf.key.end] = KEY_DELIMITER; if ((map[-1].gvkey_len > gv_currkey->end) && (0 == memcmp(gv_currkey->base, map[-1].gvkey.addr, gv_currkey->end))) { /* The prior map endpoint has the same subscript as the one we are looking for, * so do query search from there in order to avoid incorrectly matching earlier * next level subscripts. */ memcpy(gv_currkey->base, map[-1].gvkey.addr, map[-1].gvkey_len); gv_currkey->end = map[-1].gvkey_len; gv_currkey->base[gv_currkey->end] = KEY_DELIMITER; } } do { found = gvcst_query2(); /* The query result is only useful if it shares the same base as the currkey * and has subscripts beyond that. */ if (found) { assert(gv_altkey->end >= prev); assert(0 == memcmp(gv_currkey->base, gv_altkey->base, prev)); rmap = gv_srch_map_linear_backward(map, (char *)gv_altkey->base, gv_altkey->end - 1); if (map == rmap) { result_found = TRUE; /* Use altkey from query, but only up to the subscript we care about. */ for (gv_altkey->end = prev; gv_altkey->base[gv_altkey->end]; gv_altkey->end++) ; gv_altkey->base[++gv_altkey->end] = KEY_DELIMITER; /* gv_currkey will be restored from currkey_orig on the way out, * so no need to restore it here. */ matchkey = gv_altkey; break; } } if (matchkey == gv_altkey) { /* The saved gv_altkey is a miss, so don't bother restoring it. */ COPY_KEY(gv_currkey, &currkey_save_buf.key); matchkey = NULL; } else { /* The currkey check failed, so try the altkey. */ COPY_KEY(gv_currkey, &altkey_save_buf.key); assert(KEY_DELIMITER == gv_currkey->base[gv_currkey->end]); matchkey = gv_altkey; } } while (matchkey); if (matchkey) { /* Only non-NULL if we did a break out of the above loop. */ break; } } } if (map != end_map) { /* Since we know that any subsequent match would lie before the current map, * replace the last subscript in the search with the corresponding one from * the previous map endpoint. * This keeps us from getting false hits which would have been associated with earlier maps. */ assert(!memcmp(map[-1].gvkey.addr, &gv_currkey->base[0], prev)); assert(0 >= memcmp(map[-1].gvkey.addr + prev, gv_currkey->base + prev, gv_currkey->end - prev)); if (!currkey_orig_saved) { COPY_KEY(&currkey_orig_buf.key, gv_currkey); currkey_orig_saved = TRUE; } for (gv_currkey->end = prev; map[-1].gvkey.addr[gv_currkey->end]; gv_currkey->end++) { gv_currkey->base[gv_currkey->end] = map[-1].gvkey.addr[gv_currkey->end]; } gv_currkey->base[gv_currkey->end++] = KEY_DELIMITER; gv_currkey->base[gv_currkey->end] = KEY_DELIMITER; do_atorder_search = TRUE; } else do_atorder_search = FALSE; tn_array[reg_index] = gd_targ_tn; } if (currkey_orig_saved || (gv_target != start_map_gvt)) { /* Restore gv_cur_region/gv_target etc. */ gv_target = start_map_gvt; gv_cur_region = start_map->reg.addr; change_reg(); } DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); if (spr_tpwrapped) { op_tcommit(); REVERT; /* remove our condition handler */ } assert(save_dollar_tlevel == dollar_tlevel); if (result_found) { /* Restore accumulated minimal key into gv_altkey */ assert(matchkey); if (gv_altkey != matchkey) COPY_KEY(gv_altkey, matchkey); } if (currkey_orig_saved) COPY_KEY(gv_currkey, &currkey_orig_buf.key); return result_found; } fis-gtm-V7.0-005/sr_unix/gvusr.c0000755000032200000250000000263614342376330015365 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gvusr.h" #include "gvusr_queryget.h" /* this module is just a set of stubs */ void gvusr_init(gd_region *reg, gd_region **creg, gv_key **ckey, gv_key **akey) { return; } void gvusr_rundown(void) { return; } int gvusr_data(void) { return 1; } int gvusr_order(void) { return 1; } int gvusr_query(mval *v) { return 1; } int gvusr_zprevious(void) { return 1; } int gvusr_get(mval *v) { v->mvtype = MV_STR; v->str.len = SIZEOF("TestData") - 1; v->str.addr = "TestData"; return 1; } void gvusr_kill(bool do_subtree) { return; } void gvusr_put(mval *v) { return; } int gvusr_lock(uint4 lock_len, unsigned char *lock_key, gd_region *reg) { /* 0 indicates successful lock */ return TRUE; } void gvusr_unlock(uint4 lock_len, unsigned char *lock_key, gd_region *reg) { return; } boolean_t gvusr_queryget(mval *v) { return 1; } fis-gtm-V7.0-005/sr_unix/hex2utf.mpt0000755000032200000250000000231314342376330016152 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2006 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %HEX2UTF;GT.M %HEX2UTF utility - hexadecimal byte stream to UTF-8 string ;invoke with %U storing the hexadecimal byte stream to return %S having the converted string ;invoke at INT to execute interactively ;invoke at FUNC as an extrinsic function ; set %S=$$FUNC(%U) quit INT new %U read !,"Hexadecimal byte stream: ",%U set %S=$$FUNC(%U) quit FUNC(u) new l set l=$ZLENGTH(u) quit $$recurse(1,l) recurse(start,end); new l,m set l=end-start+1 if (l<512) quit $$eval(start,end) set m=l\2 if (m#2=1) set m=m+1 quit $$recurse(start,start+m-1)_$$recurse(start+m,end) eval(start,end); new i,s,d,s1 set s1="$zchar(" for i=start:2:end-2 set s=$zextract(u,i,i+1),d=$$FUNC^%HD(s),s1=s1_d_"," set i=end-1,s=$zextract(u,i,i+1),d=$$FUNC^%HD(s),s1=s1_d set s1=s1_")" quit @s1 fis-gtm-V7.0-005/sr_unix/hugetlbfs_overrides.c0000644000032200000250000000502714342376330020256 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2022 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. * * * ****************************************************************/ /* * This file contains functions related to Linux HugeTLB support. * Supported Huge Page functionality requires the following prerequisites: * Linux kernel support of Huge Pages * x86_64 or i386 architecture * Availability of Huge Pages through setting value to /proc/sys/vm/nr_hugepages or hugepages= kernel parameter or * /proc/sys/vm/nr_overcommit_hugepages * In order to use shmget with Huge Pages, either the process gid should be in /proc/sys/vm/hugetlb_shm_group or the * process should have CAP_IPC_LOCK * In order to remap .text/.data/.bss sections, a file system of type hugetlbfs should be mounted * Appropriate environmental variables should be set (gtm_hugetlb_shm) to enable/disable Huge Pages */ #include "mdef.h" #include "gtm_ipc.h" #include #include #include "gtm_unistd.h" #include "hugetlbfs_overrides.h" #undef shmget #include "send_msg.h" #include "error.h" GBLREF bool pin_shared_memory; GBLREF bool hugetlb_shm_enabled; error_def(ERR_SHMHUGETLB); error_def(ERR_SHMLOCK); int gtm_shmget (key_t key, size_t size, int shmflg, bool lock) { # ifdef HUGETLB_SUPPORTED int shmid; struct shmid_ds shmstat; bool native_shmget = FALSE; if (hugetlb_shm_enabled) { shmid = shmget(key, size, shmflg | SHM_HUGETLB); if ((-1 == shmid) && ((EPERM == errno) || (ENOMEM == errno))) { /* retry without huge pages */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SHMHUGETLB, 0, errno); shmid = shmget(key, size, shmflg); native_shmget = TRUE; } } else { shmid = shmget(key, size, shmflg); native_shmget = TRUE; } if ((-1 != shmid) && native_shmget && lock && pin_shared_memory) { /* shared memory segment successfully allocated, huge pages not in use, and locking requested * * lock shared memory so it won't be swapped */ if (-1 == shmctl(shmid, SHM_LOCK, &shmstat)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SHMLOCK, 0, errno); } } return shmid; # else return shmget(key, size, shmflg); /* if huge pages are not supported, then neither is locking shm */ # endif } fis-gtm-V7.0-005/sr_unix/hugetlbfs_overrides.h0000644000032200000250000000213114342376330020254 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2022 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. * * * ****************************************************************/ #ifndef HUGETLBFS_OVERRIDES_H_ #define HUGETLBFS_OVERRIDES_H_ #if ( defined(__linux__) && ( defined(__i386__) || defined(__x86_64__) ) ) # define HUGETLB_SUPPORTED 1 GBLREF long gtm_os_hugepage_size; # define OS_HUGEPAGE_SIZE gtm_os_hugepage_size # define ADJUST_SHM_SIZE_FOR_HUGEPAGES(SRCSIZE, DSTSIZE) DSTSIZE = ROUND_UP(SRCSIZE, OS_HUGEPAGE_SIZE) #else OS_PAGE_SIZE_DECLARE # define ADJUST_SHM_SIZE_FOR_HUGEPAGES(SRCSIZE, DSTSIZE) DSTSIZE = ROUND_UP(SRCSIZE, OS_PAGE_SIZE) #endif extern int gtm_shmget(key_t key, size_t size, int shmflg, bool lock); #endif /* HUGETLBFS_OVERRIDES_H_ */ fis-gtm-V7.0-005/sr_unix/import_and_sign_key.sh0000644000032200000250000000751314342376330020427 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright (c) 2010-2021 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. # # # ################################################################# ############################################################################################# # # import_and sign_key.sh: Import public key into the owner's keyring. After confirming # the fingerprint, sign the key. # # Arguments - # $1 - path of the public key file. # $2 - email id of the public key's owner. # ############################################################################################# hostos=`uname -s` # echo and options ECHO=/bin/echo ECHO_OPTIONS="" #Linux honors escape sequence only when run with -e if [ "Linux" = "$hostos" ] ; then ECHO_OPTIONS="-e" ; fi # Path to key file and email id are required if [ $# -lt 2 ]; then $ECHO "Usage: `basename $0` public_key_file email_id" exit 1 fi public_key_file=$1 email_id=$2 # Identify GnuPG - it is required gpg=`command -v gpg2` if [ -z "$gpg" ] ; then gpg=`command -v gpg` ; fi if [ -z "$gpg" ] ; then $ECHO "Unable to find gpg2 or gpg. Exiting" ; exit 1 ; fi # Exit if the public key for this id already exists in the keyring $gpg --list-keys $email_id 2>/dev/null 1>/dev/null if [ $? -eq 0 ] ; then $ECHO "Public key of $email_id already exists in keyring." fi # Ensure that the public key file exists and is readable if [ ! -r $public_key_file ] ; then $ECHO "Key file $public_key_file not accessible." ; exit 1 fi # Import the public key into the keyring $gpg --no-tty --import --yes $public_key_file if [ $? -ne 0 ] ; then $ECHO "Error importing public key for $email_id from $public_key_file" ; exit 1 fi # Display fingerprint of the just imported public key $ECHO "#########################################################" $gpg --fingerprint $email_id if [ $? -ne 0 ] ; then $ECHO "Error obtaining fingerprint the email id - $email_id" ; exit 1 fi $ECHO "#########################################################" trap 'stty sane ; exit 1' HUP INT QUIT TERM TRAP # Confirm with the user whether the fingerprint matches unset tmp while [ "Y" != "$tmp" ] ; do $ECHO $ECHO_OPTIONS "Please confirm validity of the fingerprint above (y/n/[?]):" \\c read tmp ; tmp=`$ECHO $tmp | tr yesno YESNO` case $tmp in "Y"|"YE"|"YES") tmp="Y" ;; "N"|"NO") $ECHO Finger print of public key for $email_id in $public_key_file not confirmed $gpg --no-tty --batch --delete-keys --yes $email_id exit 1 ;; *) $ECHO $ECHO "If the fingerprint shown above matches the fingerprint you have been indepently" $ECHO "provided for the public key of this $email_id, then press Y otherwise press N" $ECHO ;; esac done unset tmp #If yes, we need to sign the public key. In order to do so, we need the user's passphrase. # Get passphrase for GnuPG keyring $ECHO $ECHO_OPTIONS Passphrase for keyring: \\c ; stty -echo ; read passphrase ; stty echo ; $ECHO "" # Export and sign the key $ECHO $passphrase | $gpg --no-tty --batch --passphrase-fd 0 --sign-key --yes $email_id if [ $? -eq 0 ]; then $ECHO "Successfully signed public key for $email_id received in $public_key_file" ; exit 0 else $gpg --no-tty --batch --delete-keys --yes $email_id $ECHO "Failure signing public key for $email_id received in $public_key_file" ; exit 1 fi fis-gtm-V7.0-005/sr_unix/install_debug_symbols_sh.txt0000644000032200000250000000271714342376330021667 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright (c) 2017 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. # # # ################################################################# # Check installing as root uid=`id -u` if [ "$uid" -ne 0 ]; then echo "$0 must be run as root" exit 1 fi # Check valid package package_dir=`dirname $0` if [ "$package_dir" = "" ]; then package_dir="." fi if [ ! -f "${package_dir}/mumps.debug" ]; then echo "mumps.debug not found in package directory (${package_dir})" exit 1 fi # Check valid $gtm_dist if [ ! -d "$gtm_dist" ]; then echo "gtm_dist ($gtm_dist) is not a valid directory" exit 1 fi if [ ! -x "$gtm_dist/mumps" ]; then echo "gtm_dist ($gtm_dist) is not a GT.M installation" exit 1 fi # Confirm installation echo "" echo "Installing debug symbols to $gtm_dist" echo "" printf "Proceed? > " read resp response=$(echo "$resp" | tr '[a-z]' '[A-Z]') if [ "Y" != "$response" -a "YES" != "$response" ] ; then exit 1 fi # Do the copy cp ${package_dir}/*.debug $gtm_dist/ ls -ald $gtm_dist/*.debug echo "" echo "Done. Please check the permissions on the listed files and change them as appropriate." fis-gtm-V7.0-005/sr_unix/install_gtmpcat_sh.txt0000644000032200000250000000462014342376330020463 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright (c) 2017 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. # # # ################################################################# # Check installing as root uid=`id -u` if [ "$uid" -ne 0 ]; then echo "$0 must be run as root" exit 1 fi # Check valid package package_dir=`dirname $0` if [ "$package_dir" = "" ]; then package_dir="." fi if [ ! -f "${package_dir}/gtmpcat.m" ]; then echo "gtmpcat.m missing from package directory (${package_dir})" exit 1 fi # Check valid $gtm_dist if [ ! -d "$gtm_dist" ]; then echo "gtm_dist ($gtm_dist) is not a valid directory" exit 1 fi if [ ! -x "$gtm_dist/mumps" ]; then echo "gtm_dist ($gtm_dist) is not a GT.M installation" exit 1 fi gtmroutines=". $gtm_dist" export gtmroutines # Note owner and group of mumps executable for later use as the owner and group of the installed files eval $(/bin/ls -l $gtm_dist/mumps | awk 'NR == 1 { print "owner=" $3, "group=" $4 }') # Check matching version req_zver="#ZVERSION#" cur_zver="$($gtm_dist/mumps -r %XCMD 'write $zversion')" if [ "$req_zver" != "$cur_zver" -a "$req_zver" != "ANY" ]; then echo "Version to install ($req_zver) does not match \$gtm_dist/mumps version ($cur_zver)" exit 1 fi # Confirm installation echo "" echo "Installing gtmpcat files to $gtm_dist/tools/gtmpcat" echo "" printf "Proceed? > " read resp response=$(echo "$resp" | tr '[a-z]' '[A-Z]') if [ "Y" != "$response" -a "YES" != "$response" ] ; then exit 1 fi # Do the copy if [ ! -d "$gtm_dist/tools" ]; then mkdir $gtm_dist/tools chown ${owner}:${group} $gtm_dist/tools fi mkdir -p $gtm_dist/tools/gtmpcat cp ${package_dir}/gtmpcat.m ${package_dir}/#FLDBLD# $gtm_dist/tools/gtmpcat cp ${package_dir}/gtmpcat.sh $gtm_dist/gtmpcat (cd $gtm_dist/tools/gtmpcat && gtmroutines="." $gtm_dist/mumps *.m) chown -R ${owner}:${group} $gtm_dist/gtmpcat $gtm_dist/tools/gtmpcat ls -ald $gtm_dist/gtmpcat $gtm_dist/tools $gtm_dist/tools/gtmpcat $gtm_dist/tools/gtmpcat/* echo "" echo "Done. Please check the permissions on the listed files and directories and change them as appropriate." fis-gtm-V7.0-005/sr_unix/interlock.h0000644000032200000250000001314114342376330016204 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ /* note -- this may still need cleaning up for multiprocessor implementations, may need a get_latch */ #include "lockconst.h" #include "compswap.h" #include "aswp.h" #include "memcoherency.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" /* Define memory barrier macro to use in LOCK_BUFF_FOR_READ/RELEASE_BUFF_READ_LOCK macros below. On AIX * the locking macros have no memory barrier connotation because they use load/store_locked instructions. * The macros defined below give us that connotation on the platforms that need it and avoid adding additional * memory barriers on platforms that already have them. */ #if defined(_AIX) # define READ_LOCK_READ_MEMBARRIER SHM_READ_MEMORY_BARRIER # define READ_LOCK_WRITE_MEMBARRIER SHM_WRITE_MEMORY_BARRIER #else # define READ_LOCK_READ_MEMBARRIER # define READ_LOCK_WRITE_MEMBARRIER #endif /* LATCH_{CLEAR,SET,CONFLICT} need to be in ascending order. wcs_verify relies on this ordering for detecting out-of-range values */ #define LATCH_CLEAR 0 #define LATCH_SET 1 #define LATCH_CONFLICT 2 #define WRITE_LATCH_VAL(cr) (cr)->interlock.latch /* this can take either of the above 3 LATCH_* values */ #define INTERLOCK_INIT(X) \ { \ (X)->read_in_progress = -1; \ SHM_WRITE_MEMORY_BARRIER; \ SET_LATCH((sm_int_ptr_t)&((X)->interlock.latch), LATCH_CLEAR); \ SET_LATCH_GLOBAL(&((X)->rip_latch), LOCK_AVAILABLE); \ } /* On HPPA, SET_LATCH_GLOBAL forces its available value, for others it is the same as SET_LATCH */ /* New buffer doesn't need interlocked operation. */ #define LOCK_NEW_BUFF_FOR_UPDATE(X) (SET_LATCH((sm_int_ptr_t)&((X)->interlock.latch), LATCH_SET)) /* After this macro returns, Y points to the PRE-aswp (not POST-aswp) value of the latch */ #define LOCK_BUFF_FOR_UPDATE(X,Y,Z) {Y = ASWP((sm_int_ptr_t)&(X)->interlock.latch, LATCH_SET, Z); \ LOCK_HIST("OBTN", &(X)->interlock.latch, process_id, -1);} #define RELEASE_BUFF_UPDATE_LOCK(X,Y,Z) {LOCK_HIST("RLSE", &(X)->interlock.latch, process_id, -1); \ Y = ASWP((sm_int_ptr_t)&(X)->interlock.latch, LATCH_CLEAR, Z);} #define CLEAR_BUFF_UPDATE_LOCK(X,Z) (ASWP((sm_int_ptr_t)&(X)->interlock.latch, LATCH_CLEAR, Z)) /* Only the writer uses the LATCH_CONFLICT value. */ #define LOCK_BUFF_FOR_WRITE(X,Y,Z) (Y = ASWP((sm_int_ptr_t)&(X)->interlock.latch, LATCH_CONFLICT, Z)) /* The macros below currently use an interlocked increment/decrement type of locking unlike the above macros which * use an atomic swap method. */ #define LOCK_BUFF_FOR_READ(X,Y) \ { \ Y = INCR_CNT((sm_int_ptr_t)&(X)->read_in_progress, (sm_global_latch_ptr_t)&(X)->rip_latch); \ READ_LOCK_READ_MEMBARRIER; \ } #define RELEASE_BUFF_READ_LOCK(X) \ { \ READ_LOCK_WRITE_MEMBARRIER; \ DECR_CNT((sm_int_ptr_t)&(X)->read_in_progress, (sm_global_latch_ptr_t)&(X)->rip_latch); \ } /* Used after an aswp; if the return value (second argument to one of these macros) was * LATCH_CONFLICT, then the writer owned the buffer when this process performed the aswp. * WRITER_BLOCKED_BY_PROC is used by a non-writer process. * WRITER_OWNS_BUFF is used by the writer. */ #define WRITER_BLOCKED_BY_PROC(X) ((X) == LATCH_CONFLICT) #define WRITER_OWNS_BUFF(X) ((X) == LATCH_CONFLICT) #define WRITER_STILL_OWNS_BUFF(X,Y) ((X)->interlock.latch != LATCH_CLEAR) /* Used after an aswp; if the return value (second argument to one of these macros) * was LATCH_CLEAR, then nobody had it when this process performed the aswp so this * process owns the resource. */ #define OWN_BUFF(X) ((X) == LATCH_CLEAR) #define INCR_CNT(X,Y) INTERLOCK_ADD(X,Y,1) #define DECR_CNT(X,Y) INTERLOCK_ADD(X,Y,-1) #if !defined(__ia64) && !defined(__x86_64__) && !defined(__sparc) #define GET_SWAPLOCK(X) (COMPSWAP_LOCK((X), LOCK_AVAILABLE, 0, process_id, 0)) #else /* Doing the simple test before COMPSWAP_LOCK can help performance when a lock is highly contended */ #define GET_SWAPLOCK(X) (((X)->u.parts.latch_pid == LOCK_AVAILABLE) && COMPSWAP_LOCK((X), LOCK_AVAILABLE, 0, process_id, 0)) #endif /* __ia64, __x86_64__, and __sparc */ /* Use COMPSWAP_UNLOCK to release the lock because of the memory barrier and other-processor notification it implies. Also * the usage of COMPSWAP_UNLOCK allows us to check (with low cost) that we have/had the lock we are trying to release. * If we don't have the lock and are trying to release it, an assertpro seems the logical choice as the logic is very broken * at that point. If this macro is used in part of an expression, the assertpro path must also return a value (to keep * the compiler happy) thus the construct (assertpro, 0) which returns a zero (see usage with assert() on UNIX). */ #define RELEASE_SWAPLOCK(X) (COMPSWAP_UNLOCK((X), process_id, 0, LOCK_AVAILABLE, 0) ? 1 : (assertpro(FALSE), 0)) #define GRAB_LATCH_INDEFINITE_WAIT -1 /* special value indicating infinite timeout input to "grab_latch" */ /* Function prototypes */ boolean_t grab_latch(sm_global_latch_ptr_t latch, int max_timeout_in_secs, wait_state state, sgmnt_addrs *csa); void rel_latch(sm_global_latch_ptr_t latch); fis-gtm-V7.0-005/sr_unix/invocation_mode.h0000755000032200000250000000205114342376330017370 0ustar librarygtc/**************************************************************** * * * 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. * * * ****************************************************************/ #ifndef INVOCATION_MODE_H #define INVOCATION_MODE_H GBLREF unsigned int invocation_mode; /* flags for invocation_mode */ #define MUMPS_COMPILE (1 << 0) /* compile-only invocation - mumps */ #define MUMPS_RUN (1 << 1) /* mumps -run file */ #define MUMPS_DIRECT (1 << 2) /* mumps -direct */ #define MUMPS_CALLIN (1 << 3) /* libgtmci.so linked with call-in user */ #define MUMPS_GTMCI (1 << 4) /* current environment is created by gtm_ci */ #define MUMPS_UTILTRIGR (1 << 5) /* One of the utilities needs to run triggers */ #define MUMPS_GTMCI_OFF (~MUMPS_GTMCI) /* to turn-off MUMPS_GTMCI */ #endif fis-gtm-V7.0-005/sr_unix/io_get_fgn_driver.c0000755000032200000250000000130514342376330017662 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "io.h" error_def(ERR_INVMNEMCSPC); dev_dispatch_struct *io_get_fgn_driver(mstr *s) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_INVMNEMCSPC, 2, s->len, s->addr); return ((dev_dispatch_struct *)NULL); } fis-gtm-V7.0-005/sr_unix/io_init_name.c0000755000032200000250000000325714342376330016651 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "gtm_strings.h" #include "io.h" #include "gtm_logicals.h" #include "gtm_unistd.h" #include "gtm_limits.h" GBLDEF mstr sys_input; GBLDEF mstr sys_output; GBLDEF mstr gtm_principal; void io_init_name(void) { char temp[TTY_NAME_MAX + 1], *c; int i, size; if (isatty(0)) { memset(temp, '\0', TTY_NAME_MAX + 1); TTYNAME_R(0, temp, TTY_NAME_MAX, i); /* ttyname_r() is MT-Safe */ if (0 == i) { size = STRLEN(temp); sys_input.addr = (char *) malloc(size); memcpy(sys_input.addr, temp, size); sys_input.len = size; } else { sys_input.addr = "0"; sys_input.len = 1; } } else { sys_input.addr = "0"; sys_input.len = 1; } if (isatty(1)) { memset(temp, '\0', TTY_NAME_MAX + 1); TTYNAME_R(1, temp, TTY_NAME_MAX, i); if (0 == i) { size = STRLEN(temp); sys_output.addr = (char *) malloc(size); memcpy(sys_output.addr, temp, size); sys_output.len = size; } else { sys_output.addr = "&"; sys_output.len = 1; } } else { sys_output.addr = "&"; sys_output.len = 1; } gtm_principal.addr = GTM_PRINCIPAL; gtm_principal.len = STR_LIT_LEN(GTM_PRINCIPAL); return; } fis-gtm-V7.0-005/sr_unix/io_is_rm.c0000755000032200000250000000154414342376330016014 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "gtm_string.h" #include "io.h" #include "gtm_stat.h" #include "gtm_stdio.h" #include "eintr_wrappers.h" bool io_is_rm(mstr *name) { int stat_res; struct stat outbuf; char buffer[BUFSIZ]; assert(BUFSIZ > name->len); memcpy(buffer, name->addr, name->len); buffer[name->len] = '\0'; STAT_FILE(buffer, &outbuf, stat_res); return (0 == stat_res && S_ISREG(outbuf.st_mode)); } fis-gtm-V7.0-005/sr_unix/io_is_sn.c0000755000032200000250000000123714342376330016015 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "io.h" /* This module determines whether a vms device is NONLOCAL and therefore a network device, generally sys$net. In UNIX, it always returns false. */ bool io_is_sn(mstr *tn) { return FALSE; } fis-gtm-V7.0-005/sr_unix/io_open_try.c0000644000032200000250000004774214342376330016551 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_stat.h" #include "gtm_iconv.h" #include "gtm_netdb.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_stdlib.h" #include #include #include "io.h" #include "iosp.h" #include "iotimer.h" #include "io_params.h" #include "gt_timer.h" #include "copy.h" #include "iottdef.h" #include "io_dev_dispatch.h" #include "iormdef.h" #include "eintr_wrappers.h" #include "mmemory.h" #include "gtm_caseconv.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "wake_alarm.h" #include "stringpool.h" #include "gtm_conv.h" #include "gtm_utf8.h" #include "gtmimagename.h" #include "gtmio.h" #include "op.h" #include "indir_enum.h" #define LOGNAME_LEN 255 /* avoid calling getservbyname for shell and Kerberos shell */ #define RSHELL_PORT 514 #define KSHELL_PORT 544 GBLREF dev_dispatch_struct io_dev_dispatch[]; GBLREF io_desc *active_device; GBLREF mstr sys_input; GBLREF mstr sys_output; GBLREF bool out_of_time; GBLREF volatile int4 outofband; GBLREF int4 write_filter; LITREF mstr chset_names[]; LITREF unsigned char io_params_size[]; error_def(ERR_GETNAMEINFO); error_def(ERR_GTMEISDIR); error_def(ERR_TEXT); boolean_t io_open_try(io_log_name *naml, io_log_name *tl, mval *pp, int4 msec_timeout, mval *mspace) /* timeout in msec */ { uint4 status; mstr tn; /* translated name */ int oflag; params *param; unsigned char ch; bool timed = FALSE; int file_des; # ifdef __MVS__ int file_des_w = -2; /* fifo write */ uint4 status_w; d_rm_struct *d_rm_out, *d_rm_in; # endif boolean_t filecreated = FALSE; TID timer_id; struct stat outbuf; char *buf, namebuf[LOGNAME_LEN + 1]; unsigned char dev_type[MAX_DEV_TYPE_LEN]; iosb io_status_blk; mstr chset_mstr; int umask_orig, umask_creat; int char_or_block_special; int stat_res; int fstat_res; int p_offset, len; boolean_t mknod_err , stat_err, dir_err; int save_mknod_err, save_stat_err, save_gsn_err; int gso_stat, gsn_stat, sockoptval; in_port_t sockport; GTM_SOCKLEN_TYPE socknamelen; struct sockaddr_storage sockname; GTM_SOCKLEN_TYPE sockoptlen; boolean_t ichset_specified, ochset_specified; int errcode; unsigned int port_len; char port_buffer[NI_MAXSERV], *port_ptr; io_desc *iod, *temp_iod; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; iod = naml->iod; char_or_block_special = FALSE; out_of_time = FALSE; file_des = -2; oflag = 0; mknod_err = FALSE; stat_err = FALSE; dir_err = FALSE; tn.len = tl->len; if (tn.len > LOGNAME_LEN) tn.len = LOGNAME_LEN; tn.addr = tl->dollar_io; memcpy(namebuf, tn.addr, tn.len); namebuf[tn.len] = '\0'; buf = namebuf; timer_id = (TID)io_open_try; if (NO_M_TIMEOUT == msec_timeout) timed = FALSE; else timed = (0 != msec_timeout); if (0 == iod) { if (0 == tl->iod) { temp_iod = (io_desc *)malloc(SIZEOF(io_desc)); memset((char*)temp_iod, 0, SIZEOF(io_desc)); temp_iod->pair.in = temp_iod; temp_iod->pair.out = temp_iod; temp_iod->trans_name = tl; temp_iod->type = n_io_dev_types; p_offset = 0; while(iop_eol != *(pp->str.addr + p_offset)) { ch = *(pp->str.addr + p_offset++); if (iop_fifo == ch) temp_iod->type = ff; else if (iop_sequential == ch) temp_iod->type = rm; p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); } if (ff == temp_iod->type) { /* fifo with RW permissions for owner, group, other */ if ((-1 != MKNOD(buf, FIFO_PERMISSION, 0)) # ifdef __MVS__ || (EEXIST == errno) # endif ) { # ifdef __MVS__ if (EEXIST != errno) filecreated = TRUE; /* create another one for fifo write */ temp_iod->pair.out = (io_desc *)malloc(SIZEOF(io_desc)); (temp_iod->pair.out)->pair.in = temp_iod; (temp_iod->pair.out)->pair.out = (temp_iod->pair.out); (temp_iod->pair.out)->trans_name = tl; (temp_iod->pair.out)->type = ff; # else filecreated = TRUE; # endif } else if (EEXIST != errno) { mknod_err = TRUE; save_mknod_err = errno; /* This save_err will be checked immediately after error_handler is set; * In that way error handler will get the chance to handle any error */ } } tl->iod = temp_iod; } if ((n_io_dev_types == tl->iod->type) && mspace && mspace->str.len) { lower_to_upper(dev_type, (uchar_ptr_t)mspace->str.addr, mspace->str.len); if (((SIZEOF("SOCKET") - 1) == mspace->str.len) && (0 == MEMCMP_LIT(dev_type, "SOCKET"))) tl->iod->type = gtmsocket; else if (((SIZEOF("PIPE") - 1) == mspace->str.len) && (0 == MEMCMP_LIT(dev_type, "PIPE"))) tl->iod->type = pi; else tl->iod->type = us; } if (n_io_dev_types == tl->iod->type) { if (0 == memvcmp(tn.addr, tn.len, sys_input.addr, sys_input.len)) file_des = 0; else if (0 == memvcmp(tn.addr, tn.len, sys_output.addr, sys_output.len)) file_des = 1; if (0 == file_des || 1 == file_des) { FSTAT_FILE(file_des, &outbuf, fstat_res); if (-1 == fstat_res) { /* save errno to be handled by exception handler if set */ save_stat_err = errno; stat_err = TRUE; } } else if (0 == memvcmp(tn.addr, tn.len, LIT_AND_LEN(DEVNULL))) tl->iod->type = nl; else { STAT_FILE(buf, &outbuf, stat_res); if ((-1 == stat_res) && (n_io_dev_types == tl->iod->type)) { if (ENOENT == errno) { tl->iod->type = rm; filecreated = TRUE; } else { /* save errno to be checked later */ save_stat_err = errno; stat_err = TRUE; } } } } if (n_io_dev_types == tl->iod->type) { if (!stat_err) { switch(outbuf.st_mode & S_IFMT) { case S_IFCHR: case S_IFBLK: char_or_block_special = TRUE; break; case S_IFIFO: tl->iod->type = ff; break; case S_IFDIR: dir_err = TRUE; /* directories should not be opened */ /* no break in order to set iod->type value */ case S_IFREG: tl->iod->type = rm; break; case S_IFSOCK: /* If SOCK_STREAM, AF_INET/AF_INET6, and not [kr]shell port assume we were * started by inetd. * We are not able to trigger exception until active_device set * and exception parsed which is not done for fd == 0/1 which * is from io_init called from gtm_startup before condition handlers * are setup so we can't report errors, just will be treated as rm. * Note the fall through if the above conditions are not true to * maintain compatibility for rshd startup PER 3252. * 28-JUL-1995 14:24:31 FERTIG REPLACE IO_OPEN_TRY.C(18) * "PER 3252: fix problems starting up mumps from an ""rsh"" or ""remsh""" * * Different UNIX flavors behave differently when given an AF_UNIX socket, * some errors and some setting sa_family to zero, so recognize each platform * behavior and assume a local socket for those cases. */ sockoptlen = SIZEOF(sockoptval); gso_stat = getsockopt(file_des, SOL_SOCKET, SO_TYPE, &sockoptval, (GTM_SOCKLEN_TYPE *)&sockoptlen); if (!gso_stat && SOCK_STREAM == sockoptval) { socknamelen = SIZEOF(sockname); gsn_stat = getsockname(file_des, (struct sockaddr *)&sockname, (GTM_SOCKLEN_TYPE *)&socknamelen); save_gsn_err = errno; if (!gsn_stat && ((AF_INET == ((sockaddr_ptr)&sockname)->sa_family) || (AF_INET6 == ((sockaddr_ptr)&sockname)->sa_family))) { port_len = NI_MAXSERV; if (0 != (errcode = getnameinfo((struct sockaddr *)&sockname, socknamelen, NULL, 0, port_buffer, port_len, NI_NUMERICHOST))) { RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode); return FALSE; } sockport=atoi(port_buffer); if (RSHELL_PORT != sockport && KSHELL_PORT != sockport) { tl->iod->type = gtmsocket; break; } } else if ((!gsn_stat && ((AF_UNIX == ((sockaddr_ptr)&sockname)->sa_family) || (0 == socknamelen) || (0 == ((sockaddr_ptr)&sockname)->sa_family))) || (EOPNOTSUPP == save_gsn_err) # if defined(_AIX) || (ENOTCONN == save_gsn_err) # endif ) { tl->iod->type = gtmsocket; break; } } /* We don't expect any unusual socket types (e.g. SOCK_DGRAM) in testing, * so assert instead of falling through in DEBUG. */ assert(NULL == "Unknown socket type"); /* fall through */ case 0: assert(NULL == "Zero file type"); tl->iod->type = ff; break; default: assert(NULL == "Unknown value for (outbuf.st_mode & S_IFMT)"); break; } } } naml->iod = iod = tl->iod; } assert((0 <= iod->state) && (n_io_dev_states > iod->state)); active_device = iod; if ((-2 == file_des) && (dev_open != iod->state) && (us != iod->type) && (gtmsocket != iod->type) && (pi != iod->type)) { oflag |= (O_RDWR | O_CREAT | O_NOCTTY); p_offset = 0; ichset_specified = ochset_specified = FALSE; while ((iop_eol != *(pp->str.addr + p_offset)) && (pp->str.len > p_offset)) { assert((0 <= p_offset) && (p_offset < MAXPOSINT4)); assert((params) *(pp->str.addr + p_offset) < (params)n_iops); switch ((ch = *(pp->str.addr + p_offset++))) { case iop_exception: { assert((0 < p_offset) && ((1 + p_offset) < MAXPOSINT4)); DEF_EXCEPTION(pp, p_offset, iod); break; } case iop_append: /* this deviceparameter will move the file pointer to EOF in iorm_open.c * but will no longer force write to the EOF, so nothing to do here */ break; case iop_contiguous: break; case iop_newversion: if ((dev_open != iod->state) && (rm == iod->type)) oflag |= O_TRUNC; break; case iop_readonly: oflag &= ~(O_RDWR | O_CREAT | O_WRONLY); oflag |= O_RDONLY; break; case iop_writeonly: # ifdef __MVS__ if (ff != iod->type) # endif { oflag &= ~(O_RDWR | O_RDONLY); oflag |= O_WRONLY | O_CREAT; } break; case iop_ipchset: # ifdef KEEP_zOS_EBCDIC if ( (iconv_t)0 != iod->input_conv_cd ) { ICONV_CLOSE_CD(iod->input_conv_cd); } SET_CODE_SET(iod->in_code_set, (char *)(pp->str.addr + p_offset + 1)); if (DEFAULT_CODE_SET != iod->in_code_set) ICONV_OPEN_CD(iod->input_conv_cd, (char *)(pp->str.addr + p_offset + 1), INSIDE_CH_SET); # endif break; case iop_opchset: # ifdef KEEP_zOS_EBCDIC if ( (iconv_t)0 != iod->output_conv_cd) { ICONV_CLOSE_CD(iod->output_conv_cd); } SET_CODE_SET(iod->out_code_set, (char *)(pp->str.addr + p_offset + 1)); if (DEFAULT_CODE_SET != iod->out_code_set) ICONV_OPEN_CD(iod->output_conv_cd, INSIDE_CH_SET, (char *)(pp->str.addr + p_offset + 1)); # endif break; case iop_m: case iop_utf8: case iop_utf16: case iop_utf16be: case iop_utf16le: break; default: break; } if (IOP_VAR_SIZE == io_params_size[ch]) { assert((0 < p_offset) && (p_offset < (MAXPOSINT4 - 255))); p_offset += (unsigned char)*(pp->str.addr + p_offset) + 1; } else { assert((0 < p_offset) && (p_offset < (MAXPOSINT4 - io_params_size[ch]))); p_offset += io_params_size[ch]; } } /* Check the saved error from mknod() for fifo, also saved error from fstat() or stat() so error handler (if set) can handle it */ if (ff == tl->iod->type && mknod_err) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) save_mknod_err); /* Error from either stat() or fstat() function */ if (stat_err) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) save_stat_err); /* Error from trying to open a dir */ if (dir_err) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_GTMEISDIR, 2, LEN_AND_STR(buf)); if (timed) start_timer(timer_id, msec_timeout, wake_alarm, 0, NULL); /* RW permissions for owner and others as determined by umask. */ umask_orig = umask(000); /* determine umask (destructive) */ (void)umask(umask_orig); /* reset umask */ umask_creat = 0666 & ~umask_orig; /* no OPEN EINTR macros in the following while loop due to complex error checks and processing between * top of while and calls to OPEN3 */ while ((-1 == (file_des = OPEN3(buf, SETOCLOEXEC(oflag), umask_creat))) && !out_of_time) { if (timed && (0 == msec_timeout)) out_of_time = TRUE; if (outofband) async_action(FALSE); if (EINTR == errno || ETXTBSY == errno || ENFILE == errno || EBUSY == errno || ((mb == iod->type) && (ENXIO == errno))) continue; else { # ifdef __MVS__ if (EINVAL == errno && ff == tl->iod->type && (oflag & O_RDONLY) && (oflag & O_WRONLY)) { int oflag_clone; int fcntl_res; /* oflag must be O_RDWR, set it to be O_RDONLY */ oflag &= ~O_WRONLY; oflag |= O_NONBLOCK; while ((-1 == (file_des = OPEN3(buf, SETOCLOEXEC(oflag), umask_creat))) && !out_of_time) { if (0 == msec_timeout) out_of_time = TRUE; if (outofband) async_action(FALSE); if (EINTR == errno || ETXTBSY == errno || ENFILE == errno || EBUSY == errno || ((mb == iod->type) && (ENXIO == errno))) continue; else break; } FCNTL2(file_des, F_GETFL, oflag_clone); FCNTL3(file_des, F_SETFL, (oflag_clone & ~O_NONBLOCK), fcntl_res); /* oflag was just made O_RDONLY, now set it to be O_WRONLY */ oflag |= O_WRONLY; oflag &= ~O_RDONLY; while ((-1 == (file_des_w = OPEN3(buf, SETOCLOEXEC(oflag), umask_creat))) && !out_of_time) { if (0 == msec_timeout) out_of_time = TRUE; if (outofband) async_action(FALSE); if (EINTR == errno || ETXTBSY == errno || ENFILE == errno || EBUSY == errno || ((mb == iod->type) && (ENXIO == errno))) continue; else break; } } # endif break; } } SETFDCLOEXEC(file_des); if (timed && !out_of_time) cancel_timer(timer_id); if (out_of_time && (-1 == file_des)) { if ((ff == tl->iod->type) && filecreated) { /* The FIFO didn't really exist so remove all traces of it */ status = UNLINK(tl->iod->trans_name->dollar_io); assert(!status); tl->iod->type = rm; remove_rms(tl->iod); } return FALSE; } } # ifdef KEEP_zOS_EBCDIC if (gtmsocket != iod->type) { SET_CODE_SET(iod->in_code_set, OUTSIDE_CH_SET); if (DEFAULT_CODE_SET != iod->in_code_set) ICONV_OPEN_CD(iod->input_conv_cd, OUTSIDE_CH_SET, INSIDE_CH_SET); SET_CODE_SET(iod->out_code_set, OUTSIDE_CH_SET); if (DEFAULT_CODE_SET != iod->out_code_set) ICONV_OPEN_CD(iod->output_conv_cd, INSIDE_CH_SET, OUTSIDE_CH_SET); } else { SET_CODE_SET(iod->out_code_set, INSIDE_CH_SET); if (DEFAULT_CODE_SET != iod->out_code_set) ICONV_OPEN_CD(iod->output_conv_cd, OUTSIDE_CH_SET, INSIDE_CH_SET); SET_CODE_SET(iod->in_code_set, INSIDE_CH_SET); if (DEFAULT_CODE_SET != iod->in_code_set) ICONV_OPEN_CD(iod->input_conv_cd, INSIDE_CH_SET, OUTSIDE_CH_SET); } # endif if (-1 == file_des) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); if (n_io_dev_types == iod->type) { /* On AIX, /dev/{,*}random are of 'terminal type'.Hence define its type before * calling isatty() */ if ((0 == memvcmp(tn.addr, tn.len, LIT_AND_LEN("/dev/random"))) || (0 == memvcmp(tn.addr, tn.len, LIT_AND_LEN("/dev/urandom")))) tl->iod->type = rm; else if (isatty(file_des)) iod->type = tt; else if (char_or_block_special && file_des > 2) if (0 == memvcmp(tn.addr, tn.len, LIT_AND_LEN("/dev/zero"))) tl->iod->type = rm; else /* assume mag tape */ iod->type = mt; else iod->type = rm; } assert(iod->type < n_io_dev_types); iod->disp_ptr = &io_dev_dispatch[iod->type]; /* Do this deviceparameter check only if type is rm, it is closed, and no_destroy is defined. If a deviceparameter other * than SEEK or APPEND is defined, then clear no_destroy as state will not be restored. */ if ((rm == iod->type) && (dev_closed == iod->state) && ((d_rm_struct *)iod->dev_sp)->no_destroy) { for (p_offset = 0; iop_eol != *(pp->str.addr + p_offset); p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch])) { ch = *(pp->str.addr + p_offset++); assert((params)ch < (params)n_iops); if ((iop_seek != ch) && (iop_append != ch)) { ((d_rm_struct *)iod->dev_sp)->no_destroy = FALSE; break; } } } if (dev_never_opened == iod->state) { iod->wrap = DEFAULT_IOD_WRAP; iod->width = DEFAULT_IOD_WIDTH; iod->length = DEFAULT_IOD_LENGTH; iod->write_filter = write_filter; } if (dev_open != iod->state) { /* initialize x and y unless it is closed and rm and no_destroy is TRUE */ if (!((dev_closed == iod->state) && (rm == iod->type) && ((d_rm_struct *)iod->dev_sp)->no_destroy)) { iod->dollar.x = 0; iod->dollar.y = 0; } iod->dollar.za = 0; iod->dollar.zb[0] = 0; iod->dollar.key[0] = 0; iod->dollar.device[0] = 0; if (iod->dollar.devicebuffer) free(iod->dollar.devicebuffer); iod->dollar.devicebuffer = NULL; } # ifdef __MVS__ /* copy over the content of tl->iod(iod) to (tl->iod->pair.out) */ if (ff == tl->iod->type) { *(tl->iod->pair.out) = *(tl->iod); (tl->iod->pair.out)->pair.in = tl->iod; (tl->iod->pair.out)->pair.out = (tl->iod->pair.out); assert((0 <= tl->iod->pair.out->state) && (n_io_dev_states > tl->iod->pair.out->state)); if (-1 < file_des_w) { io_desc *io_ptr; io_log_name dev_name; /* dummy */ io_ptr = (tl->iod->pair.out); assert(io_ptr != tl->iod->pair.in); assert(NULL != io_ptr); assert(io_ptr->state >= 0 && io_ptr->state < n_io_dev_states); assert(ff == io_ptr->type); dev_name.iod = tl->iod->pair.out; if (TRUE == ioff_open(&dev_name, pp, file_des_w, mspace, msec_timeout)) (tl->iod->pair.out)->state = dev_open; else if (dev_open == (tl->iod->pair.out)->state) (tl->iod->pair.out)->state = dev_closed; } else if ((-2 == file_des_w) && (O_WRONLY == (oflag & O_ACCMODE))) active_device = naml->iod = iod = tl->iod->pair.out; if (0 == file_des_w) (tl->iod->pair.out)->dollar.zeof = TRUE; } # endif iod->newly_created = filecreated; status = (iod->disp_ptr->open)(naml, pp, file_des, mspace, msec_timeout); if (TRUE == status) iod->state = dev_open; else { if ((dev_open == iod->state) && (gtmsocket != iod->type)) iod->state = dev_closed; if ((gtmsocket == iod->type) && iod->newly_created) { assert (iod->state != dev_open); iosocket_destroy(iod); active_device = 0; return status; } } # ifdef __MVS__ d_rm_out = tl->iod->pair.out->dev_sp; d_rm_in = tl->iod->pair.in->dev_sp; if ((ff == tl->iod->pair.in->type) || (ff == tl->iod->pair.out->type)) { if (NULL == d_rm_in) { tl->iod->pair.in->type = rm; tl->iod->pair.in->state = tl->iod->pair.out->state; tl->iod->pair.in->dev_sp = d_rm_out; } else if (NULL == d_rm_out) { tl->iod->pair.out->type = rm; tl->iod->pair.out->state = tl->iod->pair.in->state; tl->iod->pair.out->dev_sp = d_rm_in; d_rm_in->read_fildes = FD_INVALID; } } if (-1 < file_des_w) { d_rm_out->read_fildes = d_rm_in->fildes; d_rm_out->read_filstr = d_rm_in->filstr; free(d_rm_in); tl->iod->pair.in->dev_sp = d_rm_out; } # endif if (1 == file_des) iod->dollar.zeof = TRUE; active_device = 0; iod->newly_created = FALSE; if ((NO_M_TIMEOUT != msec_timeout) && IS_MCODE_RUNNING) return (status); else return TRUE; } fis-gtm-V7.0-005/sr_unix/io_type.c0000755000032200000250000000107314342376330015661 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "io.h" enum io_dev_type io_type(mstr *tn) { enum io_dev_type type; type = tt; return type; } fis-gtm-V7.0-005/sr_unix/iob.h0000755000032200000250000000254114342376330014770 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /* iob.h - definitions used with buffered I/O routines */ typedef struct { int fd; /* descriptor for file */ char *path; /* filename */ int oflag; /* open flags (see the open system call) */ int mode; /* creation mode (see the open system call) */ int blksiz; /* minimum size of block */ int bufsiz; /* size of buffer (blksiz * blocking factor) */ char *buf; /* I/O buffer */ char *bptr; /* pointer to last character read/written + 1 */ int remaining; /* for read: bytes remaining to be read from buffer. * write: remaining space in buffer */ int write_mode; /* last operation was a write */ } BFILE; void iob_close(BFILE *bf); void iob_flush(BFILE *bf); BFILE *iob_open_rd(char *path, int blksiz, int blkfactor); BFILE *iob_open_wt(char *path, int blksiz, int blkfactor); void iob_read(BFILE *bf, char *buf, int nbytes); void iob_write(BFILE *bf, char *buf, int nbytes); fis-gtm-V7.0-005/sr_unix/iob_close.c0000755000032200000250000000157714342376330016160 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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. * * * ****************************************************************/ /* iob_close - buffered I/O with GT.M error signaling void iob_close(BFILE *bf) bf: file to close */ #include "mdef.h" #include #include "gtm_unistd.h" #include "error.h" #include "iob.h" #include "gtmio.h" void iob_close(BFILE *bf) { int rc; iob_flush(bf); CLOSEFILE_RESET(bf->fd, rc); /* resets "bf->fd" to FD_INVALID */ if (-1 == rc) rts_error(VARLSTCNT(1) errno); free(bf->buf); free(bf->path); free(bf); } fis-gtm-V7.0-005/sr_unix/iob_flush.c0000644000032200000250000000522114342376330016157 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* iob_flush - buffered I/O with GT.M error signaling flush I/O buffer. No action is taken if the last operation was not a write. void iob_flush(BFILE *bf) BFILE *bf: file to flush Note: It is assumed that the write buffer is full. Exactly one record of size bf->bufsiz is written. */ #include "mdef.h" #include "gtm_string.h" #include #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_stdio.h" #include "gtm_stat.h" #include "error.h" #include "iob.h" #include "gtmio.h" error_def(ERR_IOEOF); void iob_flush(BFILE *bf) { ssize_t nwritten, nrewritten; ssize_t nbytes; int rc; if (!bf->write_mode) return; if (bf->remaining == bf->bufsiz) /* return if nothing to write */ return; if (bf->remaining) /* set remaining bytes to null */ { memset(bf->bptr, 0, bf->remaining); /* bytes to transfer, rounded up to the nearest block */ nbytes = (((bf->bptr - bf->buf) + bf->blksiz - 1) / bf->blksiz) * bf->blksiz; } else nbytes = bf->bufsiz; DOWRITERL(bf->fd, bf->buf, nbytes, nwritten); #ifdef DEBUG_IOB PRINTF("iob_flush:\t\twrite(%d, %x, %d) = %d\n",bf->fd,bf->buf,nbytes,nwritten); #endif bf->bptr = bf->buf; bf->remaining = bf->bufsiz; if (nwritten < nbytes) { CLOSEFILE_RESET(bf->fd, rc); /* resets "bf->fd" to FD_INVALID */ #ifdef SCO if (nwritten == -1) { if (errno == ENXIO) nwritten = 0; else { rts_error_csa(NULL, VARLSTCNT(1) errno); return; } } #else if (nwritten == -1) { rts_error_csa(NULL, VARLSTCNT(1) errno); return; } #endif RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_IOEOF); /* if we continued from here, assume that this is a magnetic tape and we have loaded the next volume. Re-open and finish the write operation. */ while (FD_INVALID == (bf->fd = OPEN3(bf->path,bf->oflag,bf->mode))) rts_error_csa(NULL, VARLSTCNT(1) errno); DOWRITERL(bf->fd, bf->buf + nwritten, nbytes - nwritten, nrewritten); #ifdef DEBUG_IOB PRINTF("iob_flush:\t\twrite(%d, %x, %d) = %d\n", bf->fd, bf->buf, nbytes, nwritten); #endif if (nrewritten < nbytes - nwritten) { rts_error_csa(NULL, VARLSTCNT(1) errno); return; } } } fis-gtm-V7.0-005/sr_unix/iob_open_rd.c0000755000032200000250000000356514342376330016500 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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. * * * ****************************************************************/ /* iob_open - buffered I/O with GT.M error signaling Open a file in read-only mode. BFILE *iob_open_rd(char *path, int bufsiz) path: file to open blksiz: size of I/O block blkfactor: blocks to buffer for input (blocking factor) return value: File structure for use with other buffered I/O routines. */ #include "mdef.h" #include #include #include "gtm_string.h" #include "gtm_unistd.h" #include "gtm_fcntl.h" #include "gtm_stat.h" #include "iob.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif BFILE *iob_open_rd(path, blksiz, blkfactor) char *path; int blksiz; int blkfactor; { int fd; BFILE *file; #ifdef __MVS__ int realfiletag; /* Need the ERR_BADTAG and ERR_TEXT error_defs for the TAG_POLICY macro warning */ error_def(ERR_TEXT); error_def(ERR_BADTAG); #endif if (FD_INVALID == (fd = OPEN3(path,O_RDONLY,0))) return NULL; #ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(fd, TAG_BINARY, &realfiletag)) TAG_POLICY_GTM_PUTMSG(path, realfiletag, TAG_BINARY, errno); #endif file = malloc(SIZEOF(BFILE)); file->fd = fd; file->path = malloc(strlen(path) + 1); strcpy(file->path, path); file->oflag = O_RDONLY; file->mode = 0; file->blksiz = blksiz; file->bufsiz = blksiz * blkfactor; file->buf = malloc(file->bufsiz); file->bptr = file->buf; file->remaining = 0; file->write_mode = FALSE; return file; } fis-gtm-V7.0-005/sr_unix/iob_open_wt.c0000755000032200000250000000303214342376330016512 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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. * * * ****************************************************************/ /* iob_open - buffered I/O with GT.M error signaling Open a file in write-only mode. BFILE *iob_open_wt(char *path, int bufsiz) path: file to open blksiz: size of I/O block blkfactor: blocks to buffer for output (blocking factor) return value: File structure for use with other buffered I/O routines. */ #include "mdef.h" #include #include #include "gtm_string.h" #include "gtm_unistd.h" #include "gtm_fcntl.h" #include "gtm_stat.h" #include "iob.h" BFILE *iob_open_wt(path, blksiz, blkfactor) char *path; int blksiz; int blkfactor; { int fd; BFILE *file; if (FD_INVALID == (fd = OPEN3(path,O_WRONLY | O_CREAT,0))) return NULL; file = malloc(SIZEOF(BFILE)); file->fd = fd; file->path = malloc(strlen(path) + 1); strcpy(file->path, path); file->oflag = O_WRONLY | O_CREAT; file->mode = 0; file->blksiz = blksiz; file->bufsiz = blksiz * blkfactor; file->buf = malloc(file->bufsiz); file->bptr = file->buf; file->remaining = file->bufsiz; file->write_mode = TRUE; return file; } fis-gtm-V7.0-005/sr_unix/iob_read.c0000755000032200000250000000420514342376330015755 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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. * * * ****************************************************************/ /* iob_read - buffered I/O with GT.M error signaling void iob_read(BFILE *bf, void *buf, int nbytes) BFILE *bf: file to read buf: buffer to place data read; nbytes: bytes to read. */ #include "mdef.h" #include "gtm_string.h" #include #include "gtm_fcntl.h" #include "gtm_stdio.h" #include "gtm_unistd.h" #include "gtm_stat.h" #include "error.h" #include "iob.h" #include "gtmio.h" #include "gtmmsg.h" GBLREF uint4 restore_read_errno; error_def(ERR_IOEOF); void iob_read(BFILE *bf, char *buf, int nbytes) { int4 nread, nreread; int rc; if (bf->write_mode) { iob_flush(bf); bf->write_mode = FALSE; } # ifdef DEBUG_IOB PRINTF("iob_read:\tiob_read(%x, %x, %d), bf->remaining = %d\n", bf, buf, nbytes, bf->remaining); # endif while (nbytes > bf->remaining) { /* copy bytes remaining in buffer */ memcpy(buf, bf->bptr, bf->remaining); nbytes -= bf->remaining; buf += bf->remaining; /* refill */ DOREADRL(bf->fd, bf->buf, bf->bufsiz, nread); # ifdef DEBUG_IOB PRINTF("iob_read:\t\tread(%d, %x, %d) = %d\n", bf->fd, bf->buf, bf->bufsiz, nread); # endif bf->bptr = bf->buf; bf->remaining = nread; /* In case of error, set global variable "restore_read_errno" and return to caller ("mupip_restore") * for it to do cleanup before exiting. */ if ((-1 == nread) || (0 == nread) || (nread % bf->blksiz)) { restore_read_errno = ((-1 == nread) ? errno : ERR_IOEOF); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) restore_read_errno); CLOSEFILE_RESET(bf->fd, rc); /* resets "bf->fd" to FD_INVALID */ return; } } memcpy(buf, bf->bptr, nbytes); bf->bptr += nbytes; bf->remaining -= nbytes; } fis-gtm-V7.0-005/sr_unix/iob_write.c0000755000032200000250000000260114342376330016172 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /* iob_write - buffered I/O with GT.M error signaling void iob_write(BFILE *bf, void *buf, int nbytes) BFILE *bf: file to write buf: buffer to place data read; nbytes: bytes to write. */ #include "mdef.h" #include "gtm_string.h" #include "gtm_stdio.h" #include #include "error.h" #include "iob.h" void iob_write(BFILE *bf, char *buf, int nbytes) { int4 nwrite; error_def(ERR_IOEOF); if (!bf->write_mode) { bf->write_mode = TRUE; bf->remaining = bf->bufsiz; bf->bptr = bf->buf; } #ifdef DEBUG_IOB PRINTF("iob_write:\tiob_write(%x, %x, %d), bf->remaining = %d\n", bf, buf, nbytes, bf->remaining); #endif while (nbytes > bf->remaining) { /* fill buffer */ memcpy(bf->bptr, buf, bf->remaining); nbytes -= bf->remaining; buf += bf->remaining; bf->remaining = 0; /* empty */ iob_flush(bf); } memcpy(bf->bptr, buf, nbytes); bf->bptr += nbytes; bf->remaining -= nbytes; } fis-gtm-V7.0-005/sr_unix/ioff_open.c0000755000032200000250000000335214342376330016157 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 #include "gtm_unistd.h" #include "gtm_stat.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "io.h" #include "iormdef.h" #include "io_params.h" GBLREF io_pair io_curr_device; short ioff_open(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 msec_timeout) { io_desc *iod; d_rm_struct *d_rm; iod = dev_name->iod; assert((params) *(pp->str.addr) < (unsigned char) n_iops); assert(iod != 0); assert(iod->state >= 0 && iod->state < n_io_dev_states); assert(iod->type == ff); if (!(d_rm = (d_rm_struct *) iod->dev_sp)) { iod->dev_sp = (void*)malloc(SIZEOF(d_rm_struct)); memset(iod->dev_sp, 0, SIZEOF(d_rm_struct)); d_rm = (d_rm_struct *) iod->dev_sp; iod->state = dev_closed; d_rm->stream = FALSE; iod->width = DEF_RM_WIDTH; iod->length = DEF_RM_LENGTH; d_rm->recordsize = DEF_RM_RECORDSIZE; d_rm->def_width = d_rm->def_recsize = TRUE; d_rm->fixed = FALSE; d_rm->read_only = FALSE; d_rm->padchar = DEF_RM_PADCHAR; d_rm->inbuf = NULL; d_rm->outbuf = NULL; d_rm->read_fildes = FD_INVALID; } d_rm->fifo = TRUE; iod->type = rm; return iorm_open(dev_name, pp, fd, mspace, msec_timeout); } fis-gtm-V7.0-005/sr_unix/iopi_iocontrol.c0000644000032200000250000001251614342376330017242 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2008-2021 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. * * * ****************************************************************/ /* iopi_iocontrol.c */ #include "mdef.h" #include "gtm_unistd.h" #include "gtm_inet.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "io.h" #include "iormdef.h" #include "iotimer.h" #include "gt_timer.h" #include "gtm_caseconv.h" #include "min_max.h" #include "arit.h" #include "gtmio.h" /* define max strings for $zkey */ #define MAX_FIXED_STRING (2 * NUM_DEC_DG_2L) + 2 #define MAX_VAR_STRING NUM_DEC_DG_2L + 1 GBLREF io_pair io_curr_device; error_def(ERR_INVCTLMNE); error_def(ERR_IOERROR); void iopi_iocontrol(mstr *mn, int4 argcnt, va_list args) { char action[MAX_DEVCTL_LENGTH]; d_rm_struct *d_rm; int rc; d_rm = (d_rm_struct *) io_curr_device.out->dev_sp; /* WRITE /EOF only applies to PIPE devices. Sequential file and FIFO devices should be closed with CLOSE.*/ if (!d_rm->is_pipe) return; /* we should not get here unless there is some string length after write / */ assertpro((int)mn->len); if (0 == mn->len) return; lower_to_upper((uchar_ptr_t)&action[0], (uchar_ptr_t)mn->addr, MIN(mn->len, SIZEOF(action))); if (0 == memcmp(&action[0], "EOF", MIN(mn->len, SIZEOF(action)))) { /* Implement the write /EOF action. Close the output stream to force any blocked output to complete. * Doing a write /EOF closes the output file descriptor for the pipe device but does not close the * device. Since the M program could attempt this command more than once, check if the file descriptor * is already closed before the actual close. * Ignore the /EOF action if the device is read-only as this is a nop */ if (d_rm->read_only) return; if (FD_INVALID != d_rm->fildes) { /* A new line will be inserted by iorm_cond_wteol() if $X is non-zero, just like it is done in iorm_close.c. * After this call returns, * $X will be zero, which will keep iorm_readfl() from attempting an iorm_wteol() * in the fixed mode after the file descriptor has been closed. */ iorm_cond_wteol(io_curr_device.out); IORM_FCLOSE(d_rm, fildes, filstr); assert(FD_INVALID == d_rm->fildes); assert(NULL == d_rm->filstr); } } else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_INVCTLMNE); return; } void iopi_dlr_device(mstr *d) { io_desc *iod; /* We will default to the input device for setting $device, since pipe uses both */ iod = io_curr_device.in; PUT_DOLLAR_DEVICE_INTO_MSTR(iod, d); return; } void iopi_dlr_key(mstr *d) { io_desc *iod; int len; iod = io_curr_device.in; len = STRLEN(iod->dollar.key); /* verify internal buffer has enough space for $KEY string value */ assertpro((int)d->len > len); if (len > 0) memcpy(d->addr, iod->dollar.key, MIN(len, d->len)); d->len = len; return; } void iopi_dlr_zkey(mstr *d) { io_desc *iod; int len, save_errno; d_rm_struct *d_rm; char tname[MAX_FIXED_STRING]; gtm_int64_t record_num; /* record offset in fixed record file */ uint4 record_byte; /* byte offset in fixed record block */ boolean_t utf_active; off_t cur_position; iod = io_curr_device.in; d_rm = (d_rm_struct *)(iod->dev_sp); if (d_rm->fifo || d_rm->is_pipe || (2 >= d_rm->fildes)) d->len = 0; else { /* if last operation was a write we need to get the current location into file_pos */ if (RM_WRITE == d_rm->lastop) { /* need to do an lseek to get current location in file */ cur_position = lseek(d_rm->fildes, 0, SEEK_CUR); if ((off_t)-1 == cur_position) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"), RTS_ERROR_LITERAL("iopi_dlr_zkey()"), CALLFROM, save_errno); } else d_rm->file_pos = cur_position; } if (d_rm->fixed) { utf_active = gtm_utf8_mode ? (IS_UTF_CHSET(iod->ichset)) : FALSE; if (!utf_active) { /* for M mode the actual recordsize is in iod->width set by open with recordsize or use with width */ record_num = d_rm->file_pos / iod->width; record_byte = d_rm->file_pos % iod->width; } else { record_num = d_rm->file_pos / d_rm->recordsize; /* temporarily save bytes remaining to be read from record */ record_byte = (int4)(d_rm->inbuf_top - d_rm->inbuf_off); /* if partially empty then the offset is based on recordsize */ if (record_byte) { /* bytes read from the record is recordsize minus bytes remaining to be read from record */ record_byte = d_rm->recordsize - record_byte; /* decrement record_num since only partially filled */ record_num--; } } SNPRINTF(tname, MAX_FIXED_STRING, "%lld,%ld", record_num, record_byte); } else { record_num = d_rm->file_pos; SNPRINTF(tname, MAX_VAR_STRING, "%lld", record_num); } len = STRLEN(tname); /* verify internal buffer has enough space for $ZKEY string value */ assertpro((int)d->len > len); if (0 < len) memcpy(d->addr, tname, MIN(len,d->len)); d->len = len; } return; } fis-gtm-V7.0-005/sr_unix/iopi_open.c0000644000032200000250000006752614342376330016206 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2008-2021 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. * * * ****************************************************************/ #ifndef _REENTRANT # define _REENTRANT #endif #include "mdef.h" #include #include #include #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_stat.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_stdlib.h" #include "gtm_limits.h" #include "stringpool.h" #include "io.h" #include "iormdef.h" #include "io_params.h" #include "eintr_wrappers.h" #include "gtmio.h" #include "iosp.h" #include "jobsp.h" #include "have_crit.h" #include "fork_init.h" #include "restrict.h" #ifdef __MVS__ #include "gtm_zos_io.h" #include "gtm_zos_chset.h" #endif #include "send_msg.h" #include "wbox_test_init.h" #include "op.h" #include "indir_enum.h" #include "gtmxc_types.h" #include "gtm_filter_command.h" LITREF unsigned char io_params_size[]; ZOS_ONLY(GBLREF boolean_t gtm_tag_utf8_as_ascii;) GBLREF boolean_t gtm_pipe_child; GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF boolean_t gtm_dist_ok_to_use; GBLREF volatile boolean_t timer_in_handler; error_def(ERR_DEVOPENFAIL); error_def(ERR_RESTRICTEDOP); error_def(ERR_SYSCALL); error_def(ERR_TEXT); error_def(ERR_COMMFILTERERR); ZOS_ONLY(error_def(ERR_BADTAG);) enum { PARSE_OK = 1, /* the parse passed */ PARSE_FAIL, /* the parse failed but $PATH is defined in the environment */ PARSE_FAIL_NOPATH /* the parse failed and $PATH is undefined in the environment */ }; #define FREE_ALL \ MBSTART { \ if (NULL != copy_cmd_string) \ free(copy_cmd_string); \ if (NULL != temp) \ free(temp); \ if (NULL != buf) \ free(buf); \ if (NULL != dir_in_path) \ free(dir_in_path); \ if (NULL != command2) \ free(command2); \ } MBEND int parse_pipe(char *cmd_string, char *ret_token); /* The parse_pipe routine is to used to determine if all the commands in a pipe string are valid commands so * the iopi_open() routine can detect the problem prior to a fork. * * Command Parsing Limitations: * * All commands will be searched for in $PATH and in $gtm_dist and if not found will trap. Most pipe strings can be * entered with some minor exceptions. First, no parentheses around commands are allowed. An example of this which would * fail is: * * o p:(comm="(cd; pwd)":writeonly)::"pipe" * * This produces nothing different from: * * o p:(comm="cd; pwd":writeonly)::"pipe" * * This restriction does not include parentheses embedded in character strings as in: * * o p:(comm="echo ""(test)""":writeonly)::"pipe" (which would output: (test) ) * * or as parameters to a command as in: * * o p:(comm="tr -d '()'":writeonly)::"pipe" (which if passed (test) would output: test ) * * Second, commands must be valid commands - not aliases. Third, no shell built-in commands are allowed (unless a version * is found in the $PATH and $gtm_dist); with the exception of nohup and cd. In the case of nohup, the next token will be * the command to be looked for in $PATH and $gtm_dist. "cd" will be allowed, but no parsing for a command will occur * until the next "|" token - if there is one. In addition, environment variables may be used to define a path, or actual * paths may be given. * * The following are examples of valid open commands: * * 1. o a:(comm="tr e j | echoback":stderr=e:exception="g BADOPEN")::"pipe" * 2. o a:(comm="/bin/cat |& nl")::"pipe" * 3. o a:(comm="mupip integ mumps.dat")::"pipe" * 4. o a:(comm="$gtm_dist/mupip integ mumps.dat")::"pipe" * 5. o a:(comm="nohup cat")::"pipe" * 6. o p:(comm="cd ..; pwd | tr -d e":writeonly)::"pipe" * * In the first example the commands parsed are "tr" and "echoback". "echoback" is in the current directory which is * included in the $PATH. In the second example an explicit path is given for "cat", but "nl" is found via $PATH. * In the third example mupip will be found if it is in the $PATH or in $gtm_dist. In the fourth example the * $gtm_dist environment variable is explicitly used to find mupip. In the fifth example the "cat" after "nohup" * is used as the command looked up in $PATH. In the sixth example the default directory is moved up a level and * pwd is executed with the output piped to the tr command. The pwd command is not checked for existence, but the * "tr" command after the "|" is checked. */ int parse_pipe(char *cmd_string, char *ret_token) { char *str1, *str2, *str3; char *saveptr1, *saveptr2, *saveptr3; char *env_var; int env_inc; struct stat sb; char *path, path_buff[GTM_PATH_MAX]; char *temp = NULL; char *buf = NULL; char *dir_in_path = NULL; char *command2 = NULL; char *copy_cmd_string = NULL; char *token1; char *token2; char *token3; int notfound = FALSE; int ret_stat; int pathsize, path_len; int cmd_string_size; path = GETENV("PATH"); if (NULL != path) { path_len = STRLEN(path); if (GTM_PATH_MAX <= path_len) path_len = GTM_PATH_MAX - 1; memcpy(path_buff, path, path_len + 1); /* + 1 for null */ path = path_buff; dir_in_path = (char *)malloc(GTM_PATH_MAX); } cmd_string_size = STRLEN(cmd_string) + 1; pathsize = GTM_PATH_MAX + cmd_string_size; buf = (char *)malloc(pathsize); copy_cmd_string = (char *)malloc(cmd_string_size); command2 = (char *)malloc(cmd_string_size); temp = (char *)malloc(pathsize); memcpy(copy_cmd_string, cmd_string, cmd_string_size); /* guaranteed at least one token when we get here because it is checked right after iop_eol loop and * before this code is executed. */ for (str1 = copy_cmd_string; FALSE == notfound ; str1 = NULL) { /* separate into tokens in a pipe */ token1 = STRTOK_R(str1, "|", &saveptr1); if (NULL == token1) break; /* separate into tokens using space as delimiter */ /* if the first token is a non-alpha-numeric or if it is nohup then skip it */ /* and use the next one as a command to find */ memcpy(command2, token1, STRLEN(token1) + 1); for (str2 = command2; ; str2 = NULL) { token2 = STRTOK_R(str2, " >&;", &saveptr2); if (NULL != token2 && !strcmp(token2, "cd")) { /* if the command is cd then skip the rest before the next pipe */ token2 = NULL; break; } if (NULL != token2 && strcmp(token2, "nohup")) break; if (NULL == token2) break; } if (NULL == token2) continue; notfound = TRUE; if (NULL != strchr(token2, '/')) { /* if the first character is a $ sign then assume it is an environment variable used * like $gtm_dist/mupip. Get the environment variable and substitute it. */ notfound = TRUE; temp[0] = '\0'; if ('$' == *token2) { for (env_inc = 1; '/' != *(token2 + env_inc); env_inc++) temp[env_inc - 1] = *(token2 + env_inc); temp[env_inc - 1] = '\0'; env_var = GETENV(temp); if (NULL != env_var) { /* build a translated path to command */ assert(pathsize > (STRLEN(token2 + env_inc) + STRLEN(env_var))); SNPRINTF(temp, pathsize, "%s%s", env_var, token2 + env_inc); /* The command must be a regular file and executable */ STAT_FILE(temp, &sb, ret_stat); if (0 == ret_stat && (S_ISREG(sb.st_mode)) && (sb.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR))) notfound = FALSE; } } else { STAT_FILE(token2, &sb, ret_stat); if (0 == ret_stat && (S_ISREG(sb.st_mode)) && (sb.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR))) notfound = FALSE; } } else { /* look in $gtm_dist in case not explicitly listed or not in the $PATH variable */ if (gtm_dist_ok_to_use) { /* build a translated path to command */ SNPRINTF(temp, pathsize, "%s/%s", gtm_dist, token2); STAT_FILE(temp, &sb, ret_stat); if (0 == ret_stat && (S_ISREG(sb.st_mode)) && (sb.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR))) notfound = FALSE; else notfound = TRUE; } if (NULL != path) { /* search all the directories in the $PATH if defined */ memcpy(dir_in_path, path, path_len + 1); for (str3 = dir_in_path; TRUE == notfound; str3 = NULL) { token3 = STRTOK_R(str3, ":", &saveptr3); if (NULL == token3) break; SNPRINTF(buf, pathsize, "%s/%s", token3, token2); notfound = TRUE; /* The command must be a regular file and executable */ STAT_FILE(buf, &sb, ret_stat); if (0 == ret_stat && (S_ISREG(sb.st_mode)) && (sb.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR))) { notfound = FALSE; break; } } } } if (TRUE == notfound) { assert(GTM_PATH_MAX > (STRLEN(token2) + 1)); memcpy(ret_token, token2, STRLEN(token2) + 1); FREE_ALL; if (NULL != path) return(PARSE_FAIL); else return(PARSE_FAIL_NOPATH); } } FREE_ALL; return(PARSE_OK); } /* When we are in this routine dev_name is same as naml in io_open_try() */ #define PIPE_ERROR_INIT()\ {\ if (NULL != pcommand) free(pcommand);\ if (NULL != pshell) free(pshell);\ if (NULL != pstderr) free(pstderr);\ iod->type = rm;\ } #define INVALID_CMD "Invalid command string: " #define INVALID_CMD2 "$PATH undefined, Invalid command string: " #define PIPEOPENSTR "PIPEOPEN" short iopi_open(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout) { io_desc *iod; io_desc *stderr_iod; d_rm_struct *d_rm; io_log_name *stderr_naml; /* logical record for stderr device */ unsigned char ch; int param_cnt = 0; int p_offset = 0; int file_des_write; int file_des_read = 0; int file_des_read_stderr; int param_offset = 0; struct stat sb; int ret_stat; int cpid; int pfd_write[2]; int pfd_read[2]; int pfd_read_stderr[2]; enum pfield {PSHELL, PCOMMAND, PSTDERR}; int slen[3] = {0, 0, 0}; char *sparams[3] = {0, 0, 0}; char *pcommand = 0; char *pshell = 0; char *pshell_name; char *pstderr = 0; char *sh; int independent = FALSE; int parse = FALSE; int parse_result; int status_read; int return_stdout = TRUE; int return_stderr = FALSE; char ret_token[GTM_MAX_DIR_LEN]; char error_str[MAXDEVPARLEN + STR_LIT_LEN(INVALID_CMD2)]; int save_errno; int flags; int fcntl_res, rc; #ifdef __MVS__ gtm_chset_t read_chset, write_chset, stderr_chset = CHSET_EBCDIC; int read_cvt[2], write_cvt[2], dummy_cvt[2]; mstr chset_mstr; char *errmsg; boolean_t textflag; int ccsid, status, realfiletag; #endif gtm_string_t filtered_command; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; iod = dev_name->iod; while (iop_eol != *(pp->str.addr + p_offset)) { assert((params) *(pp->str.addr + p_offset) < (params)n_iops); switch ((ch = *(pp->str.addr + p_offset++))) { case iop_exception: DEF_EXCEPTION(pp, p_offset, iod); break; case iop_shell: slen[PSHELL] = (unsigned int)(unsigned char)*(pp->str.addr + p_offset); sparams[PSHELL] = (char *)(pp->str.addr + p_offset + 1); param_cnt++; break; case iop_command: slen[PCOMMAND] = (unsigned int)(unsigned char)*(pp->str.addr + p_offset); sparams[PCOMMAND] = (char *)(pp->str.addr + p_offset + 1); param_cnt++; break; case iop_stderr: slen[PSTDERR] = (unsigned int)(unsigned char)*(pp->str.addr + p_offset); sparams[PSTDERR] = (char *)(pp->str.addr + p_offset + 1); param_cnt++; break; case iop_independent: independent = TRUE; break; case iop_parse: parse = TRUE; break; case iop_writeonly: return_stdout = FALSE; break; default: break; } p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); } /* for z/OS, grab the chset from the device params to tag the pipe */ ZOS_ONLY(gtm_zos_iop_chset(dev_name, pp, &read_chset, &write_chset);) /* If no command device parameter or it is a null string then exit with error */ if (0 == slen[PCOMMAND] || 0 == sparams[PCOMMAND]) { PIPE_ERROR_INIT(); if (0 == sparams[PCOMMAND]) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("Missing command string")); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("Command string has no value")); } else { pcommand = malloc(slen[PCOMMAND] + 1); memcpy(pcommand, sparams[PCOMMAND], slen[PCOMMAND]); pcommand[slen[PCOMMAND]] = '\0'; if (TRUE == parse) { if (PARSE_OK != (parse_result = parse_pipe(pcommand, ret_token))) { PIPE_ERROR_INIT(); if (PARSE_FAIL == parse_result) SNPRINTF(error_str, MAXDEVPARLEN + STR_LIT_LEN(INVALID_CMD2), "%s%s", INVALID_CMD, ret_token); else SNPRINTF(error_str, MAXDEVPARLEN + STR_LIT_LEN(INVALID_CMD2), "%s%s", INVALID_CMD2, ret_token); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_STR(error_str)); } } } /* The above code is just syntax checking. Any actual operation should be below here. */ if (RESTRICTED(pipe_open)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_RESTRICTEDOP, 1, "OPEN PIPE"); /*Filter the command first, if required*/ if (RESTRICTED(pipe_filter)) { filtered_command = gtm_filter_command(pcommand, "PIPE"); if (filtered_command.length) { if (!strlen(filtered_command.address)) /*empty command returned*/ { PIPE_ERROR_INIT(); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_COMMFILTERERR, 4, LEN_AND_LIT(PIPEOPENSTR), LEN_AND_LIT("Empty return command")); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_COMMFILTERERR, 4, LEN_AND_LIT(PIPEOPENSTR),LEN_AND_LIT("Empty return command")); } free(pcommand); pcommand = (char*)malloc(filtered_command.length+1); memcpy(pcommand, filtered_command.address, filtered_command.length); pcommand[filtered_command.length] = '\0'; } else { PIPE_ERROR_INIT(); return FALSE; /* Error would have been printed in the filter execution routine */ } } /* check the shell device parameter before the fork/exec It is not required, but must not be null if entered */ if (sparams[PSHELL] && (0 == slen[PSHELL])) { PIPE_ERROR_INIT(); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("SHELL parameter has no value")); } else if (0 != slen[PSHELL]) { pshell = malloc(slen[PSHELL] + 1); memcpy(pshell, sparams[PSHELL], slen[PSHELL]); pshell[slen[PSHELL]] = '\0'; /* The shell must be a regular file and executable */ STAT_FILE(pshell, &sb, ret_stat); if (-1 == ret_stat || !(S_ISREG(sb.st_mode)) || !(sb.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR))) { save_errno = errno; assert(GTM_MAX_DIR_LEN - 1 >= STRLEN(pshell)); SNPRINTF(error_str, MAXDEVPARLEN + STR_LIT_LEN(INVALID_CMD2), "Invalid shell: %s", pshell); PIPE_ERROR_INIT(); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_STR(error_str)); } pshell_name = basename(pshell); } /* check the stderr device parameter before the fork/exec It is not required, but must not be null if entered */ if (sparams[PSTDERR] && (0 == slen[PSTDERR])) { PIPE_ERROR_INIT(); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("STDERR parameter has no value")); } else if (0 != slen[PSTDERR]) { pstderr = malloc(slen[PSTDERR] + 1); memcpy(pstderr, sparams[PSTDERR], slen[PSTDERR]); pstderr[slen[PSTDERR]] = '\0'; return_stderr = TRUE; } /* child to read from pfd_write[0] and parent to write to pfd_write[1] */ if (-1 == pipe(pfd_write)) { save_errno = errno; PIPE_ERROR_INIT(); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("PIPE - pipe(pfd_write) failed"), save_errno); } else { #ifdef __MVS__ PIPE_CVT_FROM_CHSET(write_chset, ccsid, textflag, write_cvt); /* only default and binary get tagged */ if (-1 == gtm_zos_set_tag(pfd_write[0], ccsid, textflag, TAG_FORCE, &realfiletag)) TAG_POLICY_SEND_MSG("PIPE - ccsid tag(pfd_write) failed", errno, realfiletag, ccsid); #endif iod->dollar.zeof = FALSE; } /* child to write stdout (and possibly stderr) to pfd_read[1] and parent to read from pfd_read[0] */ if (return_stdout) { if (-1 == pipe(pfd_read)) { save_errno = errno; PIPE_ERROR_INIT(); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("PIPE - pipe(pfd_read) failed"), save_errno); } else { #ifdef __MVS__ PIPE_CVT_FROM_CHSET(read_chset, ccsid, textflag, read_cvt); if (-1 == gtm_zos_set_tag(pfd_read[0], ccsid, textflag, TAG_FORCE, &realfiletag)) TAG_POLICY_SEND_MSG("PIPE - ccsid tag(pfd_read) failed", errno, realfiletag, ccsid); #endif file_des_read = pfd_read[0]; } } /* child to write to pfd_read_stderr[1] and parent to read from pfd_read_stderr[0] */ if (return_stderr) { if (-1 == pipe(pfd_read_stderr)) { save_errno = errno; PIPE_ERROR_INIT(); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("PIPE - pipe(pfd_read_stderr) failed"), save_errno); } else { #ifdef __MVS__ /* stderr is always tagged EBCDIC. there are no UTF8 or untagged ASCII error messages */ PIPE_CVT_FROM_CHSET(stderr_chset, ccsid, textflag, dummy_cvt); if (-1 == gtm_zos_set_tag(pfd_read_stderr[0], ccsid, textflag, TAG_FORCE, &realfiletag)) TAG_POLICY_SEND_MSG("PIPE - ccsid tag(pfd_read_stderr) failed", errno, realfiletag, ccsid); #endif file_des_read_stderr = pfd_read_stderr[0]; } } file_des_write = pfd_write[1]; /* Do the fork and exec but BEFORE that do a FFLUSH(NULL) to make sure any fclose (done in io_rundown * in the child process) does not affect file offsets in this (parent) process' file descriptors */ FFLUSH(NULL); FORK(cpid); if (-1 == cpid) { save_errno = errno; PIPE_ERROR_INIT(); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("PIPE - fork() failed"), save_errno); /* BYPASSOK */ } if (0 == cpid) { /* in child */ int ret; CLOSEFILE_RESET(pfd_write[1], rc); /* Close unused write end; Resets "pfd_write[1]" to FD_INVALID */ ZOS_ONLY(if (-1 == gtm_zos_setcvtmode(pfd_write[0], write_cvt[CHILDCVT])) TAG_POLICY_SEND_MSG("PIPE - conversion mode(pfd_write) failed in child", errno, realfiletag, ccsid)); if (return_stdout) { CLOSEFILE_RESET(pfd_read[0], rc); /* Close unused read end for stdout return; Reset fd to FD_INVALID */ ZOS_ONLY(if (-1 == gtm_zos_setcvtmode(pfd_read[1], read_cvt[CHILDCVT])) TAG_POLICY_SEND_MSG("PIPE - conversion mode(pfd_read) failed in child", errno, realfiletag, ccsid)); } if (return_stderr) CLOSEFILE_RESET(pfd_read_stderr[0], rc); /* Close unused read end for stderr return; Set fd to FD_INVALID */ /* need to let iorm_close() know not to reap any pipe devices during io_rundown. They should only be reaped by the parent process. */ gtm_pipe_child = TRUE; /* rundown io devices in child */ io_rundown(RUNDOWN_EXCEPT_STD); /* do common cleanup in child */ ojchildioclean(); CLOSEFILE(0, rc); /* stdin becomes pfd_write[0] */ if (DEBUG_ONLY(WBTEST_ENABLED(WBTEST_BADDUP_PIPE_STDIN) ||) (-1 == dup2(pfd_write[0], 0))) { save_errno = errno; PIPE_ERROR_INIT(); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("PIPE - dup2(pfd_write[0]) failed in child")); UNDERSCORE_EXIT(ERR_DEVOPENFAIL); } if (return_stdout) { /* stdout becomes pfd_read[1] */ CLOSEFILE(1, rc); if (DEBUG_ONLY(WBTEST_ENABLED(WBTEST_BADDUP_PIPE_STDOUT) ||) (-1 == dup2(pfd_read[1], 1))) { save_errno = errno; PIPE_ERROR_INIT(); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("PIPE - dup2(pfd_read[1],1) failed in child")); UNDERSCORE_EXIT(ERR_DEVOPENFAIL); } /* stderr also becomes pfd_read[1] if return_stderr is false*/ if (FALSE == return_stderr) { CLOSEFILE(2, rc); if (DEBUG_ONLY(WBTEST_ENABLED(WBTEST_BADDUP_PIPE_STDERR1) ||) (-1 == dup2(pfd_read[1], 2))) { save_errno = errno; PIPE_ERROR_INIT(); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("PIPE - dup2(pfd_read[1],2) failed in child")); UNDERSCORE_EXIT(ERR_DEVOPENFAIL); } } } if (return_stderr) { CLOSEFILE(2, rc); if (DEBUG_ONLY(WBTEST_ENABLED(WBTEST_BADDUP_PIPE_STDERR2) ||) (-1 == dup2(pfd_read_stderr[1], 2))) { save_errno = errno; PIPE_ERROR_INIT(); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("PIPE - dup2(pfd_read_stderr[1],2) failed in child")); UNDERSCORE_EXIT(ERR_DEVOPENFAIL); } } if (0 == slen[PSHELL]) { /* get SHELL environment */ sh = GETENV("SHELL"); /* use bourne shell as default if SHELL not set in environment*/ if (!sh) { ret = EXECL("/bin/sh", "sh", "-c", pcommand, (char *)NULL); } else if (!WBTEST_ENABLED(WBTEST_BADEXEC_PIPE_PROCESS)) ret = EXECL(sh, basename(sh), "-c", pcommand, (char *)NULL); else ret = -1; } else ret = EXECL(pshell, pshell_name, "-c", pcommand, (char *)NULL); if (-1 == ret) { save_errno = errno; PIPE_ERROR_INIT(); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("PIPE - execl() failed in child")); UNDERSCORE_EXIT(-1); } } else { /* in parent */ DEBUG_ONLY(TREF(fork_without_child_wait) = TRUE); /* set variable to help assert in "relinkctl_rundown()" */ CLOSEFILE_RESET(pfd_write[0], rc); /* Close unused read end; Resets "pfd_write[0]" to FD_INVALID */ SETFDCLOEXECALWAYS(pfd_write[1]); ZOS_ONLY(if (-1 == gtm_zos_setcvtmode(pfd_write[1], write_cvt[PARENTCVT])) TAG_POLICY_SEND_MSG("PIPE - conversion mode(pfd_write) failed", errno, realfiletag, ccsid)); /* if returning stdout then close unused write end */ if (return_stdout) { CLOSEFILE_RESET(pfd_read[1], rc); /* resets "pfd_read[1]" to FD_INVALID */ SETFDCLOEXECALWAYS(pfd_read[0]); ZOS_ONLY(if(-1 == gtm_zos_setcvtmode(pfd_read[0], read_cvt[PARENTCVT])) TAG_POLICY_SEND_MSG("PIPE - conversion mode(pfd_read) failed", errno, realfiletag, ccsid)); } if (return_stderr) { CLOSEFILE_RESET(pfd_read_stderr[1], rc); /* resets "pfd_read_stderr[1]" to FD_INVALID */ SETFDCLOEXECALWAYS(pfd_read_stderr[0]); } } assert((params) *(pp->str.addr) < (unsigned char) n_iops); assert(0 != iod); assert(0 <= iod->state && iod->state < n_io_dev_states); assert(pi == iod->type); if (!(d_rm = (d_rm_struct *) iod->dev_sp)) { iod->dev_sp = (void*)malloc(SIZEOF(d_rm_struct)); memset(iod->dev_sp, 0, SIZEOF(d_rm_struct)); d_rm = (d_rm_struct *) iod->dev_sp; /* save child's process id for reaping on close */ d_rm->pipe_pid = cpid; /* save read file descriptor for stdout return if set */ if (file_des_read) { FDOPEN(d_rm->read_filstr, file_des_read, "r"); if (NULL == d_rm->read_filstr) { save_errno = errno; PIPE_ERROR_INIT(); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("Error in stream open"), save_errno); } d_rm->read_fildes = file_des_read; } else d_rm->read_fildes = FD_INVALID; SNPRINTF(&iod->dollar.key[0], DD_BUFLEN, "%d", cpid); /* save in pipe specific structure for $KEY access */ memcpy(iod->dollar.device, "0", SIZEOF("0")); iod->state = dev_closed; d_rm->stream = FALSE; iod->width = DEF_RM_WIDTH; iod->length = DEF_RM_LENGTH; d_rm->independent = independent; d_rm->parse = parse; d_rm->recordsize = DEF_RM_RECORDSIZE; d_rm->def_width = d_rm->def_recsize = TRUE; d_rm->fixed = FALSE; d_rm->read_only = FALSE; d_rm->padchar = DEF_RM_PADCHAR; d_rm->inbuf = NULL; d_rm->outbuf = NULL; d_rm->dev_param_pairs.num_pairs = param_cnt; if (slen[PSHELL]) { MALLOC_CPY_LIT(d_rm->dev_param_pairs.pairs[param_offset].name, "SHELL="); /* add quotes around the command field */ d_rm->dev_param_pairs.pairs[param_offset].definition = malloc(STRLEN(pshell) + 5); SNPRINTF(d_rm->dev_param_pairs.pairs[param_offset++].definition, STRLEN(pshell) + 5, "\"%s\"", pshell); if (NULL != pshell) free(pshell); } /* We checked for command existence earlier so no need to check again */ MALLOC_CPY_LIT(d_rm->dev_param_pairs.pairs[param_offset].name, "COMMAND="); /* add quotes around the command field */ d_rm->dev_param_pairs.pairs[param_offset].definition = malloc(STRLEN(pcommand) + 5); SNPRINTF(d_rm->dev_param_pairs.pairs[param_offset++].definition, STRLEN(pcommand) + 5, "\"%s\"", pcommand); if (NULL != pcommand) free(pcommand); if (slen[PSTDERR]) { MALLOC_CPY_LIT(d_rm->dev_param_pairs.pairs[param_offset].name, "STDERR="); /* add quotes around the stderr field */ d_rm->dev_param_pairs.pairs[param_offset].definition = malloc(STRLEN(pstderr) + 5); SNPRINTF(d_rm->dev_param_pairs.pairs[param_offset].definition, STRLEN(pstderr) + 5, "\"%s\"", pstderr); if (NULL != pstderr) free(pstderr); } } d_rm->is_pipe = TRUE; iod->type = rm; if (return_stderr) { /* We will create an mstr (using MSTR_DEF in mdef.h) and call get_log_name(..,INSERT) like op_open does to create and insert io_log_name * in io_root_log_name. */ MSTR_DEF(stderr_mstr, slen[PSTDERR], sparams[PSTDERR]); stderr_naml = get_log_name(&stderr_mstr, INSERT); stderr_iod = stderr_naml->iod = (io_desc *)malloc(SIZEOF(io_desc)); memset((char*)stderr_naml->iod, 0, SIZEOF(io_desc)); stderr_naml->iod->pair.in = stderr_naml->iod; stderr_naml->iod->pair.out = stderr_naml->iod; stderr_naml->iod->trans_name = stderr_naml; (stderr_iod->pair.in)->trans_name = stderr_naml; { io_desc *io_ptr; d_rm_struct *in_d_rm; io_ptr = (stderr_iod->pair.in); if (!(in_d_rm = (d_rm_struct *) io_ptr->dev_sp)) { io_ptr->dev_sp = (void*)malloc(SIZEOF(d_rm_struct)); memset(io_ptr->dev_sp, 0, SIZEOF(d_rm_struct)); in_d_rm = (d_rm_struct *) io_ptr->dev_sp; io_ptr->state = dev_closed; in_d_rm->stream = d_rm->stream; io_ptr->width = iod->width; io_ptr->length = iod->length; io_ptr->wrap = DEFAULT_IOD_WRAP; io_ptr->dollar.x = 0; io_ptr->dollar.y = 0; io_ptr->dollar.za = 0; io_ptr->dollar.zb[0] = 0; io_ptr->dollar.key[0] = 0; io_ptr->dollar.device[0] = 0; if (io_ptr->dollar.devicebuffer) free(io_ptr->dollar.devicebuffer); io_ptr->dollar.devicebuffer = NULL; io_ptr->disp_ptr = iod->disp_ptr; in_d_rm->fixed = d_rm->fixed; in_d_rm->read_only = TRUE; in_d_rm->padchar = d_rm->padchar; in_d_rm->inbuf = d_rm->inbuf; in_d_rm->outbuf = d_rm->outbuf; in_d_rm->stderr_parent = iod; in_d_rm->read_fildes = FD_INVALID; /* checked in iorm_get_bom to get correct file descriptor */ } in_d_rm->is_pipe = TRUE; io_ptr->type = rm; status_read = iorm_open(stderr_naml, pp, file_des_read_stderr, mspace, timeout); if (TRUE == status_read) (stderr_iod->pair.in)->state = dev_open; else if (dev_open == ( stderr_iod->pair.out)->state) (stderr_iod->pair.in)->state = dev_closed; } d_rm->stderr_child = stderr_iod; } flags = 0; FCNTL2(file_des_write, F_GETFL, flags); if (0 > flags) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, errno); FCNTL3(file_des_write, F_SETFL, (flags | O_NONBLOCK), fcntl_res); if (0 > fcntl_res) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, errno); return iorm_open(dev_name, pp, file_des_write, mspace, timeout); } fis-gtm-V7.0-005/sr_unix/iorm_close.c0000644000032200000250000002774214342376330016354 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include #include "gtm_unistd.h" #include "gtm_stat.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "io.h" #include "iormdef.h" #include "io_params.h" #include "gtm_signal.h" #include "gtmio.h" #include "iosp.h" #include "stringpool.h" #include "iotimer.h" #include "gt_timer.h" #include "wake_alarm.h" #include "copy.h" #include "error.h" #include "is_file_identical.h" GBLREF io_pair io_curr_device; GBLREF io_pair io_std_device; GBLREF boolean_t gtm_pipe_child; GBLREF volatile bool out_of_time; GBLREF int process_exiting; error_def(ERR_SYSCALL); error_def(ERR_DEVPARMTOOSMALL); error_def(ERR_IOERROR); LITREF unsigned char io_params_size[]; void iorm_close(io_desc *iod, mval *pp) { d_rm_struct *rm_ptr; d_rm_struct *in_rm_ptr; d_rm_struct *stderr_rm_ptr; unsigned char c; char *path, *path2; int stat_res; int save_errno; int fstat_res; struct stat statbuf, fstatbuf; int p_offset; pid_t done_pid; #ifdef _BSD union wait wait_status; #else int4 wait_status; #endif int rc, status; unsigned int *dollarx_ptr; unsigned int *dollary_ptr; char *savepath2 = 0; int path2len; boolean_t rm_destroy = TRUE; boolean_t rm_rundown = FALSE; boolean_t rn_overwrite = FALSE; TID timer_id; int4 pipe_timeout = 2; /* default timeout in sec waiting for waitpid */ off_t cur_position; boolean_t ch_set; sigset_t empty_set, old_set; boolean_t use_timer; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert (iod->type == rm); if (iod->state != dev_open) { remove_rms(iod); return; } ESTABLISH_GTMIO_CH(&iod->pair, ch_set); rm_ptr = (d_rm_struct *)iod->dev_sp; #ifdef __MVS__ /* on zos if it is a fifo device then point to the pair.out for $X and $Y */ if (rm_ptr->fifo) { dollarx_ptr = &(iod->pair.out->dollar.x); dollary_ptr = &(iod->pair.out->dollar.y); } else #endif { dollarx_ptr = &(iod->dollar.x); dollary_ptr = &(iod->dollar.y); } iorm_use(iod,pp); /* We do not want a NEWLINE to be issued by the middle process. */ if (!gtm_pipe_child) iorm_cond_wteol(iod); p_offset = 0; while (*(pp->str.addr + p_offset) != iop_eol) { switch (c = *(pp->str.addr + p_offset++)) { case iop_delete: path = iod->trans_name->dollar_io; FSTAT_FILE(rm_ptr->fildes, &fstatbuf, fstat_res); if (-1 == fstat_res) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); } STAT_FILE(path, &statbuf, stat_res); if (-1 == stat_res) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); } if (CYGWIN_ONLY(rm_ptr->fifo ||) fstatbuf.st_ino == statbuf.st_ino) if (UNLINK(path) == -1) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); } break; case iop_noinsert: rn_overwrite = TRUE; break; case iop_replace: /* iop_rename is intended to protect against accidental replacement. * iop_replace(REPLACE on CLOSE) provides an overwrite functionality. */ rn_overwrite = TRUE; /* WARNING fallthrough */ case iop_rename: path = iod->trans_name->dollar_io; path2 = (char*)(pp->str.addr + p_offset + 1); FSTAT_FILE(rm_ptr->fildes, &fstatbuf, fstat_res); if (-1 == fstat_res) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); } STAT_FILE(path, &statbuf, stat_res); if (-1 == stat_res) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); } if (CYGWIN_ONLY(rm_ptr->fifo ||) fstatbuf.st_ino == statbuf.st_ino) { /* make a copy of path2 so we can null terminate it */ path2len = (int)*((unsigned char *)(pp->str.addr + p_offset)); assert(stringpool.free >= stringpool.base); ENSURE_STP_FREE_SPACE(path2len + 1); savepath2 = (char *)stringpool.free; memcpy(savepath2, path2, path2len); savepath2[path2len] = '\0'; stringpool.free += path2len + 1; assert(stringpool.free >= stringpool.base); assert(stringpool.free <= stringpool.top); if (is_file_identical(path, savepath2)) break; /* rename or replace to itself a noop */ if (rn_overwrite) UNLINK(savepath2); /* overwrite deletes now, but rename alone requires delete 1st */ test_rename_gap: if (-1 == LINK(path, savepath2)) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); } if (-1 == UNLINK(path)) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); } } break; case iop_destroy: rm_destroy = TRUE; break; case iop_nodestroy: rm_destroy = FALSE; break; case iop_rundown: rm_rundown = TRUE; break; case iop_timeout: GET_LONG(pipe_timeout, (pp->str.addr + p_offset)); if (1 > pipe_timeout) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DEVPARMTOOSMALL); break; default: break; } p_offset += ( io_params_size[c]==IOP_VAR_SIZE ? (unsigned char)(*(pp->str.addr + p_offset) + 1) : io_params_size[c] ); } if (iod->pair.in != iod) assert(iod->pair.out == iod); if (iod->pair.out != iod) assert(iod->pair.in == iod); iod->state = dev_closed; /* save no_destroy for disk device */ if ((FALSE == rm_destroy) && !rm_ptr->fifo && !rm_ptr->is_pipe && (2 < rm_ptr->fildes)) { rm_ptr->no_destroy = TRUE; /* We can write anywhere in the file so need to save current file pointer for re-open if last operation was a write*/ /* if last operation was a write then set file_pos to position after write */ if (RM_WRITE == rm_ptr->lastop) { /* need to do an lseek to get current location in file */ cur_position = lseek(rm_ptr->fildes, (off_t)0, SEEK_CUR); if ((off_t)-1 == cur_position) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"), RTS_ERROR_LITERAL("iorm_close()"), CALLFROM, save_errno); } else rm_ptr->file_pos = cur_position; } } else { iod->dollar.zeof = FALSE; *dollarx_ptr = 0; *dollary_ptr = 0; rm_ptr->lastop = RM_NOOP; if (rm_ptr->inbuf) { free(rm_ptr->inbuf); rm_ptr->inbuf = NULL; } if (rm_ptr->outbuf) { free(rm_ptr->outbuf); rm_ptr->outbuf = NULL; } if (rm_ptr->tmp_buffer) { free(rm_ptr->tmp_buffer); rm_ptr->tmp_buffer = NULL; } if (iod->dollar.devicebuffer) { free(iod->dollar.devicebuffer); iod->dollar.devicebuffer = NULL; } } /* Do the close first. If the fclose is done first and we are being called from io_rundown just prior to the execv * in a newly JOBbed off process, the fclose does an implied FFLUSH which is known to do an lseek which resets * the file pointers of any open (flat) files in the parent due to an archane interaction between child and parent * processes prior to an execv call. The fclose (for stream files) will fail but it will clean up structures orphaned * by the CLOSEFILE_RESET. */ /* Close the fildes unless this is a direct close of the stderr device */ if (!rm_ptr->stderr_parent) { /* Before closing a pipe device file descriptor, check if the fd was already closed as part of a "write /eof". * If so, do not attempt the close now. Only need to free up the device structures. */ IORM_FCLOSE(rm_ptr, fildes, filstr); assert(FD_INVALID == rm_ptr->fildes); assert(NULL == rm_ptr->filstr); /* if this is a pipe and read_fildes and read_filstr are set then close them also */ IORM_FCLOSE(rm_ptr, read_fildes, read_filstr); assert(FD_INVALID == rm_ptr->read_fildes); assert(NULL == rm_ptr->read_filstr); } /* Reap the forked shell process if a pipe - it will be a zombie otherwise. */ if (rm_ptr->pipe_pid > 0) { /* Do not reap if in a child process creating a new pipe or if in parent and independent is set. */ if (FALSE == gtm_pipe_child) { if (!rm_ptr->independent) { if (!process_exiting) { /* Find out whether timers are available. If not, instead of scheduling one to interrupt * waitpid, simply invoke it with WNOHANG flag instead. Note that SIGPROCMASK below is * operating on an empty signal set and so is not blocking any signals. */ sigemptyset(&empty_set); SIGPROCMASK(SIG_BLOCK, &empty_set, &old_set, rc); use_timer = !sigismember(&old_set, SIGALRM); } else use_timer = FALSE; out_of_time = FALSE; if (use_timer) { /* Start timer for pipe_timeout seconds to wait for reap of close via waitpid. If this times * out, set dollar_zclose = -99. */ timer_id = (TID)iorm_close; start_timer(timer_id, pipe_timeout * 1000, wake_alarm, 0, NULL); } do { status = 0; done_pid = waitpid(rm_ptr->pipe_pid, &status, use_timer ? 0 : WNOHANG); /* BYPASSOK */ } while (((pid_t)-1 == done_pid) && (EINTR == errno) && (!out_of_time)); if (((pid_t)-1 == done_pid) && (!out_of_time)) { save_errno = errno; if (use_timer) cancel_timer(timer_id); SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("waitpid"), CALLFROM, save_errno); } if (out_of_time || (!use_timer && (0 == done_pid))) { TREF(dollar_zclose) = -99; } else { /* Not an error and did not timeout. */ assert(!use_timer || (done_pid == rm_ptr->pipe_pid)); /* Cancel timer since it did not timeout. */ if (use_timer) cancel_timer(timer_id); # ifdef _BSD assert(SIZEOF(wait_status) == SIZEOF(int4)); /* The WIF* macros expect a union wait_stat as an argument on BSD. */ wait_status.w_status = status; # else wait_status = status; # endif if (WIFEXITED(wait_status)) { /* Normal exit. */ TREF(dollar_zclose) = WEXITSTATUS(wait_status); } else if (WIFSIGNALED(wait_status)) { /* Change signal to negative for distinguishing from normal exit codes. */ TREF(dollar_zclose) = -WTERMSIG(wait_status); } else { /* Set any other non-normal status to -98. */ TREF(dollar_zclose) = -98; } } } } if (rm_ptr->stderr_child) { /* If the stderr device is the current device then set it to the principal device. This is handled in op_close for a direct close of the stderr device. */ if (io_curr_device.in == rm_ptr->stderr_child) { io_curr_device.in = io_std_device.in; io_curr_device.out = io_std_device.out; } /* have to make it look open in case it was closed directly */ /* reset the stderr_parent field so the fildes will be closed */ rm_ptr->stderr_child->state = dev_open; stderr_rm_ptr = (d_rm_struct *)rm_ptr->stderr_child->dev_sp; stderr_rm_ptr->stderr_parent = 0; iorm_close(rm_ptr->stderr_child,pp); } } if ((rm_destroy || rm_ptr->is_pipe || rm_ptr->fifo) && !rm_rundown) remove_rms (iod); REVERT_GTMIO_CH(&iod->pair, ch_set); return; assertpro(FALSE); /* ensure we never reach the goto below */ goto test_rename_gap; /* Dummy goto makes the compiler satisfied the label used as a breakpoint in a test is legit. * The test_rename_gap label is used by gde/gdeput test. */ } fis-gtm-V7.0-005/sr_unix/iorm_flush.c0000755000032200000250000000217114342376330016360 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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_stdio.h" #include "io.h" #include "iormdef.h" /* The rm device is unbuffered, so the flush operation is a no-op. */ void iorm_flush(io_desc *iod) { return; } void iorm_cond_wteol(io_desc *iod) { d_rm_struct *rm_ptr; unsigned int *dollarx_ptr; rm_ptr = (d_rm_struct *)iod->dev_sp; #ifdef __MVS__ /* on zos if it is a fifo device then point to the pair.out for $X and $Y */ if (rm_ptr->fifo) dollarx_ptr = &(iod->pair.out->dollar.x); else #endif dollarx_ptr = &(iod->dollar.x); if (*dollarx_ptr && rm_ptr->lastop == RM_WRITE && !iod->dollar.za) iorm_wteol(1, iod); return; } fis-gtm-V7.0-005/sr_unix/iorm_get.c0000644000032200000250000007336014342376330016023 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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 #include "gtm_fcntl.h" #include "gtm_stdio.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "io.h" #include "iotimer.h" #include "iormdef.h" #include "stringpool.h" #include "gt_timer.h" #include "gtmio.h" #include "have_crit.h" #include "eintr_wrappers.h" #include "wake_alarm.h" #include "min_max.h" #include "deferred_events_queue.h" #ifdef UTF8_SUPPORTED #include "gtm_utf8.h" #include "gtm_conv.h" #endif #include "gtmcrypt.h" #include "error.h" GBLREF io_pair io_curr_device; GBLREF spdesc stringpool; GBLREF volatile bool out_of_time; GBLREF boolean_t gtm_utf8_mode; GBLREF volatile int4 outofband; LITREF mstr chset_names[]; error_def(ERR_BOMMISMATCH); /* check initial len bytes of buffer for a BOM * if CHSET_UTF16, set ichset to BOM or BE if no BOM * return the number of bytes to skip */ int gtm_utf_bomcheck(io_desc *iod, gtm_chset_t *chset, unsigned char *buffer, int len) { int bom_bytes = 0; boolean_t ch_set; ESTABLISH_RET_GTMIO_CH(&iod->pair, -1, ch_set); switch (*chset) { case CHSET_UTF8: assert(UTF8_BOM_LEN <= len); if (!memcmp(buffer, UTF8_BOM, UTF8_BOM_LEN)) bom_bytes = UTF8_BOM_LEN; break; case CHSET_UTF16BE: case CHSET_UTF16LE: case CHSET_UTF16: assert(UTF16BE_BOM_LEN <= len); assert(UTF16BE_BOM_LEN == UTF16LE_BOM_LEN); bom_bytes = UTF16BE_BOM_LEN; if (!memcmp(buffer, UTF16BE_BOM, UTF16BE_BOM_LEN)) { if (CHSET_UTF16LE == *chset) { iod->dollar.za = ZA_IO_ERR; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_BOMMISMATCH, 4, chset_names[CHSET_UTF16BE].len, chset_names[CHSET_UTF16BE].addr, chset_names[CHSET_UTF16LE].len, chset_names[CHSET_UTF16LE].addr); } else if (CHSET_UTF16 == *chset) *chset = CHSET_UTF16BE; } else if (!memcmp(buffer, UTF16LE_BOM, UTF16LE_BOM_LEN)) { if (CHSET_UTF16BE == *chset) { iod->dollar.za = ZA_IO_ERR; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_BOMMISMATCH, 4, chset_names[CHSET_UTF16LE].len, chset_names[CHSET_UTF16LE].addr, chset_names[CHSET_UTF16BE].len, chset_names[CHSET_UTF16BE].addr); } else if (CHSET_UTF16 == *chset) *chset = CHSET_UTF16LE; } else if (CHSET_UTF16 == *chset) { /* no BOM so set to BE and read initial bytes */ *chset = CHSET_UTF16BE; /* no BOM so set to BE */ bom_bytes = 0; } else bom_bytes = 0; /* no BOM found */ break; default: assertpro(FALSE); } REVERT_GTMIO_CH(&iod->pair, ch_set); return bom_bytes; } /* When we get to this routine it is guaranteed that rm_ptr->done_1st_read is FALSE. */ int iorm_get_bom_fol(io_desc *io_ptr, int4 *tot_bytes_read, int4 *msec_timeout, boolean_t timed, boolean_t *bom_timeout, ABS_TIME end_time) { int status, fildes; int4 bytes2read, bytes_read, reclen, bom_bytes2read, bom_bytes_read; gtm_chset_t chset; d_rm_struct *rm_ptr; int4 sleep_left; int4 sleep_time; ABS_TIME current_time, time_left; boolean_t ch_set; ESTABLISH_RET_GTMIO_CH(&io_ptr->pair, -1, ch_set); status = 0; rm_ptr = (d_rm_struct *)(io_ptr->dev_sp); fildes = rm_ptr->fildes; chset = io_ptr->ichset; assert(UTF16BE_BOM_LEN == UTF16LE_BOM_LEN); bom_bytes2read = (int4)((CHSET_UTF8 == chset) ? UTF8_BOM_LEN : UTF16BE_BOM_LEN); PIPE_DEBUG(PRINTF("enter iorm_get_bom_fl: bom_buf_cnt: %d bom_bytes2read: %d bom_read_one_done: %d chset: %d\n", rm_ptr->bom_buf_cnt,bom_bytes2read,rm_ptr->bom_read_one_done,chset); DEBUGPIPEFLUSH;); /* rms-file device in follow mode */ if (timed) { /* check iorm_get_bom_fol.... for msc_timeout */ /* recalculate msec_timeout and sleep_left as &end_time - ¤t_time */ if (0 < *msec_timeout) { /* get the current time */ sys_get_curr_time(¤t_time); time_left = sub_abs_time(&end_time, ¤t_time); if (0 > time_left.at_sec) { *msec_timeout = -1; *bom_timeout = TRUE; } else *msec_timeout = (int4)(time_left.at_sec * MILLISECS_IN_SEC + DIVIDE_ROUND_UP(time_left.at_usec, MICROSECS_IN_MSEC)); /* make sure it terminates with bom_timeout */ if (!*bom_timeout && !*msec_timeout) *msec_timeout = 1; sleep_left = *msec_timeout; } else sleep_left = 0; } /* if zeof is set in follow mode then ignore any previous zeof */ if (TRUE == io_ptr->dollar.zeof) io_ptr->dollar.zeof = FALSE; do { /* in follow mode a read will return an EOF if no more bytes are available*/ status = read(fildes, &rm_ptr->bom_buf[rm_ptr->bom_buf_cnt], bom_bytes2read - rm_ptr->bom_buf_cnt); if (0 < status) /* we read some chars */ { /* Decrypt the BOM bytes. */ if (rm_ptr->input_encrypted) READ_ENCRYPTED_DATA(rm_ptr, io_ptr->trans_name, &rm_ptr->bom_buf[rm_ptr->bom_buf_cnt], status, NULL); rm_ptr->read_occurred = TRUE; rm_ptr->bom_buf_cnt += status; } else if (0 == status) /* end of file */ { if ((TRUE == timed) && (0 >= sleep_left)) { *bom_timeout = TRUE; *tot_bytes_read = rm_ptr->bom_buf_cnt; break; } /* if a timed read, sleep the minimum of 100 ms and sleep_left. If not a timed read then just sleep 100 ms */ if (TRUE == timed) { /* recalculate msec_timeout and sleep_left as &end_time - ¤t_time */ /* get the current time */ sys_get_curr_time(¤t_time); time_left = sub_abs_time(&end_time, ¤t_time); if (0 > time_left.at_sec) { *msec_timeout = -1; *bom_timeout = TRUE; } else *msec_timeout = (int4)(time_left.at_sec * MILLISECS_IN_SEC + DIVIDE_ROUND_UP(time_left.at_usec, MICROSECS_IN_MSEC)); /* make sure it terminates with bom_timeout */ if ((!*bom_timeout) && (!*msec_timeout)) *msec_timeout = 1; sleep_left = *msec_timeout; sleep_time = MIN(100,sleep_left); } else sleep_time = 100; if (0 < sleep_time) SHORT_SLEEP(sleep_time); if (outofband) { REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return 0; } continue; /* for now try and read again if eof or no input ready */ } else /* error returned */ { if (errno != EINTR) break; } } while (rm_ptr->bom_buf_cnt < bom_bytes2read); PIPE_DEBUG(PRINTF("iorm_get_bom_fl: status: %d, bom_buf_cnt: %d\n", status,rm_ptr->bom_buf_cnt); DEBUGPIPEFLUSH;); if (rm_ptr->bom_buf_cnt >= bom_bytes2read) { PIPE_DEBUG(PRINTF("iorm_get_bom_fl do bomcheck: bom_buf_cnt: %d bom_buf: %o\n", rm_ptr->bom_buf_cnt,rm_ptr->bom_buf[0]); DEBUGPIPEFLUSH;); rm_ptr->bom_buf_off = gtm_utf_bomcheck(io_ptr, &io_ptr->ichset, rm_ptr->bom_buf, rm_ptr->bom_buf_cnt); /* Will need to know this for seek and other places */ rm_ptr->bom_checked = TRUE; /* save number of bom bytes for use in seek */ if (rm_ptr->bom_buf_off) rm_ptr->bom_num_bytes = rm_ptr->bom_buf_off; rm_ptr->file_pos += rm_ptr->bom_buf_off; /* If there is BOM bytes increment file position by bom_buf_off */ } else if (CHSET_UTF16 == chset) /* if UTF16 default to UTF16BE */ io_ptr->ichset = CHSET_UTF16BE; if (chset != io_ptr->ichset) { /* UTF16 changed to UTF16BE or UTF16LE */ chset = io_ptr->ichset; get_chset_desc(&chset_names[chset]); } /* if outofband is not set then we are done getting the bom */ if (!outofband && !*bom_timeout) rm_ptr->done_1st_read = TRUE; REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return 0; } /* If we are in this routine then it is a fixed utf disk read with rm_ptr->follow = TRUE */ int iorm_get_fol(io_desc *io_ptr, int4 *tot_bytes_read, int4 *msec_timeout, boolean_t timed, boolean_t zint_restart, boolean_t *follow_timeout, ABS_TIME end_time) { boolean_t ret; char inchar, *temp; unsigned char *pad_ptr, *nextmb, padchar, padcharray[2]; int fcntl_res, save_errno; int4 bytes2read, bytes_read, char_bytes_read, add_bytes, reclen, bytes_already_read, tmp_bytes_read; wint_t utf_code; d_rm_struct *rm_ptr; int4 status, from_bom; gtm_chset_t chset; int fildes; int bytes_count = 0; int4 sleep_left; int4 sleep_time; boolean_t bom_timeout = FALSE; ABS_TIME current_time, time_left; boolean_t ch_set; ESTABLISH_RET_GTMIO_CH(&io_ptr->pair, -1, ch_set); assert (io_ptr->state == dev_open); rm_ptr = (d_rm_struct *)(io_ptr->dev_sp); fildes = rm_ptr->fildes; assert(gtm_utf8_mode ? (IS_UTF_CHSET(io_ptr->ichset)) : FALSE); assert(rm_ptr->fixed); if (!zint_restart) { /* if last operation timed out with some chars then only read up to recordsize bytes */ bytes_already_read = rm_ptr->inbuf_top - rm_ptr->inbuf; if (bytes_already_read) rm_ptr->last_was_timeout = 1; else rm_ptr->last_was_timeout = 0; if ((bytes_already_read == rm_ptr->recordsize) || (rm_ptr->fol_bytes_read == rm_ptr->recordsize)) { bytes2read = rm_ptr->recordsize; rm_ptr->inbuf_pos = rm_ptr->inbuf_top = rm_ptr->inbuf_off = rm_ptr->inbuf; bytes_already_read = 0; rm_ptr->fol_bytes_read = 0; rm_ptr->last_was_timeout = 0; } else bytes2read = rm_ptr->recordsize - bytes_already_read; /* since it is not a restart bytes_already_read happened in a previous read so save it */ rm_ptr->orig_bytes_already_read = bytes_already_read; } else { bytes_already_read = rm_ptr->inbuf_top - rm_ptr->inbuf; bytes2read = rm_ptr->recordsize - bytes_already_read; /* skip past if bom already read */ if (rm_ptr->done_1st_read) rm_ptr->inbuf_pos = rm_ptr->inbuf_top; else rm_ptr->inbuf_pos = rm_ptr->inbuf_off = rm_ptr->inbuf; } PIPE_DEBUG(PRINTF("iorm_get_fol: bytes2read: %d, bytes_already_read: %d, last_was_timeout: %d, zint_restart: %d\n", bytes2read, bytes_already_read, rm_ptr->last_was_timeout, zint_restart); DEBUGPIPEFLUSH;); PIPE_DEBUG(PRINTF("iorm_get_fol: inbuf: 0x%08lx, top: 0x%08lx, off: 0x%08lx\n", rm_ptr->inbuf, rm_ptr->inbuf_top, rm_ptr->inbuf_off); DEBUGPIPEFLUSH;); bytes_read = 0; assert(rm_ptr->bufsize >= rm_ptr->recordsize); errno = status = 0; chset = io_ptr->ichset; if (!rm_ptr->done_1st_read) { PIPE_DEBUG(PRINTF("do iorm_get_bom_fol: bytes2read: %d\n", bytes2read); DEBUGPIPEFLUSH;) status = iorm_get_bom_fol(io_ptr, tot_bytes_read, msec_timeout, timed, &bom_timeout, end_time); if (!rm_ptr->done_1st_read && outofband) { PIPE_DEBUG(PRINTF("return since iorm_get_bom_fol went outofband\n"); DEBUGPIPEFLUSH;); REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return 0; } if (TRUE == bom_timeout) *follow_timeout = TRUE; chset = io_ptr->ichset; /* UTF16 will have changed to UTF16BE or UTF16LE */ } assert(CHSET_UTF16 != chset); PIPE_DEBUG(PRINTF("iorm_get_fol: bom_buf_cnt: %d bom_buf_off: %d\n",rm_ptr->bom_buf_cnt,rm_ptr->bom_buf_off ); DEBUGPIPEFLUSH;); if ((0 <= status) && (rm_ptr->bom_buf_cnt > rm_ptr->bom_buf_off)) { PIPE_DEBUG(PRINTF("move bom: status: %d\n", status); DEBUGPIPEFLUSH;); from_bom = MIN((rm_ptr->bom_buf_cnt - rm_ptr->bom_buf_off), bytes2read); memcpy(rm_ptr->inbuf, &rm_ptr->bom_buf[rm_ptr->bom_buf_off], from_bom); rm_ptr->bom_buf_off += from_bom; bytes2read -= from_bom; /* now in buffer */ rm_ptr->inbuf_pos += from_bom; bytes_read = from_bom; rm_ptr->file_pos += from_bom; status = 0; } if ((0 <= status) && (0 < bytes2read)) { PIPE_DEBUG(PRINTF("iorm_get_fol: bytes2read after bom: %d\n", bytes2read); DEBUGPIPEFLUSH;); if (timed) { /* recalculate msec_timeout and sleep_left as &end_time - ¤t_time */ if (0 < *msec_timeout) { /* get the current time */ sys_get_curr_time(¤t_time); time_left = sub_abs_time(&end_time, ¤t_time); if (0 > time_left.at_sec) { *msec_timeout = -1; *follow_timeout = TRUE; } else *msec_timeout = (int4)(time_left.at_sec * MILLISECS_IN_SEC + DIVIDE_ROUND_UP(time_left.at_usec, MICROSECS_IN_MSEC)); /* make sure it terminates with follow_timeout */ if (!*follow_timeout && !*msec_timeout) *msec_timeout = 1; sleep_left = *msec_timeout; } else sleep_left = 0; } /* if zeof is set in follow mode then ignore any previous zeof */ if (TRUE == io_ptr->dollar.zeof) io_ptr->dollar.zeof = FALSE; temp = (char *)rm_ptr->inbuf_pos; do { /* in follow mode a read will return an EOF if no more bytes are available */ status = read(fildes, temp, (int)bytes2read - bytes_count); if (0 < status) /* we read some chars */ { /* Decrypt whatever we read. */ if (rm_ptr->input_encrypted) READ_ENCRYPTED_DATA(rm_ptr, io_ptr->trans_name, temp, status, NULL); rm_ptr->read_occurred = TRUE; *tot_bytes_read += status; bytes_count += status; temp = temp + status; } else if (0 == status) /* end of file */ { if ((TRUE == timed) && (0 >= sleep_left)) { /* need to set tot_bytes_read and status for timeout */ *follow_timeout = TRUE; break; } /* if a timed read, sleep the minimum of 100 ms and sleep_left. If not a timed read then just sleep 100 ms */ if (TRUE == timed) { /* recalculate msec_timeout and sleep_left as &end_time - ¤t_time */ /* get the current time */ sys_get_curr_time(¤t_time); time_left = sub_abs_time(&end_time, ¤t_time); if (0 > time_left.at_sec) { *msec_timeout = -1; *follow_timeout = TRUE; } else *msec_timeout = (int4)(time_left.at_sec * MILLISECS_IN_SEC + DIVIDE_ROUND_UP(time_left.at_usec, MICROSECS_IN_MSEC)); /* make sure it terminates with follow_timeout */ if (!*follow_timeout && !*msec_timeout) *msec_timeout = 1; sleep_left = *msec_timeout; sleep_time = MIN(100,sleep_left); } else sleep_time = 100; if (0 < sleep_time) SHORT_SLEEP(sleep_time); if (outofband) break; continue; /* for now try and read again if eof or no input ready */ } else /* error returned */ { if (errno != EINTR) break; } } while (bytes_count < bytes2read); status = bytes_count; } /* if outofband and we didn't finish the read, just adjust inbuf_top and inbuf_pos and return 0 */ if (outofband && (status < bytes2read)) { PIPE_DEBUG(PRINTF("iorm_get_fol: outofband: bytes2read: %d status: %d tot_bytes_read: %d\n", bytes2read, status, *tot_bytes_read); DEBUGPIPEFLUSH;); if (0 > status) { rm_ptr->inbuf_top = rm_ptr->inbuf_pos += *tot_bytes_read; REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return 0; } else { rm_ptr->inbuf_top = rm_ptr->inbuf_pos += status; if ((rm_ptr->inbuf_pos - rm_ptr->inbuf_off) < rm_ptr->recordsize) { REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return 0; } } } /* if some bytes were read prior to timeout then process them as if no timeout occurred */ if (0 > status && *tot_bytes_read && ((FALSE == timed) || (TRUE == *follow_timeout))) status = *tot_bytes_read; if (0 > status) { bytes_read = 0; if (TRUE == *follow_timeout) status = -2; } else if (bytes_read || status || (bytes_already_read && (TRUE == timed))) { bytes_read += status; rm_ptr->file_pos += status; padchar = rm_ptr->padchar; if ((CHSET_UTF16LE == chset) || (CHSET_UTF16BE == chset)) { /* strip 2-byte PADCHAR in UTF-16LE or UTF-16BE from tail of line */ /* It's possible that only one byte is read if this is an interrupt restart one byte from the width * In that case it's not an error if already_read is non-zero, but we have to adjust bytes_read differently. */ PIPE_DEBUG(PRINTF("iorm_get_fol: bytes_read: %d bytes_already_read: %d, zint_restart: %d\n", bytes_read,bytes_already_read,zint_restart); DEBUGPIPEFLUSH;); tmp_bytes_read = bytes_read + bytes_already_read; rm_ptr->fol_bytes_read = tmp_bytes_read; assert(tmp_bytes_read >= 2); if (CHSET_UTF16LE == chset) { padcharray[0] = padchar; padcharray[1] = '\0'; } else { padcharray[0] = '\0'; padcharray[1] = padchar; } for (pad_ptr = rm_ptr->inbuf + tmp_bytes_read - 2; 0 < tmp_bytes_read && rm_ptr->inbuf <= pad_ptr; pad_ptr-=2) { PIPE_DEBUG(PRINTF("pad 16 loop: bytes_read: %d pad_ptr: 0x%08lx\n", bytes_read,pad_ptr); DEBUGPIPEFLUSH;); if ((padcharray[0] == pad_ptr[0]) && (padcharray[1] == pad_ptr[1])) tmp_bytes_read -= 2; else break; } bytes_read = tmp_bytes_read; } else { /* strip 1-byte PADCHAR in UTF-8 from tail of line */ bytes_read = bytes_read + bytes_already_read; /* store total of bytes read */ rm_ptr->fol_bytes_read = bytes_read; assert(CHSET_UTF8 == chset); for (pad_ptr = rm_ptr->inbuf + bytes_read - 1; 0 < bytes_read && rm_ptr->inbuf <= pad_ptr; pad_ptr--) { PIPE_DEBUG(PRINTF("pad 8 loop: bytes_read: %d pad_ptr: 0x%08lx\n", bytes_read,pad_ptr); DEBUGPIPEFLUSH;); if (*pad_ptr == padchar) bytes_read--; else break; } } } rm_ptr->inbuf_top = rm_ptr->inbuf_pos = rm_ptr->inbuf + bytes_read; PIPE_DEBUG(PRINTF("last_was_timeout: %d \n",rm_ptr->last_was_timeout); DEBUGPIPEFLUSH;); if (!zint_restart || (TRUE == rm_ptr->last_was_timeout)) { rm_ptr->inbuf_off = rm_ptr->inbuf + rm_ptr->orig_bytes_already_read; /* back out bytes_already_read mod */ bytes_read = bytes_read - bytes_already_read + rm_ptr->orig_bytes_already_read; rm_ptr->orig_bytes_already_read = 0; rm_ptr->last_was_timeout = 0; } else rm_ptr->inbuf_off = rm_ptr->inbuf; REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return (0 <= status ? bytes_read : status); } /* When we get to this routine it is guaranteed that rm_ptr->done_1st_read is FALSE. */ int iorm_get_bom(io_desc *io_ptr, int *blocked_in, boolean_t ispipe, int flags, int4 *tot_bytes_read, TID timer_id, int4 *msec_timeout, boolean_t pipe_zero_timeout) { int fildes, status = 0; int4 bytes2read, bytes_read, reclen, bom_bytes2read, bom_bytes_read; gtm_chset_t chset; d_rm_struct *rm_ptr; boolean_t pipe_or_fifo = FALSE; boolean_t ch_set; ESTABLISH_RET_GTMIO_CH(&io_ptr->pair, -1, ch_set); rm_ptr = (d_rm_struct *)(io_ptr->dev_sp); if (rm_ptr->is_pipe || rm_ptr->fifo) pipe_or_fifo = TRUE; /* If it is a pipe and it's the stdout returned then we need to get the read file descriptor from rm_ptr->read_fildes. * Additionally, z/OS saves its FIFO read file descriptors in read_fildes, so retrieve it. */ if ((rm_ptr->is_pipe ZOS_ONLY(|| rm_ptr->fifo)) && (FD_INVALID != rm_ptr->read_fildes)) fildes = rm_ptr->read_fildes; else fildes = rm_ptr->fildes; chset = io_ptr->ichset; assert(UTF16BE_BOM_LEN == UTF16LE_BOM_LEN); bom_bytes2read = (int4)((CHSET_UTF8 == chset) ? UTF8_BOM_LEN : UTF16BE_BOM_LEN); PIPE_DEBUG(PRINTF("enter iorm_get_bom: bom_buf_cnt: %d bom_bytes2read: %d bom_read_one_done: %d chset: %d\n", rm_ptr->bom_buf_cnt,bom_bytes2read,rm_ptr->bom_read_one_done,chset); DEBUGPIPEFLUSH;); for (; rm_ptr->bom_buf_cnt < bom_bytes2read; ) { PIPE_DEBUG(PRINTF("loop iorm_get_bom: bom_buf_cnt: %d\n", rm_ptr->bom_buf_cnt); DEBUGPIPEFLUSH;); /* Last argument is passed as FALSE(UTF_VAR_PF) since we are not doing CHUNK_SIZE read here */ /* read the first byte only if these conditions are met. Disk will still read bom size or eof. */ if (pipe_or_fifo && (chset == CHSET_UTF8) && (FALSE == rm_ptr->bom_read_one_done)) { DOREADRLTO2(fildes, &rm_ptr->bom_buf[rm_ptr->bom_buf_cnt], 1, out_of_time, blocked_in, ispipe, flags, status, tot_bytes_read, timer_id, msec_timeout, pipe_zero_timeout, FALSE, pipe_or_fifo); /* Decrypt the read bytes even if they turned out to not contain a BOM. */ if ((0 < status) || ((0 > status) && (0 < *tot_bytes_read))) { if (rm_ptr->input_encrypted) READ_ENCRYPTED_DATA(rm_ptr, io_ptr->trans_name, &rm_ptr->bom_buf[rm_ptr->bom_buf_cnt], status > 0 ? status : *tot_bytes_read, NULL); rm_ptr->read_occurred = TRUE; } PIPE_DEBUG(PRINTF("iorm_get_bom UTF8 DOREADRLTO2: status: %d\n", status); DEBUGPIPEFLUSH;); /* if status is gt 0 we got one char so see if it's a bom */ if (0 < status) { rm_ptr->bom_read_one_done = TRUE; /* unless there are 2 characters to follow it can't be a utf8 bom */ if (2 != UTF8_MBFOLLOW(&rm_ptr->bom_buf[rm_ptr->bom_buf_cnt])) { rm_ptr->bom_buf_cnt += status; break; } } } else { PIPE_DEBUG(PRINTF("DOREADRLTO2: bom_bytes2read: %d, bom_buf_cnt: %d toread: %d\n", bom_bytes2read, rm_ptr->bom_buf_cnt, bom_bytes2read - rm_ptr->bom_buf_cnt); DEBUGPIPEFLUSH;); DOREADRLTO2(fildes, &rm_ptr->bom_buf[rm_ptr->bom_buf_cnt], bom_bytes2read - rm_ptr->bom_buf_cnt, out_of_time, blocked_in, ispipe, flags, status, tot_bytes_read, timer_id, msec_timeout, pipe_zero_timeout, FALSE, pipe_or_fifo); /* Decrypt the read bytes even if they turned out to not contain a BOM. */ if ((0 < status) || ((0 > status) && (0 < *tot_bytes_read))) { if (rm_ptr->input_encrypted) READ_ENCRYPTED_DATA(rm_ptr, io_ptr->trans_name, &rm_ptr->bom_buf[rm_ptr->bom_buf_cnt], status > 0 ? status : *tot_bytes_read, NULL); rm_ptr->read_occurred = TRUE; } } if (0 > status) { if (errno == EINTR && out_of_time) status = -2; if (pipe_or_fifo && outofband) { PIPE_DEBUG(PRINTF("iorm_get_bom: status: %d, bom_buf_cnt: %d tot_bytes_read: %d\n", status, rm_ptr->bom_buf_cnt,tot_bytes_read); DEBUGPIPEFLUSH;); rm_ptr->bom_buf_cnt += *tot_bytes_read; } return status; } else { if (0 == status) break; rm_ptr->bom_buf_cnt += status; } } PIPE_DEBUG(PRINTF("iorm_get_bom: status: %d, bom_buf_cnt: %d\n", status,rm_ptr->bom_buf_cnt); DEBUGPIPEFLUSH;); if (rm_ptr->bom_buf_cnt >= bom_bytes2read) { PIPE_DEBUG(PRINTF("iorm_get_bom do bomcheck: bom_buf_cnt: %d bom_buf: %o\n", rm_ptr->bom_buf_cnt,rm_ptr->bom_buf[0]); DEBUGPIPEFLUSH;); rm_ptr->bom_buf_off = gtm_utf_bomcheck(io_ptr, &io_ptr->ichset, rm_ptr->bom_buf, rm_ptr->bom_buf_cnt); /* Will need to know this for seek and other places */ rm_ptr->bom_checked = TRUE; /* save number of bom bytes for use in seek */ if (rm_ptr->bom_buf_off) rm_ptr->bom_num_bytes = rm_ptr->bom_buf_off; rm_ptr->file_pos += rm_ptr->bom_buf_off; /* If there is BOM bytes increment file position by bom_buf_off */ } else if (CHSET_UTF16 == chset) /* if UTF16 default to UTF16BE */ io_ptr->ichset = CHSET_UTF16BE; if (chset != io_ptr->ichset) { /* UTF16 changed to UTF16BE or UTF16LE */ chset = io_ptr->ichset; get_chset_desc(&chset_names[chset]); } /* if outofband is not set or its a disk read then we are done with getting the bom */ if (!(pipe_or_fifo && outofband)) rm_ptr->done_1st_read = TRUE; REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return 0; } int iorm_get(io_desc *io_ptr, int *blocked_in, boolean_t ispipe, int flags, int4 *tot_bytes_read, TID timer_id, int4 *msec_timeout, boolean_t pipe_zero_timeout, boolean_t zint_restart) { boolean_t ret; char inchar, *temp; unsigned char *pad_ptr, *nextmb, padchar, padcharray[2]; int fcntl_res, save_errno; int4 bytes2read, bytes_read, char_bytes_read, add_bytes, reclen, bytes_already_read, tmp_bytes_read; wint_t utf_code; d_rm_struct *rm_ptr; int4 status, from_bom; gtm_chset_t chset; int fildes; boolean_t pipe_or_fifo = FALSE; boolean_t ch_set; ESTABLISH_RET_GTMIO_CH(&io_ptr->pair, -1, ch_set); assert (io_ptr->state == dev_open); rm_ptr = (d_rm_struct *)(io_ptr->dev_sp); if (rm_ptr->is_pipe || rm_ptr->fifo) pipe_or_fifo = TRUE; /* If it is a pipe and it's the stdout returned then we need to get the read file descriptor from rm_ptr->read_fildes. * Additionally, z/OS saves its FIFO read file descriptors in read_fildes, so retrieve it. */ if ((rm_ptr->is_pipe ZOS_ONLY(|| rm_ptr->fifo)) && (FD_INVALID != rm_ptr->read_fildes)) fildes = rm_ptr->read_fildes; else fildes = rm_ptr->fildes; assert(gtm_utf8_mode ? (IS_UTF_CHSET(io_ptr->ichset)) : FALSE); assert(rm_ptr->fixed); if (!zint_restart) { bytes2read = rm_ptr->recordsize; bytes_already_read = 0; rm_ptr->inbuf_pos = rm_ptr->inbuf_top = rm_ptr->inbuf_off = rm_ptr->inbuf; } else { bytes_already_read = rm_ptr->inbuf_top - rm_ptr->inbuf; bytes2read = rm_ptr->recordsize - bytes_already_read; /* skip past if bom already read */ if (rm_ptr->done_1st_read) rm_ptr->inbuf_pos = rm_ptr->inbuf_top; else rm_ptr->inbuf_pos = rm_ptr->inbuf_off = rm_ptr->inbuf; } PIPE_DEBUG(PRINTF("pipeget: bytes2read: %d, zint_restart: %d\n", bytes2read,zint_restart); DEBUGPIPEFLUSH;); bytes_read = 0; assert(rm_ptr->bufsize >= rm_ptr->recordsize); errno = status = 0; chset = io_ptr->ichset; if (!rm_ptr->done_1st_read) { PIPE_DEBUG(PRINTF("do iorm_get_bom: bytes2read: %d\n", bytes2read); DEBUGPIPEFLUSH;) /* need to check for BOM *//* smw do this later perhaps or first */ status = iorm_get_bom(io_ptr, blocked_in, ispipe, flags, tot_bytes_read, timer_id, msec_timeout, pipe_zero_timeout); if (!rm_ptr->done_1st_read && (pipe_or_fifo && outofband)) { PIPE_DEBUG(PRINTF("return since iorm_get_bom went outofband\n"); DEBUGPIPEFLUSH;); REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return 0; } chset = io_ptr->ichset; /* UTF16 will have changed to UTF16BE or UTF16LE */ } assert(CHSET_UTF16 != chset); PIPE_DEBUG(PRINTF("iorm_get: bom_buf_cnt: %d bom_buf_off: %d\n",rm_ptr->bom_buf_cnt,rm_ptr->bom_buf_off ); DEBUGPIPEFLUSH;); if ((0 <= status) && (rm_ptr->bom_buf_cnt > rm_ptr->bom_buf_off)) { PIPE_DEBUG(PRINTF("move bom: status: %d\n", status); DEBUGPIPEFLUSH;); from_bom = MIN((rm_ptr->bom_buf_cnt - rm_ptr->bom_buf_off), bytes2read); memcpy(rm_ptr->inbuf, &rm_ptr->bom_buf[rm_ptr->bom_buf_off], from_bom); rm_ptr->bom_buf_off += from_bom; bytes2read -= from_bom; /* now in buffer */ rm_ptr->inbuf_pos += from_bom; bytes_read = from_bom; rm_ptr->file_pos += from_bom; status = 0; } /* if pipe or fifo and outofband then we didn't finish so return 0 */ if (pipe_or_fifo && outofband) { PIPE_DEBUG(PRINTF("pipeget: bytes2read: %d bytes_already_read: %d, zint_restart: %d\n", bytes2read,bytes_already_read,zint_restart); DEBUGPIPEFLUSH;); REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return 0; } if ((0 <= status) && (0 < bytes2read)) { /* If the device is a PIPE, and we read at least one character, start a timer using timer_id passed by iorm_readfl, * which cancels the timer if the read completes before the time expires. The FALSE argument to DOREADRLTO2 * indicates the read is not for a CHUNK_SIZE. */ PIPE_DEBUG(PRINTF("pipeget: bytes2read after bom: %d\n", bytes2read); DEBUGPIPEFLUSH;); DOREADRLTO2(fildes, rm_ptr->inbuf_pos, (int)bytes2read, out_of_time, blocked_in, ispipe, flags, status, tot_bytes_read, timer_id, msec_timeout, pipe_zero_timeout, FALSE, pipe_or_fifo); if ((0 < status) || ((0 > status) && (0 < *tot_bytes_read))) { /* Decrypt. */ if (rm_ptr->input_encrypted) READ_ENCRYPTED_DATA(rm_ptr, io_ptr->trans_name, rm_ptr->inbuf_pos, status > 0 ? status : *tot_bytes_read, NULL); rm_ptr->read_occurred = TRUE; } } /* if pipe or fifo and outofband then we didn't finish so just adjust inbuf_top and inbuf_pos and return 0 */ if (pipe_or_fifo && outofband) { PIPE_DEBUG(PRINTF("pipeget outofband: bytes2read: %d status: %d tot_bytes_read: %d\n", bytes2read, status, *tot_bytes_read); DEBUGPIPEFLUSH;); if (0 > status) { rm_ptr->inbuf_top = rm_ptr->inbuf_pos += *tot_bytes_read; REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return 0; } else { rm_ptr->inbuf_top = rm_ptr->inbuf_pos += status; if ((rm_ptr->inbuf_pos - rm_ptr->inbuf_off) < rm_ptr->recordsize) { REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return 0; } } } /* if some bytes were read prior to timeout then process them as if no timeout occurred */ if (0 > status && *tot_bytes_read && (!*msec_timeout || (errno == EINTR && out_of_time))) status = *tot_bytes_read; if (0 > status) { bytes_read = 0; if (errno == EINTR && out_of_time) status = -2; } else if (bytes_read || status) { bytes_read += status; rm_ptr->file_pos += status; padchar = rm_ptr->padchar; if ((CHSET_UTF16LE == chset) || (CHSET_UTF16BE == chset)) { /* strip 2-byte PADCHAR in UTF-16LE or UTF-16BE from tail of line */ /* It's possible that only one byte is read if this is an interrupt restart one byte from the width * In that case it's not an error if already_read is non-zero, but we have to adjust bytes_read differently. */ PIPE_DEBUG(PRINTF("pipeget: bytes_read: %d bytes_already_read: %d, zint_restart: %d\n", bytes_read,bytes_already_read,zint_restart); DEBUGPIPEFLUSH;); if (zint_restart && bytes_already_read) { tmp_bytes_read = bytes_read + bytes_already_read; } else { tmp_bytes_read = bytes_read; } assert(tmp_bytes_read >= 2); if (CHSET_UTF16LE == chset) { padcharray[0] = padchar; padcharray[1] = '\0'; } else { padcharray[0] = '\0'; padcharray[1] = padchar; } for (pad_ptr = rm_ptr->inbuf + tmp_bytes_read - 2; 0 < tmp_bytes_read && rm_ptr->inbuf <= pad_ptr; pad_ptr-=2) { PIPE_DEBUG(PRINTF("pad 16 loop: bytes_read: %d pad_ptr: %sx\n", bytes_read,pad_ptr); DEBUGPIPEFLUSH;); if ((padcharray[0] == pad_ptr[0]) && (padcharray[1] == pad_ptr[1])) tmp_bytes_read -= 2; else break; } bytes_read = tmp_bytes_read; } else { /* strip 1-byte PADCHAR in UTF-8 from tail of line */ if (zint_restart && bytes_already_read) bytes_read = bytes_read + bytes_already_read; assert(CHSET_UTF8 == chset); for (pad_ptr = rm_ptr->inbuf + bytes_read - 1; 0 < bytes_read && rm_ptr->inbuf <= pad_ptr; pad_ptr--) { PIPE_DEBUG(PRINTF("pad 8 loop: bytes_read: %d pad_ptr: %sx\n", bytes_read,pad_ptr); DEBUGPIPEFLUSH;); if (*pad_ptr == padchar) bytes_read--; else break; } } } rm_ptr->inbuf_top = rm_ptr->inbuf_pos = rm_ptr->inbuf + bytes_read; rm_ptr->inbuf_off = rm_ptr->inbuf; REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return (0 <= status ? bytes_read : status); } fis-gtm-V7.0-005/sr_unix/iorm_open.c0000644000032200000250000003470014342376330016200 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_unistd.h" #include "gtm_stat.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_stdlib.h" #include "gtm_limits.h" #include "io.h" #include "iormdef.h" #include "io_params.h" #include "eintr_wrappers.h" #include "have_crit.h" #include "gtmio.h" #ifdef __MVS__ #include "gtm_zos_io.h" #include "gtm_zos_chset.h" #endif #ifdef UTF8_SUPPORTED #include "gtm_conv.h" #include "gtm_utf8.h" #endif #include "gtmcrypt.h" #include "error.h" GBLREF io_pair io_curr_device; GBLREF boolean_t gtm_utf8_mode; GBLREF boolean_t gtm_nofflf; /* Used to control "write #" behavior ref GTM-9136 */ ZOS_ONLY(GBLREF boolean_t gtm_tag_utf8_as_ascii;) #ifdef __MVS__ error_def(ERR_BADTAG); #endif error_def(ERR_CRYPTNOAPPEND); error_def(ERR_DEVOPENFAIL); error_def(ERR_TEXT); error_def(ERR_IOERROR); LITREF mstr chset_names[]; LITREF unsigned char io_params_size[]; /* WARNING, this routine is called from ioff_open as well as from the dispatch table. */ /* WARNING, this routine is called from iopi_open as well as from the dispatch table. */ short iorm_open(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout) { io_desc *iod; d_rm_struct *d_rm; off_t size; unsigned char ch; int fstat_res, width_bytes; int4 recsize_before; struct stat statbuf; int p_offset; int bom_size_toread; boolean_t utf_active, def_recsize_before, text_tag, newversion; boolean_t closed_nodestroy; boolean_t append; gtm_chset_t width_chset, dummy_chset; off_t new_position; long pipe_buff_size; # ifdef __MVS__ int file_tag, obtained_tag, realfiletag; char *errmsg; # endif boolean_t ch_set; newversion = closed_nodestroy = append = FALSE; iod = dev_name->iod; ESTABLISH_RET_GTMIO_CH(&iod->pair, -1, ch_set); size = 0; p_offset = 0; assert((params) *(pp->str.addr + p_offset) < (unsigned char)n_iops); assert(NULL != iod); assert(0 <= iod->state && n_io_dev_states > iod->state); assert(rm == iod->type); if (dev_never_opened == iod->state) { iod->dev_sp = (void *)malloc(SIZEOF(d_rm_struct)); memset(iod->dev_sp, 0, SIZEOF(d_rm_struct)); d_rm = (d_rm_struct *)iod->dev_sp; iod->state = dev_closed; d_rm->stream = FALSE; iod->width = DEF_RM_WIDTH; iod->length = DEF_RM_LENGTH; iod->fflf = !gtm_nofflf; /* GTM-9136: Set FFLF mode by env var gtm_nofflf */ d_rm->recordsize = DEF_RM_RECORDSIZE; d_rm->def_width = d_rm->def_recsize = TRUE; d_rm->fixed = FALSE; d_rm->read_only = FALSE; d_rm->fifo = FALSE; d_rm->is_pipe = FALSE; d_rm->padchar = DEF_RM_PADCHAR; d_rm->inbuf = NULL; d_rm->outbuf = NULL; d_rm->follow = FALSE; d_rm->no_destroy = FALSE; d_rm->read_fildes = FD_INVALID; d_rm->input_iv.addr = NULL; d_rm->input_iv.len = 0; d_rm->output_iv.addr = NULL; d_rm->output_iv.len = 0; d_rm->input_key.addr = NULL; d_rm->input_key.len = 0; d_rm->output_key.addr = NULL; d_rm->output_key.len = 0; d_rm->input_cipher_handle = GTMCRYPT_INVALID_KEY_HANDLE; d_rm->output_cipher_handle = GTMCRYPT_INVALID_KEY_HANDLE; d_rm->input_encrypted = FALSE; d_rm->output_encrypted = FALSE; d_rm->read_occurred = FALSE; d_rm->write_occurred = FALSE; d_rm->fsblock_buffer_size = 0; d_rm->fsblock_buffer = NULL; d_rm->ichset_utf16_variant = d_rm->ochset_utf16_variant = 0; } else { d_rm = (d_rm_struct *)iod->dev_sp; /* remember if device closed by nodestroy in case the no_destroy flag was cleared in io_open_try() due to a deviceparameter other than SEEK on reopen */ if ((dev_closed == iod->state) && !d_rm->no_destroy && !d_rm->fifo && !d_rm->is_pipe && (2 < fd)) closed_nodestroy = TRUE; } if (dev_closed == iod->state) { if (!d_rm->no_destroy) { d_rm->lastop = RM_NOOP; d_rm->out_bytes = 0; d_rm->crlast = FALSE; d_rm->done_1st_read = FALSE; d_rm->done_1st_write = FALSE; d_rm->follow = FALSE; } assert(0 <= fd); d_rm->fildes = fd; FSTAT_FILE(fd, &statbuf, fstat_res); if (-1 == fstat_res) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("Error in fstat"), errno); for (p_offset = 0; iop_eol != *(pp->str.addr + p_offset); ) { if (iop_append == (ch = *(pp->str.addr + p_offset++))) { if (!d_rm->fifo && !d_rm->is_pipe && (off_t)-1 == (size = lseek(fd, 0, SEEK_END))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("Error setting file pointer to end of file"), errno); if (0 < statbuf.st_size) { /* Only disable BOM writing if there is something in the file already (not empty) */ d_rm->done_1st_read = FALSE; d_rm->done_1st_write = FALSE; /* the file is not empty so set file_pos to the size of the file */ d_rm->file_pos = size; } append = TRUE; /* remember so we don't clear file_pos later */ break; } else if (iop_newversion == ch) newversion = TRUE; p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); } if (!d_rm->fifo && !d_rm->is_pipe && (2 < fd)) { if (d_rm->no_destroy) { /* if last operation was a write then file position set in iorm_close() */ if (RM_WRITE == d_rm->lastop) { new_position = d_rm->file_pos; } else { if (d_rm->fixed) new_position = d_rm->file_pos; else { /* Buffered reads in non-fixed M & UTF modes. */ new_position = d_rm->file_pos + d_rm->tot_bytes_in_buffer - d_rm->start_pos; } } /* lseek to file position for nodestroy */ if ((off_t)-1 == (size =lseek (fd, new_position, SEEK_SET))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("Error setting file pointer to the current position"), errno); } else { if ((off_t)-1 == (size = lseek(fd, 0, SEEK_CUR))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("Error setting file pointer to the current position"), errno); /* clear some status if the close was nodestroy but not saving state. The file pointer will be at the beginning or at the end if append is TRUE */ if (closed_nodestroy) { iod->dollar.y = 0; iod->dollar.x = 0; d_rm->lastop = RM_NOOP; d_rm->done_1st_read = d_rm->done_1st_write = d_rm->crlast = FALSE; d_rm->out_bytes = d_rm->bom_buf_cnt = d_rm->bom_buf_off = 0; d_rm->inbuf_top = d_rm->inbuf_off = d_rm->inbuf_pos = d_rm->inbuf; if (!append) d_rm->file_pos = 0; /* Reset temporary buffer so that the next read starts afresh */ if (!d_rm->fixed || IS_UTF_CHSET(iod->ichset)) { DEBUG_ONLY(MEMSET_IF_DEFINED(d_rm->tmp_buffer, 0, CHUNK_SIZE)); d_rm->start_pos = 0; d_rm->tot_bytes_in_buffer = 0; } } } if (size == statbuf.st_size) iod->dollar.zeof = TRUE; } else { pipe_buff_size = fpathconf(fd, _PC_PIPE_BUF); d_rm->pipe_buff_size = (-1 == pipe_buff_size) ? _POSIX_PIPE_BUF : pipe_buff_size; } if (1 == fd) d_rm->filstr = NULL; else { FDOPEN(d_rm->filstr, fd, "r+"); /* Try open R/W */ if (NULL == d_rm->filstr) FDOPEN(d_rm->filstr, fd, "r"); /* Try open RO */ if (NULL == d_rm->filstr) FDOPEN(d_rm->filstr, fd, "w"); /* Try open WO */ if (NULL == d_rm->filstr) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("Error in stream open"), errno); } } /* setting of recordsize, WIDTH, etc, based on changed (if any) CHSET is taken care in iorm_use(). */ if (closed_nodestroy) d_rm->no_destroy = TRUE; iorm_use(iod, pp); /* Now that recordsize and CHSET parms have been handled (if any), allocate the record buffer if needed (utf8 fixed) */ if (dev_closed == iod->state) { # ifdef __MVS__ /* need to get file tag info before set policy which can change what is returned */ if (-1 == gtm_zos_check_tag(fd, &file_tag, &text_tag)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("Error in check_tag fstat"), errno); SET_CHSET_FROM_TAG(file_tag, iod->file_chset); iod->text_flag = text_tag; if (!d_rm->is_pipe && 2 < fd) { /* not for stdin, stdout, stderr or pipes */ if (iod->newly_created || newversion) { /* tag the file. The macros also modify text_tag and file_tag. */ if (d_rm->fifo && (iod->is_ochset_default || d_rm->read_only)) { /* If FIFO, set tag per ichset if no ochset or READONLY */ SET_TAG_FROM_CHSET(iod->ichset, iod->file_chset, TRUE); } else { SET_TAG_FROM_CHSET(iod->ochset, iod->file_chset, TRUE); } iod->file_tag = (unsigned int)file_tag; iod->text_flag = text_tag; if (-1 == gtm_zos_set_tag(fd, file_tag, text_tag, TAG_FORCE, &realfiletag)) { errmsg = STRERROR(errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(10) ERR_BADTAG, 4, dev_name->len, dev_name->dollar_io, realfiletag, file_tag, ERR_TEXT, 2, RTS_ERROR_STRING(errmsg)); } if (gtm_utf8_mode && gtm_tag_utf8_as_ascii && (CHSET_UTF8 == iod->ochset)) iod->process_chset = iod->ochset; else iod->process_chset = iod->file_chset; } else { iod->file_tag = (unsigned int)file_tag; /* save real tag for file */ if (iod->is_ochset_default || d_rm->read_only) { /* set tag per ichset if no ochset or READONLY */ SET_TAG_FROM_CHSET(iod->ichset, dummy_chset, FALSE); } else { SET_TAG_FROM_CHSET(iod->ochset, dummy_chset, FALSE); } if (-1 == (obtained_tag = gtm_zos_tag_to_policy(fd, file_tag, &realfiletag))) { errmsg = STRERROR(errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(10) ERR_BADTAG, 4, dev_name->len, dev_name->dollar_io, realfiletag, file_tag, ERR_TEXT, 2, RTS_ERROR_STRING(errmsg)); } SET_CHSET_FROM_TAG(obtained_tag, iod->process_chset); } } else iod->process_chset = iod->file_chset; /* stdin, stdout, stderr */ if (!gtm_utf8_mode || !IS_UTF_CHSET(iod->ochset)) iod->ochset = CHSET_M; if (!gtm_utf8_mode || !IS_UTF_CHSET(iod->ichset)) iod->ichset = CHSET_M; if (CHSET_BINARY == iod->process_chset) { d_rm->fixed = TRUE; iod->wrap = TRUE; } #endif /* Unless NEWVERSION is specified or the file is empty, or the file was CLOSEd with NODESTROY at EOF, we do not * allow APPEND with encryption. */ if (d_rm->output_encrypted && append && (!newversion) && (0 != statbuf.st_size) && (!d_rm->no_destroy || !iod->dollar.zeof)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_CRYPTNOAPPEND, 2, dev_name->len, dev_name->dollar_io); } if (!d_rm->bom_checked && !d_rm->fifo && !d_rm->is_pipe && (2 < fd) && IS_UTF_CHSET(iod->ochset)) { /* if file opened with WRITEONLY */ if (d_rm->write_only) { /* get the file size again in case it was truncated */ FSTAT_FILE(fd, &statbuf, fstat_res); if (-1 == fstat_res) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("Error in fstat"), errno); /* and is not empty generate and error */ if (0 < statbuf.st_size) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("Cannot read BOM from non-empty WRITEONLY file")); /* it is empty so set bom_checked as it will be 0 for all but UTF-16 which will be set in iorm_write.c on the first write */ else d_rm->bom_checked = TRUE; } else { /* if not a reopen of file, and is not empty and not writeonly, then go to the beginning of the file if BOM bytes available and read the potential BOM */ if (!d_rm->no_destroy && (0 < statbuf.st_size) && (!d_rm->input_encrypted)) { assert(UTF16BE_BOM_LEN < UTF8_BOM_LEN); if ((CHSET_UTF8 == iod->ichset) && (statbuf.st_size >= UTF8_BOM_LEN)) bom_size_toread = UTF8_BOM_LEN; else if (IS_UTF16_CHSET(iod->ichset) && (statbuf.st_size >= UTF16BE_BOM_LEN)) bom_size_toread = UTF16BE_BOM_LEN; else bom_size_toread = 0; if (0 < bom_size_toread) { if ((off_t)-1 == lseek(fd, 0, SEEK_SET)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("Error setting file pointer to beginning of file"), errno); d_rm->bom_num_bytes = open_get_bom(iod, bom_size_toread); /* move back to previous file position */ if ((off_t)-1 == lseek(fd, d_rm->file_pos, SEEK_SET)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2, LEN_AND_LIT("Error setting file pointer to previous file position"), errno); } d_rm->bom_checked = TRUE; } } } if (d_rm->no_destroy) d_rm->no_destroy = FALSE; iod->state = dev_open; REVERT_GTMIO_CH(&iod->pair, ch_set); return TRUE; } /* check for BOM in a disk file under the covers during open, seek, or the first read after a write if not already checked */ int open_get_bom(io_desc *io_ptr, int bom_size) { int status = 0; d_rm_struct *rm_ptr; gtm_chset_t chset; int num_bom_bytes; boolean_t ch_set; ESTABLISH_RET_GTMIO_CH(&io_ptr->pair, -1, ch_set); rm_ptr = (d_rm_struct *)(io_ptr->dev_sp); assert(UTF16BE_BOM_LEN == UTF16LE_BOM_LEN); assert(!rm_ptr->input_encrypted); /* If ichset is UTF-16 save chset to see if it changes */ if (UTF16BE_BOM_LEN == bom_size) chset = io_ptr->ichset; DOREADRL(rm_ptr->fildes, &rm_ptr->bom_buf[0], bom_size, status); if (0 > status) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("read"), RTS_ERROR_LITERAL("READING BOM"), CALLFROM, errno); } else if (0 == status) { num_bom_bytes = 0; } else { num_bom_bytes = gtm_utf_bomcheck(io_ptr, &io_ptr->ichset, &rm_ptr->bom_buf[0], bom_size); if ((UTF16BE_BOM_LEN == bom_size) && chset != io_ptr->ichset) { rm_ptr->ichset_utf16_variant = chset = io_ptr->ichset; get_chset_desc(&chset_names[chset]); } } REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return (num_bom_bytes); } fis-gtm-V7.0-005/sr_unix/iorm_rdone.c0000755000032200000250000000301214342376330016341 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ /* iorm_rdone.c */ #include "mdef.h" #include "gtm_stdio.h" #include "io.h" #include "gt_timer.h" #include "iormdef.h" #include "gtm_utf8.h" GBLREF io_pair io_curr_device; int iorm_rdone(mint *v, int4 msec_timeout) { mval tmp; int ret; uint4 codepoint; gtm_chset_t ichset; *v = -1; ret = iorm_readfl(&tmp, -1, msec_timeout); if (ret) { if (0 != tmp.str.len) { ichset = io_curr_device.in->ichset; switch(ichset) { case CHSET_M: codepoint = (unsigned char)tmp.str.addr[0]; break; case CHSET_UTF8: case CHSET_UTF16BE: case CHSET_UTF16LE: UTF8_MBTOWC(tmp.str.addr, tmp.str.addr + tmp.str.len, codepoint); break; case CHSET_UTF16: assertpro(ichset && FALSE); default: #ifdef __MVS codepoint = (unsigned char)tmp.str.addr[0]; break; #else assertpro(ichset && FALSE); #endif } UTF8_ONLY(assert(WEOF != codepoint);) } else codepoint = (uint4)-1; /* zero length is end of file */ *v = (mint)(codepoint); } return ret; } fis-gtm-V7.0-005/sr_unix/iorm_read.c0000755000032200000250000000126114342376330016151 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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. * * * ****************************************************************/ /* iorm_read.c */ #include "mdef.h" #include "io.h" int iorm_read(mval *v, int4 msec_timeout) { return iorm_readfl(v, 0, msec_timeout); /* 0 means not fixed length */ } fis-gtm-V7.0-005/sr_unix/iorm_readfl.c0000644000032200000250000022357114342376330016502 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_fcntl.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "io.h" #include "iotimer.h" #include "iormdef.h" #include "stringpool.h" #include "gt_timer.h" #include "gtmio.h" #include "have_crit.h" #include "eintr_wrappers.h" #include "wake_alarm.h" #include "min_max.h" #include "deferred_events_queue.h" #include "mv_stent.h" #include "send_msg.h" #include "svnames.h" #include "op.h" #include "util.h" #include "gtmcrypt.h" #include "error.h" #ifdef UTF8_SUPPORTED #include "gtm_conv.h" #include "gtm_utf8.h" #endif GBLREF boolean_t gtm_utf8_mode, prin_dm_io, prin_in_dev_failure, prin_out_dev_failure; GBLREF io_pair io_curr_device, io_std_device; GBLREF mval dollar_zstatus; GBLREF mv_stent *mv_chain; GBLREF spdesc stringpool; GBLREF volatile bool out_of_time; GBLREF volatile boolean_t dollar_zininterrupt; GBLREF volatile int4 outofband; #ifdef UTF8_SUPPORTED GBLREF UConverter *chset_desc[]; LITREF mstr chset_names[]; LITREF UChar32 u32_line_term[]; #endif error_def(ERR_DEVICEWRITEONLY); error_def(ERR_IOEOF); error_def(ERR_IOERROR); error_def(ERR_NOPRINCIO); error_def(ERR_SYSCALL); error_def(ERR_ZINTRECURSEIO); #define fl_copy(a, b) (a > b ? b : a) #define SETZACANCELTIMER \ io_ptr->dollar.za = ZA_IO_ERR; \ v->str.len = 0; \ if (!rm_ptr->follow && timed && !out_of_time) \ cancel_timer(timer_id); #ifdef UTF8_SUPPORTED #define UTF8CRLEN 1 /* Length of CR in UTF8 mode. */ #define SET_UTF8_DOLLARKEY_DOLLARZB(UTF_CODE, DOLLAR_KEY, DOLLAR_ZB) \ { \ unsigned char *endstr; \ endstr = UTF8_WCTOMB(UTF_CODE, DOLLAR_KEY); \ *endstr = '\0'; \ endstr = UTF8_WCTOMB(UTF_CODE, DOLLAR_ZB); \ *endstr = '\0'; \ } /* Maintenance of $ZB on a badchar error and returning partial data (if any) */ void iorm_readfl_badchar(mval *vmvalptr, int datalen, int delimlen, unsigned char *delimptr, unsigned char *strend) { int tmplen, errlen; unsigned char *delimend; io_desc *iod; d_rm_struct *rm_ptr; boolean_t ch_set; assert(0 <= datalen); iod = io_curr_device.in; ESTABLISH_GTMIO_CH(&io_curr_device, ch_set); rm_ptr = (d_rm_struct *)(iod->dev_sp); assert(NULL != rm_ptr); vmvalptr->str.len = datalen; vmvalptr->str.addr = (char *)stringpool.free; if (0 < datalen) /* Return how much input we got */ stringpool.free += vmvalptr->str.len; if ((NULL != strend) && (NULL != delimptr)) { /* First find the end of the delimiter (max of 4 bytes) */ if (0 == delimlen) { for (delimend = delimptr; (GTM_MB_LEN_MAX >= delimlen) && (delimend < strend); ++delimend, ++delimlen) { if (UTF8_VALID(delimend, strend, tmplen)) break; } } if (0 < delimlen) { /* Set $KEY and $ZB with the failing badchar */ memcpy(iod->dollar.zb, delimptr, MIN(delimlen, ESC_LEN - 1)); iod->dollar.zb[MIN(delimlen, ESC_LEN - 1)] = '\0'; memcpy(iod->dollar.key, delimptr, MIN(delimlen, DD_BUFLEN - 1)); iod->dollar.key[MIN(delimlen, DD_BUFLEN - 1)] = '\0'; } } /* set dollar.device in the output device */ SET_DOLLARDEVICE_ONECOMMA_ERRSTR(iod, BADCHAR_DEVICE_MSG, errlen); REVERT_GTMIO_CH(&io_curr_device, ch_set); } #endif int iorm_readfl (mval *v, int4 width, int4 msec_timeout) /* timeout in milliseconds */ { boolean_t ret, timed, utf_active, line_term_seen = FALSE, rdone = FALSE, zint_restart; char inchar, *temp, *temp_start; unsigned char *nextmb, *char_ptr, *char_start, *buffer_start; int flags = 0; int len; int save_errno, errlen, real_errno; int fcntl_res, stp_need; int4 bytes2read, bytes_read, char_bytes_read, add_bytes, reclen; int4 buff_len, mblen, char_count, bytes_count, tot_bytes_read, chunk_bytes_read, utf_tot_bytes_read; int4 status, max_width, ltind, exp_width, from_bom, fol_bytes_read, feof_status; wint_t utf_code; char *errptr; io_desc *io_ptr; d_rm_struct *rm_ptr; gtm_chset_t chset; TID timer_id; int fildes; FILE *filstr; boolean_t pipe_zero_timeout = FALSE; boolean_t pipe_or_fifo = FALSE; boolean_t follow_timeout = FALSE; boolean_t bom_timeout = FALSE; int follow_width; int blocked_in = TRUE; int do_clearerr = FALSE; int saved_lastop; int min_bytes_to_copy; ABS_TIME cur_time, end_time, current_time, time_left; pipe_interrupt *pipeintr; mv_stent *mv_zintdev; unsigned int *dollarx_ptr; unsigned int *dollary_ptr; struct timeval poll_interval; int poll_status; fd_set input_fds; int4 sleep_left; int4 sleep_time; struct stat statbuf; int fstat_res; off_t cur_position; int bom_size_toread; boolean_t ch_set; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # ifdef DEBUG_PIPE pid=getpid(); # endif assert(stringpool.free >= stringpool.base); assert(stringpool.free <= stringpool.top); io_ptr = io_curr_device.in; ESTABLISH_RET_GTMIO_CH(&io_curr_device, -1, ch_set); /* don't allow a read from a writeonly fifo */ if (((d_rm_struct *)io_ptr->dev_sp)->write_only) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DEVICEWRITEONLY); # ifdef __MVS__ /* on zos if it is a fifo device then point to the pair.out for $X and $Y */ if (((d_rm_struct *)io_ptr->dev_sp)->fifo) { dollarx_ptr = &(io_ptr->pair.out->dollar.x); dollary_ptr = &(io_ptr->pair.out->dollar.y); } else # endif { dollarx_ptr = &(io_ptr->dollar.x); dollary_ptr = &(io_ptr->dollar.y); } assert (io_ptr->state == dev_open); rm_ptr = (d_rm_struct *)(io_ptr->dev_sp); assert(NULL != rm_ptr); pipeintr = &rm_ptr->pipe_save_state; if (rm_ptr->is_pipe || rm_ptr->fifo) pipe_or_fifo = TRUE; PIPE_DEBUG(PRINTF(" %d enter iorm_readfl\n", pid); DEBUGPIPEFLUSH); /* if it is a pipe and it's the stdout returned then we need to get the read file descriptor from rm_ptr->read_fildes and the stream pointer from rm_ptr->read_filstr */ if ((rm_ptr->is_pipe ZOS_ONLY(|| rm_ptr->fifo)) && (0 < rm_ptr->read_fildes)) { assert(rm_ptr->read_filstr); fildes = rm_ptr->read_fildes; filstr = rm_ptr->read_filstr; } else { fildes = rm_ptr->fildes; filstr = rm_ptr->filstr; } utf_active = gtm_utf8_mode ? (IS_UTF_CHSET(io_ptr->ichset)) : FALSE; /* If the last operation was a write and $X is non-zero we may have to call iorm_wteol() */ if (*dollarx_ptr && rm_ptr->lastop == RM_WRITE) { /* don't need to flush the pipe device for a streaming read */ /* Fixed mode read may output pad characters in iorm_wteol() for all device types */ if (!io_ptr->dollar.za && (!rm_ptr->is_pipe || rm_ptr->fixed)) iorm_wteol(1, io_ptr); *dollarx_ptr = 0; } /* if it's a fifo and not system input and the last operation was a write and O_NONBLOCK is set then turn if off. A write will turn it on. The default is RM_NOOP. */ if (rm_ptr->fifo && (0 != rm_ptr->fildes) && (RM_WRITE == rm_ptr->lastop)) { flags = 0; FCNTL2(rm_ptr->fildes, F_GETFL, flags); if (0 > flags) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(io_ptr, save_errno); ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, FALSE, FALSE); /* FALSE, FALSE: READ, not socket*/ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, save_errno); } if (flags & O_NONBLOCK) { FCNTL3(rm_ptr->fildes, F_SETFL, (flags & ~O_NONBLOCK), fcntl_res); if (0 > fcntl_res) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(io_ptr, save_errno); ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, FALSE, FALSE); /* FALSE, FALSE: READ, not socket*/ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, save_errno); } } } /* if the last operation was a write to a disk, we need to initialize it so file_pos is pointing to the current file position */ if (!rm_ptr->fifo && !rm_ptr->is_pipe && (2 < rm_ptr->fildes) && (RM_WRITE == rm_ptr->lastop)) { /* need to do an lseek to get current location in file */ cur_position = lseek(rm_ptr->fildes, 0, SEEK_CUR); if ((off_t)-1 == cur_position) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(io_ptr, save_errno); ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, FALSE, FALSE); /* FALSE, FALSE: READ, not socket*/ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"), RTS_ERROR_LITERAL("iorm_readfl()"), CALLFROM, save_errno); } else rm_ptr->file_pos = cur_position; *dollary_ptr = 0; *dollarx_ptr = 0; /* Reset temporary buffer so that the next read starts afresh */ if (utf_active || !rm_ptr->fixed) { rm_ptr->out_bytes = rm_ptr->bom_buf_cnt = rm_ptr->bom_buf_off = 0; rm_ptr->inbuf_top = rm_ptr->inbuf_off = rm_ptr->inbuf_pos = rm_ptr->inbuf; DEBUG_ONLY(MEMSET_IF_DEFINED(rm_ptr->tmp_buffer, 0, CHUNK_SIZE)); rm_ptr->start_pos = 0; rm_ptr->tot_bytes_in_buffer = 0; } if (utf_active) { /* If bom not checked yet, not at the beginning of the file and at least UTF16BE_BOM_LEN number of bytes, * then go to the beginning of the file and read the potential BOM. Move back to previous file position * after BOM check. Note that in case of encryption this is the only place where the BOM is read. */ if ((!rm_ptr->bom_checked) && (0 < rm_ptr->file_pos) && (!rm_ptr->input_encrypted)) { FSTAT_FILE(fildes, &statbuf, fstat_res); if (-1 == fstat_res) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(io_ptr, save_errno); ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, FALSE, FALSE); /* FALSE, FALSE: READ, not socket*/ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fstat"), CALLFROM, save_errno); } assert(UTF16BE_BOM_LEN < UTF8_BOM_LEN); if ((CHSET_UTF8 == io_ptr->ichset) && (statbuf.st_size >= UTF8_BOM_LEN)) bom_size_toread = UTF8_BOM_LEN; else if (IS_UTF16_CHSET(io_ptr->ichset) && (statbuf.st_size >= UTF16BE_BOM_LEN)) bom_size_toread = UTF16BE_BOM_LEN; else bom_size_toread = 0; if (0 < bom_size_toread) { if ((off_t)-1 == lseek(fildes, 0, SEEK_SET)) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(io_ptr, save_errno); ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, FALSE, FALSE); /* FALSE, FALSE: READ, not socket*/ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"), RTS_ERROR_LITERAL( "Error setting file pointer to beginning of file"), CALLFROM, save_errno); } rm_ptr->bom_num_bytes = open_get_bom(io_ptr, bom_size_toread); /* move back to previous file position */ if ((off_t)-1 == lseek(fildes, rm_ptr->file_pos, SEEK_SET)) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(io_ptr, save_errno); ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, FALSE, FALSE); /* FALSE, FALSE: READ, not socket*/ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"), RTS_ERROR_LITERAL( "Error restoring file pointer"), CALLFROM, save_errno); } } rm_ptr->bom_checked = TRUE; } } } zint_restart = FALSE; /* Check if new or resumed read */ if (rm_ptr->mupintr) { /* We have a pending read restart of some sort */ assertpro(pipewhich_invalid != pipeintr->who_saved); /* Interrupt should never have an invalid save state */ /* check we aren't recursing on this device */ if (dollar_zininterrupt) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZINTRECURSEIO); if (pipewhich_readfl != pipeintr->who_saved) assertpro(FALSE); /* ZINTRECURSEIO should have caught */ PIPE_DEBUG(PRINTF("piperfl: *#*#*#*#*#*#*# Restarted interrupted read\n"); DEBUGPIPEFLUSH); mv_zintdev = io_find_mvstent(io_ptr, FALSE); if (mv_zintdev) { if (mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid) { bytes_read = pipeintr->bytes_read; assert(mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.len == bytes_read); if (bytes_read) buffer_start = (unsigned char *)mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr; else buffer_start = stringpool.free; zint_restart = TRUE; } else { PIPE_DEBUG(PRINTF("Evidence of an interrupt, but it was invalid\n"); DEBUGPIPEFLUSH); assert(FALSE); } /* Done with this mv_stent. Pop it off if we can, else mark it inactive. */ if (mv_chain == mv_zintdev) POP_MV_STENT(); /* pop if top of stack */ else { /* else mark it unused */ mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = 0; mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = NULL; } } else { pipeintr->end_time_valid = FALSE; PIPE_DEBUG(PRINTF("Evidence of an interrupt, but no MV_STENT\n"); DEBUGPIPEFLUSH); } rm_ptr->mupintr = FALSE; pipeintr->who_saved = pipewhich_invalid; } else pipeintr->end_time_valid = FALSE; /* save the lastop for zeof test later */ saved_lastop = rm_ptr->lastop; rm_ptr->lastop = RM_READ; io_ptr->dollar.key[0] = '\0'; io_ptr->dollar.zb[0] = '\0'; timer_id = (TID)iorm_readfl; max_width = io_ptr->width - *dollarx_ptr; if (0 == width) { width = io_ptr->width; /* called from iorm_read */ if (!rm_ptr->fixed) max_width = width; /* preserve prior functionality */ } else if (-1 == width) { rdone = TRUE; /* called from iorm_rdone */ width = 1; } width = (width < max_width) ? width : max_width; tot_bytes_read = char_bytes_read = char_count = bytes_count = 0; ret = TRUE; if (!zint_restart) { /* Simple path (not restart or nothing read,) no worries*/ /* compute the worst case byte requirement knowing that width is in chars */ /* if utf_active, need room for multi byte characters */ exp_width = utf_active ? (GTM_MB_LEN_MAX * width) : width; ENSURE_STP_FREE_SPACE(exp_width); temp = (char *)stringpool.free; } else { exp_width = pipeintr->max_bufflen; bytes_read = pipeintr->bytes_read; /* some locals needed by UTF8 & M streaming mode */ if (!rm_ptr->fixed) { bytes2read = pipeintr->bytes2read; char_count = pipeintr->char_count; bytes_count = pipeintr->bytes_count; add_bytes = pipeintr->add_bytes; } PIPE_DEBUG(PRINTF("piperfl: .. mv_stent found - bytes_read: %d max_bufflen: %d" " interrupts: %d\n", bytes_read, exp_width, TREF(pipefifo_interrupt)); DEBUGPIPEFLUSH); PIPE_DEBUG(PRINTF("piperfl: .. timeout: %d\n", msec_timeout); DEBUGPIPEFLUSH); PIPE_DEBUG(if (pipeintr->end_time_valid) PRINTF("piperfl: .. endtime: %d/%d\n", end_time.at_sec, end_time.at_usec); DEBUGPIPEFLUSH); PIPE_DEBUG(PRINTF("piperfl: .. buffer address: 0x%08lx stringpool: 0x%08lx\n", buffer_start, stringpool.free); DEBUGPIPEFLUSH); PIPE_DEBUG(PRINTF("buffer_start =%s\n", buffer_start); DEBUGPIPEFLUSH); /* If it is fixed and utf mode then we are not doing any mods affecting stringpool during the read and don't use temp, so skip the following stringpool checks */ if (!utf_active || !rm_ptr->fixed) { if (!IS_AT_END_OF_STRINGPOOL(buffer_start, bytes_read)) { /* Not @ stringpool.free - must move what we have, so we need room for the whole anticipated message */ PIPE_DEBUG(PRINTF("socrfl: .. Stuff put on string pool after our buffer\n"); DEBUGPIPEFLUSH); stp_need = exp_width; } else { /* Previously read buffer piece is still last thing in stringpool, so we need room for the rest */ PIPE_DEBUG(PRINTF("piperfl: .. Our buffer did not move in the stringpool\n"); DEBUGPIPEFLUSH); stp_need = exp_width - bytes_read; assert(stp_need <= exp_width); } if (!IS_STP_SPACE_AVAILABLE(stp_need)) { /* need more room */ PIPE_DEBUG(PRINTF("piperfl: .. garbage collection done in starting after interrrupt\n"); DEBUGPIPEFLUSH); v->str.addr = (char *)buffer_start; /* Protect buffer from reclaim */ v->str.len = bytes_read; INVOKE_STP_GCOL(exp_width); /* if v->str.len is 0 then v->str.add is ignored by garbage collection so reset it to stringpool.free */ if (v->str.len == 0) v->str.addr = (char *)stringpool.free; buffer_start = (unsigned char *)v->str.addr; } assert((buffer_start + bytes_read) <= stringpool.free); /* BYPASSOK */ if (!IS_AT_END_OF_STRINGPOOL(buffer_start, bytes_read)) { /* now need to move it to the top */ assert(stp_need == exp_width); memcpy(stringpool.free, buffer_start, bytes_read); } else stringpool.free = buffer_start; /* it should still be just under the used space */ v->str.len = 0; /* Clear in case interrupt or error -- don't want to hold this buffer */ temp = (char *)(stringpool.free + bytes_read); tot_bytes_read = bytes_count = bytes_read; if (!(rm_ptr->fixed && rm_ptr->follow)) width -= bytes_read; } } if (utf_active || !rm_ptr->fixed) bytes_read = 0; out_of_time = FALSE; if (NO_M_TIMEOUT == msec_timeout) { timed = FALSE; assert(!pipeintr->end_time_valid); } else { /* For timed input, this routine starts only one timer. One case is for a READ x:n; another is potentially for the * PIPE device doing a READ x:0. If a timer is set, out_of_time starts as FALSE. If the timer expires prior to a * read completing, the timer handler changes out_of_time to TRUE. In the READ x:0 case for a PIPE, if an attempt to * read one character in non-blocking mode succeeds, we set blocked_in to TRUE (to prevent a 2nd timer), set the * PIPE to blocking mode and start the timer for one second. We set timed to TRUE for both READ x:n and x:0; * msec_timeout is 0 for a READ x:0 unless it's a PIPE which has read one character and started a 1 second timer. If * timed is TRUE, we manage the timer outcome at the end of this routine. */ timed = TRUE; if (0 < msec_timeout) { /* For the READ x:n case, start a timer and clean it up in the (timed) clause at the end of this routine if * it has not expired. */ sys_get_curr_time(&cur_time); if (!zint_restart || !pipeintr->end_time_valid) add_int_to_abs_time(&cur_time, msec_timeout, &end_time); else { /* Compute appropriate msec_timeout using end_time from restart data. */ end_time = pipeintr->end_time; /* Restore end_time for timeout */ cur_time = sub_abs_time(&end_time, &cur_time); if (0 > cur_time.at_sec) { msec_timeout = -1; out_of_time = TRUE; } else msec_timeout = (int4)(cur_time.at_sec * MILLISECS_IN_SEC + DIVIDE_ROUND_UP(cur_time.at_usec, MICROSECS_IN_MSEC)); if (rm_ptr->follow && !out_of_time && !msec_timeout) msec_timeout = 1; PIPE_DEBUG(PRINTF("piperfl: Taking timeout end time from read restart data - " "computed msec_timeout: %d\n", msec_timeout); DEBUGPIPEFLUSH); } PIPE_DEBUG(PRINTF("msec_timeout: %d\n", msec_timeout); DEBUGPIPEFLUSH); /* if it is a disk read with follow don't start timer, as we use a sleep loop instead */ if ((0 < msec_timeout) && !rm_ptr->follow) start_timer(timer_id, msec_timeout, wake_alarm, 0, NULL); } else { /* Except for the one-character read case with a PIPE device described above, a READ x:0 sets out_of_time to * TRUE. */ out_of_time = TRUE; FCNTL2(fildes, F_GETFL, flags); if (0 > flags) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(io_ptr, save_errno); ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, FALSE, FALSE); /* FALSE, FALSE: READ, not socket*/ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, save_errno); } FCNTL3(fildes, F_SETFL, (flags | O_NONBLOCK), fcntl_res); if (0 > fcntl_res) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(io_ptr, save_errno); ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, FALSE, FALSE); /* FALSE, FALSE: READ, not socket*/ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, save_errno); } blocked_in = FALSE; if (rm_ptr->is_pipe) pipe_zero_timeout = TRUE; } } pipeintr->end_time_valid = FALSE; errno = status = 0; chset = io_ptr->ichset; if (!utf_active) { if (rm_ptr->fixed) { /* This is M mode - one character is one byte. * Note the check for EINTR below is valid and should not be converted to an EINTR * wrapper macro, since action is taken on EINTR, not a retry. */ if (rm_ptr->follow) { PIPE_DEBUG(PRINTF(" %d fixed\n", pid); DEBUGPIPEFLUSH); if (timed) { /* recalculate msec_timeout and sleep_left as &end_time - ¤t_time */ if (0 < msec_timeout) { /* get the current time */ sys_get_curr_time(¤t_time); time_left = sub_abs_time(&end_time, ¤t_time); if (0 > time_left.at_sec) { msec_timeout = -1; out_of_time = TRUE; } else msec_timeout = (int4)(time_left.at_sec * MILLISECS_IN_SEC + DIVIDE_ROUND_UP(time_left.at_usec, MICROSECS_IN_MSEC)); /* make sure it terminates with out_of_time */ if (!out_of_time && !msec_timeout) msec_timeout = 1; sleep_left = msec_timeout; } else sleep_left = 0; } /* if zeof is set in follow mode then ignore any previous zeof */ if (TRUE == io_ptr->dollar.zeof) io_ptr->dollar.zeof = prin_in_dev_failure = FALSE; do { /* in follow mode a read will return an EOF if no more bytes are available. */ status = read(fildes, temp, width - bytes_count); if (0 < status) /* we read some chars */ { if (rm_ptr->input_encrypted) READ_ENCRYPTED_DATA(rm_ptr, io_ptr->trans_name, temp, status, NULL); rm_ptr->read_occurred = TRUE; rm_ptr->done_1st_read = TRUE; tot_bytes_read += status; rm_ptr->file_pos += status; bytes_count += status; temp = temp + status; } else if (0 == status) /* end of file */ { if ((TRUE == timed) && (0 >= sleep_left)) { follow_timeout = TRUE; break; } /* if a timed read, sleep the minimum of 100 ms and sleep_left. If not a timed read then just sleep 100 ms */ if (TRUE == timed) { /* recalculate msec_timeout and sleep_left as &end_time - ¤t_time */ /* get the current time */ sys_get_curr_time(¤t_time); time_left = sub_abs_time(&end_time, ¤t_time); if (0 > time_left.at_sec) { msec_timeout = -1; out_of_time = TRUE; } else msec_timeout = (int4)(time_left.at_sec * MILLISECS_IN_SEC + DIVIDE_ROUND_UP(time_left.at_usec, MICROSECS_IN_MSEC)); /* make sure it terminates with out_of_time */ if (!out_of_time && !msec_timeout) msec_timeout = 1; sleep_left = msec_timeout; sleep_time = MIN(100, sleep_left); } else sleep_time = 100; if (0 < sleep_time) SHORT_SLEEP(sleep_time); if (outofband) { PIPE_DEBUG(PRINTF(" %d fixed outofband\n", pid); DEBUGPIPEFLUSH); PUSH_MV_STENT(MVST_ZINTDEV); mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)stringpool.free; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = tot_bytes_read; mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE; pipeintr->who_saved = pipewhich_readfl; if ((0 < msec_timeout) && (NO_M_TIMEOUT != msec_timeout)) { pipeintr->end_time = end_time; pipeintr->end_time_valid = TRUE; } pipeintr->max_bufflen = exp_width; pipeintr->bytes_read = tot_bytes_read; rm_ptr->mupintr = TRUE; stringpool.free += tot_bytes_read; /* Don't step on our parade in the interrupt */ (TREF(pipefifo_interrupt))++; REVERT_GTMIO_CH(&io_curr_device, ch_set); async_action(FALSE); assertpro(FALSE); /* Should *never* return from async_action */ return FALSE; /* For the compiler.. */ } continue; /* for now try and read again if eof or no input ready */ } else /* error returned */ { PIPE_DEBUG(PRINTF("errno= %d fixed\n", errno); DEBUGPIPEFLUSH); if (errno != EINTR) { bytes_count = 0; break; } } } while (bytes_count < width); } else { /* If the device is a PIPE, and we read at least one character, start a timer using timer_id. We * cancel that timer later in this routine if it has not expired before the return. */ DOREADRLTO2(fildes, temp, width, out_of_time, &blocked_in, rm_ptr->is_pipe, flags, status, &tot_bytes_read, timer_id, &msec_timeout, pipe_zero_timeout, FALSE, pipe_or_fifo); PIPE_DEBUG(PRINTF(" %d fixed\n", pid); DEBUGPIPEFLUSH); if ((0 < status) || ((0 > status) && (0 < tot_bytes_read))) { if (rm_ptr->input_encrypted) READ_ENCRYPTED_DATA(rm_ptr, io_ptr->trans_name, temp, (status > 0) ? status : tot_bytes_read, NULL); rm_ptr->read_occurred = TRUE; rm_ptr->done_1st_read = TRUE; } if (0 > status) { if (pipe_or_fifo) bytes_count = tot_bytes_read; else bytes_count = 0; if (errno == EINTR && out_of_time) status = -2; } else { tot_bytes_read = bytes_count = status; rm_ptr->file_pos += status; } if (zint_restart) { tot_bytes_read += bytes_read; bytes_count = tot_bytes_read; PIPE_DEBUG(PRINTF(" %d temp= %s tot_bytes_read = %d\n", pid, temp, tot_bytes_read); DEBUGPIPEFLUSH); } if (pipe_or_fifo && outofband) { PIPE_DEBUG(PRINTF(" %d fixed outofband\n", pid); DEBUGPIPEFLUSH); PUSH_MV_STENT(MVST_ZINTDEV); mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)stringpool.free; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = tot_bytes_read; mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE; pipeintr->who_saved = pipewhich_readfl; if ((0 < msec_timeout) && (NO_M_TIMEOUT != msec_timeout)) { pipeintr->end_time = end_time; pipeintr->end_time_valid = TRUE; cancel_timer(timer_id); /* Worry about timer if/when we come back */ } pipeintr->max_bufflen = exp_width; pipeintr->bytes_read = tot_bytes_read; rm_ptr->mupintr = TRUE; stringpool.free += tot_bytes_read; /* Don't step on our parade in the interrupt */ (TREF(pipefifo_interrupt))++; REVERT_GTMIO_CH(&io_curr_device, ch_set); async_action(FALSE); assertpro(FALSE); /* Should *never* return from async_action */ return FALSE; /* For the compiler.. */ } } } else /* Handle pipe/fifo with stream.var files */ { /* rms-file device, PIPE or FIFO */ /* read using read() into buffer and manage the buffer. */ PIPE_DEBUG(PRINTF("M 1: status: %d bytes2read: %d rm_ptr->start_pos: %d " "rm_ptr->tot_bytes_in_buffer: %d add_bytes: %d bytes_read: %d\n", status, bytes2read, rm_ptr->start_pos, rm_ptr->tot_bytes_in_buffer, add_bytes, bytes_read); DEBUGPIPEFLUSH); if (rm_ptr->follow) { PIPE_DEBUG(PRINTF(" %d M streaming with follow\n", pid); DEBUGPIPEFLUSH); /* rms-file device in follow mode */ if (timed) { /* recalculate msec_timeout and sleep_left as &end_time - ¤t_time */ if (0 < msec_timeout) { /* get the current time */ sys_get_curr_time(¤t_time); time_left = sub_abs_time(&end_time, ¤t_time); if (0 > time_left.at_sec) { msec_timeout = -1; out_of_time = TRUE; } else msec_timeout = (int4)(time_left.at_sec * MILLISECS_IN_SEC + DIVIDE_ROUND_UP(time_left.at_usec, MICROSECS_IN_MSEC)); /* make sure it terminates with out_of_time */ if (!out_of_time && !msec_timeout) msec_timeout = 1; sleep_left = msec_timeout; } else sleep_left = 0; } /* if zeof is set in follow mode then ignore any previous zeof */ if (TRUE == io_ptr->dollar.zeof) io_ptr->dollar.zeof = prin_in_dev_failure = FALSE; } do { bytes2read = 1; PIPE_DEBUG(PRINTF("M 3: status: %d bytes2read: %d rm_ptr->start_pos: %d " "rm_ptr->tot_bytes_in_buffer: %d\n", status, bytes2read, rm_ptr->start_pos, rm_ptr->tot_bytes_in_buffer); DEBUGPIPEFLUSH); /* If it is a pipe and at least one character is read, a timer with timer_id will be started. It is canceled later in this routine if not expired prior to return */ if (rm_ptr->start_pos == rm_ptr->tot_bytes_in_buffer) { DEBUG_ONLY(MEMSET_IF_DEFINED(rm_ptr->tmp_buffer, 0, CHUNK_SIZE)); rm_ptr->start_pos = rm_ptr->tot_bytes_in_buffer = 0; /* Read CHUNK_SIZE bytes from device into the temporary buffer. By doing this * one-byte reads can be avoided when in non fixed format. * */ if (rm_ptr->follow) { /* In follow mode a read returns an EOF if no more bytes are available. */ status = read(fildes, rm_ptr->tmp_buffer, CHUNK_SIZE); if (0 < status) { if (rm_ptr->input_encrypted) READ_ENCRYPTED_DATA(rm_ptr, io_ptr->trans_name, rm_ptr->tmp_buffer, status, NULL); rm_ptr->read_occurred = TRUE; rm_ptr->done_1st_read = TRUE; } if (0 == status) /* end of file */ { if ((TRUE == timed) && (0 >= sleep_left)) { follow_timeout = TRUE; break; } /* If a timed read, sleep the minimum of 100 ms and sleep_left. * If not a timed read then just sleep 100 ms. */ if (TRUE == timed) { /* recalculate msec_timeout and sleep_left as * &end_time - ¤t_time. */ /* get the current time */ sys_get_curr_time(¤t_time); time_left = sub_abs_time(&end_time, ¤t_time); if (0 > time_left.at_sec) { msec_timeout = -1; out_of_time = TRUE; } else msec_timeout = (int4)(time_left.at_sec * MILLISECS_IN_SEC + DIVIDE_ROUND_UP(time_left.at_usec, MICROSECS_IN_MSEC)); /* make sure it terminates with out_of_time */ if (!out_of_time && !msec_timeout) msec_timeout = 1; sleep_left = msec_timeout; sleep_time = MIN(100, sleep_left); } else sleep_time = 100; if (0 < sleep_time) SHORT_SLEEP(sleep_time); if (outofband) { PIPE_DEBUG(PRINTF(" %d M 2 stream outofband\n", pid); DEBUGPIPEFLUSH); PUSH_MV_STENT(MVST_ZINTDEV); mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)stringpool.free; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = bytes_count; mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE; pipeintr->who_saved = pipewhich_readfl; if ((0 < msec_timeout) && (NO_M_TIMEOUT != msec_timeout)) { pipeintr->end_time = end_time; pipeintr->end_time_valid = TRUE; } pipeintr->max_bufflen = exp_width; /* streaming mode uses bytes_count to show how many bytes are in *temp, but the interrupt re-entrant code uses bytes_read */ pipeintr->bytes_read = bytes_count; pipeintr->bytes2read = bytes2read; pipeintr->char_count = bytes_count; pipeintr->add_bytes = add_bytes; pipeintr->bytes_count = bytes_count; PIPE_DEBUG(PRINTF("M 2 stream outofband " " add_bytes " "%d bytes_count %d\n", add_bytes, bytes_count); DEBUGPIPEFLUSH); rm_ptr->mupintr = TRUE; /* Don't step on our parade in the interrupt */ stringpool.free += bytes_count; (TREF(pipefifo_interrupt))++; REVERT_GTMIO_CH(&io_curr_device, ch_set); async_action(FALSE); assertpro(FALSE); /* Should never return from async_action */ return FALSE; /* For the compiler.. */ } continue; /* for now try and read again if eof or no input ready */ } else if (-1 == status && errno != EINTR) /* error returned */ { bytes_count = 0; break; } } else { /* NO FOLLOW */ DOREADRLTO2(fildes, rm_ptr->tmp_buffer, CHUNK_SIZE, out_of_time, &blocked_in, rm_ptr->is_pipe, flags, status, &chunk_bytes_read, timer_id, &msec_timeout, pipe_zero_timeout, pipe_or_fifo, pipe_or_fifo); if ((0 < status) || ((0 > status) && (0 < chunk_bytes_read))) { if (rm_ptr->input_encrypted) READ_ENCRYPTED_DATA(rm_ptr, io_ptr->trans_name, rm_ptr->tmp_buffer, (status > 0) ? status : chunk_bytes_read, NULL); rm_ptr->read_occurred = TRUE; rm_ptr->done_1st_read = TRUE; } } PIPE_DEBUG(PRINTF(" M 4: read chunk status: %d chunk_bytes_read: %d\n", status, chunk_bytes_read); DEBUGPIPEFLUSH); /* If status is -1, then total number of bytes read will be stored in * chunk_bytes_read. If chunk read returned some bytes, then ignore outofband. * We won't try a read again until bytes are processed. */ if ((pipe_or_fifo || rm_ptr->follow) && outofband && (0 >= status)) { PIPE_DEBUG(PRINTF(" %d utf2 stream outofband\n", pid); DEBUGPIPEFLUSH); PUSH_MV_STENT(MVST_ZINTDEV); mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)stringpool.free; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = bytes_count; mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE; pipeintr->who_saved = pipewhich_readfl; if ((0 < msec_timeout) && (NO_M_TIMEOUT != msec_timeout)) { pipeintr->end_time = end_time; pipeintr->end_time_valid = TRUE; /* Worry about timer if/when we come back */ if (!rm_ptr->follow) cancel_timer(timer_id); } pipeintr->max_bufflen = exp_width; /* streaming mode uses bytes_count to show how many bytes are in *temp, * but the interrupt re-entrant code uses bytes_read. */ pipeintr->bytes_read = bytes_count; pipeintr->bytes2read = bytes2read; pipeintr->char_count = bytes_count; pipeintr->add_bytes = add_bytes; pipeintr->bytes_count = bytes_count; PIPE_DEBUG(PRINTF("utf2 stream outofband " "add_bytes %d bytes_count %d\n", add_bytes, bytes_count); DEBUGPIPEFLUSH); rm_ptr->mupintr = TRUE; /* Don't step on our parade in the interrupt */ stringpool.free += bytes_count; (TREF(pipefifo_interrupt))++; REVERT_GTMIO_CH(&io_curr_device, ch_set); async_action(FALSE); assertpro(FALSE); /* Should *never* return from async_action */ return FALSE; /* For the compiler.. */ } if (-1 == status) { rm_ptr->tot_bytes_in_buffer = chunk_bytes_read; tot_bytes_read = chunk_bytes_read; if (!rm_ptr->is_pipe && !rm_ptr->fifo) status = chunk_bytes_read; } else rm_ptr->tot_bytes_in_buffer = status; } else if (pipe_zero_timeout) out_of_time = FALSE; /* reset out_of_time for pipe as no actual read is done */ if (0 <= rm_ptr->tot_bytes_in_buffer) { min_bytes_to_copy = (rm_ptr->tot_bytes_in_buffer - rm_ptr->start_pos) >= 1 ? 1 : 0; assert(rm_ptr->start_pos <= rm_ptr->tot_bytes_in_buffer); /* Copy the char to inchar */ inchar = rm_ptr->tmp_buffer[rm_ptr->start_pos]; /* Increment start_pos. Only 1 byte in M mode */ rm_ptr->start_pos += min_bytes_to_copy; /* Set status appropriately so that the following code can continue as if * a one byte read happened. In case of a negative status, preserve the status * as-is. */ status = (0 <= status) ? min_bytes_to_copy : status; } if (0 <= status) { bytes_read += status; /* bytes read this pass */ tot_bytes_read += bytes_read; /* total bytes read this command */ rm_ptr->file_pos += status; PIPE_DEBUG(PRINTF("M 5: status: %d bytes2read: %d bytes_read: %d " "%d tot_bytes_read: %d\n", status, bytes2read, bytes_read, tot_bytes_read); DEBUGPIPEFLUSH); if ((0 < bytes2read) && (0 == status)) { /* EOF on read */ SETZACANCELTIMER; break; /* nothing read for this char so treat as EOF */ } /* Check for the line terminators : NATIVE_NL */ if (NATIVE_NL == inchar) { line_term_seen = TRUE; io_ptr->dollar.key[0] = io_ptr->dollar.zb[0] = NATIVE_NL; io_ptr->dollar.key[1] = io_ptr->dollar.zb[1] = '\0'; if (!rdone) break; } *temp++ = inchar; PIPE_DEBUG(PRINTF("8: move inchar to *temp\n"); DEBUGPIPEFLUSH); bytes_count += status; PIPE_DEBUG(PRINTF("11: bytes_count: %d \n", bytes_count); DEBUGPIPEFLUSH); } else { inchar = 0; if (errno == 0) { tot_bytes_read = 0; status = 0; } else if (EINTR == errno) { if (out_of_time) status = -2; else continue; /* Ignore interrupt if not our wakeup */ } break; } } while (bytes_count < width); } } else { /* UTF8 mode */ assert(NULL != rm_ptr->inbuf); if (rm_ptr->fixed) { buff_len = (int)(rm_ptr->inbuf_top - rm_ptr->inbuf_off); assert(buff_len < rm_ptr->recordsize); /* zint_restart can only be set if we haven't finished filling the buffer. Let iorm_get() decide what to do. */ if ((0 == buff_len) || zint_restart) { /* need to refill the buffer */ if (rm_ptr->follow) { buff_len = iorm_get_fol(io_ptr, &tot_bytes_read, &msec_timeout, timed, zint_restart, &follow_timeout, end_time); /* this will include the total bytes read including any stripped pad chars */ fol_bytes_read = rm_ptr->fol_bytes_read; } else { buff_len = iorm_get(io_ptr, &blocked_in, rm_ptr->is_pipe, flags, &tot_bytes_read, timer_id, &msec_timeout, pipe_zero_timeout, zint_restart); /* not using fol_bytes_read for non-follow mode */ fol_bytes_read = buff_len; } if (0 > buff_len) { bytes_count = 0; if (errno == EINTR && out_of_time) buff_len = -2; } else if (outofband && (fol_bytes_read < rm_ptr->recordsize)) { PIPE_DEBUG(PRINTF(" %d utf fixed outofband, buff_len: %d done_1st_read: %d\n", pid, buff_len, rm_ptr->done_1st_read); DEBUGPIPEFLUSH); PUSH_MV_STENT(MVST_ZINTDEV); mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)stringpool.free; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = 0; mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE; pipeintr->who_saved = pipewhich_readfl; if ((0 < msec_timeout) && (NO_M_TIMEOUT != msec_timeout)) { pipeintr->end_time = end_time; pipeintr->end_time_valid = TRUE; if (!rm_ptr->follow) cancel_timer(timer_id); /* Worry about timer if/when we come back */ } pipeintr->max_bufflen = exp_width; /* since nothing read into stringpool.free set pipeintr->bytes_read to zero as no bytes need to be copied in restart. */ pipeintr->bytes_read = 0; rm_ptr->mupintr = TRUE; (TREF(pipefifo_interrupt))++; REVERT_GTMIO_CH(&io_curr_device, ch_set); async_action(FALSE); assertpro(FALSE); /* Should *never* return from async_action */ return FALSE; /* For the compiler.. */ } chset = io_ptr->ichset; /* in case UTF-16 was changed */ } status = tot_bytes_read = buff_len; /* for EOF checking at the end */ char_ptr = rm_ptr->inbuf_off; PIPE_DEBUG(PRINTF("iorm_readfl: inbuf: 0x%08lx, top: 0x%08lx, off: 0x%08lx\n", rm_ptr->inbuf, rm_ptr->inbuf_top, rm_ptr->inbuf_off); DEBUGPIPEFLUSH;); PIPE_DEBUG(PRINTF("iorm_readfl: status: %d, width: %d", status, width); DEBUGPIPEFLUSH;); if (0 < buff_len) { for (char_count = 0; char_count < width && char_ptr < rm_ptr->inbuf_top; char_count++) { /* count chars and check for validity */ switch (chset) { case CHSET_UTF8: if (UTF8_VALID(char_ptr, rm_ptr->inbuf_top, mblen)) { bytes_count += mblen; char_ptr += mblen; } else { SETZACANCELTIMER; /* nothing in temp so set size to 0 */ iorm_readfl_badchar(v, 0, mblen, char_ptr, rm_ptr->inbuf_top); rm_ptr->inbuf_off = char_ptr + mblen; /* mark as read */ UTF8_BADCHAR(mblen, char_ptr, rm_ptr->inbuf_top, chset_names[chset].len, chset_names[chset].addr); } break; case CHSET_UTF16BE: if ((2 <= (mblen = (rm_ptr->inbuf_top - char_ptr))) && UTF16BE_VALID(char_ptr, rm_ptr->inbuf_top, mblen)) { bytes_count += mblen; char_ptr += mblen; } else { SETZACANCELTIMER; /* nothing in temp so set size to 0 */ iorm_readfl_badchar(v, 0, mblen, char_ptr, rm_ptr->inbuf_top); rm_ptr->inbuf_off = char_ptr + mblen; /* mark as read */ UTF8_BADCHAR(mblen, char_ptr, rm_ptr->inbuf_top, chset_names[chset].len, chset_names[chset].addr); } break; case CHSET_UTF16LE: if ((2 <= (mblen = (rm_ptr->inbuf_top - char_ptr))) && UTF16LE_VALID(char_ptr, rm_ptr->inbuf_top, mblen)) { bytes_count += mblen; char_ptr += mblen; } else { SETZACANCELTIMER; /* nothing in temp so set size to 0 */ iorm_readfl_badchar(v, 0, mblen, char_ptr, rm_ptr->inbuf_top); rm_ptr->inbuf_off = char_ptr + mblen; /* mark as read */ UTF8_BADCHAR(mblen, char_ptr, rm_ptr->inbuf_top, chset_names[chset].len, chset_names[chset].addr); } break; default: assertpro(FALSE); } } if (rm_ptr->follow && (char_count == width) && (TRUE == follow_timeout)) follow_timeout = FALSE; v->str.len = INTCAST(char_ptr - rm_ptr->inbuf_off); UTF8_ONLY(v->str.char_len = char_count;) if (0 < v->str.len) { if (CHSET_UTF8 == chset) { if (!IS_AT_END_OF_STRINGPOOL(temp, 0)) { /* make sure enough space to store buffer */ ENSURE_STP_FREE_SPACE(GTM_MB_LEN_MAX * width); } memcpy(stringpool.free, rm_ptr->inbuf_off, v->str.len); } else { v->str.addr = (char *)rm_ptr->inbuf_off; v->str.len = gtm_conv(chset_desc[chset], chset_desc[CHSET_UTF8], &v->str, NULL, NULL); } v->str.addr = (char *)stringpool.free; rm_ptr->inbuf_off += char_ptr - rm_ptr->inbuf_off; } } } else { /* VARIABLE or STREAM */ PIPE_DEBUG(PRINTF("enter utf stream: %d inbuf_pos: %d inbuf_off: %d follow: %d\n", rm_ptr->done_1st_read, rm_ptr->inbuf_pos, rm_ptr->inbuf_off, rm_ptr->follow); DEBUGPIPEFLUSH); assert(IS_UTF_CHSET(chset)); if (rm_ptr->inbuf_pos <= rm_ptr->inbuf_off) { /* reset buffer pointers */ if (!zint_restart) { rm_ptr->inbuf_pos = rm_ptr->inbuf_off = rm_ptr->inbuf; bytes2read = (CHSET_UTF8 == chset) ? 1 : 2; } } else { /* use bytes from buffer for character left over from last read */ assert(rm_ptr->done_1st_read); if (!zint_restart) { bytes2read = 0; /* use bytes from buffer left over from last read */ } bytes_read = char_bytes_read = (int)(rm_ptr->inbuf_pos - rm_ptr->inbuf_off); if (!zint_restart) add_bytes = bytes_read - 1; /* to satisfy asserts */ } PIPE_DEBUG(PRINTF("1: status: %d bytes2read: %d rm_ptr->start_pos: %d " "rm_ptr->tot_bytes_in_buffer: %d char_bytes_read: %d add_bytes: %d\n", status, bytes2read, rm_ptr->start_pos, rm_ptr->tot_bytes_in_buffer, char_bytes_read, add_bytes); DEBUGPIPEFLUSH); char_start = rm_ptr->inbuf_off; if (rm_ptr->follow) { PIPE_DEBUG(PRINTF(" %d utf streaming with follow\n", pid); DEBUGPIPEFLUSH); /* rms-file device in follow mode */ if (timed) { /* recalculate msec_timeout and sleep_left as &end_time - ¤t_time */ if (0 < msec_timeout) { /* get the current time */ sys_get_curr_time(¤t_time); time_left = sub_abs_time(&end_time, ¤t_time); if (0 > time_left.at_sec) { msec_timeout = -1; out_of_time = TRUE; } else msec_timeout = (int4)(time_left.at_sec * MILLISECS_IN_SEC + DIVIDE_ROUND_UP(time_left.at_usec, MICROSECS_IN_MSEC)); /* make sure it terminates with out_of_time */ if (!out_of_time && !msec_timeout) msec_timeout = 1; sleep_left = msec_timeout; } else sleep_left = 0; } /* if zeof is set in follow mode then ignore any previous zeof */ if (TRUE == io_ptr->dollar.zeof) io_ptr->dollar.zeof = prin_in_dev_failure = FALSE; } do { /* In case the CHSET changes from non-UTF-16 to UTF-16 and a read has already been done, * there's no way to read the BOM bytes & to determine the variant. So default to UTF-16BE. */ if (rm_ptr->done_1st_read && (!IS_UTF16_CHSET(rm_ptr->ichset_utf16_variant) && (CHSET_UTF16 == io_ptr->ichset))) { chset = io_ptr->ichset = rm_ptr->ichset_utf16_variant = CHSET_UTF16BE; } if (!rm_ptr->done_1st_read) { /* need to check BOM */ if (rm_ptr->follow) { status = iorm_get_bom_fol(io_ptr, &tot_bytes_read, &msec_timeout, timed, &bom_timeout, end_time); } else status = iorm_get_bom(io_ptr, &blocked_in, rm_ptr->is_pipe, flags, &tot_bytes_read, timer_id, &msec_timeout, pipe_zero_timeout); /* if we got an interrupt then the iorm_get_bom did not complete so not as much state needs to be saved*/ if (outofband && (pipe_or_fifo || rm_ptr->follow)) { PIPE_DEBUG(PRINTF(" %d utf1 stream outofband\n", pid); DEBUGPIPEFLUSH); PUSH_MV_STENT(MVST_ZINTDEV); mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)stringpool.free; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = 0; mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE; pipeintr->who_saved = pipewhich_readfl; if ((0 < msec_timeout) && (NO_M_TIMEOUT != msec_timeout)) { pipeintr->end_time = end_time; pipeintr->end_time_valid = TRUE; if (!rm_ptr->follow) cancel_timer(timer_id); /* Worry about timer if/when we come back */ } pipeintr->max_bufflen = exp_width; /* nothing copied to stringpool.free yet */ pipeintr->bytes_read = 0; pipeintr->bytes2read = bytes2read; pipeintr->add_bytes = add_bytes; pipeintr->bytes_count = bytes_count; rm_ptr->mupintr = TRUE; (TREF(pipefifo_interrupt))++; PIPE_DEBUG(PRINTF(" %d utf1.1 stream outofband\n", pid); DEBUGPIPEFLUSH); REVERT_GTMIO_CH(&io_curr_device, ch_set); async_action(FALSE); assertpro(FALSE); /* Should *never* return from async_action */ return FALSE; /* For the compiler.. */ } /* Set the UTF-16 variant if not already set, and has been determined right now */ if (!IS_UTF16_CHSET(rm_ptr->ichset_utf16_variant) && (chset != io_ptr->ichset)) rm_ptr->ichset_utf16_variant = io_ptr->ichset; chset = io_ptr->ichset; /* UTF16 will have changed to UTF16BE or UTF16LE */ } if (0 <= status && bytes2read && rm_ptr->bom_buf_cnt > rm_ptr->bom_buf_off) { PIPE_DEBUG(PRINTF("2: status: %d bytes2read: %d bom_buf_cnt: %d bom_buf_off: %d\n", status, bytes2read, rm_ptr->bom_buf_cnt, rm_ptr->bom_buf_off); DEBUGPIPEFLUSH); from_bom = MIN((rm_ptr->bom_buf_cnt - rm_ptr->bom_buf_off), bytes2read); memcpy(rm_ptr->inbuf_pos, &rm_ptr->bom_buf[rm_ptr->bom_buf_off], from_bom); rm_ptr->bom_buf_off += from_bom; rm_ptr->inbuf_pos += from_bom; bytes2read -= from_bom; /* now in buffer */ bytes_read = from_bom; char_bytes_read += from_bom; rm_ptr->file_pos += from_bom; /* If there is no BOM increment file position */ status = 0; } if ((0 <= status) && (0 < bytes2read)) { PIPE_DEBUG(PRINTF("3: status: %d bytes2read: %d rm_ptr->start_pos: %d " "rm_ptr->tot_bytes_in_buffer: %d\n", status, bytes2read, rm_ptr->start_pos, rm_ptr->tot_bytes_in_buffer); DEBUGPIPEFLUSH); /* If it is a pipe and at least one character is read, a timer with timer_id will be started. It is canceled later in this routine if not expired prior to return */ if (rm_ptr->start_pos == rm_ptr->tot_bytes_in_buffer) { DEBUG_ONLY(MEMSET_IF_DEFINED(rm_ptr->tmp_buffer, 0, CHUNK_SIZE)); rm_ptr->start_pos = rm_ptr->tot_bytes_in_buffer = 0; /* Read CHUNK_SIZE bytes from device into the temporary buffer. By doing this * one-byte reads can be avoided when in UTF mode. * */ if (rm_ptr->follow && (FALSE == bom_timeout)) { /* In follow mode a read returns an EOF if no more bytes are available. */ status = read(fildes, rm_ptr->tmp_buffer, CHUNK_SIZE); if (0 < status) { if (rm_ptr->input_encrypted) READ_ENCRYPTED_DATA(rm_ptr, io_ptr->trans_name, rm_ptr->tmp_buffer, status, NULL); rm_ptr->read_occurred = TRUE; rm_ptr->done_1st_read = TRUE; } if (0 == status) /* end of file */ { if ((TRUE == timed) && (0 >= sleep_left)) { follow_timeout = TRUE; break; } /* If a timed read, sleep the minimum of 100 ms and sleep_left. * If not a timed read then just sleep 100 ms. */ if (TRUE == timed) { /* recalculate msec_timeout and sleep_left as * &end_time - ¤t_time. */ /* get the current time */ sys_get_curr_time(¤t_time); time_left = sub_abs_time(&end_time, ¤t_time); if (0 > time_left.at_sec) { msec_timeout = -1; out_of_time = TRUE; } else msec_timeout = (int4)(time_left.at_sec * MILLISECS_IN_SEC + DIVIDE_ROUND_UP(time_left.at_usec, MICROSECS_IN_MSEC)); /* make sure it terminates with out_of_time */ if (!out_of_time && !msec_timeout) msec_timeout = 1; sleep_left = msec_timeout; sleep_time = MIN(100, sleep_left); } else sleep_time = 100; if (0 < sleep_time) SHORT_SLEEP(sleep_time); if (outofband) { PIPE_DEBUG(PRINTF(" %d utf2 stream outofband\n", pid); DEBUGPIPEFLUSH); PUSH_MV_STENT(MVST_ZINTDEV); mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)stringpool.free; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = bytes_count; mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE; pipeintr->who_saved = pipewhich_readfl; if ((0 < msec_timeout) && (NO_M_TIMEOUT != msec_timeout)) { pipeintr->end_time = end_time; pipeintr->end_time_valid = TRUE; } pipeintr->max_bufflen = exp_width; /* streaming mode uses bytes_count to show how many bytes are in *temp, but the interrupt re-entrant code uses bytes_read */ pipeintr->bytes_read = bytes_count; pipeintr->bytes2read = bytes2read; pipeintr->char_count = char_count; pipeintr->add_bytes = add_bytes; pipeintr->bytes_count = bytes_count; PIPE_DEBUG(PRINTF("utf2 stream outofband " "char_bytes_read %d add_bytes " "%d bytes_count %d\n", char_bytes_read, add_bytes, bytes_count); DEBUGPIPEFLUSH); rm_ptr->mupintr = TRUE; /* Don't step on our parade in the interrupt */ stringpool.free += bytes_count; (TREF(pipefifo_interrupt))++; REVERT_GTMIO_CH(&io_curr_device, ch_set); async_action(FALSE); assertpro(FALSE); /* never returns from async_action */ return FALSE; /* For the compiler.. */ } continue; /* for now try and read again if eof or no input ready */ } else if (-1 == status && errno != EINTR) /* error returned */ { bytes_count = 0; break; } } else { DOREADRLTO2(fildes, rm_ptr->tmp_buffer, CHUNK_SIZE, out_of_time, &blocked_in, rm_ptr->is_pipe, flags, status, &utf_tot_bytes_read, timer_id, &msec_timeout, pipe_zero_timeout, pipe_or_fifo, pipe_or_fifo); if ((0 < status) || ((0 > status) && (0 < utf_tot_bytes_read))) { if (rm_ptr->input_encrypted) READ_ENCRYPTED_DATA(rm_ptr, io_ptr->trans_name, rm_ptr->tmp_buffer, (status > 0) ? status : utf_tot_bytes_read, NULL); rm_ptr->read_occurred = TRUE; rm_ptr->done_1st_read = TRUE; } } PIPE_DEBUG(PRINTF("4: read chunk status: %d utf_tot_bytes_read: %d\n", status, utf_tot_bytes_read); DEBUGPIPEFLUSH); /* If status is -1, then total number of bytes read will be stored in * utf_tot_bytes_read. If chunk read returned some bytes, then ignore outofband. * We won't try a read again until bytes are processed. */ if ((pipe_or_fifo || rm_ptr->follow) && outofband && (0 >= status)) { PIPE_DEBUG(PRINTF(" %d utf2 stream outofband\n", pid); DEBUGPIPEFLUSH); PUSH_MV_STENT(MVST_ZINTDEV); mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)stringpool.free; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = bytes_count; mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE; pipeintr->who_saved = pipewhich_readfl; if ((0 < msec_timeout) && (NO_M_TIMEOUT != msec_timeout)) { pipeintr->end_time = end_time; pipeintr->end_time_valid = TRUE; /* Worry about timer if/when we come back */ if (!rm_ptr->follow) cancel_timer(timer_id); } pipeintr->max_bufflen = exp_width; /* streaming mode uses bytes_count to show how many bytes are in *temp, * but the interrupt re-entrant code uses bytes_read. */ pipeintr->bytes_read = bytes_count; pipeintr->bytes2read = bytes2read; pipeintr->char_count = char_count; pipeintr->add_bytes = add_bytes; pipeintr->bytes_count = bytes_count; PIPE_DEBUG(PRINTF("utf2 stream outofband " "char_bytes_read %d add_bytes %d bytes_count %d\n", char_bytes_read, add_bytes, bytes_count); DEBUGPIPEFLUSH); rm_ptr->mupintr = TRUE; /* Don't step on our parade in the interrupt */ stringpool.free += bytes_count; (TREF(pipefifo_interrupt))++; REVERT_GTMIO_CH(&io_curr_device, ch_set); async_action(FALSE); assertpro(FALSE); /* Should *never* return from async_action */ return FALSE; /* For the compiler.. */ } if (-1 == status) { rm_ptr->tot_bytes_in_buffer = utf_tot_bytes_read; tot_bytes_read = utf_tot_bytes_read; if (!rm_ptr->is_pipe && !rm_ptr->fifo) status = utf_tot_bytes_read; } else rm_ptr->tot_bytes_in_buffer = status; } else if (pipe_zero_timeout) out_of_time = FALSE; /* reset out_of_time for pipe as no actual read is done */ if (0 <= rm_ptr->tot_bytes_in_buffer) { min_bytes_to_copy = MIN(bytes2read, (rm_ptr->tot_bytes_in_buffer - rm_ptr->start_pos)); assert(0 <= min_bytes_to_copy); assert(CHUNK_SIZE >= min_bytes_to_copy); assert(rm_ptr->start_pos <= rm_ptr->tot_bytes_in_buffer); /* If we have data in buffer, copy it to inbuf_pos */ if (0 < min_bytes_to_copy) { /* If min_bytes_to_copy == 1, avoid memcpy by direct assignment */ if (1 == min_bytes_to_copy) *rm_ptr->inbuf_pos = rm_ptr->tmp_buffer[rm_ptr->start_pos]; else { memcpy(rm_ptr->inbuf_pos, rm_ptr->tmp_buffer + rm_ptr->start_pos, min_bytes_to_copy); } } /* Increment start_pos */ rm_ptr->start_pos += min_bytes_to_copy; /* Set status appropriately so that the following code can continue as if * a one byte read happened. In case of a negative status, preserve the status * as-is. */ status = (0 <= status) ? min_bytes_to_copy : status; } } if (0 <= status) { rm_ptr->inbuf_pos += status; bytes_read += status; /* bytes read this pass */ char_bytes_read += status; /* bytes for this character */ tot_bytes_read += bytes_read; /* total bytes read this command */ rm_ptr->file_pos += status; PIPE_DEBUG(PRINTF("5: status: %d bytes2read: %d bytes_read: %d char_bytes_read: " "%d tot_bytes_read: %d\n", status, bytes2read, bytes_read, char_bytes_read, tot_bytes_read); DEBUGPIPEFLUSH); if ((0 < bytes2read) && (0 == status)) { /* EOF on read */ if (0 == char_bytes_read) break; /* nothing read for this char so treat as EOF */ assert(1 < (bytes2read + bytes_read)); /* incomplete character */ SETZACANCELTIMER; iorm_readfl_badchar(v, (int)((unsigned char *)temp - stringpool.free), bytes_read, char_start, rm_ptr->inbuf_pos); rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */ UTF8_BADCHAR(bytes_read, char_start, rm_ptr->inbuf_pos, chset_names[chset].len, chset_names[chset].addr); } else if (status < bytes2read) { bytes2read -= status; continue; } if (CHSET_UTF8 == chset) { PIPE_DEBUG(PRINTF("6: char_bytes_read: %d\n", char_bytes_read); DEBUGPIPEFLUSH); if (1 == char_bytes_read) { add_bytes = UTF8_MBFOLLOW(char_start); if (0 < add_bytes) { bytes2read = add_bytes; PIPE_DEBUG(PRINTF("7: bytes2read: %d\n", bytes2read); DEBUGPIPEFLUSH); continue; } else if (-1 == add_bytes) { SETZACANCELTIMER; iorm_readfl_badchar(v, (int)((unsigned char *)temp - stringpool.free), char_bytes_read, char_start, (char_start + char_bytes_read)); rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */ UTF8_BADCHAR(char_bytes_read, char_start, char_start + char_bytes_read, STRLEN("UTF-8"), "UTF-8"); } if (ASCII_LF == *char_start) { if (rm_ptr->crlastbuff) assert(rm_ptr->crlast); if (rm_ptr->crlast && !rdone) { /* ignore LF following CR */ rm_ptr->crlast = FALSE; rm_ptr->inbuf_pos = char_start; bytes2read = 1; /* reset */ bytes_read = char_bytes_read = 0; /* start fresh */ tot_bytes_read--; if (rm_ptr->crlastbuff) { /* $KEY contains CR at this point. append LF. * Also, CR was the last char of the previous buffer * We needed to inspect this char to be LF and did * not terminate the previous READ. Terminate it. */ rm_ptr->crlastbuff = FALSE; assert(ASCII_CR == io_ptr->dollar.key[0]); assert(ASCII_CR == io_ptr->dollar.zb[0]); SET_UTF8_DOLLARKEY_DOLLARZB(u32_line_term[U32_LT_LF] , &io_ptr->dollar.key[UTF8CRLEN], &io_ptr->dollar.zb[UTF8CRLEN]); break; } continue; } else { line_term_seen = TRUE; rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */ SET_UTF8_DOLLARKEY_DOLLARZB(u32_line_term[U32_LT_LF], &io_ptr->dollar.key[0], &io_ptr->dollar.zb[0]); if (!rdone) break; } } else if (rm_ptr->crlastbuff) { /* CR was the last char of the previous buffer. * We needed to inspect this char to be LF. * The previous READ was not terminated, terminate it. * Also reset the buffer pointers so this char goes into next read. */ rm_ptr->start_pos -= min_bytes_to_copy; rm_ptr->inbuf_pos = char_start; rm_ptr->crlastbuff = FALSE; if (!rdone) break; } rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */ if (ASCII_CR == *char_start) { rm_ptr->crlast = TRUE; line_term_seen = TRUE; SET_UTF8_DOLLARKEY_DOLLARZB(u32_line_term[U32_LT_CR], &io_ptr->dollar.key[0], &io_ptr->dollar.zb[0]); /* Peep into the next char to see if it's LF and append it to $KEY. * If The buffer ends with 'CR', the next char in the buffer not yet * read can be LF. So don't terminate this read and force the next * read to check for LF in 1st char. * Same is the case if reading from BOM bytes. */ if (0 == rm_ptr->tot_bytes_in_buffer) { /* Reading from BOM */ if (rm_ptr->bom_buf_off == rm_ptr->bom_buf_cnt) rm_ptr->crlastbuff = TRUE; else { rm_ptr->crlastbuff = FALSE; if (ASCII_LF == rm_ptr->bom_buf[rm_ptr->bom_buf_off]) { SET_UTF8_DOLLARKEY_DOLLARZB( u32_line_term[U32_LT_LF], &io_ptr->dollar.key[UTF8CRLEN], &io_ptr->dollar.zb[UTF8CRLEN]); } } } else { /* Reading from the buffer */ if (rm_ptr->start_pos == rm_ptr->tot_bytes_in_buffer) rm_ptr->crlastbuff = TRUE; else { rm_ptr->crlastbuff = FALSE; if (ASCII_LF == rm_ptr->tmp_buffer[rm_ptr->start_pos]) { SET_UTF8_DOLLARKEY_DOLLARZB( u32_line_term[U32_LT_LF], &io_ptr->dollar.key[UTF8CRLEN], &io_ptr->dollar.zb[UTF8CRLEN]); } } } if (!rdone && !rm_ptr->crlastbuff) break; } else { rm_ptr->crlast = FALSE; rm_ptr->crlastbuff = FALSE; } if (ASCII_FF == *char_start) { line_term_seen = TRUE; SET_UTF8_DOLLARKEY_DOLLARZB(u32_line_term[U32_LT_FF], &io_ptr->dollar.key[0], &io_ptr->dollar.zb[0]); if (!rdone) break; } if (!rm_ptr->crlastbuff) *temp++ = *char_start; PIPE_DEBUG(PRINTF("8: move *char_start to *temp\n"); DEBUGPIPEFLUSH); } else { PIPE_DEBUG(PRINTF("9: char_bytes_read: %d add_bytes: %d\n", char_bytes_read, add_bytes); DEBUGPIPEFLUSH); assert(char_bytes_read == (add_bytes + 1)); nextmb = UTF8_MBTOWC(char_start, rm_ptr->inbuf_pos, utf_code); if (WEOF == utf_code) { /* invalid mb char */ SETZACANCELTIMER; iorm_readfl_badchar(v, (int)((unsigned char *)temp - stringpool.free), char_bytes_read, char_start, rm_ptr->inbuf_pos); rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */ UTF8_BADCHAR(char_bytes_read, char_start, rm_ptr->inbuf_pos, 0, NULL); } assert(nextmb == rm_ptr->inbuf_pos); rm_ptr->inbuf_off = nextmb; /* mark as read */ rm_ptr->crlast = FALSE; if (u32_line_term[U32_LT_NL] == utf_code || u32_line_term[U32_LT_LS] == utf_code || u32_line_term[U32_LT_PS] == utf_code) { line_term_seen = TRUE; SET_UTF8_DOLLARKEY_DOLLARZB(utf_code, &io_ptr->dollar.key[0], &io_ptr->dollar.zb[0]); if (!rdone) break; } memcpy(temp, char_start, char_bytes_read); PIPE_DEBUG(PRINTF("10: move char_bytes_read: %d to *temp\n", char_bytes_read); DEBUGPIPEFLUSH); temp += char_bytes_read; } if (!rm_ptr->crlastbuff) bytes_count += char_bytes_read; PIPE_DEBUG(PRINTF("11: bytes_count: %d \n", bytes_count); DEBUGPIPEFLUSH); if (bytes_count > MAX_STRLEN) { /* need to leave bytes for this character in buffer */ bytes_count -= char_bytes_read; rm_ptr->inbuf_off = char_start; rm_ptr->file_pos -= char_bytes_read; break; } } else if (CHSET_UTF16BE == chset || CHSET_UTF16LE == chset) { if (2 == char_bytes_read) { if (CHSET_UTF16BE == chset) add_bytes = UTF16BE_MBFOLLOW(char_start, rm_ptr->inbuf_pos); else add_bytes = UTF16LE_MBFOLLOW(char_start, rm_ptr->inbuf_pos); if (1 < add_bytes) { /* UTF16xE_MBFOLLOW returns 1 or 3 if valid */ bytes2read = add_bytes - 1; continue; } else if (-1 == add_bytes) { /* not valid */ SETZACANCELTIMER; iorm_readfl_badchar(v, (int)((unsigned char *)temp - stringpool.free), char_bytes_read, char_start, rm_ptr->inbuf_pos); rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */ UTF8_BADCHAR(char_bytes_read, char_start, rm_ptr->inbuf_pos, chset_names[chset].len, chset_names[chset].addr); } } assert(char_bytes_read == (add_bytes + 1)); if (CHSET_UTF16BE == chset) nextmb = UTF16BE_MBTOWC(char_start, rm_ptr->inbuf_pos, utf_code); else nextmb = UTF16LE_MBTOWC(char_start, rm_ptr->inbuf_pos, utf_code); if ( (WEOF == utf_code) || (nextmb != rm_ptr->inbuf_pos) ) { /* invalid mb char */ SETZACANCELTIMER; iorm_readfl_badchar(v, (int)((unsigned char *)temp - stringpool.free), char_bytes_read, char_start, rm_ptr->inbuf_pos); rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */ UTF8_BADCHAR(char_bytes_read, char_start, rm_ptr->inbuf_pos, chset_names[chset].len, chset_names[chset].addr); } assert(nextmb == rm_ptr->inbuf_pos); if (u32_line_term[U32_LT_LF] == utf_code) { if (rm_ptr->crlastbuff) assert(rm_ptr->crlast); if (rm_ptr->crlast && !rdone) { /* ignore LF following CR */ rm_ptr->crlast = FALSE; rm_ptr->inbuf_pos = char_start; bytes2read = 2; /* reset */ bytes_read = char_bytes_read = 0; /* start fresh */ tot_bytes_read -= 2; if (rm_ptr->crlastbuff) { /* $KEY contains CR at this point. append LF * Also, CR was the last char of the previous buffer. * We needed to inspect this char to be LF and did * not terminate the previous READ. Terminate it. */ rm_ptr->crlastbuff = FALSE; assert(io_ptr->dollar.key[0] == ASCII_CR); assert(io_ptr->dollar.zb[0] == ASCII_CR); SET_UTF8_DOLLARKEY_DOLLARZB(u32_line_term[U32_LT_LF], &io_ptr->dollar.key[UTF8CRLEN], &io_ptr->dollar.zb[UTF8CRLEN]); break; } continue; } else { line_term_seen = TRUE; rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */ SET_UTF8_DOLLARKEY_DOLLARZB(u32_line_term[U32_LT_LF], &io_ptr->dollar.key[0], &io_ptr->dollar.zb[0]); if (!rdone) break; } } else if (rm_ptr->crlastbuff) { /* CR was the last char of the previous buffer. * We needed to inspect this char for LF. * The previous READ was not terminated, terminate it. * Also reset the buffer pointers so this char goes into next read. */ rm_ptr->start_pos -= min_bytes_to_copy; rm_ptr->inbuf_pos = char_start; rm_ptr->crlastbuff = FALSE; if (!rdone) break; } rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */ for (ltind = 0; 0 < u32_line_term[ltind]; ltind++) if (u32_line_term[ltind] == utf_code) { line_term_seen = TRUE; SET_UTF8_DOLLARKEY_DOLLARZB(utf_code, &io_ptr->dollar.key[0], &io_ptr->dollar.zb[0]); break; } if (u32_line_term[U32_LT_CR] == utf_code) { rm_ptr->crlast = TRUE; /* Peep into the next char to see if it's LF and append it to $KEY. * If The buffer ends with 'CR', the next char in the buffer not yet read * can be LF. So don't terminate this read and force the next read * to check for LF in 1st char. * Same is the case if reading from BOM bytes. */ if (0 == rm_ptr->tot_bytes_in_buffer) rm_ptr->crlastbuff = TRUE; /* CR read as BOM. */ else { /* Reading from buffer */ if (rm_ptr->start_pos == rm_ptr->tot_bytes_in_buffer) rm_ptr->crlastbuff = TRUE; /* CR last char of buf */ else { /* This is UTF-16. Consider the BE/LE differences. */ rm_ptr->crlastbuff = FALSE; if ((CHSET_UTF16BE == chset) ? ((0x0 == rm_ptr->tmp_buffer[rm_ptr->start_pos]) && (ASCII_LF == rm_ptr->tmp_buffer[rm_ptr->start_pos+1])) : ((0x0 == rm_ptr->tmp_buffer[rm_ptr->start_pos+1]) && (ASCII_LF == rm_ptr->tmp_buffer[rm_ptr->start_pos]))) { SET_UTF8_DOLLARKEY_DOLLARZB(u32_line_term[U32_LT_LF] , &io_ptr->dollar.key[UTF8CRLEN], &io_ptr->dollar.zb[UTF8CRLEN]); } } } } else { rm_ptr->crlast = FALSE; rm_ptr->crlastbuff = FALSE; } if (line_term_seen && !rdone && !rm_ptr->crlastbuff) break; /* out of do loop */ if (!rm_ptr->crlastbuff) { temp_start = temp; temp = (char *)UTF8_WCTOMB(utf_code, temp_start); bytes_count += (int4)(temp - temp_start); } if (bytes_count > MAX_STRLEN) { /* need to leave bytes for this character in buffer */ bytes_count -= (int4)(temp - temp_start); rm_ptr->inbuf_off = char_start; rm_ptr->file_pos -= char_bytes_read; break; } } else assertpro(FALSE); if (!rm_ptr->crlastbuff) { char_count++; } char_start = rm_ptr->inbuf_pos = rm_ptr->inbuf_off = rm_ptr->inbuf; bytes_read = char_bytes_read = 0; bytes2read = (CHSET_UTF8 == chset) ? 1 : 2; } else { inchar = 0; if (errno == 0) { tot_bytes_read = 0; status = 0; } else if (EINTR == errno) { if (out_of_time) status = -2; else continue; /* Ignore interrupt if not our wakeup */ } break; } } while (char_count < width && bytes_count < MAX_STRLEN); } } PIPE_DEBUG(PRINTF(" %d notoutofband, %d %d\n", pid, status, line_term_seen); DEBUGPIPEFLUSH); real_errno = errno; if (TRUE == do_clearerr) CLEARERR(filstr); memcpy(io_ptr->dollar.device, "0", SIZEOF("0")); io_ptr->dollar.za = 0; /* On error, getc() returns EOF while read() returns -1. Both code paths converge here. Thankfully EOF is -1 on all * platforms that we know of so it is enough to check for -1 status here. Assert that below. */ assert(EOF == -1); if ((-1 == status) && (EINTR != real_errno)) { v->str.len = 0; v->str.addr = (char *)stringpool.free; /* ensure valid address */ if (EAGAIN != real_errno) { /* Cancel the timer before taking the error return */ if (timed && !out_of_time) cancel_timer(timer_id); io_ptr->dollar.za = ZA_IO_ERR; /* save error in $device */ SET_DOLLARDEVICE_ONECOMMA_STRERROR(io_ptr, real_errno); ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, FALSE, FALSE); /* FALSE, FALSE: READ, not socket*/ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) real_errno); } ret = FALSE; } if (timed) { /* No timer if msec_timeout is zero, so handle the timer in the else. */ if (msec_timeout == 0) { if (!rm_ptr->is_pipe || FALSE == blocked_in) { FCNTL3(fildes, F_SETFL, flags, fcntl_res); if (0 > fcntl_res) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(io_ptr, save_errno); ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, FALSE, FALSE); /* FALSE, FALSE: READ, not socket*/ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, save_errno); } } if ((rm_ptr->is_pipe && (0 == status)) || (rm_ptr->fifo && (0 == status || real_errno == EAGAIN))) ret = FALSE; } else { if (out_of_time) ret = FALSE; else if (!rm_ptr->follow) /* if follow then no timer started */ cancel_timer(timer_id); } } if ((0 == status) && (0 == tot_bytes_read)) { v->str.len = 0; v->str.addr = (char *)stringpool.free; /* ensure valid address */ UTF8_ONLY(v->str.char_len = 0;) if (rm_ptr->follow) { if (TRUE == io_ptr->dollar.zeof) io_ptr->dollar.zeof = prin_in_dev_failure = FALSE; /* no EOF in follow mode */ REVERT_GTMIO_CH(&io_curr_device, ch_set); return FALSE; } /* on end of file set $za to 9 */ io_ptr->dollar.za = ZA_IO_ERR; *dollarx_ptr = 0; (*dollary_ptr)++; if (WBTEST_ENABLED(WBTEST_DOLLARDEVICE_BUFFER)) SET_DOLLARDEVICE_ERRSTR(io_ptr, ONE_COMMA_DEV_DET_EOF_DOLLARDEVICE); else SET_DOLLARDEVICE_ERRSTR(io_ptr, ONE_COMMA_DEV_DET_EOF); ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, FALSE, FALSE); /* FALSE, FALSE: READ, not socket*/ if (!prin_dm_io && (TRUE == io_ptr->dollar.zeof) && (RM_READ == saved_lastop)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_IOEOF); io_ptr->dollar.zeof = TRUE; if (!prin_dm_io && (0 < io_ptr->error_handler.len)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_IOEOF); if ((pipe_zero_timeout || rm_ptr->fifo) && out_of_time) { ret = TRUE; out_of_time = FALSE; } } else { if (rm_ptr->is_pipe) { if ((tot_bytes_read > 0) || (out_of_time && blocked_in)) ret = TRUE; else ret = FALSE; } if (rm_ptr->follow && !rm_ptr->fixed && !line_term_seen) { if (utf_active) follow_width = char_count; else follow_width = bytes_count; if (!follow_width || (follow_width < width)) ret = FALSE; } if (!utf_active || !rm_ptr->fixed) { /* if UTF8 and fixed, already setup the mstr */ v->str.len = bytes_count; v->str.addr = (char *)stringpool.free; UTF8_ONLY(v->str.char_len = char_count;) if (!utf_active) char_count = bytes_count; } if (!rm_ptr->fixed && line_term_seen) { *dollarx_ptr = 0; (*dollary_ptr)++; } else { *dollarx_ptr += char_count; if ((*dollarx_ptr >= io_ptr->width) && (rm_ptr->fixed || io_ptr->wrap)) { *dollary_ptr += (*dollarx_ptr / io_ptr->width); if(io_ptr->length) *dollary_ptr %= io_ptr->length; *dollarx_ptr %= io_ptr->width; } } } if (follow_timeout) ret = FALSE; assert (FALSE == rm_ptr->mupintr); REVERT_GTMIO_CH(&io_curr_device, ch_set); return (rm_ptr->is_pipe && out_of_time) ? FALSE : ret; } fis-gtm-V7.0-005/sr_unix/iorm_use.c0000644000032200000250000012166014342376330016035 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include #include "gtm_unistd.h" #include "gtm_stat.h" #include "gtm_stdio.h" #include "gtm_iconv.h" #include "gtm_limits.h" #include "copy.h" #include "io.h" #include "iormdef.h" #include "io_params.h" #include "iosp.h" #include "eintr_wrappers.h" #include "stringpool.h" #include "gtm_stdlib.h" #include "min_max.h" #include "arit.h" #include "gtmcrypt.h" #include "error.h" #include "send_msg.h" #include "get_fs_block_size.h" #include "op.h" #include "util.h" #include "indir_enum.h" #ifdef UTF8_SUPPORTED #include "gtm_conv.h" #include "gtm_utf8.h" #endif /* Only want to do fstat() once on this file, not on every use. */ #define FSTAT_CHECK(GETMODE) \ if (!(GETMODE) || !(fstat_done)) \ { \ FSTAT_FILE(rm_ptr->fildes, &statbuf, fstat_res); \ if (-1 == fstat_res) \ { \ save_errno = errno; \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, \ RTS_ERROR_LITERAL("fstat"), \ CALLFROM, save_errno); \ } \ fstat_done = TRUE; \ } \ if (GETMODE && !get_mode_done) \ { \ mode = mode1 = statbuf.st_mode; \ get_mode_done = TRUE; \ } #define IS_PADCHAR_VALID(chset, padchar) (IS_ASCII(padchar)) /* limit seek string to a signed long long */ #define LIMIT_SEEK_STR NUM_DEC_DG_2L + 2 /* define size of early_terminate message */ #define INVALID_SEEK_HEAD "SEEK value " #define INVALID_SEEK_TAIL " invalid." /* define max size of SDSEEKERR error string */ #define MAX_ERROR_SIZE STR_LIT_LEN(INVALID_SEEK_HEAD) + MAXDEVPARLEN + STR_LIT_LEN(INVALID_SEEK_TAIL) /* output SDSEEKERR error */ #define OUTPUT_SDSEEKERR(SEEK_STR) \ { \ SNPRINTF(error_str, MAX_ERROR_SIZE,"%s%s%s", INVALID_SEEK_HEAD, SEEK_STR, INVALID_SEEK_TAIL); \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SDSEEKERR, 2, LEN_AND_STR(error_str)); \ } /* Check if the IV for the specified direction has changed. */ #define IV_CHANGED(DIRECTION, NEW_IV, RM_PTR) \ ((NEW_IV.len != RM_PTR->DIRECTION ## _iv.len) \ || (0 != memcmp(NEW_IV.addr, RM_PTR->DIRECTION ## _iv.addr, NEW_IV.len))) /* Check if the key for the specified direction has changed. */ #define KEY_CHANGED(DIRECTION, NEW_KEY, RM_PTR) \ ((NEW_KEY.len != RM_PTR->DIRECTION ## _key.len) \ || (0 != memcmp(NEW_KEY.addr, RM_PTR->DIRECTION ## _key.addr, NEW_KEY.len))) #define GET_LEN_AND_ADDR(ADDR, LEN) \ LEN = (int)(*(pp->str.addr + p_offset)); \ ADDR = (char *)(pp->str.addr + p_offset + 1); /* Obtain the key and IV from the KEY field, which is expected to be in the format , where iv is optional. */ #define GET_KEY_AND_IV(DIRECTION) \ { \ int i, len, encr_key_delim_pos; \ \ GET_ADDR_AND_LEN(DIRECTION ## _key.addr, DIRECTION ## _key.len); \ for (i = 0, encr_key_delim_pos = len = DIRECTION ## _key.len; i < DIRECTION ## _key.len; i++) \ { \ if (ENCR_KEY_DELIM_CHAR == *((char *)DIRECTION ## _key.addr + i)) \ { \ encr_key_delim_pos = i + 1; \ DIRECTION ## _key.len = i; \ break; \ } \ } \ DIRECTION ## _iv.addr = (char *)DIRECTION ## _key.addr + encr_key_delim_pos; \ DIRECTION ## _iv.len = len - encr_key_delim_pos; \ if (GTMCRYPT_MAX_KEYNAME_LEN <= DIRECTION ## _key.len) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CRYPTKEYTOOBIG, 2, \ DIRECTION ## _key.len, GTMCRYPT_MAX_KEYNAME_LEN - 1); \ else if (0 < DIRECTION ## _key.len) \ DIRECTION ## _key_not_empty = TRUE; \ else \ DIRECTION ## _key_not_empty = FALSE; \ DIRECTION ## _key_entry_present = TRUE; \ } #define INIT_CIPHER_CONTEXT(OPERATION, KEY, IV, CIPHER_HANDLE, DEV_NAME) \ { \ int rv; \ \ GTMCRYPT_INIT_CIPHER_CONTEXT((KEY).len, (KEY).addr, (IV).len, (IV).addr, \ CIPHER_HANDLE, OPERATION, rv); \ if (0 != rv) \ GTMCRYPT_REPORT_ERROR(rv, rts_error, (DEV_NAME)->len, (DEV_NAME)->dollar_io); \ } #define ENCR_KEY_DELIM_CHAR ' ' typedef struct { unsigned short mem; unsigned short grp; } uic_struct; LITREF unsigned char io_params_size[]; GBLREF boolean_t gtm_utf8_mode; GBLREF io_pair io_std_device; /* standard device */ #ifdef UTF8_SUPPORTED GBLREF UConverter *chset_desc[]; LITREF mstr chset_names[]; #endif enum { SEEK_PLUS = 1, /* seek from current position plus offset */ SEEK_MINUS, /* seek from current position minus offset */ SEEK_ABS /* seek absolute position from start of file */ }; error_def(ERR_CRYPTKEYRELEASEFAILED); error_def(ERR_CRYPTKEYTOOBIG); error_def(ERR_CRYPTNOKEYSPEC); error_def(ERR_CRYPTNOOVERRIDE); error_def(ERR_CRYPTNOSEEK); error_def(ERR_CRYPTNOTRUNC); error_def(ERR_DEVPARMNEG); error_def(ERR_RMWIDTHPOS); error_def(ERR_RMWIDTHTOOBIG); error_def(ERR_RECSIZENOTEVEN); error_def(ERR_SYSCALL); error_def(ERR_WIDTHTOOSMALL); error_def(ERR_PADCHARINVALID); error_def(ERR_IOERROR); error_def(ERR_SDSEEKERR); void iorm_use(io_desc *iod, mval *pp) { boolean_t seen_wrap, fstat_done, get_mode_done, outdevparam, prin_in_dev_failure; boolean_t input_key_not_empty, output_key_not_empty, input_key_entry_present, output_key_entry_present; boolean_t init_input_encryption, init_output_encryption, reset_input_encryption, reset_output_encryption; boolean_t seek_specified, ichset_specified, ochset_specified, chset_allowed; unsigned char c; short mode, mode1; int4 length, width, width_bytes, recordsize, recsize_before, padchar; int fstat_res, save_errno, rv; d_rm_struct *rm_ptr; struct stat statbuf; int p_offset; mstr chset_mstr; gtm_chset_t width_chset, temp_chset; int seek_len; char seek_str[LIMIT_SEEK_STR]; char *seek_ptr; int seek_type; off_t seek_value; off_t new_position; off_t current_offset; char *endptr; off_t cur_position; int bom_size_toread; io_log_name *dev_name; mstr input_iv, output_iv, input_key, output_key; char error_str[MAX_ERROR_SIZE]; boolean_t ch_set, def_recsize_before; int disk_block_multiple; size_t fwrite_buffer_size; unsigned char *ch, ct, *end; int chown_res; uic_struct uic; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; p_offset = 0; ESTABLISH_GTMIO_CH(&iod->pair, ch_set); rm_ptr = (d_rm_struct *)iod->dev_sp; input_key_not_empty = output_key_not_empty = FALSE; input_key_entry_present = output_key_entry_present = FALSE; reset_input_encryption = reset_output_encryption = FALSE; init_input_encryption = init_output_encryption = FALSE; seen_wrap = fstat_done = get_mode_done = outdevparam = FALSE; seek_specified = ichset_specified = ochset_specified = chset_allowed = FALSE; dev_name = iod->trans_name; recsize_before = rm_ptr->recordsize; def_recsize_before = rm_ptr->def_recsize; if (ZOS_ONLY(TRUE ||) gtm_utf8_mode) chset_allowed = TRUE; while (*(pp->str.addr + p_offset) != iop_eol) { assert((params) *(pp->str.addr + p_offset) < (params)n_iops); /* need to make sure outdevparam is FALSE for every iteration in case multiple instances of OUTSEEK, etc */ if (TRUE == outdevparam) outdevparam = FALSE; switch (c = *(pp->str.addr + p_offset++)) { case iop_exception: DEF_EXCEPTION(pp, p_offset, iod); break; case iop_fixed: if (iod->state != dev_open) rm_ptr->fixed = TRUE; break; case iop_nofixed: if (iod->state != dev_open) { rm_ptr->fixed = FALSE; if (CHSET_BINARY == iod->ichset) { ichset_specified = FALSE; iod->ichset = CHSET_M; } if (CHSET_BINARY == iod->ochset) { ochset_specified = FALSE; iod->ochset = CHSET_M; } } break; case iop_length: GET_LONG(length, (pp->str.addr + p_offset)); if (length < 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DEVPARMNEG); iod->length = length; break; case iop_w_protection: FSTAT_CHECK(TRUE); mode &= ~(0x07); mode |= (short)(unsigned char)*(pp->str.addr + p_offset); break; case iop_g_protection: FSTAT_CHECK(TRUE); mode &= ~(0x07 << 3); mode |= (short)(unsigned char)*(pp->str.addr + p_offset) << 3; break; case iop_s_protection: case iop_o_protection: FSTAT_CHECK(TRUE); mode &= ~(0x07 << 6); mode |= (short)(unsigned char)*(pp->str.addr + p_offset) << 6; break; case iop_pad: if (iod->state != dev_open) { GET_LONG(padchar, (pp->str.addr + p_offset)); if (!IS_PADCHAR_VALID(iod->ochset, padchar)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(2) ERR_PADCHARINVALID, 0); rm_ptr->padchar = padchar; } break; case iop_readonly: rm_ptr->read_only = TRUE; break; case iop_noreadonly: rm_ptr->read_only = FALSE; break; case iop_writeonly: rm_ptr->write_only = TRUE; break; case iop_nowriteonly: rm_ptr->write_only = FALSE; break; case iop_recordsize: if (dev_open != iod->state || (!IS_UTF_CHSET(iod->ichset) && !IS_UTF_CHSET(iod->ochset)) || (!rm_ptr->done_1st_read && !rm_ptr->done_1st_write)) { /* only if not open, not UTF, or no reads or writes yet */ GET_LONG(recordsize, (pp->str.addr + p_offset)); if (recordsize <= 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_RMWIDTHPOS); else if (MAX_STRLEN < recordsize) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_RMWIDTHTOOBIG); rm_ptr->recordsize = recordsize; rm_ptr->def_recsize = FALSE; /* for sequential device in fixed M mode, recordsize defines initial width */ if (dev_open != iod->state && rm_ptr->fixed && (!rm_ptr->fifo && !rm_ptr->is_pipe) && (!IS_UTF_CHSET(iod->ichset) && !IS_UTF_CHSET(iod->ochset))) iod->width = recordsize; } break; case iop_outrewind: /* for a non-split device, OUTREWIND, INREWIND, and REWIND are equivalent. For a split device * like $PRINCIPAL INREWIND and REWIND operate on the input side and OUTREWIND operates on the * output side. "op_use" calls iorm_use() for both sides for $PRINCIPAL. Since the input * side of $PRINCIPAL is equal to io_std_device.in we break if OUTREWIND is the device parameter. * A similar check is done on the output side if INREWIND or REWIND is the device parameter. */ if ((io_std_device.in == iod)) break; outdevparam = TRUE; case iop_inrewind: case iop_rewind: if ((FALSE == outdevparam) && (io_std_device.out == iod)) break; if (iod->state == dev_open && !rm_ptr->fifo && !rm_ptr->is_pipe) { if ((off_t)-1 == lseek(rm_ptr->fildes, 0, SEEK_SET)) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"), RTS_ERROR_LITERAL("REWIND"), CALLFROM, errno); } /* need to do FSTAT_FILE to get file size if not already done */ FSTAT_CHECK(FALSE); /* if file size is not zero then not at end of file */ if (0 != statbuf.st_size) iod->dollar.zeof = FALSE; else { iod->dollar.zeof = TRUE; if (io_std_device.in == iod) prin_in_dev_failure = FALSE; } iod->dollar.y = 0; iod->dollar.x = 0; rm_ptr->lastop = RM_NOOP; rm_ptr->done_1st_read = rm_ptr->done_1st_write = rm_ptr->crlast = FALSE; rm_ptr->out_bytes = rm_ptr->bom_buf_cnt = rm_ptr->bom_buf_off = 0; rm_ptr->inbuf_top = rm_ptr->inbuf_off = rm_ptr->inbuf_pos = rm_ptr->inbuf; rm_ptr->file_pos = 0; /* Reset temporary buffer so that the next read starts afresh */ if (!rm_ptr->fixed || IS_UTF_CHSET(iod->ichset)) { DEBUG_ONLY(MEMSET_IF_DEFINED(rm_ptr->tmp_buffer, 0, CHUNK_SIZE)); rm_ptr->start_pos = 0; rm_ptr->tot_bytes_in_buffer = 0; } /* If encrypted, release and acquire the cipher handle once again. This way, we reset the encryption * state to read the encrypted data. Any writes henceforth will issue error (NOTTOEOFOUTPUT) unless * a truncate is also done following the rewind or reads take us to the end of the file. */ rm_ptr->read_occurred = FALSE; if (rm_ptr->input_encrypted) reset_input_encryption = TRUE; } break; case iop_stream: if (iod->state != dev_open) { rm_ptr->stream = TRUE; if (CHSET_BINARY == iod->ichset) { ichset_specified = FALSE; iod->ichset = CHSET_M; } if (CHSET_BINARY == iod->ochset) { ochset_specified = FALSE; iod->ochset = CHSET_M; } } break; case iop_truncate: /* truncate if not a fifo and not a pipe and (it is a non-split device or this is the * output side of a split device) */ if (!rm_ptr->fifo && !rm_ptr->is_pipe && ((iod->pair.in == iod->pair.out) || (io_std_device.out == iod))) { int ftruncate_res; int fc_res; FILE *newstr; int newfd; /* don't truncate unless it is a regular file */ FSTAT_CHECK(TRUE); if (!(S_IFREG & mode)) break; /* If encrypted, we cannot allow a truncate if the file pointer is not positioned at the beginning * or the end of the file, unless there have been no writes. */ if ((0 != rm_ptr->file_pos) && (!iod->dollar.zeof) && rm_ptr->output_encrypted && rm_ptr->write_occurred) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_CRYPTNOTRUNC, 2, dev_name->len, dev_name->dollar_io); } /* If already open, truncate, close, and reopen the device. */ if (dev_open == iod->state) { /* if last operation was a write then set file_pos to position after write */ if (RM_WRITE == rm_ptr->lastop) { /* need to do an lseek to get current location in file */ cur_position = lseek(rm_ptr->fildes, 0, SEEK_CUR); if ((off_t)-1 == cur_position) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"), RTS_ERROR_LITERAL("TRUNCATE"), CALLFROM, errno); } else rm_ptr->file_pos = cur_position; } FTRUNCATE(rm_ptr->fildes, rm_ptr->file_pos, ftruncate_res); if (0 != ftruncate_res) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("ftruncate"), RTS_ERROR_LITERAL("TRUNCATE"), CALLFROM, errno); } /* the following is only necessary for a non-split device */ if (iod->pair.in == iod->pair.out) { do { newfd = dup(rm_ptr->fildes); } while (-1 == newfd && EINTR == errno); if (-1 == newfd) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("dup"), CALLFROM, save_errno); if (TRUE == rm_ptr->read_only) { FDOPEN(newstr,newfd,"r"); } else if (TRUE == rm_ptr->write_only) { FDOPEN(newstr,newfd,"w"); } else FDOPEN(newstr,newfd,"r+"); CLOSE(rm_ptr->fildes,fc_res); FCLOSE(rm_ptr->filstr,fc_res); rm_ptr->filstr = newstr; rm_ptr->fildes = newfd; } } /* the following it only necessary for a non-split device */ if (iod->pair.in == iod->pair.out) { if ((off_t)-1 == fseeko(rm_ptr->filstr, rm_ptr->file_pos, SEEK_SET)) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("fseeko"), RTS_ERROR_LITERAL("TRUNCATE"), CALLFROM, errno); } } if ((off_t)-1 == lseek(rm_ptr->fildes, rm_ptr->file_pos, SEEK_SET)) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"), RTS_ERROR_LITERAL("TRUNCATE"), CALLFROM, errno); } /* if not open then do the truncate here */ if (dev_open != iod->state) { FTRUNCATE(rm_ptr->fildes, rm_ptr->file_pos, ftruncate_res); if (0 != ftruncate_res) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("ftruncate"), RTS_ERROR_LITERAL("TRUNCATE"), CALLFROM, errno); } } /* If we have truncated to the beginning of the file, we are starting afresh, so even a different * KEY or IV may be specified (hence ...was_encrypted is being set to FALSE). */ if (0 == rm_ptr->file_pos) { rm_ptr->read_occurred = FALSE; rm_ptr->write_occurred = FALSE; if (rm_ptr->input_encrypted) reset_input_encryption = TRUE; if (rm_ptr->output_encrypted) reset_output_encryption = TRUE; } iod->dollar.zeof = TRUE; /* Reset temporary buffer so that the next read can start afresh */ if (!rm_ptr->fixed || IS_UTF_CHSET(iod->ichset)) { rm_ptr->inbuf_top = rm_ptr->inbuf_off = rm_ptr->inbuf_pos = rm_ptr->inbuf; if (RM_WRITE != rm_ptr->lastop && rm_ptr->fixed) rm_ptr->out_bytes = 0; rm_ptr->bom_buf_cnt = rm_ptr->bom_buf_off = 0; DEBUG_ONLY(MEMSET_IF_DEFINED(rm_ptr->tmp_buffer, 0, CHUNK_SIZE)); rm_ptr->start_pos = 0; rm_ptr->tot_bytes_in_buffer = 0; if (RM_WRITE == rm_ptr->lastop && rm_ptr->fixed && iod->dollar.x == iod->width) iod->dollar.x = 0; if (0 == rm_ptr->file_pos) { /* If the truncate size is zero reset done_1st_read and done_1st_write * to FALSE. */ rm_ptr->done_1st_read = rm_ptr->done_1st_write = FALSE; rm_ptr->bom_num_bytes = 0; rm_ptr->bom_checked = 0; } } } break; case iop_uic: { ch = (unsigned char *)pp->str.addr + p_offset; ct = *ch++; end = ch + ct; uic.grp = uic.mem = 0; while ((*ch != ',') && (ch < end)) uic.mem = (10 * uic.mem) + (*ch++ - '0'); if (*ch == ',') { while (++ch < end) uic.grp = (10 * uic.grp) + (*ch - '0'); } CHG_OWNER(iod->trans_name->dollar_io, uic.mem, uic.grp, chown_res); if (-1 == chown_res) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); break; } case iop_width: assert(iod->state == dev_open); GET_LONG(width, (pp->str.addr + p_offset)); if (0 > width || (0 == width && !IS_UTF_CHSET(iod->ochset))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_RMWIDTHPOS); else if (MAX_STRLEN < width) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_RMWIDTHTOOBIG); /* Do not allow a WIDTH of 1 if either ICHSET or OCHSET is UTF-* */ if ((1 == width) && gtm_utf8_mode && ((IS_UTF_CHSET(iod->ochset)) || (IS_UTF_CHSET(iod->ichset)))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_WIDTHTOOSMALL); if (IS_UTF_CHSET(iod->ochset) && rm_ptr->fixed) iorm_cond_wteol(iod); /* Need to insert a newline if $X is non-zero. */ rm_ptr->def_width = FALSE; if (0 == width) { iod->width = rm_ptr->recordsize; /* Effectively eliminates consideration of width */ if (!seen_wrap) iod->wrap = FALSE; } else { iod->width = width; iod->wrap = TRUE; } break; case iop_wrap: if (dev_open != iod->state) { if (CHSET_BINARY == iod->ichset || CHSET_BINARY == iod->ochset) break; /* ignore wrap if BINARY specified */ } else { /* already open so check what conversion is in use */ # ifdef __MVS__ if (CHSET_BINARY == iod->process_chset) break; # endif } iod->wrap = TRUE; seen_wrap = TRUE; /* Don't allow WIDTH=0 to override WRAP */ break; case iop_nowrap: iod->wrap = FALSE; break; case iop_ipchset: { # ifdef KEEP_zOS_EBCDIC if ((iconv_t)0 != iod->input_conv_cd) { ICONV_CLOSE_CD(iod->input_conv_cd); } SET_CODE_SET(iod->in_code_set, (char *)(pp->str.addr + p_offset + 1)); if (DEFAULT_CODE_SET != iod->in_code_set) ICONV_OPEN_CD(iod->input_conv_cd, (char *)(pp->str.addr + p_offset + 1), INSIDE_CH_SET); # endif if (chset_allowed) { GET_ADDR_AND_LEN(chset_mstr.addr, chset_mstr.len); SET_ENCODING(temp_chset, &chset_mstr); if (!gtm_utf8_mode && IS_UTF_CHSET(temp_chset)) break; /* ignore UTF chsets if not utf8_mode */ CHECK_UTF16_VARIANT_AND_SET_CHSET(rm_ptr->ichset_utf16_variant, iod->ichset, temp_chset); ichset_specified = TRUE; } } break; case iop_opchset: { # ifdef KEEP_zOS_EBCDIC if ((iconv_t) 0 != iod->output_conv_cd) { ICONV_CLOSE_CD(iod->output_conv_cd); } SET_CODE_SET(iod->out_code_set, (char *)(pp->str.addr + p_offset + 1)); if (DEFAULT_CODE_SET != iod->out_code_set) ICONV_OPEN_CD(iod->output_conv_cd, INSIDE_CH_SET, (char *)(pp->str.addr + p_offset + 1)); # endif if (chset_allowed) { GET_ADDR_AND_LEN(chset_mstr.addr, chset_mstr.len); SET_ENCODING(temp_chset, &chset_mstr); if (!gtm_utf8_mode && IS_UTF_CHSET(temp_chset)) break; /* ignore UTF chsets if not utf8_mode */ CHECK_UTF16_VARIANT_AND_SET_CHSET(rm_ptr->ochset_utf16_variant, iod->ochset, temp_chset); ochset_specified = TRUE; if (gtm_utf8_mode && !IS_PADCHAR_VALID(iod->ochset, rm_ptr->padchar)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(2) ERR_PADCHARINVALID, 0); } } break; case iop_chset: { if (chset_allowed) { GET_ADDR_AND_LEN(chset_mstr.addr, chset_mstr.len); SET_ENCODING(temp_chset, &chset_mstr); if (!gtm_utf8_mode && IS_UTF_CHSET(temp_chset)) break; /* ignore UTF chsets if not utf8_mode */ CHECK_UTF16_VARIANT_AND_SET_CHSET(rm_ptr->ichset_utf16_variant, iod->ichset, temp_chset); CHECK_UTF16_VARIANT_AND_SET_CHSET(rm_ptr->ochset_utf16_variant, iod->ochset, temp_chset); if (gtm_utf8_mode && !IS_PADCHAR_VALID(iod->ochset, rm_ptr->padchar)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(2) ERR_PADCHARINVALID, 0); ochset_specified = ichset_specified = TRUE; } } break; case iop_m: if (chset_allowed && (dev_open != iod->state)) { iod->ichset = iod->ochset = CHSET_M; ichset_specified = ochset_specified = TRUE; } break; case iop_utf8: case iop_utf16: case iop_utf16be: case iop_utf16le: if (gtm_utf8_mode && dev_open != iod->state) { iod->ichset = iod->ochset = (iop_utf8 == c) ? CHSET_UTF8 : (iop_utf16 == c) ? CHSET_UTF16 : (iop_utf16be == c) ? CHSET_UTF16BE : CHSET_UTF16LE; ichset_specified = ochset_specified = TRUE; } break; case iop_follow: if (!rm_ptr->fifo && !rm_ptr->is_pipe) rm_ptr->follow = TRUE; break; case iop_nofollow: if (!rm_ptr->fifo && !rm_ptr->is_pipe) rm_ptr->follow = FALSE; break; case iop_outseek: /* for a non-split device, OUTSEEK, INSEEK, and SEEK are equivalent. For a split device * like $PRINCIPAL INSEEK and SEEK operate on the input side and OUTSEEK operates on the * output side. "op_use" calls iorm_use() for both sides for $PRINCIPAL. Since the input * side of $PRINCIPAL is equal to io_std_device.in we break if OUTSEEK is the device parameter. * A similar check is done on the output side if INSEEK or SEEK is the device parameter. */ if ((io_std_device.in == iod)) /* CAUTION: potential fall-through */ break; outdevparam = TRUE; case iop_inseek: case iop_seek: if ((FALSE == outdevparam) && (io_std_device.out == iod)) break; seek_specified = TRUE; if (!rm_ptr->fifo && !rm_ptr->is_pipe) { /* need to do FSTAT_FILE to get file size if not already done */ FSTAT_CHECK(FALSE); /* if file size is not zero then process seek request */ if (0 != statbuf.st_size) { /* if last operation was a write then set file_pos to position after write */ if (RM_WRITE == rm_ptr->lastop) { /* need to do an lseek to get current location in file */ cur_position = lseek(rm_ptr->fildes, 0, SEEK_CUR); if ((off_t)-1 == cur_position) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"), RTS_ERROR_LITERAL("SEEK"), CALLFROM, errno); } else rm_ptr->file_pos = cur_position; } seek_len = MIN((unsigned char)*(pp->str.addr + p_offset), (LIMIT_SEEK_STR - 1)); /* if seek_len not greater than zero then it's an error so quit now */ if (0 >= seek_len) OUTPUT_SDSEEKERR("\"\""); /* If BOM not already checked and not a fifo or pipe, not a reopen of file, is UTF mode, * file is not empty, and not writeonly, and encryption is not in effect, then go to the * beginning of the file and read the potential BOM. */ if ((!rm_ptr->bom_checked) && (!rm_ptr->no_destroy) && (!rm_ptr->fifo) && (!rm_ptr->is_pipe) && (!rm_ptr->input_encrypted) && (2 < rm_ptr->fildes) && (0 < statbuf.st_size) && IS_UTF_CHSET(iod->ichset) && !rm_ptr->write_only) { assert(UTF16BE_BOM_LEN < UTF8_BOM_LEN); if ((CHSET_UTF8 == iod->ichset) && (statbuf.st_size >= UTF8_BOM_LEN)) bom_size_toread = UTF8_BOM_LEN; else if (IS_UTF16_CHSET(iod->ichset) && (statbuf.st_size >= UTF16BE_BOM_LEN)) bom_size_toread = UTF16BE_BOM_LEN; else bom_size_toread = 0; if (0 < bom_size_toread) { if ((off_t)-1 == lseek(rm_ptr->fildes, 0, SEEK_SET)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"), RTS_ERROR_LITERAL("SEEK"), CALLFROM, errno); rm_ptr->bom_num_bytes = open_get_bom(iod, bom_size_toread); /* move back to previous file position */ if ((off_t)-1 == lseek(rm_ptr->fildes, rm_ptr->file_pos, SEEK_SET)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"), RTS_ERROR_LITERAL("SEEK"), CALLFROM, errno); } rm_ptr->bom_checked = TRUE; } memcpy(seek_str, (char *)(pp->str.addr + p_offset + 1), seek_len); seek_str[seek_len] = '\0'; seek_ptr = seek_str; if ('+' == *seek_str) { seek_type = SEEK_PLUS; seek_ptr++; /* skip sign */ } else if ('-' == *seek_str) { seek_type = SEEK_MINUS; seek_ptr++; /* skip sign */ } else { seek_type = SEEK_ABS; } /* require a digit as the next character to make sure 2 signs not entered */ if (!ISDIGIT_ASCII(*seek_ptr)) OUTPUT_SDSEEKERR(seek_str); errno = 0; /* not reset if previous failure */ seek_value = STRTOLL(seek_ptr, &endptr, 10); if (ERANGE == errno) OUTPUT_SDSEEKERR(seek_str); if ('\0' != *endptr) OUTPUT_SDSEEKERR(seek_str); if (SEEK_MINUS == seek_type) seek_value = -seek_value; /* if fixed then convert record offset to byte offset in file */ if (rm_ptr->fixed) { if (IS_UTF_CHSET(iod->ichset)) { /* utf reads recordsize so if we only processed part of the record * adjust it back to the beginning of the record. */ if (rm_ptr->inbuf_top - rm_ptr->inbuf_off) { current_offset = rm_ptr->file_pos - rm_ptr->recordsize; } else current_offset = rm_ptr->file_pos; if (!rm_ptr->bom_num_bytes) seek_value *= rm_ptr->recordsize; /* no bom to deal with */ else { /* account for bom if absolute seek or relative positive seek * and we are at the beginning of the file */ if ((seek_type == SEEK_ABS) || ((0 == current_offset) && (0 < seek_value))) seek_value = seek_value * rm_ptr->recordsize + rm_ptr->bom_num_bytes; else { if (0 == current_offset) { /* zero or less seek from beginning of the file */ seek_value = -1; /* new_position will be 0 below */ } else seek_value *= rm_ptr->recordsize; } } } else { current_offset = (rm_ptr->file_pos / iod->width) * iod->width; /* Need to adjust current_offset so we land on a record boundary. * If the current_offset is in the middle of the record then adjust * it so that a relative +0 lands at the beginning of the record */ seek_value *= iod->width; } } else current_offset = rm_ptr->file_pos; if (seek_type == SEEK_ABS) new_position = seek_value; else new_position = current_offset + seek_value; /* limit the range of the new calculated position from zero to the file size */ if (0 > new_position) new_position = 0; else if (statbuf.st_size < new_position) new_position = statbuf.st_size; if (-1 == lseek(rm_ptr->fildes, new_position, SEEK_SET)) { save_errno = errno; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"), RTS_ERROR_LITERAL("SEEK"), CALLFROM, save_errno); } if (statbuf.st_size == new_position) /* at end of file */ iod->dollar.zeof = TRUE; else { iod->dollar.zeof = FALSE; if (0 == new_position) /* at beginning of file */ rm_ptr->done_1st_read = rm_ptr->done_1st_write = rm_ptr->crlast = FALSE; } iod->dollar.y = 0; iod->dollar.x = 0; rm_ptr->lastop = RM_NOOP; rm_ptr->file_pos = new_position; /* Reset temporary buffer so that the next read starts afresh */ if (!rm_ptr->fixed || IS_UTF_CHSET(iod->ichset)) { /* if not at beginning of file then don't read BOM */ rm_ptr->out_bytes = rm_ptr->bom_buf_cnt = rm_ptr->bom_buf_off = 0; rm_ptr->inbuf_top = rm_ptr->inbuf_off = rm_ptr->inbuf_pos = rm_ptr->inbuf; DEBUG_ONLY(MEMSET_IF_DEFINED(rm_ptr->tmp_buffer, 0, CHUNK_SIZE)); rm_ptr->start_pos = 0; rm_ptr->tot_bytes_in_buffer = 0; } } } break; case iop_key: /* CAUTION: fall-through */ case iop_input_key: GET_KEY_AND_IV(input); if (iop_key != c) /* CAUTION: potential fall-through */ break; case iop_output_key: GET_KEY_AND_IV(output); break; case iop_buffered: disk_block_multiple = (int)*((unsigned char *)(pp->str.addr + p_offset + 1)); /* Not enabled for stdout (initially) */ if (1 == rm_ptr->fildes) break; assert(rm_ptr->filstr); /* No change, do nothing */ if (rm_ptr->fsblock_buffer_size == disk_block_multiple) break; /* Simple buffering request - handles both enable and disable */ if (2 > disk_block_multiple) { rm_ptr->fsblock_buffer_size = disk_block_multiple; if (NULL != rm_ptr->fsblock_buffer) free(rm_ptr->fsblock_buffer); break; } /* Request to buffer with buffer size different from before; clear out existing buffer */ if (NULL != rm_ptr->fsblock_buffer) free(rm_ptr->fsblock_buffer); /* Grab the FS block size */ fwrite_buffer_size = (size_t)get_fs_block_size(rm_ptr->fildes); fwrite_buffer_size = (size_t)(fwrite_buffer_size << disk_block_multiple); rm_ptr->fsblock_buffer = (char *)malloc(fwrite_buffer_size); if (setvbuf(rm_ptr->filstr, rm_ptr->fsblock_buffer, _IOFBF, fwrite_buffer_size)) { /* Non-fatal error. Continue to use buffered IO */ free(rm_ptr->fsblock_buffer); rm_ptr->fsblock_buffer_size = 1; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("setvbuff"), CALLFROM, errno); break; } rm_ptr->fsblock_buffer_size = fwrite_buffer_size; break; case iop_fflf: /* Turn off GTM-9136 NL suppression regardless of env gtm_fflf setting */ iod->fflf = TRUE; break; case iop_nofflf: /* Turn on GTM-9136 NL suppression regardless of env gtm_fflf setting */ iod->fflf = FALSE; break; default: break; } p_offset += ((IOP_VAR_SIZE == io_params_size[c]) ? (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[c]); } if (dev_open != iod->state || ichset_specified || ochset_specified) { if (dev_open != iod->state && !ichset_specified && !rm_ptr->no_destroy) { # ifdef __MVS__ iod->is_ichset_default = TRUE; # endif iod->ichset = (gtm_utf8_mode) ? CHSET_UTF8 : CHSET_M; } if (dev_open != iod->state && !ochset_specified && !rm_ptr->no_destroy) { # ifdef __MVS__ iod->is_ochset_default = TRUE; # endif iod->ochset = (gtm_utf8_mode) ? CHSET_UTF8 : CHSET_M; } if ((CHSET_M != iod->ichset) && (CHSET_UTF16 != iod->ichset) && (CHSET_MAX_IDX > iod->ichset)) get_chset_desc(&chset_names[iod->ichset]); if ((CHSET_M != iod->ochset) && (CHSET_UTF16 != iod->ochset) && (CHSET_MAX_IDX > iod->ochset)) get_chset_desc(&chset_names[iod->ochset]); /* If ICHSET or OCHSET is of type UTF-16, check that RECORDSIZE is even. */ if (gtm_utf8_mode && (IS_UTF16_CHSET(iod->ichset) || IS_UTF16_CHSET(iod->ochset))) { if (rm_ptr->def_recsize) { /* DEF_RM_RECORDSIZE is currently an odd number (32K-1). Round it down * to be a multiple of 4 bytes since a UTF-16 char can be 2 or 4 bytes */ assert(DEF_RM_RECORDSIZE == 32767); rm_ptr->recordsize = ROUND_DOWN2(rm_ptr->recordsize, 4); } else if (0 != rm_ptr->recordsize % 2) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_RECSIZENOTEVEN, 1, rm_ptr->recordsize); } } /* Now that recordsize and CHSET parms have been handled (if any), set WIDTH if necessary */ if (rm_ptr->def_width && (recsize_before != rm_ptr->recordsize || def_recsize_before != rm_ptr->def_recsize)) { /* record size was specified even if the same value */ SET_WIDTH_BYTES; assert(width_bytes <= rm_ptr->recordsize); /* or else RECSIZENOTEVEN error would have been issued */ iod->width = rm_ptr->recordsize; /* this width will hold at least one character due to above check */ } if (dev_closed == iod->state) { if (IS_UTF_CHSET(iod->ichset)) { assert(gtm_utf8_mode); /* We shouldn't have an existing buffer at this point if not closed nodestroy */ if (!rm_ptr->no_destroy) assert(NULL == rm_ptr->inbuf); } if (IS_UTF16_CHSET(iod->ochset)) { /* We shouldn't have an existing buffer at this point if not closed nodestroy*/ if (!rm_ptr->no_destroy) assert(NULL == rm_ptr->outbuf); } } /* Now adjust the buffer based on new CHSET & recordsize, If the file was already opened */ if (dev_open == iod->state) { if ((!rm_ptr->fixed || IS_UTF_CHSET(iod->ichset)) && (recsize_before != rm_ptr->recordsize)) { if (NULL != rm_ptr->inbuf) { free(rm_ptr->inbuf); rm_ptr->inbuf = NULL; } } if (IS_UTF16_CHSET(iod->ochset) && (recsize_before != rm_ptr->recordsize)) { if (NULL != rm_ptr->outbuf) { free(rm_ptr->outbuf); rm_ptr->outbuf=NULL; } } } /* Allocate the buffers in case it is UTF mode and not already allocated. */ if ((NULL == rm_ptr->inbuf) && IS_UTF_CHSET(iod->ichset)) { rm_ptr->bufsize = rm_ptr->fixed ? (rm_ptr->recordsize + 4) : 20; rm_ptr->inbuf = malloc(rm_ptr->bufsize); rm_ptr->inbuf_pos = rm_ptr->inbuf_top = rm_ptr->inbuf_off = rm_ptr->inbuf; } if ((NULL == rm_ptr->tmp_buffer) && (IS_UTF_CHSET(iod->ichset) || !rm_ptr->fixed)) rm_ptr->tmp_buffer = malloc(CHUNK_SIZE); if ((NULL == rm_ptr->outbuf) && IS_UTF16_CHSET(iod->ochset)) { rm_ptr->outbufsize = rm_ptr->recordsize + 4; rm_ptr->outbuf = malloc(rm_ptr->outbufsize); rm_ptr->out_bytes = 0; } assert(((!rm_ptr->input_encrypted) && (!rm_ptr->output_encrypted)) || (dev_open == iod->state) || rm_ptr->no_destroy); assert((!rm_ptr->input_encrypted) || (0 != rm_ptr->input_key.len)); assert((!rm_ptr->output_encrypted) || (0 != rm_ptr->output_key.len)); if (rm_ptr->input_encrypted) { /* Device's input already encrypted. */ if (input_key_entry_present) { /* Input KEY deviceparameter is present. */ if (input_key_not_empty) { /* Input KEY is meaningful. */ if (KEY_CHANGED(input, input_key, rm_ptr) || IV_CHANGED(input, input_iv, rm_ptr)) { /* Requested a new IV or KEY; only allow if no reads have happened. */ if (rm_ptr->read_occurred) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CRYPTNOOVERRIDE); else reset_input_encryption = init_input_encryption = TRUE; } } else { /* Encryption requested to be turned off; only allow if there have not been any reads. Also, * a non-empty IV cannot be specified at this time. */ if (rm_ptr->read_occurred) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CRYPTNOOVERRIDE); else if (0 != input_iv.len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CRYPTNOKEYSPEC); else { rm_ptr->input_encrypted = FALSE; reset_input_encryption = init_input_encryption = FALSE; if (GTMCRYPT_INVALID_KEY_HANDLE != rm_ptr->input_cipher_handle) { GTMCRYPT_REMOVE_CIPHER_CONTEXT(rm_ptr->input_cipher_handle, rv); if (0 != rv) GTMCRYPT_REPORT_ERROR(rv, rts_error, dev_name->len, dev_name->dollar_io); } } } } } else { /* Device's input is not yet encrypted. */ if (input_key_entry_present) { /* Only initialize encryption if the input key is not empty. */ if (input_key_not_empty) { /* Initialize encryption unless some unencrypted reads have occurred. */ if (rm_ptr->read_occurred) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CRYPTNOOVERRIDE); else init_input_encryption = TRUE; } else if (0 != input_iv.len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CRYPTNOKEYSPEC); } } if (rm_ptr->output_encrypted) { /* Device's output already encrypted. */ if (output_key_entry_present) { /* Input KEY deviceparameter is present. */ if (output_key_not_empty) { /* Output KEY is meaningful. */ if (KEY_CHANGED(output, output_key, rm_ptr) || IV_CHANGED(output, output_iv, rm_ptr)) { /* Requested a new IV or KEY; only allow if no writes have happened. */ if (rm_ptr->write_occurred) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CRYPTNOOVERRIDE); else reset_output_encryption = init_output_encryption = TRUE; } } else { /* Encryption requested to be turned off; only allow if there have not been any writes. Also, * a non-empty IV cannot be specified at this time. */ if (rm_ptr->write_occurred) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CRYPTNOOVERRIDE); else if (0 != output_iv.len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CRYPTNOKEYSPEC); else { rm_ptr->output_encrypted = FALSE; reset_output_encryption = init_output_encryption = FALSE; if (GTMCRYPT_INVALID_KEY_HANDLE != rm_ptr->output_cipher_handle) { GTMCRYPT_REMOVE_CIPHER_CONTEXT(rm_ptr->output_cipher_handle, rv); if (0 != rv) GTMCRYPT_REPORT_ERROR(rv, rts_error, dev_name->len, dev_name->dollar_io); } } } } } else { /* Device's output is not yet encrypted. */ if (output_key_entry_present) { /* Only initialize encryption if the output key is not empty. */ if (output_key_not_empty) { /* Initialize encryption unless some unencrypted writes have occurred. */ if (rm_ptr->write_occurred) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CRYPTNOOVERRIDE); else init_output_encryption = TRUE; } else if (0 != output_iv.len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CRYPTNOKEYSPEC); } } assert((!reset_input_encryption) || rm_ptr->input_encrypted); assert((!reset_output_encryption) || rm_ptr->output_encrypted); if ((rm_ptr->input_encrypted || init_input_encryption || reset_input_encryption || rm_ptr->output_encrypted || init_output_encryption || reset_output_encryption) && seek_specified) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_CRYPTNOSEEK, 2, dev_name->len, dev_name->dollar_io); if (init_input_encryption || init_output_encryption) { /* First time the device is getting encryption turned on. Initialize encryption and setup the keys. */ INIT_PROC_ENCRYPTION(rv); if (0 != rv) GTMCRYPT_REPORT_ERROR(rv, rts_error, dev_name->len, dev_name->dollar_io); } if (reset_input_encryption && (GTMCRYPT_INVALID_KEY_HANDLE != rm_ptr->input_cipher_handle)) { GTMCRYPT_REMOVE_CIPHER_CONTEXT(rm_ptr->input_cipher_handle, rv); if (0 != rv) GTMCRYPT_REPORT_ERROR(rv, rts_error, dev_name->len, dev_name->dollar_io); } if (init_input_encryption) { /* Get the key handle corresponding to the keyname provided. */ INIT_CIPHER_CONTEXT(GTMCRYPT_OP_DECRYPT, input_key, input_iv, rm_ptr->input_cipher_handle, dev_name); rm_ptr->input_encrypted = TRUE; rm_ptr->input_key.addr = input_key.addr; rm_ptr->input_key.len = input_key.len; rm_ptr->input_iv.addr = input_iv.addr; rm_ptr->input_iv.len = input_iv.len; s2pool(&rm_ptr->input_key); s2pool(&rm_ptr->input_iv); } else if (reset_input_encryption) { INIT_CIPHER_CONTEXT(GTMCRYPT_OP_DECRYPT, rm_ptr->input_key, rm_ptr->input_iv, rm_ptr->input_cipher_handle, dev_name); } if (reset_output_encryption && (GTMCRYPT_INVALID_KEY_HANDLE != rm_ptr->output_cipher_handle)) { GTMCRYPT_REMOVE_CIPHER_CONTEXT(rm_ptr->output_cipher_handle, rv); if (0 != rv) GTMCRYPT_REPORT_ERROR(rv, rts_error, dev_name->len, dev_name->dollar_io); } if (init_output_encryption) { /* Get the key handle corresponding to the keyname provided. */ INIT_CIPHER_CONTEXT(GTMCRYPT_OP_ENCRYPT, output_key, output_iv, rm_ptr->output_cipher_handle, dev_name); rm_ptr->output_encrypted = TRUE; rm_ptr->output_key.addr = output_key.addr; rm_ptr->output_key.len = output_key.len; rm_ptr->output_iv.addr = output_iv.addr; rm_ptr->output_iv.len = output_iv.len; s2pool(&rm_ptr->output_key); s2pool(&rm_ptr->output_iv); } else if (reset_output_encryption) { INIT_CIPHER_CONTEXT(GTMCRYPT_OP_ENCRYPT, rm_ptr->output_key, rm_ptr->output_iv, rm_ptr->output_cipher_handle, dev_name); } if (init_input_encryption || reset_input_encryption || init_output_encryption || reset_output_encryption) { /* Setup an encryption private buffer of size equal to record size. */ REALLOC_CRYPTBUF_IF_NEEDED(rm_ptr->recordsize); } if (get_mode_done && (mode != mode1)) { /* if the mode has been changed by the qualifiers, reset it */ if (-1 == CHMOD(iod->trans_name->dollar_io, mode)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); } REVERT_GTMIO_CH(&iod->pair, ch_set); return; } fis-gtm-V7.0-005/sr_unix/iorm_write.c0000644000032200000250000004341114342376330016370 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "io.h" #include "iormdef.h" #include "iormdefsp.h" #include "gtmio.h" #include "min_max.h" #include "gtmcrypt.h" #include "send_msg.h" #include "error.h" #include "svnames.h" #include "op.h" #include "util.h" #ifdef UTF8_SUPPORTED #include "gtm_conv.h" #include "gtm_utf8.h" #endif GBLREF boolean_t prin_in_dev_failure, prin_out_dev_failure; GBLREF io_pair io_curr_device, io_std_device; #ifdef UTF8_SUPPORTED LITREF mstr chset_names[]; #endif GBLREF mval dollar_zstatus; error_def(ERR_CRYPTBADWRTPOS); error_def(ERR_DEVICEREADONLY); error_def(ERR_IOERROR); error_def(ERR_NOPRINCIO); error_def(ERR_NOTTOEOFONPUT); error_def(ERR_SYSCALL); /* write ASCII characters converting to UTF16 if needed * returns bytes written */ int iorm_write_utf_ascii(io_desc *iod, char *string, int len) { int outlen, mblen, status; wint_t utf_code; unsigned char *outstart, *out, *top, *outptr, *nextoutptr, *outptrtop, *nextmb; d_rm_struct *rm_ptr; boolean_t ch_set; ESTABLISH_RET_GTMIO_CH(&iod->pair, -1, ch_set); rm_ptr = (d_rm_struct *)iod->dev_sp; assert(NULL != rm_ptr); if (CHSET_UTF8 != iod->ochset) { outstart = outptr = &rm_ptr->outbuf[rm_ptr->out_bytes]; outptrtop = rm_ptr->outbuf + rm_ptr->recordsize; /* buffer is larger than recordsize to allow for EOL */ assert(len <= (&rm_ptr->outbuf[rm_ptr->outbufsize] - outstart)); for (out = (unsigned char*)string, top = out + len, outlen = 0; out < top && outptr <= outptrtop; out = nextmb, outptr = nextoutptr) { nextmb = UTF8_MBTOWC(out, top, utf_code); assert(nextmb == (out + 1)); if (WEOF == utf_code) { iod->dollar.za = ZA_IO_ERR; UTF8_BADCHAR((int)(nextmb - out), out, top, 0, NULL); } if (CHSET_UTF16BE == iod->ochset) nextoutptr = UTF16BE_WCTOMB(utf_code, outptr); else nextoutptr = UTF16LE_WCTOMB(utf_code, outptr); if (nextoutptr == outptr) { /* invalid codepoint */ iod->dollar.za = ZA_IO_ERR; UTF8_BADCHAR((int)(nextmb - out), out, top, chset_names[iod->ochset].len, chset_names[iod->ochset].addr); } mblen = (int)(nextoutptr - outptr); outlen += mblen; } } else { outstart = (unsigned char *)string; outlen = len; } if (0 < outlen) { if (rm_ptr->output_encrypted) { REALLOC_CRYPTBUF_IF_NEEDED(outlen); WRITE_ENCRYPTED_DATA(rm_ptr, iod->trans_name, outstart, outlen, pvt_crypt_buf.addr); outptr = (unsigned char *)pvt_crypt_buf.addr; } else outptr = outstart; DOWRITERC_RM(rm_ptr, outptr, outlen, status); ISSUE_NOPRINCIO_IF_NEEDED_RM(status, ==, iod); rm_ptr->write_occurred = TRUE; rm_ptr->out_bytes += outlen; } REVERT_GTMIO_CH(&iod->pair, ch_set); return outlen; } void iorm_write_utf(mstr *v) { int4 inchars, char_count; /* in characters */ int4 inlen, outbytes, mblen; /* in bytes */ int4 availwidth, usedwidth, mbwidth; /* in display columns */ int status, padsize, fstat_res, save_errno; wint_t utf_code; io_desc *iod; d_rm_struct *rm_ptr; unsigned char *inptr, *top, *nextmb, *outptr, *nextoutptr, *outstart, temppad, temppadarray[2]; char *out_ptr; boolean_t utf8_active = TRUE; /* needed by GTM_IO_WCWIDTH macro */ boolean_t stream, wrap; struct stat statbuf; boolean_t ch_set; iod = io_curr_device.out; ESTABLISH_GTMIO_CH(&io_curr_device, ch_set); rm_ptr = (d_rm_struct *)iod->dev_sp; assert(NULL != rm_ptr); inptr = (unsigned char *)v->addr; inlen = v->len; top = inptr + inlen; if (!rm_ptr->fixed && 0 == iod->dollar.x) rm_ptr->out_bytes = 0; /* user reset $X */ inchars = UTF8_LEN_STRICT(v->addr, v->len); /* validate and get good char count */ if (0 >= inchars) { REVERT_GTMIO_CH(&io_curr_device, ch_set); return; } usedwidth = 0; stream = rm_ptr->stream; wrap = iod->wrap; if (stream && !wrap) { /* For STREAM and NOWRAP, allow the entire record to be written without any record truncations/terminations */ availwidth = inlen; /* calculate worst case requirement of width (in chars) to write out input bytes */ rm_ptr->out_bytes = 0; } else availwidth = iod->width - iod->dollar.x; outbytes = 0; if (CHSET_UTF8 != iod->ochset) { outstart = nextoutptr = outptr = &rm_ptr->outbuf[rm_ptr->out_bytes]; /* In case the CHSET changes from non-UTF-16 to UTF-16 and a read has already been done, * there's no way to read the BOM bytes & to determine the variant. So default to UTF-16BE. */ if (rm_ptr->done_1st_write && ((CHSET_UTF16 == iod->ochset) && !IS_UTF16_CHSET(rm_ptr->ochset_utf16_variant))) { iod->ochset = rm_ptr->ochset_utf16_variant = CHSET_UTF16BE; } if (!rm_ptr->done_1st_write) { /* get the file size */ FSTAT_FILE(rm_ptr->fildes, &statbuf, fstat_res); if (-1 == fstat_res) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fstat"), CALLFROM, save_errno); } if (CHSET_UTF16 == iod->ochset) { /* Write BOM but do not count it towards the bytes in the current record */ /* write BOM if file is empty */ if (0 == statbuf.st_size) { memcpy(outptr, UTF16BE_BOM, UTF16BE_BOM_LEN); outbytes = UTF16BE_BOM_LEN; if (rm_ptr->output_encrypted) { REALLOC_CRYPTBUF_IF_NEEDED(outbytes); WRITE_ENCRYPTED_DATA(rm_ptr, iod->trans_name, outstart, outbytes, pvt_crypt_buf.addr); out_ptr = pvt_crypt_buf.addr; } else out_ptr = (char *)outstart; DOWRITERC_RM(rm_ptr, out_ptr, outbytes, status); ISSUE_NOPRINCIO_IF_NEEDED_RM(status, ==, iod); rm_ptr->write_occurred = TRUE; outptr = outstart; rm_ptr->out_bytes = outbytes = 0; /* save UTF16BE_BOM_LEN in bom_num_bytes until bom is checked, but don't indicate that bom has been checked - which still needs to be done for reading the exception is if the file was opened WRITEONLY */ rm_ptr->bom_num_bytes = UTF16BE_BOM_LEN; if (rm_ptr->write_only) rm_ptr->bom_checked = TRUE; } iod->ochset = CHSET_UTF16BE; get_chset_desc(&chset_names[iod->ochset]); } if (!IS_UTF16_CHSET(rm_ptr->ochset_utf16_variant) && (IS_UTF16_CHSET(iod->ochset) && (CHSET_UTF16 != iod->ochset))) rm_ptr->ochset_utf16_variant = iod->ochset; } } else outstart = (unsigned char *)v->addr; /* write from the UTF-8 string */ if (!rm_ptr->done_1st_write) rm_ptr->done_1st_write = TRUE; for (char_count = mblen = 0; inptr <= top ; ) { if (inptr < top) { /* get "mblen" and "mbwidth" of of next character in buffer */ nextmb = UTF8_MBTOWC(inptr, top, utf_code); if (WEOF == utf_code) { iod->dollar.za = ZA_IO_ERR; UTF8_BADCHAR(0, inptr, top, 0, NULL); } if (CHSET_UTF8 != iod->ochset) { if (CHSET_UTF16BE == iod->ochset) nextoutptr = UTF16BE_WCTOMB(utf_code, outptr); else nextoutptr = UTF16LE_WCTOMB(utf_code, outptr); if (nextoutptr == outptr) { /* invalid codepoint */ iod->dollar.za = ZA_IO_ERR; UTF8_BADCHAR((int)((nextmb - inptr)), inptr, top, chset_names[iod->ochset].len, chset_names[iod->ochset].addr); } mblen = (int)(nextoutptr - outptr); outptr = nextoutptr; } else mblen = (int)(nextmb - inptr); GTM_IO_WCWIDTH(utf_code, mbwidth); assert(mblen); } assert(rm_ptr->out_bytes <= rm_ptr->recordsize); /* Note that "mblen" and "mbwidth" are valid only if "inptr < top". * This is why they are used after the "inptr >= top" check below */ if ((inptr >= top) || ((usedwidth + mbwidth) > availwidth) || ((rm_ptr->out_bytes + mblen) > rm_ptr->recordsize)) { /* filled to WIDTH or end of input or full record */ if (0 < outbytes) { if (rm_ptr->output_encrypted) { REALLOC_CRYPTBUF_IF_NEEDED(outbytes); WRITE_ENCRYPTED_DATA(rm_ptr, iod->trans_name, outstart, outbytes, pvt_crypt_buf.addr); out_ptr = pvt_crypt_buf.addr; } else out_ptr = (char *)outstart; if (rm_ptr->fifo || rm_ptr->is_pipe) { WRITEPIPE(rm_ptr->fildes, rm_ptr->pipe_buff_size, out_ptr, outbytes, status); } else { DOWRITERC_RM(rm_ptr, out_ptr, outbytes, status); } ISSUE_NOPRINCIO_IF_NEEDED_RM(status, ==, iod); rm_ptr->write_occurred = TRUE; } iod->dollar.x += usedwidth; if (inptr >= top) break; /* end of input */ if (char_count >= inchars) break; /* end of adjusted input characters */ if (!stream || wrap) { /* implicit record termination for non-stream files or stream files with the "wrap" option. */ if (!wrap) /* non-stream device wants NOWRAP, so break right away without writing any more */ break; if (!rm_ptr->fixed && wrap) iorm_write_utf_ascii(iod, RMEOL, RMEOL_LEN); else if (rm_ptr->fixed && rm_ptr->out_bytes < rm_ptr->recordsize) { /* padding bytes needed */ temppad = rm_ptr->padchar; if (CHSET_UTF16LE == iod->ochset) { temppadarray[0] = temppad; temppadarray[1] = '\0'; padsize = 2; } else if (CHSET_UTF16BE == iod->ochset) { temppadarray[0] = '\0'; temppadarray[1] = temppad; padsize = 2; } else { assert(CHSET_UTF8 == iod->ochset); temppadarray[0] = temppad; padsize = 1; } for ( ; rm_ptr->out_bytes < rm_ptr->recordsize; rm_ptr->out_bytes += padsize) { if (rm_ptr->output_encrypted) { REALLOC_CRYPTBUF_IF_NEEDED(padsize); WRITE_ENCRYPTED_DATA(rm_ptr, iod->trans_name, temppadarray, padsize, pvt_crypt_buf.addr); out_ptr = pvt_crypt_buf.addr; } else out_ptr = (char *)temppadarray; DOWRITERC_RM(rm_ptr, out_ptr, padsize, status); ISSUE_NOPRINCIO_IF_NEEDED_RM(status, ==, iod); rm_ptr->write_occurred = TRUE; } assert(rm_ptr->out_bytes == rm_ptr->recordsize); } iod->dollar.x = 0; /* don't use wteol to terminate wrapped records for fixed. */ iod->dollar.y++; /* \n is reserved as an end-of-rec delimiter for variable format */ if (iod->length) /* and fixed format requires no padding for wrapped records */ iod->dollar.y %= iod->length; availwidth = iod->width; } /* else STREAM specified with NOWRAP. * We can continue to write even if device width is exceeded since NOWRAP has been specified. */ rm_ptr->out_bytes = 0; /* For UTF16, since input and output streams ("inptr" and "outstart") point to different buffers, * the last parsed UTF16 character in "inptr" has not yet been written to "outstart" so * redo the parsing by continuing to the beginning of the loop without incrementing "inptr". * For UTF8, both are the same so we can increment "inptr" without any issues and avoid reparsing. */ if (CHSET_UTF8 == iod->ochset) { outstart = outstart + outbytes; /* advance within input string */ outbytes = usedwidth = 0; } else { outstart = nextoutptr = outptr = &rm_ptr->outbuf[rm_ptr->out_bytes]; outbytes = usedwidth = 0; continue; } } assert(mblen); assert(mblen <= rm_ptr->recordsize); /* there is room in RECORDSIZE to write at least one character */ rm_ptr->out_bytes += mblen; outbytes += mblen; inptr = nextmb; /* next input byte */ assert(inptr <= top); char_count++; usedwidth += mbwidth; assert(usedwidth <= availwidth); /* there is room in display WIDTH to write at least one character */ } iod->dollar.za = 0; REVERT_GTMIO_CH(&io_curr_device, ch_set); return; } void iorm_write(mstr *v) { io_desc *iod; char *out, *out_ptr; int inlen, outlen, status, len; d_rm_struct *rm_ptr; int flags; int fcntl_res; boolean_t stream, wrap; struct stat statbuf; int fstat_res, save_errno; boolean_t ch_set; unsigned int save_dollarx; iod = io_curr_device.out; ESTABLISH_GTMIO_CH(&io_curr_device, ch_set); #ifdef __MVS__ if (NULL == iod->dev_sp) rm_ptr = (d_rm_struct *)(iod->pair.in)->dev_sp; else rm_ptr = (d_rm_struct *)(iod->pair.out)->dev_sp; #else rm_ptr = (d_rm_struct *)iod->dev_sp; #endif memcpy(iod->dollar.device, "0", SIZEOF("0")); if (rm_ptr->read_only) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DEVICEREADONLY); if ((!rm_ptr->fifo) && (!rm_ptr->is_pipe) && rm_ptr->output_encrypted) { if (!iod->dollar.zeof) { iod->dollar.za = ZA_IO_ERR; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_NOTTOEOFONPUT); } else { /* If there have not been any writes, and input encryption attributes are different from those for output, * and the file is not empty, disallow the write. */ if ((!rm_ptr->write_occurred) && ((!rm_ptr->input_encrypted) || (rm_ptr->input_iv.len != rm_ptr->output_iv.len) || memcmp(rm_ptr->input_iv.addr, rm_ptr->output_iv.addr, rm_ptr->input_iv.len) || (((rm_ptr->input_key.len != rm_ptr->output_key.len) || memcmp(rm_ptr->input_key.addr, rm_ptr->output_key.addr, rm_ptr->input_key.len)) && (!GTMCRYPT_SAME_KEY(rm_ptr->input_cipher_handle, rm_ptr->output_cipher_handle))))) { FSTAT_FILE(rm_ptr->fildes, &statbuf, fstat_res); if (-1 == fstat_res) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fstat"), CALLFROM, save_errno); } if (0 != statbuf.st_size) { SET_DOLLARDEVICE_ERRSTR(iod, ONE_COMMA_CRYPTBADWRTPOS); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CRYPTBADWRTPOS); } } } } /* if it's a fifo and not system output/error, last operation was not a write and O_NONBLOCK is not set then set it. A read will turn it off */ if (rm_ptr->fifo && (2 < rm_ptr->fildes) && (RM_WRITE != rm_ptr->lastop)) { flags = 0; FCNTL2(rm_ptr->fildes, F_GETFL, flags); if (0 > flags) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, save_errno); } if (!(flags & O_NONBLOCK)) { FCNTL3(rm_ptr->fildes, F_SETFL, (flags | O_NONBLOCK), fcntl_res); if (0 > fcntl_res) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, save_errno); } } } if (!rm_ptr->fifo && !rm_ptr->is_pipe && !rm_ptr->fixed && (2 < rm_ptr->fildes) && (RM_WRITE != rm_ptr->lastop)) { /* need to do an lseek to set current location in file */ if ((off_t)-1 == (lseek(rm_ptr->fildes, rm_ptr->file_pos, SEEK_SET))) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"), RTS_ERROR_LITERAL("iorm_write()"), CALLFROM, save_errno); } } /* if current file position is less than bom_num_bytes and it is a disk in utf mode and last op not a WRITE skip past the BOM */ if (!rm_ptr->fifo && !rm_ptr->is_pipe && IS_UTF_CHSET(iod->ochset) && (rm_ptr->file_pos < rm_ptr->bom_num_bytes) && (2 < rm_ptr->fildes) && (RM_WRITE != rm_ptr->lastop)) { /* need to do lseek to skip the BOM before writing*/ if ((off_t)-1 == (lseek(rm_ptr->fildes, (off_t)rm_ptr->bom_num_bytes, SEEK_SET))) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"), RTS_ERROR_LITERAL("iorm_write()"), CALLFROM, save_errno); } } rm_ptr->lastop = RM_WRITE; if (IS_UTF_CHSET(iod->ochset)) { iorm_write_utf(v); REVERT_GTMIO_CH(&io_curr_device, ch_set); return; } inlen = v->len; if (!inlen) { REVERT_GTMIO_CH(&io_curr_device, ch_set); return; } stream = rm_ptr->stream; wrap = iod->wrap; if (stream && !wrap) outlen = inlen; else { if (iod->width < iod->dollar.x) save_dollarx = iod->width; else save_dollarx = iod->dollar.x; outlen = iod->width - save_dollarx; if (!wrap && !stream && (inlen > outlen)) inlen = outlen; /* implicit input truncation for non-stream files with the "nowrap" option. */ } for (out = v->addr; ; out += len) { len = MIN(inlen, outlen); if (rm_ptr->output_encrypted) { REALLOC_CRYPTBUF_IF_NEEDED(len); WRITE_ENCRYPTED_DATA(rm_ptr, iod->trans_name, out, len, pvt_crypt_buf.addr); out_ptr = pvt_crypt_buf.addr; } else out_ptr = out; if (rm_ptr->fifo || rm_ptr->is_pipe) { WRITEPIPE(rm_ptr->fildes, rm_ptr->pipe_buff_size, out_ptr, len, status); } else { DOWRITERC_RM(rm_ptr, out_ptr, len, status); } ISSUE_NOPRINCIO_IF_NEEDED_RM(status, ==, iod); rm_ptr->write_occurred = TRUE; iod->dollar.x += len; if (0 >= (inlen -= len)) break; if (!stream || wrap) { /* implicit record termination for non-stream files or stream files with the "wrap" option. */ if (!rm_ptr->fixed && wrap) { out_ptr = RMEOL; if (rm_ptr->output_encrypted) { assert(pvt_crypt_buf.len >= RMEOL_LEN); WRITE_ENCRYPTED_DATA(rm_ptr, iod->trans_name, out_ptr, RMEOL_LEN, pvt_crypt_buf.addr); out_ptr = pvt_crypt_buf.addr; } DOWRITERC_RM(rm_ptr, out_ptr, RMEOL_LEN, status); ISSUE_NOPRINCIO_IF_NEEDED_RM(status, ==, iod); rm_ptr->write_occurred = TRUE; } iod->dollar.x = 0; /* don't use wteol to terminate wrapped records for fixed. */ iod->dollar.y++; /* \n is reserved as an end-of-rec delimiter for variable format */ if (iod->length) /* and fixed format requires no padding for wrapped records */ iod->dollar.y %= iod->length; outlen = iod->width; } } iod->dollar.za = 0; REVERT_GTMIO_CH(&io_curr_device, ch_set); return; } fis-gtm-V7.0-005/sr_unix/iorm_wteol.c0000644000032200000250000001642014342376330016370 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_unistd.h" #include "io.h" #include "iosp.h" #include "iormdef.h" #include "iormdefsp.h" #include "gtmio.h" #include "gtmcrypt.h" #include "send_msg.h" #include "error.h" #include "svnames.h" #include "op.h" #include "util.h" #ifdef UTF8_SUPPORTED #include "gtm_conv.h" #include "gtm_utf8.h" #endif GBLREF boolean_t prin_in_dev_failure, prin_out_dev_failure; GBLREF io_pair io_std_device; GBLREF mval dollar_zstatus; #ifdef UTF8_SUPPORTED GBLREF UConverter *chset_desc[]; LITREF mstr chset_names[]; #endif error_def(ERR_CRYPTBADWRTPOS); error_def(ERR_DEVICEREADONLY); error_def(ERR_NOPRINCIO); error_def(ERR_NOTTOEOFONPUT); error_def(ERR_SYSCALL); void iorm_wteol(int4 x,io_desc *iod) { int i, fixed_pad, fixed_pad_bytes, bytes_per_char, avail_bytes, pad_size, res_size; int status, outbytes, len; char *outstr, temppad, temppadarray[2], *out_ptr; d_rm_struct *rm_ptr; unsigned int *dollarx_ptr; unsigned int *dollary_ptr; struct stat statbuf; int fstat_res, save_errno; boolean_t ch_set; ESTABLISH_GTMIO_CH(&iod->pair, ch_set); # ifdef __MVS__ /* on zos if it is a fifo device then point to the pair.out for $X and $Y */ if (((d_rm_struct *)iod->dev_sp)->fifo) { dollarx_ptr = &(iod->pair.out->dollar.x); dollary_ptr = &(iod->pair.out->dollar.y); rm_ptr = (d_rm_struct *) (iod->pair.out)->dev_sp; iod = iod->pair.out; } else # endif { dollarx_ptr = &(iod->dollar.x); dollary_ptr = &(iod->dollar.y); rm_ptr = (d_rm_struct *)iod->dev_sp; } if (rm_ptr->read_only) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DEVICEREADONLY); if ((!rm_ptr->fifo) && (!rm_ptr->is_pipe) && rm_ptr->output_encrypted) { if (!iod->dollar.zeof) { iod->dollar.za = ZA_IO_ERR; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_NOTTOEOFONPUT); } else { /* If there have not been any writes, and input encryption attributes are different from those for output, * and the file is not empty, disallow the write. */ if ((!rm_ptr->write_occurred) && ((!rm_ptr->input_encrypted) || (rm_ptr->input_iv.len != rm_ptr->output_iv.len) || memcmp(rm_ptr->input_iv.addr, rm_ptr->output_iv.addr, rm_ptr->input_iv.len) || (((rm_ptr->input_key.len != rm_ptr->output_key.len) || memcmp(rm_ptr->input_key.addr, rm_ptr->output_key.addr, rm_ptr->input_key.len)) && (!GTMCRYPT_SAME_KEY(rm_ptr->input_cipher_handle, rm_ptr->output_cipher_handle))))) { FSTAT_FILE(rm_ptr->fildes, &statbuf, fstat_res); if (-1 == fstat_res) { save_errno = errno; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fstat"), CALLFROM, save_errno); } if (0 != statbuf.st_size) { SET_DOLLARDEVICE_ERRSTR(iod, ONE_COMMA_CRYPTBADWRTPOS); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CRYPTBADWRTPOS); } } } } rm_ptr->lastop = RM_WRITE; # ifdef __MVS__ if (CHSET_BINARY != iod->process_chset) { # endif for (i = 0; i < x; i++) { # ifdef UTF8_SUPPORTED if (IS_UTF_CHSET(iod->ochset)) { if (!rm_ptr->done_1st_write) { if (CHSET_UTF16 == iod->ochset) { /* write BE BOM this is in raw bytes */ out_ptr = UTF16BE_BOM; if (rm_ptr->output_encrypted) { REALLOC_CRYPTBUF_IF_NEEDED(UTF16BE_BOM_LEN); WRITE_ENCRYPTED_DATA(rm_ptr, iod->trans_name, out_ptr, UTF16BE_BOM_LEN, pvt_crypt_buf.addr); out_ptr = pvt_crypt_buf.addr; } DOWRITERL_RM(rm_ptr, out_ptr, UTF16BE_BOM_LEN, res_size); ISSUE_NOPRINCIO_IF_NEEDED_RM(res_size, <=, iod); rm_ptr->write_occurred = TRUE; iod->ochset = CHSET_UTF16BE; get_chset_desc(&chset_names[iod->ochset]); } rm_ptr->done_1st_write = TRUE; } if (rm_ptr->fixed) { fixed_pad = iod->width - *dollarx_ptr; bytes_per_char = (CHSET_UTF8 == iod->ochset) ? 1 : 2; fixed_pad_bytes = fixed_pad * bytes_per_char; avail_bytes = rm_ptr->recordsize - rm_ptr->out_bytes; if (avail_bytes < fixed_pad_bytes) fixed_pad = avail_bytes / bytes_per_char; for ( ; fixed_pad > 0; fixed_pad -= pad_size) { pad_size = (fixed_pad > TAB_BUF_SZ) ? TAB_BUF_SZ : fixed_pad; res_size = iorm_write_utf_ascii(iod, (char *)RM_SPACES_BLOCK, pad_size); assert(res_size == pad_size * bytes_per_char); } if (rm_ptr->out_bytes < rm_ptr->recordsize) { /* padding bytes needed */ temppad = rm_ptr->padchar; if (CHSET_UTF16LE == iod->ochset) { temppadarray[0] = temppad; temppadarray[1] = '\0'; assert(2 == bytes_per_char); } else if (CHSET_UTF16BE == iod->ochset) { temppadarray[0] = '\0'; temppadarray[1] = temppad; assert(2 == bytes_per_char); } else { assert(CHSET_UTF8 == iod->ochset); temppadarray[0] = temppad; assert(1 == bytes_per_char); } for ( ; rm_ptr->out_bytes < rm_ptr->recordsize; rm_ptr->out_bytes += bytes_per_char) { out_ptr = temppadarray; if (rm_ptr->output_encrypted) { REALLOC_CRYPTBUF_IF_NEEDED(bytes_per_char); WRITE_ENCRYPTED_DATA(rm_ptr, iod->trans_name, out_ptr, bytes_per_char, pvt_crypt_buf.addr); out_ptr = pvt_crypt_buf.addr; } DOWRITERC_RM(rm_ptr, out_ptr, bytes_per_char, status); ISSUE_NOPRINCIO_IF_NEEDED_RM(status, ==, iod); rm_ptr->write_occurred = TRUE; } assert(rm_ptr->out_bytes == rm_ptr->recordsize); } } else { iorm_write_utf_ascii(iod, RMEOL, STRLEN(RMEOL)); } rm_ptr->out_bytes = 0; } else # endif if (rm_ptr->fixed) { for (fixed_pad = iod->width - *dollarx_ptr; fixed_pad > 0; fixed_pad -= res_size) { pad_size = (fixed_pad > TAB_BUF_SZ) ? TAB_BUF_SZ : fixed_pad; out_ptr = (char *)(RM_SPACES_BLOCK); if (rm_ptr->output_encrypted) { REALLOC_CRYPTBUF_IF_NEEDED(pad_size); WRITE_ENCRYPTED_DATA(rm_ptr, iod->trans_name, out_ptr, pad_size, pvt_crypt_buf.addr); out_ptr = pvt_crypt_buf.addr; } DOWRITERL_RM(rm_ptr, out_ptr, pad_size, res_size); ISSUE_NOPRINCIO_IF_NEEDED_RM(res_size, <=, iod); rm_ptr->write_occurred = TRUE; assert(res_size == pad_size); } } else { out_ptr = RMEOL; if (rm_ptr->output_encrypted) { REALLOC_CRYPTBUF_IF_NEEDED(RMEOL_LEN); WRITE_ENCRYPTED_DATA(rm_ptr, iod->trans_name, out_ptr, RMEOL_LEN, pvt_crypt_buf.addr); out_ptr = pvt_crypt_buf.addr; } DOWRITERC_RM(rm_ptr, out_ptr, RMEOL_LEN, status); ISSUE_NOPRINCIO_IF_NEEDED_RM(status, ==, iod); rm_ptr->write_occurred = TRUE; } *dollarx_ptr = 0; } # ifdef __MVS__ } else *dollarx_ptr = 0; /* just reset $X for BINARY */ # endif iod->dollar.za = 0; *dollary_ptr += x; if (iod->length) *dollary_ptr %= iod->length; REVERT_GTMIO_CH(&iod->pair, ch_set); return; } fis-gtm-V7.0-005/sr_unix/iormdef.h0000755000032200000250000003006614342376330015647 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #ifndef IORMDEF_H #define IORMDEF_H #include "gtmcrypt.h" /* For gtmcrypt_key_t below. */ #define DEF_RM_WIDTH 32767 #define DEF_RM_RECORDSIZE 32767 #define DEF_RM_LENGTH 66 #define CHUNK_SIZE BUFSIZ #define ONE_COMMA_UNAVAILABLE "1,Resource temporarily unavailable" #define ONE_COMMA_DEV_DET_EOF "1,Device detected EOF" #define ONE_COMMA_DEV_DET_EOF_DOLLARDEVICE "1,Device detected EOF ..... Now just exceed this line to more than DD_BUFLEN (80)." #define ONE_COMMA_CRYPTBADWRTPOS "1,Encrypted WRITE disallowed from a position different than where the last WRITE completed" #define DEF_RM_PADCHAR ' ' /* SPACE */ /* #define DEBUG_PIPE */ #ifdef DEBUG_PIPE #define PIPE_DEBUG(X) X #define DEBUGPIPEFLUSH FFLUSH(stdout) int pid; #else # define PIPE_DEBUG(X) #define DEBUGPIPEFLUSH #endif #ifdef UTF8_SUPPORTED #define SET_WIDTH_BYTES \ width_bytes = 1; \ width_chset = iod->ichset; \ switch (iod->ichset) \ { \ case CHSET_UTF16: \ case CHSET_UTF16BE: \ case CHSET_UTF16LE: \ width_bytes = 2; \ width_chset = iod->ichset; \ default: \ break; \ } \ switch (iod->ochset) \ { \ case CHSET_UTF16: \ case CHSET_UTF16BE: \ case CHSET_UTF16LE: \ width_bytes = 2; \ width_chset = iod->ochset; \ default: \ break; \ } #else #define SET_WIDTH_BYTES width_bytes = 1; #endif error_def(ERR_CLOSEFAIL); error_def(ERR_CRYPTBADWRTPOS); #define MEMSET_IF_DEFINED(BUFFER, CHAR, SIZE) \ { \ if (NULL != BUFFER) \ memset(BUFFER, CHAR, SIZE); \ } #define IORM_FCLOSE(D_RM, FILDES, FILSTR) \ { \ GBLREF int process_exiting; \ \ int fclose_res, rc, save_fd; \ \ if (NULL != D_RM->FILSTR) \ { /* Since FCLOSE also closes the fd, reset FILDES (no need to close it separately). */ \ LINUX_ONLY(assert(D_RM->FILDES == D_RM->FILSTR->_fileno);) \ if (!process_exiting) \ { /* Only do the actual FCLOSE() if the process is not exiting because a) the OS \ * takes care of opened file descriptors anyway; and b) we might have received \ * a deadly signal, such as SIGTERM, while in getc(), which can ultimately lead \ * to a hang on FCLOSE(). \ */ \ FCLOSE(D_RM->FILSTR, fclose_res); \ if (0 != fclose_res) \ { \ save_fd = D_RM->FILDES; \ rc = errno; \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CLOSEFAIL, 1, save_fd, rc);\ } \ } \ D_RM->FILSTR = NULL; \ D_RM->FILDES = FD_INVALID; \ } else if (FD_INVALID != D_RM->FILDES) \ { \ save_fd = D_RM->FILDES; \ CLOSEFILE_RESET(D_RM->FILDES, rc); /* resets "D_RM->FILDES" to FD_INVALID */ \ if (0 != rc) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CLOSEFAIL, 1, save_fd, rc); \ } \ } #define READ_ENCRYPTED_DATA(DEVICE, NAME, INBUF, INBUF_LEN, OUTBUF) \ { \ LITREF gtm_string_t null_iv; \ \ int rv; \ \ if (INBUF_LEN > 0) \ { \ GTMCRYPT_DECRYPT_CONT_IV(NULL, (DEVICE)->input_cipher_handle, \ INBUF, INBUF_LEN, OUTBUF, rv); \ if (0 != rv) \ GTMCRYPT_REPORT_ERROR(rv, rts_error, (NAME)->len, (NAME)->dollar_io); \ } \ } #define WRITE_ENCRYPTED_DATA(DEVICE, NAME, INBUF, INBUF_LEN, OUTBUF) \ { \ LITREF gtm_string_t null_iv; \ \ int rv; \ \ if (INBUF_LEN > 0) \ { \ GTMCRYPT_ENCRYPT_CONT_IV(NULL, (DEVICE)->output_cipher_handle, \ INBUF, INBUF_LEN, OUTBUF, rv); \ if (0 != rv) \ GTMCRYPT_REPORT_ERROR(rv, rts_error, (NAME)->len, (NAME)->dollar_io); \ } \ } /* Set prin_out_dev_failure if a write failed on the principal device. If it is a recurrence, issue the * NOPRINCIO error. */ #define ISSUE_NOPRINCIO_IF_NEEDED_RM(VAR, CMP_SIGN, IOD) \ MBSTART { \ int write_status; \ \ if (0 CMP_SIGN VAR) \ { /* Set prin_out_dev_failure to FALSE if it was set TRUE earlier but is working now. */ \ if (IOD == io_std_device.out) \ prin_out_dev_failure = FALSE; \ } else \ { \ ISSUE_NOPRINCIO_IF_NEEDED(iod, TRUE, FALSE); /* TRUE, FALSE: WRITE, not socket */ \ write_status = (-1 == VAR) ? errno : VAR; \ if (EAGAIN == write_status) \ SET_DOLLARDEVICE_ERRSTR(IOD, ONE_COMMA_UNAVAILABLE); \ else \ SET_DOLLARDEVICE_ONECOMMA_STRERROR(IOD, write_status); \ IOD->dollar.za = ZA_IO_ERR; \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) write_status); \ } \ } MBEND /* Operations for this device type */ #define RM_NOOP 0 #define RM_WRITE 1 #define RM_READ 2 enum pipe_which /* which module saved the interrupted info */ { pipewhich_invalid, pipewhich_readfl, }; /* ***************************************************** */ /* *********** structure for RMS driver **************** */ /* ***************************************************** */ /* The Dev_param_pair structure may exist for any device parameter whose definition is a character string, */ /* but it is being added here primarily for the pipe device type. */ /* For instance, a pipe will contain a "command" device parameter like command="/usr/bin/cat" */ /* The name field will be initialized to "COMMAND=" and the definition field will be "/usr/bin/cat" */ /* These will be output when the type is fifo. Other device types may be modified as desired */ typedef struct dev_param_pair{ char *name; char *definition; } Dev_param_pair; typedef struct dev_pairs { int num_pairs; Dev_param_pair pairs[3]; } Dev_param_pairs; typedef struct pipe_interrupt_type { ABS_TIME end_time; enum pipe_which who_saved; int max_bufflen; int bytes_read; int bytes2read; int char_count; int bytes_count; int add_bytes; boolean_t end_time_valid; struct d_rm_struct *newpipe; } pipe_interrupt; typedef struct { boolean_t fixed; /* Fixed format file */ boolean_t read_only; /* READONLY specified */ boolean_t write_only; /* WRITEONLY specified */ boolean_t stream; boolean_t fifo; boolean_t is_pipe; /* True if pipe device */ boolean_t independent; /* True if pipe process to live after pipe is closed */ boolean_t parse; /* True if pipe command is to be parsed */ boolean_t done_1st_read; /* If UTF16, we need to know if this is the first read or not to check for BOM */ boolean_t done_1st_write; /* If UTF16, we need to know if this is the first write or not to check for BOM */ boolean_t crlast; /* If not M, the last character seen was a CR so if LF is next, ignore it */ boolean_t def_width; /* WIDTH has not been changed */ boolean_t def_recsize; /* RECORDSIZE has not been changed */ boolean_t bom_read_one_done; /* If pipe/fifo and UTF8, read one byte to check for bom if not set */ boolean_t follow; /* True if disk read with follow - similar to tail -f on a file */ boolean_t no_destroy; /* true if disk and NO_DESTROY on CLOSE and filedes >= 2*/ boolean_t bom_checked; /* If disk and UTF8 the bom has been read and bom_num_bytes has been set */ pipe_interrupt pipe_save_state; /* Saved state of interrupted IO */ boolean_t mupintr; /* We were mupip interrupted */ unsigned int lastop; /* Last operation done on file */ int fildes; /* File descriptor */ int read_fildes; /* Read file descriptor if it's a pipe and stdout is returned */ FILE *read_filstr; /* Read FILE pointer if it's a pipe and stdout is returned */ struct io_desc_struct *stderr_child; /* pointer to io descriptor for pipe stderr device */ struct io_desc_struct *stderr_parent; /* pointer to io descriptor for pipe which opened stderr device */ pid_t pipe_pid; /* child process id for reaping on close */ Dev_param_pairs dev_param_pairs; int bufsize; /* Size of inbuf */ int outbufsize; /* Size of outbuf */ int4 recordsize; /* Size of record in bytes */ int4 padchar; /* Character to use for padding */ int4 fol_bytes_read; /* Number of bytes read from current record in utf fix mode with follow */ int4 last_was_timeout; /* last read in utf fix mode with follow was a timeout */ int4 orig_bytes_already_read; /* bytes_already_read from a previous read in utf fix mode with follow */ int out_bytes; /* Number of bytes output for this fixed record */ uint4 bom_buf_cnt; /* Count of bytes in BOM buffer */ uint4 bom_buf_off; /* Next available byte in BOM buffer */ uint4 bom_num_bytes; /* number of bom bytes read */ unsigned char bom_buf[4]; /* Buffer area for BOM assembly */ unsigned char *inbuf; /* Input buffer area */ unsigned char *inbuf_pos; /* Put next char in inbuf here */ unsigned char *inbuf_off; /* Next char to take from inbuf */ unsigned char *inbuf_top; /* Last char (+1) in inbuf */ unsigned char *outbuf; /* Output buffer area */ FILE *filstr; off_t file_pos; long pipe_buff_size; char *tmp_buffer; /* Buffer to store CHUNK_SIZE bytes */ int tot_bytes_in_buffer; /* Number of bytes read from device, it refers tmp_buffer buffer */ int start_pos; /* Current position in tmp_buffer */ boolean_t write_occurred; /* Flag indicating whether a write has occurred on this device. */ boolean_t read_occurred; /* Flag indicating whether a read has occurred on this device. */ boolean_t input_encrypted; /* Whether this device's input stream is encrypted or not. */ boolean_t output_encrypted; /* Whether this device's output stream is encrypted or not. */ mstr input_iv; /* Input Initialization Vector for this device's encryption. */ mstr output_iv; /* Output Initialization Vector for this device's encryption. */ mstr input_key; /* Name that maps to an input encryption key on disk. */ mstr output_key; /* Name that maps to an output encryption key on disk. */ gtmcrypt_key_t input_cipher_handle; /* Encryption cipher handle for this device. */ gtmcrypt_key_t output_cipher_handle; /* Decryption cipher handle for this device. */ gtm_chset_t ichset_utf16_variant; /* Used to determine UTF16 variant when CHSET is changed b/w UTF16 & M. */ gtm_chset_t ochset_utf16_variant; /* Used to determine UTF16 variant when CHSET is changed b/w UTF16 & M. */ uint4 fsblock_buffer_size; /* I/O buffer size; 1 == default size; 0 == no buffering */ char *fsblock_buffer; /* I/O buffer for, erm, buffered I/O */ boolean_t crlastbuff; /* Whether CR was last char of the buffer */ } d_rm_struct; /* rms */ #ifdef KEEP_zOS_EBCDIC #define NATIVE_NL 0x15 /* EBCDIC */ #else #define NATIVE_NL '\n' /* ASCII */ #endif int gtm_utf_bomcheck(io_desc *iod, gtm_chset_t *chset, unsigned char *buffer, int len); void iorm_cond_wteol(io_desc *iod); int iorm_get_bom(io_desc *io_ptr, int *blocked_in, boolean_t ispipe, int flags, int4 *tot_bytes_read, TID timer_id, int4 *msec_timeout, boolean_t colon_zero); int iorm_get_bom_fol(io_desc *io_ptr, int4 *tot_bytes_read, int4 *msec_timeout, boolean_t timed, boolean_t *bom_timeout, ABS_TIME end_time); int iorm_get_fol(io_desc *io_ptr, int4 *tot_bytes_read, int4 *msec_timeout, boolean_t timed, boolean_t zint_restart, boolean_t *follow_timeout, ABS_TIME end_time); int iorm_get(io_desc *io_ptr, int *blocked_in, boolean_t ispipe, int flags, int4 *tot_bytes_read, TID timer_id, int4 *msec_timeout, boolean_t colon_zero, boolean_t zint_restart); int iorm_write_utf_ascii(io_desc *iod, char *string, int len); void iorm_write_utf(mstr *v); void iorm_readfl_badchar(mval *vmvalptr, int datalen, int delimlen, unsigned char *delimptr, unsigned char *strend); int open_get_bom(io_desc *io_ptr, int bom_size); int open_get_bom2(io_desc *io_ptr, int max_bom_size ); #endif fis-gtm-V7.0-005/sr_unix/iosize.h0000755000032200000250000000073314342376330015522 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, Inc. * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #define LEGAL_IO_SIZE(X) (X) fis-gtm-V7.0-005/sr_unix/iosocket_pass_local.c0000644000032200000250000004504614342376330020236 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2022 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 #ifdef __sun #include #endif #include "gtm_socket.h" #include "gtm_unistd.h" #include "io_params.h" #include "io.h" #include "iotimer.h" #include "wake_alarm.h" #include "iosocketdef.h" #include "min_max.h" #include "gtm_netdb.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "eintr_wrappers.h" #include "stringpool.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "error.h" #define MAX_PASS_FDS 256 #define PID_CHECKING_SUPPORTED defined(__linux__) || defined(__sun) || defined(_AIX) #define RECVALL(FD, BUF, BUFLEN, RVAL) XFERALL(recv, FD, BUF, BUFLEN, RVAL) #define SENDALL(FD, BUF, BUFLEN, RVAL) XFERALL(send, FD, BUF, BUFLEN, RVAL) #define XFERALL(XFOP, FD, BUF, BUFLEN, RVAL) \ { \ int xf_fd = (FD); \ unsigned char *xf_buf = (unsigned char *)(BUF); \ size_t xf_buflen = (BUFLEN), xf_xfercnt = 0; \ ssize_t xf_rval = 0; \ \ while (!outofband && !out_of_time && (xf_buflen > xf_xfercnt)) \ { \ xf_rval = (XFOP)(xf_fd, xf_buf + xf_xfercnt, xf_buflen - xf_xfercnt, 0); \ if (-1 == xf_rval) \ { \ if (EINTR == errno) \ continue; \ else \ break; \ } \ else if (0 == xf_rval) \ { \ xf_rval = -1; \ errno = ECONNRESET; \ break; \ } \ xf_xfercnt += xf_rval; \ } \ if (outofband || out_of_time) \ { \ xf_rval = -1; \ errno = EINTR; \ } \ RVAL = (-1 == xf_rval) ? (ssize_t)-1 : (ssize_t)xf_xfercnt; \ } #define PASS_COMPLETE "PASS_COMPLETE" #define ACCEPT_COMPLETE "ACCEPT_COMPLETE" #define PROTOCOL_ERROR "Protocol Error" GBLREF d_socket_struct *socket_pool; GBLREF io_pair io_std_device; GBLREF int4 gtm_max_sockets; GBLREF spdesc stringpool; GBLREF int dollar_truth; GBLREF bool out_of_time; GBLREF volatile int4 outofband; error_def(ERR_CONNSOCKREQ); error_def(ERR_CREDNOTPASSED); error_def(ERR_CURRSOCKOFR); error_def(ERR_EXPR); error_def(ERR_LOCALSOCKREQ); error_def(ERR_NOSOCKETINDEV); error_def(ERR_NOSOCKHANDLE); error_def(ERR_PEERPIDMISMATCH); error_def(ERR_SOCKMAX); error_def(ERR_SOCKACCEPT); error_def(ERR_SOCKNOTFND); error_def(ERR_SOCKNOTPASSED); error_def(ERR_SOCKPASS); error_def(ERR_SOCKPASSDATAMIX); error_def(ERR_TEXT); error_def(ERR_ZINTRECURSEIO); pid_t get_peer_pid(int fd); struct msgdata { int magic; unsigned int proto_version; int fdcount; }; #define MSG_MAGIC 1431655765 #define MSG_PROTO_VERSION 1 void iosocket_pass_local(io_desc *iod, pid_t pid, int4 msec_timeout, int argcnt, va_list args) { d_socket_struct *dsocketptr; socket_struct *socketptr, *psocketptr; int argn, index, rval, save_errno; mval *handle; mstr handlestr; mstr handles[MAX_PASS_FDS]; char cmsg_buffer[SIZEOF(struct cmsghdr) * 2 + CMSG_SPACE(MAX_PASS_FDS * SIZEOF(int))]; int4 cmsg_buflen; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; struct msgdata mdata; int *fds; pid_t peerpid; TID timer_id; char complete_buf[STR_LIT_LEN(ACCEPT_COMPLETE)]; char *errptr; int4 errlen; boolean_t ch_set; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (1 > argcnt) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(2) ERR_NOSOCKHANDLE, 0); return; } assert(gtmsocket == iod->type); dsocketptr = (d_socket_struct *)iod->dev_sp; if (0 >= dsocketptr->n_socket) { if (iod != io_std_device.out) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOSOCKETINDEV); return; } if (dsocketptr->n_socket <= dsocketptr->current_socket) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket); return; } if (dsocketptr->mupintr) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZINTRECURSEIO); socketptr = dsocketptr->socket[dsocketptr->current_socket]; if (socket_local != socketptr->protocol) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(2) ERR_LOCALSOCKREQ, 0); return; } if (socket_connected != socketptr->state) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(2) ERR_CONNSOCKREQ, 0); return; } ESTABLISH_GTMIO_CH(&iod->pair, ch_set); ENSURE_PASS_SOCKET(socketptr); out_of_time = FALSE; # if PID_CHECKING_SUPPORTED if (-1 < pid) { peerpid = get_peer_pid(socketptr->sd); if (-1 == peerpid) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(2) ERR_CREDNOTPASSED, 0); return; } if (pid != peerpid) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_PEERPIDMISMATCH, 2, peerpid, pid); return; } } # endif /* pass fds */ fds = (int *)CMSG_DATA((struct cmsghdr *)cmsg_buffer); for (argn = 0; argn < argcnt; argn++) { handle = va_arg(args, mval *); if ((NULL == handle) || !MV_DEFINED(handle)) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(2) ERR_EXPR, 0); return; } MV_FORCE_STR(handle); if ((NULL == socket_pool) || (0 > (index = iosocket_handle(handle->str.addr, &handle->str.len, FALSE, socket_pool)))) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKNOTFND, 2, handle->str.len, handle->str.addr); return; } handles[argn] = handle->str; psocketptr = socket_pool->socket[index]; fds[argn] = psocketptr->sd; } /* send argcnt with fds */ mdata.magic = MSG_MAGIC; mdata.proto_version = MSG_PROTO_VERSION; mdata.fdcount = argcnt; iov.iov_base = &mdata; iov.iov_len = SIZEOF(mdata); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_control = (struct cmsghdr *)cmsg_buffer; msg.msg_controllen = CMSG_SPACE(argcnt * SIZEOF(int)); cmsg = CMSG_FIRSTHDR(&msg); assert(cmsg); cmsg->cmsg_len = CMSG_LEN(argcnt * SIZEOF(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; memcpy(iod->dollar.device, "0", SIZEOF("0")); if (NO_M_TIMEOUT != msec_timeout) { timer_id = (TID)iosocket_pass_local; start_timer(timer_id, msec_timeout, wake_alarm, 0, NULL); } do { rval = sendmsg(socketptr->sd, &msg, 0); } while (!outofband && !out_of_time && (-1 == rval) && (EINTR == errno)); if (-1 == rval) goto ioerr; assert(rval == iov.iov_len); for (argn = 0; argn < argcnt; argn++) { if (0 > (index = iosocket_handle(handles[argn].addr, &handles[argn].len, FALSE, socket_pool))) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKNOTFND, 2, handle->str.len, handle->str.addr); return; } psocketptr = socket_pool->socket[index]; /* send handle length */ SENDALL(socketptr->sd, &handles[argn].len, SIZEOF(handles[argn].len), rval); if (-1 == rval) goto ioerr; assert(rval == SIZEOF(handles[argn].len)); /* send handle */ SENDALL(socketptr->sd, handles[argn].addr, handles[argn].len, rval); if (-1 == rval) goto ioerr; assert(rval == handles[argn].len); /* send buffer length */ SENDALL(socketptr->sd, &psocketptr->buffered_length, SIZEOF(psocketptr->buffered_length), rval); if (-1 == rval) goto ioerr; assert(rval == SIZEOF(psocketptr->buffered_length)); /* send buffer */ SENDALL(socketptr->sd, psocketptr->buffer + psocketptr->buffered_offset, psocketptr->buffered_length, rval); if (-1 == rval) goto ioerr; assert(rval == psocketptr->buffered_length); } SENDALL(socketptr->sd, PASS_COMPLETE, STR_LIT_LEN(PASS_COMPLETE), rval); if (-1 == rval) goto ioerr; assert(rval == STR_LIT_LEN(PASS_COMPLETE)); RECVALL(socketptr->sd, complete_buf, STR_LIT_LEN(ACCEPT_COMPLETE), rval); if (-1 == rval) goto ioerr; assert(rval == STR_LIT_LEN(ACCEPT_COMPLETE)); if (0 != STRNCMP_LIT(complete_buf, ACCEPT_COMPLETE)) { if ((NO_M_TIMEOUT != msec_timeout) && !out_of_time) cancel_timer(timer_id); iod->dollar.za = ZA_IO_ERR; errptr = PROTOCOL_ERROR; SET_DOLLARDEVICE_ONECOMMA_ERRSTR(iod, errptr, errlen); if (socketptr->ioerror) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_SOCKPASS, 0, ERR_TEXT, 2, errlen, errptr); REVERT_GTMIO_CH(&iod->pair, ch_set); return; } if ((NO_M_TIMEOUT != msec_timeout) && !out_of_time) cancel_timer(timer_id); for (argn = 0; argn < argcnt; argn++) { handlestr = handles[argn]; if (-1 != (index = iosocket_handle(handlestr.addr, &handlestr.len, FALSE, socket_pool))) iosocket_close_one(socket_pool, index); } if (NO_M_TIMEOUT != msec_timeout) dollar_truth = TRUE; REVERT_GTMIO_CH(&iod->pair, ch_set); return; ioerr: save_errno = errno; if (out_of_time && (EINTR == save_errno)) { dollar_truth = FALSE; REVERT_GTMIO_CH(&iod->pair, ch_set); return; } if ((NO_M_TIMEOUT != msec_timeout) && !out_of_time) cancel_timer(timer_id); iod->dollar.za = ZA_IO_ERR; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); if (socketptr->ioerror) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_SOCKPASS, 0, save_errno, 0); REVERT_GTMIO_CH(&iod->pair, ch_set); return; } void iosocket_accept_local(io_desc *iod, mval *handlesvar, pid_t pid, int4 msec_timeout, int argcnt, va_list args) { d_socket_struct *dsocketptr; socket_struct *socketptr, *psocketptr; int argn, index, fdcount = 0, fdn, scnt = 0, rval, save_errno, handleslen = 0; mval *handle, tmp; mstr handles[MAX_PASS_FDS]; mstr handlestr; char cmsg_buffer[SIZEOF(struct cmsghdr) * 2 + CMSG_SPACE(MAX_PASS_FDS * SIZEOF(int))]; int4 cmsg_buflen; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg = NULL; struct msgdata mdata; int *fds; char *hptr; int handlelen; size_t tmpbuflen; pid_t peerpid; TID timer_id; char complete_buf[STR_LIT_LEN(PASS_COMPLETE)]; char *errptr; int4 errlen; boolean_t ch_set; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(gtmsocket == iod->type); dsocketptr = (d_socket_struct *)iod->dev_sp; if (0 >= dsocketptr->n_socket) { if (iod != io_std_device.out) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOSOCKETINDEV); return; } if (dsocketptr->n_socket <= dsocketptr->current_socket) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket); return; } if (dsocketptr->mupintr) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZINTRECURSEIO); socketptr = dsocketptr->socket[dsocketptr->current_socket]; if (socket_local != socketptr->protocol) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(2) ERR_LOCALSOCKREQ, 0); return; } if (socket_connected != socketptr->state) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(2) ERR_CONNSOCKREQ, 0); return; } ESTABLISH_GTMIO_CH(&iod->pair, ch_set); ENSURE_PASS_SOCKET(socketptr); out_of_time = FALSE; # if PID_CHECKING_SUPPORTED if (-1 < pid) { peerpid = get_peer_pid(socketptr->sd); if (-1 == peerpid) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(2) ERR_CREDNOTPASSED, 0); return; } if (pid != peerpid) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_PEERPIDMISMATCH, 2, peerpid, pid); return; } } # endif /* accept fds */ if (NULL == socket_pool) iosocket_poolinit(); for (argn = 0; argn < argcnt; argn++) { handle = va_arg(args, mval *); if ((NULL != handle) && MV_DEFINED(handle)) MV_FORCE_STR(handle); if ((NULL == handle) || !MV_DEFINED(handle) || (-1 != iosocket_handle(handle->str.addr, &handle->str.len, FALSE, socket_pool))) handles[argn].addr = NULL; /* use passed or generated handle */ else handles[argn] = handle->str; } memcpy(iod->dollar.device, "0", SIZEOF("0")); if (NO_M_TIMEOUT != msec_timeout) { timer_id = (TID)iosocket_accept_local; start_timer(timer_id, msec_timeout, wake_alarm, 0, NULL); } msg.msg_name = NULL; msg.msg_namelen = 0; /* read fd count first */ iov.iov_base = &mdata; iov.iov_len = SIZEOF(mdata); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = (struct cmsghdr *)cmsg_buffer; msg.msg_controllen = CMSG_SPACE(MAX_PASS_FDS * SIZEOF(int)); do { rval = recvmsg(socketptr->sd, &msg, 0); } while (!outofband && !out_of_time && (-1 == rval) && (EINTR == errno)); if (0 == rval) { rval = -1; errno = ECONNRESET; } if (-1 == rval) goto ioerr; if (SIZEOF(mdata) != rval) { if ((NO_M_TIMEOUT != msec_timeout) && !out_of_time) cancel_timer(timer_id); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_SOCKNOTPASSED); return; } assert(rval == iov.iov_len); if ((MSG_MAGIC != mdata.magic) || (MSG_PROTO_VERSION != mdata.proto_version)) { iod->dollar.za = ZA_IO_ERR; errptr = PROTOCOL_ERROR; SET_DOLLARDEVICE_ONECOMMA_ERRSTR(iod, errptr, errlen); if (socketptr->ioerror) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_SOCKACCEPT, 0, ERR_TEXT, 2, errlen, errptr); } cmsg = CMSG_FIRSTHDR(&msg); while((cmsg != NULL) && ((SOL_SOCKET != cmsg->cmsg_level) || (SCM_RIGHTS != cmsg->cmsg_type))) cmsg = CMSG_NXTHDR(&msg, cmsg); if (NULL == cmsg) { if ((NO_M_TIMEOUT != msec_timeout) && !out_of_time) cancel_timer(timer_id); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(2) ERR_SOCKNOTPASSED, 0); return; } fdcount = mdata.fdcount; fds = (int *)CMSG_DATA(cmsg); assert(((cmsg->cmsg_len - ((char *)CMSG_DATA(cmsg) - (char *)cmsg)) / SIZEOF(int)) == fdcount); if (0 == fdcount) { if ((NO_M_TIMEOUT != msec_timeout) && !out_of_time) cancel_timer(timer_id); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(2) ERR_SOCKNOTPASSED, 0); return; } if (gtm_max_sockets <= (socket_pool->n_socket + fdcount)) { if ((NO_M_TIMEOUT != msec_timeout) && !out_of_time) cancel_timer(timer_id); for (fdn = 0; fdn < fdcount; fdn++) { CLOSE(fds[fdn], rval); } rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SOCKMAX, 1, gtm_max_sockets); return; } for (fdn=0; fdn < fdcount; fdn++) { /* read handle length */ RECVALL(socketptr->sd, &handlestr.len, SIZEOF(handlestr.len), rval); if (-1 == rval) goto ioerr; assertpro(SIZEOF(handlestr.len) == rval); /* read handle */ ENSURE_STP_FREE_SPACE(MAX_HANDLE_LEN); handlestr.addr = (char *)stringpool.free; RECVALL(socketptr->sd, handlestr.addr, handlestr.len, rval); if (-1 == rval) goto ioerr; assertpro(handlestr.len == rval); if ((fdn >= argcnt) || (NULL == handles[fdn].addr)) { /* If the passed handle name already exists in the socket pool, create a new one */ if (-1 != iosocket_handle(handlestr.addr, &handlestr.len, FALSE, socket_pool)) iosocket_handle(handlestr.addr, &handlestr.len, TRUE, socket_pool); stringpool.free += handlestr.len; handles[fdn] = handlestr; } else handlestr = handles[fdn]; /* Use the handle from the argument list */ /* read socket buffer length */ RECVALL(socketptr->sd, &tmpbuflen, SIZEOF(tmpbuflen), rval); if (-1 == rval) goto ioerr; assertpro(SIZEOF(tmpbuflen) == rval); psocketptr = iosocket_create(NULL, ((tmpbuflen > DEFAULT_SOCKET_BUFFER_SIZE) ? tmpbuflen : DEFAULT_SOCKET_BUFFER_SIZE), fds[fdn], FALSE); assertpro(NULL != psocketptr); psocketptr->handle_len = handlestr.len; memcpy(psocketptr->handle, handlestr.addr, handlestr.len); psocketptr->dev = socket_pool; socket_pool->socket[socket_pool->n_socket++] = psocketptr; socket_pool->current_socket = socket_pool->n_socket - 1; scnt++; if (0 < tmpbuflen) { /* read socket buffer */ psocketptr->buffered_length = tmpbuflen; RECVALL(socketptr->sd, psocketptr->buffer, psocketptr->buffered_length, rval); if (-1 == rval) goto ioerr; assertpro(psocketptr->buffered_length == rval); } psocketptr->buffered_offset = 0; handleslen += handlestr.len; } RECVALL(socketptr->sd, complete_buf, STR_LIT_LEN(PASS_COMPLETE), rval); if (-1 == rval) goto ioerr; assert(rval == STR_LIT_LEN(PASS_COMPLETE)); if (0 != STRNCMP_LIT(complete_buf, PASS_COMPLETE)) { if ((NO_M_TIMEOUT != msec_timeout) && !out_of_time) cancel_timer(timer_id); for (fdn = scnt - 1; fdn >= 0; fdn--) { if (-1 != (index = iosocket_handle(handles[fdn].addr, &handles[fdn].len, FALSE, socket_pool))) iosocket_close_one(socket_pool, index); } for (fdn = scnt; fdn < fdcount; fdn++) { CLOSE(fds[fdn], rval); } iod->dollar.za = ZA_IO_ERR; errptr = PROTOCOL_ERROR; SET_DOLLARDEVICE_ONECOMMA_ERRSTR(iod, errptr, errlen); if (socketptr->ioerror) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_SOCKACCEPT, 0, ERR_TEXT, 2, errlen, errptr); REVERT_GTMIO_CH(&iod->pair, ch_set); return; } SENDALL(socketptr->sd, ACCEPT_COMPLETE, STR_LIT_LEN(ACCEPT_COMPLETE), rval); if (-1 == rval) goto ioerr; assert(rval == STR_LIT_LEN(ACCEPT_COMPLETE)); if ((NO_M_TIMEOUT != msec_timeout) && !out_of_time) cancel_timer(timer_id); if (NULL != handlesvar) { handleslen += (fdcount > 1) ? (fdcount - 1) : 0; /* space for delimiters */ ENSURE_STP_FREE_SPACE(handleslen); hptr = (char *)stringpool.free; stringpool.free += handleslen; handlesvar->mvtype = MV_STR; handlesvar->str.addr = hptr; handlesvar->str.len = handleslen; memcpy(hptr, handles[0].addr, handles[0].len); hptr += handles[0].len; for (fdn=1; fdn < fdcount; fdn++) { *hptr++ = '|'; memcpy(hptr, handles[fdn].addr, handles[fdn].len); hptr += handles[fdn].len; } } if (NO_M_TIMEOUT != msec_timeout) dollar_truth = TRUE; return; ioerr: save_errno = errno; if (out_of_time && (EINTR == save_errno)) { dollar_truth = FALSE; } if ((NO_M_TIMEOUT != msec_timeout) && !out_of_time) cancel_timer(timer_id); for (fdn = scnt - 1; fdn >= 0; fdn--) { if (-1 != (index = iosocket_handle(handles[fdn].addr, &handles[fdn].len, FALSE, socket_pool))) iosocket_close_one(socket_pool, index); } for (fdn = scnt; fdn < fdcount; fdn++) { CLOSE(fds[fdn], rval); } if ((EINTR != save_errno) || outofband) { iod->dollar.za = ZA_IO_ERR; SET_DOLLARDEVICE_ONECOMMA_STRERROR(iod, save_errno); if (socketptr->ioerror) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_SOCKACCEPT, 0, save_errno, 0); } REVERT_GTMIO_CH(&iod->pair, ch_set); return; } pid_t get_peer_pid(int fd) { # if defined(__linux__) struct ucred creds; GTM_SOCKLEN_TYPE solen; solen = SIZEOF(struct ucred); if (-1 == getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &solen)) return -1; else return creds.pid; # elif defined(_AIX) struct peercred_struct creds; GTM_SOCKLEN_TYPE solen; solen = SIZEOF(struct peercred_struct); if (-1 == getsockopt(fd, SOL_SOCKET, SO_PEERID, &creds, &solen)) return -1; else return creds.pid; # elif defined(__sun) ucred_t *credsptr = NULL; pid_t peerpid; if (-1 == getpeerucred(fd, &credsptr)) return -1; else { peerpid = ucred_getpid(credsptr); ucred_free(credsptr); return peerpid; } # else return -1; # endif } fis-gtm-V7.0-005/sr_unix/iosocket_tls.c0000644000032200000250000004327114342376330016716 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2022 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" #ifdef GTM_TLS #include #ifdef USE_POLL #include "gtm_poll.h" #else #include "gtm_select.h" #endif #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "eintr_wrappers.h" #include "io.h" #include "gt_timer.h" #include "iosocketdef.h" #include "gtm_conv.h" #include "gtmio.h" #include "gtm_utf8.h" #include "rel_quant.h" #include "send_msg.h" #include "error.h" #include "min_max.h" #include "gtm_caseconv.h" #include "fgncalsp.h" #include "iotimer.h" #include "gtm_tls.h" GBLREF io_pair io_curr_device; GBLREF char dl_err[MAX_ERRSTR_LEN]; GBLREF int dollar_truth; GBLREF void (*primary_exit_handler)(void); GBLREF gtm_tls_ctx_t *tls_ctx; error_def(ERR_CURRSOCKOFR); error_def(ERR_TLSCONVSOCK); error_def(ERR_TLSDLLNOOPEN); error_def(ERR_TLSHANDSHAKE); error_def(ERR_TLSINIT); error_def(ERR_TLSPARAM); error_def(ERR_TLSRENEGOTIATE); error_def(ERR_NOSOCKETINDEV); error_def(ERR_SOCKPASSDATAMIX); error_def(ERR_TEXT); error_def(ERR_ZINTRECURSEIO); error_def(ERR_GETSOCKOPTERR); error_def(ERR_SETSOCKOPTERR); #define MAX_TLSOPTION 12 #define TLSLABEL "tls: { " #define COLONBRACKET ": { " #define BRACKETSSEMIS " }; };" #define RENEGOTIATE "RENEGOTIATE" #define FLUSH_OUTPUT_ERROR "unable to flush pending output" #define UNREAD_INPUT "unread input" typedef enum { tlsopt_invalid, tlsopt_client, tlsopt_server, tlsopt_renegotiate } tls_option; void iosocket_tls(mval *optionmval, int4 msec_timeout, mval *tlsid, mval *password, mval *extraarg) { int4 devlen, extrastr_len, flags, len, length, save_errno, status, status2, timeout, tls_errno; int4 errlen, errlen2; int fcntl_flags, fcntl_res; io_desc *iod; d_socket_struct *dsocketptr; socket_struct *socketptr; char idstr[MAX_TLSID_LEN + SIZEOF(RENEGOTIATE) + 1], optionstr[MAX_TLSOPTION]; char passwordstr[GTM_PASSPHRASE_MAX_ASCII + 1]; const char *errp, *errp2; char *charptr, *extraptr, *extrastr, *errptr; tls_option option; gtm_tls_socket_t *tlssocket; ABS_TIME cur_time, end_time; # ifdef USE_POLL struct pollfd fds; # else fd_set fds, *readfds, *writefds; struct timeval *timeout_ptr, timeout_spec; # endif boolean_t ch_set; iod = io_curr_device.out; assert(gtmsocket == iod->type); dsocketptr = (d_socket_struct *)iod->dev_sp; if (0 >= dsocketptr->n_socket) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOSOCKETINDEV); return; } if (dsocketptr->n_socket <= dsocketptr->current_socket) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket); return; } if (dsocketptr->mupintr) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZINTRECURSEIO); socketptr = dsocketptr->socket[dsocketptr->current_socket]; ESTABLISH_GTMIO_CH(&iod->pair, ch_set); ENSURE_DATA_SOCKET(socketptr); if (socket_tcpip != socketptr->protocol) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_TLSPARAM, 4, LEN_AND_LIT("/TLS"), LEN_AND_LIT("but socket is not TCP")); return; } if (socket_connected != socketptr->state) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_TLSPARAM, 4, LEN_AND_LIT("/TLS"), LEN_AND_LIT("but socket not connected")); return; } if (NULL != tlsid) { length = tlsid->str.len; if (MAX_TLSID_LEN < (length + 1)) /* for null */ { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_TLSPARAM, 4, LEN_AND_LIT("TLSID"), LEN_AND_LIT("too long")); return; } STRNCPY_STR(idstr, tlsid->str.addr, length); idstr[length] = '\0'; } else idstr[0] = '\0'; if (('\0' != idstr[0]) && (NULL != password)) { /* password only usable if tlsid provided - iosocket_iocontrol checks */ length = password->str.len; if (GTM_PASSPHRASE_MAX_ASCII < length) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_TLSPARAM, 4, LEN_AND_LIT("passphrase"), LEN_AND_LIT("too long")); return; } STRNCPY_STR(passwordstr, password->str.addr, length); passwordstr[length] = '\0'; } else if (NULL != password) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_TLSPARAM, 4, LEN_AND_LIT("passphrase"), LEN_AND_LIT("requires TLSID")); return; } else passwordstr[0] = '\0'; length = MIN((SIZEOF(optionstr) - 1), optionmval->str.len); lower_to_upper((uchar_ptr_t)optionstr, (uchar_ptr_t)optionmval->str.addr, length); optionstr[length] = '\0'; if (0 == memcmp(optionstr, "CLIENT", MIN(length, STRLEN("CLIENT")))) option = tlsopt_client; else if (0 == memcmp(optionstr, "SERVER", MIN(length, STRLEN("SERVER")))) option = tlsopt_server; else if (0 == memcmp(optionstr, "RENEGOTIATE", length)) option = tlsopt_renegotiate; else option = tlsopt_invalid; memcpy(iod->dollar.device, "0", SIZEOF("0")); if (NO_M_TIMEOUT != msec_timeout) { sys_get_curr_time(&cur_time); add_int_to_abs_time(&cur_time, msec_timeout, &end_time); } if ((tlsopt_client == option) || (tlsopt_server == option)) { /* most of the setup is common */ if (socketptr->tlsenabled) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_TLSPARAM, 4, LEN_AND_STR(optionstr), LEN_AND_LIT("but TLS already enabled")); return; } assertpro((0 >= socketptr->buffered_length) && (0 >= socketptr->obuffer_length)); if (NULL == tls_ctx) { /* first use of TLS in process */ if (-1 == gtm_tls_loadlibrary()) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_TLSDLLNOOPEN, 0, ERR_TEXT, 2, LEN_AND_STR(dl_err)); return; } if (NULL == (tls_ctx = (gtm_tls_init(GTM_TLS_API_VERSION, GTMTLS_OP_INTERACTIVE_MODE)))) { errp = gtm_tls_get_error(NULL); SET_DOLLARDEVICE_ONECOMMA_ERRSTR(iod, errp, errlen); if (socketptr->ioerror) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TLSINIT, 0, ERR_TEXT, 2, errlen, errp); if (NO_M_TIMEOUT != msec_timeout) dollar_truth = FALSE; REVERT_GTMIO_CH(&iod->pair, ch_set); return; } if (primary_exit_handler) atexit(primary_exit_handler); } socketptr->tlsenabled = TRUE; flags = GTMTLS_OP_SOCKET_DEV | ((tlsopt_client == option) ? GTMTLS_OP_CLIENT_MODE : 0); if (NULL != extraarg) { if ('\0' == idstr[0]) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_TLSPARAM, 4, LEN_AND_STR(optionstr), LEN_AND_LIT("TLSID required for configuration string")); return; } length = extraarg->str.len; extrastr = malloc(length + 1 + SIZEOF(TLSLABEL) - 1 + tlsid->str.len + SIZEOF(COLONBRACKET) - 1 + SIZEOF(BRACKETSSEMIS) - 1); MEMCPY_LIT(extrastr, TLSLABEL); extraptr = extrastr + SIZEOF(TLSLABEL) - 1; memcpy(extraptr, tlsid->str.addr, tlsid->str.len); extraptr += tlsid->str.len; MEMCPY_LIT(extraptr, COLONBRACKET); extraptr = extraptr + SIZEOF(COLONBRACKET) - 1; STRNCPY_STR(extraptr, extraarg->str.addr, length); extraptr += length; MEMCPY_LIT(extraptr, BRACKETSSEMIS); extraptr += SIZEOF(BRACKETSSEMIS) - 1; *extraptr = '\0'; if (0 > gtm_tls_add_config(tls_ctx, idstr, extrastr)) { /* error string available */ socketptr->tlsenabled = FALSE; free(extrastr); errp = gtm_tls_get_error(NULL); SET_DOLLARDEVICE_ONECOMMA_ERRSTR(iod, errp, errlen); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_TLSCONVSOCK, 0, ERR_TEXT, 2, errlen, errp); return; } else free(extrastr); } if ('\0' != passwordstr[0]) { if ('\0' == idstr[0]) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_TLSPARAM, 4, LEN_AND_STR(optionstr), LEN_AND_LIT("TLSID required for passphrase")); return; } if (0 > gtm_tls_store_passwd(tls_ctx, idstr, passwordstr)) { /* error string available */ socketptr->tlsenabled = FALSE; errp = gtm_tls_get_error(NULL); SET_DOLLARDEVICE_ONECOMMA_ERRSTR(iod, errp, errlen); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_TLSCONVSOCK, 0, ERR_TEXT, 2, errlen, errp); return; } } if (socketptr->nonblocked_output) { if (GTM_TLS_API_VERSION_NONBLOCK > tls_ctx->plugin_version) { /* Due to lack of gtm_tls_version(), old pre GTM_TLS_API_VERSION_NONBLOCK plugins will not even * load but include this test as an example of how to check if the plugin loaded is new enough */ socketptr->tlsenabled = FALSE; errp = gtm_tls_get_error(NULL); SET_DOLLARDEVICE_ONECOMMA_ERRSTR(iod, errp, errlen); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_TLSCONVSOCK, 0, ERR_TEXT, 2, errlen, errp); return; } flags |= GTMTLS_OP_NONBLOCK_WRITE; if (!socketptr->nonblocking) { /* set O_NONBLOCK if needed before calling plugin */ FCNTL2(socketptr->sd, F_GETFL, fcntl_flags); if (fcntl_flags < 0) { iod->dollar.za = 9; save_errno = errno; errptr = (char *)STRERROR(save_errno); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_GETSOCKOPTERR, 5, LEN_AND_LIT("F_GETFL FOR NON BLOCKING WRITE"), save_errno, LEN_AND_STR(errptr)); return; } FCNTL3(socketptr->sd, F_SETFL, fcntl_flags | (O_NDELAY | O_NONBLOCK), fcntl_res); if (fcntl_res < 0) { iod->dollar.za = 9; save_errno = errno; errptr = (char *)STRERROR(save_errno); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("F_SETFL FOR NON BLOCKING WRITE"), save_errno, LEN_AND_STR(errptr)); return; } socketptr->nonblocking = TRUE; # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_SOCKET_NONBLOCK) && (0 < gtm_white_box_test_case_count)) { if (-1 == setsockopt(socketptr->sd, SOL_SOCKET, SO_SNDBUF, >m_white_box_test_case_count, SIZEOF(gtm_white_box_test_case_count))) { save_errno = errno; errptr = (char *)STRERROR(save_errno); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("SO_SNDBUF"), save_errno, LEN_AND_STR(errptr)); return; } } # endif } } socketptr->tlssocket = gtm_tls_socket(tls_ctx, NULL, socketptr->sd, idstr, flags); if (NULL == socketptr->tlssocket) { socketptr->tlsenabled = FALSE; errp = gtm_tls_get_error(NULL); SET_DOLLARDEVICE_ONECOMMA_ERRSTR(iod, errp, errlen); if (socketptr->ioerror) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TLSCONVSOCK, 0, ERR_TEXT, 2, errlen, errp); if (NO_M_TIMEOUT != msec_timeout) dollar_truth = FALSE; REVERT_GTMIO_CH(&iod->pair, ch_set); return; } status = 0; # ifndef USE_POLL if (NO_M_TIMEOUT == msec_timeout) timeout_ptr = NULL; else { timeout_spec.tv_sec = msec_timeout / MILLISECS_IN_SEC; /* remainder in millsecs to microsecs */ timeout_spec.tv_usec = (msec_timeout % MILLISECS_IN_SEC) * MICROSECS_IN_MSEC; timeout_ptr = &timeout_spec; } # endif do { status2 = 0; if (0 != status) { # ifdef USE_POLL fds.fd = socketptr->sd; fds.events = (GTMTLS_WANT_READ == status) ? POLLIN : POLLOUT; # else readfds = writefds = NULL; assertpro(FD_SETSIZE > socketptr->sd); FD_ZERO(&fds); FD_SET(socketptr->sd, &fds); writefds = (GTMTLS_WANT_WRITE == status) ? &fds : NULL; readfds = (GTMTLS_WANT_READ == status) ? &fds : NULL; # endif POLL_ONLY(if (-1 == (status2 = poll(&fds, 1, (NO_M_TIMEOUT == msec_timeout) ? -1 : msec_timeout)))) SELECT_ONLY(if (-1 == (status2 = select(socketptr->sd + 1, readfds, writefds, NULL, timeout_ptr)))) { save_errno = errno; if (EAGAIN == save_errno) { rel_quant(); /* allow resources to become available - likely sb nanosleep */ status2 = 0; /* treat as timeout */ } else if (EINTR == save_errno) status2 = 0; } } else status2 = 1; /* do accept/connect first time */ if (0 < status2) { if (tlsopt_server == option) status = gtm_tls_accept((gtm_tls_socket_t *)socketptr->tlssocket); else status = gtm_tls_connect((gtm_tls_socket_t *)socketptr->tlssocket); } if ((0 > status2) || ((status != 0) && ((GTMTLS_WANT_READ != status) && (GTMTLS_WANT_WRITE != status)))) { if (0 != status) { tls_errno = gtm_tls_errno(); if (0 > tls_errno) errp = gtm_tls_get_error((gtm_tls_socket_t *)socketptr->tlssocket); else errp = STRERROR(tls_errno); } else errp = STRERROR(save_errno); socketptr->tlsenabled = FALSE; SET_DOLLARDEVICE_ONECOMMA_ERRSTR(iod, errp, errlen); if (socketptr->ioerror) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TLSHANDSHAKE, 0, ERR_TEXT, 2, errlen, errp); if (NO_M_TIMEOUT != msec_timeout) dollar_truth = FALSE; REVERT_GTMIO_CH(&iod->pair, ch_set); return; } if ((0 != status) && (0 <= status2)) /* not accepted/connected and not error */ { /* check for timeout if not error or want read or write */ if ((0 != msec_timeout) && (NO_M_TIMEOUT != msec_timeout)) { sys_get_curr_time(&cur_time); cur_time = sub_abs_time(&end_time, &cur_time); if (0 >= cur_time.at_sec) { /* no more time */ gtm_tls_session_close((gtm_tls_socket_t **)&socketptr->tlssocket); socketptr->tlsenabled = FALSE; dollar_truth = FALSE; REVERT_GTMIO_CH(&iod->pair, ch_set); return; } else { /* adjust msec_timeout for poll/select */ # ifdef USE_POLL msec_timeout = (cur_time.at_sec * MILLISECS_IN_SEC) + DIVIDE_ROUND_UP(cur_time.at_usec, MICROSECS_IN_MSEC); # else timeout_spec.tv_sec = cur_time.at_sec; timeout_spec.tv_usec = (gtm_tv_usec_t)cur_time.at_usec; # endif } } else if (0 == msec_timeout) { /* only one chance */ gtm_tls_session_close((gtm_tls_socket_t **)&socketptr->tlssocket); socketptr->tlsenabled = FALSE; dollar_truth = FALSE; REVERT_GTMIO_CH(&iod->pair, ch_set); return; } continue; } } while ((GTMTLS_WANT_READ == status) || (GTMTLS_WANT_WRITE == status)); /* turn on output buffering */ SOCKET_OBUFFER_INIT(socketptr, socketptr->buffer_size, DEFAULT_WRITE_WAIT, (DEFAULT_WRITE_WAIT * 2)); socketptr->obuffer_in_use = TRUE; } else if (tlsopt_renegotiate == option) { if (!socketptr->tlsenabled) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_TLSPARAM, 4, LEN_AND_STR(optionstr), LEN_AND_LIT("but TLS not enabled")); return; /* make compiler and analyzers happy */ } tlssocket = (gtm_tls_socket_t *)socketptr->tlssocket; if (GTMTLS_OP_CLIENT_MODE & tlssocket->flags) { /* only server can renegotiate */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_TLSPARAM, 4, LEN_AND_STR(optionstr), RTS_ERROR_LITERAL("not allowed when client")); return; } if ((0 < socketptr->buffered_length) || (0 < gtm_tls_cachedbytes((gtm_tls_socket_t *)socketptr->tlssocket))) { /* if anything in input buffer or ready to be read then error */ errp = UNREAD_INPUT; SET_DOLLARDEVICE_ONECOMMA_ERRSTR(iod, errp, errlen); if (socketptr->ioerror) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TLSRENEGOTIATE, 0, ERR_TEXT, 2, errlen, errp); if (NO_M_TIMEOUT != msec_timeout) dollar_truth = FALSE; REVERT_GTMIO_CH(&iod->pair, ch_set); return; } if (0 < socketptr->obuffer_length) { /* flush pending output */ iosocket_flush(iod); if (0 < socketptr->obuffer_length) { /* get obuffer_error as additional ERR_TEXT */ errp = FLUSH_OUTPUT_ERROR; if (-1 == socketptr->obuffer_errno) errp2 = gtm_tls_get_error((gtm_tls_socket_t *)socketptr->tlssocket); else errp2 = (char *)STRERROR(socketptr->obuffer_errno); SET_DOLLARDEVICE_ONECOMMA_ERRSTR1_ERRSTR2(iod, errp, errlen, errp2, errlen2); if (socketptr->ioerror) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(10) ERR_TLSRENEGOTIATE, 0, ERR_TEXT, 2, errlen, errp, ERR_TEXT, 2, errlen2, errp2); if (NO_M_TIMEOUT != msec_timeout) dollar_truth = FALSE; REVERT_GTMIO_CH(&iod->pair, ch_set); return; } } if (NULL != extraarg) { length = extraarg->str.len; if ('\0' == idstr[0]) { /* create new section from initial tlsid -renegotiate */ charptr = ((gtm_tls_socket_t *)socketptr->tlssocket)->tlsid; assert(MAX_TLSID_LEN >= STRLEN(charptr)); len = SNPRINTF(idstr, SIZEOF(idstr), "%s-" RENEGOTIATE, charptr); assert(SIZEOF(idstr) > len); if (len >= SIZEOF(idstr)) len = ((int4) SIZEOF(idstr)) - 1; idstr[len] = '\0'; } else len = STRLEN(idstr); extrastr = malloc(length + 1 + STR_LIT_LEN(TLSLABEL) + len + STR_LIT_LEN(COLONBRACKET) + STR_LIT_LEN(BRACKETSSEMIS)); extrastr_len = length + 1 + STR_LIT_LEN(TLSLABEL) + len + STR_LIT_LEN(COLONBRACKET) + STR_LIT_LEN(BRACKETSSEMIS); len = SNPRINTF(extrastr, extrastr_len, TLSLABEL "%s" COLONBRACKET "%.*s" BRACKETSSEMIS, idstr, extraarg->str.len, extraarg->str.addr); assert(len == extrastr_len - 1); /* snprintf excludes null terminator in return value */ } else extrastr = NULL; status = gtm_tls_renegotiate_options((gtm_tls_socket_t *)socketptr->tlssocket, msec_timeout, idstr, extrastr, (NULL != tlsid)); if (NULL != extrastr) free(extrastr); if (0 != status) { tls_errno = gtm_tls_errno(); if (0 > tls_errno) errp = gtm_tls_get_error((gtm_tls_socket_t *)socketptr->tlssocket); else errp = STRERROR(tls_errno); SET_DOLLARDEVICE_ONECOMMA_ERRSTR(iod, errp, errlen); if (socketptr->ioerror) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TLSRENEGOTIATE, 0, ERR_TEXT, 2, errlen, errp); if (NO_M_TIMEOUT != msec_timeout) dollar_truth = FALSE; REVERT_GTMIO_CH(&iod->pair, ch_set); return; } } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TLSPARAM, 4, LEN_AND_STR(optionstr), LEN_AND_LIT("not a valid option")); if (NO_M_TIMEOUT != msec_timeout) dollar_truth = TRUE; REVERT_GTMIO_CH(&iod->pair, ch_set); return; } #endif fis-gtm-V7.0-005/sr_unix/iosp.h0000755000032200000250000000177614342376330015202 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2008 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 "gtm_ctype.h" #define SS_NORMAL 0 #define SS_NOLOGNAM -1 #define SS_LOG2LONG -2 #define SS_ENDOFTAPE 2 #define SS_ENDOFFILE 4 /* * To avoid any possible deadlock because of ftok collisions, we use different project ids. */ #define GTM_ID 43 #define REPLPOOL_ID 44 #define GTMSECSHR_ID 46 #define RALL 0444 #define RWALL 0666 #define RWDALL 0777 /* parameters for io_rundown() */ #define NORMAL_RUNDOWN 0 #define RUNDOWN_EXCEPT_STD 1 #define SYSCALL_SUCCESS(STATUS) (SS_NORMAL == (STATUS)) #define SYSCALL_ERROR(STATUS) (SS_NORMAL != (STATUS)) fis-gtm-V7.0-005/sr_unix/iott_close.c0000644000032200000250000000426414342376330016357 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_unistd.h" #include #include "io.h" #include "iottdef.h" #include "io_params.h" #include "gtmio.h" #include "stringpool.h" #include "setterm.h" #include "error.h" #include "op.h" #include "indir_enum.h" GBLREF io_pair io_std_device; LITREF unsigned char io_params_size[]; error_def(ERR_SYSCALL); void iott_close(io_desc *v, mval *pp) { d_tt_struct *ttptr; params ch; int status; int p_offset; boolean_t ch_set; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(v->type == tt); if (v->state != dev_open) return; ESTABLISH_GTMIO_CH(&v->pair, ch_set); iott_flush(v); if (v->pair.in != v) assert(v->pair.out == v); ttptr = (d_tt_struct *)v->dev_sp; if (v->pair.out != v) assert(v->pair.in == v); v->state = dev_closed; resetterm(v); p_offset = 0; while (*(pp->str.addr + p_offset) != iop_eol) { if ((ch = *(pp->str.addr + p_offset++)) == iop_exception) DEF_EXCEPTION(pp, p_offset, v); p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); } if (v == io_std_device.in || (v == io_std_device.out)) { REVERT_GTMIO_CH(&v->pair, ch_set); return; } CLOSEFILE_RESET(ttptr->fildes, status); /* resets "ttptr->fildes" to FD_INVALID */ if (0 != status) { assert(status == errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("iott_close(CLOSEFILE)"), CALLFROM, status); } if (ttptr->recall_buff.addr) { free(ttptr->recall_buff.addr); ttptr->recall_buff.addr = NULL; } if (v->dollar.devicebuffer) { free(v->dollar.devicebuffer); v->dollar.devicebuffer = NULL; } REVERT_GTMIO_CH(&v->pair, ch_set); return; } fis-gtm-V7.0-005/sr_unix/iott_edit.c0000755000032200000250000003510214342376330016175 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2018 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. * * * ****************************************************************/ /* WARNING: this module contains a mixture of ASCII and EBCDIC on S390*/ #include "mdef.h" #include "gtm_string.h" #include "gtm_signal.h" #include "gtm_unistd.h" #include "gtm_stdlib.h" #include #include "io.h" #include "trmdef.h" #include "iottdef.h" #include "iottdefsp.h" #include "iott_edit.h" #include "gtmio.h" #include "eintr_wrappers.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" #include "gtm_utf8.h" #endif #include "min_max.h" #include "error.h" GBLREF io_pair io_curr_device; GBLREF int AUTO_RIGHT_MARGIN, EAT_NEWLINE_GLITCH; GBLREF char *CURSOR_UP, *CURSOR_DOWN, *CURSOR_LEFT, *CURSOR_RIGHT; int iott_write_raw(int fildes, void *str832, unsigned int len) { /* Writes len characters without considering the cursor position * The characters are really utf codepoints if the current * device is in UTF-8 mode * Returns -1 if error or number of bytes written */ unsigned char *str, string[TTDEF_BUF_SZ], *outptr, *outtop; wint_t *str32, temp32; int written, this_write, outlen; io_desc *io_ptr = io_curr_device.in; d_tt_struct *tt_ptr; boolean_t utf8_active; boolean_t ch_set; if (0 == len) return 0; ESTABLISH_RET_GTMIO_CH(&io_curr_device, -1, ch_set); tt_ptr = (d_tt_struct *)io_ptr->dev_sp; utf8_active = gtm_utf8_mode ? (CHSET_M != io_ptr->ichset) : FALSE; if (!utf8_active) { str = (unsigned char *)str832; DOWRITERL_A(fildes, str, len, written); if (0 > written) { REVERT_GTMIO_CH(&io_curr_device, ch_set); return -1; } #ifdef UTF8_SUPPORTED } else { str32 = (wint_t *)str832; for (written = 0; 0 < len; ) { for (outptr = string, outtop = (unsigned char *)(string + SIZEOF(string)); 0 < len && (outptr + GTM_MB_LEN_MAX) < outtop; str32++, len--) { temp32 = *str32; /* so argument not modified */ outptr = UTF8_WCTOMB(temp32, outptr); } /* either end of input or end of conversion buffer */ if (0 < (outlen = (int)((outptr - string)))) { /* something to write */ DOWRITERL_A(fildes, string, outlen, this_write); if (0 > this_write) { REVERT_GTMIO_CH(&io_curr_device, ch_set); return -1; } written += this_write; } } #endif } REVERT_GTMIO_CH(&io_curr_device, ch_set); return written; } int write_str(void *str832, unsigned int len, unsigned int start_x, boolean_t move, boolean_t multibyte) { /* writes a specified string starting from the current cursor position and returns the cursor back to the same place. str832 -> the string to write, may be utf code points len -> the length of the string start_x -> is the current cursor's column in the window. move -> whether the cursor moves or not. multibyte -> str832 is always bytes, if utf8_active then multibyte possible returns -1 if error, 0 if successful */ int number_of_lines_up, number_of_chars_left, written, ret, outlen; unsigned int cur_x; int width = io_curr_device.in->width, cur_width, line_width, this_width; int fildes = ((d_tt_struct *)((io_curr_device.in)->dev_sp))->fildes; unsigned char *str, string[TTDEF_BUF_SZ], *outptr, *outtop, *strstart, *nextptr; wint_t *str32, temp32; io_desc *io_ptr = io_curr_device.in; d_tt_struct *tt_ptr; boolean_t utf8_active, writenewline; boolean_t ch_set; assert(width); ESTABLISH_RET_GTMIO_CH(&io_ptr->pair, -1, ch_set); tt_ptr = (d_tt_struct *)io_ptr->dev_sp; utf8_active = gtm_utf8_mode ? (CHSET_M != io_ptr->ichset) : FALSE; if (utf8_active && !multibyte) str32 = (wint_t *)str832; else str = (unsigned char *)str832; if (multibyte && utf8_active) { outptr = str; outtop = str + len; } cur_x = start_x % width; /* start_x is absolute, get relative value into cur_x */ number_of_lines_up = 0; #ifdef UTF8_SUPPORTED if (utf8_active) { cur_width = width - cur_x; for (line_width = 0; 0 < len; ) { writenewline = FALSE; if (multibyte) { /* go through mb string one line at a time */ for (strstart = outptr ; outptr < outtop; ) { nextptr = UTF8_MBTOWC(outptr, outtop, temp32); GTM_IO_WCWIDTH(temp32, this_width); if ((line_width + this_width) > cur_width) { writenewline = TRUE; break; } line_width += this_width; outptr = nextptr; } outlen = (int)(outptr - strstart); len -= outlen; } else { for (outptr = string, outtop = string + SIZEOF(string); (0 < len) && ((outptr + GTM_MB_LEN_MAX) < outtop); str32++, len--) { GTM_IO_WCWIDTH(*str32, this_width); if ((line_width + this_width) > cur_width) { writenewline = TRUE; break; } line_width += this_width; temp32 = *str32; /* preserve argument */ outptr = UTF8_WCTOMB(temp32, outptr); } outlen =(int)(outptr - string); strstart = string; } assert(!writenewline || len); assert(line_width <= cur_width); if (line_width >= cur_width) /* our write has positioned us to end of width. write new line */ writenewline = TRUE; /* either end of input, end of conversion buffer, or full line */ if (0 < outlen) { /* something to write */ DOWRITERL_A(fildes, strstart, outlen, written); if (0 > written) { REVERT_GTMIO_CH(&io_curr_device, ch_set); return -1; } } if (!writenewline) { if (0 < len) continue; /* end of buffer in middle of line so no EOL */ if (0 == len) { /* last line is partial so no new line */ cur_x = cur_x + line_width; break; } } /* ------------------------------------------------------------------------- * for terminals that have the EAT_NEWLINE_GLITCH auto_margin doesn't work * even though AUTO_RIGHT_MARGIN may be 1. So you have to check both * before writing the TTEOL * ------------------------------------------------------------------------- */ if (!AUTO_RIGHT_MARGIN || EAT_NEWLINE_GLITCH) { DOWRITERC(fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL), ret); if (0 != ret) { REVERT_GTMIO_CH(&io_curr_device, ch_set); return -1; } } number_of_lines_up++; cur_x = line_width = 0; cur_width = width; } } else { #endif for ( ; 0 < len; ) { cur_width = width - cur_x; if (cur_width) { if (len < cur_width) cur_width = len; DOWRITERL_A(fildes, str, cur_width, written); if (0 > written) { REVERT_GTMIO_CH(&io_curr_device, ch_set); return -1; } str += written; len -= written; cur_x += cur_width; } if ((cur_x < width) && (0 == len)) break; /* last line is partial so no new line */ /* ------------------------------------------------------------------------- * for terminals that have the EAT_NEWLINE_GLITCH auto_margin doesn't work * even though AUTO_RIGHT_MARGIN may be 1. So you have to check both * before writing the TTEOL * ------------------------------------------------------------------------- */ if (!AUTO_RIGHT_MARGIN || EAT_NEWLINE_GLITCH) { DOWRITERC(fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL), ret); if (0 != ret) { REVERT_GTMIO_CH(&io_curr_device, ch_set); return -1; } } number_of_lines_up++; cur_x = 0; cur_width = width; } #ifdef UTF8_SUPPORTED } #endif number_of_chars_left = cur_x - start_x; if (!move) { ret = (NULL != CURSOR_UP) ? write_loop(fildes, (unsigned char *)CURSOR_UP, number_of_lines_up) : 0; if (0 > ret) { REVERT_GTMIO_CH(&io_curr_device, ch_set); return -1; } if (number_of_chars_left > 0) ret = (NULL != CURSOR_LEFT) ? write_loop(fildes, (unsigned char *)CURSOR_LEFT, number_of_chars_left) : 0; else ret = (NULL != CURSOR_RIGHT) ? write_loop(fildes, (unsigned char *)CURSOR_RIGHT, -number_of_chars_left) : 0; if (0 > ret) { REVERT_GTMIO_CH(&io_curr_device, ch_set); return -1; } } REVERT_GTMIO_CH(&io_curr_device, ch_set); return 0; } /* This function is almost the same as "write_str" except that the string that it writes is spaces. It uses "write_str" in turn */ int write_str_spaces(unsigned int len, unsigned int start_x, boolean_t move) { static unsigned char *space_buf = NULL; static int buflen = 0; int i; if (len > buflen) { if (NULL != space_buf) free(space_buf); space_buf = (unsigned char *)malloc(len); buflen = len; for (i = 0; i < buflen; i++) space_buf[i] = ' '; } /* Currently "write_str" treats any input string as a "wint_t" array if in utf8 mode. To avoid that we take the * sleazy route of calling it with "multibyte" option (last parameter) set to TRUE that way it treats this as * a byte array rather than a wint_t array. */ return write_str(&space_buf[0], len, start_x, move, TRUE); } int move_cursor_left(int col, int num_cols) { /* ------------------------------------------------------- * moves cursor left by num_cols columns. if col is leftmost, * then it goes back to the end of the previous line * returns 0 if success, != 0 if error * ------------------------------------------------------- */ int fildes = ((d_tt_struct *)((io_curr_device.in)->dev_sp))->fildes; int ret; boolean_t ch_set; ESTABLISH_RET_GTMIO_CH(&io_curr_device, -1, ch_set); if (0 == num_cols) ret = 0; else if (0 > num_cols) ret = move_cursor_right(col, -num_cols); else if (0 < col) { ret = (NULL != CURSOR_LEFT) ? write_loop(fildes, (unsigned char *)CURSOR_LEFT, MIN(col, num_cols)) : 0; num_cols -= MIN(col, num_cols); if (num_cols) { if (NULL != CURSOR_UP) { DOWRITERC(fildes, CURSOR_UP, strlen(CURSOR_UP), ret); if (0 > ret) { REVERT_GTMIO_CH(&io_curr_device, ch_set); return -1; } } ret = (NULL != CURSOR_RIGHT) ? write_loop(fildes, (unsigned char *)CURSOR_RIGHT, io_curr_device.in->width - num_cols) : 0; } } else { if (NULL != CURSOR_UP) { DOWRITERC(fildes, CURSOR_UP, strlen(CURSOR_UP), ret); if (0 > ret) { REVERT_GTMIO_CH(&io_curr_device, ch_set); return -1; } } ret = (NULL != CURSOR_RIGHT) ? write_loop(fildes, (unsigned char *)CURSOR_RIGHT, io_curr_device.in->width - num_cols) : 0; } REVERT_GTMIO_CH(&io_curr_device, ch_set); return ret; } int move_cursor_right(int col, int num_cols) { /* ------------------------------------------------------- * moves cursor right by num_cols columns, if col is rightmost, * then it goes to the start of the next line * returns 0 if success, != 0 if error * ------------------------------------------------------- */ int fildes = ((d_tt_struct *)((io_curr_device.in)->dev_sp))->fildes; int ret; boolean_t ch_set; ESTABLISH_RET_GTMIO_CH(&io_curr_device, -1, ch_set); if (0 == num_cols) ret = 0; else if (0 > num_cols) ret = move_cursor_left(col, -num_cols); else if ((io_curr_device.in->width - num_cols) > col) ret = (NULL != CURSOR_RIGHT) ? write_loop(fildes, (unsigned char *)CURSOR_RIGHT, num_cols) : 0; else { DOWRITERC(fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL), ret); if (0 > ret) { REVERT_GTMIO_CH(&io_curr_device, ch_set); return -1; } num_cols -= (io_curr_device.in->width - col); if (num_cols) ret = (NULL != CURSOR_RIGHT) ? write_loop(fildes, (unsigned char *)CURSOR_RIGHT, num_cols) : 0; } REVERT_GTMIO_CH(&io_curr_device, ch_set); return ret; } int write_loop(int fildes, unsigned char *str, int num_times) { int i, size_required, ret; unsigned char string[1024]; unsigned char *temp = NULL; *string = '\0'; size_required = num_times * STRLEN((char *)str); if (size_required > SIZEOF(string)) { for (i = 0; i < num_times; i++) { DOWRITERC(fildes, str, strlen((char *)str), ret); if (0 > ret) return -1; } } else if (num_times) { for (i = 0; i < num_times; i++) { strcat((char *)string, (char *)str); } DOWRITERC(fildes, string, size_required, ret); if (0 > ret) return -1; } return 0; } int move_cursor(int fildes, int num_up, int num_left) { int ret; ret = 0; if ((num_up < 0) && (NULL != CURSOR_DOWN)) ret = write_loop (fildes, (unsigned char *)CURSOR_DOWN, -num_up); else if ((num_up > 0) && (NULL != CURSOR_UP)) ret = write_loop (fildes, (unsigned char *)CURSOR_UP, num_up); if (0 > ret) return -1; ret = 0; if ((num_left < 0) && (NULL != CURSOR_RIGHT)) ret = write_loop(fildes, (unsigned char *)CURSOR_RIGHT, -num_left); else if ((num_left > 0) && (NULL != CURSOR_LEFT)) ret = write_loop(fildes, (unsigned char *)CURSOR_LEFT, num_left); if (0 > ret) return -1; return 0; } /* This function computes the absolute value of $X (i.e. without doing modulo the terminal device width) * for an arbitrary index in an array of characters. This requires the width of the terminal as input as * well as the starting display column (dx_start) for the 0th element of the character array. The final * value that it returns does not include "dx_start" as this is how "dx_instr" and "dx_outlen" is maintained * in the caller functions (dm_read and iott_readfl). * * str832 -> the array of characters, may be utf code points * index -> the index of this array upto which we want to cumulative dollarx computed * width -> the WIDTH of the terminal device * dx_start -> the value of $X after the GT.M prompt has been displayed (in case of dm_read) and 0 (for iott_readfl). * */ int compute_dx(void *str832, unsigned int index, unsigned int width, unsigned int dx_start) { boolean_t utf8_active; io_desc *io_ptr = io_curr_device.in; wint_t *str32; int dx_ret, this_width, i; boolean_t ch_set; ESTABLISH_RET_GTMIO_CH(&io_curr_device, -1, ch_set); utf8_active = gtm_utf8_mode ? (CHSET_M != io_ptr->ichset) : FALSE; str32 = (wint_t *)str832; if (utf8_active) { dx_ret = dx_start; for (i = 0; i < index; i++) { GTM_IO_WCWIDTH(str32[i], this_width); assert(this_width <= width); /* width cannot be less than one wide character's display width */ if (((dx_ret % width) + this_width) > width) dx_ret = ROUND_UP(dx_ret, width); /* add $X padding for wide character in last column */ dx_ret += this_width; } REVERT_GTMIO_CH(&io_curr_device, ch_set); return dx_ret - dx_start; /* before returning make sure "dx_ret" is of same dimension as "dx_instr" * variable in "dm_read" or "iott_readfl" (the callers) */ } else { REVERT_GTMIO_CH(&io_curr_device, ch_set); return index; /* every character is 1-display-column wide so no need to look at "str832" */ } } fis-gtm-V7.0-005/sr_unix/iott_edit.h0000755000032200000250000000713514342376330016207 0ustar librarygtc/**************************************************************** * * * Copyright 2005, 2009 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. * * * ****************************************************************/ #ifndef IOTT_EDIT_H #define IOTT_EDIT_H #ifdef KEEP_zOS_EBCDIC LITREF unsigned char ebcdic_lower_to_upper_table[]; LITREF unsigned char e2a[]; # define INPUT_CHAR asc_inchar # define GETASCII(OUTPARM, INPARM) {OUTPARM = e2a[INPARM];} # define NATIVE_CVT2UPPER(OUTPARM, INPARM) {OUTPARM = ebcdic_lower_to_upper_table[INPARM];} #else # define INPUT_CHAR inchar # define GETASCII(OUTPARM, INPARM) # define NATIVE_CVT2UPPER(OUTPARM, INPARM) {OUTPARM = lower_to_upper_table[INPARM];} #endif #define GET_OFF(OFFSET) (utf8_active ? buffer_32_start[OFFSET] : buffer_start[OFFSET]) #define STORE_OFF(CHAR,OFFSET) {if (utf8_active) \ buffer_32_start[OFFSET] = CHAR; \ else \ buffer_start[OFFSET] = (unsigned char)CHAR;} #define BUFF_ADDR(OFFSET) (utf8_active ? (char *)&buffer_32_start[OFFSET] : (char *)&buffer_start[OFFSET]) #define BUFF_CHAR_SIZE (utf8_active ? SIZEOF(wint_t) : SIZEOF(unsigned char)) #define SET_BUFF(OFFSET,CHAR,LENGTH) {if (utf8_active) \ { int off = OFFSET; \ for ( ; off < (OFFSET + (LENGTH)); off++) \ buffer_32_start[off] = CHAR; \ } \ else \ memset(&buffer_start[OFFSET], CHAR, LENGTH);} #define MOVE_BUFF(OFFSET,SOURCE,LENGTH) {if (utf8_active) \ memmove(&buffer_32_start[OFFSET], SOURCE, (LENGTH) * SIZEOF(wint_t)); \ else \ memmove(&buffer_start[OFFSET], SOURCE, LENGTH);} #define IOTT_COMBINED_CHAR_CHECK \ { \ int tmp_instr; \ \ /* It is possible that a combining character is the character under the cursor in which \ * case we need to erase it by going back "instr" as much as needed until we find \ * a non-zero width representation and from that point on repaint the display. \ */ \ dx_next = compute_dx(BUFF_ADDR(0), instr + 1, ioptr_width, dx_start); \ if (dx_next == dx_instr) \ { /* 0-width character (i.e. combining character). handle specially */ \ for (dx_prev = dx_instr, tmp_instr = instr; (tmp_instr != 0); ) \ { \ tmp_instr--; \ dx_prev = compute_dx(BUFF_ADDR(0), tmp_instr, ioptr_width, dx_start); \ if (dx_prev != dx_next) \ break; \ } \ delchar_width = dx_instr - dx_prev; \ if (delchar_width) \ { \ move_cursor_left(dx, delchar_width); \ dx = (dx - delchar_width + ioptr_width) % ioptr_width; \ write_str(BUFF_ADDR(tmp_instr), instr - tmp_instr, dx, FALSE, FALSE); \ move_cursor_right(dx, delchar_width); \ dx = (dx + delchar_width + ioptr_width) % ioptr_width; \ } \ } \ } int iott_write_raw(int fildes, void *str832, unsigned int len); int write_str(void *str832, unsigned int len, unsigned int start_x, boolean_t move, boolean_t multibyte); int write_str_spaces(unsigned int len, unsigned int start_x, boolean_t move); int move_cursor_left (int col, int num_cols); int move_cursor_right (int col, int num_cols); int write_loop(int fildes, unsigned char *str, int num_times); int move_cursor(int fildes, int num_up, int num_left); int compute_dx(void *str832, unsigned int index, unsigned int width, unsigned int dx_start); #endif fis-gtm-V7.0-005/sr_unix/iott_flush.c0000755000032200000250000000763514342376330016403 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_unistd.h" #include #include "error.h" #include "io.h" #include "iottdef.h" #include "gtmio.h" #include "gt_timer.h" #include "send_msg.h" #include "have_crit.h" #include "iott_flush_time.h" #include "deferred_events.h" #include "deferred_events_queue.h" GBLREF boolean_t prin_out_dev_failure; GBLREF int process_exiting; GBLREF int4 error_condition; GBLREF io_pair io_curr_device, io_std_device; error_def(ERR_TERMHANGUP); error_def(ERR_TERMWRITE); void iott_flush_buffer(io_desc *io_ptr, boolean_t new_write_flag) { d_tt_struct *tt_ptr; int4 status; ssize_t write_len; boolean_t ch_set; tt_ptr = io_ptr->dev_sp; if (!tt_ptr->write_active) return; /* Was assert but that ended up causing endless loops -- now we just survive */ ESTABLISH_GTMIO_CH(&io_ptr->pair, ch_set); write_len = (ssize_t)(tt_ptr->tbuffp - tt_ptr->ttybuff); if (0 < write_len) { DOWRITERC(tt_ptr->fildes, tt_ptr->ttybuff, write_len, status); if ((0 == status) && (ERR_TERMHANGUP != error_condition)) { tt_ptr->tbuffp = tt_ptr->ttybuff; if (io_ptr == io_std_device.out) { /* ------------------------------------------------ * set prin_out_dev_failure to FALSE in case it * had been set TRUE earlier and is now working. * for eg. a write fails and the next write works. * ------------------------------------------------ */ prin_out_dev_failure = FALSE; } } else /* (0 != status) */ { tt_ptr->write_active = FALSE; /* In case we come back this-a-way */ if ((io_ptr == io_std_device.out) && !prin_out_dev_failure) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_TERMWRITE, 0, status); prin_out_dev_failure = TRUE; /* set flag for NOPRINCIO, to give app a chance */ } xfer_set_handlers(defer_error, status, FALSE); } } tt_ptr->write_active = new_write_flag; REVERT_GTMIO_CH(&io_ptr->pair, ch_set); } void iott_flush(io_desc *io_ptr) { d_tt_struct *tt_ptr; boolean_t ch_set; ESTABLISH_GTMIO_CH(&io_ptr->pair, ch_set); tt_ptr = io_ptr->dev_sp; if (tt_ptr->timer_set) { cancel_timer((TID)io_ptr); tt_ptr->timer_set = FALSE; } if (tt_ptr->write_active) { REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return; /* We are nesting the buffer flush -- let original copy do its job */ } tt_ptr->write_active = TRUE; iott_flush_buffer(io_ptr, FALSE); REVERT_GTMIO_CH(&io_ptr->pair, ch_set); } void iott_flush_time(TID id, int4 hd_len, io_desc **io_ptr_parm) { io_desc *io_ptr, *flush_parm; d_tt_struct *tt_ptr; boolean_t flush_immediately; boolean_t ch_set; io_ptr = *io_ptr_parm; ESTABLISH_GTMIO_CH(&io_ptr->pair, ch_set); tt_ptr = io_ptr->dev_sp; assert(tt_ptr->timer_set); if ((FALSE == tt_ptr->write_active) && !prin_out_dev_failure) /* If not in write code and no failure already, do flush */ { tt_ptr->timer_set = FALSE; tt_ptr->write_active = TRUE; iott_flush_buffer(io_ptr, FALSE); } else /* doing write, reschedule the flush */ { /* We cannot be starting unsafe timers during process exiting or in an interrupt-deferred window. */ flush_immediately = (process_exiting || (INTRPT_OK_TO_INTERRUPT != intrpt_ok_state)); assert(!flush_immediately || (FALSE == tt_ptr->write_active)); if (!flush_immediately) { flush_parm = io_ptr; start_timer((TID)io_ptr, IOTT_FLUSH_RETRY, &iott_flush_time, SIZEOF(flush_parm), (char *)&flush_parm); } } REVERT_GTMIO_CH(&io_ptr->pair, ch_set); } fis-gtm-V7.0-005/sr_unix/iott_flush_time.h0000755000032200000250000000122614342376330017414 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef IOTT_FLUSH_TIME_H_INCLUDED #define IOTT_FLUSH_TIME_H_INCLUDED void iott_flush_time(TID id, int4 hd_len, io_desc **io_ptr_parm); #endif fis-gtm-V7.0-005/sr_unix/iott_iocontrol.c0000644000032200000250000000215614342376330017260 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 #include "gtm_inet.h" #include "gtm_string.h" #include "io.h" GBLREF io_pair io_curr_device; void iott_iocontrol(mstr *mn, int4 argcnt, va_list args) { return; } void iott_dlr_device(mstr *d) { io_desc *iod; iod = io_curr_device.in; PUT_DOLLAR_DEVICE_INTO_MSTR(iod, d); return; } void iott_dlr_key(mstr *d) { io_desc *iod; int len; iod = io_curr_device.in; len = STRLEN(iod->dollar.key); /* verify internal buffer has enough space for $KEY string value */ assert((int)d->len > len); memcpy(d->addr, iod->dollar.key, len); d->len = len; return; } fis-gtm-V7.0-005/sr_unix/iott_open.c0000644000032200000250000001500014342376330016201 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_fcntl.h" #include "gtm_string.h" #include "gtm_stdlib.h" #include "gtm_termios.h" #include "io.h" #include "iottdef.h" #include "io_params.h" #include "trmdef.h" #include "gtmio.h" #include "stringpool.h" #include "setterm.h" #include "getcaps.h" #include "gtm_isanlp.h" #include "gtm_conv.h" #include "gtmimagename.h" #include "error.h" #include "op.h" #include "indir_enum.h" GBLREF int COLUMNS, GTM_LINES, AUTO_RIGHT_MARGIN; GBLREF uint4 gtm_principal_editing_defaults; GBLREF io_pair io_std_device; GBLREF boolean_t gtm_utf8_mode; LITREF unsigned char io_params_size[]; error_def(ERR_BADCHSET); error_def(ERR_NOTERMENTRY); error_def(ERR_NOTERMENV); error_def(ERR_NOTERMINFODB); error_def(ERR_TCGETATTR); error_def(ERR_ZINTRECURSEIO); short iott_open(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout) { unsigned char ch; d_tt_struct *tt_ptr; io_desc *ioptr; int status, chset_index; int save_errno; int p_offset; mstr chset_mstr; gtm_chset_t temp_chset, old_ichset, old_ochset; boolean_t empt = FALSE; boolean_t ch_set; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ioptr = dev_name->iod; ESTABLISH_RET_GTMIO_CH(&ioptr->pair, -1, ch_set); if (ioptr->state == dev_never_opened) { dev_name->iod->dev_sp = (void *)malloc(SIZEOF(d_tt_struct) + SIZEOF(struct termios)); memset(dev_name->iod->dev_sp, 0, SIZEOF(d_tt_struct) + SIZEOF(struct termios)); tt_ptr = (d_tt_struct *)dev_name->iod->dev_sp; tt_ptr->ttio_struct = (struct termios *)((char *)tt_ptr + SIZEOF(d_tt_struct)); tt_ptr->in_buf_sz = TTDEF_BUF_SZ; tt_ptr->enbld_outofbands.x = 0; tt_ptr->term_ctrl &= (~TRM_NOECHO); tt_ptr->ttybuff = (char *)malloc(IOTT_BUFF_LEN); tt_ptr->default_mask_term = TRUE; ioptr->ichset = ioptr->ochset = gtm_utf8_mode ? CHSET_UTF8 : CHSET_M; /* default */ } tt_ptr = (d_tt_struct *)dev_name->iod->dev_sp; if (tt_ptr->mupintr) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZINTRECURSEIO); p_offset = 0; old_ichset = ioptr->ichset; old_ochset = ioptr->ochset; while (*(pp->str.addr + p_offset) != iop_eol) { switch (ch = *(pp->str.addr + p_offset++)) { case iop_exception: DEF_EXCEPTION(pp, p_offset, ioptr); break; case iop_canonical: tt_ptr->canonical = TRUE; break; case iop_nocanonical: tt_ptr->canonical = FALSE; break; case iop_empterm: empt = TRUE; break; case iop_noempterm: empt = FALSE; break; case iop_m: ioptr->ichset = ioptr->ochset = CHSET_M; break; case iop_utf8: if (gtm_utf8_mode) ioptr->ichset = ioptr->ochset = CHSET_UTF8; break; case iop_ipchset: case iop_opchset: case iop_chset: if (gtm_utf8_mode) { GET_ADDR_AND_LEN(chset_mstr.addr, chset_mstr.len); SET_ENCODING(temp_chset, &chset_mstr); if (IS_UTF16_CHSET(temp_chset)) /* Not allowed for terminals */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADCHSET, 2, chset_mstr.len, chset_mstr.addr); else if (ch == iop_ipchset) ioptr->ichset = temp_chset; else if (ch == iop_opchset) ioptr->ochset = temp_chset; else if (ch == iop_chset) { ioptr->ichset = temp_chset; ioptr->ochset = temp_chset; } } break; } p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); } if (ioptr->state != dev_open) { int status; char *env_term; assert(fd >= 0); tt_ptr->fildes = fd; status = tcgetattr(tt_ptr->fildes, tt_ptr->ttio_struct); if (0 != status) { save_errno = errno; if (gtm_isanlp(tt_ptr->fildes) == 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_TCGETATTR, 1, tt_ptr->fildes, save_errno); } if (IS_GTM_IMAGE) /* Only the true runtime runs with the modified terminal settings */ setterm(ioptr); status = getcaps(tt_ptr->fildes); if (1 != status) { if (status == 0) { env_term = GETENV("TERM"); if (!env_term) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOTERMENV); env_term = "unknown"; } rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NOTERMENTRY, 2, LEN_AND_STR(env_term)); } else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOTERMINFODB); } ioptr->width = COLUMNS; ioptr->length = GTM_LINES; ioptr->wrap = (0 == AUTO_RIGHT_MARGIN) ? FALSE : TRUE; /* defensive programming; till we are absolutely, positively * certain that there are no uses of wrap == TRUE */ tt_ptr->tbuffp = tt_ptr->ttybuff; /* Buffer is now empty */ tt_ptr->discard_lf = FALSE; if (!io_std_device.in || io_std_device.in == ioptr->pair.in) /* io_std_device.in not set yet in io_init */ { /* $PRINCIPAL */ tt_ptr->ext_cap = gtm_principal_editing_defaults; ioptr->ichset = ioptr->ochset = gtm_utf8_mode ? CHSET_UTF8 : CHSET_M; /* default */ } else tt_ptr->ext_cap = 0; if (empt) tt_ptr->ext_cap |= TT_EMPTERM; /* Set terminal mask on the terminal not open, if default_term or if CHSET changes */ if (tt_ptr->default_mask_term || (old_ichset != ioptr->ichset)) { memset(&tt_ptr->mask_term.mask[0], 0, SIZEOF(io_termmask)); if (CHSET_M != ioptr->ichset) { tt_ptr->mask_term.mask[0] = TERM_MSK_UTF8_0; tt_ptr->mask_term.mask[4] = TERM_MSK_UTF8_4; } else tt_ptr->mask_term.mask[0] = TERM_MSK; tt_ptr->default_mask_term = TRUE; } ioptr->state = dev_open; if ((TT_EDITING & tt_ptr->ext_cap) && !tt_ptr->recall_buff.addr) { assert(tt_ptr->in_buf_sz); tt_ptr->recall_buff.addr = malloc(tt_ptr->in_buf_sz); tt_ptr->recall_size = tt_ptr->in_buf_sz; tt_ptr->recall_buff.len = 0; /* nothing in buffer */ tt_ptr->recall_width = 0; } } else { /* Set terminal mask on the already open terminal, if CHSET changes */ if (old_ichset != ioptr->ichset) { memset(&tt_ptr->mask_term.mask[0], 0, SIZEOF(io_termmask)); if (CHSET_M != ioptr->ichset) { tt_ptr->mask_term.mask[0] = TERM_MSK_UTF8_0; tt_ptr->mask_term.mask[4] = TERM_MSK_UTF8_4; } else tt_ptr->mask_term.mask[0] = TERM_MSK; tt_ptr->default_mask_term = TRUE; } } REVERT_GTMIO_CH(&ioptr->pair, ch_set); return TRUE; } fis-gtm-V7.0-005/sr_unix/iott_rdone.c0000644000032200000250000003622214342376330016360 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include #include #include #include "gtm_string.h" #include "gtm_select.h" #include "gtm_stdlib.h" #include "io.h" #include "iottdef.h" #include "iott_edit.h" #include "trmdef.h" #include "iotimer.h" #include "gt_timer.h" #include "gtmio.h" #include "eintr_wrappers.h" #include "send_msg.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "error.h" #include "std_dev_outbndset.h" #include "wake_alarm.h" #include "svnames.h" #include "op.h" #include "util.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" #include "gtm_utf8.h" #endif GBLREF bool out_of_time; GBLREF boolean_t gtm_utf8_mode, hup_on, prin_in_dev_failure, prin_out_dev_failure; GBLREF int4 exi_condition; GBLREF io_pair io_curr_device, io_std_device; GBLREF mval dollar_zstatus; GBLREF mv_stent *mv_chain; GBLREF stack_frame *frame_pointer; GBLREF unsigned char *msp, *stackbase, *stacktop, *stackwarn; GBLREF volatile boolean_t dollar_zininterrupt; GBLREF volatile int4 outofband; LITREF unsigned char lower_to_upper_table[]; error_def(ERR_IOEOF); error_def(ERR_NOPRINCIO); error_def(ERR_TERMHANGUP); error_def(ERR_ZINTRECURSEIO); int iott_rdone (mint *v, int4 msec_timeout) /* timeout in milliseconds */ { ABS_TIME cur_time, end_time; boolean_t ch_set, first_time, ret = FALSE, timed, utf8_active, zint_restart; char dc1, dc3; d_tt_struct *tt_ptr; fd_set input_fd; int inchar_width, msk_in, msk_num, rdlen, selstat, status, utf8_more; io_desc *io_ptr; mv_stent *mv_zintdev; short int i; struct timeval input_timeval; TID timer_id; tt_interrupt *tt_state; uint4 mask; unsigned char inbyte, *zb_ptr, *zb_top; unsigned char more_buf[GTM_MB_LEN_MAX + 1], *more_ptr; /* to build up multi byte for character */ wint_t inchar; #ifdef __MVS__ wint_t asc_inchar; #endif io_ptr = io_curr_device.in; if (ERR_TERMHANGUP == error_condition) { TERMHUP_NOPRINCIO_CHECK(FALSE); /* FALSE for READ */ io_ptr->dollar.za = ZA_IO_ERR; return FALSE; } ESTABLISH_RET_GTMIO_CH(&io_curr_device, -1, ch_set); assert(io_ptr->state == dev_open); iott_flush(io_curr_device.out); tt_ptr = (d_tt_struct*) io_ptr->dev_sp; timer_id = (TID) iott_rdone; *v = -1; dc1 = (char) 17; dc3 = (char) 19; mask = tt_ptr->term_ctrl; utf8_active = gtm_utf8_mode ? (CHSET_M != io_ptr->ichset) : FALSE; zint_restart = FALSE; if (tt_ptr->mupintr) { /* restore state to before job interrupt */ tt_state = &tt_ptr->tt_state_save; assertpro(ttwhichinvalid != tt_state->who_saved); if (dollar_zininterrupt) { tt_ptr->mupintr = FALSE; tt_state->who_saved = ttwhichinvalid; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZINTRECURSEIO); } assertpro(ttrdone == tt_state->who_saved); /* ZINTRECURSEIO should have caught */ mv_zintdev = io_find_mvstent(io_ptr, FALSE); if (NULL != mv_zintdev) { mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; mv_zintdev->mv_st_cont.mvs_zintdev.io_ptr = NULL; if (mv_chain == mv_zintdev) POP_MV_STENT(); /* pop if top of stack */ end_time = tt_state->end_time; if (utf8_active) { utf8_more = tt_state->utf8_more; more_ptr = tt_state->more_ptr; memcpy(more_buf, tt_state->more_buf, SIZEOF(more_buf)); } zb_ptr = tt_state->zb_ptr; zb_top = tt_state->zb_top; tt_state->who_saved = ttwhichinvalid; tt_ptr->mupintr = FALSE; zint_restart = TRUE; } } if (!zint_restart) { utf8_more = 0; /* --------------------------------------------------------- * zb_ptr is used to fill-in the value of $zb as we go * If we drop-out with error or otherwise permaturely, * consider $zb to be null. * --------------------------------------------------------- */ zb_ptr = io_ptr->dollar.zb; zb_top = zb_ptr + SIZEOF(io_ptr->dollar.zb) - 1; *zb_ptr = 0; io_ptr->esc_state = START; io_ptr->dollar.za = 0; io_ptr->dollar.zeof = FALSE; if (mask & TRM_NOTYPEAHD) TCFLUSH(tt_ptr->fildes, TCIFLUSH, status); if (mask & TRM_READSYNC) { DOWRITERC(tt_ptr->fildes, &dc1, 1, status); if (0 != status) { io_ptr->dollar.za = ZA_IO_ERR; rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status); } } } out_of_time = FALSE; if (NO_M_TIMEOUT == msec_timeout) { timed = FALSE; input_timeval.tv_sec = 100; input_timeval.tv_usec = 0; } else { timed = TRUE; input_timeval.tv_sec = msec_timeout / MILLISECS_IN_SEC; input_timeval.tv_usec = (msec_timeout % MILLISECS_IN_SEC) * MICROSECS_IN_MSEC; if (0 == msec_timeout) { if (!zint_restart) iott_mterm(io_ptr); } else { sys_get_curr_time(&cur_time); if (!zint_restart) add_int_to_abs_time(&cur_time, msec_timeout, &end_time); } } first_time = TRUE; do { if (outofband) { if (jobinterrupt == outofband) { /* save state if jobinterrupt */ tt_state = &tt_ptr->tt_state_save; tt_state->exp_length = 0; tt_state->who_saved = ttrdone; tt_state->end_time = end_time; if (utf8_active) { tt_state->utf8_more = utf8_more; tt_state->more_ptr = more_ptr; memcpy(tt_state->more_buf, more_buf, SIZEOF(more_buf)); } tt_state->zb_ptr = zb_ptr; tt_state->zb_top = zb_top; PUSH_MV_STENT(MVST_ZINTDEV); /* used as a flag only */ mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr; tt_ptr->mupintr = TRUE; } else { if (timed && (0 == msec_timeout)) iott_rterm(io_ptr); } REVERT_GTMIO_CH(&io_curr_device, ch_set); async_action(FALSE); break; } if (!first_time) { if (timed) { if (0 != msec_timeout) { sys_get_curr_time(&cur_time); cur_time = sub_abs_time(&end_time, &cur_time); if (0 > cur_time.at_sec) { ret = FALSE; break; } input_timeval.tv_sec = cur_time.at_sec; input_timeval.tv_usec = (gtm_tv_usec_t)cur_time.at_usec; } } else { /* This is an untimed read. We had set the select timeout to be 100 seconds by default. But since * "select" changes the timeout (in Linux) to account for the elapsed wait time, we need to reset * the timeout to be 100 seconds (else multiple iterations of this for loop could cause it to be * reset to 0 causing a CPU spin loop). */ input_timeval.tv_sec = 100; } } else first_time = FALSE; assertpro(FD_SETSIZE > tt_ptr->fildes); FD_ZERO(&input_fd); FD_SET(tt_ptr->fildes, &input_fd); assert(FD_ISSET(tt_ptr->fildes, &input_fd) != 0); /* the checks for EINTR below are valid and should not be converted to EINTR * wrapper macros, since the select/read is not retried on EINTR. */ selstat = select(tt_ptr->fildes + 1, (void *)&input_fd, (void *)NULL, (void *)NULL, &input_timeval); if (0 > selstat) { if (EINTR != errno) { io_ptr->dollar.za = ZA_IO_ERR; if (timed && (0 == msec_timeout)) iott_rterm(io_ptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); break; } } else if (0 == selstat) { if (timed) { wake_alarm(); /* sets out_of_time to be true for zero as well as non-zero timeouts */ break; } continue; /* select() timeout; try again */ } else if ((rdlen = (int)(read(tt_ptr->fildes, &inbyte, 1))) == 1) /* This read is protected */ { assert(FD_ISSET(tt_ptr->fildes, &input_fd) != 0); /* -------------------------------------------------- * set prin_in_dev_failure to FALSE to indicate that * input device is working now. * -------------------------------------------------- */ prin_in_dev_failure = FALSE; # ifdef UTF8_SUPPORTED if (utf8_active) { if (tt_ptr->discard_lf) { /* saw CR last time so ignore following LF */ tt_ptr->discard_lf = FALSE; if (NATIVE_LF == inbyte) continue; } if (utf8_more) { /* needed extra bytes */ *more_ptr++ = inbyte; if (--utf8_more) continue; /* get next byte */ UTF8_MBTOWC(more_buf, more_ptr, inchar); if (WEOF == inchar) { /* invalid char */ io_ptr->dollar.za = ZA_IO_ERR; /* No data to return */ iott_readfl_badchar(NULL, NULL, 0, (int)((more_ptr - more_buf)), more_buf, more_ptr, NULL); utf8_badchar((int)(more_ptr - more_buf), more_buf, more_ptr, 0, NULL); /* BADCHAR */ break; } } else { more_ptr = more_buf; if (0 < (utf8_more = UTF8_MBFOLLOW(&inbyte))) /* assignment */ { if ((0 > utf8_more) || (GTM_MB_LEN_MAX < utf8_more)) { /* invalid character */ io_ptr->dollar.za = ZA_IO_ERR; *more_ptr++ = inbyte; /* No data to return */ iott_readfl_badchar(NULL, NULL, 0, 1, more_buf, more_ptr, NULL); utf8_badchar(1, more_buf, more_ptr, 0, NULL); /* ERR_BADCHAR */ break; } else { *more_ptr++ = inbyte; continue; /* get next byte */ } } else { /* single byte */ *more_ptr++ = inbyte; UTF8_MBTOWC(more_buf, more_ptr, inchar); if (WEOF == inchar) { /* invalid char */ io_ptr->dollar.za = ZA_IO_ERR; /* No data to return */ iott_readfl_badchar(NULL, NULL, 0, 1, more_buf, more_ptr, NULL); utf8_badchar(1, more_buf, more_ptr, 0, NULL); /* ERR_BADCHAR */ break; } } } if (mask & TRM_CONVERT) inchar = u_toupper(inchar); GTM_IO_WCWIDTH(inchar, inchar_width); } else { # endif if (mask & TRM_CONVERT) NATIVE_CVT2UPPER(inbyte, inbyte); inchar = inbyte; inchar_width = 1; # ifdef UTF8_SUPPORTED } # endif GETASCII(asc_inchar, inchar); if (INPUT_CHAR < ' ' && ((1 << INPUT_CHAR) & tt_ptr->enbld_outofbands.mask)) { std_dev_outbndset(INPUT_CHAR); if (timed) { if (0 == msec_timeout) iott_rterm(io_ptr); } async_action(FALSE); ret = FALSE; break; } else if ( ((mask & TRM_ESCAPE) != 0) && (inchar == NATIVE_ESC || io_ptr->esc_state != START)) { *v = INPUT_CHAR; ret = FALSE; do { if (zb_ptr >= zb_top UTF8_ONLY(|| (utf8_active && ASCII_MAX < inchar))) { /* ------------- * $zb overflow * ------------- */ io_ptr->dollar.za = 2; break; } *zb_ptr++ = (unsigned char)inchar; iott_escape(zb_ptr - 1, zb_ptr, io_ptr); /* RESTORE_DOLLARZB(asc_inchar); */ *(zb_ptr - 1) = (unsigned char)INPUT_CHAR; /* need to store ASCII value */ if (io_ptr->esc_state == FINI) { ret = TRUE; break; } if (io_ptr->esc_state == BADESC) { /* ------------------------------ * Escape sequence failed parse. * ------------------------------ */ io_ptr->dollar.za = 2; break; } DOREADRL(tt_ptr->fildes, &inbyte, 1, rdlen); inchar = inbyte; GETASCII(asc_inchar, inchar); } while (1 == rdlen); *zb_ptr++ = 0; if (rdlen != 1 && io_ptr->dollar.za == 0) io_ptr->dollar.za = ZA_IO_ERR; /* ------------------------------------------------- * End of escape sequence...do not process further. * ------------------------------------------------- */ break; } else { /* may need to deal with terminators > ASCII_MAX and/or LS and PS if default_mask_term */ ret = TRUE; if (!utf8_active || ASCII_MAX >= INPUT_CHAR) { msk_num = (uint4)INPUT_CHAR / NUM_BITS_IN_INT4; msk_in = (1 << ((uint4)INPUT_CHAR % NUM_BITS_IN_INT4)); } else msk_num = msk_in = 0; /* force to not match terminator */ if ((!(msk_in & tt_ptr->mask_term.mask[msk_num])) && (!(mask & TRM_NOECHO))) { status = iott_write_raw(tt_ptr->fildes, utf8_active ? (void *)&inchar : (void *)&inbyte, 1); if (0 >= status) { status = errno; io_ptr->dollar.za = ZA_IO_ERR; if (timed) { if (0 == msec_timeout) iott_rterm(io_ptr); } rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status); } } break; } } else if (rdlen < 0) { if (errno != EINTR) { io_ptr->dollar.za = ZA_IO_ERR; if (timed && (0 == msec_timeout)) iott_rterm(io_ptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); break; } } else /* ------------ * rdlen == 0 * ------------ */ { /* --------------------------------------------------------- * select() says there's something to read, but * read() found zero characters; assume connection dropped. * --------------------------------------------------------- */ ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, FALSE, FALSE); /* FALSE, FALSE: READ, not socket*/ if (io_ptr->dollar.zeof) { io_ptr->dollar.za = ZA_IO_ERR; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_IOEOF); } else { io_ptr->dollar.zeof = TRUE; io_ptr->dollar.za = 0; if (io_ptr->error_handler.len > 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_IOEOF); } break; } } while (!out_of_time); if (timed) { if (0 == msec_timeout) iott_rterm(io_ptr); } if (mask & TRM_READSYNC) { DOWRITERC(tt_ptr->fildes, &dc3, 1, status); if (0 != status) { io_ptr->dollar.za = ZA_IO_ERR; rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status); } } if (outofband && jobinterrupt != outofband) { io_ptr->dollar.za = ZA_IO_ERR; REVERT_GTMIO_CH(&io_curr_device, ch_set); return FALSE; } io_ptr->dollar.za = 0; if (ret && io_ptr->esc_state != FINI) { *v = INPUT_CHAR; if ((TT_EDITING & tt_ptr->ext_cap) && !((TRM_PASTHRU|TRM_NOECHO) & mask)) { /* keep above test in sync with iott_readfl */ assert(tt_ptr->recall_buff.addr); if (!utf8_active) { tt_ptr->recall_buff.addr[0] = INPUT_CHAR; tt_ptr->recall_buff.len = 1; } # ifdef UTF8_SUPPORTED else { memcpy(tt_ptr->recall_buff.addr, &INPUT_CHAR, SIZEOF(INPUT_CHAR)); tt_ptr->recall_buff.len = SIZEOF(INPUT_CHAR); } # endif tt_ptr->recall_width = inchar_width; } /* SIMPLIFY THIS! */ if (!utf8_active || ASCII_MAX >= INPUT_CHAR) { /* may need changes to allow terminator > MAX_ASCII and/or LS and PS if default_mask_term */ msk_num = (uint4)INPUT_CHAR / NUM_BITS_IN_INT4; msk_in = (1 << ((uint4)INPUT_CHAR % NUM_BITS_IN_INT4)); } else msk_num = msk_in = 0; /* force no match to terminator */ if (msk_in & tt_ptr->mask_term.mask[msk_num]) { *zb_ptr++ = INPUT_CHAR; *zb_ptr++ = 0; } else io_ptr->dollar.zb[0] = '\0'; if ((!(msk_in & tt_ptr->mask_term.mask[msk_num])) && (!(mask & TRM_NOECHO))) { if ((io_ptr->dollar.x += inchar_width) >= io_ptr->width && io_ptr->wrap) { ++(io_ptr->dollar.y); if (io_ptr->length) io_ptr->dollar.y %= io_ptr->length; io_ptr->dollar.x %= io_ptr->width; if (io_ptr->dollar.x == 0) DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, STRLEN(NATIVE_TTEOL)); } } } memcpy(io_ptr->dollar.key, io_ptr->dollar.zb, (zb_ptr - io_ptr->dollar.zb)); REVERT_GTMIO_CH(&io_curr_device, ch_set); return ret; } fis-gtm-V7.0-005/sr_unix/iott_read.c0000755000032200000250000000146514342376330016170 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "io.h" #include "iottdef.h" GBLREF io_pair io_curr_device; int iott_read (mval *v, int4 msec_timeout) /* timeout in milliseconds */ { d_tt_struct *tt_ptr; tt_ptr = (d_tt_struct*) (io_curr_device.in->dev_sp); return iott_readfl(v, tt_ptr->in_buf_sz, msec_timeout); } fis-gtm-V7.0-005/sr_unix/iott_readfl.c0000644000032200000250000011242614342376330016507 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include #include #include "gtm_string.h" #include "gtm_select.h" #include "gtm_stdlib.h" #include "io_params.h" #include "io.h" #include "trmdef.h" #include "iotimer.h" #include "gt_timer.h" #include "iottdef.h" #include "iott_edit.h" #include "stringpool.h" #include "gtmio.h" #include "eintr_wrappers.h" #include "send_msg.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "error.h" #include "std_dev_outbndset.h" #include "wake_alarm.h" #include "min_max.h" #include "svnames.h" #include "op.h" #include "util.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" #include "gtm_utf8.h" #endif GBLREF boolean_t gtm_utf8_mode, hup_on, prin_in_dev_failure, prin_out_dev_failure; GBLREF char *KEY_BACKSPACE, *KEY_DC, *KEY_DOWN, *KEY_INSERT, *KEY_LEFT, *KEYPAD_LOCAL, *KEYPAD_XMIT, *KEY_RIGHT, *KEY_UP; GBLREF int AUTO_RIGHT_MARGIN, EAT_NEWLINE_GLITCH; GBLDEF int term_error_line; /* record for cores */ GBLREF int4 exi_condition; GBLREF io_pair io_curr_device, io_std_device; GBLREF mval dollar_zstatus; GBLREF mv_stent *mv_chain; GBLREF spdesc stringpool; GBLREF stack_frame *frame_pointer; GBLREF unsigned char *msp, *stackbase, *stacktop, *stackwarn; GBLREF volatile boolean_t dollar_zininterrupt; GBLREF volatile int4 outofband; LITREF unsigned char lower_to_upper_table[]; #ifdef UTF8_SUPPORTED LITREF UChar32 u32_line_term[]; #endif #ifdef __MVS__ # define SEND_KEYPAD_LOCAL #else # define SEND_KEYPAD_LOCAL \ if (edit_mode && NULL != KEYPAD_LOCAL && (keypad_len = STRLEN(KEYPAD_LOCAL))) /* embedded assignment */ \ DOWRITE(tt_ptr->fildes, KEYPAD_LOCAL, keypad_len); #endif /* dc1 & dc3 have the same value in ASCII and EBCDIC */ static readonly char dc1 = 17; static readonly char dc3 = 19; static readonly unsigned char eraser[3] = { NATIVE_BS, NATIVE_SP, NATIVE_BS }; error_def(ERR_IOEOF); error_def(ERR_NOPRINCIO); error_def(ERR_TERMHANGUP); error_def(ERR_ZINTRECURSEIO); #ifdef UTF8_SUPPORTED /* Maintenance of $ZB on a badchar error and returning partial data (if any) */ void iott_readfl_badchar(mval *vmvalptr, wint_t *dataptr32, int datalen, int delimlen, unsigned char *delimptr, unsigned char *strend, unsigned char *buffer_start) { int i, len, tmplen; unsigned char *delimend, *outptr, *outtop; wint_t *curptr32; io_desc *iod; if (0 < datalen && NULL != dataptr32) { /* Return how much input we got */ if (gtm_utf8_mode) { outptr = buffer_start; outtop = ((unsigned char *)dataptr32); curptr32 = dataptr32; for (i = 0; i < datalen && outptr < outtop; i++, curptr32++) outptr = UTF8_WCTOMB(*curptr32, outptr); vmvalptr->str.len = INTCAST(outptr - buffer_start); } else vmvalptr->str.len = datalen; vmvalptr->str.addr = (char *)buffer_start; if (IS_AT_END_OF_STRINGPOOL(buffer_start, 0)) stringpool.free += vmvalptr->str.len; /* The BADCHAR error after this won't do this for us */ } if (NULL != strend && NULL != delimptr) { /* First find the end of the delimiter (max of 4 bytes) */ if (0 == delimlen) { for (delimend = delimptr; 4 >= delimlen && delimend < strend; ++delimend, ++delimlen) { if (UTF8_VALID(delimend, strend, tmplen)) break; } } if (0 < delimlen) { /* Set $ZB with the failing badchar */ iod = io_curr_device.in; memcpy(iod->dollar.zb, delimptr, MIN(delimlen, ESC_LEN - 1)); iod->dollar.zb[MIN(delimlen, ESC_LEN - 1)] = '\0'; memcpy(iod->dollar.key, delimptr, MIN(delimlen, DD_BUFLEN - 1)); iod->dollar.key[MIN(delimlen, DD_BUFLEN - 1)] = '\0'; } } } #endif int iott_readfl(mval *v, int4 length, int4 msec_timeout) /* timeout in milliseconds */ { ABS_TIME cur_time, end_time; boolean_t buffer_moved, ch_set, edit_mode, empterm, escape_edit, insert_mode, nonzerotimeout, ret, timed, utf8_active, zint_restart; d_tt_struct *tt_ptr; fd_set input_fd; int backspace, delete, down, i, insert_key, ioptr_width, exp_length, keypad_len, left, msk_in, msk_num, rdlen, right, save_errno, selstat, status, up, utf8_more; int delchar_width; /* display width of deleted char */ int delta_width; /* display width change for replaced char */ int dx, dx_start; /* local dollar X, starting value */ int dx_instr, dx_outlen; /* wcwidth of string to insert point, whole string */ int dx_prev, dx_cur, dx_next;/* wcwidth of string to char BEFORE, AT and AFTER the insert point */ int inchar_width; /* display width of inchar */ int instr; /* insert point in input string */ int outlen; /* total characters in line so far */ io_desc *io_ptr; io_terminator outofbands; io_termmask mask_term; mv_stent *mvc, *mv_zintdev; tt_interrupt *tt_state; uint4 mask; unsigned char inbyte, *outptr, *outtop, *zb_ptr, *zb_top; unsigned char more_buf[GTM_MB_LEN_MAX + 1], *more_ptr; /* to build up multi byte for character */ unsigned char *current_ptr; /* insert next character into buffer here */ unsigned char *buffer_start; /* beginning of non UTF8 buffer */ struct timeval input_timeval; struct timeval save_input_timeval; wint_t inchar, *current_32_ptr, *buffer_32_start, switch_char; #ifdef __MVS__ wint_t asc_inchar; #endif assert(stringpool.free >= stringpool.base); assert(stringpool.free <= stringpool.top); io_ptr = io_curr_device.in; if (ERR_TERMHANGUP == error_condition) { TERMHUP_NOPRINCIO_CHECK(FALSE); /* FALSE for READ */ io_ptr->dollar.za = ZA_IO_ERR; return FALSE; } ESTABLISH_RET_GTMIO_CH(&io_curr_device, -1, ch_set); tt_ptr = (d_tt_struct *)(io_ptr->dev_sp); assert(dev_open == io_ptr->state); iott_flush(io_curr_device.out); insert_mode = !(TT_NOINSERT & tt_ptr->ext_cap); /* get initial mode */ empterm = (TT_EMPTERM & tt_ptr->ext_cap); ioptr_width = io_ptr->width; utf8_active = gtm_utf8_mode ? (CHSET_M != io_ptr->ichset) : FALSE; /* if utf8_active, need room for multi byte characters plus wint_t buffer */ exp_length = utf8_active ? (int)(((SIZEOF(wint_t) * length) + (GTM_MB_LEN_MAX * length) + SIZEOF(gtm_int64_t))) : length; zint_restart = FALSE; if (tt_ptr->mupintr) { /* restore state to before job interrupt */ tt_state = &tt_ptr->tt_state_save; assertpro(ttwhichinvalid != tt_state->who_saved); if (dollar_zininterrupt) { tt_ptr->mupintr = FALSE; tt_state->who_saved = ttwhichinvalid; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZINTRECURSEIO); } assert(length == tt_state->length); assertpro(ttread == tt_state->who_saved); /* ZINTRECURSEIO should have caught */ mv_zintdev = io_find_mvstent(io_ptr, FALSE); if (NULL != mv_zintdev && mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid) { /* looks good so use it */ buffer_start = (unsigned char *)mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr; current_ptr = buffer_start; mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; mv_zintdev->mv_st_cont.mvs_zintdev.io_ptr = NULL; if (mv_chain == mv_zintdev) POP_MV_STENT(); /* pop if top of stack */ buffer_moved = (buffer_start != tt_state->buffer_start); if (utf8_active) { /* need to properly align U32 buffer */ assert(exp_length == tt_state->exp_length); buffer_32_start = (wint_t *)ROUND_UP2((INTPTR_T)(buffer_start + (GTM_MB_LEN_MAX * length)), SIZEOF(gtm_int64_t)); if (buffer_moved && (((unsigned char *)buffer_32_start - buffer_start) != ((unsigned char *)tt_state->buffer_32_start - tt_state->buffer_start))) memmove(buffer_32_start, buffer_start + ((unsigned char *)tt_state->buffer_32_start - tt_state->buffer_start), (SIZEOF(wint_t) * length)); current_32_ptr = buffer_32_start; utf8_more = tt_state->utf8_more; more_ptr = tt_state->more_ptr; memcpy(more_buf, tt_state->more_buf, SIZEOF(more_buf)); } instr = tt_state->instr; outlen = tt_state->outlen; dx = tt_state->dx; dx_start = tt_state->dx_start; dx_instr = tt_state->dx_instr; dx_outlen = tt_state->dx_outlen; insert_mode = tt_state->insert_mode; end_time = tt_state->end_time; zb_ptr = tt_state->zb_ptr; zb_top = tt_state->zb_top; tt_state->who_saved = ttwhichinvalid; tt_ptr->mupintr = FALSE; zint_restart = TRUE; } } if (!zint_restart) { ENSURE_STP_FREE_SPACE(exp_length); buffer_start = current_ptr = stringpool.free; if (utf8_active) { buffer_32_start = (wint_t *)ROUND_UP2((INTPTR_T)(stringpool.free + (GTM_MB_LEN_MAX * length)), SIZEOF(gtm_int64_t)); current_32_ptr = buffer_32_start; } instr = outlen = 0; dx_instr = dx_outlen = 0; utf8_more = 0; /* --------------------------------------------------------- * zb_ptr is used to fill-in the value of $zb as we go * If we drop-out with error or otherwise permaturely, * consider $zb to be null. * --------------------------------------------------------- */ zb_ptr = io_ptr->dollar.zb; zb_top = zb_ptr + SIZEOF(io_ptr->dollar.zb) - 1; *zb_ptr = 0; io_ptr->esc_state = START; io_ptr->dollar.za = 0; io_ptr->dollar.zeof = FALSE; dx_start = (int)io_ptr->dollar.x; } v->str.len = 0; ret = TRUE; mask = tt_ptr->term_ctrl; mask_term = tt_ptr->mask_term; /* keep test in next line in sync with test in iott_rdone.c */ edit_mode = (0 != (TT_EDITING & tt_ptr->ext_cap) && !((TRM_NOECHO|TRM_PASTHRU) & mask)); if (!zint_restart) { if (mask & TRM_NOTYPEAHD) TCFLUSH(tt_ptr->fildes, TCIFLUSH, status); if (mask & TRM_READSYNC) { DOWRITERC(tt_ptr->fildes, &dc1, 1, status); if (0 != status) { io_ptr->dollar.za = ZA_IO_ERR; rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status); } } } if (edit_mode) { /* remove ESC and editing control characters from terminator list */ mask_term.mask[ESC / NUM_BITS_IN_INT4] &= ~(1 << ESC); mask_term.mask[EDIT_SOL / NUM_BITS_IN_INT4] &= ~(1 << EDIT_SOL); mask_term.mask[EDIT_EOL / NUM_BITS_IN_INT4] &= ~(1 << EDIT_EOL); mask_term.mask[EDIT_DEOL / NUM_BITS_IN_INT4] &= ~(1 << EDIT_DEOL); mask_term.mask[EDIT_DELETE / NUM_BITS_IN_INT4] &= ~(1 << EDIT_DELETE); mask_term.mask[EDIT_LEFT / NUM_BITS_IN_INT4] &= ~(1 << EDIT_LEFT); mask_term.mask[EDIT_RIGHT / NUM_BITS_IN_INT4] &= ~(1 << EDIT_RIGHT); mask_term.mask[EDIT_ERASE / NUM_BITS_IN_INT4] &= ~(1 << EDIT_ERASE); if (!zint_restart) { /* to turn keypad on if possible */ #ifndef __MVS__ if (NULL != KEYPAD_XMIT && (keypad_len = STRLEN(KEYPAD_XMIT))) /* embedded assignment */ { DOWRITERC(tt_ptr->fildes, KEYPAD_XMIT, keypad_len, status); if (0 != status) { io_ptr->dollar.za = ZA_IO_ERR; rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status); } } #endif dx_start = (dx_start + ioptr_width) % ioptr_width; /* normalize within width */ } } if (!zint_restart) dx = dx_start; nonzerotimeout = FALSE; if (NO_M_TIMEOUT == msec_timeout) { timed = FALSE; input_timeval.tv_sec = 100; input_timeval.tv_usec = 0; } else { timed = TRUE; input_timeval.tv_sec = msec_timeout / MILLISECS_IN_SEC; input_timeval.tv_usec = (msec_timeout % MILLISECS_IN_SEC) * MICROSECS_IN_MSEC; if (!msec_timeout) { if (!zint_restart) iott_mterm(io_ptr); } else { nonzerotimeout = TRUE; sys_get_curr_time(&cur_time); if (!zint_restart) add_int_to_abs_time(&cur_time, msec_timeout, &end_time); } } do { if (outofband) { if (jobinterrupt == outofband) { /* save state if jobinterrupt */ tt_state = &tt_ptr->tt_state_save; tt_state->who_saved = ttread; tt_state->length = length; tt_state->buffer_start = buffer_start; PUSH_MV_STENT(MVST_ZINTDEV); mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)buffer_start; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = exp_length; mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE; mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr; if (utf8_active) { tt_state->exp_length = exp_length; tt_state->buffer_32_start = buffer_32_start; tt_state->utf8_more = utf8_more; tt_state->more_ptr = more_ptr; memcpy(tt_state->more_buf, more_buf, SIZEOF(more_buf)); } if (IS_AT_END_OF_STRINGPOOL(buffer_start, 0)) stringpool.free += exp_length; /* reserve space */ tt_state->instr = instr; tt_state->outlen = outlen; tt_state->dx = dx; tt_state->dx_start = dx_start; tt_state->dx_instr = dx_instr; tt_state->dx_outlen = dx_outlen; tt_state->insert_mode = insert_mode; tt_state->end_time = end_time; tt_state->zb_ptr = zb_ptr; tt_state->zb_top = zb_top; tt_ptr->mupintr = TRUE; REVERT_GTMIO_CH(&io_curr_device, ch_set); } else { instr = outlen = 0; SEND_KEYPAD_LOCAL if (!msec_timeout) iott_rterm(io_ptr); } async_action(FALSE); break; } errno = 0; assertpro(FD_SETSIZE > tt_ptr->fildes); FD_ZERO(&input_fd); FD_SET(tt_ptr->fildes, &input_fd); assert(0 != FD_ISSET(tt_ptr->fildes, &input_fd)); /* the checks for EINTR below are valid and should not be converted to EINTR * wrapper macros, since the select/read is not retried on EINTR. */ save_input_timeval = input_timeval; /* take a copy and pass it because select() below might change it */ selstat = select(tt_ptr->fildes + 1, (void *)&input_fd, (void *)NULL, (void *)NULL, &save_input_timeval); if (selstat < 0) { if (EINTR != errno) { term_error_line = __LINE__; goto term_error; } } else if (0 == selstat) { if (timed) { ret = FALSE; break; } continue; /* select() timeout; keep going */ } else if (0 < (rdlen = (int)(read(tt_ptr->fildes, &inbyte, 1)))) /* This read is protected */ { assert(0 != FD_ISSET(tt_ptr->fildes, &input_fd)); /* set prin_in_dev_failure to FALSE to indicate input device is working now */ prin_in_dev_failure = FALSE; if (tt_ptr->canonical) { if (0 == inbyte) { /* -------------------------------------- * This means that the device has hungup * -------------------------------------- */ io_ptr->dollar.zeof = TRUE; io_ptr->dollar.x = 0; io_ptr->dollar.za = ZA_IO_ERR; io_ptr->dollar.y++; tt_ptr->discard_lf = FALSE; if (io_ptr->error_handler.len > 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_IOEOF); break; } else io_ptr->dollar.zeof = FALSE; } #ifdef UTF8_SUPPORTED if (utf8_active) { if (tt_ptr->discard_lf) { /* saw CR last time so ignore following LF */ tt_ptr->discard_lf = FALSE; if (NATIVE_LF == inbyte) continue; } if (utf8_more) { /* needed extra bytes */ *more_ptr++ = inbyte; if (--utf8_more) continue; /* get next byte */ UTF8_MBTOWC(more_buf, more_ptr, inchar); if (WEOF == inchar) { /* invalid char */ io_ptr->dollar.za = ZA_IO_ERR; iott_readfl_badchar(v, buffer_32_start, outlen, (int)(more_ptr - more_buf), more_buf, more_ptr, buffer_start); utf8_badchar((int)(more_ptr - more_buf), more_buf, more_ptr, 0, NULL); /* BADCHAR */ break; } } else if (0 < (utf8_more = UTF8_MBFOLLOW(&inbyte))) /* assignment */ { more_ptr = more_buf; if (0 > utf8_more) { /* invalid character */ io_ptr->dollar.za = ZA_IO_ERR; *more_ptr++ = inbyte; iott_readfl_badchar(v, buffer_32_start, outlen, 1, more_buf, more_ptr, buffer_start); utf8_badchar(1, more_buf, more_ptr, 0, NULL); /* ERR_BADCHAR */ break; } else if (GTM_MB_LEN_MAX < utf8_more) { /* too big to be valid */ io_ptr->dollar.za = ZA_IO_ERR; *more_ptr++ = inbyte; iott_readfl_badchar(v, buffer_32_start, outlen, 1, more_buf, more_ptr, buffer_start); utf8_badchar(1, more_buf, more_ptr, 0, NULL); /* ERR_BADCHAR */ break; } else { *more_ptr++ = inbyte; continue; /* get next byte */ } } else { /* single byte */ more_ptr = more_buf; *more_ptr++ = inbyte; UTF8_MBTOWC(more_buf, more_ptr, inchar); if (WEOF == inchar) { /* invalid char */ io_ptr->dollar.za = ZA_IO_ERR; iott_readfl_badchar(v, buffer_32_start, outlen, 1, more_buf, more_ptr, buffer_start); utf8_badchar(1, more_buf, more_ptr, 0, NULL); /* ERR_BADCHAR */ break; } } if (!tt_ptr->done_1st_read) { tt_ptr->done_1st_read = TRUE; if (BOM_CODEPOINT == inchar) continue; } if (mask & TRM_CONVERT) inchar = u_toupper(inchar); GTM_IO_WCWIDTH(inchar, inchar_width); } else { #endif if (mask & TRM_CONVERT) NATIVE_CVT2UPPER(inbyte, inbyte); inchar = inbyte; inchar_width = 1; #ifdef UTF8_SUPPORTED } #endif GETASCII(asc_inchar,inchar); if (!edit_mode && (dx >= ioptr_width) && io_ptr->wrap && !(mask & TRM_NOECHO)) { DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, STRLEN(NATIVE_TTEOL)); dx = 0; } if ((' ' > INPUT_CHAR) && (tt_ptr->enbld_outofbands.mask & (1 << INPUT_CHAR))) { /* ctrap supercedes editing so check first */ instr = outlen = 0; io_ptr->dollar.za = ZA_IO_ERR; std_dev_outbndset(INPUT_CHAR); /* it needs ASCII? */ SEND_KEYPAD_LOCAL if (!msec_timeout) iott_rterm(io_ptr); outofband = ctrap; async_action(FALSE); break; } if (((0 != (mask & TRM_ESCAPE)) || edit_mode) && ((NATIVE_ESC == inchar) || (START != io_ptr->esc_state))) { if (zb_ptr >= zb_top UTF8_ONLY(|| (utf8_active && ASCII_MAX < inchar))) { /* $zb overflow or not ASCII in utf8 mode */ io_ptr->dollar.za = 2; break; } *zb_ptr++ = (unsigned char)inchar; iott_escape(zb_ptr - 1, zb_ptr, io_ptr); *(zb_ptr - 1) = (unsigned char)INPUT_CHAR; /* need to store ASCII value */ if (FINI == io_ptr->esc_state && !edit_mode) break; if (BADESC == io_ptr->esc_state) { /* Escape sequence failed parse */ io_ptr->dollar.za = 2; break; } /* -------------------------------------------------------------------- * In escape sequence...do not process further, but get next character * -------------------------------------------------------------------- */ } else { /* SIMPLIFY THIS! */ if (!utf8_active || ASCII_MAX >= INPUT_CHAR) { /* may need changes to allow terminator > MAX_ASCII and/or LS and PS if default_mask_term */ msk_num = (uint4)INPUT_CHAR / NUM_BITS_IN_INT4; msk_in = (1 << ((uint4)INPUT_CHAR % NUM_BITS_IN_INT4)); if (msk_in & mask_term.mask[msk_num]) { *zb_ptr++ = (unsigned char)INPUT_CHAR; if (utf8_active && ASCII_CR == INPUT_CHAR) tt_ptr->discard_lf = TRUE; break; } } else if (utf8_active && tt_ptr->default_mask_term && (u32_line_term[U32_LT_NL] == INPUT_CHAR || u32_line_term[U32_LT_LS] == INPUT_CHAR || u32_line_term[U32_LT_PS] == INPUT_CHAR)) { /* UTF and default terminators and UTF terminators above ASCII_MAX */ zb_ptr = UTF8_WCTOMB(INPUT_CHAR, zb_ptr); break; } assert(0 <= instr); assert(!edit_mode || 0 <= dx); assert(outlen >= instr); /* For most of the terminal the 'kbs' string capability is a byte in length. It means that it is Not treated as escape sequence. So explicitly check if the input corresponds to the 'kbs' */ if ((((int)inchar == tt_ptr->ttio_struct->c_cc[VERASE]) || (empterm && (NULL != KEY_BACKSPACE) && ('\0' == KEY_BACKSPACE[1]) && (inchar == KEY_BACKSPACE[0]))) && !(mask & TRM_PASTHRU)) { if (0 < instr && (edit_mode || 0 < dx)) { dx_prev = compute_dx(BUFF_ADDR(0), instr - 1, ioptr_width, dx_start); delchar_width = dx_instr - dx_prev; if (edit_mode) { if (!(mask & TRM_NOECHO)) move_cursor_left(dx, delchar_width); dx = (dx - delchar_width + ioptr_width) % ioptr_width; } else dx -= delchar_width; instr--; dx_instr -= delchar_width; STORE_OFF(' ', outlen); outlen--; if (!(mask & TRM_NOECHO) && edit_mode) { IOTT_COMBINED_CHAR_CHECK; } MOVE_BUFF(instr, BUFF_ADDR(instr + 1), outlen - instr); if (!(mask & TRM_NOECHO)) { if (!edit_mode) { for (i = 0; i < delchar_width; i++) { DOWRITERC(tt_ptr->fildes, eraser, SIZEOF(eraser), status); if (0 != status) break; } } else { /* First write spaces on all the display columns that * the current string occupied. Then overwrite that * with the new string. This way we are guaranteed all * display columns are clean. */ status = write_str_spaces(dx_outlen - dx_instr, dx, FALSE); if (0 == status) status = write_str(BUFF_ADDR(instr), outlen - instr, dx, FALSE, FALSE); } if (0 != status) { term_error_line = __LINE__; goto term_error; } } dx_outlen = compute_dx(BUFF_ADDR(0), outlen, ioptr_width, dx_start); } else if (empterm && (0 == outlen)) { assert(zb_ptr == io_ptr->dollar.zb); *zb_ptr++ = (unsigned char)INPUT_CHAR; break; } } else { if (!edit_mode) switch_char = 'A'; /* force to default case */ else switch_char = inchar; switch (switch_char) { case EDIT_SOL: /* ctrl A start of line */ { int num_lines_above; int num_chars_left; num_lines_above = (dx_instr + dx_start) / ioptr_width; num_chars_left = dx - dx_start; if (!(mask & TRM_NOECHO)) { if (0 != move_cursor(tt_ptr->fildes, num_lines_above, num_chars_left)) { term_error_line = __LINE__; goto term_error; } } instr = dx_instr = 0; dx = dx_start; break; } case EDIT_EOL: /* ctrl E end of line */ { int num_lines_above; int num_chars_left; num_lines_above = (dx_instr + dx_start) / ioptr_width - (dx_outlen + dx_start) / ioptr_width; /* For some reason, a CURSOR_DOWN ("\n") seems to reposition the cursor * at the beginning of the next line rather than maintain the vertical * position. Therefore if we are moving down, we need to calculate * the num_chars_left differently to accommodate this. */ if (0 <= num_lines_above) num_chars_left = dx - (dx_outlen + dx_start) % ioptr_width; else num_chars_left = - ((dx_outlen + dx_start) % ioptr_width); if (!(mask & TRM_NOECHO)) { if (0 != move_cursor(tt_ptr->fildes, num_lines_above, num_chars_left)) { term_error_line = __LINE__; goto term_error; } } instr = outlen; dx_instr = dx_outlen; dx = (dx_outlen + dx_start) % ioptr_width; break; } case EDIT_LEFT: /* ctrl B left one */ { if (instr != 0) { dx_prev = compute_dx(BUFF_ADDR(0), instr - 1, ioptr_width, dx_start); inchar_width = dx_instr - dx_prev; if (!(mask & TRM_NOECHO)) { if (0 != move_cursor_left(dx, inchar_width)) { term_error_line = __LINE__; goto term_error; } } instr--; dx = (dx - inchar_width + ioptr_width) % ioptr_width; dx_instr -= inchar_width; } break; } case EDIT_RIGHT: /* ctrl F right one */ { if (instr < outlen) { dx_next = compute_dx(BUFF_ADDR(0), instr + 1, ioptr_width, dx_start); inchar_width = dx_next - dx_instr; if (!(mask & TRM_NOECHO)) { if (0 != move_cursor_right(dx, inchar_width)) { term_error_line = __LINE__; goto term_error; } } instr++; dx = (dx + inchar_width) % ioptr_width; dx_instr += inchar_width; } break; } case EDIT_DEOL: /* ctrl K delete to end of line */ { if (!(mask & TRM_NOECHO)) { if (0 != write_str_spaces(dx_outlen - dx_instr, dx, FALSE)) { term_error_line = __LINE__; goto term_error; } } SET_BUFF(instr, ' ', outlen - instr); outlen = instr; dx_outlen = dx_instr; break; } case EDIT_ERASE: /* ctrl U delete whole line */ { int num_lines_above; int num_chars_left; num_lines_above = (dx_instr + dx_start) / ioptr_width; num_chars_left = dx - dx_start; SET_BUFF(0, ' ', outlen); if (!(mask & TRM_NOECHO)) { status = move_cursor(tt_ptr->fildes, num_lines_above, num_chars_left); if (0 != status || 0 != write_str_spaces(dx_outlen, dx_start, FALSE)) { term_error_line = __LINE__; goto term_error; } } instr = 0; outlen = 0; dx = dx_start; dx_instr = dx_outlen = 0; break; } case EDIT_DELETE: /* ctrl D delete char */ { if (instr < outlen) { STORE_OFF(' ', outlen); outlen--; if (!(mask & TRM_NOECHO)) { IOTT_COMBINED_CHAR_CHECK; } MOVE_BUFF(instr, BUFF_ADDR(instr + 1), outlen - instr); if (!(mask & TRM_NOECHO)) { /* First write spaces on all the display columns that * the current string occupied. Then overwrite that * with the new string. This way we are guaranteed all * display columns are clean. */ if (0 != write_str_spaces(dx_outlen - dx_instr, dx, FALSE)) { term_error_line = __LINE__; goto term_error; } if (0 != write_str(BUFF_ADDR(instr), outlen - instr, dx, FALSE, FALSE)) { term_error_line = __LINE__; goto term_error; } } dx_outlen = compute_dx(BUFF_ADDR(0), outlen, ioptr_width, dx_start); } break; } default: { if (outlen > instr) { if (insert_mode) MOVE_BUFF(instr + 1, BUFF_ADDR(instr), outlen - instr) else if (edit_mode && !insert_mode) { /* only needed if edit && !insert_mode && not at end */ GTM_IO_WCWIDTH(GET_OFF(instr), delchar_width); delta_width = inchar_width - delchar_width; } } STORE_OFF(inchar, instr); if (!(mask & TRM_NOECHO)) { if (!edit_mode) { term_error_line = __LINE__; status = iott_write_raw(tt_ptr->fildes, BUFF_ADDR(instr), 1); if (0 <= status) status = 0; else status = errno; } else if (instr == outlen) { term_error_line = __LINE__; status = write_str(BUFF_ADDR(instr), 1, dx, FALSE, FALSE); } else { /* First write spaces on all the display columns that the * current string occupied. Then overwrite that with the * new string. This way we are guaranteed all display * columns are clean. Note that this space overwrite is * needed even in case of insert mode because due to * differing wide-character alignments before and after * the insert, it is possible that a column might be left * empty in the post insert write of the new string even * though it had something displayed before. */ term_error_line = __LINE__; status = write_str_spaces(dx_outlen - dx_instr, dx, FALSE); if (0 == status) { term_error_line = __LINE__; status = write_str(BUFF_ADDR(instr), outlen - instr + (insert_mode ? 1 : 0), dx, FALSE, FALSE); } } if (0 != status) goto term_error; } if (insert_mode || instr == outlen) outlen++; instr++; if (edit_mode) { /* Compute value of dollarx at the new cursor position */ dx_cur = compute_dx(BUFF_ADDR(0), instr, ioptr_width, dx_start); inchar_width = dx_cur - dx_instr; if (!(mask & TRM_NOECHO)) { term_error_line = __LINE__; status = move_cursor_right(dx, inchar_width); if (0 != status) goto term_error; } } if (!edit_mode) { dx += inchar_width; dx_instr += inchar_width; dx_outlen += inchar_width; } else if (insert_mode || instr >= outlen) { /* at end or insert */ dx = (dx + inchar_width) % ioptr_width; dx_instr = dx_cur; dx_outlen = compute_dx(BUFF_ADDR(0), outlen, ioptr_width, dx_start); } else { /* replaced character */ dx = (dx + inchar_width) % ioptr_width; dx_instr = dx_cur; dx_outlen = compute_dx(BUFF_ADDR(0), outlen, ioptr_width, dx_start); } break; } } } } /* Ensure that the actual display position of the current character matches the computed value */ assert(!edit_mode || dx_instr == compute_dx(BUFF_ADDR(0), instr, ioptr_width, dx_start)); assert(!edit_mode || dx_outlen == compute_dx(BUFF_ADDR(0), outlen, ioptr_width, dx_start)); } else if (0 == rdlen) { if (0 < selstat) { /* this should be the only possibility */ io_ptr->dollar.zeof = TRUE; io_ptr->dollar.x = 0; io_ptr->dollar.za = 0; io_ptr->dollar.y++; ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, FALSE, FALSE); /* FALSE, FALSE: READ, not socket*/ if (io_ptr->dollar.zeof) { io_ptr->dollar.za = ZA_IO_ERR; SEND_KEYPAD_LOCAL RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_IOEOF); } else { io_ptr->dollar.zeof = TRUE; io_ptr->dollar.za = 0; SEND_KEYPAD_LOCAL if (0 < io_ptr->error_handler.len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_IOEOF); } break; } if (0 == errno) { /* eof */ io_ptr->dollar.zeof = TRUE; io_ptr->dollar.x = 0; io_ptr->dollar.za = 0; io_ptr->dollar.y++; SEND_KEYPAD_LOCAL if (0 < io_ptr->error_handler.len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_IOEOF); break; } } else if (EINTR != errno) /* rdlen < 0 */ { term_error_line = __LINE__; goto term_error; } if (FINI == io_ptr->esc_state) { int zb_len = (int)(zb_ptr - io_ptr->dollar.zb); escape_edit = FALSE; /* The arbitrary value -1 signifies inequality in case KEY_* is NULL */ down = (NULL != KEY_DOWN) ? strncmp((const char *)io_ptr->dollar.zb, KEY_DOWN, zb_len) : -1; up = (NULL != KEY_UP) ? strncmp((const char *)io_ptr->dollar.zb, KEY_UP, zb_len) : -1; right = (NULL != KEY_RIGHT) ? strncmp((const char *)io_ptr->dollar.zb, KEY_RIGHT, zb_len) : -1; left = (NULL != KEY_LEFT) ? strncmp((const char *)io_ptr->dollar.zb, KEY_LEFT, zb_len) : -1; backspace = (KEY_BACKSPACE != NULL) ? strncmp((const char *)io_ptr->dollar.zb, KEY_BACKSPACE, zb_len) : -1; delete = (KEY_DC != NULL) ? strncmp((const char *)io_ptr->dollar.zb, KEY_DC, zb_len) : -1; insert_key = (KEY_INSERT != NULL && '\0' != KEY_INSERT[0]) ? strncmp((const char *)io_ptr->dollar.zb, KEY_INSERT, zb_len) : -1; if (0 == backspace || 0 == delete) { if ((0 == backspace) && (instr > 0)) { /* Move one character to the left */ dx_prev = compute_dx(BUFF_ADDR(0), instr - 1, ioptr_width, dx_start); delchar_width = dx_instr - dx_prev; if (!(mask & TRM_NOECHO)) { term_error_line = __LINE__; status = move_cursor_left(dx, delchar_width); } dx = (dx - delchar_width + ioptr_width) % ioptr_width; instr--; dx_instr -= delchar_width; } if (instr != outlen) { STORE_OFF(' ', outlen); outlen--; if (!(mask & TRM_NOECHO)) { IOTT_COMBINED_CHAR_CHECK; } MOVE_BUFF(instr, BUFF_ADDR(instr + 1), outlen - instr); if (!(mask & TRM_NOECHO)) { /* First write spaces on all the display columns that the current string occupied. * Then overwrite that with the new string. This way we are guaranteed all * display columns are clean. */ if (0 == status) { term_error_line = __LINE__; status = write_str_spaces(dx_outlen - dx_instr, dx, FALSE); } if (0 == status) { term_error_line = __LINE__; status = write_str(BUFF_ADDR(instr), outlen - instr, dx, FALSE, FALSE); } if (0 != status) goto term_error; } dx_outlen = compute_dx(BUFF_ADDR(0), outlen, ioptr_width, dx_start); } else if (empterm && 0 == outlen) { assert(instr == 0); break; } escape_edit = TRUE; } if (up == 0 || down == 0) { /* move cursor to start of field */ if (0 < instr) { if (0 != move_cursor(tt_ptr->fildes, ((dx_instr + dx_start) / ioptr_width), (dx - dx_start))) { term_error_line = __LINE__; goto term_error; } } instr = (int)tt_ptr->recall_buff.len; if (length < instr) instr = length; /* restrict to length of read */ if (0 != instr) { /* need to blank old output first */ SET_BUFF(instr, ' ', outlen); if (0 != write_str_spaces(dx_outlen, dx_start, FALSE)) { term_error_line = __LINE__; goto term_error; } MOVE_BUFF(0, tt_ptr->recall_buff.addr, instr); if (0 != write_str(BUFF_ADDR(0), instr, dx_start, TRUE, FALSE)) { term_error_line = __LINE__; goto term_error; } } dx_instr = dx_outlen = tt_ptr->recall_width; dx = (unsigned)(dx_instr + dx_start) % ioptr_width; outlen = instr; escape_edit = TRUE; } else if ( !(mask & TRM_NOECHO) && !(right == 0 && instr == outlen) && !(left == 0 && instr == 0)) { if (right == 0) { dx_next = compute_dx(BUFF_ADDR(0), instr + 1, ioptr_width, dx_start); inchar_width = dx_next - dx_instr; if (0 != move_cursor_right(dx, inchar_width)) { term_error_line = __LINE__; goto term_error; } instr++; dx = (dx + inchar_width) % ioptr_width; dx_instr += inchar_width; } if (left == 0) { dx_prev = compute_dx(BUFF_ADDR(0), instr - 1, ioptr_width, dx_start); inchar_width = dx_instr - dx_prev; if (0 != move_cursor_left(dx, inchar_width)) { term_error_line = __LINE__; goto term_error; } instr--; dx = (dx - inchar_width + ioptr_width) % ioptr_width; dx_instr -= inchar_width; } } if (0 == insert_key) insert_mode = !insert_mode; /* toggle */ if (0 == right || 0 == left || 0 == insert_key) escape_edit = TRUE; if (escape_edit || (0 == (TRM_ESCAPE & mask))) { /* reset dollar zb if editing function or not trm_escape */ memset(io_ptr->dollar.zb, '\0', SIZEOF(io_ptr->dollar.zb)); io_ptr->esc_state = START; zb_ptr = io_ptr->dollar.zb; zb_top = zb_ptr + SIZEOF(io_ptr->dollar.zb) - 1; } else break; /* not edit function and TRM_ESCAPE */ } if (nonzerotimeout) { sys_get_curr_time(&cur_time); cur_time = sub_abs_time(&end_time, &cur_time); if (0 > cur_time.at_sec) { ret = FALSE; break; } input_timeval.tv_sec = cur_time.at_sec; input_timeval.tv_usec = (gtm_tv_usec_t)cur_time.at_usec; } } while (outlen < length); *zb_ptr++ = 0; memcpy(io_ptr->dollar.key, io_ptr->dollar.zb, (zb_ptr - io_ptr->dollar.zb)); if (!msec_timeout) { iott_rterm(io_ptr); if (0 == outlen && ((io_ptr->dollar.zb + 1) == zb_ptr)) /* No input and no delimiter seen */ ret = FALSE; } if (mask & TRM_READSYNC) { DOWRITERC(tt_ptr->fildes, &dc3, 1, status); if (0 != status) { io_ptr->dollar.za = ZA_IO_ERR; rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status); } } SEND_KEYPAD_LOCAL /* to turn keypad off if possible */ if (outofband && (jobinterrupt != outofband)) { v->str.len = 0; io_ptr->dollar.za = ZA_IO_ERR; REVERT_GTMIO_CH(&io_curr_device, ch_set); return(FALSE); } #ifdef UTF8_SUPPORTED if (utf8_active) { outptr = buffer_start; outtop = ((unsigned char *)buffer_32_start); current_32_ptr = buffer_32_start; for (i = 0; i < outlen && outptr < outtop; i++, current_32_ptr++) outptr = UTF8_WCTOMB(*current_32_ptr, outptr); v->str.len = INTCAST(outptr - buffer_start); } else #endif v->str.len = outlen; v->str.addr = (char *)buffer_start; if (edit_mode) { /* store in recall buffer */ if ((BUFF_CHAR_SIZE * outlen) > tt_ptr->recall_size) { if (tt_ptr->recall_buff.addr) free(tt_ptr->recall_buff.addr); tt_ptr->recall_size = (int)(BUFF_CHAR_SIZE * outlen); tt_ptr->recall_buff.addr = malloc(tt_ptr->recall_size); } tt_ptr->recall_width = dx_outlen; tt_ptr->recall_buff.len = outlen; memcpy(tt_ptr->recall_buff.addr, BUFF_ADDR(0), BUFF_CHAR_SIZE * outlen); } if (!(mask & TRM_NOECHO)) { if ((io_ptr->dollar.x += dx_outlen) >= ioptr_width && io_ptr->wrap) { io_ptr->dollar.y += (io_ptr->dollar.x / ioptr_width); if (io_ptr->length) io_ptr->dollar.y %= io_ptr->length; io_ptr->dollar.x %= ioptr_width; if (0 == io_ptr->dollar.x) DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, STRLEN(NATIVE_TTEOL)); } } REVERT_GTMIO_CH(&io_curr_device, ch_set); return ((short)ret); term_error: save_errno = errno; io_ptr->dollar.za = ZA_IO_ERR; tt_ptr->discard_lf = FALSE; SEND_KEYPAD_LOCAL /* to turn keypad off if possible */ if (!msec_timeout) iott_rterm(io_ptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); return FALSE; } fis-gtm-V7.0-005/sr_unix/iott_use.c0000644000032200000250000003733314342376330016051 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include #include "gtm_fcntl.h" #include "gtm_string.h" #include "gtm_iconv.h" #include "gtm_termios.h" #include "gtm_unistd.h" #include "gtm_signal.h" /* for SIGPROCMASK used inside Tcsetattr */ #include "gtm_stdlib.h" #include "io_params.h" #include "io.h" #include "iottdef.h" #include "iosp.h" #include "trmdef.h" #include "nametabtyp.h" #include "copy.h" #include "gtmio.h" #include "eintr_wrappers.h" #include "stringpool.h" #include "send_msg.h" #include "namelook.h" #include "gtm_conv.h" #include "error.h" #include "gtm_tputs.h" #include "gtm_tparm.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "restrict.h" #include "op.h" #include "indir_enum.h" #include "svnames.h" #include "util.h" LITDEF nametabent filter_names[] = { {4, "CHAR*"}, {3, "ESC*"}, {6, "NOCHAR*"}, {5, "NOESC*"} }; LITDEF unsigned char filter_index[27] = { 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2 ,2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4 ,4, 4, 4 }; GBLREF boolean_t ctrlc_on, hup_on, prin_in_dev_failure, prin_out_dev_failure; GBLREF char *CURSOR_ADDRESS, *CLR_EOL, *CLR_EOS; GBLREF io_pair io_curr_device, io_std_device; GBLREF mval dollar_zstatus; GBLREF void (*ctrlc_handler_ptr)(); GBLREF volatile boolean_t dollar_zininterrupt; LITREF unsigned char io_params_size[]; error_def(ERR_DEVPARMNEG); error_def(ERR_NOPRINCIO); error_def(ERR_SYSCALL); error_def(ERR_TCGETATTR); error_def(ERR_TCSETATTR); error_def(ERR_TTINVFILTER); error_def(ERR_WIDTHTOOSMALL); error_def(ERR_ZINTRECURSEIO); void iott_use(io_desc *iod, mval *pp) { boolean_t flush_input, terminator_specified = FALSE; char dc1, *ttab; d_tt_struct *temp_ptr, *tt_ptr; int p_offset, fil_type, save_errno, status; int4 length, width; io_desc *d_in, *d_out; io_termmask mask_term; struct sigaction act; struct termios t; mstr chset_mstr; gtm_chset_t temp_chset, old_ochset, old_ichset; uint4 mask_in; unsigned char ch, len; boolean_t ch_set; mval mv; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; p_offset = 0; assert(iod->state == dev_open); ESTABLISH_GTMIO_CH(&iod->pair, ch_set); iott_flush(iod); tt_ptr = (d_tt_struct *)iod->dev_sp; if (*(pp->str.addr + p_offset) != iop_eol) { if (tt_ptr->mupintr) if (dollar_zininterrupt) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZINTRECURSEIO); else { /* The interrupted read was not properly resumed so clear it now */ tt_ptr->mupintr = FALSE; tt_ptr->tt_state_save.who_saved = ttwhichinvalid; io_find_mvstent(iod, TRUE); } status = tcgetattr(tt_ptr->fildes, &t); if (0 != status) { save_errno = errno; ISSUE_NOPRINCIO_IF_NEEDED(io_curr_device.out, FALSE, FALSE); /* FALSE, FALSE: READ (sorta), not socket */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_TCGETATTR, 1, tt_ptr->fildes, save_errno); } flush_input = FALSE; d_in = iod->pair.in; d_out = iod->pair.out; temp_ptr = (d_tt_struct *)d_in->dev_sp; mask_in = temp_ptr->term_ctrl; mask_term = temp_ptr->mask_term; old_ochset = iod->ochset; old_ichset = iod->ichset; while (*(pp->str.addr + p_offset) != iop_eol) { switch (ch = *(pp->str.addr + p_offset++)) { case iop_canonical: tt_ptr->canonical = TRUE; t.c_lflag |= ICANON; break; case iop_nocanonical: tt_ptr->canonical = FALSE; t.c_lflag &= ~(ICANON); break; case iop_empterm: tt_ptr->ext_cap |= TT_EMPTERM; break; case iop_noempterm: tt_ptr->ext_cap &= ~TT_EMPTERM; break; case iop_cenable: if (!ctrlc_on && !RESTRICTED(cenable)) { /* if it's already cenable, no need to change */ temp_ptr = (d_tt_struct *)io_std_device.in->dev_sp; if (tt_ptr->fildes == temp_ptr->fildes) { /* if this is $PRINCIPAL make sure the ctrlc_handler is enabled */ sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = ctrlc_handler_ptr; sigaction(SIGINT, &act, 0); ctrlc_on = TRUE; } } break; case iop_nocenable: if (ctrlc_on && !RESTRICTED(cenable)) { /* if it's already nocenable, no need to change */ temp_ptr = (d_tt_struct *)io_std_device.in->dev_sp; if (tt_ptr->fildes == temp_ptr->fildes) { /* if this is $PRINCIPAL may disable the ctrlc_handler */ if (0 == (CTRLC_MSK & tt_ptr->enbld_outofbands.mask)) { /* but only if ctrap=$c(3) is not active */ sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = SIG_IGN; sigaction(SIGINT, &act, 0); } ctrlc_on = FALSE; } } break; case iop_clearscreen: if (NULL != CLR_EOS) gtm_tputs(CLR_EOS, 1, outc); break; case iop_convert: mask_in |= TRM_CONVERT; break; case iop_noconvert: mask_in &= ~TRM_CONVERT; break; case iop_ctrap: GET_LONG(tt_ptr->enbld_outofbands.mask, pp->str.addr + p_offset); if (!ctrlc_on) { /* if cenable, ctrlc_handler active anyway, otherwise, depends on ctrap=$c(3) */ sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = (CTRLC_MSK & tt_ptr->enbld_outofbands.mask) ? ctrlc_handler_ptr : SIG_IGN; sigaction(SIGINT, &act, 0); } break; case iop_downscroll: if (d_out->dollar.y > 0) { d_out->dollar.y--; if (NULL != CURSOR_ADDRESS) gtm_tputs(gtm_tparm(CURSOR_ADDRESS, d_out->dollar.y, d_out->dollar.x), 1, outc); } break; case iop_echo: mask_in &= (~TRM_NOECHO); break; case iop_noecho: mask_in |= TRM_NOECHO; break; case iop_editing: if (io_curr_device.in == io_std_device.in) { /* $PRINCIPAL only */ tt_ptr->ext_cap |= TT_EDITING; if (!tt_ptr->recall_buff.addr) { assert(tt_ptr->in_buf_sz); tt_ptr->recall_buff.addr = malloc(tt_ptr->in_buf_sz); tt_ptr->recall_size = tt_ptr->in_buf_sz; tt_ptr->recall_buff.len = 0; /* nothing in buffer */ } } break; case iop_noediting: if (io_curr_device.in == io_std_device.in) tt_ptr->ext_cap &= ~TT_EDITING; /* $PRINCIPAL only */ break; case iop_escape: mask_in |= TRM_ESCAPE; break; case iop_noescape: mask_in &= (~TRM_ESCAPE); default: break; case iop_eraseline: if (NULL != CLR_EOL) gtm_tputs(CLR_EOL, 1, outc); break; case iop_exception: DEF_EXCEPTION(pp, p_offset, iod); break; case iop_filter: len = *(pp->str.addr + p_offset); ttab = pp->str.addr + p_offset + 1; if ((fil_type = namelook(filter_index, filter_names, ttab, len)) < 0) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_TTINVFILTER); return; } switch (fil_type) { case 0: iod->write_filter |= CHAR_FILTER; break; case 1: iod->write_filter |= ESC1; break; case 2: iod->write_filter &= ~CHAR_FILTER; break; case 3: iod->write_filter &= ~ESC1; break; } break; case iop_nofilter: iod->write_filter = 0; break; case iop_flush: flush_input = TRUE; break; case iop_hostsync: t.c_iflag |= IXOFF; break; case iop_nohostsync: t.c_iflag &= ~IXOFF; break; case iop_hupenable: if (!hup_on) { /* if it's already hupenable, no need to change */ temp_ptr = (d_tt_struct *)io_std_device.in->dev_sp; if (tt_ptr->fildes == temp_ptr->fildes) { /* if $PRINCIPAL, enable hup_handler; similar code in term_setup.c */ sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = ctrlc_handler_ptr; sigaction(SIGHUP, &act, 0); hup_on = TRUE; } } break; case iop_nohupenable: if (hup_on) { /* if it's already nohupenable, no need to change */ temp_ptr = (d_tt_struct *)io_std_device.in->dev_sp; if (tt_ptr->fildes == temp_ptr->fildes) { /* if $PRINCIPAL, disable the hup_handler */ sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = SIG_IGN; sigaction(SIGHUP, &act, 0); hup_on = FALSE; } } break; case iop_insert: if (io_curr_device.in == io_std_device.in) tt_ptr->ext_cap &= ~TT_NOINSERT; /* $PRINCIPAL only */ break; case iop_noinsert: if (io_curr_device.in == io_std_device.in) tt_ptr->ext_cap |= TT_NOINSERT; /* $PRINCIPAL only */ break; case iop_length: GET_LONG(length, pp->str.addr + p_offset); if (0 > length) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DEVPARMNEG); d_out->length = length; break; case iop_pasthru: mask_in |= TRM_PASTHRU; break; case iop_nopasthru: mask_in &= (~TRM_PASTHRU); break; case iop_readsync: mask_in |= TRM_READSYNC; break; case iop_noreadsync: dc1 = (char)17; temp_ptr = (d_tt_struct *)io_std_device.in->dev_sp; DOWRITERC(temp_ptr->fildes, &dc1, 1, status); if (0 != status) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status); mask_in &= (~TRM_READSYNC); break; case iop_terminator: memcpy(&mask_term.mask[0], (pp->str.addr + p_offset), SIZEOF(io_termmask)); terminator_specified = TRUE; temp_ptr = (d_tt_struct *)d_in->dev_sp; if (mask_term.mask[0] == NUL && mask_term.mask[1] == NUL && mask_term.mask[2] == NUL && mask_term.mask[3] == NUL && mask_term.mask[4] == NUL && mask_term.mask[5] == NUL && mask_term.mask[6] == NUL && mask_term.mask[7] == NUL) temp_ptr->default_mask_term = TRUE; else temp_ptr->default_mask_term = FALSE; break; case iop_noterminator: temp_ptr = (d_tt_struct *)d_in->dev_sp; temp_ptr->default_mask_term = FALSE; memset(&mask_term.mask[0], 0, SIZEOF(io_termmask)); break; case iop_ttsync: t.c_iflag |= IXON; break; case iop_nottsync: t.c_iflag &= ~IXON; break; case iop_typeahead: mask_in &= (~TRM_NOTYPEAHD); break; case iop_notypeahead: mask_in |= TRM_NOTYPEAHD; break; case iop_upscroll: d_out->dollar.y++; if (d_out->length) d_out->dollar.y %= d_out->length; if (NULL != CURSOR_ADDRESS) gtm_tputs(gtm_tparm(CURSOR_ADDRESS, d_out->dollar.y, d_out->dollar.x), 1, outc); break; case iop_width: GET_LONG(width, pp->str.addr + p_offset); if (0 > width) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DEVPARMNEG); /* Do not allow a WIDTH of 1 if UTF mode (ICHSET or OCHSET is not M) */ if ((1 == width) && ((CHSET_M != d_in->ochset) || (CHSET_M != d_in->ichset))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_WIDTHTOOSMALL); if (0 == width) { d_out->wrap = FALSE; d_out->width = TTDEF_PG_WIDTH; } else { d_out->width = width; d_out->wrap = TRUE; } break; case iop_wrap: d_out->wrap = TRUE; break; case iop_nowrap: d_out->wrap = FALSE; break; case iop_x: GET_LONG(d_out->dollar.x, pp->str.addr + p_offset); if (0 > (int4)d_out->dollar.x) d_out->dollar.x = 0; if (d_out->dollar.x > d_out->width && d_out->wrap) { d_out->dollar.y += (d_out->dollar.x / d_out->width); if (d_out->length) d_out->dollar.y %= d_out->length; d_out->dollar.x %= d_out->width; } if (NULL != CURSOR_ADDRESS) gtm_tputs(gtm_tparm(CURSOR_ADDRESS, d_out->dollar.y, d_out->dollar.x), 1, outc); break; case iop_y: GET_LONG(d_out->dollar.y, pp->str.addr + p_offset); if (0 > (int4)d_out->dollar.y) d_out->dollar.y = 0; if (d_out->length) d_out->dollar.y %= d_out->length; if (NULL != CURSOR_ADDRESS) gtm_tputs(gtm_tparm(CURSOR_ADDRESS, d_out->dollar.y, d_out->dollar.x), 1, outc); break; case iop_ipchset: { # ifdef KEEP_zOS_EBCDIC if ((iconv_t)0 != iod->input_conv_cd) { ICONV_CLOSE_CD(iod->input_conv_cd); } SET_CODE_SET(iod->in_code_set, (char *)(pp->str.addr + p_offset + 1)); if (DEFAULT_CODE_SET != iod->in_code_set) ICONV_OPEN_CD(iod->input_conv_cd, (char *)(pp->str.addr + p_offset + 1), INSIDE_CH_SET); # endif GET_ADDR_AND_LEN(chset_mstr.addr, chset_mstr.len); SET_ENCODING(temp_chset, &chset_mstr); if (!gtm_utf8_mode && IS_UTF_CHSET(temp_chset)) break; /* ignore UTF chsets if not utf8_mode. */ if (IS_UTF16_CHSET(temp_chset)) /* UTF16 is not valid for TTY */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADCHSET, 2, chset_mstr.len, chset_mstr.addr); iod->ichset = temp_chset; break; } case iop_opchset: { # ifdef KEEP_zOS_EBCDIC if ((iconv_t)0 != iod->output_conv_cd) { ICONV_CLOSE_CD(iod->output_conv_cd); } SET_CODE_SET(iod->out_code_set, (char *)(pp->str.addr + p_offset + 1)); if (DEFAULT_CODE_SET != iod->out_code_set) ICONV_OPEN_CD(iod->output_conv_cd, INSIDE_CH_SET, (char *)(pp->str.addr + p_offset + 1)); # endif GET_ADDR_AND_LEN(chset_mstr.addr, chset_mstr.len); SET_ENCODING(temp_chset, &chset_mstr); if (!gtm_utf8_mode && IS_UTF_CHSET(temp_chset)) break; /* ignore UTF chsets if not utf8_mode. */ if (IS_UTF16_CHSET(temp_chset)) /* UTF16 is not valid for TTY */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADCHSET, 2, chset_mstr.len, chset_mstr.addr); iod->ochset = temp_chset; break; } case iop_chset: { GET_ADDR_AND_LEN(chset_mstr.addr, chset_mstr.len); SET_ENCODING(temp_chset, &chset_mstr); if (!gtm_utf8_mode && IS_UTF_CHSET(temp_chset)) break; /* ignore UTF chsets if not utf8_mode. */ if (IS_UTF16_CHSET(temp_chset)) /* UTF16 is not valid for TTY */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADCHSET, 2, chset_mstr.len, chset_mstr.addr); iod->ichset = iod->ochset = temp_chset; break; } } p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); } temp_ptr = (d_tt_struct *)d_in->dev_sp; Tcsetattr(tt_ptr->fildes, TCSANOW, &t, status, save_errno); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_TCSETATTR, 1, tt_ptr->fildes, save_errno); if (tt == d_in->type) { temp_ptr->term_ctrl = mask_in; /* reset the mask to default if chset was changed without specifying new terminators or Default */ if ((!terminator_specified && (old_ichset != iod->ichset)) || (terminator_specified && temp_ptr->default_mask_term)) { memset(&mask_term.mask[0], 0, SIZEOF(io_termmask)); if (CHSET_M != iod->ichset) { mask_term.mask[0] = TERM_MSK_UTF8_0; mask_term.mask[4] = TERM_MSK_UTF8_4; } else mask_term.mask[0] = TERM_MSK; temp_ptr->default_mask_term = TRUE; } memcpy(&temp_ptr->mask_term, &mask_term, SIZEOF(io_termmask)); } if (flush_input) { TCFLUSH(tt_ptr->fildes, TCIFLUSH, status); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LIT_AND_LEN("tcflush input"), CALLFROM, errno); } } else if (tt_ptr->mupintr && !dollar_zininterrupt) { /* The interrupted read was not properly resumed so clear it now */ tt_ptr->mupintr = FALSE; tt_ptr->tt_state_save.who_saved = ttwhichinvalid; io_find_mvstent(iod, TRUE); /* clear mv stack entry */ } REVERT_GTMIO_CH(&iod->pair, ch_set); return; } fis-gtm-V7.0-005/sr_unix/iott_write.c0000644000032200000250000001354014342376330016401 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_unistd.h" #include "gtm_stdlib.h" #include "gtm_signal.h" #include "gtm_string.h" #include #include #include #include "io.h" #include "iottdef.h" #include "gtmio.h" #include "gt_timer.h" #include "send_msg.h" #include "error.h" #include "dollarx.h" #include "have_crit.h" #include "iott_flush_time.h" #include "svnames.h" #include "op.h" #include "util.h" #include "deferred_events_queue.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" #include "gtm_utf8.h" #endif GBLREF boolean_t gtm_utf8_mode, hup_on, prin_in_dev_failure, prin_out_dev_failure; GBLREF int exi_condition, process_exiting; GBLREF int4 error_condition; GBLREF io_pair io_curr_device, io_std_device; GBLREF mval dollar_zstatus; GBLREF volatile int4 outofband; error_def(ERR_NOPRINCIO); error_def(ERR_TERMHANGUP); error_def(ERR_TERMWRITE); error_def(ERR_ZINTRECURSEIO); void iott_write_buffered_text(io_desc *io_ptr, char *text, int textlen); void iott_write_buffered_text(io_desc *io_ptr, char *text, int textlen) { d_tt_struct *tt_ptr; int buff_left, status; boolean_t ch_set; tt_ptr = io_ptr->dev_sp; assert(tt_ptr->write_active == FALSE); ESTABLISH_GTMIO_CH(&io_ptr->pair, ch_set); if (sighup == outofband) { TERMHUP_NOPRINCIO_CHECK(TRUE); /* TRUE for WRITE */ return; } tt_ptr->write_active = TRUE; buff_left = IOTT_BUFF_LEN - (int)((tt_ptr->tbuffp - tt_ptr->ttybuff)); assert(buff_left > IOTT_BUFF_MIN || prin_out_dev_failure); if (!prin_out_dev_failure && (buff_left < textlen)) { iott_flush_buffer(io_ptr, TRUE); buff_left = IOTT_BUFF_LEN; } if (textlen <= buff_left) { memcpy((void *)tt_ptr->tbuffp, text, textlen); tt_ptr->tbuffp += textlen; buff_left -= textlen; if (buff_left <= IOTT_BUFF_MIN) iott_flush_buffer(io_ptr, FALSE); /* tt_ptr->write_active = FALSE */ else tt_ptr->write_active = FALSE; } else { DOWRITERC(tt_ptr->fildes, text, textlen, status); tt_ptr->write_active = FALSE; if ((0 == status) && (ERR_TERMHANGUP != error_condition)) { if ((io_ptr == io_std_device.out) && (prin_out_dev_failure)) { /* if it was TRUE, try flush we may have skipped above & clear flag because write worked */ prin_out_dev_failure = FALSE; iott_flush_buffer(io_ptr, TRUE); } } else /* (0 != status) */ { ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, TRUE, FALSE); /* TRUE, FALSE: WRITE, not socket */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_TERMWRITE, 0, status); } } REVERT_GTMIO_CH(&io_ptr->pair, ch_set); } void iott_write(mstr *v) { unsigned str_len; unsigned len; int status, this_width, char_width, avail_width; char *str; unsigned char *ptr, *ptrnext, *ptrtop; io_desc *io_ptr, *flush_parm; d_tt_struct *tt_ptr; boolean_t utf8_active = FALSE; wint_t codepoint; boolean_t flush_immediately; boolean_t ch_set; /* We cannot be starting unsafe timers during process exiting or in an interrupt-deferred window. */ flush_immediately = (process_exiting || (INTRPT_OK_TO_INTERRUPT != intrpt_ok_state)); str_len = v->len; if (0 != str_len) { str = v->addr; io_ptr = io_curr_device.out; tt_ptr = (d_tt_struct *)io_ptr->dev_sp; if (tt_ptr->mupintr) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZINTRECURSEIO); ESTABLISH_GTMIO_CH(&io_curr_device, ch_set); UTF8_ONLY(utf8_active = gtm_utf8_mode ? (CHSET_M != io_ptr->ochset) : FALSE;) for (; ;) { if (FALSE == io_ptr->wrap) len = str_len; else { if ((io_ptr->dollar.x >= io_ptr->width) && (START == io_ptr->esc_state)) { iott_write_buffered_text(io_ptr, STR_AND_LEN(NATIVE_TTEOL)); io_ptr->dollar.y++; io_ptr->dollar.x = 0; } if (START != io_ptr->esc_state) len = str_len; /* write all if in escape sequence */ #ifdef UTF8_SUPPORTED else if (utf8_active) { ptrtop = (unsigned char *)str + str_len; avail_width = io_ptr->width - io_ptr->dollar.x; for (this_width = 0, ptr = (unsigned char *)str; ptr < ptrtop; ptr = ptrnext) { ptrnext = UTF8_MBTOWC(ptr, ptrtop, codepoint); if (WEOF == codepoint) UTF8_BADCHAR(0, ptr, ptrtop, 0, NULL); GTM_IO_WCWIDTH(codepoint, char_width); if ((this_width + char_width) > avail_width) break; this_width += char_width; } len = (int)(ptr - (unsigned char *)str); if (0 == len) { if (char_width <= io_ptr->width) { io_ptr->dollar.x = io_ptr->width; /* force wrap */ continue; } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_TERMWRITE); } } #endif else if ((io_ptr->dollar.x + str_len) <= io_ptr->width) len = str_len; else len = io_ptr->width - io_ptr->dollar.x; } assert(0 != len); iott_write_buffered_text(io_ptr, str, len); dollarx(io_ptr, (uchar_ptr_t)str, (uchar_ptr_t)str + len); str_len -= len; if (0 >= (signed)str_len) break; str += len; } if (flush_immediately) { if (TRUE == tt_ptr->timer_set) { cancel_timer((TID)io_ptr); tt_ptr->timer_set = FALSE; } tt_ptr->write_active = TRUE; iott_flush_buffer(io_ptr, FALSE); } else if (FALSE == tt_ptr->timer_set) { flush_parm = io_ptr; tt_ptr->timer_set = TRUE; start_timer((TID)io_ptr, IOTT_FLUSH_WAIT, &iott_flush_time, SIZEOF(flush_parm), (char *)&flush_parm); } REVERT_GTMIO_CH(&io_curr_device, ch_set); } } fis-gtm-V7.0-005/sr_unix/iottdef.h0000755000032200000250000000702614342376330015660 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ #ifndef IOTTDEF_H #define IOTTDEF_H #include "gtm_termios.h" #include "gtm_stdio.h" #include "compiler.h" #define TERM_MSK 0x08002400 /* CR LF ESC */ #define TERM_MSK_UTF8_0 0x08003400 /* add FF */ #define TERM_MSK_UTF8_4 0x00000020 /* NL */ #include "iottdefsp.h" #define NUM_BITS_IN_INT4 (SIZEOF(int4) * 8) #define TTDEF_BUF_SZ MAX_SRCLINE #define TTDEF_PG_WIDTH 255 #define IOTT_FLUSH_WAIT 300 #define IOTT_FLUSH_RETRY 50 #define IOTT_BUFF_LEN 3072 #define IOTT_BUFF_MIN 128 #define TT_EDITING 0x1000 #define TT_NOINSERT 0x2000 #define TT_EMPTERM 0x4000 #define TERMHUP_NOPRINCIO_CHECK(WRITE) \ MBSTART { \ assert(hup_on || prin_in_dev_failure); \ exi_condition = -ERR_TERMHANGUP; \ ISSUE_NOPRINCIO_IF_NEEDED(io_ptr, WRITE, FALSE); \ async_action(FALSE); \ } MBEND enum tt_which { ttwhichinvalid, dmread, ttread, ttrdone }; typedef struct { enum tt_which who_saved; unsigned char *buffer_start; /* initial stringpool.free */ wint_t *buffer_32_start; int utf8_more; int dx; int dx_start; int dx_instr; int dx_outlen; int instr; int outlen; int index; /* dm_read only */ int cl; /* dm_read only */ int length; int exp_length; boolean_t insert_mode; ABS_TIME end_time; unsigned char *more_ptr; unsigned char *zb_ptr; unsigned char *zb_top; unsigned short escape_length; /* dm_read only */ unsigned char escape_sequence[ESC_LEN]; /* dm_read only */ unsigned char more_buf[GTM_MB_LEN_MAX + 1]; } tt_interrupt; typedef struct { uint4 mask[8]; } io_termmask; typedef struct { unsigned short status; unsigned short char_ct; uint4 dev_dep_info; }iosb; typedef struct { uint4 x; uint4 mask; }io_terminator; typedef struct { uint4 in_buf_sz; /* size of read buffer */ /* unsigned short pg_width; width of output page */ uint4 ext_cap; io_terminator enbld_outofbands; /* enabled out-of-band chars */ uint4 term_ctrl; io_termmask mask_term; int fildes; struct termios *ttio_struct; tt_interrupt tt_state_save; /* in case job interrupt */ boolean_t mupintr; /* read was interrupted */ char *ttybuff; /* buffer for tty */ volatile char *tbuffp; /* next open space in buffer */ volatile boolean_t timer_set; /* text flush timer is set */ volatile boolean_t write_active; /* we are in write -- postpone flush by timer */ boolean_t canonical; mstr recall_buff; /* if EDITING enabled */ int recall_size; /* size of recall_buff allocated */ int recall_width; /* display width of current contents */ boolean_t discard_lf; /* UTF8 mode - previous char was CR so ignore following LF */ boolean_t default_mask_term; /* mask_term is the default */ boolean_t done_1st_read; /* UTF8 mode - check for BOM if not */ }d_tt_struct; void iott_flush_buffer(io_desc *ioptr, boolean_t new_write_flag); void iott_mterm(io_desc *ioptr); void iott_rterm(io_desc *ioptr); void iott_readfl_badchar(mval *vmvalptr, wint_t *dataptr32, int datalen, int delimlen, unsigned char *delimptr, unsigned char *strend, unsigned char *buffer_start); #endif fis-gtm-V7.0-005/sr_unix/iottdefsp.h0000755000032200000250000000247214342376330016223 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifdef UTF8_SUPPORTED #include "gtm_utf8.h" #endif #define ASCII_TTEOL "\012" #define EBCDIC_TTEOL "\025" #define ESC ASCII_ESC #define NATIVE_ESC ASCII_ESC #define NATIVE_CR ASCII_CR #define NATIVE_LF ASCII_LF #define NATIVE_FF ASCII_FF #define NATIVE_BS ASCII_BS #define NATIVE_VT VT #ifdef KEEP_zOS_EBCDIC #define NATIVE_TTEOL ((ascii != io_ptr->out_code_set) ? EBCDIC_TTEOL : ASCII_TTEOL) #else #define NATIVE_TTEOL ASCII_TTEOL #endif /* Editing control characters */ #define CTRL_A '\001' #define CTRL_B '\002' #define CTRL_D '\004' #define CTRL_E '\005' #define CTRL_F '\006' #define CTRL_K '\013' #define CTRL_U '\025' #define EDIT_SOL CTRL_A #define EDIT_LEFT CTRL_B #define EDIT_DELETE CTRL_D #define EDIT_EOL CTRL_E #define EDIT_RIGHT CTRL_F #define EDIT_DEOL CTRL_K #define EDIT_ERASE CTRL_U fis-gtm-V7.0-005/sr_unix/ious_iocontrol.c0000755000032200000250000000132314342376330017256 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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 "io.h" #include "iousdef.h" GBLREF io_pair io_curr_device; void ious_iocontrol(mstr *mn, int4 argcnt, va_list args) { return; } void ious_dlr_device(mstr *d) { d->len = 0; return; } void ious_dlr_key(mstr *d) { d->len = 0; return; } fis-gtm-V7.0-005/sr_unix/ious_open.c0000755000032200000250000000350614342376330016214 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "io.h" #include "iousdef.h" error_def(ERR_USRIOINIT); /* Open a device belonging to the user defined nmemonicspace */ short ious_open(io_log_name *dev, mval *pp, int file_des, mval *mspace, int4 timeout) { io_desc *iod; dev_dispatch_struct *fgn_driver; iod = dev->iod; if (iod->state == dev_never_opened) { iod->dev_sp = (void *)malloc(SIZEOF(d_us_struct)); memset(iod->dev_sp, 0, SIZEOF(d_us_struct)); iod->state = dev_closed; } if (iod->state != dev_open) { if (mspace && mspace->str.len) { /* Currently there is no mechanism implementing a user nmemonicspace device. As a * result, io_get_fgn_driver() issues an rts_error() which leaves * (NULL == (d_us_struct*)(iod->dev_sp))->disp) true. If a user nmemonicspace is * implemented, it must open the device and complete enough setup/configuration to * avoid a memory access violation. */ fgn_driver = io_get_fgn_driver(&mspace->str); ((d_us_struct*)(iod->dev_sp))->disp = fgn_driver; } else if ((NULL == ((d_us_struct*)(iod->dev_sp))->disp) || (NULL == (((d_us_struct*)(iod->dev_sp))->disp->open))) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_USRIOINIT); return FALSE; } ((void(*)())(((d_us_struct*)(iod->dev_sp))->disp->open))(); iod->state = dev_open; } return TRUE; } fis-gtm-V7.0-005/sr_unix/ious_rdone.c0000755000032200000250000000113214342376330016353 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2006 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 "io.h" #include "iousdef.h" GBLREF io_pair io_curr_device; int ious_rdone(mint *v, int4 t) { *v = -1; return TRUE; } fis-gtm-V7.0-005/sr_unix/ious_read.c0000755000032200000250000000125514342376330016165 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2006 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 "io.h" #include "iousdef.h" #include "stringpool.h" GBLREF spdesc stringpool; GBLREF io_pair io_curr_device; int ious_read(mval *v, int4 t) { v->str.len = 0; v->str.addr = (char *)0; return TRUE; } fis-gtm-V7.0-005/sr_unix/ious_readfl.c0000755000032200000250000000127414342376330016510 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2006 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 "io.h" #include "iousdef.h" #include "stringpool.h" GBLREF spdesc stringpool; GBLREF io_pair io_curr_device; int ious_readfl(mval *v, int4 length, int4 t) { v->str.len = 0; v->str.addr = (char *)0; return TRUE; } fis-gtm-V7.0-005/sr_unix/ious_write.c0000755000032200000250000000107514342376330016404 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "io.h" #include "iousdef.h" GBLREF io_pair io_curr_device; void ious_write(mstr *v) { return; } fis-gtm-V7.0-005/sr_unix/ious_wtone.c0000755000032200000250000000110214342376330016375 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2006 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 "io.h" #include "iousdef.h" GBLREF io_pair io_curr_device; void ious_wtone(int v) { return; } fis-gtm-V7.0-005/sr_unix/ipcrmid.c0000755000032200000250000000576014342376330015647 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* Perform forks to priv'd gtmsecshr routine to remove ipc ids */ #include "mdef.h" #include #include #include #include #include "gtm_string.h" #include "gtm_limits.h" #include "io.h" #include "gtmsecshr.h" #include "secshr_client.h" #include "gtmmsg.h" #include "ipcrmid.h" #include "send_msg.h" #define REMIPC(op, ipcid) (send_mesg2gtmsecshr((unsigned int)(op), (unsigned int)(ipcid), (char *)NULL, 0)) error_def(ERR_SYSCALL); /* General note - in the routines below (and indeed in general), if the "error" is not relevant to the current process * being able to continue, process should (at most) send the message to the operator log and continue. No gtm_putmsg() * to the console and continue. This just screws up the output console for the client. */ /* Remove a semaphore id : if the current process has no permissions to remove it by itself, * force it through gtmsecshr. If we are a debug build and $gtm_useproc is set, send it through * gtmsecshr anyway. */ int sem_rmid(int ipcid) { int status, save_errno; char buff[128]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DEBUG_ONLY(if (!TREF(gtm_usesecshr))) { if (-1 == semctl(ipcid, 0, IPC_RMID)) { # ifndef _AIX if (EIDRM == errno) return 0; # endif if (EPERM != errno) { save_errno = errno; SNPRINTF(buff, 128, "semctl(IPC_RMID, %d)", ipcid); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_STR(buff), CALLFROM, save_errno); errno = save_errno; return -1; } else { if (0 != (status = REMIPC(REMOVE_SEM, ipcid))) { errno = status; return -1; } } } } # ifdef DEBUG else if (0 != (status = REMIPC(REMOVE_SEM, ipcid))) { errno = status; return -1; } # endif return 0; } /* Remove a shared memory id */ int shm_rmid(int ipcid) { int status, save_errno; char buff[128]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DEBUG_ONLY(if (!TREF(gtm_usesecshr))) { if (-1 == shmctl(ipcid, IPC_RMID, NULL)) { if (EPERM != errno) { save_errno = errno; SNPRINTF(buff, 128, "shmctl(IPC_RMID, %d)", ipcid); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_STR(buff), CALLFROM, save_errno); errno = save_errno; return -1; } else { if (0 != (status = REMIPC(REMOVE_SHM, ipcid))) { errno = status; return -1; } } } } # ifdef DEBUG else if (0 != (status = REMIPC(REMOVE_SHM, ipcid))) { errno = status; return -1; } # endif return 0; } fis-gtm-V7.0-005/sr_unix/ipcrmid.h0000755000032200000250000000110214342376330015636 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef IPCRMID_INCLUDED #define IPCRMID_INCLUDED int sem_rmid(int ipcid); int shm_rmid(int ipcid); #endif /* IPCRMID_INCLUDED */ fis-gtm-V7.0-005/sr_unix/is_file_identical.c0000755000032200000250000000542614342376330017645 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* is_file_identical.c * returns TRUE if the two files are identical, * returns FALSE if 1. either one of the files specified * doesn't exist, or * 2. they are different files. */ #include "mdef.h" #include "gtm_stdlib.h" #include "gtm_stat.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "eintr_wrappers.h" #include "copy.h" #include "is_file_identical.h" #include "is_gdid.h" #include "iosp.h" /* for SS_NORMAL */ bool is_gdid_file_identical(gd_id_ptr_t fid, char *filename, int4 filelen) { int stat_res; struct stat stat_buf; assert(0 == filename[filelen]); STAT_FILE(filename, &stat_buf, stat_res); return is_gdid_stat_identical(fid, &stat_buf); } bool is_file_identical(char *filename1, char *filename2) { int rv = FALSE; int stat_res; struct stat st1, st2; STAT_FILE(filename1, &st1, stat_res); if (0 == stat_res) { STAT_FILE(filename2, &st2, stat_res); if (0 == stat_res) if ((st1.st_dev == st2.st_dev) && (st1.st_ino == st2.st_ino)) rv = TRUE; } return rv; } bool is_gdid_identical(gd_id_ptr_t fid1, gd_id_ptr_t fid2) { bool rv = FALSE; if ((fid1->inode == fid2->inode) && (fid1->device == fid2->device)) rv = TRUE; return rv; } bool is_gdid_stat_identical(gd_id_ptr_t fid, struct stat *stat_buf) { if (fid->device == stat_buf->st_dev && fid->inode == stat_buf->st_ino) return TRUE; else return FALSE; } void set_gdid_from_stat(gd_id_ptr_t fid, struct stat *stat_buf) { /* AIX/JFS has data in stat_buf->st_gen that seems related to relative time, but it's garbage for remote (NFS) files * and for files on non-OSF decended implementations, so we conclude it's irrelevant and ignore it; * because we use our own ftok generation there should be no consequence to this approach */ assert(SIZEOF(gd_id) <= SIZEOF(gds_file_id)); fid->inode = stat_buf->st_ino; fid->device = stat_buf->st_dev; } /* * Here we create a unique_id for a file. */ uint4 filename_to_id(gd_id_ptr_t fid, char *filename) { int stat_res; struct stat filestat; STAT_FILE(filename, &filestat, stat_res); if (-1 == stat_res) return errno; set_gdid_from_stat(fid, &filestat); return SS_NORMAL; } fis-gtm-V7.0-005/sr_unix/is_fstype_nfs.c0000644000032200000250000000260114342376330017057 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2017 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 #ifdef _AIX #include /* needed for MNT_NFS */ #endif #if defined( __linux__) || defined(__CYGWIN__) /* Define NFS_SUPER_MAGIC. This should ideally include for NFS_SUPER_MAGIC. * However, this header file doesn't seem to be standard and gives lots of * compilation errors and hence defining again here. The constant value * seems to be portable across all linuxes (courtesy 'statfs' man pages) */ # define NFS_SUPER_MAGIC 0x6969 #endif #include "is_fstype_nfs.h" boolean_t is_fstype_nfs(int fd) { struct statfs buf; boolean_t is_nfs; is_nfs = FALSE; if (0 != fstatfs(fd, &buf)) return is_nfs; # if defined(__linux__) || defined(__CYGWIN__) is_nfs = (NFS_SUPER_MAGIC == buf.f_type); # elif defined(_AIX) is_nfs = (MNT_NFS == buf.f_vfstype); # else # error UNSUPPORTED PLATFORM # endif return is_nfs; } fis-gtm-V7.0-005/sr_unix/is_fstype_nfs.h0000644000032200000250000000115514342376330017067 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016 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. * * * ****************************************************************/ #ifndef IS_FSTYPE_NFS_H_INCLUDED #define IS_FSTYPE_NFS_H_INCLUDED boolean_t is_fstype_nfs(int fd); #endif fis-gtm-V7.0-005/sr_unix/is_icu_symbol_rename.csh0000755000032200000250000000461314342376330020736 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2009-2020 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. # # # ################################################################# # This tool crudely checks if it is possible to start gtm in utf8 mode. If "undefined symbol: u_getVersion" is seen, # which is possible if icu is built with symbol renaming, then print icu version using pkg-config or icu-config # The version that started support for gtm_icu_version is : V53004 if !($?gtm_dist) then setenv gtm_dist $gtm_root/$gtm_curpro/dbg/ endif # depending on the list of locales configured, locale -a might be considered a binary output. (on scylla currently) # grep needs -a option to process the output as text to get the actual value instead of "Binary file (standard input) matches" # but -a is not supported on the non-linux servers we have. if ("Linux" == "$HOSTOS") then set binaryopt = "-a" else set binaryopt = "" endif set utflocale = `locale -a | grep $binaryopt -iE 'en_us\.utf.?8$' | head -n 1` if ( -f $gtm_tools/set_library_path.csh ) then source $gtm_tools/set_library_path.csh >& /dev/null endif setenv LC_CTYPE $utflocale ; unsetenv LC_ALL setenv gtm_chset UTF-8 set gtmstat = `echo "halt" | $gtm_dist/mumps -direct |& grep "u_getVersion"` echo $gtmstat |& grep -q "u_getVersion" if !($status) then # icu-config is deprecated. So try "pkg-config icu-io" first, followed by "icu-config" and "pkg-config icu" # Set cmd to icu-config, in case none of the conditions below satisfy. It is better to fail with error than exit quietly set cmd = "icu-config --version" if ( (-X pkg-config) && ( { pkg-config --exists icu-io } ) ) then set cmd = "pkg-config --modversion icu-io" else if (-X icu-config) then set cmd = "icu-config --version" else if ( (-X pkg-config) && ( { pkg-config --exists icu } ) ) then set cmd = "pkg-config --modversion icu" endif # pkg-config/icu-config can report versions like 4.2.1 but we want just the 4.2 part, so choose only upto the 2nd field echo `$cmd | awk '{ver=+$0;if(ver>5){ver=ver/10}printf("%.1f\n",ver);exit}'` endif fis-gtm-V7.0-005/sr_unix/is_proc_alive.c0000755000032200000250000000301214342376330017022 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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 #ifdef __MVS__ #include #endif #include "gtm_signal.h" #include "is_proc_alive.h" #ifdef DEBUG GBLREF uint4 process_id; #endif /* ---------------------------------------------- * Check if process is still alive * * Arguments: * pid - process ID * imagecnt- used by VMS, dummy only in UNIX * * Return: * TRUE - Process still alive * FALSE - Process is gone * ---------------------------------------------- */ bool is_proc_alive(int4 pid, int4 imagecnt) { int status; bool ret; if (0 == pid) return FALSE; # ifdef __MVS__ errno = 0; /* it is possible getpriority returns -1 even in case of success */ status = getpriority(PRIO_PROCESS, (id_t)pid); if ((-1 == status) && (0 == errno)) status = 0; # else status = kill(pid, 0); /* just checking */ # endif if (status) { assert((-1 == status) && ((EPERM == errno) || (ESRCH == errno))); ret = (ESRCH != errno); } else ret = TRUE; assert(ret || (process_id != pid)); return ret; } fis-gtm-V7.0-005/sr_unix/is_raw_dev.c0000755000032200000250000000145514342376330016337 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "gtm_stat.h" #include "eintr_wrappers.h" #include "is_raw_dev.h" #define RAW_PATH "/dev/" bool is_raw_dev(char *path) { char *c; int stat_res; struct stat fileinfo; STAT_FILE(path, &fileinfo, stat_res); if ((0 == stat_res) && (S_ISCHR(fileinfo.st_mode) || S_ISBLK(fileinfo.st_mode))) return TRUE; else return FALSE; } fis-gtm-V7.0-005/sr_unix/is_raw_dev.h0000755000032200000250000000106614342376330016342 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef IS_RAW_DEV_INCLUDED #define IS_RAW_DEV_INCLUDED bool is_raw_dev(char *path); #endif /* IS_RAW_DEV_INCLUDED */ fis-gtm-V7.0-005/sr_unix/jnl_file_close_timer.c0000644000032200000250000000734214342376330020362 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_stat.h" #include "gtm_fcntl.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "gtmio.h" /* for CLOSEFILE used by the F_CLOSE macro in JNL_FD_CLOSE */ #include "repl_sp.h" /* for F_CLOSE used by the JNL_FD_CLOSE macro */ #include "iosp.h" /* for SS_NORMAL used by the JNL_FD_CLOSE macro */ #include "gt_timer.h" #include "gtmimagename.h" #include "dpgbldir.h" #include "have_crit.h" #include "anticipatory_freeze.h" #include "jnl_file_close_timer.h" #include "db_snapshot.h" GBLREF boolean_t oldjnlclose_started; GBLREF uint4 process_id; GBLREF int process_exiting; void jnl_file_close_timer(void) { boolean_t do_timer_restart = FALSE; gd_addr *addr_ptr; gd_region *r_local, *r_top; int rc; jnl_private_control *jpc; sgmnt_addrs *csa; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Check every 1 minute if we need to close an older generation journal file open; also close any lingering snapshot. * The only exceptions are * a) The source server can have older generations open and they should not be closed. * b) If we are in the process of switching to a new journal file while we get interrupted * by the heartbeat timer, we should not close the older generation journal file * as it will anyways be closed by the mainline code. But identifying that we are in * the midst of a journal file switch is tricky so we check if the process is in * crit for this region and if so we skip the close this time and wait for the next heartbeat. */ if ((INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) && (!process_exiting)) { for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) { for (r_local = addr_ptr->regions, r_top = r_local + addr_ptr->n_regions; r_local < r_top; r_local++) { if (!r_local->open || r_local->was_open) continue; if (!IS_REG_BG_OR_MM(r_local)) continue; csa = REG2CSA(r_local); SS_RELEASE_IF_NEEDED(csa, (node_local_ptr_t)csa->nl); jpc = csa->jnl; if (csa->now_crit) { do_timer_restart |= csa->snapshot_in_prog || ((NULL != jpc) && (NOJNL != jpc->channel)); continue; } if ((NULL != jpc) && (NOJNL != jpc->channel) && JNL_FILE_SWITCHED(jpc)) { /* The journal file we have as open is not the latest generation journal file. Close it */ /* Assert that we never have an active write on a previous generation journal file. */ assert(process_id != jpc->jnl_buff->io_in_prog_latch.u.parts.latch_pid); JNL_FD_CLOSE(jpc->channel, rc); /* sets jpc->channel to NOJNL */ jpc->pini_addr = 0; } do_timer_restart |= csa->snapshot_in_prog || ((NULL != jpc) && (NOJNL != jpc->channel)); } } } /* Only restart the timer if there are still journal files open, or if we didn't check because it wasn't safe. * Otherwise, it will be restarted on the next successful jnl_file_open(), or never, if we are exiting. */ if (do_timer_restart || (INTRPT_OK_TO_INTERRUPT != intrpt_ok_state)) start_timer((TID)jnl_file_close_timer, OLDERJNL_CHECK_INTERVAL, jnl_file_close_timer, 0, NULL); else { assert(!do_timer_restart || process_exiting); oldjnlclose_started = FALSE; } } fis-gtm-V7.0-005/sr_unix/jnl_file_close_timer.h0000755000032200000250000000230414342376330020363 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2018 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. * * * ****************************************************************/ #ifndef JNL_FILE_CLOSE_TIMER_H_INCLUDED #define JNL_FILE_CLOSE_TIMER_H_INCLUDED #define OLDERJNL_CHECK_INTERVAL (60 * MILLISECS_IN_SEC) #define START_JNL_FILE_CLOSE_TIMER_IF_NEEDED \ MBSTART { \ GBLREF boolean_t heartbeat_started; \ GBLREF boolean_t oldjnlclose_started; \ GBLREF boolean_t is_src_server; \ \ if (!oldjnlclose_started && !is_src_server) \ { \ start_timer((TID)jnl_file_close_timer, OLDERJNL_CHECK_INTERVAL, jnl_file_close_timer, 0, NULL); \ oldjnlclose_started = TRUE; /* should always be set AFTER start_timer */ \ } \ } MBEND void jnl_file_close_timer(void); #endif fis-gtm-V7.0-005/sr_unix/jnl_file_extend.c0000644000032200000250000003234014342376330017340 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_stat.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gdsroot.h" #include "gtm_statvfs.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "ccp.h" #include "iosp.h" #include "jnl.h" #include "gtmio.h" #include "eintr_wrappers.h" #include "send_msg.h" #include "is_file_identical.h" #include "dbfilop.h" #include "disk_block_available.h" #include "wcs_flu.h" #include "anticipatory_freeze.h" #include "error.h" GBLREF gd_region *gv_cur_region; GBLREF jnl_gbls_t jgbl; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF boolean_t in_jnl_file_autoswitch; error_def(ERR_DBFILERR); error_def(ERR_DSKSPACEFLOW); error_def(ERR_JNLEXTEND); error_def(ERR_JNLFILEXTERR); error_def(ERR_JNLNOCREATE); error_def(ERR_JNLRDERR); error_def(ERR_JNLREADEOF); error_def(ERR_JNLSPACELOW); error_def(ERR_JNLWRERR); error_def(ERR_NEWJNLFILECREAT); error_def(ERR_NOSPACEEXT); uint4 jnl_file_extend(jnl_private_control *jpc, uint4 total_jnl_rec_size) { file_control *fc; boolean_t need_extend; jnl_buffer_ptr_t jb; jnl_create_info jnl_info; jnl_file_header *header; unsigned char hdr_buff[REAL_JNL_HDR_LEN + MAX_IO_BLOCK_SIZE]; uint4 new_alq; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; char prev_jnl_fn[JNL_NAME_SIZE]; uint4 jnl_status = 0, status; int new_blocks, warn_blocks, result; gtm_uint64_t avail_blocks, temp_blk; uint4 aligned_tot_jrec_size, count; uint4 jnl_fs_block_size, read_write_size; unix_db_info *udi; jnlpool_addrs_ptr_t local_jnlpool; /* needed by INST_FREEZE_ON_NOSPC_ENABLED */ DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; switch(jpc->region->dyn.addr->acc_meth) { case dba_mm: case dba_bg: csa = &FILE_INFO(jpc->region)->s_addrs; break; default: assertpro(IS_REG_BG_OR_MM(jpc->region)); } csd = csa->hdr; assert(csa == cs_addrs && csd == cs_data); assert(csa->now_crit || (csd->clustered && (CCST_CLOSED == csa->nl->ccp_state))); assert(&FILE_INFO(jpc->region)->s_addrs == csa); assert(csa->jnl_state == csd->jnl_state); assertpro(JNL_ENABLED(csa) && (NOJNL != jpc->channel) && (!JNL_FILE_SWITCHED(jpc))); /* crit and messing with the journal file - how could it have vanished? */ if (!total_jnl_rec_size) { /* rec_size=0 has special meaning. Caller (source server for now) wants to force an autoswitch. */ new_blocks = 0; } else if (!csd->jnl_deq || (csd->jnl_alq + csd->jnl_deq > csd->autoswitchlimit)) { assert(DIVIDE_ROUND_UP(total_jnl_rec_size, DISK_BLOCK_SIZE) <= csd->jnl_alq); assert(csd->jnl_alq == csd->autoswitchlimit); new_blocks = csd->jnl_alq; } else /* May cause extension of csd->jnl_deq * n blocks where n > 0 */ new_blocks = ROUND_UP(DIVIDE_ROUND_UP(total_jnl_rec_size, DISK_BLOCK_SIZE), csd->jnl_deq); jpc->status = SS_NORMAL; jb = jpc->jnl_buff; assert(0 <= new_blocks); DEBUG_ONLY(count = 0); do { DEBUG_ONLY(count++); /* Usually we will do the loop just once where we do the file extension. * Rarely we might need to do an autoswitch instead. * Even more rarely, we might need to do a file extension of the autoswitched journal to fit in the * transaction's journal requirements (because journal file initial allocation did not fit it). * Therefore we should do this loop a maximum of twice. Assert it. */ assert(count <= 2); need_extend = FALSE; if (SS_NORMAL == (status = disk_block_available(jpc->channel, &avail_blocks, TRUE))) { warn_blocks = (csd->jnl_alq + csd->jnl_deq > csd->autoswitchlimit) ? ((csd->jnl_deq > csd->autoswitchlimit) ? csd->jnl_deq : csd->autoswitchlimit) : new_blocks; if ((warn_blocks * EXTEND_WARNING_FACTOR) > avail_blocks) { if (new_blocks > avail_blocks) { /* If we cannot satisfy the request, it is an error, unless the anticipatory freeze * scheme is in effect in which case, we will assume space is available even if * it is not and go ahead with writes to the disk. If the writes fail with ENOSPC * we will freeze the instance and wait for space to become available and keep * retrying the writes. Therefore, we make the NOSPACEEXT a warning in this case. */ if (!INST_FREEZE_ON_NOSPC_ENABLED(csa, local_jnlpool)) { temp_blk = new_blocks; send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_NOSPACEEXT, 4, JNL_LEN_STR(csd), &temp_blk, &avail_blocks); new_blocks = -1; jpc->status = ERR_NOSPACEEXT; break; } else { temp_blk = new_blocks; send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) MAKE_MSG_WARNING(ERR_NOSPACEEXT), 4, JNL_LEN_STR(csd), &temp_blk, &avail_blocks); } } else { temp_blk = avail_blocks - warn_blocks; send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DSKSPACEFLOW, 3, JNL_LEN_STR(csd), &temp_blk); } } } else send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_JNLFILEXTERR, 2, JNL_LEN_STR(csd), status); new_alq = jb->filesize + new_blocks; /* ensure current journal file size is well within autoswitchlimit --> design constraint */ assert(csd->autoswitchlimit >= jb->filesize); if (csd->autoswitchlimit < (jb->filesize + (EXTEND_WARNING_FACTOR * new_blocks))) /* close to max */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_JNLSPACELOW, 3, JNL_LEN_STR(csd), csd->autoswitchlimit - jb->filesize); if (jb->last_eof_written || (csd->autoswitchlimit < new_alq) || !new_blocks) { /* Reached max OR caller wants to switch unconditionally. Need to autoswitch */ /* Ensure new journal file can hold the entire current transaction's journal record requirements */ assert(csd->autoswitchlimit >= MAX_REQD_JNL_FILE_SIZE(total_jnl_rec_size)); memset(&jnl_info, 0, SIZEOF(jnl_info)); jnl_info.prev_jnl = &prev_jnl_fn[0]; set_jnl_info(gv_cur_region, &jnl_info); assert(JNL_ENABLED(csa) && (NOJNL != jpc->channel) && !(JNL_FILE_SWITCHED(jpc))); jnl_status = jnl_ensure_open(gv_cur_region, csa); if (0 != jnl_status) { if (SS_NORMAL != jpc->status) rts_error_csa(CSA_ARG(csa) VARLSTCNT(7) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(gv_cur_region), jpc->status); else rts_error_csa(CSA_ARG(csa) VARLSTCNT(6) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(gv_cur_region)); return EXIT_ERR; } if (!jb->last_eof_written) { /* flush the cache and jnl-buffer-contents to current journal file before * switching to a new journal. Set a global variable in_jnl_file_autoswitch * so jnl_write can know not to do the padding check. But because this is a global * variable, we also need to make sure it is reset in case of errors during the * autoswitch (or else calls to jnl_write after we are out of the autoswitch logic * will continue to incorrectly not do the padding check. Hence a condition handler. */ assert(!in_jnl_file_autoswitch); in_jnl_file_autoswitch = TRUE; /* Also make sure time is not changed. This way if "jnl_write" as part of writing a * journal record invokes jnl_file_extend, when the autoswitch is done and writing * of the parent jnl_write resumes, we want it to continue with the same timestamp * and not have to reset its time (non-trivial task) to reflect any changes since then. */ assert(!jgbl.save_dont_reset_gbl_jrec_time); jgbl.save_dont_reset_gbl_jrec_time = jgbl.dont_reset_gbl_jrec_time; jgbl.dont_reset_gbl_jrec_time = TRUE; /* Establish a condition handler so we reset a few global variables that have * temporarily been modified in case of errors inside wcs_flu/jnl_file_close. */ ESTABLISH_RET(jnl_file_autoswitch_ch, EXIT_ERR); if (!wcs_flu(WCSFLU_FLUSH_HDR | WCSFLU_WRITE_EPOCH | WCSFLU_SPEEDUP_NOBEFORE)) { if (SS_NORMAL != jpc->status) rts_error_csa(CSA_ARG(csa) VARLSTCNT(6) jpc->status, 4, JNL_LEN_STR(csd), DB_LEN_STR(gv_cur_region)); } assert(in_jnl_file_autoswitch); jnl_file_close(gv_cur_region, TRUE, TRUE); assert(in_jnl_file_autoswitch); REVERT; in_jnl_file_autoswitch = FALSE; jgbl.dont_reset_gbl_jrec_time = jgbl.save_dont_reset_gbl_jrec_time; DEBUG_ONLY(jgbl.save_dont_reset_gbl_jrec_time = FALSE); } else jnl_file_close(gv_cur_region, TRUE, TRUE); /* for jb->last_eof_written, just close */ assert((dba_mm == cs_data->acc_meth) || (csd == cs_data)); csd = cs_data; /* In MM, wcs_flu() can remap an extended DB, so reset csd to be sure */ assert(!jgbl.forw_phase_recovery || (NULL != jgbl.mur_pini_addr_reset_fnptr)); assert(jgbl.forw_phase_recovery || (NULL == jgbl.mur_pini_addr_reset_fnptr)); if (NULL != jgbl.mur_pini_addr_reset_fnptr) (*jgbl.mur_pini_addr_reset_fnptr)(csa); assert(!jnl_info.no_rename); assert(!jnl_info.no_prev_link); assert(!in_jnl_file_autoswitch); if (EXIT_NRM == cre_jnl_file(&jnl_info)) { assert(0 == memcmp(csd->jnl_file_name, jnl_info.jnl, jnl_info.jnl_len)); assert(csd->jnl_file_name[jnl_info.jnl_len] == '\0'); assert(csd->jnl_file_len == jnl_info.jnl_len); assert(csd->jnl_buffer_size == jnl_info.buffer); assert(csd->jnl_alq == jnl_info.alloc); assert(csd->jnl_deq == jnl_info.extend); assert(csd->jnl_before_image == jnl_info.before_images); csd->jnl_checksum = jnl_info.checksum; csd->jnl_eovtn = csd->trans_hist.curr_tn; send_msg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_NEWJNLFILECREAT, 2, JNL_LEN_STR(csd)); fc = gv_cur_region->dyn.addr->file_cntl; fc->op = FC_WRITE; udi = FC2UDI(fc); if (!udi->fd_opened_with_o_direct) fc->op_buff = (sm_uc_ptr_t)csd; else { memcpy((TREF(dio_buff)).aligned, csd, SGMNT_HDR_LEN); fc->op_buff = (sm_uc_ptr_t)(TREF(dio_buff)).aligned; } fc->op_len = SGMNT_HDR_LEN; fc->op_pos = 1; status = dbfilop(fc); if (SS_NORMAL != status) send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(gv_cur_region), status); assert(JNL_ENABLED(csa)); /* call jnl_ensure_open instead of jnl_file_open to make sure jpc->pini_addr is set to 0 */ jnl_status = jnl_ensure_open(gv_cur_region, csa); /* sets jpc->status */ if (0 != jnl_status) { if (jpc->status) rts_error_csa(CSA_ARG(csa) VARLSTCNT(7) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(gv_cur_region), jpc->status); else rts_error_csa(CSA_ARG(csa) VARLSTCNT(6) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(gv_cur_region)); } assert(jb->filesize == csd->jnl_alq); assert(!jb->last_eof_written); if (csd->jnl_alq + csd->jnl_deq <= csd->autoswitchlimit) { aligned_tot_jrec_size = ALIGNED_ROUND_UP(MAX_REQD_JNL_FILE_SIZE(total_jnl_rec_size), csd->jnl_alq, csd->jnl_deq); if (aligned_tot_jrec_size > csd->jnl_alq) { /* need to extend more than initial allocation in the new journal file * to accommodate the current transaction. */ new_blocks = aligned_tot_jrec_size - csd->jnl_alq; assert(new_blocks); assert(0 == new_blocks % csd->jnl_deq); need_extend = TRUE; } } } else { send_msg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_JNLNOCREATE, 2, JNL_LEN_STR(csd)); jpc->status = ERR_JNLNOCREATE; new_blocks = -1; } } else { assert(!need_extend); /* ensure we won't go through the for loop again */ /* Virtually extend currently used journal file */ jnl_fs_block_size = jb->fs_block_size; header = (jnl_file_header *)(ROUND_UP2((uintszofptr_t)hdr_buff, jnl_fs_block_size)); read_write_size = ROUND_UP2(REAL_JNL_HDR_LEN, jnl_fs_block_size); assert((unsigned char *)header + read_write_size <= ARRAYTOP(hdr_buff)); DO_FILE_READ(jpc->channel, 0, header, read_write_size, jpc->status, jpc->status2); if (SS_NORMAL != jpc->status) { assert(FALSE); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(5) ERR_JNLRDERR, 2, JNL_LEN_STR(csd), jpc->status); } assert((header->virtual_size + new_blocks) == new_alq); header->virtual_size = new_alq; JNL_DO_FILE_WRITE(csa, csd->jnl_file_name, jpc->channel, 0, header, read_write_size, jpc->status, jpc->status2); if (SS_NORMAL != jpc->status) { assert(WBTEST_RECOVER_ENOSPC == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(5) ERR_JNLWRERR, 2, JNL_LEN_STR(csd), jpc->status); } jb->filesize = new_alq; /* Actually this is virtual file size blocks */ } if (0 > new_blocks) break; } while (need_extend); if (0 <= new_blocks) { INCR_GVSTATS_COUNTER(csa, csa->nl, n_jnl_extends, 1); return EXIT_NRM; } if (SS_NORMAL == jpc->status) jpc->status = ERR_JNLREADEOF; jnl_file_lost(jpc, ERR_JNLEXTEND); return EXIT_ERR; } CONDITION_HANDLER(jnl_file_autoswitch_ch) { START_CH(TRUE); assert(in_jnl_file_autoswitch); in_jnl_file_autoswitch = FALSE; jgbl.dont_reset_gbl_jrec_time = jgbl.save_dont_reset_gbl_jrec_time; DEBUG_ONLY(jgbl.save_dont_reset_gbl_jrec_time = FALSE); NEXTCH; } fis-gtm-V7.0-005/sr_unix/jnl_file_open.c0000755000032200000250000001646314342376330017025 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_statvfs.h" #include #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_stat.h" #include "gtm_string.h" #include "util.h" #include "gtm_rename.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "eintr_wrappers.h" #include "gtmmsg.h" #include "iosp.h" /* for SS_NORMAL */ #include "gtmio.h" #include "sgtm_putmsg.h" #include "repl_sp.h" /* for F_CLOSE used by the JNL_FD_CLOSE macro */ #include "interlock.h" #include "lockconst.h" #include "aswp.h" #include "is_file_identical.h" #include "jnl_file_close_timer.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif #ifdef GTM_FD_TRACE #include "gtm_dbjnl_dupfd_check.h" #endif #include "wbox_test_init.h" #define SET_JPC_ERR_STR(ERR1, ERR2, BUF, BUF_LEN) \ { \ jpc->status = ERR2; \ sts = ERR1; \ sgtm_putmsg(BUF, BUF_LEN, VARLSTCNT(7) sts, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg), jpc->status); \ jpc->err_str = BUF; \ } GBLREF boolean_t is_src_server; GBLREF boolean_t mupip_jnl_recover; error_def(ERR_JNLFILOPN); error_def(ERR_JNLMOVED); error_def(ERR_JNLOPNERR); error_def(ERR_JNLRDERR); uint4 jnl_file_open(gd_region *reg, boolean_t init) { sgmnt_addrs *csa; sgmnt_data_ptr_t csd; jnl_private_control *jpc; jnl_buffer_ptr_t jb; struct stat stat_buf; uint4 sts; sm_uc_ptr_t nameptr; int fstat_res; int close_res; boolean_t switch_and_retry; char buff[OUT_BUFF_SIZE]; ZOS_ONLY(int realfiletag;) csa = &FILE_INFO(reg)->s_addrs; csd = csa->hdr; jpc = csa->jnl; jb = jpc->jnl_buff; assert(NOJNL == jpc->channel); sts = 0; jpc->status = jpc->status2 = SS_NORMAL; assert(NULL == jpc->err_str); nameptr = csd->jnl_file_name; assert('/' == csd->jnl_file_name[0]); if (init) { assert(csd->jnl_file_len < JNL_NAME_SIZE); /* csd->jnl_file_name[] is of size JNL_NAME_SIZE and must not change across GT.M versions hence the below asserts */ assert(JNL_NAME_SIZE == ARRAYSIZE(csd->jnl_file_name)); assert(256 == JNL_NAME_SIZE); nameptr[csd->jnl_file_len] = 0; cre_jnl_file_intrpt_rename(csa); /* although "jnl_file_close" would have reset jnl_file.u.inode and device to 0 and incremented cycle, it * might have got shot in the middle of executing those instructions. we redo it here just to be safe. */ csa->nl->jnl_file.u.inode = 0; csa->nl->jnl_file.u.device = 0; jb->cycle++; /* Source Server only reads journal files so must never try to create and switch to a new journal file. */ switch_and_retry = (!is_src_server); for (;;) { /* D9E04-002445 MUPIP RECOVER always open journal file without O_SYNC, ignoring jnl_sync_io */ if (csd->jnl_sync_io && !mupip_jnl_recover) { OPENFILE_SYNC_CLOEXEC((sm_c_ptr_t)csd->jnl_file_name, O_RDWR, jpc->channel); jpc->sync_io = TRUE; } else { OPENFILE_CLOEXEC((sm_c_ptr_t)csd->jnl_file_name, O_RDWR, jpc->channel); jpc->sync_io = FALSE; } /* Check that if ever open errors out (i.e. return status is FD_INVALID=-1), * jpc->channel will be already set to NOJNL (which is also defined to be -1). */ assert(FD_INVALID == NOJNL); if (FD_INVALID == jpc->channel) { jpc->status = errno; sts = ERR_JNLFILOPN; if (EACCES == jpc->status || EROFS == jpc->status) break; } else { ZOS_ONLY(gtm_zos_tag_to_policy(jpc->channel, TAG_BINARY, &realfiletag);) FSTAT_FILE(jpc->channel, &stat_buf, fstat_res); if (-1 == fstat_res) { jpc->status = errno; assert(FALSE); sts = ERR_JNLRDERR; JNL_FD_CLOSE(jpc->channel, close_res); /* sets jpc->channel to NOJNL */ break; } else sts = jnl_file_open_common(reg, (off_jnl_t) stat_buf.st_size, buff, OUT_BUFF_SIZE); } # ifdef DEBUG /* Will fail if Source Server would need to switch journal files. */ assert((gtm_white_box_test_case_enabled && (WBTEST_JNL_SWITCH_EXPECTED == gtm_white_box_test_case_number)) || (0 == sts) || (!is_src_server)); # endif if ((0 != sts) && switch_and_retry) { /* Switch to a new journal file and retry, but only once */ sts = jnl_file_open_switch(reg, sts, buff, OUT_BUFF_SIZE); if (0 == sts) { switch_and_retry = FALSE; continue; } } break; } } else /* not init */ { assert(0 == nameptr[csd->jnl_file_len]); ASSERT_JNLFILEID_NOT_NULL(csa); /* D9E04-002445 MUPIP RECOVER always open journal file without O_SYNC, ignoring jnl_sync_io */ if (csd->jnl_sync_io && !mupip_jnl_recover) { OPENFILE_SYNC_CLOEXEC((sm_c_ptr_t)nameptr, O_RDWR, jpc->channel); jpc->sync_io = TRUE; } else { OPENFILE_CLOEXEC((sm_c_ptr_t)nameptr, O_RDWR, jpc->channel); jpc->sync_io = FALSE; } /* Check that if ever open errors out (i.e. return status is FD_INVALID=-1, * jpc->channel will be already set to NOJNL (which is also defined to be -1). */ assert(FD_INVALID == NOJNL); if (FD_INVALID == jpc->channel) { jpc->status = errno; sts = ERR_JNLFILOPN; } else { FSTAT_FILE(jpc->channel, &stat_buf, fstat_res); ZOS_ONLY(gtm_zos_tag_to_policy(jpc->channel, TAG_BINARY, &realfiletag);) } } /* if init */ if (0 == sts) { if (0 == fstat_res) { if (init || is_gdid_stat_identical(&csa->nl->jnl_file.u, &stat_buf)) { if (jnl_closed == csd->jnl_state) { /* Operator close came in while opening */ JNL_FD_CLOSE(jpc->channel, close_res); /* sets jpc->channel to NOJNL */ jpc->fileid.inode = 0; jpc->fileid.device = 0; jpc->pini_addr = 0; } else { if (init) { /* Stash the file id in shared-memory for subsequent users */ set_gdid_from_stat(&csa->nl->jnl_file.u, &stat_buf); } GTM_WHITE_BOX_TEST(WBTEST_JNL_FILE_OPEN_FAIL, sts, ERR_JNLFILOPN); DEBUG_ONLY(if (0 == sts)) jpc->cycle = jb->cycle; /* make private cycle and shared cycle in sync */ GTM_FD_TRACE_ONLY( gtm_dbjnl_dupfd_check(); /* Check if db or jnl fds collide (D9I11-002714) */ /* The dupfd check above should not reset our channel. */ assertpro(NOJNL != jpc->channel); ) START_JNL_FILE_CLOSE_TIMER_IF_NEEDED; } /* if jnl_state */ } else { /* not init and file moved */ SET_JPC_ERR_STR(ERR_JNLOPNERR, ERR_JNLMOVED, buff, OUT_BUFF_SIZE); assert(gtm_white_box_test_case_enabled && ((WBTEST_JNLOPNERR_EXPECTED == gtm_white_box_test_case_number) || (WBTEST_JNL_CREATE_FAIL == gtm_white_box_test_case_number))); } } else { /* stat failed */ SET_JPC_ERR_STR(ERR_JNLFILOPN, errno, buff, OUT_BUFF_SIZE); } } /* sts == 0 when we started */ if (0 != sts) { if (NOJNL != jpc->channel) JNL_FD_CLOSE(jpc->channel, close_res); /* sets jpc->channel to NOJNL */ assert(NOJNL == jpc->channel); jnl_send_oper(jpc, sts); } jpc->err_str = NULL; assert((0 != sts) || (NOJNL != jpc->channel)); return sts; } fis-gtm-V7.0-005/sr_unix/jnl_fsync.c0000644000032200000250000001247314342376330016201 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_unistd.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsbgtr.h" #include "filestruct.h" #include "iosp.h" #include "jnl.h" #include "aswp.h" #include "interlock.h" #include "sleep_cnt.h" #include "eintr_wrappers.h" #include "performcaslatchcheck.h" #include "send_msg.h" #include "wcs_sleep.h" #include "is_file_identical.h" #include "gtm_string.h" #include "gtm_c_stack_trace.h" #include "gtmsecshr.h" /* for continue_proc */ #include "anticipatory_freeze.h" #include "wbox_test_init.h" #ifdef DEBUG #include "gt_timer.h" #include "gtm_stdio.h" #include "is_proc_alive.h" #endif GBLREF uint4 process_id; GBLREF jnl_gbls_t jgbl; error_def(ERR_JNLFSYNCERR); error_def(ERR_JNLFSYNCLSTCK); error_def(ERR_TEXT); void jnl_fsync(gd_region *reg, uint4 fsync_addr) { jnl_private_control *jpc; jnl_buffer_ptr_t jb; uint4 lcnt, saved_dsk_addr, saved_status; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; int4 lck_state; int fsync_ret, save_errno; intrpt_state_t prev_intrpt_state; DEBUG_ONLY(uint4 onln_rlbk_pid;) csa = &FILE_INFO(reg)->s_addrs; jpc = csa->jnl; jb = jpc->jnl_buff; assert(process_id != CURRENT_JNL_FSYNC_WRITER(jb)); if ((NOJNL != jpc->channel) && !JNL_FILE_SWITCHED(jpc)) { csd = csa->hdr; for (lcnt = 1; fsync_addr > jb->fsync_dskaddr && !JNL_FILE_SWITCHED(jpc); lcnt++) { /* should use a mutex */ if (0 == (lcnt % FSYNC_WAIT_HALF_TIME)) { saved_status = jpc->status; jpc->status = SS_NORMAL; # ifdef DEBUG if (CURRENT_JNL_FSYNC_WRITER(jb)) GET_C_STACK_FROM_SCRIPT("JNLFSYNCSTUCK_HALF_TIME", process_id, CURRENT_JNL_FSYNC_WRITER(jb), lcnt / FSYNC_WAIT_HALF_TIME); # endif jnl_send_oper(jpc, ERR_JNLFSYNCLSTCK); jpc->status = saved_status; } else if (0 == (lcnt % FSYNC_WAIT_TIME)) { saved_status = jpc->status; jpc->status = SS_NORMAL; if (CURRENT_JNL_FSYNC_WRITER(jb)) GET_C_STACK_FROM_SCRIPT("JNLFSYNCSTUCK", process_id, CURRENT_JNL_FSYNC_WRITER(jb), lcnt / FSYNC_WAIT_HALF_TIME); jnl_send_oper(jpc, ERR_JNLFSYNCLSTCK); jpc->status = saved_status; } BG_TRACE_PRO_ANY(csa, n_jnl_fsync_tries); if (GET_SWAPLOCK(&jb->fsync_in_prog_latch)) { DEFER_INTERRUPTS(INTRPT_IN_JNL_FSYNC, prev_intrpt_state); break; } wcs_sleep(lcnt); /* trying to wake up the lock holder one iteration before calling c_script */ if ((lcnt % FSYNC_WAIT_HALF_TIME) == (FSYNC_WAIT_HALF_TIME - 1)) performCASLatchCheck(&jb->fsync_in_prog_latch, TRUE); } # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_EXTEND_JNL_FSYNC == gtm_white_box_test_case_number)) { FPRINTF(stderr, "JNL_FSYNC: will sleep for 40 seconds\n", process_id); LONG_SLEEP(40); FPRINTF(stderr, "JNL_FSYNC: done sleeping\n", process_id); gtm_white_box_test_case_enabled = FALSE; } # endif if (fsync_addr > jb->fsync_dskaddr && !JNL_FILE_SWITCHED(jpc)) { assert(process_id == CURRENT_JNL_FSYNC_WRITER(jb)); /* assert we have the lock */ saved_dsk_addr = jb->dskaddr; if (jpc->sync_io) { /* We need to maintain the fsync control fields irrespective of the type of IO, because we might * switch between these at any time. */ jb->fsync_dskaddr = saved_dsk_addr; } else { /* If a concurrent online rollback is running, we should never be here since online rollback at the * start flushes all the dirty buffers and ensures that the journal buffers are all synced to disk. * So, there is no need for GT.M processes to reach here with a concurrent online rollback. Assert * to that effect. */ DEBUG_ONLY(onln_rlbk_pid = csa->nl->onln_rlbk_pid); assert(jgbl.onlnrlbk || !onln_rlbk_pid || !is_proc_alive(onln_rlbk_pid, 0) || (onln_rlbk_pid != csa->nl->in_crit)); GTM_JNL_FSYNC(csa, jpc->channel, fsync_ret); GTM_WHITE_BOX_TEST(WBTEST_FSYNC_SYSCALL_FAIL, fsync_ret, -1); WBTEST_ASSIGN_ONLY(WBTEST_FSYNC_SYSCALL_FAIL, errno, EIO); if (-1 == fsync_ret) { save_errno = errno; assert(WBTEST_ENABLED(WBTEST_FSYNC_SYSCALL_FAIL)); RELEASE_SWAPLOCK(&jb->fsync_in_prog_latch); ENABLE_INTERRUPTS(INTRPT_IN_JNL_FSYNC, prev_intrpt_state); send_msg_csa(CSA_ARG(csa) VARLSTCNT(9) ERR_JNLFSYNCERR, 2, JNL_LEN_STR(csd), ERR_TEXT, 2, RTS_ERROR_TEXT("Error with fsync"), save_errno); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(9) ERR_JNLFSYNCERR, 2, JNL_LEN_STR(csd), ERR_TEXT, 2, RTS_ERROR_TEXT("Error with fsync"), save_errno); } else { jb->fsync_dskaddr = saved_dsk_addr; BG_TRACE_PRO_ANY(csa, n_jnl_fsyncs); } } } if (process_id == CURRENT_JNL_FSYNC_WRITER(jb)) { RELEASE_SWAPLOCK(&jb->fsync_in_prog_latch); ENABLE_INTERRUPTS(INTRPT_IN_JNL_FSYNC, prev_intrpt_state); } } assert(process_id != CURRENT_JNL_FSYNC_WRITER(jb)); return; } fis-gtm-V7.0-005/sr_unix/jnl_output_sp.c0000644000032200000250000004016114342376330017114 0ustar librarygtc/*************************************************************** * * * Copyright (c) 2001-2018 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_unistd.h" /* DB_FSYNC macro needs this */ #include "gtm_string.h" #include "gtmio.h" /* this has to come in before gdsfhead.h, for all "open" to be defined to "open64", including the open in header files */ #include "aswp.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "gt_timer.h" #include "jnl.h" #include "lockconst.h" #include "interlock.h" #include "iosp.h" #include "gdsbgtr.h" #include "is_file_identical.h" #include "dpgbldir.h" #include "gtm_rel_quant.h" #include "repl_sp.h" /* for F_CLOSE used by the JNL_FD_CLOSE macro */ #include "memcoherency.h" #include "gtm_dbjnl_dupfd_check.h" #include "anticipatory_freeze.h" GBLREF volatile int4 db_fsync_in_prog; GBLREF volatile int4 jnl_qio_in_prog; GBLREF uint4 process_id; GBLREF jnlpool_addrs_ptr_t jnlpool; error_def(ERR_DBFSYNCERR); error_def(ERR_ENOSPCQIODEFER); error_def(ERR_JNLACCESS); error_def(ERR_JNLCNTRL); error_def(ERR_JNLRDERR); error_def(ERR_JNLWRTDEFER); error_def(ERR_JNLWRTNOWWRTR); error_def(ERR_PREMATEOF); uint4 jnl_sub_qio_start(jnl_private_control *jpc, boolean_t aligned_write); /* If the second argument is TRUE, then the jnl write is done only upto the previous aligned boundary. * else the write is done upto the freeaddr */ uint4 jnl_sub_qio_start(jnl_private_control *jpc, boolean_t aligned_write) { boolean_t was_wrapped; int tsz, close_res; jnl_buffer_ptr_t jb; int4 free_ptr; sgmnt_addrs *csa; node_local_ptr_t cnl; sm_uc_ptr_t base; unix_db_info *udi; unsigned int status; int save_errno; uint4 aligned_dskaddr, dskaddr; uint4 jnl_wrt_start_mask; int4 aligned_dsk, dsk; int aligned_tsz; sm_uc_ptr_t aligned_base; uint4 jnl_fs_block_size, new_dsk, new_dskaddr; gd_region *reg; intrpt_state_t prev_intrpt_state; assert(NULL != jpc); reg = jpc->region; udi = FILE_INFO(reg); csa = &udi->s_addrs; jb = jpc->jnl_buff; if (jb->io_in_prog_latch.u.parts.latch_pid == process_id) /* We already have the lock? */ return ERR_JNLWRTNOWWRTR; /* timer driven io in progress */ DEFER_INTERRUPTS(INTRPT_IN_JNL_QIO, prev_intrpt_state); if (!GET_SWAPLOCK(&jb->io_in_prog_latch)) { ENABLE_INTERRUPTS(INTRPT_IN_JNL_QIO, prev_intrpt_state); return ERR_JNLWRTDEFER; } if (IS_REPL_INST_FROZEN) { RELEASE_SWAPLOCK(&jb->io_in_prog_latch); ENABLE_INTERRUPTS(INTRPT_IN_JNL_QIO, prev_intrpt_state); return ERR_JNLWRTDEFER; } # ifdef DEBUG /* When jnl_sub_qio_start() is called as part of WBTEST_SIGTSTP_IN_JNL_OUTPUT_SP white-box test case, * aligned_write should always be FALSE. But depending upon the filesystem block size, it is possible that * the function could also be called with aligned_write being TRUE. This could lead to sending SIGTSTP * twice. Hence ensure that SIGTSTP is sent only for the unaligned write. */ if (gtm_white_box_test_case_enabled && (WBTEST_SIGTSTP_IN_JNL_OUTPUT_SP == gtm_white_box_test_case_number) && !aligned_write) kill(process_id, SIGTSTP); # endif if (jb->dsk != (jb->dskaddr % jb->size)) { assert(gtm_white_box_test_case_enabled && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number)); RELEASE_SWAPLOCK(&jb->io_in_prog_latch); ENABLE_INTERRUPTS(INTRPT_IN_JNL_QIO, prev_intrpt_state); return ERR_JNLCNTRL; } if (!JNL_FILE_SWITCHED(jpc)) jpc->fd_mismatch = FALSE; else { /* journal file has been switched; release io_in_prog lock and return */ jpc->fd_mismatch = TRUE; RELEASE_SWAPLOCK(&jb->io_in_prog_latch); ENABLE_INTERRUPTS(INTRPT_IN_JNL_QIO, prev_intrpt_state); return SS_NORMAL; } /* Currently we overload io_in_prog_latch to perform the db fsync too. Anyone trying to do a * jnl_qio_start will first check if a db_fsync is needed and if so sync that before doing any jnl qio. * Note that since an epoch record is written when need_db_fsync is set to TRUE, we are guaranteed that * (dskaddr < freeaddr) which is necessary for the jnl_wait --> jnl_write_attempt mechanism (triggered * by wcs_flu) to actually initiate a call to jnl_qio_start(). */ if (jb->need_db_fsync) { DB_FSYNC(reg, udi, csa, db_fsync_in_prog, save_errno); GTM_WHITE_BOX_TEST(WBTEST_ANTIFREEZE_DBFSYNCERR, save_errno, EIO); if (0 != save_errno) { RELEASE_SWAPLOCK(&jb->io_in_prog_latch); ENABLE_INTERRUPTS(INTRPT_IN_JNL_QIO, prev_intrpt_state); /* DBFSYNCERR can potentially cause syslog flooding. Remove the following line if we it becomes an issue. */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFSYNCERR, 2, DB_LEN_STR(reg), save_errno); rts_error_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFSYNCERR, 2, DB_LEN_STR(reg), save_errno); assert(FALSE); /* should not come here as the rts_error above should not return */ return ERR_DBFSYNCERR; /* ensure we do not fall through to the code below as we no longer have the lock */ } jb->need_db_fsync = FALSE; } free_ptr = jb->free; /* The following barrier is to make sure that for the value of "free" that we extract (which may be * slightly stale but that is not a correctness issue) we make sure we don't write out a stale version of * the journal buffer contents. While it is possible that we see journal buffer contents that are more * uptodate than "free", this would only mean writing out a less than optimal number of bytes but again, * not a correctness issue. Secondary effect is that it also enforces a corresponding non-stale value of * freeaddr is read and this is relied upon by asserts below. */ SHM_READ_MEMORY_BARRIER; dsk = jb->dsk; dskaddr = jb->dskaddr; was_wrapped = (free_ptr < dsk); jnl_fs_block_size = jb->fs_block_size; base = &jb->buff[dsk + jb->buff_off]; aligned_base = (sm_uc_ptr_t)ROUND_DOWN2((uintszofptr_t)base, jnl_fs_block_size); assert(aligned_base >= &jb->buff[jb->buff_off]); aligned_dskaddr = ROUND_DOWN2(dskaddr, jnl_fs_block_size); if (jb->re_read_dskaddr) { /* Need to re-read the filesystem-block-size-aligned partial block before jb->dskaddr */ assert(jb->re_read_dskaddr == dskaddr); tsz = dskaddr - aligned_dskaddr; if (tsz) { /* Assert that both ends of the source buffer for the read falls within journal buffer limits */ assert(aligned_base >= &jb->buff[jb->buff_off]); assert(aligned_base + tsz <= &jb->buff[jb->buff_off + jb->size]); LSEEKREAD(jpc->channel, aligned_dskaddr, aligned_base, tsz, jpc->status); if (SS_NORMAL != jpc->status) { RELEASE_SWAPLOCK(&jb->io_in_prog_latch); ENABLE_INTERRUPTS(INTRPT_IN_JNL_QIO, prev_intrpt_state); jpc->status2 = SS_NORMAL; jnl_send_oper(jpc, ERR_JNLRDERR); rts_error_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_JNLRDERR, 2, JNL_LEN_STR(csa->hdr), jpc->status); assert(FALSE); /* should not come here as the rts_error above should not return */ return ERR_JNLRDERR; /* ensure we do not fall through to code below as we no longer have the lock */ } } jb->re_read_dskaddr = 0; } if (aligned_write) free_ptr = ROUND_DOWN2(free_ptr, jnl_fs_block_size); assert(!(jb->size % jnl_fs_block_size)); tsz = (free_ptr < dsk ? jb->size : free_ptr) - dsk; if ((aligned_write && !was_wrapped && (free_ptr <= dsk)) || (NOJNL == jpc->channel)) tsz = 0; assert(0 <= tsz); assert(dskaddr + tsz <= jb->freeaddr); status = SS_NORMAL; if (tsz) { /* ensure that dsk and free are never equal and we have left space for filesystem-aligned offset BEFORE dsk */ assert(SS_NORMAL == status); DEBUG_ONLY(jnl_wrt_start_mask = ~(jb->fs_block_size - 1);) assert((free_ptr > dsk) || (free_ptr < (dsk & jnl_wrt_start_mask)) || (dsk != (dsk & jnl_wrt_start_mask))); jb->wrtsize = tsz; jb->qiocnt++; assert(NOJNL != jpc->channel); /* If sync_io is turned on, we would have turned on the O_DIRECT flag on some platforms. That will * require us to do aligned writes. Both the source buffer and the size of the write need to be aligned * for this to work on some platforms. The alignment needs to be on a filesystem-block-size granularity. * If sync_io is not turned on, doing aligned writes saves us from the OS doing a read of the block * under the covers in case we write only a part of the filesystem block. * Therefore we do aligned writes no matter what. This means we could be writing some garbage padding * data out after the last valid journal record jut to fit in the alignment requirements. But that is * considered okay because as part of writing the EOF record out (for a clean termination), jnl_write * would have 0-padded the journal buffer for us. So a cleanly shutdown journal file will have 0-padding * following the EOF record but an actively used journal file might have garbage padding following the * last valid record. This is considered okay as journal recovery has logic to scan past the garbage and * locate the last valid record in case of a crash before writing the EOF. */ DEBUG_ONLY(aligned_dsk = ROUND_DOWN2(dsk, jnl_fs_block_size)); aligned_tsz = ROUND_UP2((tsz + (dskaddr - aligned_dskaddr)), jnl_fs_block_size); /* Assert that aligned_dsk never backs up to a point BEFORE where the free pointer is */ assert((aligned_dsk > free_ptr) || (dsk <= free_ptr)); /* Assert that aligned_dskaddr never backs up to a point inside journal file header territory. * This is because those fields are always updated inside crit and therefore we should * never touch those while we hold only the jnl qio lock. */ assert(JNL_HDR_LEN <= aligned_dskaddr); /* Assert that both ends of the source buffer for the write falls within journal buffer limits */ assert(aligned_base >= &jb->buff[jb->buff_off]); assert(aligned_base + aligned_tsz <= &jb->buff[jb->buff_off + jb->size]); JNL_LSEEKWRITE(csa, csa->hdr->jnl_file_name, jpc->channel, (off_t)aligned_dskaddr, aligned_base, (size_t)aligned_tsz, jpc->status); status = jpc->status; if (SS_NORMAL == status) { /* update jnl_buff pointers to reflect the successful write to the journal file */ assert(dsk <= jb->size); assert(jb->io_in_prog_latch.u.parts.latch_pid == process_id); new_dskaddr = dskaddr + tsz; new_dsk = dsk + tsz; if (new_dsk >= jb->size) { assert(new_dsk == jb->size); new_dsk = 0; } assert(new_dsk == new_dskaddr % jb->size); assert(jb->freeaddr >= new_dskaddr); /* Note: "wcs_flu" does a "performCASLatchCheck" of jb->io_in_prog_latch and relies * on "jb->dskaddr" being updated BEFORE "jb->dsk". */ jb->dskaddr = new_dskaddr; jb->dsk = new_dsk; cnl = csa->nl; INCR_GVSTATS_COUNTER(csa, cnl, n_jfile_bytes, aligned_tsz); INCR_GVSTATS_COUNTER(csa, cnl, n_jfile_writes, 1); } else { assert((ENOSPC == status) || (ERR_ENOSPCQIODEFER == status)); jb->errcnt++; if (ENOSPC == status) jb->enospc_errcnt++; else jb->enospc_errcnt = 0; if (ERR_ENOSPCQIODEFER != status) { jnl_send_oper(jpc, ERR_JNLACCESS); jpc->status = status; /* set jpc->status back to original error as jnl_send_oper resets * jpc->status to SS_NORMAL. We need it in callers of this function * (e.g. jnl_write_attempt). */ } # ifdef GTM_FD_TRACE if ((EBADF == status) || (ESPIPE == status)) { /* likely case of D9I11-002714. check if fd is valid */ gtm_dbjnl_dupfd_check(); /* If fd of this journal points to some other database or journal file opened by this process * the above call would have reset jpc->channel. If it did not get reset, then check * if the fd in itself is valid and points back to the journal file. If not reset it to NOJNL. */ if (NOJNL != jpc->channel) gtm_check_fd_is_valid(reg, FALSE, jpc->channel); /* If jpc->channel still did not get reset to NOJNL, it means the file descriptor is valid but * not sure why we are getting EBADF/ESPIPE errors. No further recovery attempted at this point. */ } # endif if (ERR_ENOSPCQIODEFER == status) status = ERR_JNLWRTDEFER; else status = ERR_JNLACCESS; } } RELEASE_SWAPLOCK(&jb->io_in_prog_latch); if ((jnl_closed == csa->hdr->jnl_state) && (NOJNL != jpc->channel)) { JNL_FD_CLOSE(jpc->channel, close_res); /* sets jpc->channel to NOJNL */ jpc->pini_addr = 0; } ENABLE_INTERRUPTS(INTRPT_IN_JNL_QIO, prev_intrpt_state); return status; } /* This is a wrapper for jnl_sub_qio_start that tries to divide the writes into optimal chunks. * It calls jnl_sub_qio_start() with appropriate arguments in two stages, the first one with * optimal "jnl_fs_block_size" boundary and the other suboptimal tail end of the write. The latter * call is made only if no other process has finished the jnl write upto the required point * during the time this process yields */ uint4 jnl_qio_start(jnl_private_control *jpc) { unsigned int yield_cnt, status; uint4 lcl_dskaddr, old_freeaddr, spin_sleep_mask, target_freeaddr; jnl_buffer_ptr_t jb; sgmnt_addrs *csa; unix_db_info *udi; uint4 jnl_fs_block_size; int index1; assert(NULL != jpc); udi = FILE_INFO(jpc->region); csa = &udi->s_addrs; jb = jpc->jnl_buff; /* this block of code (till yield()) processes the buffer upto an "jnl_fs_block_size" alignment boundary * and the next block of code (after the yield()) processes the tail end of the data (if necessary) */ lcl_dskaddr = jb->dskaddr; /* Check if there are any pending jnl phase2 commits that can be cleaned up. That will bring jb->freeaddr more uptodate. */ index1 = jb->phase2_commit_index1; ASSERT_JNL_PHASE2_COMMIT_INDEX_IS_VALID(index1, JNL_PHASE2_COMMIT_ARRAY_SIZE); if ((index1 != jb->phase2_commit_index2) && jb->phase2_commit_array[index1].write_complete && (LOCK_AVAILABLE == jb->phase2_commit_latch.u.parts.latch_pid)) jnl_phase2_cleanup(csa, jb); /* Now that any possible phase2 commit cleanup is done, go ahead with qio (if needed) using updated jb->freeaddr */ target_freeaddr = jb->freeaddr; if (lcl_dskaddr >= target_freeaddr) return SS_NORMAL; /* ROUND_DOWN2 macro is used under the assumption that "jnl_fs_block_size" would be a power of 2 */ jnl_fs_block_size = jb->fs_block_size; if (ROUND_DOWN2(lcl_dskaddr, jnl_fs_block_size) != ROUND_DOWN2(target_freeaddr, jnl_fs_block_size)) { /* data crosses/touches an alignment boundary */ if (SS_NORMAL != (status = jnl_sub_qio_start(jpc, TRUE))) return status; } /* else, data does not cross/touch an alignment boundary, yield and see if someone else * does the dirty job more efficiently */ spin_sleep_mask = csa->hdr->mutex_spin_parms.mutex_spin_sleep_mask; for (yield_cnt = 0; !csa->now_crit && (yield_cnt < csa->hdr->yield_lmt); yield_cnt++) { /* If not in crit, wait until someone has finished your job or no one else is active on the jnl file */ old_freeaddr = jb->freeaddr; GTM_REL_QUANT(spin_sleep_mask); /* Purpose of this memory barrier is to get a current view of asyncrhonously changed fields * like whether the jnl file was switched, the write position in the journal file and the * write address in the journal buffer for all the remaining statements in this loop because * the GTM_REL_QUANT invocation above allows any and all of them to change and we aren't under any * locks while in this loop. This is not a correctness issue as we would either eventually * see the updates or it means we are writing what has already been written. It is a performance * issue keeping more current with state changes done by other processes on other processors. */ SHM_READ_MEMORY_BARRIER; if (JNL_FILE_SWITCHED(jpc)) return SS_NORMAL; /* assert(old_freeaddr <= jb->freeaddr) ** Potential race condition with jnl file switch could * make this assert fail so it is removed */ if (old_freeaddr == jb->freeaddr || target_freeaddr <= jb->dskaddr) break; } status = SS_NORMAL; if (target_freeaddr > jb->dskaddr) status = jnl_sub_qio_start(jpc, FALSE); return status; } fis-gtm-V7.0-005/sr_unix/jnl_prc_vector.c0000755000032200000250000000470714342376330017231 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_pwd.h" #include "gtm_unistd.h" #include "gtm_stdio.h" #include "gtm_time.h" #include "gtm_string.h" #include #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" /* needed by *TYPEMASK* macros defined in gtm_utf8.h */ #include "gtm_utf8.h" #endif GBLREF uint4 process_id; GBLREF boolean_t gtm_utf8_mode; void jnl_prc_vector (jnl_process_vector *pv) { unsigned char *c, *s, *ptrstart, *ptrend, *ptrinvalidbegin; struct passwd *pw; uid_t eff_uid; /* try to see why gets pwuid == nil */ int gethostname_res; time_t temp_time; memset(pv, 0, SIZEOF(jnl_process_vector)); pv->jpv_pid = process_id; time(&temp_time); pv->jpv_time = temp_time; GETHOSTNAME(pv->jpv_node, JPV_LEN_NODE, gethostname_res); eff_uid = geteuid(); /* save results of each step */ errno = 0; /* force to known value per man page */ GETPWUID(eff_uid, pw); if (pw) { SNPRINTF(pv->jpv_user, JPV_LEN_USER, "%s", pw->pw_name); SNPRINTF(pv->jpv_prcnam, JPV_LEN_PRCNAM, "%s", pw->pw_name); # ifdef UTF8_SUPPORTED /* In UTF8 mode, trim the string (if necessary) to contain only as many valid multi-byte characters as can fit in */ if (gtm_utf8_mode) { gtm_utf8_trim_invalid_tail((unsigned char *)pv->jpv_user, JPV_LEN_USER); gtm_utf8_trim_invalid_tail((unsigned char *)pv->jpv_prcnam, JPV_LEN_PRCNAM); } # endif } else { # ifdef DEBUG SNPRINTF(pv->jpv_user, JPV_LEN_USER, "ERROR=%d", (errno < 1000) ? errno : 1000); # endif } endpwent(); /* close passwd file to free channel */ if ((c = (unsigned char *)TTYNAME(0)) != NULL) { /* Get basename for tty */ for (s = c + strlen((char*)c); s > c; --s) { if (*s == '/') { ++s; break; } } SNPRINTF(pv->jpv_terminal, JPV_LEN_TERMINAL, "%s", (char *)s); } } fis-gtm-V7.0-005/sr_unix/jnlext_merge_sort_prepare.c0000644000032200000250000000774514342376330021472 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015 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_string.h" #include "gtm_stdio.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "buddy_list.h" #include "jnl.h" #include "hashtab_mname.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "muprec.h" #include "io.h" #include "io_params.h" #include "op.h" #include "gtm_multi_proc.h" GBLREF mur_gbls_t murgbl; void jnlext_merge_sort_prepare(jnl_ctl_list *jctl, jnl_record *rec, enum broken_type recstat, int length) { reg_ctl_list *rctl; enum jnl_record_type rectype; jnlext_multi_t *jext_rec; boolean_t is_logical_rec, need_new_elem; forw_multi_struct *forw_multi; buddy_list *list; rctl = jctl->reg_ctl; assert(1 < murgbl.reg_total); /* caller should have ensured this */ jext_rec = rctl->last_jext_rec[recstat]; if (NULL != rec) { assert(rec == rctl->mur_desc->jnlrec); rectype = (enum jnl_record_type)rec->prefix.jrec_type; is_logical_rec = (IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(rectype) || IS_COM(rectype)); need_new_elem = (is_logical_rec || (NULL == jext_rec) || rctl->last_jext_logical_rec[recstat] || (jext_rec->time != rec->prefix.time)); assert(need_new_elem || (NULL != rctl->jnlext_multi_list[recstat])); } else need_new_elem = FALSE; if (need_new_elem) { list = rctl->jnlext_multi_list[recstat]; if (NULL == list) { list = (buddy_list *)malloc(SIZEOF(buddy_list)); initialize_list(list, SIZEOF(jnlext_multi_t), MUR_JNLEXT_LIST_INIT_ELEMS); rctl->jnlext_multi_list[recstat] = list; } jext_rec = (jnlext_multi_t *)get_new_element(list, 1); rctl->jnlext_multi_list_size[recstat]++; jext_rec->time = rec->prefix.time; if (is_logical_rec) { jext_rec->token_seq = rec->jrec_set_kill.token_seq; if (!IS_COM(rectype)) jext_rec->update_num = rec->jrec_set_kill.update_num * 2 + (IS_ZTWORM(rectype) ? 0 : 1); else jext_rec->update_num = MAXUINT4; forw_multi = rctl->forw_multi; if ((NULL != forw_multi) && (repl_closed == rctl->repl_state)) { assert(!IS_TUPD(rectype) || (1 < rec->jrec_set_kill.num_participants) && (forw_multi->num_reg_seen_backward <= rec->jrec_set_kill.num_participants)); assert(!IS_COM(rectype) || (forw_multi->num_reg_seen_backward <= rec->jrec_tcom.num_participants)); jext_rec->num_more_reg = IS_TUPD(rectype) ? forw_multi->num_reg_seen_backward - 1 : 0; } else jext_rec->num_more_reg = 0; } else { jext_rec->token_seq.token = 0; jext_rec->update_num = 0; jext_rec->num_more_reg = 0; } jext_rec->size = length; rctl->last_jext_rec[recstat] = jext_rec; /* Store this for next call to "jnlext_write" in case of need */ rctl->last_jext_logical_rec[recstat] = is_logical_rec; } else { assert(rctl->jnlext_multi_list_size[recstat]); assert(jext_rec == (jnlext_multi_t *)find_element(rctl->jnlext_multi_list[recstat], rctl->jnlext_multi_list_size[recstat] - 1)); jext_rec->size += length; /* Tag this record along with previous extract record */ } # ifdef MUR_DEBUG jext_rec = rctl->last_jext_rec[recstat]; fprintf(stderr, "%s : list size = %d : time = %d : token_seq = %lld : update_num = %u : num_reg = %d : " "size = %lld\n", rctl->gd->rname, rctl->jnlext_multi_list_size[recstat], jext_rec->time, (long long int)jext_rec->token_seq.token, jext_rec->update_num, jext_rec->num_more_reg, (long long int)jext_rec->size); # endif } fis-gtm-V7.0-005/sr_unix/jnlext_write.c0000755000032200000250000000507214342376330016732 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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_string.h" #include "gtm_stdio.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "buddy_list.h" #include "jnl.h" #include "hashtab_mname.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "muprec.h" #include "io.h" #include "io_params.h" #include "op.h" #include "gtm_multi_proc.h" GBLREF io_pair io_curr_device; GBLREF mstr sys_output; GBLREF mur_gbls_t murgbl; static readonly unsigned char open_params_list[] = { (unsigned char)iop_stream, (unsigned char)iop_nowrap, (unsigned char)iop_eol }; /* Due to historic reasons, *buffer must be terminated with '\\' character (or something else). (length-1) of the buffer will be * written to the file or STDOUT which means '\\' will be ignored here. */ void jnlext_write(jnl_ctl_list *jctl, jnl_record *rec, enum broken_type recstat, char *buffer, int length) { mval op_val, op_pars; io_pair dev_in_use; fi_type *file_info; reg_ctl_list *rctl; rctl = jctl->reg_ctl; assert(buffer[length - 1] == '\\'); dev_in_use = io_curr_device; op_val.mvtype = MV_STR; file_info = rctl->file_info[recstat]; if (NULL != file_info) { /* Output to a file */ op_val.str.addr = (char *)file_info->fn; op_val.str.len = file_info->fn_len; } else { /* Output to stdout */ assert(1 == murgbl.reg_total); assert(sys_output.len > 0); assert(sys_output.addr); op_val.str = sys_output; } op_pars.str.len = SIZEOF(open_params_list); op_pars.str.addr = (char *)open_params_list; op_pars.mvtype = MV_STR; op_use(&op_val, &op_pars); op_val.mvtype = MV_STR; op_val.str.addr = (char *)buffer; op_val.str.len = length - 1; op_write(&op_val); op_wteol(1); io_curr_device = dev_in_use; if (1 < murgbl.reg_total) { /* We need to do merge-sort of extract files created for multiple regions later in "mur_cre_merge_extfile". * Prepare for it below. */ jnlext_merge_sort_prepare(jctl, rec, recstat, length); } } fis-gtm-V7.0-005/sr_unix/jnlpool_init.c0000644000032200000250000017543414342376330016723 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_ipc.h" #include #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_inet.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include #include #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" #include "gtm_logicals.h" #include "mutex.h" #include "jnl.h" #include "repl_sem.h" #include "gtmimagename.h" #include "io.h" #include "do_shmat.h" #include "repl_instance.h" #include "mu_gv_cur_reg_init.h" #include "gtmmsg.h" #include "gtm_sem.h" #include "ipcrmid.h" #include "ftok_sems.h" #include "is_proc_alive.h" #include "repl_shutdcode.h" #include "send_msg.h" #include "lockconst.h" #include "anticipatory_freeze.h" #include "have_crit.h" #include "gtmsource_srv_latch.h" #include "util.h" /* For OUT_BUFF_SIZE */ #include "repl_inst_ftok_counter_halted.h" #include "eintr_wrapper_semop.h" #include "is_file_identical.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnlpool_addrs_ptr_t jnlpool_head; GBLREF recvpool_addrs recvpool; GBLREF uint4 process_id; GBLREF gd_region *gv_cur_region; GBLREF gtmsource_options_t gtmsource_options; GBLREF gtmrecv_options_t gtmrecv_options; GBLREF int pool_init; GBLREF seq_num seq_num_zero; GBLREF enum gtmImageTypes image_type; GBLREF node_local_ptr_t locknl; GBLREF uint4 log_interval; GBLREF boolean_t is_updproc; GBLREF uint4 mutex_per_process_init_pid; GBLREF repl_conn_info_t *this_side, *remote_side; GBLREF int4 strm_index; GBLREF is_anticipatory_freeze_needed_t is_anticipatory_freeze_needed_fnptr; GBLREF set_anticipatory_freeze_t set_anticipatory_freeze_fnptr; GBLREF err_ctl merrors_ctl; GBLREF jnl_gbls_t jgbl; GBLREF gd_addr *gd_header; GBLREF char repl_instfilename[]; GBLREF char repl_inst_name[]; GBLREF gd_addr *repl_inst_from_gld; GBLREF boolean_t in_repl_inst_edit; #ifdef DEBUG GBLREF uint4 is_updhelper; #endif LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; error_def(ERR_ACTIVATEFAIL); error_def(ERR_JNLPOOLBADSLOT); error_def(ERR_JNLPOOLSETUP); error_def(ERR_NOJNLPOOL); error_def(ERR_PRIMARYISROOT); error_def(ERR_PRIMARYNOTROOT); error_def(ERR_REPLINSTACC); error_def(ERR_REPLINSTNMSAME); error_def(ERR_REPLINSTNOHIST); error_def(ERR_REPLINSTSECNONE); error_def(ERR_REPLINSTSEQORD); error_def(ERR_REPLREQROLLBACK); error_def(ERR_REPLREQRUNDOWN); error_def(ERR_REPLWARN); error_def(ERR_SRCSRVEXISTS); error_def(ERR_SRCSRVNOTEXIST); error_def(ERR_SRCSRVTOOMANY); error_def(ERR_TEXT); #define REMOVE_OR_RELEASE_SEM(NEW_IPC) \ { \ if (!skip_locks) \ { \ if (NEW_IPC) \ remove_sem_set(SOURCE); \ else \ rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); \ } \ } #define DETACH_FROM_JNLPOOL_IF_NEEDED(JNLPOOL, RTS_ERROR_OR_GTM_PUTMSG) \ { \ int status, save_errno; \ \ if ((NULL != JNLPOOL) && (NULL != JNLPOOL->jnlpool_ctl)) \ { \ JNLPOOL_SHMDT(jnlpool, status, save_errno); \ if (0 > status) \ RTS_ERROR_OR_GTM_PUTMSG(CSA_ARG(NULL) VARLSTCNT(5) ERR_REPLWARN, 2, \ RTS_ERROR_LITERAL("Could not detach from journal pool"), save_errno); \ } \ } #define DETACH_AND_REMOVE_SHM_AND_SEM(JNLPOOL) \ { \ if (new_ipc) \ { \ assert(!IS_GTM_IMAGE); /* Since "gtm_putmsg" is done below ensure it is never GT.M */ \ assert(NULL != JNLPOOL); \ DETACH_FROM_JNLPOOL_IF_NEEDED(JNLPOOL, gtm_putmsg_csa); \ assert(INVALID_SHMID != udi->shmid); \ if (0 != shm_rmid(udi->shmid)) \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, \ RTS_ERROR_LITERAL("Error removing jnlpool "), errno); \ remove_sem_set(SOURCE); \ } \ } #define CHECK_SLOT(gtmsourcelocal_ptr) \ { \ if ((GTMSOURCE_DUMMY_STATE != gtmsourcelocal_ptr->gtmsource_state) || (0 != gtmsourcelocal_ptr->gtmsource_pid)) \ { /* Slot is in an out-of-design situation. Send an operator log message with enough debugging detail */ \ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_JNLPOOLBADSLOT, 5, \ LEN_AND_STR((char *)gtmsourcelocal_ptr->secondary_instname), \ gtmsourcelocal_ptr->gtmsource_pid, gtmsourcelocal_ptr->gtmsource_state, \ gtmsourcelocal_ptr->gtmsrc_lcl_array_index); \ } \ } #define RELEASE_NEW_TMP_JNLPOOL(HAVETMPJPL, TMPJPL, DUMMYREG, JPL, SAVEJPL, GVCURREGION) \ { \ gd_region *save_cur_reg; \ \ if (HAVETMPJPL) \ { \ if (DUMMYREG) \ { \ save_cur_reg = GVCURREGION; \ GVCURREGION = TMPJPL->jnlpool_dummy_reg; \ mu_gv_cur_reg_free(); \ GVCURREGION = save_cur_reg; \ } \ JPL = SAVEJPL; \ free(TMPJPL); \ } \ } void jnlpool_init(jnlpool_user pool_user, boolean_t gtmsource_startup, boolean_t *jnlpool_creator, gd_addr *gd_ptr) { boolean_t hold_onto_ftok_sem, is_src_srvr, new_ipc, reset_gtmsrclcl_info, slot_needs_init, srv_alive; boolean_t cannot_activate, ftok_counter_halted, skip_locks, new_tmp_jnlpool, new_dummy_reg, gdid_matched; char instfilename[MAX_FN_LEN + 1], machine_name[MAX_MCNAMELEN], scndry_msg[OUT_BUFF_SIZE]; gd_region *r_save, *reg; int status, save_errno; int4 index; union semun semarg; struct semid_ds semstat; struct shmid_ds shmstat; repl_inst_hdr repl_instance; sm_long_t status_l; unsigned int full_len; mutex_spin_parms_ptr_t jnlpool_mutex_spin_parms; unix_db_info *udi; gd_id instfilename_gdid; sgmnt_addrs *csa, *csa_save; gd_segment *seg; gd_addr *repl_gld, *local_gdptr; gtmsrc_lcl_ptr_t gtmsrclcl_ptr; gtmsource_local_ptr_t gtmsourcelocal_ptr, reuse_slot_ptr; uint4 gtmsource_pid, gtmrecv_pid; gtmsource_state_t gtmsource_state; seq_num reuse_slot_seqnum, instfilehdr_seqno; repl_histinfo last_histinfo; jnlpool_ctl_ptr_t tmp_jnlpool_ctl; jnlpool_addrs_ptr_t tmp_jnlpool, last_jnlpool, save_jnlpool; struct sembuf sop[3]; uint4 sopcnt; DEBUG_ONLY(int4 semval); DEBUG_ONLY(boolean_t sem_created = FALSE); DEBUG_ONLY(int i); DEBUG_ONLY(char *ptr); DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(gtmsource_startup == gtmsource_options.start); skip_locks = (gtmsource_options.setfreeze && (gtmsource_options.freezeval == FALSE)) || gtmsource_options.showfreeze; memset(machine_name, 0, SIZEOF(machine_name)); if (GETHOSTNAME(machine_name, MAX_MCNAMELEN, status)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_TEXT, 2, RTS_ERROR_TEXT("Unable to get the hostname"), errno); save_jnlpool = jnlpool; /* so can be restored if error */ local_gdptr = gd_ptr ? gd_ptr : gd_header; /* note gd_header may be NULL */ /* note embedded assignment below */ assertpro(repl_gld = (gd_addr *)repl_inst_get_name(instfilename, &full_len, MAX_FN_LEN + 1, issue_rts_error, gd_ptr)); status = filename_to_id(&instfilename_gdid, instfilename); if (SS_NORMAL != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_REPLINSTACC, 2, full_len, instfilename, ERR_TEXT, 2, RTS_ERROR_LITERAL("could not get file id"), status); /* look through jnlpool_head list for matching instfilename */ for (tmp_jnlpool = jnlpool_head, gdid_matched = FALSE; tmp_jnlpool; tmp_jnlpool = tmp_jnlpool->next) { if (NULL != tmp_jnlpool->jnlpool_dummy_reg) { reg = tmp_jnlpool->jnlpool_dummy_reg; udi = FILE_INFO(reg); if (is_gdid_identical(&instfilename_gdid, &udi->fileid)) { gdid_matched = TRUE; break; } } } if ((NULL == tmp_jnlpool) || (tmp_jnlpool->pool_init && !gdid_matched)) { /* no jnlpool for instfilename or in use jnlpool not matching so get a new one */ tmp_jnlpool = malloc(SIZEOF(jnlpool_addrs)); memset((uchar_ptr_t)tmp_jnlpool, 0, SIZEOF(jnlpool_addrs)); tmp_jnlpool->relaxed = (GTMRELAXED == pool_user); new_tmp_jnlpool = TRUE; } else new_tmp_jnlpool = FALSE; /* tmp_jnlpool may be new, in use, not in use but different instance */ /* mupip_backup assumes jnlpool not NULL after jnlpool_init */ new_dummy_reg = FALSE; if (!tmp_jnlpool->pool_init && (NULL != recvpool.recvpool_dummy_reg) && !STRCMP(recvpool.recvpool_dummy_reg->dyn.addr->fname, instfilename)) { /* Have already attached to the receive pool and this jnlpool is for the same instance. * Use the receive pool region for the journal pool as well as they * both correspond to one and the same file (replication instance file). We need to do this to ensure that an * "ftok_sem_get" done with either "jnlpool->jnlpool_dummy_reg" region or "recvpool.recvpool_dummy_reg" region * locks the same entity. */ assert(is_updproc || ((GTMRELAXED == pool_user) && is_updhelper)); reg = recvpool.recvpool_dummy_reg; } else if (!tmp_jnlpool->pool_init) { /* reuse uninitialized jnlpool */ r_save = gv_cur_region; if ((NULL != tmp_jnlpool->jnlpool_dummy_reg) && (tmp_jnlpool->jnlpool_dummy_reg != recvpool.recvpool_dummy_reg)) { /* free jnlpool_dummy_reg if not in recvpool dummy reg */ gv_cur_region = tmp_jnlpool->jnlpool_dummy_reg; mu_gv_cur_reg_free(); } mu_gv_cur_reg_init(); reg = gv_cur_region; gv_cur_region = r_save; new_dummy_reg = TRUE; ASSERT_IN_RANGE(MIN_RN_LEN, SIZEOF(JNLPOOL_DUMMY_REG_NAME) - 1, MAX_RN_LEN); MEMCPY_LIT(reg->rname, JNLPOOL_DUMMY_REG_NAME); reg->rname_len = STR_LIT_LEN(JNLPOOL_DUMMY_REG_NAME); reg->rname[reg->rname_len] = 0; } else reg = tmp_jnlpool->jnlpool_dummy_reg; assert(NULL != reg); tmp_jnlpool->jnlpool_dummy_reg = reg; tmp_jnlpool->recv_pool = (tmp_jnlpool->jnlpool_dummy_reg == recvpool.recvpool_dummy_reg); udi = FILE_INFO(reg); csa = &udi->s_addrs; seg = reg->dyn.addr; assert(!udi->s_addrs.hold_onto_crit); /* so that we can do unconditional grab_locks and rel_locks */ assert(NULL != repl_gld); /* rts_error should have been issued by repl_inst_get_name if error */ if (!tmp_jnlpool->pool_init) { if (IS_INST_FROM_GLD(repl_gld)) { /* instance file from global directory used */ if (NULL == tmp_jnlpool->gd_ptr) { tmp_jnlpool->gd_ptr = repl_gld; tmp_jnlpool->gd_instinfo = repl_gld->instinfo; } else assert(repl_gld == tmp_jnlpool->gd_ptr); } else if (NULL == tmp_jnlpool->gd_ptr) { /* instance file from gtm_repl_instance environment variable */ tmp_jnlpool->gd_ptr = local_gdptr; tmp_jnlpool->gd_instinfo = NULL; } } if (0 == seg->fname_len) { /* Fill in fields only if this is the first time this jnlpool is opening the replication instance file */ memcpy((char *)seg->fname, instfilename, full_len); udi->fn = (char *)seg->fname; seg->fname_len = full_len; seg->fname[full_len] = '\0'; udi->fileid = instfilename_gdid; } /* First grab ftok semaphore for replication instance file. Once we have it locked, no one else can start up * or shut down replication for this instance. We will release ftok semaphore when initialization is done. */ jnlpool = tmp_jnlpool; assert(NULL != jnlpool); /* ftok_sem_get uses jnlpool for asserts */ if (!ftok_sem_get(tmp_jnlpool->jnlpool_dummy_reg, TRUE, REPLPOOL_ID, FALSE, &ftok_counter_halted)) { save_errno = errno; RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error grabbing the ftok semaphore"), save_errno); } repl_inst_read(udi->fn, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr)); /* At this point, we have not yet attached to the jnlpool so we do not know if the ftok counter got halted * previously or not. So be safe and assume it has halted in case the jnlpool_shmid indicates it is up and running. * We will set udi->counter_ftok_incremented back to an accurate value after we attach to the jnlpool. * This means we might not delete the ftok semaphore in some cases of error codepaths but it should be rare * and is better than incorrectly deleting it while live processes are concurrently using it. */ assert(udi->counter_ftok_incremented == !ftok_counter_halted); udi->counter_ftok_incremented = udi->counter_ftok_incremented && (INVALID_SHMID == repl_instance.jnlpool_shmid); is_src_srvr = (GTMSOURCE == pool_user); /* If caller is source server and secondary instance name has been specified check if it is different from THIS instance */ if (is_src_srvr && gtmsource_options.instsecondary) { if (0 == STRCMP(repl_instance.inst_info.this_instname, gtmsource_options.secondary_instname)) { ftok_sem_release(tmp_jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_REPLINSTNMSAME, 2, LEN_AND_STR((char *)repl_instance.inst_info.this_instname)); } } new_ipc = FALSE; if (INVALID_SEMID == repl_instance.jnlpool_semid) { /* First process to do "jnlpool_init". Create the journal pool. */ assertpro(INVALID_SHMID == repl_instance.jnlpool_shmid); /* Source server startup is the only command that can create the journal pool. Check that. */ if (!is_src_srvr || !gtmsource_options.start) { /* Release the ftok if we're not in -editinstance, otherwise we want to hold onto it. */ if (!in_repl_inst_edit) ftok_sem_release(tmp_jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); if (GTMRELAXED == pool_user) { if (NULL == jnlpool_head) jnlpool_head = jnlpool; return; /* jnlpool must be allocated by here */ } assert(!STRCMP(udi->fn, instfilename)); assert(is_gdid_identical(&udi->fileid, &instfilename_gdid)); RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_NOJNLPOOL, 2, full_len, instfilename); } if (repl_instance.crash) { ftok_sem_release(tmp_jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); SNPRINTF(scndry_msg, OUT_BUFF_SIZE, "Instance file header has crash field set to TRUE"); assert(!STRCMP(udi->fn, instfilename)); assert(is_gdid_identical(&udi->fileid, &instfilename_gdid)); RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_REPLREQROLLBACK, 2, full_len, instfilename, ERR_TEXT, 2, LEN_AND_STR(scndry_msg)); } DEBUG_ONLY(sem_created = TRUE); new_ipc = TRUE; assert((int)NUM_SRC_SEMS == (int)NUM_RECV_SEMS); if (INVALID_SEMID == (udi->semid = init_sem_set_source(IPC_PRIVATE, NUM_SRC_SEMS, RWDALL | IPC_CREAT))) { save_errno = errno; ftok_sem_release(tmp_jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error creating journal pool semaphore"), save_errno); } /* Following will set semaphore SOURCE_ID_SEM value as GTM_ID. In case we have orphaned semaphore * for some reason, mupip rundown will be able to identify GTM semaphores checking the value and can remove. */ semarg.val = GTM_ID; if (-1 == semctl(udi->semid, SOURCE_ID_SEM, SETVAL, semarg)) { save_errno = errno; remove_sem_set(SOURCE); /* Remove what we created */ ftok_sem_release(tmp_jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with jnlpool semctl SETVAL"), save_errno); } /* Warning: We must read the sem_ctime using IPC_STAT after SETVAL, which changes it. We must NOT do any * more SETVAL after this. Our design is to use sem_ctime as creation time of semaphore. */ semarg.buf = &semstat; if (-1 == semctl(udi->semid, SOURCE_ID_SEM, IPC_STAT, semarg)) { save_errno = errno; remove_sem_set(SOURCE); /* Remove what we created */ ftok_sem_release(tmp_jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with jnlpool semctl IPC_STAT"), save_errno); } udi->gt_sem_ctime = semarg.buf->sem_ctime; } else { /* find create time of semaphore from the file header and check if the id is reused by others */ semarg.buf = &semstat; if (-1 == semctl(repl_instance.jnlpool_semid, DB_CONTROL_SEM, IPC_STAT, semarg)) { save_errno = errno; ftok_sem_release(tmp_jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); SNPRINTF(scndry_msg, OUT_BUFF_SIZE, "Error with semctl on Journal Pool SEMID (%d)", repl_instance.jnlpool_semid); RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_REPLREQROLLBACK, 2, full_len, instfilename, ERR_TEXT, 2, LEN_AND_STR(scndry_msg), save_errno); } else if (semarg.buf->sem_ctime != repl_instance.jnlpool_semid_ctime) { ftok_sem_release(tmp_jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); SNPRINTF(scndry_msg, OUT_BUFF_SIZE, "Creation time for Journal Pool SEMID (%d) is %d; Expected %d", repl_instance.jnlpool_semid, semarg.buf->sem_ctime, repl_instance.jnlpool_semid_ctime); assert(!STRCMP(udi->fn, instfilename)); assert(is_gdid_identical(&udi->fileid, &instfilename_gdid)); RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_REPLREQROLLBACK, 2, full_len, instfilename, ERR_TEXT, 2, LEN_AND_STR(scndry_msg)); } udi->semid = repl_instance.jnlpool_semid; udi->gt_sem_ctime = repl_instance.jnlpool_semid_ctime; set_sem_set_src(udi->semid); /* repl_sem.c has some functions which needs some static variable to have the id */ } assert((INVALID_SEMID != udi->semid) && (0 != udi->gt_sem_ctime)); assert(!udi->grabbed_access_sem); if (!skip_locks) { status = grab_sem(SOURCE, JNL_POOL_ACCESS_SEM); if (SS_NORMAL != status) { save_errno = errno; ftok_sem_release(tmp_jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with journal pool access semaphore"), save_errno); } udi->grabbed_access_sem = TRUE; udi->counter_acc_incremented = TRUE; } if (INVALID_SHMID == repl_instance.jnlpool_shmid) { /* We have an INVALID shmid in the file header. There are three ways this can happen * * 1. A rollback started off with either no journal pool or rundown'd an existing journal pool and created new * semaphores, but got killed in the middle. At this point, if a new source server starts up, it will notice * a valid usable semid, but will find an invalid shmid. * * 2. A rollback started off with either no journal pool or rundown'd an existing journal pool and created new * semaphores. Before it goes to mur_close_files, lets say a source server started. It will acquire the ftok * semaphore, but will be waiting for the access control semaphore which rollback is holding. Rollback, on the * other hand, will see if the ftok semaphore is available BEFORE removing the semaphores from the system. But, * since source server is holding the ftok, Rollback, will not remove the access control semaphore. But, would * just let go of them and exit (repl_instance.file_corrupt can be either TRUE or FALSE depending on whether * Rollback completed successfully or not). * * 3. A fresh startup. */ /* Ensure that NO one has yet incremented the SRC_SERV_COUNT_SEM (as implied by all the 3 cases above) */ assert(0 == (semval = semctl(udi->semid, SRC_SERV_COUNT_SEM, GETVAL))); /* semval = number of processes attached */ new_ipc = TRUE; /* need to create new IPC */ } else if (-1 == shmctl(repl_instance.jnlpool_shmid, IPC_STAT, &shmstat)) { /* shared memory ID was removed form the system by an IPCRM command or we have a permission issue (or such) */ save_errno = errno; REMOVE_OR_RELEASE_SEM(new_ipc); ftok_sem_release(tmp_jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); assert(!STRCMP(udi->fn, instfilename)); assert(is_gdid_identical(&udi->fileid, &instfilename_gdid)); RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); SNPRINTF(scndry_msg, OUT_BUFF_SIZE, "Error with semctl on Journal Pool SHMID (%d)", repl_instance.jnlpool_shmid); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_REPLREQROLLBACK, 2, full_len, instfilename, ERR_TEXT, 2, LEN_AND_STR(scndry_msg), save_errno); } else if (shmstat.shm_ctime != repl_instance.jnlpool_shmid_ctime) { /* shared memory was possibly reused (causing shm_ctime and jnlpool_shmid_ctime to be different. We can't rely * on the shmid as it could be connected to a valid instance file in a different environment. Create new IPCs */ new_ipc = TRUE; /* need to create new IPC */ } else { udi->shmid = repl_instance.jnlpool_shmid; udi->gt_shm_ctime = repl_instance.jnlpool_shmid_ctime; } /* Source server startup is the only command that can create the journal pool. Check that. */ if (new_ipc && (!is_src_srvr || !gtmsource_options.start)) { REMOVE_OR_RELEASE_SEM(new_ipc); ftok_sem_release(tmp_jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); if (GTMRELAXED == pool_user) { if (NULL == jnlpool_head) jnlpool_head = jnlpool; return; } assert(!STRCMP(udi->fn, instfilename)); assert(is_gdid_identical(&udi->fileid, &instfilename_gdid)); RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_NOJNLPOOL, 2, full_len, instfilename); } if (repl_instance.file_corrupt) { /* Indicates that a prior rollback was killed and so requires a re-run. It is also possible this process started * waiting on the semaphores during a concurrent rollback and so has a stale file header values. Read the instance * file header again to see if the file_corrupt field is still TRUE. */ repl_inst_read(udi->fn, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr)); assert((udi->shmid == repl_instance.jnlpool_shmid) && (udi->gt_shm_ctime == repl_instance.jnlpool_shmid_ctime)); assert(sem_created || ((udi->semid == repl_instance.jnlpool_semid) && (udi->gt_sem_ctime == repl_instance.jnlpool_semid_ctime))); if (repl_instance.file_corrupt) { REMOVE_OR_RELEASE_SEM(new_ipc); ftok_sem_release(tmp_jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); SNPRINTF(scndry_msg, OUT_BUFF_SIZE, "Instance file header has file_corrupt field set to TRUE"); assert(!STRCMP(udi->fn, instfilename)); assert(is_gdid_identical(&udi->fileid, &instfilename_gdid)); RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_REPLREQROLLBACK, 2, full_len, instfilename, ERR_TEXT, 2, LEN_AND_STR(scndry_msg)); } } if (new_ipc) { /* create new shared memory */ if (-1 == (udi->shmid = gtm_shmget(IPC_PRIVATE, gtmsource_options.buffsize, RWDALL | IPC_CREAT, TRUE))) { udi->shmid = INVALID_SHMID; save_errno = errno; remove_sem_set(SOURCE); ftok_sem_release(tmp_jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with journal pool creation"), save_errno); } if (-1 == shmctl(udi->shmid, IPC_STAT, &shmstat)) { save_errno = errno; DETACH_AND_REMOVE_SHM_AND_SEM(tmp_jnlpool); /* remove any sem/shm we had created */ ftok_sem_release(tmp_jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with jnlpool shmctl IPC_STAT"), save_errno); } udi->gt_shm_ctime = shmstat.shm_ctime; } assert((INVALID_SHMID != udi->shmid) && (0 != udi->gt_shm_ctime)); status_l = (sm_long_t)(tmp_jnlpool_ctl = (jnlpool_ctl_ptr_t)do_shmat(udi->shmid, 0, 0)); if (-1 == status_l) { save_errno = errno; DETACH_AND_REMOVE_SHM_AND_SEM(tmp_jnlpool); /* remove any sem/shm we had created */ assert(NULL != tmp_jnlpool); ftok_sem_release(tmp_jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); /* Assert below ensures we dont try to clean up the journal pool even though we errored out while attaching to it */ assert(NULL == tmp_jnlpool->jnlpool_ctl); tmp_jnlpool_ctl = NULL; RELEASE_NEW_TMP_JNLPOOL(new_tmp_jnlpool, tmp_jnlpool, new_dummy_reg, jnlpool, save_jnlpool, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with journal pool shmat"), save_errno); } /* if new jnlpool, add to jnlpool_head list */ if (new_tmp_jnlpool) if (NULL != jnlpool_head) { for (last_jnlpool = jnlpool_head; last_jnlpool->next; last_jnlpool = last_jnlpool->next) ; if (tmp_jnlpool != last_jnlpool) last_jnlpool->next = tmp_jnlpool; } else jnlpool_head = tmp_jnlpool; jnlpool = tmp_jnlpool; jnlpool->jnlpool_ctl = tmp_jnlpool_ctl; /* Now that we have attached to the journal pool, fix udi->counter_ftok_incremented back to an accurate value */ udi->counter_ftok_incremented = !ftok_counter_halted; if (udi->counter_ftok_incremented && jnlpool->jnlpool_ctl->ftok_counter_halted) { /* If shared counter has overflown previously, undo the counter bump we did. * There is no specific reason but just in case a future caller invokes "jnlpool_init", followed by * "jnlpool_detach" followed by "mu_rndwn_repl_instance". (See comment in "db_init" where similar * cleanup is done. */ SET_SOP_ARRAY_FOR_DECR_CNT(sop, sopcnt, (SEM_UNDO | IPC_NOWAIT)); SEMOP(udi->ftok_semid, sop, sopcnt, status, NO_WAIT); udi->counter_ftok_incremented = FALSE; assert(-1 != status); /* since we hold the access control lock, we do not expect any errors */ } /* Set a flag to indicate the journal pool is uninitialized. Do this as soon as attaching to shared memory. * This flag will be reset by "gtmsource_seqno_init" when it is done with setting the jnl_seqno fields. */ if (new_ipc) jnlpool->jnlpool_ctl->pool_initialized = FALSE; assert(SIZEOF(jnlpool_ctl_struct) % 16 == 0); /* enforce 16-byte alignment for this structure */ /* Since seqno is an 8-byte quantity and is used in most of the sections below, we require all sections to * be at least 8-byte aligned. In addition we expect that the beginning of the journal data (JNLDATA_BASE_OFF) is * aligned at a boundary that is suitable for journal records (defined by JNL_WRT_END_MASK). */ assert(JNLPOOL_CTL_SIZE % 8 == 0); /* The assert below trips, if node_local struct is unaligned in gdsbt.h. If you have added a new field, verify that filler * arrays are adjusted accordingly. */ assert(JNLPOOL_CRIT_SIZE % 8 == 0); assert(SIZEOF(repl_inst_hdr) % 8 == 0); assert(SIZEOF(gtmsrc_lcl) % 8 == 0); assert(SIZEOF(gtmsource_local_struct) % 8 == 0); assert(REPL_INST_HDR_SIZE % 8 == 0); assert(GTMSRC_LCL_SIZE % 8 == 0); assert(GTMSOURCE_LOCAL_SIZE % 8 == 0); assert(JNLDATA_BASE_OFF % JNL_WRT_END_MODULUS == 0); /* Ensure that the overhead in the journal pool is never greater than gtmsource_options.buffsize as that would indicate a * out-of-design situation */ assert(!gtmsource_options.start || ((JNLPOOL_CTL_SIZE + JNLPOOL_CRIT_SIZE + REPL_INST_HDR_SIZE + GTMSRC_LCL_SIZE) < gtmsource_options.buffsize)); /* jnlpool_ctl has an array of size MERRORS_ARRAY_SZ which holds one byte of information for each error in the merrors.msg. * Whenever the below assert fails, the MERRORS_ARRAY_SZ has to be increased while maintaining the 16 byte alignment of the * journal pool. */ assert(MERRORS_ARRAY_SZ > merrors_ctl.msg_cnt); csa->critical = (CRIT_PTR_T)((sm_uc_ptr_t)jnlpool->jnlpool_ctl + JNLPOOL_CTL_SIZE); assert(jnlpool->jnlpool_ctl == REPLCSA2JPL(csa)); /* secshr_db_clnup uses this relationship */ jnlpool_mutex_spin_parms = (mutex_spin_parms_ptr_t)((sm_uc_ptr_t)csa->critical + JNLPOOL_CRIT_SPACE); csa->nl = (node_local_ptr_t)((sm_uc_ptr_t)jnlpool_mutex_spin_parms + SIZEOF(mutex_spin_parms_struct)); # ifdef DEBUG if (new_ipc) { /* We allocated shared storage -- "shmget" ensures it is null initialized. Assert that. */ ptr = (char *)csa->nl; for (i = 0; i < SIZEOF(*csa->nl); i++) assert('\0' == ptr[i]); } # endif csa->now_crit = FALSE; csa->onln_rlbk_cycle = jnlpool->jnlpool_ctl->onln_rlbk_cycle; /* Take a copy of the latest onln_rlbk_cycle */ jnlpool->repl_inst_filehdr = (repl_inst_hdr_ptr_t)((sm_uc_ptr_t)csa->critical + JNLPOOL_CRIT_SIZE); jnlpool->gtmsrc_lcl_array = (gtmsrc_lcl_ptr_t)((sm_uc_ptr_t)jnlpool->repl_inst_filehdr + REPL_INST_HDR_SIZE); jnlpool->gtmsource_local_array = (gtmsource_local_ptr_t)((sm_uc_ptr_t)jnlpool->gtmsrc_lcl_array + GTMSRC_LCL_SIZE); jnlpool->jnldata_base = (sm_uc_ptr_t)jnlpool->jnlpool_ctl + JNLDATA_BASE_OFF; assert(!mutex_per_process_init_pid || mutex_per_process_init_pid == process_id); if (!mutex_per_process_init_pid) mutex_per_process_init(); if (new_ipc) { jnlpool->jnlpool_ctl->instfreeze_environ_inited = FALSE; if (CUSTOM_ERRORS_AVAILABLE && !init_anticipatory_freeze_errors()) { DETACH_AND_REMOVE_SHM_AND_SEM(jnlpool); /* remove any sem/shm we had created */ udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, LEN_AND_LIT("Error initializing custom errors")); } jnlpool->jnlpool_ctl->critical_off = (sm_uc_ptr_t)csa->critical - (sm_uc_ptr_t)jnlpool->jnlpool_ctl; jnlpool->jnlpool_ctl->filehdr_off = (sm_uc_ptr_t)jnlpool->repl_inst_filehdr - (sm_uc_ptr_t)jnlpool->jnlpool_ctl; jnlpool->jnlpool_ctl->srclcl_array_off = (sm_uc_ptr_t)jnlpool->gtmsrc_lcl_array - (sm_uc_ptr_t)jnlpool->jnlpool_ctl; jnlpool->jnlpool_ctl->sourcelocal_array_off = (sm_uc_ptr_t)jnlpool->gtmsource_local_array - (sm_uc_ptr_t)jnlpool->jnlpool_ctl; /* Need to initialize the different sections of journal pool. Start with the FILE HEADER section */ repl_instance.jnlpool_semid = udi->semid; repl_instance.jnlpool_shmid = udi->shmid; repl_instance.jnlpool_semid_ctime = udi->gt_sem_ctime; repl_instance.jnlpool_shmid_ctime = udi->gt_shm_ctime; assert((REPL_INST_HDR_SIZE == sizeof(repl_instance)) && (REPL_INST_HDR_SIZE == sizeof(*jnlpool->repl_inst_filehdr))); memcpy(jnlpool->repl_inst_filehdr, &repl_instance, REPL_INST_HDR_SIZE); /* Initialize FILE HEADER */ jnlpool->repl_inst_filehdr->crash = TRUE; /* Since we are creating the journal pool, initialize the mutex structures in the shared memory for later * grab_locks to work correctly */ DEBUG_ONLY(locknl = csa->nl;) /* for DEBUG_ONLY LOCK_HIST macro */ gtm_mutex_init(reg, DEFAULT_NUM_CRIT_ENTRY, FALSE); DEBUG_ONLY(locknl = NULL;) /* restore "locknl" to default value */ jnlpool_mutex_spin_parms->mutex_hard_spin_count = MUTEX_HARD_SPIN_COUNT; jnlpool_mutex_spin_parms->mutex_sleep_spin_count = MUTEX_SLEEP_SPIN_COUNT; jnlpool_mutex_spin_parms->mutex_spin_sleep_mask = MUTEX_SPIN_SLEEP_MASK; jnlpool_mutex_spin_parms->mutex_que_entry_space_size = DEFAULT_NUM_CRIT_ENTRY; assert(!skip_locks); grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); /* Flush the file header to disk so future callers of "jnlpool_init" see the jnlpool_semid and jnlpool_shmid */ repl_inst_flush_filehdr(); /* Initialize GTMSRC_LCL section in journal pool */ repl_inst_read(udi->fn, (off_t)REPL_INST_HDR_SIZE, (sm_uc_ptr_t)jnlpool->gtmsrc_lcl_array, GTMSRC_LCL_SIZE); rel_lock(jnlpool->jnlpool_dummy_reg); /* Initialize GTMSOURCE_LOCAL section in journal pool */ memset(jnlpool->gtmsource_local_array, 0, GTMSOURCE_LOCAL_SIZE); gtmsourcelocal_ptr = &jnlpool->gtmsource_local_array[0]; gtmsrclcl_ptr = &jnlpool->gtmsrc_lcl_array[0]; for (index = 0; index < NUM_GTMSRC_LCL; index++, gtmsrclcl_ptr++, gtmsourcelocal_ptr++) { COPY_GTMSRCLCL_TO_GTMSOURCELOCAL(gtmsrclcl_ptr, gtmsourcelocal_ptr); gtmsourcelocal_ptr->gtmsource_state = GTMSOURCE_DUMMY_STATE; gtmsourcelocal_ptr->gtmsrc_lcl_array_index = index; /* since we are setting up the journal pool for the first time, use this time to initialize the * gtmsource_srv_latch as well */ SET_LATCH_GLOBAL(>msourcelocal_ptr->gtmsource_srv_latch, LOCK_AVAILABLE); } } else if (!jnlpool->jnlpool_ctl->pool_initialized) { /* Source server that created the journal pool died before completing initialization. */ if (udi->grabbed_access_sem) rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(10) ERR_REPLREQRUNDOWN, 4, DB_LEN_STR(reg), LEN_AND_STR(machine_name), ERR_TEXT, 2, RTS_ERROR_TEXT("Journal pool is incompletely initialized. Run MUPIP RUNDOWN first.")); } if (ftok_counter_halted && !jnlpool->jnlpool_ctl->ftok_counter_halted) repl_inst_ftok_counter_halted(udi); slot_needs_init = FALSE; /* Do not release ftok semaphore in the following cases as each of them involve the callers writing to the instance file * which requires the ftok semaphore to be held. The callers will take care of releasing the semaphore. * a) MUPIP REPLIC -SOURCE -START * Invoke the function "gtmsource_rootprimary_init" * b) MUPIP REPLIC -SOURCE -SHUTDOWN * Invoke the function "gtmsource_flush_jnlpool" from the function "repl_ipc_cleanup" * c) MUPIP REPLIC -SOURCE -ACTIVATE -ROOTPRIMARY (or -UPDOK) on a journal pool that has updates disabled. * Invoke the function "gtmsource_rootprimary_init" * d) MUPIP REPLIC -EDITINSTANCE * Invoke the function "repl_inst_edit" */ hold_onto_ftok_sem = is_src_srvr && (gtmsource_options.start || gtmsource_options.shut_down) || in_repl_inst_edit; /* Determine "gtmsourcelocal_ptr" to later initialize jnlpool->gtmsource_local */ if (!is_src_srvr || !gtmsource_options.instsecondary) { /* GT.M or Update process or receiver server or a source server command that did not specify INSTSECONDARY */ gtmsourcelocal_ptr = NULL; } else { /* In jnlpool->gtmsource_local_array, find the structure which corresponds to the input secondary instance name. * Each gtmsource_local structure in the array is termed a slot. A slot is used if the "secondary_instname" * (the secondary instance name) member has a non-zero value. A slot is unused otherwise. Below is a tabulation * of the possible cases and actions for each of the source server commands. * * ------------------------------------------------------------------------------------------------------- * Used Slot found Unused slot found No slot found * ------------------------------------------------------------------------------------------------------- * activate Set gtmsource_local REPLINSTSECNONE REPLINSTSECNONE * deactivate Set gtmsource_local REPLINSTSECNONE REPLINSTSECNONE * showbacklog Set gtmsource_local REPLINSTSECNONE REPLINSTSECNONE * checkhealth Set gtmsource_local REPLINSTSECNONE REPLINSTSECNONE * statslog Set gtmsource_local REPLINSTSECNONE REPLINSTSECNONE * changelog Set gtmsource_local REPLINSTSECNONE REPLINSTSECNONE * stopsourcefilter Set gtmsource_local REPLINSTSECNONE REPLINSTSECNONE * start AAAA BBBB CCCC * shutdown Set gtmsource_local REPLINSTSECNONE REPLINSTSECNONE * needrestart Set gtmsource_local DDDD DDDD * * AAAA : if the slot has a gtmsource_pid value that is still alive, then issue error message SRCSRVEXISTS * Else initialize the slot for the new source server. * * BBBB : Set gtmsource_local to this slot. Initialize slot. * * CCCC : Slot reuse algorithm. Scan through all the slots again looking for those with a * gtmsource_state field value 0 (GTMSOURCE_DUMMY_STATE) or those with a gtmsource_pid that does * not exist. Note down the connect_jnl_seqno field of all these slots. Set gtmsource_local to that * slot with the least value for connect_jnl_seqno. Initialize that slot. If no slot is found issue * SRCSRVTOOMANY error * * DDDD : Set gtmsource_local to NULL. The caller gtmsource_needrestart.c will issue the appropriate * message. * * Slot Initialization : Set "read_jnl_seqno" to 1. */ reuse_slot_ptr = NULL; gtmsourcelocal_ptr = &jnlpool->gtmsource_local_array[0]; for ( index = 0; index < NUM_GTMSRC_LCL; index++, gtmsourcelocal_ptr++) { if ((NULL == reuse_slot_ptr) && ('\0' == gtmsourcelocal_ptr->secondary_instname[0])) reuse_slot_ptr = gtmsourcelocal_ptr; if (0 == STRCMP(gtmsource_options.secondary_instname, gtmsourcelocal_ptr->secondary_instname)) { /* Found matching slot */ gtmsource_state = gtmsourcelocal_ptr->gtmsource_state; gtmsource_pid = gtmsourcelocal_ptr->gtmsource_pid; /* Check if source server is already running for this secondary instance */ if ((0 != gtmsource_pid) && is_proc_alive(gtmsource_pid, 0)) { /* Source server is already up and running for this secondary instance */ if (gtmsource_options.start) { assert(!skip_locks); rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); /* Assert we did not create shm or sem so no need to remove any */ assert(!new_ipc); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_SRCSRVEXISTS, 3, LEN_AND_STR(gtmsource_options.secondary_instname), gtmsource_pid); } } else { /* Source server is not running for this secondary instance */ if (gtmsource_options.start) { /* We want to reinitialize the source server related fields in "gtmsource_local" * but do NOT want to reinitialize any fields that intersect with "gtmsrc_lcl" */ CHECK_SLOT(gtmsourcelocal_ptr); slot_needs_init = TRUE; reset_gtmsrclcl_info = FALSE; } else if (!gtmsource_options.needrestart && !gtmsource_options.showbacklog && !gtmsource_options.checkhealth) { /* If NEEDRESTART, we don't care if the source server is alive or not. All that * we care about is if the primary and secondary did communicate or not. That * will be determined in gtmsource_needrestart.c. Do not trigger an error here. * If SHOWBACKLOG or CHECKHEALTH, do not trigger an error as slot was found * even though the source server is not alive. We can generate backlog/checkhealth * information using values from the matched slot. */ if (udi->grabbed_access_sem) rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); /* Assert we did not create shm or sem so no need to remove any */ assert(!new_ipc); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_SRCSRVNOTEXIST, 2, LEN_AND_STR(gtmsource_options.secondary_instname)); } } break; } } if (NUM_GTMSRC_LCL == index) { /* Did not find a matching slot. Use the unused slot if it was already found and if appropriate. */ if (gtmsource_options.needrestart) { /* If -NEEDRESTART is specified, set gtmsource_local to NULL. The caller function * "gtmsource_needrestart" will print the appropriate message. */ gtmsourcelocal_ptr = NULL; } else if (NULL == reuse_slot_ptr) { /* No used or unused slot found. Issue REPLINSTSECNONE error except for -start */ if (!gtmsource_options.start) { if (udi->grabbed_access_sem) rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); /* Assert we did not create shm or sem so no need to remove any */ assert(!new_ipc); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLINSTSECNONE, 4, LEN_AND_STR(gtmsource_options.secondary_instname), full_len, udi->fn); } else { /* Find a used slot that can be reused. Find one with least value of "connect_jnl_seqno". */ reuse_slot_seqnum = MAX_SEQNO; gtmsourcelocal_ptr = &jnlpool->gtmsource_local_array[0]; for (index = 0; index < NUM_GTMSRC_LCL; index++, gtmsourcelocal_ptr++) { gtmsource_state = gtmsourcelocal_ptr->gtmsource_state; gtmsource_pid = gtmsourcelocal_ptr->gtmsource_pid; if ((0 == gtmsource_pid) || !is_proc_alive(gtmsource_pid, 0)) { /* Slot can be reused */ if (gtmsourcelocal_ptr->connect_jnl_seqno < reuse_slot_seqnum) { reuse_slot_seqnum = gtmsourcelocal_ptr->connect_jnl_seqno; reuse_slot_ptr = gtmsourcelocal_ptr; } } } if (NULL == reuse_slot_ptr) { if (udi->grabbed_access_sem) rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); /* Assert we did not create shm or sem so no need to remove any */ assert(!new_ipc); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_SRCSRVTOOMANY, 3, NUM_GTMSRC_LCL, full_len, udi->fn); } else { /* We want to reinitialize the source server related fields in "gtmsource_local" * as well as reinitialize any fields that intersect with "gtmsrc_lcl" as this * slot is being reused for a different secondary instance than is currently stored. */ gtmsourcelocal_ptr = reuse_slot_ptr; CHECK_SLOT(gtmsourcelocal_ptr); slot_needs_init = TRUE; reset_gtmsrclcl_info = TRUE; } } } else { /* No used slot found. But an unused slot was found. */ if (gtmsource_options.start) { /* Initialize the unused slot. We want to reinitialize the source server related fields * in "gtmsource_local" as well as reinitialize any fields that intersect with "gtmsrc_lcl" * as this slot is being reused for the first time for any secondary instance name. */ gtmsourcelocal_ptr = reuse_slot_ptr; CHECK_SLOT(gtmsourcelocal_ptr); slot_needs_init = TRUE; reset_gtmsrclcl_info = TRUE; } else { /* One of the following qualifiers has been specified. Issue error. * ACTIVATE, CHANGELOG, CHECKHEALTH, DEACTIVATE, SHOWBACKLOG, * STATSLOG, SHUTDOWN or STOPSOURCEFILTER */ if (udi->grabbed_access_sem) rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); /* Assert we did not create shm or sem so no need to remove any */ assert(!new_ipc); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLINSTSECNONE, 4, LEN_AND_STR(gtmsource_options.secondary_instname), full_len, udi->fn); } } } } assert((ROOTPRIMARY_UNSPECIFIED == gtmsource_options.rootprimary) || (PROPAGATEPRIMARY_SPECIFIED == gtmsource_options.rootprimary) || (ROOTPRIMARY_SPECIFIED == gtmsource_options.rootprimary)); if (!new_ipc) { /* We did not create shm or sem so no need to remove any of them for any "rts_error" within this IF */ assert(!STRCMP(repl_instance.inst_info.this_instname, jnlpool->repl_inst_filehdr->inst_info.this_instname)); /* Source Server restart - attempt to install custom errors if not installed before */ if (gtmsource_startup && (!jnlpool->jnlpool_ctl->instfreeze_environ_inited) && CUSTOM_ERRORS_AVAILABLE && !init_anticipatory_freeze_errors()) { ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, LEN_AND_LIT("Error initializing custom errors")); } /* Check compatibility of caller source server or receiver server command with the current state of journal pool */ if (!jnlpool->jnlpool_ctl->upd_disabled) { if (((is_src_srvr && (PROPAGATEPRIMARY_SPECIFIED == gtmsource_options.rootprimary)) || ((GTMRECEIVE == pool_user) && !jnlpool->repl_inst_filehdr->is_supplementary))) { /* Journal pool was created as -ROOTPRIMARY (or -UPDOK) and a source server command has * specified -PROPAGATEPRIMARY (or -UPDNOTOK) or a receiver server command is being attempted * on a non-supplementary instance. Issue error. */ if (udi->grabbed_access_sem) rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_PRIMARYISROOT, 2, LEN_AND_STR((char *)repl_instance.inst_info.this_instname)); } } else if (is_src_srvr) { /* Source server command issued on a propagating primary */ if (ROOTPRIMARY_SPECIFIED == gtmsource_options.rootprimary) { /* Journal pool was created with a -PROPAGATEPRIMARY command and current source server command * has specified -ROOTPRIMARY (or -UPDOK). */ assert(!skip_locks); if (!gtmsource_options.activate) { /* START or DEACTIVATE was specified. Issue incompatibility error right away */ rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_PRIMARYNOTROOT, 2, LEN_AND_STR((char *)repl_instance.inst_info.this_instname)); } else { /* ACTIVATE was specified. Check if there is a receiver server OR update process * attached to the journal pool. If so we cannot allow the ACTIVATE (issue ACTIVATEFAIL * error). Those have to be shut down before the instance can be activated. In addition, * disallow an in-progress receiver server startup command. This is because we don't want * the activate to sneak in between the jnlpool_init and recvpool_init calls done by the * receiver server startup command creating a confusing situation (because the receiver * will be ready to play updates as if this is a secondary but an active source server * will be ready to transmit updates as if this is a primary at the same time). */ cannot_activate = FALSE; if (INVALID_SEMID != repl_instance.recvpool_semid) { /* Receive pool semaphore is available from instance file header. Use it * to check whether receiver server and/or update process are alive. The easiest * way is to check if the counter semaphore is non-zero. */ if (semctl(repl_instance.recvpool_semid, RECV_SERV_COUNT_SEM, GETVAL) || semctl(repl_instance.recvpool_semid, UPD_PROC_COUNT_SEM, GETVAL)) cannot_activate = TRUE; } else { /* No receiver server or update process running. But check if a receiver server * startup command is in progress and has already done a jnlpool_init. */ if (semctl(repl_instance.jnlpool_semid, RECV_SERV_STARTUP_SEM, GETVAL)) cannot_activate = TRUE; } if (cannot_activate) { rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ACTIVATEFAIL, 2, LEN_AND_STR(gtmsource_options.secondary_instname)); } else hold_onto_ftok_sem = TRUE; } } } else if ((GTMRECEIVE == pool_user) && gtmrecv_options.start) { /* This is a receiver server startup command. Increment RECV_SERV_STARTUP_SEM semaphore for * a later source server activate command to know this command is in progress. We don't do * a corresponding decr_sem later but rely on the OS doing it when the receiver startup command * exits (due to the SEM_UNDO done inside incr_sem). */ status = incr_sem(SOURCE, RECV_SERV_STARTUP_SEM); if (0 != status) { save_errno = errno; rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Receiver startup counter semaphore increment failure"), save_errno); } } } if (!csa->nl->glob_sec_init) { assert(new_ipc); assert(slot_needs_init); assert(!skip_locks); assert(GTMRELAXED != pool_user); if (!is_src_srvr || !gtmsource_options.start) { assert(FALSE); if (udi->grabbed_access_sem) rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Journal pool has not been initialized")); } /* Initialize the shared memory fields. */ /* Start_jnl_seqno (and jnl_seqno, read_jnl_seqno) need region shared mem to be properly setup. For now set to 0. */ jnlpool->jnlpool_ctl->start_jnl_seqno = 0; jnlpool->jnlpool_ctl->jnl_seqno = 0; jnlpool->jnlpool_ctl->max_zqgblmod_seqno = 0; jnlpool->jnlpool_ctl->jnldata_base_off = JNLDATA_BASE_OFF; jnlpool->jnlpool_ctl->jnlpool_size = gtmsource_options.buffsize - jnlpool->jnlpool_ctl->jnldata_base_off; assert((jnlpool->jnlpool_ctl->jnlpool_size & ~JNL_WRT_END_MASK) == 0); jnlpool->jnlpool_ctl->lastwrite_len = 0; jnlpool->jnlpool_ctl->write_addr = 0; jnlpool->jnlpool_ctl->rsrv_write_addr = 0; if (0 < jnlpool->repl_inst_filehdr->num_histinfo) { grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); status = repl_inst_histinfo_get(jnlpool->repl_inst_filehdr->num_histinfo - 1, &last_histinfo); rel_lock(jnlpool->jnlpool_dummy_reg); assert(0 == status); if (0 != status) { assert(ERR_REPLINSTNOHIST == status); /* the only error returned by repl_inst_histinfo_get() */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); repl_inst_flush_jnlpool(TRUE, TRUE); /* to reset "crash" field in instance file header to FALSE */ rel_lock(jnlpool->jnlpool_dummy_reg); DETACH_AND_REMOVE_SHM_AND_SEM(jnlpool); /* remove any sem/shm we had created */ udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, LEN_AND_LIT("Error reading last history record in replication instance file")); } instfilehdr_seqno = jnlpool->repl_inst_filehdr->jnl_seqno; assert(last_histinfo.start_seqno); assert(instfilehdr_seqno); if (instfilehdr_seqno < last_histinfo.start_seqno) { /* The jnl seqno in the instance file header is not greater than the last histinfo's start seqno */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); repl_inst_flush_jnlpool(TRUE, TRUE); /* to reset "crash" field in instance file header to FALSE */ rel_lock(jnlpool->jnlpool_dummy_reg); DETACH_AND_REMOVE_SHM_AND_SEM(jnlpool); /* remove any sem/shm we had created */ udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_REPLINSTSEQORD, 6, LEN_AND_LIT("Instance file header"), &instfilehdr_seqno, &last_histinfo.start_seqno, LEN_AND_STR(udi->fn)); } jnlpool->jnlpool_ctl->last_histinfo_seqno = last_histinfo.start_seqno; } else jnlpool->jnlpool_ctl->last_histinfo_seqno = 0; assert(ROOTPRIMARY_UNSPECIFIED != gtmsource_options.rootprimary); jnlpool->jnlpool_ctl->upd_disabled = TRUE; /* truly initialized later by a call to "gtmsource_rootprimary_init" */ jnlpool->jnlpool_ctl->primary_instname[0] = '\0'; jnlpool->jnlpool_ctl->send_losttn_complete = FALSE; memcpy(jnlpool->jnlpool_ctl->jnlpool_id.instfilename, seg->fname, seg->fname_len); jnlpool->jnlpool_ctl->jnlpool_id.instfilename[seg->fname_len] = '\0'; memcpy(jnlpool->jnlpool_ctl->jnlpool_id.label, GDS_RPL_LABEL, GDS_LABEL_SZ); memcpy(jnlpool->jnlpool_ctl->jnlpool_id.now_running, gtm_release_name, gtm_release_name_len + 1); assert(0 == (offsetof(jnlpool_ctl_struct, start_jnl_seqno) % 8)); /* ensure that start_jnl_seqno starts at an 8 byte boundary */ assert(0 == offsetof(jnlpool_ctl_struct, jnlpool_id)); /* ensure that the pool identifier is at the top of the pool */ jnlpool->jnlpool_ctl->jnlpool_id.pool_type = JNLPOOL_SEGMENT; SET_LATCH_GLOBAL(&jnlpool->jnlpool_ctl->phase2_commit_latch, LOCK_AVAILABLE); jnlpool->jnlpool_ctl->phase2_commit_index1 = jnlpool->jnlpool_ctl->phase2_commit_index2 = 0; /* The below value of "tot_jrec_len == 0" is relied upon by "mutex_salvage" of jnlpool in case the * jnlpool is created, a process goes to t_end and gets killed and salvage happens right afterwards. * The salvage logic needs to set jnlpool_ctl->lastwrite_len correctly and for that it needs to * go one previous entry in the phase2_commit_array. "shmget()" guarantees this by initializing * all of shm to 0 at startup. */ assert(0 == jnlpool->jnlpool_ctl->phase2_commit_array[JPL_PHASE2_COMMIT_ARRAY_SIZE - 1].process_id); assert(0 == jnlpool->jnlpool_ctl->phase2_commit_array[JPL_PHASE2_COMMIT_ARRAY_SIZE - 1].start_write_addr); assert(0 == jnlpool->jnlpool_ctl->phase2_commit_array[JPL_PHASE2_COMMIT_ARRAY_SIZE - 1].tot_jrec_len); assert(0 == jnlpool->jnlpool_ctl->phase2_commit_array[JPL_PHASE2_COMMIT_ARRAY_SIZE - 1].jnl_seqno); csa->nl->glob_sec_init = TRUE; assert(NULL != jnlpool_creator); *jnlpool_creator = TRUE; } else if (NULL != jnlpool_creator) *jnlpool_creator = FALSE; /* If this is a supplementary instance, initialize strm_index to a non-default value. * In case of an update process and a root primary supplementary instance, "strm_index" * will be initialized to a non-zero value later in updproc.c. */ if (jnlpool->repl_inst_filehdr->is_supplementary) { /* The value of 0 is possible in rare cases, if a process does jnlpool_init more than once * (possible if the first jnlpool_init failed say due to a REPLINSTUNDEF error). In that * case, we are anyways going to set it to the exact same value so allow that in the assert. */ assert((INVALID_SUPPL_STRM == strm_index) || (0 == strm_index)); strm_index = 0; } assert(!(is_src_srvr && gtmsource_options.start) || slot_needs_init); jnlpool->gtmsource_local = gtmsourcelocal_ptr; assert((NULL == gtmsourcelocal_ptr) || (gtmsourcelocal_ptr->gtmsrc_lcl_array_index == (gtmsourcelocal_ptr - jnlpool->gtmsource_local_array))); reg->open = TRUE; /* this is used by t_commit_cleanup/tp_restart/mutex_deadlock_check */ reg->read_only = FALSE; /* maintain csa->read_write simultaneously */ csa->read_write = TRUE; /* maintain reg->read_only simultaneously */ if (slot_needs_init) { assert(is_src_srvr); assert(NULL != gtmsourcelocal_ptr); assert(gtmsource_options.start || gtmsource_options.showbacklog); assert(!skip_locks); assert(GTMRELAXED != pool_user); gtmsourcelocal_ptr->gtmsource_pid = 0; gtmsourcelocal_ptr->gtmsource_state = GTMSOURCE_DUMMY_STATE; if (gtmsource_options.start) { /* Source server startup needs to initialize source server specific fields in the journal pool */ assert(NULL != gtmsourcelocal_ptr); QWASSIGNDW(gtmsourcelocal_ptr->read_addr, 0); gtmsourcelocal_ptr->read = 0; gtmsourcelocal_ptr->read_state = gtmsourcelocal_ptr->jnlfileonly ? READ_FILE : READ_POOL; gtmsourcelocal_ptr->mode = gtmsource_options.mode; gtmsourcelocal_ptr->statslog = FALSE; gtmsourcelocal_ptr->shutdown = NO_SHUTDOWN; gtmsourcelocal_ptr->shutdown_time = -1; gtmsourcelocal_ptr->secondary_port = gtmsource_options.secondary_port; STRCPY(gtmsourcelocal_ptr->secondary_host, gtmsource_options.secondary_host); STRCPY(gtmsourcelocal_ptr->filter_cmd, gtmsource_options.filter_cmd); STRCPY(gtmsourcelocal_ptr->log_file, gtmsource_options.log_file); gtmsourcelocal_ptr->log_interval = log_interval = gtmsource_options.src_log_interval; gtmsourcelocal_ptr->statslog_file[0] = '\0'; gtmsourcelocal_ptr->last_flush_resync_seqno = 0; gtmsourcelocal_ptr->next_histinfo_seqno = 0;/* fully initialized when source server connects to receiver */ gtmsourcelocal_ptr->next_histinfo_num = 0; /* fully initialized when source server connects to receiver */ gtmsourcelocal_ptr->num_histinfo = 0; /* fully initialized when source server connects to receiver */ if (GTMSOURCE_MODE_ACTIVE == gtmsource_options.mode) { gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HARD_TRIES_COUNT] = gtmsource_options.connect_parms[GTMSOURCE_CONN_HARD_TRIES_COUNT]; gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HARD_TRIES_PERIOD] = gtmsource_options.connect_parms[GTMSOURCE_CONN_HARD_TRIES_PERIOD]; gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_SOFT_TRIES_PERIOD] = gtmsource_options.connect_parms[GTMSOURCE_CONN_SOFT_TRIES_PERIOD]; gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_ALERT_PERIOD] = gtmsource_options.connect_parms[GTMSOURCE_CONN_ALERT_PERIOD]; gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD] = gtmsource_options.connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD]; gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HEARTBEAT_MAX_WAIT] = gtmsource_options.connect_parms[GTMSOURCE_CONN_HEARTBEAT_MAX_WAIT]; } /* At this point online rollback cannot be concurrently running because we hold the journal pool access * control semaphore. So, go ahead and initialize the gtmsource_srv_latch for this source server. */ SET_LATCH_GLOBAL(>msourcelocal_ptr->gtmsource_srv_latch, LOCK_AVAILABLE); # ifdef GTM_TLS /* Since the slot is reused for (possibly) a different secondary instance, reset the # of renegotiations * counter. */ gtmsourcelocal_ptr->num_renegotiations = 0; # endif } if (reset_gtmsrclcl_info) { /* Initialize all fields of "gtmsource_local" that are also present in the corresponding "gtmsrc_lcl" */ gtmsourcelocal_ptr->read_jnl_seqno = 1; /* fully initialized when source server connects to receiver */ assert((MAX_INSTNAME_LEN == sizeof(gtmsource_options.secondary_instname)) && (MAX_INSTNAME_LEN == sizeof(gtmsourcelocal_ptr->secondary_instname))); memcpy(gtmsourcelocal_ptr->secondary_instname, gtmsource_options.secondary_instname, MAX_INSTNAME_LEN - 1); gtmsourcelocal_ptr->connect_jnl_seqno = 0; /* fully initialized when source server connects to receiver */ gtmsourcelocal_ptr->send_losttn_complete = FALSE; /* Now make the corresponding changes from gtmsource_local to the gtmsrc_lcl structure and flush to disk. * This assumes "jnlpool->gtmsource_local" is set appropriately. */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); repl_inst_flush_gtmsrc_lcl(); rel_lock(jnlpool->jnlpool_dummy_reg); } } /* Assert that gtmsource_local is set to non-NULL value for all those qualifiers that care about it */ assert(!(gtmsource_options.start || gtmsource_options.activate || gtmsource_options.deactivate || gtmsource_options.stopsourcefilter || gtmsource_options.changelog || gtmsource_options.statslog) || (NULL != jnlpool->gtmsource_local)); assert((NULL == jnlpool->gtmsource_local) || !STRCMP(jnlpool->gtmsource_local->secondary_instname, gtmsource_options.secondary_instname)); /* Release control lockout now that this process has attached to the journal pool except if caller is source server. * Source Server will release the control lockout only after it is done with * a) initializing other fields in the pool (in case of source server startup) or * b) the actual command (in case of any other source server command like checkhealth, shutdown, showbacklog etc.) */ if (!is_src_srvr) { if (udi->grabbed_access_sem) { if (0 != (save_errno = rel_sem(SOURCE, JNL_POOL_ACCESS_SEM))) { ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); /* Assert we did not create shm or sem so no need to remove any */ assert(!new_ipc); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error in rel_sem"), save_errno); } udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; } } else { this_side = &jnlpool->jnlpool_ctl->this_side; remote_side = >msourcelocal_ptr->remote_side; /* Set global variable now. Structure will be initialized * later when source server connects to receiver */ } if (!hold_onto_ftok_sem && !ftok_sem_release(jnlpool->jnlpool_dummy_reg, FALSE, FALSE)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_JNLPOOLSETUP); /* Set up pool_init if jnlpool is still attached (e.g. we could have detached if GTMRELAXED and upd_disabled) */ if ((NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl)) { if (('\0' == repl_inst_name[0]) && ('\0' != repl_instfilename[0])) { /* fill in instance name if right instance file name and first */ if (0 == STRCMP(repl_instfilename, instfilename)) memcpy(repl_inst_name, repl_instance.inst_info.this_instname, MAX_INSTNAME_LEN); } jnlpool->pool_init = TRUE; pool_init++; ENABLE_FREEZE_ON_ERROR; } return; } void jnlpool_detach(void) { if (pool_init && jnlpool && jnlpool->pool_init) { assert(NULL != jnlpool); rel_lock(jnlpool->jnlpool_dummy_reg); mutex_cleanup(jnlpool->jnlpool_dummy_reg); if (jnlpool->gtmsource_local && (process_id == jnlpool->gtmsource_local->gtmsource_srv_latch.u.parts.latch_pid)) rel_gtmsource_srv_latch(&jnlpool->gtmsource_local->gtmsource_srv_latch); DETACH_FROM_JNLPOOL_IF_NEEDED(jnlpool, rts_error_csa); } } fis-gtm-V7.0-005/sr_unix/jnlsp.h0000755000032200000250000000427214342376330015350 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef JNLSP_H_INCLUDED #define JNLSP_H_INCLUDED /* Start jnlsp.h - platform-specific journaling definitions. */ #include #include "filestruct.h" /* needed for unix_file_info */ typedef gtm_int64_t jnl_proc_time; typedef int fd_type; typedef unix_file_info fi_type; /* in disk blocks but jnl file addresses are kept by byte so limited by uint4 for now */ #ifndef OFF_T_LONG #define JNL_ALLOC_MAX 4194304 /* 2GB */ #else #define JNL_ALLOC_MAX 8388607 /* 4GB - 512 Bytes */ #endif /* Since the journal buffer size always gets rounded up to the next multiple of * MIN(MAX_IO_BLOCK_SIZE, csd->blk_size) / DISK_BLOCK_SIZE), make the default journal * buffer size a multiple of default-block-size-to-512 ratio, which equals 8 (default block size = 4K). * That value might need readjustment for block sizes > 4K */ #define JNL_BUFFER_DEF ROUND_UP(JNL_BUFFER_MIN, 8) #define NOJNL FD_INVALID_NONPOSIX #define MID_TIME(W) W #define EXTTIMEVMS(T) #define EXTINTVMS(I) #define EXTTXTVMS(T,L) #ifndef GTM64 #define JNL_SHORT_TIME(S) (time((time_t *)&S)) #else #define JNL_SHORT_TIME(S) \ { \ time_t temp_t; \ time(&temp_t); \ S = (int4) temp_t; \ } #endif #define JNL_WHOLE_FROM_SHORT_TIME(W, S) W = (S) #define JNL_WHOLE_TIME(W) \ { \ time_t temp_t; \ time(&temp_t); \ W = temp_t; \ } #define UNIX_TIME_T_OVERFLOW_WARN_THRESHOLD 0x7acdd140 /* Mon Apr 16 00:00:00 2035 EST */ #define SECONDS_PER_EPOCH_SECOND 1 #define SECOND2EPOCH_SECOND(s) (s) #define EPOCH_SECOND2SECOND(e) (e) #define JNL_EXT_DEF "*.mjl" #define DEF_DB_EXT_NAME "dat" #define DEF_JNL_EXT_NAME ".mjl" uint4 jnl_file_open(gd_region *reg, boolean_t init); #endif /* JNLSP_H_INCLUDED */ fis-gtm-V7.0-005/sr_unix/jobchild_init.h0000755000032200000250000000115214342376330017015 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef JOBCHILD_INIT_H_INCLUDED #define JOBCHILD_INIT_H_INCLUDED void jobchild_init(void); #endif fis-gtm-V7.0-005/sr_unix/joberr.h0000755000032200000250000000500214342376330015475 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ typedef struct joberr_msg_struct{ char *msg; int len; }joberr_msg; /* * The follwoing array is index by values from the enum joberr_t from jobsp.h. */ LITDEF joberr_msg joberrs[] = { { "", 0 }, { LIT_AND_LEN("Job error in child process") }, { LIT_AND_LEN("Job error in opening STDIN") }, { LIT_AND_LEN("Job error in directing input to STDIN") }, { LIT_AND_LEN("Job error in creating STDOUT") }, { LIT_AND_LEN("Job error in opening STDOUT") }, { LIT_AND_LEN("Job error in directing output to STDOUT") }, { LIT_AND_LEN("Job error in creating STDERR") }, { LIT_AND_LEN("Job error in opening STDERR") }, { LIT_AND_LEN("Job error in directing output to STDERR") }, { LIT_AND_LEN("Job error in directory specification") }, { LIT_AND_LEN("Job error - CHDIR error") }, { LIT_AND_LEN("Job error in routine specification. Label and offset not found in created process") }, { LIT_AND_LEN("Job error in setting independent session") }, { LIT_AND_LEN("Job error in socketpair") }, { LIT_AND_LEN("Job error in fork") }, { LIT_AND_LEN("Job error in renaming standard output file") }, { LIT_AND_LEN("Job error in renaming standard error file") }, { LIT_AND_LEN("Job error in middle process to parent process pipe communication") }, { LIT_AND_LEN("Job error in middle process to grandchild process pipe communication") }, { LIT_AND_LEN("Job error - INPUT socket not found in socket pool") }, { LIT_AND_LEN("Job error - OUTPUT socket not found in socket pool") }, { LIT_AND_LEN("Job error - ERROR socket not found in socket pool") }, { LIT_AND_LEN("Job error in copying INPUT socket descriptor") }, { LIT_AND_LEN("Job error in copying OUTPUT socket descriptor") }, { LIT_AND_LEN("Job error in copying ERROR socket descriptor") }, { LIT_AND_LEN("Job error sending setup command") }, { LIT_AND_LEN("Job error sending setup data") }, { LIT_AND_LEN("Job child terminated due to signal") }, { LIT_AND_LEN("Job child was stopped by signal") }, { LIT_AND_LEN("") } /* this is used internally to determine try-again situations */ }; fis-gtm-V7.0-005/sr_unix/jobexam_signal_handler.c0000755000032200000250000001111014342376330020661 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ /* jobexam signal processor: a very simplistic stripped down version of generic_signal_handler(). It is only intended to handle terminal errors during execution of the $zjobexam function. These are currently SIGSEGV and SIGBUS. On receipt, we use rts error to drive the appropriate message and let the condition handler take care of it. */ #include "mdef.h" #include "gtm_string.h" #include #include "gtm_inet.h" #include "gtm_stdio.h" #include "error.h" #include "gtmsiginfo.h" #include "gtmimagename.h" #include "jobexam_signal_handler.h" #include "send_msg.h" #include "gtmmsg.h" GBLREF uint4 process_id; GBLREF enum gtmImageTypes image_type; GBLREF boolean_t need_core; DEBUG_ONLY(GBLREF boolean_t ok_to_UNWIND_in_exit_handling;) LITREF gtmImageName gtmImageNames[]; error_def(ERR_JOBEXAMFAIL); error_def(ERR_KILLBYSIG); error_def(ERR_KILLBYSIGUINFO); error_def(ERR_KILLBYSIGSINFO1); error_def(ERR_KILLBYSIGSINFO2); error_def(ERR_KILLBYSIGSINFO3); void jobexam_signal_handler(int sig, siginfo_t *info, void *context) { siginfo_t exi_siginfo; gtmsiginfo_t signal_info; gtm_sigcontext_t exi_context, *context_ptr; if (NULL != info) exi_siginfo = *info; else memset(&exi_siginfo, 0, SIZEOF(*info)); #if defined(__ia64) && defined(__hpux) context_ptr = (gtm_sigcontext_t *)context; /* no way to make a copy of the context */ memset(&exi_context, 0, SIZEOF(exi_context)); #else if (NULL != context) exi_context = *(gtm_sigcontext_t *)context; else memset(&exi_context, 0, SIZEOF(exi_context)); context_ptr = &exi_context; #endif extract_signal_info(sig, &exi_siginfo, context_ptr, &signal_info); switch(signal_info.infotype) { case GTMSIGINFO_NONE: send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_KILLBYSIG, 4, GTMIMAGENAMETXT(image_type), process_id, sig); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_KILLBYSIG, 4, GTMIMAGENAMETXT(image_type), process_id, sig); break; case GTMSIGINFO_USER: send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_KILLBYSIGUINFO, 6, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.send_pid, signal_info.send_uid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_KILLBYSIGUINFO, 6, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.send_pid, signal_info.send_uid); break; case GTMSIGINFO_ILOC + GTMSIGINFO_BADR: send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_KILLBYSIGSINFO1, 6, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.int_iadr, signal_info.bad_vadr); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_KILLBYSIGSINFO1, 6, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.int_iadr, signal_info.bad_vadr); break; case GTMSIGINFO_ILOC: send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_KILLBYSIGSINFO2, 5, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.int_iadr); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_KILLBYSIGSINFO2, 5, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.int_iadr); break; case GTMSIGINFO_BADR: send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_KILLBYSIGSINFO3, 5, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.bad_vadr); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_KILLBYSIGSINFO3, 5, GTMIMAGENAMETXT(image_type), process_id, sig, signal_info.bad_vadr); break; default: GTMASSERT; } send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_JOBEXAMFAIL, 1, process_id); /* Create a core to examine later. Note this handler is only enabled for two fatal core types so we don't * do any futher checking in this regard. */ need_core = TRUE; gtm_fork_n_core(); /* Note this routine do NOT invoke create_fatal_error_zshow_dmp() because it would in turn call jobexam * again which would loop us right back around to here. We basically want to do UNWIND(NULL, NULL) logic * but the UNWIND macro can only be used in a condition handler so next is a block that pretends it is * our condition handler and does the needful. */ { /* Needs new block since START_CH declares a new var used in UNWIND() */ int arg = 0; /* Needed for START_CH macro if debugging enabled */ START_CH(TRUE); DEBUG_ONLY(ok_to_UNWIND_in_exit_handling = TRUE); UNWIND(NULL, NULL); } } fis-gtm-V7.0-005/sr_unix/jobexam_signal_handler.h0000755000032200000250000000114414342376330020674 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2002 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef JOBEXAM_SIGNAL_HANDLER_INCLUDED #define JOBEXAM_SIGNAL_HANDLER_INCLUDED void jobexam_signal_handler(int sig, siginfo_t *info, void *context); #endif fis-gtm-V7.0-005/sr_unix/jobsp.h0000755000032200000250000001166214342376330015340 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef JOBSP_H_INCLUDED #define JOBSP_H_INCLUDED #include "gtm_stdio.h" #define MAX_JOBPAR_LEN 255 #define MAX_FILSPC_LEN 255 #define MAX_PIDSTR_LEN 10 #define MAX_MBXNAM_LEN 16 #define MAX_PRCNAM_LEN 15 #define MAX_STDIOE_LEN 1024 #define MAX_JOBPARM_LEN 1024 #define MAX_JOB_LEN 8192 /* The length of the buffer used for storing each argument of the job command */ #define TIMEOUT_ERROR (MAX_SYSERR + 1) /* a special value to differentiate it from the rest of errno's */ #define CHILD_FLAG_ENV "gtmj0" #define CLEAR_CHILD_FLAG_ENV "gtmj0=""" #define GBLDIR_ENV "gtmgbldir" #define JOB_SOCKET_PREFIX "SOCKET:" #define IS_JOB_SOCKET(ADDR, LEN) ((LEN >= SIZEOF(JOB_SOCKET_PREFIX)) && (0 == STRNCMP_LIT(ADDR, JOB_SOCKET_PREFIX))) #define JOB_SOCKET_HANDLE(ADDR) (((char *)(ADDR)) + SIZEOF(JOB_SOCKET_PREFIX) - 1) #define JOB_SOCKET_HANDLE_LEN(LEN) (LEN - SIZEOF(JOB_SOCKET_PREFIX) + 1) GBLREF int job_errno; /******************************************************************************************************************** * Following enum is used to identify the cause of error in the middle process (M) to the main thread (P) * during the startup of a Job. (passed to the parent (P) through the exit status). * The last two enums are special. They MUST be the last two for any new additions in the future. * The last one identifies the end of the enum list. Last but one (joberr_tryagain) is used to identify * the situations where trying again by the main thread might succeed (like errors due to insufficient * swap space etc..). When the middle process comes across an error that it thinks is worth trying again, * it adds joberr_tryagain to the main status (one of the status' upto joberr_tryagain) and exits with the new status. * * Additions to this enum must match the joberrs array in joberr.h. *********************************************************************************************************************/ typedef enum { joberr_ok, joberr_gen, joberr_io_stdin_open, joberr_io_stdin_dup, joberr_io_stdout_creat, joberr_io_stdout_open, joberr_io_stdout_dup, joberr_io_stderr_creat, joberr_io_stderr_open, joberr_io_stderr_dup, joberr_cd_toolong, joberr_cd, joberr_rtn, joberr_sid, joberr_sp, joberr_frk, joberr_stdout_rename, joberr_stderr_rename, joberr_pipe_mp, joberr_pipe_mgc, joberr_stdin_socket_lookup, joberr_stdout_socket_lookup, joberr_stderr_socket_lookup, joberr_io_stdin_socket_dup, joberr_io_stdout_socket_dup, joberr_io_stderr_socket_dup, joberr_io_setup_op_write, joberr_io_setup_write, joberr_sig, joberr_stp, /* These two should stay at the end of the enum. */ joberr_end } joberr_t; typedef struct job_parm_struct { mval *parm; struct job_parm_struct *next; } job_parm; typedef struct job_param_str_struct { size_t len; char buffer[MAX_JOBPARM_LEN]; } job_param_str; struct job_params_struct { job_param_str directory; job_param_str gbldir; job_param_str startup; job_param_str input; job_param_str output; job_param_str error; job_param_str routine; job_param_str label; int offset; int baspri; }; typedef struct { struct job_params_struct params; job_param_str cmdline; job_parm *parms; size_t input_prebuffer_size; char *input_prebuffer; boolean_t passcurlvn; char *curlvn_buffer_ptr; size_t curlvn_buffer_size; } job_params_type; typedef enum { jpdt_nul, jpdt_num, jpdt_str } jp_datatype; #define JPDEF(a,b) a typedef enum { #include "jobparams.h" } jp_type; typedef enum { job_done, /* last message */ job_set_params, /* followed by a job_params_msg message */ job_set_parm_list, /* followed by a job_arg_count_msg and "arg_count" job_arg_msg messages */ job_set_input_buffer, /* followed by a job_buffer_size_msg and a data message of "buffer_size" */ job_set_locals, /* followed by local_variable */ local_trans_done /* Indicates all of the locals have been sent to the grandchild */ } job_setup_op; typedef struct job_params_struct job_params_msg; typedef size_t job_arg_count_msg; typedef struct { ssize_t len; /* negative len indicates null arg */ char data[MAX_JOB_LEN]; } job_arg_msg; typedef size_t job_buffer_size_msg; int ojchildioset(job_params_type *jparms); int ojstartchild(job_params_type *jparms, int argcnt, boolean_t *non_exit_return, int pipe_fds[]); void ojparams(char *p, job_params_type *job_params); void ojchildioclean(void); void ojpassvar_hook(void); void local_variable_marshalling(FILE *output); #endif fis-gtm-V7.0-005/sr_unix/kitstart.csh0000755000032200000250000004743214342376330016422 0ustar librarygtc#!/usr/local/bin/tcsh ################################################################# # # # Copyright (c) 2011-2021 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. # # # ################################################################# # kitstart.csh creates distribution kits for pro and dbg and bta. # In order to test any configure.csh changes, copy a modified version into /usr/library/Vxxx/pro/configure # and /usr/library/Vxxx/dbg/configure. Then execute $gtm_tools/kitstart.csh -ti Vxxx and check output log. # If changes are made to the configure script, such as removing files or changing permissions in the install # directory there may need to be changes made to files used by kitstart.csh to execute # $gtm_tools/gtm_compare_dist.csh. # # set echo # set verbose # # root user of most (distribution) servers has default umask of 022. Some has 077 # The file permissions while creating kits and installing are individually handled (with specific chmod operations) # The below umask is to have uniformity across servers for files in intermediate directories, especially when they # are compared against the installed copies (gtm_compare_dir.csh) umask 22 # Make sure don't start in utf-8 mode if ($?gtm_chset) then if (M != $gtm_chset) then set longline='$LC_CTYPE, $gtm_dist, and $gtmroutines for M mode' echo '$gtm_chset'" = $gtm_chset, so change to M mode and also check "$longline exit endif endif # This script needs root privileges to # - test install GT.M # - set file ownership to 40535 set euser = `whoami` if ("$euser" != "root") then echo "You must have root privileges to run kitstart" exit -1 endif # we need s_linux and s_linux64 here source $btc_tools/cms_cshrc.csh source $btc_tools/server_list.csh # To set $distrib_servers_unix setenv PATH "/usr/local/bin:/usr/sbin:/usr/ccs/bin:/usr/bin:/bin" # get the osname and arch type from the tables in server_list.csh set servers = ( $distrib_servers_unix ) set platformarch = ( $distrib_unix_platformarch ) foreach server ( $servers ) @ index++ if ("$server" =~ *${HOST:r:r:r}*) then set os_arch=$platformarch[$index] # contortion alert! get the OS_ARCH value from the list set os_arch="${os_arch:s/_/ /}" # and spilt OS_ARCH into "OS ARCH" and set os_arch=( ${os_arch:s/_/ /} ) # enclose it inside parenthesis to force conversion to an array break endif end # if not found in distribution servers then try from uname in case -allow entered if (! $?os_arch) then # make a directory in /tmp to use to run unamearch.m setenv randstr `$gtm_dist/mumps -r %XCMD 'do ^%RANDSTR'` set tempdir = /tmp/kit_unamearch_${randstr} mkdir $tempdir cp $btc_tools/unamearch.m $tempdir set os_arch=`(cd $tempdir; $gtm_dist/mumps -r unamearch $distrib_unix_platformarch%$uname_platformarch)` if ("" == "$os_arch") then echo "Problem getting platform and arch from uname -a" rm -rf $tempdir exit 1 endif set os_arch="${os_arch:s/_/ /}" # and spilt OS_ARCH into "OS ARCH" and set os_arch=( ${os_arch:s/_/ /} ) # enclose it inside parenthesis to force conversion to an array rm -rf $tempdir endif set osname = $os_arch[1] set arch = $os_arch[2] set package = "tar cf" set repackage = "tar rf" set package_ext = "tar" if ("$osname" == "os390") then set package = "pax -w -x pax -f" set repackage = "pax -a -f" set package_ext = "pax" endif set syntaxerr = 0 set arguments = "$argv" if ($#argv < 1) then set syntaxerr = 1 else set testinstall = 0 set leavedir = 0 # when present, do not fire off a background kitstart if ("$1" == "logfile") then set logfile = 1 shift endif # perform a test installation if ("$1" == "-ti") then set testinstall = 1 shift endif # build a kit on a non-dist server set allow = 0 if ("$1" == "-allow") then set allow = 1 shift endif # Test the test install if ("$1" == "-tti") then set testinstall = 1 set leavedir = 1 set allow = 1 shift endif if ("$1" == "" || "$2" != "" && "$2" != "pro" && "$2" != "dbg" && "$2" != "bta") then set syntaxerr = 1 endif endif if ($syntaxerr) then echo "" echo "Usage : $0 [-ti] [-allow] [-tti] [pro | dbg | bta]" echo "" echo " : Version with no punctuations; create distribution of this GT.M version (must be in $gtm_root)" echo "-ti : Test installation" echo "-allow : allow kit to be built on a non-distribution server" echo "-tti : Test the test installation, implies -allow and always leaves dist, tmp_dist, install directories" echo "[pro | dbg | bta] : Create distribution of this image; or pro and dbg if not specified" echo "" exit 1 endif if (! $allow && ("$distrib_servers_unix" !~ *${HOST:r:r:r}*)) then echo "This is not a distribution server. Exiting." exit endif set version = "${1:au}" # ':au' - 'a' means apply to the whole string and 'u' means uppercase everything set imagetype = "pro dbg" if ($2 != "") then set imagetype = $2 endif if (! -d $gtm_root/$version) then echo "" echo "$gtm_root/$version does not exist" echo "" exit 2 endif # make sure $gtm_tools version is same as indicated in $version if ($gtm_tools:h:t != $version) then echo "" echo "$version selected for kit should equal $gtm_tools:h:t in gtm_tools" echo "" exit 1 endif if (! $?logfile) then set fname = ${gtm_root}/$version/log/kitstart.`date +%Y%m%d%H%M` echo "output will be in $fname" $0 logfile $arguments >&! $fname set save_status = $status grep -E "PASS|FAIL" $fname exit $save_status endif ######################################################################################## set setactive_parms = ( $version p ) ; source $gtm_tools/setactive.csh set zver = `$gtm_dist/mumps -run %XCMD 'write $zversion'` set releasever = $zver[2] set icuver = `setenv gtm_dist $gtm_dist ; $gtm_tools/is_icu_symbol_rename.csh` if ("" != "$icuver") setenv gtm_icu_version "$icuver" # create a README.txt which has the current year in it setenv readme_txt ${gtm_com}/README.txt set year = `date +%Y` sed "s/#YEAR#/$year/" $btc_tools/license_README.txt > $readme_txt chmod 444 $readme_txt # Set the open source flag and set lib_specific to the platform specific directories that needs to # be copied as a part of open source distribution (down the script) set open_source = 0 set GNU_COPYING_license = "${gtm_com}/COPYING" if ("$osname" == "linux" && ( "$arch" == "i586" || "x8664" == "$arch" )) then set open_source = 1 /bin/cp -pf $btc_tools/opensource_COPYING.txt $GNU_COPYING_license chmod 444 $GNU_COPYING_license endif set product = "gtm" set dist = "$gtm_ver/dist" set tmp_dist = "$gtm_ver/tmp_dist" set install = "$gtm_ver/install" set dist_prefix = "${product}_${version}_${osname}_${arch}" set mnotdistributed = '{CHK2LEV,CHKOP,GENDASH,GENOUT,GETNEAR,GTMDEFINEDTYPESTODB,GTMHLPLD,GTMTHREADGBLASM,LOAD,LOADOP,LOADVX,MSG,TTTGEN,TTTSCAN,UNLOAD}.[om] GDE*.m' #BYPASSOKLENGTH set notdistributed = '_*.o *.log map obj plugin/libgtm* plugin/gpgagent.tab plugin/gtmcrypt/maskpass plugin/r plugin/o' set utf8_notdistributed = '_*.o *.m *.log map obj [a-z]*' if (-d $dist || -d $tmp_dist || -d $install) then echo "" echo "$dist or $tmp_dist or $install exists. Exiting..." exit 3 endif echo "" set opensource_dist = "${dist}/opensource" if (1 == $open_source) then echo "Creating $dist (for non open source customers) and $opensource_dist (for open source)" mkdir -p $opensource_dist || exit 4 else echo "Creating $dist" mkdir $dist || exit 4 endif foreach image ($imagetype) set rm_from_dist = "" echo "" echo "Creating ${tmp_dist}/${image}" mkdir -p ${tmp_dist}/${image} || exit 5 cd ${tmp_dist}/${image} || exit 7 echo "" echo "Copying files from ${gtm_ver}/${image}" set cpflags="-r" if ("aix" == $osname) set cpflags="-rh" cp ${cpflags} ${gtm_ver}/${image}/* . || exit 8 # Put pinentry into the plugin directory so that it ends up in the final package cp ${gtm_pct}/pinentry.m ./plugin/gtmcrypt/ # Move debug symbols out of the way if they exist set debug_symbol_dir = "${tmp_dist}/${image}_DBG/" mkdir -p "$debug_symbol_dir" if ( -f ${tmp_dist}/${image}/mumps.debug ) then set rm_from_dist = "$rm_from_dist `echo *.debug`" mv *.debug $debug_symbol_dir pushd $debug_symbol_dir set dist_file = "${dist}/${product}-debug_${version}_${osname}_${arch}_${image}.${package_ext}" echo "" echo "Packaging debug symbols to ${dist_file}" cp $gtm_tools/install_debug_symbols_sh.txt install_debug_symbols.sh chmod +x install_debug_symbols.sh $package $dist_file *.debug install_debug_symbols.sh || exit 10 echo "Gzipping $dist_file" gzip $dist_file || exit 11 # Move debug symbols out of the way before other packages get made echo "Moving debug symbols to $debug_symbol_dir during kitstart" popd endif echo "" echo "Removing files that are not distributed (${notdistributed} ${mnotdistributed})" set rm_from_dist = "$rm_from_dist `echo $mnotdistributed`" /bin/rm -rf ${notdistributed} ${mnotdistributed} || exit 9 if (-e utf8) then cd utf8 /bin/rm -rf ${utf8_notdistributed} ${mnotdistributed} || exit 9 cd .. endif # add the README.txt file cp $readme_txt README.txt || exit 9 # add the custom_errors_sample.txt file cp $gtm_tools/custom_errors_sample.txt . || exit 9 if (-e gtmsecshrdir) then $gtm_com/IGS gtmsecshr "UNHIDE" # make root-owned gtmsecshrdir world-readable chmod u+w gtmsecshrdir endif if (-e GTMDefinedTypesInit.m) then set dist_file = "${dist}/GTMDefinedTypesInit_${version}_${osname}_${arch}_${image}.${package_ext}" echo "" echo "Creating $dist_file" $package $dist_file README.txt GTMDefinedTypesInit.m || exit 10 echo "Gzipping $dist_file" gzip $dist_file || exit 11 if (1 == $open_source) then echo "" echo "Creating GTMDefinedTypesInit distribution for open source (includes GNU License)" echo "" echo "Copying $GNU_COPYING_license to $cwd" /bin/cp $GNU_COPYING_license . || exit 8 set dist_file="${opensource_dist}/GTMDefinedTypesInit_${version}_${osname}_${arch}_${image}.${package_ext}" echo "" echo "Creating $dist_file" $package $dist_file README.txt COPYING GTMDefinedTypesInit.m || exit 10 echo "" echo "Gzipping $dist_file" gzip $dist_file || exit 11 rm -f COPYING || exit 9 endif echo "Removing GTMDefinedTypesInit" set rm_from_dist = "$rm_from_dist `echo GTMDefinedTypesInit.*`" rm -f GTMDefinedTypesInit.* || exit 9 if (-e utf8) then cd utf8 rm -f GTMDefinedTypesInit.* || exit 9 cd .. endif else echo "" echo "FAIL:GTMDefinedTypesInit was not found" endif set dist_file = "${dist}/${dist_prefix}_${image}.${package_ext}" # no files to be executable or writeable find . -type f -exec chmod a-xw {} \; # no directories to be writeable for group or world if aix or 32-bit linux, otherwise for all chmod a+x configure chmod a+x gtminstall if ((aix == ${osname}) || ((linux == ${osname}) && ("i586" == "$arch"))) then find . -type d -exec chmod go-w {} \; else find . -type d -exec chmod a-w {} \; endif # use 40535 for owner and group find . -exec chown 40535:40535 {} \; echo "" echo "Creating $dist_file" $package $dist_file . || exit 10 echo "" echo "Gzipping $dist_file" gzip $dist_file || exit 11 if (1 == $open_source) then echo "" echo "Creating distribution for open source (includes GNU License)" echo "" echo "Copying $GNU_COPYING_license to $cwd" /bin/cp $GNU_COPYING_license . || exit 8 chown 40535:40535 COPYING set dist_file="${opensource_dist}/${dist_prefix}_${image}.${package_ext}" echo "" echo "Creating $dist_file" $package $dist_file . || exit 10 echo "" echo "Gzipping $dist_file" gzip $dist_file || exit 11 rm -f COPYING || exit 9 endif set not_in_dist_${image} = `echo $rm_from_dist | sed 's/ /|/g'` end echo "" if (-f $gtm_tools/gtmpcat.m) then pushd $gtm_tools set nonomatch ; set fldbld = (gtmpcat*On*${version}.m) ; unset nonomatch if (("$fldbld" == "gtmpcat*On*${version}.m") || ($#fldbld > 1)) then echo "" echo "FAIL:missing or duplicate gtmpcat field build file ($fldbld)" else set dist_file = "${dist}/gtmpcat_for_${version}_${osname}_${arch}.${package_ext}" echo "" echo "Creating $dist_file" sed "s/#ZVERSION#/${zver}/;s/#FLDBLD#/${fldbld}/" < install_gtmpcat_sh.txt > install_gtmpcat.sh cat gtmpcat_sh.txt > gtmpcat.sh chmod 500 install_gtmpcat.sh gtmpcat.sh set prev_user = `filetest -U gtmpcat.m` set prev_group = `filetest -G gtmpcat.m` set prev_perm = `filetest -P: gtmpcat.m` chown 0:0 gtmpcat.m $fldbld chmod 400 gtmpcat.m $fldbld $package $dist_file gtmpcat.m $fldbld install_gtmpcat.sh gtmpcat.sh || exit 10 chown ${prev_user}:${prev_group} gtmpcat.m $fldbld chmod ${prev_perm} gtmpcat.m $fldbld echo "" echo "Gzipping $dist_file" gzip $dist_file || exit 11 endif popd else echo "" echo "FAIL:gtmpcat was not found" endif set verowner = `filetest -U: $gtm_ver` set vergroup = `filetest -G: $gtm_ver` find $dist -type f -exec chmod 444 {} \; find $dist -type d -exec chmod 755 {} \; chown -R ${verowner}:${vergroup} $dist echo "Files in $dist" /bin/ls -lR $dist echo "" set kitver = ${gtm_ver:t:s/V//} if ($testinstall) then echo "" echo "Testing installation" echo "" echo "Creating $install" mkdir ${install} || exit 12 foreach image ($imagetype) echo "" echo "Testing installation for $image" cd ${tmp_dist}/${image} || exit 13 # V54000 introduced tests for installation validity. The post v54000(includes it) creates an installation # for comparison using gtm_compare_build.csh later, but this is based on a restricted group # installation. We now include an unrestricted group installation into the "${install}/defgroup" directory # for automated testing later. This will also verify the correct gtmsecshr permissions for both types # of installation. We answer "n" to the last question to remove files since we need them for the # restricted build. # V54002 now asks for an installation group (newline entered for default) so response needs one more # blank line for default group # V54003 now asks whether or not to retain .o files if libgtmutil.so is created # We answer "y" to this question # If libgtmutil.so is not created(on i586) this question is not asked if ("$osname" == "linux" && "$arch" == "i586") then sh ./configure << CONFIGURE_EOF n ${install}/defgroup/${image} y y n n CONFIGURE_EOF else sh ./configure << CONFIGURE_EOF n ${install}/defgroup/${image} y y n y n CONFIGURE_EOF endif # We need for root to be a member of the restricted group so that it can run tests. root is a # member of the gtmsec NIS group. setenv rootgroup gtmsec # V54002 now asks for an installation group before the restricted group question so response is # reversed from V54000 # V54003 now asks whether or not to retain .o files if libgtmutil.so is created # We answer "y" to this question # If libgtmutil.so is not created(on i586) this question is not asked if ("$osname" == "linux" && "$arch" == "i586") then sh ./configure << CONFIGURE_EOF $rootgroup y ${install}/${image} y y n y CONFIGURE_EOF else sh ./configure << CONFIGURE_EOF $rootgroup y ${install}/${image} y y n y y CONFIGURE_EOF endif # exit if the installation of the image failed if ($status) then echo "" echo "Installation of $image failed; configure returned error" exit 14 endif if ("`/bin/ls`" != "") then echo "" echo "Installation of $image failed; leftover files in ${tmp_dist}/${image}" /bin/ls -l exit 15 endif # compare the files and directories in the installation to those in the build if ("pro" == ${image}) then # create the build.dir. Only have to do it once cd $gtm_ver || exit 14 # Filter away files not distributed ls -l pro pro/gtmsecshrdir | grep -vEw "$not_in_dist_pro|obj|map|lowerc_cp|arch|configure|gtminstall" | \ awk '{if (NF > 2) print $1,$NF}' >> ${tmp_dist}/build.dir # make a defgroup directory under ${tmp_dist} and copy in the build.dir for use in # first iteration of the while loop mkdir ${tmp_dist}/defgroup cp ${tmp_dist}/build.dir ${tmp_dist}/defgroup set defgroup = "defgroup" @ both = 0 # Exclude files that are created only during installation and not in regular builds set install_only_files = "README.txt|custom_errors_sample.txt|libgtmutil.so" set install_only_files = "$install_only_files|install_permissions.log|install_sha256_checksum.log" while (2 > $both) # create the install.dir from both installations cd ${install}/$defgroup ls -l pro pro/gtmsecshrdir |& grep -vEw "$install_only_files" | \ awk '{if (NF > 2) print $1,$NF}' >> ${tmp_dist}/$defgroup/install.dir cd ${tmp_dist}/${image} env defgroup=$defgroup $gtm_tools/gtm_compare_dir.csh ${install} ${tmp_dist}/$defgroup set teststat = $status if ($teststat) then echo "" echo "Comparison of build and install directories failed." echo "Look in ${tmp_dist}/$defgroup/dircompare/diff.out" chmod -R ugo+rwx ${tmp_dist}/$defgroup/dircompare exit 16 endif # to simplify the code to do the gtm_compare_dir.csh for both restricted and unrestricted group # installations in a loop, we set defgroup to null for the second pass. This takes advantage of # the unix/linux path interpretation where dir//subdir is the same as dir/subdir # Also, if the value of $defgroup is "" (in the second round), dircompare.m removes # all world permissions from dev file listing set defgroup = "" @ both = $both + 1 end endif # test the default group installation chmod a+x $install ${install}/defgroup ${install}/defgroup/${image} $gtm_tools/gtm_test_install.csh ${install}/defgroup/${image} set teststat = $status echo "" echo "" if (! $teststat) then echo "Test of installation for default group ${version}/defgroup/${image} PASSED" else echo "Test of installation for default group ${version}/defgroup/${image} FAILED" set leavedir = 1 endif # test the group restricted installation $gtm_tools/gtm_test_install.csh ${install}/${image} set teststat = $status echo "" echo "" if (! $teststat) then echo "Test of installation for group restricted ${version}/${image} PASSED" else echo "Test of installation for group restriced ${version}/${image} FAILED" set leavedir = 1 endif # Install gtmpcat pushd $gtm_tools yes | env gtm_dist=${install}/defgroup/${image} sh ./install_gtmpcat.sh yes | env gtm_dist=${install}/${image} sh ./install_gtmpcat.sh popd # Test gtmpcat pushd /tmp foreach dir (defgroup/${image} ${image}) set gtmpcatout = "${install}/$dir/invoke_gtmpcat.out" set gtmcshrc = "$install/$dir/gtmcshrc" (source $gtmcshrc ; $gtm_dist/mumps -r %XCMD 'zsystem "$gtm_dist/gtmpcat "_$job') |& tee $gtmpcatout grep -q 'GTMPCAT Complete' $gtmpcatout if (0 == $status) then echo "Test of installation of gtmpcat ${version}/$dir PASSED" else echo "Test of installation of gtmpcat ${version}/$dir FAILED" endif end popd # install debug symbols if ( ( "`uname`" == "Linux" ) && ("pro" == "$image") ) then pushd ${tmp_dist}/${image}_DBG/ yes | env gtm_dist=${install}/${image} sh ./install_debug_symbols.sh popd # Test debug symbols set result=`source ${install}/${image}/gtmcshrc ; gdb $gtm_dist/mumps -ex quit | grep mumps.debug | wc -l` echo "" echo "" if ( $result ) then echo "Test of installation of debug symbols ${version}/${image} PASSED" else echo "Test of installation of debug symbols ${version}/${image} FAILED" endif endif end endif cd $gtm_ver || exit 16 if (! $leavedir) then echo "" echo "Removing temporary directories" echo "" echo "" /bin/rm -rf ${tmp_dist} ${install} /bin/rm -f $gtm_tools/{gtmpcat.sh,install_gtmpcat.sh} exit 0 endif echo "" echo "Distribution creation/testing failed. Leaving directories ${tmp_dist} ${install}" exit 17 fis-gtm-V7.0-005/sr_unix/lib_list_ar.sed0000755000032200000250000000075414342376330017032 0ustar librarygtc################################################################# # # # Copyright 2002 Sanchez Computer Associates, 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. # # # ################################################################# # Append ".o" suffix to end of every file name. s/$/.o/ fis-gtm-V7.0-005/sr_unix/lib_list_lint.sed0000755000032200000250000000077514342376330017401 0ustar librarygtc################################################################# # # # Copyright 2002 Sanchez Computer Associates, 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. # # # ################################################################# # Append ".c" suffix to end of every file name (input to lint). s/$/.c/ fis-gtm-V7.0-005/sr_unix/libdse.list0000755000032200000250000000110014342376330016173 0ustar librarygtcdse_adrec dse_adstar dse_all dse_b_dmp dse_cache dse_chng_bhead dse_chng_fhead dse_chng_rhead dse_cmd_disallow dse_crit dse_ctrlc_handler dse_data dse_dmp dse_dmp_fhead dse_eval dse_exhaus dse_exit dse_f_blk dse_f_free dse_f_key dse_f_reg dse_fdmp dse_find_gvt dse_find_roots dse_flush dse_getblk dse_getki dse_help dse_integ dse_is_blk_free dse_is_blk_in dse_ksrch dse_lm_blk_free dse_m_rest dse_maps dse_open dse_order dse_over dse_page dse_puttime dse_r_dmp dse_range dse_remove dse_rest dse_rmrec dse_rmsb dse_save dse_shift dse_wcreinit dump_record skan_offset skan_rnum fis-gtm-V7.0-005/sr_unix/liblke.list0000755000032200000250000000024314342376330016202 0ustar librarygtclke_clean lke_clear lke_clearlock lke_cleartree lke_ctrlc_handler lke_exit lke_fileio lke_getansw lke_getcli lke_getki lke_help lke_show lke_showlock lke_showtree fis-gtm-V7.0-005/sr_unix/libmupip.list0000755000032200000250000000700514342376330016564 0ustar librarygtcbin_load db_ipcs_reset desired_db_format_set ext2jnl go_load gtm_tempnam gtm_zlib gtmrecv gtmrecv_ch gtmrecv_changelog gtmrecv_checkhealth gtmrecv_comm_init gtmrecv_end gtmrecv_end_helpers gtmrecv_exit gtmrecv_fetchresync gtmrecv_get_opt gtmrecv_helpers_init gtmrecv_poll_actions gtmrecv_process gtmrecv_reap_helpers gtmrecv_reinit_logseqno gtmrecv_showbacklog gtmrecv_shutdown gtmrecv_start_helpers gtmrecv_statslog gtmrecv_upd_proc_init gtmsource gtmsource_ch gtmsource_changelog gtmsource_checkhealth gtmsource_comm_init gtmsource_ctl_init gtmsource_end gtmsource_exit gtmsource_flush_fh gtmsource_get_opt gtmsource_heartbeat gtmsource_jnlpool gtmsource_mode_change gtmsource_poll_actions gtmsource_process gtmsource_process_ops gtmsource_readfiles gtmsource_readpool gtmsource_reinit_logseqno gtmsource_rootprimary_init gtmsource_seqno_init gtmsource_showbacklog gtmsource_shutdown gtmsource_statslog gtmsource_stopfilter gv_select jnl2ext jnlext_write jnlpool_hasnt_overflowed mu_all_version_standalone mu_clsce mu_cre_structs mu_decrypt mu_dwngrd_header mu_extr_gblout mu_extr_getblk mu_extr_ident mu_extract mu_freeze_ch mu_getkey mu_getlst mu_gv_stack_init mu_gvis mu_int_blk mu_int_ch mu_int_err mu_int_fhead mu_int_init mu_int_maps mu_int_read mu_int_reg mu_int_reg_ch mu_int_wait_rdonly mu_int_write mu_op_open mu_outofband_setup mu_put_gvdata mu_put_gvn_fragment mu_reduce_level mu_reorg mu_reorg_upgrd_dwngrd mu_replpool_grab_sem mu_replpool_release_sem mu_rndwn_all mu_rndwn_file mu_rndwn_repl_instance mu_rndwn_replpool mu_rndwn_rlnkctl mu_signal_process mu_size_arsample mu_size_impsample mu_size_scan mu_split mu_swap_blk mu_swap_root mu_term_setup mu_trig_trgfile mu_truncate mu_upgrd_dngrd_confirmed mu_upgrd_header mubclnup mubexpfilnam mubfilcpy mubgetfil mubinccpy mup_bak_sys mupip_backup mupip_cmd_disallow mupip_create mupip_crypt mupip_ctrl mupip_cvtgbl mupip_cvtpgm mupip_downgrade mupip_endiancvt mupip_exit mupip_exit_handler mupip_extend mupip_freeze mupip_ftok mupip_hash mupip_help mupip_integ mupip_intrpt mupip_quit mupip_rctldump mupip_recover mupip_reorg mupip_reorg_encrypt mupip_restore mupip_rundown mupip_set mupip_set_file mupip_set_jnl_ch mupip_set_jnl_cleanup mupip_set_jnlfile mupip_set_jnlfile_aux mupip_set_journal mupip_set_journal_fname mupip_set_journal_newstate mupip_set_journal_parse mupip_size mupip_stop mupip_trigger mupip_upgrade mupip_upgrade_standalone mur_apply_pblk mur_back_process mur_block_count_correct mur_blocks_free mur_close_file_extfmt mur_close_files mur_cre_file_extfmt mur_db_files_from_jnllist mur_do_wildcard mur_forward mur_forward_play_cur_jrec mur_forward_play_multireg_tp mur_gbldefs mur_get_options mur_get_pini mur_init mur_insert_prev mur_jctl_from_next_gen mur_jnl_ext mur_merge_sort_extfmt mur_multi_rehash mur_open_files mur_output_record mur_output_show mur_pini_addr_reset mur_pini_state mur_process_intrpt_recov mur_process_seqno_table mur_process_timequal mur_process_token_table mur_put_aimg_rec mur_read_file mur_read_file_sp mur_rem_jctls mur_report_error mur_select_rec mur_sort_files mur_token_lookup mur_tp_resolve_time mur_validate_checksum mur_write_header_extfmt mur_ztp_lookback murgetlst read_db_files_from_gld repl_comm repl_filter repl_inst_create repl_inst_dump repl_inst_edit repl_ipc_cleanup repl_log repl_log_init repl_logfileinfo_get repl_tr_good replic_gbldefs ss_anal_shdw_file ss_get_block ss_initiate ss_read_block trigger_upgrade upd_log_init updhelper_end updhelper_init updhelper_reader updhelper_writer updproc updproc_end updproc_get_gblname updproc_init updproc_open_files fis-gtm-V7.0-005/sr_unix/libstub.list0000755000032200000250000000026214342376330016405 0ustar librarygtcgtcmstub cmistub gtcmtrstub gvcmz_error_stub gvcmz_neterr_stub gvcmx_reqremlk_stub gvcmx_resremlk_stub gvcmx_susremlk_stub gvcmx_canremlk_stub gvcmz_zflush_stub gvcmz_bunch_stub fis-gtm-V7.0-005/sr_unix/lintgtm.csh0000755000032200000250000001324114342376330016222 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2020 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. # # # ################################################################# # ################################################################################## # # lintgtm.csh - lint C sources used to build GT.M # # arguments: # $1 - version number (without punctuation) or code letter: # e.g., "V123" => version "V1.2-3" or: # "a" => current active (in current process) release # "d" => current development release # "p" => current production release # $2 - "gtm_bta" => lint bta images ($gtm_ver/bta) # "gtm_dbg" => lint dbg images ($gtm_ver/dbg) # "gtm_pro" => lint pro images ($gtm_ver/pro) # $3 - any lint options in addition to the defaults # ################################################################################## echo "Start of $0 `date`" echo "" echo "lint'ed on $HOST" echo "" echo "arguments: '$1' '$2' '$3'" echo "" echo "" echo "lint-related aliases:" echo "" alias | grep lint | sort echo "" echo "" echo "lint-related environment variables:" echo "" env | grep lint | sort echo "" echo "" set lintgtm_status = 0 set lintgtm_start_directory = `pwd` if ( "$HOSTOS" == "OSF1" && "$MACHTYPE" == "alpha" ) then # Check to make certain the system header files have been properly edited # to include the #pragma's necessary to preserve 64-bit pointer sizes # independent of C compiler options. grep xtaso_header_edit /usr/include/stdio.h > /dev/null if ( $status != 0 ) then echo "lintgtm-E-xtaso_header_edit: system header files do not support 32-bit pointers" echo "lintgtm-I-xtaso_header_edit: you need to run \$gtm_tools/xtaso_header_edit as superuser" set lintgtm_status = -10 goto lint.END endif endif # Verify arguments: # Default to linting the current version. if ( $1 == "" ) then set p1 = "$gtm_verno" else set p1 = $1 endif # Default to linting the current images. if ( $2 == "" ) then switch ( `basename $gtm_exe` ) case "gtm_bta": set p2 = "b" breaksw case "gtm_dbg": set p2 = "d" breaksw case "gtm_pro": set p2 = "p" breaksw endsw else set p2 = $2 endif # Define image type-specific information. set setactive_parms = ( $p1 $p2 ) ; source $gtm_tools/setactive.csh # Default lint options. set p3 = "$3" switch ( $p2 ) case "gtm_bta": set gt_lint_options = "$gt_lint_options_common $gt_lint_options_bta $p3" breaksw case "gtm_dbg": set gt_lint_options = "$gt_lint_options_common $gt_lint_options_dbg $p3" breaksw case "gtm_pro": set gt_lint_options = "$gt_lint_options_common $gt_lint_options_pro $p3" breaksw endsw cd $gtm_exe if ( ! -d ./lint ) then mkdir ./lint endif cd ./lint # Remove any left-over files. rm * set eol_anchor = '$' set gi = ($gtm_inc) set gs = ($gtm_src) cp $gtm_inc/*.h . ls $gs[1] | egrep '\.c$' | xargs -i cp "$gs[1]/{}" . chmod +w *.c *.h set lintgtm_verbose = $?verbose set lintgtm_liblist = "dse lke mupip stub mumps" foreach i ( $lintgtm_liblist ) echo "Start of $i lint library creation: `date`" echo "lint options: ${gt_lint_option_output}$i $gt_lint_options" > llib-l$i.log echo "" >> llib-l$i.log switch ( $i ) case "dse": case "lke": case "mupip": case "stub": pwd sed -f $gtm_tools/lib_list_lint.sed $gtm_tools/lib$i.list >& lib$i.list gt_lint ${gt_lint_option_output}$i $gt_lint_options $gt_lint_options_library `cat lib$i.list` >>& llib-l$i.log rm -f `cat lib$i.list` breaksw case "mumps": # (Almost) everything else goes into llib-lmumps.ln, but the list is too long for a single command line # so use xargs. This case must be executed last in the switch statement (because it picks up "everything # else") and, hence, must appear last in the for statement. # Exclude files that define the same externals (e.g., "main" and the VMS CLI [command line interpreter] # emulator arrays): pwd rm -f gtm.c lke.c lke_cmd.c dse.c dse_cmd.c mupip.c mupip_cmd.c daemon.c gtmsecshr.c gt_lint ${gt_lint_option_output}$i $gt_lint_options $gt_lint_options_library *.c >>& llib-l$i.log rm *.c breaksw endsw echo "" >> llib-l$i.log echo "End of $i lint library creation: `date`" echo "" end # $shell $gtm_tools/lintshr.csh $p1 # for true parallelism, the following commands would be in lintshr.csh cp $gtm_src/{gtm.c . gt_lint $gt_lint_options gtm.c llib-l{mumps,stub}.ln $gt_lint_syslibs \ >& lint.mumps.log # $shell $gtm_tools/lintaux.csh $p1 # for true parallelism, the following commands would be in lingaux.csh cp $gtm_src/{dse,dse_cmd}.c . gt_lint $gt_lint_options {dse,dse_cmd}.c llib-l{dse,mumps,stub}.ln $gt_lint_syslibs >& lint.dse.log cp $gtm_src/gtmsecshr.c . gt_lint $gt_lint_options gtmsecshr.c llib-lmumps.ln $gt_lint_syslibs >& lint.gtmsecshr.log cp $gtm_src/{lke,lke_cmd}.c . gt_lint $gt_lint_options {lke,lke_cmd}.c llib-l{lke,mumps,stub}.ln $gt_lint_syslibs >& lint.lke.log cp $gtm_src/{mupip,mupip_cmd}.c . gt_lint $gt_lint_options {mupip,mupip_cmd}.c llib-l{mupip,mumps,stub,dse}.ln $gt_lint_syslibs >& lint.mupip.log chmod +w *.c rm *.h *.c lint.END: if ( $lintgtm_verbose == "0" ) then unset verbose endif # Return to starting directory: cd $lintgtm_start_directory # Clean up environment variables: unsetenv lintgtm_gt_as unsetenv lintgtm_gt_cc # Clean up local shell variables: unset lintgtm_liblist unset lintgtm_start_directory unset p1 unset p2 unset p3 echo "" echo "End of $0 `date`" exit $lintgtm_status fis-gtm-V7.0-005/sr_unix/lintimage.csh0000755000032200000250000000622014342376330016514 0ustar librarygtc#!/usr/local/bin/tcsh ################################################################# # # # Copyright (c) 2001-2019 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. # # # ################################################################# ################################################################## # # lintimage.csh - submit background task to lint GT.M # # lintimage.csh is an interactive script to run lint on a # release of GT.M. It prompts the user for 3 # parameters: # version - version to be built (default: current) # images - type of images (default: current) # lint options in addition to defaults # ################################################################## # # Get the version number/designation: echo " " echo -n "Enter Version " echo -n "[$gtm_verno]: " set lintimage_ver = $< if ( "$lintimage_ver" == "" ) then set lintimage_ver = $gtm_verno endif echo " " # Get the image type: if ( "$gtm_exe" == "" ) then set lintimage_image = "p" else # Convert current image type to single-character prompt set lintimage_image = `basename $gtm_exe` switch ( $lintimage_image ) case "b*": set lintimage_image = "b" breaksw case "d*": set lintimage_image = "d" breaksw case "p*": default: set lintimage_image = "p" breaksw endsw endif echo -n "Enter Image " echo -n "[$lintimage_image]: " set lintimage_image_input = $< if ( "$lintimage_image_input" != "" ) then set lintimage_image = $lintimage_image_input endif echo " " # Convert to name and set default compiler and assembler options. # N.B.: These default options must be calculated the same way gtm_env.csh and gtm_env_sp.csh calculate them. switch ( $lintimage_image ) case "[Bb]*": set lintimage_image = "bta" set lintimage_lint_options_default = "$gt_lint_options_common $gt_lint_options_bta" breaksw case "[Dd*]": set lintimage_image = "dbg" set lintimage_lint_options_default = "$gt_lint_options_common $gt_lint_options_dbg" breaksw case "[Pp]*": set lintimage_image = "pro" set lintimage_lint_options_default = "$gt_lint_options_common $gt_lint_options_pro" breaksw endsw # Get lint options: echo "Enter additional lint options" echo " [default: $lintimage_lint_options_default]" echo -n ' --> ' set lintimage_lint_options_extra = "$<" if ( "$lintimage_lint_options_extra" != "" ) then echo " [new: $lintimage_lint_options_default $lintimage_lint_options_extra]" endif echo " " set setactive_parms = ( $lintimage_ver $lintimage_image ) ; source $gtm_tools/setactive.csh if ( ! -d $gtm_ver/log ) then mkdir $gtm_ver/log chmod 775 $gtm_ver/log endif nohup /usr/local/bin/tcsh $gtm_tools/lintgtm.csh \ "$lintimage_ver" "gtm_$lintimage_image" "$lintimage_lint_options_extra" \ >& $gtm_ver/log/lintgtm.$lintimage_image.log < /dev/null & unset lintimage_lint_options_default unset lintimage_lint_options_extra unset lintimage_image unset lintimage_image_input unset lintimage_ver fis-gtm-V7.0-005/sr_unix/linuxi686_badd.txt0000644000032200000250000000227714342376330017340 0ustar librarygtc################################################################# # # # Copyright 2011, 2013 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. # # # ################################################################# # Do not add any blank lines after this comment pro: TTTGEN.m%-r--r----- README.txt dse%-r--r----- custom_errors_sample.txt gdehelp.dat%-r--r----- gdedefaults gtm_common_defs.h%-r-xr-x--- gtm gtmcshrc.gtc%-r-xr-x--- gtmbase gtmhelp.dat%-r-xr-x--- gtmcshrc gtmsecshr%-r-xr-x--- gtmprofile gtmsecshr%-r-xr-x--- gtmprofile_preV54000 pro/plugin: zzz_insert%dr-xr-x--- o zzz_insert%dr-xr-x--- r pro/plugin/gtmcrypt: zzz_insert%-r--r----- source.tar pro/utf8: TTTGEN.m -> ../TTTGEN.m%lrwxrwxrwx README.txt -> ../README.txt dse -> ../dse%lrwxrwxrwx custom_errors_sample.txt -> ../custom_errors_sample.txt gtmstart.gtc -> ../gtmstart.gtc%-r-sr-x--- gtmsecshr gtmstart.gtc -> ../gtmstart.gtc%dr-x------ gtmsecshrdir pro/utf8/gtmsecshrdir: zzz_insert%-r-s------ gtmsecshr fis-gtm-V7.0-005/sr_unix/list_file.c0000755000032200000250000001426114342376330016166 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_string.h" #include "gtm_time.h" #include "io.h" #include "io_params.h" #include "cmd_qlf.h" #include "parse_file.h" #include "list_file.h" #include "op.h" #include "have_crit.h" #define LISTEXT ".lis" GBLREF char rev_time_buf[], source_file_name[]; GBLREF command_qualifier cmd_qlf; GBLREF int (*op_open_ptr)(mval *v, mval *p, mval *t, mval *mspace); GBLREF io_pair io_curr_device; GBLREF list_params lst_param; GBLREF mident module_name; GBLREF unsigned short source_name_len; LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; LITREF mval literal_zero; static char print_time_buf[20]; static io_pair dev_in_use; static readonly struct { unsigned char newversion; unsigned char wrap; unsigned char width; unsigned char v_width[SIZEOF(int4)]; # ifdef __MVS__ unsigned char chsetebcdic[8]; # else unsigned char chsetm[3]; # endif unsigned char eol; } open_params_list = { (unsigned char)iop_newversion, (unsigned char)iop_wrap, (unsigned char)iop_recordsize, # ifdef BIGENDIAN {(unsigned char)0, (unsigned char)0, (unsigned char)0, (unsigned char)132}, # else {(unsigned char)132, (unsigned char)0, (unsigned char)0, (unsigned char)0}, # endif # ifdef __MVS__ {(unsigned char)iop_chset, 6, 'E', 'B', 'C', 'D', 'I', 'C'}, # else {(unsigned char)iop_chset, 1, 'M'}, # endif (unsigned char)iop_eol }; void open_list_file(void) { char charspace, cp, fname[MAX_FN_LEN + 1], list_name[MAX_MIDENT_LEN + STR_LIT_LEN(LISTEXT)], *p; mstr fstr; mval file, parms; parse_blk pblk; time_t clock; uint4 status; lst_param.list_line = 1; lst_param.page = 0; memset(&pblk, 0, SIZEOF(pblk)); for (cp = source_name_len - 1; cp && ('/' != source_file_name[cp]); cp--) ; /* scan back from the end to find the source file name */ cp += cp ? 1 : 0; pblk.def1_size = source_name_len - cp - SIZEOF(DOTM) + 1; assert(pblk.def1_size <= MAX_MIDENT_LEN); memcpy(list_name, &source_file_name[cp], pblk.def1_size); MEMCPY_LIT(&list_name[pblk.def1_size], LISTEXT); pblk.def1_size += STR_LIT_LEN(LISTEXT); pblk.def1_buf = list_name; pblk.buffer = &fname[0]; pblk.buff_size = MAX_FN_LEN; pblk.fop = F_SYNTAXO; fstr.len = (MV_DEFINED(&cmd_qlf.list_file) ? cmd_qlf.list_file.str.len : 0); fstr.addr = cmd_qlf.list_file.str.addr; if (!(status = parse_file(&fstr, &pblk)) & 1) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status); file.mvtype = parms.mvtype = MV_STR; file.str.len = pblk.b_esl; file.str.addr = &fname[0]; parms.str.len = SIZEOF(open_params_list); parms.str.addr = (char *)&open_params_list; (*op_open_ptr)(&file, &parms, (mval *)&literal_zero, 0); parms.str.len = 1; charspace = (char)iop_eol; parms.str.addr = &charspace; dev_in_use = io_curr_device; op_use(&file,&parms); clock = time(0); GTM_CTIME(p, &clock); memcpy (print_time_buf, p + 4, SIZEOF(print_time_buf)); list_head(0); return; } void close_list_file(void) { mval param,list_file; char charspace; param.str.len = 1; charspace = (char) iop_eol; param.str.addr = &charspace; list_file.mvtype = param.mvtype = MV_STR; list_file.str.len = io_curr_device.in->trans_name->len; list_file.str.addr = &io_curr_device.in->trans_name->dollar_io[0]; op_close(&list_file, ¶m); io_curr_device = dev_in_use; } void list_head(bool newpage) { short col_2 = 70; static readonly unsigned char page_lit[] = "page "; unsigned char page_no_buf[10]; mval head; if (newpage) op_wtff(); head.mvtype = MV_STR; head.str.addr = (char *)>m_release_name[0]; head.str.len = gtm_release_name_len; op_write (&head); op_wttab(col_2); head.str.addr = print_time_buf; head.str.len = 20; op_write(&head); op_wttab(100); lst_param.page++; head.str.addr = (char *)page_lit; head.str.len = SIZEOF(page_lit) - 1; op_write(&head); head.str.addr = (char *)page_no_buf; head.str.len = INTCAST(i2asc(page_no_buf, lst_param.page) - page_no_buf); op_write(&head); op_wteol(1); head.str.addr = source_file_name; head.str.len = source_name_len; op_write(&head); if (source_name_len >= col_2) op_wteol(1); op_wttab(col_2); head.str.addr = rev_time_buf; head.str.len = 20; op_write(&head); op_wteol(3); } #define BIG_PG 32 #define BIG_PG_BOT_SP 10 #define SMALL_PG_BOT_SP 3 void list_line(char *c) { short n, c_len, space_avail; mval out; if (io_curr_device.out->dollar.y >= lst_param.lines_per_page - ((lst_param.lines_per_page < BIG_PG) ? SMALL_PG_BOT_SP : BIG_PG_BOT_SP)) list_head(1); out.mvtype = MV_STR; c_len = (short)strlen(c); while(c_len > 0) { if (c_len < (space_avail = PG_WID - io_curr_device.out->dollar.x)) space_avail = c_len; out.str.len = space_avail; out.str.addr = c; op_write(&out); c_len -= space_avail; c += space_avail; if (c_len > 0) { assert(io_curr_device.out->dollar.x != 0); op_wteol(1); } } if ((n = lst_param.lines_per_page - io_curr_device.out->dollar.y) < lst_param.space) { assert(n > 0); op_wteol(n); } else op_wteol(lst_param.space); } void list_line_number(void) { unsigned char buf[8]; int n,m, i, q; unsigned char *pt; mval out; assert(cmd_qlf.qlf & CQ_LIST); if (io_curr_device.out->dollar.y >= lst_param.lines_per_page - ((lst_param.lines_per_page < BIG_PG) ? SMALL_PG_BOT_SP : BIG_PG_BOT_SP)) list_head(1); n = lst_param.list_line++; pt = &buf[5]; memset(&buf[0],SP,SIZEOF(buf)); do { i = n / 10; q = n - (i * 10); *--pt = q + '0'; n = i; } while(i > 0); out.mvtype = MV_STR; out.str.addr = (char*)buf; out.str.len = SIZEOF(buf); op_write(&out); } void list_chkpage(void) { if (io_curr_device.out->dollar.y >= lst_param.lines_per_page - ((lst_param.lines_per_page < BIG_PG) ? SMALL_PG_BOT_SP : BIG_PG_BOT_SP)) list_head(1); } void list_tab(void) { op_wttab(LISTTAB); return; } fis-gtm-V7.0-005/sr_unix/lke.c0000644000032200000250000001137414342376330014766 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "main_pragma.h" #include "gtm_inet.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_signal.h" #include "mlkdef.h" #include "gtm_stdlib.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "stp_parms.h" #include "iosp.h" #include "error.h" #include "cli.h" #include "io.h" #include "stringpool.h" #include "interlock.h" #include "gtmimagename.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdskill.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" #include "tp.h" #include "repl_msg.h" #include "gtmsource.h" #include "util.h" #include "gt_timer.h" #include "lke.h" #include "lke_fileio.h" #include "gtm_startup_chk.h" #include "generic_signal_handler.h" #include "cli_parse.h" #include "getzdir.h" #include "getjobname.h" #include "sig_init.h" #include "gtmmsg.h" #include "suspsigs_handler.h" #include "patcode.h" #include "common_startup_init.h" #include "gtm_threadgbl_init.h" #include "gtmio.h" #include "have_crit.h" #include "gt_timers_add_safe_hndlrs.h" #include "continue_handler.h" #include "restrict.h" #include "dm_audit_log.h" #ifdef UTF8_SUPPORTED # include "gtm_icu_api.h" # include "gtm_utf8.h" # include "gtm_conv.h" GBLREF u_casemap_t gtm_strToTitle_ptr; /* Function pointer for gtm_strToTitle */ #endif GBLREF VSIG_ATOMIC_T util_interrupt; GBLREF bool licensed; GBLREF void (*func)(void); GBLREF spdesc rts_stringpool, stringpool; GBLREF char cli_err_str[]; GBLREF CLI_ENTRY lke_cmd_ary[]; GBLREF ch_ret_type (*stpgc_ch)(); /* Function pointer to stp_gcol_ch */ GBLREF void (*primary_exit_handler)(void); GBLDEF CLI_ENTRY *cmd_ary = &lke_cmd_ary[0]; /* Define cmd_ary to be the LKE specific cmd table */ GBLREF IN_PARMS *cli_lex_in_ptr; static bool lke_process(int argc); static void display_prompt(void); error_def(ERR_CTRLC); error_def(ERR_RESTRICTEDOP); int main (int argc, char *argv[]) { DCL_THREADGBL_ACCESS; GTM_THREADGBL_INIT; common_startup_init(LKE_IMAGE); licensed = TRUE; err_init(util_base_ch); UTF8_ONLY(gtm_strToTitle_ptr = >m_strToTitle); sig_init(generic_signal_handler, lke_ctrlc_handler, suspsigs_handler, continue_handler); atexit(util_exit_handler); primary_exit_handler = util_exit_handler; stp_init(STP_INITSIZE); stpgc_ch = &stp_gcol_ch; rts_stringpool = stringpool; getjobname(); getzdir(); gtm_chk_dist(argv[0]); prealloc_gt_timers(); gt_timers_add_safe_hndlrs(); initialize_pattern_table(); if (RESTRICTED(lke)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_RESTRICTEDOP, 1, "LKE"); gvinit(); region_init(FALSE); /* Was TRUE, but that doesn't actually work if there are GTCM regions in the GLD, * at least in DEBUG, so leave it off for now to allow LKE to work in this situation. */ io_init_name(); cli_lex_setup(argc, argv); /* this should be after cli_lex_setup() due to S390 A/E conversion */ OPERATOR_LOG_MSG; while (1) { if (!lke_process(argc) || 2 <= argc) break; } lke_exit(); return 0; } static bool lke_process(int argc) { bool flag = FALSE; int res, status; static int save_stderr = SYS_STDERR; ESTABLISH_RET(util_ch, TRUE); if (util_interrupt) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CTRLC); if (SYS_STDERR != save_stderr) /* necesary in case of rts_error */ close_fileio(&save_stderr); assert(SYS_STDERR == save_stderr); func = 0; util_interrupt = 0; if (argc < 2) display_prompt(); res = parse_cmd(); if (EOF != res) { status = log_cmd_if_needed(cli_lex_in_ptr->in_str); if (status) return FALSE; } if (EOF == res) { if (util_interrupt) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CTRLC); REVERT; return TRUE; } else { REVERT; return FALSE; } } else if (res) { if (1 < argc) { REVERT; rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) res, 2, LEN_AND_STR(cli_err_str)); } else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) res, 2, LEN_AND_STR(cli_err_str)); } if (func) { flag = open_fileio(&save_stderr); /* save_stderr = SYS_STDERR if -output option not present */ func(); if (flag) close_fileio(&save_stderr); assert(SYS_STDERR == save_stderr); } REVERT; return(1 >= argc); } static void display_prompt(void) { PRINTF("LKE> "); FFLUSH(stdout); } fis-gtm-V7.0-005/sr_unix/lke_cmd.c0000755000032200000250000001033414342376330015607 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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. * * * ****************************************************************/ /* * -------------------------------------------------------------- * Parser tables. * Entries need to be made in sorted order (lexicographic) within * each table. * * This file might have lines longer than 132 characters * since the command tables are being initialized. * -------------------------------------------------------------- */ #include "mdef.h" #include "mlkdef.h" #include "cmidef.h" #include "cli.h" #include "lke.h" #include "util_spawn.h" #include "util_help.h" #include "lke_cmd_disallow.h" static readonly CLI_ENTRY clear_qual[] = { { "ALL", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "EXACT", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "INTERACTIVE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "LOCK", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "OUTPUT", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "PID", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, VAL_DCM }, { "REGION", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "" } }; static readonly CLI_ENTRY show_qual[] = { { "ALL", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "CRITICAL", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "LOCK", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "MEMORY", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "OUTPUT", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "PID", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, VAL_DCM }, { "REGION", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "WAIT", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_STR, 0 }, { "" } }; /* These must be alphabetized */ static readonly CLI_ENTRY clnup_qual[] = { { "ALL", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "INTEG", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "PERIODIC", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, 0 }, { "REGION", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "" } }; static readonly CLI_ENTRY growhash_qual[] = { { "ALL", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "REGION", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "" } }; static readonly CLI_ENTRY rehash_qual[] = { { "ALL", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "REGION", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "" } }; /* Main command table (cmd_ary is defined to this array in lke.c) * This table contains the names of all commands, and corresponding functions to be * dispatched to, and the qualifier sub-tables, containing all legal qualifiers. */ GBLDEF CLI_ENTRY lke_cmd_ary[] = { { "CLEAR", lke_clear, clear_qual, 0, 0, cli_disallow_lke_clear, 0, VAL_NOT_REQ, 2, 0, VAL_STR, 0}, { "CLNUP", lke_clean, clnup_qual, 0, 0, 0, 0, VAL_NOT_REQ, 0, 0, 0, 0}, { "EXIT", lke_exit, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0}, { "GROWHASH", lke_growhash, growhash_qual, 0, 0, 0, 0, VAL_NOT_REQ, 0, 0, 0, 0}, { "HELP", util_help, 0, 0, 0, 0, 0, VAL_NOT_REQ, 1, 0, 0, 0}, { "REHASH", lke_rehash, rehash_qual, 0, 0, 0, 0, VAL_NOT_REQ, 0, 0, 0, 0}, { "SHOW", lke_show, show_qual, 0, 0, 0, 0, VAL_NOT_REQ, 1, 0, VAL_STR, 0}, { "SPAWN", util_spawn, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0}, { "" } }; fis-gtm-V7.0-005/sr_unix/lke_cmd_disallow.c0000755000032200000250000000153714342376330017512 0ustar librarygtc/**************************************************************** * * * Copyright 2008 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 "gtm_stdio.h" #include "gtm_string.h" #include "cli.h" #include "cli_parse.h" #include "cli_disallow.h" #include "lke_cmd_disallow.h" GBLREF char *cli_err_str_ptr; boolean_t cli_disallow_lke_clear(void) { int disallow_return_value = 0; *cli_err_str_ptr = '\0'; disallow_return_value = d_c_cli_present("EXACT") && !d_c_cli_present("LOCK"); CLI_DIS_CHECK_N_RESET; return FALSE; } fis-gtm-V7.0-005/sr_unix/lke_cmd_disallow.h0000755000032200000250000000117614342376330017516 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2008-2018 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. * * * ****************************************************************/ #ifndef LKE_CMD_DISALLOW_H_INCLUDED #define LKE_CMD_DISALLOW_H_INCLUDED boolean_t cli_disallow_lke_clear(void); #endif fis-gtm-V7.0-005/sr_unix/lke_ctrlc_handler.c0000755000032200000250000000126014342376330017646 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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 #include "mlkdef.h" #include "error.h" #include "cmidef.h" #include "lke.h" #include "have_crit.h" GBLREF VSIG_ATOMIC_T util_interrupt; void lke_ctrlc_handler(int sig) { util_interrupt = 1; } fis-gtm-V7.0-005/sr_unix/lke_fileio.c0000755000032200000250000000522714342376330016320 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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. * * * ****************************************************************/ /* lke_fileio.c */ #include "mdef.h" #include "gtm_fcntl.h" #include "gtm_string.h" #include "gtm_unistd.h" #include "error.h" #include "cli.h" #include "eintr_wrappers.h" #include "util.h" #include "lke_fileio.h" #include "gtmio.h" #ifdef __MVS__ #include "gtm_stat.h" #include "gtm_zos_io.h" #endif /* This function will process I/O if -OUTPUT suboption is present. This will create the file and duplicate it as stderr. So anything written to stderr will be written to this file. */ bool open_fileio(int *save_stderr) { char ofnamebuf[1024]; mstr ofname; int status=FALSE, fd; unsigned short len; char *errmsg; #ifdef __MVS__ int realfiletag, fstatus; struct stat info; /* Need the ERR_BADTAG and ERR_TEXT error_defs for the TAG_POLICY macro warning */ error_def(ERR_TEXT); error_def(ERR_BADTAG); #endif *save_stderr = SYS_STDERR; ofname.addr=ofnamebuf; ofname.len=SIZEOF(ofnamebuf); if (cli_present("OUTPUT") == CLI_PRESENT) { len = ofname.len; if (cli_get_str("OUTPUT", ofname.addr, &len)) { int dup2_res; ZOS_ONLY(STAT_FILE(ofname.addr, &info, fstatus);) /* create output file */ CREATE_FILE(ofname.addr, 0666, fd); if (0 > fd) { errmsg = STRERROR(errno); util_out_print("Cannot create !AD.!/!AD", TRUE, len, ofname.addr, RTS_ERROR_STRING(errmsg)); return status; } /* All output from LKE is done through util_out_print which uses stderr so make output file as new stderr */ *save_stderr = dup(SYS_STDERR); DUP2(fd, SYS_STDERR, dup2_res); #ifdef __MVS__ if (0 == fstatus) { if (-1 == gtm_zos_tag_to_policy(fd, TAG_UNTAGGED, &realfiletag)) TAG_POLICY_GTM_PUTMSG(ofname.addr, realfiletag, TAG_UNTAGGED, errno); } else { if (-1 == gtm_zos_set_tag(fd, TAG_EBCDIC, TAG_TEXT, TAG_FORCE, &realfiletag)) TAG_POLICY_GTM_PUTMSG(ofname.addr, realfiletag, TAG_EBCDIC, errno); } #endif status=TRUE; } else util_out_print("Error getting FILE name", TRUE); } return status; } /* Close the file I/O and restore stderr */ void close_fileio(int *save_stderr) { int dup2_res; int rc; DUP2(*save_stderr, SYS_STDERR, dup2_res); CLOSEFILE_RESET(*save_stderr, rc); /* resets "*save_stderr" to FD_INVALID */ *save_stderr = SYS_STDERR; } fis-gtm-V7.0-005/sr_unix/lke_fileio.h0000755000032200000250000000117714342376330016325 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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. * * * ****************************************************************/ #ifndef LKE_FILEIO_INCLUDED #define LKE_FILEIO_INCLUDED #define SYS_STDERR 2 bool open_fileio(int *save_stderr); void close_fileio(int *save_stderr); #endif /* LKE_FILEIO_INCLUDED */ fis-gtm-V7.0-005/sr_unix/lke_getansw.c0000755000032200000250000000226514342376330016520 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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_ctype.h" #include "gtm_stdio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "iosp.h" #include "mlkdef.h" #include "lke.h" #include "util.h" /* * ----------------------------------------------- * Read terminal input, displaying a prompt string * * Return: * TRUE - the answer was 'Y' * FALSE - answer is 'N' * ----------------------------------------------- */ bool lke_get_answ(char *prompt) { char buff[11], *bp; PRINTF("%s", prompt); if (NULL != (bp = util_input(buff, 10, stdin, TRUE))) { if (*bp == 'Y' || *bp == 'y') return (TRUE); } return(FALSE); } fis-gtm-V7.0-005/sr_unix/lke_help.c0000755000032200000250000000135414342376330015776 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "gtm_stdio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "iosp.h" #include "mlkdef.h" #include "lke.h" void lke_help(void) { /* This function is a STUB to avoid editting sr_port/lke.h */ } fis-gtm-V7.0-005/sr_unix/load.h0000755000032200000250000000432514342376330015140 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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. * * * ****************************************************************/ #ifndef LOAD_INCLUDED #define LOAD_INCLUDED /* * RECLOAD error message is used in 2 cases. * 1. Range of records ( Eg: Error loading record number: 3 to 10 ) * 2. Single record ( Eg: Error loading record number: 3 ) */ #define MAX_RECLOAD_ERR_MSG_SIZE ((STR_LEN_OF_E_9 * 2) + SIZEOF(" to ") + 1) #define FAKE_BIG_KEY_COUNT 4294967196UL /* (2**32)-100=4294967196 */ #define ONERROR_STOP 0 #define ONERROR_PROCEED 1 #define ONERROR_INTERACTIVE 2 #define BADZCHSET -1 #define ONERROR_PROCESS \ { \ GBLREF int onerror; \ \ if (ONERROR_STOP == onerror) \ { \ break; \ } \ if (ONERROR_INTERACTIVE == onerror && !mu_interactive("Load terminated by operator\n")) \ { \ onerror = ONERROR_STOP; \ /* User selected Not to proceed */ \ break; \ } \ mupip_error_occurred = FALSE; \ continue; /* continue, when (onerror = ONERROR_PROCEED) or when user selects Yes in ONERROR_INTERACTIVE */ \ } void bin_load(gtm_uint64_t begin, gtm_uint64_t end, char *line1_ptr, int line1_len); void go_call_db(int routine, char *parm1, int parm2, int val_off1, int val_len1); int go_get(char **in_ptr, int max_len, uint4 max_rec_size); void go_load(gtm_uint64_t begin, gtm_uint64_t end, unsigned char *recbuf, char *line3_ptr, int line3_len, uint4 max_rec_size, int fmt, int utf8_extract, int dos); void goq_load(void); int get_load_format(char **line1_ptr, char **line3_ptr, int *line1_len, int *line3_len, uint4 *max_rec_size, int *utf8_extract, int *dos); boolean_t gtm_regex_perf(const char *rexpr, char *str_buff); #endif /* LOAD_INCLUDED */ fis-gtm-V7.0-005/sr_unix/lockdefs.h0000755000032200000250000000135214342376330016010 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #define MLK_LOGIN(x) #define BLOCKING_PROC_DEAD(w,x,y,z) (!is_proc_alive((w)->blocked->owner, 0)) #define PENDING_PROC_DEAD(w,x,y,z) (!is_proc_alive((w)->process_id, 0)) #define PROC_DEAD(w,x,y,z) (!is_proc_alive((w)->owner, 0)) fis-gtm-V7.0-005/sr_unix/lowerc_cp.sh0000755000032200000250000000170414342376330016357 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2022 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. # # # ################################################################# SHELL=/bin/sh export SHELL ext=.m curdir=`pwd` utf8=`basename $curdir` for i in $* do base=`basename $i $ext` if test $i != $base then { newf=`echo $base | tr '[A-Z]' '[a-z]'` if test $base != $newf then { dir=`dirname $i` if [ "utf8" = $utf8 ]; then echo $dir/$i "---> " $newf$ext "-> "../$newf$ext ln -fs ../$newf$ext $newf$ext else echo $dir/$i "---> " $dir/$newf$ext cp -p $i $dir/$newf$ext fi } fi } fi done fis-gtm-V7.0-005/sr_unix/m_zrupdate.c0000644000032200000250000000341014342376330016355 0ustar librarygtc/**************************************************************** * * * Copyright 2014 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 "compiler.h" #include "mdq.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "advancewindow.h" #include "cmd.h" #ifdef AUTORELINK_SUPPORTED /* Routine to compile ZRUPDATE command * * Current syntax: * * ZRUPDATE [,.. * * Where: * * object-file-path - Is the path and filename of an object file that may include wildcards. We allow multiple specifications * separated by commas. We let cmd.c break these into separate commands. * * Parameters: none * Returncode: success (TRUE) and failure (FALSE) * * Future enhancement - Allow paths to be grouped by surrounding with parens. At that point, this becomes a variable length * parameter list but for now tiz simplistic. */ int m_zrupdate(void) { oprtype objfilespec; triple *triptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; switch (expr(&objfilespec, MUMPS_STR)) { case EXPR_FAIL: return FALSE; case EXPR_GOOD: triptr = newtriple(OC_ZRUPDATE); triptr->operand[0] = put_ilit(1); /* Currently single arg but that will change for phase2 */ triptr->operand[1] = objfilespec; return TRUE; case EXPR_INDR: make_commarg(&objfilespec, indir_zrupdate); return TRUE; } assert(FALSE); return FALSE; } #endif /* USHBIN_SUPPORTED */ fis-gtm-V7.0-005/sr_unix/m_ztrigger.c0000644000032200000250000000231414342376330016356 0ustar librarygtc/**************************************************************** * * * Copyright 2010, 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 "compiler.h" #include "mdq.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "advancewindow.h" #include "cmd.h" error_def(ERR_GBLEXPECTED); int m_ztrigger(void) { # ifdef GTM_TRIGGER oprtype tmparg; triple *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; switch (TREF(window_token)) { case TK_CIRCUMFLEX: if (!gvn()) return FALSE; ref = newtriple(OC_ZTRIGGER); break; case TK_ATSIGN: if (!indirection(&tmparg)) return FALSE; ref = maketriple(OC_COMMARG); ref->operand[0] = tmparg; ref->operand[1] = put_ilit((mint)indir_ztrigger); ins_triple(ref); return TRUE; default: stx_error(ERR_GBLEXPECTED); return FALSE; } return TRUE; # else return FALSE; # endif } fis-gtm-V7.0-005/sr_unix/mu_rndwn_rlnkctl.c0000644000032200000250000001212314342376334017572 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2021 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" #ifdef AUTORELINK_SUPPORTED #include "gtm_string.h" #include "gtm_stat.h" #include "gtm_stdlib.h" #include "gtm_limits.h" #include "relinkctl.h" #include "util.h" #include /* needed for zroutines.h */ #include "zroutines.h" #include "cli.h" #include "cliif.h" #include "cli_parse.h" #include "eintr_wrappers.h" #include "gtmmsg.h" #endif #include "mu_rndwn_rlnkctl.h" /* for mupip_rctldump prototype */ #ifdef AUTORELINK_SUPPORTED error_def(ERR_FILEPARSE); error_def(ERR_MUPCLIERR); error_def(ERR_RLNKCTLRNDWNSUC); #endif /* Implements MUPIP RUNDOWN -RELINKCTL */ void mu_rndwn_rlnkctl(void) { # ifdef AUTORELINK_SUPPORTED open_relinkctl_sgm *linkctl; relinkshm_hdr_t *shm_hdr; relinkrec_t *linkrec; relinkctl_data *hdr; rtnobjshm_hdr_t *rtnobj_shm_hdr; char objdir[GTM_PATH_MAX]; int i, j, recnum, n_records, shmid, shm_stat, save_errno, objcnt, stat_res; unsigned short param_len; mstr dir; zro_ent *op; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; TREF(is_mu_rndwn_rlnkctl) = TRUE; /* relinkctl_open and relinkctl_rundown check this flag to act differently. * No need to have condition handler to reset this in case of error because * this is invoked from MUPIP RUNDOWN -RELINKCTL which is the only action * that the calling process will do in its lifetime. */ if (TREF(parms_cnt)) { assert(1 == TREF(parms_cnt)); param_len = SIZEOF(objdir) - 1; if (!cli_get_str("WHAT", objdir, ¶m_len)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MUPCLIERR); objdir[param_len] = '\0'; dir.addr = objdir; dir.len = param_len; linkctl = relinkctl_attach(&dir, NULL, SIZEOF(objdir)); assert((dir.len <= SIZEOF(objdir) - 1) && (('\0' == dir.addr[dir.len]) || (0 == dir.len))); assert(linkctl == TREF(open_relinkctl_list)); assert((NULL == linkctl) || (NULL == linkctl->next)); if (NULL == linkctl) { if (0 == dir.len) { /* If relinkctl_attach() did not find the object directory corresponding to the user's argument, * dir.len is set to 0; in that case we want to print an error containing the argument, and so the * proper length needs to be restored. */ dir.len = param_len; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_FILEPARSE, 2, RTS_ERROR_MSTR(&dir), errno); } else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_RLNKCTLRNDWNSUC, 2, RTS_ERROR_MSTR(&dir)); } else relinkctl_rundown(TRUE, FALSE); /* Runs down the relinkctl file opened above. */ } else { /* Unlike other callers of zro_init(), MUPIP RUNDOWN -RELINKCTL does not want it to do relinkctl_attach() on all * relinkctl files at once because we leave the attach logic holding the linkctl lock, which might potentially cause * a deadlock if multiple processes are run concurrently with different $gtmroutines. So, zro_init() / zro_load() * set the count field of each autorelink-enabled object directory to a negative number based on the * TREF(is_mu_rndwn_rlnkctl) global, and we look at the count to decide whether to attach to an individual segment. */ zro_init(); objcnt = (TREF(zro_root))->count; assert(0 < objcnt); for (op = TREF(zro_root) + 1; (0 < objcnt--);) { /* Go through each object directory in our array to run down its relinkctl file. */ assert((ZRO_TYPE_OBJECT == op->type) || (ZRO_TYPE_OBJLIB == op->type)); if (ZRO_TYPE_OBJLIB == op->type) continue; /* We only deal with object directories in this loop */ /* Currently the count field of object directory entries is either unused (with the value of 0) or marked as * autorelink-enabled (only in case of MUPIP RUNDOWN -RELINKCTL). Assert that. */ assert(0 >= op->count); if (0 > op->count) { /* Third parameter is 0 because the object directories are stored in fully resolved format in the * gtmroutines object, so there is no need to update the string. */ linkctl = relinkctl_attach(&op->str, NULL, 0); assert(linkctl == TREF(open_relinkctl_list)); assert((NULL == linkctl) || (NULL == linkctl->next)); if (NULL == linkctl) { /* We do not do a check of whether the object directory exists, unlike with an argument, * because zro_init() would have errored out on a non-existent path. */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_RLNKCTLRNDWNSUC, 2, RTS_ERROR_MSTR(&op->str)); } else relinkctl_rundown(TRUE, FALSE); /* Runs down the relinkctl file opened above. */ } /* Bump past source entries to next object entry */ op++; assert(ZRO_TYPE_COUNT == op->type); op += op->count; op++; } } TREF(is_mu_rndwn_rlnkctl) = FALSE; # endif /* AUTORELINK_SUPPORTED */ } fis-gtm-V7.0-005/sr_unix/mu_trig_trgfile.c0000644000032200000250000000445414342376335017403 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2017 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 "gdsroot.h" #include "gtm_string.h" #include "cli.h" #include #include "mu_gv_stack_init.h" #include "trigger_trgfile_protos.h" #include "mu_trig_trgfile.h" #include "mupip_exit.h" GBLREF boolean_t skip_dbtriggers; /* see gbldefs.c for description of this global */ GBLREF IN_PARMS *cli_lex_in_ptr; GBLREF int tprestart_state; error_def(ERR_MUNOACTION); void mu_trig_trgfile(char *trigger_filename, int trigger_filename_len, boolean_t noprompt) { IN_PARMS *cli_lex_in_ptr_save = NULL; boolean_t trigger_error; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Since ^#t global is the ONLY global that is touched by MUPIP TRIGGERS and we currently dont support triggers * on ^#t global, it is safe to set skip_dbtriggers to TRUE for this process. This is in fact needed so we * skip cdb_sc_triggermod processing in t_end and tp_tend as otherwise that could cause unnecessary restarts in * journal recovery and update process which at least the former is not designed to handle. */ skip_dbtriggers = TRUE; mu_gv_stack_init(); if (NULL != cli_lex_in_ptr) { cli_lex_in_ptr_save = (IN_PARMS *)malloc(SIZEOF(IN_PARMS) + cli_lex_in_ptr->buflen); cli_lex_in_ptr_save->argc = cli_lex_in_ptr->argc; cli_lex_in_ptr_save->argv = cli_lex_in_ptr->argv; cli_lex_in_ptr_save->tp = cli_lex_in_ptr_save->in_str + (cli_lex_in_ptr->tp - cli_lex_in_ptr->in_str); cli_lex_in_ptr_save->buflen = cli_lex_in_ptr->buflen; memcpy(cli_lex_in_ptr_save->in_str, cli_lex_in_ptr->in_str, cli_lex_in_ptr->buflen); } else cli_lex_in_ptr_save = NULL; trigger_error = trigger_trgfile_tpwrap(trigger_filename, trigger_filename_len, noprompt); if (NULL != cli_lex_in_ptr) free(cli_lex_in_ptr); cli_lex_in_ptr = cli_lex_in_ptr_save; if (trigger_error) mupip_exit(ERR_MUNOACTION); } fis-gtm-V7.0-005/sr_unix/mupip_rctldump.c0000644000032200000250000000332514342376335017261 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2021 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" #ifdef AUTORELINK_SUPPORTED #include "gtm_string.h" #include "relinkctl.h" #include "util.h" #include /* needed for zroutines.h */ #include "zroutines.h" #include "cli.h" #include "cliif.h" #include "cli_parse.h" #include "zshow.h" #endif #include "mupip_rctldump.h" /* for mupip_rctldump prototype */ #ifdef AUTORELINK_SUPPORTED error_def(ERR_MUPCLIERR); #endif /* Implements MUPIP RCTLDUMP */ void mupip_rctldump(void) { # ifdef AUTORELINK_SUPPORTED unsigned short max_len; mstr dir; char objdir[GTM_PATH_MAX]; open_relinkctl_sgm *linkctl; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TREF(parms_cnt)) { assert(1 == TREF(parms_cnt)); max_len = SIZEOF(objdir); if (!cli_get_str("DIRECTORY", objdir, &max_len)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MUPCLIERR); dir.addr = objdir; dir.len = max_len; linkctl = relinkctl_attach(&dir, NULL, 0); assert(linkctl == TREF(open_relinkctl_list)); assert((NULL == linkctl) || (NULL == linkctl->next)); } else zro_init(); util_out_print("", RESET); /* Reset output buffer */ zshow_rctldump(NULL); /* callee knows caller is mupip_rctldump type based on the NULL parameter */ # endif /* AUTORELINK_SUPPORTED */ } fis-gtm-V7.0-005/sr_unix/mupip_trigger.c0000644000032200000250000001157614342376335017101 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2021 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" #ifdef GTM_TRIGGER #include #include "gtm_stdlib.h" /* for EXIT() */ #include "gtm_limits.h" #include "gtm_stat.h" #include "gtm_string.h" #include "cli.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include #include "gv_trigger.h" #include "mupip_trigger.h" #include "mu_trig_trgfile.h" #include "trigger_select_protos.h" #include "util.h" #include "mupip_exit.h" #include "change_reg.h" #include "targ_alloc.h" #include "gvcst_protos.h" #include "trigger_upgrade_protos.h" #include "restrict.h" GBLREF gd_addr *gd_header; GBLREF boolean_t is_replicator; error_def(ERR_INVSTRLEN); error_def(ERR_MUNOACTION); error_def(ERR_MUPCLIERR); error_def(ERR_NOSELECT); error_def(ERR_RESTRICTEDOP); error_def(ERR_TRIGMODREGNOTRW); void mupip_trigger(void) { char trigger_file_name[MAX_FN_LEN + 1], select_list[MAX_LINE], select_file_name[MAX_FN_LEN + 1]; unsigned short trigger_file_len = MAX_FN_LEN + 1, select_list_len = MAX_LINE; unsigned short sf_name_len; int local_errno; struct stat statbuf; boolean_t noprompt, trigger_error; gd_region *reg, *reg_top; sgmnt_addrs *csa; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (CLI_PRESENT == cli_present("TRIGGERFILE")) { noprompt = (CLI_PRESENT == cli_present("NOPROMPT")); if (!cli_get_str("TRIGGERFILE", trigger_file_name, &trigger_file_len)) { util_out_print("Error parsing TRIGGERFILE name", TRUE); mupip_exit(ERR_MUPCLIERR); } assert('\0' == trigger_file_name[trigger_file_len]); /* should have been made sure by caller */ if (0 == trigger_file_len) { util_out_print("Missing input file name", TRUE); mupip_exit(ERR_MUPCLIERR); } if (RESTRICTED(trigger_mod)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_RESTRICTEDOP, 1, "MUPIP TRIGGER -TRIGGERFILE"); mupip_exit(ERR_MUNOACTION); } is_replicator = TRUE; TREF(ok_to_see_statsdb_regs) = TRUE; gvinit(); mu_trig_trgfile(trigger_file_name, (uint4)trigger_file_len, noprompt); } if (CLI_PRESENT == cli_present("SELECT")) { if (FALSE == cli_get_str("SELECT", select_list, &select_list_len)) mupip_exit(ERR_MUPCLIERR); sf_name_len = MAX_FN_LEN; if (FALSE == cli_get_str("FILE", select_file_name, &sf_name_len)) mupip_exit(ERR_MUPCLIERR); if (0 == sf_name_len) select_file_name[0] = '\0'; else if (-1 == Stat((char *)select_file_name, &statbuf)) { if (ENOENT != errno) { local_errno = errno; perror("Error opening output file"); mupip_exit(local_errno); } } else { util_out_print("Error opening output file: !AD -- File exists", TRUE, sf_name_len, select_file_name); mupip_exit(ERR_MUNOACTION); } trigger_error = trigger_select_tpwrap(select_list, (uint4)select_list_len, select_file_name, (uint4)sf_name_len); if (trigger_error) mupip_exit(ERR_MUNOACTION); } if (CLI_PRESENT == cli_present("UPGRADE")) { /* Invoke MUPIP TRIGGER -UPGRADE */ if (RESTRICTED(trigger_mod)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_RESTRICTEDOP, 1, "MUPIP TRIGGER -UPGRADE"); mupip_exit(ERR_MUNOACTION); } gvinit(); DEBUG_ONLY(TREF(in_trigger_upgrade) = TRUE;) for (reg = gd_header->regions, reg_top = reg + gd_header->n_regions; reg < reg_top; reg++) { if (IS_STATSDB_REGNAME(reg)) continue; GVTR_SWITCH_REG_AND_HASHT_BIND_NAME(reg); csa = cs_addrs; if (NULL == csa) /* not BG or MM access method OR a statsdb region */ continue; if (!csa->hdr->hasht_upgrade_needed) { util_out_print("Triggers in region !AD have already been upgraded", TRUE, REG_LEN_STR(reg)); continue; /* ^#t already upgraded */ } if (reg->read_only) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_TRIGMODREGNOTRW, 2, REG_LEN_STR(reg)); if (0 == gv_target->root) { util_out_print("No triggers found in region !AD and so no upgrade needed", TRUE, REG_LEN_STR(reg)); csa->hdr->hasht_upgrade_needed = FALSE; /* Reset now that we know there is no ^#t global in this db. * Note: It is safe to do so even if we dont hold crit. */ continue; /* no ^#t records exist in this region */ } assert(!dollar_tlevel); assert(!is_replicator); trigger_upgrade(reg); assert(!csa->hdr->hasht_upgrade_needed); /* should have been cleared inside trigger_upgrade */ util_out_print("Triggers in region !AD have been upgraded", TRUE, REG_LEN_STR(reg)); } DEBUG_ONLY(TREF(in_trigger_upgrade) = FALSE;) } } #endif fis-gtm-V7.0-005/sr_unix/maskpass.c0000755000032200000250000000711314342376330016034 0ustar librarygtc/**************************************************************** * * * Copyright 2009, 2014 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 "main_pragma.h" #include #include #include #include #include #include #include #include #include #include #include "gtmxc_types.h" #include "gtmcrypt_util.h" static struct termios *tty = NULL; void maskpass_signal_handler(int sig); int main() { char passwd[GTM_PASSPHRASE_MAX], hex_out[GTM_PASSPHRASE_MAX * 2], mumps_exe[GTM_PATH_MAX], *env_ptr; struct stat stat_info; gtm_string_t passwd_str; struct sigaction reset_term_handler, ignore_handler; int sig; /* Since the obfuscated password depends on $USER and the inode of $gtm_dist/mumps, make sure all the pre-requisites are * available to this process. */ if (NULL == (env_ptr = (char *)getenv(USER_ENV))) { printf(ENV_UNDEF_ERROR "\n", USER_ENV); exit(EXIT_FAILURE); } if (NULL == (env_ptr = (char *)getenv(GTM_DIST_ENV))) { printf(ENV_UNDEF_ERROR "\n", GTM_DIST_ENV); exit(EXIT_FAILURE); } SNPRINTF(mumps_exe, GTM_PATH_MAX, "%s/%s", env_ptr, "mumps"); if (0 != stat(mumps_exe, &stat_info)) { printf("Cannot stat %s\n", mumps_exe); exit(EXIT_FAILURE); } /* We want the process to restore the terminal settings (if they already changed by the time a signal is caught) on the more * conventional terminal signals, such as SIGINT and SIGTERM, and ignore the non-critical other ones. We also do not want to * allow putting the process in the background because the terminal settings may be unsuitable for user interaction at that * point, and the user may decide to "sanitize" them, which might render the entered password visible upon resumption. */ reset_term_handler.sa_handler = maskpass_signal_handler; reset_term_handler.sa_flags = 0; sigfillset(&reset_term_handler.sa_mask); ignore_handler.sa_handler = SIG_IGN; ignore_handler.sa_flags = 0; sigemptyset(&ignore_handler.sa_mask); for (sig = 1; sig <= NSIG; sig++) { switch (sig) { case SIGINT: case SIGTERM: sigaction(sig, &reset_term_handler, NULL); break; case SIGSEGV: case SIGABRT: case SIGBUS: case SIGFPE: case SIGTRAP: case SIGKILL: break; default: sigaction(sig, &ignore_handler, NULL); } } /* Read the password (with terminal echo turned off). */ if (-1 == gc_read_passwd(GTMCRYPT_DEFAULT_PASSWD_PROMPT, passwd, GTM_PASSPHRASE_MAX, &tty)) { printf("%s\n", gtmcrypt_err_string); exit(EXIT_FAILURE); } /* Obfuscate the password. */ passwd_str.address = &passwd[0]; passwd_str.length = (int)STRLEN(passwd); if (-1 == gc_mask_unmask_passwd(2, &passwd_str, &passwd_str)) { printf("%s\n", gtmcrypt_err_string); exit(EXIT_FAILURE); } /* Convert obfuscated password to a hex representation for easy viewing. */ GC_HEX(passwd, hex_out, passwd_str.length * 2); printf("%s\n", hex_out); return 0; } void maskpass_signal_handler(int sig) { /* If gc_read_passwd() changed the terminal settings before we got hit by an interrupt, the original terminal state should * have been saved in tty, so we will only restore the terminal settings when the pointer is non-NULL. */ if (NULL != tty) tcsetattr(fileno(stdin), TCSAFLUSH, tty); exit(-1); } fis-gtm-V7.0-005/sr_unix/mdefsa.h0000755000032200000250000000507514342376330015463 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef MDEFSA_included #define MDEFSA_included /* Declarations common to all unix mdefsp.h, to be moved here */ /* DSK_WRITE_NOCACHE macro needs to be included. Use this flavor if writing direct from storage (not cache buffer) */ #define DSK_WRITE_NOCACHE(reg, blk, ptr, odv, status) \ MBSTART { \ if (-1 == dsk_write_nocache(reg, blk, ptr, odv)) \ status = errno; \ else \ status = 0; \ } MBEND #define DOT_CHAR "." #define DOTM ".m" #define DOTOBJ ".o" #define GTM_DIST "gtm_dist" #define GTM_IMAGE_NAME "mumps" #define GTM_IMAGE_NAMELEN (SIZEOF(GTM_IMAGE_NAME) - 1) #define GTMSECSHR_NAME "gtmsecshr" #define GTMSECSHR_NAMELEN (SIZEOF(GTMSECSHR_NAME) - 1) #define ICU_LIBFLAGS (RTLD_NOW | RTLD_GLOBAL) #define ICU_LIBNAME_ROOT "libicuio" #if defined(__MVS__) # define GTMSHR_IMAGE_NAME "libgtmshr.dll" # define ICU_LIBNAME_EXT "so" #elif defined(__CYGWIN__) # define GTMSHR_IMAGE_NAME "libgtmshr.dll" # define ICU_LIBNAME_EXT "dll" #else # define GTMSHR_IMAGE_NAME "libgtmshr.so" # ifdef _AIX /* Conventionally, AIX archives shared objects into a static library. * So we need to link with a member of the library instead of the library itself. */ # define ICU_LIBNAME_EXT "a" /* AIX system default ICU library uses a different convention for the library name */ # define AIX_SHR_64 "(shr_64.o)" /* Could also be "(libicuio.so)" */ # define ICU_LIBNAME_DEF ICU_LIBNAME_ROOT "." ICU_LIBNAME_EXT AIX_SHR_64 # define LIBRARY_PATH_MAX (GTM_PATH_MAX - SIZEOF(AIX_SHR_64)) # else # define ICU_LIBNAME_EXT "so" # endif #endif #ifndef LIBRARY_PATH_MAX #define LIBRARY_PATH_MAX GTM_PATH_MAX #endif #define ICU_LIBNAME ICU_LIBNAME_ROOT "." ICU_LIBNAME_EXT #define GTM_PLUGIN_FMT_SHORT "%s/plugin/" #define GTM_PLUGIN_FMT_FULL "%s/plugin/%s" #define GTM_MAIN_FUNC "gtm_main" /* Prefix GT.M callback functions with "gtm_" */ #define GTM_PREFIX(func) gtm_##func #define cancel_timer GTM_PREFIX(cancel_timer) #define hiber_start GTM_PREFIX(hiber_start) #define hiber_start_wait_any GTM_PREFIX(hiber_start_wait_any) #endif /* MDEFSA_included */ fis-gtm-V7.0-005/sr_unix/mdefsp.h0000755000032200000250000002076114342376330015501 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ #ifndef MDESP_included #define MDESP_included #include /* All current Unix platforms support {u,}int64_t, so set these up regardless of GTM64 setting. */ #ifndef int8 typedef int64_t int8; /* 8-byte signed integer */ typedef uint64_t uint8; /* 8-byte unsigned integer */ #endif typedef uint64_t gtm_uint8; /* these two datatypes are defined because */ typedef int64_t gtm_int8; /* int8 and uint8 are system defined in AIX_64 */ typedef int64_t gtm_timet; /* Differentiate a type for this so we can dump epochs intelligently to a string */ #define INT8_NATIVE #ifdef __s390__ typedef short int2; /* 2-byte signed integer */ typedef unsigned short uint2; /* 2-byte unsigned integer */ #define LINKAGE_PSECT_BOUNDARY 8 typedef uint2 mach_inst; #endif #define INT8_SUPPORTED #define INT8_FMT "%llu" #define INT8_FMTX "[0x%llx]" #define UTF8_SUPPORTED #define UNIX 1 #undef VMS #define BIGENDIAN 1 #define CNTR_WORD_32 #ifdef __sparc #define CACHELINE_SIZE 256 #define USHBIN_SUPPORTED #define AUTORELINK_SUPPORTED #define LINKAGE_PSECT_BOUNDARY 8 #define OFF_T_LONG #define INO_T_LONG #define MUTEX_MSEM_WAKE #define POSIX_MSEM #undef sssize_t #define sssize_t ssize_t #undef SHMDT #define SHMDT(X) shmdt((char *)(X)) typedef uint4 mach_inst; /* Use rc_mval2subsc only for sun until every DTM client (that needs 16-bit precision as opposed to 18-bit for GT.M) is gone */ #define mval2subsc rc_mval2subsc #ifndef VAR_COPY #define VAR_COPY va_copy #endif #endif /* __sparc */ #if defined(__ia64) #define CACHELINE_SIZE 128 #define INO_T_LONG /* define this for both Linux ia64 and HPUX ia64 as these are 64-bit builds */ #elif defined(__hppa) #define CACHELINE_SIZE 64 #endif /* __ia64 */ #ifdef __hpux #define MUTEX_MSEM_WAKE #define POSIX_MSEM #define USHBIN_SUPPORTED /* #define AUTORELINK_SUPPORTED -- not yet due to issues with replacing mapped object files - revisit if/when support objects in * shared memory. */ #define OFF_T_LONG /* Make sure linkage Psect is aligned on appropriate boundary. */ #ifdef __ia64 #define LINKAGE_PSECT_BOUNDARY 8 #else /* parisc */ #define LINKAGE_PSECT_BOUNDARY 4 #ifdef __GNUC__ typedef unsigned short in_port_t; /* GCC needs this on PARISC */ #endif #endif typedef uint4 mach_inst; /* machine instruction */ #endif /* __hpux */ #if defined(__linux__) || defined(__CYGWIN__) #define OFF_T_LONG #ifdef NeedInAddrPort typedef unsigned short in_port_t; #endif #ifndef VAR_COPY #define VAR_COPY(dst,src) __va_copy(dst, src) #endif #ifdef __s390__ #ifndef Linux390 #define Linux390 #endif #define INO_T_LONG /* see gdsfhead.h, actually for dev_t == 8 on Linux390 2.2.15 */ #endif /* __s390__ */ #endif /* __linux__ */ #ifdef __linux__ #define SYS_ERRLIST_INCLUDE "gtm_stdio.h" #define MUTEX_MSEM_WAKE #define POSIX_MSEM #endif #ifdef __CYGWIN__ #ifdef UTF8_SUPPORTED_OBEYED #undef UTF8_SUPPORTED #endif #define MUTEX_MSEM_WAKE #define POSIX_MSEM #define KEY_T_LONG /* 8 bytes */ #define SYS_ERRLIST_INCLUDE #endif #ifdef __s390__ #define CACHELINE_SIZE 256 #define GTM_CONTEXT(func) (unsigned char *)func #define SSM_SIZE 256*1024*1024 /* Segments on 256M boundary */ #define SHMAT_ADDR_INCS SSM_SIZE #define USHBIN_SUPPORTED #endif /* __s390__ */ #ifdef __ia64 # ifdef __linux__ # undef BIGENDIAN # define USHBIN_SUPPORTED # define AUTORELINK_SUPPORTED /* If this ever comes back */ /* Make sure linkage Psect is aligned on appropriate boundary */ # define LINKAGE_PSECT_BOUNDARY 8 typedef uint4 mach_inst; /* machine instruction */ # elif defined(__hpux) void call_runtime(); void opp_dmode(); void dyncall(); # endif #endif /* __ia64 */ #ifdef __i386 /* Through Pentium Pro/II/III, should use CPUID to get real value perhaps */ #define CACHELINE_SIZE 32 #undef BIGENDIAN typedef char mach_inst; /* machine instruction */ #endif /* __i386 */ #ifdef __x86_64__ #define CACHELINE_SIZE 64 #define USHBIN_SUPPORTED #define AUTORELINK_SUPPORTED #define INO_T_LONG /* #define MUTEX_MSEM_WAKE #define POSIX_MSEM */ #define LINKAGE_PSECT_BOUNDARY 8 #undef BIGENDIAN typedef char mach_inst; /* machine instruction */ #endif #ifdef Linux390 # define INTERLOCK_ADD(X,Y,Z) (interlock_add(Z, (sm_int_ptr_t)(X))) #else # ifdef __linux__ # ifdef __atomic_add_fetch # define INTERLOCK_ADD(X,Y,Z) (__atomic_add_fetch(X, Z, __ATOMIC_SEQ_CST)) # else # define INTERLOCK_ADD(X,Y,Z) (__sync_add_and_fetch(X, Z)) # endif # else # define INTERLOCK_ADD(X,Y,Z) (add_inter(Z, (sm_int_ptr_t)(X), (sm_global_latch_ptr_t)(Y))) # endif #endif /* On NON_USHBIN_ONLY platforms, reserve enough space in routine header for the dummy * string "GTM_CODE". On USHBIN_ONLY platforms, reserve space of 16 bytes that holds * instructions for simple 'return -1' plus as many characters of "GTM_CODE" as can be fit * in the rest of the available bytes */ #if __ia64 #define RHEAD_JSB_SIZE 24 /* We need 16 bytes for putting the 'return -1' instruction + the GTM_CODE string */ #elif __x86_64__ #define RHEAD_JSB_SIZE 16 /* We need 8 bytes for putting the 'return -1' instruction + the GTM_CODE string */ #else #define RHEAD_JSB_SIZE NON_USHBIN_ONLY(8) USHBIN_ONLY(16) #endif /* __ia64 */ typedef struct { unsigned short mvtype; # ifdef BIGENDIAN unsigned char sgn : 1; unsigned char e : 7; # else unsigned char e : 7; unsigned char sgn : 1; # endif unsigned char fnpc_indx; /* Index to fnpc_work area this mval is using */ # ifdef UTF8_SUPPORTED unsigned int utfcgr_indx; /* Index to utfcgr_work area this mval is using */ NON_GTM64_ONLY(unsigned int filler2;) /* To 8 byte align mval on 32 bit platforms */ # endif int4 m[2]; mstr str; } mval; /* Another version of mval struct with byte fields instead of bit fields */ typedef struct { unsigned short mvtype; unsigned char sgne; unsigned char fnpc_indx; /* Index to fnpc_work area this mval is using */ # ifdef UTF8_SUPPORTED unsigned int utfcgr_indx; /* Index to utfcgr_work area this mval is using */ NON_GTM64_ONLY(unsigned int filler2;) /* To 8 byte align mval on 32 bit platforms */ # endif int4 m[2]; mstr str; } mval_b; #define VAR_START(a, b) va_start(a, b) #define VARLSTCNT(a) a, /* push count of arguments*/ #define malloc gtm_malloc #define free gtm_free /* gtm_shmget either calls the native shmget or requests Huge Pages to back the * shared memory segment if the environment variable gtm_hugetlb_shm is set. * Huge Pages are only supported on Linux. */ extern int gtm_shmget(key_t , size_t , int , bool); #ifndef __ia64 #define CODE_ADDRESS(func) (unsigned char *)func #else /* On IA64, there is a need to differentiate between generated code and regular * functions. When regular function addresses are obtained, this macro is * always used, and this will set the bottom two bits of the given address * (which is actually the function descriptor/PLABEL). Later in call_runtime/dyncall, * the bottom two bits will be checked and removed if required and a * dereference will happen to extract the actual target address. * This is done to mimic the behaviour of PLABEL/$$DYNCALL of HPPA on IA64. */ #define CODE_ADDRESS_C(func) ((unsigned char*) ((unsigned long)func | (unsigned long)0x3)) #define CODE_ADDRESS_ASM(func) ((unsigned char *) *(unsigned long *)func) #define CODE_ADDRESS(func) CODE_ADDRESS_ASM(func) #endif /* __ia64 */ #ifndef GTM_CONTEXT #define GTM_CONTEXT(func) 0 /* not used on this target */ #endif /* PSECT in which the address of the module is defined: */ #define GTM_MODULE_DEF_PSECT GTM_CODE #define OS_PAGELET_SIZE 512 #define OS_VIRTUAL_BLOCK_SIZE OS_PAGELET_SIZE #define GTM_MM_FLAGS MAP_SHARED #ifndef SSM_SIZE #define SSM_SIZE OS_PAGE_SIZE #endif typedef volatile int4 latch_t; typedef volatile uint4 ulatch_t; #define INSIDE_CH_SET "ISO8859-1" #define OUTSIDE_CH_SET "ISO8859-1" #define EBCDIC_SP 0x40 #define NATIVE_SP 0x20 #define DEFAULT_CODE_SET ascii /* enum ascii defined in io.h */ #endif /* MDESP_included */ fis-gtm-V7.0-005/sr_unix/mem_access.c0000755000032200000250000000247214342376330016314 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "mem_access.h" /* Set a region of memory to be inaccessable */ void set_noaccess(unsigned char *na_page[], unsigned char *prvprt) /* unsigned char *na_page[2]; array of addresses: the low and high addresses to be protected unsigned char *prvprt; A place to save the previous protection, should the caller later wish to restore the protection */ { /* STUB */ return; } /* Return memory protection to the state of affairs which existed prior to a call to set_noaccess */ void reset_access(unsigned char *na_page[], unsigned char oldprt) /* unsigned char *na_page[2]; array of addresses: the low and high addresses to be protected unsigned char oldprt; A place to save the previous protection, should the caller later wish to restore the protection */ { /* STUB */ return; } fis-gtm-V7.0-005/sr_unix/memprot.c0000644000032200000250000000246014342376330015672 0ustar librarygtc/**************************************************************** * * * Copyright 2014 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 #include "mdef.h" #include "memprot.h" OS_PAGE_SIZE_DECLARE /* * Ensure base address has at least bytes of accessible space, followed by an inaccessible (PROT_NONE) page. */ void memprot(mstr *base, uint4 size) { int status; if (base->len < size) { if (NULL != base->addr) munmap(base->addr, base->len + OS_PAGE_SIZE); /* clear previous allocation */ base->len = (mstr_len_t)ROUND_UP(size, OS_PAGE_SIZE); base->addr = (char *)mmap(NULL, base->len + OS_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (MAP_FAILED == base->addr) { base->len = 0; base->addr = NULL; } else { status = mprotect(base->addr + base->len, OS_PAGE_SIZE, PROT_NONE); if (-1 == status) { munmap(base->addr, base->len + OS_PAGE_SIZE); base->len = 0; base->addr = NULL; } } } return; } fis-gtm-V7.0-005/sr_unix/memprot.h0000644000032200000250000000134514342376330015700 0ustar librarygtc/**************************************************************** * * * Copyright 2014 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. * * * ****************************************************************/ /* * This function creates protected memory. The created protected memory is always multiple of OS_PAGE_SIZE, so it is advisable to * have input 'size' in the multiple of OS_PAGE_SIZE. The return value is the starting address of the protected memory. */ void memprot(mstr *base, uint4 size); fis-gtm-V7.0-005/sr_unix/mkutf8dir.csh0000755000032200000250000000412314342376330016460 0ustar librarygtc#! tcsh -f ################################################################# # # # Copyright (c) 2007-2018 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. # # # ################################################################# # ####################################################################### # # mkutf8dir.csh - Used by makefile build procedure # Should be kept in sync with similar code in comlist.csh # If ICU is installed, create utf8 subdirectory. # Mirror parent directory except for .o's. # Build .o's in UTF-8 mode. # ####################################################################### set checkutf8 = "../sr_unix/check_utf8_support.csh" if ( -e $checkutf8 ) then set incdir = "" if ( "Linux" == `uname` && "64" == "$gt_build_type" ) then switch (`uname -m`) case "x86_64": set incdir = "../sr_x86_64" breaksw case "s390x": set incdir = "../sr_s390" breaksw default: breaksw endsw endif source $checkutf8 $incdir if ("TRUE" == "$is_utf8_support") then if (! -e utf8) mkdir utf8 if ( "OS/390" == $HOSTOS ) then setenv gtm_chset_locale $utflocale # LC_CTYPE not picked up right endif setenv LC_CTYPE $utflocale unsetenv LC_ALL setenv gtm_chset UTF-8 # switch to "UTF-8" mode foreach file (*) # Skip utf8 directory if (-d $file && "utf8" == $file) then continue endif # Skip soft linking .o files set extension = $file:e if ($extension == "o") then continue endif # Soft link everything else if (-e utf8/$file) then rm -rf utf8/$file endif ln -s ../$file utf8/$file end cd utf8 ../mumps *.m if ($status != 0) then echo "mkutf8dir-E-compile_UTF8, Failed to compile .m programs in UTF-8 mode" endif cd .. setenv LC_CTYPE C unsetenv gtm_chset # switch back to "M" mode endif endif fis-gtm-V7.0-005/sr_unix/mmrhash.c0000644000032200000250000005666314342376330015664 0ustar librarygtc/**************************************************************** * * * Copyright 2011, 2014 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. * * * ****************************************************************/ /*----------------------------------------------------------------------------- * MurmurHash3 was written by Austin Appleby, and is placed in the public * domain. The author hereby disclaims copyright to this source code. * * Note - The x86 and x64 versions do _not_ produce the same results, as the * algorithms are optimized for their respective platforms. You can still * compile and run any of them on any platform, but your performance with the * non-native version will be less than optimal. * * This version converted to C for use in GT.M by FIS. *-----------------------------------------------------------------------------*/ #include "mdef.h" #include "gtm_string.h" #include "mmrhash.h" /* * Since this code is largely third-party, its form is somewhat different than * other code in GT.M. This is intentional, at least for the initial version, * in order to allow for comparison with the original. This may be cleaned up * in future versions, as no revisions to the original public domain code are * expected. The original code is hosted at http://code.google.com/p/smhasher/ . * * Note also that GT.M is currently only using the 32 bit hash function, * MurmurHash3_x86_32(). The 128 bit functions are retained for completeness * and possible future use. * */ #if 0 #define FORCE_INLINE __attribute__((always_inline)) #else #define FORCE_INLINE #endif static uint4 fmix ( uint4 h ); static gtm_uint8 fmix64 ( gtm_uint8 k ); #define ROTL32(X,Y) (((X) << (Y)) | ((X) >> (32 - (Y)))) #define ROTL64(X,Y) (((X) << (Y)) | ((X) >> (64 - (Y)))) #define BIG_CONSTANT(x) (x##LLU) #define GETBLOCK(p,i) (((const uint4 *)p)[(int)i]) #define GETBLOCK64(p,i) (((const gtm_uint8 *)p)[(int)i]) #ifdef BIGENDIAN # if defined(__GNUC__) && (__GNUC__>4 || (__GNUC__==4 && __GNUC_MINOR__>=3)) # define UI64_TO_LE(X) (__builtin_bswap64(*((uint64_t*)&(X)))) # else # define UI64_TO_LE(X) (X) = ((((X) & BIG_CONSTANT(0xff)) << 56) \ | (((X) & BIG_CONSTANT(0xff00)) << 40) \ | (((X) & BIG_CONSTANT(0xff0000)) << 24) \ | (((X) & BIG_CONSTANT(0xff000000)) << 8) \ | (((X) & BIG_CONSTANT(0xff00000000)) >> 8) \ | (((X) & BIG_CONSTANT(0xff0000000000)) >> 24) \ | (((X) & BIG_CONSTANT(0xff000000000000)) >> 40) \ | (((X) & BIG_CONSTANT(0xff00000000000000)) >> 56)) # endif #else #define UI64_TO_LE(X) /* nothing */ #endif /*----------------------------------------------------------------------------- * Finalization mix - force all bits of a hash block to avalanche */ static FORCE_INLINE uint4 fmix ( uint4 h ) { h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; return h; } /*----------*/ static FORCE_INLINE gtm_uint8 fmix64 ( gtm_uint8 k ) { k ^= k >> 33; k *= BIG_CONSTANT(0xff51afd7ed558ccd); k ^= k >> 33; k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53); k ^= k >> 33; return k; } /*-----------------------------------------------------------------------------*/ void MurmurHash3_x86_32 ( const void * key, int len, uint4 seed, void * out ) { int i; const unsigned char * tail; const uint4 * blocks; const int nblocks = len / 4; static char *buff; static int bufflen; uint4 h1 = seed; uint4 c1 = 0xcc9e2d51; uint4 c2 = 0x1b873593; uint4 k1; /*---------- * body */ # ifndef UNALIGNED_ACCESS_SUPPORTED /* Murmur3 hash works only on 4-byte aligned keys so align key to avoid unaligned access error * messages from an architecture that does not support it. */ if (len && (0 != ((UINTPTR_T)key % 4))) { /* make buffer 4-byte aligned */ if (bufflen < len) { if (NULL != buff) { assert(bufflen); free(buff); } bufflen = len * 2; buff = malloc(bufflen); } assert(bufflen >= len); memcpy(buff, key, len); key = (const unsigned char*)buff; } # endif blocks = (const uint4 *)((char *)key + nblocks*4); for(i = -nblocks; i; i++) { k1 = GETBLOCK(blocks,i); k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; h1 = ROTL32(h1,13); h1 = h1*5+0xe6546b64; } /*---------- * tail */ tail = (const unsigned char*)blocks; k1 = 0; /* The shifts below assume little endian, so the handling of the tail block is inconsistent with normal blocks * on big endian systems. However, we are already using this version and we don't want to change the resulting * hashes, so leave it alone. */ switch(len & 3) { case 3: k1 ^= tail[2] << 16; case 2: k1 ^= tail[1] << 8; case 1: k1 ^= tail[0]; k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; }; /*---------- * finalization */ h1 ^= len; h1 = fmix(h1); *(uint4*)out = h1; } /*-----------------------------------------------------------------------------*/ void MurmurHash3_x86_128 ( const void * key, const int len, uint4 seed, void * out ) { int i; const unsigned char * data = (const unsigned char*)key; const unsigned char * tail; const int nblocks = len / 16; static char *buff; static int bufflen; uint4 h1 = seed; uint4 h2 = seed; uint4 h3 = seed; uint4 h4 = seed; uint4 c1 = 0x239b961b; uint4 c2 = 0xab0e9789; uint4 c3 = 0x38b34ae5; uint4 c4 = 0xa1e38b93; uint4 k1; uint4 k2; uint4 k3; uint4 k4; /*---------- * body */ # ifndef UNALIGNED_ACCESS_SUPPORTED /* Murmur3 hash works only on 4-byte aligned keys so align key to avoid unaligned access error * messages from an architecture that does not support it. */ if (len && (0 != ((UINTPTR_T)key % 4))) { /* make buffer 4-byte aligned */ if (bufflen < len) { if (NULL != buff) { assert(bufflen); free(buff); } bufflen = len * 2; buff = malloc(bufflen); } assert(bufflen >= len); memcpy(buff, key, len); data = (const unsigned char*)buff; } # endif const uint4 * blocks = (const uint4 *)(data + nblocks*16); for(i = -nblocks; i; i++) { k1 = GETBLOCK(blocks,i*4+0); k2 = GETBLOCK(blocks,i*4+1); k3 = GETBLOCK(blocks,i*4+2); k4 = GETBLOCK(blocks,i*4+3); k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b; k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747; k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35; k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17; } /*---------- * tail */ tail = (const unsigned char*)(data + nblocks*16); k1 = 0; k2 = 0; k3 = 0; k4 = 0; /* The shifts below assume little endian, so the handling of the tail block is inconsistent with normal blocks * on big endian systems. If we start using this routine, we should decide whether to fix it or not. */ switch(len & 15) { case 15: k4 ^= tail[14] << 16; case 14: k4 ^= tail[13] << 8; case 13: k4 ^= tail[12] << 0; k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; case 12: k3 ^= tail[11] << 24; case 11: k3 ^= tail[10] << 16; case 10: k3 ^= tail[ 9] << 8; case 9: k3 ^= tail[ 8] << 0; k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; case 8: k2 ^= tail[ 7] << 24; case 7: k2 ^= tail[ 6] << 16; case 6: k2 ^= tail[ 5] << 8; case 5: k2 ^= tail[ 4] << 0; k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; case 4: k1 ^= tail[ 3] << 24; case 3: k1 ^= tail[ 2] << 16; case 2: k1 ^= tail[ 1] << 8; case 1: k1 ^= tail[ 0] << 0; k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; }; /*---------- * finalization */ h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; h1 += h2; h1 += h3; h1 += h4; h2 += h1; h3 += h1; h4 += h1; h1 = fmix(h1); h2 = fmix(h2); h3 = fmix(h3); h4 = fmix(h4); h1 += h2; h1 += h3; h1 += h4; h2 += h1; h3 += h1; h4 += h1; ((uint4*)out)[0] = h1; ((uint4*)out)[1] = h2; ((uint4*)out)[2] = h3; ((uint4*)out)[3] = h4; } /*-----------------------------------------------------------------------------*/ void MurmurHash3_x64_128 ( const void * key, const int len, const uint4 seed, void * out ) { int i; const unsigned char * data = (const unsigned char*)key; const unsigned char * tail; const int nblocks = len / 16; static char *buff; static int bufflen; gtm_uint8 h1 = seed; gtm_uint8 h2 = seed; gtm_uint8 c1 = BIG_CONSTANT(0x87c37b91114253d5); gtm_uint8 c2 = BIG_CONSTANT(0x4cf5ad432745937f); gtm_uint8 k1; gtm_uint8 k2; /*---------- * body */ # ifndef UNALIGNED_ACCESS_SUPPORTED /* Murmur3 hash works only on 4-byte aligned keys so align key to avoid unaligned access error * messages from an architecture that does not support it. */ if (len && (0 != ((UINTPTR_T)key % 8))) { /* make buffer 4-byte aligned */ if (bufflen < len) { if (NULL != buff) { assert(bufflen); free(buff); } bufflen = len * 2; buff = malloc(bufflen); } assert(bufflen >= len); memcpy(buff, key, len); data = (const unsigned char*)buff; } # endif const gtm_uint8 * blocks = (const gtm_uint8 *)(data); for(i = 0; i < nblocks; i++) { k1 = GETBLOCK64(blocks,i*2+0); k2 = GETBLOCK64(blocks,i*2+1); UI64_TO_LE(k1); UI64_TO_LE(k2); k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729; k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5; } /*---------- * tail */ tail = (const unsigned char*)(data + nblocks*16); k1 = 0; k2 = 0; /* The shifts below originally assumed little endian, so the handling of the tail block was inconsistent with normal blocks * on big endian systems. Since we haven't used this routine previously, fix it here instead of making the progressive * version consistent with the broken implementation. */ switch(len & 15) { case 15: k2 ^= ((gtm_uint8)tail[14]) << 48; case 14: k2 ^= ((gtm_uint8)tail[13]) << 40; case 13: k2 ^= ((gtm_uint8)tail[12]) << 32; case 12: k2 ^= ((gtm_uint8)tail[11]) << 24; case 11: k2 ^= ((gtm_uint8)tail[10]) << 16; case 10: k2 ^= ((gtm_uint8)tail[ 9]) << 8; case 9: k2 ^= ((gtm_uint8)tail[ 8]) << 0; k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; case 8: k1 ^= ((gtm_uint8)tail[ 7]) << 56; case 7: k1 ^= ((gtm_uint8)tail[ 6]) << 48; case 6: k1 ^= ((gtm_uint8)tail[ 5]) << 40; case 5: k1 ^= ((gtm_uint8)tail[ 4]) << 32; case 4: k1 ^= ((gtm_uint8)tail[ 3]) << 24; case 3: k1 ^= ((gtm_uint8)tail[ 2]) << 16; case 2: k1 ^= ((gtm_uint8)tail[ 1]) << 8; case 1: k1 ^= ((gtm_uint8)tail[ 0]) << 0; k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; }; /*---------- * finalization */ h1 ^= len; h2 ^= len; h1 += h2; h2 += h1; h1 = fmix64(h1); h2 = fmix64(h2); h1 += h2; h2 += h1; ((gtm_uint8*)out)[0] = h1; ((gtm_uint8*)out)[1] = h2; } /******************************************************************************* * Progressive 128-bit MurmurHash3 *******************************************************************************/ #define C1 BIG_CONSTANT(0x87c37b91114253d5) #define C2 BIG_CONSTANT(0x4cf5ad432745937f) #define M1 0x52dce729 #define M2 0x38495ab5 #define LSHIFT_BYTES_SAFE(LHS, RHS) ((LHS) << ((RHS) * 8)) #define RSHIFT_BYTES_SAFE(LHS, RHS) ((LHS) >> ((RHS) * 8)) #define LSHIFT_BYTES(LHS, RHS) ((RHS < SIZEOF(LHS)) ? LSHIFT_BYTES_SAFE(LHS, RHS) : 0) #define RSHIFT_BYTES(LHS, RHS) ((RHS < SIZEOF(LHS)) ? RSHIFT_BYTES_SAFE(LHS, RHS) : 0) #if !defined(BIGENDIAN) /* Little Endian */ #define ADD_BYTES(STATEPTR, KEY, COUNT) \ MBSTART { \ int ab_count, ab_offset, ab_carry_bytes; \ const unsigned char *ab_key, *ab_ptr; \ gtm_uint8 c1, c2; \ \ assert((STATEPTR)->carry_bytes + (COUNT) >= 0); \ assert((STATEPTR)->carry_bytes + (COUNT) <= 16); \ ab_count = (COUNT); \ ab_carry_bytes = (STATEPTR)->carry_bytes; \ ab_offset = ab_carry_bytes + ab_count; \ ab_key = (KEY); \ ab_ptr = ab_key + ab_count - 1; \ c1 = (STATEPTR)->c.one; \ c2 = (STATEPTR)->c.two; \ assert((ab_carry_bytes != 0) || ((c1 == 0) && (c2 == 0))); \ for (; (ab_offset > 8) && (ab_ptr >= ab_key); ab_ptr--, ab_offset--) \ c2 |= LSHIFT_BYTES_SAFE((gtm_uint8)*ab_ptr, (ab_offset - 9)); \ for (; ab_ptr >= ab_key; ab_ptr--, ab_offset--) \ c1 |= LSHIFT_BYTES_SAFE((gtm_uint8)*ab_ptr, (ab_offset - 1)); \ assert(((ab_carry_bytes + ab_count) != 0) || ((c1 == 0) && (c2 == 0))); \ assert((ab_count != 0) || (((STATEPTR)->c.one == c1) && ((STATEPTR)->c.two == c2))); \ (STATEPTR)->c.one = c1; \ (STATEPTR)->c.two = c2; \ (STATEPTR)->carry_bytes = ab_carry_bytes + ab_count; \ } MBEND #define GET_BLOCK(STATEPTR, KEY, K1, K2) \ MBSTART { \ int gb_carry_bytes; \ gtm_uint8 gb_m1, gb_m2, gb_k1, gb_k2, gb_c1, gb_c2; \ \ assert(((STATEPTR)->carry_bytes != 0) || (((STATEPTR)->c.one == 0) && ((STATEPTR)->c.two == 0))); \ assert((STATEPTR)->carry_bytes < 16); \ gb_carry_bytes = (STATEPTR)->carry_bytes; \ gb_m1 = ((gtm_uint8 *)(KEY))[0]; \ gb_m2 = ((gtm_uint8 *)(KEY))[1]; \ if (0 == gb_carry_bytes) \ { \ (K1) = gb_m1; \ (K2) = gb_m2; \ } \ else if (8 == gb_carry_bytes) \ { \ gb_k1 = (STATEPTR)->c.one; \ gb_k2 = gb_m1; \ gb_c1 = gb_m2; \ /* gb_c2 = 0; */ \ (K1) = gb_k1; \ (K2) = gb_k2; \ (STATEPTR)->c.one = gb_c1; \ assert(0 == (STATEPTR)->c.two); \ } \ else if (gb_carry_bytes < 8) \ { \ gb_k1 = (STATEPTR)->c.one | LSHIFT_BYTES_SAFE(gb_m1, gb_carry_bytes); \ gb_k2 = RSHIFT_BYTES_SAFE(gb_m1, (8 - gb_carry_bytes)) \ | LSHIFT_BYTES_SAFE(gb_m2, gb_carry_bytes); \ gb_c1 = RSHIFT_BYTES_SAFE(gb_m2, (8 - gb_carry_bytes)); \ /* gb_c2 = 0; */ \ (K1) = gb_k1; \ (K2) = gb_k2; \ (STATEPTR)->c.one = gb_c1; \ assert(0 == (STATEPTR)->c.two); \ } \ else { \ gb_k1 = (STATEPTR)->c.one; \ gb_k2 = (STATEPTR)->c.two \ | LSHIFT_BYTES_SAFE(gb_m1, (gb_carry_bytes - 8)); \ gb_c1 = RSHIFT_BYTES_SAFE(gb_m1, (16 - gb_carry_bytes)) \ | LSHIFT_BYTES_SAFE(gb_m2, (gb_carry_bytes - 8)); \ gb_c2 = RSHIFT_BYTES_SAFE(gb_m2, (16 - gb_carry_bytes)); \ (K1) = gb_k1; \ (K2) = gb_k2; \ (STATEPTR)->c.one = gb_c1; \ (STATEPTR)->c.two = gb_c2; \ } \ } MBEND #else /* Big Endian */ #define ADD_BYTES(STATEPTR, KEY, COUNT) \ MBSTART { \ int ab_count, ab_offset, ab_carry_bytes; \ const unsigned char *ab_key, *ab_ptr; \ gtm_uint8 c1, c2; \ \ assert((STATEPTR)->carry_bytes + (COUNT) >= 0); \ assert((STATEPTR)->carry_bytes + (COUNT) <= 16); \ ab_count = (COUNT); \ ab_carry_bytes = (STATEPTR)->carry_bytes; \ ab_offset = ab_carry_bytes + ab_count; \ ab_key = (KEY); \ ab_ptr = ab_key + ab_count - 1; \ c1 = (STATEPTR)->c.one; \ c2 = (STATEPTR)->c.two; \ assert((ab_carry_bytes != 0) || ((c1 == 0) && (c2 == 0))); \ for (; (ab_offset > 8) && (ab_ptr >= ab_key); ab_ptr--, ab_offset--) \ c2 |= LSHIFT_BYTES_SAFE((gtm_uint8)*ab_ptr, (16 - ab_offset)); \ for (; ab_ptr >= ab_key; ab_ptr--, ab_offset--) \ c1 |= LSHIFT_BYTES_SAFE((gtm_uint8)*ab_ptr, (8 - ab_offset)); \ assert(((ab_carry_bytes + ab_count) != 0) || ((c1 == 0) && (c2 == 0))); \ assert((ab_count != 0) || (((STATEPTR)->c.one == c1) && ((STATEPTR)->c.two == c2))); \ (STATEPTR)->c.one = c1; \ (STATEPTR)->c.two = c2; \ (STATEPTR)->carry_bytes = ab_carry_bytes + ab_count; \ } MBEND #define GET_BLOCK(STATEPTR, KEY, K1, K2) \ MBSTART { \ int gb_carry_bytes; \ gtm_uint8 gb_m1, gb_m2, gb_k1, gb_k2, gb_c1, gb_c2; \ \ assert(((STATEPTR)->carry_bytes != 0) || (((STATEPTR)->c.one == 0) && ((STATEPTR)->c.two == 0))); \ gb_carry_bytes = (STATEPTR)->carry_bytes; \ gb_m1 = ((gtm_uint8 *)(KEY))[0]; \ gb_m2 = ((gtm_uint8 *)(KEY))[1]; \ if (0 == gb_carry_bytes) \ { \ (K1) = gb_m1; \ (K2) = gb_m2; \ } \ else if (8 == gb_carry_bytes) \ { \ gb_k1 = (STATEPTR)->c.one; \ gb_k2 = gb_m1; \ gb_c1 = gb_m2; \ /* gb_c2 = 0; */ \ (K1) = gb_k1; \ (K2) = gb_k2; \ (STATEPTR)->c.one = gb_c1; \ assert(0 == (STATEPTR)->c.two); \ } \ else if ( gb_carry_bytes < 8) \ { \ gb_k1 = (STATEPTR)->c.one | RSHIFT_BYTES_SAFE(gb_m1, gb_carry_bytes); \ gb_k2 = LSHIFT_BYTES_SAFE(gb_m1, (8 - gb_carry_bytes)) \ | RSHIFT_BYTES_SAFE(gb_m2, gb_carry_bytes); \ gb_c1 = LSHIFT_BYTES_SAFE(gb_m2, (8 - gb_carry_bytes)); \ /* gb_c2 = 0; */ \ (K1) = gb_k1; \ (K2) = gb_k2; \ (STATEPTR)->c.one = gb_c1; \ assert(0 == (STATEPTR)->c.two); \ } \ else { \ gb_k1 = (STATEPTR)->c.one; \ gb_k2 = (STATEPTR)->c.two \ | RSHIFT_BYTES_SAFE(gb_m1, (gb_carry_bytes - 8)); \ gb_c1 = LSHIFT_BYTES_SAFE(gb_m1, (16 - gb_carry_bytes)) \ | RSHIFT_BYTES_SAFE(gb_m2, (gb_carry_bytes - 8)); \ gb_c2 = LSHIFT_BYTES_SAFE(gb_m2, (16 - gb_carry_bytes)); \ (K1) = gb_k1; \ (K2) = gb_k2; \ (STATEPTR)->c.one = gb_c1; \ (STATEPTR)->c.two = gb_c2; \ } \ } MBEND #endif #define INTEGRATE_K1(K1, H1) \ MBSTART { \ (K1) *= C1; \ (K1) = ROTL64((K1), 31); \ (K1) *= C2; \ (H1) ^= (K1); \ } MBEND #define INTEGRATE_K2(K2, H2) \ MBSTART { \ (K2) *= C2; \ (K2) = ROTL64((K2), 33); \ (K2) *= C1; \ (H2) ^= (K2); \ } MBEND #define PROCESS_BLOCK(K1, K2, H1, H2) \ MBSTART { \ UI64_TO_LE(K1); \ UI64_TO_LE(K2); \ INTEGRATE_K1(K1, H1); \ (H1) = ROTL64((H1), 27); \ (H1) += (H2); \ (H1) = (H1) * 5 + M1; \ INTEGRATE_K2(K2, H2); \ (H2) = ROTL64((H2), 31); \ (H2) += (H1); \ (H2) = (H2) * 5 + M2; \ } MBEND #define PROCESS_BYTES(STATEPTR, KEY, LEN) \ MBSTART { \ int pb_carry_fill = 0; \ \ assert((LEN) < 16); \ if ((STATEPTR)->carry_bytes + (LEN) >= 16) \ { \ /* fill the carry field, process it as a block, then clear it */ \ pb_carry_fill = 16 - (STATEPTR)->carry_bytes; \ ADD_BYTES((STATEPTR), (KEY), pb_carry_fill); \ assert((STATEPTR)->carry_bytes == 16); \ PROCESS_BLOCK((STATEPTR)->c.one, (STATEPTR)->c.two, (STATEPTR)->h.one, (STATEPTR)->h.two); \ (STATEPTR)->c.one = (STATEPTR)->c.two = 0; \ (STATEPTR)->carry_bytes = 0; \ } \ ADD_BYTES((STATEPTR), (KEY) + pb_carry_fill, (LEN) - pb_carry_fill); \ } MBEND #define UNALIGNED_SAFE (defined(__i386) || defined(__x86_64__) || defined(_AIX)) /* This is an endian-independent 16-byte murmur hash function */ void gtmmrhash_128(const void *key, int len, uint4 seed, gtm_uint16 *out) { hash128_state_t state; HASH128_STATE_INIT(state, seed); assert((state.carry_bytes == 0) && (state.c.one == 0) && (state.c.two == 0)); gtmmrhash_128_ingest(&state, key, len); gtmmrhash_128_result(&state, len, out); } /* This is the same as gtmmrhash_128 (i.e. is endian independent) except that it generates a 4-byte hash * (needed e.g. by STR_HASH macro). To avoid the overhead of an extra function call, we duplicate the * code of "gtmmmrhash_128" here. */ void gtmmrhash_32(const void *key, int len, uint4 seed, uint4 *out4) { hash128_state_t state; gtm_uint16 out16; HASH128_STATE_INIT(state, seed); assert((state.carry_bytes == 0) && (state.c.one == 0) && (state.c.two == 0)); gtmmrhash_128_ingest(&state, key, len); gtmmrhash_128_result(&state, len, &out16); *out4 = (uint4)out16.one; } void gtmmrhash_128_ingest(hash128_state_t *state, const void *key, int len) { int i; gtm_uint8 k1, k2; const unsigned char *keyptr; if (0 == len) return; keyptr = key; # if !UNALIGNED_SAFE /* determine the number of bytes to consume to reach 64-bit (8 byte) alignment */ i = (0x8 - ((UINTPTR_T)keyptr & 0x7)) & 0x7; if (i > len) i = len; if (i > 0) { PROCESS_BYTES(state, keyptr, i); keyptr += i; len -= i; } # endif for (; len >= 16; len -= 16, keyptr += 16) { GET_BLOCK(state, keyptr, k1, k2); PROCESS_BLOCK(k1, k2, state->h.one, state->h.two); } if (len > 0) PROCESS_BYTES(state, keyptr, len); } void gtmmrhash_128_result(hash128_state_t *state, uint4 total_len, gtm_uint16 *out) { gtm_uint8 k1, k2, h1, h2; k1 = state->c.one; k2 = state->c.two; h1 = state->h.one; h2 = state->h.two; UI64_TO_LE(k1); UI64_TO_LE(k2); if (state->carry_bytes != 0) { if (state->carry_bytes > 8) { INTEGRATE_K2(k2, h2); } INTEGRATE_K1(k1, h1); } h1 ^= total_len; h2 ^= total_len; h1 += h2; h2 += h1; h1 = fmix64(h1); h2 = fmix64(h2); h1 += h2; h2 += h1; out->one = h1; out->two = h2; } void gtmmrhash_128_bytes(const gtm_uint16 *hash, unsigned char *out) { # ifdef BIGENDIAN # define EXTRACT_BYTE(X, N) (((uint64_t)(X) & ((uint64_t)0xff << (N) * 8)) >> (N) * 8) out[0] = EXTRACT_BYTE(hash->one, 0); out[1] = EXTRACT_BYTE(hash->one, 1); out[2] = EXTRACT_BYTE(hash->one, 2); out[3] = EXTRACT_BYTE(hash->one, 3); out[4] = EXTRACT_BYTE(hash->one, 4); out[5] = EXTRACT_BYTE(hash->one, 5); out[6] = EXTRACT_BYTE(hash->one, 6); out[7] = EXTRACT_BYTE(hash->one, 7); out[8] = EXTRACT_BYTE(hash->two, 0); out[9] = EXTRACT_BYTE(hash->two, 1); out[10] = EXTRACT_BYTE(hash->two, 2); out[11] = EXTRACT_BYTE(hash->two, 3); out[12] = EXTRACT_BYTE(hash->two, 4); out[13] = EXTRACT_BYTE(hash->two, 5); out[14] = EXTRACT_BYTE(hash->two, 6); out[15] = EXTRACT_BYTE(hash->two, 7); # else ((gtm_uint8 *)out)[0] = hash->one; ((gtm_uint8 *)out)[1] = hash->two; # endif } void gtmmrhash_128_hex(const gtm_uint16 *hash, unsigned char *out) { int i; unsigned char bytes[16], n; gtmmrhash_128_bytes(hash, bytes); for (i = 0; i < 16; i++) { n = bytes[i] & 0xf; out[i * 2 + 1] = (n < 10) ? (n + '0') : (n - 10 + 'a'); n = (bytes[i] >> 4) & 0xf; out[i * 2] = (n < 10) ? (n + '0') : (n - 10 + 'a'); } } /*******************************************************************************/ /*-----------------------------------------------------------------------------*/ fis-gtm-V7.0-005/sr_unix/mmrhash.h0000644000032200000250000000440414342376330015653 0ustar librarygtc/**************************************************************** * * * Copyright 2011, 2014 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. * * * ****************************************************************/ #ifndef MURMURHASH_H #define MURMURHASH_H 1 /*----------------------------------------------------------------------------- * MurmurHash3 was written by Austin Appleby, and is placed in the public * domain. The author hereby disclaims copyright to this source code. * * This version converted to C for use in GT.M by FIS. *-----------------------------------------------------------------------------*/ /* Note that all these want the key to be a multiple of N bits, where N is the last number in the function name. */ void MurmurHash3_x86_32 ( const void * key, int len, uint4 seed, void * out ); void MurmurHash3_x86_128 ( const void * key, int len, uint4 seed, void * out ); void MurmurHash3_x64_128 ( const void * key, int len, uint4 seed, void * out ); typedef struct { gtm_uint8 one, two; } gtm_uint16; typedef struct { gtm_uint16 h, c; int carry_bytes; } hash128_state_t; #define HASH128_STATE_INIT(STATE, SEED) \ MBSTART { \ (STATE).h.one = (STATE).h.two = (SEED); \ (STATE).c.one = (STATE).c.two = 0; \ (STATE).carry_bytes = 0; \ } MBEND void gtmmrhash_128(const void *key, int len, uint4 seed, gtm_uint16 *out); void gtmmrhash_32(const void *key, int len, uint4 seed, uint4 *out4); void gtmmrhash_128_ingest(hash128_state_t *state, const void *key, int len); void gtmmrhash_128_result(hash128_state_t *state, uint4 total_len, gtm_uint16 *out); void gtmmrhash_128_hex(const gtm_uint16 *hash, unsigned char *out); void gtmmrhash_128_bytes(const gtm_uint16 *hash, unsigned char *out); #endif /*-----------------------------------------------------------------------------*/ fis-gtm-V7.0-005/sr_unix/msg.h0000755000032200000250000000072514342376330015007 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, Inc. * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #define DEF_MSG_ARGS 8 fis-gtm-V7.0-005/sr_unix/mu_all_version_standalone.c0000644000032200000250000002033414342376330021435 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2022 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 #include #include "errno.h" #include "gtm_ipc.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_stat.h" #include "gtm_fcntl.h" #include "error.h" #include "send_msg.h" #include "gtmio.h" #include "iosp.h" #include "gdsroot.h" #include "v15_gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "v15_gdsbt.h" #include "gdsfhead.h" #include "v15_gdsfhead.h" #include "gdsblk.h" #include "gtm_c_stack_trace.h" #include "eintr_wrappers.h" #include "eintr_wrapper_semop.h" #include "mu_all_version_standalone.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif static int ftok_ids[FTOK_ID_CNT] = {1, 43, 43}; static int ftok_ver[FTOK_ID_CNT] = {0, 0, 1}; error_def(ERR_MUSTANDALONE); error_def(ERR_DBOPNERR); error_def(ERR_FTOKKEY); error_def(ERR_SEMID); error_def(ERR_SYSCALL); error_def(ERR_TEXT); /* Aquire semaphores that on on all V4.x releases are the access control semaphores. In pre V4.2 releases they were based on an FTOK of the database name with an ID of '1'. In V4.2 and later, they are based on the FTOK of the database name with an ID of '43'. Since we do not know which flavor of database we are dealing with, we must create and acquire both flavors of semaphore and hold them for the duration of the phase 2 run. But just holding these semaphore is not sufficient to guarrantee standalone access. We also must attempt to attach to the shared memory for the segment. If it is found, standalone access is not achieved. Early V4 versions (prior to V4.2) created the shared memory with the same FTOK id as the semaphore. Later versions would have had the key of the created private section in the file-header. Use both approaches and fail our attempt if either succeeds. */ void mu_all_version_get_standalone(char_ptr_t db_fn, sem_info *sem_inf) { int i, rc, save_errno, fd; struct sembuf sop[4]; int shmid; ZOS_ONLY(int realfiletag;) v15_sgmnt_data v15_csd; /* Both semaphores must have value 0 and then all will be incremented to completely lockout any other potential users until we are done */ sop[0].sem_num = 0; sop[0].sem_op = 0; /* First check all semaphore have 0 value */ sop[1].sem_num = 1; sop[1].sem_op = 0; sop[2].sem_num = 0; sop[2].sem_op = 1; /* Increment all semaphores */ sop[3].sem_num = 1; sop[3].sem_op = 1; sop[0].sem_flg = sop[1].sem_flg = sop[2].sem_flg = sop[3].sem_flg = SEM_UNDO | IPC_NOWAIT; memset(sem_inf, 0, (SIZEOF(sem_inf) * FTOK_ID_CNT)); /* Zero all fields so we know what to clean up */ for (i = 0; FTOK_ID_CNT > i; ++i) { /* Once through for both ftok key'd semaphores, and both FTOKs for the new semaphore */ if (ftok_ver[i] == 0) sem_inf[i].ftok_key = FTOK_OLD(db_fn, ftok_ids[i]); else sem_inf[i].ftok_key = FTOK(db_fn, ftok_ids[i]); if (-1 == sem_inf[i].ftok_key) { save_errno = errno; mu_all_version_release_standalone(sem_inf); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("ftok()"), CALLFROM, save_errno); } sem_inf[i].sem_id = semget(sem_inf[i].ftok_key, 3, RWDALL | IPC_CREAT | IPC_EXCL); if (-1 == sem_inf[i].sem_id) { save_errno = errno; mu_all_version_release_standalone(sem_inf); /* Different platforms seem to make different checks in different order here so if the semaphore exists but is locked, on some platforms we get EAGAIN, if the semaphore exists but has fewer semaphores in the set that we are requesting we get EINVAL, but if all that is ok and the semaphore just already exists, we get EEXIST which is all we wanted to check anyway.. */ if (EEXIST == save_errno || EAGAIN == save_errno || EINVAL == save_errno) /* Semaphore already exists and/or is locked-- likely rundown needed */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) MAKE_MSG_TYPE(ERR_MUSTANDALONE, ERROR), 2, RTS_ERROR_TEXT(db_fn), save_errno, 0, ERR_FTOKKEY, 1, sem_inf[i].ftok_key); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(12) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("semget()"), CALLFROM, save_errno, 0, ERR_FTOKKEY, 1, sem_inf[i].ftok_key); } SEMOP(sem_inf[i].sem_id, sop, 4, rc, NO_WAIT); if (-1 == rc) { save_errno = errno; mu_all_version_release_standalone(sem_inf); if (EAGAIN == save_errno) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(12) MAKE_MSG_TYPE(ERR_MUSTANDALONE, ERROR), 2, RTS_ERROR_TEXT(db_fn), save_errno, 0, ERR_FTOKKEY, 1, sem_inf[i].ftok_key, ERR_SEMID, 1, sem_inf[i].sem_id); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(15) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("semop()"), CALLFROM, save_errno, 0, ERR_FTOKKEY, 1, sem_inf[i].ftok_key, ERR_SEMID, 1, sem_inf[i].sem_id); } } /* First try to access shared memory based on the database FTOK id. Only need to do the ftok returned by the first FTOK done above as it was the only method where the shared memory and semaphore had the same key. Detailed description: The first semaphore (for project id 1) is for early V4 versions. In these versions, the database had the same shmid as the semaphore did. Given that we can try to attach to it and if it is found, we assume we do not have a standalone lock. The other option is this is a later database so we open it up and pull the shmid field out of the file-header (it was in the same place in all versions that had the shmid). Whatever that field is (regardless of version), if we are able to attach to shared memory, then we consider standalone a failure. This is not a 100% valid check but it is good enough for the few times this will actually be run (upgrade and downgrade). */ shmid = gtm_shmget(sem_inf[0].ftok_key, 0, RWDALL, FALSE); if (-1 == shmid) { /* That failed, second check is if shmid stored in file-header (if any) exists */ fd = OPEN(db_fn, O_RDONLY); /* udi not available so OPENFILE_DB not used */ if (FD_INVALID == fd) { save_errno = errno; mu_all_version_release_standalone(sem_inf); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_DBOPNERR, 2, RTS_ERROR_TEXT(db_fn), save_errno); } # ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(fd, TAG_BINARY, &realfiletag)) TAG_POLICY_GTM_PUTMSG(db_fn, errno, realfiletag, TAG_BINARY); # endif LSEEKREAD(fd, 0, &v15_csd, SIZEOF(v15_csd), rc); if (0 != rc) { mu_all_version_release_standalone(sem_inf); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("LSEEKREAD()"), CALLFROM, rc); } CLOSEFILE_RESET(fd, rc); /* resets "fd" to FD_INVALID */ if (0 != v15_csd.shmid && INVALID_SHMID != v15_csd.shmid) shmid = gtm_shmget(v15_csd.shmid, 0, RWDALL, FALSE); } if (-1 != shmid) { mu_all_version_release_standalone(sem_inf); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) MAKE_MSG_TYPE(ERR_MUSTANDALONE, ERROR), 2, RTS_ERROR_TEXT(db_fn), save_errno, 0, ERR_FTOKKEY, 1, sem_inf[i].ftok_key); } } /* The input array (filled out by mu_all_version_get_standalone()) should tell us which resources we specifically allocated so we can then free those resources (and no other). */ void mu_all_version_release_standalone(sem_info *sem_inf) { int i, rc, save_errno; /* Note that we ignore most errors in this routine as we may get called with the alleged semaphores in * just about any state. */ for (i = 0; FTOK_ID_CNT > i; ++i) { /* release/delete any held semaphores in this set */ if (sem_inf[i].sem_id && -1 != sem_inf[i].sem_id) { rc = semctl(sem_inf[i].sem_id, 0, IPC_RMID); if (-1 == rc) { save_errno = errno; if (!SEM_REMOVED(save_errno)) { /* Don't care if semaphore already removed (probably by a concurrent mupip rundown) */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("semctl(remid)"), CALLFROM, save_errno); } } sem_inf[i].sem_id = 0; /* Clear so we don't repeat if redriven */ } } } fis-gtm-V7.0-005/sr_unix/mu_all_version_standalone.h0000755000032200000250000000173414342376330021450 0ustar librarygtc/**************************************************************** * * * Copyright 2005, 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. * * * ****************************************************************/ #ifndef MU_ALL_VERSION_STANDALONE_H_INCLUDED #define MU_ALL_VERSION_STANDALONE_H_INCLUDED /* When the sem_info structure below is allocated, it must be allocated in an array of at least the dimension of FTOK_ID_CNT. */ #define FTOK_ID_CNT 3 /* Structure that holds information for semaphores we create for standalone processing */ typedef struct { int ftok_key; int sem_id; } sem_info; void mu_all_version_get_standalone(char_ptr_t db_fn, sem_info *sem_inf); void mu_all_version_release_standalone(sem_info *sem_id); #endif fis-gtm-V7.0-005/sr_unix/mu_cre_file.c0000644000032200000250000005036214342376330016464 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_fcntl.h" #include "gtm_unistd.h" #include "gtm_string.h" #include #include "gtm_stat.h" #include "gtm_stdio.h" #include "gtm_statvfs.h" #if defined(__MVS__) #include "gtm_zos_io.h" #endif #include "parse_file.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "filestruct.h" #include "mlkdef.h" #include "gtmio.h" #include "send_msg.h" #include "is_raw_dev.h" #include "disk_block_available.h" #include "mucregini.h" #include "mu_cre_file.h" #include "gtmmsg.h" #include "util.h" #include "gtmdbglvl.h" #include "anticipatory_freeze.h" #include "gtmcrypt.h" #include "shmpool.h" /* Needed for the shmpool structures */ #include "jnl.h" #include "db_write_eof_block.h" #include "get_fs_block_size.h" #include "gtm_permissions.h" #include "getzposition.h" #include "error.h" #include "db_header_conversion.h" #define BLK_SIZE (((gd_segment*)gv_cur_region->dyn.addr)->blk_size) /* Note: CLEANUP macro is invoked by "mu_cre_file_ch" and "mu_cre_file" and hence needs to use "static" variables * (mu_cre_file_fd & mu_cre_file_path) instead of "local" variables. */ #define CLEANUP(XX) \ MBSTART { \ int rc; \ \ if (cs_data) \ free(cs_data); \ if (FD_INVALID != mu_cre_file_fd) \ CLOSEFILE_RESET(mu_cre_file_fd, rc); /* resets "mu_cre_file_fd" to FD_INVALID */ \ assert(NULL != mu_cre_file_path); \ if (EXIT_ERR == XX) \ { \ rc = UNLINK(mu_cre_file_path); \ assert(0 == rc); \ } \ } MBEND /* Macros to send warning or error messages to the correct destination: * - If MUPIP image, message goes to stderr of the process. * - Else MUMPS image captures the error message and wraps it with MUCREFILERR and sends it to the system log. * In addition, some messages require cleanup if they are emitted past a certain point in the processing (said point * setting the 'cleanup_needed' flag to TRUE. */ #define PUTMSG_MSG_ROUTER_CSA(CSAARG, VARCNT, ERRORID, ...) \ MBSTART { \ mval zpos; \ \ if (IS_MUPIP_IMAGE) \ gtm_putmsg_csa(CSA_ARG(CSAARG) VARLSTCNT(VARCNT) ERRORID, __VA_ARGS__); \ else \ { \ /* Need to reflect the current error to the syslog - find entry ref to add to error. The VARLSTCNT \ * computation is 8 for the prefix message, plus the VARLSTCNT() that would apply to the actual error \ * message that got us here. \ */ \ getzposition(&zpos); \ send_msg_csa(CSA_ARG(CSAARG) VARLSTCNT((8 + VARCNT)) ERR_MUCREFILERR, 6, zpos.str.len, zpos.str.addr, \ DB_LEN_STR(gv_cur_region), REG_LEN_STR(gv_cur_region), ERRORID, __VA_ARGS__); \ } \ } MBEND #define PUTMSG_ERROR_CSA(CSAARG, VARCNT, ERRORID, ...) \ MBSTART { \ if (cleanup_needed) \ CLEANUP(EXIT_ERR); \ PUTMSG_MSG_ROUTER_CSA(CSAARG, VARCNT, ERRORID, __VA_ARGS__); \ } MBEND #define PUTMSG_WARN_CSA(CSAARG, VARCNT, ERRORID, ...) PUTMSG_MSG_ROUTER_CSA(CSAARG, VARCNT, ERRORID, __VA_ARGS__) /* zOS is a currently unsupported platform so this macro remains unconverted but with an assertpro(FALSE) should * the zOS port ever be resurrected. In that case, uses of this macro need to be converted to PUTMSG_ERROR_CSA * invocations. */ #define SNPRINTF_AND_PERROR_MVS(MESSAGE) \ MBSTART { \ assertpro(FALSE); \ save_errno = errno; \ SNPRINTF(errbuff, OUT_LINE, MESSAGE, path, realfiletag, TAG_BINARY); \ errno = save_errno; \ PERROR(errbuff); \ } MBEND #define OUT_LINE 512 + 1 GBLREF gd_region *gv_cur_region; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF uint4 gtmDebugLevel; GBLREF enum db_ver gtm_db_create_ver; /* database creation version */ #ifdef DEBUG GBLREF boolean_t in_mu_cre_file; #endif STATICDEF int mu_cre_file_fd; /* needed for "mu_cre_file_ch" */ STATICDEF char *mu_cre_file_path; /* needed for "mu_cre_file_ch" */ error_def(ERR_DBBLKSIZEALIGN); error_def(ERR_DBFILECREATED); error_def(ERR_DBOPNERR); error_def(ERR_DSKSPCCHK); error_def(ERR_FILECREERR); error_def(ERR_FNTRANSERROR); error_def(ERR_LOWSPACECRE); error_def(ERR_MUCREFILERR); error_def(ERR_MUNOSTRMBKUP); error_def(ERR_NOCREMMBIJ); error_def(ERR_NOCRENETFILE); error_def(ERR_NOSPACECRE); error_def(ERR_PARNORMAL); error_def(ERR_PREALLOCATEFAIL); error_def(ERR_RAWDEVUNSUP); error_def(ERR_DBFILNOFULLWRT); /* Condition handler primarily to handle ERR_MEMORY errors by cleaning up the file that we created * before passing on control to higher level condition handlers. */ CONDITION_HANDLER(mu_cre_file_ch) { START_CH(TRUE); if ((SUCCESS == SEVERITY) || (INFO == SEVERITY)) { assert(FALSE); /* don't know of any possible INFO/SUCCESS errors */ CONTINUE; /* Keep going for non-error issues */ } CLEANUP(EXIT_ERR); NEXTCH; } unsigned char mu_cre_file(void) { char path[MAX_FN_LEN + 1], errbuff[OUT_LINE]; unsigned char buff[DISK_BLOCK_SIZE]; int i, lower, upper, norm_vbn; ssize_t status; uint4 raw_dev_size; /* size of a raw device, in bytes */ int4 save_errno; gtm_uint64_t avail_blocks, blocks_for_create, blocks_for_extension, delta_blocks; file_control fc; mstr file; parse_blk pblk; unix_db_info udi_struct, *udi; char *fgets_res; gd_segment *seg; gd_region *baseDBreg; char hash[GTMCRYPT_HASH_LEN]; int retcode, perms, user_id, group_id; uint4 gtmcrypt_errno; off_t new_eof; uint4 fsb_size; boolean_t cleanup_needed; sgmnt_addrs *baseDBcsa; struct stat stat_buf; struct perm_diag_data pdd; uint4 fbwsize; int4 dblksize; uint4 file_hdr_size; block_id start_vbn_target; ZOS_ONLY(int realfiletag;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DEBUG_ONLY(mu_cre_file_fd = FD_INVALID); DEBUG_ONLY(mu_cre_file_path = NULL); if (GDSVCURR == gtm_db_create_ver) { /* Current version defaults */ file_hdr_size = SIZEOF_FILE_HDR_DFLT; start_vbn_target = START_VBN_CURRENT; } else { /* Prior version settings */ file_hdr_size = SIZEOF_FILE_HDR_V6; start_vbn_target = START_VBN_V6; } cleanup_needed = FALSE; DEBUG_ONLY(in_mu_cre_file = TRUE;) assert((-(SIZEOF(uint4) * 2) & SIZEOF_FILE_HDR_DFLT) == SIZEOF_FILE_HDR_DFLT); cs_addrs = &udi_struct.s_addrs; cs_data = (sgmnt_data_ptr_t)NULL; /* for CLEANUP */ memset(&pblk, 0, SIZEOF(pblk)); pblk.fop = (F_SYNTAXO | F_PARNODE); pblk.buffer = path; pblk.buff_size = MAX_FN_LEN; file.addr = (char*)gv_cur_region->dyn.addr->fname; file.len = gv_cur_region->dyn.addr->fname_len; strncpy(path, file.addr, file.len); *(path + file.len) = '\0'; if (is_raw_dev(path)) { PUTMSG_ERROR_CSA(cs_addrs, 4, ERR_RAWDEVUNSUP, 2, REG_LEN_STR(gv_cur_region)); return EXIT_ERR; } pblk.def1_buf = DEF_DBEXT; pblk.def1_size = SIZEOF(DEF_DBEXT) - 1; if (ERR_PARNORMAL != (retcode = parse_file(&file, &pblk))) /* Note assignment */ { PUTMSG_ERROR_CSA(cs_addrs, 4, ERR_FNTRANSERROR, 2, REG_LEN_STR(gv_cur_region)); return EXIT_ERR; } path[pblk.b_esl] = 0; if (pblk.fnb & F_HAS_NODE) { /* Remote node specification given */ assert(pblk.b_node); PUTMSG_ERROR_CSA(cs_addrs, 4, ERR_NOCRENETFILE, 2, LEN_AND_STR(path)); return EXIT_WRN; } udi = &udi_struct; memset(udi, 0, SIZEOF(unix_db_info)); /* Check if this file is an encrypted database. If yes, do init */ if (IS_ENCRYPTED(gv_cur_region->dyn.addr->is_encrypted)) { assert(!TO_BE_ENCRYPTED(gv_cur_region->dyn.addr->is_encrypted)); INIT_PROC_ENCRYPTION(gtmcrypt_errno); if (0 != gtmcrypt_errno) { GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, file.len, file.addr); return EXIT_ERR; } } do { mu_cre_file_fd = OPEN3(pblk.l_dir, O_CREAT | O_EXCL | O_RDWR, 0600); } while ((-1 == mu_cre_file_fd) && (EINTR == errno)); if (FD_INVALID == mu_cre_file_fd) { /* Avoid error message if file already exists (another process created it) for AUTODBs that are NOT also * STATSDBs. */ save_errno = errno; TREF(mu_cre_file_openrc) = errno; /* Save for gvcst_init() */ /* If this is an AUTODB (but not a STATSDB) and the file already exists, this is not an error (some other * process created the file. This is ok so return as if we created it. */ if (IS_AUTODB_REG(gv_cur_region) && !IS_STATSDB_REG(gv_cur_region) && (EEXIST == errno)) return EXIT_NRM; /* Suppress EEXIST messages for statsDBs */ if (!IS_STATSDB_REG(gv_cur_region) || (EEXIST != errno)) PUTMSG_ERROR_CSA(cs_addrs, 5, ERR_DBOPNERR, 2, LEN_AND_STR(path), save_errno); return EXIT_ERR; } cleanup_needed = TRUE; /* File open now so cleanup needed */ mu_cre_file_path = &path[0]; /* needed by "mu_cre_file_ch" */ /* mu_cre_file_fd is also needed by "mu_cre_file_ch" but that is already set */ ESTABLISH_RET(mu_cre_file_ch, EXIT_ERR); # ifdef __MVS__ if (-1 == gtm_zos_set_tag(mu_cre_file_fd, TAG_BINARY, TAG_NOTTEXT, TAG_FORCE, &realfiletag)) SNPRINTF_AND_PERROR_MVS("Error setting tag policy for file %s (%d) to %d\n"); # endif if (0 != (save_errno = disk_block_available(mu_cre_file_fd, &avail_blocks, FALSE))) { errno = save_errno; PUTMSG_ERROR_CSA(cs_addrs, 5, ERR_DSKSPCCHK, 2, LEN_AND_STR(path), errno); /* Note: Internally invokes CLEANUP */ REVERT; return EXIT_ERR; } seg = gv_cur_region->dyn.addr; seg->read_only = FALSE; if (seg->asyncio) { /* AIO = ON, implies we need to use O_DIRECT. Check for db vs fs blksize alignment issues. */ fsb_size = get_fs_block_size(mu_cre_file_fd); if (0 != (seg->blk_size % fsb_size)) { PUTMSG_ERROR_CSA(cs_addrs, 6, ERR_DBBLKSIZEALIGN, 4, LEN_AND_STR(path), seg->blk_size, fsb_size); /* Note: Above macro internally invokes CLEANUP(EXIT_ERR) */ REVERT; return EXIT_ERR; } } /* Blocks_for_create is in the unit of DISK_BLOCK_SIZE */ blocks_for_create = (gtm_uint64_t)(DIVIDE_ROUND_UP(file_hdr_size, DISK_BLOCK_SIZE) + 1 + (seg->blk_size / DISK_BLOCK_SIZE * (gtm_uint64_t)((DIVIDE_ROUND_UP(seg->allocation, BLKS_PER_LMAP - 1)) + seg->allocation))); blocks_for_extension = (seg->blk_size / DISK_BLOCK_SIZE * ((DIVIDE_ROUND_UP(EXTEND_WARNING_FACTOR * (gtm_uint64_t)seg->ext_blk_count, BLKS_PER_LMAP - 1)) + EXTEND_WARNING_FACTOR * (gtm_uint64_t)seg->ext_blk_count)); if (!(gtmDebugLevel & GDL_IgnoreAvailSpace)) { /* Bypass this space check if debug flag above is on. Allows us to create a large sparse DB * in space it could never fit it if wasn't sparse. Needed for some tests. * Also, if the anticipatory freeze scheme is in effect at this point, we would have issued * a NOSPACECRE warning (see NOSPACEEXT message which goes through a similar transformation). * But at this point, we are guaranteed to not have access to the journal pool or csa both * of which are necessary for the INST_FREEZE_ON_ERROR_ENABLED(csa) macro so we don't bother * to do the warning transformation in this case. The only exception to this is a statsdb * which is anyways not journaled so need not worry about INST_FREEZE_ON_ERROR_ENABLED. */ assert(((NULL == jnlpool) || (NULL == jnlpool->jnlpool_ctl)) || IS_AUTODB_REG(gv_cur_region)); if (avail_blocks < blocks_for_create) { PUTMSG_ERROR_CSA(cs_addrs, 6, ERR_NOSPACECRE, 4, LEN_AND_STR(path), &blocks_for_create, &avail_blocks); /* Note: Above macro internally invokes CLEANUP(EXIT_ERR) */ if (IS_MUPIP_IMAGE) send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(6) ERR_NOSPACECRE, 4, LEN_AND_STR(path), &blocks_for_create, &avail_blocks); REVERT; return EXIT_ERR; } delta_blocks = avail_blocks - blocks_for_create; if (delta_blocks < blocks_for_extension) { PUTMSG_WARN_CSA(cs_addrs,8, ERR_LOWSPACECRE, 6, LEN_AND_STR(path), EXTEND_WARNING_FACTOR, &blocks_for_extension, DISK_BLOCK_SIZE, &delta_blocks); if (IS_MUPIP_IMAGE) /* Is not mupip, msg already went to operator log */ send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(8) ERR_LOWSPACECRE, 6, LEN_AND_STR(path), EXTEND_WARNING_FACTOR, &blocks_for_extension, DISK_BLOCK_SIZE, &delta_blocks); } } gv_cur_region->dyn.addr->file_cntl = &fc; memset(&fc, 0, SIZEOF(file_control)); fc.file_info = (void*)&udi_struct; udi->fd = mu_cre_file_fd; cs_data = (sgmnt_data_ptr_t)malloc(file_hdr_size); memset(cs_data, 0, file_hdr_size); cs_data->createinprogress = TRUE; cs_data->semid = INVALID_SEMID; cs_data->shmid = INVALID_SHMID; /* We want our datablocks to start on what would be a block boundary within the file which will aid I/O * so pad the fileheader if necessary to make this happen. */ norm_vbn = DIVIDE_ROUND_UP(file_hdr_size, DISK_BLOCK_SIZE) + 1; assert(start_vbn_target >= norm_vbn); cs_data->start_vbn = start_vbn_target; cs_data->free_space += (start_vbn_target - norm_vbn) * DISK_BLOCK_SIZE; cs_data->acc_meth = gv_cur_region->dyn.addr->acc_meth; if ((dba_mm == cs_data->acc_meth) && (gv_cur_region->jnl_before_image)) { PUTMSG_ERROR_CSA(cs_addrs, 4, ERR_NOCREMMBIJ, 2, LEN_AND_STR(path)); /* Note: Above macro internally invokes CLEANUP(EXIT_ERR) */ REVERT; return EXIT_ERR; } cs_data->write_fullblk = gv_cur_region->dyn.addr->full_blkwrt; if (cs_data->write_fullblk) { /* We have been asked to do FULL BLOCK WRITES for this database. On *NIX, attempt to get the filesystem * blocksize from statvfs. This allows a full write of a blockwithout the OS having to fetch the old * block for a read/update operation. We will round the IOs to the next filesystem blocksize if the * following criteria are met: * * 1) Database blocksize must be a whole multiple of the filesystem blocksize for the above * mentioned reason. * * 2) Filesystem blocksize must be a factor of the location of the first data block * given by the start_vbn. * * The saved length (if the feature is enabled) will be the filesystem blocksize and will be the * length that a database IO is rounded up to prior to initiation of the IO. */ fbwsize = get_fs_block_size(udi->fd); dblksize = seg->blk_size; if (0 == fbwsize || (0 != dblksize % fbwsize) || (0 != (BLK_ZERO_OFF(cs_data->start_vbn)) % fbwsize)) { cs_data->write_fullblk = 0; /* This region is not fullblockwrite enabled */ if (!IS_STATSDB_REGNAME(gv_cur_region)) { if (!fbwsize) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_DBFILNOFULLWRT, 5, LEN_AND_LIT("Could not get native filesize"), LEN_AND_LIT("File size extracted: "), fbwsize); else if (0 != dblksize % fbwsize) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_DBFILNOFULLWRT, 5, LEN_AND_LIT("Database block size not a multiple of file system block size\n"), LEN_AND_LIT("DB blocks size: "), dblksize); } } /* Report this length in DSE even if not enabled */ cs_addrs->fullblockwrite_len = fbwsize; /* Length for rounding fullblockwrite */ } cs_data->trans_hist.total_blks = gv_cur_region->dyn.addr->allocation; /* There are (bplmap - 1) non-bitmap blocks per bitmap, so add (bplmap - 2) to number of non-bitmap blocks * and divide by (bplmap - 1) to get total number of bitmaps for expanded database. (must round up in this * manner as every non-bitmap block must have an associated bitmap) */ cs_data->trans_hist.total_blks += DIVIDE_ROUND_UP(cs_data->trans_hist.total_blks, BLKS_PER_LMAP - 1); cs_data->extension_size = gv_cur_region->dyn.addr->ext_blk_count; /* Check if this file is an encrypted database. If yes, do init */ if (IS_ENCRYPTED(gv_cur_region->dyn.addr->is_encrypted)) { GTMCRYPT_HASH_GEN(cs_addrs, strnlen(path, sizeof(path)), path, 0, NULL, hash, gtmcrypt_errno); if (0 != gtmcrypt_errno) { GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, file.len, file.addr); REVERT; CLEANUP(EXIT_ERR); return EXIT_ERR; } memcpy(cs_data->encryption_hash, hash, GTMCRYPT_HASH_LEN); SET_AS_ENCRYPTED(cs_data->is_encrypted); /* Mark this file as encrypted */ INIT_DB_OR_JNL_ENCRYPTION(cs_addrs, cs_data, strnlen(path, sizeof(path)), path, gtmcrypt_errno); if (0 != gtmcrypt_errno) { GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, file.len, file.addr); REVERT; CLEANUP(EXIT_ERR); return EXIT_ERR; } } else SET_AS_UNENCRYPTED(cs_data->is_encrypted); cs_data->non_null_iv = TRUE; cs_data->encryption_hash_cutoff = UNSTARTED; cs_data->encryption_hash2_start_tn = 0; cs_data->span_node_absent = TRUE; cs_data->maxkeysz_assured = TRUE; cs_data->problksplit = DEFAULT_PROBLKSPLIT; mucregini(cs_data->trans_hist.total_blks, gtm_db_create_ver); cs_data->createinprogress = FALSE; if (GDSVCURR != gtm_db_create_ver) db_header_dwnconv(cs_data); ASSERT_NO_DIO_ALIGN_NEEDED(udi); /* because we are creating the database and so effectively have standalone access */ DB_LSEEKWRITE(cs_addrs, udi, udi->fn, udi->fd, 0, cs_data, file_hdr_size, status); if (0 != status) { PUTMSG_ERROR_CSA(cs_addrs, 7, ERR_FILECREERR, 4, LEN_AND_LIT("writing out file header"), LEN_AND_LIT(path), status); /* Note: Above macro internally invokes CLEANUP(EXIT_ERR) */ REVERT; return EXIT_ERR; } new_eof = (off_t)BLK_ZERO_OFF(cs_data->start_vbn) + (off_t)cs_data->trans_hist.total_blks * cs_data->blk_size; status = db_write_eof_block(udi, udi->fd, cs_data->blk_size, new_eof, &(TREF(dio_buff))); if (0 != status) { PUTMSG_ERROR_CSA(cs_addrs, 7, ERR_FILECREERR, 4, LEN_AND_LIT("writing out end-of-file marker"), LEN_AND_LIT(path), status); /* Note: Above macro internally invokes CLEANUP(EXIT_ERR) */ REVERT; return EXIT_ERR; } if (!cs_data->defer_allocate) { POSIX_FALLOCATE(udi->fd, 0, BLK_ZERO_OFF(cs_data->start_vbn) + ((off_t)(cs_data->trans_hist.total_blks + 1) * cs_data->blk_size), status); if (0 != status) { assert(ENOSPC == status); PUTMSG_ERROR_CSA(cs_addrs, 5, ERR_PREALLOCATEFAIL, 2, DB_LEN_STR(gv_cur_region), status); /* Note: Above macro internally invokes CLEANUP(EXIT_ERR) */ REVERT; return EXIT_ERR; } } /* If we are opening a statsDB, use IPC type permissions derived from the baseDB */ if (IS_STATSDB_REG(gv_cur_region)) { STATSDBREG_TO_BASEDBREG(gv_cur_region, baseDBreg); assert(baseDBreg->open); baseDBcsa = &FILE_INFO(baseDBreg)->s_addrs; STAT_FILE((char *)baseDBcsa->nl->fname, &stat_buf, retcode); if (0 > retcode) { /* Should be rare-if-ever message as we just opened the baseDB so it should be there */ save_errno = errno; PUTMSG_ERROR_CSA(cs_addrs, 7, ERR_FILECREERR, 4, LEN_AND_LIT("getting base file information"), LEN_AND_STR(path), save_errno); /* Note: Above macro internally invokes CLEANUP(EXIT_ERR) */ REVERT; return EXIT_ERR; } if (!gtm_permissions(&stat_buf, &user_id, &group_id, &perms, PERM_IPC, &pdd)) { /* Not sure what could cause this as we would have done the same call when opening the baseDB but * make sure it is present just in case. */ PUTMSG_ERROR_CSA(cs_addrs, 7, ERR_FILECREERR, 4, LEN_AND_LIT("obtaining permissions from base DB"), LEN_AND_STR(path), EPERM); /* Note: Above macro internally invokes CLEANUP(EXIT_ERR) */ REVERT; return EXIT_ERR; } } else perms = 0666; if (-1 == CHMOD(pblk.l_dir, perms)) { save_errno = errno; PUTMSG_WARN_CSA(cs_addrs, 7, MAKE_MSG_WARNING(ERR_FILECREERR), 4, LEN_AND_LIT("changing file mode"), LEN_AND_LIT(path), save_errno); REVERT; CLEANUP(EXIT_WRN); return EXIT_WRN; } if ((32 * 1024 - SIZEOF(shmpool_blk_hdr)) < cs_data->blk_size) PUTMSG_WARN_CSA(cs_addrs, 5, ERR_MUNOSTRMBKUP, 3, RTS_ERROR_STRING(path), 32 * 1024 - DISK_BLOCK_SIZE); if (!(RDBF_AUTODB & gv_cur_region->reservedDBFlags)) PUTMSG_WARN_CSA(cs_addrs, 4, ERR_DBFILECREATED, 2, LEN_AND_STR(path)); REVERT; CLEANUP(EXIT_NRM); return EXIT_NRM; } fis-gtm-V7.0-005/sr_unix/mu_cre_structs.c0000755000032200000250000000134714342376330017256 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "mu_cre_structs.h" void mu_cre_structs(gd_region *greg) { FILE_CNTL_INIT(greg->dyn.addr); return; } fis-gtm-V7.0-005/sr_unix/mu_cre_structs.h0000755000032200000250000000117014342376330017255 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef MU_CRE_STRUCTS_H_INCLUDED #define MU_CRE_STRUCTS_H_INCLUDED void mu_cre_structs(gd_region *greg); #endif fis-gtm-V7.0-005/sr_unix/mu_decrypt.c0000644000032200000250000001750714342376330016372 0ustar librarygtc /**************************************************************** * * * Copyright (c) 2009-2020 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_facility.h" #include "gtm_strings.h" #include "gtm_stdio.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "fileinfo.h" #include "filestruct.h" #include "jnl.h" #include "util.h" #include "gtmio.h" #include "gtmcrypt.h" #include "mu_decrypt.h" #define GC_DISPLAY_ERROR_AND_RETURN(MESSAGE, RC, ...) \ { \ char *errptr; \ \ util_out_print(MESSAGE, TRUE, __VA_ARGS__); \ if (0 < RC) \ { \ errptr = (char *)STRERROR(RC); \ util_out_print("System Error: !AZ", TRUE, errptr); \ } \ return RC; \ } /* Given a file (journal or database), the function extracts the buffer of the given length at the given offset and displays it on * the STDIN. Note that the offset and length should match the boundaries of a database block or a journal record at the encryption * time. In case of journal files, this offset could be obtained for every record using a detailed journal extract. That, however, * does not guarantee that the actual length of the content printed will be that of the data contained in the respective record due * to paddings inserted in journal files for proper alignment. * * NOTE: This utility does not work with encryption on-the-fly. */ int mu_decrypt(char *fname, int fname_len, uint4 off, uint4 len, char *type, int type_len) { int fd, save_errno, gtmcrypt_errno, i, status, iv_len; char hash[GTMCRYPT_HASH_LEN], iv[GTM_MAX_IV_LEN], *iv_ptr, *buff, *buff_ptr; boolean_t is_encrypted, is_journal; gtmcrypt_key_t key_handle; jrec_prefix *prefix; blk_hdr *header; if (!STRNCASECMP_LIT(type, "JNL_NONLOG_IV") || !STRNCASECMP_LIT(type, "JNL_LOG_IV") || !STRNCASECMP_LIT(type, "JNL_LOG_NO_IV") || !STRNCASECMP_LIT(type, "JNL_NONLOG_NO_IV")) { if (REAL_JNL_HDR_LEN > off) GC_DISPLAY_ERROR_AND_RETURN("Incorrect offset specified for file !AD with type !AD", -1, fname_len, fname, type_len, type); is_journal = TRUE; } else { assert(!STRNCASECMP_LIT(type, "DB_IV") || !STRNCASECMP_LIT(type, "DB_NO_IV")); if (SGMNT_HDR_LEN > off) GC_DISPLAY_ERROR_AND_RETURN("Incorrect offset specified for file !AD with type !AD", -1, fname_len, fname, type_len, type); is_journal = FALSE; } if (0 != (status = get_file_encr_hash(is_journal, fname, fname_len, &fd, hash, &is_encrypted))) return status; buff = (char *)malloc(len); buff_ptr = buff; LSEEKREAD(fd, off, buff_ptr, len, save_errno); if (0 != save_errno) { close(fd); free(buff); GC_DISPLAY_ERROR_AND_RETURN("Error reading from file !AD", save_errno, fname_len, fname); } if (is_encrypted) { INIT_PROC_ENCRYPTION(gtmcrypt_errno); if (is_journal) { GTMCRYPT_INIT_BOTH_CIPHER_CONTEXTS(NULL, hash, 0, NULL, key_handle, gtmcrypt_errno); } else { GTMCRYPT_INIT_BOTH_CIPHER_CONTEXTS(NULL, hash, fname_len, fname, key_handle, gtmcrypt_errno); } if (0 != gtmcrypt_errno) { close(fd); free(buff); GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, rts_error, fname_len, fname); } } if (!STRNCASECMP_LIT(type, "JNL_NONLOG_IV")) { /* For non-logical records using non-null IVs the IV is the block header, so skip the prefix and other meta part. */ prefix = (jrec_prefix *)buff_ptr; len = prefix->forwptr - FIXED_AIMG_RECLEN - SIZEOF(blk_hdr) - SIZEOF(jrec_suffix); buff_ptr += FIXED_AIMG_RECLEN; if (is_encrypted) { /* Set up block-header-based IV. */ iv_ptr = (char *)buff_ptr; iv_len = SIZEOF(blk_hdr); } buff_ptr += SIZEOF(blk_hdr); } else if (!STRNCASECMP_LIT(type, "JNL_LOG_IV")) { /* For logical records using non-null IVs the IV is the forwptr field of the prefix, so first obtain that and then * skip to the beginning of the data section. */ prefix = (jrec_prefix *)buff_ptr; len = prefix->forwptr - FIXED_UPD_RECLEN - SIZEOF(jrec_suffix); if (is_encrypted) { /* Set up record-prefix-based IV. */ PREPARE_LOGICAL_REC_IV(prefix->forwptr, iv); iv_ptr = (char *)iv; iv_len = SIZEOF(uint4) * 4; } buff_ptr += FIXED_UPD_RECLEN; } else if (!STRNCASECMP_LIT(type, "JNL_NONLOG_NO_IV")) { /* For non-logical records using null IVs the IV is 16 zeroes (which is automatically filled in by the encryption * plug-in), so skip the prefix and other meta part right away. */ prefix = (jrec_prefix *)buff_ptr; len = prefix->forwptr - FIXED_AIMG_RECLEN - SIZEOF(jrec_suffix); buff_ptr += FIXED_AIMG_RECLEN + SIZEOF(blk_hdr); iv_len = 0; } else if (!STRNCASECMP_LIT(type, "JNL_LOG_NO_IV")) { /* For logical records using null IVs the IV is 16 zeroes (which is automatically filled in by the encryption * plug-in), so skip the prefix and other meta part right away. */ prefix = (jrec_prefix *)buff_ptr; len = prefix->forwptr - FIXED_UPD_RECLEN - SIZEOF(jrec_suffix); iv_len = 0; buff_ptr += FIXED_UPD_RECLEN; } else if (!STRNCASECMP_LIT(type, "DB_IV")) { /* For database blocks using non-null IVs the IV is the block header, so only skip to the records after setting up * the IV. */ header = (blk_hdr *)buff_ptr; len = header->bsiz - SIZEOF(blk_hdr); if (is_encrypted) { /* Set up block-header-based IV. */ iv_ptr = (char *)buff_ptr; iv_len = SIZEOF(blk_hdr); } buff_ptr += SIZEOF(blk_hdr); } else { /* For database blocks using null IVs the IV is 16 zeroes (which is automatically filled in by the encryption * plug-in), so skip to the records right away. */ assert(!STRNCASECMP_LIT(type, "DB_NO_IV")); header = (blk_hdr *)buff_ptr; len = header->bsiz - SIZEOF(blk_hdr); iv_len = 0; buff_ptr += SIZEOF(blk_hdr); } if (is_encrypted) { GTMCRYPT_DECRYPT_WITH_IV(NULL, key_handle, buff_ptr, len, NULL, iv_ptr, iv_len, gtmcrypt_errno); if (0 != gtmcrypt_errno) { close(fd); free(buff); GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, rts_error, fname_len, fname); } } for (i = 0; i < len; i++) { PRINTF("%c", buff_ptr[i]); } PRINTF("\n"); FFLUSH(stdout); free(buff); close(fd); return SS_NORMAL; } int get_file_encr_hash(boolean_t is_journal, char *fname, int fname_len, int *fd, char *hash, boolean_t *is_encrypted) { jnl_file_header *jfh; sgmnt_data *dfh; void *header; int save_errno, hdr_sz; uint4 status; OPENFILE(fname, O_RDONLY, *fd); /* udi not available so OPENFILE_DB not used */ if (-1 == *fd) { save_errno = errno; GC_DISPLAY_ERROR_AND_RETURN("Error accessing file !AD", save_errno, fname_len, fname); } hdr_sz = is_journal ? REAL_JNL_HDR_LEN : SGMNT_HDR_LEN; header = malloc(hdr_sz); LSEEKREAD(*fd, 0, header, hdr_sz, save_errno); if (0 != save_errno) { free(header); GC_DISPLAY_ERROR_AND_RETURN("Error reading file !AD", save_errno, fname_len, fname); } if (is_journal) { jfh = (jnl_file_header *)header; status = 0; CHECK_JNL_FILE_IS_USABLE(jfh, status, TRUE, fname_len, fname); if (0 != status) { /* gtm_putmsg would have already been done by CHECK_JNL_FILE_IS_USABLE macro */ free(header); return status; } } else { dfh = (sgmnt_data *)header; if (status = MEMCMP_LIT(dfh->label, GDS_LABEL_GENERIC)) /* Note: assignment! */ { free(header); GC_DISPLAY_ERROR_AND_RETURN("Invalid database file !AD", status, fname_len, fname); } } if (is_journal) { memcpy(hash, jfh->encryption_hash, GTMCRYPT_HASH_LEN); *is_encrypted = IS_ENCRYPTED(jfh->is_encrypted); } else { memcpy(hash, dfh->encryption_hash, GTMCRYPT_HASH_LEN); *is_encrypted = IS_ENCRYPTED(dfh->is_encrypted); } free(header); return 0; } fis-gtm-V7.0-005/sr_unix/mu_decrypt.h0000755000032200000250000000147614342376330016400 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2015 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. * * * ****************************************************************/ #ifndef MUPIP_CRYPT_INCLUDED #define MUPIP_CRYPT_INCLUDED int get_file_encr_hash(boolean_t is_journal, char *fname, int fname_len, int *fd, char *hash, boolean_t *is_encrypted); int mu_decrypt(char *fname, int fname_len, uint4 offset, uint4 length, char *type, int type_len); #endif /* MUPIP_CRYPT_INCLUDED */ fis-gtm-V7.0-005/sr_unix/mu_extract.c0000644000032200000250000005347214342376330016373 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_iconv.h" #include "gtm_stdio.h" #include "gtm_stat.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif #include "stp_parms.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "muextr.h" #include "cli.h" #include "io.h" #include "iosp.h" #include "gtmio.h" #include "io_params.h" #include "eintr_wrappers.h" #include "error.h" #include "util.h" #include "gtm_caseconv.h" #include "op.h" #include "mupip_exit.h" #include "is_raw_dev.h" #include "gv_select.h" #include "mu_outofband_setup.h" #include "gtmmsg.h" #include "mvalconv.h" #include "gtm_conv.h" #include "gtm_utf8.h" #include "filestruct.h" /* needed for jnl.h */ #include "hashtab_mname.h" #include "gvcst_protos.h" /* for gvcst_root_search in DO_OP_GVNAME macro */ #include "change_reg.h" /* for change_reg call in DO_OP_GVNAME macro */ #include "mu_getlst.h" #include "gdskill.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdscc.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "gtmcrypt.h" #include "is_proc_alive.h" #include "gtm_reservedDB.h" #include "min_max.h" GBLREF int (*op_open_ptr)(mval *v, mval *p, mval *t, mval *mspace); GBLREF bool mu_ctrlc_occurred; GBLREF bool mu_ctrly_occurred; GBLREF gd_region *gv_cur_region; GBLREF gd_addr *gd_header; GBLREF io_pair io_curr_device; GBLREF io_desc *active_device; GBLREF gv_namehead *gv_target; GBLREF boolean_t jnlpool_init_needed; GBLREF mstr sys_output; GBLREF tp_region *grlist; GBLREF sgmnt_data_ptr_t cs_data; error_def(ERR_DBNOREGION); error_def(ERR_EXTRACTCTRLY); error_def(ERR_EXTRACTFILERR); error_def(ERR_ENCRYPTCONFLT); error_def(ERR_EXTRFILEXISTS); error_def(ERR_EXTRINTEGRITY); error_def(ERR_ICUNOTENABLED); error_def(ERR_MUNOACTION); error_def(ERR_MUNOFINISH); error_def(ERR_MUPCLIERR); error_def(ERR_NOSELECT); error_def(ERR_NULLCOLLDIFF); error_def(ERR_RECORDSTAT); error_def(ERR_TEXT); LITDEF mval mu_bin_datefmt = DEFINE_MVAL_LITERAL(MV_STR, 0, 0, SIZEOF(BIN_HEADER_DATEFMT) - 1, BIN_HEADER_DATEFMT, 0, 0); LITREF mval literal_zero; LITREF mstr chset_names[]; STATICDEF readonly unsigned char datefmt_txt[] = "DD-MON-YEAR 24:60:SS"; STATICDEF readonly unsigned char select_text[] = "SELECT"; STATICDEF readonly mval datefmt = DEFINE_MVAL_LITERAL(MV_STR, 0, 0, SIZEOF(datefmt_txt) - 1, (char *)datefmt_txt, 0, 0); STATICDEF readonly mval null_str = DEFINE_MVAL_LITERAL(MV_STR, 0, 0, 0, 0, 0, 0); STATICDEF char outfilename[256]; STATICDEF unsigned short filename_len; STATICDEF unsigned char ochset_set = FALSE; STATICDEF readonly unsigned char open_params_list[] = { (unsigned char)iop_noreadonly, (unsigned char)iop_m, (unsigned char)iop_stream, (unsigned char)iop_nowrap, (unsigned char)iop_buffered, 1, 0x03, (unsigned char)iop_eol }; STATICDEF readonly unsigned char use_params[] = { (unsigned char)iop_nowrap, (unsigned char)iop_eol }; STATICDEF readonly unsigned char no_param = (unsigned char)iop_eol; STATICDEF boolean_t is_binary_format; STATICDEF gd_region **opened_regions; STATICDEF uint4 opened_region_count; #define BINARY_FORMAT_STRING "BINARY" #define ZWR_FORMAT_STRING "ZWR" #define GO_FORMAT_STRING "GO" #define WRITE_NUMERIC(nmfield) \ { \ MV_FORCE_MVAL(&val, nmfield); \ n2s(&val); \ assertpro(!(val.mvtype & MV_NUM_APPROX)); \ assertpro(BIN_HEADER_NUMSZ >= val.str.len); \ for (iter = val.str.len; iter < BIN_HEADER_NUMSZ; iter++) \ *outptr++ = '0'; \ memcpy(outptr, val.str.addr, val.str.len); \ outptr += val.str.len; \ } #define GET_BIN_HEADER_SIZE(LABEL) (SIZEOF(LABEL) + SIZEOF(BIN_HEADER_DATEFMT) - 1 + 4 * BIN_HEADER_NUMSZ + BIN_HEADER_LABELSZ) CONDITION_HANDLER(mu_extract_handler) { int i; gd_region *reg; node_local_ptr_t cnl; START_CH(TRUE); if (is_binary_format) { for (i = 0; i < opened_region_count; i++) { reg = opened_regions[i]; cnl = (&FILE_INFO(reg)->s_addrs)->nl; grab_crit(reg, WS_85); cnl->mupip_extract_count--; rel_crit(reg); } } NEXTCH; } CONDITION_HANDLER(mu_extract_handler1) { mval op_val, op_pars; unsigned char delete_params[2] = { (unsigned char)iop_delete, (unsigned char)iop_eol }; START_CH(TRUE); op_val.mvtype = op_pars.mvtype = MV_STR; op_val.str.addr = (char *)outfilename; op_val.str.len = filename_len; op_pars.str.len = SIZEOF(delete_params); op_pars.str.addr = (char *)delete_params; op_close(&op_val, &op_pars); util_out_print("!/MUPIP is not able to create extract file !AD due to the above error!/", TRUE, filename_len, outfilename); NEXTCH; } CONDITION_HANDLER(mu_extract_handler2) { START_CH(TRUE); util_out_print("!/MUPIP is not able to complete the extract due the the above error!/", TRUE); util_out_print("!/WARNING!!!!!! Extract file !AD is incomplete!!!/", TRUE, filename_len, outfilename); NEXTCH; } void mu_extract(void) { int stat_res, truncate_res, index, index2; int reg_max_rec, reg_max_key, reg_max_blk, reg_std_null_coll; int iter, format, local_errno, int_nlen; boolean_t freeze, override, logqualifier, success, success2; char format_buffer[FORMAT_STR_MAX_SIZE], ch_set_name[MAX_CHSET_NAME + 1], cli_buff[MAX_LINE], label_buff[LABEL_STR_MAX_SIZE]; glist gl_head, *gl_ptr, *next_gl_ptr; gd_region *reg, *region_top; mu_extr_stats global_total, grand_total, spangbl_total; uint4 item_code, devbufsiz, maxfield; unsigned short label_len, n_len, ch_set_len, buflen; unsigned char *outbuf, *outptr, *chptr, *leadptr; struct stat statbuf; mval val, curr_gbl_name, op_val, op_pars; mstr chset_mstr; mname_entry gvname; gtm_chset_t saved_out_set; coll_hdr extr_collhdr; int bin_header_size; boolean_t any_file_encrypted, any_file_uses_non_null_iv, null_iv; gvnh_reg_t *gvnh_reg; gvnh_spanreg_t *gvspan, *last_gvspan; boolean_t region; unsigned short hash_array_len, hash2_index_array_len, null_iv_array_len; uint4 *curr_hash2_index_ptr, *hash2_index_array_ptr; unsigned char *curr_hash_ptr, *hash_array_ptr, *null_iv_array_ptr; sgmnt_data_ptr_t csd; sgmnt_addrs *csa; node_local_ptr_t cnl; int use_null_iv; tp_region *rptr; uint4 pid; freeze = override = FALSE; any_file_encrypted = FALSE; any_file_uses_non_null_iv = FALSE; /* Initialize all local character arrays to zero before using */ memset(cli_buff, 0, SIZEOF(cli_buff)); memset(outfilename, 0, SIZEOF(outfilename)); memset(label_buff, 0, SIZEOF(label_buff)); memset(format_buffer, 0, SIZEOF(format_buffer)); active_device = io_curr_device.out; mu_outofband_setup(); if (CLI_PRESENT == cli_present("OCHSET")) { if (gtm_utf8_mode) { ch_set_len = MAX_CHSET_NAME; if (cli_get_str("OCHSET", ch_set_name, &ch_set_len)) { if (0 == ch_set_len) mupip_exit(ERR_MUNOACTION); /* need to change to OCHSET error when added */ ch_set_name[ch_set_len] = '\0'; # ifdef KEEP_zOS_EBCDIC if ( (iconv_t)0 != active_device->output_conv_cd) ICONV_CLOSE_CD(active_device->output_conv_cd); if (DEFAULT_CODE_SET != active_device->out_code_set) ICONV_OPEN_CD(active_device->output_conv_cd, INSIDE_CH_SET, ch_set_name); # else chset_mstr.addr = ch_set_name; chset_mstr.len = ch_set_len; SET_ENCODING(active_device->ochset, &chset_mstr); get_chset_desc(&chset_names[active_device->ochset]); # endif ochset_set = TRUE; } } else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_ICUNOTENABLED, 0, ERR_TEXT, 2, LEN_AND_LIT("Cannot use the -OCHSET qualifier. Continuing without it.")); } region = FALSE; if (CLI_PRESENT == cli_present("REGION")) { gvinit(); /* side effect: initializes gv_altkey (used by code below) & gv_currkey (not used by below code) */ mu_getlst("REGION", SIZEOF(tp_region)); if (!grlist) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_DBNOREGION); mupip_exit(ERR_MUNOACTION); } region = TRUE; } logqualifier = (CLI_NEGATED != cli_present("LOG")); if (CLI_PRESENT == cli_present("FREEZE")) freeze = TRUE; if (CLI_PRESENT == cli_present("OVERRIDE")) override = TRUE; if (CLI_PRESENT == cli_present("NULL_IV")) use_null_iv = 1; else if (CLI_NEGATED == cli_present("NULL_IV")) use_null_iv = 0; else use_null_iv = -1; n_len = SIZEOF(format_buffer); if (FALSE == cli_get_str("FORMAT", format_buffer, &n_len)) { n_len = SIZEOF(ZWR_FORMAT_STRING) - 1; memcpy(format_buffer, ZWR_FORMAT_STRING, n_len); } int_nlen = n_len; lower_to_upper((uchar_ptr_t)format_buffer, (uchar_ptr_t)format_buffer, int_nlen); if (0 == STRNCMP_LIT_LEN(format_buffer, ZWR_FORMAT_STRING, n_len)) format = MU_FMT_ZWR; else if (0 == STRNCMP_LIT_LEN(format_buffer, GO_FORMAT_STRING, n_len)) { if (gtm_utf8_mode) { util_out_print("Extract error: GO format is not supported in UTF-8 mode. Use ZWR format.", TRUE); mupip_exit(ERR_MUPCLIERR); } format = MU_FMT_GO; } else if (0 == STRNCMP_LIT_LEN(format_buffer, BINARY_FORMAT_STRING, n_len)) { format = MU_FMT_BINARY; is_binary_format = TRUE; } else { util_out_print("Extract error: bad format type", TRUE); mupip_exit(ERR_MUPCLIERR); } n_len = SIZEOF(cli_buff); if (FALSE == cli_get_str((char *)select_text, cli_buff, &n_len)) { n_len = 1; cli_buff[0] = '*'; } /* gv_select will select globals */ jnlpool_init_needed = TRUE; gv_select(cli_buff, n_len, freeze, (char *)select_text, &gl_head, ®_max_rec, ®_max_key, ®_max_blk, region); if (!gl_head.next) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOSELECT); mupip_exit(ERR_NOSELECT); } if (!region) { for (reg = gd_header->regions, region_top = gd_header->regions + gd_header->n_regions; reg < region_top; reg++) { if (reg->open) insert_region(reg, &(grlist), NULL, SIZEOF(tp_region)); } } ESTABLISH(mu_extract_handler); opened_regions = (gd_region **)malloc(SIZEOF(gd_region *) * gd_header->n_regions); /* For binary format, check whether all regions have same null collation order */ if (MU_FMT_BINARY == format) { hash_array_len = GTMCRYPT_HASH_LEN * gd_header->n_regions; hash_array_ptr = malloc(hash_array_len * 2); memset(hash_array_ptr, 0, hash_array_len * 2); hash2_index_array_len = gd_header->n_regions * SIZEOF(uint4); hash2_index_array_ptr = malloc(hash2_index_array_len); memset(hash2_index_array_ptr, 0, hash2_index_array_len); null_iv_array_len = gd_header->n_regions; null_iv_array_ptr = malloc(null_iv_array_len); memset(null_iv_array_ptr, 0, null_iv_array_len); for (rptr = grlist, reg_std_null_coll = -1, index = 0; NULL != rptr; rptr = rptr->fPtr, index++) { reg = rptr->reg; if (reg->open) { if (reg_std_null_coll != reg->std_null_coll) { if (reg_std_null_coll == -1) reg_std_null_coll = reg->std_null_coll; else { RTS_ERROR_CSA_ABT(REG2CSA(reg), VARLSTCNT(1) ERR_NULLCOLLDIFF); mupip_exit(ERR_NULLCOLLDIFF); } } csa = &FILE_INFO(reg)->s_addrs; csd = csa->hdr; cnl = csa->nl; grab_crit(reg, WS_86); pid = cnl->reorg_encrypt_pid; if (pid && is_proc_alive(pid, 0)) { RTS_ERROR_CSA_ABT(REG2CSA(reg), VARLSTCNT(8) ERR_ENCRYPTCONFLT, 6, RTS_ERROR_LITERAL("MUPIP EXTRACT -FORMAT=BIN"), REG_LEN_STR(reg), DB_LEN_STR(reg)); mupip_exit(ERR_ENCRYPTCONFLT); } cnl->mupip_extract_count++; opened_regions[opened_region_count++] = reg; rel_crit(reg); if (!freeze && !override && (!csd->span_node_absent || USES_NEW_KEY(csd))) { RTS_ERROR_CSA_ABT(REG2CSA(reg), VARLSTCNT(8) ERR_EXTRINTEGRITY, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("Use the -FREEZE qualifier to freeze the " "database(s) or -OVERRIDE qualifier to proceed without a freeze")); mupip_exit(ERR_EXTRINTEGRITY); } if (IS_ENCRYPTED(csd->is_encrypted)) { curr_hash_ptr = hash_array_ptr + (GTMCRYPT_HASH_LEN * index); memcpy(curr_hash_ptr, csd->encryption_hash, GTMCRYPT_HASH_LEN); any_file_encrypted = TRUE; } if (USES_NEW_KEY(csd)) { curr_hash_ptr = hash_array_ptr + hash_array_len; memcpy(curr_hash_ptr, csd->encryption_hash2, GTMCRYPT_HASH_LEN); curr_hash2_index_ptr = hash2_index_array_ptr + index; *curr_hash2_index_ptr = hash_array_len / GTMCRYPT_HASH_LEN; hash_array_len += GTMCRYPT_HASH_LEN; any_file_encrypted = TRUE; } if ((1 == use_null_iv) || (!USES_NEW_KEY(csd) && (!IS_ENCRYPTED(csd->is_encrypted) || !csd->non_null_iv))) *(null_iv_array_ptr + index) = '1'; else { *(null_iv_array_ptr + index) = '0'; any_file_uses_non_null_iv = TRUE; } } } assert(-1 != reg_std_null_coll); } MU_EXTR_STATS_INIT(grand_total); MU_EXTR_STATS_INIT(global_total); n_len = SIZEOF(outfilename); if (CLI_PRESENT == cli_present("STDOUT")) op_val.str = sys_output; /* Redirect to standard output */ else if (FALSE == cli_get_str("FILE", outfilename, &n_len)) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MUPCLIERR); mupip_exit(ERR_MUPCLIERR); } else if (-1 == Stat((char *)outfilename, &statbuf)) { /* Redirect to file */ if (ENOENT != errno) { local_errno = errno; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_EXTRACTFILERR, 2, LEN_AND_STR(outfilename), local_errno); mupip_exit(local_errno); } op_val.str.len = filename_len = n_len; op_val.str.addr = (char *)outfilename; } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_EXTRFILEXISTS, 2, LEN_AND_STR(outfilename)); mupip_exit(ERR_MUNOACTION); } op_pars.mvtype = MV_STR; op_pars.str.len = SIZEOF(open_params_list); op_pars.str.addr = (char *)open_params_list; op_val.mvtype = MV_STR; (*op_open_ptr)(&op_val, &op_pars, (mval *)&literal_zero, 0); ESTABLISH(mu_extract_handler1); op_pars.str.len = SIZEOF(use_params); op_pars.str.addr = (char *)&use_params; op_use(&op_val, &op_pars); if (MU_FMT_BINARY == format) { /* binary header label format: * fixed length text, fixed length date & time, * fixed length max blk size, fixed length max rec size, fixed length max key size, fixed length std_null_coll * 32-byte padded user-supplied string */ outbuf = (unsigned char *)malloc(SIZEOF(BIN_HEADER_LABEL) + SIZEOF(BIN_HEADER_DATEFMT) - 1 + 4 * BIN_HEADER_NUMSZ + BIN_HEADER_LABELSZ); outptr = outbuf; if (any_file_encrypted) { if (any_file_uses_non_null_iv) { MEMCPY_LIT(outptr, BIN_HEADER_LABEL_ENCR_IV); outptr += STR_LIT_LEN(BIN_HEADER_LABEL_ENCR_IV); } else { MEMCPY_LIT(outptr, BIN_HEADER_LABEL_ENCR_INDEX); outptr += STR_LIT_LEN(BIN_HEADER_LABEL_ENCR_INDEX); } } else { MEMCPY_LIT(outptr, BIN_HEADER_LABEL); outptr += STR_LIT_LEN(BIN_HEADER_LABEL); } op_zhorolog(&val, FALSE); op_fnzdate(&val, (mval *)&mu_bin_datefmt, &null_str, &null_str, &val); memcpy(outptr, val.str.addr, val.str.len); outptr += val.str.len; WRITE_NUMERIC(reg_max_blk); WRITE_NUMERIC(reg_max_rec); WRITE_NUMERIC(reg_max_key); WRITE_NUMERIC(reg_std_null_coll); if (gtm_utf8_mode) { MEMCPY_LIT(outptr, UTF8_NAME); label_len = STR_LIT_LEN(UTF8_NAME); outptr[label_len++] = ' '; } else label_len = 0; buflen = SIZEOF(label_buff); if (FALSE == cli_get_str("LABEL", label_buff, &buflen)) { MEMCPY_LIT(&outptr[label_len], EXTR_DEFAULT_LABEL); buflen = STR_LIT_LEN(EXTR_DEFAULT_LABEL); } else memcpy(&outptr[label_len], label_buff, buflen); label_len += buflen; if (label_len > BIN_HEADER_LABELSZ) { /* Label size exceeds the space, so truncate the label and back off to the valid beginning * (i.e. to the leading byte) of the last character that can entirely fit in the space */ label_len = BIN_HEADER_LABELSZ; chptr = &outptr[BIN_HEADER_LABELSZ]; UTF8_LEADING_BYTE(chptr, outptr, leadptr); assert(chptr - leadptr < 4); if (leadptr < chptr) label_len -= (chptr - leadptr); } outptr += label_len; for (iter = label_len; iter < BIN_HEADER_LABELSZ; iter++) *outptr++ = ' '; label_len = outptr - outbuf; if (!ochset_set) { # ifdef KEEP_zOS_EBCDIC /* extract ascii header for binary by default */ /* Do we need to restore it somewhere? */ saved_out_set = (io_curr_device.out)->out_code_set; (io_curr_device.out)->out_code_set = DEFAULT_CODE_SET; # else saved_out_set = (io_curr_device.out)->ochset; (io_curr_device.out)->ochset = CHSET_M; # endif } op_val.str.addr = (char *)(&label_len); op_val.str.len = SIZEOF(label_len); op_write(&op_val); op_val.str.addr = (char *)outbuf; op_val.str.len = label_len; op_write(&op_val); if (any_file_encrypted) { op_val.str.addr = (char *)(&hash_array_len); op_val.str.len = SIZEOF(hash_array_len); op_write(&op_val); op_val.str.addr = (char *)hash_array_ptr; op_val.str.len = hash_array_len; op_write(&op_val); if (any_file_uses_non_null_iv) { op_val.str.addr = (char *)(&null_iv_array_len); op_val.str.len = SIZEOF(null_iv_array_len); op_write(&op_val); op_val.str.addr = (char *)null_iv_array_ptr; op_val.str.len = null_iv_array_len; op_write(&op_val); } } } else { assert((MU_FMT_GO == format) || (MU_FMT_ZWR == format)); label_len = SIZEOF(label_buff); if (FALSE == cli_get_str("LABEL", label_buff, &label_len)) { MEMCPY_LIT(label_buff, EXTR_DEFAULT_LABEL); label_len = STR_LIT_LEN(EXTR_DEFAULT_LABEL); } if (gtm_utf8_mode) { label_buff[label_len++] = ' '; MEMCPY_LIT(&label_buff[label_len], UTF8_NAME); label_len += STR_LIT_LEN(UTF8_NAME); } label_buff[label_len++] = '\n'; op_val.mvtype = MV_STR; op_val.str.len = label_len; op_val.str.addr = label_buff; op_write(&op_val); op_zhorolog(&val, FALSE); op_fnzdate(&val, &datefmt, &null_str, &null_str, &val); op_val = val; op_val.mvtype = MV_STR; op_write(&op_val); if (MU_FMT_ZWR == format) { op_val.str.addr = " ZWR"; op_val.str.len = SIZEOF(" ZWR") - 1; op_write(&op_val); } op_wteol(1); } REVERT; ESTABLISH(mu_extract_handler2); success = TRUE; gvspan = NULL; for (gl_ptr = gl_head.next; gl_ptr; gl_ptr = next_gl_ptr) { if (mu_ctrly_occurred) break; /* Sets gv_target/gv_currkey/gv_cur_region/cs_addrs/cs_data to correspond to in gl_ptr. */ DO_OP_GVNAME(gl_ptr); if (MU_FMT_BINARY == format) { label_len = SIZEOF(extr_collhdr); op_val.mvtype = MV_STR; op_val.str.addr = (char *)(&label_len); op_val.str.len = SIZEOF(label_len); op_write(&op_val); extr_collhdr.act = gv_target->act; extr_collhdr.nct = gv_target->nct; extr_collhdr.ver = gv_target->ver; op_val.str.addr = (char *)(&extr_collhdr); op_val.str.len = SIZEOF(extr_collhdr); op_write(&op_val); } if ((MU_FMT_BINARY == format) && any_file_encrypted && USES_ANY_KEY(cs_data)) { /* The index variable should still be set properly. */ for (rptr = grlist, index = 0; ; rptr = rptr->fPtr, index++) { assert(NULL != rptr); if (&FILE_INFO(gv_cur_region)->fileid == &FILE_INFO(rptr->reg)->fileid) break; } index2 = *(hash2_index_array_ptr + index); null_iv = *(null_iv_array_ptr + index) == '1'; if (!IS_ENCRYPTED(cs_data->is_encrypted)) index = -1; if (!USES_NEW_KEY(cs_data)) index2 = -1; success2 = mu_extr_gblout(gl_ptr, &global_total, format, TRUE, any_file_uses_non_null_iv, index, index2, null_iv); } else success2 = mu_extr_gblout(gl_ptr, &global_total, format, any_file_encrypted, any_file_uses_non_null_iv, -1, -1, FALSE); success = success2 && success; gvnh_reg = gl_ptr->gvnh_reg; last_gvspan = gvspan; gvspan = gvnh_reg->gvspan; if (NULL != gvspan) { /* this global spans more than one region. aggregate stats across all regions */ if (last_gvspan != gvspan) MU_EXTR_STATS_INIT(spangbl_total); /* this is the FIRST spanned region. initialize spangbl_total */ MU_EXTR_STATS_ADD(spangbl_total, global_total); /* add global_total to grand_total */ } next_gl_ptr = gl_ptr->next; if ((logqualifier || mu_ctrlc_occurred) && (0 < global_total.recknt)) { ISSUE_RECORDSTAT_MSG(gl_ptr, global_total, PRINT_REG_TRUE); if ((NULL != gvspan) && ((NULL == next_gl_ptr) || (next_gl_ptr->gvnh_reg != gvnh_reg))) { /* this is the LAST spanned region. Display summary line across all spanned regions */ ISSUE_RECORDSTAT_MSG(gl_ptr, spangbl_total, PRINT_REG_FALSE); } mu_ctrlc_occurred = FALSE; } MU_EXTR_STATS_ADD(grand_total, global_total); /* add global_total to grand_total */ } assert((MV_STR == op_val.mvtype) && (MV_STR == op_pars.mvtype)); op_val.str.addr = (char *)outfilename; op_val.str.len = filename_len; op_pars.str.len = SIZEOF(no_param); op_pars.str.addr = (char *)&no_param; op_close(&op_val, &op_pars); REVERT; REVERT; if (MU_FMT_BINARY == format) { assert(NULL != hash_array_ptr); assert(NULL != null_iv_array_ptr); assert(NULL != hash2_index_array_ptr); free(hash_array_ptr); free(null_iv_array_ptr); free(hash2_index_array_ptr); } if (mu_ctrly_occurred) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_EXTRACTCTRLY); mupip_exit(ERR_MUNOFINISH); } if (0 != grand_total.recknt) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_RECORDSTAT, 6, LEN_AND_LIT("TOTAL"), &grand_total.recknt, grand_total.keylen, grand_total.datalen, grand_total.reclen); } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOSELECT); UNLINK(outfilename); mupip_exit(ERR_NOSELECT); } mupip_exit(success ? SS_NORMAL : ERR_MUNOFINISH); } fis-gtm-V7.0-005/sr_unix/mu_getkey.c0000755000032200000250000001477514342376330016217 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gtm_ctype.h" #include "util.h" #include "mupint.h" #include "mvalconv.h" #include "collseq.h" #include "muextr.h" #include "mupip_size.h" #include "mu_getkey.h" #include "gvt_inline.h" GBLDEF gv_key *mu_start_key; GBLDEF gv_key *mu_end_key; GBLDEF int mu_start_keyend; GBLDEF int mu_end_keyend; GBLREF boolean_t mu_subsc; GBLREF boolean_t mu_key; GBLREF int mu_sub_idx_st; GBLREF int mu_sub_idx_end; #define CLNUP_AND_RETURN_FALSE \ { \ GVKEY_FREE_IF_NEEDED(mu_start_key); \ GVKEY_FREE_IF_NEEDED(mu_end_key); \ return FALSE; \ } int mu_getkey(unsigned char *key_buff, int keylen) { int4 keysize; mval tmpmval; unsigned char *top, *startsrc, *src, *dest, slit[MAX_KEY_SZ + 1], *tmp; int iter; gv_key *mu_tmpkey; gd_region *reg; boolean_t nullsubs_seen; src = key_buff; if ('"' == key_buff[keylen - 1]) keylen--; if ('"' == key_buff[0]) { keylen--; src++; } if (0 == keylen) { /* null subscript specified. signal an error */ UNIX_ONLY(assert(FALSE)); /* Unix should not reach here at all. cli_parse() would have errored out */ util_out_print("%GTM-E-CLIERR, Unrecognized option : SUBSCRIPT, value expected but not found", TRUE); CLNUP_AND_RETURN_FALSE; } top = src + keylen; startsrc = src; keysize = DBKEYSIZE(MAX_KEY_SZ); assert(MUKEY_FALSE == mu_key); for (iter = 0, top = src + keylen; (iter < 2) && (src < top); iter++) { mu_tmpkey = NULL; /* GVKEY_INIT macro requires this */ GVKEY_INIT(mu_tmpkey, keysize); if (!iter) { assert(NULL == mu_start_key); mu_start_key = mu_tmpkey; /* used by CLNUP_AND_RETURN_FALSE macro */ } else { assert(NULL == mu_end_key); mu_end_key = mu_tmpkey; } nullsubs_seen = FALSE; dest = mu_tmpkey->base; if ('^' != *src++) { util_out_print("Error in SUBSCRIPT qualifier : Key does not begin with '^' at offset !UL in !AD", TRUE, src - 1 - startsrc, top - startsrc, startsrc); CLNUP_AND_RETURN_FALSE; } if (ISALPHA_ASCII(*src) || ('%' == *src)) *dest++ = *src++; else { util_out_print("Error in SUBSCRIPT qualifier : Global variable name does not begin with an alphabet" " or % at offset !UL in !AD", TRUE, src - startsrc, top - startsrc, startsrc); CLNUP_AND_RETURN_FALSE; } for ( ; ('(' != *src) && (src < top); ) { if (':' == *src) break; if (ISALNUM_ASCII(*src)) *dest++ = *src++; else { util_out_print("Error in SUBSCRIPT qualifier : Global variable name contains non-alphanumeric " "characters at offset !UL in !AD", TRUE, src - startsrc, top - startsrc, startsrc); CLNUP_AND_RETURN_FALSE; } } iter ? (mu_sub_idx_end = dest - mu_tmpkey->base) : (mu_sub_idx_st = dest - mu_tmpkey->base); *dest++ = 0; *dest = 0; mu_tmpkey->end = dest - mu_tmpkey->base; if (!iter) mu_start_keyend = mu_start_key->end; else mu_end_keyend = mu_end_key->end; if ('(' == *src) { mu_subsc = TRUE; src++; for ( ; src < top; ) { tmpmval.mvtype = MV_STR; if ('\"' != *src) { for (tmpmval.str.addr = (char *)src; (src < top) && (')' != *src) && (',' != *src); src++) { if ((*src < '0' || *src > '9') && ('-' != *src) && ('.' != *src)) { util_out_print("Error in SUBSCRIPT qualifier : Non-string subscript " "contains non-numerical characters at offset !UL in !AD", TRUE, src - startsrc, top - startsrc, startsrc); CLNUP_AND_RETURN_FALSE; } } tmpmval.str.len = INTCAST(src - (unsigned char*)tmpmval.str.addr); if (!tmpmval.str.len) { util_out_print("Error in SUBSCRIPT qualifier : Empty subscript specified at " "offset !UL in !AD", TRUE, src - startsrc, top - startsrc, startsrc); CLNUP_AND_RETURN_FALSE; } s2n(&tmpmval); tmpmval.mvtype &= MV_NUM_MASK; } else { src++; tmp = slit; for (;;) { if (src >= top) { util_out_print("Error in SUBSCRIPT qualifier : String subscript does not " "terminate with double-quote (\") character at offset !UL " "in !AD", TRUE, src - startsrc, top - startsrc, startsrc); CLNUP_AND_RETURN_FALSE; } if ('\"' == *src) if ('\"' != *++src) break; *tmp++ = *src++; } tmpmval.str.addr = (char*)slit; tmpmval.str.len = INTCAST(tmp - slit); if (!tmpmval.str.len) nullsubs_seen = TRUE; } /* We could be looking for this -subscript=... specification in one of many database files * each with a different standard null collation setting. As MUPIP INTEG switches to different * database files, it needs to recompute the subscript representation of the input subscript * if there are null subscripts in it and the regions have different standard null collation * properties. For now assume standard null collation is FALSE in all regions. And note down * if any null subscript was seen. If so do recomputation of key as db files get switched in integ. */ mval2subsc(&tmpmval, mu_tmpkey, STD_NULL_COLL_FALSE); if ((src >= top) || (',' != *src)) break; src++; } if (src >= top) { assert(src == top); util_out_print("Error in SUBSCRIPT qualifier : Empty/Incomplete subscript specified at " "offset !UL in !AD", TRUE, src - startsrc, top - startsrc, startsrc); CLNUP_AND_RETURN_FALSE; } if (')' != *src++) { util_out_print("Error in SUBSCRIPT qualifier : Subscript terminating right parentheses not found " "at offset !UL in !AD", TRUE, src - 1 - startsrc, top - startsrc, startsrc); CLNUP_AND_RETURN_FALSE; } } if (':' == *src) src++; if (MUKEY_NULLSUBS != mu_key) mu_key = (nullsubs_seen ? MUKEY_NULLSUBS : MUKEY_TRUE); } if (src < top) { assert(iter == 2); util_out_print("Error: Subscript qualifier keyrange not clear. More than two keys specified at offset !UL in !AD", TRUE, src - startsrc, top - startsrc, startsrc); CLNUP_AND_RETURN_FALSE; } return TRUE; } fis-gtm-V7.0-005/sr_unix/mu_getkey.h0000644000032200000250000000224314342376330016204 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2019-2020 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. * * * ****************************************************************/ #ifndef MUPGETKEY_H #define MUPGETKEY_H /* requires gdsroot.h */ #define MUKEY_FALSE 0 /* -subscript was NOT specified as part of mupip integ */ #define MUKEY_TRUE 1 /* -subscript was specified as part of mupip integ */ #define MUKEY_NULLSUBS 2 /* -subscript was specified as part of mupip integ AND there was at least one null subscript */ #define DUMMY_GLOBAL_VARIABLE "%D%DUMMY_VARIABLE" #define DUMMY_GLOBAL_VARIABLE_LEN SIZEOF(DUMMY_GLOBAL_VARIABLE) GBLREF gv_key *mu_start_key; GBLREF gv_key *mu_end_key; GBLREF int mu_start_keyend; GBLREF int mu_end_keyend; int mu_getkey(unsigned char *key_buff, int keylen); #endif fis-gtm-V7.0-005/sr_unix/mu_getlst.c0000755000032200000250000001771414342376330016225 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "sys/types.h" #include "gtm_stat.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "gtm_limits.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "mupipbckup.h" #include "gdscc.h" #include "gdskill.h" #include "jnl.h" #include "buddy_list.h" #include "tp.h" #include "util.h" #include "cli.h" #include "mupip_exit.h" #include "str_match.h" #include "mu_getlst.h" #include "gtmmsg.h" #include "gtm_reservedDB.h" #include "gtm_malloc.h" #include "mdq.h" #include "compiler.h" GBLDEF boolean_t is_directory; GBLDEF mstr directory; GBLREF backup_reg_list *mu_repl_inst_reg_list; GBLREF bool error_mupip; GBLREF bool in_backup; GBLREF boolean_t mu_star_specified; GBLREF boolean_t mu_region_found; GBLREF gd_addr *gd_header; GBLREF tp_region *grlist; GBLDEF usr_reg_que *usr_spec_regions = NULL; /* Queue of region(s) in user-specified order */ error_def(ERR_MUBCKNODIR); error_def(ERR_MUNOACTION); error_def(ERR_MUNODBNAME); error_def(ERR_MUPCLIERR); error_def(ERR_NOREGION); #define CHECK_IF_NOT_ABSENT(QUALIFIER) \ { \ if (CLI_ABSENT != cli_present(QUALIFIER)) \ { \ util_out_print(QUALIFIER " cannot be specified without specifying a backup region", TRUE); \ mupip_exit(ERR_MUPCLIERR); \ } \ } void mu_getlst(char *name, int4 size) { boolean_t matched, is_statsDB, is_autoDB; char *c1, *c2, *c3, *c4, rbuff[GTM_PATH_MAX], fbuff[GTM_PATH_MAX], fnbuff[GTM_PATH_MAX + 1]; gd_region *reg; tp_region *list; unsigned short flen, i, rlen; struct stat stat_buf; int rc; usr_reg_que *region_que_entry = NULL; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; mu_star_specified = FALSE; assert(size > 0); rlen = SIZEOF(rbuff); flen = SIZEOF(fbuff); is_directory = FALSE; /* Initialize the usr_spec_regions queue */ usr_spec_regions = (usr_reg_que *)malloc(SIZEOF(usr_reg_que)); dqinit(usr_spec_regions, que); if (!in_backup) { if (!cli_get_str(name, rbuff, &rlen)) mupip_exit(ERR_MUNODBNAME); for (i = 0; i < rlen; i++) /* Region names are always upper-case ASCII and thoroughly NUL terminated */ rbuff[i] = TOUPPER(rbuff[i]); for ( ; i < ARRAYSIZE(rbuff); i++) rbuff[i] = 0; } else { if (CLI_PRESENT == cli_present("REPLINSTANCE")) { /* Region corresponding to the replication instance file has been specified. */ if (0 == TREF(parms_cnt)) { /* No REG_NAME or SAVE_DIR parameter specified in command line. Disable other backup qualifiers. */ CHECK_IF_NOT_ABSENT("BKUPDBJNL"); CHECK_IF_NOT_ABSENT("BYTESTREAM"); CHECK_IF_NOT_ABSENT("COMPREHENSIVE"); CHECK_IF_NOT_ABSENT("DATABASE"); CHECK_IF_NOT_ABSENT("DBG"); CHECK_IF_NOT_ABSENT("INCREMENTAL"); CHECK_IF_NOT_ABSENT("JOURNAL"); CHECK_IF_NOT_ABSENT("NETTIMEOUT"); CHECK_IF_NOT_ABSENT("NEWJNLFILES"); CHECK_IF_NOT_ABSENT("ONLINE"); CHECK_IF_NOT_ABSENT("RECORD"); CHECK_IF_NOT_ABSENT("REPLICATION"); CHECK_IF_NOT_ABSENT("SINCE"); CHECK_IF_NOT_ABSENT("TRANSACTION"); } assert(NULL == mu_repl_inst_reg_list); if (NULL == mu_repl_inst_reg_list) mu_repl_inst_reg_list = gtm_malloc(SIZEOF(backup_reg_list)); if ((!cli_get_str("REPLINSTANCE", fbuff, &flen)) || (0 == flen)) { util_out_print("Error parsing REPLINSTANCE qualifier", TRUE); mupip_exit(ERR_MUPCLIERR); } if (FALSE == mubgetfil(mu_repl_inst_reg_list, fbuff, flen)) return; /* Do not let the db region backup destination list be affected if -replinstance had directory specified */ is_directory = FALSE; } if ((0 == TREF(parms_cnt)) && mu_repl_inst_reg_list) { /* -REPLINSTANCE was specified and no other parameters were specified. Do not bother prompting * the user to enter values for REG_NAME and SAVE_DIR parameters. */ return; } if (!cli_get_str(name, rbuff, &rlen)) mupip_exit(ERR_MUNODBNAME); for (i = 0; i < rlen; i++) /* Region names are always upper-case ASCII and thoroughly NUL terminated */ rbuff[i] = TOUPPER(rbuff[i]); for ( ; i < ARRAYSIZE(rbuff); i++) rbuff[i] = 0; flen = SIZEOF(fbuff); /* reset max_buflen to original before call to "cli_get_str" */ if ((!cli_get_str("SAVE_DIR", fbuff, &flen)) || (0 == flen)) mupip_exit(ERR_MUBCKNODIR); } for (c1 = c2 = rbuff, c3 = c4 = fbuff;;) { for (; *c2 && (*c2 != ','); c2++) /* locate a reg spec */ ; if (c2 - c1 > MAX_RN_LEN) { error_mupip = TRUE; util_out_print("!UL exceeds maximum REGION name length of !UL characters.", TRUE, c2 - c1, MAX_RN_LEN); } else { /* handle the reg spec here */ if ('*' == *c1 && (1 == c2 - c1)) mu_star_specified = TRUE; matched = FALSE; for (i = 0, reg = gd_header->regions; i < gd_header->n_regions; i++, reg++) { is_statsDB = IS_STATSDB_REG(reg); is_autoDB = IS_AUTODB_REG(reg); /* See if autoDB file actually exists to know whether to include this region in the list or not. * But a statsDB (which is also an autoDB) region does not point to the real statsdb file name. * (will be determined only when the baseDB is opened). So skip this check for statsDBs. * LOG_ERROR_FALSE usage below as we do not want error message displayed if file does not exist. */ if (!is_statsDB && is_autoDB && !mupfndfil(reg, NULL, LOG_ERROR_FALSE)) continue; /* autoDB does not exist - do not include */ if (IS_STATSDB_REGNAME(reg)) { /* This is a statsdb region. Since the statsdb file name is not determined until the * basedb region is opened, we cannot run "insert_region" on this region. Skip for now. * Caller needs to do the needful for statsdb regions since it is the one that will * do a "gvcst_init" of the baseDB region. */ continue; } if (TRUE == str_match((char *)REG_STR_LEN(reg), c1, c2 - c1)) { matched = TRUE; if (NULL == (list = insert_region(reg, &(grlist), NULL, size))) { error_mupip = TRUE; mu_region_found = FALSE; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NOREGION, 2, REG_LEN_STR(reg)); continue; } if (FALSE == usr_reg_que_checkdup(®->rname[0], reg->rname_len)) { /* Allocate memory for an entry in usr_spec_regions queue. No need to free. */ region_que_entry = (usr_reg_que *)malloc(SIZEOF(usr_reg_que)); region_que_entry->usr_reg = (char *)®->rname[0]; region_que_entry->usr_reg_len = reg->rname_len; /* Insert on the right side of the queue */ dqrins(usr_spec_regions, que, region_que_entry); } if ((FALSE == in_backup) || (0 != ((backup_reg_list *)list)->backup_file.len)) continue; if (TRUE == is_directory) mubexpfilnam(directory.addr, directory.len, (backup_reg_list *)list); else { for (; *c4 && (*c4 != ','); c4++) /* locate a file spec */ ; if (FALSE == mubgetfil((backup_reg_list *)list, c3, c4 - c3)) break; if (*c4) c3 = ++c4; else if (FALSE == is_directory) break; } } } if (!matched) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NOREGION, 2, c2 - c1, c1); mupip_exit(ERR_MUNOACTION); } } if (!*c2) break; else c1 = ++c2; } return; } static bool usr_reg_que_checkdup(unsigned char *regname, unsigned short regname_len) { usr_reg_que *iterator; dqloop(usr_spec_regions, que, iterator) { if ((iterator->usr_reg_len == regname_len) && (FALSE == memcmp(iterator->usr_reg, (char *)regname, regname_len))) return TRUE; } return FALSE; } fis-gtm-V7.0-005/sr_unix/mu_gvis.c0000755000032200000250000000205714342376330015665 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2006 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 "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "util.h" #include "format_targ_key.h" #include "mu_gvis.h" GBLREF gv_key *gv_currkey; void mu_gvis(void ) { char key_buff[MAX_ZWR_KEY_SZ], *key_end; if (gv_currkey->end) { if ((key_end = (char*)format_targ_key((uchar_ptr_t)&key_buff[0], MAX_ZWR_KEY_SZ, gv_currkey, TRUE)) == 0) key_end = &key_buff[MAX_ZWR_KEY_SZ - 1]; } else key_end = &key_buff[0]; util_out_print("!_!_Global variable : !AD", TRUE, key_end - &key_buff[0], key_buff); return; } fis-gtm-V7.0-005/sr_unix/mu_int_ch.c0000755000032200000250000000160714342376330016161 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "error.h" #include "mu_gv_cur_reg_init.h" #include "db_ipcs_reset.h" GBLREF bool region; GBLREF gd_region *gv_cur_region; CONDITION_HANDLER(mu_int_ch) { START_CH(TRUE); if (!region) { db_ipcs_reset(gv_cur_region); mu_gv_cur_reg_free(); /* mu_gv_cur_reg_init was done in mu_int_init() */ } NEXTCH; } fis-gtm-V7.0-005/sr_unix/mu_op_open.c0000644000032200000250000002626514342376330016360 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_fcntl.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_unistd.h" #include "gtm_stat.h" #include "gtm_iconv.h" #include #include #include "io.h" #include "iosp.h" #include "io_params.h" #include "cryptdef.h" #include "iotimer.h" #include "gt_timer.h" #include "copy.h" #include "iottdef.h" #include "iormdef.h" #include "mupip_io_dev_dispatch.h" #include "eintr_wrappers.h" #include "mmemory.h" #include "mu_op_open.h" #include "trans_log_name.h" #include "gtm_conv.h" #include "gtm_utf8.h" #include "gtmimagename.h" #define LOGNAME_LEN 255 GBLREF io_desc *active_device; GBLREF bool licensed; GBLREF dev_dispatch_struct io_dev_dispatch_mupip[]; GBLREF int4 lkid,lid; GBLREF mstr sys_input, sys_output; error_def(LP_NOTACQ); /* bad license */ error_def(ERR_LOGTOOLONG); error_def(ERR_SYSCALL); LITREF mstr chset_names[]; LITREF unsigned char io_params_size[]; static boolean_t mu_open_try(io_log_name *, io_log_name *, mval *, mval *); /* The third parameter is dummy to keep the inteface same as op_open */ int mu_op_open(mval *v, mval *p, mval *t, mval *mspace) { char buf1[MAX_TRANS_NAME_LEN]; /* buffer to hold translated name */ int4 stat; /* status */ io_log_name *naml; /* logical record for passed name */ io_log_name *tl; /* logical record for translated name */ mstr tn; /* translated name */ MV_FORCE_STR(v); MV_FORCE_STR(p); if (mspace) MV_FORCE_STR(mspace); assert((unsigned char)*p->str.addr < n_iops); naml = get_log_name(&v->str, INSERT); if (0 != naml->iod) tl = naml; else { # ifdef NOLICENSE licensed= TRUE ; # else CRYPT_CHKSYSTEM; if (!licensed || LP_CONFIRM(lid,lkid)==LP_NOTACQ) licensed= FALSE ; # endif switch (stat = TRANS_LOG_NAME(&v->str, &tn, &buf1[0], SIZEOF(buf1), dont_sendmsg_on_log2long)) { case SS_NORMAL: tl = get_log_name(&tn, INSERT); break; case SS_NOLOGNAM: tl = naml; break; case SS_LOG2LONG: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_LOGTOOLONG, 3, v->str.len, v->str.addr, SIZEOF(buf1) - 1); break; default: rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) stat); } } stat = mu_open_try(naml, tl, p, mspace); return (stat); } static boolean_t mu_open_try(io_log_name *naml, io_log_name *tl, mval *pp, mval *mspace) { boolean_t ichset_specified, ochset_specified, filecreated = FALSE; char *buf, namebuf[LOGNAME_LEN + 1]; d_rm_struct *d_rm; int char_or_block_special, file_des, fstat_res, oflag, p_offset, save_errno, umask_creat, umask_orig; int4 recordsize, status; io_desc *iod; mstr chset_mstr; mstr tn; /* translated name */ struct stat outbuf; unsigned char ch; char_or_block_special = FALSE; file_des = -2; oflag = 0; tn.len = tl->len; if (tn.len > LOGNAME_LEN) tn.len = LOGNAME_LEN; tn.addr = tl->dollar_io; memcpy(namebuf, tn.addr, tn.len); namebuf[tn.len] = '\0'; buf = namebuf; if (0 == naml->iod) { if (0 == tl->iod) { iod = (io_desc *)malloc(SIZEOF(io_desc)); memset((char*)iod, 0, SIZEOF(io_desc)); iod->pair.in = iod; iod->pair.out = iod; iod->trans_name = tl; iod->type = n_io_dev_types; p_offset = 0; while (iop_eol != *(pp->str.addr + p_offset)) { ch = *(pp->str.addr + p_offset++); if (iop_sequential == ch) iod->type = rm; if (IOP_VAR_SIZE == io_params_size[ch]) p_offset += (int)(unsigned char)*(pp->str.addr + p_offset) + 1; else p_offset += io_params_size[ch]; } tl->iod = iod; } else iod = tl->iod; if ((n_io_dev_types == iod->type) && mspace && mspace->str.len) iod->type = us; if (n_io_dev_types == iod->type) { if (0 == memvcmp(tn.addr, tn.len, sys_input.addr, sys_input.len)) { file_des = 0; FSTAT_FILE(file_des, &outbuf, fstat_res); if (-1 == fstat_res) { save_errno = errno; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fstat()"), CALLFROM, save_errno); } } else { if (0 == memvcmp(tn.addr, tn.len, sys_output.addr, sys_output.len)) { file_des = 1; FSTAT_FILE(file_des, &outbuf, fstat_res); if (-1 == fstat_res) { save_errno = errno; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fstat()"), CALLFROM, save_errno); } } else if (0 == memvcmp(tn.addr, tn.len, DEVNULL, 9)) iod->type = nl; else if ((-1 == Stat(buf, &outbuf)) && (n_io_dev_types == iod->type)) { if (ENOENT == errno) { iod->type = rm; filecreated = TRUE; } else { save_errno = errno; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fstat()"), CALLFROM, save_errno); } } } } if (n_io_dev_types == iod->type) { switch (outbuf.st_mode & S_IFMT) { case S_IFCHR: case S_IFBLK: char_or_block_special = TRUE; break; case S_IFIFO: iod->type = ff; break; case S_IFREG: case S_IFDIR: iod->type = rm; break; case S_IFSOCK: case 0: iod->type = ff; break; default: break; } } naml->iod = iod; } else iod = naml->iod; active_device = iod; if ((-2 == file_des) && (dev_open != iod->state) && (us != iod->type)) { oflag |= (O_RDWR | O_CREAT | O_NOCTTY); p_offset = 0; ichset_specified = ochset_specified = FALSE; while ((iop_eol != *(pp->str.addr + p_offset)) && (pp->str.len > p_offset)) { ch = *(pp->str.addr + p_offset++); assert((params)ch < (params)n_iops); assert(pp->str.len > p_offset); switch (ch) { case iop_append: if (rm == iod->type) oflag |= O_APPEND; break; case iop_contiguous: break; case iop_newversion: if ((dev_open != iod->state) && (rm == iod->type)) oflag |= O_TRUNC; break; case iop_readonly: oflag &= ~(O_RDWR | O_CREAT | O_WRONLY); oflag |= O_RDONLY; break; case iop_writeonly: oflag &= ~(O_RDWR | O_RDONLY); oflag |= O_WRONLY | O_CREAT; break; case iop_ipchset: assert(pp->str.len > (p_offset + 1)); # ifdef KEEP_zOS_EBCDIC if ( (iconv_t)0 != iod->input_conv_cd ) { ICONV_CLOSE_CD(iod->input_conv_cd); } SET_CODE_SET(iod->in_code_set, (char *)(pp->str.addr + p_offset + 1)); if (DEFAULT_CODE_SET != iod->in_code_set) ICONV_OPEN_CD(iod->input_conv_cd, (char *)(pp->str.addr + p_offset + 1), INSIDE_CH_SET); break; # endif if (gtm_utf8_mode) { chset_mstr.addr = (char *)(pp->str.addr + p_offset + 1); chset_mstr.len = (int)(unsigned char)*(pp->str.addr + p_offset); SET_ENCODING(iod->ichset, &chset_mstr); ichset_specified = TRUE; } break; case iop_opchset: assert(pp->str.len > (p_offset + 1)); # ifdef KEEP_zOS_EBCDIC if ( (iconv_t)0 != iod->output_conv_cd) { ICONV_CLOSE_CD(iod->output_conv_cd); } SET_CODE_SET(iod->out_code_set, (char *)(pp->str.addr + p_offset + 1)); if (DEFAULT_CODE_SET != iod->out_code_set) ICONV_OPEN_CD(iod->output_conv_cd, INSIDE_CH_SET, (char *)(pp->str.addr + p_offset + 1)); break; # endif if (gtm_utf8_mode) { chset_mstr.addr = (char *)(pp->str.addr + p_offset + 1); chset_mstr.len = (int)(unsigned char)*(pp->str.addr + p_offset); SET_ENCODING(iod->ochset, &chset_mstr); ochset_specified = TRUE; } break; case iop_m: case iop_utf8: case iop_utf16: case iop_utf16be: case iop_utf16le: if (gtm_utf8_mode) { iod->ichset = iod->ochset = (iop_m == ch) ? CHSET_M : (iop_utf8 == ch) ? CHSET_UTF8 : (iop_utf16 == ch) ? CHSET_UTF16 : (iop_utf16be == ch) ? CHSET_UTF16BE : CHSET_UTF16LE; ichset_specified = ochset_specified = TRUE; } break; default: break; } p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); } if (!ichset_specified) iod->ichset = (gtm_utf8_mode) ? CHSET_UTF8 : CHSET_M; if (!ochset_specified) iod->ochset = (gtm_utf8_mode) ? CHSET_UTF8 : CHSET_M; if (IS_UTF_CHSET(iod->ichset) && CHSET_UTF16 != iod->ichset) get_chset_desc(&chset_names[iod->ichset]); if (IS_UTF_CHSET(iod->ochset) && CHSET_UTF16 != iod->ochset) get_chset_desc(&chset_names[iod->ochset]); /* RW permissions for owner and others as determined by umask. */ umask_orig = umask(000); /* determine umask (destructive) */ (void)umask(umask_orig); /* reset umask */ umask_creat = 0666 & ~umask_orig; /* the check for EINTR below is valid and should not be converte d to an EINTR * wrapper macro, due to the other errno values being checked. */ while ((-1 == (file_des = OPEN3(buf, oflag, umask_creat)))) { if ( EINTR == errno || ETXTBSY == errno || ENFILE == errno || EBUSY == errno || ((mb == iod->type) && (ENXIO == errno))) continue; else break; } if (-1 == file_des) return FALSE; } #ifdef KEEP_zOS_EBCDIC SET_CODE_SET(iod->in_code_set, OUTSIDE_CH_SET); if (DEFAULT_CODE_SET != iod->in_code_set) ICONV_OPEN_CD(iod->input_conv_cd, OUTSIDE_CH_SET, INSIDE_CH_SET); SET_CODE_SET(iod->out_code_set, OUTSIDE_CH_SET); if (DEFAULT_CODE_SET != iod->out_code_set) ICONV_OPEN_CD(iod->output_conv_cd, INSIDE_CH_SET, OUTSIDE_CH_SET); #endif /* smw 99/12/18 not possible to be -1 here */ if (-1 == file_des) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("open()"), CALLFROM, save_errno); } if (n_io_dev_types == iod->type) { if (isatty(file_des)) iod->type = tt; else if (char_or_block_special && (2 < file_des)) { /* assume mag tape */ iod->type = mt; assert(FALSE); }else iod->type = rm; } assert(iod->type < n_io_dev_types); iod->disp_ptr = &io_dev_dispatch_mupip[iod->type]; active_device = iod; if (dev_never_opened == iod->state) { iod->wrap = DEFAULT_IOD_WRAP; iod->width = DEFAULT_IOD_WIDTH; iod->length = DEFAULT_IOD_LENGTH; iod->write_filter = 0; /* MUPIP should not use FILTER */ } if (dev_open != iod->state) { iod->dollar.x = 0; iod->dollar.y = 0; iod->dollar.za = 0; iod->dollar.zb[0] = 0; } iod->newly_created = filecreated; status = (iod->disp_ptr->open)(naml, pp, file_des, mspace, NO_M_TIMEOUT); if (TRUE == status) { iod->state = dev_open; if (rm == iod->type) { d_rm = (d_rm_struct *)iod->dev_sp; if (!d_rm->def_recsize && d_rm->def_width) { iod->width = d_rm->recordsize; d_rm->def_width = FALSE; } } } else if (dev_open == iod->state) iod->state = dev_closed; if (1 == file_des) iod->dollar.zeof = TRUE; active_device = 0; iod->newly_created = FALSE; if (IS_MCODE_RUNNING) return (status); return TRUE; } fis-gtm-V7.0-005/sr_unix/mu_op_open.h0000755000032200000250000000123114342376330016352 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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. * * * ****************************************************************/ #ifndef MU_OP_OPEN_INCLUDED #define MU_OP_OPEN_INCLUDED int mu_op_open(mval *v, mval *p, mval *t, mval *mspace); #endif /* MU_OP_OPEN_INCLUDED */ fis-gtm-V7.0-005/sr_unix/mu_outofband_setup.c0000755000032200000250000000133614342376330020115 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 #include "mu_outofband_setup.h" #include "mupip_ctrl.h" void mu_outofband_setup(void) { struct sigaction act; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = mupip_ctrl; sigaction(SIGINT, &act, 0); return; } fis-gtm-V7.0-005/sr_unix/mu_replpool_grab_sem.c0000644000032200000250000002517114342376330020407 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_ipc.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "gtm_inet.h" #include "gtm_fcntl.h" #include #include #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "gtmrecv.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtm_logicals.h" #include "jnl.h" #include "repl_sem.h" #include "repl_shutdcode.h" #include "io.h" #include "trans_log_name.h" #include "repl_instance.h" #include "mu_gv_cur_reg_init.h" #include "gtmmsg.h" #include "gtm_sem.h" #include "mu_rndwn_replpool.h" #include "ftok_sems.h" #include "util.h" #include "anticipatory_freeze.h" #define REMOVE_SEM_SET(SEM_CREATED, POOL_TYPE) /* Assumes 'sem_created_ptr' is is already declared */ \ { \ if (SEM_CREATED) \ remove_sem_set(JNLPOOL_SEGMENT == POOL_TYPE ? SOURCE : RECV); \ *sem_created_ptr = SEM_CREATED = FALSE; \ } #define DO_CLNUP_AND_RETURN(SAVE_ERRNO, SEM_CREATED, POOL_TYPE, INSTFILENAME, INSTFILELEN, SEM_ID, FAILED_OP) \ { \ REMOVE_SEM_SET(SEM_CREATED, REPLPOOL_ID); \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_REPLACCSEM, 3, SEM_ID, INSTFILELEN, INSTFILENAME); \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT(FAILED_OP), CALLFROM, SAVE_ERRNO); \ return -1; \ } #define RELEASE_ALREADY_HELD_SEMAPHORE(SEM_SET, SEM_NUM) \ { \ int status, lcl_save_errno; \ \ assert(holds_sem[SEM_SET][SEM_NUM]); \ status = rel_sem_immediate(SEM_SET, SEM_NUM); \ if (-1 == status) \ { \ lcl_save_errno = errno; \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("semop()"), CALLFROM, lcl_save_errno); \ } \ } #define DECR_ALREADY_INCREMENTED_SEMAPHORE(SEM_SET, SEM_NUM) \ { \ int status, lcl_save_errno; \ \ assert(holds_sem[SEM_SET][SEM_NUM]); \ status = decr_sem(SEM_SET, SEM_NUM); \ if (-1 == status) \ { \ lcl_save_errno = errno; \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("semop()"), CALLFROM, lcl_save_errno); \ } \ } GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF recvpool_addrs recvpool; GBLREF gd_region *gv_cur_region; GBLREF jnl_gbls_t jgbl; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; GBLREF boolean_t argumentless_rundown; error_def(ERR_JNLPOOLSETUP); error_def(ERR_RECVPOOLSETUP); error_def(ERR_REPLACCSEM); error_def(ERR_REPLFTOKSEM); error_def(ERR_SYSCALL); error_def(ERR_TEXT); /* * Description: * Grab ftok semaphore on replication instance file * Grab all replication semaphores for the instance (both jnlpool and recvpool) * Release ftok semaphore * Parameters: * Return Value: SS_NORMAL, if succsessful * -1, if fails. */ int mu_replpool_grab_sem(repl_inst_hdr_ptr_t repl_inst_filehdr, char pool_type, boolean_t *sem_created_ptr, boolean_t immediate) { int status, save_errno, sem_id, semval, semnum, instfilelen; time_t semctime; boolean_t sem_created, force_increment; char *instfilename; union semun semarg; struct semid_ds semstat; gd_region *replreg; DEBUG_ONLY(unix_db_info *udi;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; *sem_created_ptr = sem_created = FALSE; /* assume semaphore not created by default */ assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); /* ROLLBACK -FORWARD should not call this function */ force_increment = (jgbl.onlnrlbk || (!jgbl.mur_rollback && !argumentless_rundown && INST_FREEZE_ON_ERROR_POLICY)); /* First ensure that the caller has grabbed the ftok semaphore on the replication instance file */ assert(!jnlpool || ((NULL != jnlpool->jnlpool_dummy_reg) && (jnlpool->jnlpool_dummy_reg == recvpool.recvpool_dummy_reg))); if (jnlpool && (NULL != jnlpool->jnlpool_dummy_reg)) replreg = jnlpool->jnlpool_dummy_reg; else replreg = recvpool.recvpool_dummy_reg; assert(NULL != replreg); DEBUG_ONLY(udi = FILE_INFO(replreg)); assert(udi->grabbed_ftok_sem); /* the caller should have grabbed ftok semaphore */ instfilename = (char *)replreg->dyn.addr->fname; instfilelen = replreg->dyn.addr->fname_len; assert((NULL != instfilename) && (0 != instfilelen) && ('\0' == instfilename[instfilelen])); assert((JNLPOOL_SEGMENT == pool_type) || (RECVPOOL_SEGMENT == pool_type)); if (JNLPOOL_SEGMENT == pool_type) { sem_id = repl_inst_filehdr->jnlpool_semid; semctime = repl_inst_filehdr->jnlpool_semid_ctime; } else { sem_id = repl_inst_filehdr->recvpool_semid; semctime = repl_inst_filehdr->recvpool_semid_ctime; } semarg.buf = &semstat; if ((INVALID_SEMID == sem_id) || (-1 == semctl(sem_id, 0, IPC_STAT, semarg)) || (semctime != semarg.buf->sem_ctime)) { /* Semaphore doesn't exist. Create new ones */ if (JNLPOOL_SEGMENT == pool_type) { repl_inst_filehdr->jnlpool_semid = INVALID_SEMID; /* Invalidate previous semid in file header */ repl_inst_filehdr->jnlpool_semid_ctime = 0; sem_id = init_sem_set_source(IPC_PRIVATE, NUM_SRC_SEMS, RWDALL | IPC_CREAT); } else { repl_inst_filehdr->recvpool_semid = INVALID_SEMID; /* Invalidate previous semid in file header */ repl_inst_filehdr->recvpool_semid_ctime = 0; sem_id = init_sem_set_recvr(IPC_PRIVATE, NUM_RECV_SEMS, RWDALL | IPC_CREAT); } if (INVALID_SEMID == sem_id) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semget()"); } *sem_created_ptr = sem_created = TRUE; semarg.val = GTM_ID; if (-1 == semctl(sem_id, SOURCE_ID_SEM, SETVAL, semarg)) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semctl()"); } semarg.buf = &semstat; if (-1 == semctl(sem_id, DB_CONTROL_SEM, IPC_STAT, semarg)) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semctl()"); } semctime = semarg.buf->sem_ctime; } else if (JNLPOOL_SEGMENT == pool_type) set_sem_set_src(sem_id); else set_sem_set_recvr(sem_id); /* Semaphores are setup. Grab them */ if (JNLPOOL_SEGMENT == pool_type) { assert(!jgbl.onlnrlbk || !immediate); if (!immediate) status = grab_sem(SOURCE, JNL_POOL_ACCESS_SEM); /* want to wait in case of online rollback */ else status = grab_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); if (SS_NORMAL != status) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semop()"); } semval = semctl(sem_id, SRC_SERV_COUNT_SEM, GETVAL); if (-1 == semval) { save_errno = errno; RELEASE_ALREADY_HELD_SEMAPHORE(SOURCE, JNL_POOL_ACCESS_SEM); DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semctl()"); } if (0 < semval && !force_increment) { RELEASE_ALREADY_HELD_SEMAPHORE(SOURCE, JNL_POOL_ACCESS_SEM); util_out_print("Replpool semaphore (id = !UL) for replication instance !AD is in use by another process.", TRUE, sem_id, instfilelen, instfilename); REMOVE_SEM_SET(sem_created, pool_type); return -1; } status = incr_sem(SOURCE, SRC_SERV_COUNT_SEM); if (SS_NORMAL != status) { save_errno = errno; RELEASE_ALREADY_HELD_SEMAPHORE(SOURCE, JNL_POOL_ACCESS_SEM); assert(FALSE); /* we hold it, so there is no reason why we cannot release it */ DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semop()"); } holds_sem[SOURCE][SRC_SERV_COUNT_SEM] = TRUE; repl_inst_filehdr->jnlpool_semid = sem_id; repl_inst_filehdr->jnlpool_semid_ctime = semctime; } else { if (!immediate) status = grab_sem(RECV, RECV_POOL_ACCESS_SEM); else status = grab_sem_immediate(RECV, RECV_POOL_ACCESS_SEM); if (SS_NORMAL != status) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semop()"); } if (!immediate) status = grab_sem(RECV, RECV_SERV_OPTIONS_SEM); else status = grab_sem_immediate(RECV, RECV_SERV_OPTIONS_SEM); if (SS_NORMAL != status) { save_errno = errno; RELEASE_ALREADY_HELD_SEMAPHORE(RECV, RECV_POOL_ACCESS_SEM); DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semop()"); } assert(RECV_SERV_COUNT_SEM + 1 == UPD_PROC_COUNT_SEM); for (semnum = RECV_SERV_COUNT_SEM; semnum <= UPD_PROC_COUNT_SEM; semnum++) { semval = semctl(sem_id, semnum, GETVAL); if (-1 == semval) { save_errno = errno; RELEASE_ALREADY_HELD_SEMAPHORE(RECV, RECV_POOL_ACCESS_SEM); RELEASE_ALREADY_HELD_SEMAPHORE(RECV, RECV_SERV_OPTIONS_SEM); DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semctl()"); } if ((0 < semval) && !jgbl.onlnrlbk) { RELEASE_ALREADY_HELD_SEMAPHORE(RECV, RECV_POOL_ACCESS_SEM); RELEASE_ALREADY_HELD_SEMAPHORE(RECV, RECV_SERV_OPTIONS_SEM); if (UPD_PROC_COUNT_SEM == semnum) { /* Need to decrement RECV_SERV_COUNT_SEM before returning to the caller since we have * incremented it before */ DECR_ALREADY_INCREMENTED_SEMAPHORE(RECV, RECV_SERV_COUNT_SEM); } util_out_print("Replpool semaphore (id = !UL) for replication instance !AD is in use by " "another process.", TRUE, sem_id, instfilelen, instfilename); REMOVE_SEM_SET(sem_created, pool_type); return -1; } status = incr_sem(RECV, semnum); if (SS_NORMAL != status) { save_errno = errno; if (UPD_PROC_COUNT_SEM == semnum) { /* Need to decrement RECV_SERV_COUNT_SEM before returning to the caller since we have * incremented it before */ DECR_ALREADY_INCREMENTED_SEMAPHORE(RECV, RECV_SERV_COUNT_SEM); } DO_CLNUP_AND_RETURN(save_errno, sem_created, pool_type, instfilename, instfilelen, sem_id, "semop()"); } holds_sem[RECV][semnum] = TRUE; } repl_inst_filehdr->recvpool_semid = sem_id; repl_inst_filehdr->recvpool_semid_ctime = semctime; } return SS_NORMAL; } fis-gtm-V7.0-005/sr_unix/mu_replpool_release_sem.c0000644000032200000250000001742614342376330021120 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2017 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_ipc.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "gtm_inet.h" #include "gtm_fcntl.h" #include #include #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "gtmrecv.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtm_logicals.h" #include "jnl.h" #include "repl_sem.h" #include "repl_shutdcode.h" #include "io.h" #include "trans_log_name.h" #include "repl_instance.h" #include "gtmmsg.h" #include "gtm_sem.h" #include "mu_rndwn_replpool.h" #include "ftok_sems.h" #include "anticipatory_freeze.h" #define DO_CLNUP_AND_RETURN(SAVE_ERRNO, INSTFILENAME, INSTFILELEN, SEM_ID, FAILED_OP) \ { \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_REPLACCSEM, 3, SEM_ID, INSTFILELEN, INSTFILENAME); \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT(FAILED_OP), CALLFROM, SAVE_ERRNO); \ return -1; \ } GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF recvpool_addrs recvpool; GBLREF gd_region *gv_cur_region; GBLREF jnl_gbls_t jgbl; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; GBLREF boolean_t argumentless_rundown; error_def(ERR_REPLACCSEM); int mu_replpool_release_sem(repl_inst_hdr_ptr_t repl_inst_filehdr, char pool_type, boolean_t remove_sem) { int save_errno, instfilelen, status, sem_id, semval; uint4 semnum; char *instfilename; gd_region *replreg; # ifdef DEBUG unix_db_info *udi; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # endif assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); /* ROLLBACK -FORWARD should not call this function */ assert(!jnlpool || (NULL != jnlpool->jnlpool_dummy_reg) && (jnlpool->jnlpool_dummy_reg == recvpool.recvpool_dummy_reg)); if (jnlpool && (NULL != jnlpool->jnlpool_dummy_reg)) replreg = jnlpool->jnlpool_dummy_reg; else replreg = recvpool.recvpool_dummy_reg; assert(NULL != replreg); DEBUG_ONLY(udi = FILE_INFO(replreg)); assert(udi->grabbed_ftok_sem || jgbl.mur_rollback); /* Rollback already holds standalone access so no need for ftok lock */ instfilename = (char *)replreg->dyn.addr->fname; instfilelen = replreg->dyn.addr->fname_len; assert((NULL != instfilename) && (0 != instfilelen) && ('\0' == instfilename[instfilelen])); assert((JNLPOOL_SEGMENT == pool_type) || (RECVPOOL_SEGMENT == pool_type)); if (JNLPOOL_SEGMENT == pool_type) { sem_id = repl_inst_filehdr->jnlpool_semid; semval = semctl(sem_id, SRC_SERV_COUNT_SEM, GETVAL); /* mu_replpool_grab_sem always increments the counter semaphore. So, it should be 1 at this point. In addition, * if not ONLINE ROLLBACK or RUNDOWN with Anticipatory Freeze, we wait for the counter to become zero before * grabbing it. The only exception where semval can be greater than 1 is if we are ONLINE ROLLBACK or RUNDOWN -REG * with anticipatory freeze scheme in effect. */ assert((1 == semval) || ((1 <= semval) && (jgbl.onlnrlbk || (!jgbl.mur_rollback && !argumentless_rundown && INST_FREEZE_ON_ERROR_POLICY)))); remove_sem &= (1 == semval); /* we can remove the sem if the caller intends to and the counter semaphore is 1 */ if (0 < semval) { status = decr_sem(SOURCE, SRC_SERV_COUNT_SEM); if (SS_NORMAL != status) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, instfilename, instfilelen, sem_id, "semop()"); } } else if (-1 == semval) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, instfilename, instfilelen, sem_id, "semctl()"); } holds_sem[SOURCE][SRC_SERV_COUNT_SEM] = FALSE; assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); assert(1 == semctl(sem_id, JNL_POOL_ACCESS_SEM, GETVAL)); /* we hold the access control semaphore */ status = rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); assert(SS_NORMAL == status); /* We hold it. So, we should be able to release it */ holds_sem[SOURCE][JNL_POOL_ACCESS_SEM] = FALSE; /* Now that we have released the access control semaphore, see if we can remove the semaphore altogether from the * system. For this, we should be holding the FTOK on the replication instance AND the counter semaphore should be * 0. If we are called from mu_rndwn_repl_instance, we are guaranteed we will be holding the FTOK. But, if we are * ROLLBACK (online or noonline), we need not hold the FTOK, if somebody else (possibly a source or receiver server * startup command) is holding it. In that case, we don't remove the semaphore, because any process that is waiting * on the access control will now get an EIDRM error which is NOT user friendly. So, just release the access * control and let the waiting process do the clean up. */ if (remove_sem) { status = remove_sem_set(SOURCE); if (SS_NORMAL != status) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, instfilename, instfilelen, sem_id, "sem_rmid()"); } repl_inst_filehdr->jnlpool_semid = INVALID_SEMID; repl_inst_filehdr->jnlpool_semid_ctime = 0; } } else { sem_id = repl_inst_filehdr->recvpool_semid; for (semnum = RECV_SERV_COUNT_SEM; semnum <= UPD_PROC_COUNT_SEM; semnum++) { semval = semctl(sem_id, semnum, GETVAL); assert((1 == semval) || jgbl.onlnrlbk); remove_sem &= (1 == semval); if (0 < semval) { status = decr_sem(RECV, semnum); if (SS_NORMAL != status) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, instfilename, instfilelen, sem_id, "semop()"); } } else if (-1 == semval) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, instfilename, instfilelen, sem_id, "semctl()"); } holds_sem[RECV][semnum] = FALSE; } assert(holds_sem[RECV][RECV_POOL_ACCESS_SEM] && holds_sem[RECV][RECV_SERV_OPTIONS_SEM]); assert(1 == semctl(sem_id, RECV_POOL_ACCESS_SEM, GETVAL) && (1 == semctl(sem_id, RECV_SERV_OPTIONS_SEM, GETVAL))); status = rel_sem_immediate(RECV, RECV_SERV_OPTIONS_SEM); assert(SS_NORMAL == status); /* We hold it. So, we should be able to release it */ status = rel_sem_immediate(RECV, RECV_POOL_ACCESS_SEM); assert(SS_NORMAL == status); /* We hold it. So, we should be able to release it */ holds_sem[RECV][RECV_POOL_ACCESS_SEM] = FALSE; holds_sem[RECV][RECV_SERV_OPTIONS_SEM] = FALSE; /* Now that we have released the access control semaphore, see if we can remove the semaphore altogether from the * system. For this, we should be holding the FTOK on the replication instance AND the counter semaphore should be * 0. If we are called from mu_rndwn_repl_instance, we are guaranteed we will be holding the FTOK. But, if we are * ROLLBACK (online or noonline), we need not hold the FTOK, if somebody else (possibly a source or receiver server * startup command) is holding it. In that case, we don't remove the semaphore, because any process that is waiting * on the access control will now get an EIDRM error which is NOT user friendly. So, just release the access * control and let the waiting process do the clean up. */ if (remove_sem) { status = remove_sem_set(RECV); if (SS_NORMAL != status) { save_errno = errno; DO_CLNUP_AND_RETURN(save_errno, instfilename, instfilelen, sem_id, "sem_rmid()"); } repl_inst_filehdr->recvpool_semid = INVALID_SEMID; repl_inst_filehdr->recvpool_semid_ctime = 0; } } return SS_NORMAL; } fis-gtm-V7.0-005/sr_unix/mu_rndwn_all.c0000755000032200000250000006304114342376330016675 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 #include #include #include "gtm_ipc.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_inet.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_sem.h" #include "gtm_stat.h" #include "gtm_stdio.h" #include "gtmio.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "db_header_conversion.h" #include "filestruct.h" #include "iosp.h" #include "mutex.h" #include "jnl.h" #include "repl_sem.h" #include "eintr_wrappers.h" #include "mu_rndwn_file.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" #include "gtm_logicals.h" #include "min_max.h" #include "util.h" #include "mu_rndwn_replpool.h" #include "mu_rndwn_all.h" #include "dbfilop.h" #include "ipcrmid.h" #include "mu_gv_cur_reg_init.h" #include "gtmmsg.h" #include "cliif.h" #include "mu_rndwn_repl_instance.h" #include "send_msg.h" #include "do_shmat.h" /* for do_shmat() prototype */ #include "shmpool.h" /* Needed for the shmpool structures */ #include "error.h" #include "db_snapshot.h" #define PRINT_AND_SEND_SHMREMOVED_MSG(MSGBUFF, FNAME_LEN, FNAME, SHMID) \ MBSTART { \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), \ ERR_SHMREMOVED, 3, SHMID, FNAME_LEN, FNAME); \ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), \ ERR_SHMREMOVED, 3, SHMID, FNAME_LEN, FNAME); \ } MBEND #define PRINT_AND_SEND_REPLPOOL_FAILURE_MSG(MSGBUFF, REPLPOOL_ID, SHMID) \ MBSTART { \ int msgid; \ unsigned char ipcs_buff[MAX_IPCS_ID_BUF], *ipcs_ptr; \ \ ipcs_ptr = i2asc(ipcs_buff, SHMID); \ *ipcs_ptr = '\0'; \ msgid = (JNLPOOL_SEGMENT == REPLPOOL_ID->pool_type) ? ERR_MUJPOOLRNDWNFL : ERR_MURPOOLRNDWNFL; \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), \ msgid, 4, LEN_AND_STR(ipcs_buff), LEN_AND_STR(REPLPOOL_ID->instfilename)); \ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), msgid, 4, LEN_AND_STR(ipcs_buff), \ LEN_AND_STR(REPLPOOL_ID->instfilename)); \ } MBEND #define PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(MSGBUFF, FNAME, SHMID) \ MBSTART { \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), ERR_MUFILRNDWNFL2, 3, \ SHMID, LEN_AND_STR(FNAME)); \ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_TEXT, 2, LEN_AND_STR(MSGBUFF), ERR_MUFILRNDWNFL2, 3, \ SHMID, LEN_AND_STR(FNAME)); \ } MBEND GBLREF gd_region *gv_cur_region; GBLREF mval dollar_zdir; GBLREF semid_queue_elem *keep_semids; GBLREF uid_t user_id; GBLREF uint4 process_id; LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; error_def(ERR_DBFILERR); error_def(ERR_MUFILRNDWNFL2); error_def(ERR_MUFILRNDWNSUC); error_def(ERR_MUJPOOLRNDWNFL); error_def(ERR_MUJPOOLRNDWNSUC); error_def(ERR_MUNOTALLSEC); error_def(ERR_MURNDWNARGLESS); error_def(ERR_MURPOOLRNDWNFL); error_def(ERR_MURPOOLRNDWNSUC); error_def(ERR_SEMREMOVED); error_def(ERR_SHMREMOVED); error_def(ERR_SYSCALL); error_def(ERR_TEXT); STATICFNDCL boolean_t validate_db_shm_entry(shm_parms *parm_buff, char *fname, int *tmp_exit_status); STATICFNDCL boolean_t validate_replpool_shm_entry(shm_parms *parm_buff, replpool_id_ptr_t replpool_id, int *tmp_exit_status); STATICFNDCL shm_parms *get_shm_parm(char *entry); STATICFNDCL char *parse_shm_entry(char *entry, int which_field); STATICFNDCL void mu_rndwn_all_helper(shm_parms *parm_buff, char *fname, int *exit_status, int *tmp_exit_status); STATICDEF boolean_t mu_rndwn_all_helper_error = FALSE; /* This condition handler is necessary so the argumentless "mupip rundown" does not terminate in case of an error * while processing one ipc. Instead it moves on to the next ipc. The only exception is fatal errors (SEVERE) * where it might not be safe to continue processing so we transfer control to a higher level condition handler. */ CONDITION_HANDLER(mu_rndwn_all_helper_ch) { START_CH(TRUE); mu_rndwn_all_helper_error = TRUE; PRN_ERROR; if (SEVERITY == SEVERE) { NEXTCH; } else UNWIND(NULL, NULL); } STATICFNDEF void mu_rndwn_all_helper(shm_parms *parm_buff, char *fname, int *exit_status, int *tmp_exit_status) { replpool_identifier replpool_id; boolean_t ret_status, jnlpool_sem_created; unsigned char ipcs_buff[MAX_IPCS_ID_BUF], *ipcs_ptr; ESTABLISH(mu_rndwn_all_helper_ch); if (validate_db_shm_entry(parm_buff, fname, tmp_exit_status)) { if (SS_NORMAL == *tmp_exit_status) { /* shm still exists */ mu_gv_cur_reg_init(); gv_cur_region->dyn.addr->fname_len = strlen(fname); STRNCPY_STR(gv_cur_region->dyn.addr->fname, fname, gv_cur_region->dyn.addr->fname_len); if (mu_rndwn_file(gv_cur_region, FALSE)) { /* If this is a statsdb, "mu_rundwn_file" would have invoked rundown on basedb which would * in turn invoke rundown on the statsdb. The basedb rundown would print the MUFILRNDWNSUC * message if needed. So skip double-printing it here. */ if (!IS_RDBF_STATSDB(gv_cur_region)) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_MUFILRNDWNSUC, 2, DB_LEN_STR(gv_cur_region)); } else { /* Save semid so that it will not be removed by mu_rndwn_sem_all() */ add_to_semids_list(FILE_INFO(gv_cur_region)->semid); *exit_status = ERR_MUNOTALLSEC; } mu_gv_cur_reg_free(); } else { /* shm has been cleaned up by "validate_db_shm_entry" so no need of any more cleanup here */ assert(ERR_SHMREMOVED == *tmp_exit_status); *tmp_exit_status = SS_NORMAL; /* reset tmp_exit_status for below logic to treat this as normal */ } } else if ((SS_NORMAL == *tmp_exit_status) && validate_replpool_shm_entry(parm_buff, (replpool_id_ptr_t)&replpool_id, tmp_exit_status)) { if (SS_NORMAL == *tmp_exit_status) { assert(JNLPOOL_SEGMENT == replpool_id.pool_type || RECVPOOL_SEGMENT == replpool_id.pool_type); ret_status = mu_rndwn_repl_instance(&replpool_id, TRUE, FALSE, &jnlpool_sem_created); ipcs_ptr = i2asc((uchar_ptr_t)ipcs_buff, parm_buff->shmid); *ipcs_ptr = '\0'; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) (JNLPOOL_SEGMENT == replpool_id.pool_type) ? (ret_status ? ERR_MUJPOOLRNDWNSUC : ERR_MUJPOOLRNDWNFL) : (ret_status ? ERR_MURPOOLRNDWNSUC : ERR_MURPOOLRNDWNFL), 4, LEN_AND_STR(ipcs_buff), LEN_AND_STR(replpool_id.instfilename)); if (!ret_status) *exit_status = ERR_MUNOTALLSEC; } else { /* shm has been cleaned up by "validate_replpool_shm_entry" so no need of any more cleanup here */ assert(ERR_SHMREMOVED == *tmp_exit_status); *tmp_exit_status = SS_NORMAL; /* reset tmp_exit_status for below logic to treat this as normal */ } } REVERT; } int mu_rndwn_all(void) { int save_errno, fname_len, exit_status = SS_NORMAL, shmid, tmp_exit_status; char entry[MAX_ENTRY_LEN]; FILE *pf; char *fname, *fgets_res; shm_parms *parm_buff; /* Record the start of an argumentless MUPIP RUNDOWN with current directory info. Useful for debugging purposes */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MURNDWNARGLESS, 4, process_id, user_id, dollar_zdir.str.len, dollar_zdir.str.addr); if (NULL == (pf = POPEN(IPCS_CMD_STR ,"r"))) { save_errno = errno; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("POPEN()"), CALLFROM, save_errno); return ERR_MUNOTALLSEC; } fname = (char *)malloc(MAX_FN_LEN + 1); while (NULL != (FGETS(entry, SIZEOF(entry), pf, fgets_res)) && entry[0] != '\n') { tmp_exit_status = SS_NORMAL; parm_buff = get_shm_parm(entry); if (NULL == parm_buff) { exit_status = ERR_MUNOTALLSEC; continue; } mu_rndwn_all_helper(parm_buff, fname, &exit_status, &tmp_exit_status); if ((SS_NORMAL == exit_status) && (SS_NORMAL != tmp_exit_status)) exit_status = tmp_exit_status; if (mu_rndwn_all_helper_error) { /* Encountered a runtime error while processing this ipc. Make sure we return with * MUNOTALLSEC and reset this static variable before starting processing on next ipc. */ mu_rndwn_all_helper_error = FALSE; if (SS_NORMAL == exit_status) exit_status = ERR_MUNOTALLSEC; } if (NULL != parm_buff) free(parm_buff); } pclose(pf); free(fname); return exit_status; } /* Takes an entry from 'ipcs -m' and checks for its validity to be a GT.M db segment. * Returns TRUE if the shared memory segment is a valid GT.M db segment * (based on a check on some fields in the shared memory) else FALSE. * If the segment belongs to GT.M it returns the database file name by the second argument. * Sets exit_stat to ERR_MUNOTALLSEC if appropriate. */ boolean_t validate_db_shm_entry(shm_parms *parm_buff, char *fname, int *exit_stat) { boolean_t remove_shmid; file_control *fc; int fname_len, save_errno, status, shmid; node_local_ptr_t nl_addr; sm_uc_ptr_t start_addr; struct stat st_buff; struct shmid_ds shmstat; sgmnt_data tsd; unix_db_info *udi; char msgbuff[OUT_BUFF_SIZE]; if (NULL == parm_buff) return FALSE; /* check for the bare minimum size of the shared memory segment that we expect * (with no fileheader related information at hand) */ if (MIN_NODE_LOCAL_SPACE + SHMPOOL_SECTION_SIZE > parm_buff->sgmnt_siz) return FALSE; if (IPC_PRIVATE != parm_buff->key) return FALSE; shmid = parm_buff->shmid; /* we do not need to lock the shm for reading the rundown information as * the other rundowns (if any) can also be allowed to share reading the * same info concurrently. */ if (-1 == (sm_long_t)(start_addr = (sm_uc_ptr_t) do_shmat(shmid, 0, SHM_RND))) return FALSE; nl_addr = (node_local_ptr_t)start_addr; memcpy(fname, nl_addr->fname, MAX_FN_LEN + 1); fname[MAX_FN_LEN] = '\0'; /* make sure the fname is null terminated */ fname_len = STRLEN(fname); msgbuff[0] = '\0'; /* memcmp returns 0 on a match, so use && to see if both return a non-0 value meaning it isn't one of the expected labels */ if (memcmp(nl_addr->label, GDS_LABEL, GDS_LABEL_SZ - 1) && memcmp(nl_addr->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) { if (!memcmp(nl_addr->label, GDS_LABEL, GDS_LABEL_SZ - 3)) { util_out_print("Cannot rundown shmid = !UL for database !AD as it has format !AD " "but this mupip uses formats !AD and !AD", TRUE, shmid, fname_len, fname, GDS_LABEL_SZ - 1, nl_addr->label, GDS_LABEL_SZ - 1, GDS_LABEL, GDS_LABEL_SZ - 1, V6_GDS_LABEL); *exit_stat = ERR_MUNOTALLSEC; } shmdt((void *)start_addr); return FALSE; } if (-1 == shmctl(shmid, IPC_STAT, &shmstat)) { save_errno = errno; assert(FALSE);/* we were able to attach to this shmid before so should be able to get stats on it */ util_out_print("!AD -> Error with shmctl for shmid = !UL", TRUE, fname_len, fname, shmid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = FALSE; /* Check if db filename reported in shared memory still exists. If not, clean this shared memory section * without even invoking "mu_rndwn_file" as that expects the db file to exist. Same case if shared memory * points back to a database whose file header does not have this shmid. */ if (-1 == Stat(fname, &st_buff)) { if (ENOENT == errno) { SNPRINTF(msgbuff, OUT_BUFF_SIZE, "File %s does not exist", fname); if (1 < shmstat.shm_nattch) { PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(msgbuff, fname, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = TRUE; } else { /* Stat errored out e.g. due to file permissions. Log that */ save_errno = errno; util_out_print("Cannot rundown shmid !UL for database file !AD as stat() on the file" " returned the following error", TRUE, shmid, fname_len, fname); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } } else { mu_gv_cur_reg_init(); gv_cur_region->dyn.addr->fname_len = strlen(fname); STRNCPY_STR(gv_cur_region->dyn.addr->fname, fname, gv_cur_region->dyn.addr->fname_len); fc = gv_cur_region->dyn.addr->file_cntl; fc->op = FC_OPEN; status = dbfilop(fc); if (SS_NORMAL != status) { util_out_print("!AD -> Error with dbfilop for shmid = !UL", TRUE, fname_len, fname, shmid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) status, 2, DB_LEN_STR(gv_cur_region), errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } udi = FILE_INFO(gv_cur_region); assert(!udi->fd_opened_with_o_direct); DB_LSEEKREAD(udi, udi->fd, 0, &tsd, SIZEOF(sgmnt_data), status); if (0 == memcmp(tsd.label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_upconv(&tsd); if (0 != status) { save_errno = errno; util_out_print("!AD -> Error with LSEEKREAD for shmid = !UL", TRUE, fname_len, fname, shmid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } mu_gv_cur_reg_free(); if (tsd.shmid != shmid) { if (tsd.read_only) return TRUE; SNPRINTF(msgbuff, OUT_BUFF_SIZE, "Shared memory ID (%d) in the DB file header does not match with the one" " reported by \"ipcs\" command (%d)", tsd.shmid, shmid); if (1 < shmstat.shm_nattch) { PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(msgbuff, fname, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = TRUE; } else if (tsd.gt_shm_ctime.ctime != shmstat.shm_ctime) { SNPRINTF(msgbuff, OUT_BUFF_SIZE, "Shared memory creation time in the DB file header does not match with" " the one reported by shmctl"); if (1 < shmstat.shm_nattch) { PRINT_AND_SEND_DBRNDWN_FAILURE_MSG(msgbuff, fname, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = TRUE; } } shmdt((void *)start_addr); if (remove_shmid) { assert('\0' != msgbuff[0]); if (0 != shm_rmid(shmid)) { save_errno = errno; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_DBFILERR, 2, fname_len, fname, ERR_TEXT, 2, RTS_ERROR_TEXT("Error removing shared memory")); util_out_print("!AD -> Error removing shared memory for shmid = !UL", TRUE, fname_len, fname, shmid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; return FALSE; } PRINT_AND_SEND_SHMREMOVED_MSG(msgbuff, fname_len, fname, shmid); *exit_stat = ERR_SHMREMOVED; } else *exit_stat = SS_NORMAL; return TRUE; } /* Takes an entry from 'ipcs -am' and checks for its validity to be a GT.M replication segment. * Returns TRUE if the shared memory segment is a valid GT.M replication segment * (based on a check on some fields in the shared memory) else FALSE. * If the segment belongs to GT.M, it returns the replication id of the segment * by the second argument. * Sets exit_stat to ERR_MUNOTALLSEC if appropriate. */ boolean_t validate_replpool_shm_entry(shm_parms *parm_buff, replpool_id_ptr_t replpool_id, int *exit_stat) { boolean_t remove_shmid, jnlpool_segment; int fd; repl_inst_hdr repl_instance; sm_uc_ptr_t start_addr; int save_errno, status, shmid; struct shmid_ds shmstat; char msgbuff[OUT_BUFF_SIZE], *instfilename; if (NULL == parm_buff) return FALSE; /* Check for the bare minimum size of the replic shared segment that we expect */ /* if (parm_buff->sgmnt_siz < (SIZEOF(replpool_identifier) + MIN(MIN_JNLPOOL_SIZE, MIN_RECVPOOL_SIZE))) */ if (parm_buff->sgmnt_siz < MIN(MIN_JNLPOOL_SIZE, MIN_RECVPOOL_SIZE)) return FALSE; if (IPC_PRIVATE != parm_buff->key) return FALSE; shmid = parm_buff->shmid; /* we do not need to lock the shm for reading the rundown information as * the other rundowns (if any) can also be allowed to share reading the * same info concurrently. */ if (-1 == (sm_long_t)(start_addr = (sm_uc_ptr_t) do_shmat(shmid, 0, SHM_RND))) return FALSE; memcpy((void *)replpool_id, (void *)start_addr, SIZEOF(replpool_identifier)); instfilename = replpool_id->instfilename; /* Even though we could be looking at a replication pool structure that has been created by an older version * or newer version of GT.M, the format of the "replpool_identifier" structure is expected to be the same * across all versions so we can safely dereference the "label" and "instfilename" fields in order to generate * user-friendly error messages. Asserts for the layout are in "mu_rndwn_repl_instance" (not here) with a * comment there as to why that location was chosen. */ if (memcmp(replpool_id->label, GDS_RPL_LABEL, GDS_LABEL_SZ - 1)) { if (!memcmp(replpool_id->label, GDS_RPL_LABEL, GDS_LABEL_SZ - 3)) { util_out_print("Cannot rundown replpool shmid = !UL as it has format !AD " "created by !AD but this mupip is version and uses format !AD", TRUE, shmid, GDS_LABEL_SZ - 1, replpool_id->label, LEN_AND_STR(replpool_id->now_running), gtm_release_name_len, gtm_release_name, GDS_LABEL_SZ - 1, GDS_RPL_LABEL); *exit_stat = ERR_MUNOTALLSEC; } shmdt((void *)start_addr); return FALSE; } assert(JNLPOOL_SEGMENT == replpool_id->pool_type || RECVPOOL_SEGMENT == replpool_id->pool_type); if(JNLPOOL_SEGMENT != replpool_id->pool_type && RECVPOOL_SEGMENT != replpool_id->pool_type) { shmdt((void *)start_addr); return FALSE; } jnlpool_segment = (JNLPOOL_SEGMENT == replpool_id->pool_type); if (-1 == shmctl(shmid, IPC_STAT, &shmstat)) { save_errno = errno; assert(FALSE);/* we were able to attach to this shmid before so should be able to get stats on it */ util_out_print("!AD -> Error with shmctl for shmid = !UL", TRUE, LEN_AND_STR(instfilename), shmid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } /* Check if instance filename reported in shared memory still exists. If not, clean this * shared memory section without even invoking "mu_rndwn_repl_instance" as that expects * the instance file to exist. Same case if shared memory points back to an instance file * whose file header does not have this shmid. */ OPENFILE(instfilename, O_RDONLY, fd); /* check if we can open it */ msgbuff[0] = '\0'; remove_shmid = FALSE; if (FD_INVALID == fd) { if (ENOENT == errno) { SNPRINTF(msgbuff, OUT_BUFF_SIZE, "File %s does not exist", instfilename); if (1 < shmstat.shm_nattch) { PRINT_AND_SEND_REPLPOOL_FAILURE_MSG(msgbuff, replpool_id, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = TRUE; } else { /* open() errored out e.g. due to file permissions. Log that */ save_errno = errno; util_out_print("Cannot rundown replpool shmid !UL for instance file" " !AD as open() on the file returned the following error", TRUE, shmid, LEN_AND_STR(instfilename)); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } } else { LSEEKREAD(fd, 0, &repl_instance, SIZEOF(repl_inst_hdr), status); if (0 != status) { save_errno = errno; util_out_print("!AD -> Error with LSEEKREAD for shmid = !UL", TRUE, LEN_AND_STR(instfilename), shmid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } if ((jnlpool_segment && (repl_instance.jnlpool_shmid != shmid)) || (!jnlpool_segment && (repl_instance.recvpool_shmid != shmid))) { SNPRINTF(msgbuff, OUT_BUFF_SIZE, "%s SHMID (%d) in the instance file header does not match with the" " one reported by \"ipcs\" command (%d)", jnlpool_segment ? "Journal Pool" : "Receive Pool", jnlpool_segment ? repl_instance.jnlpool_shmid : repl_instance.recvpool_shmid, shmid); if (1 < shmstat.shm_nattch) { PRINT_AND_SEND_REPLPOOL_FAILURE_MSG(msgbuff, replpool_id, shmid); *exit_stat = ERR_MUNOTALLSEC; shmdt((void *)start_addr); return FALSE; } remove_shmid = TRUE; } CLOSEFILE_RESET(fd, status); /* resets "fd" to FD_INVALID */ } shmdt((void *)start_addr); if (remove_shmid) { assert('\0' != msgbuff[0]); if (0 != shm_rmid(shmid)) { save_errno = errno; util_out_print("!AD -> Error removing shared memory for shmid = !UL", TRUE, LEN_AND_STR(instfilename), shmid); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) save_errno); *exit_stat = ERR_MUNOTALLSEC; return FALSE; } PRINT_AND_SEND_SHMREMOVED_MSG(msgbuff, STRLEN(instfilename), instfilename, shmid); *exit_stat = ERR_SHMREMOVED; } else *exit_stat = SS_NORMAL; return TRUE; } /* Gets all the required fields in shm_parms struct for a given shm entry */ shm_parms *get_shm_parm(char *entry) { char *parm; shm_parms *parm_buff; struct shmid_ds shm_buf; parm_buff = (shm_parms *)malloc(SIZEOF(shm_parms)); parm = parse_shm_entry(entry, SHMID); CONVERT_TO_NUM(shmid); parm = parse_shm_entry(entry, KEY); CONVERT_TO_NUM(key); /* get shm segment size directly from shmid_ds instead of parsing ipcs * output (thus avoiding the -a option for ipcs in mu_rndwn_all() */ if (-1 == shmctl(parm_buff->shmid, IPC_STAT, &shm_buf)) { free(parm_buff); return NULL; } parm_buff->sgmnt_siz = shm_buf.shm_segsz; return parm_buff; } /* Parses the output of 'IPCS_CMD_STR' command. Returns the value of the * specified field. */ /* NOTE : Even though the standard says that every column in the output * of 'ipcs' command should be separated by atleast one space, we have * observed a case where there is no space between the first (T) and * second (ID) fields on AIX under certain conditions. * The workaround is to always insert a blank space for all UNIX platforms * after the first (T field) character assuming the entry always starts with a * character describing the type of the ipc resource ('m' for shared memory). * On linux, the ipcs output starts with a KEY field (a hexadecimal number). * See the definition of IPCS_CMD_STR for this handling */ char *parse_shm_entry(char *entry, int which_field) { char *parm; int iter, indx1 = 0, indx2 = 0; for(iter = 1; iter < which_field; iter++) { /* Strip leading spaces */ while(entry[indx1] == ' ') indx1++; /* Accept until spaces or NULL */ while(entry[indx1] && entry[indx1] != ' ') indx1++; } /* Strip leading spaces */ while(entry[indx1] == ' ') indx1++; if ('\0' == entry[indx1]) { assert(FALSE); return NULL; } parm = (char *)malloc(MAX_PARM_LEN); /* Copy value from entry until NULL or a space character */ while(entry[indx1] && (entry[indx1] != ' ') && (indx2 < (MAX_PARM_LEN - 1))) parm[indx2++] = entry[indx1++]; parm[indx2] = '\0'; return parm; } int parse_sem_id(char *entry) { char *parm; int iter, indx1 = 0, indx2; while(entry[indx1] == ' ') indx1++; while(entry[indx1] && entry[indx1] != ' ') indx1++; while(entry[indx1] == ' ') indx1++; if ('\0' == entry[indx1]) { assert(FALSE); return -1; } indx2 = indx1; parm = &entry[indx1]; while(entry[indx2] && entry[indx2] != ' ') indx2++; entry[indx2] = '\0'; if (cli_is_dcm(parm)) return (int)STRTOUL(parm, NULL, 10); else if (cli_is_hex(parm + 2)) return (int)STRTOUL(parm, NULL, 16); else { assert(FALSE); return -1; } } int mu_rndwn_sem_all(void) { int save_errno, exit_status = SS_NORMAL, semid; char entry[MAX_ENTRY_LEN]; FILE *pf; char fname[MAX_FN_LEN + 1], *fgets_res; boolean_t rem_sem; shm_parms *parm_buff; if (NULL == (pf = POPEN(IPCS_SEM_CMD_STR ,"r"))) { save_errno = errno; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("POPEN()"), CALLFROM, save_errno); return ERR_MUNOTALLSEC; } while (NULL != (FGETS(entry, SIZEOF(entry), pf, fgets_res)) && entry[0] != '\n') { if (-1 != (semid = parse_sem_id(entry))) { if (is_orphaned_gtm_semaphore(semid)) { /* semval == 0 and corresponding shared memory has been removed */ if (-1 != semctl(semid, 0, IPC_RMID)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SEMREMOVED, 1, semid); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SEMREMOVED, 1, semid); } } } } pclose(pf); return exit_status; } boolean_t is_orphaned_gtm_semaphore(int semid) { int semno, semval; struct semid_ds semstat; union semun semarg; semarg.buf = &semstat; if (-1 != semctl(semid, 0, IPC_STAT, semarg)) { if (-1 == (semval = semctl(semid, semarg.buf->sem_nsems - 1, GETVAL)) || GTM_ID != semval) return FALSE; else { /* Make sure all has value = 0 */ for (semno = 0; semno < semarg.buf->sem_nsems - 1; semno++) if (-1 == (semval = semctl(semid, semno, GETVAL)) || semval) return FALSE; } /* We know that the semvals are 0, and this is a GTM semaphore. Now, we can remove it if it is not specifically * meant to be kept. */ return !in_keep_sems_list(semid); } return FALSE; } boolean_t in_keep_sems_list(int semid) { semid_queue_elem *iter; iter = keep_semids; while (iter) { if (iter->semid == semid) return TRUE; iter = iter->prev; } return FALSE; } void add_to_semids_list(int semid) { semid_queue_elem *new_semid_elem; new_semid_elem = (semid_queue_elem*) malloc(SIZEOF(semid_queue_elem)); new_semid_elem->semid = semid; new_semid_elem->prev = keep_semids; keep_semids = new_semid_elem; } fis-gtm-V7.0-005/sr_unix/mu_rndwn_all.h0000755000032200000250000000152114342376330016675 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ #ifndef MU_RNDWN_ALL_INCLUDED #define MU_RNDWN_ALL_INCLUDED typedef struct semid_queue_elem_t { int4 semid; struct semid_queue_elem_t *prev; } semid_queue_elem; int mu_rndwn_all(void); int mu_rndwn_sem_all(void); int parse_sem_id(char *); boolean_t is_orphaned_gtm_semaphore(int); boolean_t in_keep_sems_list(int semid); void add_to_semids_list(int semid); #endif /* MU_RNDWN_ALL_INCLUDED */ fis-gtm-V7.0-005/sr_unix/mu_rndwn_file.c0000644000032200000250000021244414342376330017044 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_ipc.h" #include "gtm_string.h" #include "gtm_time.h" #include "gtm_unistd.h" #include "gtm_stdio.h" #include #include #include #include #include #include "gtm_sem.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gtmio.h" #include "gdsfhead.h" #include "db_header_conversion.h" #include "filestruct.h" #include "iosp.h" #include "jnl.h" #include "mutex.h" #include "lockconst.h" #include "interlock.h" #include "aswp.h" #include "gtm_c_stack_trace.h" #include "eintr_wrappers.h" #include "eintr_wrapper_semop.h" #include "mu_rndwn_file.h" #include "performcaslatchcheck.h" #include "util.h" #include "send_msg.h" #include "tp_change_reg.h" #include "dbfilop.h" #include "gvcst_protos.h" /* for gvcst_init_sysops prototype */ #include "do_semop.h" #include "ipcrmid.h" #include "rc_cpt_ops.h" #include "gtmmsg.h" #include "wcs_flu.h" #include "do_shmat.h" #include "is_file_identical.h" #include "io.h" #include "gtmsecshr.h" #include "secshr_client.h" #include "ftok_sems.h" #include "mu_rndwn_all.h" #include "error.h" #include "anticipatory_freeze.h" #include "gtmcrypt.h" #include "db_snapshot.h" #include "shmpool.h" /* Needed for the shmpool structures */ #include "is_proc_alive.h" #include "ss_lock_facility.h" #include "cli.h" #include "gtm_file_stat.h" #include "buddy_list.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "hashtab_mname.h" /* needed for muprec.h */ #include "hashtab.h" /* needed for muprec.h */ #include "muprec.h" #include "aio_shim.h" #include "mu_gv_cur_reg_init.h" #include "mlkdef.h" GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_data_ptr_t cs_data; GBLREF uint4 process_id; GBLREF boolean_t mupip_jnl_recover; GBLREF ipcs_mesg db_ipcs; GBLREF jnl_gbls_t jgbl; GBLREF gd_region *ftok_sem_reg; GBLREF mur_opt_struct mur_options; GBLREF mval dollar_zgbldir; GBLREF boolean_t mu_region_found; #ifdef DEBUG GBLREF boolean_t in_mu_rndwn_file; #endif /* Any additions/removal from this "static" list requires a similar change in NESTED_MU_RNDWN_FILE_CALL */ static gd_region *rundown_reg = NULL; static gd_region *temp_region; static sgmnt_data_ptr_t temp_cs_data; static sgmnt_addrs *temp_cs_addrs; static boolean_t mu_rndwn_file_standalone; static boolean_t sem_created; static boolean_t no_shm_exists; static boolean_t shm_status_confirmed; LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; error_def(ERR_BADDBVER); error_def(ERR_DBFILERR); error_def(ERR_DBIDMISMATCH); error_def(ERR_DBNAMEMISMATCH); error_def(ERR_DBNOTGDS); error_def(ERR_DBRDONLY); error_def(ERR_DBSHMNAMEDIFF); error_def(ERR_JNLORDBFLU); error_def(ERR_MUFILRNDWNSUC); error_def(ERR_MURNDWNOVRD); error_def(ERR_MUUSERECOV); error_def(ERR_MUUSERLBK); error_def(ERR_NOMORESEMCNT); error_def(ERR_OFRZACTIVE); error_def(ERR_SEMREMOVED); error_def(ERR_SHMREMOVED); error_def(ERR_STATSDBNOTSUPP); error_def(ERR_SYSCALL); error_def(ERR_TEXT); error_def(ERR_VERMISMATCH); #define ALIGN_BUFF_IF_NEEDED_FOR_DIO(UDI, BUFF, TSD, TSD_SIZE) \ MBSTART { \ if (UDI->fd_opened_with_o_direct) \ { \ BUFF = (TREF(dio_buff)).aligned; \ memcpy(BUFF, TSD, TSD_SIZE); \ } else \ BUFF = (char *)TSD; \ } MBEND #define RESET_GV_CUR_REGION \ MBSTART { \ gv_cur_region = temp_region; \ cs_addrs = temp_cs_addrs; \ cs_data = temp_cs_data; \ } MBEND #define MU_RNDWN_FILE_CLNUP(REG, UDI, TSD, SEM_CREATED, SEM_INCREMENTED) \ MBSTART { \ int rc; \ \ IF_LIBAIO(aio_shim_destroy(UDI->owning_gd);) \ if (FD_INVALID != UDI->fd) \ { \ CLOSEFILE_RESET(UDI->fd, rc); \ assert(FD_INVALID == UDI->fd); \ } \ if (NULL != TSD) \ { \ free(TSD); \ TSD = NULL; \ } \ if (SEM_INCREMENTED) \ { \ do_semop(UDI->semid, DB_CONTROL_SEM, -1, IPC_NOWAIT | SEM_UNDO); \ SEM_INCREMENTED = FALSE; \ } \ if (SEM_CREATED) \ { \ if (-1 == sem_rmid(UDI->semid)) \ { \ RNDWN_ERR("!AD -> Error removing semaphore.", REG); \ } else \ SEM_CREATED = FALSE; \ } \ REVERT; \ assert((NULL == ftok_sem_reg) || (REG == ftok_sem_reg)); \ if (REG == ftok_sem_reg) \ ftok_sem_release(REG, UDI->counter_ftok_incremented, TRUE); \ RESET_GV_CUR_REGION; \ } MBEND #define SEG_SHMATTACH(addr, reg, udi, tsd, sem_created, sem_incremented) \ MBSTART { \ if (-1 == (sm_long_t)(cs_addrs->db_addrs[0] = (sm_uc_ptr_t) \ do_shmat(udi->shmid, addr, SHM_RND))) \ { \ if (EINVAL != errno) \ RNDWN_ERR("!AD -> Error attaching to shared memory", (reg)); \ /* shared memory segment no longer exists */ \ MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, sem_incremented); \ return FALSE; \ } \ } MBEND /* Print an error message that, based on whether replication was enabled at the time of the crash, would instruct * the user to a more appropriate operation than RUNDOWN, such as RECOVER or ROLLBACK. */ #define PRINT_PREVENT_RUNDOWN_MESSAGE(REG, CS_ADDRS, NEED_ROLLBACK) \ MBSTART { \ if (NEED_ROLLBACK) \ { \ rts_error_csa(CSA_ARG(CS_ADDRS) VARLSTCNT(8) ERR_MUUSERLBK, 2, DB_LEN_STR(REG), \ ERR_TEXT, 2, LEN_AND_LIT("Run MUPIP JOURNAL ROLLBACK")); \ } else \ { \ rts_error_csa(CSA_ARG(CS_ADDRS) VARLSTCNT(8) ERR_MUUSERECOV, 2, DB_LEN_STR(REG), \ ERR_TEXT, 2, LEN_AND_LIT("Run MUPIP JOURNAL RECOVER")); \ } \ } MBEND /* This macro is called whenever a "mu_rndwn_file" call is done from within "mu_rndwn_file". * In that case, some static variables (relied upon by mu_rndwn_file_ch to handle errors in current * "mu_rndwn_file" frame level) are saved off in the stack and restored once the call returns. */ #define NESTED_MU_RNDWN_FILE_CALL(REG, STANDALONE, STATUS) \ { \ gd_region *save_rundown_reg; \ gd_region *save_temp_region; \ sgmnt_data_ptr_t save_temp_cs_data; \ sgmnt_addrs *save_temp_cs_addrs; \ boolean_t save_mu_rndwn_file_standalone; \ boolean_t save_sem_created; \ boolean_t save_no_shm_exists; \ boolean_t save_shm_status_confirmed; \ \ save_rundown_reg = rundown_reg; \ save_temp_region = temp_region; \ save_temp_cs_data = temp_cs_data; \ save_temp_cs_addrs = temp_cs_addrs; \ save_mu_rndwn_file_standalone = mu_rndwn_file_standalone; \ save_sem_created = sem_created; \ save_no_shm_exists = no_shm_exists; \ save_shm_status_confirmed = shm_status_confirmed; \ \ STATUS = mu_rndwn_file(REG, STANDALONE); \ \ rundown_reg = save_rundown_reg; \ temp_region = save_temp_region; \ temp_cs_data = save_temp_cs_data; \ temp_cs_addrs = save_temp_cs_addrs; \ mu_rndwn_file_standalone = save_mu_rndwn_file_standalone; \ sem_created = save_sem_created; \ no_shm_exists = save_no_shm_exists; \ shm_status_confirmed = save_shm_status_confirmed; \ } /* Runs down a stats db pointed to by "statsDBreg". Returns TRUE/FALSE depending on whether rundown succeeded or not. * Input parameter "standalone" is FALSE if caller is MUPIP RUNDOWN and TRUE otherwise. Used to decide whether to * print the MUFILRNDWNSUC message or not (only mupip rundown should print it). * Modifies "*statsdb_exists" to TRUE or FALSE depending on whether the file exists or not. */ STATICFNDEF boolean_t mu_rndwn_file_statsdb(gd_region *statsDBreg, boolean_t *statsdb_exists, boolean_t standalone) { boolean_t statsDBrundown_status; gd_region *save_gv_cur_region, *save_ftok_sem_reg; gd_segment *statsDBseg; /* We are in "mu_rndwn_file" of a basedb and are about to do a nested call of "mu_rndwn_file" on a statsdb. * But we don't want that to in turn call a "mu_rndwn_file" of the basedb (which it can if IS_RDBF_STATSDB * is TRUE; see NESTED_MU_RNDWN_FILE_CALL usage in "mu_rndwn_file") as that would lead to an indefinite recursion. * Below assert is done to ensure that does not happen. */ assert(IS_STATSDB_REGNAME(statsDBreg)); assert(statsDBreg->owning_gd->regions != statsDBreg); /* A statsdb is never the first region in a gld */ if (mupfndfil(statsDBreg, NULL, LOG_ERROR_FALSE)) { /* statsDB exists. Do its rundown first. */ *statsdb_exists = TRUE; statsDBseg = statsDBreg->dyn.addr; if (NULL == statsDBseg->file_cntl) FILE_CNTL_INIT(statsDBseg); /* Since this function is called only from "mu_rndwn_file", we hold the ftok lock on the basedb at this point. * But the "mu_rndwn_file" call below is going to get the ftok lock on the statsdb and "ftok_sem_get" does not * like holding more than one ftok at any point in time. So fix structures to indicate no ftok lock is held * and restore that after the "mu_rndwn_file" call. */ save_ftok_sem_reg = ftok_sem_reg; ftok_sem_reg = NULL; NESTED_MU_RNDWN_FILE_CALL(statsDBreg, FALSE, statsDBrundown_status); if (statsDBrundown_status && !standalone) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_MUFILRNDWNSUC, 2, DB_LEN_STR(statsDBreg)); ftok_sem_reg = save_ftok_sem_reg; save_gv_cur_region = gv_cur_region; /* In case "mu_rndwn_file" call on the statsdb failed with SIG-11 or so, it would return with "gv_cur_region" * still pointing to the statsdb so fix it to point back to the basedb and continue with basedb rundown. */ if (save_gv_cur_region != gv_cur_region) { gv_cur_region = save_gv_cur_region; tp_change_reg(); } } else { *statsdb_exists = FALSE; statsDBrundown_status = TRUE; } return statsDBrundown_status; } /* Description: * This routine is used for two reasons * 1) get standalone access: * First uses/creates ftok semaphore. * Then create a new semaphore for that region. * Releases ftok semaphore (does not remove). * 2) rundown shared memory * Uses/creates ftok semaphore. * Parameters: * standalone = TRUE => create semaphore to get standalone access * standalone = FALSE => rundown shared memory * Note: Currently there are no callers with standalone == FALSE other * than MUPIP RUNDOWN. * Return Value: * TRUE for success * FALSE for failure */ boolean_t mu_rndwn_file(gd_region *reg, boolean_t standalone) { int status, save_errno, sopcnt, tsd_size, save_udi_semid = INVALID_SEMID, semop_res, stat_res, rc; int csd_size; char now_running[MAX_REL_NAME]; boolean_t is_gtm_shm, is_statsdb, rc_cpt_removed, statsDBexists; boolean_t glob_sec_init, db_shm_in_sync, remove_shmid, remove_mlk_shmid, ftok_counter_halted; boolean_t crypt_warning, do_crypt_init, need_statsdb_rundown; boolean_t baseDBrundown_status, statsDBrundown_status; sgmnt_data_ptr_t csd, tsd = NULL; sgmnt_addrs *csa; jnl_private_control *jpc; struct sembuf sop[4], *sopptr; struct shmid_ds shm_buf; file_control *fc; unix_db_info *statsDBudi, *udi; enum db_acc_method acc_meth; struct stat stat_buf; struct semid_ds semstat; union semun semarg; uint4 status_msg, ss_pid; shm_snapshot_t *ss_shm_ptr; gtm_uint64_t sec_size, mmap_sz = 0; gd_segment *seg, *baseDBseg; int gtmcrypt_errno; boolean_t cleanjnl_present, override_present, wcs_flu_success, prevent_mu_rndwn, need_rollback; unsigned char *fn; mstr jnlfile; int jnl_fd; jnl_file_header header; int4 status1; uint4 status2; uint4 index1, index2; int iter, secshrstat; char *buff; node_local_ptr_t cnl; char basedb_fname[MAX_FN_LEN + 1], statsdb_fname[MAX_FN_LEN + 1], *statsdb_fname_ptr; int basedb_fname_len; uint4 statsdb_fname_len; gd_addr *owning_gd; gd_region *statsDBreg, *save_ftok_sem_reg; gd_segment *statsDBseg; jnl_buffer_ptr_t jbp; int mlk_shmid; # ifdef DEBUG boolean_t already_grabbed_ftok_sem = FALSE; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; mu_region_found = TRUE; mu_rndwn_file_standalone = standalone; rc_cpt_removed = FALSE; sem_created = FALSE; shm_status_confirmed = FALSE; assert(!jgbl.onlnrlbk); assert(!mupip_jnl_recover || standalone || IS_STATSDB_REG(reg)); /* save gv_cur_region, cs_data, cs_addrs, and restore them on return */ temp_region = gv_cur_region; temp_cs_data = cs_data; temp_cs_addrs = cs_addrs; rundown_reg = gv_cur_region = reg; # ifdef GTCM_RC rc_cpt_removed = mupip_rundown_cpt(); # endif seg = reg->dyn.addr; fc = seg->file_cntl; SYNC_OWNING_GD(reg); owning_gd = reg->owning_gd; for (iter = 0; ; iter++) { fc->op = FC_OPEN; status = dbfilop(fc); udi = FILE_INFO(reg); csa = &(udi->s_addrs); /* Need valid cs_addrs in is_anticipatory_freeze_needed, which can be called */ cs_addrs = csa; /* by gtm_putmsg(), so set it up here. */ if (SS_NORMAL != status) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) status, 2, DB_LEN_STR(reg), errno); if (ENOENT == errno) mu_region_found = FALSE; if (0 == iter) { if (FD_INVALID != udi->fd) /* Since dbfilop failed, close udi->fd only if it was opened */ CLOSEFILE_RESET(udi->fd, rc); /* resets "udi->fd" to FD_INVALID */ } else { /* Release ftok semaphore grabbed in first iteration and never released in later iterations */ assert(reg == ftok_sem_reg); ftok_sem_release(reg, udi->counter_ftok_incremented, TRUE); } return FALSE; } ESTABLISH_RET(mu_rndwn_file_ch, FALSE); if (0 == iter) { /* Get FTOK lock only once in for loop and do not release it as otherwise it introduces complications * related to "ftok_counter_halted" maintenance across all iterations and how much to finally * decrement the counter semaphore at the end (as part of the "ftok_sem_release"). * Note that if we are a statsdb it is possible that a parent "mu_rndwn_file" invocation grabbed the * ftok (and in turn invoked "mu_rndwn_file" on the basedb which in turn invoked "mu_rndwn_file" * on the statsdb again). In that case, the nested statsdb rundown invocation will continue to use * the ftok grabbed by the parent statsdb rundown invocation. We do not release the ftok in the parent * invocation as otherwise we run the possibility of deleting the ftok semaphore as part of that * (since at the time we do the basedb rundown invocation we have not yet attached to db shm to know * if the ftok counter semaphore overflowed or not). */ DEBUG_ONLY(already_grabbed_ftok_sem = udi->grabbed_ftok_sem); if (!udi->grabbed_ftok_sem) { if (!ftok_sem_get(reg, TRUE, GTM_ID, !standalone, &ftok_counter_halted)) { MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } } else { assert(NULL == ftok_sem_reg); /* parent statsdb "mu_rndwn_file" invocation would have done this */ ftok_sem_reg = reg; /* Inherit "ftok_counter_halted" from parent "ftok_sem_get" invocation since we did not do one */ ftok_counter_halted = !udi->counter_ftok_incremented; } /* At this point, we have not yet attached to the database shared memory so we do not know if the ftok * counter got halted previously or not. So be safe and assume it has halted in case the db shmid * indicates it is up and running. */ assert(udi->counter_ftok_incremented == !ftok_counter_halted); udi->counter_ftok_incremented = FALSE; } /* Now we have standalone access of the database using ftok semaphore. Any other ftok conflicted database suspends * their operation at this point. At the end of this routine, we release ftok semaphore lock. */ tsd_size = ROUND_UP(SIZEOF(sgmnt_data), DISK_BLOCK_SIZE); /* Assert that later DB_LSEEKWRITE with DIO will be fine */ assert(!udi->fd_opened_with_o_direct || (tsd_size == ROUND_UP2(tsd_size, DIO_ALIGNSIZE(udi)))); tsd = (sgmnt_data_ptr_t)malloc(tsd_size); buff = (udi->fd_opened_with_o_direct ? (TREF(dio_buff)).aligned : (char *)tsd); DB_LSEEKREAD(udi, udi->fd, 0, buff, tsd_size, status); if (0 != status) { RNDWN_ERR("!AD -> Error reading from file.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } if (udi->fd_opened_with_o_direct) memcpy(tsd, buff, tsd_size); if (0 == memcmp(tsd->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_upconv(tsd); if (standalone && IS_RDBF_STATSDB(tsd)) { /* Only MUPIP RUNDOWN (which has "standalone" set to FALSE) is supported for statsdb files. * All other MUPIP commands which require standalone access cannot directly operate on statsdb files. */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_STATSDBNOTSUPP, 2, DB_LEN_STR(reg)); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } seg->read_only = tsd->read_only; SYNC_RESERVEDDBFLAGS_REG_CSA_CSD(reg, csa, tsd, ((node_local_ptr_t)NULL)); if (!IS_AIO_DBGLDMISMATCH(seg, tsd)) break; /* Close current file and reopen with the correct O_DIRECT setting based on the revised asyncio value */ CLOSEFILE_RESET(udi->fd, rc); /* resets "udi->fd" to FD_INVALID */ /* asyncio settings differ between segment and db. In this case, copy asyncio setting from db into segment * and retry the db file open. */ COPY_AIO_SETTINGS(seg, tsd); /* copies from tsd to seg */ free(tsd); tsd = NULL; REVERT; } CSD2UDI(tsd, udi); /* copies tsd->semid/tsd->shmid into udi->semid/udi->shmid */ is_statsdb = IS_RDBF_STATSDB(tsd); assert(!already_grabbed_ftok_sem || is_statsdb); assert(!is_statsdb || !standalone); /* or else an ERR_STATSDBNOTSUPP error would have been issued */ /* If this is a statsdb, then we are guaranteed the caller is a MUPIP RUNDOWN command (standalone is FALSE). * Check if it is a MUPIP RUNDOWN -FILE statsdb call. In that case, we need to instead do a * MUPIP RUNDOWN -FILE basedb call and let that in turn rundown the statsdb (need that to safely cleanup * the .gst file). Similar handling is needed if the caller is argumentless MUPIP RUNDOWN. In all other cases, * we are guaranteed "mu_rndwn_file" will be invoked only on the basedb and so this call for a rundown of the * statsdb is not a direct call and so we can continue the rundown. Thankfully the two desired cases are * easily identified as in that case we would have done a "mu_gv_cur_reg_init" (which would have set * owning_gd->is_dummy_gbldir to TRUE) AND "reg" would be the first region in the gld even though it points * to a statsdb file. */ if (is_statsdb && owning_gd->is_dummy_gbldir && (reg == owning_gd->regions)) { /* Copy basedb file name to local variable before freeing up tsd, releasing ftok lock etc. */ assert(tsd->basedb_fname_len); assert(ARRAYSIZE(tsd->basedb_fname) <= ARRAYSIZE(basedb_fname)); basedb_fname_len = MIN(tsd->basedb_fname_len, ARRAYSIZE(basedb_fname) - 1); assert('\0' == tsd->basedb_fname[basedb_fname_len]); memcpy(basedb_fname, tsd->basedb_fname, basedb_fname_len + 1); /* copy trailing '\0' too */ /* Record "ftok_counter_halted" value from "ftok_sem_get" invocation for child statsdb "mu_rndwn_file" invocation */ udi->counter_ftok_incremented = !ftok_counter_halted; /* Create fresh gld/region structure and point basedb to it and invoke mu_rndwn_file on this */ mu_gv_cur_reg_init(); /* changes "gv_cur_region" */ baseDBseg = gv_cur_region->dyn.addr; assert(ARRAYSIZE(baseDBseg->fname) >= ARRAYSIZE(basedb_fname)); memcpy(baseDBseg->fname, basedb_fname, basedb_fname_len + 1); /* copy trailing '\0' too */ baseDBseg->fname_len = basedb_fname_len; /* Check if the basedb exists. If not, skip nested "mu_rndwn_file" invocation altogether */ if (mupfndfil(gv_cur_region, NULL, LOG_ERROR_FALSE)) { /* We have gotten the ftok semaphore on the statsdb. Releasing it here (and reobtaining it in the nested * statsdb "mu_rndwn_file" invocation) runs the risk of deleting the ftok semaphore while processes are * still attached. But we also do not want to leak the ftok ipcs. So hold on to the ftok sem of the statsdb * here and use that in the nested statsdb "mu_rndwn_file" invocation. And do the release of this there when * we have access to statsdb shm (and can therefore correctly decide whether counter semaphore got halted or * not). Save "ftok_sem_reg" before it is modified (a few lines later). */ save_ftok_sem_reg = ftok_sem_reg; /* Set "ftok_sem_reg" to NULL since the nested "mu_rndwn_file" call is going to get ftok lock on the * basedb and that would assert fail otherwise. */ ftok_sem_reg = NULL; /* Now that statsdb region has been setup, transfer parent "mu_rndwn_file" udi info to it */ BASEDBREG_TO_STATSDBREG(gv_cur_region, statsDBreg); statsDBseg = statsDBreg->dyn.addr; if (NULL == statsDBseg->file_cntl) FILE_CNTL_INIT(statsDBseg); statsDBudi = FILE_INFO(statsDBreg); memcpy(statsDBudi, udi, SIZEOF(*udi)); NESTED_MU_RNDWN_FILE_CALL(gv_cur_region, FALSE, baseDBrundown_status); /* If above "mu_rndwn_file" invocation of the basedb did a rundown of the statsdb too, then we would have * released the ftok lock on the statsdb. But if an error happened in "mu_rndwn_file" of the basedb before * it even attempted "mu_rndwn_file" of statsdb, then we would still be holding the ftok sem on the statsdb. * So use that as a means of distinguishing the two scenarios. We can return right away if the statsdb * rundown is already happened in the nested call and should continue with the rundown in this call * otherwise. */ need_statsdb_rundown = statsDBudi->grabbed_ftok_sem; /* Note: It is possible that need_statsdb_rundown is TRUE and baseDBrundown_status is also TRUE * in case the baseDB has already been run down but statsDB has not been rundown and * the gtm_statsdir env var used to create this statsDB is different from the current value of * the gtm_statsdir env var. Therefore continue running down the statsDB as long as that is * needed, irrespective of the rundown status of the baseDB. */ if (!need_statsdb_rundown) { mu_gv_cur_reg_free(); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return baseDBrundown_status; } ftok_sem_reg = save_ftok_sem_reg; } /* else : basedb does not exist. Continue rundown of statsdb without going through basedb rundown */ } /* read_only process cannot rundown database. * read only process can succeed in getting standalone access of the database, * if the db is clean with no orphaned shared memory. * Note: we use gtmsecshr for updating file header for semaphores id. * Note: We place this check AFTER the LSEEKREAD so an argumentless rundown caller will have access to * udi->semid and make sure that does not later get removed if we are not running down shm due to DBRDONLY. */ if (reg->read_only && !standalone) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(reg)); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } override_present = (cli_present("OVERRIDE") == CLI_PRESENT); csa->hdr = tsd; csa->region = gv_cur_region; /* At this point, we have not yet attached to the database shared memory so we do not know if the ftok counter got halted * previously or not. So be safe and assume it has halted in case the db shmid indicates it is up and running. * We will set udi->counter_ftok_incremented back to an accurate value after we attach to the db shm. * This means we might not delete the ftok semaphore in some cases of error codepaths but it should be rare * and is better than incorrectly deleting it while live processes are concurrently using it. */ udi->counter_ftok_incremented = !ftok_counter_halted && (INVALID_SHMID == udi->shmid); if (USES_ENCRYPTION(tsd->is_encrypted) && !TREF(mu_set_file_noencryptable)) { INIT_PROC_ENCRYPTION(gtmcrypt_errno); if (0 == gtmcrypt_errno) INIT_DB_OR_JNL_ENCRYPTION(csa, tsd, seg->fname_len, seg->fname, gtmcrypt_errno); if (0 != gtmcrypt_errno) { GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, seg->fname_len, seg->fname); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } } semarg.buf = &semstat; if (INVALID_SEMID == udi->semid || (-1 == semctl(udi->semid, DB_CONTROL_SEM, IPC_STAT, semarg)) || # ifdef GTM64 (((tsd->gt_sem_ctime.ctime & 0xffffffff) == 0) && ((tsd->gt_sem_ctime.ctime >> 32) != semarg.buf->sem_ctime)) || # endif tsd->gt_sem_ctime.ctime != semarg.buf->sem_ctime) { /* Access control semaphore doesn't exist. Create one. Note that if creation time does not match, we ignore that * semaphore and assume it never existed. Argumentless mupip rundown will remove such a semaphore later. */ if (-1 == (udi->semid = semget(IPC_PRIVATE, FTOK_SEM_PER_ID, RWDALL | IPC_CREAT))) { udi->semid = INVALID_SEMID; RNDWN_ERR("!AD -> Error with semget with IPC_CREAT.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } sem_created = TRUE; tsd->semid = udi->semid; /* * Set value of semaphore number 2 ( = FTOK_SEM_PER_ID - 1) as GTM_ID. * In case we have orphaned semaphore for some reason, mupip rundown will be * able to identify GTM semaphores from the value and can remove. */ semarg.val = GTM_ID; if (-1 == semctl(udi->semid, FTOK_SEM_PER_ID - 1, SETVAL, semarg)) { RNDWN_ERR("!AD -> Error with semctl with SETVAL.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } /* * Warning: We must read the sem_ctime after SETVAL, which changes it. * We must not do any more SETVAL after this. * We consider sem_ctime as creation time of semaphore. */ semarg.buf = &semstat; if (-1 == semctl(udi->semid, FTOK_SEM_PER_ID - 1, IPC_STAT, semarg)) { RNDWN_ERR("!AD -> Error with semctl with IPC_STAT.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } udi->gt_sem_ctime = tsd->gt_sem_ctime.ctime = semarg.buf->sem_ctime; } # if defined(GTM64) && defined(BIGENDIAN) /* If the semaphore was created by a 32-bit big-endian version of GT.M the correct ctime will be in the * upper 32 bits and the lower 32 bits will be zero. Detect this case and adjust the time. We expect it to * error out later due to version mismatch or other conflict. */ if (((tsd->gt_sem_ctime.ctime & 0xffffffff) == 0) && ((tsd->gt_sem_ctime.ctime >> 32) == semarg.buf->sem_ctime)) tsd->gt_sem_ctime.ctime >>= 32; # endif /* Now lock the database using access control semaphore and increment counter. Typically, multiple statements are not * specified in a single line. However, each of the 4 lines below represent "one" semaphore operation and hence an * acceptible exception to the coding guidelines. */ sop[0].sem_num = DB_CONTROL_SEM; sop[0].sem_op = 0; /* wait for access control semaphore to be available */ sop[1].sem_num = DB_CONTROL_SEM; sop[1].sem_op = 1; /* lock it */ sop[2].sem_num = DB_COUNTER_SEM; sop[2].sem_op = 0; /* wait for counter semaphore to become 0 */ sop[3].sem_num = DB_COUNTER_SEM; sop[3].sem_op = DB_COUNTER_SEM_INCR; /* increment the counter semaphore */ # if defined(GTM64) && defined(BIGENDIAN) /* If the shared memory was created by a 32-bit big-endian version of GT.M the correct ctime will be in the * upper 32 bits and the lower 32 bits will be zero. Detect this case and adjust the time. We expect it to * error out later due to version mismatch or other conflict. */ no_shm_exists = (INVALID_SHMID == udi->shmid || -1 == shmctl(udi->shmid, IPC_STAT, &shm_buf)); if (!no_shm_exists && ((tsd->gt_shm_ctime.ctime & 0xffffffff) == 0) && ((tsd->gt_shm_ctime.ctime >> 32) == shm_buf.shm_ctime)) tsd->gt_shm_ctime.ctime >>= 32; no_shm_exists |= (tsd->gt_shm_ctime.ctime != shm_buf.shm_ctime); # else no_shm_exists = (INVALID_SHMID == udi->shmid || -1 == shmctl(udi->shmid, IPC_STAT, &shm_buf) || tsd->gt_shm_ctime.ctime != shm_buf.shm_ctime); # endif shm_status_confirmed = TRUE; /* Now we have ascertained the status of shared memory. */ /* Whether we want to wait for the counter semaphore to become zero NOW (or defer it) depends on the following conditions : * a) MUPIP RUNDOWN : If shared memory exists, then it is possible that the database we are attempting to rundown does not * match the existing shared memory (DBSHMNAMEDIFF case). If so, we should not wait for the counter semaphore, but proceed * with the rest of the rundown (!db_shm_in_sync case). So, we should NOT wait for the counter semaphore at THIS stage. If * we find later that the db and shared memory is in sync, we can then wait for the counter semaphore before doing the * actual rundown. * If shared memory doesn't exist, it is not possible to have DBSHMNAMEDIFF case. So, wait for the counter semaphore to * become zero NOW before proceeding with LSEEKWRITEs of the file header to nullify shmid/semid and other fields. * * b) STANDALONE ACCESS : In this case (like Regular NOONLINE ROLLBACK or MUPIP INTEG -FILE), the scenario is similar to * MUPIP RUNDOWN. So, wait for counter semaphore if no shared memory exists. If shared memory exists, defer waiting for * counter semaphore until later (when db_shm_in_sync is TRUE). */ sopcnt = no_shm_exists ? 4 : 2; sop[0].sem_flg = sop[1].sem_flg = sop[2].sem_flg = sop[3].sem_flg = SEM_UNDO | IPC_NOWAIT; SEMOP(udi->semid, sop, sopcnt, semop_res, NO_WAIT); if (-1 == semop_res) { RNDWN_ERR("!AD -> File already open by another process.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } udi->grabbed_access_sem = TRUE; udi->counter_acc_incremented = no_shm_exists; # ifdef DEBUG cleanjnl_present = (cli_present("CLEANJNL") == CLI_PRESENT); # else cleanjnl_present = FALSE; # endif /* Proceed with rundown if either journaling is off or we got here as a result of MUPIP JOURNAL -RECOVER or * MUPIP JOURNAL -ROLLBACK, unless the OVERRIDE qualifier is present (see the following code). */ /* If journaling and/or replication is enabled, prevent rundown on this database. Instead force caller to use * MUPIP JOURNAL RECOVER BACKWARD or MUPIP JOURNAL ROLLBACK BACKWARD respectively. Note though that those commands * too can call this function so allow them in those respective cases. */ if (JNL_ENABLED(tsd)) { if (REPL_ENABLED(tsd)) { need_rollback = TRUE; prevent_mu_rndwn = !mur_options.rollback; } else { need_rollback = FALSE; prevent_mu_rndwn = !mur_options.update; /* Allow MUPIP JOURNAL RECOVER or MUPIP JOURNAL ROLLBACK */ } } else { prevent_mu_rndwn = FALSE; need_rollback = FALSE; } statsDBrundown_status = TRUE; /* Now rundown database if shared memory segment exists. We try this for both values of 'standalone'. */ if (no_shm_exists) { /* Since we know no shm exists, we can safely say the counter has not halted so restore counter_ftok_incremented */ udi->counter_ftok_incremented = !ftok_counter_halted; assert(udi->counter_ftok_incremented); /* If this is a basedb, check for statsdb existence and if so call mu_rndwn_file on it */ if (!is_statsdb) { assert(udi->fn == (char *)&seg->fname[0]); statsdb_fname_len = ARRAYSIZE(statsdb_fname); gvcst_set_statsdb_fname(tsd, reg, statsdb_fname, &statsdb_fname_len); if (statsdb_fname_len) { BASEDBREG_TO_STATSDBREG(reg, statsDBreg); COPY_STATSDB_FNAME_INTO_STATSREG(statsDBreg, statsdb_fname, statsdb_fname_len); /* Note: Error status of statsdb rundown is factored into final basedb rundown status * by storing the return value in "statsDBrundown_status". */ statsDBrundown_status = mu_rndwn_file_statsdb(statsDBreg, &statsDBexists, standalone); /* If statsDB exists and rundown was successful, remove it. It is okay to do so since * we hold an ftok on the basedb at this point. */ if (statsDBexists && statsDBrundown_status) { rc = UNLINK(statsdb_fname); assert(0 == rc); } } } if (prevent_mu_rndwn) { if (override_present) { /* If the rundown should normally be prevented, but the operator specified an OVERRIDE qualifier, * record the fact of the usage in the syslog and continue. */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_MURNDWNOVRD, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("Overriding enabled journaling state")); } else { /* Issue error if the crash bit in the journal file is set thereby preventing the user from doing a * less appropriate operation than RECOVER or ROLLBACK. */ jnlfile.addr = (char *)tsd->jnl_file_name; jnlfile.len = tsd->jnl_file_len; if (FILE_PRESENT & gtm_file_stat(&jnlfile, NULL, NULL, TRUE, &status2)) { /* The journal file exists. */ assert('\0' == jnlfile.addr[jnlfile.len]); jnlfile.addr[jnlfile.len] = '\0'; /* In case the above assert fails. */ OPENFILE(jnlfile.addr, O_RDONLY, jnl_fd); if (0 <= jnl_fd) { DO_FILE_READ(jnl_fd, 0, &header, SIZEOF(header), status1, status2); if (SS_NORMAL == status1) { /* FALSE in the call below is to skip gtm_putmsgs even on errors. */ CHECK_JNL_FILE_IS_USABLE(&header, status1, FALSE, 0, NULL); if ((SS_NORMAL == status1) && (ARRAYSIZE(header.data_file_name) > header.data_file_name_length)) { assert('\0' == header.data_file_name[header.data_file_name_length]); header.data_file_name[header.data_file_name_length] = '\0'; if (is_file_identical((char *)header.data_file_name, (char *)seg->fname) && header.crash) { PRINT_PREVENT_RUNDOWN_MESSAGE(reg, csa, need_rollback); } } } } } } } if (rc_cpt_removed) { /* reset RC values if we've rundown the RC CPT */ /* attempt to force-write header */ tsd->rc_srv_cnt = tsd->dsid = tsd->rc_node = 0; assert(FALSE); /* not sure what to do here. handle it if/when it happens */ MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } else { /* Note that if creation time does not match, we ignore that shared memory segment. It might result in * orphaned shared memory segment which can be later removed with argument-less MUPIP RUNDOWN. */ tsd->shmid = udi->shmid = INVALID_SHMID; tsd->gt_shm_ctime.ctime = udi->gt_shm_ctime = 0; if (standalone) { if (!reg->read_only) { if (mupip_jnl_recover) memset(tsd->machine_name, 0, MAX_MCNAMELEN); ALIGN_BUFF_IF_NEEDED_FOR_DIO(udi, buff, tsd, tsd_size); /* sets "buff" */ if (0 == memcmp(tsd, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv((sgmnt_data_ptr_t)buff); DB_LSEEKWRITE(csa, udi, udi->fn, udi->fd, (off_t)0, buff, tsd_size, status); if (0 != status) { RNDWN_ERR("!AD -> Unable to write header to disk.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } } else { db_ipcs.open_fd_with_o_direct = udi->fd_opened_with_o_direct; db_ipcs.semid = tsd->semid; db_ipcs.gt_sem_ctime = tsd->gt_sem_ctime.ctime; db_ipcs.shmid = tsd->shmid; db_ipcs.gt_shm_ctime = tsd->gt_shm_ctime.ctime; if (!get_full_path((char *)DB_STR_LEN(reg), db_ipcs.fn, &db_ipcs.fn_len, GTM_PATH_MAX, &status_msg)) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(1) status_msg); RNDWN_ERR("!AD -> get_full_path failed.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } db_ipcs.fn[db_ipcs.fn_len] = 0; WAIT_FOR_REPL_INST_UNFREEZE_SAFE(csa); if (!tsd->read_only) { /* Respect read-only databases */ secshrstat = send_mesg2gtmsecshr(FLUSH_DB_IPCS_INFO, 0, (char *)NULL, 0); csa->read_only_fs = (EROFS == secshrstat); } if ((0 != secshrstat) && !csa->read_only_fs && !tsd->read_only) { RNDWN_ERR("!AD -> gtmsecshr was unable to write header to disk.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } } if (!ftok_sem_release(reg, FALSE, FALSE)) { RNDWN_ERR("!AD -> Error from ftok_sem_release.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } CLOSEFILE_RESET(udi->fd, rc); /* resets "udi->fd" to FD_INVALID */ REVERT; free(tsd); assert(udi->grabbed_access_sem); RESET_GV_CUR_REGION; return statsDBrundown_status; /* For "standalone" and "no shared memory existing", we exit here */ } else { /* We are here for not standalone (basically the "mupip rundown" command). */ if (0 != do_semop(udi->semid, DB_CONTROL_SEM, -1, IPC_NOWAIT | SEM_UNDO)) { assert(FALSE); /* We incremented the semaphore, so we should be able to decrement it */ RNDWN_ERR("!AD -> Error decrementing semaphore.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } udi->counter_acc_incremented = FALSE; if (0 != sem_rmid(udi->semid)) { assert(FALSE); /* We've created the semaphore, so we should be able to remove it */ RNDWN_ERR("!AD -> Error removing semaphore.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } if (!sem_created) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(3) ERR_SEMREMOVED, 1, udi->semid); gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(3) ERR_SEMREMOVED, 1, udi->semid); } sem_created = FALSE; udi->grabbed_access_sem = FALSE; udi->semid = INVALID_SEMID; /* "orphaned" and "newly" created semaphores are now removed */ /* Reset IPC fields in the file header and exit */ memset(tsd->machine_name, 0, MAX_MCNAMELEN); RESET_IPC_FIELDS(tsd); } } assert(!standalone); if (!tsd->read_only) { ALIGN_BUFF_IF_NEEDED_FOR_DIO(udi, buff, tsd, tsd_size); /* sets "buff" */ if (0 == memcmp(tsd, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv((sgmnt_data_ptr_t)buff); DB_LSEEKWRITE(csa, udi, udi->fn, udi->fd, (off_t)0, buff, tsd_size, status); } else status = 0; if (0 != status) { RNDWN_ERR("!AD -> Unable to write header to disk.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } CLOSEFILE_RESET(udi->fd, rc); /* resets "udi->fd" to FD_INVALID */ free(tsd); REVERT; /* For mupip rundown (standalone = FALSE), we release/remove ftok semaphore here. */ if (!ftok_sem_release(reg, udi->counter_ftok_incremented, TRUE)) { RNDWN_ERR("!AD -> Error from ftok_sem_release.", reg); return FALSE; } RESET_GV_CUR_REGION; return statsDBrundown_status; /* For "!standalone" and "no shared memory existing", we exit here */ } /* Now that we know shared memory exists, make sure FORWARD RECOVER or FORWARD ROLLBACK are not allowed to recover * the database. Only backward rollback and/or recover should be allowed. */ prevent_mu_rndwn = prevent_mu_rndwn || mur_options.forward; if (reg->read_only) /* read only process can't succeed beyond this point */ { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(reg)); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } /* Now we have a pre-existing shared memory section. Do some setup */ /* memcmp returns 0 on a match, so use && to see if both return a non-0 value meaning it isn't one of the expected labels */ if (memcmp(tsd->label, GDS_LABEL, GDS_LABEL_SZ - 1) && memcmp(tsd->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) { if (memcmp(tsd->label, GDS_LABEL, GDS_LABEL_SZ - 3)) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_DBNOTGDS, 2, DB_LEN_STR(reg)); else gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_BADDBVER, 2, DB_LEN_STR(reg)); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } seg->acc_meth = acc_meth = tsd->acc_meth; dbsecspc(reg, tsd, &sec_size); # ifdef __MVS__ /* match gvcst_init_sysops.c shmget with __IPC_MEGA or _LP64 */ if (ROUND_UP(sec_size, MEGA_BOUND) != shm_buf.shm_segsz) # else if (sec_size != shm_buf.shm_segsz) # endif { util_out_print("Expected shared memory size is !SL, but found !SL", TRUE, sec_size, shm_buf.shm_segsz); util_out_print("!AD -> Existing shared memory sizes do not match.", TRUE, DB_LEN_STR(reg)); /* fall through to get VERMISMATCH message */ } gv_cur_region = reg; tp_change_reg(); SEG_SHMATTACH(0, reg, udi, tsd, sem_created, udi->counter_acc_incremented); assert(csa == cs_addrs); csa->nl = cnl = (node_local_ptr_t)csa->db_addrs[0]; /* The following checks for GDS_LABEL_GENERIC, gtm_release_name, and cnl->glob_sec_init ensure that the * shared memory under consideration is valid. First, since cnl->label is in the same place for every * version, a failing check means it is most likely NOT a GT.M created shared memory, so no attempt will be * made to delete it. Next a successful match of the currently running release will guarantee that all fields in * the structure are where they're expected to be -- without this check the glob_sec_init check should not be done * as this field is at different offsets in different versions. Finally, we can check the glob_sec_init flag to * verify that shared memory has been completely initialized. If not, we should delete it right away. [C9D07-002355] */ for ( ; ; ) /* for loop only there to let us break from error cases without having a deep if-then-else structure */ { if (MEMCMP_LIT(cnl->label, GDS_LABEL_GENERIC)) { is_gtm_shm = FALSE; assert(FALSE); break; } is_gtm_shm = TRUE; remove_shmid = TRUE; memcpy(now_running, cnl->now_running, MAX_REL_NAME); if (memcmp(now_running, gtm_release_name, gtm_release_name_len + 1)) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_VERMISMATCH, 6, DB_LEN_STR(reg), gtm_release_name_len, gtm_release_name, LEN_AND_STR(now_running)); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } if (!cnl->glob_sec_init) { glob_sec_init = FALSE; break; } glob_sec_init = TRUE; /* memcmp returns 0 on a match, so use && to see if both return a non-0 value meaning * it isn't one of the expected labels */ if (memcmp(cnl->label, GDS_LABEL, GDS_LABEL_SZ - 1) && memcmp(cnl->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) { if (memcmp(cnl->label, GDS_LABEL, GDS_LABEL_SZ - 3)) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_DBNOTGDS, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_LITERAL("(from shared segment - nl)")); else gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_BADDBVER, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_LITERAL("(from shared segment - nl)")); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } /* Since nl is memset to 0 initially and then fname is copied over from gv_cur_region and since "fname" is * guaranteed to not exceed MAX_FN_LEN, we should have a terminating '\0' at least at * cnl->fname[MAX_FN_LEN] */ assert(cnl->fname[MAX_FN_LEN] == '\0'); /* First '\0' in cnl->fname can be earlier */ db_shm_in_sync = TRUE; /* assume this unless proven otherwise */ /* Check whether cnl->fname exists. If not, then db & shm are not in sync. */ STAT_FILE((char *)cnl->fname, &stat_buf, stat_res); if (-1 == stat_res) { save_errno = errno; db_shm_in_sync = FALSE; if (ENOENT == save_errno) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(7) MAKE_MSG_INFO(ERR_DBNAMEMISMATCH), 4, DB_LEN_STR(reg), udi->shmid, cnl->fname, save_errno); gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(6) MAKE_MSG_INFO(ERR_DBNAMEMISMATCH), 4, DB_LEN_STR(reg), udi->shmid, cnl->fname); /* In this case, the shared memory no longer points to a valid db file in the filesystem. * So it is best that we remove this shmid. But remove_shmid is already TRUE. Assert that. */ assert(remove_shmid); } else /* Could be permission issue */ { send_msg_csa(CSA_ARG(csa) VARLSTCNT(7) MAKE_MSG_INFO(ERR_DBNAMEMISMATCH), 4, DB_LEN_STR(reg), udi->shmid, cnl->fname, save_errno); gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(7) MAKE_MSG_INFO(ERR_DBNAMEMISMATCH), 4, DB_LEN_STR(reg), udi->shmid, cnl->fname, save_errno); remove_shmid = FALSE; /* Shared memory might be pointing to valid database */ } } if (db_shm_in_sync) { /* Check if cnl->fname and cnl->dbfid are in sync. If not, then db & shm are not in sync */ if (FALSE == is_gdid_stat_identical(&cnl->unique_id.uid, &stat_buf)) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(10) MAKE_MSG_INFO(ERR_DBIDMISMATCH), 4, cnl->fname, DB_LEN_STR(reg), udi->shmid, ERR_TEXT, 2, LEN_AND_LIT("[MUPIP] Database filename and fileid in shared memory are not in sync")); gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(10) MAKE_MSG_INFO(ERR_DBIDMISMATCH), 4, cnl->fname, DB_LEN_STR(reg), udi->shmid, ERR_TEXT, 2, LEN_AND_LIT("[MUPIP] Database filename and fileid in shared memory are not in sync")); db_shm_in_sync = FALSE; /* In this case, the shared memory points to a file that exists in the filesystem but * the fileid of the currently present file does not match the file id recorded in * shared memory at shm creation time. Therefore the database file has since been * overwritten. In this case, we have no other option but to remove this shmid. * remove_shmid is already TRUE. Assert that. */ assert(remove_shmid); } } /* Check if seg->fname and cnl->fname are identical filenames. * If not, then db that we want to rundown points to a shared memory which in turn points * to a DIFFERENT db file. In this case, do NOT remove the shared memory as it is possible * some other database file on the system points to it and passes the filename identicality check. */ if (db_shm_in_sync && !is_file_identical((char *)cnl->fname, (char *)seg->fname)) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) MAKE_MSG_INFO(ERR_DBSHMNAMEDIFF), 4, DB_LEN_STR(reg), udi->shmid, cnl->fname); gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(6) MAKE_MSG_INFO(ERR_DBSHMNAMEDIFF), 4, DB_LEN_STR(reg), udi->shmid, cnl->fname); db_shm_in_sync = FALSE; remove_shmid = FALSE; } wcs_flu_success = TRUE; /* If db & shm are not in sync at this point, skip the part of flushing shm to db file on disk. * We still need to reset the fields (shmid, semid etc.) in db file header. * About deleting the shmid, it depends on the type of out-of-sync between db & shm. This is handled * by setting the variable "remove_shmid" appropriately in each case. */ if (db_shm_in_sync) { /* Now that we have attached to db shm and verified it is usable, fix udi->counter_ftok_incremented * to take into account the "ftok_counter_halted" flag. This will make sure we do not incorrectly * delete the ftok semaphore in any error codepaths from now on until the end. In the end when we * are sure it is safe to rundown this shm, we will fix udi->counter_ftok_incremented to what it * should be to correctly remove the ftok semaphore. */ udi->counter_ftok_incremented = udi->counter_ftok_incremented && !cnl->ftok_counter_halted; if (!udi->counter_acc_incremented) { /* Now that we have ensured db & shm are in sync and will be doing the "actual" * rundown, we need to ensure that no one is attached to the database (counter * sempahore is 0) */ assert(!no_shm_exists); assert(udi->grabbed_access_sem); assert(INVALID_SEMID != udi->semid); sopcnt = 2; /* Need only the last 2 "sop" array */ sopptr = &sop[2]; SEMOP(udi->semid, sopptr, sopcnt, semop_res, NO_WAIT); if (-1 == semop_res) { RNDWN_ERR("!AD -> File already open by another process (2).", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } udi->counter_acc_incremented = TRUE; } /* If db & shm are in sync AND we aren't alone in using it, we can do nothing */ if (0 != shm_buf.shm_nattch) { util_out_print("!AD [!UL]-> File is in use by another process.", TRUE, DB_LEN_STR(reg), udi->shmid); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } if (prevent_mu_rndwn && cnl->jnl_file.u.inode) { if (override_present) { /* If the rundown should normally be prevented, but the operator specified an * OVERRIDE qualifier, record the fact of the usage in the syslog and continue. */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_MURNDWNOVRD, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT("Overriding OPEN journal file state in shared memory")); } else if (!FROZEN_CHILLED(csa)) { /* Journal file state being still open in shared memory implies a crashed state, * so error out. */ PRINT_PREVENT_RUNDOWN_MESSAGE(reg, csa, need_rollback); } } /* The shared section is valid and up-to-date with respect to the database file header; * ignore the temporary storage and use the shared section from here on. Now that the * shared section is valid, check if there is a statsdb associated with this (that we created * in this lifetime of the basedb shm or even before) and if so attempt a rundown of that first. */ if (cnl->statsdb_created) { assert(!IS_STATSDB_REG(reg)); statsdb_fname_len = cnl->statsdb_fname_len; assert(statsdb_fname_len); statsdb_fname_ptr = cnl->statsdb_fname; } else if (!is_statsdb) { assert(!IS_STATSDB_REG(reg)); assert(udi->fn == (char *)&seg->fname[0]); statsdb_fname_len = ARRAYSIZE(statsdb_fname); gvcst_set_statsdb_fname(tsd, reg, statsdb_fname, &statsdb_fname_len); statsdb_fname_ptr = &statsdb_fname[0]; } else statsdb_fname_len = 0; if (statsdb_fname_len) { BASEDBREG_TO_STATSDBREG(reg, statsDBreg); COPY_STATSDB_FNAME_INTO_STATSREG(statsDBreg, statsdb_fname_ptr, statsdb_fname_len); /* Note: Error status of statsdb rundown is factored into final basedb rundown status * by storing the return value in "statsDBrundown_status". */ statsDBrundown_status = mu_rndwn_file_statsdb(statsDBreg, &statsDBexists, standalone); assert(!cnl->statsdb_created || statsDBexists); assert(csa == cs_addrs); /* cs_addrs should not have changed in above call */ if (cnl->statsdb_created) { /* If basedb shm exists, note statsdb rundown status in basedb shm too */ cnl->statsdb_rundown_clean = statsDBrundown_status; } } csa->critical = (CRIT_PTR_T)(csa->db_addrs[0] + NODE_LOCAL_SIZE); assert(((INTPTR_T)csa->critical & 0xf) == 0); /* critical should be 16-byte aligned */ # ifdef CACHELINE_SIZE assert(0 == ((INTPTR_T)csa->critical & (CACHELINE_SIZE - 1))); # endif JNL_INIT(csa, reg, tsd); csa->shmpool_buffer = (shmpool_buff_hdr_ptr_t)(csa->db_addrs[0] + NODE_LOCAL_SPACE(tsd) + JNL_SHARE_SIZE(tsd)); csa->mlkctl = (struct mlk_ctldata_struct *)((sm_uc_ptr_t)csa->shmpool_buffer + SHMPOOL_SECTION_SIZE); csa->mlkctl_len = LOCK_SPACE_SIZE(tsd); if (dba_bg == acc_meth) { csd = (sgmnt_data_ptr_t)((sm_uc_ptr_t)csa->mlkctl + csa->mlkctl_len + CACHE_CONTROL_SIZE(tsd)); cs_data = csa->hdr = csd; assert(cnl->cache_off == -CACHE_CONTROL_SIZE(csd)); db_csh_ini(csa); } else { csd = (sgmnt_data_ptr_t)((sm_uc_ptr_t)csa->mlkctl + csa->mlkctl_len); cs_data = csa->hdr = csd; FSTAT_FILE(udi->fd, &stat_buf, stat_res); if (-1 == stat_res) { RNDWN_ERR("!AD -> Error with fstat.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } mmap_sz = stat_buf.st_size - BLK_ZERO_OFF(csd->start_vbn); CHECK_LARGEFILE_MMAP(reg, mmap_sz); /* can issue rts_error MMFILETOOLARGE */ csa->db_addrs[0] = (sm_uc_ptr_t) MMAP_FD(udi->fd, mmap_sz, BLK_ZERO_OFF(csd->start_vbn), FALSE); if (-1 == (sm_long_t)(csa->db_addrs[0])) { RNDWN_ERR("!AD -> Error mapping memory", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } csa->db_addrs[1] = csa->db_addrs[0] + mmap_sz - 1; } assert(sem_created || ((csd->semid == tsd->semid) && (csd->gt_sem_ctime.ctime == tsd->gt_sem_ctime.ctime))); if (sem_created && standalone) { csd->semid = tsd->semid; csd->gt_sem_ctime.ctime = tsd->gt_sem_ctime.ctime; } assert(JNL_ALLOWED(csd) == JNL_ALLOWED(tsd)); free(tsd); tsd = NULL; /* Check to see that the fileheader in the shared segment is valid, so we wont end up * flushing garbage to the db file. * Check the label in the header - keep adding any further appropriate checks in future here. */ /* memcmp returns 0 on a match, so use && to see if both return a non-0 value meaning it isn't * one of the expected labels */ if (memcmp(csd->label, GDS_LABEL, GDS_LABEL_SZ - 1) && memcmp(csd->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) { if (memcmp(csd->label, GDS_LABEL, GDS_LABEL_SZ - 3)) { status_msg = ERR_DBNOTGDS; if (0 != shm_rmid(udi->shmid)) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_TEXT("Error removing shared memory")); else { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_SHMREMOVED, 3, udi->shmid, DB_LEN_STR(reg)); send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_SHMREMOVED, 3, udi->shmid, DB_LEN_STR(reg)); } } else status_msg = ERR_BADDBVER; gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) status_msg, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_LITERAL("(File header in the shared segment seems corrupt)")); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } if (FROZEN_CHILLED(csa) && !override_present) { /* If there is an online freeze, we can't do the file writes. * Reject any command that requires a write to the frozen database file. */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_OFRZACTIVE, 2, DB_LEN_STR(reg)); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } /* If there was an online freeze and it was autoreleased, we don't want to take down the shared memory * and lose the freeze_online state. */ if (CHILLED_AUTORELEASE(csa) && !override_present) remove_shmid = FALSE; db_common_init(reg, csa, csd); /* do initialization common to "db_init" and "mu_rndwn_file" */ do_crypt_init = USES_ENCRYPTION(csd->is_encrypted); crypt_warning = FALSE; INITIALIZE_CSA_ENCR_PTR(csa, csd, udi, do_crypt_init, crypt_warning, FALSE); /* sets csa->encr_ptr */ /* cleanup mutex stuff */ csa->hdr->image_count = 0; gtm_mutex_init(reg, NUM_CRIT_ENTRY(csa->hdr), FALSE); /* this is the only process running */ assert(!csa->hold_onto_crit); /* so it is safe to do unconditional grab_crit/rel_crit below */ cnl->in_crit = 0; csa->now_crit = FALSE; reg->open = TRUE; if (csd->write_fullblk) csa->fullblockwrite_len = get_fs_block_size(udi->fd); if (rc_cpt_removed) /* reset RC values if we've rundown the RC CPT */ csd->rc_srv_cnt = csd->dsid = csd->rc_node = 0; /* At this point we are holding standalone access and are about to invoke wcs_recover/wcs_flu. If * one or more GT.M processes were at the midst of phase 2 commit, wcs_recover/wcs_flu invokes * wcs_phase2_commit_wait to wait for the processes to complete the phase 2 commit. But, if we have * standalone access, there is NO point waiting for the phase 2 commits to complete as the processes * might have been killed. So, set wcs_phase2_commit_pidcnt to 0 so wcs_recover/wcs_flu skips * invoking wcs_phase2_commit_wait */ cnl->wcs_phase2_commit_pidcnt = 0; DEBUG_ONLY(in_mu_rndwn_file = TRUE); TREF(donot_write_inctn_in_wcs_recover) = TRUE; /* If an orphaned snapshot is lying around, then clean it up */ /* SS_MULTI: If multiple snapshots are supported, then we must run-through each of the * "possible" snapshots in progress and clean them up */ assert(1 == MAX_SNAPSHOTS); ss_get_lock(gv_cur_region); /* Snapshot initiator will wait until this cleanup is done */ ss_shm_ptr = (shm_snapshot_ptr_t)SS_GETSTARTPTR(csa); ss_pid = ss_shm_ptr->ss_info.ss_pid; if (ss_pid && !is_proc_alive(ss_pid, 0)) ss_release(NULL); ss_release_lock(gv_cur_region); /* If cnl->donotflush_dbjnl is set, it means mupip recover/rollback was interrupted and * therefore we need not flush shared memory contents to disk as they might be in an inconsistent * state. Moreover, any more flushing will only cause the future rollback/recover to undo more * journal records (PBLKs). In this case, we will go ahead and remove shared memory (without * flushing the contents) in this routine. A reissue of the recover/rollback command will restore * the database to a consistent state. */ if (!cnl->donotflush_dbjnl) { jpc = csa->jnl; /* Check for a special case when the update/MUMPS process is killed in CMT06 * in UPDATE_JRS_RSRV_FREEADDR before updating phase2_commit_index2. * This causes a hang in jnl_flush() from wcs_flu(WCSFLU_NONE) below. * Restore phase2_commit_index2 by incrementing it. */ if ((NULL != jpc) && (NULL != jpc->jnl_buff)) { /* Standalone access, no need for a read barrier */ jbp = jpc->jnl_buff; index1 = jbp->phase2_commit_index1; index2 = jbp->phase2_commit_index2; /* This condition implies that a update/MUMPS process was killed in CMT06, right before * updating jb->phase2_commit_index2. Since this is the only process running, increment * index2 and let wcs_flu() called via jnl_flush() below handle it. */ if ((index1 == index2) && (jbp->freeaddr < jbp->rsrv_freeaddr) && (csa->nl->update_underway_tn != csd->trans_hist.curr_tn) && ((jbp->phase2_commit_array[index1].start_freeaddr + jbp->phase2_commit_array[index1].tot_jrec_len) == jbp->rsrv_freeaddr)) { INCR_PHASE2_COMMIT_INDEX(jbp->phase2_commit_index2, JNL_PHASE2_COMMIT_ARRAY_SIZE); } } if (cnl->glob_sec_init) { /* WCSFLU_NONE only is done here, as we aren't sure of the state, so no EPOCHs are * written. If we write an EPOCH record, recover may get confused. Note that for * journaling we do not call jnl_file_close() with TRUE for second parameter. * As a result journal file might not have an EOF record. * So, a new process will switch the journal file and cut the journal file link, * though it might be a good journal without an EOF */ wcs_flu_success = wcs_flu(WCSFLU_NONE); if (!wcs_flu_success) { if (override_present && !standalone) { /* Case of MUPIP RUNDOWN with OVERRIDE flag; continue. */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_MURNDWNOVRD, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT( "Overriding error during database block flush")); csd = csa->hdr; } else { /* In case of MUPIP RUNDOWN append a suggestion to use OVERRIDE flag * to bypass the error. */ if (standalone) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_JNLORDBFLU, 2, DB_LEN_STR(reg)); else rts_error_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_JNLORDBFLU, 2, DB_LEN_STR(reg), ERR_TEXT, 2, LEN_AND_LIT( "To force the operation to proceed, use the " "OVERRIDE qualifier")); assert(FALSE); /* The above rts_error should not return. */ return FALSE; } } } if (NULL != jpc) { /* this swaplock should probably be a mutex */ grab_crit(gv_cur_region, WS_87); /* If we own it or owner died, clear the fsync lock */ if (process_id == jpc->jnl_buff->fsync_in_prog_latch.u.parts.latch_pid) { RELEASE_SWAPLOCK(&jpc->jnl_buff->fsync_in_prog_latch); } else performCASLatchCheck(&jpc->jnl_buff->fsync_in_prog_latch, FALSE); if (NOJNL != jpc->channel) jnl_file_close(gv_cur_region, cleanjnl_present, FALSE); free(jpc); csa->jnl = NULL; rel_crit(gv_cur_region); } } /* Write master-map (in addition to file header) as wcs_flu done above would not have updated it */ csd_size = SIZEOF_FILE_HDR(csd); /* SIZEOF(sgmnt_data) + master-map */ TREF(donot_write_inctn_in_wcs_recover) = FALSE; /* Remove lock hash shared memory if needed */ if (remove_shmid && (INVALID_SHMID != csa->mlkctl->hash_shmid)) { remove_mlk_shmid = TRUE; mlk_shmid = csa->mlkctl->hash_shmid; } else remove_mlk_shmid = FALSE; } else { /* In this case, we just want to clear a few fields in the file header but we do NOT want to * write the master map which we don't have access to at this point so set csd_size accordingly. */ csd = tsd; csd_size = tsd_size; /* SIZEOF(sgmnt_data) */ remove_mlk_shmid = FALSE; /* We don't have mlkctl to check, so just leave it. */ } reg->open = FALSE; /* If wcs_flu returned FALSE, it better be because of MUPIP RUNDOWN run with OVERRIDE qualifier. */ assert(wcs_flu_success || (override_present && !standalone)); /* In case MUPIP RUNDOWN is invoked with OVERRIDE qualifier and we ignored a FALSE return from wcs_flu, do * not update the database header, thus forcing the operator to either use a ROLLBACK/RECOVER or supply the * OVERRIDE qualifier with RUNDOWN before GT.M could again be used to access the database. */ if (wcs_flu_success) { /* Note: At this point we have write permission */ memset(csd->machine_name, 0, MAX_MCNAMELEN); RESET_SHMID_CTIME(csd); if (!standalone) { /* Invalidate semid in the file header as part of rundown. The actual semaphore still * exists and we'll remove that just before releasing the ftok semaphore. However, if the * MUPIP RUNDOWN command gets killed AFTER we write the file header but BEFORE we remove * the semaphore from the system, we can have an orphaned semaphore. But, this is okay * since an arugment-less MUPIP RUNDOWN, if invoked, will remove those orphaned semaphores */ RESET_SEMID_CTIME(csd); } /* If "db_shm_in_sync" is TRUE, csd points to shared memory which is already aligned so we do * not need to do any more alignment if fd is opened with O_DIRECT. */ if (db_shm_in_sync) buff = (char *)csd; else { assert(!udi->fd_opened_with_o_direct || DIO_BUFF_NO_OVERFLOW((TREF(dio_buff)), csd_size)); ALIGN_BUFF_IF_NEEDED_FOR_DIO(udi, buff, csd, csd_size); /* sets "buff" */ } if (0 == memcmp(csd, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv((sgmnt_data_ptr_t)buff); DB_LSEEKWRITE(csa, udi, udi->fn, udi->fd, (off_t)0, buff, csd_size, status); if (0 != status) { RNDWN_ERR("!AD -> Error writing header to disk.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } } if (NULL != tsd) { assert(!db_shm_in_sync); free(tsd); tsd = NULL; } # if !defined(_AIX) if ((dba_mm == acc_meth) && db_shm_in_sync) { assert(0 != mmap_sz); if (-1 == msync((caddr_t)csa->db_addrs[0], mmap_sz, MS_SYNC)) { RNDWN_ERR("!AD -> Error synchronizing mapped memory.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } if (-1 == munmap((caddr_t)csa->db_addrs[0], mmap_sz)) { RNDWN_ERR("!AD -> Error unmapping mapped memory.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } } # endif IF_LIBAIO(aio_shim_destroy(udi->owning_gd);) /* If this is a BASEDB and we are the last one out, unlink/remove the corresponding STASDB if one exists */ /* Note that if "is_statsdb" is TRUE and basedb was not found, we cannot safely delete the statsdb (deletion * of statsdb requires ftok lock on the basedb). In that case, we leave it as is. And when the basedb is next * opened, we will remove this statsdb and create a new one. */ UNLINK_STATSDB_AT_BASEDB_RUNDOWN(cnl); break; } /* Detach from shared memory whether it is a GT.M shared memory or not */ if (-1 == shmdt((caddr_t)cnl)) { assert(FALSE); RNDWN_ERR("!AD -> Error detaching from shared memory.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } csa->nl = cnl = NULL; csa->hdr = NULL; /* Remove the shared memory only if it is a GT.M created one. */ if (is_gtm_shm) { if (remove_mlk_shmid) { if (0 != shm_rmid(mlk_shmid)) { save_errno = errno; if (!SHM_REMOVED(save_errno)) { assert(FALSE); RNDWN_ERR("!AD -> Error removing lock shared memory.", reg); /* Fall through and attempt removing main shared memory */ } } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_SHMREMOVED, 3, mlk_shmid, DB_LEN_STR(reg)); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_SHMREMOVED, 3, mlk_shmid, DB_LEN_STR(reg)); } } if (remove_shmid) /* Note: remove_shmid is defined only if is_gtm_shm is TRUE */ { if (0 != shm_rmid(udi->shmid)) { save_errno = errno; if (!SHM_REMOVED(save_errno)) { assert(FALSE); RNDWN_ERR("!AD -> Error removing shared memory.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_SHMREMOVED, 3, udi->shmid, DB_LEN_STR(reg)); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_SHMREMOVED, 3, udi->shmid, DB_LEN_STR(reg)); } } udi->shmid = INVALID_SHMID; udi->gt_shm_ctime = 0; } /* If the shared memory was found to a valid GT.M created segment and if it was properly initialized, we would * have cleared shmid in the database file header to INVALID_SHMID. If not, do the reset now as we nevertheless * don't want the database to connect to that non-GT.M-shmid or uninitialized-GT.M-shmid anymore. */ if (!is_gtm_shm || !glob_sec_init) { assert(NULL != tsd); /* should not have been freed */ RESET_SHMID_CTIME(tsd); if (mupip_jnl_recover) memset(tsd->machine_name, 0, MAX_MCNAMELEN); ALIGN_BUFF_IF_NEEDED_FOR_DIO(udi, buff, tsd, tsd_size); /* sets "buff" */ if (0 == memcmp(tsd, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv((sgmnt_data_ptr_t)buff); DB_LSEEKWRITE(csa, udi, udi->fn, udi->fd, (off_t)0, buff, tsd_size, status); if (0 != status) { RNDWN_ERR("!AD -> Unable to write header to disk.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } free(tsd); tsd = NULL; } assert(INVALID_SHMID == udi->shmid); assert(0 == udi->gt_shm_ctime); assert(udi->grabbed_access_sem); assert(!db_shm_in_sync || udi->counter_acc_incremented); assert(INVALID_SEMID != udi->semid); if (!standalone && (db_shm_in_sync || sem_created)) { if (0 != sem_rmid(udi->semid)) { assert(FALSE); /* We've created the semaphore, so we should be able to remove it */ RNDWN_ERR("!AD -> Error removing semaphore.", reg); MU_RNDWN_FILE_CLNUP(reg, udi, tsd, sem_created, udi->counter_acc_incremented); return FALSE; } if (!sem_created) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(3) ERR_SEMREMOVED, 1, udi->semid); gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(3) ERR_SEMREMOVED, 1, udi->semid); } udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; udi->semid = INVALID_SEMID; sem_created = FALSE; } REVERT; RESET_GV_CUR_REGION; /* We successfully ran down the database. So restore udi->counter_ftok_incremented before "ftok_sem_release" * that way the ftok semaphore gets deleted correctly as part of the counter decrement. */ udi->counter_ftok_incremented = !ftok_counter_halted; if (!ftok_sem_release(reg, udi->counter_ftok_incremented && !standalone, !standalone)) return FALSE; /* if "standalone" we better leave this function with standalone access */ assert(!standalone || udi->grabbed_access_sem); CLOSEFILE_RESET(udi->fd, rc); /* resets "udi->fd" to FD_INVALID */ DEBUG_ONLY(in_mu_rndwn_file = FALSE); return statsDBrundown_status; } CONDITION_HANDLER(mu_rndwn_file_ch) { unix_db_info *udi; sgmnt_addrs *csa; START_CH(TRUE); PRN_ERROR; assert(NULL != rundown_reg); if (NULL != rundown_reg) { udi = FILE_INFO(rundown_reg); csa = &udi->s_addrs; DEBUG_ONLY(in_mu_rndwn_file = FALSE); TREF(donot_write_inctn_in_wcs_recover) = FALSE; if (udi->counter_acc_incremented) { /* Decrement the access control semaphore in case we incremented it. */ do_semop(udi->semid, DB_CONTROL_SEM, -1, IPC_NOWAIT | SEM_UNDO); udi->counter_acc_incremented = FALSE; } if (sem_created || (shm_status_confirmed && no_shm_exists)) { /* Remove the access control semaphore if either we just created it, or we know for a fact that there is * no shared memory */ sem_rmid(udi->semid); udi->semid = INVALID_SEMID; } if (udi->grabbed_ftok_sem) ftok_sem_release(rundown_reg, udi->counter_ftok_incremented, !mu_rndwn_file_standalone); } RESET_GV_CUR_REGION; rundown_reg->open = FALSE; /* We want to proceed to the next condition handler in case we have stand-alone access, because if an error happens on one * region, we should signal an issue and not proceed to the next region. Otherwise, we try to rundown the next region. */ if (mu_rndwn_file_standalone) { NEXTCH; } else { UNWIND(NULL, NULL); } } fis-gtm-V7.0-005/sr_unix/mu_rndwn_file.h0000755000032200000250000000426714342376330017056 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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. * * * ****************************************************************/ #ifndef MU_RNDWN_DEFINED #define MU_RNDWN_DEFINED /* for FTOK_SEM_PER_ID */ #include "gtm_sem.h" #define SHMID 2 #ifdef __linux__ #define KEY 1 #define IPCS_CMD_STR "ipcs -m | grep '^0x'" #define IPCS_SEM_CMD_STR "ipcs -s | grep '^0x'" #else #define KEY 3 /* though the extra blank space is required in AIX under certain cases, we * are adding it for all UNIX versions to avoid another ifdef for AIX. */ #define IPCS_CMD_STR "ipcs -m | grep '^m' | sed 's/^m/m /g'" #define IPCS_SEM_CMD_STR "ipcs -s | grep '^s' | sed 's/^s/s /g'" #endif /* __linux__ */ #define SGMNTSIZ 10 #define MAX_PARM_LEN 128 #define MAX_ENTRY_LEN 1024 typedef struct shm_parms_struct { ssize_t sgmnt_siz; int shmid; key_t key; } shm_parms; #define RNDWN_ERR(str, reg) \ { \ save_errno = errno; \ if (reg) \ util_out_print(str, TRUE, DB_LEN_STR(reg)); \ else \ util_out_print(str, TRUE); \ util_out_print(STRERROR(save_errno), TRUE); \ } #define CONVERT_TO_NUM(ENTRY) \ { \ if (!parm) \ { \ free(parm_buff); \ return NULL; \ } \ if (cli_is_dcm(parm)) \ parm_buff->ENTRY = (int)(STRTOUL(parm, NULL, 10)); \ else if (cli_is_hex(parm + 2)) \ parm_buff->ENTRY = (int)(STRTOUL(parm, NULL, 16)); \ else \ { \ assert(FALSE); \ free(parm_buff); \ free(parm); \ return NULL; \ } \ free(parm); \ } STATICFNDCL boolean_t mu_rndwn_file_statsdb(gd_region *statsDBreg, boolean_t *statsdb_exists, boolean_t standalone); boolean_t mu_rndwn_file(gd_region *reg, boolean_t standalone); #endif fis-gtm-V7.0-005/sr_unix/mu_rndwn_repl_instance.c0000644000032200000250000004375714342376330020764 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_inet.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include #include #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "repl_msg.h" #include "repl_instance.h" #include "gtmsource.h" #include "gtmrecv.h" #include "iosp.h" #include "gtm_stdio.h" #include "gtmio.h" #include "gtm_string.h" #include "gtm_logicals.h" #include "repl_sem.h" #include "mu_rndwn_replpool.h" #include "mu_rndwn_repl_instance.h" #include "mu_gv_cur_reg_init.h" #include "gtm_sem.h" #include "gtmmsg.h" #include "gtm_ipc.h" #include "eintr_wrappers.h" #include "ftok_sems.h" #include "mu_rndwn_all.h" #include "util.h" #include "ipcrmid.h" /* for sem_rmid() prototype */ #include "buddy_list.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "hashtab_mname.h" /* needed for muprec.h */ #include "muprec.h" #include "error.h" #include "anticipatory_freeze.h" #include "mutex.h" #include "do_semop.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF recvpool_addrs recvpool; GBLREF gd_region *gv_cur_region; GBLREF gd_region *ftok_sem_reg; GBLREF jnl_gbls_t jgbl; GBLREF mur_opt_struct mur_options; GBLREF mur_gbls_t murgbl; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; GBLREF gd_addr *gd_header; error_def(ERR_NOMORESEMCNT); error_def(ERR_MUJPOOLRNDWNFL); error_def(ERR_MUJPOOLRNDWNSUC); error_def(ERR_MURPOOLRNDWNFL); error_def(ERR_MURPOOLRNDWNSUC); error_def(ERR_REPLACCSEM); error_def(ERR_SEMREMOVED); error_def(ERR_SYSCALL); /* * This will rundown a replication instance journal (and receiver) pool. * Input Parameter: * replpool_id of the instance. Instance file name must be null terminated in replpool_id. * Returns : * TRUE, if successful. * FALSE, otherwise. */ boolean_t mu_rndwn_repl_instance(replpool_identifier *replpool_id, boolean_t immediate, boolean_t rndwn_both_pools, boolean_t *jnlpool_sem_created) { boolean_t jnlpool_stat = SS_NORMAL, recvpool_stat = SS_NORMAL, decr_cnt, sem_created = FALSE, ipc_rmvd; char *instfilename; unsigned char ipcs_buff[MAX_IPCS_ID_BUF], *ipcs_ptr; gd_region *r_save; repl_inst_hdr repl_instance; static gd_region *reg = NULL; struct shmid_ds shmstat; union semun semarg; unix_db_info *udi; int save_errno, sem_id, shm_id, status; sgmnt_addrs *repl_csa; boolean_t was_crit, remove_sem, ftok_counter_halted = FALSE; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); /* ROLLBACK -FORWARD should not call this function */ if (NULL == reg) { r_save = gv_cur_region; mu_gv_cur_reg_init(); reg = gv_cur_region; gv_cur_region = r_save; } *jnlpool_sem_created = FALSE; /* Assert that the layout of replpool_identifier is identical for all versions going forward as the function * "validate_replpool_shm_entry" (used by the argumentless mupip rundown aka "mupip rundown") relies on this. * This assert is placed here (instead of there) because the automated tests exercise this logic much more * than the argumentless code. If any of these asserts fail, "validate_replpool_shm_entry" needs to change * to handle the old and new layouts. * * Structure ----> replpool_identifier <---- size 312 [0x0138] * * offset = 0000 [0x0000] size = 0012 [0x000c] ----> replpool_identifier.label * offset = 0012 [0x000c] size = 0001 [0x0001] ----> replpool_identifier.pool_type * offset = 0013 [0x000d] size = 0036 [0x0024] ----> replpool_identifier.now_running * offset = 0052 [0x0034] size = 0004 [0x0004] ----> replpool_identifier.repl_pool_key_filler * offset = 0056 [0x0038] size = 0256 [0x0100] ----> replpool_identifier.instfilename */ assert(0 == OFFSETOF(replpool_identifier, label[0])); assert(12 == SIZEOF(((replpool_identifier *)NULL)->label)); assert(12 == OFFSETOF(replpool_identifier, pool_type)); assert(1 == SIZEOF(((replpool_identifier *)NULL)->pool_type)); assert(13 == OFFSETOF(replpool_identifier, now_running[0])); assert(36 == SIZEOF(((replpool_identifier *)NULL)->now_running)); assert(56 == OFFSETOF(replpool_identifier, instfilename[0])); assert(256 == SIZEOF(((replpool_identifier *)NULL)->instfilename)); /* End asserts */ if (NULL != jnlpool) jnlpool->jnlpool_dummy_reg = reg; recvpool.recvpool_dummy_reg = reg; instfilename = replpool_id->instfilename; reg->dyn.addr->fname_len = strlen(instfilename); assert(0 == instfilename[reg->dyn.addr->fname_len]); memcpy((char *)reg->dyn.addr->fname, instfilename, reg->dyn.addr->fname_len + 1); udi = FILE_INFO(reg); udi->fn = (char *)reg->dyn.addr->fname; ftok_sem_reg = NULL; /* clean any residue from region work as we have now moved on to an instance */ /* Lock replication instance using ftok semaphore so that no other replication process can startup until we are done with * rundown */ if (!ftok_sem_get(reg, TRUE, REPLPOOL_ID, immediate, &ftok_counter_halted)) return FALSE; ESTABLISH_RET(mu_rndwn_repl_instance_ch, FALSE); repl_inst_read(instfilename, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr)); assert(rndwn_both_pools || JNLPOOL_SEGMENT == replpool_id->pool_type || RECVPOOL_SEGMENT == replpool_id->pool_type); /* At this point, we have not yet attached to the jnlpool so we do not know if the ftok counter got halted * previously or not. So be safe and assume it has halted in case the jnlpool_shmid indicates it is up and running. * We will set udi->counter_ftok_incremented back to an accurate value after we attach to the jnlpool. * This means we might not delete the ftok semaphore in some cases of error codepaths but it should be rare * and is better than incorrectly deleting it while live processes are concurrently using it. */ udi->counter_ftok_incremented = udi->counter_ftok_incremented && (INVALID_SHMID == repl_instance.jnlpool_shmid); if (rndwn_both_pools || (JNLPOOL_SEGMENT == replpool_id->pool_type)) { /* -------------------------- * First rundown Journal pool * -------------------------- */ shm_id = repl_instance.jnlpool_shmid; if (SS_NORMAL == (jnlpool_stat = mu_replpool_grab_sem(&repl_instance, JNLPOOL_SEGMENT, &sem_created, immediate))) { /* Got JNL_POOL_ACCESS_SEM and incremented SRC_SRV_COUNT_SEM */ assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); assert(holds_sem[SOURCE][SRC_SERV_COUNT_SEM]); sem_id = repl_instance.jnlpool_semid; if ((INVALID_SHMID == shm_id) || (-1 == shmctl(shm_id, IPC_STAT, &shmstat)) || (shmstat.shm_ctime != repl_instance.jnlpool_shmid_ctime)) { repl_instance.jnlpool_shmid = shm_id = INVALID_SHMID; repl_instance.jnlpool_shmid_ctime = 0; } assert((INVALID_SHMID != shm_id) || ((NULL == jnlpool) || (NULL == jnlpool->jnlpool_ctl))); ipc_rmvd = TRUE; if (INVALID_SHMID != shm_id) { replpool_id->pool_type = JNLPOOL_SEGMENT; jnlpool_stat = mu_rndwn_replpool(replpool_id, &repl_instance, shm_id, &ipc_rmvd); ipcs_ptr = i2asc((uchar_ptr_t)ipcs_buff, shm_id); *ipcs_ptr = '\0'; if (rndwn_both_pools && ((SS_NORMAL != jnlpool_stat) || ipc_rmvd)) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) (jnlpool_stat ? ERR_MUJPOOLRNDWNFL : ERR_MUJPOOLRNDWNSUC), 4, LEN_AND_STR(ipcs_buff), LEN_AND_STR(instfilename)); } assert(ipc_rmvd || (jnlpool && (NULL != jnlpool->jnlpool_ctl)) || !mur_options.rollback); assert((!jnlpool || (NULL == jnlpool->jnlpool_ctl)) || (SS_NORMAL == jnlpool_stat) || jgbl.onlnrlbk); assert((INVALID_SHMID != repl_instance.jnlpool_shmid) || (0 == repl_instance.jnlpool_shmid_ctime)); assert((INVALID_SHMID == repl_instance.jnlpool_shmid) || (0 != repl_instance.jnlpool_shmid_ctime)); assert(INVALID_SEMID != sem_id); if (!mur_options.rollback) { /* Invoked by MUPIP RUNDOWN in which case the semaphores needs to be removed. But, remove the * semaphore ONLY if we created it here OR the journal pool was successfully removed. */ if ((NULL == jnlpool) || (NULL == jnlpool->jnlpool_ctl)) { remove_sem = (sem_created || ((SS_NORMAL == jnlpool_stat) && ipc_rmvd)); if (!remove_sem) add_to_semids_list(repl_instance.jnlpool_semid); status = mu_replpool_release_sem(&repl_instance, JNLPOOL_SEGMENT, remove_sem); if ((SS_NORMAL == status) && remove_sem) { /* Now that semaphores are removed, reset fields in file header */ if (!sem_created) { /* If sem_id was created by mu_replpool_grab_sem then do NOT report the * MURPOOLRNDWNSUC message as it indicates that the semaphore was orphaned * and we removed it when in fact there was no orphaned semaphore and we * created it as part of mu_replpool_grab_sem to get standalone access to * rundown the receiver pool (which may or may not exist) */ ipcs_ptr = i2asc((uchar_ptr_t)ipcs_buff, sem_id); *ipcs_ptr = '\0'; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_MUJPOOLRNDWNSUC, 4, LEN_AND_STR(ipcs_buff), LEN_AND_STR(instfilename), ERR_SEMREMOVED, 1, sem_id); } repl_inst_jnlpool_reset(); } } else { /* This is MUPIP RUNDOWN -REG "*" and anticipatory freeze scheme is turned ON. * So, release just the JNL_POOL_ACCESS_SEM. The semaphore will be released/removed * in the caller (mupip_rundown). */ assert(INST_FREEZE_ON_ERROR_POLICY); assertpro(SS_NORMAL == (status = rel_sem(SOURCE, JNL_POOL_ACCESS_SEM))); assert(!holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); /* Since we are not resetting the semaphore IDs in the file header, we need to write out * the semaphore IDs in the instance file (if we created them). */ if (sem_created) repl_inst_write(instfilename, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr)); } } } else if (rndwn_both_pools && (INVALID_SHMID != shm_id)) { ipcs_ptr = i2asc((uchar_ptr_t)ipcs_buff, shm_id); *ipcs_ptr = '\0'; if (rndwn_both_pools) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUJPOOLRNDWNFL, 4, LEN_AND_STR(ipcs_buff), LEN_AND_STR(instfilename)); } *jnlpool_sem_created = sem_created; } if (((SS_NORMAL == jnlpool_stat) || !jgbl.mur_rollback) && (rndwn_both_pools || (RECVPOOL_SEGMENT == replpool_id->pool_type))) { /* -------------------------- * Now rundown Receivpool * -------------------------- * Note: RECVPOOL is rundown ONLY if the JNLPOOL rundown was successful. This way, we don't end up * creating new semaphores for the RECVPOOL if ROLLBACK is not going to start anyways because of the failed * JNLPOOL rundown. The only exception is MUPIP RUNDOWN command in which case we try running down the * RECVPOOL even if the JNLPOOL rundown failed. */ shm_id = repl_instance.recvpool_shmid; if (SS_NORMAL == (recvpool_stat = mu_replpool_grab_sem(&repl_instance, RECVPOOL_SEGMENT, &sem_created, immediate))) { sem_id = repl_instance.recvpool_semid; if ((INVALID_SHMID == shm_id) || (-1 == shmctl(shm_id, IPC_STAT, &shmstat)) || (shmstat.shm_ctime != repl_instance.recvpool_shmid_ctime)) { repl_instance.recvpool_shmid = shm_id = INVALID_SHMID; repl_instance.recvpool_shmid_ctime = 0; } ipc_rmvd = TRUE; if (INVALID_SHMID != shm_id) { replpool_id->pool_type = RECVPOOL_SEGMENT; recvpool_stat = mu_rndwn_replpool(replpool_id, &repl_instance, shm_id, &ipc_rmvd); ipcs_ptr = i2asc((uchar_ptr_t)ipcs_buff, shm_id); *ipcs_ptr = '\0'; if (rndwn_both_pools && ((SS_NORMAL != recvpool_stat) || ipc_rmvd)) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) (recvpool_stat ? ERR_MURPOOLRNDWNFL : ERR_MURPOOLRNDWNSUC), 4, LEN_AND_STR(ipcs_buff), LEN_AND_STR(instfilename)); } assert((TRUE == ipc_rmvd) || (SS_NORMAL != recvpool_stat) || jgbl.onlnrlbk); assert((INVALID_SHMID != repl_instance.recvpool_shmid) || (0 == repl_instance.recvpool_shmid_ctime)); assert((INVALID_SHMID == repl_instance.recvpool_shmid) || (0 != repl_instance.recvpool_shmid_ctime)); assert(INVALID_SEMID != sem_id); if (!mur_options.rollback) { /* Invoked by MUPIP RUNDOWN in which case the semaphores needs to be removed. But, remove the * semaphore ONLY if we created it here OR the receive pool was successfully removed. */ remove_sem = sem_created || (SS_NORMAL == jnlpool_stat); if (!remove_sem) add_to_semids_list(repl_instance.jnlpool_semid); status = mu_replpool_release_sem(&repl_instance, RECVPOOL_SEGMENT, remove_sem); if ((SS_NORMAL == status) && remove_sem) { /* Now that semaphores are removed, reset fields in file header */ if (!sem_created) { /* if sem_id was "created" by mu_replpool_grab_sem then do NOT report the * MURPOOLRNDWNSUC message as it indicates that the semaphore was orphaned and we * removed it when in fact there was no orphaned semaphore and we "created" it as * part of mu_replpool_grab_sem to get standalone access to rundown the receiver * pool (which may or may not exist) */ ipcs_ptr = i2asc((uchar_ptr_t)ipcs_buff, sem_id); *ipcs_ptr = '\0'; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_MURPOOLRNDWNSUC, 4, LEN_AND_STR(ipcs_buff), LEN_AND_STR(instfilename), ERR_SEMREMOVED, 1, sem_id); } if (jnlpool && (NULL != jnlpool->jnlpool_ctl)) { /* Journal pool is not yet removed. So, grab lock before resetting semid/shmid * fields in the file header as the function expects the caller to hold crit * if the journal pool is available */ assert(INVALID_SHMID != repl_instance.jnlpool_shmid); repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; assert(!repl_csa->now_crit); assert(!repl_csa->hold_onto_crit); was_crit = repl_csa->now_crit; /* Since we do grab_lock, below, we need to do a per-process initialization. */ mutex_per_process_init(); if (!was_crit) grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY); } repl_inst_recvpool_reset(); if (((NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl)) && !was_crit) rel_lock(jnlpool->jnlpool_dummy_reg); } assert(!holds_sem[RECV][RECV_POOL_ACCESS_SEM]); } } else if (rndwn_both_pools && (INVALID_SHMID != shm_id)) { ipcs_ptr = i2asc((uchar_ptr_t)ipcs_buff, shm_id); *ipcs_ptr = '\0'; if (rndwn_both_pools) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MURPOOLRNDWNFL, 4, LEN_AND_STR(ipcs_buff), LEN_AND_STR(instfilename)); } } assert(jgbl.onlnrlbk || INST_FREEZE_ON_ERROR_POLICY || ((NULL == jnlpool) || (NULL == jnlpool->repl_inst_filehdr))); if (mur_options.rollback && (SS_NORMAL == jnlpool_stat) && (SS_NORMAL == recvpool_stat)) { assert(jgbl.onlnrlbk || INST_FREEZE_ON_ERROR_POLICY || ((INVALID_SHMID == repl_instance.jnlpool_shmid) && (INVALID_SHMID == repl_instance.recvpool_shmid))); /* Initialize jnlpool->repl_inst_filehdr as it is used later by gtmrecv_fetchresync() */ decr_cnt = FALSE; if ((NULL == jnlpool) || (NULL == jnlpool->repl_inst_filehdr)) { /* Possible if there is NO journal pool in the first place. In this case, malloc the structure here and * copy the file header from repl_instance structure. */ SET_JNLPOOL_FROM_RECVPOOL_P(jnlpool); jnlpool->repl_inst_filehdr = (repl_inst_hdr_ptr_t)malloc(SIZEOF(repl_inst_hdr)); memcpy(jnlpool->repl_inst_filehdr, &repl_instance, SIZEOF(repl_inst_hdr)); } else { assert(repl_instance.jnlpool_semid == jnlpool->repl_inst_filehdr->jnlpool_semid); assert(repl_instance.jnlpool_semid_ctime == jnlpool->repl_inst_filehdr->jnlpool_semid_ctime); assert(repl_instance.jnlpool_shmid == jnlpool->repl_inst_filehdr->jnlpool_shmid); assert(repl_instance.jnlpool_shmid_ctime == jnlpool->repl_inst_filehdr->jnlpool_shmid_ctime); /* If the ONLINE ROLLBACK command is run on the primary when the source server is up and running, * jnlpool->repl_inst_filehdr->recvpool_semid will be INVALID because there is NO receiver server * running. However, ROLLBACK creates semaphores for both journal pool and receive pool and writes * it to the instance file header. Copy this information to the file header copy in the jnlpool * as well */ jnlpool->repl_inst_filehdr->recvpool_semid = repl_instance.recvpool_semid; jnlpool->repl_inst_filehdr->recvpool_semid_ctime = repl_instance.recvpool_semid_ctime; } /* Flush changes to the replication instance file header to disk */ repl_inst_write(instfilename, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr)); } else /* for MUPIP RUNDOWN, semid fields in the file header are reset and is written in mu_replpool_release_sem() above */ /* for anticipatory freeze, "mupip_rundown" releases the ftok semaphore */ decr_cnt = (NULL == (jnlpool ? jnlpool->jnlpool_ctl : NULL)); REVERT; udi->counter_ftok_incremented = !ftok_counter_halted; /* Restore counter_ftok_incremented before release */ /* Release replication instance ftok semaphore lock. Do not decrement the counter if ROLLBACK */ if (!ftok_sem_release(reg, decr_cnt && udi->counter_ftok_incremented, immediate)) return FALSE; return ((SS_NORMAL == jnlpool_stat) && (SS_NORMAL == recvpool_stat)); } CONDITION_HANDLER(mu_rndwn_repl_instance_ch) { unix_db_info *udi; sgmnt_addrs *csa; gd_region *reg; START_CH(TRUE); reg = jnlpool ? jnlpool->jnlpool_dummy_reg : NULL; if (NULL == reg) reg = recvpool.recvpool_dummy_reg; assert(NULL != reg); if (NULL != reg) { udi = FILE_INFO(reg); csa = &udi->s_addrs; if (udi->grabbed_ftok_sem) ftok_sem_release(reg, udi->counter_ftok_incremented, TRUE); } NEXTCH; } fis-gtm-V7.0-005/sr_unix/mu_rndwn_repl_instance.h0000755000032200000250000000133614342376330020757 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #ifndef MU_RNDWN_REPL_INSTANCE_INCLUDED #define MU_RNDWN_REPL_INSTANCE_INCLUDED boolean_t mu_rndwn_repl_instance(replpool_identifier *replpool_id, boolean_t immediate, boolean_t rndwn_both_pools, boolean_t *jnlpool_sem_created); #endif /* MU_RNDWN_REPL_INSTANCE_INCLUDED */ fis-gtm-V7.0-005/sr_unix/mu_rndwn_replpool.c0000644000032200000250000003077414342376330017765 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_unistd.h" #include "gtm_inet.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_stat.h" #include "gtm_stdio.h" #include "gtm_fcntl.h" #include "gtm_ipc.h" #include #include #include #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "gtmio.h" #include "repl_instance.h" #include "mutex.h" #include "jnl.h" #include "repl_sem.h" #include "eintr_wrappers.h" #include "mu_rndwn_file.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" #include "mu_rndwn_replpool.h" #include "ipcrmid.h" #include "do_semop.h" #include "util.h" #include "gtmmsg.h" #include "gtm_sem.h" #include "do_shmat.h" /* for do_shmat() prototype */ #include "buddy_list.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "hashtab_mname.h" /* needed for muprec.h */ #include "muprec.h" #include "error.h" #include "anticipatory_freeze.h" GBLREF boolean_t argumentless_rundown; GBLREF jnl_gbls_t jgbl; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF recvpool_addrs recvpool; GBLREF mur_gbls_t murgbl; GBLREF mur_opt_struct mur_options; GBLREF uint4 mutex_per_process_init_pid; GBLREF uint4 process_id; LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; error_def(ERR_REPLACCSEM); error_def(ERR_REPLINSTOPEN); error_def(ERR_REPLPOOLINST); error_def(ERR_SYSCALL); error_def(ERR_TEXT); #define DETACH(START_ADDR, SHM_ID, INSTFILENAME) \ { \ int lcl_save_errno; \ \ if (-1 == shmdt((void *)START_ADDR)) \ { \ lcl_save_errno = errno; \ ISSUE_REPLPOOLINST(lcl_save_errno, SHM_ID, INSTFILENAME, "shmdt()"); \ } \ } int mu_rndwn_replpool2(replpool_identifier *replpool_id, repl_inst_hdr_ptr_t repl_inst_filehdr, int shm_id, boolean_t *ipc_rmvd, char *instfilename, sm_uc_ptr_t start_addr, int nattch) { int save_errno, status; char pool_type; unix_db_info *udi; sgmnt_addrs *csa; gd_region *reg; boolean_t anticipatory_freeze_available, reset_crash; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; pool_type = replpool_id->pool_type; anticipatory_freeze_available = INST_FREEZE_ON_ERROR_POLICY; /* assert that the identifiers are at the top of replpool control structure */ assert(0 == offsetof(jnlpool_ctl_struct, jnlpool_id)); assert(0 == offsetof(recvpool_ctl_struct, recvpool_id)); memcpy((void *)replpool_id, (void *)start_addr, SIZEOF(replpool_identifier)); if (memcmp(replpool_id->label, GDS_RPL_LABEL, GDS_LABEL_SZ - 1)) { if (!memcmp(replpool_id->label, GDS_RPL_LABEL, GDS_LABEL_SZ - 3)) util_out_print( "Incorrect version for the replpool segment (id = !UL) belonging to replication instance !AD", TRUE, shm_id, LEN_AND_STR(instfilename)); else util_out_print("Incorrect replpool format for the segment (id = !UL) belonging to replication instance !AD", TRUE, shm_id, LEN_AND_STR(instfilename)); DETACH(start_addr, shm_id, instfilename); return -1; } if (memcmp(replpool_id->now_running, gtm_release_name, gtm_release_name_len + 1)) { util_out_print("Attempt to access with version !AD, while already using !AD for replpool segment (id = !UL)" " belonging to replication instance !AD.", TRUE, gtm_release_name_len, gtm_release_name, LEN_AND_STR(replpool_id->now_running), shm_id, LEN_AND_STR(instfilename)); DETACH(start_addr, shm_id, instfilename); return -1; } reset_crash = (!anticipatory_freeze_available || argumentless_rundown); /* Assert that if we haven't yet attached to the journal pool yet, jnlpool_ctl better be NULL */ assert((JNLPOOL_SEGMENT != pool_type) || ((NULL == jnlpool) || (NULL == jnlpool->jnlpool_ctl))); if (JNLPOOL_SEGMENT == pool_type) { /* Initialize variables to simulate a "jnlpool_init". This is required by "repl_inst_flush_jnlpool" called below */ SET_JNLPOOL_FROM_RECVPOOL_P(jnlpool); jnlpool->jnlpool_ctl = (jnlpool_ctl_ptr_t)start_addr; assert(NULL != jnlpool->jnlpool_dummy_reg); udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); csa = &udi->s_addrs; csa->critical = (CRIT_PTR_T)((sm_uc_ptr_t)jnlpool->jnlpool_ctl + JNLPOOL_CTL_SIZE); csa->nl = (node_local_ptr_t)((sm_uc_ptr_t)csa->critical + JNLPOOL_CRIT_SPACE + SIZEOF(mutex_spin_parms_struct)); /* secshr_db_clnup uses this relationship */ assert(jnlpool->jnlpool_ctl->filehdr_off); assert(jnlpool->jnlpool_ctl->srclcl_array_off > jnlpool->jnlpool_ctl->filehdr_off); assert(jnlpool->jnlpool_ctl->sourcelocal_array_off > jnlpool->jnlpool_ctl->srclcl_array_off); /* Initialize "jnlpool->repl_inst_filehdr" and related fields as "repl_inst_flush_jnlpool" relies on that */ jnlpool->repl_inst_filehdr = (repl_inst_hdr_ptr_t)((sm_uc_ptr_t)jnlpool->jnlpool_ctl + jnlpool->jnlpool_ctl->filehdr_off); jnlpool->gtmsrc_lcl_array = (gtmsrc_lcl_ptr_t)((sm_uc_ptr_t)jnlpool->jnlpool_ctl + jnlpool->jnlpool_ctl->srclcl_array_off); jnlpool->gtmsource_local_array = (gtmsource_local_ptr_t)((sm_uc_ptr_t)jnlpool->jnlpool_ctl + jnlpool->jnlpool_ctl->sourcelocal_array_off); if (0 == nattch) { /* No one attached. So, we can safely flush the journal pool so that the gtmsrc_lcl structures in the * jnlpool and disk are in sync with each other. More importantly we are about to remove the jnlpool * so we better get things in sync before that. If anticipatory freeze scheme is in effect, then we * need to keep the journal pool up and running. So, don't reset the crash field in the instance file * header (dictated by the second parameter to repl_inst_flush_jnlpool below). * Note: * If mu_rndwn_repl_instance created new semaphores (in mu_replpool_remove_sem), we need to flush those * to the instance file as well. So, override the jnlpool_semid and jnlpool_semid_ctime with the new * values. */ assert((INVALID_SEMID != repl_inst_filehdr->jnlpool_semid) && (0 != repl_inst_filehdr->jnlpool_semid_ctime)); jnlpool->repl_inst_filehdr->jnlpool_semid = repl_inst_filehdr->jnlpool_semid; jnlpool->repl_inst_filehdr->jnlpool_semid_ctime = repl_inst_filehdr->jnlpool_semid_ctime; repl_inst_flush_jnlpool(FALSE, reset_crash); assert(!jnlpool->repl_inst_filehdr->crash || !reset_crash); /* Refresh local copy (repl_inst_filehdr) with the copy that was just flushed (jnlpool->repl_inst_filehdr) */ memcpy(repl_inst_filehdr, jnlpool->repl_inst_filehdr, SIZEOF(repl_inst_hdr)); if (reset_crash) { /* Now that jnlpool has been flushed and there is going to be no journal pool, reset * "jnlpool->repl_inst_filehdr" as otherwise other routines (e.g. "repl_inst_recvpool_reset") are * affected by whether this is NULL or not. */ JNLPOOL_CLEAR_FIELDS(jnlpool); } } /* else we are ONLINE ROLLBACK. repl_inst_flush_jnlpool will be done later after gvcst_init in mur_open_files */ } if ((0 == nattch) && (reset_crash || (RECVPOOL_SEGMENT == pool_type))) { if (-1 == shmdt((caddr_t)start_addr)) { save_errno = errno; ISSUE_REPLPOOLINST(save_errno, shm_id, instfilename, "shmdt()"); return -1; } if (0 != shm_rmid(shm_id)) { save_errno = errno; ISSUE_REPLPOOLINST(save_errno, shm_id, instfilename, "shm_rmid()"); return -1; } if (JNLPOOL_SEGMENT == pool_type) { repl_inst_filehdr->jnlpool_shmid = INVALID_SHMID; repl_inst_filehdr->jnlpool_shmid_ctime = 0; assert((NULL == jnlpool) || ((NULL == jnlpool->jnlpool_ctl))); *ipc_rmvd = TRUE; } else { repl_inst_filehdr->recvpool_shmid = INVALID_SHMID; repl_inst_filehdr->recvpool_shmid_ctime = 0; if ((NULL != jnlpool) && (NULL != jnlpool->repl_inst_filehdr)) { jnlpool->repl_inst_filehdr->recvpool_shmid = INVALID_SHMID; jnlpool->repl_inst_filehdr->recvpool_shmid_ctime = 0; } *ipc_rmvd = TRUE; } } else { assert((JNLPOOL_SEGMENT != pool_type) || ((NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl))); if (JNLPOOL_SEGMENT == pool_type) { *ipc_rmvd = FALSE; /* Caller can be MUPIP RUNDOWN (-reg * OR argumentless) or MUPIP ROLLBACK (online or standalone flavor). * In all cases except the argumentless MUPIP RUNDOWN case return with jnlpool_ctl as is. Here is why. * In the rollback case, we are ONLINE ROLLBACK OR anticipatory freeze is in effect and so we want to * keep the journal pool available for the duration of the rollback (to record errors and trigger instance * freeze). Do not detach from the journal pool in that case. * In the rundown -reg * case, anticipatory freeze is in effect and we want to keep the journal pool * available for the duration of the rundown (to record errors and trigger instance freeze). The actual * jnlpool detach will happen in the caller ("mupip_rundown") once all regions have been rundown. * In the argumentless rundown case, detach from the jnlpool. This does not honor the custom errors scheme * and we do not want to be attached to a LOT of journal pools as the argumentless rundown proceeds * (virtual memory bloat etc.). */ if (argumentless_rundown) { JNLPOOL_SHMDT(jnlpool, status, save_errno); assert(0 == status); /* even if shmdt fails, there is not much we can do so move on in pro */ } } if (RECVPOOL_SEGMENT == pool_type) *ipc_rmvd = FALSE; } return 0; } int mu_rndwn_replpool(replpool_identifier *replpool_id, repl_inst_hdr_ptr_t repl_inst_filehdr, int shm_id, boolean_t *ipc_rmvd) { int status, save_errno, nattch; char *instfilename; sm_uc_ptr_t start_addr; struct shmid_ds shm_buf; boolean_t force_attach; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(INVALID_SHMID != shm_id); instfilename = replpool_id->instfilename; assert((JNLPOOL_SEGMENT == replpool_id->pool_type) || (RECVPOOL_SEGMENT == replpool_id->pool_type)); assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); /* ROLLBACK -FORWARD should not call this function */ force_attach = (jgbl.onlnrlbk || (!jgbl.mur_rollback && !argumentless_rundown && INST_FREEZE_ON_ERROR_POLICY)); if (-1 == shmctl(shm_id, IPC_STAT, &shm_buf)) { save_errno = errno; ISSUE_REPLPOOLINST(save_errno, shm_id, instfilename, "shmctl()"); return -1; } nattch = shm_buf.shm_nattch; if ((0 != nattch) && !force_attach) { util_out_print("Replpool segment (id = !UL) for replication instance !AD is in use by another process.", TRUE, shm_id, LEN_AND_STR(instfilename)); return -1; } if (-1 == (sm_long_t)(start_addr = (sm_uc_ptr_t) do_shmat(shm_id, 0, 0))) { save_errno = errno; ISSUE_REPLPOOLINST(save_errno, shm_id, instfilename, "shmat()"); return -1; } ESTABLISH_RET(mu_rndwn_replpool_ch, -1); status = mu_rndwn_replpool2(replpool_id, repl_inst_filehdr, shm_id, ipc_rmvd, instfilename, start_addr, nattch); REVERT; return status; } CONDITION_HANDLER(mu_rndwn_replpool_ch) { unix_db_info *udi; int status, save_errno; int jnlpool_shmid; START_CH(TRUE); PRN_ERROR; /* flush the error string */ if (SEVERITY == SEVERE) NEXTCH; /* If we have a journal pool, detach it to prevent leaking the shared memory. * However, if IFOE is configured, we may need the journal pool attached so that we can check for instance freeze * in database rundown. * In that case, the detach will happen automatically when the process terminates. * On the other hand, we don't respect IFOE in argumentless rundown, so go ahead and detach in that case, since otherwise * we could potentially leak the shared memory of multiple journal pools. */ if (((NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl)) && (!INST_FREEZE_ON_ERROR_POLICY || argumentless_rundown)) { jnlpool_shmid = jnlpool->repl_inst_filehdr->jnlpool_shmid; JNLPOOL_SHMDT(jnlpool, status, save_errno); if (0 > status) { assert(NULL != jnlpool->jnlpool_dummy_reg); udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); assert(ERR_REPLINSTOPEN == SIGNAL); /* only reason we know why mu_rndwn_replpool can fail */ ISSUE_REPLPOOLINST(save_errno, jnlpool_shmid, udi->fn, "shmdt()"); } } UNWIND(NULL, NULL); } fis-gtm-V7.0-005/sr_unix/mu_rndwn_replpool.h0000755000032200000250000000361014342376330017762 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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. * * * ****************************************************************/ #ifndef MU_RNDWN_REPLPOOL_INCLUDED #define MU_RNDWN_REPLPOOL_INCLUDED int mu_rndwn_replpool(replpool_identifier *replpool_id, repl_inst_hdr_ptr_t repl_inst_filehdr, int shmid, boolean_t *ipc_rmvd); int mu_rndwn_replpool2(replpool_identifier *replpool_id, repl_inst_hdr_ptr_t repl_inst_filehdr, int shm_id, boolean_t *ipc_rmvd, char *instfilename, sm_uc_ptr_t start_addr, int nattch); int mu_replpool_grab_sem(repl_inst_hdr_ptr_t repl_inst_filehdr, char pool_type, boolean_t *sem_created, boolean_t immediate); int mu_replpool_release_sem(repl_inst_hdr_ptr_t repl_inst_filehdr, char pool_type, boolean_t remove_sem); #define MAX_IPCS_ID_BUF 64 /* Shared memory or semaphore ID is an int and so won't be more than 12 digits long */ #define ISSUE_REPLPOOLINST(SAVE_ERRNO, SHM_ID, INSTFILENAME, FAILED_OP) \ { \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_REPLPOOLINST, 3, SHM_ID, LEN_AND_STR(INSTFILENAME)); \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT(FAILED_OP), CALLFROM, SAVE_ERRNO); \ } #define SET_JNLPOOL_FROM_RECVPOOL_P(JNLPOOL) \ { \ if (NULL == JNLPOOL) \ { \ JNLPOOL = malloc(SIZEOF(jnlpool_addrs)); \ memset(JNLPOOL, 0, SIZEOF(jnlpool_addrs)); \ JNLPOOL->jnlpool_dummy_reg = recvpool.recvpool_dummy_reg; \ JNLPOOL->recv_pool = TRUE; \ } \ } #endif /* MU_RNDWN_REPLPOOL_INCLUDED */ fis-gtm-V7.0-005/sr_unix/ojstartchild.c0000644000032200000250000010126614342376335016712 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* * ------------------------------------------------------- * This routine starts a new child process and passes the * job parameters to it. * ------------------------------------------------------- */ #include "mdef.h" #include "gtm_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" #include "gtm_limits.h" #include #include #if defined(SYS_ERRLIST_INCLUDE) && !defined(__CYGWIN__) #include SYS_ERRLIST_INCLUDE #endif #include "job.h" #include "error.h" #include #include "io.h" #include "iormdef.h" #include "io_params.h" #include "iosp.h" #include "eintr_wrappers.h" #include "compiler.h" #include "job_addr.h" #include "util.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gtmio.h" #include "fork_init.h" #include "rtnobj.h" #include "getjobnum.h" #include "zshow.h" #include "zwrite.h" #include "gtm_maxstr.h" #include "getzdir.h" #ifdef DEBUG #include #include #endif GBLREF bool jobpid; /* job's output files should have the pid appended to them. */ GBLREF volatile boolean_t ojtimeout; GBLREF boolean_t gtm_pipe_child; GBLREF int job_errno; GBLREF zshow_out *zwr_output; GBLREF uint4 pat_everything[]; GBLREF mstr_len_t sizeof_pat_everything; GBLREF io_pair io_curr_device; GBLREF boolean_t exit_handler_active; GBLREF boolean_t skip_exit_handler; static joberr_t joberr = joberr_gen; static int pipe_fd; static int setup_fds[2]; /* socket pair for setup of final mumps process */ static FILE *setup_file; static pid_t child_pid; #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 GBLREF char **environ; GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF boolean_t gtm_dist_ok_to_use; LITREF gtmImageName gtmImageNames[]; #define MAX_MUMPS_EXE_PATH_LEN 8192 #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=" #define JOB_CONTINUE 1 #define JOB_EXIT 0 #define KILL_N_REAP(PROCESS_ID, SIGNAL, RET_VAL) \ { \ if (-1 != (RET_VAL = kill(PROCESS_ID, SIGNAL))) \ { \ /* reap the just killed child, so there won't be any zombies */ \ WAITPID(PROCESS_ID, &wait_status, 0, done_pid); \ assert(done_pid == PROCESS_ID); \ } \ } #define FORK_RETRY(PID) \ { \ FORK(PID); \ while (-1 == PID) \ { \ assertpro(EAGAIN == errno || ENOMEM == errno); \ usleep(50000); \ FORK(PID); \ } \ } #define SETUP_OP_FAIL() \ { \ kill(child_pid, SIGTERM); \ joberr = joberr_io_setup_op_write; \ job_errno = errno; \ DOWRITERC(pipe_fd, &job_errno, SIZEOF(job_errno), pipe_status); \ assert(FALSE); \ UNDERSCORE_EXIT(joberr); \ } #define SETUP_DATA_FAIL() \ { \ kill(child_pid, SIGTERM); \ joberr = joberr_io_setup_write; \ job_errno = errno; \ DOWRITERC(pipe_fds[1], &job_errno, SIZEOF(job_errno), pipe_status); \ assert(FALSE); \ UNDERSCORE_EXIT(joberr); \ } error_def(ERR_GTMDISTUNVERIF); error_def(ERR_JOBFAIL); error_def(ERR_JOBLVN2LONG); error_def(ERR_JOBPARTOOLONG); error_def(ERR_LOGTOOLONG); error_def(ERR_TEXT); /* 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. */ STATICFNDCL void job_term_handler(int sig); STATICFNDCL int io_rename(job_params_msg *params, const int jobid); /* Middle process sets the entryref for the jobbed-off process by calling job_addr(). job_addr() internally calls op_zlink(). If * rts_error occurs when we are in the job_addr() function, the topmost condition handler inherited from parent process will be * executed. That condition handler might not be adequate to handle the rts_error happened in the context of middle process. * Hence middle process is provided with its own condition handler. */ static CONDITION_HANDLER(middle_child) { int pipe_status; START_CH(FALSE); /* As of now, middle process could encounter rts_error only while setting entryref. Hence the following assert. * Do assert after write to prevent parent hang. */ assert(joberr == joberr_rtn); DOWRITERC(pipe_fd, &job_errno, SIZEOF(job_errno), pipe_status); UNDERSCORE_EXIT(joberr); } /* Following condition handles the rts_error occurred in the grandchild before doing execv(). Since we have not started executing * M-cmd specified as a part of JOB command, it is enough to print the error and exit. */ static CONDITION_HANDLER(grand_child) { PRN_ERROR; UNDERSCORE_EXIT(EXIT_SUCCESS); } /* Clean up zwrite state if it threw an error in ojpassvar_hook() */ static CONDITION_HANDLER(ojlvzwr_ch) { START_CH(FALSE); TREF(in_zwrite) = FALSE; setup_file = NULL; TREF(zwrite_output_hook) = NULL; maxstr_stack_level--; NEXTCH; } /* This is to close the window of race condition where the timeout occurs and actually by that time, the middle process had already * successfully forked-off the job. */ STATICFNDEF void job_term_handler(int sig) { int ret; int status; joberr_t exit_status = joberr_gen; /* * ret = 0 - Child is present but not changed the state * < 0 - Error. No child present. * > 0 - Child PID. */ ret = waitpid(-1, &status, WNOHANG); /* BYPASSOK */ job_errno = errno; if (0 == ret) return; else if (0 > ret) { if (job_errno == ECHILD) exit_status = joberr_sig; else assert(FALSE); UNDERSCORE_EXIT(exit_status); } else return; } /* This function does not update params->output and params->error when it renames those files. This is fine for now since nothing * else after this point uses those JOB command parameters. */ STATICFNDEF int io_rename(job_params_msg *params, const int jobid) { char path[MAX_STDIOE_LEN]; STRNCPY_STR(path, params->output.buffer, params->output.len); SNPRINTF(&path[params->output.len], MAX_STDIOE_LEN - params->output.len, ".%d", jobid); if (rename(params->output.buffer, path)) { job_errno = errno; return(joberr_stdout_rename); } /* When OUTPUT and ERROR both point to the same file, rename it once */ if ((params->output.len == params->error.len) && (0 == STRNCMP_STR(params->output.buffer, params->error.buffer, params->output.len))) return 0; STRNCPY_STR(path, params->error.buffer, params->error.len); SNPRINTF(&path[params->error.len], MAX_STDIOE_LEN - params->error.len, ".%d", jobid); if (rename(params->error.buffer, path)) { job_errno = errno; return(joberr_stderr_rename); } return 0; } /* For use as a zwrite_output_hook to convert each formatted local into a message and send it to the setup_file. */ void ojpassvar_hook(void) { job_setup_op setup_op; int rc, pipe_status; job_buffer_size_msg buffer_size; size_t written; buffer_size = zwr_output->ptr - zwr_output->buff; if (buffer_size == 0) /* We can end up here if lvzwr_var()->lvzwr_out() sends the entire buffer so there's nothing left to send from * lvzwr_var() */ return; setup_op = job_set_locals; rc = gtm_fwrite(&setup_op, 1, SIZEOF(setup_op), setup_file, &written); if (0 < rc) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fwrite(op)"), CALLFROM, errno, 0); /* Crop the ' ;*' string at the end of the aliases */ if (zwr_output->buff[buffer_size - 1] == '*' && zwr_output->buff[buffer_size - 2] == ';' && zwr_output->buff[buffer_size - 3] == ' ') buffer_size -= 3; /* Always send the buffer size. If it is bigger than MAX_STRLEN, the child will handle this as a JOBLVN2LONG */ rc = gtm_fwrite(&buffer_size, 1, SIZEOF(buffer_size), setup_file, &written); if (0 < rc) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fwrite(size)"), CALLFROM, errno, 0); if (buffer_size > MAX_STRLEN) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_JOBLVN2LONG, 2, MAX_STRLEN, buffer_size); rc = gtm_fwrite(zwr_output->buff, 1, buffer_size, setup_file, &written); if (0 < rc) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fwrite(buf)"), CALLFROM, errno, 0); zwr_output->ptr = zwr_output->buff; return; } void local_variable_marshalling(FILE *setup_file_orig) { /* Setup buffer to write local variables one at a time */ zshow_out output; mval pattern; char buffer[MAX_STRLEN]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; memset(&output, 0, SIZEOF(output)); output.code = 'V'; output.type = ZSHOW_BUFF_ONLY; /* Expanded MAXSTR_BUFF_DECL/MAXSTR_BUFF_INIT below because they are designed to handle the size of MAX_STRBUFF_INIT when we * really want is MAX_STRLEN The condition handler established within MAXSTR_BUFF_INIT is futile because any error we * encounter here is fatal */ output.buff = buffer; output.size = MAX_STRLEN; maxstr_stack_level++; assert(maxstr_stack_level < MAXSTR_STACK_SIZE); maxstr_buff[maxstr_stack_level].len = output.size; maxstr_buff[maxstr_stack_level].addr = NULL; output.ptr = output.buff; pattern.str.addr = (char *)pat_everything; pattern.str.len = sizeof_pat_everything; pattern.mvtype = MV_STR; TREF(zwrite_output_hook) = (void *) &ojpassvar_hook; setup_file = setup_file_orig; ESTABLISH(ojlvzwr_ch); lvzwr_init(zwr_patrn_mval, &pattern); lvzwr_fini(&output, ZWRITE_END); REVERT; /* Keep the following in sync with ojlvzwr_ch() */ TREF(in_zwrite) = FALSE; setup_file = NULL; TREF(zwrite_output_hook) = NULL; maxstr_stack_level--; return; } /* -------------------------------------------------------------------------------------------------------------------------------- * 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 execs 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], cmdbuff[TEMP_BUFF_SIZE]; char tbuff[MAX_MUMPS_EXE_PATH_LEN], tbuff2[MAX_MUMPS_EXE_PATH_LEN], fname_buf[MAX_STDIOE_LEN]; char *pgbldir_str; char *transfer_addr; int4 index, environ_count, string_len, temp; int wait_status, save_errno, kill_ret; int rc, dup_ret, in_fd; int status; pid_t done_pid; job_parm *jp; rhdtyp *rtnhdr; struct sigaction act, old_act; int pipe_status, env_len; int mproc_fds[2]; /* pipe between middle process and grandchild process */ int decision; job_setup_op setup_op; job_params_msg params; job_arg_count_msg arg_count; job_arg_msg arg_msg; job_buffer_size_msg buffer_size; char *c1, *c2, **c3; char *argv[4]; char **env_ary, **env_ind; char **new_env_cur, **new_env_top, **old_env_cur, **old_env_top, *env_end; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Do the fork and exec but BEFORE that do a FFLUSH(NULL) to make sure any fclose (done in io_rundown * in the child process) does not affect file offsets in this (parent) process' file descriptors */ if (!gtm_dist_ok_to_use) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_GTMDISTUNVERIF, 4, STRLEN(gtm_dist), gtm_dist, gtmImageNames[image_type].imageNameLen, gtmImageNames[image_type].imageName); FFLUSH(NULL); FORK_RETRY(child_pid); 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. */ getjobnum(); /* set "process_id" to a value different from parent. This is particularly needed for the * RELINKCTL_RUNDOWN_MIDDLE_PARENT macro since it does rtnobj_shm_free which deals with latches * that in turn rely on the "process_id" global variable reflecting the current pid. */ skip_exit_handler = TRUE; /* The grandchild and the middle child should never execute gtm_exit_handler() */ /* 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; job_errno = -1; pipe_fd = pipe_fds[1]; ESTABLISH_RET(middle_child, 0); sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = job_term_handler; sigaction(SIGTERM, &act, &old_act); OPEN_PIPE(mproc_fds, pipe_status); if (-1 == pipe_status) { joberr = joberr_pipe_mgc; job_errno = errno; DOWRITERC(pipe_fds[1], &job_errno, SIZEOF(job_errno), pipe_status); assert(FALSE); UNDERSCORE_EXIT(joberr); } if (!IS_JOB_SOCKET(jparms->params.input.buffer, jparms->params.input.len)) { assert(MAX_STDIOE_LEN > jparms->params.input.len); /* Redirect input before potentially changing the default directory below.*/ strncpy(fname_buf, jparms->params.input.buffer, jparms->params.input.len); *(fname_buf + jparms->params.input.len) = '\0'; OPENFILE(fname_buf, O_RDONLY, in_fd); if (FD_INVALID == in_fd) { joberr = joberr_io_stdin_open; job_errno = errno; DOWRITERC(pipe_fds[1], &job_errno, SIZEOF(job_errno), pipe_status); assert(FALSE); UNDERSCORE_EXIT(joberr); } CLOSEFILE(0, rc); FCNTL3(in_fd, F_DUPFD, 0, dup_ret); if (-1 == dup_ret) { joberr = joberr_io_stdin_dup; job_errno = errno; DOWRITERC(pipe_fds[1], &job_errno, SIZEOF(job_errno), pipe_status); assert(FALSE); UNDERSCORE_EXIT(joberr); } # ifdef __MVS__ /* policy tagging because by default input is /dev/null */ if (-1 == gtm_zos_tag_to_policy(in_fd, TAG_UNTAGGED, &realfiletag)) TAG_POLICY_SEND_MSG(fname_buf, errno, realfiletag, TAG_UNTAGGED); # endif CLOSEFILE_RESET(in_fd, rc); /* resets "in_fd" to FD_INVALID */ } if (0 != jparms->params.directory.len) { /* If directory is specified, change it */ if (jparms->params.directory.len >= TEMP_BUFF_SIZE) { joberr = joberr_cd_toolong; DOWRITERC(pipe_fds[1], &job_errno, SIZEOF(job_errno), pipe_status); assert(FALSE); UNDERSCORE_EXIT(joberr); } strncpy(pbuff, jparms->params.directory.buffer, jparms->params.directory.len); *(pbuff + jparms->params.directory.len) = '\0'; if (CHDIR(pbuff) != 0) { joberr = joberr_cd; job_errno = errno; DOWRITERC(pipe_fds[1], &job_errno, SIZEOF(errno), pipe_status); UNDERSCORE_EXIT(joberr); } else /* update dollar_zdir with the new current working directory */ getzdir(); } /* 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))) { DOWRITERC(pipe_fds[1], &job_errno, SIZEOF(job_errno), pipe_status); UNDERSCORE_EXIT(status); } joberr = joberr_sid; if (-1 == setsid()) { job_errno = errno; DOWRITERC(pipe_fds[1], &job_errno, SIZEOF(job_errno), pipe_status); assert(FALSE); UNDERSCORE_EXIT(joberr); } # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_JOBFAIL_FILE_LIM)) { const struct rlimit flim = {2, 2}; if (-1 == setrlimit(RLIMIT_NOFILE, &flim)) { /* the white box test will fail bacause the error reported won't match expectations */ job_errno = errno; DOWRITERC(pipe_fds[1], &job_errno, SIZEOF(job_errno), pipe_status); assert(FALSE); /* core the middle child toward diagnosing why the white box case failed */ } } # endif joberr = joberr_sp; if (-1 == (rc = socketpair(AF_UNIX, SOCK_STREAM, 0, setup_fds))) { job_errno = errno; DOWRITERC(pipe_fds[1], &job_errno, SIZEOF(job_errno), pipe_status); assert(WBTEST_ENABLED(WBTEST_JOBFAIL_FILE_LIM)); UNDERSCORE_EXIT(joberr); } assert(!WBTEST_ENABLED(WBTEST_JOBFAIL_FILE_LIM)); /* Kill ourselves before we fork again */ if (WBTEST_ENABLED(WBTEST_SIGTERM_IN_JOB_CHILD)) kill(getpid(), SIGTERM); /* clone self and exit */ FORK_RETRY(child_pid); if (child_pid) { /* This is still the middle process. */ /* Close the read end of the pipe between middle process and grandchild process. */ CLOSEFILE_RESET(mproc_fds[0], pipe_status); /* resets "mproc_fds[0]" to FD_INVALID */ CLOSEFILE_RESET(setup_fds[1], pipe_status); /* resets "setup_fds[1]" to FD_INVALID */ assert(SIZEOF(pid_t) == SIZEOF(child_pid)); /* params data for 'output' and 'error' is populated here because io_rename() needs it in case appending of * JOB ID to Standard Output and Standard Error is required. */ params.output.len = jparms->params.output.len; memcpy(params.output.buffer, jparms->params.output.buffer, jparms->params.output.len); params.output.buffer[jparms->params.output.len] = '\0'; params.error.len = jparms->params.error.len; memcpy(params.error.buffer, jparms->params.error.buffer, jparms->params.error.len); params.error.buffer[jparms->params.error.len] = '\0'; /* if the Job pid need to be appended to the std out/err file names */ if (jobpid) { joberr = io_rename(¶ms, child_pid); if (joberr) { /* Inform grandchild that it will have to exit. If pipe operation failed terminate * the grandchild. */ decision = JOB_EXIT; DOWRITERC(mproc_fds[1], &decision, SIZEOF(decision), pipe_status); if (pipe_status) kill(child_pid, SIGTERM); DOWRITERC(pipe_fds[1], &job_errno, SIZEOF(job_errno), pipe_status); assert(FALSE); UNDERSCORE_EXIT(joberr); } } /* Inform grandchild that everything is properly set for it and it is ready to continue. */ decision = JOB_CONTINUE; DOWRITERC(mproc_fds[1], &decision, SIZEOF(decision), pipe_status); if (pipe_status) { kill(child_pid, SIGTERM); joberr = joberr_pipe_mgc; job_errno = errno; DOWRITERC(pipe_fds[1], &job_errno, SIZEOF(job_errno), pipe_status); assert(FALSE); UNDERSCORE_EXIT(joberr); } /* send job parameters and arguments to final mumps process over setup socket */ setup_op = job_set_params; SEND(setup_fds[0], &setup_op, SIZEOF(setup_op), 0, rc); if (rc < 0) SETUP_OP_FAIL(); SEND(setup_fds[0], &jparms->params, SIZEOF(jparms->params), 0, rc); if (rc < 0) SETUP_DATA_FAIL(); /* Read status to catch any basic errors */ DOREADRC(setup_fds[0], &joberr, SIZEOF(joberr), rc); if (rc < 0) joberr = joberr_rtn; /* Assume routine error if there is a problem getting the report */ if (joberr_ok != joberr) { job_errno = errno; DOWRITERC(pipe_fds[1], &job_errno, SIZEOF(job_errno), pipe_status); UNDERSCORE_EXIT(joberr); } setup_op = job_set_parm_list; SEND(setup_fds[0], &setup_op, SIZEOF(setup_op), 0, rc); if (rc < 0) SETUP_OP_FAIL(); arg_count = argcnt; SEND(setup_fds[0], &arg_count, SIZEOF(arg_count), 0, rc); if (rc < 0) SETUP_DATA_FAIL(); for (jp = jparms->parms; jp ; jp = jp->next) { if (jp->parm->str.len > MAX_JOB_LEN) { kill(child_pid, SIGTERM); joberr = joberr_io_setup_write; job_errno = ERR_JOBPARTOOLONG; DOWRITERC(pipe_fds[1], &job_errno, SIZEOF(job_errno), pipe_status); assert(FALSE); UNDERSCORE_EXIT(joberr); } if (0 == jp->parm->mvtype) arg_msg.len = -1; /* negative len indicates null arg */ else { MV_FORCE_STR(jp->parm); arg_msg.len = jp->parm->str.len; memcpy(arg_msg.data, jp->parm->str.addr, jp->parm->str.len); } SEND(setup_fds[0], &arg_msg.len, SIZEOF(arg_msg.len), 0, rc); if (rc < 0) SETUP_DATA_FAIL(); if (arg_msg.len >= 0) { SEND(setup_fds[0], &arg_msg.data, arg_msg.len, 0, rc); if (rc < 0) SETUP_DATA_FAIL(); } } if (0 < jparms->input_prebuffer_size) { setup_op = job_set_input_buffer; SEND(setup_fds[0], &setup_op, SIZEOF(setup_op), 0, rc); if (rc < 0) SETUP_OP_FAIL(); buffer_size = jparms->input_prebuffer_size; SEND(setup_fds[0], &buffer_size, SIZEOF(buffer_size), 0, rc); if (rc < 0) SETUP_DATA_FAIL(); SEND(setup_fds[0], jparms->input_prebuffer, jparms->input_prebuffer_size, 0, rc); if (rc < 0) SETUP_DATA_FAIL(); /* input_prebuffer leaks, but the middle process is about to exit, so don't worry about it */ } setup_op = job_done; SEND(setup_fds[0], &setup_op, SIZEOF(setup_op), 0, rc); if (rc < 0) SETUP_OP_FAIL(); /* Send the local variables */ if (jparms->passcurlvn) { SEND(setup_fds[0], jparms->curlvn_buffer_ptr, jparms->curlvn_buffer_size, 0, rc); system_free(jparms->curlvn_buffer_ptr); /* Space allocated by open_memstream in ojparams */ if (rc < 0) SETUP_OP_FAIL(); } /* Tell job to proceed */ setup_op = local_trans_done; SEND(setup_fds[0], &setup_op, SIZEOF(setup_op), 0, rc); if (rc < 0) SETUP_OP_FAIL(); /* Write child_pid into pipe to be read by parent process(P) for $ZJOB */ /* Ignore the status if this fails, as the child is already running, and there is likely not a parent * to report to. */ DOWRITERC(pipe_fds[1], &child_pid, SIZEOF(child_pid), pipe_status); UNDERSCORE_EXIT(EXIT_SUCCESS); } /* This is now the grandchild process (actual Job process) -- an orphan as soon as the EXIT(EXIT_SUCCESS) above * occurs. Revert the condition handler established by middle process and establish its own condition handler */ REVERT; getjobnum(); /* set "process_id" to a value different from parent (middle child). This is particularly needed * for the RELINKCTL_RUNDOWN_MIDDLE_PARENT macro since it does rtnobj_shm_free which deals with * latches that in turn rely on the "process_id" global variable reflecting the current pid. */ ESTABLISH_RET(grand_child, 0); sigaction(SIGTERM, &old_act, 0); /* restore the SIGTERM handler */ CLOSEFILE_RESET(setup_fds[0], pipe_status); /* resets "setup_fds[0]" to FD_INVALID */ /* Since middle child and grand child go off independently, it is possible the grandchild executes * "relinkctl_rundown(TRUE,...)" a little before the middle child has done "relinkctl_rundown(FALSE,...)" * and this means the grand child could potentially find the GT.M nattached counter to be 0 but the shmctl * nattach counter could still be non-zero (because the middle child has not yet detached from relinkctl shm). * To avoid a related assert in "relinkctl_rundown" from failing, set dbg-only variable. */ DEBUG_ONLY(TREF(fork_without_child_wait) = TRUE); DOREADRC(mproc_fds[0], &decision, SIZEOF(decision), pipe_status); if (pipe_status) /* We failed to read the communication from middle process */ { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2, LEN_AND_LIT("Error reading from pipe"), errno); } else { if (JOB_EXIT == decision) EXIT(EXIT_SUCCESS); assert(JOB_CONTINUE == decision); } CLOSEFILE_RESET(mproc_fds[0], pipe_status); /* resets "mproc_fds[0]" to FD_INVALID */ CLOSEFILE_RESET(mproc_fds[1], pipe_status); /* resets "mproc_fds[1]" to FD_INVALID */ /* 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 */ /* Count the number of environment variables. */ for (environ_count = 0, c3 = environ, c2 = *c3; c2; c3++, c2 = *c3) environ_count++; /* 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 * 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; * gtmjb= // startup parameter to job command * 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 + 1)*SIZEOF(char *)); string_len = STRLEN("%s=%d") + STRLEN(CHILD_FLAG_ENV) + MAX_NUM_LEN - 4; if (string_len >= MAX_MUMPS_EXE_PATH_LEN) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_JOBPARTOOLONG); } c1 = (char *)malloc(string_len + 1); #ifdef KEEP_zOS_EBCDIC #pragma convlit(suspend) #endif SNPRINTF_ENV_NUM(c1, string_len + 1, CHILD_FLAG_ENV, setup_fds[1], 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->params.gbldir.len != 0) { assert(jparms->params.gbldir.len < TEMP_BUFF_SIZE); strncpy(pbuff, jparms->params.gbldir.buffer, jparms->params.gbldir.len); *(pbuff + jparms->params.gbldir.len) = '\0'; string_len = STRLEN("%s=%s") + STRLEN(GBLDIR_ENV) + STRLEN(pbuff) - 4; assert(string_len < TEMP_BUFF_SIZE); c1 = (char *)malloc(string_len + 1); #ifdef KEEP_zOS_EBCDIC #pragma convlit(suspend) #endif SNPRINTF_ENV_STR(c1, string_len + 1, GBLDIR_ENV, pbuff, env_ind); #ifdef KEEP_zOS_EBCDIC #pragma convlit(resume) #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() */ c1 = 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_CSA_ABT(NULL, 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 /* pass cmdline to child */ if (jparms->cmdline.len != 0) { if (jparms->cmdline.len >= TEMP_BUFF_SIZE) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_JOBPARTOOLONG); } memcpy(cmdbuff, jparms->cmdline.buffer, jparms->cmdline.len); *(cmdbuff + jparms->cmdline.len) = 0; } else memset(cmdbuff, 0, TEMP_BUFF_SIZE); /* Do common cleanup in child. Note that the below call to "ojchildioclean" invokes "relinkctl_rundown" * but that is not needed since we have already done it in the RELINKCTL_RUNDOWN_MIDDLE_PARENT invocation. */ ojchildioclean(); #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] = cmdbuff; argv[3] = (char *)0; /* Ignore all SIGHUPs until sig_init() is called. On AIX we have seen SIGHUP from middlechild to grandchild */ signal(SIGHUP, SIG_IGN); EXECVE(tbuff, argv, env_ary); /* if we got here, error starting the Job */ RTS_ERROR_CSA_ABT(NULL, 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) return (wait_status); /* 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) /* Some other signal received OR there is no child to be waited on */ { *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 */ } fis-gtm-V7.0-005/sr_unix/mu_rndwn_rlnkctl.h0000644000032200000250000000111114342376330017566 0ustar librarygtc/**************************************************************** * * * Copyright 2014 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. * * * ****************************************************************/ #ifndef MU_RNDWN_RLNKCTL_INCLUDED #define MU_RNDWN_RLNKCTL_INCLUDED void mu_rndwn_rlnkctl(void); #endif /* MU_RNDWN_RLNKCTL_INCLUDED */ fis-gtm-V7.0-005/sr_unix/mu_signal_process.c0000755000032200000250000000323514342376330017727 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 #include #include "gtm_string.h" #include "gtm_stdio.h" #include "cli.h" #include "util.h" #include "mupip_exit.h" #include "mupip_stop.h" #include "mu_signal_process.h" #include "send_msg.h" GBLREF uint4 process_id; void mu_signal_process(char *command, int signal) { unsigned short slen; int len, toprocess_id, save_errno; char buff[256]; error_def(ERR_MUPCLIERR); error_def(ERR_MUPIPSIG); slen = SIZEOF(buff); if (!cli_get_str("ID", buff, &slen)) mupip_exit(ERR_MUPCLIERR); len = slen; toprocess_id = asc2i((uchar_ptr_t)buff, len); if (toprocess_id < 0) { util_out_print("Error converting !AD to a number", FLUSH, len, buff); mupip_exit(ERR_MUPCLIERR); } else { if (-1 == kill(toprocess_id, signal)) { save_errno = errno; util_out_print("Error issuing !AD to process !UL: !AZ", FLUSH, LEN_AND_STR(command), toprocess_id, STRERROR(errno)); } else { util_out_print("!AD issued to process !UL", FLUSH, LEN_AND_STR(command), toprocess_id); if (!MEMCMP_LIT(command, STOP_STR)) { send_msg(VARLSTCNT(9) ERR_MUPIPSIG, 7, LEN_AND_STR(command), signal, process_id, process_id, toprocess_id, toprocess_id); } } } return; } fis-gtm-V7.0-005/sr_unix/mu_size_arsample.c0000644000032200000250000002465114342376330017554 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2019 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_string.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdsblkops.h" #include "gdskill.h" #include "gdscc.h" #include "interlock.h" #include "muextr.h" #include "mu_reorg.h" /* Include prototypes */ #include "t_end.h" #include "t_retry.h" #include "collseq.h" #include "mu_getkey.h" #include "mupip_size.h" #include "util.h" #include "t_begin.h" #include "op.h" #include "gvcst_protos.h" /* for gvcst_rtsib,gvcst_search prototype */ #include "gvcst_bmp_mark_free.h" #include "gvcst_kill_sort.h" #include "gtmmsg.h" #include "add_inter.h" #include "t_abort.h" #include "sleep_cnt.h" #include "wcs_sleep.h" #include "memcoherency.h" #include "change_reg.h" #include "gtm_time.h" #include "mvalconv.h" #include "longset.h" /* needed for cws_insert.h */ #include "cws_insert.h" #include "min_max.h" #include error_def(ERR_GBLNOEXIST); error_def(ERR_MUSIZEFAIL); GBLREF bool mu_ctrlc_occurred; GBLREF bool mu_ctrly_occurred; GBLREF gv_namehead *gv_target; GBLREF inctn_opcode_t inctn_opcode; GBLREF int muint_adj; GBLREF int4 mu_int_adj[]; GBLREF int4 process_id; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF unsigned int t_tries; GBLREF boolean_t mu_key; GBLREF boolean_t null_coll_key; GBLREF gv_key *mu_start_key; GBLREF gv_key *mu_end_key; #define APPROX_F_MAX 500 /* Approximate upper bound for the number of records per index block in * a database. The estimated max fanning factor is initially APPROX_F_MAX. * After DYNAMIC_F_MAX samples, we try to get a closer approximation, by * dynamically adjusting the estimated max fanning factor to be EXTRA_F_MAX * more than the maximum fanning observed ("fanning" is number of records in * a block a particular level). We want a closer approximation so that we reject * fewer samples, and therefore get a size estimation more quickly */ #define DYNAMIC_F_MAX 10 /* Choice of these two constants relates to choice of APPROX_F_MAX - how? */ #define EXTRA_F_MAX 50 typedef struct { /* cumulative running stats */ int n; /* number of previous traversals */ int N[MAX_BT_DEPTH + 1]; /* number of accepted samples at each given level (more are likely for higher levels) */ double M[MAX_BT_DEPTH + 1]; /* M[j] := mean of r[j]'s over previous n traversals * Note: M_n = M_{n-1} + (r_n - M_{n-1}) / n */ double S[MAX_BT_DEPTH + 1]; /* S[j] := sum of (r_i[j] - M[j])^2 over each previous traversal, i=1..n * Note: S_n = S_{n-1} + (r_n - M_n) * (r_n - M_{n-1}) * Later, S values are divided by number of samples to give a plugin estimate of variance * and subsequently are divided by the sample size to give the variance of the mean */ double f_max[MAX_BT_DEPTH + 1]; /* estimated max fanning factor */ double r_max[MAX_BT_DEPTH + 1]; /* max records found in a block at a given level */ double A[MAX_BT_DEPTH + 1]; /* A[j] := mean of a[j] over previous n traversals]; see note on M */ /* Final estimates */ double blktot[MAX_BT_DEPTH + 1]; /* estimated #blocks at each level */ double blkerr[MAX_BT_DEPTH + 1]; /* approximate variance of blktot */ double rectot[MAX_BT_DEPTH + 1]; /* estimated #records at each level */ double B; /* estimated total blocks */ double error; /* approximate error in estimate B */ double R; /* estimated total records */ double AT; /* estimated total adjacency */ } stat_t; /* macro makes it convenient to manange initialization with changes to stat_t */ #define INIT_STATS(stat) \ { \ int J; \ \ stat.n = 0; \ for (J = 0; MAX_BT_DEPTH >= J; J++) \ { \ stat.f_max[J] = APPROX_F_MAX; \ stat.r_max[J] = 1; \ } \ CLEAR_VECTOR(stat.N); \ CLEAR_VECTOR(stat.M); \ CLEAR_VECTOR(stat.S); \ CLEAR_VECTOR(stat.blktot); \ CLEAR_VECTOR(stat.blkerr); \ CLEAR_VECTOR(stat.rectot); \ CLEAR_VECTOR(stat.A); \ } STATICFNDCL void accum_stats_ar(stat_t *stat, double *r, double *a); STATICFNDCL void finalize_stats_ar(stat_t *stat); int4 mu_size_arsample(glist *gl_ptr, uint4 M, int seed) { boolean_t tn_aborted; double a[MAX_BT_DEPTH + 1]; /* a[j] is # of adjacent block pointers in level j block of cur traversal */ double r[MAX_BT_DEPTH + 1]; /* r[j] is #records in level j block of current traversal */ enum cdb_sc status; int k, h; stat_t rstat; trans_num ret_tn; unsigned int lcl_t_tries; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; inctn_opcode = inctn_invalid_op; /* set gv_target/gv_currkey/gv_cur_region/cs_addrs/cs_data to correspond to in gl_ptr */ DO_OP_GVNAME(gl_ptr); if (0 == gv_target->root) { /* Global does not exist (online rollback). Not an error. */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_GBLNOEXIST, 2, GNAME(gl_ptr).len, GNAME(gl_ptr).addr); return EXIT_NRM; } if (!seed) seed = (int4)(time(0) * process_id); srand48(seed); if (MUKEY_NULLSUBS == mu_key) CHECK_COLL_KEY(gl_ptr, null_coll_key); /* do random traversals until M of them are accepted at level 1 */ INIT_STATS(rstat); for (k = 1; rstat.N[1] < M; k++) { if (mu_ctrlc_occurred || mu_ctrly_occurred) return EXIT_ERR; t_begin(ERR_MUSIZEFAIL, 0); for (;;) { CLEAR_VECTOR(r); CLEAR_VECTOR(a); if (cdb_sc_normal != (status = mu_size_rand_traverse(r, a))) /* WARNING assignment */ { assert(UPDATE_CAN_RETRY(t_tries, status)); t_retry(status); continue; } gv_target->clue.end = 0; gv_target->hist.h[0] = gv_target->hist.h[1]; /* No level 0 block to validate */ DEBUG_ONLY(lcl_t_tries = t_tries); if ((trans_num)0 == (ret_tn = t_end(&gv_target->hist, NULL, TN_NOT_SPECIFIED))) /* WARNING: assignment */ { ABORT_TRANS_IF_GBL_EXIST_NOMORE(lcl_t_tries, tn_aborted); if (tn_aborted) { /* Global does not exist (online rollback). Not an error. */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_GBLNOEXIST, 2, GNAME(gl_ptr).len, GNAME(gl_ptr).addr); return EXIT_NRM; } continue; } accum_stats_ar(&rstat, r, a); break; } } finalize_stats_ar(&rstat); /* display rstat data * Showing error as 2 standard deviations which is a 95% confidence interval for the mean number of blocks at each level */ util_out_print("!/Number of generated samples = !UL", FLUSH, rstat.n); util_out_print("Number of accepted samples = !UL", FLUSH, rstat.N[1]); util_out_print("Level Blocks Adjacent 2 sigma(+/-) % Accepted", FLUSH); for (h = MAX_BT_DEPTH; (0 <= h) && (rstat.blktot[h] < EPS); h--) ; for ( ; h > 0; h--) util_out_print("!5UL !15UL !15UL !15UL ~ !3UL% !15UL", FLUSH, h, (int)ROUND(rstat.blktot[h]), (int)ROUND(sqrt(rstat.blkerr[h]) * 2), (int)ROUND(mu_int_adj[h]), (int)ROUND(sqrt(rstat.blkerr[h]) * 2 / rstat.blktot[h] * 100), (int)ROUND(100.0 * rstat.N[h] / rstat.n) ); util_out_print("!5UL !15UL !15UL !15UL ~ !3UL% N/A", FLUSH, h, (int)ROUND(rstat.blktot[h]), (int)ROUND(mu_int_adj[h]), (int)ROUND(sqrt(rstat.blkerr[h]) * 2), (int)ROUND(sqrt(rstat.blkerr[h]) * 2 / rstat.blktot[h] * 100.0) ); util_out_print("Total !15UL !15UL !15UL ~ !3UL% N/A", FLUSH, (int)ROUND(rstat.B), (int)ROUND(rstat.AT), (int)ROUND(sqrt(rstat.error) * 2), (int)ROUND(sqrt(rstat.error) * 2 / rstat.B * 100.0) ); return EXIT_NRM; } STATICFNDCL void accum_stats_ar(stat_t *stat, double *r, double *a) { double random, M0, accept[MAX_BT_DEPTH + 1]; int j, depth, n; ++stat->n; for (j = MAX_BT_DEPTH; (0 <= j) && (r[j] < EPS); j--) accept[j] = 0; depth = j; assert(0 <= depth); /* r[0] should remain zero since we don't maintain it */ accept[depth] = 1; /* always accept the root */ for (; 2 <= j; j--) accept[j - 1] = accept[j] * ((j == depth) ? 1 : (r[j] / stat->f_max[j])); accept[0] = 0; /* computing #blks (e.g #recs in lvl 1+), not #recs in lvl 0+ */ random = drand48(); for (j = 0; MAX_BT_DEPTH >= j; j++) { if (random < accept[j]) { n = ++stat->N[j]; M0 = stat->M[j]; stat->M[j] += ((r[j] - M0) / n); stat->S[j] += (r[j] - stat->M[j]) * (r[j] - M0); if (n > DYNAMIC_F_MAX) stat->f_max[j] = stat->r_max[j] + EXTRA_F_MAX; stat->A[j] += ((a[j] - stat->A[j]) / n); } stat->r_max[j] = MAX(stat->r_max[j], r[j]); } } STATICFNDCL void finalize_stats_ar(stat_t *stat) { int j, k; for (j = 0; MAX_BT_DEPTH >= j; j++) /* Variance of the mean (mean referes to avg number of records per block) is Var(R)/N where N is samples size */ if (stat->N[j] > 0) { stat->S[j] /= stat->N[j]; stat->S[j] /= stat->N[j]; } stat->N[0] = stat->n; /* for arithmetic below */ /* Note: stat->M[0] should remain zero since we don't maintain it. Also stat->M[1] should be > EPS. * So "j" is guaranteed to be at least 1 at the end of the for loop. Assert that. * In "pro" we be safe and add the "(0 < j)" check in the for loop below to prevent "j" from becoming negative. */ assert(0 == stat->M[0]); assert(EPS > 0); assert(EPS < 1); assert(1 <= stat->M[1]); for (j = MAX_BT_DEPTH; (0 < j) && (stat->M[j] < EPS); j--) ; assert(0 < j); mu_int_adj[j] = stat->AT = stat->blkerr[j] = stat->error = 0; stat->B = stat->blktot[j] = 1; for (k = j - 1; j > 0; j--, k--) { if (0 == stat->M[j]) stat->M[j] = EPS; /* remove any chance of division by zero */ stat->blktot[k] = stat->blktot[j] * stat->M[j]; stat->B += stat->blktot[k]; mu_int_adj[k] = stat->blktot[j] * stat->A[j]; stat->AT += mu_int_adj[k]; /* Var(XY) assuming X and Y are independent = E[X]^2*Var(Y) + E[Y]^2*Var(X) + Var(X)*Var(Y) */ stat->blkerr[k] = SQR(stat->M[j]) * stat->blkerr[j] + SQR(stat->blktot[j]) * stat->S[j] + stat->blkerr[j] * stat->S[j]; stat->error += stat->blkerr[k]; } stat->R = 0; for (j = 0; MAX_BT_DEPTH >= j; j++) { stat->rectot[j] = stat->blktot[j] * stat->M[j]; stat->R += stat->rectot[j]; } } fis-gtm-V7.0-005/sr_unix/mu_size_impsample.c0000644000032200000250000002324014342376330017730 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2019 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_string.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdsblkops.h" #include "gdskill.h" #include "gdscc.h" #include "interlock.h" #include "muextr.h" #include "mu_reorg.h" /* Include prototypes */ #include "t_end.h" #include "t_retry.h" #include "collseq.h" #include "mu_getkey.h" #include "mupip_size.h" #include "util.h" #include "t_begin.h" #include "op.h" #include "gvcst_protos.h" /* for gvcst_rtsib,gvcst_search prototype */ #include "gvcst_bmp_mark_free.h" #include "gvcst_kill_sort.h" #include "gtmmsg.h" #include "add_inter.h" #include "t_abort.h" #include "sleep_cnt.h" #include "wcs_sleep.h" #include "memcoherency.h" #include "change_reg.h" #include "gtm_time.h" #include "mvalconv.h" #include "longset.h" /* needed for cws_insert.h */ #include "cws_insert.h" #include error_def(ERR_GBLNOEXIST); error_def(ERR_MUSIZEFAIL); GBLREF bool mu_ctrlc_occurred; GBLREF bool mu_ctrly_occurred; GBLREF gv_namehead *gv_target; GBLREF inctn_opcode_t inctn_opcode; GBLREF int muint_adj; GBLREF int4 mu_int_adj[]; GBLREF int4 process_id; GBLREF unsigned int t_tries; GBLREF boolean_t null_coll_key; GBLREF gv_key *mu_start_key; GBLREF gv_key *mu_end_key; GBLREF boolean_t mu_key; #define MAX_RECS_PER_BLK 65535 #define MAX_RELIABLE 10000 /* Used to tweak the error estimates */ typedef struct { /* cumulative stats */ int4 n; /* number of samples */ double W[MAX_BT_DEPTH + 1]; /* Sum of the importance values of samples for each depth level */ double w_mu[MAX_BT_DEPTH + 1]; /* The mean of importance values. It is used to calculate w_variance */ double w_variance[MAX_BT_DEPTH + 1]; /* The variance of importance values; used to calculate effective sample size */ double mu[MAX_BT_DEPTH + 1]; /* mu[j] := mean of weighted r[j]'s over previous n traversals. * It is the expected number of records at depth j * Note: mu_n = mu_{n-1} + w_n/W_n*(r_n - M_{n-1}) */ double S[MAX_BT_DEPTH + 1]; /* S[j] := sum of w_i*(r_i[j] - M[j])^2 over previous traversals. * Note: S_n = S_{n-1} + w_n*(r_n - M_n)*(r_n - M_{n-1}) * Later, S values are divided by W values to give plugin estimate of the variance. * Subsequently they are divided by the effective sample size to give the variance * of the mean */ double A[MAX_BT_DEPTH + 1]; /* A[j] := mean of a[j] over previous n traversals]; see note on mu */ /* Final estimates */ double AT; /* estimated total adjacency */ double blktot[MAX_BT_DEPTH + 1]; /* estimated #blocks at each level */ double blkerr[MAX_BT_DEPTH + 1]; /* approximate variance of blktot */ double rectot[MAX_BT_DEPTH + 1]; /* estimated #records at each level */ double B; /* estimated total blocks */ double error; /* approximate error in estimate B */ double R; /* estimated total records */ } stat_t; STATICFNDCL void finalize_stats_impsmpl(stat_t *stat); STATICFNDCL void accum_stats_impsmpl(stat_t *stat, double *r, double *a); /* macro makes it convenient to manange initialization with changes to stat_t */ #define INIT_STATS(stat) \ { \ stat.n = 0; \ CLEAR_VECTOR(stat.W); \ CLEAR_VECTOR(stat.w_mu); \ CLEAR_VECTOR(stat.w_variance); \ CLEAR_VECTOR(stat.mu); \ CLEAR_VECTOR(stat.S); \ CLEAR_VECTOR(stat.blktot); \ CLEAR_VECTOR(stat.blkerr); \ CLEAR_VECTOR(stat.rectot); \ CLEAR_VECTOR(stat.A); \ } /* Importance Sampling */ int4 mu_size_impsample(glist *gl_ptr, int4 M, int4 seed) { boolean_t tn_aborted; double a[MAX_BT_DEPTH + 1]; /* a[j] is # of adjacent block pointers in level j block of cur traversal */ double r[MAX_BT_DEPTH + 1]; /* r[j] is #records in level j block of current traversal */ enum cdb_sc status; int k, h; stat_t rstat; trans_num ret_tn; unsigned int lcl_t_tries; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; inctn_opcode = inctn_invalid_op; /* set gv_target/gv_currkey/gv_cur_region/cs_addrs/cs_data to correspond to in gl_ptr */ DO_OP_GVNAME(gl_ptr); if (0 == gv_target->root) { /* Global does not exist (online rollback). Not an error. */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_GBLNOEXIST, 2, GNAME(gl_ptr).len, GNAME(gl_ptr).addr); return EXIT_NRM; } if (!seed) seed = (int4)(time(0) * process_id); srand48(seed); if (MUKEY_NULLSUBS == mu_key) CHECK_COLL_KEY(gl_ptr, null_coll_key); /* do M random traversals */ INIT_STATS(rstat); for (k = 1; k <= M; k++) { if (mu_ctrlc_occurred || mu_ctrly_occurred) return EXIT_ERR; t_begin(ERR_MUSIZEFAIL, 0); for (;;) { CLEAR_VECTOR(r); CLEAR_VECTOR(a); if (cdb_sc_normal != (status = mu_size_rand_traverse(r, a))) /* WARNING: assignment */ { assert(UPDATE_CAN_RETRY(t_tries, status)); t_retry(status); continue; } gv_target->clue.end = 0; gv_target->hist.h[0] = gv_target->hist.h[1]; /* No level 0 block to validate */ DEBUG_ONLY(lcl_t_tries = t_tries); if ((trans_num)0 == (ret_tn = t_end(&gv_target->hist, NULL, TN_NOT_SPECIFIED))) /* WARNING: assignment */ { ABORT_TRANS_IF_GBL_EXIST_NOMORE(lcl_t_tries, tn_aborted); if (tn_aborted) { /* Global does not exist (online rollback). Not an error. */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_GBLNOEXIST, 2, GNAME(gl_ptr).len, GNAME(gl_ptr).addr); return EXIT_NRM; } continue; } accum_stats_impsmpl(&rstat, r, a); break; } } finalize_stats_impsmpl(&rstat); /* display rstat data * Showing the error as 2 standard deviations which is a 95% confidence interval for the * mean number of blocks at each level */ util_out_print("Number of generated samples = !UL", FLUSH, rstat.n); util_out_print("Level Blocks Adjacent 2 sigma(+/-)", FLUSH); for (h = MAX_BT_DEPTH; (0 <= h) && (rstat.blktot[h] < EPS); h--); for ( ; h > 0; h--) util_out_print("!5UL !15UL !15UL !15UL ~ !3UL%", FLUSH, h, (int)ROUND(rstat.blktot[h]), (int)ROUND(mu_int_adj[h]), (int)ROUND(sqrt(rstat.blkerr[h]) * 2), (int)ROUND(sqrt(rstat.blkerr[h]) * 2 / rstat.blktot[h] * 100.0) ); util_out_print("!5UL !15UL !15UL !15UL ~ !3UL%", FLUSH, h, (int)ROUND(rstat.blktot[h]), (int)ROUND(mu_int_adj[h]), (int)ROUND(sqrt(rstat.blkerr[h]) * 2), (int)ROUND(sqrt(rstat.blkerr[h]) * 2 / rstat.blktot[h] * 100.0) ); util_out_print("Total !15UL !15UL !15UL ~ !3UL%", FLUSH, (int)ROUND(rstat.B), (int)ROUND(rstat.AT), (int)ROUND(sqrt(rstat.error) * 2), (int)ROUND(sqrt(rstat.error) * 2 / rstat.B * 100.0) ); return EXIT_NRM; } STATICFNDEF void accum_stats_impsmpl(stat_t *stat, double *r, double *a) { double mu0, w_mu0, w[MAX_BT_DEPTH + 1] /* importance */; int k, l, root_level; ++stat->n; for (l = MAX_BT_DEPTH; (0 <= l) && (r[l] < EPS); l--) w[l] = 0; root_level = l; assert(0 <= root_level); w[root_level] = 1; for (k = l - 1; 2 <= l; k--, l--) w[k] = w[l] * r[l]; /* NOTE: consider using log to avoid overflow if it becomes an issue */ w[0] = 0; /* computing #blks (e.g #recs in lvl 1+), not #recs in lvl 0+ */ for (l = 1; l <= root_level; l++) { stat->W[l] += w[l]; w_mu0 = stat->w_mu[l]; stat->w_mu[l] += (w[l] - w_mu0) / stat->n; stat->w_variance[l] += (w[l] - stat->w_mu[l]) * (w[l] - w_mu0); mu0 = stat->mu[l]; stat->mu[l] += (w[l] / stat->W[l] * (r[l] - stat->mu[l])); stat->S[l] += (w[l] * (r[l] - stat->mu[l]) * (r[l] - mu0)); stat->A[l] += (w[l] / stat->W[l] * (a[l] - stat->A[l])); } } STATICFNDEF void finalize_stats_impsmpl(stat_t *stat) { double ess; /* effective sample size */ int k, l; for (l = 1; MAX_BT_DEPTH >= l; l++) if (stat->W[l] > 0) { /* ess = n / ( 1 + Var( w/mu(w) ) ). * This comes from effective sample size for importance sampling in the literature */ ess = stat->n / ( 1 + (stat->w_variance[l] / stat->n) / SQR(stat->w_mu[l]) ); /* Variance of the mean (mean referes to avg number of records per block) is * Var(R)/N where N is effective sample size */ stat->S[l] /= stat->W[l]; stat->S[l] /= (ess + 1); } stat->W[0] = stat->n; /* for arithmetic below */ /* Note: stat->mu[0] should remain zero since we don't maintain it. Also stat->mu[1] should be > EPS. * So "l" is guaranteed to be at least 1 at the end of the for loop. Assert that. * In "pro" we be safe and add the "(0 < l)" check in the for loop below to prevent "l" from becoming negative. */ assert(0 == stat->mu[0]); assert(EPS > 0); assert(EPS < 1); assert(1 <= stat->mu[1]); for (l = MAX_BT_DEPTH; (0 < l) && (stat->mu[l] < EPS); l--) ; assert(0 < l); stat->AT = stat->blkerr[l] = stat->error = stat->R = 0; stat->B = stat->blktot[l] = 1; for (k = l - 1 ; 0 < l; k--, l--) { stat->blktot[k] = stat->blktot[l] * stat->mu[l]; /* Var(XY) assuming X and Y are independent = E[X]^2*Var(Y) + E[Y]^2*Var(X) + Var(X)*Var(Y) */ stat->blkerr[k] = SQR(stat->mu[l]) * stat->blkerr[l] + SQR(stat->blktot[l]) * stat->S[l] + stat->blkerr[l] * stat->S[l]; stat->B += stat->blktot[k]; mu_int_adj[k] = stat->blktot[l] * stat->A[l]; stat->AT += mu_int_adj[k]; stat->error += stat->blkerr[k]; stat->rectot[k] = stat->blktot[k] * stat->mu[k]; stat->R += stat->rectot[k]; } } fis-gtm-V7.0-005/sr_unix/mu_size_scan.c0000644000032200000250000003355014342376330016672 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2020 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_string.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdsblkops.h" #include "gdskill.h" #include "gdscc.h" #include "copy.h" #include "interlock.h" #include "muextr.h" #include "mupint.h" /* Include prototypes */ #include "t_end.h" #include "t_retry.h" #include "collseq.h" #include "mu_getkey.h" #include "mupip_size.h" #include "util.h" #include "t_begin.h" #include "op.h" #include "gvcst_protos.h" /* for gvcst_rtsib,gvcst_search prototype */ #include "gvcst_bmp_mark_free.h" #include "gvcst_kill_sort.h" #include "gtmmsg.h" #include "add_inter.h" #include "t_abort.h" #include "sleep_cnt.h" #include "wcs_sleep.h" #include "memcoherency.h" #include "change_reg.h" #include "gtm_time.h" #include "mvalconv.h" #include "t_qread.h" #include "longset.h" /* needed for cws_insert.h */ #include "cws_insert.h" #include "tp.h" #include error_def(ERR_GBLNOEXIST); error_def(ERR_MUSIZEFAIL); error_def(ERR_MUSIZEINVARG); GBLREF bool mu_ctrlc_occurred; GBLREF bool mu_ctrly_occurred; GBLREF gv_namehead *gv_target; GBLREF int4 process_id; GBLREF inctn_opcode_t inctn_opcode; GBLREF unsigned char rdfail_detail; GBLREF int4 mu_int_adj[]; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF unsigned int t_tries; GBLREF boolean_t mu_subsc; GBLREF block_id mu_int_adj_prev[MAX_BT_DEPTH + 1]; GBLREF boolean_t mu_key; GBLREF boolean_t null_coll_key; GBLREF gv_key *mu_start_key; GBLREF gv_key *mu_end_key; STATICDEF uint4 mu_size_cumulative[MAX_BT_DEPTH + 1][CUMULATIVE_TYPE_MAX]; STATICDEF int targ_levl; STATICDEF INTPTR_T saveoff[MAX_BT_DEPTH + 1]; STATICFNDCL enum cdb_sc dfs(int lvl, sm_uc_ptr_t pBlkBase, boolean_t endtree, boolean_t skiprecs); STATICFNDCL enum cdb_sc read_block(block_id nBlkId, sm_uc_ptr_t *pBlkBase_ptr, int *nLevl_ptr, int desired_levl); #define ANY_ROOT_LEVL (MAX_BT_DEPTH + 5) /* overload invalid level value */ #define MAX_SCANS 200000000 /* catch infinite loops */ /* No MBSTART and MBEND below as the macro uses a continue command */ #define CHECK_KEY_RANGE(MUSZ_START_KEY, MUSZ_END_KEY, BUFF, RCNT, MUSZ_RANGE_DONE) \ { \ int4 CMP_KEY; \ \ CMP_KEY = memcmp(BUFF, MUSZ_START_KEY->base, MUSZ_START_KEY->end + 1); \ if (MUSZ_END_KEY) \ { \ if (memcmp(BUFF, MUSZ_END_KEY->base, MUSZ_END_KEY->end + 1) > 0) \ MUSZ_RANGE_DONE = TRUE; \ } else if (0 < CMP_KEY) \ MUSZ_RANGE_DONE = TRUE; \ if (0 > CMP_KEY) \ { \ RCNT--; \ continue; \ } \ } int4 mu_size_scan(glist *gl_ptr, int4 level) { block_id nBlkId; boolean_t equal, tn_aborted, verify_reads, retry=FALSE; enum cdb_sc status; trans_num ret_tn; int h, i, k; int4 nLevl; sm_uc_ptr_t pBlkBase; unsigned int lcl_t_tries; unsigned char mu_size_root_lvl; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; inctn_opcode = inctn_invalid_op; memset(mu_size_cumulative,0, SIZEOF(mu_size_cumulative)); memset(mu_int_adj, 0, SIZEOF(int4) * (MAX_BT_DEPTH + 1)); memset(mu_int_adj_prev, 0, SIZEOF(mu_int_adj_prev)); /* set gv_target/gv_currkey/gv_cur_region/cs_addrs/cs_data to correspond to in gl_ptr */ DO_OP_GVNAME(gl_ptr); if (0 == gv_target->root) { /* Global does not exist (online rollback). Not an error. */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_GBLNOEXIST, 2, GNAME(gl_ptr).len, GNAME(gl_ptr).addr); return EXIT_NRM; } gv_target->alt_hist->depth = MAX_BT_DEPTH; /* initialize: don't copy to saveoff if restart before a single success */ for (k = 0; k <= MAX_BT_DEPTH; k++) { saveoff[k] = 0; gv_target->hist.h[k].cr = NULL; /* initialize for optimization in read_block which bumps cr refer bits */ } if (MUKEY_NULLSUBS == mu_key) CHECK_COLL_KEY(gl_ptr, null_coll_key); targ_levl = 0; /* Read the root block and convert negative levels to positive. Negative levels are defined to be counted from root with * -1 identifying the children of root */ t_begin(ERR_MUSIZEFAIL, 0); for(;;) { /* retry loop */ status = read_block(gv_target->root, &pBlkBase, &nLevl, ANY_ROOT_LEVL); if (cdb_sc_normal != status) { t_retry(status); continue; } memcpy(&gv_target->hist.h[0], &gv_target->hist.h[nLevl], SIZEOF(srch_blk_status)); gv_target->hist.h[1].blk_num = 0; if ((trans_num)0 == t_end(&gv_target->hist, NULL, TN_NOT_SPECIFIED)){ lcl_t_tries = TREF(prev_t_tries); ABORT_TRANS_IF_GBL_EXIST_NOMORE(lcl_t_tries, tn_aborted); if (tn_aborted) { /* Global does not exist (online rollback). Not an error. */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_GBLNOEXIST, 2, GNAME(gl_ptr).len, GNAME(gl_ptr).addr); return EXIT_NRM; } continue; } break; } mu_size_root_lvl = nLevl; if (level < 0) level += nLevl; if (level < 0 || nLevl < level) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_MUSIZEINVARG, 2, LEN_AND_LIT("HEURISTIC.LEVEL")); return EXIT_ERR; } targ_levl = level; /* Run the dfs down to targ_levl to count records and blocks. Validate every path from root to blocks at targ_levl */ t_begin(ERR_MUSIZEFAIL, 0); for (;;) { /* retry loop. note that multiple successful read transactions can occur within a single iteration */ nBlkId = gv_target->root; nLevl = ANY_ROOT_LEVL; status = read_block(nBlkId, &pBlkBase, &nLevl, ANY_ROOT_LEVL); if (cdb_sc_normal == status) { if (!retry) CHECK_ADJACENCY(nBlkId, nLevl, mu_int_adj[nLevl]); status = dfs(nLevl, pBlkBase, TRUE, TRUE); } if (cdb_sc_endtree != status) { assert(cdb_sc_normal != status); /* should have continued recursive search */ if (cdb_sc_restarted != status) t_retry(status); lcl_t_tries = TREF(prev_t_tries); ABORT_TRANS_IF_GBL_EXIST_NOMORE(lcl_t_tries, tn_aborted); if (tn_aborted) { /* Global does not exist (online rollback). Not an error. */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_GBLNOEXIST, 2, GNAME(gl_ptr).len, GNAME(gl_ptr).addr); return EXIT_NRM; } /* update saveoff */ if (gv_target->alt_hist->depth < MAX_BT_DEPTH) { for (i = targ_levl; i <= gv_target->alt_hist->depth; i++) saveoff[i] = gv_target->alt_hist->h[i - targ_levl].curr_rec.offset; } retry = TRUE; continue; } break; } for (i = mu_size_root_lvl; i >= level; i--) { util_out_print("Level Blocks Records Adjacent", FLUSH); util_out_print("!5UL !15UL !16UL !16UL", FLUSH, i, mu_size_cumulative[i][BLK], mu_size_cumulative[i][REC], mu_int_adj[i]); } if (mu_ctrlc_occurred || mu_ctrly_occurred) return EXIT_ERR; return EXIT_NRM; } enum cdb_sc dfs(int lvl, sm_uc_ptr_t pBlkBase, boolean_t endtree, boolean_t skiprecs) { block_id nBlkId; boolean_t next_endtree, first_iter, last_rec, next_skiprecs, long_blk_id; cache_rec_ptr_t cr; enum cdb_sc status; int curroff, incr_recs = 0, incr_scans = 0; int4 child_nLevl, i, rCnt; sm_uc_ptr_t pTop, pRec, child_pBlkBase; srch_hist sibhist; trans_num ret_tn; unsigned short nRecLen; unsigned short rec_cmpc; uchar_ptr_t key_base, ptr; boolean_t first_key = TRUE, musz_range_done = FALSE; int name_len, key_size, buff_length, rec_len, bstar_rec_sz; unsigned char buff[MAX_KEY_SZ + 1]; assert(mu_size_cumulative[lvl][BLK] < MAX_SCANS); long_blk_id = IS_64_BLK_ID(pBlkBase); if (lvl == targ_levl) { /* reached the bottom. count records in this block and validate */ BLK_LOOP(rCnt, pRec, pBlkBase, pTop, nRecLen, musz_range_done) { GET_AND_CHECK_RECLEN(status, nRecLen, pRec, pTop, nBlkId, long_blk_id); if (cdb_sc_normal != status) { assert(CDB_STAGNATE > t_tries); return status; } if (lvl) CHECK_ADJACENCY(nBlkId, lvl -1, mu_int_adj[lvl - 1]); rec_cmpc = EVAL_CMPC((rec_hdr_ptr_t)pRec); key_base = pRec + SIZEOF(rec_hdr); GET_KEY_CPY_BUFF(key_base, rec_cmpc, ptr, first_key, name_len, key_size, buff, buff_length, rec_len); if (0 == key_size) continue; /* indication of trouble parsing the block - don't use it */ if (mu_subsc) /* Subscript option chosen */ { CHECK_KEY_RANGE(mu_start_key, mu_end_key, buff, rCnt, musz_range_done); if (musz_range_done) { if (!lvl) /* Dont count at data level */ break; else continue; } } } incr_recs = rCnt; incr_scans = 1; } else if (lvl > targ_levl) { /* visit each child */ /* Assumption on the fact that level > 0 is always true for this case, * since lowest the level can go is 0 and is checked before calling dfs. */ assert(lvl); first_iter = TRUE; gv_target->hist.h[lvl - targ_levl].curr_rec.offset = saveoff[lvl]; bstar_rec_sz = bstar_rec_size(long_blk_id); BLK_LOOP(rCnt, pRec, pBlkBase, pTop, nRecLen, musz_range_done) { GET_AND_CHECK_RECLEN(status, nRecLen, pRec, pTop, nBlkId, long_blk_id); if (cdb_sc_normal != status) { assert(CDB_STAGNATE > t_tries); return status; } curroff = (INTPTR_T)(pRec - pBlkBase); gv_target->hist.h[lvl - targ_levl].curr_rec.offset = curroff; rec_cmpc = EVAL_CMPC((rec_hdr_ptr_t)pRec); key_base = pRec + SIZEOF(rec_hdr); if ((((rec_hdr *)pRec)->rsiz) == bstar_rec_sz) /* Found the star key */ { if (skiprecs && (curroff < saveoff[lvl])) continue; /* skip these guys, we've already counted over there */ } else { GET_KEY_CPY_BUFF(key_base, rec_cmpc, ptr, first_key, name_len, key_size, buff, buff_length, rec_len); if (0 == key_size) continue; /* indication of trouble parsing the block - don't use it */ if (mu_subsc) /* Subscript option chosen */ CHECK_KEY_RANGE(mu_start_key, mu_end_key, buff, rCnt, musz_range_done); if (skiprecs && (curroff < saveoff[lvl])) continue; /* skip these guys, we've already counted over there */ } status = read_block(nBlkId, &child_pBlkBase, &child_nLevl, lvl - 1); if (status != cdb_sc_normal) return status; last_rec = ((pRec + nRecLen) == pTop); if (lvl && (nBlkId != mu_int_adj_prev[lvl - 1])) CHECK_ADJACENCY(nBlkId, lvl - 1, mu_int_adj[lvl - 1]); first_iter = (curroff == saveoff[lvl]); next_endtree = endtree && last_rec; next_skiprecs = skiprecs && first_iter; status = dfs(lvl - 1, child_pBlkBase, next_endtree, next_skiprecs); if (status != cdb_sc_normal) { if (cdb_sc_endtree == status) { mu_size_cumulative[lvl][REC] += rCnt + 1; mu_size_cumulative[lvl][BLK]++; } return status; } first_iter = FALSE; } incr_recs = rCnt; incr_scans = 1; } /* make sure we can really move on from this block to the next: validate all blocks down to here */ memcpy(&sibhist.h[0], &gv_target->hist.h[lvl], SIZEOF(srch_blk_status) * (gv_target->hist.depth - lvl + 2)); if ((trans_num)0 == (ret_tn = t_end(&sibhist, NULL, TN_NOT_SPECIFIED))) return cdb_sc_restarted; mu_size_cumulative[lvl][BLK] += incr_scans; mu_size_cumulative[lvl][REC] += incr_recs; if (endtree || mu_ctrlc_occurred || mu_ctrly_occurred) return cdb_sc_endtree; /* note: usage slightly different from elsewhere, since we've already done validation */ assert(lvl >= targ_levl); memcpy(gv_target->alt_hist, &gv_target->hist, SIZEOF(srch_hist)); /* take a copy of most recently validated history */ gv_target->alt_hist->h[lvl - targ_levl + 1].curr_rec.offset++; /* don't recount the previously validated/counted path */ for (i = 0; i <= (lvl - targ_levl); i++) gv_target->alt_hist->h[i].curr_rec.offset = 0; /* Free up the cache record for the block we're done with. I.e. mark it available to whoever makes the next pass through * db_csh_getn. */ cr = gv_target->alt_hist->h[lvl - targ_levl].cr; assert((NULL != cr) || (dba_mm == cs_data->acc_meth)); if (NULL != cr) cr->refer = FALSE; gv_target->clue.end = 1; /* to set start_tn to earliest tn in history */ t_begin(ERR_MUSIZEFAIL, 0); /* start a new transaction and continue recursive search */ gv_target->clue.end = 0; return cdb_sc_normal; } enum cdb_sc read_block(block_id nBlkId, sm_uc_ptr_t *pBlkBase_ptr, int *nLevl_ptr, int desired_levl) { cache_rec_ptr_t cr; enum cdb_sc status; int cycle, i; register srch_blk_status *pCurr; register srch_hist *pTargHist; sm_uc_ptr_t pBlkBase; trans_num tn; unsigned char nLevl; pTargHist = &gv_target->hist; tn = cs_addrs->ti->curr_tn; if ((dba_mm != cs_data->acc_meth) && (ANY_ROOT_LEVL != desired_levl)) { /* avoid reading into a cache record we're already using in this transaction. prevents self-induced restarts. */ for (i = 0; i <= MAX_BT_DEPTH; i++) if (pTargHist->h[i].blk_num && (NULL != (cr = pTargHist->h[i].cr))) /* note: assignment */ cr->refer = TRUE; } # ifdef DEBUG /* restart occasionally */ if ((nBlkId % ((process_id % 25) + 25) == 0) && (t_tries == 0)) return cdb_sc_blkmod; # endif if (NULL == (pBlkBase = t_qread(nBlkId, (sm_int_ptr_t)&cycle, &cr))) return (enum cdb_sc)rdfail_detail; GET_AND_CHECK_LEVL(status, nLevl, desired_levl, pBlkBase); if (cdb_sc_normal != status) { assert(CDB_STAGNATE > t_tries); return status; } pCurr = &pTargHist->h[nLevl - targ_levl]; /* No blocks to read beneath input level */ if (ANY_ROOT_LEVL == desired_levl) { if (nLevl < targ_levl) pCurr = &pTargHist->h[0]; (pCurr + 1)->blk_num = 0; pTargHist->depth = (int)nLevl; } pCurr->cse = NULL; pCurr->blk_num = nBlkId; pCurr->buffaddr = pBlkBase; pCurr->tn = tn; pCurr->cycle = cycle; pCurr->cr = cr; *nLevl_ptr = nLevl; *pBlkBase_ptr = pBlkBase; return cdb_sc_normal; } fis-gtm-V7.0-005/sr_unix/mu_swap_root.c0000644000032200000250000004520714342376330016733 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2021 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_string.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "gdsblkops.h" #include "gdskill.h" #include "gdscc.h" #include "copy.h" #include "interlock.h" #include "muextr.h" #include "mu_reorg.h" /* Include prototypes */ #include "t_end.h" #include "t_retry.h" #include "mupip_reorg.h" #include "util.h" #include "t_begin.h" #include "op.h" #include "gvcst_protos.h" /* for gvcst_rtsib,gvcst_search prototype */ #include "gvcst_bmp_mark_free.h" #include "gvcst_kill_sort.h" #include "gtmmsg.h" #include "add_inter.h" #include "t_abort.h" #include "sleep_cnt.h" #include "wcs_sleep.h" #include "memcoherency.h" #include "gdsbml.h" #include "jnl_get_checksum.h" #include "t_qread.h" #include "t_create.h" #include "t_write_map.h" #include "t_write.h" #include "change_reg.h" GBLREF boolean_t mu_reorg_process; GBLREF boolean_t need_kip_incr; GBLREF char *update_array, *update_array_ptr; GBLREF cw_set_element cw_set[]; GBLREF gd_region *gv_cur_region; GBLREF gv_key *gv_altkey; GBLREF gv_key *gv_currkey, *gv_altkey; GBLREF gv_namehead *gv_target; GBLREF gv_namehead *reorg_gv_target; GBLREF inctn_opcode_t inctn_opcode; GBLREF inctn_opcode_t inctn_opcode; GBLREF kill_set *kill_set_tail; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_addrs *kip_csa; GBLREF sgmnt_data_ptr_t cs_data; GBLREF uint4 t_err; GBLREF uint4 update_trans; GBLREF uint4 update_array_size; GBLREF unsigned char cw_map_depth; GBLREF unsigned char cw_set_depth; GBLREF unsigned char rdfail_detail; GBLREF unsigned int t_tries; error_def(ERR_DBRDONLY); error_def(ERR_GBLNOEXIST); error_def(ERR_MAXBTLEVEL); error_def(ERR_MUREORGFAIL); error_def(ERR_MUTRUNCNOTBG); #define RETRY_SWAP (0) #define ABORT_SWAP (-1) void mu_swap_root(glist *gl_ptr, int *root_swap_statistic_ptr, block_id upg_mv_block) { block_id child_blk_id, free_blk_id; block_id save_root; boolean_t killed_global; enum cdb_sc status; gv_namehead *save_targ; int level; kill_set kill_set_list; node_local_ptr_t cnl; sgmnt_data_ptr_t csd; sgmnt_addrs *csa; sm_uc_ptr_t child_blk_ptr; srch_hist *dir_hist_ptr, *gvt_hist_ptr; trans_num curr_tn, ret_tn; unsigned int lcl_t_tries; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(mu_reorg_process); /* TODO: use upg_mv_block? */ gv_target = gl_ptr->gvt; gv_target->clue.end = 0; /* reset clue since reorg action on later globals might have invalidated it */ reorg_gv_target->gvname.var_name = gv_target->gvname.var_name; /* needed by SAVE_ROOTSRCH_ENTRY_STATE */ dir_hist_ptr = gv_target->alt_hist; gvt_hist_ptr = &(gv_target->hist); inctn_opcode = inctn_invalid_op; if (0 == upg_mv_block) { /* set gv_target/gv_currkey/gv_cur_region/cs_addrs/cs_data to correspond to in gl_ptr */ gv_target->root = 0; /* reset root so we recompute it in DO_OP_GVNAME below */ DO_OP_GVNAME(gl_ptr); } csa = cs_addrs; cnl = csa->nl; csd = cs_data; /* keep csd up to date; with MM, cs_data can change, and, dereferencing an older copy, cause SIG-11 */ if (gv_cur_region->read_only) /* TODO: ensure such a check exists early on in upgrade logic */ return; /* Cannot proceed for read-only data files */ killed_global = FALSE; while (0 == gv_target->root) { /* Global does not "exist" */ if ((0 != upg_mv_block) && (gv_target->hist.h[1].blk_num == gv_target->hist.h[0].blk_num)) { gv_target->root = gv_target->hist.h[0].blk_num; killed_global = TRUE; break; } gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_GBLNOEXIST, 2, GNAME(gl_ptr).len, GNAME(gl_ptr).addr); return; /* must have been an online rollback - no problem, a message suffices */ } if ((dba_mm == csd->acc_meth) && (0 == upg_mv_block)) /* return for now without doing any swapping operation because later mu_truncate * is going to issue the MUTRUNCNOTBG message. */ return; SET_GV_ALTKEY_TO_GBLNAME_FROM_GV_CURRKEY; /* set up gv_altkey to be just the gblname */ /* ------------ Swap root block of global variable tree --------- */ t_begin(ERR_MUREORGFAIL, UPDTRNS_DB_UPDATED_MASK); for (;;) { curr_tn = csa->ti->curr_tn; kill_set_list.used = 0; if (!killed_global) { save_root = gv_target->root; gv_target->root = csa->dir_tree->root; gv_target->clue.end = 0; if (cdb_sc_normal != (status = gvcst_search(gv_altkey, dir_hist_ptr))) { /* Assign directory tree path to dir_hist_ptr */ assert(t_tries < CDB_STAGNATE); gv_target->root = save_root; t_retry(status); continue; } gv_target->root = save_root; gv_target->clue.end = 0; if (cdb_sc_normal != (gvcst_search(gv_currkey, NULL))) { /* Assign global variable tree path to gvt_hist_ptr */ assert(t_tries < CDB_STAGNATE); t_retry(status); continue; } } /* we've already searched the directory tree in op_gvname/t_retry and obtained gv_target->root. * Should restart with gvtrootmod2 if they don't agree. gvcst_root_search is the final arbiter. * Really need that for debug info and also should assert(gv_currkey is global name). */ free_blk_id = mu_swap_root_blk(gl_ptr, gvt_hist_ptr, dir_hist_ptr, &kill_set_list, curr_tn, upg_mv_block); if (RETRY_SWAP == free_blk_id) continue; else if (ABORT_SWAP == free_blk_id) break; DECR_KIP(csd, csa, kip_csa); *root_swap_statistic_ptr += 1; break; } if (DIR_ROOT == upg_mv_block) return; /* ------------ Swap blocks in branch of directory tree --------- */ for (level = 0; (0 == upg_mv_block) && (level <= MAX_BT_DEPTH); level++) { t_begin(ERR_MUREORGFAIL, UPDTRNS_DB_UPDATED_MASK); for (;;) { curr_tn = csa->ti->curr_tn; kill_set_list.used = 0; save_root = gv_target->root; gv_target->root = csa->dir_tree->root; gv_target->clue.end = 0; if (cdb_sc_normal != (status = gvcst_search(gv_altkey, dir_hist_ptr))) { /* assign branch path of directory tree into dir_hist_ptr */ assert(t_tries < CDB_STAGNATE); gv_target->root = save_root; t_retry(status); continue; } gv_target->root = save_root; gv_target->clue.end = 0; if (level >= dir_hist_ptr->depth) { /* done */ t_abort(gv_cur_region, csa); return; } child_blk_ptr = dir_hist_ptr->h[level].buffaddr; child_blk_id = dir_hist_ptr->h[level].blk_num; assert(csa->dir_tree->root != child_blk_id); free_blk_id = swap_root_or_directory_block(level + 1, level, dir_hist_ptr, child_blk_id, child_blk_ptr, &kill_set_list, curr_tn, 0); if (level == 0) /* set level as 1 to mark this kill set is for level-0 block in directory tree. * The kill-set level later will be used in gvcst_bmp_markfree to assign a special value to * cw_set_element, which will be eventually used by t_end to write the block to snapshot */ kill_set_list.blk[kill_set_list.used - 1].level = 1; if (RETRY_SWAP == free_blk_id) continue; else if (ABORT_SWAP == free_blk_id) break; update_trans = UPDTRNS_DB_UPDATED_MASK; inctn_opcode = inctn_mu_reorg; assert(1 == kill_set_list.used); need_kip_incr = TRUE; if (!csa->now_crit) WAIT_ON_INHIBIT_KILLS(cnl, MAXWAIT2KILL); DEBUG_ONLY(lcl_t_tries = t_tries); TREF(in_mu_swap_root_state) = MUSWP_DIRECTORY_SWAP; if ((trans_num)0 == (ret_tn = t_end(dir_hist_ptr, NULL, TN_NOT_SPECIFIED))) { TREF(in_mu_swap_root_state) = MUSWP_NONE; need_kip_incr = FALSE; assert(NULL == kip_csa); continue; } TREF(in_mu_swap_root_state) = MUSWP_NONE; gvcst_kill_sort(&kill_set_list); TREF(in_mu_swap_root_state) = MUSWP_FREE_BLK; GVCST_BMP_MARK_FREE(&kill_set_list, ret_tn, inctn_mu_reorg, inctn_bmp_mark_free_mu_reorg, inctn_opcode, csa); TREF(in_mu_swap_root_state) = MUSWP_NONE; DECR_KIP(csd, csa, kip_csa); break; } } DEFERRED_EXIT_REORG_CHECK; /* a single directory tree has to be quick, so check at end, rather than each DECR_KIP */ return; } block_id mu_swap_root_blk(glist *gl_ptr, srch_hist *gvt_hist_ptr, srch_hist *dir_hist_ptr, kill_set *kill_set_list, trans_num curr_tn, block_id upg_mv_block) { block_id free_blk_id, root_blk_id; boolean_t tn_aborted; int root_blk_lvl; sm_uc_ptr_t root_blk_ptr; trans_num ret_tn; unsigned int lcl_t_tries; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; root_blk_lvl = gvt_hist_ptr->depth; assert(0 < root_blk_lvl); root_blk_ptr = gvt_hist_ptr->h[root_blk_lvl].buffaddr; root_blk_id = gvt_hist_ptr->h[root_blk_lvl].blk_num; assert((CDB_STAGNATE > t_tries) || (gv_target->root == gvt_hist_ptr->h[root_blk_lvl].blk_num)); free_blk_id = swap_root_or_directory_block(0, root_blk_lvl, dir_hist_ptr, root_blk_id, root_blk_ptr, kill_set_list, curr_tn, upg_mv_block); if ((RETRY_SWAP == free_blk_id) || (ABORT_SWAP == free_blk_id)) return free_blk_id; assert(ABORT_SWAP < free_blk_id); update_trans = UPDTRNS_DB_UPDATED_MASK; inctn_opcode = inctn_mu_reorg; assert((1 == kill_set_list->used) || ((DIR_ROOT == gv_target->root) && (1 == gv_target->hist.h[1].blk_num))); need_kip_incr = TRUE; if (!cs_addrs->now_crit) WAIT_ON_INHIBIT_KILLS(cs_addrs->nl, MAXWAIT2KILL); DEBUG_ONLY(lcl_t_tries = t_tries); TREF(in_mu_swap_root_state) = MUSWP_INCR_ROOT_CYCLE; assert(!TREF(in_gvcst_redo_root_search)); if ((trans_num)0 == (ret_tn = t_end(gvt_hist_ptr, (DIR_ROOT != root_blk_id) ? dir_hist_ptr : NULL, TN_NOT_SPECIFIED))) { TREF(in_mu_swap_root_state) = MUSWP_NONE; need_kip_incr = FALSE; assert(NULL == kip_csa); ABORT_TRANS_IF_GBL_EXIST_NOMORE(lcl_t_tries, tn_aborted); if (tn_aborted) { /* It is not an error if the global (that once existed) doesn't exist anymore (due to ROLLBACK) */ gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_GBLNOEXIST, 2, GNAME(gl_ptr).len, GNAME(gl_ptr).addr); return curr_tn; } return RETRY_SWAP; } TREF(in_mu_swap_root_state) = MUSWP_NONE; /* Note that this particular process's csa->root_search_cycle is now behind cnl->root_search_cycle. * This forces a cdb_sc_gvtrootmod2 restart in gvcst_bmp_mark_free below. TODO: how to handle after refactor??? */ assert(cs_addrs->nl->root_search_cycle > cs_addrs->root_search_cycle); if (0 != upg_mv_block) cs_addrs->root_search_cycle = cs_addrs->nl->root_search_cycle; gvcst_kill_sort(kill_set_list); GVCST_BMP_MARK_FREE(kill_set_list, ret_tn, inctn_mu_reorg, inctn_bmp_mark_free_mu_reorg, inctn_opcode, cs_addrs); return free_blk_id; } /* Finds a free block and adds information to update array and cw_set */ block_id swap_root_or_directory_block(int parent_blk_lvl, int child_blk_lvl, srch_hist *dir_hist_ptr, block_id child_blk_id, sm_uc_ptr_t child_blk_ptr, kill_set *kill_set_list, trans_num curr_tn, block_id upg_mv_block) { blk_segment *bs1, *bs_ptr; block_id hint_blk_num, free_blk_id, parent_blk_id, total_blks, num_local_maps, master_bit, free_bit, temp_blk; boolean_t free_blk_recycled, child_long_blk_id, parent_long_blk_id; cw_set_element *tmpcse; int blk_seg_cnt, blk_size; int parent_blk_size, child_blk_size, bsiz; int rec_size1, curr_offset, bpntr_end, hdr_len; int tmp_cmpc, child_blk_id_sz, parent_blk_id_sz; int4 hint_bit, maxbitsthismap; jnl_buffer_ptr_t jbbp; /* jbbp is non-NULL only if before-image journaling */ node_local_ptr_t cnl; sgmnt_data_ptr_t csd; sgmnt_addrs *csa; sm_uc_ptr_t parent_blk_ptr, bn_ptr, saved_blk; srch_blk_status bmlhist, freeblkhist; unsigned char save_cw_set_depth; unsigned short temp_ushort; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; csd = cs_data; csa = cs_addrs; cnl = csa->nl; blk_size = csd->blk_size; child_long_blk_id = IS_64_BLK_ID(child_blk_ptr); child_blk_id_sz = SIZEOF_BLK_ID(child_long_blk_id); /* Find a free/recycled block for new block location. */ hint_blk_num = upg_mv_block; total_blks = csa->ti->total_blks; num_local_maps = DIVIDE_ROUND_UP(total_blks, BLKS_PER_LMAP); master_bit = bmm_find_free((hint_blk_num / BLKS_PER_LMAP), csa->bmm, num_local_maps); if ((NO_FREE_SPACE == master_bit)) { assert(0 == upg_mv_block); t_abort(gv_cur_region, csa); return ABORT_SWAP; } bmlhist.blk_num = master_bit * BLKS_PER_LMAP; if (NULL == (bmlhist.buffaddr = t_qread(bmlhist.blk_num, (sm_int_ptr_t)&bmlhist.cycle, &bmlhist.cr))) { /* WARNING: assignment above */ assert(0 == upg_mv_block); assert(t_tries < CDB_STAGNATE); t_retry((enum cdb_sc)rdfail_detail); return RETRY_SWAP; } hint_bit = (0 != upg_mv_block) ? ((upg_mv_block % BLKS_PER_LMAP) - 1) : 0; /* (total_blks - bmlhist.blk_num) can be cast because it should never be larger then BLKS_PER_LMAP */ assert(((master_bit + 1) < num_local_maps) || (BLKS_PER_LMAP >= (total_blks - bmlhist.blk_num))); maxbitsthismap = (master_bit != (num_local_maps - 1)) ? BLKS_PER_LMAP : (int4)(total_blks - bmlhist.blk_num); free_bit = bm_find_blk(hint_bit, bmlhist.buffaddr + SIZEOF(blk_hdr), maxbitsthismap, &free_blk_recycled); free_blk_id = bmlhist.blk_num + free_bit; assert((0 == upg_mv_block) || (upg_mv_block + 1 == free_blk_id)); if (DIR_ROOT >= free_blk_id) { /* Bitmap block 0 and directory tree root block 1 should always be marked busy. */ assert(0 == upg_mv_block); assert(t_tries < CDB_STAGNATE); t_retry(cdb_sc_badbitmap); return RETRY_SWAP; } if ((child_blk_id <= free_blk_id) && (0 == upg_mv_block)) { /* stop swapping root or DT blocks once the database is truncated well enough. A good heuristic for this is * to check if the block is to be swapped into a higher block number and if so do not swap */ t_abort(gv_cur_region, csa); return ABORT_SWAP; } /* ====== begin update array ====== * Four blocks get changed. * 1. Free block becomes busy and gains the contents of child (root block/directory tree block) * 2. Parent block in directory tree remains busy, but points to new root block location. * 3. Free block's corresponding bitmap reflects above change. * 4. Child block gets marked recycled in bitmap. (GVCST_BMP_MARK_FREE) */ CHECK_AND_RESET_UPDATE_ARRAY; if (free_blk_recycled) { /* Otherwise, it's a completely free block, in which case no need to read. */ freeblkhist.blk_num = free_blk_id; if (NULL == (freeblkhist.buffaddr = t_qread(free_blk_id, (sm_int_ptr_t)&freeblkhist.cycle, &freeblkhist.cr))) { assert(t_tries < CDB_STAGNATE); t_retry((enum cdb_sc)rdfail_detail); return RETRY_SWAP; } } child_blk_size = ((blk_hdr_ptr_t)child_blk_ptr)->bsiz; BLK_INIT(bs_ptr, bs1); BLK_ADDR(saved_blk, child_blk_size, unsigned char); memcpy(saved_blk, child_blk_ptr, child_blk_size); BLK_SEG(bs_ptr, saved_blk + SIZEOF(blk_hdr), child_blk_size - SIZEOF(blk_hdr)); assert(blk_seg_cnt == child_blk_size); if (!BLK_FINI(bs_ptr, bs1)) { assert(t_tries < CDB_STAGNATE); t_retry(cdb_sc_blkmod); return RETRY_SWAP; } tmpcse = &cw_set[cw_set_depth]; (free_blk_recycled) ? BIT_SET_RECYCLED_AND_CLEAR_FREE(tmpcse->blk_prior_state) : BIT_CLEAR_RECYCLED_AND_SET_FREE(tmpcse->blk_prior_state); t_create(free_blk_id, (unsigned char *)bs1, 0, 0, child_blk_lvl); tmpcse->mode = gds_t_acquired; if (!free_blk_recycled || !cs_data->db_got_to_v5_once) tmpcse->old_block = NULL; else { tmpcse->old_block = freeblkhist.buffaddr; tmpcse->cr = freeblkhist.cr; tmpcse->cycle = freeblkhist.cycle; jbbp = (JNL_ENABLED(csa) && csa->jnl_before_image) ? csa->jnl->jnl_buff : NULL; if ((NULL != jbbp) && (((blk_hdr_ptr_t)tmpcse->old_block)->tn < jbbp->epoch_tn)) { bsiz = ((blk_hdr_ptr_t)(tmpcse->old_block))->bsiz; if (bsiz > blk_size) { assert(CDB_STAGNATE > t_tries); t_retry(cdb_sc_lostbmlcr); return RETRY_SWAP; } JNL_GET_CHECKSUM_ACQUIRED_BLK(tmpcse, csd, csa, tmpcse->old_block, bsiz); } } if (DIR_ROOT != child_blk_id) { /* 2. Parent block in directory tree remains busy, but points to new child block location. */ parent_blk_ptr = dir_hist_ptr->h[parent_blk_lvl].buffaddr; /* 0 == parent_blk_lvl if moving a gvt root block */ parent_blk_id = dir_hist_ptr->h[parent_blk_lvl].blk_num; parent_long_blk_id = IS_64_BLK_ID(parent_blk_ptr); parent_blk_id_sz = SIZEOF_BLK_ID(parent_long_blk_id); curr_offset = dir_hist_ptr->h[parent_blk_lvl].curr_rec.offset; parent_blk_size = ((blk_hdr_ptr_t)parent_blk_ptr)->bsiz; GET_RSIZ(rec_size1, (parent_blk_ptr + curr_offset)); if ((parent_blk_size < rec_size1 + curr_offset) || (bstar_rec_size(parent_long_blk_id) > rec_size1)) { assert(t_tries < CDB_STAGNATE); t_retry(cdb_sc_blkmod); return RETRY_SWAP; } BLK_INIT(bs_ptr, bs1); if (0 == parent_blk_lvl) /* There can be collation stuff in the record value after the block pointer. See gvcst_root_search. */ hdr_len = SIZEOF(rec_hdr) + gv_altkey->end + 1 - EVAL_CMPC((rec_hdr_ptr_t)(parent_blk_ptr + curr_offset)); else hdr_len = rec_size1 - parent_blk_id_sz; bpntr_end = curr_offset + hdr_len + parent_blk_id_sz; BLK_SEG(bs_ptr, parent_blk_ptr + SIZEOF(blk_hdr), curr_offset + hdr_len - SIZEOF(blk_hdr)); BLK_ADDR(bn_ptr, parent_blk_id_sz, unsigned char); WRITE_BLK_ID(parent_long_blk_id, free_blk_id, bn_ptr); BLK_SEG(bs_ptr, bn_ptr, parent_blk_id_sz); BLK_SEG(bs_ptr, parent_blk_ptr + bpntr_end, parent_blk_size - bpntr_end); assert(blk_seg_cnt == parent_blk_size); if (!BLK_FINI(bs_ptr, bs1)) { assert(t_tries < CDB_STAGNATE); t_retry(cdb_sc_blkmod); return RETRY_SWAP; } t_write(&dir_hist_ptr->h[parent_blk_lvl], (unsigned char *)bs1, 0, 0, parent_blk_lvl, FALSE, TRUE, GDS_WRITE_KILLTN); /* To indicate later snapshot file writing process during fast_integ must write the block to snapshot file */ BIT_SET_DIR_TREE(cw_set[cw_set_depth - 1].blk_prior_state); } /* 3. Free block's corresponding bitmap reflects above change. */ PUT_BLK_ID(update_array_ptr, free_bit); save_cw_set_depth = cw_set_depth; /* Bit maps go on end of cw_set (more fake acquired) */ assert(!cw_map_depth); t_write_map(&bmlhist, (uchar_ptr_t)update_array_ptr, curr_tn, 1); cw_map_depth = cw_set_depth; cw_set_depth = save_cw_set_depth; update_array_ptr += SIZEOF(block_id); temp_blk = 0; PUT_BLK_ID(update_array_ptr, temp_blk); update_array_ptr += SIZEOF(block_id); assert(1 == cw_set[cw_map_depth - 1]. reference_cnt); /* 4. Child block gets marked recycled in bitmap. (GVCST_BMP_MARK_FREE) */ kill_set_list->blk[kill_set_list->used].flag = 0; kill_set_list->blk[kill_set_list->used].level = 0; kill_set_list->blk[kill_set_list->used++].block = child_blk_id; return free_blk_id; } fis-gtm-V7.0-005/sr_unix/mu_term_setup.c0000755000032200000250000000504414342376330017103 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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 #include "gtm_stdio.h" #include "gtm_unistd.h" #include "gtm_termios.h" #include "gtm_signal.h" /* for SIGPROCMASK used inside Tcsetattr */ #include "eintr_wrappers.h" #include "mu_term_setup.h" #define STDOUT_FILENO 1 #define STDERR_FILENO 2 static struct termios tty_settings; static boolean_t mu_get_term_invoked = FALSE; static boolean_t get_stdout_charc_pass = TRUE; static boolean_t get_stderr_charc_pass = TRUE; void mu_get_term_characterstics(void) { assert(!mu_get_term_invoked); /* No need to invoke this more than once per process. If assert fails fix second caller. */ mu_get_term_invoked = TRUE; if (get_stdout_charc_pass = isatty(STDOUT_FILENO)) { if (-1 == tcgetattr(STDOUT_FILENO, &tty_settings)) { get_stdout_charc_pass = FALSE; PERROR("tcgetattr :"); FPRINTF(stderr, "Unable to get terminal characterstics for standard out\n"); } } else if (get_stderr_charc_pass = isatty(STDERR_FILENO)) { if (-1 == tcgetattr(STDERR_FILENO, &tty_settings)) { get_stderr_charc_pass = FALSE; PERROR("tcgetattr :"); FPRINTF(stderr, "Unable to get terminal characterstics for standard err\n"); } } } void mu_reset_term_characterstics(void) { int tcsetattr_res; int save_errno; if (!mu_get_term_invoked) return; /* We did not initialize "tty_settings" in this process so dont use it */ /* Do not use TCSAFLUSH as it drains all buffered (but yet unprocessed) input in the terminal * even if that was for the next command at the shell prompt. So use TCSADRAIN instead. */ if (get_stdout_charc_pass) { Tcsetattr(STDOUT_FILENO, TCSADRAIN, &tty_settings, tcsetattr_res, save_errno); if (-1 == tcsetattr_res) { PERROR("tcsetattr :"); FPRINTF(stderr, "Unable to set terminal characterstics for standard out\n"); } } else if (get_stderr_charc_pass) { Tcsetattr(STDERR_FILENO, TCSADRAIN, &tty_settings, tcsetattr_res, save_errno); if (-1 == tcsetattr_res) { PERROR("tcsetattr :"); FPRINTF(stderr, "Unable to set terminal characterstics for standard err\n"); } } } fis-gtm-V7.0-005/sr_unix/mu_term_setup.h0000755000032200000250000000124114342376330017103 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef MU_TERM_SETUP_H_INCLUDED #define MU_TERM_SETUP_H_INCLUDED void mu_get_term_characterstics(void); void mu_reset_term_characterstics(void); #endif fis-gtm-V7.0-005/sr_unix/mu_trig_trgfile.h0000644000032200000250000000115114342376330017372 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef MU_TRIG_TRGFILE_H_INCLUDED #define MU_TRIG_TRGFILE_H_INCLUDED void mu_trig_trgfile(char *trigger_filename, int trigger_filename_len, boolean_t noprompt); #endif fis-gtm-V7.0-005/sr_unix/mu_truncate.c0000644000032200000250000004743114342376330016544 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2021 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. * * * ****************************************************************/ /*************************************************************************************************************** * mu_truncate.c: * This program truncates the db file corresponding to a given region, if there is enough free space. * It operates in two phases: * Phase 1: * Working from the end of the file towards its beginning, mu_truncate sets recycled blocks * free, which allows t_end to write PBLKS if needed. If it sees a busy block, skip ahead * to Phase 2. * Phase 2: * Grab crit, write JRT_TRUNC and INCTN journal records, reduce csa->ti->total_blks and * finally truncate the file. If mu_truncate crashes here, recover_truncate.c finishes the job. * Meanwhile: * Before completing Phase 2, mu_truncate has to detect other processes marking blocks * busy through gvcst_map_build. Likewise, other processes have to detect concurrent * truncates that occur between allocating blocks outside crit and transaction completion. * mu_truncate returns TRUE if and only if it completes successfully or halts for a benign reason. * **************************************************************************************************************/ #include "mdef.h" #include #include "gtm_string.h" #include "gtm_time.h" #include "gdsroot.h" #include "gdsblk.h" #include "gdsbml.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblkops.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "min_max.h" #include "t_qread.h" #include "dse.h" #include "gtmmsg.h" #include "t_begin.h" #include "t_write_map.h" #include "t_abort.h" #include "t_retry.h" #include "t_end.h" #include "wbox_test_init.h" #include "error.h" #include "t_recycled2free.h" #include "cdb_sc.h" #include "eintr_wrappers.h" #include "gtmimagename.h" #include "mu_truncate.h" #include "gtmio.h" #include "util.h" #include "anticipatory_freeze.h" #include "sleep_cnt.h" #include "wcs_sleep.h" #include "interlock.h" #include "gdsbgtr.h" #include "copy.h" #include "shmpool.h" #include "clear_cache_array.h" #include "wcs_flu.h" #include "repl_msg.h" #include "gtmsource.h" #include "db_write_eof_block.h" #include "mvalconv.h" error_def(ERR_BUFFLUFAILED); error_def(ERR_DBFILERR); error_def(ERR_DBFSYNCERR); error_def(ERR_IOERROR); error_def(ERR_JNLFLUSH); error_def(ERR_MUTRUNCFAIL); error_def(ERR_MUTRUNCNOSPACE); error_def(ERR_MUTRUNCERROR); error_def(ERR_MUTRUNCNOV4); error_def(ERR_MUTRUNCNOTBG); error_def(ERR_MUTRUNCSSINPROG); error_def(ERR_MUTRUNCSUCCESS); error_def(ERR_MUTRUNCBACKINPROG); error_def(ERR_TEXT); error_def(ERR_TRUNCBACKUPPROG); error_def(ERR_TRUNCNOTRUN); error_def(ERR_TRUNCSSINPROG); error_def(ERR_MUKEEPPERCENT); error_def(ERR_MUTRUNCNOSPKEEP); GBLREF bool mu_ctrlc_occurred; GBLREF bool mu_ctrly_occurred; GBLREF sgmnt_data_ptr_t cs_data; GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF gv_namehead *gv_target; GBLREF inctn_opcode_t inctn_opcode; GBLREF uint4 update_trans; GBLREF char *update_array, *update_array_ptr; GBLREF uint4 update_array_size; GBLREF unsigned char rdfail_detail; GBLREF uint4 process_id; GBLREF unsigned int t_tries; GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; GBLREF volatile int4 db_fsync_in_prog; /* for DB_FSYNC macro usage */ GBLREF jnl_gbls_t jgbl; GBLREF int num_additional_processors; GBLREF jnlpool_addrs_ptr_t jnlpool; boolean_t mu_truncate(int4 truncate_percent, mval *keep_mval) { sgmnt_addrs *csa; sgmnt_data_ptr_t csd; block_id num_local_maps, lmap_num, lmap_blk_num, found_busy_blk; int bml_status, sigkill; int save_errno; int ftrunc_status; uint4 jnl_status; block_id old_total, new_total, old_free, new_free, keep_blocks, trunc_blocks; int4 blks_in_lmap, blk, end_blocks; gtm_uint64_t before_trunc_file_size; off_t trunc_file_size; off_t padding; uchar_ptr_t lmap_addr; boolean_t was_crit; srch_blk_status bmphist; srch_blk_status *blkhist; srch_hist alt_hist; trans_num curr_tn; blk_hdr_ptr_t lmap_blk_hdr; block_id *blkid_ptr; unix_db_info *udi; jnl_private_control *jpc; jnl_buffer_ptr_t jbp; char *err_msg; intrpt_state_t prev_intrpt_state; off_t offset; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; csa = cs_addrs; csd = cs_data; keep_blocks = 0; if (dba_mm == csd->acc_meth) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_MUTRUNCNOTBG, 2, REG_LEN_STR(gv_cur_region)); return TRUE; } if (((GDSVCURR != csd->desired_db_format) && !(BLK_ID_32_VER > csd->desired_db_format)) || (csd->blks_to_upgrd != 0)) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_MUTRUNCNOV4, 2, REG_LEN_STR(gv_cur_region)); return TRUE; } trunc_blocks = (truncate_percent * csa->ti->total_blks / 100); if (csa->ti->free_blocks < trunc_blocks) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_MUTRUNCNOSPACE, 3, REG_LEN_STR(gv_cur_region), truncate_percent); return TRUE; } if (keep_mval->str.addr) { /* Some value was defined by the user*/ keep_blocks = MIN(MV_FORCE_ULONG(keep_mval), MAXTOTALBLKS(csd)); if (keep_blocks && ('%' == keep_mval->str.addr[keep_mval->str.len - 1])) keep_blocks = (csa->ti->total_blks * keep_blocks) / 100; if ((csa->ti->total_blks - trunc_blocks) < keep_blocks) /*No truncate*/ { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_MUTRUNCNOSPKEEP, 4, REG_LEN_STR(gv_cur_region), truncate_percent, &keep_blocks); return FALSE; } } /* already checked for parallel truncates on this region --- see mupip_reorg.c */ gv_target = NULL; assert(csa->nl->trunc_pid == process_id); assert(dba_mm != csd->acc_meth); old_total = csa->ti->total_blks; old_free = csa->ti->free_blocks; sigkill = 0; found_busy_blk = 0; memset(&alt_hist, 0, SIZEOF(alt_hist)); /* null-initialize history */ assert(BLKS_PER_LMAP == csd->bplmap); /* (old_total % BLKS_PER_LMAP) can be cast because it should never be larger then BLKS_PER_LMAP */ end_blocks = (int4)(old_total % BLKS_PER_LMAP); /* blocks in the last lmap (first one we start scanning) */ if (0 == end_blocks) end_blocks = BLKS_PER_LMAP; num_local_maps = DIVIDE_ROUND_UP(old_total, BLKS_PER_LMAP); /* ======================================== PHASE 1 ======================================== */ for (lmap_num = num_local_maps - 1; (lmap_num > 0 && !found_busy_blk); lmap_num--) { if (mu_ctrly_occurred || mu_ctrlc_occurred) return TRUE; assert(csa->ti->total_blks >= old_total); /* otherwise, a concurrent truncate happened... */ if (csa->ti->total_blks != old_total) /* Extend (likely called by mupip extend) -- don't truncate */ { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_MUTRUNCNOSPACE, 3, REG_LEN_STR(gv_cur_region), truncate_percent); return TRUE; } lmap_blk_num = lmap_num * BLKS_PER_LMAP; if (csa->nl->highest_lbm_with_busy_blk >= lmap_blk_num) { found_busy_blk = lmap_blk_num; break; } blks_in_lmap = (lmap_num == num_local_maps - 1) ? end_blocks : BLKS_PER_LMAP; /* Loop through non-bitmap blocks of this lmap, do recycled2free */ DBGEHND((stdout, "DBG:: lmap_num = [%lu], lmap_blk_num = [%lu], blks_in_lmap = [%lu]\n", lmap_num, lmap_blk_num, blks_in_lmap)); for (blk = 1; blk < blks_in_lmap && blk != -1 && !found_busy_blk;) { t_begin(ERR_MUTRUNCFAIL, UPDTRNS_DB_UPDATED_MASK); for (;;) /* retry loop for recycled to free transactions */ { curr_tn = csd->trans_hist.curr_tn; /* Read the nth local bitmap into memory */ bmphist.blk_num = lmap_blk_num; bmphist.buffaddr = t_qread(bmphist.blk_num, &bmphist.cycle, &bmphist.cr); lmap_blk_hdr = (blk_hdr_ptr_t)bmphist.buffaddr; if (!(bmphist.buffaddr) || (BM_SIZE(BLKS_PER_LMAP) != lmap_blk_hdr->bsiz)) { /* Could not read the block successfully. Retry. */ t_retry((enum cdb_sc)rdfail_detail); continue; } lmap_addr = bmphist.buffaddr + SIZEOF(blk_hdr); /* starting from the hint (blk itself), find the first busy or recycled block */ blk = bml_find_busy_recycled(blk, lmap_addr, blks_in_lmap, &bml_status); assert(BLKS_PER_LMAP > blk); if (blk == -1 || blk >= blks_in_lmap) { /* done with this lmap, continue to next */ t_abort(gv_cur_region, csa); break; } else if (BLK_BUSY == bml_status || csa->nl->highest_lbm_with_busy_blk >= lmap_blk_num) { /* stop processing blocks... skip ahead to phase 2 */ found_busy_blk = lmap_blk_num; t_abort(gv_cur_region, csa); break; } else if (BLK_RECYCLED == bml_status) { /* Write PBLK records for recycled blocks only if before_image journaling is * enabled. t_end() takes care of checking if journaling is enabled and * writing PBLK record. We have to at least mark the recycled block as free. */ RESET_UPDATE_ARRAY; update_trans = UPDTRNS_DB_UPDATED_MASK; *((block_id *)update_array_ptr) = blk; update_array_ptr += SIZEOF(block_id); *(block_id *)update_array_ptr = 0; alt_hist.h[1].blk_num = 0; alt_hist.h[0].level = 0; alt_hist.h[0].cse = NULL; alt_hist.h[0].tn = curr_tn; alt_hist.h[0].blk_num = lmap_blk_num + blk; alt_hist.h[0].buffaddr = t_qread(alt_hist.h[0].blk_num, &alt_hist.h[0].cycle, &alt_hist.h[0].cr); if (!alt_hist.h[0].buffaddr) { t_retry((enum cdb_sc)rdfail_detail); continue; } if (!t_recycled2free(&alt_hist.h[0])) { t_retry(cdb_sc_lostbmlcr); continue; } t_write_map(&bmphist, (unsigned char *)update_array, curr_tn, 0); /* Set the opcode for INCTN record written by t_end() */ inctn_opcode = inctn_blkmarkfree; if ((trans_num)0 == t_end(&alt_hist, NULL, TN_NOT_SPECIFIED)) continue; /* block processed, scan from the next one */ blk++; break; } else { assert(t_tries < CDB_STAGNATE); t_retry(cdb_sc_badbitmap); continue; } } /* END recycled2free retry loop */ } /* END scanning blocks of this particular lmap */ /* Write PBLK for the bitmap block, in case it hasn't been written i.e. t_end() was never called above */ /* Do a transaction that just increments the bitmap block's tn so that t_end() can do its thing */ DBGEHND((stdout, "DBG:: bitmap block inctn -- lmap_blk_num = [%lu]\n", lmap_blk_num)); t_begin(ERR_MUTRUNCFAIL, UPDTRNS_DB_UPDATED_MASK); for (;;) { RESET_UPDATE_ARRAY; BLK_ADDR(blkid_ptr, SIZEOF(block_id), block_id); *blkid_ptr = 0; update_trans = UPDTRNS_DB_UPDATED_MASK; inctn_opcode = inctn_mu_reorg; /* inctn_mu_truncate */ curr_tn = csd->trans_hist.curr_tn; blkhist = &alt_hist.h[0]; blkhist->blk_num = lmap_blk_num; blkhist->tn = curr_tn; blkhist->cse = NULL; /* start afresh (do not use value from previous retry) */ /* Read the nth local bitmap into memory */ blkhist->buffaddr = t_qread(lmap_blk_num, (sm_int_ptr_t)&blkhist->cycle, &blkhist->cr); lmap_blk_hdr = (blk_hdr_ptr_t)blkhist->buffaddr; if (!(blkhist->buffaddr) || (BM_SIZE(BLKS_PER_LMAP) != lmap_blk_hdr->bsiz)) { /* Could not read the block successfully. Retry. */ t_retry((enum cdb_sc)rdfail_detail); continue; } t_write_map(blkhist, (unsigned char *)blkid_ptr, curr_tn, 0); blkhist->blk_num = 0; /* create empty history for bitmap block */ if ((trans_num)0 == t_end(&alt_hist, NULL, TN_NOT_SPECIFIED)) continue; break; } } /* END scanning lmaps */ /* ======================================== PHASE 2 ======================================== */ assert(!csa->now_crit); for (;;) { /* wait for FREEZE, we don't want to truncate a frozen database */ grab_crit(gv_cur_region, WS_88); if (FROZEN_CHILLED(cs_addrs)) DO_CHILLED_AUTORELEASE(csa, cs_data); if (!FROZEN(cs_data) && !IS_REPL_INST_FROZEN) break; rel_crit(gv_cur_region); while (FROZEN(cs_data) || IS_REPL_INST_FROZEN) { hiber_start(1000); if (FROZEN_CHILLED(cs_addrs) && CHILLED_AUTORELEASE(cs_addrs)) break; } } assert(csa->nl->trunc_pid == process_id); /* Flush pending updates to disk. If this is not done, old updates can be flushed AFTER ftruncate, extending the file. */ if (!wcs_flu(WCSFLU_FLUSH_HDR | WCSFLU_WRITE_EPOCH | WCSFLU_MSYNC_DB)) { assert(FALSE); gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_BUFFLUFAILED, 4, LEN_AND_LIT("MUPIP REORG TRUNCATE"), DB_LEN_STR(gv_cur_region)); rel_crit(gv_cur_region); return FALSE; } csa->nl->highest_lbm_with_busy_blk = MAX(found_busy_blk, csa->nl->highest_lbm_with_busy_blk); assert(IS_BITMAP_BLK(csa->nl->highest_lbm_with_busy_blk)); new_total = MAX(MIN(old_total, csa->nl->highest_lbm_with_busy_blk + BLKS_PER_LMAP), keep_blocks); if (mu_ctrly_occurred || mu_ctrlc_occurred) { rel_crit(gv_cur_region); return TRUE; } else if (csa->ti->total_blks != old_total || new_total == old_total) { assert(csa->ti->total_blks >= old_total); /* Better have been an extend, not a truncate... */ gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_MUTRUNCNOSPACE, 3, REG_LEN_STR(gv_cur_region), truncate_percent); rel_crit(gv_cur_region); return TRUE; } else if (((GDSVCURR != csd->desired_db_format) && !(BLK_ID_32_VER > csd->desired_db_format)) || csd->blks_to_upgrd != 0 || !csd->fully_upgraded) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_MUTRUNCNOV4, 2, REG_LEN_STR(gv_cur_region)); rel_crit(gv_cur_region); return TRUE; } else if (SNAPSHOTS_IN_PROG(csa->nl)) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_MUTRUNCSSINPROG, 2, REG_LEN_STR(gv_cur_region)); rel_crit(gv_cur_region); return TRUE; } else if (BACKUP_NOT_IN_PROGRESS != cs_addrs->nl->nbb) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_MUTRUNCBACKINPROG, 2, REG_LEN_STR(gv_cur_region)); rel_crit(gv_cur_region); return TRUE; } DEFER_INTERRUPTS(INTRPT_IN_TRUNC, prev_intrpt_state); if (JNL_ENABLED(csa)) { /* Write JRT_TRUNC and INCTN records */ if (!jgbl.dont_reset_gbl_jrec_time) SET_GBL_JREC_TIME; /* needed before jnl_ensure_open as that can write jnl records */ jpc = csa->jnl; jbp = jpc->jnl_buff; /* Before writing to jnlfile, adjust jgbl.gbl_jrec_time if needed to maintain time order * of jnl records. This needs to be done BEFORE the jnl_ensure_open as that could write * journal records (if it decides to switch to a new journal file). */ ADJUST_GBL_JREC_TIME(jgbl, jbp); jnl_status = jnl_ensure_open(gv_cur_region, csa); if (SS_NORMAL != jnl_status) send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(gv_cur_region)); else { if (0 == jpc->pini_addr) jnl_write_pini(csa); jnl_write_trunc_rec(csa, old_total, csa->ti->free_blocks, new_total); inctn_opcode = inctn_mu_reorg; jnl_write_inctn_rec(csa); jnl_status = jnl_flush(gv_cur_region); if (SS_NORMAL != jnl_status) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(9) ERR_JNLFLUSH, 2, JNL_LEN_STR(csd), ERR_TEXT, 2, RTS_ERROR_TEXT("Error with journal flush during mu_truncate"), jnl_status); assert(NOJNL == jpc->channel); /* jnl file lost has been triggered */ } } } /* Good to go ahead and REALLY truncate (reduce total_blks, clear cache_array, FTRUNCATE) */ curr_tn = csa->ti->curr_tn; CHECK_TN(csa, csd, curr_tn); udi = FILE_INFO(gv_cur_region); /* Information used by recover_truncate to check if the file size and csa->ti->total_blks are INCONSISTENT */ trunc_file_size = BLK_ZERO_OFF(csd->start_vbn) + ((off_t)csd->blk_size * (new_total + 1)); csd->after_trunc_total_blks = new_total; csd->before_trunc_free_blocks = csa->ti->free_blocks; csd->before_trunc_total_blks = old_total; /* Flags interrupted truncate for recover_truncate */ /* file size and total blocks: INCONSISTENT */ csa->ti->total_blks = new_total; /* past the point of no return -- shared memory intact */ assert(csa->ti->free_blocks >= DELTA_FREE_BLOCKS(old_total, new_total)); csa->ti->free_blocks -= DELTA_FREE_BLOCKS(old_total, new_total); new_free = csa->ti->free_blocks; KILL_TRUNC_TEST(WBTEST_CRASH_TRUNCATE_1); /* 55 : Issue a kill -9 before 1st fsync */ fileheader_sync(gv_cur_region); DB_FSYNC(gv_cur_region, udi, csa, db_fsync_in_prog, save_errno); CHECK_DBSYNC(gv_cur_region, save_errno); /* past the point of no return -- shared memory deleted */ KILL_TRUNC_TEST(WBTEST_CRASH_TRUNCATE_2); /* 56 : Issue a kill -9 after 1st fsync */ clear_cache_array(csa, csd, gv_cur_region, new_total, old_total); offset = (off_t)BLK_ZERO_OFF(csd->start_vbn) + (off_t)new_total * csd->blk_size; save_errno = db_write_eof_block(udi, udi->fd, csd->blk_size, offset, &(TREF(dio_buff))); if (0 != save_errno) { err_msg = (char *)STRERROR(errno); rts_error_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_MUTRUNCERROR, 4, REG_LEN_STR(gv_cur_region), LEN_AND_STR(err_msg)); return FALSE; } KILL_TRUNC_TEST(WBTEST_CRASH_TRUNCATE_3); /* 57 : Issue a kill -9 after reducing csa->ti->total_blks, before FTRUNCATE */ /* Execute an ftruncate() and truncate the DB file * ftruncate() is a SYSTEM CALL on almost all platforms (except SunOS) * It ignores kill -9 signal till its operation is completed. * So we can safely assume that the result of ftruncate() will be complete. */ FTRUNCATE(FILE_INFO(gv_cur_region)->fd, trunc_file_size, ftrunc_status); if (0 != ftrunc_status) { err_msg = (char *)STRERROR(errno); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(6) ERR_MUTRUNCERROR, 4, REG_LEN_STR(gv_cur_region), LEN_AND_STR(err_msg)); /* should go through recover_truncate now, which will again try to FTRUNCATE */ return FALSE; } /* file size and total blocks: CONSISTENT (shrunk) */ KILL_TRUNC_TEST(WBTEST_CRASH_TRUNCATE_4); /* 58 : Issue a kill -9 after FTRUNCATE, before 2nd fsync */ csa->nl->root_search_cycle++; /* Force concurrent processes to restart in t_end/tp_tend to make sure no one * tries to commit updates past the end of the file. Bitmap validations together * with highest_lbm_with_busy_blk should actually be sufficient, so this is * just to be safe. */ csd->before_trunc_total_blks = 0; /* indicate CONSISTENT */ /* Increment TN */ assert(csa->ti->early_tn == csa->ti->curr_tn); csd->trans_hist.early_tn = csd->trans_hist.curr_tn + 1; INCREMENT_CURR_TN(csd); fileheader_sync(gv_cur_region); DB_FSYNC(gv_cur_region, udi, csa, db_fsync_in_prog, save_errno); KILL_TRUNC_TEST(WBTEST_CRASH_TRUNCATE_5); /* 58 : Issue a kill -9 after after 2nd fsync */ CHECK_DBSYNC(gv_cur_region, save_errno); ENABLE_INTERRUPTS(INTRPT_IN_TRUNC, prev_intrpt_state); curr_tn = csa->ti->curr_tn; rel_crit(gv_cur_region); send_msg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_MUTRUNCSUCCESS, 5, DB_LEN_STR(gv_cur_region), &old_total, &new_total, &curr_tn); util_out_print( "Truncated region: !AD. Reduced total blocks from [!@UQ] to [!@UQ]. Reduced free blocks from [!@UQ] to [!@UQ].", FLUSH, REG_LEN_STR(gv_cur_region), &old_total, &new_total, &old_free, &new_free); return TRUE; } /* END of mu_truncate() */ STATICFNDEF int4 bml_find_busy_recycled(int4 hint, uchar_ptr_t base_addr, int4 blks_in_lmap, int *bml_status_ptr) { uchar_ptr_t ptr, top; int status; int4 base_blk, blknum, i; top = base_addr + DIVIDE_ROUND_UP(blks_in_lmap, BML_BLKS_PER_UCHAR); for (ptr = base_addr + DIVIDE_ROUND_DOWN(hint, BML_BLKS_PER_UCHAR); ptr < top; ptr++) { if (FOUR_BLKS_FREE == *ptr) continue; base_blk = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK)); /* loop through 4 blocks corresponding to this byte */ for (i = 0; i < BML_BLKS_PER_UCHAR; i++) { blknum = i + base_blk; if (blknum < hint || blks_in_lmap <= blknum) continue; GET_STATUS(*ptr, i, status); if (status != BLK_FREE) { assert((t_tries < CDB_STAGNATE) || (status == BLK_BUSY) || (status == BLK_RECYCLED)); *bml_status_ptr = status; return blknum; } } } return -1; } fis-gtm-V7.0-005/sr_unix/mu_truncate.h0000644000032200000250000000330314342376330016537 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2019 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. * * * ****************************************************************/ #ifndef MU_TRUNCATE_DEFINED #define MU_TRUNCATE_DEFINED #define BML_BLKS_PER_UCHAR (BITS_PER_UCHAR / BML_BITS_PER_BLK) #define GET_STATUS(bp, blknum, bml_status) \ bml_status = (bp >> (blknum * BML_BITS_PER_BLK)) & ((1 << BML_BITS_PER_BLK) - 1); #define CHECK_DBSYNC(reg, save_errno) \ { \ if (0 != save_errno) \ { \ send_msg_csa(CSA_ARG(REG2CSA(reg)) VARLSTCNT(5) ERR_DBFSYNCERR, 2, DB_LEN_STR(reg), save_errno); \ rts_error_csa(CSA_ARG(REG2CSA(reg)) VARLSTCNT(5) ERR_DBFSYNCERR, 2, DB_LEN_STR(reg), save_errno); \ assert(FALSE); /* should not come here as the rts_error above should not return */ \ rel_crit(reg); \ return FALSE; \ } \ } #define KILL_TRUNC_TEST(label) \ { \ GTM_WHITE_BOX_TEST(label, sigkill, 1); \ if (sigkill == 1) \ kill( getpid(), 9); \ } typedef struct trunc_reg_struct { gd_region *reg; struct trunc_reg_struct *next; } trunc_region; boolean_t mu_truncate(int4 truncate_percent, mval * keep_mval); STATICFNDCL int4 bml_find_busy_recycled(int4 hint, uchar_ptr_t base_addr, int4 blks_in_lmap, int *bml_status_ptr); #endif fis-gtm-V7.0-005/sr_unix/mu_upgrd_sig_init.h0000755000032200000250000000116714342376330017731 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef MU_UPGRD_SIG_INIT_H_INCLUDED #define MU_UPGRD_SIG_INIT_H_INCLUDED void mu_upgrd_sig_init(void); #endif fis-gtm-V7.0-005/sr_unix/mubexpfilnam.c0000755000032200000250000000356414342376330016707 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "mupipbckup.h" #include "util.h" #include "repl_instance.h" GBLREF bool error_mupip; GBLREF backup_reg_list *mu_repl_inst_reg_list; void mubexpfilnam(char *dirname, unsigned int dirlen, backup_reg_list *list) { char *c1; mstr file; char tmp_mstr_addr[MAX_FN_LEN + 1]; file.len = MAX_FN_LEN; file.addr = tmp_mstr_addr; if (list != mu_repl_inst_reg_list) { /* Database region */ if (!mupfndfil(list->reg, &file, LOG_ERROR_TRUE)) { util_out_print("Backup not finished because of the above error.", TRUE); error_mupip = TRUE; return; } } else { /* Replication instance region */ if (!repl_inst_get_name(file.addr, (unsigned int *)&file.len, MAX_FN_LEN, issue_rts_error, NULL)) assertpro(FALSE); /* rts_error should have been issued by repl_inst_get_name */ } for (c1 = file.addr + file.len; (*c1 != '/') && (c1 != file.addr); c1--) ; list->backup_file.len = INTCAST(dirlen + (file.len - (c1 - file.addr))); list->backup_file.addr = (char *)malloc(list->backup_file.len + 1); memcpy(list->backup_file.addr, dirname, dirlen); memcpy(list->backup_file.addr + dirlen, c1, (file.len - (c1 - file.addr))); list->backup_file.addr[list->backup_file.len] = '\0'; return; } fis-gtm-V7.0-005/sr_unix/mubfilcpy.c0000644000032200000250000010630114342376330016200 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_fcntl.h" #include "gtm_stdlib.h" #include "gtm_unistd.h" #include #include /* needed for basename */ #include "gtm_stat.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_permissions.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "db_header_conversion.h" #include "filestruct.h" #include "gtmio.h" #include "mupipbckup.h" #include "copy.h" #include "eintr_wrappers.h" #include "sleep_cnt.h" #include "util.h" #include "gtmmsg.h" #include "wcs_sleep.h" #include "gtm_file_stat.h" #include "gtm_tempnam.h" #include "gds_blk_downgrade.h" #include "shmpool.h" #include "wcs_phase2_commit_wait.h" #include "wbox_test_init.h" #include "db_write_eof_block.h" #include "mu_outofband_setup.h" #include "wcs_flu.h" #include "jnl.h" #ifdef __x86_64 # include # include "gtm_utsname.h" # include # include # define BUF_MAX 100 # define MIN_ETA 10 # define SHOWPERCENT 24 # define ADJUSTED_ETA 1.25 # define MEGABYTE 1024 * 1024 # define GIGABYTE 1024 *MEGABYTE #endif #define TMPDIR_ACCESS_MODE R_OK | W_OK | X_OK #define TMPDIR_CREATE_MODE S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH #define COMMAND_ARRAY_SIZE 1024 #define MV_CMD "mv " #define RMDIR_CMD "rm " #define RMDIR_OPT "-r " #define CD_CMD "cd " #define CMD_SEPARATOR " && " #ifdef __linux__ # define CP_CMD "cp " # define CP_OPT "--sparse=always " #elif defined(_AIX) # define CP_CMD "pax " # define CP_OPT "-r -w " #else # define CP_CMD "cp " # define CP_OPT "" #endif #define NUM_CMD 3 #define FREE_COMMAND_STR_IF_NEEDED \ { \ if (command != &cmdarray[0]) \ { \ free(command); \ command = &cmdarray[0]; \ } \ } #define CLEANUP_AND_RETURN_FALSE \ { \ int rc; \ int4 rv2, tmpcmdlen; \ int maxtmpcmdlen = (MAX_FN_LEN) * 2 + STR_LIT_LEN(UNALIAS) + 1; \ char tmpcmd[maxtmpcmdlen]; \ if (FD_INVALID != backup_fd) \ CLOSEFILE_RESET(backup_fd, rc); /* resets "backup_fd" to FD_INVALID */ \ memset(tmpcmd, '\0', maxtmpcmdlen); \ if (!debug_mupip) \ { /* An error happened. We are not sure if the temp dir is empty. Can't use rmdir() */ \ MEMCPY_LIT(tmpcmd, UNALIAS); \ tmpcmdlen = STR_LIT_LEN(UNALIAS); \ cmdpathlen = STRLEN(fulpathcmd[2]); \ memcpy(&tmpcmd[tmpcmdlen], fulpathcmd[2], cmdpathlen); \ tmpcmdlen += cmdpathlen; \ MEMCPY_LIT(&tmpcmd[tmpcmdlen], RMDIR_OPT); \ tmpcmdlen += STR_LIT_LEN(RMDIR_OPT); \ memcpy(&tmpcmd[tmpcmdlen], tempdir, tmpdirlen); \ tmpcmdlen += tmpdirlen; \ rv2 = SYSTEM((char *)tmpcmd); \ if (0 != rv2) \ { \ if (-1 == rv2) \ { \ save_errno = errno; \ errptr = (char *)STRERROR(save_errno); \ util_out_print("system : !AZ", TRUE, errptr); \ } \ util_out_print("Error removing temp dir !AD.", TRUE, tmpcmdlen, tmpcmd);\ } \ } \ return FALSE; \ } #define ABORTBACKUP \ { \ if (online) \ cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS; \ FREE_COMMAND_STR_IF_NEEDED; \ CLEANUP_AND_RETURN_FALSE; \ } GBLREF bool file_backed_up; GBLREF gd_region *gv_cur_region; GBLREF bool record; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF bool online; GBLREF uint4 process_id; GBLREF boolean_t debug_mupip; GBLREF bool mu_ctrlc_occurred; #ifdef __x86_64 STATICDEF void *func_ptr; #endif error_def(ERR_BKUPFILEPERM); error_def(ERR_BKUPPROGRESS); error_def(ERR_BCKUPBUFLUSH); error_def(ERR_BUFFLUFAILED); error_def(ERR_COMMITWAITSTUCK); error_def(ERR_DBCCERR); error_def(ERR_DBROLLEDBACK); error_def(ERR_ERRCALL); error_def(ERR_TEXT); error_def(ERR_TMPFILENOCRE); error_def(ERR_BACKUPDBFILE); error_def(ERR_BACKUPTN); error_def(ERR_FILENAMETOOLONG); boolean_t mubfilcpy (backup_reg_list *list, boolean_t showprogress, int attemptcnt, boolean_t *stopretries) { mstr *file, tempfile; unsigned char cmdarray[COMMAND_ARRAY_SIZE], *command = &cmdarray[0]; char fulpathcmd[NUM_CMD][MAX_FN_LEN] = {{CP_CMD}, {MV_CMD}, {RMDIR_CMD}}; sgmnt_data_ptr_t header_cpy; int4 backup_fd = FD_INVALID, counter, hdrsize, rsize, ntries; ssize_t status; block_id blk_num; int4 cmdlen, rv, save_errno, tempfilelen, tmpdirlen, tmplen; int4 sourcefilelen, sourcedirlen, realpathlen; struct stat stat_buf, stat; off_t filesize, offset; char *inbuf, *ptr, *errptr, *sourcefilename, *sourcedirname; char tempfilename[MAX_FN_LEN + 1], tempdir[MAX_FN_LEN], prefix[MAX_FN_LEN]; char tmpsrcfname[MAX_FN_LEN], tmpsrcdirname[MAX_FN_LEN], realpathname[PATH_MAX]; char sourcefilepathname[GTM_PATH_MAX + MAX_FN_LEN], tempfilepathname[GTM_PATH_MAX + MAX_FN_LEN]; char tmprealpath[GTM_PATH_MAX + MAX_FN_LEN]; int fstat_res, i, cmdpathlen; uint4 ustatus, size; muinc_blk_hdr_ptr_t sblkh_p; ZOS_ONLY(int realfiletag;) int user_id; int group_id; int perm; struct perm_diag_data pdd; int ftruncate_res; trans_num ONE = 0x1; boolean_t in_kernel, usesystem = TRUE; # ifdef __x86_64 int digicnt = 0, eta = MIN_ETA, infd, outfd, speedcnt = 0, transpadcnt = 0; int csdigicnt = 0, cspadcnt = 0, speedigits = 0; double progper = 0, progfact = SHOWPERCENT; size_t tmpsize, transfersize, remaining; ssize_t ret; loff_t *inoffp, *outoffp; size_t bytesspliced = 0, currspeed = 0, trans_cnt = 0, start_cnt = 0; time_t begtm, endtm, strtm; char sizep[BUF_MAX], progperstr[BUF_MAX], padding[MAX_DIGITS_IN_INT8], fil=' '; boolean_t sizeGiB = TRUE; char *etaptr; char transferbuf[BUF_MAX], speedbuf[BUF_MAX], errstrbuff[BUF_MAX + GTM_PATH_MAX]; ssize_t (*copy_file_range_p)(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags); # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; file = &(list->backup_file); file->addr[file->len] = '\0'; header_cpy = list->backup_hdr; hdrsize = (int4)ROUND_UP(SIZEOF_FILE_HDR(header_cpy), DISK_BLOCK_SIZE); # ifdef DEBUG /* This code is shared by two WB tests */ if (WBTEST_ENABLED(WBTEST_MM_CONCURRENT_FILE_EXTEND) || (WBTEST_ENABLED(WBTEST_WSSTATS_PAUSE) && (10 == gtm_white_box_test_case_count))) if (!MEMCMP_LIT(gv_cur_region->rname, "DEFAULT") && !cs_addrs->nl->wbox_test_seq_num) { cs_addrs->nl->wbox_test_seq_num = 1; while (1 == cs_addrs->nl->wbox_test_seq_num) SHORT_SLEEP(1); /* wait for signal from gdsfilext that mupip backup can continue */ } # endif /* the temporary file should be located in the destination directory */ ptr = file->addr + file->len - 1; while (('/' != *ptr) && (ptr > file->addr)) ptr--; if (ptr > file->addr) { memcpy(tempdir, file->addr, ptr - file->addr + 1); tempdir[ptr - file->addr + 1] = '\0'; } else { tempdir[0] = '.'; tempdir[1] = '\0'; } memset(prefix, 0, MAX_FN_LEN); memcpy(prefix, gv_cur_region->rname, gv_cur_region->rname_len); SNPRINTF(&prefix[gv_cur_region->rname_len], MAX_FN_LEN - gv_cur_region->rname_len, "_%d_", process_id); /* verify that we have access to the temporary directory to avoid /tmp */ if (0 != ACCESS(tempdir, TMPDIR_ACCESS_MODE)) { save_errno = errno; errptr = (char *)STRERROR(save_errno); util_out_print("access : !AZ", TRUE, errptr); if (online) cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS; util_out_print("ERROR: Do NOT have full access to directory !AD", TRUE, LEN_AND_STR(tempdir)); return FALSE; } /* verify that we have write access to the destination backup file */ if (0 == ACCESS(file->addr, F_OK) && (0 != ACCESS(file->addr, W_OK))) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_BKUPFILEPERM, 2, file->len, file->addr); if (online) cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS; return FALSE; } /* make this cp a two step process ==> cp followed by mv */ ntries = 0; fstat_res = FILE_NOT_FOUND; /* do go into the loop for the first time, irrespective of fstat_res*/ while ((FILE_NOT_FOUND != fstat_res) || (!ntries)) { gtm_tempnam(tempdir, prefix, tempfilename); tempfile.addr = tempfilename; tempfile.len = STRLEN(tempfilename); if ((FILE_STAT_ERROR == (fstat_res = gtm_file_stat(&tempfile, NULL, NULL, FALSE, &ustatus))) || (ntries > MAX_TEMP_OPEN_TRY)) { if (FILE_STAT_ERROR != fstat_res) gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(8) ERR_TMPFILENOCRE, 2, tempfile.len, tempfile.addr, ERR_TEXT, 2, LEN_AND_LIT("Tried a maximum number of times, clean-up temporary files " \ "in backup directory and retry.")); else gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_TMPFILENOCRE, 2, tempfile.len, tempfile.addr, ustatus); return FALSE; } ntries++; } tmplen = gv_cur_region->dyn.addr->fname_len; /* basename() may modify the argument passed to it. so pass it a temp string */ memcpy(tmpsrcfname, gv_cur_region->dyn.addr->fname, gv_cur_region->dyn.addr->fname_len); tmpsrcfname[gv_cur_region->dyn.addr->fname_len] = 0; sourcefilename = basename((char *)tmpsrcfname); sourcefilelen = STRLEN(sourcefilename); /* dirname() may modify its argument too. Also, basename() above may've m odified tmp str. reset it */ memcpy(tmpsrcdirname, gv_cur_region->dyn.addr->fname, gv_cur_region->dyn.addr->fname_len); tmpsrcdirname[gv_cur_region->dyn.addr->fname_len] = 0; sourcedirname = dirname((char *)tmpsrcdirname); sourcedirlen = STRLEN(sourcedirname); /* Right now, "tempfilename" is the temporary directory under which the *.dat files will get created. * * Save this directory into tempdir, which will later be used to remove the temp dir. */ tempfilelen = STRLEN(tempfilename); memcpy(tempdir, tempfilename, tempfilelen); tmpdirlen = tempfilelen; /* mkdir tempdir*/ if (0 != MKDIR(tempfilename, TMPDIR_CREATE_MODE)) { save_errno = errno; errptr = (char *)STRERROR(save_errno); util_out_print("Temporary directory !AD could not be created.", TRUE, tempfilelen, tempfilename); gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_STR(errptr)); CLEANUP_AND_RETURN_FALSE; } if (NULL == realpath(tempfilename, realpathname)) { save_errno = errno; errptr = (char *)STRERROR(save_errno); util_out_print("Temporary directory !AD could not be found after creation.", TRUE, tempfilelen, tempfilename); gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_STR(errptr)); CLEANUP_AND_RETURN_FALSE; } realpathlen = STRLEN(realpathname); /* Get the path to system utilities and prepend it to the command. */ for (i = 0; i < NUM_CMD; i++) { rv = CONFSTR(fulpathcmd[i], MAX_FN_LEN); if (0 != rv) CLEANUP_AND_RETURN_FALSE; } /* Start prep for in-kernel copy */ # ifdef __x86_64 /* Do not use cp/pax on the first attempt */ in_kernel = ink_backup(); if (FALSE == in_kernel) usesystem = TRUE; else usesystem = (1 < attemptcnt); /* Do not use cp on the first attempt */ if (!usesystem) { mu_outofband_setup(); if (NULL == realpath((char *)tmpsrcfname, sourcefilepathname)) { SNPRINTF(errstrbuff, GTM_PATH_MAX, "Unable to resolve path to database file %s. " "Reason: %s", sourcefilename, (char *)STRERROR(errno)); gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_STR(errstrbuff)); if (online) cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS; return FALSE; } if (NULL == realpath((char *)tempfilename, tmprealpath)) { SNPRINTF(errstrbuff, GTM_PATH_MAX, "Unable to resolve path to temp file %s. " "Reason: %s", tempfilename, (char *)STRERROR(errno)); gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_STR(errstrbuff)); if (online) cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS; return FALSE; } SNPRINTF(tempfilepathname, SIZEOF(tempfilepathname),"%s/%s", tmprealpath, sourcefilename); infd = open(sourcefilepathname, O_RDONLY); if (-1 == infd) { if (1 == (status = handle_err("Unable to open() the database file", errno))) /* WARNING assignment */ ABORTBACKUP; } if (-1 == fstat(infd, &stat)) { if (1 == (status = handle_err("Error obtaining fstat() from the database file", errno))) /* WARNING assignment */ ABORTBACKUP; } outfd = open(tempfilepathname, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (-1 == outfd) { if (1 == (status = handle_err("Unable to open() the backup file/location", errno))) /* WARNING assignment */ ABORTBACKUP; } /* set transfer size as maximum. Full copy-acceleration */ remaining = stat.st_size; tmpsize = transfersize = remaining; bytesspliced = 0; if (GIGABYTE > tmpsize) sizeGiB = FALSE; tmpsize = (sizeGiB) ? DIVIDE_ROUND_UP(tmpsize, GIGABYTE) : DIVIDE_ROUND_UP(tmpsize, MEGABYTE); digicnt = SNPRINTF(transferbuf, MAX_DIGITS_IN_INT8 + 7, "/ %lld %siB", tmpsize, (sizeGiB ? "G" : "M")) - 6; memcpy(sizep, transferbuf, STRLEN(transferbuf) + 1); begtm = time(NULL); start_cnt = header_cpy->trans_hist.curr_tn; if (showprogress) util_out_print("Starting BACKUP for region : !AD", TRUE, REG_LEN_STR(gv_cur_region)); do { strtm = time(NULL); tmpsize = remaining; copy_file_range_p = func_ptr; ret = copy_file_range_p(infd, inoffp, outfd, outoffp, remaining, 0); if (WBTEST_ENABLED(WBTEST_BACKUP_FORCE_SLEEP)) { util_out_print("BACKUP_STARTED", TRUE); LONG_SLEEP(20); showprogress = TRUE; } if (-1 == ret) { if (1 == (status = handle_err("Error occurred during the copy phase of MUPIP BACKUP", errno))) /* WARNING assignment */ ABORTBACKUP; } endtm = time(NULL); bytesspliced = bytesspliced + (size_t)ret; if ((endtm > strtm) && (0 < ret)) currspeed = DIVIDE_ROUND_UP(ret, (endtm - strtm)); /* bytes per second*/ remaining = remaining - ret; if (showprogress) { if (0 < currspeed) eta = (int)(DIVIDE_ROUND_UP(remaining, currspeed) * ADJUSTED_ETA); /* Increase ETA */ progper = ((double)bytesspliced / (double)transfersize) * 100; if (progper > progfact) { progfact = progfact + SHOWPERCENT; tmpsize = bytesspliced; tmpsize = (sizeGiB) ? DIVIDE_ROUND_UP(bytesspliced, GIGABYTE) : DIVIDE_ROUND_UP (bytesspliced, MEGABYTE); memset(padding, '\0', 3); if (100 != (int)progper) memset(padding, fil, (10 > progper) ? 2 : 1); /* left pad progress percent string */ SNPRINTF(progperstr, BUF_MAX, "%s (%s%d%c)", sizep, padding, (int)progper, '%'); memset(padding, '\0', MAX_DIGITS_IN_INT8); transpadcnt = digicnt - countdigits(tmpsize); if (transpadcnt > 0) memset(padding, fil, transpadcnt); /* left pad transferbuf string */ SNPRINTF(transferbuf, BUF_MAX, "%s%lld %s", padding, tmpsize, progperstr); currspeed = DIVIDE_ROUND_DOWN(currspeed, 1024); csdigicnt = countdigits(currspeed); speedigits = (csdigicnt > speedigits) ? csdigicnt: speedigits; cspadcnt = speedigits - csdigicnt; memset(padding, '\0', MAX_DIGITS_IN_INT8); if (cspadcnt > 0) memset(padding, fil, cspadcnt); /* left pad speedbuf string */ SNPRINTF(speedbuf, BUF_MAX, "%s%lld", padding, currspeed); if (eta > 60) { eta = DIVIDE_ROUND_UP(eta, 60); etaptr = (1 < eta) ? "minutes": "minute"; } else etaptr = (1 < eta) ? "seconds": "second"; trans_cnt = cs_data->trans_hist.curr_tn - start_cnt; gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(10) ERR_BKUPPROGRESS, 8, LEN_AND_STR(transferbuf), LEN_AND_STR(speedbuf), &trans_cnt, eta, LEN_AND_STR(etaptr)); if (WBTEST_ENABLED(WBTEST_BACKUP_FORCE_SLEEP)) LONG_SLEEP(10); } } if (TRUE == mu_ctrlc_occurred) ABORTBACKUP; } while ((0 < remaining) && (-1 < ret)); close(infd); close(outfd); if ((0 != remaining) || (ret == -1)) ABORTBACKUP; /* Copy failed so no need to retry */ } # endif if (TRUE == usesystem) { /* Calculate total line length for commands to execute (pushd + cp). * * If cannot fit in local variable array, malloc space * * commands to be executed : pushd sourcedir && CP_CMD fname tempfilename */ cmdlen = STR_LIT_LEN(UNALIAS) + STR_LIT_LEN(CD_CMD) + sourcedirlen + STR_LIT_LEN(CMD_SEPARATOR); cmdlen += STR_LIT_LEN(fulpathcmd[0]) + STR_LIT_LEN(CP_OPT) + sourcefilelen + 5 /* 4 quotes and 1 space */ + realpathlen + 1 /* terminating NULL byte */; if (cmdlen > SIZEOF(cmdarray)) command = malloc(cmdlen); /* allocate memory and use that instead of local array "cmdarray" */ /* cd */ MEMCPY_LIT(command, UNALIAS); cmdlen = STR_LIT_LEN(UNALIAS); MEMCPY_LIT(&command[cmdlen], CD_CMD); cmdlen += STR_LIT_LEN(CD_CMD); memcpy(&command[cmdlen], sourcedirname, sourcedirlen); cmdlen += sourcedirlen; MEMCPY_LIT(&command[cmdlen], CMD_SEPARATOR); cmdlen += STR_LIT_LEN(CMD_SEPARATOR); /* cp */ cmdpathlen = STRLEN(fulpathcmd[0]); memcpy(&command[cmdlen], fulpathcmd[0], cmdpathlen); cmdlen += cmdpathlen; MEMCPY_LIT(&command[cmdlen], CP_OPT); cmdlen += STR_LIT_LEN(CP_OPT); command[cmdlen++] = '\''; memcpy(&command[cmdlen], sourcefilename, sourcefilelen); cmdlen += sourcefilelen; command[cmdlen++] = '\''; command[cmdlen++] = ' '; command[cmdlen++] = '\''; memcpy(&command[cmdlen], realpathname, realpathlen); cmdlen += realpathlen; command[cmdlen++] = '\''; command[cmdlen] = 0; if (debug_mupip) util_out_print("!/MUPIP INFO: !AD", TRUE, cmdlen, command); if (WBTEST_ENABLED(WBTEST_BACKUP_FORCE_SLEEP)) { util_out_print("BACKUP_STARTED", TRUE); LONG_SLEEP(20); } rv = SYSTEM((char *)command); if (0 != rv) { if (-1 == rv) { save_errno = errno; errptr = (char *)STRERROR(save_errno); util_out_print("system : !AZ", TRUE, errptr); } util_out_print("Error doing !AD", TRUE, cmdlen, command); ABORTBACKUP; } if (TRUE == mu_ctrlc_occurred) ABORTBACKUP; } FREE_COMMAND_STR_IF_NEEDED; assert(command == &cmdarray[0]); /* tempfilename currently contains the name of temporary directory created. * add the DB filename (only the final filename, without the pathname) to point to tmpfilename. * Check that enough space is there in the buffer. */ if (tempfilelen + sourcefilelen + 1 > MAX_FN_LEN) // +1 for the '/' { save_errno = errno; errptr = (char *)STRERROR(save_errno); gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) ERR_FILENAMETOOLONG); *stopretries = TRUE; CLEANUP_AND_RETURN_FALSE; } tempfilename[tempfilelen++] = '/'; memcpy(&tempfilename[tempfilelen], sourcefilename, sourcefilelen); tempfilelen += sourcefilelen; tempfilename[tempfilelen] = 0; /* give temporary files the group and permissions as other shared resources - like journal files */ OPENFILE(tempfilename, O_RDWR, backup_fd); if (FD_INVALID == backup_fd) { save_errno = errno; errptr = (char *)STRERROR(save_errno); util_out_print("open : !AZ", TRUE, errptr); if (online) cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS; util_out_print("Error opening backup file !AD.", TRUE, file->len, file->addr); util_out_print("WARNING: backup file !AD is not valid.", TRUE, file->len, file->addr); CLEANUP_AND_RETURN_FALSE; } FSTAT_FILE(((unix_db_info *)(gv_cur_region->dyn.addr->file_cntl->file_info))->fd, &stat_buf, fstat_res); if (-1 != fstat_res) if (!gtm_permissions(&stat_buf, &user_id, &group_id, &perm, PERM_FILE, &pdd)) { send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(6+PERMGENDIAG_ARG_COUNT) ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("backup file"), RTS_ERROR_STRING(((unix_db_info *)(gv_cur_region->dyn.addr->file_cntl->file_info))->fn), PERMGENDIAG_ARGS(pdd)); gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(6+PERMGENDIAG_ARG_COUNT) ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("backup file"), RTS_ERROR_STRING(((unix_db_info *)(gv_cur_region->dyn.addr->file_cntl->file_info))->fn), PERMGENDIAG_ARGS(pdd)); if (online) cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS; CLEANUP_AND_RETURN_FALSE; } /* Setup new group and permissions if indicated by the security rules. */ if ((-1 == fstat_res) || (-1 == FCHMOD(backup_fd, perm)) || (((INVALID_UID != user_id) || (INVALID_GID != group_id)) && (-1 == fchown(backup_fd, user_id, group_id)))) { save_errno = errno; errptr = (char *)STRERROR(save_errno); util_out_print("system : !AZ", TRUE, errptr); if (online) cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS; CLEANUP_AND_RETURN_FALSE; } # if defined(__MVS__) if (-1 == gtm_zos_tag_to_policy(backup_fd, TAG_BINARY, &realfiletag)) TAG_POLICY_GTM_PUTMSG( tempfilename, realfiletag, TAG_BINARY, errno); # endif if (online) { cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS; if ((cs_addrs->onln_rlbk_cycle != cs_addrs->nl->onln_rlbk_cycle) || (0 != cs_addrs->nl->onln_rlbk_pid)) { /* A concurrent online rollback happened since we did the gvcst_init. The backup is not reliable. * Cleanup and exit */ gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) ERR_DBROLLEDBACK); CLEANUP_AND_RETURN_FALSE; } /* if there has been an extend, truncate it */ FSTAT_FILE(backup_fd, &stat_buf, fstat_res); if (-1 == fstat_res) { save_errno = errno; errptr = (char *)STRERROR(save_errno); util_out_print("fstat : !AZ", TRUE, errptr); util_out_print("Error obtaining status of backup file !AD.", TRUE, file->len, file->addr); util_out_print("WARNING: backup file !AD is not valid.", TRUE, file->len, file->addr); CLEANUP_AND_RETURN_FALSE; } filesize = (off_t)BLK_ZERO_OFF(header_cpy->start_vbn) + (off_t)(header_cpy->trans_hist.total_blks + 1) * header_cpy->blk_size; if (filesize != stat_buf.st_size) { /* file has been extended, so truncate it and set the end of database block */ FTRUNCATE(backup_fd, filesize, ftruncate_res); if (-1 == ftruncate_res) { save_errno = errno; errptr = (char *)STRERROR(save_errno); util_out_print("ftruncate : !AZ", TRUE, errptr); util_out_print("Error truncating backup file !AD.", TRUE, file->len, file->addr); util_out_print("WARNING: backup file !AD is not valid.", TRUE, file->len, file->addr); CLEANUP_AND_RETURN_FALSE; } status = db_write_eof_block(NULL, backup_fd, cs_data->blk_size, filesize - header_cpy->blk_size, &(TREF(dio_buff))); if (0 != status) { util_out_print("Error writing the last block in database !AD.", TRUE, file->len, file->addr); util_out_print("WARNING: backup file !AD is not valid.", TRUE, file->len, file->addr); CLEANUP_AND_RETURN_FALSE; } } /* By getting crit here, we ensure that there is no process still in transaction logic that sees * (nbb != BACKUP_NOT_IN_PRORESS). After rel_crit(), any process that enters transaction logic will * see (nbb == BACKUP_NOT_IN_PRORESS) because we just set it to that value. At this point, backup * buffer is complete and there will not be any more new entries in the backup buffer until the next * backup. */ assert(!cs_addrs->hold_onto_crit); /* this ensures we can safely do unconditional grab_crit and rel_crit */ grab_crit(gv_cur_region, WS_89); assert(cs_data == cs_addrs->hdr); if (dba_bg == cs_data->acc_meth) { /* Now that we have crit, wait for any pending phase2 updates to finish. Since phase2 updates happen * outside of crit, we don't want them to keep writing to the backup temporary file even after the * backup is complete and the temporary file has been deleted. */ if (cs_addrs->nl->wcs_phase2_commit_pidcnt && !wcs_phase2_commit_wait(cs_addrs, NULL)) { assert(FALSE); gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(7) ERR_COMMITWAITSTUCK, 5, process_id, 1, cs_addrs->nl->wcs_phase2_commit_pidcnt, DB_LEN_STR(gv_cur_region)); rel_crit(gv_cur_region); CLEANUP_AND_RETURN_FALSE; } } if (debug_mupip) { util_out_print("MUPIP INFO: Current Transaction # at end of backup is 0x!16@XQ", TRUE, &cs_data->trans_hist.curr_tn); } rel_crit(gv_cur_region); counter = 0; while ((0 != cs_addrs->shmpool_buffer->backup_cnt) && (0 == cs_addrs->shmpool_buffer->failed)) { backup_buffer_flush(gv_cur_region); if (++counter > MAX_BACKUP_FLUSH_TRY) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) ERR_BCKUPBUFLUSH); CLEANUP_AND_RETURN_FALSE; } if (counter & 0xF) { # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_FORCE_SHMPLRECOV)) /* Fake shmpool_blocked */ cs_addrs->shmpool_buffer->shmpool_blocked = TRUE; # endif wcs_sleep(counter); } else { /* Force recovery every few retries - this should not be happening */ if (FALSE == shmpool_lock_hdr(gv_cur_region)) { assert(FALSE); gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(9) ERR_DBCCERR, 2, REG_LEN_STR(gv_cur_region), ERR_ERRCALL, 3, CALLFROM); CLEANUP_AND_RETURN_FALSE; } shmpool_abandoned_blk_chk(gv_cur_region, TRUE); shmpool_unlock_hdr(gv_cur_region); } } if (0 != cs_addrs->shmpool_buffer->failed) { assert(EACCES == cs_addrs->shmpool_buffer->backup_errno); util_out_print("Process !UL encountered the following error.", TRUE, cs_addrs->shmpool_buffer->failed); if (0 != cs_addrs->shmpool_buffer->backup_errno) gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) cs_addrs->shmpool_buffer->backup_errno); util_out_print("WARNING: backup file !AD is not valid.", TRUE, file->len, file->addr); CLEANUP_AND_RETURN_FALSE; } FSTAT_FILE(list->backup_fd, &stat_buf, fstat_res); if (-1 == fstat_res) { save_errno = errno; errptr = (char *)STRERROR(save_errno); util_out_print("fstat : !AZ", TRUE, errptr); util_out_print("Error obtaining status of temporary file !AD.", TRUE, LEN_AND_STR(list->backup_tempfile)); util_out_print("WARNING: backup file !AD is not valid.", TRUE, file->len, file->addr); CLEANUP_AND_RETURN_FALSE; } if (0 < (filesize = stat_buf.st_size)) { rsize = (int4)(SIZEOF(muinc_blk_hdr) + header_cpy->blk_size); sblkh_p = (muinc_blk_hdr_ptr_t)malloc(rsize); /* Do not use LSEEKREAD macro here because of dependence on setting filepointer for * subsequent reads. */ if (-1 != (status = (ssize_t)lseek(list->backup_fd, 0, SEEK_SET))) { DOREADRC(list->backup_fd, (sm_uc_ptr_t)sblkh_p, rsize, status); } else status = errno; if (0 != status) { if (0 < status) { errptr = (char *)STRERROR((int)status); util_out_print("read : ", TRUE, errptr); util_out_print("Error reading the temporary file !AD.", TRUE, LEN_AND_STR(list->backup_tempfile)); } else util_out_print("Premature end of temporary file !AD.", TRUE, LEN_AND_STR(list->backup_tempfile)); util_out_print("WARNING: backup file !AD is not valid.", TRUE, file->len, file->addr); free(sblkh_p); CLEANUP_AND_RETURN_FALSE; } while (TRUE) { assert(sblkh_p->valid_data); blk_num = sblkh_p->blkid; if (WBTEST_ENABLED(WBTEST_BACKUP_FORCE_SLEEP)) { util_out_print("RESTORE_BLOCK_STAGE", TRUE); LONG_SLEEP(10); } if (debug_mupip) util_out_print("MUPIP INFO: Restoring block 0x!XL from temporary file.", TRUE, blk_num); if (blk_num < header_cpy->trans_hist.total_blks) { inbuf = (char_ptr_t)(sblkh_p + 1); /* If the incoming block has an ondisk version of V4, convert it back to that * version before writing it out so it is the same as the block in the original * database. */ if (GDSV4 == sblkh_p->use.bkup.ondsk_blkver) { /* Need to downgrade this block back to a previous format. Downgrade in place. */ assert(GDSV4 != sblkh_p->use.bkup.ondsk_blkver); /* not supported */ gds_blk_downgrade((v15_blk_hdr_ptr_t)inbuf, (blk_hdr_ptr_t)inbuf); size = (((v15_blk_hdr_ptr_t)inbuf)->bsiz + 1) & ~1; } else size = (((blk_hdr_ptr_t)inbuf)->bsiz + 1) & ~1; if (cs_data->write_fullblk) size = ROUND_UP(size, cs_addrs->fullblockwrite_len); assert((uint4)cs_addrs->hdr->blk_size >= size); offset = BLK_ZERO_OFF(header_cpy->start_vbn) + ((off_t)header_cpy->blk_size * blk_num); LSEEKWRITE(backup_fd, offset, inbuf, size, save_errno); if (0 != save_errno) { util_out_print("Error accessing output file !AD. Aborting restore.", TRUE, file->len, file->addr); errptr = (char *)STRERROR(save_errno); util_out_print("write : !AZ", TRUE, errptr); free(sblkh_p); CLEANUP_AND_RETURN_FALSE; } } /* Else ignore block that is larger than file was at time backup initiated */ DOREADRC(list->backup_fd, (sm_uc_ptr_t)sblkh_p, rsize, save_errno); if (0 != save_errno) { if (0 < save_errno) { util_out_print("Error accessing temporary file !AD.", TRUE, LEN_AND_STR(list->backup_tempfile)); errptr = (char *)STRERROR(save_errno); util_out_print("read : !AZ", TRUE, errptr); free(sblkh_p); CLEANUP_AND_RETURN_FALSE; } else /* End of file .. Note this does not detect the difference between * clean end of file and partial record end of file. */ break; } } free(sblkh_p); } } if (0 == memcmp(header_cpy->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv(header_cpy); LSEEKWRITE(backup_fd, 0, header_cpy, hdrsize, save_errno); if (0 != save_errno) { errptr = (char *)STRERROR(save_errno); util_out_print("write : !AZ", TRUE, errptr); util_out_print("Error writing data to backup file !AD.", TRUE, file->len, file->addr); util_out_print("WARNING: backup file !AD is not valid.", TRUE, file->len, file->addr); CLEANUP_AND_RETURN_FALSE; } CLOSEFILE_RESET(backup_fd, status); /* resets "backup_fd" to FD_INVALID */ /* mv it to destination */ /* Calculate total line length for "mv" command. If cannot fit in local variable array, malloc space */ assert(command == &cmdarray[0]); tmplen = file->len; /* Command to be executed : mv tempfilename backup_file */ cmdlen = STR_LIT_LEN(UNALIAS) + STR_LIT_LEN(fulpathcmd[1]); cmdlen += tempfilelen + 5 /* 4 quotes, 1 space */ + tmplen + 1 /* terminating NULL byte */; if (cmdlen > SIZEOF(cmdarray)) command = malloc(cmdlen); /* allocate memory and use that instead of local array "cmdarray" */ /* mv tmpfile destfile */ MEMCPY_LIT(command, UNALIAS); cmdlen = STR_LIT_LEN(UNALIAS); cmdpathlen = STRLEN(fulpathcmd[1]); memcpy(&command[cmdlen], fulpathcmd[1], cmdpathlen); cmdlen += cmdpathlen; command[cmdlen++] = '\''; memcpy(&command[cmdlen], tempfilename, tempfilelen); cmdlen += tempfilelen; command[cmdlen++] = '\''; command[cmdlen++] = ' '; command[cmdlen++] = '\''; memcpy(&command[cmdlen], file->addr, tmplen); cmdlen += tmplen; command[cmdlen++] = '\''; command[cmdlen] = 0; if (debug_mupip) util_out_print("MUPIP INFO: !AD", TRUE, cmdlen, command); rv = SYSTEM((char *)command); if (WBTEST_ENABLED(WBTEST_BACKUP_FORCE_MV_RV)) { util_out_print("Simulated mv returns an error.", TRUE); rv = -1; } if (0 != rv) { if (-1 == rv) { save_errno = errno; errptr = (char *)STRERROR(save_errno); util_out_print("system : !AZ", TRUE, errptr); } util_out_print("Error executing command : !AD", TRUE, cmdlen, command); FREE_COMMAND_STR_IF_NEEDED; CLEANUP_AND_RETURN_FALSE; } /* rm tempdir */ tempdir[tmpdirlen] = 0; if (0 != rmdir(tempdir)) { save_errno = errno; errptr = (char *)STRERROR(save_errno); util_out_print("access : !AZ", TRUE, errptr); util_out_print("Removing temp dir : !AD", TRUE, tmpdirlen, tempdir); FREE_COMMAND_STR_IF_NEEDED; CLEANUP_AND_RETURN_FALSE; } FREE_COMMAND_STR_IF_NEEDED; assert(command == &cmdarray[0]); gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(6) ERR_BACKUPDBFILE, 4, gv_cur_region->dyn.addr->fname_len, gv_cur_region->dyn.addr->fname, file->len, file->addr); gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_BACKUPTN, 2, &ONE, &header_cpy->trans_hist.curr_tn); cs_addrs->hdr->last_com_backup = header_cpy->trans_hist.curr_tn; cs_addrs->hdr->last_com_bkup_last_blk = header_cpy->trans_hist.total_blks; if (record) { cs_addrs->hdr->last_rec_backup = header_cpy->trans_hist.curr_tn; cs_addrs->hdr->last_rec_bkup_last_blk = header_cpy->trans_hist.total_blks; cs_addrs->hdr->last_start_backup = header_cpy->last_start_backup; } file_backed_up = TRUE; if (TRUE == grab_crit_immediate(gv_cur_region, TRUE, NOT_APPLICABLE)) { if (!wcs_flu(WCSFLU_FLUSH_HDR)) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(6) MAKE_MSG_WARNING(ERR_BUFFLUFAILED), 4, LEN_AND_LIT("MUPIP BACKUP -DATABASE"), DB_LEN_STR(gv_cur_region)); } rel_crit(gv_cur_region); } return TRUE; } #ifdef __x86_64 /* error handler for copy_file_range() */ inline int handle_err(char errorstr[], int saved_errno) { char *customptr, *adviceptr; char oserror[BUF_MAX] = "\0"; int status; customptr = "\0"; switch (saved_errno) { case EXDEV: { SNPRINTF(oserror, BUF_MAX, "%s, %s for backing up region %s", "EXDEV", (char *)STRERROR(saved_errno), gv_cur_region->rname); break; } case ENOSPC: { SNPRINTF(oserror, BUF_MAX, "%s, %s for backing up region %s", "ENOSPC", (char *)STRERROR(saved_errno), gv_cur_region->rname); if (NULL == getenv("gtm_baktmpdir")) { customptr = "As $gtm_baktmpdir is not defined, GT.M uses the backup destination " "to store temporary files. Consider defining $gtm_baktmpdir to " "an appropriate location."; } else customptr = "Ensure that you have adequate space available on the backup location."; break; } default: { if (0 != saved_errno) { SNPRINTF(oserror, BUF_MAX, "Error code: %d, %s for backing up region %s", saved_errno, (char *)STRERROR(saved_errno), gv_cur_region->rname); } break; } } if (0 < STRLEN(errorstr)) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_STR(errorstr)); if (0 < STRLEN(oserror)) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_STR(oserror)); /* Extra error information about what the error may mean to GT.M */ if (0 < STRLEN(customptr)) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_STR(customptr)); return 1; /* In all error conditions, return 1 which initiates the CLEANUP sequence */ } /* countdigits(size_t num) returns the number of digits */ inline int countdigits(size_t n) { uint4 digitcnt = 0; do { n = n / 10; digitcnt++; } while (n); return digitcnt; } /* boolean_t ink_backup(void) performs a run-time check and returns TRUE when the copy_file_range symbol exists, * Linux kernel >= 5.3 and GLIBC > 2.27. * Ensure that ink_backup(void) matches com/backup_copy_type.c in the test system. */ inline boolean_t ink_backup(void) { struct utsname relbuff; char *ptr; long int kernel_major, kernel_minor; float glibcversion; /* Check for the copy_file_range symbol */ func_ptr = dlsym(RTLD_DEFAULT, "copy_file_range"); if (NULL == func_ptr) return FALSE; /* Get kernel version */ if (uname(&relbuff) != 0) return FALSE; ptr = relbuff.release; kernel_major = STRTOL(ptr, &ptr, 10); ptr++; kernel_minor = STRTOL(ptr, &ptr, 10); /* Get glibc version */ glibcversion = ATOF(gnu_get_libc_version()); /* Check if Linux Kernel >= 5.3 and GLIBC > 2.27 */ if ((5 < kernel_major) || ((5 == kernel_major) && (3 <= kernel_minor) && ((glibcversion > 2.27)))) return TRUE; else return FALSE; } #endif fis-gtm-V7.0-005/sr_unix/mubgetfil.c0000755000032200000250000000575114342376330016176 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_stat.h" #include "gtm_string.h" #include "gtm_unistd.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "iosp.h" #include "gdsfhead.h" #include "filestruct.h" #include "mupipbckup.h" #include "gdscc.h" #include "gdskill.h" #include "jnl.h" #include "buddy_list.h" #include "tp.h" #include "eintr_wrappers.h" #include "util.h" #include "gtm_caseconv.h" #include "mupip_exit.h" GBLREF backup_reg_list *mu_repl_inst_reg_list; GBLREF boolean_t is_directory; GBLREF mstr directory; #define SET_BACKUP_FILE_LIST(BACKUPFILE, FULLPATH, LEN) \ { \ BACKUPFILE.len = LEN; \ BACKUPFILE.addr = (char *)malloc(LEN + 1); \ assert(NULL != BACKUPFILE.addr); \ memcpy(BACKUPFILE.addr, FULLPATH, LEN); \ *(BACKUPFILE.addr + LEN) = 0; \ } boolean_t mubgetfil(backup_reg_list *list, char *name, unsigned short len) { char tcp[5], temp, fullpath[GTM_PATH_MAX]; uint4 fullpathlen, status; int stat_res; struct stat stat_buf; boolean_t newfile = FALSE; if (0 == len) return FALSE; if (list != mu_repl_inst_reg_list) { /* Do the following checks only if this region does NOT correspond to the replication instance region. */ if ('|' == *name) { len -= 1; list->backup_to = backup_to_exec; SET_BACKUP_FILE_LIST(list->backup_file, name + 1, len); return TRUE; } if (len > 5) { lower_to_upper((uchar_ptr_t)tcp, (uchar_ptr_t)name, 5); if (0 == memcmp(tcp, "TCP:/", 5)) { list->backup_to = backup_to_tcp; len -= 5; name += 5; while ('/' == *name) { len--; name++; } SET_BACKUP_FILE_LIST(list->backup_file, name, len); return TRUE; } } } temp = name[len]; name[len] = '\0'; STAT_FILE(name, &stat_buf, stat_res); get_full_path(name, len, fullpath, &fullpathlen, GTM_PATH_MAX, &status); if (-1 == stat_res) { if (errno != ENOENT) { util_out_print("Error accessing backup output file or directory: !AD", TRUE, len, name); mupip_exit(errno); } else { /* new file */ SET_BACKUP_FILE_LIST(list->backup_file, fullpath, fullpathlen); } } else if (S_ISDIR(stat_buf.st_mode)) { assert(!newfile); if (!is_directory) { is_directory = TRUE; SET_BACKUP_FILE_LIST(directory, fullpath, fullpathlen); } mubexpfilnam(fullpath, fullpathlen, list); } else { /* The file already exists. */ assert(!newfile); SET_BACKUP_FILE_LIST(list->backup_file, fullpath, fullpathlen) } name[len] = temp; return TRUE; } fis-gtm-V7.0-005/sr_unix/mubinccpy.c0000755000032200000250000006665314342376330016221 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* mubinccpy.c * * -- online incremental online && incremental * -- incremental !online && incremental * -------- requires cs_addrs, cs_data and gv_cur_region be current. */ #include "mdef.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_stat.h" #include "gtm_unistd.h" #include "gtm_fcntl.h" #include "gtm_socket.h" #include "gtm_inet.h" #include #include #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "db_header_conversion.h" #include "filestruct.h" #include "gdsblk.h" #include "gdsbml.h" #include "stringpool.h" #include "muextr.h" #include "murest.h" #include "iob.h" #include "error.h" #include "mupipbckup.h" #include "gtmio.h" #include "gtm_pipe.h" #include "iotimer.h" #include "eintr_wrappers.h" #include "sleep_cnt.h" #include "util.h" #include "cli.h" #include "op.h" #include "io.h" #include "is_proc_alive.h" #include "is_raw_dev.h" #include "gtmmsg.h" #include "wcs_sleep.h" #include "gds_blk_upgrade.h" #include "iosp.h" #include "shmpool.h" #include "min_max.h" #include "gvcst_lbm_check.h" #include "wcs_phase2_commit_wait.h" #include "gtm_permissions.h" #include "gtmcrypt.h" GBLREF bool record; GBLREF bool online; GBLREF bool incremental; GBLREF bool file_backed_up; GBLREF bool mubtomag; GBLREF bool mu_ctrly_occurred; GBLREF bool mu_ctrlc_occurred; GBLREF int4 mubmaxblk; GBLREF spdesc stringpool; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF uint4 pipe_child; GBLREF uint4 process_id; GBLREF boolean_t debug_mupip; GBLREF int4 backup_write_errno; LITREF mval literal_null; error_def(ERR_BCKUPBUFLUSH); error_def(ERR_COMMITWAITSTUCK); error_def(ERR_DBCCERR); error_def(ERR_DBROLLEDBACK); error_def(ERR_ERRCALL); error_def(ERR_BACKUPDBFILE); error_def(ERR_BACKUPTN); #ifdef DEBUG_INCBKUP # define DEBUG_INCBKUP_ONLY(X) X #else # define DEBUG_INCBKUP_ONLY(X) #endif #define COMMON_WRITE(A, B, C) { \ (*common_write)(A, B, (int)C); \ if (0 != backup_write_errno) \ return FALSE; \ DEBUG_INCBKUP_ONLY(backup_write_offset += (int)C;) \ } #define CLEANUP_AND_RETURN_FALSE { \ if (backup_to_file == list->backup_to) \ { \ if (NULL != backup) \ iob_close(backup); \ if (!debug_mupip) \ UNLINK(file->addr); \ } \ return FALSE; \ } #define MAX_FILENAME_LENGTH 256 static char incbackupfile[MAX_FILENAME_LENGTH]; static BFILE *backup; void exec_write(BFILE *bf, char *buf, int nbytes); void tcp_write(BFILE *bf, char *buf, int nbytes); error_def(ERR_BCKUPBUFLUSH); error_def(ERR_COMMITWAITSTUCK); error_def(ERR_DBCCERR); error_def(ERR_ERRCALL); error_def(ERR_IOEOF); bool mubinccpy (backup_reg_list *list) { mstr *file; uchar_ptr_t bm_blk_buff, ptr1, ptr1_top; char_ptr_t outptr, data_ptr; char *c, addr[SA_MAXLEN + 1]; sgmnt_data_ptr_t header; uint4 total_blks, bplmap, gds_ratio, save_blks; int4 status; int4 size1, bsize, bm_num, hint, lmsize, rsize, timeout, outsize, blks_per_buff, counter, i, match; size_t copysize, write_size, read_size; off_t copied, read_offset; int db_fd, exec_fd; enum db_acc_method access; blk_hdr_ptr_t bp, bptr; inc_header *outbuf; mval val; unsigned short port; void (*common_write)(BFILE *, char *, int); muinc_blk_hdr_ptr_t sblkh_p; trans_num blk_tn; int4 blk_bsiz; block_id blk_num_base, blk_num; boolean_t is_bitmap_blk, backup_this_blk; enum db_ver dummy_odbv; int rc; int fstat_res; struct stat stat_buf; int user_id; int group_id; int perm; struct perm_diag_data pdd; int4 cur_mdb_ver; unix_db_info *udi; DEBUG_INCBKUP_ONLY(int blks_this_lmap;) DEBUG_INCBKUP_ONLY(gtm_uint64_t backup_write_offset = 0;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(list->reg == gv_cur_region); assert(incremental); /* Make sure inc_header can be same size on all platforms. Some platforms pad 8 byte aligned structures * that end on a 4 byte boundary and some do not. It is critical that this structure is the same size on * all platforms as it is sent across TCP connections when doing TCP backup. */ assert(0 == (SIZEOF(inc_header) % 8)); /* ================= Initialization and some checks ======================== */ header = list->backup_hdr; file = &(list->backup_file); if (list->tn >= header->trans_hist.curr_tn) { util_out_print("!/TRANSACTION number is greater than or equal to current transaction,", TRUE); util_out_print("no blocks backed up from database !AD", TRUE, DB_LEN_STR(gv_cur_region)); return TRUE; } if (!mubtomag) mubmaxblk = (64 * 1024); udi = FILE_INFO(gv_cur_region); db_fd = udi->fd; /* =================== open backup destination ============================= */ backup_write_errno = 0; switch (list->backup_to) { case backup_to_file: common_write = iob_write; backup = iob_open_wt(file->addr, DISK_BLOCK_SIZE, BLOCKING_FACTOR); if (NULL == backup) { PERROR("open error: "); util_out_print("Error: Cannot create backup file !AD.", TRUE, file->len, file->addr); return FALSE; } FSTAT_FILE(db_fd, &stat_buf, fstat_res); if (-1 != fstat_res && !gtm_permissions(&stat_buf, &user_id, &group_id, &perm, PERM_FILE, &pdd)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6+PERMGENDIAG_ARG_COUNT) ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("incremental backup file"), file->len, file->addr, PERMGENDIAG_ARGS(pdd)); CLEANUP_AND_RETURN_FALSE; } /* setup new group and permissions if indicated by the security rules. */ if ((-1 == fstat_res) || (-1 == FCHMOD(backup->fd, perm)) || (((INVALID_UID != user_id) || (INVALID_GID != group_id)) && (-1 == fchown(backup->fd, user_id, group_id)))) { PERROR("fchmod/fchown error: "); util_out_print("ERROR: Cannot access incremental backup file !AD.", TRUE, file->len, file->addr); util_out_print("WARNING: Backup file !AD is not valid.", TRUE, file->len, file->addr); CLEANUP_AND_RETURN_FALSE; } memcpy(incbackupfile, file->addr, file->len); incbackupfile[file->len] = 0; ESTABLISH_RET(iob_io_error2, FALSE); break; case backup_to_exec: pipe_child = 0; common_write = exec_write; backup = (BFILE *)malloc(SIZEOF(BFILE)); backup->blksiz = DISK_BLOCK_SIZE; backup->remaining = 0; /* number of zeros to be added in the end, just use this field */ if (0 > (backup->fd = gtm_pipe(file->addr, output_to_comm))) { util_out_print("ERROR: Cannot create backup pipe.", TRUE); util_out_print("WARNING: backup !AD is not valid.", TRUE, file->len, file->addr); return FALSE; } break; case backup_to_tcp: common_write = tcp_write; backup = (BFILE *)malloc(SIZEOF(BFILE)); backup->blksiz = DISK_BLOCK_SIZE; backup->remaining = 0; /* number of zeros to be added in the end, just use this field */ /* parse it first */ switch (match = SSCANF(file->addr, "%[^:]:%hu", addr, &port)) { case 1 : port = DEFAULT_BKRS_PORT; case 2 : break; default : util_out_print("ERROR: A hostname has to be specified to backup through a TCP connection.", TRUE); return FALSE; } assert(SIZEOF(timeout) == SIZEOF(int)); if ((0 == cli_get_int("NETTIMEOUT", (int4 *)&timeout)) || (0 > timeout)) timeout = DEFAULT_BKRS_TIMEOUT; if (0 > (backup->fd = tcp_open(addr, port, timeout, FALSE))) { util_out_print("ERROR: Cannot open tcp connection due to the above error.", TRUE); util_out_print("WARNING: Backup !AD is not valid.", TRUE, file->len, file->addr); return FALSE; } break; default : util_out_print("ERROR: Backup format not supported.", TRUE); util_out_print("WARNING: Backup not valid.", TRUE); return FALSE; } /* ============================= write inc_header =========================================== */ outbuf = (inc_header *)malloc(SIZEOF(inc_header)); MEMCPY_LIT(&outbuf->label[0], INC_HEADER_LABEL_DFLT); stringpool.free = stringpool.base; op_zhorolog(&val, FALSE); stringpool.free = stringpool.base; op_fnzdate(&val, (mval *)&mu_bin_datefmt, (mval *)&literal_null, (mval *)&literal_null, &val); memcpy(&outbuf->date[0], val.str.addr, val.str.len); memcpy(&outbuf->reg[0], gv_cur_region->rname, MAX_RN_LEN); outbuf->start_tn = list->tn; outbuf->end_tn = header->trans_hist.curr_tn; outbuf->db_total_blks = header->trans_hist.total_blks; outbuf->blk_size = header->blk_size; outbuf->blks_to_upgrd = header->blks_to_upgrd; GTMCRYPT_COPY_ENCRYPT_SETTINGS(header, outbuf); COMMON_WRITE(backup, (char *)outbuf, SIZEOF(inc_header)); free(outbuf); cur_mdb_ver = header->minor_dbver; COMMON_WRITE(backup, (char *)&cur_mdb_ver, SIZEOF(int4)); if (mu_ctrly_occurred || mu_ctrlc_occurred) { util_out_print("WARNING: DB file !AD backup aborted, file !AD not valid", TRUE, DB_LEN_STR(gv_cur_region), file->len, file->addr); CLEANUP_AND_RETURN_FALSE; } /* ============================ read/write appropriate blocks =============================== */ bsize = header->blk_size; gds_ratio = bsize / DISK_BLOCK_SIZE; blks_per_buff = BACKUP_READ_SIZE / bsize; /* Worse case holds one block */ read_size = blks_per_buff * bsize; outsize = SIZEOF(muinc_blk_hdr) + bsize; outptr = (char_ptr_t)malloc(MAX(outsize, mubmaxblk)); sblkh_p = (muinc_blk_hdr_ptr_t)outptr; data_ptr = (char_ptr_t)(sblkh_p + 1); /* If udi->fd_opened_with_o_direct is TRUE, we need an aligned buffer to proceed with the reads. And if it is FALSE, * we anyways have to allocate a 64Kb (BACKUP_READ_SIZE) buffer and free it. Most of this logic is already present * in the below macro so we use it even for the no-O_DIRECT case (i.e. dio_buff.aligned is used even if * udi->fd_opened_with_o_direct is FALSE). */ DIO_BUFF_EXPAND_IF_NEEDED_NO_UDI(BACKUP_READ_SIZE, BACKUP_READ_SIZE, &(TREF(dio_buff))); bp = (blk_hdr_ptr_t)(TREF(dio_buff)).aligned; bm_blk_buff = (uchar_ptr_t)malloc(SIZEOF(blk_hdr) + (BLKS_PER_LMAP * BML_BITS_PER_BLK / BITS_PER_UCHAR)); save_blks = 0; memset(sblkh_p, 0, SIZEOF(*sblkh_p)); sblkh_p->use.bkup.ondsk_blkver = GDSNOVER; DEBUG_INCBKUP_ONLY(blks_this_lmap = 0); if (cs_addrs->nl->onln_rlbk_pid) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) ERR_DBROLLEDBACK); free(outptr); free(bm_blk_buff); CLEANUP_AND_RETURN_FALSE; } read_offset = (off_t)BLK_ZERO_OFF(header->start_vbn); for (blk_num_base = 0; blk_num_base < header->trans_hist.total_blks; blk_num_base += blks_per_buff) { if (online && (0 != cs_addrs->shmpool_buffer->failed)) break; if ((header->trans_hist.total_blks - blk_num_base) < blks_per_buff) { /* (header->trans_hist.total_blks - blk_num_base) can be cast because it should fit in an int4 */ assert((header->trans_hist.total_blks - blk_num_base) == (int4)(header->trans_hist.total_blks - blk_num_base)); blks_per_buff = (int4)(header->trans_hist.total_blks - blk_num_base); read_size = blks_per_buff * bsize; } DB_LSEEKREAD(udi, db_fd, read_offset, bp, read_size, status); if (0 != status) { PERROR("read error: "); util_out_print("Error reading from database file !AD.", TRUE, DB_LEN_STR(gv_cur_region)); util_out_print("WARNING: backup file !AD is not valid.", TRUE, DB_LEN_STR(gv_cur_region)); free(outptr); free(bm_blk_buff); CLEANUP_AND_RETURN_FALSE; } read_offset += read_size; bptr = (blk_hdr *)bp; /* The blocks we back up will be whatever version they are. There is no implicit conversion in this * part of the backup/restore. Since we aren't even looking at the blocks (and indeed some of these blocks * could potentially contain unintialized garbage data), we set the block version to GDSNOVER to signal * that the block version is unknown. The above applies to "regular" blocks but not to bitmap blocks which * we know are initialized. Because we have to read the bitmap blocks, they will be converted as necessary. */ for (i = 0; i < blks_per_buff; i++, bptr = (blk_hdr *)((char *)bptr + bsize)) { blk_num = blk_num_base + i; if (mu_ctrly_occurred || mu_ctrlc_occurred) { free(outptr); free(bm_blk_buff); util_out_print("WARNING: DB file !AD backup aborted, file !AD not valid", TRUE, DB_LEN_STR(gv_cur_region), file->len, file->addr); CLEANUP_AND_RETURN_FALSE; } if (cs_addrs->nl->onln_rlbk_pid) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) ERR_DBROLLEDBACK); free(outptr); free(bm_blk_buff); CLEANUP_AND_RETURN_FALSE; } /* Before we check if this block needs backing up, check if this is a new bitmap block or not. If it is, * we can fall through and back it up as normal. But if this is NOT a bitmap block, use the * existing bitmap to determine if this block has ever been allocated or not. If not, we don't want to * even look at this block. It could be uninitialized which will just make things run slower if we * go to read it and back it up. */ if (0 != ((BLKS_PER_LMAP - 1) & blk_num)) { /* Not a local bitmap block */ if (!gvcst_blk_ever_allocated(bm_blk_buff + SIZEOF(blk_hdr), ((blk_num * BML_BITS_PER_BLK) % (BLKS_PER_LMAP * BML_BITS_PER_BLK)))) continue; /* Bypass never-set blocks to avoid conversion problems */ is_bitmap_blk = FALSE; if (SIZEOF(v15_blk_hdr) <= (blk_bsiz = ((v15_blk_hdr_ptr_t)bptr)->bsiz)) { /* We have either a V4 block or uninitialized garbage */ if (blk_bsiz > bsize) /* This is not a valid V4 block so ignore it */ continue; blk_tn = ((v15_blk_hdr_ptr_t)bptr)->tn; } else { /* Assume V5 block */ if ((blk_bsiz = bptr->bsiz) > bsize) /* Not a valid V5 block either */ continue; blk_tn = bptr->tn; } } else { /* This is a bitmap block so save it into our bitmap block buffer. It is used as the * basis of whether or not we have to process a given block or not. We process allocated and * recycled blocks leaving free (never used) blocks alone as they have no data worth saving. * But after saving it, upgrade it to the current format if necessary. */ #ifdef DEBUG_INCBKUP if (0 != blk_num) /* Skip first time thorugh loop */ { PRINTF("Dumped %d blks from lcl bitmap blk 0x%016lx\n", blks_this_lmap, (blk_num - BLKS_PER_LMAP)); blks_this_lmap = 0; } #endif is_bitmap_blk = TRUE; memcpy(bm_blk_buff, bptr, BM_SIZE(header->bplmap)); if (SIZEOF(v15_blk_hdr) <= ((v15_blk_hdr_ptr_t)bm_blk_buff)->bsiz) { /* This is a V4 format block -- needs upgrading */ status = gds_blk_upgrade(bm_blk_buff, bm_blk_buff, bsize, &dummy_odbv); if (SS_NORMAL != status) { free(outptr); free(bm_blk_buff); util_out_print("Error: Block 0x!XL is too large for automatic upgrade", TRUE, blk_num); CLEANUP_AND_RETURN_FALSE; } } assert(BM_SIZE(header->bplmap) == ((blk_hdr_ptr_t)bm_blk_buff)->bsiz); assert(LCL_MAP_LEVL == ((blk_hdr_ptr_t)bm_blk_buff)->levl); assert(gvcst_blk_is_allocated(bm_blk_buff + SIZEOF(blk_hdr), ((blk_num * BML_BITS_PER_BLK) % (BLKS_PER_LMAP * BML_BITS_PER_BLK)))); blk_bsiz = BM_SIZE(header->bplmap); blk_tn = ((blk_hdr_ptr_t)bm_blk_buff)->tn; } /* The conditions for backing up a block or ignoring it (in order of evaluation): * 1) If blk is larger than size of db at time backup was initiated, we ignore the block. * 2) Always backup blocks 0, 1, and 2 as these are the only blocks that can contain data * and still have a transaction number of 0. * 3) For bitmap blocks, if blks_to_upgrd != 0 and the TN is 0 and the block number >= * last_blk_at_last_bkup, then backup the block. This way we get the correct version of * the bitmap block in the restore (otherwise have no clue what version to create them in * as bitmaps are created with a TN of 0 when before image journaling is enabled). * Given the possibility of a db file truncate occurring between backups, it is now possible * for gdsfilext to create a new bitmap block such that blk_num < list->last_blk_at_last_bkup. * This means the previous state (at the last backup) of a bitmap block with tn=0 is * indeterminate: it might have held data and it might have had a different version. So * backup all bitmap blocks with tn=0. * One concern is that incremental backup files can end up somewhat larger (due to backing * up extra bitmap blocks), even if no truncate occurred between backups. Users can zip their * incremental backup files which should deflate almost all of the tn=0 bitmap blocks. * Future enhancement: Instead of backing up bitmap blocks with tn=0, make a list of the * the blk_nums and versions of all such blocks. The bitmap blocks can be created in * mupip_restore from that information alone. * 4) If the block TN is below our TN threshold, ignore the block. * 5) Else if none of the above conditions, backup the block. */ if (online && (header->trans_hist.curr_tn <= blk_tn)) backup_this_blk = FALSE; else if ((3 > blk_num) || (is_bitmap_blk && (0 != header->blks_to_upgrd) && ((trans_num)0 == blk_tn) && (blk_num >= list->last_blk_at_last_bkup))) backup_this_blk = TRUE; else if (is_bitmap_blk && ((trans_num)0 == blk_tn)) backup_this_blk = TRUE; else if ((blk_tn < list->tn)) backup_this_blk = FALSE; else backup_this_blk = TRUE; if (!backup_this_blk) { if (online) cs_addrs->nl->nbb = blk_num; continue; /* not applicable */ } DEBUG_INCBKUP_ONLY(++blks_this_lmap); sblkh_p->blkid = blk_num; memcpy(data_ptr, bptr, blk_bsiz); sblkh_p->valid_data = TRUE; /* Validation marker */ DEBUG_INCBKUP_ONLY(PRINTF("DEBUG - write block: blk_num = 0x%08lx, start = 0x%016lx, size = 0x%x \n", blk_num, backup_write_offset, outsize);) COMMON_WRITE(backup, outptr, outsize); if (online) { if (0 != cs_addrs->shmpool_buffer->failed) break; cs_addrs->nl->nbb = blk_num; } save_blks++; } DEBUG_INCBKUP_ONLY(PRINTF("Dumped %d blks from lcl bitmap blk 0x%016lx\n", blks_this_lmap, (blk_num & ~(BLKS_PER_LMAP - 1)))); } if (cs_addrs->nl->onln_rlbk_pid) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) ERR_DBROLLEDBACK); free(outptr); free(bm_blk_buff); CLEANUP_AND_RETURN_FALSE; } /* After this point, if an Online Rollback is detected, the BACKUP will NOT e affected as all the before images it needs * is already written by GT.M in the temporary file and the resulting BACKUP will be valid*/ /* ============================ write saved information for online backup ========================== */ if (online && (0 == cs_addrs->shmpool_buffer->failed)) { cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS; /* By getting crit here, we ensure that there is no process still in transaction logic that sees * (nbb != BACKUP_NOT_IN_PRORESS). After rel_crit(), any process that enters transaction logic will * see (nbb == BACKUP_NOT_IN_PRORESS) because we just set it to that value. At this point, backup * buffer is complete and there will not be any more new entries in the backup buffer until the next * backup. */ assert(!cs_addrs->hold_onto_crit); /* this ensures we can safely do unconditional grab_crit and rel_crit */ grab_crit(gv_cur_region, WS_90); assert(cs_data == cs_addrs->hdr); if (dba_bg == cs_data->acc_meth) { /* Now that we have crit, wait for any pending phase2 updates to finish. Since phase2 updates happen * outside of crit, we don't want them to keep writing to the backup temporary file even after the * backup is complete and the temporary file has been deleted. */ if (cs_addrs->nl->wcs_phase2_commit_pidcnt && !wcs_phase2_commit_wait(cs_addrs, NULL)) { assert(FALSE); gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(7) ERR_COMMITWAITSTUCK, 5, process_id, 1, cs_addrs->nl->wcs_phase2_commit_pidcnt, DB_LEN_STR(gv_cur_region)); rel_crit(gv_cur_region); CLEANUP_AND_RETURN_FALSE; } } if (debug_mupip) { util_out_print("MUPIP INFO: Current Transaction # at end of backup is 0x!16@XQ", TRUE, &cs_data->trans_hist.curr_tn); } rel_crit(gv_cur_region); counter = 0; while (0 != cs_addrs->shmpool_buffer->backup_cnt) { backup_buffer_flush(gv_cur_region); if (++counter > MAX_BACKUP_FLUSH_TRY) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) ERR_BCKUPBUFLUSH); CLEANUP_AND_RETURN_FALSE; } if (counter & 0xF) { # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_FORCE_SHMPLRECOV)) /* Fake shmpool_blocked */ cs_addrs->shmpool_buffer->shmpool_blocked = TRUE; # endif wcs_sleep(counter); } else { /* Force shmpool recovery to see if it can find the lost blocks */ if (!shmpool_lock_hdr(gv_cur_region)) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(9) ERR_DBCCERR, 2, REG_LEN_STR(gv_cur_region), ERR_ERRCALL, 3, CALLFROM); assert(FALSE); CLEANUP_AND_RETURN_FALSE; } shmpool_abandoned_blk_chk(gv_cur_region, TRUE); shmpool_unlock_hdr(gv_cur_region); } } if (-1 == lseek(list->backup_fd, 0, SEEK_SET)) { PERROR("lseek error : "); CLEANUP_AND_RETURN_FALSE; } copysize = BACKUP_READ_SIZE; for (copied = 0; copied < cs_addrs->shmpool_buffer->dskaddr; copied += copysize) { if (cs_addrs->shmpool_buffer->dskaddr < copied + copysize) copysize = (size_t)(cs_addrs->shmpool_buffer->dskaddr - copied); DOREADRC(list->backup_fd, (TREF(dio_buff)).aligned, copysize, status); if (0 != status) { PERROR("read error : "); CLEANUP_AND_RETURN_FALSE; } COMMON_WRITE(backup, (char *)(TREF(dio_buff)).aligned, copysize); } } /* Write one last (zero-filled) block into this file that designates the end of the blocks */ memset(outptr, 0, SIZEOF(muinc_blk_hdr) + bsize); COMMON_WRITE(backup, outptr, outsize); /* ============================= write end_msg and fileheader =============================== */ if ((!online) || (0 == cs_addrs->shmpool_buffer->failed)) { /* Write a secondary end-of-block list marker used for further validation on restore */ rsize = SIZEOF(END_MSG) + SIZEOF(int4); COMMON_WRITE(backup, (char *)&rsize, SIZEOF(int4)); COMMON_WRITE(backup, END_MSG, SIZEOF(END_MSG)); if (0 == MEMCMP_LIT(header->label, V6_GDS_LABEL)) db_header_dwnconv(header); ptr1 = (uchar_ptr_t)header; ptr1_top = ptr1 + ROUND_UP(SIZEOF(sgmnt_data), DISK_BLOCK_SIZE); for ( ; ptr1 < ptr1_top; ptr1 += size1) { if ((size1 = (int4)(ptr1_top - ptr1)) > mubmaxblk) size1 = (mubmaxblk / DISK_BLOCK_SIZE) * DISK_BLOCK_SIZE; size1 += SIZEOF(int4); COMMON_WRITE(backup, (char *)&size1, SIZEOF(int4)); size1 -= SIZEOF(int4); COMMON_WRITE(backup, (char *)ptr1, size1); } rsize = SIZEOF(HDR_MSG) + SIZEOF(int4); COMMON_WRITE(backup, (char *)&rsize, SIZEOF(int4)); COMMON_WRITE(backup, HDR_MSG, SIZEOF(HDR_MSG)); ptr1 = MM_ADDR(header); ptr1_top = ptr1 + ROUND_UP(MASTER_MAP_SIZE(header), DISK_BLOCK_SIZE); for ( ; ptr1 < ptr1_top ; ptr1 += size1) { if ((size1 = (int4)(ptr1_top - ptr1)) > mubmaxblk) size1 = (mubmaxblk / DISK_BLOCK_SIZE) * DISK_BLOCK_SIZE; size1 += SIZEOF(int4); COMMON_WRITE(backup, (char *)&size1, SIZEOF(int4)); size1 -= SIZEOF(int4); COMMON_WRITE(backup, (char *)ptr1, size1); } rsize = SIZEOF(MAP_MSG) + SIZEOF(int4); COMMON_WRITE(backup, (char *)&rsize, SIZEOF(int4)); COMMON_WRITE(backup, MAP_MSG, SIZEOF(MAP_MSG)); rsize = 0; COMMON_WRITE(backup, (char *)&rsize, SIZEOF(rsize)); } /* ========================== close backup destination ======================================== */ switch (list->backup_to) { case backup_to_file: REVERT; iob_close(backup); backup = NULL; break; case backup_to_exec: if (0 != backup->remaining) { assert(backup->blksiz > backup->remaining); memset(outptr, 0, backup->blksiz - backup->remaining); COMMON_WRITE(backup, outptr, backup->blksiz - backup->remaining); } CLOSEFILE_RESET(backup->fd, rc); /* resets "backup->fd" to FD_INVALID */ /* needs to wait till the child dies, because of the rundown issues */ if ((pipe_child > 0) && (FALSE != is_proc_alive(pipe_child, 0))) { pid_t waitpid_res; WAITPID(pipe_child, (int *)&status, 0, waitpid_res); } break; case backup_to_tcp: if (0 != backup->remaining) { assert(backup->blksiz > backup->remaining); memset(outptr, 0, backup->blksiz - backup->remaining); COMMON_WRITE(backup, outptr, backup->blksiz - backup->remaining); } CLOSEFILE_RESET(backup->fd, rc); /* resets "backup->fd" to FD_INVALID */ break; } /* ============================ output and return =========================================== */ free(outptr); free(bm_blk_buff); if (online && (0 != cs_addrs->shmpool_buffer->failed)) { util_out_print("Process !UL encountered the following error.", TRUE, cs_addrs->shmpool_buffer->failed); if (0 != cs_addrs->shmpool_buffer->backup_errno) gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) cs_addrs->shmpool_buffer->backup_errno); util_out_print("!AD, backup for DB file !AD, is not valid.", TRUE, file->len, file->addr, DB_LEN_STR(gv_cur_region)); } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_BACKUPDBFILE, 4, DB_LEN_STR(gv_cur_region), file->len, file->addr); util_out_print("!UL blocks saved.", TRUE, save_blks); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_BACKUPTN, 2, &list->tn, &header->trans_hist.curr_tn); cs_addrs->hdr->last_inc_backup = header->trans_hist.curr_tn; cs_addrs->hdr->last_inc_bkup_last_blk = (block_id)header->trans_hist.total_blks; if (record) { cs_addrs->hdr->last_rec_backup = header->trans_hist.curr_tn; cs_addrs->hdr->last_com_bkup_last_blk = (block_id)header->trans_hist.total_blks; cs_addrs->hdr->last_start_backup = header->last_start_backup; } file_backed_up = TRUE; return TRUE; } CLEANUP_AND_RETURN_FALSE; } void exec_write(BFILE *bf, char *buf, int nbytes) { int nwritten; uint4 status; pid_t waitpid_res; int rc; DOWRITERL(bf->fd, buf, nbytes, nwritten); bf->remaining += nwritten; bf->remaining %= bf->blksiz; if ((nwritten < nbytes) && (-1 == nwritten)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); CLOSEFILE_RESET(bf->fd, rc); /* resets "bf->fd" to FD_INVALID */ if ((pipe_child > 0) && (FALSE != is_proc_alive(pipe_child, 0))) WAITPID(pipe_child, (int *)&status, 0, waitpid_res); backup_write_errno = errno; } return; } void tcp_write(BFILE *bf, char *buf, int nbytes) { int nwritten, iostatus; int send_retry; int rc; nwritten = 0; send_retry = 5; do { SEND(bf->fd, buf + nwritten, nbytes - nwritten, 0, iostatus); if (-1 != iostatus) { nwritten += iostatus; if (nwritten == nbytes) break; } else break; } while (0 < send_retry--); bf->remaining += nwritten; bf->remaining %= bf->blksiz; if ((nwritten != nbytes) && (-1 == iostatus)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); CLOSEFILE_RESET(bf->fd, rc); /* resets "bf->fd" to FD_INVALID */ backup_write_errno = errno; } return; } CONDITION_HANDLER(iob_io_error2) { int dummy1, dummy2; char s[80]; START_CH(TRUE); PRN_ERROR; if (!debug_mupip) UNLINK(incbackupfile); UNWIND(dummy1, dummy2); } fis-gtm-V7.0-005/sr_unix/mucblkini.c0000644000032200000250000000760314342376330016170 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include "gtm_stdio.h" #include "gtm_stdlib.h" /* for EXIT() */ #include "gtm_unistd.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "gdsblk.h" #include "filestruct.h" #include "iosp.h" #include "copy.h" #include "gdsbml.h" #include "gtmio.h" #include "mucblkini.h" #include "anticipatory_freeze.h" #include "jnl.h" #define DIR_ROOT 1 #define DIR_DATA 2 #define PUTMSG_ERROR_CSA(MSGPARMS) \ MBSTART { \ if (IS_MUPIP_IMAGE) \ gtm_putmsg_csa MSGPARMS; \ else \ rts_error_csa MSGPARMS; \ } MBEND GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; error_def(ERR_AUTODBCREFAIL); error_def(ERR_FILECREERR); /* This function is only called during the creation of a new region */ void mucblkini(enum db_ver desired_db_ver) { uchar_ptr_t c, bmp; blk_hdr_ptr_t bp1, bp2; rec_hdr_ptr_t rp; unix_db_info *udi; int4 bmpsize, status; block_id blk; boolean_t isv7blk; isv7blk = (GDSVCURR == desired_db_ver); udi = FILE_INFO(gv_cur_region); bp1 = (blk_hdr_ptr_t)malloc(cs_addrs->hdr->blk_size); bp2 = (blk_hdr_ptr_t)malloc(cs_addrs->hdr->blk_size); bmpsize = BM_SIZE(cs_addrs->hdr->bplmap); if (cs_data->write_fullblk) bmpsize = (int4)ROUND_UP(bmpsize, cs_addrs->fullblockwrite_len); bmp = (uchar_ptr_t)malloc(bmpsize); DB_LSEEKREAD(udi, udi->fd, (off_t)BLK_ZERO_OFF(cs_addrs->hdr->start_vbn), bmp, bmpsize, status); if (0 != status) { PUTMSG_ERROR_CSA((CSA_ARG(cs_addrs) VARLSTCNT(7) ERR_FILECREERR, 4, LEN_AND_LIT("reading first bitmap"), DB_LEN_STR(gv_cur_region), status)); return; } bml_busy(DIR_ROOT, bmp + SIZEOF(blk_hdr)); bml_busy(DIR_DATA, bmp + SIZEOF(blk_hdr)); ASSERT_NO_DIO_ALIGN_NEEDED(udi); /* because we are creating the database and so effectively have standalone access */ DB_LSEEKWRITE(cs_addrs, udi, udi->fn, udi->fd, (off_t)BLK_ZERO_OFF(cs_addrs->hdr->start_vbn), bmp, bmpsize, status); free(bmp); if (0 != status) { PUTMSG_ERROR_CSA((CSA_ARG(cs_addrs) VARLSTCNT(7) ERR_FILECREERR, 4, LEN_AND_LIT("writing out first bitmap"), DB_LEN_STR(gv_cur_region), status)); return; } rp = (rec_hdr_ptr_t)((uchar_ptr_t)bp1 + SIZEOF(blk_hdr)); bstar_rec((sm_uc_ptr_t)rp, isv7blk); /* This function is creating a new region so it will always be making V7 blocks */ c = CST_BOK(rp); blk = DIR_DATA; if (isv7blk) PUT_BLK_ID_64(c, blk); else PUT_BLK_ID_32(c, blk); bp1->bver = desired_db_ver; bp1->levl = 1; bp1->bsiz = bstar_rec_size(isv7blk) + SIZEOF(blk_hdr); bp1->tn = 0; bp2->bver = desired_db_ver; bp2->levl =0; bp2->bsiz = SIZEOF(blk_hdr); bp2->tn = 0; DSK_WRITE_NOCACHE(gv_cur_region, DIR_ROOT, (uchar_ptr_t)bp1, cs_addrs->hdr->desired_db_format, status); free(bp1); if (0 != status) { if (IS_MUMPS_IMAGE) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_AUTODBCREFAIL, 4, DB_LEN_STR(gv_cur_region), REG_LEN_STR(gv_cur_region), status); else { PERROR("Error writing to disk"); EXIT(status); } } DSK_WRITE_NOCACHE(gv_cur_region, DIR_DATA, (uchar_ptr_t)bp2, cs_addrs->hdr->desired_db_format, status); free(bp2); if (0 != status) { if (IS_MUMPS_IMAGE) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_AUTODBCREFAIL, 4, DB_LEN_STR(gv_cur_region), REG_LEN_STR(gv_cur_region), status); else { PERROR("Error writing to disk"); EXIT(status); } } return; } fis-gtm-V7.0-005/sr_unix/mumps.cmake0000644000032200000250000000163714342376330016213 0ustar librarygtc################################################################# # # # Copyright (c) 2012-2015 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. # # # ################################################################# foreach(v gtm_dist gtmroutines gtm_chset gtm_icu_version gtm_inc gtm_tools gtmgbldir LC_ALL ) if(DEFINED ${v}) set("ENV{${v}}" "${${v}}") endif() endforeach() if(input_file) set(input_file INPUT_FILE ${input_file}) endif() if(output_file) set(output_file OUTPUT_FILE ${output_file}) endif() execute_process( COMMAND ${mumps} ${args} ${input_file} ${output_file} ) fis-gtm-V7.0-005/sr_unix/mumps_clitab.c0000755000032200000250000000465314342376330016677 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "cli.h" /************************************************************* * NOTE * This file might have lines longer than 132 characters * since the command tables are being initialized. * * Entries need to be made in sorted order (lexicographic) within * each table. ************************************************************/ static readonly CLI_PARM mumps_parm[] = { {"INFILE", "What file: ", PARM_REQ}, {""} }; static readonly CLI_ENTRY mumps_qual[] = { { "ALIGN_STRINGS", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0}, { "CROSS_REFERENCE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0}, { "DEBUG", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0}, { "DIRECT_MODE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0}, { "DYNAMIC_LITERALS", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0}, { "EMBED_SOURCE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0}, { "IGNORE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0}, { "INLINE_LITERALS", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0}, { "LABELS", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0}, { "LENGTH", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0}, { "LINE_ENTRY", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0}, { "LIST", 0, 0, 0, 0, 0, 0, VAL_NOT_REQ, 1, NEG, VAL_STR, 0}, { "MACHINE_CODE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0}, { "NAMEOFRTN", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0}, { "OBJECT", 0, 0, 0, 0, 0, 0, VAL_NOT_REQ, 1, NEG, VAL_STR, 0}, { "RUN", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0}, { "SPACE", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0}, { "WARNINGS", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0}, { "" } }; GBLDEF CLI_ENTRY mumps_cmd_ary[] = { { "MUMPS", 0, mumps_qual, mumps_parm, 0, 0, 0, VAL_DISALLOWED, 1, 0, VAL_LIST, 0}, { "" } }; fis-gtm-V7.0-005/sr_unix/mup_bak_sys.c0000755000032200000250000000127714342376330016533 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "mupipbckup.h" GBLDEF bool mubtomag=FALSE; void mup_bak_mag(void) { return; } void mup_bak_pause(void) { return; } fis-gtm-V7.0-005/sr_unix/mupip.c0000644000032200000250000000764014342376330015346 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "main_pragma.h" #include "gtm_inet.h" #include "gtm_signal.h" #include "mlkdef.h" #include "gtm_stdlib.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "iosp.h" #include "error.h" #include "interlock.h" #include "gtmimagename.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdskill.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" #include "tp.h" #include "repl_msg.h" #include "gtmsource.h" #include "stp_parms.h" #include "cli.h" #include "io.h" #include "mupip_exit.h" #include "patcode.h" #include "lke.h" #include "gtm_startup_chk.h" #include "generic_signal_handler.h" #include "mu_op_open.h" #include "cli_parse.h" #include "getzdir.h" #include "mu_term_setup.h" #include "sig_init.h" #include "gtmmsg.h" #include "suspsigs_handler.h" #include "startup.h" #include "gtm_startup.h" #include "invocation_mode.h" #include "common_startup_init.h" #include "gtm_threadgbl_init.h" #include "continue_handler.h" #include "gtmio.h" #include "restrict.h" #include "dm_audit_log.h" #ifdef UTF8_SUPPORTED # include "gtm_icu_api.h" # include "gtm_utf8.h" # include "gtm_conv.h" GBLREF u_casemap_t gtm_strToTitle_ptr; /* Function pointer for gtm_strToTitle */ #endif GBLREF int (*op_open_ptr)(mval *v, mval *p, mval *t, mval *mspace); GBLREF bool in_backup; GBLREF bool licensed; GBLREF int (*func)(); GBLREF char cli_err_str[]; GBLREF CLI_ENTRY mupip_cmd_ary[]; GBLREF void (*mupip_exit_fp)(int errcode); GBLREF void (*primary_exit_handler)(void); GBLDEF CLI_ENTRY *cmd_ary = &mupip_cmd_ary[0]; /* Define cmd_ary to be the MUPIP specific cmd table */ GBLREF IN_PARMS *cli_lex_in_ptr; error_def(ERR_MUPCLIERR); error_def(ERR_MUNOACTION); void display_prompt(void); int main (int argc, char **argv) { int i, lenargv, res, status; char *cmdletter; DCL_THREADGBL_ACCESS; GTM_THREADGBL_INIT; if (1 < argc) { cmdletter = argv[1]; lenargv = STRLEN(argv[1]); for (i = 0; i < lenargv; i++) { if (!IS_ASCII(*cmdletter)) mupip_exit(ERR_MUPCLIERR); // return an error if the first argument is not ascii cmdletter++; } } common_startup_init(MUPIP_IMAGE); invocation_mode = MUMPS_UTILTRIGR; err_init(util_base_ch); UTF8_ONLY(gtm_strToTitle_ptr = >m_strToTitle); sig_init(generic_signal_handler, NULL, suspsigs_handler, continue_handler); /* Note: no ^C handler is defined (yet) */ atexit(mupip_exit_handler); primary_exit_handler = mupip_exit_handler; licensed = TRUE; in_backup = FALSE; op_open_ptr = mu_op_open; INIT_FNPTR_GLOBAL_VARIABLES; mu_get_term_characterstics(); gtm_chk_dist(argv[0]); init_gtm(); cli_lex_setup(argc,argv); if (argc < 2) display_prompt(); /* Interactive mode */ mupip_exit_fp = mupip_exit; /* Initialize function pointer for use during MUPIP */ while (TRUE) { func = 0; res = parse_cmd(); if (EOF == res) break; status = log_cmd_if_needed(cli_lex_in_ptr->in_str); if (status) mupip_exit(ERR_MUNOACTION); if (res) { if (1 < argc) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) res, 2, LEN_AND_STR(cli_err_str)); else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) res, 2, LEN_AND_STR(cli_err_str)); } if (func) func(); if (argc > 1) /* Non-interactive mode, exit after command */ break; display_prompt(); } mupip_exit(SS_NORMAL); return 0; } void display_prompt(void) { PRINTF("MUPIP> "); FFLUSH(stdout); } fis-gtm-V7.0-005/sr_unix/mupip_cmd.c0000644000032200000250000015257714342376330016203 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "repl_msg.h" #include "cli.h" #include "mupip_cmd_disallow.h" #include "iob.h" /* needed for mupip_restore.h */ /********************************************************************* * Parameters must be defined in the order they are to be specified * AND must be alphabetical * * This file might have lines longer than 132 characters * since the command tables are being initialized. * * Ensure all the command lines (statements of the form: *CLI_ENTRY.*_qual[]), * Contain a comment on the same line indicating the exact MUPIP command it represents. * The test framework uses this to validate input for the values. *********************************************************************/ #include "mupip_backup.h" #include "mupip_create.h" #include "mupip_cvtgbl.h" #include "mupip_cvtpgm.h" #include "mupip_downgrade.h" #include "mupip_extend.h" #include "muextr.h" #include "mupip_freeze.h" #include "util_help.h" #include "mupip_integ.h" #include "mupip_intrpt.h" #include "mupip_quit.h" #include "mupip_recover.h" #include "mupip_reorg.h" #include "mupip_restore.h" #include "mupip_rundown.h" #include "mupip_set.h" #include "mupip_size.h" #include "mupip_stop.h" #include "mupip_trigger.h" #include "mupip_upgrade.h" #include "mupip_ftok.h" #include "mupip_sems.h" #include "mupip_endiancvt.h" #include "mupip_crypt.h" #include "mupip_hash.h" #include "mupip_dump_fhead.h" #include "gtmsource.h" #include "gtmrecv.h" #include "read_db_files_from_gld.h" /* Needed for updproc.h */ #include "updproc.h" #include "repl_instance.h" #include "db_snapshot.h" #include "mupip_rctldump.h" #include "mu_rndwn_rlnkctl.h" static CLI_ENTRY mup_set_journal_qual[] = { /* SET -JOURNAL */ { "ALIGNSIZE", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "ALLOCATION", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "AUTOSWITCHLIMIT", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "BEFORE_IMAGES", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "BUFFER_SIZE", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "DISABLE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "ENABLE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "EPOCH_INTERVAL", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "EXTENSION", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "FILENAME", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "OFF", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "ON", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "SYNC_IO", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "YIELD_LIMIT", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "" } }; static CLI_ENTRY mub_since_qual[] = { /* BACKUP -SINCE */ { "BYTESTREAM", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "COMPREHENSIVE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "DATABASE", 0, 0, 0, 0, 0, DEFA_PRESENT, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "INCREMENTAL", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "RECORD", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "" } }; static CLI_ENTRY mup_set_acc_qual[] = { /* SET -ACC_METH */ { "BG", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "MM", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "" } }; static CLI_ENTRY mup_set_dbver_qual[] = { /* SET -DBVER */ { "V4", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "V6", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "" } }; static CLI_ENTRY mup_crypt_type_qual[] = { /* CRYPT -TYPE */ { "DB_IV", 0, 0, 0, 0, 0, DEFA_PRESENT, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "DB_NO_IV", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "JNL_LOG_IV", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "JNL_NONLOG_IV", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "JNL_LOG_NO_IV", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "JNL_NONLOG_NO_IV", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "" } }; static CLI_ENTRY mup_downgrade_dbver_qual[] = { /* DOWNGRADE -DBVER */ { "V4", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "V5", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "V63000A", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "" } }; static CLI_PARM mup_dumpfhead_parm[] = { { "WHAT", "File or Region: ", PARM_REQ}, { "", "", PARM_REQ} }; static CLI_ENTRY mup_dumpfhead_qual[] = { /* DUMP -FHEAD */ { "FILE", mupip_dump_fhead, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "FLUSH", mupip_dump_fhead, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "REGION", mupip_dump_fhead, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "" } }; static CLI_ENTRY mup_extract_format_qual[] = { /* EXTRACT -FORMAT */ { "BINARY", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "GO", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "ZWR", 0, 0, 0, 0, 0, DEFA_PRESENT, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "" } }; static CLI_ENTRY mup_convert_format_qual[] = { /* CONVER -FORMAT */ { "RO", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "" } }; static CLI_ENTRY mup_jnl_show_qual[] = { /* JOURNAL -SHOW */ { "ACTIVE_PROCESSES", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "ALL", 0, 0, 0, 0, 0, DEFA_PRESENT, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "BROKEN_TRANSACTIONS", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "HEADER", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "PROCESSES", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "STATISTICS", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "" } }; static CLI_ENTRY mup_load_fmt_qual[] = { /* LOAD -FORMAT */ { "BINARY", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "GO", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "ZWR", 0, 0, 0, 0, 0, DEFA_PRESENT, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "" } }; static CLI_ENTRY mup_load_onerror_qual[] = { /* LOAD -ONERR */ { "INTERACTIVE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "PROCEED", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "STOP", 0, 0, 0, 0, 0, DEFA_PRESENT, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "" } }; static CLI_ENTRY mup_jnl_fences_qual[] = { /* JOURNAL -FENCES */ { "ALWAYS", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "NONE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "PROCESS", 0, 0, 0, 0, 0, DEFA_PRESENT, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "" } }; static CLI_ENTRY mup_null_subs_qual[] = { /* NULLSUBS */ { "ALWAYS", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_STR, 0 }, { "EXISTING", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_STR, 0 }, { "FALSE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_STR, 0 }, { "NEVER", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_STR, 0 }, { "TRUE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_STR, 0 }, { "" } }; static CLI_ENTRY mup_repl_qual[] = { /* REPLIC */ { "OFF", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "ON", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "" } }; static CLI_PARM mup_backup_parm[] = { { "REG_NAME", "Region: ", PARM_REQ}, { "SAVE_DIR", "Backup Directory: ", PARM_REQ}, { "", "" } }; static CLI_ENTRY mub_njnl_val_qual[] = { /* BACKUP -NJNL */ { "PREVLINK", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "SYNC_IO", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "" } }; static CLI_ENTRY mub_dbjnl_val_qual[] = { /* BACKUP -DBJNL */ { "DISABLE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "OFF", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "" } }; static readonly CLI_PARM mub_njnl_parm[] = { { "NEWJNLFILES", "PREVLINK", PARM_REQ}, { "", "" } }; static CLI_ENTRY mup_backup_qual[] = { /* BACKUP */ { "BKUPDBJNL", mupip_backup, 0, 0, mub_dbjnl_val_qual, 0, 0, VAL_REQ, 2, NON_NEG, VAL_STR, 0 }, { "BYTESTREAM", mupip_backup, 0, 0, 0, 0, 0, VAL_DISALLOWED, 2, NON_NEG, VAL_N_A, 0 }, { "COMPREHENSIVE", mupip_backup, 0, 0, 0, 0, 0, VAL_DISALLOWED, 2, NON_NEG, VAL_N_A, 0 }, { "DATABASE", mupip_backup, 0, 0, 0, 0, 0, VAL_DISALLOWED, 2, NON_NEG, VAL_N_A, 0 }, { "DBG", mupip_backup, 0, 0, 0, 0, 0, VAL_DISALLOWED, 2, NON_NEG, VAL_N_A, 0 }, { "INCREMENTAL", mupip_backup, 0, 0, 0, 0, 0, VAL_DISALLOWED, 2, NON_NEG, VAL_N_A, 0 }, { "JOURNAL", mupip_backup, 0, 0, 0, 0, 0, VAL_REQ, 2, NEG, VAL_STR, 0 }, { "NETTIMEOUT", mupip_backup, 0, 0, 0, 0, 0, VAL_REQ, 2, NON_NEG, VAL_NUM, 0 }, { "NEWJNLFILES", mupip_backup, 0, mub_njnl_parm, mub_njnl_val_qual, 0, 0, VAL_NOT_REQ, 2, NEG, VAL_STR, 0 }, { "ONLINE", mupip_backup, 0, 0, 0, 0, 0, VAL_DISALLOWED, 2, NEG, VAL_N_A, 0 }, { "RECORD", mupip_backup, 0, 0, 0, 0, 0, VAL_DISALLOWED, 2, NON_NEG, VAL_N_A, 0 }, { "REPLACE", mupip_backup, 0, 0, 0, 0, 0, VAL_DISALLOWED, 2, NON_NEG, VAL_N_A, 0 }, { "REPLICATION", mupip_backup, 0, 0, mup_repl_qual, 0, 0, VAL_REQ, 1, NEG, VAL_STR, 0 }, { "REPLINSTANCE", mupip_backup, 0, 0, 0, 0, 0, VAL_REQ, 2, NON_NEG, VAL_STR, 0 }, { "RETRY", mupip_backup, 0, 0, 0, 0, 0, VAL_REQ, 2, NON_NEG, VAL_NUM, 0 }, { "SHOWPROGRESS", mupip_backup, 0, 0, 0, 0, 0, VAL_DISALLOWED, 2, NEG, VAL_N_A, 0 }, { "SINCE", mupip_backup, 0, 0, mub_since_qual, 0, 0, VAL_REQ, 2, NON_NEG, VAL_STR, 0 }, { "TRANSACTION", mupip_backup, 0, 0, 0, 0, 0, VAL_REQ, 2, NON_NEG, VAL_NUM, VAL_HEX }, { "" } }; static CLI_PARM mup_convert_parm[] = { { "FILE", "Input File: ", PARM_REQ}, { "", "", PARM_REQ} }; static CLI_ENTRY mup_convert_qual[] = { /* CONVERT */ { "FORMAT", mupip_cvtpgm, 0, 0, mup_convert_format_qual, 0, 0, VAL_REQ, 2, NON_NEG, VAL_STR, 0 }, { "" } }; static CLI_ENTRY mup_create_qual[] = { /* CREATE */ { "REGION", mupip_create, 0, 0, 0, 0, 0, VAL_REQ, 2, NON_NEG, VAL_STR, 0 }, { "V6", mupip_create, 0, 0, 0, 0, 0, VAL_DISALLOWED, 2, NEG, VAL_N_A, 0 }, { "" } }; static readonly CLI_PARM mup_endian_parm[] = { { "DATABASE", "Database: ", PARM_REQ}, { "", "", PARM_REQ} }; static readonly CLI_ENTRY mup_endian_qual[] = { /* ENDIAN */ { "OUTDB", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "OVERRIDE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "" } }; static CLI_PARM mup_extend_parm[] = { { "REG_NAME", "Region: ", PARM_REQ}, { "", "", PARM_REQ} }; static CLI_ENTRY mup_extend_qual[] = { /* EXTEND */ { "BLOCKS", mupip_extend, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "" } }; static CLI_PARM mup_extract_parm[] = { { "FILE", "Output File: ", PARM_REQ}, { "REGION", "Region(s): " , PARM_REQ}, { "", "", PARM_REQ} }; static readonly CLI_PARM mup_extr_label_parm[] = { { "LABEL", "GT.M MUPIP EXTRACT", PARM_REQ}, { "", "" } }; static CLI_ENTRY mup_extract_qual[] = { /* EXTRACT */ { "FORMAT", mu_extract, 0, 0, mup_extract_format_qual, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "FREEZE", mu_extract, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "LABEL", mu_extract, 0, mup_extr_label_parm, 0, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_STR, 0 }, { "LOG", mu_extract, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "NULL_IV", mu_extract, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "OCHSET", mu_extract, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "OVERRIDE", mu_extract, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "REGION", mu_extract, 0, 0, 0, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_N_A, 0 }, { "SELECT", mu_extract, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "STDOUT", mu_extract, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "" } }; static CLI_PARM mup_freeze_parm[] = { { "REG_NAME", "Region: ", PARM_REQ}, { "", "", PARM_REQ} }; static CLI_ENTRY mup_freeze_qual[] = { /* FREEZE */ { "AUTORELEASE", mupip_freeze, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "DBG", mupip_freeze, 0, 0, 0, 0, 0, VAL_DISALLOWED, 2, NON_NEG, VAL_N_A, 0 }, { "OFF", mupip_freeze, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "ON", mupip_freeze, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "ONLINE", mupip_freeze, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "OVERRIDE", mupip_freeze, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "RECORD", mupip_freeze, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "" } }; static CLI_PARM mup_ftok_parm[] = { { "FILE", "File: ", PARM_REQ}, { "", "",} }; static CLI_ENTRY mup_ftok_qual[] = { /* FTOK */ { "DB", mupip_ftok, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "HEADER", mupip_ftok, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "ID", mupip_ftok, 0, 0, 0, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "JNLPOOL", mupip_ftok, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "ONLY", mupip_ftok, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "RECVPOOL", mupip_ftok, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "" } }; static CLI_PARM mup_integ_parm[] = { { "WHAT", "File or Region: ", PARM_REQ}, { "", "", PARM_REQ} }; static readonly CLI_PARM mup_integ_map_parm[] = { { "MAP", "10", PARM_REQ}, { "", "" } }; static CLI_ENTRY mup_integ_qual[] = { /* INTEG */ { "ADJACENCY", mupip_integ, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "ANALYZE", mupip_integ, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "BLOCK", mupip_integ, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, VAL_HEX }, { "BRIEF", mupip_integ, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "DBG", mupip_integ, 0, 0, 0, 0, 0, VAL_DISALLOWED, 2, NON_NEG, VAL_N_A, 0 }, { "FAST", mupip_integ, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "FILE", mupip_integ, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "FULL", mupip_integ, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "KEYRANGES", mupip_integ, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "MAP", mupip_integ, 0, mup_integ_map_parm, 0, 0, 0, VAL_NOT_REQ, 1, NEG, VAL_NUM, 0 }, { "MAXKEYSIZE", mupip_integ, 0, mup_integ_map_parm, 0, 0, 0, VAL_NOT_REQ, 1, NEG, VAL_NUM, 0 }, { "ONLINE", mupip_integ, 0, 0, 0, 0, 0, VAL_DISALLOWED, 2, NEG, VAL_N_A, 0 }, { "PRESERVE", mupip_integ, 0, 0, 0, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_N_A, 0 }, { "REGION", mupip_integ, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "STATS", mupip_integ, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "SUBSCRIPT", mupip_integ, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "TN_RESET", mupip_integ, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "TRANSACTION", mupip_integ, 0, mup_integ_map_parm, 0, 0, 0, VAL_NOT_REQ, 1, NEG, VAL_NUM, 0 }, { "" } }; static CLI_PARM mup_intrpt_parm[] = { { "ID", "ID: ", PARM_REQ}, { "", "", PARM_REQ} }; static CLI_PARM mup_journal_parm[] = { { "FILE", "Journal File(s): ", PARM_REQ}, { "", "", PARM_REQ} }; static readonly CLI_PARM mup_jnl_errlimit_parm[] = { { "ERROR_LIMIT", "", PARM_REQ}, { "", "" } }; static readonly CLI_PARM mup_jnl_lost_fn[] = { { "LOSTTRANS", "losttrans.mlt", PARM_REQ}, { "", "" } }; static readonly CLI_PARM mup_jnl_lookback_parm[] = { { "LOOKBACK_LIMIT", "TIME=0 00:05", PARM_REQ}, { "", "" } }; static readonly CLI_PARM mup_jnl_fences_parm[] = { { "FENCES", "PROCESS", PARM_REQ}, { "", "" } }; static CLI_ENTRY mur_jnl_lookback_qual[] = { /* JOURNAL -LOOKBACK */ { "OPERATIONS", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, 0 }, { "TIME", 0, 0, 0, 0, 0, DEFA_PRESENT, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "" } }; static CLI_ENTRY mup_trans_qual[] = { /* TRANS */ { "KILL", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "SET", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "" } }; static readonly CLI_PARM mup_jnl_show_parm[] = { { "SHOW", "ALL", PARM_REQ}, { "", "" } }; static CLI_ENTRY mup_journal_qual[] = { /* JOURNAL */ { "AFTER", mupip_recover, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "APPLY_AFTER_IMAGE", mupip_recover, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "BACKWARD", mupip_recover, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "BEFORE", mupip_recover, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, #ifdef DEBUG { "BLOCKID", mupip_recover, 0, 0, 0, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_STR, 0 }, #endif { "BROKENTRANS", mupip_recover, 0, 0, 0, 0, 0, VAL_REQ, 1, NEG, VAL_STR, 0 }, { "CHAIN", mupip_recover, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "CHECKTN", mupip_recover, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "CORRUPTDB", mupip_recover, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "DETAIL", mupip_recover, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "ERROR_LIMIT", mupip_recover, 0, mup_jnl_errlimit_parm, 0, 0, 0, VAL_NOT_REQ, 1, NEG, VAL_NUM, 0 }, { "EXTRACT", mupip_recover, 0, 0, 0, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_STR, 0 }, { "FENCES", mupip_recover, 0, mup_jnl_fences_parm, mup_jnl_fences_qual, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_STR, 0 }, { "FETCHRESYNC", mupip_recover, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "FORWARD", mupip_recover, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "FULL", mupip_recover, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "GLOBAL", mupip_recover, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "GVPATFILE", mupip_recover, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "ID", mupip_recover, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "INTERACTIVE", mupip_recover, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "LOOKBACK_LIMIT", mupip_recover, 0, mup_jnl_lookback_parm, mur_jnl_lookback_qual, 0, 0, VAL_NOT_REQ, 1, NEG, VAL_STR, 0 }, { "LOSTTRANS", mupip_recover, 0, mup_jnl_lost_fn, 0, 0, 0, VAL_REQ, 1, NEG, VAL_STR, 0 }, { "ONLINE", mupip_recover, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "PARALLEL", mupip_recover, 0, 0, 0, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "RECOVER", mupip_recover, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "REDIRECT", mupip_recover, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "RESYNC", mupip_recover, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "ROLLBACK", mupip_recover, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "RSYNC_STRM", mupip_recover, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "SEQNO", mupip_recover, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "SHOW", mupip_recover, 0, mup_jnl_show_parm, mup_jnl_show_qual, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_STR, 0 }, { "SINCE", mupip_recover, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "TRANSACTION", mupip_recover, 0, 0, mup_trans_qual, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "USER", mupip_recover, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "VERBOSE", mupip_recover, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "VERIFY", mupip_recover, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "" } }; static CLI_PARM mup_load_parm[] = { { "FILE", "Input File: ", PARM_NOT_REQ}, { "", "", PARM_REQ} }; static readonly CLI_PARM mup_load_ff_parm[] = { { "FILL_FACTOR", "100", PARM_REQ}, { "", "" } }; static readonly CLI_PARM mup_load_fmt_parm[] = { { "FORMAT", "GO", PARM_REQ}, { "", "" } }; static readonly CLI_PARM mup_load_onerror_parm[] = { { "ONERROR", "PROCEED", PARM_REQ}, { "", "" } }; static CLI_ENTRY mup_load_qual[] = { /* LOAD */ { "BEGIN", mupip_cvtgbl, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "BLOCK_DENSITY", mupip_cvtgbl, 0, 0, 0, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "END", mupip_cvtgbl, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "FILL_FACTOR", mupip_cvtgbl, 0, mup_load_ff_parm, 0, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "FORMAT", mupip_cvtgbl, 0, mup_load_fmt_parm, mup_load_fmt_qual, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_STR, 0 }, { "ONERROR", mupip_cvtgbl, 0, mup_load_onerror_parm, mup_load_onerror_qual, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_STR, 0 }, { "STDIN", mupip_cvtgbl, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "" } }; static readonly CLI_PARM mup_reorg_ff_parm[] = { { "FILL_FACTOR", "100", PARM_REQ}, { "", "" } }; /* USER_DEFINED_REORG is currently undocumented */ static CLI_ENTRY mup_reorg_qual[] = { /* REORG */ { "DOWNGRADE", mupip_reorg, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "ENCRYPT", mupip_reorg, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "EXCLUDE", mupip_reorg, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "FILL_FACTOR", mupip_reorg, 0, mup_reorg_ff_parm, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "INDEX_FILL_FACTOR", mupip_reorg, 0, mup_reorg_ff_parm, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "KEEP", mupip_reorg, 0, 0, 0, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_STR, 0 }, { "NOCOALESCE", mupip_reorg, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "NOSPLIT", mupip_reorg, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "NOSWAP", mupip_reorg, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "REGION", mupip_reorg, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "RESUME", mupip_reorg, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "SAFEJNL", mupip_reorg, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "SELECT", mupip_reorg, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "STARTBLK", mupip_reorg, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, VAL_HEX }, { "STOPBLK", mupip_reorg, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, VAL_HEX }, { "TRUNCATE", mupip_reorg, 0, 0, 0, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "UPGRADE", mupip_reorg, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "USER_DEFINED_REORG", mupip_reorg, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "" } }; /* * MUPIP SIZE */ static CLI_ENTRY mup_size_heuristic_qual[] = { /* SIZE -HEURISTIC */ { "ARSAMPLE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "IMPSAMPLE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "LEVEL", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, /* VAL_STR to be able to get negative values */ { "SAMPLES", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, VAL_DCM}, { "SCAN", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "SEED", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, VAL_DCM}, { "" } }; static CLI_ENTRY mup_size_qual[] = { /* SIZE */ { "ADJACENCY", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "HEURISTIC", 0, 0, 0, mup_size_heuristic_qual, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "REGION", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "SELECT", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "SUBSCRIPT", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "" } }; static readonly CLI_PARM gtmsource_timeout_parm[] = { {"TIMEOUT", "30", PARM_REQ}, {"", "", PARM_REQ} }; static readonly CLI_PARM gtmrecv_helpers_parm[] = { {"HELPERS", DEFAULT_UPD_HELPERS_STR, PARM_REQ}, {"", "", PARM_REQ} }; static CLI_ENTRY inst_edit_qual[] = { /* INSTEDIT */ {"CHANGE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_STR, 0 }, {"CLEANSLOTS", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"DETAIL", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, {"NAME", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"OFFSET", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, VAL_HEX }, {"QDBRUNDOWN", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, {"SHOW", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_STR, 0 }, {"SIZE", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, VAL_HEX }, {"VALUE", 0, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, VAL_HEX }, { "" } }; static CLI_ENTRY inst_freeze_qual[] = { /* instance -freeze */ {"COMMENT", 0, 0, 0, 0, 0, 0, VAL_NOT_REQ, 0, NEG, VAL_STR, 0 }, { "" } }; static CLI_ENTRY gtmsource_qual[] = { /* REPLIC -SOURCE */ {"ACTIVATE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"BUFFSIZE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, 0 }, {"CHANGELOG", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"CHECKHEALTH", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"CMPLVL", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, 0 }, {"CONNECTPARAMS", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"DEACTIVATE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"DETAIL", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"FILTER", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"FREEZE", 0, inst_freeze_qual, 0, 0, 0, 0, VAL_NOT_REQ, 0, NON_NEG, VAL_STR, 0 }, {"INSTSECONDARY", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"JNLFILEONLY", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, {"JNLPOOL", 0, inst_edit_qual, 0, 0, cli_disallow_mupip_replic_editinst, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"LOG", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"LOG_INTERVAL", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, 0 }, {"LOSTTNCOMPLETE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"NEEDRESTART", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"PASSIVE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"PLAINTEXTFALLBACK", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, {"PROPAGATEPRIMARY", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"RENEGOTIATE_INTERVAL", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"ROOTPRIMARY", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"SECONDARY", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"SHOWBACKLOG", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"SHUTDOWN", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"START", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"STATSLOG", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"STOPSOURCEFILTER", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"TIMEOUT", 0, 0, gtmsource_timeout_parm, 0, 0, 0, VAL_NOT_REQ, 1, NEG, VAL_NUM, 0 }, {"TLSID", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"UPDNOTOK", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"UPDOK", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"ZEROBACKLOG", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "" } }; static readonly CLI_PARM gtmrecv_timeout_parm[] = { {"TIMEOUT", "30", PARM_REQ}, {"", "", PARM_REQ} }; static CLI_ENTRY gtmrecv_autorlbk_qual[] = { /* REPLIC -RECEIVE -AUTOROLLBACK */ { "VERBOSE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "" } }; static CLI_ENTRY gtmrecv_qual[] = { /* REPLIC -RECEIVE */ {"AUTOROLLBACK", 0, 0, 0, gtmrecv_autorlbk_qual, 0, 0, VAL_NOT_REQ, 1, NEG, VAL_STR, 0 }, {"BUFFSIZE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, 0 }, {"CHANGELOG", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"CHECKHEALTH", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"CMPLVL", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, 0 }, {"FILTER", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"HELPERS", 0, 0, gtmrecv_helpers_parm, 0, 0, 0, VAL_NOT_REQ, 0, NON_NEG, VAL_STR, 0 }, {"INITIALIZE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"LISTENPORT", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, 0 }, {"LOG", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"LOG_INTERVAL", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"NORESYNC", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"PLAINTEXTFALLBACK", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, {"RESUME", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_NUM, 0 }, {"REUSE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"SHOWBACKLOG", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"SHUTDOWN", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"START", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"STATSLOG", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"STOPRECEIVERFILTER", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"STOPSOURCEFILTER", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"TIMEOUT", 0, 0, gtmrecv_timeout_parm, 0, 0, 0, VAL_NOT_REQ, 1, NEG, VAL_NUM, 0 }, {"TLSID", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"UPDATEONLY", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"UPDATERESYNC", 0, 0, 0, 0, 0, 0, VAL_NOT_REQ, 0, NON_NEG, VAL_STR, 0 }, { "" } }; static CLI_ENTRY updproc_qual[] = { /* UPDPROC */ { "" } }; static CLI_ENTRY inst_cre_qual[] = { /* -INSTANCE_CREATE */ {"NAME", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, {"NOREPLACE", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, {"QDBRUNDOWN", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, {"SUPPLEMENTARY", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "" } }; static CLI_PARM mup_reorg_parm[] = { { "REG_NAME", "Region: ", PARM_REQ}, { "", "", PARM_REQ} }; static CLI_ENTRY updhelp_qual[] = { /* UPDATE_HELPER */ { "READER", (void(*)(void))updhelper_reader, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "WRITER", (void(*)(void))updhelper_writer, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "" } }; static CLI_PARM mup_replicate_parm[] = { { "INSTFILE", "Instance File Name: ", PARM_REQ}, { "WHAT", "Source or Receiver: ", PARM_REQ}, { "", "" , PARM_REQ} }; static CLI_ENTRY mup_replicate_qual[] = { /* REPLICATE */ { "EDITINSTANCE", repl_inst_edit, inst_edit_qual, 0, 0, cli_disallow_mupip_replic_editinst, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "INSTANCE_CREATE", repl_inst_create, inst_cre_qual, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "RECEIVER", (void(*)(void))gtmrecv, gtmrecv_qual, 0, 0, cli_disallow_mupip_replic_receive, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "SOURCE", (void(*)(void))gtmsource, gtmsource_qual, 0, 0, cli_disallow_mupip_replic_source, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "UPDATEPROC", (void(*)(void))updproc, updproc_qual, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "UPDHELPER", 0, updhelp_qual, 0, 0, cli_disallow_mupip_replic_updhelper, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "" } }; static CLI_PARM mup_restore_parm[] = { { "DATABASE", "Database: ", PARM_REQ}, { "INPUT_FILE", "Input File(s): ", PARM_REQ}, { "", "" , PARM_REQ} }; static CLI_ENTRY mup_restore_qual[] = { /* RESTORE */ { "EXTEND", mupip_restore, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "NETTIMEOUT", mupip_restore, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "" } }; static CLI_PARM mup_rundown_parm[] = { { "WHAT", "File or Region: ", PARM_REQ}, { "", "", PARM_REQ} }; /* Note: "R" is a duplicate of "REGION". It is there so -R usages continue to be treated as -REGION and not have an * ambiguity with "RELINKCTL". */ static CLI_ENTRY mup_rundown_qual[] = { /* RUNDOWN */ { "CLEANJNL", mupip_rundown, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, /* DBG-only qualifier */ { "FILE", mupip_rundown, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "OVERRIDE", mupip_rundown, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "R", mupip_rundown, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "REGION", mupip_rundown, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "RELINKCTL", mu_rndwn_rlnkctl, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "" } }; static CLI_PARM mup_sems_parm[] = { { "SEMAPHORE", "Semaphore: ", PARM_REQ}, { "", "",} }; static CLI_PARM mup_set_parm[] = { { "WHAT", "File or Region: ", PARM_REQ}, { "", "", PARM_REQ} }; static readonly CLI_PARM mup_set_ftime_parm[] = { { "FLUSH_TIME", "100", PARM_REQ}, { "", "" } }; static CLI_ENTRY mup_set_qual[] = { /* SET */ { "ACCESS_METHOD", mupip_set, 0, 0, mup_set_acc_qual, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "ASYNCIO", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "BYPASS", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "DBFILENAME", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NEG, VAL_STR, 0 }, { "DEFER_ALLOCATE", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "DEFER_TIME", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NEG, VAL_STR, 0 }, { "ENCRYPTABLE", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "ENCRYPTIONCOMPLETE", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "EPOCHTAPER", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "EXTENSION_COUNT", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "FILE", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "FLUSH_TIME", mupip_set, 0, mup_set_ftime_parm, 0, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_TIME, 0 }, { "FULLBLKWRT", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "GLOBAL_BUFFERS", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "HARD_SPIN_COUNT", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, VAL_DCM }, { "INST_FREEZE_ON_ERROR", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "JNLFILE", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "JOURNAL", mupip_set, 0, 0, mup_set_journal_qual, 0, 0, VAL_NOT_REQ, 1, NEG, VAL_STR, 0 }, { "KEY_SIZE", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "LCK_SHARES_DB_CRIT", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "LOCK_SPACE", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "MUTEX_SLOTS", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "NULL_SUBSCRIPTS", mupip_set, 0, 0, mup_null_subs_qual, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "PARTIAL_RECOV_BYPASS", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "PREVJNLFILE", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NEG, VAL_STR, 0 }, { "PROBLKSPLIT", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "QDBRUNDOWN", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "READ_ONLY", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "RECORD_SIZE", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "REGION", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "REPLICATION", mupip_set, 0, 0, mup_repl_qual, 0, 0, VAL_REQ, 1, NEG, VAL_STR, 0 }, { "REPL_STATE", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NEG, VAL_STR, 0 }, { "RESERVED_BYTES", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "SLEEP_SPIN_COUNT", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "SPIN_SLEEP_MASK", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, VAL_HEX }, { "STANDALONENOT", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "STATS", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "STATSDB_ALLOCATION", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "STDNULLCOLL", mupip_set, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NEG, VAL_N_A, 0 }, { "TRIGGER_FLUSH_LIMIT", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "VERSION", mupip_set, 0, 0, mup_set_dbver_qual, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "WAIT_DISK", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "WRITES_PER_FLUSH", mupip_set, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "" } }; static CLI_ENTRY mup_crypt_qual[] = { /* CRYPT */ { "DECRYPT", mupip_crypt, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0 }, { "FILE", mupip_crypt, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "LENGTH", mupip_crypt, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "OFFSET", mupip_crypt, 0, 0, 0, 0, 0, VAL_REQ, 1, NON_NEG, VAL_NUM, 0 }, { "TYPE", mupip_crypt, 0, 0, mup_crypt_type_qual, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "" } }; #ifdef GTM_TRIGGER static CLI_PARM mup_trig_parm[] = { { "FILE", "Output File: ", PARM_REQ}, { "", "", PARM_REQ} }; static readonly CLI_PARM mup_trig_sel_parm[] = { { "SELECT", "*", PARM_REQ}, { "", "" } }; static CLI_ENTRY mup_trigger_qual[] = { /* TRIGGER */ { "NOPROMPT", mupip_trigger, 0, 0, 0, 0, 0, VAL_NOT_REQ, 0, NON_NEG, VAL_STR, 0 }, { "SELECT", mupip_trigger, 0, mup_trig_sel_parm, 0, 0, 0, VAL_NOT_REQ, 1, NON_NEG, VAL_STR, 0 }, { "TRIGGERFILE", mupip_trigger, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "UPGRADE", mupip_trigger, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "" } }; #endif static CLI_PARM mup_stop_parm[] = { { "ID", "ID: ", PARM_REQ}, { "", "", PARM_REQ} }; static CLI_ENTRY mup_upgrade_qual[] = { /* UPGRADE */ { "MASTERMAP", mupip_upgrade, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0}, { "REGION", mupip_upgrade, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, NON_NEG, VAL_N_A, 0}, { "" } }; /* TODO: revisit file vs region vs choice? 1 function or two? names? */ static CLI_PARM mup_upgrade_parm[] = { { "REGION", "Region: ", PARM_REQ}, { "", "", PARM_REQ} }; static CLI_PARM mup_downgrade_parm[] = { { "FILE", "File: ", PARM_REQ}, { "", "", PARM_REQ} }; static CLI_ENTRY mup_downgrade_qual[] = { /* DOWNGRADE */ { "VERSION", mupip_downgrade, 0, 0, mup_downgrade_dbver_qual, 0, 0, VAL_REQ, 1, NON_NEG, VAL_STR, 0 }, { "" } }; static CLI_PARM mup_rctldump_parm[] = { { "DIRECTORY", "Directory: ", PARM_REQ}, { "", "", PARM_REQ} }; GBLDEF CLI_ENTRY mupip_cmd_ary[] = { { "BACKUP", mupip_backup, mup_backup_qual, mup_backup_parm, 0, cli_disallow_mupip_backup, 0, VAL_DISALLOWED, 2, 0, 0, 0 }, { "CONVERT", mupip_cvtpgm, mup_convert_qual, mup_convert_parm, 0, 0, 0, VAL_DISALLOWED, 2, 0, 0, 0 }, { "CREATE", mupip_create, mup_create_qual, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "CRYPT", mupip_crypt, mup_crypt_qual, 0, 0, cli_disallow_mupip_crypt, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "DOWNGRADE", mupip_downgrade, mup_downgrade_qual, mup_downgrade_parm, 0, 0, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "DUMPFHEAD", mupip_dump_fhead, mup_dumpfhead_qual, mup_dumpfhead_parm, 0, cli_disallow_mupip_dumpfhead, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "ENDIANCVT", mupip_endiancvt, mup_endian_qual, mup_endian_parm, 0, 0, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "EXIT", mupip_quit, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "EXTEND", mupip_extend, mup_extend_qual, mup_extend_parm, 0, 0, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "EXTRACT", mu_extract, mup_extract_qual, mup_extract_parm, 0, cli_disallow_mupip_extract, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "FREEZE", mupip_freeze, mup_freeze_qual, mup_freeze_parm, 0, cli_disallow_mupip_freeze, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "FTOK", mupip_ftok, mup_ftok_qual, mup_ftok_parm, 0, 0, 0, VAL_DISALLOWED, MAX_PARMS, 0, 0, 0 }, { "HASH", mupip_hash, 0, 0, 0, 0, 0, VAL_DISALLOWED, MAX_PARMS, 0, 0, 0 }, { "HELP", util_help, 0, 0, 0, 0, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "INTEG", mupip_integ, mup_integ_qual, mup_integ_parm, 0, cli_disallow_mupip_integ, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "INTRPT", mupip_intrpt, 0, mup_intrpt_parm, 0, 0, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "JOURNAL", mupip_recover, mup_journal_qual, mup_journal_parm, 0, cli_disallow_mupip_journal, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "LOAD", mupip_cvtgbl, mup_load_qual, mup_load_parm, 0, 0, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "QUIT", mupip_quit, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "RCTLDUMP", mupip_rctldump, 0, mup_rctldump_parm, 0, 0, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "REORG", mupip_reorg, mup_reorg_qual, mup_reorg_parm, 0, cli_disallow_mupip_reorg, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "REPLICATE", 0, mup_replicate_qual, mup_replicate_parm, 0, cli_disallow_mupip_replicate, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "RESTORE", mupip_restore, mup_restore_qual, mup_restore_parm, 0, 0, 0, VAL_DISALLOWED, 2, 0, 0, 0 }, { "RUNDOWN", mupip_rundown, mup_rundown_qual, mup_rundown_parm, 0, cli_disallow_mupip_rundown, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "SEMAPHORE", mupip_sems, 0, mup_sems_parm, 0, 0, 0, VAL_DISALLOWED, MAX_PARMS, 0, 0, 0 }, { "SET", mupip_set, mup_set_qual, mup_set_parm, 0, cli_disallow_mupip_set, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "SIZE", mupip_size, mup_size_qual, 0, 0, cli_disallow_mupip_size, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "STOP", mupip_stop, 0, mup_stop_parm, 0, 0, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, #ifdef GTM_TRIGGER { "TRIGGER", mupip_trigger, mup_trigger_qual, mup_trig_parm, 0, cli_disallow_mupip_trigger, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, #endif { "UPGRADE", mupip_upgrade, mup_upgrade_qual, mup_upgrade_parm, 0, 0, 0, VAL_DISALLOWED, 1, 0, 0, 0 }, { "" } }; fis-gtm-V7.0-005/sr_unix/mupip_cmd_disallow.c0000644000032200000250000006605514342376330020074 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2002-2021 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_stdio.h" #include "gtm_string.h" #include "cli.h" #include "cli_parse.h" #include "cli_disallow.h" #include "mupip_cmd_disallow.h" GBLREF char *cli_err_str_ptr; boolean_t cli_disallow_mupip_backup(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; disallow_return_value = (d_c_cli_present("TRANSACTION") || d_c_cli_present("SINCE")) && !(d_c_cli_present("INCREMENTAL") || d_c_cli_present("BYTESTREAM")); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("INCREMENTAL") || d_c_cli_present("BYTESTREAM")) && (d_c_cli_present("COMPREHENSIVE") || d_c_cli_present("DATABASE")); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("INCREMENTAL") || d_c_cli_present("BYTESTREAM")) && (d_c_cli_present("RETRY") || d_c_cli_present("SHOWPROGRESS")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("TRANSACTION") && d_c_cli_present("SINCE"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("BKUPDBJNL.DISABLE") && d_c_cli_present("BKUPDBJNL.OFF"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("REPLICATION.OFF"); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("REPLICATION.ON") && d_c_cli_negated("NEWJNLFILES")); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_mupip_crypt(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; disallow_return_value = !d_c_cli_present("DECRYPT") || !d_c_cli_present("FILE") || !d_c_cli_present("OFFSET") || !d_c_cli_present("LENGTH"); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_mupip_extract(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; disallow_return_value = d_c_cli_present("NULL_IV") && !d_c_cli_present("BINARY"); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_mupip_freeze(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; disallow_return_value = !d_c_cli_present("ON") && !d_c_cli_present("OFF"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("RECORD") && !d_c_cli_present("ON"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("OVERRIDE") && !d_c_cli_present("OFF"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("ON") && d_c_cli_present("OFF"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("ONLINE") && d_c_cli_present("OFF"); CLI_DIS_CHECK_N_RESET; disallow_return_value = !d_c_cli_present("ONLINE") && d_c_cli_present("AUTORELEASE"); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_mupip_dumpfhead(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; disallow_return_value = d_c_cli_present("FILE") && d_c_cli_present("REGION"); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_mupip_integ(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; disallow_return_value = d_c_cli_present("BRIEF") && d_c_cli_present("FULL"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("FILE") && d_c_cli_present("REGION"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("TN_RESET") && (d_c_cli_present("FAST") || d_c_cli_present("BLOCK") || d_c_cli_present("SUBSCRIPT") || d_c_cli_present("REGION")); CLI_DIS_CHECK_N_RESET; /* -ONLINE and -FILE/-TN_RESET is incompatible as the former requires shared memory and the latter requires * standalone access */ disallow_return_value = d_c_cli_present("ONLINE") && (d_c_cli_present("TN_RESET") || d_c_cli_present("FILE")); CLI_DIS_CHECK_N_RESET; /* [NO]STATS is supported only with REGION */ disallow_return_value = !d_c_cli_present("REGION") && (d_c_cli_present("STATS") || d_c_cli_negated("STATS")); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_mupip_journal(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; disallow_return_value = !(d_c_cli_present("RECOVER") || d_c_cli_present("VERIFY") || d_c_cli_present("SHOW") || d_c_cli_present("EXTRACT") || d_c_cli_present("ROLLBACK")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("RECOVER") && d_c_cli_present("ROLLBACK"); CLI_DIS_CHECK_N_RESET; disallow_return_value = !(d_c_cli_present("FORWARD") || d_c_cli_present("BACKWARD")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("FORWARD") && d_c_cli_present("BACKWARD"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("SINCE") && d_c_cli_present("FORWARD"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("LOOKBACK_LIMIT") && d_c_cli_present("FORWARD"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("CHECKTN") && d_c_cli_present("BACKWARD"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("RESYNC") && d_c_cli_present("FETCHRESYNC"); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("RESYNC") || d_c_cli_present("FETCHRESYNC") || d_c_cli_present("ONLINE")) && !d_c_cli_present("ROLLBACK"); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("FETCHRESYNC") || d_c_cli_present("ONLINE")) && d_c_cli_present("FORWARD"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("RSYNC_STRM") && !d_c_cli_present("RESYNC"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("RSYNC_STRM") && d_c_cli_present("FORWARD"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("LOSTTRANS") && !(d_c_cli_present("RECOVER") || d_c_cli_present("ROLLBACK") || d_c_cli_present("EXTRACT")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("BROKENTRANS") && !(d_c_cli_present("RECOVER") || d_c_cli_present("ROLLBACK") || d_c_cli_present("EXTRACT")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("FULL") && (d_c_cli_present("RECOVER") || d_c_cli_present("ROLLBACK")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("DETAIL") && !d_c_cli_present("EXTRACT"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("AFTER") && !d_c_cli_present("FORWARD"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("AFTER") && (d_c_cli_present("RECOVER") || d_c_cli_present("ROLLBACK")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("SINCE") && !d_c_cli_present("BACKWARD"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("LOOKBACK_LIMIT") && !d_c_cli_present("BACKWARD"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("LOOKBACK_LIMIT") && !(d_c_cli_present("VERIFY") || d_c_cli_present("RECOVER") || d_c_cli_present("EXTRACT") || d_c_cli_present("SHOW")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("APPLY_AFTER_IMAGE") && !(d_c_cli_present("ROLLBACK") || d_c_cli_present("RECOVER")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("REDIRECT") && !d_c_cli_present("RECOVER"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("REDIRECT") && d_c_cli_present("BACKWARD"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("BACKWARD") && d_c_cli_negated("CHAIN"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("ROLLBACK") && (d_c_cli_present("AFTER") || d_c_cli_present("LOOKBACK_LIMIT")); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("GLOBAL") || d_c_cli_present("USER") || d_c_cli_present("ID") || d_c_cli_present("SEQNO") || d_c_cli_present("GVPATFILE") || d_c_cli_present("TRANSACTION")) && (d_c_cli_present("RECOVER") || d_c_cli_present("ROLLBACK") || d_c_cli_present("VERIFY")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("CORRUPTDB") && d_c_cli_present("FENCES"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("CORRUPTDB") && (d_c_cli_present("BROKEN") || d_c_cli_present("LOST")); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_mupip_reorg(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; disallow_return_value = (d_c_cli_present("SELECT") || d_c_cli_present("ENCRYPT") || d_c_cli_present("EXCLUDE") || d_c_cli_present("FILL_FACTOR") || d_c_cli_present("INDEX_FILL_FACTOR") || d_c_cli_present("NOCOALESCE") || d_c_cli_present("NOSPLIT") || d_c_cli_present("NOSWAP") || d_c_cli_present("RESUME") || d_c_cli_present("TRUNCATE") || d_c_cli_present("USER_DEFINED_REORG")) && (d_c_cli_present("UPGRADE") || d_c_cli_present("DOWNGRADE")); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("UPGRADE") && d_c_cli_present("DOWNGRADE"); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("UPGRADE") || d_c_cli_present("DOWNGRADE")) && !d_c_cli_present("REGION"); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("SAFEJNL") || d_c_cli_negated("SAFEJNL") || d_c_cli_present("STARTBLK") || d_c_cli_present("STOPBLK")) && !(d_c_cli_present("UPGRADE") || d_c_cli_present("DOWNGRADE")); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("DOWNGRADE") || d_c_cli_present("EXCLUDE") || d_c_cli_present("FILL_FACTOR") || d_c_cli_present("INDEX_FILL_FACTOR") || d_c_cli_present("NOCOALESCE") || d_c_cli_present("NOSPLIT") || d_c_cli_present("NOSWAP") || d_c_cli_present("RESUME") || d_c_cli_present("SELECT") || d_c_cli_present("TRUNCATE") || d_c_cli_present("UPGRADE") || d_c_cli_present("USER_DEFINED_REORG")) && d_c_cli_present("ENCRYPT"); CLI_DIS_CHECK_N_RESET; disallow_return_value = d_c_cli_present("ENCRYPT") && !d_c_cli_present("REGION"); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_mupip_replicate(void) { int disallow_return_value = 0; boolean_t p1, p2, p3, p4, p5, p6; *cli_err_str_ptr = 0; p1 = d_c_cli_present("EDITINSTANCE"); p2 = d_c_cli_present("INSTANCE_CREATE"); p3 = d_c_cli_present("RECEIVER"); p4 = d_c_cli_present("SOURCE"); p5 = d_c_cli_present("UPDATEPROC"); p6 = d_c_cli_present("UPDHELPER"); /* any MUPIP REPLIC command should contain at LEAST one of the above qualifiers */ disallow_return_value = !(p1 || p2 || p3 || p4 || p5 || p6); CLI_DIS_CHECK; /* Note CLI_DIS_CHECK_N_RESET is not used as we want to reuse the computed error string (cli_err_str_ptr) * for the next check as well in case it fails. Note that this can be done only if both checks use * exactly the same set of qualifiers (which is TRUE in this case). */ /* any MUPIP REPLIC command should contain at MOST one of the above qualifiers */ disallow_return_value = cli_check_any2(VARLSTCNT(6) p1, p2, p3, p4, p5, p6); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_mupip_replic_editinst(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; /* any MUPIP REPLIC -EDITINSTANCE command should contain one of CHANGE or SHOW or NAME or QDBRUNDOWN or CLEANSLOTS*/ disallow_return_value = !(d_c_cli_present("CHANGE") || d_c_cli_present("SHOW") || d_c_cli_present("NAME") || (d_c_cli_present("QDBRUNDOWN") || d_c_cli_negated("QDBRUNDOWN")) || d_c_cli_present("CLEANSLOTS")); CLI_DIS_CHECK_N_RESET; /* CHANGE and SHOW are mutually exclusive */ disallow_return_value = (d_c_cli_present("CHANGE") && d_c_cli_present("SHOW")); CLI_DIS_CHECK_N_RESET; /* CHANGE and NAME are mutually exclusive */ disallow_return_value = (d_c_cli_present("CHANGE") && d_c_cli_present("NAME")); CLI_DIS_CHECK_N_RESET; /* CHANGE and QDBRUNDOWN/CLEANSLOTS are mutually exclusive */ disallow_return_value = (d_c_cli_present("CHANGE") && (d_c_cli_present("QDBRUNDOWN") || d_c_cli_negated("QDBRUNDOWN") || d_c_cli_present("CLEANSLOTS"))); CLI_DIS_CHECK_N_RESET; /* SHOW and NAME are mutually exclusive */ disallow_return_value = (d_c_cli_present("SHOW") && d_c_cli_present("NAME")); CLI_DIS_CHECK_N_RESET; /* SHOW and QDBRUNDOWN/CLEANSLOTS are mutually exclusive */ disallow_return_value = (d_c_cli_present("SHOW") && (d_c_cli_present("QDBRUNDOWN") || d_c_cli_negated("QDBRUNDOWN") || d_c_cli_present("CLEANSLOTS"))); CLI_DIS_CHECK_N_RESET; /* OFFSET, SIZE and VALUE is compatible only with CHANGE */ disallow_return_value = (!d_c_cli_present("CHANGE") && (d_c_cli_present("OFFSET") || d_c_cli_present("SIZE") || d_c_cli_present("VALUE"))); CLI_DIS_CHECK_N_RESET; /* OFFSET and SIZE have to be present when CHANGE is specified */ disallow_return_value = (d_c_cli_present("CHANGE") && (!d_c_cli_present("OFFSET") || !d_c_cli_present("SIZE"))); CLI_DIS_CHECK_N_RESET; /* DETAIL is compatible only with SHOW */ disallow_return_value = (!d_c_cli_present("SHOW") && d_c_cli_present("DETAIL")); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_mupip_replic_receive(void) { int disallow_return_value = 0; boolean_t p1, p2, p3, p4, p5, p6, p7; *cli_err_str_ptr = 0; p1 = d_c_cli_present("START"); p2 = d_c_cli_present("SHUTDOWN"); p3 = d_c_cli_present("CHECKHEALTH"); p4 = d_c_cli_present("STATSLOG"); p5 = d_c_cli_present("SHOWBACKLOG"); p6 = d_c_cli_present("CHANGELOG"); p7 = d_c_cli_present("STOPRECEIVERFILTER"); /* any MUPIP REPLIC -RECEIVE command should contain at LEAST one of the above qualifiers */ disallow_return_value = !(p1 || p2 || p3 || p4 || p5 || p6 || p7); CLI_DIS_CHECK; /* Note CLI_DIS_CHECK_N_RESET is not used as we want to reuse the computed error string (cli_err_str_ptr) * for the next check as well in case it fails. Note that this can be done only if both checks use * exactly the same set of qualifiers (which is TRUE in this case). */ /* any MUPIP REPLIC -RECEIVE command should contain at MOST one of the above qualifiers */ disallow_return_value = cli_check_any2(VARLSTCNT(7) p1, p2, p3, p4, p5, p6, p7); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("START") && !(d_c_cli_present("LISTENPORT") || d_c_cli_present("UPDATEONLY") || d_c_cli_present("HELPERS"))); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("START") && d_c_cli_present("LISTENPORT") && !d_c_cli_present("LOG")); CLI_DIS_CHECK_N_RESET; disallow_return_value = (!d_c_cli_present("START") && (d_c_cli_present("LISTENPORT") || d_c_cli_present("UPDATERESYNC") || d_c_cli_present("NORESYNC"))); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("UPDATERESYNC") && d_c_cli_present("NORESYNC")); CLI_DIS_CHECK_N_RESET; disallow_return_value = (!d_c_cli_present("UPDATERESYNC") && (d_c_cli_present("REUSE") || d_c_cli_present("RESUME") || d_c_cli_present("INITIALIZE"))); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("INITIALIZE") && d_c_cli_present("RESUME")); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("REUSE") && d_c_cli_present("RESUME")); CLI_DIS_CHECK_N_RESET; disallow_return_value = (!(d_c_cli_present("START") || d_c_cli_present("SHUTDOWN")) && d_c_cli_present("UPDATEONLY")); CLI_DIS_CHECK_N_RESET; disallow_return_value = (!(d_c_cli_present("START") || d_c_cli_present("SHUTDOWN") || d_c_cli_present("CHECKHEALTH")) && d_c_cli_present("HELPERS")); disallow_return_value = (d_c_cli_present("LISTENPORT") && d_c_cli_present("UPDATEONLY")); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("UPDATEONLY") && d_c_cli_present("HELPERS")); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("CHANGELOG") && !(d_c_cli_present("LOG") || d_c_cli_present("LOG_INTERVAL"))); CLI_DIS_CHECK_N_RESET; /* BUFFSIZE, CMPSIZE or FILTER are supported only with START qualifier */ disallow_return_value = (!d_c_cli_present("START") && (d_c_cli_present("BUFFSIZE") || d_c_cli_present("CMPLVL") || d_c_cli_present("FILTER"))); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("AUTOROLLBACK") && !d_c_cli_present("LISTENPORT") && !d_c_cli_present("START")); CLI_DIS_CHECK_N_RESET; /* LOG are not allowed with STATS qualifier */ disallow_return_value = (d_c_cli_present("STATSLOG") && d_c_cli_present("LOG")); CLI_DIS_CHECK_N_RESET; disallow_return_value = (p7 && (d_c_cli_present("AUTOROLLBACK") || d_c_cli_present("HELPERS") || d_c_cli_present("INITIALIZE") || d_c_cli_present("LISTENPORT") || d_c_cli_present("LOG") || d_c_cli_present("LOG_INTERVAL") || d_c_cli_present("NORESYNC") || d_c_cli_present("PLAINTEXTFALLBACK") || d_c_cli_present("RESUME") || d_c_cli_present("REUSE") || d_c_cli_present("STOPSOURCEFILTER") || d_c_cli_present("TIMEOUT") || d_c_cli_present("TLSID") || d_c_cli_present("UPDATEONLY") || d_c_cli_present("UPDATERESYNC"))); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_mupip_replic_source(void) { int disallow_return_value = 0; boolean_t p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13; *cli_err_str_ptr = 0; p1 = d_c_cli_present("START"); p2 = d_c_cli_present("SHUTDOWN"); p3 = d_c_cli_present("ACTIVATE"); p4 = d_c_cli_present("DEACTIVATE"); p5 = d_c_cli_present("CHECKHEALTH"); p6 = d_c_cli_present("STATSLOG"); p7 = d_c_cli_present("SHOWBACKLOG"); p8 = d_c_cli_present("CHANGELOG"); p9 = d_c_cli_present("STOPSOURCEFILTER"); p10 = d_c_cli_present("LOSTTNCOMPLETE"); p11 = d_c_cli_present("NEEDRESTART"); p12 = d_c_cli_present("JNLPOOL"); p13 = d_c_cli_present("FREEZE"); /* every source server command must have at least one of the above control qualifiers */ disallow_return_value = !(p1 || p2 || p3 || p4 || p5 || p6 || p7 || p8 || p9 || p10 || p11 || p12 || p13); CLI_DIS_CHECK; /* Note CLI_DIS_CHECK_N_RESET is not used as we want to reuse the computed error string (cli_err_str_ptr) * for the next check as well in case it fails. Note that this can be done only if both checks use * exactly the same set of qualifiers (which is TRUE in this case). */ /* every source server command cannot have any more than one of the above control qualifiers */ disallow_return_value = cli_check_any2(VARLSTCNT(11) p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13); CLI_DIS_CHECK_N_RESET; /* It's an error to specify TLSID and PLAINTEXTFALLBACK qualifiers on platforms that don't support SSL/TLS communication. */ # ifndef GTM_TLS disallow_return_value = (d_c_cli_present("PLAINTEXTFALLBACK") || d_c_cli_present("TLSID")); CLI_DIS_CHECK_N_RESET; # endif /* BUFFSIZE, CMPLVL, FILTER, PASSIVE, PLAINTEXTFALLBACK and TLSID are supported only with START qualifier */ disallow_return_value = (!d_c_cli_present("START") && (d_c_cli_present("BUFFSIZE") || d_c_cli_present("CMPLVL") || d_c_cli_present("FILTER") || d_c_cli_present("PASSIVE") || d_c_cli_present("PLAINTEXTFALLBACK") || d_c_cli_present("TLSID"))); CLI_DIS_CHECK_N_RESET; /* CONNECTPARAMS, SECONDARY are supported only with START or ACTIVATE qualifiers */ disallow_return_value = (!d_c_cli_present("START") && !d_c_cli_present("ACTIVATE") && (d_c_cli_present("CONNECTPARAMS") || d_c_cli_present("SECONDARY"))); CLI_DIS_CHECK_N_RESET; /* LOG are not allowed with STATS qualifier */ disallow_return_value = (d_c_cli_present("STATSLOG") && d_c_cli_present("LOG")); CLI_DIS_CHECK_N_RESET; /* LOG are supported only with START, CHANGELOG, ACTIVATE qualifiers */ disallow_return_value = (!d_c_cli_present("START") && !d_c_cli_present("CHANGELOG") && !d_c_cli_present("ACTIVATE") && d_c_cli_present("LOG")); CLI_DIS_CHECK_N_RESET; /* LOG_INTERVAL are supported only with START, CHANGELOG, ACTIVATE, STATSLOG qualifiers */ disallow_return_value = (!d_c_cli_present("START") && !d_c_cli_present("CHANGELOG") && !d_c_cli_present("ACTIVATE") && !d_c_cli_present("STATSLOG") && d_c_cli_present("LOG_INTERVAL")); CLI_DIS_CHECK_N_RESET; /* TIMEOUT is supported only with SHUTDOWN qualifier */ disallow_return_value = (!d_c_cli_present("SHUTDOWN") && d_c_cli_present("TIMEOUT")); CLI_DIS_CHECK_N_RESET; /* One and only one of PASSIVE or SECONDARY must be specified with START */ disallow_return_value = (d_c_cli_present("START") && d_c_cli_present("PASSIVE") && d_c_cli_present("SECONDARY")); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("START") && !d_c_cli_present("PASSIVE") && !d_c_cli_present("SECONDARY")); CLI_DIS_CHECK_N_RESET; /* LOG is a mandatory qualifier with START */ disallow_return_value = (d_c_cli_present("START") && !d_c_cli_present("LOG")); CLI_DIS_CHECK_N_RESET; /* SECONDARY is a mandatory qualifier with ACTIVATE */ disallow_return_value = (d_c_cli_present("ACTIVATE") && !d_c_cli_present("SECONDARY")); CLI_DIS_CHECK_N_RESET; /* One of LOG or LOG_INTERVAL needs to be specified with CHANGELOG */ disallow_return_value = (d_c_cli_present("CHANGELOG") && !d_c_cli_present("LOG") && !d_c_cli_present("LOG_INTERVAL")); CLI_DIS_CHECK_N_RESET; /* ROOTPRIMARY (or UPDOK) and PROPAGATEPRIMARY (or UPDNOTOK) are mutually exclusive */ disallow_return_value = ((d_c_cli_present("ROOTPRIMARY") || d_c_cli_present("UPDOK")) && (d_c_cli_present("PROPAGATEPRIMARY") || d_c_cli_present("UPDNOTOK"))); CLI_DIS_CHECK_N_RESET; /* ROOTPRIMARY and PROPAGATEPRIMARY are allowed only along with START, ACTIVATE or DEACTIVATE qualifiers */ disallow_return_value = ((d_c_cli_present("ROOTPRIMARY") || d_c_cli_present("PROPAGATEPRIMARY") || d_c_cli_present("UPDOK") || d_c_cli_present("UPDNOTOK")) && !(d_c_cli_present("START") || d_c_cli_present("ACTIVATE") || d_c_cli_present("DEACTIVATE"))); CLI_DIS_CHECK_N_RESET; /* INSTSECONDARY is allowed with every control qualifier except LOSTTNCOMPLETE and JNLPOOL */ disallow_return_value = (d_c_cli_present("INSTSECONDARY") && (d_c_cli_present("LOSTTNCOMPLETE") || d_c_cli_present("JNLPOOL"))); CLI_DIS_CHECK_N_RESET; /* DETAIL is compatible only with JNLPOOL */ disallow_return_value = (!d_c_cli_present("JNLPOOL") && d_c_cli_present("DETAIL")); CLI_DIS_CHECK_N_RESET; # ifdef GTM_TLS /* PLAINTEXTFALLBACK is supported only with TLSID qualifier */ disallow_return_value = (!d_c_cli_present("TLSID") && d_c_cli_present("PLAINTEXTFALLBACK")); CLI_DIS_CHECK_N_RESET; # endif return FALSE; } boolean_t cli_disallow_mupip_replic_updhelper(void) { int disallow_return_value = 0; disallow_return_value = !(d_c_cli_present("READER") || d_c_cli_present("WRITER")); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_mupip_rundown(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; disallow_return_value = d_c_cli_present("FILE") && d_c_cli_present("REGION"); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_mupip_set(void) { int disallow_return_value = 0; boolean_t p1, p2, p3; *cli_err_str_ptr = 0; p1 = d_c_cli_present("FILE"); p2 = d_c_cli_present("REGION"); p3 = d_c_cli_present("JNLFILE"); /* any MUPIP SET command should contain at LEAST one of the above qualifiers */ disallow_return_value = !(p1 || p2 || p3); CLI_DIS_CHECK; /* Note CLI_DIS_CHECK_N_RESET is not used as we want to reuse the computed error string (cli_err_str_ptr) * for the next check as well in case it fails. Note that this can be done only if both checks use * exactly the same set of qualifiers (which is TRUE in this case). */ /* any MUPIP SET command should contain at MOST one of the above qualifiers */ disallow_return_value = cli_check_any2(VARLSTCNT(3) p1, p2, p3); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("JOURNAL.ON") && d_c_cli_present("JOURNAL.OFF")); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("JOURNAL.DISABLE") && (d_c_cli_present("JOURNAL.ON") || d_c_cli_present("JOURNAL.OFF") || d_c_cli_present("JOURNAL.ENABLE") || d_c_cli_present("JOURNAL.BEFORE_IMAGES") || d_c_cli_negated("JOURNAL.BEFORE_IMAGES") || d_c_cli_present("JOURNAL.FILENAME") || d_c_cli_present("JOURNAL.ALLOCATION") || d_c_cli_present("JOURNAL.EXTENSION") || d_c_cli_present("JOURNAL.BUFFER_SIZE") || d_c_cli_present("JOURNAL.ALIGNSIZE") || d_c_cli_present("JOURNAL.EPOCH_INTERVAL") || d_c_cli_present("JOURNAL.AUTOSWITCHLIMIT"))); CLI_DIS_CHECK_N_RESET; disallow_return_value = (!(!d_c_cli_present("JOURNAL") || d_c_cli_present("DISABLE") || d_c_cli_present("OFF") || d_c_cli_present("JOURNAL.BEFORE_IMAGES") || d_c_cli_negated("JOURNAL.BEFORE_IMAGES"))); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("REPLICATION.ON") && d_c_cli_present("REPLICATION.OFF")); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("REPLICATION.ON") && (d_c_cli_present("JOURNAL.OFF") || d_c_cli_present("JOURNAL.DISABLE") || d_c_cli_negated("JOURNAL"))); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("PREVJNLFILE") && !(d_c_cli_present("JNLFILE"))); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("VERSION") && (d_c_cli_present("ACCESS_METHOD") || d_c_cli_present("GLOBAL_BUFFERS") || d_c_cli_present("RESERVED_BYTES") || d_c_cli_present("FLUSH_TIME") || d_c_cli_present("LOCK_SPACE") || d_c_cli_present("DEFER_TIME") || d_c_cli_present("WAIT_DISK") || d_c_cli_present("PARTIAL_RECOV_BYPASS"))); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("INST_FREEZE_ON_ERROR") && p3); CLI_DIS_CHECK_N_RESET; p1 = d_c_cli_present("KEY_SIZE"); p2 = d_c_cli_present("RESERVED_BYTES"); disallow_return_value = cli_check_any2(VARLSTCNT(2) p1, p2); CLI_DIS_CHECK_N_RESET; return FALSE; } boolean_t cli_disallow_mupip_trigger(void) { int disallow_return_value = 0; *cli_err_str_ptr = 0; /* any MUPIP TRIGGER command has to have either SELECT or TRIGGERFILE or UPGRADE */ disallow_return_value = !(d_c_cli_present("SELECT") || d_c_cli_present("TRIGGERFILE") || d_c_cli_present("UPGRADE")); CLI_DIS_CHECK_N_RESET; return FALSE; } /* * Disallows multiple heuristics, both region and filename, and invalid matching of heuristic parameter with the heuristic. */ boolean_t cli_disallow_mupip_size(void) { boolean_t disallow_return_value = FALSE; int4 heur_cnt = 0; heur_cnt += (d_c_cli_present("HEURISTIC.ARSAMPLE") ? 1 : 0); heur_cnt += (d_c_cli_present("HEURISTIC.IMPSAMPLE") ? 1 : 0); heur_cnt += (d_c_cli_present("HEURISTIC.SCAN") ? 1 : 0); disallow_return_value = heur_cnt > 1; CLI_DIS_CHECK_N_RESET; disallow_return_value = ! ( /* SAMPLES is param for AR and IMP. So: SAMPLES => (AR || IMP) */ (d_c_cli_present("HEURISTIC.ARSAMPLE") || d_c_cli_present("HEURISTIC.IMPSAMPLE") || !d_c_cli_present("HEURISTIC.SAMPLES")) && /* LEVEL is param for SCAN So: LEVEL => SCAN */ (d_c_cli_present("HEURISTIC.SCAN") || !d_c_cli_present("HEURISTIC.LEVEL")) ); CLI_DIS_CHECK_N_RESET; disallow_return_value = ! ( /* SEED is param for AR and IMP. So: SAMPLES => (AR || IMP) */ (d_c_cli_present("HEURISTIC.ARSAMPLE") || d_c_cli_present("HEURISTIC.IMPSAMPLE") || !d_c_cli_present("HEURISTIC.SEED")) ); CLI_DIS_CHECK_N_RESET; disallow_return_value = (d_c_cli_present("SELECT") && d_c_cli_present("SUBSCRIPT")); CLI_DIS_CHECK_N_RESET; return disallow_return_value; } fis-gtm-V7.0-005/sr_unix/mupip_cmd_disallow.h0000755000032200000250000000261314342376330020072 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2002-2018 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. * * * ****************************************************************/ #ifndef MUPIP_CMD_DISALLOW_H_INCLUDED #define MUPIP_CMD_DISALLOW_H_INCLUDED boolean_t cli_disallow_mupip_backup(void); boolean_t cli_disallow_mupip_crypt(void); boolean_t cli_disallow_mupip_dumpfhead(void); boolean_t cli_disallow_mupip_extract(void); boolean_t cli_disallow_mupip_freeze(void); boolean_t cli_disallow_mupip_integ(void); boolean_t cli_disallow_mupip_journal(void); boolean_t cli_disallow_mupip_reorg(void); boolean_t cli_disallow_mupip_replicate(void); boolean_t cli_disallow_mupip_replic_editinst(void); boolean_t cli_disallow_mupip_replic_receive(void); boolean_t cli_disallow_mupip_replic_source(void); boolean_t cli_disallow_mupip_replic_updhelper(void); boolean_t cli_disallow_mupip_rundown(void); boolean_t cli_disallow_mupip_set(void); boolean_t cli_disallow_mupip_trigger(void); boolean_t cli_disallow_mupip_size(void); boolean_t cli_disallow_mupip_size_heuristic(void); #endif fis-gtm-V7.0-005/sr_unix/mupip_crypt.c0000755000032200000250000000240214342376330016561 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2016 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_limits.h" #include "gtm_string.h" #include "cli.h" #include "util.h" #include "mupip_exit.h" #include "mupip_crypt.h" #include "mu_decrypt.h" #include "gtmcrypt.h" error_def(ERR_MUPCLIERR); void mupip_crypt(void) { unsigned short fname_len, type_len; char fname[GTM_PATH_MAX], type[32]; /* Type should not be too long */ int4 len, off; fname_len = SIZEOF(fname); cli_get_str("FILE", fname, &fname_len); cli_get_int("OFFSET", &off); cli_get_int("LENGTH", &len); if (CLI_PRESENT == cli_present("TYPE")) { type_len = SIZEOF(type); cli_get_str("TYPE", type, &type_len); } else { STRCPY(type, "DB_IV"); type_len = STR_LIT_LEN("DB_IV"); } mupip_exit(mu_decrypt(fname, fname_len, off, len, type, type_len)); } fis-gtm-V7.0-005/sr_unix/mupip_crypt.h0000755000032200000250000000072714342376330016576 0ustar librarygtc/**************************************************************** * * * Copyright 2009 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. * * * ****************************************************************/ void mupip_crypt(void); fis-gtm-V7.0-005/sr_unix/mupip_ctrl.c0000755000032200000250000000166414342376330016375 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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 #include #include "mupip_ctrl.h" GBLDEF bool mu_ctrly_occurred; GBLDEF bool mu_ctrlc_occurred; static int4 curr_time, prev_time = 0; void mupip_ctrl(int sig) { assert (sig == SIGINT); curr_time = (int4)time(0); if (prev_time) { if ((curr_time - prev_time) > 1) { mu_ctrlc_occurred = TRUE; }else { mu_ctrly_occurred = TRUE; } }else mu_ctrlc_occurred = TRUE; prev_time = curr_time; signal(SIGINT, mupip_ctrl); return; } fis-gtm-V7.0-005/sr_unix/mupip_ctrl.h0000755000032200000250000000106314342376330016373 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef MUPIP_CTRL_INCLUDED #define MUPIP_CTRL_INCLUDED void mupip_ctrl(int sig); #endif /* MUPIP_CTRL_INCLUDED */ fis-gtm-V7.0-005/sr_unix/mupip_cvtgbl.c0000755000032200000250000003322314342376330016706 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "gtm_stdlib.h" /* for EXIT() */ #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "stp_parms.h" #include "iosp.h" #include "cli.h" #include "util.h" #include "gtm_caseconv.h" #include "mupip_exit.h" #include "mupip_cvtgbl.h" #include "file_input.h" #include "load.h" #include "mu_outofband_setup.h" #include #include #include "muextr.h" #include #include "op.h" #include "min_max.h" GBLREF int gv_fillfactor; GBLREF bool mupip_error_occurred; GBLREF boolean_t is_replicator; GBLREF boolean_t skip_dbtriggers; GBLREF mstr sys_input; GBLDEF int onerror; error_def(ERR_EXTRFMT); error_def(ERR_LDBINFMT); error_def(ERR_LOADBGSZ); error_def(ERR_LOADINVCHSET); error_def(ERR_LOADEDBG); error_def(ERR_LOADEDSZ); error_def(ERR_MAXSTRLEN); error_def(ERR_MUNOFINISH); error_def(ERR_MUPCLIERR); #define CHAR_TO_READ_LINE1_BIN STR_LIT_LEN("d0GDS BINARY") /* read first 12 characters to check file is binary [d\0GDS BINARY] */ #define MAX_ONERROR_VALUE_LEN STR_LIT_LEN("INTERACTIVE") /* PROCEED, STOP, INTERACTIVE are the choices with INTERACTIVE as max */ #define MAX_FORMAT_VALUE_LEN STR_LIT_LEN("BINARY") /* ZWR, BINARY, GO, GOQ are the choices with BINARY being the longest */ void mupip_cvtgbl(void) { char fn[MAX_FN_LEN + 1], *line1_ptr, *line3_ptr; gtm_uint64_t begin, end; int dos, i, file_format, line1_len, line3_len, utf8; uint4 cli_status, max_rec_size; unsigned char buff[MAX_ONERROR_VALUE_LEN]; unsigned short fn_len, len; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(MAX_ONERROR_VALUE_LEN > MAX_FORMAT_VALUE_LEN); /* so the buff[] definition above is good for FORMAT and ONERROR */ /* If an online rollback occurs when we are loading up the database with new globals and takes us back to a prior logical * state, then we should not continue with the load. The reason being that the application might rely on certain globals to * be present before loading others and that property could be violated if online rollback takes the database back to a * completely different logical state. Set the variable issue_DBROLLEDBACK_anyways that forces the restart logic to issue * an rts_error the first time it detects an online rollback (that takes the database to a prior logical state). */ TREF(issue_DBROLLEDBACK_anyways) = TRUE; is_replicator = TRUE; TREF(ok_to_see_statsdb_regs) = TRUE; skip_dbtriggers = TRUE; fn_len = SIZEOF(fn); if (cli_present("STDIN")) { /* Check if both file name and -STDIN specified. */ if (cli_get_str("FILE", fn, &fn_len)) { util_out_print("STDIN and FILE (!AD) cannot be specified at the same time", TRUE, fn_len, fn); mupip_exit(ERR_MUPCLIERR); } /* User wants to load from standard input */ assert(SIZEOF(fn) > sys_input.len); memcpy(fn, sys_input.addr, sys_input.len); fn_len = sys_input.len; assert(-1 != fcntl(fileno(stdin), F_GETFD)); } else if (!cli_get_str("FILE", fn, &fn_len)) /* User wants to read from a file. */ mupip_exit(ERR_MUPCLIERR); /* Neither -STDIN nor file name specified. */ file_input_init(fn, fn_len, IOP_EOL); if (mupip_error_occurred) EXIT(-1); mu_outofband_setup(); if ((cli_status = cli_present("BEGIN")) == CLI_PRESENT) { if (!cli_get_uint64("BEGIN", &begin)) mupip_exit(ERR_MUPCLIERR); if (1 > begin) mupip_exit(ERR_LOADBGSZ); } else { begin = 1; } if ((cli_status = cli_present("END")) == CLI_PRESENT) { if (!cli_get_uint64("END", &end)) mupip_exit(ERR_MUPCLIERR); if (1 > end) mupip_exit(ERR_LOADEDSZ); if (end < begin) mupip_exit(ERR_LOADEDBG); } else end = MAXUINT8; if ((cli_status = cli_present("FILL_FACTOR")) == CLI_PRESENT) { assert(SIZEOF(gv_fillfactor) == SIZEOF(int4)); if (!cli_get_int("FILL_FACTOR", (int4 *)&gv_fillfactor)) gv_fillfactor = MAX_FILLFACTOR; if (gv_fillfactor < MIN_FILLFACTOR) gv_fillfactor = MIN_FILLFACTOR; else if (gv_fillfactor > MAX_FILLFACTOR) gv_fillfactor = MAX_FILLFACTOR; } else gv_fillfactor = MAX_FILLFACTOR; if (cli_present("ONERROR") == CLI_PRESENT) { len = SIZEOF(buff); if (!cli_get_str("ONERROR", (char *)buff, &len)) { assert(FALSE); onerror = ONERROR_PROCEED; } else { lower_to_upper(buff, buff, len); if (!STRNCMP_LIT_LEN(buff, "STOP", len)) onerror = ONERROR_STOP; else if (!STRNCMP_LIT_LEN(buff, "PROCEED", len)) onerror = ONERROR_PROCEED; else if (!STRNCMP_LIT_LEN(buff, "INTERACTIVE", len)) { if (isatty(0)) /*if stdin is a terminal*/ onerror = ONERROR_INTERACTIVE; else onerror = ONERROR_STOP; } else { util_out_print("Illegal ONERROR parameter for load",TRUE); mupip_exit(ERR_MUPCLIERR); } } } else onerror = ONERROR_PROCEED; /* Default: Proceed on error */ file_format = get_load_format(&line1_ptr, &line3_ptr, &line1_len, &line3_len, &max_rec_size, &utf8, &dos); /* from header */ if (MU_FMT_GOQ == file_format) mupip_exit(ERR_LDBINFMT); if ((BADZCHSET == utf8) || (0 >= line1_len)) mupip_exit(ERR_MUNOFINISH); if (cli_present("FORMAT") == CLI_PRESENT) { /* If the command speficies a format see if it matches the label */ len = SIZEOF(buff); if (!cli_get_str("FORMAT", (char *)buff, &len)) go_load(begin, end, (unsigned char *)line1_ptr, line3_ptr, line3_len, max_rec_size, file_format, utf8, dos); else { lower_to_upper(buff, buff, len); if (!STRNCMP_LIT_LEN(buff, "ZWR", len)) { /* If the label did not determine a format let them specify ZWR and they can sort out the result */ if ((MU_FMT_ZWR == file_format) || (MU_FMT_UNRECOG == file_format)) go_load(begin, end, (unsigned char *)line1_ptr, line3_ptr, line3_len, max_rec_size, MU_FMT_ZWR, utf8, dos); else mupip_exit(ERR_LDBINFMT); } else if (!STRNCMP_LIT_LEN(buff, "BINARY", len)) { if (MU_FMT_BINARY == file_format) bin_load(begin, end, line1_ptr, line1_len); else mupip_exit(ERR_LDBINFMT); } else if (!STRNCMP_LIT_LEN(buff, "GO", len)) { /* If the label did not determine a format let them specify GO and they can sort out the result */ if ((MU_FMT_GO == file_format) || (MU_FMT_UNRECOG == file_format)) go_load(begin, end, (unsigned char *)line1_ptr, line3_ptr, line3_len, max_rec_size, MU_FMT_GO, utf8, dos); else mupip_exit(ERR_LDBINFMT); } else if (!STRNCMP_LIT_LEN(buff, "GOQ", len)) { /* get_load_format doesn't recognize GOQ labels' */ if (MU_FMT_UNRECOG == file_format) goq_load(); else mupip_exit(ERR_LDBINFMT); } else { util_out_print("Illegal file format for load",TRUE); mupip_exit(ERR_MUPCLIERR); } } } else { if (MU_FMT_BINARY == file_format) bin_load(begin, end, line1_ptr, line1_len); else if ((MU_FMT_ZWR == file_format) || (MU_FMT_GO == file_format)) go_load(begin, end, (unsigned char *)line1_ptr, line3_ptr, line3_len, max_rec_size, file_format, utf8, dos); else { assert(MU_FMT_UNRECOG == file_format); mupip_exit(ERR_LDBINFMT); } } mupip_exit(mupip_error_occurred ? ERR_MUNOFINISH : SS_NORMAL); } /* Make an attempt to discover the input file format based on its content principally the label */ int get_load_format(char **line1_ptr, char **line3_ptr, int *line1_len, int *line3_len, uint4 *max_rec_size, int *utf8_extract, int *dos) { char *c, *c1, *ctop, *line1, *line2, *line3, *ptr; int len, line2_len, ret; mval v; uint4 max_io_size; max_io_size = MAX_IO_BLOCK_SIZE - 1; /* label gets less room */ *max_rec_size = MAX_STRLEN + ZWR_EXP_RATIO(MAX_KEY_SZ); /* go for max to avoid interaction with the regex stuff */ line1 = *line1_ptr = malloc(*max_rec_size); /* no corresponding free; released at MUPIP termination */ line3 = *line3_ptr = malloc(*max_rec_size); /* ditto */ *line1_len = file_input_read_xchar(line1, CHAR_TO_READ_LINE1_BIN); *dos = *line3_len = *utf8_extract = 0; ret = MU_FMT_UNRECOG; /* actually means as yet undetermined; used to decide if still trying to find a format */ if (0 < *line1_len) { if (0 == STRNCMP_LIT(line1 + 6, "BINARY")) /* If file is binary do not look further */ return MU_FMT_BINARY; for (line2_len = 0, c = line1, ctop = c + *line1_len; c < ctop; c++) { /* that 1st read is fixed length, so look for a terminator */ if ('\n' == *c) { /* found a terminator */ line2 = c + 1; line2_len = *line1_len - (line2 - line1); *line1_len -= (line2_len + 1); break; } } if (c == ctop) { /* did not find a terminator - read some more of 1st line */ ptr = c; if (0 <= (len = go_get(&ptr, 0, max_io_size))) /* WARNING assignment */ *line1_len += len; else { /* chances of this are small but we are careful not to overflow buffers */ mupip_error_occurred = TRUE; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_MAXSTRLEN); } line2_len = 0; line2 = line1 + *line1_len; } else if (line2_len) { /* If line1 length is actually < 12 chars, the buffer has characters from line2 as well */ for (c = line2, ctop = c + line2_len; c < ctop; c++) { /* look for a line 2 terminator */ if ('\n' == *c) { /* found a terminator */ *line3_len = line2_len - (c - line2 + 1); line2_len = c - line2; break; } } } c1 = line1 + *line1_len; *c1-- = 0; /* null terminate the line to keep util_out_print happy */ if (*dos = ('\r' == *c1)) /* WARNING assignment */ { /* [cariage] return before the / new line - we'll need to keep stripping them off */ *line1_len -= 1; *c1 = 0; /* null terminate earlier to keep util_out_print happy */ } util_out_print("!AD", TRUE, *line1_len, line1); if ((0 == line2_len) || (c == ctop)) { /* need to get at least some more of 2nd line */ ptr = line2 + line2_len; if (0 < (len = go_get(&ptr, 0, max_io_size))) /* WARNING assignment */ line2_len += len; else { /* chances of this are small but we are careful not to overflow buffers */ ret = MU_FMT_GOQ; /* abusing this value to mean not working, as we can't discover GOQ */ line2_len = 0; /* There is no second line in this extract file */ mupip_error_occurred = TRUE; } } if (0 < line2_len) { /* we have 2 label lines to work with */ line2_len -= *dos; c1 = line2 + line2_len; *c1 = 0; /* null terminate the line to keep regex in bounds */ util_out_print("!AD", TRUE, line2_len, line2); if (gtm_regex_perf("ZWR", line2)) ret = MU_FMT_ZWR; /* settle for any ZWR in the second line of the label */ if ((MU_FMT_UNRECOG == ret) && gtm_regex_perf("(GT.M )?[0-9]{2}[-]([A-Z]{3})[-][0-9]{4}[ ]{1,2}[0-9]{2}[:][0-9]{2}[:][0-9]{2}", line2)) ret = MU_FMT_GO; /* GT.M DD-MON-YEAR 24:60:SS used by MUPIP EXTRACT & %GO */ if ((MU_FMT_UNRECOG == ret) && gtm_regex_perf("GLO", line2)) ret = MU_FMT_GO; /* settle for any GLO in the second line of the label */ for (c = line2 + line2_len + 1, ctop = c + *line3_len, c1 = line3; c < ctop; c++) { /* if the first 2 lines were really short, move to other buffer looking for a line 3 terminator */ if ('\n' == *c) { /* found a terminator */ *line3_len = c1 - line3; break; } else *c1 = *c; } if (c == ctop) { /* get all or some of line 3 - the first non-label line */ ptr = line3 + *line3_len; if (0 < (len = go_get(&ptr, 0, *max_rec_size))) { *line3_len += (len - *dos); c1 = line3 + *line3_len; *c1 = 0; /* null terminate the line to keep regex in bounds */ } else { /* chances of this are small but we are careful not to overflow buffers */ ret = MU_FMT_GOQ; mupip_error_occurred = TRUE; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_EXTRFMT); } } else { *line3_len = 0; ret = MU_FMT_GOQ; /* abusing this value to mean not working, as we can't discover GOQ */ } if ((MU_FMT_UNRECOG == ret) && gtm_regex_perf("\\^[%A-Za-z][0-9A-Za-z]*(\\(.*\\))?$", line3)) ret = MU_FMT_GO; /* gvn only */ if ((MU_FMT_UNRECOG == ret) && gtm_regex_perf("\\^[%A-Za-z][0-9A-Za-z]*(\\(.*\\))?=(\".*\"|-?([0-9]+|[0-9]*\\.[0-9]+))$", line3)) ret = MU_FMT_ZWR; /* gvn=val */ if (MU_FMT_UNRECOG != ret) { *utf8_extract = gtm_regex_perf("UTF-8", line1); if ((*utf8_extract && !gtm_utf8_mode) || (!*utf8_extract && gtm_utf8_mode)) { /* extract CHSET doesn't match current $ZCHSET */ if (*utf8_extract) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_LOADINVCHSET, 2, LEN_AND_LIT("UTF-8")); else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_LOADINVCHSET, 2, LEN_AND_LIT("M")); *utf8_extract = BADZCHSET; } } } else return MU_FMT_GOQ; } else return MU_FMT_GOQ; *max_rec_size = (MU_FMT_GO == ret) ? MAX_STRLEN : *max_rec_size; /* for GO, keys are separate */ return MU_FMT_GOQ == ret ? MU_FMT_UNRECOG : ret; /* turn the GOQs back into unrecognized */ } /* given a regular expression definition and a string run the glibc interface NOT RECOMMENDED for general use */ boolean_t gtm_regex_perf(const char *rexpr, char *str_buff) { /* This routine interacts VICIOUSLY with gtm_malloc and gtm_free and thus should only be used if they are not */ boolean_t ret; regex_t regex; regcomp(®ex, rexpr, REG_EXTENDED | REG_ICASE); ret = !regexec(®ex, str_buff, 0, NULL, 0); regfree(®ex); return ret; } fis-gtm-V7.0-005/sr_unix/mupip_cvtpgm.c0000755000032200000250000000104114342376330016716 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /* STUB FILE */ #include "mdef.h" #include "mupip_cvtpgm.h" void mupip_cvtpgm(void) { return; } fis-gtm-V7.0-005/sr_unix/mupip_dump_fhead.c0000644000032200000250000001055514342376330017521 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "gtm_limits.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "mupipbckup.h" #include "gdscc.h" #include "gdskill.h" #include "jnl.h" #include "buddy_list.h" #include "tp.h" #include "util.h" #include "cli.h" #include "mupip_exit.h" #include "str_match.h" #include "mu_getlst.h" #include "gtmmsg.h" #include "mupip_dump_fhead.h" #include "gtm_stdlib.h" #include "wcs_flu.h" #include "mdq.h" GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF boolean_t gtm_dist_ok_to_use; GBLREF tp_region *grlist; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF usr_reg_que *usr_spec_regions; error_def(ERR_BUFFLUFAILED); error_def(ERR_DBNOREGION); error_def(ERR_GTMDISTUNVERIF); error_def(ERR_MUNOFINISH); error_def(ERR_MUPCLIERR); #define DUMPFHEAD_CMD_STRING_SIZE 256 + GTM_PATH_MAX + GTM_PATH_MAX #define EXEC_GTMDUMPFHEAD "%s/mumps -run %%XCMD 'do dumpfhead^%%DUMPFHEAD(\"%s\")'" int4 dumpfhead(int len, unsigned char *filepath); void mupip_dump_fhead(void) { int4 status; tp_region *rptr; unsigned char file[GTM_PATH_MAX]; unsigned short file_len = GTM_PATH_MAX - 1; usr_reg_que *region_que_entry; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Verify gtm_dist, and make sure there is a parameter. */ if (!gtm_dist_ok_to_use) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_GTMDISTUNVERIF, 4, LEN_AND_STR(gtm_dist)); if (CLI_PRESENT == cli_present("REGION")) { /* region */ status = SS_NORMAL; gvinit(); mu_getlst("WHAT", SIZEOF(tp_region)); if (!grlist) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DBNOREGION); dqloop(usr_spec_regions, que, region_que_entry) { for (rptr = grlist; NULL != rptr; rptr = rptr->fPtr) { gv_cur_region = rptr->reg; if ((char *)gv_cur_region->rname == (char *)region_que_entry->usr_reg) break; /* Matching region found. Exit the loop */ } if (NULL == rptr) continue; /* continue the dqloop */ if (CLI_PRESENT == cli_present("FLUSH")) { gv_init_reg(rptr->reg, NULL); gv_cur_region = rptr->reg; /* required for wcs_flu */ cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; if (TRUE == grab_crit_immediate(gv_cur_region, TRUE, NOT_APPLICABLE)) { if (!wcs_flu(WCSFLU_FLUSH_HDR)) { rel_crit(gv_cur_region); gtm_putmsg_csa(CSA_ARG(REG2CSA(gv_cur_region)) VARLSTCNT(6) MAKE_MSG_WARNING(ERR_BUFFLUFAILED), 4, LEN_AND_LIT("MUPIP DUMPFHEAD -FLUSH" " while flushing file header elements"), DB_LEN_STR(gv_cur_region)); } else rel_crit(gv_cur_region); } else { gtm_putmsg_csa(CSA_ARG(REG2CSA(gv_cur_region)) VARLSTCNT(6) MAKE_MSG_WARNING(ERR_BUFFLUFAILED), 4, LEN_AND_LIT("MUPIP DUMPFHEAD -FLUSH while grabbing critical section"), DB_LEN_STR(gv_cur_region)); } } util_out_print("Fileheader dump of region !AD", TRUE, REG_LEN_STR(rptr->reg)); util_out_print("Dumping fileheader of !AD", TRUE, DB_LEN_STR(rptr->reg)); status |= dumpfhead(DB_LEN_STR(rptr->reg)); } mupip_exit(status == SS_NORMAL ? status : ERR_MUNOFINISH); } else { /* we default to file if neither -file nor -region could be found */ if (!cli_get_str("WHAT", (char *) file, (unsigned short *) &file_len)) mupip_exit(ERR_MUPCLIERR); file[file_len] = '\0'; /* Null terminate */ mupip_exit(dumpfhead(file_len, file)); } } int4 dumpfhead(int len, unsigned char *file) { char cmd_dmpfhead_string[DUMPFHEAD_CMD_STRING_SIZE]; # ifdef _BSD union wait wait_stat; # else int4 wait_stat; # endif SNPRINTF(cmd_dmpfhead_string, SIZEOF(cmd_dmpfhead_string), EXEC_GTMDUMPFHEAD, gtm_dist, file); #ifdef _BSD assert(SIZEOF(wait_stat) == SIZEOF(int4)); wait_stat.w_status = SYSTEM(cmd_dmpfhead_string); #else wait_stat = SYSTEM(cmd_dmpfhead_string); #endif return WEXITSTATUS(wait_stat); } fis-gtm-V7.0-005/sr_unix/mupip_dump_fhead.h0000644000032200000250000000121714342376330017521 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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. * * * ****************************************************************/ #ifndef MUPIP_DUMP_FHEAD_INCLUDED #define MUPIP_DUMP_FHEAD_INCLUDED void mupip_dump_fhead(void); #endif /* MUPIP_DUMP_FHEAD_INCLUDED */ fis-gtm-V7.0-005/sr_unix/mupip_endiancvt.c0000644000032200000250000020617514342376330017405 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2022 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_fcntl.h" #include "gtm_unistd.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_sem.h" #include #include #include #include "gtm_time.h" #ifdef __MVS__ #include "gtm_zos_io.h" #include #endif #include "gdsroot.h" #include "v15_gdsroot.h" /* for v15_trans_num */ #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "v6_gdsfhead.h" #include "db_header_conversion.h" #include "gdsblk.h" #include "filestruct.h" #include "jnl.h" #include "gdsbml.h" #include "cli.h" #include "iosp.h" #include "copy.h" #include "error.h" #include "gtmio.h" #include "iotimer.h" #include "gt_timer.h" #include "stp_parms.h" #include "gtm_stat.h" #include "eintr_wrappers.h" #include "util.h" #include "gtm_caseconv.h" #include "io.h" #include "is_proc_alive.h" #include "mu_rndwn_file.h" #include "mupip_exit.h" #include "mu_gv_cur_reg_init.h" #include "mupip_endiancvt.h" #include "gtmmsg.h" #include "wcs_sleep.h" #include "gvcst_lbm_check.h" /* gvcst_blk_ever_allocated */ #include "shmpool.h" #include "min_max.h" #include "spec_type.h" /* collation info */ #include "anticipatory_freeze.h" #include "gtmcrypt.h" #include "db_ipcs_reset.h" #include "db_write_eof_block.h" GBLREF gd_region *gv_cur_region; GBLREF mstr pvt_crypt_buf; LITREF char *gtm_dbversion_table[]; error_def(ERR_BADTAG); error_def(ERR_DBRDONLY); error_def(ERR_ENDIANCVT); error_def(ERR_IOEOF); error_def(ERR_MUNOACTION); error_def(ERR_MUNOFINISH); error_def(ERR_MUPCLIERR); error_def(ERR_MUSTANDALONE); error_def(ERR_NOENDIANCVT); error_def(ERR_TEXT); #define ABANDONED_KILLS "abandoned kills present" #define DBCREATE "database creation in progress" #define DBCORRUPT "the database is corrupted" #define GTCMSERVERACTIVE "a GT.CM server accessing the database" #define KILLINPROG "kills in progress" #define NOTCURRDBFORMAT "database format is not the current version" #define NOTCURRMDBFORMAT "minor database format is not the current version" #define NOTFULLYUPGRADED "some blocks are not upgraded to the current version" #define RECOVINTRPT "recovery was interrupted" #define MAX_CONF_RESPONSE 30 /* No journal pool for endiancvt, so ignore csa everywhere. Saves a bunch of CSA_ARG(NULL)s. */ #define GTM_PUTMSG_CSA(...) gtm_putmsg_csa(CSA_ARG(NULL) __VA_ARGS__) typedef struct { /* adapted from dbcertify.h */ unsigned int top; /* Offset to top of the key */ unsigned int end; /* End of the current key */ unsigned int gvn_len; /* Length of key */ unsigned char *key; /* Pointer to the key */ } end_gv_key; typedef struct { int db_fd; int outdb_fd; /* FD_INVALID if inplace */ boolean_t inplace; /* update in place */ boolean_t endian_native; /* original database */ block_id tot_blks; int bsize; /* GDS block size */ int4 startvbn; /* in DISK_BLOCK_SIZE units */ block_id last_blk_cvt; /* highest block converted so far not lbm */ char *database_fn; int database_fn_len; uint4 is_encrypted; block_id encryption_hash_cutoff; boolean_t non_null_iv; trans_num encryption_hash2_start_tn; enc_handles encr_handles; struct /* used by find_dtblk related routines */ { char *buff; char *dtrbuff; /* keep the DT root */ block_id blkid; boolean_t native; /* records are native endian */ boolean_t dtrnative; /* records in dtrbuff are native endian */ int count; /* number of times needed */ } dtblk; } endian_info; void endian_header(sgmnt_data *new, sgmnt_data *old, boolean_t new_is_native); void v6_endian_header(v6_sgmnt_data *new, v6_sgmnt_data *old, boolean_t new_is_native); int4 endian_process(endian_info *info, sgmnt_data *new_data, sgmnt_data *old_data, boolean_t override_specified); void endian_cvt_blk_hdr(blk_hdr_ptr_t blkhdr, boolean_t new_is_native, boolean_t make_empty); void endian_cvt_blk_recs(endian_info *info, char *new_block, blk_hdr_ptr_t blkhdr, block_id blknum); char *endian_read_dbblk(endian_info *info, block_id blk_to_get); void endian_find_key(endian_info *info, end_gv_key *gv_key, char *rec_p, int rec_len, int blk_levl, boolean_t long_blk_id); boolean_t endian_match_key(end_gv_key *gv_key1, int blk_levl, end_gv_key *key2); block_id endian_find_dtblk(endian_info *info, end_gv_key *gv_key); /* If we acquired standalone access, we need to release it before we exit. Ideally, mupip_exit_handler should take care of doing * it. But, since we free up memory allocated to gv_cur_region before exiting out of this module. An alternative would be to free * gv_cur_region AFTER db_ipcs_reset in mupip_exit_handler but since various code paths set gv_cur_region, the implication of such * a change is not clear at this writing. */ #define DO_STANDALONE_CLNUP_IF_NEEDED(ENDIAN_NATIVE) \ { \ if (ENDIAN_NATIVE) \ { /* release standalone access */ \ assert(FILE_INFO(gv_cur_region)->grabbed_access_sem); \ db_ipcs_reset(gv_cur_region); \ mu_gv_cur_reg_free(); \ } \ } void mupip_endiancvt(void) { block_id blk_num; boolean_t outdb_specified, endian_native, swap_boolean, got_standalone, override_specified; char db_name[MAX_FN_LEN + 1], *t_name; char outdb[MAX_FN_LEN + 1], conf_buff[MAX_CONF_RESPONSE + 1], *response; char *errptr, *check_error, *mastermap; char *from_endian, *to_endian; endian_info info; endian32_struct endian_check; enum db_ver swap_dbver; enum mdb_ver swap_mdbver; int rc; int i, db_fd, outdb_fd, mastermap_size; int gtmcrypt_errno; int4 status, save_errno; sgmnt_data *old_data, *new_data; trans_num curr_tn; uint4 swap_uint4; uint4 cli_status; unsigned short n_len, outdb_len, t_len; ZOS_ONLY(int realfiletag;) if (CLI_PRESENT == (cli_status = cli_present("OUTDB"))) { outdb_specified = TRUE; outdb_len = SIZEOF(outdb) - 1; if (!cli_get_str("OUTDB", outdb, &outdb_len)) mupip_exit(ERR_MUPCLIERR); } else outdb_specified = FALSE; n_len = SIZEOF(db_name) - 1; if (cli_get_str("DATABASE", db_name, &n_len) == FALSE) mupip_exit(ERR_MUPCLIERR); OPENFILE(db_name, (!outdb_specified ? O_RDWR : O_RDONLY), db_fd); if (FD_INVALID == db_fd) { save_errno = errno; util_out_print("Error accessing database file !AD. Aborting endiancvt.", TRUE, n_len, db_name); errptr = (char *)STRERROR(save_errno); util_out_print("open : !AZ", TRUE, errptr); mupip_exit(save_errno); } # ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(db_fd, TAG_BINARY, &realfiletag)) TAG_POLICY_GTM_PUTMSG(db_name, realfiletag, TAG_BINARY, errno); # endif old_data = (sgmnt_data *)malloc(SIZEOF(sgmnt_data)); LSEEKREAD(db_fd, 0, old_data, SIZEOF(sgmnt_data), save_errno); if (0 == memcmp(old_data, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_upconv(old_data); if (0 != save_errno) { free(old_data); CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ util_out_print("Error reading database file !AD header. Aborting endiancvt.", TRUE, n_len, db_name); if (-1 != save_errno) { errptr = (char *)STRERROR(save_errno); util_out_print("read : !AZ", TRUE, errptr); mupip_exit(save_errno); } else mupip_exit(ERR_IOEOF); } if (MEMCMP_LIT(&old_data->label[0], GDS_LABEL) && MEMCMP_LIT(&old_data->label[0], V6_GDS_LABEL)) { util_out_print("Database file !AD has an unrecognizable format", TRUE, n_len, db_name); free(old_data); CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ mupip_exit(ERR_MUNOACTION); } override_specified = (CLI_PRESENT == (cli_status = cli_present("OVERRIDE"))); check_error = NULL; endian_check.word32 = (uint4)old_data->minor_dbver; #ifdef BIGENDIAN if (!endian_check.shorts.big_endian) #else if (!endian_check.shorts.little_endian) #endif { endian_native = FALSE; /* nobody can be using the db */ /* do checks after swapping fields */ assert(SIZEOF(int4) == SIZEOF(old_data->desired_db_format)); /* If OVERRIDE is specified, skip checking for those fields that are not critical for the integrity of the db. * Any field that indicates the db is potentially damaged cannot be overridden. */ if (!override_specified) { swap_mdbver = (enum mdb_ver)GTM_BYTESWAP_32(old_data->minor_dbver); if ((GDSMVCURR != swap_mdbver) && (BLK_ID_32_MVER != swap_mdbver)) { check_error = NOTCURRMDBFORMAT; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } swap_uint4 = GTM_BYTESWAP_32(old_data->kill_in_prog); if (0 != swap_uint4) { check_error = KILLINPROG; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } swap_uint4 = GTM_BYTESWAP_32(old_data->abandoned_kills); if (0 != swap_uint4) { check_error = ABANDONED_KILLS; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } swap_uint4 = GTM_BYTESWAP_32(old_data->rc_srv_cnt); if (0 != swap_uint4) { check_error = GTCMSERVERACTIVE; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } } swap_dbver = (enum db_ver)GTM_BYTESWAP_32(old_data->desired_db_format); if (!swap_dbver || ((GDSVCURR != swap_dbver) && !(BLK_ID_32_VER > swap_dbver))) { check_error = NOTCURRDBFORMAT; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } assert(SIZEOF(int4) == SIZEOF(old_data->fully_upgraded)); swap_boolean = GTM_BYTESWAP_32(old_data->fully_upgraded); if (!swap_boolean) { check_error = NOTFULLYUPGRADED; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } swap_uint4 = GTM_BYTESWAP_32(old_data->recov_interrupted); if (0 != swap_uint4) { check_error = RECOVINTRPT; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } swap_uint4 = GTM_BYTESWAP_32(old_data->createinprogress); if (0 != swap_uint4) { check_error = DBCREATE; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } swap_uint4 = GTM_BYTESWAP_32(old_data->file_corrupt); if (0 != swap_uint4) { check_error = DBCORRUPT; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } } else { endian_native = TRUE; /* need to get standalone */ mu_gv_cur_reg_init(); strcpy((char *)gv_cur_region->dyn.addr->fname, db_name); gv_cur_region->dyn.addr->fname_len = n_len; got_standalone = STANDALONE(gv_cur_region); if (FALSE == got_standalone) { mu_gv_cur_reg_free(); free(old_data); CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ GTM_PUTMSG_CSA(VARLSTCNT(4) MAKE_MSG_TYPE(ERR_MUSTANDALONE, ERROR), 2, n_len, db_name); mupip_exit(ERR_MUNOACTION); } /* Now that we got standalone access, re-read uptodate fileheader in case it changed in between. * And redo the checks that we did before getting standalone access to make sure nothing changed. */ LSEEKREAD(db_fd, 0, old_data, SIZEOF(sgmnt_data), save_errno); if (0 == memcmp(old_data, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_upconv(old_data); if (0 != save_errno) { assert(FALSE); DO_STANDALONE_CLNUP_IF_NEEDED(endian_native); free(old_data); CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ util_out_print("Error reading database file !AD header. Aborting endiancvt.", TRUE, n_len, db_name); if (-1 != save_errno) { errptr = (char *)STRERROR(save_errno); util_out_print("read : !AZ", TRUE, errptr); mupip_exit(save_errno); } else mupip_exit(ERR_IOEOF); } if (MEMCMP_LIT(&old_data->label[0], GDS_LABEL) && MEMCMP_LIT(&old_data->label[0], V6_GDS_LABEL)) { assert(FALSE); DO_STANDALONE_CLNUP_IF_NEEDED(endian_native); util_out_print("Database file !AD has an unrecognizable format2", TRUE, n_len, db_name); free(old_data); CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ mupip_exit(ERR_MUNOACTION); } if (endian_check.word32 != (uint4)old_data->minor_dbver) { /* file header endianness changed between the two LSEEKREADs. abort endiancvt */ assert(FALSE); DO_STANDALONE_CLNUP_IF_NEEDED(endian_native); util_out_print("Database file !AD endianness changed during endiancvt. Aborting", TRUE, n_len, db_name); free(old_data); CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ mupip_exit(ERR_MUNOACTION); } /* Now that the redone checks are okay, go ahead with the rest of the endian conversion */ if (gv_cur_region->read_only && !outdb_specified) { DO_STANDALONE_CLNUP_IF_NEEDED(endian_native); free(old_data); CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ GTM_PUTMSG_CSA(VARLSTCNT(4) ERR_DBRDONLY, 2, n_len, db_name); mupip_exit(ERR_MUNOACTION); } if (!override_specified) { if ((GDSMVCURR != old_data->minor_dbver) && (BLK_ID_32_MVER != old_data->minor_dbver)) { check_error = NOTCURRMDBFORMAT; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } if (0 != old_data->kill_in_prog) { check_error = KILLINPROG; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } if (0 != old_data->abandoned_kills) { check_error = ABANDONED_KILLS; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } if (0 != old_data->rc_srv_cnt) { check_error = GTCMSERVERACTIVE; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } } if (!old_data->desired_db_format || ((GDSVCURR != old_data->desired_db_format) && !(BLK_ID_32_VER > old_data->desired_db_format))) { check_error = NOTCURRDBFORMAT; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } if (!old_data->fully_upgraded) { check_error = NOTFULLYUPGRADED; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } if (0 != old_data->recov_interrupted) { check_error = RECOVINTRPT; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } if (0 != old_data->createinprogress) { check_error = DBCREATE; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } if (0 != old_data->file_corrupt) { check_error = DBCORRUPT; GTM_PUTMSG_CSA(VARLSTCNT(6) ERR_NOENDIANCVT, 4, n_len, db_name, LEN_AND_STR(check_error)); } if (!check_error && !outdb_specified) { if (JNL_ENABLED(old_data)) { /* report what we are doing ERR_JNLSTATE */ util_out_print("!_Journaling was enabled - now closed",TRUE); old_data->jnl_state = jnl_closed; } if (REPL_ENABLED(old_data)) { /* report what we are doing ERR_REPLSTATE */ util_out_print("!_Replication was enabled - now closed",TRUE); old_data->repl_state = repl_closed; } } } if (check_error) { DO_STANDALONE_CLNUP_IF_NEEDED(endian_native); free(old_data); CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ mupip_exit(ERR_MUNOACTION); } info.is_encrypted = endian_native ? old_data->is_encrypted : GTM_BYTESWAP_32(old_data->is_encrypted); info.encryption_hash_cutoff = endian_native ? old_data->encryption_hash_cutoff : BLK_ID_BYTESWAP(old_data->encryption_hash_cutoff); if (USES_ANY_KEY(&info)) { /* Database is encrypted. Initialize encryption and setup the keys to be used in later encryption/decryption */ info.database_fn = &db_name[0]; info.database_fn_len = n_len; INIT_PROC_ENCRYPTION(gtmcrypt_errno); if (0 == gtmcrypt_errno) { info.encr_handles.encr_key_handle = GTMCRYPT_INVALID_KEY_HANDLE; info.encr_handles.encr_key_handle2 = GTMCRYPT_INVALID_KEY_HANDLE; if (IS_ENCRYPTED(info.is_encrypted)) { GTMCRYPT_INIT_BOTH_CIPHER_CONTEXTS(NULL, old_data->encryption_hash, info.database_fn_len, info.database_fn, info.encr_handles.encr_key_handle, gtmcrypt_errno); } if ((0 == gtmcrypt_errno) && USES_NEW_KEY(&info)) { GTMCRYPT_INIT_BOTH_CIPHER_CONTEXTS(NULL, old_data->encryption_hash2, info.database_fn_len, info.database_fn, info.encr_handles.encr_key_handle2, gtmcrypt_errno); } } if (0 != gtmcrypt_errno) { GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, n_len, db_name); mupip_exit(gtmcrypt_errno); } REALLOC_CRYPTBUF_IF_NEEDED(SIZEOF(blk_hdr)); } from_endian = endian_check.shorts.big_endian ? "BIG" : "LITTLE"; to_endian = endian_check.shorts.big_endian ? "LITTLE" : "BIG"; util_out_print("Converting database file !AD from !AZ endian to !AZ endian on a !AZ endian system", TRUE, n_len, db_name, from_endian, to_endian, ENDIANTHIS); if (!outdb_specified) { util_out_print("Converting in place - database will be damaged by an abnormal termination", TRUE); util_out_print("You must have a backup before proceeding", TRUE); } else util_out_print("Converting to new file !AD", TRUE, outdb_len, outdb); util_out_print("Proceed [yes/no] ?", TRUE); response = util_input(conf_buff, MAX_CONF_RESPONSE, stdin, TRUE); if (NULL == response || ('Y' != conf_buff[0] && 'y' != conf_buff[0])) { DO_STANDALONE_CLNUP_IF_NEEDED(endian_native); free(old_data); CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ mupip_exit(ERR_MUNOACTION); } new_data = (sgmnt_data *)malloc(SIZEOF(sgmnt_data)); memcpy(new_data, old_data, SIZEOF(sgmnt_data)); new_data->file_corrupt = endian_native ? GTM_BYTESWAP_32(TRUE) : TRUE; info.db_fd = db_fd; info.inplace = !outdb_specified; info.endian_native = info.dtblk.native = info.dtblk.dtrnative = endian_native; info.tot_blks = info.last_blk_cvt = info.bsize = info.startvbn = 0; info.dtblk.buff = info.dtblk.dtrbuff = NULL; info.dtblk.blkid = -1; /* invalid block number */ info.dtblk.count = 0; if (0 == memcmp(new_data->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) { /* convert V6 file header fields */ v6_endian_header((v6_sgmnt_data_ptr_t)new_data, (v6_sgmnt_data_ptr_t)old_data, !endian_native); } else endian_header(new_data, old_data, !endian_native); /* convert file header fields */ if (outdb_specified) { OPENFILE3(outdb, O_RDWR | O_CREAT | O_EXCL, 0666, outdb_fd); if (FD_INVALID == outdb_fd) { /* error */ save_errno = errno; util_out_print("Error creating converted databasae file !AD. Aborting endiancvt.", TRUE, outdb_len, outdb); errptr = (char *)STRERROR(save_errno); util_out_print("open : !AZ", TRUE, errptr); DO_STANDALONE_CLNUP_IF_NEEDED(endian_native); free(new_data); free(old_data); CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ mupip_exit(save_errno); } # ifdef __MVS__ if (-1 == gtm_zos_set_tag(outdb_fd, TAG_BINARY, TAG_NOTTEXT, TAG_DONTFORCE, &realfiletag)) TAG_POLICY_GTM_PUTMSG(outdb, realfiletag, TAG_BINARY, errno); # endif new_data->file_corrupt = endian_native ? GTM_BYTESWAP_32(TRUE) : TRUE; if (0 == memcmp(new_data->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) { db_header_dwnconv(new_data); LSEEKWRITE(outdb_fd, 0, new_data, SIZEOF(sgmnt_data), save_errno); }else LSEEKWRITE(outdb_fd, 0, new_data, SIZEOF(sgmnt_data), save_errno); if (0 != save_errno) { free(new_data); free(old_data); CLOSEFILE_RESET(outdb_fd, rc); /* resets "outdb_fd" to FD_INVALID */ CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ DO_STANDALONE_CLNUP_IF_NEEDED(endian_native); util_out_print("Error writing converted database file !AD header. Aborting endiancvt.", TRUE, outdb_len, outdb); if (-1 != save_errno) { errptr = (char *)STRERROR(save_errno); util_out_print("write : !AZ", TRUE, errptr); mupip_exit(save_errno); } else { util_out_print("write : unexpected error", TRUE); mupip_exit(ERR_MUNOFINISH); } } /* read master bit map from old file */ mastermap_size = endian_native ? old_data->master_map_len : new_data->master_map_len; mastermap = malloc(mastermap_size); LSEEKREAD(db_fd, SGMNT_HDR_LEN, mastermap, mastermap_size, save_errno); if (0 != save_errno) { free(new_data); free(old_data); CLOSEFILE_RESET(outdb_fd, rc); /* resets "outdb_fd" to FD_INVALID */ CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ DO_STANDALONE_CLNUP_IF_NEEDED(endian_native); util_out_print("Error reading database file !AD master map. Aborting endiancvt.", TRUE, n_len, db_name); if (-1 != save_errno) { errptr = (char *)STRERROR(save_errno); util_out_print("read : !AZ", TRUE, errptr); mupip_exit(save_errno); } else mupip_exit(ERR_IOEOF); } /* write master bit map to new file */ LSEEKWRITE(outdb_fd, SGMNT_HDR_LEN, mastermap, mastermap_size, save_errno); if (0 != save_errno) { free(new_data); free(old_data); CLOSEFILE_RESET(outdb_fd, rc); /* resets "outdb_fd" to FD_INVALID */ CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ DO_STANDALONE_CLNUP_IF_NEEDED(endian_native); util_out_print("Error writing converted database file !AD master map. Aborting endiancvt.", TRUE, outdb_len, outdb); if (-1 != save_errno) { errptr = (char *)STRERROR(save_errno); util_out_print("write : !AZ", TRUE, errptr); mupip_exit(save_errno); } else { util_out_print("write : unexpected error", TRUE); mupip_exit(ERR_MUNOFINISH); } } } else outdb_fd = FD_INVALID; info.outdb_fd = outdb_fd; status = endian_process(&info, new_data, old_data, override_specified); if (0 != status) { /* db_ipcs_reset in the macro below works even with the now converted opposite endian header since it just sets * csd->s{e|h}mid to INVALIDS{E|H}MID and zeroes s{e|h}m_ctime. */ DO_STANDALONE_CLNUP_IF_NEEDED(endian_native); free(new_data); free(old_data); CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ if (outdb_specified) CLOSEFILE_RESET(outdb_fd, rc); /* resets "outdb_fd" to FD_INVALID */ mupip_exit(ERR_MUNOFINISH); /* endian_process issued specific message */ } new_data->file_corrupt = endian_native ? GTM_BYTESWAP_32(FALSE) : FALSE; if (outdb_specified) { if (0 == memcmp(new_data->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv(new_data); LSEEKWRITE(outdb_fd, 0, new_data, SIZEOF(sgmnt_data), save_errno); } else { if (0 == memcmp(new_data->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv(new_data); DB_LSEEKWRITE((sgmnt_addrs *)NULL, ((unix_db_info *)NULL), (char *)NULL, db_fd, 0, new_data, SIZEOF(sgmnt_data), save_errno); } if (0 != save_errno) { free(new_data); free(old_data); if (outdb_specified) CLOSEFILE_RESET(outdb_fd, rc); /* resets "outdb_fd" to FD_INVALID */ CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ DO_STANDALONE_CLNUP_IF_NEEDED(endian_native); util_out_print("Error writing!AZ database file !AD header. Aborting endiancvt.", TRUE, outdb_specified ? "new" : "", outdb_specified ? outdb_len : n_len, outdb_specified ? outdb : db_name); if (-1 != save_errno) { errptr = (char *)STRERROR(save_errno); util_out_print("write : !AZ", TRUE, errptr); mupip_exit(save_errno); } else { util_out_print("write : unexpected error", TRUE); mupip_exit(ERR_MUNOFINISH); } } DO_STANDALONE_CLNUP_IF_NEEDED(endian_native); free(new_data); free(old_data); if (outdb_specified) { GTM_FSYNC(outdb_fd, rc); } else GTM_DB_FSYNC((sgmnt_addrs *)NULL, db_fd, rc); if (-1 == rc) { save_errno = errno; assert(FALSE); if (-1 != save_errno) { errptr = (char *)STRERROR(save_errno); util_out_print("fsync : !AZ : !AZ", TRUE, (outdb_specified ? "outdb_fd" : "db_fd"), errptr); mupip_exit(save_errno); } else { util_out_print("fsync : !AZ : unexpected error", TRUE, (outdb_specified ? "outdb_fd" : "db_fd")); mupip_exit(ERR_MUNOFINISH); } } CLOSEFILE_RESET(db_fd, rc); /* resets "db_fd" to FD_INVALID */ if (outdb_specified) CLOSEFILE_RESET(outdb_fd, rc); /* resets "outdb_fd" to FD_INVALID */ /* Display success message only after all data has been synced to disk and the file descriptors closed */ GTM_PUTMSG_CSA(VARLSTCNT(7) ERR_ENDIANCVT, 5, n_len, db_name, from_endian, to_endian, ENDIANTHIS); mupip_exit(SS_NORMAL); } #define SWAP_SD4(FIELD) \ { \ assert(SIZEOF(int4) == SIZEOF(old->FIELD)); \ new->FIELD = GTM_BYTESWAP_32(old->FIELD); \ } #define SWAP_SD4_CAST(FIELD, castType) \ { \ assert(SIZEOF(int4) == SIZEOF(old->FIELD)); \ new->FIELD = (castType)GTM_BYTESWAP_32(old->FIELD); \ } #define SWAP_SD8(FIELD) \ { \ assert(SIZEOF(gtm_int8) == SIZEOF(old->FIELD)); \ new->FIELD = GTM_BYTESWAP_64(old->FIELD); \ } void endian_header(sgmnt_data *new, sgmnt_data *old, boolean_t new_is_native) { int idx; time_t ctime; /************* MOSTLY STATIC DATABASE STATE FIELDS **************************/ SWAP_SD4(blk_size); SWAP_SD4(v6_master_map_len); SWAP_SD4(bplmap); SWAP_SD4(v6_start_vbn); SWAP_SD4_CAST(acc_meth, enum db_acc_method); SWAP_SD4(max_bts); SWAP_SD4(n_bts); SWAP_SD4(bt_buckets); SWAP_SD4(reserved_bytes); SWAP_SD4(max_rec_size); SWAP_SD4(max_key_size); SWAP_SD4(lock_space_size); SWAP_SD4(extension_size); SWAP_SD4(def_coll); SWAP_SD4(def_coll_ver); SWAP_SD4(write_fullblk); SWAP_SD4(statsdb_allocation); SWAP_SD4(std_null_coll); SWAP_SD4(null_subs); SWAP_SD4(free_space); SWAP_SD4(mutex_spin_parms.mutex_hard_spin_count); /* gdsbt.h */ SWAP_SD4(mutex_spin_parms.mutex_sleep_spin_count); SWAP_SD4(mutex_spin_parms.mutex_spin_sleep_mask); SWAP_SD4(mutex_spin_parms.mutex_que_entry_space_size); SWAP_SD4(max_update_array_size); SWAP_SD4(max_non_bm_update_array_size); /* SWAP_SD4(file_corrupt); is set in main routine */ SWAP_SD4_CAST(minor_dbver, enum mdb_ver); SWAP_SD4(jnl_checksum); SWAP_SD4(wcs_phase2_commit_wait_spincnt); SWAP_SD4_CAST(last_mdb_ver, enum mdb_ver); /************* FIELDS SET AT CREATION TIME ********************************/ /* SWAP_SD4(createinprogress); checked above as FALSE so no need */ assert(SIZEOF(int4) == SIZEOF(old->creation_time4)); time(&ctime); assert(SIZEOF(ctime) >= SIZEOF(int4)); new->creation_time4 = (int4)ctime;/* Take only lower order 4-bytes of current time */ if (!new_is_native) SWAP_SD4(creation_time4); /************* FIELDS USED BY TN WARN PROCESSING *************************/ SWAP_SD8(max_tn); SWAP_SD8(max_tn_warn); /************* FIELDS SET BY MUPIP BACKUP/REORG *************************/ SWAP_SD8(last_inc_backup); SWAP_SD8(last_com_backup); SWAP_SD8(last_rec_backup); SWAP_SD4(v6_last_inc_bkup_last_blk); SWAP_SD4(v6_last_com_bkup_last_blk); SWAP_SD4(v6_last_rec_bkup_last_blk); SWAP_SD4(v6_reorg_restart_block); SWAP_SD8(last_start_backup); /************* FIELDS SET WHEN DB IS OPEN ********************************/ new->image_count = 0; /* should be zero when db is not open so reset it unconditionally */ new->freeze = 0; /* should be zero when db is not open so reset it unconditionally */ SWAP_SD4(kill_in_prog); SWAP_SD4(abandoned_kills); /************* FIELDS USED IN V4 <==> V5 COMPATIBILITY MODE ****************/ SWAP_SD8(tn_upgrd_blks_0); SWAP_SD8(desired_db_format_tn); SWAP_SD8(reorg_db_fmt_start_tn); SWAP_SD4(v6_reorg_upgrd_dwngrd_restart_block); SWAP_SD4(v6_blks_to_upgrd); SWAP_SD4(v6_blks_to_upgrd_subzero_error); SWAP_SD4_CAST(desired_db_format, enum db_ver); SWAP_SD4(fully_upgraded); /* should be TRUE */ assert(new->fully_upgraded); /* Since the source database is fully upgraded and since all RECYCLED blocks will be marked as FREE, we are guaranteed * there are NO V4 format block that is too full to be upgraded to V5 format (i.e. will cause DYNUPGRDFAIL error). */ new->db_got_to_v5_once = TRUE; /* should be TRUE */ new->opened_by_gtmv53 = TRUE; /* should be TRUE */ /************* FIELDS RELATED TO DB TRANSACTION HISTORY *****************************/ SWAP_SD8(trans_hist.curr_tn); SWAP_SD8(trans_hist.early_tn); SWAP_SD8(trans_hist.last_mm_sync); SWAP_SD8(trans_hist.mm_tn); SWAP_SD4(trans_hist.lock_sequence); SWAP_SD4(trans_hist.ccp_jnl_filesize); SWAP_SD8(trans_hist.total_blks); SWAP_SD8(trans_hist.free_blocks); /************* FIELDS RELATED TO WRITE CACHE FLUSHING *******************************/ SWAP_SD4(flush_time[0]); SWAP_SD4(flush_time[1]); SWAP_SD4(flush_trigger); SWAP_SD4(n_wrt_per_flu); SWAP_SD4(wait_disk_space); SWAP_SD4(defer_time); SWAP_SD4(mumps_can_bypass); SWAP_SD4(epoch_taper); SWAP_SD4(epoch_taper_time_pct); SWAP_SD4(epoch_taper_jnl_pct); SWAP_SD4(asyncio); /************* FIELDS Used for update process performance improvement. Some may go away in later releases ********/ SWAP_SD4(reserved_for_upd); SWAP_SD4(avg_blks_per_100gbl); SWAP_SD4(pre_read_trigger_factor); SWAP_SD4(writer_trigger_factor); /************* FIELDS USED ONLY BY UNIX ********************************/ /* Solaris complains about swapping -1 * assert(INVALID_SEMID == GTM_BYTESWAP_32(INVALID_SEMID)); * assert(INVALID_SHMID == GTM_BYTESWAP_32(INVALID_SHMID)); */ assert(-1 == INVALID_SEMID); assert(-1 == INVALID_SHMID); if (new_is_native) { /* Since we have standalone access, reset volatile fields in the database file header */ new->semid = INVALID_SEMID; new->shmid = INVALID_SHMID; new->gt_sem_ctime.ctime = 0; new->gt_shm_ctime.ctime = 0; memset(new->machine_name, 0, MAX_MCNAMELEN); } /************* ACCOUNTING INFORMATION ********************************/ /************* CCP/RC RELATED FIELDS (CCP STUFF IS NOT USED CURRENTLY BY GT.M) *************/ SWAP_SD4(staleness[0]); SWAP_SD4(staleness[1]); SWAP_SD4(ccp_tick_interval[0]); SWAP_SD4(ccp_tick_interval[1]); SWAP_SD4(ccp_quantum_interval[0]); SWAP_SD4(ccp_quantum_interval[1]); SWAP_SD4(ccp_response_interval[0]); SWAP_SD4(ccp_response_interval[1]); SWAP_SD4(ccp_jnl_before); SWAP_SD4(clustered); SWAP_SD4(unbacked_cache); /* RC server related fields sb zero when not active */ SWAP_SD4(rc_srv_cnt); SWAP_SD4(dsid); SWAP_SD4(rc_node); /************* REPLICATION RELATED FIELDS ****************/ SWAP_SD8(reg_seqno); SWAP_SD8(pre_multisite_resync_seqno); /* Note some of the following names were added or renamed in V5.1 but should be of no issue for V5.0 builds * since we will be swapping unused fields. */ SWAP_SD8(zqgblmod_tn); SWAP_SD8(zqgblmod_seqno); new->repl_state = repl_closed; if (!new_is_native) SWAP_SD4(repl_state); SWAP_SD4(multi_site_open); /************* TP RELATED FIELDS ********************/ for (idx = 0; idx < ARRAYSIZE(old->tp_cdb_sc_blkmod); idx++) new->tp_cdb_sc_blkmod[idx] = 0; /************* JOURNALLING RELATED FIELDS ****************/ SWAP_SD4(jnl_alq); SWAP_SD4(jnl_deq); SWAP_SD4(jnl_buffer_size); SWAP_SD4(jnl_before_image); new->jnl_state = jnl_closed; if (!new_is_native) SWAP_SD4(jnl_state); SWAP_SD4(jnl_file_len); SWAP_SD4(autoswitchlimit); SWAP_SD4(epoch_interval); SWAP_SD4(alignsize); SWAP_SD4(jnl_sync_io); SWAP_SD4(yield_lmt); SWAP_SD4(turn_around_point); SWAP_SD8(jnl_eovtn); /************* INTERRUPTED RECOVERY RELATED FIELDS ****************/ SWAP_SD8(intrpt_recov_resync_seqno); SWAP_SD4(intrpt_recov_tp_resolve_time); SWAP_SD4(recov_interrupted); SWAP_SD4(intrpt_recov_jnl_state); SWAP_SD4(intrpt_recov_repl_state); /************* TRUNCATE RELATED FIELDS ****************/ SWAP_SD4(v6_before_trunc_total_blks); SWAP_SD4(v6_after_trunc_total_blks); SWAP_SD4(v6_before_trunc_free_blocks); /************* POTENTIALLY LARGE CHARACTER ARRAYS **************/ /************* ENCRYPTION-RELATED FIELDS **************/ SWAP_SD4(non_null_iv); SWAP_SD4(v6_encryption_hash_cutoff); SWAP_SD8(encryption_hash2_start_tn); /************* BG_TRC_REC RELATED FIELDS ***********/ /* Convert "bg_trc_rec_cntr_filler" and "bg_trc_rec_tn_filler" */ # define TAB_BG_TRC_REC(A,B) new->B##_cntr = (bg_trc_rec_cntr) 0; new->B##_tn = (bg_trc_rec_tn) 0; # include "tab_bg_trc_rec.h" # undef TAB_BG_TRC_REC /************* DB_CSH_ACCT_REC RELATED FIELDS ***********/ /* Convert "db_csh_acct_rec_filler_4k" */ # define TAB_DB_CSH_ACCT_REC(A,B,C) new->A.cumul_count = new->A.curr_count = 0; # include "tab_db_csh_acct_rec.h" # undef TAB_DB_CSH_ACCT_REC /************* GVSTATS_REC RELATED FIELDS ***********/ # define TAB_GVSTATS_REC(COUNTER,TEXT1,TEXT2) SWAP_SD8(gvstats_rec.COUNTER); # include "tab_gvstats_rec.h" # undef TAB_GVSTATS_REC /************* FIELDS EXTENDED IN V7 HEADER ********************************/ SWAP_SD8(master_map_len); SWAP_SD8(start_vbn); SWAP_SD8(last_inc_bkup_last_blk); SWAP_SD8(last_com_bkup_last_blk); SWAP_SD8(last_rec_bkup_last_blk); SWAP_SD8(reorg_restart_block); SWAP_SD8(reorg_upgrd_dwngrd_restart_block); SWAP_SD8(blks_to_upgrd); SWAP_SD8(blks_to_upgrd_subzero_error); SWAP_SD8(before_trunc_total_blks); SWAP_SD8(after_trunc_total_blks); SWAP_SD8(before_trunc_free_blocks); SWAP_SD8(encryption_hash_cutoff); /************* INTERRUPTED RECOVERY RELATED FIELDS continued ****************/ for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) SWAP_SD8(intrpt_recov_resync_strm_seqno[idx]); /************* DB CREATION AND UPGRADE CERTIFICATION FIELDS ***********/ SWAP_SD4_CAST(creation_db_ver, enum db_ver); SWAP_SD4_CAST(creation_mdb_ver, enum mdb_ver); SWAP_SD4_CAST(certified_for_upgrade_to, enum db_ver); /************* SECSHR_DB_CLNUP RELATED FIELDS (now moved to node_local) ***********/ /* "secshr_ops_index_filler" and "secshr_ops_array_filler" need not be converted (they are fillers) */ /********************************************************/ /* "next_upgrd_warn" isn't valid since the database is fully_upgraded and the latch values differ by platform * and since we don't know where the db will be used, we will ignore it. */ SWAP_SD4(is_encrypted); SWAP_SD4(db_trigger_cycle); /************* SUPPLEMENTARY REPLICATION INSTANCE RELATED FIELDS ****************/ for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) { SWAP_SD8(strm_reg_seqno[idx]); SWAP_SD8(save_strm_reg_seqno[idx]); } /************* MISCELLANEOUS FIELDS ****************/ SWAP_SD4(freeze_on_fail); SWAP_SD4(span_node_absent); SWAP_SD4(maxkeysz_assured); SWAP_SD4(hasht_upgrade_needed); SWAP_SD4(defer_allocate); SWAP_SD4(problksplit); } void v6_endian_header(v6_sgmnt_data *new, v6_sgmnt_data *old, boolean_t new_is_native) { int idx; time_t ctime; /* convert the header back down to V6 format */ db_header_dwnconv((sgmnt_data_ptr_t)old); db_header_dwnconv((sgmnt_data_ptr_t)new); /************* MOSTLY STATIC DATABASE STATE FIELDS **************************/ SWAP_SD4(blk_size); SWAP_SD4(master_map_len); SWAP_SD4(bplmap); SWAP_SD4(start_vbn); SWAP_SD4_CAST(acc_meth, enum db_acc_method); SWAP_SD4(max_bts); SWAP_SD4(n_bts); SWAP_SD4(bt_buckets); SWAP_SD4(reserved_bytes); SWAP_SD4(max_rec_size); SWAP_SD4(max_key_size); SWAP_SD4(lock_space_size); SWAP_SD4(extension_size); SWAP_SD4(def_coll); SWAP_SD4(def_coll_ver); SWAP_SD4(write_fullblk); SWAP_SD4(statsdb_allocation); SWAP_SD4(std_null_coll); SWAP_SD4(null_subs); SWAP_SD4(free_space); SWAP_SD4(mutex_spin_parms.mutex_hard_spin_count); /* gdsbt.h */ SWAP_SD4(mutex_spin_parms.mutex_sleep_spin_count); SWAP_SD4(mutex_spin_parms.mutex_spin_sleep_mask); SWAP_SD4(mutex_spin_parms.mutex_que_entry_space_size); SWAP_SD4(max_update_array_size); SWAP_SD4(max_non_bm_update_array_size); /* SWAP_SD4(file_corrupt); is set in main routine */ SWAP_SD4_CAST(minor_dbver, enum mdb_ver); SWAP_SD4(jnl_checksum); SWAP_SD4(wcs_phase2_commit_wait_spincnt); SWAP_SD4_CAST(last_mdb_ver, enum mdb_ver); /************* FIELDS SET AT CREATION TIME ********************************/ /* SWAP_SD4(createinprogress); checked above as FALSE so no need */ assert(SIZEOF(int4) == SIZEOF(old->creation_time4)); time(&ctime); assert(SIZEOF(ctime) >= SIZEOF(int4)); new->creation_time4 = (int4)ctime;/* Take only lower order 4-bytes of current time */ if (!new_is_native) SWAP_SD4(creation_time4); /************* FIELDS USED BY TN WARN PROCESSING *************************/ SWAP_SD8(max_tn); SWAP_SD8(max_tn_warn); /************* FIELDS SET BY MUPIP BACKUP/REORG *************************/ SWAP_SD8(last_inc_backup); SWAP_SD8(last_com_backup); SWAP_SD8(last_rec_backup); SWAP_SD4(last_inc_bkup_last_blk); SWAP_SD4(last_com_bkup_last_blk); SWAP_SD4(last_rec_bkup_last_blk); SWAP_SD4(reorg_restart_block); /************* FIELDS SET WHEN DB IS OPEN ********************************/ new->image_count = 0; /* should be zero when db is not open so reset it unconditionally */ new->freeze = 0; /* should be zero when db is not open so reset it unconditionally */ SWAP_SD4(kill_in_prog); SWAP_SD4(abandoned_kills); /************* FIELDS USED IN V4 <==> V5 COMPATIBILITY MODE ****************/ SWAP_SD8(tn_upgrd_blks_0); SWAP_SD8(desired_db_format_tn); SWAP_SD8(reorg_db_fmt_start_tn); SWAP_SD4(reorg_upgrd_dwngrd_restart_block); SWAP_SD4(blks_to_upgrd); SWAP_SD4(blks_to_upgrd_subzero_error); SWAP_SD4_CAST(desired_db_format, enum db_ver); SWAP_SD4(fully_upgraded); /* should be TRUE */ assert(new->fully_upgraded); /* Since the source database is fully upgraded and since all RECYCLED blocks will be marked as FREE, we are guaranteed * there are NO V4 format block that is too full to be upgraded to V5 format (i.e. will cause DYNUPGRDFAIL error). */ new->db_got_to_v5_once = TRUE; /* should be TRUE */ new->opened_by_gtmv53 = TRUE; /* should be TRUE */ /************* FIELDS RELATED TO DB TRANSACTION HISTORY *****************************/ SWAP_SD8(trans_hist.curr_tn); SWAP_SD8(trans_hist.early_tn); SWAP_SD8(trans_hist.last_mm_sync); SWAP_SD8(trans_hist.mm_tn); SWAP_SD4(trans_hist.lock_sequence); SWAP_SD4(trans_hist.ccp_jnl_filesize); SWAP_SD4(trans_hist.total_blks); SWAP_SD4(trans_hist.free_blocks); /************* FIELDS RELATED TO WRITE CACHE FLUSHING *******************************/ SWAP_SD4(flush_time[0]); SWAP_SD4(flush_time[1]); SWAP_SD4(flush_trigger); SWAP_SD4(n_wrt_per_flu); SWAP_SD4(wait_disk_space); SWAP_SD4(defer_time); SWAP_SD4(mumps_can_bypass); SWAP_SD4(epoch_taper); SWAP_SD4(epoch_taper_time_pct); SWAP_SD4(epoch_taper_jnl_pct); SWAP_SD4(asyncio); /************* FIELDS Used for update process performance improvement. Some may go away in later releases ********/ SWAP_SD4(reserved_for_upd); SWAP_SD4(avg_blks_per_100gbl); SWAP_SD4(pre_read_trigger_factor); SWAP_SD4(writer_trigger_factor); /************* FIELDS USED ONLY BY UNIX ********************************/ /* Solaris complains about swapping -1 * assert(INVALID_SEMID == GTM_BYTESWAP_32(INVALID_SEMID)); * assert(INVALID_SHMID == GTM_BYTESWAP_32(INVALID_SHMID)); */ assert(-1 == INVALID_SEMID); assert(-1 == INVALID_SHMID); if (new_is_native) { /* Since we have standalone access, reset volatile fields in the database file header */ new->semid = INVALID_SEMID; new->shmid = INVALID_SHMID; new->gt_sem_ctime.ctime = 0; new->gt_shm_ctime.ctime = 0; memset(new->machine_name, 0, MAX_MCNAMELEN); } /************* ACCOUNTING INFORMATION ********************************/ /************* CCP/RC RELATED FIELDS (CCP STUFF IS NOT USED CURRENTLY BY GT.M) *************/ SWAP_SD4(staleness[0]); SWAP_SD4(staleness[1]); SWAP_SD4(ccp_tick_interval[0]); SWAP_SD4(ccp_tick_interval[1]); SWAP_SD4(ccp_quantum_interval[0]); SWAP_SD4(ccp_quantum_interval[1]); SWAP_SD4(ccp_response_interval[0]); SWAP_SD4(ccp_response_interval[1]); SWAP_SD4(ccp_jnl_before); SWAP_SD4(clustered); SWAP_SD4(unbacked_cache); /* RC server related fields sb zero when not active */ SWAP_SD4(rc_srv_cnt); SWAP_SD4(dsid); SWAP_SD4(rc_node); /************* REPLICATION RELATED FIELDS ****************/ SWAP_SD8(reg_seqno); SWAP_SD8(pre_multisite_resync_seqno); /* Note some of the following names were added or renamed in V5.1 but should be of no issue for V5.0 builds * since we will be swapping unused fields. */ SWAP_SD8(zqgblmod_tn); SWAP_SD8(zqgblmod_seqno); new->repl_state = repl_closed; if (!new_is_native) SWAP_SD4(repl_state); SWAP_SD4(multi_site_open); /************* TP RELATED FIELDS ********************/ for (idx = 0; idx < ARRAYSIZE(old->tp_cdb_sc_blkmod); idx++) new->tp_cdb_sc_blkmod[idx] = 0; /************* JOURNALLING RELATED FIELDS ****************/ SWAP_SD4(jnl_alq); SWAP_SD4(jnl_deq); SWAP_SD4(jnl_buffer_size); SWAP_SD4(jnl_before_image); new->jnl_state = jnl_closed; if (!new_is_native) SWAP_SD4(jnl_state); SWAP_SD4(jnl_file_len); SWAP_SD4(autoswitchlimit); SWAP_SD4(epoch_interval); SWAP_SD4(alignsize); SWAP_SD4(jnl_sync_io); SWAP_SD4(yield_lmt); SWAP_SD4(turn_around_point); SWAP_SD8(jnl_eovtn); /************* INTERRUPTED RECOVERY RELATED FIELDS ****************/ SWAP_SD8(intrpt_recov_resync_seqno); SWAP_SD4(intrpt_recov_tp_resolve_time); SWAP_SD4(recov_interrupted); SWAP_SD4(intrpt_recov_jnl_state); SWAP_SD4(intrpt_recov_repl_state); /************* TRUNCATE RELATED FIELDS ****************/ SWAP_SD4(before_trunc_total_blks); SWAP_SD4(after_trunc_total_blks); SWAP_SD4(before_trunc_free_blocks); /************* POTENTIALLY LARGE CHARACTER ARRAYS **************/ /************* ENCRYPTION-RELATED FIELDS **************/ SWAP_SD4(non_null_iv); SWAP_SD4(encryption_hash_cutoff); SWAP_SD8(encryption_hash2_start_tn); /************* BG_TRC_REC RELATED FIELDS ***********/ /* Convert "bg_trc_rec_cntr_filler" and "bg_trc_rec_tn_filler" */ # define TAB_BG_TRC_REC(A,B) new->B##_cntr = (bg_trc_rec_cntr) 0; new->B##_tn = (bg_trc_rec_tn) 0; # include "tab_bg_trc_rec.h" # undef TAB_BG_TRC_REC /************* DB_CSH_ACCT_REC RELATED FIELDS ***********/ /* Convert "db_csh_acct_rec_filler_4k" */ # define TAB_DB_CSH_ACCT_REC(A,B,C) new->A.cumul_count = new->A.curr_count = 0; # include "tab_db_csh_acct_rec.h" # undef TAB_DB_CSH_ACCT_REC /************* GVSTATS_REC RELATED FIELDS ***********/ # define TAB_GVSTATS_REC(COUNTER,TEXT1,TEXT2) SWAP_SD8(gvstats_rec.COUNTER); # include "tab_gvstats_rec.h" # undef TAB_GVSTATS_REC /************* INTERRUPTED RECOVERY RELATED FIELDS continued ****************/ for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) SWAP_SD8(intrpt_recov_resync_strm_seqno[idx]); /************* DB CREATION AND UPGRADE CERTIFICATION FIELDS ***********/ SWAP_SD4_CAST(creation_db_ver, enum db_ver); SWAP_SD4_CAST(creation_mdb_ver, enum mdb_ver); SWAP_SD4_CAST(certified_for_upgrade_to, enum db_ver); /************* SECSHR_DB_CLNUP RELATED FIELDS (now moved to node_local) ***********/ /* "secshr_ops_index_filler" and "secshr_ops_array_filler" need not be converted (they are fillers) */ /********************************************************/ /* "next_upgrd_warn" isn't valid since the database is fully_upgraded and the latch values differ by platform * and since we don't know where the db will be used, we will ignore it. */ SWAP_SD4(is_encrypted); SWAP_SD4(db_trigger_cycle); /************* SUPPLEMENTARY REPLICATION INSTANCE RELATED FIELDS ****************/ for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) { SWAP_SD8(strm_reg_seqno[idx]); SWAP_SD8(save_strm_reg_seqno[idx]); } /************* MISCELLANEOUS FIELDS ****************/ SWAP_SD4(freeze_on_fail); SWAP_SD4(span_node_absent); SWAP_SD4(maxkeysz_assured); SWAP_SD4(hasht_upgrade_needed); SWAP_SD4(defer_allocate); SWAP_SD4(problksplit); /* convert the header back up to V7 format */ db_header_upconv((sgmnt_data_ptr_t)old); db_header_upconv((sgmnt_data_ptr_t)new); } int4 endian_process(endian_info *info, sgmnt_data *new_data, sgmnt_data *old_data, boolean_t override_specified) { /* returns 0 for success This routine based on mubinccpy and dbcertify_scan_phase */ blk_hdr_ptr_t bp_new, bp_native, bp_old; block_id blk_num, totblks, last_blk_written, mm_offset, lbm_done, busy_done, recycled_done, free_done, lbmap_cnt; boolean_t new_is_native; boolean_t blk_needs_encryption, non_null_iv, use_old_key, use_new_key; char *blk_buff[2], *lbmap_buff[2], *errptr; int save_errno, bsize, lbm_status; int buff_native, buff_old, buff_new; int lm_offset; int crypt_blk_size, gtmcrypt_errno; int4 startvbn; int4 bplmap; off_t dbptr; trans_num encryption_hash2_start_tn; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (info->endian_native) { /* use fields from old header */ bplmap = old_data->bplmap; totblks = old_data->trans_hist.total_blks; lbmap_cnt = (totblks + bplmap - 1) / bplmap; bsize = old_data->blk_size; startvbn = old_data->start_vbn; non_null_iv = old_data->non_null_iv; encryption_hash2_start_tn = old_data->encryption_hash2_start_tn; buff_native = buff_old = 0; new_is_native = FALSE; buff_new = 1; } else { /* use swapped fields from new header */ bplmap = new_data->bplmap; totblks = new_data->trans_hist.total_blks; lbmap_cnt = (totblks + bplmap - 1) / bplmap; bsize = new_data->blk_size; startvbn = new_data->start_vbn; non_null_iv = new_data->non_null_iv; encryption_hash2_start_tn = new_data->encryption_hash2_start_tn; buff_native = buff_new = 1; new_is_native = TRUE; buff_old = 0; } /* Asserting that the database is either not using any encryption or has set the database_fn_len field for future uses. */ assert((0 != info->database_fn_len) || !USES_ANY_KEY(info)); dbptr = (off_t)BLK_ZERO_OFF(startvbn); info->tot_blks = totblks; info->bsize = bsize; info->startvbn = startvbn; info->non_null_iv = non_null_iv; info->encryption_hash2_start_tn = encryption_hash2_start_tn; blk_buff[0] = malloc(bsize); blk_buff[1] = malloc(bsize); lbmap_buff[0] = malloc(bsize); lbmap_buff[1] = malloc(bsize); blk_num = last_blk_written = lbm_done = busy_done = recycled_done = free_done = 0; for (mm_offset = 0; (mm_offset < lbmap_cnt) && (blk_num < totblks); ++mm_offset) { /* for each local bit map */ assert(0 == (blk_num % bplmap)); /* check proper local bit map alignment */ LSEEKREAD(info->db_fd, dbptr, lbmap_buff[buff_old], bsize, save_errno); if (0 != save_errno) { free(blk_buff[0]); free(lbmap_buff[0]); free(blk_buff[1]); free(lbmap_buff[1]); errptr = (char *)STRERROR(save_errno); util_out_print("Error reading local bit map block !UL : !AZ", TRUE, blk_num, errptr); return save_errno; } memcpy(lbmap_buff[buff_new], lbmap_buff[buff_old], bsize); endian_cvt_blk_hdr((blk_hdr_ptr_t)lbmap_buff[buff_new], new_is_native, FALSE); assert(LCL_MAP_LEVL == ((blk_hdr_ptr_t)lbmap_buff[buff_native])->levl); /* set all recycled bits to free to avoid trouble if pre GDSV6 */ /* lm_offset 0 is the local bit map itself */ for (lm_offset = 1; lm_offset < bplmap && (blk_num + lm_offset) < totblks; lm_offset++) { GET_BM_STATUS(lbmap_buff[buff_new], lm_offset, lbm_status); if (BLK_RECYCLED == lbm_status) { SET_BM_STATUS(lbmap_buff[buff_new], lm_offset, BLK_FREE); recycled_done++; } else if (BLK_FREE == lbm_status) free_done++; /* count before changing recycled to free */ else assertpro(BLK_MAPINVALID != lbm_status); } if (info->inplace) { DB_LSEEKWRITE(NULL, ((unix_db_info *)NULL), NULL, info->db_fd, dbptr, lbmap_buff[buff_new], bsize, save_errno); } else LSEEKWRITE(info->outdb_fd, dbptr, lbmap_buff[buff_new], bsize, save_errno); if (0 != save_errno) { free(blk_buff[0]); free(lbmap_buff[0]); free(blk_buff[1]); free(lbmap_buff[1]); errptr = (char *)STRERROR(save_errno); util_out_print("Error writing local bit map block !UL : !AZ", TRUE, blk_num, errptr); return save_errno; } last_blk_written = blk_num; lbm_done++; /* lm_offset 0 is the local bit map itself */ for (lm_offset = 1, dbptr += bsize, blk_num++; (blk_num < totblks) && (lm_offset < bplmap); lm_offset++, dbptr += bsize, blk_num++) { /* for each local bit map entry - there will only be busy or free blocks in the (new) database */ GET_BM_STATUS(lbmap_buff[buff_new], lm_offset, lbm_status); if (BLK_BUSY == lbm_status) { LSEEKREAD(info->db_fd, dbptr, blk_buff[buff_old], bsize, save_errno); if (0 != save_errno) { free(blk_buff[0]); free(lbmap_buff[0]); free(blk_buff[1]); free(lbmap_buff[1]); errptr = (char *)STRERROR(save_errno); util_out_print("Error reading block !UL : !AZ", TRUE, blk_num, errptr); return save_errno; } memcpy(blk_buff[buff_new], blk_buff[buff_old], bsize); bp_new = (blk_hdr_ptr_t)blk_buff[buff_new]; bp_old = (blk_hdr_ptr_t)blk_buff[buff_old]; bp_native = (blk_hdr_ptr_t)blk_buff[buff_native]; if (new_is_native) endian_cvt_blk_hdr(bp_new, new_is_native, BLK_RECYCLED == lbm_status); assert((bp_new->bsiz <= bsize) && (bp_new->bsiz >= SIZEOF(*bp_new))); crypt_blk_size = MIN(bsize, bp_new->bsiz) - (SIZEOF(*bp_new)); blk_needs_encryption = BLK_NEEDS_ENCRYPTION(bp_new->levl, crypt_blk_size); if (blk_needs_encryption) { use_new_key = NEEDS_NEW_KEY(info, bp_new->tn); use_old_key = IS_ENCRYPTED(info->is_encrypted); if (use_new_key || use_old_key) { ASSERT_ENCRYPTION_INITIALIZED; /* We are doing decryption in place on blk_buff[bp_new] because a) the original data * from blk_buff[bp_old] has already been copied to it, and b) blk_buff[bp_new] is * where we want the decrypted data to be. Note, however, that we are using the * block header from the old buffer as IV because the block header from the new * buffer might have already been endian-converted and thus become incompatible with * the encrypted data we are operating on. */ if (use_new_key) { GTMCRYPT_DECRYPT(NULL, TRUE, info->encr_handles.encr_key_handle2, (char *)(bp_new + 1), crypt_blk_size, NULL, bp_old, SIZEOF(blk_hdr), gtmcrypt_errno); } else { GTMCRYPT_DECRYPT(NULL, non_null_iv, info->encr_handles.encr_key_handle, (char *)(bp_new + 1), crypt_blk_size, NULL, bp_old, SIZEOF(blk_hdr), gtmcrypt_errno); } if (0 != gtmcrypt_errno) { GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, info->database_fn_len, info->database_fn); return gtmcrypt_errno; } } } if (!new_is_native) endian_cvt_blk_hdr(bp_new, new_is_native, BLK_RECYCLED == lbm_status); endian_cvt_blk_recs(info, (char *)bp_new, bp_native, blk_num); if (blk_needs_encryption && (use_new_key || use_old_key)) { /* Now that we have both the block header and the records in the right endianness, * both stored in blk_buff[bp_new], we proceed to reencrypt the data in place. */ if (use_new_key) { GTMCRYPT_ENCRYPT(NULL, TRUE, info->encr_handles.encr_key_handle2, (char *)(bp_new + 1), crypt_blk_size, NULL, bp_new, SIZEOF(blk_hdr), gtmcrypt_errno); } else { GTMCRYPT_ENCRYPT(NULL, non_null_iv, info->encr_handles.encr_key_handle, (char *)(bp_new + 1), crypt_blk_size, NULL, bp_new, SIZEOF(blk_hdr), gtmcrypt_errno); } if (0 != gtmcrypt_errno) { GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, info->database_fn_len, info->database_fn); return gtmcrypt_errno; } } if (info->inplace) { DB_LSEEKWRITE(NULL, ((unix_db_info *)NULL), NULL, info->db_fd, dbptr, blk_buff[buff_new], bsize, save_errno); } else LSEEKWRITE(info->outdb_fd, dbptr, blk_buff[buff_new], bsize, save_errno); if (0 != save_errno) { free(blk_buff[0]); free(lbmap_buff[0]); free(blk_buff[1]); free(lbmap_buff[1]); errptr = (char *)STRERROR(save_errno); util_out_print("Error writing block !UL : !AZ", TRUE, blk_num, errptr); return save_errno; } last_blk_written = info->last_blk_cvt = blk_num; if (BLK_BUSY == lbm_status) busy_done++; } } } /* If MUPIP ENDIANCVT -OVERRIDE is specified and source db minor_dbver is not same as that of the endiancvt, * then skip the EOF block writing as otherwise we could end up creating a db with dbver pointing to a * pre-V63001 GT.M version but with a GDS-block EOF block (instead of a 512-byte EOF block which is the * norm pre-V63001) effectively creating a db reflecting bits and pieces of multiple GT.M versions. */ if ((last_blk_written < totblks) && (!override_specified || (GDSMVCURR == old_data->minor_dbver))) { /* need to create last disk block */ dbptr = ((off_t)BLK_ZERO_OFF(startvbn) + (off_t)totblks * bsize); save_errno = db_write_eof_block(NULL, info->inplace ? info->db_fd : info->outdb_fd, bsize, dbptr, &(TREF(dio_buff))); if (0 != save_errno) { free(blk_buff[0]); free(lbmap_buff[0]); free(blk_buff[1]); free(lbmap_buff[1]); errptr = (char *)STRERROR(save_errno); util_out_print("Error writing last block : !AZ", TRUE, errptr); return save_errno; } } free(lbmap_buff[0]); free(lbmap_buff[1]); free(blk_buff[0]); free(blk_buff[1]); if (NULL != info->dtblk.buff) { free(info->dtblk.buff); info->dtblk.buff = NULL; info->dtblk.blkid = -1; } if (NULL != info->dtblk.dtrbuff) { free(info->dtblk.dtrbuff); info->dtblk.dtrbuff = NULL; } return 0; } void endian_cvt_blk_hdr(blk_hdr_ptr_t blkhdr, boolean_t new_is_native, boolean_t make_empty) { /* convert fields in block header */ uint4 v15bsiz, v15levl, bsiz; v15_trans_num v15tn; trans_num tn; unsigned short bver; v15bsiz = blkhdr->bver; blkhdr->bver = GTM_BYTESWAP_16(blkhdr->bver); if (new_is_native) v15bsiz = blkhdr->bver; /* now it is native endian */ if (SIZEOF(v15_blk_hdr) <= v15bsiz) { /* old format block so it must be recycled and not upgraded */ assert(FALSE); /* should have been changed to FREE above */ assert(make_empty); v15levl = ((v15_blk_hdr *)blkhdr)->levl; v15tn = ((v15_blk_hdr *)blkhdr)->tn; assert(SIZEOF(char) == SIZEOF(blkhdr->levl)); /* no need to swap */ blkhdr->levl = v15levl; bver = GDSV6; bsiz = SIZEOF(v15_blk_hdr); if (!new_is_native) { tn = ((v15_blk_hdr *)blkhdr)->tn; /* expand while native; */ blkhdr->tn = GTM_BYTESWAP_64(tn); bsiz = GTM_BYTESWAP_32(bsiz); bver = GTM_BYTESWAP_16(bver); } else { v15tn = GTM_BYTESWAP_32(v15tn); blkhdr->tn = v15tn; /* expand while native */ } blkhdr->bver = bver; blkhdr->bsiz = bsiz; return; } assert(SIZEOF(char) == SIZEOF(blkhdr->levl)); /* no need to swap */ if (make_empty) blkhdr->bsiz = new_is_native ? SIZEOF(blk_hdr) : GTM_BYTESWAP_32(SIZEOF(blk_hdr)); else blkhdr->bsiz = GTM_BYTESWAP_32(blkhdr->bsiz); blkhdr->tn = GTM_BYTESWAP_64(blkhdr->tn); return; } char *endian_read_dbblk(endian_info *info, block_id blk_to_get) { blk_hdr_ptr_t bp; boolean_t use_old_key, use_new_key; boolean_t blk_is_native; char *buff; char *inbuf; int save_errno; int gtmcrypt_errno; int req_dec_blk_size; off_t blkoff; if (DIR_ROOT == blk_to_get) { if (NULL == info->dtblk.dtrbuff) { /* need to really get it */ info->dtblk.dtrbuff = malloc(info->bsize); buff = info->dtblk.dtrbuff; } else /* already have it */ return info->dtblk.dtrbuff; } else { if (NULL == info->dtblk.buff) { info->dtblk.buff = malloc(info->bsize); info->dtblk.blkid = -1; /* invalid */ } else if (blk_to_get == info->dtblk.blkid) return info->dtblk.buff; /* already have it */ buff = info->dtblk.buff; } blkoff = ((off_t)BLK_ZERO_OFF(info->startvbn) + (off_t)blk_to_get * info->bsize); LSEEKREAD(info->db_fd, blkoff, buff, info->bsize, save_errno); if (0 != save_errno) { return NULL; } if (info->inplace && info->last_blk_cvt >= blk_to_get) blk_is_native = !info->endian_native; /* already converted */ else blk_is_native = info->endian_native; /* still original endian */ /* We need to save a copy of the block header before it is endian-converted because after the conversion it will become * incompatible with the encrypted data. */ use_new_key = USES_NEW_KEY(info); use_old_key = IS_ENCRYPTED(info->is_encrypted); if (use_new_key || use_old_key) memcpy(pvt_crypt_buf.addr, buff, SIZEOF(blk_hdr)); if (!blk_is_native) endian_cvt_blk_hdr((blk_hdr_ptr_t)buff, TRUE, FALSE); use_new_key = use_new_key && (((blk_hdr_ptr_t)buff)->tn >= info->encryption_hash2_start_tn); if (use_new_key || use_old_key) { bp = (blk_hdr_ptr_t)buff; assert((bp->bsiz <= info->bsize) && (bp->bsiz >= SIZEOF(*bp))); req_dec_blk_size = MIN(info->bsize, bp->bsiz) - (SIZEOF(*bp)); if (BLK_NEEDS_ENCRYPTION(bp->levl, req_dec_blk_size)) { ASSERT_ENCRYPTION_INITIALIZED; inbuf = (char *)(bp + 1); /* For IV use the block header we copied prior to endian conversion. */ if (use_new_key) { GTMCRYPT_DECRYPT(NULL, TRUE, info->encr_handles.encr_key_handle2, inbuf, req_dec_blk_size, NULL, pvt_crypt_buf.addr, SIZEOF(blk_hdr), gtmcrypt_errno); } else { GTMCRYPT_DECRYPT(NULL, info->non_null_iv, info->encr_handles.encr_key_handle, inbuf, req_dec_blk_size, NULL, pvt_crypt_buf.addr, SIZEOF(blk_hdr), gtmcrypt_errno); } if (0 != gtmcrypt_errno) { GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, info->database_fn, info->database_fn_len); return NULL; } } } if (DIR_ROOT == blk_to_get) info->dtblk.dtrnative = blk_is_native; else { info->dtblk.blkid = blk_to_get; info->dtblk.native = blk_is_native; } return buff; } void endian_find_key(endian_info *info, end_gv_key *targ_gv_key, char *rec_p, int rec_len, int blk_levl, boolean_t long_blk_id) { /* find the key for the record and set targ_gv_key */ char *rec_key; int cmpc; int tmp_cmpc; unsigned char *targ_key; if (bstar_rec_size(long_blk_id) == rec_len && 0 < blk_levl) { /* no key for star key records */ targ_gv_key->end = 0; return; } cmpc = EVAL_CMPC((rec_hdr_ptr_t)rec_p); targ_key = targ_gv_key->key + cmpc; rec_key = rec_p + SIZEOF(rec_hdr); while (TRUE) { for (; *rec_key; ++targ_key, ++rec_key) *targ_key = *rec_key; if (0 == *(rec_key + 1)) { /* end of key since two nulls */ *targ_key++ = 0; /* end the target key */ *targ_key = 0; /* with two as well */ targ_gv_key->end = (unsigned int)(targ_key - targ_gv_key->key); break; } /* Else, copy subscript separator char and keep scanning */ *targ_key++ = *rec_key++; assert((rec_key - rec_p) < rec_len); } assert(cmpc <= targ_gv_key->end); return; } boolean_t endian_match_key(end_gv_key *gv_key1, int blk_levl, end_gv_key *gv_key2) { int key1_len, key2_len, key_len; unsigned char *key1, *key2; key1 = gv_key1->key; key2 = gv_key2->key; key1_len = gv_key1->end + 1; if (1 == key1_len && 0 < blk_levl) return TRUE; /* a star key record is greater than the second key */ assert(1 < key1_len); key2_len = gv_key2->end + 1; assert(1 < key2_len); /* should never look for star key */ key_len = MIN(key1_len, key2_len); for (; key_len; key1++, key2++, key_len--) { if (*key1 != *key2) break; } if ((0 == key_len && key1_len >= key2_len) || (0 != key_len && *key1 > *key2)) return TRUE; return FALSE; } /* find the directory tree leaf block for a key to check if a level zero block is in the DT or GVT. The need for this should be rare so little attempt is made at efficiency other than caching the DT root block since it is the start of all searches. */ block_id endian_find_dtblk(endian_info *info, end_gv_key *gv_key) { block_id blk_to_get, blk_ptr; block_id_32 temp_32_id; boolean_t blk_is_native, long_blk_id; char *buff, *blk_top, *rec_p; end_gv_key found_gv_key; int save_errno, rec_len, blk_levl, ptroffset; int tmp_cmpc; unsigned char found_gv_key_buff[MAX_KEY_SZ + 1]; unsigned short us_rec_len; info->dtblk.count++; found_gv_key.key = found_gv_key_buff; found_gv_key.end = found_gv_key.top = found_gv_key.gvn_len = 0; blk_to_get = DIR_ROOT; buff = endian_read_dbblk(info, blk_to_get); /* will use dtrbuff after first time */ if (!buff) return -1; blk_is_native = info->dtblk.dtrnative; while (TRUE) { blk_top = buff + ((blk_hdr_ptr_t)buff)->bsiz; blk_levl = ((blk_hdr_ptr_t)buff)->levl; long_blk_id = IS_64_BLK_ID(buff); rec_p = buff + SIZEOF(blk_hdr); while (rec_p < blk_top) { GET_USHORT(us_rec_len, &((rec_hdr *)rec_p)->rsiz); if (!blk_is_native) us_rec_len = GTM_BYTESWAP_16(us_rec_len); rec_len = us_rec_len; if (0 >= rec_len) return -1; if ((0 != blk_levl) && (bstar_rec_size(long_blk_id) == rec_len)) { /* down to the next level */ READ_BLK_ID(long_blk_id, &blk_ptr, (sm_uc_ptr_t)(rec_p + SIZEOF(rec_hdr))); if (!blk_is_native) { if(long_blk_id) blk_ptr = BLK_ID_64_BYTESWAP(blk_ptr); else { assert(blk_ptr == (block_id_32)blk_ptr); temp_32_id = (block_id_32)blk_ptr; temp_32_id = BLK_ID_32_BYTESWAP(temp_32_id); blk_ptr = temp_32_id; } assert(0 <= blk_ptr); } if (blk_ptr > info->tot_blks) return -1; /* past end of database */ blk_to_get = blk_ptr; break; } endian_find_key(info, &found_gv_key, rec_p, rec_len, blk_levl, long_blk_id); if (endian_match_key(&found_gv_key, blk_levl, gv_key)) { if (0 == blk_levl) return blk_to_get; /* found dtleaf block we are looking for */ ptroffset = found_gv_key.end - EVAL_CMPC((rec_hdr *)rec_p) + 1; READ_BLK_ID(long_blk_id, &blk_ptr, (sm_uc_ptr_t)(rec_p + SIZEOF(rec_hdr) + ptroffset)); if (!blk_is_native) { if(long_blk_id) blk_ptr = BLK_ID_64_BYTESWAP(blk_ptr); else { assert(blk_ptr == (block_id_32)blk_ptr); temp_32_id = (block_id_32)blk_ptr; temp_32_id = BLK_ID_32_BYTESWAP(temp_32_id); blk_ptr = temp_32_id; } assert(0 <= blk_ptr); } if (blk_ptr > info->tot_blks) return -1; /* past end of database */ blk_to_get = blk_ptr; break; } rec_p = rec_p + rec_len; } if (0 == blk_levl) return -1; /* we didn't find what should have been there */ buff = endian_read_dbblk(info, blk_to_get); if (!buff) return -1; blk_is_native = info->dtblk.native; } } void endian_cvt_blk_recs(endian_info *info, char *new_block, blk_hdr_ptr_t blkhdr, block_id blknum) { /* convert records in new_block, could be data, index, or directory use converted header fields from blkhdr which is in native format */ block_id ptr2blk, ptr2blk_swap, dtblk; block_id_32 temp_32_id; boolean_t new_is_native, have_dt_blk; boolean_t have_gvtleaf; boolean_t long_blk_id; end_gv_key gv_key; int rec1_len, rec1_gvn_len, rec2_cmpc, rec2_len; int tmp_cmpc; int blk_levl; int blk_id_sz; unsigned short us_rec_len, us_rec_len_swap; unsigned char *rec1_ptr, *rec2_ptr, *blk_top, *key_top; if (SIZEOF(v15_blk_hdr) <= blkhdr->bver) return; /* pre V5 version so ignore records */ new_is_native = !info->endian_native; blk_levl = blkhdr->levl; long_blk_id = IS_64_BLK_ID(blkhdr); blk_id_sz = SIZEOF_BLK_ID(long_blk_id); blk_top = (unsigned char *)new_block + blkhdr->bsiz; rec1_ptr = (unsigned char *)new_block + SIZEOF(blk_hdr); GET_USHORT(us_rec_len, &((rec_hdr *)rec1_ptr)->rsiz); rec1_len = new_is_native ? GTM_BYTESWAP_16(us_rec_len) : us_rec_len; /* May not need this whole thing, just do what dump_record does and check if block_id follows keys - but what if data is 4 chars */ /* need to check there really is a 2nd record */ rec2_ptr = rec1_ptr + rec1_len; if (rec2_ptr < blk_top) rec2_cmpc = EVAL_CMPC((rec_hdr *)rec2_ptr); else rec2_cmpc = -1; /* no second record */ /* Determine type of block (DT lvl 0, DT lvl !0, GVT lvl 0, GVT lvl !0) Rules for checking (from dbcertify_scan_phase.c): 1) If compression count of 2nd record is zero, it *must* be a directory tree block. This is a fast path check to avoid doing the strlen in the second check. 2) If compression count of second record is less than or equal to the length of the global variable name, then this must be a directory tree block. The reason this check works is a GVT index or data block would have same GVN in the 2nd record as the first so the compression count would be a minimum of (length(GVN) + 1). The "+ 1" is for the terminating null of the GVN. dbcertify only cares about too full blocks so the above rules may not apply in all other cases. endian cvt only care about index (levl > 0), dtleaf, or gvtleaf. */ have_dt_blk = FALSE; if (0 == rec2_cmpc) have_dt_blk = TRUE; else { rec1_gvn_len = STRLEN((char *)rec1_ptr + SIZEOF(rec_hdr)); if (-1 != rec2_cmpc && rec2_cmpc <= rec1_gvn_len) have_dt_blk = TRUE; } if (have_dt_blk) have_gvtleaf = FALSE; /* Could be dtleaf, dtindex, or dtroot but not gvtleaf */ else if (-1 != rec2_cmpc) { /* more than one record */ if (0 == blk_levl) have_gvtleaf = TRUE; /* gdsblk_gvtleaf only sure if more than one record */ else /* ambiguous whether gvtroot or gvtindex */ have_gvtleaf = FALSE; } else if (blk_levl) have_gvtleaf = FALSE; /* gdsblk_gvtindex at least not leaf */ else { /* only one record and level is 0 */ /* if subscripts at level 0, it must be gvtleaf */ /* find key_top to check if it has four/eight bytes (depends on V6 or V7 block) * of data which look like a valid pointer */ /* note dtleaf may have collation info after the pointer */ for (key_top = rec1_ptr + SIZEOF(rec_hdr); *key_top && key_top < (rec1_ptr + rec1_len); key_top++) ; if (*++key_top) have_gvtleaf = TRUE; /* gdsblk_gvtleaf subscript so must be */ else if (blk_id_sz <= ((rec1_ptr + rec1_len) - ++key_top) && (blk_id_sz + MAX_SPEC_TYPE_LEN) >= ((rec1_ptr + rec1_len) - key_top)) { /* record value long enough for block_id but not longer than block_id plus collation information */ READ_BLK_ID(long_blk_id, &ptr2blk, key_top); if (new_is_native) { if(long_blk_id) ptr2blk = BLK_ID_64_BYTESWAP(ptr2blk); else { assert(ptr2blk == (block_id_32)ptr2blk); temp_32_id = (block_id_32)ptr2blk; temp_32_id = BLK_ID_32_BYTESWAP(temp_32_id); ptr2blk = temp_32_id; } assert(0 <= ptr2blk); } if (ptr2blk <= info->tot_blks) { /* might be a pointer so need to check the hard way */ gv_key.key = rec1_ptr + SIZEOF(rec_hdr); gv_key.top = gv_key.end = gv_key.gvn_len = (unsigned int)(key_top - gv_key.key - 1); dtblk = endian_find_dtblk(info, &gv_key); if (dtblk != blknum) have_gvtleaf = TRUE; /* blknum is not DT level 0 */ else have_gvtleaf = FALSE; /* DT level 0 has pointers */ } else /* points after last block so not a block_id */ have_gvtleaf = TRUE; /* gdsblk_gvtleaf should be data */ } else have_gvtleaf = TRUE; /* gdsblk_gvtleaf too short for pointer or too long with collation info */ } for (; rec1_ptr < blk_top; rec1_ptr += rec1_len) { GET_USHORT(us_rec_len, &((rec_hdr *)rec1_ptr)->rsiz); us_rec_len_swap = GTM_BYTESWAP_16(us_rec_len); PUT_USHORT(&((rec_hdr *)rec1_ptr)->rsiz, us_rec_len_swap); rec1_len = new_is_native ? us_rec_len_swap : us_rec_len; /* leave cmpc and cmpc2 bytes alone */ if (!have_gvtleaf) { /* fix up pointers as well */ key_top = rec1_ptr + SIZEOF(rec_hdr); if (bstar_rec_size(long_blk_id) != rec1_len || 0 == blk_levl) { /* find pointer after subscripts */ for ( ; key_top < (rec1_ptr + rec1_len); ) if (!*key_top++ && !*key_top++) break; /* 2 nulls is end of subscripts */ } else assert((key_top + blk_id_sz == blk_top) || blk_levl); /* must be last if not leaf */ assert((key_top + blk_id_sz) <= (rec1_ptr + rec1_len)); if(long_blk_id) { GET_BLK_ID_64(ptr2blk, key_top); ptr2blk_swap = BLK_ID_64_BYTESWAP(ptr2blk); PUT_BLK_ID_64(key_top, ptr2blk_swap); } else { GET_BLK_ID_32(ptr2blk, key_top); assert(ptr2blk == (block_id_32)ptr2blk); temp_32_id = (block_id_32)ptr2blk; temp_32_id = BLK_ID_32_BYTESWAP(temp_32_id); ptr2blk_swap = temp_32_id; PUT_BLK_ID_32(key_top, ptr2blk_swap); } #ifdef DEBUG if (new_is_native) ptr2blk = ptr2blk_swap; assert(0 <= ptr2blk); assert(ptr2blk <= info->tot_blks); #endif } } } fis-gtm-V7.0-005/sr_unix/mupip_endiancvt.h0000755000032200000250000000110514342376330017377 0ustar librarygtc/**************************************************************** * * * Copyright 2006 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. * * * ****************************************************************/ #ifndef MUPIP_ENDIANCVT_INCLUDED #define MUPIP_ENDIANCVT_INCLUDED void mupip_endiancvt(void); #endif /* MUPIP_ENDIANCVT_INCLUDED */ fis-gtm-V7.0-005/sr_unix/mupip_exit.c0000755000032200000250000000225714342376330016401 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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 "iosp.h" #include "error.h" #include "mupip_exit.h" #include "gtmmsg.h" GBLREF boolean_t mupip_exit_status_displayed; void mupip_exit(int4 stat) { int4 tmp_severity; if (stat != SS_NORMAL) { if (error_condition != stat) /* If message not already put out.. */ { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) stat); tmp_severity = SEVMASK(stat); } else tmp_severity = severity; /* Make sure give an rc when we exit */ if (SUCCESS != tmp_severity && INFO != tmp_severity) stat = (((stat & UNIX_EXIT_STATUS_MASK) != 0) ? stat : -1); else stat = 0; } mupip_exit_status_displayed = TRUE; EXIT(stat); } fis-gtm-V7.0-005/sr_unix/mupip_exit_handler.c0000644000032200000250000001456514342376330020100 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ /* The mupip exit handler called on all exits from mupip */ #include "mdef.h" #include "gtm_ipc.h" #include "gtm_inet.h" #include "gtm_signal.h" #include #include #include #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_time.h" #include "gtmsiginfo.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "repl_sem.h" #include "gtmsource.h" #include "gtmrecv.h" #include "error.h" #include "gtmimagename.h" #include "eintr_wrappers.h" #include "repl_log.h" #include "gt_timer.h" #include "util.h" #include "mutex.h" #include "gv_rundown.h" #include "mu_term_setup.h" #include "mupip_exit.h" #include "print_exit_stats.h" #include "ftok_sems.h" #include "db_ipcs_reset.h" #include "gtm_unistd.h" #include "jnl.h" #include "buddy_list.h" #include "hashtab_mname.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "muprec.h" #include "gtmmsg.h" #include "secshr_db_clnup.h" #include "gtmio.h" #include "repl_shutdcode.h" #include "io.h" #include "gtmsource_srv_latch.h" #include "gtmcrypt.h" #include "relinkctl.h" GBLREF boolean_t mupip_jnl_recover; GBLREF boolean_t need_core; GBLREF boolean_t created_core; GBLREF unsigned int core_in_progress; GBLREF boolean_t exit_handler_active; GBLREF boolean_t skip_exit_handler; GBLREF recvpool_addrs recvpool; GBLREF int pool_init; GBLREF boolean_t is_src_server; GBLREF boolean_t is_rcvr_server; GBLREF boolean_t is_updproc; GBLREF uint4 is_updhelper; GBLREF FILE *gtmsource_log_fp; GBLREF FILE *gtmrecv_log_fp; GBLREF FILE *updproc_log_fp; GBLREF FILE *updhelper_log_fp; GBLREF int gtmsource_log_fd; GBLREF int gtmrecv_log_fd; GBLREF int updproc_log_fd; GBLREF int updhelper_log_fd; GBLREF gd_region *gv_cur_region; GBLREF jnl_gbls_t jgbl; GBLREF upd_helper_entry_ptr_t helper_entry; GBLREF uint4 dollar_tlevel; GBLREF uint4 process_id; void close_repl_logfiles(void); void mupip_exit_handler(void) { char err_log[1024]; FILE *fp; boolean_t files_closed = TRUE; if (exit_handler_active || skip_exit_handler) /* Skip exit handling if specified or if exit handler already active */ return; exit_handler_active = TRUE; SET_PROCESS_EXITING_TRUE; jgbl.dont_reset_gbl_jrec_time = jgbl.forw_phase_recovery = FALSE; if (jgbl.mupip_journal) { files_closed = mur_close_files(); mupip_jnl_recover = FALSE; } CANCEL_TIMERS; /* Cancel all unsafe timers - No unpleasant surprises */ /* Note we call secshr_db_clnup() with the flag NORMAL_TERMINATION even in an error condition * here because we know at this point that we aren't in the middle of a transaction but we may * be holding crit in one or more regions and/or we could have other odds/ends to cleanup. */ secshr_db_clnup(NORMAL_TERMINATION); if (is_updhelper && NULL != helper_entry) /* haven't had a chance to cleanup, must be an abnormal exit */ { helper_entry->helper_shutdown = ABNORMAL_SHUTDOWN; helper_entry->helper_pid = 0; /* vacate my slot */ helper_entry = NULL; } if (recvpool.recvpool_ctl) { /* In case we hold the write_updated_ctl mutex, we need to release it before detaching the recvpool. * The robust mutex handling can't work if the memory no longer exists in the process' address space, * which is why we have to do it manually. * Ignore errors, as we may not have held it, and we're exiting anyway. * Only the update process acquires this lock, so limit the unlock accordingly. */ if (is_updproc) pthread_mutex_unlock(&recvpool.recvpool_ctl->write_updated_ctl); SHMDT(recvpool.recvpool_ctl); recvpool.recvpool_ctl = NULL; } WITH_CH(exi_ch, gv_rundown(), 0); /* also takes care of detaching from the journal pool */ relinkctl_rundown(TRUE, TRUE); /* decrement relinkctl-attach & rtnobj-reference counts */ /* Log the exit of replication servers. In case they are exiting abnormally, their log file pointers * might not be set up. In that case, use "stderr" for logging. */ if (is_src_server) { fp = (NULL != gtmsource_log_fp) ? gtmsource_log_fp : stderr; repl_log(fp, TRUE, TRUE, "Source server exiting...\n\n"); } else if (is_rcvr_server) { fp = (NULL != gtmrecv_log_fp) ? gtmrecv_log_fp : stderr; repl_log(fp, TRUE, TRUE, "Receiver server exiting...\n\n"); } else if (is_updproc) { fp = (NULL != updproc_log_fp) ? updproc_log_fp : stderr; repl_log(fp, TRUE, TRUE, "Update process exiting...\n\n"); } else if (is_updhelper) { fp = (NULL != updhelper_log_fp) ? updhelper_log_fp : stderr; repl_log(fp, TRUE, TRUE, "Helper exiting...\n\n"); } else mu_reset_term_characterstics(); /* the replication servers use files for output/error, not terminal */ flush_pio(); util_out_close(); close_repl_logfiles(); print_exit_stats(); io_rundown(RUNDOWN_EXCEPT_STD); GTMCRYPT_CLOSE; if (need_core && !created_core) { ++core_in_progress; DUMP_CORE; /* This will not return */ } if (!files_closed) UNDERSCORE_EXIT(EXIT_FAILURE); } void close_repl_logfiles() { int rc; if (FD_INVALID != gtmsource_log_fd) CLOSEFILE_RESET(gtmsource_log_fd, rc); /* resets "gtmsource_log_fd" to FD_INVALID */ if (NULL != gtmsource_log_fp) FCLOSE(gtmsource_log_fp, rc); if (FD_INVALID != gtmrecv_log_fd) CLOSEFILE_RESET(gtmrecv_log_fd, rc); /* resets "gtmrecv_log_fd" to FD_INVALID */ if (NULL != gtmrecv_log_fp) FCLOSE(gtmrecv_log_fp, rc); if (FD_INVALID != updproc_log_fd) { assert(updproc_log_fd != updhelper_log_fd); CLOSEFILE_RESET(updproc_log_fd, rc); /* resets "updproc_log_fd" to FD_INVALID */ } if (NULL != updproc_log_fp) FCLOSE(updproc_log_fp, rc); if (FD_INVALID != updhelper_log_fd) CLOSEFILE_RESET(updhelper_log_fd, rc); /* resets "updhelper_log_fd" to FD_INVALID */ if (NULL != updhelper_log_fp) FCLOSE(updhelper_log_fp, rc); } fis-gtm-V7.0-005/sr_unix/mupip_ftok.c0000755000032200000250000001412214342376330016365 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_ipc.h" #include "gtm_unistd.h" #include "gtm_fcntl.h" /* for O_RDONLY */ #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_sizeof.h" #include "cli.h" #include "parse_file.h" #include "gtm_stat.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmio.h" #include "iosp.h" #include "eintr_wrappers.h" #include "repl_instance.h" #include "mupip_ftok.h" #include "mupip_exit.h" #include "file_head_read.h" /* for file_head_read() prototype */ #include "is_gdid.h" /* for filename_to_id() prototype */ GBLREF boolean_t in_mupip_ftok; /* Used by an assert in repl_inst_read */ error_def(ERR_MUPCLIERR); error_def(ERR_MUNOACTION); error_def(ERR_TEXT); /* * By default. this reads file header and prints the semaphore/shared memory id * This is needed because GTM/MUPIP creates semaphore for database access control and * shared memory segment using IPC_CREATE flag. GTM saves that ids in database file header. * In case of a crash we may need to get those ids. Specially for debugging/testing this is very useful * Format is filename ftok::ftok for the -only case, bases 10 & 16 respectively, * and described by the optional headers below for the others. */ void mupip_ftok(void) { boolean_t fd, jnlpool, only, recvpool, showheader; char fn[MAX_FN_LEN + 1], instfilename[MAX_FN_LEN + 1], replf[MAX_FN_LEN + 1]; gd_id fid; int index, ispool, semid, shmid; int4 id, status; key_t semkey; mstr file; parse_blk pblk; repl_inst_hdr repl_instance; sgmnt_data header; sm_uc_ptr_t fid_ptr, fid_top; unsigned int full_len; unsigned short fn_len; /* cli library expects unsigned short */ DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (FALSE == cli_get_int("ID", &id)) id = GTM_ID; only = (CLI_PRESENT == cli_present("ONLY")); jnlpool = (CLI_PRESENT == cli_present("JNLPOOL")); recvpool = (CLI_PRESENT == cli_present("RECVPOOL")); ispool = jnlpool + recvpool; if (ispool) /* forget any parameters and use parms_cnt as loop control*/ TREF(parms_cnt) = ispool + 1; showheader = (!cli_negated("HEADER")); index = (only && ispool); /* if only, skip the instance file */ for (; index < TREF(parms_cnt); index++) { /* in order to handle multiple files, this loop directly uses the array built by cli */ if (ispool) { /* ispool idicates we're precessing based on the replication instance file */ if (only == index) { /* first pass - process the instance file */ if (!repl_inst_get_name(instfilename, &full_len, SIZEOF(instfilename), issue_rts_error, NULL)) assertpro(NULL == instfilename); /* otherwise, repl_inst_get_name issues rts_error */ in_mupip_ftok = TRUE; /* this flag implicitly relies on mupip ftok being once and done */ repl_inst_read(instfilename, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr)); in_mupip_ftok = FALSE; /* no condition hander to reset this in case of error - see comment above */ memset(&pblk, 0, SIZEOF(pblk)); pblk.buff_size = MAX_FN_LEN; pblk.buffer = replf; pblk.fop = F_SYNTAXO; file.addr = instfilename; file.len = full_len; status = parse_file(&file, &pblk); /* this gets us to directory, name and extension */ fn_len = pblk.b_dir + pblk.b_name + pblk.b_ext; memcpy(fn, pblk.l_dir, fn_len); fn[fn_len] = 0; semkey = FTOK(fn, REPLPOOL_ID); semid = shmid = -1; /* not relevant for the file */ } if (jnlpool && (1 == index)) { /* goes first if also -recvpool */ semid = repl_instance.jnlpool_semid; shmid = repl_instance.jnlpool_shmid; fn_len = SIZEOF("jnlpool"); strncpy(fn, "jnlpool", fn_len); } else if (recvpool && ((jnlpool ? 2 : 1) == index)) { /* last or sole */ semid = repl_instance.recvpool_semid; shmid = repl_instance.recvpool_shmid; fn_len = SIZEOF("recvpool"); strncpy(fn, "recvpool", fn_len); } } else { /* not instance file based */ strncpy(fn, TAREF1(parm_ary, index), MAX_FN_LEN); semkey = FTOK(fn, id); assert(semkey); if (!only) { /* try as a database file */ OPENFILE(fn, O_RDONLY, fd); /* if OPEN works, file_head_read takes care of close if other issues */ if ((FD_INVALID == fd) || (!file_head_read(fn, &header, SIZEOF(header)))) { FPRINTF(stderr, "%s is not a database file\n",fn); FPRINTF(stderr, "This and any subsequent files are treated as -only\n"); only = TRUE; } else { semid = header.semid; shmid = header.shmid; } } } if (!only || ispool) { if (showheader) { FPRINTF(stderr, "%20s :: %23s :: %23s :: %23s :: %34s\n", "File", "Semaphore Id", "Shared Memory Id", "FTOK Key", "FileId"); FPRINTF(stderr, "-----------------------------------------------------------------------------"); FPRINTF(stderr, "--------------------------------------------------------------\n"); showheader = FALSE; } if (!ispool || !index) { /* it's a file */ FPRINTF(stderr, "%20s :: %10d [0x%.8x] :: %10d [0x%.8x] :: %10d [0x%.8x] :: 0x", fn, semid, semid, shmid, shmid, semkey, semkey); fid_ptr = (sm_uc_ptr_t)&fid; filename_to_id((gd_id_ptr_t)fid_ptr, fn); for (fid_top = fid_ptr + SIZEOF(fid) ; fid_ptr < fid_top; fid_ptr++) FPRINTF(stderr, "%.2x", *(sm_uc_ptr_t)fid_ptr); } else FPRINTF(stderr, "%20s :: %10d [0x%.8x] :: %10d [0x%.8x]", fn, semid, semid, shmid, shmid); FPRINTF(stderr, "\n"); } else /* simple legacy format */ FPRINTF(stderr, "%20s :: %10d [ 0x%8x ]\n", fn, semkey, semkey); } mupip_exit(SS_NORMAL); } fis-gtm-V7.0-005/sr_unix/mupip_ftok.h0000755000032200000250000000106014342376330016367 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef MUPIP_FTOK_INCLUDED #define MUPIP_FTOK_INCLUDED void mupip_ftok(void); #endif /* MUPIP_FTOK_INCLUDED */ fis-gtm-V7.0-005/sr_unix/mupip_hash.c0000644000032200000250000000336714342376330016353 0ustar librarygtc/**************************************************************** * * * Copyright 2014 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 "util.h" /* #include "cli.h" */ #include "gtmio.h" #include "gtm_threadgbl.h" #include "mmrhash.h" #include "mupip_hash.h" /* Display the 128-bit MurmurHash3 value(s) for the file(s) given on the command line. * For example, * % mupip hash foo.m bar.m * foo.m: 77c5e66fcaedebf32199d87b0f6b6d80 * bar.m: 4b2a2ddc6803a30f4a769d4f6d9c8bd5 */ void mupip_hash(void) { int i, fd, status, size; hash128_state_t hash_state; gtm_uint16 hash; unsigned char hash_hex[32], readbuf[4096]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; for (i = 0; i < TREF(parms_cnt); i++) { OPENFILE(TAREF1(parm_ary, i), O_RDONLY, fd); if (fd < 0) { util_out_print("Error opening !AZ for read", TRUE, TAREF1(parm_ary, i)); continue; } size = 0; HASH128_STATE_INIT(hash_state, 0); while (1) { DOREADRL(fd, readbuf, SIZEOF(readbuf), status); if (-1 == status) { util_out_print("Error reading from !AZ", TRUE, TAREF1(parm_ary, i)); goto skipfile; } if (0 == status) break; gtmmrhash_128_ingest(&hash_state, readbuf, status); size += status; } gtmmrhash_128_result(&hash_state, size, &hash); gtmmrhash_128_hex(&hash, hash_hex); util_out_print("!AZ: !AD", TRUE, TAREF1(parm_ary, i), SIZEOF(hash_hex), hash_hex); skipfile: CLOSEFILE_RESET(fd, status); } } fis-gtm-V7.0-005/sr_unix/mupip_hash.h0000644000032200000250000000072614342376330016354 0ustar librarygtc/**************************************************************** * * * Copyright 2014 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. * * * ****************************************************************/ void mupip_hash(void); fis-gtm-V7.0-005/sr_unix/mupip_help.c0000755000032200000250000000111314342376330016346 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "mupip_help.h" void mupip_help(void) { /* This function is a STUB to avoid editting sr_port/mupip.h */ } fis-gtm-V7.0-005/sr_unix/mupip_load_reg_list.c0000644000032200000250000000512714342376330020233 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "muextr.h" #include "mupip_load_reg_list.h" GBLREF gd_addr *gd_header; GBLREF gd_region *db_init_region; error_def(ERR_RECLOAD); void insert_reg_to_list(gd_region **reg_list, gd_region *r_ptr, uint4 *num_of_reg) { int reg_iter; boolean_t reg_found = FALSE; for (reg_iter = 0; reg_iter < gd_header->n_regions; reg_iter++) { if (reg_list[reg_iter] == r_ptr) { reg_found = TRUE; break; } } if (!reg_found) reg_list[(*num_of_reg)++] = r_ptr; } boolean_t search_reg_list(gd_region **reg_list, gd_region *r_ptr, uint4 num_of_reg) { int reg_iter; for (reg_iter = 0; reg_iter < gd_header->n_regions; reg_iter++) { if (reg_list[reg_iter] == r_ptr) return TRUE; } return FALSE; } boolean_t check_db_status_for_global(mname_entry *gvname, int fmt, gtm_uint64_t *failed_record_count, gtm_uint64_t iter, gtm_uint64_t *first_failed_rec_count, gd_region **reg_list, uint4 num_of_reg) { gd_binding *map; ht_ent_mname *tabent; hash_table_mname *tab_ptr; char msg_buff[128]; gd_region *reg_ptr = NULL; gvnh_reg_t *gvnh_reg; gtm_uint64_t tmp_rec_count; tab_ptr = gd_header->tab_ptr; if (NULL == (tabent = lookup_hashtab_mname((hash_table_mname *)tab_ptr, gvname))) { map = gv_srch_map(gd_header, gvname->var_name.addr, gvname->var_name.len, SKIP_BASEDB_OPEN_TRUE); reg_ptr = map->reg.addr; } else { gvnh_reg = (gvnh_reg_t *)tabent->value; assert(NULL != gvnh_reg); reg_ptr = gvnh_reg->gd_reg; } if ((reg_ptr == db_init_region) || search_reg_list(reg_list, reg_ptr, num_of_reg)) { (*failed_record_count) += (1 + (MU_FMT_GO == fmt)); return FALSE; } else { tmp_rec_count = iter - 1; if (tmp_rec_count > *first_failed_rec_count) SNPRINTF(msg_buff, SIZEOF(msg_buff), "%lld to %lld", *first_failed_rec_count, tmp_rec_count ); else SNPRINTF(msg_buff, SIZEOF(msg_buff), "%lld", tmp_rec_count ); *first_failed_rec_count = 0; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_RECLOAD, 2, LEN_AND_STR(msg_buff)); } return TRUE; } fis-gtm-V7.0-005/sr_unix/mupip_load_reg_list.h0000644000032200000250000000175214342376330020240 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018 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. * * * ****************************************************************/ #ifndef MUPIP_LOAD_REG_LIST_INCLUDED #define MUPIP_LOAD_REG_LIST_INCLUDED void insert_reg_to_list(gd_region **reg_list, gd_region *r_ptr, uint4 *num_of_reg); boolean_t search_reg_list(gd_region **reg_list, gd_region *r_ptr, uint4 num_of_reg); boolean_t check_db_status_for_global(mname_entry *gvname, int fmt, gtm_uint64_t *failed_record_count, gtm_uint64_t iter, gtm_uint64_t *first_failed_rec_count, gd_region **reg_list, uint4 num_of_reg); #endif /* MUPIP_LOAD_REG_LIST_INCLUDED */ fis-gtm-V7.0-005/sr_unix/mupip_load_triggerfile.h0000644000032200000250000000122714342376330020730 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef MUPIP_LOAD_TRIGGERFILE_INCLUDED #define MUPIP_LOAD_TRIGGERFILE_INCLUDED void mupip_load_triggerfile(char *trigger_filename, int trigger_filename_len, char p_m); #endif /* MUPIP_LOAD_TRIGGERFILE_INCLUDED */ fis-gtm-V7.0-005/sr_unix/mupip_quit.c0000755000032200000250000000115214342376330016403 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "gtm_stdio.h" #include "iosp.h" #include "mupip_exit.h" #include "mupip_quit.h" void mupip_quit(void) { PRINTF("\n"); mupip_exit(SS_NORMAL); } fis-gtm-V7.0-005/sr_unix/mupip_rctldump.h0000644000032200000250000000110114342376330017247 0ustar librarygtc/**************************************************************** * * * Copyright 2014 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. * * * ****************************************************************/ #ifndef MUPIP_RCTLDUMP_INCLUDED #define MUPIP_RCTLDUMP_INCLUDED void mupip_rctldump(void); #endif /* MUPIP_RCTLDUMP_INCLUDED */ fis-gtm-V7.0-005/sr_unix/mupip_reorg_encrypt.c0000644000032200000250000011007514342376330020305 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2021 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 "gdsroot.h" #include "gdsbt.h" #include "gdskill.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "gdscc.h" #include "buddy_list.h" #include "tp.h" #include "gdsblk.h" #include "gdsblkops.h" #include "cli.h" #include "mu_getlst.h" #include "mupip_exit.h" #include "util.h" #include "targ_alloc.h" #include "wcs_flu.h" #include "t_qread.h" #include "gdsbml.h" #include "cert_blk.h" #include "t_begin.h" #include "t_retry.h" #include "t_write_map.h" #include "t_write.h" #include "t_end.h" #include "sleep_cnt.h" #include "gtm_c_stack_trace.h" #include "is_proc_alive.h" #include "gtmsecshr.h" #include "wcs_backoff.h" #include "wcs_sleep.h" #include "gtm_sem.h" #include "gtm_semutils.h" #include "eintr_wrapper_semop.h" #include "do_semop.h" #include "ftok_sems.h" #include "have_crit.h" #include "mupip_reorg_encrypt.h" #include "t_abort.h" #include "interlock.h" #include "repl_msg.h" /* for gtmsource.h */ #include "gtmsource.h" /* for jnlpool_addrs_ptr_t */ GBLREF bool error_mupip; GBLREF bool mu_ctrlc_occurred; GBLREF bool mu_ctrly_occurred; GBLREF char *update_array; /* for the BLK_INIT/BLK_SEG/BLK_ADDR macros */ GBLREF char *update_array_ptr; /* for the BLK_INIT/BLK_SEG/BLK_ADDR macros */ GBLREF cw_set_element cw_set[]; /* create write set */ GBLREF boolean_t mu_reorg_process; GBLREF gd_region *ftok_sem_reg; GBLREF gd_region *gv_cur_region; GBLREF gv_namehead *gv_target; GBLREF gv_namehead *gv_target_list; GBLREF inctn_detail_t inctn_detail; /* holds detail to fill in to inctn jnl record */ GBLREF inctn_opcode_t inctn_opcode; GBLREF int4 gv_keysize; GBLREF jnl_gbls_t jgbl; GBLREF uint4 mu_reorg_encrypt_in_prog; /* Non-zero if MUPIP REORG ENCRYPT is in progress. The numeric value is * needed to set a special temporary indication to a dsk_read call on a * local buffer. */ GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF tp_region *grlist; GBLREF uint4 process_id; GBLREF uint4 update_array_size; /* for the BLK_INIT/BLK_SEG/BLK_ADDR macros */ GBLREF uint4 update_trans; GBLREF unsigned char cw_map_depth; GBLREF unsigned char cw_set_depth; GBLREF unsigned char rdfail_detail; GBLREF sgmnt_addrs *reorg_encrypt_restart_csa; error_def(ERR_BUFFLUFAILED); error_def(ERR_BUFRDTIMEOUT); error_def(ERR_CRITSEMFAIL); error_def(ERR_CRYPTNOKEY); error_def(ERR_DBFILERR); error_def(ERR_DBNOREGION); error_def(ERR_DBRDONLY); error_def(ERR_DSEBLKRDFAIL); error_def(ERR_JNLEXTEND); error_def(ERR_MUNOACTION); error_def(ERR_MUNOFINISH); error_def(ERR_MUREORGFAIL); error_def(ERR_ENCRYPTCONFLT); error_def(ERR_MUREENCRYPTEND); error_def(ERR_MUREENCRYPTSTART); error_def(ERR_MUREENCRYPTV4NOALLOW); error_def(ERR_REORGCTRLY); STATICFNDEF boolean_t wait_for_concurrent_reads(sgmnt_addrs *csa); STATICFNDEF void get_ftok_semaphore(gd_region *reg, sgmnt_addrs *csa); STATICFNDEF void release_ftok_semaphore(gd_region *reg, sgmnt_addrs *csa, sgmnt_data_ptr_t csd); STATICFNDEF void switch_journal_file(sgmnt_addrs *csa, sgmnt_data_ptr_t csd); #define EXIT_MUPIP_REORG(STATUS) \ MBSTART { \ mu_reorg_encrypt_in_prog = MUPIP_REORG_IN_PROG_FALSE; \ mupip_exit(STATUS); \ } MBEND /* Note: The below should not use MBSTART/MBEND as it does a "continue" (see MBSTART macro definition comment) */ #define CONTINUE_TO_NEXT_REGION(CSA, CSD, CNL, REG, REG_STATUS, STATUS, REORG_IN_PROG, HAVE_CRIT, ERROR, OPER, ...) \ { \ if (REORG_IN_PROG) \ { \ assert((CNL)->reorg_encrypt_pid == process_id); \ (CNL)->reorg_encrypt_pid = 0; \ } \ if (HAVE_CRIT) \ rel_crit(REG); \ release_ftok_semaphore(REG, CSA, CSD); \ OPER __VA_ARGS__; \ if (ERROR) \ STATUS = REG_STATUS = ERR_MUNOFINISH; \ continue; \ } #define REORG_IN_PROG_SET TRUE #define REORG_IN_PROG_NOT_SET FALSE #define HOLDING_CRIT TRUE #define NOT_HOLDING_CRIT FALSE #define IS_ERROR TRUE #define IS_NOT_ERROR FALSE /* Perform an online (re)encryption of the specified region(s) using the specified key. A lot of code in this module is modeled on * mu_reorg_upgrd_dwngrd.c. */ void mupip_reorg_encrypt(void) { char key[GTM_PATH_MAX], hash[GTMCRYPT_HASH_LEN]; char *db_name, *bml_lcl_buff; enum db_ver dummy_blkver; /* used for dsk_read call in order to simplify a critical path */ int db_name_len, gtmcrypt_errno, status, reg_status, status1; int reg_count, i, cycle, lcnt, bml_status; int4 blk_seg_cnt, blk_size, mapsize; /* needed for BLK_INIT,BLK_SEG and BLK_FINI macros */ unsigned short key_len; gd_region *reg; gtmcrypt_key_t *handles; boolean_t need_journal_switch; uint4 is_encrypted; srch_hist alt_hist; srch_blk_status *blkhist, bmlhist; tp_region *rptr; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; node_local_ptr_t cnl; block_id *blkid_ptr, start_blk, start_bmp, last_bmp, curbmp, curblk, total_blks; trans_num curr_tn, start_tn, blk_tn; sm_uc_ptr_t blkBase, bml_sm_buff; /* shared memory pointer to the bitmap global buffer */ cache_rec_ptr_t cr; blk_segment *bs1, *bs_ptr; sm_uc_ptr_t bptr, buff; blk_hdr new_hdr; unsigned char save_cw_set_depth; uint4 lcl_update_trans, pid, bptr_size; jnl_private_control *jpc; # ifdef DEBUG uint4 reencryption_count; block_id initial_blk_cnt; # endif unix_db_info *udi; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; mu_reorg_encrypt_in_prog = MUPIP_REORG_IN_PROG_TRUE; status = SS_NORMAL; /* Get the region(s) parameter. */ gvinit(); error_mupip = FALSE; mu_getlst("REG_NAME", SIZEOF(tp_region)); if (error_mupip) EXIT_MUPIP_REORG(ERR_MUNOACTION); else if (!grlist) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_DBNOREGION); EXIT_MUPIP_REORG(ERR_MUNOACTION); } /* Get the key parameter (we should not have come here unless the -ENCRYPT qualifier was supplied). */ assert(CLI_PRESENT == cli_present("ENCRYPT")); key_len = SIZEOF(key); if (!cli_get_str("ENCRYPT", key, &key_len)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CRYPTNOKEY); EXIT_MUPIP_REORG(ERR_MUNOACTION); } /* Even though we are only specifying one key to be potentially applied to multiple regions, we need to ensure that every * database file has an association with the specified key file in the encryption configuration. The fact that we are * allocating space for all of the encryption handles instead of one should not matter much in terms of memory because each * handles is quite small and we do not expect multiple REORG -ENCRYPT processes running concurrently. However, this saves * us the trouble of releasing the same handle multiple times in the loop and once more in the end. */ for (rptr = grlist, reg_count = 0; NULL != rptr; rptr = rptr->fPtr, reg_count++) ; handles = (gtmcrypt_key_t *)malloc(SIZEOF(gtmcrypt_key_t) * reg_count); memset(handles, 0, SIZEOF(gtmcrypt_key_t) * reg_count); for (i = 0, rptr = grlist; NULL != rptr; rptr = rptr->fPtr, i++) { reg = rptr->reg; db_name_len = reg->dyn.addr->fname_len; db_name = (char *)reg->dyn.addr->fname; /* Initialize encryption once. */ if (grlist == rptr) { INIT_PROC_ENCRYPTION(gtmcrypt_errno); if (0 != gtmcrypt_errno) { GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, db_name_len, db_name); EXIT_MUPIP_REORG(ERR_MUNOACTION); } } /* Obtain the hash of the specified key and initialize an encryption handle for it. It is somewhat wasteful to do it * for every region given that we only need one hash and the handle will also be shared among all regions, but, as * explained above, this ensures that the operation is provisioned for every database in the encryption * configuration file. */ GTMCRYPT_HASH_GEN(NULL, db_name_len, db_name, key_len, key, hash, gtmcrypt_errno); if (0 != gtmcrypt_errno) { GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, db_name_len, db_name); EXIT_MUPIP_REORG(ERR_MUNOACTION); } GTMCRYPT_INIT_BOTH_CIPHER_CONTEXTS(NULL, hash, db_name_len, db_name, handles[i], gtmcrypt_errno); if (0 != gtmcrypt_errno) { GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, db_name_len, db_name); EXIT_MUPIP_REORG(ERR_MUNOACTION); } } assert(DBKEYSIZE(MAX_KEY_SZ) == gv_keysize); /* no need to invoke GVKEYSIZE_INIT_IF_NEEDED macro */ gv_target = targ_alloc(gv_keysize, NULL, NULL); /* t_begin needs this initialized */ gv_target_list = NULL; memset(&alt_hist, 0, SIZEOF(alt_hist)); /* null-initialize history */ blkhist = &alt_hist.h[0]; bptr = NULL; bptr_size = 0; bml_lcl_buff = NULL; /* Start processing regions one by one. */ for (i = 0, rptr = grlist; NULL != rptr; rptr = rptr->fPtr, i++) { if (mu_ctrly_occurred || mu_ctrlc_occurred) break; reg_status = SS_NORMAL; reg = rptr->reg; if (i != 0) util_out_print("", TRUE); util_out_print("Region !AD : MUPIP REORG ENCRYPT started", TRUE, REG_LEN_STR(reg)); if (reg_cmcheck(reg)) { util_out_print("Region !AD : MUPIP REORG ENCRYPT cannot run across network", TRUE, REG_LEN_STR(reg)); status = reg_status = ERR_MUNOFINISH; continue; } mu_reorg_process = TRUE; /* gvcst_init will use this value to use gtm_poollimit settings. */ gvcst_init(reg, NULL); mu_reorg_process = FALSE; /* Note that db_init() does not release the access-control semaphore in case of MUPIP REORG -ENCRYPT (as determined * based on the mu_reorg_encrypt_in_prog variable), so no need to obtain it here. */ assert((ftok_sem_reg == reg) && (TRUE == FILE_INFO(reg)->grabbed_ftok_sem)); TP_CHANGE_REG(reg); /* sets gv_cur_region, cs_addrs, cs_data, which are needed by jnl_ensure_open and wcs_flu */ csa = cs_addrs; csd = cs_data; cnl = csa->nl; db_name_len = reg->dyn.addr->fname_len; db_name = (char *)reg->dyn.addr->fname; /* Access method stored in global directory and database file header might be different, in which case the database * setting prevails. */ if (dba_bg != REG_ACC_METH(reg)) { CONTINUE_TO_NEXT_REGION(csa, csd, cnl, reg, reg_status, status, REORG_IN_PROG_NOT_SET, NOT_HOLDING_CRIT, IS_ERROR, util_out_print, ("Region !AD : MUPIP REORG -ENCRYPT cannot continue as access method is not BG", TRUE, REG_LEN_STR(reg))); } /* The mu_getlst call above uses insert_region to create the grlist, which ensures that duplicate regions mapping to * the same db file correspond to only one grlist entry. */ assert(FALSE == reg->was_open); if (reg->read_only) { CONTINUE_TO_NEXT_REGION(csa, csd, cnl, reg, reg_status, status, REORG_IN_PROG_NOT_SET, NOT_HOLDING_CRIT, IS_ERROR, gtm_putmsg_csa, (CSA_ARG(csa) VARLSTCNT(4) ERR_DBRDONLY, 2, db_name_len, db_name)); } /* ++++++++++++++++++++++++++ IN CRIT ++++++++++++++++++++++++++ */ grab_crit(reg, WS_91); if (!csd->fully_upgraded) { CONTINUE_TO_NEXT_REGION(csa, csd, cnl, reg, reg_status, status, REORG_IN_PROG_NOT_SET, HOLDING_CRIT, IS_ERROR, gtm_putmsg_csa, (CSA_ARG(csa) VARLSTCNT(4) ERR_MUREENCRYPTV4NOALLOW, 2, DB_LEN_STR(reg))); } if (cnl->mupip_extract_count) { CONTINUE_TO_NEXT_REGION(csa, csd, cnl, reg, reg_status, status, REORG_IN_PROG_NOT_SET, HOLDING_CRIT, IS_ERROR, gtm_putmsg_csa, (CSA_ARG(csa) VARLSTCNT(8) ERR_ENCRYPTCONFLT, 6, RTS_ERROR_LITERAL("MUPIP REORG -ENCRYPT"), REG_LEN_STR(reg), DB_LEN_STR(reg))); } pid = cnl->reorg_encrypt_pid; if (pid && is_proc_alive(pid, 0)) { CONTINUE_TO_NEXT_REGION(csa, csd, cnl, reg, reg_status, status, REORG_IN_PROG_NOT_SET, HOLDING_CRIT, IS_ERROR, util_out_print, ("Region !AD : MUPIP REORG -ENCRYPT processes cannot operate concurrently. " "Concurrent REORG's pid is !UL", TRUE, REG_LEN_STR(reg), pid)); } if ((UNSTARTED == csd->encryption_hash_cutoff) && (0 != csd->encryption_hash2_start_tn)) { CONTINUE_TO_NEXT_REGION(csa, csd, cnl, reg, reg_status, status, REORG_IN_PROG_NOT_SET, HOLDING_CRIT, IS_ERROR, util_out_print, ("Region !AD : A previous MUPIP REORG -ENCRYPT has finished, but (re)encryption has" " not been marked complete. Run MUPIP SET -ENCRYPTIONCOMPLETE to do so", TRUE, REG_LEN_STR(reg))); } cnl->reorg_encrypt_pid = process_id; is_encrypted = csd->is_encrypted; if (IS_ENCRYPTED(is_encrypted)) { if ((!TO_BE_ENCRYPTED(is_encrypted) || (UNSTARTED == csd->encryption_hash_cutoff)) && !memcmp(hash, csd->encryption_hash, GTMCRYPT_HASH_LEN)) { CONTINUE_TO_NEXT_REGION(csa, csd, cnl, reg, reg_status, status, REORG_IN_PROG_SET, HOLDING_CRIT, IS_NOT_ERROR, util_out_print, ("Region !AD : Data already encrypted with the desired key - " "no change made", TRUE, REG_LEN_STR(reg))); } # ifdef DEBUG /* In case the database is at all encrypted now, we will need the encryption handle to decrypt existing * blocks. It should have been set up by gvcst_init(). Assert that. */ assert(NULL != csa->encr_key_handle); GTMCRYPT_HASH_CHK(csa, csd->encryption_hash, db_name_len, db_name, gtmcrypt_errno); assert(0 == gtmcrypt_errno); # endif } else if (!TO_BE_ENCRYPTED(is_encrypted)) { CONTINUE_TO_NEXT_REGION(csa, csd, cnl, reg, reg_status, status, REORG_IN_PROG_SET, HOLDING_CRIT, IS_ERROR, util_out_print, ("Region !AD : MUPIP REORG -ENCRYPT can only operate on encryptable databases", TRUE, REG_LEN_STR(reg))); } /* Wait for all the readers to complete to prevent them from attempting to digest an encrypted block or trying to * decrypt a block with a wrong key in case MUPIP REORG -ENCRYPT has concurrently processed that block. */ if (!wait_for_concurrent_reads(csa)) { CONTINUE_TO_NEXT_REGION(csa, csd, cnl, reg, reg_status, status, REORG_IN_PROG_SET, HOLDING_CRIT, IS_ERROR, util_out_print, ("Region !AD : Timed out waiting for concurrent reads to finish", TRUE, REG_LEN_STR(reg))); } /* Same for writers. Since we are going to switch the journal file, we might as well write the last EPOCH with the * old encryption settings. */ if (!wcs_flu(WCSFLU_WRITE_EPOCH)) { CONTINUE_TO_NEXT_REGION(csa, csd, cnl, reg, reg_status, status, REORG_IN_PROG_SET, HOLDING_CRIT, IS_ERROR, gtm_putmsg_csa, (CSA_ARG(csa) VARLSTCNT(6) ERR_BUFFLUFAILED, 4, LEN_AND_LIT("MUPIP REORG ENCRYPT"), db_name_len, db_name)); } curr_tn = csd->trans_hist.curr_tn; total_blks = csd->trans_hist.total_blks; # ifdef DEBUG initial_blk_cnt = total_blks; # endif blk_size = csd->blk_size; /* "blk_size" is used by the BLK_FINI macro */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_MUREENCRYPTSTART, 4, DB_LEN_STR(reg), process_id, &curr_tn); if (UNSTARTED == csd->encryption_hash_cutoff) { /* Database is either fully encrypted or unencrypted. Start encryption from the first block. */ memcpy(csd->encryption_hash2, hash, GTMCRYPT_HASH_LEN); csd->encryption_hash2_start_tn = curr_tn; start_tn = curr_tn; start_blk = 0; need_journal_switch = TRUE; } else { /* Encryption was already underway when it was stopped. Resume from the first unencrypted block. */ assert(UNSTARTED < csd->encryption_hash_cutoff); assert((0 < csd->encryption_hash2_start_tn) && (curr_tn >= csd->encryption_hash2_start_tn)); if (memcmp(hash, csd->encryption_hash2, GTMCRYPT_HASH_LEN)) { CONTINUE_TO_NEXT_REGION(csa, csd, cnl, reg, reg_status, status, REORG_IN_PROG_SET, HOLDING_CRIT, IS_ERROR, util_out_print, ("Region !AD : MUPIP REORG -ENCRYPT process was previously started with a " "different hash. Use the same hash to complete the operation.", TRUE, REG_LEN_STR(reg))); } start_tn = csd->encryption_hash2_start_tn; start_blk = csd->encryption_hash_cutoff; if (start_blk > total_blks) start_blk = total_blks; need_journal_switch = FALSE; } cnl->reorg_encrypt_cycle++; csd->encryption_hash_cutoff = start_blk; MARK_AS_TO_BE_ENCRYPTED(csd->is_encrypted); assert(NULL != csa->encr_ptr); COPY_ENC_INFO(csd, csa->encr_ptr, cnl->reorg_encrypt_cycle); memcpy(&csa->encr_key_handle2, &handles[i], SIZEOF(gtmcrypt_key_t)); if (JNL_ENABLED(csd) && need_journal_switch) switch_journal_file(csa, csd); DBG_RECORD_CRYPT_UPDATE(csd, csa, cnl, process_id); /* Before releasing crit, flush the file header to disk. */ if (!wcs_flu(WCSFLU_FLUSH_HDR)) /* wcs_flu assumes gv_cur_region is set (which it is in this routine) */ { CONTINUE_TO_NEXT_REGION(csa, csd, cnl, reg, reg_status, status, REORG_IN_PROG_SET, HOLDING_CRIT, IS_ERROR, gtm_putmsg_csa, (CSA_ARG(csa) VARLSTCNT(6) ERR_BUFFLUFAILED, 4, LEN_AND_LIT("MUPIP REORG ENCRYPT2"), db_name_len, db_name)); } rel_crit(reg); release_ftok_semaphore(reg, csa, csd); /* -------------------------- OUT OF CRIT -------------------------- */ # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_SLEEP_IN_MUPIP_REORG_ENCRYPT)) { if (2 > gtm_white_box_test_case_count) { util_out_print("Starting the sleep", TRUE); if (0 == gtm_white_box_test_case_count) { LONG_SLEEP(60); } } else reencryption_count = gtm_white_box_test_case_count; } # endif udi = FILE_INFO(reg); if (udi->fd_opened_with_o_direct) { DIO_BUFF_EXPAND_IF_NEEDED(udi, blk_size, &(TREF(dio_buff))); } else if ((NULL != bptr) && (bptr_size < blk_size)) { /* malloc/free "bptr" for each region as GDS block-size can be different */ free(bptr); bptr = NULL; } start_bmp = ROUND_DOWN2(start_blk, BLKS_PER_LMAP); last_bmp = ROUND_DOWN2(total_blks - 1, BLKS_PER_LMAP); for (curbmp = start_bmp; curbmp <= last_bmp; curbmp += BLKS_PER_LMAP) { if (mu_ctrly_occurred || mu_ctrlc_occurred) { reg_status = ERR_MUNOFINISH; break; } assert(!csa->now_crit); bml_sm_buff = t_qread(curbmp, (sm_int_ptr_t)&cycle, &cr); /* bring block into the cache outside of crit */ /* ++++++++++++++++++++++++++ IN CRIT ++++++++++++++++++++++++++ */ grab_crit_encr_cycle_sync(reg, WS_92); /* needed so t_qread does not return NULL below */ /* Safeguard against someone concurrently changing the database file header. It is unsafe to continue. */ if (start_tn != csd->encryption_hash2_start_tn) { rel_crit(reg); reg_status = ERR_MUNOFINISH; break; } if (total_blks > csd->trans_hist.total_blks) { total_blks = csd->trans_hist.total_blks; last_bmp = ROUND_DOWN2(total_blks - 1, BLKS_PER_LMAP); if (curbmp >= total_blks) { rel_crit(reg); assert(SS_NORMAL == reg_status); break; } } /* Before changing the hash cutoff, check if the journal file is not open in shared memory (possible * if a concurrent jnl switch happened due to a MUPIP SET JOURNAL or MUPIP BACKUP etc.). If so, open * the journal file in shared memory while the db and jnl headers have identical hash cutoff or else * whoever later opens the journal file first would get a CRYPTJNLMISMATCH error. */ if (JNL_ENABLED(csd)) { jpc = csa->jnl; if (0 == cnl->jnl_file.u.inode) { assert(JNL_FILE_SWITCHED(jpc)); ENSURE_JNL_OPEN(csa, reg); } assert(0 != cnl->jnl_file.u.inode); } csd->encryption_hash_cutoff = curbmp; bml_sm_buff = t_qread(curbmp, (sm_int_ptr_t)&cycle, &cr); /* now that in crit, note down stable buffer */ if (NULL == bml_sm_buff) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(1) ERR_DSEBLKRDFAIL); /* Take a copy of the shared memory bitmap buffer into process-private memory before releasing crit. We are * interested in those blocks that are currently marked as USED in the bitmap. It is possible that once we * release crit, concurrent updates change the bitmap state of those blocks. In that case, those updates * will take care of doing the (re)encryption of those blocks based on the encryption_hash2_start_tn value. */ if (NULL == bml_lcl_buff) bml_lcl_buff = malloc(BM_SIZE(BLKS_PER_LMAP)); memcpy(bml_lcl_buff, (blk_hdr_ptr_t)bml_sm_buff, BM_SIZE(BLKS_PER_LMAP)); if (FALSE == cert_blk(reg, curbmp, (blk_hdr_ptr_t)bml_lcl_buff, 0, FALSE, NULL)) { /* Certify the block while holding crit as cert_blk uses fields from file-header (shared memory). */ rel_crit(reg); assert(FALSE); /* In pro, skip ugprading/downgarding all blks in this unreliable local bitmap. */ util_out_print("Region !AD : Bitmap Block [0x!XL] has integrity errors. Skipping this bitmap.", TRUE, REG_LEN_STR(reg), curbmp); status = reg_status = ERR_MUNOFINISH; continue; } rel_crit(reg); /* -------------------------- OUT OF CRIT -------------------------- */ /* ------------------------------------------------------------------------ * (Re)encrypt all BUSY and REUSABLE blocks in the current bitmap * ------------------------------------------------------------------------ */ /* (total_blks - curbmp) can be cast because it should never be larger then BLKS_PER_LMAP */ DEBUG_ONLY(if(curbmp == last_bmp) assert(BLKS_PER_LMAP >= (total_blks - curbmp))); mapsize = (curbmp == last_bmp) ? (int4)(total_blks - curbmp) : BLKS_PER_LMAP; assert(0 != mapsize); for (lcnt = 0; lcnt < mapsize; lcnt++) { if (mu_ctrly_occurred || mu_ctrlc_occurred) { reg_status = ERR_MUNOFINISH; break; } GET_BM_STATUS(bml_lcl_buff, lcnt, bml_status); assert(BLK_MAPINVALID != bml_status); /* cert_blk ran clean so we don't expect invalid entries */ if (BLK_FREE == bml_status) continue; curblk = curbmp + lcnt; if (lcnt) { /* non-bitmap block */ /* read in block from disk into private buffer. don't pollute the cache yet */ if (!udi->fd_opened_with_o_direct) { if (NULL == bptr) { bptr = (sm_uc_ptr_t)malloc(blk_size); bptr_size = blk_size; } buff = bptr; } else buff = (sm_uc_ptr_t)(TREF(dio_buff)).aligned; mu_reorg_encrypt_in_prog = MUPIP_REORG_IN_PROG_LOCAL_DSK_READ; status1 = dsk_read(curblk, buff, &dummy_blkver, FALSE); mu_reorg_encrypt_in_prog = MUPIP_REORG_IN_PROG_TRUE; if (SS_NORMAL != status1) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), status1); util_out_print("Region !AD : Error occurred while reading block [0x!XL]", TRUE, REG_LEN_STR(reg), curblk); reg_status = ERR_MUNOFINISH; break; } blk_tn = ((blk_hdr_ptr_t)buff)->tn; if (blk_tn >= start_tn) continue; } /* Begin non-TP transaction to (re)encrypt the block. * The way we do that is by updating the block using a null update array. * Any update to a block will trigger an automatic (re)encryption of the block based on * the current fileheader's encryption_hash2_start_tn setting. */ t_begin(ERR_MUREORGFAIL, UPDTRNS_DB_UPDATED_MASK); for (; ;) { CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ curr_tn = csd->trans_hist.curr_tn; blkhist->cse = NULL; /* start afresh (do not use value from previous retry) */ if ((CDB_STAGNATE == t_tries) && (curblk >= csd->trans_hist.total_blks)) { /* Possible in case of a concurrent reorg truncate. * In this case, the reorg encrypt is done. * Set variables so we fall out of the nested for loops. */ assert(csa->now_crit); total_blks = csd->trans_hist.total_blks; last_bmp = ROUND_DOWN2(total_blks - 1, BLKS_PER_LMAP); assert(NULL == reorg_encrypt_restart_csa); t_abort(reg, csa); assert(!csa->now_crit); /* "t_abort" should have released crit */ assert(SS_NORMAL == reg_status); curbmp = last_bmp; /* ensure we "break" out of 1st level (outermost) for loop */ lcnt = mapsize; /* to break out of 2nd level (outer) for-loop */ break; } blkBase = t_qread(curblk, (sm_int_ptr_t)&blkhist->cycle, &blkhist->cr); if (NULL == blkBase) { t_retry((enum cdb_sc)rdfail_detail); continue; } blkhist->blk_num = curblk; blkhist->buffaddr = blkBase; new_hdr = *(blk_hdr_ptr_t)blkBase; blk_tn = new_hdr.tn; inctn_opcode = inctn_blkreencrypt; inctn_detail.blknum_struct.blknum = curblk; if (!lcnt) { /* Means a bitmap block. */ BLK_ADDR(blkid_ptr, SIZEOF(block_id), block_id); *blkid_ptr = 0; t_write_map(blkhist, (unsigned char *)blkid_ptr, curr_tn, 0); assert(&alt_hist.h[0] == blkhist); alt_hist.h[0].blk_num = 0; /* create empty history for bitmap block */ assert(update_trans); } else { /* Non-bitmap block. Fill in history for validation in t_end */ assert(curblk); /* we should never come here for block 0 (bitmap) */ assert(blkhist->blk_num == curblk); assert(blkhist->buffaddr == blkBase); blkhist->tn = curr_tn; alt_hist.h[1].blk_num = 0; /* Also need to pass the bitmap as history to detect if any concurrent M-kill * is freeing up the same USED block that we are trying to (re)encrypt OR if any * concurrent M-set is reusing the same RECYCLED block that we are trying to * (re)encrypt. Because of t_end currently not being able to validate a bitmap * without that simultaneously having a cse, we need to create a cse for the * bitmap that is used only for bitmap history validation, but should not be * used to update the contents of the bitmap block in bg_update. */ bmlhist.buffaddr = t_qread(curbmp, (sm_int_ptr_t)&bmlhist.cycle, &bmlhist.cr); if (NULL == bmlhist.buffaddr) { t_retry((enum cdb_sc)rdfail_detail); continue; } bmlhist.blk_num = curbmp; bmlhist.tn = curr_tn; GET_BM_STATUS(bmlhist.buffaddr, lcnt, bml_status); if (BLK_MAPINVALID == bml_status) { t_retry(cdb_sc_lostbmlcr); continue; } if ((BLK_FREE != bml_status) && (blk_tn < start_tn)) { /* Block still needs to be (re)encrypted; create cse. */ /* TODO: See if we can avoid the full-blown block write and instead make * "t_end" or "bg_update_phase1" only bump the tn but otherwise leave the * block alone. */ BLK_INIT(bs_ptr, bs1); BLK_SEG(bs_ptr, blkBase + SIZEOF(new_hdr), new_hdr.bsiz - SIZEOF(new_hdr)); BLK_FINI(bs_ptr, bs1); t_write(blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)blkBase)->levl, FALSE, FALSE, GDS_WRITE_PLAIN); /* The directory tree status for now is only used to determine whether * writing the block to snapshot file (see t_end_sysops.c). For * REORG -ENCRYPT process, the block is updated in a sequential way without * changing the gv_target. In this case, we assume the block is in directory * tree so as to have it written to the snapshot file. */ BIT_SET_DIR_TREE(cw_set[cw_set_depth - 1].blk_prior_state); /* Reset update_trans in case previous retry had set it to 0 */ update_trans = UPDTRNS_DB_UPDATED_MASK; if (BLK_RECYCLED == bml_status) { assert(cw_set[cw_set_depth - 1].mode == gds_t_write); cw_set[cw_set_depth - 1].mode = gds_t_write_recycled; /* We SET block as NOT RECYCLED, otherwise, the mm_update() * or bg_update_phase2 may skip writing it to snapshot file * when its level is 0 */ BIT_CLEAR_RECYCLED(cw_set[cw_set_depth - 1].blk_prior_state); } } else { /* Block got (re)encrypted by another process since we did the dsk_read or * this block became marked free in the bitmap. No need to update this * block; just call t_end for validation of both the non-bitmap block as * well as the bitmap block. Note down that this transaction is no longer * updating any blocks. */ update_trans = 0; } /* Need to put bit maps on the end of the cw set for concurrency checking. * We want to simulate t_write_map, except we want to update "cw_map_depth" * instead of "cw_set_depth". Hence the save and restore logic below. * This part of the code is similar to the one in mu_swap_blk.c */ save_cw_set_depth = cw_set_depth; assert(!cw_map_depth); t_write_map(&bmlhist, NULL, curr_tn, 0); /* will increment cw_set_depth */ cw_map_depth = cw_set_depth; /* set cw_map_depth to latest cw_set_depth */ cw_set_depth = save_cw_set_depth;/* restore cw_set_depth */ /* t_write_map simulation end */ } assert(SIZEOF(lcl_update_trans) == SIZEOF(update_trans)); lcl_update_trans = update_trans; /* take a copy before t_end modifies it */ if ((trans_num)0 != t_end(&alt_hist, NULL, TN_NOT_SPECIFIED)) { # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_SLEEP_IN_MUPIP_REORG_ENCRYPT) && (2 <= gtm_white_box_test_case_count)) { reencryption_count--; if (0 == reencryption_count) { reg_status = ERR_MUNOFINISH; break; } } # endif assert(csd == cs_data); if (!lcl_update_trans) assert(lcnt); break; } assert(csd == cs_data); } # ifdef DEBUG if (SS_NORMAL != reg_status) break; /* this takes into account the WBTEST_SLEEP_IN_MUPIP_REORG_ENCRYPT case above */ # endif } if (SS_NORMAL != reg_status) break; } if (SS_NORMAL == reg_status) { get_ftok_semaphore(reg, csa); grab_crit(reg, WS_93); /* Wait for all the readers to complete to prevent them from attempting to digest an * encrypted block or decrypt a block with a wrong key in case MUPIP REORG -ENCRYPT has * concurrently processed that block. */ if (!wait_for_concurrent_reads(csa)) { util_out_print("Region !AD : Timed out waiting for concurrent reads to finish2", TRUE, REG_LEN_STR(reg)); reg_status = ERR_MUNOFINISH; break; } /* Same for writers. */ if (!wcs_flu(WCSFLU_WRITE_EPOCH)) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_BUFFLUFAILED, 4, LEN_AND_LIT("MUPIP REORG ENCRYPT3"), db_name_len, db_name); reg_status = ERR_MUNOFINISH; break; } /* We are not resetting encryption_hash2_start_tn because we do not want the * database to be reencryptable before the user had a chance to back it up. */ csd->encryption_hash_cutoff = UNSTARTED; csd->non_null_iv = TRUE; SET_AS_ENCRYPTED(csd->is_encrypted); memcpy(csd->encryption_hash, csd->encryption_hash2, GTMCRYPT_RESERVED_HASH_LEN); memset(csd->encryption_hash2, 0, GTMCRYPT_RESERVED_HASH_LEN); /* A simple copy because gtmcrypt_key_t is a pointer type. */ csa->encr_key_handle = csa->encr_key_handle2; csa->encr_key_handle2 = NULL; cnl->reorg_encrypt_cycle++; assert(NULL != csa->encr_ptr); COPY_ENC_INFO(csd, csa->encr_ptr, cnl->reorg_encrypt_cycle;); if (JNL_ENABLED(csd)) switch_journal_file(csa, csd); DBG_RECORD_CRYPT_UPDATE(csd, csa, cnl, process_id); } /* We are done (although potentially due to an error or a Ctrl-C), so update file-header fields to store reorg's * progress before exiting. */ if (!csa->now_crit) { get_ftok_semaphore(reg, csa); grab_crit(reg, WS_94); } assert(csa->now_crit); assert(UNSTARTED == csd->encryption_hash_cutoff || (SS_NORMAL != reg_status)); if (start_tn != csd->encryption_hash2_start_tn) { /* csd->encryption_hash2_start_tn changed since reorg started. discontinue the reorg */ util_out_print("Region !AD : Starting tn number changed during REORG (expected 0x!16@XQ but got 0x!16@XQ). " "Stopping REORG.", TRUE, REG_LEN_STR(reg), &csd->encryption_hash2_start_tn, start_tn); reg_status = ERR_MUNOFINISH; } else { /* Flush all changes noted down in the file-header. */ if (!wcs_flu(WCSFLU_FLUSH_HDR)) /* wcs_flu assumes gv_cur_region is set (which it is in this routine) */ { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_BUFFLUFAILED, 4, LEN_AND_LIT("MUPIP REORG ENCRYPT4"), db_name_len, db_name); reg_status = ERR_MUNOFINISH; } } curr_tn = csd->trans_hist.curr_tn; cnl->reorg_encrypt_pid = 0; rel_crit(reg); release_ftok_semaphore(reg, csa, csd); /* Issue success or failure message for this region */ if (SS_NORMAL == reg_status) { util_out_print("Region !AD : Database is now FULLY ENCRYPTED with the following key: !AD", TRUE, REG_LEN_STR(reg), key_len, key); util_out_print("Region !AD : MUPIP REORG ENCRYPT finished", TRUE, REG_LEN_STR(reg)); send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_MUREENCRYPTEND, 4, DB_LEN_STR(reg), process_id, &curr_tn); } else { assert(ERR_MUNOFINISH == reg_status); util_out_print("Region !AD : MUPIP REORG ENCRYPT incomplete. See above messages.", TRUE, REG_LEN_STR(reg)); status = reg_status; } } if (NULL != handles) free(handles); if (NULL != bptr) free(bptr); if (NULL != bml_lcl_buff) free(bml_lcl_buff); if (mu_ctrly_occurred || mu_ctrlc_occurred) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_REORGCTRLY); status = ERR_MUNOFINISH; } EXIT_MUPIP_REORG(status); } /* This code is similar to that in wcs_recover.c and should be merged eventually. */ boolean_t wait_for_concurrent_reads(sgmnt_addrs *csa) { uint4 stuck_cnt, blocking_pid; int4 total_stuck_cnt_left; cache_rec_ptr_t cr, cr_top; sgmnt_data_ptr_t csd; total_stuck_cnt_left = MAX_WAIT_FOR_RIP * BUF_OWNER_STUCK; assert(0 < total_stuck_cnt_left); /* safety net just in case the macro values grow and we overflow a signed int */ csd = csa->hdr; assert(csa == cs_addrs); assert(csd == cs_data); assert(csa->now_crit); cr = (cache_rec_ptr_t)csa->acc_meth.bg.cache_state->cache_array + csd->bt_buckets; for (cr_top = cr + csd->n_bts; cr < cr_top; cr++) { for (stuck_cnt = 1; -1 != cr->read_in_progress; stuck_cnt++, total_stuck_cnt_left--) { blocking_pid = cr->r_epid; assert(process_id != blocking_pid); if ((BUF_OWNER_STUCK < stuck_cnt) || (0 > total_stuck_cnt_left)) { if (0 != blocking_pid) GET_C_STACK_FROM_SCRIPT("BUFOWNERSTUCK", process_id, blocking_pid, stuck_cnt); send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_BUFRDTIMEOUT, 6, process_id, cr->blk, cr, blocking_pid, DB_LEN_STR(csa->region)); return FALSE; } if ((0 != blocking_pid) && is_proc_alive(blocking_pid, 0)) { /* Kickstart the process taking a long time in case it was suspended */ UNIX_ONLY(continue_proc(blocking_pid)); } wcs_sleep(stuck_cnt); } } return TRUE; } void get_ftok_semaphore(gd_region *reg, sgmnt_addrs *csa) { unix_db_info *udi; udi = FILE_INFO(reg); if (!ftok_sem_lock(reg, FALSE)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CRITSEMFAIL, 2, LEN_AND_STR(udi->fn)); assert(ftok_sem_reg == reg); } void release_ftok_semaphore(gd_region *reg, sgmnt_addrs *csa, sgmnt_data_ptr_t csd) { unix_db_info *udi; udi = FILE_INFO(reg); /* Second parameter decr_cnt is FALSE because we will decrement the counter semaphore as part of gds_rundown. This is just * releasing the ftok in the middle of the reorg encrypt process and we do not want to modify the counter in those cases. */ if (!ftok_sem_release(reg, FALSE, FALSE)) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBFILERR, 2, DB_LEN_STR(reg)); FTOK_TRACE(csa, csd->trans_hist.curr_tn, ftok_ops_release, process_id); udi->grabbed_ftok_sem = FALSE; } void switch_journal_file(sgmnt_addrs *csa, sgmnt_data_ptr_t csd) { jnl_private_control *jpc; jnl_buffer_ptr_t jbp; uint4 jnl_status; assert(csa->now_crit); SET_GBL_JREC_TIME; /* jnl_ensure_open/jnl_file_extend and its callees assume jgbl.gbl_jrec_time is set */ jpc = csa->jnl; /* Before writing to jnlfile, adjust jgbl.gbl_jrec_time if needed to maintain time order of jnl records. This needs to be * done BEFORE the jnl_ensure_open as that could write journal records (if it decides to switch to a new journal file). */ jbp = jpc->jnl_buff; ADJUST_GBL_JREC_TIME(jgbl, jbp); jnl_status = jnl_ensure_open(gv_cur_region, csa); if (0 == jnl_status) { if (EXIT_ERR == SWITCH_JNL_FILE(jpc)) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_JNLEXTEND, 2, JNL_LEN_STR(csd)); } else { if (SS_NORMAL != jpc->status) rts_error_csa(CSA_ARG(csa) VARLSTCNT(7) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(gv_cur_region), jpc->status); else rts_error_csa(CSA_ARG(csa) VARLSTCNT(6) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(gv_cur_region)); } } fis-gtm-V7.0-005/sr_unix/mupip_reorg_encrypt.h0000644000032200000250000000134614342376330020312 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2016 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. * * * ****************************************************************/ #ifndef MU_REORG_ENCRYPT_DEFINED #define MU_REORG_ENCRYPT_DEFINED void mupip_reorg_encrypt(void); #define MUPIP_REORG_IN_PROG_FALSE 0 #define MUPIP_REORG_IN_PROG_TRUE 1 #define MUPIP_REORG_IN_PROG_LOCAL_DSK_READ 2 #endif fis-gtm-V7.0-005/sr_unix/mupip_restore.c0000644000032200000250000007213014342376330017105 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "gdsdbver.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_select.h" #include #include #include #ifdef __MVS__ #include #include "gtm_zos_io.h" #endif #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "db_header_conversion.h" #include "mupipbckup.h" #include "murest.h" #include "filestruct.h" #include "gdsblk.h" #include "gdsbml.h" #include "cli.h" #include "iosp.h" #include "copy.h" #include "iob.h" #include "error.h" #include "gtmio.h" #include "iotimer.h" #include "gtm_pipe.h" #include "gt_timer.h" #include "stp_parms.h" #include "gtm_stat.h" #include "eintr_wrappers.h" #include "util.h" #include "gtm_caseconv.h" #include "io.h" #include "is_proc_alive.h" #include "mu_rndwn_file.h" #include "mupip_exit.h" #include "mu_outofband_setup.h" #include "mu_gv_cur_reg_init.h" #include "mupip_restore.h" #include "gtmmsg.h" #include "wcs_sleep.h" #include "db_ipcs_reset.h" #include "gds_blk_downgrade.h" #include "shmpool.h" #include "min_max.h" #include "gtmxc_types.h" #include "gtmcrypt.h" #include "jnl.h" #include "anticipatory_freeze.h" #include "db_write_eof_block.h" GBLDEF inc_list_struct in_files; GBLREF uint4 pipe_child; GBLREF gd_region *gv_cur_region; GBLREF uint4 restore_read_errno; LITREF char *gtm_dbversion_table[]; LITREF char *mdb_ver_names[]; #define GTMCRYPT_ERRLIT "during GT.M startup" error_def(ERR_BADTAG); error_def(ERR_CRYPTDLNOOPEN); error_def(ERR_CRYPTDLNOOPEN2); error_def(ERR_CRYPTINIT); error_def(ERR_CRYPTINIT2); error_def(ERR_IOEOF); error_def(ERR_MUPCLIERR); error_def(ERR_MUPRESTERR); error_def(ERR_TEXT); error_def(ERR_RESTORESUCCESS); #define COMMON_READ(S, BUFF, LEN, INBUF) \ { \ assert(BACKUP_TEMPFILE_BUFF_SIZE >= LEN); \ (*common_read)(S, BUFF, LEN); \ if (0 != restore_read_errno) \ CLNUP_AND_EXIT(ERR_MUPRESTERR, INBUF); \ } #define CLNUP_AND_EXIT(EXIT_STATUS, INBUF) \ { \ if (INBUF) \ free(INBUF); \ assert(FILE_INFO(gv_cur_region)->grabbed_access_sem); \ db_ipcs_reset(gv_cur_region); \ mu_gv_cur_reg_free(); \ mupip_exit(EXIT_STATUS); \ } void mupip_restore(void) { static readonly char label[] = GDS_LABEL; static readonly char v6_label[] = V6_GDS_LABEL; char db_name[MAX_FN_LEN + 1], *inbuf, *p, *blk_ptr; inc_list_struct *ptr; inc_header inhead; sgmnt_data old_data; unsigned short n_len; int4 status, rsize, temp, save_errno, old_start_vbn; trans_num curr_tn; block_id blk_num, old_tot_blks, totblks, old_bit_maps, new_bit_maps, rest_blks, i, ii; boolean_t extend; uint4 cli_status; BFILE *in; int db_fd; uint4 old_blk_size, orig_size, size, bplmap; off_t new_eof, offset; off_t new_size; char msg_buffer[1024], *newmap; mstr msg_string; char addr[SA_MAXLEN + 1]; unsigned char tcp[5]; backup_type type; unsigned short port; int4 timeout, cut, match; void (*common_read)(); char *errptr; pid_t waitpid_res; muinc_blk_hdr_ptr_t sblkh_p; int rc; char *inptr; int in_len, gtmcrypt_errno; boolean_t same_encr_settings; boolean_t check_mdb_ver, bad_mdb_ver; boolean_t in_is_encrypted, in_to_be_encrypted; boolean_t old_is_encrypted, old_to_be_encrypted; boolean_t in_use_new_key; enc_handles in_encr_handles, old_encr_handles; int4 cur_mdb_ver; gd_segment *seg; unix_db_info *udi; sgmnt_data_ptr_t csd; ZOS_ONLY(int realfiletag;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; inbuf = NULL; extend = TRUE; if (CLI_NEGATED == (cli_status = cli_present("EXTEND"))) extend = FALSE; mu_outofband_setup(); mu_gv_cur_reg_init(); n_len = SIZEOF(db_name); if (cli_get_str("DATABASE", db_name, &n_len) == FALSE) mupip_exit(ERR_MUPCLIERR); strcpy((char *)gv_cur_region->dyn.addr->fname, db_name); gv_cur_region->dyn.addr->fname_len = n_len; if (!STANDALONE(gv_cur_region)) { util_out_print("Error securing stand alone access to output file !AD. Aborting restore.", TRUE, n_len, db_name); mupip_exit(ERR_MUPRESTERR); } udi = FILE_INFO(gv_cur_region); seg = gv_cur_region->dyn.addr; OPENFILE_DB(db_name, O_RDWR, udi, seg); db_fd = udi->fd; if (FD_INVALID == db_fd) { save_errno = errno; util_out_print("Error accessing output file !AD. Aborting restore.", TRUE, n_len, db_name); errptr = (char *)STRERROR(save_errno); util_out_print("open : !AZ", TRUE, errptr); CLNUP_AND_EXIT(save_errno, NULL); } # ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(db_fd, TAG_BINARY, &realfiletag)) TAG_POLICY_GTM_PUTMSG(db_name, realfiletag, TAG_BINARY, errno); # endif murgetlst(); csd = !udi->fd_opened_with_o_direct ? &old_data : (sgmnt_data_ptr_t)(TREF(dio_buff)).aligned; DB_LSEEKREAD(udi, db_fd, 0, csd, SGMNT_HDR_LEN, save_errno); if (0 == memcmp(csd->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_upconv(csd); if (0 != save_errno) { util_out_print("Error accessing output file !AD. Aborting restore.", TRUE, n_len, db_name); if (-1 != save_errno) { errptr = (char *)STRERROR(save_errno); util_out_print("read : !AZ", TRUE, errptr); CLNUP_AND_EXIT(save_errno, NULL); } else CLNUP_AND_EXIT(ERR_IOEOF, NULL); } if (udi->fd_opened_with_o_direct) memcpy(&old_data, csd, SGMNT_HDR_LEN); if (memcmp(old_data.label, label, GDS_LABEL_SZ) && memcmp(old_data.label, v6_label, GDS_LABEL_SZ)) { util_out_print("Output file !AD has an unrecognizable format", TRUE, n_len, db_name); CLNUP_AND_EXIT(ERR_MUPRESTERR, NULL); } CHECK_DB_ENDIAN(&old_data, n_len, db_name); curr_tn = old_data.trans_hist.curr_tn; old_blk_size = old_data.blk_size; old_tot_blks = old_data.trans_hist.total_blks; old_start_vbn = old_data.start_vbn; bplmap = old_data.bplmap; old_bit_maps = DIVIDE_ROUND_UP(old_tot_blks, bplmap); inbuf = (char *)malloc(BACKUP_TEMPFILE_BUFF_SIZE); sblkh_p = (muinc_blk_hdr_ptr_t)inbuf; msg_string.addr = msg_buffer; msg_string.len = SIZEOF(msg_buffer); memset(&inhead, 0, SIZEOF(inc_header)); rest_blks = 0; for (ptr = in_files.next; NULL != ptr; ptr = ptr->next) { /* --- determine source type --- */ type = backup_to_file; if (0 == ptr->input_file.len) continue; else if ('|' == *(ptr->input_file.addr + ptr->input_file.len - 1)) { type = backup_to_exec; ptr->input_file.len--; *(ptr->input_file.addr + ptr->input_file.len) = '\0'; } else if (ptr->input_file.len > 5) { lower_to_upper(tcp, (uchar_ptr_t)ptr->input_file.addr, 5); if (0 == memcmp(tcp, "TCP:/", 5)) { type = backup_to_tcp; cut = 5; while ('/' == *(ptr->input_file.addr + cut)) cut++; ptr->input_file.len -= cut; p = ptr->input_file.addr; while (p < ptr->input_file.addr + ptr->input_file.len) { *p = *(p + cut); p++; } *p = '\0'; } } /* --- open the input stream --- */ restore_read_errno = 0; switch (type) { case backup_to_file: common_read = iob_read; if ((in = iob_open_rd(ptr->input_file.addr, DISK_BLOCK_SIZE, BLOCKING_FACTOR)) == NULL) { save_errno = errno; util_out_print("Error accessing input file !AD. Aborting restore.", TRUE, ptr->input_file.len, ptr->input_file.addr); errptr = (char *)STRERROR(save_errno); util_out_print("open : !AZ", TRUE, errptr); CLNUP_AND_EXIT(save_errno, inbuf); } break; case backup_to_exec: pipe_child = 0; common_read = exec_read; in = (BFILE *)malloc(SIZEOF(BFILE)); if (0 > (in->fd = gtm_pipe(ptr->input_file.addr, input_from_comm))) { util_out_print("Error creating input pipe from !AD.", TRUE, ptr->input_file.len, ptr->input_file.addr); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } # ifdef DEBUG_ONLINE DBGFPF(stdout, "file descriptor for the openned pipe is %d.\n", in->fd); DBGFPF(stdout, "the command passed to gtm_pipe is %s.\n", ptr->input_file.addr); # endif break; case backup_to_tcp: common_read = tcp_read; /* parse the input */ switch (match = SSCANF(ptr->input_file.addr, "%[^:]:%hu", addr, &port)) { case 1 : port = DEFAULT_BKRS_PORT; case 2 : break; default : util_out_print("Error : A hostname has to be specified.", TRUE); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } assert(SIZEOF(timeout) == SIZEOF(int)); if ((0 == cli_get_int("NETTIMEOUT", (int4 *)&timeout)) || (0 > timeout)) timeout = DEFAULT_BKRS_TIMEOUT; in = (BFILE *)malloc(SIZEOF(BFILE)); if (0 > (in->fd = tcp_open(addr, port, timeout, TRUE))) { util_out_print("Error establishing TCP connection to !AD.", TRUE, ptr->input_file.len, ptr->input_file.addr); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } break; default: util_out_print("Aborting restore!/", TRUE); util_out_print("Unrecognized input format !AD", TRUE, ptr->input_file.len, ptr->input_file.addr); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } COMMON_READ(in, &inhead, SIZEOF(inc_header), inbuf); check_mdb_ver = FALSE; if (!memcmp(inhead.label, INC_HEADER_LABEL_V5_NOENCR, INC_HDR_LABEL_SZ)) assert(!IS_ENCRYPTED(inhead.is_encrypted)); else if (!memcmp(inhead.label, INC_HEADER_LABEL_V6_ENCR, INC_HDR_LABEL_SZ)) assert(IS_ENCRYPTED(inhead.is_encrypted)); else if (!memcmp(inhead.label, INC_HEADER_LABEL_V7, INC_HDR_LABEL_SZ)) check_mdb_ver = FALSE; else if (!memcmp(inhead.label, INC_HEADER_LABEL_V8, INC_HDR_LABEL_SZ)) check_mdb_ver = TRUE; else { util_out_print("Input file !AD has an unrecognizable format", TRUE, ptr->input_file.len, ptr->input_file.addr); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } if (check_mdb_ver) { COMMON_READ(in, (char *)&cur_mdb_ver, SIZEOF(int4), inbuf); bad_mdb_ver = (old_data.minor_dbver != cur_mdb_ver); } else { cur_mdb_ver = -1; bad_mdb_ver = TRUE; } if (bad_mdb_ver) { if (0 > cur_mdb_ver) util_out_print("Minor DB version in the extract predates that in the database (!AD)", TRUE, LEN_AND_STR(mdb_ver_names[old_data.minor_dbver])); else if (GDSMVLAST <= cur_mdb_ver) util_out_print("Minor DB version in the extract is higher than in the database (!AD)", TRUE, LEN_AND_STR(mdb_ver_names[old_data.minor_dbver])); else util_out_print("Minor DB version in the extract (!AD) is different from that in the database (!AD)", TRUE, LEN_AND_STR(mdb_ver_names[cur_mdb_ver]), LEN_AND_STR(mdb_ver_names[old_data.minor_dbver])); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } if (!SAME_ENCRYPTION_SETTINGS(&inhead, &old_data)) { same_encr_settings = FALSE; INIT_PROC_ENCRYPTION(gtmcrypt_errno); if (0 != gtmcrypt_errno) { CLEAR_CRYPTERR_MASK(gtmcrypt_errno); assert(!IS_REPEAT_MSG_MASK(gtmcrypt_errno)); assert((ERR_CRYPTDLNOOPEN == gtmcrypt_errno) || (ERR_CRYPTINIT == gtmcrypt_errno)); if (ERR_CRYPTDLNOOPEN == gtmcrypt_errno) gtmcrypt_errno = ERR_CRYPTDLNOOPEN2; else if (ERR_CRYPTINIT == gtmcrypt_errno) gtmcrypt_errno = ERR_CRYPTINIT2; gtmcrypt_errno = SET_CRYPTERR_MASK(gtmcrypt_errno); GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, rts_error, SIZEOF(GTMCRYPT_ERRLIT) - 1, GTMCRYPT_ERRLIT); } INIT_DB_OR_JNL_ENCRYPTION(&in_encr_handles, &inhead, 0, NULL, gtmcrypt_errno); if (0 != gtmcrypt_errno) { GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, ptr->input_file.len, ptr->input_file.addr); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } INIT_DB_OR_JNL_ENCRYPTION(&old_encr_handles, &old_data, seg->fname_len, seg->fname, gtmcrypt_errno); if (0 != gtmcrypt_errno) { GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, seg->fname_len, seg->fname); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } in_is_encrypted = IS_ENCRYPTED(inhead.is_encrypted); in_to_be_encrypted = USES_NEW_KEY(&inhead); old_is_encrypted = IS_ENCRYPTED(old_data.is_encrypted); old_to_be_encrypted = USES_NEW_KEY(&old_data); } else same_encr_settings = TRUE; if (curr_tn != inhead.start_tn) { util_out_print("Transaction in input file !AD does not align with database TN.!/DB: !16@XQ!_" "Input file: !16@XQ", TRUE, ptr->input_file.len, ptr->input_file.addr, &curr_tn, &inhead.start_tn); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } if (old_blk_size != inhead.blk_size) { util_out_print("Incompatible block size. Output file !AD has block size !XL,", TRUE, n_len, db_name, old_blk_size); util_out_print("while input file !AD is from a database with block size !XL,", TRUE, ptr->input_file.len, ptr->input_file.addr, inhead.blk_size); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } new_bit_maps = DIVIDE_ROUND_UP(inhead.db_total_blks, bplmap); if (old_tot_blks != inhead.db_total_blks) { if (old_tot_blks > inhead.db_total_blks) { /* Truncate occurred between incremental backups. FTRUNCATE the db file to the appropriate size, * write the EOF block. */ new_eof = (off_t)BLK_ZERO_OFF(old_start_vbn) + ((off_t)inhead.db_total_blks * old_blk_size); new_size = new_eof + old_blk_size; status = db_write_eof_block(udi, db_fd, old_blk_size, new_eof, &(TREF(dio_buff))); if (0 != status) { util_out_print("Aborting restore!/", TRUE); util_out_print("lseek or write error : Error accessing output file !AD while truncating.", TRUE, n_len, db_name); totblks = old_tot_blks - old_bit_maps; util_out_print("Output file !AD has!/ !UL (!XL hex) total blocks,", TRUE, n_len, db_name, totblks, totblks); totblks = inhead.db_total_blks - new_bit_maps; util_out_print("while input file !AD is from a database with!/ !UL (!XL hex) total blocks", TRUE, ptr->input_file.len, ptr->input_file.addr, totblks, totblks); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } FTRUNCATE(db_fd, new_size, status); if (0 != status) { util_out_print("Aborting restore!/", TRUE); util_out_print("FTRUNCATE error : Error error truncating output file !AD.", TRUE, n_len, db_name); totblks = old_tot_blks - old_bit_maps; util_out_print("Output file !AD has!/ !UL (!XL hex) total blocks,", TRUE, n_len, db_name, totblks, totblks); totblks = inhead.db_total_blks - new_bit_maps; util_out_print("while input file !AD is from a database with!/ !UL (!XL hex) total blocks", TRUE, ptr->input_file.len, ptr->input_file.addr, totblks, totblks); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } old_tot_blks = inhead.db_total_blks; old_bit_maps = new_bit_maps; } else if (old_tot_blks > inhead.db_total_blks || !extend) { totblks = old_tot_blks - old_bit_maps; util_out_print("Incompatible database sizes. Output file !AD has!/ !UL (!XL hex) total blocks,", TRUE, n_len, db_name, totblks, totblks); totblks = inhead.db_total_blks - new_bit_maps; util_out_print("while input file !AD is from a database with!/ !UL (!XL hex) total blocks", TRUE, ptr->input_file.len, ptr->input_file.addr, totblks, totblks); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } else { /* The db must be extended which we will do ourselves (to avoid jnl and other interferences * in gdsfilext). These local bit map blocks will be created in GDSVCURR format (always). The * reason for this is that we do not know at this time whether these blocks will be replaced * by blocks in the backup or not. If we are in compatibility mode, this is highly likely * even if before image journaling is on which creates bit maps with TN=0. In either case, * a GDSVCURR format block is the only one that can be added to the database without affecting * the blks_to_upgrd counter. */ new_eof = (off_t)BLK_ZERO_OFF(old_start_vbn) + ((off_t)inhead.db_total_blks * old_blk_size); status = db_write_eof_block(udi, db_fd, old_blk_size, new_eof, &(TREF(dio_buff))); if (0 != status) { util_out_print("Aborting restore!/", TRUE); util_out_print("lseek or write error : Unable to extend output file !AD!/", TRUE, n_len, db_name); util_out_print(" from !UL (!XL hex) total blocks to !UL (!XL hex) total blocks.!/", TRUE, old_tot_blks, old_tot_blks, inhead.db_total_blks, inhead.db_total_blks); util_out_print(" Current input file is !AD with !UL (!XL hex) total blocks!/", TRUE, ptr->input_file.len, ptr->input_file.addr, inhead.db_total_blks, inhead.db_total_blks); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) status); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } /* --- initialize all new bitmaps, just in case they are not touched later --- */ if (new_bit_maps > old_bit_maps) { if (udi->fd_opened_with_o_direct) { /* Align buffers for O_DIRECT */ DIO_BUFF_EXPAND_IF_NEEDED(udi, old_blk_size, &(TREF(dio_buff))); newmap = (TREF(dio_buff)).aligned; } else newmap = (char *)malloc(old_blk_size); bml_newmap((blk_hdr_ptr_t)newmap, BM_SIZE(bplmap), curr_tn, csd->desired_db_format); for (ii = ROUND_UP(old_tot_blks, bplmap); ii < inhead.db_total_blks; ii += bplmap) { offset = (off_t)BLK_ZERO_OFF(old_start_vbn) + (off_t)ii * old_blk_size; DB_LSEEKWRITE(NULL, udi, NULL, db_fd, offset, newmap, old_blk_size, status); if (0 != status) { util_out_print("Aborting restore!/", TRUE); util_out_print("Bitmap 0x!XL initialization error!", TRUE, ii); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) status); free(newmap); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } } if (!udi->fd_opened_with_o_direct) free(newmap); } old_tot_blks = inhead.db_total_blks; old_bit_maps = new_bit_maps; } } rsize = SIZEOF(muinc_blk_hdr) + inhead.blk_size; for ( ; ; ) { /* All records are of fixed size so process until we get to a zeroed record marking the end */ COMMON_READ(in, inbuf, rsize, inbuf); /* Note rsize == sblkh_p */ if (0 == sblkh_p->blkid && FALSE == sblkh_p->valid_data) { /* This is supposed to be the end of list marker (null entry */ COMMON_READ(in, &rsize, SIZEOF(rsize), inbuf); if (SIZEOF(END_MSG) + SIZEOF(int4) == rsize) { /* the length of our secondary check is correct .. now check substance */ COMMON_READ(in, inbuf, rsize - SIZEOF(int4), inbuf); if (0 == MEMCMP_LIT(inbuf, END_MSG)) break; /* We are done */ } util_out_print("Invalid information in restore file !AD. Aborting restore.", TRUE, ptr->input_file.len, ptr->input_file.addr); assert(FALSE); if (backup_to_file == type) iob_close(in); CLNUP_AND_EXIT(ERR_MUPRESTERR, inbuf); } rest_blks++; blk_num = sblkh_p->blkid; /* Between incremental backups, an extend followed by a truncate could have occurred. The block below * would have been truncated, so no need to write it. */ if (blk_num >= old_tot_blks) /* Should be a bitmap block */ continue; /* For blocks that were read during the main backup phase of stream backup, the blocks are recorded without version (there may even be some garbage blocks in the stream of indeterminate/invalid format if a bitmap was written out prior to the data blocks that were recently allocated in it). For these blocks, we just write out what we have as a full block. For blocks that were written out during the backup as part of the online image processing, these are always recorded in V5 mode. We will rewrite these in the mode they were oringally found on disk (potentially necessitating a downgrade of the block). This allows us to exactly match the blks_to_upgrade counter in the saved file-header without worrying about what blocks were converted (or not) in the interim. */ blk_ptr = inbuf + SIZEOF(muinc_blk_hdr); size = old_blk_size; if (GDSNOVER != sblkh_p->use.bkup.ondsk_blkver) { /* Specifically versioned blocks - Put them back in the version they were originally */ if (GDSV4 == sblkh_p->use.bkup.ondsk_blkver) { gds_blk_downgrade((v15_blk_hdr_ptr_t)blk_ptr, (blk_hdr_ptr_t)blk_ptr); size = (((v15_blk_hdr_ptr_t)blk_ptr)->bsiz + 1) & ~1; assert((size <= old_blk_size) && (size >= SIZEOF(v15_blk_hdr))); } else { size = (((blk_hdr_ptr_t)blk_ptr)->bsiz + 1) & ~1; assert((size <= old_blk_size) && (size >= SIZEOF(blk_hdr))); } } in_len = MIN(old_blk_size, size) - SIZEOF(blk_hdr); if (!same_encr_settings && IS_BLK_ENCRYPTED(((blk_hdr_ptr_t)blk_ptr)->levl, in_len)) { gtmcrypt_errno = 0; in_use_new_key = in_to_be_encrypted && (((blk_hdr_ptr_t)blk_ptr)->tn >= inhead.encryption_hash2_start_tn); if (in_use_new_key || in_is_encrypted) { inptr = blk_ptr + SIZEOF(blk_hdr); if (in_use_new_key) { GTMCRYPT_DECRYPT(NULL, TRUE, in_encr_handles.encr_key_handle2, inptr, in_len, NULL, blk_ptr, SIZEOF(blk_hdr), gtmcrypt_errno); } else { GTMCRYPT_DECRYPT(NULL, inhead.non_null_iv, in_encr_handles.encr_key_handle, inptr, in_len, NULL, blk_ptr, SIZEOF(blk_hdr), gtmcrypt_errno); } if (0 != gtmcrypt_errno) { GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, ptr->input_file.len, ptr->input_file.addr); CLNUP_AND_EXIT(gtmcrypt_errno, inbuf); } } if (old_to_be_encrypted || old_is_encrypted) { inptr = blk_ptr + SIZEOF(blk_hdr); if (old_to_be_encrypted) { GTMCRYPT_ENCRYPT(NULL, TRUE, old_encr_handles.encr_key_handle2, inptr, in_len, NULL, blk_ptr, SIZEOF(blk_hdr), gtmcrypt_errno); } else { GTMCRYPT_ENCRYPT(NULL, old_data.non_null_iv, old_encr_handles.encr_key_handle, inptr, in_len, NULL, blk_ptr, SIZEOF(blk_hdr), gtmcrypt_errno); } if (0 != gtmcrypt_errno) { GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, n_len, db_name); CLNUP_AND_EXIT(gtmcrypt_errno, inbuf); } } } offset = (off_t)BLK_ZERO_OFF(old_start_vbn) + (off_t)blk_num * old_blk_size; if (udi->fd_opened_with_o_direct) { /* Align buffers for O_DIRECT */ assert(size <= old_blk_size); DIO_BUFF_EXPAND_IF_NEEDED(udi, old_blk_size, &(TREF(dio_buff))); memcpy((TREF(dio_buff)).aligned, blk_ptr, size); memset((TREF(dio_buff)).aligned + size, 0, old_blk_size - size); blk_ptr = (char *)(TREF(dio_buff)).aligned; size = old_blk_size; } DB_LSEEKWRITE(NULL, udi, NULL, db_fd, offset, blk_ptr, size, save_errno); if (0 != save_errno) { util_out_print("Error accessing output file !AD. Aborting restore.", TRUE, n_len, db_name); errptr = (char *)STRERROR(save_errno); util_out_print("write : !AZ", TRUE, errptr); CLNUP_AND_EXIT(save_errno, inbuf); } } /* Next section is the file header which we need to restore */ COMMON_READ(in, &rsize, SIZEOF(rsize), inbuf); assert((SGMNT_HDR_LEN + SIZEOF(int4)) == rsize); COMMON_READ(in, inbuf, rsize, inbuf); if (0 == memcmp(inbuf, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_upconv((sgmnt_data_ptr_t)inbuf); ((sgmnt_data_ptr_t)inbuf)->start_vbn = old_start_vbn; ((sgmnt_data_ptr_t)inbuf)->free_space = (uint4)(BLK_ZERO_OFF(old_start_vbn) - SIZEOF_FILE_HDR(inbuf)); GTMCRYPT_COPY_ENCRYPT_SETTINGS(&old_data, ((sgmnt_data_ptr_t)inbuf)); assert((udi->semid == old_data.semid) && (udi->gt_sem_ctime == old_data.gt_sem_ctime.ctime) && (udi->shmid == old_data.shmid) && (udi->gt_shm_ctime == old_data.gt_shm_ctime.ctime)); /* Since the file header we are about to write is taken from the BACKUP database, the semid/shmid (and the * corresponding ctime fields) will be INVALID. But, we want the restor'ed file to continue having standalone * access until we are done with MUPIP RESTORE (and release the semaphores and clear the file header fields in * db_ipcs_reset). So, before writing the new file header, set the semid/shmid (and ctime fields) to the semid and * shmid fields that is currently valid in the system (when we did the mu_rndwn_file). */ csd = (sgmnt_data_ptr_t)inbuf; csd->semid = old_data.semid; csd->gt_sem_ctime.ctime = old_data.gt_sem_ctime.ctime; csd->shmid = old_data.shmid; csd->gt_shm_ctime.ctime = old_data.gt_shm_ctime.ctime; if (udi->fd_opened_with_o_direct) { /* Align buffers for O_DIRECT */ DIO_BUFF_EXPAND_IF_NEEDED(udi, SGMNT_HDR_LEN, &(TREF(dio_buff))); memcpy((TREF(dio_buff)).aligned, csd, SGMNT_HDR_LEN); csd = (sgmnt_data_ptr_t)(TREF(dio_buff)).aligned; } if (0 == memcmp(csd, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv(csd); DB_LSEEKWRITE(NULL, udi, NULL, db_fd, 0, csd, SGMNT_HDR_LEN, save_errno); if (0 != save_errno) { util_out_print("Error accessing output file !AD. Aborting restore.", TRUE, n_len, db_name); errptr = (char *)STRERROR(save_errno); util_out_print("write : !AZ", TRUE, errptr); CLNUP_AND_EXIT(save_errno, inbuf); } GET_LONG(temp, (inbuf + rsize - SIZEOF(int4))); rsize = temp; COMMON_READ(in, inbuf, rsize, inbuf); if (0 != MEMCMP_LIT(inbuf, HDR_MSG)) { util_out_print("Unexpected backup format error restoring !AD. Aborting restore.", TRUE, n_len, db_name); errptr = (char *)STRERROR(save_errno); util_out_print("write : !AZ", TRUE, errptr); CLNUP_AND_EXIT(save_errno, inbuf); } GET_LONG(temp, (inbuf + rsize - SIZEOF(int4))); rsize = temp; offset = (MM_BLOCK - 1) * DISK_BLOCK_SIZE; assert(SGMNT_HDR_LEN == offset); /* Still have contiguous master map for now */ for (i = 0; ; i++) /* Restore master map */ { COMMON_READ(in, inbuf, rsize, inbuf); if (!MEMCMP_LIT(inbuf, MAP_MSG)) break; size = rsize - SIZEOF(int4); if (udi->fd_opened_with_o_direct) { /* Align buffers for O_DIRECT */ orig_size = size; size = ROUND_UP2(orig_size, DIO_ALIGNSIZE(udi)); DIO_BUFF_EXPAND_IF_NEEDED(udi, size, &(TREF(dio_buff))); memcpy((TREF(dio_buff)).aligned, inbuf, orig_size); p = (char *)(TREF(dio_buff)).aligned; } else p = (char *)inbuf; DB_LSEEKWRITE(NULL, udi, NULL, db_fd, offset, p, size, save_errno); if (0 != save_errno) { util_out_print("Error accessing output file !AD. Aborting restore.", TRUE, n_len, db_name); errptr = (char *)STRERROR(save_errno); util_out_print("write : !AZ", TRUE, errptr); CLNUP_AND_EXIT(save_errno, inbuf); } offset += size; GET_LONG(temp, inbuf + size); rsize = temp; } curr_tn = inhead.end_tn; switch (type) { case backup_to_file: iob_close(in); break; case backup_to_exec: CLOSEFILE_RESET(in->fd, rc); /* resets "in->fd" to FD_INVALID */ if ((pipe_child > 0) && (FALSE != is_proc_alive(pipe_child, 0))) WAITPID(pipe_child, (int *)&status, 0, waitpid_res); break; case backup_to_tcp: break; } } gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_RESTORESUCCESS); util_out_print("!UL blocks restored", TRUE, rest_blks); CLNUP_AND_EXIT(SS_NORMAL, inbuf); } STATICFNDEF void exec_read(BFILE *bf, char *buf, int nbytes) { int needed, got; int4 status; char *curr; pid_t waitpid_res; int rc; assert(nbytes > 0); needed = nbytes; curr = buf; # ifdef DEBUG_ONLINE DBGFPF(stdout, "file descriptor is %d and bytes needed is %d\n", bf->fd, needed); # endif while (0 != (got = (int)(read(bf->fd, curr, needed)))) { if (got == needed) break; else if (got > 0) { needed -= got; curr += got; } /* the check for EINTR below is valid and should not be converted to an EINTR * wrapper macro, for an immediate retry is not attempted. Instead, wcs_sleep * is called. */ else if ((EINTR != errno) && (EAGAIN != errno)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); if ((pipe_child > 0) && (FALSE != is_proc_alive(pipe_child, 0))) WAITPID(pipe_child, (int *)&status, 0, waitpid_res); CLOSEFILE_RESET(bf->fd, rc); /* resets "bf->fd" to FD_INVALID */ restore_read_errno = errno; break; } wcs_sleep(100); } return; } /* the logic here can be reused in iosocket_readfl.c */ STATICFNDEF void tcp_read(BFILE *bf, char *buf, int nbytes) { int needed, status; char *curr; fd_set fs; struct timeval save_nap, nap; int rc; needed = nbytes; curr = buf; nap.tv_sec = 1; nap.tv_usec = 0; while (1) { assertpro(FD_SETSIZE > bf->fd); FD_ZERO(&fs); FD_SET(bf->fd, &fs); assert(0 != FD_ISSET(bf->fd, &fs)); /* Note: the check for EINTR from the select below should remain, as aa_select is a * function, and not all callers of aa_select behave the same when EINTR is returned. */ save_nap = nap; status = select(bf->fd + 1, (void *)(&fs), (void *)0, (void *)0, &nap); nap = save_nap; if (status > 0) { RECV(bf->fd, curr, needed, 0, status); if ((0 == status) || (needed == status)) /* lost connection or all set */ { break; } else if (status > 0) { needed -= status; curr += status; } } if ((status < 0) && (errno != EINTR)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); CLOSEFILE_RESET(bf->fd, rc); /* resets "bf->fd" to FD_INVALID */ restore_read_errno = errno; break; } } return; } fis-gtm-V7.0-005/sr_unix/mupip_rundown.c0000644000032200000250000003137714342376330017126 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 #include "gtm_string.h" #include "gtm_inet.h" #include "gtm_ipc.h" #include "stp_parms.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "cli.h" #include "filestruct.h" #include "iosp.h" #include "jnl.h" #include "gdscc.h" #include "gdskill.h" #include "buddy_list.h" #include "tp.h" #include "error.h" #include "gbldirnam.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" #include "mupipbckup.h" #include "mu_rndwn_file.h" #include "mu_rndwn_replpool.h" #include "mu_rndwn_all.h" #include "mupip_exit.h" #include "mu_getlst.h" #include "dpgbldir.h" #include "gtmio.h" #include "dpgbldir_sysops.h" #include "mu_gv_cur_reg_init.h" #include "mupip_rundown.h" #include "gtmmsg.h" #include "repl_instance.h" #include "mu_rndwn_repl_instance.h" #include "util.h" #include "anticipatory_freeze.h" #include "repl_sem.h" #include "ftok_sems.h" #include "ipcrmid.h" #include "do_semop.h" GBLREF bool in_backup; GBLREF bool error_mupip; GBLREF tp_region *grlist; GBLREF gd_region *gv_cur_region; GBLREF boolean_t mu_star_specified; GBLREF boolean_t donot_fflush_NULL; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; GBLREF boolean_t argumentless_rundown; GBLREF semid_queue_elem *keep_semids; error_def(ERR_MUFILRNDWNSUC); error_def(ERR_MUJPOOLRNDWNFL); error_def(ERR_MUJPOOLRNDWNSUC); error_def(ERR_MUNOACTION); error_def(ERR_MUNODBNAME); error_def(ERR_MUNOTALLSEC); error_def(ERR_MUPCLIERR); error_def(ERR_MUQUALINCOMP); error_def(ERR_REPLPOOLINST); error_def(ERR_SEMREMOVED); error_def(ERR_SYSCALL); error_def(ERR_TEXT); void mupip_rundown(void) { int exit_status, semid, shmid, save_errno, status; boolean_t region, file, arg_present, do_jnlpool_detach, jnlpool_sem_created, jnlpool_rndwn_required; boolean_t shm_removed = FALSE, anticipatory_freeze_available; void *repl_inst_available; tp_region *rptr, single; replpool_identifier replpool_id; repl_inst_hdr repl_instance; unix_db_info *udi; sgmnt_addrs *csa; struct shmid_ds shm_buf; union semun semarg; unsigned int full_len; char *instfilename; unsigned char ipcs_buff[MAX_IPCS_ID_BUF], *ipcs_ptr; semid_queue_elem *prev_elem; gd_segment *seg; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; exit_status = SS_NORMAL; file = (CLI_PRESENT == cli_present("FILE")); region = (CLI_PRESENT == cli_present("REGION")) || (CLI_PRESENT == cli_present("R")); TREF(skip_file_corrupt_check) = TRUE; /* rundown the database even if csd->file_corrupt is TRUE */ TREF(ok_to_see_statsdb_regs) = TRUE; /* No need to do the following set (like is done in mupip_integ.c) since we call "mu_rndwn_file" (not "gvcst_init") * TREF(statshare_opted_in) = NO_STATS_OPTIN; // Do not open statsdb automatically when basedb is opened */ arg_present = (0 != TREF(parms_cnt)); if ((file == region) && (TRUE == file)) mupip_exit(ERR_MUQUALINCOMP); if (arg_present && !file && !region) { util_out_print("MUPIP RUNDOWN only accepts a parameter when -FILE or -REGION is specified.", TRUE); mupip_exit(ERR_MUPCLIERR); } if (region) { gvinit(); mu_getlst("WHAT", SIZEOF(tp_region)); rptr = grlist; if (error_mupip) exit_status = ERR_MUNOTALLSEC; } else if (file) { mu_gv_cur_reg_init(); seg = gv_cur_region->dyn.addr; seg->fname_len = MAX_FN_LEN; if (!cli_get_str("WHAT", (char *)&seg->fname[0], &seg->fname_len)) mupip_exit(ERR_MUNODBNAME); seg->fname[seg->fname_len] = '\0'; rptr = &single; /* a dummy value that permits one trip through the loop */ rptr->fPtr = NULL; } in_backup = FALSE; /* Only want yes/no from mupfndfil, not an address */ if (region || file) { do_jnlpool_detach = FALSE; anticipatory_freeze_available = INST_FREEZE_ON_ERROR_POLICY; if ((jnlpool_rndwn_required = (region && mu_star_specified)) || anticipatory_freeze_available) /* note:assigmnent */ { /* sets replpool_id/full_len; note: assignment */ if (DEBUG_ONLY(repl_inst_available = )REPL_INST_AVAILABLE(NULL)) { instfilename = &replpool_id.instfilename[0]; if (!mu_rndwn_repl_instance(&replpool_id, !anticipatory_freeze_available, TRUE, &jnlpool_sem_created)) { /* It is possible, we attached to the journal pool (and did not run it down because there * were other processes still attached to it) but got an error while trying to grab the * access control semaphore for the receive pool (because a receiver server was still * running) and because anticipatory_freeze_available is TRUE, we did not detach from * the journal pool inside "mu_rndwn_repl_instance". We need to do the detach here, * except if IFOE is configured, in which case we need the journal pool attached * so that we can check for instance freeze in database rundown. * In that case, the detach will happen automatically when the process terminates. * No need to do any instance file cleanup since there is nothing to rundown there * from either the journal pool or receive pool. */ assert((!jnlpool || (NULL == jnlpool->jnlpool_ctl)) || anticipatory_freeze_available); if ((jnlpool && (NULL != jnlpool->jnlpool_ctl)) && !anticipatory_freeze_available) { shmid = jnlpool->repl_inst_filehdr->jnlpool_shmid; JNLPOOL_SHMDT(jnlpool, status, save_errno); if (0 > status) { ISSUE_REPLPOOLINST(save_errno, shmid, instfilename, "shmdt()"); mupip_exit(ERR_MUNOTALLSEC); } } exit_status = ERR_MUNOTALLSEC; } else do_jnlpool_detach = (NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl); ENABLE_FREEZE_ON_ERROR; } } for ( ; NULL != rptr; rptr = rptr->fPtr) { if (region) { if (!mupfndfil(rptr->reg, NULL, LOG_ERROR_TRUE)) { exit_status = ERR_MUNOTALLSEC; continue; } gv_cur_region = rptr->reg; seg = gv_cur_region->dyn.addr; if (NULL == seg->file_cntl) FILE_CNTL_INIT(seg); } if (mu_rndwn_file(gv_cur_region, FALSE)) { if (!IS_RDBF_STATSDB(gv_cur_region)) /* See comment in "mu_rndwn_all" for why this is needed */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_MUFILRNDWNSUC, 2, DB_LEN_STR(gv_cur_region)); } else exit_status = ERR_MUNOTALLSEC; } if (do_jnlpool_detach) { udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); assert(anticipatory_freeze_available && repl_inst_available); assert(NULL != jnlpool->jnlpool_ctl); /* Read the instance file to invalidate the journal pool semaphore ID and shared memory ID */ assert(NULL != jnlpool->jnlpool_dummy_reg); assert(INVALID_SEMID != jnlpool->repl_inst_filehdr->jnlpool_semid); assert(INVALID_SHMID != jnlpool->repl_inst_filehdr->jnlpool_shmid); assert(0 != jnlpool->repl_inst_filehdr->jnlpool_semid_ctime); assert(0 != jnlpool->repl_inst_filehdr->jnlpool_shmid_ctime); semid = jnlpool->repl_inst_filehdr->jnlpool_semid; shmid = jnlpool->repl_inst_filehdr->jnlpool_shmid; /* Detach from the journal pool */ JNLPOOL_SHMDT(jnlpool, status, save_errno); if (0 > status) { ISSUE_REPLPOOLINST(save_errno, shmid, instfilename, "shmdt()"); mupip_exit(ERR_MUNOTALLSEC); } /* Grab the ftok again */ if (!ftok_sem_lock(jnlpool->jnlpool_dummy_reg, FALSE)) { /* CRITSEMFAIL is issued in case of an error */ assert(FALSE); mupip_exit(ERR_MUNOTALLSEC); } if (jnlpool_rndwn_required) { /* User requested for running down the journal pool as well. So, regrab the access control semaphore * before proceeding to remove the shared memory. We should already have incremented the counter * semaphore. But, before that read the instance file since we need to invalidate the semid/shmid * below. */ repl_inst_read(instfilename, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr)); assert(semid == repl_instance.jnlpool_semid); /* Should still be valid */ assert(shmid == repl_instance.jnlpool_shmid); /* Should still be valid */ assert(holds_sem[SOURCE][SRC_SERV_COUNT_SEM]); if (SS_NORMAL != grab_sem(SOURCE, JNL_POOL_ACCESS_SEM)) { save_errno = errno; ISSUE_REPLPOOLINST(save_errno, shmid, instfilename, "semop()"); mupip_exit(ERR_MUNOTALLSEC); } if (-1 == shmctl(shmid, IPC_STAT, &shm_buf)) { save_errno = errno; ISSUE_REPLPOOLINST(save_errno, shmid, instfilename, "shmctl(IPC_STAT)"); mupip_exit(ERR_MUNOTALLSEC); } ipcs_ptr = i2asc((uchar_ptr_t)ipcs_buff, shmid); *ipcs_ptr = '\0'; if (0 == shm_buf.shm_nattch) { /* No one else attached and no new process can attach (as we hold the ftok and access * control semaphore on the journal pool). */ if (SS_NORMAL != shm_rmid(shmid)) { save_errno = errno; ISSUE_REPLPOOLINST(save_errno, shmid, instfilename, "shmctl(IPC_RMID)"); mupip_exit(ERR_MUNOTALLSEC); } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUJPOOLRNDWNSUC, 4, LEN_AND_STR(ipcs_buff), LEN_AND_STR(instfilename)); repl_instance.jnlpool_shmid = INVALID_SHMID; repl_instance.jnlpool_shmid_ctime = 0; shm_removed = TRUE; } } else { util_out_print("Replpool segment (id = !UL) for replication instance !AD is in" " use by another process.", TRUE, shmid, LEN_AND_STR(instfilename)); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUJPOOLRNDWNFL, 4, LEN_AND_STR(ipcs_buff), LEN_AND_STR(instfilename)); } /* Now, go ahead and release/remove the access control semaphores */ ipcs_ptr = i2asc((uchar_ptr_t)ipcs_buff, semid); *ipcs_ptr = '\0'; assert(INVALID_SEMID != repl_instance.jnlpool_semid); if (SS_NORMAL == mu_replpool_release_sem(&repl_instance, JNLPOOL_SEGMENT, shm_removed)) { if (INVALID_SEMID == repl_instance.jnlpool_semid) { /* Successfully removed the semaphore */ assert(0 == repl_instance.jnlpool_semid_ctime); if (!jnlpool_sem_created) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_MUJPOOLRNDWNSUC, 4, LEN_AND_STR(ipcs_buff), LEN_AND_STR(instfilename), ERR_SEMREMOVED, 1, semid); } repl_instance.crash = FALSE; /* No more semaphore IDs. Reset crash bit */ } } else { /* REPLACCESSSEM is issued from within mu_replpool_release_sem */ assert(FALSE); mupip_exit(ERR_MUNOTALLSEC); } assert(shm_removed || (INVALID_SEMID != repl_instance.jnlpool_semid)); assert(!shm_removed || (INVALID_SEMID == repl_instance.jnlpool_semid)); repl_inst_write(instfilename, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr)); } else { /* User did not request to rundown the journal pool as well. So, just decrement the counter * semaphore. We've already released the access control semaphore. */ if (SS_NORMAL != decr_sem(SOURCE, SRC_SERV_COUNT_SEM)) { save_errno = errno; ISSUE_REPLPOOLINST(save_errno, shmid, instfilename, "semop()"); mupip_exit(ERR_MUNOTALLSEC); } } if (!ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, FALSE)) { /* CRITSEMFAIL is issued in case of an error */ assert(FALSE); mupip_exit(ERR_MUNOTALLSEC); } } } else { argumentless_rundown = TRUE; /* Both "mu_rndwn_all" and "mu_rndwn_sem_all" do POPEN which opens an input stream (of type "FILE *"). * We have noticed that on HPUX, a call to "fflush NULL" (done inside gtm_putmsg which is called from * the above two functions at various places) causes unread (but buffered) data from the input stream * to be cleared/consumed resulting in incomplete processing of the input list of ipcs. To avoid this * we set this global variable. That causes gtm_putmsg to skip the fflush NULL. We don't have an issue * with out-of-order mixing of stdout and stderr streams (like is there with replication server logfiles) * and so it is okay for this global variable to be set to TRUE for the entire lifetime of the argumentless * rundown command. See . */ donot_fflush_NULL = TRUE; exit_status = mu_rndwn_all(); if (SS_NORMAL == exit_status) exit_status = mu_rndwn_sem_all(); else mu_rndwn_sem_all(); /* Free keep_semids queue */ while (keep_semids) { prev_elem = keep_semids->prev; free(keep_semids); keep_semids = prev_elem; } } mupip_exit(exit_status); } fis-gtm-V7.0-005/sr_unix/mupip_sems.c0000644000032200000250000000561614342376330016376 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 #include "gtm_ipc.h" #include "cli.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gtmio.h" #include "iosp.h" #include "mupip_sems.h" #include "mupip_exit.h" #include "gtm_sem.h" #undef EXIT #define EXIT exit /* Use system "exit" (not gtm_image_exit) directly since this is a standalone module */ #define OUT_LINE 80 + 1 /* mupip_sems.c - display information about GT.M semaphores * * semval - current value of the semaphore * semncnt - count of processes waiting for the semaphore value to * become greater than its current value. * semzcnt - count of processes waiting for the semaphore value to * become zero. * sempid - last process id to perform an operation on the * semaphore. */ void mupip_sems(void) { const char *statname[] = {"sempid","semzcnt", "semncnt", "semval"}; const int stat[] = { GETPID, GETZCNT, GETNCNT, GETVAL}; char s[OUT_LINE]; int i, j, k, sem, semval; struct semid_ds semstat; union semun semarg; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; semarg.buf = &semstat; for (i = 0; i < TREF(parms_cnt); i++) { /* in order to handle multiple smephores, this loop directly uses the array built by cli */ strncpy(s, TAREF1(parm_ary, i), MAX_DIGITS_IN_INT); /* because cli routines don't handle single type lists */ s[MAX_DIGITS_IN_INT] = 0; sem = ATOI(s); if (-1 == semctl(sem, 0, IPC_STAT, semarg)) { FPRINTF(stderr, "Error obtaining semaphore status.\n"); SNPRINTF(s, OUT_LINE, "semctl(%s)", s); PERROR(s); continue; } # ifndef _AIX /* at this writing, the key is known to be available for Linux, but not for AIX */ FPRINTF(stderr, "semid %d :: semkey (0x%lx): %hu semaphores in the set\n", sem, semstat.sem_perm.__key, (unsigned short int)semarg.buf->sem_nsems); # else FPRINTF(stderr, "semid %d: %hu semaphores in the set\n", sem, (unsigned short int)semarg.buf->sem_nsems); # endif for (j = 0; j < semarg.buf->sem_nsems; j++) { PRINTF("sem %2d: (", j); for (k = 3; 0 <= k ; k--) { if (-1 == (semval = semctl(sem, j, stat[k]))) { FPRINTF(stderr, "Error obtaining semaphore %d %s.\n", j, statname[k]); SNPRINTF(s, OUT_LINE, "semctl(%d)", sem); PERROR(s); continue; } PRINTF("%s=%*d%s", statname[k], 8, semval, (k ? ", " : ")\n")); } } } mupip_exit(SS_NORMAL); } fis-gtm-V7.0-005/sr_unix/mupip_sems.h0000644000032200000250000000116714342376330016400 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef MUPIP_SEMS_INCLUDED #define MUPIP_SEMS_INCLUDED void mupip_sems(void); #endif /* MUPIP_SEMS_INCLUDED */ fis-gtm-V7.0-005/sr_unix/mupip_set_file.c0000644000032200000250000011742614342376330017224 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_unistd.h" #include "gtm_fcntl.h" #include "gtm_ipc.h" #include "gtm_stdio.h" #include "gtm_stat.h" #include "gtm_string.h" #include #ifdef __MVS__ #include "gtm_zos_io.h" #endif #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "db_header_conversion.h" #include "gdsblk.h" #include "gdscc.h" #include "gdskill.h" #include "filestruct.h" #include "cli.h" #include "error.h" #include "gtmio.h" #include "iosp.h" #include "jnl.h" #include "mupipbckup.h" #include "timersp.h" #include "gt_timer.h" #include "buddy_list.h" #include "tp.h" #include "util.h" #include "mupip_set.h" #include "mu_rndwn_file.h" #include "mupip_exit.h" #include "ipcrmid.h" #include "mu_gv_cur_reg_init.h" #include "gvcst_protos.h" /* for gvcst_init prototype */ #include "timers.h" #include "db_ipcs_reset.h" #include "wcs_flu.h" #include "gds_rundown.h" #include "change_reg.h" #include "desired_db_format_set.h" #include "gtmmsg.h" /* for gtm_putmsg prototype */ #include "gtmcrypt.h" #include "anticipatory_freeze.h" #include "get_fs_block_size.h" #include "interlock.h" GBLREF bool in_backup; GBLREF bool region; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_data_ptr_t cs_data; GBLREF sgmnt_addrs *cs_addrs; GBLREF tp_region *grlist; LITREF char *gtm_dbversion_table[]; #define GTMCRYPT_ERRLIT "during GT.M startup" error_def(ERR_ASYNCIONOV4); error_def(ERR_ASYNCIONOMM); error_def(ERR_CRYPTDLNOOPEN); error_def(ERR_CRYPTDLNOOPEN2); error_def(ERR_CRYPTINIT); error_def(ERR_CRYPTINIT2); error_def(ERR_CRYPTNOMM); error_def(ERR_DBBLKSIZEALIGN); error_def(ERR_DBFILOPERR); error_def(ERR_DBPREMATEOF); error_def(ERR_DBRDERR); error_def(ERR_DBRDONLY); error_def(ERR_GTMCURUNSUPP); error_def(ERR_INVACCMETHOD); error_def(ERR_MMNODYNDWNGRD); error_def(ERR_MUPIPSET2BIG); error_def(ERR_MUPIPSET2SML); error_def(ERR_MUREENCRYPTV4NOALLOW); error_def(ERR_NODFRALLOCSUPP); error_def(ERR_NOUSERDB); error_def(ERR_OFRZACTIVE); error_def(ERR_READONLYNOBG); error_def(ERR_SETQUALPROB); error_def(ERR_TEXT); error_def(ERR_WCERRNOTCHG); error_def(ERR_WCWRNNOTCHG); #define MAX_ACC_METH_LEN 2 #define MAX_KEY_SIZE MAX_KEY_SZ - 4 /* internal and external maximums differ */ #define MIN_MAX_KEY_SZ 3 #define DO_CLNUP_AND_SET_EXIT_STAT(EXIT_STAT, EXIT_WRN_ERR_MASK) \ MBSTART { \ exit_stat |= EXIT_WRN_ERR_MASK; \ db_ipcs_reset(gv_cur_region); \ mu_gv_cur_reg_free(); \ } MBEND int4 mupip_set_file(int db_fn_len, char *db_fn) { boolean_t bypass_partial_recov, flush_buffers = FALSE, got_standalone, need_standalone = FALSE, acc_meth_changing, long_blk_id; char acc_spec[MAX_ACC_METH_LEN + 1], *command = "MUPIP SET VERSION", *errptr, exit_stat, *fn, ver_spec[MAX_DB_VER_LEN + 1]; enum db_acc_method access, access_new; enum db_ver desired_dbver; uint4 fbwsize; int4 dblksize; gd_region *temp_cur_region; gd_segment *seg; int asyncio_status, defer_allocate_status, defer_status, disk_wait_status, encryptable_status, encryption_complete_status, epoch_taper_status, extn_count_status, fd, fn_len, glbl_buff_status, gtmcrypt_errno, hard_spin_status, inst_freeze_on_error_status, key_size_status, locksharesdbcrit, lock_space_status, mutex_space_status, null_subs_status, qdbrundown_status, rec_size_status, reg_exit_stat, rc, rsrvd_bytes_status, sleep_cnt_status, save_errno, stats_status, status, status1, stdb_alloc_status, stdnullcoll_status, trigger_flush_limit_status, wrt_per_flu_status, full_blkwrt_status, problksplit_status; int4 defer_time, new_cache_size, new_disk_wait, new_extn_count, new_flush_trigger, new_hard_spin, new_key_size, new_lock_space, new_mutex_space, new_null_subs, new_rec_size, new_sleep_cnt, new_spin_sleep, new_statsdb_alloc, new_stdnullcoll, new_wrt_per_flu, reserved_bytes, spin_sleep_status, read_only_status, new_full_blkwrt, new_problksplit; sgmnt_data_ptr_t csd, pvt_csd; tp_region *rptr, single; uint4 fsb_size, reservedDBFlags; unsigned short acc_spec_len = MAX_ACC_METH_LEN, ver_spec_len = MAX_DB_VER_LEN; ZOS_ONLY(int realfiletag;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; exit_stat = EXIT_NRM; defer_status = cli_present("DEFER_TIME"); if (defer_status) need_standalone = TRUE; /* If the user requested MUPIP SET -PARTIAL_RECOV_BYPASS, then skip the check in grab_crit, which triggers an rts_error, as * this is one of the ways of turning off the file_corrupt flag in the file header */ TREF(skip_file_corrupt_check) = bypass_partial_recov = cli_present("PARTIAL_RECOV_BYPASS") == CLI_PRESENT; if (bypass_partial_recov) need_standalone = TRUE; if (cli_present("ACCESS_METHOD")) { cli_get_str("ACCESS_METHOD", acc_spec, &acc_spec_len); acc_spec[acc_spec_len] = '\0'; cli_strupper(acc_spec); if (0 == memcmp(acc_spec, "MM", acc_spec_len)) access = dba_mm; else if (0 == memcmp(acc_spec, "BG", acc_spec_len)) access = dba_bg; else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_INVACCMETHOD); exit_stat |= EXIT_ERR; } need_standalone = TRUE; } else access = n_dba; /* really want to keep current method, which has not yet been read */ if (asyncio_status = cli_present("ASYNCIO")) need_standalone = TRUE; if (defer_allocate_status = cli_present("DEFER_ALLOCATE")) flush_buffers = TRUE; if (encryptable_status = cli_present("ENCRYPTABLE")) { need_standalone = TRUE; if (CLI_PRESENT == encryptable_status) { /* When turning on encryption, validate the encryption setup */ INIT_PROC_ENCRYPTION(gtmcrypt_errno); if (0 != gtmcrypt_errno) { CLEAR_CRYPTERR_MASK(gtmcrypt_errno); assert(!IS_REPEAT_MSG_MASK(gtmcrypt_errno)); assert((ERR_CRYPTDLNOOPEN == gtmcrypt_errno) || (ERR_CRYPTINIT == gtmcrypt_errno)); if (ERR_CRYPTDLNOOPEN == gtmcrypt_errno) gtmcrypt_errno = ERR_CRYPTDLNOOPEN2; else if (ERR_CRYPTINIT == gtmcrypt_errno) gtmcrypt_errno = ERR_CRYPTINIT2; gtmcrypt_errno = SET_CRYPTERR_MASK(gtmcrypt_errno); GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, rts_error, SIZEOF(GTMCRYPT_ERRLIT) - 1, GTMCRYPT_ERRLIT); } } else /* When disabling encryption ignore invalid encryption setup errors */ TREF(mu_set_file_noencryptable) = TRUE; } if (read_only_status = cli_present("READ_ONLY")) /* Note assignment */ need_standalone = TRUE; encryption_complete_status = cli_present("ENCRYPTIONCOMPLETE"); epoch_taper_status = cli_present("EPOCHTAPER"); if (problksplit_status = cli_present("PROBLKSPLIT")) if (!cli_get_int("PROBLKSPLIT", &new_problksplit)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("PROBLKSPLIT")); exit_stat |= EXIT_ERR; } /* EXTENSION_COUNT does not require standalone access and hence need_standalone will not be set to TRUE for this. */ if (extn_count_status = cli_present("EXTENSION_COUNT")) { if (cli_get_int("EXTENSION_COUNT", &new_extn_count)) { /* minimum is 0 & mupip_cmd defines this qualifier to not accept negative values, so no min check */ if (new_extn_count > MAX_EXTN_COUNT) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2BIG, 4, new_extn_count, LEN_AND_LIT("EXTENSION_COUNT"), MAX_EXTN_COUNT); exit_stat |= EXIT_ERR; } } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("EXTENSION COUNT")); exit_stat |= EXIT_ERR; } flush_buffers = TRUE; } if (full_blkwrt_status = cli_present("FULLBLKWRT")) { if (cli_get_int("FULLBLKWRT", &new_full_blkwrt)) { /* minimum is 0 & mupip_cmd defines this qualifier to not accept negative values, so no min check */ if (new_full_blkwrt > FULL_DATABASE_WRITE) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2BIG, 4, new_full_blkwrt, LEN_AND_LIT("FULLBLKWRT"), 2); exit_stat |= EXIT_ERR; } need_standalone = TRUE; } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("FULLBLKWRT")); exit_stat |= EXIT_ERR; } } if (glbl_buff_status = cli_present("GLOBAL_BUFFERS")) { if (cli_get_int("GLOBAL_BUFFERS", &new_cache_size)) { if (new_cache_size > GTM64_ONLY(GTM64_WC_MAX_BUFFS) NON_GTM64_ONLY(WC_MAX_BUFFS)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2BIG, 4, new_cache_size, LEN_AND_LIT("GLOBAL_BUFFERS"),GTM64_ONLY(GTM64_WC_MAX_BUFFS) NON_GTM64_ONLY(WC_MAX_BUFFS)); exit_stat |= EXIT_ERR; } if (new_cache_size < WC_MIN_BUFFS) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2SML, 4, new_cache_size, LEN_AND_LIT("GLOBAL_BUFFERS"), WC_MIN_BUFFS); exit_stat |= EXIT_ERR; } } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("GLOBAL_BUFFERS")); exit_stat |= EXIT_ERR; } need_standalone = TRUE; } if (hard_spin_status = cli_present("HARD_SPIN_COUNT")) { /* No min or max tests needed because mupip_cmd enforces min of 0 and no max requirement is documented*/ if (!cli_get_int("HARD_SPIN_COUNT", &new_hard_spin)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("HARD_SPIN_COUNT")); exit_stat |= EXIT_ERR; } } inst_freeze_on_error_status = cli_present("INST_FREEZE_ON_ERROR"); if (key_size_status = cli_present("KEY_SIZE")) { if (cli_get_int("KEY_SIZE", &new_key_size)) { if (MAX_KEY_SIZE < new_key_size) { /* bigger than 1019 not supported */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2BIG, 4, new_key_size, LEN_AND_LIT("KEY_SIZE"), MAX_KEY_SIZE); exit_stat |= EXIT_ERR; } if (MIN_MAX_KEY_SZ > new_key_size) { /* less than 3 not supported */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2SML, 4, new_key_size, LEN_AND_LIT("KEY_SIZE"), MIN_MAX_KEY_SZ); exit_stat |= EXIT_ERR; } } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("KEY_SIZE")); exit_stat |= EXIT_ERR; } need_standalone = TRUE; } if (locksharesdbcrit = cli_present("LCK_SHARES_DB_CRIT")) need_standalone = TRUE; if (lock_space_status = cli_present("LOCK_SPACE")) { if (cli_get_int("LOCK_SPACE", &new_lock_space)) { if (new_lock_space > MAX_LOCK_SPACE) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2BIG, 4, new_lock_space, LEN_AND_LIT("LOCK_SPACE"), MAX_LOCK_SPACE); exit_stat |= EXIT_ERR; } else if (new_lock_space < MIN_LOCK_SPACE) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2SML, 4, new_lock_space, LEN_AND_LIT("LOCK_SPACE"), MIN_LOCK_SPACE); exit_stat |= EXIT_ERR; } } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("LOCK_SPACE")); exit_stat |= EXIT_ERR; } need_standalone = TRUE; } if (mutex_space_status = cli_present("MUTEX_SLOTS")) { if (cli_get_int("MUTEX_SLOTS", &new_mutex_space)) { if (new_mutex_space > MAX_CRIT_ENTRY) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2BIG, 4, new_mutex_space, LEN_AND_LIT("MUTEX_SLOTS"), MAX_CRIT_ENTRY); exit_stat |= EXIT_ERR; } else if (new_mutex_space < MIN_CRIT_ENTRY) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2SML, 4, new_mutex_space, LEN_AND_LIT("MUTEX_SLOTS"), MIN_CRIT_ENTRY); exit_stat |= EXIT_ERR; } } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("MUTEX_SLOTS")); exit_stat |= EXIT_ERR; } need_standalone = TRUE; } if (null_subs_status = cli_present("NULL_SUBSCRIPTS")) { if (-1 == (new_null_subs = cli_n_a_e("NULL_SUBSCRIPTS"))) exit_stat |= EXIT_ERR; need_standalone = TRUE; } if (qdbrundown_status = cli_present("QDBRUNDOWN")) need_standalone = TRUE; if (rec_size_status = cli_present("RECORD_SIZE")) { if (cli_get_int("RECORD_SIZE", &new_rec_size)) { /* minimum is 0 & mupip_cmd defines this qualifier to not accept negative values, so no min check */ if (MAX_STRLEN < new_rec_size) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2BIG, 4, new_rec_size, LEN_AND_LIT("RECORD_SIZE"), MAX_STRLEN); exit_stat |= EXIT_ERR; } } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("RECORD_SIZE")); exit_stat |= EXIT_ERR; } need_standalone = TRUE; } if (rsrvd_bytes_status = cli_present("RESERVED_BYTES")) { if (!cli_get_int("RESERVED_BYTES", &reserved_bytes)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("RESERVED_BYTES")); exit_stat |= EXIT_ERR; } need_standalone = TRUE; } /* SLEEP_SPIN_COUNT does not require standalone access and hence need_standalone will not be set to TRUE for this. */ if (sleep_cnt_status = cli_present("SLEEP_SPIN_COUNT")) { if (cli_get_int("SLEEP_SPIN_COUNT", &new_sleep_cnt)) { /* minimum is 0 & mupip_cmd defines this qualifier to not accept negative values, so no min check */ if (new_sleep_cnt > MAX_SLEEP_CNT) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2BIG, 4, new_sleep_cnt, LEN_AND_LIT("SLEEP_SPIN_COUNT"), MAX_SLEEP_CNT); exit_stat |= EXIT_ERR; } } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("SLEEP_SPIN_COUNT")); exit_stat |= EXIT_ERR; } } if (spin_sleep_status = cli_present("SPIN_SLEEP_MASK")) { if (cli_get_hex("SPIN_SLEEP_MASK", (uint4 *) &new_spin_sleep)) { /* minimum is 0 and mupip_cmd defines this qualifier to only accept hex values no min check*/ if ((uint4) new_spin_sleep > MAX_SPIN_SLEEP_MASK) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2BIG, 4, new_spin_sleep, LEN_AND_LIT("SPIN_SLEEP_MASK"), MAX_SPIN_SLEEP_MASK); exit_stat |= EXIT_ERR; } } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("SPIN_SLEEP_MASK")); exit_stat |= EXIT_ERR; } } if (stats_status = cli_present("STATS")) need_standalone = TRUE; if (stdb_alloc_status = cli_present("STATSDB_ALLOCATION")) { if (cli_get_int("STATSDB_ALLOCATION", &new_statsdb_alloc)) { if (new_statsdb_alloc > STDB_ALLOC_MAX) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2BIG, 4, new_statsdb_alloc, LEN_AND_LIT("STATSDB_ALLOCATION"), STDB_ALLOC_MAX); exit_stat |= EXIT_ERR; } if (new_statsdb_alloc < STDB_ALLOC_MIN) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2SML, 4, new_statsdb_alloc, LEN_AND_LIT("STATSDB_ALLOCATION"), STDB_ALLOC_MIN); exit_stat |= EXIT_ERR; } need_standalone = TRUE; } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("STATSDB_ALLOCATION")); exit_stat |= EXIT_ERR; } } if (stdnullcoll_status = cli_present("STDNULLCOLL")) need_standalone = TRUE; if (cli_present("VERSION")) { cli_get_str("VERSION", ver_spec, &ver_spec_len); ver_spec[ver_spec_len] = '\0'; cli_strupper(ver_spec); if (0 == memcmp(ver_spec, "V7", ver_spec_len + 1)) desired_dbver = GDSV7; else if (0 == memcmp(ver_spec, "V6", ver_spec_len + 1)) desired_dbver = GDSV6; else if (0 == memcmp(ver_spec, "V4", ver_spec_len + 1)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_GTMCURUNSUPP); exit_stat |= EXIT_ERR; desired_dbver = GDSV4; } else assertpro(FALSE); /* CLI should prevent us ever getting here */ flush_buffers = TRUE; } else desired_dbver = GDSVLAST; /* really want to keep version, which has not yet been read */ if (disk_wait_status = cli_present("WAIT_DISK")) { if (cli_get_int("WAIT_DISK", &new_disk_wait)) { if (new_disk_wait < 0) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2SML, 4, new_disk_wait, LEN_AND_LIT("WAIT_DISK"), 0); exit_stat |= EXIT_ERR; } need_standalone = TRUE; } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("WAIT_DISK")); exit_stat |= EXIT_ERR; } } if (exit_stat & EXIT_ERR) return (int4)ERR_WCERRNOTCHG; if (region) rptr = grlist; else { rptr = &single; memset(&single, 0, SIZEOF(single)); } pvt_csd = (sgmnt_data *)malloc(ROUND_UP(SIZEOF(sgmnt_data), DISK_BLOCK_SIZE)); pvt_csd->read_only = FALSE; in_backup = FALSE; /* Only want yes/no from mupfndfil, not an address */ for (; rptr != NULL; rptr = rptr->fPtr) { reg_exit_stat = EXIT_NRM; if (region) { if (dba_usr == rptr->reg->dyn.addr->acc_meth) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_NOUSERDB, 4, LEN_AND_LIT("MUPIP SET"), REG_LEN_STR(rptr->reg)); exit_stat |= EXIT_WRN; continue; } if (!mupfndfil(rptr->reg, NULL, LOG_ERROR_TRUE)) continue; fn = (char *)rptr->reg->dyn.addr->fname; fn_len = rptr->reg->dyn.addr->fname_len; } else { fn = db_fn; fn_len = db_fn_len; } mu_gv_cur_reg_init(); memcpy(gv_cur_region->dyn.addr->fname, fn, fn_len); gv_cur_region->dyn.addr->fname_len = fn_len; acc_meth_changing = FALSE; if (need_standalone) { /* Following part needs standalone access */ got_standalone = STANDALONE(gv_cur_region); if (FALSE == got_standalone) { exit_stat |= EXIT_WRN; mu_gv_cur_reg_free(); continue; } /* we should open it (for changing) after mu_rndwn_file, since mu_rndwn_file changes the file header too */ if (FD_INVALID == (fd = OPEN(fn, O_RDWR))) /* udi not available so OPENFILE_DB not used */ { save_errno = errno; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); DO_CLNUP_AND_SET_EXIT_STAT(exit_stat, EXIT_ERR); continue; } # ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(fd, TAG_BINARY, &realfiletag)) TAG_POLICY_GTM_PUTMSG(fn, realfiletag, TAG_BINARY, errno); # endif LSEEKREAD(fd, 0, pvt_csd, SIZEOF(sgmnt_data), status); if (0 == memcmp(pvt_csd->label, V6_GDS_LABEL, GDS_LABEL_SZ -1)) db_header_upconv(pvt_csd); if (0 != status) { save_errno = errno; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(fn), save_errno); if (-1 != status) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_DBRDERR, 2, fn_len, fn); else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_DBPREMATEOF, 2, fn_len, fn); DO_CLNUP_AND_SET_EXIT_STAT(exit_stat, EXIT_ERR); continue; } if ((n_dba != access) && (pvt_csd->acc_meth != access)) /* n_dba is a proxy for no change */ { acc_meth_changing = TRUE; if (dba_mm == access) { if (USES_ENCRYPTION(pvt_csd->is_encrypted)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CRYPTNOMM, 2, DB_LEN_STR(gv_cur_region)); DO_CLNUP_AND_SET_EXIT_STAT(exit_stat, EXIT_ERR); continue; } if (pvt_csd->asyncio && (CLI_NEGATED != asyncio_status)) { /* ASYNCIO=ON */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ASYNCIONOMM, 6, DB_LEN_STR(gv_cur_region), LEN_AND_LIT(" has ASYNCIO enabled;"), LEN_AND_LIT("enable MM")); } else if (!pvt_csd->asyncio && (CLI_PRESENT == asyncio_status)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ASYNCIONOMM, 6, DB_LEN_STR(gv_cur_region), LEN_AND_LIT(";"), LEN_AND_LIT("enable MM and ASYNCIO at the same time")); reg_exit_stat |= EXIT_WRN; } pvt_csd->defer_time = 1; /* defer defaults to 1 */ } if(dba_mm != access) { if (pvt_csd->read_only && (CLI_NEGATED != read_only_status)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_READONLYNOBG); reg_exit_stat |= EXIT_WRN; } } pvt_csd->acc_meth = access; if (0 == pvt_csd->n_bts) { pvt_csd->n_bts = WC_DEF_BUFFS; pvt_csd->bt_buckets = getprime(pvt_csd->n_bts); } } access_new = (n_dba == access ? pvt_csd->acc_meth : access); if (dba_mm == access_new) { if (CLI_NEGATED == defer_status) pvt_csd->defer_time = 0; else if (CLI_PRESENT == defer_status) { if (cli_get_defertime("DEFER_TIME", &defer_time)) { if (-1 > defer_time) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2SML, 4, defer_time, LEN_AND_LIT("DEFER_TIME"), -1); reg_exit_stat |= EXIT_WRN; } else pvt_csd->defer_time = defer_time; } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("DEFER_TIME")); reg_exit_stat |= EXIT_WRN; } } if (pvt_csd->blks_to_upgrd) { util_out_print("MM access method cannot be set if there are blocks to upgrade", TRUE); util_out_print("Database file !AD not changed", TRUE, fn_len, fn); reg_exit_stat |= EXIT_WRN; } if (pvt_csd->offset && ((GDSVCURR - 1) == pvt_csd->desired_db_format)) { /* if we support downgrade, enhance the above conditions */ util_out_print("MM access method cannot be set during DB upgrade", TRUE); util_out_print("Database file !AD not changed", TRUE, fn_len, fn); reg_exit_stat |= EXIT_WRN; } if (JNL_ENABLED(pvt_csd) && pvt_csd->jnl_before_image) { util_out_print("MM access method cannot be set with BEFORE image journaling", TRUE); util_out_print("Database file !AD not changed", TRUE, fn_len, fn); reg_exit_stat |= EXIT_WRN; } pvt_csd->jnl_before_image = FALSE; } else { if (defer_status) { util_out_print("DEFER cannot be specified with BG access method.", TRUE); util_out_print("Database file !AD not changed", TRUE, fn_len, fn); reg_exit_stat |= EXIT_WRN; } } if (bypass_partial_recov) { pvt_csd->file_corrupt = FALSE; util_out_print("Database file !AD now has partial recovery flag set to !UL(FALSE) ", TRUE, fn_len, fn, pvt_csd->file_corrupt); } if (encryptable_status) { if (!pvt_csd->fully_upgraded) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_MUREENCRYPTV4NOALLOW, 2, fn_len, fn); reg_exit_stat |= EXIT_WRN; } else if (dba_mm == access_new) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CRYPTNOMM, 2, fn_len, fn); reg_exit_stat |= EXIT_WRN; } else if (CLI_PRESENT == encryptable_status) MARK_AS_TO_BE_ENCRYPTED(pvt_csd->is_encrypted); else if (USES_NEW_KEY(pvt_csd)) { util_out_print("Database file !AD is being (re)encrypted and must stay encryptable", TRUE, fn_len, fn); reg_exit_stat |= EXIT_WRN; } else if (IS_ENCRYPTED(pvt_csd->is_encrypted)) SET_AS_ENCRYPTED(pvt_csd->is_encrypted); else SET_AS_UNENCRYPTED(pvt_csd->is_encrypted); } if (glbl_buff_status) { pvt_csd->n_bts = BT_FACTOR(new_cache_size); pvt_csd->bt_buckets = getprime(pvt_csd->n_bts); pvt_csd->flush_trigger = FLUSH_FACTOR(pvt_csd->n_bts); } if (key_size_status) { long_blk_id = BLK_ID_32_VER < pvt_csd->desired_db_format; key_size_status = pvt_csd->blk_size - SIZEOF(blk_hdr) - SIZEOF(rec_hdr) - SIZEOF_BLK_ID(long_blk_id) - bstar_rec_size(long_blk_id) - pvt_csd->reserved_bytes; if (key_size_status < new_key_size) { /* too big for block */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_MUPIPSET2BIG, 4, new_key_size, LEN_AND_LIT("KEY_SIZE"), key_size_status, ERR_TEXT, 2, RTS_ERROR_TEXT("for current block size and reserved bytes")); reg_exit_stat |= EXIT_WRN; } else if (pvt_csd->max_key_size > new_key_size) { /* lowering the maximum key size can cause problems if large keys already exist in db */ util_out_print("!UL smaller than current maximum key size !UL", TRUE, new_key_size, pvt_csd->max_key_size); reg_exit_stat |= EXIT_WRN; } pvt_csd->max_key_size = new_key_size; } if (locksharesdbcrit) pvt_csd->lock_crit_with_db = CLI_PRESENT == locksharesdbcrit; if (lock_space_status) pvt_csd->lock_space_size = (uint4)new_lock_space * OS_PAGELET_SIZE; if (mutex_space_status) NUM_CRIT_ENTRY(pvt_csd) = new_mutex_space; if (null_subs_status) gv_cur_region->null_subs = pvt_csd->null_subs = (unsigned char)new_null_subs; if (qdbrundown_status) pvt_csd->mumps_can_bypass = (CLI_PRESENT == qdbrundown_status); if (rec_size_status) { if (pvt_csd->max_rec_size > new_rec_size) { util_out_print("!UL smaller than current maximum record size !UL", TRUE, new_rec_size, pvt_csd->max_rec_size); reg_exit_stat |= EXIT_WRN; } pvt_csd->max_rec_size = new_rec_size; } if (rsrvd_bytes_status) { long_blk_id = BLK_ID_32_VER < pvt_csd->desired_db_format; if (reserved_bytes > MAX_RESERVE_B(pvt_csd, long_blk_id)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2BIG, 4, reserved_bytes, LEN_AND_LIT("RESERVED_BYTES"), MAX_RESERVE_B(pvt_csd, long_blk_id)); reg_exit_stat |= EXIT_WRN; } pvt_csd->reserved_bytes = reserved_bytes; } if (stats_status) { reservedDBFlags = pvt_csd->reservedDBFlags & ~RDBF_NOSTATS; if (CLI_NEGATED == stats_status) reservedDBFlags |= RDBF_NOSTATS; pvt_csd->reservedDBFlags = reservedDBFlags; } if (stdb_alloc_status) { pvt_csd->statsdb_allocation = new_statsdb_alloc; } if (read_only_status) { if (dba_mm != pvt_csd->acc_meth && CLI_NEGATED != read_only_status) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_READONLYNOBG); reg_exit_stat |= EXIT_WRN; } else { pvt_csd->read_only = !(CLI_NEGATED == read_only_status); } } if (CLI_NEGATED == stdnullcoll_status) gv_cur_region->std_null_coll = pvt_csd->std_null_coll = FALSE; else if (CLI_PRESENT == stdnullcoll_status) gv_cur_region->std_null_coll = pvt_csd->std_null_coll = TRUE; if (EXIT_NRM != reg_exit_stat) { DO_CLNUP_AND_SET_EXIT_STAT(exit_stat, reg_exit_stat); continue; } if (full_blkwrt_status) { fbwsize = get_fs_block_size(fd); dblksize = pvt_csd->blk_size; if (0 != fbwsize && (0 == dblksize % fbwsize) && (0 == (BLK_ZERO_OFF(pvt_csd->start_vbn)) % fbwsize)) pvt_csd->write_fullblk = new_full_blkwrt; } csd = pvt_csd; } else /* if (!need_standalone) */ { got_standalone = FALSE; if (region) { /* We have a region from a gld file. Find out asyncio setting from there. And copy that * over to the dummy region we created. This is to avoid DBGLDMISMATCH errors inside "gvcst_init". */ COPY_AIO_SETTINGS(gv_cur_region->dyn.addr, rptr->reg->dyn.addr); /* copies from rptr->reg->dyn.addr * to gv_cur_region->dyn.addr */ } else { /* We do not have a region from a gld file. All we have is the name of the db file. * "db_init" (invoked by "gvcst_init") takes care of initializing the "asyncio" field * as appropriate. */ } gvcst_init(gv_cur_region, NULL); change_reg(); /* sets cs_addrs and cs_data */ if (gv_cur_region->read_only) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); exit_stat |= EXIT_ERR; gds_rundown(CLEANUP_UDI_TRUE); mu_gv_cur_reg_free(); continue; } csd = cs_data; assert(!cs_addrs->hold_onto_crit); /* this ensures we can safely do unconditional grab_crit and rel_crit */ grab_crit(gv_cur_region, WS_95); if (FROZEN_CHILLED(cs_addrs)) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_OFRZACTIVE, 2, DB_LEN_STR(gv_cur_region)); exit_stat |= EXIT_WRN; exit_stat |= gds_rundown(CLEANUP_UDI_TRUE); mu_gv_cur_reg_free(); continue; } } access_new = (n_dba == access ? csd->acc_meth : access); if (GDSVLAST != desired_dbver) { /* TODO: reject GDSV4 requests and figure out what to do with MM; revise desired_db_format_set */ if ((dba_mm != access_new) || (GDSV4 != desired_dbver)) (void)desired_db_format_set(gv_cur_region, desired_dbver, command); else /* for other errors desired_db_format_set prints appropriate error messages */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_MMNODYNDWNGRD, 2, REG_LEN_STR(gv_cur_region)); if (csd->desired_db_format != desired_dbver) reg_exit_stat |= EXIT_WRN; else util_out_print("Database file !AD now has desired DB format !AD", TRUE, fn_len, fn, LEN_AND_STR(gtm_dbversion_table[csd->desired_db_format])); } if (encryption_complete_status) { if (!csd->fully_upgraded) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_MUREENCRYPTV4NOALLOW, 2, fn_len, fn); reg_exit_stat |= EXIT_WRN; } else if (dba_mm == access_new) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CRYPTNOMM, 2, fn_len, fn); reg_exit_stat |= EXIT_WRN; } else if (((NULL == cs_addrs->nl) || (!cs_addrs->nl->reorg_encrypt_pid)) && (!USES_NEW_KEY(csd))) csd->encryption_hash2_start_tn = 0; else { util_out_print("Cannot mark encryption complete on database file !AD due to" " an ongoing MUPIP REORG -ENCRYPT operation", TRUE, fn_len, fn); reg_exit_stat |= EXIT_WRN; } } if (asyncio_status && (CLI_PRESENT == asyncio_status)) { if (!csd->fully_upgraded) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ASYNCIONOV4, 6, DB_LEN_STR(gv_cur_region), LEN_AND_LIT("V4 format"), LEN_AND_LIT("enable ASYNCIO")); reg_exit_stat |= EXIT_WRN; } if (!acc_meth_changing && (dba_bg != access_new)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ASYNCIONOMM, 6, DB_LEN_STR(gv_cur_region), LEN_AND_LIT(" has MM access method;"), LEN_AND_LIT("enable ASYNCIO")); reg_exit_stat |= EXIT_WRN; } seg = gv_cur_region->dyn.addr; /* AIO = ON, implies we need to use O_DIRECT. Check for db vs fs blksize alignment issues. */ fsb_size = get_fs_block_size(got_standalone ? fd : FILE_INFO(gv_cur_region)->fd); if (0 != (csd->blk_size % fsb_size)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_DBBLKSIZEALIGN, 4, DB_LEN_STR(gv_cur_region), csd->blk_size, fsb_size); reg_exit_stat |= EXIT_WRN; } } if (wrt_per_flu_status = (CLI_PRESENT == cli_present("WRITES_PER_FLUSH"))) { if (cli_get_int("WRITES_PER_FLUSH", &new_wrt_per_flu)) { if (0 >= new_wrt_per_flu) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2SML, 4, new_wrt_per_flu, LEN_AND_LIT("WRITES_PER_FLUSH"), 1); reg_exit_stat |= EXIT_ERR; } else if (MAX_WRT_PER_FLU < new_wrt_per_flu) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2BIG, 4, new_wrt_per_flu, LEN_AND_LIT("WRITES_PER_FLUSH"), MAX_WRT_PER_FLU); reg_exit_stat |= EXIT_ERR; } } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("WRITES_PER_FLUSH")); reg_exit_stat |= EXIT_ERR; } } if (trigger_flush_limit_status = (CLI_PRESENT == cli_present("TRIGGER_FLUSH_LIMIT"))) { if (cli_get_int("TRIGGER_FLUSH_LIMIT", &new_flush_trigger)) { if (glbl_buff_status ? (MIN_FLUSH_TRIGGER(BT_FACTOR(new_cache_size)) > new_flush_trigger) : (MIN_FLUSH_TRIGGER(cs_data->n_bts) > new_flush_trigger)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2SML, 4, new_flush_trigger, LEN_AND_LIT("TRIGGER_FLUSH_LIMIT"), glbl_buff_status ? MIN_FLUSH_TRIGGER(BT_FACTOR(new_cache_size)) : MIN_FLUSH_TRIGGER(cs_data->n_bts)); reg_exit_stat |= EXIT_ERR; } else if (glbl_buff_status ? (FLUSH_FACTOR(BT_FACTOR(new_cache_size)) < new_flush_trigger) : (FLUSH_FACTOR(cs_data->n_bts) < new_flush_trigger)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUPIPSET2BIG, 4, new_flush_trigger, LEN_AND_LIT("TRIGGER_FLUSH_LIMIT"), glbl_buff_status ? FLUSH_FACTOR(BT_FACTOR(new_cache_size)) : FLUSH_FACTOR(cs_data->n_bts)); reg_exit_stat |= EXIT_ERR; } } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SETQUALPROB, 2, LEN_AND_LIT("TRIGGER_FLUSH_LIMIT")); reg_exit_stat |= EXIT_ERR; } } if (EXIT_NRM == reg_exit_stat) { if (n_dba != access) util_out_print("Database file !AD now has !AD access method", TRUE, fn_len, fn, 2, ((dba_bg == csd->acc_meth) ? "BG" : "MM")); if (defer_allocate_status) { # if defined(__sun) || defined(__hpux) if (CLI_NEGATED == defer_allocate_status) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NODFRALLOCSUPP); # else csd->defer_allocate = (CLI_PRESENT == defer_allocate_status); util_out_print("Database file !AD now has defer allocation flag set to !AD", TRUE, fn_len, fn, 5, (csd->defer_allocate ? " TRUE" : "FALSE")); # endif } if (disk_wait_status) csd->wait_disk_space = new_disk_wait; if (epoch_taper_status) csd->epoch_taper = (CLI_PRESENT == epoch_taper_status); if (asyncio_status) csd->asyncio = (CLI_PRESENT == asyncio_status); if (problksplit_status) csd->problksplit = (uint4)new_problksplit; if (extn_count_status) csd->extension_size = (uint4)new_extn_count; change_fhead_timer("FLUSH_TIME", csd->flush_time, (dba_bg == access_new ? TIM_FLU_MOD_BG : TIM_FLU_MOD_MM), FALSE); if (CLI_NEGATED == inst_freeze_on_error_status) csd->freeze_on_fail = FALSE; else if (CLI_PRESENT == inst_freeze_on_error_status) csd->freeze_on_fail = TRUE; if (hard_spin_status) HARD_SPIN_COUNT(csd) = new_hard_spin; if (sleep_cnt_status) SLEEP_SPIN_CNT(csd) = new_sleep_cnt; if (spin_sleep_status) SPIN_SLEEP_MASK(csd) = new_spin_sleep; if (trigger_flush_limit_status) csd->flush_trigger = csd->flush_trigger_top = new_flush_trigger; if (wrt_per_flu_status) csd->n_wrt_per_flu = new_wrt_per_flu; /* --------------------- report results ------------------------- */ if (asyncio_status) { if (csd->asyncio) util_out_print("Database file !AD now has asyncio !AD", TRUE, fn_len, fn, LEN_AND_LIT("enabled")); else util_out_print("Database file !AD now has asyncio !AD", TRUE, fn_len, fn, LEN_AND_LIT("disabled")); } if (CLI_NEGATED == read_only_status) util_out_print("Database file !AD is no longer read-only", TRUE, fn_len, fn); else if (CLI_PRESENT == read_only_status) util_out_print("Database file !AD is now read-only", TRUE, fn_len, fn); if (disk_wait_status) util_out_print("Database file !AD now has wait disk set to !UL seconds", TRUE, fn_len, fn, csd->wait_disk_space); if (encryption_complete_status) util_out_print("Database file !AD now has encryption marked complete", TRUE, fn_len, fn); if (epoch_taper_status) util_out_print("Database file !AD now has epoch taper flag set to !AD", TRUE, fn_len, fn, 5, (csd->epoch_taper ? " TRUE" : "FALSE")); if (extn_count_status) util_out_print("Database file !AD now has extension count !UL", TRUE, fn_len, fn, csd->extension_size); if (CLI_NEGATED == inst_freeze_on_error_status) util_out_print("Database file !AD now has inst freeze on fail flag set to FALSE", TRUE, fn_len, fn); else if (CLI_PRESENT == inst_freeze_on_error_status) util_out_print("Database file !AD now has inst freeze on fail flag set to TRUE", TRUE, fn_len, fn); if (hard_spin_status) util_out_print("Database file !AD now has hard spin count !UL", TRUE, fn_len, fn, HARD_SPIN_COUNT(csd)); if (sleep_cnt_status) util_out_print("Database file !AD now has sleep spin count !UL", TRUE, fn_len, fn, SLEEP_SPIN_CNT(csd)); if (spin_sleep_status) util_out_print("Database file !AD now has spin sleep mask !UL", TRUE, fn_len, fn, SPIN_SLEEP_MASK(csd)); if (trigger_flush_limit_status) util_out_print("Database file !AD now has trigger_flush_limit !UL", TRUE, fn_len, fn, csd->flush_trigger_top); if (wrt_per_flu_status) util_out_print("Database file !AD now has writes per flush !UL", TRUE, fn_len, fn, csd->n_wrt_per_flu); if (full_blkwrt_status) { switch(csd->write_fullblk) { case 0: util_out_print("Database file !AD now has full blk writes: !AD", TRUE, fn_len, fn, LEN_AND_LIT("disabled")); break; case 1: util_out_print("Database file !AD now has full blk writes: !AD", TRUE, fn_len, fn, LEN_AND_LIT("file system block writes")); break; case 2: util_out_print("Database file !AD now has full blk writes: !AD", TRUE, fn_len, fn, LEN_AND_LIT("full DB block writes")); break; } } if (problksplit_status) util_out_print("Database file !AD now has proactive block split flag set to !UL", TRUE, fn_len, fn, csd->problksplit); if (got_standalone) { if (0 == memcmp(pvt_csd->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv(pvt_csd); DB_LSEEKWRITE(NULL,((unix_db_info *)NULL),NULL,fd,0,pvt_csd,SIZEOF(sgmnt_data),status); if (0 != status) { save_errno = errno; errptr = (char *)STRERROR(save_errno); util_out_print("write : !AZ", TRUE, errptr); util_out_print("Error writing header of file", TRUE); util_out_print("Database file !AD not changed: ", TRUE, fn_len, fn); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_DBRDERR, 2, fn_len, fn); } if (defer_status && (dba_mm == pvt_csd->acc_meth)) util_out_print("Database file !AD now has defer_time set to !SL", TRUE, fn_len, fn, pvt_csd->defer_time); if (glbl_buff_status) util_out_print("Database file !AD now has !UL global buffers", TRUE, fn_len, fn, pvt_csd->n_bts); if (key_size_status) util_out_print("Database file !AD now has maximum key size !UL", TRUE, fn_len, fn, pvt_csd->max_key_size); if (encryptable_status) util_out_print("Database file !AD now has encryptable flag set to !AD", TRUE, fn_len, fn, 5, (TO_BE_ENCRYPTED(pvt_csd->is_encrypted) ? " TRUE" : "FALSE")); if (locksharesdbcrit) util_out_print("Database file !AD now has LOCK sharing crit with DB !AD", TRUE, fn_len, fn, 5, (pvt_csd->lock_crit_with_db ? " TRUE" : "FALSE")); if (lock_space_status) util_out_print("Database file !AD now has lock space !UL pages", TRUE, fn_len, fn, pvt_csd->lock_space_size/OS_PAGELET_SIZE); if (mutex_space_status) util_out_print("Database file !AD now has !UL mutex queue slots", TRUE, fn_len, fn, NUM_CRIT_ENTRY(pvt_csd)); if (null_subs_status) util_out_print("Database file !AD now has null subscripts set to !AD", TRUE, fn_len, fn, strlen("EXISTING"), (pvt_csd->null_subs == ALWAYS) ? "ALWAYS " : (pvt_csd->null_subs == ALLOWEXISTING) ? "EXISTING" : "NEVER "); if (qdbrundown_status) util_out_print("Database file !AD now has quick database rundown flag set to !AD", TRUE, fn_len, fn, 5, (pvt_csd->mumps_can_bypass ? " TRUE" : "FALSE")); if (rec_size_status) util_out_print("Database file !AD now has maximum record size !UL", TRUE, fn_len, fn, pvt_csd->max_rec_size); if (rsrvd_bytes_status) util_out_print("Database file !AD now has !UL reserved bytes", TRUE, fn_len, fn, pvt_csd->reserved_bytes); if (stats_status) util_out_print("Database file !AD now has sharing of gvstats set to !AD", TRUE, fn_len, fn, 5, (CLI_PRESENT == stats_status) ? " TRUE" : "FALSE"); if (stdb_alloc_status) util_out_print("Database file !AD now has !UL statsdb allocation", TRUE, fn_len, fn, pvt_csd->statsdb_allocation); if (stdnullcoll_status) util_out_print("Database file !AD is now using !AD", TRUE, fn_len, fn, strlen("M standard null collation"), (CLI_PRESENT == stdnullcoll_status) ? "M standard null collation" : "GT.M null collation "); } else { if (0 == memcmp(csd->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv(csd); if (flush_buffers) wcs_flu(WCSFLU_FLUSH_HDR); else fileheader_sync(gv_cur_region); } } else exit_stat |= reg_exit_stat; if (got_standalone) { CLOSEFILE_RESET(fd, rc); /* resets "fd" to FD_INVALID */ db_ipcs_reset(gv_cur_region); } else { rel_crit(gv_cur_region); exit_stat |= gds_rundown(CLEANUP_UDI_TRUE); } mu_gv_cur_reg_free(); } free(pvt_csd); assert(!(exit_stat & EXIT_INF)); return (exit_stat & EXIT_ERR ? (int4)ERR_WCERRNOTCHG : (exit_stat & EXIT_WRN ? (int4)ERR_WCWRNNOTCHG : SS_NORMAL)); } fis-gtm-V7.0-005/sr_unix/mupip_set_jnlfile.c0000755000032200000250000000624114342376330017723 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "gtm_ipc.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_stat.h" #include "gtm_stdio.h" #include "gtm_string.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif #include "gdsroot.h" #include "gtm_facility.h" #include "gtmio.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "cli.h" #include "iosp.h" #include "jnl.h" #include "mupip_set.h" #include "mupint.h" #include "eintr_wrappers.h" #include "util.h" #include "mu_rndwn_file.h" #include "db_ipcs_reset.h" #include "anticipatory_freeze.h" GBLREF gd_region *gv_cur_region; GBLREF boolean_t need_no_standalone; error_def(ERR_BADTAG); /* Need for TAG_POLICY macro */ error_def(ERR_JNLFILNOTCHG); error_def(ERR_TEXT); /* Need for TAG_POLICY macro */ int4 mupip_set_jnlfile(char *jnl_fname, int jnl_fn_len) { int jnl_fd, status; char hdr_buffer[REAL_JNL_HDR_LEN]; jnl_file_header *header; char *errptr; int save_no; int rc; ZOS_ONLY(int realfiletag;) OPENFILE(jnl_fname, O_RDWR, jnl_fd); if (FD_INVALID == jnl_fd) { save_no = errno; util_out_print("Error opening journal file !AD", TRUE, LEN_AND_STR(jnl_fname)); errptr = (char *)STRERROR(save_no); util_out_print("open : !AZ", TRUE, errptr); return((int4)ERR_JNLFILNOTCHG); } #ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(jnl_fd, TAG_BINARY, &realfiletag)) TAG_POLICY_GTM_PUTMSG(jnl_fname, realfiletag, TAG_BINARY, errno); #endif LSEEKREAD(jnl_fd, 0, (sm_uc_ptr_t)hdr_buffer, SIZEOF(hdr_buffer), status); if (0 != status) { if (-1 != status) { save_no = errno; util_out_print("Error reading journal file !AD", TRUE, LEN_AND_STR(jnl_fname)); errptr = (char *)STRERROR(save_no); util_out_print("read : !AZ", TRUE, errptr); } else util_out_print("Premature end of journal file !AD", TRUE, LEN_AND_STR(jnl_fname)); return((int4)ERR_JNLFILNOTCHG); } header = (jnl_file_header *)hdr_buffer; if (SS_NORMAL != (status = mupip_set_jnlfile_aux(header, jnl_fname))) return status; JNL_LSEEKWRITE(NULL, NULL, jnl_fd, 0, (sm_uc_ptr_t)hdr_buffer, SIZEOF(hdr_buffer), status); if (0 != status) { save_no = errno; util_out_print("Error writing journal file !AD", TRUE, LEN_AND_STR(jnl_fname)); errptr = (char *)STRERROR(save_no); util_out_print("write : !AZ", TRUE, errptr); return((int4)ERR_JNLFILNOTCHG); } CLOSEFILE_RESET(jnl_fd, rc); /* resets "jnl_fd" to FD_INVALID */ if (-1 == rc) { save_no = errno; util_out_print("Error closing journal file !AD", TRUE, LEN_AND_STR(jnl_fname)); errptr = (char *)STRERROR(save_no); util_out_print("close :!AZ", TRUE, errptr); return((int4)ERR_JNLFILNOTCHG); } if (!need_no_standalone) db_ipcs_reset(gv_cur_region); return SS_NORMAL; } fis-gtm-V7.0-005/sr_unix/mupip_size.c0000644000032200000250000003056614342376330016403 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2022 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_string.h" #include "stp_parms.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "copy.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdskill.h" #include "muextr.h" #include "iosp.h" #include "cli.h" #include "cliif.h" #include "mu_reorg.h" #include "util.h" #include "filestruct.h" #include "error.h" #include "gdscc.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "t_qread.h" #include "tp.h" #include "mupint.h" /* Prototypes */ #include "mupip_size.h" #include "targ_alloc.h" #include "mupip_exit.h" #include "gv_select.h" #include "mu_outofband_setup.h" #include "gtmmsg.h" #include "mu_getlst.h" #include "warn_db_sz.h" #include "mu_getkey.h" error_def(ERR_INTEGERRS); error_def(ERR_MUNOACTION); error_def(ERR_MUNOFINISH); error_def(ERR_MUPCLIERR); error_def(ERR_MUSIZEINVARG); error_def(ERR_NOSELECT); GBLREF block_id mu_int_adj_prev[MAX_BT_DEPTH + 1]; GBLREF bool error_mupip; GBLREF bool mu_ctrlc_occurred; GBLREF bool mu_ctrly_occurred; GBLREF int muint_adj; GBLREF int4 mu_int_adj[MAX_BT_DEPTH + 1]; GBLREF sgmnt_data_ptr_t cs_data; GBLREF tp_region *grlist; GBLREF unsigned char rdfail_detail; GBLDEF boolean_t mu_subsc = FALSE; GBLDEF boolean_t mu_key = MUKEY_FALSE; GBLDEF int mu_sub_idx_st = 0; GBLDEF int mu_sub_idx_end = 0; GBLDEF boolean_t null_coll_key = FALSE; GBLREF boolean_t null_coll_key; GBLREF gv_key *mu_start_key; GBLREF gv_key *mu_end_key; GBLREF int mu_start_keyend; GBLREF int mu_end_keyend; typedef struct { enum {arsample, scan, impsample} heuristic; int4 samples; int4 level; int4 seed; } mupip_size_cfg_t; STATICFNDCL void mupip_size_check_error(void); /* * This function reads command line parameters and forms a configuration for mupip size invocation. * It later executes mupip size on each global based on the configuration * * MUPIP SIZE interface is described in GTM-7292 */ void mupip_size(void) { boolean_t restrict_reg = FALSE; boolean_t subscr = FALSE; char buff[MAX_LINE], cli_buff[MAX_LINE]; char *db_file_name; char *p_end; /* used for strtol validation */ glist exclude_gl_head, gl_head, *gl_ptr; int4 reg_max_rec, reg_max_key, reg_max_blk; mupip_size_cfg_t mupip_size_cfg = { impsample, 1000, 1, 0 }; /* configuration default values */ sgmnt_addrs *tcsa; uint4 status = EXIT_NRM; unsigned char key_buff[2048]; unsigned short BUFF_LEN = SIZEOF(buff), n_len; unsigned short keylen; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; mu_outofband_setup(); error_mupip = FALSE; memset(mu_int_adj, 0, SIZEOF(mu_int_adj)); memset(mu_int_adj_prev, 0, SIZEOF(mu_int_adj_prev)); /* Region qualifier */ grlist = NULL; if (CLI_PRESENT == cli_present("REGION")) { restrict_reg = TRUE; gvinit(); /* init gd_header (needed to call mu_getlst) */ mu_getlst("REGION", SIZEOF(tp_region)); } mupip_size_check_error(); if (CLI_PRESENT == cli_present("SUBSCRIPT")) { if (NULL == gv_target) gv_target = (gv_namehead *)targ_alloc(DUMMY_GLOBAL_VARIABLE_LEN, NULL, NULL); subscr = TRUE; keylen = SIZEOF(key_buff); if (0 == cli_get_str("SUBSCRIPT", (char *)key_buff, &keylen)) mupip_exit(ERR_MUPCLIERR); if (FALSE == mu_getkey(key_buff, keylen)) { error_mupip = TRUE; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_MUSIZEINVARG, 2, LEN_AND_LIT("SUBSCRIPT")); } mupip_size_check_error(); assert(mu_key); /* or else "mu_getkey" call above would have returned FALSE */ /* Create the buff with the global names only to be selected by gv_select below */ p_end = cli_buff; memcpy(p_end ,"^",1); p_end++; memcpy(p_end , mu_start_key->base, mu_sub_idx_st); p_end += mu_sub_idx_st; if (mu_sub_idx_end) { memcpy(p_end ,":^",2); p_end += 2; memcpy(p_end, mu_end_key->base, mu_sub_idx_end); p_end += mu_sub_idx_end; } *p_end = '\0'; n_len = p_end - cli_buff; gv_target->regcnt--; gv_target = NULL; } /* SELECT qualifier */ if (!subscr) { memset(cli_buff, 0, SIZEOF(cli_buff)); n_len = SIZEOF(cli_buff); } if ((CLI_PRESENT != cli_present("SELECT"))) { if (!subscr) { /* No select or subscript */ n_len = 1; cli_buff[0] = '*'; } } else if (FALSE == cli_get_str("SELECT", cli_buff, &n_len)) { n_len = 1; cli_buff[0] = '*'; } /* gv_select will select globals for this clause*/ gv_select(cli_buff, n_len, FALSE, "SELECT", &gl_head, ®_max_rec, ®_max_key, ®_max_blk, restrict_reg); if (!gl_head.next) { error_mupip = TRUE; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOSELECT); } mupip_size_check_error(); if (CLI_PRESENT == cli_present("ADJACENCY")) { assert(SIZEOF(muint_adj) == SIZEOF(int4)); if (0 == cli_get_int("ADJACENCY", (int4 *)&muint_adj)) { error_mupip = TRUE; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_MUPCLIERR); } } else muint_adj = DEFAULT_ADJACENCY; /* HEURISTIC qualifier */ if (cli_present("HEURISTIC.SCAN") == CLI_PRESENT) { mupip_size_cfg.heuristic = scan; if (cli_present("HEURISTIC.LEVEL")) { boolean_t valid = TRUE; if (cli_get_str("HEURISTIC.LEVEL", buff, &BUFF_LEN)) { if (cli_is_hex_explicit(buff)) { /* buff starts with 0x or -0x or +0x. Remove the 0x, and retain the +/- */ if (('+' == *buff) || ('-' == *buff)) *(buff + 2) = *buff; /* Overwrite the X with +/- */ mupip_size_cfg.level = STRTOL(buff+2, &p_end, 16); } else mupip_size_cfg.level = STRTOL(buff, &p_end, 10); valid = (*p_end == '\0'); } else valid = FALSE; if (!valid || mupip_size_cfg.level <= -MAX_BT_DEPTH || MAX_BT_DEPTH <= mupip_size_cfg.level) { error_mupip = TRUE; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_MUSIZEINVARG, 2, LEN_AND_LIT("HEURISTIC.LEVEL")); } } /* else level is already initialized with default value */ } else if (cli_present("HEURISTIC.ARSAMPLE") == CLI_PRESENT || cli_present("HEURISTIC.IMPSAMPLE") == CLI_PRESENT) { if (cli_present("HEURISTIC.ARSAMPLE") == CLI_PRESENT) mupip_size_cfg.heuristic = arsample; else if (cli_present("HEURISTIC.IMPSAMPLE") == CLI_PRESENT) mupip_size_cfg.heuristic = impsample; if (cli_present("HEURISTIC.SAMPLES")) { boolean_t valid = cli_get_int("HEURISTIC.SAMPLES", &(mupip_size_cfg.samples)); if (!valid || mupip_size_cfg.samples <= 0){ error_mupip = TRUE; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_MUSIZEINVARG, 2, LEN_AND_LIT("HEURISTIC.SAMPLES")); } } /* else samples is already initialized with default value */ /* undocumented SEED parameter used for testing sampling method */ if (cli_present("HEURISTIC.SEED")) { boolean_t valid = cli_get_int("HEURISTIC.SEED", &(mupip_size_cfg.seed)); if (!valid){ error_mupip = TRUE; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_MUSIZEINVARG, 2, LEN_AND_LIT("HEURISTIC.SEED")); } } /* else seed will be based on the time */ } mupip_size_check_error(); /* run mupip size on each global */ for (gl_ptr = gl_head.next; gl_ptr; gl_ptr = gl_ptr->next) { util_out_print("!/Global: !AD (region !AD)", FLUSH, GNAME(gl_ptr).len, GNAME(gl_ptr).addr, REG_LEN_STR(gl_ptr->reg)); switch (mupip_size_cfg.heuristic) { case scan: status |= mu_size_scan(gl_ptr, mupip_size_cfg.level); break; case arsample: status |= mu_size_arsample(gl_ptr, mupip_size_cfg.samples, mupip_size_cfg.seed); break; case impsample: status |= mu_size_impsample(gl_ptr, mupip_size_cfg.samples, mupip_size_cfg.seed); break; default: assertpro(FALSE && mupip_size_cfg.heuristic); break; } if (mu_ctrlc_occurred || mu_ctrly_occurred) mupip_exit(ERR_MUNOFINISH); } if ((NULL != grlist) && (NULL != grlist->reg)) { tcsa = REG2CSA(grlist->reg); if ((NULL != tcsa) && (NULL != tcsa->ti) && (0 != tcsa->ti->total_blks) && (NULL != tcsa->hdr) && (0 != MAXTOTALBLKS(tcsa->hdr))) { if ((NULL != grlist->reg->dyn.addr) && (0 != grlist->reg->dyn.addr->fname_len)) db_file_name = (char *)grlist->reg->dyn.addr->fname; else db_file_name = ""; warn_db_sz(db_file_name, 0, tcsa->ti->total_blks, MAXTOTALBLKS(tcsa->hdr)); } } mupip_exit(status == EXIT_NRM ? SS_NORMAL : ERR_MUNOFINISH); } STATICDEF void mupip_size_check_error(void) { if (error_mupip) { util_out_print("!/MUPIP SIZE cannot proceed with above errors!/", FLUSH); mupip_exit(ERR_MUNOACTION); } } /* Performs a random traversal for the sampling methods */ enum cdb_sc mu_size_rand_traverse(double *r, double *a) { block_id nBlkId, valBlk[MAX_RECS_PER_BLK]; /* valBlk[j] := value in j-th record of current block */ boolean_t first_key = TRUE, is_mm, long_blk_id, musz_range_done; cache_rec_ptr_t cr; enum cdb_sc status; int blk_size, bstar_rec_sz, buff_length, cycle, key_size, name_len, rec_len; int4 cmp_key, musz_rec, random, rCnt; /* rCnt : number of entries in valBlk */ register gv_namehead *pTarg; register srch_blk_status *pCurr; register srch_hist *pTargHist; sm_uc_ptr_t pVal, pTop, pRec, pBlkBase; trans_num tn; uchar_ptr_t key_base, ptr; unsigned char buff[MAX_KEY_SZ + 1], nLevl; unsigned short nRecLen, rec_cmpc; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; is_mm = (dba_mm == cs_data->acc_meth); pTarg = gv_target; pTargHist = &gv_target->hist; /* The following largely mimics gvcst_search/gvcst_search_blk */ nBlkId = pTarg->root; tn = cs_addrs->ti->curr_tn; if (NULL == (pBlkBase = t_qread(nBlkId, (sm_int_ptr_t)&cycle, &cr))) return (enum cdb_sc)rdfail_detail; nLevl = ((blk_hdr_ptr_t)pBlkBase)->levl; if (MAX_BT_DEPTH < (int)nLevl) { assert(CDB_STAGNATE > t_tries); return cdb_sc_maxlvl; } if (0 == (int)nLevl) { assert(CDB_STAGNATE > t_tries); return cdb_sc_badlvl; } pTargHist->depth = (int)nLevl; pCurr = &pTargHist->h[nLevl]; (pCurr + 1)->blk_num = 0; pCurr->tn = tn; pCurr->cycle = cycle; pCurr->cr = cr; for (;;) { long_blk_id = IS_64_BLK_ID(pBlkBase); bstar_rec_sz = bstar_rec_size(long_blk_id); musz_range_done = FALSE; musz_rec = 0; assert(pCurr->level == nLevl); pCurr->cse = NULL; pCurr->blk_num = nBlkId; pCurr->buffaddr = pBlkBase; BLK_LOOP(rCnt, pRec, pBlkBase, pTop, nRecLen, musz_range_done) { /* enumerate records in block */ GET_AND_CHECK_RECLEN(status, nRecLen, pRec, pTop, nBlkId, long_blk_id); if (cdb_sc_normal != status) { assert(CDB_STAGNATE > t_tries); return status; } CHECK_ADJACENCY(nBlkId, nLevl, a[nLevl]); if (mu_subsc) /*Subscript option chosen */ { rec_cmpc = EVAL_CMPC((rec_hdr_ptr_t)pRec); key_base = pRec + SIZEOF(rec_hdr); if ((((rec_hdr *)pRec)->rsiz) != bstar_rec_sz) /* Did not find the star key */ { GET_KEY_CPY_BUFF(key_base, rec_cmpc, ptr, first_key, name_len, key_size,buff, buff_length, rec_len); if (0 == key_size) continue; /* indication of trouble parsing the block - don't use it */ cmp_key = memcmp(buff, mu_start_key->base, mu_start_key->end + 1); if (mu_end_key) { if (0 < memcmp(buff, mu_end_key->base, mu_end_key->end + 1)) musz_range_done = TRUE; } else if (0 < cmp_key) musz_range_done = TRUE; if (0 > cmp_key) continue; } valBlk[musz_rec] = nBlkId; musz_rec++; } else valBlk[rCnt] = nBlkId; } (mu_subsc) ? (r[nLevl] = musz_rec) : (r[nLevl] = rCnt); /* randomly select next block */ random = ((mu_subsc) ? (int4)(musz_rec * drand48()) : (int4)(rCnt * drand48())); random = random & 0x7fffffff; /* to make sure that the sign bit(msb) is off */ nBlkId = valBlk[random]; if (is_mm && (nBlkId > cs_addrs->total_blks)) { if (cs_addrs->total_blks < cs_addrs->ti->total_blks) return cdb_sc_helpedout; else return cdb_sc_blknumerr; } --pCurr; --nLevl; if (nLevl < 1) break; pCurr->tn = cs_addrs->ti->curr_tn; if (NULL == (pBlkBase = t_qread(nBlkId, (sm_int_ptr_t)&pCurr->cycle, &pCurr->cr))) return (enum cdb_sc)rdfail_detail; if (((blk_hdr_ptr_t)pBlkBase)->levl != nLevl) { assert(CDB_STAGNATE > t_tries); return cdb_sc_badlvl; } } return cdb_sc_normal; } fis-gtm-V7.0-005/sr_unix/mupip_size.h0000644000032200000250000000565014342376330016404 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2020 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. * * * ****************************************************************/ #ifndef MUPIP_SIZE_H_INCLUDED #define MUPIP_SIZE_H_INCLUDED #include "cdb_sc.h" #define EPS 1e-6 #define MAX_RECS_PER_BLK 65535 #define ROUND(X) ((int)((X) + 0.5)) /* c89 does not have round() and some Solaris machines uses that compiler */ #define SQR(X) ((double)(X) * (double)(X)) #define BLK_LOOP(rCnt, pRec, pBlkBase, pTop, nRecLen, lDone) \ for (rCnt = 0, pTop = pBlkBase + ((blk_hdr_ptr_t)pBlkBase)->bsiz, \ pRec = pBlkBase + SIZEOF(blk_hdr); \ rCnt < MAX_RECS_PER_BLK && (pRec != pTop) && (!lDone); rCnt++, pRec += nRecLen) #define CLEAR_VECTOR(v) \ { \ int J; \ \ for (J = 0; MAX_BT_DEPTH >= J; J++) \ v[J] = 0; \ } #define GET_KEY_CPY_BUFF(KEY_BASE, REC_CMPC, PTR, FIRST_KEY, NAME_LEN, KEY_SIZE, BUFF, BUFF_LENGTH, REC_LEN) \ MBSTART { \ for (PTR = key_base; ;) \ { \ if (KEY_DELIMITER == *PTR++) \ { \ if (FIRST_KEY) \ { \ FIRST_KEY = FALSE; \ NAME_LEN = (int)(PTR - key_base); \ } \ if (KEY_DELIMITER == *PTR++) \ break; \ } \ } \ KEY_SIZE = (int)(PTR - KEY_BASE); \ if (KEY_SIZE + REC_CMPC > MAX_KEY_SZ) \ KEY_SIZE = 0; /* user should check and skip */ \ else \ { \ memcpy(BUFF + REC_CMPC, KEY_BASE, KEY_SIZE); \ BUFF_LENGTH = REC_CMPC + KEY_SIZE; \ REC_LEN = BUFF_LENGTH; \ } \ } MBEND #define CHECK_COLL_KEY(GL_PTR, NULL_COLL_KEY) \ MBSTART { \ if (GL_PTR->reg->std_null_coll && (!NULL_COLL_KEY)) \ { \ GTM2STDNULLCOLL(mu_start_key->base, mu_start_key->end); \ if (mu_end_key) \ GTM2STDNULLCOLL(mu_end_key->base, mu_end_key->end); \ NULL_COLL_KEY = TRUE; \ } \ if (!(GL_PTR->reg->std_null_coll) && (NULL_COLL_KEY)) \ { \ STD2GTMNULLCOLL(mu_start_key->base, mu_start_key->end); \ if (mu_end_key) \ STD2GTMNULLCOLL(mu_end_key->base, mu_end_key->end); \ NULL_COLL_KEY = FALSE; \ } \ } MBEND enum size_cumulative_type { BLK, REC, ADJ, CUMULATIVE_TYPE_MAX }; void mupip_size(void); int4 mu_size_arsample(glist *gl_ptr, uint M, int seed); int4 mu_size_impsample(glist *gl_ptr, int4 M, int4 seed); int4 mu_size_scan(glist *gl_ptr, int4 level); enum cdb_sc mu_size_rand_traverse(double *r, double *a); #endif fis-gtm-V7.0-005/sr_unix/mupip_trigger.h0000644000032200000250000000107514342376330017072 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef MUPIP_TRIGGER_INCLUDED #define MUPIP_TRIGGER_INCLUDED void mupip_trigger(void); #endif /* MUPIP_TRIGGER_INCLUDED */ fis-gtm-V7.0-005/sr_unix/mupip_upgrade_standalone.c0000755000032200000250000000572214342376330021267 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_unistd.h" #include "gtm_ipc.h" #include "gtm_stdio.h" #include #include #include #include "gtm_sem.h" #include "iosp.h" #include "gtm_c_stack_trace.h" #include "eintr_wrappers.h" #include "eintr_wrapper_semop.h" #include "ipcrmid.h" #include "util.h" #include "mupip_upgrade_standalone.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" /* Lock a file using semaphore. * This is written so that we can use it to lock all database pre-V4.2000. * Parameters: * fn : database file name. * semid: returns semaphore id. Caller may need to remove it. * Return TRUE if successful. * Else Return FALSE. */ boolean_t mupip_upgrade_standalone(char *fn, int *semid) { struct sembuf sop[4]; int key, semop_res, sems; *semid = INVALID_SEMID; if ((key = FTOK(fn, 1)) == -1) /* V3.2 and V4.0x had project id = 1 */ { util_out_print("Error with ftok", TRUE); return FALSE; } /* If shared memory exists, the disk file is probably incomplete */ if ((gtm_shmget(key, 0, 0, FALSE) != -1) || errno != ENOENT) { util_out_print("File is locked by another user", TRUE); return FALSE; } if ((sems = semget(key, FTOK_SEM_PER_ID, RWDALL | IPC_CREAT | IPC_NOWAIT)) == -1) { util_out_print("Error with semget", TRUE); return FALSE; } /* * Both semaphores must have value 0 and then all will be * incremented to completely lockout any other potential users * until we are done */ sop[0].sem_num = 0; sop[0].sem_op = 0; /* First check all semaphore have 0 value */ sop[1].sem_num = 1; sop[1].sem_op = 0; sop[2].sem_num = 0; sop[2].sem_op = 1; /* Increment all semaphores */ sop[3].sem_num = 1; sop[3].sem_op = 1; sop[0].sem_flg = sop[1].sem_flg = sop[2].sem_flg = sop[3].sem_flg = SEM_UNDO | IPC_NOWAIT; SEMOP(sems, sop, 4, semop_res, NO_WAIT); if (-1 == semop_res) { if (errno == EAGAIN) { util_out_print("File already open by another process", TRUE); return FALSE; } util_out_print("Error with SEMOP", TRUE); return FALSE; } /* See if someone built some shared memory in the time we were * building semaphores. If so, we have failed */ if((gtm_shmget(key, 0, 0, FALSE) != -1) || errno != ENOENT) { if (0 != sem_rmid(sems)) { util_out_print("Error with sem_rmid", TRUE); return FALSE; } util_out_print("File is locked by another user", TRUE); return FALSE; } *semid = sems; return TRUE; } fis-gtm-V7.0-005/sr_unix/mupip_upgrade_standalone.h0000755000032200000250000000116614342376330021272 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef MUPIP_UPGRADE_STANDALONE_INCLUDED #define MUPIP_UPGRADE_STANDALONE_INCLUDED boolean_t mupip_upgrade_standalone(char *, int *); #endif /* MUPIP_UPGRADE_STANDALONE_INCLUDED */ fis-gtm-V7.0-005/sr_unix/muprecsp.h0000755000032200000250000000122414342376330016052 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #ifndef MUPRECSP_H_INCLUDED #define MUPRECSP_H_INCLUDED #define EXT_MJF ".mjf" #define EXT_BROKEN ".broken" #define EXT_LOST ".lost" #endif fis-gtm-V7.0-005/sr_unix/mur_cre_file_extfmt.c0000755000032200000250000002567614342376330020252 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2021 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_string.h" #include "gtm_stdio.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "buddy_list.h" #include "jnl.h" #include "hashtab_mname.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "muprec.h" #include "gtmio.h" #include "io.h" #include "io_params.h" #include "op.h" #include "iosp.h" #include "gtmmsg.h" #include "gtm_rename.h" #include "repl_instance.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtm_utf8.h" #include "gtm_multi_proc.h" #include "interlock.h" /* This function creates a file to hold either journal extract or broken transaction or lost transaction data. * The headerline of the file created will contain one of the following * * Created by MUPIP JOURNAL -EXTRACT --> EXTRACTLABELEXTRACT * Created by MUPIP JOURNAL -RECOVER --> EXTRACTLABELRECOVER * Created by MUPIP JOURNAL -ROLLBACK --> EXTRACTLABELROLLBACK[PRIMARY|SECONDARY]INSTANCENAME * * EXTRACTLABEL = NORMAL-EXTRACT-LABEL | DETAILED-EXTRACT-LABEL // see muprec.h * : Space * RECOVER, EXTRACT, ROLLBACK, PRIMARY, SECONDARY : literal strings that appear as is. * INSTANCENAME : Name of the replication instance */ GBLREF mur_gbls_t murgbl; GBLREF mur_opt_struct mur_options; GBLREF int (*op_open_ptr)(mval *v, mval *p, mval *t, mval *mspace); GBLREF mur_shm_hdr_t *mur_shm_hdr; /* Pointer to mur_forward-specific header in shared memory */ GBLREF reg_ctl_list *mur_ctl; GBLREF readonly char *ext_file_type[]; LITREF mval literal_zero; error_def(ERR_FILECREATE); error_def(ERR_FILENAMETOOLONG); error_def(ERR_FILENOTCREATE); int4 mur_cre_file_extfmt(jnl_ctl_list *jctl, int recstat) { fi_type *file_info; char *ptr, rename_fn[MAX_FN_LEN + 1]; int rename_fn_len, base_len, fn_exten_size, tmplen, rctl_index; uint4 status; mval op_val, op_pars; boolean_t is_stdout; /* Output will go STDOUT?. Matters only for single-region in this function */ boolean_t need_rel_latch, copy_from_shm, single_reg, release_latch, key_reset; boolean_t is_dummy_gbldir, fname_is_devnull, is_regfile=TRUE; reg_ctl_list *rctl; gd_region *reg; shm_reg_ctl_t *shm_rctl_start, *shm_rctl; # ifdef DEBUG unsigned char *tmp_key; # endif static readonly char *fn_exten[] = {EXT_MJF, EXT_BROKEN, EXT_LOST}; static readonly unsigned char open_params_list[]= { (unsigned char)iop_m, (unsigned char)iop_newversion, (unsigned char)iop_noreadonly, (unsigned char)iop_nowrap, (unsigned char)iop_stream, (unsigned char)iop_buffered, 1, 0x03, (unsigned char)iop_eol }; assert(GOOD_TN == recstat || BROKEN_TN == recstat || LOST_TN == recstat); assert(0 == GOOD_TN); assert(1 == BROKEN_TN); assert(2 == LOST_TN); assert(GOOD_TN != recstat || mur_options.extr[GOOD_TN]); need_rel_latch = FALSE; single_reg = (1 == murgbl.reg_total); rctl = jctl->reg_ctl; if (multi_proc_in_use) { /* Determine if some other parallel process has already created this file. If so, use that. If not create one */ assert(!single_reg); GRAB_MULTI_PROC_LATCH_IF_NEEDED(release_latch); assert(release_latch); if ('\0' == mur_shm_hdr->extr_fn[recstat].fn[0]) { /* No one created this file. Let us first determine the name of the extract file we need to create. * This is needed so each parallel process can create a temporary extract file based on this name. */ need_rel_latch = TRUE; } else { /* We know mur_shm_hdr->extr_fn[recstat] has been completely initialized. And it is not touched anymore. * So we can read it without the latch. Release it now and read file name later. */ REL_MULTI_PROC_LATCH_IF_NEEDED(release_latch); } if (NULL == multi_proc_key) { MUR_SET_MULTI_PROC_KEY(rctl, multi_proc_key); key_reset = TRUE; } else { /* Assert that key is already set to the right value */ # ifdef DEBUG MUR_SET_MULTI_PROC_KEY(rctl, tmp_key); assert(tmp_key == multi_proc_key); # endif key_reset = FALSE; } } is_stdout = single_reg && mur_options.extr_fn[recstat] && mur_options.extr_fn_is_stdout[recstat]; fname_is_devnull = mur_options.extr_fn_is_devnull[recstat] && mur_options.extr_fn[recstat]; /* Keep fname_is_regfile to be TRUE by default. FALSE only if it is sure to be something else */ is_regfile = mur_options.extr_fn[recstat] ? mur_options.extr_fn_is_regfile[recstat] : TRUE; assert(is_regfile ? (fname_is_devnull==0 && is_stdout==0) : TRUE); if (!is_stdout) { file_info = (void *)malloc(SIZEOF(fi_type)); file_info->fn = malloc(MAX_FN_LEN + 1); /* +1 for '\0' */ memset(file_info->fn, 0, MAX_FN_LEN + 1); if (0 == mur_options.extr_fn_len[recstat]) { if (!multi_proc_in_use || need_rel_latch) { mur_options.extr_fn[recstat] = malloc(MAX_FN_LEN + 1); /* +1 for '\0' */ memset(mur_options.extr_fn[recstat], 0, MAX_FN_LEN + 1); ptr = (char *)&jctl->jnl_fn[jctl->jnl_fn_len]; while (DOT != *ptr) /* we know journal file name always has a DOT */ ptr--; base_len = (int)(ptr - (char *)&jctl->jnl_fn[0]); mur_options.extr_fn_len[recstat] = base_len; memcpy(mur_options.extr_fn[recstat], jctl->jnl_fn, base_len); fn_exten_size = STRLEN(fn_exten[recstat]); memcpy(mur_options.extr_fn[recstat] + base_len, fn_exten[recstat], fn_exten_size); mur_options.extr_fn_len[recstat] += fn_exten_size; copy_from_shm = FALSE; } else { /* Copy extract file name that has already been determined by another process */ copy_from_shm = TRUE; } } else copy_from_shm = FALSE; if (!copy_from_shm) { memcpy(file_info->fn, mur_options.extr_fn[recstat], mur_options.extr_fn_len[recstat]); file_info->fn_len = mur_options.extr_fn_len[recstat]; assert(!multi_proc_in_use || need_rel_latch || !memcmp(mur_shm_hdr->extr_fn[recstat].fn, file_info->fn, file_info->fn_len)); if (need_rel_latch) { /* Now that the extract file name has been determined, copy this over to shared memory */ memcpy(mur_shm_hdr->extr_fn[recstat].fn, file_info->fn, file_info->fn_len); mur_shm_hdr->extr_fn_len[recstat] = file_info->fn_len; REL_MULTI_PROC_LATCH_IF_NEEDED(release_latch); } } else { assert(!need_rel_latch); memcpy(file_info->fn, mur_shm_hdr->extr_fn[recstat].fn, mur_shm_hdr->extr_fn_len[recstat]); file_info->fn_len = mur_shm_hdr->extr_fn_len[recstat]; } rctl->file_info[recstat] = file_info; rctl->extr_fn_len_orig[recstat] = file_info->fn_len; /* Now adjust the file name to be region-specific. Add a region-name suffix. If no region-name is found, * add region #. Do this only if there are at least 2 regions. Otherwise no need of a merge sort. * Also, this is not required if o/p is /dev/null */ if (!single_reg && !fname_is_devnull) { reg = rctl->gd; /* Calculate if appending region name will not overflow allocation. If so error out */ tmplen = file_info->fn_len; tmplen++; /* for the '_' */ /* If this region corresponds to a gld created by "gd_load" then it is a real gld and use that * "region-name". Else it is a dummy gld created by "mu_gv_cur_reg_init" in which case use a * number (of the rctl in the rctl array) to differentiate multiple journal files. Thankfully * "mu_gv_cur_reg_init" uses "create_dummy_gbldir" which sets gd->link = NULL whereas "gd_load" * sets it to a non-NULL value. So use that as the distinguishing characteristic. */ is_dummy_gbldir = reg->owning_gd->is_dummy_gbldir; if (!is_dummy_gbldir) { assert(reg->rname_len); tmplen += reg->rname_len; } else { /* maximum # of regions is limited by MULTI_PROC_MAX_PROCS (since that is the limit * that "gtm_multi_proc" can handle. Use the byte-length of MULTI_PROC_MAX_PROCS-1. */ assert(!memcmp(reg->rname, "DEFAULT", reg->rname_len)); assert(1000 == MULTI_PROC_MAX_PROCS); tmplen += 3; /* 999 is maximum valid value and has 3 decimal digits */ } if (tmplen > MAX_FN_LEN) { /* We cannot create a file. Error out */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_FILENAMETOOLONG); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) MAKE_MSG_ERROR(ERR_FILENOTCREATE), 4, LEN_AND_STR(ext_file_type[recstat]), file_info->fn_len, file_info->fn); return ERR_FILENOTCREATE; } tmplen = file_info->fn_len; ptr = &file_info->fn[tmplen]; *ptr++ = '_'; tmplen++; if (!is_dummy_gbldir) { memcpy(ptr, reg->rname, reg->rname_len); tmplen += reg->rname_len; } else tmplen += SNPRINTF(ptr, MAX_FN_LEN - tmplen, "%d", rctl - &mur_ctl[0]); file_info->fn_len = tmplen; } rename_fn_len = ARRAYSIZE(rename_fn); /* Only rename regfiles... */ if (is_regfile && (RENAME_FAILED == rename_file_if_exists(file_info->fn, file_info->fn_len, rename_fn, &rename_fn_len, &status))) return status; op_pars.mvtype = MV_STR; op_pars.str.len = SIZEOF(open_params_list); op_pars.str.addr = (char *)open_params_list; op_val.mvtype = MV_STR; op_val.str.len = file_info->fn_len; op_val.str.addr = (char *)file_info->fn; if ((status = (*op_open_ptr)(&op_val, &op_pars, (mval *)&literal_zero, NULL)) == 0) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) MAKE_MSG_ERROR(ERR_FILENOTCREATE), 4, LEN_AND_STR(ext_file_type[recstat]), file_info->fn_len, file_info->fn, errno); if (single_reg) murgbl.filenotcreate_displayed[recstat] = TRUE; return ERR_FILENOTCREATE; } } else rctl->file_info[recstat] = NULL; /* special meaning for STDOUT */ if (single_reg) { /* For multi-region, this header writing will be done later as part of "mur_merge_sort_extfmt" */ mur_write_header_extfmt(jctl, NULL, NULL, recstat); if (is_regfile) /* Only show the message for file creation, if it were a regular file. */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_FILECREATE, 4, LEN_AND_STR(ext_file_type[recstat]), file_info->fn_len, file_info->fn); } else if (multi_proc_in_use) { if (key_reset) multi_proc_key = NULL; /* reset key until it can be set to rctl's region-name again */ /* Record the fact that this child process created an extract file in shared memory * so parent can clean it up later in case the child process dies abruptly (e.g. GTM-F-MEMORY) * before it does the full copy of needed information at the end of "mur_forward_multi_proc". */ rctl_index = rctl - &mur_ctl[0]; shm_rctl_start = mur_shm_hdr->shm_rctl_start; shm_rctl = &shm_rctl_start[rctl_index]; shm_rctl->extr_file_created[recstat] = TRUE; } rctl->extr_file_created[recstat] = TRUE; return SS_NORMAL; } fis-gtm-V7.0-005/sr_unix/mur_read_file_sp.c0000755000032200000250000001554114342376330017515 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2015 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 "min_max.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_stat.h" #include "gtm_string.h" #include "gtm_aio.h" #include "gtmio.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" #include "hashtab_mname.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "muprec.h" #include "mur_read_file.h" #include "iosp.h" #include "copy.h" #include "gtmmsg.h" #include "repl_sp.h" /* for F_CLOSE used by the JNL_FD_CLOSE macro */ GBLREF mur_opt_struct mur_options; GBLREF mur_gbls_t murgbl; error_def(ERR_JNLFILEOPNERR); error_def(ERR_SYSCALL); #ifdef MUR_USE_AIO /**************************************************************************************** * Function Name: mur_fread_start * Input: struct mur_buffer_desc * buff * Output : SS_NORMAL on successful * error status on unsuccessful * This function starts an asynchronous read in a given buffer ****************************************************************************************/ /* #GTM_THREAD_SAFE : The below function (mur_fread_start) is thread-safe */ uint4 mur_fread_start(jnl_ctl_list *jctl, mur_buff_desc_t *buff) { buff->aiocbp->aio_offset = buff->dskaddr; buff->aiocbp->aio_buf = (char *)buff->base; buff->blen = MIN(MUR_BUFF_SIZE, jctl->eof_addr - buff->dskaddr); buff->aiocbp->aio_nbytes = buff->blen; buff->rip_channel = jctl->channel; /* store channel that issued the AIO in order to use later for aio_cancel() */ assert(!buff->read_in_progress); buff->read_in_progress = TRUE; AIO_READ(buff->rip_channel, buff->aiocbp, jctl->status, jctl->status2); return jctl->status; } /************************************************************************************ * Function name: mur_fread_wait * Input : struct mur_buffer_desc *buff * Output: SS_NORMAL on success * error status on unsuccessful * Purpose: The purpose of this routine is to make sure that a previously issued asysnchronous read * in a given buffer has completed **************************************************************************************/ /* #GTM_THREAD_SAFE : The below function (mur_fread_wait) is thread-safe */ uint4 mur_fread_wait(jnl_ctl_list *jctl, mur_buff_desc_t *buff) { ssize_t nbytes; assert(buff->read_in_progress); buff->read_in_progress = FALSE; /* The aio_error function returns the error status associated with the specified aiocbp. If the aio_error function returns * anything but EINPROGRESS, the asynchronous I/O operation has completed. Subsequently, we can fetch the status of the * operation from a call to aio_return. */ AIO_ERROR(buff->aiocbp, jctl->status); if (-1 != jctl->status) { AIO_RETURN(buff->aiocbp, nbytes); /* if successful jctl->status will contain number of bytes read */ if (-1 != nbytes) { if (buff->blen == nbytes) return (jctl->status = SS_NORMAL); /* AIO_READ didn't fetch the requested size chunk */ assert(buff->blen > nbytes); DO_FILE_READ(buff->rip_channel, buff->dskaddr + nbytes, buff->base + nbytes, buff->blen - nbytes, jctl->status, jctl->status2); return jctl->status; } } return (jctl->status = errno); } /* #GTM_THREAD_SAFE : The below function (mur_fread_cancel) is thread-safe */ /* cancel asynchronous read */ uint4 mur_fread_cancel(jnl_ctl_list *jctl) { int status, index, save_err; reg_ctl_list *rctl; mur_read_desc_t *mur_desc; mur_buff_desc_t *seq_buff; rctl = jctl->reg_ctl; mur_desc = rctl->mur_desc; /* At most one buffer can have read_in_progress, not both */ assert(!(mur_desc->seq_buff[0].read_in_progress && mur_desc->seq_buff[1].read_in_progress)); for (index = 0, save_err = 0; index < ARRAYSIZE(mur_desc->seq_buff); index++) { seq_buff = &mur_desc->seq_buff[index]; if (seq_buff->read_in_progress) { AIO_CANCEL(seq_buff->rip_channel, NULL, status); if (-1 == status) save_err = errno; else if (AIO_NOTCANCELED == status) /* the OS cannot cancel the aio once it has actually started */ jctl->status = mur_fread_wait(seq_buff); /* wait for it to finish. */ seq_buff->read_in_progress = FALSE; } } /* Note that although the cancellation errored out for rip_channel, we are storing the status in jctl which need not * actually be the jctl corresponding to rip_channel */ return (jctl->status = ((0 == save_err) ? SS_NORMAL : save_err)); } #endif /* MUR_USE_AIO */ /* #GTM_THREAD_SAFE : The below function (mur_fopen_sp) is thread-safe */ /* Returns 0 (SS_NORMAL) for success; Non-zero for failure */ uint4 mur_fopen_sp(jnl_ctl_list *jctl, reg_ctl_list *rctl) { struct stat stat_buf; int status, perms; sgmnt_addrs *csa; ZOS_ONLY(int realfiletag;) perms = O_RDONLY; jctl->read_only = TRUE; jctl->reg_ctl = rctl; /* fill in reg_ctl backpointer from jctl to rctl. Note: rctl could be NULL */ /* Both for recover and rollback open in read/write mode. We do not need to write in journal file * for mupip journal extract/show/verify or recover -forward. So open it as read-only */ if (mur_options.update && !mur_options.forward) { perms = O_RDWR; jctl->read_only = FALSE; } jctl->channel = OPEN((char *)jctl->jnl_fn, perms); if (FD_INVALID != jctl->channel) { FSTAT_FILE(jctl->channel, &stat_buf, status); if (-1 != status) { # ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(jctl->channel, TAG_BINARY, &realfiletag)) TAG_POLICY_GTM_PUTMSG((char *)jctl->jnl_fn, errno, realfiletag, TAG_BINARY); # endif jctl->os_filesize = (off_jnl_t)stat_buf.st_size; return SS_NORMAL; } jctl->status = errno; JNL_FD_CLOSE(jctl->channel, status); /* sets jctl->channel to NOJNL */ } else jctl->status = errno; assert(NOJNL == jctl->channel); csa = JCTL2CSA(jctl); /* need JCTL2CSA macro instead of jctl->reg_ctl->csa because rctl could be NULL */ if (ENOENT == jctl->status) /* File Not Found is a common error, so no need for SYSCALL */ gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_JNLFILEOPNERR, 2, jctl->jnl_fn_len, jctl->jnl_fn, jctl->status); else gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(12) ERR_JNLFILEOPNERR, 2, jctl->jnl_fn_len, jctl->jnl_fn, ERR_SYSCALL, 5, LEN_AND_STR((-1 == jctl->channel) ? "open" : "fstat"), CALLFROM, jctl->status); return ERR_JNLFILEOPNERR; } fis-gtm-V7.0-005/sr_unix/murgetlst.c0000755000032200000250000000257314342376330016245 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "gtm_string.h" #include "cli.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "murest.h" #include "mupip_exit.h" GBLREF inc_list_struct in_files; static readonly char name[] = "INPUT_FILE"; void murgetlst(void) { char *c1, *c2, buff[MAX_LINE]; unsigned short len; inc_list_struct *ptr; error_def(ERR_MUNODBNAME); len = SIZEOF(buff); if (!cli_get_str(name,buff,&len)) mupip_exit(ERR_MUNODBNAME); ptr = &in_files; for (c1 = c2 = buff; ; ) { for ( ; *c2 && (*c2 != ',') ; c2++) ; ptr->next = (inc_list_struct*)malloc(SIZEOF(inc_list_struct)); ptr = ptr->next; ptr->next = 0; ptr->input_file.len = INTCAST(c2 - c1); ptr->input_file.addr = (char *)malloc(c2 - c1 + 1); memcpy(ptr->input_file.addr, c1, c2 - c1); *(char*)(ptr->input_file.addr + (c2 - c1)) = '\0'; if (!*c2) break; else c1 = ++c2; } return; } fis-gtm-V7.0-005/sr_unix/mutex.c0000644000032200000250000016014014342376330015351 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* GT.M Mutex Control */ #include "mdef.h" #include "gtm_time.h" /* for time() */ #include "gtm_socket.h" #include "gtm_string.h" #include "gtm_stdlib.h" #include "gtm_unistd.h" #include "gtm_stdio.h" #include "gtm_select.h" #include "gtm_un.h" #include #if defined(__MVS__) || defined(__linux__) || defined(__CYGWIN__) #include "gtm_limits.h" #else #include #endif #include "aswp.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "lockconst.h" #include "interlock.h" #include "filestruct.h" #include "io.h" #include "jnl.h" #include "gdsbgtr.h" #include "mutex.h" #include "relqueopi.h" #include "eintr_wrappers.h" #include "send_msg.h" #include "is_proc_alive.h" #include "compswap.h" #include "gtmsecshr.h" #include "gtm_rel_quant.h" #include "add_inter.h" #include "mutex_deadlock_check.h" #include "gt_timer.h" #include "gtmio.h" #include "gtm_c_stack_trace.h" #include "sleep.h" #include "anticipatory_freeze.h" #ifdef DEBUG #include "wbox_test_init.h" #include "repl_msg.h" /* needed by gtmsource.h */ #include "gtmsource.h" /* required for jnlpool GBLREF */ #endif #ifdef MUTEX_MSEM_WAKE #define MUTEX_MAX_WAIT (MUTEX_CONST_TIMEOUT_VAL * MILLISECS_IN_SEC) #endif #define PROBE_BG_TRACE_PRO_ANY(CSA, EVENT) \ { \ sgmnt_data_ptr_t lcl_csd; \ \ lcl_csd = CSA->hdr; \ assert((NULL != lcl_csd) \ || (CSA == &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs)); \ if (NULL != lcl_csd) \ BG_TRACE_PRO_ANY(CSA, EVENT); \ } #define ONE_MUTEX_TRY(CSA, ADDR, CRASH_CNT, PID, LOCK_TYPE, SPINS, SPIN_CNT, YIELDS, YIELD_CNT, Q_SLPS, IN_EPOCH, ATSTART, STATE) \ MBSTART { \ ABS_TIME ATEND; \ enum cdb_sc STATUS; \ gtm_uint64_t FAILED_LOCK_ATTEMPTS; \ node_local *CNL; \ \ CNL = (CSA)->nl; \ if ((CRASH_CNT) != (ADDR)->crashcnt) \ STATUS = cdb_sc_critreset; \ else if (GET_SWAPLOCK(&(ADDR)->semaphore)) \ { \ (CSA)->critical->crit_cycle++; \ MUTEX_DPRINT3("%d: Write %sACQUIRED\n", (PID), (MUTEX_LOCK_WRITE == (LOCK_TYPE)) ? "" : "IMMEDIATE "); \ MUTEX_TEST_SIGNAL_HERE("WRTLCK NOW CRIT\n", FALSE); \ SET_CSA_NOW_CRIT_TRUE(CSA); \ MUTEX_TEST_SIGNAL_HERE("WRTLCK SUCCESS\n", FALSE); \ if (-1 != (SPIN_CNT)) \ { \ assert((gtm_uint64_t)(SPIN_CNT) <= (SPINS)); \ (SPINS) -= (gtm_uint64_t)(SPIN_CNT); /* reduce by the number of unused */ \ } \ if (-1 != (YIELD_CNT)) \ { \ assert((gtm_uint64_t)(YIELD_CNT) <= (YIELDS)); \ (YIELDS) -= (gtm_uint64_t)(YIELD_CNT); /* reduce by the number of unused */ \ } \ STATUS = cdb_sc_normal; \ INCR_GVSTATS_COUNTER((CSA), CNL, n_crit_success, 1); \ } else \ STATUS = cdb_sc_nolock; \ if ((cdb_sc_normal == STATUS) || (MUTEX_LOCK_WRITE_IMMEDIATE == (LOCK_TYPE)) || (cdb_sc_critreset == STATUS)) \ { \ FAILED_LOCK_ATTEMPTS = (SPINS) + (YIELDS) + (Q_SLPS) + (cdb_sc_nolock == STATUS); \ if ((IN_EPOCH)) \ INCR_GVSTATS_COUNTER((CSA), CNL, n_crits_in_epch, FAILED_LOCK_ATTEMPTS); \ INCR_GVSTATS_COUNTER((CSA), CNL, n_crit_failed, FAILED_LOCK_ATTEMPTS); \ INCR_GVSTATS_COUNTER((CSA), CNL, sq_crit_failed, FAILED_LOCK_ATTEMPTS * FAILED_LOCK_ATTEMPTS); \ if (YIELDS) \ { \ INCR_GVSTATS_COUNTER((CSA), CNL, n_crit_yields, (YIELDS)); \ INCR_GVSTATS_COUNTER((CSA), CNL, sq_crit_yields, (YIELDS) * (YIELDS)); \ } \ if (Q_SLPS) \ { \ INCR_GVSTATS_COUNTER((CSA), CNL, n_crit_que_slps, (Q_SLPS)); \ INCR_GVSTATS_COUNTER((CSA), CNL, sq_crit_que_slps, (Q_SLPS) * (Q_SLPS)); \ } \ if ((CSA)->crit_probe) \ { \ sys_get_curr_time(&ATEND); /* end time for the probcrit */ \ ATEND = sub_abs_time(&ATEND, &(ATSTART)); /* times currently use usec but might someday use ns*/ \ (CSA)->probecrit_rec.t_get_crit = ((gtm_uint64_t)(ATEND.at_sec * 1000000) + ATEND.at_usec) * 1000; \ (CSA)->probecrit_rec.p_crit_failed = (gtm_uint64_t)FAILED_LOCK_ATTEMPTS; \ (CSA)->probecrit_rec.p_crit_yields = (gtm_uint64_t)(YIELDS); \ (CSA)->probecrit_rec.p_crit_que_slps = (gtm_uint64_t)(Q_SLPS); \ } \ if (save_jnlpool != jnlpool) \ jnlpool = save_jnlpool; \ return STATUS; \ } \ } MBEND GBLREF int num_additional_processors; GBLREF jnl_gbls_t jgbl; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF uint4 process_id; GBLREF uint4 mutex_per_process_init_pid; #ifdef MUTEX_MSEM_WAKE # ifdef POSIX_MSEM static sem_t *mutex_wake_msem_ptr = NULL; # else static msemaphore *mutex_wake_msem_ptr = NULL; # endif static mutex_que_entry_ptr_t msem_slot; #else GBLREF fd_set mutex_wait_on_descs; GBLREF int mutex_sock_fd; #endif #ifdef DEBUG GBLREF boolean_t in_mu_rndwn_file; #endif DECLARE_MUTEX_TRACE_CNTRS DECLARE_MUTEX_TEST_SIGNAL_FLAG static boolean_t woke_self; static boolean_t woke_none; static unsigned short next_rand[3]; static int optimistic_attempts; static int mutex_expected_wake_instance = 0; #ifndef CRIT_USE_PTHREAD_MUTEX static enum cdb_sc mutex_wakeup(mutex_struct_ptr_t addr, mutex_spin_parms_ptr_t mutex_spin_parms); #endif void mutex_salvage(gd_region *reg); void mutex_clean_dead_owner(gd_region* reg, uint4 holder_pid); error_def(ERR_MUTEXERR); error_def(ERR_MUTEXFRCDTERM); error_def(ERR_MUTEXLCKALERT); error_def(ERR_ORLBKINPROG); error_def(ERR_TEXT); error_def(ERR_WCBLOCKED); /* * General: * Uses compare-and-swap logic to obtain/release a semaphore * in shared memory. * * Interface: * void gtm_mutex_init(reg, n, crash) * Initialize mutex structure for region reg with n * queue slots. If crash is TRUE, then this is a "crash" * reinitialization; otherwise, it's a "clean" initialization. * * enum cdb_sc gtm_mutex_lock(reg, mutex_spin_parms, seq, mutex_lock_type) * mutex for region reg * * enum cdb_sc mutex_unlockw(reg, seq); * Unlock mutex for region reg * * For routines taking the seq argument, if seq != crash count, * return cdb_sc_critreset. * * * Mutex structure must be quadword aligned * * * Mutex structure : * * --------------------------------- * | semaphore | * --------------------------------- * | crash count | * --------------------------------- * | stuckexec | <-UNIX only * -------------------------------- * | # of que slots | * -------------------------------- * |_ fl waiting process que head _| * |_ bl _| * |_ global_latch _| * --------------------------------- * |_ fl unused slots queue head _| * |_ bl _| * |_ global_latch _| * --------------------------------- * |_ fl first queue entry _| * |_ bl _| * |_ pid _| * | super_crit [CCP use only]^ | * --------------------------------- * |_ fl second queue entry _| * |_ bl _| * |_ pid _| * | super_crit [CCP use only]^ | * --------------------------------- * : : : : : * --------------------------------- * |_ fl last queue entry _| * |_ bl _| * |_ pid _| * | super_crit [CCP use only]^ | * --------------------------------- * * ^Note: only one entry at a time (at the head of the * waiting process queue) will ever use "super_crit". * CCP is used in VMS only - 03/11/98 * 07-31-2002 se: super-crit is not used at all anymore. Comments are left for historical purposes. * * Fields may be interspersed with fillers for alignment purposes. */ #ifndef CRIT_USE_PTHREAD_MUTEX static void clean_initialize(mutex_struct_ptr_t addr, int n, bool crash) { mutex_que_entry_ptr_t q_free_entry; # if defined(MUTEX_MSEM_WAKE) && !defined(POSIX_MSEM) msemaphore *status; # endif assert(n > 0); addr->queslots = n; /* Initialize the waiting process queue to be empty */ addr->prochead.que.fl = addr->prochead.que.bl = 0; SET_LATCH_GLOBAL(&addr->prochead.latch, LOCK_AVAILABLE); /* Initialize the free queue to be empty */ addr->freehead.que.fl = addr->freehead.que.bl = 0; SET_LATCH_GLOBAL(&addr->freehead.latch, LOCK_AVAILABLE); /* Clear the first free entry */ q_free_entry = (mutex_que_entry_ptr_t)((sm_uc_ptr_t)&addr->freehead + SIZEOF(mutex_que_head)); q_free_entry->que.fl = q_free_entry->que.bl = 0; q_free_entry->pid = 0; q_free_entry->super_crit = (void *)NULL; q_free_entry->mutex_wake_instance = 0; while (n--) { # ifdef MUTEX_MSEM_WAKE # ifdef POSIX_MSEM if (-1 == sem_init(&q_free_entry->mutex_wake_msem, TRUE, 0)) /* Shared lock with no initial resources (locked) */ # else if ((NULL == (status = msem_init(&q_free_entry->mutex_wake_msem, MSEM_LOCKED))) || ((msemaphore *)-1 == status)) # endif RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_MUTEXERR, 0, ERR_TEXT, 2, RTS_ERROR_TEXT("Error with mutex wait memory semaphore initialization"), errno); # endif /* Initialize fl,bl links to 0 before INSQTI as it (gtm_insqti in relqueopi.c) asserts this */ DEBUG_ONLY(((que_ent_ptr_t)q_free_entry)->fl = 0;) DEBUG_ONLY(((que_ent_ptr_t)q_free_entry)->bl = 0;) if (INTERLOCK_FAIL == INSQTI((que_ent_ptr_t)q_free_entry++, (que_head_ptr_t)&addr->freehead)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_MUTEXERR, 0, ERR_TEXT, 2, RTS_ERROR_TEXT("Interlock instruction failure in mutex initialize")); } SET_LATCH_GLOBAL(&addr->semaphore, LOCK_AVAILABLE); SET_LATCH_GLOBAL((global_latch_t *)&addr->stuckexec, LOCK_AVAILABLE); if (!crash) { SET_LATCH(&addr->crashcnt, 0); SET_LATCH_GLOBAL(&addr->crashcnt_latch, LOCK_AVAILABLE); } return; } static void crash_initialize(mutex_struct_ptr_t addr, int n, bool crash) { /* * mutex_wake_proc() is not declared here because its return value * is left unspecified in its definition (see mutex_wake_proc.c) */ mutex_que_entry_ptr_t next_entry; INCR_CNT(&addr->crashcnt, &addr->crashcnt_latch); addr->freehead.que.fl = addr->freehead.que.bl = 0; next_entry = (mutex_que_entry_ptr_t)&addr->prochead; do { if (0 == next_entry->que.fl) { /* Wait queue empty; do a clean initialization */ clean_initialize(addr, n, crash); return; } next_entry = (mutex_que_entry_ptr_t)((sm_uc_ptr_t)next_entry + next_entry->que.fl); if (next_entry <= (mutex_que_entry_ptr_t)&addr->prochead || next_entry >= (mutex_que_entry_ptr_t)&addr->prochead + n + 1 || (0 != ((INTPTR_T)next_entry & (SIZEOF(mutex_que_entry) - 1)))) { /* * next_entry == &addr->prochead => loop is done; * next_entry below queue head => queue is corrupt; * next_entry above queue top => queue is corrupt; * next_entry is not (SIZEOF(queue) entry)-byte * aligned => queue is corrupt ... * ... in all cases do a clean initialization */ clean_initialize(addr, n, crash); return; } /* Wake up process */ if (next_entry->pid != process_id) # ifdef MUTEX_MSEM_WAKE mutex_wake_proc(&next_entry->mutex_wake_msem); # else mutex_wake_proc((sm_int_ptr_t)&next_entry->pid, next_entry->mutex_wake_instance); # endif } while (TRUE); } static enum cdb_sc mutex_long_sleep(mutex_struct_ptr_t addr, sgmnt_addrs *csa, mutex_spin_parms_ptr_t mutex_spin_parms) { enum cdb_sc status; boolean_t wakeup_status; # ifdef MUTEX_MSEM_WAKE boolean_t msem_timedout; int save_errno; # else struct timeval timeout; int timeout_threshold; struct sockaddr_un mutex_woke_me_proc; GTM_SOCKLEN_TYPE mutex_woke_me_proc_len; mutex_wake_msg_t mutex_wake_msg[2]; int sel_stat; ssize_t nbrecvd; int timeout_intr_slpcnt; long timeout_val; # endif # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_SENDTO_EPERM == gtm_white_box_test_case_number)) { FPRINTF(stderr, "MUPIP BACKUP is about to start long sleep\n"); } # endif if (LOCK_AVAILABLE == addr->semaphore.u.parts.latch_pid && --optimistic_attempts) { MUTEX_DPRINT2("%d: Nobody in crit (II) wake procs\n", process_id); MUTEX_TRACE_CNTR(mutex_trc_mutex_slp_fn_noslp); status = mutex_wakeup(addr, mutex_spin_parms); if ((cdb_sc_normal == status) && (woke_self || woke_none)) return (cdb_sc_normal); else if (cdb_sc_dbccerr == status) return (cdb_sc_dbccerr); } optimistic_attempts = MUTEX_MAX_OPTIMISTIC_ATTEMPTS; do { # ifdef MUTEX_MSEM_WAKE if (msem_slot->pid != process_id) { /* My msemaphore is already used by another process. * In other words, I was woken up, but missed my wakeup call. * I should return immediately. */ wakeup_status = TRUE; } else { TIMEOUT_INIT(msem_timedout, MUTEX_MAX_WAIT); /* * the check for EINTR below is valid and should not be converted to an EINTR * wrapper macro, because another condition is checked for the while loop. */ while (!(wakeup_status = (0 == MSEM_LOCKW(mutex_wake_msem_ptr)))) { save_errno = errno; if (EINTR == save_errno) { if (msem_timedout) { MUTEX_DPRINT3("%d: msem sleep done, heartbeat_counter = %d\n", process_id, heartbeat_counter); break; } MUTEX_DPRINT3("%d: msem sleep continue, heartbeat_counter = %d\n", process_id, heartbeat_counter); } else { RTS_ERROR_CSA_ABT(csa, VARLSTCNT(7) ERR_MUTEXERR, 0, ERR_TEXT, 2, RTS_ERROR_TEXT("Error with mutex wake msem"), save_errno); } } TIMEOUT_DONE(msem_timedout); /* wakeup_status is set to true, if I was able to lock...somebody woke me up; * wakeup_status is set to false, if I timed out and should go to recovery. */ } # else do { timeout.tv_sec = MUTEX_CONST_TIMEOUT_VAL; timeout.tv_usec = (gtm_tv_usec_t)(nrand48(next_rand) & ((1U << MUTEX_NUM_WAIT_BITS) - 1)) + 1; timeout_val = (timeout.tv_sec * E_6) + timeout.tv_usec; /* * Can add backoff logic here to increase the timeout * as the number of attempts increase */ timeout_intr_slpcnt = MUTEX_INTR_SLPCNT; MUTEX_DPRINT4("%d: Sleeping for %d s %d us\n", process_id, timeout.tv_sec, timeout.tv_usec); assertpro(FD_SETSIZE > mutex_sock_fd); FD_SET(mutex_sock_fd, &mutex_wait_on_descs); MUTEX_TRACE_CNTR(mutex_trc_slp); /* * the check for EINTR below is valid and should not be converted to an EINTR * wrapper macro, since it might be a timeout. */ while (-1 == (sel_stat = select(mutex_sock_fd + 1, &mutex_wait_on_descs, (fd_set *)NULL, (fd_set *)NULL, &timeout))) { if (EINTR == errno) { /* somebody interrupted me, reduce the timeout by half and continue */ MUTEX_TRACE_CNTR(mutex_trc_slp_intr); if (!(timeout_intr_slpcnt--)) /* Assume timed out */ { sel_stat = 0; MUTEX_TRACE_CNTR(mutex_trc_intr_tmout); break; } } else rts_error_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_TEXT, 2, RTS_ERROR_TEXT("Error with mutex select. Running in degraded mode"), errno); timeout_val >>= 1; timeout.tv_sec = timeout_val / E_6; timeout.tv_usec = (gtm_tv_usec_t)(timeout_val % E_6); MUTEX_DPRINT4("%d: Interrupted select, new timeout %d s %d us\n", process_id, timeout.tv_sec, timeout.tv_usec); /* the next line deals with the case that an interrupted select has changed mutex_wait_on_descs */ assertpro(FD_SETSIZE > mutex_sock_fd); FD_SET(mutex_sock_fd, &mutex_wait_on_descs); MUTEX_TRACE_CNTR(mutex_trc_slp); } if (1 == sel_stat) /* Somebody woke me up */ { mutex_woke_me_proc_len = SIZEOF(struct sockaddr_un); RECVFROM_SOCK(mutex_sock_fd, (void *)&mutex_wake_msg[0], SIZEOF(mutex_wake_msg), 0, (struct sockaddr *)&mutex_woke_me_proc, (GTM_SOCKLEN_TYPE *)&mutex_woke_me_proc_len, nbrecvd); if (SIZEOF(mutex_wake_msg) == nbrecvd) /* Drained out both old and new wake messages */ { MUTEX_TRACE_CNTR(mutex_trc_slp_wkup); MUTEX_TRACE_CNTR(mutex_trc_pgybckd_dlyd_wkup); MUTEX_DPRINT3("%d: %d woke me up, drained delayed message too\n", process_id, mutex_wake_msg[1].pid); wakeup_status = TRUE; break; } if (BIN_TOGGLE(mutex_expected_wake_instance) == mutex_wake_msg[0].mutex_wake_instance) { MUTEX_DPRINT3("%d: %d woke me up\n", process_id, mutex_wake_msg[0].pid); MUTEX_TRACE_CNTR(mutex_trc_slp_wkup); wakeup_status = TRUE; break; } /* else, old wake msg, ignore */ MUTEX_DPRINT3("%d: %d sent me delayed wake msg\n", process_id, mutex_wake_msg[0].pid); MUTEX_TRACE_CNTR(mutex_trc_xplct_dlyd_wkup); } else if (0 == sel_stat) /* Timed out */ { MUTEX_DPRINT2("%d: Sleep done, go wake others\n", process_id); MUTEX_TRACE_CNTR(mutex_trc_slp_tmout); wakeup_status = FALSE; break; } } while (TRUE); # endif /* * If I was woken up and am a writer, others are blocking on * me. So, I shall try to get the lock NOW */ if (wakeup_status) return (cdb_sc_normal); else mutex_deadlock_check(addr, csa); /* Timed out: See if any deadlocks and fix if detected */ status = mutex_wakeup(addr, mutex_spin_parms); if (cdb_sc_dbccerr == status) return (cdb_sc_dbccerr); /* else status is cdb_sc_normal */ if (woke_self || woke_none) return (cdb_sc_normal); /* * There are others above me in the queue or I missed my * wakeup call. In the latter case, select or msem_lock will return * immediately and there won't be further sleeps. */ } while (TRUE); } static enum cdb_sc mutex_wakeup(mutex_struct_ptr_t addr, mutex_spin_parms_ptr_t mutex_spin_parms) { mutex_que_entry_ptr_t free_entry; int queue_retry_counter_remq, quant_retry_counter_remq, queue_retry_counter_insq, quant_retry_counter_insq; uint4 wake_this_pid; int wake_instance; woke_self = FALSE; woke_none = TRUE; quant_retry_counter_remq = queue_retry_counter_remq = 0; do { do { free_entry = (mutex_que_entry_ptr_t)REMQHI((que_head_ptr_t)&addr->prochead); if ((mutex_que_entry_ptr_t)NULL != free_entry && (mutex_que_entry_ptr_t)INTERLOCK_FAIL != free_entry) { wake_this_pid = free_entry->pid; wake_instance = free_entry->mutex_wake_instance; # ifdef MUTEX_MSEM_WAKE /* * In case of msem wakeup, the msem has to be * unlocked before returning free_entry to * free queue, or else another process might * use the same msem (in free_entry) for its * sleep. */ if (wake_this_pid != process_id) mutex_wake_proc(&free_entry->mutex_wake_msem); else woke_self = TRUE; /* This makes this entry not belong to any process before * inserting it into the free queue. */ free_entry->pid = quant_retry_counter_insq = queue_retry_counter_insq = 0; # endif do { do { if (INTERLOCK_FAIL != INSQTI((que_ent_ptr_t)free_entry, (que_head_ptr_t)&addr->freehead)) { MUTEX_DPRINT3("%d: Waking up %d\n", process_id, wake_this_pid); woke_none = FALSE; if (wake_this_pid != process_id) { MUTEX_TRACE_CNTR(mutex_trc_crit_wk); # ifndef MUTEX_MSEM_WAKE mutex_wake_proc((sm_int_ptr_t)&wake_this_pid, wake_instance); # endif } else { /* With * msem wake, * this can * never * happen */ woke_self = TRUE; } return (cdb_sc_normal); /* No more wakes */ } if (!queue_retry_counter_insq) /* save memory reference on fast path */ queue_retry_counter_insq = mutex_spin_parms->mutex_hard_spin_count; } while (--queue_retry_counter_insq); if (!quant_retry_counter_insq) /* save memory reference on fast path */ quant_retry_counter_insq = MAX(E_4 - mutex_spin_parms->mutex_hard_spin_count, mutex_spin_parms->mutex_sleep_spin_count); if (!(--quant_retry_counter_insq)) { # ifndef MUTEX_MSEM_WAKE if (wake_this_pid != process_id) mutex_wake_proc((sm_int_ptr_t)&wake_this_pid, wake_instance); # endif /* Too many failures */ return (cdb_sc_dbccerr); } else GTM_REL_QUANT(mutex_spin_parms->mutex_spin_sleep_mask); } while (quant_retry_counter_insq); /* actually terminated by return 3 lines above */ } else if ((mutex_que_entry_ptr_t)NULL == free_entry) { /* Empty wait queue */ MUTEX_DPRINT2("%d: Empty wait queue\n", process_id); return (cdb_sc_normal); } /* else secondary interlock failed */ if (!queue_retry_counter_remq) /* save memory reference on fast path */ quant_retry_counter_remq = mutex_spin_parms->mutex_hard_spin_count; } while (--queue_retry_counter_remq); if (!quant_retry_counter_remq) /* save memory reference on fast path */ quant_retry_counter_remq = MAX(E_4 - mutex_spin_parms->mutex_hard_spin_count, mutex_spin_parms->mutex_sleep_spin_count); if (!(--quant_retry_counter_remq)) return (cdb_sc_dbccerr); /* Too many queue failures */ else GTM_REL_QUANT(mutex_spin_parms->mutex_spin_sleep_mask); } while (quant_retry_counter_remq); return (cdb_sc_dbccerr); /* This will never get executed, added to make compiler happy */ } #endif void gtm_mutex_init(gd_region *reg, int n, bool crash) { # ifdef CRIT_USE_PTHREAD_MUTEX sgmnt_addrs *csa; int4 status; pthread_mutexattr_t crit_attr; csa = &FILE_INFO(reg)->s_addrs; if (crash) { pthread_mutex_unlock(&csa->critical->mutex); /* We may not have it locked, so ignore errors. */ status = pthread_mutex_destroy(&csa->critical->mutex); if (0 != status) { RTS_ERROR_CSA_ABT(csa, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutex_destroy"), CALLFROM, status); } } /* Set up mutex for new file */ status = pthread_mutexattr_init(&crit_attr); if (0 != status) { RTS_ERROR_CSA_ABT(csa, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutexattr_init"), CALLFROM, status); } status = pthread_mutexattr_settype(&crit_attr, PTHREAD_MUTEX_ERRORCHECK); if (0 != status) { RTS_ERROR_CSA_ABT(csa, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutexattr_settype"), CALLFROM, status); } status = pthread_mutexattr_setpshared(&crit_attr, PTHREAD_PROCESS_SHARED); if (0 != status) { RTS_ERROR_CSA_ABT(csa, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutexattr_setpshared"), CALLFROM, status); } # if PTHREAD_MUTEX_ROBUST_SUPPORTED status = pthread_mutexattr_setrobust(&crit_attr, PTHREAD_MUTEX_ROBUST); if (0 != status) { RTS_ERROR_CSA_ABT(csa, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutexattr_setrobust"), CALLFROM, status); } # endif status = pthread_mutex_init(&csa->critical->mutex, &crit_attr); if (0 != status) { RTS_ERROR_CSA_ABT(csa, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutex_init"), CALLFROM, status); } # else if (!crash) clean_initialize((&FILE_INFO(reg)->s_addrs)->critical, n, crash); else crash_initialize((&FILE_INFO(reg)->s_addrs)->critical, n, crash); return; # endif } enum cdb_sc gtm_mutex_lock(gd_region *reg, mutex_spin_parms_ptr_t mutex_spin_parms, int crash_count, mutex_lock_t mutex_lock_type, wait_state state) { sgmnt_addrs *csa; node_local *cnl; ABS_TIME atstart; latch_t local_crit_cycle = 0; int4 local_stuck_cycle = 0; uint4 timeout_count = 0; # ifdef CRIT_USE_PTHREAD_MUTEX int status; ABS_TIME atend; struct timespec timeout; # else enum cdb_sc status; boolean_t epoch_count, try_recovery; gtm_int64_t hard_spin_cnt, sleep_spin_cnt; gtm_uint64_t queue_sleeps, spins, yields; int n_queslots, redo_cntr; mutex_struct_ptr_t addr; mutex_que_entry_ptr_t free_slot; uint4 in_crit_pid; jnlpool_addrs_ptr_t save_jnlpool; time_t curr_time; uint4 curr_time_uint4, next_alert_uint4; # ifdef MUTEX_MSEM_WAKE int rc; # endif # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; csa = &FILE_INFO(reg)->s_addrs; assert(!csa->now_crit); cnl = csa->nl; /* Check that "mutex_per_process_init" has happened before we try to grab crit and that it was done with our current * pid (i.e. ensure that even in the case where parent did the mutex init with its pid and did a fork, the child process * has done a reinitialization with its pid). The only exception is if we are in "mu_rndwn_file" in which case we * know for sure there is no other pid accessing the database shared memory. */ assert((MUTEX_LOCK_WRITE_IMMEDIATE == mutex_lock_type) || (MUTEX_LOCK_WRITE == mutex_lock_type)); assert((mutex_per_process_init_pid == process_id) || ((0 == mutex_per_process_init_pid) && in_mu_rndwn_file)); MUTEX_TRACE_CNTR((MUTEX_LOCK_WRITE == mutex_lock_type) ? mutex_trc_lockw : mutex_trc_lockwim); # ifdef CRIT_USE_PTHREAD_MUTEX if (csa->crit_probe) { csa->probecrit_rec.p_crit_que_slots = 0; sys_get_curr_time(&atstart); /* start time for the probecrit */ } /* Do a trylock first. If we are locking immediate, we are done. Otherwise we have the opportunity to update * stats before doing a longer timed lock attempt. */ UPDATE_CRIT_COUNTER(csa, state); status = pthread_mutex_trylock(&csa->critical->mutex); do { if (((EBUSY == status) && (MUTEX_LOCK_WRITE == mutex_lock_type)) || (ETIMEDOUT == status)) { /* Got EBUSY from the trylock above or ETIMEDOUT from a previous iteration. */ INCR_GVSTATS_COUNTER(csa, cnl, n_crit_failed, 1); INCR_GVSTATS_COUNTER(csa, cnl, sq_crit_failed, 1); if (cnl->doing_epoch) INCR_GVSTATS_COUNTER(csa, cnl, n_crits_in_epch, 1); local_crit_cycle = csa->critical->crit_cycle; local_stuck_cycle = csa->critical->stuck_cycle; status = clock_gettime(CLOCK_REALTIME, &timeout); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("clock_gettime"), CALLFROM, errno, 0); timeout.tv_sec += MUTEX_CONST_TIMEOUT_VAL; status = pthread_mutex_timedlock(&csa->critical->mutex, &timeout); } switch (status) { case EOWNERDEAD: mutex_clean_dead_owner(reg, cnl->in_crit); /* Record salvage event in db file header if applicable. * Take care not to do it for jnlpool which has no concept of a db cache. * In that case csa->hdr is NULL so check accordingly. */ assert((NULL != csa->hdr) || (jnlpool && (csa == &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs))); if (NULL != csa->hdr) { SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER); BG_TRACE_PRO_ANY(csa, wcb_mutex_salvage); /* no need to use PROBE_BG_TRACE_PRO_ANY macro * since we already checked for csa->hdr NULL. */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_WCBLOCKED, 6, LEN_AND_LIT("wcb_mutex_salvage"), process_id, &csa->ti->curr_tn, DB_LEN_STR(reg)); } # if PTHREAD_MUTEX_CONSISTENT_SUPPORTED status = pthread_mutex_consistent(&csa->critical->mutex); if (0 != status) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutex_consistent"), CALLFROM, status); # endif /* fall through */ case 0: SET_CSA_NOW_CRIT_TRUE(csa); if (csa->crit_probe) { sys_get_curr_time(&atend); /* end time for the probcrit */ atend = sub_abs_time(&atend, &atstart); csa->probecrit_rec.t_get_crit = ((gtm_uint64_t)(atend.at_sec * E_6) + atend.at_usec) * 1000; csa->probecrit_rec.p_crit_failed = 0; csa->probecrit_rec.p_crit_yields = 0; csa->probecrit_rec.p_crit_que_slps = 0; } INCR_GVSTATS_COUNTER(csa, cnl, n_crit_success, 1); csa->critical->crit_cycle++; return cdb_sc_normal; case EBUSY: assert(MUTEX_LOCK_WRITE_IMMEDIATE == mutex_lock_type); return cdb_sc_nolock; case ETIMEDOUT: mutex_deadlock_check(csa->critical, csa); /* Timed out: See if any deadlocks and fix if detected */ assert((MUTEX_CONST_TIMEOUT_VAL * 2) == MUTEXLCKALERT_INTERVAL); if ((0 == (++timeout_count % 2)) && (csa->critical->crit_cycle == local_crit_cycle)) { if (IS_REPL_INST_FROZEN) break; if (0 != cnl->onln_rlbk_pid) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_ORLBKINPROG, 3, cnl->onln_rlbk_pid, DB_LEN_STR(reg)); assert(cnl->in_crit == cnl->onln_rlbk_pid); break; } if (INTERLOCK_ADD(&csa->critical->stuck_cycle, NULL, 1) == (local_stuck_cycle + 1)) { GET_C_STACK_FROM_SCRIPT("MUTEXLCKALERT", process_id, cnl->in_crit, csa->critical->crit_cycle); send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_MUTEXLCKALERT, 4, DB_LEN_STR(reg), cnl->in_crit, csa->critical->crit_cycle); } } if ((csa->critical->crit_cycle == local_crit_cycle) && !TREF(disable_sigcont)) { /* The process might have been STOPPED (kill -SIGSTOP). * Send SIGCONT and nudge the stopped process forward. * However, skip this call in case of SENDTO_EPERM white-box test, because we do not want * the intentionally stuck process to be awakened prematurely. */ if (DEBUG_ONLY(!WBTEST_ENABLED(WBTEST_SENDTO_EPERM) &&) TRUE) continue_proc(cnl->in_crit); } break; default: assertpro(!status); return cdb_sc_nolock; /* Keep syntax checkers happy */ } } while (ETIMEDOUT == status); assertpro(!status); return cdb_sc_nolock; /* To keep compiler happy; should never happen. */ # else UPDATE_CRIT_COUNTER(csa, state); save_jnlpool = jnlpool; optimistic_attempts = MUTEX_MAX_OPTIMISTIC_ATTEMPTS; queue_sleeps = csa->probecrit_rec.p_crit_que_full = 0; spins = yields = 0; local_crit_cycle = 0; /* this keeps us from doing a MUTEXLCKALERT on the first cycle in case the time latch is stale */ try_recovery = jgbl.onlnrlbk; /* salvage lock the first time if we are online rollback thereby reducing unnecessary waits */ assert(cnl); epoch_count = cnl->doing_epoch; addr = csa->critical; if (csa->crit_probe) { /* run the active queue to find how many slots are left */ csa->probecrit_rec.p_crit_que_slots = (gtm_uint64_t)addr->queslots; /* free = total number of slots */ csa->probecrit_rec.p_crit_que_slots -= verify_queue_lock((que_head_ptr_t)&addr->prochead, csa); /* less used slots*/ sys_get_curr_time(&atstart); /* start time for the probecrit */ } do { /* master loop */ in_crit_pid = cnl->in_crit; sleep_spin_cnt = -1; MUTEX_TRACE_CNTR(mutex_trc_w_atmpts); do { /* fast grab loop for the master lock */ for (status = cdb_sc_nolock, hard_spin_cnt = -1; hard_spin_cnt; --hard_spin_cnt) { /* hard spin loop for the master lock - don't admit any MUTEX_LOCK_WRITE_IMMEDIATE to try a bit '*/ ONE_MUTEX_TRY(csa, addr, crash_count, process_id, MUTEX_LOCK_WRITE, spins, hard_spin_cnt, yields, sleep_spin_cnt, queue_sleeps, epoch_count, atstart, state); if (try_recovery) { mutex_salvage(reg); try_recovery = FALSE; } if (-1 == hard_spin_cnt) /* save memory reference on fast path */ { hard_spin_cnt = num_additional_processors ? mutex_spin_parms->mutex_hard_spin_count : 1; spins += hard_spin_cnt; /* start with max */ } } /* Sleep for a very short duration */ # ifdef MUTEX_TRACE if (MUTEX_LOCK_WRITE == mutex_lock_type) MUTEX_TRACE_CNTR(mutex_trc_wt_short_slp); else MUTEX_TRACE_CNTR(mutex_trc_wtim_short_slp); # endif if (-1 == sleep_spin_cnt) /* save memory reference on fast path */ { sleep_spin_cnt = mutex_spin_parms->mutex_sleep_spin_count; if (0 == sleep_spin_cnt) break; yields += sleep_spin_cnt; /* start with max */ } assert(!csa->now_crit); GTM_REL_QUANT(mutex_spin_parms->mutex_spin_sleep_mask); } while (--sleep_spin_cnt); MUTEX_DPRINT4("%d: Could not acquire WRITE %sLOCK, held by %d\n", process_id, (MUTEX_LOCK_WRITE == mutex_lock_type) ? "" : "IMMEDIATE ", addr->semaphore.u.parts.latch_pid); if (MUTEX_LOCK_WRITE_IMMEDIATE == mutex_lock_type) /* immediate gets 1 last try which returns regardless */ ONE_MUTEX_TRY(csa, addr, crash_count, process_id, mutex_lock_type, /* use real lock type here */ spins, (gtm_int64_t)-1, yields, (gtm_int64_t)-1, queue_sleeps, epoch_count, atstart, state); try_recovery = FALSE; /* only try recovery once per MUTEXLCKALERT */ assert(cdb_sc_nolock == status); time(&curr_time); assert(MAXUINT4 > curr_time); curr_time_uint4 = (uint4)curr_time; next_alert_uint4 = csa->critical->stuckexec.cas_time; assert(save_jnlpool == jnlpool); assert(!csa->jnlpool || (csa->jnlpool == jnlpool)); assert((curr_time_uint4 <= next_alert_uint4) || (!csa->jnlpool || (csa->jnlpool == jnlpool))); if ((curr_time_uint4 > next_alert_uint4) && !IS_REPL_INST_FROZEN) { /* We've waited long enough and the Instance is not frozen - might be time to send MUTEXLCKALERT */ if (COMPSWAP_LOCK(&csa->critical->stuckexec.time_latch, next_alert_uint4, 0, (curr_time_uint4 + MUTEXLCKALERT_INTERVAL), 0)) { /* and no one else beat us to it */ MUTEX_DPRINT3("%d: Acquired STUCKEXEC time lock, to trace %d\n", process_id, in_crit_pid); if (process_id == in_crit_pid) { /* This is just a precaution - shouldn't ever happen and has no code to maintain gvstats */ assert(FALSE); SET_CSA_NOW_CRIT_TRUE(csa); return (cdb_sc_normal); } if (in_crit_pid && (in_crit_pid == cnl->in_crit) && is_proc_alive(in_crit_pid, 0)) { /* and we're waiting on some living process */ if (local_crit_cycle == csa->critical->crit_cycle) { /* and things aren't moving */ assert(local_crit_cycle); if (IS_REPL_INST_FROZEN) /* recheck to minimize spurious reports */ continue; if (0 == cnl->onln_rlbk_pid) { /* not rollback - send_msg after trace less likely to lose process */ GET_C_STACK_FROM_SCRIPT("MUTEXLCKALERT", process_id, in_crit_pid, csa->critical->crit_cycle); send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_MUTEXLCKALERT, 4, DB_LEN_STR(reg), in_crit_pid, csa->critical->crit_cycle); try_recovery = TRUE; /* set off a salvage */ continue; /* make sure to act on it soon, likely this process */ } /* If the holding PID belongs to online rollback which holds crit on database and * journal pool for its entire duration, use a different message */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_ORLBKINPROG, 3, cnl->onln_rlbk_pid, DB_LEN_STR(reg)); assert(cnl->in_crit == cnl->onln_rlbk_pid); } } else { /* nobody home */ local_crit_cycle = csa->critical->crit_cycle; try_recovery = TRUE; /* set off a salvage */ continue; /* make sure to act on it soon, likely this process */ } } else { /* didn't get resource to do the MUTEXLCKALERT and procestuckexec */ MUTEX_DPRINT2("%d: Could not acquire STUCKEXEC time lock", process_id); } } /* time to try for a slot on the mutex queue in order to wait for a wake up when someone releases crit */ if (0 == local_crit_cycle) local_crit_cycle = csa->critical->crit_cycle; /* sync first time waiter */ MUTEX_TRACE_CNTR(mutex_trc_mutex_slp_fn); MUTEX_DPRINT2("%d: looking for mutex queue slot\n", process_id); if (LOCK_AVAILABLE == addr->semaphore.u.parts.latch_pid) /* there is nobody in crit */ { /* The above condition is an optimistic check to speed hings up by not letting a process sleep. * In an n-way SMP, there is a possibility that n processes including at least one writer might run in * lock-step,testing the above condition almost at the same time and deciding that nobody is in crit. * This might go on until one of them grabs crit, or lock-attempts cross a threshold leading to recovery. */ if (--optimistic_attempts) { /* To avoid such an undesireable scenario, we test the number of times we have run into this * a situation d if too many, sleep sleep as if the latch were held. */ MUTEX_DPRINT2("%d: Nobody in crit (I) wake procs\n", process_id); MUTEX_TRACE_CNTR(mutex_trc_mutex_slp_fn_noslp); if (cdb_sc_normal == mutex_wakeup(addr, mutex_spin_parms)) continue; return (cdb_sc_dbccerr); } } for (redo_cntr = MUTEX_MAX_WAIT_FOR_PROGRESS_CNTR; redo_cntr;) { /* loop on getting a slot on the queue - every time through, if crit is available, grab it and go */ ONE_MUTEX_TRY(csa, addr, crash_count, process_id, mutex_lock_type, /* lock type is MUTEX_LOCK_WRITE */ spins, (gtm_int64_t)-1, yields, (gtm_int64_t)-1, queue_sleeps, epoch_count, atstart, state); free_slot = (mutex_que_entry_ptr_t)REMQHI((que_head_ptr_t)&addr->freehead); # ifdef MUTEX_MSEM_WAKE msem_slot = free_slot; # endif if ((NULL != free_slot) && (mutex_que_entry_ptr_t)INTERLOCK_FAIL != free_slot) { free_slot->pid = process_id; free_slot->mutex_wake_instance = mutex_expected_wake_instance; # ifdef MUTEX_MSEM_WAKE mutex_wake_msem_ptr = &free_slot->mutex_wake_msem; /* this loop makes sure that the msemaphore is locked initially before the process goes to * long sleep */ do { rc = MSEM_LOCKNW(mutex_wake_msem_ptr); } while (-1 == rc && EINTR == errno); # endif /* * Significance of mutex_wake_instance field : After queueing itself, a process might go to * sleep -select call in mutex_long_sleep- awaiting a wakeup message or a timeout. It is * possible that a wakeup message might arrive after timeout. In this case, a later attempt * at waiting for a wakeup message will falsely succeed on an old wakeup message. We use the * mutex_wake_instance field (value 0 or 1) to distinguish between an old and a new wakeup * message. Since at any given time there is atmost one entry in the queue for a process, * the only values we need for mutex_wake_instance are 0 and 1. */ mutex_expected_wake_instance = BIN_TOGGLE(mutex_expected_wake_instance); hard_spin_cnt = sleep_spin_cnt = -1; assert(MUTEX_LOCK_WRITE == mutex_lock_type); do { do { if (INTERLOCK_FAIL != INSQTI((que_ent_ptr_t)free_slot, (que_head_ptr_t)&addr->prochead)) { queue_sleeps++; MUTEX_DPRINT3("%d: Inserted %d into wait queue\n", process_id, free_slot->pid); if (cdb_sc_normal == mutex_long_sleep(addr, csa, mutex_spin_parms)) break; } if (-1 == hard_spin_cnt) /* save memory reference on fast path */ { hard_spin_cnt = num_additional_processors ? mutex_spin_parms->mutex_hard_spin_count : 1; spins += hard_spin_cnt; /* start with max */ } } while (--hard_spin_cnt); if (hard_spin_cnt) break; if (-1 == sleep_spin_cnt) /* save memory reference on fast path */ { sleep_spin_cnt = mutex_spin_parms->mutex_sleep_spin_count; if (0 == sleep_spin_cnt) break; sleep_spin_cnt = MAX(E_4 - mutex_spin_parms->mutex_hard_spin_count, sleep_spin_cnt); yields += sleep_spin_cnt; /* start with max */ } # ifndef MUTEX_MSEM_WAKE if (wake_this_pid != process_id) mutex_wake_proc((sm_int_ptr_t)&wake_this_pid, wake_instance); # endif if (0 != (--sleep_spin_cnt)) { if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; return (cdb_sc_dbccerr); /* Too many failures */ } assert(!csa->now_crit); GTM_REL_QUANT(mutex_spin_parms->mutex_spin_sleep_mask); } while (sleep_spin_cnt); /* actually terminated by the return three lines above */ } if (sleep_spin_cnt) { redo_cntr = 0; break; } if ((mutex_que_entry_ptr_t)NULL == free_slot) { /* Record queue full event in db file header if applicable. Take care not to do it for * jnlpool which has no concept of a db cache. In that case csa->hdr is NULL so use * PROBE_BG_TRACE_PRO_ANY macro. */ PROBE_BG_TRACE_PRO_ANY(csa, mutex_queue_full); csa->probecrit_rec.p_crit_que_full++; MUTEX_DPRINT2("%d: Free Queue full\n", process_id); /* When I can't find a free slot in the queue repeatedly, it means that there is no progress * in the system. A recovery attempt might be warranted in this scenario. The trick is to * return cdb_sc_normal which in turn causes another spin-loop initiation (or recovery when * implemented). The objective of mutex_sleep is achieved (partially) in that sleep is * done, though queueing isn't. */ } mutex_deadlock_check(addr, csa); if (redo_cntr--) { yields++; SLEEP_USEC(HUNDRED_MSEC, FALSE); /* Wait .1 second or until interrupted, then try again */ continue; } } while (redo_cntr); } while (TRUE); # endif } enum cdb_sc mutex_unlockw(gd_region *reg, int crash_count) { /* Unlock write access to the mutex at addr */ uint4 already_clear; sgmnt_addrs *csa; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; csa = &FILE_INFO(reg)->s_addrs; # ifdef CRIT_USE_PTHREAD_MUTEX pthread_mutex_unlock(&csa->critical->mutex); SET_CSA_NOW_CRIT_FALSE(csa); return cdb_sc_normal; # else if (crash_count != csa->critical->crashcnt) return (cdb_sc_critreset); assert(csa->now_crit); MUTEX_TEST_SIGNAL_HERE("WRTUNLCK NOW CRIT\n", FALSE); assert(csa->critical->semaphore.u.parts.latch_pid == process_id); RELEASE_SWAPLOCK(&csa->critical->semaphore); SET_CSA_NOW_CRIT_FALSE(csa); MUTEX_DPRINT2("%d: WRITE LOCK RELEASED\n", process_id); return (mutex_wakeup(csa->critical, NULL != csa->hdr ? (mutex_spin_parms_ptr_t)(&csa->hdr->mutex_spin_parms) : (mutex_spin_parms_ptr_t)((sm_uc_ptr_t)csa->critical + JNLPOOL_CRIT_SPACE))); # endif } void mutex_cleanup(gd_region *reg) { # ifndef CRIT_USE_PTHREAD_MUTEX sgmnt_addrs *csa; /* mutex_cleanup is called after doing a rel_crit on the same area so if we still own the lock it is because csa->now_crit was not in sync with our semaphore. At this point, if we own the lock, go ahead and release it. */ csa = &FILE_INFO(reg)->s_addrs; if (COMPSWAP_UNLOCK(&csa->critical->semaphore, process_id, CMPVAL2, LOCK_AVAILABLE, 0)) { MUTEX_DPRINT2("%d mutex_cleanup : released lock\n", process_id); } # endif } void mutex_seed_init(void) { # ifndef CRIT_USE_PTHREAD_MUTEX time_t mutex_seed; mutex_seed = time(NULL) * process_id; next_rand[0] = (unsigned short)(mutex_seed & ((1U << (SIZEOF(unsigned short) * 8)) - 1)); mutex_seed >>= (SIZEOF(unsigned short) * 8); next_rand[1] = (unsigned short)(mutex_seed & ((1U << (SIZEOF(unsigned short) * 8)) - 1)); mutex_seed >>= (SIZEOF(unsigned short) * 8); next_rand[2] = (unsigned short)(mutex_seed & ((1U << (SIZEOF(unsigned short) * 8)) - 1)); # endif } /* Release the COMPSWAP lock AFTER setting cnl->in_crit to 0 as an assert in * grab_crit (checking that cnl->in_crit is 0) relies on this order. */ void mutex_clean_dead_owner(gd_region* reg, uint4 holder_pid) { sgmnt_addrs *csa; node_local *cnl; sgmnt_data_ptr_t csd; jnlpool_ctl_ptr_t jpl; int index1, index2,orig_index2; jnl_buffer_ptr_t jbp; uint4 start_freeaddr, orig_freeaddr; seq_num jnl_seqno, strm_seqno; int strmIndex; seq_num strmSeqno60; jpl_phase2_in_prog_t *lastJplCmt; jbuf_phase2_in_prog_t *lastJbufCmt; csa = &FILE_INFO(reg)->s_addrs; send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_MUTEXFRCDTERM, 3, holder_pid, DB_LEN_STR(reg)); cnl = csa->nl; cnl->in_crit = 0; /* Mutex crash repaired, want to do write cache recovery, in case previous holder of crit had set * some cr->in_cw_set to a non-zero value. Not doing cache recovery could cause incorrect * GTMASSERTs in PIN_CACHE_RECORD macro in t_end/tp_tend. BYPASSOK(GTMASSERT) * Take care not to do it for jnlpool (csa->hdr is NULL in that case) which has no concept of a db cache. */ csd = csa->hdr; assert((NULL != csd) || (NULL != jnlpool)); assert((NULL != csd) || (csa == &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs)); if (NULL == csd) { /* This is a jnlpool. Check if a process in t_end/tp_tend was killed BEFORE * it incremented jpl->jnl_seqno. If so, undo any changes done in UPDATE_JPL_RSRV_WRITE_ADDR. */ jpl = jnlpool->jnlpool_ctl; assert(NULL != jpl); index1 = jpl->phase2_commit_index1; index2 = jpl->phase2_commit_index2; orig_index2 = index2; assert(jpl->write_addr <= jpl->rsrv_write_addr); DECR_PHASE2_COMMIT_INDEX(index2, JPL_PHASE2_COMMIT_ARRAY_SIZE); lastJplCmt = &jpl->phase2_commit_array[index2]; /* This process could have been killed during a commit in t_end/tp_tend. * a) In the middle of Step CMT03 (see secshr_db_clnup.c for CMTxx steps) * UPDATE_JPL_RSRV_WRITE_ADDR macro OR * b) After Step CMT03 but before Step CMT07 finished. * In either case, we need to reset jpl to what it was BEFORE Step CMT03 began i.e. roll back. * If the process gets killed AFTER CMT07 finishes, the transaction is rolled forward even * if it means writing JRT_NULL and/or JRT_INCTN records in jnlpool and/or jnlbuff. * Note that there is still a small window of instructions after CMT07 is done but before * CMT08 is done (for the first region in case of a multi-region TP transaction) if * a process gets killed, we will roll forward the jnlpool but roll back the jnlbuff * and so there would be a seqno with no corresponding journal records in the journal * files. This is not easily handled so is left as a todo for the future. */ if ((index1 == orig_index2) || (lastJplCmt->process_id != holder_pid)) { /* CMT02 < killed <= CMT03. * Kill could have happened before CMT03 finished so reset things. * This reset is a no-op if the kill happened even before CMT03 started. * This is Case (a). */ jpl->rsrv_write_addr = lastJplCmt->start_write_addr + lastJplCmt->tot_jrec_len; assert(((lastJplCmt->jnl_seqno + 1) == jpl->jnl_seqno) || !lastJplCmt->jnl_seqno); jpl->lastwrite_len = lastJplCmt->tot_jrec_len; } else { assert((lastJplCmt->jnl_seqno == jpl->jnl_seqno) || ((lastJplCmt->jnl_seqno + 1) == jpl->jnl_seqno)); if (lastJplCmt->jnl_seqno == jpl->jnl_seqno) { /* CMT03 < killed < CMT07 */ jpl->rsrv_write_addr = lastJplCmt->start_write_addr; jpl->lastwrite_len = lastJplCmt->prev_jrec_len; ; /* similar layout as UPDATE_JPL_RSRV_WRITE_ADDR */ jpl->phase2_commit_index2 = index2; /* remove last commit entry */ } /* else : CMT07 < killed and so no rollback needed */ } } else { /* This is a database shm. Check if a process in t_end/tp_tend was killed BEFORE * Step CMT08 (see secshr_db_clnup.c) when it would have set cnl->update_underway_tn. * If so, undo any changes done in Step CMT06 (UPDATE_JBP_RSRV_FREEADDR). * Effectively rolling back the aborted commit in this region. */ assert((csd->trans_hist.early_tn == csd->trans_hist.curr_tn) || (csd->trans_hist.early_tn == (csd->trans_hist.curr_tn + 1))); assert(cnl->update_underway_tn <= csd->trans_hist.curr_tn); assert(csd->trans_hist.early_tn >= cnl->update_underway_tn); if (JNL_ENABLED(csd) && (csd->trans_hist.early_tn != csd->trans_hist.curr_tn)) { /* i.e. Process was killed after CMT04 but before CMT12. It is represented as * CMT04 < killed < CMT12 */ assert(NULL != csa->jnl); assert(NULL != csa->jnl->jnl_buff); jbp = csa->jnl->jnl_buff; index1 = jbp->phase2_commit_index1; index2 = jbp->phase2_commit_index2; orig_index2 = index2; assert(jbp->freeaddr <= jbp->rsrv_freeaddr); DECR_PHASE2_COMMIT_INDEX(index2, JNL_PHASE2_COMMIT_ARRAY_SIZE); lastJbufCmt = &jbp->phase2_commit_array[index2]; if (cnl->update_underway_tn != csd->trans_hist.curr_tn) { /* CMT04 < killed < CMT08. * ---------------------------------------------------------------- * Roll-back the entire transaction's effect on this database file * ---------------------------------------------------------------- */ start_freeaddr = lastJbufCmt->start_freeaddr; if ((index1 == orig_index2) || (lastJbufCmt->process_id != holder_pid) || (lastJbufCmt->curr_tn != csd->trans_hist.curr_tn)) { /* CMT04 < KILLED <= CMT06. * Kill could have happened before CMT06 finished so reset things. * This reset is a no-op if the kill happened even before CMT06 started. */ SET_JBP_RSRV_FREEADDR(jbp, start_freeaddr + lastJbufCmt->tot_jrec_len); } else { /* CMTO6 < killed < CMT08 */ assert(lastJbufCmt->curr_tn == csd->trans_hist.curr_tn); /* CMT06 finished. So undo it as a whole */ assert(lastJbufCmt->process_id == holder_pid); /* If CMT06a was in progress when the process was KILLED, then it is * possible jbp->freeaddr was updated as CMT16 (which is what CMT06a * executes) progressed. So undo that too. Likewise for dskaddr, * fsync_dskaddr etc. And finally reset rsrv_freeaddr. */ assert(jbp->fsync_dskaddr <= jbp->dskaddr); orig_freeaddr = jbp->freeaddr; if (orig_freeaddr > start_freeaddr) { jbp->freeaddr = start_freeaddr; jbp->free = start_freeaddr % jbp->size; } else assert(jbp->free == (orig_freeaddr % jbp->size)); if (jbp->dskaddr > start_freeaddr) { assert(!GLOBAL_LATCH_HELD_BY_US(&jbp->io_in_prog_latch)); grab_latch(&jbp->io_in_prog_latch, GRAB_LATCH_INDEFINITE_WAIT, WS_35, csa); /* Fix jbp->dskaddr & jbp->dsk while holding io latch */ assert(orig_freeaddr > start_freeaddr); jbp->dskaddr = start_freeaddr; jbp->dsk = start_freeaddr % jbp->size; /* Setting jbp->dskaddr to start_freeaddr is not enough. * We also need to re-read the partial filesystem-block-size * aligned block of data that precedes the new jbp->dskaddr * since that part is most likely no longer in the jnl buffer * (have been overwritten by the current aborted tn's jnl records). * We can try and optimize this by avoiding setting * jbp->re_read_dskaddr in case no overwrite happened. But it is * not straightforward to detect that and the risk is journal * file corruption. Given "mutex_salvage" is a rare occurrence, * it is safer to re-read unconditionally. */ jbp->re_read_dskaddr = start_freeaddr; rel_latch(&jbp->io_in_prog_latch); if (jbp->fsync_dskaddr > start_freeaddr) { /* Fix jbp->fsync_dskaddr while holding fsync io latch */ assert(!GLOBAL_LATCH_HELD_BY_US(&jbp->fsync_in_prog_latch)); grab_latch(&jbp->fsync_in_prog_latch, GRAB_LATCH_INDEFINITE_WAIT, WS_36, csa); jbp->fsync_dskaddr = start_freeaddr; rel_latch(&jbp->fsync_in_prog_latch); } } else assert(jbp->dsk == (jbp->dskaddr % jbp->size)); /* "jnl_write_phase2" is never called with JRT_EPOCH (see assert there * at function entry of possible rectype values and EPOCH is not in that * list). Therefore we are guaranteed a "jnl_write_epoch_rec" call never * happened since the first call to "jnl_write_reserve" happened in this * transaction. Therefore no UNDO of the effects of "jnl_write_epoch_rec" * needed here (e.g. jbp->post_epoch_freeaddr). */ assert(jbp->post_epoch_freeaddr <= start_freeaddr); SET_JBP_RSRV_FREEADDR(jbp, start_freeaddr);; /* see corresponding * SHM_READ_MEMORY_BARRIER in * "jnl_phase2_cleanup". */ jbp->phase2_commit_index2 = index2; /* remove last commit entry */ } csd->trans_hist.early_tn = csd->trans_hist.curr_tn; /* Undo CMT04 */ /* CMT07 is jnlpool related, so no undo done here (in db mutex_salvage) for that */ } else { /* CMT08 < killed < CMT12. * ------------------------------------------------------------------- * Roll-forward the entire transaction's effect on this database file * ------------------------------------------------------------------- * In case process got killed before CMT09 occurred, redo it. * If the process got killed after CMT09, the below redo is a no-op. */ /* CMT09 redo : start */ jnl_seqno = lastJbufCmt->jnl_seqno + 1; strm_seqno = lastJbufCmt->strm_seqno; /* If "strm_seqno" is 0, we are guaranteed this is not a supplementary * instance (i.e. "supplementary" variable in t_end/tp_tend is FALSE). */ if (strm_seqno) { strmIndex = GET_STRM_INDEX(strm_seqno); strmSeqno60 = GET_STRM_SEQ60(strm_seqno) + 1; } SET_REG_SEQNO(csa, jnl_seqno, strm_seqno, strmIndex, strmSeqno60, SKIP_ASSERT_TRUE); /* CMT09 redo : end */ csd->trans_hist.curr_tn = csd->trans_hist.early_tn; /* Redo CMT12 */ } } /* else: Step CMT04 did not happen OR Database is not journaled. * Nothing to undo in this db for Steps CMT01, CMT02 and CMT03. */ SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER); /* This will ensure we call "wcs_recover" which * will recover CMT04 and other CMTxx steps. */ } } #ifndef CRIT_USE_PTHREAD_MUTEX void mutex_salvage(gd_region *reg) { sgmnt_addrs *csa; node_local *cnl; int index1, index2, orig_index2, salvage_status; uint4 holder_pid, onln_rlbk_pid, start_freeaddr, orig_freeaddr; boolean_t mutex_salvaged; sgmnt_data_ptr_t csd; jnlpool_ctl_ptr_t jpl; jpl_phase2_in_prog_t *lastJplCmt; jbuf_phase2_in_prog_t *lastJbufCmt; seq_num jnl_seqno, strm_seqno, strmSeqno60; int strmIndex; jnl_buffer_ptr_t jbp; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; csa = &FILE_INFO(reg)->s_addrs; cnl = csa->nl; if (0 != (holder_pid = csa->critical->semaphore.u.parts.latch_pid)) { mutex_salvaged = FALSE; /* If we were trying to obtain a lock we already held, it is an out-of-design situation. We cannot safely * release the lock in that case so create a core file even in pro. */ assertpro(holder_pid != process_id); if (!is_proc_alive(holder_pid, 0)) { /* Release the COMPSWAP lock AFTER setting cnl->in_crit to 0 as an assert in * grab_crit (checking that cnl->in_crit is 0) relies on this order. */ mutex_clean_dead_owner(reg, holder_pid); COMPSWAP_UNLOCK(&csa->critical->semaphore, holder_pid, holder_imgcnt, LOCK_AVAILABLE, 0); mutex_salvaged = TRUE; /* Reset jbp->blocked as well if the holder_pid had it set */ if ((NULL != csa->jnl) && (NULL != csa->jnl->jnl_buff) && (csa->jnl->jnl_buff->blocked == holder_pid)) csa->jnl->jnl_buff->blocked = 0; MUTEX_DPRINT3("%d : mutex salvaged, culprit was %d\n", process_id, holder_pid); } else if (!TREF(disable_sigcont)) { /* The process might have been STOPPED (kill -SIGSTOP). Send SIGCONT and nudge the stopped process forward. * However, skip this call in case of SENDTO_EPERM white-box test, because we do not want the intentionally * stuck process to be awakened prematurely. */ DEBUG_ONLY(if (!gtm_white_box_test_case_enabled || WBTEST_SENDTO_EPERM != gtm_white_box_test_case_number)) continue_proc(holder_pid); } /* Record salvage event in db file header if applicable. * Take care not to do it for jnlpool which has no concept of a db cache. * In that case csa->hdr is NULL so check accordingly. */ assert((NULL != csa->hdr) || (jnlpool && (csa == &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs))); if (mutex_salvaged && (NULL != csa->hdr)) { SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER); BG_TRACE_PRO_ANY(csa, wcb_mutex_salvage); /* no need to use PROBE_BG_TRACE_PRO_ANY macro * since we already checked for csa->hdr non-NULL. */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_WCBLOCKED, 6, LEN_AND_LIT("wcb_mutex_salvage"), process_id, &csa->ti->curr_tn, DB_LEN_STR(reg)); } } } #endif /* Do the per process initialization of mutex stuff. This function should be invoked only once per process. The only * exception is the receiver server which could invoke this twice. Once through the receiver server startup command when * it does "jnlpool_init" and the second through the child receiver server process initialization. The second initialization * is needed to set the mutex structures up to correspond to the child process id (and not the parent pid). The function below * has to be coded to ensure that the second call nullifies any effects of the first call. */ void mutex_per_process_init(void) { int4 status; assert(process_id != mutex_per_process_init_pid); mutex_seed_init(); # ifndef MUTEX_MSEM_WAKE if (mutex_per_process_init_pid) { /* Close socket opened by the first call. But don't delete the socket file as the parent process will do that. */ assert(FD_INVALID != mutex_sock_fd); if (FD_INVALID != mutex_sock_fd) CLOSEFILE_RESET(mutex_sock_fd, status); /* resets "mutex_sock_fd" to FD_INVALID */ } assert(FD_INVALID == mutex_sock_fd); mutex_sock_init(); assert(FD_INVALID != mutex_sock_fd); # endif mutex_per_process_init_pid = process_id; } fis-gtm-V7.0-005/sr_unix/mutex_sock_cleanup.c0000755000032200000250000000275614342376330020112 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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" #ifndef MUTEX_MSEM_WAKE #include #include "gtm_socket.h" #include #include "gtm_stdio.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "mutex.h" #include "send_msg.h" #include "gtmio.h" GBLREF int mutex_sock_fd; GBLREF struct sockaddr_un mutex_sock_address; error_def(ERR_MUTEXERR); error_def(ERR_TEXT); void mutex_sock_cleanup(void) { int save_errno; int rc; /* Close the mutex wake socket */ if (FD_INVALID != mutex_sock_fd) CLOSEFILE_RESET(mutex_sock_fd, rc); /* resets "mutex_sock_fd" to FD_INVALID */ if ((NULL != mutex_sock_address.sun_path) && (-1 == UNLINK(mutex_sock_address.sun_path)) && (ENOENT != errno)) { save_errno = errno; send_msg(VARLSTCNT(11) ERR_MUTEXERR, 0, ERR_TEXT, 2, RTS_ERROR_TEXT("unlinking socket"), ERR_TEXT, 2, LEN_AND_STR(mutex_sock_address.sun_path), save_errno); } DUMP_MUTEX_TRACE_CNTRS; } #endif /*MUTEX_MSEM_WAKE*/ fis-gtm-V7.0-005/sr_unix/mutex_sock_init.c0000644000032200000250000001652114342376330017416 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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" #ifndef MUTEX_MSEM_WAKE #include "gtm_ipc.h" #include "gtm_socket.h" #include "gtm_un.h" #include #include #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_stdio.h" #include "gtm_stat.h" #include "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "mutex.h" #include "io.h" #include "secshr_client.h" #include "gtmsecshr.h" #include "iosp.h" #include "gtm_logicals.h" #include "eintr_wrappers.h" #include "send_msg.h" #include "trans_log_name.h" GBLREF uint4 process_id; GBLREF int mutex_sock_fd; GBLREF struct sockaddr_un mutex_sock_address; GBLREF struct sockaddr_un mutex_wake_this_proc; GBLREF int mutex_wake_this_proc_len; GBLREF int mutex_wake_this_proc_prefix_len; GBLREF fd_set mutex_wait_on_descs; static readonly char hex_table[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; error_def(ERR_MUTEXERR); error_def(ERR_MUTEXRSRCCLNUP); error_def(ERR_TEXT); void mutex_sock_init(void) { mstr mutex_sock_dir_lognam, mutex_sock_dir_transnam; int mutex_sock_path_len; uint4 mutex_sock_trans_status; char mutex_sock_path[MAX_TRANS_NAME_LEN]; int mutex_sock_len, save_errno; struct stat mutex_sock_stat_buf; int status; unsigned char pid_str[2 * SIZEOF(pid_t) + 1]; DEBUG_ONLY(boolean_t existed;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (FD_INVALID != mutex_sock_fd) /* Initialization done already */ return; /* Create the socket used for sending and receiving mutex wake mesgs */ if (FD_INVALID == (mutex_sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_MUTEXERR, 0, ERR_TEXT, 2, RTS_ERROR_TEXT("Error with mutex socket create"), errno); memset((char *)&mutex_sock_address, 0, SIZEOF(mutex_sock_address)); /* Get the socket path */ mutex_sock_dir_lognam.len = SIZEOF(MUTEX_SOCK_DIR) - 1; mutex_sock_dir_lognam.addr = MUTEX_SOCK_DIR; mutex_sock_trans_status = TRANS_LOG_NAME(&mutex_sock_dir_lognam, &mutex_sock_dir_transnam, mutex_sock_path, SIZEOF(mutex_sock_path), do_sendmsg_on_log2long); if (mutex_sock_trans_status != SS_NORMAL) { strcpy(mutex_sock_path, DEFAULT_MUTEX_SOCK_DIR); mutex_sock_path_len = SIZEOF(DEFAULT_MUTEX_SOCK_DIR) - 1; } else mutex_sock_path_len = mutex_sock_dir_transnam.len; /* If the path doesn't already end with a '/' pad a '/' */ if (mutex_sock_path[mutex_sock_path_len - 1] != '/') { mutex_sock_path[mutex_sock_path_len++] = '/'; mutex_sock_path[mutex_sock_path_len] = '\0'; } if ((mutex_sock_path_len + MAX_MUTEX_SOCKFILE_NAME_LEN) > SIZEOF(mutex_sock_address.sun_path)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_MUTEXERR, 0, ERR_TEXT, 2, RTS_ERROR_TEXT("Mutex socket path too long")); strcpy(mutex_sock_path + mutex_sock_path_len, MUTEX_SOCK_FILE_PREFIX); mutex_sock_path_len += (SIZEOF(MUTEX_SOCK_FILE_PREFIX) - 1); mutex_wake_this_proc_prefix_len = mutex_sock_path_len; /* Extend mutex_sock_path with pid */ strcpy(mutex_sock_path + mutex_sock_path_len, (char *)pid2ascx(pid_str, process_id)); mutex_sock_path_len += STRLEN((char *)pid_str); if (mutex_sock_path_len > SIZEOF(mutex_sock_address.sun_path)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_MUTEXERR, 0, ERR_TEXT, 2, RTS_ERROR_TEXT("Mutex socket path too long")); mutex_sock_address.sun_family = AF_UNIX; strcpy(mutex_sock_address.sun_path, mutex_sock_path); mutex_sock_len = SIZEOF(mutex_sock_address.sun_family) + mutex_sock_path_len + 1; /* Include NULL byte in length */ DEBUG_ONLY(if (!TREF(gtm_usesecshr))) { status = UNLINK(mutex_sock_address.sun_path); /* in case it was left from last time */ save_errno = (-1 == status) ? errno : 0; } DEBUG_ONLY(else save_errno = -1); /* Non-zero and non-ENOENT value so uses gtmsecshr */ if (0 != save_errno) { /* Separate checks for unlink success vs no file removal needed */ if (ENOENT != save_errno) { # ifdef DEBUG /* If using gtm_usesecshr, can get a log of bogus MUTEXRSRCCLNUP messages so see if the socket * actually exists or not so can supress the message if not (but still push it through secshr). */ if (TREF(gtm_usesecshr)) { STAT_FILE(mutex_sock_address.sun_path, &mutex_sock_stat_buf, status); existed = (0 == status) ? TRUE : FALSE; } else existed = TRUE; /* If no gtm_usesecshr, wouldn't be here unless existed */ # endif if (0 == (status = send_mesg2gtmsecshr(REMOVE_FILE, (unsigned int)-1, mutex_sock_address.sun_path, mutex_sock_path_len + 1))) { DEBUG_ONLY(if (existed)) /* Avoid mesg unless socket existed */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_MUTEXRSRCCLNUP, 2, mutex_sock_path_len, mutex_sock_path, ERR_TEXT, 2, LEN_AND_LIT("Resource removed by gtmsecshr")); } else if (ENOENT != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(10) ERR_MUTEXERR, 0, ERR_TEXT, 2, LEN_AND_LIT("gtmsecshr failed to remove leftover mutex resource"), ERR_TEXT, 2, mutex_sock_path_len, mutex_sock_path); /* else don't bother if somebody removed the file before gtmsecshr got to it */ } } else /* unlink succeeded - socket must have existed - now cleaned up */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_MUTEXRSRCCLNUP, 2, mutex_sock_path_len, mutex_sock_path); if (0 > BIND(mutex_sock_fd, (struct sockaddr *)&mutex_sock_address, mutex_sock_len)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_MUTEXERR, 0, ERR_TEXT, 2, RTS_ERROR_TEXT("Error with mutex socket bind"), errno); /* Set the socket permissions to override any umask settings. * Allow owner and group read and write access. */ STAT_FILE(mutex_sock_address.sun_path, &mutex_sock_stat_buf, status); if (-1 == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_MUTEXERR, 0, ERR_TEXT, 2, RTS_ERROR_TEXT("Error with mutex socket stat"), errno); mutex_sock_stat_buf.st_mode |= (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (-1 == CHMOD(mutex_sock_address.sun_path, mutex_sock_stat_buf.st_mode)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_MUTEXERR, 0, ERR_TEXT, 2, RTS_ERROR_TEXT("Error with mutex socket chmod"), errno); /* Clear the descriptor set used to sense wake up message */ FD_ZERO(&mutex_wait_on_descs); /* To make mutex_wake_proc faster, pre-initialize portions of * mutex_wake_this_proc which are invariant of the pid to be woken up. */ memset((char *)&mutex_wake_this_proc, 0, SIZEOF(mutex_wake_this_proc)); mutex_wake_this_proc.sun_family = AF_UNIX; strcpy(mutex_wake_this_proc.sun_path, mutex_sock_path); mutex_wake_this_proc_len = mutex_sock_len; } unsigned char *pid2ascx(unsigned char *pid_str, pid_t pid) { /* pid_str should accommodate atleast 2*SIZEOF(pid_t) + 1 characters */ register unsigned char *cp; cp = &pid_str[2*SIZEOF(pid_t)]; *cp = '\0'; /* Null terminate the string */ while(cp > pid_str) { *--cp = hex_table[pid & 0xF]; pid >>= 4; } return(pid_str); } #endif /*MUTEX_MSEM_WAKE*/ fis-gtm-V7.0-005/sr_unix/mutex_wake_proc.c0000644000032200000250000001300014342376330017373 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_stdio.h" #include "gtm_socket.h" #include "gtm_string.h" #include "gtm_un.h" #include #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "mutex.h" #include "eintr_wrappers.h" #include "is_proc_alive.h" #include "send_msg.h" #include "gtmsecshr.h" #ifdef DEBUG #include "wbox_test_init.h" #endif error_def(ERR_MUTEXERR); error_def(ERR_TEXT); error_def(ERR_SYSCALL); #ifndef MUTEX_MSEM_WAKE GBLREF int mutex_sock_fd; GBLREF struct sockaddr_un mutex_wake_this_proc; GBLREF int mutex_wake_this_proc_len; GBLREF int mutex_wake_this_proc_prefix_len; GBLREF uint4 process_id; void mutex_wake_proc(sm_int_ptr_t pid, int mutex_wake_instance) { /* * Wakeup process by sending a message over waiting process's socket. * The waiting process (in select) is woken up on sensing input on its * socket. The message is not relevant, a character will achieve the * objective. But, we will send the waking process's pid which might * be of use for debugging. */ unsigned char mutex_wake_this_proc_str[2 * SIZEOF(pid_t) + 1]; mutex_wake_msg_t msg; int status; ssize_t sendto_res; static int sendto_fail_pid; char sendtomsg[256]; /* Set up the socket structure for sending */ strcpy(mutex_wake_this_proc.sun_path + mutex_wake_this_proc_prefix_len, (char *)pid2ascx(mutex_wake_this_proc_str, *pid)); msg.pid = process_id; msg.mutex_wake_instance = mutex_wake_instance; # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_SENDTO_EPERM == gtm_white_box_test_case_number)) { FPRINTF(stderr, "PATH TO SOCKET IS\n%s\n", mutex_wake_this_proc.sun_path); LONG_SLEEP(20); } # endif /* We have seen an issue where the sendto() call done below blocked for at least more than a minute. The only reason * we know of this can happen is if the TCPIP buffer on the receiving end of the pipe is already full. But as long as * the receiving side is waiting for this wakeup message, it should be in a loop clearing the incoming messages thereby * avoiding this situation. But in reality, we have seen the receiving side not waiting for the wakeup signal at all * but instead doing something totally different effectively causing a deadlock. One approach to fix this issue is to * open the mutex socket with the O_NONBLOCK parameter that way the sendto() done below would be a non-blocking send * but that introduces issues in the mutex logic as it then needs to handle partial messages (this is because a * non-blocking sendto would send as much bytes as possible before things would block). For now, the only platform * that use this wakeup scheme is Linux and that is almost transitioning to using memory-semaphores. The blocking * sendto() issue is therefore not considered critical at this moment. This might need to be revisited in case this * code starts to get used again. -- nars - 2008/01/15. */ SENDTO_SOCK(mutex_sock_fd, (char *)&msg, SIZEOF(msg), 0, (struct sockaddr *)&mutex_wake_this_proc, mutex_wake_this_proc_len, sendto_res); if (0 > sendto_res) { /* Sending wakeup to the mutex socket file of the waiting pid can fail if the process terminated (and hence deleted * its mutex socket file) while waiting for crit. Except for that case, signal an error in case wakeup send fails. */ status = errno; assert(0 != *pid); /* if the other process could not be woken up with SENDTO_SOCK due to permissions issue, * try continue_proc() before erroring out */ if (EACCES == status) continue_proc(*pid); # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_SENDTO_EPERM == gtm_white_box_test_case_number)) { FPRINTF(stderr, "CALLED CONTINUE_PROC() ON THE OTHER PROCESS\n"); LONG_SLEEP(20); } # endif /* check if the process is still hung; if so, signal an error */ if ((sendto_fail_pid == *pid) && is_proc_alive(*pid, 0)) { SNPRINTF(sendtomsg, ARRAYSIZE(sendtomsg), "sendto() to pid [%d]", *pid); send_msg_csa(NULL, VARLSTCNT(10) ERR_MUTEXERR, 0, ERR_SYSCALL, 5, LEN_AND_STR(sendtomsg), CALLFROM, status); assert(FALSE); } sendto_fail_pid = *pid; } return; } #else void #ifdef POSIX_MSEM mutex_wake_proc(sem_t *mutex_wake_msem_ptr) #else mutex_wake_proc(msemaphore *mutex_wake_msem_ptr) #endif { /* Unlock the memsem to wake the proc waiting on it */ int rc; /* * CAUTION : man pages on beowulf and hrothgar do not * mention anything about msem_unlock being interrupted. * It is being assumed here that msem_unlock, if interrupted * returns -1 and sets errno to EINTR. If the behavior is * undefined when interrupted, processes waiting to be woken * up may hang, and WE ARE TOAST!!! */ /* * Additonal note: this was converted to an EINTR wrapper macro. */ do { rc = MSEM_UNLOCK(mutex_wake_msem_ptr); } while (-1 == rc && EINTR == errno); if (0 > rc) { assert(FALSE); RTS_ERROR_ABT(VARLSTCNT(7) ERR_MUTEXERR, 0, ERR_TEXT, 2, RTS_ERROR_TEXT("Error with msem_unlock()/sem_post()"), errno); } return; } #endif fis-gtm-V7.0-005/sr_unix/mutexsp.h0000755000032200000250000002051314342376330015723 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef MUTEXSP_H #define MUTEXSP_H #ifdef MUTEX_MSEM_WAKE #ifdef POSIX_MSEM #include "gtm_semaphore.h" #else #include #endif #endif #define MUTEX_INIT_SEMVAL LOCK_AVAILABLE #define MUTEX_IMPOSSIBLE_SEMVAL -1024 /* Any value other than * GLOBAL_LOCK_AVAILABLE, * GLOBAL_LOCK_IN_USE, and * REC_UNDER_PROGRESS */ #define MUTEX_CONST_TIMEOUT_VAL 16 #define MUTEX_NUM_WAIT_BITS 19 /* max p such that 2**p <= 10**6 */ #define MUTEX_INTR_SLPCNT 2 #define WRTPND_LSBIT_MASK 1 #define MUTEX_SOCK_DIR GTM_TMP_ENV #define DEFAULT_MUTEX_SOCK_DIR DEFAULT_GTM_TMP #define MUTEX_SOCK_FILE_PREFIX "gtm_mutex" #define MAX_MUTEX_SOCKFILE_NAME_LEN (SIZEOF(MUTEX_SOCK_FILE_PREFIX) + MAX_DIGITS_IN_INT) #define BIN_TOGGLE(x) ((x) ? 0 : 1) #define HUNDRED_MSEC 100000 #define E_4 10000 #define ONE_MILLION 1000000 typedef struct { uint4 pid; int mutex_wake_instance; } mutex_wake_msg_t; typedef enum { MUTEX_LOCK_WRITE, MUTEX_LOCK_WRITE_IMMEDIATE } mutex_lock_t; /* * Initialize a mutex with n que entries. If crash is TRUE, then this is * a "crash" reinitialization; otherwise it's a "clean" initialization. */ void gtm_mutex_init(gd_region *reg, int n, bool crash); /* gtm_mutex_lock - lock access to mutex for region reg */ /* gtm prefix added because solaris icu has a mutex_lock */ enum cdb_sc gtm_mutex_lock(gd_region *reg, mutex_spin_parms_ptr_t mutex_spin_parms, int crash_count, mutex_lock_t mutex_lock_type, wait_state state); /* mutex_unlockw - unlock write access to mutex for region reg */ enum cdb_sc mutex_unlockw(gd_region *reg, int crash_count); /* mutex_seed_init - set the seed for pseudo random mutex sleep times */ void mutex_seed_init(void); void mutex_cleanup(gd_region *reg); #ifdef MUTEX_MSEM_WAKE # ifdef POSIX_MSEM void mutex_wake_proc(sem_t *mutex_wake_msem_ptr); # else void mutex_wake_proc(msemaphore *mutex_wake_msem_ptr); # endif #else void mutex_wake_proc(sm_int_ptr_t pid, int mutex_wake_instance); #endif void mutex_sock_init(void); void mutex_sock_cleanup(void); unsigned char *pid2ascx(unsigned char *pid_str, pid_t pid); void mutex_per_process_init(void); #ifdef MUTEX_MSEM_WAKE #ifdef POSIX_MSEM # define MSEM_LOCKW(X) sem_wait(X) # define MSEM_LOCKNW(X) sem_trywait(X) # define MSEM_UNLOCK(X) sem_post(X) #else # define MSEM_LOCKW(X) msem_lock(X, 0) # define MSEM_LOCKNW(X) msem_lock(X, MSEM_IF_NOWAIT) # define MSEM_UNLOCK(X) msem_unlock(X, 0) #endif #endif #if defined(MUTEX_DEBUG) || defined(MUTEX_TRACE) || defined(MUTEX_TEST_RECOVERY) #include "gtm_stdio.h" #endif #ifdef MUTEX_DEBUG #define MUTEX_DPRINT1(p) {FPRINTF(stderr, p); FFLUSH(stderr);} #define MUTEX_DPRINT2(p, q) {FPRINTF(stderr, p, q); FFLUSH(stderr);} #define MUTEX_DPRINT3(p, q, r) {FPRINTF(stderr, p, q, r); FFLUSH(stderr);} #define MUTEX_DPRINT4(p, q, r, s) {FPRINTF(stderr, p, q, r, s); FFLUSH(stderr);} #define MUTEX_DPRINT5(p, q, r, s,t) {FPRINTF(stderr, p, q, r, s, t); FFLUSH(stderr);} #else #define MUTEX_DPRINT1(p) #define MUTEX_DPRINT2(p, q) #define MUTEX_DPRINT3(p, q, r) #define MUTEX_DPRINT4(p, q, r, s) #define MUTEX_DPRINT5(p, q, r, s, t) #endif #ifdef MUTEX_TRACE #define DECLARE_MUTEX_TRACE_CNTRS \ GBLDEF int mutex_trc_lockw = 0; \ GBLDEF int mutex_trc_lockwim = 0; \ GBLDEF int mutex_trc_w_atmpts = 0; \ GBLREF int mutex_trc_mutex_slp_fn = 0; \ GBLREF int mutex_trc_mutex_slp_fn_noslp = 0; \ GBLDEF int mutex_trc_slp = 0; \ GBLDEF int mutex_trc_wt_short_slp = 0; \ GBLDEF int mutex_trc_wtim_short_slp = 0; \ GBLDEF int mutex_trc_slp_tmout = 0; \ GBLDEF int mutex_trc_intr_tmout = 0; \ GBLDEF int mutex_trc_slp_intr = 0; \ GBLDEF int mutex_trc_slp_wkup = 0; \ GBLDEF int mutex_trc_pgybckd_dlyd_wkup = 0; \ GBLDEF int mutex_trc_xplct_dlyd_wkup = 0; \ GBLDEF int mutex_trc_crit_wk = 0; \ GBLDEF int mutex_trc_dump_done = 0; #define USE_MUTEX_TRACE_CNTRS \ GBLREF int mutex_trc_lockw; \ GBLREF int mutex_trc_lockwim; \ GBLREF int mutex_trc_w_atmpts; \ GBLREF int mutex_trc_mutex_slp_fn; \ GBLREF int mutex_trc_mutex_slp_fn_noslp; \ GBLREF int mutex_trc_slp; \ GBLREF int mutex_trc_wt_short_slp; \ GBLREF int mutex_trc_wtim_short_slp; \ GBLREF int mutex_trc_slp_tmout; \ GBLDEF int mutex_trc_intr_tmout; \ GBLDEF int mutex_trc_slp_intr; \ GBLREF int mutex_trc_slp_wkup; \ GBLREF int mutex_trc_pgybckd_dlyd_wkup; \ GBLREF int mutex_trc_xplct_dlyd_wkup; \ GBLREF int mutex_trc_crit_wk; \ GBLDEF int mutex_trc_dump_done; #define MUTEX_TRACE_CNTR(x) ((x)++) #define DUMP_MUTEX_TRACE_CNTRS \ if (!mutex_trc_dump_done) \ { \ FPRINTF(stderr, "mutex_lockw : %d\n", mutex_trc_lockw);\ FPRINTF(stderr, "mutex_lockwim : %d\n", mutex_trc_lockwim);\ FPRINTF(stderr, "Lock write attempts : %d\n", mutex_trc_w_atmpts);\ FPRINTF(stderr, "Calls to mutex_sleep : %d\n", mutex_trc_mutex_slp_fn);\ FPRINTF(stderr, "Mutex_sleep no sleep : %d\n", mutex_trc_mutex_slp_fn_noslp);\ FPRINTF(stderr, "Write short sleep count : %d\n", mutex_trc_wt_short_slp);\ FPRINTF(stderr, "Wrt Imm short slp count : %d\n", mutex_trc_wtim_short_slp);\ FPRINTF(stderr, "Sleep timeout : %d\n", mutex_trc_slp_tmout);\ FPRINTF(stderr, "Sleep timeout caused by intr: %d\n", mutex_trc_intr_tmout);\ FPRINTF(stderr, "Sleep interrupted : %d\n", mutex_trc_slp_intr);\ FPRINTF(stderr, "Sleep woken up : %d\n", mutex_trc_slp_wkup);\ FPRINTF(stderr, "Dlyd pgybckd slp wkup : %d\n", mutex_trc_pgybckd_dlyd_wkup);\ FPRINTF(stderr, "Dlyd xplct slp wkup : %d\n", mutex_trc_xplct_dlyd_wkup);\ FPRINTF(stderr, "Crit wake others : %d\n", mutex_trc_crit_wk);\ mutex_trc_dump_done = 1;\ } #else #define DECLARE_MUTEX_TRACE_CNTRS #define USE_MUTEX_TRACE_CNTRS #define MUTEX_TRACE_CNTR(x) #define DUMP_MUTEX_TRACE_CNTRS #endif /* MUTEX_TRACE */ #ifdef MUTEX_TEST_RECOVERY #include #define DECLARE_MUTEX_TEST_SIGNAL_FLAG GBLDEF int mutex_test_signalled = FALSE; #define USE_MUTEX_TEST_SIGNAL_FLAG GBLREF int mutex_test_signalled; #define MUTEX_TEST_SIGNAL_HERE(x, y) \ if ((y) && !mutex_test_signalled) \ { \ FPRINTF(stderr, x); \ FFLUSH(stderr); \ mutex_test_signalled = TRUE; \ exi_rundown(SIGQUIT); change this before use \ } \ else ; #define MUTEX_TEST_PRINT1(p) {FPRINTF(stderr, p); FFLUSH(stderr);} #define MUTEX_TEST_PRINT2(p, q) {FPRINTF(stderr, p, q); FFLUSH(stderr);} #define MUTEX_TEST_PRINT3(p, q, r) {FPRINTF(stderr, p, q, r); FFLUSH(stderr);} #define MUTEX_TEST_PRINT4(p, q, r, s) {FPRINTF(stderr, p, q, r, s); FFLUSH(stderr);} #define MUTEX_TEST_PRINT5(p, q, r, s,t) {FPRINTF(stderr, p, q, r, s, t); FFLUSH(stderr);} #else #define DECLARE_MUTEX_TEST_SIGNAL_FLAG #define USE_MUTEX_TEST_SIGNAL_FLAG #define MUTEX_TEST_SIGNAL_HERE(x, y) #define MUTEX_TEST_PRINT1(p) #define MUTEX_TEST_PRINT2(p, q) #define MUTEX_TEST_PRINT3(p, q, r) #define MUTEX_TEST_PRINT4(p, q, r, s) #define MUTEX_TEST_PRINT5(p, q, r, s, t) #endif /* MUTEX_TEST */ #define TOO_MANY_REGIONS 1025 /* An arbitrary number that is a value much higher than the * total # of database regions + jnlpools that a process is * supposed to attach to. Used only in debug to ensure * TREF(crit_reg_count) does not go too high. */ #define SET_CSA_NOW_CRIT_TRUE(CSA) \ MBSTART { \ (CSA)->now_crit = TRUE; \ (TREF(crit_reg_count))++; \ assert((0 < TREF(crit_reg_count)) && (TOO_MANY_REGIONS > TREF(crit_reg_count))); \ } MBEND #define SET_CSA_NOW_CRIT_FALSE(CSA) \ MBSTART { \ (CSA)->now_crit = FALSE; \ (TREF(crit_reg_count))--; \ assert((0 <= TREF(crit_reg_count)) && (TOO_MANY_REGIONS > TREF(crit_reg_count))); \ } MBEND #endif /* MUTEXSP_H */ fis-gtm-V7.0-005/sr_unix/ojchildioclean.c0000644000032200000250000000557414342376335017174 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2017 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. * * * ****************************************************************/ /* * ------------------------------------------------------- * This routine cleans up the child process prior to the exec * ------------------------------------------------------- */ #include "mdef.h" #include "gtm_fcntl.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "dpgbldir.h" #include "jnl.h" #include "jobsp.h" #include "gtmio.h" #include "gtmcrypt.h" #include #include "relinkctl.h" GBLREF int mutex_sock_fd; void ojchildioclean(void) { int rc; unix_db_info *udi; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; gd_region *r_top, *r_local; gd_addr *addr_ptr; open_relinkctl_sgm *linkctl; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Close any encryption related fds that the plug-in might have opened */ GTMCRYPT_CLOSE; /* Run through the list of databases to simply close them out (still open by parent) */ for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) { for (r_local = addr_ptr->regions, r_top = r_local + addr_ptr->n_regions; r_local < r_top; r_local++) { if (r_local->open && !r_local->was_open && IS_REG_BG_OR_MM(r_local)) { udi = (unix_db_info *)(r_local->dyn.addr->file_cntl->file_info); csa = &udi->s_addrs; csd = csa->hdr; /* Close journal file if open. Check for JNL_ALLOWED instead of JNL_ENABLED to ensure * we do not miss out on closing open journal file descriptors in the case where the * current jnl_state is "jnl_closed" but we had opened the file when it was "jnl_open". */ if (JNL_ALLOWED(csd) && (NULL != csa->jnl) && (NOJNL != csa->jnl->channel)) CLOSEFILE_RESET(csa->jnl->channel, rc); /* resets "channel" to FD_INVALID */ CLOSEFILE_RESET(udi->fd, rc); /* resets "udi->fd" to FD_INVALID */ } } } #ifndef MUTEX_MSEM_WAKE /* We don't need the parent's mutex socket anymore either (if we are using the socket) */ if (FD_INVALID != mutex_sock_fd) CLOSEFILE_RESET(mutex_sock_fd, rc); /* resets "mutex_sock_fd" to FD_INVALID */ #endif /* Since we are removing artifacts from the originating process (which still has these files open), there is * no need to decrement the counts (they will increase if this process links the same files). The FALSE * argument prevents the relinkctl-attach & rtnobj-reference counts from being modified in this cleanup. */ ARLINK_ONLY(relinkctl_rundown(FALSE, FALSE)); } fis-gtm-V7.0-005/sr_unix/obj_filesp.h0000755000032200000250000000257614342376330016343 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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. * * * ****************************************************************/ #ifndef OBJ_FILESP_INCLUDED #define OBJ_FILESP_INCLUDED typedef struct linkage_entry { struct linkage_entry *next; struct sym_table *symbol; size_t lit_offset; /* Offset into literal string pool where string name was stored */ } linkage_entry; struct sym_table *define_symbol(unsigned char psect, mstr *name); void comp_linkages(void); void resolve_sym (void); void output_relocation (void); void output_symbol (void); void buff_emit(void); void buff_flush(void); /* Currently JSB contains two instructions: * load %ret, -1 * ret # nop # (hppa) */ #if defined(__ia64) # define JSB_ACTION_N_INS 4 #elif defined(__hpux) # define JSB_ACTION_N_INS 3 #elif defined(__x86_64__) # define JSB_ACTION_N_INS 8 #elif defined(__sparc) # define JSB_ACTION_N_INS 3 #elif defined(__mvs__) || defined(Linux390) # define JSB_ACTION_N_INS 4 #else # define JSB_ACTION_N_INS 2 #endif #define JSB_MARKER "GTM_CODE" #define MIN_LINK_PSECT_SIZE 0 #endif fis-gtm-V7.0-005/sr_unix/offset.awk0000755000032200000250000000506514342376330016044 0ustar librarygtc################################################################# # # # Copyright 2002, 2004 Sanchez Computer Associates, 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. # # # ################################################################# BEGIN { curlyparenlevel = 0; squareparenlevel = 0; prevfield = ""; fieldcount = 0; } { input = $0; gsub("\\[.*\\]", " "); gsub("\".*\"", " "); gsub("'.*'", " "); gsub("\\[", " & "); # telecaster's awk doesn't allow for putting [ within a [] block hence this is separate from the following line gsub("\\]", " & "); gsub("[#;,{}=*]", " & "); if ($1 != "") { for (i = 1; i <= NF; i++) { if ($i == "{") { if (curlyparenlevel == 0 && squareparenlevel == 0 && prevfield == c_struct) { found_struct=1; gsub(c_struct,c_struct"_type",input); } curlyparenlevel++; if (curlyparenlevel == 1 && squareparenlevel == 0) fieldcount = 0; } else if ($i == "}") curlyparenlevel--; else if ($i == "[") squareparenlevel++; else if ($i == "]") squareparenlevel--; else if ($i == ";" || $i == ",") { if (squareparenlevel == 0 && curlyparenlevel == 1 && prevfield !~ /^[0-9][0-9]*$/) structarray[fieldcount++] = prevfield; else if ((curlyparenlevel == 0 && squareparenlevel == 0) && (prevfield == c_struct || found_struct)) { for (j = 1; j <= i; j++) printf "%s ", $j; if (found_struct) printf "\ntypedef struct %s_type %s;\n",c_struct,c_struct; printf "\n#undef offsetof\n#define offsetof(TYPE, MEMBER) ((int) &((TYPE *)0)->MEMBER)"; printf "\n#define PRINT_OFFSET(TYPE, MEMBER) printf(\"\\toffset = %%.4d [0x%%.4x] size = %%.4d [0x%%.4x] ----> \"#MEMBER\" \\n\", offsetof(TYPE,MEMBER), offsetof(TYPE,MEMBER), sizeof(temp_##TYPE.MEMBER), sizeof(temp_##TYPE.MEMBER))\n"; printf "\nmain()\n{\n\t%s\ttemp_%s;\n\n", c_struct, c_struct; printf "\tprintf (\"\\nStructure ----> %s <---- \tsize = %%d [0x%%x] \\n\\n\", sizeof(temp_%s), sizeof(temp_%s));\n", c_struct, c_struct, c_struct; for (i = 0; i < fieldcount; i++) { gsub(/[)(]*/,"",structarray[i]); printf "\n\tPRINT_OFFSET(%s,%s);", c_struct, structarray[i]; } printf "\n\tprintf(\"\\n\");\n}\n"; exit } } if (squareparenlevel == 0 && $i != "]") prevfield = $i; } print input; } } fis-gtm-V7.0-005/sr_unix/offset.csh0000755000032200000250000001071414342376330016034 0ustar librarygtc#!/usr/local/bin/tcsh ################################################################# # # # 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. # # # ################################################################# if ($# != 2) then echo "" echo " Usage: $0 " echo "" exit endif ############################################################################################################### # # This block of demarcated code initializes the environment variables and aliases used by the offset program. # If this program is used anywhere outside the Greystone development environment, care should be taken in doing the following. # # (a) This utility offset.csh needs the shell "tcsh" to exist in /usr/local/bin (a soft link would suffice too). # (b) This utility also needs an accompanying awk script "offset.awk" to do primitive parsing of the C file. # (c) The environment variable "gtm_src" should be set to point to the absolute pathname containing the C-source-file. # (d) The environment variable "gtm_tools" should be set to point to the absolute pathname containing the "offset.awk" program. # (e) The alias "gt_cc" should be set to the command for the C-compiler with appropriate flags (including #defines). # (f) The alias "gt_ld" should be set to the command for the C-linker (cc or ld would do) with appropriate flags. # (g) The environment variable "user" should be set to the appropriate user-name. Usually this is taken care of by the shell. # # Note that this utility creates temporary files for its processing in the form /tmp__${user}_offset_* # It removes these files only in the case the offset determination was successful. # Therefore care should be taken to avoid unintended proliferation of these temporary files in the system. # # Once the above changes have been done in place here, comment out the greystone-environment-specific-initialization below. # switch ($gtm_exe:t) case "[bB]*": alias gtcc "`alias gt_cc_bta`" set gt_ld_options = "$gt_ld_options_bta" breaksw case "[dD]*": alias gtcc "`alias gt_cc_dbg`" set gt_ld_options = "$gt_ld_options_dbg" breaksw case "[pP]*": alias gtcc "`alias gt_cc_pro`" set gt_ld_options = "$gt_ld_options_pro" breaksw default: echo "Environment Variable gtm_exe should point to either 'pro' or 'bta' or 'dbg' only. Exiting..." exit -1 breaksw endsw alias gt_ld $gt_ld_linker $gt_ld_options -L$gtm_obj $gt_ld_extra_libs $gt_ld_sysrtns $gt_ld_syslibs ############################################################################################################### set c_struct = $1 set srcfile = $2 set TMPFILE = /tmp/__${user}_offset_ if !(-e $gtm_src/$srcfile) then echo "OFFSET-E-INVALIDSRCFILE : $gtm_src/$srcfile doesn't exist. Please give a valid c-source-file-name. Exiting..." exit -1 endif if !(-r $gtm_src/$srcfile) then echo "OFFSET-E-SRCRDERR : Can't read $gtm_src/$srcfile for compiling. Please ensure read permissions before reissuing the command. Exiting..." exit -1 endif if ($srcfile:e != "c") then echo "OFFSET-E-INVALIDCFILE : $srcfile doesn't have a .c extension. Please give a valid c-source-file-name. Exiting..." exit -1 endif (gtcc -E $gtm_src/$srcfile > ${TMPFILE}_$srcfile:r.lis) >& /dev/null awk -v c_struct=${c_struct} -f $gtm_tools/offset.awk ${TMPFILE}_$srcfile:r.lis > ${TMPFILE}_$srcfile gtcc ${TMPFILE}_$srcfile -o ${TMPFILE}_$srcfile.o >& /dev/null if ($status != 0) then echo "OFFSET-E-SRCSTRUCTMISMATCH : Very likely that the c-structure isn't used in the c-source-file-name. Please give a valid input." echo " If you feel that the input is valid, please contact the author for assistance in debugging the offset utility (See ${TMPFILE}_$srcfile:r* for details)" echo " Exiting..." echo "-----------------------------------------------------------------------------------------" echo "###### Below is the output from the compiler ########" echo "-----------------------------------------------------------------------------------------" gtcc ${TMPFILE}_$srcfile -o ${TMPFILE}_$srcfile.o exit -1 endif gt_ld ${TMPFILE}_$srcfile.o -o ${TMPFILE}_$srcfile:r.out >& /dev/null ${TMPFILE}_$srcfile:r.out rm -f ${TMPFILE}_$srcfile:r.lis rm -f ${TMPFILE}_$srcfile rm -f ${TMPFILE}_$srcfile.o rm -f ${TMPFILE}_$srcfile:r.out fis-gtm-V7.0-005/sr_unix/ojchildioset.c0000755000032200000250000001424014342376330016671 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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 #include "gtm_fcntl.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_unistd.h" #include "gtm_stat.h" #include "job.h" #include "gtmio.h" #include "eintr_wrappers.h" #include "gtmmsg.h" #include "io.h" #include "iosocketdef.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif GBLREF int job_errno; GBLREF d_socket_struct *socket_pool; ZOS_ONLY(error_def(ERR_BADTAG);) error_def(ERR_JOBFAIL); error_def(ERR_TEXT); /* * --------------------------------------------------------- * Set up output and error file descriptors in * a child. * --------------------------------------------------------- */ int ojchildioset(job_params_type *jparms) { int dup_ret, in_fd, out_fd, err_fd; char fname_buf[MAX_STDIOE_LEN], buf[MAX_STDIOE_LEN]; int rc; joberr_t joberr = joberr_gen; int4 index; mstr_len_t handle_len; socket_struct *socketptr; ZOS_ONLY(int realfiletag;) /* * Open Input */ if (IS_JOB_SOCKET(jparms->params.input.buffer, jparms->params.input.len)) { handle_len = JOB_SOCKET_HANDLE_LEN(jparms->params.input.len); index = iosocket_handle(JOB_SOCKET_HANDLE(jparms->params.input.buffer), &handle_len, FALSE, socket_pool); if (-1 == index) { joberr = joberr_stdin_socket_lookup; job_errno = EINVAL; return joberr; } socketptr = socket_pool->socket[index]; FCNTL3(socketptr->sd, F_DUPFD, 1, dup_ret); if (-1 == dup_ret) { joberr = joberr_io_stdin_socket_dup; job_errno = errno; return joberr; } in_fd = dup_ret; jparms->input_prebuffer_size = socketptr->buffered_length; if (0 < jparms->input_prebuffer_size) { /* copy any buffered reads */ jparms->input_prebuffer = malloc(jparms->input_prebuffer_size); memcpy(jparms->input_prebuffer, socketptr->buffer + socketptr->buffered_offset, jparms->input_prebuffer_size); } } else { strncpy(fname_buf, jparms->params.input.buffer, jparms->params.input.len); *(fname_buf + jparms->params.input.len) = '\0'; OPENFILE(fname_buf, O_RDONLY, in_fd); if (FD_INVALID == in_fd) { joberr = joberr_io_stdin_open; job_errno = errno; return joberr; } jparms->input_prebuffer_size = 0; } /* * Open Output */ if (IS_JOB_SOCKET(jparms->params.output.buffer, jparms->params.output.len)) { handle_len = JOB_SOCKET_HANDLE_LEN(jparms->params.output.len); index = iosocket_handle(JOB_SOCKET_HANDLE(jparms->params.output.buffer), &handle_len, FALSE, socket_pool); if (-1 == index) { joberr = joberr_stdout_socket_lookup; job_errno = EINVAL; return joberr; } FCNTL3(socket_pool->socket[index]->sd, F_DUPFD, 1, dup_ret); if (-1 == dup_ret) { joberr = joberr_io_stdout_socket_dup; job_errno = errno; return joberr; } out_fd = dup_ret; } else { strncpy(fname_buf, jparms->params.output.buffer, jparms->params.output.len); *(fname_buf + jparms->params.output.len) = '\0'; CREATE_FILE(fname_buf, 0666, out_fd); if (FD_INVALID == out_fd) { joberr = joberr_io_stdout_creat; job_errno = errno; return joberr; } # ifdef __MVS__ /* tagging as ASCII is fine now, that might change in the future for gtm_utf8_mode */ if (-1 == gtm_zos_set_tag(out_fd, TAG_ASCII, TAG_TEXT, TAG_FORCE, &realfiletag)) TAG_POLICY_SEND_MSG(fname_buf, errno, realfiletag, TAG_ASCII); # endif CLOSEFILE_RESET(out_fd, rc); /* resets "out_fd" to FD_INVALID */ OPENFILE(fname_buf, O_WRONLY, out_fd); if (FD_INVALID == out_fd) { joberr = joberr_io_stdout_open; job_errno = errno; return joberr; } } /* * Open Error */ if (IS_JOB_SOCKET(jparms->params.error.buffer, jparms->params.error.len)) { handle_len = JOB_SOCKET_HANDLE_LEN(jparms->params.error.len); index = iosocket_handle(JOB_SOCKET_HANDLE(jparms->params.error.buffer), &handle_len, FALSE, socket_pool); if (-1 == index) { joberr = joberr_stderr_socket_lookup; job_errno = EINVAL; return joberr; } FCNTL3(socket_pool->socket[index]->sd, F_DUPFD, 1, dup_ret); if (-1 == dup_ret) { joberr = joberr_io_stderr_socket_dup; job_errno = errno; return joberr; } err_fd = dup_ret; } else { strncpy(fname_buf, jparms->params.error.buffer, jparms->params.error.len); *(fname_buf + jparms->params.error.len) = '\0'; CREATE_FILE(fname_buf, 0666, err_fd); if (FD_INVALID == err_fd) { joberr = joberr_io_stderr_creat; job_errno = errno; return joberr; } # ifdef __MVS__ if (-1 == gtm_zos_set_tag(err_fd, TAG_EBCDIC, TAG_TEXT, TAG_FORCE, &realfiletag)) TAG_POLICY_SEND_MSG(fname_buf, errno, realfiletag, TAG_EBCDIC); # endif CLOSEFILE_RESET(err_fd, rc); /* resets "err_fd" to FD_INVALID */ OPENFILE(fname_buf, O_WRONLY, err_fd); if (FD_INVALID == err_fd) { joberr = joberr_io_stderr_open; job_errno = errno; return joberr; } } /* * Redirect Input */ CLOSEFILE(0, rc); FCNTL3(in_fd, F_DUPFD, 0, dup_ret); if (-1 == dup_ret) { joberr = joberr_io_stdin_dup; job_errno = errno; return joberr; } # ifdef __MVS__ /* policy tagging because by default input is /dev/null */ if (-1 == gtm_zos_tag_to_policy(in_fd, TAG_UNTAGGED, &realfiletag)) TAG_POLICY_SEND_MSG(fname_buf, errno, realfiletag, TAG_UNTAGGED); # endif CLOSEFILE_RESET(in_fd, rc); /* resets "in_fd" to FD_INVALID */ /* * Redirect Output */ CLOSEFILE(1, rc); FCNTL3(out_fd, F_DUPFD, 0, dup_ret); if (-1 == dup_ret) { joberr = joberr_io_stdout_dup; job_errno = errno; return joberr; } CLOSEFILE_RESET(out_fd, rc); /* resets "out_fd" to FD_INVALID */ /* * Redirect Error */ CLOSEFILE(2, rc); FCNTL3(err_fd, F_DUPFD, 0, dup_ret); if (-1 == dup_ret) { joberr = joberr_io_stderr_dup; job_errno = errno; return joberr; } CLOSEFILE_RESET(err_fd, rc); /* resets "err_fd" to FD_INVALID */ return 0; } fis-gtm-V7.0-005/sr_unix/ojchildparms.c0000644000032200000250000002315314342376330016670 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* * --------------------------------------------------------- * Parse job parameters * --------------------------------------------------------- */ #include "mdef.h" #include "gtm_string.h" #include "gtm_stdlib.h" #include "gtm_unistd.h" #include "eintr_wrappers.h" #include "job.h" #include "compiler.h" #include "gcall.h" #include "stringpool.h" #include "op.h" /* for op_nullexp() */ #include "io.h" #include "iosocketdef.h" #include "indir_enum.h" #include #include "gtm_maxstr.h" #include "job_addr.h" #include "gtmci.h" #include "cache.h" #include "hashtab_objcode.h" #include "gtmio.h" #include "gtmmsg.h" /* for gtm_putmsg prototype */ GBLREF spdesc stringpool; GBLREF io_log_name *dollar_principal; GBLREF io_pair io_std_device; GBLREF int4 aligned_source_buffer[]; GBLREF stack_frame *frame_pointer; GBLREF hash_table_objcode cache_table; #ifdef DEBUG GBLREF unsigned char *msp; static unsigned char *save_msp; #endif static char *sp; static int setup_fd; STATICFNDCL void receive_child_locals_init(char **local_buff, mval **comm_stack_ptr); STATICFNDCL void receive_child_locals_finalize(char **local_buff); /* All other platforms use this much faster direct return */ void gtm_levl_ret_code(void); error_def(ERR_CLOSEFAIL); error_def(ERR_JOBSETUP); error_def(ERR_STRINGOFLOW); error_def(ERR_JOBLABOFF); error_def(ERR_JOBLVN2LONG); error_def(ERR_MAXACTARG); static CONDITION_HANDLER(job_addr_ch) { joberr_t joberr; int rc; START_CH(FALSE); joberr = joberr_rtn; DOWRITERC(setup_fd, &joberr, SIZEOF(joberr), rc); /* Ignore rc, as it is more important to report the underlying error than it is to report problems reporting it. */ NEXTCH; } /* * ------------------------------------------------ * Get parameters from passed socket into * parameter structure * ------------------------------------------------ */ void ojchildparms(job_params_type *jparms, gcall_args *g_args, mval *arglst) { char parm_string[8]; int4 argcnt, i; int rc; job_setup_op setup_op; boolean_t setup_done = FALSE; job_params_msg params; job_arg_count_msg arg_count; job_arg_msg arg_msg; job_buffer_size_msg buffer_size; d_socket_struct *dsocketptr; socket_struct *socketptr; char *local_buff = NULL; mval *command_str; joberr_t joberr; rhdtyp *rtnhdr; char *transfer_addr; if ((NULL == sp) && (!((sp = GETENV(CHILD_FLAG_ENV)) && sp[0]))) /* note assignment */ return; setup_fd = (int)ATOL(sp); if (NULL != g_args) g_args->callargs = 0; while(!setup_done) { DOREADRC(setup_fd, &setup_op, SIZEOF(setup_op), rc); if (rc < 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_JOBSETUP, 2, LEN_AND_LIT("setup operation"), errno, 0); switch(setup_op) { case job_done: case local_trans_done: setup_done = TRUE; break; case job_set_params: DOREADRC(setup_fd, &jparms->params, SIZEOF(jparms->params), rc); if (rc < 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_JOBSETUP, 2, LEN_AND_LIT("job parameters"), errno, 0); /* Validate the routine and label */ MSTR_DEF(routine_mstr, jparms->params.routine.len, jparms->params.routine.buffer); MSTR_DEF(label_mstr, jparms->params.label.len, jparms->params.label.buffer); ESTABLISH(job_addr_ch); if (!job_addr(&routine_mstr, &label_mstr, jparms->params.offset, (char **)&rtnhdr, &transfer_addr)) { REVERT; /* Send routine status */ joberr = joberr_rtn; DOWRITERC(setup_fd, &joberr, SIZEOF(joberr), rc); /* Ignore rc, as it is more important to report the underlying error than it is * to report problems reporting it. */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(2) ERR_JOBLABOFF, 0); } REVERT; /* Send routine status */ joberr = joberr_ok; DOWRITERC(setup_fd, &joberr, SIZEOF(joberr), rc); if (rc < 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_JOBSETUP, 2, LEN_AND_LIT("routine status"), errno, 0); break; case job_set_parm_list: DOREADRC(setup_fd, &arg_count, SIZEOF(arg_count), rc); if (rc < 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_JOBSETUP, 2, LEN_AND_LIT("argument count"), errno, 0); if (arg_count > MAX_ACTUALS) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXACTARG); g_args->callargs = arg_count + PUSH_PARM_OVERHEAD; g_args->truth = 1; g_args->retval = 0; g_args->mask = 0; g_args->argcnt = arg_count; ENSURE_STP_FREE_SPACE(arg_count * MAX_JOB_LEN); for (i = 0; i < arg_count; i++) { DOREADRC(setup_fd, &arg_msg.len, SIZEOF(arg_msg.len), rc); if (rc < 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_JOBSETUP, 2, LEN_AND_LIT("argument length"), errno, 0); if (0 > arg_msg.len) g_args->argval[i] = op_nullexp(); /* negative len indicates null arg */ else { DOREADRC(setup_fd, &arg_msg.data, arg_msg.len, rc); if (rc < 0) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_JOBSETUP, 2, LEN_AND_LIT("argument"), errno, 0); assertpro(arg_msg.len <= MAX_JOB_LEN); arglst[i].str.len = arg_msg.len; arglst[i].str.addr = (char *)stringpool.free; memcpy(stringpool.free, arg_msg.data, arg_msg.len); stringpool.free += arg_msg.len; arglst[i].mvtype = MV_STR; g_args->argval[i] = &arglst[i]; } } break; case job_set_input_buffer: assertpro(io_std_device.in && (gtmsocket == io_std_device.in->type)); dsocketptr = (d_socket_struct *)(io_std_device.in->dev_sp); assertpro(dsocketptr); assertpro(-1 != dsocketptr->current_socket); assertpro(dsocketptr->current_socket < dsocketptr->n_socket); socketptr = dsocketptr->socket[dsocketptr->current_socket]; DOREADRC(setup_fd, &buffer_size, SIZEOF(buffer_size), rc); if (rc < 0) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_JOBSETUP, 2, LEN_AND_LIT("input buffer size"), errno, 0); assertpro(buffer_size <= DEFAULT_SOCKET_BUFFER_SIZE); assertpro(buffer_size <= socketptr->buffer_size); DOREADRC(setup_fd, socketptr->buffer, buffer_size, rc); if (rc < 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_JOBSETUP, 2, LEN_AND_LIT("input buffer"), errno, 0); socketptr->buffered_length = buffer_size; socketptr->buffered_offset = 0; break; case job_set_locals: /* We should get here from the second ojchildparams() call only */ assert(NULL == jparms); if (NULL == local_buff) { /* Initializations to receive the local vars */ receive_child_locals_init(&local_buff, &command_str); } command_str->mvtype = MV_STR; command_str->str.addr = local_buff; DOREADRC(setup_fd, &buffer_size, SIZEOF(buffer_size), rc); if (rc < 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_JOBSETUP, 2, LEN_AND_LIT("receive buffer size"), errno, 0); if(buffer_size > MAX_STRLEN) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_JOBLVN2LONG, 2, MAX_STRLEN, buffer_size); assert(buffer_size > 0); DOREADRC(setup_fd, local_buff, buffer_size, rc); if (rc < 0) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_JOBSETUP, 2, LEN_AND_LIT("local fragment"), errno, 0); assert((NULL != command_str->str.addr) && (0 != buffer_size)); command_str->str.len = buffer_size; s2pool(&command_str->str); op_commarg(command_str, indir_set); dm_start(); break; default: assertpro(FALSE && setup_op); } } if (NULL != local_buff) receive_child_locals_finalize(&local_buff); /* Keep the pipe alive until local transfer is done which is done at the second call to this function */ if (local_trans_done == setup_op) if ((rc = close(setup_fd)) < 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_CLOSEFAIL, 1, setup_fd, errno, 0); } STATICFNDEF void receive_child_locals_init(char **local_buff, mval **command_str) { rhdtyp *base_addr; int i; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(active_ch == ctxt); assert(ctxt == chnd); DEBUG_ONLY(save_msp = msp); *local_buff = malloc(MAX_STRLEN); (TREF(source_buffer)).addr = malloc(MAX_STRLEN + 2); /* changing source_buffer prevents long items from causing errors */ (TREF(source_buffer)).len = MAX_STRLEN + 1; /* Get space from the stack to save the command strings before putting the base stack frame This must be done first (before * putting the base frame) so that dm_start does not unintentionally pop strings off the stack */ PUSH_MV_STENT(MVST_MVAL); *command_str = &mv_chain->mv_st_cont.mvs_mval; /* Setup the base frame */ base_addr = make_cimode(); base_frame(base_addr); /* Finish base frame initialization - reset mpc/context to return to us without unwinding base frame */ frame_pointer->mpc = CODE_ADDRESS(gtm_levl_ret_code); frame_pointer->ctxt = GTM_CONTEXT(gtm_levl_ret_code); } STATICFNDEF void receive_child_locals_finalize(char **local_buff) { int i; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Unwind the base frame */ op_unwind(); frame_pointer = *(stack_frame**)msp; msp += SIZEOF(stack_frame *); /* Remove frame save pointer from stack */ free(*local_buff); free((TREF(source_buffer)).addr); /* Reset the source buffer */ (TREF(source_buffer)).len = MAX_SRCLINE; (TREF(source_buffer)).addr = (char *)&aligned_source_buffer; /* Return the space saved for command strings */ POP_MV_STENT(); ctxt = active_ch = chnd; /* Clear extra condition handlers added by dm_start()s */ assert(save_msp == msp); } fis-gtm-V7.0-005/sr_unix/ojchkfs.c0000755000032200000250000000271514342376330015644 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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_string.h" #include "gtm_stdio.h" #include "gtm_fcntl.h" #include "job.h" #include "eintr_wrappers.h" #ifdef KEEP_ZOS_EBCDIC extern unsigned char a2e[]; #define SLSH a2e['/'] #else #define SLSH '/' #endif /* * -------------------------------------------------------- * Check if file spec is legal. * If exist flag is on, check if file exist * * Return: * FALSE - filespec is illegal, or * file does not exist (if exist flag is ON) * TRUE - good filespec (and file exist, if exist flag is ON) * -------------------------------------------------------- */ int4 ojchkfs (char *addr, int4 len, bool exist) { char es[MAX_FILSPC_LEN + 1]; int fclose_res; FILE *fp; /* First, check for a legal filespec */ if (len > MAX_FILSPC_LEN) return(FALSE); strncpy(es, addr, len); *(es + len) = '\0'; if (!exist) return(TRUE); Fopen(fp, es, "r"); if (0 == fp) return (FALSE); FCLOSE(fp, fclose_res); return(TRUE); } fis-gtm-V7.0-005/sr_unix/ojparams.c0000644000032200000250000002137014342376330016024 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "job.h" #include "min_max.h" #include "io.h" #include "iosocketdef.h" #include "eintr_wrappers.h" /* * --------------------------------------------------------- * Parse job parameters * --------------------------------------------------------- */ static readonly char definput[] = DEVNULL; MSTR_CONST(defoutext, ".mjo"); MSTR_CONST(deferrext, ".mje"); LITREF jp_datatype job_param_datatypes[]; GBLREF mval dollar_zgbldir; GBLREF d_socket_struct *socket_pool; error_def (ERR_PARFILSPC); /* * ------------------------------------------------ * Parse the parameter buffer and extract all * parameters for job command. * Save them in job_params structure. * ------------------------------------------------ */ void ojparams (char *p, job_params_type *job_params) { unsigned char ch; int4 status; mstr_len_t handle_len; FILE *curlvn_out; /* Initializations */ job_params->params.baspri = 0; job_params->params.input.len = 0; job_params->params.output.len = 0; job_params->params.error.len = 0; job_params->params.gbldir.len = 0; job_params->params.startup.len = 0; job_params->params.directory.len = 0; /* job_params->params.routine.len initialized by caller */ /* job_params->params.label.len initialized by caller */ job_params->cmdline.len = 0; job_params->passcurlvn = FALSE; /* Process parameter list */ while (*p != jp_eol) { switch (ch = *p++) { case jp_default: if (*p != 0) { job_params->params.directory.len = (int)((unsigned char) *p); memcpy(job_params->params.directory.buffer, p + 1, job_params->params.directory.len); } break; case jp_error: if (*p != 0) { job_params->params.error.len = (int)((unsigned char) *p); memcpy(job_params->params.error.buffer, p + 1, job_params->params.error.len); } break; case jp_gbldir: if (*p != 0) { job_params->params.gbldir.len = (int)((unsigned char) *p); memcpy(job_params->params.gbldir.buffer, p + 1, job_params->params.gbldir.len); } break; case jp_input: if (*p != 0) { job_params->params.input.len = (int)((unsigned char) *p); memcpy(job_params->params.input.buffer, p + 1, job_params->params.input.len); } break; case jp_output: if (*p != 0) { job_params->params.output.len = (int)((unsigned char) *p); memcpy(job_params->params.output.buffer, p + 1, job_params->params.output.len); } break; case jp_priority: job_params->params.baspri = (int4)(*((int4 *)p)); break; case jp_startup: if (*p != 0) { job_params->params.startup.len = (int)((unsigned char) *p); memcpy(job_params->params.startup.buffer, p + 1, job_params->params.startup.len); } break; case jp_cmdline: if(*p != 0) { job_params->cmdline.len = (int)((unsigned char) *p); memcpy(job_params->cmdline.buffer, p + 1, job_params->cmdline.len); } break; case jp_passcurlvn: job_params->passcurlvn = TRUE; break; case jp_account: case jp_detached: case jp_image: case jp_logfile: case jp_noaccount: case jp_nodetached: case jp_noswapping: case jp_process_name: case jp_schedule: case jp_swapping: break; default: assertpro(ch != ch); } switch (job_param_datatypes[ch]) { case jpdt_nul: break; case jpdt_num: p += SIZEOF(int4); break; case jpdt_str: p += ((int)((unsigned char)*p)) + 1; break; default: assertpro((jpdt_nul == job_param_datatypes[ch]) || (jpdt_num == job_param_datatypes[ch]) || (jpdt_str == job_param_datatypes[ch])); } } /* Defaults and Checks */ /* * Input file */ if (0 == job_params->params.input.len) { job_params->params.input.len = STRLEN(definput); memcpy(job_params->params.input.buffer, definput, job_params->params.input.len); } else if (IS_JOB_SOCKET(job_params->params.input.buffer, job_params->params.input.len)) { handle_len = JOB_SOCKET_HANDLE_LEN(job_params->params.input.len); if ((NULL == socket_pool) || (-1 == iosocket_handle(JOB_SOCKET_HANDLE(job_params->params.input.buffer), &handle_len, FALSE, socket_pool))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_PARFILSPC, 4, 5, "INPUT", job_params->params.input.len, job_params->params.input.buffer); } else if (!(status = ojchkfs (job_params->params.input.buffer, job_params->params.input.len, TRUE))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_PARFILSPC, 4, 5, "INPUT", job_params->params.input.len, job_params->params.input.buffer); /* * Output file */ if (0 == job_params->params.output.len) { memcpy (&job_params->params.output.buffer[0], job_params->params.routine.buffer, job_params->params.routine.len); memcpy (&job_params->params.output.buffer[job_params->params.routine.len], defoutext.addr, defoutext.len); if (job_params->params.output.buffer[0] == '%') job_params->params.output.buffer[0] = '_'; job_params->params.output.len = job_params->params.routine.len + defoutext.len; } else if (IS_JOB_SOCKET(job_params->params.output.buffer, job_params->params.output.len)) { handle_len = JOB_SOCKET_HANDLE_LEN(job_params->params.output.len); if ((NULL == socket_pool) || (-1 == iosocket_handle(JOB_SOCKET_HANDLE(job_params->params.output.buffer), &handle_len, FALSE, socket_pool))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_PARFILSPC, 4, 5, "OUTPUT", job_params->params.output.len, job_params->params.output.buffer); } else if (!(status = ojchkfs (job_params->params.output.buffer, job_params->params.output.len, FALSE))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_PARFILSPC, 4, 6, "OUTPUT", job_params->params.output.len, job_params->params.output.buffer); /* * Error file */ if (0 == job_params->params.error.len) { memcpy(&job_params->params.error.buffer[0], job_params->params.routine.buffer, job_params->params.routine.len); memcpy(&job_params->params.error.buffer[job_params->params.routine.len], deferrext.addr, deferrext.len); if (job_params->params.error.buffer[0] == '%') job_params->params.error.buffer[0] = '_'; job_params->params.error.len = job_params->params.routine.len + deferrext.len; } else if (IS_JOB_SOCKET(job_params->params.error.buffer, job_params->params.error.len)) { handle_len = JOB_SOCKET_HANDLE_LEN(job_params->params.error.len); if ((NULL == socket_pool) || (-1 == iosocket_handle(JOB_SOCKET_HANDLE(job_params->params.error.buffer), &handle_len, FALSE, socket_pool))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_PARFILSPC, 4, 5, "ERROR", job_params->params.error.len, job_params->params.error.buffer); } else if (!(status = ojchkfs (job_params->params.error.buffer, job_params->params.error.len, FALSE))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_PARFILSPC, 4, 5, "ERROR", job_params->params.error.len, job_params->params.error.buffer); /* * Global Directory */ if (0 == job_params->params.gbldir.len) { assert(MAX_JOBPARM_LEN > dollar_zgbldir.str.len); job_params->params.gbldir.len = dollar_zgbldir.str.len; memcpy(job_params->params.gbldir.buffer, dollar_zgbldir.str.addr, dollar_zgbldir.str.len); } else if (!(status = ojchkfs (job_params->params.gbldir.buffer, job_params->params.gbldir.len, FALSE))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_PARFILSPC, 4, 6, "GBLDIR", job_params->params.gbldir.len, job_params->params.gbldir.buffer); /* * Startup */ if (job_params->params.startup.len) if (!(status = ojchkfs (job_params->params.startup.buffer, job_params->params.startup.len, TRUE))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_PARFILSPC, 4, 7, "STARTUP", job_params->params.startup.len, job_params->params.startup.buffer); /* * Default Directory */ if (job_params->params.directory.len) if (!(status = ojchkfs (job_params->params.directory.buffer, job_params->params.directory.len, FALSE))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_PARFILSPC, 4, 7, "DEFAULT", job_params->params.directory.len, job_params->params.directory.buffer); /* Gather local variables to pass */ if (job_params->passcurlvn) { /* Create a "memory file" to store the job_set_locals messages for later transmission by the middle child. */ curlvn_out = open_memstream(&job_params->curlvn_buffer_ptr, &job_params->curlvn_buffer_size); local_variable_marshalling(curlvn_out); FCLOSE(curlvn_out, status); /* Force "written" messages into the buffer */ } } fis-gtm-V7.0-005/sr_unix/op_fgnlookup.c0000644000032200000250000000121114342376330016702 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "op.h" error_def(ERR_UNIMPLOP); void op_fgnlookup(void) { RTS_ERROR_ABT(VARLSTCNT(1) ERR_UNIMPLOP); } fis-gtm-V7.0-005/sr_unix/trigger.h0000644000032200000250000005020214342376330015654 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2019 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. * * * ****************************************************************/ #ifndef MUPIP_TRIGGER_INCLUDED #define MUPIP_TRIGGER_INCLUDED /* The order of these must match trigger_subs defined in mtables.c */ typedef enum { #define TRIGGER_SUBSDEF(SUBSTYPE, SUBSNAME, LITMVALNAME, TRIGFILEQUAL, PARTOFHASH) SUBSTYPE, #include "trigger_subs_def.h" #undef TRIGGER_SUBSDEF NUM_TOTAL_SUBS } trig_subs_t; #define NUM_SUBS NUM_TOTAL_SUBS - 2 /* Number of subscripts users deal with. * Hash related subscripts BHASH/LHASH not included. * This assumes BHASH and LHASH are last two entires and is asserted * in "trigger_delete"/"write_out_trigger"/"trigger_update" function entry. */ typedef enum { STATS_ADDED = 0, STATS_DELETED, STATS_MODIFIED, STATS_ERROR_TRIGFILE, STATS_UNCHANGED_TRIGFILE, STATS_NOERROR_TRIGFILE, NUM_STATS } trig_stats_t; #define TRIG_SUCCESS TRUE #define TRIG_FAILURE FALSE #define MAX_COUNT_DIGITS 6 /* Max of 999,999 triggers for a global */ #define MAX_BUFF_SIZE 32768 /* Size of input and output buffers. The cli routines can't handle lines * longer than 32767 + 256 (MAX_LINE -- see cli.h). Longer lines will * be truncated by cli_str_setup(). */ #define MAX_XECUTE_LEN MAX_STRLEN /* Maximum length of the xecute string */ #define COMMENT_LITERAL ';' #define TRIGNAME_SEQ_DELIM '#' #define MAX_GVSUBS_LEN 8192 /* Maximum length of the gvsubs string */ #define MAX_HASH_INDEX_LEN 32768 /* Max length of value for hash index entries */ #define NUM_TRIGNAME_SEQ_CHARS 6 #define MAX_TRIGNAME_LEN (MAX_MIDENT_LEN - 2) /* 2 for runtime characters */ #define MAX_USER_TRIGNAME_LEN MAX_MIDENT_LEN - 3 /* 3 -- 2 for run time chars and 1 for delimiter */ #define MAX_AUTO_TRIGNAME_LEN (MAX_MIDENT_LEN - 4 - NUM_TRIGNAME_SEQ_CHARS) /* 4 -- 2 for runtime characters, 2 for delims */ #define LITERAL_MAXHASHVAL "$" /* '$' collates between '#' and '%' */ #define LITERAL_HASHSEQNUM "#SEQNUM" #define LITERAL_HASHTNAME "#TNAME" #define LITERAL_HASHTNCOUNT "#TNCOUNT" #define TRSBS_IN_NONE 0 #define TRSBS_IN_BHASH 1 #define TRSBS_IN_LHASH 2 #define INITIAL_CYCLE "1" #define XTENDED_START "<<" #define XTENDED_STOP ">>" #define XTENDED_START_LEN STR_LIT_LEN(XTENDED_START) #define XTENDED_STOP_LEN STR_LIT_LEN(XTENDED_STOP) #define PUT_SUCCESS 0 #define INVALID_LABEL -1 #define K_ZTK_CONFLICT -2 #define VAL_TOO_LONG -3 #define KEY_TOO_LONG -4 #define TOO_MANY_TRIGGERS -5 #define ADD_SET_MODIFY_KILL_TRIG -6 #define ADD_SET_NOCHNG_KILL_TRIG -7 #define OPTIONS_CMDS_CONFLICT -8 #define NAME_CMDS_CONFLICT -9 #define AUTO_NAME_GEN_FAIL -10 #define CONV_TO_ZWR(LEN, PTR, OUT_LEN, OUT_STR) \ { \ LEN = MIN(OUT_BUFF_SIZE, LEN); \ format2zwr((sm_uc_ptr_t)PTR, LEN, OUT_STR, &OUT_LEN); \ } #define CONV_TO_ZWR_AND_PRINT(STR, LEN, PTR, OUT_LEN, OUT_STR) \ { \ CONV_TO_ZWR(LEN, PTR, OUT_LEN, OUT_STR); \ util_out_print_gtmio(STR"!AD", FLUSH, OUT_LEN, OUT_STR); \ } #define CONV_STR_AND_PRINT(STR, LEN, PTR) \ { \ int out_len; \ unsigned char out_str[MAX_ZWR_EXP_RATIO * OUT_BUFF_SIZE]; \ \ out_len = MAX_ZWR_EXP_RATIO * OUT_BUFF_SIZE; \ CONV_TO_ZWR_AND_PRINT(STR, LEN, PTR, out_len, out_str); \ } #define CHECK_FOR_ROOM_IN_OUTPUT_AND_COPY(SRC, DST, LEN, MAX_LEN) \ { \ if (MAX_LEN < LEN) \ { \ util_out_print_gtmio("Trigger definition too long", FLUSH); \ return FALSE; \ } \ MAX_LEN -= LEN; \ memcpy(DST, SRC, LEN); \ } /* Build up a comma delimited string */ #define ADD_COMMA_IF_NEEDED(COUNT, PTR, MAX_LEN) \ { \ if (0 != COUNT) \ { \ if (0 > --MAX_LEN) \ { \ util_out_print_gtmio("Trigger definition too long", FLUSH); \ return FALSE; \ } \ MEMCPY_LIT(PTR, ","); \ PTR++; \ } \ } #define ADD_STRING(COUNT, PTR, LEN, COMMAND, MAX_LEN) \ { \ CHECK_FOR_ROOM_IN_OUTPUT_AND_COPY(COMMAND, PTR, LEN, MAX_LEN) \ PTR += LEN; \ COUNT++; \ } #define STR2MVAL(MVAL, STR, LEN) \ { \ MVAL.mvtype = MV_STR; \ MVAL.str.addr = (char *)STR; /* Cast to override "const" attribute */ \ MVAL.str.len = LEN; \ } /* Sets up gv_currkey with ^#t */ #define BUILD_HASHT_CURRKEY_NAME \ { \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ memcpy(gv_currkey->base, HASHT_GBLNAME, HASHT_GBLNAME_LEN); \ gv_currkey->base[HASHT_GBLNAME_LEN] = '\0'; \ gv_currkey->base[HASHT_GBLNAME_FULL_LEN] = '\0'; \ gv_currkey->end = HASHT_GBLNAME_FULL_LEN; \ gv_currkey->prev = 0; \ TREF(gv_last_subsc_null) = FALSE; \ TREF(gv_some_subsc_null) = FALSE; \ } #define BUILD_HASHT_SUB_CURRKEY_T(TRIG_VAL, SUB, LEN) \ { \ boolean_t was_null = FALSE, is_null = FALSE; \ mval *subsc_ptr; \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ BUILD_HASHT_CURRKEY_NAME; \ subsc_ptr = &TRIG_VAL; \ STR2MVAL(TRIG_VAL, SUB, LEN); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ TREF(gv_last_subsc_null) = is_null; \ TREF(gv_some_subsc_null) = was_null; \ } #define BUILD_HASHT_SUB_MSUB_CURRKEY_T(TRIG_VAL, SUB1, LEN1, SUB2) \ { \ boolean_t was_null = FALSE, is_null = FALSE; \ mval *subsc_ptr; \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ BUILD_HASHT_CURRKEY_NAME; \ subsc_ptr = &TRIG_VAL; \ STR2MVAL(TRIG_VAL, SUB1, LEN1); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ subsc_ptr = &SUB2; \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ TREF(gv_last_subsc_null) = is_null; \ TREF(gv_some_subsc_null) = was_null; \ } #define BUILD_HASHT_SUB_SUB_CURRKEY_T(TRIG_VAL, SUB1, LEN1, SUB2, LEN2) \ { \ boolean_t was_null = FALSE, is_null = FALSE; \ mval *subsc_ptr; \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ BUILD_HASHT_CURRKEY_NAME; \ subsc_ptr = &TRIG_VAL; \ STR2MVAL(TRIG_VAL, SUB1, LEN1); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ subsc_ptr = &TRIG_VAL; \ STR2MVAL(TRIG_VAL, SUB2, LEN2); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ TREF(gv_last_subsc_null) = is_null; \ TREF(gv_some_subsc_null) = was_null; \ } #define BUILD_HASHT_SUB_MSUB_MSUB_CURRKEY_T(TRIG_VAL, SUB1, LEN1, SUB2, SUB3) \ { \ boolean_t was_null = FALSE, is_null = FALSE; \ mval *subsc_ptr; \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ BUILD_HASHT_CURRKEY_NAME; \ subsc_ptr = &TRIG_VAL; \ STR2MVAL(TRIG_VAL, SUB1, LEN1); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ subsc_ptr = &SUB2; \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ subsc_ptr = &SUB3; \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ TREF(gv_last_subsc_null) = is_null; \ TREF(gv_some_subsc_null) = was_null; \ } #define BUILD_HASHT_SUB_SUB_MSUB_MSUB_CURRKEY_T(TRIG_VAL, SUB0, LEN0, SUB1, LEN1, SUB2, SUB3) \ { \ boolean_t was_null = FALSE, is_null = FALSE; \ mval *subsc_ptr; \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ BUILD_HASHT_CURRKEY_NAME; \ subsc_ptr = &TRIG_VAL; \ STR2MVAL(TRIG_VAL, SUB0, LEN0); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ STR2MVAL(TRIG_VAL, SUB1, LEN1); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ subsc_ptr = &SUB2; \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ subsc_ptr = &SUB3; \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ TREF(gv_last_subsc_null) = is_null; \ TREF(gv_some_subsc_null) = was_null; \ } #define BUILD_HASHT_SUB_MSUB_SUB_CURRKEY_T(TRIG_VAL, SUB1, LEN1, SUB2, SUB3, LEN3) \ { \ boolean_t was_null = FALSE, is_null = FALSE; \ mval *subsc_ptr; \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ BUILD_HASHT_CURRKEY_NAME; \ subsc_ptr = &TRIG_VAL; \ STR2MVAL(TRIG_VAL, SUB1, LEN1); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ subsc_ptr = &SUB2; \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ subsc_ptr = &TRIG_VAL; \ STR2MVAL(TRIG_VAL, SUB3, LEN3); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ TREF(gv_last_subsc_null) = is_null; \ TREF(gv_some_subsc_null) = was_null; \ } #define BUILD_HASHT_SUB_SUB_MSUB_SUB_CURRKEY_T(TRIG_VAL, SUB0, LEN0, SUB1, LEN1, SUB2, SUB3, LEN3) \ { \ boolean_t was_null = FALSE, is_null = FALSE; \ mval *subsc_ptr; \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ BUILD_HASHT_CURRKEY_NAME; \ subsc_ptr = &TRIG_VAL; \ STR2MVAL(TRIG_VAL, SUB0, LEN0); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ STR2MVAL(TRIG_VAL, SUB1, LEN1); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ subsc_ptr = &SUB2; \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ subsc_ptr = &TRIG_VAL; \ STR2MVAL(TRIG_VAL, SUB3, LEN3); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ TREF(gv_last_subsc_null) = is_null; \ TREF(gv_some_subsc_null) = was_null; \ } #define BUILD_HASHT_SUB_SUB_SUB_CURRKEY_T(TRIG_VAL, SUB1, LEN1, SUB2, LEN2, SUB3, LEN3) \ { \ boolean_t was_null = FALSE, is_null = FALSE; \ mval *subsc_ptr; \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ BUILD_HASHT_CURRKEY_NAME; \ subsc_ptr = &TRIG_VAL; \ STR2MVAL(TRIG_VAL, SUB1, LEN1); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ subsc_ptr = &TRIG_VAL; \ STR2MVAL(TRIG_VAL, SUB2, LEN2); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ STR2MVAL(TRIG_VAL, SUB3, LEN3); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ TREF(gv_last_subsc_null) = is_null; \ TREF(gv_some_subsc_null) = was_null; \ } #define BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY_T(TRIG_VAL, SUB1, LEN1, SUB2, SUB3, LEN3, SUB4) \ { \ boolean_t was_null = FALSE, is_null = FALSE; \ mval *subsc_ptr; \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ BUILD_HASHT_CURRKEY_NAME; \ subsc_ptr = &TRIG_VAL; \ STR2MVAL(TRIG_VAL, SUB1, LEN1); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ subsc_ptr = &SUB2; \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ subsc_ptr = &TRIG_VAL; \ STR2MVAL(TRIG_VAL, SUB3, LEN3); \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ subsc_ptr = &SUB4; \ COPY_SUBS_TO_GVCURRKEY(subsc_ptr, gv_cur_region, gv_currkey, was_null, is_null); \ TREF(gv_last_subsc_null) = is_null; \ TREF(gv_some_subsc_null) = was_null; \ } #define TRIGGER_GLOBAL_ASSIGNMENT_STR(TRIG_VAL, VALUE, LEN, RES) \ { \ STR2MVAL(TRIG_VAL, VALUE, LEN); \ if (gv_currkey->end + 1 > MAX_KEY_SZ - 4) \ RES = KEY_TOO_LONG; \ else \ { \ gvcst_put(&TRIG_VAL); \ RES = PUT_SUCCESS; \ } \ } #define TRIGGER_GLOBAL_ASSIGNMENT_MVAL(VALUE, RES) \ { \ mval *lcl_mv_ptr; \ \ lcl_mv_ptr = &VALUE; \ MV_FORCE_STR(lcl_mv_ptr); \ if (gv_currkey->end + 1 > MAX_KEY_SZ - 4) \ RES = KEY_TOO_LONG; \ else \ { \ gvcst_put(lcl_mv_ptr); \ RES = PUT_SUCCESS; \ } \ } #define BUILD_HASHT_SUB_CURRKEY(SUB, LEN) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_CURRKEY_T(trig_val, SUB, LEN); \ } #define BUILD_HASHT_SUB_MSUB_CURRKEY(SUB1, LEN1, SUB2) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_MSUB_CURRKEY_T(trig_val, SUB1, LEN1, SUB2); \ } #define BUILD_HASHT_SUB_SUB_CURRKEY(SUB1, LEN1, SUB2, LEN2) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_SUB_CURRKEY_T(trig_val, SUB1, LEN1, SUB2, LEN2); \ } #define BUILD_HASHT_SUB_MSUB_MSUB_CURRKEY(SUB1, LEN1, SUB2, SUB3) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_MSUB_MSUB_CURRKEY_T(trig_val, SUB1, LEN1, SUB2, SUB3); \ } #define BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(SUB1, LEN1, SUB2, SUB3, LEN3) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY_T(trig_val, SUB1, LEN1, SUB2, SUB3, LEN3); \ } #define BUILD_HASHT_SUB_SUB_MSUB_SUB_CURRKEY(SUB0, LEN0, SUB1, LEN1, SUB2, SUB3, LEN3) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_SUB_MSUB_SUB_CURRKEY_T(trig_val, SUB0, LEN0, SUB1, LEN1, SUB2, SUB3, LEN3); \ } #define BUILD_HASHT_SUB_SUB_MSUB_MSUB_CURRKEY(SUB0, LEN0, SUB1, LEN1, SUB2, SUB3) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_SUB_MSUB_MSUB_CURRKEY_T(trig_val, SUB0, LEN0, SUB1, LEN1, SUB2, SUB3); \ } #define BUILD_HASHT_SUB_SUB_SUB_CURRKEY(SUB1, LEN1, SUB2, LEN2, SUB3, LEN3) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_SUB_SUB_CURRKEY_T(trig_val, SUB1, LEN1, SUB2, LEN2, SUB3, LEN3); \ } #define BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY(SUB1, LEN1, SUB2, SUB3, LEN3, SUB4) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY_T(trig_val, SUB1, LEN1, SUB2, SUB3, LEN3, SUB4); \ } #define SET_TRIGGER_GLOBAL_SUB_STR(SUB1, LEN1, VALUE, LEN, RES) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_CURRKEY_T(trig_val, SUB, LEN); \ TRIGGER_GLOBAL_ASSIGNMENT_STR(trig_val, VALUE, LEN, RES); \ } #define SET_TRIGGER_GLOBAL_SUB_SUB_STR(SUB1, LEN1, SUB2, LEN2, VALUE, LEN, RES) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_SUB_CURRKEY_T(trig_val, SUB1, LEN1, SUB2, LEN2); \ TRIGGER_GLOBAL_ASSIGNMENT_STR(trig_val, VALUE, LEN, RES); \ } #define SET_TRIGGER_GLOBAL_SUB_MVAL(SUB1, LEN1, VALUE, RES) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_CURRKEY_T(trig_val, SUB1, LEN1); \ TRIGGER_GLOBAL_ASSIGNMENT_MVAL(VALUE, RES); \ } #define SET_TRIGGER_GLOBAL_SUB_SUB_MVAL(SUB1, LEN1, SUB2, LEN2, VALUE, RES) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_SUB_CURRKEY_T(trig_val, SUB1, LEN1, SUB2, LEN2); \ TRIGGER_GLOBAL_ASSIGNMENT_MVAL(VALUE, RES); \ } #define SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(SUB1, LEN1, SUB2, SUB3, LEN3, VALUE, LEN, RES) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY_T(trig_val, SUB1, LEN1, SUB2, SUB3, LEN3); \ TRIGGER_GLOBAL_ASSIGNMENT_STR(trig_val, VALUE, LEN, RES); \ } #define SET_TRIGGER_GLOBAL_SUB_MSUB_MSUB_STR(SUB1, LEN1, SUB2, SUB3, VALUE, LEN, RES) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_MSUB_MSUB_CURRKEY_T(trig_val, SUB1, LEN1, SUB2, SUB3); \ TRIGGER_GLOBAL_ASSIGNMENT_STR(trig_val, VALUE, LEN, RES); \ } #define SET_TRIGGER_GLOBAL_SUB_SUB_MSUB_MSUB_STR(SUB0, LEN0, SUB1, LEN1, SUB2, SUB3, VALUE, LEN, RES) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_SUB_MSUB_MSUB_CURRKEY_T(trig_val, SUB0, LEN0, SUB1, LEN1, SUB2, SUB3); \ TRIGGER_GLOBAL_ASSIGNMENT_STR(trig_val, VALUE, LEN, RES); \ } #define SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MVAL(SUB1, LEN1, SUB2, SUB3, LEN3, VALUE, RES) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY_T(trig_val, SUB1, LEN1, SUB2, SUB3, LEN3); \ TRIGGER_GLOBAL_ASSIGNMENT_MVAL(VALUE, RES); \ } #define SET_TRIGGER_GLOBAL_SUB_SUB_SUB_STR(SUB1, LEN1, SUB2, LEN2, SUB3, LEN3, VALUE, LEN, RES) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_SUB_SUB_CURRKEY_T(trig_val, SUB1, LEN1, SUB2, LEN2, SUB3, LEN3); \ TRIGGER_GLOBAL_ASSIGNMENT_STR(trig_val, VALUE, LEN, RES); \ } #define SET_TRIGGER_GLOBAL_SUB_SUB_SUB_MVAL(SUB1, LEN1, SUB2, LEN2, SUB3, LEN3, VALUE, RES) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_SUB_SUB_CURRKEY_T(trig_val, SUB1, LEN1, SUB2, LEN2, SUB3, LEN3); \ TRIGGER_GLOBAL_ASSIGNMENT_MVAL(VALUE, RES); \ } #define SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MSUB_MVAL(SUB1, LEN1, SUB2, SUB3, LEN3, SUB4, VALUE, RES) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY_T(trig_val, SUB1, LEN1, SUB2, SUB3, LEN3, SUB4); \ TRIGGER_GLOBAL_ASSIGNMENT_MVAL(VALUE, RES); \ } #define SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MSUB_STR(SUB1, LEN1, SUB2, SUB3, LEN3, SUB4, VALUE, LEN, RES) \ { \ mval trig_val; \ \ BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY_T(trig_val, SUB1, LEN1, SUB2, SUB3, LEN3, SUB4); \ TRIGGER_GLOBAL_ASSIGNMENT_STR(trig_val, VALUE, LEN, RES); \ } #define INT2STR(INT, STR) \ { \ char *lcl_ptr; \ int num_len; \ \ lcl_ptr = STR; \ num_len = 0; \ I2A(lcl_ptr, num_len, INT); \ assert(MAX_DIGITS_IN_INT >= num_len); \ *(lcl_ptr + num_len) = '\0'; \ } /* If this is the first call of this macro inside a function, then note down whatever is in the util_output buffer. * This is the prefix that is already printed for the first "util_out_print_gtmio" call. For the second and future * calls of the "util_out_print_gtmio" function inside the current function, print the noted down prefix first. * This gives more user-friendly output (e.g. deleting wildcard triggers by name prints same Line # in each trigger * that gets deleted). */ #define UTIL_PRINT_PREFIX_IF_NEEDED(FIRST_GTMIO, UTILPREFIX, UTILPREFIXLEN) \ { \ boolean_t ret; \ \ if (FIRST_GTMIO) \ { \ ret = util_out_save(UTILPREFIX, UTILPREFIXLEN); \ assert(ret); /* assert we have space to save prefix in UTILPREFIX */ \ /* If there was no space to save the prefix, print second and later calls of \ * "util_out_print_gtmio" without the prefix in pro. \ */ \ if (ret) \ FIRST_GTMIO = FALSE; \ } else \ util_out_print_gtmio("!AD", NOFLUSH_OUT, *UTILPREFIXLEN, UTILPREFIX); \ } #define SET_DISP_TRIGVN(REG, DISP_TRIGVN, DISP_TRIGVN_LEN, TRIGVN, TRIGVN_LEN) \ { \ memcpy(DISP_TRIGVN, TRIGVN, TRIGVN_LEN); \ DISP_TRIGVN_LEN = TRIGVN_LEN; \ MEMCPY_LIT(&DISP_TRIGVN[DISP_TRIGVN_LEN], SPANREG_REGION_LIT); \ DISP_TRIGVN_LEN += SPANREG_REGION_LITLEN; \ memcpy(&DISP_TRIGVN[DISP_TRIGVN_LEN], REG->rname, REG->rname_len); \ DISP_TRIGVN_LEN += REG->rname_len; \ DISP_TRIGVN[DISP_TRIGVN_LEN++] = ')'; \ assert(DISP_TRIGVN_LEN < ARRAYSIZE(DISP_TRIGVN)); \ DISP_TRIGVN[DISP_TRIGVN_LEN] = '\0'; /* null terminate just in case */ \ } #endif /* MUPIP_TRIGGER_INCLUDED */ fis-gtm-V7.0-005/sr_unix/op_fnextract.c0000755000032200000250000000131414342376330016703 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2018 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 "op.h" #include "gtm_utf8.h" #ifdef UTF8_SUPPORTED # include "utfcgr.h" #else # include "utfcgr_trc.h" #endif #define OP_FNEXTRACT op_fnextract #include "op_fnextract.h" fis-gtm-V7.0-005/sr_unix/op_fnextract.h0000644000032200000250000002063314342376330016712 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2008-2018 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. * * * ****************************************************************/ /* Note this routine is built as op_fnextract() on all but IA64 platforms * where it is instead built as op_fnextract2() due to linkage requirements * where values in the transfer table must be consistently assembler or * "C" for the correct call signature to be made. Since this routine is * changed in the transfer table under certain conditions (UTF8 or not), * the interface needed to be consistent so an assembler stub is made to * call this C routine to match the alternate op_fnzextract that could be * in that transfer table slot. * * Parameters: * * last - last character position to extract from source string. * first - first character position to extract from source string. * src - the actual source string. * dest - destination mval for extracted string. * * Note src and dest can be the same mval in certain situations like "SET X=$EXTRACT(X,2,5)" * so dest fields should only be modified once we no longer need the same src field. */ DBGUTFC_ONLY(STATICDEF uint4 xtrctcnt;) GBLREF boolean_t badchar_inhibit; GBLREF boolean_t gtm_utf8_mode; void OP_FNEXTRACT(int last, int first, mval *src, mval *dest) { char *srcbase, *srctop, *srcptr; unsigned char *cptr, *cstart, *ctop; int len, skip, bytelen, clen, first_charcnt; # ifdef UTF8_SUPPORTED utfscan_parseblk utf_parse_blk; DEBUG_ONLY(utfscan_parseblk utf_parse_blk_save;) boolean_t success, found_start, utf_parse_blk_setup; # endif DCL_THREADGBL_ACCESS; if (!gtm_utf8_mode) { /* Might be called directly from compiler with no check for utf8 mode or not */ op_fnzextract(last, first, src, dest); return; } SETUP_THREADGBL_ACCESS; assert(!TREF(compile_time) || valid_utf_string(&src->str)); MV_FORCE_STR(src); if (0 >= first) first = 1; else if (first > src->str.len) { dest->mvtype = MV_STR; dest->str.len = 0; return; } if ((0 < last) && (last > src->str.len)) last = (src->mvtype & MV_UTF_LEN) ? src->str.char_len : src->str.len; if (MV_IS_SINGLEBYTE(src)) { /* Fast-path extraction of an entirely single byte string (char_len == len) */ DBGUTFC((stderr, "\nop_fnextract(%d): M mode extraction - mval: 0x"lvaddr" mval->str.len: %d " "mval->str.addr: 0x"lvaddr" first: %d last: %d\n", ++xtrctcnt, src, src->str.len, src->str.addr, first, last)); if (0 < (len = last - first + 1)) /* Note assignment */ { cstart = (unsigned char *)(src->str.addr + first - 1); if (!badchar_inhibit) { /* We need to care about whether there are any BADCHARs in this string so run through them * right quick and look for non-ASCII chars since we already know these are all single-byte * characters (no multi-byte characters). Since we'll know char_len, set that too. */ cptr = cstart; ctop = cstart + len; for (clen = 0; cptr < ctop; ++clen, cptr++) { if (ASCII_MAX < *cptr) UTF8_BADCHAR(0, cptr, ctop, 0, NULL); } assert(clen == len); dest->str.char_len = clen; dest->mvtype = MV_STR | MV_UTF_LEN; } else dest->mvtype = MV_STR; dest->str.addr = (char *)cstart; dest->str.len = len; } else { dest->mvtype = MV_STR; dest->str.len = 0; } DBGUTFC((stderr, "op_fnextract(%d): Return value: length %d string: %.*s\n", xtrctcnt, dest->str.len, dest->str.len, dest->str.addr)); } else { /* Generic extraction of a multi-byte string or UTF8 mode and length unknown */ # ifdef UTF8_SUPPORTED utf_parse_blk_setup = FALSE; if (0 >= (last - first + 1)) { /* first is > last - return NULL string */ dest->mvtype = MV_STR; dest->str.len = 0; return; } srcbase = src->str.addr; srctop = srcbase + src->str.len; DBGUTFC((stderr, "\nop_fnextract(%d): UTF mode extraction - mval: 0x"lvaddr" mval->str.len: %d " "mval->str.addr: 0x"lvaddr" first: %d last: %d\n", ++xtrctcnt, src, src->str.len, src->str.addr, first, last)); /* Locate starting extract position */ if (1 < first) { utf_parse_blk.mv = src; utf_parse_blk.stoponbadchar = !badchar_inhibit; utf_parse_blk.scan_byte_offset = 0; /* Start at first char */ utf_parse_blk_setup = TRUE; success = utfcgr_scanforcharN(first, &utf_parse_blk); DBGUTFC((stderr, "op_fnextract(%d): Return from utfcgr_scanforcharN(1st): success: %d" " utf_parse_blk.scan_byte_offset: %d utf_parse_blk.scan_char_count: %d" " utf_parse_blk.scan_char_len: %d utf_parse_blk.scan_char_type: %d\n", xtrctcnt, success, utf_parse_blk.scan_byte_offset, utf_parse_blk.scan_char_count, utf_parse_blk.scan_char_len, utf_parse_blk.scan_char_type)); if (success) { /* Scan succeeded - found starting place */ found_start = TRUE; srcptr = src->str.addr + utf_parse_blk.scan_byte_offset; } else { /* Scan failed - find out why */ found_start = FALSE; /* Didn't find starting char */ if (UTFCGR_EOL == utf_parse_blk.scan_char_type) /* If ran out of chars before finding Nth.. */ { /* Return 0 if character position exceeds chars available */ dest->mvtype = MV_STR; dest->str.len = 0; return; } else if ((UTFCGR_BADCHAR == utf_parse_blk.scan_char_type) && !badchar_inhibit) /* Ran into a badchar that was not ignorable - no return */ UTF8_BADCHAR(0, utf_parse_blk.badcharstr, utf_parse_blk.badchartop, 0, NULL); else assertpro(FALSE); /* Unknown error - no return */ } first_charcnt = utf_parse_blk.scan_char_count; /* Save char count for "first" */ } else { srcptr = srcbase; first_charcnt = 0; } assert(srcptr <= srctop); srcbase = srcptr; /* Now locate the last character of the extract */ if (utf_parse_blk_setup) { DEBUG_ONLY(utf_parse_blk_save = utf_parse_blk); /* Save first return for debugging */ } else { /* We didn't use utf_parse_blk to find first char so set it up now */ utf_parse_blk.mv = src; utf_parse_blk.stoponbadchar = !badchar_inhibit; utf_parse_blk.scan_byte_offset = 0; /* Start at first char */ } success = utfcgr_scanforcharN(last, &utf_parse_blk); DBGUTFC((stderr, "op_fnextract(%d): Return from utfcgr_scanforcharN(end): success: %d" " utf_parse_blk.scan_byte_offset: %d utf_parse_blk.scan_char_count: %d utf_parse_blk.scan_char_len:" " %d utf_parse_blk.scan_char_type: %d\n", xtrctcnt, success, utf_parse_blk.scan_byte_offset, utf_parse_blk.scan_char_count, utf_parse_blk.scan_char_len, utf_parse_blk.scan_char_type)); if (!success) { /* Scan failed - find out why */ found_start = FALSE; if (UTFCGR_EOL == utf_parse_blk.scan_char_type) { /* This is possibly due to $E(str,99999) sort of thing so we ran out of characters to scan. * Pretend scan worked and let it pickup the scan termination values but in this case of EOL * (end of line), we compute character length differently since the scan ended AFTER the * last byte of the string instead of the char before the one we were looking for. */ dest->str.char_len = utf_parse_blk.scan_char_count - first_charcnt; } else if ((UTFCGR_BADCHAR == utf_parse_blk.scan_char_type) && !badchar_inhibit) /* Ran into a badchar that was not ignorable - no return */ UTF8_BADCHAR(0, utf_parse_blk.badcharstr, utf_parse_blk.badchartop, 0, NULL); else assertpro(FALSE); /* Unknown error - no return */ } else { /* Since utf_parse_blk.scan_char_count returns the length prior to the last character, we have to add 1 * to the length. Then part of the difference is also to add one so we add another one. */ dest->str.char_len = (utf_parse_blk.scan_char_count - first + 1) + 1; } srcptr = src->str.addr + utf_parse_blk.scan_byte_offset + utf_parse_blk.scan_char_len; assert(srcptr <= srctop); dest->mvtype = MV_STR; dest->str.addr = srcbase; dest->str.len = INTCAST(srcptr - srcbase); dest->mvtype |= MV_UTF_LEN; DBGUTFC((stderr, "op_fnextract(%d): Return value: length %d string: %.*s\n", xtrctcnt, dest->str.len, dest->str.len, dest->str.addr)); # else assertpro(FALSE); /* Shouldn't be here if not supported */ # endif /* UTF8_SUPPORTED */ } } fis-gtm-V7.0-005/sr_unix/op_fnfgncal.c0000644000032200000250000013017414342376335016474 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include #include #ifdef GTM_PTHREAD # include "gtm_pthread.h" #endif #include "gtm_stdlib.h" #include "stringpool.h" #include "copy.h" #include #include "stack_frame.h" #include "op.h" #include "lv_val.h" /* needed for "fgncal.h" */ #include "fgncal.h" #include "gtmci.h" #include "gtmxc_types.h" #include "mvalconv.h" #include "gt_timer.h" #include "callg.h" #include "callintogtmxfer.h" #include "min_max.h" #include "have_crit.h" #include "gtmdbglvl.h" /* for gtm_malloc.h */ #include "gtm_malloc.h" /* for VERIFY_STORAGE_CHAINS */ #include "send_msg.h" /****************************************************************************** * * This routine is called by generated code with the following arguments: * * n argument count (int4) * dst destination (mval *), that is, result for extrinsic function call * null pointer means that this is a routine call * package package name (mval *) * extref external reference name (mval *) * mask call-by refrence mask (int4) * If bit=1, then call by reference, * otherwise, call by value. * lsb is first argument, etc. * Implies all call-by-reference arguments must be in the first 32 arguments. * argcnt argument count (int4). Number of input parameters. * always equal to argument count n - 5 * arg0 the first input paramter (mval *) * arg1 the second input parameter (mval *) * ... ... * argi the last input parameter, i = argcnt - 1 * * example: * d &mathpak.bessel(p1,p2,.p3) * * would result in the following call: * * op_fnfgncal(8, 0, mval*("mathpak"), mval*("bessel"), 4, 3, mval*(p1), mval*(p2), mval*(p3)) * * where, mval*(val) indicates the address of an mval that has the string value val. * * If the extref has a routine name, than the extref looks like an M source * reference. E.g.: * s x=&$foo^bar * entryref-->"foo.bar"* * * * This routine is platform independent (at least for UNIX). * It requires two platform specific routines: * * lookup package * Return package_handle if success, NULL otherwise. * * void * fgn_getpak(char *package_name, int error_severity) * { * lookup package * If not found, return NULL; * store any system specific information in package_handle * return package_handle; * } * * lookup function * return function address if success, NULL otherwise * * typedef int (*fgnfnc)(); * * fgnfnc fgn_getrtn(void *package_handle, mstr *entry_name) * { * lookup entry_name * If not found return NULL; * return function address; * } * ******************************************************************************/ GBLREF stack_frame *frame_pointer; GBLREF unsigned char *msp; GBLREF spdesc stringpool; GBLREF int (*callintogtm_vectortable[])(); GBLREF int mumps_status; GBLREF volatile int4 gtmMallocDepth; #ifdef GTM_PTHREAD GBLREF boolean_t gtm_jvm_process; GBLREF pthread_t gtm_main_thread_id; GBLREF boolean_t gtm_main_thread_id_set; #endif LITREF mval literal_null; LITREF mval skiparg; error_def(ERR_JNI); error_def(ERR_MAXSTRLEN); error_def(ERR_TEXT); error_def(ERR_UNIMPLOP); error_def(ERR_XCVOIDRET); error_def(ERR_ZCARGMSMTCH); error_def(ERR_ZCCONVERT); error_def(ERR_ZCCTENV); error_def(ERR_ZCMAXPARAM); error_def(ERR_ZCNOPREALLOUTPAR); error_def(ERR_ZCRTENOTF); error_def(ERR_ZCSTATUSRET); error_def(ERR_ZCUSRRTN); error_def(ERR_ZCVECTORINDX); error_def(ERR_XCRETNULLREF); error_def(ERR_EXTCALLBOUNDS); error_def(ERR_EXCEEDSPREALLOC); STATICDEF int call_table_initialized = 0; /* The following are one-letter mnemonics for Java argument types (capital letters to denote output direction): * boolean int long float double String byte[] */ STATICDEF char gtm_jtype_chars[] = { 'b', 'i', 'l', 'f', 'd', 'j', 'a', 'B', 'I', 'L', 'F', 'D', 'J', 'A' }; STATICDEF int gtm_jtype_start_idx = gtm_jboolean, /* Value of first gtm_j... type for calculation of table indices. */ gtm_jtype_count = gtm_jbyte_array - gtm_jboolean + 1; /* Number of types supported with Java call-outs. */ STATICFNDCL void extarg2mval(void *src, enum gtm_types typ, mval *dst, boolean_t java, boolean_t starred, int prealloc_size, char *m_label, gparam_list *ext_buff_start, int4 ext_buff_len); STATICFNDCL int extarg_getsize(void *src, enum gtm_types typ, mval *dst, struct extcall_entry_list *entry_ptr); STATICFNDCL void op_fgnjavacal(mval *dst, mval *package, mval *extref, uint4 mask, int4 argcnt, int4 entry_argcnt, struct extcall_package_list *package_ptr, struct extcall_entry_list *entry_ptr, va_list var); STATICFNDCL gparam_list *set_up_buffer(char *p_list, int len); STATICFNDCL void verify_buffer(char *p_list, int len, char *m_label); STATICFNDCL void free_return_type(INTPTR_T ret_val, enum gtm_types typ); static const int buff_boarder_len = 7; static const char *buff_front_boarder = "SMARKER"; static const char *buff_end_boarder = "EMARKER"; /* If the assigned pointer value is NULL, pass back a literal_null */ #define VALIDATE_AND_CONVERT_PTR_TO_TYPE(LMVTYPE, TYPE, CONTAINER, DST, SRC) \ MBSTART { \ if (NULL == SRC) \ *DST = literal_null; \ else \ { \ CONTAINER = *((TYPE *)SRC); \ MV_FORCE_##LMVTYPE(DST, CONTAINER); \ } \ } MBEND /* if FLAG is false, set it to true and syslog it once */ #define ISSUE_ERR_ONCE(FLAG, ...) \ { \ if (!FLAG) \ { \ FLAG = TRUE; \ send_msg_csa(__VA_ARGS__); \ } \ } /* Routine to set up boarders around the external call buffer */ STATICFNDCL gparam_list *set_up_buffer(char *p_list, int len) { memcpy(p_list, buff_front_boarder, buff_boarder_len); memcpy((p_list + len + buff_boarder_len), buff_end_boarder, buff_boarder_len); return (gparam_list *)(p_list + buff_boarder_len); } STATICFNDCL void verify_buffer(char *p_list, int len, char *m_label) { if ((0 != memcmp((p_list + len), buff_end_boarder, buff_boarder_len)) || (0 != memcmp((p_list - buff_boarder_len), buff_front_boarder, buff_boarder_len))) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_EXTCALLBOUNDS, 1, m_label); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_EXTCALLBOUNDS, 1, m_label); } VERIFY_STORAGE_CHAINS; } STATICFNDCL void free_return_type(INTPTR_T ret_val, enum gtm_types typ) { if (0 == ret_val) /* Nothing to free regardless of type */ return; switch (typ) { case gtm_jstring: case gtm_jbyte_array: free((struct extcall_string *)ret_val); break; case gtm_notfound: case gtm_void: case gtm_status: case gtm_int: case gtm_uint: case gtm_long: case gtm_ulong: case gtm_float: case gtm_double: case gtm_pointertofunc: case gtm_pointertofunc_star: case gtm_jboolean: case gtm_jint: case gtm_jlong: case gtm_jfloat: case gtm_jdouble: case gtm_jbig_decimal: break; case gtm_string_star: if (NULL != (((gtm_string_t *)ret_val)->address)) free((((gtm_string_t *)ret_val)->address)); free(((gtm_string_t *)ret_val)); break; case gtm_float_star: free(((gtm_float_t *)ret_val)); break; case gtm_double_star: free(((gtm_double_t *)ret_val)); break; case gtm_int_star: free(((gtm_int_t *)ret_val)); break; case gtm_uint_star: free(((gtm_uint_t *)ret_val)); break; case gtm_long_star: free(((gtm_long_t *)ret_val)); break; case gtm_ulong_star: free(((gtm_ulong_t *)ret_val)); break; case gtm_char_star: free(((gtm_char_t *)ret_val)); break; case gtm_char_starstar: if (NULL != *((gtm_char_t **)ret_val)) free(*((gtm_char_t **)ret_val)); free(((gtm_char_t **)ret_val)); break; } } /* Routine to convert external return values to mval's */ STATICFNDEF void extarg2mval(void *src, enum gtm_types typ, mval *dst, boolean_t java, boolean_t starred, int prealloc_size, char *m_label, gparam_list *ext_buff_start, int4 ext_buff_len) { gtm_int_t s_int_num; gtm_long_t str_len, s_long_num; gtm_uint_t uns_int_num; gtm_ulong_t uns_long_num; char *cp; struct extcall_string *sp; if (java) { switch (typ) { case gtm_notfound: break; case gtm_void: break; case gtm_status: /* Note: reason for double cast is to first turn ptr to same sized int, then big int to little int * (on 64 bit platforms). This avoids a warning msg with newer 64 bit gcc compilers. */ s_int_num = (gtm_int_t)(intszofptr_t)src; if (0 != s_int_num) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZCSTATUSRET); MV_FORCE_MVAL(dst, s_int_num); break; case gtm_jboolean: case gtm_jint: if (starred) s_int_num = *((gtm_int_t *)src); else s_int_num = (gtm_int_t)(intszofptr_t)src; MV_FORCE_MVAL(dst, s_int_num); break; case gtm_jlong: # ifdef GTM64 if (starred) s_long_num = *((gtm_long_t *)src); else s_long_num = (gtm_long_t)src; MV_FORCE_LMVAL(dst, s_long_num); # else i82mval(dst, *(gtm_int64_t *)src); # endif break; case gtm_jfloat: float2mval(dst, *((float *)src)); break; case gtm_jdouble: double2mval(dst, *((double *)src)); break; case gtm_jstring: case gtm_jbyte_array: sp = (struct extcall_string *)src; dst->mvtype = MV_STR; if (sp->len > MAX_STRLEN) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXSTRLEN); if ((0 < sp->len) && (NULL != sp->addr)) { dst->str.len = (mstr_len_t)sp->len; dst->str.addr = sp->addr; s2pool(&dst->str); /* In case of GTMByteArray or GTMString, the buffer is allocated in xc_gateway.c (since the * user might need to return a bigger buffer than the original array size will allow (in * case of a string the content is immutable). So, if we have determined that the provided * value buffer is legitimate (non-null and non-empty), we free it on the GT.M side. */ free(sp->addr); } else *dst = literal_null; break; default: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); break; } return; } /* The following switch is for non-Java call-outs. */ switch (typ) { case gtm_notfound: break; case gtm_void: break; case gtm_status: /* Note: reason for double cast is to first turn ptr to same sized int, then big int to little int * (on 64 bit platforms). This avoids a warning msg with newer 64 bit gcc compilers. */ s_int_num = (gtm_int_t)(intszofptr_t)src; if (0 != s_int_num) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZCSTATUSRET); MV_FORCE_MVAL(dst, s_int_num); break; case gtm_int: s_int_num = (gtm_int_t)(intszofptr_t)src; MV_FORCE_MVAL(dst, s_int_num); break; case gtm_uint: uns_int_num = (gtm_uint_t)(intszofptr_t)src; MV_FORCE_UMVAL(dst, uns_int_num); break; case gtm_int_star: VALIDATE_AND_CONVERT_PTR_TO_TYPE(MVAL, gtm_int_t, s_int_num, dst, src); break; case gtm_uint_star: VALIDATE_AND_CONVERT_PTR_TO_TYPE(UMVAL, gtm_uint_t, uns_int_num, dst, src); break; case gtm_long: s_long_num = (gtm_long_t)src; MV_FORCE_LMVAL(dst, s_long_num); break; case gtm_ulong: uns_long_num = (gtm_ulong_t)src; MV_FORCE_ULMVAL(dst, uns_long_num); break; case gtm_long_star: VALIDATE_AND_CONVERT_PTR_TO_TYPE(LMVAL, gtm_long_t, s_long_num, dst, src); break; case gtm_ulong_star: VALIDATE_AND_CONVERT_PTR_TO_TYPE(ULMVAL, gtm_ulong_t, uns_long_num, dst, src); break; case gtm_string_star: sp = (struct extcall_string *)src; if (NULL == sp) /* If the assigned pointer value is NULL, pass back a literal_null */ *dst = literal_null; else { dst->mvtype = MV_STR; if (sp->len > MAX_STRLEN) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXSTRLEN); if ((0 <= prealloc_size) && (sp->len > prealloc_size) && (sp->addr >= (char *)ext_buff_start) && (sp->addr < ((char *)ext_buff_start + ext_buff_len))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_EXCEEDSPREALLOC, 3, prealloc_size, m_label, sp->len); if ((0 < sp->len) && (NULL != sp->addr)) { dst->str.len = (mstr_len_t)sp->len; dst->str.addr = sp->addr; s2pool(&dst->str); } else *dst = literal_null; } break; case gtm_float_star: if (NULL == src) /* If the assigned pointer value is NULL, pass back a literal_null */ *dst = literal_null; else float2mval(dst, *((float *)src)); break; case gtm_char_star: cp = (char *)src; if (NULL == cp) /* If the assigned pointer value is NULL, pass back a literal_null */ *dst = literal_null; else { assert(((INTPTR_T)cp < (INTPTR_T)stringpool.base) || ((INTPTR_T)cp > (INTPTR_T)stringpool.top)); dst->mvtype = MV_STR; str_len = STRLEN(cp); if (str_len > MAX_STRLEN) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXSTRLEN); if ((0 <= prealloc_size) && (str_len > prealloc_size)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_EXCEEDSPREALLOC, 3, prealloc_size, m_label, str_len); dst->str.len = (mstr_len_t)str_len; dst->str.addr = cp; s2pool(&dst->str); } break; case gtm_char_starstar: if (NULL == src) /* If the assigned pointer value is NULL, pass back a literal_null */ *dst = literal_null; else extarg2mval(*((char **)src), gtm_char_star, dst, java, starred, prealloc_size, m_label, ext_buff_start, ext_buff_len); break; case gtm_double_star: if (NULL == src) /* If the assigned pointer value is NULL, pass back a literal_null */ *dst = literal_null; else double2mval(dst, *((double *)src)); break; default: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); break; } return; } /* Subroutine to calculate stringpool requirements for an external argument */ STATICFNDEF int extarg_getsize(void *src, enum gtm_types typ, mval *dst, struct extcall_entry_list *entry_ptr) { char *cp, **cpp; struct extcall_string *sp; static bool issued_ERR_XCRETNULLREF = FALSE; static bool issued_ERR_ZCCONVERT = FALSE; if (!src) switch (typ) { case gtm_notfound: case gtm_void: case gtm_status: case gtm_int: case gtm_uint: case gtm_long: case gtm_ulong: case gtm_jboolean: case gtm_jint: case gtm_jlong: case gtm_jfloat: case gtm_jdouble: return 0; default: /* Handle null pointer return types */ ISSUE_ERR_ONCE(issued_ERR_XCRETNULLREF, CSA_ARG(NULL) VARLSTCNT(4) ERR_XCRETNULLREF, 2, LEN_AND_STR(entry_ptr->call_name.addr)) return 0; break; } switch (typ) { /* The following group of cases either return nothing or use the numeric part of the mval */ case gtm_notfound: case gtm_void: case gtm_double_star: case gtm_status: case gtm_int: case gtm_uint: case gtm_long: case gtm_ulong: case gtm_float_star: case gtm_int_star: case gtm_uint_star: case gtm_long_star: case gtm_ulong_star: case gtm_jboolean: case gtm_jint: case gtm_jlong: case gtm_jfloat: case gtm_jdouble: return 0; case gtm_char_starstar: cpp = (char **)src; if (NULL == (*cpp)) { ISSUE_ERR_ONCE(issued_ERR_XCRETNULLREF, CSA_ARG(NULL) VARLSTCNT(4) ERR_XCRETNULLREF, 2, LEN_AND_STR(entry_ptr->call_name.addr)) return 0; } return STRLEN(*cpp); case gtm_char_star: cp = (char *)src; return STRLEN(cp); case gtm_jstring: case gtm_jbyte_array: case gtm_string_star: sp = (struct extcall_string *)src; if (NULL == sp->addr) { sp->len = 0; ISSUE_ERR_ONCE(issued_ERR_XCRETNULLREF, CSA_ARG(NULL) VARLSTCNT(4) ERR_XCRETNULLREF, 2, LEN_AND_STR(entry_ptr->call_name.addr)) } if (0 > sp->len) { /* Negative string length. syslog and reset to zero */ sp->len = 0; ISSUE_ERR_ONCE(issued_ERR_ZCCONVERT, CSA_ARG(NULL) VARLSTCNT(4) ERR_ZCCONVERT, 2, LEN_AND_STR(entry_ptr->call_name.addr)) } else assert(!(((INTPTR_T)sp->addr < (INTPTR_T)stringpool.free) && ((INTPTR_T)sp->addr >= (INTPTR_T)stringpool.base))); return (int)(sp->len); break; default: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); break; } return 0; /* This should never get executed, added to make compiler happy */ } STATICFNDEF void op_fgnjavacal(mval *dst, mval *package, mval *extref, uint4 mask, int4 argcnt, int4 entry_argcnt, struct extcall_package_list *package_ptr, struct extcall_entry_list *entry_ptr, va_list var) { boolean_t error_in_xc = FALSE; char *free_string_pointer, *free_string_pointer_start, jtype_char; char str_buffer[MAX_NAME_LENGTH], *tmp_buff_ptr, *jni_err_buf; char *types_descr_ptr, *types_descr_dptr, *xtrnl_table_name; gparam_list *param_list; gtm_long_t *free_space_pointer; int i, j, save_mumps_status; int4 m1, m2, n, space_n, call_buff_size; INTPTR_T status; mval *v; va_list var_copy; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # ifdef GTM_PTHREAD gtm_jvm_process = TRUE; if (!gtm_main_thread_id_set) { gtm_main_thread_id = pthread_self(); gtm_main_thread_id_set = TRUE; } # endif /* This is how many parameters we will use for callg, including the implicit ones described below. So, better make * sure we are not trying to pass more than callg can handle. */ if (MAX_ACTUALS < argcnt + 3) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZCMAXPARAM); VAR_COPY(var_copy, var); /* Compute size of parameter block */ n = entry_ptr->parmblk_size + (3 * SIZEOF(void *)); /* This is enough for the parameters and the fixed length entries */ mask >>= 2; /* Bypass the class and method arguments. */ /* The first byte of the first argument (types_descr_ptr) stores the number of expected arguments, according to the external * calls table; the second byte describes the expected return type, and subsequent bytes---the expected argument types using * a one-character mnemonic which gets deciphered in the JNI layer. Note that in case of an error the xc_gateway.c module * would set the first byte to 0xFF, followed by an address at which the error message is stored. For that reason, we * allocate more space than we might strictly need for the arguments, return type, and type descriptor. */ types_descr_ptr = (char *)malloc(MAX(SIZEOF(char) * (argcnt + 2), SIZEOF(char *) * 2)); /* zero the contents for unmodified output values */ memset(types_descr_ptr, 0, MAX(SIZEOF(char) * (argcnt + 2), SIZEOF(char *) * 2)); types_descr_dptr = types_descr_ptr; *types_descr_dptr = (char)entry_argcnt; types_descr_dptr++; if (dst) { /* Record the expected return type. */ switch (entry_ptr->return_type) { case gtm_status: *types_descr_dptr = 'i'; break; case gtm_jlong: *types_descr_dptr = 'l'; break; case gtm_jfloat: *types_descr_dptr = 'b'; break; case gtm_jdouble: *types_descr_dptr = 'd'; break; case gtm_jstring: *types_descr_dptr = 'j'; break; case gtm_jbyte_array: *types_descr_dptr = 'a'; break; default: *types_descr_dptr = 'v'; } } else *types_descr_dptr = 'v'; types_descr_dptr++; assert(2 * gtm_jtype_count == SIZEOF(gtm_jtype_chars)); for (i = argcnt + 2, j = -2, m1 = entry_ptr->input_mask, m2 = entry_ptr->output_mask, space_n = 0; 0 < i; i--, j++) { /* Enforce mval values and record expected argument types. */ v = va_arg(var, mval *); if (0 > j) { MV_FORCE_STR(v); n += SIZEOF(void *) + v->str.len + 1; continue; } if (MASK_BIT_ON(m1)) { MV_FORCE_DEFINED_UNLESS_SKIPARG(v); } /* Estimate how much allocation we are going to need for arguments which require more than pointer-size space. */ if (MASK_BIT_ON(m1) && ((gtm_jstring == entry_ptr->parms[j]) || (gtm_jbyte_array == entry_ptr->parms[j]))) { if (MV_DEFINED(v)) { MV_FORCE_STR(v); n += SIZEOF(gtm_long_t) + v->str.len + 1; /* length + string + '\0' */ } else n += SIZEOF(gtm_long_t) + 1; /* length + '\0' */ } # ifndef GTM64 else if ((gtm_jdouble == entry_ptr->parms[j]) || (gtm_jlong == entry_ptr->parms[j])) { /* Account for potential 8-byte alignment on 32-bit boxes */ n += SIZEOF(gtm_int64_t); space_n += SIZEOF(gtm_int64_t); } # endif jtype_char = entry_ptr->parms[j] - gtm_jtype_start_idx; if ((0 > jtype_char) || (gtm_jtype_count <= jtype_char)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); else *types_descr_dptr = gtm_jtype_chars[MASK_BIT_ON(m2) ? (gtm_jtype_count + jtype_char) : jtype_char]; types_descr_dptr++; m1 >>= 1; m2 >>= 1; } va_end(var); /* Allocate space for argument handling. Overall, the allocated space has the following structure: * ___________________________________________ * | | | | * | param_list | space buffer | string buffer | * |____________|______________|_______________| * * All input-output and output-only parameters have to be passed by reference, which means that param_list contains a * pointer to the space buffer where the actual value is stored. Furthermore, in case of gtm_jstring_t and gtm_jbyte_array_t * another pointer from within the space buffer is referencing an area inside the string buffer. Note, however, that certain * arguments, such as gtm_jfloat_t and gtm_jdouble_t, and gtm_jlong_t on 32-bit boxes, are always passed by reference. */ /* Note that this is the nominal buffer size; or, explicitly, the size of buffer without the proctection tags*/ call_buff_size = n; char param_list_buff[(call_buff_size + 2*buff_boarder_len)]; param_list = set_up_buffer(param_list_buff, n); param_list->arg[0] = (void *)types_descr_ptr; /* Adding 3 to account for type descriptions, class name, and method name arguments. */ free_space_pointer = (gtm_long_t *)((char *)param_list + SIZEOF(intszofptr_t) + (SIZEOF(void *) * (argcnt + 3))); /* Adding 3 for the same reason as above and another 3 to account for the fact that each of type description, class name, * and method name arguments require room in the free_space buffer, which comes ahead of free_string buffer in memory. */ free_string_pointer_start = free_string_pointer = (char *)param_list + entry_ptr->parmblk_size + (SIZEOF(void *) * 3) + space_n; /* Load-up the parameter list */ VAR_COPY(var, var_copy); /* We need to enter this loop even if argcnt == 0, so that the class and method arguments get set. */ for (i = (0 == argcnt ? -1 : 0), j = 1, m1 = entry_ptr->input_mask, m2 = entry_ptr->output_mask; i < argcnt; j++) { v = va_arg(var_copy, mval *); if (j < 3) { param_list->arg[j] = free_string_pointer; if (v->str.len) { memcpy(free_string_pointer, v->str.addr, v->str.len); free_string_pointer += v->str.len; } *free_string_pointer++ = '\0'; /* In case there are 0 arguments. */ if ((2 == j) && (0 > i)) i = 0; continue; } /* Verify that all input values are defined. */ switch (entry_ptr->parms[i]) { case gtm_jboolean: if (MASK_BIT_ON(m2)) { /* Output expected. */ param_list->arg[j] = free_space_pointer; *((gtm_int_t *)free_space_pointer) = (gtm_int_t)(MV_ON(m1, v) ? (mval2i(v) ? 1 : 0) : 0); free_space_pointer++; } else /* Input expected. */ param_list->arg[j] = (void *)(gtm_long_t)(MV_ON(m1, v) ? (mval2i(v) ? 1 : 0) : 0); break; case gtm_jint: if (MASK_BIT_ON(m2)) { /* Output expected. */ param_list->arg[j] = free_space_pointer; *((gtm_int_t *)free_space_pointer) = (gtm_int_t)(MV_ON(m1, v) ? mval2i(v) : 0); free_space_pointer++; } else /* Input expected. */ param_list->arg[j] = (void *)(gtm_long_t)(MV_ON(m1, v) ? mval2i(v) : 0); break; case gtm_jlong: # ifndef GTM64 /* Only need to do this rounding on non-64 it platforms because this one type has a 64-bit * alignment requirement on those platforms. */ free_space_pointer = (gtm_long_t *)(ROUND_UP2(((INTPTR_T)free_space_pointer), SIZEOF(gtm_int64_t))); # endif # ifdef GTM64 if (MASK_BIT_ON(m2)) { /* Output expected. */ # endif param_list->arg[j] = free_space_pointer; *((gtm_int64_t *)free_space_pointer) = (gtm_int64_t)(MV_ON(m1, v) ? mval2i8(v) : 0); free_space_pointer = (gtm_long_t *)((char *)free_space_pointer + SIZEOF(gtm_int64_t)); # ifdef GTM64 } else /* Input expected. */ param_list->arg[j] = (void *)(gtm_int64_t)(MV_ON(m1, v) ? mval2i8(v) : 0); # endif break; case gtm_jfloat: /* Have to go with additional storage either way due to the limitations of callg. */ param_list->arg[j] = free_space_pointer; *((float *)free_space_pointer) = (float)(MV_ON(m1, v) ? mval2double(v) : 0.0); free_space_pointer++; break; case gtm_jdouble: # ifndef GTM64 /* Only need to do this rounding on non-64 it platforms because this one type has a 64 bit * alignment requirement on those platforms. */ free_space_pointer = (gtm_long_t *)(ROUND_UP2(((INTPTR_T)free_space_pointer), SIZEOF(double))); # endif /* Have to go with additional storage either way due to the limitations of callg. */ param_list->arg[j] = free_space_pointer; *((double *)free_space_pointer) = (double)(MV_ON(m1, v) ? mval2double(v) : 0.0); free_space_pointer = (gtm_long_t *)((char *)free_space_pointer + SIZEOF(double)); break; case gtm_jstring: case gtm_jbyte_array: param_list->arg[j] = free_space_pointer; /* If this is input-enabled and defined, it should have been forced to string in an earlier loop. */ assert(!MV_ON(m1, v) || MV_IS_STRING(v)); if (MV_ON(m1, v) && v->str.len) { *free_space_pointer++ = (gtm_long_t)v->str.len; memcpy(free_string_pointer, v->str.addr, v->str.len); *(char **)free_space_pointer = (char *)free_string_pointer; free_string_pointer += v->str.len; } else { /* If an argument is for output only, an empty string, or skipped altogether, we still want * a valid length and a pointer to an empty null-terminated character array. */ *free_space_pointer++ = 0; *(char **)free_space_pointer = (char *)free_string_pointer; } free_space_pointer++; *free_string_pointer++ = '\0'; break; default: va_end(var_copy); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); break; } assert(((char *)free_string_pointer <= ((char *)param_list + n * 2)) && ((char *)free_space_pointer <= ((char *)param_list + n * 2))); i++; m1 = m1 >> 1; m2 = m2 >> 1; } assert((char *)free_space_pointer <= free_string_pointer_start); va_end(var_copy); param_list->n = argcnt + 3; /* Take care of the three implicit parameters. */ VERIFY_STORAGE_CHAINS; save_mumps_status = mumps_status; /* Save mumps_status as a callin from external call may change it. */ assert(INTRPT_OK_TO_INTERRUPT == intrpt_ok_state); /* Expected for DEFERRED_EXIT_HANDLING_CHECK below */ TREF(in_ext_call) = TRUE; status = callg((callgfnptr)entry_ptr->fcn, param_list); TREF(in_ext_call) = FALSE; verify_buffer((char *)param_list, n, entry_ptr->entry_name.addr); check_for_timer_pops(!entry_ptr->ext_call_behaviors[SIGSAFE]); mumps_status = save_mumps_status; /* The first byte of the type description argument gets set to 0xFF in case error happened in JNI glue code, * so check for that and act accordingly. */ if ((char)0xFF == *(char *)param_list->arg[0]) { error_in_xc = TRUE; jni_err_buf = *(char **)((char *)param_list->arg[0] + SIZEOF(char *)); if (NULL != jni_err_buf) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_JNI, 2, LEN_AND_STR(jni_err_buf)); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZCSTATUSRET); } free(types_descr_ptr); /* Exit from the residual call-in environment(SFF_CI and base frames) which might still exist on M stack when the externally * called function in turn called into an M routine. */ if (frame_pointer->flags & SFF_CI) ci_ret_code_quit(); /* Only process the input-output and output-only arguments if the external call succeeded; otherwise, return -1 * if non-void return is expected. */ if (!error_in_xc) { /* Compute space requirement for return values. */ n = 0; VAR_COPY(var_copy, var); for (i = 0, j = 1, m1 = mask & entry_ptr->output_mask; i < argcnt; j++) { v = va_arg(var, mval *); if (j < 3) continue; if (MASK_BIT_ON(m1)) n += extarg_getsize(param_list->arg[j], entry_ptr->parms[i], v, entry_ptr); i++; m1 = m1 >> 1; } va_end(var); if (dst) n += extarg_getsize((void *)status, entry_ptr->return_type, dst, entry_ptr); ENSURE_STP_FREE_SPACE(n); /* Convert return values. */ VAR_COPY(var, var_copy); for (i = 0, j = 1, m1 = mask & entry_ptr->output_mask; i < argcnt; j++) { v = va_arg(var_copy, mval *); if (j < 3) continue; if (MASK_BIT_ON(m1)) extarg2mval((void *)param_list->arg[j], entry_ptr->parms[i], v, TRUE, TRUE, entry_ptr->param_pre_alloc_size[i], entry_ptr->entry_name.addr, param_list, call_buff_size); i++; m1 = m1 >> 1; } va_end(var); if (dst) { if (entry_ptr->return_type != gtm_void) extarg2mval((void *)status, entry_ptr->return_type, dst, TRUE, FALSE, -1, entry_ptr->entry_name.addr, param_list, call_buff_size); else { memcpy(str_buffer, PACKAGE_ENV_PREFIX, SIZEOF(PACKAGE_ENV_PREFIX)); tmp_buff_ptr = &str_buffer[SIZEOF(PACKAGE_ENV_PREFIX) - 1]; if (package->str.len) { assert(package->str.len < MAX_NAME_LENGTH - SIZEOF(PACKAGE_ENV_PREFIX) - 1); *tmp_buff_ptr++ = '_'; memcpy(tmp_buff_ptr, package->str.addr, package->str.len); tmp_buff_ptr += package->str.len; } *tmp_buff_ptr = '\0'; xtrnl_table_name = GETENV(str_buffer); if (NULL == xtrnl_table_name) { /* Environment variable for the package not found. This part of code is for more safety. * We should not come into this path at all. */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZCCTENV, 2, LEN_AND_STR(str_buffer)); } RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_XCVOIDRET, 4, LEN_AND_STR(entry_ptr->call_name.addr), LEN_AND_STR(xtrnl_table_name)); } } } else if (dst && (gtm_void != entry_ptr->return_type)) i2mval(dst, -1); free_return_type(status, entry_ptr->return_type); return; } void op_fnfgncal(uint4 n_mvals, mval *dst, mval *package, mval *extref, uint4 mask, int4 argcnt, ...) { boolean_t java = FALSE; char *free_string_pointer, *free_string_pointer_start; char str_buffer[MAX_NAME_LENGTH], *tmp_buff_ptr, *xtrnl_table_name; int i, pre_alloc_size, rslt, save_mumps_status; int4 callintogtm_vectorindex, n, call_buff_size; gparam_list *param_list; gtm_long_t *free_space_pointer; INTPTR_T status; mval *v; struct extcall_package_list *package_ptr; struct extcall_entry_list *entry_ptr; uint4 m1; va_list var; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(n_mvals == argcnt + 5); assert(MV_IS_STRING(package)); /* Package and routine are literal strings */ assert(MV_IS_STRING(extref)); if (MAX_ACTUALS < argcnt) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZCMAXPARAM); assert(INTRPT_OK_TO_INTERRUPT == intrpt_ok_state); /* Interrupts should be enabled for external calls */ /* Find package */ for (package_ptr = TREF(extcall_package_root); package_ptr; package_ptr = package_ptr->next_package) { MSTR_CMP(package_ptr->package_name, package->str, rslt); if (0 == rslt) break; } /* If package has not been found, create it */ if (NULL == package_ptr) { package_ptr = exttab_parse(package); package_ptr->next_package = TREF(extcall_package_root); TREF(extcall_package_root) = package_ptr; } /* At this point, we have a valid package, pointed to by package_ptr */ assert(NULL != package_ptr); if (NULL == package_ptr->package_handle) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); /* Find entry */ for (entry_ptr = package_ptr->first_entry; entry_ptr; entry_ptr = entry_ptr->next_entry) { MSTR_CMP(entry_ptr->entry_name, extref->str, rslt); if (0 == rslt) break; } if (call_table_initialized == FALSE) { call_table_initialized = TRUE; init_callin_functable(); } /* Entry not found */ if ((NULL == entry_ptr) || (NULL == entry_ptr->fcn) || (NULL == entry_ptr->call_name.addr)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZCRTENOTF, 2, extref->str.len, extref->str.addr); /* Detect a call-out to Java. */ if (!strncmp(entry_ptr->call_name.addr, "gtm_xcj", 7)) { java = TRUE; argcnt -= 2; } /* It is an error to have more actual parameters than formal parameters */ if (argcnt > entry_ptr->argcnt) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZCARGMSMTCH, 2, argcnt, entry_ptr->argcnt); VAR_START(var, argcnt); if (java) { op_fgnjavacal(dst, package, extref, mask, argcnt, entry_ptr->argcnt, package_ptr, entry_ptr, var); return; } /* Compute size of parameter block */ n = entry_ptr->parmblk_size; /* This is enough for the parameters and the fixed length entries */ /* Now, add enough for the char *'s and the char **'s and string *'s */ for (i = 0, m1 = entry_ptr->input_mask; i < argcnt; i++, m1 = m1 >> 1) { v = va_arg(var, mval *); /* For char*, char **, and gtm_string_t * types, add the length. Also a good time to force it into string form. */ switch (entry_ptr->parms[i]) { case gtm_string_star: /* CAUTION: Fall-through. */ case gtm_char_star: n += (-1 != entry_ptr->param_pre_alloc_size[i]) ? entry_ptr->param_pre_alloc_size[i] : 0; /* CAUTION: Fall-through. */ case gtm_char_starstar: if (MASK_BIT_ON(m1)) { if (MV_DEFINED(v)) { MV_FORCE_STR(v); n += v->str.len + 1; /* gtm_string_star does not really need the extra byte */ } else { MV_FORCE_DEFINED_UNLESS_SKIPARG(v); n += 1; } } break; # ifndef GTM64 case gtm_double_star: n += SIZEOF(double); /* CAUTION: Fall-through. */ # endif default: if (MASK_BIT_ON(m1)) { MV_FORCE_DEFINED_UNLESS_SKIPARG(v); } } } va_end(var); /* Double the size, to take care of any alignments in the middle. Overall, the allocated space has the following structure: * ___________________________________________ * | | | | * | param_list | space buffer | string buffer | * |____________|______________|_______________| * * For pointer-type arguments (gtm_long_t *, gtm_float_t *, etc.) the value in param_list is a pointer to a slot inside the * space buffer, unless it is gtm_char_t *, in which case the string buffer is used. For double-pointer types (char ** or * gtm_string_t *) the value in param_list is always a pointer inside the space buffer, where a pointer to an area inside * the string buffer is stored. */ /* Note that this is the nominal buffer size; or, explicitly, the size of buffer without the proctection tags*/ call_buff_size = 2*n; char param_list_buff[(call_buff_size + (2 * buff_boarder_len))]; memset(param_list_buff, 0, (call_buff_size + (2 * buff_boarder_len))); /* zero the contents for unmodified output values */ param_list = set_up_buffer(param_list_buff, 2*n); free_space_pointer = (gtm_long_t *)((char *)param_list + SIZEOF(intszofptr_t) + (SIZEOF(void *) * argcnt)); free_string_pointer_start = free_string_pointer = (char *)param_list + entry_ptr->parmblk_size; /* Load-up the parameter list */ VAR_START(var, argcnt); for (i = 0, m1 = entry_ptr->input_mask; i < argcnt; i++, m1 = m1 >> 1) { v = va_arg(var, mval *); /* Verify that all input values are defined */ pre_alloc_size = entry_ptr->param_pre_alloc_size[i]; switch (entry_ptr->parms[i]) { case gtm_uint: param_list->arg[i] = (void *)(gtm_ulong_t)(MV_ON(m1, v) ? mval2ui(v) : 0); /* Note: output gtm_int and gtm_uint is an error (only "star" flavor can be modified). */ break; case gtm_int: param_list->arg[i] = (void *)(gtm_long_t)(MV_ON(m1, v) ? mval2i(v) : 0); break; case gtm_ulong: param_list->arg[i] = (void *)GTM64_ONLY((gtm_uint64_t)) NON_GTM64_ONLY((gtm_ulong_t)) (MV_ON(m1, v) ? GTM64_ONLY(mval2ui8(v)) NON_GTM64_ONLY(mval2ui(v)) : 0); /* Note: output xc_long and xc_ulong is an error as described above. */ break; case gtm_long: param_list->arg[i] = (void *)GTM64_ONLY((gtm_int64_t)) NON_GTM64_ONLY((gtm_long_t)) (MV_ON(m1, v) ? GTM64_ONLY(mval2i8(v)) NON_GTM64_ONLY(mval2i(v)) : 0); break; case gtm_char_star: param_list->arg[i] = free_string_pointer; if (MASK_BIT_ON(m1)) { /* If this is defined and input-enabled, it should have already been forced to string. */ assert(!MV_DEFINED(v) || MV_IS_STRING(v)); if (MV_DEFINED(v) && v->str.len) { memcpy(free_string_pointer, v->str.addr, v->str.len); free_string_pointer += v->str.len; } *free_string_pointer++ = '\0'; } else if (0 < pre_alloc_size) { *free_string_pointer = '\0'; free_string_pointer += pre_alloc_size; } else /* Output and no pre-allocation specified */ { if (0 == package->str.len) /* Default package - do not display package name */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_ZCNOPREALLOUTPAR, 5, i + 1, RTS_ERROR_LITERAL(""), extref->str.len, extref->str.addr); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_ZCNOPREALLOUTPAR, 5, i + 1, package->str.len, package->str.addr, extref->str.len, extref->str.addr); } break; case gtm_char_starstar: param_list->arg[i] = free_space_pointer; /* If this is defined and input-enabled, it should have been forced to string in an earlier loop. */ assert(!MV_ON(m1, v) || MV_IS_STRING(v)); *(char **)free_space_pointer = free_string_pointer; if (MV_ON(m1, v) && v->str.len) { memcpy(free_string_pointer, v->str.addr, v->str.len); free_string_pointer += v->str.len; } *free_string_pointer++ = '\0'; free_space_pointer++; break; case gtm_int_star: param_list->arg[i] = free_space_pointer; *((gtm_int_t *)free_space_pointer) = MV_ON(m1, v) ? (gtm_int_t)mval2i(v) : 0; free_space_pointer++; break; case gtm_uint_star: param_list->arg[i] = free_space_pointer; *((gtm_uint_t *)free_space_pointer) = MV_ON(m1, v) ? (gtm_uint_t)mval2ui(v) : 0; free_space_pointer++; break; case gtm_long_star: param_list->arg[i] = free_space_pointer; GTM64_ONLY(*((gtm_int64_t *)free_space_pointer) = MV_ON(m1, v) ? (gtm_int64_t)mval2i8(v) : 0); NON_GTM64_ONLY(*((gtm_long_t *)free_space_pointer) = MV_ON(m1, v) ? (gtm_long_t)mval2i(v) : 0); free_space_pointer++; break; case gtm_ulong_star: param_list->arg[i] = free_space_pointer; GTM64_ONLY(*((gtm_uint64_t *)free_space_pointer) = MV_ON(m1, v) ? (gtm_uint64_t)mval2ui8(v) : 0); NON_GTM64_ONLY(*((gtm_ulong_t *)free_space_pointer) = MV_ON(m1, v) ? (gtm_ulong_t)mval2ui(v) : 0); free_space_pointer++; break; case gtm_string_star: param_list->arg[i] = free_space_pointer; if (MASK_BIT_ON(m1)) { /* If this is defined and input-enabled, it should have already been forced to string. */ assert(!MV_DEFINED(v) || MV_IS_STRING(v)); if (MV_DEFINED(v) && v->str.len) { *free_space_pointer++ = (gtm_long_t)v->str.len; *(char **)free_space_pointer = (char *)free_string_pointer; memcpy(free_string_pointer, v->str.addr, v->str.len); free_string_pointer += v->str.len; free_space_pointer++; } else { *free_space_pointer++ = 0; *free_space_pointer++ = 0; /* Effectively a NULL pointer. */ } } else if (0 < pre_alloc_size) { *free_space_pointer++ = (gtm_long_t)pre_alloc_size; *(char **)free_space_pointer = (char *)free_string_pointer; *free_string_pointer = '\0'; free_space_pointer++; free_string_pointer += pre_alloc_size; } else /* Output and no pre-allocation specified */ { if (0 == package->str.len) /* Default package - do not display package name */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_ZCNOPREALLOUTPAR, 5, i + 1, RTS_ERROR_LITERAL(""), extref->str.len, extref->str.addr); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_ZCNOPREALLOUTPAR, 5, i + 1, package->str.len, package->str.addr, extref->str.len, extref->str.addr); } break; case gtm_float_star: param_list->arg[i] = free_space_pointer; *((float *)free_space_pointer) = MV_ON(m1, v) ? (float)mval2double(v) : (float)0.0; free_space_pointer++; break; case gtm_double_star: /* Only need to do this rounding on non-64 it platforms because this one type has a 64 bit * alignment requirement on those platforms. */ NON_GTM64_ONLY(free_space_pointer = (gtm_long_t *)(ROUND_UP2(((INTPTR_T)free_space_pointer), SIZEOF(double)))); param_list->arg[i] = free_space_pointer; *((double *)free_space_pointer) = MV_ON(m1, v) ? (double)mval2double(v) : 0.0; free_space_pointer += (SIZEOF(double) / SIZEOF(gtm_long_t)); break; case gtm_pointertofunc: callintogtm_vectorindex = MV_DEFINED(v) ? (int4)mval2i(v) : 0; if ((callintogtm_vectorindex >= gtmfunc_unknown_function) || (callintogtm_vectorindex < 0)) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_ZCVECTORINDX, 1, callintogtm_vectorindex, ERR_TEXT, 2, RTS_ERROR_TEXT("Passing Null vector")); param_list->arg[i] = 0; } else param_list->arg[i] = (void *)callintogtm_vectortable[callintogtm_vectorindex]; break; case gtm_pointertofunc_star: /* Cannot pass in a function address to be modified by the user program */ free_space_pointer = (gtm_long_t *)ROUND_UP2(((INTPTR_T)free_space_pointer), SIZEOF(INTPTR_T)); param_list->arg[i] = free_space_pointer; *((INTPTR_T *)free_space_pointer) = 0; free_space_pointer++; break; default: va_end(var); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); break; } } assert((char *)free_space_pointer <= free_string_pointer_start); va_end(var); param_list->n = argcnt; VERIFY_STORAGE_CHAINS; save_mumps_status = mumps_status; /* Save mumps_status as a callin from external call may change it */ assert(INTRPT_OK_TO_INTERRUPT == intrpt_ok_state); /* Expected for DEFERRED_EXIT_HANDLING_CHECK below */ TREF(in_ext_call) = TRUE; status = callg((callgfnptr)entry_ptr->fcn, param_list); TREF(in_ext_call) = FALSE; verify_buffer((char *)param_list, (2*n), entry_ptr->entry_name.addr); check_for_timer_pops(!entry_ptr->ext_call_behaviors[SIGSAFE]); mumps_status = save_mumps_status; /* Exit from the residual call-in environment(SFF_CI and base frames) which might * still exist on M stack when the externally called function in turn called * into an M routine. */ if (frame_pointer->flags & SFF_CI) ci_ret_code_quit(); /* NOTE: ADD RETURN STATUS CALCUATIONS HERE */ /* Compute space requirement for return values */ n = 0; VAR_START(var, argcnt); for (i = 0, m1 = mask & entry_ptr->output_mask; i < argcnt; i++, m1 = m1 >> 1) { v = va_arg(var, mval *); if (MASK_BIT_ON(m1)) n += extarg_getsize(param_list->arg[i], entry_ptr->parms[i], v, entry_ptr); } va_end(var); if (dst) n += extarg_getsize((void *)status, entry_ptr->return_type, dst, entry_ptr); ENSURE_STP_FREE_SPACE(n); /* Convert return values */ VAR_START(var, argcnt); for (i = 0, m1 = mask & entry_ptr->output_mask; i < argcnt; i++, m1 = m1 >> 1) { v = va_arg(var, mval *); if (MASK_BIT_ON(m1)) extarg2mval((void *)param_list->arg[i], entry_ptr->parms[i], v, FALSE, TRUE, entry_ptr->param_pre_alloc_size[i], entry_ptr->entry_name.addr, param_list, call_buff_size); } va_end(var); if (dst) { if (entry_ptr->return_type != gtm_void) extarg2mval((void *)status, entry_ptr->return_type, dst, FALSE, FALSE, -1, entry_ptr->entry_name.addr, param_list, call_buff_size); else { memcpy(str_buffer, PACKAGE_ENV_PREFIX, SIZEOF(PACKAGE_ENV_PREFIX)); tmp_buff_ptr = &str_buffer[SIZEOF(PACKAGE_ENV_PREFIX) - 1]; if (package->str.len) { assert(package->str.len < MAX_NAME_LENGTH - SIZEOF(PACKAGE_ENV_PREFIX) - 1); *tmp_buff_ptr++ = '_'; memcpy(tmp_buff_ptr, package->str.addr, package->str.len); tmp_buff_ptr += package->str.len; } *tmp_buff_ptr = '\0'; xtrnl_table_name = GETENV(str_buffer); if (NULL == xtrnl_table_name) { /* Environment variable for the package not found. This part of code is for more safety. * We should not come into this path at all. */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZCCTENV, 2, LEN_AND_STR(str_buffer)); } RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_XCVOIDRET, 4, LEN_AND_STR(entry_ptr->call_name.addr), LEN_AND_STR(xtrnl_table_name)); } } free_return_type(status, entry_ptr->return_type); return; } fis-gtm-V7.0-005/sr_unix/op_fngetdvi.c0000755000032200000250000000110414342376330016510 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "op.h" void op_fngetdvi(mval *device, mval *keyword, mval* ret) { assert (FALSE); } fis-gtm-V7.0-005/sr_unix/op_fngetjpi.c0000644000032200000250000000672614342376330016524 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_unistd.h" #include "gtm_string.h" #include "stringpool.h" #include "gtm_times.h" #include "gtm_caseconv.h" #include "op.h" #include "mvalconv.h" #include "is_proc_alive.h" #include "eintr_wrappers.h" #include "gtmio.h" #include "have_crit.h" #define MAX_KEY 16 #define MAX_STR 16 GBLREF spdesc stringpool ; typedef char keyword[MAX_KEY] ; #define MAX_KEY_LEN 20 /* maximum length across all keywords in the key[] array below */ error_def (ERR_BADJPIPARAM); error_def (ERR_SYSCALL); static keyword key[]= { "CPUTIM", "CSTIME", "CUTIME", "ISPROCALIVE", "STIME", "UTIME", "" } ; enum kwind { kw_cputim, kw_cstime, kw_cutime, kw_isprocalive, kw_stime, kw_utime, kw_end }; void op_fngetjpi(mint jpid, mval *kwd, mval *ret) { struct tms proc_times; gtm_uint64_t info, sc_clk_tck; int keywd_indx; char upcase[MAX_KEY_LEN]; assert (stringpool.free >= stringpool.base); assert (stringpool.top >= stringpool.free); ENSURE_STP_FREE_SPACE(MAX_STR); MV_FORCE_STR(kwd); if (kwd->str.len == 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADJPIPARAM, 2, 4, "Null"); if (MAX_KEY < kwd->str.len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADJPIPARAM, 2, kwd->str.len, kwd->str.addr); lower_to_upper((uchar_ptr_t)upcase, (uchar_ptr_t)kwd->str.addr, (int)kwd->str.len); keywd_indx = kw_cputim ; /* future enhancement: * (i) since keywords are sorted, we can exit the while loop if 0 < memcmp. * (ii) also, the current comparison relies on kwd->str.len which means a C would imply CPUTIM instead of CSTIME * or CUTIME this ambiguity should probably be removed by asking for an exact match of the full keyword */ while (key[keywd_indx][0] && (0 != STRNCMP_STR(upcase, key[keywd_indx], kwd->str.len))) keywd_indx++; if( !key[keywd_indx][0] ) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADJPIPARAM, 2, kwd->str.len, kwd->str.addr); } if ((kw_isprocalive != keywd_indx) && ((unsigned int)-1 == times(&proc_times))) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("clock_gettime"), CALLFROM, errno); return; } switch (keywd_indx) { case kw_cputim: info = proc_times.tms_utime + proc_times.tms_stime + proc_times.tms_cutime + proc_times.tms_cstime; break; case kw_cstime: info = proc_times.tms_cstime; break; case kw_cutime: info = proc_times.tms_cutime; break; case kw_isprocalive: info = (0 != jpid) ? is_proc_alive(jpid, 0) : 1; break; case kw_stime: info = proc_times.tms_stime; break; case kw_utime: info = proc_times.tms_utime; break; case kw_end: default: rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_BADJPIPARAM, 2, kwd->str.len, kwd->str.addr); return; } if (kw_isprocalive != keywd_indx) { SYSCONF(_SC_CLK_TCK, sc_clk_tck); GTM_WHITE_BOX_TEST(WBTEST_FAKE_BIG_CNTS, info, info << 31); info = (gtm_uint64_t)((info * 100) / sc_clk_tck); /* Convert to standard 100 ticks per second */ } ui82mval(ret, info); } fis-gtm-V7.0-005/sr_unix/op_fngetlki.c0000755000032200000250000000110514342376330016506 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "op.h" void op_fngetlki(mval *lkid_mval, mval *keyword, mval *ret) { assert(FALSE); } fis-gtm-V7.0-005/sr_unix/op_fngetsyi.c0000755000032200000250000000110014342376330016526 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "op.h" void op_fngetsyi(mval *keyword,mval *node,mval *ret) { assert (FALSE); } fis-gtm-V7.0-005/sr_unix/op_fnp1.c0000755000032200000250000001671014342376330015557 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2018 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. * * * ****************************************************************/ /* * ----------------------------------------------- * op_fnp1 Piece function (the piecemaker) for UTF * Special case of 1 char delimiter and 1 piece (reference) * * Arguments: * src - pointer to Source mval * del - delimiter char to use looking for a piece * trgpcidx - index of piece to extract from source string * dst - pointer to Destination mval to save the piece in * * Return: * none * * Side effects: * dst structure gets filled with the result * ----------------------------------------------- */ #include "mdef.h" #include "gtm_string.h" #include "fnpc.h" #include "gtm_stdio.h" #include "min_max.h" #include "op.h" #include "gtm_utf8.h" GBLREF boolean_t gtm_utf8_mode; /* We are indeed doing the UTF8 thang */ GBLREF boolean_t badchar_inhibit; /* No BADCHAR errors should be signaled */ void op_fnp1(mval *src, int delim, int trgpcidx, mval *dst) { unsigned char *first, *last, *start, *end; unsigned int *pcoff, *pcoffmax, fnpc_indx, slen; int trgpc, cpcidx, spcidx, mblen, dlmlen; boolean_t valid_char; mval ldst; /* Local copy since &dst == &src .. move to dst at return */ fnpc *cfnpc; delimfmt ldelim; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(gtm_utf8_mode); MV_FORCE_STR(src); ldelim.unichar_val = delim; if (!UTF8_VALID(ldelim.unibytes_val, ldelim.unibytes_val + SIZEOF(ldelim.unibytes_val), dlmlen) && !badchar_inhibit) { /* The delimiter is a bad character so error out if badchar not inhibited */ UTF8_BADCHAR(0, ldelim.unibytes_val, ldelim.unibytes_val + SIZEOF(ldelim.unibytes_val), 0, NULL); } ldst.mvtype = MV_STR; start = first = last = (unsigned char *)src->str.addr; slen = src->str.len; end = start + slen; /* Detect annoyance cases and deal with quickly so we don't muck up the * logic below trying to handle it properly. */ if (0 >= trgpcidx || 0 == slen) { ldst.str.addr = (char *)start; ldst.str.len = 0; *dst = ldst; return; } /* Test mval for valid cache: index ok, mval addr same, delim same. One additional test * is if the cache entry is byte_oriented, then this cache entry was created by $ZPIECE * (using bytes) and since its results are not same as $PIECE(), we must ignore the cache * and rebuild it for this mval. */ fnpc_indx = src->fnpc_indx - 1; cfnpc = &(TREF(fnpca)).fnpcs[fnpc_indx]; if (FNPC_MAX > fnpc_indx && cfnpc->last_str.addr == (char *)first && cfnpc->last_str.len == slen && cfnpc->delim == ldelim.unichar_val && !cfnpc->byte_oriented) /* cannot use the cache created by an earlier $ZPIECE() */ { /* Have valid cache. See if piece we want already in cache */ COUNT_EVENT(hit); INCR_COUNT(pskip, cfnpc->npcs); pcoffmax = &cfnpc->pstart[FNPC_ELEM_MAX]; /* Local end of array value */ if (trgpcidx <= cfnpc->npcs) { /* Piece is totally in cache no scan needed */ ldst.str.addr = (char *)first + cfnpc->pstart[trgpcidx - 1]; ldst.str.len = cfnpc->pstart[trgpcidx] - cfnpc->pstart[trgpcidx - 1] - dlmlen; assert(ldst.str.len >= 0 && ldst.str.len <= src->str.len); *dst = ldst; return; } else { /* Not in cache but pick up scan where we left off */ cpcidx = cfnpc->npcs; first = last = start + cfnpc->pstart[cpcidx]; /* First byte of next pc */ pcoff = &cfnpc->pstart[cpcidx]; if (pcoff == pcoffmax) ++pcoff; /* No further updates to pstart array */ ++cpcidx; /* Now past last piece and on to next one */ COUNT_EVENT(parscan); } } else { /* The piece cache index or mval validation was incorrect. * Start from the beginning */ COUNT_EVENT(miss); /* Need to steal a new piece cache, get "least recently reused" */ cfnpc = (TREF(fnpca)).fnpcsteal; /* Get next element to steal */ if ((TREF(fnpca)).fnpcmax < cfnpc) cfnpc = &(TREF(fnpca)).fnpcs[0]; (TREF(fnpca)).fnpcsteal = cfnpc + 1; /* -> next element to steal */ cfnpc->last_str = src->str; /* Save validation info */ cfnpc->delim = ldelim.unichar_val; cfnpc->npcs = 0; cfnpc->byte_oriented = FALSE; src->fnpc_indx = cfnpc->indx + 1; /* Save where we are putting this element * (1 based index in mval so 0 isn't so common) */ pcoff = &cfnpc->pstart[0]; pcoffmax = &cfnpc->pstart[FNPC_ELEM_MAX]; /* Local end of array value */ cpcidx = 1; /* current piece index */ } /* Do scan filling in offsets of pieces if they fit in the cache */ spcidx = cpcidx; /* Starting value for search */ while ((cpcidx <= trgpcidx) && (last < end)) { /* Once through for each piece we pass, last time through to find length of piece we want */ first = last; /* First char of current piece */ while (last < end) { valid_char = UTF8_VALID(last, end, mblen); /* Length of next char */ if (!valid_char) { /* Next character is not valid UTF8. If badchar error is not inhibited, * signal it now. If it is inhibited, just treat the character as a single * character and continue. */ if (!badchar_inhibit) utf8_badchar(0, last, end, 0, NULL); assert(1 == mblen); } /* Getting mblen first allows us to do quick length compare before the * heavier weight memcmp call. */ assert(0 < mblen); if (mblen == dlmlen) { if (1 == dlmlen) { if (*last == ldelim.unibytes_val[0]) /* Shortcut - test single byte */ break; } else if (0 == memcmp(last, ldelim.unibytes_val, dlmlen)) /* Longcut - for multibyte chk */ break; } last += mblen; /* Find delim signaling end of piece */ } last += dlmlen; /* Bump past delim to first byte of next piece. The length of * the delimiter is assumed in the pcoff array and is removed * when piece length is calculated so even if we hit the end of * the scanned source, we bump the pointer so this extra length * is reflected in the pcoff array consistently. */ ++cpcidx; /* Next piece */ if (pcoff < pcoffmax) *pcoff++ =(unsigned int)(first - start); /* Offset to this piece */ if (pcoff == pcoffmax) *pcoff++ = (unsigned int)(last - start); /* Store start of first piece beyond what is in cache */ } ldst.str.addr = (char *)first; /* If we scanned some chars, adjust end pointer and save end of final piece */ if (spcidx != cpcidx) { if (pcoff < pcoffmax) *pcoff = (unsigned int)(last - start); /* If not at end of cache, save start of "next" piece */ last -= dlmlen; /* Undo bump past last delim (existing or not) * of piece for accurate string len */ /* Update count of pieces in cache */ cfnpc->npcs = MIN((cfnpc->npcs + cpcidx - spcidx), FNPC_ELEM_MAX); assert(cfnpc->npcs <= FNPC_ELEM_MAX); assert(cfnpc->npcs > 0); /* If we the above loop ended prematurely because we ran out of text, we return null string */ if (trgpcidx < cpcidx) ldst.str.len = INTCAST(last - first); /* Length of piece we located */ else ldst.str.len = 0; INCR_COUNT(pscan, cpcidx - spcidx); /* Pieces scanned */ } else ldst.str.len = 0; assert(0 < cfnpc->npcs); assert((0 <= ldst.str.len) && (ldst.str.len <= src->str.len)); *dst = ldst; return; } fis-gtm-V7.0-005/sr_unix/op_fnpiece.c0000755000032200000250000000561714342376330016330 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2018 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_string.h" #include "op.h" #include "matchc.h" #include "fnpc.h" #include "gtm_utf8.h" GBLREF boolean_t gtm_utf8_mode; /* -------------------------------------------------------------------- * NOTE: This module is a near copy of sr_port/op_fnzpiece.c differing * only in that it calls "matchc" instead of "matchb" to do matching. * -------------------------------------------------------------------- */ /* * ----------------------------------------------- * op_fnpiece() * MUMPS $Piece function for UTF8 * * Arguments: * src - Pointer to Source string mval * del - Pointer to delimiter mval * first - starting piece number * last - last piece number * dst - destination buffer to save the piece in * * Return: * none * ----------------------------------------------- */ void op_fnpiece(mval *src, mval *del, int first, int last, mval *dst) { int piece_cnt, del_len, src_len; char *del_str, *src_str; char *match_start; int match_res, int_del; delimfmt unichar; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!TREF(compile_time) || valid_utf_string(&src->str)); assert(gtm_utf8_mode); if (--first < 0) first = 0; if ((piece_cnt = last - first) < 1) { MV_INIT_STRING(dst, 0, NULL); return; } MV_FORCE_STR(src); MV_FORCE_STR(del); /* See if we can take a short cut to op_fnp1. If so, the delimiter value needs to be reformated. */ if (1 == piece_cnt && 1 == MV_FORCE_LEN(del)) { /* Both valid chars of char_len=1 and single byte invalid chars get the fast path */ unichar.unichar_val = 0; assert(SIZEOF(unichar.unibytes_val) >= del->str.len); memcpy(unichar.unibytes_val, del->str.addr, del->str.len); op_fnp1(src, unichar.unichar_val, last, dst); /* Use last as it is unmodified */ return; } src_len = src->str.len; src_str = src->str.addr; del_len = del->str.len; del_str = del->str.addr; if (first) { match_start = (char *)matchc(del_len, (uchar_ptr_t)del_str, src_len, (uchar_ptr_t)src_str, &match_res, &first); if (0 == match_res) { MV_INIT_STRING(dst, 0, NULL); return; } src_len -= INTCAST(match_start - src_str); src_str = match_start; } else match_start = src_str; src_str = (char *)matchc(del_len, (uchar_ptr_t)del_str, src_len, (uchar_ptr_t)src_str, &match_res, &piece_cnt); if (0 != match_res) src_str -= del_len; MV_INIT_STRING(dst, INTCAST(src_str - match_start), match_start); return; } fis-gtm-V7.0-005/sr_unix/op_fnpopulation.c0000755000032200000250000000156714342376330017435 0ustar librarygtc/**************************************************************** * * * Copyright 2006 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 "op.h" #include "mvalconv.h" /* Called for $LENGTH() when a second argument is supplied. Returns the number of "pieces" in the string given the supplied delimiter. */ void op_fnpopulation(mval *arg1, mval *arg2, mval *dst) { int x, y; mval dummy; y = 0; MV_FORCE_STR(arg1); MV_FORCE_STR(arg2); if (arg2->str.len) for (x = 1; x ; y++) x = op_fnfind(arg1, arg2, x, &dummy); MV_FORCE_MVAL(dst,y) ; } fis-gtm-V7.0-005/sr_unix/op_fnrandom.c0000644000032200000250000000214714342376330016513 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_time.h" #include "op.h" #include "mvalconv.h" GBLREF int4 process_id; error_def(ERR_RANDARGNEG); double drand48(void); void srand48(long int); void op_fnrandom (int4 interval, mval *ret) { static int4 seed = 0; int4 random; if (interval < 1) { RTS_ERROR_ABT(VARLSTCNT(1) ERR_RANDARGNEG); } if (seed == 0) { seed = (int4)(time(0) * process_id); srand48(seed); } random = (int4)(interval * drand48()); random = random & 0x7fffffff; /* to make sure that the sign bit(msb) is off*/ MV_FORCE_MVAL(ret, random); } fis-gtm-V7.0-005/sr_unix/op_fnzcall.c0000755000032200000250000000104114342376330016333 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2005 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "op.h" void op_fnzcall() {assert (FALSE);} fis-gtm-V7.0-005/sr_unix/op_fnzconvert.c0000644000032200000250000001724214342376330017107 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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_string.h" #include "op.h" #include "stringpool.h" #include "gtm_icu_api.h" #include "gtm_conv.h" #include "gtm_utf8.h" #include "have_crit.h" GBLREF boolean_t badchar_inhibit; GBLREF boolean_t gtm_utf8_mode; GBLREF spdesc stringpool; GBLREF casemap_t casemaps[]; error_def(ERR_BADCASECODE); error_def(ERR_BADCHSET); error_def(ERR_ICUERROR); error_def(ERR_MAXSTRLEN); error_def(ERR_INVFCN); error_def(ERR_TEXT); #define RELEASE_IF_NOT_LOCAL(ptr, local) ((ptr) != (local)) ? (free(ptr), (ptr = NULL)) : ptr; /************************************************************************************************** * Routine to perform string-level case conversion to "upper", "lower" and "title" case. * Since ICU only supports API using UTF-16 representation, case conversion of UTF-8 strings involves * encoding conversion as described below: * 1. First, the UTF-8 source string is converted to UTF-16 representation (u_strFromUTF8()) * which is stored in a local buffer of size MAX_ZCONVBUFF. If this space is not sufficient, * we try to allocate it in heap. * 2. Since case conversion may expand the string, we compute the desired space required by * preflighting the ICU case conversion API and then allocate the space before performing * the real conversion. * 3. Translating the converted UTF-16 string back to UTF-8 is done in stringpool (with similar * preflighting to compute the required space. * NOTE: * Malloc is used only if the size exceeds 2K characters (a very unlikely situation esp. with * case conversion). * ***************************************************************************************************/ void op_fnzconvert2(mval *src, mval *kase, mval *dst) { int index; int32_t src_ustr_len, src_chlen, dst_chlen, ulen, dstlen = 0; UErrorCode status; char *dstbase; UChar src_ustr[MAX_ZCONVBUFF], dst_ustr[MAX_ZCONVBUFF], *src_ustr_ptr, *dst_ustr_ptr; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); MV_FORCE_STR(kase); if (-1 == (index = verify_case(&kase->str))) { ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADCASECODE, 2, kase->str.len, kase->str.addr); } MV_FORCE_STR(src); /* allocate stringpool */ if (!gtm_utf8_mode) { dstlen = src->str.len; ENSURE_STP_FREE_SPACE(dstlen); dstbase = (char *)stringpool.free; assert(NULL != casemaps[index].m); (*casemaps[index].m)((unsigned char *)dstbase, (unsigned char *)src->str.addr, dstlen); } else if (0 != src->str.len) { if (!badchar_inhibit) MV_FORCE_LEN_STRICT(src); else MV_FORCE_LEN(src); if (2 * src->str.char_len <= MAX_ZCONVBUFF) { /* Check if the stack buffer is sufficient considering the worst case where all characters are surrogate pairs, each of which needs 2 UChars */ src_ustr_ptr = src_ustr; src_ustr_len = MAX_ZCONVBUFF; } else { /* To avoid preflight, allocate (2 * lenght of src->str.char_len). */ src_ustr_len = 2 * src->str.char_len; src_ustr_ptr = (UChar*)malloc(src_ustr_len * SIZEOF(UChar)); } /* Convert UTF-8 src to UTF-16 (UChar*) representation */ status = U_ZERO_ERROR; u_strFromUTF8(src_ustr_ptr, src_ustr_len, &src_chlen, src->str.addr, src->str.len, &status); if ((U_ILLEGAL_CHAR_FOUND == status || U_INVALID_CHAR_FOUND == status) && badchar_inhibit) { /* VIEW "NOBADCHAR" in effect, use the M mode conversion only on error */ dstlen = src->str.len; ENSURE_STP_FREE_SPACE(dstlen); dstbase = (char *)stringpool.free; assert(NULL != casemaps[index].m); (*casemaps[index].m)((unsigned char *)dstbase, (unsigned char *)src->str.addr, dstlen); } else if (U_FAILURE(status)) { RELEASE_IF_NOT_LOCAL(src_ustr_ptr, src_ustr); if (U_ILLEGAL_CHAR_FOUND == status || U_INVALID_CHAR_FOUND == status) utf8_len_strict((unsigned char *)src->str.addr, src->str.len); /* to report BADCHAR error */ ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_ICUERROR, 1, status); /* ICU said bad, we say good or don't recognize error*/ } else { /* BADCHAR should not be possible after the above validations */ status = U_ZERO_ERROR; dst_ustr_ptr = dst_ustr; dst_chlen = (*casemaps[index].u)(dst_ustr_ptr, MAX_ZCONVBUFF, src_ustr_ptr, src_chlen, NULL, &status); if (U_BUFFER_OVERFLOW_ERROR == status) { status = U_ZERO_ERROR; dst_ustr_ptr = (UChar*)malloc(dst_chlen * SIZEOF(UChar)); /* Now, perform the real conversion with sufficient buffers */ dst_chlen = (*casemaps[index].u)(dst_ustr_ptr, dst_chlen, src_ustr_ptr, src_chlen, NULL, &status); } else if (U_FILE_ACCESS_ERROR == status) { RELEASE_IF_NOT_LOCAL(src_ustr_ptr, src_ustr); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_ICUERROR, 1, status); } /* Fake the conversion from UTF-16 to UTF-8 to compute the required buffer size */ status = U_ZERO_ERROR; dstlen = 0; u_strToUTF8(NULL, 0, &dstlen, dst_ustr_ptr, dst_chlen, &status); assert(U_BUFFER_OVERFLOW_ERROR == status || U_SUCCESS(status)); if (MAX_STRLEN < dstlen) { RELEASE_IF_NOT_LOCAL(dst_ustr_ptr, dst_ustr); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXSTRLEN); } ENSURE_STP_FREE_SPACE(dstlen); dstbase = (char *)stringpool.free; status = U_ZERO_ERROR; u_strToUTF8(dstbase, dstlen, &ulen, dst_ustr_ptr, dst_chlen, &status); RELEASE_IF_NOT_LOCAL(src_ustr_ptr, src_ustr); if (U_FAILURE(status)) { RELEASE_IF_NOT_LOCAL(src_ustr_ptr, src_ustr); RELEASE_IF_NOT_LOCAL(dst_ustr_ptr, dst_ustr); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_ICUERROR, 1, status); /* ICU said bad, but same call above just returned OK */ } assertpro(ulen == dstlen); RELEASE_IF_NOT_LOCAL(dst_ustr_ptr, dst_ustr); } } else dstbase = NULL; /* 4SCA: Assigned value is garbage or undefined, but below memcpy protected by dstlen */ MV_INIT_STRING(dst, dstlen, dstbase); stringpool.free += dstlen; ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); } void op_fnzconvert3(mval *src, mval* ichset, mval* ochset, mval* dst) { UConverter *from, *to; int dstlen; MV_FORCE_STR(src); if (!gtm_utf8_mode) { /* UTF8 not enabled, report error rather than silently ignoring the conversion */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_INVFCN, 0, ERR_TEXT, 2, LEN_AND_LIT("Three-argument form of $ZCONVERT() is not allowed in the current $ZCHSET")); } MV_FORCE_STR(ichset); MV_FORCE_STR(ochset); /* The only supported names are: "UTF-8", "UTF-16", "UTF-16LE" and "UTF-16BE */ if (NULL == (from = get_chset_desc(&ichset->str))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADCHSET, 2, ichset->str.len, ichset->str.addr); if (NULL == (to = get_chset_desc(&ochset->str))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADCHSET, 2, ochset->str.len, ochset->str.addr); dstlen = gtm_conv(from, to, &src->str, NULL, NULL); assert(-1 != dstlen); MV_INIT_STRING(dst, dstlen, stringpool.free); stringpool.free += dst->str.len; } fis-gtm-V7.0-005/sr_unix/op_fnzfile.c0000755000032200000250000000107314342376330016344 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "op.h" void op_fnzfile(mval *name,mval *key,mval *ret) { assert (FALSE); } fis-gtm-V7.0-005/sr_unix/op_fnzlkid.c0000755000032200000250000000106514342376330016351 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "op.h" void op_fnzlkid (mint boolex, mval *retval) { assert(FALSE); } fis-gtm-V7.0-005/sr_unix/op_fnzparse.c0000644000032200000250000000767714342376330016554 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "parse_file.h" #include "gtm_caseconv.h" #include "stringpool.h" #include "op.h" #define ZP_DEVICE 1 #define ZP_DIRECTORY 2 #define ZP_NAME 3 #define ZP_NODE 4 #define ZP_TYPE 5 #define ZP_VERSION 6 #define ZP_FULL 7 #define DEV_LEN 6 #define DIR_LEN 9 #define VER_LEN 7 #define ZP_LEN 4 #define NCON_LEN 10 #define SYN_LEN 11 error_def(ERR_ZPARSETYPE); error_def(ERR_ZPARSFLDBAD); error_def(ERR_INVSTRLEN); void op_fnzparse (mval *file, mval *field, mval *def1, mval *def2, mval *type, mval *ret) { char field_type; uint4 status; char field_buf[DIR_LEN], type_buf[SYN_LEN], result[MAX_FN_LEN + 1]; parse_blk pblk; MV_FORCE_STR(field); MV_FORCE_STR(def1); MV_FORCE_STR(def2); MV_FORCE_STR(file); MV_FORCE_STR(type); if (def1->str.len > MAX_FN_LEN) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_INVSTRLEN, 2, def1->str.len, MAX_FN_LEN); if (def2->str.len > MAX_FN_LEN) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_INVSTRLEN, 2, def2->str.len, MAX_FN_LEN); if (field->str.len == 0) { field_type = ZP_FULL; } else { field_type = 0; if (field->str.len <= DIR_LEN) { lower_to_upper((uchar_ptr_t)&field_buf[0], (uchar_ptr_t)field->str.addr, field->str.len); switch( field_buf[0] ) { case 'D': if (field->str.len <= DEV_LEN && memcmp(&field_buf[0], "DEVICE", field->str.len) == 0) field_type = ZP_DEVICE; else if (field->str.len <= DIR_LEN && memcmp(&field_buf[0], "DIRECTORY", field->str.len) == 0) field_type = ZP_DIRECTORY; break; case 'N': if (field->str.len <= ZP_LEN) { if (memcmp(&field_buf[0], "NAME", field->str.len) == 0) field_type = ZP_NAME; else if (memcmp(&field_buf[0], "NODE", field->str.len) == 0) field_type = ZP_NODE; } break; case 'T': if (field->str.len <= ZP_LEN && memcmp(&field_buf[0], "TYPE", field->str.len) == 0) field_type = ZP_TYPE; break; case 'V': if (field->str.len <= VER_LEN && memcmp(&field_buf[0], "VERSION", field->str.len) == 0) field_type = ZP_VERSION; break; default: break; } } if (field_type == 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZPARSFLDBAD, 2, field->str.len, field->str.addr); } memset(&pblk, 0, SIZEOF(pblk)); if (type->str.len != 0) { if (type->str.len <= SYN_LEN) lower_to_upper((uchar_ptr_t)&type_buf[0], (uchar_ptr_t)type->str.addr, type->str.len); if (type->str.len <= SYN_LEN && memcmp(&type_buf[0], "SYNTAX_ONLY", type->str.len) == 0) pblk.fop |= F_SYNTAXO; else if (type->str.len <= NCON_LEN && memcmp(&type_buf[0], "NO_CONCEAL", type->str.len) == 0) { /* no meaning on unix */ } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZPARSETYPE, 2, type->str.len, type->str.addr); } pblk.buffer = result; pblk.buff_size = MAX_FN_LEN; pblk.def1_size = def1->str.len; pblk.def1_buf = def1->str.addr; pblk.def2_size = def2->str.len; pblk.def2_buf = def2->str.addr; if ((parse_file(&file->str, &pblk) & 1) == 0) { ret->mvtype = MV_STR; ret->str.len = 0; return; } ret->mvtype = MV_STR; switch( field_type ) { case ZP_DIRECTORY: ret->str.addr = pblk.l_dir; ret->str.len = pblk.b_dir; break; case ZP_NAME: ret->str.addr = pblk.l_name; ret->str.len = pblk.b_name; break; case ZP_TYPE: ret->str.addr = pblk.l_ext; ret->str.len = pblk.b_ext; break; default: ret->str.addr = pblk.buffer; ret->str.len = pblk.b_esl; break; } s2pool(&ret->str); return; } fis-gtm-V7.0-005/sr_unix/op_fnzpeek.c0000644000032200000250000006602314342376330016354 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2021 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 #include "gtm_signal.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "gtm_time.h" #include "send_msg.h" #include "error.h" #include "stringpool.h" #include "util.h" #include "op.h" #include "nametabtyp.h" #include "namelook.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" #include "anticipatory_freeze.h" #include "gtm_caseconv.h" #include "jnl.h" #include "dollarh.h" error_def(ERR_BADZPEEKARG); error_def(ERR_BADZPEEKFMT); error_def(ERR_BADZPEEKRANGE); error_def(ERR_MAXSTRLEN); error_def(ERR_ZPEEKNOJNLINFO); error_def(ERR_ZPEEKNORPLINFO); #define FMTHEXDGT(spfree, digit) *spfree++ = digit + ((digit <= 9) ? '0' : ('A' - 0x0A)) #define ARGUMENT_MAX_LEN MAX_MIDENT_LEN #define PASS1 1 #define PASS2 2 /* Codes for peek operation mnemonics */ typedef enum { PO_CSAREG = 0, /* 0 Region information - sgmnt_addrs struct - process private structure */ PO_FHREG, /* 1 Fileheader information from sgmnt_data for specified region */ PO_GDRREG, /* 2 Region information - gd_region struct - process private structure */ PO_NLREG, /* 3 Fileheader information from node_local for specified region (transient - non permanent) */ PO_NLREPL, /* 4 Fileheader information from node_local for replication dummy region */ PO_GLFREPL, /* 5 Replication information from gtmsrc_lcl_array structure */ PO_GSLREPL, /* 6 Replication information from gtmsource_local_array structure */ PO_JPCREPL, /* 7 Replication information from jnlpool_ctl structure */ PO_PEEK, /* 8 Generalized peek specifying (base) address argument */ PO_RIHREPL, /* 9 Replication information from repl_inst_hdr structure */ PO_RPCREPL, /* 10 Replication information from recvpool_ctl_struct */ PO_UPLREPL, /* 11 Replication information from upd_proc_local_struct */ PO_GRLREPL, /* 12 Replication information from gtmrecv_local_struct */ PO_UHCREPL, /* 13 Replication information from upd_helper_ctl */ PO_JNLREG, /* 14 Journal information - jnl_private_control */ PO_JBFREG /* 15 Journal buffer information - jnl_buffer_ptr_t */ } zpeek_mnemonic; GBLREF boolean_t created_core; GBLREF sigset_t blockalrm; GBLREF gd_addr *gd_header; GBLREF int pool_init; GBLREF boolean_t jnlpool_init_needed; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF recvpool_addrs recvpool; #ifdef DEBUG GBLREF int process_exiting; #endif LITREF unsigned char lower_to_upper_table[]; STATICFNDCL void op_fnzpeek_signal_handler(int sig, siginfo_t *info, void *context); STATICFNDCL int op_fnzpeek_stpcopy(char *zpeekadr, int len, mval *ret, char fmtcode); STATICFNDCL uchar_ptr_t op_fnzpeek_uint64fmt(uchar_ptr_t p, gtm_uint64_t n); STATICFNDCL uchar_ptr_t op_fnzpeek_hexfmt(uchar_ptr_t p, gtm_uint64_t n, int fmtlen); STATICFNDEF boolean_t op_fnzpeek_attach_jnlpool(void); STATICFNDEF boolean_t op_fnzpeek_attach_recvpool(void); typedef struct { int peekop; /* Peek operation mnemonic id */ boolean_t allowargs; /* Number of arguments allowed */ } zpeek_data_typ; /* Lookup tables for first argument - Note names are limited to NAME_ENTRY_SZ bytes each */ LITDEF nametabent zpeek_names[] = { /* Array offsets */ {3, "CSA"}, {6, "CSAREG"} /* 0, 1 */ ,{2, "FH"}, {5, "FHREG"} /* 2, 3 */ ,{3, "GDR"}, {6, "GDRREG"} /* 4, 5 */ ,{3, "GLF"}, {7, "GLFREPL"} /* 6, 7 */ ,{3, "GRL"}, {7, "GRLREPL"} /* 8, 9 */ ,{3, "GSL"}, {7, "GSLREPL"} /* 10, 11 */ ,{3, "JBF"}, {6, "JBFREG"} /* 12, 13 */ ,{3, "JPC"}, {7, "JPCREPL"} /* 14, 15 */ ,{3, "JNL"}, {6, "JNLREG"} /* 16, 17 */ ,{2, "NL"}, {5, "NLREG"} /* 18, 19 */ ,{6, "NLREPL"} /* 20 */ ,{4, "PEEK"} /* 21 */ ,{3, "RIH"}, {7, "RIHREPL"} /* 22, 23 */ ,{3, "RPC"}, {7, "RPCREPL"} /* 24, 25 */ ,{3, "UHC"}, {7, "UHCREPL"} /* 26, 27 */ ,{3, "UPL"}, {7, "UPLREPL"} /* 28, 29 */ /* Total length 30 */ }; /* Index to first entry with given starting letter */ LITDEF unsigned char zpeek_index[] = { 0, 0, 0, 2, 2, 2, 4, 12, 12, /* a b c d e f g h i */ 12, 18, 18, 18, 18, 21, 21, 22, 22, /* j k l m n o p q r */ 26, 26, 26, 30, 30, 30, 30, 30, 30 /* s t u v w x y z ~ */ }; /* Associated fetch code for each entry with flag for whether arguments accepted after code (e.g. CSAREG:MUMPS) */ LITDEF zpeek_data_typ zpeek_data[] = { {PO_CSAREG, 1}, {PO_CSAREG, 1} ,{PO_FHREG, 1}, {PO_FHREG, 1} ,{PO_GDRREG, 1}, {PO_GDRREG, 1} ,{PO_GLFREPL, 1}, {PO_GLFREPL, 1} ,{PO_GRLREPL, 0}, {PO_GRLREPL, 0} ,{PO_GSLREPL, 1}, {PO_GSLREPL, 1} ,{PO_JBFREG, 1}, {PO_JBFREG, 1} ,{PO_JPCREPL, 0}, {PO_JPCREPL, 0} ,{PO_JNLREG, 1}, {PO_JNLREG, 1} ,{PO_NLREG, 1}, {PO_NLREG, 1} ,{PO_NLREPL, 0} ,{PO_PEEK, 1} ,{PO_RIHREPL, 0}, {PO_RIHREPL, 0} ,{PO_RPCREPL, 0}, {PO_RPCREPL, 0} ,{PO_UHCREPL, 0}, {PO_UHCREPL, 0} ,{PO_UPLREPL, 0}, {PO_UPLREPL, 0} }; /* Condition handler for use during copy of memory range to the stringpool for return. Note this condition handler is itself * never tripped but serves as an unwind target for the signal handler defined below (see its comments). */ CONDITION_HANDLER(op_fnzpeek_ch) { START_CH(TRUE); NEXTCH; /* In the unlikely event it gets driven, just be a pass-thru */ } /* $ZPEEK() is processing a process memory range specified by an M routine so is definitely capable of getting * user inspired address type exceptions. We protect against this by setting up our signal handler to catch any * such exceptions for the duration of this routine and just unwind them so we can throw a non-fatal error * message instead. */ void op_fnzpeek_signal_handler(int sig, siginfo_t *info, void *context) { /* We basically want to do UNWIND(NULL, NULL) logic but the UNWIND macro can only be used in a condition * handler so next is a block that pretends it is our condition handler and does the needful. Note in order * for this to work, we need to be wrapped in a condition handler even if that condition handler is never * actually invoked to serve as the target for the UNWIND(). */ { /* Needs new block since START_CH declares a new var used in UNWIND() */ int arg = 0; /* Needed for START_CH macro if debugging enabled */ START_CH(TRUE); assert(!process_exiting); UNWIND(NULL, NULL); } } /* Routine to convert gtm_uint64_t to ascii value not losing any precision. Routine is based on i2asc() but * uses gtm_uint64_t as the type. */ STATICFNDCL uchar_ptr_t op_fnzpeek_uint64fmt(uchar_ptr_t p, gtm_uint64_t n) { unsigned char ar[MAX_DIGITS_IN_INT8], *q; gtm_uint64_t m; int len; q = ar + SIZEOF(ar); if (!n) *--q = '0'; else { while (n) { m = n / 10; *--q = n - (m * 10) + '0'; n = m; } } assert((uintszofptr_t)q >= (uintszofptr_t)ar); len = (unsigned int)(ar + SIZEOF(ar) - q); memcpy(p, q, len); return p + len; } /* Routine to format hex output to given length with format 0xhh>>. Similar to i2asclx(). * * p - Output buffer (generally stringpool.free) * n - Hex value to format * fmtlen - Length in bytes of output value */ STATICFNDCL uchar_ptr_t op_fnzpeek_hexfmt(uchar_ptr_t p, gtm_uint64_t n, int fmtlen) { unsigned char ar[MAX_HEX_DIGITS_IN_INT8], *q; int m, digits; q = ar + SIZEOF(ar); for (digits = fmtlen; (0 < digits); --digits) { m = n & 0xF; if (m <= 9) *--q = m + '0'; else *--q = m - 0xa + 'A'; n = n >> 4; } assert(0 == n); /* Verify complete number has been output (no truncated digits) */ memcpy(p, q, fmtlen); return p + fmtlen; } /* Routine to extract and optionally format the requested data leaving it in the stringpool. This routine is protected * by a signal handler for data access against SIGSEGV or SIGBUS signals. Note the fields that are sub-integer (1 or * 2 bytes) are pulled into integer forms before processing. */ STATICFNDEF int op_fnzpeek_stpcopy(char *zpeekadr, int len, mval *ret, char fmtcode) { unsigned int uint; boolean_t negative; gtm_uint64_t uint64; unsigned char *numstrstart, *numstrend; unsigned char *hexchr, *hexchrend, hexc, hexdgt, *spfree; char timebuff[MAXNUMLEN + 1]; time_t seconds; uint4 days; ESTABLISH_RET(op_fnzpeek_ch, ERR_BADZPEEKRANGE); /* If get an exception, likely due to bad range */ ret->mvtype = 0; /* Prevent GC of incomplete field */ switch(fmtcode) { case 'S': /* Null terminated string processing */ STRNLEN(zpeekadr, len, len); /* Reset len to actual len, fall into "C" processing */ /* warning - fall through */ case 'C': /* Character area (no processing - just copy */ if (len > MAX_STRLEN) { /* Requested string return is too large */ REVERT; return ERR_MAXSTRLEN; } ENSURE_STP_FREE_SPACE(len); memcpy(stringpool.free, zpeekadr, len); ret->str.addr = (char *)stringpool.free; ret->str.len = len; stringpool.free += len; break; case 'I': /* Initially, treat signed/unsigned the same */ case 'U': negative = FALSE; switch(len) { case SIZEOF(gtm_uint64_t): /* Dealing with 8 byte integer style values is not GT.M's forte since its internal * number scheme is limited to 20 digits. So use our own routine to do the conversion. * Note: we could use this routine for all the below cases but on 32 bit platforms * with no native 8 byte values, they would run far slower so only use this for the * 8 byte values we deal with. */ uint64 = *(gtm_uint64_t *)zpeekadr; if ('I' == fmtcode) { /* If signed, check if need to add minus sign to value and change value to * positive. */ negative = (0 > (gtm_int64_t)uint64); if (negative) uint64 = (gtm_uint64_t)(-(gtm_int64_t)uint64); } fmtcode = 'u'; /* Change fmtcode to skip negative value check below */ break; case SIZEOF(unsigned int): uint = *(unsigned int *)zpeekadr; break; case SIZEOF(short): uint = (unsigned int)*(unsigned short *)zpeekadr; break; case SIZEOF(char): uint = (unsigned int)*(unsigned char *)zpeekadr; break; default: REVERT; return ERR_BADZPEEKFMT; } if ('I' == fmtcode) { /* If signed, check if need to add minus sign to value and change value to positive. Note this test * is bypassed for uint64 types because the check is already made (in a differet/longer value). */ negative = (0 > (signed int)uint); if (negative) uint = (unsigned int)(-(signed int)uint); } ENSURE_STP_FREE_SPACE(MAX_DIGITS_IN_INT + negative); /* Space to hold # */ numstrstart = stringpool.free; if (negative) *stringpool.free++ = '-'; /* Value is negative, record in output */ /* Use the correct formmating routine based on size */ numstrend = (SIZEOF(gtm_uint64_t) != len) ? i2asc(stringpool.free, uint) : op_fnzpeek_uint64fmt(stringpool.free, uint64); ret->str.addr = (char *)numstrstart; ret->str.len = INTCAST(numstrend - numstrstart); stringpool.free = numstrend; break; case 'X': /* Hex format for numeric values */ switch(len) { case SIZEOF(gtm_uint64_t): uint64 = *(gtm_uint64_t *)zpeekadr; break; case SIZEOF(unsigned int): uint64 = (gtm_uint64_t)*(unsigned int *)zpeekadr; break; case SIZEOF(unsigned short): uint64 = (gtm_uint64_t)*(unsigned short *)zpeekadr; break; case SIZEOF(unsigned char): uint64 = (gtm_uint64_t)*(unsigned char *)zpeekadr; break; default: REVERT; return ERR_BADZPEEKFMT; } ENSURE_STP_FREE_SPACE((len * 2) + 2); numstrstart = stringpool.free; *stringpool.free++ = '0'; *stringpool.free++ = 'x'; numstrend = op_fnzpeek_hexfmt(stringpool.free, uint64, (len * 2)); ret->str.addr = (char *)numstrstart; ret->str.len = INTCAST(numstrend - numstrstart); stringpool.free = numstrend; break; case 'T': /* Time ($HOROLOG) format */ if ((SIZEOF(uint4) != len) && (SIZEOF(gtm_uint64_t) != len)) return ERR_BADZPEEKFMT; ENSURE_STP_FREE_SPACE(MAXNUMLEN + 1); seconds = (SIZEOF(gtm_uint64_t) == len) ? *(gtm_uint64_t *)zpeekadr : *(uint4 *)zpeekadr; dollarh(seconds, &days, &seconds); ret->str.addr = (char *)stringpool.free; stringpool.free = i2asc(stringpool.free, days); *stringpool.free++ = ','; stringpool.free = i2asc(stringpool.free, (uint4)seconds); ret->str.len = INTCAST((char *)stringpool.free - ret->str.addr); break; case 'Z': /* Hex format (no 0x prefix) of storage as it exists */ if ((len * 2) > MAX_STRLEN) { /* Requested string return is too large */ REVERT; return ERR_MAXSTRLEN; } ENSURE_STP_FREE_SPACE(len * 2); /* Need enough space for hex string */ spfree = stringpool.free; ret->str.addr = (char *)spfree; hexchr = (unsigned char *)zpeekadr; hexchrend = hexchr + len; if (hexchr > hexchrend) /* Wrapped address - range error */ { REVERT; return ERR_BADZPEEKRANGE; } for (; hexchr < hexchrend; ++hexchr) { /* Format 2 digits in each character encountered */ hexc = *hexchr; hexdgt = (hexc & 0xF0) >> 4; FMTHEXDGT(spfree, hexdgt); hexdgt = (hexc & 0x0F); FMTHEXDGT(spfree, hexdgt); } stringpool.free = spfree; /* "commit" string to stringpool */ ret->str.len = len * 2; break; default: REVERT; return ERR_BADZPEEKARG; } REVERT; ret->mvtype = MV_STR; return 0; } /* A condition handler for when we are attaching to either the jnlpool or the gtmrecv pool. We don't * care why we can't get to them. On the fact that we can't is material for $ZPEEK(). */ CONDITION_HANDLER(op_fnzpeek_getpool_ch) { START_CH(TRUE); if (DUMPABLE) NEXTCH; /* Let next (more robust) handler deal with it */ UNWIND(NULL, NULL); } /* Attach to the journal pool. Separate routine so can be wrapped in a condition handler */ STATICFNDEF boolean_t op_fnzpeek_attach_jnlpool(void) { ESTABLISH_RET(op_fnzpeek_getpool_ch, FALSE); jnlpool_init(GTMRELAXED, FALSE, NULL, NULL); /* Attach to journal pool */ REVERT; return pool_init; } /* Attach to the receive pool. Separate routine so can be wrapped in a condition handler */ STATICFNDEF boolean_t op_fnzpeek_attach_recvpool(void) { ESTABLISH_RET(op_fnzpeek_getpool_ch, FALSE); recvpool_init(GTMZPEEK, FALSE); /* Attach to receive pool */ REVERT; return ((NULL != recvpool.recvpool_ctl) && recvpool.recvpool_ctl->initialized); } /* Generalized peek facility: * * structid - String that describes the structure * offset - Offset of item within that structure. * len - Length of the fetch. * format - Option format character - codes described below * ret - Return mval */ void op_fnzpeek(mval *structid, int offset, int len, mval *format, mval *ret) { void *zpeekadr; UINTPTR_T prmpeekadr = 0; struct sigaction new_action, prev_action_bus, prev_action_segv; sigset_t savemask; int errtoraise, rc, rslt; char fmtcode; boolean_t arg_supplied, attach_success; unsigned char mnemonic[NAME_ENTRY_SZ], *nptr, *cptr, *cptrend, *argptr; int mnemonic_len, mnemonic_index, mnemonic_opcode, arglen, arryidx; gd_region *r_top, *r_ptr; replpool_identifier replpool_id; unsigned int full_len; unsigned char argument_uc_buf[ARGUMENT_MAX_LEN]; sgmnt_addrs *csa; int pass; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Make sure lookup table is setup correctly */ assert(zpeek_index[26] == (SIZEOF(zpeek_names) / SIZEOF(nametabent))); assert((SIZEOF(zpeek_names) / SIZEOF(nametabent)) == (SIZEOF(zpeek_data) / SIZEOF(zpeek_data_typ))); /* Initialize */ fmtcode = 'C'; /* If arg is NULL string (noundef default), provide default */ MV_FORCE_STR(structid); MV_FORCE_STR(format); /* Parse and lookup the first arg's mnemonic and arg (if supplied) */ for (nptr = mnemonic, cptr = (unsigned char *)structid->str.addr, cptrend = cptr + structid->str.len; cptr < cptrend; ++cptr) { if (':' == *cptr) break; /* End of mnemonic, start of arg */ *nptr++ = *cptr; } arg_supplied = (cptr < cptrend); mnemonic_len = INTCAST(nptr - mnemonic); mnemonic_index = namelook(zpeek_index, zpeek_names, (char *)mnemonic, mnemonic_len); if (0 > mnemonic_index) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("mnemonic type")); mnemonic_opcode = zpeek_data[mnemonic_index].peekop; if ((arg_supplied && !zpeek_data[mnemonic_index].allowargs) || (!arg_supplied && zpeek_data[mnemonic_index].allowargs)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("mnemonic argument")); if (arg_supplied) { /* Parse supplied argument */ argptr = ++cptr; /* Bump past ":" - if now have end-of-arg then arg is missing */ if (argptr == cptrend) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("mnemonic argument")); arglen = INTCAST(cptrend - cptr); if (ARGUMENT_MAX_LEN < arglen) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("mnemonic argument")); switch(mnemonic_opcode) { case PO_CSAREG: /* These types have a region name argument */ case PO_FHREG: case PO_GDRREG: case PO_NLREG: case PO_JNLREG: case PO_JBFREG: /* Uppercase the region name since that is what GDE does when creating them. * But we want the ability to do $zpeek on statsdb regions (lower-case regions) * so first check if region as is does exist. If so use that. If not, do uppercase. */ assert(arglen); pass = (ISLOWER_ASCII(argptr[0]) ? PASS1 : PASS2); for ( ; ; ) { if (PASS1 != pass) { lower_to_upper(argument_uc_buf, argptr, arglen); argptr = argument_uc_buf; /* Reset now to point to upper case version */ } /* See if region recently used so can avoid lookup */ if ((arglen == TREF(zpeek_regname_len)) && (0 == memcmp(argptr, TADR(zpeek_regname), arglen))) { /* Fast path - no lookup necessary */ r_ptr = TREF(zpeek_reg_ptr); break; } /* Region now defined - make sure it is open */ if (!gd_header) /* If gd_header is NULL, open gbldir */ gvinit(); r_ptr = gd_header->regions; for (r_top = r_ptr + gd_header->n_regions; ; r_ptr++) { if (r_ptr >= r_top) { if (PASS1 == pass) break; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("mnemonic argument (region name)")); } if ((r_ptr->rname_len == arglen) && (0 == memcmp(r_ptr->rname, argptr, arglen))) break; } if (r_ptr == r_top) { /* Could not find lower-case region specified. Try upper-casing it. * Note that even though GDE guarantees all upper-case region names have a corresponding * lower-case region name, it is possible the user specified a region name with * a lower-case letter as the first letter and at least one upper-case letter in the * region name. In that case, it is possible we find the region in the gld only when we * convert the entire region name into upper case. */ assert(PASS1 == pass); pass = PASS2; continue; } /* Cache new region access for followup references */ memcpy(TADR(zpeek_regname), argptr, arglen); TREF(zpeek_regname_len) = arglen; TREF(zpeek_reg_ptr) = r_ptr; break; } /* PO_GDRREG opcode examines only the region's fields so does not need the region to be open. * All the rest need it to be open. If there are any errors in the open (e.g. statsdb specified * and gtm_statsdir env var is too long etc.) then handle it by issuing an error. */ if ((PO_GDRREG != mnemonic_opcode) && !r_ptr->open) { gv_init_reg(r_ptr, NULL); if (!r_ptr->open) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("mnemonic argument (region name could not be opened)")); } break; case PO_GLFREPL: /* These types have an array index argument */ case PO_GSLREPL: arryidx = asc2i(argptr, arglen); if ((0 > arryidx) || (NUM_GTMSRC_LCL <= arryidx)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("mnemonic argument (array index)")); break; case PO_PEEK: /* Argument is address of form 0Xhhhhhhhh[hhhhhhhh] */ if (('0' != *cptr++) || ('x' != *cptr) && ('X' != *cptr)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("mnemonic argument (peek base address)")); cptr++; /* Bump past 'x' or 'X' - rest of arg should be hex value */ prmpeekadr = (UINTPTR_T)GTM64_ONLY(asc_hex2l)NON_GTM64_ONLY(asc_hex2i)(cptr, arglen - 2); if (-1 == (INTPTR_T)prmpeekadr) /* Either an error occurred or the user specified the maximum address. So it's * either an error from the conversion routine or an otherwise useless value. */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("mnemonic argument (peek base address)")); break; default: assert(FALSE); /* Only the above types should ever have an argument */ } } /* Figure out the address of each block to return */ switch(mnemonic_opcode) { case PO_CSAREG: /* r_ptr set from option processing */ zpeekadr = &FILE_INFO(r_ptr)->s_addrs; break; case PO_FHREG: /* r_ptr set from option processing */ zpeekadr = (&FILE_INFO(r_ptr)->s_addrs)->hdr; break; case PO_GDRREG: /* r_ptr set from option processing */ assert(arg_supplied); /* 4SCA: Assigned value is garbage or undefined, even though args are required */ zpeekadr = r_ptr; break; case PO_NLREG: /* r_ptr set from option processing */ zpeekadr = (&FILE_INFO(r_ptr)->s_addrs)->nl; break; case PO_JNLREG: /* r_ptr set from option processing */ case PO_JBFREG: csa = &FILE_INFO(r_ptr)->s_addrs; if (NULL == csa->jnl) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZPEEKNOJNLINFO, 2, REG_LEN_STR(r_ptr)); zpeekadr = (PO_JNLREG == mnemonic_opcode) ? (void *)csa->jnl : (void *)csa->jnl->jnl_buff; break; case PO_GLFREPL: /* This set of opcodes all require the journal pool to be initialized. Verify it */ case PO_GSLREPL: case PO_JPCREPL: case PO_NLREPL: case PO_RIHREPL: /* Make sure jnlpool_addrs are availble */ if (!REPL_INST_AVAILABLE(NULL)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZPEEKNORPLINFO); if (!pool_init) { attach_success = op_fnzpeek_attach_jnlpool(); if (!attach_success) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZPEEKNORPLINFO); } switch(mnemonic_opcode) { case PO_GLFREPL: /* arryidx set by option processing */ zpeekadr = (jnlpool->gtmsrc_lcl_array + arryidx); break; case PO_GSLREPL: /* arryidx set by option processing */ zpeekadr = (jnlpool->gtmsource_local_array + arryidx); break; case PO_NLREPL: zpeekadr = (&FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs)->nl; break; case PO_JPCREPL: zpeekadr = jnlpool->jnlpool_ctl; break; case PO_RIHREPL: zpeekadr = jnlpool->repl_inst_filehdr; break; default: assert(FALSE); } break; case PO_RPCREPL: /* This set of opcodes all require the receive pool to be initialized. Verify it */ case PO_GRLREPL: case PO_UPLREPL: case PO_UHCREPL: /* Make sure recvpool_addrs are available */ if (!REPL_INST_AVAILABLE(NULL)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZPEEKNORPLINFO); if (NULL == recvpool.recvpool_ctl) { attach_success = op_fnzpeek_attach_recvpool(); if (!attach_success) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZPEEKNORPLINFO); } switch(mnemonic_opcode) { case PO_RPCREPL: zpeekadr = recvpool.recvpool_ctl; break; case PO_GRLREPL: zpeekadr = recvpool.gtmrecv_local; break; case PO_UPLREPL: zpeekadr = recvpool.upd_proc_local; break; case PO_UHCREPL: zpeekadr = recvpool.upd_helper_ctl; break; default: assert(FALSE); } break; case PO_PEEK: /* prmpeekadr set up in argument processing */ assert(prmpeekadr); /* 4SCA: Assigned value is garbage or undefined */ zpeekadr = (void *)prmpeekadr; break; default: assert(FALSE); } assert(NULL != zpeekadr); /* Check the rest of the args */ if (0 > offset) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("offset")); zpeekadr = (void *)((char *)zpeekadr + offset); if ((0 > len) || (MAX_STRLEN < len)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("length")); if (1 < format->str.len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("format")); else if (1 == format->str.len) { /* Validate format option */ fmtcode = *format->str.addr; fmtcode = lower_to_upper_table[fmtcode]; switch(fmtcode) { case 'C': /* Character data - returned as is */ case 'I': /* Signed integer format - up to 31 bits */ case 'S': /* String data - Same as 'C' except string is NULL terminated */ case 'T': /* Time ($H) format - length must be of 4 or 8 byte unsigned int */ case 'U': /* Unsigned integer format - up to 64 bits */ case 'X': /* Humeric hex format: e.g. 0x12AB. Total length is (2 * bytes) + 2 */ case 'Z': /* Hex format - not treated as numeric. Shown as occurs in memory (subject to endian) * and is returned with no 0x prefix. */ break; default: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("format")); } } /* Block out timer calls that might trigger processing that could fail. We especially want to prevent * nesting of signal handlers since the longjump() function used by the UNWIND macro is undefined on * Tru64 when signal handlers are nested. */ SIGPROCMASK(SIG_BLOCK, &blockalrm, &savemask, rc); /* Setup new signal handler to just drive condition handler which will do the right thing */ memset(&new_action, 0, SIZEOF(new_action)); sigemptyset(&new_action.sa_mask); new_action.sa_flags = SA_SIGINFO; # ifdef __sparc new_action.sa_handler = op_fnzpeek_signal_handler; # else new_action.sa_sigaction = op_fnzpeek_signal_handler; # endif sigaction(SIGBUS, &new_action, &prev_action_bus); sigaction(SIGSEGV, &new_action, &prev_action_segv); /* Attempt to copy return string to stringpool which protected by our handlers. If the copy completes, the return * mval is updated to point to the return string. Even errors return here so these sigactions can be reversed. */ errtoraise = op_fnzpeek_stpcopy(zpeekadr, len, ret, fmtcode); /* Can restore handlers now that access verified */ sigaction(SIGBUS, &prev_action_bus, NULL); sigaction(SIGSEGV, &prev_action_segv, NULL); /* Let the timers pop again.. */ SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc); /* If we didn't complete correctly, raise error */ if (0 != errtoraise) { /* The only time ERR_BADZPEEKARG is driven is when the format code is not recognized so give that error * specifically with the additional args. Else just raise the error. */ if (ERR_BADZPEEKARG == errtoraise) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) errtoraise, 2, RTS_ERROR_LITERAL("format")); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errtoraise); } return; } fis-gtm-V7.0-005/sr_unix/op_fnzpid.c0000755000032200000250000000106314342376330016200 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "op.h" void op_fnzpid(mint boolexpr,mval *ret) { assert (FALSE); } fis-gtm-V7.0-005/sr_unix/op_fnzpriv.c0000755000032200000250000000106014342376330016401 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "op.h" void op_fnzpriv(mval *prv,mval *ret) { assert (FALSE); } fis-gtm-V7.0-005/sr_unix/op_fnzsearch.c0000644000032200000250000002727514342376330016703 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_stat.h" #include "gtm_facility.h" #include "gtm_stdlib.h" #include "gtm_limits.h" #include "gtm_unistd.h" #include #include #include #include "error.h" #include "parse_file.h" #include "eintr_wrappers.h" #include "lv_val.h" #include "stringpool.h" #include "op.h" #include "mvalconv.h" #include "gdsroot.h" #include "gdsbt.h" #include "have_crit.h" #include "op_fnzsearch.h" LITREF mval literal_null; STATICFNDCL int pop_top(lv_val *src, mval *res, mint mfunc); error_def(ERR_INVSTRLEN); error_def(ERR_MEMORY); error_def(ERR_ZSRCHSTRMCT); /* This routine is invoked on $ZSEARCH() and ZRUPDATE commands as well as when compiling a source file (via compile_source_file()). * The main purpose of the routine is to return the full path to a file that corresponds to the specified pattern. The pattern may * be absolute (starting with '/') or relative and may include wildcard characters ('*' and '?' for multi- and single-character * replacements) and environment variables. * * To traverse all files matching the specified pattern, in collating sequence, the function may be invoked consecutively with the * same argument. Once the list of matching files is exhaused (or if the query did not yield any results), an empty string is * returned. The implementation relies on a local M variable, referenced by the fnzsearch_lv_vars global, to store the results of * the first invocation with a new pattern; subsequent invocations use the pop_top() function to $ORDER() to, and KILL, the first * found entry. * * The function supports 256 individual search "streams," allowing to maintain results of various searches independently. Each * stream is identified by an integer in the range of [0; 255]. Negative numbers have been adopted for internal callers to avoid * interference with user-initiated searches. * * Parameters: * pattern - search pattern, such as 'a.*', '/etc/lib*.?', or 'file-1'. * indx - search stream number, between 0 and 255, inclusive. * mfunc - indication of whether the caller is M code or an internal function, 0 being the latter. * ret - full path to the first matching file in collating sequence. * * Returns: an integer encoded in plength format that contains in each of its bytes the length of one of the matching entry's * characteristics: length of the directory path, length of the (file) name, and length of the extension. For more details, * refer to parse_file.h. */ int op_fnzsearch(mval *pattern, mint indx, mint mfunc, mval *ret) { plength pret; char pblk_buf[GTM_PATH_MAX], sanitized_buf[GTM_PATH_MAX]; char *match, *buf_ptr; int i, status, length; mval file; parse_blk pblk; lv_val *var_ref; plength *match_len; glob_t globbuf; boolean_t absolute; intrpt_state_t prev_intrpt_state; #ifdef _AIX boolean_t use_stat; struct stat statbuf; #endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (mfunc && ((MAX_STRM_CT <= indx) || (0 > indx))) /* Allow an out-of-range stream only if used internally. */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZSRCHSTRMCT); ESTABLISH_RET(fnzsrch_ch, -1); TREF(fnzsearch_nullsubs_sav) = TREF(lv_null_subs); TREF(lv_null_subs) = LVNULLSUBS_OK; /* $ZSearch processing depends on this. */ MV_FORCE_STR(pattern); if (MAX_FN_LEN < pattern->str.len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_INVSTRLEN, 2, pattern->str.len, MAX_FN_LEN); MV_FORCE_MVAL(((mval *)TADR(fnzsearch_sub_mval)), indx); TREF(fnzsearch_lv_vars) = op_srchindx(VARLSTCNT(2) TREF(zsearch_var), (mval *)TADR(fnzsearch_sub_mval)); if (TREF(fnzsearch_lv_vars)) { /* If the parameter is different, kill the local with previous results. */ assert((TREF(fnzsearch_lv_vars))->v.mvtype & MV_STR); if ((pattern->str.len != (TREF(fnzsearch_lv_vars))->v.str.len) || memcmp(pattern->str.addr, (TREF(fnzsearch_lv_vars))->v.str.addr, pattern->str.len)) { op_kill(TREF(fnzsearch_lv_vars)); TREF(fnzsearch_lv_vars) = NULL; } } ret->mvtype = MV_STR; if ((0 != pattern->str.len) && !TREF(fnzsearch_lv_vars)) { memset(&pblk, 0, SIZEOF(pblk)); pblk.buffer = pblk_buf; pblk.buff_size = MAX_FN_LEN; if (parse_file(&pattern->str, &pblk) & 1) { /* Establish new search context. */ TREF(fnzsearch_lv_vars) = op_putindx(VARLSTCNT(2) TREF(zsearch_var), TADR(fnzsearch_sub_mval)); (TREF(fnzsearch_lv_vars))->v = *pattern; /* zsearch_var(indx)=original spec */ if (0 != pblk.b_esl) { /* Create a NULL-terminated buffer with the pattern to be passed to glob(). If we are dealing with a * relative-path pattern, prepend it with the absolute path of the working directory first. */ if ('/' != pblk_buf[0]) { if (NULL == getcwd(sanitized_buf, ARRAYSIZE(sanitized_buf))) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); else { length = STRLEN(sanitized_buf); if (MAX_FN_LEN < length + 1 + pblk.b_esl) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_INVSTRLEN, 2, length + 1 + pblk.b_esl, MAX_FN_LEN); sanitized_buf[length] = '/'; } buf_ptr = sanitized_buf + length + 1; } else buf_ptr = sanitized_buf; /* Escape '[' and ']' for glob() processing (because $zsearch() does not support such sets). Make * sure that we have enough storage room (the string includes the length of our working directory, * if prepended, the escaped pattern, which could be twice as long as the original, and a trailing * '\0' character). */ assert(ARRAYSIZE(sanitized_buf) >= (buf_ptr - sanitized_buf) + 2 * pblk.b_esl + 1); pblk_buf[pblk.b_esl] = '\0'; ESCAPE_BRACKETS(pblk_buf, buf_ptr); /* Canonicalize the path by appropriately removing '.' and '..' path modifiers. */ CANONICALIZE_PATH(buf_ptr); /* Do not sort the matches because we use $order() to obtain them from a local anyway. */ DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); #ifdef _AIX use_stat = !((pblk.fnb & (1 << V_WILD_NAME)) || (pblk.fnb & (1 << V_WILD_DIR))); if (use_stat) { STAT_FILE(sanitized_buf, &statbuf, status); if (!status) globbuf.gl_pathc = 1; } else #endif status = glob(sanitized_buf, LINUX_ONLY(GLOB_PERIOD | ) GLOB_NOSORT, (int (*)(const char *, int))NULL, &globbuf); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); if (0 == status) { TREF(fnzsearch_globbuf_ptr) = &globbuf; file.mvtype = MV_STR; for (i = 0; i < globbuf.gl_pathc; i++) { /* We do not care for . and .. */ #ifdef _AIX if (use_stat) match = sanitized_buf; else #endif match = globbuf.gl_pathv[i]; length = STRLEN(match); if ((length > 1) && ('.' == match[length - 1]) && (('/' == match[length - 2]) || ((length > 2) && ('.' == match[length - 2]) && ('/' == match[length - 3])))) continue; /* If the resolved length is too long to be used in a local, skip it. */ if (MAX_FN_LEN < length) continue; ENSURE_STP_FREE_SPACE(length); file.str.addr = match; file.str.len = length; s2pool(&file.str); var_ref = op_putindx(VARLSTCNT(2) TREF(fnzsearch_lv_vars), &file); var_ref->v.mvtype = MV_STR; var_ref->v.str.len = 0; match_len = (plength *)&(var_ref->v.m[1]); SET_LENGTHS(match_len, file.str.addr, length, TRUE); } #ifdef _AIX if (!use_stat) { #endif DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); globfree(&globbuf); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); TREF(fnzsearch_globbuf_ptr) = NULL; #ifdef _AIX } #endif } else { #ifdef _AIX if (!use_stat) { #endif DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); globfree(&globbuf); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); TREF(fnzsearch_globbuf_ptr) = NULL; if (GLOB_NOSPACE == status) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ENOMEM); /* Ran out of memory. */ else if (GLOB_ABORTED == status) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) EACCES); /* Access error. */ else assert(GLOB_NOMATCH == status); /* No matches found. */ #ifdef _AIX } #endif } } } } /* If we have placed something into a local (now or in a prior invocation), obtain it. */ if (TREF(fnzsearch_lv_vars)) pret.p.pint = pop_top(TREF(fnzsearch_lv_vars), ret, mfunc); else { ret->str.len = 0; pret.p.pint = 0; } assert((0 == ret->str.len) || (pret.p.pblk.b_esl == ret->str.len)); TREF(lv_null_subs) = TREF(fnzsearch_nullsubs_sav); REVERT; return pret.p.pint; } /* Condition handler for the op_fnzsearch() operation. It takes care of restoring the lv_null_subs value and freeing the glob() * buffer, if necessary. */ CONDITION_HANDLER(fnzsrch_ch) { START_CH(TRUE); /* START_CH() defines prev_intrpt_state */ if (NULL != TREF(fnzsearch_globbuf_ptr)) { DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); globfree(TREF(fnzsearch_globbuf_ptr)); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); TREF(fnzsearch_globbuf_ptr) = NULL; } TREF(lv_null_subs) = TREF(fnzsearch_nullsubs_sav); NEXTCH; } /* This routine clears the cached search results on a particular "stream." * * Parameters: * indx - search stream number, between 0 and 255, inclusive. */ void zsrch_clr(int indx) { lv_val *tmp; mval x; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; MV_FORCE_MVAL(&x, indx); tmp = op_srchindx(VARLSTCNT(2) TREF(zsearch_var), &x); op_kill(tmp); } /* This routine returns the value stored at the first subscript (in collating sequence) of the passed M local. It also sets one of * the passed arguments to the string containing the subscript. Its main use is to aid op_fnzsearch() in traversing through the list * of previously cached search results for a particular pattern. The function also verifies that the file it returns still exists. * If not, it skips it. * * Parameters: * src - pointer to the local that is to be $ORDER()ed. * res - pointer to an mval where the first valid subscript is to be placed. * * Returns: the value stored at the first subscript in collating sequence. It is an integer encoded in plength format that contains * in each of its bytes the length of one of the matching entry's characteristics: length of the directory path, length of * the (file) name, and length of the extension. For more details, refer to parse_file.h. */ STATICFNDEF int pop_top(lv_val *src, mval *res, mint mfunc) { lv_val *tmp; plength pret; struct stat statbuf; int stat_res; char file_name[MAX_FN_LEN + 1]; while (TRUE) { op_fnorder(src, (mval *)&literal_null, res); if (res->str.len) { tmp = op_getindx(VARLSTCNT(2) src, res); assert(MAX_FN_LEN >= res->str.len); pret.p.pint = tmp->v.m[1]; op_kill(tmp); /* Remove this element from the tree. */ memcpy(file_name, res->str.addr, res->str.len); file_name[res->str.len] = '\0'; STAT_FILE(file_name, &statbuf, stat_res); if (-1 == stat_res) { if ((ENOENT != errno) && (mfunc || (ELOOP != errno))) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); continue; } } else { pret.p.pint = 0; op_kill(src); } break; } return pret.p.pint; } fis-gtm-V7.0-005/sr_unix/op_fnzsetprv.c0000755000032200000250000000106214342376330016746 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "op.h" void op_fnzsetprv(mval *prv,mval *ret) { assert (FALSE); } fis-gtm-V7.0-005/sr_unix/op_fnzsyslog.c0000644000032200000250000000277414342376330016753 0ustar librarygtc /**************************************************************** * * * Copyright (c) 2014-2022 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_string.h" #include "gtm_multi_thread.h" #include "min_max.h" #include "op.h" #include "util.h" LITREF mval literal_one; void op_fnzsyslog(mval* src, mval* dst) { char rebuff[OUT_BUFF_SIZE]; int len; char *save_util_outptr; va_list save_last_va_list_ptr; boolean_t util_copy_saved = FALSE; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; MV_FORCE_STR(src); len = MIN(src->str.len, OUT_BUFF_SIZE - 1); if (0 < len) { ASSERT_SAFE_TO_UPDATE_THREAD_GBLS; if ((NULL != TREF(util_outptr)) && (TREF(util_outptr) != TREF(util_outbuff_ptr))) { SAVE_UTIL_OUT_BUFFER(save_util_outptr, save_last_va_list_ptr, util_copy_saved); } memcpy(rebuff, src->str.addr, len); /* Rebuffer to add null terminator */ rebuff[len] = '\0'; /* Add null terminator */ util_out_print_args(NULL, 0, RESET); util_out_print_args(rebuff, 0, OPER); RESTORE_UTIL_OUT_BUFFER(save_util_outptr, save_last_va_list_ptr, util_copy_saved); } memcpy(dst, &literal_one, SIZEOF(mval)); } fis-gtm-V7.0-005/sr_unix/op_fnztrnlnm.c0000644000032200000250000000550614342376330016741 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "gtm_string.h" #include "gtm_stdlib.h" #include "stringpool.h" #include "nametabtyp.h" #include "op.h" #include "namelook.h" #include "mvalconv.h" GBLREF spdesc stringpool; error_def(ERR_INVSTRLEN); error_def(ERR_BADTRNPARAM); error_def(ERR_MAXSTRLEN); #define FULL 3 #define LENGTH 4 #define NO_ALIAS 6 #define TABLE_NAME 8 #define TERMINAL 9 #define VALUE 10 static readonly nametabent trnitm_table[] = { { 11, "ACCESS_MODE"}, { 9, "CONCEALED"}, { 7, "CONFINE"}, { 3, "FUL*"}, { 3, "LEN*"}, { 9, "MAX_INDEX"}, { 8, "NO_ALIAS"}, { 5, "TABLE"}, { 10, "TABLE_NAME"}, { 3, "TER*"}, { 3, "VAL*"} }; static readonly unsigned char trnitm_index[] = { 0, 1, 1, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 6, 7, 7, 7, 7, 7, 7, 10, 10, 11, 11, 11, 11 }; void op_fnztrnlnm(mval *name, mval *table, int4 ind, mval *mode, mval *case_blind, mval *item, mval *ret) { char buf[MAX_TRANS_NAME_LEN]; char *status; int item_code; uint4 retlen; MV_FORCE_STR(name); if (name->str.len >= MAX_TRANS_NAME_LEN) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_INVSTRLEN, 2, name->str.len, MAX_TRANS_NAME_LEN - 1); MV_FORCE_STR(item); if (item->str.len) { if ((item_code = namelook(trnitm_index, trnitm_table, item->str.addr, item->str.len)) < 0) /* NOTE assignment */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADTRNPARAM,2,item->str.len,item->str.addr); } else item_code = VALUE; ret->mvtype = MV_STR; memcpy(buf, name->str.addr, name->str.len); buf[name->str.len] = 0; status = GETENV(buf); switch (item_code) { case FULL: case VALUE: if (status) { retlen = (uint4)STRLEN(status); if (MAX_STRLEN < retlen) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXSTRLEN); ENSURE_STP_FREE_SPACE(retlen); ret->str.addr = (char *)stringpool.free; ret->str.len = retlen; memcpy(ret->str.addr, status, retlen); stringpool.free += retlen; } else ret->str.len = 0; break; case LENGTH: if (status) { MV_FORCE_MVAL(ret, (int)STRLEN(status)); n2s(ret); } else ret->str.len = 0; break; case NO_ALIAS: case TERMINAL: MV_FORCE_MVAL(ret, ((NO_ALIAS == item_code) || status) ? 1 : 0); n2s(ret); break; default: if ((status) && (TABLE_NAME != item_code)) { MV_FORCE_MVAL(ret, 0); n2s(ret); } else ret->str.len = 0; } return; } fis-gtm-V7.0-005/sr_unix/op_job.c0000644000032200000250000002613514342376330015464 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_stdio.h" #include #include #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 "deferred_events_queue.h" #include "op.h" #include "io.h" #include "mvalconv.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "gdscc.h" #include "filestruct.h" #include "buddy_list.h" /* needed for tp.h */ #include "jnl.h" #include "tp.h" #include "send_msg.h" #include "gtmmsg.h" /* for gtm_putmsg() prototype */ #include "change_reg.h" #include "setterm.h" #include "getzposition.h" #include "iosocketdef.h" #include "min_max.h" #ifdef DEBUG #include "have_crit.h" /* for the TPNOTACID_CHECK macro */ #endif void job_timer_handler(void); GBLDEF short jobcnt = 0; GBLDEF int job_errno; GBLDEF volatile boolean_t ojtimeout = TRUE; GBLREF d_socket_struct *socket_pool; GBLREF int dollar_truth; GBLREF uint4 dollar_trestart, dollar_zjob; GBLREF volatile int4 outofband; static int4 tid; /* Job Timer ID */ LITREF mval skiparg; error_def(ERR_JOBFAIL); error_def(ERR_JOBLVN2LONG); error_def(ERR_NULLENTRYREF); error_def(ERR_TEXT); #define JOBTIMESTR "JOB" /* * --------------------------------------------------- * 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; int4 offset; mval *routine, *param_buf; mval *timeout; /* timeout in milliseconds */ 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; mstr_len_t handle_len; int4 index; job_buffer_size_msg buffer_size; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; 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, mval *); /* in milliseconds */ 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_CSA_ABT(NULL, 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.params.routine.len = routine->str.len; memcpy(job_params.params.routine.buffer, routine->str.addr, job_params.params.routine.len); job_params.params.label.len = label->str.len; memcpy(job_params.params.label.buffer, label->str.addr, job_params.params.label.len); job_params.params.offset = offset; ojparams(param_buf->str.addr, &job_params); /* * Verify that entryref to JOB command is not NULL. */ if (!job_params.params.routine.len) { va_end(var); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_JOBFAIL, 0, ERR_NULLENTRYREF, 0); } /* Start the timer */ ojtimeout = timed = FALSE; MV_FORCE_MSTIMEOUT(timeout, msec_timeout, JOBTIMESTR); if ((0 < msec_timeout) && (NO_M_TIMEOUT != msec_timeout)) { timed = TRUE; start_timer((TID)&tid, msec_timeout, job_timer_handler, 0, NULL); } if (argcnt) { jp = job_params.parms = (job_parm *)malloc(SIZEOF(job_parm) * argcnt); i = argcnt; for(;;) { jp->parm = va_arg(var, mval *); if (!M_ARG_SKIPPED(jp->parm)) MV_FORCE_STR(jp->parm); if (0 == --i) break; jp->next = jp + 1; jp = jp->next; } jp->next = 0; } else job_params.parms = 0; va_end(var); /* Setup parameters and start the job */ job_errno = -1; non_exit_return = FALSE; status = ojstartchild(&job_params, argcnt, &non_exit_return, pipe_fds); /* the child process (M), that wrote to pipe, would have been exited by now. Close the write-end to make the following read * non-blocking. also resets "pipe_fds[1]" to FD_INVALID */ CLOSEFILE_RESET(pipe_fds[1], pipe_status); /* Do cleanup here so we avoid leaks in case we hit an error below */ if (timed && !ojtimeout) cancel_timer((TID)&tid); if (argcnt) free(job_params.parms); if (job_params.passcurlvn) system_free(job_params.curlvn_buffer_ptr); /* Space allocated by open_memstream in ojparams */ if (!non_exit_return) { #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 * argument. */ #else wait_stat = status; #endif exit_stat = WIFEXITED(wait_stat) ? WEXITSTATUS(wait_stat) : 0; /* Middle process uses pipe(pipe_fds) * a) to communicate a PID of grandchild to its parent process(i.e. current process) * b) to communicate an errno to current process if any required setup for the grandchild is failed. * exit status joberr_pipe_mp of middle process means it failed WRITE operation on pipe used to communicate * grandchild's PID to current process. In this scenario, grandchild is terminated and middle process do not * communicate errno to current process. Hence this process do not read the errno for joberr_pipe_mp exit status. */ if (status && joberr_pipe_mp != exit_stat) { DOREADRC(pipe_fds[0], &job_errno, SIZEOF(job_errno), pipe_status); if (0 < pipe_status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2, joberrs[exit_stat].len, joberrs[exit_stat].msg, errno); if (ERR_JOBLVN2LONG == job_errno) { /* This message takes buffer_size as argument so take it before closing the pipe */ DOREADRC(pipe_fds[0], &buffer_size, SIZEOF(buffer_size), pipe_status); if (0 < pipe_status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2, LEN_AND_LIT("Error reading buffer_size from pipe after a JOBLVN2LONG error"), errno); } } } assert(SIZEOF(pid_t) == SIZEOF(zjob_pid)); DOREADRC(pipe_fds[0], &zjob_pid, SIZEOF(zjob_pid), pipe_status); /* empty pipe (pipe_status == -1) is ignored and not reported as error */ if (0 < pipe_status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2, LEN_AND_LIT("Error reading zjobid from pipe"), errno); /* release the pipe; also resets "pipe_fds[0]" to FD_INVALID */ CLOSEFILE_RESET(pipe_fds[0], pipe_status); 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_CSA_ABT(NULL, 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); assert(WBTEST_ENABLED(WBTEST_JOBFAIL_FILE_LIM)); RTS_ERROR_CSA_ABT(NULL, 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); assert(FALSE); RTS_ERROR_CSA_ABT(NULL, 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 */ { if ((-1 == job_errno) || (0 == job_errno)) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_JOBFAIL, 0, ERR_TEXT, 2, joberrs[exit_stat].len, joberrs[exit_stat].msg); } else if (ERR_JOBLVN2LONG == job_errno) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_JOBLVN2LONG, 2, MAX_STRLEN, buffer_size); } else { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2, joberrs[exit_stat].len, joberrs[exit_stat].msg, job_errno); } } else /* unknown exit status */ { assert(FALSE); util_out_print("Unknown exit status !UL (status = !UL)", TRUE, exit_stat, status); RTS_ERROR_CSA_ABT(NULL, 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_CSA_ABT(NULL, 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; if (IS_JOB_SOCKET(job_params.params.input.buffer, job_params.params.input.len)) { handle_len = JOB_SOCKET_HANDLE_LEN(job_params.params.input.len); index = iosocket_handle(JOB_SOCKET_HANDLE(job_params.params.input.buffer), &handle_len, FALSE, socket_pool); if (-1 != index) iosocket_close_one(socket_pool, index); } if (IS_JOB_SOCKET(job_params.params.output.buffer, job_params.params.output.len)) { handle_len = JOB_SOCKET_HANDLE_LEN(job_params.params.output.len); index = iosocket_handle(JOB_SOCKET_HANDLE(job_params.params.output.buffer), &handle_len, FALSE, socket_pool); if (-1 != index) iosocket_close_one(socket_pool, index); } if (IS_JOB_SOCKET(job_params.params.error.buffer, job_params.params.error.len)) { handle_len = JOB_SOCKET_HANDLE_LEN(job_params.params.error.len); index = iosocket_handle(JOB_SOCKET_HANDLE(job_params.params.error.buffer), &handle_len, FALSE, socket_pool); if (-1 != index) iosocket_close_one(socket_pool, index); } return TRUE; } return FALSE; /* This will never get executed, added to make compiler happy */ } fis-gtm-V7.0-005/sr_unix/op_setextract.c0000644000032200000250000001062614342376330017076 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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_string.h" #include "stringpool.h" #include "min_max.h" #include "op.h" #include "gtm_utf8.h" GBLREF spdesc stringpool; GBLREF boolean_t badchar_inhibit; error_def(ERR_MAXSTRLEN); /* * ---------------------------------------------------------- * Set version of $extract * * Arguments: * src - source mval * expr - expression string mval to be inserted into source * schar - starting character index to be replaced * echar - ending character index to be replaced * dst - destination mval where the result is saved. * * Return: * none * ---------------------------------------------------------- */ void op_setextract(mval *src, mval *expr, int schar, int echar, mval *dst) { size_t dstlen, padlen; int pfxlen, sfxoff, sfxlen, skip, bytelen, srclen, char_len; unsigned char *srcptr, *srcbase, *srctop, *straddr; padlen = pfxlen = sfxlen = 0; MV_FORCE_STR(expr); /* Expression to put into piece place */ if (MV_DEFINED(src)) { MV_FORCE_STR(src); /* Make sure is string prior to length check */ srclen = src->str.len; } else /* Source is not defined -- treat as a null string */ srclen = 0; schar = MAX(schar, 1); /* schar starts at 1 at a minimum */ /* There are four cases in the spec: * 1) schar > echar or echar < 1 -- glvn and naked indicator are not changed. This is * handled by generated code in m_set * 2) echar >= schar-1 > $L(src) -- dst = src_$J("",schar-1-$L(src))_expr * 3) schar-1 <= $L(src) < echar -- dst = $E(src,1,schar-1)_expr * 4) All others -- dst = $E(src,1,schar-1)_expr_$E(src,echar+1,$L(src)) */ srcbase = (unsigned char *)src->str.addr; srctop = srcbase + srclen; for (srcptr = srcbase, skip = schar - 1; ((0 < skip) && (srcptr < srctop)); --skip) { /* Skip the first schar - 1 characters */ if (!UTF8_VALID(srcptr, srctop, bytelen) && !badchar_inhibit) utf8_badchar(0, srcptr, srctop, 0, NULL); srcptr += bytelen; } pfxlen = INTCAST(srcptr - srcbase); if (0 < skip) /* Case #2: schar is past the string length. echar test handled in generated code. * Should be padded with as many spaces as characters remained to be skipped */ padlen = (size_t)skip; for (skip = echar - schar + 1; (0 < skip) && (srcptr < srctop); --skip) { /* skip up to the character position echar */ if (!UTF8_VALID(srcptr, srctop, bytelen) && !badchar_inhibit) utf8_badchar(0, srcptr, srctop, 0, NULL); srcptr += bytelen; } char_len = 0; if (0 >= skip) { /* Case #4: echar is within the string length, suffix to be added */ sfxoff = INTCAST(srcptr - srcbase); sfxlen = INTCAST(srctop - srcptr); if (!badchar_inhibit && (0 str.char_len; } } /* Calculate total string len */ dstlen = (size_t)pfxlen + padlen + (size_t)expr->str.len + (size_t)sfxlen; if (MAX_STRLEN < dstlen) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXSTRLEN); ENSURE_STP_FREE_SPACE((int)dstlen); srcbase = (unsigned char *)src->str.addr; straddr = stringpool.free; if (0 < pfxlen) { /* Copy prefix */ memcpy(straddr, srcbase, pfxlen); straddr += pfxlen; } if (0 < padlen) { /* Insert padding */ memset(straddr, ' ', padlen); straddr += padlen; } if (0 < expr->str.len) { /* Copy expression */ memcpy(straddr, expr->str.addr, expr->str.len); straddr += expr->str.len; } if (0 < sfxlen) { /* Copy suffix */ memcpy(straddr, srcbase + sfxoff, sfxlen); straddr += sfxlen; } assert(IS_AT_END_OF_STRINGPOOL(straddr, -dstlen)); MV_INIT_STRING(dst, straddr - stringpool.free, (char *)stringpool.free); if (0 < char_len) { dst->mvtype |= MV_UTF_LEN; dst->str.char_len = char_len; } stringpool.free = straddr; } fis-gtm-V7.0-005/sr_unix/op_setp1.c0000644000032200000250000003016314342376330015742 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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_string.h" #include "stringpool.h" #include "min_max.h" #include "fnpc.h" #include "op.h" #include "gtm_utf8.h" GBLREF spdesc stringpool; GBLREF boolean_t gtm_utf8_mode; /* We are indeed doing the UTF8 thang */ GBLREF boolean_t badchar_inhibit; /* No BADCHAR errors should be signaled */ #ifdef DEBUG # define SETWON setp_work = TRUE; # define SETWOFF setp_work = FALSE; #else # define SETWON # define SETWOFF #endif error_def(ERR_MAXSTRLEN); /* * ---------------------------------------------------------- * Fast path setpiece when delimiter is one (lit) char replacing * a single piece (last is same as first). UTF8 flavor. * * Arguments: * src - source mval * delim - delimiter char * expr - expression string mval * ind - index in source mval to be set * dst - destination mval where the result is saved. * * Return: * none * ---------------------------------------------------------- */ void op_setp1(mval *src, int delim, mval *expr, int ind, mval *dst) { size_t str_len, delim_cnt; int len, pfx_str_len, sfx_start_offset, sfx_str_len, rep_str_len, pfx_scan_offset; int dlmlen, cpy_cache_lines, mblen, lmsdelim; unsigned char *start_sfx, *str_addr, *end_pfx, *end_src, *start_pfx; boolean_t do_scan, delim_last_scan, valid_char; mval dummymval; /* It's value is not used but is part of the call to op_fnp1() */ fnpc *cfnpc, *pfnpc; delimfmt ldelim; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(gtm_utf8_mode); do_scan = FALSE; cpy_cache_lines = -1; ldelim.unichar_val = delim; if (!UTF8_VALID(ldelim.unibytes_val, (ldelim.unibytes_val + SIZEOF(ldelim.unibytes_val)), dlmlen) && !badchar_inhibit) { /* The delimiter is a bad character so error out if badchar not inhibited */ UTF8_BADCHAR(0, ldelim.unibytes_val, ldelim.unibytes_val + SIZEOF(ldelim.unibytes_val), 0, NULL); } MV_FORCE_STR(expr); /* Expression to put into piece place */ if (MV_DEFINED(src)) { /* We have 3 possible scenarios: * 1) The source string is null. Nothing to do but proceed to building output. * 2) If the requested piece is larger than can be cached by op_fnp1, call fnp1 * for the maximum piece possible, use the cache info to "prime the pump" and * then process the rest of the string ourselves. * 3) If the requested piece can be obtained from the cache, call op_fnp1 to validate * and rebuild the cache if necessary and then retrieve the necessary info from * the fnpc cache. */ MV_FORCE_STR(src); /* Make sure is string prior to length check */ if (0 == src->str.len) { /* We have a null source string */ pfx_str_len = sfx_str_len = sfx_start_offset = 0; delim_cnt = (0 < ind) ? (size_t)ind - 1 : 0; } else if (FNPC_ELEM_MAX >= ind) { /* 3) Best of all possible cases. The op_fnp1 can do most of our work for us * and we can preload the cache on the new string to help its subsequent * uses along as well. */ SETWON; op_fnp1(src, delim, ind, &dummymval); SETWOFF; cfnpc = &(TREF(fnpca)).fnpcs[src->fnpc_indx - 1]; assert(cfnpc->last_str.addr == src->str.addr); assert(cfnpc->last_str.len == src->str.len); assert(cfnpc->delim == delim); assert(0 < cfnpc->npcs); /* Three more scenarios: #1 piece all in cache, #2 piece would be in cache but ran * out of text or #3 piece is beyond what can be cached */ if (cfnpc->npcs >= ind) { /* #1 The piece we want is totally within the cache which is good news */ pfx_str_len = cfnpc->pstart[ind - 1]; delim_cnt = 0; sfx_start_offset = cfnpc->pstart[ind] - dlmlen; /* Include delimiter */ rep_str_len = cfnpc->pstart[ind] - cfnpc->pstart[ind - 1] - dlmlen; /* Replace string length */ sfx_str_len = src->str.len - pfx_str_len - rep_str_len; cpy_cache_lines = ind - 1; } else { /* #2 The string was too short so the cache does not contain our string. This means * that the prefix becomes any text that IS in the cache and we set the delim_cnt * to be the number of missing pieces so the delimiters can be put in as part of the * prefix when we build the new string. */ pfx_str_len = cfnpc->pstart[cfnpc->npcs] - dlmlen; delim_cnt = (size_t)(ind - cfnpc->npcs); sfx_start_offset = 0; sfx_str_len = 0; cpy_cache_lines = cfnpc->npcs; } } else { /* 2) We have a element that would not be able to be in the fnpc cache. Go ahead * and call op_fnp1 to get cache info up to the maximum and then we will continue * the scan on our own. */ SETWON; op_fnp1(src, delim, FNPC_ELEM_MAX, &dummymval); SETWOFF; cfnpc = &(TREF(fnpca)).fnpcs[src->fnpc_indx - 1]; assert(cfnpc->last_str.addr == src->str.addr); assert(cfnpc->last_str.len == src->str.len); assert(cfnpc->delim == delim); assert(0 < cfnpc->npcs); if (FNPC_ELEM_MAX > cfnpc->npcs) { /* We ran out of text so the scan is complete. This is basically the same * as case #2 above. */ pfx_str_len = cfnpc->pstart[cfnpc->npcs] - dlmlen; delim_cnt = (size_t)(ind - cfnpc->npcs); sfx_start_offset = 0; sfx_str_len = 0; cpy_cache_lines = cfnpc->npcs; } else { /* We have a case where the piece we want cannot be kept in cache. In the special * case where there is no more text to handle, we don't need to scan further. Otherwise * we prime the pump and continue the scan where the cache left off. */ if ((pfx_scan_offset = cfnpc->pstart[FNPC_ELEM_MAX]) < src->str.len) /* Note assignment */ /* Normal case where we prime the pump */ do_scan = TRUE; else { /* Special case -- no more text to scan */ pfx_str_len = cfnpc->pstart[FNPC_ELEM_MAX] - dlmlen; sfx_start_offset = 0; sfx_str_len = 0; } delim_cnt = (size_t)ind - FNPC_ELEM_MAX; cpy_cache_lines = FNPC_ELEM_MAX; } } } else { /* Source is not defined -- treat as a null string */ pfx_str_len = sfx_str_len = sfx_start_offset = 0; delim_cnt = (size_t)ind - 1; } /* If we have been forced to do our own scan, do that here. Note the variable pfx_scan_offset has been * set to where the scan should begin in the src string and delim_cnt has been set to how many delimiters * still need to be processed. */ if (do_scan) { /* Scan the line isolating prefix piece, and end of the * piece being replaced */ COUNT_EVENT(small); end_pfx = start_sfx = (unsigned char *)src->str.addr + pfx_scan_offset; end_src = (unsigned char *)src->str.addr + src->str.len; /* The compiler would unroll this loop this way anyway but we want to * adjust the start_sfx pointer after the loop but only if we have gone * into it at least once. */ if ((0 < delim_cnt) && (start_sfx < end_src)) { do { end_pfx = start_sfx; delim_last_scan = FALSE; /* Whether delimiter is last character scanned */ while (start_sfx < end_src) { valid_char = UTF8_VALID(start_sfx, end_src, mblen); /* Length of next char */ if (!valid_char) { /* Next character is not valid UTF8. If badchar error is not inhibited, * signal it now. If it is inhibited, just treat the character as a single * character and continue. */ if (!badchar_inhibit) utf8_badchar(0, start_sfx, end_src, 0, NULL); assert(1 == mblen); } /* Getting mblen first allows us to do quick length compare before the * heavier weight memcmp call. */ assert(0 < mblen); if (mblen == dlmlen && 0 == memcmp(start_sfx, ldelim.unibytes_val, dlmlen)) { delim_last_scan = TRUE; break; } /* Increment ptrs by size of found char */ start_sfx += mblen; } start_sfx += dlmlen; delim_cnt--; } while ((0 < delim_cnt) && (start_sfx < end_src)); /* We have to backup up the suffix start pointer except under the condition * that the last character in the buffer is the last delimiter we were looking * for. */ if ((0 == delim_cnt) || (start_sfx < end_src) || !delim_last_scan) start_sfx -= dlmlen; /* Back up suffix to include delimiter char */ /* If we scanned to the end (no text left) and still have delimiters to * find, the entire src text should be part of the prefix */ if ((start_sfx >= end_src) && (0 < delim_cnt)) { end_pfx = start_sfx; if (delim_last_scan) /* if last char was delim, reduce delim cnt */ --delim_cnt; } } else { /* If not doing any token finding, then this count becomes the number * of tokens to output. Adjust accordingly. */ if (0 < delim_cnt) --delim_cnt; } INCR_COUNT(small_pcs, (int)((size_t)ind - delim_cnt)); /* Now having the following situation: * end_pfx -> end of the prefix piece including delimiter * start_sfx -> start of suffix piece (with delimiter) or = end_pfx/src->str.addr if none */ pfx_str_len = (int)(end_pfx - (unsigned char *)src->str.addr); if (0 > pfx_str_len) pfx_str_len = 0; sfx_start_offset = (int)(start_sfx - (unsigned char *)src->str.addr); sfx_str_len = src->str.len - sfx_start_offset; if (0 > sfx_str_len) sfx_str_len = 0; } /* Calculate total string len. delim_cnt has needed padding delimiters for null fields */ str_len = (size_t)expr->str.len + (size_t)pfx_str_len + (delim_cnt * (size_t)dlmlen) + (size_t)sfx_str_len; if (MAX_STRLEN < str_len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXSTRLEN); ENSURE_STP_FREE_SPACE((int)str_len); str_addr = stringpool.free; start_pfx = (unsigned char *)src->str.addr; /* copy prefix */ if (0 < pfx_str_len) { memcpy(str_addr, src->str.addr, pfx_str_len); str_addr += pfx_str_len; } /* copy delimiters */ if (gtm_utf8_mode && (1 < dlmlen)) { /* In this mode, delimiters can exceed 1 character so copy them this way */ while (0 < delim_cnt--) { memcpy(str_addr, ldelim.unibytes_val, dlmlen); str_addr += dlmlen; } } else { /* If delimiters are 1 byte (M mode always and perhaps UTF8 mode), use this simpler/faster method */ lmsdelim = (gtm_utf8_mode) ? ldelim.unibytes_val[0] : delim; memset(str_addr, lmsdelim, delim_cnt); str_addr += delim_cnt; } /* copy expression */ if (0 < expr->str.len) { memcpy(str_addr, expr->str.addr, expr->str.len); str_addr += expr->str.len; } /* copy suffix */ if (0 < sfx_str_len) { memcpy(str_addr, start_pfx + sfx_start_offset, sfx_str_len); str_addr += sfx_str_len; } assert(IS_AT_END_OF_STRINGPOOL(str_addr, -str_len)); dst->mvtype = MV_STR; dst->str.len = INTCAST(str_addr - stringpool.free); dst->str.addr = (char *)stringpool.free; stringpool.free = str_addr; /* If available, update the cache information for this newly created mval to hopefully * give it a head start on its next usage. Note that we can only copy over the cache info * for the prefix. We cannot include information for the 'expression' except where it starts * because the expression could itself contain delimiters that would be found on a rescan. */ if (0 < cpy_cache_lines) { pfnpc = cfnpc; /* pointer for src mval's cache */ do { cfnpc = (TREF(fnpca)).fnpcsteal; /* Next cache element to steal */ if ((TREF(fnpca)).fnpcmax < cfnpc) cfnpc = &(TREF(fnpca)).fnpcs[0]; (TREF(fnpca)).fnpcsteal = cfnpc + 1; /* -> next element to steal */ } while (cfnpc == pfnpc); /* Make sure we don't step on ourselves */ cfnpc->last_str = dst->str; /* Save validation info */ cfnpc->delim = delim; cfnpc->npcs = cpy_cache_lines; dst->fnpc_indx = cfnpc->indx + 1; /* Save where we are putting this element * (1 based index in mval so 0 isn't so common) */ memcpy(&cfnpc->pstart[0], &pfnpc->pstart[0], (cfnpc->npcs + 1) * SIZEOF(unsigned int)); } else /* No cache available -- just reset index pointer to get fastest cache validation failure */ dst->fnpc_indx = (unsigned char)-1; } fis-gtm-V7.0-005/sr_unix/op_setpiece.c0000755000032200000250000001265014342376330016513 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2018 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_string.h" #include "stringpool.h" #include "op.h" #include "matchc.h" #include "fnpc.h" #include "gtm_utf8.h" GBLREF boolean_t gtm_utf8_mode; GBLREF spdesc stringpool; error_def(ERR_MAXSTRLEN); /* -------------------------------------------------------------------- * NOTE: This module is a near copy of sr_port/op_setzpiece.c differing * only in that it calls "matchc" instead of "matchb" to do matching. * -------------------------------------------------------------------- */ /* * ---------------------------------------------------------- * Set piece procedure (UTF8 flavor). * Set pieces first through last to expr. * * Arguments: * src - source mval * del - delimiter string mval * expr - expression string mval * first - starting index in source mval to be set * last - last index * dst - destination mval where the result is saved. * * Return: * none * ---------------------------------------------------------- */ void op_setpiece(mval *src, mval *del, mval *expr, int4 first, int4 last, mval *dst) { size_t str_len, delim_cnt; int match_res, len, src_len, first_src_ind, second_src_ind, numpcs; unsigned char *match_ptr, *src_str, *str_addr, *tmp_str; delimfmt unichar; /* --- code start --- */ assert(gtm_utf8_mode); if (--first < 0) first = 0; second_src_ind = last - first; MV_FORCE_STR(del); /* Null delimiter */ if (0 == del->str.len) { if (first && src->mvtype) { /* concat src & expr to dst */ op_cat(VARLSTCNT(3) dst, src, expr); return; } MV_FORCE_STR(expr); *dst = *expr; return; } MV_FORCE_STR(expr); if (!MV_DEFINED(src)) { first_src_ind = 0; second_src_ind = -1; } else { /* Valid delimiter - See if we can take a short cut to op_fnp1. If so, delimiter value needs to be reformated */ if ((1 == second_src_ind) && (1 == MV_FORCE_LEN(del))) { /* Both valid chars of char_len=1 and single byte invalid chars get the fast path */ unichar.unichar_val = 0; assert(SIZEOF(unichar.unibytes_val) >= del->str.len); memcpy(unichar.unibytes_val, del->str.addr, del->str.len); op_setp1(src, unichar.unichar_val, expr, last, dst); /* Use last since it has not been changed */ return; } /* We have a valid src with something in it */ MV_FORCE_STR(src); src_str = (unsigned char *)src->str.addr; src_len = src->str.len; /* skip all pieces until start one */ if (first) { numpcs = first; /* copy int4 type "first" into "int" type numpcs for passing to matchc */ match_ptr = matchc(del->str.len, (uchar_ptr_t)del->str.addr, src_len, src_str, &match_res, &numpcs); /* Note: "numpcs" is modified above by the function "matchc" to reflect the # of unmatched pieces */ first = numpcs; /* copy updated "numpcs" value back into "first" */ } else { match_ptr = src_str; match_res = 1; } first_src_ind = INTCAST(match_ptr - (unsigned char *)src->str.addr); if (0 == match_res) /* if match not found */ second_src_ind = -1; else { src_len -= INTCAST(match_ptr - src_str); src_str = match_ptr; /* skip # delimiters this piece will replace, e.g. if we are setting * pieces 2 - 4, then the pieces 2-4 will be replaced by one piece - expr. */ match_ptr = matchc(del->str.len, (uchar_ptr_t)del->str.addr, src_len, src_str, &match_res, &second_src_ind); second_src_ind = (0 == match_res) ? -1 : INTCAST(match_ptr - (unsigned char *)src->str.addr - del->str.len); } } delim_cnt = (size_t)first; /* Calculate total string len. */ str_len = (size_t)expr->str.len + ((size_t)first_src_ind + ((size_t)del->str.len * delim_cnt)); /* add len. of trailing chars past insertion point */ if (0 <= second_src_ind) str_len += (size_t)(src->str.len - second_src_ind); if (MAX_STRLEN < str_len) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_MAXSTRLEN); return; } ENSURE_STP_FREE_SPACE((int)str_len); str_addr = stringpool.free; /* copy prefix */ if (first_src_ind) { memcpy(str_addr, src->str.addr, first_src_ind); str_addr += first_src_ind; } /* copy delimiters */ if (gtm_utf8_mode && (1 < del->str.len)) { /* In this mode, delimiters can exceed 1 character so copy them this way */ while (0 < delim_cnt--) { memcpy(str_addr, del->str.addr, del->str.len); str_addr += del->str.len; } } else { /* If delimiters are 1 byte (M mode always and perhaps UTF8 mode), use this simpler/faster method */ memset(str_addr, (char)*del->str.addr, delim_cnt); str_addr += delim_cnt; } /* copy expression */ memcpy(str_addr, expr->str.addr, expr->str.len); str_addr += expr->str.len; /* copy trailing pieces */ if (0 <= second_src_ind) { len = src->str.len - second_src_ind; tmp_str = (unsigned char *)src->str.addr + second_src_ind; memcpy(str_addr, tmp_str, len); str_addr += len; } assert(IS_AT_END_OF_STRINGPOOL(str_addr, -str_len)); dst->mvtype = MV_STR; dst->str.len = INTCAST(str_addr - stringpool.free); dst->str.addr = (char *)stringpool.free; stringpool.free = str_addr; return; } fis-gtm-V7.0-005/sr_unix/op_zattach.c0000644000032200000250000000125614342376330016345 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "io.h" #include "op.h" void op_zattach(mval *v) { error_def(ERR_UNIMPLOP); RTS_ERROR_ABT(VARLSTCNT(1) ERR_UNIMPLOP); } fis-gtm-V7.0-005/sr_unix/op_zedit.c0000644000032200000250000001230714342376330016025 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_signal.h" #include "gtm_string.h" #include "gtm_stdlib.h" #include "gtm_unistd.h" #include #include #include "io.h" #include "parse_file.h" #include "zroutines.h" #include "eintr_wrappers.h" #include "stringpool.h" #include "setterm.h" #include "op.h" #include "fork_init.h" #include "geteditor.h" #include "restrict.h" #include "wbox_test_init.h" GBLREF io_pair io_std_device; GBLREF mval dollar_zsource; GBLREF mstr editor; GBLREF int4 dollar_zeditor; error_def(ERR_NOEDITOR); error_def(ERR_ZEDFILSPEC); error_def(ERR_RESTRICTEDOP); void op_zedit(mval *v, mval *p) { char *edt; char es[MAX_FN_LEN + 1], typ, *ptr; short path_len, tslash; int objcnt; int waitid; unsigned int childid, status; # ifdef _BSD union wait wait_status; # endif bool has_ext, exp_dir; parse_blk pblk; mstr src; zro_ent *sp, *srcdir; struct sigaction act, intr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; geteditor(); if (!editor.len) { edt = GETENV("EDITOR"); if (!edt) edt = "editor"; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_NOEDITOR, 2, LEN_AND_STR(edt)); } MV_FORCE_STR(v); MV_FORCE_STR(p); src.len = v->str.len; src.addr = v->str.addr; if (0 == src.len) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_ZEDFILSPEC, 2, src.len, src.addr); return; } memset(&pblk, 0, SIZEOF(pblk)); pblk.buffer = es; pblk.buff_size = MAX_FN_LEN; status = parse_file(&src, &pblk); if (!(status & 1)) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_ZEDFILSPEC, 2, src.len, src.addr, status); return; } has_ext = 0 != (pblk.fnb & F_HAS_EXT); exp_dir = 0 != (pblk.fnb & F_HAS_DIR); if (!(pblk.fnb & F_HAS_NAME)) { assert(!has_ext); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_ZEDFILSPEC, 2, pblk.b_esl, pblk.buffer); return; } if (!exp_dir) { memmove(&es[0], pblk.l_name, pblk.b_name + pblk.b_ext); path_len = pblk.b_name + pblk.b_ext; ptr = es; } else { path_len = pblk.b_esl; ptr = pblk.l_name; } typ = 0; if (!has_ext) { if ('.' != *ptr) { typ = STR_LIT_LEN(DOTM); if (path_len + typ > MAX_FN_LEN) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_ZEDFILSPEC, 2, path_len, es); return; } memcpy(&es[path_len], DOTM, STR_LIT_LEN(DOTM)); path_len += typ; } } else { if ((STR_LIT_LEN(DOTOBJ) == pblk.b_ext) && !MEMCMP_LIT(ptr + pblk.b_name, DOTOBJ)) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_ZEDFILSPEC, 2, path_len, es); return; } else if ((STR_LIT_LEN(DOTM) == pblk.b_ext) && !MEMCMP_LIT(ptr + pblk.b_name, DOTM)) typ = STR_LIT_LEN(DOTM); } dollar_zsource.str.addr = es; dollar_zsource.str.len = path_len - typ; s2pool(&dollar_zsource.str); es[path_len] = 0; if (!exp_dir) { src.addr = es; src.len = path_len; srcdir = (zro_ent *)0; zro_search(NULL, NULL, &src, &srcdir, TRUE); if (NULL == srcdir) { /* find the first source directory */ objcnt = (TREF(zro_root))->count; for (sp = TREF(zro_root) + 1; (NULL == srcdir) && (0 < objcnt--); ++sp) { if (ZRO_TYPE_OBJECT == sp->type) { sp++; assert(ZRO_TYPE_COUNT == sp->type); if (0 != sp->count) srcdir = sp + 1; } else { /* shared library entries (ZRO_TYPE_OBJLIB) do not have source directories */ assert(ZRO_TYPE_OBJLIB == sp->type); } } } if (srcdir && srcdir->str.len) { assert(ZRO_TYPE_SOURCE == srcdir->type); tslash = ('/' == srcdir->str.addr[srcdir->str.len - 1]) ? 0 : 1; if (path_len + srcdir->str.len + tslash >= SIZEOF(es)) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_ZEDFILSPEC, 2, src.len, src.addr); return; } memmove(&es[ srcdir->str.len + tslash], &es[0], path_len); if (tslash) es[ srcdir->str.len ] = '/'; memcpy(&es[0], srcdir->str.addr, srcdir->str.len); path_len += srcdir->str.len + tslash; es[ path_len ] = 0; } } if (RESTRICTED(zedit_op)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_RESTRICTEDOP, 1, "ZEDIT"); flush_pio(); if (tt == io_std_device.in->type) resetterm(io_std_device.in); /* ignore interrupts */ sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = SIG_IGN; sigaction(SIGINT, &act, &intr); FORK(childid); if (childid) { waitid = (int)childid; for (;;) { #ifdef _BSD WAIT(&wait_status, waitid); #else WAIT((int *)&status, waitid); #endif if (waitid == (int)childid) break; if (-1 == waitid) break; } if (-1 != waitid) dollar_zeditor = 0; else dollar_zeditor = errno; /* restore interrupt handler */ sigaction(SIGINT, &intr, 0); if (tt == io_std_device.in->type) setterm(io_std_device.in); } else { if (WBTEST_ENABLED(WBTEST_BADEXEC_OP_ZEDIT)) STRCPY(editor.addr, ""); EXECL(editor.addr, editor.addr, es, 0); UNDERSCORE_EXIT(-1); } } fis-gtm-V7.0-005/sr_unix/op_zhelp_xfr.c0000755000032200000250000000331614342376330016712 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "indir_enum.h" #include "stringpool.h" #include "op.h" #include "io.h" static mval com = DEFINE_MVAL_STRING(MV_STR, 0 , 0 , 1 , (char *) "," , 0 , 0 ); static mval rpar = DEFINE_MVAL_STRING(MV_STR, 0 , 0 , 1 , (char *) ")" , 0 , 0 ); static mval dlib = DEFINE_MVAL_STRING(MV_STR, 0 , 0 , SIZEOF("$gtm_dist/gtmhelp.gld") - 1 , (char *) "$gtm_dist/gtmhelp.gld" , 0 , 0 ); GBLREF spdesc stringpool; void op_zhelp_xfr(mval *subject, mval *lib) { mstr x; mval *action; MV_FORCE_STR(subject); MV_FORCE_STR(lib); if (!lib->str.len) lib = &dlib; flush_pio(); action = push_mval(subject); action->mvtype = 0; action->str.len = SIZEOF("D ^GTMHELP(") - 1; action->str.addr = "D ^GTMHELP("; s2pool(&action->str); action->mvtype = MV_STR; mval_lex(subject, &x); if (IS_AT_END_OF_STRINGPOOL(x.addr, 0)) { action->str.len += x.len; stringpool.free += x.len; } else op_cat(VARLSTCNT(3) action, action, subject); op_cat(VARLSTCNT(3) action, action, &com); /* add "," */ mval_lex(lib, &x); if (IS_AT_END_OF_STRINGPOOL(x.addr, 0)) { action->str.len += x.len; stringpool.free += x.len; } else op_cat(VARLSTCNT(3) action, action, lib); op_cat(VARLSTCNT(3) action, action, &rpar); /* add ")" */ op_commarg(action,indir_linetail); } fis-gtm-V7.0-005/sr_unix/op_zhorolog.c0000644000032200000250000000416714342376330016556 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2021 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 #include "gtm_time.h" #include "stringpool.h" #include "op.h" #include "dollarh.h" GBLREF spdesc stringpool; error_def(ERR_WEIRDSYSTIME); /* the first argument is the destination mval * the second argument is a flag indicating whether it the standard ISV ($HOROLOG - FALSE) or $ZHOROLOG - TRUE */ void op_zhorolog(mval *s, boolean_t z) { uint4 days; time_t seconds; struct timespec ts; unsigned char *strpool_free; long gmtoffset; assert(stringpool.free <= stringpool.top); assert(stringpool.free >= stringpool.base); ENSURE_STP_FREE_SPACE(MAXNUMLEN + 1); strpool_free = stringpool.free; assertpro(-1 != clock_gettime(CLOCK_REALTIME, &ts)); #ifdef DEBUG /* The OS should never return an invalid time */ if ((ts.tv_sec < 0) || (ts.tv_nsec < 0) || (ts.tv_nsec > NANOSECS_IN_SEC)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_WEIRDSYSTIME); #endif seconds = ts.tv_sec; gmtoffset = dollarh(seconds, &days, &seconds); s->str.addr = (char *)strpool_free; strpool_free = i2asc(strpool_free, days); *strpool_free++ = ','; strpool_free = i2asc(strpool_free, (uint4)seconds); if (z) { *strpool_free++ = ','; strpool_free = i2asc(strpool_free, (uint4)(ts.tv_nsec / NANOSECS_IN_USEC)); *strpool_free++ = ','; if (gmtoffset >= 0) /* The sign check is neccessary because i2ascl doesn't handle negative values */ strpool_free = i2ascl(strpool_free, gmtoffset); else { *strpool_free++ = '-'; strpool_free = i2ascl(strpool_free, -1UL * gmtoffset); } } s->str.len = INTCAST((char *)strpool_free - s->str.addr); s->mvtype = MV_STR; stringpool.free = strpool_free; return; } fis-gtm-V7.0-005/sr_unix/op_zlink.c0000644000032200000250000005656314342376330016051 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_fcntl.h" #include "gtm_stat.h" #include "gtm_string.h" #include "gtm_unistd.h" #include #include "stringpool.h" #ifdef AUTORELINK_SUPPORTED # include "rtnhdr.h" #endif #include "zroutines.h" #include "cmd_qlf.h" #include "parse_file.h" #include "gtmio.h" #include "eintr_wrappers.h" #include "op.h" #include "incr_link.h" #include "compiler.h" #ifdef __MVS__ # include "gtm_zos_io.h" #endif #include "arlinkdbg.h" #include "relinkctl.h" #include "toktyp.h" /* Needed for "valid_mname.h" */ #include "valid_mname.h" #include "restrict.h" typedef enum { SRC = 1, OBJ, NOTYPE } linktyp; /* On shared platforms, skip parameter should be FALSE to indicate an auto-ZLINK so that * zro_search looks into shared libraries. On non-shared platforms, it should be * TRUE to instruct zro_search to always skip shared libraries */ #define SKIP_SHLIBS TRUE #ifdef USHBIN_SUPPORTED #define PROBE_SHLIBS (!SKIP_SHLIBS) /* i.e., don't skip (skip = FALSE) */ #else #define PROBE_SHLIBS SKIP_SHLIBS #endif /* On certain platforms the st_mtime field of the stat structure got replaced by a timespec st_mtim field, which in turn has tv_sec * and tv_nsec fields. For compatibility reasons, those platforms define an st_mtime macro which points to st_mtim.tv_sec. Whenever * we detect such a situation, we define a nanosecond flavor of that macro to point to st_mtim.tv_nsec. On HPUX Itanium and older * AIX boxes the stat structure simply has additional fields with the nanoseconds value, yet the names of those field are different * on those two architectures, so we choose our mapping accordingly. */ #if defined st_mtime # define st_nmtime st_mtim.tv_nsec #elif defined(_AIX) # define st_nmtime st_mtime_n #elif defined(__hpux) && defined(__ia64) # define st_nmtime st_nmtime #endif /* Macro to close object file and give appropriate error */ #define CLOSE_OBJECT_FD(FD, STATUS) \ { \ CLOSE_OBJECT_FILE(FD, STATUS); /* Resets "object_file_des" to FD_INVALID */ \ if (-1 == (STATUS)) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("close()"), CALLFROM, errno); \ } #ifdef AUTORELINK_SUPPORTED # define CHECK_OBJECT_HISTORY(OBJPATH, OBJDIR, RECENT_ZHIST_PARM) \ { \ if (TREF(arlink_enabled) && !TREF(trigger_compile_and_link)) \ { /* Autorelink is enabled, this is not a trigger and we need a search history for the object file to pass \ * to incr_link(). We had to wait till this point to discover the search history since at the time of the \ * call to zro_search(), there may not have been an object file to find or, given where we found the source, \ * the object file we are linking now may be different from one found before so create the history array given \ * the supplied object file path and our $ZROUTINES directory array. See GTM-8311 for potential improvements \ * in this area. \ * \ * Note we cannot assert RECENT_ZHIST_PARM is null here as we may be about to link a recompiled module after \ * a failed ZLINK but incr_link() took care of releasing the old history in its error path. \ */ \ RECENT_ZHIST_PARM = zro_search_hist(OBJPATH, &OBJDIR); \ } \ } #else # define CHECK_OBJECT_HISTORY(OBJPATH, OBJDIR, RECENT_ZHIST_PARM) #endif # define COMBINE_OBJ_DIR_W_NAME() \ MBSTART{ \ if (objdir && objdir->str.len) \ { \ assert((objdir->str.len + tslash + module_name.len) < MAX_FN_LEN); \ assert('/' == objnamebuf[objdir->str.len + tslash - 1]); \ memcpy(&objnamebuf[objdir->str.len + tslash], module_name.addr, module_name.len); \ objnamelen = objdir->str.len + tslash + module_name.len; \ memcpy(&objnamebuf[objnamelen], DOTOBJ, SIZEOF(DOTOBJ)); /* Copies null terminator */ \ objnamelen += (SIZEOF(DOTOBJ) - 1); \ } \ assert(objnamelen && (objnamelen <= MAX_FN_LEN)); \ cmd_qlf.object_file.mvtype = MV_STR; \ memcpy(cmd_qlf.object_file.str.addr, objnamebuf, objnamelen); \ cmd_qlf.object_file.str.len = objnamelen; \ cmd_qlf.object_file.str.addr[cmd_qlf.object_file.str.len] = 0; \ } MBEND GBLREF command_qualifier cmd_qlf; GBLREF int object_file_des; GBLREF mident module_name, routine_name; GBLREF mval dollar_zsource; GBLREF spdesc rts_stringpool, stringpool; GBLREF unsigned char object_file_name[]; GBLREF unsigned short object_name_len; error_def(ERR_FILENOTFND); error_def(ERR_FILEPARSE); error_def(ERR_RESTRICTEDOP); error_def(ERR_SYSCALL); error_def(ERR_TEXT); error_def(ERR_VERSION); error_def(ERR_WILDCARD); error_def(ERR_ZLINKFILE); error_def(ERR_ZLNOOBJECT); ZOS_ONLY(error_def(ERR_BADTAG);) /* Routine to locate object files, or source and compile to an object file if necessary, and drive the linker to link the file * into this process appropriately. Three types of linking are currently supported on UNIX platforms (excepting Linux i386 which * has its own less fully-featured linker): * * 1. Link into process private - Executable code becomes part of the process private space. * 2. Link from a shared library - M routines linked into a shared library can be linked into a process allowing much of the * object file to be shared. * 3. Link from a shared object - Shared objects are loaded into shared memory by GT.M (rtnobj.c is the manager) and linked * much like objects linked from a shared library. * * Parameters: * - v - mval containing the name/path of the object file. * - quals - mval containing the ZLINK command options (see GT.M User's Guide). */ void op_zlink (mval *v, mval *quals) { boolean_t compile, expdir, obj_found, src_found; char *err_code, *fname, ceprep_file[MAX_FN_LEN + 1], inputf[MAX_FN_LEN + 1], list_file[MAX_FN_LEN + 1], obj_file[MAX_FN_LEN + 1], objnamebuf[MAX_FN_LEN + 1], srcnamebuf[MAX_FN_LEN + 1]; int initial_object_file_des, qlf, save_errno, status, tslash; linktyp type; mstr srcstr, objstr, file; parse_blk pblk; struct stat obj_stat, src_stat; unsigned short objnamelen, srcnamelen; size_t cpy_len; zro_ent *srcdir, *objdir; ARLINK_ONLY(zro_hist *recent_zhist;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((NULL != quals) && RESTRICTED(zlink_op)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_RESTRICTEDOP, 1, "ZLINK"); ARLINK_ONLY(recent_zhist = NULL); MV_FORCE_STR(v); if (!v->str.len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, v->str.len, v->str.addr, ERR_TEXT, 2, RTS_ERROR_LITERAL("Filename/path is missing")); assert(0 <= v->str.len); if ((0 > v->str.len) || (MAX_FN_LEN < v->str.len)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, (0 < v->str.len) ? MAX_FN_LEN : 0, v->str.addr, ERR_TEXT, 2, RTS_ERROR_LITERAL("Filename/path exceeds max length")); DBGARLNK((stderr, "op_zlink: Call to (re)link routine %.*s\n", v->str.len, v->str.addr)); assert((SIZEOF(DOTM) == SIZEOF(DOTOBJ)) && (SIZEOF(srcnamebuf) == SIZEOF(objnamebuf))); object_file_des = FD_INVALID; srcdir = objdir = NULL; expdir = FALSE; module_name.len = object_name_len = 0; memset(&pblk, 0, SIZEOF(pblk)); pblk.buff_size = MAX_FN_LEN; pblk.buffer = inputf; pblk.fop = F_SYNTAXO; status = parse_file(&v->str, &pblk); if (!(status & 1)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_FILEPARSE, 2, v->str.len, v->str.addr, status); if (pblk.fnb & F_WILD) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, v->str.len, v->str.addr, ERR_WILDCARD, 2, v->str.len, v->str.addr); /* 4SCA: file.len is bounded by MAX_FN_LEN in parse_file() */ file.addr = pblk.buffer; file.len = pblk.b_esl; type = NOTYPE; if (pblk.b_ext) { /* 4SCA: file.len is decremented by the extension length pblk.b_ext */ file.len -= pblk.b_ext; type = (('o' == pblk.l_ext[1]) && (2 == pblk.b_ext)) ? OBJ : SRC; } srcnamelen = MIN(v->str.len, MAX_FN_LEN); memcpy(srcnamebuf, v->str.addr, srcnamelen); if ('%' == srcnamebuf[0]) srcnamebuf[0] = '_'; INIT_CMD_QLF_STRINGS(cmd_qlf, obj_file, list_file, ceprep_file, MAX_FN_LEN); if (!TREF(trigger_compile_and_link)) /* triggers don't rely on $ZCOMPILE */ zl_cmd_qlf(&(TREF(dollar_zcompile)), &cmd_qlf, srcnamebuf, &srcnamelen, !quals); if (NULL != quals) /* after initization w default quals, override with any actual quals */ zl_cmd_qlf(&quals->str, &cmd_qlf, srcnamebuf, &srcnamelen, TRUE); objnamelen = MIN(pblk.b_name, MAX_MIDENT_LEN); memcpy(objnamebuf, pblk.l_name, objnamelen); if (!TREF(trigger_compile_and_link) && (objnamelen - object_name_len) || memcmp(objnamebuf, object_file_name, objnamelen)) { /* appear to have an object name that differs from the file name */ assert(MAX_MIDENT_LEN >= object_name_len); objnamelen = object_name_len; memcpy(objnamebuf, object_file_name, objnamelen); } if (NULL != quals) { /* Explicit ZLINK from generated code or from gtm_trigger() */ expdir = (0 != (pblk.fnb & F_HAS_DIR)); if (!expdir) { /* if the directory was not explicit, skip past it */ file.addr = pblk.l_name; file.len = pblk.b_name; } else if (SRC != type) { /* if we have an explicit, directory shift the object name to make room and fill in the directory */ assert((OBJ == type) || (NOTYPE == type)); cpy_len = objnamelen; assert(sizeof(objnamebuf) >= cpy_len); assert(sizeof(objnamebuf) >= (cpy_len + pblk.b_dir)); if (sizeof(objnamebuf) < (cpy_len + pblk.b_dir)) /* 4SCA BYPASSOK */ cpy_len = sizeof(objnamebuf) - pblk.b_dir; assert(256 >= cpy_len); /* For Veracode grins */ memmove(&objnamebuf[pblk.b_dir], objnamebuf, cpy_len); assert(sizeof(objnamebuf) >= pblk.b_dir); memcpy(objnamebuf, file.addr, pblk.b_dir); objnamelen += pblk.b_dir; } assert(MAX_FN_LEN > (objnamelen + SIZEOF(DOTOBJ))); memcpy(&objnamebuf[objnamelen], DOTOBJ, SIZEOF(DOTOBJ)); /* Copies null terminator */ objnamelen += STR_LIT_LEN(DOTOBJ); if (TREF(trigger_compile_and_link)) { /* trigger sources don't have a .m extension */ assert(OBJ == type); srcnamelen -= (SIZEOF(DOTOBJ) - 1); srcnamebuf[srcnamelen] = 0; } else { /* maintain $ZSOURCE */ ENSURE_STP_FREE_SPACE(file.len); memcpy(stringpool.free, file.addr, file.len); dollar_zsource.str.addr = (char *)stringpool.free; dollar_zsource.str.len = file.len; stringpool.free += file.len; if (OBJ == type) memcpy(&srcnamebuf[srcnamelen], DOTM, SIZEOF(DOTM)); /* Copies null terminator */ } if (SRC == type) { /* source file extension specified */ srcnamelen = file.len + pblk.b_ext; assert(MAX_FN_LEN >= srcnamelen); memcpy(srcnamebuf, file.addr, srcnamelen); srcnamebuf[srcnamelen] = 0; } if (NOTYPE == type) { /* No file extension specified - object done above, so do source */ if ((file.len + SIZEOF(DOTM)) > SIZEOF(srcnamebuf)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, v->str.len, v->str.addr, ERR_TEXT, 2, RTS_ERROR_LITERAL("Filename/path exceeds max length")); memcpy(srcnamebuf, file.addr, file.len); srcnamelen = file.len + SIZEOF(DOTM) - 1; assert(MAX_FN_LEN >= srcnamelen); memcpy(&srcnamebuf[file.len], DOTM, SIZEOF(DOTM)); /* Copies null terminator */ } if (!expdir) { /* No full or relative directory specified on file to link - fill it in */ srcstr.addr = srcnamebuf; srcstr.len = srcnamelen; objstr.addr = objnamebuf; objstr.len = objnamelen; if (OBJ == type) { /* Explicit ZLINK of object - don't locate source */ zro_search(&objstr, &objdir, NULL, NULL, SKIP_SHLIBS); if (NULL == objdir) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, dollar_zsource.str.len, dollar_zsource.str.addr, ERR_FILENOTFND, 2, objnamelen, objnamebuf); } else if (SRC == type) { /* Explicit ZLINK of source - locate both source and object*/ zro_search(&objstr, &objdir, &srcstr, &srcdir, SKIP_SHLIBS); if (NULL == srcdir) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, dollar_zsource.str.len, dollar_zsource.str.addr, ERR_FILENOTFND,2, srcnamelen, srcnamebuf); } else { /* Explicit ZLINK no file type specified - locate both source and object */ zro_search(&objstr, &objdir, &srcstr, &srcdir, PROBE_SHLIBS); if ((NULL == objdir) && (NULL == srcdir)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(12) ERR_ZLINKFILE, 2, dollar_zsource.str.len, dollar_zsource.str.addr, ERR_FILENOTFND,2, srcnamelen, srcnamebuf, ERR_FILENOTFND,2, objnamelen, objnamebuf); } } } else { /* auto-ZLINK for execution, ZBREAK, $TEXT(), or ZEDIT (i.e. non-specific call to op_zlink()) */ assert(NOTYPE == type); memcpy(srcnamebuf, pblk.l_name, pblk.b_name); memcpy(&srcnamebuf[pblk.b_name], DOTM, SIZEOF(DOTM)); srcnamelen = pblk.b_name + SIZEOF(DOTM) - 1; if ('%' == srcnamebuf[0]) srcnamebuf[0] = '_'; assert(MAX_FN_LEN > (objnamelen + SIZEOF(DOTOBJ))); memcpy(&objnamebuf[objnamelen], DOTOBJ, SIZEOF(DOTOBJ)); /* Copies null terminator */ objnamelen += (SIZEOF(DOTOBJ) - 1); srcstr.addr = srcnamebuf; srcstr.len = srcnamelen; objstr.addr = objnamebuf; objstr.len = objnamelen; zro_search(&objstr, &objdir, &srcstr, &srcdir, PROBE_SHLIBS); if (NULL == srcdir) if (NULL == objdir) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(12) ERR_ZLINKFILE, 2, v->str.len, v->str.addr, ERR_FILENOTFND, 2, srcnamelen, srcnamebuf, ERR_FILENOTFND, 2, objnamelen, objnamebuf); else if (NULL == objdir) type = SRC; } if (OBJ == type) { /* Object file extension specified */ if (objdir) { /* Object file found via zro_search() */ assert(ZRO_TYPE_OBJLIB != objdir->type); if ((objdir->str.len + objnamelen + 2) > (SIZEOF(objnamebuf) - 1)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, v->str.len, v->str.addr, ERR_TEXT, 2, RTS_ERROR_LITERAL("Filename/path exceeds max length")); if (objdir->str.len) { tslash = ('/' == objdir->str.addr[objdir->str.len - 1]) ? 0 : 1; memmove(&objnamebuf[objdir->str.len + tslash], objnamebuf, objnamelen); if (tslash) { assert(objnamelen + tslash <= MAX_FN_LEN + 1); objnamebuf[objdir->str.len] = '/'; } /* 4SCA: Protected by rts_error above */ memcpy(objnamebuf, objdir->str.addr, objdir->str.len); objnamelen += objdir->str.len + tslash; objnamebuf[objnamelen] = 0; } } OPEN_OBJECT_FILE(objnamebuf, O_RDONLY, object_file_des); if (FD_INVALID == object_file_des) { /* Could not find object file */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_ZLINKFILE, 2, objnamelen, objnamebuf, errno); } /* Note - if explicit ZLINK, objdir can be NULL if link is from a directory not mentioned in $ZROUTINES */ CHECK_OBJECT_HISTORY(objnamebuf, objdir, RECENT_ZHIST); if (IL_RECOMPILE == INCR_LINK(&object_file_des, objdir, RECENT_ZHIST, objnamelen, objnamebuf)) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_ZLINKFILE, 2, v->str.len, v->str.addr, ERR_VERSION); } CLOSE_OBJECT_FD(object_file_des, status); } else { /* Either NO file extension specified or is SOURCE file extension type */ if (srcdir) { /* A source directory containing routine was found by zro_search() */ assert(ZRO_TYPE_OBJLIB != objdir->type); if ((srcdir->str.len + srcnamelen) > (SIZEOF(srcnamebuf) - 1)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, v->str.len, v->str.addr, ERR_TEXT, 2, RTS_ERROR_LITERAL("Filename/path exceeds max length")); if (srcdir->str.len) { tslash = ('/' == srcdir->str.addr[srcdir->str.len - 1]) ? 0 : 1; /* 4SCA: Protected by rts_error above */ memmove(&srcnamebuf[srcdir->str.len + tslash], srcnamebuf, srcnamelen); if (tslash) srcnamebuf[srcdir->str.len] = '/'; memcpy(srcnamebuf, srcdir->str.addr, srcdir->str.len); srcnamelen += srcdir->str.len + tslash; srcnamebuf[srcnamelen] = 0; } } if (objdir) { /* An object directory or shared library containing the routine was found by zro_search() */ if (ZRO_TYPE_OBJLIB == objdir->type) { /* For object libraries, there are no auto-relink complications so no need to hunt down * any history for it or to pass in any. */ assert(objdir->shrlib); assert(objdir->shrsym); /* The incr_link() routine should drive errors for any issue found with linking from a shared * library so IL_DONE is the only valid return code we *ever* expect back. */ assertpro(IL_DONE == INCR_LINK(NULL, objdir, NULL, objnamelen, objnamebuf)); return; } if ((objdir->str.len + objnamelen) > (SIZEOF(objnamebuf) - 1)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, v->str.len, v->str.addr, ERR_TEXT, 2, RTS_ERROR_LITERAL("Filename/path exceeds max length")); if (objdir->str.len) { tslash = ('/' == objdir->str.addr[objdir->str.len - 1]) ? 0 : 1; assert((objnamelen + objdir->str.len + tslash) <= (MAX_FN_LEN + 1)); /* 4SCA: Protected by rts_error above */ memmove(&objnamebuf[objdir->str.len + tslash], objnamebuf, objnamelen); if (tslash) objnamebuf[objdir->str.len] = '/'; memcpy(objnamebuf, objdir->str.addr, objdir->str.len); objnamelen += objdir->str.len + tslash; objnamebuf[objnamelen] = 0; } } /* Check which source/object we ended up with and whether they are for the same file or not so we can determine if * recompilation might be required. */ src_found = obj_found = compile = FALSE; if (SRC != type) { /* Object or NO file extension specified - check object file exists */ initial_object_file_des = object_file_des; OPEN_OBJECT_FILE(objnamebuf, O_RDONLY, object_file_des); if (FD_INVALID == object_file_des) { save_errno = errno; if (ENOENT != save_errno) { err_code = STRERROR(save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, objnamelen, objnamebuf, ERR_TEXT, 2, LEN_AND_STR(err_code)); } } else obj_found = TRUE; } else /* If source file extension specified, force re-compile */ { compile = TRUE; assert(FD_INVALID == object_file_des); /* Shouldn't be an object file open yet */ } STAT_FILE(srcnamebuf, &src_stat, status); /* Check if source file exists */ if (-1 == status) { save_errno = errno; if ((ENOENT == errno) && (SRC != type)) src_found = FALSE; else { if (FD_INVALID != object_file_des) /* Chose object file if open, ignore error */ CLOSE_OBJECT_FILE(object_file_des, status); err_code = STRERROR(save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, v->str.len, v->str.addr, ERR_TEXT, 2, LEN_AND_STR(err_code)); } } else src_found = TRUE; if (SRC != type) { /* If object or no file extension type specified, check if have both source and object and if so, decide * if source needs to be recompiled. */ if (src_found) { if (obj_found) { FSTAT_FILE(object_file_des, &obj_stat, status); if (-1 == status) { save_errno = errno; if (FD_INVALID != object_file_des) /* Close object file if open */ CLOSE_OBJECT_FILE(object_file_des, status); err_code = STRERROR(save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, v->str.len, v->str.addr, ERR_TEXT, 2, LEN_AND_STR(err_code)); } if ((src_stat.st_mtime > obj_stat.st_mtime) || ((src_stat.st_mtime == obj_stat.st_mtime) && (src_stat.st_nmtime > obj_stat.st_nmtime))) { CLOSE_OBJECT_FD(object_file_des, status); compile = TRUE; } } else { compile = TRUE; assert(FD_INVALID == object_file_des); /* Make sure no object file open */ } } else if (!obj_found) { assert(FD_INVALID == object_file_des); /* Make sure closed */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, v->str.len, v->str.addr, ERR_FILENOTFND, 2, objnamelen, objnamebuf); } } if (compile) { /* (Re)Compile source file */ qlf = cmd_qlf.qlf; if (!(qlf & CQ_OBJECT) && (SRC != type)) { assert(FD_INVALID == object_file_des); /* Make sure no object file open */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_ZLINKFILE, 2, v->str.len, v->str.addr, ERR_ZLNOOBJECT); } COMBINE_OBJ_DIR_W_NAME(); zlcompile(srcnamelen, (uchar_ptr_t)srcnamebuf); assert(object_name_len <= MAX_FN_LEN); objnamelen = object_name_len; memcpy(objnamebuf, object_file_name, object_name_len); objnamebuf[object_name_len] = 0; assert(FD_INVALID == object_file_des); /* zlcompile() should have driven obj_code() which closes object */ assertpro(FALSE == TREF(compile_time) && (stringpool.base == rts_stringpool.base)); /* sb back in rts */ if (!(cmd_qlf.qlf & CQ_OBJECT)) return; OPEN_OBJECT_FILE(objnamebuf, O_RDONLY, object_file_des); if (FD_INVALID == object_file_des) { save_errno = errno; err_code = STRERROR(save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(10) ERR_ZLINKFILE, 2, v->str.len, v->str.addr, ERR_ZLNOOBJECT, 0, ERR_TEXT, 2, LEN_AND_STR(err_code)); } } else { module_name.len = routine_name.len = MIN(v->str.len, MAX_MIDENT_LEN); memcpy(module_name.addr, v->str.addr, module_name.len); memcpy(routine_name.addr, v->str.addr, routine_name.len); CONVERT_FILENAME_TO_RTNNAME(routine_name); } assert(FD_INVALID != object_file_des); /* Object file should be open at this point */ CHECK_OBJECT_HISTORY(objnamebuf, objdir, RECENT_ZHIST); status = INCR_LINK(&object_file_des, objdir, RECENT_ZHIST, objnamelen, objnamebuf); if (IL_RECOMPILE == status) { /* Failure due only to version mismatch, so recompile */ assertpro(!compile); if (!src_found) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_ZLINKFILE, 2, objnamelen, objnamebuf, ERR_FILENOTFND, 2, srcnamelen, srcnamebuf, ERR_VERSION); if (!compile) { if (!TREF(trigger_compile_and_link)) COMBINE_OBJ_DIR_W_NAME(); else if (!MV_DEFINED(&cmd_qlf.object_file)) { cmd_qlf.object_file.mvtype = MV_STR; cmd_qlf.object_file.str.addr = objnamebuf; cmd_qlf.object_file.str.len = objnamelen; } assert((MV_STR & cmd_qlf.object_file.mvtype) && cmd_qlf.object_file.str.len && strlen(cmd_qlf.object_file.str.addr) == cmd_qlf.object_file.str.len); } zlcompile(srcnamelen, (uchar_ptr_t)srcnamebuf); assertpro(FALSE == TREF(compile_time) && (stringpool.base == rts_stringpool.base)); /* sb back in rts */ if (!(cmd_qlf.qlf & CQ_OBJECT) && (SRC != type)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_ZLINKFILE, 2, v->str.len, v->str.addr, ERR_ZLNOOBJECT); assert(FD_INVALID == object_file_des); /* zlcompile() should have driven obj_code() which closes it */ OPEN_OBJECT_FILE(objnamebuf, O_RDONLY, object_file_des); CHECK_OBJECT_HISTORY(objnamebuf, objdir, RECENT_ZHIST); /* We just did a fresh re-compile a few lines above so IL_DONE is the only return code we ever * expect to see back. Only a race-condition created by a different version overlaying the newly * created object file could conceivably cause an IL_RECOMPILE code here (incr_link handles all * the other errors itself). Not at this time considered worthy of special coding. */ status = INCR_LINK(&object_file_des, objdir, RECENT_ZHIST, objnamelen, objnamebuf); assertpro(IL_DONE == status); } CLOSE_OBJECT_FD(object_file_des, status); } } fis-gtm-V7.0-005/sr_unix/op_zmess.c0000644000032200000250000001162514342376330016051 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "fao_parm.h" #include "error.h" #include "op.h" #include "mval2fao.h" #include "gtmmsg.h" #include "send_msg.h" #include "wbox_test_init.h" #define ZMESS_DISALLWD_LIST_SIZE 10 #define FAO_BUFFER_SPACE 2048 #define MAX_ERR_MSG_LEN 256 STATICFNDCL boolean_t is_disallowed(unsigned int errnum); STATICDEF unsigned int zmess_disallowed_list[ZMESS_DISALLWD_LIST_SIZE] = {0}; error_def(ERR_REPEATERROR); error_def(ERR_TPRETRY); error_def(ERR_JOBINTRRQST); error_def(ERR_JOBINTRRETHROW); error_def(ERR_UNSOLCNTERR); error_def(ERR_CTRLY); error_def(ERR_CTRLC); error_def(ERR_CTRAP); error_def(ERR_STACKCRIT); error_def(ERR_SPCLZMSG); /* Returns whether an errnum is not allowed to be raised by ZMESSAGE. The errors on this list generally * trigger additional processing by the error handler which either assumes certain context to be setup * before the processing or does something which should not be triggered manually. */ STATICFNDEF boolean_t is_disallowed(unsigned int errnum) { int i; /* search iterator */ if (0 == zmess_disallowed_list[0]) { /* Lazy initialization of the disallowed array. The ERR_XXXX take value at runtime, hence individual assignments. */ i = 0; zmess_disallowed_list[i++] = ERR_REPEATERROR; zmess_disallowed_list[i++] = ERR_TPRETRY; zmess_disallowed_list[i++] = ERR_JOBINTRRQST; zmess_disallowed_list[i++] = ERR_JOBINTRRETHROW; zmess_disallowed_list[i++] = ERR_UNSOLCNTERR; zmess_disallowed_list[i++] = ERR_CTRLY; zmess_disallowed_list[i++] = ERR_CTRLC; zmess_disallowed_list[i++] = ERR_CTRAP; zmess_disallowed_list[i++] = ERR_STACKCRIT; zmess_disallowed_list[i++] = ERR_SPCLZMSG; assert(ZMESS_DISALLWD_LIST_SIZE == i); } /* Linear search as the list is short. */ for (i = 0; i < ZMESS_DISALLWD_LIST_SIZE; ++i) if ((zmess_disallowed_list[i] >> MSGSEVERITY) == (errnum >> MSGSEVERITY)) /* Message severity doesn't matter. */ return TRUE; return FALSE; } void op_zmess(unsigned int cnt, ...) { va_list var; const err_ctl *ectl; const err_msg *eptr; UINTPTR_T fao[NUM_OF_FAO_SLOTS]; char buff[FAO_BUFFER_SPACE]; unsigned int errnum, j; int faocnt; int4 tmp_severity; VAR_START(var, cnt); assert(34 == MAX_FAO_PARMS); /* Defined in fao_parm.h. */ errnum = va_arg(var, int); cnt--; if (NULL != (ectl = err_check(errnum))) /* Note assignment. */ { GET_MSG_INFO(errnum, ectl, eptr); tmp_severity = SEVMASK(errnum); faocnt = eptr->parm_count; faocnt = (faocnt > MAX_FAO_PARMS ? MAX_FAO_PARMS : faocnt); faocnt = mval2fao(eptr->msg, var, &fao[0], cnt, faocnt, buff, buff + SIZEOF(buff)); va_end(var); if (0 <= faocnt) { if (WBTEST_ENABLED(WBTEST_INFO_HUB_SEND_ZMESS)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(2 + faocnt) errnum, faocnt, fao[0], fao[1], fao[2], fao[3], fao[4], fao[5], fao[6], fao[7], fao[8], fao[9], fao[10], fao[11], fao[12], fao[13], fao[14], fao[15], fao[16], fao[17], fao[18], fao[19], fao[20], fao[21], fao[22], fao[23], fao[24], fao[25], fao[26], fao[27], fao[28], fao[29], fao[30], fao[31], fao[32], fao[33]); } else if (is_disallowed(errnum)) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4 + faocnt) ERR_SPCLZMSG, 0, errnum, faocnt, fao[0], fao[1], fao[2], fao[3], fao[4], fao[5], fao[6], fao[7], fao[8], fao[9], fao[10], fao[11], fao[12], fao[13], fao[14], fao[15], fao[16], fao[17], fao[18], fao[19], fao[20], fao[21], fao[22], fao[23], fao[24], fao[25], fao[26], fao[27], fao[28], fao[29], fao[30], fao[31], fao[32], fao[33]); } else if ((INFO == tmp_severity) || (SUCCESS == tmp_severity)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(2 + faocnt) errnum, faocnt, fao[0], fao[1], fao[2], fao[3], fao[4], fao[5], fao[6], fao[7], fao[8], fao[9], fao[10], fao[11], fao[12], fao[13], fao[14], fao[15], fao[16], fao[17], fao[18], fao[19], fao[20], fao[21], fao[22], fao[23], fao[24], fao[25], fao[26], fao[27], fao[28], fao[29], fao[30], fao[31], fao[32], fao[33]); } else { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(2 + faocnt) errnum, faocnt, fao[0], fao[1], fao[2], fao[3], fao[4], fao[5], fao[6], fao[7], fao[8], fao[9], fao[10], fao[11], fao[12], fao[13], fao[14], fao[15], fao[16], fao[17], fao[18], fao[19], fao[20], fao[21], fao[22], fao[23], fao[24], fao[25], fao[26], fao[27], fao[28], fao[29], fao[30], fao[31], fao[32], fao[33]); } } } else { va_end(var); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errnum); } } fis-gtm-V7.0-005/sr_unix/op_zrupdate.c0000644000032200000250000003026214342376335016551 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2022 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 #include #include "gtm_limits.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_stat.h" #include "gtm_stdlib.h" #include "gtmio.h" #include "io.h" #include "iosp.h" #include #include "relinkctl.h" #include "parse_file.h" #include "eintr_wrappers.h" #include "error.h" #include "min_max.h" #include "op.h" #include "op_fnzsearch.h" #include "interlock.h" #include "toktyp.h" #include "valid_mname.h" #include "restrict.h" #ifdef DEBUG # include "toktyp.h" /* Needed for "valid_mname.h" */ #endif #define DOTOBJEXT ".o" #define OBJEXT 'o' #define ASTERISK '*' #define QUESTION '?' LITREF mval literal_null; error_def(ERR_FILEPARSE); error_def(ERR_PARNORMAL); error_def(ERR_RESTRICTEDOP); error_def(ERR_TEXT); #ifndef AUTORELINK_SUPPORTED /* Stub routine for unsupported platforms */ void op_zrupdate(int argcnt, ...) { return; } #else /* The ZRUPDATE command drives this routine once through for each argument (object file path and object file - potentially * containing wildcards). Each file specified, or found in a wildcard search, is separated into its path and its routine name; * the path is then looked up and the appropriate relinkctl file opened, where we find the routine name and bump its cycle. * * Although this routine is set up to handle a variable argument list, more than 1 argument is not currently supported. The * ZRUPDATE command itself does support a commented list of filespecs, but the compiler turns each argument into a separate * call to this routine. The purpose of the variable argument list is to support a future proposed enhancement, which would * allow a ZRUPDATE argument to be a parenthesized list of filespecs with the intention that all of them be simultaneously * updated. Such a list, when supported, would be passed as a list of files to this routine - hence the multi-arg support. * * Parameters: * argcnt - currently always 1 (see note above). * objfilespec - mval address holding string containing filespec to process. * * No return value. */ void op_zrupdate(int argcnt, ...) { boolean_t wildcarded, noresult, seenfext, invalid; char pblkbuf[MAX_FN_LEN + 1], statbuf[MAX_FN_LEN + 1], namebuf[MAX_FN_LEN + 1]; char *chptr, chr; int status, fextlen, fnamlen, object_count; mstr objdir, rtnname; mval *objfilespec, objpath; open_relinkctl_sgm *linkctl; parse_blk pblk; plength plen; relinkrec_t *rec; struct stat outbuf; uint4 hash, prev_hash_index; va_list var; if (RESTRICTED(zrupdate_op)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_RESTRICTEDOP, 1, "ZRUPDATE"); /* Currently only expecting one value per invocation right now. That will change in phase 2, hence the stdarg setup. */ va_start(var, argcnt); assert(1 == argcnt); objfilespec = va_arg(var, mval *); va_end(var); MV_FORCE_STR(objfilespec); /* Initialize pblk with information about the pattern in the argument to ZRUPDATE. */ memset(&pblk, 0, SIZEOF(pblk)); pblk.buffer = pblkbuf; pblk.buff_size = (unsigned char)(MAX_FN_LEN); /* Pass size of buffer - 1 (standard protocol for parse_file). */ pblk.def1_buf = DOTOBJEXT; /* Default .o file type if not specified. */ pblk.def1_size = SIZEOF(DOTOBJEXT) - 1; pblk.fop = F_SYNTAXO; /* Syntax check only - bypass directory / file existence check. */ status = parse_file(&objfilespec->str, &pblk); if (ERR_PARNORMAL != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_FILEPARSE, 2, objfilespec->str.len, objfilespec->str.addr, status); wildcarded = (pblk.fnb & F_WILD); /* Our error logic is different depending on the presence of wildcards. */ invalid = FALSE; if (0 != pblk.b_name) { /* A file name was specified (if not, it is probably hard to find the file name, but that can be dealt with later). * Like above, the string must be comprised of valid chars for routine names. */ for (chptr = pblk.l_name, fnamlen = pblk.b_name; 0 < fnamlen; chptr++, fnamlen--) { if ((ASTERISK != *chptr) && (QUESTION != *chptr)) { /* Substitute '%' for '_'. While this substitution is valid just for the first char, only the first * char can be '%', so a check of the second or later char would fail the '%' substitution anyway. */ chr = ('_' == *chptr) ? '%' : *chptr; /* We see a char that isn't a wildcard character. If this is the first character, it can be * alpha or percent. If the second or later character, it can be alphanumeric. */ if (((fnamlen == pblk.b_name) && (!VALID_MNAME_FCHAR(chr) || ('%' == *chptr))) /* If 1st char */ || ((fnamlen != pblk.b_name) && !VALID_MNAME_NFCHAR(chr))) /* If 2nd+ char */ { invalid = TRUE; break; } } } } else if (!wildcarded) invalid = TRUE; if (invalid) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_FILEPARSE, 2, objfilespec->str.len, objfilespec->str.addr, ERR_TEXT, 2, RTS_ERROR_TEXT("Filename is not a valid routine name")); /* Do a simlar check for the file type */ seenfext = FALSE; if (0 != pblk.b_ext) { /* If a file extension was specified - get the extension sans any potential wildcard character. */ for (chptr = pblk.l_ext + 1, fextlen = pblk.b_ext - 1; 0 < fextlen; chptr++, fextlen--) { /* Check each character in the extension except the first, which is the dot if extension exists at all. */ if (ASTERISK != *chptr) { /* We see a char that is not a '*' wildcard character. If we have already seen our "o" file * extension or a '?' wildcard character (which we assume is "o"), this char makes our requirement * filetype impossible, so raise an error. */ if (seenfext || ((OBJEXT != *chptr) && (QUESTION != *chptr))) { invalid = TRUE; break; } seenfext = TRUE; } } } else if (!wildcarded) invalid = TRUE; if (invalid) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_FILEPARSE, 2, objfilespec->str.len, objfilespec->str.addr, ERR_TEXT, 2, RTS_ERROR_TEXT("Unsupported filetype specified")); zsrch_clr(STRM_ZRUPDATE); /* Clear any existing search cache */ object_count = 0; do { /* The DO-WHILE form is to do one iteration even if not wildcarded. */ plen.p.pint = op_fnzsearch(objfilespec, STRM_ZRUPDATE, 0, &objpath); if (TRUE == (noresult = (0 == objpath.str.len))) /* Note: assignment! */ { /* No (more) matches. In wildcarded case we are simply done with this loop. */ if (wildcarded) break; else { /* In a non-wildcarded case we want to verify whether the user is referring to a previously existent * file that got removed or the one that op_fnzsearch() silently skipped due to access issues. So, * set the objpath to the user-provided string after processing by parse_file() and adjust the * length fields accordingly. */ objpath.str.addr = pblk.buffer; objpath.str.len = pblk.b_esl; SET_LENGTHS(&plen, objpath.str.addr, objpath.str.len, FALSE); } } /* Verify the extension and filename on wildcarded patterns; the non-wildcarded ones have been checked earlier. * Start with the extension. */ if (wildcarded && ((SIZEOF(DOTOBJEXT) - 1 != plen.p.pblk.b_ext) || (OBJEXT != objpath.str.addr[plen.p.pblk.b_dir + plen.p.pblk.b_name + 1]))) continue; /* Before opening the relinkctl file, verify that a valid routine name can be derived, thus almost definitely * telling us that the object name is also correct. The only exception is when the object name starts with a '%', * so we want to note down that fact. Note that we cannot operate on the objpath memory because we would be * affecting the object name, so we have to make a copy first. */ if (wildcarded && ((0 == plen.p.pblk.b_name) || ('%' == objpath.str.addr[plen.p.pblk.b_dir]))) continue; memcpy(namebuf, objpath.str.addr + plen.p.pblk.b_dir, plen.p.pblk.b_name); rtnname.len = plen.p.pblk.b_name; rtnname.addr = namebuf; CONVERT_FILENAME_TO_RTNNAME(rtnname); /* Get rtnname before searching in relinkctl file */ if (wildcarded && !valid_mname(&rtnname)) continue; assert(!noresult || !wildcarded); /* We should have left the loop early on no results with a wildcard. */ /* The reasons for doing the below STAT depend on the situation. If we do have at least one result, we need to make * sure it is legitimate. If we have no results, we do the STAT because op_fnzsearch() on non-wildcarded requests * may cleanly return an empty list even in the face of access errors, whereas we want to notify the user about * potential access issues on a single file. */ memcpy(statbuf, objpath.str.addr, objpath.str.len); statbuf[objpath.str.len] = '\0'; LSTAT_FILE(statbuf, &outbuf, status); /* We use lstat to detect and eliminate soft links. */ if (-1 == status) { /* In the wildcarded case we just skip missing files. Any access error (but not the case of a missing file) * gets reported on non-wildcarded patterns. If we did not find this file initially, it does not matter if * exists now. We simply want to determine whether the reason that we could not find it had to do with * access errors. */ if (wildcarded) continue; else if (ENOENT != errno) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_FILEPARSE, 2, objfilespec->str.len, objfilespec->str.addr, errno); } else if (!S_ISREG(outbuf.st_mode)) { /* We are only interested in regular files. */ continue; } /* Extraction of object directory is different depending on whether a match was found or not. If we have a match, * then objpath already contains the full path to the object, including the directory. If not, then we cannot use * objpath because it was populated with user's argument to ZRUPDATE, which might not have a directory name in it. * So, in that case we derive the directory name from the original parsing results populated by parse_file(). */ if (noresult) { objdir.addr = pblk.l_dir; objdir.len = pblk.b_dir; } else { objdir.addr = objpath.str.addr; objdir.len = plen.p.pblk.b_dir; } linkctl = relinkctl_attach(&objdir, &objpath.str, 0); /* Create/attach/open relinkctl file. */ if (NULL == linkctl) { if (wildcarded) continue; else { /* Note that the below errno value should come from the realpath() call in relinkctl_attach() * invoked above, so we need to make sure nothing gets called in between. */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_FILEPARSE, 2, objfilespec->str.len, objfilespec->str.addr, errno); } } if (!wildcarded) { /* In the non-wildcarded case we decide whether to proceed with the cycle bump thusly: * 1. If the specified file exists, it may or may not be accounted for in the relinkctl file, meaning that * we need to either add it there or simply update its cycle. * 2. If the file does not exist on disk, but the routine is found in the relinkctl file, update its cycle. * 3. If there is no file and no entry for it in the relinkctl file, do nothing (info error removed by * request). */ COMPUTE_RELINKCTL_HASH(&rtnname, hash, linkctl->hdr->relinkctl_hash_buckets); rec = relinkctl_find_record(linkctl, &rtnname, hash, &prev_hash_index); if ((NULL == rec) && noresult) return; } rec = relinkctl_insert_record(linkctl, &rtnname); RELINKCTL_CYCLE_INCR(rec, linkctl); /* Increment cycle indicating change to world */ object_count++; /* Update the count of valid objects encountered. */ } while (wildcarded); /* For a wildcarded request that did not return any suitable object files give a no objects found error as a "soft" INFO * level message, which gets supressed in except in direct mode. */ if (wildcarded && (0 == object_count)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) MAKE_MSG_INFO(ERR_FILEPARSE), 2, objfilespec->str.len, objfilespec->str.addr, ERR_TEXT, 2, RTS_ERROR_TEXT("No object files found")); } #endif /* AUTORELINK_SUPPORTED */ fis-gtm-V7.0-005/sr_unix/op_ztrigger.c0000644000032200000250000003112314342376335016545 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2021 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_string.h" #include "gtm_stdlib.h" #include "gtm_stdio.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "jnl.h" #include "copy.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "repl_msg.h" #include "gtmsource.h" #include "interlock.h" #include #include "stack_frame.h" #include "gv_trigger.h" #include "gtm_trigger.h" #include "gv_trigger_protos.h" #include "mv_stent.h" #include "stringpool.h" #include "trigger.h" #include "gtm_trigger_trc.h" #include "tp_frame.h" #include "tp_restart.h" #include "t_end.h" #include "t_retry.h" #include "t_begin.h" #include "rc_cpt_ops.h" #include "add_inter.h" #include "sleep_cnt.h" #include "wcs_sleep.h" #include "util.h" #include "op.h" /* for op_tstart prototype */ #include "format_targ_key.h" /* for format_targ_key prototype */ #include "tp_set_sgm.h" /* for tp_set_sgm prototype */ #include "op_tcommit.h" /* for op_tcommit prototype */ #include "have_crit.h" #include "gvcst_protos.h" #include "gtmimagename.h" #include "is_file_identical.h" #include "anticipatory_freeze.h" #include "gvt_inline.h" LITREF mval literal_null; #ifdef DEBUG GBLREF char *update_array, *update_array_ptr; GBLREF uint4 update_array_size; /* needed for the ENSURE_UPDATE_ARRAY_SPACE macro */ #endif GBLREF gd_region *gv_cur_region; GBLREF gv_key *gv_currkey, *gv_altkey; GBLREF int4 gv_keysize; GBLREF gv_namehead *gv_target; GBLREF jnl_fence_control jnl_fence_ctl; GBLREF uint4 dollar_tlevel; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF sgm_info *sgm_info_ptr; GBLREF unsigned char cw_set_depth; GBLREF unsigned int t_tries; GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; GBLREF boolean_t need_kip_incr; GBLREF uint4 update_trans; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF sgmnt_addrs *kip_csa; GBLREF boolean_t skip_dbtriggers; /* see gbldefs.c for description of this global */ GBLREF int tprestart_state; GBLREF stack_frame *frame_pointer; #ifdef GTM_TRIGGER GBLREF int4 gtm_trigger_depth; GBLREF int4 tstart_trigger_depth; GBLREF boolean_t skip_INVOKE_RESTART; GBLREF boolean_t ztwormhole_used; /* TRUE if $ztwormhole was used by trigger code */ GBLREF mval dollar_ztwormhole; #endif error_def(ERR_DBROLLEDBACK); error_def(ERR_GVZTRIGFAIL); error_def(ERR_TPRETRY); error_def(ERR_ZTRIGNOTRW); error_def(ERR_REMOTEDBNOTRIG); #ifndef GTM_TRIGGER error_def(ERR_UNIMPLOP); void op_ztrigger(void) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP); } #else void op_ztrigger(void) { node_local_ptr_t cnl; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; enum cdb_sc cdb_status; jnl_format_buffer *jfb, *ztworm_jfb; uint4 nodeflags; boolean_t write_logical_jnlrecs, jnl_format_done; boolean_t is_tpwrap; boolean_t lcl_implicit_tstart; /* local copy of the global variable "implicit_tstart" */ boolean_t want_root_search = FALSE; uint4 lcl_onln_rlbkd_cycle; gtm_trigger_parms trigparms; gvt_trigger_t *gvt_trigger; gvtr_invoke_parms_t gvtr_parms; int gtm_trig_status, rc; unsigned int idx; unsigned char *save_msp; mv_stent *save_mv_chain; # ifdef DEBUG boolean_t is_mm; GTMTRIG_ONLY(enum cdb_sc save_cdb_status;) # endif DCL_THREADGBL_ACCESS; if (gv_cur_region->read_only) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_ZTRIGNOTRW, 2, REG_LEN_STR(gv_cur_region)); SETUP_THREADGBL_ACCESS; csa = cs_addrs; assert(('\0' != gv_currkey->base[0]) && gv_currkey->end); assert(MAX_MIDENT_LEN >= strlen((char *)gv_currkey->base)); /* For Veracode to not flag strlen() below */ if (NULL == csa) /* Remote region */ RTS_ERROR_CSA_ABT(csa, VARLSTCNT(6) ERR_REMOTEDBNOTRIG, 4, strlen((char *)gv_currkey->base), (char *)gv_currkey->base, REG_LEN_STR(gv_cur_region)); csd = csa->hdr; cnl = csa->nl; save_msp = NULL; DEBUG_ONLY(is_mm = (dba_mm == csd->acc_meth)); TRIG_CHECK_REPLSTATE_MATCHES_EXPLICIT_UPDATE(gv_cur_region, csa); if (IS_EXPLICIT_UPDATE) { /* This is an explicit update. Set ztwormhole_used to FALSE. Note that we initialize this only at the * beginning of the transaction and not at the beginning of each try/retry. If the application used * $ztwormhole in any retsarting try of the transaction, we consider it necessary to write the * TZTWORM/UZTWORM record even though it was not used in the succeeding/committing try. */ ztwormhole_used = FALSE; } JNLPOOL_INIT_IF_NEEDED(csa, csd, cnl, SCNDDBNOUPD_CHECK_TRUE); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_GVZTRIGFAIL); lcl_implicit_tstart = FALSE; assert(NULL != update_array); assert(NULL != update_array_ptr); assert(0 != update_array_size); assert(update_array + update_array_size >= update_array_ptr); for (;;) { assert(csd == cs_data); /* assert csd is in sync with cs_data even if there were MM db file extensions */ assert(csd == csa->hdr); jnl_format_done = FALSE; write_logical_jnlrecs = JNL_WRITE_LOGICAL_RECS(csa); gvtr_parms.num_triggers_invoked = 0; /* clear any leftover value */ is_tpwrap = FALSE; if (!skip_dbtriggers) /* No trigger init needed if skip_dbtriggers is TRUE (e.g. mupip load etc.) */ { GVTR_INIT_AND_TPWRAP_IF_NEEDED(csa, csd, gv_target, gvt_trigger, lcl_implicit_tstart, is_tpwrap, ERR_GVZTRIGFAIL); assert(0 < dollar_tlevel); assert(gvt_trigger == gv_target->gvt_trigger); sgm_info_ptr->update_trans |= UPDTRNS_ZTRIGGER_MASK; if (NULL != gvt_trigger) { /* In other trigger types, a PUSH_ZTOLDMVAL_ON_M_STACK is used here to save ztoldval but since * this trigger type doesn't have one, pushing that extra mval on the stack makes no sense. * Instead, we just do the basics that the PUSH_ZTOLDMVAL_ON_M_STACK does to save a marker of * msp and mv_chain to easily restore later rather than having to pop the values (more * expensively) */ save_msp = msp; save_mv_chain = mv_chain; /* Invoke relevant trigger(s) regardless whether data exists or not (we don't even check) */ JNL_FORMAT_ZTWORM_IF_NEEDED(csa, write_logical_jnlrecs, JNL_ZTRIG, gv_currkey, NULL, ztworm_jfb, jfb, jnl_format_done); /* Initialize trigger parms that dont depend on the context of the matching trigger. All of * these parms are initialized to NULL. This causes op_svget to report them as NULL strings * whichi s all we need for ZTRIGGER. Note $ztvalue is not not updateable for this type * of trigger. */ trigparms.ztoldval_new = NULL; trigparms.ztdata_new = NULL; trigparms.ztvalue_new = NULL; gvtr_parms.gvtr_cmd = GVTR_CMDTYPE_ZTRIGGER; gvtr_parms.gvt_trigger = gvt_trigger; /* Now that we have filled in minimal information, let "gvtr_match_n_invoke" do the rest */ gtm_trig_status = gvtr_match_n_invoke(&trigparms, &gvtr_parms); INCR_GVSTATS_COUNTER(csa, csa->nl, n_ztrigger_fired, gvtr_parms.num_triggers_invoked); assert((0 == gtm_trig_status) || (ERR_TPRETRY == gtm_trig_status)); if (ERR_TPRETRY == gtm_trig_status) { /* A restart has been signaled that we need to handle or complete the handling of. * This restart could have occurred reading the trigger in which case no * tp_restart() has yet been done or it could have occurred in trigger code in * which case we need to finish the incomplete tp_restart. In both cases this * must be an implicitly TP wrapped transaction. Our action is to complete the * necessary tp_restart() logic (t_retry is already completed so should be skipped) * and then re-do the op_ztrigger logic. */ assert(lcl_implicit_tstart); assert(CDB_STAGNATE >= t_tries); cdb_status = cdb_sc_normal; /* signal "retry:" to avoid t_retry call */ goto retry; } REMOVE_ZTWORM_JFB_IF_NEEDED(ztworm_jfb, jfb, sgm_info_ptr); /* Instead of POP_MVALS_FROM_M_STACK_IF_NEEDED, do a stripped-down version since we don't * do anything with $ztoldval. This usually pops off 3 or more mvals saved by trigger processing. */ if (save_msp > msp) UNW_MV_STENT_TO(save_msp, save_mv_chain); } } /* finish off any pending root search from previous retry */ REDO_ROOT_SEARCH_IF_NEEDED(want_root_search, cdb_status); if (cdb_sc_normal != cdb_status) { /* gvcst_root_search invoked from REDO_ROOT_SEARCH_IF_NEEDED ended up with a restart situation but did not * actually invoke t_retry. Instead, it returned control back to us asking us to restart. */ goto retry; } if (write_logical_jnlrecs) { /* Only write jnl recs if we are in fact journaling.. * skip_dbtriggers is set to TRUE for trigger unsupporting platforms. So, nodeflags will be set to skip * triggers on secondary. This ensures that updates happening in primary (trigger unsupporting platform) * is treated in the same order in the secondary (trigger supporting platform) irrespective of whether * the secondary has defined triggers or not for the global that is being updated. */ assert(dollar_tlevel); if (!jnl_format_done) { nodeflags = 0; if (skip_dbtriggers) nodeflags |= JS_SKIP_TRIGGERS_MASK; /* Do not replicate implicit updates */ assert(tstart_trigger_depth <= gtm_trigger_depth); if (gtm_trigger_depth > tstart_trigger_depth) { /* Ensure that JS_SKIP_TRIGGERS_MASK and JS_NOT_REPLICATED_MASK are mutually exclusive. */ assert(!(nodeflags & JS_SKIP_TRIGGERS_MASK)); nodeflags |= JS_NOT_REPLICATED_MASK; } /* Write ZTRIGGER journal record */ jfb = jnl_format(JNL_ZTRIG, gv_currkey, NULL, nodeflags); assert(NULL != jfb); } } /* If we started this transaction, finish it now verifying it completed successfully */ if (lcl_implicit_tstart) { GVTR_OP_TCOMMIT(cdb_status); if (cdb_sc_normal != cdb_status) goto retry; } INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_ztrigger, 1); return; retry: if (lcl_implicit_tstart) { assert(!skip_dbtriggers); assert(!skip_INVOKE_RESTART); assert((cdb_sc_normal != cdb_status) || (ERR_TPRETRY == gtm_trig_status)); if (cdb_sc_normal != cdb_status) skip_INVOKE_RESTART = TRUE; /* causes t_retry to invoke only tp_restart * without any rts_error */ /* else: t_retry has already been done by gtm_trigger so no need to do it again for this try */ } assert((cdb_sc_normal != cdb_status) || lcl_implicit_tstart); if (cdb_sc_normal != cdb_status) { /* See comment above about POP_MVALS_FROM_M_STACK_IF_NEEDED */ if ((NULL != save_msp) && (save_msp > msp)) UNW_MV_STENT_TO(save_msp, save_mv_chain); t_retry(cdb_status); skip_INVOKE_RESTART = FALSE; } else { /* else: t_retry has already been done so no need to do that again but need to still invoke tp_restart * to complete pending "tprestart_state" related work. */ assert(ERR_TPRETRY == gtm_trig_status); TRIGGER_BASE_FRAME_UNWIND_IF_NOMANSLAND; /* See comment above about POP_MVALS_FROM_M_STACK_IF_NEEDED */ if ((NULL != save_msp) && (save_msp > msp)) UNW_MV_STENT_TO(save_msp, save_mv_chain); rc = tp_restart(1, !TP_RESTART_HANDLES_ERRORS); assert(0 == rc && TPRESTART_STATE_NORMAL == tprestart_state); } assert(0 < t_tries); if (lcl_implicit_tstart) { SET_WANT_ROOT_SEARCH(cdb_status, want_root_search); assert(!skip_INVOKE_RESTART); /* if set to TRUE above, should have been reset by t_retry */ } /* At this point, we can be in TP only if we implicitly did a tstart in op_ztrigger trying to drive a trigger. * Assert that. So reinvoke the T_BEGIN call only in case of TP. For non-TP, update_trans is unaffected by * t_retry. */ assert(!dollar_tlevel || lcl_implicit_tstart); if (dollar_tlevel) { /* gvcst_kill has similar code and should be maintained in parallel */ tp_set_sgm(); /* set sgm_info_ptr & first_sgm_info for TP start */ T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_GVZTRIGFAIL); } /* In case this is MM and t_retry() remapped an extended database, reset csd */ assert(is_mm || (csd == cs_data)); csd = cs_data; } } #endif /* #ifdef GTM_TRIGGER */ fis-gtm-V7.0-005/sr_unix/op_zut.c0000644000032200000250000000420114342376330015522 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2021 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 #include "gtm_time.h" #include "op.h" #include "arit.h" LITREF int4 ten_pwr[]; error_def(ERR_WEIRDSYSTIME); #define DECIMAL_BASE 10 /* stolen from gdsfhead which is silly to include here */ #define FLOAT_SKEW 10 /* 1 order of magnitude microsec shrinkage to prevent floating point arithmetic from allowing * ordered comparisons with other time ISVs to seem like time can go backward */ void op_zut(mval *s) { struct timespec ts; gtm_int8 microseconds, msectmp; int numdigs; int4 pwr; assertpro(-1 != clock_gettime(CLOCK_REALTIME, &ts)); #ifdef DEBUG /* The OS should never return an invalid time */ if ((ts.tv_sec < 0) || (ts.tv_nsec < 0) || (ts.tv_nsec > NANOSECS_IN_SEC)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_WEIRDSYSTIME); #endif /* $ZUT original supported only up to microsecond granularity. While it is tempting to * expose upto nanosecond granularity, doing so is a major change to the interface. */ msectmp = microseconds = (1LL * MICROSECS_IN_SEC * ts.tv_sec) + (ts.tv_nsec / NANOSECS_IN_USEC); assert(0 < microseconds); /* Count the number of digits */ for (numdigs = 0; msectmp; numdigs++, msectmp /= DECIMAL_BASE) ; if (numdigs <= NUM_DEC_DG_1L) { s->m[0] = 0; s->m[1] = (int4)microseconds * ten_pwr[NUM_DEC_DG_1L - numdigs]; } else { microseconds -= FLOAT_SKEW; /* to prevent floating arithmetic from making time appear to run backwards */ pwr = ten_pwr[numdigs - NUM_DEC_DG_1L]; s->m[0] = (microseconds % pwr) * ten_pwr[NUM_DEC_DG_2L - numdigs]; s->m[1] = microseconds / pwr; } s->mvtype = MV_NM; s->e = MV_XBIAS + numdigs; s->sgn = 0; return; } fis-gtm-V7.0-005/sr_unix/outc.c0000755000032200000250000000157414342376330015171 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "gtm_unistd.h" #include #include "io.h" #include "iottdef.h" #include "gtmio.h" GBLREF io_pair io_curr_device; #ifdef __sparc int outc(char intch) #else int outc(int intch) #endif { char ch; d_tt_struct *tt_ptr; int status; ch = intch; tt_ptr = (d_tt_struct*) io_curr_device.out->dev_sp; DOWRITERC(tt_ptr->fildes, &ch, 1, status); if(0 != status) rts_error(VARLSTCNT(1) status); return 0; } fis-gtm-V7.0-005/sr_unix/parse_file.c0000644000032200000250000003340614342376330016324 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_inet.h" /* for struct in_addr */ #include "gtm_stat.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_unistd.h" #include "gtm_socket.h" /* for using sockaddr and sockaddr_storage */ #include "gtm_netdb.h" #include "gtm_ipv6.h" #include "parse_file.h" #include "have_crit.h" #include "io.h" #include "iosp.h" #include "eintr_wrappers.h" #include "trans_log_name.h" #include "setzdir.h" #include "gtmmsg.h" /* for gtm_putmsg */ #include "min_max.h" #define LOCALHOSTNAME "localhost" #define LOCALHOSTNAME6 "::1" error_def(ERR_FILENOTFND); error_def(ERR_GETADDRINFO); error_def(ERR_GETNAMEINFO); error_def(ERR_FILEPATHTOOLONG); error_def(ERR_PARNORMAL); error_def(ERR_SYSCALL); error_def(ERR_TEXT); enum parse_state { NOSTATE, NAME, DOT1, DOT2, SLASH }; GBLREF mval dollar_zdir; int4 parse_file(mstr *file, parse_blk *pblk) { struct stat statbuf; struct addrinfo *ai_ptr, *localhost_ai_ptr, *temp_ai_ptr, hints; mstr trans, tmp; int status, diff; uint4 local_node_len, query_node_len, node_name_len; parse_blk def; char local_node_name[MAX_HOST_NAME_LEN + 1], query_node_name[MAX_HOST_NAME_LEN + 1]; char *base, *ptr, *top, *del, *node, *name, *ext, ch; char **hostaddrlist; char def_string[MAX_FN_LEN + 1]; boolean_t hasnode, hasdir, hasname, hasext, wilddir, wildname; enum parse_state state; struct sockaddr_storage query_sas; struct sockaddr localhost_sa, *localhost_sa_ptr; mval def_trans; int errcode; intrpt_state_t prev_intrpt_state; size_t move_len; /* try to give static analysis more assurance on mmemove() params */ pblk->fnb = 0; ai_ptr = localhost_ai_ptr = temp_ai_ptr = NULL; assert(((unsigned int)pblk->buff_size + 1) <= (MAX_FN_LEN + 1)); /* All callers of parse_blk set buff_size to 1 less than the allocated buffer. This is because buff_size is a char * type (for historical reasons) and so cannot go more than 255 whereas we support a max of 255 characters. So we * allocate buffers that contain one more byte (for the terminating '\0') but dont set that in buff_size. Use * that extra byte for the trans_log_name call. */ status = TRANS_LOG_NAME(file, &trans, pblk->buffer, pblk->buff_size + 1, dont_sendmsg_on_log2long); if (SS_LOG2LONG == status) return ERR_FILEPATHTOOLONG; assert(trans.addr == pblk->buffer); assert(0 <= trans.len); typedef char transbuf_t[trans.len]; /* let static analysis infer our buffer size */ transbuf_t *delptr, *trans_ptr; memset(&def, 0, SIZEOF(def)); /* Initial the defaults to zero */ if (pblk->def1_size > 0) { /* Parse default filespec if supplied */ def.fop = F_SYNTAXO; def.buffer = def_string; def.buff_size = MAX_FN_LEN; def.def1_size = pblk->def2_size; def.def1_buf = pblk->def2_buf; tmp.len = pblk->def1_size; tmp.addr = pblk->def1_buf; if (ERR_PARNORMAL != (status = parse_file(&tmp, &def))) /* Note Assignment */ return status; assert(!def.b_node); if (def.b_dir) def.fnb |= F_HAS_DIR; if (def.b_name) def.fnb |= F_HAS_NAME; if (def.b_ext) def.fnb |= F_HAS_EXT; } wildname = wilddir = hasnode = hasdir = hasname = hasext = FALSE; node = base = ptr = trans.addr; top = ptr + trans.len; node_name_len = (uint4)trans.len; if ((0 == trans.len) || ('/' != *ptr)) { /* No file given, no full path given, or a nodename was specified */ setzdir(NULL, &def_trans); /* Default current directory if none given */ assert((0 == dollar_zdir.str.len) /* dollar_zdir not initialized yet, possible thru main() -> gtm_chk_dist() */ || ((def_trans.str.len == dollar_zdir.str.len) /* Check if cwd and cached value are the same */ && (0 == memcmp(def_trans.str.addr, dollar_zdir.str.addr, def_trans.str.len)))); if (pblk->fop & F_PARNODE) { /* What we have could be a nodename */ assert(pblk->fop & F_SYNTAXO); while (node < top) { ch = *node++; if (':' == ch) /* We have nodeness */ break; if ('/' == ch) { /* Not a node - bypass node checking */ node = top; break; } } if (node < top) { hasnode = TRUE; ptr = base = node; /* Update pointers past node name */ /* See if the desired (query) node is the local node */ node_name_len = (uint4)(node - trans.addr); /* Scanned node including ':' */ query_node_len = MIN((node_name_len - 1), MAX_HOST_NAME_LEN); /* Pure name length, no ':' on end */ assert(MAX_HOST_NAME_LEN >= query_node_len); assert(0 < query_node_len); assert(':' == *(trans.addr + query_node_len)); memcpy(query_node_name, trans.addr, query_node_len); query_node_name[query_node_len] = 0; localhost_sa_ptr = NULL; /* Null value needed if not find query node (remote default) */ CLIENT_HINTS(hints); DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); if (0 != (errcode = getaddrinfo(query_node_name, NULL, &hints, &ai_ptr))) /* Assignment! */ ai_ptr = NULL; /* Skip additional lookups */ else memcpy((sockaddr_ptr)&query_sas, ai_ptr->ai_addr, ai_ptr->ai_addrlen); CLIENT_HINTS(hints); if (0 == (errcode = getaddrinfo(LOCALHOSTNAME, NULL, &hints, &localhost_ai_ptr)) && (0 == memcmp(localhost_ai_ptr->ai_addr, (sockaddr_ptr)&query_sas, localhost_ai_ptr->ai_addrlen))) { localhost_sa_ptr = localhost_ai_ptr->ai_addr; } FREEADDRINFO(localhost_ai_ptr); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); if (ai_ptr && !localhost_sa_ptr) { /* Have not yet established this is not a local node -- check further */ GETHOSTNAME(local_node_name, MAX_HOST_NAME_LEN, status); if (-1 == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("gethostname"), CALLFROM, errno); CLIENT_HINTS(hints); DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); if (0 != (errcode = getaddrinfo(local_node_name, NULL, &hints, &localhost_ai_ptr))) localhost_ai_ptr = NULL; /* Empty address list */ for (temp_ai_ptr = localhost_ai_ptr; temp_ai_ptr!= NULL; temp_ai_ptr = temp_ai_ptr->ai_next) { if (0 == memcmp((sockaddr_ptr)&query_sas, temp_ai_ptr->ai_addr, temp_ai_ptr->ai_addrlen)) { localhost_sa_ptr = temp_ai_ptr->ai_addr; break; /* Tiz truly a local node */ } } FREEADDRINFO(localhost_ai_ptr); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); } if (ai_ptr && !localhost_sa_ptr) { CLIENT_HINTS(hints); DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); if (0 != (errcode = getaddrinfo(LOCALHOSTNAME6, NULL, &hints, &localhost_ai_ptr))) localhost_ai_ptr = NULL; /* Empty address list */ for (temp_ai_ptr = localhost_ai_ptr; temp_ai_ptr!= NULL; temp_ai_ptr = temp_ai_ptr->ai_next) { if (0 == memcmp((sockaddr_ptr)&query_sas, temp_ai_ptr->ai_addr, temp_ai_ptr->ai_addrlen)) { localhost_sa_ptr = temp_ai_ptr->ai_addr; break; /* Tiz truly a local node */ } } FREEADDRINFO(localhost_ai_ptr); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); } if (!localhost_sa_ptr) /* Not local (or an unknown) host given */ { /* Remote node specified -- don't apply any defaults */ DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); FREEADDRINFO(ai_ptr); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); pblk->l_node = trans.addr; pblk->b_node = node_name_len; pblk->l_dir = base; pblk->b_dir = top - base; pblk->l_name = pblk->l_ext = base + pblk->b_dir; pblk->b_esl = pblk->b_node + pblk->b_dir; pblk->b_name = pblk->b_ext = 0; pblk->fnb |= (hasnode << V_HAS_NODE); return ERR_PARNORMAL; } DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); FREEADDRINFO(ai_ptr); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); /* Remove local node name from filename buffer */ assert(trans.len > node_name_len); /* this is a function of how we determined node_name_len */ trans_ptr = (transbuf_t *) trans.addr; move_len = MIN(trans.len - node_name_len, MAX_FN_LEN); assert (sizeof(transbuf_t) >= move_len); assert(NULL != trans_ptr); memmove((void *)trans_ptr, node, move_len); ptr = base = node -= node_name_len; top -= node_name_len; trans.len -= node_name_len; if ('/' == *base) /* No default directory if full path given */ def_trans.str.len = 0; } else { /* Supplied text was not a node -- reset pointer back to beginning for rescan */ node = trans.addr; } } /* If parse buffer is not large enough, return error */ if (def_trans.str.len + trans.len > pblk->buff_size) return ERR_FILEPATHTOOLONG; /* Construct full filename to parse prefixing given filename with default path prefix */ if (0 < def_trans.str.len) { memmove(ptr + def_trans.str.len, ptr, MIN(trans.len, MAX_FN_LEN)); memcpy(ptr, def_trans.str.addr, def_trans.str.len); assert('/' == ptr[def_trans.str.len - 1]); ptr += def_trans.str.len; top += def_trans.str.len; } } name = ptr; state = NOSTATE; for (; ptr < top;) { ch = *ptr; if ('.' == ch) { /* Could be /./ or /../ or name.name */ ptr++; state = (DOT1 == state) ? ((DOT2 == state) ? NAME : DOT2) : DOT1; } else if (ch == '/') { /* We must still be doing the path */ ptr++; hasdir = TRUE; hasname = FALSE; hasext = FALSE; wilddir |= wildname; wildname = FALSE; if ((DOT1 != state) && (DOT2 != state) && (SLASH != state)) { /* No dots seen recently so scan as if this is start of filename */ state = SLASH; name = ptr; continue; } if (DOT1 == state) { /* Just remove "./" chars from path */ del = ptr - 2; } else if (DOT2 == state) { /* Have xx/../ construct. Remove /../ and previous level directory from path */ del = ptr - 4; /* /../ characters being removed */ assert ('/' == *del); if (del > base) { del--; while ('/' != *del) del--; } assert((del >= base) && ('/' == *del)); del++; } else if (SLASH == state) { /* Remove duplicate slash from path */ del = ptr - 1; while ((ptr < top) && ('/' == *ptr)) ptr++; } assert(top >= ptr); /* Use asserts to tell static analyis that we have thought about this memmove() */ move_len = top - ptr; assert((0 <= move_len) && (trans.len >= move_len)) ; assert(NULL != ptr); assert(NULL != del); assert((del <= ptr) && (del >= trans.addr)); delptr = (transbuf_t *) del; memmove((void *) delptr, ptr, move_len); diff = (int)(ptr - del); ptr -= diff; top -= diff; state = SLASH; name = ptr; } else { /* Hopeful of filename */ hasname = TRUE; while (ptr < top) /* Do small scan looking for filename end */ { ch = *ptr; if ('/' == ch) break; /* Ooops, still doing path */ if ('.' == ch) {/* Filename has an extension */ hasext = TRUE; ext = ptr; } else if (('?' == ch) || ('*' == ch)) wildname = TRUE; ptr++; } state = NAME; } } /* Handle scan end with non-normal state */ if ((SLASH == state) || (DOT1 == state) || (DOT2 == state)) { assert(!hasname && !hasext); hasdir = TRUE; if (state == DOT1) { /* Ignore ./ */ top--; ptr--; } if (DOT2 == state) { /* Ignore ../ plus last directory level specified */ del = ptr - 3; /* on the end */ assert ('/' == *del); if (del > base) { del--; while ('/' != *del) del--; } assert((del >= base) && ('/' == *del)); del++; ptr = top = del; name = ptr; } } if (!hasname) { assert(!hasext); name = ptr; if (def.fnb & F_HAS_NAME) { /* Use default filename if we didn't find one */ diff = (int)(name - node); if (def.b_name + diff > pblk->buff_size) return ERR_FILEPATHTOOLONG; memcpy(name, def.l_name, def.b_name); ptr += def.b_name; } ext = ptr; } if (!hasext) { ext = ptr; if (def.fnb & F_HAS_EXT) { /* Use default file extension if we didn't find one */ diff = (int)((ext - node)); if (def.b_ext + diff > pblk->buff_size) return ERR_FILEPATHTOOLONG; memcpy(ext, def.l_ext, def.b_ext); ptr += def.b_ext; } } pblk->b_name = ext - name; pblk->b_ext = ptr - ext; if (!hasdir && (def.fnb & F_HAS_DIR)) { diff = (int)(name - base); diff = def.b_dir - diff; if ((pblk->b_name + pblk->b_ext + ((0 < def.b_dir) ? def.b_dir : 0 )) > pblk->buff_size) return ERR_FILEPATHTOOLONG; if (0 < diff) memmove(name + diff, name, pblk->b_name + pblk->b_ext); /*return ERR_FILEPATHTOOLONG ensures this is safe*/ else if (0 > diff) memcpy(name + diff, name, pblk->b_name + pblk->b_ext); memcpy(base, def.l_dir, def.b_dir); ptr += diff; name += diff; } pblk->b_dir = name - base; pblk->b_esl = ptr - base; pblk->l_dir = base; pblk->l_name = base + pblk->b_dir; pblk->l_ext = pblk->l_name + pblk->b_name; pblk->fnb |= (hasdir << V_HAS_DIR); pblk->fnb |= (hasname << V_HAS_NAME); pblk->fnb |= (hasext << V_HAS_EXT); pblk->fnb |= (wildname << V_WILD_NAME); pblk->fnb |= (wilddir << V_WILD_DIR); if (!(pblk->fop & F_SYNTAXO) && !wilddir) { assert('/' == pblk->l_dir[pblk->b_dir - 1]); if (pblk->b_dir > 1) { pblk->l_dir[pblk->b_dir - 1] = 0; STAT_FILE(pblk->l_dir, &statbuf, status); pblk->l_dir[pblk->b_dir - 1] = '/'; if ((-1 == status) || !(statbuf.st_mode & S_IFDIR)) return ERR_FILENOTFND; } } return ERR_PARNORMAL; } fis-gtm-V7.0-005/sr_unix/parse_file.h0000755000032200000250000001776414342376330016345 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef PARSE_FILE_H_INCLUDED #define PARSE_FILE_H_INCLUDED #define DEF_DBEXT "*.dat" #define DEF_NODBEXT "*" typedef struct parse_blk_struct { unsigned char b_esl; /* Resultant name length */ unsigned char b_node; /* Length of node name at front - db opening only */ unsigned char b_dir; /* Length of directory path */ unsigned char b_name; /* Length of file name */ unsigned char b_ext; /* Length of extension */ unsigned char def1_size; /* Default 1 string size */ char *def1_buf; /* Default 1 buffer */ unsigned char def2_size; /* Default 2 string size */ char *def2_buf; /* Default 2 buffer */ unsigned char buff_size; /* Result buffer size */ char *buffer; /* Result buffer */ int4 fnb; /* Parse result characteristics */ int4 fop; /* Parse options SYNTAX_ONLY only */ char *l_node, /* Pointer to node specification - db opening only */ *l_dir, /* Pointer to directory path string */ *l_name, /* Pointer to file name string */ *l_ext; /* Pointer to extension string */ } parse_blk; typedef struct plength_struct { union { int4 pint; struct { unsigned char b_esl; /* Resultant name length */ unsigned char b_dir; /* Length of directory path */ unsigned char b_name; /* Length of file name */ unsigned char b_ext; /* Length of extension */ } pblk; } p; } plength; #define F_HAS_EXT 1 /* 0x01 If file has explicit extension */ #define F_HAS_NAME 2 /* 0x02 If file has explicit name */ #define F_HAS_DIR 4 /* 0x04 If file has explicit directory path */ #define F_WILD_NAME 8 /* 0x08 If there is a wild card character in the name */ #define F_WILD_DIR 16 /* 0x10 If there is a wild card character in the directory */ #define F_WILD 24 /* 0x18 If there is a wild card character in the result (dir or name) */ #define F_HAS_NODE 32 /* 0x20 If there is a node specification on the front - db opening only */ #define V_HAS_EXT 0 /* Bit offsets for F_ constants */ #define V_HAS_NAME 1 #define V_HAS_DIR 2 #define V_WILD_NAME 3 #define V_WILD_DIR 4 #define V_HAS_NODE 5 /* DB opening only */ #define F_SYNTAXO 1 /* SYNTAX ONLY - Otherwise checks on directory existence returning ERR_FILENOTFOUND if * directory does not exist or is not a directory. */ #define F_PARNODE 2 /* Look for a node specification - db opening only */ /* Sets the relevant length fields in a plength-typed structure: * b_esl - full length of PATH; * b_dir - length of the PATH preceding the file name (includes the slash); * b_name - length of the file name in PATH (excludes the extension); and * b_ext - length of the file name extension. * Note that PLEN is a pointer-type argument. ABSOLUTE expects the indication of whether the PATH is absolute or relative. */ #define SET_LENGTHS(PLEN, PATH, LENGTH, ABSOLUTE) \ { \ int i; \ boolean_t seen_ext; \ \ (PLEN)->p.pblk.b_esl = LENGTH; \ (PLEN)->p.pblk.b_ext = 0; \ for (i = LENGTH - 1, seen_ext = FALSE; i >= 0; i--) \ { \ if ('.' == *((PATH) + i)) \ { \ if (!seen_ext) \ { \ (PLEN)->p.pblk.b_ext = LENGTH - i; \ seen_ext = TRUE; \ } \ } else if ('/' == *((PATH) + i)) \ break; \ } \ assert((i >= 0) || !(ABSOLUTE)); /* On UNIX absolute paths must have '/'. */ \ (PLEN)->p.pblk.b_dir = i + 1; \ (PLEN)->p.pblk.b_name = LENGTH - (PLEN)->p.pblk.b_dir - (PLEN)->p.pblk.b_ext; \ } /* Canonicalize the path by appropriately removing '.' and '..' path modifiers. Note that all* modifications are * performed on the passed string directly. */ #define CANONICALIZE_PATH(PATH) \ { \ char *src, *dst; \ char cur_char; \ boolean_t need_slash; \ \ src = dst = (PATH); \ assert('/' == *src); \ need_slash = FALSE; \ while ('\0' != (cur_char = *src++)) \ { \ if ('/' == cur_char) \ { /* Current character is '/'. If it is the last one, do not append a trailing slash. */ \ if ('\0' == (cur_char = *src++)) \ break; \ if ('/' == cur_char) \ { /* Current sequence is '//'. Restart the loop from the second slash. */ \ src--; \ need_slash = TRUE; \ } else if ('.' == cur_char) \ { /* Current sequence is '/.'. We need to examine a few potential scenarios. In \ * particular, if we are at the last character, no need to append anything. \ */ \ if ('\0' == (cur_char = *src++)) \ break; \ if ('/' == cur_char) \ { /* Current sequence is '/./'. Restart the loop from the second '/'. */ \ src--; \ need_slash = FALSE; \ } else if ('.' == cur_char) \ { /* Current sequence is '/..'. If the next character is '/' or we are at \ * the end of the line, snip off one directory from the tail, if found. \ */ \ cur_char = *src++; \ if (('\0' == cur_char) || ('/' == cur_char)) \ { /* Find an earlier '/'. Reset to '/' if not found. */ \ while (--dst >= (PATH)) \ if ('/' == *dst) \ break; \ if ((PATH) > dst) \ { \ need_slash = TRUE; \ dst = (PATH); \ } else \ need_slash = FALSE; \ src--; \ } else \ { /* Current sequence is '/..', where x is not '/' or '\0'. */ \ need_slash = FALSE; \ *dst++ = '/'; \ *dst++ = '.'; \ *dst++ = '.'; \ *dst++ = cur_char; \ } \ } else \ { /* Current sequence is '/.', where x is not '/' or '.' or '\0'. */ \ need_slash = FALSE; \ *dst++ = '/'; \ *dst++ = '.'; \ *dst++ = cur_char; \ } \ } else \ { /* Current sequence is '/', where x is not '/' or '.' or '\0'. */ \ need_slash = FALSE; \ *dst++ = '/'; \ *dst++ = cur_char; \ } \ } else \ { /* Current character is not '/', so write it. But prepend it with a '/' if we have \ * previously indicated a need for one. \ */ \ if (need_slash) \ *dst++ = '/'; \ need_slash = FALSE; \ *dst++ = cur_char; \ } \ } \ assert(dst >= (PATH)); \ /* If we did not have anything to put in the canonicalized path, default to '/'. */ \ if (dst == (PATH)) \ *dst++ = '/'; \ *dst = '\0'; \ } /* Escape all '[' and ']' characters to prevent glob() from trying to match sets enclosed in them. */ #define ESCAPE_BRACKETS(ORIG_PATH, RES_PATH) \ { \ char *src, *dst; \ char cur_char; \ boolean_t has_slash; \ \ src = ORIG_PATH; \ dst = RES_PATH; \ has_slash = FALSE; \ while ('\0' != (cur_char = *src++)) \ { \ if ('\\' == cur_char) \ has_slash = !has_slash; \ else if (('[' == cur_char) || (']' == cur_char)) \ { \ if (!has_slash) \ *dst++ = '\\'; \ else \ has_slash = FALSE; \ } else if (has_slash) \ has_slash = FALSE; \ *dst++ = cur_char; \ } \ *dst = '\0'; \ } int4 parse_file(mstr *file, parse_blk *pblk); #endif /* PARSE_FILE_H_INCLUDED */ fis-gtm-V7.0-005/sr_unix/peekbyname.mpt0000755000032200000250000002442214342376330016712 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2015-2022 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Calls $ZPEEK() with proper offset, length and format based on the info given in ^gtmtypes global ; ; field: specifies the memory location that needs to be accessed in the C-style dotted format i.e. CONTROL_BLOCK[.FIELD].* (For ; example "gd_region.max_key_size") ; regindex: (Optional) a region name, structure index or a base address that is associated with the first (field name) argument. ; The choice is governed by the following rules applied in the following order: ; ; 1. If the value is a hex value in the form of "0xhhhhhhhh[hhhhhhhh]", then it is used as the base address of the data to ; fetch. Also in this case, the offset, length, and type are taken from the field specified in the first expression (field) ; See the $ZPEEK() function description of the "PEEK" mnemonic for more information. ; ; 2. If the first expression refers to one of the region-related structures supported by the $ZPEEK() function, this second ; expression is treated as a region name. ; ; 3. If the first expression refers to one of the replication related structures supported by the $ZPEEK() function that are ; indexed, this second expression is treated as a numerical (base 10) index value. ; ; 4. For those structures supported by the $ZPEEK() function that do not accept an argument, this second expression must be ; NULL or not specified. ; ; format: (Optional) specifies the output format in one character as defined in the "format" argument in the $ZPEEK() documentation. ; This argument overrides the automatic format detection by the %PEEKBYNAME utility. %PEEKBYNAME(field,regindex,format,gldpath) if '$quit write "GTM-E-EXTRINSIC Use $$ rather than DO to invoke ",$text(+0) quit new anindexval,aregname,digits,etrap,first,ishexnum,land,length,mnemonicandreg,offset,pattern,rest,type,typeindex new dim,ret,idx,off set etrap=$etrap new $etrap ; Do not modify caller's error trap set $ecode="",$etrap="quit" ; Defer error handling to the caller without doing anything set land=$zlevel-1 set anindexval="an index value",aregname="a region name" set first=$piece(field,".",1) set:"v6_sgmnt_data"=first first="sgmnt_data" set rest=$piece(field,".",2,$length(field,".")) if ("sgmnt_data"=first)&("freeze_online"=rest) set first="node_local" ; cough, cough, kludge, kludge - temporarily hide move do:""=rest error("NOFIELD") set digits=$select("x86"=$p($zver," ",4):8,1:16) set regindex=$get(regindex) ; Is regindex a hex number in the acceptable address range (1-8 digits on 32-bit platforms and 1-16 digits on 64-bit)? set pattern=""""_regindex_"""?1""0x""1."_digits_"(1N,1""A"",1""B"",1""C"",1""D"",1""E"",1""F"")" set @("ishexnum=("_pattern_")") ; PEEK takes a hexadecimal number as the regindex if ishexnum&(""'=field) set mnemonicandreg="PEEK:"_regindex else if "sgmnt_addrs"=first do:""=regindex error("PARMREQ",first,aregname) set mnemonicandreg="CSAREG:"_regindex else if "sgmnt_data"=first do:""=regindex error("PARMREQ",first,aregname) set mnemonicandreg="FHREG:"_regindex else if "gd_region"=first do:""=regindex error("PARMREQ",first,aregname) set mnemonicandreg="GDRREG:"_regindex else if "gtmsrc_lcl"=first do:""=regindex error("PARMREQ",first,anindexval) set mnemonicandreg="GLFREPL:"_regindex else if "gtmrecv_local_struct"=first do:""'=regindex error("NOPARM",first) set mnemonicandreg="GRLREPL" else if "gtmsource_local_struct"=first do:""=regindex error("PARMREQ",first,anindexval) do . set mnemonicandreg="GSLREPL:"_regindex else if "jnl_buffer"=first do:""=regindex error("PARMREQ",first,aregname) set mnemonicandreg="JBFREG:"_regindex else if "jnl_private_control"=first do:""=regindex error("PARMREQ",first,aregname) set mnemonicandreg="JNLREG:"_regindex else if "jnlpool_ctl_struct"=first do:""'=regindex error("NOPARM",first) set mnemonicandreg="JPCREPL" else if ("node_local"=first)&(""'=regindex) set mnemonicandreg="NLREG:"_regindex else if ("node_local"=first)&(""=regindex) set mnemonicandreg="NLREPL" else if "repl_inst_hdr"=first do:""'=regindex error("NOPARM",first) set mnemonicandreg="RIHREPL" else if "recvpool_ctl_struct"=first do:""'=regindex error("NOPARM",first) set mnemonicandreg="RPCREPL" else if "upd_helper_ctl_struct"=first do:""'=regindex error("NOPARM",first) set mnemonicandreg="UHCREPL" else if "upd_proc_local_struct"=first do:""'=regindex error("NOPARM",first) set mnemonicandreg="UPLREPL" else do error("UNSUPSTRUCT",first) do . new $zgbldir . set $zgbldir=$select($length($get(gldpath)):gldpath,1:$ztrnlnm("gtm_dist"))_"/gtmhelp.gld" . set typeindex=$get(^gtmtypfldindx(first,rest)) . do:0=+typeindex error("INVALID",first,rest) . set type=^gtmtypes(first,typeindex,"type") . ; determine if struct or union . do:0'=$data(^gtmtypes(type)) error("UNSUPTYPE",type) . set offset=^gtmtypes(first,typeindex,"off") . set length=^gtmtypes(first,typeindex,"len") . set dim=$get(^gtmtypes(first,typeindex,"dim"),1) if $get(format)="" do . ; note that even with the check above for structs, anonymous . ; structs and unions "types" can still appear here . set format=$get(format) . set:(""=format)&(1=length) format=$select((type="unsigned-char"):"U",(type="char"):"I",1:"") . set:""=format format=$select(type["char":"C",1:"") . set:""=format format=$select(type["addr":"X",type["ptr":"X",type["void":"X",1:"") . set:""=format format=$select(type["uint64":"X",type["int64":"X",type["long":"X",1:"") . set:""=format format=$select(type["uint":"U",type["unsigned":"U",1:"") . set:""=format format=$select(type["int":"I",1:"") . ; miscellaneous whitelist of non-standard types . set:""=format format=$select("boolean_t"=type:"I","time_t"=type:"I","gtm_timet"=type:"I","size_t"=type:"U",1:"") . set:""=format format=$select("_Bool"=type:"I",1:"") . do:""=format error("UNSUPTYPE",type) set $etrap=etrap ; determine if this is an array if (1=dim)!("C"=format) do . set ret=$zpeek(mnemonicandreg,offset,length,format) else do . set length=length/dim . set ret=$zpeek(mnemonicandreg,offset,length,format) . for idx=1:1:dim-1 do . . set off=offset+(length*idx) . . set ret=ret_","_$zpeek(mnemonicandreg,off,length,format) quit ret ; Raises an error with given arguments error(err,first,args) new str,x set str=$text(@("ERRPBN"_err)),x="x="_$piece(str,";",3),@x,str=$piece(str,";",2)_"," if ("PARMREQ"=err)!("INVALID"=err) set args=""""_first_""":"""_args_"""" else if ("NOPARM"=err)!("UNSUPSTRUCT"=err)!("UNSUPTYPE"=err) set args=""""_first_"""" else set args="""""" set $etrap="if $zstatus[""UNKNOWN"" set $zstatus=""%GTM-E-""_str_x zgoto land" ; in case of older versions xecute "zmessage "_$translate(str,",",":")_args ; XECUTE because indirection exploded in older versions zgoto land ; Prints all $ZPEEK() acceptable fields LISTALL(gldpath) do listiterate(,$get(gldpath)) quit ; Populates output with type and length information indexed by $ZPEEK() acceptable fields ; e.g. output("gd_region.jnl_file_name")="unsigned-char,256" LIST(output,gldpath) do listiterate(.output,$get(gldpath)) quit ; Iterate through names of the structures defined in ^gtmtypes and execute given command for each one listiterate(out,gldpath) new $zgbldir,fieldname,i,j,struct,write set $zgbldir=$select($length($get(gldpath)):gldpath,1:$ztrnlnm("gtm_dist"))_"/gtmhelp.gld" set write=100>$zdata(out) for i=1:1 set struct=$piece($text(struct+i),";",2) quit:""=struct do . for j=1:1 set fieldname=$get(^gtmtypes(struct,j,"name")) quit:""=fieldname do . . if 'write set out(fieldname)=^gtmtypes(struct,j,"type")_","_^gtmtypes(struct,j,"len") . . else write fieldname,! quit ; Identify the $ZPEEK() arguments for a structure and name; output pass-by-reference csv is type,length,offset[,dimension] ARGS(struct,name,output,gldpath) new $zgbldir,arg,dim,fieldname,idx,land,r,zwrite new $etrap ; Do not modify caller's error trap set $ecode="",$etrap="quit" ; Defer error handling to the caller without doing anything set land=$zlevel-1 set $zgbldir=$select($length($get(gldpath)):gldpath,1:$ztrnlnm("gtm_dist"))_"/gtmhelp.gld" if ""=$get(struct) do error("UNSUPSTRUCT","") if '$data(^gtmtypfldindx(struct)) do error("UNSUPSTRUCT",struct) if ""=$get(name) do error("INVALID","") set idx=$get(^gtmtypfldindx(struct,name)) if 'idx do error("INVALID",name) set zwrite=100>$zdata(output) set fieldname=$get(^gtmtypes(struct,idx,"name")),output(fieldname)="" zwrite:zwrite fieldname for arg="type","len","off","dim" set r=$get(^gtmtypes(struct,idx,arg)) do . if zwrite zwrite:""'=r ^gtmtypes(struct,idx,arg) . else set:""'=r output(fieldname)=output(fieldname)_","_r set:"dim"=arg $extract(output(fieldname),1)="" quit data(field,gldpath) new $etrap set $etrap="set $ecode="""" quit:$quit 0 quit" set gbldirpath=$select($length($get(gldpath)):gldpath,1:$ztrnlnm("gtm_dist"))_"/gtmhelp.gld" quit $data(^|gbldirpath|gtmtypfldindx($piece(field,"."),$piece(field,".",2,$length(field,".")))) quit ; the below error definitions derived from merrors.msg are relatively complete so older version where they weren't defined get info ERRPBNPARMREQ ;150383746;"A first parameter value "_first_" requires a second parameter specified containing "_args ERRPBNNOPARM ;150383754;"First parameter "_first_" does not support a second parameter" ERRPBNUNSUPSTRUCT ;150383762;"$ZPEEK() does not support structure "_first ERRPBNINVALID ;150383770;first_" does not have a field named "_args ERRPBNNOFIELD ;150383778;"%ZPEEKBYNAME() requires a field.item in its first parameter" ERRPBNUNSUPTYPE ;150383882;"$ZPEEK() does not support type "_first struct ; listed below, in alphabetical order, structures currently supported by ^%PEEKBYNAME ;gd_region ;gtmrecv_local_struct ;gtmsource_local_struct ;gtmsrc_lcl ;jnl_buffer ;jnl_private_control ;jnlpool_ctl_struct ;node_local ;recvpool_ctl_struct ;repl_inst_hdr ;sgmnt_addrs ;sgmnt_data ;upd_helper_ctl_struct ;upd_proc_local_struct fis-gtm-V7.0-005/sr_unix/pinentry-gtm.sh0000755000032200000250000000747214342376330017047 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright (c) 2010-2017 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. # # # ################################################################# ############################################################################################# # # pinentry-gtm.sh - This script is used as a "pinentry-program" in gpg-agent.conf. # If the gtm_passwd environment variable exists and a usable mumps exists, run # pinentry.m to get the passphrase from the environment variable. # ############################################################################################# dir=`dirname $0` ; if [ -z "$dir" ] ; then dir=$PWD ; fi punt=1 if [ -z "$gtm_dist" ] ; then # $gtm_dist is not set in the environment. See if we can use dirname to find one if [ "`echo $gtm_chset | tr utf UTF`" = "UTF-8" -a -x "$dir/../../utf8/mumps" ] ; then gtm_dist=$dir/../../utf8 export gtm_dist elif [ -x "$dir/../../mumps" ] ; then gtm_dist=$dir/../.. gtm_chset=M export gtm_dist gtm_chset fi fi if [ -n "$gtm_passwd" -a -x "$gtm_dist/mumps" ] ; then pinentry=pinentry # Password and MUMPS exists, perform some extended setup checks if [ -z "$gtmroutines" ] ; then utfodir="" if [ "`echo $gtm_chset | tr utf UTF`" = "UTF-8" -a -x "$dir/../../utf8/mumps" ] ; then utfodir="/utf8" fi # $gtmroutines is not set in the environment, attempt to pick it up from libgtmutil.so, # $gtm_dist, $gtm_dist/plugin/o if [ -e "$gtm_dist/libgtmutil.so" ] ; then gtmroutines="$gtm_dist/libgtmutil.so" export gtmroutines elif [ -e "$gtm_dist/PINENTRY.o" ] ; then pinentry=PINENTRY gtmroutines="$gtm_dist" export gtmroutines elif [ -e "$gtm_dist/plugin/o${utfodir}/pinentry.o" ] ; then gtmroutines="$gtm_dist $gtm_dist/plugin/o${utfodir}" export gtmroutines fi fi # Protect the pinentry program from other env vars gtm_env_translate= gtm_etrap= gtm_local_collate= gtm_sysid= gtm_trace_gbl_name= gtm_zdate_form= gtm_zstep= gtm_ztrap_form=code gtm_zyerror= gtmcompile= gtmdbglvl= LD_PRELOAD= #BYPASSOKLENGTH export gtm_env_translate gtm_etrap gtm_local_collate gtm_sysid gtm_trace_gbl_name gtm_zdate_form gtm_zstep gtm_ztrap_form gtm_zyerror gtmcompile gtmdbglvl LD_PRELOAD #BYPASSOKLENGTH # Validate gtmroutines. Redirect output or it will affect the password protocol printf 'zhalt (0=$zlength($text(pinentry^'$pinentry')))' | $gtm_dist/mumps -direct >> /dev/null 2>&1 needsprivroutines=$? if [ 0 -ne "${needsprivroutines}" ] ; then pinentry=pinentry # Need to create a temporary directory for object routines if [ -x "`which mktemp 2>/dev/null`" ] ; then tmpdir=`mktemp -d` else tmpdir=/tmp/`basename $0`_$$.tmp ; mkdir $tmpdir fi trapstr="rm -rf $tmpdir" trap "$trapstr" HUP INT QUIT TERM TRAP pinentry_in="$dir" if [ -e "$gtm_dist/plugin/r/pinentry.m" ] ; then pinentry_in="$pinentry_in $gtm_dist/plugin/r"; fi if [ -e "$gtm_dist/plugin/gtmcrypt/pinentry.m" ] ; then pinentry_in="$pinentry_in $gtm_dist/plugin/gtmcrypt"; fi gtmroutines="$tmpdir($pinentry_in)" export gtmroutines fi $gtm_dist/mumps -run $pinentry punt=$? if [ -d "$tmpdir" ] ; then rm -rf "$tmpdir" ; fi fi if [ 0 -ne $punt ] ;then # Punt to the regular pinentry program pinentry=`which pinentry 2>/dev/null` if [ -x "$pinentry" ] ; then $pinentry $* ; else exit 1 ; fi fi fis-gtm-V7.0-005/sr_unix/pinentry.m0000644000032200000250000000571214342376330016074 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2010-2017 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; pinentry; Custom pinentry that returns an unobfuscated password if $gtm_passwd is defined in the environment ; ; See the following link for non-authoritative information on the pinentry protocol: ; http://info2html.sourceforge.net/cgi-bin/info2html-demo/info2html/info2html?%28pinentry%29Protocol ; set $etrap="do error^"_$text(+0),$zinterrupt=$etrap ; gtm_passwd is validated as non-null by pinentry-gtm.sh set obfpwd=$zconvert($ztrnlnm("gtm_passwd"),"U"),obfpwdlen=$zlength(obfpwd) ; Avoid NOBADCHAR on $PRINCIPAL and other functions use $principal:(chset="M") view "NOBADCHAR" ; Unmask the password ahead of initiating the pinentry protocol. If the external call is not ; available, the error handler is invoked set binobfpwd="" for i=1:2:$zlength(obfpwd) do . set msb=$zfind("0123456789ABCDEF",$zextract(obfpwd,i))-2 . set lsb=$zfind("0123456789ABCDEF",$zextract(obfpwd,i+1))-2 . set binobfpwd=binobfpwd_$zchar(16*msb+lsb) ; If unmasking fails, exit do:$&gpgagent.unmaskpwd(binobfpwd,.clrpwds) error use $principal:(exception="goto error^"_$text(+0)) write "OK Your orders please",! for read in quit:'$zlength(in) do . if "GETPIN"'=$zconvert($zpiece(in," ",1),"U") write "OK",! quit . write "D " . write clrpwds,! . write "OK",! . set $etrap="zgoto 0" . zhalt 0 ; Since this routine only responds to GETPIN, issue an error if it did not receive that command, ; letting pinentry-gtm.sh execute the default pinentry set $etrap="zgoto 0" zhalt 1 ; The error handler's primary function is to exit with error status so that the calling pinentry-gtm.sh ; can execute the default pinentry program. If $gtm_pinentry_log is defined, the routine will dump all ; status. Note that the locals are all killed prior to dumping status error kill new $etrap set $etrap="set $etrap=""zgoto 0"" zhalt +$zstatus" new i,info set errmsg="%GTM-E-PINENTRYERR, Custom pinentry program failure. "_$zstatus_"; from "_$zdirectory if $zsyslog(errmsg) set pinlog=$ztrnlnm("gtm_pinentry_log") if $zlength(pinlog) do . open pinlog:(append:chset="M") . use pinlog . write !,$zdate($horolog,"YYYY/MM/DD 24:60:SS"),errmsg,! . zwrite $zversion,$ecode,$job,$zchset,$zdirectory,$zroutines,$zstatus . write "Stack trace:" . for i=$STACK:-1:0 write !,i,?8 for info="ecode","place","mcode" do . . write ":",info,"=",$zjustify($stack(i,info),18) . write !,"Loaded external calls:",! zshow "C" . write "Device parameters:",! zshow "D" . close pinlog set $etrap="zgoto 0" zhalt +$zstatus quit fis-gtm-V7.0-005/sr_unix/pipeint_stats.c0000644000032200000250000000242514342376330017076 0ustar librarygtc/**************************************************************** * * * Copyright 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 "gtm_stdio.h" #include "gtmimagename.h" #include "pipeint_stats.h" GBLREF int process_id; /* Print stats on how many times pipe processing was interrupted by mupip interrupt. Note that this routine stands alone so that inclusion of it in mupip, lke, etc by print_exit_stats() doesn't pull in all the pipe code plus its dependencies.. */ void pipeint_stats(void) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (IS_GTM_IMAGE) /* Only give these stats for GTM/MUMPS image */ FPRINTF(stderr, "Pipe/Fifo read for process %d was interrupted %d times\n", process_id, TREF(pipefifo_interrupt)); } fis-gtm-V7.0-005/sr_unix/pipeint_stats.h0000644000032200000250000000136314342376330017103 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef PIPEINTSTATS_DEFINED #define PIPEINTSTATS_DEFINED void pipeint_stats(void); #endif fis-gtm-V7.0-005/sr_unix/probecrit_rec.h0000644000032200000250000000147614342376330017044 0ustar librarygtc/**************************************************************** * * * Copyright 2014 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. * * * ****************************************************************/ #ifndef PROBECRIT_REC_H_INCLUDED #define PROBECRIT_REC_H_INCLUDED #define TAB_PROBECRIT_REC(A,B,C) A, enum probecrit_rec_type { # include "tab_probecrit_rec.h" n_probecrit_rec_types }; #undef TAB_PROBECRIT_REC typedef struct probecrit_rec_struct { # define TAB_PROBECRIT_REC(A,B,C) gtm_uint64_t A; # include "tab_probecrit_rec.h" } probecrit_rec_t; #undef TAB_PROBECRIT_REC #endif fis-gtm-V7.0-005/sr_unix/proc_wait_stat.c0000644000032200000250000000567414342376330017243 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2008-2021 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include #include "gtm_stat.h" #include "gtm_unistd.h" #include "gtm_fcntl.h" /* This function is called only from White Box testing, so getting it inline or macro-ised is not a performance concern * It is called with the wait state being sent into UPDATE_CRIT_COUNTER() and if it is the one under test, * will coordinate a pause in execution with the test framework using a lock file. In general it will leave the querying * of the statistics to the test script, but there are a few special cases where it all must be done here. * Note that we abuse gtm_white_box_test_case_count a bit because it is an environment variable we get for "free" and * is not otherwise used in this testing. * * csa_generic: Csa pointer * ws: The state being entered */ void wb_gtm8863_lock_pause(void *csa_generic, wait_state ws) { static int never_fired = 1; /* only fires once per run */ struct stat statbuf; char buf[80]; int fd; sgmnt_addrs *csa = (sgmnt_addrs *) csa_generic; /* This special case for WS_47 is unfortunate, as we hoped to leverage the automatic handling * of existing WB env vars in all cases, but that codepath fires so often that it fires in our driver M script * before we are ready to come here */ if ( (WS_47 == ws) && (ws == gtm_white_box_test_case_count) && (! GETENV("gtm8863_wbox_ws47"))) return; /* WS_41 (in PRC) is also unfortunate as it happens during rundown and shared stats are locked out (it may not even be directly observable), so we manually dump the PRC counter here as better than naught */ if ( (WS_41 == ws) && (ws == gtm_white_box_test_case_count) ) { fd = creat("prc.txt", 0666); snprintf(buf, SIZEOF(buf), "%lu", csa->gvstats_rec_p->n_proc_wait); write(fd, STR_AND_LEN(buf)); close(fd); } /* As is WS_82 (in ZAD)*/ if ( (WS_82 == ws) && (ws == gtm_white_box_test_case_count) ) { fd = creat("zad.txt", 0666); snprintf(buf, SIZEOF(buf), "%lu", csa->gvstats_rec_p->n_util_wait); write(fd, STR_AND_LEN(buf)); close(fd); } if( (ws == gtm_white_box_test_case_count) && never_fired) { if ((WS_11 == gtm_white_box_test_case_count) && IS_MUPIP_IMAGE) /* Don't step on mupip during this test */ return; close(creat("gtm8863.lck", 0666)); while(0 == stat("gtm8863.lck", &statbuf)) { LONG_SLEEP(1); } never_fired = 0; } } fis-gtm-V7.0-005/sr_unix/proc_wait_stat.h0000644000032200000250000004345214342376330017244 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2019-2021 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. * * * ****************************************************************/ #ifndef PROC_WAIT_STAT #define PROC_WAIT_STAT /* This code tracks the entrance/exit of the various crit states and keeps the statistic counters * (including those built up from components) updated. During white box testing, it calls a function * to coordinate with test scripts. States which are visible to the user have names and comments. * This file is based on information from tab_gvstats_rec.h. */ typedef enum { WS_1, /* (In ZAD) [desired_db_format_set.c] */ WS_2, /* JOPA: (In JNL) [gdsfhead.h] */ WS_3, /* (In JNL) [jnl.h] */ WS_4, /* (In JNL) [jnl_write_attempt.c] */ WS_5, /* (In MLK) [mlk_ops.h] */ WS_6, /* (In ZAD) [mu_int_reg.c] */ WS_7, /* (In ZAD) [mu_int_wait_rdonly.c] */ WS_8, /* (In ZAD) [mu_reorg_upgrd_dwngrd.c] */ WS_9, /* (In ZAD) [mu_reorg_upgrd_dwngrd.c] */ WS_10, /* (In ZAD) [mupip_backup.c] */ WS_11, /* (In DEXA) [wcs_recover.c] */ WS_12, /* AFRA: (In DEXA) [gdsfilext.c] */ WS_13, /* (In GLB) [gvcst_expand_free_subtree.c] */ WS_14, /* (In GLB) [t_qread.c] */ WS_15, /* BREA: (In GLB) [t_qread.c] */ WS_16, /* (In GLB) [t_qread.c] */ WS_17, /* (In GLB) [t_qread.c] */ WS_18, /* (In GLB) [t_qread.c] */ WS_19, /* (In GLB) [updproc.c] */ WS_20, /* (In GLB) [verify_queue.c] */ WS_21, /* (In GLB) [wcs_get_space.h] */ WS_22, /* (In GLB) [wcs_verify.c] */ WS_23, /* (In GLB) [aio_shim.c] */ WS_24, /* (In GLB) [wcs_flu.c] */ WS_25, /* (In GLB) [wcs_wtfini.c] */ WS_26, /* (In GLB) [wcs_wtstart.c] */ WS_27, /* (In GLB) [wcs_wtstart_fini.c] */ WS_28, /* (In JNL) [jnl_phase2_cleanup.c] */ WS_29, /* (In JNL) [jnl_write.c] */ WS_30, /* (In JNL) [op_fnview.c] */ WS_31, /* (In JNL) [op_fnview.c] */ WS_32, /* (In JNL) [op_view.c] */ WS_33, /* (In JNL) [repl_phase2_cleanup.c] */ WS_34, /* (In JNL) [gtmsource_readfiles.c] */ WS_35, /* (In JNL) [mutex.c] */ WS_36, /* (In JNL) [mutex.c] */ WS_37, /* (In JNL) [repl_instance.c] */ WS_38, /* (In MLK) [mlk_ops.h] */ WS_39, /* MLBA: (In MLK) [op_lock2.c] */ WS_40, /* (In PRC) [gds_rundown.c] */ WS_41, /* (In PRC) [gds_rundown.c] */ WS_42, /* [gds_rundown.c] */ WS_43, /* (In TRX) [t_begin_crit.c] */ WS_44, /* (In TRX) [t_commit_cleanup.c] */ WS_45, /* (In TRX) [t_end.c] */ WS_46, /* (In TRX) [t_end.c] */ WS_47, /* TRGA: (In TRX) [t_end.c] */ WS_48, /* (In TRX) [t_retry.c] */ WS_49, /* (In TRX) [t_retry.c] */ WS_50, /* (In TRX) [t_retry.c] */ WS_51, /* (In TRX) [tp_hist.c] */ WS_52, /* (In TRX) [tp_restart.c] */ WS_53, /* (In TRX) [tp_tend.c] */ WS_54, /* (In TRX) [tp_tend.c] */ WS_55, /* (In ZAD) [dse.h] */ WS_56, /* (In ZAD) [dse_all.c] */ WS_57, /* (In ZAD) [dse_all.c] */ WS_58, /* (In ZAD) [dse_crit.c] */ WS_59, /* (In ZAD) [dse_maps.c] */ WS_60, /* (In ZAD) [dse_maps.c] */ WS_61, /* (In ZAD) [dse_maps.c] */ WS_62, /* (In ZAD) [dse_wcreinit.c] */ WS_63, /* (In ZAD) [gdsfhead.h] */ WS_64, /* (In ZAD) [mu_reorg_upgrd_dwngrd.c] */ WS_65, /* (In ZAD) [mupip_backup.c] */ WS_66, /* (In ZAD) [mupip_backup.c] */ WS_67, /* (In ZAD) [mupip_extend.c] */ WS_68, /* (In ZAD) [mupip_extend.c] */ WS_69, /* (In ZAD) [mupip_reorg.c] */ WS_70, /* (In ZAD) [mupip_reorg.c] */ WS_71, /* (In ZAD) [mupip_set_journal.c] */ WS_72, /* (In ZAD) [mur_close_files.c] */ WS_73, /* (In ZAD) [mur_open_files.c] */ WS_74, /* (In ZAD) [mur_output_record.c] */ WS_75, /* (In ZAD) [mur_output_record.c] */ WS_76, /* (In ZAD) [op_fnview.c] */ WS_77, /* (In ZAD) [op_fnview.c] */ WS_78, /* (In ZAD) [op_view.c] */ WS_79, /* (In ZAD) [region_freeze.c] */ WS_80, /* (In ZAD) [region_freeze.c] */ WS_81, /* (In ZAD) [region_freeze.c] */ WS_82, /* (In ZAD) [region_freeze.c] */ WS_83, /* (In ZAD) [region_freeze.c] */ WS_84, /* (In ZAD) [gtmsource_rootprimary_init.c] */ WS_85, /* (In ZAD) [mu_extract.c] */ WS_86, /* (In ZAD) [mu_extract.c] */ WS_87, /* (In ZAD) [mu_rndwn_file.c] */ WS_88, /* (In ZAD) [mu_truncate.c] */ WS_89, /* (In ZAD) [mubfilcpy.c] */ WS_90, /* (In ZAD) [mubinccpy.c] */ WS_91, /* (In ZAD) [mupip_reorg_encrypt.c] */ WS_92, /* (In ZAD) [mupip_reorg_encrypt.c] */ WS_93, /* (In ZAD) [mupip_reorg_encrypt.c] */ WS_94, /* (In ZAD) [mupip_reorg_encrypt.c] */ WS_95, /* (In ZAD) [mupip_set_file.c] */ WS_96, /* (In ZAD) [ss_initiate.c] */ WS_97, /* (In ZAD) [ss_initiate.c] */ WS_98, /* (In ZAD) [ss_initiate.c] */ WS_99, /* (In ZAD) [ss_release.c] */ WS_100, /* (In ZAD) [trigger_upgrade.c] */ WS_101, /* (In ZAD) [trigger_upgrade.c] */ WS_102, /* (In ZAD) [mupip_freeze.c] */ NOT_APPLICABLE } wait_state; void wb_gtm8863_lock_pause(void *,wait_state); #define UPDATE_CRIT_COUNTER(CSADDRS, WAITSTATE) \ MBSTART { \ if (CSADDRS && CSADDRS->nl) \ { \ switch(WAITSTATE) \ { \ case WS_2 : \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_ws2, 1); \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_jnl_wait, 1); \ break; \ case WS_12 : \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_ws12, 1); \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_dbext_wait, 1); \ break; \ case WS_15 : \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_ws15, 1); \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_bg_wait, 1); \ break; \ case WS_39 : \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_ws39, 1); \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_mlk_wait, 1); \ break; \ case WS_47 : \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_ws47, 1); \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_trans_wait, 1); \ break; \ case WS_11 : /* For aggregate DEXA */ \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_dbext_wait, 1); \ break; \ case WS_13 : /* For aggregate GLB */ \ case WS_14 : /* For aggregate GLB */ \ case WS_16 : /* For aggregate GLB */ \ case WS_17 : /* For aggregate GLB */ \ case WS_18 : /* For aggregate GLB */ \ case WS_19 : /* For aggregate GLB */ \ case WS_20 : /* For aggregate GLB */ \ case WS_21 : /* For aggregate GLB */ \ case WS_22 : /* For aggregate GLB */ \ case WS_23 : /* For aggregate GLB */ \ case WS_24 : /* For aggregate GLB */ \ case WS_25 : /* For aggregate GLB */ \ case WS_26 : /* For aggregate GLB */ \ case WS_27 : /* For aggregate GLB */ \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_bg_wait, 1); \ break; \ case WS_3 : /* For aggregate JNL */ \ case WS_4 : /* For aggregate JNL */ \ case WS_28 : /* For aggregate JNL */ \ case WS_29 : /* For aggregate JNL */ \ case WS_30 : /* For aggregate JNL */ \ case WS_31 : /* For aggregate JNL */ \ case WS_32 : /* For aggregate JNL */ \ case WS_33 : /* For aggregate JNL */ \ case WS_34 : /* For aggregate JNL */ \ case WS_35 : /* For aggregate JNL */ \ case WS_36 : /* For aggregate JNL */ \ case WS_37 : /* For aggregate JNL */ \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_jnl_wait, 1); \ break; \ case WS_5 : /* For aggregate MLK */ \ case WS_38 : /* For aggregate MLK */ \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_mlk_wait, 1); \ break; \ case WS_40 : /* For aggregate PRC */ \ case WS_41 : /* For aggregate PRC */ \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_proc_wait, 1); \ break; \ case WS_43 : /* For aggregate TRX */ \ case WS_44 : /* For aggregate TRX */ \ case WS_45 : /* For aggregate TRX */ \ case WS_46 : /* For aggregate TRX */ \ case WS_48 : /* For aggregate TRX */ \ case WS_49 : /* For aggregate TRX */ \ case WS_50 : /* For aggregate TRX */ \ case WS_51 : /* For aggregate TRX */ \ case WS_52 : /* For aggregate TRX */ \ case WS_53 : /* For aggregate TRX */ \ case WS_54 : /* For aggregate TRX */ \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_trans_wait, 1); \ break; \ case WS_1 : /* For aggregate ZAD */ \ case WS_6 : /* For aggregate ZAD */ \ case WS_7 : /* For aggregate ZAD */ \ case WS_8 : /* For aggregate ZAD */ \ case WS_9 : /* For aggregate ZAD */ \ case WS_10 : /* For aggregate ZAD */ \ case WS_55 : /* For aggregate ZAD */ \ case WS_56 : /* For aggregate ZAD */ \ case WS_57 : /* For aggregate ZAD */ \ case WS_58 : /* For aggregate ZAD */ \ case WS_59 : /* For aggregate ZAD */ \ case WS_60 : /* For aggregate ZAD */ \ case WS_61 : /* For aggregate ZAD */ \ case WS_62 : /* For aggregate ZAD */ \ case WS_63 : /* For aggregate ZAD */ \ case WS_64 : /* For aggregate ZAD */ \ case WS_65 : /* For aggregate ZAD */ \ case WS_66 : /* For aggregate ZAD */ \ case WS_67 : /* For aggregate ZAD */ \ case WS_68 : /* For aggregate ZAD */ \ case WS_69 : /* For aggregate ZAD */ \ case WS_70 : /* For aggregate ZAD */ \ case WS_71 : /* For aggregate ZAD */ \ case WS_72 : /* For aggregate ZAD */ \ case WS_73 : /* For aggregate ZAD */ \ case WS_74 : /* For aggregate ZAD */ \ case WS_75 : /* For aggregate ZAD */ \ case WS_76 : /* For aggregate ZAD */ \ case WS_77 : /* For aggregate ZAD */ \ case WS_78 : /* For aggregate ZAD */ \ case WS_79 : /* For aggregate ZAD */ \ case WS_80 : /* For aggregate ZAD */ \ case WS_81 : /* For aggregate ZAD */ \ case WS_82 : /* For aggregate ZAD */ \ case WS_83 : /* For aggregate ZAD */ \ case WS_84 : /* For aggregate ZAD */ \ case WS_85 : /* For aggregate ZAD */ \ case WS_86 : /* For aggregate ZAD */ \ case WS_87 : /* For aggregate ZAD */ \ case WS_88 : /* For aggregate ZAD */ \ case WS_89 : /* For aggregate ZAD */ \ case WS_90 : /* For aggregate ZAD */ \ case WS_91 : /* For aggregate ZAD */ \ case WS_92 : /* For aggregate ZAD */ \ case WS_93 : /* For aggregate ZAD */ \ case WS_94 : /* For aggregate ZAD */ \ case WS_95 : /* For aggregate ZAD */ \ case WS_96 : /* For aggregate ZAD */ \ case WS_97 : /* For aggregate ZAD */ \ case WS_98 : /* For aggregate ZAD */ \ case WS_99 : /* For aggregate ZAD */ \ case WS_100 : /* For aggregate ZAD */ \ case WS_101 : /* For aggregate ZAD */ \ case WS_102 : /* For aggregate ZAD */ \ INCR_GVSTATS_COUNTER(CSADDRS, (CSADDRS)->nl, n_util_wait, 1); \ break; \ default : /* It is not an error for some instrumentation to be ignored */ \ break; \ } \ \ /* We use gtm_white_box_test_case_count here as a WS value. \ The flag file lets us coordinate without timing issues. */ \ WBTEST_ONLY(WBTEST_WSSTATS_PAUSE, \ { \ wb_gtm8863_lock_pause(CSADDRS,WAITSTATE); \ }); \ } \ } MBEND #endif fis-gtm-V7.0-005/sr_unix/process_reorg_encrypt_restart.c0000644000032200000250000000355314342376330022377 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2016 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 "gdsroot.h" #include "gdsbt.h" #include "gdskill.h" #include "gdsfhead.h" #include "have_crit.h" #include "process_reorg_encrypt_restart.h" GBLREF sgmnt_addrs *reorg_encrypt_restart_csa; #ifdef DEBUG GBLREF uint4 dollar_tlevel; GBLREF uint4 update_trans; #endif void process_reorg_encrypt_restart(void) { intrpt_state_t prev_intrpt_state; enc_info_t *encr_ptr; int gtmcrypt_errno; gd_segment *seg; sgmnt_addrs *csa; csa = reorg_encrypt_restart_csa; assert(NULL != csa); /* caller should have ensured this */ /* Opening handles for encryption is a heavyweight operation. Caller should have ensured we are not in crit for * any region when the new key handles are opened for any one region. Assert that. */ assert(0 == have_crit(CRIT_HAVE_ANY_REG)); DEFER_INTERRUPTS(INTRPT_IN_CRYPT_RECONFIG, prev_intrpt_state); encr_ptr = csa->encr_ptr; assert(NULL != encr_ptr); DBG_RECORD_CRYPT_RECEIVE(csa->hdr, csa, csa->nl, process_id, encr_ptr); seg = csa->region->dyn.addr; INIT_DB_OR_JNL_ENCRYPTION(csa, encr_ptr, seg->fname_len, seg->fname, gtmcrypt_errno); if (0 != gtmcrypt_errno) { ENABLE_INTERRUPTS(INTRPT_IN_CRYPT_RECONFIG, prev_intrpt_state); GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, rts_error, seg->fname_len, seg->fname); } reorg_encrypt_restart_csa = NULL; ENABLE_INTERRUPTS(INTRPT_IN_CRYPT_RECONFIG, prev_intrpt_state); } fis-gtm-V7.0-005/sr_unix/process_reorg_encrypt_restart.h0000644000032200000250000000121114342376330022371 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2016 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. * * * ****************************************************************/ #ifndef MU_REORG_ENCRYPT_RESTART_DEFINED #define MU_REORG_ENCRYPT_RESTART_DEFINED void process_reorg_encrypt_restart(void); #endif fis-gtm-V7.0-005/sr_unix/put_cdidx.c0000644000032200000250000000264614342376330016200 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2015 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_string.h" #include "opcode.h" #include "compiler.h" #include "mmemory.h" /* Creates an operator reference to a given routine or label ultimately used to refer to an index into the linkage * table holding the address of the needed component. * * Parameter: * - x - mstr address containing name of routine or label. * * Return: * - operator descriptor */ oprtype put_cdidx(mstr *x) { triple *ref; # ifdef AUTORELINK_SUPPORTED mstr *str; ref = newtriple(OC_CDIDX); ref->operand[0].oprclass = CDIDX_REF; ref->operand[0].oprval.cdidx = str = (mstr *)mcalloc(SIZEOF(mstr)); str->addr = mcalloc(x->len); str->len = x->len; memcpy(str->addr, x->addr, x->len); # else assertpro(FALSE); /* Routine should never be used in non-autorelink platform */ ref = NULL; /* Avoid unintialized warnings. Need return in this path to avoid compiler error */ # endif return put_tref(ref); } fis-gtm-V7.0-005/sr_unix/random.c0000755000032200000250000000462214342376330015474 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_string.h" #include "gtm_stdio.h" #include "gtm_time.h" #include "gtm_stdlib.h" #include "random.h" #define MAXNUM 2147483561L #define MAX_RND_IDX 99 #define MAX_SEED_LEN 85 /* "global" random table -- must be visible to get_rand_from_table and init_rand_table */ static int rannum_table[MAX_RND_IDX+1]; int get_rand_from_table (void) { int ini_index = rannum_table[MAX_RND_IDX - 1]%MAX_RND_IDX; int fin_index = rannum_table[MAX_RND_IDX]%MAX_RND_IDX; int temp_val1, temp_val2; if ((temp_val2 = rannum_table[ini_index] - rannum_table[fin_index]) < 0) temp_val2 += 1000000000; rannum_table[ini_index] = temp_val2; rannum_table[MAX_RND_IDX - 1]--; rannum_table[MAX_RND_IDX]--; if (rannum_table[MAX_RND_IDX - 1] == 0) rannum_table[MAX_RND_IDX - 1] = 55; if (rannum_table[MAX_RND_IDX] == 0) rannum_table[MAX_RND_IDX] = 55; temp_val1 = rannum_table[MAX_RND_IDX]%42 + 56; rannum_table[MAX_RND_IDX] = rannum_table[temp_val1]; rannum_table[temp_val1] = temp_val2; return(rannum_table[MAX_RND_IDX]); } int init_rand_table (void) { char buf[MAX_RND_IDX+2]; char c_seed[MAX_SEED_LEN + 1]; int i_seed, seed_len; int i, j, k; i_seed = (int) time(NULL); SNPRINTF(c_seed, MAX_SEED_LEN + 1, "%d", i_seed); seed_len = STRLEN(c_seed); if (seed_len > MAX_SEED_LEN) return(0); SNPRINTF(buf, MAX_RND_IDX + 2, "%s aEbFcGdHeI", c_seed); for (i = 1; i < MAX_RND_IDX; i++) rannum_table[i] = buf[i%seed_len] * 8171717 + i * 997; i = 97; j = 12; for (k = 1; k < MAX_RND_IDX; k++) { rannum_table[i] -= rannum_table[j]; if (rannum_table[i] < 0) rannum_table[i] = -rannum_table[i]; i--; j--; if (i == 0) i=97; if (j == 0) j=97; } rannum_table[MAX_RND_IDX - 2] = 55; rannum_table[MAX_RND_IDX - 1] = 24; rannum_table[MAX_RND_IDX] = 77; return 1; /* No error. This is added to make compiler happy */ } fis-gtm-V7.0-005/sr_unix/random.h0000755000032200000250000000120014342376330015466 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef RANDOM_H_INCLUDED #define RANDOM_H_INCLUDED int get_rand_from_table (void) ; int init_rand_table (void) ; #endif fis-gtm-V7.0-005/sr_unix/rc.h0000755000032200000250000001251614342376330014626 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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. * * * ****************************************************************/ /* * rc.h --- * * Include file for the GTCM server (RC code). * * $Header:$ * */ #ifndef RC_H #define RC_H #define RC_METHOD 3 #define RC_MIN_CPT_SIZ 512 #define RC_AQ_HDR 14 /* Operations */ #define RC_OPEN_DS 0x0001 #define RC_CLOSE_DS 0x0002 #define RC_EXCH_INFO 0x0003 #define RC_LOCK_NAMES 0x0004 #define RC_LOGIN 0x0005 #define RC_GET_PAGE 0x0011 #define RC_GET_RECORD 0x0012 #define RC_KILL 0x0021 #define RC_SET 0x0022 #define RC_SET_FRAG 0x0023 #define RC_NETTRAX 0x0040 #define RC_OP_MAX 0x0041 /* Modes */ #define RC_MODE_WRITE 0x001 #define RC_MODE_WAIT 0x002 #define RC_MODE_CLEARLOCK 0x020 #define RC_MODE_DECRLOCK 0x040 #define RC_MODE_NEXT 0x100 #define RC_MODE_PREV 0x200 #define RC_MODE_TREE 0x400 #define RC_MODE_GET 0x002 #define RC_MODE_SET 0x003 #define RC_MODE_KILL 0x403 /* Error codes */ #define RC_ER_NO_ERROR 0 #define RC_ER_MAX 1 #define RC_SUCCESS 0x00000 #define RC_NOFILE 0x000CB #define RC_BADFILESPEC 0x000CD #define RC_FILEACCESS 0x000DB #define RC_GLOBERRUNSPEC 0x0C000 #define RC_DSALREADYMNT 0x0C022 #define RC_NETERRNTIMPL 0x0C106 #define RC_BADXBUF 0x0C131 #define RC_NETERRDBEDGE 0x0C151 #define RC_MUMERRUNDEFVAR 0x0D031 #define RC_KEYTOOLONG 0x0D042 #define RC_SUBTOOLONG 0x0D044 #define RC_BADSUBSCRIPT 0x0D047 #define RC_NETERRRETRY 0x0C10F #define RC_NETREQIGN 0x0C11F #define RC_LOCKCONFLICT 0x0C0A1 #define RC_UNDEFNAMSPC 0x0D063 #define RC_GLOBERRPAGENOTFOUND 0x0C032 #define RC_GLOBERRINVPAGEADDR 0x0C033 #define RC_XBLKOVERFLOW 0x0C035 /* Data structures */ typedef union rc_byte { char octet[SIZEOF(unsigned char)]; unsigned char value; } rc_byte; typedef union rc_word { char octet[SIZEOF(unsigned short)]; unsigned short value; } rc_word; typedef union rc_lword { char octet[SIZEOF(uint4)]; uint4 value; } rc_lword; /* Operation pointers */ typedef int (*rc_op)(/* char *, short */); /* XBLK header */ typedef struct rc_xblk_hdr { rc_word end; rc_word free; rc_byte method; rc_byte alert; rc_word sync; rc_word cpt_tab; rc_word cpt_siz; rc_word aq_res; rc_word resp_max; rc_lword client; rc_byte alive; char filler0; rc_word err_aq; rc_word last_aq; rc_byte big_endian; char filler1[5]; char asm1[32]; } rc_xblk_hdr; typedef struct rc_xdsid { rc_word dsid; rc_word node; } rc_xdsid; typedef struct rc_xnsid { char value[4]; }rc_xnsid; typedef struct rc_rq_hdr { rc_word len; rc_word typ; rc_word fmd; rc_word pid1; rc_word pid2; rc_xdsid xdsid; } rc_rq_hdr; typedef struct rc_aq_hdr { rc_word len; rc_word typ; rc_word erc; rc_word pid1; rc_word pid2; rc_xdsid xdsid; } rc_aq_hdr; typedef union rc_q_hdr { rc_rq_hdr r; rc_aq_hdr a; } rc_q_hdr; typedef struct rc_sbkey { rc_byte len; char key[1]; } rc_sbkey; typedef struct rc_swstr { rc_word len; char str[1]; } rc_swstr; typedef struct rc_lknam { rc_xdsid xdsid; rc_word node_handle; rc_sbkey sb_key; } rc_lknam; typedef struct { rc_q_hdr hdr; rc_word nlocks; rc_lknam dlocks[1]; } rc_req_lock; typedef struct { rc_q_hdr hdr; char pageaddr[4]; rc_word offset; } rc_req_getp; typedef struct { rc_q_hdr hdr; rc_sbkey key; } rc_req_getr; typedef struct { rc_q_hdr hdr; char pageaddr[4]; rc_word frag_offset; rc_word size_return; rc_word size_remain; rc_word before; rc_word after; rc_word xcc; rc_byte rstatus; rc_byte zcode; char page[1]; } rc_rsp_page; typedef struct { rc_q_hdr hdr; rc_xnsid xnsid; rc_sbkey key; } rc_kill; typedef struct { rc_q_hdr hdr; rc_xnsid xnsid; rc_sbkey key; } rc_set; typedef struct { rc_q_hdr hdr; char license_num[12]; char license_blk[224]; } rc_req_logn; typedef struct { rc_q_hdr hdr; rc_word version; rc_word method; rc_word session; rc_word date; char time[4]; char license_blk[224]; } rc_rsp_logn; /* Incomplete structure reference ("rc_oflow" in rc/rc_oflow.h) */ typedef struct rc_oflow rof_struct; #ifdef __STDC__ #define P(X) X #else /* defined(__STDC__) */ #define P(X) () #endif /* !defined(__STDC__) */ /* Routines */ int rc_prc_opnd P((rc_q_hdr *)); int rc_prc_lock P((rc_q_hdr *)); int rc_prc_clsd P((rc_q_hdr *)); int rc_prc_logn P((rc_q_hdr *)); int rc_prc_getp P((rc_q_hdr *)); int rc_prc_getr P((rc_q_hdr *)); int rc_prc_kill P((rc_q_hdr *)); int rc_prc_set P((rc_q_hdr *)); int rc_prc_setf P((rc_q_hdr *)); rof_struct *rc_oflow_alc P((void)); void rc_oflow_fin P((rof_struct *)); void rc_send_cpt P((rc_xblk_hdr *, rc_rsp_page *)); short rc_fnd_file P((rc_xdsid *)); int rc_frmt_lck P((char *, int4 , unsigned char *, short , short *)); void rc_gbl_ord P((rc_rsp_page *)); void rc_rundown P((void)); #undef P #endif /* !defined(RC_H) */ fis-gtm-V7.0-005/sr_unix/rc_cpt.h0000755000032200000250000000226314342376330015472 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #define RC_CPT_SPGINV 0x00 #define RC_CPT_SRVLST 0x01 #define RC_CPT_OFLOW 0x02 #define RC_CPT_INVAL 0x03 #define RC_CPT_AGNTQ 0x04 #define RC_CPT_NAMINV 0x0D #define RC_CPT_LKINV 0x0E #define RC_CPT_ALLINV 0x0F #define RC_MAX_CPT_SYNC 65535 #define RC_CPT_OVERFLOW 0xFFFF #define RC_CPT_ENTRY_SIZE SIZEOF(int4) #define RC_CPT_TABSIZE 512 typedef struct { int4 ring_buff[RC_CPT_TABSIZE]; unsigned short cpsync; /* entry count since server start */ unsigned short cpvfy; /* number of entries since last delivered to a client */ short index; /* next entry to fill */ short server_count; /* number of servers using CPT */ }rc_cp_table; #define RC_CPT_PATH "$gtm_dist/gtcm_server" fis-gtm-V7.0-005/sr_unix/rc_cpt_ops.c0000644000032200000250000003643514342376330016353 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "gtm_stdio.h" #include "gtm_ipc.h" #include #include #include #include "iosp.h" #include "rc_cpt.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "io.h" #include "copy.h" #include "rc.h" #include "gtm_c_stack_trace.h" #include "eintr_wrappers.h" #include "eintr_wrapper_semop.h" #include "rc_cpt_ops.h" #include "do_shmat.h" #include "trans_log_name.h" GBLDEF trans_num rc_read_stamp; GBLREF sgmnt_data_ptr_t cs_data; GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF bool rc_locked; error_def(ERR_DBFILERR); error_def(ERR_TEXT); static int rc_shmid = INVALID_SHMID, rc_sem = INVALID_SEMID; static rc_cp_table *rc_cpt = 0; static int rc_init_ipc(void); static void rc_cpt_unlock(void); static void rc_cpt_lock(void); #define HIGH_WORD_SHIFT 0x00010000 #define HIGH_4BIT_SHIFT 0x10000000 #define LOW_WORD_MASK 0x0000FFFF #define LOW_BYTE_MASK 0x000000FF #define HIGH_BYTE_MASK 0x0000FF00 /* This function was not maintained during the 64-bit block_id conversion */ int rc_cpt_entry(block_id blk) { key_t rc_key; int4 entry; int i; mstr fpath1, fpath2; struct sembuf sop[2]; bool found; # ifdef DEBUG int4 entry2, blk2 = (int4)blk; # endif /* This function was not maintained during the 64-bit block_id conversion so add this to * catch any uses of it in test */ assert(FALSE); # ifdef DEBUG_CPT FPRINTF(stderr,"\trc_cpt_entry(%lld)",blk); # endif /* test any existing RC semaphore first */ if (rc_sem) { errno = 0; i = semctl(rc_sem,0,GETVAL); if (errno) /* invalid semaphore */ { rc_sem = 0; /* detach shared memory segment as well */ if (rc_cpt) { (void) shmdt((char *)rc_cpt); rc_cpt = NULL; } } } if (!rc_cpt) { if (i = rc_init_ipc()) return i; /* return error code (errno) */ } /* cpvfy is the value of cpsync when the CPT table was last sent to a client. * If this block has already been entered since then, do not reenter. */ /* entry = (((cs_data->dsid / 256) * 9) % LOW_BYTE_MASK) + (blk % LOW_WORD_MASK) + (cs_data->rc_node * HIGH_WORD_SHIFT); */ entry = ((cs_data->dsid / 256 * 9 + blk) & LOW_BYTE_MASK) + (blk & HIGH_BYTE_MASK) + (cs_data->rc_node * HIGH_WORD_SHIFT); #ifdef DEBUG entry2 = ((cs_data->dsid / 256 * 9 + blk2) & LOW_BYTE_MASK) + (blk2 & HIGH_BYTE_MASK) + (cs_data->rc_node * HIGH_WORD_SHIFT); #endif assert(entry == entry2); /* This code was not maintained during the 64-bit block_id conversion. * This assert is for testing but if it fails it can be removed. */ found = FALSE; rc_cpt_lock(); i = rc_cpt->index - (rc_cpt->cpsync - rc_cpt->cpvfy); while (i < 0) i += RC_CPT_TABSIZE; for (; i < RC_CPT_TABSIZE; i++) { if (i == rc_cpt->index) break; if (rc_cpt->ring_buff[i] == entry) { found = TRUE; break; } } if (i == RC_CPT_TABSIZE) { for (i = 0; i < rc_cpt->index; i++) { if (rc_cpt->ring_buff[i] == entry) { found = TRUE; break; } } } # ifdef DEBUG_CPT if (found) FPRINTF(stderr," exists"); else FPRINTF(stderr," add"); # endif if (!found) { rc_cpt->ring_buff[rc_cpt->index++] = entry; rc_cpt->cpsync++; } if (rc_cpt->index == RC_CPT_TABSIZE) rc_cpt->index = 0; rc_cpt_unlock(); # ifdef DEBUG_CPT FPRINTF(stderr,"\n"); # endif return 0; } /* set up shared memory and semaphore segments, clearing away any existing RC IPC */ static int rc_init_ipc(void) { key_t rc_key; mstr fpath1, fpath2; int old_errno; char buff[1024]; /* RC semaphores are automatically cleared by shmclean, but it * is necessary to clear any previously existing shared memory. */ if (rc_cpt) { (void) shmdt((char *)rc_cpt); rc_cpt = NULL; } fpath1.addr = RC_CPT_PATH; fpath1.len = SIZEOF(RC_CPT_PATH); if (SS_NORMAL != TRANS_LOG_NAME(&fpath1, &fpath2, buff, SIZEOF(buff), do_sendmsg_on_log2long)) { PERROR("Error translating rc path"); return errno; } if ((rc_key = FTOK(fpath2.addr,GTM_ID)) == -1) { PERROR("Error with rc ftok"); return errno; } if ((rc_shmid = gtm_shmget(rc_key, SIZEOF(rc_cp_table), RWDALL, FALSE)) == -1) { rc_shmid = INVALID_SHMID; PERROR("Error with rc shmget"); return errno; } if ((INTPTR_T)(rc_cpt = (rc_cp_table*)do_shmat(rc_shmid, 0, 0)) == -1L) { PERROR("Error with rc shmat"); return errno; } if ((rc_sem = semget(rc_key, 4, RWDALL)) == -1) { old_errno = errno; PERROR("Error with rc semget"); (void) shmdt((char *)rc_cpt); rc_cpt = 0; rc_sem = INVALID_SEMID; return old_errno; } return 0; } static void rc_cpt_unlock(void) { struct sembuf sop[2]; int rv; int semop_rv; sop[0].sem_num = 0; sop[0].sem_op = -1; sop[0].sem_flg = SEM_UNDO; rc_locked = FALSE; SEMOP(rc_sem, sop, 1, semop_rv, NO_WAIT); if (-1 == semop_rv) { if (errno == EINVAL) { /* try reinitializing semaphore... */ if (!(rv=rc_init_ipc())) { SEMOP(rc_sem, sop, 1, semop_rv, NO_WAIT); if (-1 != semop_rv) { rc_locked = FALSE; return; } } } if (gv_cur_region) RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(9) ERR_DBFILERR,2, DB_LEN_STR(gv_cur_region), ERR_TEXT, 2, LEN_AND_LIT("Error with rc semaphore unlock"), errno); else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_TEXT, 2, LEN_AND_LIT("Error with rc semaphore unlock"), errno); } return; } static void rc_cpt_lock(void) { struct sembuf sop[2]; int rv; int semop_rv; /* WARNING: To prevent deadlocks, never attempt to acquire a database critical section while holding this semaphore */ sop[0].sem_num = sop[1].sem_num = 0; sop[0].sem_op = 0; sop[1].sem_op = 1; sop[0].sem_flg = sop[1].sem_flg = SEM_UNDO; SEMOP(rc_sem, sop, 2, semop_rv, FORCED_WAIT) if (-1 == semop_rv) { if (errno == EINVAL) { /* try reinitializing semaphore... */ if (!(rv=rc_init_ipc())) { SEMOP(rc_sem, sop, 2, semop_rv, FORCED_WAIT); if (-1 != semop_rv) { rc_locked = TRUE; return; } } } if (gv_cur_region) RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(9) ERR_DBFILERR,2, DB_LEN_STR(gv_cur_region), ERR_TEXT, 2, LEN_AND_LIT("Error with rc semaphore lock"), errno); else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_TEXT, 2, LEN_AND_LIT("Error with rc semaphore lock"), errno); } rc_locked = TRUE; return; } int rc_cpt_inval(void) { key_t rc_key; mstr fpath1, fpath2; int4 entry; struct sembuf sop[2]; int i, old_errno; char buff[1024]; bool found; # ifdef DEBUG_CPT FPRINTF(stderr,"\trc_cpt_inval()\n"); # endif if (!rc_cpt) { fpath1.addr = RC_CPT_PATH; fpath1.len = SIZEOF(RC_CPT_PATH); if (SS_NORMAL != TRANS_LOG_NAME(&fpath1, &fpath2, buff, SIZEOF(buff), do_sendmsg_on_log2long)) { PERROR("Error translating rc path"); return errno; } if ((rc_key = FTOK(fpath2.addr,GTM_ID)) == -1) { PERROR("Error with rc ftok"); return errno; } if ((rc_shmid = gtm_shmget(rc_key, SIZEOF(rc_cp_table), RWDALL, FALSE)) == -1) { rc_shmid = INVALID_SHMID; PERROR("Error with rc shmget"); return errno; } if ((INTPTR_T)(rc_cpt = (rc_cp_table*)do_shmat(rc_shmid, 0, 0)) == -1L) { PERROR("Error with rc shmat"); return errno; } if ((rc_sem = semget(rc_key, 4, RWDALL)) == -1) { old_errno = errno; PERROR("Error with rc semget"); (void) shmdt((char *)rc_cpt); rc_cpt = 0; rc_sem = INVALID_SEMID; return old_errno; } } rc_cpt_lock(); entry = cs_data->dsid + (cs_data->rc_node * HIGH_WORD_SHIFT) + (RC_CPT_INVAL * HIGH_4BIT_SHIFT); rc_cpt->ring_buff[rc_cpt->index++] = entry; rc_cpt->cpsync++; if (rc_cpt->index == RC_CPT_TABSIZE) rc_cpt->index = 0; rc_cpt_unlock(); return 0; } void rc_close_section(void) { # ifndef GTCM_RC if (rc_cpt) { (void) shmdt((char *)rc_cpt); rc_cpt = 0; } # endif return; } /* mupip_rundown_cpt() * * Called from mupip rundown to force the deletion of the RC cpt, if * present * * returns: * zero GT.CM installed and CPT deleted or not present or * GT.CM not installed * * non-zero CPT present, but could not be deleted. * * The CPT is not deleted if there are other processes attached to it. */ int mupip_rundown_cpt() { char buff[1024]; key_t rc_key; mstr fpath1, fpath2; struct shmid_ds shm_buf; /* detach from CPT if we happen to be connected */ if (rc_cpt) (void) shmdt((char *)rc_cpt); fpath1.addr = RC_CPT_PATH; fpath1.len = SIZEOF(RC_CPT_PATH); if (SS_NORMAL != TRANS_LOG_NAME(&fpath1, &fpath2, buff, SIZEOF(buff), do_sendmsg_on_log2long)) { /* invalid environment variable setup....error */ return -1; } if ((rc_key = FTOK(fpath2.addr,GTM_ID)) == -1) { /* no GT.CM server installed on system - okay to reset RC values */ return 0; } if ((rc_shmid = gtm_shmget(rc_key, SIZEOF(rc_cp_table), RWDALL, FALSE)) == -1) { /* no RC CPT - okay to reset RC values */ rc_shmid = INVALID_SHMID; return 0; } if (shmctl(rc_shmid, IPC_STAT, &shm_buf) == -1) { PERROR("Warning- can't access RC CPT"); return -1; } if (shm_buf.shm_nattch == 0) /* no GT.CM servers out there */ { /* delete CPT shared memory */ if (shmctl(rc_shmid, IPC_RMID, 0) == -1) { PERROR("Warning- can't delete RC CPT"); return -1; } /* attach to and delete CPT semaphore */ if ((rc_sem = semget(rc_key, 4, RWDALL)) == -1) { PERROR("Warning- can't access RC CPT semaphore"); rc_cpt = 0; rc_sem = INVALID_SEMID; return 0; } if (semctl(rc_sem, 0, IPC_RMID, 0) == -1) { PERROR("Error cleaning up RC CPT semaphore"); } return 0; /* we successfully deleted CPT shared memory */ } return -1; } void rc_delete_cpt(void) { struct sembuf sop[2]; struct shmid_ds shm_buf; int semop_rv; if (!rc_cpt) return; sop[0].sem_num = sop[1].sem_num = 0; sop[0].sem_op = 0; sop[1].sem_op = 1; sop[0].sem_flg = sop[1].sem_flg = SEM_UNDO; SEMOP(rc_sem, sop, 2, semop_rv, FORCED_WAIT); if (-1 == semop_rv) { PERROR("Error with RC semaphore lock"); (void) shmdt((char *)rc_cpt); rc_cpt = 0; rc_sem = 0; return; } if (rc_cpt->server_count == 1) /* only daemon, delete it */ { if (shmctl(rc_shmid,IPC_RMID,&shm_buf) == -1) { PERROR("Error cleaning up rc shared memory segment"); } if (semctl(rc_sem, 0, IPC_RMID, 0) == -1) { PERROR("Error cleaning up rc semaphores"); } } else { rc_cpt->server_count--; sop[0].sem_num = 0; sop[0].sem_op = -1; sop[0].sem_flg = SEM_UNDO; SEMOP(rc_sem, sop, 1, semop_rv, NO_WAIT); if (-1 == semop_rv) { PERROR("Error with RC semaphore unlock"); (void) shmdt((char *)rc_cpt); rc_cpt = 0; rc_sem = 0; if (semctl(rc_sem, 0, IPC_RMID, 0) == -1) { PERROR("Error cleaning up rc semaphores"); } } } return; } /****************************** Routines used only by the server **********************************/ int rc_create_cpt(void) { char buff[1024]; int old_errno; int semop_rv; key_t rc_key; mstr fpath1, fpath2; struct sembuf sop[2]; struct shmid_ds shm_buf; if (rc_cpt) return 0; fpath1.addr = RC_CPT_PATH; fpath1.len = SIZEOF(RC_CPT_PATH); if (SS_NORMAL != TRANS_LOG_NAME(&fpath1, &fpath2, buff, SIZEOF(buff), do_sendmsg_on_log2long)) { PERROR("Error translating rc path"); return errno; } if ((rc_key = FTOK(fpath2.addr,GTM_ID)) == -1) { PERROR("Error with rc ftok"); return errno; } if ((rc_shmid = gtm_shmget(rc_key, SIZEOF(rc_cp_table), IPC_CREAT | RWDALL, TRUE)) == -1) { rc_shmid = INVALID_SHMID; PERROR("Error with rc shmget"); return errno; } if ((INTPTR_T)(rc_cpt = (rc_cp_table*)do_shmat(rc_shmid, 0, 0)) == -1L) { PERROR("Error with rc shmat"); return errno; } if ((rc_sem = semget(rc_key, 4, IPC_CREAT | RWDALL)) == -1) { old_errno = errno; PERROR("Error with rc semget"); (void) shmdt((char *)rc_cpt); rc_cpt = 0; rc_sem = INVALID_SEMID; return old_errno; } sop[0].sem_num = sop[1].sem_num = 0; sop[0].sem_op = 0; sop[1].sem_op = 1; sop[0].sem_flg = sop[1].sem_flg = SEM_UNDO; SEMOP(rc_sem, sop, 2, semop_rv, FORCED_WAIT); if (-1 == semop_rv) { old_errno = errno; PERROR("Error with RC semaphore lock"); (void) shmdt((char *)rc_cpt); rc_cpt = 0; rc_sem = 0; return old_errno; } rc_cpt->server_count++; sop[0].sem_num = 0; sop[0].sem_op = -1; sop[0].sem_flg = SEM_UNDO; SEMOP(rc_sem, sop, 1, semop_rv, NO_WAIT); if (-1 == semop_rv) { old_errno = errno; PERROR("Error with RC semaphore unlock"); rc_cpt->server_count++; (void) shmdt((char *)rc_cpt); rc_cpt = 0; rc_sem = 0; if (semctl(rc_sem, 0, IPC_RMID, 0) == -1) { PERROR("Error cleaning up rc semaphores"); } return old_errno; } return 0; } short rc_get_cpsync(void) { return rc_cpt->cpsync; } /* Copy CPT into XBLK, check for overflow, verify that if there is a getrecord operation, the page is still valid */ void rc_send_cpt(rc_xblk_hdr *head, rc_rsp_page *last_aq) /* Zero if no read op in this XBLK */ { char *ptr; int cpt_size, copy_size; bt_rec_ptr_t b; /*This code is probably not being maintained as part of the v7 change so put this assert here to see * if we are even hitting this code during testing*/ assert(FALSE); if (!rc_cpt) { head->free.value = head->cpt_tab.value; head->cpt_siz.value = 0; return; } if (last_aq && (last_aq->hdr.r.typ.value == RC_GET_PAGE || last_aq->hdr.r.typ.value == RC_GET_RECORD)) { int4 blknum; assert(!cs_addrs->hold_onto_crit); /* this ensures we can safely do unconditional grab_crit and rel_crit */ grab_crit(gv_cur_region, NOT_APPLICABLE); /* DBP Need to verify block hasn't changed since copied. look in BT? */ rc_cpt_lock(); GET_LONG(blknum, last_aq->pageaddr); if ((b = bt_get(blknum)) ? rc_read_stamp <= b->tn : (rc_read_stamp <= OLDEST_HIST_TN(cs_addrs))) { last_aq->hdr.a.erc.value = RC_NETERRRETRY; } rel_crit(gv_cur_region); } else rc_cpt_lock(); cpt_size = rc_cpt->cpsync - head->sync.value; if (cpt_size < 0) /* handle wraps */ cpt_size += RC_MAX_CPT_SYNC; cpt_size *= (int)(RC_CPT_ENTRY_SIZE); if ((cpt_size > (int)head->cpt_siz.value) || (cpt_size > (RC_CPT_ENTRY_SIZE * RC_CPT_TABSIZE))) { head->cpt_siz.value = RC_CPT_OVERFLOW; rc_cpt->cpvfy = rc_cpt->cpsync; head->sync.value = rc_cpt->cpsync; head->free.value = head->cpt_tab.value; rc_cpt_unlock(); return; } else { head->cpt_siz.value = cpt_size; ptr = (char *)((char *)&rc_cpt->ring_buff[rc_cpt->index] - cpt_size); if (ptr < (char *)rc_cpt->ring_buff) { ptr = (char *)rc_cpt->ring_buff; copy_size = (int4)((char *)&rc_cpt->ring_buff[rc_cpt->index] - (char *)rc_cpt->ring_buff); } else copy_size = cpt_size; memcpy((char *)head + head->cpt_tab.value, ptr, copy_size); if (copy_size != cpt_size) { memcpy((char *)head + head->cpt_tab.value + copy_size, (char *)ARRAYTOP(rc_cpt->ring_buff) - (cpt_size - copy_size), cpt_size - copy_size); } } rc_cpt->cpvfy = rc_cpt->cpsync; head->sync.value = rc_cpt->cpsync; head->free.value = head->cpt_tab.value + head->cpt_siz.value; rc_cpt_unlock(); return; } fis-gtm-V7.0-005/sr_unix/recover_truncate.c0000644000032200000250000001106014342376330017555 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2021 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 #include #include "gtm_string.h" #include "gdsroot.h" #include "gdsblk.h" #include "gdsbml.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblkops.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "min_max.h" #include "gtmmsg.h" #include "error.h" #include "eintr_wrappers.h" #include "recover_truncate.h" #include "interlock.h" #include "shmpool.h" #include "gtmio.h" #include "clear_cache_array.h" #include "is_proc_alive.h" #include "do_semop.h" #include "gtm_semutils.h" #include "db_write_eof_block.h" error_def(ERR_DBFILERR); error_def(ERR_MUTRUNCERROR); GBLREF uint4 process_id; void recover_truncate(sgmnt_addrs *csa, sgmnt_data_ptr_t csd, gd_region* reg) { char *err_msg; block_id old_total, cur_total, new_total; off_t old_size, cur_size, new_size; int ftrunc_status, status; unix_db_info *udi; int blk_size, semval; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (NULL != csa->nl && csa->nl->trunc_pid && !is_proc_alive(csa->nl->trunc_pid, 0)) csa->nl->trunc_pid = 0; if (!csd->before_trunc_total_blks) return; assert(((GDSVCURR == csd->desired_db_format) || (GDSV7m == csd->desired_db_format) || (BLK_ID_32_VER > csd->desired_db_format)) && (csd->blks_to_upgrd == 0) && (dba_mm != csd->acc_meth)); /* If called from db_init, assure we've grabbed the access semaphor and are the only process attached to the database. * Otherwise, we should have crit when called from wcs_recover. */ udi = FILE_INFO(reg); assert((udi->grabbed_access_sem && (DB_COUNTER_SEM_INCR == (semval = semctl(udi->semid, DB_COUNTER_SEM, GETVAL)))) || csa->now_crit); /* Interrupted truncate scenario */ if (NULL != csa->nl) csa->nl->root_search_cycle++; old_total = csd->before_trunc_total_blks; /* Pre-truncate total_blks */ blk_size = csd->blk_size; old_size = (off_t)SIZEOF_FILE_HDR(csd) + (off_t)(old_total + 1) * blk_size; /* Pre-truncate file size (in bytes) */ cur_total = csa->ti->total_blks; /* Actual total_blks right now */ cur_size = (off_t)gds_file_size(reg->dyn.addr->file_cntl) * DISK_BLOCK_SIZE; /* Actual file size right now (in bytes) */ new_total = csd->after_trunc_total_blks; /* Post-truncate total_blks */ new_size = old_size - (off_t)(old_total - new_total) * blk_size; /* Post-truncate file size (in bytes) */ /* We don't expect FTRUNCATE to leave the file size in an 'in between' state, hence the assert below. */ assert(old_size == cur_size || new_size == cur_size); if (new_total == cur_total && old_size == cur_size) { /* Crash after reducing total_blks, before successful FTRUNCATE. Complete the FTRUNCATE here. */ DBGEHND((stdout, "DBG:: recover_truncate() -- completing truncate, old_total = [%lu], cur_total = [%lu]\n", old_total, new_total)); assert(csd->before_trunc_free_blocks >= DELTA_FREE_BLOCKS(old_total, new_total)); csa->ti->free_blocks = csd->before_trunc_free_blocks - DELTA_FREE_BLOCKS(old_total, new_total); clear_cache_array(csa, csd, reg, new_total, old_total); status = db_write_eof_block(udi, udi->fd, blk_size, new_size - blk_size, &(TREF(dio_buff))); if (status != 0) { err_msg = (char *)STRERROR(errno); rts_error_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_MUTRUNCERROR, 4, REG_LEN_STR(reg), LEN_AND_STR(err_msg)); return; } FTRUNCATE(FILE_INFO(reg)->fd, new_size, ftrunc_status); if (ftrunc_status != 0) { err_msg = (char *)STRERROR(errno); rts_error_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_MUTRUNCERROR, 4, REG_LEN_STR(reg), LEN_AND_STR(err_msg)); return; } } else { /* Crash before even changing csa->ti->total_blks OR after successful FTRUNCATE */ /* In either case, the db file is in a consistent state, so no need to do anything further */ assert((old_total == cur_total && old_size == cur_size) || (new_total == cur_total && new_size == cur_size)); if (!((old_total == cur_total && old_size == cur_size) || (new_total == cur_total && new_size == cur_size))) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBFILERR, 2, DB_LEN_STR(reg)); } csd->before_trunc_total_blks = 0; /* indicate CONSISTENT */ } fis-gtm-V7.0-005/sr_unix/recover_truncate.h0000644000032200000250000000113114342376330017560 0ustar librarygtc/**************************************************************** * * * Copyright 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #ifndef RECOVER_TRUNCATE_DEFINED #define RECOVER_TRUNCATE_DEFINED void recover_truncate(sgmnt_addrs *csa, sgmnt_data_ptr_t csd, gd_region *reg); #endif fis-gtm-V7.0-005/sr_unix/recvpool_init.c0000644000032200000250000006357714342376330017103 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_ipc.h" #include #include #include #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_inet.h" #include "gtm_string.h" #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "gtmrecv.h" #include "gtm_logicals.h" #include "jnl.h" #include "repl_sem.h" #include "repl_shutdcode.h" #include "io.h" #include "do_shmat.h" #include "trans_log_name.h" #include "mu_gv_cur_reg_init.h" #include "gtmmsg.h" #include "gtm_sem.h" #include "ipcrmid.h" #include "ftok_sems.h" #include "interlock.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_instance.h" #include "util.h" /* For OUT_BUFF_SIZE */ GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnlpool_addrs_ptr_t jnlpool_head; GBLREF recvpool_addrs recvpool; GBLREF int recvpool_shmid; GBLREF uint4 process_id; GBLREF gtmrecv_options_t gtmrecv_options; GBLREF gd_region *gv_cur_region; GBLREF repl_conn_info_t *this_side, *remote_side; GBLREF int4 strm_index; GBLREF char repl_instfilename[MAX_FN_LEN + 1]; /* save first instance */ GBLREF char repl_inst_name[MAX_INSTNAME_LEN]; /* for syslog */ LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; error_def(ERR_NORECVPOOL); error_def(ERR_RECVPOOLSETUP); error_def(ERR_TEXT); error_def(ERR_SYSCALL); #define MAX_RES_TRIES 620 /* Also defined in gvcst_init_sysops.c */ #define REMOVE_OR_RELEASE_SEM(NEW_IPC) \ { \ if (NEW_IPC) \ remove_sem_set(RECV); \ else \ rel_sem_immediate(RECV, RECV_POOL_ACCESS_SEM); \ } void recvpool_init(recvpool_user pool_user, boolean_t gtmrecv_startup) { boolean_t shm_created, new_ipc = FALSE, ftok_counter_halted; char instfilename[MAX_FN_LEN + 1]; char machine_name[MAX_MCNAMELEN]; char scndry_msg[OUT_BUFF_SIZE]; gd_region *r_save, *reg; int status, save_errno; union semun semarg; struct semid_ds semstat; struct shmid_ds shmstat; repl_inst_hdr repl_instance; sm_long_t status_l; unix_db_info *udi; unsigned int full_len; sgmnt_addrs *repl_csa; jnlpool_addrs_ptr_t tmp_jnlpool, save_jnlpool; pthread_mutexattr_t write_updated_ctl_attr; pthread_condattr_t write_updated_attr; DEBUG_ONLY(int4 semval;) memset(machine_name, 0, SIZEOF(machine_name)); if (GETHOSTNAME(machine_name, MAX_MCNAMELEN, status)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_TEXT, 2, RTS_ERROR_TEXT("Unable to get the hostname"), errno); /* rts_error should be issued by repl_inst_get_name */ assertpro(repl_inst_get_name(instfilename, &full_len, MAX_FN_LEN + 1, issue_rts_error, NULL)); /* look through jnlpool_head list for matching instfilename */ for (tmp_jnlpool = jnlpool_head; tmp_jnlpool && !tmp_jnlpool->recv_pool; tmp_jnlpool = tmp_jnlpool->next) { if (NULL != tmp_jnlpool->jnlpool_dummy_reg) { reg = tmp_jnlpool->jnlpool_dummy_reg; if ((reg->dyn.addr->fname_len == full_len) && !STRCMP(reg->dyn.addr->fname, instfilename)) { tmp_jnlpool->recv_pool = TRUE; break; } } } assert(!tmp_jnlpool || !tmp_jnlpool->recv_pool || (NULL != tmp_jnlpool->jnlpool_dummy_reg)); if ((NULL == tmp_jnlpool) || (NULL == tmp_jnlpool->jnlpool_dummy_reg)) { r_save = gv_cur_region; mu_gv_cur_reg_init(); reg = recvpool.recvpool_dummy_reg = gv_cur_region; gv_cur_region = r_save; assert(!gtmrecv_options.start); /* Should not be receiver server startup as that would have attached to jnl pool */ } else { /* Have already attached to the journal pool. Use the journal pool region for the receive pool as well as they * both correspond to one and the same file (replication instance file). We need to do this to ensure that an * "ftok_sem_get" done with either "jnlpool->jnlpool_dummy_reg" region or "recvpool.recvpool_dummy_reg" region * locks the same entity. * Should have already attached to journal pool only for receiver server startup or shutdown or $ZPEEK * or update process. Assert that. */ assert(gtmrecv_options.start || gtmrecv_options.shut_down || (GTMZPEEK == pool_user) || (UPDPROC == pool_user)); reg = recvpool.recvpool_dummy_reg = tmp_jnlpool->jnlpool_dummy_reg; tmp_jnlpool->recv_pool = TRUE; } udi = FILE_INFO(reg); assert((tmp_jnlpool && (recvpool.recvpool_dummy_reg != tmp_jnlpool->jnlpool_dummy_reg)) || (!tmp_jnlpool && (NULL != recvpool.recvpool_dummy_reg) || ((reg->dyn.addr->fname_len == full_len) && !STRCMP(reg->dyn.addr->fname, instfilename)))); if ((NULL == tmp_jnlpool) || (recvpool.recvpool_dummy_reg != tmp_jnlpool->jnlpool_dummy_reg)) { /* Fill in fields only if this is the first time this process is opening the replication instance file */ memcpy((char *)reg->dyn.addr->fname, instfilename, full_len); reg->dyn.addr->fname_len = full_len; udi->fn = (char *)reg->dyn.addr->fname; } save_jnlpool = jnlpool; jnlpool = tmp_jnlpool; /* repl_inst_read/write and ftok_sem_get/lock use jnlpool */ /* First grab ftok semaphore for replication instance file. Once we have it locked, no one else can start up * or, shut down replication for this instance. We will release ftok semaphore when initialization is done. */ if (!ftok_sem_get(recvpool.recvpool_dummy_reg, TRUE, REPLPOOL_ID, FALSE, &ftok_counter_halted)) /* uses jnlpool */ { jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_RECVPOOLSETUP); } save_errno = errno; repl_inst_read(instfilename, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr)); /* uses jnlpool */ /* At this point, we might not have yet attached to the jnlpool so we do not know if the ftok counter got halted * previously or not. So be safe and assume it has halted in case the jnlpool_shmid indicates it is up and running. * This means we might not delete the ftok semaphore in some cases of error codepaths but it should be rare * and is better than incorrectly deleting it while live processes are concurrently using it. */ assert(udi->counter_ftok_incremented == !ftok_counter_halted); udi->counter_ftok_incremented = udi->counter_ftok_incremented && (INVALID_SHMID == repl_instance.jnlpool_shmid); if (INVALID_SEMID == repl_instance.recvpool_semid) { assertpro(INVALID_SHMID == repl_instance.recvpool_shmid); if (GTMRECV != pool_user || !gtmrecv_startup) { ftok_sem_release(recvpool.recvpool_dummy_reg, udi->counter_ftok_incremented, TRUE); jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_NORECVPOOL, 2, full_len, udi->fn); } new_ipc = TRUE; assert((int)NUM_SRC_SEMS == (int)NUM_RECV_SEMS); if (INVALID_SEMID == (udi->semid = init_sem_set_recvr(IPC_PRIVATE, NUM_RECV_SEMS, RWDALL | IPC_CREAT))) { save_errno = errno; ftok_sem_release(recvpool.recvpool_dummy_reg, udi->counter_ftok_incremented, TRUE); jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error creating recv pool"), save_errno); } /* Following will set semaphore RECV_ID_SEM value as GTM_ID. In case we have orphaned semaphore for some reason, * mupip rundown will be able to identify GTM semaphores checking the value and can remove. */ semarg.val = GTM_ID; if (-1 == semctl(udi->semid, RECV_ID_SEM, SETVAL, semarg)) { save_errno = errno; remove_sem_set(RECV); /* Remove what we created */ ftok_sem_release(recvpool.recvpool_dummy_reg, udi->counter_ftok_incremented, TRUE); jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with recvpool semctl"), save_errno); } /* Warning: We must read the sem_ctime using IPC_STAT after SETVAL, which changes it. We must NOT do any more * SETVAL after this. Our design is to use sem_ctime as creation time of semaphore. */ semarg.buf = &semstat; if (-1 == semctl(udi->semid, RECV_ID_SEM, IPC_STAT, semarg)) { save_errno = errno; remove_sem_set(RECV); /* Remove what we created */ ftok_sem_release(recvpool.recvpool_dummy_reg, udi->counter_ftok_incremented, TRUE); jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with recvpool semctl"), save_errno); } udi->gt_sem_ctime = semarg.buf->sem_ctime; } else { /* find create time of semaphore from the file header and check if the id is reused by others */ semarg.buf = &semstat; if (-1 == semctl(repl_instance.recvpool_semid, DB_CONTROL_SEM, IPC_STAT, semarg)) { save_errno = errno; ftok_sem_release(recvpool.recvpool_dummy_reg, udi->counter_ftok_incremented, TRUE); jnlpool = save_jnlpool; SNPRINTF(scndry_msg, OUT_BUFF_SIZE, "Error with semctl on Receive Pool SEMID (%d)", repl_instance.recvpool_semid); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_REPLREQROLLBACK, 2, full_len, udi->fn, ERR_TEXT, 2, LEN_AND_STR(scndry_msg), save_errno); } else if (semarg.buf->sem_ctime != repl_instance.recvpool_semid_ctime) { ftok_sem_release(recvpool.recvpool_dummy_reg, udi->counter_ftok_incremented, TRUE); jnlpool = save_jnlpool; SNPRINTF(scndry_msg, OUT_BUFF_SIZE, "Creation time for Receive Pool SEMID (%d) is %d; Expected %d", repl_instance.recvpool_semid, semarg.buf->sem_ctime, repl_instance.recvpool_semid_ctime); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_REPLREQROLLBACK, 2, full_len, udi->fn, ERR_TEXT, 2, LEN_AND_STR(scndry_msg)); } udi->semid = repl_instance.recvpool_semid; udi->gt_sem_ctime = repl_instance.recvpool_semid_ctime; set_sem_set_recvr(udi->semid); /* repl_sem.c has some functions which needs some static variable to have the id */ } assert((INVALID_SEMID != udi->shmid) && (0 != udi->gt_sem_ctime)); assert(!udi->grabbed_access_sem); status = grab_sem(RECV, RECV_POOL_ACCESS_SEM); if (SS_NORMAL != status) { save_errno = errno; ftok_sem_release(recvpool.recvpool_dummy_reg, udi->counter_ftok_incremented, TRUE); jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with receive pool semaphores"), save_errno); } udi->grabbed_access_sem = TRUE; udi->counter_acc_incremented = TRUE; if (INVALID_SHMID == repl_instance.recvpool_shmid) { /* We have an INVALID shmid in the file header. There are three ways this can happen * * 1. A rollback started off with either no receive pool or rundown'd an existing receive pool and created new * semaphores, but got killed in the middle. At this point, if a new receiver server starts up, it will notice * a valid usable semid, but will find an invalid shmid. * * 2. A rollback started off with either no journal pool or rundown'd an existing journal pool and created new * semaphores. Before it goes to mur_close_files, lets say a receiver server started. It will acquire the ftok * semaphore, but will be waiting for the access control semaphore which rollback is holding. Rollback, on the * other hand, will see if the ftok semaphore is available BEFORE removing the semaphores from the system. But, * since receiver server is holding the ftok, Rollback, will not remove the access control semaphore. But, would * just let go of them and exit (repl_instance.file_corrupt can be either TRUE or FALSE depending on whether * Rollback completed successfully or not). * * 3. A fresh startup. * * For all the cases, we need to check if repl_instance.file_corrupt is TRUE. If so, we should issue an error. But, * the check is NOT needed here, because jnlpool_init already checks that condition and does an rts_error. So, we * should have never come here. Assert that. If file_corrupt is FALSE and this is the receiver server startup * command, create the receive pool anyways. */ assert(!repl_instance.file_corrupt); /* Ensure that NO one has yet incremented the RECV_SERV_COUNT_SEM (as implied by all the 3 cases above) */ assert(0 == (semval = semctl(udi->semid, RECV_SERV_COUNT_SEM, GETVAL))); /* semval = number of processes attached */ new_ipc = TRUE; /* need to create a new IPC */ } else if (-1 == shmctl(repl_instance.recvpool_shmid, IPC_STAT, &shmstat)) { /* shared memory ID was removed form the system by an IPCRM command or we have a permission issue (or such) */ save_errno = errno; REMOVE_OR_RELEASE_SEM(new_ipc); ftok_sem_release(recvpool.recvpool_dummy_reg, udi->counter_ftok_incremented, TRUE); jnlpool = save_jnlpool; SNPRINTF(scndry_msg, OUT_BUFF_SIZE, "Error with shmctl on Receive Pool SHMID (%d)", repl_instance.recvpool_shmid); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_REPLREQROLLBACK, 2, full_len, udi->fn, ERR_TEXT, 2, LEN_AND_STR(scndry_msg), save_errno); } else if (shmstat.shm_ctime != repl_instance.recvpool_shmid_ctime) { /* shared memory was possibly reused (causing shm_ctime and jnlpool_shmid_ctime to be different. We can't rely * on the shmid as it could be connected to a valid instance file in a different environment. Create new IPCs */ new_ipc = TRUE; /* need to create a new IPC */ } else { recvpool_shmid = udi->shmid = repl_instance.recvpool_shmid; udi->gt_shm_ctime = repl_instance.recvpool_shmid_ctime; } if (new_ipc && (GTMRECV != pool_user || !gtmrecv_startup)) { REMOVE_OR_RELEASE_SEM(new_ipc); ftok_sem_release(recvpool.recvpool_dummy_reg, udi->counter_ftok_incremented, TRUE); jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_NORECVPOOL, 2, full_len, udi->fn); } shm_created = FALSE; if (new_ipc) { /* create new shared memory */ if (-1 == (udi->shmid = recvpool_shmid = gtm_shmget(IPC_PRIVATE, gtmrecv_options.buffsize, IPC_CREAT | RWDALL, TRUE))) { udi->shmid = recvpool_shmid = INVALID_SHMID; save_errno = errno; remove_sem_set(RECV); /* Remove what we created */ ftok_sem_release(recvpool.recvpool_dummy_reg, udi->counter_ftok_incremented, TRUE); jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with receive pool creation"), save_errno); } if (-1 == shmctl(udi->shmid, IPC_STAT, &shmstat)) { save_errno = errno; if (0 != shm_rmid(udi->shmid)) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error removing recvpool "), errno); remove_sem_set(RECV); /* Remove what we created */ ftok_sem_release(recvpool.recvpool_dummy_reg, udi->counter_ftok_incremented, TRUE); jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with recvpool shmctl"), save_errno); } udi->gt_shm_ctime = shmstat.shm_ctime; shm_created = TRUE; } assert((INVALID_SHMID != udi->shmid) && (0 != udi->gt_shm_ctime) && (INVALID_SHMID != recvpool_shmid)); status_l = (sm_long_t)(recvpool.recvpool_ctl = (recvpool_ctl_ptr_t)do_shmat(recvpool_shmid, 0, 0)); if (-1 == status_l) { save_errno = errno; if (new_ipc) remove_sem_set(RECV); else rel_sem_immediate(RECV, RECV_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(recvpool.recvpool_dummy_reg, udi->counter_ftok_incremented, TRUE); jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with receive pool shmat"), save_errno); } if (shm_created) recvpool.recvpool_ctl->initialized = FALSE; recvpool.upd_proc_local = (upd_proc_local_ptr_t)((sm_uc_ptr_t)recvpool.recvpool_ctl + RECVPOOL_CTL_SIZE); recvpool.gtmrecv_local = (gtmrecv_local_ptr_t)((sm_uc_ptr_t)recvpool.upd_proc_local + UPD_PROC_LOCAL_SIZE); recvpool.upd_helper_ctl = (upd_helper_ctl_ptr_t)((sm_uc_ptr_t)recvpool.gtmrecv_local + GTMRECV_LOCAL_SIZE); recvpool.recvdata_base = (sm_uc_ptr_t)recvpool.recvpool_ctl + RECVDATA_BASE_OFF; if (GTMRECV == pool_user && gtmrecv_startup) recvpool.recvpool_ctl->fresh_start = FALSE; this_side = &recvpool.recvpool_ctl->this_side; remote_side = &recvpool.gtmrecv_local->remote_side; /* Set global variable now. Structure will be initialized * later when receiver server connects to a source server */ if (!recvpool.recvpool_ctl->initialized) { if (GTMRECV != pool_user || !gtmrecv_startup) { if (new_ipc) remove_sem_set(RECV); else rel_sem_immediate(RECV, RECV_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(recvpool.recvpool_dummy_reg, udi->counter_ftok_incremented, TRUE); jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Receive pool has not been initialized")); } /* Initialize the shared memory fields */ recvpool.recvpool_ctl->jnl_seqno = 0; recvpool.recvpool_ctl->recvdata_base_off = RECVDATA_BASE_OFF; recvpool.recvpool_ctl->recvpool_size = gtmrecv_options.buffsize - recvpool.recvpool_ctl->recvdata_base_off; recvpool.recvpool_ctl->write = 0; recvpool.recvpool_ctl->write_wrap = recvpool.recvpool_ctl->recvpool_size; recvpool.recvpool_ctl->wrapped = FALSE; memcpy( (char *)recvpool.recvpool_ctl->recvpool_id.instfilename, instfilename, full_len); memcpy(recvpool.recvpool_ctl->recvpool_id.label, GDS_RPL_LABEL, GDS_LABEL_SZ); memcpy(recvpool.recvpool_ctl->recvpool_id.now_running, gtm_release_name, gtm_release_name_len + 1); assert(0 == offsetof(recvpool_ctl_struct, recvpool_id)); /* ensure that the pool identifier is at the top of the pool */ recvpool.recvpool_ctl->recvpool_id.pool_type = RECVPOOL_SEGMENT; assert(NULL != jnlpool); GTMRECV_CLEAR_CACHED_HISTINFO(recvpool.recvpool_ctl, jnlpool, INSERT_STRM_HISTINFO_TRUE); /* Initialize mutex/cond for notifying update process of new writes */ status = pthread_mutexattr_init(&write_updated_ctl_attr); if (0 != status) { jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutexattr_init"), CALLFROM, status, 0); } status = pthread_mutexattr_settype(&write_updated_ctl_attr, PTHREAD_MUTEX_ERRORCHECK); if (0 != status) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutexattr_settype"), CALLFROM, status, 0); } status = pthread_mutexattr_setpshared(&write_updated_ctl_attr, PTHREAD_PROCESS_SHARED); if (0 != status) { jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutexattr_setpshared"), CALLFROM, status, 0); } # if PTHREAD_MUTEX_ROBUST_SUPPORTED status = pthread_mutexattr_setrobust(&write_updated_ctl_attr, PTHREAD_MUTEX_ROBUST); if (0 != status) { jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutexattr_setrobust"), CALLFROM, status, 0); } # endif status = pthread_mutex_init(&recvpool.recvpool_ctl->write_updated_ctl, &write_updated_ctl_attr); if (0 != status) { jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutex_init"), CALLFROM, status, 0); } status = pthread_condattr_init(&write_updated_attr); if (0 != status) { jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_condattr_init"), CALLFROM, status, 0); } status = pthread_condattr_setpshared(&write_updated_attr, PTHREAD_PROCESS_SHARED); if (0 != status) { jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_condattr_setpshared"), CALLFROM, status, 0); } status = pthread_cond_init(&recvpool.recvpool_ctl->write_updated, &write_updated_attr); if (0 != status) { jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_cond_init"), CALLFROM, status, 0); } this_side = &recvpool.recvpool_ctl->this_side; this_side->proto_ver = REPL_PROTO_VER_THIS; this_side->jnl_ver = JNL_VER_THIS; /* this_side->is_std_null_coll = will be initialized by update process since receiver does not have access to db */ this_side->trigger_supported = GTMTRIG_ONLY(TRUE) NON_GTMTRIG_ONLY(FALSE); /* The following 3 members make sense only if the other side of a replication connection is also known. Since * this_side talks about the properties of this instance, these 3 dont make sense in this context. When a connection * to the other side is made, each rcvr server's gtmrecv_local->remote_side will have these fields appropriately set */ this_side->cross_endian = FALSE; this_side->endianness_known = FALSE; this_side->null_subs_xform = FALSE; this_side->is_supplementary = repl_instance.is_supplementary; recvpool.upd_proc_local->upd_proc_shutdown = NO_SHUTDOWN; recvpool.upd_proc_local->upd_proc_shutdown_time = -1; recvpool.upd_proc_local->upd_proc_pid = 0; recvpool.upd_proc_local->upd_proc_pid_prev = 0; recvpool.upd_proc_local->read_jnl_seqno = 0; recvpool.upd_proc_local->read = 0; recvpool.gtmrecv_local->recv_serv_pid = process_id; assert((NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl)); if ((NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl)) jnlpool->jnlpool_ctl->gtmrecv_pid = process_id; recvpool.gtmrecv_local->lastrecvd_time = -1; recvpool.gtmrecv_local->restart = GTMRECV_NO_RESTART; recvpool.gtmrecv_local->statslog = FALSE; recvpool.gtmrecv_local->shutdown = NO_SHUTDOWN; recvpool.gtmrecv_local->shutdown_time = -1; STRCPY(recvpool.gtmrecv_local->filter_cmd, gtmrecv_options.filter_cmd); recvpool.gtmrecv_local->statslog_file[0] = '\0'; /* recvpool.gtmrecv_local->remote_side will be initialized at connection establishment time */ assert(NULL != jnlpool->repl_inst_filehdr); recvpool.gtmrecv_local->strm_index = strm_index; /* The following fields will be initialized in gtmrecv.c * recvpool.gtmrecv_local->updateresync * recvpool.gtmrecv_local->updresync_instfile_fd * recvpool.gtmrecv_local->updresync_lms_group * recvpool.gtmrecv_local->updresync_jnl_seqno * recvpool.gtmrecv_local->updresync_num_histinfo */ memset(recvpool.upd_helper_ctl, 0, SIZEOF(*recvpool.upd_helper_ctl)); SET_LATCH_GLOBAL(&recvpool.upd_helper_ctl->pre_read_lock, LOCK_AVAILABLE); recvpool.recvpool_ctl->initialized = TRUE; recvpool.recvpool_ctl->fresh_start = TRUE; } if (new_ipc) { /* Flush shmid/semid changes to the instance file header if this process created the receive pool. * Also update the instance file header section in the journal pool with the recvpool sem/shm ids. * Before updating jnlpool fields, ensure the journal pool lock is grabbed. */ assert((NULL != jnlpool) && (NULL != jnlpool->repl_inst_filehdr)); assert(NULL != jnlpool->jnlpool_dummy_reg); DEBUG_ONLY(repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs;) assert(!repl_csa->hold_onto_crit); /* so it is ok to invoke "grab_lock" and "rel_lock" unconditionally */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); jnlpool->repl_inst_filehdr->recvpool_shmid = udi->shmid; jnlpool->repl_inst_filehdr->recvpool_semid = udi->semid; jnlpool->repl_inst_filehdr->recvpool_shmid_ctime = udi->gt_shm_ctime; jnlpool->repl_inst_filehdr->recvpool_semid_ctime = udi->gt_sem_ctime; /* Flush the file header to disk so future callers of "jnlpool_init" see the jnlpool_semid and jnlpool_shmid */ repl_inst_flush_filehdr(); /* uses jnlpool */ rel_lock(jnlpool->jnlpool_dummy_reg); } /* Release control lockout and ftok semaphore now that the receive pool has been attached. * The only exception is receiver server shutdown command. In this case, all these locks will be released * once the receiver server shutdown is actually complete. Note that if -UPDATEONLY or -HELPERS is specified * in the shutdown, we do not want to hold any of these locks. */ if ((GTMRECV != pool_user) || !gtmrecv_options.shut_down || gtmrecv_options.updateonly || gtmrecv_options.helpers) { rel_sem(RECV, RECV_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; if (!ftok_sem_release(recvpool.recvpool_dummy_reg, FALSE, FALSE)) { jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_RECVPOOLSETUP); } } /* If receiver server startup, grab the options semaphore to lock out checkhealth, statslog or changelog. * Ideally one should grab this before releasing the ftok and access semaphore but the issue with this is that * if we do, we can cause a deadlock. Hence we do this just after releasing the ftok and access semaphore. This * introduces a small window of instructions where a checkhealth, changelog, statslog etc. can sneak in right * after we release the ftok and access lock but before we grab the options semaphore and attach to a receive pool * that is uninitialized. This will be addressed by C9F12-002766. */ if ((GTMRECV == pool_user) && gtmrecv_options.start && (0 != grab_sem(RECV, RECV_SERV_OPTIONS_SEM))) { save_errno = errno; if (new_ipc) remove_sem_set(RECV); else rel_sem_immediate(RECV, RECV_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(recvpool.recvpool_dummy_reg, udi->counter_ftok_incremented, TRUE); jnlpool = save_jnlpool; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with receive pool options semaphore"), save_errno); } jnlpool = save_jnlpool; if (('\0' == repl_inst_name[0]) && ('\0' != repl_instfilename[0])) { /* fill in instance name for syslog if right instance file name and first */ if (0 == STRCMP(repl_instfilename, instfilename)) memcpy(repl_inst_name, repl_instance.inst_info.this_instname, MAX_INSTNAME_LEN); } return; } fis-gtm-V7.0-005/sr_unix/reg_cmcheck.c0000755000032200000250000000370414342376330016446 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "parse_file.h" #include "is_raw_dev.h" #define MAX_NODE_NAME 32 error_def(ERR_DBFILERR); bool reg_cmcheck(gd_region *reg) { gd_segment *seg; char fbuff[MAX_FN_LEN + 1]; parse_blk pblk; mstr file; int status; seg = reg->dyn.addr; if (dba_cm == seg->acc_meth) return TRUE; /* if access method has already been set to dba_cm, return right away (avoid recomputation) */ file.addr = (char *)seg->fname; file.len = seg->fname_len; memset(&pblk, 0, SIZEOF(pblk)); pblk.buffer = fbuff; pblk.buff_size = MAX_FN_LEN; pblk.fop = (F_SYNTAXO | F_PARNODE); strncpy(fbuff,file.addr,file.len); *(fbuff + file.len) = '\0'; if (is_raw_dev(fbuff)) { pblk.def1_buf = DEF_NODBEXT; pblk.def1_size = SIZEOF(DEF_NODBEXT) - 1; } else { pblk.def1_buf = DEF_DBEXT; pblk.def1_size = SIZEOF(DEF_DBEXT) - 1; } status = parse_file(&file, &pblk); if (!(status & 1)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_DBFILERR,2, seg->fname_len, seg->fname, status); assert((int)pblk.b_esl + 1 <= SIZEOF(seg->fname)); memcpy(seg->fname, pblk.buffer, pblk.b_esl); pblk.buffer[pblk.b_esl] = 0; seg->fname[pblk.b_esl] = 0; seg->fname_len = pblk.b_esl; if (pblk.fnb & F_HAS_NODE) { assert(pblk.b_node && ':' == pblk.l_node[pblk.b_node - 1]); seg->acc_meth = dba_cm; return TRUE; } return FALSE; } fis-gtm-V7.0-005/sr_unix/rel_crit.c0000644000032200000250000000550214342376330016012 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_signal.h" /* for VSIG_ATOMIC_T type */ #include #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmsiginfo.h" #include "mutex.h" #include "deferred_signal_handler.h" #include "have_crit.h" #include "caller_id.h" #include "jnl.h" #include "gtmimagename.h" GBLREF short crash_count; GBLREF volatile int4 crit_count; GBLREF uint4 process_id; GBLREF volatile int suspend_status; GBLREF node_local_ptr_t locknl; GBLREF volatile int4 gtmMallocDepth; /* Recursion indicator */ GBLREF jnl_gbls_t jgbl; error_def(ERR_CRITRESET); error_def(ERR_DBCCERR); void rel_crit(gd_region *reg) { unix_db_info *udi; sgmnt_addrs *csa; enum cdb_sc status; intrpt_state_t prev_intrpt_state; udi = FILE_INFO(reg); csa = &udi->s_addrs; /* Assert that we never come into rel_crit with hold_onto_crit being TRUE. The only exception is for online rollback * when it is done with the actual rollback and is now in mur_close_files to release crit. At this point it will have * hold_onto_crit set to TRUE. */ assert(!csa->hold_onto_crit || (process_exiting && jgbl.onlnrlbk) || (IS_DSE_IMAGE && !csa->dse_crit_seize_done)); if (csa->now_crit) { DEFER_INTERRUPTS(INTRPT_IN_CRIT_FUNCTION, prev_intrpt_state); assert(csa->nl->in_crit == process_id || csa->nl->in_crit == 0); CRIT_TRACE(csa, crit_ops_rw); /* see gdsbt.h for comment on placement */ csa->nl->in_crit = 0; DEBUG_ONLY(locknl = csa->nl;) /* for DEBUG_ONLY LOCK_HIST macro */ status = mutex_unlockw(reg, crash_count); DEBUG_ONLY(locknl = NULL;) /* restore "locknl" to default value */ if (status != cdb_sc_normal) { csa->now_crit = FALSE; ENABLE_INTERRUPTS(INTRPT_IN_CRIT_FUNCTION, prev_intrpt_state); if (status == cdb_sc_critreset) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_CRITRESET, 2, REG_LEN_STR(reg)); else { assert(status == cdb_sc_dbccerr); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBCCERR, 2, REG_LEN_STR(reg)); } return; } ENABLE_INTERRUPTS(INTRPT_IN_CRIT_FUNCTION, prev_intrpt_state); } else CRIT_TRACE(csa, crit_ops_nocrit); /* Now that crit for THIS region is released, check if deferred signal/exit handling can be done and if so do it */ DEFERRED_EXIT_HANDLING_CHECK; } fis-gtm-V7.0-005/sr_unix/rel_latch.c0000644000032200000250000000216314342376330016144 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2015 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" #ifdef DEBUG #include "gtm_unistd.h" /* for "getpid" */ #endif #include "interlock.h" GBLREF volatile int4 fast_lock_count; /* Stop interrupts while we have our parts exposed */ GBLREF uint4 process_id; /* Release latch specified by argument */ void rel_latch(sm_global_latch_ptr_t latch) { ++fast_lock_count; /* Disable interrupts (i.e. wcs_stale) for duration to avoid potential deadlocks */ assert(process_id == getpid()); /* make sure "process_id" global variable is reliable (used below in RELEASE_SWAPLOCK) */ RELEASE_SWAPLOCK(latch); --fast_lock_count; assert(0 <= fast_lock_count); } fis-gtm-V7.0-005/sr_unix/rel_lock.c0000644000032200000250000000602614342376330016003 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_signal.h" /* for VSIG_ATOMIC_T type */ #include #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmsiginfo.h" #include "mutex.h" #include "deferred_signal_handler.h" #include "have_crit.h" #include "caller_id.h" #include "jnl.h" GBLREF volatile int4 crit_count; GBLREF uint4 process_id; GBLREF volatile int suspend_status; GBLREF node_local_ptr_t locknl; GBLREF volatile int4 gtmMallocDepth; /* Recursion indicator */ GBLREF jnl_gbls_t jgbl; error_def(ERR_CRITRESET); error_def(ERR_DBCCERR); /* Note about usage of this function : Create dummy gd_region, gd_segment, file_control, * unix_db_info, sgmnt_addrs, and allocate mutex_struct (and NUM_CRIT_ENTRY * mutex_que_entry), * mutex_spin_parms_struct, and node_local in shared memory. Initialize the fields as in * jnlpool_init(). Pass the address of the dummy region as argument to this function. */ void rel_lock(gd_region *reg) { unix_db_info *udi; sgmnt_addrs *csa; enum cdb_sc status; intrpt_state_t prev_intrpt_state; udi = FILE_INFO(reg); csa = &udi->s_addrs; /* Assert that we never come into rel_lock with hold_onto_crit being TRUE. The only exception is for online rollback * when it is done with the actual rollback and is now in mur_close_files to release crit. At this point it will have * hold_onto_crit set to TRUE. */ assert(!csa->hold_onto_crit || (process_exiting && jgbl.onlnrlbk)); if (csa->now_crit) { DEFER_INTERRUPTS(INTRPT_IN_CRIT_FUNCTION, prev_intrpt_state); assert(csa->nl->in_crit == process_id || csa->nl->in_crit == 0); CRIT_TRACE(csa, crit_ops_rw); /* see gdsbt.h for comment on placement */ csa->nl->in_crit = 0; DEBUG_ONLY(locknl = csa->nl;) /* for DEBUG_ONLY LOCK_HIST macro */ /* As of 10/07/98, crashcnt field in mutex_struct is not changed by any function for the dummy region */ status = mutex_unlockw(reg, 0); DEBUG_ONLY(locknl = NULL;) /* restore "locknl" to default value */ if (status != cdb_sc_normal) { csa->now_crit = FALSE; ENABLE_INTERRUPTS(INTRPT_IN_CRIT_FUNCTION, prev_intrpt_state); if (status == cdb_sc_critreset) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_CRITRESET, 2, REG_LEN_STR(reg)); else { assert(status == cdb_sc_dbccerr); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBCCERR, 2, REG_LEN_STR(reg)); } return; } ENABLE_INTERRUPTS(INTRPT_IN_CRIT_FUNCTION, prev_intrpt_state); } else CRIT_TRACE(csa, crit_ops_nocrit); } fis-gtm-V7.0-005/sr_unix/rel_quant.c0000755000032200000250000000134614342376330016206 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 #include "gtm_unistd.h" #include "rel_quant.h" #include "gtm_rel_quant.h" /* relinquish the processor to the next process in the scheduling queue */ void rel_quant(void) { RELQUANT; } fis-gtm-V7.0-005/sr_unix/relinkctl.c0000644000032200000250000012565714342376335016221 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2022 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 #include #include #include "gtm_ipc.h" #include "gtm_limits.h" #include "gtm_unistd.h" #include "gtm_stdlib.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_stat.h" #include "add_inter.h" #include "interlock.h" #include "gtmio.h" #include #include "relinkctl.h" #include "mmrhash.h" #include "iosp.h" #include "do_shmat.h" #include "hashtab.h" #include "ipcrmid.h" #include "eintr_wrappers.h" #include "stack_frame.h" #ifdef DEBUG # include "wbox_test_init.h" # include "toktyp.h" /* needed by "valid_mname.h" */ # include "valid_mname.h" #endif #include "arlinkdbg.h" #include "min_max.h" #include "rtnobj.h" #include "gtmmsg.h" #include "hugetlbfs_overrides.h" /* for the ADJUST_SHM_SIZE_FOR_HUGEPAGES macro */ #include "gtm_permissions.h" #include "sleep.h" #include "time.h" #include "objlabel.h" /* This module contains routines that maintain autorelink 'relinkctl' structures */ /* Constants defining how many times to retry the loop in relinkctl_open() based on the specific error conditions encountered. */ #define MAX_RCTL_INIT_WAIT_RETRIES 1000 /* # of sleeps to allow while waiting for the shared memory to be initialized. */ #define MAX_RCTL_DELETED_RETRIES 16 /* # of times to allow an existing relinkctl file to be deleted before open(). */ #define MAX_RCTL_RUNDOWN_RETRIES 16 /* # of times to allow a mapped relinkctl file to get run down before shmat(). */ DEBUG_ONLY(GBLDEF int saved_errno;) GBLREF uint4 process_id; GBLREF rtn_tabent *rtn_names, *rtn_names_end; GBLREF stack_frame *frame_pointer; GBLREF int process_exiting; GBLREF boolean_t gtm_pipe_child; OS_PAGE_SIZE_DECLARE STATICFNDCL void relinkctl_map(open_relinkctl_sgm *linkctl); STATICFNDCL void relinkctl_unmap(open_relinkctl_sgm *linkctl); STATICFNDCL void relinkctl_delete(open_relinkctl_sgm *linkctl); #define SLASH_GTM_RELINKCTL "/gtm-relinkctl-" #define SLASH_GTM_RELINKCTL_LEN STRLEN(SLASH_GTM_RELINKCTL) #define MAX_RCTL_OPEN_RETRIES 16 error_def(ERR_FILEPARSE); error_def(ERR_RELINKCTLERR); error_def(ERR_RELINKCTLFULL); error_def(ERR_REQRLNKCTLRNDWN); error_def(ERR_RLNKCTLRNDWNFL); error_def(ERR_RLNKCTLRNDWNSUC); error_def(ERR_SYSCALL); error_def(ERR_TEXT); /* Routine called to see if a relinkctl structure already exists for the given zroutines element. * * Parameters: * - obj_container_name - object container name string * - objpath - object name string * - objpath_alloc_len - length of the buffer where the resolved path to the object directory is to be placed in case of MUPIP * RUNDOWN -RELINKCTL * * Output: * - Found or newly created private structure which points to shared relink control structure */ open_relinkctl_sgm *relinkctl_attach(mstr *obj_container_name, mstr *objpath, int objpath_alloc_len) { open_relinkctl_sgm *linkctl, new_link, *new_link_ptr; int i, len, save_errno; mstr objdir; char pathin[GTM_PATH_MAX], resolvedpath[GTM_PATH_MAX]; /* Includes null terminator char */ char *pathptr; boolean_t obj_dir_found; char relinkctl_path[GTM_PATH_MAX], *ptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # ifdef AUTORELINK_SUPPORTED /* Directory name normalization - the directory name must always be the same for purposes of mapping the relinkctl * file. To accomplish this takes two steps: * 1. Use realpath() on the directory name to disambiguate it. * 2. Remove trailing slash(es) from the object directory name. */ obj_dir_found = TRUE; /* Assume we will find the path */ assert(GTM_PATH_MAX > obj_container_name->len); /* Should have been checked by our caller */ memcpy(pathin, obj_container_name->addr, obj_container_name->len); pathin[obj_container_name->len] = '\0'; /* Needs null termination for realpath call */ pathptr = realpath(pathin, resolvedpath); if (NULL == pathptr) { if (ENOENT == (save_errno = errno)) /* Note assignment */ { obj_dir_found = FALSE; /* Path no longer exists - use our best attempt to find it */ pathptr = pathin; } else { /* This error is appropriate here as the directory was checked in the caller before coming here * so this error is "just-in-case". */ if (NULL != objpath) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_FILEPARSE, 2, objpath->len, objpath->addr, save_errno); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("realpath()"), CALLFROM, save_errno); } } objdir.addr = pathptr; objdir.len = STRLEN(pathptr); assert(objdir.len < ARRAYSIZE(resolvedpath)); assert((0 == objpath_alloc_len) || TREF(is_mu_rndwn_rlnkctl)); if (TREF(is_mu_rndwn_rlnkctl) && (0 < objpath_alloc_len)) { /* In case MUPIP RUNDOWN -RELINKCTL is done on an argument with a relative path, provide it with the full path. In * case the object directory is not found, set the length of the passed mstr to 0, but only if the objpath_alloc_len * argument is non-zero. */ if (!obj_dir_found) obj_container_name->len = 0; else if (objdir.len <= objpath_alloc_len) { memcpy(obj_container_name->addr, objdir.addr, objdir.len); obj_container_name->len = objdir.len; } } while ((1 < objdir.len) && ('/' == *(objdir.addr + objdir.len - 1))) objdir.len--; objdir.addr[objdir.len] = '\0'; /* Now look the directory up in our list to see if we have it or not already. */ for (linkctl = TREF(open_relinkctl_list); NULL != linkctl; linkctl = linkctl->next) if (MSTR_EQ(&objdir, &linkctl->zro_entry_name)) return linkctl; /* Populate the relinkctl segment structure locally rather than in malloced space, so that in case relinkctl_open() below * fails we do not leak memory. */ len = relinkctl_get_key(relinkctl_path, &objdir) + 1; /* + 1 for trailing null in relinkctl_path */ assert((len <= ARRAYSIZE(relinkctl_path)) && ('\0' == relinkctl_path[len - 1])); memset(&new_link, 0, SIZEOF(open_relinkctl_sgm)); new_link.zro_entry_name = objdir; assert('\0' == new_link.zro_entry_name.addr[new_link.zro_entry_name.len]); new_link.relinkctl_path = relinkctl_path; assert('\0' == new_link.relinkctl_path[len - 1]); for (i = 0; i < NUM_RTNOBJ_SHM_INDEX; i++) new_link.rtnobj_shmid[i] = INVALID_SHMID; /* Now call relinkctl_open() to initialize new_link.fd, new_link.hdr, new_link.n_records, new_link.rec_base, * new_link.shm_hashbase, and new_link.locked. Note that in situations when the object file is not found, the call will * return a non-zero status, giving us a chance to return NULL. * * We do that because it is possible that although realpath() did not find the object and we have never opened its relinkctl * file (derived in case of ZRUPDATE from the user-provided input, which might actually not be the real path), the relinkctl * file was already created by a different process. Only if relinkctl_open() fails do we return NULL. * * Keep in mind that if the caller of relinkctl_attach() is op_zrupdate(), very shortly upon return we are going to attempt * to use the errno value set above in realpath(), so we need to restore it here first. * * It is also possible to get a non-zero return status if the caller is MUPIP RUNDOWN -RELINKCTL and the relinkctl file does * not exist and therefore does not need to be run down. */ if (0 != relinkctl_open(&new_link, !obj_dir_found)) { if (!obj_dir_found) errno = save_errno; return NULL; } /* No errors were raised so far, so copy the segment information into a malloced space. */ new_link_ptr = malloc(SIZEOF(open_relinkctl_sgm) + objdir.len + 1 + len); /* + 1 for trailing null in zro_entry_name */ memcpy(new_link_ptr, &new_link, SIZEOF(open_relinkctl_sgm)); ptr = (char *)(new_link_ptr + 1); memcpy(ptr, new_link.zro_entry_name.addr, new_link.zro_entry_name.len + 1); new_link_ptr->zro_entry_name.addr = ptr; ptr += new_link.zro_entry_name.len + 1; memcpy(ptr, new_link.relinkctl_path, len); new_link_ptr->relinkctl_path = ptr; /* Add to open list. */ new_link_ptr->next = TREF(open_relinkctl_list); TREF(open_relinkctl_list) = new_link_ptr; return new_link_ptr; # else return NULL; # endif } /* Routine to open and mmap a relinkctl file for a given $ZROUTINES object directory. * * Parameter: * - linkctl - open_relinkctl_sgm-type (process-private) block describing shared (mmap'd) entry and supplying the path * and name of the relinkctl file comprised of both static and hashed directory names for uniqueness. * - object_dir_missing - flag indicating whether we know for a fact that the object directory we are operating on does not exist. * * Returns: * Various fields in linkctl (fd, hdr). * * The control structure should be both readable *and* writable by anything that can read the object directory. */ int relinkctl_open(open_relinkctl_sgm *linkctl, boolean_t object_dir_missing) { # ifdef AUTORELINK_SUPPORTED int fd, i, j, rc, save_errno, shmid, status, stat_res, user_id, group_id, perm; struct stat stat_buf; size_t shm_size; boolean_t is_mu_rndwn_rlnkctl, shm_removed, rctl_create_attempted, rctl_existed, need_shmctl; relinkshm_hdr_t *shm_base; rtnobjshm_hdr_t *rtnobj_shm_hdr; relinkctl_data *hdr; char errstr[256]; struct stat dir_stat_buf; int rctl_deleted_count, rctl_rundown_count, rctl_init_wait_count; struct perm_diag_data pdd; struct shmid_ds shmstat; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; linkctl->hdr = NULL; /* Open the given relinkctl file. */ DBGARLNK((stderr, "relinkctl_open: pid = %d : Opening relinkctl file %s for entry %.*s\n", getpid(), linkctl->relinkctl_path, linkctl->zro_entry_name.len, linkctl->zro_entry_name.addr)); /* Anybody that has read permissions to the object container should have write permissions to the relinkctl file. */ rctl_deleted_count = rctl_rundown_count = rctl_init_wait_count = 0; is_mu_rndwn_rlnkctl = TREF(is_mu_rndwn_rlnkctl); do { /* We do not need to check the existence of the actual object directory if we verify that the respective relinkctl * file has already been created. */ STAT_FILE(linkctl->relinkctl_path, &stat_buf, stat_res); if (-1 == stat_res) { if (ENOENT != errno) { /* If the STAT call failed for a reason other than ENOENT, we will be unable to use the relinkctl * file, so error out right away. */ SNPRINTF(errstr, SIZEOF(errstr), "stat() of file %s failed", linkctl->relinkctl_path); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, errno); } else { /* If the relinkctl file is missing, then we are not going to create one if either the object * directory does not exist or we are a MUPIP RUNDOWN -RELINKCTL process. */ if (object_dir_missing || is_mu_rndwn_rlnkctl) return -1; /* We have to create a relinkctl file. We derive the permissions to use based on those of the object * directory. */ assert('\0' == linkctl->zro_entry_name.addr[linkctl->zro_entry_name.len]); /* For STAT_FILE. */ STAT_FILE(linkctl->zro_entry_name.addr, &dir_stat_buf, stat_res); if (-1 == stat_res) { SNPRINTF(errstr, SIZEOF(errstr), "stat() of file %s failed", linkctl->zro_entry_name.addr); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, errno); } if (!gtm_permissions(&dir_stat_buf, &user_id, &group_id, &perm, PERM_IPC, &pdd)) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(10 + PERMGENDIAG_ARG_COUNT) ERR_RELINKCTLERR, 2, RTS_ERROR_MSTR(&linkctl->zro_entry_name), ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("relinkctl"), RTS_ERROR_MSTR(&linkctl->zro_entry_name), PERMGENDIAG_ARGS(pdd)); } /* Attempt to create the relinkctl file with desired permissions. */ OPEN3_CLOEXEC(linkctl->relinkctl_path, O_CREAT | O_RDWR | O_EXCL, perm, fd); rctl_create_attempted = TRUE; } } else rctl_create_attempted = FALSE; assert((!rctl_create_attempted) || (!object_dir_missing)); /* If file already existed, try to open it. */ if ((!rctl_create_attempted) || ((FD_INVALID == fd) && (errno == EEXIST))) { rctl_existed = TRUE; OPEN_CLOEXEC(linkctl->relinkctl_path, O_RDWR, fd); } else rctl_existed = FALSE; if (FD_INVALID == fd) { /* If the file that we have previously seen is now gone, retry. However, avoid an infinite loop. */ if (rctl_existed && (ENOENT == errno) && (MAX_RCTL_DELETED_RETRIES > rctl_deleted_count++)) continue; SNPRINTF(errstr, SIZEOF(errstr), "open() of file %s failed", linkctl->relinkctl_path); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, errno); } FSTAT_FILE(fd, &stat_buf, status); if (0 != status) { save_errno = errno; CLOSEFILE_RESET(fd, rc); SNPRINTF(errstr, SIZEOF(errstr), "fstat() of file %s failed", linkctl->relinkctl_path); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno); } /* If we just created the relinkctl file, ensure correct permissions on it. */ if (!rctl_existed && (((INVALID_UID != user_id) && (user_id != stat_buf.st_uid)) || ((INVALID_GID != group_id) && (group_id != stat_buf.st_gid))) && (-1 == fchown(fd, user_id, group_id))) { save_errno = errno; CLOSEFILE_RESET(fd, rc); SNPRINTF(errstr, SIZEOF(errstr), "fchown() of file %s failed", linkctl->relinkctl_path); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno); } linkctl->fd = fd; if (stat_buf.st_size < RELINKCTL_MMAP_SZ) { FTRUNCATE(fd, RELINKCTL_MMAP_SZ, rc); if (0 != rc) { save_errno = errno; SNPRINTF(errstr, SIZEOF(errstr), "ftruncate() of file %s failed", linkctl->relinkctl_path); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno); } } relinkctl_map(linkctl); /* linkctl->hdr is now accessible */ hdr = linkctl->hdr; if (!hdr->initialized && rctl_existed && (MAX_RCTL_INIT_WAIT_RETRIES > rctl_init_wait_count++)) { /* The creator process has not yet initialized the shared memory for the relinkctl file; give it a chance to * do so before proceeding with grabbing the lock. */ relinkctl_unmap(linkctl); SLEEP_USEC(1000, FALSE); continue; } if (!rctl_existed) relinkctl_init_exclu(linkctl); relinkctl_lock_exclu(linkctl); if (hdr->file_deleted) { /* Some other process concurrently opened and closed/rundown the relinkctl file while we were still inside * relinkctl_open(). Reattach to new file (create it if necessary) instead of using the current fd as this * will not be visible to other new processes. */ DBGARLNK((stderr, "relinkctl_open: file_deleted = 1. Retrying open.\n")); relinkctl_unlock_exclu(linkctl); relinkctl_unmap(linkctl); assert(NULL == linkctl->hdr); assertpro(MAX_RCTL_RUNDOWN_RETRIES > rctl_rundown_count++); /* Too many loops should not be possible. */ continue; } if (0 == hdr->relinkctl_max_rtn_entries) { hdr->relinkctl_max_rtn_entries = TREF(gtm_autorelink_ctlmax); hdr->relinkctl_hash_buckets = getprime(TREF(gtm_autorelink_ctlmax)); } else assert(hdr->relinkctl_hash_buckets == getprime(hdr->relinkctl_max_rtn_entries)); shm_size = RELINKCTL_SHM_SIZE(hdr->relinkctl_hash_buckets, hdr->relinkctl_max_rtn_entries); ADJUST_SHM_SIZE_FOR_HUGEPAGES(shm_size, shm_size); /* Second parameter "shm_size" is adjusted size */ if (hdr->initialized) { /* There is an existing shared memory segment, to which we need to attach. Need lock (already obtained) to * prevent interaction with concurrent initialization, nattached = 0. */ shmid = hdr->relinkctl_shmid; DBGARLNK((stderr, "relinkctl_open: file already initialized : pre-increment hdr->nattached = %d" " : shmid = %d\n", hdr->nattached, shmid)); assert(INVALID_SHMID != shmid); assert(!hdr->file_deleted); INTERLOCK_ADD(&hdr->nattached, NULL, 1); if (!is_mu_rndwn_rlnkctl) relinkctl_unlock_exclu(linkctl); if (-1 == (sm_long_t)(shm_base = (relinkshm_hdr_t *)do_shmat(shmid, 0, 0))) { save_errno = errno; shm_removed = SHM_REMOVED(save_errno); /* If shm has been removed, then direct one to use MUPIP RUNDOWN -RELINKCTL. */ if (!is_mu_rndwn_rlnkctl) relinkctl_lock_exclu(linkctl); if (!shm_removed || !is_mu_rndwn_rlnkctl) { INTERLOCK_ADD(&hdr->nattached, NULL, -1); relinkctl_unlock_exclu(linkctl); relinkctl_unmap(linkctl); SNPRINTF(errstr, SIZEOF(errstr), "shmat() failed for shmid=%d shmsize=%llu [0x%llx]", shmid, shm_size, shm_size); if (!shm_removed) ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno); else ISSUE_REQRLNKCTLRNDWN_SYSCALL(linkctl, errstr, save_errno); } else { /* This is MUPIP RUNDOWN -RELINKCTL and shm is removed. There is no point creating one. */ DBGARLNK((stderr, "relinkctl_open: Set hdr->relinkctl_shmid to INVALID_SHMID\n")); hdr->relinkctl_shmid = INVALID_SHMID; return 0; } } } else if (!is_mu_rndwn_rlnkctl) { /* We have come here ahead of the process that has created the relinkctl file even though we have slept many * times, giving the creator a chance to grab the lock. So if we did not obtain the permissions of the * object directory, we have no permissions to apply on a shared memory segment we are about to create; * therefore, error out. */ if (rctl_existed) { relinkctl_unlock_exclu(linkctl); relinkctl_unmap(linkctl); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_REQRLNKCTLRNDWN, 3, linkctl->relinkctl_path, RTS_ERROR_MSTR(&linkctl->zro_entry_name)); } DBGARLNK((stderr, "relinkctl_open: file first open\n")); hdr->n_records = 0; /* Create shared memory to store hash buckets of routine names for faster search in relinkctl file */ shmid = gtm_shmget(IPC_PRIVATE, shm_size, RWDALL | IPC_CREAT, TRUE); if (-1 == shmid) { save_errno = errno; relinkctl_delete(linkctl); relinkctl_unlock_exclu(linkctl); relinkctl_unmap(linkctl); SNPRINTF(errstr, SIZEOF(errstr), "shmget() failed for shmsize=%llu [0x%llx]", shm_size, shm_size); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno); } if (-1 == shmctl(shmid, IPC_STAT, &shmstat)) { save_errno = errno; relinkctl_delete(linkctl); relinkctl_unlock_exclu(linkctl); relinkctl_unmap(linkctl); shm_rmid(shmid); /* if error removing shmid we created, just move on */ SNPRINTF(errstr, SIZEOF(errstr), "shmctl(IPC_STAT) failed for shmid=%d shmsize=%llu [0x%llx]", shmid, shm_size, shm_size); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno); } /* Change uid, group-id and permissions if needed */ need_shmctl = FALSE; if ((INVALID_UID != user_id) && (user_id != shmstat.shm_perm.uid)) { shmstat.shm_perm.uid = user_id; need_shmctl = TRUE; } if ((INVALID_GID != group_id) && (group_id != shmstat.shm_perm.gid)) { shmstat.shm_perm.gid = group_id; need_shmctl = TRUE; } if (shmstat.shm_perm.mode != perm) { shmstat.shm_perm.mode = perm; need_shmctl = TRUE; } if (need_shmctl && (-1 == shmctl(shmid, IPC_SET, &shmstat))) { save_errno = errno; relinkctl_delete(linkctl); relinkctl_unlock_exclu(linkctl); relinkctl_unmap(linkctl); shm_rmid(shmid); /* if error removing shmid we created, just move on */ SNPRINTF(errstr, SIZEOF(errstr), "shmctl(IPC_SET) failed for shmid=%d shmsize=%llu [0x%llx]", shmid, shm_size, shm_size); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno); } /* Initialize shared memory header */ if (-1 == (sm_long_t)(shm_base = (relinkshm_hdr_t *)do_shmat(shmid, 0, 0))) { save_errno = errno; relinkctl_delete(linkctl); relinkctl_unlock_exclu(linkctl); relinkctl_unmap(linkctl); shm_rmid(shmid); /* if error removing shmid we created, just move on */ SNPRINTF(errstr, SIZEOF(errstr), "shmat() failed for shmid=%d shmsize=%llu [0x%llx]", shmid, shm_size, shm_size); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno); } hdr->relinkctl_shmid = shmid; hdr->relinkctl_shmlen = shm_size; assert(ARRAYSIZE(shm_base->relinkctl_fname) > strlen(linkctl->relinkctl_path)); assert(0 == ((UINTPTR_T)shm_base % 8)); assert(0 == (SIZEOF(relinkshm_hdr_t) % SIZEOF(uint4))); /* assert SIZEOF(*sm_uint_ptr_t) alignment */ memset(shm_base, 0, shm_size); strcpy(shm_base->relinkctl_fname, linkctl->relinkctl_path); shm_base->min_shm_index = TREF(relinkctl_shm_min_index); /* Since search for a routine proceeds to check all rtnobj shmids from rtnobj_min_shm_index to * rtnobj_max_shm_index, set the two to impossible values so creation of the first rtnobj shmid * (whatever its shm_index turns out to be) causes these two to be overwritten to that shm_index. * Since rtnobj_min_shm_index is overwritten only if it is greater than shm_index, we set it to * one more than the highest value possible for shm_index i.e. NUM_RTNOBJ_SHM_INDEX. Likewise for * rtnobj_max_shm_index. */ shm_base->rtnobj_min_shm_index = NUM_RTNOBJ_SHM_INDEX; shm_base->rtnobj_max_shm_index = 0; shm_base->rndwn_adjusted_nattch = FALSE; DEBUG_ONLY(shm_base->skip_rundown_check = FALSE;) for (i = 0; i < NUM_RTNOBJ_SHM_INDEX; i++) { rtnobj_shm_hdr = &shm_base->rtnobj_shmhdr[i]; rtnobj_shm_hdr->rtnobj_shmid = INVALID_SHMID; rtnobj_shm_hdr->rtnobj_min_free_index = NUM_RTNOBJ_SIZE_BITS; for (j = 0; j < NUM_RTNOBJ_SIZE_BITS; j++) { rtnobj_shm_hdr->freeList[j].fl = NULL_RTNOBJ_SM_OFF_T; rtnobj_shm_hdr->freeList[j].bl = NULL_RTNOBJ_SM_OFF_T; } } SET_LATCH_GLOBAL(&shm_base->relinkctl_latch, LOCK_AVAILABLE); hdr->nattached = 1; hdr->zro_entry_name_len = MIN(linkctl->zro_entry_name.len, ARRAYSIZE(hdr->zro_entry_name) - 1); memcpy(hdr->zro_entry_name, linkctl->zro_entry_name.addr, hdr->zro_entry_name_len); hdr->zro_entry_name[hdr->zro_entry_name_len] = '\0'; /* Shared memory initialization complete. */ hdr->initialized = TRUE; relinkctl_unlock_exclu(linkctl); } else { /* This is MUPIP RUNDOWN -RELINKCTL and relinkctl file exists but the shared memory does not. We are not * going to create the shared memory to only later run it down. */ DBGARLNK((stderr, "relinkctl_open: Set hdr->relinkctl_shmid to INVALID_SHMID\n")); hdr->relinkctl_shmid = INVALID_SHMID; return 0; } assert(linkctl->locked == is_mu_rndwn_rlnkctl); assert(0 == ((UINTPTR_T)shm_base % 8)); assert(0 == (SIZEOF(relinkshm_hdr_t) % SIZEOF(uint4))); /* assert SIZEOF(*sm_uint_ptr_t) alignment */ shm_base->skip_rundown_check = TRUE; linkctl->shm_hashbase = (sm_uint_ptr_t)(sm_uc_ptr_t)(shm_base + 1); /* Skip past shm header to reach hash array start */ assert(0 == ((UINTPTR_T)linkctl->shm_hashbase % 8)); /* assert each section is 8-byte aligned at least */ /* RELINKSHM_RTNHASH_SIZE definition relies on this for 8-byte alignment */ linkctl->rec_base = (relinkrec_t *)((sm_uc_ptr_t)linkctl->shm_hashbase + RELINKSHM_RTNHASH_SIZE(hdr->relinkctl_hash_buckets)); assert(128 >= SIZEOF(relinkrec_t)); /* or else adjust CACHELINE_PAD_COND filler downwards */ assert(0 == ((UINTPTR_T)linkctl->rec_base % 8)); /* assert each section is 8-byte aligned at least */ break; } while (TRUE); # endif return 0; } #ifdef AUTORELINK_SUPPORTED /* This is called from processes that already have "linkctl" attached but have not done an increment of "linkctl->hdr->nattached". * Example is a jobbed off process in ojstartchild.c */ void relinkctl_incr_nattached(void) { open_relinkctl_sgm *linkctl; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; for (linkctl = TREF(open_relinkctl_list); NULL != linkctl; linkctl = linkctl->next) { DBGARLNK((stderr, "relinkctl_incr_nattached : pid = %d : file %s : pre-incr hdr->nattached = %d\n", getpid(), linkctl->relinkctl_path, linkctl->hdr->nattached)); assert(linkctl->hdr->nattached); INTERLOCK_ADD(&linkctl->hdr->nattached, NULL, 1); } } /* Routine to generate unique key for a $ZROUTINES entry name used to create relinkctl file for that entry in the directory * $gtm_linktmpdir (e.g. /testarea1/gtm/temp --> $gtm_linktmpdir/gtm-relinkctl-d0f3d074c724430bc1c7679141b96411). * Theoretically, we'd need a scheme to resolve hash collisions. Say, append - to the key. * But since this is 128-bit MurmurHash3, we can assume a collision will never happen in practice, so we do not * handle the extremely unlikely event of a hash collision for the few $ZROUTINES entries used by processes using * the same $gtm_linktmpdir value. * * Parameters: * * key - Generated as $gtm_linktmpdir/gtm-relinkctl-. Buffer should be GTM_PATH_MAX bytes (output). * zro_entry_name - Address of mstr containing the fully expanded zroutines entry directory name. */ int relinkctl_get_key(char key[GTM_PATH_MAX], mstr *zro_entry_name) { gtm_uint16 hash; hash128_state_t hash_state; unsigned int obj_label = OBJ_UNIX_LABEL, obj_plat_label = OBJ_PLATFORM_LABEL; unsigned char hexstr[33]; int keylen; char *key_ptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; HASH128_STATE_INIT(hash_state, 0); gtmmrhash_128_ingest(&hash_state, zro_entry_name->addr, zro_entry_name->len); gtmmrhash_128_ingest(&hash_state, &obj_label, SIZEOF(obj_label)); gtmmrhash_128_ingest(&hash_state, &obj_plat_label, SIZEOF(obj_plat_label)); gtmmrhash_128_result(&hash_state, zro_entry_name->len + SIZEOF(obj_label) + SIZEOF(obj_plat_label), &hash); gtmmrhash_128_hex(&hash, hexstr); hexstr[32] = '\0'; /* If the cumulative path to the relinkctl file exceeds GTM_PATH_MAX, it will be inaccessible, so no point continuing. */ if (GTM_PATH_MAX < (TREF(gtm_linktmpdir)).len + SLASH_GTM_RELINKCTL_LEN + SIZEOF(hexstr)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_RELINKCTLERR, 2, RTS_ERROR_MSTR(zro_entry_name), ERR_TEXT, 2, RTS_ERROR_LITERAL("Path to the relinkctl file is too long")); key_ptr = key; memcpy(key_ptr, (TREF(gtm_linktmpdir)).addr, (TREF(gtm_linktmpdir)).len); key_ptr += (TREF(gtm_linktmpdir)).len; STRCPY(key_ptr, SLASH_GTM_RELINKCTL); key_ptr += SLASH_GTM_RELINKCTL_LEN; STRNCPY_STR(key_ptr, hexstr, 33); /* NULL-terminate the string. */ key_ptr += 32; keylen = (key_ptr - key); assert((0 < keylen) && (GTM_PATH_MAX > keylen)); return keylen; } /** * Relinkctl mmap-related methods */ /* Routine to map at least n_records, currently known number of entries. * * Parameter: * linkctl - addr of process-private block describing the shared file. * * Fills in: * linkctl->hdr * linkctl->n_records */ STATICFNDEF void relinkctl_map(open_relinkctl_sgm *linkctl) { sm_uc_ptr_t addr; assert(NULL == linkctl->hdr); addr = (sm_uc_ptr_t)mmap(NULL, RELINKCTL_MMAP_SZ, (PROT_READ + PROT_WRITE), MAP_SHARED, linkctl->fd, 0); if (MAP_FAILED == addr) ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, "mmap()", errno); linkctl->hdr = (relinkctl_data *)addr; linkctl->n_records = 0; } /* Routine similar to relink_map except UNMAPs the file */ STATICFNDEF void relinkctl_unmap(open_relinkctl_sgm *linkctl) { sm_uc_ptr_t addr; int rc; if (linkctl->locked) pthread_mutex_unlock(&linkctl->hdr->exclu); addr = (sm_uc_ptr_t)linkctl->hdr; munmap(addr, RELINKCTL_MMAP_SZ); /* If munmap errors, it seems better to move on than stop for a non-critical error */ linkctl->hdr = NULL; linkctl->n_records = 0; CLOSEFILE_RESET(linkctl->fd, rc); } #endif /** * Exclusive locking methods controlling WRITE access to relinkctl control files. */ /* Routine to initialize locking for the relinkctl file. * * Parameter: * * linkctl - address of relink control structure for a given $ZROUTINEs node */ void relinkctl_init_exclu(open_relinkctl_sgm* linkctl) { # ifdef AUTORELINK_SUPPORTED int status; pthread_mutexattr_t exclu_attr; /* Set up mutex for new file */ status = pthread_mutexattr_init(&exclu_attr); if (0 != status) { relinkctl_unmap(linkctl); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, "pthread_mutexattr_init", status); } status = pthread_mutexattr_settype(&exclu_attr, PTHREAD_MUTEX_ERRORCHECK); if (0 != status) { relinkctl_unmap(linkctl); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, "pthread_mutexattr_settype", status); } status = pthread_mutexattr_setpshared(&exclu_attr, PTHREAD_PROCESS_SHARED); if (0 != status) { relinkctl_unmap(linkctl); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, "pthread_mutexattr_setpshared", status); } # if PTHREAD_MUTEX_ROBUST_SUPPORTED status = pthread_mutexattr_setrobust(&exclu_attr, PTHREAD_MUTEX_ROBUST); if (0 != status) { relinkctl_unmap(linkctl); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, "pthread_mutexattr_setrobust", status); } # endif status = pthread_mutex_init(&linkctl->hdr->exclu, &exclu_attr); if (0 != status) { relinkctl_unmap(linkctl); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, "pthread_mutex_init", status); } #endif return; } /* Routine to exclusively lock the relinkctl file. * * Parameter: * * linkctl - address of relink control structure for a given $ZROUTINEs node */ void relinkctl_lock_exclu(open_relinkctl_sgm *linkctl) { int status; # ifdef AUTORELINK_SUPPORTED assert(!linkctl->locked); if (linkctl->locked) return; status = pthread_mutex_lock(&linkctl->hdr->exclu); if (EOWNERDEAD == status) { # if PTHREAD_MUTEX_CONSISTENT_SUPPORTED status = pthread_mutex_consistent(&linkctl->hdr->exclu); if (0 != status) ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, "pthread_mutex_consistent", status); # endif } else if (0 != status) { relinkctl_data hdr_copy = *linkctl->hdr; /* Keep a copy in case we core */ assert(EINVAL != status); ISSUE_RELINKCTLERR_TEXT(&linkctl->zro_entry_name, "lock attempt failed", status); } linkctl->locked = TRUE; # endif return; } /* Routine same as relinkctl_lock_exclu() but instead UNLOCKs the lock */ void relinkctl_unlock_exclu(open_relinkctl_sgm *linkctl) { int status; # ifdef AUTORELINK_SUPPORTED assert(linkctl->locked); if (!linkctl->locked) return; status = pthread_mutex_unlock(&linkctl->hdr->exclu); if (0 != status) ISSUE_RELINKCTLERR_TEXT(&linkctl->zro_entry_name, "unlock attempt failed", status); linkctl->locked = FALSE; # endif return; } #ifdef AUTORELINK_SUPPORTED /* * Relinkctl file record management routines */ /* Iterate through each relink_record_struct starting at (relink_record_ptr)&linkctl->map_addr[0] * Find rec such that rec->rtnname == rtnname, return offset of rec. * Otherwise, return NOMATCH (defined 0xffff..). */ relinkrec_t *relinkctl_find_record(open_relinkctl_sgm *linkctl, mstr *rtnname, uint4 hash, uint4 *prev_hash_index) { relinkrec_t *rec, *base; unsigned int nrec, index; sm_uint_ptr_t ptr; uint4 rtnhash, prev_index; assert(valid_mname(rtnname)); linkctl->n_records = linkctl->hdr->n_records; /* Make sure we search among all currently existing records */ ptr = linkctl->shm_hashbase; assert(linkctl->hdr->relinkctl_hash_buckets > hash); ptr += hash; prev_index = *ptr; index = prev_index - 1; /* 'index' is unsigned so will become huge positive number in case *ptr is 0 */ base = linkctl->rec_base; nrec = linkctl->n_records; assert(linkctl->hdr->relinkctl_max_rtn_entries >= nrec); while (index < nrec) { rec = &base[index]; DEBUG_ONLY(COMPUTE_RELINKCTL_HASH(rtnname, rtnhash, linkctl->hdr->relinkctl_hash_buckets);) assert(rtnhash == hash); /* Check routine name plus null trailer in fixed version */ if ((0 == memcmp(&rec->rtnname_fixed.c, rtnname->addr, rtnname->len)) && ('\0' == rec->rtnname_fixed.c[rtnname->len])) return rec; assert(linkctl->hdr->relinkctl_max_rtn_entries >= rec->hashindex_fl); prev_index = index + 1; index = rec->hashindex_fl - 1; } *prev_hash_index = prev_index; return NULL; } /* Like relinkctl_find_record, but inserts a new entry instead of returning NULL */ relinkrec_t *relinkctl_insert_record(open_relinkctl_sgm *linkctl, mstr *rtnname) { relinkrec_t *base, *rec; uint4 hash, prev_hash_index, nrec; COMPUTE_RELINKCTL_HASH(rtnname, hash, linkctl->hdr->relinkctl_hash_buckets); rec = relinkctl_find_record(linkctl, rtnname, hash, &prev_hash_index); if (NULL == rec) { /* Record not found while not under lock - lock it and try again */ relinkctl_lock_exclu(linkctl); rec = relinkctl_find_record(linkctl, rtnname, hash, &prev_hash_index); if (NULL == rec) { /* Add a new record if room exists - else error */ assert(linkctl->locked); nrec = linkctl->n_records; assert(nrec == linkctl->hdr->n_records); /* Assured by relinkctl_find_record() */ if (linkctl->hdr->relinkctl_max_rtn_entries == linkctl->hdr->n_records) { relinkctl_unlock_exclu(linkctl); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_RELINKCTLFULL, 3, linkctl->zro_entry_name.len, linkctl->zro_entry_name.addr, linkctl->hdr->relinkctl_max_rtn_entries); } base = linkctl->rec_base; rec = base + nrec; memset(&rec->rtnname_fixed.c[0], 0, SIZEOF(mident_fixed)); assert(MAX_MIDENT_LEN >= rtnname->len); /* assert(valid_mname(rtnname)) is not needed here because it was already done in relinkctl_find_record */ memcpy(&rec->rtnname_fixed.c[0], rtnname->addr, rtnname->len); rec->cycle = 0; /* Note incr_link() will bump this to 1 when routine is linked */ rec->hashindex_fl = 0; rec->objhash = 0; rec->rtnobj_shm_offset = (rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T; if (prev_hash_index) { --prev_hash_index; assert(prev_hash_index < nrec); assert(0 == base[prev_hash_index].hashindex_fl); base[prev_hash_index].hashindex_fl = nrec + 1; } else linkctl->shm_hashbase[hash] = nrec + 1; linkctl->hdr->n_records++; assert(rec == relinkctl_find_record(linkctl, rtnname, hash, &prev_hash_index)); } relinkctl_unlock_exclu(linkctl); } return rec; } #endif /** * Relinkctl file rundown routines */ /* * Unmap all relinkctl structs after (atomically) decrementing nattached field in relinkctl file header */ void relinkctl_rundown(boolean_t decr_attached, boolean_t do_rtnobj_shm_free) { # ifdef AUTORELINK_SUPPORTED int rc; open_relinkctl_sgm *linkctl, *nextctl; rtn_tabent *rtab; rhdtyp *rtnhdr; relinkctl_data *hdr; int shmid, i, nattached; relinkshm_hdr_t *shm_hdr; rtnobjshm_hdr_t *rtnobj_shm_hdr; stack_frame *fp; boolean_t is_mu_rndwn_rlnkctl, remove_shm, remove_rctl; struct shmid_ds shm_buf; # ifdef DEBUG relinkrec_t *linkrec, *linktop; int j, k; rtnobj_sm_off_t rtnobj_shm_offset; boolean_t clean_shutdown; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (do_rtnobj_shm_free && !TREF(gtm_autorelink_keeprtn)) { assert(process_exiting); /* Run through all loaded routines and if any are loaded in shared memory, decrement their reference counts. * Since this involves shared memory, this is best accomplished by invoking rtnobj_shm_free. * For non-shared objects, zr_unlink_rtn will only release process-private memory. Since this process is * going to die (asserted by process_exiting above) no need to do anything in that case. * But before that check if the M-stack has any frames with rtnhdr->rtn_relinked TRUE. These are * routine-header structures that are not added to the rtn_names[] array but could potentially have * done rtnobj_shm_malloc(). In that case do rtnobj_shm_free() on those to decrement shared memory refcnts. */ for (fp = frame_pointer; NULL != fp; fp = SKIP_BASE_FRAME(fp->old_frame_pointer)) { rtnhdr = CURRENT_RHEAD_ADR(fp->rvector); if ((NULL != rtnhdr) && rtnhdr->rtn_relinked && rtnhdr->shared_object) { rtnobj_shm_free(rtnhdr, LATCH_GRABBED_FALSE); rtnhdr->rtn_relinked = FALSE; rtnhdr->shared_object = FALSE; } } for (rtab = rtn_names_end; rtab > rtn_names; rtab--, rtn_names_end = rtab) { /* [0] is not used (for some reason) */ rtnhdr = rtab->rt_adr; if (rtnhdr->shared_object) rtnobj_shm_free(rtnhdr, LATCH_GRABBED_FALSE); } } is_mu_rndwn_rlnkctl = TREF(is_mu_rndwn_rlnkctl); for (linkctl = TREF(open_relinkctl_list); NULL != linkctl; linkctl = nextctl) { for (i = 0; i < NUM_RTNOBJ_SHM_INDEX; i++) { if (NULL != linkctl->rtnobj_shm_base[i]) { assert(INVALID_SHMID != linkctl->rtnobj_shmid[i]); SHMDT(linkctl->rtnobj_shm_base[i]); linkctl->rtnobj_shm_base[i] = NULL; } else assert(INVALID_SHMID == linkctl->rtnobj_shmid[i]); } if (decr_attached) { /* MUPIP RUNDOWN -RELINKCTL should still hold a lock. */ assert(linkctl->locked == is_mu_rndwn_rlnkctl); if (!is_mu_rndwn_rlnkctl && !gtm_pipe_child) relinkctl_lock_exclu(linkctl); hdr = linkctl->hdr; assert((INVALID_SHMID != hdr->relinkctl_shmid) || is_mu_rndwn_rlnkctl); if (INVALID_SHMID != hdr->relinkctl_shmid) { assert(0 < hdr->nattached); if (0 < hdr->nattached) INTERLOCK_ADD(&hdr->nattached, NULL, -1); nattached = hdr->nattached; DBGARLNK((stderr, "relinkctl_rundown : pid = %d : file %s : post-decr nattached = %d\n", getpid(), linkctl->relinkctl_path, nattached)); assert(0 <= nattached); shm_hdr = GET_RELINK_SHM_HDR(linkctl); } else nattached = -1; if ((0 == nattached) && !gtm_pipe_child) { DBGARLNK((stderr, "relinkctl_rundown : nattached = 0\n")); remove_shm = remove_rctl = TRUE; } else if (is_mu_rndwn_rlnkctl) { /* If MUPIP RUNDOWN -RELINKCTL, check if shm_buff.nattch is 1 (i.e. mupip rundown -relinkctl * is the only one attached to this shm). If so, ignore nattached and run this shm down. * Most likely processes that had bumped nattached got kill -9ed. * If shm_buff.nattch is not 1, fix hdr->nattached to match shm_buf.nattch so when the time * comes for the last GTM process to rundown, it will remove the shm without the need for * any more MUPIP RUNDOWN -RELINKCTL commands. */ shmid = hdr->relinkctl_shmid; if (INVALID_SHMID != hdr->relinkctl_shmid) { if (0 != shmctl(shmid, IPC_STAT, &shm_buf)) { assert(FALSE); remove_shm = remove_rctl = FALSE; } else { nattached = shm_buf.shm_nattch - 1; /* remove self since we will do a SHMDT soon */ if (hdr->nattached != nattached) { hdr->nattached = nattached; /* fix hdr->nattached while at this */ shm_hdr->rndwn_adjusted_nattch = TRUE; } remove_shm = remove_rctl = !nattached; } if (!remove_shm) { assert(nattached); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_RLNKCTLRNDWNFL, 3, RTS_ERROR_MSTR(&linkctl->zro_entry_name), nattached); } } else { remove_shm = FALSE; remove_rctl = TRUE; } } else { assert(!gtm_pipe_child || (0 != nattached)); remove_shm = FALSE; remove_rctl = FALSE; } if (remove_shm) { # ifdef DEBUG clean_shutdown = (!shm_hdr->rndwn_adjusted_nattch && !shm_hdr->skip_rundown_check); if (clean_shutdown) { /* Check that ALL reference counts are zero */ for (linkrec = linkctl->rec_base, linktop = &linkrec[hdr->n_records]; linkrec < linktop; linkrec++) { /* 0 => do not wait for latch */ assert(grab_latch(&linkrec->rtnobj_latch, 0, NOT_APPLICABLE, NULL)); DEBUG_ONLY(rtnobj_shm_offset = linkrec->rtnobj_shm_offset;) assert(NULL_RTNOBJ_SM_OFF_T == rtnobj_shm_offset); assert(0 == linkrec->objLen); assert(0 == linkrec->usedLen); } grab_latch(&shm_hdr->relinkctl_latch, 0, NOT_APPLICABLE, NULL); /*0=> don't wait for latch*/ } /* No need to release latch as we will be deleting the shared memory anyways */ # endif for (i = 0; i < NUM_RTNOBJ_SHM_INDEX; i++) { rtnobj_shm_hdr = &shm_hdr->rtnobj_shmhdr[i]; assert(!clean_shutdown || (0 == rtnobj_shm_hdr->real_len)); assert(!clean_shutdown || (0 == rtnobj_shm_hdr->used_len)); shmid = rtnobj_shm_hdr->rtnobj_shmid; if (INVALID_SHMID != shmid) { # ifdef DEBUG if (clean_shutdown) { /* Note: shmctl can fail if some other process changes permissions * concurrently (after we had attached to the shmid) so account for * that in the assert below. */ if (0 == shmctl(shmid, IPC_STAT, &shm_buf)) { /* We expect no one else to be attached to the rtnobj shm. One case we * know of is if a process opens a PIPE device and iopi_open forks off * a child (which would cause shm_nattch to increment implicitly) and * continues execution issuing say a CRYPTNOSEEK error and exit BEFORE the * child process has done the EXEC (which would cause the shm_nattch to * decrement it back to the expected value). Account for it in the assert. */ assert((0 == shm_buf.shm_nattch) || TREF(fork_without_child_wait)); } k = i + MIN_RTNOBJ_SHM_INDEX - MIN_RTNOBJ_SIZE_BITS; assert(rtnobj_shm_hdr->shm_len == ((gtm_uint64_t)1 << (k + MIN_RTNOBJ_SIZE_BITS))); for (j = 0; j < NUM_RTNOBJ_SIZE_BITS; j++) { if (j != k) { assert(NULL_RTNOBJ_SM_OFF_T == rtnobj_shm_hdr->freeList[j].fl); assert(NULL_RTNOBJ_SM_OFF_T == rtnobj_shm_hdr->freeList[j].bl); } else { assert(OFFSETOF(rtnobj_hdr_t, userStorage) == rtnobj_shm_hdr->freeList[j].fl); assert(OFFSETOF(rtnobj_hdr_t, userStorage) == rtnobj_shm_hdr->freeList[j].bl); } } } # endif shm_rmid(shmid); /* If error removing shmid, not much we can do. Just move on */ } } SHMDT(shm_hdr); /* If error detaching, not much we can do. Just move on */ shmid = hdr->relinkctl_shmid; shm_rmid(shmid); /* If error removing shmid, not much we can do. Just move on */ } else { SHMDT(shm_hdr); /* If error detaching, not much we can do. Just move on */ } if (remove_rctl) { relinkctl_delete(linkctl); if (is_mu_rndwn_rlnkctl) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_RLNKCTLRNDWNSUC, 2, RTS_ERROR_MSTR(&linkctl->zro_entry_name)); } linkctl->rec_base = NULL; linkctl->shm_hashbase = NULL; if (!gtm_pipe_child) relinkctl_unlock_exclu(linkctl); } relinkctl_unmap(linkctl); /* sets "linkctl->hdr" to NULL */ nextctl = linkctl->next; free(linkctl); } TREF(open_relinkctl_list) = NULL; # endif return; } #ifdef AUTORELINK_SUPPORTED /* * Clean up (i.e. delete) relinkctl file whose descriptor block is passed in */ STATICFNDEF void relinkctl_delete(open_relinkctl_sgm *linkctl) { int status; DBGARLNK((stderr, "relinkctl_delete : pid = %d : Deleting %s\n", getpid(), linkctl->relinkctl_path)); status = UNLINK(linkctl->relinkctl_path); /* If unlink succeeds, then set "hdr->file_deleted" to TRUE to notify other processes in relinkctl_open * about this concurrent delete so they can reattach. If the unlink fails (e.g. due to permission issues) * do not set "hdr->file_deleted". */ if (-1 != status) linkctl->hdr->file_deleted = TRUE; /* Notify any other process in relinkctl_open about this so they can reattach */ else linkctl->hdr->initialized = FALSE; /* So next process reinitializes hdr->relinkctl_shmid etc. */ return; } #endif fis-gtm-V7.0-005/sr_unix/relinkctl.h0000644000032200000250000004122314342376330016203 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2020 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. * * * ****************************************************************/ #ifndef RELINKCTL_H_INCLUDED #define RELINKCTL_H_INCLUDED # include "gtm_limits.h" /* Input RTNNAME is derived from a file name and we need to convert it to a proper routine name. * a) It is possible we got a '_' as the first character in case of a % routine. But the actual routine name stored in * that object file would have '%' as the first character. Fix that along with removing any .m extension * b) Also limit routine name length to MAX_MIDENT_LEN (internal design max). Ignore the rest of the file name. */ #define CONVERT_FILENAME_TO_RTNNAME(RTNNAME) \ MBSTART { \ if ('_' == RTNNAME.addr[0]) \ RTNNAME.addr[0] = '%'; \ if (!MEMCMP_LIT(&RTNNAME.addr[RTNNAME.len - SIZEOF(DOTM) + 1],DOTM)) \ RTNNAME.len -= (SIZEOF(DOTM) - 1); \ if (MAX_MIDENT_LEN < RTNNAME.len) \ RTNNAME.len = MAX_MIDENT_LEN; \ } MBEND #define COMPUTE_RELINKCTL_HASH(RTNNAME, RTNHASH, RELINKCTL_HASH_BUCKETS) \ { \ STR_HASH((RTNNAME)->addr, (RTNNAME)->len, RTNHASH, 0); \ RTNHASH = RTNHASH % RELINKCTL_HASH_BUCKETS; \ } /* One relinkctl file can contain at most this many # of routines */ # define RELINKCTL_MAX_ENTRIES 16000000 # define RELINKCTL_MIN_ENTRIES (WBTEST_ENABLED(WBTEST_RELINKCTL_MAX_ENTRIES) ? 1 : 1000) # define RELINKCTL_DEFAULT_ENTRIES (WBTEST_ENABLED(WBTEST_RELINKCTL_MAX_ENTRIES) ? 100 : 50000) #define RELINKCTL_MMAP_SZ ((size_t)SIZEOF(relinkctl_data)) #define RELINKSHM_HDR_SIZE ((size_t)SIZEOF(relinkshm_hdr_t)) /* We are guaranteed relinkctl_hash_buckets is an odd prime number and since we want at least 8-byte alignment between different * sections of shared memory, we add a 4-byte filler to the RELINKSHM_RTNHASH_SIZE macro computation (that's why we are adding + 1). */ #define RELINKSHM_RTNHASH_SIZE(RELINKCTL_HASH_BUCKETS) (((size_t)RELINKCTL_HASH_BUCKETS + 1) * SIZEOF(uint4)) #define RELINKSHM_RECARRAY_SIZE(RELINKCTL_MAX_RTN_ENTRIES) (((size_t)RELINKCTL_MAX_RTN_ENTRIES) * SIZEOF(relinkrec_t)) #define RELINKCTL_SHM_SIZE(RELINKCTL_HASH_BUCKETS, RELINKCTL_MAX_RTN_ENTRIES) (RELINKSHM_HDR_SIZE \ + RELINKSHM_RTNHASH_SIZE(RELINKCTL_HASH_BUCKETS) + RELINKSHM_RECARRAY_SIZE(RELINKCTL_MAX_RTN_ENTRIES)) #define GET_RELINK_SHM_HDR(LINKCTL) (relinkshm_hdr_t *)((sm_uc_ptr_t)LINKCTL->shm_hashbase - SIZEOF(relinkshm_hdr_t)) error_def(ERR_RLNKRECLATCH); /* needed for the RELINKCTL_CYCLE_INCR macro */ /* Macro to bump the cycle# of a given relinkctl record. Make sure shared cycle never becomes 0 after the bump since a process * initializes its private cycle to 0 at relinkctl file open time and we want to make sure the private-cycle == shared-cycle * check fails always the first time for a process. */ #define RELINKCTL_CYCLE_INCR(RELINKREC, LINKCTL) \ { \ if (!grab_latch(&(RELINKREC)->rtnobj_latch, RLNKREC_LATCH_TIMEOUT_SEC, NOT_APPLICABLE, NULL)) \ { \ assert(FALSE); \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_RLNKRECLATCH, 3, \ RELINKREC->rtnname_fixed.c, RTS_ERROR_MSTR(&LINKCTL->zro_entry_name)); \ } \ if (0 == ++(RELINKREC)->cycle) \ ++(RELINKREC)->cycle; \ rel_latch(&(RELINKREC)->rtnobj_latch); \ } /* A routine buffer is stored a shared memory segment. Since shm sizes are fixed, if ever we cannot fit in a given routine * buffer in a given shm, we create another shm with double the size and keep doing this until we come with a shm that can * fit in the given routine buffer. We allow for a max of MAX_RTNOBJ_SHM_INDEX such shmids to be allocated per relinkctl file. * Since we need 6 bits to represent MAX_RTNOBJ_SHM_INDEX, we treat the array of shmids to be on huge 2**64 sized shmid. Where * the top 6 bits represent the index in the shmid array and the remaining 58 bits correspond to the offset within that shmid * where the routine buffer can be found. Such an offset into a routine buffer in shared memory is typed as rtnobj_sm_off_t. */ #define RTNOBJ_SHMID_INDEX_MAXBITS 6 /* Max # of bits needed to store NUM_RTNOBJ_SHM_INDEX */ #define MIN_RTNOBJ_SHM_INDEX 20 /* Minimum size of shared memory segment created to store .o (rtnobj) files is 2**20. * Do not change this macro as gtm_autorelink_shm env var is specified in a multiple of * 2**MIN_RTNOBJ_SHM_INDEX bytes and will have user doc implications. */ #define MAX_RTNOBJ_SHM_INDEX 58 /* Maximum size of shared memory segment created to store .o (rtnobj) files is 2**57 */ #define NUM_RTNOBJ_SHM_INDEX (MAX_RTNOBJ_SHM_INDEX - MIN_RTNOBJ_SHM_INDEX) #define NULL_RTNOBJ_SM_OFF_T ((sm_off_t)MAXUINT8) /* Is (2**64 - 1) i.e. 0xFFFF FFFF FFFF FFFF */ #define RTNOBJ_GET_SHM_INDEX(SHM_OFF) (SHM_OFF >> MAX_RTNOBJ_SHM_INDEX) #define RTNOBJ_GET_SHM_OFFSET(SHM_OFF) (SHM_OFF & 0x03FFFFFFFFFFFFFFULL) #define RTNOBJ_SET_SHM_INDEX_OFF(SHM_INDEX, SHM_OFF) (((rtnobj_sm_off_t)SHM_INDEX << MAX_RTNOBJ_SHM_INDEX) | (SHM_OFF)) /* For the latch timeouts below, we believe most are likely done within 1 minute but since IO can be done and a failure in one * of these locks is a hard-error, the max is set to 4 mins. */ #define RLNKSHM_LATCH_TIMEOUT_SEC (4 * 60) /* 4 min */ #define RLNKREC_LATCH_TIMEOUT_SEC (4 * 60) /* 4 min */ #define MIN_RTNOBJ_SIZE_BITS 8 /* Minimum object file size (including SIZEOF(rtnobj_hdr_t)) is 2**8 = 256 */ #define MAX_RTNOBJ_SIZE_BITS MAX_RTNOBJ_SHM_INDEX /* Maximum object file size (including SIZEOF(rtnobj_hdr_t)) is 2**32 * but when we allocate a rtnobj shared memory segment of size * (2**MAX_RTNOBJ_SHM_INDEX) we want to add that as one element to the * "rtnobjshm_hdr_t->freeList[]" array and hence this definition. */ #define NUM_RTNOBJ_SIZE_BITS (MAX_RTNOBJ_SIZE_BITS + 1 - MIN_RTNOBJ_SIZE_BITS) #define IS_INSERT 0 #define IS_DELETE 1 error_def(ERR_REQRLNKCTLRNDWN); /* needed for the ISSUE_REQRLNKCTLRNDWN_SYSCALL macro */ #define ISSUE_REQRLNKCTLRNDWN_SYSCALL(LINKCTL, ERRSTR, ERRNO) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(13) \ ERR_REQRLNKCTLRNDWN, 3, LINKCTL->relinkctl_path, RTS_ERROR_MSTR(&LINKCTL->zro_entry_name), \ ERR_SYSCALL, 5, LEN_AND_STR(ERRSTR), CALLFROM, DEBUG_ONLY(saved_errno = )ERRNO) #define ISSUE_RELINKCTLERR_SYSCALL(ZRO_ENTRY_NAME, ERRSTR, ERRNO) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(12) ERR_RELINKCTLERR, 2, RTS_ERROR_MSTR(ZRO_ENTRY_NAME), \ ERR_SYSCALL, 5, LEN_AND_STR(ERRSTR), CALLFROM, DEBUG_ONLY(saved_errno = )ERRNO) #define ISSUE_RELINKCTLERR_TEXT(ZRO_ENTRY_NAME, ERRORTEXT, ERRNO) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_RELINKCTLERR, 2, RTS_ERROR_MSTR(ZRO_ENTRY_NAME), \ ERR_TEXT, 2, LEN_AND_STR(ERRORTEXT), DEBUG_ONLY(saved_errno = )ERRNO) typedef gtm_uint64_t rtnobj_sm_off_t; /* Shared structure - relink record corresponding to a relinkctl file (resides in shared memory) */ typedef struct relinkrec_struct { mident_fixed rtnname_fixed; uint4 cycle; uint4 hashindex_fl; /* Forward link ptr to linked list of relinkrec_t structures that have same rtnhash value */ uint4 numvers; /* Number of versions of this .o file currently in this relinkctl shared memory */ uint4 filler_8byte_align; gtm_uint64_t objLen; /* Total object file length of various versions of this .o file in shared memory */ gtm_uint64_t usedLen; /* Total length used up in shared memory for various versions of this .o file. * Due to rounding up of objLen to nearest 2-power, usedLen >= objLen always. */ rtnobj_sm_off_t rtnobj_shm_offset; /* offset into shared memory where this object file can be found for linking. * If object is not loaded, set to NULL_RTNOBJ_SM_OFF_T. */ global_latch_t rtnobj_latch; /* Lock used to search/insert/delete entries in the * object file linked list (starting at rtnobj_shm_offset) */ gtm_uint64_t objhash; /* Hash of the object file last touched here */ CACHELINE_PAD_COND(88, 1) /* Add 40 bytes (on top of 88 bytes) to make this structure 128 bytes to avoid cacheline * interference (between successive relinkrec_t structures in shared memory) on the RS6000 * (where the cacheline is 128 bytes currently). Other platforms have smaller * cachelines (i.e. 64 bytes or lesser) and so this structure (88 bytes currently) * taking up more than one cacheline dont require this padding. */ } relinkrec_t; #define ZRO_DIR_PATH_MAX MAX_FN_LEN /* since "zro_load" which parses $zroutines uses MAX_FN_LEN */ /* Shared structure - relinkctl file header */ typedef struct relinkctl_data_struct { uint4 n_records; int4 nattached; /* Number of processes currently attached. this is approximate, because if a process * is kill 9'd, nattached is not decrememented. * If nattached is 0 upon exiting, we can remove the file. * TODO: Provide fancier cleanup scheme for kill 9. two options: * 1. SYSV semaphore (as with the db). increment in open_relinkctl * 2. When we want to cleanup (say mupip routine -rundown), execute 'fuser' */ int4 relinkctl_shmid;/* ID of primary shared memory segment corresponding to the mmaped relinkctl file. * This contains the array of relinkrec_t structures as well as hash buckets to * speed up search of routine names. */ uint4 relinkctl_shmlen;/* size of shared memory */ int4 file_deleted; /* Way of signaling processes in relinkctl_open about a concurrent delete so they * close their fd and reopen the file. */ uint4 initialized; /* relinkctl file has been successfully initialized */ char zro_entry_name[ZRO_DIR_PATH_MAX + 1]; /* null-terminated full path of the directory in $zroutines * whose relinkctl file is this. Given a relinkctl file, one can * use this to find the corresponding directory. */ int zro_entry_name_len; /* strlen of the null-terminated "zro_entry_name" */ int relinkctl_max_rtn_entries; /* One relinkctl file can contain at most this many of routines */ int relinkctl_hash_buckets; /* The first prime # above relinkctl_max_rtn_entries */ pthread_mutex_t exclu; /* Protect the file while mmap'd */ } relinkctl_data; /* Process private structure - describes a relinkctl file. Process private so can be linked into a list in $ZROUTINES order */ typedef struct open_relinkctl_struct { struct open_relinkctl_struct *next; /* List of open ctl structures, sorted by zro_entry_name */ mstr zro_entry_name; /* object directory name from $zroutines */ char *relinkctl_path; /* full path of the relinkctl file corresponding to this objdir */ uint4 n_records; /* Private copy */ boolean_t locked; /* TRUE if this process owns exclusive lock */ relinkctl_data *hdr; /* Base of mapped file */ relinkrec_t *rec_base; sm_uint_ptr_t shm_hashbase; /* base of hash table in shared memory */ sm_uc_ptr_t rtnobj_shm_base[NUM_RTNOBJ_SHM_INDEX]; int rtnobj_shmid[NUM_RTNOBJ_SHM_INDEX]; int fd; int rtnobj_min_shm_index; /* Copied over from relinkshm_hdr->rtnobj_min_shm_index */ int rtnobj_max_shm_index; /* Copied over from relinkshm_hdr->rtnobj_max_shm_index */ } open_relinkctl_sgm; typedef struct rtnobjshm_hdr_struct { que_ent freeList[NUM_RTNOBJ_SIZE_BITS]; int rtnobj_min_free_index; /* minimum 'i' where freeList[i] has non-zero fl,bl links */ int rtnobj_max_free_index; /* maximum 'i' where freeList[i-1] has non-zero fl,bl links * if no freeList[i] has non-zero fl,bl links, this will be set to 0. */ int rtnobj_shmid; gtm_uint64_t real_len; /* sum of realLen of .o files used currently in this rtnobj shared memory segment */ gtm_uint64_t used_len; /* sum of space occupied by .o files currently in this rtnobj shared memory segment */ gtm_uint64_t shm_len; /* size of shared memory segment */ } rtnobjshm_hdr_t; /* Shared memory header structure corresponding to relinkctl file. This is followed by a hash-array for speedy routine name * access. */ typedef struct relinkshm_hdr { char relinkctl_fname[GTM_PATH_MAX]; /* full path of the relinkctl file (mmr hash is in this name) */ int min_shm_index; int rtnobj_min_shm_index; /* Minimum 'i' where rtnobj_shmhdr[i].rtnobj_shmid is a valid shmid */ int rtnobj_max_shm_index; /* Maximum 'i' where rtnobj_shmhdr[i-1].rtnobj_shmid is a valid shmid. * If no rtnobj_shmhdr[i] has valid shmid, this will be set to 0. */ boolean_t rndwn_adjusted_nattch; /* MUPIP RUNDOWN -RELINKCTL did adjust nattached */ boolean_t skip_rundown_check; /* TRUE if at least one process with gtm_autorelink_keeprtn=1 opened this */ rtnobjshm_hdr_t rtnobj_shmhdr[NUM_RTNOBJ_SHM_INDEX]; /* CACHELINE_PAD macro usages surrounding the actual latch below provides spacing so updates to the latch do not interfere * with updates to adjoining fields which can happen if they fall in the same data cacheline of a processor. No * CACHELINE_PAD before the latch as adjoining fields before the latch are updated only if we hold the latch so no * interference possible. */ global_latch_t relinkctl_latch; /* latch for insertions/deletions of buddy list structurs in any rtnobj shmids */ CACHELINE_PAD(SIZEOF(global_latch_t), 1) } relinkshm_hdr_t; #define STATE_FREE 0 #define STATE_ALLOCATED 1 /* "refcnt" is a signed 4-byte integer so the max it can go to is 2**31-1. * Once it reaches this value, we can no longer accurately maintain refcnt (rollover issues). * So keep it there thereby never removing the corresponding object from shared memory. */ #define REFCNT_INACCURATE MAXPOSINT4 /* Header structure for each .o file in the "rtnobj" shared memory */ typedef struct rtnobj_hdr_struct { unsigned short queueIndex; /* 2**queueIndex is the size of this element (including this header) */ unsigned char state; /* State of this block */ unsigned char initialized; /* Has the .o file been read from disk into this slot */ int4 refcnt; /* # of processes that are currently using this .o file */ gtm_uint64_t objhash; /* 8-byte checksum of object file contents. * Used to differentiate multiple versions of the .o file with the same * routine name; Each gets a different routine buffer in shared memory. */ rtnobj_sm_off_t next_rtnobj_shm_offset; /* Offset into shared memory where the routine buffer of the .o file with * the same name as this one can be found but with a different checksum. * Basically a linked list. Null pointer set to NULL_RTNOBJ_SM_OFF_T. */ uint4 relinkctl_index; /* Index into reclinkrec_t[] array where the rtnname corresponding to this * .o file can be found; Note multiple versions of .o file with different * values will have different routine buffers in * shared memory each of them pointing back to the same relinkctl_index. */ uint4 objLen; /* Size of the allocated .o file. Currently not allowed to go > 4Gb. * together are used to differentiate multiple * versions of the same .o file name; Each of them with a different value * of either src_cksum_8byte or objLen gets a different routine buffer in * shared memory. Note that it is theoretically possible (though rare) that * two different .o files have the same 8-byte src checksum and same objLen. * In that case they will use the same routine buffer. But since we expect * this to be very rare in practice, we consider this acceptable for now. */ union { que_ent freePtr; /* Pointer to next and previous storage element on free queue */ unsigned char userStart; /* First byte of user useable storage */ } userStorage; } rtnobj_hdr_t; /* * Prototypes */ open_relinkctl_sgm *relinkctl_attach(mstr *obj_container_name, mstr *objpath, int objpath_alloc_len); void relinkctl_incr_nattached(void); int relinkctl_get_key(char key[GTM_PATH_MAX], mstr *zro_entry_name); relinkrec_t *relinkctl_find_record(open_relinkctl_sgm *linkctl, mstr *rtnname, uint4 hash, uint4 *prev_hash_index); relinkrec_t *relinkctl_insert_record(open_relinkctl_sgm *linkctl, mstr *rtnname); int relinkctl_open(open_relinkctl_sgm *linkctl, boolean_t obj_file_missing); void relinkctl_init_exclu(open_relinkctl_sgm* linkctl); void relinkctl_lock_exclu(open_relinkctl_sgm *linkctl); void relinkctl_unlock_exclu(open_relinkctl_sgm *linkctl); void relinkctl_rundown(boolean_t decr_attached, boolean_t do_rtnobj_shm_free); #endif /* RELINKCTL_H_INCLUDED */ fis-gtm-V7.0-005/sr_unix/remove_rms.c0000755000032200000250000001130414342376330016365 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_stdio.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "io.h" #include "gtm_limits.h" #include "iormdef.h" #include "gtmio.h" #include "gtmcrypt.h" GBLREF io_log_name *io_root_log_name; GBLREF int process_exiting; error_def(ERR_CRYPTKEYRELEASEFAILED); void remove_rms (io_desc *ciod) { io_log_name **lpp, *lp; /* logical name pointers */ d_rm_struct *rm_ptr; io_desc *iod; int i, rc, fclose_res; # ifdef DEBUG DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # endif /* The routine is also called now when there's an open error and ciod->type == n_io_dev_types. */ assert((rm == ciod->type) || (n_io_dev_types == ciod->type) || (NULL == ciod->dev_sp)); assert(ciod->state == dev_closed || ciod->state == dev_never_opened); /* When we get here after an open error with n_io_dev_types == ciod->type, the ciod->dev_sp should be NULL. Assert it */ assert((n_io_dev_types != ciod->type) || (NULL == ciod->dev_sp)); rm_ptr = ((rm == ciod->type) || (pi == ciod->type) || (ff == ciod->type)) ? (d_rm_struct *) ciod->dev_sp : NULL; /* This routine will now be called if there is an open error to remove the partially created device * from the list of devices in io_root_log_name. This means rm_ptr may be zero so don't use it if it is. */ /* if this is the stderr device being closed directly by user then let the close of the pipe handle it */ if (rm_ptr && rm_ptr->stderr_parent) return; if (rm_ptr && (0 < rm_ptr->fildes)) CLOSEFILE_RESET(rm_ptr->fildes, rc); /* resets "rm_ptr->fildes" to FD_INVALID */ if (rm_ptr && (NULL != rm_ptr->filstr)) FCLOSE(rm_ptr->filstr, fclose_res); if (rm_ptr && (0 < rm_ptr->read_fildes)) CLOSEFILE_RESET(rm_ptr->read_fildes, rc); /* resets "rm_ptr->read_fildes" to FD_INVALID */ if (rm_ptr && (rm_ptr->read_filstr != NULL)) FCLOSE(rm_ptr->read_filstr, fclose_res); if (rm_ptr && rm_ptr->input_encrypted && (GTMCRYPT_INVALID_KEY_HANDLE != rm_ptr->input_cipher_handle)) { GTMCRYPT_REMOVE_CIPHER_CONTEXT(rm_ptr->input_cipher_handle, rc); if (0 != rc) GTMCRYPT_REPORT_ERROR(rc, rts_error, ciod->trans_name->len, ciod->trans_name->dollar_io); } if (rm_ptr && rm_ptr->output_encrypted && (GTMCRYPT_INVALID_KEY_HANDLE != rm_ptr->output_cipher_handle)) { GTMCRYPT_REMOVE_CIPHER_CONTEXT(rm_ptr->output_cipher_handle, rc); if (0 != rc) GTMCRYPT_REPORT_ERROR(rc, rts_error, ciod->trans_name->len, ciod->trans_name->dollar_io); } if (rm_ptr && (NULL != rm_ptr->fsblock_buffer)) free(rm_ptr->fsblock_buffer); if ((n_io_dev_types != ciod->type) && ciod->newly_created) { if (((rm == ciod->type) && ((NULL == rm_ptr) || !rm_ptr->is_pipe)) || (ff == ciod->type)) { rc = UNLINK(ciod->trans_name->dollar_io); assert(!rc); } } for (lpp = &io_root_log_name, lp = *lpp; lp; lp = *lpp) { if ((NULL != lp->iod) && (n_io_dev_types == lp->iod->type)) { /* remove the uninitialized device */ *lpp = (*lpp)->next; free(lp); continue; } iod = lp->iod; /* Handle case where iod can be NULL (e.g. if GTM-F-MEMORY occurred during device setup & we are creating * zshow dump file). */ assert((NULL != iod) || (process_exiting && TREF(jobexam_counter))); if ((NULL != iod) && (iod->pair.in == ciod) ZOS_ONLY(|| (rm_ptr && rm_ptr->fifo && (iod->pair.out == ciod)))) { assert (iod == ciod); # ifndef __MVS__ assert (iod->pair.out == ciod); # else if (rm_ptr && rm_ptr->fifo) { if (ciod == iod->pair.out) free(iod->pair.in); else if (ciod == iod->pair.in) free(iod->pair.out); } # endif /* The only device that may be "split" is the principal device, other than a PIPE device which * is handled above. Since that device is permanently open, it will never get here. */ *lpp = (*lpp)->next; free(lp); continue; } lpp = &lp->next; } if (rm_ptr) { if ((rm_ptr->is_pipe) || (pi == ciod->type)) { /* free up dev_param_pairs if defined */ for ( i = 0; i < rm_ptr->dev_param_pairs.num_pairs; i++ ) { if (NULL != rm_ptr->dev_param_pairs.pairs[i].name) free(rm_ptr->dev_param_pairs.pairs[i].name); if (NULL != rm_ptr->dev_param_pairs.pairs[i].definition) free(rm_ptr->dev_param_pairs.pairs[i].definition); } } free (rm_ptr); } free(ciod); } fis-gtm-V7.0-005/sr_unix/repl_inst_create.c0000644000032200000250000002351414342376330017534 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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_stdio.h" #include "gtm_string.h" #include "gtm_unistd.h" #include "gtm_fcntl.h" #include "gtm_stat.h" #include "gtm_time.h" #include "gtm_inet.h" #include #include #include #include "eintr_wrappers.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "repl_msg.h" #include "gtmsource.h" #include "cli.h" #include "gtmrecv.h" #include "iosp.h" #include "gtmio.h" #include "gtm_logicals.h" #include "trans_log_name.h" #include "gtmmsg.h" #include "repl_sem.h" #include "repl_instance.h" #include "gtm_rename.h" GBLREF boolean_t in_repl_inst_create; /* used by repl_inst_read/repl_inst_write */ GBLREF uint4 process_id; error_def(ERR_FILEEXISTS); error_def(ERR_FILERENAME); error_def(ERR_LOGTOOLONG); error_def(ERR_RENAMEFAIL); error_def(ERR_REPLINSTACC); error_def(ERR_REPLINSTNMLEN); error_def(ERR_REPLINSTNMUNDEF); error_def(ERR_REPLINSTSTNDALN); error_def(ERR_TEXT); /* Description: * Creates replication instance file. * Parameters: * None * Return Value: * None */ void repl_inst_create(void) { unsigned int inst_fn_len; unsigned short inst_name_len; int rename_fn_len; char rename_fn[MAX_FN_LEN + 1]; char inst_fn[MAX_FN_LEN + 1], inst_name[MAX_FN_LEN + 1]; char machine_name[MAX_MCNAMELEN], buff_unaligned[REPL_INST_HDR_SIZE + GTMSRC_LCL_SIZE + 8]; char *buff_8byte_aligned; int idx, status; struct stat stat_buf; repl_inst_hdr_ptr_t repl_instance; gtmsrc_lcl_ptr_t gtmsrc_lcl_array; mstr log_nam, trans_name; uint4 status2; jnl_tm_t now; assertpro(repl_inst_get_name(inst_fn, &inst_fn_len, MAX_FN_LEN + 1, issue_rts_error, NULL)); /* rts_error should prevent * return if there is a problem. */ /* Although the maximum length of an instance name is MAX_INSTNAME_LEN-1 characters, the input buffer needs to hold a lot * more since the input instance name might be longer. Hence inst_name (containing MAX_FN_LEN+1 = 257 bytes) is used. */ inst_name_len = 0; if (cli_present("NAME")) { inst_name_len = SIZEOF(inst_name); if (!cli_get_str("NAME", &inst_name[0], &inst_name_len)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TEXT, 2, RTS_ERROR_TEXT("Error parsing NAME qualifier")); } else { log_nam.addr = GTM_REPL_INSTNAME; log_nam.len = SIZEOF(GTM_REPL_INSTNAME) - 1; trans_name.addr = &inst_name[0]; if (SS_NORMAL != (status = TRANS_LOG_NAME(&log_nam, &trans_name, inst_name, SIZEOF(inst_name), dont_sendmsg_on_log2long))) { if (SS_LOG2LONG == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_LOGTOOLONG, 3, log_nam.len, log_nam.addr, SIZEOF(inst_name) - 1); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_REPLINSTNMUNDEF); } inst_name_len = trans_name.len; } if ((MAX_INSTNAME_LEN <= inst_name_len) || (0 == inst_name_len)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_REPLINSTNMLEN, 2, inst_name_len, inst_name); inst_name[inst_name_len] = '\0'; buff_8byte_aligned = &buff_unaligned[0]; buff_8byte_aligned = (char *)ROUND_UP2((INTPTR_T)buff_8byte_aligned, 8); repl_instance = (repl_inst_hdr_ptr_t)&buff_8byte_aligned[0]; gtmsrc_lcl_array = (gtmsrc_lcl_ptr_t)&buff_8byte_aligned[REPL_INST_HDR_SIZE]; memset(machine_name, 0, SIZEOF(machine_name)); if (GETHOSTNAME(machine_name, MAX_MCNAMELEN, status)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_TEXT, 2, RTS_ERROR_TEXT("Unable to get the hostname"), errno); STAT_FILE(inst_fn, &stat_buf, status); if (-1 != status) { if (cli_present("NOREPLACE")) /* the file exists, so error out */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_FILEEXISTS, 2, inst_fn_len, inst_fn); in_repl_inst_create = TRUE; /* used by an assert in the call to "repl_inst_read" below */ repl_inst_read(inst_fn, (off_t)0, (sm_uc_ptr_t)repl_instance, SIZEOF(repl_inst_hdr)); in_repl_inst_create = FALSE; if ((INVALID_SEMID != repl_instance->jnlpool_semid) || (INVALID_SHMID != repl_instance->jnlpool_shmid) || (INVALID_SEMID != repl_instance->recvpool_semid) || (INVALID_SHMID != repl_instance->recvpool_shmid)) { assert(FALSE || WBTEST_ENABLED(WBTEST_REPLINSTSTNDALN)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_REPLINSTSTNDALN, 2, inst_fn_len, inst_fn); } JNL_SHORT_TIME(now); rename_fn_len = ARRAYSIZE(rename_fn); if (SS_NORMAL != (status = prepare_unique_name((char *)inst_fn, inst_fn_len, "", "", rename_fn, &rename_fn_len, now, &status2))) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_LIT("Error preparing unique name for renaming instance file")); if (SS_NORMAL != status2) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLINSTACC, 2, inst_fn_len, inst_fn, status, 0, status2); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_REPLINSTACC, 2, inst_fn_len, inst_fn, status); } if (SS_NORMAL != (status = gtm_rename((char *)inst_fn, (int)inst_fn_len, (char *)rename_fn, rename_fn_len, &status2))) { if (SS_NORMAL != status2) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_RENAMEFAIL, 4, inst_fn_len, inst_fn, rename_fn_len, rename_fn, status, 0, status2); else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_RENAMEFAIL, 4, inst_fn_len, inst_fn, rename_fn_len, rename_fn, status); } else /* successfully renamed the existing file; print a message */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_FILERENAME, 4, inst_fn_len, inst_fn, rename_fn_len, rename_fn); } else if (ENOENT != errno) /* some error happened */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_REPLINSTACC, 2, inst_fn_len, inst_fn, errno); /* The instance file consists of 3 parts. * File header ("repl_inst_hdr" structure) * Array of 16 "gtmsrc_lcl" structures * Variable length array of "repl_histinfo" structures * Of these the last part is not allocated at file creation time. The rest have to be initialized now. */ /************************** Initialize "repl_inst_hdr" section ***************************/ memset(repl_instance, 0, SIZEOF(repl_inst_hdr)); memcpy(&repl_instance->label[0], GDS_REPL_INST_LABEL, GDS_REPL_INST_LABEL_SZ-1); repl_instance->replinst_minorver = GDS_REPL_INST_MINOR_LABEL; repl_instance->is_little_endian = GTM_IS_LITTLE_ENDIAN; repl_instance->is_64bit = GTM_IS_64BIT; repl_instance->jnlpool_semid = INVALID_SEMID; repl_instance->jnlpool_shmid = INVALID_SHMID; repl_instance->recvpool_semid = INVALID_SEMID; repl_instance->recvpool_shmid = INVALID_SHMID; /********* initialize "inst_info" structure member of "repl_inst_hdr" ***********/ /* machine_name was obtained from GETHOSTNAME above. It is an array of MAX_MCNAMELEN (256) bytes. The actual * machine name might be longer than can fit in the "created_nodename" field which is MAX_NODENAME_LEN (16) in size. * Take care to copy only as much as needed leaving one character for the null-termination. */ assert(MAX_NODENAME_LEN <= MAX_MCNAMELEN); /* '=' is valid since we have space to store MAX_NODENAME_LEN characters */ memcpy(repl_instance->inst_info.created_nodename, machine_name, MAX_NODENAME_LEN); /* if machine_name is less than MAX_NODENAME_LEN then set the last valid character of created_nodename array to '\0' which * is relied by repl_inst_dump_filehdr */ if (MAX_NODENAME_LEN > STRLEN(machine_name)) repl_instance->inst_info.created_nodename[MAX_NODENAME_LEN - 1] = '\0'; DBG_CHECK_CREATED_NODENAME(repl_instance->inst_info.created_nodename); memcpy(repl_instance->inst_info.this_instname, inst_name, inst_name_len); JNL_SHORT_TIME(repl_instance->inst_info.created_time); assert(process_id == getpid()); repl_instance->inst_info.creator_pid = process_id; /* repl_instance->lms_group_info should be initialized to NULL at this point. * That is the case already because of the memset above. So nothing more needed for now. */ repl_instance->jnl_seqno = 0; repl_instance->root_primary_cycle = 0; repl_instance->num_histinfo = 0; repl_instance->num_alloc_histinfo = 0; repl_instance->crash = FALSE; repl_instance->was_rootprimary = FALSE; repl_instance->is_supplementary = cli_present("SUPPLEMENTARY"); repl_instance->qdbrundown = (CLI_PRESENT == cli_present("QDBRUNDOWN")); for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) repl_instance->last_histinfo_num[idx] = INVALID_HISTINFO_NUM; /* strm_seqno[] and strm_group_info[] are already initialized to 0 as part of the memset above. Nothing more needed * except the 0th stream seqno. This needs to be set to 1 so the first local update done on a supplementary instance * correctly uses the stream seqno of 1. For non-zero stream #s, a UPDATERESYNC= startup of the receiver to be done * anyways from a supplementary root primary instance and so that will initialize the strm_seqno[] to a non-zero * value before any updates from that stream occur. */ if (repl_instance->is_supplementary) repl_instance->strm_seqno[0] = 1; /* Initialize 0th stream starting sequence number */ /************************** Initialize "gtmsrc_lcl" section ***************************/ memset(gtmsrc_lcl_array, 0, GTMSRC_LCL_SIZE); /************************** Write stuff to file on disk ***********************************/ in_repl_inst_create = TRUE; /* used by repl_inst_write to determine if O_CREAT needs to be specified in the open */ repl_inst_write(inst_fn, (off_t)0, (sm_uc_ptr_t)repl_instance, REPL_INST_HDR_SIZE + GTMSRC_LCL_SIZE); in_repl_inst_create = FALSE; } fis-gtm-V7.0-005/sr_unix/repl_inst_dump.c0000644000032200000250000015330514342376330017240 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2017 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_stdio.h" #include "gtm_string.h" #include /* for "offsetof" macro */ #include "cli.h" #include "util.h" #include "repl_instance.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" /* for JNL_WHOLE_FROM_SHORT_TIME */ #include "buddy_list.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "hashtab_mname.h" /* needed for muprec.h */ #include "muprec.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" #include "repl_inst_dump.h" #include "repl_log.h" /* for "repl_log" prototype */ LITDEF char state_array[][23] = { "DUMMY_STATE", "START", "WAITING_FOR_CONNECTION", "WAITING_FOR_RESTART", "SEARCHING_FOR_RESTART", "SENDING_JNLRECS", "WAITING_FOR_XON", "CHANGING_MODE" }; #define PREFIX_FILEHDR "HDR " #define PREFIX_SRCLCL "SLT #!2UL : " #define PREFIX_HISTINFO "HST #!7UL : " #define PREFIX_JNLPOOLCTL "CTL " #define PREFIX_SOURCELOCAL "SRC #!2UL : " #define FILEHDR_TITLE_STRING "File Header" #define SRCLCL_TITLE_STRING "Source Server Slots" #define HISTINFO_TITLE_STRING "History Records (histinfo structures)" #define JNLPOOLCTL_TITLE_STRING "Journal Pool Control Structure" #define SOURCELOCAL_TITLE_STRING "Source Server Structures" #define PRINT_BOOLEAN(printstr, value, index) \ { \ char *string; \ \ string = ((TRUE == (value)) ? "TRUE" : ((FALSE == (value)) ? "FALSE" : "UNKNOWN")); \ if ((FALSE == value) || (TRUE == value)) \ { \ if (-1 == (index)) \ util_out_print(printstr, TRUE, string); \ else \ util_out_print(printstr, TRUE, (index), string); \ } else \ { \ if (-1 == (index)) \ util_out_print(printstr " [0x!XL]", TRUE, string, (value)); \ else \ util_out_print(printstr " [0x!XL]", TRUE, (index), string, (value)); \ } \ } #define PRINT_TIME(printstr, value) \ { \ jnl_proc_time whole_time; \ int time_len; \ char time_str[LENGTH_OF_TIME + 1]; \ \ if (0 != value) \ { \ JNL_WHOLE_FROM_SHORT_TIME(whole_time, value); \ time_len = format_time(whole_time, time_str, SIZEOF(time_str), SHORT_TIME_FORMAT); \ } else \ { \ time_len = 1; \ time_str[0] = '0'; \ } \ util_out_print( printstr "!R20AD", TRUE, time_len, time_str); \ } #define PRINT_SEM_SHM_ID(printstr, value) \ { \ assert(INVALID_SEMID == INVALID_SHMID); \ if (INVALID_SEMID != value) \ util_out_print( printstr "!10UL [0x!XL]", TRUE, value, value); \ else \ util_out_print( printstr " INVALID", TRUE); \ } #define PRINT_OFFSET_HEADER if (detail_specified) { util_out_print("Offset Size", TRUE); } #define PRINT_OFFSET_PREFIX(offset, size) \ if (detail_specified) { util_out_print("0x!XL 0x!4XW ", FALSE, offset + section_offset, size); }; GBLREF uint4 section_offset; /* Used by PRINT_OFFSET_PREFIX macro in repl_inst_dump.c */ void repl_inst_dump_filehdr(repl_inst_hdr_ptr_t repl_instance) { char dststr[MAX_DIGITS_IN_INT], dstlen; char *string; int4 minorver, nodename_len, last_histinfo_num; int idx, strm_idx, offset; repl_inst_uuid *strm_group_info; seq_num strm_seqno; uchar_ptr_t nodename_ptr; util_out_print("", TRUE); PRINT_DASHES; util_out_print(FILEHDR_TITLE_STRING, TRUE); PRINT_DASHES; PRINT_OFFSET_HEADER; PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, label[0]), SIZEOF(repl_instance->label)); util_out_print( PREFIX_FILEHDR "Label (contains Major Version) !11AD", TRUE, GDS_REPL_INST_LABEL_SZ - 1, repl_instance->label); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, replinst_minorver), SIZEOF(repl_instance->replinst_minorver)); dstlen = 0; minorver = repl_instance->replinst_minorver; /* store minorver in a > 1-byte sized variable to avoid warning in I2A macro */ I2A(dststr, dstlen, minorver); assert(dstlen <= 3); util_out_print( PREFIX_FILEHDR "Minor Version !R3AD", TRUE, dstlen, dststr); /* Assert that the endianness of the instance file matches the endianness of the GT.M version * as otherwise we would have errored out long before reaching here. */ # ifdef BIGENDIAN assert(!repl_instance->is_little_endian); # else assert(repl_instance->is_little_endian); # endif PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, is_little_endian), SIZEOF(repl_instance->is_little_endian)); util_out_print( PREFIX_FILEHDR "Endian Format !6AZ", TRUE, ENDIANTHISJUSTIFY); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, is_64bit), SIZEOF(repl_instance->is_64bit)); util_out_print( PREFIX_FILEHDR "64-bit Format !5AZ", TRUE, repl_instance->is_64bit ? " TRUE" : "FALSE"); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, jnlpool_semid), SIZEOF(repl_instance->jnlpool_semid)); PRINT_SEM_SHM_ID( PREFIX_FILEHDR "Journal Pool Sem Id ", repl_instance->jnlpool_semid); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, jnlpool_shmid), SIZEOF(repl_instance->jnlpool_shmid)); PRINT_SEM_SHM_ID( PREFIX_FILEHDR "Journal Pool Shm Id ", repl_instance->jnlpool_shmid); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, recvpool_semid), SIZEOF(repl_instance->recvpool_semid)); PRINT_SEM_SHM_ID( PREFIX_FILEHDR "Receive Pool Sem Id ", repl_instance->recvpool_semid); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, recvpool_shmid), SIZEOF(repl_instance->recvpool_shmid)); PRINT_SEM_SHM_ID( PREFIX_FILEHDR "Receive Pool Shm Id ", repl_instance->recvpool_shmid); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, jnlpool_semid_ctime), SIZEOF(repl_instance->jnlpool_semid_ctime)); PRINT_TIME( PREFIX_FILEHDR "Journal Pool Sem Create Time ", repl_instance->jnlpool_semid_ctime); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, jnlpool_shmid_ctime), SIZEOF(repl_instance->jnlpool_shmid_ctime)); PRINT_TIME( PREFIX_FILEHDR "Journal Pool Shm Create Time ", repl_instance->jnlpool_shmid_ctime); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, recvpool_semid_ctime), SIZEOF(repl_instance->recvpool_semid_ctime)); PRINT_TIME( PREFIX_FILEHDR "Receive Pool Sem Create Time ", repl_instance->recvpool_semid_ctime); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, recvpool_shmid_ctime), SIZEOF(repl_instance->recvpool_shmid_ctime)); PRINT_TIME( PREFIX_FILEHDR "Receive Pool Shm Create Time ", repl_instance->recvpool_shmid_ctime); /**************** Dump the "inst_info" structure member ****************/ PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, inst_info) + offsetof(repl_inst_uuid, created_nodename[0]), SIZEOF(repl_instance->inst_info.created_nodename)); /* created_nodename can contain upto MAX_NODENAME_LEN characters. If it consumes the entire array, then * the last character will NOT be null terminated. Check and set the array length to be used for the call * to util_out_print */ nodename_ptr = repl_instance->inst_info.created_nodename; DBG_CHECK_CREATED_NODENAME(nodename_ptr); nodename_len = ('\0' == nodename_ptr[MAX_NODENAME_LEN - 1]) ? STRLEN((char *)nodename_ptr) : MAX_NODENAME_LEN; util_out_print( PREFIX_FILEHDR "Instance File Created Nodename !R16AD", TRUE, nodename_len, nodename_ptr); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, inst_info) + offsetof(repl_inst_uuid, this_instname[0]), SIZEOF(repl_instance->inst_info.this_instname)); util_out_print( PREFIX_FILEHDR "Instance Name !R15AD", TRUE, LEN_AND_STR((char *)repl_instance->inst_info.this_instname)); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, inst_info) + offsetof(repl_inst_uuid, created_time), SIZEOF(repl_instance->inst_info.created_time)); PRINT_TIME( PREFIX_FILEHDR "Instance File Create Time ", repl_instance->inst_info.created_time); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, inst_info) + offsetof(repl_inst_uuid, creator_pid), SIZEOF(repl_instance->inst_info.creator_pid)); util_out_print( PREFIX_FILEHDR "Instance File Creator Pid !10UL [0x!XL]", TRUE, repl_instance->inst_info.creator_pid, repl_instance->inst_info.creator_pid); /**************** Dump the "lms_group_info" structure member only if it is non-null ****************/ if (IS_REPL_INST_UUID_NON_NULL(repl_instance->lms_group_info)) { PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, lms_group_info) + offsetof(repl_inst_uuid, created_nodename[0]), SIZEOF(repl_instance->lms_group_info.created_nodename)); /* created_nodename can contain upto MAX_NODENAME_LEN characters. If it consumes the entire array, then * the last character will NOT be null terminated. Check and set the array length to be used for the call * to util_out_print */ nodename_ptr = repl_instance->lms_group_info.created_nodename; DBG_CHECK_CREATED_NODENAME(nodename_ptr); nodename_len = ('\0' == nodename_ptr[MAX_NODENAME_LEN - 1]) ? STRLEN((char *)nodename_ptr) : MAX_NODENAME_LEN; util_out_print( PREFIX_FILEHDR "LMS Group Created Nodename !R16AD", TRUE, nodename_len, nodename_ptr); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, lms_group_info) + offsetof(repl_inst_uuid, this_instname[0]), SIZEOF(repl_instance->lms_group_info.this_instname)); util_out_print( PREFIX_FILEHDR "LMS Group Instance Name !R15AD", TRUE, LEN_AND_STR((char *)repl_instance->lms_group_info.this_instname)); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, lms_group_info) + offsetof(repl_inst_uuid, creator_pid), SIZEOF(repl_instance->lms_group_info.creator_pid)); util_out_print( PREFIX_FILEHDR "LMS Group Creator Pid !10UL [0x!XL]", TRUE, repl_instance->lms_group_info.creator_pid, repl_instance->lms_group_info.creator_pid); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, lms_group_info) + offsetof(repl_inst_uuid, created_time), SIZEOF(repl_instance->lms_group_info.created_time)); PRINT_TIME( PREFIX_FILEHDR "LMS Group Create Time ", repl_instance->lms_group_info.created_time); } /**************** Dump the remaining members ****************/ PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, jnl_seqno), SIZEOF(repl_instance->jnl_seqno)); util_out_print( PREFIX_FILEHDR "Journal Sequence Number !20@UQ [0x!16@XQ]", TRUE, &repl_instance->jnl_seqno, &repl_instance->jnl_seqno); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, root_primary_cycle), SIZEOF(repl_instance->root_primary_cycle)); util_out_print( PREFIX_FILEHDR "Root Primary Cycle !10UL [0x!XL]", TRUE, repl_instance->root_primary_cycle, repl_instance->root_primary_cycle); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, num_histinfo), SIZEOF(repl_instance->num_histinfo)); util_out_print( PREFIX_FILEHDR "Number of used history records !10UL [0x!XL]", TRUE, repl_instance->num_histinfo, repl_instance->num_histinfo); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, num_alloc_histinfo), SIZEOF(repl_instance->num_alloc_histinfo)); util_out_print( PREFIX_FILEHDR "Allocated history records !10UL [0x!XL]", TRUE, repl_instance->num_alloc_histinfo, repl_instance->num_alloc_histinfo); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, crash), SIZEOF(repl_instance->crash)); PRINT_BOOLEAN(PREFIX_FILEHDR "Crash !R10AZ", repl_instance->crash, -1); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, was_rootprimary), SIZEOF(repl_instance->was_rootprimary)); PRINT_BOOLEAN(PREFIX_FILEHDR "Root Primary !R10AZ", repl_instance->was_rootprimary, -1); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, is_supplementary), SIZEOF(repl_instance->is_supplementary)); PRINT_BOOLEAN(PREFIX_FILEHDR "Supplementary Instance !R10AZ", repl_instance->is_supplementary, -1); /**************** Dump the supplementary information ONLY if it is a supplementary instance ****************/ if (repl_instance->is_supplementary) { for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) { /* Dump information for each stream as long as it contains valid data. */ assert(SIZEOF(last_histinfo_num) == SIZEOF(repl_instance->last_histinfo_num[0])); last_histinfo_num = repl_instance->last_histinfo_num[idx]; if (INVALID_HISTINFO_NUM != last_histinfo_num) { PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, last_histinfo_num[0]) + (idx * SIZEOF(last_histinfo_num)), SIZEOF(last_histinfo_num)); util_out_print( PREFIX_FILEHDR "STRM !2UL: Last history record number !10UL [0x!XL]", TRUE, idx, last_histinfo_num, last_histinfo_num); } assert(SIZEOF(strm_seqno) == SIZEOF(repl_instance->strm_seqno[0])); strm_seqno = repl_instance->strm_seqno[idx]; if (strm_seqno) { PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, strm_seqno[0]) + (idx * SIZEOF(strm_seqno)), SIZEOF(strm_seqno)); util_out_print( PREFIX_FILEHDR "STRM !2UL: Journal Sequence Number !20@UQ [0x!16@XQ]", TRUE, idx, &strm_seqno, &strm_seqno); } assert(ARRAYSIZE(repl_instance->strm_group_info) == (MAX_SUPPL_STRMS - 1)); /**************** Dump the "strm_group_info" structure member only if it is non-null ****************/ if (idx) { strm_idx = idx - 1; strm_group_info = &repl_instance->strm_group_info[strm_idx]; assert(SIZEOF(*strm_group_info) == SIZEOF(repl_instance->strm_group_info[0])); if (IS_REPL_INST_UUID_NON_NULL(*strm_group_info)) { offset = offsetof(repl_inst_hdr, strm_group_info[0]) + (strm_idx * SIZEOF(*strm_group_info)); PRINT_OFFSET_PREFIX(offset + offsetof(repl_inst_uuid, created_nodename[0]), SIZEOF(strm_group_info->created_nodename)); /* created_nodename can contain upto MAX_NODENAME_LEN characters. If it consumes the entire * array, then the last character will NOT be null terminated. Check and set the array * length to be used for the call to util_out_print. */ nodename_ptr = strm_group_info->created_nodename; DBG_CHECK_CREATED_NODENAME(nodename_ptr); nodename_len = ('\0' == nodename_ptr[MAX_NODENAME_LEN - 1]) ? STRLEN((char *)nodename_ptr) : MAX_NODENAME_LEN; util_out_print( PREFIX_FILEHDR "STRM !2UL: Group Created Nodename !R16AD", TRUE, idx, nodename_len, nodename_ptr); PRINT_OFFSET_PREFIX(offset + offsetof(repl_inst_uuid, this_instname[0]), SIZEOF(strm_group_info->this_instname)); util_out_print( PREFIX_FILEHDR "STRM !2UL: Group Instance Name !R16AD", TRUE, idx, LEN_AND_STR((char *)strm_group_info->this_instname)); PRINT_OFFSET_PREFIX(offset + offsetof(repl_inst_uuid, creator_pid), SIZEOF(strm_group_info->creator_pid)); util_out_print( PREFIX_FILEHDR "STRM !2UL: Group Creator Pid " "!10UL [0x!XL]", TRUE, idx, strm_group_info->creator_pid, strm_group_info->creator_pid); PRINT_OFFSET_PREFIX(offset + offsetof(repl_inst_uuid, created_time), SIZEOF(strm_group_info->created_time)); util_out_print( PREFIX_FILEHDR "STRM !2UL: ", FALSE, idx); PRINT_TIME( "Group Create Time ", strm_group_info->created_time); } } } } PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, file_corrupt), SIZEOF(repl_instance->file_corrupt)); PRINT_BOOLEAN(PREFIX_FILEHDR "Corrupt !R10AZ", repl_instance->file_corrupt, -1); PRINT_OFFSET_PREFIX(offsetof(repl_inst_hdr, qdbrundown), SIZEOF(repl_instance->qdbrundown)); PRINT_BOOLEAN(PREFIX_FILEHDR "Quick database rundown is active !R10AZ", repl_instance->qdbrundown, -1); } void repl_inst_dump_gtmsrclcl(gtmsrc_lcl_ptr_t gtmsrclcl_ptr) { int idx; boolean_t first_time = TRUE; for (idx = 0; idx < NUM_GTMSRC_LCL; idx++, gtmsrclcl_ptr++) { if (('\0' == gtmsrclcl_ptr->secondary_instname[0]) && (0 == gtmsrclcl_ptr->resync_seqno) && (0 == gtmsrclcl_ptr->connect_jnl_seqno)) continue; if (first_time) { util_out_print("", TRUE); first_time = FALSE; PRINT_DASHES; util_out_print(SRCLCL_TITLE_STRING, TRUE); PRINT_DASHES; } else PRINT_DASHES; PRINT_OFFSET_HEADER; PRINT_OFFSET_PREFIX(offsetof(gtmsrc_lcl, secondary_instname[0]), SIZEOF(gtmsrclcl_ptr->secondary_instname)); util_out_print( PREFIX_SRCLCL "Secondary Instance Name !R15AD", TRUE, idx, LEN_AND_STR((char *)gtmsrclcl_ptr->secondary_instname)); PRINT_OFFSET_PREFIX(offsetof(gtmsrc_lcl, resync_seqno), SIZEOF(gtmsrclcl_ptr->resync_seqno)); util_out_print( PREFIX_SRCLCL "Resync Sequence Number !20@UQ [0x!16@XQ]", TRUE, idx, >msrclcl_ptr->resync_seqno, >msrclcl_ptr->resync_seqno); PRINT_OFFSET_PREFIX(offsetof(gtmsrc_lcl, connect_jnl_seqno), SIZEOF(gtmsrclcl_ptr->connect_jnl_seqno)); util_out_print( PREFIX_SRCLCL "Connect Sequence Number !20@UQ [0x!16@XQ]", TRUE, idx, >msrclcl_ptr->connect_jnl_seqno, >msrclcl_ptr->connect_jnl_seqno); section_offset += SIZEOF(gtmsrc_lcl); } } void repl_inst_dump_history_records(char *inst_fn, int4 num_histinfo) { int4 idx, idx2, last_histnum; off_t offset; repl_histinfo curhistinfo; jnl_proc_time whole_time; int time_len; char time_str[LENGTH_OF_TIME + 1]; boolean_t first_time = TRUE; unsigned char *created_nodename; for (idx = 0; idx < num_histinfo; idx++, offset += SIZEOF(repl_histinfo)) { if (first_time) { util_out_print("", TRUE); first_time = FALSE; PRINT_DASHES; util_out_print(HISTINFO_TITLE_STRING, TRUE); PRINT_DASHES; offset = REPL_INST_HISTINFO_START; } else PRINT_DASHES; repl_inst_read(inst_fn, (off_t)offset, (sm_uc_ptr_t)&curhistinfo, SIZEOF(repl_histinfo)); PRINT_OFFSET_HEADER; PRINT_OFFSET_PREFIX(offsetof(repl_histinfo, root_primary_instname[0]), SIZEOF(curhistinfo.root_primary_instname)); util_out_print(PREFIX_HISTINFO "Root Primary Instance Name !R15AD", TRUE, idx, LEN_AND_STR((char *)curhistinfo.root_primary_instname)); PRINT_OFFSET_PREFIX(offsetof(repl_histinfo, start_seqno), SIZEOF(curhistinfo.start_seqno)); util_out_print(PREFIX_HISTINFO "Start Sequence Number !20@UQ [0x!16@XQ]", TRUE, idx, &curhistinfo.start_seqno, &curhistinfo.start_seqno); PRINT_OFFSET_PREFIX(offsetof(repl_histinfo, strm_seqno), SIZEOF(curhistinfo.strm_seqno)); util_out_print(PREFIX_HISTINFO "Stream Sequence Number !20@UQ [0x!16@XQ]", TRUE, idx, &curhistinfo.strm_seqno, &curhistinfo.strm_seqno); PRINT_OFFSET_PREFIX(offsetof(repl_histinfo, root_primary_cycle), SIZEOF(curhistinfo.root_primary_cycle)); util_out_print(PREFIX_HISTINFO "Root Primary Cycle !10UL [0x!XL]", TRUE, idx, curhistinfo.root_primary_cycle, curhistinfo.root_primary_cycle); PRINT_OFFSET_PREFIX(offsetof(repl_histinfo, creator_pid), SIZEOF(curhistinfo.creator_pid)); util_out_print(PREFIX_HISTINFO "Creator Process ID !10UL [0x!XL]", TRUE, idx, curhistinfo.creator_pid, curhistinfo.creator_pid); PRINT_OFFSET_PREFIX(offsetof(repl_histinfo, created_time), SIZEOF(curhistinfo.created_time)); JNL_WHOLE_FROM_SHORT_TIME(whole_time, curhistinfo.created_time); time_len = format_time(whole_time, time_str, SIZEOF(time_str), SHORT_TIME_FORMAT); util_out_print(PREFIX_HISTINFO "Creation Time "TIME_DISPLAY_FAO, TRUE, idx, time_len, time_str); PRINT_OFFSET_PREFIX(offsetof(repl_histinfo, histinfo_num), SIZEOF(curhistinfo.histinfo_num)); util_out_print(PREFIX_HISTINFO "History Number !10UL [0x!XL]", TRUE, idx, curhistinfo.histinfo_num, curhistinfo.histinfo_num); assert(curhistinfo.histinfo_num == idx); PRINT_OFFSET_PREFIX(offsetof(repl_histinfo, prev_histinfo_num), SIZEOF(curhistinfo.prev_histinfo_num)); util_out_print(PREFIX_HISTINFO "Previous History Number !10SL [0x!XL]", TRUE, idx, curhistinfo.prev_histinfo_num, curhistinfo.prev_histinfo_num); assert(curhistinfo.histinfo_num == idx); PRINT_OFFSET_PREFIX(offsetof(repl_histinfo, strm_index), SIZEOF(curhistinfo.strm_index)); util_out_print(PREFIX_HISTINFO "Stream # !2UL [0x!XL]", TRUE, idx, curhistinfo.strm_index, curhistinfo.strm_index); PRINT_OFFSET_PREFIX(offsetof(repl_histinfo, history_type), SIZEOF(curhistinfo.history_type)); util_out_print(PREFIX_HISTINFO "History record type !2UL [0x!XL]", TRUE, idx, curhistinfo.history_type, curhistinfo.history_type); /* Assert that lms_group is filled in for non-zero stream #s and not for zero stream #s */ assert(!curhistinfo.strm_index && IS_REPL_INST_UUID_NULL(curhistinfo.lms_group) || curhistinfo.strm_index && IS_REPL_INST_UUID_NON_NULL(curhistinfo.lms_group)); /* Assert that UPDATERESYNC type of history record is possible only in non-zero stream #s */ assert((HISTINFO_TYPE_UPDRESYNC != curhistinfo.history_type) || curhistinfo.strm_index); /* Do not print "lms_group" info for all history records as it will clutter the output. * Print it only in case of HISTINFO_TYPE_UPDRESYNC as that is the only case when it changes. * between history records belonging to the same stream #. */ if (HISTINFO_TYPE_UPDRESYNC == curhistinfo.history_type) { DBG_CHECK_CREATED_NODENAME(curhistinfo.lms_group.created_nodename); PRINT_OFFSET_PREFIX(offsetof(repl_histinfo, lms_group) + offsetof(repl_inst_uuid, created_nodename[0]), SIZEOF(curhistinfo.lms_group.created_nodename)); if (curhistinfo.lms_group.created_nodename[MAX_NODENAME_LEN -1]) { assert(16 == MAX_NODENAME_LEN); /* because of the !R16AD hardcoding below */ util_out_print(PREFIX_HISTINFO "LMS Group Created Nodename !R16AD", TRUE, idx, MAX_NODENAME_LEN, (char *)curhistinfo.lms_group.created_nodename); } else util_out_print(PREFIX_HISTINFO "LMS Group Created Nodename !R15AD", TRUE, idx, LEN_AND_STR((char *)curhistinfo.lms_group.created_nodename)); PRINT_OFFSET_PREFIX(offsetof(repl_histinfo, lms_group) + offsetof(repl_inst_uuid, this_instname[0]), SIZEOF(curhistinfo.lms_group.this_instname)); util_out_print(PREFIX_HISTINFO "LMS Group Instance Name !R15AD", TRUE, idx, LEN_AND_STR((char *)curhistinfo.lms_group.this_instname)); PRINT_OFFSET_PREFIX(offsetof(repl_histinfo, lms_group) + offsetof(repl_inst_uuid, created_time), SIZEOF(curhistinfo.lms_group.created_time)); JNL_WHOLE_FROM_SHORT_TIME(whole_time, curhistinfo.lms_group.created_time); time_len = format_time(whole_time, time_str, SIZEOF(time_str), SHORT_TIME_FORMAT); util_out_print(PREFIX_HISTINFO "LMS Group Creation Time"TIME_DISPLAY_FAO, TRUE, idx, time_len, time_str); PRINT_OFFSET_PREFIX(offsetof(repl_histinfo, lms_group) + offsetof(repl_inst_uuid, creator_pid), SIZEOF(curhistinfo.lms_group.creator_pid)); util_out_print(PREFIX_HISTINFO "LMS Group Creator PID !10UL [0x!XL]", TRUE, idx, curhistinfo.lms_group.creator_pid, curhistinfo.lms_group.creator_pid); } for (idx2 = 0; idx2 < MAX_SUPPL_STRMS; idx2++) { last_histnum = curhistinfo.last_histinfo_num[idx2]; assert(SIZEOF(last_histnum) == SIZEOF(curhistinfo.last_histinfo_num[idx2])); if (INVALID_HISTINFO_NUM != last_histnum) { PRINT_OFFSET_PREFIX(offsetof(repl_histinfo, last_histinfo_num[0]) + (idx2 * SIZEOF(last_histnum)), SIZEOF(last_histnum)); util_out_print(PREFIX_HISTINFO "Stream !2UL: Last History Number !10UL [0x!XL]", TRUE, idx, idx2, last_histnum, last_histnum); } } section_offset += SIZEOF(repl_histinfo); } } void repl_dump_histinfo(FILE *log_fp, boolean_t stamptime, boolean_t flush, char *start_text, repl_histinfo *cur_histinfo) { unsigned char nodename_buff[MAX_NODENAME_LEN + 1], *created_nodename; repl_log(log_fp, stamptime, flush, "%s : Start Seqno = %llu [0x%llx] : Stream Seqno = %llu [0x%llx] : " "Root Primary = [%s] : Cycle = [%d] : Creator pid = %d : Created time = %d [0x%x] : History number = %d : " "Prev History number = %d : Stream # = %d : History type = %d\n", start_text, cur_histinfo->start_seqno, cur_histinfo->start_seqno, cur_histinfo->strm_seqno, cur_histinfo->strm_seqno, cur_histinfo->root_primary_instname, cur_histinfo->root_primary_cycle, cur_histinfo->creator_pid, cur_histinfo->created_time, cur_histinfo->created_time, cur_histinfo->histinfo_num, cur_histinfo->prev_histinfo_num, cur_histinfo->strm_index, cur_histinfo->history_type); /* Assert that lms_group is filled in for non-zero stream #s and not for zero stream #s */ assert(!cur_histinfo->strm_index && IS_REPL_INST_UUID_NULL(cur_histinfo->lms_group) || cur_histinfo->strm_index && IS_REPL_INST_UUID_NON_NULL(cur_histinfo->lms_group)); /* Assert that UPDATERESYNC type of history record is possible only in non-zero stream #s */ assert((HISTINFO_TYPE_UPDRESYNC != cur_histinfo->history_type) || cur_histinfo->strm_index); /* Do not print "lms_group" info for all history records as it will clutter the output. * Print it only in case of HISTINFO_TYPE_UPDRESYNC as that is the only case when it changes * between history records belonging to the same stream #. */ if (HISTINFO_TYPE_UPDRESYNC == cur_histinfo->history_type) { /* history record that also contains NEW lms group info (due to -UPDATERESYNC) */ DBG_CHECK_CREATED_NODENAME(cur_histinfo->lms_group.created_nodename); assert(MAX_NODENAME_LEN == SIZEOF(cur_histinfo->lms_group.created_nodename)); if ('\0' == cur_histinfo->lms_group.created_nodename[MAX_NODENAME_LEN - 1]) created_nodename = &cur_histinfo->lms_group.created_nodename[0]; else { created_nodename = &nodename_buff[0]; memcpy(created_nodename, cur_histinfo->lms_group.created_nodename, MAX_NODENAME_LEN); created_nodename[MAX_NODENAME_LEN] = '\0'; } repl_log(log_fp, stamptime, flush, "History has non-zero Supplementary Stream LMS Group Content : " "LMS Group Nodename = [%s] : LMS Group Instance Name = [%s] : " "Creator pid = %d : Created time = %d [0x%x]\n", created_nodename, cur_histinfo->lms_group.this_instname, cur_histinfo->lms_group.creator_pid, cur_histinfo->lms_group.created_time, cur_histinfo->lms_group.created_time); } /* Do not print "last_histinfo_num" output as that is not of concern to the user and only clutters the output */ } void repl_inst_dump_jnlpoolctl(jnlpool_ctl_ptr_t jnlpool_ctl) { char *string; repl_conn_info_t *this_side; int idx; seq_num strm_seqno; util_out_print("", TRUE); PRINT_DASHES; util_out_print(JNLPOOLCTL_TITLE_STRING, TRUE); PRINT_DASHES; PRINT_OFFSET_HEADER; assert(0 == offsetof(jnlpool_ctl_struct, jnlpool_id)); /* The following offsetof calculations depend on this */ PRINT_OFFSET_PREFIX(offsetof(replpool_identifier, label[0]), SIZEOF(jnlpool_ctl->jnlpool_id.label)); util_out_print( PREFIX_JNLPOOLCTL "Label !11AD", TRUE, GDS_LABEL_SZ - 1, jnlpool_ctl->jnlpool_id.label); PRINT_OFFSET_PREFIX(offsetof(replpool_identifier, pool_type), SIZEOF(jnlpool_ctl->jnlpool_id.pool_type)); string = (JNLPOOL_SEGMENT == jnlpool_ctl->jnlpool_id.pool_type) ? "JNLPOOL" : ((RECVPOOL_SEGMENT == jnlpool_ctl->jnlpool_id.pool_type) ? "RECVPOOL" : "UNKNOWN"); if (MEMCMP_LIT(string, "UNKNOWN")) util_out_print( PREFIX_JNLPOOLCTL "Type !R22AZ", TRUE, string); else { util_out_print( PREFIX_JNLPOOLCTL "Type !R22AZ [0x!XL]", TRUE, string, jnlpool_ctl->jnlpool_id.pool_type); } PRINT_OFFSET_PREFIX(offsetof(replpool_identifier, now_running[0]), SIZEOF(jnlpool_ctl->jnlpool_id.now_running)); util_out_print( PREFIX_JNLPOOLCTL "GT.M Version !R30AZ", TRUE, jnlpool_ctl->jnlpool_id.now_running); PRINT_OFFSET_PREFIX(offsetof(replpool_identifier, instfilename[0]), SIZEOF(jnlpool_ctl->jnlpool_id.instfilename)); if (22 >= strlen(jnlpool_ctl->jnlpool_id.instfilename)) { util_out_print( PREFIX_JNLPOOLCTL "Instance file name !R22AZ", TRUE, jnlpool_ctl->jnlpool_id.instfilename); } else util_out_print( PREFIX_JNLPOOLCTL "Instance file name !AZ", TRUE, jnlpool_ctl->jnlpool_id.instfilename); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, start_jnl_seqno), SIZEOF(jnlpool_ctl->start_jnl_seqno)); util_out_print( PREFIX_JNLPOOLCTL "Start Journal Seqno !20@UQ [0x!16@XQ]", TRUE, &jnlpool_ctl->start_jnl_seqno, &jnlpool_ctl->start_jnl_seqno); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, jnl_seqno), SIZEOF(jnlpool_ctl->jnl_seqno)); util_out_print( PREFIX_JNLPOOLCTL "Journal Seqno !20@UQ [0x!16@XQ]", TRUE, &jnlpool_ctl->jnl_seqno, &jnlpool_ctl->jnl_seqno); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, last_histinfo_seqno), SIZEOF(jnlpool_ctl->last_histinfo_seqno)); util_out_print( PREFIX_JNLPOOLCTL "Last histinfo Seqno !20@UQ [0x!16@XQ]", TRUE, &jnlpool_ctl->last_histinfo_seqno, &jnlpool_ctl->last_histinfo_seqno); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, jnldata_base_off), SIZEOF(jnlpool_ctl->jnldata_base_off)); util_out_print( PREFIX_JNLPOOLCTL "Journal Data Base Offset !10UL [0x!XL]", TRUE, jnlpool_ctl->jnldata_base_off, jnlpool_ctl->jnldata_base_off); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, jnlpool_size), SIZEOF(jnlpool_ctl->jnlpool_size)); util_out_print( PREFIX_JNLPOOLCTL "Journal Pool Size (in bytes) !10UL [0x!XL]", TRUE, jnlpool_ctl->jnlpool_size, jnlpool_ctl->jnlpool_size); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, rsrv_write_addr), SIZEOF(jnlpool_ctl->rsrv_write_addr)); util_out_print( PREFIX_JNLPOOLCTL "Reserved Write Offset !20@UQ [0x!16@XQ]", TRUE, &jnlpool_ctl->rsrv_write_addr, &jnlpool_ctl->rsrv_write_addr); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, write_addr), SIZEOF(jnlpool_ctl->write_addr)); util_out_print( PREFIX_JNLPOOLCTL "Absolute Write Offset !20@UQ [0x!16@XQ]", TRUE, &jnlpool_ctl->write_addr, &jnlpool_ctl->write_addr); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, upd_disabled), SIZEOF(jnlpool_ctl->upd_disabled)); PRINT_BOOLEAN(PREFIX_JNLPOOLCTL "Updates Disabled !R22AZ", jnlpool_ctl->upd_disabled, -1); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, lastwrite_len), SIZEOF(jnlpool_ctl->lastwrite_len)); util_out_print( PREFIX_JNLPOOLCTL "Last Write Length (in bytes) !10UL [0x!XL]", TRUE, jnlpool_ctl->lastwrite_len, jnlpool_ctl->lastwrite_len); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, send_losttn_complete), SIZEOF(jnlpool_ctl->send_losttn_complete)); PRINT_BOOLEAN(PREFIX_JNLPOOLCTL "Send LostTN Complete !R22AZ", jnlpool_ctl->send_losttn_complete, -1); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, primary_instname[0]), SIZEOF(jnlpool_ctl->primary_instname)); util_out_print( PREFIX_JNLPOOLCTL "Primary Instance Name !R20AZ", TRUE, jnlpool_ctl->primary_instname); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, max_zqgblmod_seqno), SIZEOF(jnlpool_ctl->max_zqgblmod_seqno)); util_out_print( PREFIX_JNLPOOLCTL "Zqgblmod Seqno !20@UQ [0x!16@XQ]", TRUE, &jnlpool_ctl->max_zqgblmod_seqno, &jnlpool_ctl->max_zqgblmod_seqno); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, prev_jnlseqno_time), SIZEOF(jnlpool_ctl->prev_jnlseqno_time)); PRINT_TIME( PREFIX_JNLPOOLCTL "Prev JnlSeqno Time ", jnlpool_ctl->prev_jnlseqno_time); /**************** Dump the "this_side" structure member ****************/ this_side = &jnlpool_ctl->this_side; PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, this_side) + offsetof(repl_conn_info_t, proto_ver), SIZEOF(this_side->proto_ver)); util_out_print( PREFIX_JNLPOOLCTL "Protocol Version !3UL [0x!XL]", TRUE, this_side->proto_ver, this_side->proto_ver); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, this_side) + offsetof(repl_conn_info_t, jnl_ver), SIZEOF(this_side->jnl_ver)); util_out_print( PREFIX_JNLPOOLCTL "Journal Version !3UL [0x!XL]", TRUE, this_side->jnl_ver, this_side->jnl_ver); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, this_side) + offsetof(repl_conn_info_t, is_std_null_coll), SIZEOF(this_side->is_std_null_coll)); PRINT_BOOLEAN(PREFIX_JNLPOOLCTL "Standard Null Collation !R22AZ", this_side->is_std_null_coll, -1); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, this_side) + offsetof(repl_conn_info_t, trigger_supported), SIZEOF(this_side->trigger_supported)); PRINT_BOOLEAN(PREFIX_JNLPOOLCTL "Trigger Supported !R22AZ", this_side->trigger_supported, -1); /* The following 3 members of "this_side" don't make sense as the structure reflects properties of this instance whereas * these 3 members require connection with the remote side in order to make sense. So skip dumping them. * this_side->cross_endian * this_side->endianness_known * this_side->null_subs_xform */ PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, this_side) + offsetof(repl_conn_info_t, is_supplementary), SIZEOF(this_side->is_supplementary)); PRINT_BOOLEAN(PREFIX_JNLPOOLCTL "Supplementary Instance !R22AZ", this_side->is_supplementary, -1); /**************** Dump the "strm_seqno" structure member if this is a supplementary instance ****************/ if (this_side->is_supplementary) { assert(SIZEOF(strm_seqno) == SIZEOF(jnlpool_ctl->strm_seqno[0])); assert(MAX_SUPPL_STRMS == ARRAYSIZE(jnlpool_ctl->strm_seqno)); for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) { strm_seqno = jnlpool_ctl->strm_seqno[idx]; if (strm_seqno) { PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, strm_seqno[0]) + idx * SIZEOF(strm_seqno), SIZEOF(strm_seqno)); util_out_print( PREFIX_JNLPOOLCTL "Stream !2UL: Journal Seqno !20@UQ [0x!16@XQ]", TRUE, idx, &strm_seqno, &strm_seqno); } } } PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, onln_rlbk_pid), SIZEOF(jnlpool_ctl->onln_rlbk_pid)); util_out_print( PREFIX_JNLPOOLCTL "Online Rollback PID !20UL [0x!XL]", TRUE, jnlpool_ctl->onln_rlbk_pid, jnlpool_ctl->onln_rlbk_pid); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, onln_rlbk_cycle), SIZEOF(jnlpool_ctl->onln_rlbk_cycle)); util_out_print( PREFIX_JNLPOOLCTL "Online Rollback Cycle !20UL [0x!XL]", TRUE, jnlpool_ctl->onln_rlbk_cycle, jnlpool_ctl->onln_rlbk_cycle); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, freeze), SIZEOF(jnlpool_ctl->freeze)); PRINT_BOOLEAN( PREFIX_JNLPOOLCTL "Freeze !R10AZ", jnlpool_ctl->freeze, -1); if (jnlpool_ctl->freeze) { PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, freeze_comment[0]), SIZEOF(jnlpool_ctl->freeze_comment)); if (STRLEN(jnlpool_ctl->freeze_comment) <= 38) util_out_print( PREFIX_JNLPOOLCTL "Freeze Comment !R38AZ", TRUE, jnlpool_ctl->freeze_comment); else util_out_print( PREFIX_JNLPOOLCTL "Freeze Comment: !AZ", TRUE, jnlpool_ctl->freeze_comment); } PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, instfreeze_environ_inited), SIZEOF(jnlpool_ctl->instfreeze_environ_inited)); PRINT_BOOLEAN( PREFIX_JNLPOOLCTL "Custom Errors Loaded !R10AZ", jnlpool_ctl->instfreeze_environ_inited, -1); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, ftok_counter_halted), SIZEOF(jnlpool_ctl->ftok_counter_halted)); PRINT_BOOLEAN( PREFIX_JNLPOOLCTL "FTOK Counter Halted !R10AZ", jnlpool_ctl->ftok_counter_halted, -1); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, phase2_commit_index1), SIZEOF(jnlpool_ctl->phase2_commit_index1)); util_out_print( PREFIX_JNLPOOLCTL "Phase2 Commit Index1 !10UL [0x!XL]", TRUE, jnlpool_ctl->phase2_commit_index1, jnlpool_ctl->phase2_commit_index1); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, phase2_commit_index2), SIZEOF(jnlpool_ctl->phase2_commit_index2)); util_out_print( PREFIX_JNLPOOLCTL "Phase2 Commit Index2 !10UL [0x!XL]", TRUE, jnlpool_ctl->phase2_commit_index2, jnlpool_ctl->phase2_commit_index2); PRINT_OFFSET_PREFIX(0, 0); util_out_print( PREFIX_JNLPOOLCTL "Phase2 Commit IndexMax !10UL [0x!XL]", TRUE, JPL_PHASE2_COMMIT_ARRAY_SIZE, JPL_PHASE2_COMMIT_ARRAY_SIZE); PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, phase2_commit_latch), SIZEOF(jnlpool_ctl->phase2_commit_latch)); util_out_print( PREFIX_JNLPOOLCTL "Phase2 Commit Latch Pid !10UL [0x!XL]", TRUE, jnlpool_ctl->phase2_commit_latch.u.parts.latch_pid, jnlpool_ctl->phase2_commit_latch.u.parts.latch_pid); # define TAB_JPL_TRC_REC(A,B) \ { \ PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, B.cntr), SIZEOF(jnlpool_ctl->B.cntr)); \ util_out_print( PREFIX_JNLPOOLCTL A " Cntr !10UL [0x!XL]", TRUE, \ jnlpool_ctl->B.cntr, jnlpool_ctl->B.cntr); \ PRINT_OFFSET_PREFIX(offsetof(jnlpool_ctl_struct, B.seqno), SIZEOF(jnlpool_ctl->B.seqno)); \ util_out_print( PREFIX_JNLPOOLCTL A " Seqno !10UL [0x!XL]", TRUE, \ jnlpool_ctl->B.seqno, jnlpool_ctl->B.seqno); \ } # include "tab_jpl_trc_rec.h" # undef TAB_JPL_TRC_REC } void repl_inst_dump_gtmsourcelocal(gtmsource_local_ptr_t gtmsourcelocal_ptr) { int idx, idx2; char *string; boolean_t first_time = TRUE; repl_conn_info_t *remote_side; int errcode; char secondary_addr[SA_MAXLEN + 1]; for (idx = 0; idx < NUM_GTMSRC_LCL; idx++, gtmsourcelocal_ptr++) { if (('\0' == gtmsourcelocal_ptr->secondary_instname[0]) && (0 == gtmsourcelocal_ptr->read_jnl_seqno) && (0 == gtmsourcelocal_ptr->connect_jnl_seqno)) continue; if (first_time) { util_out_print("", TRUE); first_time = FALSE; PRINT_DASHES; util_out_print(SOURCELOCAL_TITLE_STRING, TRUE); PRINT_DASHES; } else PRINT_DASHES; PRINT_OFFSET_HEADER; PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, secondary_instname[0]), SIZEOF(gtmsourcelocal_ptr->secondary_instname)); util_out_print( PREFIX_SOURCELOCAL "Secondary Instance Name !R16AZ", TRUE, idx, (char *)gtmsourcelocal_ptr->secondary_instname); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, gtmsource_pid), SIZEOF(gtmsourcelocal_ptr->gtmsource_pid)); util_out_print( PREFIX_SOURCELOCAL "Source Server Pid !10UL", TRUE, idx, gtmsourcelocal_ptr->gtmsource_pid); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, mode), SIZEOF(gtmsourcelocal_ptr->mode)); if (GTMSOURCE_MODE_ACTIVE == gtmsourcelocal_ptr->mode) string = "ACTIVE"; else if (GTMSOURCE_MODE_PASSIVE == gtmsourcelocal_ptr->mode) string = "PASSIVE"; else if (GTMSOURCE_MODE_ACTIVE_REQUESTED == gtmsourcelocal_ptr->mode) string = "ACTIVE REQUESTED"; else if (GTMSOURCE_MODE_PASSIVE_REQUESTED == gtmsourcelocal_ptr->mode) string = "PASSIVE REQUESTED"; else string = "UNKNOWN"; if (MEMCMP_LIT(string, "UNKNOWN")) util_out_print( PREFIX_SOURCELOCAL "Source Server Mode !R22AZ", TRUE, idx, string); else { util_out_print( PREFIX_SOURCELOCAL "Source Server Mode !R22AZ [0x!XL]", TRUE, idx, string, gtmsourcelocal_ptr->mode); } PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, gtmsource_state), SIZEOF(gtmsourcelocal_ptr->gtmsource_state)); string = ((0 <= gtmsourcelocal_ptr->gtmsource_state) && (GTMSOURCE_NUM_STATES > gtmsourcelocal_ptr->gtmsource_state)) ? (char *)state_array[gtmsourcelocal_ptr->gtmsource_state] : "UNKNOWN"; if (MEMCMP_LIT(string, "UNKNOWN")) util_out_print( PREFIX_SOURCELOCAL "Processing State !R22AZ", TRUE, idx, string); else { util_out_print( PREFIX_SOURCELOCAL "Processing State !R22AZ [0x!XL]", TRUE, idx, string, gtmsourcelocal_ptr->gtmsource_state); } PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, gtmsrc_lcl_array_index), SIZEOF(gtmsourcelocal_ptr->gtmsrc_lcl_array_index)); util_out_print( PREFIX_SOURCELOCAL "Slot Index !15UL", TRUE, idx, gtmsourcelocal_ptr->gtmsrc_lcl_array_index); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, repl_zlib_cmp_level), SIZEOF(gtmsourcelocal_ptr->repl_zlib_cmp_level)); util_out_print( PREFIX_SOURCELOCAL "Journal record Compression Level !15UL", TRUE, idx, gtmsourcelocal_ptr->repl_zlib_cmp_level); /**************** Dump the "remote_side" structure member ****************/ remote_side = >msourcelocal_ptr->remote_side; PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, remote_side) + offsetof(repl_conn_info_t, proto_ver), SIZEOF(remote_side->proto_ver)); util_out_print( PREFIX_SOURCELOCAL "Remote Protocol Version !3SL [0x!XL]", TRUE, idx, remote_side->proto_ver, remote_side->proto_ver); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, remote_side) + offsetof(repl_conn_info_t, jnl_ver), SIZEOF(remote_side->jnl_ver)); util_out_print( PREFIX_SOURCELOCAL "Remote Journal Version !3UL [0x!XL]", TRUE, idx, remote_side->jnl_ver, remote_side->jnl_ver); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, remote_side) + offsetof(repl_conn_info_t, is_std_null_coll), SIZEOF(remote_side->is_std_null_coll)); PRINT_BOOLEAN(PREFIX_SOURCELOCAL "Remote has Standard Null Collation !R12AZ", remote_side->is_std_null_coll, idx); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, remote_side) + offsetof(repl_conn_info_t, trigger_supported), SIZEOF(remote_side->trigger_supported)); PRINT_BOOLEAN(PREFIX_SOURCELOCAL "Remote Supports Triggers !R12AZ", remote_side->trigger_supported, idx); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, remote_side) + offsetof(repl_conn_info_t, cross_endian), SIZEOF(remote_side->cross_endian)); PRINT_BOOLEAN(PREFIX_SOURCELOCAL "Remote is Cross Endian !R12AZ", remote_side->cross_endian, idx); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, remote_side) + offsetof(repl_conn_info_t, endianness_known), SIZEOF(remote_side->endianness_known)); PRINT_BOOLEAN(PREFIX_SOURCELOCAL "Remote Endianness Known !R12AZ", remote_side->endianness_known, idx); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, remote_side) + offsetof(repl_conn_info_t, null_subs_xform), SIZEOF(remote_side->null_subs_xform)); PRINT_BOOLEAN(PREFIX_SOURCELOCAL "Remote needs Null Subs Xform !R12AZ", remote_side->null_subs_xform, idx); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, remote_side) + offsetof(repl_conn_info_t, is_supplementary), SIZEOF(remote_side->is_supplementary)); PRINT_BOOLEAN(PREFIX_SOURCELOCAL "Remote is Supplementary Instance !R12AZ", remote_side->is_supplementary, idx); /**************** "remote_side" structure member dump done ****************/ PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, read_state), SIZEOF(gtmsourcelocal_ptr->read_state)); string = (READ_POOL == gtmsourcelocal_ptr->read_state) ? "POOL" : ((READ_FILE == gtmsourcelocal_ptr->read_state) ? "FILE" : "UNKNOWN"); if (STRNCMP_LIT(string, "UNKNOWN")) util_out_print( PREFIX_SOURCELOCAL "Currently Reading from !R21AZ", TRUE, idx, string); else { util_out_print( PREFIX_SOURCELOCAL "Currently Reading from !R21AZ [0x!XL]", TRUE, idx, string, gtmsourcelocal_ptr->read_state); } PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, jnlfileonly), SIZEOF(gtmsourcelocal_ptr->jnlfileonly)); PRINT_BOOLEAN(PREFIX_SOURCELOCAL "Journal File Only !R12AZ", gtmsourcelocal_ptr->jnlfileonly, idx); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, read), SIZEOF(gtmsourcelocal_ptr->read)); util_out_print( PREFIX_SOURCELOCAL "Relative Read Offset !10UL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->read, gtmsourcelocal_ptr->read); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, read_addr), SIZEOF(gtmsourcelocal_ptr->read_addr)); util_out_print( PREFIX_SOURCELOCAL "Absolute Read Offset !20@UQ [0x!16@XQ]", TRUE, idx, >msourcelocal_ptr->read_addr, >msourcelocal_ptr->read_addr); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, read_jnl_seqno), SIZEOF(gtmsourcelocal_ptr->read_jnl_seqno)); util_out_print( PREFIX_SOURCELOCAL "Resync Sequence Number !20@UQ [0x!16@XQ]", TRUE, idx, >msourcelocal_ptr->read_jnl_seqno, >msourcelocal_ptr->read_jnl_seqno); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, connect_jnl_seqno), SIZEOF(gtmsourcelocal_ptr->connect_jnl_seqno)); util_out_print( PREFIX_SOURCELOCAL "Connect Sequence Number !20@UQ [0x!16@XQ]", TRUE, idx, >msourcelocal_ptr->connect_jnl_seqno, >msourcelocal_ptr->connect_jnl_seqno); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, num_histinfo), SIZEOF(gtmsourcelocal_ptr->num_histinfo)); util_out_print( PREFIX_SOURCELOCAL "Number of histinfo structures !10UL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->num_histinfo, gtmsourcelocal_ptr->num_histinfo); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, next_histinfo_num), SIZEOF(gtmsourcelocal_ptr->next_histinfo_num)); util_out_print( PREFIX_SOURCELOCAL "Next histinfo Number !10UL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->next_histinfo_num, gtmsourcelocal_ptr->next_histinfo_num); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, next_histinfo_seqno), SIZEOF(gtmsourcelocal_ptr->next_histinfo_seqno)); util_out_print( PREFIX_SOURCELOCAL "Next histinfo Seqno !20@UQ [0x!16@XQ]", TRUE, idx, >msourcelocal_ptr->next_histinfo_seqno, >msourcelocal_ptr->next_histinfo_seqno); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, last_flush_resync_seqno), SIZEOF(gtmsourcelocal_ptr->last_flush_resync_seqno)); util_out_print( PREFIX_SOURCELOCAL "Last Flush Resync Seqno !20@UQ [0x!16@XQ]", TRUE, idx, >msourcelocal_ptr->last_flush_resync_seqno, >msourcelocal_ptr->last_flush_resync_seqno); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, send_new_histrec), SIZEOF(gtmsourcelocal_ptr->send_new_histrec)); PRINT_BOOLEAN( PREFIX_SOURCELOCAL "Send New histinfo !R8AZ", gtmsourcelocal_ptr->send_new_histrec, idx); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, send_losttn_complete), SIZEOF(gtmsourcelocal_ptr->send_losttn_complete)); PRINT_BOOLEAN( PREFIX_SOURCELOCAL "Send LostTN Complete !R8AZ", gtmsourcelocal_ptr->send_losttn_complete, idx); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, secondary_host[0]), SIZEOF(gtmsourcelocal_ptr->secondary_host)); if (20 >= strlen(gtmsourcelocal_ptr->secondary_host)) { util_out_print( PREFIX_SOURCELOCAL "Secondary HOSTNAME !R20AZ", TRUE, idx, gtmsourcelocal_ptr->secondary_host); } else { util_out_print( PREFIX_SOURCELOCAL "Secondary HOSTNAME !AZ", TRUE, idx, gtmsourcelocal_ptr->secondary_host); } PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, secondary_af), SIZEOF(gtmsourcelocal_ptr->secondary_af)); string = (AF_INET == gtmsourcelocal_ptr->secondary_af)? "IPv4" : ((AF_INET6 == gtmsourcelocal_ptr->secondary_af) ? "IPv6" : "UNKNOWN"); util_out_print( PREFIX_SOURCELOCAL "Secondary Address Family !R10AZ",TRUE, idx, string); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, secondary_inet_addr), SIZEOF(gtmsourcelocal_ptr->secondary_inet_addr)); errcode = getnameinfo((struct sockaddr *)>msourcelocal_ptr->secondary_inet_addr, gtmsourcelocal_ptr->secondary_addrlen, secondary_addr, SA_MAXLEN, NULL, 0, NI_NUMERICHOST); if (0 == errcode) { util_out_print( PREFIX_SOURCELOCAL "Secondary INET Address !R24AZ", TRUE, idx, secondary_addr); } else { string = "UNKNOWN"; util_out_print( PREFIX_SOURCELOCAL "Secondary INET Address !R10AZ", TRUE, idx, string); } PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, secondary_port), SIZEOF(gtmsourcelocal_ptr->secondary_port)); util_out_print( PREFIX_SOURCELOCAL "Secondary Port !10UL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->secondary_port, gtmsourcelocal_ptr->secondary_port); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, log_interval), SIZEOF(gtmsourcelocal_ptr->log_interval)); util_out_print( PREFIX_SOURCELOCAL "Log Interval !10UL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->log_interval, gtmsourcelocal_ptr->log_interval); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, log_file[0]), SIZEOF(gtmsourcelocal_ptr->log_file)); if (20 >= strlen(gtmsourcelocal_ptr->log_file)) util_out_print( PREFIX_SOURCELOCAL "Log File !R20AZ", TRUE, idx, gtmsourcelocal_ptr->log_file); else util_out_print( PREFIX_SOURCELOCAL "Log File !AZ", TRUE, idx, gtmsourcelocal_ptr->log_file); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, changelog), SIZEOF(gtmsourcelocal_ptr->changelog)); util_out_print( PREFIX_SOURCELOCAL "Changelog !10UL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->changelog, gtmsourcelocal_ptr->changelog); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, statslog), SIZEOF(gtmsourcelocal_ptr->statslog)); util_out_print( PREFIX_SOURCELOCAL "Statslog !10UL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->statslog, gtmsourcelocal_ptr->statslog); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, statslog_file[0]), SIZEOF(gtmsourcelocal_ptr->statslog_file)); if (20 >= strlen(gtmsourcelocal_ptr->statslog_file)) { util_out_print( PREFIX_SOURCELOCAL "Statslog File !R20AZ", TRUE, idx, gtmsourcelocal_ptr->statslog_file); } else { /*After gtm-7296, the statslog_file length should always be 0, so the following in fact won't be executed*/ util_out_print( PREFIX_SOURCELOCAL "Statslog File !AZ", TRUE, idx, gtmsourcelocal_ptr->statslog_file); } PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, connect_parms[GTMSOURCE_CONN_HARD_TRIES_COUNT]), SIZEOF(gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HARD_TRIES_COUNT])); util_out_print( PREFIX_SOURCELOCAL "Connect Parms Hard Tries Count !10UL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HARD_TRIES_COUNT], gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HARD_TRIES_COUNT]); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, connect_parms[GTMSOURCE_CONN_HARD_TRIES_PERIOD]), SIZEOF(gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HARD_TRIES_PERIOD])); util_out_print( PREFIX_SOURCELOCAL "Connect Parms Hard Tries Period !10UL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HARD_TRIES_PERIOD], gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HARD_TRIES_PERIOD]); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, connect_parms[GTMSOURCE_CONN_SOFT_TRIES_PERIOD]), SIZEOF(gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_SOFT_TRIES_PERIOD])); util_out_print( PREFIX_SOURCELOCAL "Connect Parms Soft Tries Period !10UL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_SOFT_TRIES_PERIOD], gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_SOFT_TRIES_PERIOD]); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, connect_parms[GTMSOURCE_CONN_ALERT_PERIOD]), SIZEOF(gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_ALERT_PERIOD])); util_out_print( PREFIX_SOURCELOCAL "Connect Parms Alert Period !10UL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_ALERT_PERIOD], gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_ALERT_PERIOD]); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD]), SIZEOF(gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD])); util_out_print( PREFIX_SOURCELOCAL "Connect Parms Heartbeat Period !10UL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD], gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD]); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, connect_parms[GTMSOURCE_CONN_HEARTBEAT_MAX_WAIT]), SIZEOF(gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HEARTBEAT_MAX_WAIT])); util_out_print( PREFIX_SOURCELOCAL "Connect Parms Heartbeat Max Wait !10UL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HEARTBEAT_MAX_WAIT], gtmsourcelocal_ptr->connect_parms[GTMSOURCE_CONN_HEARTBEAT_MAX_WAIT]); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, shutdown), SIZEOF(gtmsourcelocal_ptr->shutdown)); util_out_print( PREFIX_SOURCELOCAL "Shutdown State !10UL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->shutdown, gtmsourcelocal_ptr->shutdown); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, shutdown_time), SIZEOF(gtmsourcelocal_ptr->shutdown_time)); util_out_print( PREFIX_SOURCELOCAL "Shutdown Time in seconds !10SL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->shutdown_time, gtmsourcelocal_ptr->shutdown_time); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, filter_cmd[0]), SIZEOF(gtmsourcelocal_ptr->filter_cmd)); if (20 >= strlen(gtmsourcelocal_ptr->filter_cmd)) { util_out_print( PREFIX_SOURCELOCAL "Filter Command !R20AZ", TRUE, idx, gtmsourcelocal_ptr->filter_cmd); } else { util_out_print( PREFIX_SOURCELOCAL "Filter Command !AZ", TRUE, idx, gtmsourcelocal_ptr->filter_cmd); } PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, next_renegotiate_time), SIZEOF(gtmsourcelocal_ptr->next_renegotiate_time)); util_out_print(PREFIX_SOURCELOCAL, FALSE, idx); PRINT_TIME("Next Renegotiate Time ", gtmsourcelocal_ptr->next_renegotiate_time); PRINT_OFFSET_PREFIX(offsetof(gtmsource_local_struct, num_renegotiations), SIZEOF(gtmsourcelocal_ptr->num_renegotiations)); util_out_print(PREFIX_SOURCELOCAL "Number of TLS/SSL renegotiations !10UL [0x!XL]", TRUE, idx, gtmsourcelocal_ptr->num_renegotiations, gtmsourcelocal_ptr->num_renegotiations); PRINT_DASHES; } } fis-gtm-V7.0-005/sr_unix/repl_inst_dump.h0000755000032200000250000000313614342376330017244 0ustar librarygtc/**************************************************************** * * * Copyright 2006, 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. * * * ****************************************************************/ #ifndef REPL_INSTANCE_DUMP_INCLUDED #define REPL_INSTANCE_DUMP_INCLUDED #define TIME_DISPLAY_FAO " !19AD" #define DASHES_NO_OFFSET "----------------------------------------------------------" #define DASHES_YES_OFFSET "----------------------------------------------------------------------------" #define PRINT_DASHES util_out_print(detail_specified ? DASHES_YES_OFFSET : DASHES_NO_OFFSET, TRUE) GBLREF boolean_t detail_specified; void repl_inst_dump_filehdr(repl_inst_hdr_ptr_t repl_instance); void repl_inst_dump_gtmsrclcl(gtmsrc_lcl_ptr_t gtmsrclcl_ptr); void repl_inst_dump_history_records(char *inst_fn, int4 num_histinfo); void repl_dump_histinfo(FILE *log_fp, boolean_t stamptime, boolean_t flush, char *start_text, repl_histinfo *cur_histinfo); void repl_inst_dump_jnlpoolctl(jnlpool_ctl_ptr_t jnlpool_ctl); void repl_inst_dump_gtmsourcelocal(gtmsource_local_ptr_t gtmsourcelocal_ptr); void mupcli_get_offset_size_value(uint4 *offset, uint4 *size, gtm_uint64_t *value, boolean_t *value_present); void mupcli_edit_offset_size_value(sm_uc_ptr_t buff, uint4 offset, uint4 size, gtm_uint64_t value, boolean_t value_present); #endif /* REPL_INSTANCE_DUMP_INCLUDED */ fis-gtm-V7.0-005/sr_unix/repl_inst_edit.c0000644000032200000250000003503314342376330017215 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2021 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_string.h" #include "gtm_stdio.h" #include "gtm_ipc.h" #include #include "cli.h" #include "util.h" #include "repl_instance.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" /* for JNL_WHOLE_FROM_SHORT_TIME */ #include "buddy_list.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "hashtab_mname.h" /* needed for muprec.h */ #include "muprec.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_inst_dump.h" #include "is_proc_alive.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "ftok_sems.h" #include "gtm_sem.h" GBLREF jnlpool_addrs_ptr_t jnlpool; /* Used to access the gtmsource_local structures (slots) */ GBLREF boolean_t in_repl_inst_edit; /* used by an assert in repl_inst_read/repl_inst_write */ GBLREF boolean_t detail_specified; /* set to TRUE if -DETAIL is specified */ GBLREF uint4 section_offset; /* Used by PRINT_OFFSET_PREFIX macro in repl_inst_dump.c */ error_def(ERR_JNLPOOLSETUP); error_def(ERR_MUPCLIERR); error_def(ERR_SIZENOTVALID8); void mupcli_get_offset_size_value(uint4 *offset, uint4 *size, gtm_uint64_t *value, boolean_t *value_present) { if (!cli_get_hex("OFFSET", offset)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MUPCLIERR); if (!cli_get_hex("SIZE", size)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MUPCLIERR); if (!((SIZEOF(char) == *size) || (SIZEOF(short) == *size) || (SIZEOF(int4) == *size) || (SIZEOF(gtm_int64_t) == *size))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_SIZENOTVALID8); if (0 > (int4)*size) { util_out_print("Error: SIZE specified cannot be negative", TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MUPCLIERR); } if (0 != (*offset % *size)) { util_out_print("Error: OFFSET [0x!XL] should be a multiple of Size [!UL]", TRUE, *offset, *size); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MUPCLIERR); } if (CLI_PRESENT == cli_present("VALUE")) { *value_present = TRUE; if (!cli_get_hex64("VALUE", value)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MUPCLIERR); } else *value_present = FALSE; } void mupcli_edit_offset_size_value(sm_uc_ptr_t buff, uint4 offset, uint4 size, gtm_uint64_t value, boolean_t value_present) { char temp_str[MAX_REPL_OPMSG_LEN], temp_str1[MAX_REPL_OPMSG_LEN]; gtm_uint64_t old_value = -1; memset(temp_str, 0, MAX_REPL_OPMSG_LEN); memset(temp_str1, 0, MAX_REPL_OPMSG_LEN); if (SIZEOF(char) == size) { SNPRINTF(temp_str, MAX_REPL_OPMSG_LEN, "!UB [0x!XB]"); old_value = *(sm_uc_ptr_t)buff; } else if (SIZEOF(short) == size) { SNPRINTF(temp_str, MAX_REPL_OPMSG_LEN, "!UW [0x!XW]"); old_value = *(sm_ushort_ptr_t)buff; } else if (SIZEOF(int4) == size) { SNPRINTF(temp_str, MAX_REPL_OPMSG_LEN, "!UL [0x!XL]"); old_value = *(sm_uint_ptr_t)buff; } else if (SIZEOF(gtm_int64_t) == size) { SNPRINTF(temp_str, MAX_REPL_OPMSG_LEN, "!@UQ [0x!@XQ]"); old_value = *(qw_num_ptr_t)buff; } assert(-1 != old_value); if (value_present) { if (SIZEOF(char) == size) *(sm_uc_ptr_t)buff = (unsigned char)value; else if (SIZEOF(short) == size) *(sm_ushort_ptr_t)buff = (unsigned short)value; else if (SIZEOF(int4) == size) *(sm_uint_ptr_t)buff = (unsigned int)value; else if (SIZEOF(gtm_int64_t) == size) *(qw_num_ptr_t)buff = value; } else value = old_value; SNPRINTF(temp_str1, MAX_REPL_OPMSG_LEN, "Offset !UL [0x!XL] : Old Value = %s : New Value = %s : Size = !UB [0x!XB]", temp_str, temp_str); if (SIZEOF(int4) >= size) util_out_print(temp_str1, TRUE, offset, offset, (uint4)old_value, (uint4)old_value, (uint4)value, (uint4)value, size, size); else util_out_print(temp_str1, TRUE, offset, offset, &old_value, &old_value, &value, &value, size, size); } /* Description: * Edits or displays the contents of a replication instance file. * Parameters: None * Return Value: None */ void repl_inst_edit(void) { unsigned short inst_fn_len, instname_len; char inst_fn[MAX_FN_LEN + 1], buff_unaligned[REPL_INST_HDR_SIZE + GTMSRC_LCL_SIZE + 8]; char instname[MAX_INSTNAME_LEN]; char *buff; repl_inst_hdr_ptr_t repl_instance; gtmsrc_lcl_ptr_t gtmsrclcl_ptr; gtmsource_local_ptr_t gtmsourcelocal_ptr; uint4 offset, size; gtm_uint64_t value; boolean_t value_present, name_present, change_present, jnlpool_available, need_inst_access, need_lock; int idx, num_slots_cleaned; int status, save_errno; int qdbrundown_status, cleanslots_status; off_t off; unix_db_info *udi; union semun semarg; struct semid_ds semstat; in_repl_inst_edit = IN_REPL_INST_EDIT_TRUE; /* Indicate to "repl_inst_read" and "jnlpool_init" * we are in MUPIP REPLIC -EDITINSTANCE */ inst_fn_len = MAX_FN_LEN; if (!cli_get_str("INSTFILE", inst_fn, &inst_fn_len) || (0 == inst_fn_len)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MUPCLIERR); inst_fn[inst_fn_len] = '\0'; buff = &buff_unaligned[0]; buff = (char *)ROUND_UP2((INTPTR_T)buff, 8); /* Make sure journal pool is initially not open while we're in MUPIP REPLIC -EDITINSTANCE */ assert(NULL == jnlpool); jnlpool_available = FALSE; if (CLI_PRESENT == cli_present("SHOW")) { detail_specified = (CLI_PRESENT == cli_present("DETAIL")); repl_inst_read(inst_fn, (off_t)0, (sm_uc_ptr_t)buff, REPL_INST_HDR_SIZE + GTMSRC_LCL_SIZE); util_out_print("GTM-I-MUREPLSHOW, SHOW output for replication instance file !AD", TRUE, inst_fn_len, inst_fn); repl_instance = (repl_inst_hdr_ptr_t)&buff[0]; section_offset = 0; repl_inst_dump_filehdr(repl_instance); section_offset = REPL_INST_HDR_SIZE; repl_inst_dump_gtmsrclcl((gtmsrc_lcl_ptr_t)&buff[REPL_INST_HDR_SIZE]); section_offset = REPL_INST_HISTINFO_START; repl_inst_dump_history_records(inst_fn, repl_instance->num_histinfo); } change_present = (CLI_PRESENT == cli_present("CHANGE")); name_present = (CLI_PRESENT == cli_present("NAME")); qdbrundown_status = cli_present("QDBRUNDOWN"); cleanslots_status = cli_present("CLEANSLOTS"); /* Check if need to access and modify the instance file */ need_inst_access = change_present || name_present || qdbrundown_status || cleanslots_status; need_lock = FALSE; if (need_inst_access) { need_lock = TRUE; repl_inst_read(inst_fn, (off_t)0, (sm_uc_ptr_t)buff, REPL_INST_HDR_SIZE); repl_instance = (repl_inst_hdr_ptr_t)&buff[0]; semarg.buf = &semstat; /* Check if primary instance has potentially crashed. * If so, set need_lock to FALSE so we can avoid calling * jnlpool_init() and avoid a REPLREQROLLBACK error. */ if ((INVALID_SEMID != repl_instance->jnlpool_semid) && (-1 == semctl(repl_instance->jnlpool_semid, DB_CONTROL_SEM, IPC_STAT, semarg))) need_lock = FALSE; } if (need_lock) { assert(NULL == jnlpool); /* Attach to journal pool so we can obtain locks and access slots */ jnlpool_init(GTMRELAXED, (boolean_t)FALSE, (boolean_t *)NULL, NULL); assert(NULL != jnlpool); jnlpool_available = ((NULL != jnlpool->repl_inst_filehdr) && (INVALID_SEMID != jnlpool->repl_inst_filehdr->jnlpool_semid) && (INVALID_SHMID != jnlpool->repl_inst_filehdr->jnlpool_shmid)); assert(NULL != jnlpool->jnlpool_dummy_reg); udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); assert((NULL != udi) && udi->grabbed_ftok_sem); if (jnlpool_available) { assert(!udi->grabbed_access_sem); /* Journal pool shared memory and semaphore is available * so grab the journal pool access semaphore */ status = grab_sem(SOURCE, JNL_POOL_ACCESS_SEM); if (SS_NORMAL != status) { save_errno = errno; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with journal pool access semaphore, no changes were made"), save_errno); } udi->grabbed_access_sem = TRUE; udi->counter_acc_incremented = TRUE; /* Now that we have the access semaphore, we * can release the ftok and then do grab_lock. */ ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, FALSE); grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); } } /* Assert that when lock is needed, jnlpool is attached and either the ftok or jnlpool access semaphore is obtained */ assert(!need_inst_access || !need_lock || ((NULL != jnlpool) && (NULL != udi) && ((jnlpool_available && udi->grabbed_access_sem) || (!jnlpool_available && udi->grabbed_ftok_sem)))); if (change_present) { /* Indicate to "repl_inst_read" that we are in a MUPIP REPLIC -EDITINSTANCE -CHANGE command by modifying * the global variable "in_repl_inst_edit" to a more specific value for the duration of the CHANGE command. */ assert(IN_REPL_INST_EDIT_CHANGE_OFFSET != IN_REPL_INST_EDIT_TRUE); assert(IN_REPL_INST_EDIT_CHANGE_OFFSET != IN_REPL_INST_EDIT_FALSE); in_repl_inst_edit = IN_REPL_INST_EDIT_CHANGE_OFFSET; /* needed by "repl_inst_read" to avoid errors */ mupcli_get_offset_size_value(&offset, &size, &value, &value_present); assert(size <= REPL_INST_HDR_SIZE + GTMSRC_LCL_SIZE); repl_inst_read(inst_fn, (off_t)offset, (sm_uc_ptr_t)buff, size); mupcli_edit_offset_size_value((sm_uc_ptr_t)buff, offset, size, value, value_present); repl_inst_write(inst_fn, (off_t)offset, (sm_uc_ptr_t)buff, size); in_repl_inst_edit = IN_REPL_INST_EDIT_TRUE; } if (name_present || qdbrundown_status || cleanslots_status) { if (name_present) { /* Edit the instance name */ instname_len = MAX_INSTNAME_LEN; assert(MAX_INSTNAME_LEN == SIZEOF(instname)); if (!cli_get_str("NAME", instname, &instname_len)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_MUPCLIERR); assert(MAX_INSTNAME_LEN >= instname_len); if (MAX_INSTNAME_LEN == instname_len) { util_out_print("Error: Instance name length can be at most 15", TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MUPCLIERR); } } repl_inst_read(inst_fn, (off_t)0, (sm_uc_ptr_t)buff, REPL_INST_HDR_SIZE + GTMSRC_LCL_SIZE); repl_instance = (repl_inst_hdr_ptr_t)&buff[0]; if (name_present) { util_out_print("HDR Instance Name changing from !AZ to !AZ", TRUE, repl_instance->inst_info.this_instname, instname); assert('\0' == instname[instname_len]); memcpy(repl_instance->inst_info.this_instname, instname, instname_len + 1); } if (qdbrundown_status) { util_out_print("HDR Quick database rundown is active changing from !AZ to !AZ", TRUE, repl_instance->qdbrundown ? "TRUE" : "FALSE", (CLI_PRESENT == qdbrundown_status) ? "TRUE" : "FALSE"); repl_instance->qdbrundown = (CLI_PRESENT == qdbrundown_status); } if (cleanslots_status && jnlpool_available) { assert(udi->grabbed_access_sem); /* If shared memory is available and clean up desired, * then clean up the inactive gtmsource_local structures (slots) * from shared memory and make the corresponding changes to the * gtmsrc_lcl strunctures in the instance file header */ assert((NULL != jnlpool->jnlpool_dummy_reg) && jnlpool->jnlpool_dummy_reg->open); gtmsourcelocal_ptr = &jnlpool->gtmsource_local_array[0]; gtmsrclcl_ptr = (gtmsrc_lcl_ptr_t)&buff[REPL_INST_HDR_SIZE]; num_slots_cleaned = 0; for (idx = 0; idx < NUM_GTMSRC_LCL; idx++, gtmsourcelocal_ptr++, gtmsrclcl_ptr++) { assert((NULL != gtmsourcelocal_ptr) && (NULL != gtmsrclcl_ptr)); /* Check if slot is used and inactive */ if ((('\0' != gtmsourcelocal_ptr->secondary_instname[0]) || (0 != gtmsourcelocal_ptr->read_jnl_seqno) || (0 != gtmsourcelocal_ptr->connect_jnl_seqno)) && ((GTMSOURCE_DUMMY_STATE == gtmsourcelocal_ptr->gtmsource_state) || (NO_SHUTDOWN != gtmsourcelocal_ptr->shutdown) || !is_proc_alive(gtmsourcelocal_ptr->gtmsource_pid, 0))) { /* Slot is inactive so clean (i.e. set certain * fields to default value so slot can be reused) */ gtmsourcelocal_ptr->secondary_instname[0] = '\0'; gtmsourcelocal_ptr->gtmsource_state = GTMSOURCE_DUMMY_STATE; gtmsourcelocal_ptr->read_jnl_seqno = 0; gtmsourcelocal_ptr->connect_jnl_seqno = 0; gtmsourcelocal_ptr->gtmsource_pid = 0; /* Clean the corresponding gtmsrc_lcl structure */ COPY_GTMSOURCELOCAL_TO_GTMSRCLCL(gtmsourcelocal_ptr, gtmsrclcl_ptr); gtmsourcelocal_ptr->last_flush_resync_seqno = gtmsourcelocal_ptr->read_jnl_seqno; num_slots_cleaned++; } } } else if (cleanslots_status) { assert(udi->grabbed_ftok_sem); /* If shared memory is not available and cleaning desired, * only clean the inactive gtmsrc_lcl structures because the * gtmsource_local structures are not available. */ gtmsrclcl_ptr = (gtmsrc_lcl_ptr_t)&buff[REPL_INST_HDR_SIZE]; num_slots_cleaned = 0; for (idx = 0; idx < NUM_GTMSRC_LCL; idx++, gtmsrclcl_ptr++) { assert(NULL != gtmsrclcl_ptr); /* Skip slots that are not used */ if (('\0' == gtmsrclcl_ptr->secondary_instname[0]) && (0 == gtmsrclcl_ptr->resync_seqno) && (0 == gtmsrclcl_ptr->connect_jnl_seqno)) continue; /* Clean the slot. The gtmsrc_lcl structure only has three * fields as opposed to the gtmsource_local structure which * has numerous fields. */ gtmsrclcl_ptr->secondary_instname[0] = '\0'; gtmsrclcl_ptr->resync_seqno = 0; gtmsrclcl_ptr->connect_jnl_seqno = 0; num_slots_cleaned++; } } if (cleanslots_status) { util_out_print("Cleaned !2UL inactive source server slot(s) from instance file header", TRUE, num_slots_cleaned); } /* Write/Flush any changes to the instance file header */ repl_inst_write(inst_fn, (off_t)0, (sm_uc_ptr_t)buff, REPL_INST_HDR_SIZE + GTMSRC_LCL_SIZE); } if (need_lock) { if (jnlpool_available) { assert(udi->grabbed_access_sem); /* Release the lock and journal pool access semaphore */ rel_lock(jnlpool->jnlpool_dummy_reg); rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; } else { assert(udi->grabbed_ftok_sem); /* Release the ftok */ ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, FALSE); } } in_repl_inst_edit = IN_REPL_INST_EDIT_FALSE; } fis-gtm-V7.0-005/sr_unix/repl_inst_ftok_counter_halted.c0000644000032200000250000000432014342376330022306 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2021 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 #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_instance.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_sem.h" #include "ftok_sems.h" #include "repl_inst_ftok_counter_halted.h" GBLREF jnlpool_addrs_ptr_t jnlpool; error_def(ERR_JNLPOOLSETUP); error_def(ERR_NOMORESEMCNT); error_def(ERR_TEXT); /* This function sets the "ftok_counter_halted" field to TRUE in the journal pool. * Caller must be attached to the journal pool and have already gotten the ftok lock on the instance file. */ void repl_inst_ftok_counter_halted(unix_db_info *udi) { assert(udi->grabbed_ftok_sem); /* this ensures we have a lock before we modify the instance file header */ if ((NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl)) { grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, ASSERT_NO_ONLINE_ROLLBACK); assert(!jnlpool->jnlpool_ctl->ftok_counter_halted); if (!jnlpool->repl_inst_filehdr->qdbrundown) { rel_lock(jnlpool->jnlpool_dummy_reg); if (udi->grabbed_access_sem) rel_sem_immediate(SOURCE, JNL_POOL_ACCESS_SEM); udi->grabbed_access_sem = FALSE; udi->counter_acc_incremented = FALSE; ftok_sem_release(jnlpool->jnlpool_dummy_reg, udi->counter_ftok_incremented, TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_JNLPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error incrementing the ftok semaphore counter"), ERANGE); } jnlpool->jnlpool_ctl->ftok_counter_halted = TRUE; rel_lock(jnlpool->jnlpool_dummy_reg); } send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_NOMORESEMCNT, 5, LEN_AND_LIT("ftok"), FILE_TYPE_REPLINST, LEN_AND_STR(udi->fn)); } fis-gtm-V7.0-005/sr_unix/repl_inst_ftok_counter_halted.h0000644000032200000250000000131414342376330022313 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016 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. * * * ****************************************************************/ #ifndef REPL_INST_FTOK_COUNTER_HALTED_INCLUDED #define REPL_INST_FTOK_COUNTER_HALTED_INCLUDED void repl_inst_ftok_counter_halted(unix_db_info *udi); #endif /* REPL_INST_FTOK_COUNTER_HALTED_INCLUDED */ fis-gtm-V7.0-005/sr_unix/repl_instance.c0000644000032200000250000020234414342376330017040 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_stdio.h" #include "gtm_string.h" #include "gtm_unistd.h" #include "gtm_fcntl.h" #include "gtm_stat.h" #include "gtm_inet.h" #include "gtm_time.h" #include #include #include #include "eintr_wrappers.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" #include "iosp.h" #include "gtmio.h" #include "gtm_logicals.h" #include "trans_log_name.h" #include "gtmmsg.h" #include "repl_sem.h" #include "repl_instance.h" #include "ftok_sems.h" #include "error.h" #include "gds_rundown.h" #include "buddy_list.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "hashtab_mname.h" /* needed for muprec.h */ #include "muprec.h" #include "have_crit.h" #include "anticipatory_freeze.h" #include "gtmimagename.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF recvpool_addrs recvpool; GBLREF boolean_t in_repl_inst_edit; /* Used by an assert in repl_inst_read/repl_inst_write */ GBLREF boolean_t in_repl_inst_create; /* Used by repl_inst_read/repl_inst_write */ GBLREF boolean_t in_mupip_ftok; /* Used by an assert in repl_inst_read */ GBLREF jnl_gbls_t jgbl; GBLREF gd_addr *gd_header; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF bool in_backup; GBLREF int4 strm_index; GBLREF boolean_t is_src_server; GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; GBLREF boolean_t is_rcvr_server; GBLREF char repl_instfilename[]; GBLREF gd_addr *repl_inst_from_gld; ZOS_ONLY(error_def(ERR_BADTAG);) error_def(ERR_LOGTOOLONG); error_def(ERR_NOTALLDBOPN); error_def(ERR_REPLFTOKSEM); error_def(ERR_REPLINSTACC); error_def(ERR_REPLINSTCLOSE); error_def(ERR_REPLINSTCREATE); error_def(ERR_REPLINSTFMT); error_def(ERR_REPLINSTNOHIST); error_def(ERR_REPLINSTOPEN); error_def(ERR_REPLINSTREAD); error_def(ERR_REPLINSTSEQORD); error_def(ERR_REPLINSTUNDEF); error_def(ERR_REPLINSTWRITE); error_def(ERR_SYSCALL); error_def(ERR_TEXT); /* Description: * Get the environment of replication instance. * Parameters: * fn : repl instance file name it gets * fn_len: length of fn. * bufsize: the buffer size caller gives. If exceeded, it trucates file name. * gd_ptr: global directory for extended reference, otherwise NULL. * Return Value: * non NULL, on success - 1 if from gtm_repl_instance, otherwise global directory * NULL, otherwise. */ struct gd_addr_struct *repl_inst_get_name(char *fn, unsigned int *fn_len, unsigned int bufsize, instname_act error_action, struct gd_addr_struct *gd_ptr) { char temp_inst_fn[MAX_FN_LEN + 1]; mstr log_nam, trans_name; gd_addr *gd_local; uint4 ustatus; int4 status; boolean_t ret, inst_from_gld; /* check global directory first */ gd_local = (NULL == gd_ptr) ? gd_header : (gd_addr *)gd_ptr; SETUP_INST_INFO(gd_local, log_nam, inst_from_gld); /* set log_nam from gld or environment variable */ trans_name.addr = temp_inst_fn; trans_name.len = 0; ret = FALSE; GET_INSTFILE_NAME(do_sendmsg_on_log2long, issue_gtm_putmsg); if (inst_from_gld && (SS_NOLOGNAM == status) && (0 != trans_name.len)) status = SS_NORMAL; if ((0 == trans_name.len) && (SS_NORMAL == status)) status = SS_NOLOGNAM; if (FALSE == ret) { if (issue_rts_error == error_action) { if (SS_LOG2LONG == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_LOGTOOLONG, 3, log_nam.len, log_nam.addr, SIZEOF(temp_inst_fn) - 1); else if (!inst_from_gld) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_REPLINSTUNDEF); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_REPLINSTACC, 2, log_nam.len, log_nam.addr, ERR_TEXT, 2, RTS_ERROR_LITERAL("from global directory")); } else if (issue_gtm_putmsg == error_action) { if (SS_LOG2LONG == status) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_LOGTOOLONG, 3, log_nam.len, log_nam.addr, SIZEOF(temp_inst_fn) - 1); else if (!inst_from_gld) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_REPLINSTUNDEF); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_REPLINSTACC, 2, log_nam.len, log_nam.addr, ERR_TEXT, 2, RTS_ERROR_LITERAL("from global directory")); } } else if ('\0' == repl_instfilename[0]) { /* save information from first instance for syslog */ memcpy(repl_instfilename, fn, *fn_len + 1); /* include null from get_full_path */ if (inst_from_gld) repl_inst_from_gld = gd_local; /* got from global directory */ } if (FALSE == ret) return NULL; else if (inst_from_gld) return gd_local; else return INST_NOT_GLD; } /* Description: * Reads "buflen" bytes of data into "buff" from the file "fn" at offset "offset" * Parameters: * fn : Instance file name. * offset: Offset at which to read * buff : Buffer to read into * buflen: Number of bytes to read * Return Value: * None */ void repl_inst_read(char *fn, off_t offset, sm_uc_ptr_t buff, size_t buflen) { int status, fd; size_t actual_readlen; unix_db_info *udi; gd_region *reg; repl_inst_hdr_ptr_t replhdr; assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); /* ROLLBACK -FORWARD should not call this function */ /* Assert that except for MUPIP REPLIC -INSTANCE_CREATE or -EDITINSTANCE or MUPIP FTOK, all callers hold the FTOK semaphore * on the replication instance file OR the journal pool lock. Note that the instance file might be pointed to by one of the * two region pointers "jnlpool->jnlpool_dummy_reg" or "recvpool.recvpool_dummy_reg" depending on whether the journal pool * or the receive pool was attached to first by this particular process. If both of them are non-NULL, both the region * pointers should be identical. This is also asserted below. * Note: Typically, journal pool lock should have sufficed. However, in certain places like jnlpool_init and recvpool_init, * the journal pool is not yet created and hence grab_lock/rel_lock does not make sense. In those cases we need the FTOK * lock on the instance file. The ONLY exception to this is ROLLBACK in which case it does NOT hold the FTOK semaphore and * since it is NOT necessary for ROLLBACK to have a journal pool open, grab_lock will not be done either. Assert * accordingly. */ assert(((NULL == jnlpool) || (NULL == jnlpool->jnlpool_dummy_reg)) || (NULL == recvpool.recvpool_dummy_reg) || ((NULL != jnlpool) && (jnlpool->jnlpool_dummy_reg == recvpool.recvpool_dummy_reg))); reg = jnlpool ? jnlpool->jnlpool_dummy_reg : NULL; if (NULL == reg) reg = recvpool.recvpool_dummy_reg; assert(((NULL == reg) && (in_repl_inst_create || in_repl_inst_edit || in_mupip_ftok)) || ((NULL != reg) && !in_repl_inst_create && !in_repl_inst_edit && !in_mupip_ftok) || ((NULL != reg) && (NULL != jnlpool) && in_repl_inst_edit)); if (NULL != reg) { udi = FILE_INFO(reg); assert(udi->grabbed_ftok_sem || ((jnlpool && jnlpool->jnlpool_ctl) && udi->s_addrs.now_crit) || jgbl.mur_rollback || ((NULL != jnlpool) && in_repl_inst_edit)); } OPENFILE_CLOEXEC(fn, O_RDONLY, fd); if (FD_INVALID == fd) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_REPLINSTOPEN, 2, LEN_AND_STR(fn), errno); assert(0 < buflen); if (0 != offset) { LSEEKREAD(fd, offset, buff, buflen, status); } else { /* Read starts from the replication instance file header. Assert that the entire file header was requested. * The only exception is MUPIP REPLIC -EDIT -CHANGE -OFFSET=0 -SIZE=xxx where xxx is < REPL_INST_HDR_SIZE. * "in_repl_inst_edit" being equal to IN_REPL_INST_EDIT_CHANGE_OFFSET identifies that situation. */ assert((REPL_INST_HDR_SIZE <= buflen) || (IN_REPL_INST_EDIT_CHANGE_OFFSET == in_repl_inst_edit)); /* Use LSEEKREAD_AVAILABLE macro instead of LSEEKREAD. This is because if we are not able to read the entire * fileheader, we still want to see if the "label" field of the file header got read in which case we can * do the format check first. It is important to do the format check before checking "status" returned from * LSEEKREAD* macros since the inability to read the entire file header might actually be due to the * older format replication instance file being smaller than even the newer format instance file header. */ LSEEKREAD_AVAILABLE(fd, offset, buff, buflen, actual_readlen, status); /* Skip error checking if we are inside a MUPIP REPLIC -EDITINSTANCE -CHANGE */ if (IN_REPL_INST_EDIT_CHANGE_OFFSET != in_repl_inst_edit) { if (GDS_REPL_INST_LABEL_SZ <= actual_readlen) { /* Have read the entire label in the instance file header. Check if it is the right version */ if (memcmp(buff, GDS_REPL_INST_LABEL, GDS_REPL_INST_LABEL_SZ - 1)) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_REPLINSTFMT, 6, LEN_AND_STR(fn), GDS_REPL_INST_LABEL_SZ - 1, GDS_REPL_INST_LABEL, GDS_REPL_INST_LABEL_SZ - 1, buff); } } if (0 == status) { /* Check a few other fields in the file-header for compatibility */ assert(actual_readlen == buflen); replhdr = (repl_inst_hdr_ptr_t)buff; /* Check endianness match */ if (GTM_IS_LITTLE_ENDIAN != replhdr->is_little_endian) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_REPLINSTFMT, 6, LEN_AND_STR(fn), LEN_AND_LIT(ENDIANTHIS), LEN_AND_LIT(ENDIANOTHER)); } /* Check 64bitness match */ if (GTM_IS_64BIT != replhdr->is_64bit) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_REPLINSTFMT, 6, LEN_AND_STR(fn), LEN_AND_LIT(GTM_BITNESS_THIS), LEN_AND_LIT(GTM_BITNESS_OTHER)); } /* At the time of this writing, the only minor version supported is 1. * Whenever this gets updated, we need to add code to do the online upgrade. * Add an assert as a reminder to do this. */ assert(1 == replhdr->replinst_minorver); /* Check if on-the-fly minor-version upgrade is necessary */ if (GDS_REPL_INST_MINOR_LABEL != replhdr->replinst_minorver) assert(FALSE); } } } assert((0 == status) || in_repl_inst_edit || in_mupip_ftok); if (0 != status) { if (-1 == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLINSTREAD, 4, buflen, (qw_off_t *)&offset, LEN_AND_STR(fn)); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLINSTREAD, 4, buflen, (qw_off_t *)&offset, LEN_AND_STR(fn), status); } CLOSEFILE_RESET(fd, status); /* resets "fd" to FD_INVALID */ assert(0 == status); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_REPLINSTCLOSE, 2, LEN_AND_STR(fn), status); } /* Description: * Writes "buflen" bytes of data from "buff" into the file "fn" at offset "offset" * Parameters: * fn : Instance file name. * offset: Offset at which to write * buff : Buffer to write from * buflen: Number of bytes to write * Return Value: * None. */ void repl_inst_write(char *fn, off_t offset, sm_uc_ptr_t buff, size_t buflen) { int status, fd, oflag; unix_db_info *udi; gd_region *reg; ZOS_ONLY(int realfiletag;) assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); /* ROLLBACK -FORWARD should not call this function */ /* Assert that except for MUPIP REPLIC -INSTANCE_CREATE or -EDITINSTANCE, all callers hold the FTOK semaphore on the * replication instance file OR the journal pool lock. Note that the instance file might be pointed to by one of the * two region pointers "jnlpool->jnlpool_dummy_reg" or "recvpool.recvpool_dummy_reg" depending on whether the journal pool * or the receive pool was attached to first by this particular process. If both of them are non-NULL, both the region * pointers should be identical. This is also asserted below. * Note: Typically, journal pool lock should have sufficed. However, in certain places like jnlpool_init and recvpool_init, * the journal pool is not yet created and hence grab_lock/rel_lock does not make sense. In those case we need the FTOK * lock on the instance file. The ONLY exception to this is ROLLBACK in which case it does NOT hold the FTOK semaphore and * since it is NOT necessary for ROLLBACK to have a journal pool open, grab_lock will not be done either. Assert * accordingly. */ assert(((NULL == jnlpool) || (NULL == jnlpool->jnlpool_dummy_reg)) || (NULL == recvpool.recvpool_dummy_reg) || (NULL != jnlpool) && (jnlpool->jnlpool_dummy_reg == recvpool.recvpool_dummy_reg)); DEBUG_ONLY( reg = jnlpool ? jnlpool->jnlpool_dummy_reg : NULL; if (NULL == reg) reg = recvpool.recvpool_dummy_reg; ) assert(((NULL == reg) && (in_repl_inst_create || in_repl_inst_edit)) || ((NULL != reg) && !in_repl_inst_create && !in_repl_inst_edit) || ((NULL != reg) && (NULL != jnlpool) && in_repl_inst_edit)); DEBUG_ONLY( if (NULL != reg) { udi = FILE_INFO(reg); assert(udi->grabbed_ftok_sem || (jnlpool && (NULL != jnlpool->jnlpool_ctl) && udi->s_addrs.now_crit) || jgbl.mur_rollback || ((NULL != jnlpool) && in_repl_inst_edit)); } ) oflag = O_RDWR; if (in_repl_inst_create) oflag |= (O_CREAT | O_EXCL); OPENFILE3_CLOEXEC(fn, oflag, 0666, fd); if (FD_INVALID == fd) { if (!in_repl_inst_create) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_REPLINSTOPEN, 2, LEN_AND_STR(fn), errno); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_REPLINSTCREATE, 2, LEN_AND_STR(fn), errno); } #ifdef __MVS__ if (-1 == (in_repl_inst_create ? gtm_zos_set_tag(fd, TAG_BINARY, TAG_NOTTEXT, TAG_FORCE, &realfiletag) : gtm_zos_tag_to_policy(fd, TAG_BINARY, &realfiletag))) TAG_POLICY_GTM_PUTMSG(fn, errno, realfiletag, TAG_BINARY); #endif assert(0 < buflen); REPL_INST_LSEEKWRITE(fd, offset, buff, buflen, status); assert(0 == status); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLINSTWRITE, 4, buflen, (qw_off_t *)&offset, LEN_AND_STR(fn), status); CLOSEFILE_RESET(fd, status); /* resets "fd" to FD_INVALID */ assert(0 == status); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_REPLINSTCLOSE, 2, LEN_AND_STR(fn), status); } /* Description: * Hardens all pending writes for the instance file to disk * Parameters: * fn : Instance file name. * Return Value: * None. */ void repl_inst_sync(char *fn) { int status, fd, oflag; unix_db_info *udi; gd_region *reg; /* Assert that except for MUPIP REPLIC -INSTANCE_CREATE or -EDITINSTANCE, all callers hold the FTOK semaphore * on the replication instance file. Note that the instance file might be pointed to by one of the two region * pointers "jnlpool->jnlpool_dummy_reg" or "recvpool.recvpool_dummy_reg" depending on whether the journal pool * or the receive pool was attached to first by this particular process. If both of them are non-NULL, both the * region pointers should be identical. This is also asserted below. */ assert(((NULL != jnlpool) && (NULL == jnlpool->jnlpool_dummy_reg)) || (NULL == recvpool.recvpool_dummy_reg) || ((NULL != jnlpool) && (jnlpool->jnlpool_dummy_reg == recvpool.recvpool_dummy_reg))); DEBUG_ONLY( reg = jnlpool ? jnlpool->jnlpool_dummy_reg : NULL; if (NULL == reg) reg = recvpool.recvpool_dummy_reg; ) DEBUG_ONLY( assert(NULL != reg); udi = FILE_INFO(reg); assert((NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl) && udi->s_addrs.now_crit); ) oflag = O_RDWR; OPENFILE3_CLOEXEC(fn, oflag, 0666, fd); if (FD_INVALID == fd) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_REPLINSTOPEN, 2, LEN_AND_STR(fn), errno); GTM_REPL_INST_FSYNC(fd, status); assert(0 == status); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fsync()"), CALLFROM, errno); CLOSEFILE_RESET(fd, status); /* resets "fd" to FD_INVALID */ assert(0 == status); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_REPLINSTCLOSE, 2, LEN_AND_STR(fn), status); } /* Description: * Reset journal pool shmid and semid in replication instance file. * Parameters: * None * Return Value: * None */ void repl_inst_jnlpool_reset(void) { repl_inst_hdr repl_instance; unix_db_info *udi; gd_region *reg; if ((NULL != jnlpool) && (NULL != jnlpool->repl_inst_filehdr)) { /* If journal pool exists, reset sem/shm ids in the file header in the journal pool and flush changes to disk */ jnlpool->repl_inst_filehdr->jnlpool_semid = INVALID_SEMID; jnlpool->repl_inst_filehdr->jnlpool_shmid = INVALID_SHMID; jnlpool->repl_inst_filehdr->jnlpool_semid_ctime = 0; jnlpool->repl_inst_filehdr->jnlpool_shmid_ctime = 0; repl_inst_flush_filehdr(); } else { /* If journal pool does not exist, reset sem/shm ids directly in the replication instance file header on disk */ reg = jnlpool ? jnlpool->jnlpool_dummy_reg : NULL; if (NULL == reg) reg = recvpool.recvpool_dummy_reg; assert(NULL != reg); udi = FILE_INFO(reg); assert(udi->grabbed_ftok_sem); repl_inst_read((char *)udi->fn, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr)); repl_instance.jnlpool_semid = INVALID_SEMID; repl_instance.jnlpool_shmid = INVALID_SHMID; repl_instance.jnlpool_semid_ctime = 0; repl_instance.jnlpool_shmid_ctime = 0; repl_inst_write((char *)udi->fn, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr)); } } /* Description: * Reset receiver pool shmid and semid in replication instance file. * Parameters: * None * Return Value: * None */ void repl_inst_recvpool_reset(void) { repl_inst_hdr repl_instance; unix_db_info *udi; assert(NULL != recvpool.recvpool_dummy_reg); udi = FILE_INFO(recvpool.recvpool_dummy_reg); assert(udi->grabbed_ftok_sem); if ((NULL != jnlpool) && (NULL != jnlpool->repl_inst_filehdr)) { /* If journal pool exists, reset sem/shm ids in the file header in the journal pool and flush changes to disk */ jnlpool->repl_inst_filehdr->recvpool_semid = INVALID_SEMID; jnlpool->repl_inst_filehdr->recvpool_shmid = INVALID_SHMID; jnlpool->repl_inst_filehdr->recvpool_semid_ctime = 0; jnlpool->repl_inst_filehdr->recvpool_shmid_ctime = 0; repl_inst_flush_filehdr(); } else { /* If journal pool does not exist, reset sem/shm ids directly in the replication instance file header on disk */ repl_inst_read((char *)udi->fn, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr)); repl_instance.recvpool_semid = INVALID_SEMID; repl_instance.recvpool_shmid = INVALID_SHMID; repl_instance.recvpool_semid_ctime = 0; repl_instance.recvpool_shmid_ctime = 0; repl_inst_write((char *)udi->fn, (off_t)0, (sm_uc_ptr_t)&repl_instance, SIZEOF(repl_inst_hdr)); } } /* Wrapper routine to GRAB the ftok semaphore lock of the replication instance file and to test for errors */ void repl_inst_ftok_sem_lock(void) { gd_region *reg; unix_db_info *udi; assert(!jgbl.mur_rollback); /* Rollback already has standalone access and will not ask for ftok lock */ assert(((NULL != jnlpool) && (NULL != jnlpool->jnlpool_dummy_reg)) || (NULL != recvpool.recvpool_dummy_reg)); assert(((NULL == jnlpool) || (NULL == jnlpool->jnlpool_dummy_reg)) || (NULL == recvpool.recvpool_dummy_reg) || ((NULL != jnlpool) && ((recvpool.recvpool_dummy_reg == jnlpool->jnlpool_dummy_reg)))); reg = jnlpool ? jnlpool->jnlpool_dummy_reg : NULL; if (NULL == reg) reg = recvpool.recvpool_dummy_reg; assert(NULL != reg); udi = FILE_INFO(reg); assert(!udi->grabbed_ftok_sem); if (!udi->grabbed_ftok_sem) { assert(0 == have_crit(CRIT_HAVE_ANY_REG)); if (!ftok_sem_lock(reg, FALSE)) { assert(FALSE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_REPLFTOKSEM, 2, LEN_AND_STR(udi->fn)); } } assert(udi->grabbed_ftok_sem); } /* Wrapper routine to RELEASE the ftok semaphore lock of the replication instance file and to test for errors */ void repl_inst_ftok_sem_release(void) { gd_region *reg; unix_db_info *udi; assert(!jgbl.mur_rollback); /* Rollback already has standalone access and will not ask for ftok lock */ assert(((NULL != jnlpool) && (NULL != jnlpool->jnlpool_dummy_reg)) || (NULL != recvpool.recvpool_dummy_reg)); assert(((NULL == jnlpool) || (NULL == jnlpool->jnlpool_dummy_reg)) || (NULL == recvpool.recvpool_dummy_reg) || ((NULL != jnlpool) && ((recvpool.recvpool_dummy_reg == jnlpool->jnlpool_dummy_reg)))); reg = jnlpool ? jnlpool->jnlpool_dummy_reg : NULL; if (NULL == reg) reg = recvpool.recvpool_dummy_reg; assert(NULL != reg); udi = FILE_INFO(reg); assert(udi->grabbed_ftok_sem); if (udi->grabbed_ftok_sem) /* Be safe in PRO and avoid releasing if we do not hold the ftok semaphore */ { assert(0 == have_crit(CRIT_HAVE_ANY_REG)); if (!ftok_sem_release(reg, FALSE, FALSE)) { assert(FALSE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_REPLFTOKSEM, 2, LEN_AND_STR(udi->fn)); } } assert(!udi->grabbed_ftok_sem); } /* Description: * Get the 'n'th histinfo record from the instance file. * Parameters: * index : The number of the histinfo record to be read. 0 for the first histinfo record, 1 for the second and so on... * histinfo : A pointer to the repl_histinfo structure to be filled in. * Return Value: * 0, on success * ERR_REPLINSTNOHIST, if "index" is not a valid histinfo index. */ int4 repl_inst_histinfo_get(int4 index, repl_histinfo *histinfo) { off_t offset; unix_db_info *udi; repl_inst_hdr_ptr_t repl_inst_filehdr; assert(NULL != jnlpool); udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); /* ROLLBACK -FORWARD should not call this function */ assert(udi->s_addrs.now_crit || jgbl.mur_rollback); if (0 > index) return ERR_REPLINSTNOHIST; repl_inst_filehdr = jnlpool->repl_inst_filehdr; assert(NULL != repl_inst_filehdr); assert(index < repl_inst_filehdr->num_histinfo); /* assert that no caller should request a get of an unused (but allocated) histinfo */ if (index >= repl_inst_filehdr->num_alloc_histinfo) return ERR_REPLINSTNOHIST; offset = REPL_INST_HISTINFO_START + (index * SIZEOF(repl_histinfo)); repl_inst_read((char *)udi->fn, offset, (sm_uc_ptr_t)histinfo, SIZEOF(repl_histinfo)); assert(histinfo->histinfo_num == index); return 0; } /* * Parameters: * seqno : The journal seqno that is to be searched in the instance file history. * strm_idx : -1, 0, 1, 2, ... 15 indicating the stream # within which to search. * : -1 (aka INVALID_SUPPL_STRM) implies search across ALL streams. * histinfo : A pointer to the repl_histinfo to be filled in. Contents might have been modified even on error return. * Description: * If strm_idx=-1 * ----------------- * Given an input "seqno", locate the histinfo record (from ANY stream) in the instance file whose "start_seqno" * corresponds to "seqno-1". * If strm_idx=0 * ---------------- * Given an input "seqno", locate the histinfo record (from 0th stream) in the instance file whose "start_seqno" * corresponds to "seqno-1". * If strm_idx=1,2,...,15 * ------------------------- * Given an input "seqno", locate the histinfo record (from "strm_index"th stream) in the instance file * whose "strm_seqno" (not start_seqno) corresponds to "seqno-1". * Return Value: * 0, on success * ERR_REPLINSTNOHIST, if "seqno" is NOT present in the instance file history range. There are two cases to consider here. * If there was an error fetching a history record, "histinfo->histinfo_num" will be set to INVALID_HISTINFO_NUM. * Otherwise, if we ran out of history records, "histinfo" will point to the 0th history record corresponding to "strm_idx". */ int4 repl_inst_histinfo_find_seqno(seq_num seqno, int4 strm_idx, repl_histinfo *histinfo) { unix_db_info *udi; int4 histnum, status; seq_num cur_seqno; # ifdef DEBUG seq_num prev_seqno; int4 prev_histnum; # endif repl_inst_hdr_ptr_t inst_hdr; assert((NULL != jnlpool) && (NULL != jnlpool->jnlpool_dummy_reg)); udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); /* ROLLBACK -FORWARD should not call this function */ assert(udi->s_addrs.now_crit || jgbl.mur_rollback); assert(0 != seqno); inst_hdr = jnlpool->repl_inst_filehdr; assert(NULL != inst_hdr); assert((INVALID_SUPPL_STRM == strm_idx) || inst_hdr->is_supplementary && (0 <= strm_idx) && (MAX_SUPPL_STRMS > strm_idx)); assert(inst_hdr->num_histinfo <= inst_hdr->num_alloc_histinfo); if (INVALID_SUPPL_STRM == strm_idx) histnum = inst_hdr->num_histinfo - 1; else histnum = inst_hdr->last_histinfo_num[strm_idx]; assert(-1 == INVALID_HISTINFO_NUM); /* so we can safely decrement 0 and reach -1 i.e. an invalid history number */ DEBUG_ONLY(prev_seqno = 0;) do { assert(histnum < inst_hdr->num_histinfo); assert(INVALID_HISTINFO_NUM <= histnum); if (INVALID_HISTINFO_NUM == histnum) return ERR_REPLINSTNOHIST; status = repl_inst_histinfo_get(histnum, histinfo); if (0 != status) { assert(FALSE); histinfo->histinfo_num = INVALID_HISTINFO_NUM; /* signal to caller this is an out-of-design situation */ return ERR_REPLINSTNOHIST; } assert((INVALID_SUPPL_STRM == strm_idx) || (strm_idx == histinfo->strm_index)); cur_seqno = (0 < strm_idx) ? histinfo->strm_seqno : histinfo->start_seqno; assert(cur_seqno); assert((0 == prev_seqno) || (prev_seqno > cur_seqno) || ((INVALID_SUPPL_STRM == strm_idx) && (prev_seqno == cur_seqno))); DEBUG_ONLY(prev_seqno = cur_seqno;) if (seqno > cur_seqno) break; DEBUG_ONLY(prev_histnum = histnum;) histnum = (INVALID_SUPPL_STRM == strm_idx) ? (histnum - 1) : histinfo->prev_histinfo_num; } while (TRUE); return 0; } /* This function finds the histinfo in the local replication instance file corresponding to seqno "seqno-1". * It is a wrapper on top of the function "repl_inst_histinfo_find_seqno" which additionally does error checking. * For the case where "repl_inst_histinfo_find_seqno" returns 0 with a -1 histinfo_num, this function returns ERR_REPLINSTNOHIST. */ int4 repl_inst_wrapper_histinfo_find_seqno(seq_num seqno, int4 strm_idx, repl_histinfo *local_histinfo) { unix_db_info *udi; char histdetail[MAX_REPL_OPMSG_LEN]; int4 status; repl_histinfo *next_histinfo; assert((NULL != jnlpool) && (NULL != jnlpool->jnlpool_dummy_reg)); udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); /* ROLLBACK -FORWARD should not call this function */ assert(udi->s_addrs.now_crit || jgbl.mur_rollback); assert(NULL != jnlpool->repl_inst_filehdr); /* journal pool should be set up */ assert((is_src_server && ((INVALID_SUPPL_STRM == strm_index) || (0 == strm_index))) || (!is_src_server && ((INVALID_SUPPL_STRM == strm_index) || ((0 <= strm_index) && (MAX_SUPPL_STRMS > strm_index))))); status = repl_inst_histinfo_find_seqno(seqno, strm_idx, local_histinfo); assert((0 == status) || (ERR_REPLINSTNOHIST == status)); /* the only error returned by "repl_inst_histinfo_find_seqno" */ if (0 != status) { status = ERR_REPLINSTNOHIST; SNPRINTF(histdetail, MAX_REPL_OPMSG_LEN, "seqno "INT8_FMT" "INT8_FMTX, seqno - 1, seqno - 1); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_REPLINSTNOHIST, 4, LEN_AND_STR(histdetail), LEN_AND_STR(udi->fn)); } else assert(0 <= local_histinfo->histinfo_num); return status; } /* Description: * Add a new histinfo record to the replication instance file. * Parameters: * histinfo : A pointer to the histinfo structure to be added to the instance file. * Return Value: * None * Errors: * Issues ERR_REPLINSTSEQORD error if new histinfo will cause seqno to be out of order. */ void repl_inst_histinfo_add(repl_histinfo *histinfo) { boolean_t is_supplementary, start_seqno_equal; int4 histinfo_num, strm_histinfo_num, prev_histinfo_num, status; int strm_idx, idx; off_t offset; repl_histinfo *last_histinfo, last_histrec, *last_strm_histinfo, last_strm_histrec; repl_histinfo last2_histinfo, *prev_strm_histinfo, prev_strm_histrec; seq_num histinfo_strm_seqno, prev_strm_seqno; unix_db_info *udi; assert((NULL != jnlpool) && (NULL != jnlpool->jnlpool_dummy_reg)); udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); assert(udi->s_addrs.now_crit); assert(NULL != jnlpool->repl_inst_filehdr); assert(jnlpool->repl_inst_filehdr->num_histinfo <= jnlpool->repl_inst_filehdr->num_alloc_histinfo); histinfo_num = jnlpool->repl_inst_filehdr->num_histinfo; assert(0 <= histinfo_num); strm_idx = histinfo->strm_index; /* Assert that the very first history record in any instance file (irrespective of whether the * instance is a root primary or propagating primary) should correspond to stream-0. */ assert((0 < histinfo_num) || (0 == strm_idx)); is_supplementary = jnlpool->repl_inst_filehdr->is_supplementary; assert(!is_supplementary && (0 == strm_idx) || (is_supplementary && (0 <= strm_idx) && (MAX_SUPPL_STRMS > strm_idx))); /* If -updateresync is specified and instance is not supplementary, then there better be NO history records */ assert((HISTINFO_TYPE_UPDRESYNC != histinfo->history_type) || is_supplementary || (0 == histinfo_num)); assert(NULL != jnlpool->jnlpool_ctl); if (strm_idx && !jnlpool->jnlpool_ctl->upd_disabled) { /* A non-supplementary stream history record is being written into a supplementary root primary instance. * Convert the history record as appropriate. See below macro definition for more comments on the conversion. */ CONVERT_NONSUPPL2SUPPL_HISTINFO(histinfo, jnlpool->jnlpool_ctl) } if (0 < histinfo_num) { last_histinfo = &last_histrec; status = repl_inst_histinfo_get(histinfo_num - 1, last_histinfo); assert(0 == status); /* Since histinfo_num-1 we are passing is >=0 and < num_histinfo */ assert(jnlpool->jnlpool_ctl->last_histinfo_seqno == last_histinfo->start_seqno); if (histinfo->start_seqno < last_histinfo->start_seqno) { /* cannot create histinfo with out-of-order start_seqno */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_REPLINSTSEQORD, 6, LEN_AND_LIT("New history record"), &histinfo->start_seqno, &last_histinfo->start_seqno, LEN_AND_STR(udi->fn)); } } strm_histinfo_num = jnlpool->repl_inst_filehdr->last_histinfo_num[strm_idx]; prev_histinfo_num = strm_histinfo_num; if (0 <= strm_histinfo_num) { assert(strm_histinfo_num < histinfo_num); if (strm_histinfo_num != (histinfo_num - 1)) { last_strm_histinfo = &last_strm_histrec; status = repl_inst_histinfo_get(strm_histinfo_num, last_strm_histinfo); assert(0 == status); /* Since the strm_histinfo_num we are passing is >=0 and < num_histinfo */ } else { /* Had read this history record just now from the instance file. Use it and avoid another read */ last_strm_histinfo = last_histinfo; } assert(strm_idx == last_strm_histinfo->strm_index); /* Check if the history record to be added has the same histinfo content as the last history record * already present in the instance file (in the stream of interest). This is possible in case of a secondary * where the receiver was receiving journal records (from the primary) for a while, was shut down and then * restarted. Same instance is sending information so no new histinfo information needed. Return right away. * The only exception is if this is a supplementary instance and the new history record is an UPDATERESYNC * type of record in which case it is possible the two histories have the histinfo content identical but * have different start_seqnos. In this case, some updates went in between the two histories so we want * to record the input history as a separate record instead of returning (since this signals the beginning * of a new stream of updates). */ if ((!is_supplementary || (HISTINFO_TYPE_UPDRESYNC != histinfo->history_type)) && !STRCMP(last_strm_histinfo->root_primary_instname, histinfo->root_primary_instname) && (last_strm_histinfo->root_primary_cycle == histinfo->root_primary_cycle) && (last_strm_histinfo->creator_pid == histinfo->creator_pid) && (last_strm_histinfo->created_time == histinfo->created_time)) { return; } assert((histinfo->start_seqno != last_strm_histinfo->start_seqno) || (histinfo->strm_seqno == last_strm_histinfo->strm_seqno) || (HISTINFO_TYPE_NORESYNC == histinfo->history_type) || (HISTINFO_TYPE_UPDRESYNC == histinfo->history_type)); /* If stream seqnos match between input history and last stream specific history in the instance file, * make sure the to-be-written history record skips past the last stream specific history record (as we * expect a decreasing sequence of strm_seqnos in the "prev_histinfo_num" linked list of history records). * The only exception is if we are a supplementary instance and this is stream # 0. In that case, only if * the start_seqno is also equal, will we skip. This is because if start_seqno is not equal, the stream # 0 * history records identify a range of updates that happened (even if the updates happened in non-zero * stream #s) and that is used by history record matching between two supplementary instances at replication * connection time. * The same skipping logic applies to "start_seqno" in case the instance is non-supplementary (in which case * the "strm_seqno" field is 0). */ histinfo_strm_seqno = histinfo->strm_seqno; prev_strm_seqno = last_strm_histinfo->strm_seqno; if (histinfo_strm_seqno == prev_strm_seqno) { start_seqno_equal = (histinfo->start_seqno == last_strm_histinfo->start_seqno); if (histinfo_strm_seqno && strm_idx || start_seqno_equal) { assert(prev_histinfo_num > last_strm_histinfo->prev_histinfo_num); prev_histinfo_num = last_strm_histinfo->prev_histinfo_num; } if (start_seqno_equal && (strm_histinfo_num == (histinfo_num - 1)) && (histinfo->history_type == last_strm_histinfo->history_type)) { /* Starting seqno of the last histinfo in the instance file matches the input histinfo. * This means there are no journal records corresponding to the input stream in the journal * files after the last histinfo (which happens to be same as the input stream) was written * in the instance file. Overwrite the last histinfo with the new histinfo information before * writing new journal records. * Note: The check for history_type above is to take into account a supplementary * instance where a HISTINFO_TYPE_UPDRESYNC type history record is first written when A->P * connect for the first time and later a HISTINFO_TYPE_NORMAL record is written when A->P * connect for the second time. If there were no intervening updates on P from the disconnect * to the reconnect, we do not want to overwrite the HISTINFO_TYPE_UPDRESYNC type record * as that will confuse Q in a A->P->Q configuration when Q receives updates from A (GTM-8657). * If the history types do not match, treat the two history records as different and avoid * overwriting even if the start_seqno matches. */ histinfo_num--; } } else if (HISTINFO_TYPE_NORESYNC == histinfo->history_type) { /* Determine the correct value of "prev_histinfo_num" */ prev_strm_histinfo = &prev_strm_histrec; prev_strm_histrec = *last_strm_histinfo; assert(prev_strm_seqno == prev_strm_histinfo->strm_seqno); while (histinfo_strm_seqno <= prev_strm_seqno) { prev_histinfo_num = prev_strm_histinfo->prev_histinfo_num; assert(INVALID_HISTINFO_NUM != prev_histinfo_num); if (INVALID_HISTINFO_NUM == prev_histinfo_num) break; status = repl_inst_histinfo_get(prev_histinfo_num, prev_strm_histinfo); assert(0 == status); /* Since prev_histinfo_num we are passing is >=0 and < num_histinfo */ assert(prev_strm_seqno > prev_strm_histinfo->strm_seqno); prev_strm_seqno = prev_strm_histinfo->strm_seqno; } } } /* Assert that the history record we are going to add is in sync with the current seqno state of the instance */ assert(jnlpool->jnlpool_ctl->jnl_seqno == histinfo->start_seqno); assert(jnlpool->jnlpool_ctl->strm_seqno[histinfo->strm_index] == histinfo->strm_seqno); offset = REPL_INST_HISTINFO_START + (SIZEOF(repl_histinfo) * (off_t)histinfo_num); /* Initialize the following members of the repl_histinfo structure. Everything else should be initialized by caller. * histinfo_num * prev_histinfo_num * last_histinfo_num[] */ histinfo->histinfo_num = histinfo_num; histinfo->prev_histinfo_num = (HISTINFO_TYPE_UPDRESYNC == histinfo->history_type) ? INVALID_HISTINFO_NUM : prev_histinfo_num; assert(histinfo->prev_histinfo_num < histinfo->histinfo_num); for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) { assert((jnlpool->repl_inst_filehdr->last_histinfo_num[idx] < histinfo_num) || (idx == strm_idx) && (jnlpool->repl_inst_filehdr->last_histinfo_num[idx] == histinfo_num)); histinfo->last_histinfo_num[idx] = jnlpool->repl_inst_filehdr->last_histinfo_num[idx]; } if (strm_histinfo_num == histinfo_num) { /* The last history record in the instance file is going to be overwritten with another history record of * the same stream. In this case, jnlpool->repl_inst_filehdr->last_histinfo_num[strm_idx] would not reflect a * state of the instance file BEFORE this history record was added. So find the correct value. Thankfully * the last history record (that we are about to overwrite) already has this value so copy it over. */ histinfo->last_histinfo_num[strm_idx] = last_histinfo->last_histinfo_num[strm_idx]; } assert(strm_histinfo_num == jnlpool->repl_inst_filehdr->last_histinfo_num[strm_idx]); assert(strm_histinfo_num <= histinfo_num); assert(strm_histinfo_num >= prev_histinfo_num); assert(histinfo_num > prev_histinfo_num); assert((INVALID_HISTINFO_NUM == histinfo->prev_histinfo_num) || (0 <= histinfo->prev_histinfo_num)); assert(is_supplementary || (prev_histinfo_num == (histinfo_num - 1))); # ifdef DEBUG /* Assert that the prev_histinfo_num list of history records have decreasing "start_seqno" and "strm_seqno" values. * The only exception is stream # 0 for a supplementary instance as described in a previous comment in this function. */ if (INVALID_HISTINFO_NUM != histinfo->prev_histinfo_num) { assert(histinfo->prev_histinfo_num == prev_histinfo_num); status = repl_inst_histinfo_get(prev_histinfo_num, &last2_histinfo); assert(0 == status); /* Since the strm_histinfo_num we are passing is >=0 and < num_histinfo */ assert(strm_idx == last2_histinfo.strm_index); /* they both better have the same stream # */ assert(histinfo->start_seqno > last2_histinfo.start_seqno); assert(!histinfo->strm_seqno || (histinfo->strm_seqno > last2_histinfo.strm_seqno) || (0 == strm_idx)); } /* Assert that the last_histinfo_num fields reflect a state of the instance file that does not include the about-to-be * added history record. This ensures the instance file header will get restored to a valid state in case of a rollback * that truncates exactly at this history record boundary. */ for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) assert(histinfo->last_histinfo_num[idx] < histinfo_num); # endif /* Assert that if this is not the first history record being written into the instance file * it should have a valid 0th stream history record number. This is relied upon by "gtmsource_send_new_histrec" */ assert((0 == histinfo_num) || (INVALID_HISTINFO_NUM != histinfo->last_histinfo_num[0])); repl_inst_write(udi->fn, offset, (sm_uc_ptr_t)histinfo, SIZEOF(repl_histinfo)); /* Update stream specific history number fields in the file header to reflect the latest history addition to this stream */ jnlpool->repl_inst_filehdr->last_histinfo_num[strm_idx] = histinfo_num; /* If -updateresync history record for a non-zero stream #, then initialize strm_group_info in file header */ if ((0 < strm_idx) && (HISTINFO_TYPE_UPDRESYNC == histinfo->history_type)) jnlpool->repl_inst_filehdr->strm_group_info[strm_idx - 1] = histinfo->lms_group; histinfo_num++; if (jnlpool->repl_inst_filehdr->num_alloc_histinfo < histinfo_num) jnlpool->repl_inst_filehdr->num_alloc_histinfo = histinfo_num; jnlpool->repl_inst_filehdr->num_histinfo = histinfo_num; /* Since we are going to flush the file header, take this opportunity to update the current jnl seqno in it. * Note that the current stream jnl seqnos are already udpated inside "repl_inst_flush_filehdr" by the * "COPY_JCTL_STRMSEQNO_TO_INSTHDR_IF_NEEDED" macro. We dont do the current jnl seqno to there because * "repl_inst_histinfo_truncate" invokes "repl_inst_flush_filehdr" with a different jnl seqno than the current. */ jnlpool->repl_inst_filehdr->jnl_seqno = jnlpool->jnlpool_ctl->jnl_seqno; repl_inst_flush_filehdr(); jnlpool->jnlpool_ctl->last_histinfo_seqno = histinfo->start_seqno; repl_inst_sync(udi->fn); /* Harden the new histinfo to disk before any logical records for this arrive. */ return; } /* Description: * Given an input "rollback_seqno", virtually truncate all histinfo records that correspond to seqnos >= "rollback_seqno" * This function also updates other fields (unrelated to histinfo truncation) in the file header * to reflect a clean shutdown by MUPIP JOURNAL ROLLBACK. This function is also invoked by MUPIP BACKUP in order * to ensure the backed up instance file is initialized to reflect a clean shutdown. * Parameters: * rollback_seqno : The seqno after which all histinfo records have to be truncated. * Note: In case of a supplementary instance file, this function expects the caller to have * set "inst_hdr->strm_seqno[]" to reflect the "rollback_seqno". * Return Value: * Sequence number (start_seqno) of the last history record in the instance file * Errors: * Issues ERR_REPLINSTNOHIST message if the call to "repl_inst_histinfo_find_seqno" returned an error. */ seq_num repl_inst_histinfo_truncate(seq_num rollback_seqno) { char histdetail[MAX_REPL_OPMSG_LEN]; int4 status, index, num_histinfo, last_histnum; int idx; repl_histinfo temphistinfo, nexthistinfo, strmhistinfo; repl_inst_hdr_ptr_t inst_hdr; unix_db_info *udi; gd_region *reg; seq_num last_histinfo_seqno = 0; assert(NULL != jnlpool); reg = jnlpool->jnlpool_dummy_reg; if (NULL == reg) reg = recvpool.recvpool_dummy_reg; assert(NULL != reg); udi = FILE_INFO(reg); assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); /* ROLLBACK -FORWARD should not call this function */ assert(in_backup || jgbl.mur_rollback); /* Only ROLLBACK or BACKUP calls this function */ assert(udi->s_addrs.now_crit || jgbl.mur_rollback); inst_hdr = jnlpool->repl_inst_filehdr; assert(NULL != inst_hdr); /* Should have been set when mupip rollback invoked "mu_replpool_grab_sem" */ num_histinfo = inst_hdr->num_histinfo; if (0 != num_histinfo) { status = repl_inst_histinfo_find_seqno(rollback_seqno, INVALID_SUPPL_STRM, &temphistinfo); if (0 != status) { assert(ERR_REPLINSTNOHIST == status); /* the only error returned by "repl_inst_histinfo_find_seqno" */ if ((INVALID_HISTINFO_NUM == temphistinfo.histinfo_num) || (temphistinfo.start_seqno != rollback_seqno)) { /* The truncation seqno is not the starting seqno of the instance file. In that case, issue * a RELINSTNOHIST warning message even though rollback is going to proceed anycase. */ assert(FALSE); NON_GTM64_ONLY(SNPRINTF(histdetail, MAX_REPL_OPMSG_LEN, "seqno [0x%llx]", rollback_seqno - 1)); GTM64_ONLY(SNPRINTF(histdetail, MAX_REPL_OPMSG_LEN, "seqno [0x%lx]", rollback_seqno - 1)); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) MAKE_MSG_WARNING(ERR_REPLINSTNOHIST), 4, LEN_AND_STR(histdetail), LEN_AND_STR(udi->fn)); } index = -1; /* Since we are rolling back all history records in the instance file, * clear all of "strm_group_info[]" and "last_histinfo_num[]" arrays. * The following logic is similar to that in "repl_inst_create" to initialize the above 2 fields. * Note that we keep "jnl_seqno" and "strm_seqno" set to whatever value it came in with (as opposed * to setting it to 0). This is different from what is done in "repl_inst_create" because we want * to keep these set to a non-zero value if possible (see detailed comment below where "jnl_seqno" * gets set). Keeping "jnl_seqno" at a non-zero value necessitates keeping "strm_seqno" at a non-zero * value as well in order to avoid REPLINSTDBSTRM errors at source server startup. */ assert(MAX_SUPPL_STRMS == ARRAYSIZE(inst_hdr->last_histinfo_num)); for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) inst_hdr->last_histinfo_num[idx] = INVALID_HISTINFO_NUM; if (inst_hdr->is_supplementary) { assert(MAX_SUPPL_STRMS == ARRAYSIZE(inst_hdr->strm_seqno)); assert(SIZEOF(seq_num) == SIZEOF(inst_hdr->strm_seqno[0])); assert((MAX_SUPPL_STRMS - 1) == ARRAYSIZE(inst_hdr->strm_group_info)); assert(SIZEOF(repl_inst_uuid) == SIZEOF(inst_hdr->strm_group_info[0])); /* Keep the strm_seqno 0 for those streams which this instance has never used/communicated. For all * other stream#, set the strm_seqno to 1 if the current value of strm_seqno is 0. If the current * value of strm_seqno is non-zero, let it stay as it is (see comment above about strm_seqno). * This way, if this instance reconnects after the ROLLBACK to the same instance it was * communicating before, we avoid issuing REPLINSTNOHIST thereby making it user-friendly. * Note: The LMS group info for stream# "i" is found in strm_group_info[i - 1] (used below) */ for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) { if ((idx == 0) || (IS_REPL_INST_UUID_NON_NULL(inst_hdr->strm_group_info[idx - 1]))) { if (0 == inst_hdr->strm_seqno[idx]) inst_hdr->strm_seqno[idx] = 1; } # ifdef DEBUG else assert(0 == inst_hdr->strm_seqno[idx]); # endif } /* Leave the LMS group information as-is in the instance file header. By doing so, we avoid cases * where receiver server continuing after the rollback issues an INSUNKNOWN error. While this is * a valid error, we try to make it as user-friendly as possible. */ } } else { index = temphistinfo.histinfo_num; assert(temphistinfo.start_seqno < rollback_seqno); assert(0 <= index); assert(index <= (num_histinfo - 1)); last_histinfo_seqno = temphistinfo.start_seqno; if (index < (num_histinfo - 1)) { status = repl_inst_histinfo_get(index + 1, &nexthistinfo); assert(0 == status); /* Since the histinfo_num we are passing is >=0 and <= num_histinfo */ assert(nexthistinfo.start_seqno >= rollback_seqno); assert(nexthistinfo.histinfo_num == (index + 1)); /* Copy over information from this history record back to the instance file header */ assert(SIZEOF(inst_hdr->last_histinfo_num) == SIZEOF(nexthistinfo.last_histinfo_num)); memcpy(inst_hdr->last_histinfo_num, nexthistinfo.last_histinfo_num, SIZEOF(nexthistinfo.last_histinfo_num)); if (inst_hdr->is_supplementary) { /* inst_hdr->strm_seqno[] is already set by caller */ assert((MAX_SUPPL_STRMS - 1) == ARRAYSIZE(inst_hdr->strm_group_info)); for (idx = 0; idx < (MAX_SUPPL_STRMS - 1); idx++) { last_histnum = nexthistinfo.last_histinfo_num[idx + 1]; assert(INVALID_HISTINFO_NUM <= last_histnum); assert(last_histnum < nexthistinfo.histinfo_num); if (INVALID_HISTINFO_NUM != last_histnum) { status = repl_inst_histinfo_get(last_histnum, &strmhistinfo); assert(0 == status); assert(strmhistinfo.histinfo_num == last_histnum); assert(strmhistinfo.start_seqno < rollback_seqno); assert(strmhistinfo.strm_index); assert(MAX_SUPPL_STRMS > strmhistinfo.strm_index); assert(IS_REPL_INST_UUID_NON_NULL(strmhistinfo.lms_group)); inst_hdr->strm_group_info[idx] = strmhistinfo.lms_group; } else if (IS_REPL_INST_UUID_NON_NULL(inst_hdr->strm_group_info[idx])) { /* stream# (idx + 1) has a non-zero UUID information in the file header * but all the history records corresponding to this stream are now * truncated. This also implies that strm_seqno of this stream is reset * to zero by ROLLBACK. To avoid REPLINSTNOHIST next time a communication * happens with the instance corresponding to stream# idx + 1, set the * strm_seqno to 1. * Note: The LMS group info for stream-i is found in strm_group_info[i - 1] */ inst_hdr->strm_seqno[idx + 1] = 1; /* Also, leave the LMS group information for stream# idx + 1 as-is in the * instance file header By doing so, we avoid cases where receiver server * continuing after the rollback issues an INSUNKNOWN error. While this is * a valid error, we try to mae it as user-friendly as possible. */ } } } } /* else index == "num_histinfo - 1" so no changes needed to "last_histinfo_num[]" * or "strm_seqno[]" or "strm_group_info[]" arrays. */ } index++; assert((index == inst_hdr->num_histinfo) || ((inst_hdr->num_histinfo >= 0) && (inst_hdr->num_alloc_histinfo > index))); inst_hdr->num_histinfo = index; } /* Reset "jnl_seqno" to the rollback seqno so future REPLINSTDBMATCH errors are avoided in "gtmsource_seqno_init". * Note that it is possible inst_hdr->num_histinfo is 0 at this point (i.e. no history records). In that case, * repl_inst_create sets the "jnl_seqno" to 0 whereas we might set it here to a potentially non-zero value. * That is because repl_inst_create does not go through the database and get the max of the reg_seqnos to figure * out the instance jnl_seqno. Hence it sets it to a value of 0 indicating the source server that starts up the * instance to fill it in with a non-zero value. On the other hand, rollback or backup (both of which can call * this function "repl_inst_histinfo_truncate") know exactly what the instance seqno is and so can safely set the * "jnl_seqno" to a non-zero value even though there are no history records. Setting it to a non-zero value whenever * possible is useful for example when we ship a backup of a freshly created live non-supplementary instance (with * jnl_seqno of 1) to be used as input to the -updateresync qualifier of a receiver startup on a supplementary * instance. In this case, if the backup had a jnl_seqno of 0, the startup would fail. But since it has a non-zero * "jnl_seqno" (even though there are no history records), the initial handshake between the non-supplementary and * supplementary instances is possible (they avoid history record exchanges due to jnl_seqno == 1). A zero jnl_seqno * would have resulted in a UPDSYNCINSTFILE error in the initial handshake. */ inst_hdr->jnl_seqno = rollback_seqno; /* Reset sem/shm ids to reflect a clean shutdown so future REPLREQRUNDOWN errors are avoided at "jnlpool_init" time */ if (!jgbl.mur_rollback) { /* Reset semid/sem_ctime fields in the instance file header. */ /* Reset "crash" to FALSE so future REPLREQROLLBACK errors are avoided at "jnlpool_init" time */ inst_hdr->crash = FALSE; inst_hdr->jnlpool_semid = INVALID_SEMID; inst_hdr->jnlpool_shmid = INVALID_SHMID; inst_hdr->jnlpool_semid_ctime = 0; inst_hdr->jnlpool_shmid_ctime = 0; inst_hdr->recvpool_semid = INVALID_SEMID; /* Just in case it is not already reset */ inst_hdr->recvpool_shmid = INVALID_SHMID; /* Just in case it is not already reset */ inst_hdr->recvpool_semid_ctime = 0; inst_hdr->recvpool_shmid_ctime = 0; } /* else for rollback, we reset the IPC fields in mu_replpool_release_sem() and crash in mur_close_files */ /* Flush all file header changes in jnlpool->repl_inst_filehdr to disk */ repl_inst_flush_filehdr(); assert((0 == inst_hdr->num_histinfo) || (0 < last_histinfo_seqno)); return last_histinfo_seqno; } /* Description: * Flushes the instance file header pointed to by "jnlpool->repl_inst_filehdr" to disk. * Parameters: * None * Return Value: * None */ void repl_inst_flush_filehdr() { unix_db_info *udi; assert(NULL != jnlpool); udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); /* We could come here from several paths. If journal pool exists, we would have done a grab_lock. This covers most of the * cases. If the journal pool doesn't exist, then we could come here from one of the following places * * ROLLBACK (online/noonline): * We already hold standalone access on the journal pool and if the journal pool exists, we also hold the journal pool * lock * * MUPIP RUNDOWN -> mu_rndwn_repl_instance: * We hold the ftok on the instance file and have already made sure that no one else is attached to the journal pool. Even * though we don't hold the access control on the journal pool, no one else can startup at this point because they need * the ftok for which they will have to wait. * * gtmsource_shutdown -> repl_inst_jnlpool_reset: * We hold the ftok on the instance file and have already made sure that no one else is attached to the journal pool. Even * though we don't hold the access control on the journal pool, no one else can startup at this point because they need * the ftok for which they will have to wait. * * gtmrecv_shutdown -> repl_inst_recvpool_reset: * Same as above. * So, in all cases, we are guaranteed that the following code is mutually exclusive (which is what we want). */ assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); /* ROLLBACK -FORWARD should not call this function */ assert(udi->s_addrs.now_crit || udi->grabbed_ftok_sem || (jgbl.mur_rollback && holds_sem[SOURCE][JNL_POOL_ACCESS_SEM])); if (jnlpool->jnlpool_dummy_reg->open) COPY_JCTL_STRMSEQNO_TO_INSTHDR_IF_NEEDED; /* Keep the file header copy of "strm_seqno" uptodate with jnlpool_ctl */ assert((NULL == jnlpool->jnlpool_ctl) || udi->s_addrs.now_crit); assert(NULL != jnlpool->repl_inst_filehdr); /* flush the instance file header */ repl_inst_write(udi->fn, (off_t)0, (sm_uc_ptr_t)jnlpool->repl_inst_filehdr, REPL_INST_HDR_SIZE); } /* Description: * Flushes the "gtmsrc_lcl" structure corresponding to the jnlpool->gtmsource_local structure for the * calling source server. Updates "gtmsource_local->last_flush_resync_seqno" to equal "gtmsource_local->read_jnl_seqno" * Parameters: * None * Return Value: * None */ void repl_inst_flush_gtmsrc_lcl() { unix_db_info *udi; int4 index; off_t offset; gtmsrc_lcl_ptr_t gtmsrclcl_ptr; assert(NULL != jnlpool); udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); assert(!jgbl.mur_rollback); /* Rollback should never reach here */ assert(udi->s_addrs.now_crit); assert(NULL != jnlpool->gtmsource_local); index = jnlpool->gtmsource_local->gtmsrc_lcl_array_index; assert(0 <= index); assert(jnlpool->gtmsource_local == &jnlpool->gtmsource_local_array[index]); gtmsrclcl_ptr = &jnlpool->gtmsrc_lcl_array[index]; assert(jnlpool->jnlpool_dummy_reg->open); /* journal pool exists and this process has done "jnlpool_init" */ /* Copy each field from "gtmsource_local" to "gtmsrc_lcl" before flushing it to disk. * Do not need the journal pool lock, as we are the only ones reading/updating the below fields * in "gtmsource_local" or "gtmsrc_lcl". */ COPY_GTMSOURCELOCAL_TO_GTMSRCLCL(jnlpool->gtmsource_local, gtmsrclcl_ptr); offset = REPL_INST_HDR_SIZE + (SIZEOF(gtmsrc_lcl) * (off_t)index); repl_inst_write(udi->fn, offset, (sm_uc_ptr_t)gtmsrclcl_ptr, SIZEOF(gtmsrc_lcl)); jnlpool->gtmsource_local->last_flush_resync_seqno = jnlpool->gtmsource_local->read_jnl_seqno; } /* Description: * Flushes the "repl_inst_hdr" and "gtmsrc_lcl" sections in the journal pool to the on disk copy of the instance file. * Parameters: * None * Return Value: * None */ void repl_inst_flush_jnlpool(boolean_t reset_replpool_fields, boolean_t reset_crash) { unix_db_info *udi; int4 index; gtmsrc_lcl_ptr_t gtmsrclcl_ptr; gtmsource_local_ptr_t gtmsourcelocal_ptr; assert((NULL != jnlpool) && (NULL != jnlpool->jnlpool_dummy_reg)); udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); /* This function should be invoked only if the caller determines this is last process attached to the journal pool. * Since the ftok lock on the instance file is already held, no other process will be allowed to attach to the * journal pool and hence this is the only process having access to the journal pool during this function. The only * exception is if it is invoked from mur_open_files for Online Rollback. But, in that case Online Rollback will be * holding the access control. Any process calling this function, needs the access control semaphore and hence will * wait for Online Rollback to complete. */ assert(udi->grabbed_ftok_sem || (jgbl.onlnrlbk && udi->s_addrs.now_crit)); assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); assert(NULL != jnlpool->gtmsource_local_array); assert(NULL != jnlpool->gtmsrc_lcl_array); assert(NULL != jnlpool->repl_inst_filehdr); assert(NULL != jnlpool->jnlpool_ctl); assert((sm_uc_ptr_t)jnlpool->gtmsrc_lcl_array == (sm_uc_ptr_t)jnlpool->repl_inst_filehdr + REPL_INST_HDR_SIZE); /* Reset the instance file header fields (if needed) before flushing and removing the journal pool shared memory */ if (reset_crash) jnlpool->repl_inst_filehdr->crash = FALSE; if (!jgbl.onlnrlbk) { if (reset_replpool_fields) { jnlpool->repl_inst_filehdr->jnlpool_semid = INVALID_SEMID; jnlpool->repl_inst_filehdr->jnlpool_shmid = INVALID_SHMID; jnlpool->repl_inst_filehdr->recvpool_semid = INVALID_SEMID; /* Just in case it is not already reset */ jnlpool->repl_inst_filehdr->recvpool_shmid = INVALID_SHMID; /* Just in case it is not already reset */ } } /* If the source server that created the journal pool died before it was completely initialized in "gtmsource_seqno_init" * do not copy seqnos from the journal pool into the instance file header. Instead keep the instance file header unchanged. */ if (jnlpool->jnlpool_ctl->pool_initialized) { assert(jnlpool->jnlpool_ctl->start_jnl_seqno); assert(jnlpool->jnlpool_ctl->jnl_seqno); jnlpool->repl_inst_filehdr->jnl_seqno = jnlpool->jnlpool_ctl->jnl_seqno; COPY_JCTL_STRMSEQNO_TO_INSTHDR_IF_NEEDED; /* Keep the file header copy of "strm_seqno" uptodate with jnlpool_ctl */ /* Copy all "gtmsource_local" to corresponding "gtmsrc_lcl" structures before flushing to instance file */ gtmsourcelocal_ptr = &jnlpool->gtmsource_local_array[0]; gtmsrclcl_ptr = &jnlpool->gtmsrc_lcl_array[0]; for (index = 0; index < NUM_GTMSRC_LCL; index++, gtmsourcelocal_ptr++, gtmsrclcl_ptr++) COPY_GTMSOURCELOCAL_TO_GTMSRCLCL(gtmsourcelocal_ptr, gtmsrclcl_ptr); repl_inst_write(udi->fn, (off_t)0, (sm_uc_ptr_t)jnlpool->repl_inst_filehdr, REPL_INST_HDR_SIZE + GTMSRC_LCL_SIZE); } else repl_inst_write(udi->fn, (off_t)0, (sm_uc_ptr_t)jnlpool->repl_inst_filehdr, REPL_INST_HDR_SIZE); } /* This function determines if this replication instance was formerly a root primary. It finds this out by looking at the * last histinfo record in the instance file and comparing the "root_primary_instname" field there with this instance name. * If they are the same, it means the last histinfo was generated by this instance and hence was a root primary then. This * function will only be invoked by a propagating primary instance (RECEIVER SERVER or ROLLBACK -FETCHRESYNC) or a rootprimary * supplementary instance. * * It returns TRUE only if the instance file header field "was_rootprimary" is TRUE and if the last histinfo record was generated * by this instance. It returns FALSE otherwise. */ boolean_t repl_inst_was_rootprimary(void) { int4 histinfo_num, status; repl_histinfo temphistinfo, *last_histinfo = &temphistinfo; boolean_t was_rootprimary, was_crit = FALSE; sgmnt_addrs *csa; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); /* ROLLBACK -FORWARD should not call this function */ if ((NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl)) { /* If the journal pool is available (indicated by NULL != jnlpool_ctl), we expect jnlpool_dummy_reg to be open. * The only exception is online rollback which doesn't do a jnlpool_init thereby leaving jnlpool_dummy_reg->open * to be FALSE. Assert accordingly. */ assert(((NULL != jnlpool->jnlpool_dummy_reg) && jnlpool->jnlpool_dummy_reg->open) || jgbl.onlnrlbk || (jgbl.mur_rollback && INST_FREEZE_ON_ERROR_POLICY)); csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; ASSERT_VALID_JNLPOOL(csa); assert(csa->now_crit); if (!jnlpool->jnlpool_ctl->upd_disabled) { /* This instance is a root primary supplementary instance where an online rollback is being run * (either explicitly or implicitly through a receiver server started with -autorollback) * Since this instance is currently a rootprimary, was_rootprimary is FALSE. Return right away. */ return FALSE; } } else assert(jgbl.mur_rollback); /* ROLLBACK (holding access control lock) can come here without journal pool */ /* If this is a supplementary instance, look at the last history record corresponding to the 0th stream index. * If not, look at the last history record. This is okay since there is no multiple streams in this case. */ histinfo_num = (!jnlpool->repl_inst_filehdr->is_supplementary) ? (jnlpool->repl_inst_filehdr->num_histinfo - 1) : jnlpool->repl_inst_filehdr->last_histinfo_num[0]; was_rootprimary = jnlpool->repl_inst_filehdr->was_rootprimary; assert(histinfo_num < jnlpool->repl_inst_filehdr->num_alloc_histinfo); if (was_rootprimary && (0 <= histinfo_num)) { status = repl_inst_histinfo_get(histinfo_num, last_histinfo); assert(0 == status); /* Since the histinfo_num we are passing is >=0 and < num_histinfo */ was_rootprimary = !STRCMP(last_histinfo->root_primary_instname, jnlpool->repl_inst_filehdr->inst_info.this_instname); } else was_rootprimary = FALSE; return was_rootprimary; } /* This function resets "zqgblmod_seqno" and "zqgblmod_tn" in all replicated database file headers to 0. * This shares a lot of its code with the function "gtmsource_update_zqgblmod_seqno_and_tn". * Any changes there might need to be reflected here. */ int4 repl_inst_reset_zqgblmod_seqno_and_tn(void) { gd_region *reg, *reg_top; int ret; boolean_t all_files_open; sgmnt_addrs *repl_csa; ret = SS_NORMAL; /* assume success */ /* source server calls this from gtmsource_losttncomplete which always holds the journal pool access control semaphore * Assert this. */ assert(is_rcvr_server || holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); if (0 == jnlpool->jnlpool_ctl->max_zqgblmod_seqno) { /* Already reset to 0 by a previous call to this function. No need to do it again. */ return ret; } /* This function is currently ONLY called by receiver server AND mupip replic -source -losttncomplete * The receiver should have NO GBLDIR or REGION OPEN at this time. Assert that. * is_src_server is not set for -losttncomplete so other check needed */ assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM] || (NULL == gd_header)); if (NULL == gd_header) gvinit(); /* We use the same code dse uses to open all regions but we must make sure they are all open before proceeding. */ all_files_open = region_init(FALSE); if (!all_files_open) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_NOTALLDBOPN); repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; for (reg = gd_header->regions, reg_top = reg + gd_header->n_regions; reg < reg_top; reg++) { assert(reg->open); TP_CHANGE_REG(reg); if (!REPL_ALLOWED(cs_data)) continue; /* csa->hdr->zqgblmod_seqno is modified by the source server and an online rollback (both of these hold the * database crit while doing so). It is also read by fileheader_sync() which does so while holding crit. * To avoid the latter from reading an inconsistent value (i.e neither the pre-update nor the post-update * value, which is possible if the 8-byte operation is not atomic but a sequence of two 4-byte operations * AND if the pre-update and post-update value differ in their most significant 4-bytes) we grab_crit. We * could have used QWCHANGE_IS_READER_CONSISTENT macro (which checks for most significant 4-byte difference) * instead to determine if it is really necessary to grab crit. But, since the update to zqgblmod_seqno is a * rare operation, we decided to play it safe. */ assert(!cs_addrs->hold_onto_crit); /* this ensures we can safely do unconditional grab_crit and rel_crit */ grab_crit(reg, WS_37); if (cs_addrs->onln_rlbk_cycle != cs_addrs->nl->onln_rlbk_cycle) { /* concurrent online rollback */ assert(is_rcvr_server); SYNC_ONLN_RLBK_CYCLES; rel_crit(reg); ret = -1; /* failure */ break; } cs_addrs->hdr->zqgblmod_seqno = (seq_num)0; cs_addrs->hdr->zqgblmod_tn = (trans_num)0; rel_crit(reg); } assert((SS_NORMAL == ret) || (reg < reg_top)); if (reg >= reg_top) { assert(!repl_csa->hold_onto_crit); /* so we can do unconditional grab_lock and rel_lock */ /* Since the source server holds the access control at this point, a concurrent online rollback is NOT possible. * But, if we are here from receiver code, then we cannot guarantee this. So, get the journal pool lock and if * an online rollback is detected, return without resetting max_zqgblmod_seqno. The caller knows to take appropriate * action (on seeing -1 as the return code). */ grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY); if (repl_csa->onln_rlbk_cycle != jnlpool->jnlpool_ctl->onln_rlbk_cycle) { assert(is_rcvr_server); SYNC_ONLN_RLBK_CYCLES; rel_lock(jnlpool->jnlpool_dummy_reg); ret = -1; /* failure */ } else { jnlpool->jnlpool_ctl->max_zqgblmod_seqno = 0; rel_lock(jnlpool->jnlpool_dummy_reg); } } for (reg = gd_header->regions, reg_top = reg + gd_header->n_regions; reg < reg_top; reg++) { /* Rundown all databases that we opened as we dont need them anymore. This is not done in the previous * loop as it has to wait until the ftok semaphore of the instance file has been released as otherwise * an assert in "gds_rundown" will fail as it tries to get the ftok semaphore of the database while holding * another ftok semaphore already. */ assert(reg->open); TP_CHANGE_REG(reg); assert(!cs_addrs->now_crit); ret |= gds_rundown(CLEANUP_UDI_TRUE); } assert(!repl_csa->now_crit); return ret; } fis-gtm-V7.0-005/sr_unix/repl_instance.h0000644000032200000250000003363114342376330017046 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ #ifndef REPL_INSTANCE_INCLUDED #define REPL_INSTANCE_INCLUDED /* MAJOR format of the instance file; Any change to this means recreating instance file. * This needs to be changed whenever the layout of the instance file (file header and/or contents) changes in a major way. */ #define GDS_REPL_INST_LABEL "GDSRPLUNX05" #define GDS_REPL_INST_LABEL_SZ 12 /* The current MINOR version. Changes to this would mean an on-the-fly upgrade of the instance file. * This needs to be changed whenever the changes to the file header layout of the instance file are small enough * that it is possible to do an online upgrade of the file header. */ #define GDS_REPL_INST_MINOR_LABEL 1 /* can go upto 255 at which point the major version has to change */ #define IN_REPL_INST_EDIT_FALSE FALSE /* to indicate we are not in a MUPIP REPLIC -EDITINSTANCE */ #define IN_REPL_INST_EDIT_TRUE TRUE /* to indicate we are MUPIP REPLIC -EDITINSTANCE */ #define IN_REPL_INST_EDIT_CHANGE_OFFSET 2 /* to indicate we are in MUPIP REPLIC -EDITINSTANCE -CHANGE */ /* Replication Instance file format * * [size = 512 bytes] File-Header structure "repl_inst_hdr" (size 512 bytes) * [size = 1024 bytes] Array of 16 "gtmsrc_lcl" structures (each size 64 bytes) * [size = 64*n bytes] Variable-length Array of structure "repl_histinfo" (each size 64 bytes) * Each "repl_histinfo" defines the beginning of a range of seqnos (identified by the "start_seqno" field) * that was received from a particular invocation of a root primary instance. The range ends with the * "start_seqno" field of the next histinfo record. * Note: "repl_histinfo" structure is defined in mdef.h (not in this file). * * An uptodate copy of the fixed length section of the instance file (file header and array of gtmsrc_lcl structures) * is maintained in the journal pool. The file on disk is not as uptodate but periodically flushed. */ #define DEF_INST_FN "mumps.repl" typedef struct repl_inst_hdr_struct { unsigned char label[GDS_REPL_INST_LABEL_SZ]; /* format of instance file. initialized to GDS_REPL_INST_LABEL */ unsigned char replinst_minorver; /* minor version of the GT.M release that last touched this file */ unsigned char is_little_endian; /* TRUE if little-endian, FALSE if big-endian */ unsigned char is_64bit; /* TRUE if created by 64-bit GT.M, FALSE if created by 32-bit GT.M */ unsigned char filler_16[1]; int4 jnlpool_semid; /* id of IPC_PRIVATE semaphore created for jnlpool */ int4 jnlpool_shmid; /* id of IPC_PRIVATE shared memory created for jnlpool */ int4 recvpool_semid; /* id of IPC_PRIVATE semaphore created for recvpool */ int4 recvpool_shmid; /* id of IPC_PRIVATE semaphore created for recvpool */ /* All time fields have a NON_GTM64_ONLY filler so all fields in the file header start * at the same offset irrespective of whether time_t is 4-bytes or 8-bytes. */ time_t jnlpool_semid_ctime; /* creation time of IPC_PRIVATE semaphore for jnlpool */ NON_GTM64_ONLY(int4 filler8bytealign_1;) time_t jnlpool_shmid_ctime; /* creation time of IPC_PRIVATE shared memory for jnlpool */ NON_GTM64_ONLY(int4 filler8bytealign_2;) time_t recvpool_semid_ctime; /* creation time of IPC_PRIVATE semaphore for recvpool */ NON_GTM64_ONLY(int4 filler8bytealign_3;) time_t recvpool_shmid_ctime; /* creation time of IPC_PRIVATE shared memory for recvpool */ NON_GTM64_ONLY(int4 filler8bytealign_4;) repl_inst_uuid inst_info; /* Initialized at instance file creation time */ repl_inst_uuid lms_group_info; /* Set to NULL at instance file creation time; * Set to a non-NULL value ONLY IF current value is NULL and * a) the instance is brought up as a rootprimary. * In this case, the value is inherited from the "inst_info" field * of the current instance file * OR * b) the instance is brought up as a propagating primary. * In this case, the value is inherited from the "lms_group_info" * field of the instance file from the root primary instance * that this instance connects to directly or indirectly (through * a sequence of propagating primary instances in between). */ seq_num jnl_seqno; /* Holds the current seqno of the instance when the journal pool was last shutdown. * Compared with db reg_seqno at open time to check if both are in sync. */ uint4 root_primary_cycle; /* Incremented every time this instance is brought up as root primary */ int4 num_histinfo; /* Number of histinfo records currently USED in this file. Usually incremented as * new histinfo records get added to the file. Usually maintained in tandem with * "num_alloc_histinfo". When MUPIP ROLLBACK virtually truncates the histinfo * history it only updates this field to reflect the decrement. */ int4 num_alloc_histinfo; /* Actual number of histinfo record slots ALLOCATED in this file. Incremented * whenever a new histinfo record is allocated at the end of the instance file. * Not incremented if a new histinfo record reuses virtually truncated space * (possible by MUPIP ROLLBACK). This field is never decremented. */ boolean_t crash; /* This is set to TRUE if the journal pool exists and set to FALSE whenever * it is cleanly shutdown. Used to identify abnormal shutdowns and require rollback. */ boolean_t was_rootprimary; /* Set to TRUE when this instance starts up as a root primary. Set to FALSE when * this instance is brought up as a propagating primary and the receiver server * connects to the new primary source server successfully. */ boolean_t is_supplementary; /* Whether this instance is a supplementary instance or not */ int4 last_histinfo_num[MAX_SUPPL_STRMS]; /* Maintained whether "is_supplementary" is TRUE or FALSE. * the slot number corresponding to the most recent history record * written for each stream */ seq_num strm_seqno[MAX_SUPPL_STRMS]; /* Maintained only if "is_supplementary" is TRUE. * The current jnl seqno of each stream when the jnl pool was last * shutdown. Used to initialize similarly-named field in the * jnlpool_ctl structure at jnlpool_init time. Not as frequently * updated/maintained as the jnlpool_ctl->strm_seqno[] field */ repl_inst_uuid strm_group_info[MAX_SUPPL_STRMS - 1]; /* Maintained only if "is_supplementary" is TRUE. * The "lms_group_info" field of the non-supplementary source * that connects to this supplementary root primary instance. * Initialized by a -UPDATERESYNC= receiver server startup on a * supplementary root primary instance. Used by the receiver server * on the supplementary instance when it connects to a source * server from the non-supplementary instance. Note: We dont need * this for the 0th stream as its group info is the "lms_group_info" * member of the instance file header. Hence the MAX_SUPPL_STRMS-1. */ boolean_t file_corrupt; /* Set to TRUE by online rollback at start up. Set to FALSE when online rollback * completes successfully. */ boolean_t filler_ftok_counter_halted;/* Used only in V6.3-000. Kept as a filler to be safe */ boolean_t qdbrundown; /* TRUE if -QDBRUNDOWN was specified at time of instance file creation. * FALSE otherwise. */ unsigned char filler_1024[44]; } repl_inst_hdr; /* Any changes to the following structure might have to be reflected in "gtmsource_local_struct" structure in gtmsource.h as well. * All fields here have a corresponding parallel field in the latter structure. */ typedef struct gtmsrc_lcl_struct { unsigned char secondary_instname[MAX_INSTNAME_LEN]; /* Secondary instance corresponding to this structure */ seq_num resync_seqno; /* Next seqno to be sent from this primary instance. * Maintained in parallel with "read_jnl_seqno" in the corresponding * gtmsource_local structure in the journal pool. */ seq_num connect_jnl_seqno; /* jnl_seqno of the instance when last connection with secondary * was made. Used to determine least recently used slot for reuse */ unsigned char filler_64[32]; /* For future expansion */ } gtmsrc_lcl; #define COPY_GTMSOURCELOCAL_TO_GTMSRCLCL(sourcelocal, srclcl) \ { \ memcpy(&(srclcl)->secondary_instname[0], &(sourcelocal)->secondary_instname[0], MAX_INSTNAME_LEN - 1); \ (srclcl)->resync_seqno = (sourcelocal)->read_jnl_seqno; \ (srclcl)->connect_jnl_seqno = (sourcelocal)->connect_jnl_seqno; \ } #define COPY_GTMSRCLCL_TO_GTMSOURCELOCAL(srclcl, sourcelocal) \ { \ memcpy(&(sourcelocal)->secondary_instname[0], &(srclcl)->secondary_instname[0], MAX_INSTNAME_LEN - 1); \ (sourcelocal)->read_jnl_seqno = (srclcl)->resync_seqno; \ (sourcelocal)->connect_jnl_seqno = (srclcl)->connect_jnl_seqno; \ } #define COPY_JCTL_STRMSEQNO_TO_INSTHDR_IF_NEEDED \ { \ int idx; \ GBLREF jnlpool_addrs_ptr_t jnlpool; /* used by the below macro */ \ \ /* Keep the file header copy of "strm_seqno" uptodate with jnlpool_ctl */ \ if (jnlpool->repl_inst_filehdr->is_supplementary) \ { \ for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) \ jnlpool->repl_inst_filehdr->strm_seqno[idx] = jnlpool->jnlpool_ctl->strm_seqno[idx]; \ } \ } /* The below assert ensures that on a supplementary instance, an update to any stream only occurs if there is a history record * corresponding to that stream in the instance file. If this is not the case, those history-less updates will have no way of * being replicated (propagated downstream) from this instance as there is no history record to identify the update originator. */ #define ASSERT_INST_FILE_HDR_HAS_HISTREC_FOR_STRM(STRM_IDX, JNLPOOL) \ { \ assert(INVALID_HISTINFO_NUM != JNLPOOL->repl_inst_filehdr->last_histinfo_num[STRM_IDX]); \ } #define OK_TO_LOG_FALSE FALSE #define OK_TO_LOG_TRUE TRUE #define INST_NOT_GLD (struct gd_addr_struct *)1 #define IS_INST_FROM_GLD(REPL_GLD) (INST_NOT_GLD != REPL_GLD) /* where repl_inst_get_name found instance file name */ #define GET_INSTFILE_NAME(sendmsg, err_act) \ { \ if (((SS_NORMAL == (status = TRANS_LOG_NAME(&log_nam, &trans_name, temp_inst_fn, \ SIZEOF(temp_inst_fn), sendmsg))) || (inst_from_gld && (SS_NOLOGNAM == status))) \ && (0 != trans_name.len)) \ { \ temp_inst_fn[trans_name.len] = '\0'; \ if (!get_full_path(trans_name.addr, (unsigned int)trans_name.len, fn, fn_len, bufsize, &ustatus) \ && err_act) \ { \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_REPLINSTACC, 2, trans_name.len, trans_name.addr, \ ERR_TEXT, 2, RTS_ERROR_LITERAL("full path could not be found"), ustatus); \ } else \ ret = TRUE; \ } \ } #define SETUP_INST_INFO(GDPTR, LOGNAM, INSTFROMGLD) \ { \ if (IS_MUMPS_IMAGE && GDPTR && GDPTR->instinfo) \ { /* use global directory information */ \ LOGNAM.addr = GDPTR->instinfo->instfilename; \ LOGNAM.len = STRLEN(GDPTR->instinfo->instfilename); \ INSTFROMGLD = TRUE; \ } else \ { \ LOGNAM.addr = GTM_REPL_INSTANCE; \ LOGNAM.len = SIZEOF(GTM_REPL_INSTANCE) - 1; \ INSTFROMGLD = FALSE; \ } \ } typedef enum { return_on_error, issue_rts_error, issue_gtm_putmsg } instname_act; /* return and gd_ptr are really gd_addr * but gdsfhead.h would be needed */ struct gd_addr_struct *repl_inst_get_name(char *, unsigned int *, unsigned int, instname_act error_action, struct gd_addr_struct *gd_ptr); void repl_inst_create(void); void repl_inst_edit(void); void repl_inst_read(char *fn, off_t offset, sm_uc_ptr_t buff, size_t buflen); void repl_inst_write(char *fn, off_t offset, sm_uc_ptr_t buff, size_t buflen); void repl_inst_sync(char *fn); void repl_inst_jnlpool_reset(void); void repl_inst_recvpool_reset(void); void repl_inst_ftok_sem_lock(void); void repl_inst_ftok_sem_release(void); int4 repl_inst_histinfo_get(int4 index, repl_histinfo *histinfo); int4 repl_inst_histinfo_find_seqno(seq_num seqno, int4 strm_idx, repl_histinfo *histinfo); int4 repl_inst_wrapper_histinfo_find_seqno(seq_num seqno, int4 strm_idx, repl_histinfo *local_histinfo); void repl_inst_histinfo_add(repl_histinfo *histinfo); seq_num repl_inst_histinfo_truncate(seq_num rollback_seqno); void repl_inst_flush_filehdr(void); void repl_inst_flush_gtmsrc_lcl(void); void repl_inst_flush_jnlpool(boolean_t reset_recvpool_fields, boolean_t reset_crash); boolean_t repl_inst_was_rootprimary(void); int4 repl_inst_reset_zqgblmod_seqno_and_tn(void); boolean_t gtmsource_get_cmp_info(int4 *repl_zlib_cmp_level_ptr); void repl_cmp_solve_src_timeout(void); void repl_cmp_solve_rcv_timeout(void); boolean_t gtmsource_get_instance_info(boolean_t *secondary_was_rootprimary, seq_num *strm_jnl_seqno); boolean_t gtmsource_get_remote_histinfo(seq_num seqno, repl_histinfo *histinfo); boolean_t gtmsource_check_remote_strm_histinfo(seq_num seqno, boolean_t *rollback_first); void gtmsource_histinfo_get(int4 index, repl_histinfo *histinfo); boolean_t gtmsource_is_histinfo_identical(repl_histinfo *remote_histinfo, repl_histinfo *local_histinfo, seq_num jnl_seqno, boolean_t ok_to_log); seq_num gtmsource_find_resync_seqno(repl_histinfo *local_histinfo, repl_histinfo *remote_histinfo); void gtmsource_set_next_histinfo_seqno(boolean_t detect_new_histinfo); #endif /* REPL_INSTANCE_INCLUDED */ fis-gtm-V7.0-005/sr_unix/repl_ipc_cleanup.c0000644000032200000250000002024014342376330017507 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_inet.h" #include "gtm_ipc.h" #include #include #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" #include "repl_dbg.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "repl_sp.h" #include "repl_log.h" #include "ipcrmid.h" #include "repl_instance.h" #include "wbox_test_init.h" #include "have_crit.h" #include "anticipatory_freeze.h" #include "jnl.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF int pool_init; GBLREF int gtmrecv_srv_count; GBLREF recvpool_addrs recvpool; GBLREF int recvpool_shmid; int gtmsource_ipc_cleanup(boolean_t auto_shutdown, int *exit_status, int4 *num_src_servers_running) { boolean_t i_am_the_last_user, attempt_ipc_cleanup; int status, detach_status, remove_status, semval, save_errno; unix_db_info *udi; struct shmid_ds shm_buf; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Attempt cleaning up the IPCs */ attempt_ipc_cleanup = TRUE; *num_src_servers_running = 0; if (!auto_shutdown) { /* If the value of counter semaphore is not 0 (some other source server is still up), cannot cleanup ipcs */ semval = get_sem_info(SOURCE, SRC_SERV_COUNT_SEM, SEM_INFO_VAL); if (-1 == semval) { save_errno = errno; repl_log(stderr, TRUE, TRUE, "Error fetching source server count semaphore value : %s. " "Shutdown not complete\n", STRERROR(save_errno)); attempt_ipc_cleanup = FALSE; *exit_status = ABNORMAL_SHUTDOWN; } if (0 != semval) { repl_log(stderr, TRUE, TRUE, "Not deleting jnlpool ipcs. %d source servers still attached to jnlpool\n", semval); *num_src_servers_running = semval; attempt_ipc_cleanup = FALSE; *exit_status = ABNORMAL_SHUTDOWN; } } /* If the instance is frozen, there is no point checking if we are the last user, since we need to keep the journal pool * around, and we will need the journal pool attached so that we can check for instance freeze in database rundown. * In that case, the detach will happen automatically when the process terminates. */ assert(NULL != jnlpool); if (IS_REPL_INST_FROZEN) return FALSE; udi = (unix_db_info *)FILE_INFO(jnlpool->jnlpool_dummy_reg); assert(INVALID_SHMID != udi->shmid); if (attempt_ipc_cleanup) { i_am_the_last_user = (((status = shmctl(udi->shmid, IPC_STAT, &shm_buf)) == 0) && (1 == shm_buf.shm_nattch)); if (!i_am_the_last_user) { if (status < 0) repl_log(stderr, TRUE, TRUE, "Error in jnlpool shmctl : %s\n", STRERROR(ERRNO)); else repl_log(stderr, TRUE, TRUE, "Not deleting jnlpool ipcs. %d processes still attached to jnlpool\n", shm_buf.shm_nattch - 1); attempt_ipc_cleanup = FALSE; *exit_status = ABNORMAL_SHUTDOWN; } else if (INVALID_SHMID != udi->shmid) { if (INVALID_SEMID != jnlpool->repl_inst_filehdr->recvpool_semid DEBUG_ONLY(&& !(gtm_white_box_test_case_enabled && (WBTEST_UPD_PROCESS_ERROR == gtm_white_box_test_case_number)))) repl_log(stderr, TRUE, TRUE, "Receiver pool semaphore IDs were not removed\n"); if ((INVALID_SHMID != jnlpool->repl_inst_filehdr->recvpool_shmid) DEBUG_ONLY(&& !(gtm_white_box_test_case_enabled && (WBTEST_UPD_PROCESS_ERROR == gtm_white_box_test_case_number)))) repl_log(stderr, TRUE, TRUE, "Receiver pool shared memory not removed\n"); repl_inst_flush_jnlpool(TRUE, TRUE); } } /* We need to keep the journal pool attached when IFOE is configured so that we can check for instance freeze * in database rundown. * However, if we are about to cleanup ipcs, it doesn't matter, so we are better off cleaning up. */ if (INST_FREEZE_ON_ERROR_POLICY && !(attempt_ipc_cleanup && (INVALID_SHMID != udi->shmid))) return FALSE; /* detach from shared memory irrespective of whether we need to cleanup ipcs or not */ JNLPOOL_SHMDT(jnlpool, detach_status, save_errno); if (0 != detach_status) { repl_log(stderr, TRUE, TRUE, "Error detaching from Journal Pool : %s\n", STRERROR(save_errno)); attempt_ipc_cleanup = FALSE; *num_src_servers_running = 0; *exit_status = ABNORMAL_SHUTDOWN; } if ((attempt_ipc_cleanup) && (INVALID_SHMID != udi->shmid)) { remove_status = shm_rmid(udi->shmid); if (0 == remove_status) { repl_log(stdout, TRUE, FALSE, "Journal pool shared memory removed\n"); if (0 == remove_sem_set(SOURCE)) repl_log(stdout, TRUE, TRUE, "Journal pool semaphore removed\n"); else { repl_log(stderr, TRUE, TRUE, "Error removing jnlpool semaphore : %s\n", STRERROR(ERRNO)); *exit_status = ABNORMAL_SHUTDOWN; } } else { repl_log(stderr, TRUE, TRUE, "Error removing jnlpool shared memory : %s\n", STRERROR(ERRNO)); *exit_status = ABNORMAL_SHUTDOWN; } } return attempt_ipc_cleanup; } int gtmrecv_ipc_cleanup(boolean_t auto_shutdown, int *exit_status) { boolean_t i_am_the_last_user, attempt_ipc_cleanup; int status, detach_status, remove_status, expected_nattach, save_errno; struct shmid_ds shm_buf; /* Attempt cleaning up the IPCs */ attempt_ipc_cleanup = TRUE; /* Wait for the Receiver Server and Update Process to detach and takeover the semaphores. * Note that the Receiver Server has already waited for the Update Process to detach. * It is done here as a precaution against Receiver Server crashes. */ if (!auto_shutdown) status = grab_sem(RECV, RECV_SERV_COUNT_SEM); else status = 0; if (0 == status && 0 > (status = grab_sem(RECV, UPD_PROC_COUNT_SEM))) { save_errno = errno; status = rel_sem(RECV, RECV_SERV_COUNT_SEM); assert(0 == status); repl_log(stderr, TRUE, TRUE, "Error taking control of Receiver Server/Update Process count semaphore : %s. " "Shutdown not complete\n", STRERROR(save_errno)); *exit_status = ABNORMAL_SHUTDOWN; attempt_ipc_cleanup = FALSE; } /* Now we have locked out all users from the receive pool */ if (!auto_shutdown || !gtmrecv_srv_count) expected_nattach = 1; /* Self, or parent */ else expected_nattach = 0; /* Receiver server already detached */ i_am_the_last_user = (((status = shmctl(recvpool_shmid, IPC_STAT, &shm_buf)) == 0) && (shm_buf.shm_nattch == expected_nattach)); if (!i_am_the_last_user) { if (status < 0) repl_log(stderr, TRUE, TRUE, "Error in recvpool shmctl : %s\n", STRERROR(errno)); else repl_log(stderr, TRUE, TRUE, "Not deleting receive pool ipcs. %d processes still attached to receive pool\n", shm_buf.shm_nattch - expected_nattach); attempt_ipc_cleanup = FALSE; *exit_status = ABNORMAL_SHUTDOWN; } if (attempt_ipc_cleanup) { if (INVALID_SHMID != recvpool_shmid && (auto_shutdown || (detach_status = SHMDT(recvpool.recvpool_ctl)) == 0) && (remove_status = shm_rmid(recvpool_shmid)) == 0) { recvpool.recvpool_ctl = NULL; repl_log(stdout, TRUE, FALSE, "Receive pool shared memory removed\n"); if (0 == (status = remove_sem_set(RECV))) repl_log(stdout, TRUE, TRUE, "Receive pool semaphore removed\n"); else { repl_log(stderr, TRUE, TRUE, "Error removing receive pool semaphore : %s\n", STRERROR(status)); *exit_status = ABNORMAL_SHUTDOWN; } } else if (INVALID_SHMID != recvpool_shmid) { if (!auto_shutdown && detach_status < 0) repl_log(stderr, TRUE, TRUE, "Error detaching from receive pool shared memory : %s. Shared memory not removed\n", STRERROR(ERRNO)); else if (remove_status != 0) { if (!auto_shutdown) recvpool.recvpool_ctl = NULL; /* Detached successfully */ repl_log(stderr, TRUE, TRUE, "Error removing receive pool shared memory : %s\n", STRERROR(ERRNO)); } *exit_status = ABNORMAL_SHUTDOWN; } } return attempt_ipc_cleanup; } fis-gtm-V7.0-005/sr_unix/repl_log.c0000755000032200000250000000301714342376330016014 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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 #include #include "gtm_time.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gtmio.h" #include "have_crit.h" #include "repl_log.h" #include "iosp.h" GBLREF int gtmsource_log_fd; GBLREF int gtmrecv_log_fd; GBLREF int updproc_log_fd; GBLREF FILE *gtmsource_log_fp; GBLREF FILE *gtmrecv_log_fp; GBLREF FILE *updproc_log_fp; int repl_log(FILE *fp, boolean_t stamptime, boolean_t flush, char *fmt, ...) { va_list printargs; char time_str[CTIME_BEFORE_NL + 2]; /* for GET_CUR_TIME macro */ char fmt_str[BUFSIZ]; int rc; assert(NULL != fp); if (stamptime) { GET_CUR_TIME(time_str); strcpy(fmt_str, time_str); fmt_str[CTIME_BEFORE_NL] = ' '; /* Overwrite \n */ fmt_str[CTIME_BEFORE_NL + 1] = ':'; fmt_str[CTIME_BEFORE_NL + 2] = ' '; strcpy(fmt_str + CTIME_BEFORE_NL + 3, fmt); fmt = &fmt_str[0]; } va_start(printargs, fmt); VFPRINTF(fp, fmt, printargs, rc); assert(0 <= rc); va_end(printargs); if (flush) FFLUSH(fp); return(SS_NORMAL); } fis-gtm-V7.0-005/sr_unix/repl_log_init.c0000755000032200000250000001106214342376330017036 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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_fcntl.h" #include "gtm_stdio.h" #include "gtm_unistd.h" #include "gtm_stat.h" #include "gtm_string.h" #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "iosp.h" #include "repl_log.h" #include "repl_errno.h" #include "gtmio.h" #include "eintr_wrappers.h" #include "repl_sp.h" #include "send_msg.h" #include "gtmmsg.h" #include "have_crit.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif GBLDEF int gtmsource_log_fd = FD_INVALID; GBLDEF int gtmrecv_log_fd = FD_INVALID; GBLDEF int updproc_log_fd = FD_INVALID; GBLDEF int updhelper_log_fd = FD_INVALID; GBLDEF FILE *gtmsource_log_fp = NULL; GBLDEF FILE *gtmrecv_log_fp = NULL; GBLDEF FILE *updproc_log_fp = NULL; GBLDEF FILE *updhelper_log_fp = NULL; error_def(ERR_REPLLOGOPN); error_def(ERR_TEXT); error_def(ERR_REPLEXITERR); ZOS_ONLY(error_def(ERR_BADTAG);) int repl_log_init(repl_log_file_t log_type, int *log_fd, char *log) { /* Open the log file */ char log_file_name[MAX_FN_LEN + 1], *err_code; int tmp_fd; int save_errno; int stdout_status, stderr_status; int rc; #ifdef __MVS__ int status; int realfiletag; struct stat info; #endif if (*log == '\0') return(EREPL_LOGFILEOPEN); strcpy(log_file_name, log); ZOS_ONLY(STAT_FILE(log_file_name, &info, status);) OPENFILE3_CLOEXEC(log_file_name, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, tmp_fd); if (tmp_fd < 0) { if ((REPL_GENERAL_LOG == log_type) && (FD_INVALID == *log_fd)) { save_errno = ERRNO; err_code = STRERROR(save_errno); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_REPLLOGOPN, 6, LEN_AND_STR(log_file_name), LEN_AND_STR(err_code),LEN_AND_STR(NULL_DEVICE)); strcpy(log_file_name, NULL_DEVICE); if (log_type == REPL_GENERAL_LOG) strcpy(log, NULL_DEVICE); OPENFILE_CLOEXEC(log_file_name, O_RDWR, tmp_fd); /* Should not fail */ EXIT(EREPL_LOGFILEOPEN); } else { save_errno = ERRNO; err_code = STRERROR(save_errno); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_REPLLOGOPN, 6, LEN_AND_STR(log_file_name), LEN_AND_STR(err_code), LEN_AND_STR(log)); return(EREPL_LOGFILEOPEN); } } #ifdef __MVS__ if (0 == status) { if (-1 == gtm_zos_tag_to_policy(tmp_fd, TAG_UNTAGGED, &realfiletag)) TAG_POLICY_GTM_PUTMSG(log_file_name, errno, realfiletag, TAG_UNTAGGED); } else { if (-1 == gtm_zos_set_tag(tmp_fd, TAG_EBCDIC, TAG_TEXT, TAG_FORCE, &realfiletag)) TAG_POLICY_GTM_PUTMSG(log_file_name, errno, realfiletag, TAG_EBCDIC); } #endif if (log_type == REPL_GENERAL_LOG) { int dup2_res; /* Duplicate stdout and stderr onto log file */ DUP2(tmp_fd, 1, stdout_status); if (stdout_status >= 0) { DUP2(tmp_fd, 2, stderr_status); if (stderr_status < 0) { save_errno = ERRNO; if (FD_INVALID != *log_fd) { DUP2(*log_fd, 1, dup2_res); /* Restore old log file */ DUP2(*log_fd, 2, dup2_res); } } } else { save_errno = ERRNO; if (FD_INVALID != *log_fd) DUP2(*log_fd, 1, dup2_res); /* Restore old log file */ } if (stdout_status >= 0 && stderr_status >= 0) { if (FD_INVALID != *log_fd) CLOSEFILE_RESET(*log_fd, rc); /* resets "*log_fd" to FD_INVALID */ *log_fd = tmp_fd; } else { err_code = STRERROR(save_errno); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_REPLLOGOPN, 6, LEN_AND_STR(log_file_name), LEN_AND_STR(err_code), strlen(log), log, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error in dup2")); } } return(SS_NORMAL); } int repl_log_fd2fp(FILE **fp, int fd) { int fclose_res; /* For stats log file, we need to have FCLOSEd *fp because a later open() in repl_log_init() might return the same file * descriptor as a previously closed stats log file. In that case, FCLOSE if done here affects the newly opened file and * FDOPEN will fail returning NULL for the file pointer. */ if (NULL != *fp) FCLOSE(*fp, fclose_res); FDOPEN(*fp, fd, "a"); assert(NULL != *fp); /* we don't expect FDOPEN to fail */ return(SS_NORMAL); } fis-gtm-V7.0-005/sr_unix/repl_logfileinfo_get.c0000644000032200000250000000622714342376330020372 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2017 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 #include #include "mdef.h" #include "gtm_limits.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_unistd.h" #include "repl_msg.h" #include "repl_log.h" #include "gdsroot.h" #include "have_crit.h" #include "min_max.h" error_def(ERR_FILENAMETOOLONG); GBLREF uint4 process_id; uint4 repl_logfileinfo_get(char *logfile, repl_logfile_info_msg_t *msgp, boolean_t cross_endian, FILE *logfp) { uint4 status, fullpath_len, msglen; int save_errno; char fullpath[GTM_PATH_MAX], *cwdptr; assert(NULL != msgp); msgp->type = cross_endian ? GTM_BYTESWAP_32(REPL_LOGFILE_INFO) : REPL_LOGFILE_INFO; assert(GTM_PATH_MAX >= REPL_LOGFILE_PATH_MAX); assert(GTM_PATH_MAX >= PATH_MAX); if (NULL == logfile) { GETCWD(fullpath, GTM_PATH_MAX, cwdptr); assert(NULL != cwdptr); if (NULL == cwdptr) { save_errno = errno; assert(FALSE); repl_log(logfp, TRUE, TRUE, "Could not obtain current working directory: %s\n", STRERROR(save_errno)); SNPRINTF(fullpath, GTM_PATH_MAX, "Could not obtain current working directory"); } fullpath_len = STRLEN(fullpath); } else if (!get_full_path(STR_AND_LEN(logfile), fullpath, &fullpath_len, GTM_PATH_MAX, &status)) { /* Either GETCWD failed or buffer not large enough to hold the expanded logfile path. In either case, we don't want * to error out as this is just a supplementary message. Copy whatever possible. */ assert(ERR_FILENAMETOOLONG != status); SNPRINTF(fullpath, GTM_PATH_MAX, logfile); fullpath_len = STRLEN(fullpath); /* Print a warning message for diagnostic purposes */ if (ERR_FILENAMETOOLONG != status) repl_log(logfp, TRUE, TRUE, "Could not obtain current working directory: %s\n", STRERROR(status)); else repl_log(logfp, TRUE, TRUE, "Could not obtain full path of log file: Path name exceeds %d characters\n", GTM_PATH_MAX); } assert('\0' == fullpath[fullpath_len]); fullpath_len = MIN(fullpath_len, REPL_LOGFILE_PATH_MAX); fullpath[fullpath_len] = '\0'; /* truncate if needed */ fullpath_len++; /* So that, we copy and send null-terminator as well */ memcpy(msgp->fullpath, fullpath, fullpath_len); msgp->fullpath_len = cross_endian ? GTM_BYTESWAP_32(fullpath_len) : fullpath_len; assert(fullpath_len <= REPL_LOGFILE_PATH_MAX); /* Receiver expects 8 byte alignment on data portion of the message. */ fullpath_len = ROUND_UP2(fullpath_len, REPL_MSG_ALIGN); assert(fullpath_len <= REPL_LOGFILE_PATH_MAX + 1); msglen = REPL_LOGFILE_INFO_MSGHDR_SZ + fullpath_len; msgp->len = cross_endian ? GTM_BYTESWAP_32(msglen) : msglen; msgp->proto_ver = REPL_PROTO_VER_THIS; msgp->pid = cross_endian ? GTM_BYTESWAP_32(process_id) : process_id; return msglen; } fis-gtm-V7.0-005/sr_unix/repl_msg.h0000755000032200000250000005250614342376330016035 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2018 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. * * * ****************************************************************/ #ifndef REPL_MSG_H_INCLUDED #define REPL_MSG_H_INCLUDED enum { REPL_INVALID = -1, /* -1 */ REPL_START_JNL_SEQNO = 0, /* 0 */ REPL_TR_JNL_RECS, /* 1 */ REPL_ROLLBACK_FIRST, /* 2 */ REPL_OBSOLETE_WILL_RESTART, /* 3 */ /* Obsoleted effective V4.4-002 since we no longer support dual site config with * V4.1 versions. But slot# preserved to not perturb other message #s */ REPL_XOFF, /* 4 */ REPL_XON, /* 5 */ REPL_BADTRANS, /* 6 */ REPL_HEARTBEAT, /* 7 */ REPL_FETCH_RESYNC, /* 8 */ REPL_RESYNC_SEQNO, /* 9 */ REPL_OBSOLETE_STOPSRCFILTER, /* 10 */ /* message no longer used but slot# 10 preserved to not perturb other message #s */ REPL_XOFF_ACK_ME, /* 11 */ REPL_XOFF_ACK, /* 12 */ REPL_WILL_RESTART_WITH_INFO, /* 13 */ REPL_MULTISITE_MSG_START, /* 14 */ /* All messages after this are newly introduced for multi-site support only */ REPL_OLD_NEED_INSTANCE_INFO, /* 15 */ REPL_OLD_INSTANCE_INFO, /* 16 */ REPL_NEED_HISTINFO, /* 17 */ REPL_OLD_TRIPLEINFO1, /* 18 */ REPL_OLD_TRIPLEINFO2, /* 19 */ REPL_OLD_TRIPLE, /* 20 */ REPL_INST_NOHIST, /* 21 */ REPL_LOSTTNCOMPLETE, /* 22 */ REPL_CMP_TEST, /* 23 */ REPL_CMP_SOLVE, /* 24 */ REPL_CMP2UNCMP, /* 25 */ /* used to signal a transition to uncompressed messages in case of errors */ REPL_TR_CMP_JNL_RECS, /* 26 */ /* used to send compressed messages whose pre-compressed length is < 2**24 */ REPL_TR_CMP_JNL_RECS2, /* 27 */ /* used to send compressed messages whose pre-compressed length is >= 2**24 */ REPL_NEED_INSTINFO, /* 28 */ REPL_INSTINFO, /* 29 */ REPL_HISTINFO, /* 30 */ /* used to exchange history information as part of a replication handshake */ REPL_HISTREC, /* 31 */ /* sent in the middle of a journal record stream to signal start of new history */ REPL_NEED_STRMINFO, /* 32 */ /* sent by a supplementary source server to a supplementary receiver server */ REPL_STRMINFO, /* 33 */ /* sent in response to a REPL_NEED_STRMINFO message */ REPL_LOGFILE_INFO, /* 34 */ /* sent (at time of handshake) to communicate to one another the $CWD/logfile */ REPL_NEED_TLS_INFO, /* 35 */ /* sent to get the SSL/TLS information from the receiver. */ REPL_TLS_INFO, /* 36 */ /* Receiver's response to REPL_NEED_TLS_INFO message. */ REPL_RENEG_ACK_ME, /* 37 */ /* Start a renegotiation request with the receiver server. */ REPL_RENEG_ACK, /* 38 */ /* Receiver's acknowledgement of REPL_RENEG_ACK_ME message. */ REPL_RENEG_COMPLETE, /* 39 */ /* Completion of SSL/TLS renegotiation between the Source and Receiver server. */ REPL_MSGTYPE_LAST=256 /* 256 */ /* any new message need to be added before REPL_MSGTYPE_LAST */ }; #define REPL_PROTO_VER_UNINITIALIZED (char)0xFF /* -1, the least of the versions to denote an uninitialized version field */ #define REPL_PROTO_VER_DUALSITE (char)0x0 /* Versions GT.M V5.0 and prior that dont support multi site replication */ #define REPL_PROTO_VER_MULTISITE (char)0x1 /* Versions V5.1-000 and above that support multi site replication */ #define REPL_PROTO_VER_MULTISITE_CMP (char)0x2 /* Versions V5.3-003 and above that support multisite replication with * the ability to compress the logical records in the replication pipe. */ #define REPL_PROTO_VER_SUPPLEMENTARY (char)0x3 /* Versions >= V5.5-000 that support supplementary instances */ #define REPL_PROTO_VER_REMOTE_LOGPATH (char)0x4 /* Versions >= V6.0-003 that send remote $CWD as part of handshake */ #define REPL_PROTO_VER_TLS_SUPPORT (char)0x5 /* Versions >= V6.1-000 that supports SSL/TLS communication. */ #define REPL_PROTO_VER_XENDIANFIXES (char)0x6 /* Versions >= V6.2-001 support cross-endian replication (GTM-8205) */ #define REPL_PROTO_VER_THIS REPL_PROTO_VER_XENDIANFIXES /* The current/latest version of the communication protocol between the * primary (source server) and secondary (receiver server or rollback) */ /* Below macro defines the maximum size of the replication logfile path information that will be sent across to the other side. * While the actual path max is defined by GTM_PATH_MAX, the value of GTM_PATH_MAX differ across different platforms and so we * cannot always be sure if this side can store the remote side's logfile path. Since REPL_LOGFILE_INFO message is sent across * only during handshake, malloc too is an overkill. So, have a fixed size buffer (see repl_logfile_info_msg_t beow) bounded by * the below size. */ #define REPL_LOGFILE_PATH_MAX 1023 /* A few of these flag bits (e.g. START_FLAG_SRCSRV_IS_VMS) are no longer used but they should not be removed just in case prior * versions that used those bitslots communicate with a newer version that assigns a different meaning to those slots. Any new * slot additions need to happen at the end of the list. No replacements of unused slots. */ #define START_FLAG_NONE 0x00000000 #define START_FLAG_STOPSRCFILTER 0x00000001 #define START_FLAG_UPDATERESYNC 0x00000002 #define START_FLAG_HASINFO 0x00000004 #define START_FLAG_COLL_M 0x00000008 #define START_FLAG_VERSION_INFO 0x00000010 #define START_FLAG_TRIGGER_SUPPORT 0x00000020 #define START_FLAG_SRCSRV_IS_VMS 0x00000040 /* Obsolete but preserve slot */ #define START_FLAG_NORESYNC 0x00000080 #define START_FLAG_ENABLE_TLS 0x00000100 #define MIN_REPL_MSGLEN 32 /* To keep compiler happy with * the definition of repl_msg_t as well * as to accommodate a seq_num */ #define REPL_MSG_CMPINFOLEN (SIZEOF(repl_cmpinfo_msg_t)) #define REPL_MSG_CMPDATALEN 256 /* length of data part of message exchanged between source/receiver to test compression */ #define REPL_MSG_CMPDATAMASK 0xff #define MAX_CMP_EXPAND_FACTOR 2 /* the worst case factor by which compression could actually expand input data */ #define REPL_MSG_CMPEXPDATALEN (MAX_CMP_EXPAND_FACTOR * REPL_MSG_CMPDATALEN) /* The "datalen" field of the REPL_CMP_SOLVE message is set to the following value by the receiver server in case of errors. */ #define REPL_RCVR_CMP_TEST_FAIL (-1) #define REPL_MSG_ALIGN 8 /* every message sent across the pipe is expected to be at least 8-byte aligned */ #define REPL_MSG_TYPE (uint4)(SIZEOF(int4)) #define REPL_MSG_LEN (uint4)(SIZEOF(int4)) #define REPL_MSG_HDRLEN (uint4)(REPL_MSG_TYPE + REPL_MSG_LEN) /* For type and len fields */ #define REPL_MSG_SIZE 1024 + 1 typedef struct /* Used also to send a message of type REPL_INST_NOHIST */ { int4 type; int4 len; /* Is 8-byte aligned for ALL messages except REPL_TR_CMP_JNL_RECS message */ unsigned char msg[MIN_REPL_MSGLEN - REPL_MSG_HDRLEN]; /* All that we need is msg[1], but keep the compiler happy with * this definition for msg. Also provide space to accommodate a seq_num * so that a static definition would suffice instead of malloc'ing a * small message buffer */ } repl_msg_t; /* To send a REPL_TR_CMP_JNL_RECS type of message, we include the pre-compressed length as part of the 4-byte type field * to keep the replication message header overhead to the current 8-bytes. Define macros to access those fields. * If the pre-compressed length cannot fit in 24 bits, then we send a REPL_TR_CMP_JNL_RECS2 message which has 8-more * byte overhead as part of the replication message header. It is considered ok to take this extra 8-byte hit for huge messages. * The threshold is defined as 2**23 (instead of 2**24) so we ensure the most significant bit is 0 and does not give us * problems while doing >> operations (e.g. (((repl_msg_ptr_t)buffp)->type >> REPL_TR_CMP_MSG_TYPE_BITS); */ #define REPL_TR_CMP_MSG_TYPE_BITS 8 #define REPL_TR_CMP_MSG_UNCMPLEN_BITS 24 #define REPL_TR_CMP_THRESHOLD (1 << (REPL_TR_CMP_MSG_UNCMPLEN_BITS - 1)) #define REPL_TR_CMP_MSG_TYPE_MASK ((1 << REPL_TR_CMP_MSG_TYPE_BITS) - 1) /* Defines for sending REPL_TR_CMP_JNL_RECS2 message */ #define REPL_MSG_UNCMPLEN SIZEOF(int4) #define REPL_MSG_CMPLEN SIZEOF(int4) #define REPL_MSG_HDRLEN2 (REPL_MSG_HDRLEN + REPL_MSG_UNCMPLEN + REPL_MSG_CMPLEN) /* 16-byte header */ typedef struct /* Used also to send a message of type REPL_TR_CMP_JNL_RECS2 */ { int4 type; int4 len; int4 uncmplen; int4 cmplen; unsigned char msg[MIN_REPL_MSGLEN - REPL_MSG_HDRLEN2]; /* All that we need is msg[1], but keep the compiler happy with * this definition for msg. Also provide space to accommodate a seq_num * so that a static definition would suffice instead of malloc'ing a * small message buffer */ } repl_cmpmsg_t; /* This should ideally match the TCP send/recv buffer size of source/receiver server. However, since the maximum TCP buffer * varies by system, and on many systems it is less than 2 MB or even 1 MB, we keep GTMRECV_TCP_RECV_BUFSIZE and * GTMSOURCE_TCP_SEND_BUFSIZE at 1 MB to avoid extra attempts to send/receive TCP packets before the limit is lowered enough * for the OS to support it. */ #define MAX_REPL_MSGLEN (2 * 1024 * 1024) #define MAX_TR_BUFFSIZE (MAX_REPL_MSGLEN - REPL_MSG_HDRLEN2) /* allow for biggest replication message header */ #define MAX_REPL_OPMSG_LEN 256 + 1 typedef struct /* used to send a message of type REPL_START_JNL_SEQNO */ { int4 type; int4 len; unsigned char start_seqno[SIZEOF(seq_num)]; uint4 start_flags; unsigned char jnl_ver; char proto_ver; /* Needs to be "signed char" in order to be able to do signed comparisons of this with * the macros REPL_PROTO_VER_DUALSITE (0) and REPL_PROTO_VER_UNINITIALIZED (-1) */ char node_endianness; /* 'L' if this machine is little endian, 'B' if it is big endian */ char is_supplementary; /* TRUE for a supplementary instance; FALSE otherwise */ char filler_32[8]; } repl_start_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct /* used to send a message of type REPL_WILL_RESTART_WITH_INFO or REPL_ROLLBACK_FIRST or REPL_RESYNC_SEQNO */ { int4 type; int4 len; unsigned char start_seqno[SIZEOF(seq_num)]; unsigned char jnl_ver; char start_flags[4]; char proto_ver; /* Needs to be "signed char" in order to be able to do signed comparisons of this with * the macros REPL_PROTO_VER_DUALSITE (0) and REPL_PROTO_VER_UNINITIALIZED (-1) */ char node_endianness; /* 'L' if this machine is little endian, 'B' if it is big endian */ char is_supplementary; /* TRUE for a supplementary instance; FALSE otherwise */ char filler_32[8]; } repl_start_reply_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct /* used to send a message of type REPL_FETCH_RESYNC */ { int4 type; int4 len; seq_num resync_seqno; char proto_ver; /* Needs to be "signed char" in order to be able to do signed comparisons of this with * the macros REPL_PROTO_VER_DUALSITE (0) and REPL_PROTO_VER_UNINITIALIZED (-1) */ char node_endianness; /* 'L' if this machine is little endian, 'B' if it is big endian */ char is_supplementary; /* TRUE for a supplementary instance; FALSE otherwise */ char filler_32[13]; } repl_resync_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct /* used to send a message of type REPL_OLD_NEED_INSTANCE_INFO */ { int4 type; int4 len; unsigned char instname[MAX_INSTNAME_LEN]; char proto_ver; /* Needs to be "signed char" in order to be able to do signed comparisons of this with * the macros REPL_PROTO_VER_DUALSITE (0) and REPL_PROTO_VER_UNINITIALIZED (-1) */ char node_endianness; /* 'L' if this machine is little endian, 'B' if it is big endian */ char is_rootprimary; /* Whether the source server that is sending this message is a root primary or not. */ char filler_32[5]; } repl_old_needinst_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct /* used to send a message of type REPL_NEED_INSTINFO */ { int4 type; int4 len; unsigned char instname[MAX_INSTNAME_LEN]; repl_inst_uuid lms_group_info; char proto_ver; /* Needs to be "signed char" in order to be able to do signed comparisons of this with * the macros REPL_PROTO_VER_DUALSITE (0) and REPL_PROTO_VER_UNINITIALIZED (-1) */ char is_rootprimary; /* Whether the source server that is sending this message is a root primary or not. */ char is_supplementary; /* TRUE for a supplementary instance; FALSE otherwise */ char jnl_ver; /* jnl format of this side */ char filler_32[4]; } repl_needinst_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct /* used to send a message of type REPL_OLD_INSTANCE_INFO */ { int4 type; int4 len; unsigned char instname[MAX_INSTNAME_LEN]; /* The name of this replication instance */ unsigned char was_rootprimary; char filler_32[7]; } repl_old_instinfo_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct /* used to send a message of type REPL_INSTINFO */ { int4 type; int4 len; unsigned char instname[MAX_INSTNAME_LEN]; /* The name of this replication instance */ seq_num strm_jnl_seqno; /* Whenever a supplementary receiver handshakes with a non-supplementary source, * the current jnl seqno (which is across all streams) is sent in the initial * REPL_START_JNL_SEQNO message. Only after the source server sends the * REPL_NEED_INSTINFO message does the receiver know which non-supplementary stream * the source corresponds to and can therefore determine the stream specific * jnl seqno. Once this is known, instead of re-sending the REPL_START_JNL_SEQNO * message, the receiver sends this new seqno in the "strm_jnl_seqno" field as * part of the REPL_INSTINFO message. In cases where the receiver is not * supplementary or if the soruce is supplementary, this field is set to 0. */ repl_inst_uuid lms_group_info; unsigned char was_rootprimary; char filler_32[7]; } repl_instinfo_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct /* used to send messages of type REPL_CMP_TEST or REPL_CMP_SOLVE */ { int4 type; int4 len; int4 datalen; /* length of compressed or uncompressed data */ char proto_ver; char filler_16[3]; char data[REPL_MSG_CMPDATALEN]; /* compressed (if REPL_CMP_TEST) or uncompressed (if REPL_CMP_SOLVE) data */ char overflowdata[(MAX_CMP_EXPAND_FACTOR - 1) * REPL_MSG_CMPDATALEN]; /* buffer to hold overflow in case compression expands data */ } repl_cmpinfo_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct /* used to send a message of type REPL_NEED_HISTINFO */ { int4 type; int4 len; seq_num seqno; /* The histinfo requested is that of "seqno-1" */ int4 strm_num; /* used only in case of supplementary instances */ int4 histinfo_num; /* if not INVALID_HISTINFO_NUM, then we want the histinfo_num'th history record * on the remote side. in this case ignore seqno & strm_num. */ char filler_32[8]; } repl_needhistinfo_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct /* used to send a message of type REPL_OLD_TRIPLEINFO1 */ { int4 type; int4 len; seq_num start_seqno; /* the starting seqno of the histinfo range */ unsigned char instname[MAX_INSTNAME_LEN]; } repl_histinfo1_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct /* used to send a message of type REPL_OLD_TRIPLEINFO2 */ { int4 type; int4 len; seq_num start_seqno; /* the starting seqno of the histinfo range */ uint4 cycle; int4 histinfo_num; char filler_32[8]; } repl_histinfo2_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct /* used to send a message of type REPL_HISTINFO */ { int4 type; int4 len; repl_histinfo history; } repl_histinfo_msg_t; /* The first two fields should be as in repl_msg_t */ /* A REPL_OLD_TRIPLE message is sent in such a form that the receiver server can copy it (after removing the 8-byte header * containing the "type" and "len") as such into the receive pool. The update process treats this content as yet another * journal record type. Therefore we need to introduce a new journal record type (JRT_TRIPLE) for this purpose and define a * structure (repl_old_triple_jnl_t) that has this type information and the triple related information. This will then be a * member of the REPL_OLD_TRIPLE message. * Note: GT.M versions that support supplementary instances send a REPL_HISTREC message instead of REPL_OLD_TRIPLE. */ typedef struct /* sub-structure used to send a message of type REPL_OLD_TRIPLE */ { uint4 jrec_type : 8; /* This definition is copied from that of "jrec_prefix" */ uint4 forwptr : 24; /* This definition is copied from that of "jrec_prefix" */ uint4 cycle; /* The cycle of the instance at the time of generating this triple */ seq_num start_seqno; /* The starting seqno of the triple range */ unsigned char instname[MAX_INSTNAME_LEN]; /* The instance name that generated this triple */ unsigned char rcvd_from_instname[MAX_INSTNAME_LEN]; /* instance name this triple was received from (on the secondary) */ } repl_old_triple_jnl_t; typedef struct /* used to send a message of type REPL_OLD_TRIPLE */ { int4 type; int4 len; repl_old_triple_jnl_t triplecontent; } repl_old_triple_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct /* sub-structure used to send a message of type REPL_HISTREC */ { uint4 jrec_type : 8; /* This definition is copied from that of "jrec_prefix" */ uint4 forwptr : 24; /* This definition is copied from that of "jrec_prefix" */ uint4 filler_8byte_align; repl_histinfo histcontent; /* the structure containing the history information */ } repl_histrec_jnl_t; typedef struct /* used to send a message of type REPL_HISTREC */ { int4 type; int4 len; repl_histrec_jnl_t histjrec; /* the journal record containing the history information */ } repl_histrec_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct /* used to send a message of type REPL_NEED_STRMINFO */ { int4 type; int4 len; seq_num seqno; /* The strminfo requested is that of "seqno-1" */ char filler_32[16]; } repl_needstrminfo_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct /* used to send a message of type REPL_STRMINFO */ { int4 type; int4 len; int4 last_histinfo_num[MAX_SUPPL_STRMS]; } repl_strminfo_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct { int4 type; int4 len; unsigned char ack_seqno[SIZEOF(seq_num)]; unsigned char ack_time[SIZEOF(gtm_time4_t)]; unsigned char filler_32[12]; } repl_heartbeat_msg_t; /* The first two fields should be as in repl_msg_t */ typedef struct { struct { sm_off_t fl; sm_off_t bl; } que; repl_heartbeat_msg_t heartbeat; } repl_heartbeat_que_entry_t; typedef struct /* Used to send a message of type REPL_BADTRANS or REPL_CMP2UNCMP */ { int4 type; int4 len; seq_num start_seqno; /* The seqno that source server should restart sending from */ char filler_32[16]; } repl_badtrans_msg_t; typedef struct { int4 type; int4 len; uint4 API_version; /* The GT.M TLS version understood by this side. */ uint4 library_version; /* The SSL/TLS implementation library that this side is linked with at runtime. */ char filler_32[16]; } repl_tlsinfo_msg_t; typedef struct { int4 type; int4 len; int4 fullpath_len; uint4 pid; char proto_ver; char filler_32[15]; /* to ensure that at least 32 bytes are sent across (gtmrecv_fetchresync relies on this) */ char fullpath[REPL_LOGFILE_PATH_MAX + 1]; /* + 1 for null-terminator */ } repl_logfile_info_msg_t; #define REPL_LOGFILE_INFO_MSGHDR_SZ OFFSETOF(repl_logfile_info_msg_t, fullpath[0]) #if defined(__osf__) && defined(__alpha) # pragma pointer_size(save) # pragma pointer_size(long) #endif typedef repl_msg_t *repl_msg_ptr_t; typedef repl_cmpmsg_t *repl_cmpmsg_ptr_t; typedef repl_start_msg_t *repl_start_msg_ptr_t; typedef repl_start_reply_msg_t *repl_start_reply_msg_ptr_t; typedef repl_resync_msg_t *repl_resync_msg_ptr_t; typedef repl_old_needinst_msg_t *repl_old_needinst_msg_ptr_t; typedef repl_needinst_msg_t *repl_needinst_msg_ptr_t; typedef repl_old_instinfo_msg_t *repl_old_instinfo_msg_ptr_t; typedef repl_instinfo_msg_t *repl_instinfo_msg_ptr_t; typedef repl_needhistinfo_msg_t *repl_needhistinfo_msg_ptr_t; typedef repl_histinfo1_msg_t *repl_histinfo1_msg_ptr_t; typedef repl_histinfo2_msg_t *repl_histinfo2_msg_ptr_t; typedef repl_old_triple_jnl_t *repl_old_triple_jnl_ptr_t; typedef repl_histrec_jnl_t *repl_histrec_jnl_ptr_t; typedef repl_histinfo_msg_t *repl_histinfo_msg_ptr_t; typedef repl_needstrminfo_msg_t *repl_needstrminfo_msg_ptr_t; typedef repl_strminfo_msg_t *repl_strminfo_msg_ptr_t; typedef repl_heartbeat_msg_t *repl_heartbeat_msg_ptr_t; typedef repl_cmpinfo_msg_t *repl_cmpinfo_msg_ptr_t; #if defined(__osf__) && defined(__alpha) # pragma pointer_size(restore) #endif void gtmsource_repl_send(repl_msg_ptr_t msg, char *msgtypestr, seq_num optional_seqno, int4 optional_strm_num); void gtmsource_send_new_histrec(void); void gtmrecv_repl_send(repl_msg_ptr_t msgp, int4 type, int4 len, char *msgtypestr, seq_num optional_seqno); void gtmrecv_send_histinfo(repl_histinfo *histinfo); void gtmrecv_check_and_send_instinfo(repl_needinst_msg_ptr_t need_instinfo_msg, boolean_t is_rcvr_srvr); uint4 repl_logfileinfo_get(char *logfile, repl_logfile_info_msg_t *msgp, boolean_t cross_endian, FILE *logfp); #endif fis-gtm-V7.0-005/sr_unix/repl_sem.c0000644000032200000250000001610214342376330016013 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_inet.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include #include #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" #include "iosp.h" #include "gtm_stdio.h" #include "gtmio.h" #include "gtm_string.h" #include "repl_instance.h" #include "gtm_logicals.h" #include "gtm_c_stack_trace.h" #include "eintr_wrappers.h" #include "eintr_wrapper_semop.h" #include "do_semop.h" #include "ipcrmid.h" #include "ftok_sems.h" #include "repl_sem.h" /* In the present shape, this module is not generic enough. Coded with the view of bringing SEM related code out of replication module. Could be made more generic. Handles two semaphore sets one for source and another for receiver server. FTOK related semaphore for replication is also added here. */ GBLREF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; static struct sembuf replsop[5]; static int sem_set_id[NUM_SEM_SETS] = {0, 0}; static int sem_info_id_map[SEM_NUM_INFOS] = { GETVAL, GETPID }; boolean_t sem_set_exists(int which_set) { return(sem_set_id[which_set] > 0); } int init_sem_set_source(sem_key_t key, int nsems, permissions_t sem_flags) { assert(IPC_PRIVATE == (key_t)key); assert(SIZEOF(key_t) >= SIZEOF(sem_key_t)); sem_set_id[SOURCE] = semget(key, nsems, sem_flags); holds_sem[SOURCE][JNL_POOL_ACCESS_SEM] = FALSE; holds_sem[SOURCE][SRC_SERV_COUNT_SEM] = FALSE; return sem_set_id[SOURCE]; } void set_sem_set_src(int semid) { sem_set_id[SOURCE] = semid; holds_sem[SOURCE][JNL_POOL_ACCESS_SEM] = FALSE; holds_sem[SOURCE][SRC_SERV_COUNT_SEM] = FALSE; } int init_sem_set_recvr(sem_key_t key, int nsems, permissions_t sem_flags) { int semid; assert(IPC_PRIVATE == (key_t)key); assert(SIZEOF(key_t) >= SIZEOF(sem_key_t)); semid = semget(key, nsems, sem_flags); set_sem_set_recvr(semid); return sem_set_id[RECV]; } void set_sem_set_recvr(int semid) { sem_set_id[RECV] = semid; holds_sem[RECV][RECV_POOL_ACCESS_SEM] = FALSE; holds_sem[RECV][RECV_SERV_COUNT_SEM] = FALSE; holds_sem[RECV][UPD_PROC_COUNT_SEM] = FALSE; holds_sem[RECV][RECV_SERV_OPTIONS_SEM] = FALSE; } int grab_sem(int set_index, int sem_num) { int rc; int id; assert((SOURCE == set_index) || (RECV == set_index)); assert((int)NUM_SRC_SEMS == (int)NUM_RECV_SEMS); /* holds_sem[][] relies on this as it uses NUM_SRC_SEMS for array bounds */ assert(!holds_sem[set_index][sem_num]); ASSERT_SET_INDEX; replsop[0].sem_op = 0; /* Wait for 0 */ replsop[0].sem_num = sem_num; replsop[1].sem_op = 1; /* lock it */ replsop[1].sem_num = sem_num; replsop[0].sem_flg = replsop[1].sem_flg = SEM_UNDO; id = sem_set_id[set_index]; SEMOP(id, replsop, 2, rc, FORCED_WAIT); if (0 == rc) holds_sem[set_index][sem_num] = TRUE; return rc; } int incr_sem(int set_index, int sem_num) { int rc; ASSERT_SET_INDEX; replsop[0].sem_op = 1; /* Increment it */ replsop[0].sem_num = sem_num; replsop[0].sem_flg = SEM_UNDO; SEMOP(sem_set_id[set_index], replsop, 1, rc, NO_WAIT); if (0 == rc) { /* decr_sem internally calls rel_sem directly which expects that the entry for hold_sem[SOURCE][SRC_SERV_COUNT_SEM] * is set to TRUE. But, incr_sem doesn't set this entry. This causes decr_sem (done below) to assert fail. to avoid * the assert, set the hold_sem array to TRUE even if this is an increment operation */ holds_sem[set_index][sem_num] = TRUE; } return rc; } int grab_sem_all_source() { int rc; assert(!holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]); replsop[0].sem_op = 0; /* Wait for 0 */ replsop[0].sem_num = JNL_POOL_ACCESS_SEM; replsop[1].sem_op = 1; /* Increment it */ replsop[1].sem_num = JNL_POOL_ACCESS_SEM; replsop[2].sem_op = 1; /* Increment it */ replsop[2].sem_num = SRC_SERV_COUNT_SEM; replsop[0].sem_flg = replsop[1].sem_flg = replsop[2].sem_flg = SEM_UNDO; SEMOP(sem_set_id[SOURCE], replsop, 3, rc, FORCED_WAIT); if (0 == rc) { holds_sem[SOURCE][JNL_POOL_ACCESS_SEM] = TRUE; holds_sem[SOURCE][SRC_SERV_COUNT_SEM] = TRUE; } return rc; } int grab_sem_all_receive() { int rc; assert(!holds_sem[RECV][RECV_POOL_ACCESS_SEM]); replsop[0].sem_op = 0; /* Wait for 0 */ replsop[0].sem_num = RECV_POOL_ACCESS_SEM; replsop[1].sem_op = 1; /* Increment it */ replsop[1].sem_num = RECV_POOL_ACCESS_SEM; replsop[2].sem_op = 1; /* Increment it */ replsop[2].sem_num = RECV_SERV_COUNT_SEM; replsop[3].sem_op = 1; /* Increment it */ replsop[3].sem_num = UPD_PROC_COUNT_SEM; replsop[4].sem_op = 1; /* Increment it */ replsop[4].sem_num = RECV_SERV_OPTIONS_SEM; replsop[0].sem_flg = replsop[1].sem_flg = replsop[2].sem_flg = replsop[3].sem_flg = replsop[4].sem_flg = SEM_UNDO; SEMOP(sem_set_id[RECV], replsop, 5, rc, FORCED_WAIT); if (0 == rc) { holds_sem[RECV][RECV_POOL_ACCESS_SEM] = TRUE; holds_sem[RECV][RECV_SERV_COUNT_SEM] = TRUE; holds_sem[RECV][UPD_PROC_COUNT_SEM] = TRUE; holds_sem[RECV][RECV_SERV_OPTIONS_SEM] = TRUE; } return rc; } int grab_sem_immediate(int set_index, int sem_num) { int rc; ASSERT_SET_INDEX; assert(!holds_sem[set_index][sem_num]); replsop[0].sem_op = 0; /* Wait for 0 */ replsop[0].sem_num = sem_num; replsop[1].sem_op = 1; /* Increment it */ replsop[1].sem_num = sem_num; replsop[0].sem_flg = replsop[1].sem_flg = SEM_UNDO | IPC_NOWAIT; SEMOP(sem_set_id[set_index], replsop, 2, rc, NO_WAIT); if (0 == rc) holds_sem[set_index][sem_num] = TRUE; return rc; } int rel_sem(int set_index, int sem_num) { int rc; ASSERT_SET_INDEX; assert(holds_sem[set_index][sem_num]); rc = do_semop(sem_set_id[set_index], sem_num, -1, SEM_UNDO); if (0 == rc) holds_sem[set_index][sem_num] = FALSE; return rc; } int decr_sem(int set_index, int sem_num) { return rel_sem(set_index, sem_num); } int rel_sem_immediate(int set_index, int sem_num) { int rc; ASSERT_SET_INDEX; assert(holds_sem[set_index][sem_num]); rc = do_semop(sem_set_id[set_index], sem_num, -1, SEM_UNDO | IPC_NOWAIT); if (0 == rc) holds_sem[set_index][sem_num] = FALSE; return rc; } int get_sem_info(int set_index, int sem_num, sem_info_type info_id) /* Cannot be used to get info which require an additional argument. See man semctl */ { ASSERT_SET_INDEX; return(semctl(sem_set_id[set_index], sem_num, sem_info_id_map[info_id])); } int remove_sem_set(int set_index) { int rc, i; ASSERT_SET_INDEX; rc = sem_rmid(sem_set_id[set_index]); if (!rc) /* successful removal of sem set */ { sem_set_id[set_index] = 0; for (i = 0; i < NUM_SRC_SEMS; i++) holds_sem[set_index][i] = FALSE; } return rc; } fis-gtm-V7.0-005/sr_unix/repl_sem.h0000755000032200000250000000514214342376330016025 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2018 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. * * * ****************************************************************/ #ifndef REPL_SEM_H_INCLUDED #define REPL_SEM_H_INCLUDED #include "repl_sem_sp.h" #define ASSERT_SET_INDEX assert (NUM_SEM_SETS > set_index) enum { SOURCE, RECV, NUM_SEM_SETS }; typedef enum { JNL_POOL_ACCESS_SEM, /* For Startup / Shutdown / Options-change */ SRC_SERV_COUNT_SEM, /* Source sever holds it while alive */ RECV_SERV_STARTUP_SEM, /* records the # of MUPIP REPLIC -RECEIV -START commands in progress at any point in time */ DUMMY_SEM1, /* added just to make the number of semaphores same as in recvpool */ SOURCE_ID_SEM, NUM_SRC_SEMS } source_sem_type; /* The need for the options semaphore is documented towards the end of Section 6.2.6 of the design spec for C9F06-002729 */ typedef enum { RECV_POOL_ACCESS_SEM, /* For Startup / Shutdown */ RECV_SERV_COUNT_SEM, /* Receiver sever holds it while alive */ UPD_PROC_COUNT_SEM, /* Update process holds it while alive */ RECV_SERV_OPTIONS_SEM, /* For options change, since it is done through the shared memory */ RECV_ID_SEM, NUM_RECV_SEMS } recv_sem_type; typedef enum { SEM_INFO_VAL, SEM_INFO_PID, SEM_NUM_INFOS } sem_info_type; int grab_sem(int set_index, int sem_num); /* set_index can be SOURCE or RECV */ int grab_sem_immediate(int set_index, int sem_num); int rel_sem(int set_index, int sem_num); int rel_sem_immediate(int set_index, int sem_num); int get_sem_info(int set_index, int sem_num, sem_info_type info_id); int remove_sem_set(int set_index); boolean_t sem_set_exists(int which_set); #ifdef UNIX void rel_recvpool_ftok_sems(boolean_t, boolean_t); void rel_jnlpool_ftok_sems(boolean_t, boolean_t); void lock_recvpool_ftok_sems(boolean_t, boolean_t); void lock_jnlpool_ftok_sems(boolean_t, boolean_t); void get_lock_recvpool_ftok_sems(boolean_t, boolean_t); void get_lock_jnlpool_ftok_sems(boolean_t, boolean_t); void set_sem_set_src(int semid); void set_sem_set_recvr(int semid); int grab_sem_all_source(void); /* rollback needs this */ int grab_sem_all_receive(void); /* rollback needs this */ int incr_sem(int set_index, int sem_num); int decr_sem(int set_index, int sem_num); #endif #endif /* _REPL_SEM_H */ fis-gtm-V7.0-005/sr_unix/repl_sem_sp.h0000755000032200000250000000153214342376330016526 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef REPL_SEM_SP_H_INCLUDED #define REPL_SEM_SP_H_INCLUDED #define REPL_SEM_NOT_GRABBED (EAGAIN == errno) typedef int sem_key_t; typedef int permissions_t; int init_sem_set_source(sem_key_t key, int nsems, permissions_t sem_flags); int init_sem_set_recvr(sem_key_t key, int nsems, permissions_t sem_flags); #endif /* _REPL_SEM_SP_H */ fis-gtm-V7.0-005/sr_unix/repl_sp.h0000755000032200000250000000204314342376330015660 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef REPL_SP_H_INCLUDED #define REPL_SP_H_INCLUDED #define ERRNO (errno) #define FORMAT_STR "PID %d %s is%s alive\n" #define FORMAT_STR1 "PID %d %s is%s alive in %s mode\n" /*----- File I/O related -----*/ #define F_CLOSE(FD, RC) CLOSEFILE_RESET(FD, RC) /* interrupt safe close(); also resets "FD" to FD_INVALID */ #define F_COPY_GDID(to, from) memcpy(&(to), &(from), SIZEOF(to)) #define F_COPY_GDID_FROM_STAT(to, stat_buf) set_gdid_from_stat(&(to), &stat_buf); #define F_READ_BLK_ALIGNED LSEEKREAD #endif fis-gtm-V7.0-005/sr_unix/resetterm.c0000644000032200000250000000233014342376330016215 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_termios.h" #include "gtm_signal.h" /* for SIGPROCMASK used inside Tcsetattr */ #include "io.h" #include "iottdef.h" #include "gtmio.h" #include "eintr_wrappers.h" #include "setterm.h" #include "gtm_isanlp.h" error_def(ERR_TCSETATTR); void resetterm(io_desc *iod) { int status; int save_errno; struct termios t; d_tt_struct *ttptr; ttptr =(d_tt_struct *) iod->dev_sp; if (ttptr->ttio_struct) { t = *ttptr->ttio_struct; Tcsetattr(ttptr->fildes, TCSANOW, &t, status, save_errno); if (status != 0) { if (gtm_isanlp(ttptr->fildes) == 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_TCSETATTR, 1, ttptr->fildes, save_errno); } } return; } fis-gtm-V7.0-005/sr_unix/rmv_mul_slsh.c0000644000032200000250000000200214342376330016711 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2017 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 "rmv_mul_slsh.h" #include "gtm_string.h" #include "mdef.h" /*Remove multiple slashes from the source and returns the new length*/ /*Note this function does not null terminate the new string.*/ /*This is the responsibility of the calling function*/ unsigned int rmv_mul_slsh(char *src, unsigned int src_len) { char *ci, *co, *ct; ci = co = src; ct = ci + src_len - 1; while (ci < ct) { if (('/' != *ci) || ('/' != *(ci + 1))) *co++ = *ci++; else ci++; } *co = *ci; return (++co - src); } fis-gtm-V7.0-005/sr_unix/rmv_mul_slsh.h0000755000032200000250000000124514342376330016731 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2017 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. * * * ****************************************************************/ #ifndef RMV_MUL_SLSH_H_INCLUDED #define RMV_MUL_SLSH_H_INCLUDED unsigned int rmv_mul_slsh(char *src, unsigned int src_len); #endif /* RMV_MUL_SLSH_H_INCLUDED */ fis-gtm-V7.0-005/sr_unix/rtnhdr.h0000755000032200000250000003415514342376330015526 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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. * * * ****************************************************************/ #ifndef RTNHDR_H_INCLUDED #define RTNHDR_H_INCLUDED #include "srcline.h" /* rtnhdr.h - routine header for shared binary Unix platforms */ /* There are several references to this structure from assembly language; these include: * * From Unix: g_msf.si * * Any changes to the routine header must be reflected in those files as well. * * Warning: the list above may not be complete. */ /* Variable table entry */ typedef mname_entry var_tabent; /* the actual variable name is stored in the literal text pool */ /* Linenumber table entry */ typedef int4 lnr_tabent; /* Label table entry */ typedef struct { mident lab_name; /* The name of the label */ lnr_tabent *lnr_adr; /* Pointer to lnrtab entry offset into code for this label */ boolean_t has_parms; /* Flag to indicate whether the callee has a formallist */ GTM64_ONLY(int4 filler;) } lab_tabent; /* Linkage table entry */ typedef struct { char_ptr_t ext_ref; /* Address (quadword on alpha) this linkage entry resolves to or NULL */ } lnk_tabent; #ifdef AUTORELINK_SUPPORTED #include "relinkctl.h" /* Needed for open_relinkctl_sgm type in zro_validation_entry */ /* Link search history entry - contains information to find the record in the relevant relinkctl file and what * the cycle was when loaded. */ typedef struct { uint4 cycle; /* Copy of relinkctl file cycle when loaded */ relinkrec_t *relinkrec; /* Pointer to relinkctl record */ open_relinkctl_sgm *relinkctl_bkptr; /* Address of mapped relinkctl file */ } zro_validation_entry; /* Header for search history block */ typedef struct { uint4 zroutines_cycle; /* Value of set_zroutines_cycle when history created */ zro_validation_entry *end; /* -> Last record + 1 */ zro_validation_entry base[1]; /* First history record (others follow) */ } zro_hist; /* Structure used to queue up a list of validation entries and routine names before finalizing them into a * zro_hist structure. */ typedef struct { zro_validation_entry zro_valent; /* Validation entry data (this record) */ mident_fixed rtnname; /* Routine name associated with this validation entry */ int4 rtnname_len; } zro_search_hist_ent; #endif /* rhead_struct is the routine header; it occurs at the beginning of the * object code part of each module. Since this structure may be resident in * a shared library, this structure is considered inviolate. Therefore there is * a process-private version of this header that is modified as necessary and * always points to the current version. * * The routine header is initialized when a module is first linked into * an executable. The fields marked with "(#)" are updated when the routine * is replaced by a newer version via explicit zlink. */ typedef struct rhead_struct { char jsb[RHEAD_JSB_SIZE]; /* GTM_CODE object marker */ void_ptr_t shlib_handle; /* Null if header not for shared object. Non-zero means header is * describing shared library resident routine and this is its handle. * Note this is an 8 byte field on Tru64 (hence its position near top). */ mstr src_full_name; /* (#) Fully qualified path of routine source code */ uint4 compiler_qlf; /* Bit flags of compiler qualifiers used (see cmd_qlf.h) */ uint4 objlabel; /* Object code level/label (see objlable.h). * Note: this field must be the 10th word (11th on Tru64) on 32-bit * environments so that incr_link() can deference object label from old * (pre-V5 32-bit) objects as well. In 64-bit environments though, this * situation wouldn't occur since dlopen() would/should have failed * when a 32-bit shared library is loaded */ mident routine_name; /* External routine name */ var_tabent *vartab_adr; /* (#) Address of variable table (offset in original rtnhdr) */ int4 vartab_len; /* (#) Number of variable table entries */ lab_tabent *labtab_adr; /* Address of label table (offset in original rtnhdr) */ int4 labtab_len; /* Number of label table entries */ lnr_tabent *lnrtab_adr; /* Address of linenumber table (offset in original rtnhdr) */ int4 lnrtab_len; /* Number of linenumber table entries */ unsigned char *literal_text_adr; /* Address of literal text pool (offset in original rtnhdr) */ int4 literal_text_len; /* Length of literal text pool */ boolean_t shared_object; /* Linked as a shared object */ mval *literal_adr; /* (#) Address of literal mvals (offset in original rtnhdr) */ int4 literal_len; /* Number of literal mvals */ lnk_tabent *linkage_adr; /* (#) Address of linkage Psect (offset in original rtnhdr) */ int4 linkage_len; /* Number of linkage entries */ int4 rel_table_off; /* Offset to relocation table (not kept) */ int4 sym_table_off; /* Offset to symbol table (not kept) */ boolean_t rtn_relinked; /* Routine has been relinked while this version was active. Check on * unwind to see if this routine needs to be cleaned up. */ unsigned char *shared_ptext_adr; /* If shared routine (shared library or object), points to shared copy */ unsigned char *ptext_adr; /* (#) address of start of instructions (offset in original rtnhdr) * If shared routine, points to shared copy unless breakpoints are active. * In that case, points to private copy. */ unsigned char *ptext_end_adr; /* (#) Address of end of instructions + 1 (offset in original rtnhdr) */ int4 checksum; /* 4-byte source code checksum (for platforms where MD5 is unavailable) */ int4 temp_mvals; /* (#) temp_mvals value of current module version */ int4 temp_size; /* (#) temp_size value of current module version */ boolean_t has_ZBREAK; /* This routine has a ZBREAK in it - disable it for autorelink */ struct rhead_struct *current_rhead_adr; /* (#) Address of routine header of current module version */ struct rhead_struct *old_rhead_adr; /* (#) Chain of replaced routine headers */ # ifdef GTM_TRIGGER void_ptr_t trigr_handle; /* Type is void to avoid needing gv_trigger.h for gv_trigger_t type addr */ # else void_ptr_t filler1; # endif unsigned char checksum_128[16]; /* 16-byte MurmurHash3 checksum of routine source code */ struct rhead_struct *active_rhead_adr; /* addr of copy of this (original) header when that new version replaced * this routine and was on M-Stack. See handle_active_versions() rtn. */ routine_source *source_code; /* Source code used by $TEXT */ ARLINK_ONLY(zro_hist *zhist;) /* If shared object -> validation list/array */ gtm_uint64_t objhash; /* When object file is created, contains hash of object file */ unsigned char *lbltext_ptr; /* Label name text blob if shared object replaced */ uint4 object_len; /* Length of wrapped GT.M object */ uint4 routine_source_offset; /* Offset of M source within literal text pool */ mstr *linkage_names; /* Offset to mstr table of symbol names indexed same as linkage table */ # ifdef AUTORELINK_SUPPORTED open_relinkctl_sgm *relinkctl_bkptr; /* Back pointer to relinkctl file that loaded this shared rtnobj */ # endif } rhdtyp; /* Routine table entry */ typedef struct { mident rt_name; /* The name of the routine (in the literal text pool) */ rhdtyp *rt_adr; /* Pointer to its routine header */ } rtn_tabent; /* Linkage table proxy for run-time linking of indirects. This struct has some overloaded meanings: * 1. The group of fields rtnhdr_adr/lnr_adr act as a proxy/surrogate linkage table holding those * two values. They are defined in code with the indexes 0 and 1 respectively. * 2. The group of fields lnr_adr/has_parms pretend they are the proxy part of a label table * entry. Assembler code expects has_parms to be in the location following lnr_adr. */ typedef struct { rhdtyp *rtnhdr_adr; /* Pointer to routine header for called routine */ lnr_tabent *lnr_adr; /* Pointer to lnrtab entry offset into code for this label */ boolean_t has_parms; /* Flag to indicate whether the callee has a formallist */ int filler1; /* 64 bit alignment */ } lnk_tabent_proxy; #define TABENT_PROXY TREF(lnk_proxy) /* Byte offset of the routine_name field in the routine headers of pre-V5 releases */ #define PRE_V5_RTNHDR_RTNOFF 24 /* Byte offset of the routine_name mstr (len,addr) in V50 and V51 - only used in Tru64/HPUX-HPPA */ #if defined(__osf__) || defined(__hppa) # define V50V51_RTNHDR_RTNMSTR_OFFSET 24 # ifdef __osf__ # define V50V51_FTNHDR_LITBASE_OFFSET 68 # elif defined(__hppa) # define V50V51_FTNHDR_LITBASE_OFFSET 64 # else # error "Unsupported platform" # endif typedef struct { unsigned int len; /* Byte length */ int4 *addr; /* Offset at this stage */ } v50v51_mstr; #endif /* Macros for accessing routine header fields in a portable way */ #define VARTAB_ADR(rtnhdr) ((rtnhdr)->vartab_adr) #define LABTAB_ADR(rtnhdr) ((rtnhdr)->labtab_adr) #define LNRTAB_ADR(rtnhdr) ((rtnhdr)->lnrtab_adr) #define LITERAL_ADR(rtnhdr) ((rtnhdr)->literal_adr) #define LINKAGE_ADR(rtnhdr) ((rtnhdr)->linkage_adr) #define PTEXT_ADR(rtnhdr) ((rtnhdr)->ptext_adr) #define PTEXT_END_ADR(rtnhdr) ((rtnhdr)->ptext_end_adr) #define CURRENT_RHEAD_ADR(rtnhdr) ((rtnhdr)->current_rhead_adr) #define OLD_RHEAD_ADR(rtnhdr) ((rtnhdr)->old_rhead_adr) #define LINE_NUMBER_ADDR(rtnhdr, lnr_tabent_adr) ((rtnhdr)->ptext_adr + *(lnr_tabent_adr)) #define LABENT_LNR_ENTRY(rtnhdr, lab_tabent_adr) ((lab_tabent_adr)->lnr_adr) #define LABEL_ADDR(rtnhdr, lab_tabent_adr) (CODE_BASE_ADDR(rtnhdr) + *(LABENT_LNR_ENTRY(rtnhdr, lab_tabent_adr))) #define CODE_BASE_ADDR(rtnhdr) ((rtnhdr)->ptext_adr) #define CODE_OFFSET(rtnhdr, addr) ((char *)(addr) - (char *)(rtnhdr->ptext_adr)) #define DYNAMIC_LITERALS_ENABLED(rtnhdr) ((rtnhdr)->compiler_qlf & CQ_DYNAMIC_LITERALS) #define RW_REL_START_ADR(rtnhdr) (((DYNAMIC_LITERALS_ENABLED(rtnhdr)) ? (char *)VARTAB_ADR(rtnhdr) : (char *)LITERAL_ADR(rtnhdr))) #define PTEXT_OFFSET SIZEOF(rhdtyp) /* Macro to determine if given address is inside code segment. Note that even though * the PTEXT_END_ADR macro is the address of end_of_code + 1, we still want a <= check * here because in many cases, the address being tested is the RETURN address from a * call that was done as the last instruction in the code segment. Sometimes this call * is to an error or it could be the implicit quit. On HPUX, the delay slot for the * implicit quit call at the end of the module can also cause the problem. Without * the "=" check also being there, the test will fail when it should succeed. */ #define ADDR_IN_CODE(caddr, rtnhdr) (PTEXT_ADR((rtnhdr)) <= (caddr) && (caddr) <= PTEXT_END_ADR((rtnhdr))) /* Types that are different depending on shared/unshared unix binaries */ #define LABENT_LNR_OFFSET lnr_adr /* When a routine is recursively linked, the old routine hdr/label table are copied and * attached to active_rhead_adr pointer of the replaced routine header. At unwind or goto time, * we check if these copies can be cleaned up with the following macro. */ #define CLEANUP_COPIED_RECURSIVE_RTN(RTNHDR) \ { \ /* For USHBIN_SUPPORTED routines, if the rtn_relinked flag is ON in the routine header (which results from not cleaning \ * up a routine when it is either explicitly or automatically re-linked), check if the stack contains any more \ * references to this routine header. If not, it is ripe for cleanup as it has been replaced so drive zr_unlink_rtn() \ * on it. \ */ \ if ((RTNHDR)->rtn_relinked) \ zr_cleanup_recursive_rtn(RTNHDR); \ } /* Format of a relocation datum. */ struct relocation_info { int r_address; /* address which is relocated */ unsigned int r_symbolnum; /* local symbol ordinal */ }; struct rel_table { struct rel_table *next, *resolve; struct relocation_info r; }; /* Format of a symbol table entry; this file is included by * and should be used if you aren't interested the a.out header * or relocation information. */ struct nlist { int4 n_type; /* Type flag, i.e. N_TEXT etc; see below */ uint4 n_value; /* Value of this symbol (or sdb offset) */ }; struct sym_table { struct sym_table *next; struct nlist n; struct rel_table *resolve; int4 linkage_offset; unsigned short name_len; unsigned char name[1]; }; /* Simple values for n_type. */ #define N_TEXT 0x04 /* Text */ #define N_EXT 0x01 /* cexternal bit, or'ed in */ /* Flag values for get_src_line call */ #define VERIFY TRUE #define NOVERIFY FALSE /* Prototypes */ int get_src_line(mval *routine, mval *label, int offset, mstr **srcret, rhdtyp **rtn_vec); void free_src_tbl(rhdtyp *rtn_vector); unsigned char *find_line_start(unsigned char *in_addr, rhdtyp *routine); int4 *find_line_addr(rhdtyp *routine, mstr *label, int4 offset, mident **lent_name); rhdtyp *find_rtn_hdr(mstr *name); boolean_t find_rtn_tabent(rtn_tabent **res, mstr *name); bool zlput_rname(rhdtyp *hdr); void zlmov_lnames(rhdtyp *hdr); rhdtyp *make_dmode(void); void comp_lits(rhdtyp *rhead); int op_rhdaddr(mval *name, int rtnidx); int op_labaddr(int rtnidx, mval *label, int4 offset); void urx_resolve(rhdtyp *rtn, lab_tabent *lbl_tab, lab_tabent *lbl_top); char *rtnlaboff2entryref(char *entryref_buff, mident *rtn, mident *lab, int offset); boolean_t on_stack(rhdtyp *rtnhdr, boolean_t *need_duplicate); rhdtyp *op_rhd_ext(mval *rtname, mval *lblname, rhdtyp *rhd, void *lnr); void *op_lab_ext(void); void zr_cleanup_recursive_rtn(rhdtyp *rtnhdr); #ifdef AUTORELINK_SUPPORTED #include "zroutinessp.h" /* Needed for zro_ent type for zro_record_zhist declaration */ boolean_t need_relink(rhdtyp *rtnhdr, zro_hist *zhist); zro_hist *zro_zhist_saverecent(zro_search_hist_ent *zhist_valent, zro_search_hist_ent *zhist_valent_base); void zro_record_zhist(zro_search_hist_ent *zhist_valent, zro_ent *obj_container, mstr *rtnname); #endif /* AUTORELINK_DEFINED */ #endif /* RTNHDR_H_INCLUDED */ fis-gtm-V7.0-005/sr_unix/rtnobj.c0000644000032200000250000014310614342376335015515 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2022 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 #include #include "gtm_ipc.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "rtnobj.h" #include "gtmio.h" #include #include "iosp.h" #include "do_shmat.h" #include "min_max.h" #include "relqop.h" #include "ipcrmid.h" #include "interlock.h" #include "gdsroot.h" /* for CDB_STAGNATE */ #include "incr_link.h" /* for NATIVE_HDR_LEN */ #include "hugetlbfs_overrides.h" /* for the ADJUST_SHM_SIZE_FOR_HUGEPAGES macro */ #include "gtm_permissions.h" #include "cachectl.h" #include "cacheflush.h" #include "send_msg.h" #include "wbox_test_init.h" /* Uncomment below line if you want RTNOBJ_DBG lines to be enabled */ /* #define RTNOBJ_DEBUG */ #ifdef DEBUG #define MAXVERS_TEST 2 #endif #ifdef RTNOBJ_DEBUG # define RTNOBJ_DBG(X) X #else # define RTNOBJ_DBG(X) #endif #define DBG_ASSERT_INDEX_VALID(MIN_INDEX, MAX_INDEX) \ { \ assert(0 <= MIN_INDEX); \ assert(NUM_RTNOBJ_SHM_INDEX >= MIN_INDEX); \ assert(0 <= MAX_INDEX); \ assert(NUM_RTNOBJ_SHM_INDEX >= MAX_INDEX); \ } /* Macro to make sure this process has attached to ALL rtnobj shared memory segments that currently exist for the * relinkctl shared memory corresponding to LINKCTL. Need to execute this macro before checking if a given routine object * does exist in one of the rtnobj shared memory segments. */ #define SYNC_RTNOBJ_SHMID_CYCLE_IF_NEEDED(LINKCTL, MIN_INDEX, MAX_INDEX, SHM_HDR, HAS_RELINKCTL_LOCK, RELINKREC) \ { \ int pvt_max_index, i, pvt_shmid, shr_shmid; \ int save_errno; \ size_t shm_size; \ sm_uc_ptr_t shm_base; \ char errstr[256]; \ \ MAX_INDEX = SHM_HDR->rtnobj_max_shm_index; \ SHM_READ_MEMORY_BARRIER; /* Read "rtnobj_max_shm_index" BEFORE read memory barrier and everything else \ * else associated with this index AFTER the memory barrier. \ */ \ pvt_max_index = LINKCTL->rtnobj_max_shm_index; \ DEBUG_ONLY( \ for (i = LINKCTL->rtnobj_min_shm_index; i < pvt_max_index; i++) \ { \ pvt_shmid = LINKCTL->rtnobj_shmid[i]; \ shr_shmid = SHM_HDR->rtnobj_shmhdr[i].rtnobj_shmid; \ assert(pvt_shmid == shr_shmid); \ } \ ) \ if (pvt_max_index != MAX_INDEX) \ { \ MIN_INDEX = SHM_HDR->rtnobj_min_shm_index; \ DBG_ASSERT_INDEX_VALID(MIN_INDEX, MAX_INDEX); /* assert validity of min_index/max_index */ \ for (i = pvt_max_index; i < MAX_INDEX; i++) \ { \ pvt_shmid = LINKCTL->rtnobj_shmid[i]; \ shr_shmid = SHM_HDR->rtnobj_shmhdr[i].rtnobj_shmid; \ if (pvt_shmid != shr_shmid) \ { \ assert(INVALID_SHMID == pvt_shmid); /* currently rtnobj shmids are never deleted */ \ shm_size = ((size_t)1 << (i + MIN_RTNOBJ_SHM_INDEX)); \ shm_base = (sm_uc_ptr_t)do_shmat_exec_perm(shr_shmid, shm_size, &save_errno); \ if (-1 == (sm_long_t)shm_base) \ { \ if (HAS_RELINKCTL_LOCK) \ rel_latch(&SHM_HDR->relinkctl_latch); \ rel_latch(&RELINKREC->rtnobj_latch); \ SNPRINTF(errstr, SIZEOF(errstr), "rtnobj2 shmat() failed for shmid=%d shmsize=0x%llx", \ shr_shmid, shm_size); \ if (!SHM_REMOVED(save_errno)) \ ISSUE_RELINKCTLERR_SYSCALL(&LINKCTL->zro_entry_name, errstr, save_errno); \ else \ ISSUE_REQRLNKCTLRNDWN_SYSCALL(LINKCTL, errstr, save_errno); \ } \ assert(0 == ((UINTPTR_T)shm_base % 8)); \ LINKCTL->rtnobj_shmid[i] = shr_shmid; \ LINKCTL->rtnobj_shm_base[i] = shm_base; \ } \ } \ LINKCTL->rtnobj_min_shm_index = MIN_INDEX; \ LINKCTL->rtnobj_max_shm_index = MAX_INDEX; \ } else \ { \ MIN_INDEX = LINKCTL->rtnobj_min_shm_index; \ MAX_INDEX = pvt_max_index; \ DBG_ASSERT_INDEX_VALID(MIN_INDEX, MAX_INDEX); \ } \ } #ifdef AUTORELINK_SUPPORTED error_def(ERR_RELINKCTLERR); error_def(ERR_RLNKINTEGINFO); error_def(ERR_RLNKRECLATCH); error_def(ERR_RLNKRECNFL); error_def(ERR_RLNKSHMLATCH); error_def(ERR_SYSCALL); DEBUG_ONLY(GBLREF int saved_errno;) DEBUG_ONLY(GBLREF int process_exiting;) #ifdef DEBUG /* Routine to verify the doubly-linked (fl/bl) freelists for each rtnobj size (various 2-powers) are in good shape */ void rtnobj_verify_freelist_fl_bl(rtnobjshm_hdr_t *rtnobj_shm_hdr, sm_uc_ptr_t shm_base) { int min_index, max_index, i; que_ent_ptr_t freeList, elem, prev_elem; rtnobj_hdr_t *rtnobj, *rtnobj2; sm_off_t rtnobj_off; gtm_uint64_t elemSize; sm_uc_ptr_t shm_top; freeList = &rtnobj_shm_hdr->freeList[0]; shm_top = shm_base + rtnobj_shm_hdr->shm_len; for (i = 0; i < NUM_RTNOBJ_SIZE_BITS; i++, freeList++) { if (NULL_RTNOBJ_SM_OFF_T != freeList->fl) { assert(NULL_RTNOBJ_SM_OFF_T != freeList->bl); assert(0 == freeList->fl % 8); prev_elem = NULL; elem = (que_ent_ptr_t)(shm_base + freeList->fl); do { rtnobj = (rtnobj_hdr_t *)((sm_uc_ptr_t)elem - OFFSETOF(rtnobj_hdr_t, userStorage)); assert(STATE_FREE == rtnobj->state); assert(i == rtnobj->queueIndex); rtnobj_off = (sm_uc_ptr_t)rtnobj - shm_base; elemSize = ((gtm_uint64_t)1 << (rtnobj->queueIndex + MIN_RTNOBJ_SIZE_BITS)); rtnobj2 = (rtnobj_hdr_t *)(shm_base + (rtnobj_off ^ elemSize)); /* address of buddy */ assert(((sm_uc_ptr_t)rtnobj2 == shm_top) || (rtnobj2->queueIndex <= rtnobj->queueIndex)); if (NULL_RTNOBJ_SM_OFF_T == elem->bl) assert(NULL == prev_elem); else assert(((NULL == prev_elem) && (NULL_RTNOBJ_SM_OFF_T == elem->bl)) || (prev_elem->fl == -elem->bl)); if (NULL_RTNOBJ_SM_OFF_T == elem->fl) { assert(elem == (que_ent_ptr_t)(shm_base + freeList->bl)); break; } assert(0 == elem->fl % 8); prev_elem = elem; elem = (que_ent_ptr_t)((sm_uc_ptr_t)elem + elem->fl); } while (TRUE); } else assert(NULL_RTNOBJ_SM_OFF_T == freeList->bl); } } /* Routine to verify the correctness of derived fields, rtnobj_shm_hdr->rtnobj_min_free_index & rtnobj->rtnobj_max_free_index */ void rtnobj_verify_min_max_free_index(rtnobjshm_hdr_t *rtnobj_shm_hdr) { int min_index, max_index, i; que_ent_ptr_t freeList; min_index = NUM_RTNOBJ_SIZE_BITS; max_index = 0; freeList = &rtnobj_shm_hdr->freeList[0]; for (i = 0; i < NUM_RTNOBJ_SIZE_BITS; i++, freeList++) { if (NULL_RTNOBJ_SM_OFF_T != freeList->fl) { if (min_index > i) min_index = i; if (max_index < (i + 1)) max_index = i + 1; } } assert(min_index == rtnobj_shm_hdr->rtnobj_min_free_index); assert(max_index == rtnobj_shm_hdr->rtnobj_max_free_index); } #endif /* Insert "new_tail" at tail of the doubly-linked relative-queue starting from "que_base". * Note "que_base" is a pointer to relinkctl shared memory whereas "new_tail" is a pointer to rtnobj shared memory. * Hence the need for NULL_RTNOBJ_SM_OFF_T to indicate a link from rtnobj shm to relinkctl shm. * "shm_base" is pointer to the start of rtnobj shared memory. Used to calculate relative offsets. */ void insqt_rtnobj(que_ent_ptr_t new_tail, que_ent_ptr_t que_base, sm_uc_ptr_t shm_base) { que_ent_ptr_t old_tail; RTNOBJ_DBG(fprintf(stderr, "insqt_rtnobj : que_base = 0x%llx : shm_base = 0x%llx : elem = 0x%llx\n", \ (UINTPTR_T)que_base, (UINTPTR_T)shm_base, (UINTPTR_T)new_tail);) if (NULL_RTNOBJ_SM_OFF_T == que_base->bl) { /* Queue has nothing */ assert(NULL_RTNOBJ_SM_OFF_T == que_base->fl); que_base->fl = (uchar_ptr_t)new_tail - (uchar_ptr_t)shm_base; assert(0 == que_base->fl % 8); que_base->bl = que_base->fl; new_tail->fl = NULL_RTNOBJ_SM_OFF_T; new_tail->bl = NULL_RTNOBJ_SM_OFF_T; } else { old_tail = (que_ent_ptr_t)(shm_base + que_base->bl); assert(NULL_RTNOBJ_SM_OFF_T == old_tail->fl); new_tail->fl = NULL_RTNOBJ_SM_OFF_T; new_tail->bl = (uchar_ptr_t)old_tail - (uchar_ptr_t)new_tail; assert(0 == new_tail->bl % 8); old_tail->fl = -new_tail->bl; que_base->bl += old_tail->fl; } return; } /* Remove an element from the head of the doubly-linked relative-queue starting from "que_base" and return a pointer to it. * Note "que_base" is a pointer to relinkctl shared memory whereas the returned value is a pointer to rtnobj shared memory. * Hence the need for NULL_RTNOBJ_SM_OFF_T to indicate a link from rtnobj shm to relinkctl shm. * "shm_base" is pointer to the start of rtnobj shared memory. Used to calculate relative offsets. */ rtnobj_hdr_t *remqh_rtnobj(que_ent_ptr_t que_base, sm_uc_ptr_t shm_base) { que_ent_ptr_t ret, new_head; rtnobj_hdr_t *rtnobj; assert(NULL_RTNOBJ_SM_OFF_T != que_base->fl); ret = (que_ent_ptr_t)(shm_base + que_base->fl); RTNOBJ_DBG(fprintf(stderr, "remqh_rtnobj : que_base = 0x%llx : shm_base = 0x%llx : elem = 0x%llx\n", \ (UINTPTR_T)que_base, (UINTPTR_T)shm_base, (UINTPTR_T)ret);) assert(0 == (sm_off_t)ret % 8); assert(NULL_RTNOBJ_SM_OFF_T == ret->bl); if (NULL_RTNOBJ_SM_OFF_T == ret->fl) { /* Empty queue after this removal */ que_base->fl = NULL_RTNOBJ_SM_OFF_T; que_base->bl = NULL_RTNOBJ_SM_OFF_T; } else { new_head = (que_ent_ptr_t)((uchar_ptr_t)ret + ret->fl); assert(0 == (sm_off_t)new_head % 8); new_head->bl = NULL_RTNOBJ_SM_OFF_T; assert(0 == que_base->fl % 8); que_base->fl += ret->fl; } rtnobj = (rtnobj_hdr_t *)((sm_uc_ptr_t)ret - OFFSETOF(rtnobj_hdr_t, userStorage)); return rtnobj; } /* Routine to help traversing the open_relinkctl_sgm struct */ rtnobj_hdr_t *rtnobj_retpos(open_relinkctl_sgm *linkctl, rtnobj_sm_off_t shm_index_off) { int shm_index; sm_off_t shm_off; shm_index = RTNOBJ_GET_SHM_INDEX(shm_index_off); shm_off = RTNOBJ_GET_SHM_OFFSET(shm_index_off); return ((rtnobj_hdr_t *)(linkctl->rtnobj_shm_base[shm_index] + shm_off)); } /* Use the Floyd Cycle Detection * This algorithm determines if there is a loop in the linked list by using two iterator: * Slow iterator - moves one step at a time. * Fast iterator - moves two steps at a time. * If there is loop the two iterators have to collide, otherwise the fast iterator reaches * the end of the list and returns the number of elements. */ void rtnobj_integ(relinkrec_t *relinkrec, open_relinkctl_sgm *linkctl) { rtnobj_hdr_t *rtnobj; rtnobj_sm_off_t shm_index_off_slow, shm_index_off_fast, shm_index_off_prev, shm_index_off_loop_found; boolean_t complete_loop = TRUE; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; shm_index_off_slow = shm_index_off_fast = relinkrec->rtnobj_shm_offset; relinkrec->numvers = 0; while ((rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T != shm_index_off_fast) { shm_index_off_prev = shm_index_off_slow; rtnobj = rtnobj_retpos(linkctl, shm_index_off_slow); /* Move the slow runner one step forward */ shm_index_off_slow = rtnobj->next_rtnobj_shm_offset; relinkrec->numvers++; if (TREF(gtm_autorelink_ctlmax) == relinkrec->numvers) { /* verify we don't traverse more than the maximum number of routines that the * shared memory segment is capable of holding. */ rtnobj = rtnobj_retpos(linkctl, shm_index_off_fast); rtnobj->next_rtnobj_shm_offset = (rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_RLNKINTEGINFO, 5, LEN_AND_LIT("number of elements in shm segment exceeded gtm_autorelink_ctlmax - fixed"), CALLFROM, 1, 1); return; } rtnobj = rtnobj_retpos(linkctl, shm_index_off_fast); /* Move the fast runner one step forward */ shm_index_off_fast = rtnobj->next_rtnobj_shm_offset; if((rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T == shm_index_off_fast) { /* the fast iterator moves two steps at a time, therefore a check is needed to * verify that the first step is not NULL. "NULL->next" would cause a seg fault. */ break; } relinkrec->numvers++; if (TREF(gtm_autorelink_ctlmax) == relinkrec->numvers) { /* verify we don't traverse more than the maximum number of routines that the * shared memory segment is capable of holding. */ rtnobj = rtnobj_retpos(linkctl, shm_index_off_fast); rtnobj->next_rtnobj_shm_offset = (rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_RLNKINTEGINFO, 5, LEN_AND_LIT("number of elements in shm segment exceeded gtm_autorelink_ctlmax - fixed"), CALLFROM, 1, 1); return; } rtnobj = rtnobj_retpos(linkctl, shm_index_off_fast); /* Move the fast runner one more step forward */ shm_index_off_fast = rtnobj->next_rtnobj_shm_offset; if (shm_index_off_slow == shm_index_off_fast) { /* A loop found, and we need to check where it started. * We keep the slow iterator in the collision-point and move the fast iterator * to the head (relinkrec->rtnobj_shm_offset). */ relinkrec->numvers /= 2; /* Slow iterator steps */ shm_index_off_fast = relinkrec->rtnobj_shm_offset; shm_index_off_loop_found = shm_index_off_slow; while(shm_index_off_slow != shm_index_off_fast) { /* Move the two iterators one step at a time, which would cause them to * collide again at the loop-start. */ complete_loop = FALSE; shm_index_off_prev = shm_index_off_slow; rtnobj = rtnobj_retpos(linkctl, shm_index_off_slow); /* Move the slow runner one step forward */ shm_index_off_slow = rtnobj->next_rtnobj_shm_offset; rtnobj = rtnobj_retpos(linkctl, shm_index_off_fast); /* Move the fast runner one step forward */ shm_index_off_fast = rtnobj->next_rtnobj_shm_offset; } /* The loop-start found, so we use the prev pointer(one step behind the slow iterator), * and change it to point to NULL which breaks the infinite loop. */ rtnobj = rtnobj_retpos(linkctl, shm_index_off_prev); rtnobj->next_rtnobj_shm_offset = (rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T; /* If complete_loop is TRUE, the last element in the list originaly pointed to the first one */ if (complete_loop) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_RLNKINTEGINFO, 5, LEN_AND_LIT("a loop to the start of linkctl was detected and fixed"), CALLFROM, 1, 1); return; } else send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_RLNKINTEGINFO, 5, LEN_AND_LIT("a loop to the middle of linkctl was detected and fixed"), CALLFROM, 1, 1); /* If complete_loop is FALSE we need to complete the counting of relinkrec->numvers */ for( ; (rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T != shm_index_off_loop_found; relinkrec->numvers++) { rtnobj = rtnobj_retpos(linkctl, shm_index_off_loop_found); shm_index_off_loop_found = rtnobj->next_rtnobj_shm_offset; } return; } } send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_RLNKINTEGINFO, 5, LEN_AND_LIT("relinkrec->numvers verified"), CALLFROM, 1, 1); return; } /* Remove a specific element "rtnobj" from wherever it is in the doubly-linked relative-queue starting from "que_base". * Note "que_base" is a pointer to relinkctl shared memory whereas "rtnobj" is a pointer to rtnobj shared memory. * Hence the need for NULL_RTNOBJ_SM_OFF_T to indicate a link from rtnobj shm to relinkctl shm. * "shm_base" is pointer to the start of rtnobj shared memory. Used to calculate relative offsets. */ void remq_rtnobj_specific(que_ent_ptr_t que_base, sm_uc_ptr_t shm_base, rtnobj_hdr_t *rtnobj) { que_ent_ptr_t elem, new_head, rtnque; assert(NULL_RTNOBJ_SM_OFF_T != que_base->fl); rtnque = (que_ent_ptr_t)((sm_uc_ptr_t)rtnobj + OFFSETOF(rtnobj_hdr_t, userStorage)); RTNOBJ_DBG(fprintf(stderr, "remqh_rtnobj_specific : que_base = 0x%llx : shm_base = 0x%llx : elem = 0x%llx\n", \ (UINTPTR_T)que_base, (UINTPTR_T)shm_base, (UINTPTR_T)rtnque);) # ifdef DEBUG /* Verify that "rtnobj" is actually in the queue first */ elem = (que_ent_ptr_t)(shm_base + que_base->fl); assert(NULL_RTNOBJ_SM_OFF_T == elem->bl); while (elem != rtnque) { assert(NULL_RTNOBJ_SM_OFF_T != elem->fl); elem = (que_ent_ptr_t)((sm_uc_ptr_t)elem + elem->fl); } # endif if (NULL_RTNOBJ_SM_OFF_T == rtnque->bl) { /* rtnobj is at head of queue */ if (NULL_RTNOBJ_SM_OFF_T == rtnque->fl) { /* rtnobj is only element in queue */ que_base->fl = NULL_RTNOBJ_SM_OFF_T; que_base->bl = NULL_RTNOBJ_SM_OFF_T; } else { elem = (que_ent_ptr_t)((uchar_ptr_t)rtnque + rtnque->fl); assert(0 == (sm_off_t)elem % 8); elem->bl = NULL_RTNOBJ_SM_OFF_T; assert(0 == que_base->fl % 8); assert(0 == rtnque->fl % 8); que_base->fl += rtnque->fl; } } else if (NULL_RTNOBJ_SM_OFF_T == rtnque->fl) { /* rtnobj is at tail of queue with at least one more element in queue */ elem = (que_ent_ptr_t)((uchar_ptr_t)rtnque + rtnque->bl); elem->fl = NULL_RTNOBJ_SM_OFF_T; assert(0 == que_base->bl % 8); assert(0 == rtnque->bl % 8); que_base->bl += rtnque->bl; } else { /* rtnobj is in middle of queue with at least one element in queue on either side */ elem = (que_ent_ptr_t)((uchar_ptr_t)rtnque + rtnque->bl); assert(0 == (sm_off_t)elem % 8); assert(0 == elem->fl % 8); assert(0 == rtnque->fl % 8); elem->fl += rtnque->fl; assert(0 == (sm_off_t)rtnque % 8); assert(0 == rtnque->fl % 8); elem = (que_ent_ptr_t)((uchar_ptr_t)rtnque + rtnque->fl); assert(0 == elem->bl % 8); assert(0 == rtnque->bl % 8); elem->bl += rtnque->bl; } } /* Function to allocate "objSize" bytes of space, for the object file that "fd" points to and has a hash value "objhash", * in rtnobj shared memory for the relinkctl file/shared-memory derived from "zhist". If such an object already exists, return * a pointer to that location in rtnobj shared memory after incrementing reference-counts (to indicate how many processes * have linked in to this shared object). In this fast-path case, we hold a lock only on the relink record corresponding * to this object's routine-name. In the other case (where space has to be allocated), we also hold a lock on the entire * relinkctl shared memory (and effectively all rtnobj shared memory segments). */ sm_uc_ptr_t rtnobj_shm_malloc(zro_hist *zhist, int fd, off_t objSize, gtm_uint64_t objhash) { boolean_t has_relinkctl_lock, has_rtnobj_lock; boolean_t initialized, return_NULL, need_shmctl; char errstr[256]; gtm_uint64_t src_cksum_8byte; int min_index, max_index, min_free_index, max_free_index, shm_index; int objIndex, tSize, sizeIndex, shmSizeIndex, shmid, actualSize; int codelen, loopcnt, save_errno; open_relinkctl_sgm *linkctl; que_ent_ptr_t freeList; relinkrec_t *relinkrec; relinkshm_hdr_t *shm_hdr; sm_uc_ptr_t shm_base; rtnobj_hdr_t *rtnobj, *prev_rtnobj, *rtnobj2; rtnobj_sm_off_t shm_index_off; rtnobjshm_hdr_t *rtnobj_shm_hdr; size_t shm_size; off_t rtnobjSize; sm_off_t shm_off; sm_uc_ptr_t objBuff, codeadr; rhdtyp tmprhd, *rhdr; gtm_uint64_t elemSize; zro_validation_entry *zhent; struct stat dir_stat_buf; int stat_res; int user_id; int group_id; int perm; int curvers, maxvers; struct shmid_ds shmstat; struct perm_diag_data pdd; # ifdef DEBUG boolean_t rtnobj_integ_done = FALSE; rtnobj_sm_off_t shm_index_off_mid; int dbg_shm_cnt; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # endif zhent = zhist->end - 1; relinkrec = zhent->relinkrec; linkctl = zhent->relinkctl_bkptr; /* Assert certain design assumptions */ assert((MAX_RTNOBJ_SHM_INDEX + RTNOBJ_SHMID_INDEX_MAXBITS) <= (8 * SIZEOF(rtnobj_sm_off_t))); assert(((size_t)1 << RTNOBJ_SHMID_INDEX_MAXBITS) >= MAX_RTNOBJ_SHM_INDEX); shm_hdr = GET_RELINK_SHM_HDR(linkctl); loopcnt = 0; has_relinkctl_lock = FALSE; assert(!linkctl->locked); assert(!linkctl->hdr->file_deleted); if (!grab_latch(&relinkrec->rtnobj_latch, RLNKREC_LATCH_TIMEOUT_SEC, NOT_APPLICABLE, NULL)) { assert(FALSE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_RLNKRECLATCH, 3, relinkrec->rtnname_fixed.c, RTS_ERROR_MSTR(&linkctl->zro_entry_name)); } do { if (CDB_STAGNATE <= loopcnt++) { /* We have tried THRICE to search without a lock but found something inconsistent (due to concurrent * changes to shared memory structures). Get a lock on the entire relinkctl file before searching * for one last time. */ assert(!has_relinkctl_lock); if (!grab_latch(&shm_hdr->relinkctl_latch, RLNKSHM_LATCH_TIMEOUT_SEC, NOT_APPLICABLE, NULL)) { assert(FALSE); rel_latch(&relinkrec->rtnobj_latch); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_RLNKSHMLATCH, 2, RTS_ERROR_MSTR(&linkctl->zro_entry_name)); } has_relinkctl_lock = TRUE; } /* Check if we are in sync with the rtnobj shared memory segments corresponding to this relinkctl shared memory. * If not attach to those rtnobj shmids first. */ SYNC_RTNOBJ_SHMID_CYCLE_IF_NEEDED(linkctl, min_index, max_index, shm_hdr, has_relinkctl_lock, relinkrec); /* Now that we have attached to the necessary routine buffer shmids, search within shared memory for the * routine buffer whose matches ours. */ assert(MAXUINT4 >= objSize); shm_index_off = relinkrec->rtnobj_shm_offset; prev_rtnobj = NULL; maxvers = relinkrec->numvers; curvers = 0; while ((rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T != shm_index_off) { shm_index = RTNOBJ_GET_SHM_INDEX(shm_index_off); shm_off = RTNOBJ_GET_SHM_OFFSET(shm_index_off); assert(0 == (shm_off % 8)); /* Since we hold the rtnobj_latch we should see valid values of shm_index/shm_off */ assert(min_index <= shm_index); assert(max_index > shm_index); assert(INVALID_SHMID != linkctl->rtnobj_shmid[shm_index]); assert(shm_off < ((off_t)1 << (MIN_RTNOBJ_SHM_INDEX + shm_index))); rtnobj = (rtnobj_hdr_t *)(linkctl->rtnobj_shm_base[shm_index] + shm_off); assert((rtnobj->objhash != objhash) || (rtnobj->objLen == objSize)); if ((rtnobj->objLen == objSize) && (rtnobj->objhash == objhash)) break; prev_rtnobj = rtnobj; shm_index_off = rtnobj->next_rtnobj_shm_offset; # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_RTNOBJ_INTEG) && !rtnobj_integ_done) { for (dbg_shm_cnt = 0; (rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T != shm_index_off; dbg_shm_cnt++) { if (dbg_shm_cnt == maxvers/2) shm_index_off_mid = shm_index_off; rtnobj = rtnobj_retpos(linkctl, shm_index_off); shm_index_off = rtnobj->next_rtnobj_shm_offset; } if (MAXVERS_TEST < maxvers) // non-head node as the loop point when there is more than 2 elements rtnobj->next_rtnobj_shm_offset = shm_index_off_mid; else rtnobj->next_rtnobj_shm_offset = relinkrec->rtnobj_shm_offset; maxvers = -1; rtnobj_integ_done = TRUE; } # endif if (maxvers < curvers++) /* Avoid infinite loops (just in case) */ { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_RLNKRECNFL, 3, linkctl->relinkctl_path, RTS_ERROR_MSTR(&linkctl->zro_entry_name)); rtnobj_integ(relinkrec, linkctl); assertpro(maxvers != relinkrec->numvers); /* Intinialize all values and iterate the fixed list from the start */ maxvers = relinkrec->numvers; curvers = 0; shm_index_off = relinkrec->rtnobj_shm_offset; } } if ((rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T != shm_index_off) { /* If found return */ assert(rtnobj->initialized); /* If gtm_autorelink_keeprtn is FALSE (i.e. we maintain reference counts even on process exit), * we do not expect refcnt to go to high values as it is the # of processes concurrently running * and actively using this buffer and that cannot be close to 2**31 which is the max value for refcnt. */ assert(TREF(gtm_autorelink_keeprtn) || (REFCNT_INACCURATE != rtnobj->refcnt)); if (REFCNT_INACCURATE != rtnobj->refcnt) rtnobj->refcnt++; /* increment refcnt while holding the rtnobj_lock */ if (has_relinkctl_lock) rel_latch(&shm_hdr->relinkctl_latch); RTNOBJ_DBG(fprintf(stderr, "rtnobj_shm_malloc : rtnname = %s : objhash = 0x%llx : " \ "linkctl = 0x%llx : Found : refcnt = %d : elem = 0x%llx\n", \ relinkrec->rtnname_fixed.c, (UINTPTR_T)objhash, \ (UINTPTR_T)linkctl, rtnobj->refcnt, \ (UINTPTR_T)&rtnobj->userStorage.userStart);) zhent->cycle = relinkrec->cycle; /* Update private cycle to be in sync with shared copy */ rel_latch(&relinkrec->rtnobj_latch); objBuff = (sm_uc_ptr_t)&rtnobj->userStorage.userStart; return objBuff; /* object code starts after rtnobj_hdr_t */ } assert(MAXUINT4 >= (objSize + OFFSETOF(rtnobj_hdr_t, userStorage))); assert(4 <= SIZEOF(tSize)); /* above assert guarantees 4-bytes is enough for storage */ tSize = objSize + OFFSETOF(rtnobj_hdr_t, userStorage); sizeIndex = ceil_log2_32bit(tSize); assert(MAX_RTNOBJ_SIZE_BITS >= sizeIndex); sizeIndex = MAX(sizeIndex, MIN_RTNOBJ_SIZE_BITS); /* Round up to minimum supported obj file size */ /* Find the smallest shm index that can possibly contain space for sizeIndex */ shm_index = (MIN_RTNOBJ_SHM_INDEX > sizeIndex) ? 0 : sizeIndex - MIN_RTNOBJ_SHM_INDEX; sizeIndex -= MIN_RTNOBJ_SIZE_BITS; shm_index = MAX(shm_index, shm_hdr->min_shm_index); /* If not found, allocate space in shared memory for one and return. Now that we are going to * play with the buddy list structures in shared memory, get an overarching lock on all rtnobj shmids * i.e. a lock on the entire relinkctl file */ if (!has_relinkctl_lock) { if (!grab_latch(&shm_hdr->relinkctl_latch, RLNKSHM_LATCH_TIMEOUT_SEC, NOT_APPLICABLE, NULL)) { assert(FALSE); rel_latch(&relinkrec->rtnobj_latch); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_RLNKSHMLATCH, 2, RTS_ERROR_MSTR(&linkctl->zro_entry_name)); } has_relinkctl_lock = TRUE; } /* Sync up with rtnobj shmids one final time if needed now that we have the relinkctl file lock */ SYNC_RTNOBJ_SHMID_CYCLE_IF_NEEDED(linkctl, min_index, max_index, shm_hdr, has_relinkctl_lock, relinkrec); /* Find a shared memory buffer to hold the routine object */ rtnobj = NULL; for ( ; shm_index < max_index; shm_index++) { rtnobj_shm_hdr = &shm_hdr->rtnobj_shmhdr[shm_index]; if (INVALID_SHMID == rtnobj_shm_hdr->rtnobj_shmid) continue; min_free_index = rtnobj_shm_hdr->rtnobj_min_free_index; max_free_index = rtnobj_shm_hdr->rtnobj_max_free_index; shm_base = linkctl->rtnobj_shm_base[shm_index]; objIndex = MAX(sizeIndex, min_free_index); freeList = &rtnobj_shm_hdr->freeList[objIndex]; for ( ; objIndex < max_free_index; objIndex++, freeList++) { assert(objIndex < NUM_RTNOBJ_SIZE_BITS); if (NULL_RTNOBJ_SM_OFF_T == freeList->fl) { assert(NULL_RTNOBJ_SM_OFF_T == freeList->bl); continue; } rtnobj = remqh_rtnobj(freeList, shm_base); if (NULL_RTNOBJ_SM_OFF_T == freeList->fl) { assert(NULL_RTNOBJ_SM_OFF_T == freeList->bl); /* This was the only element in the freeList. * Set min_free_index/max_free_index to impossible values * if the new minimums/maximums need to be recomputed. */ if (objIndex == min_free_index) min_free_index = NUM_RTNOBJ_SIZE_BITS; if (max_free_index == (objIndex + 1)) max_free_index = 0; } else if (min_free_index > sizeIndex) { assert(NUM_RTNOBJ_SIZE_BITS > min_free_index); min_free_index = NUM_RTNOBJ_SIZE_BITS; } break; } if (NULL != rtnobj) break; } if (NULL == rtnobj) { /* Need to allocate rtnobj buffer in a new shared memory segment */ assert(shm_index >= max_index); shm_size = ((size_t)1 << (shm_index + MIN_RTNOBJ_SHM_INDEX)); ADJUST_SHM_SIZE_FOR_HUGEPAGES(shm_size, shm_size); /* If minimum huge page size is much higher than requested shm size, adjust "shm_index" * accordingly so we use the allocated (bigger-than-requested) shm completely. */ shmSizeIndex = ceil_log2_64bit(shm_size); assert(((size_t)1 << shmSizeIndex) == shm_size); assert(shmSizeIndex >= (shm_index + MIN_RTNOBJ_SHM_INDEX)); shm_index = shmSizeIndex - MIN_RTNOBJ_SHM_INDEX; shmid = gtm_shmget(IPC_PRIVATE, shm_size, RWDALL | IPC_CREAT, TRUE); if (-1 == shmid) { save_errno = errno; rel_latch(&shm_hdr->relinkctl_latch); rel_latch(&relinkrec->rtnobj_latch); SNPRINTF(errstr, SIZEOF(errstr), "rtnobj shmget() failed for shmsize=0x%llx", shm_size); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno); } STAT_FILE(linkctl->zro_entry_name.addr, &dir_stat_buf, stat_res); if (-1 == stat_res) { save_errno = errno; rel_latch(&shm_hdr->relinkctl_latch); rel_latch(&relinkrec->rtnobj_latch); shm_rmid(shmid); /* if error removing shmid we created, just move on */ SNPRINTF(errstr, SIZEOF(errstr), "rtnobj stat() of file %s failed", linkctl->zro_entry_name.addr); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno); } if (!gtm_permissions(&dir_stat_buf, &user_id, &group_id, &perm, PERM_IPC|PERM_EXEC, &pdd)) { rel_latch(&shm_hdr->relinkctl_latch); rel_latch(&relinkrec->rtnobj_latch); shm_rmid(shmid); /* if error removing shmid we created, just move on */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(10 + PERMGENDIAG_ARG_COUNT) ERR_RELINKCTLERR, 2, RTS_ERROR_MSTR(&linkctl->zro_entry_name), ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("rtnobj"), RTS_ERROR_MSTR(&linkctl->zro_entry_name), PERMGENDIAG_ARGS(pdd)); } if (-1 == shmctl(shmid, IPC_STAT, &shmstat)) { save_errno = errno; rel_latch(&shm_hdr->relinkctl_latch); rel_latch(&relinkrec->rtnobj_latch); shm_rmid(shmid); /* if error removing shmid we created, just move on */ SNPRINTF(errstr, SIZEOF(errstr), "rtnobj shmctl(IPC_STAT) failed for shmid=%d shmsize=0x%llx", shmid, shm_size); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno); } /* change uid, group-id and permissions if needed */ need_shmctl = FALSE; if ((INVALID_UID != user_id) && (user_id != shmstat.shm_perm.uid)) { shmstat.shm_perm.uid = user_id; need_shmctl = TRUE; } if ((INVALID_GID != group_id) && (group_id != shmstat.shm_perm.gid)) { shmstat.shm_perm.gid = group_id; need_shmctl = TRUE; } if (shmstat.shm_perm.mode != perm) { shmstat.shm_perm.mode = perm; need_shmctl = TRUE; } if (need_shmctl && (-1 == shmctl(shmid, IPC_SET, &shmstat))) { save_errno = errno; rel_latch(&shm_hdr->relinkctl_latch); rel_latch(&relinkrec->rtnobj_latch); shm_rmid(shmid); /* if error removing shmid we created, just move on */ SNPRINTF(errstr, SIZEOF(errstr), "rtnobj shmctl(IPC_SET) failed for shmid=%d shmsize=0x%llx", shmid, shm_size); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno); } shm_base = (sm_uc_ptr_t)do_shmat_exec_perm(shmid, shm_size, &save_errno); if (-1 == (sm_long_t)shm_base) { save_errno = errno; rel_latch(&shm_hdr->relinkctl_latch); rel_latch(&relinkrec->rtnobj_latch); shm_rmid(shmid); /* if error removing shmid we created, just move on */ SNPRINTF(errstr, SIZEOF(errstr), "rtnobj shmat() failed for shmid=%d shmsize=0x%llx", shmid, shm_size); ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno); } assert(shm_index < NUM_RTNOBJ_SHM_INDEX); rtnobj_shm_hdr = &shm_hdr->rtnobj_shmhdr[shm_index]; objIndex = (shm_index + MIN_RTNOBJ_SHM_INDEX - MIN_RTNOBJ_SIZE_BITS); assert(objIndex < NUM_RTNOBJ_SIZE_BITS); freeList = &rtnobj_shm_hdr->freeList[objIndex]; rtnobj = (rtnobj_hdr_t *)shm_base; assert(NULL_RTNOBJ_SM_OFF_T == freeList->fl); assert(NULL_RTNOBJ_SM_OFF_T == freeList->bl); assert(sizeIndex <= objIndex); if (sizeIndex < objIndex) { /* rtnobj_shm_hdr->rtnobj_min_free_index and ->rtnobj_min_free_index will be set a little later */ min_free_index = NUM_RTNOBJ_SIZE_BITS; max_free_index = 0; } else { /* rtnobj_shm_hdr->rtnobj_min_free_index and ->rtnobj_min_free_index are already set correctly. * set min_free_index/max_free_index such that the above two dont get set again (a little later). */ assert(NUM_RTNOBJ_SIZE_BITS == rtnobj_shm_hdr->rtnobj_min_free_index); assert(0 == rtnobj_shm_hdr->rtnobj_max_free_index); assert(NUM_RTNOBJ_SIZE_BITS != objIndex); min_free_index = objIndex; assert(0 != NUM_RTNOBJ_SIZE_BITS); max_free_index = NUM_RTNOBJ_SIZE_BITS; } rtnobj_shm_hdr->rtnobj_shmid = shmid; assert(0 == rtnobj_shm_hdr->real_len); rtnobj_shm_hdr->shm_len = shm_size; if (shm_hdr->rtnobj_min_shm_index > shm_index) { assert(NUM_RTNOBJ_SHM_INDEX == shm_hdr->rtnobj_min_shm_index); shm_hdr->rtnobj_min_shm_index = shm_index; } assert(linkctl->rtnobj_max_shm_index == shm_hdr->rtnobj_max_shm_index); assert(shm_hdr->rtnobj_max_shm_index < (shm_index + 1)); SHM_WRITE_MEMORY_BARRIER; /* Update everything except "rtnobj_max_shm_index" BEFORE write memory barrier */ shm_hdr->rtnobj_max_shm_index = (shm_index + 1); /* Sync shared memory with private contents for this process to avoid duplicate shmget/shmat */ linkctl->rtnobj_min_shm_index = shm_hdr->rtnobj_min_shm_index; linkctl->rtnobj_max_shm_index = shm_hdr->rtnobj_max_shm_index; linkctl->rtnobj_shm_base[shm_index] = shm_base; linkctl->rtnobj_shmid[shm_index] = shmid; } /* At this point, we have a non-null rtnobj but it could be bigger than we want. * Split it into half until we reach the desired size. Insert into free queues along the way. */ assert(objIndex >= sizeIndex); if (objIndex > sizeIndex) { if (0 == max_free_index) { rtnobj_shm_hdr->rtnobj_max_free_index = objIndex; max_free_index = objIndex; } rtnobjSize = ((off_t)1 << (objIndex + MIN_RTNOBJ_SIZE_BITS)); freeList = &rtnobj_shm_hdr->freeList[objIndex]; do { rtnobjSize /= 2; objIndex--; freeList--; rtnobj2 = (rtnobj_hdr_t *)((sm_uc_ptr_t)rtnobj + rtnobjSize); rtnobj2->state = STATE_FREE; rtnobj2->queueIndex = objIndex; DEBUG_ONLY(shm_off = (sm_uc_ptr_t)rtnobj2 - shm_base); assert(0 == (shm_off % ((sm_off_t)1 << (objIndex + MIN_RTNOBJ_SIZE_BITS)))); /* The following fields need initialization only when state becomes STATE_ALLOCATED. * rtnobj2->initialized * rtnobj2->refcnt * rtnobj2->src_cksum_8byte * rtnobj2->next_rtnobj_shm_offset * rtnobj2->relinkctl_index * rtnobj2->objLen */ insqt_rtnobj(&rtnobj2->userStorage.freePtr, freeList, shm_base); } while (objIndex > sizeIndex); if (NUM_RTNOBJ_SIZE_BITS == min_free_index) rtnobj_shm_hdr->rtnobj_min_free_index = sizeIndex; } else { if (NUM_RTNOBJ_SIZE_BITS == min_free_index) { assert(objIndex == rtnobj_shm_hdr->rtnobj_min_free_index); if (0 == max_free_index) { /* Need to recompute BOTH rtnobj_shm_hdr->rtnobj_min_free_index * AND rtnobj_shm_hdr->rtnobj_max_free_index. But the fact that * min and max are the same implies there are NO more free buffers * in this entire shared memory so the recomputation is easy. */ assert((objIndex + 1) == rtnobj_shm_hdr->rtnobj_max_free_index); rtnobj_shm_hdr->rtnobj_max_free_index = 0; rtnobj_shm_hdr->rtnobj_min_free_index = NUM_RTNOBJ_SIZE_BITS; } else { /* Need to ONLY recompute rtnobj_shm_hdr->rtnobj_min_free_index */ max_free_index = rtnobj_shm_hdr->rtnobj_max_free_index; freeList = &rtnobj_shm_hdr->freeList[objIndex]; for ( ; objIndex < max_free_index; objIndex++, freeList++) { assert(objIndex < NUM_RTNOBJ_SIZE_BITS); if (NULL_RTNOBJ_SM_OFF_T != freeList->fl) { rtnobj_shm_hdr->rtnobj_min_free_index = objIndex; break; } } assert(objIndex < max_free_index); } } else if (0 == max_free_index) { /* Need to ONLY recompute rtnobj_shm_hdr->rtnobj_min_free_index */ assert((objIndex + 1) == rtnobj_shm_hdr->rtnobj_max_free_index); min_free_index = rtnobj_shm_hdr->rtnobj_min_free_index; freeList = &rtnobj_shm_hdr->freeList[objIndex]; for ( ; objIndex >= min_free_index; objIndex--, freeList--) { assert(objIndex < NUM_RTNOBJ_SIZE_BITS); if (NULL_RTNOBJ_SM_OFF_T != freeList->fl) { rtnobj_shm_hdr->rtnobj_max_free_index = objIndex + 1; break; } } assert(objIndex >= min_free_index); } } rtnobj->state = STATE_ALLOCATED; rtnobj->queueIndex = sizeIndex; rtnobj_shm_hdr->real_len += objSize; elemSize = ((gtm_uint64_t)1 << (sizeIndex + MIN_RTNOBJ_SIZE_BITS)); rtnobj_shm_hdr->used_len += elemSize; DEBUG_ONLY(rtnobj_verify_min_max_free_index(rtnobj_shm_hdr)); DEBUG_ONLY(rtnobj_verify_freelist_fl_bl(rtnobj_shm_hdr, shm_base);) /* Now that we have gotten a "rtnobj" pointer, we can release the relinkctl latch on shared memory. * We still need to hold on to the lower-granular relinkrec->rtnobj_latch to read the object file * into shared memory. */ rel_latch(&shm_hdr->relinkctl_latch); assert(((off_t)1 << (sizeIndex + MIN_RTNOBJ_SIZE_BITS)) >= (objSize + OFFSETOF(rtnobj_hdr_t, userStorage))); assert(((off_t)1 << (sizeIndex + MIN_RTNOBJ_SIZE_BITS - 1)) < (objSize + OFFSETOF(rtnobj_hdr_t, userStorage))); shm_off = (sm_uc_ptr_t)rtnobj - shm_base; assert(0 == (shm_off % ((sm_off_t)1 << (sizeIndex + MIN_RTNOBJ_SIZE_BITS)))); rtnobj->initialized = TRUE; rtnobj->refcnt = 1; assert(0 <= shm_off); assert(shm_off < rtnobj_shm_hdr->shm_len); shm_index_off = RTNOBJ_SET_SHM_INDEX_OFF(shm_index, shm_off); if (NULL != prev_rtnobj) prev_rtnobj->next_rtnobj_shm_offset = shm_index_off; else relinkrec->rtnobj_shm_offset = shm_index_off; rtnobj->next_rtnobj_shm_offset = (rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T; rtnobj->relinkctl_index = relinkrec - linkctl->rec_base; rtnobj->objLen = objSize; rtnobj->objhash = objhash; relinkrec->objLen += objSize; relinkrec->usedLen += elemSize; relinkrec->numvers++; /* Fill the shared memory buffer with the object code */ objBuff = (sm_uc_ptr_t)&rtnobj->userStorage.userStart; if (-1 == lseek(fd, NATIVE_HDR_LEN, SEEK_SET)) return_NULL = TRUE; else { DOREADRL(fd, objBuff, objSize, actualSize); return_NULL = (actualSize != objSize); } if (return_NULL) { /* Free allocated object in shared memory using rtnobj_shm_free. Set up a dummy rtnhdr * with just the necessary fields initialized since rtnobj_shm_free requires it. */ rhdr = &tmprhd; rhdr->shared_ptext_adr = objBuff + SIZEOF(rhdtyp); rhdr->zhist = zhist; DEBUG_ONLY(rhdr->shared_object = TRUE); /* for an assert inside rtnobj_shm_free */ DEBUG_ONLY(rhdr->relinkctl_bkptr = linkctl); /* for an assert inside rtnobj_shm_free */ /* We already hold the relinkrec->rtnobj_latch. Free up the shared object before releasing this * lock to avoid other processes from seeing the allocated shared object and assuming it is * properly initialized when actually it is not. rtnobj_shm_free obtains the relinkrec->rtnobj_latch * which would fail an assert since we already hold that latch so add a dbg-flag to avoid an assert. */ rtnobj_shm_free(rhdr, LATCH_GRABBED_TRUE); /* Note: this will release relinkrec->rtnobj_latch for us */ return NULL; /* caller will issue error */ } rhdr = (rhdtyp *)objBuff; codelen = rhdr->ptext_end_adr - rhdr->ptext_adr; /* Length of object code */ assert(0 < codelen); codeadr = objBuff + (UINTPTR_T)rhdr->ptext_adr; /* Pointer to object code */ cacheflush(codeadr, codelen, BCACHE); /* Cacheflush executable part from instruction cache */ RTNOBJ_DBG(fprintf(stderr, "rtnobj_shm_malloc : rtnname = %s : objhash = 0x%llx : " \ "linkctl = 0x%llx : Inserted : refcnt = %d : elem = 0x%llx : numvers = %d\n", \ relinkrec->rtnname_fixed.c, (UINTPTR_T)objhash, \ (UINTPTR_T)linkctl, rtnobj->refcnt, \ (UINTPTR_T)objBuff, relinkrec->numvers);) relinkrec->objhash = objhash; ++relinkrec->cycle; /* cycle bump is because of the implicit ZRUPDATE that linking a routine does */ if (0 == relinkrec->cycle) relinkrec->cycle = 1; /* cycle value of 0 has special meaning, so bump to 1 in case we roll over */ zhent->cycle = relinkrec->cycle; /* Update private cycle to be in sync with shared copy */ rel_latch(&relinkrec->rtnobj_latch); return objBuff; } while (TRUE); } /* Function to free the object file in rtnobj shared memory whose routine header is "rhead". If this object file is used * by more than one process (i.e. reference count is > 1), then decrement the reference count and return. This fast path * only holds lock on the relink record corresponding to this object's routine-name. If, however, the reference count is 1, * space for this object file will be freed up and that requires holding a lock on the entire relinkctl shared memory (and * effectively all rtnobj shared memory segments). */ void rtnobj_shm_free(rhdtyp *rhead, boolean_t latch_grabbed) { relinkrec_t *relinkrec; open_relinkctl_sgm *linkctl; relinkshm_hdr_t *shm_hdr; int maxObjIndex, sizeIndex; int min_index, max_index, min_free_index, max_free_index, shm_index; gtm_uint64_t elemSize, origElemSize, objLen; rtnobj_hdr_t *rtnobj, *prev_rtnobj, *rtnobj2; rtnobj_sm_off_t shm_index_off; rtnobjshm_hdr_t *rtnobj_shm_hdr; sm_uc_ptr_t shm_base; sm_off_t shm_off, rtnobj_off; que_ent_ptr_t freeList; zro_validation_entry *zhent; sm_uc_ptr_t objBuff; int curvers, maxvers; DEBUG_ONLY(size_t shm_size;) DEBUG_ONLY(int dbg_shm_index;) # ifdef DEBUG DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # endif /* Note that if breakpoints are in effect, rhead->shared_ptext_adr will not be equal to rhead->ptext_adr. * This is because we would have taken a private copy of the code (for breakpoints) into ptext_adr and kept * shared_ptext_adr untouched. In that case, it is still possible this function is called as part of process * exit (when breakpoints on a shared routine object are still active). We will still need to free-up/decrement-refcnt * the shared copy (shared_ptext_adr). So look at shared_ptext_adr below instead of ptext_adr; */ assert(rhead->shared_object); assert(NULL != rhead->shared_ptext_adr); if (NULL == rhead->shared_ptext_adr) return; /* in pro, be safe */ assert(process_exiting || (rhead->shared_ptext_adr == rhead->ptext_adr)); objBuff = rhead->shared_ptext_adr - SIZEOF(rhdtyp); rtnobj = (rtnobj_hdr_t *)(objBuff - OFFSETOF(rtnobj_hdr_t, userStorage)); assert(STATE_ALLOCATED == rtnobj->state); assert(rtnobj->initialized); assert(0 < rtnobj->refcnt); assert(NULL != rhead->zhist); zhent = rhead->zhist->end - 1; relinkrec = zhent->relinkrec; linkctl = zhent->relinkctl_bkptr; assert(rhead->relinkctl_bkptr == linkctl); shm_hdr = GET_RELINK_SHM_HDR(linkctl); assert(!linkctl->locked); assert(!linkctl->hdr->file_deleted); sizeIndex = rtnobj->queueIndex; # ifdef DEBUG /* Find shm_index corresponding to this routine buffer to cross check later */ min_index = linkctl->rtnobj_min_shm_index; max_index = linkctl->rtnobj_max_shm_index; for (dbg_shm_index = min_index; dbg_shm_index < max_index; dbg_shm_index++) { rtnobj_shm_hdr = &shm_hdr->rtnobj_shmhdr[dbg_shm_index]; if (INVALID_SHMID == rtnobj_shm_hdr->rtnobj_shmid) continue; shm_base = linkctl->rtnobj_shm_base[dbg_shm_index]; assert(NULL != shm_base); shm_size = ((size_t)1 << (dbg_shm_index + MIN_RTNOBJ_SHM_INDEX)); if (((gtm_uint64_t)shm_base <= (gtm_uint64_t)rtnobj) && (((gtm_uint64_t)shm_base + shm_size) > (gtm_uint64_t)rtnobj)) break; /* shared memory corresponding to rtnobj is found */ } assert(dbg_shm_index < max_index); # endif if (!latch_grabbed && !grab_latch(&relinkrec->rtnobj_latch, RLNKREC_LATCH_TIMEOUT_SEC, NOT_APPLICABLE, NULL)) { assert(FALSE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_RLNKRECLATCH, 3, relinkrec->rtnname_fixed.c, RTS_ERROR_MSTR(&linkctl->zro_entry_name)); } assert(0 < rtnobj->refcnt); assert(TREF(gtm_autorelink_keeprtn) || (REFCNT_INACCURATE != rtnobj->refcnt)); if (REFCNT_INACCURATE != rtnobj->refcnt) rtnobj->refcnt--; /* decrement refcnt while holding the rtnobj_lock */ if (rtnobj->refcnt) { /* The loaded object cannot be freed until refcnt becomes 0. But caller's job done. Return. */ RTNOBJ_DBG(fprintf(stderr, \ "rtnobj_shm_free : rtnname = %s : objhash = 0x%llx : Decremented : refcnt = %d : elem = 0x%llx\n", \ relinkrec->rtnname_fixed.c, (UINTPTR_T)rtnobj->objhash, rtnobj->refcnt, \ (UINTPTR_T)objBuff);) rel_latch(&relinkrec->rtnobj_latch); return; } assert(sizeIndex == rtnobj->queueIndex); /* since this pid has not freed it yet, queueIndex must not change concurrently */ /* Note: Although we hold the rtnobj_latch, it is possible that new rtnobj_shmids get created concurrently * and an addition happens to the rtnobj->next_rtnobj_shm_offset linked list pointing to the new shmids. * But since additions to the linked list happen only at the end, we are guaranteed that the element of * interest (rtnobj which we want to free and have already located in rtnobj_shm_malloc) would be found * without reaching the tail portion of that linked list (which would require attaching to the new shmid). * So no need to invoke the SYNC_RTNOBJ_SHMID_CYCLE_IF_NEEDED macro like we needed to in "rtnobj_shm_malloc". */ shm_index_off = relinkrec->rtnobj_shm_offset; prev_rtnobj = NULL; maxvers = relinkrec->numvers; for (curvers = 0; (rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T != shm_index_off; curvers++) { assertpro(maxvers >= curvers); /* assertpro to avoid infinite loops in PRO (just in case) */ shm_index = RTNOBJ_GET_SHM_INDEX(shm_index_off); shm_off = RTNOBJ_GET_SHM_OFFSET(shm_index_off); assert(0 == (shm_off % 8)); /* Since we hold the rtnobj_latch we should see valid values of shm_index/shm_off */ assert(min_index <= shm_index); assert(max_index > shm_index); assert(INVALID_SHMID != linkctl->rtnobj_shmid[shm_index]); assert(shm_off < ((off_t)1 << (MIN_RTNOBJ_SHM_INDEX + shm_index))); rtnobj2 = (rtnobj_hdr_t *)(linkctl->rtnobj_shm_base[shm_index] + shm_off); if (rtnobj2 == rtnobj) break; prev_rtnobj = rtnobj2; shm_index_off = rtnobj2->next_rtnobj_shm_offset; } assertpro((rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T != shm_index_off); shm_index_off = rtnobj->next_rtnobj_shm_offset; assert(dbg_shm_index == shm_index); rtnobj_shm_hdr = &shm_hdr->rtnobj_shmhdr[shm_index]; shm_base = linkctl->rtnobj_shm_base[shm_index]; maxObjIndex = (shm_index + MIN_RTNOBJ_SHM_INDEX - MIN_RTNOBJ_SIZE_BITS); assert(sizeIndex <= maxObjIndex); elemSize = origElemSize = ((gtm_uint64_t)1 << (sizeIndex + MIN_RTNOBJ_SIZE_BITS)); /* Now that we need to unload the object from shared memory, get a shared memory lock */ if (!grab_latch(&shm_hdr->relinkctl_latch, RLNKSHM_LATCH_TIMEOUT_SEC, NOT_APPLICABLE, NULL)) { assert(FALSE); rel_latch(&relinkrec->rtnobj_latch); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_RLNKSHMLATCH, 2, RTS_ERROR_MSTR(&linkctl->zro_entry_name)); } assert(rtnobj->objLen <= rtnobj_shm_hdr->real_len); assert(elemSize <= rtnobj_shm_hdr->used_len); objLen = rtnobj->objLen; rtnobj_shm_hdr->real_len -= objLen; rtnobj_shm_hdr->used_len -= elemSize; /* Check if buddy of "rtnobj" is free and if so free the two together. Merge the two into a bigger chunk * and redo the free-buddy-check/merge as much as possible. */ min_free_index = rtnobj_shm_hdr->rtnobj_min_free_index; if (sizeIndex < min_free_index) { min_free_index = sizeIndex; rtnobj_shm_hdr->rtnobj_min_free_index = sizeIndex; /* we are about to do a insqt_rtnobj at freeList[sizeIndex] */ } freeList = &rtnobj_shm_hdr->freeList[sizeIndex]; rtnobj_off = (sm_uc_ptr_t)rtnobj - shm_base; while (sizeIndex < maxObjIndex) { rtnobj2 = (rtnobj_hdr_t *)(shm_base + (rtnobj_off ^ elemSize)); /* address of buddy */ assert(rtnobj2->queueIndex <= sizeIndex); assert((STATE_ALLOCATED == rtnobj2->state) || (STATE_FREE == rtnobj2->state)); if (STATE_ALLOCATED == rtnobj2->state) break; if (rtnobj2->queueIndex != sizeIndex) break; remq_rtnobj_specific(freeList, shm_base, rtnobj2); if ((NULL_RTNOBJ_SM_OFF_T == freeList->fl) && (sizeIndex == min_free_index)) min_free_index = NUM_RTNOBJ_SIZE_BITS; if (rtnobj2 < rtnobj) { assert(elemSize == ((sm_off_t)rtnobj - (sm_off_t)rtnobj2)); rtnobj = rtnobj2; /* Pick lower address buddy for top of new bigger block */ assert(rtnobj_off >= (sm_off_t)elemSize); rtnobj_off -= elemSize; } freeList++; sizeIndex++; elemSize *= 2; } rtnobj->state = STATE_FREE; rtnobj->queueIndex = sizeIndex; assert(rtnobj_off == ((sm_uc_ptr_t)rtnobj - shm_base)); assert(0 == (rtnobj_off % ((sm_off_t)1 << (sizeIndex + MIN_RTNOBJ_SIZE_BITS)))); insqt_rtnobj(&rtnobj->userStorage.freePtr, freeList, shm_base); max_free_index = rtnobj_shm_hdr->rtnobj_max_free_index; if (sizeIndex >= max_free_index) rtnobj_shm_hdr->rtnobj_max_free_index = sizeIndex + 1; /* Recompute max_free_index */ if (NUM_RTNOBJ_SIZE_BITS == min_free_index) { /* Recompute min_free_index */ max_free_index = sizeIndex + 1; sizeIndex = rtnobj_shm_hdr->rtnobj_min_free_index; freeList = &rtnobj_shm_hdr->freeList[sizeIndex]; for ( ; sizeIndex < max_free_index; sizeIndex++, freeList++) { assert(sizeIndex < NUM_RTNOBJ_SIZE_BITS); if (NULL_RTNOBJ_SM_OFF_T != freeList->fl) break; } rtnobj_shm_hdr->rtnobj_min_free_index = sizeIndex; } DEBUG_ONLY(rtnobj_verify_min_max_free_index(rtnobj_shm_hdr)); DEBUG_ONLY(rtnobj_verify_freelist_fl_bl(rtnobj_shm_hdr, shm_base);) rel_latch(&shm_hdr->relinkctl_latch); if (NULL != prev_rtnobj) prev_rtnobj->next_rtnobj_shm_offset = shm_index_off; else relinkrec->rtnobj_shm_offset = shm_index_off; assert(objLen <= relinkrec->objLen); relinkrec->objLen -= objLen; assert(origElemSize <= relinkrec->usedLen); relinkrec->usedLen -= origElemSize; assert(0 < relinkrec->numvers); relinkrec->numvers--; RTNOBJ_DBG(fprintf(stderr, \ "rtnobj_shm_free : rtnname = %s : objhash = 0x%llx : Freed : refcnt = %d : elem = 0x%llx : numvers = %d\n", \ relinkrec->rtnname_fixed.c, (UINTPTR_T)rtnobj->objhash, rtnobj->refcnt, \ (UINTPTR_T)objBuff, relinkrec->numvers);) rel_latch(&relinkrec->rtnobj_latch); return; } #endif fis-gtm-V7.0-005/sr_unix/rtnobj.h0000644000032200000250000000270714342376335015523 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2022 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. * * * ****************************************************************/ #ifndef RTNOBJ_H_INCLUDED #define RTNOBJ_H_INCLUDED #ifdef AUTORELINK_SUPPORTED #include #define LATCH_GRABBED_FALSE FALSE #define LATCH_GRABBED_TRUE TRUE #ifdef DEBUG void rtnobj_verify_freelist_fl_bl(rtnobjshm_hdr_t *rtnobj_shm_hdr, sm_uc_ptr_t shm_base); void rtnobj_verify_min_max_free_index(rtnobjshm_hdr_t *rtnobj_shm_hdr); #endif void insqt_rtnobj(que_ent_ptr_t new, que_ent_ptr_t que_base, sm_uc_ptr_t shm_base); rtnobj_hdr_t *remqh_rtnobj(que_ent_ptr_t base, sm_uc_ptr_t shm_base); void remq_rtnobj_specific(que_ent_ptr_t que_base, sm_uc_ptr_t shm_base, rtnobj_hdr_t *rtnobj); sm_uc_ptr_t rtnobj_shm_malloc(zro_hist *zhist, int fd, off_t obj_size, gtm_uint64_t objhash); void rtnobj_shm_free(rhdtyp *rhead, boolean_t latch_grabbed); rtnobj_hdr_t *rtnobj_retpos(open_relinkctl_sgm *linkctl, rtnobj_sm_off_t shm_index_off); void rtnobj_integ(relinkrec_t *relinkrec, open_relinkctl_sgm *linkctl); #endif #endif /* RTNOBJ_H_INCLUDED */ fis-gtm-V7.0-005/sr_unix/rts_error.c0000644000032200000250000001341414342376330016231 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_stdio.h" #include #include #include "gtm_multi_thread.h" #include "gtm_putmsg_list.h" #include "gtmimagename.h" #include "error.h" #include "util.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmmsg.h" #include "repl_msg.h" #include "gtmsource.h" #include "anticipatory_freeze.h" #include "toktyp.h" #include "cgp.h" GBLREF boolean_t created_core; GBLREF boolean_t dont_want_core; GBLREF boolean_t run_time; GBLREF char cg_phase; GBLREF gd_region *gv_cur_region; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF int gtm_errno; GBLREF void (*stx_error_va_fptr)(int in_error, ...); /* Function pointer for stx_error_va() so this can avoid * pulling stx_error() into gtmsecshr. */ error_def(ERR_ASSERT); error_def(ERR_DRVLONGJMP); error_def(ERR_GTMASSERT); error_def(ERR_GTMASSERT2); error_def(ERR_GTMCHECK); error_def(ERR_JOBINTRRETHROW); error_def(ERR_JOBINTRRQST); error_def(ERR_MEMORY); error_def(ERR_OUTOFSPACE); error_def(ERR_REPEATERROR); error_def(ERR_REPLONLNRLBK); error_def(ERR_STACKOFLOW); error_def(ERR_TPRETRY); int rts_error_va(void *csa, int argcnt, va_list var); /* ---------------------------------------------------------------------------------------- * WARNING: For chained error messages, all messages MUST be followed by an fao count; * ======= zero MUST be specified if there are no parameters. * ---------------------------------------------------------------------------------------- */ /* coverity[+kill] */ int rts_error(int argcnt, ...) { va_list var; sgmnt_addrs *csa; jnlpool_addrs_ptr_t local_jnlpool; /* needed by PTHREAD_CSA_FROM_GV_CUR_REGION */ DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; PTHREAD_CSA_FROM_GV_CUR_REGION(csa, local_jnlpool); VAR_START(var, argcnt); return rts_error_va(csa, argcnt, var); } /* coverity[+kill] */ int rts_error_csa(void *csa, int argcnt, ...) { va_list var; VAR_START(var, argcnt); return rts_error_va(csa, argcnt, var); } int rts_error_va(void *csa, int argcnt, va_list var) { int msgid; va_list var_dup; const err_ctl *ctl; boolean_t was_holder; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # ifdef DEBUG if (TREF(rts_error_unusable) && !TREF(rts_error_unusable_seen)) { TREF(rts_error_unusable_seen) = TRUE; /* The below assert ensures that this rts_error invocation is appropriate in the current context of the code that * triggered this rts_error. If ever this assert fails, investigate the window of DBG_MARK_RTS_ERROR_UNUSABLE * and DBG_MARK_RTS_ERROR_USABLE in the call-stack. */ assert(FALSE); } # endif PTHREAD_MUTEX_LOCK_IF_NEEDED(was_holder); /* get thread lock in case threads are in use */ /* Note that rts_error does not return most of the times (control gets transferred somewhere else) so no point * unlocking thread mutex at the end of this function. This means that if one thread gets an error, the rest of * the threads might wait if/when they need to make a PTHREAD_MUTEX_LOCK_IF_NEEDED call. But that is considered * okay since the process is anyways going to terminate due to the error in one thread. This is at least true * for the current use of threads in mupip. If/when threads are used in GT.M this will need to be revisited. */ VAR_COPY(var_dup, var); if (-1 == gtm_errno) gtm_errno = errno; msgid = va_arg(var_dup, int); /* If there was a previous fatal error that did not yet get printed, do it before overwriting the * util_output buffer with the about-to-be-handled nested error. This way one will see ALL the * fatal error messages (e.g. assert failures) in the order in which they occurred instead of * just the last nested one. */ if (DUMPABLE) PRN_ERROR; /* This is simply a place holder msg to signal tp restart or otherwise rethrow an error */ if ((ERR_TPRETRY == msgid) || (ERR_REPEATERROR == msgid) || (ERR_REPLONLNRLBK == msgid) || (ERR_JOBINTRRQST == msgid) || (ERR_JOBINTRRETHROW == msgid) || (ERR_DRVLONGJMP == msgid)) { SET_ERROR_CONDITION(msgid); /* sets "error_condition" & "severity" */ } else { /* Note this message is not flushed out. This is so user console is not polluted with messages that are going to be * handled by a ZTRAP. If ZTRAP is not active, the message will be flushed out in mdb_condition_handler - which is * usually the top level handler or is rolled over into by higher handlers. */ if (IS_GTMSECSHR_IMAGE) util_out_print(NULL, RESET); SET_ERROR_CONDITION(msgid); /* sets "error_condition" & "severity" */ if (!run_time && (CGP_PARSE == cg_phase) && !DUMP) { (*stx_error_va_fptr)(msgid, var_dup); TREF(director_token) = TK_ERROR; return FALSE; } gtm_putmsg_list(csa, argcnt, var); if (DUMPABLE) created_core = dont_want_core = FALSE; /* We can create a(nother) core now */ if (IS_GTMSECSHR_IMAGE) util_out_print(NULL, OPER); /* gtmsecshr errors always immediately pushed out */ } va_end(var_dup); va_end(var); DRIVECH(msgid); /* Drive the topmost (inactive) condition handler */ /* Note -- at one time there was code here to catch if we returned from the condition handlers * when the severity was error or above. That code had to be removed because of several errors * that are handled and returned from. An example is EOF errors. SE 9/2000 */ return FALSE; } fis-gtm-V7.0-005/sr_unix/runall.csh0000755000032200000250000004265314342376330016052 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2001-2021 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. # # # ################################################################# # Get rid of debug options producing huge amounts of output if set unsetenv gtmdbglvl # Unconditionally switch to C locale. UTF-8 locales have shown issues in various stages of runall. unsetenv LC_ALL setenv LC_CTYPE C setenv LC_COLLATE C if ($?RUNALL_DEBUG != 0) then set verbose set echo endif echo "" set runall_status = 0 set listonly = 0 set compileonly = 0 set linkonly = 0 set helponly = 0 set mumps_changed = "" set gtmplatformlib_changed = "" while (0 < $#) switch ($1:q) case -n : set listonly = 1 breaksw case -c : set compileonly = 1 breaksw case -l : set linkonly = 1 breaksw case -h : set helponly = 1 breaksw default : break endsw shift end if ($helponly) then echo "Usage : `basename $0` [-n|-c|-h] [file...]" echo " -n List the out-of-date sources; don't compile, or build" echo " -c Compile the out-of-date sources; don't build" echo " -l Link the out-of-date sources; don't compile" echo " -h Print this message" echo "" exit 1 endif source $gtm_root/$gtm_verno/tools/gtm_cshrc.csh source $gtm_root/$gtm_verno/tools/gtm_env.csh if (! $?gtm_curpro) then # The tools and test system relies on $gtm_curpro defined in the environment for greater flexibility # Some day we need to eliminate versions.csh and expect $gtm_curpro to be defined in the environment. # setenv gtm_curpro `source $gtm_root/$gtm_verno/tools/versions.csh ; echo $gtm_curpro` echo 'RUNALL-E-CURPRO: gtm_curpro not defined. runall expects gtm_curpro to be defined and exist in $gtm_root' exit 1 endif \unalias unalias >& /dev/null # TCSH does not let you alias unalias unalias cd chown rm touch echo grep sed awk sort cat mv ls if ($?RUNALL_VERSION == 0) then setenv RUNALL_VERSION endif if ($?RUNALL_IMAGE == 0) then setenv RUNALL_IMAGE endif if ("$RUNALL_VERSION" == "") then setenv RUNALL_VERSION $gtm_verno endif if ("$RUNALL_IMAGE" == "") then setenv RUNALL_IMAGE $gtm_exe:t endif if ($?RUNALL_EXTRA_CC_FLAGS == 0) then setenv RUNALL_EXTRA_CC_FLAGS "" endif if ($?RUNALL_EXTRA_AS_FLAGS == 0) then setenv RUNALL_EXTRA_AS_FLAGS "" endif if ($RUNALL_IMAGE == "d") then setenv RUNALL_IMAGE "dbg" else if ($RUNALL_IMAGE == "p") then setenv RUNALL_IMAGE "pro" else if ($RUNALL_IMAGE == "b") then setenv RUNALL_IMAGE "bta" endif cd $gtm_root/$RUNALL_VERSION/${RUNALL_IMAGE}/obj set setactive_parms = ( $RUNALL_VERSION $RUNALL_IMAGE ) ; source $gtm_tools/setactive.csh if ($?RUNALL_BYPASS_VERSION_CHECK == 0) then if ($gtm_verno =~ V[4-8]* || $gtm_verno == "V990" ) then echo "" echo "-----------------------------------------------------------------------------------" echo "RUNALL-E-WRONGVERSION : Cannot Runall a Non-Developemental Version ----> $gtm_verno" echo "-----------------------------------------------------------------------------------" echo "" @ runall_status = 1 # to signal that the runall failed to do its job goto cleanup endif endif echo "" echo "Start of $gtm_tools/runall.csh" echo "" echo -n " --> " date echo "" echo "Input Command Line" echo "" echo -n " --> " echo "[ $0 "$argv" ]" echo "" echo "Environment Variables" echo "" echo " RUNALL_VERSION ----> [ $RUNALL_VERSION ]" echo " RUNALL_IMAGE ----> [ $RUNALL_IMAGE ]" echo " RUNALL_EXTRA_CC_FLAGS ----> [ $RUNALL_EXTRA_CC_FLAGS ]" echo " RUNALL_EXTRA_AS_FLAGS ----> [ $RUNALL_EXTRA_AS_FLAGS ]" echo " gtmroutines ----> [ $gtmroutines ]" echo "" if (`uname` == "SunOS") then set path = (/usr/xpg4/bin $path) endif set user=`id -u -n` # Remove old temporary files and logs. rm -f $gtm_log/error.$RUNALL_IMAGE.log $gtm_log/gt_cc_*.{csh,out} $gtm_log/buildaux_*.{out,err,csh} $gtm_log/tttgen_*.log set TMP_DIR_PREFIX = "/tmp/__${user}__runall" setenv TMP_DIR "${TMP_DIR_PREFIX}__`date +"%y%m%d_%H_%M_%S"`_$$" # needed by runall_cc_many.csh/runall_cc_one.csh hence "setenv" rm -f ${TMP_DIR}_* >& /dev/null onintr cleanup set platform_name = `uname | sed 's/-//g' | sed 's,/,,' | tr '[A-Z]' '[a-z]'` set mach_type = `uname -m` cat - << LABEL >>! ${TMP_DIR}_exclude dse dse dse_cmd dse mupip mupip mupip_cmd mupip lke lke lke_cmd lke gtm mumps gtmsecshr gtmsecshr gtmsecshr_wrapper gtmsecshr gtm_main mumps gtcm_main gtcm_server omi_srvc_xct gtcm_server gtcm_play gtcm_play omi_sx_play gtcm_play gtcm_shmclean gtcm_shmclean gtcm_pkdisp gtcm_pkdisp gtcm_gnp_server gtcm_gnp_server gtmcrypt_dbk_ref gtmcrypt gtmcrypt_pk_ref gtmcrypt gtmcrypt_sym_ref gtmcrypt gtmcrypt_ref gtmcrypt gtmcrypt_util gtmcrypt maskpass gtmcrypt gtm_tls_impl gtmcrypt LABEL # this first set of excluded modules are from the list above of modules that get built as plugins. Only plugins # should be metioned in this list. set exclude_compile_list = (gtmcrypt_dbk_ref gtmcrypt_sym_ref gtmcrypt_pk_ref gtmcrypt_ref maskpass gtm_tls_impl) set exclude_compile_list = ($exclude_compile_list gtmcrypt_util) # modules that should never be built or compiled are in this list. They are used in other capacities (e.g. used to # generate other routines) but are NOT part of the GTM runtime. Other scripts compile and use these routines. set exclude_build_list = "gtm_threadgbl_deftypes" # find all libnames other than mumps pushd $gtm_tools >& /dev/null set comlist_liblist = `ls *.list | sed 's/.list//' | sed 's/^lib//'` popd >& /dev/null foreach value ($comlist_liblist) cat $gtm_tools/lib$value.list | awk '{print "lib'${value}'.a", $1, " "}' >>! ${TMP_DIR}_list end rm -f ${TMP_DIR}_main_misc >& /dev/null touch ${TMP_DIR}_main_.misc rm -f $gtm_inc/__temp_runall_* >& /dev/null rm -f $gtm_src/__temp_runall_* >& /dev/null rm -f ${TMP_DIR}_inc_files >& /dev/null rm -f ${TMP_DIR}_src_files >& /dev/null rm -f ${TMP_DIR}_latest_exe touch ${TMP_DIR}_inc_files touch ${TMP_DIR}_src_files echo "Modifying release_name.h" $btc_tools/edrelnam.csh $RUNALL_VERSION # For all builds, gtm_threadgbl_deftypes.h needs to be generated unless we are bypassing. # This only updates gtm_threadgbl_deftypes.h in $gtm_inc if generation shows it changed. # Note shortened variable to RUNALL_BYPASS_GEN_THREADGBL due to failure on Tru64 (too long var name). if ($?RUNALL_BYPASS_GEN_THREADGBL == 0) then tcsh $gtm_tools/gen_gtm_threadgbl_deftypes.csh if (0 != $status) @ runall_status = $status if (0 != $runall_status) then echo "Failed to build gtm_threadgbl_deftypes.h - aborting build" exit $runall_status endif # Setup link from $gtm_obj to the proper assembler include file if (! -e ${gtm_obj}/gtm_threadgbl_deftypes_asm.si) then set asmtgbltype = $gtm_exe:t \ln -s ${gtm_inc}/gtm_threadgbl_deftypes_asm_${asmtgbltype}.si ${gtm_obj}/gtm_threadgbl_deftypes_asm.si endif endif if ($#argv) then while ($#argv > 0) set ext = $1:e if ($ext == "h" || $ext == "si") then echo $1 >>&! ${TMP_DIR}_inc_files else if ("$ext" == "c" || "$ext" == "s" || "$ext" == "msg" || "$ext" == "") then echo $1 >>&! ${TMP_DIR}_src_files endif shift end else echo "...... Searching for --------> Last Built Executable in $gtm_exe ...... " pushd $gtm_exe >& /dev/null # Look down the $gtm_exe recursively so that the search for recent build executable is inclusive of $gtm_exe/plugin/ and # $gtm_exe/plugin/gtmcrypt. Also the new shell invocation is needed to redirect the stderr that will arise due to the # gtmsecshr privileges for which (find .) will issue 'permission denied'. # Look for files with execute permissions (-111) and ignore help database (! -name "*.dat") (ls -lart `find . -type f -perm -111 ! -name "*.dat"` | tail -n 1 | awk '{print $NF}' > ${TMP_DIR}_latest_exe) >&! /dev/null set latest_exe_with_rel_path = `cat ${TMP_DIR}_latest_exe` set latest_exe = `basename $latest_exe_with_rel_path` rm -f ${TMP_DIR}_latest_exe popd >& /dev/null if ("$latest_exe" != "") then echo "...... Searching for --------> out-of-date INCLUDEs ...... " pushd $gtm_inc >& /dev/null ln -s $gtm_exe/${latest_exe_with_rel_path} __temp_runall_${latest_exe} ls -1Lat | sed -n '1,/__temp_runall_'${latest_exe}'/p' | grep -E '(\.h$|\.si$)' >>&! ${TMP_DIR}_inc_files rm -f __temp_runall_${latest_exe} >& /dev/null popd >& /dev/null echo "...... Searching for --------> out-of-date SOURCEs ...... " pushd $gtm_src >& /dev/null ln -s $gtm_exe/${latest_exe_with_rel_path} __temp_runall_${latest_exe} ls -1Lat | sed -n '1,/__temp_runall_'${latest_exe}'/p' | grep -E '(\.c$|\.msg$|\.s$)' >>&! ${TMP_DIR}_src_files rm -f __temp_runall_${latest_exe} >& /dev/null popd >& /dev/null echo "...... Searching for --------> out-of-date PCT ROUTINEs ...... " pushd $gtm_pct >& /dev/null ln -s $gtm_exe/${latest_exe_with_rel_path} __temp_runall_${latest_exe} ls -1Lat | sed -n '1,/__temp_runall_'${latest_exe}'/p' | grep -E '\.(m|hlp)$' >>&! ${TMP_DIR}_pct_files rm -f __temp_runall_${latest_exe} >& /dev/null popd >& /dev/null else echo "" echo "Unable to find the last modified executable in $gtm_exe. ....... Exiting" echo "" @ runall_status = 1 # to signal that the runall failed to do its job goto cleanup endif endif ###### if *.msg files got included, then also include *_ansi.h files for recompiling ####### if (!(-z ${TMP_DIR}_src_files)) then foreach file (`cat ${TMP_DIR}_src_files`) set ext = $file:e if ("$ext" == "msg") then set file=$file:t:r echo ${file}_ansi.h >>&! ${TMP_DIR}_inc_files endif end endif if (! -z ${TMP_DIR}_inc_files) then sort -u ${TMP_DIR}_inc_files >&! ${TMP_DIR}_inc_files_sorted mv ${TMP_DIR}_inc_files_sorted ${TMP_DIR}_inc_files endif set backslash_quote if (!(-z ${TMP_DIR}_inc_files)) then echo "...... Searching for --------> out-of-date Nested HEADER Files ...... " pushd $gtm_inc >& /dev/null rm -f ${TMP_DIR}_inc_file_temp >& /dev/null touch ${TMP_DIR}_inc_file_temp while (1) diff ${TMP_DIR}_inc_file_temp ${TMP_DIR}_inc_files | grep "^>" | sed 's/> //' > ${TMP_DIR}_inc_file_diff if (! -z ${TMP_DIR}_inc_file_diff) then cp -f ${TMP_DIR}_inc_files ${TMP_DIR}_inc_file_temp foreach value (`cat ${TMP_DIR}_inc_file_diff`) ls -1 | grep -E '(\.h$|\.si$)' | \ xargs grep -l "^[ ]*#[ ]*include[ ]*\"$value\"" >>&! ${TMP_DIR}_inc_files end sort -u ${TMP_DIR}_inc_files >&! ${TMP_DIR}_inc_files_sorted mv ${TMP_DIR}_inc_files_sorted ${TMP_DIR}_inc_files else break endif end popd >& /dev/null endif if (!(-z ${TMP_DIR}_inc_files)) then echo "...... Searching for --------> #INCLUDEing Source Files ...... " pushd $gtm_src >& /dev/null foreach value (`cat ${TMP_DIR}_inc_files`) ls -1 | grep -E '(\.c$|\.msg$|\.s$)' | \ xargs grep -l "^[ ]*#[ ]*include[ ]*\"$value\"" >>&! ${TMP_DIR}_src_files end popd >& /dev/null endif # For ia64 & x86_64, the file - xfer_desc.i - needs to be generated. if ($?RUNALL_BYPASS_GEN_XFER_DESC == 0) then if ( "ia64" == $mach_type || "x86_64" == $mach_type ) then pushd $gtm_src tcsh $gtm_tools/gen_xfer_desc.csh popd endif endif # Generate ttt.c if (!(-e $gtm_src/ttt.c) || ((-M $gtm_tools/ttt.txt) > (-M $gtm_src/ttt.c))) then if (-x $gtm_root/$gtm_curpro/pro/mumps) then pushd $gtm_src >& /dev/null /usr/local/bin/tcsh $gtm_tools/gen_ttt.csh echo ttt.c >>&! ${TMP_DIR}_src_files popd >& /dev/null else echo "RUNALL-E-NoMUMPS, unable to regenerate ttt.c due to missing $gtm_curpro/pro/mumps" \ >> $gtm_log/error.$RUNALL_IMAGE.log endif endif # Run the list of files we are supposed to compile and delete the ones we never want to build if (-e ${TMP_DIR}_src_files) then rm -f ${TMP_DIR}_src_files_tmp foreach value ($exclude_build_list) sed "/$value/d" ${TMP_DIR}_src_files > ${TMP_DIR}_src_files_tmp mv ${TMP_DIR}_src_files_tmp ${TMP_DIR}_src_files end endif if (!(-z ${TMP_DIR}_src_files)) then sort -u ${TMP_DIR}_src_files >&! ${TMP_DIR}_src_files_sorted mv ${TMP_DIR}_src_files_sorted ${TMP_DIR}_src_files else if (-z ${TMP_DIR}_pct_files) then echo "" echo " -- Nothing to Compile ............ Exiting" echo "" @ runall_status = 0 # runall's job is done even though there is nothing to compile goto cleanup endif if ($listonly) then if ($?latest_exe) then echo "" echo "************** Last built executable ************** " echo "" echo $latest_exe endif echo "" echo "************** Out of date include files ********** " echo "" cat ${TMP_DIR}_inc_files echo "" echo "************** Out of date source files *********** " echo "" cat ${TMP_DIR}_src_files echo "" echo "************** Out of date pct files *********** " echo "" cat ${TMP_DIR}_pct_files echo "" @ runall_status = 0 goto cleanup endif # Irrespective of the gtm_chset value from the user environment, all # M objects generated in $gtm_dist (GDE*.o, _*.o, ttt.o) must be # compiled with gtm_chset="M". unsetenv gtm_chset echo "" echo "****************************** COMPILING *********************************" echo "" set exclude_compile_list_modified = "FALSE" if (! -z ${TMP_DIR}_src_files) then set filelist = `cat ${TMP_DIR}_src_files` set newfilelist = "" foreach file ( $filelist ) set newfile = $file:t # Do not compile plugin files if they are modified. Compilation and subsequent build will happen in # buildaux.csh. set skip_file_compile = 0 set tmplist = ($exclude_compile_list) set -f tmplist = ($tmplist $newfile:r) if ($#tmplist == $#exclude_compile_list) then # $newfile is part of $exclude_compile_list set exclude_compile_list_modified = "TRUE" else set newfilelist = "$newfilelist $file" endif end echo $newfilelist | xargs -n25 $gtm_tools/runall_cc_many.csh $linkonly @ runall_status = $status if ($compileonly || (0 != $runall_status)) then goto cleanup endif echo "" echo "-------------------------------- ARCHIVING --------------------------------" echo "" (ls -1 ${TMP_DIR}_lib_.* > ${TMP_DIR}_Lib_list) >& /dev/null foreach lib_ (`cat ${TMP_DIR}_Lib_list`) set libext = $lib_:e set library = $lib_:r set library = $library:e set library = $library.$libext set dstlib = "$gtm_obj/$library" # remove pre-existing object files from object library to ensure an older working version of the module does # not get used in case the current version of the module did not compile and failed to produce an object file. gt_ar $gt_ar_option_delete $dstlib `cat $lib_` >& /dev/null echo "--> into $dstlib <--" gt_ar $gt_ar_option_update $dstlib `cat $lib_` if (0 != $status) @ runall_status = $status if (("ia64" == $mach_type) && ("hpux" == $platform_name)) then ranlib $dstlib if (0 != $status) @ runall_status = $status endif if (0 != $status) @ runall_status = $status cat $lib_ | xargs rm -f echo "" end if (0 != $runall_status) then goto cleanup endif echo "" echo "----------------------------------------------------------" echo " BUILDing the executables for the $RUNALL_IMAGE version" echo "----------------------------------------------------------" echo # did libmumps.a change set mumps_changed = `grep libmumps.a ${TMP_DIR}_Lib_list` # on z/OS only, did libgtmzos.a change set gtmplatformlib_changed = `grep libgtmzos.a ${TMP_DIR}_Lib_list` cat ${TMP_DIR}_Lib_list ${TMP_DIR}_main_.misc | sed 's/.*lib//g' | sed 's/\.a$//g' | sort -u >! ${TMP_DIR}_main_.final endif # if (! -z ${TMP_DIR}_src_files) then # if either libmumps.a or a platform specific support library changes rebuild everything if ("$mumps_changed" != "" || "$gtmplatformlib_changed" != "") then $shell $gtm_tools/build${RUNALL_IMAGE}.csh $RUNALL_VERSION if (0 != $status) @ runall_status = $status else # If the plugin files are modified, include them in the final list of build routines. if ("TRUE" == "$exclude_compile_list_modified") then echo "gtmcrypt" >>! ${TMP_DIR}_build_routine.final endif if (-e ${TMP_DIR}_main_.final) then cat ${TMP_DIR}_main_.final >>! ${TMP_DIR}_build_routine.final endif # If we have a non-zero pct file list then include gde as one of the final build routines if (-e ${TMP_DIR}_pct_files && ! -z ${TMP_DIR}_pct_files) then echo "gde" >>! ${TMP_DIR}_build_routine.final endif set build_routine = `cat ${TMP_DIR}_build_routine.final` $gtm_tools/buildaux.csh $RUNALL_VERSION $RUNALL_IMAGE $gtm_root/$RUNALL_VERSION/$RUNALL_IMAGE $build_routine if (0 != $status) @ runall_status = $status endif # Create the GT.M/GDE/MUPIP/DSE/LKE help databases $gtm_tools/generate_help.csh $gtm_pct $gtm_log/error.${RUNALL_IMAGE}.log if ($status) then @ runall_status++ echo "runall-E-hlp, Error generating hlp databases" endif echo "" if ( -f $gtm_log/error.${RUNALL_IMAGE}.log ) then echo "Error summary:" echo "--------------" echo "" cat $gtm_log/error.${RUNALL_IMAGE}.log echo "" echo " ----> Leaving runall logfiles ${TMP_DIR}_* intact. Please remove after troubleshooting." echo "" goto done endif # Generate Special Debug Files (z/OS specific at the moment) if ( -e $gtm_tools/gtm_dbgld.csh ) then $gtm_tools/gtm_dbgld.csh $RUNALL_IMAGE if (0 != $status) @ runall_status = $status endif cleanup: if ($?latest_exe) then rm -f $gtm_src/__temp_runall_${latest_exe} $gtm_inc/__temp_runall_${latest_exe} >& /dev/null endif if ($?TMP_DIR_PREFIX) then rm -f ${TMP_DIR_PREFIX}* >& /dev/null endif done: echo "" echo "End of $gtm_tools/runall.csh" echo "" echo -n " -- " date echo "" exit $runall_status fis-gtm-V7.0-005/sr_unix/runall_cc_many.csh0000755000032200000250000000421114342376330017527 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2015 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. # # # ################################################################# # This is modeled on gt_cc.csh. It issues parallel compilations and aggregates results for caller (runall.csh) # # $1 - linkonly # $2 onwards - list of filenames to compile # set linkonly = $1 shift set filelist = ( $* ) set cmdfile="$gtm_log/runall_$$__batch.csh" set background="&" rm -f $cmdfile.err set dollar = '$' set err_check = "if (${dollar}status) touch $cmdfile.err" foreach cfile ($filelist) set outfile="$gtm_log/runall_$$_${cfile:t:r}.out" set redir=">& $outfile" echo "($gtm_tools/runall_cc_one.csh $linkonly $cfile; ${err_check}) $redir $background" >> $cmdfile end echo "wait" >> $cmdfile set cmdout="$gtm_log/runall_$$__batch.out" source $cmdfile >& $cmdout set stat=$status foreach cfile ($filelist) set file = ${cfile:t:r} set outfile="$gtm_log/runall_$$_$file.out" /bin/cat $outfile /bin/rm $outfile # Note: TMP_DIR env var is set by parent caller runall.csh # Check if a file of the form ${TMP_DIR}_lib_${file}.* exists. # If so move it to ${TMP_DIR}_lib_.* set filename = `ls -1 ${TMP_DIR}_lib_${file}.* |& grep ${TMP_DIR}_lib_${file}` if ("" != "$filename") then set newfilename = `echo $filename | sed 's/'$file'//g'` cat $filename >> $newfilename rm -f $filename endif # Check if a file of the form ${TMP_DIR}_main_${file}.misc exists. if (-e ${TMP_DIR}_main_${file}.misc) then cat ${TMP_DIR}_main_${file}.misc >> ${TMP_DIR}_main_.misc rm -f ${TMP_DIR}_main_${file}.misc endif end set exit_status = 0 if ($stat) then /bin/cat $cmdout set exit_status = 1 else /bin/rm $cmdfile /bin/rm $cmdout endif if (-e $cmdfile.err) then rm -f $cmdfile.err set exit_status = 1 endif exit $exit_status fis-gtm-V7.0-005/sr_unix/runall_cc_one.csh0000755000032200000250000000663414342376330017357 0ustar librarygtc#!/usr/local/bin/tcsh -f ################################################################# # # # Copyright (c) 2015-2021 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. # # # ################################################################# # This does compile of one module on behalf of runall.csh/runall_cc.csh # # $1 - linkonly # $2 - one filename to compile # set linkonly = $1 set file = $2 source $gtm_tools/gtm_env.csh alias runall_cc gt_cc_${RUNALL_IMAGE} alias gt_as $gt_as_assembler $gt_as_options_common $gt_as_option_I $RUNALL_EXTRA_AS_FLAGS alias runall_as gt_as_${RUNALL_IMAGE} set file = $file:t set ext = $file:e if ("$ext" == "") then set ext = "c" endif set file = $file:r # take the non-extension part for the obj file set objfile = ${file}.o @ runall_status = 0 if ($linkonly == 0) then # remove pre-existing object files before the current compilation # to ensure they do not get used if the current compile fails rm -f $gtm_obj/$file.o if ($ext == "s") then echo "$gtm_src/$file.$ext ----> $gtm_obj/$file.o" runall_as $gtm_src/${file}.s if (0 != $status) @ runall_status = $status else if ($ext == "c") then echo "$gtm_src/$file.$ext ----> $gtm_obj/$file.o" runall_cc $RUNALL_EXTRA_CC_FLAGS $gtm_src/$file.c if (0 != $status) @ runall_status = $status if ($file == "omi_srvc_xct") then chmod a+w $gtm_src/omi_sx_play.c \cp $gtm_src/omi_srvc_xct.c $gtm_src/omi_sx_play.c chmod a-w $gtm_src/omi_sx_play.c echo "$gtm_src/omi_sx_play.c ----> $gtm_obj/omi_sx_play.o" # remove pre-existing object rm -f $gtm_obj/omi_sx_play.o runall_cc -DFILE_TCP $RUNALL_EXTRA_CC_FLAGS $gtm_src/omi_sx_play.c if (0 != $status) @ runall_status = $status endif else if ($ext == "msg") then echo "$gtm_src/$file.$ext ----> $gtm_obj/${file}_ctl.c ----> $gtm_obj/${file}_ctl.o" # gtm_startup_chk requires gtm_dist setup rm -f ${file}_ctl.c ${file}_ansi.h # in case an old version is lying around set real_gtm_dist = "$gtm_dist" if ($?gtmroutines) set save_gtmroutines = "$gtmroutines" setenv gtm_dist "$gtm_root/$gtm_curpro/pro" setenv gtmroutines "$gtm_obj($gtm_pct)" $gtm_root/$gtm_curpro/pro/mumps -run msg $gtm_src/$file.msg if (0 != $status) @ runall_status = $status setenv gtm_dist "$real_gtm_dist" unset real_gtm_dist if ($?save_gtmroutines) setenv gtmroutines "$save_gtmroutines" \mv -f ${file}_ctl.c $gtm_src/${file}_ctl.c if ( -f ${file}_ansi.h ) then \mv -f ${file}_ansi.h $gtm_inc endif runall_cc $RUNALL_EXTRA_CC_FLAGS $gtm_src/${file}_ctl.c if (0 != $status) @ runall_status = $status set objfile = ${file}_ctl.o endif endif # Note: TMP_DIR env var is set by grandparent caller runall.csh set library=`grep "^$file " ${TMP_DIR}_exclude` if ("$library" == "") then set library=`grep " $file " ${TMP_DIR}_list | awk '{print $1}' | uniq` if ("$library" == "") then set library="libmumps.a" endif echo $objfile >> ${TMP_DIR}_lib_${file}.$library else if ("$library[1]" != "$library") then echo $library[2] >> ${TMP_DIR}_main_${file}.misc if ($file == "omi_srvc_xct.c") then echo "gtcm_play" >> ${TMP_DIR}_main_${file}.misc endif endif endif exit $runall_status fis-gtm-V7.0-005/sr_unix/same_device_check.c0000644000032200000250000001523514342376330017614 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2021 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_stat.h" #include "eintr_wrappers.h" #include "io.h" #include "gtm_netdb.h" #include "gtm_socket.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_inet.h" #include "gtm_un.h" error_def(ERR_GETSOCKNAMERR); error_def(ERR_GETNAMEINFO); error_def(ERR_TEXT); error_def(ERR_SYSCALL); typedef struct sockaddr_un *sockun_ptr; /* This module checks whether standard in and standard out are the same. * In VMS, it gets the input device from the previously established GT.M structure and the output device from its caller. * In UNIX, it ignores its arguments and gets the devices from the system designators * st_mode includes permissions so just check file type * if same device indicated in st_mode do the following: * if is a socket compare * else if is a character device then st_rdev will be the same if same device * else assert */ bool same_device_check (mstr tname, char *buf) { int fstat_res, gsn_stat; struct stat outbuf1, outbuf2; GTM_SOCKLEN_TYPE socknamelen1; GTM_SOCKLEN_TYPE socknamelen2; GTM_SOCKLEN_TYPE psocknamelen1; GTM_SOCKLEN_TYPE psocknamelen2; struct sockaddr_storage sockname1; struct sockaddr_storage sockname2; struct sockaddr_storage psockname1; struct sockaddr_storage psockname2; char port_buffer1[NI_MAXSERV]; char port_buffer2[NI_MAXSERV]; char pport_buffer1[NI_MAXSERV]; char pport_buffer2[NI_MAXSERV]; char host_buffer1[NI_MAXHOST]; char host_buffer2[NI_MAXHOST]; char phost_buffer1[NI_MAXHOST]; char phost_buffer2[NI_MAXHOST]; int errcode, tmplen, save_errno; const char *errptr; FSTAT_FILE(0, &outbuf1, fstat_res); if (-1 == fstat_res) { save_errno = errno; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fstat"), CALLFROM, save_errno); } FSTAT_FILE(1, &outbuf2, fstat_res); if (-1 == fstat_res) { save_errno = errno; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fstat"), CALLFROM, save_errno); } if ((S_IFMT & outbuf1.st_mode) != (S_IFMT & outbuf2.st_mode)) return FALSE; if (S_ISSOCK(outbuf1.st_mode)) { /* if here then both 0,1 are sockets */ socknamelen1 = SIZEOF(sockname1); if (-1 == (gsn_stat = getsockname(0, (struct sockaddr *)&sockname1, (GTM_SOCKLEN_TYPE *)&socknamelen1))) { save_errno = errno; if (IS_SOCKNAME_UNIXERROR(save_errno)) { /* problem with getsockname for AF_UNIX socket so just assign family for the switch below */ (((sockaddr_ptr)&sockname1)->sa_family) = AF_UNIX; } else { /* process error */ errptr = (char *)STRERROR(save_errno); tmplen = STRLEN(errptr); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_GETSOCKNAMERR, 3, save_errno, tmplen, errptr); } } socknamelen2 = SIZEOF(sockname2); if (-1 == (gsn_stat = getsockname(1, (struct sockaddr *)&sockname2, (GTM_SOCKLEN_TYPE *)&socknamelen2))) { save_errno = errno; if (IS_SOCKNAME_UNIXERROR(save_errno)) { /* problem with getsockname for AF_UNIX socket so just assign family for the switch below */ (((sockaddr_ptr)&sockname2)->sa_family) = AF_UNIX; } else { /* process error */ errptr = (char *)STRERROR(save_errno); tmplen = STRLEN(errptr); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_GETSOCKNAMERR, 3, save_errno, tmplen, errptr); } } /* if both sockets not the same family then not the same device */ if ((((sockaddr_ptr)&sockname1)->sa_family) != (((sockaddr_ptr)&sockname2)->sa_family)) return FALSE; switch(((sockaddr_ptr)&sockname1)->sa_family) { case AF_INET: case AF_INET6: GETNAMEINFO((struct sockaddr *)&sockname1, socknamelen1, host_buffer1, NI_MAXHOST, port_buffer1, NI_MAXSERV, NI_NUMERICHOST|NI_NUMERICSERV, errcode); if (0 != errcode) { RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode); return FALSE; } GETNAMEINFO((struct sockaddr *)&sockname2, socknamelen2, host_buffer2, NI_MAXHOST, port_buffer2, NI_MAXSERV, NI_NUMERICHOST|NI_NUMERICSERV, errcode); if (0 != errcode) { RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode); return FALSE; } /* hosts and ports must be the same */ if (STRCMP(host_buffer1, host_buffer2) || STRCMP(port_buffer1, port_buffer2)) return FALSE; psocknamelen1 = SIZEOF(psockname1); if (-1 == (gsn_stat = getpeername(0, (struct sockaddr *)&psockname1, (GTM_SOCKLEN_TYPE *)&psocknamelen1))) { save_errno = errno; errptr = (char *)STRERROR(save_errno); tmplen = STRLEN(errptr); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_GETSOCKNAMERR, 3, save_errno, tmplen, errptr); } psocknamelen2 = SIZEOF(psockname2); if (-1 == (gsn_stat = getpeername(1, (struct sockaddr *)&psockname2, (GTM_SOCKLEN_TYPE *)&psocknamelen2))) { save_errno = errno; errptr = (char *)STRERROR(save_errno); tmplen = STRLEN(errptr); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_GETSOCKNAMERR, 3, save_errno, tmplen, errptr); } GETNAMEINFO((struct sockaddr *)&psockname1, psocknamelen1, phost_buffer1, NI_MAXHOST, pport_buffer1, NI_MAXSERV, NI_NUMERICHOST|NI_NUMERICSERV, errcode); if (0 != errcode) { RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode); return FALSE; } GETNAMEINFO((struct sockaddr *)&psockname2, psocknamelen2, phost_buffer2, NI_MAXHOST, pport_buffer2, NI_MAXSERV, NI_NUMERICHOST|NI_NUMERICSERV, errcode); if (0 != errcode) { RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode); return FALSE; } /* hosts and ports for the peer sockets must also be the same */ if (STRCMP(phost_buffer1, phost_buffer2) || STRCMP(pport_buffer1, pport_buffer2)) return FALSE; break; case AF_UNIX: default: /* if inodes are different or st_dev different then not the same device */ if ((outbuf1.st_ino != outbuf2.st_ino) || (outbuf1.st_dev != outbuf2.st_dev)) return FALSE; break; } return TRUE; } else if (S_ISCHR(outbuf1.st_mode)) { /* if here then both 0,1 are character devices */ /* if inodes are different or st_dev different then not the same device */ if ((outbuf1.st_ino != outbuf2.st_ino) || (outbuf1.st_dev != outbuf2.st_dev)) return FALSE; else return TRUE; } else { /* unexpected type so assert */ assert(FALSE); return FALSE; } } fis-gtm-V7.0-005/sr_unix/scantypedefs.m0000644000032200000250000020072714342376330016717 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2010-2022 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Part of gengtmdeftypes. ; ; Routine to process gtmtypeslist.txt file and generate GTMDefinedTypesInit.m from it. ; ; Since we want to map only GT.M's structures, an earlier phase has processed the raw ; GT.M include files to extract the typedefs that GT.M itself does. We read those in ; so we know what structures to generate maps for. This second phase generates expansions ; for ALL the structures. We separate the ones we care about from the ones we don't ; by comparing the names to our list from phase 1. This secondary selection is so we don't ; create maps for the thousands of system dependent includes that would bloat into near ; unusability. ; Set TRUE=1,FALSE=0 Set debug=FALSE Set debugtoken=FALSE Set KeepFiles=FALSE Set gtmsdver="1.2.0" ; Set version id Set $ETrap="Goto ErrorTrap^scantypedefs" Set TAB=$ZChar(9) Set entrylvl=$ZLevel Set gtmver=$ZCmdline ; ; Set version flags ; If ("V52000"]gtmver) Set PreV52000=TRUE Else Set PreV52000=FALSE If ("V52001"]gtmver) Set PreV52001=TRUE Else Set PreV52001=FALSE If ("V53000"]gtmver) Set PreV53000=TRUE Else Set PreV53000=FALSE If ("V53001"]gtmver) Set PreV53001=TRUE Else Set PreV53001=FALSE If ("V53001A"]gtmver) Set PreV53001A=TRUE Else Set PreV53001A=FALSE If ("V53002"]gtmver) Set PreV53002=TRUE Else Set PreV53002=FALSE If ("V53003"]gtmver) Set PreV53003=TRUE Else Set PreV53003=FALSE If ("V53004"]gtmver) Set PreV53004=TRUE Else Set PreV53004=FALSE If ("V54000"]gtmver) Set PreV54000=TRUE Else Set PreV54000=FALSE If ("V54001"]gtmver) Set PreV54001=TRUE Else Set PreV54001=FALSE If ("V54002"]gtmver) Set PreV54002=TRUE Else Set PreV54002=FALSE ; Set gtmhdw=$ZPiece($ZVersion," ",4) Set ExtraCcLdParms="",ExtraCcParms="" Set:(PreV53001&("x86_64"=gtmhdw)) (ExtraCcParms,ExtraCcLdParms)="-m32" Do:("RS6000"=gtmhdw) . Set:(PreV53001) ExtraCcParms=ExtraCcParms_" -q32",ExtraCcLdParms=ExtraCcLdParms_" -b32" . Set:('PreV53001) ExtraCcParms=ExtraCcParms_" -q64",ExtraCcLdParms=ExtraCcLdParms_" -b64" Do:("SPARC"=gtmhdw) . Set:(PreV53001) ExtraCcParms=ExtraCcParms_" -m32",ExtraCcLdParms=ExtraCcLdParms_" -m32" . Set:('PreV53001) ExtraCcParms=ExtraCcParms_" -m64",ExtraCcLdParms=ExtraCcLdParms_" -m64" Do:($ZVersion["Linux") . Set ExtraCcParms=ExtraCcParms_$$iquote() ; ; Define basic token types we will use in our parse ; Set TKEOF=1 ; End of file Set TKLBRACE=2 ; Left brace "{" Set TKRBRACE=3 ; Right brace "}" Set TKLBRACKET=4 ; Left bracket "[" Set TKRBRACKET=5 ; Right bracket "]" Set TKCOLON=6 ; Colon ":" Set TKSEMI=7 ; Semi-colon ";" Set TKLPAREN=8 ; Left paren "(" Set TKRPAREN=9 ; Right paren ")" Set TKASTERISK=10 ; Asterisk "*" (multiply) Set TKPLUS=11 ; Plus sign "+" Set TKMINUS=12 ; Minus sign "-" Set TKSLASH=13 ; Slash "/" (divide) Set TKCOMMA=14 ; Comma "," Set TKDOT=15 ; Period "." Set TKTYPEDEF=16 ; Keyword "typedef" Set TKUNION=17 ; Keyword "union" Set TKSTRUCT=18 ; Keyword "struct" Set TKENUM=19 ; Keyword "enum" Set TKSIZEOF=20 ; Keyword "sizeof" Set TKVOLATILE=21 ; Keyword "volatile" Set TKCONST=22 ; Keyword "const" Set TKBASETYPE=23 ; basic type (int, char, etc) Set TKGTMTYPE=24 ; Defined GTM type (in types()) Set TKINTEGER=25 ; An integer Set TKOTHERTYPE=26 ; Other type we don't care about ; ; "reverse lookups" for the token parser ; Set CharScn("{")=TKLBRACE Set CharScn("}")=TKRBRACE Set CharScn("[")=TKLBRACKET Set CharScn("]")=TKRBRACKET Set CharScn(":")=TKCOLON Set CharScn(";")=TKSEMI Set CharScn("(")=TKLPAREN Set CharScn(")")=TKRPAREN Set CharScn("*")=TKASTERISK Set CharScn("+")=TKPLUS Set CharScn("-")=TKMINUS Set CharScn("/")=TKSLASH Set CharScn(",")=TKCOMMA Set CharScn(".")=TKDOT ; Set Keywd("typedef")=TKTYPEDEF Set Keywd("union")=TKUNION Set Keywd("struct")=TKSTRUCT Set Keywd("enum")=TKENUM Set Keywd("sizeof")=TKSIZEOF Set Keywd("volatile")=TKVOLATILE Set Keywd("const")=TKCONST ; ; Define the token types we can see as a typedef target type ; Set TypdfTypes(TKOTHERTYPE)="" Set TypdfTypes(TKGTMTYPE)="" Set TypdfTypes(TKBASETYPE)="" ; e.g. "addr" is a valid var name if a bit overloaded ; ; Type transformations. Others added dynamically ; Set TransType("*fnptr")="addr" ; ; Define basic types we know about ; Do DefBasicType("addr") Do DefBasicType("boolean_t") Do DefBasicType("char") Do DefBasicType("double") Do DefBasicType("float") Do DefBasicType("int") Do DefBasicType("int2") Do DefBasicType("int64_t") Do DefBasicType("intptr_t") Do DefBasicType("long") Do DefBasicType("short") Do DefBasicType("size_t") Do DefBasicType("ssize_t") Do DefBasicType("uint2") Do DefBasicType("uint64_t") Do DefBasicType("uintptr_t") Do DefBasicType("unsigned") Do DefBasicType("unsigned-char") Do DefBasicType("unsigned-int") Do DefBasicType("unsigned-long") Do DefBasicType("unsigned-short") Do DefBasicType("void") ; ; Create parse check aid (easy check for "struct" or "union") ; Set sutype("struct")=1 Set sutype("union")=1 ; Do:("SPARC"=gtmhdw) . Set types("boolean_t","type")="int" ; todo - sparc and AIX have boolean_t as a basic type ; ; Define the token types we can experience while ignoring a function pointer's parameter. ; Merge ParmTypes=TypdfTypes ; The GTMTYPEs Set ParmTypes(TKBASETYPE)="" Set ParmTypes(TKCOMMA)="" Set ParmTypes(TKASTERISK)="" Set ParmTypes(TKCONST)="" ; ; Determine if we are doing a pro build or a dbg build. Note that $gtm_dist is set to a verison ; V5.2-000 or later (use of $Z-nonICU function). ; Set gtmexe=$ZTrnlnm("gtm_exe") Set gtmver=$ZPiece(gtmexe,"/",4) Set gtmtyp=$ZPiece(gtmexe,"/",5) Do:(("pro"'=gtmtyp)&("dbg"'=gtmtyp)) Error("INVEXE","F","Invalid $gtm_exe value - verison not pro or dbg") ; ; Read in the type exclude list (types we ignore or otherwise don't need) ; Set infile="gtmexcludetypelist.txt" Open infile:Readonly Use infile For i=1:1 Quit:$ZEof Do . Read line . Quit:$ZEof . Quit:("#"=$ZExtract(line,1,1)) ; Ignore lines starting with "#" . Set excludestructs(line)=1 Close infile ; ; Version/platform dependent excludes ; Do:(("x86"=gtmhdw)!("HP-PA"=gtmhdw)!(PreV53001&(("x86_64"=gtmhdw)!("RS6000"=gtmhdw)))!(PreV53002&("SPARC"=gtmhdw))) . Set excludestructs("int8")=1 ; Not defined on 32 bit platforms . Set excludestructs("uint8")=1 . Set excludestructs("gtm_int8")=1 . Set excludestructs("gtm_timet")=1 ; Epoch dates recognizably typed so we can dumpfhead them to a string . Set excludestructs("gtm_uint8")=1 Do:(("HP-PA"=gtmhdw)!("AXP"=gtmhdw)) ; Eliminate encryption structures . Set excludestructs("gtmcrypt_init_t")=1 . Set excludestructs("gtmcrypt_close_t")=1 . Set excludestructs("gtmcrypt_hash_gen_t")=1 . Set excludestructs("gtmcrypt_encode_t")=1 . Set excludestructs("gtmcrypt_decode_t")=1 . Set excludestructs("gtmcrypt_getkey_by_name_t")=1 . Set excludestructs("gtmcrypt_getkey_by_hash_t")=1 . Set excludestructs("gtmcrypt_strerror_t")=1 . Set excludestructs("gtmcrypt_key_t")=1 . Set excludestructs("muext_hash_hdr")=1 . Set excludestructs("muext_hash_hdr_ptr_t")=1 . Set excludestructs("db_key_map")=1 Do:("HP-PA"=gtmhdw) ; Eliminate trigger structures . Set excludestructs("gvtr_subs_star_t")=1 . Set excludestructs("gvtr_subs_point_t")=1 . Set excludestructs("gvtr_subs_range_t")=1 . Set excludestructs("gvtr_subs_pattern_t")=1 . Set excludestructs("gvtr_subs_t")=1 . Set excludestructs("gv_trigger_t")=1 . Set excludestructs("gvtr_invoke_parms_t")=1 . Set excludestructs("gtm_trigger_parms")=1 . Set excludestructs("gvtr_piece_t")=1 Do:("SPARC"=gtmhdw) . Set excludestructs("zcall_server")=1 . Set excludestructs("zcall_entry")=1 ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Read in the structure list excluding those in the exclude list ; ; Structure and union names have potentially two names that we need to remember and deal with: ; ; 1. An optional "struct|union name" such as "struct gd_region_struct" that has uses in ; other structures, ; 2. The structure/union typedef name at the end of the typedef. ; ; All typedef structures have #2 so that is the key used in our types and structs ; arrays. ; ; Arrays that get setup here: ; ; Initially setup rawtypes() array to record records from the gtmtypelist.txt file. Note that ; duplicate types can found here because we don't do pre-processor #ifdef resolutions in the initial scans ; so types with multiple possible configurations can show up more than once. If we find a duplicate type, ; the type is instead put into the alttypes() array. This gets resolved in a later step where the preprocessor ; gets involved and we can make a definitive choice of which type to use. ; ; rawtypes() array: ; ; rawtypes()= ; ; alttypes() array: ; ; alttypes(,0)= ; alttypes(,)= ; ; Once all gtmtypelist.txt records are read in, the following arrays are created from rawtypes() array ; records and rawtypes() is removed: ; ; types() array: ; ; types()= ; ; When is "struct", the following additional entries are created: ; ; types(),"struct")= - where is the structs array index ; structnames()= - where is the structs array index ; structs() (see structs array below) ; ; When is "union", the following additional entries are created: ; ; types(),"union")= - where is the union array index ; unionnames()= - where is the union array inde ; union() (see union array below) ; Set infile="gtmtypelist.txt" Open infile:Readonly Use infile Set (stridx,typidx,unnidx)=0 For Quit:$ZEof Do . Read line . Quit:$ZEof . Set typename=$ZPiece(line," ",1) . Do:(0=$Data(excludestructs(typename))) . . If (0<$Data(alttypes(typename))) Do . . . ; . . . ; typename already exists in alttypes, add new record there too . . . ; . . . Set atinx=$Increment(alttypes(typename,0)) . . . Set alttypes(typename,atinx)=$ZPiece(line," ",2,3) . . Else If 0<$Data(rawtypes(typename)) Do . . . ; . . . ; typename is already in rawtypes, migrate prev record out of rawtypes into alttypes . . . ; plus move new record to alttypes. . . . ; . . . Set atinx=$Increment(alttypes(typename,0)) . . . Set alttypes(typename,atinx)=rawtypes(typename) . . . Kill rawtypes(typename) . . . Set atinx=$Increment(alttypes(typename,0)) . . . Set alttypes(typename,atinx)=$ZPiece(line," ",2,3) . . Else Do . . . Set rawtypes(typename)=$ZPiece(line," ",2,3) Close infile ; ; Now more fully process the rawtypes into types, structs, and unions ; Set typename="" For Set typename=$Order(rawtypes(typename)) Quit:typename="" Do . Set deftype=$ZPiece(rawtypes(typename)," ",1) . If ("struct"=deftype)!("union"=deftype) Do . . Set types(typename)=deftype . . Set types(typename,"topname")=$ZPiece(rawtypes(typename)," ",2) . Else If ("enum"=deftype) Set TransType(typename)="int" Quit ; Enums become integers later (and thus base types) . Else Do . . ; . . ; We have a simple(r) typedef. Verify 3rd token is same as first with . . ; few exceptions: . . ; . . ; 1. If 3rd token is NULL, ignore it . . ; 2. If 3rd token is "*", ignore it . . ; 3. If 2nd token is "volatile", ignore it so 3rd token becomes 2nd token . . ; . . Set type2=$ZPiece(rawtypes(typename)," ",2) . . Set:("volatile"=deftype) deftype=type2,type2="" . . Do:((""'=type2)&(typename'=type2)&("*"'=type2)) . . . Do DoWrite("Unable to handle the following record: "_typename_" "_rawtypes(typename)_" - Aborting") . . . Halt . . Set types(typename)=deftype Kill rawtypes ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Begin the next phase of processing. Create a C file with every include in it from $gtm_inc plus the appropriate ; system includes to make it all work. The order of most of these includes is fairly important so we take a two-pronged ; approach. We have a list of includes we know we want and the order they should be in. Any additional includes we ; find that are not included in this list gets added to the end. This file is run through the pre-processor, scrubbed with ; an awk strip-mining script to pull the typedefs we want back out of it then we will process that file further. ; ; First, the includes we know we want. Note: If the first char of the include name is "#", then the line is assumed ; to be a pre-processor statement so is added to the output array without further adornment. ; Do AddInclude("mdef.h",TRUE) Do AddInclude("") Do AddInclude("gtm_fcntl.h") Do AddInclude("gtm_inet.h") Do AddInclude("gtm_iconv.h") Do AddInclude("gtm_socket.h") Do AddInclude("gtm_unistd.h") Do AddInclude("gtm_limits.h") Do AddInclude("") Do AddInclude("") Do AddInclude("") Do AddInclude("cache.h") Do:'PreV53004 AddInclude("hashtab_addr.h") Do AddInclude("hashtab_int4.h") Do AddInclude("hashtab_int8.h") Do AddInclude("hashtab_mname.h") Do:'PreV53004 AddInclude("hashtab_str.h") Do AddInclude("hashtab_objcode.h") Do AddInclude("error.h") Do AddInclude("rtnhdr.h") Do AddInclude("gdsroot.h") Do AddInclude("gdskill.h") Do AddInclude("ccp.h") Do AddInclude("gtm_facility.h") Do AddInclude("fileinfo.h") Do AddInclude("gdsbt.h") Do AddInclude("gdsfhead.h") Do AddInclude("v6_gdsfhead.h") Do AddInclude("filestruct.h") Do AddInclude("gdscc.h") Do AddInclude("comline.h") Do AddInclude("compiler.h") Do AddInclude("cmd_qlf.h") Do AddInclude("io.h") Do AddInclude("iosp.h") Do AddInclude("jnl.h") Do AddInclude("lv_val.h") Do:PreV54002 AddInclude("sbs_blk.h") Do:'PreV54002 AddInclude("tree.h") Do AddInclude("mdq.h") Do AddInclude("mprof.h") Do AddInclude("mv_stent.h") Do AddInclude("stack_frame.h") Do AddInclude("stp_parms.h") Do AddInclude("stringpool.h") Do AddInclude("buddy_list.h") Do AddInclude("tp.h") Do AddInclude("tp_frame.h") Do AddInclude("mlkdef.h") Do AddInclude("zshow.h") Do AddInclude("zwrite.h") Do AddInclude("zbreak.h") Do AddInclude("mmseg.h") Do AddInclude("gtmsiginfo.h") Do AddInclude("gtmimagename.h") Do AddInclude("iotcpdef.h") Do AddInclude("gt_timer.h") Do AddInclude("iosocketdef.h") Do AddInclude("ctrlc_handler_dummy.h") Do AddInclude("unw_prof_frame_dummy.h") Do AddInclude("op.h") Do AddInclude("gtmsecshr.h") Do AddInclude("error_trap.h") Do AddInclude("patcode.h") Do AddInclude("source_file.h") Do AddInclude("mupipbckup.h") Do AddInclude("dpgbldir.h") Do AddInclude("mmemory.h") Do AddInclude("have_crit.h") Do:'PreV53004 AddInclude("alias.h") Do AddInclude("repl_msg.h") Do AddInclude("gtmsource.h") Do AddInclude("gtmrecv.h") Do AddInclude("subscript.h") Do AddInclude("lvname_info.h") Do AddInclude("gvname_info.h") Do AddInclude("op_merge.h") Do AddInclude("cli.h") Do AddInclude("invocation_mode.h") Do AddInclude("fgncal.h") Do AddInclude("parse_file.h") Do AddInclude("repl_sem.h") Do:'PreV53003 AddInclude("gtm_zlib.h") Do AddInclude("gdsblk.h") Do AddInclude("jnl_typedef.h") Do AddInclude("gds_blk_upgrade.h") Do AddInclude("cws_insert.h") Do AddInclude("#ifdef UTF8_SUPPORTED") Do AddInclude("gtm_icu_api.h") Do AddInclude("gtm_utf8.h") Do AddInclude("#endif") Do AddInclude("muextr.h") Do AddInclude("#ifdef GTM_CRYPT") Do AddInclude("gtmcrypt.h") Do AddInclude("#endif") Do AddInclude("#ifdef GTM_TRIGGER") Do AddInclude("gv_trigger.h") Do AddInclude("gtm_trigger.h") Do AddInclude("#endif") Do AddInclude("gdsblkops.h") Do AddInclude("cmidef.h") Do AddInclude("cmmdef.h") Do AddInclude("gtcmlkdef.h") Do AddInclude("v15_gdsroot.h") Do AddInclude("v15_gdsbt.h") Do AddInclude("v15_gdsfhead.h") Do AddInclude("gtm_statvfs.h") Do AddInclude("startup.h") Do AddInclude("job.h") Do AddInclude("zroutines.h") Do AddInclude("iottdef.h") Do AddInclude("iomtdef.h") Do AddInclude("read_db_files_from_gld.h") Do AddInclude("nametabtyp.h") Do AddInclude("tp_unwind.h") Do AddInclude("toktyp.h") Do AddInclude("gtm_tls.h") ; This is needed to make sure gtm_repl.h is included *AFTER* gtm_tls.h ; ; List of #includes to ignore (because they get pulled in by other includes and/or are plug-in related or just cause errors) ; Do ExclInclude("ccpact_tab.h") Do ExclInclude("cdb_sc_table.h") Do ExclInclude("devoptionstab.h") Do ExclInclude("emit_code_sp.h") Do ExclInclude("errorsp.h") Do ExclInclude("gdsdbver_sp.h") Do ExclInclude("gtcmtr_protos.h") Do ExclInclude("gtm_build_transport_string.h") Do ExclInclude("gtm_icu.h") Do ExclInclude("gtm_malloc_src.h") Do ExclInclude("gtm_rpc.h") Do ExclInclude("gtm_term.h") Do ExclInclude("gtm_threadgbl.h") Do ExclInclude("gtm_threadgbl_defs.h") Do ExclInclude("gtm_threadgbl_deftypes.h") Do ExclInclude("gtm_threadgbl_init.h") Do ExclInclude("gtmcrypt_dbk_ref.h") Do ExclInclude("gtmcrypt_interface.h") Do ExclInclude("gtmcrypt_pk_ref.h") Do ExclInclude("gtmcrypt_ref.h") Do ExclInclude("gtmcrypt_sym_ref.h") Do ExclInclude("gtmcrypt_util.h") Do ExclInclude("gtm_tls_impl.h") Do ExclInclude("gtmcrypt_funclist.h") Do ExclInclude("gtm_tls_funclist.h") Do ExclInclude("gtmimagetable.h") Do ExclInclude("gtmxc_types.h") Do ExclInclude("gv_trig_cmd_table.h") Do ExclInclude("gv_trigger_protos.h") Do ExclInclude("gvcst_blk_search.h") Do ExclInclude("gvcst_protos.h") Do ExclInclude("hashtab_implementation.h") Do ExclInclude("i386_mod_16.h") Do ExclInclude("i386_mod_32.h") Do ExclInclude("i386_ops.h") Do ExclInclude("i386_ops_2b.h") Do ExclInclude("i386_ops_g1.h") Do ExclInclude("i386_ops_g2.h") Do ExclInclude("i386_ops_g3.h") Do ExclInclude("i386_ops_g4.h") Do ExclInclude("i386_ops_g5.h") Do ExclInclude("i386_ops_g6.h") Do ExclInclude("i386_ops_g7.h") Do ExclInclude("i386_ops_g8.h") Do ExclInclude("i386_reg16.h") Do ExclInclude("i386_reg32.h") Do ExclInclude("i386_reg64.h") Do ExclInclude("i386_reg8.h") Do ExclInclude("i386_ss.h") Do ExclInclude("ia64_inst_struct.h") Do ExclInclude("incr_link_sp.h") Do ExclInclude("indir.h") Do ExclInclude("io_dev_dispatch.h") Do ExclInclude("iop.h") Do ExclInclude("jnl_rec_table.h") Do ExclInclude("jnlsp.h") Do ExclInclude("jobparams.h") Do ExclInclude("jobparamstrs.h") Do ExclInclude("jobsp.h") Do ExclInclude("lockhist.h") Do ExclInclude("make_mode_sp.h") Do ExclInclude("main_pragma.h") Do ExclInclude("mdefsa.h") Do ExclInclude("mdefsp.h") Do ExclInclude("merrors_ansi.h") Do ExclInclude("mu_dwngrd_header.h") Do ExclInclude("muext_rec_table.h") Do ExclInclude("mupip.h") Do ExclInclude("mupip_io_dev_dispatch.h") Do ExclInclude("op_fnextract.h") Do ExclInclude("opcode_def.h") Do ExclInclude("option.h") Do ExclInclude("outofband.h") Do ExclInclude("stp_gcol_src.h") Do ExclInclude("tab_bg_trc_rec.h") Do ExclInclude("tab_db_csh_acct_rec.h") Do ExclInclude("tab_gvstats_rec.h") Do ExclInclude("tab_jpl_trc_rec.h") Do ExclInclude("tab_probecrit_rec.h") Do ExclInclude("tab_ystats_rec.h") Do ExclInclude("timersp.h") Do ExclInclude("trace_table_types.h") Do ExclInclude("trigger_compare_protos.h") Do ExclInclude("trigger_delete_protos.h") Do ExclInclude("trigger_fill_xecute_buffer.h") Do ExclInclude("trigger_parse_protos.h") Do ExclInclude("trigger_select_protos.h") Do ExclInclude("trigger_subs_def.h") Do ExclInclude("trigger_trgfile_protos.h") Do ExclInclude("trigger_update_protos.h") Do ExclInclude("urxsp.h") Do ExclInclude("v010_jnl.h") Do ExclInclude("v3_gdsfhead.h") Do ExclInclude("v07_cdb_sc.h") Do ExclInclude("v07_copy.h") Do ExclInclude("v07_fileinfo.h") Do ExclInclude("v07_filestruct.h") Do ExclInclude("v07_gdsbt.h") Do ExclInclude("v07_gdsfhead.h") Do ExclInclude("v07_gdsroot.h") Do ExclInclude("v07_gtm_facility.h") Do ExclInclude("v07_jnl.h") Do ExclInclude("v07_jnl_rec_table.h") Do ExclInclude("v07_jnlsp.h") Do ExclInclude("v07_mdef.h") Do ExclInclude("v07_mdefsp.h") Do ExclInclude("v12_jnl.h") Do ExclInclude("v12_jnl_rec_table.h") Do ExclInclude("v12_jnlsp.h") Do ExclInclude("v15_filestruct.h") Do ExclInclude("v15_tab_bg_trc_rec_fixed.h") Do ExclInclude("v15_tab_bg_trc_rec_variable.h") Do ExclInclude("v15_tab_db_csh_acct_rec.h") Do ExclInclude("viewtab.h") Do ExclInclude("xfer.h") Do ExclInclude("zbreaksp.h") Do ExclInclude("zcall.h") Do ExclInclude("zroutinessp.h") Do ExclInclude("zsockettab.h") ; ; Version dependent excludes (depends on which versions are shbin/non-shbin for a given release) ; Do:((gtmhdw="x86")!(PreV53001&(gtmhdw="x86_64"))!(PreV53002&(gtmhdw="SPARC"))) . Do ExclInclude("make_mode.h") Do:("HP-PA"=gtmhdw) . Do ExclInclude("trigger.h") ; ; Write our preferred includes to output file ; Set cfile="tmpCFile.c" Open cfile:New Use cfile For i=1:1:inclstmt(0) Do . Write inclstmt(i),! . Write:("#include ""gtmsecshr.h"""=inclstmt(i)) "#undef CLIENT",! ; definition causes problems on Solaris ; ; Make sure all header files added. ; Do CommandToPipe("/bin/ls -1 $gtm_inc/*.h",.results) For i=1:1:results(0) Do . Set incname=$ZPiece(results(i),"/",6) . Quit:(""=incname) ; Sometimes get blank lines at end. . If 0=$Data(exclincl(incname)),0=$Data(inclref(incname)) Do . . Write:("mu_reorg.h"=incname) "#undef DEFAULT",! ; On HPPA, someone defines DEFAULT as 2 which causes this to fail . . Write "#include """_incname_"""",! . . Write:("gtm_mtio.h"=incname) "#undef NOFLUSH",! ; Causes problems on AIX since is defined in sys/ioctl.h Close cfile ; ; Run it through the pre-processor to expand pre-processor statements selecting conditional ; typedefs, etc. Disable all warnings which may occur in older sources and newer compilers ; set cmdToPipe="$gt_cc_"_gtmtyp_" -I$gtm_inc -w -E "_ExtraCcParms_" "_cfile_" > "_cfile_"exp" Do CommandToPipe(cmdToPipe,.results) Do:(0'=results(0)) . Use $P . Do DoWrite("Error messages from pre-processor compile: "_cmdToPipe) . For i=1:1:results(0) Write results(i),! . Do Error("BADPREPCOMP","E","Pre-process compile failed - aborting") ; ; Run the preprocessor output through the stripmine scrubber. This performs the following: ; ; 1. Extracts all typedefs. ; 2. Puts spaces around token chars we need to scan (parens, brackets, etc). ; 3. Eliminates leading white space. ; 4. Translates tabs to spaces and eliminates multiple spaces allowing easy $ZPiece parsing ; Do CommandToPipe("awk -f stripmine.awk "_cfile_"exp > "_cfile_"exp.smine",.results) Do:(0'=results(0)) . Use $P . Do DoWrite("Error messages from awk processing:") . For i=1:1:results(0) Write results(i),! . Do Error("BADPREPCOMP","E","Awk stripmine operation failed - aborting") ; ; Remove tmpCFile.* ; Do:'KeepFiles . Open cfile:Write . Close cfile:Delete . Open cfile_"exp":Write . Close cfile_"exp":Delete ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Read in the expanded stripmined file. Should contain nothing but typedefs in it now to ; reduce our need for parser sophistication. ; Set infile=cfile_"exp.smine" Open infile:Readonly Use infile Set scstate=0 ; Initial scan state (nothing pending) Set (inbuf,token,dirtoken,tokenval,dirtokenval)="" Set inlines=0,tokcnt=0 Do GetToken(FALSE) ; Prime scanner pump Set tokcnt=0 ; Reset counter after pump prime Do GetToken(FALSE) ; ; High level typedef loop - At start of loop, we only ever expect to see typedef statements. ; For Quit:(TKEOF=token) Do . ; . ; Expect a typedef token here . ; . Do:(TKTYPEDEF'=token) Error("ASSERTFAIL","F","Expected token: TKTYPEDEF") . Kill typdf . Do GetToken(FALSE) . ; . ; Possibilities here are: . ; . ; 1. struct . ; 2. union . ; 3. enum (find end of definition and ignore it) . ; 4. volatile (ignored) . ; 5. const (ignored) . ; 6. some known type . ; . ; First get rid of the ignorable conditions . ; . Set isstructuniondef=FALSE,createdisuidx=0 . Do:(TKCONST=token) GetToken(FALSE) . Do:(TKVOLATILE=token) GetToken(FALSE) . If (TKENUM=token) Do IgnoreTypedef(0) Quit ; Ignore and restart loop for this one . ; . ; Now we have a type we need to do something with . ; . If ((TKBASETYPE=token)!(TKOTHERTYPE=token)!(TKGTMTYPE=token)) Do . . Set typdf("type")=tokenval . . Do GetToken(FALSE) . . ; . . ; Possible to have an ignorable const or volatile here too . . ; . . Do:(TKCONST=token) GetToken(FALSE) . . Do:(TKVOLATILE=token) GetToken(FALSE) . Else If ((TKSTRUCT=token)!(TKUNION=token)) Do ; Struct and union processing is identical . . Set typdf("type")=tokenval . . Do GetToken(FALSE) . . ; . . ; Possibilities here are: . . ; . . ; 1. struct name . . ; 2. left brace to start structure definition . . ; . . If ((TKOTHERTYPE=token)!(TKGTMTYPE=token)) Do ; name of struct or union . . . Set typdf("isuname")=tokenval . . . Do GetToken(FALSE) . . . ; . . . ; Syntactic possibilities here having already parsed "struct" or "union" are: . . . ; . . . ; 1. "structunionname type ;" . . . ; 2. "structunionname * type ;" . . . ; 3. Note either of the above could have a "," and one or more types instead of the semi-colon . . . ; 4. "structunionname {" . . . ; 5. "{" . . . ; . . . ; For possibilities 1-3, record the structunionname and fall into end-of-entry processing. For 4-5, record the . . . ; structunion name (if #4) and parse the defined struct or union within the braces. . . . ; . . . Do:(TKLBRACE=token) . . . . Set createdisuidx=$$ParseUnionStructDef(.typdf) . . . . Do GetToken(FALSE) . . . . Set isstructuniondef=TRUE . . Else Do ; We should be defining a new structure here . . . Do:(TKLBRACE'=token) Error("ASSERTFAIL","F","Expecting token TKLBRACE") . . . Set createdisuidx=$$ParseUnionStructDef(.typdf) . . . Do GetToken(FALSE) . . . Set isstructuniondef=TRUE . Else Do Error("ASSERTFAIL","F","Unexpected token encounterd: "_tokenval) . ; . ; At this point we expect the next token to be one or more types that the previous type is to be typedef'd as. . ; It is possible for any of these (potentially comma delimited) type(s) to be preceeded by an asterisk in which . ; case the given type is morphed into an address type instead. Note if the preceeding type was a struct or union . ; definition, there cannot be an address. . ; . Do:((0=$Data(TypdfTypes(token)))&(TKASTERISK'=token)&(TKLPAREN'=token)) . . Do Error("ASSERTFAIL","F","Expecting typedef target type") . Do:((TKASTERISK=token)&isstructuniondef) Error("ASSERTFAIL","F","Address field for structure def") . Do:(TKASTERISK=token) . . Set typdf("origtype")=typdf("type"),typdf("type")="addr" . . Do GetToken(FALSE) . . For Quit:(TKASTERISK'=token) Do GetToken(FALSE) ; eat potential "**" or more . If (TKLPAREN=token) Do ; Have function pointer definition . . Do GetToken(FALSE) . . Do:(TKASTERISK'=token) Error("ASSERTFAIL","F","Expecting TKASTERISK in fuction pointer definition") . . Do GetToken(FALSE) . . Do:(TKVOLATILE=token) GetToken(FALSE) . . Do:(0=$Data(TypdfTypes(token))) Error("ASSERTFAIL","F","Expecting function pointer type") . . Set typdeftype=tokenval . . Do GetToken(FALSE) . . Do:(TKRPAREN'=token) Error("ASSERTFAIL","F","Expecting TKRPAREN in fuction pointer definition") . . Do GetToken(FALSE) . . Do:(TKLPAREN'=token) Error("ASSERTFAIL","F","Expecting TKLPAREN in fuction pointer definition") . . Do GetToken(FALSE) . . For Quit:(TKRPAREN=token) Do ; Have to eat the parms until find a closing paren . . . Do:(0=$Data(ParmTypes(token))) Error("ASSERTFAIL","F","Unexpected token in function pointer parse") . . . Do GetToken(FALSE) . Else Set typdeftype=tokenval . ; . ; If this type is known in types, then we need to remember it. Else we don't.. . ; . Set keeptype=TRUE ; Start with this assumption although we may change our mind if we . ; discover this type is not one of ours . Do:(0=$Data(types(typdeftype))) . . Set keeptype=FALSE ; If not found, change assumption to FALSE unless we find it . . Do:(0'=$Data(alttypes(typdeftype,0))) . . . Set maxloop=alttypes(typdeftype,0) ; Var could disappear inside loop so cache maxval . . . For i=1:1:maxloop Quit:keeptype Do . . . . Do:(typdf("type")=$ZPiece(alttypes(typdeftype,i)," ",1)) . . . . . Set keeptype=TRUE . . . . . Set types(typdeftype)=$ZPiece(alttypes(typdeftype,i)," ",1) . . . . . Do:(("struct"=types(typdeftype))!("union"=types(typdeftype))) . . . . . . Set types(typdeftype,"topname")=$ZPiece(alttypes(typdeftype,i)," ",2) . . . . . ;Do DoWrite("Converted "_typdeftype_" from alttype to type") . . . . . ;Do dbgzwrite("types(typdeftype),alttypes(typdeftype,*),typdeftype","**** Converted type") . . . . . Kill alttypes(typdeftype) . . Quit:(keeptype) . . ; . . ; We don't recognize this type. Put out a temporary message that we are ignoring it and continue . . ; . . Do DoWrite("Did not recognize type for this typedef: "_typdeftype_" - ignoring type and all defs of that type") . . Set keeptype=FALSE; . . For Quit:(TKSEMI=token) Do ; Get rid of everything up to the semi-colon . . . For Quit:(TKASTERISK'=token) Do GetToken(FALSE) . . . If (TKCOLON=token) Do Quit ; Do under before quitting . . . . Do GetToken(FALSE) . . . . Do:(TKINTEGER'=token) Error("ASSERTFAIL","F","Non-numeric bit length") . . . . Do GetToken(FALSE) . . . . Set typdf("nofield")=TRUE . . . Do:((TKOTHERTYPE'=token)&(TKRPAREN'=token)) Error("ASSERTFAIL","F","Unexpected token while ignoring types") . . . Do GetToken(FALSE) . . . Do:(TKCOMMA=token) GetToken(FALSE) . Do:(keeptype) . . ; . . ; Ok, we want to keep this type. . . ; . . Set typdf("newtype")=typdeftype . . Merge types(typdeftype)=typdf ; Add to sum of knowledge for this type . . Do GetToken(FALSE) . . For Quit:(TKCOMMA'=token) Do ; Could be a list of types equated to this definition . . . Do GetToken(FALSE) . . . Do:(0=$Data(TypdfTypes(token))) Error("ASSERTFAIL","F","Expecting typedef target type") . . . ; . . . ; We have a comma delimited type that needs a copy of the typdf entry just created. . . . ; . . . If (TKASTERISK=token) Do . . . . Do:(0=$Data(typdf("origtype"))) ; Avoid if prev type already an address . . . . . Set typdf("origtype")=typdf("type") . . . . . Set typdf("type")="addr" . . . . For Quit:(TKASTERISK'=token) Do GetToken(FALSE) . . . . Do:(0=$Data(TypdfTypes(token))) Error("ASSERTFAIL","F","Expecting typedef target type") . . . Else If (0<$Data(typdf("origtype"))) Do ; Previous type was *'d, this one isn't = unconvert . . . . Set typdf("type")=typdf("origtype") . . . . Kill typdf("origtype") . . . Set typdf("newtype")=tokenval ; Set typedef target type as comma delimited type . . . Do GetToken(FALSE) . . . Merge types(typdf("newtype"))=typdf ; Add to sum of knowledge for this type . Do:(TKSEMI'=token) Error("ASSERTFAIL","F","Expecting end of statement TKSEMI token") . Do GetToken(TRUE) Do:'KeepFiles . Close infile . Open infile:Write . Close infile:Delete Do DoWrite($ZDate($Horolog,"24:60:SS")_" Finished typedef parse - lines: "_inlines_" tokens: "_tokcnt) ; ; Pre-process the types looking for: ; ; 1. Types that can be "promoted" to other simple types. ; 2. If new type is a basic type, set basic flag appropriately. ; 3. For simple struct references (e.g. type is "mstr", pull in the reference if it is known so it ; all fields get expanded appropriately when the structure is sniffed for offset/length info. ; ; First up, promote all the simple types first so they can be properly resolved in structures when ; we do them next. ; Set type="" For Set type=$Order(types(type)) Quit:""=type Do . Set idxcnt=$Get(types(type,"idxcnt"),0) . Do:((0=idxcnt)&(0=$Data(types(type,"topname")))) . . ; Process type define with no structures or unions (simple typedef) . . set errordetail="Type "_type_" is not excluded but neither is its definition found - if is for a plug-in," . . set errordetail=errordetail_" add to excluded types in gtmexcludetypelist.txt" . . set errordetail=errordetail_" or if system dependent exclude - add to excludestructs() in this routine" . . set errordetail=errordetail_" or add a call to ExclInclude() also in this routine" . . Do:(0=($Data(types(type,"type"))#2)) Error("TYPENOTFND","E",errordetail) . . Set types(type,"type")=$$ResolveType(types(type,"type")) ; ; Now promote all the simple types in the isuidx entries ; Set type="" For Set type=$Order(types(type)) Quit:""=type Do . For isuidx=1:1:$Get(types(type,"idxcnt"),0) Do . . For fldidx=1:1:types(type,"isuidx",isuidx,0) Do . . . Set types(type,"isuidx",isuidx,fldidx,"type")=$$ResolveType(types(type,"isuidx",isuidx,fldidx,"type")) ; ; Delete the simple types, they have no more useful information ; Set type="" For Set type=$Order(types(type)) Quit:""=type Do . Kill:(0=$Data(types(type,"isuidx",1,0))) types(type) ; ; Go through the remaining types. For isuidx types that are not simple, see if a type exists exists that we can use ; to redefine this type. We should already be resolved down to the lowest level simple type possible and all simple ; types have now been removed so if we find one, it is a struct or union. If found, substitute its definition in for ; the current one expanding the isuidx entires as necessary. ; Set type="" For Set type=$Order(types(type)) Quit:""=type Do . For isuidx=1:1:types(type,"idxcnt") Do . . For fldidx=1:1:types(type,"isuidx",isuidx,0) Do . . . Set basetypeData=$Data(basetype(types(type,"isuidx",isuidx,fldidx,"type"))) . . . Set typesData=$Data(types(types(type,"isuidx",isuidx,fldidx,"type"))) . . . Do:((0=basetypeData)&(0nextoffset padding(type)=1 . . Set nextoffset=fldoff+fldlen . Else If "susize"=type Do ; Define size for entire structure/union . . Set type=$ZPiece(input,"|",1) . . Set typlen=$ZPiece(input,"|",2) . . Set types(type,"typlen")=typlen . Else Do Error("BADINPUT","F","Line from running routine does not start with a known record type token (field or typsz)") Close pipe Set pipeopen=FALSE If $Data(padding) Do . ; This file is compared with a standard reference by the build tools. New entries are caught. . Set sdfile="struct_padding_warn.out" . Open sdfile:New . Use sdfile . Set next="" . Write "# The below structures have unnecessary paddings",! . Write "# This was figured by a simple fact that the size reported by the structure is greater than the sum " . Write "of the sizes of the elements of the structure",! . Write "# This might be the outer structure itself or one of the inner structures",! . Write "# Use offset.sh to get detailed offsets and sizes of the structures",! . For Set next=$Order(padding(next)) Quit:next="" Write next,! . Close sdfile Set endline=" Finished running and reading results of C offset/size routine ("_lincnt_" lines read)" Do DoWrite($ZDate($Horolog,"24:60:SS")_endline) ; Do:'KeepFiles . Open cfile:Write . Close cfile:Delete . Set cfile=$ZPiece(cfile,".",1) ; Isolate filename . Open cfile_".o":Write . Close cfile_".o":Delete . Open cfile:Write . Close cfile:Delete ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Start of last phase - write out the GTM structure definition file ; Set sdfile="GTMDefinedTypesInit.m" Open sdfile:New For f=sdfile do Prolog(f) Set lincnt=32,(fldcnt,gvstats,strctcnt)=0 Use sdfile ; Write TAB,"Set gtmsdver=""",gtmsdver,"""",! Write TAB,"Set gtmsdtyp=""",gtmtyp,"""",! Write TAB,";",! Set type="" For Set type=$Order(types(type)) Quit:""=type Do . Quit:(0=$Data(types(type,"fullexp"))) ; Ignore any types with no displayable fields (e.g. ModR_M) . Write TAB,";",! . Write TAB,"Set gtmtypes(""",type,""")=""",types(type),"""",! . Write TAB,"Set gtmtypes(""",type,""",0)=",types(type,"fullexp",0),! . Write TAB,"Set gtmtypes(""",type,""",""len"")=",types(type,"typlen"),! . Set lincnt=lincnt+4 . Set strctcnt=strctcnt+1 . Do:(0'=$Data(types(type,"isuname"))) . . Set:("struct"=types(type)) gtmstructs(types(type,"isuname"))=type . . Set:("union"=types(type)) gtmunions(types(type,"isuname"))=type . For fldidx=1:1:types(type,"fullexp",0) Do . . Write TAB,"Set gtmtypes(""",type,""",",fldidx,",""name"")=""",types(type,"fullexp",fldidx,"fldname"),"""",! . . Write TAB,"Set gtmtypes(""",type,""",",fldidx,",""off"")=",types(type,"fullexp",fldidx,"fldoff"),! . . Write TAB,"Set gtmtypes(""",type,""",",fldidx,",""len"")=",types(type,"fullexp",fldidx,"fldlen"),! . . Write TAB,"Set gtmtypes(""",type,""",",fldidx,",""type"")=""",types(type,"fullexp",fldidx,"type"),"""",! . . If "gvstats_rec_t"=type,$Increment(gvstats,types(type,"fullexp",fldidx,"fldlen")) . . Set fld=$ZPiece(types(type,"fullexp",fldidx,"fldname"),".",2,99) ; Eliminate structure (type) header . . ; isolating just the field name . . Write TAB,"Set gtmtypfldindx(""",type,""",""",fld,""")=",fldidx,! . . Set lincnt=lincnt+5 . . Set fldcnt=fldcnt+1 . . Do:(0'=$Data(types(type,"fullexp",fldidx,"flddim"))) . . . Write TAB,"Set gtmtypes(""",type,""",",fldidx,",""dim"")=",types(type,"fullexp",fldidx,"flddim"),! . . . Set lincnt=lincnt+1 ; Write TAB,";",! Write TAB,"; Structure cross reference (struct topname key to retrieve type",! Write TAB,";",! Set lincnt=lincnt+3 Set type="" For Set type=$Order(gtmstructs(type)) Quit:""=type Do . Write TAB,"Set gtmstructs(""",type,""")=""",gtmstructs(type),"""",! Set lincnt=lincnt+1 Write TAB,";",! Write TAB,"; Union cross reference (union topname key to retrieve type",! Write TAB,";",! Set lincnt=lincnt+3 Set type="" For Set type=$Order(gtmunions(type)) Quit:""=type Do . Write TAB,"Set gtmunions(""",type,""")=""",gtmunions(type),"""",! Set lincnt=lincnt+1 ; Write TAB,";",! Write TAB,"Quit",! Set lincnt=lincnt+2 Close sdfile Use $Principal Set endline=" Finished writing GTMDefinedTypesInit.m output file ("_lincnt_" lines written for "_strctcnt Set endline=endline_" structures with "_fldcnt_" fields)" Do DoWrite($ZDate($Horolog,"24:60:SS")_endline) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; All done, close it down.. ; Done Close:pipeopen pipe Use $P Do:debug . Set dumpfile="scantypedefs-DEBUG-zshowdump.txt" . Open dumpfile:New . Use dumpfile . ZShow "*" . Close dumpfile . Use $P Quit ; Prolog(file) copyrightbegin ; set cfile=$ztrnlnm("gtm_tools")_"/copyright.txt" ; If $gtm_tools/copyright.txt does not exist (possible when building older versions), proceed without erroring out set cfileexception="set ecode="""" goto copyrightend^scantypedefs" set xxxx="2010" set yyyy=$zdate($H,"YYYY") open cfile:(readonly:exception=cfileexception) use file write ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;",! for i=1:1 use cfile read line quit:$zeof do . if (1<$zl(line,"XXXX")) do . . set str=$zpiece(line,"XXXX",1)_xxxx_$zpiece(line,"XXXX",2) . . set str=$zpiece(str,"YYYY",1)_yyyy_$zpiece(str,"YYYY",2) . else do . . set str=line . use file w ";"_str_";",! close cfile copyrightend ; use file write ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;",!! Write ";",! Write "; Generated by scantypedefs.m at ",$ZDate($Horolog,"24:60:SS")," on ",$ZDate($Horolog,"YEAR-MM-DD"),! Write "; Build done with GT.M version: ",$ZVersion,! Write ";",! Write "; Environment variables during run:",! Write "; $gtm_dist: ",$ZTrnlnm("gtm_dist"),! Write "; $gtm_exe: ",$ZTrnlnm("gtm_exe"),! Write "; $gtm_src: ",$ZTrnlnm("gtm_src"),! Write "; $gtm_inc: ",$ZTrnlnm("gtm_inc"),! Write ";",! Write "; Note this file should not be manually invoked",! Write ";",! Write TAB,"Write ""GTM-E-GTMSDFILE This routine ("",$TEXT(+0),"") should not be manually invoked"",!",! Write TAB,"Quit",! Write ";",! If sdfile=file Write "; Entry point used by ",$select(sdfile=file:"gtmpcat and others",1:"gdeinit.m")," to define GTM structure fields",! Write ";",! Write "Init",! Write TAB,"; GT.M structure and field definitions",! Quit ; ; Routine to parse a union or struct definition putting the pieces in the passed in array. If/when ; we recurse, a new level is created at the insert point. ; ParseUnionStructDef(typdf) New newidx,fldidx,bracecnt,newfldidx,isstructuniondef,createdisuidx,typdeftype Set newidx=$Increment(typdf("idxcnt")) Do GetToken(FALSE) ; Get past first bracket Set bracecnt=1 ; Will be done (with this level) when this goes back to zero For Quit:(0=bracecnt) Do . ; . ; Processing somewhat mirrors the above parse but without "typedef" preceeding it. . ; . Set isstructuniondef=FALSE,createdisuidx=0 ; Imbedded type not (currently) a struct or a union definition . Do:(TKCONST=token) GetToken(FALSE) ; First check for ignorable tokens . Do:(TKVOLATILE=token) GetToken(FALSE) . If (TKRBRACE=token) Set bracecnt=bracecnt-1 Quit ; Caller takes care of advancing the token . If ((TKBASETYPE=token)!(TKOTHERTYPE=token)!(TKGTMTYPE=token)) Do . . Set fldidx=$Increment(typdf("isuidx",newidx,0)) . . Set typdf("isuidx",newidx,fldidx,"type")=tokenval . . Do GetToken(FALSE) . . ; . . Do:(TKCONST=token) GetToken(FALSE) ; More ignorable possibilities . . Do:(TKVOLATILE=token) GetToken(FALSE) . . ; . . ; We have a base type. Next token could be either the type . . ; we are typedef'd to or could be "*" in which case our type morphs . . ; to an address. . . ; . . Do:(TKASTERISK=token) . . . Set typdf("isuidx",newidx,fldidx,"origtype")=typdf("isuidx",newidx,fldidx,"type") . . . Set typdf("isuidx",newidx,fldidx,"type")="addr" . . . For Quit:(TKASTERISK'=token) Do GetToken(FALSE) . Else If ((TKSTRUCT=token)!(TKUNION=token)) Do ; Struct and union processing is identical . . Set fldidx=$Increment(typdf("isuidx",newidx,0)) . . Set typdf("isuidx",newidx,fldidx,"type")=tokenval . . Do GetToken(FALSE) . . ; . . ; Syntactic possibilities here having already parsed "struct" or "union" are: . . ; . . ; 1. "structunionname type ;" . . ; 2. "structunionname * type ;" . . ; 3. Note either of the above could have a "," and one or more types instead of the semi-colon . . ; 4. "structunionname {" . . ; 5. "{" . . ; . . ; For possibilities 1-3, record the structunionname and fall into end-of-entry processing. For 4-5, record the . . ; structunion name (if #4) and parse the defined struct or union within the braces. . . ; . . If ((TKOTHERTYPE=token)!(TKGTMTYPE=token)) Do ; name of struct or union . . . Set typdf("isuidx",newidx,fldidx,"isuname")=tokenval . . . Do GetToken(FALSE) . . . ; . . . ; Of all the possibilities at this point, the only one we care about is if we have an opening . . . ; brace or not. If yes, off we go to process the struct. If not, fall out to end of item processing. . . . ; . . . Do:(TKLBRACE=token) . . . . Set createdisuidx=$$ParseUnionStructDef(.typdf) . . . . Do GetToken(FALSE) . . . . Set isstructuniondef=TRUE . . Else Do ; We should be defining a new structure here . . . Do:(TKLBRACE'=token) Error("ASSERTFAIL","F","Expecting token TKLBRACE") . . . Set createdisuidx=$$ParseUnionStructDef(.typdf) . . . Set isstructuniondef=TRUE . . . Do GetToken(FALSE) . Else If (TKENUM=token) Do ; Simple enum type - ignore name and consider it an int . . Do GetToken(FALSE) . . Set fldidx=$Increment(typdf("isuidx",newidx,0)) . . Set typdf("isuidx",newidx,fldidx,"type")="int" . . Do GetToken(FALSE) ; Eat enum-type - fall into end type processing . Else Do Error("ASSERTFAIL","F","Unexpected token during struct/union parse:"_tokenval) . ; . ; At this point we expect the next token to be one or more types that the previous type is to be typedef'd as. . ; It is possible for any of these (potentially comma delimited) type(s) to be preceeded by an asterisk in which . ; case the given type is morphed into an address type instead. Note if the preceeding type was a struct or union . ; definition, there cannot be an address. . ; . Do:((0=$Data(TypdfTypes(token)))&(TKASTERISK'=token)&(TKLPAREN'=token)) . . Do Error("ASSERTFAIL","F","Expecting typedef target type") . ; . ; We always record internal types - only thrown away when/if outer typedef is tossed. If this new type was . ; a union or struct, we need to record a pointer to the just created type (whatever typdf("idxcnt") is). . ; . If isstructuniondef Do . . Do:(0=createdisuidx) Error("ASSERTFAIL","F","Created index is 0 after structure create") . . Set typdf("isuidx",newidx,fldidx,"suptr")=createdisuidx ; -> created struct/union def . Else If (TKASTERISK=token) Do . . Set typdf("isuidx",newidx,fldidx,"origtype")=typdf("isuidx",newidx,fldidx,"type") . . set typdf("isuidx",newidx,fldidx,"type")="addr" . . For Quit:(TKASTERISK'=token) Do GetToken(FALSE) . If (TKLPAREN=token) Do ; Have function pointer definition . . Do GetToken(FALSE) . . Do:(TKASTERISK'=token) Error("ASSERTFAIL","F","Expecting TKASTERISK in fuction pointer definition") . . Do GetToken(FALSE) . . Do:(0=$Data(TypdfTypes(token))) Error("ASSERTFAIL","F","Expecting function pointer type") . . Set typdeftype=tokenval . . Do GetToken(FALSE) . . Do:(TKRPAREN'=token) Error("ASSERTFAIL","F","Expecting TKRPAREN in fuction pointer definition") . . Do GetToken(FALSE) . . Do:(TKLPAREN'=token) Error("ASSERTFAIL","F","Expecting TKLPAREN in fuction pointer definition") . . Do GetToken(FALSE) . . For Quit:(TKRPAREN=token) Do ; Have to eat the parms until find a closing paren . . . Do:(0=$Data(ParmTypes(token))) Error("ASSERTFAIL","F","Unexpected token in function pointer parse") . . . Do GetToken(FALSE) . . Set typdf("isuidx",newidx,fldidx,"type")="addr" ; function pointers are address so use that as type . . ; instead of return val type from func . Else Set typdeftype=tokenval . Set typdf("isuidx",newidx,fldidx,"newtype")=typdeftype . Do GetToken(FALSE) . Do:(TKLBRACKET=token) ; Dimension specification (for field) . . Set expr="" . . Do GetToken(FALSE) . . For Quit:(TKRBRACKET=token) Do ; Capture tokens until "]" . . . Set expr=expr_tokenval . . . Do GetToken(FALSE) . . Set typdf("isuidx",newidx,fldidx,"dim")=expr . . Do GetToken(FALSE) . Do:(TKCOLON=token) ; Bit field definition . . Do GetToken(FALSE) . . Do:(TKINTEGER'=token) Error("ASSERTFAIL","F","Non-numeric bit length") . . Do GetToken(FALSE) . . Set typdf("isuidx",newidx,fldidx,"nofield")=TRUE ; This is a bit field so cannot be processed later - marked . For Quit:(TKCOMMA'=token) Do ; Could be a list of comma-delimited types equated to this definition . . Do GetToken(FALSE) . . Do:((0=$Data(TypdfTypes(token)))&(TKASTERISK'=token)) Error("ASSERTFAIL","F","Expecting typedef target type") . . ; . . ; We have a comma delimited type that needs a copy of the typdf entry just created. . . ; . . Set newfldidx=$Increment(typdf("isuidx",newidx,0)) . . Merge typdf("isuidx",newidx,newfldidx)=typdf("isuidx",newidx,fldidx) . . Kill typdf("isuidx",newidx,newfldidx,"dim") ; This copy doesn't (yet) have a dimension . . If (TKASTERISK=token) Do . . . Do:(0=$Data(typdf("isuidx",newidx,newfldidx,"origtype"))) ; Avoid if prev type already an address . . . . Set typdf("isuidx",newidx,newfldidx,"origtype")=typdf("isuidx",newidx,newfldidx,"type") . . . . Set typdf("isuidx",newidx,newfldidx,"type")="addr" . . . For Quit:(TKASTERISK'=token) Do GetToken(FALSE) . . . Do:(0=$Data(TypdfTypes(token))) Error("ASSERTFAIL","F","Expecting typedef target type") . . Else If (0<$Data(typdf("isuidx",newidx,newfldidx,"origtype"))) Do ; Previous type was *'d, this one isn't = unconvert . . . Set typdf("isuidx",newidx,newfldidx,"type")=typdf("isuidx",newidx,newfldidx,"origtype") . . . Kill typdf("isuidx",newidx,newfldidx,"origtype") . . Set typdf("isuidx",newidx,newfldidx,"newtype")=tokenval . . Do GetToken(FALSE) . . If (TKLBRACKET=token) Do ; Dimension specification (for field) . . . Set expr="" . . . Do GetToken(FALSE) . . . For Quit:(TKRBRACKET=token) Do ; Capture tokens until "]" . . . . Set expr=expr_tokenval . . . . Do GetToken(FALSE) . . . Set typdf("isuidx",newidx,newfldidx,"dim")=expr . . . Do GetToken(FALSE) . . If (TKCOLON=token) Do; ; Have another bit field . . . Do GetToken(FALSE) . . . Do:(TKINTEGER'=token) Error("ASSERTFAIL","F","Non-numeric bit length") . . . Do GetToken(FALSE) . . . Set typdf("isuidx",newidx,newfldidx,"nofield")=TRUE ; This is a bit field so cannot be processed later - marked . Do:(TKSEMI'=token) Error("ASSERTFAIL","F","Expecting end of statement TKSEMI token") . Do GetToken(FALSE) ; ; Our parsing is complete after the closing brace for this level. The caller (either us or the main level) ; knows how to deal with the defined type(s) at that level. Return the level we just created. ; Quit newidx ; ; Routine to generate nested queries for nested structures or unions ; ProcessNestedStructUnion(type,isuidx,pfldidx,structname,fieldnameTD) New fieldname,fldidx For fldidx=1:1:types(type,"isuidx",isuidx,0) Do ; Loop thru the fields in this structure . Quit:(0<$Data(types(type,"isuidx",isuidx,fldidx,"nofield"))) ; ignore bitfield or void fields . Set fieldname=types(type,"isuidx",isuidx,fldidx,"newtype") . If (0<$Data(types(type,"isuidx",isuidx,fldidx,"suptr"))) Do . . ; . . ; Have a pointer to a secondary structure. Output the high(er) level structure first, then its fields . . ; . . Do ExtractFieldInfo(type,isuidx,fldidx,structname,fieldnameTD_"."_fieldname) . . If (0<$Data(types(type,"isuidx",isuidx,fldidx,"dim"))) Do . . . Set fldTDparm=fieldnameTD_"."_fieldname_"[0]" . . Else Do . . . Set fldTDparm=fieldnameTD_"."_fieldname . . Do ProcessNestedStructUnion(type,types(type,"isuidx",isuidx,fldidx,"suptr"),fldidx,structname,fldTDparm) . Else Do ExtractFieldInfo(type,isuidx,fldidx,structname,fieldnameTD_"."_fieldname) Quit ; ; Routine to generate the query line into the C program for a given element ; ExtractFieldInfo(type,isuidx,fldidx,structname,fieldname) New fldsgn,dim,fldtyp Set fldsgn=type_"|"_isuidx_"|"_fldidx_"|"_types(type,"isuidx",isuidx,fldidx,"type")_"|"_structname_"."_fieldname_"|" Set fldtyp=types(type,"isuidx",isuidx,fldidx,"type") ; Flexible arrays have no dimension, so dim is just "". Structures without dimension are given an invalid value to let ; us process them differently Set dim=$Get(types(type,"isuidx",isuidx,fldidx,"dim"),-1) Do:'$length(dim) ; Flexible array definition . Write TAB,"PRINT_FLEXBASE(""",fldsgn,""", ",structname,", ",fieldname,", (int)(",dim,"));",! Do:$length(dim) ; Dimension defined . ; Dimension for dimension-less definitions and [unsigned] char types forced to 1 . Set:(dim<0)!(("char"=fldtyp)!("unsigned-char"=fldtyp)) dim=1 . Write TAB,"PRINT_OFFSET(""",fldsgn,""", ",structname,", ",fieldname,", (int)(",dim,"));",! Set lincnt=lincnt+1 Quit ; ; Routine to resolve a given type into its most basic known type (e.g. TID->INTPTR_T->intptr_t) ; ResolveType(type) Quit:(0<$Data(basetype(type))) type ; Fast return if it is a basic type Quit:(0<$Data(TransType(type))) TransType(type) ; Not quite as fast return if it already transformed to lowest type ; ; Type has not yet been transformed to its lowest type so do that now if we can ; If ((0<$Data(types(type)))&(0=$Data(sutype(types(type,"type"))))) Do Quit TransType(type) ; simple (not function pointer) . Do:(type=types(type)) Error("ASSERTFAIL","F","types(type) same as type which can never be") . Set TransType(type)=$$ResolveType(types(type,"type")) ; ; Else we can't resolve it further at this stage - maybe next stage.. Quit type ; ; Find end of typedef as we are going to ignore the rest of this one ; IgnoreTypedef(bracecnt) New seensemi Set seensemi=FALSE For Quit:(seensemi&(0=bracecnt)) Do . Do GetToken(FALSE) . If (TKEOF=token) Do Error("ASSERTFAIL","F","Hit EOF while scanning for end of typedef (IgnoreTypedef)") . Else If ((0=bracecnt)&(TKSEMI=token)) Set seensemi=TRUE . Else If (TKLBRACE=token) Set bracecnt=bracecnt+1 . Else If (TKRBRACE=token) Set bracecnt=bracecnt-1 Do GetToken(FALSE) Quit ; ; Add an include to the structures. Creates two indexes: ; ; inclstmt(0)= - max count ; inclstmt()= - could be #ifdef/#endif ; inclref()= - index into inclstmt ; AddInclude(incl,writeit) New incidx,addquotes,preproc If "<"=$ZExtract(incl,1) Set writeit=TRUE,addquotes=FALSE,preproc=FALSE Else If "#"=$ZExtract(incl,1) Set writeit=TRUE,addquotes=FALSE,preproc=TRUE Else Set addquotes=TRUE,preproc=FALSE Set writeit=$Get(writeit,FALSE) Set:('writeit) writeit=$$StatFile("$gtm_inc/"_incl) Do:(writeit) . Set incidx=$Increment(inclstmt(0)) . Set inclref(incl)=incidx . Set:(addquotes) incl=""""_incl_"""" . Set inclstmt(incidx)=$Select(preproc:"",TRUE:"#include ")_incl Quit ; ; Add an excluded include to the structures ; ; exclincl()="" ; ExclInclude(incl) Set exclincl(incl)="" Quit ; ; Routine to tokenize the input stripmined file. This is a basic tokenizer with the following ; dependencies (which are all part of the stripmine.awk script): ; ; 1. The input file has had spaces put around the tokens it recognizes so parsing can be done ; quickly and efficiently using $ZPiece(). ; 2. White space has been reduced to single spaces, again for parsing efficiency (no tabs or ; multiple consecutive white space. ; 3. We only have to parse typedef statements, not the larger C universe again streamlining ; our parsing. ; 4. First token on a line has no preceeding white space. ; GetToken(EofOk) New done Set done=FALSE If TKEOF=dirtoken Do . ; . ; Simple case where we are out of input . ; . Set token=TKEOF . Set tokenval="" . Set done=TRUE Do:(done&'EofOk) Error("ASSERTFAIL","F","End of file detected in an inappropriate place") Quit:done ; ; Else we need the next token ; Set prevtoken=token Set token=dirtoken Set tokenval=dirtokenval Set:(TKEOF'=token) tokcnt=tokcnt+1 Do:((TKEOF=token)&'EofOk) Error("ASSERTFAIL","F","End of file detected in an inappropriate place") Do:(debugtoken) dbgzwrite("token,tokenval","Called from "_$Stack($Stack(-1)-1,"PLACE")) ; ; Scan to create next director token/val ; Set dirtokenval="" For Quit:((""'=inbuf)!done) Do . If $ZEof Do ; Oops, at EOF with nothing read . . Set dirtoken=TKEOF . . Set done=TRUE . Else Do ; Read a new line - still might detect EOF but might get lucky too! . . Read inbuf . . Set lastreadline=inbuf ; Save original line to parse for debuggingg . . Set:'$ZEof inlines=inlines+1 Quit:done ; Processing already complete - bypass parse scan ; ; Compute the director token ; For Quit:(""'=dirtokenval) Do . Set dirtokenval=$ZPiece(inbuf," ",1) . Set inbuf=$ZExtract(inbuf,$ZLength(dirtokenval)+2,999999) ; Remaining buffer ; ; Decide on the director token type and return ; If (0<$Data(CharScn(dirtokenval))) Set dirtoken=CharScn(dirtokenval) Quit If (0<$Data(Keywd(dirtokenval))) Set dirtoken=Keywd(dirtokenval) Quit If (0<$Data(types(dirtokenval))) Set dirtoken=TKGTMTYPE Quit If (0<$Data(basetype(dirtokenval))) Set dirtoken=TKBASETYPE Quit If (dirtokenval?1.N) Set dirtoken=TKINTEGER Quit Set dirtoken=TKOTHERTYPE Quit ; ; Routine to define basic types (no further redefs of these types). ; ; - basetype(type) - indicates this is a basic type ; DefBasicType(type) Set type=$Translate(type," ","-") ; So types match existing defined types (no imbedded spaces) Set basetype(type)=1 Quit ; ; Build the include string needed to help older GT.M versions find include files in new locations ; https://superuser.com/questions/981780/how-does-gcc-find-the-following-header-file ; iquote() new cmd,i,include,line,pipe,start set pipe="pipe",cmd="cpp -v /dev/null -o /dev/null",start=0 open pipe:(command=cmd)::pipe use pipe for i=1:1 read line(i) quit:($zeof)!(line(i)="End of search list.") do . set:('start)&(line(i)?1"#include "5E1" search starts here:") start=1 . quit:'start . if line(i)?1" /".E set include=$get(include)_" -I"_line(i) close pipe quit:$quit include write include,! quit ; ; Routine to execute a command in a pipe and return the executed lines in an array ; CommandToPipe(cmd,results) New pipecnt,pipe,saveIO Set lastpipecmd=cmd Kill results Set pipe="CmdPipe" Set saveIO=$IO Open pipe:(Shell="/usr/local/bin/tcsh":Command=cmd)::"PIPE" Use pipe Set pipecnt=1 For Read results(pipecnt) Quit:$ZEof Set pipecnt=pipecnt+1 Close pipe Set results(0)=pipecnt-1 Kill results(pipecnt) Use saveIO Quit ; ; Routine to see if a file exists ; StatFile(fname) Open fname:(Readonly:Exception="Set $ECode="""" Quit FALSE") Close fname Quit TRUE ; ; Output error message - generate dump for fatal errors.. ; Error(msgid,severity,text) New zshowdmps Use $P Write !,"SCANTYPEDEFS-",severity,"-",msgid," ",text," from ",$Stack($Stack(-1)-1,"PLACE"),!! Do ;:("F"=severity) . Set zshowdmps=$Increment(ZShowDumpsCreated) . Set dumpfile="scantypedefs-fail.zshowdmp-"_$ZDate($Horolog,"YEARMMDD-2460SS")_"-"_zshowdmps_".txt" . Open dumpfile:New . Use dumpfile . ZShow "*" . Close dumpfile Break:debug Halt ; ; Write to $P saving and restoring $IO ; DoWrite(text) New saveio Set saveio=$IO Use $P Write text,! Use saveio Quit ; ; Routine to enable debugging ; dbgzwrite(zwrarg,sfx) New saveio Set saveio=$IO Use $P Write "DbgZwrite at ",$Stack($Stack-1,"PLACE"),":----------- ",$Select(""'=$Get(sfx,""):"("_sfx_")",TRUE:"")_":",! ZWrite @zwrarg Use saveio Quit ; ; Error trap to record info about what the problem might be.. ; ErrorTrap Use $P Write !!,$ZDate($Horolog,"24:60:SS")," Error Trap has been signaled!",!!! Do Error("ETRAPINVOK","F",$ZStatus) Write !,"SCANTYPEDEFS-F-UNABLECONT Unable to continue after error - halting",!! Halt ; We should never return from Error but if we do.. fis-gtm-V7.0-005/sr_unix/secshr_client.c0000644000032200000250000004557614342376330017053 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_signal.h" #include "gtm_ctype.h" #include "gtm_stdlib.h" /* for EXIT() */ #include "gtm_string.h" #include "gtm_socket.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_stdio.h" #include "gtm_stat.h" #include "gtm_limits.h" #include "gtm_syslog.h" #include "gtm_ipc.h" #include #include #include #include #include #include #include #include "gt_timer.h" #include "gtmio.h" #include "io.h" #include "gtmsecshr.h" #include "gtmimagename.h" #include "iosp.h" #include "error.h" #include "eintr_wrappers.h" #include "util.h" #include "send_msg.h" #include "gtm_un.h" #include "gtmmsg.h" #include "wcs_backoff.h" #include "trans_log_name.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtm_logicals.h" #include "secshr_client.h" #include "hashtab.h" /* for STR_HASH macro */ #include "fork_init.h" #include "gtm_permissions.h" #include "wbox_test_init.h" GBLREF struct sockaddr_un gtmsecshr_sock_name; GBLREF key_t gtmsecshr_key; GBLREF int gtmsecshr_sockpath_len; GBLREF int gtmsecshr_sockfd; GBLREF mstr gtmsecshr_pathname; GBLREF int server_start_tries; GBLREF boolean_t gtmsecshr_sock_init_done; GBLREF uint4 process_id; GBLREF ipcs_mesg db_ipcs; GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF unsigned int gtm_dist_len; GBLREF boolean_t gtm_dist_ok_to_use; LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; LITREF gtmImageName gtmImageNames[]; static int secshr_sem; static boolean_t gtmsecshr_file_check_done; static char gtmsecshr_path[GTM_PATH_MAX]; static volatile boolean_t client_timer_popped; static unsigned long cur_seqno; /* The below messages match up with the gtmsecshr_mesg_type codes */ const static char readonly *secshr_fail_mesg_code[] = { "", "Wake Message Failed", "Remove Semaphore failed", "Remove Shared Memory segment failed", "Remove File failed", "Continue Process failed", "Database Header flush failed", }; /* The below messages match up with gtmsecshr exit codes from gtmsecshr.h. */ const static char readonly *secshrstart_error_code[] = { "", "gtmsecshr unable to set-uid to root", "The environmental variable gtm_dist is pointing to an invalid path", "Unable to start gtmsecshr executable", "gtmsecshr unable to create a child process", "Error with gtmsecshr semaphore", "gtmsecshr already running - invalid invocation", "See syslog for cause of failure", "gtmsecshr startup failed - gtmsecshr unable to chdir to tmp directory", "gtmsecshr startup failed - gtmsecshr unable to determine invocation path", "gtmsecshr startup failed - gtmsecshr not named gtmsecshr", "gtmsecshr startup failed - startup path through $gtm_dist not setup correctly - check path and permissions" }; #define MAX_COMM_ATTEMPTS 4 /* 1 to start secshr, 2 maybe slow, 3 maybe really slow, 4 outside max */ #define CLIENT_ACK_TIMER 5 * MILLISECS_IN_SEC #define START_SERVER \ { \ int arraysize, errorindex; \ \ if (0 != (create_server_status = create_server())) \ { \ assert(ARRAYSIZE(secshrstart_error_code) == (LASTEXITCODE + 1)); \ errorindex = create_server_status; \ if ((0 > errorindex) || (LASTEXITCODE < errorindex)) \ errorindex = LASTEXITCODE; \ assert(0 <= errorindex); \ assert(ARRAYSIZE(secshrstart_error_code) > errorindex); \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_GTMSECSHRSTART, 3, RTS_ERROR_TEXT("Client"), \ process_id, ERR_TEXT, 2, \ RTS_ERROR_STRING(secshrstart_error_code[errorindex])); \ if (FATALFAILURE(create_server_status)) \ { \ gtmsecshr_sock_cleanup(CLIENT); \ return create_server_status; \ } \ /* For transient failures we will continue after printing out message */ \ } \ hiber_start(500); /* half-a-second to allow server to come up */ \ } #define SETUP_FOR_RECV \ { \ recv_ptr = (char *)&mesg; \ recv_len = SIZEOF(mesg); \ client_timer_popped = FALSE; \ recv_complete = FALSE; \ save_errno = 0; \ assert((0 <= CLIENT_ACK_TIMER) && (MAXPOSINT4 >= CLIENT_ACK_TIMER)); \ start_timer(timer_id, CLIENT_ACK_TIMER, client_timer_handler, 0, NULL); \ } error_def(ERR_GTMDISTUNVERIF); error_def(ERR_GTMSECSHR); error_def(ERR_GTMSECSHRPERM); error_def(ERR_GTMSECSHRSOCKET); error_def(ERR_GTMSECSHRSRVF); error_def(ERR_GTMSECSHRSRVFID); error_def(ERR_GTMSECSHRSRVFIL); error_def(ERR_GTMSECSHRSTART); error_def(ERR_GTMSECSHRTMPPATH); error_def(ERR_LOGTOOLONG); error_def(ERR_SYSCALL); error_def(ERR_TEXT); void client_timer_handler(void) { client_timer_popped = TRUE; } int send_mesg2gtmsecshr(unsigned int code, unsigned int id, char *path, int path_len) { int client_sockfd, create_server_status, fcntl_res; int req_code, wait_count = 0; int recv_len, send_len; ssize_t num_chars_recvd, num_chars_sent; int save_errno, ret_code = 0, init_ret_code = 0; int loop_count = 0; int recv_complete, send_complete; boolean_t retry = FALSE; size_t server_proc_len; int semop_res; int selstat, status; char *recv_ptr, *send_ptr; struct sockaddr_un server_proc; struct sembuf sop[4]; fd_set wait_on_fd; gtmsecshr_mesg mesg; TID timer_id; int4 msec_timeout; char *gtm_tmp_ptr; struct stat stat_buf; char file_perm[MAX_PERM_LEN]; struct shmid_ds shm_info; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DBGGSSHR((LOGFLAGS, "secshr_client: New send request\n")); if (!gtm_dist_ok_to_use) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_GTMDISTUNVERIF, 4, STRLEN(gtm_dist), gtm_dist, gtmImageNames[image_type].imageNameLen, gtmImageNames[image_type].imageName); /* Create communication key (hash of release name) if it has not already been done */ if (0 == TREF(gtmsecshr_comkey)) { STR_HASH((char *)gtm_release_name, gtm_release_name_len, TREF(gtmsecshr_comkey), 0); } timer_id = (TID)send_mesg2gtmsecshr; if (!gtmsecshr_file_check_done) { assert(GTM_PATH_MAX >= (gtm_dist_len + 1 + sizeof(GTMSECSHR_EXECUTABLE))); /* Includes null */ memcpy(gtmsecshr_path, gtm_dist, gtm_dist_len); gtmsecshr_path[gtm_dist_len] = '/'; memcpy(gtmsecshr_path + gtm_dist_len + 1, GTMSECSHR_EXECUTABLE, sizeof(GTMSECSHR_EXECUTABLE)); /* Includes null */ gtmsecshr_pathname.addr = gtmsecshr_path; gtmsecshr_pathname.len = (mstr_len_t)(gtm_dist_len + 1 + strlen(GTMSECSHR_EXECUTABLE)); /* Excludes null */ assert((0 < gtmsecshr_pathname.len) && (GTM_PATH_MAX > gtmsecshr_pathname.len)); if (-1 == Stat(gtmsecshr_pathname.addr, &stat_buf)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("stat"), CALLFROM, errno); if ((ROOTUID != stat_buf.st_uid) || !(stat_buf.st_mode & S_ISUID)) { SNPRINTF(file_perm, SIZEOF(file_perm), "%04o", stat_buf.st_mode & PERMALL); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_GTMSECSHRPERM, 5, gtmsecshr_pathname.len, gtmsecshr_pathname.addr, RTS_ERROR_STRING(file_perm), stat_buf.st_uid); } if (0 != ACCESS(gtmsecshr_pathname.addr, (X_OK))) { SNPRINTF(file_perm, SIZEOF(file_perm), "%04o", stat_buf.st_mode & PERMALL); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_GTMSECSHRPERM, 5, gtmsecshr_pathname.len, gtmsecshr_pathname.addr, RTS_ERROR_STRING(file_perm), stat_buf.st_uid, errno); } gtmsecshr_file_check_done = TRUE; } if (!gtmsecshr_sock_init_done && (0 < (init_ret_code = gtmsecshr_sock_init(CLIENT)))) /* Note assignment */ return init_ret_code; DEBUG_ONLY(mesg.usesecshr = TREF(gtm_usesecshr)); /* Flag ignored in PRO build */ while (MAX_COMM_ATTEMPTS >= loop_count) { /* first, try the sendto */ req_code = mesg.code = code; send_len = (int4)(GTM_MESG_HDR_SIZE); if (REMOVE_FILE == code) { assert(GTM_PATH_MAX > path_len); /* Name is not user supplied so simple check */ memcpy(mesg.mesg.path, path, path_len); send_len += path_len; } else if (FLUSH_DB_IPCS_INFO == code) { assert(GTM_PATH_MAX > db_ipcs.fn_len); /* Most of the time file length is much smaller than GTM_PATH_MAX, hence the fn_len + 1 below */ memcpy(&mesg.mesg.db_ipcs, &db_ipcs, (offsetof(ipcs_mesg, fn[0]) + db_ipcs.fn_len + 1)); send_len += offsetof(ipcs_mesg, fn[0]); send_len += mesg.mesg.db_ipcs.fn_len + 1; } else { mesg.mesg.id = id; send_len += SIZEOF(mesg.mesg.id); } DBGGSSHR((LOGFLAGS, "secshr_client: loop %d frm-pid: %d to-pid: %d send_len: %d code: %d\n", loop_count, process_id, id, send_len, code)); mesg.comkey = TREF(gtmsecshr_comkey); /* Version communication key */ mesg.pid = process_id; /* Process id of client */ mesg.seqno = ++cur_seqno; send_ptr = (char *)&mesg; send_complete = FALSE; SENDTO_SOCK(gtmsecshr_sockfd, send_ptr, send_len, 0, (struct sockaddr *)>msecshr_sock_name, (GTM_SOCKLEN_TYPE)gtmsecshr_sockpath_len, num_chars_sent); /* This form handles EINTR internally */ save_errno = errno; DBGGSSHR((LOGFLAGS, "secshr_client: sendto rc: %d errno: %d (only important if rc=-1)\n", (int)num_chars_sent, save_errno)); if (0 >= num_chars_sent) { /* SENDTO_SOCK failed - start server and attempt to resend */ if ((EISCONN == save_errno) || (EBADF == save_errno)) { gtmsecshr_sock_cleanup(CLIENT); gtmsecshr_sock_init(CLIENT); wcs_backoff(loop_count + 1); DBGGSSHR((LOGFLAGS, "secshr_client: Connection error, reset socket\n")); } else { if (0 < loop_count) /* No message unless attempted server start at least once */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(11) ERR_GTMSECSHRSRVF, 4, RTS_ERROR_TEXT("Client"), process_id, loop_count - 1, ERR_TEXT, 2, RTS_ERROR_TEXT("sendto to gtmsecshr failed"), save_errno); START_SERVER; DBGGSSHR((LOGFLAGS, "secshr_client: sendto() failed - restarting server\n")); } loop_count++; continue; } SETUP_FOR_RECV; /* Sets timer, recvcomplete = FALSE */ do { /* Note RECVFROM does not loop on EINTR return codes so must be handled. Note also we only expect * to receive the message header back as an acknowledgement. */ num_chars_recvd = RECVFROM(gtmsecshr_sockfd, recv_ptr, GTM_MESG_HDR_SIZE, 0, (struct sockaddr *)0, (GTM_SOCKLEN_TYPE *)0); save_errno = errno; DBGGSSHR((LOGFLAGS, "secshr_client: recvfrom rc: %d errno: %d (only important if rc=-1)\n", (int)num_chars_recvd, save_errno)); if (0 <= num_chars_recvd) { /* Message received - make sure it is large enough to have set seqno before we do anything * to rely on it. */ if ((GTM_MESG_HDR_SIZE <= num_chars_recvd) && (mesg.seqno == cur_seqno) && (TREF(gtmsecshr_comkey) == mesg.comkey)) recv_complete = TRUE; else { /* Message too short or not correct sequence */ cancel_timer(timer_id); /* Print True/False for the possibilities we failed */ DBGGSSHR((LOGFLAGS, "secshr_client: Message incorrect - chars: %d, seq: %d\n", (GTM_MESG_HDR_SIZE <= num_chars_recvd), (mesg.seqno == cur_seqno))); SETUP_FOR_RECV; continue; } } else { /* Something untoward happened */ if (client_timer_popped) break; if (EINTR == save_errno) /* Had an irrelevant interrupt - ignore */ continue; if (EBADF == save_errno) break; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(11) ERR_GTMSECSHRSRVF, 4, RTS_ERROR_TEXT("Client"), process_id, loop_count - 1, ERR_TEXT, 2, RTS_ERROR_TEXT("recvfrom from gtmsecshr failed"), save_errno); if ((ECONNRESET == save_errno) || (ENOTCONN == save_errno)) { num_chars_recvd = 0; break; } gtmsecshr_sock_cleanup(CLIENT); return save_errno; } } while (!recv_complete); cancel_timer(timer_id); if (client_timer_popped || (EBADF == save_errno) || (0 == num_chars_recvd)) { /* Timeout, connection issues, bad descriptor block - retry */ gtmsecshr_sock_cleanup(CLIENT); gtmsecshr_sock_init(CLIENT); retry = TRUE; if (client_timer_popped) { START_SERVER; DBGGSSHR((LOGFLAGS, "secshr_client: Read timer popped - restarting server\n")); } else DBGGSSHR((LOGFLAGS, "secshr_client: Read error - socket reset, retrying\n")); loop_count++; continue; } /* Response to *our* latest message available */ assert(recv_complete); if (ret_code = mesg.code) /* Warning - assignment */ { DBGGSSHR((LOGFLAGS, "secshr_client: non-zero response from gtmsecshr - request: %d retcode: %d\n", req_code, ret_code)); if (INVALID_COMKEY == ret_code) { /* Comkey mismatch means for a different version of GT.M - we will not handle it */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFIL, 7, RTS_ERROR_TEXT("Client"), process_id, mesg.pid, req_code, RTS_ERROR_TEXT(mesg.mesg.path), ERR_TEXT, 2, RTS_ERROR_STRING("Communicating with wrong GT.M version")); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(13) MAKE_MSG_ERROR(ERR_GTMSECSHRSRVFIL), 7, RTS_ERROR_TEXT("Client"), process_id, mesg.pid, req_code, RTS_ERROR_TEXT(mesg.mesg.path), ERR_TEXT, 2, RTS_ERROR_STRING("Communicating with wrong GT.M version")); break; /* rts_error should not return */ } switch(req_code) { case REMOVE_FILE: /* Called from mutex_sock_init(). Path (and length) contain null terminator byte. * See if file still exists (may have been deleted by earlier attempt). Caller * handles actual error. */ if ((-1 != Stat(path, &stat_buf)) || (ENOENT != ret_code)) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(14) ERR_GTMSECSHRSRVFIL, 7, RTS_ERROR_TEXT("Client"), process_id, mesg.pid, req_code, RTS_ERROR_TEXT(mesg.mesg.path), ERR_TEXT, 2, RTS_ERROR_STRING(secshr_fail_mesg_code[req_code]), mesg.code); else ret_code = 0; /* File is gone so this or a previous try actually worked */ break; case REMOVE_SEM: /* See if semaphore still eixsts (may have been removed by earlier attempt that * got a reply confused or lost). If not there, no error. Else error to op-log. */ if ((-1 != semctl(id, 0, GETVAL)) && !SEM_REMOVED(errno)) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_TEXT("Client"), process_id, mesg.pid, req_code, mesg.mesg.id, ERR_TEXT, 2, RTS_ERROR_STRING(secshr_fail_mesg_code[req_code]), mesg.code); else ret_code = 0; /* File is gone so this or a previous try actually worked */ case REMOVE_SHM: /* See if shmem still eixsts (may have been removed by earlier attempt that * got a reply confused or lost). If not there, no error. Else error to op-log. * Note - */ if ((-1 != shmctl(id, IPC_STAT, &shm_info)) && !SEM_REMOVED(errno)) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_TEXT("Client"), process_id, mesg.pid, req_code, mesg.mesg.id, ERR_TEXT, 2, RTS_ERROR_STRING(secshr_fail_mesg_code[req_code]), mesg.code); else ret_code = 0; /* File is gone so this or a previous try actually worked */ break; case FLUSH_DB_IPCS_INFO: /* Errors handled by caller */ break; default: if (EPERM != mesg.code && EACCES != mesg.code) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(13) ERR_GTMSECSHRSRVFID, 6, RTS_ERROR_TEXT("Client"), process_id, mesg.pid, req_code, mesg.mesg.id, ERR_TEXT, 2, RTS_ERROR_STRING(secshr_fail_mesg_code[req_code]), mesg.code); break; } } break; } if (MAX_COMM_ATTEMPTS < loop_count) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_GTMSECSHRSRVF, 4, RTS_ERROR_TEXT("Client"), process_id, loop_count - 1, ERR_TEXT, 2, RTS_ERROR_TEXT("Unable to communicate with gtmsecshr")); if (FLUSH_DB_IPCS_INFO >= req_code) { if (ret_code) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_TEXT, 2, RTS_ERROR_STRING(secshr_fail_mesg_code[req_code]), ret_code); else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TEXT, 2, RTS_ERROR_STRING(secshr_fail_mesg_code[req_code])); } ret_code = -1; /* If gtm_tmp is not defined, show default path */ if (gtm_tmp_ptr = GETENV("gtm_tmp")) { if (!IS_GTM_IMAGE) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_GTMSECSHRTMPPATH, 2, RTS_ERROR_TEXT(gtm_tmp_ptr), ERR_TEXT, 2, RTS_ERROR_TEXT("(from $gtm_tmp)")); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_GTMSECSHRTMPPATH, 2, RTS_ERROR_TEXT(gtm_tmp_ptr), ERR_TEXT, 2, RTS_ERROR_TEXT("(from $gtm_tmp)")); } else { if (!IS_GTM_IMAGE) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_GTMSECSHRTMPPATH, 2, RTS_ERROR_TEXT("/tmp")); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_GTMSECSHRTMPPATH, 2, RTS_ERROR_TEXT("/tmp")); } } if (ONETIMESOCKET == init_ret_code) gtmsecshr_sock_cleanup(CLIENT); return ret_code; } int create_server(void) { int child_pid, done_pid, status = 0; # ifdef _BSD union wait chld_status; # define CSTAT chld_status # else # define CSTAT status # endif int save_errno; FORK(child_pid); if (0 == child_pid) { process_id = getpid(); /* Do exec using gtmsecshr_path, which was initialize in file check code - send_mesg2gtmsecshr */ if (WBTEST_ENABLED(WBTEST_BADEXEC_SECSHR_PROCESS)) STRCPY(gtmsecshr_path, ""); status = EXECL(gtmsecshr_path, gtmsecshr_path, 0); if (-1 == status) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_GTMSECSHRSTART, 3, RTS_ERROR_TEXT("Client"), process_id, ERR_TEXT, 2, RTS_ERROR_STRING(secshrstart_error_code[UNABLETOEXECGTMSECSHR])); UNDERSCORE_EXIT(UNABLETOEXECGTMSECSHR); } } else { if (-1 == child_pid) { status = GNDCHLDFORKFLD; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_GTMSECSHRSTART, 3, RTS_ERROR_TEXT("Client"), process_id, ERR_TEXT, 2, RTS_ERROR_TEXT("Failed to fork off gtmsecshr"), errno); /* Sleep for a while and hope a subsequent fork will succeed */ hiber_start(1000); } for (; !status ;) { /* To prevent a warning message that the compiler issues */ done_pid = wait(&CSTAT); if (done_pid == child_pid) { status = WEXITSTATUS(CSTAT); break; } else if (-1 == done_pid) { if (ECHILD == errno) /* Assume normal exit status */ break; else if (EINTR != errno) { status = GNDCHLDFORKFLD; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_GTMSECSHRSTART, 3, RTS_ERROR_TEXT("Client"), process_id, ERR_TEXT, 2, RTS_ERROR_TEXT("Error spawning gtmsecshr"), errno); } } } } return status; } fis-gtm-V7.0-005/sr_unix/secshr_client.h0000644000032200000250000000160114342376330017035 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef SECSHR_CLIENT_HANDLER_INCLUDED #define SECSHR_CLIENT_HANDLER_INCLUDED void client_timer_handler(void); int send_mesg2gtmsecshr(unsigned int, unsigned int, char *, int); int create_server(void); #endif /* SECSHR_HANDLER_INCLUDED */ fis-gtm-V7.0-005/sr_unix/send_msg.c0000644000032200000250000001337614342376330016016 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include "gtm_multi_thread.h" #include "gtmmsg.h" #include "error.h" #include "fao_parm.h" #include "util.h" #include "util_out_print_vaparm.h" #include "send_msg.h" #include "caller_id.h" #include "gtmsiginfo.h" /* database/replication related includes due to anticipatory freeze */ #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" /* for gtmsource.h */ #include "gtmsource.h" /* for anticipatory_freeze.h */ #include "anticipatory_freeze.h" /* for SET_ANTICIPATORY_FREEZE_IF_NEEDED */ GBLREF VSIG_ATOMIC_T forced_exit; GBLREF boolean_t caller_id_flag; GBLREF gd_region *gv_cur_region; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF volatile boolean_t timer_in_handler; GBLREF int4 exit_state; #ifdef DEBUG static uint4 nesting_level = 0; #endif /* Skip frame for send_msg/send_msg_csa */ #define PRINT_CALLERID util_out_print(" -- generated from 0x!XJ.", NOFLUSH_OUT, caller_id(1)) void send_msg_va(void *csa, int arg_count, va_list var); /* ** WARNING: For chained error messages, all messages MUST be followed by an fao count; ** ======= zero MUST be specified if there are no parameters. */ /* This routine is a variation on the unix version of rts_error, and has an identical interface */ /* #GTM_THREAD_SAFE : The below function (send_msg) is thread-safe */ void send_msg(int arg_count, ...) { va_list var; sgmnt_addrs *csa; jnlpool_addrs_ptr_t local_jnlpool; /* used by PTHREAD_CSA_FROM_GV_CUR_REGION */ DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; PTHREAD_CSA_FROM_GV_CUR_REGION(csa, local_jnlpool); VAR_START(var, arg_count); send_msg_va(csa, arg_count, var); va_end(var); } /* #GTM_THREAD_SAFE : The below function (send_msg_csa) is thread-safe */ void send_msg_csa(void *csa, int arg_count, ...) { va_list var; VAR_START(var, arg_count); send_msg_va(csa, arg_count, var); va_end(var); } /* #GTM_THREAD_SAFE : The below function (send_msg_va) is thread-safe */ void send_msg_va(void *csa, int arg_count, va_list var) { int dummy, fao_actual, fao_count, i, msg_id, freeze_msg_id; char msg_buffer[PUT_BUFF_SIZE]; mstr msg_string; char *save_util_outptr; va_list save_last_va_list_ptr; boolean_t util_copy_saved = FALSE; boolean_t freeze_needed = FALSE, was_holder; jnlpool_addrs_ptr_t local_jnlpool; /* used by CHECK_IF_FREEZE_ON_ERROR_NEEDED and FREEZE_INSTANCE_IF_NEEDED */ DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; PTHREAD_MUTEX_LOCK_IF_NEEDED(was_holder); /* get thread lock in case threads are in use */ /* Since send_msg uses a global variable buffer, reentrant calls to send_msg will use the same buffer. * Ensure we never overwrite an under-construction send_msg buffer with a nested send_msg call. One * exception to this is if the nested call to send_msg is done by exit handling code in which case the * latest send_msg call prevails and it is ok since we will never return to the original send_msg call * again. The other exception is if enable interrupts in util_out_send_oper results in a new send_msg * in deferred_signal_handler. */ assert((0 == nesting_level) || ((2 > nesting_level) && timer_in_handler) || (EXIT_IMMED == exit_state) || (2 == forced_exit)); DEBUG_ONLY(nesting_level++;) assert(arg_count > 0); ASSERT_SAFE_TO_UPDATE_THREAD_GBLS; if ((NULL != TREF(util_outptr)) && (TREF(util_outptr) != TREF(util_outbuff_ptr))) { SAVE_UTIL_OUT_BUFFER(save_util_outptr, save_last_va_list_ptr, util_copy_saved); } util_out_print(NULL, RESET); for (;;) { msg_id = (int) va_arg(var, VA_ARG_TYPE); CHECK_IF_FREEZE_ON_ERROR_NEEDED(csa, msg_id, freeze_needed, freeze_msg_id, local_jnlpool); --arg_count; msg_string.addr = msg_buffer; msg_string.len = SIZEOF(msg_buffer); gtm_getmsg(msg_id, &msg_string); if (0 < arg_count) { fao_actual = (int) va_arg(var, VA_ARG_TYPE); --arg_count; fao_count = fao_actual; if (fao_count > MAX_FAO_PARMS) { assert(FALSE); fao_count = MAX_FAO_PARMS; } } else fao_actual = fao_count = 0; util_out_print_vaparm(msg_string.addr, NOFLUSH_OUT, var, fao_count); va_end(var); /* need this before used as dest in copy */ VAR_COPY(var, TREF(last_va_list_ptr)); va_end(TREF(last_va_list_ptr)); arg_count -= fao_count; if (0 >= arg_count) { if (caller_id_flag) PRINT_CALLERID; break; } util_out_print("!/", NOFLUSH_OUT); } util_out_print(NULL, OPER); RESTORE_UTIL_OUT_BUFFER(save_util_outptr, save_last_va_list_ptr, util_copy_saved); /* it has been suggested that this would be a place to check a view_debugN * and conditionally enter a "forever" loop on wcs_sleep for unix debugging */ DEBUG_ONLY(nesting_level--;) FREEZE_INSTANCE_IF_NEEDED(csa, freeze_needed, freeze_msg_id, local_jnlpool); PTHREAD_MUTEX_UNLOCK_IF_NEEDED(was_holder); /* release exclusive thread lock if needed */ } fis-gtm-V7.0-005/sr_unix/set_jnl_file_close.c0000644000032200000250000000251514342376330020032 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "gtmmsg.h" #include "wcs_flu.h" GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; uint4 set_jnl_file_close(void) { uint4 jnl_status = 0; cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; jnl_status = jnl_ensure_open(gv_cur_region, cs_addrs); if (0 == jnl_status) { if (0 == cs_addrs->jnl->pini_addr) jnl_write_pini(cs_addrs); wcs_flu(WCSFLU_FLUSH_HDR | WCSFLU_WRITE_EPOCH); jnl_write_pfin(cs_addrs); jnl_file_close(gv_cur_region, TRUE, TRUE); } else gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(6) jnl_status, 4, JNL_LEN_STR(cs_addrs->hdr), DB_LEN_STR(gv_cur_region)); return jnl_status; } fis-gtm-V7.0-005/sr_unix/set_library_path.csh0000644000032200000250000000355214342376330020100 0ustar librarygtc################################################################# # # # Copyright (c) 2010-2022 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. # # # ################################################################# # ########################################################################################### # # set_library_path.csh - setenv LIBPATH and LD_LIBRARY_PATH # # The calling gtm installation script should sould source this script in order to # avoid duplication of 'setenv LD_LIBRARY_PATH'. ########################################################################################### # GT.M generally shouldn't need the library path adjusted, but the below line can be commented # out to enable adjustments. exit if ($HOSTOS == "AIX") then setenv LIBPATH else # Its worth noting that SuSE+RedHat, Debian & Ubuntu handle the lib32 vs lib64 differently # Debian way: /lib 32bit points to /emul/ia32-linux/lib # /lib64 64bit # Debian 7 layout is uniform across archs, but is more annoying than before # /usr/lib/i386-linux-gnu 32bit # /usr/lib/x86_64-linux-gnu 64bit # Ubuntu way: /lib ARCH default points to either lib32 or lib64 # /lib32 32bit # /lib64 64bit # Redhat/SuSE way: /lib 32bit # /lib64 64bit if !($?gtm_inc) then echo "GTM-E-ERROR : gtm_inc not defined!" exit endif if (( -e $gtm_inc/s390.h ) || ( -e $gtm_inc/x86_64.h )) then setenv LD_LIBRARY_PATH "/usr/local/lib64:/usr/local/lib:/usr/lib64:/usr/lib:/usr/lib/x86_64-linux-gnu" else setenv LD_LIBRARY_PATH "/usr/local/lib:/usr/lib32:/usr/lib:/usr/lib/i386-linux-gnu" endif endif fis-gtm-V7.0-005/sr_unix/set_num_additional_processors.c0000755000032200000250000000525414342376330022342 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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_signal.h" /* needed for VSIG_ATOMIC_T */ #ifndef __MVS__ #include #ifdef __hpux #include #else #include "gtm_unistd.h" #endif #else /* __MVS__ */ #define CVT_ADDR 0x10 /* -> to CVT */ #define OFFSET_IN_CVT_OF_CSD_ADDR 0x294 /* CVT+294 -> CSD */ #define OFFSET_IN_CSD_OF_NUM_CPUS_ADDR 0x0a /* CSD+a #cpus online */ #define TYPE_OF_NUM_CPUS short #endif #include "send_msg.h" #include "gtmio.h" #include "have_crit.h" #include "eintr_wrappers.h" #include "set_num_additional_processors.h" #ifdef DEBUG #include "io.h" #include "gtm_stdio.h" #include "wcs_sleep.h" #include "wbox_test_init.h" #include "deferred_signal_handler.h" #endif GBLREF int num_additional_processors; error_def(ERR_NUMPROCESSORS); void set_num_additional_processors(void) { long numcpus; # ifdef __hpux intrpt_state_t prev_intrpt_state; struct pst_dynamic psd; DEFER_INTERRUPTS(INTRPT_IN_SET_NUM_ADD_PROCS, prev_intrpt_state); # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_SYSCONF_WRAPPER == gtm_white_box_test_case_number)) { DBGFPF((stderr, "will sleep indefinitely now\n")); while (TRUE) LONG_SLEEP(60); } # endif if (pstat_getdynamic(&psd, SIZEOF(psd), (size_t)1, 0) == -1) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NUMPROCESSORS); numcpus = 1; } else numcpus = psd.psd_proc_cnt; ENABLE_INTERRUPTS(INTRPT_IN_SET_NUM_ADD_PROCS, prev_intrpt_state); # else # ifdef __MVS__ # ifdef DEBUG DEFER_INTERRUPTS(INTRPT_IN_SET_NUM_ADD_PROCS); if (gtm_white_box_test_case_enabled && (WBTEST_SYSCONF_WRAPPER == gtm_white_box_test_case_number)) { DBGFPF((stderr, "will sleep indefinitely now\n")); while (TRUE) LONG_SLEEP(60); } ENABLE_INTERRUPTS(INTRPT_IN_SET_NUM_ADD_PROCS); # endif numcpus = *(TYPE_OF_NUM_CPUS *)((*(int *)((*(int *)CVT_ADDR) + OFFSET_IN_CVT_OF_CSD_ADDR)) + OFFSET_IN_CSD_OF_NUM_CPUS_ADDR); # else SYSCONF(_SC_NPROCESSORS_ONLN, numcpus); if (numcpus == -1) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NUMPROCESSORS); numcpus = 1; } # endif # endif num_additional_processors = (int)(numcpus - 1); } fis-gtm-V7.0-005/sr_unix/set_zstatus.c0000644000032200000250000001035214342376335016603 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_string.h" #include "gtm_stdio.h" #include "gtm_multi_thread.h" #include "error.h" #include "min_max.h" #include "stringpool.h" #include "mlkdef.h" #include "zshow.h" #include #include "stack_frame.h" #include "mvalconv.h" #include "error_trap.h" #include "trans_code_cleanup.h" #include "util.h" #include "gtmmsg.h" GBLREF mval dollar_zstatus, dollar_zerror; GBLREF stack_frame *zyerr_frame, *frame_pointer; GBLREF mstr *err_act; error_def(ERR_MEMORY); unsigned char *set_zstatus(mstr *src, int max_len, int arg, unsigned char **ctxtp, boolean_t need_rtsloc) { unsigned char *b_line; /* beginning of line (used to restart line) */ mval val; /* pointer to dollar_zstatus */ unsigned char zstatus_buff[2*OUT_BUFF_SIZE]; unsigned char *zstatus_bptr, *zstatus_iter; int save_arg; size_t util_len ; mval *status_loc; boolean_t trans_frame; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; b_line = NULL; if (!need_rtsloc) trans_frame = FALSE; else { /* get the line address of the last "known" MUMPS code that was executed. MUMPS * indirection constitutes MUMPS code that is "unknown" is the sense that there is no * line address for it. */ trans_frame = !(SFT_DM & frame_pointer->type) && ((!(frame_pointer->type & SFT_COUNT || 0 == frame_pointer->type)) || (SFT_ZINTR & frame_pointer->type)); if (trans_frame) { save_arg = arg; SET_ERR_CODE(frame_pointer, arg); } src->len = INTCAST(get_symb_line((unsigned char*)src->addr, max_len, &b_line, ctxtp) - (unsigned char*)src->addr); } MV_FORCE_MVAL(&val, arg); n2s(&val); memcpy(zstatus_buff, val.str.addr, val.str.len); zstatus_bptr = zstatus_buff + val.str.len; *zstatus_bptr++ = ','; if (NULL != b_line) { memcpy(zstatus_bptr, src->addr, src->len); zstatus_bptr += src->len; *zstatus_bptr++ = ','; } zstatus_iter = zstatus_bptr; ASSERT_SAFE_TO_UPDATE_THREAD_GBLS; util_len = TREF(util_outptr) - TREF(util_outbuff_ptr); if (trans_frame) { /* currently no inserted message (arg) needs arguments. The following code needs * to be changed if any new parametered message is added. */ *(TREF(util_outbuff_ptr)) = '-'; memcpy(&zstatus_buff[OUT_BUFF_SIZE], TREF(util_outbuff_ptr), util_len); /* save original message */ util_out_print(NULL, RESET); /* clear any pending msgs and reset util_out_buff */ gtm_putmsg_noflush_csa(CSA_ARG(NULL) VARLSTCNT(1) arg); memcpy(zstatus_bptr, TREF(util_outbuff_ptr), TREF(util_outptr) - TREF(util_outbuff_ptr)); zstatus_bptr += (TREF(util_outptr) - TREF(util_outbuff_ptr)); *zstatus_bptr++ = ','; memcpy(zstatus_bptr, &zstatus_buff[OUT_BUFF_SIZE], util_len); /* restore original message into util_outbuf */ memcpy(TREF(util_outbuff_ptr), &zstatus_buff[OUT_BUFF_SIZE], util_len); *(TREF(util_outbuff_ptr)) = '%'; TREF(util_outptr) = TREF(util_outbuff_ptr) + util_len; arg = save_arg; } else memcpy(zstatus_bptr, TREF(util_outbuff_ptr), util_len); zstatus_bptr += util_len; for (; zstatus_iter < zstatus_bptr; zstatus_iter++) { if ('\n' == *zstatus_iter) *zstatus_iter = ','; } status_loc = (NULL == zyerr_frame) ? &dollar_zstatus : &dollar_zerror; status_loc->str.len = INTCAST(zstatus_bptr - zstatus_buff); status_loc->str.addr = (char *)zstatus_buff; s2pool(&status_loc->str); status_loc->mvtype = MV_STR; /* If this is a MEMORY issue, setting the ecode is of dubious worth since we are not going * to drive any handlers and it can definitely be expensive in terms of memory use as ecode_add() * (further down the pike) is likely to load the text of the module into storage if it can. So we bypass * ecode setting for these two fatal errors. 02/2008 se */ if (ERR_MEMORY != arg) ecode_set(arg); return (b_line); } fis-gtm-V7.0-005/sr_unix/setactive.csh0000755000032200000250000001256014342376330016536 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2020 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. # # # ################################################################# # ####################################################################################################################### # # source setactive.csh - set active GT.M version (aliased to "version") # # Because setactive must be source'd, it cannot take any arguments (in csh). We get around this by requiring the # shell variable setactive_parms to be set to a (possibly empty) list: # # setactive_parms[1] - version # "A" or "a" => use (current) active version (i. e., don't change the version) # "D" or "d" => use current development (volatile) version # "P" or "p" => use current production (fully tested) version # omitted - same as "A" or "a" # # setactive_parms[2] - type of binaries # "B" or "b" => use bta (beta) binaries (optimized with asserts but without debugger information) # "D" or "d" => use dbg (debug) binaries (unoptimized with asserts and debugger information) # "P" or "p" => use pro (production) binaries (optimized without asserts or debugger information) # omitted - use the current type of binary (or P if current type is not known) # # If both arguments are omitted: # setactive will not change anything (no-op). # ####################################################################################################################### set setactive_switchto_ver = "" set setactive_switchto_img = "" if (! $?setactive_parms) then set setactive_parms = ("$1" "$2") endif if ( $#setactive_parms >= 1 ) then set setactive_switchto_ver = $setactive_parms[1]:au endif if ( $#setactive_parms >= 2 ) then set setactive_switchto_img = $setactive_parms[2]:au endif if (! $?gtm_root) then if ($?gtm_dist) then set gtm_root = $gtm_dist:h:h endif endif if (! $?gtm_root) then echo '$gtm_root is not defined and cannot be found using $gtm_dist' exit 1 endif if ($?gtm_dist) then set setactive_current_ver = $gtm_dist:h:t set setactive_current_img = $gtm_dist:t endif # Determine the version to switch to # If nothing is passed or if A is passed, stick to the current version if ( ("" == "$setactive_switchto_ver") || ("A" == "$setactive_switchto_ver") ) then if ($?setactive_current_ver) then set setactive_switchto_ver = "$setactive_current_ver" else echo "SETACTIVE-E-CURRENT : $setactive_switchto_ver - Current Active version not known" exit 1 endif endif # If "P" or "D" is passed, switch to the current production version defined in $gtm_curpro if ( "$setactive_switchto_ver" =~ {P,D} ) then if ($?gtm_curpro) then set setactive_switchto_ver = "$gtm_curpro" else echo "SETACTIVE-E-CURPRO : $setactive_switchto_ver - Current Production version not defined in \$gtm_curpro" exit 1 endif endif # Determine the image to switch to # If nothing is passed, stick to the current image. If the current image is unknown switch to pro if ( "" == "$setactive_switchto_img" ) then if ($?setactive_current_img) then set setactive_switchto_img = "$setactive_current_img" else set setactive_switchto_img = "pro" endif endif set setactive_switchto_img_type = "$setactive_switchto_img:au" switch ($setactive_switchto_img_type) case "P*": set setactive_switchto_img = "pro" breaksw case "D*": set setactive_switchto_img = "dbg" breaksw case "B*": set setactive_switchto_img = "bta" breaksw default: echo "Image type $setactive_switchto_img_type is not known. Will switch to pro" set setactive_switchto_img = "pro" breaksw endsw # Now we know the current version & image and the to be switched to version & image if (! -d $gtm_root/$setactive_switchto_ver) then echo "SETACTIVE-E-NOT_EXIST : $gtm_root/$setactive_switchto_ver does not exist" exit 1 endif # Now we know the to be switched version exists. Setup all the environment variables setenv gtm_ver $gtm_root/$setactive_switchto_ver setenv gtm_verno $setactive_switchto_ver setenv gtm_bta $gtm_ver/bta setenv gtm_dbg $gtm_ver/dbg setenv gtm_pro $gtm_ver/pro setenv gtm_log $gtm_ver/log setenv gtm_exe $gtm_ver/$setactive_switchto_img setenv gtm_dist $gtm_exe setenv gtm_map $gtm_exe/map setenv gtm_obj $gtm_exe/obj setenv gtm_inc $gtm_ver/inc setenv gtm_pct $gtm_ver/pct setenv gtm_src $gtm_ver/src setenv gtm_tools $gtm_ver/tools # The below environment variable is set only for versions V63011 and earlier. # This can be removed once there are no V63011 and prior versions. setenv gtm_vrt $gtm_ver # unset environment variables set by prior versions. We will not set them going forward unsetenv gtm_misc gtm_tags gtm_lint # Replacing all $gtm_dist in gtmroutines to point to the new $gtm_dist is tricky. # Simply removing all $gtm_dist now and adding them later changes the order and be unexpected. # So, just reset gtmroutines. (Like how any changes to gt_cc* are simply lost when version is switched.) setenv gtmroutines "." source $gtm_tools/gtmsrc.csh unset setactive_switchto_ver setactive_switchto_img setactive_switchto_img_type setactive_current_ver setactive_current_img unset setactive_parms fis-gtm-V7.0-005/sr_unix/setterm.c0000644000032200000250000000561714342376330015701 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_termios.h" #include "gtm_signal.h" /* for SIGPROCMASK used inside Tcsetattr */ #include "io.h" #include "iosp.h" #include "iottdef.h" #include "gtmio.h" #include "eintr_wrappers.h" #include "setterm.h" #include "gtm_isanlp.h" error_def(ERR_TCSETATTR); void setterm(io_desc *ioptr) { int status; int save_errno; struct termios t; d_tt_struct *tt_ptr; tt_ptr = (d_tt_struct *) ioptr->dev_sp; t = *tt_ptr->ttio_struct; if (tt_ptr->canonical) { t.c_lflag &= ~(ECHO); t.c_lflag |= ICANON; }else { t.c_lflag &= ~(ICANON | ECHO); t.c_cc[VTIME] = 8; t.c_cc[VMIN] = 1; } t.c_iflag &= ~(ICRNL); Tcsetattr(tt_ptr->fildes, TCSANOW, &t, status, save_errno); if (0 != status) { if (gtm_isanlp(tt_ptr->fildes) == 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_TCSETATTR, 1, tt_ptr->fildes, save_errno); } return; } /* These routines are here because it is frightfully important to keep them in synch with setterm. When they get out of line, a r x:0 causes your terminal to be unreachable thereafter. */ /* iott_mterm sets the inter-character timer (t.c_cc[VTIME]) to 0.0 seconds so that a read with a zero timeout (ie. Read x:0) will not wait. */ void iott_mterm(io_desc *ioptr) { int status; int save_errno; struct termios t; d_tt_struct *tt_ptr; tt_ptr = (d_tt_struct *) ioptr->dev_sp; t = *tt_ptr->ttio_struct; if (tt_ptr->canonical) { t.c_lflag &= ~(ECHO); t.c_lflag |= ICANON; }else { t.c_lflag &= ~(ICANON | ECHO); #ifdef __MVS__ t.c_cc[VTIME] = 1; #else t.c_cc[VTIME] = 0; #endif t.c_cc[VMIN] = 0; } t.c_iflag &= ~(ICRNL); Tcsetattr(tt_ptr->fildes, TCSANOW, &t, status, save_errno); if (0 != status) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TCSETATTR, 1, tt_ptr->fildes, save_errno); return; } /* iott_rterm restores the inter-character timer (t.c_cc[VTIME]) to 0.8 seconds */ void iott_rterm(io_desc *ioptr) { int status; int save_errno; struct termios t; d_tt_struct *tt_ptr; tt_ptr = (d_tt_struct *) ioptr->dev_sp; t = *tt_ptr->ttio_struct; if (tt_ptr->canonical) { t.c_lflag &= ~(ECHO); t.c_lflag |= ICANON; }else { t.c_lflag &= ~(ICANON | ECHO); t.c_cc[VTIME] = 8; t.c_cc[VMIN] = 1; } t.c_iflag &= ~(ICRNL); Tcsetattr(tt_ptr->fildes, TCSANOW, &t, status, save_errno); if (0 != status) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TCSETATTR, 1, tt_ptr->fildes, save_errno); return; } fis-gtm-V7.0-005/sr_unix/sgtm_putmsg.c0000755000032200000250000000513714342376330016567 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_string.h" #include "gtm_stdio.h" #include #include "gtm_multi_thread.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtmmsg.h" #include "error.h" #include "fao_parm.h" #include "util.h" #include "util_out_print_vaparm.h" #include "sgtm_putmsg.h" /* ** WARNING: For chained error messages, all messages MUST be followed by an fao count; ** ======= zero MUST be specified if there are no parameters. * This routine is a variation on the unix version of rts_error, and has an identical interface. */ void sgtm_putmsg(char *out_str, size_t out_str_len, ...) { va_list var; int arg_count, dummy, fao_actual, fao_count, i, msg_id; char msg_buffer[OUT_BUFF_SIZE]; mstr msg_string; size_t util_outbufflen; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; VAR_START(var, out_str_len); arg_count = va_arg(var, int); assert(arg_count > 0); ASSERT_SAFE_TO_UPDATE_THREAD_GBLS; util_out_print(NULL, RESET); for (;;) { msg_id = (int) va_arg(var, VA_ARG_TYPE); --arg_count; msg_string.addr = msg_buffer; msg_string.len = SIZEOF(msg_buffer); gtm_getmsg(msg_id, &msg_string); if (arg_count > 0) { fao_actual = (int) va_arg(var, VA_ARG_TYPE); --arg_count; fao_count = fao_actual; if (fao_count > MAX_FAO_PARMS) { assert(FALSE); fao_count = MAX_FAO_PARMS; } } else fao_actual = fao_count = 0; util_out_print_vaparm(msg_string.addr, NOFLUSH_OUT, var, fao_count); va_end(var); /* need before using as dest in va_copy */ VAR_COPY(var, TREF(last_va_list_ptr)); va_end(TREF(last_va_list_ptr)); arg_count -= fao_count; if (0 >= arg_count) break; util_out_print("!/", NOFLUSH_OUT); } va_end(var); util_out_print(NULL, SPRINT); util_outbufflen = STRLEN(TREF(util_outbuff_ptr)); out_str_len -= 2; // for the trailing \n and \0 out_str_len = MIN(out_str_len, util_outbufflen); memcpy(out_str, TREF(util_outbuff_ptr), out_str_len); out_str[out_str_len] = '\n'; out_str[out_str_len + 1] = '\0'; } fis-gtm-V7.0-005/sr_unix/show_install_config.sh0000644000032200000250000000337014342376330020433 0ustar librarygtc#!/bin/sh ################################################################# # # # Copyright 2012 Fidelity Information Services, Inc # # # # This source code contains the intellectual property # # of its copyright holder(s), and is made available # # under a license. If you do not know the terms of # # the license, please stop and do not read further. # # # ################################################################# if [ "x" = x"$gtm_dist" ]; then echo "Environment variable - gtm_dist not defined." exit 1 fi if [ ! -d $gtm_dist/plugin ]; then echo "Unable to locate $gtm_dist/plugin. Exiting..." exit 1 fi platform_name=`uname -s` ext=".so" if [ "OS/390" = $platform_name ]; then ext=".dll" ; fi base_libname="libgtmcrypt" generic_libname=$base_libname$ext if [ "x" = x"$gtm_crypt_plugin" ]; then shared_object="$gtm_dist/plugin/$generic_libname" txt="symbolic link pointed by $gtm_dist/plugin/$generic_libname" else shared_object="$gtm_dist/plugin/$gtm_crypt_plugin" txt='$gtm_crypt_plugin' fi if [ ! -f $shared_object ] ; then echo "Cannot find $shared_object. Exiting..." exit 1 fi # Obtain the symbolic link (if any) link=`ls -l $shared_object | awk '{print $NF}'` # Get rid of the prefix (any path associated with the link) and the extension basepart=`echo $link | awk -F/ '{print $NF}' | sed 's/'"$ext"'$//'` # Resulting $basepart should be of form -- A_B_C encryption_lib=`echo $basepart | cut -f2 -d'_'` algorithm=`echo $basepart | cut -f3 -d'_'` if [ "$encryption_lib" = "$algorithm" -o "" = "$algorithm" ] ; then echo "Unable to determine encryption library name or algorithm. Please ensure that $txt has the correct format. Exiting..." exit 1 fi echo "ENCRYPTION_LIB = $encryption_lib" echo "ALGORITHM = $algorithm" exit 0 fis-gtm-V7.0-005/sr_unix/sig_init.c0000755000032200000250000001013514342376330016015 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ /* Define handling of ALL signals. Most are now ignored but nothing should interrupt us * without our knowledge and a handler defined. */ #include "mdef.h" #include "gtm_string.h" #include #include "continue_handler.h" #include "sig_init.h" #include "gtmci_signals.h" #ifdef GTM_PTHREAD GBLREF boolean_t gtm_jvm_process; #endif void null_handler(int sig); void sig_init(void (*signal_handler)(), void (*ctrlc_handler)(), void (*suspsig_handler)(), void (*continue_handler)()) { struct sigaction ignore, null_action, def_action, susp_action, gen_action, ctrlc_action, cont_action; int sig; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; memset(&ignore, 0, SIZEOF(ignore)); sigemptyset(&ignore.sa_mask); /* Copying only up to susp_action here to later specify SA_SIGINFO flag and continue copying with it. */ null_action = def_action = susp_action = ignore; ignore.sa_handler = SIG_IGN; null_action.sa_handler = null_handler; def_action.sa_handler = SIG_DFL; /* Give us extra info on the following signals and a full core if necessary. */ susp_action.sa_flags = SA_SIGINFO; gen_action = ctrlc_action = cont_action = susp_action; susp_action.sa_sigaction = suspsig_handler; gen_action.sa_sigaction = signal_handler; ctrlc_action.sa_sigaction = ctrlc_handler; cont_action.sa_sigaction = continue_handler; for (sig = 1; sig <= NSIG; sig++) { switch (sig) { case SIGHUP: /* Tandem hack: rather than ignore SIGHUP, we must catch it and do nothing with the signal. This * prevents ioctls on modem/tty devices from hanging when carrier drops during the system call. */ sigaction(sig, &null_action, NULL); break; case SIGCLD: /* Default handling necessary for SIGCLD signal. CAUTION: consider the affect on JOB (timeout) * implementation before changing this behavior (like defuncts, ECHILD errors, etc.). */ sigaction(sig, &def_action, NULL); break; case SIGTSTP: case SIGTTIN: case SIGTTOU: /* These are all signals that suspend a process. */ if (NULL != suspsig_handler) sigaction(sig, &susp_action, NULL); else sigaction(sig, &ignore, NULL); break; case SIGINT: /* If supplied with a control-C handler, install it now. */ if (NULL != ctrlc_handler) sigaction(sig, &ctrlc_action, NULL); else sigaction(sig, &ignore, NULL); break; case SIGCONT: /* Special handling for SIGCONT. */ if (NULL != continue_handler) { # ifndef DISABLE_SIGCONT_PROCESSING if (FALSE == TREF(disable_sigcont)) sigaction(SIGCONT, &cont_action, NULL); # else TREF(disable_sigcont) = TRUE; # endif } else { sigaction(sig, &ignore, NULL); TREF(disable_sigcont) = TRUE; } break; case SIGSEGV: # ifdef GTM_PTHREAD if (gtm_jvm_process) break; # endif case SIGABRT: # ifdef GTM_PTHREAD if (gtm_jvm_process) break; # endif case SIGBUS: # ifdef _AIX case SIGDANGER: # endif case SIGFPE: # ifdef __MVS__ case SIGABND: # else /* On Linux SIGIOT is commonly same as SIGABRT, so to avoid duplicate cases, check for that. */ # if !defined(__CYGWIN__) && defined (SIGIOT) && (SIGIOT != SIGABRT) case SIGIOT: # endif # ifndef __linux__ case SIGEMT: # endif # endif case SIGILL: case SIGQUIT: # ifndef __linux__ case SIGSYS: # endif case SIGTERM: case SIGTRAP: /* These are all being handled by the generic_signal_handler. */ sigaction(sig, &gen_action, NULL); break; default: sigaction(sig, &ignore, NULL); } } } /* Provide null signal handler */ void null_handler(int sig) { /* */ } fis-gtm-V7.0-005/sr_unix/sig_init.h0000755000032200000250000000133214342376330016021 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef SIG_INIT_H_INCLUDED #define SIG_INIT_H_INCLUDED void sig_init(void (*signal_handler)(), void (*ctrlc_handler)(), void (*suspsig_handler)(), void (*continue_handler)()); void null_handler(int sig); #endif fis-gtm-V7.0-005/sr_unix/simple_timeout_timer.c0000644000032200000250000000224114342376330020443 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016 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 "gt_timer.h" #include "error.h" /* This is a timer routine used by the TIMEOUT_INIT() macro. * When fired it simply sets the referenced timedout value to TRUE. */ void simple_timeout_timer(TID tid, int4 hd_len, boolean_t **timedout) { **timedout = TRUE; } /* This is a condition handler established by the TIMEOUT_INIT() macro and reverted by the TIMEOUT_DONE() macro. * The real cleanup work is done in the TIMEOUT_INIT() code, as it has the necessary context, so this handler * simply does an unwind so that control can return to the establishment point. */ CONDITION_HANDLER(timer_cancel_ch) { START_CH(TRUE); UNWIND(NULL, NULL); } fis-gtm-V7.0-005/sr_unix/sleep.c0000755000032200000250000000210114342376330015312 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_time.h" #include #include #include "sleep.h" #ifdef __MVS__ /* OS390 versions must use usleep */ # include "gtm_unistd.h" #endif /* Formerly a separate sleep high performance mechanism that is now a wrapper around * SLEEP_USEC in sleep.h * * Input: * useconds - sleep duration in microseconds. Callers frequently sleep in * terms of milliseconds and so multiply the input parameter by * 1000 to turn milliseconds into microseconds */ void m_usleep(int useconds) { SLEEP_USEC(useconds, FALSE); } fis-gtm-V7.0-005/sr_unix/sleep.h0000755000032200000250000002054214342376330015330 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #ifndef SLEEP_H #define SLEEP_H /* Note: GT.M code *MUST NOT* use the sleep function because it causes problems with GT.M's timers on some platforms. Specifically, * the sleep function results in SIGARLM handler being silently deleted on Solaris systems (through Solaris 9 at least). This leads * to lost timer pops and has the potential for system hangs. The proper long sleep mechanism is hiber_start which can be accessed * through the LONG_SLEEP macro defined in mdef.h. * * On Linux boxes be sure to define USER_HZ macro (in gt_timers.c) appropriately to mitigate the timer clustering imposed by * the OS. Historically, the USER_HZ value has defaulted to 100 (same as HZ), thus resulting in at most 10ms accuracy when * delivering timed events. */ /* SLEEP_USEC wrapper, see sleep.c for more information */ void m_usleep(int useconds); #if !defined(_AIX) && !defined(__linux__) && !defined(__MVS__) && !defined(__CYGWIN__) # error "Unsure of support for sleep functions on this platform" #endif /* Where possible we use clock_nanosleep and clock_gettime, which, while currently no faster than gettimeofday(), do eventually * promise sub-millisecond accuracy. */ #define CLOCK_NANOSLEEP(CLOCKID, SECONDS, NANOSECONDS, RESTART) \ MBSTART { \ int STATUS; \ struct timespec REQTIM; \ \ assert(0 <= (SECONDS)); \ assert((0 <= (NANOSECONDS)) && (E_9 > (NANOSECONDS))); \ clock_gettime(CLOCKID, &REQTIM); \ REQTIM.tv_sec += (long)(SECONDS); \ REQTIM.tv_nsec += (long)(NANOSECONDS); \ if (NANOSECS_IN_SEC <= REQTIM.tv_nsec) \ { \ REQTIM.tv_sec++; \ REQTIM.tv_nsec -= NANOSECS_IN_SEC; \ } \ do \ { \ STATUS = clock_nanosleep(CLOCKID, TIMER_ABSTIME, &REQTIM, NULL); \ if (!RESTART || (0 == STATUS)) \ break; \ assert(EINTR == STATUS); \ } while (TRUE); \ } MBEND /* For most UNIX platforms a combination of nanosleep() and gettimeofday() proved to be the most supported, accurate, and * operationally sound approach. Alternatives for implementing high-resolution sleeps include clock_nanosleep() and nsleep() */ #define SET_EXPIR_TIME(NOW_TIMEVAL, EXPIR_TIMEVAL, SECS, USECS) \ MBSTART { \ gettimeofday(&(NOW_TIMEVAL), NULL); \ if (E_6 <= ((NOW_TIMEVAL).tv_usec + USECS)) \ { \ (EXPIR_TIMEVAL).tv_sec = (NOW_TIMEVAL).tv_sec + (SECS) + 1; \ (EXPIR_TIMEVAL).tv_usec = (NOW_TIMEVAL).tv_usec + (USECS) - E_6; \ } else \ { \ (EXPIR_TIMEVAL).tv_sec = (NOW_TIMEVAL).tv_sec + (SECS); \ (EXPIR_TIMEVAL).tv_usec = (NOW_TIMEVAL).tv_usec + (USECS); \ } \ } MBEND /* This macro *does not* have surrounding braces and *will break* out of the block it is in on non-positive remaining time. */ #define UPDATE_REM_TIME_OR_BREAK(NOW_TIMEVAL, EXPIR_TIMEVAL, SECS, USECS) \ gettimeofday(&(NOW_TIMEVAL), NULL); \ if (((NOW_TIMEVAL).tv_sec > (EXPIR_TIMEVAL).tv_sec) \ || (((NOW_TIMEVAL).tv_sec == (EXPIR_TIMEVAL).tv_sec) \ && ((NOW_TIMEVAL).tv_usec >= (EXPIR_TIMEVAL).tv_usec))) \ break; \ if ((EXPIR_TIMEVAL).tv_usec < (NOW_TIMEVAL).tv_usec) \ { \ SECS = (time_t)((EXPIR_TIMEVAL).tv_sec - (NOW_TIMEVAL).tv_sec - 1); \ USECS = (int)(E_6 + (EXPIR_TIMEVAL).tv_usec - (NOW_TIMEVAL).tv_usec); \ } else \ { \ SECS = (time_t)((EXPIR_TIMEVAL).tv_sec - (NOW_TIMEVAL).tv_sec); \ USECS = (int)((EXPIR_TIMEVAL).tv_usec - (NOW_TIMEVAL).tv_usec); \ } \ /* Because, as of this writing, in AIX the clock_* routines are so erratic with short times we use the functions mentioned above * for most things but give the following macro a separate name so AIX can use it in op_hang.c to ensure that a 1 second sleep * always puts the process in a different second as measured by $HOROLOG and the like. */ #define MICROSECOND_SLEEP_with_NANOSECOND_SLEEP(USECONDS, RESTART) \ MBSTART { \ int status, usecs, save_errno; \ struct timespec req; \ struct timeval now, expir; \ \ assert(0 < (USECONDS)); \ req.tv_sec = (time_t)((USECONDS) / E_6); \ req.tv_nsec = (long)((usecs = (USECONDS) % E_6) * 1000); /* Assignment! */ \ assert(E_9 > req.tv_nsec); \ /* A little wasteful for the non-restart case */ \ SET_EXPIR_TIME(now, expir, req.tv_sec, usecs); \ do \ { /* This macro will break the loop when it is time. */ \ status = nanosleep(&req, NULL); \ if (!(RESTART) || (0 == status)) \ break; \ UPDATE_REM_TIME_OR_BREAK(now, expir, req.tv_sec, usecs); \ req.tv_nsec = (long)(usecs * 1000); \ assert(EINTR == (save_errno = errno)); /* inline assignment */ \ } while (TRUE); \ } MBEND /* On z/OS neither clock_nanosleep nor nanosleep is available, so use a combination of sleep, usleep, and gettimeofday instead. * Since we do not have a z/OS box presently, this implementation has not been tested, and so it likely needs some casts at the very * least. Another note is that sleep is unsafe to mix with timers on other platforms, but on z/OS the documentation does not mention * any fallouts, so this should be verified. If it turns out that sleep is unsafe, we might have to use pthread_cond_timewait or * call usleep (which, given that we have used it on z/OS before, should be safe) in a loop. * Due to the above stated limitations the minimum sleep on z/OS is 1 Usec * cywin is a mystery so assume the worst */ #define MICROSECOND_SLEEP_with_SLEEP_and_USLEEP(USECONDS, RESTART) \ MBSTART { \ int secs, interrupted; \ useconds_t usecs; \ struct timeval now, expir; \ \ assert(0 < (USECONDS)); \ secs = (USECONDS) / E_6; \ usecs = (USECONDS) % E_6; \ SET_EXPIR_TIME(now, expir, secs, usecs); \ do \ { \ /* Sleep for seconds first */ \ interrupted = sleep(secs); /* BYPASSOK */ \ if (interrupted && !(RESTART)) \ break; \ /* This macro will break the loop when it is time. */ \ UPDATE_REM_TIME_OR_BREAK(now, expir, secs, usecs); \ /* Recalculate time and sleep for remaining microseconds */ \ interrupted = usleep(usecs); /* BYPASSOK */ \ if (interrupted && !(RESTART)) \ break; \ /* This macro will break the loop when it is time. */ \ UPDATE_REM_TIME_OR_BREAK(now, expir, secs, usecs); \ } while ((0 < secs) || (0 < usecs)); \ } MBEND #if defined(__MVS__) || defined(__CYGWIN__) /* z/OS and Cygwin use the lowest quality sleep options. Sub-millisecond nanosecond sleeps round up to 1 millisecond */ # define SLEEP_USEC(USECONDS, RESTART) MICROSECOND_SLEEP_with_SLEEP_and_USLEEP(USECONDS, RESTART) # define NANOSLEEP(NANOSECONDS, RESTART) SLEEP_USEC((1000 > (NANOSECONDS)) ? 1 : ((NANOSECONDS) / 1000), RESTART); #elif defined(_AIX) /* Because of unreliability, AIX uses plain old nanosleep(). Sub-millisecond nanosecond sleeps round up to 1 millisecond */ # define SLEEP_USEC(USECONDS, RESTART) MICROSECOND_SLEEP_with_NANOSECOND_SLEEP(USECONDS, RESTART) # define NANOSLEEP(NANOSECONDS, RESTART) SLEEP_USEC((1000 > (NANOSECONDS)) ? 1 : ((NANOSECONDS) / 1000), RESTART); #else /* All other platforms use high performance sleeps with CLOCK_NANOSLEEP */ # define SLEEP_USEC(USECONDS, RESTART) \ MBSTART { \ time_t seconds = (USECONDS) / E_6; \ time_t nanoseconds = (USECONDS % E_6) * NANOSECS_IN_USEC; \ \ CLOCK_NANOSLEEP(CLOCK_MONOTONIC, seconds, nanoseconds, RESTART); \ } MBEND # define NANOSLEEP(NANOSECONDS, RESTART) \ MBSTART { \ time_t seconds = (NANOSECONDS) / E_9; \ time_t nanoseconds = (NANOSECONDS % E_9); \ \ /* Really shouldn't be using this macro for sleep > 1 second */ \ assert(E_9 >= (NANOSECONDS)); \ CLOCK_NANOSLEEP(CLOCK_MONOTONIC, seconds, nanoseconds, RESTART); \ } MBEND #endif #endif /* SLEEP_H */ fis-gtm-V7.0-005/sr_unix/source_file.c0000644000032200000250000002302314342376330016504 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_time.h" #include "gtm_stat.h" #include "gtm_unistd.h" #include "compiler.h" #include "parse_file.h" #include "error.h" #include "io.h" #include "io_params.h" #include "eintr_wrappers.h" #include "op.h" #include "source_file.h" #include "zroutines.h" #include "gtmio.h" #include "iotimer.h" #include "cmd_qlf.h" #include "min_max.h" #include "cli.h" #include "have_crit.h" #include "util.h" #include "op_fnzsearch.h" #include "toktyp.h" /* Needed for "valid_mname.h" */ #include "valid_mname.h" #include "stringpool.h" #include "gtmmsg.h" GBLREF char object_file_name[], rev_time_buf[]; GBLREF command_qualifier cmd_qlf; GBLREF int object_file_des; GBLREF io_pair io_curr_device, io_std_device; GBLREF mident routine_name, module_name, int_module_name; GBLREF short object_name_len; GBLREF stack_frame *frame_pointer; GBLREF uint4 dollar_tlevel; GBLREF unsigned char source_file_name[]; GBLREF unsigned short source_name_len; GBLREF spdesc indr_stringpool, rts_stringpool, stringpool; LITREF mval literal_null; LITREF mval literal_notimeout; LITREF mval literal_zero; static bool tt_so_do_once; static io_pair compile_src_dev; static io_pair dev_in_use; /* before opening source file */ static io_pair tmp_list_dev; /* before reading source file */ /* it equal to dev_in_use if list file not open */ static readonly unsigned char open_params_list[] = { (unsigned char)iop_readonly, (unsigned char)iop_m, (unsigned char)iop_eol }; error_def(ERR_ASSERT); error_def(ERR_ERRORSUMMARY); error_def(ERR_FILENOTFND); error_def(ERR_FILEPARSE); error_def(ERR_GTMASSERT); error_def(ERR_GTMASSERT2); error_def(ERR_GTMCHECK); error_def(ERR_LSINSERTED); error_def(ERR_MEMORY); error_def(ERR_NOTMNAME); error_def(ERR_OBJFILERR); error_def(ERR_SRCFILERR); error_def(ERR_STACKOFLOW); error_def(ERR_ZLNOOBJECT); void compile_source_file(unsigned short flen, char *faddr, boolean_t MFtIsReqd) { boolean_t wildcarded, dm_action; int ci, i, rc; mval fstr, ret; plength plen; unsigned char *p, source_file_string[MAX_FN_LEN + 1]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(rts_stringpool.base == stringpool.base); if (MAX_FN_LEN < flen) { dec_err(VARLSTCNT(4) ERR_FILEPARSE, 2, flen, faddr); TREF(dollar_zcstatus) = -ERR_ERRORSUMMARY; return; } object_file_des = FD_INVALID; fstr.mvtype = MV_STR; if (!MEMCMP_LIT(&faddr[flen - SIZEOF(DOTM) + 1], DOTM) || (MAX_FN_LEN < (flen + SIZEOF(DOTM) - 1))) { fstr.str.addr = faddr; fstr.str.len = flen - (!TREF(trigger_compile_and_link) ? 0 : (SIZEOF(DOTM) - 1)); } else { memcpy(source_file_string, faddr, flen); MEMCPY_LIT(&source_file_string[flen], DOTM); fstr.str.addr = (char *)source_file_string; fstr.str.len = flen + SIZEOF(DOTM) - 1; } ESTABLISH(source_ch); tt_so_do_once = FALSE; zsrch_clr(STRM_COMP_SRC); /* Clear any existing search cache */ for (i = 0; ; i++) { plen.p.pint = op_fnzsearch(&fstr, STRM_COMP_SRC, 0, &ret); /* 3rd argument of 0 means internal invocation */ if (!ret.str.len) { if (!i) { dec_err(VARLSTCNT(4) ERR_FILENOTFND, 2, fstr.str.len, fstr.str.addr); TREF(dollar_zcstatus) = -ERR_ERRORSUMMARY; } break; } assert(ret.mvtype == MV_STR); source_name_len = ret.str.len; assert(MAX_FN_LEN >= source_name_len); memcpy(source_file_name, ret.str.addr, source_name_len); source_file_name[source_name_len] = 0; p = &source_file_name[plen.p.pblk.b_dir]; if ((plen.p.pblk.b_dir >= SIZEOF("/dev/") - 1) && !MEMCMP_LIT(source_file_name, "/dev/")) tt_so_do_once = TRUE; else if (MFtIsReqd && (plen.p.pblk.b_ext != 2 || ('M' != p[plen.p.pblk.b_name + 1] && 'm' != p[plen.p.pblk.b_name + 1]))) { /* M filetype is required but not present */ dec_err(VARLSTCNT(4) ERR_FILEPARSE, 2, source_name_len, source_file_name); TREF(dollar_zcstatus) = -ERR_ERRORSUMMARY; continue; } if (i || !MV_DEFINED(&cmd_qlf.object_file)) { routine_name.len = MIN(MAX_MIDENT_LEN, plen.p.pblk.b_name); memcpy(routine_name.addr, p, routine_name.len); memcpy(module_name.addr, routine_name.addr, routine_name.len); if ('_' == *routine_name.addr) routine_name.addr[0] = '%'; if (!TREF(trigger_compile_and_link) && !valid_mname(&routine_name)) { gtm_putmsg_csa(CSA_ARG(NUL) VARLSTCNT(5) ERR_NOTMNAME, 2, source_name_len, source_file_name, ERR_ZLNOOBJECT); TREF(dollar_zcstatus) = -ERR_ERRORSUMMARY; continue; } module_name.len = int_module_name.len = routine_name.len; memcpy(int_module_name.addr, routine_name.addr, routine_name.len); object_file_name[0] = object_name_len = 0; } if ((compiler_startup()) && !TREF(dollar_zcstatus)) TREF(dollar_zcstatus) = ERR_ERRORSUMMARY; if (FD_INVALID != object_file_des) { CLOSEFILE_RESET(object_file_des, rc); /* resets "object_file_des" to FD_INVALID */ if (-1 == rc) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_OBJFILERR, 2, object_name_len, object_file_name, errno); } if (tt_so_do_once) break; } REVERT; } CONDITION_HANDLER(source_ch) { int dummy1, dummy2; START_CH(TRUE); if (DUMP) { NEXTCH; } assert(rts_stringpool.base == stringpool.base); zsrch_clr(0); TREF(dollar_zcstatus) = ERR_ERRORSUMMARY; UNWIND(dummy1, dummy2); } void open_source_file(void) { mstr fstr; int status, n; parse_blk pblk; char *p, buff[MAX_FN_LEN + 1]; time_t clock; struct stat statbuf; mval val; mval pars; unsigned short clen; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; memset(&pblk, 0, SIZEOF(pblk)); pblk.buffer = buff; pblk.buff_size = MAX_FN_LEN; pblk.fop = F_SYNTAXO; fstr.addr = (char *)source_file_name; fstr.len = source_name_len; status = parse_file(&fstr, &pblk); if (!(status & 1)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_FILEPARSE, 2, fstr.len, fstr.addr, status); pars.mvtype = MV_STR; pars.str.len = SIZEOF(open_params_list); pars.str.addr = (char *)open_params_list; val.mvtype = MV_STR; val.str.len = source_name_len; val.str.addr = (char *)source_file_name; p = pblk.l_name; n = (pblk.b_name > MAX_MIDENT_LEN) ? MAX_MIDENT_LEN : pblk.b_name; if (!module_name.len) { memcpy(module_name.addr, p, n); memcpy(routine_name.addr, p, n); if ('_' == *routine_name.addr) routine_name.addr[0] = '%'; routine_name.len = n; if (!TREF(trigger_compile_and_link) && !valid_mname(&routine_name)) stx_error(VARLSTCNT(4) ERR_NOTMNAME, 2, RTS_ERROR_MSTR(&routine_name)); module_name.len = int_module_name.len = n; memcpy(int_module_name.addr, routine_name.addr, n); } op_open(&val, &pars, (mval *)&literal_zero, 0); dev_in_use = io_curr_device; /* save list file info in use if it is opened */ op_use(&val, &pars); compile_src_dev = io_curr_device; if (tt_so_do_once) clock = time(0); else { STAT_FILE((char *)source_file_name, &statbuf, status); assert(status == 0); clock = statbuf.st_mtime; } GTM_CTIME(p, &clock); memcpy(rev_time_buf, p + 4, REV_TIME_BUFF_LEN); io_curr_device = dev_in_use; /* set it back to make open_list_file save the device */ return; } /* * Return: * length of line read */ int4 read_source_file (void) { static char extra_ch; unsigned char *cp; int4 read_len; mval val; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; errno = 0; tmp_list_dev = io_curr_device; io_curr_device = compile_src_dev; ESTABLISH_RET(read_source_ch, -1); read_len = MAX_SRCLINE + (extra_ch ? 0 : 1); /* read up to 1 extra character in case of line > 8k */ op_readfl(&val, read_len, (mval *)(dollar_tlevel ? &literal_zero : &literal_notimeout)); REVERT; if (extra_ch) *((TREF(source_buffer)).addr++) = extra_ch; /* start with the overflow character from the last readfl */ memcpy((TREF(source_buffer)).addr, val.str.addr, val.str.len); if (extra_ch) { /* we had an overflow character from the last readfl */ extra_ch = '\0'; (TREF(source_buffer)).addr--; val.str.len++; } cp = (unsigned char *)((TREF(source_buffer)).addr + val.str.len); (TREF(source_buffer)).len = val.str.len; if (MAX_SRCLINE < val.str.len) { /* Emit a warning */ extra_ch = *(--cp); /* save the overflow character */ dec_err(VARLSTCNT(4) ERR_LSINSERTED, 3, TREF(source_line), source_name_len, source_file_name); if (1 < TREF(source_line)) (TREF(source_line))--; } else (TREF(source_buffer)).len++; *cp = '\n'; /* insert \n needed in checksum calculation */ *(++cp) = '\0'; /* UNIX string terminator */ if (FALSE != io_curr_device.in->dollar.zeof) return -1; io_curr_device = tmp_list_dev; /* restore list file after reading in case it's opened */ return (int4)((TREF(source_buffer)).len); } CONDITION_HANDLER(read_source_ch) { int dummy1, dummy2; START_CH(TRUE); UNWIND(dummy1, dummy2); } void close_source_file (void) { mval val; mval pars; unsigned char no_param; no_param = (unsigned char)iop_eol; pars.mvtype = MV_STR; pars.str.len = SIZEOF(no_param); pars.str.addr = (char *)&no_param; val.mvtype = MV_STR; val.str.len = source_name_len; val.str.addr = (char *)source_file_name; op_close(&val, &pars); io_curr_device = tmp_list_dev; /* not dev_in_use to make sure list file works */ return; } fis-gtm-V7.0-005/sr_unix/ss_anal_shdw_file.c0000644000032200000250000001175714342376330017664 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2020 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_fcntl.h" #include "gtm_stat.h" #include "gtm_unistd.h" #include "gtm_permissions.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_tempnam.h" #include "gtm_time.h" #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmio.h" #include "util.h" #include "eintr_wrappers.h" #include "db_snapshot.h" error_def(ERR_SSFILOPERR); error_def(ERR_SSPREMATEOF); static void ss_print_blk_details(block_id, blk_hdr_ptr_t); static void ss_print_fil_hdr(snapshot_filhdr_ptr_t); void ss_anal_shdw_file(char *filename, int flen) { int shdw_fd, num; int status, db_blk_size; off_t blk_offset; snapshot_filhdr_t ss_filhdr; blk_hdr_ptr_t bp = NULL; block_id blkno, tot_blks, bitmap_size, shadow_vbn, word, bit; unsigned int *bitmap_buffer = NULL; OPENFILE(filename, O_RDONLY, shdw_fd); if (FD_INVALID == shdw_fd) { status = errno; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SSFILOPERR, 4, LEN_AND_LIT("open"), flen, filename, status); } LSEEKREAD(shdw_fd, 0, ((sm_uc_ptr_t)&ss_filhdr), SNAPSHOT_HDR_SIZE, status); if (0 != status) { if (-1 != status) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SSFILOPERR, 4, LEN_AND_LIT("read"), flen, filename, status); return; } else { util_out_print("!/Premature EOF with !AD", TRUE, flen, filename); return; } } bitmap_size = ss_filhdr.ss_info.ss_shmsize - SNAPSHOT_HDR_SIZE; if (0 >= bitmap_size) { util_out_print("!/Incorrect snapshot file format", TRUE); return; } ss_print_fil_hdr(&ss_filhdr); bitmap_buffer = (unsigned int *)malloc(bitmap_size); memset(bitmap_buffer, 0, bitmap_size); LSEEKREAD(shdw_fd, SNAPSHOT_HDR_SIZE, (sm_uc_ptr_t)(bitmap_buffer), bitmap_size, status); if (0 != status) { if (-1 != status) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SSFILOPERR, 4, LEN_AND_LIT("read"), flen, filename, status); return; } else { util_out_print("!/Premature EOF with !AD", TRUE, flen, filename); return; } } tot_blks = ss_filhdr.ss_info.total_blks; shadow_vbn = ss_filhdr.ss_info.shadow_vbn; db_blk_size = ss_filhdr.ss_info.db_blk_size; assert(bitmap_size >= (tot_blks + 8 - 1) / 8); bp = malloc(db_blk_size); for (blkno = 0; blkno <= tot_blks; blkno++) { if (0 == blkno % BLKS_PER_WORD) { word = blkno / BLKS_PER_WORD; num = bitmap_buffer[word]; } bit = blkno % BLKS_PER_WORD; if (num & (1 << bit)) { blk_offset = (BLK_ZERO_OFF(shadow_vbn) + (off_t)blkno * db_blk_size); LSEEKREAD(shdw_fd, blk_offset, bp, db_blk_size, status); if (0 != status) { if (-1 != status) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SSFILOPERR, 4, LEN_AND_LIT("read"), flen, filename, status); return; } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SSPREMATEOF, 5, &blkno, db_blk_size, blk_offset, flen, filename); return; } } ss_print_blk_details(blkno, bp); } } if (NULL != bitmap_buffer) free(bitmap_buffer); if (NULL != bp) free(bp); return; } static void ss_print_fil_hdr(snapshot_filhdr_ptr_t ss_filhdr_ptr) { util_out_print("----------------------------------------", TRUE); util_out_print("Snapshot Initiator PID :!UL", TRUE, ss_filhdr_ptr->ss_info.ss_pid); util_out_print("Snapshot TN :0x!16@XQ", TRUE, &ss_filhdr_ptr->ss_info.snapshot_tn); util_out_print("DB Block Size :!UL", TRUE, ss_filhdr_ptr->ss_info.db_blk_size); util_out_print("Free Blocks :!UL", TRUE, ss_filhdr_ptr->ss_info.free_blks); util_out_print("Total Blocks :!UL", TRUE, ss_filhdr_ptr->ss_info.total_blks); util_out_print("Shadow File :!AD", TRUE, ss_filhdr_ptr->shadow_file_len, ss_filhdr_ptr->ss_info.shadow_file); util_out_print("Shadow Start VBN :!UL", TRUE, ss_filhdr_ptr->ss_info.shadow_vbn); util_out_print("Shadow Shared Mem Size :!UL", TRUE, ss_filhdr_ptr->ss_info.ss_shmsize); util_out_print("----------------------------------------", TRUE); return; } static void ss_print_blk_details(block_id blk, blk_hdr_ptr_t bp) { util_out_print("--------------------------", TRUE); util_out_print("Block Number : !UL", TRUE, blk); util_out_print("Block TN : 0x!16@XQ", TRUE, &bp->tn); util_out_print("Block Level : !UL", TRUE, bp->levl); util_out_print("Block Size : !UL", TRUE, bp->bsiz); util_out_print("--------------------------", TRUE); return; } fis-gtm-V7.0-005/sr_unix/ss_context_mgr.c0000644000032200000250000001334014342376330017244 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2021 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_fcntl.h" #include "gtm_stat.h" #include "gtm_unistd.h" #include #include #include "gtm_permissions.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_tempnam.h" #include "gtm_time.h" #ifdef DEBUG #include "gtm_ipc.h" #endif #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "error.h" #include "cli.h" #include "eintr_wrappers.h" #include "gtmio.h" #include "repl_sp.h" #include "gtm_file_stat.h" #include "util.h" #include "gtm_caseconv.h" #include "gt_timer.h" #include "is_proc_alive.h" #include "is_file_identical.h" #include "dbfilop.h" #include "wcs_flu.h" #include "jnl.h" #include "interlock.h" #include "sleep_cnt.h" #include "mupip_exit.h" #include "trans_log_name.h" #include "gtmimagename.h" #include "send_msg.h" #include "gtm_logicals.h" #include "memcoherency.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif #include "shmpool.h" #include "db_snapshot.h" #include "do_shmat.h" GBLREF sgmnt_addrs *cs_addrs; GBLREF enum gtmImageTypes image_type; GBLREF uint4 process_id; error_def(ERR_SYSCALL); boolean_t ss_create_context(snapshot_context_ptr_t lcl_ss_ctx, int ss_shmcycle) { shm_snapshot_ptr_t ss_shm_ptr; node_local_ptr_t cnl; sgmnt_addrs *csa; int shdw_fd, status; void *ss_shmaddr; ZOS_ONLY(int realfiletag;) ZOS_ONLY(error_def(ERR_BADTAG);) ZOS_ONLY(error_def(ERR_TEXT);) assert(NULL != cs_addrs); csa = cs_addrs; cnl = csa->nl; ss_destroy_context(lcl_ss_ctx); /* Before creating a new one relinquish the existing one */ lcl_ss_ctx->ss_shmcycle = ss_shmcycle; /* SS_MULTI: In case of multiple snapshots, we can't assume that the first index of the snapshot structure in the db * shared memory is the currently active snapshot. */ assert(1 == MAX_SNAPSHOTS); /* This assert will fail when we increase the MAX_SNAPSHOTS per region */ ss_shm_ptr = (shm_snapshot_ptr_t)SS_GETSTARTPTR(csa); DBG_ENSURE_PTR_WITHIN_SS_BOUNDS(csa, (sm_uc_ptr_t)ss_shm_ptr); /* The below memory barrier will ensure that if ever the shmcycle is modified in ss_initiate, we get the up to date * values of the other members in the snapshot structure (copied below). */ SHM_READ_MEMORY_BARRIER; STRCPY(lcl_ss_ctx->shadow_file, ss_shm_ptr->ss_info.shadow_file); lcl_ss_ctx->total_blks = ss_shm_ptr->ss_info.total_blks; lcl_ss_ctx->shadow_vbn = ss_shm_ptr->ss_info.shadow_vbn; lcl_ss_ctx->ss_shm_ptr = ss_shm_ptr; lcl_ss_ctx->nl_shmid = cnl->ss_shmid; /* Attach to the shared memory created by snapshot */ if (INVALID_SHMID != lcl_ss_ctx->nl_shmid) { if (-1 == (sm_long_t)(ss_shmaddr = do_shmat(lcl_ss_ctx->nl_shmid, 0, 0))) { status = errno; /* It's possible that by the time we attach to the shared memory, the identifier has been removed from the * system by INTEG which completed it's processing. */ assert(SHM_REMOVED(status)); assert(INVALID_SHMID == lcl_ss_ctx->attach_shmid); lcl_ss_ctx->cur_state = SNAPSHOT_SHM_ATTACH_FAIL; lcl_ss_ctx->failure_errno = status; return FALSE; } lcl_ss_ctx->attach_shmid = lcl_ss_ctx->nl_shmid; lcl_ss_ctx->start_shmaddr = (sm_uc_ptr_t)ss_shmaddr; lcl_ss_ctx->bitmap_addr = ((sm_uc_ptr_t)ss_shmaddr + SNAPSHOT_HDR_SIZE); } else { /* No ongoing snapshots. Do early return. t_end and tp_tend during validation will identify this change in state * and will turn off snapshot activities */ lcl_ss_ctx->start_shmaddr = NULL; lcl_ss_ctx->bitmap_addr = NULL; lcl_ss_ctx->cur_state = SNAPSHOT_NOT_INITED; return FALSE; } OPENFILE(lcl_ss_ctx->shadow_file, O_RDWR, shdw_fd); lcl_ss_ctx->shdw_fd = shdw_fd; if (FD_INVALID == shdw_fd) { assert((NULL != ss_shmaddr) && (-1 != lcl_ss_ctx->attach_shmid)); /* shared memory attach SHOULD have succeeded */ if (-1 == SHMDT(ss_shmaddr)) { status = errno; assert(FALSE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("Error with shmdt"), CALLFROM, status); } lcl_ss_ctx->attach_shmid = lcl_ss_ctx->nl_shmid = INVALID_SHMID; /* reset shmid since we failed */ lcl_ss_ctx->cur_state = SHADOW_FIL_OPEN_FAIL; lcl_ss_ctx->failure_errno = errno; return FALSE; } # ifdef __MVS__ if (-1 == gtm_zos_tag_to_policy(shdw_fd, TAG_BINARY, &realfiletag)) TAG_POLICY_SEND_MSG(ss_shm_ptr->ss_info.shadow_file, errno, realfiletag, TAG_BINARY); # endif lcl_ss_ctx->cur_state = SNAPSHOT_INIT_DONE; return TRUE; } boolean_t ss_destroy_context(snapshot_context_ptr_t lcl_ss_ctx) { int status; assert(NULL != lcl_ss_ctx); if (FD_INVALID != lcl_ss_ctx->shdw_fd) { CLOSEFILE_RESET(lcl_ss_ctx->shdw_fd, status); } # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_FAKE_SS_SHMDT_WINDOW == gtm_white_box_test_case_number)) { FPRINTF(stdout, "About to delete shm at %x\n", lcl_ss_ctx->start_shmaddr); if (INVALID_SHMID == lcl_ss_ctx->attach_shmid) lcl_ss_ctx->attach_shmid = 0 ; /* ensure / fake that it's not invalid */ } # endif if (INVALID_SHMID != lcl_ss_ctx->attach_shmid) SHMDT((void *)(lcl_ss_ctx->start_shmaddr)); /* no check for errors that 'cause gone is the goal */ /* Invalidate the context */ DEFAULT_INIT_SS_CTX(lcl_ss_ctx); return TRUE; } fis-gtm-V7.0-005/sr_unix/ss_get_block.c0000644000032200000250000000412114342376330016641 0ustar librarygtc/**************************************************************** * * * Copyright 2009, 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 "gtm_fcntl.h" #include "gtm_stat.h" #include "gtm_unistd.h" #include "gtm_permissions.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_tempnam.h" #include "gtm_time.h" #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "eintr_wrappers.h" #include "gtmio.h" #include "shmpool.h" #include "db_snapshot.h" #include "wbox_test_init.h" GBLREF gd_region *gv_cur_region; /* * This function returns the before image of a block (if it exists in the snapshot file) */ boolean_t ss_get_block(sgmnt_addrs *csa, block_id blk, sm_uc_ptr_t blk_buff_ptr) { shm_snapshot_ptr_t ss_shm_ptr; snapshot_context_ptr_t lcl_ss_ctx; lcl_ss_ctx = SS_CTX_CAST(csa->ss_ctx); assert(NULL != lcl_ss_ctx); ss_shm_ptr = lcl_ss_ctx->ss_shm_ptr; DBG_ENSURE_PTR_WITHIN_SS_BOUNDS(csa, (sm_uc_ptr_t)ss_shm_ptr); assert(ss_shm_ptr->in_use && SNAPSHOTS_IN_PROG(csa->nl)); /* Ensure that we never try to request a block that is outside the snapshot range. The only exception is when GT.M * failed while initializing snapshot resources and hence refrained from writing before images. Include whitebox * test case to account for invalid snapshot */ assert((blk < ss_shm_ptr->ss_info.total_blks) || (WBTEST_INVALID_SNAPSHOT_EXPECTED == gtm_white_box_test_case_number)); if (blk >= ss_shm_ptr->ss_info.total_blks) return FALSE; if (ss_chk_shdw_bitmap(csa, lcl_ss_ctx, blk)) { ss_read_block(lcl_ss_ctx, blk, blk_buff_ptr); /* no return in case of failure */ return TRUE; } return FALSE; } fis-gtm-V7.0-005/sr_unix/ss_initiate.c0000644000032200000250000007643514342376330016537 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2022 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_fcntl.h" #include "gtm_stat.h" #include "gtm_unistd.h" #include "gtm_permissions.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_tempnam.h" #include "gtm_time.h" #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "db_header_conversion.h" #include "filestruct.h" #include "iosp.h" #include "error.h" #include "cli.h" #include "gtmio.h" #include "repl_sp.h" #include "gtm_file_stat.h" #include "util.h" #include "gtm_caseconv.h" #include "gt_timer.h" #include "is_proc_alive.h" #include "wcs_flu.h" #include "jnl.h" #include "wcs_sleep.h" #include "interlock.h" #include "add_inter.h" #include "sleep_cnt.h" #include "trans_log_name.h" #include "mupint.h" #include "memcoherency.h" #include "gtm_logicals.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif #include "shmpool.h" #include "db_snapshot.h" #include "gtm_c_stack_trace.h" #include "sys/shm.h" #include "do_shmat.h" #include "have_crit.h" #include "ss_lock_facility.h" #include "gtmimagename.h" #include "jnl_file_close_timer.h" GBLREF boolean_t muint_fast, ointeg_this_reg, online_specified; GBLREF gd_region *gv_cur_region; GBLREF uint4 process_id; GBLREF void (*call_on_signal)(); #ifdef DEBUG_ONLY GBLREF int process_exiting; #endif error_def(ERR_BUFFLUFAILED); error_def(ERR_DBROLLEDBACK); error_def(ERR_MAXSSREACHED); error_def(ERR_SSFILOPERR); error_def(ERR_SSTMPCREATE); error_def(ERR_SSTMPDIRSTAT); error_def(ERR_SSV4NOALLOW); error_def(ERR_SYSCALL); ZOS_ONLY(error_def(ERR_BADTAG);) ZOS_ONLY(error_def(ERR_TEXT);) #define SNAPSHOT_TMP_PREFIX "gtm_snapshot_" #define ISSUE_WRITE_ERROR_AND_EXIT(reg, RC, csa, tempfilename) \ { \ gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_SSFILOPERR, 4, LEN_AND_LIT("write"), LEN_AND_STR(tempfilename), RC); \ if (csa->now_crit) \ rel_crit(csa->region); \ UNFREEZE_REGION_IF_NEEDED(csa->hdr, reg); \ return FALSE; \ } #define TOT_BYTES_REQUIRED(BLKS) DIVIDE_ROUND_UP(BLKS, 8) /* One byte can represent 8 blocks' before image state */ #define EOF_MARKER_SIZE DISK_BLOCK_SIZE #define MAX_TRY_FOR_TOT_BLKS 3 /* This value is the # of tries we do outside of crit in the hope that no * concurrent db extensions occur. This is chosen to be the same as * CDB_STAGNATE but is kept as a different name because there is no reason * for it to be the same. */ #define FILL_SS_FILHDR_INFO(ss_shm_ptr, ss_filhdr_ptr) \ { \ MEMCPY_LIT(ss_filhdr_ptr->label, SNAPSHOT_HDR_LABEL); \ ss_filhdr_ptr->ss_info.ss_pid = ss_shm_ptr->ss_info.ss_pid; \ ss_filhdr_ptr->ss_info.snapshot_tn = ss_shm_ptr->ss_info.snapshot_tn; \ ss_filhdr_ptr->ss_info.db_blk_size = ss_shm_ptr->ss_info.db_blk_size; \ ss_filhdr_ptr->ss_info.free_blks = ss_shm_ptr->ss_info.free_blks; \ ss_filhdr_ptr->ss_info.total_blks = ss_shm_ptr->ss_info.total_blks; \ STRCPY(ss_filhdr_ptr->ss_info.shadow_file, ss_shm_ptr->ss_info.shadow_file); \ ss_filhdr_ptr->shadow_file_len = STRLEN((ss_shm_ptr->ss_info.shadow_file)); \ ss_filhdr_ptr->ss_info.shadow_vbn = ss_shm_ptr->ss_info.shadow_vbn; \ ss_filhdr_ptr->ss_info.ss_shmsize = ss_shm_ptr->ss_info.ss_shmsize; \ } #define GET_CRIT_AND_DECR_INHIBIT_KILLS(REG, CNL) \ { \ grab_crit(REG, WS_96); \ if (!(TREF(in_mupip_integ) && TREF(instance_frozen_crit_skipped))) \ { \ DECR_INHIBIT_KILLS(CNL); \ rel_crit(REG); \ } \ } #define SS_INIT_SHM(SS_SHMSIZE, SS_SHMADDR, SS_SHMID, RESIZE_NEEDED, RC) \ { \ RC = 0; \ \ if (RESIZE_NEEDED) \ { \ assert(INVALID_SHMID != SS_SHMID); \ assert(NULL != SS_SHMADDR); \ assert(0 == ((long)SS_SHMADDR % OS_PAGE_SIZE)); \ if (0 != SHMDT(SS_SHMADDR)) \ { \ RC = errno; \ assert(FALSE); \ gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_SYSCALL, 5, \ LEN_AND_LIT("Error with shmdt"), CALLFROM, RC); \ } \ if (0 != shmctl(SS_SHMID, IPC_RMID, 0)) \ { \ RC = errno; \ assert(FALSE); \ gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_SYSCALL, 5, \ LEN_AND_LIT("Error with shmctl"), CALLFROM, RC); \ } \ } \ SS_SHMID = gtm_shmget(IPC_PRIVATE, SS_SHMSIZE, RWDALL | IPC_CREAT, FALSE); \ if (-1 == SS_SHMID) \ { \ RC = errno; \ assert(FALSE); \ gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_SYSCALL, 5, \ LEN_AND_LIT("Error with shmget"), CALLFROM, RC); \ } \ if (-1 == (sm_long_t)(SS_SHMADDR = do_shmat(SS_SHMID, 0, 0))) \ { \ RC = errno; \ assert(FALSE); \ gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_SYSCALL, 5, \ LEN_AND_LIT("Error with shmat"), CALLFROM, RC); \ } \ } /* In case of an error, un-freeze the region before returning if we had done the region_freeze * in mu_int_reg for a read_only process */ #define UNFREEZE_REGION_IF_NEEDED(CSD, REG) \ { \ if (process_id == (CSD)->image_count) \ { \ assert((REG)->read_only); \ region_freeze((REG), FALSE, FALSE, FALSE, FALSE, FALSE); \ } \ } /* The below function is modelled around mupip_backup_call_on_signal. This is invoked for doing snapshot clean up if ss_initiate * gets interrupted by a MUPIP STOP or other interruptible signals. In such cases, information would not have been transferred to * database shared memory and hence gds_rundown cannot do the cleanup. */ void ss_initiate_call_on_signal(void) { sgmnt_addrs *csa; csa = &FILE_INFO(gv_cur_region)->s_addrs; call_on_signal = NULL; /* Do not recurse via call_on_signal if there is an error */ assert(process_exiting); /* Set by generic_signal_handler() */ assert(NULL != csa->ss_ctx); ss_release(&csa->ss_ctx); return; } boolean_t ss_initiate(gd_region *reg, /* Region in which snapshot has to be started */ util_snapshot_ptr_t util_ss_ptr, /* Utility specific snapshot structure */ snapshot_context_ptr_t *ss_ctx, /* Snapshot context */ boolean_t preserve_snapshot, /* Should the snapshots be preserved ? */ char *calling_utility) /* Who called ss_initiate */ { boolean_t debug_mupip = FALSE; boolean_t final_retry, wait_for_zero_kip; char *tempfilename, eof_marker[EOF_MARKER_SIZE], tempdir_trans_buffer[GTM_PATH_MAX]; char tempdir_full_buffer[GTM_PATH_MAX], tempnamprefix[MAX_FN_LEN + 1]; char time_str[CTIME_BEFORE_NL + 2]; /* for GET_CUR_TIME macro */ enum db_acc_method acc_meth; gtm_uint64_t db_file_size, native_size; int dsk_addr = 0; int fclose_res, fstat_res, group_id, perm, pwrite_res, retries; int save_errno, shdw_fd, status, tmpfd, user_id; ZOS_ONLY(int realfiletag;) long ss_shmid = INVALID_SHMID; mstr tempdir_full, tempdir_log, tempdir_trans; uint4 *kip_pids_arr_ptr; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; node_local_ptr_t cnl; shm_snapshot_ptr_t ss_shm_ptr; snapshot_context_ptr_t lcl_ss_ctx; snapshot_filhdr_ptr_t ss_filhdr_ptr; struct perm_diag_data pdd; struct stat stat_buf; uint4 crit_counter, fstat_status, tempnamprefix_len; block_id tot_blks, ss_shmsize, prev_ss_shmsize, ss_shm_vbn; void *ss_shmaddr; intrpt_state_t prev_intrpt_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(IS_MUPIP_IMAGE); assert(NULL != calling_utility); csa = &FILE_INFO(reg)->s_addrs; csd = csa->hdr; cnl = csa->nl; acc_meth = csd->acc_meth; debug_mupip = (CLI_PRESENT == cli_present("DBG")); /* Create a context containing default information pertinent to this initiate invocation */ lcl_ss_ctx = malloc(SIZEOF(snapshot_context_t)); /* should be free'd by ss_release */ DEFAULT_INIT_SS_CTX(lcl_ss_ctx); call_on_signal = ss_initiate_call_on_signal; /* Snapshot context created. Any error below before the next cur_state assignment and a later call to ss_release will * have to free the malloc'ed snapshot context instance */ lcl_ss_ctx->cur_state = BEFORE_SHADOW_FIL_CREAT; *ss_ctx = lcl_ss_ctx; assert(!csa->now_crit); /* Right now mu_int_reg (which does not hold crit) is the only one that calls ss_initiate */ assert(!csa->hold_onto_crit); /* this ensures we can safely do unconditional grab_crit and rel_crit */ ss_get_lock(reg); /* Grab hold of the snapshot crit lock (low level latch) */ ss_shm_ptr = (shm_snapshot_ptr_t)SS_GETSTARTPTR(csa); if (MAX_SNAPSHOTS == cnl->num_snapshots_in_effect) { /* SS_MULTI: If multiple snapshots are supported, then we should run through each of the "possibly" running * snapshots */ assert(1 == MAX_SNAPSHOTS); /* Check if the existing snapshot is still alive. If not, go ahead and cleanup that for us to continue */ if ((0 != ss_shm_ptr->ss_info.ss_pid) && !is_proc_alive(ss_shm_ptr->ss_info.ss_pid, 0)) ss_release(NULL); else { ss_release_lock(reg); UNFREEZE_REGION_IF_NEEDED(csd, reg); gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_MAXSSREACHED, 3, MAX_SNAPSHOTS, REG_LEN_STR(reg)); return FALSE; } } ss_shm_ptr->ss_info.ss_pid = process_id; cnl->num_snapshots_in_effect++; assert(ss_lock_held_by_us(reg)); ss_release_lock(reg); /* For a readonly database for the current process, we better have the region frozen */ assert(!reg->read_only || FROZEN_HARD(csa)); /* ============================ STEP 1 : Shadow file name construction ============================== * * --> Directory is taken from GTM_SNAPTMPDIR, if available, else GTM_BAK_TEMPDIR_LOG_NAME_UC, if available, * else use current directory. * --> use the template - gtm_snapshot___XXXXXX * --> the last six characters will be replaced by MKSTEMP below * Note: MUPIP RUNDOWN will rely on the above template to do the cleanup if there were a * crash that led to the improper shutdown of the snapshot process. So, if the above template * changes make sure it's taken care of in MUPIP RUNDOWN * Note: most of the shadow file name construction and the creation of shadow file is been * borrowed from mupip_backup.c. This code should be modularized and should be used in both mupip_backup * as well as here. */ /* set up a prefix for the temporary file. */ tempnamprefix_len = 0; memset(tempnamprefix, 0, MAX_FN_LEN); memcpy(tempnamprefix, SNAPSHOT_TMP_PREFIX, STR_LIT_LEN(SNAPSHOT_TMP_PREFIX)); tempnamprefix_len += STR_LIT_LEN(SNAPSHOT_TMP_PREFIX); memcpy(tempnamprefix + tempnamprefix_len, reg->rname, reg->rname_len); tempnamprefix_len += reg->rname_len; SNPRINTF(&tempnamprefix[tempnamprefix_len], MAX_FN_LEN, "_%x", process_id); tempdir_log.addr = GTM_SNAPTMPDIR; tempdir_log.len = STR_LIT_LEN(GTM_SNAPTMPDIR); tempfilename = tempdir_full.addr = tempdir_full_buffer; tempdir_full.len = SIZEOF(tempdir_full_buffer); /* Check if the environment variable is defined or not. * Side-effect: tempdir_trans.addr = tempdir_trans_buffer irrespective of whether TRANS_LOG_NAME * succeeded or not. */ status = TRANS_LOG_NAME(&tempdir_log, &tempdir_trans, tempdir_trans_buffer, SIZEOF(tempdir_trans_buffer), do_sendmsg_on_log2long); if (SS_NORMAL == status && (NULL != tempdir_trans.addr) && (0 != tempdir_trans.len)) *(tempdir_trans.addr + tempdir_trans.len) = 0; else { /* Not found - try GTM_BAK_TEMPDIR_LOG_NAME_UC instead */ tempdir_log.addr = GTM_BAK_TEMPDIR_LOG_NAME_UC; tempdir_log.len = STR_LIT_LEN(GTM_BAK_TEMPDIR_LOG_NAME_UC); status = TRANS_LOG_NAME(&tempdir_log, &tempdir_trans, tempdir_trans_buffer, SIZEOF(tempdir_trans_buffer), do_sendmsg_on_log2long); if (SS_NORMAL == status && (NULL != tempdir_trans.addr) && (0 != tempdir_trans.len)) *(tempdir_trans.addr + tempdir_trans.len) = 0; else { /* Not found - use the current directory via a relative filespec */ tempdir_trans_buffer[0] = '.'; tempdir_trans_buffer[1] = '\0'; tempdir_trans.len = 1; } } /* Verify if we can stat the temporary directory */ if (FILE_STAT_ERROR == (fstat_res = gtm_file_stat(&tempdir_trans, NULL, &tempdir_full, FALSE, &fstat_status))) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_SSTMPDIRSTAT, 2, tempdir_trans.len, tempdir_trans.addr, fstat_status); UNFREEZE_REGION_IF_NEEDED(csd, reg); return FALSE; } SNPRINTF(tempfilename + tempdir_full.len, GTM_PATH_MAX, "/%s_XXXXXX", tempnamprefix); /* ========================== STEP 2 : Create the shadow file ======================== */ /* get a unique temporary file name. The file gets created on success */ DEFER_INTERRUPTS(INTRPT_IN_SS_INITIATE, prev_intrpt_state); /* Defer MUPIP STOP till the file is created */ MKSTEMP(tempfilename, tmpfd); STRCPY(lcl_ss_ctx->shadow_file, tempfilename); /* Shadow file created. Any error below before the next cur_state assignment and a later call to ss_release will have * to delete at least the shadow file apart from free'ing the malloc'ed snapshot context instance */ lcl_ss_ctx->cur_state = AFTER_SHADOW_FIL_CREAT; if (FD_INVALID == tmpfd) { status = errno; gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_SSTMPCREATE, 2, tempdir_trans.len, tempdir_trans.addr, status); UNFREEZE_REGION_IF_NEEDED(csd, reg); ENABLE_INTERRUPTS(INTRPT_IN_SS_INITIATE, prev_intrpt_state); return FALSE; } # ifdef __MVS__ if (-1 == gtm_zos_set_tag(tmpfd, TAG_BINARY, TAG_NOTTEXT, TAG_FORCE, &realfiletag)) TAG_POLICY_GTM_PUTMSG(tempfilename, realfiletag, TAG_BINARY, errno); # endif /* Temporary file for backup was created above using "mkstemp" which on AIX opens the file without * large file support enabled. Work around that by closing the file descriptor returned and reopening * the file with the "open" system call (which gets correctly translated to "open64"). We need to do * this because the temporary file can get > 2GB. Since it is not clear if mkstemp on other Unix platforms * will open the file for large file support, we use this solution for other Unix flavours as well. */ OPENFILE(tempfilename, O_RDWR, shdw_fd); if (FD_INVALID == shdw_fd) { status = errno; gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_SSFILOPERR, 4, LEN_AND_LIT("open"), tempdir_full.len, tempdir_full.addr, status); UNFREEZE_REGION_IF_NEEDED(csd, reg); ENABLE_INTERRUPTS(INTRPT_IN_SS_INITIATE, prev_intrpt_state); return FALSE; } # ifdef __MVS__ if (-1 == gtm_zos_set_tag(shdw_fd, TAG_BINARY, TAG_NOTTEXT, TAG_FORCE, &realfiletag)) TAG_POLICY_GTM_PUTMSG(tempfilename, realfiletag, TAG_BINARY, errno); # endif /* Now that the temporary file has been opened successfully, close the fd returned by mkstemp */ F_CLOSE(tmpfd, fclose_res); lcl_ss_ctx->shdw_fd = shdw_fd; ENABLE_INTERRUPTS(INTRPT_IN_SS_INITIATE, prev_intrpt_state); tempdir_full.len = STRLEN(tempdir_full.addr); /* update the length */ assert(GTM_PATH_MAX >= tempdir_full.len); /* give temporary files the group and permissions as other shared resources - like journal files */ FSTAT_FILE(((unix_db_info *)(reg->dyn.addr->file_cntl->file_info))->fd, &stat_buf, fstat_res); assert(-1 != fstat_res); if (-1 != fstat_res) { /* Even though the temporary snapshot file is a physical file, we give it a relaxed IPC permissions to allow * INTEG started by read-only processes to create snapshot files that are writable by processes having write * permissions on the database file. */ if (!gtm_permissions(&stat_buf, &user_id, &group_id, &perm, PERM_IPC, &pdd)) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(6+PERMGENDIAG_ARG_COUNT) ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("snapshot file"), RTS_ERROR_STRING(((unix_db_info *)(reg->dyn.addr->file_cntl->file_info))->fn), PERMGENDIAG_ARGS(pdd)); gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(6+PERMGENDIAG_ARG_COUNT) ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("snapshot file"), RTS_ERROR_STRING(((unix_db_info *)(reg->dyn.addr->file_cntl->file_info))->fn), PERMGENDIAG_ARGS(pdd)); UNFREEZE_REGION_IF_NEEDED(csd, reg); return FALSE; } } if ((-1 == fstat_res) || (-1 == FCHMOD(shdw_fd, perm)) || (((INVALID_UID != user_id) || (INVALID_GID != group_id)) && (-1 == fchown(shdw_fd, user_id, group_id)))) { status = errno; gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fchmod/fchown"), CALLFROM, status); UNFREEZE_REGION_IF_NEEDED(csd, reg); return FALSE; } if (debug_mupip) { util_out_print("!/MUPIP INFO: Successfully created the shadow file: !AD", TRUE, tempdir_full.len, tempdir_full.addr); } /* STEP 3: Wait for kills-in-progress and initialize snapshot shadow file * * Snapshot Shadow File Layout - * * ----------------------- * | RESERVE_SHM_SPACE | <-- Variable sized space reserved in case we would like the snapshots to be preserved * ----------------------- * | DB_FILE_HEADER | <-- Copy of the database file header taken at the snapshot time (in crit) (SGMNT_HDR_LEN) * ----------------------- * | DB_MASTER_MAP | <-- MASTER_MAP_SIZE_MAX * ----------------------- <-- offset of block zero * | BLOCK_ZERO_BIMG | <-- csd->blk_size * ----------------------- * | BLOCK_ONE_BIMG | <-- csd->blk_size * ----------------------- * . * . * ----------------------- * | EOF_MARKER | <-- 512 bytes (DISK_BLOCK_SIZE) * ----------------------- */ /* Below, master map and the file header will be copied and hence check if we have the right * pointers setup by the caller */ assert(NULL != util_ss_ptr); assert(NULL != util_ss_ptr->master_map); assert(NULL != util_ss_ptr->header); grab_crit(reg, WS_97); INCR_INHIBIT_KILLS(cnl); kip_pids_arr_ptr = cnl->kip_pid_array; prev_ss_shmsize = ss_shmsize = 0; crit_counter = 1; for (retries = 0; MAX_TRY_FOR_TOT_BLKS >= retries; ++retries) { final_retry = (MAX_TRY_FOR_TOT_BLKS == retries); /* On all but the 4th retry (inclusive of retries = -1), release crit */ if ((!final_retry) && (csa->now_crit)) /* In MUPIP INTEG and instance freeze, it is possible we don't have crit */ rel_crit(reg); /* * Outside crit (on the final retry, we will be holding crit while calculating the below): * 1. total blocks * 2. native size (total number of blocks in terms of DISK_BLOCK_SIZE) * 3. write EOF an offset equal to the current database file size * 4. create shared memory segment to be used as bitmap for storing whether a given database block * was before imaged or not. */ DEFER_INTERRUPTS(INTRPT_IN_SS_INITIATE, prev_intrpt_state); /* Defer MUPIP STOP till the shared memory is created */ tot_blks = csd->trans_hist.total_blks; prev_ss_shmsize = ss_shmsize; ss_shmsize = (ROUND_UP((SNAPSHOT_HDR_SIZE + TOT_BYTES_REQUIRED(tot_blks)), OS_PAGE_SIZE)); assert((0 == retries) || (0 != prev_ss_shmsize)); if (0 == retries) { /* If this is the first try, then create shared memory anyways. */ SS_INIT_SHM(ss_shmsize, ss_shmaddr, ss_shmid, FALSE, status); } else if ((prev_ss_shmsize - SNAPSHOT_HDR_SIZE) < TOT_BYTES_REQUIRED(tot_blks)) { /* Shared memory created with SS_INIT_SHM (in prior retries) is aligned at the OS page size boundary. * So, in case of an extension (in subsequent retry) we check if the newly added blocks would fit in * the existing shared memory (possible if the extra bytes allocated for OS page size alignment will * be enough to hold the newly added database blocks). If not, remove the original shared memory and * create a new one. */ if (debug_mupip) { util_out_print("!/MUPIP INFO: Existing shared memory !UL of size !UL not enough for total blocks \ !UL. Creating new shared memory", TRUE, ss_shmid, prev_ss_shmsize, tot_blks); } SS_INIT_SHM(ss_shmsize, ss_shmaddr, ss_shmid, TRUE, status); } if (status) { /* error while creating shared memory */ GET_CRIT_AND_DECR_INHIBIT_KILLS(reg, cnl); UNFREEZE_REGION_IF_NEEDED(csd, reg); ENABLE_INTERRUPTS(INTRPT_IN_SS_INITIATE, prev_intrpt_state); return FALSE; } /* At this point, we are done with allocating shared memory (aligned with OS_PAGE_SIZE) enough to hold * the total number of blocks. Any error below before the next cur_state assignment and a later call * to ss_release will have to detach/rmid the shared memory identifier apart from deleting the shadow * file and free'ing the malloc'ed snapshot context instance. */ lcl_ss_ctx->attach_shmid = ss_shmid; lcl_ss_ctx->start_shmaddr = ss_shmaddr; lcl_ss_ctx->cur_state = AFTER_SHM_CREAT; ENABLE_INTERRUPTS(INTRPT_IN_SS_INITIATE, prev_intrpt_state); if (debug_mupip) { util_out_print("!/MUPIP INFO: Shared memory created. SHMID = !UL", TRUE, ss_shmid); } /* Write EOF block in the snapshot file */ native_size = gds_file_size(reg->dyn.addr->file_cntl); db_file_size = native_size * DISK_BLOCK_SIZE; /* Size of database file in bytes */ LSEEKWRITE(shdw_fd, ((off_t)db_file_size + ss_shmsize), eof_marker, EOF_MARKER_SIZE, status); if (0 != status) { /* error while writing EOF record to snapshot file */ GET_CRIT_AND_DECR_INHIBIT_KILLS(reg, cnl); ISSUE_WRITE_ERROR_AND_EXIT(reg, status, csa, tempfilename); } /* Wait for KIP to reset */ wait_for_zero_kip = csd->kill_in_prog && (MAX_CRIT_TRY > crit_counter); /* only do kip wait once */ /* Wait for existing kills-in-progress to be reset. Since a database file extension is possible as * we don't hold crit while wcs_sleep below, we will retry if the total blocks have changed since * we last checked it. However, in the final retry, the shared memory and shadow file initialization * will happen in crit. Also, in the final retry, we won't be waiting for the kills in progress as * we would be holding crit and cannot afford to wcs_sleep. So, in such a case, MUKILLIP should be * issued by the caller of ss_initiate if csd->kill_in_prog is set to TRUE. But, such a case should * be rare. */ if (wait_for_zero_kip) { /* after inhibitting kills wait once for up to a minute; if that's insufficient, it's probably abandoned */ /* Release crit before going into the wait loop */ if (csa->now_crit) /* In MUPIP INTEG and instance freeze, it is possible we don't have crit */ rel_crit(reg); GET_CUR_TIME(time_str); util_out_print("!/MUPIP INFO: !AD : Start kill-in-prog wait for database !AD", TRUE, CTIME_BEFORE_NL, time_str, DB_LEN_STR(reg)); while (csd->kill_in_prog && (MAX_CRIT_TRY > crit_counter++)) { GET_C_STACK_FOR_KIP(kip_pids_arr_ptr, crit_counter, MAX_CRIT_TRY, 1, MAX_KIP_PID_SLOTS); wcs_sleep(crit_counter); } if (debug_mupip) { GET_CUR_TIME(time_str); util_out_print("!/MUPIP INFO: !AD : Done with kill-in-prog wait on !AD", TRUE, CTIME_BEFORE_NL, time_str, DB_LEN_STR(reg)); } } grab_crit(reg, WS_98); /* After we have created the shared memory and before we grab crit, another process can add new blocks to the * database, in which case csd->trans_hist.total_blks is no longer the value as we noted down above. Check if this * is the case and if so, retry to obtain a consistent copy of the total number of blocks. */ if (tot_blks == csd->trans_hist.total_blks) break; /* We have a consistent copy of the total blocks No need for retry. */ } /* At this point, we are MOST likely guaranteed that kill-in-prog is set to zero for this region and CERTAINLY * guaranteed that no more kills will be started for this region. Now, we are still holding crit, so any error * that occurs henceforth should do a DECR_INHIBIT_KILLS and rel_crit before exiting to let the other processes * proceed gracefully */ assert((TREF(in_mupip_integ) && TREF(instance_frozen_crit_skipped)) || csa->now_crit); assert(NULL != ss_shmaddr); assert(0 == ((long)ss_shmaddr % OS_PAGE_SIZE)); assert(0 == ss_shmsize % OS_PAGE_SIZE); assert(INVALID_SHMID != ss_shmid); assert(0 == (ss_shmsize % DISK_BLOCK_SIZE)); ss_shm_vbn = ss_shmsize / DISK_BLOCK_SIZE; /* # of DISK_BLOCK_SIZEs the shared memory spans across */ assert(ss_shmid == lcl_ss_ctx->attach_shmid); assert(AFTER_SHM_CREAT == lcl_ss_ctx->cur_state); if (debug_mupip) { util_out_print("!/MUPIP INFO: Successfully created shared memory. SHMID = !UL", TRUE, ss_shmid); } /* It is possible that we did saw csd->full_upgraded TRUE before invoking ss_initiate but MUPIP SET -VER=V4 was done after * ss_initiate was called. Handle appropriately. */ if (!csd->fully_upgraded) { /* If -ONLINE was specified explicitly, then it is an ERROR. Issue the error and return FALSE. */ if (online_specified) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_SSV4NOALLOW, 2, DB_LEN_STR(reg)); util_out_print(NO_ONLINE_ERR_MSG, TRUE); GET_CRIT_AND_DECR_INHIBIT_KILLS(reg, cnl); UNFREEZE_REGION_IF_NEEDED(csd, reg); return FALSE; } else { /* If -ONLINE was assumed implicitly (default of INTEG -REG), then set ointeg_this_reg to FALSE, * relinquish the resources and continue as if it is -NOONLINE */ ointeg_this_reg = FALSE; assert(NULL != *ss_ctx); ss_release(ss_ctx); GET_CRIT_AND_DECR_INHIBIT_KILLS(reg, cnl); UNFREEZE_REGION_IF_NEEDED(csd, reg); return TRUE; } } /* ===================== STEP 5: Flush the pending updates in the global cache =================== */ /* For a readonly database for the current process, we cannot do wcs_flu. We would have waited for the active queues * to complete in mu_int_reg after doing a freeze. Now that we have crit, unfreeze the region * We can't do any of it If, however, we don't have crit (instance frozen, and we didn't wait for the freeze to be held off) */ if (!(TREF(in_mupip_integ) && TREF(instance_frozen_crit_skipped))) { if (reg->read_only) { region_freeze(reg, FALSE, FALSE, FALSE, FALSE, FALSE); } else if (!wcs_flu(WCSFLU_FLUSH_HDR | WCSFLU_WRITE_EPOCH | WCSFLU_MSYNC_DB)) /* wcs_flu guarantees that all the * pending phase 2 commits are done with before returning */ { assert(process_id != csd->freeze); /* We would not have frozen the region if the database is read-write */ gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_BUFFLUFAILED, 4, LEN_AND_STR(calling_utility), DB_LEN_STR(reg)); GET_CRIT_AND_DECR_INHIBIT_KILLS(reg, cnl); return FALSE; } } assert(0 < cnl->num_snapshots_in_effect || (!SNAPSHOTS_IN_PROG(cnl))); assert((TREF(in_mupip_integ) && TREF(instance_frozen_crit_skipped)) || csa->now_crit); /* ========== STEP 6: Copy the file header, master map and the native file size into a private structure =========== */ memcpy(util_ss_ptr->header, csd, SGMNT_HDR_LEN); memcpy(util_ss_ptr->master_map, MM_ADDR(csd), MASTER_MAP_SIZE(csd)); util_ss_ptr->native_size = native_size; /* We are about to copy the process private variables to shared memory. Although we have done grab_crit above, we take * snapshot crit lock to ensure that no other process attempts snapshot cleanup. */ DEFER_INTERRUPTS(INTRPT_IN_SS_INITIATE, prev_intrpt_state);/* Defer MUPIP STOP until we complete copying to shared memory */ /* == STEP 7: Populate snapshot context, database shared memory snapshot structure and snapshot file header structure == */ ss_shm_ptr = (shm_snapshot_ptr_t)SS_GETSTARTPTR(csa); DBG_ENSURE_PTR_WITHIN_SS_BOUNDS(csa, (sm_uc_ptr_t)ss_shm_ptr); /* Fill in the information for this snapshot in database shared memory */ ss_shm_ptr->ss_info.snapshot_tn = csd->trans_hist.curr_tn; ss_shm_ptr->ss_info.free_blks = csd->trans_hist.free_blocks; ss_shm_ptr->ss_info.total_blks = csd->trans_hist.total_blks; ss_shm_ptr->ss_info.db_blk_size = csd->blk_size; ss_shm_ptr->failure_errno = 0; ss_shm_ptr->failed_pid = 0; STRCPY(ss_shm_ptr->ss_info.shadow_file, tempfilename); memcpy(&ss_shm_ptr->shadow_file_header, csd, SGMNT_HDR_LEN); ss_shm_ptr->ss_info.shadow_vbn = ss_shm_vbn + csd->start_vbn; ss_shm_ptr->ss_info.ss_shmid = cnl->ss_shmid = ss_shmid; ss_shm_ptr->ss_info.ss_shmsize = ss_shmsize; ss_shm_ptr->in_use = 1; /* Fill in the information for the process specific snapshot context information */ lcl_ss_ctx->ss_shm_ptr = (shm_snapshot_ptr_t)(ss_shm_ptr); lcl_ss_ctx->start_shmaddr = (sm_uc_ptr_t)ss_shmaddr; lcl_ss_ctx->bitmap_addr = ((sm_uc_ptr_t)ss_shmaddr + SNAPSHOT_HDR_SIZE); lcl_ss_ctx->shadow_vbn = ss_shm_ptr->ss_info.shadow_vbn; lcl_ss_ctx->total_blks = ss_shm_ptr->ss_info.total_blks; /* Fill snapshot file header information. This data will be persisted if -PRESERVE option is given */ ss_filhdr_ptr = (snapshot_filhdr_ptr_t)(ss_shmaddr); FILL_SS_FILHDR_INFO(ss_shm_ptr, ss_filhdr_ptr) ss_shm_ptr->preserve_snapshot = preserve_snapshot; SET_LATCH_GLOBAL(&ss_shm_ptr->bitmap_latch, LOCK_AVAILABLE); DECR_INHIBIT_KILLS(cnl); /* announce GT.M that it's now ok to write the before images */ if (!SNAPSHOTS_IN_PROG(cnl)) SET_SNAPSHOTS_IN_PROG(cnl); SET_SNAPSHOTS_IN_PROG(csa); cnl->fastinteg_in_prog = muint_fast; /* Having a write memory barrier here ensures that whenever GT.M reads a newer value of cnl->ss_shmcycle, it is guaranteed * that the remaining fields that it reads from the shared memory will be the latest ones. */ SHM_WRITE_MEMORY_BARRIER; cnl->ss_shmcycle++; /* indicate that the ss_shmid field of cnl is being reused */ lcl_ss_ctx->ss_shmcycle = cnl->ss_shmcycle; if (csa->now_crit) /* In MUPIP INTEG and instance freeze, it is possible we don't have crit */ rel_crit(reg); if ((csa->onln_rlbk_cycle != csa->nl->onln_rlbk_cycle) || csa->nl->onln_rlbk_pid) { /* A concurrent online rollback happened since we did the gvcst_init. The INTEG is not reliable. * Cleanup and exit */ gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(1) ERR_DBROLLEDBACK); UNFREEZE_REGION_IF_NEEDED(csa->hdr, reg); ENABLE_INTERRUPTS(INTRPT_IN_SS_INITIATE, prev_intrpt_state); return FALSE; } /* ============= STEP 8: Write the database file header and the master map ============= * Write the database file header at an offset equal to the database shared memory size. * This is because if we want to preserve the snapshot then we would want to write the * shared memory information at the beginning of the file. */ if (0 == memcmp(util_ss_ptr->header->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_dwnconv(util_ss_ptr->header); LSEEKWRITE(shdw_fd, (off_t)ss_shmsize, (sm_uc_ptr_t)util_ss_ptr->header, SGMNT_HDR_LEN, pwrite_res); if (0 != pwrite_res) { ENABLE_INTERRUPTS(INTRPT_IN_SS_INITIATE, prev_intrpt_state); ISSUE_WRITE_ERROR_AND_EXIT(reg, pwrite_res, csa, tempfilename); } dsk_addr += ((int)ss_shmsize + (int)SGMNT_HDR_LEN); /* write the database master map to the shadow file */ assert(0 == ((dsk_addr + MASTER_MAP_SIZE_MAX) % OS_PAGE_SIZE)); LSEEKWRITE(shdw_fd, dsk_addr, (sm_uc_ptr_t)util_ss_ptr->master_map, MASTER_MAP_SIZE(csd), pwrite_res); if (0 != pwrite_res) { ENABLE_INTERRUPTS(INTRPT_IN_SS_INITIATE, prev_intrpt_state); ISSUE_WRITE_ERROR_AND_EXIT(reg, pwrite_res, csa, tempfilename); } lcl_ss_ctx->cur_state = SNAPSHOT_INIT_DONE; /* Same as AFTER_SHM_CREAT but set for clarity of the snapshot state */ call_on_signal = NULL; /* Any further cleanup on signals will be taken care by gds_rundown */ ENABLE_INTERRUPTS(INTRPT_IN_SS_INITIATE, prev_intrpt_state); assert(!ss_lock_held_by_us(reg)); /* We should never leave the function with the snapshot latch not being released */ return TRUE; } fis-gtm-V7.0-005/sr_unix/ss_lock_facility.c0000644000032200000250000001103614342376330017527 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2019 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_string.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "relqop.h" #include "copy.h" #include "wcs_sleep.h" #include "caller_id.h" #include "gtm_rel_quant.h" #include "sleep_cnt.h" #include "interlock.h" #include "is_proc_alive.h" #include "mupipbckup.h" #include "send_msg.h" #include "performcaslatchcheck.h" #include "gdsbgtr.h" #include "lockconst.h" #include "memcoherency.h" #include "ss_lock_facility.h" GBLREF volatile int4 fast_lock_count; GBLREF uint4 process_id; GBLREF uint4 image_count; GBLREF int num_additional_processors; GBLREF node_local_ptr_t locknl; GBLREF gd_region *gv_cur_region; /* The below lock modules are modeled over shmpool.c. Change in one should be reflected in the other one as well */ boolean_t ss_get_lock(gd_region *reg) { int retries, spins, maxspins; int4 max_sleep_mask; node_local_ptr_t cnl; sgmnt_addrs *csa; sm_global_latch_ptr_t latch; uint4 latch_pid; csa = &FILE_INFO(reg)->s_addrs; cnl = csa->nl; latch = &cnl->snapshot_crit_latch; max_sleep_mask = -1; /* initialized to -1 to defer memory reference until needed */ maxspins = num_additional_processors ? MAX_LOCK_SPINS(LOCK_SPINS, num_additional_processors) : 1; /* Since LOCK_TRIES is approx 50 seconds, give us 4X that long since IO is involved */ ++fast_lock_count; /* Disable wcs_stale for duration */ for (retries = (LOCK_TRIES * 4) - 1; 0 < retries; retries--) { /* this should use a mutex rather than a spin lock */ for (spins = maxspins; 0 < spins; spins--) { /* We better not hold it if trying to get it */ assert(latch->u.parts.latch_pid != process_id VMS_ONLY(|| latch->u.parts.latch_image_count != image_count)); if (GET_SWAPLOCK(latch)) { DEBUG_ONLY(locknl = csa->nl); LOCK_HIST("OBTN", latch, process_id, retries); DEBUG_ONLY(locknl = NULL); /* Note that fast_lock_count is kept incremented for the duration that we hold the lock to prevent our dispatching an interrupt that could deadlock getting this lock */ return TRUE; } if (!is_proc_alive(latch_pid = latch->u.parts.latch_pid, 0)) /* WARNING: assignment */ COMPSWAP_UNLOCK(latch, latch_pid, 0, LOCK_AVAILABLE, 0); } REST_FOR_LATCH(latch, (-1 == max_sleep_mask) ? SPIN_SLEEP_MASK(csa->hdr) : max_sleep_mask, retries); } DUMP_LOCKHIST(); --fast_lock_count; assert(0 <= fast_lock_count); assert(FALSE); return FALSE; } boolean_t ss_get_lock_nowait(gd_region *reg) { sm_global_latch_ptr_t latch; sgmnt_addrs *csa; node_local_ptr_t cnl; csa = &FILE_INFO(reg)->s_addrs; cnl = csa->nl; latch = &cnl->snapshot_crit_latch; ++fast_lock_count; /* Disable wcs_stale for duration */ /* We better not hold it if trying to get it */ assert(latch->u.parts.latch_pid != process_id VMS_ONLY(|| latch->u.parts.latch_image_count != image_count)); if (GET_SWAPLOCK(latch)) { DEBUG_ONLY(locknl = csa->nl); LOCK_HIST("OBTN", latch, process_id, -1); DEBUG_ONLY(locknl = NULL); /* Note that fast_lock_count is kept incremented for the duration that we hold the lock * to prevent our dispatching an interrupt that could deadlock getting this lock */ return TRUE; } --fast_lock_count; assert(0 <= fast_lock_count); return FALSE; } void ss_release_lock(gd_region *reg) { sm_global_latch_ptr_t latch; sgmnt_addrs *csa; node_local_ptr_t cnl; csa = &FILE_INFO(reg)->s_addrs; cnl = csa->nl; latch = &cnl->snapshot_crit_latch; assert(process_id == latch->u.parts.latch_pid VMS_ONLY(&& image_count == latch->u.parts.latch_image_count)); DEBUG_ONLY(locknl = csa->nl); LOCK_HIST("RLSE", latch, process_id, 0); RELEASE_SWAPLOCK(latch); DEBUG_ONLY(locknl = NULL); --fast_lock_count; assert(0 <= fast_lock_count); } boolean_t ss_lock_held_by_us(gd_region *reg) { sgmnt_addrs *csa; node_local_ptr_t cnl; csa = &FILE_INFO(reg)->s_addrs; cnl = csa->nl; return GLOBAL_LATCH_HELD_BY_US(&cnl->snapshot_crit_latch); } fis-gtm-V7.0-005/sr_unix/ss_lock_facility.h0000644000032200000250000000125714342376330017540 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef SS_LOCK_FACILITY_H #define SS_LOCK_FACILITY_H boolean_t ss_get_lock(gd_region *reg); boolean_t ss_get_lock_nowait(gd_region *reg); void ss_release_lock(gd_region *reg); boolean_t ss_lock_held_by_us(gd_region *reg); #endif fis-gtm-V7.0-005/sr_unix/ss_read_block.c0000644000032200000250000000445514342376330017007 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2020 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_fcntl.h" #include "gtm_stat.h" #include "gtm_unistd.h" #include "gtm_permissions.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_tempnam.h" #include "gtm_time.h" #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "error.h" #include "eintr_wrappers.h" #include "gtmio.h" #include "shmpool.h" #include "db_snapshot.h" #include "mupip_exit.h" error_def(ERR_INTEGERRS); error_def(ERR_SSFILOPERR); error_def(ERR_SSPREMATEOF); /* * This function is called to read the before image from the snapshot file */ GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF util_snapshot_ptr_t util_ss_ptr; void ss_read_block(snapshot_context_ptr_t lcl_ss_ctx, block_id blk, sm_uc_ptr_t blk_buff_ptr) { int blk_size, pread_res; boolean_t issue_error; off_t blk_offset; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; shm_snapshot_ptr_t ss_shm_ptr; assert(-1 != lcl_ss_ctx->shdw_fd); csa = cs_addrs; csd = csa->hdr; ss_shm_ptr = lcl_ss_ctx->ss_shm_ptr; DBG_ENSURE_PTR_WITHIN_SS_BOUNDS(csa, (sm_uc_ptr_t)ss_shm_ptr); blk_size = csd->blk_size; blk_offset = (off_t)BLK_ZERO_OFF(lcl_ss_ctx->shadow_vbn) + (off_t)blk * blk_size; LSEEKREAD(lcl_ss_ctx->shdw_fd, blk_offset, (uchar_ptr_t) blk_buff_ptr, blk_size, pread_res); if (0 != pread_res) { if (-1 == pread_res) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_SSPREMATEOF, 5, &blk, blk_size, blk_offset, LEN_AND_STR(ss_shm_ptr->ss_info.shadow_file)); } else { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_SSFILOPERR, 4, LEN_AND_LIT("read"), LEN_AND_STR(ss_shm_ptr->ss_info.shadow_file), pread_res); } mupip_exit(ERR_INTEGERRS); } } fis-gtm-V7.0-005/sr_unix/ss_release.c0000644000032200000250000001734514342376330016344 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2020 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_fcntl.h" #include "gtm_stat.h" #include "gtm_unistd.h" #include #include #include "gtm_permissions.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_tempnam.h" #include "gtm_time.h" #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "error.h" #include "cli.h" #include "eintr_wrappers.h" #include "gtmio.h" #include "gtm_file_stat.h" #include "gtmimagename.h" #include "interlock.h" #include "wcs_phase2_commit_wait.h" #include "util.h" #include "ipcrmid.h" #include "shmpool.h" #include "db_snapshot.h" #include "ss_lock_facility.h" GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data *cs_data; GBLREF gd_region *gv_cur_region; GBLREF uint4 process_id; GBLREF enum gtmImageTypes image_type; error_def(ERR_COMMITWAITSTUCK); error_def(ERR_SYSCALL); error_def(ERR_SSFILCLNUPFAIL); error_def(ERR_SSSHMCLNUPFAIL); #define CLEANUP_SHADOW_FILE(preserve_snapshot, shadow_file, csa) \ { \ int status; \ struct stat stat_buf; \ int save_errno; \ \ if (!preserve_snapshot && (-1 != stat(shadow_file, &stat_buf))) \ { \ status = UNLINK(shadow_file); \ if (0 != status) \ { \ save_errno = errno; \ if (IS_MUMPS_IMAGE) \ send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) \ ERR_SSFILCLNUPFAIL, 2, LEN_AND_STR(shadow_file), save_errno); \ else \ gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) \ ERR_SSFILCLNUPFAIL, 2, LEN_AND_STR(shadow_file), save_errno); \ } \ } \ } \ #define CLEANUP_SHARED_MEMORY(ss_shmid, csa) \ { \ int save_errno; \ struct shmid_ds ss_shmstat; \ \ if ((INVALID_SHMID != ss_shmid) && (0 == shmctl((int)ss_shmid, IPC_STAT, &ss_shmstat))) \ { \ if (0 != shmctl((int)ss_shmid, IPC_RMID, &ss_shmstat)) \ { \ save_errno = errno; \ if (IS_MUMPS_IMAGE) \ send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) \ ERR_SSSHMCLNUPFAIL, 3, LEN_AND_LIT("shmctl"), ss_shmid, save_errno); \ else \ gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(6) \ ERR_SSSHMCLNUPFAIL, 3, LEN_AND_LIT("shmctl"), ss_shmid, save_errno); \ } \ } \ } \ #define ADJUST_SHARED_MEMORY_FIELDS(cnl, ss_shm_ptr) \ { \ if (0 < cnl->num_snapshots_in_effect) \ cnl->num_snapshots_in_effect--; \ if (0 == cnl->num_snapshots_in_effect) \ { \ CLEAR_SNAPSHOTS_IN_PROG(cnl); \ cnl->ss_shmid = INVALID_SHMID; \ } \ SS_DEFAULT_INIT_POOL(ss_shm_ptr); \ cnl->ss_shmcycle++; \ } void ss_release(snapshot_context_ptr_t *ss_ctx) { int status; long ss_shmid; block_id ss_shmsize; boolean_t preserve_snapshot = FALSE; sgmnt_addrs *csa; DEBUG_ONLY(sgmnt_data *csd;) node_local_ptr_t cnl; shm_snapshot_ptr_t ss_shm_ptr; snapshot_context_ptr_t lcl_ss_ctx; boolean_t was_crit; struct shmid_ds ss_shmstat; struct stat stat_buf; char shadow_file[GTM_PATH_MAX]; ss_proc_status cur_state; assert(NULL != cs_addrs && (NULL != gv_cur_region)); csa = cs_addrs; cnl = csa->nl; was_crit = csa->now_crit; DEBUG_ONLY(csd = csa->hdr;) if (NULL == ss_ctx) /* orphaned cleanup */ { assert(ss_lock_held_by_us(gv_cur_region)); ss_shm_ptr = (shm_snapshot_ptr_t)(SS_GETSTARTPTR(csa)); DBG_ENSURE_PTR_WITHIN_SS_BOUNDS(csa, (sm_uc_ptr_t)ss_shm_ptr); ss_shmid = ss_shm_ptr->ss_info.ss_shmid; CLEANUP_SHADOW_FILE(FALSE, ss_shm_ptr->ss_info.shadow_file, csa); CLEANUP_SHARED_MEMORY(ss_shmid, csa); ADJUST_SHARED_MEMORY_FIELDS(cnl, ss_shm_ptr); return; } assert(NULL != *ss_ctx); lcl_ss_ctx = *ss_ctx; cur_state = lcl_ss_ctx->cur_state; if (SNAPSHOT_INIT_DONE == cur_state) { /* Ensure that we don't try to grab_crit and wait for Phase2 commits to complete if we are dying */ assert(!process_exiting); ss_shm_ptr = lcl_ss_ctx->ss_shm_ptr; DBG_ENSURE_PTR_WITHIN_SS_BOUNDS(csa, (sm_uc_ptr_t)ss_shm_ptr); ss_shmsize = ss_shm_ptr->ss_info.ss_shmsize; assert(SNAPSHOTS_IN_PROG(cnl)); assert(ss_shm_ptr->in_use); preserve_snapshot = ss_shm_ptr->preserve_snapshot; /* Irrespective of whether there was an error or not, announce GT.M that it should now stop writing before images. * After rel_crit below any process that enters transaction logic will either see cnl->snapshot_in_prog set to FALSE * or a change in cnl->ss_shmid and cnl->ss_shmcycle in which case it will restart itself in t_end or tp_tend. */ if (!was_crit) grab_crit(gv_cur_region, WS_99); assert(cs_data == cs_addrs->hdr); if (dba_bg == cs_data->acc_meth) { /* Now that we have crit, wait for any pending phase2 updates to finish. Since phase2 updates happen * outside of crit, we dont want them to keep writing to the snapshot file even after the snapshot * is complete. This is needed as otherwise a GT.M process might see the value of csa->snapshot_in_prog * as TRUE and before it can proceed any further(starvation, maybe), we went ahead and removed the * snapshot file(below). Now, if the GT.M process resumes execution, it might end up writing * the before image to a temporary file which is no longer available. */ if (cnl->wcs_phase2_commit_pidcnt && !wcs_phase2_commit_wait(csa, NULL)) { assert(FALSE); gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_COMMITWAITSTUCK, 5, process_id, 1, cnl->wcs_phase2_commit_pidcnt, DB_LEN_STR(gv_cur_region)); } } ADJUST_SHARED_MEMORY_FIELDS(cnl, ss_shm_ptr); if (!was_crit) rel_crit(gv_cur_region); if (preserve_snapshot) { assert(lcl_ss_ctx->shadow_vbn >= csd->start_vbn); assert(ss_shmsize == (lcl_ss_ctx->shadow_vbn - csd->start_vbn) * DISK_BLOCK_SIZE); LSEEKWRITE(lcl_ss_ctx->shdw_fd, 0, (sm_uc_ptr_t)(lcl_ss_ctx->start_shmaddr), ss_shmsize, status); if (0 != status) { util_out_print("!/Failed while writing the snapshot file header. ", TRUE); assert(FALSE); } } } STRCPY(shadow_file, lcl_ss_ctx->shadow_file); ss_shmid = lcl_ss_ctx->attach_shmid; ss_destroy_context(lcl_ss_ctx); /* Do cleanup depending on what state of the snapshot init we are in */ switch(cur_state) { case SNAPSHOT_INIT_DONE: case AFTER_SHM_CREAT: /* Remove shared memory identifier. Note, because of a race condition, it is possible that some GT.M * process is still attached to this identifier. In such a case, the identifier will be marked to be * delted by the OS until number of processes attached to this shared segment becomes zero. GT.M * at the end of each transaction, will try to detach itself from this shared memory segment and will * eventually lead to number of attached processes becoming zero.*/ CLEANUP_SHARED_MEMORY(ss_shmid, csa); /* intentional fall through */ case AFTER_SHADOW_FIL_CREAT: CLEANUP_SHADOW_FILE(preserve_snapshot, shadow_file, csa); /* intentional fall through */ case BEFORE_SHADOW_FIL_CREAT: if (NULL != *ss_ctx) { free(*ss_ctx); *ss_ctx = NULL; } break; default: assert(FALSE); assertpro(FALSE); } return; } fis-gtm-V7.0-005/sr_unix/ss_shdw_mgr.c0000644000032200000250000001173514342376330016533 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2019 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 "gdsroot.h" #include "gtm_facility.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "interlock.h" #include "sleep_cnt.h" #include "mupip_exit.h" #include "copy.h" #include "memcoherency.h" #include "add_inter.h" #include "shmpool.h" #include "db_snapshot.h" #define COMPUTE_BLOCK_OFFSET(blk, word, bit) \ { \ word = blk / BLKS_PER_WORD; \ /* This can be cast to int4 because \ * it is a bit offset within a word \ * and should have a max value of 31 */ \ assert((blk % BLKS_PER_WORD) == (int4)(blk % BLKS_PER_WORD)); \ bit = (int4)(blk % BLKS_PER_WORD); \ } boolean_t ss_chk_shdw_bitmap(sgmnt_addrs *csa, snapshot_context_ptr_t lcl_ss_ctx, block_id blk) { block_id word; int4 bit; unsigned int *bitmap_addr; unsigned int concerned_word; node_local_ptr_t cnl; assert(NULL != lcl_ss_ctx); cnl = csa->nl; if (blk >= lcl_ss_ctx->total_blks) return FALSE; /* This function will be called by either GT.M while operating on phase 2 of the commit or by the snapshot initiator * ss_release (the function that invalidates a snapshot) waits for the existing phase 2 commits to complete and * the snapshot initiator will not call ss_release until it's done with it's operation. Hence the below assert is * safe to be used */ assert((SNAPSHOTS_IN_PROG(csa) || SNAPSHOTS_IN_PROG(cnl))); bitmap_addr = (unsigned int *)(lcl_ss_ctx->bitmap_addr); /* Point to the beginning of the shadow bitmap */ /* It is possible that ss_chk_shdw_bitmap was called in the beginning of t_end or tp_tend where the snapshot context * information cannot be relied upon always as we don't hold crit when we copy information from snapshot information in * shared memory to the private snapshot context. So, the bitmap_addr value could be NULL in which case we don't want * to proceed as dereferencing would cause SIG11. Assert accordingly. */ if (NULL == bitmap_addr) { assert(!csa->wcs_pidcnt_incremented); return FALSE; } COMPUTE_BLOCK_OFFSET(blk, word, bit); assert(0 == ((long)bitmap_addr % 4)); /* MUPIP INTEG when running with -ONLINE uses the following approach: * 1. Checks if the shared memory has the bit turned ON for the block it's currently reading * 2. If TRUE, then read the block from the shadow temporary file * 3. If STEP 1 is FALSE, then read the block from the database file * 4. Check if this bit in the shared memory for this block is still turned OFF (as it was in STEP 1) * 5. If TRUE, proceed with the database block that was read in STEP 3 * 6. If STEP 5 is FALSE (means the bit is now turned ON), discard the database block and read * from shadow temporary file * If not memory barrier is issued below, then an out-of-sync MUPIP INTEG process (running on a processor whose cache * is not in sync with other processes) might end up using the database block which is a post-update copy. So, * issue a READ memory barrier here to receive the changes done by other processors. */ SHM_READ_MEMORY_BARRIER; concerned_word = (*(unsigned int *)(bitmap_addr + word)); if (concerned_word & (1 << bit)) return TRUE; else return FALSE; } void ss_set_shdw_bitmap(sgmnt_addrs *csa, snapshot_context_ptr_t lcl_ss_ctx, block_id blk) { unsigned int *bitmap_addr; DEBUG_ONLY(unsigned int prev_val;) block_id word; int4 bit; node_local_ptr_t cnl; shm_snapshot_ptr_t ss_shm_ptr; assert(NULL != lcl_ss_ctx); cnl = csa->nl; ss_shm_ptr = lcl_ss_ctx->ss_shm_ptr; DBG_ENSURE_PTR_WITHIN_SS_BOUNDS(csa, (sm_uc_ptr_t)ss_shm_ptr); assert(blk < lcl_ss_ctx->total_blks); /* This function will be called by GT.M while operating on phase 2 of the commit. ss_release (the function that * invalidates a snapshot) waits for the existing phase 2 commits to complete and Hence the below assert is safe to * be used */ assert(SNAPSHOTS_IN_PROG(csa) && ss_shm_ptr->in_use); /* Before dereferencing the shared memory, verify if the shared memory that GT.M has attached to and the one that * INTEG has created is actually in sync */ assert(lcl_ss_ctx->attach_shmid == cnl->ss_shmid); assert(ss_shm_ptr->ss_info.ss_shmid == lcl_ss_ctx->attach_shmid); bitmap_addr = (unsigned int *)(lcl_ss_ctx->bitmap_addr); /* Point to the beginning of the shadow bitmap */ COMPUTE_BLOCK_OFFSET(blk, word, bit); DEBUG_ONLY(prev_val = (*(unsigned int *)(bitmap_addr + word));) assert(0 == (prev_val & (1 << bit))); INTERLOCK_ADD((bitmap_addr + word), &ss_shm_ptr->bitmap_latch, (1 << bit)); return; } fis-gtm-V7.0-005/sr_unix/ss_write_block.c0000644000032200000250000001410514342376330017217 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2020 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_fcntl.h" #include "gtm_stat.h" #include "gtm_unistd.h" #include "gtm_permissions.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_tempnam.h" #include "gtm_time.h" #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "error.h" #include "cli.h" #include "gtmio.h" #include "send_msg.h" #include "shmpool.h" #include "db_snapshot.h" GBLREF uint4 process_id; GBLREF mstr pvt_crypt_buf; error_def(ERR_SSFILOPERR); boolean_t ss_write_block(sgmnt_addrs *csa, block_id blk, cache_rec_ptr_t cr, sm_uc_ptr_t mm_blk_ptr, snapshot_context_ptr_t lcl_ss_ctx) { sgmnt_data_ptr_t csd; node_local_ptr_t cnl; sm_uc_ptr_t blk_ptr; shm_snapshot_ptr_t ss_shm_ptr; int save_errno; uint4 size, blk_size; off_t blk_offset; boolean_t is_bg; trans_num ss_tn; # ifdef DEBUG blk_hdr_ptr_t save_blk_ptr; char *ss_encrypt_hash_ptr, *db_encrypt_hash_ptr; boolean_t ss_uses_new_key, db_uses_new_key, ss_was_encrypted, db_was_encrypted; sgmnt_data_ptr_t ss_csd; # endif assert(NULL != lcl_ss_ctx); csd = csa->hdr; cnl = csa->nl; is_bg = (dba_bg == csd->acc_meth); assert(is_bg || (dba_mm == csd->acc_meth)); assert((is_bg && (NULL != cr) && (NULL == mm_blk_ptr)) || (!is_bg && (NULL == cr) && (NULL != mm_blk_ptr))); ss_shm_ptr = lcl_ss_ctx->ss_shm_ptr; DBG_ENSURE_PTR_WITHIN_SS_BOUNDS(csa, (sm_uc_ptr_t)ss_shm_ptr); if (ss_shm_ptr->failure_errno) { /* A prior GT.M process has encountered an error and hence the snapshot is invalid and hence there is * no use in continuing with the before image writes. So, do early return. */ return FALSE; } assert(cnl->ss_shmid == lcl_ss_ctx->attach_shmid); assert(ss_shm_ptr->ss_info.ss_shmid == lcl_ss_ctx->attach_shmid); /* ss_release (function that invalidates a snapshot and announces GT.M not to write any more * before images) waits for the active phase 2 commits to complete and hence the below * assert is safe to be used. */ assert(ss_shm_ptr->in_use && SNAPSHOTS_IN_PROG(csa)); assert(!is_bg || ((NULL != cr) && cr->in_cw_set)); /* ensure the buffer has been pinned (from preemption in db_csh_getn) */ blk_size = (uint4)csd->blk_size; blk_ptr = is_bg ? GDS_ANY_REL2ABS(csa, cr->buffaddr) : mm_blk_ptr; size = ((blk_hdr_ptr_t)blk_ptr)->bsiz; /* If the block is FREE and block size is zero, we don't want to issue an empty write below. * Instead write just the block header (and empty data portion of the block). */ if (!size) size = SIZEOF(blk_hdr); else if (size > blk_size) size = blk_size; if (csd->write_fullblk) size = ROUND_UP(size, csa->fullblockwrite_len); assert(size <= ss_shm_ptr->ss_info.db_blk_size); /* If the database is encrypted, the old_block will be in the encrypted twin buffer. Logic similar to the one * done in backup_block.c */ ss_tn = ((blk_hdr_ptr_t)blk_ptr)->tn; if (NEEDS_ANY_KEY(csd, ss_tn)) { # ifdef DEBUG assert(is_bg); DBG_ENSURE_PTR_IS_VALID_GLOBUFF(csa, csd, blk_ptr); ss_csd = &lcl_ss_ctx->ss_shm_ptr->shadow_file_header; ss_uses_new_key = NEEDS_NEW_KEY(ss_csd, ss_tn); db_uses_new_key = NEEDS_NEW_KEY(csd, ss_tn); ss_was_encrypted = IS_ENCRYPTED(ss_csd->is_encrypted); db_was_encrypted = IS_ENCRYPTED(csd->is_encrypted); if (ss_uses_new_key) ss_encrypt_hash_ptr = ss_csd->encryption_hash2; else if (ss_was_encrypted) ss_encrypt_hash_ptr = ss_csd->encryption_hash; else assert(FALSE); /* If db was unencrypted and unencryptable at start of snapshot, we should never be here */ if (db_uses_new_key) db_encrypt_hash_ptr = csd->encryption_hash2; else if (db_was_encrypted) db_encrypt_hash_ptr = csd->encryption_hash; else assert(FALSE); assert(memcmp(db_encrypt_hash_ptr, EMPTY_GTMCRYPT_HASH, GTMCRYPT_HASH_LEN)); /* The below assert implies we can safely copy the encrypted global buffer to the snapshot file. * The only exception is if the block does not have any data portion. In that case it is okay for the * hashes to not match (since there is no encrypted text). This is possible in case of FREE blocks with bsiz=0 */ assert(!memcmp(ss_encrypt_hash_ptr, db_encrypt_hash_ptr, GTMCRYPT_HASH_LEN) || !((blk_hdr_ptr_t)blk_ptr)->bsiz); # endif DEBUG_ONLY(save_blk_ptr = (blk_hdr_ptr_t)blk_ptr;) blk_ptr = GDS_ANY_ENCRYPTGLOBUF(blk_ptr, csa); /* Ensure that the unencrypted buffer (save_blk_ptr) and the encrypted twin buffer (blk_ptr) are indeed * holding the same block */ assert(save_blk_ptr->tn == ss_tn); assert(save_blk_ptr->bsiz == ((blk_hdr_ptr_t)blk_ptr)->bsiz); assert(save_blk_ptr->levl == ((blk_hdr_ptr_t)blk_ptr)->levl); DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(csa, csd, blk_ptr); } assert(NULL != blk_ptr); blk_offset = (off_t)BLK_ZERO_OFF(lcl_ss_ctx->shadow_vbn) + (off_t)blk * blk_size; /* Note: If a FREE block is being written here, then we could avoid the write below: if the underlying file system * is guaranteed to give us all zeros for a block and if the block header is empty */ assert(-1 != lcl_ss_ctx->shdw_fd); LSEEKWRITE(lcl_ss_ctx->shdw_fd, blk_offset, blk_ptr, size, save_errno); if ((0 != save_errno) && SNAPSHOTS_IN_PROG(cnl)) { assert(FALSE); send_msg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_SSFILOPERR, 4, LEN_AND_LIT("write"), LEN_AND_STR(lcl_ss_ctx->shadow_file), save_errno); ss_shm_ptr->failed_pid = process_id; ss_shm_ptr->failure_errno = save_errno; return FALSE; } /* Mark the block as before imaged in the bitmap */ ss_set_shdw_bitmap(csa, lcl_ss_ctx, blk); return TRUE; } fis-gtm-V7.0-005/sr_unix/std_dev_outbndset.c0000755000032200000250000000340514342376330017731 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "io.h" #include "iottdef.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "deferred_events.h" #include "std_dev_outbndset.h" #define SHFT_MSK 0x00000001 GBLREF boolean_t ctrlc_on, hup_on; GBLREF volatile io_pair io_std_device; GBLREF volatile bool std_dev_outbnd; /* NOTE: xfer_set_handlers() returns success or failure for attempts to set * xfer_table. That value is not currently used here, hence the * cast to void. */ void std_dev_outbndset(int4 ob_char) { uint4 mask; unsigned short n; d_tt_struct *tt_ptr; assertpro(MAXOUTOFBAND >= ob_char); if ((NULL != io_std_device.in) && (tt == io_std_device.in->type)) { /* The NULL check above protects against a hitting while we're initializing the terminal */ tt_ptr = (d_tt_struct *)io_std_device.in->dev_sp; std_dev_outbnd = TRUE; mask = SHFT_MSK << ob_char; if (mask & tt_ptr->enbld_outofbands.mask) (void)xfer_set_handlers(ctrap, ob_char, FALSE); else if (CTRLC_MSK & mask) { if (ctrlc_on) (void)xfer_set_handlers(ctrlc, 0, FALSE); } else if (hup_on && (SIGHUP_MSK & mask)) (void)xfer_set_handlers(sighup, ob_char, FALSE); else assertpro((mask & tt_ptr->enbld_outofbands.mask) || (OUTOFBAND_MSK & mask)); } } fis-gtm-V7.0-005/sr_unix/stop_image.c0000755000032200000250000000330514342376330016340 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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" /* for EXIT() */ #include "gtm_signal.h" #include #include "error.h" #include "util.h" GBLREF boolean_t need_core; /* Core file should be created */ GBLREF int4 exi_condition; GBLREF boolean_t created_core; GBLREF boolean_t dont_want_core; error_def(ERR_ASSERT); error_def(ERR_GTMASSERT); error_def(ERR_GTMASSERT2); error_def(ERR_MEMORY); error_def(ERR_STACKOFLOW); error_def(ERR_GTMCHECK); error_def(ERR_OUTOFSPACE); /* This entry point is going to core */ void stop_image(void) { PRN_ERROR; need_core = TRUE; gtm_fork_n_core(); if (0 == exi_condition) exi_condition = SIGQUIT; EXIT(-exi_condition); } /* This entry point will core if necessary */ void stop_image_conditional_core(void) { /* If coming here because of an IO error presumably while handling a real error, then don't core. */ if (EIO == error_condition) stop_image_no_core(); PRN_ERROR; if ((DUMPABLE) && !SUPPRESS_DUMP) { need_core = TRUE; gtm_fork_n_core(); } if (0 == exi_condition) exi_condition = SIGQUIT; EXIT(-exi_condition); } /* This entry point will not core */ void stop_image_no_core(void) { PRN_ERROR; need_core = FALSE; EXIT(-exi_condition); } fis-gtm-V7.0-005/sr_unix/stop_image_ch.c0000644000032200000250000000173014342376330017007 0ustar librarygtc/**************************************************************** * * * Copyright 2008, 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 "gtm_stdlib.h" #include "error.h" error_def(ERR_ASSERT); error_def(ERR_GTMASSERT); error_def(ERR_GTMASSERT2); error_def(ERR_GTMCHECK); error_def(ERR_MEMORY); error_def(ERR_OUTOFSPACE); error_def(ERR_STACKOFLOW); /* This function is invoked whenever we find NO condition handlers have been ESTABLISHed yet. * Most likely this is an error occuring at process startup. Just print error and exit with error status. */ void stop_image_ch(void) { PRN_ERROR; if (DUMPABLE) DUMP_CORE; EXIT(SIGNAL); } fis-gtm-V7.0-005/sr_unix/str_match.c0000755000032200000250000000515214342376330016177 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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. * * * ****************************************************************/ /* str_match.c */ #include "mdef.h" #include "str_match.h" char *mystrstr(char *o, unsigned short olen, char *t, unsigned short tlen); char *mystrstr(char *o, unsigned short olen, char *t, unsigned short tlen) { char *ptr; unsigned short length; bool matched = FALSE; if (olen < tlen) return NULL; for (ptr = o; ptr <= o + olen - tlen; ptr++) { length = 0; matched = TRUE; for (length = 0; length < tlen; length++) { if (('?' != *(t + length)) && (*(ptr + length) != *(t + length))) { matched = FALSE; break; } } if (TRUE == matched) return ptr; } return NULL; } bool str_match(char *ori, unsigned short orilen, char *template, unsigned short template_len) { char *c, *c_top, *start, *c_prev; template_struct temps; unsigned short counter = 0, i, len, prev_counter; bool wild = FALSE; /* ================== Analyze the template string ============================== */ for (c = template, c_top = template + template_len; c < c_top;) { c_prev = c; while (c < c_top && '*' == *c) c++; if (c > c_prev) { temps.sub[counter].addr = NULL; temps.sub[counter].len = 0; counter++; c_prev = c; } while (c < c_top && '*' != *c) c++; if (c > c_prev) { temps.sub[counter].addr = c_prev; temps.sub[counter].len = INTCAST(c - c_prev); counter++; } } temps.n_subs = counter; /* ================== Match the original string ================================ */ c_prev = ori; c_top = ori + orilen; for(i = 0; i < temps.n_subs; i++) { if (0 == temps.sub[i].len) { wild = TRUE; continue; } else { if ((NULL == (c = mystrstr(c_prev, c_top - c_prev, temps.sub[i].addr, temps.sub[i].len))) || ((c_prev != c) && (TRUE != wild))) return FALSE; c_prev = c + temps.sub[i].len; wild = FALSE; } } if ((c_prev != c_top) && (TRUE != wild)) return FALSE; return TRUE; } fis-gtm-V7.0-005/sr_unix/str_match.h0000755000032200000250000000125014342376330016177 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /* str_match.h */ #define MAX_N_TEMPLATES 256 typedef struct { unsigned short n_subs; mstr sub[MAX_N_TEMPLATES]; } template_struct; bool str_match(char *ori, unsigned short orilen, char *template, unsigned short template_len); fis-gtm-V7.0-005/sr_unix/stripmine.awk0000644000032200000250000001026114342376330016557 0ustar librarygtc################################################################# # # # Copyright (c) 2010-2018 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. # # # ################################################################# # # Part of gengtmdeftypes. # # Script to scrub preprocessor output of routine with all header files. Actions performed: # a. Eliminates all statements other than "typedef" statements. # b. Puts spaces around all tokens, all special chars. # c. Eliminates multiple adjacent white space chars to make easy parsing for $ZPiece(). # BEGIN \ { curlylevel = 0; bracketlevel = 0; parenlevel = 0; typedefactv = 0; prevfield = ""; # # Read in the list of structures we are interested in # while (0 < (getline < "gtmtypelist.txt")) structs[$1]="" # # Read in the list of excluded structures and remove them from structs # while (0 < (getline < "gtmexcludetypelist.txt")) { if ("#" == $1) continue; delete structs[$1] } } # # Main # { gsub("\".*\"", " "); # Eliminate double-quoted strings from consideration - don't need them for our parse gsub("'.*'", " "); # Eliminate single-quoted strings from consideration - don't need them for our parse # # These gsubs are to isolate specific chars with surrounding spaces so our parse can recognize them # gsub("\\[", " & "); # AIX awk doesn't (at least at one point) allow for putting [ within a [] block hence # this is separate from the following line(s). gsub("\\]", " & "); gsub("[#;:,)({}=*]", " & "); # # Change 2 word types to single word for more consistent (less complex) parsing # gsub("unsigned int", "unsigned-int"); gsub("unsigned long", "unsigned-long"); gsub("unsigned short", "unsigned-short"); gsub("unsigned char", "unsigned-char"); gsub("unsigned int", "unsigned-int"); gsub("short unsigned", "unsigned-short"); gsub("signed int", "int"); gsub("short int", "short"); gsub("signed short", "short"); gsub("signed char", "char"); # # Select only typedef types records # if ("#" == $1) next; if ("typedef" == $1) { if (1 == typedefactv) { print "ERROR - found typedef while typedefactv is set at record", NR; exit 1; } tokenssincetypedef = -1; # Since we increment it for the typedef as well parenssincetypedef = 0; isfnptr=0; typedefactv = 1; foundstruct = 0; linecnt = 0; } if (typedefactv) { if ("" == $1) next; # Ignore blank lines for (i = 1; NF >= i; ++i) # Process fields of this line in streaming mode { tokenssincetypedef++; # allow us to track topside if ("{" == $i) curlylevel++; # So we know when we are done with this typedef else if ("}" == $i) curlylevel--; else if ("[" == $i) bracketlevel++; else if ("]" == $i) bracketlevel--; else if ("(" == $i) { parenlevel++; if (2 == tokenssincetypedef && 0 == parenssincetypedef && "*" == $(i+1)) { # Probable function pointer definition i++; # Get rid of "(" i++; # Get rid of "*" if ("volatile" == $i) i++; # Ignore volatile in this definition prevfield = $i; prevfldnum = i; isfnptr = 1; # Prevents prevfield from being updated thus maintaining the type we found } parenssincetypedef++; } else if (")" == $i) parenlevel--; else if (0 == curlylevel && 0 == bracketlevel && (";" == $i || "," == $i)) { if (prevfield in structs) foundstruct = 1; if (";" == $i) typedefactv = 0; } if (0 == bracketlevel && "]" != $i && 0 == parenlevel && ")" != $i && !isfnptr) prevfield = $i; } # # space elimination for easier (and faster) GT.M parsing # line = ""; for (i = 1; NF >= i; ++i) { if (1 != i) line = line " " $i else line = $i } lines[++linecnt] = line; if (0 == typedefactv) { # end of typedef - flush out accumulated lines if this was a structure we care about if (foundstruct) { for (i = 1; linecnt >= i; ++i) print lines[i]; printf("\n"); } } } } fis-gtm-V7.0-005/sr_unix/suspend.c0000644000032200000250000000315414342376330015671 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_signal.h" #include #include "gtm_unistd.h" #include "gtmsiginfo.h" #include "io.h" #include "send_msg.h" #include "setterm.h" GBLREF volatile int suspend_status; GBLREF io_pair io_std_device; GBLREF uint4 process_id; GBLREF uint4 sig_count; error_def(ERR_SUSPENDING); error_def(ERR_TEXT); void suspend(int sig) { int status; /* Clear suspend_status first to prevent recursion */ suspend_status = NOW_SUSPEND; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SUSPENDING, 1, sig); /* Dont call flush_pio() if the received signal is SIGTTOU OR if the received signal is SIGTTIN * and the $P.out is terminal. Arrival of SIGTTIN and SIGTTOU indicates that current process is * in the background. Hence it does not make sense to do flush_pio() $P.out is terminal. */ if (!(sig == SIGTTOU || ((sig == SIGTTIN) && (NULL != io_std_device.out) && (tt == io_std_device.out->type)))) flush_pio(); if (NULL != io_std_device.in && tt == io_std_device.in->type) resetterm(io_std_device.in); sig_count = 0; status = kill(process_id, SIGSTOP); assert(0 == status); return; } fis-gtm-V7.0-005/sr_unix/suspsigs_handler.c0000755000032200000250000000536514342376330017576 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include #include #include "error.h" #include "gtmsiginfo.h" #include "gtmimagename.h" #include "have_crit.h" #include "suspsigs_handler.h" #define MAXINOUT 16 GBLREF volatile int suspend_status; GBLREF uint4 process_id; GBLREF volatile int4 exit_state; GBLREF volatile int4 gtmMallocDepth; /* Recursion indicator */ GBLDEF uint4 sig_count=0; void suspsigs_handler(int sig, siginfo_t* info, void *context) { sigset_t block_susp_sigs, oldsigmask; int status; switch(sig) { case SIGTTIN: case SIGTTOU: /* Terminal access while running in the background */ if (!IS_GTMSECSHR_IMAGE) /* Ignore signal if gtmsecshr */ { if ((EXIT_NOTPENDING < exit_state) || !OK_TO_INTERRUPT) { /* Let the process run in the foreground till it finishes the terminal I/O */ if (EXIT_NOTPENDING == exit_state) suspend_status = DEFER_SUSPEND; status = kill(process_id, SIGCONT); assert(0 == status); /* * If SIGCONT is not delivered before we go back to the I/O operation that caused * SIGTTIN/OU, these signals can be generated again. In such cases, * we might send ourselves an extra SIGCONT. The extra SIGCONT won't affect * the I/O operation since we are running in the foreground already due to the * first SIGCONT. */ sig_count++; /* When process receives SIGTTIN or SIGTTOUT signal, it goes into suspend mode. * If it is NOT OK_TO_INTERRUPT i.e. if process is holding some critical * resources, the suspension of the process is deferred until the critical * resources are released. It is not expected to arrive extra TTIN/TTOU signals * between arrival of first TTIN/TTOU and actual suspension of the process. * If such out of design situation arises, we limit the number of unexpected * TTIN/TTOU to MAXINOUT and then GTMASSERT. */ if (MAXINOUT == sig_count) GTMASSERT; break; } suspend(sig); } break; case SIGTSTP: if (!IS_GTMSECSHR_IMAGE) /* not a good idea to suspend gtmsecshr */ { if (EXIT_NOTPENDING == exit_state) /* not processing other signals */ { if (!OK_TO_INTERRUPT) { suspend_status = DEFER_SUSPEND; break; } suspend(sig); } } break; default: GTMASSERT; } return; } fis-gtm-V7.0-005/sr_unix/suspsigs_handler.h0000755000032200000250000000122614342376330017573 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef SUSPSIGS_HANDLER_H_INCLUDED #define SUSPSIGS_HANDLER_H_INCLUDED void suspsigs_handler(int sig, siginfo_t *info, void *context); #endif fis-gtm-V7.0-005/sr_unix/t_recycled2free.c0000644000032200000250000000522014342376330017245 0ustar librarygtc/**************************************************************** * * * Copyright 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsblk.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "t_recycled2free.h" #include "min_max.h" #include "jnl_get_checksum.h" GBLREF cw_set_element cw_set[]; GBLREF unsigned char cw_set_depth; GBLREF sgmnt_data_ptr_t cs_data; GBLREF sgmnt_addrs *cs_addrs; GBLREF unsigned int t_tries; boolean_t t_recycled2free(srch_blk_status *blkhist) { cw_set_element *cse; blk_hdr_ptr_t old_block; unsigned int bsiz; sgmnt_addrs *csa; jnl_buffer_ptr_t jbbp; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; csa = cs_addrs; assert(cw_set_depth < CDB_CW_SET_SIZE); assert(dba_bg == csa->hdr->acc_meth); cse = &cw_set[cw_set_depth]; cse->mode = gds_t_recycled2free; cse->blk = blkhist->blk_num; cse->old_block = blkhist->buffaddr; old_block = (blk_hdr_ptr_t)cse->old_block; /* t_recycled2free operates on RECYCLED blocks and hence cse->blk_prior_state's free_status is set to FALSE */ BIT_CLEAR_FREE(cse->blk_prior_state); BIT_SET_RECYCLED(cse->blk_prior_state); /* set recycled bit as this is relied upon to write to snapshot file */ cse->level = old_block->levl; /* maintain level information so only level>0 will be written to snapshot file */ cse->cr = blkhist->cr; cse->cycle = blkhist->cycle; cse->blk_checksum = 0; assert(NULL != old_block); jbbp = (JNL_ENABLED(csa) && csa->jnl_before_image) ? csa->jnl->jnl_buff : NULL; if ((NULL != jbbp) && (old_block->tn < jbbp->epoch_tn)) { /* Pre-compute CHECKSUM */ bsiz = old_block->bsiz; if (bsiz > cs_data->blk_size) { assert(CDB_STAGNATE > t_tries); return FALSE; /* restart */ } JNL_GET_CHECKSUM_ACQUIRED_BLK(cse, cs_data, cs_addrs, cse->old_block, bsiz); } cse->upd_addr = NULL; cse->jnl_freeaddr = 0; /* reset jnl_freeaddr that previous transaction might have filled in */ cse->done = FALSE; cse->new_buff = NULL; blkhist->cse = cse; cw_set_depth++; return TRUE; } fis-gtm-V7.0-005/sr_unix/t_recycled2free.h0000644000032200000250000000141614342376330017255 0ustar librarygtc/**************************************************************** * * * Copyright 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #ifndef T_RECYCLED2FREE_DEFINED #define T_RECYCLED2FREE_DEFINED boolean_t t_recycled2free(srch_blk_status *blkhist); #endif fis-gtm-V7.0-005/sr_unix/tab_probecrit_rec.h0000644000032200000250000000267314342376330017672 0ustar librarygtc/**************************************************************** * * * Copyright 2008, 2014 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. * * * ****************************************************************/ /* Note that each TAB_PROBECRIT_REC entry corresponds to a field in cs_addrs * Therefore, avoid any operation that changes the offset of any of these entries in the cs_addrs. * Additions are to be done at the END of the structures. * Replacing existing fields with new fields is allowed (provided their implications are thoroughly analyzed). */ TAB_PROBECRIT_REC(t_get_crit , "CPT", "nanoseconds for the probe to get crit ") TAB_PROBECRIT_REC(p_crit_failed , "CFN", "# of failures of the probe to get crit ") TAB_PROBECRIT_REC(p_crit_que_slps , "CQN", "# of queue sleeps by the probe ") TAB_PROBECRIT_REC(p_crit_yields , "CYN", "# of process yields by the probe ") TAB_PROBECRIT_REC(p_crit_que_full , "CQF", "# of queue fulls encountered by the probe ") TAB_PROBECRIT_REC(p_crit_que_slots , "CQE", "# of empty queue slots found by the probe ") TAB_PROBECRIT_REC(p_crit_success , "CAT", "# of crit acquired total successes ") fis-gtm-V7.0-005/sr_unix/term_setup.c0000755000032200000250000000371014342376330016400 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "io.h" #include "term_setup.h" #include "gtm_signal.h" #include "sig_init.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "deferred_events.h" #include "interlock.h" #include "mdq.h" #include "compiler.h" GBLREF boolean_t ctrlc_on, hup_on; /* TRUE in cenable mode; FALSE in nocenable mode */ GBLREF io_pair io_std_device; /* standard device */ GBLREF void (*ctrlc_handler_ptr)(); GBLREF volatile int4 outofband; /* enumerated event ID*/ void term_setup(boolean_t ctrlc_enable) { int4 event_type; save_xfer_entry *entry; struct sigaction act; #define D_EVENT(a,b) (void *)&b void *set_event_table[] = { #include "outofband.h" }; #undef D_EVENT DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; outofband = no_event; ctrlc_on = (tt == io_std_device.in->type) ? ctrlc_enable : FALSE; if (hup_on && (tt == io_std_device.in->type)) { /* if $PRINCIPAL, enable the hup_handler - similar to iop_hupenable code in iott_use.c */ sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = ctrlc_handler_ptr; sigaction(SIGHUP, &act, 0); } for (event_type = 1; event_type < DEFERRED_EVENTS; event_type++) { /* setup deferred_events */ entry = &(TAREF1(save_xfer_root, event_type)); entry->outofband = event_type; entry->param_val = 0; entry->set_fn = (void(*)(int))set_event_table[event_type]; } TREF(save_xfer_root_ptr) = &(TREF(save_xfer_root)); dqinit(TREF(save_xfer_root_ptr), ev_que); } fis-gtm-V7.0-005/sr_unix/timersp.h0000755000032200000250000000136114342376330015701 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ #ifndef TIMERSP_included #define TIMERSP_included #define TIMER_SCALE 1 /* These values are used during file creation but may be changed on the fly */ #define TIM_FLU_MOD_BG (1000 * TIMER_SCALE) /* 1 sec */ #define TIM_FLU_MOD_MM (1000 * TIMER_SCALE) /* 1 sec */ #endif /* TIMERSP_included */ fis-gtm-V7.0-005/sr_unix/trans_log_name.c0000755000032200000250000001165314342376330017206 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_ctype.h" #include "gtm_string.h" #include "gtm_stdlib.h" #include "io.h" #include "iosp.h" #include "trans_log_name.h" #include "send_msg.h" /* Allocate a buffer to be used for passing a null-terminated environment-variable to GETENV. * If more space is needed, we will expand later. Need to statically allocate space to hold "$gtmdbglvl" * since this is the first environment variable that will be passed to trans_log_name and needs * to be translated BEFORE doing any mallocs hence the initial static allocation of MAX_TRANS_NAME_LEN bytes. */ STATICDEF char trans_log_name_startbuff[MAX_TRANS_NAME_LEN]; STATICDEF unsigned int trans_log_name_buflen = MAX_TRANS_NAME_LEN - 1; /* length of buffer currently allocated */ STATICDEF char *trans_log_name_buff = &trans_log_name_startbuff[0]; error_def(ERR_LOGTOOLONG); int4 trans_log_name(mstr *log, mstr *trans, char *buffer, int4 buffer_len, translog_act do_sendmsg) { char *s_start, *s_ptr, *s_top, *tran_buff, *b_ptr, *b_top, ch; unsigned int s_len; int4 ret; ret = SS_NOLOGNAM; /* assume we don't find it */ b_ptr = buffer; /* b_ptr is points to next place to fill in in output buffer */ b_top = buffer + buffer_len; s_start = log->addr; s_top = s_start + log->len; for (s_ptr = s_start; s_ptr < s_top; ) { assert(s_ptr != buffer); /* should be no intersections between input and output buffer at any point of processing */ if ('$' == *s_ptr) { /* We hit a env var that needs to be translated - they start with $ */ /* For non-initial pass copy any non-env var text that we have passed over into output buffer * and update output buffer pointer. Before that check if output buffer can hold that data. */ s_len = (unsigned int)(s_ptr - s_start); if ((b_ptr + s_len) >= b_top) { ret = SS_LOG2LONG; break; } assert(s_len <= buffer_len); memcpy(b_ptr, s_start, s_len); b_ptr += s_len; /* Move forward in input buffer (over text just processed) */ s_start = s_ptr++; /* Get the env var name. Take care not to exceed input string length */ for ( ; (s_ptr < s_top) && (ch = *s_ptr, ('_' == ch) || ISALNUM_ASCII(ch)); s_ptr++) ; s_len = (unsigned int)(s_ptr - s_start) - 1; /* Copy it into "temporary-buffer" so we can null-terminate it and pass to GETENV */ if (trans_log_name_buflen <= s_len) { /* Currently allocated buffer is not enough. Expand it. */ assert(NULL != trans_log_name_buff); if (trans_log_name_buff != trans_log_name_startbuff) /* do not free static starting buffer */ free(trans_log_name_buff); trans_log_name_buff = malloc(s_len + 1); trans_log_name_buflen = s_len; /* preserve our initial condition that buflen == bufsize -1 */ } assert(NULL != trans_log_name_buff); /* gird against malloc failure */ assert((0 <= s_len) && (s_len < trans_log_name_buflen)); memcpy(trans_log_name_buff, s_start + 1, s_len); trans_log_name_buff[s_len] = 0; /* try to convert it */ if (NULL != (tran_buff = GETENV(trans_log_name_buff))) { s_start = tran_buff; s_len = strlen(tran_buff); ret = SS_NORMAL; } else { /* if there is no env var then just copy the name of the env var name including $ */ s_len = (unsigned int)(s_ptr - s_start); } if ((b_ptr + s_len) >= b_top) { ret = SS_LOG2LONG; break; } assert(s_len <= buffer_len); memcpy(b_ptr, s_start, s_len); b_ptr += s_len; assert(b_ptr < b_top); /* move over env var just processed */ s_start = s_ptr; } else s_ptr++; /* keep going until you either hit end of string or $ */ } if (SS_LOG2LONG != ret) { /* if there is anything left after the last env var name, copy it */ s_len = (unsigned int)(s_ptr - s_start); if ((b_ptr + s_len) >= b_top) ret = SS_LOG2LONG; else { memcpy(b_ptr, s_start, s_len); b_ptr += s_len; assert(b_ptr < b_top); } } assert(b_ptr < b_top); /* "<" instead of "<=" so it is safe to write the termination '\0' character */ /* create the return mstr */ trans->addr = buffer; trans->len = INTCAST(b_ptr - buffer); assert(trans->len < buffer_len); /* Null-terminate returned string (even though an mstr), as this is relied upon * by callers who do ATOI etc. directly on the return string. */ trans->addr[trans->len] = '\0'; if (do_sendmsg && (SS_LOG2LONG == ret)) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_LOGTOOLONG, 3, log->len, log->addr, buffer_len - 1); /* - 1 for terminating null byte */ return ret; } fis-gtm-V7.0-005/sr_unix/trigger_cmd.c0000644000032200000250000000377614342376330016510 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2019 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 "cli.h" static CLI_ENTRY mup_trig_cmd_qual[] = { { "KILL", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "SET", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "ZKILL", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "ZTKILL", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "ZTRIGGER", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "ZWITHDRAW", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NON_NEG, VAL_N_A, 0 }, { "" } }; static CLI_ENTRY mup_trig_option_qual[] = { { "CONSISTENCYCHECK", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "ISOLATION", 0, 0, 0, 0, 0, 0, VAL_DISALLOWED, 0, NEG, VAL_N_A, 0 }, { "" } }; static CLI_ENTRY triggerfile_ary[] = { { "COMMANDS", 0, 0, 0, mup_trig_cmd_qual, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "DELIM", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "NAME", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "OPTIONS", 0, 0, 0, mup_trig_option_qual, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "PIECES", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "XECUTE", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "ZDELIM", 0, 0, 0, 0, 0, 0, VAL_REQ, 0, NON_NEG, VAL_STR, 0 }, { "" } }; GBLDEF CLI_ENTRY trigger_cmd_ary[] = { { "TRIGGER", 0, 0, 0, triggerfile_ary, 0, 0, VAL_DISALLOWED, 0, 0, 0, 0 }, { "" } }; fis-gtm-V7.0-005/sr_unix/trigger_compare.c0000644000032200000250000003231114342376335017363 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2021 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" #ifdef GTM_TRIGGER #include "gtm_string.h" #include "gdsroot.h" /* for gdsfhead.h */ #include "gdsbt.h" /* for gdsfhead.h */ #include "gdsfhead.h" /* For gvcst_protos.h */ #include "gvcst_protos.h" #include "hashtab_str.h" #include "mv_stent.h" /* for COPY_SUBS_TO_GVCURRKEY macro */ #include "gvsub2str.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include "format_targ_key.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include #include "gv_trigger.h" #include "trigger.h" #include "trigger_compare_protos.h" #include "trigger_gbl_fill_xecute_buffer.h" #include "mvalconv.h" /* Needed for MV_FORCE_UMVAL, MV_FORCE_* variants */ #include "op.h" #include "nametabtyp.h" #include "targ_alloc.h" /* for SET_GVTARGET_TO_HASHT_GBL & SWITCH_TO_DEFAULT_REGION */ #include "gdscc.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "filestruct.h" /* needed for jnl.h */ #include "jnl.h" /* needed for tp.h */ #include "tp.h" #include "hashtab_mname.h" #include "t_retry.h" GBLREF sgmnt_data_ptr_t cs_data; GBLREF gd_region *gv_cur_region; GBLREF gv_key *gv_currkey; GBLREF gv_namehead *gv_target; GBLREF unsigned int t_tries; GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; LITREF mval literal_one; LITREF mval literal_ten; LITREF char *trigger_subs[]; LITDEF int trig_subsc_partofhash[] = { #define TRIGGER_SUBSDEF(SUBSTYPE, SUBSNAME, LITMVALNAME, TRIGFILEQUAL, PARTOFHASH) PARTOFHASH, #include "trigger_subs_def.h" #undef TRIGGER_SUBSDEF }; #define COPY_VAL_TO_INDEX_STR(SUB, PTR) \ { \ int len; \ \ len = value_len[SUB]; \ if (len) \ { \ memcpy(PTR, values[SUB], len); \ PTR += len; \ *PTR++ = '\0'; \ } \ } error_def(ERR_TRIGDEFBAD); STATICFNDEF boolean_t compare_vals(char *trigger_val, uint4 trigger_val_len, char *key_val, uint4 key_val_len) { char *end_ptr; char *k_ptr; char *t_ptr; if (key_val_len < trigger_val_len) return FALSE; end_ptr = trigger_val + trigger_val_len; t_ptr = trigger_val; k_ptr = key_val; while ((end_ptr > t_ptr) && (*t_ptr == *k_ptr)) { t_ptr++; k_ptr++; } return ((end_ptr > t_ptr) && ('\0' == *k_ptr)); } void build_kill_cmp_str(char *trigvn, int trigvn_len, char **values, uint4 *value_len, mstr *kill_key, boolean_t multi_line_xecute) { uint4 lcl_len; char *ptr; lcl_len = kill_key->len; assert(lcl_len >= (trigvn_len + 1 + value_len[GVSUBS_SUB] + 1 + value_len[XECUTE_SUB] + 1)); ptr = kill_key->addr; memcpy(ptr, trigvn, trigvn_len); ptr += trigvn_len; *ptr++ = '\0'; COPY_VAL_TO_INDEX_STR(GVSUBS_SUB, ptr); if (!multi_line_xecute) { COPY_VAL_TO_INDEX_STR(XECUTE_SUB, ptr); } else *ptr++ = '\0'; kill_key->len = INTCAST(ptr - kill_key->addr) - 1; } void build_set_cmp_str(char *trigvn, int trigvn_len, char **values, uint4 *value_len, mstr *set_key, boolean_t multi_line_xecute) { uint4 lcl_len; char *ptr; lcl_len = set_key->len; assert(lcl_len >= (trigvn_len + 1 + value_len[GVSUBS_SUB] + 1 + value_len[PIECES_SUB] + 1 + value_len[DELIM_SUB] + 1 + value_len[ZDELIM_SUB] + 1 + value_len[XECUTE_SUB] + 1)); ptr = set_key->addr; memcpy(ptr, trigvn, trigvn_len); ptr += trigvn_len; *ptr++ = '\0'; COPY_VAL_TO_INDEX_STR(GVSUBS_SUB, ptr); COPY_VAL_TO_INDEX_STR(PIECES_SUB, ptr); COPY_VAL_TO_INDEX_STR(DELIM_SUB, ptr); COPY_VAL_TO_INDEX_STR(ZDELIM_SUB, ptr); if (!multi_line_xecute) { COPY_VAL_TO_INDEX_STR(XECUTE_SUB, ptr); } else *ptr++ = '\0'; set_key->len = INTCAST(ptr - set_key->addr) - 1; } boolean_t search_trigger_hash(char *trigvn, int trigvn_len, stringkey *trigger_hash, int match_index, int *hash_indx) { mval collision_indx; mval *collision_indx_ptr; int hash_index; mval key_val; int4 len; mval mv_hash; boolean_t match; char *ptr, *ptr2; int trig_index; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; collision_indx_ptr = &collision_indx; MV_FORCE_UMVAL(&mv_hash, trigger_hash->hash_code); BUILD_HASHT_SUB_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHTRHASH, STR_LIT_LEN(LITERAL_HASHTRHASH), mv_hash, "", 0); while (TRUE) { op_gvorder(collision_indx_ptr); if (0 == collision_indx_ptr->str.len) { hash_index = 0; match = FALSE; break; } BUILD_HASHT_SUB_SUB_MSUB_MSUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHTRHASH, STR_LIT_LEN(LITERAL_HASHTRHASH), mv_hash, collision_indx); if (!gvcst_get(&key_val)) { /* There has to be a #TRHASH entry or else it is a retry situation (due to concurrent updates) */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, LEN_AND_LIT("\"#TRHASH\""), mv_hash.str.len, mv_hash.str.addr); } ptr = key_val.str.addr; ptr2 = memchr(ptr, '\0', key_val.str.len); if (NULL == ptr2) { /* We expect $c(0) in the middle of ptr. If we dont find it, this is a restartable situation */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, LEN_AND_LIT("\"#TRHASH\""), mv_hash.str.len, mv_hash.str.addr); } len = ptr2 - ptr; if ((len != trigvn_len) || (0 != memcmp(trigvn, ptr, len))) continue; assert(0 != match_index); assert(('\0' == *ptr2) && (key_val.str.len > len)); ptr2++; A2I(ptr2, key_val.str.addr + key_val.str.len, trig_index); assert(0 < trig_index); match = (match_index == trig_index); if (match) { hash_index = MV_FORCE_UINT(collision_indx_ptr); break; } } *hash_indx = hash_index; return match; } boolean_t search_triggers(char *trigvn, int trigvn_len, char **values, uint4 *value_len, stringkey *trigger_hash, int *hash_indx, int *trig_indx, boolean_t doing_set) { mval collision_indx; mval *collision_indx_ptr; mval data_val; boolean_t have_value; mval key_val; int4 len; boolean_t multi_record, kill_cmp, first_match_kill_cmp; mval mv_hash; mval mv_trig_indx; boolean_t match, first_match; char *ptr, *ptr2; int sub_indx; int trig_hash_index; int trig_index; char *xecute_buff; int4 xecute_len; unsigned char util_buff[MAX_TRIG_UTIL_LEN]; /* needed for HASHT_GVN_DEFINITION_RETRY_OR_ERROR macro */ int4 util_len; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(cs_addrs->hasht_tree == gv_target); /* should have been set up by caller */ assert(gv_target->root); /* should have been ensured by caller */ collision_indx_ptr = &collision_indx; MV_FORCE_UMVAL(&mv_hash, trigger_hash->hash_code); BUILD_HASHT_SUB_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHTRHASH, STR_LIT_LEN(LITERAL_HASHTRHASH), mv_hash, "", 0); DEBUG_ONLY(xecute_buff = NULL;) first_match = TRUE; while (TRUE) { match = TRUE; op_gvorder(collision_indx_ptr); if (0 == collision_indx_ptr->str.len) { match = FALSE; break; } BUILD_HASHT_SUB_SUB_MSUB_MSUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHTRHASH, STR_LIT_LEN(LITERAL_HASHTRHASH), mv_hash, collision_indx); if (!gvcst_get(&key_val)) { /* There has to be a #TRHASH entry or else it is a retry situation (due to concurrent updates) */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, LEN_AND_LIT("\"#TRHASH\""), mv_hash.str.len, mv_hash.str.addr); } ptr = key_val.str.addr; ptr2 = memchr(ptr, '\0', key_val.str.len); if (NULL == ptr2) { /* We expect $c(0) in the middle of ptr. If we dont find it, this is a restartable situation */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, LEN_AND_LIT("\"#TRHASH\""), mv_hash.str.len, mv_hash.str.addr); } len = ptr2 - ptr; if ((len != trigvn_len) || (0 != memcmp(trigvn, ptr, len))) { /* We expect all hashes under ^#t(,"#TRHASH",...) to correspond to in their value. * If not this is a TRIGDEFBAD situation. */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, LEN_AND_LIT("\"#TRHASH\""), mv_hash.str.len, mv_hash.str.addr); } ptr += len; assert(('\0' == *ptr) && (key_val.str.len > len)); ptr++; A2I(ptr, key_val.str.addr + key_val.str.len, trig_index); assert(0 < trig_index); MV_FORCE_UMVAL(&mv_trig_indx, trig_index); for (sub_indx = 0; sub_indx < NUM_SUBS; sub_indx++) { if (((TRSBS_IN_NONE == trig_subsc_partofhash[sub_indx]) && (CMD_SUB != sub_indx)) || (!doing_set && (TRSBS_IN_BHASH == trig_subsc_partofhash[sub_indx]))) continue; /* Skip indices that are not used for the trigger hash */ assert((CMD_SUB == sub_indx) || ((TRSBS_IN_LHASH | TRSBS_IN_BHASH) == trig_subsc_partofhash[sub_indx]) || (doing_set && (TRSBS_IN_BHASH == trig_subsc_partofhash[sub_indx]))); BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, mv_trig_indx, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx])); multi_record = FALSE; have_value = gvcst_get(&key_val); if (CMD_SUB == sub_indx) { if (!have_value || !key_val.str.len) { /* No CMD node. Retry situation (due to concurrent updates) */ HASHT_GVN_DEFINITION_RETRY_OR_ERROR(trig_index, ",\"CMD\"", cs_addrs); } kill_cmp = ((NULL != memchr(key_val.str.addr, 'K', key_val.str.len)) || (NULL != memchr(key_val.str.addr, 'R', key_val.str.len))); continue; } if (!have_value && (XECUTE_SUB == sub_indx)) { op_gvdata(&data_val); multi_record = (literal_ten.m[0] == data_val.m[0]) && (literal_ten.m[1] == data_val.m[1]); have_value = multi_record; } if ((0 == value_len[sub_indx]) && !have_value) continue; if (XECUTE_SUB == sub_indx) { assert(NULL == xecute_buff); /* We don't want a memory leak */ xecute_buff = trigger_gbl_fill_xecute_buffer(trigvn, trigvn_len, &mv_trig_indx, multi_record ? NULL : &key_val, &xecute_len); if ((value_len[sub_indx] == xecute_len) && (0 == memcmp(values[sub_indx], xecute_buff, value_len[sub_indx]))) { free(xecute_buff); DEBUG_ONLY(xecute_buff = NULL;) continue; } else { free(xecute_buff); DEBUG_ONLY(xecute_buff = NULL;) match = FALSE; break; } } else { if (have_value && (value_len[sub_indx] == key_val.str.len) && (0 == memcmp(values[sub_indx], key_val.str.addr, value_len[sub_indx]))) continue; else { match = FALSE; break; } } } if (match) { trig_hash_index = MV_FORCE_UINT(collision_indx_ptr); assert(trig_index); /* It is possible for 2 triggers to match (in case a KILL and SET trigger for same gvn/subs/xecute string * exists separately). In this case, based on "doing_set", we match the appropriate trigger. If 2 triggers * dont match, we use the only matching trigger even if the trigger type (set/kill) does not match. */ if (first_match) { *trig_indx = trig_index; *hash_indx = trig_hash_index; if (!doing_set && kill_cmp) break; /* KILL type trigger sought and found one. Stop at first. */ first_match = FALSE; /* search once more */ first_match_kill_cmp = kill_cmp; /* Assume this is the only matching trigger for now. Later match if found will override */ } else { assert((first_match_kill_cmp != kill_cmp) || !kill_cmp || (WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number)); /* We have TWO matches. Pick the more appropriate one. */ if (doing_set != kill_cmp) { /* Current trigger matches input trigger type. Overwrite first_match */ *trig_indx = trig_index; *hash_indx = trig_hash_index; } /* else current trigger does not match input trigger type. Use first_match as is (already done) */ break; } } /* We did a BUILD_HASHT_SUB_SUB_MSUB_MSUB_CURRKEY at the beginning of this for loop but gv_currkey would have been * overwritten in various places since then so reinitialize it before doing op_gvorder of next iteration. */ BUILD_HASHT_SUB_SUB_MSUB_MSUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHTRHASH, STR_LIT_LEN(LITERAL_HASHTRHASH), mv_hash, collision_indx); } if (!match) { if (first_match) { *trig_indx = 0; *hash_indx = 0; } else match = TRUE; } return match; } #endif /* GTM_TRIGGER */ fis-gtm-V7.0-005/sr_unix/trigger_compare_protos.h0000644000032200000250000000225314342376330020773 0ustar librarygtc/**************************************************************** * * * Copyright 2010, 2014 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. * * * ****************************************************************/ #ifndef TRIGGER_COMPARE_PROTOS_H_INCLUDED #define TRIGGER_COMPARE_PROTOS_H_INCLUDED STATICFNDCL boolean_t compare_vals(char *trigger_val, uint4 trigger_val_len, char *key_val, uint4 key_val_len); void build_kill_cmp_str(char *trigvn, int trigvn_len, char **values, uint4 *value_len, mstr *kill_key, boolean_t multi_line); void build_set_cmp_str(char *trigvn, int trigvn_len, char **values, uint4 *value_len, mstr *set_key, boolean_t multi_line); boolean_t search_trigger_hash(char *trigvn, int trigvn_len, stringkey *trigger_hash, int trig_indx, int *hash_indx); boolean_t search_triggers(char *trigvn, int trigvn_len, char **values, uint4 *value_len, stringkey *trigger_hash, int *hash_indx, int *trig_indx, boolean_t doing_set); #endif fis-gtm-V7.0-005/sr_unix/trigger_delete.c0000644000032200000250000011460414342376335017205 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2021 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" #ifdef GTM_TRIGGER #include "gdsroot.h" /* for gdsfhead.h */ #include "gdsbt.h" /* for gdsfhead.h */ #include "gdsfhead.h" /* For gvcst_protos.h */ #include "gvcst_protos.h" #include /* for gv_trigger.h */ #include "gv_trigger.h" #include "gdscc.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "filestruct.h" /* needed for jnl.h */ #include "jnl.h" /* needed for tp.h */ #include "tp.h" #include "t_retry.h" #include "gdsblk.h" #include "mv_stent.h" /* for COPY_SUBS_TO_GVCURRKEY macro */ #include "gvsub2str.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include "format_targ_key.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include "targ_alloc.h" /* for SET_GVTARGET_TO_HASHT_GBL */ #include "hashtab_str.h" #include "wbox_test_init.h" #include "trigger_delete_protos.h" #include "trigger.h" #include "trigger_incr_cycle.h" #include "trigger_user_name.h" #include "trigger_compare_protos.h" #include "trigger_parse_protos.h" #include "gtm_trigger_trc.h" #include "min_max.h" #include "mvalconv.h" /* Needed for MV_FORCE_* */ #include "change_reg.h" #include "op.h" #include "util.h" #include "zshow.h" /* for format2zwr() prototype */ #include "hashtab_mname.h" #include "t_begin.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtm_reservedDB.h" #include "is_file_identical.h" #include "anticipatory_freeze.h" #include "gtm_repl_multi_inst.h" /* for DISALLOW_MULTIINST_UPDATE_IN_TP */ GBLREF boolean_t dollar_ztrigger_invoked; GBLREF sgm_info *first_sgm_info; GBLREF gd_addr *gd_header; GBLREF gd_region *gv_cur_region; GBLREF gv_key *gv_currkey; GBLREF gv_namehead *gv_target; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnlpool_addrs_ptr_t jnlpool_head; GBLREF sgm_info *sgm_info_ptr; GBLREF sgmnt_data_ptr_t cs_data; GBLREF uint4 dollar_tlevel; GBLREF unsigned int t_tries; GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; LITREF char *trigger_subs[]; error_def(ERR_TEXT); error_def(ERR_TRIGDEFBAD); error_def(ERR_TRIGLOADFAIL); error_def(ERR_TRIGMODREGNOTRW); error_def(ERR_TRIGNAMBAD); #define MAX_CMD_LEN 20 /* Plenty of room for S,K,ZK,ZTK */ /* This error macro is used for all definition errors where the target is ^#t("TRHASH",) */ #define TRHASH_DEFINITION_RETRY_OR_ERROR(HASH, CSA) \ { \ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) \ t_retry(cdb_sc_triggermod); \ assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); \ rts_error_csa(CSA_ARG(CSA) VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, \ trigvn, LEN_AND_LIT("\"#TRHASH\""),HASH->str.len, \ HASH->str.addr); \ } #define SEARCH_AND_KILL_BY_HASH(TRIGVN, TRIGVN_LEN, HASH, TRIG_INDEX, CSA) \ { \ mval mv_hash_indx; \ mval mv_hash_val; \ int hash_index; \ \ if (search_trigger_hash(TRIGVN, TRIGVN_LEN, HASH, TRIG_INDEX, &hash_index)) \ { \ MV_FORCE_UMVAL(&mv_hash_val, HASH->hash_code); \ MV_FORCE_UMVAL(&mv_hash_indx, hash_index); \ BUILD_HASHT_SUB_SUB_MSUB_MSUB_CURRKEY(TRIGVN, TRIGVN_LEN, \ LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash_val, mv_hash_indx); \ gvcst_kill(FALSE); \ } else \ { /* There has to be a #TRHASH entry */ \ TRHASH_DEFINITION_RETRY_OR_ERROR(HASH, CSA); \ } \ } void cleanup_trigger_hash(char *trigvn, int trigvn_len, char **values, uint4 *value_len, stringkey *set_hash, stringkey *kill_hash, boolean_t del_kill_hash, int match_index) { sgmnt_addrs *csa; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; csa = cs_addrs; assert(!gv_cur_region->read_only); /* caller should have already checked this */ assert(cs_addrs->hasht_tree == gv_target); /* should have been set up by caller */ assert(gv_target->root); /* should have been ensured by caller */ if ((NULL != strchr(values[CMD_SUB], 'S')) && (set_hash->hash_code != kill_hash->hash_code)) SEARCH_AND_KILL_BY_HASH(trigvn, trigvn_len, set_hash, match_index, csa) if (del_kill_hash) SEARCH_AND_KILL_BY_HASH(trigvn, trigvn_len, kill_hash, match_index, csa); } void cleanup_trigger_name(char *trigvn, int trigvn_len, char *trigger_name, int trigger_name_len) { int4 result; char trunc_name[MAX_TRIGNAME_LEN + 1]; uint4 used_trigvn_len; mval val; mval *val_ptr; int var_count; boolean_t is_auto_name; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!gv_cur_region->read_only); /* caller should have already checked this */ assert(cs_addrs->hasht_tree == gv_target); /* should have been set up by caller */ assert(gv_target->root); /* should have been ensured by caller */ /* assume user defined name or auto gen name whose GVN < 21 chars */ if (!trigger_user_name(trigger_name, trigger_name_len)) { /* auto gen name uses #TNCOUNT and #SEQNO under #TNAME */ is_auto_name = TRUE; used_trigvn_len = MIN(trigvn_len, MAX_AUTO_TRIGNAME_LEN); memcpy(trunc_name, trigvn, used_trigvn_len); if (is_auto_name) { /* $get(^#t("#TNAME",,"#TNCOUNT")) */ BUILD_HASHT_SUB_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trunc_name, used_trigvn_len, LITERAL_HASHTNCOUNT, STRLEN(LITERAL_HASHTNCOUNT)); if (gvcst_get(&val)) { /* each autogenerated name has a #TNCOUNT entry */ val_ptr = &val; var_count = MV_FORCE_UINT(val_ptr); if (1 == var_count) { /* kill ^#t("#TNAME",) to kill #TNCOUNT and #SEQNO */ BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trunc_name, used_trigvn_len); gvcst_kill(TRUE); } else { var_count--; MV_FORCE_UMVAL(&val, var_count); /* set ^#t("#TNAME",GVN,"#TNCOUNT")=var_count */ SET_TRIGGER_GLOBAL_SUB_SUB_SUB_MVAL(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trunc_name, used_trigvn_len, LITERAL_HASHTNCOUNT, STRLEN(LITERAL_HASHTNCOUNT), val, result); assert(PUT_SUCCESS == result); /* The count size can only decrease */ } } } } else is_auto_name = FALSE; /* kill ^#t("#TNAME",,:) or zkill ^#t("#TNAME",) if is_auto_name==FALSE */ BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trigger_name, trigger_name_len - 1); gvcst_kill(is_auto_name); } STATICFNDEF int4 update_trigger_name_value(char *trig_name, int trig_name_len, int new_trig_index) { int len; char name_and_index[MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT]; int num_len; char *ptr; int4 result; mval trig_gbl; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!gv_cur_region->read_only); /* caller should have already checked this */ assert(cs_addrs->hasht_tree == gv_target); /* should have been set up by caller */ assert(gv_target->root); /* should have been ensured by caller */ /* $get(^#t("#TNAME",^#t(GVN,index,"#TRIGNAME"))) */ BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trig_name, trig_name_len - 1); if (!gvcst_get(&trig_gbl)) { /* There has to be a #TNAME entry */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TRIGNAMBAD, 4, LEN_AND_LIT("\"#TNAME\""), trig_name_len - 1, trig_name); } ptr = trig_gbl.str.addr; len = MIN(trig_gbl.str.len, MAX_MIDENT_LEN); STRNLEN(ptr, len, len); ptr += len; if ((trig_gbl.str.len == len) || ('\0' != *ptr)) { if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TRIGNAMBAD, 4, LEN_AND_LIT("\"#TNAME\""), trig_name_len - 1, trig_name); } memcpy(name_and_index, trig_gbl.str.addr, ++len); /* inline increment intended */ ptr = name_and_index + len; num_len = 0; I2A(ptr, num_len, new_trig_index); len += num_len; /* set ^#t("#TNAME",)=gblname_$C(0)_new_trig_index */ SET_TRIGGER_GLOBAL_SUB_SUB_STR(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trig_name, trig_name_len - 1, name_and_index, len, result); return result; } STATICFNDEF int4 update_trigger_hash_value(char *trigvn, int trigvn_len, char **values, uint4 *value_len, stringkey *set_hash, stringkey *kill_hash, int old_trig_index, int new_trig_index) { sgmnt_addrs *csa; int hash_index; mval key_val; uint4 len; mval mv_hash; mval mv_hash_indx; int num_len; char *ptr; int4 result; char tmp_str[MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!gv_cur_region->read_only); /* caller should have already checked this */ assert(cs_addrs->hasht_tree == gv_target); /* should have been set up by caller */ assert(gv_target->root); /* should have been ensured by caller */ csa = cs_addrs; if ((NULL != strchr(values[CMD_SUB], 'S')) && (set_hash->hash_code != kill_hash->hash_code)) { if (!search_trigger_hash(trigvn, trigvn_len, set_hash, old_trig_index, &hash_index)) { /* There has to be an entry with the old hash value, we just looked it up */ TRHASH_DEFINITION_RETRY_OR_ERROR(set_hash, csa); } MV_FORCE_UMVAL(&mv_hash, set_hash->hash_code); MV_FORCE_UMVAL(&mv_hash_indx, hash_index); BUILD_HASHT_SUB_SUB_MSUB_MSUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_hash_indx); if (!gvcst_get(&key_val)) { /* There has to be a #TRHASH entry */ TRHASH_DEFINITION_RETRY_OR_ERROR(set_hash, csa); } assert((MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT) >= key_val.str.len); ptr = key_val.str.addr; len = MIN(key_val.str.len, MAX_MIDENT_LEN); STRNLEN(ptr, len, len); ptr += len; if ((key_val.str.len == len) || ('\0' != *ptr)) { /* We expect $c(0) in the middle of ptr. If we dont find it, this is a restartable situation */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, LEN_AND_LIT("\"#BHASH\""), mv_hash.str.len, mv_hash.str.addr); } memcpy(tmp_str, key_val.str.addr, len); ptr = tmp_str + len; *ptr++ = '\0'; num_len = 0; I2A(ptr, num_len, new_trig_index); len += num_len + 1; SET_TRIGGER_GLOBAL_SUB_SUB_MSUB_MSUB_STR(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_hash_indx, tmp_str, len, result); if (PUT_SUCCESS != result) return result; } if (!search_trigger_hash(trigvn, trigvn_len, kill_hash, old_trig_index, &hash_index)) { /* There has to be an entry with the old hash value, we just looked it up */ TRHASH_DEFINITION_RETRY_OR_ERROR(kill_hash, csa); } MV_FORCE_UMVAL(&mv_hash, kill_hash->hash_code); MV_FORCE_UMVAL(&mv_hash_indx, hash_index); BUILD_HASHT_SUB_SUB_MSUB_MSUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_hash_indx); if (!gvcst_get(&key_val)) { /* There has to be a #TRHASH entry */ TRHASH_DEFINITION_RETRY_OR_ERROR(kill_hash, csa); } assert((MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT) >= key_val.str.len); ptr = key_val.str.addr; len = MIN(key_val.str.len, MAX_MIDENT_LEN); STRNLEN(ptr, len, len); ptr += len; if ((key_val.str.len == len) || ('\0' != *ptr)) { /* We expect $c(0) in the middle of ptr. If we dont find it, this is a restartable situation */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, LEN_AND_LIT("\"#LHASH\""), mv_hash.str.len, mv_hash.str.addr); } memcpy(tmp_str, key_val.str.addr, len); ptr = tmp_str + len; *ptr++ = '\0'; num_len = 0; I2A(ptr, num_len, new_trig_index); len += num_len + 1; SET_TRIGGER_GLOBAL_SUB_SUB_MSUB_MSUB_STR(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_hash_indx, tmp_str, len, result); return result; } boolean_t trigger_delete_name(mval *trigger_rec, uint4 *trig_stats) { sgmnt_addrs *csa; char curr_name[MAX_MIDENT_LEN + 1]; uint4 curr_name_len, orig_name_len, trigger_name_len; mval mv_curr_nam; char *ptr, *trigger_name; char *name_tail_ptr; char save_name[MAX_MIDENT_LEN + 1]; gv_key_buf save_currkey; gd_region *save_gv_cur_region, *lgtrig_reg; gv_namehead *save_gv_target; sgm_info *save_sgm_info_ptr; jnlpool_addrs_ptr_t save_jnlpool; mval trig_gbl; mval *trigger_count; char trigvn[MAX_MIDENT_LEN + 1]; int trigvn_len; int trig_indx; int badpos; boolean_t wildcard; char utilprefix[1024]; int utilprefixlen; boolean_t first_gtmio; uint4 triggers_deleted; boolean_t jnl_format_done; gd_region *reg, *reg_top; char disp_trigvn[MAX_MIDENT_LEN + SPANREG_REGION_LITLEN + MAX_RN_LEN + 1 + 1]; /* SPANREG_REGION_LITLEN for " (region ", MAX_RN_LEN for region name, * 1 for ")" and 1 for trailing '\0'. */ int disp_trigvn_len; int trig_protected_mval_push_count; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; badpos = 0; orig_name_len = trigger_name_len = trigger_rec->str.len - 1; trigger_name = trigger_rec->str.addr + 1; if ((0 == trigger_name_len) || (trigger_name_len != (badpos = validate_input_trigger_name(trigger_name, trigger_name_len, &wildcard)))) { /* is the input name valid */ CONV_STR_AND_PRINT("Invalid trigger NAME string: ", orig_name_len, trigger_name); /* badpos is the string position where the bad character was found, pretty print it */ trig_stats[STATS_ERROR_TRIGFILE]++; return TRIG_FAILURE; } if (trig_stats[STATS_ERROR_TRIGFILE]) { /* If an error has occurred during trigger processing the above validity check is all we need */ CONV_STR_AND_PRINT("No errors processing trigger delete by name: ", orig_name_len, trigger_name); /* Return success because there was no error with the name to delete */ return TRIG_SUCCESS; } name_tail_ptr = trigger_name + trigger_name_len - 1; if ((TRIGNAME_SEQ_DELIM == *name_tail_ptr) || wildcard) trigger_name_len--; /* drop the trailing # sign for wildcard */ jnl_format_done = FALSE; lgtrig_reg = NULL; first_gtmio = TRUE; triggers_deleted = 0; assert(trigger_name_len < MAX_MIDENT_LEN); memcpy(save_name, trigger_name, trigger_name_len); save_name[trigger_name_len] = '\0'; utilprefixlen = ARRAYSIZE(utilprefix); trig_protected_mval_push_count = 0; INCR_AND_PUSH_MV_STENT(trigger_count); /* Protect trigger_count from garbage collection */ for (reg = gd_header->regions, reg_top = reg + gd_header->n_regions; reg < reg_top; reg++) { if (IS_STATSDB_REGNAME(reg)) continue; GVTR_SWITCH_REG_AND_HASHT_BIND_NAME(reg); csa = cs_addrs; if (NULL == csa) /* not BG or MM access method */ continue; /* gv_target now points to ^#t in region "reg" */ DISALLOW_MULTIINST_UPDATE_IN_TP(dollar_tlevel, jnlpool_head, csa, first_sgm_info, TRUE); /* To write the LGTRIG logical jnl record, choose some region that has journaling enabled */ if (!reg->read_only && !jnl_format_done && JNL_WRITE_LOGICAL_RECS(csa)) lgtrig_reg = reg; if (!gv_target->root) continue; memcpy(curr_name, save_name, trigger_name_len); curr_name_len = trigger_name_len; do { /* GVN = $get(^#t("#TNAME",curr_name)) */ BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), curr_name, curr_name_len); if (gvcst_get(&trig_gbl)) { if (reg->read_only) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_TRIGMODREGNOTRW, 2, REG_LEN_STR(reg)); SAVE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); ptr = trig_gbl.str.addr; trigvn_len = MIN(trig_gbl.str.len, MAX_MIDENT_LEN); STRNLEN(ptr, trigvn_len, trigvn_len); ptr += trigvn_len; if ((trig_gbl.str.len == trigvn_len) || ('\0' != *ptr)) { /* We expect $c(0) in the middle of ptr. If not found, this is a restartable situation */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TRIGNAMBAD, 4, LEN_AND_LIT("\"#TNAME\""), curr_name_len, curr_name); } memcpy(trigvn, trig_gbl.str.addr, trigvn_len); /* the index is just beyond the length of the GVN string */ ptr++; A2I(ptr, trig_gbl.str.addr + trig_gbl.str.len, trig_indx); if (1 > trig_indx) { /* Trigger indexes start from 1 */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TRIGNAMBAD, 4, LEN_AND_LIT("\"#TNAME\""), curr_name_len, curr_name); } SET_DISP_TRIGVN(reg, disp_trigvn, disp_trigvn_len, trigvn, trigvn_len); /* $get(^#t(GVN,"COUNT") */ BUILD_HASHT_SUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT)); if (!gvcst_get(trigger_count)) { /* We just looked this up, if it doesn't exist then assume a concurrent update occurred */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, trigvn_len, trigvn, LEN_AND_LIT("\"#COUNT\"")); } if (!jnl_format_done && JNL_WRITE_LOGICAL_RECS(csa)) { jnl_format(JNL_LGTRIG, NULL, trigger_rec, 0); jnl_format_done = TRUE; } /* kill the target trigger for GVN at index trig_indx */ if (PUT_SUCCESS != (trigger_delete(trigvn, trigvn_len, trigger_count, trig_indx))) { UTIL_PRINT_PREFIX_IF_NEEDED(first_gtmio, utilprefix, &utilprefixlen); util_out_print_gtmio("Trigger named !AD exists in the lookup table for global ^!AD," \ " but was not deleted!", FLUSH, orig_name_len, trigger_name, disp_trigvn_len, disp_trigvn); trig_stats[STATS_ERROR_TRIGFILE]++; RETURN_AND_POP_MVALS(TRIG_FAILURE); } else { csa->incr_db_trigger_cycle = TRUE; trigger_incr_cycle(trigvn, trigvn_len); /* ^#t records changed, increment cycle */ if (dollar_ztrigger_invoked) { /* Increment db_dztrigger_cycle so that next gvcst_put/gvcst_kill in this * transaction, on this region, will re-read triggers. See trigger_update.c * for a comment on why it is okay for db_dztrigger_cycle to be incremented * more than once in the same transaction. */ DBGTRIGR((stderr, "trigger_delete_name: CSA->db_dztrigger_cycle=%d\n", csa->db_dztrigger_cycle)); csa->db_dztrigger_cycle++; } trig_stats[STATS_DELETED]++; if (0 == trig_stats[STATS_ERROR_TRIGFILE]) { UTIL_PRINT_PREFIX_IF_NEEDED(first_gtmio, utilprefix, &utilprefixlen); util_out_print_gtmio("Deleted trigger named '!AD' for global ^!AD", FLUSH, curr_name_len, curr_name, disp_trigvn_len, disp_trigvn); } } trigger_count->mvtype = 0; /* allow stp_gcol to release the current contents if necessary */ RESTORE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); triggers_deleted++; } if (!wildcard) /* not a wild card, don't $order for the next match */ break; op_gvorder(&mv_curr_nam); if (0 == mv_curr_nam.str.len) break; assert(mv_curr_nam.str.len < MAX_MIDENT_LEN); memcpy(curr_name, mv_curr_nam.str.addr, mv_curr_nam.str.len); curr_name_len = mv_curr_nam.str.len; if (0 != memcmp(curr_name, save_name, trigger_name_len)) /* stop when gv_order returns a string that no longer starts save_name */ break; } while (TRUE); } DECR_AND_POP_MV_STENT(); if (!jnl_format_done && (NULL != lgtrig_reg)) { /* There was no journaled region that had a ^#t update, but found at least one journaled region * so write a LGTRIG logical jnl record there. */ GVTR_SWITCH_REG_AND_HASHT_BIND_NAME(lgtrig_reg); csa = cs_addrs; /* Attach to jnlpool. Normally SET or KILL of the ^#t records take care of this but in * case this is a NO-OP trigger operation that wont update any ^#t records and we still * want to write a TLGTRIG/ULGTRIG journal record. Hence the need to do this. */ JNLPOOL_INIT_IF_NEEDED(csa, csa->hdr, csa->nl, SCNDDBNOUPD_CHECK_TRUE); assert(dollar_tlevel); /* below is needed to set update_trans TRUE on this region even if NO db updates happen to ^#t nodes */ T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_TRIGLOADFAIL); jnl_format(JNL_LGTRIG, NULL, trigger_rec, 0); jnl_format_done = TRUE; } if (wildcard) { UTIL_PRINT_PREFIX_IF_NEEDED(first_gtmio, utilprefix, &utilprefixlen); if (triggers_deleted) { trig_stats[STATS_NOERROR_TRIGFILE]++; util_out_print_gtmio("All existing triggers named !AD (count = !UL) now deleted", FLUSH, orig_name_len, trigger_name, triggers_deleted); } else { trig_stats[STATS_UNCHANGED_TRIGFILE]++; util_out_print_gtmio("No matching triggers of the form !AD found for deletion", FLUSH, orig_name_len, trigger_name); } } else if (triggers_deleted) { /* util_out_print_gtmio of "Deleted trigger named ..." already done so no need to do it again */ trig_stats[STATS_NOERROR_TRIGFILE]++; } else { /* No names match. But treat it as a no-op (i.e. success). */ UTIL_PRINT_PREFIX_IF_NEEDED(first_gtmio, utilprefix, &utilprefixlen); util_out_print_gtmio("Trigger named !AD does not exist", FLUSH, orig_name_len, trigger_name); trig_stats[STATS_UNCHANGED_TRIGFILE]++; } return TRIG_SUCCESS; } int4 trigger_delete(char *trigvn, int trigvn_len, mval *trigger_count, int index) { int count; mval mv_val; mval *mv_val_ptr; char *ptr1; int4 result; int4 retval; stringkey kill_hash, set_hash; int sub_indx; char tmp_trig_str[MAX_BUFF_SIZE]; int4 trig_len; char trig_name[MAX_TRIGNAME_LEN]; int trig_name_len; int tmp_len; char *tt_val[NUM_SUBS]; uint4 tt_val_len[NUM_SUBS]; mval trigger_value; mval trigger_index; mval xecute_index; uint4 xecute_idx; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!gv_cur_region->read_only); /* caller should have already checked this */ assert(cs_addrs->hasht_tree == gv_target); /* should have been set up by caller */ assert(gv_target->root); /* should have been ensured by caller */ mv_val_ptr = &mv_val; MV_FORCE_UMVAL(&trigger_index, index); count = MV_FORCE_UINT(trigger_count); /* build up array of values - needed for comparison in hash stuff */ ptr1 = tmp_trig_str; memcpy(ptr1, trigvn, trigvn_len); ptr1 += trigvn_len; *ptr1++ = '\0'; tmp_len = trigvn_len + 1; /* Assert that BHASH and LHASH are not part of NUM_SUBS calculation (confirms the -2 done in the #define of NUM_SUBS) */ assert(BHASH_SUB == NUM_SUBS); assert(LHASH_SUB == (NUM_SUBS + 1)); for (sub_indx = 0; sub_indx < NUM_SUBS; sub_indx++) { BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigger_index, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx])); trig_len = gvcst_get(&trigger_value) ? trigger_value.str.len : 0; if (0 == trig_len) { if (((TRIGNAME_SUB == sub_indx) || (CMD_SUB == sub_indx) || (CHSET_SUB == sub_indx))) { /* CMD, NAME and CHSET cannot be zero length */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, trigvn_len, trigvn, STRLEN(trigger_subs[sub_indx]), trigger_subs[sub_indx]); } tt_val[sub_indx] = NULL; tt_val_len[sub_indx] = 0; continue; } if (TRIGNAME_SUB == sub_indx) { trig_name_len = MIN(trig_len, MAX_TRIGNAME_LEN); assert(MAX_TRIGNAME_LEN >= trig_len); memcpy(trig_name, trigger_value.str.addr, trig_name_len); tt_val[sub_indx] = NULL; tt_val_len[sub_indx] = 0; continue; } tt_val[sub_indx] = ptr1; tt_val_len[sub_indx] = trig_len; tmp_len += trig_len; if (0 < trig_len) { if (MAX_BUFF_SIZE <= tmp_len) return VAL_TOO_LONG; memcpy(ptr1, trigger_value.str.addr, trig_len); ptr1 += trig_len; } *ptr1++ = '\0'; tmp_len++; } /* Get trigger name, set hash value, and kill hash values from trigger before we delete it. * The values will be used in clean ups associated with the deletion */ /* $get(^#t(GVN,trigger_index,"LHASH") for deletion in cleanup_trigger_hash */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigger_index, trigger_subs[LHASH_SUB], STRLEN(trigger_subs[LHASH_SUB])); if (gvcst_get(mv_val_ptr)) kill_hash.hash_code = (uint4)MV_FORCE_UINT(mv_val_ptr); else { util_out_print_gtmio("The LHASH for global ^!AD does not exist", FLUSH, trigvn_len, trigvn); kill_hash.hash_code = 0; } /* $get(^#t(GVN,trigger_index,"BHASH") for deletion in cleanup_trigger_hash */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigger_index, trigger_subs[BHASH_SUB], STRLEN(trigger_subs[BHASH_SUB])); if (gvcst_get(mv_val_ptr)) set_hash.hash_code = (uint4)MV_FORCE_UINT(mv_val_ptr); else { util_out_print_gtmio("The BHASH for global ^!AD does not exist", FLUSH, trigvn_len, trigvn); set_hash.hash_code = 0; } /* kill ^#t(GVN,trigger_index) */ BUILD_HASHT_SUB_MSUB_CURRKEY(trigvn, trigvn_len, trigger_index); gvcst_kill(TRUE); assert(0 == gvcst_data()); if (1 == count) { /* This is the last trigger for "trigvn" - clean up trigger name, remove #LABEL and #COUNT */ assert(1 == index); BUILD_HASHT_SUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHLABEL, STRLEN(LITERAL_HASHLABEL)); gvcst_kill(TRUE); BUILD_HASHT_SUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT)); gvcst_kill(TRUE); cleanup_trigger_name(trigvn, trigvn_len, trig_name, trig_name_len); cleanup_trigger_hash(trigvn, trigvn_len, tt_val, tt_val_len, &set_hash, &kill_hash, TRUE, index); } else { cleanup_trigger_hash(trigvn, trigvn_len, tt_val, tt_val_len, &set_hash, &kill_hash, TRUE, index); cleanup_trigger_name(trigvn, trigvn_len, trig_name, trig_name_len); if (index != count) { /* Shift the last trigger (index is #COUNT value) to the just deleted trigger's index. * This way count is always accurate and can still be used as the index for new triggers. * Note - there is no dependence on the trigger order, or this technique wouldn't work. */ ptr1 = tmp_trig_str; memcpy(ptr1, trigvn, trigvn_len); ptr1 += trigvn_len; *ptr1++ = '\0'; tmp_len = trigvn_len + 1; for (sub_indx = 0; sub_indx < NUM_TOTAL_SUBS; sub_indx++) { /* $get(^#t(GVN,trigger_count,sub_indx) */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, *trigger_count, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx])); if (gvcst_get(&trigger_value)) { trig_len = trigger_value.str.len; /* set ^#t(GVN,trigger_index,sub_indx)=^#t(GVN,trigger_count,sub_indx) */ SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MVAL(trigvn, trigvn_len, trigger_index, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), trigger_value, result); assert(PUT_SUCCESS == result); } else if (XECUTE_SUB == sub_indx) { /* multi line trigger broken up because it exceeds record size */ for (xecute_idx = 0; ; xecute_idx++) { i2mval(&xecute_index, xecute_idx); BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY(trigvn, trigvn_len, *trigger_count, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), xecute_index); if (!gvcst_get(&trigger_value)) break; SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MSUB_MVAL(trigvn, trigvn_len, trigger_index, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), xecute_index, trigger_value, result); assert(PUT_SUCCESS == result); } assert (xecute_idx >= 2); /* multi-line trigger, indices 0, 1 and 2 MUST be defined */ } else { if (((TRIGNAME_SUB == sub_indx) || (CMD_SUB == sub_indx) || (CHSET_SUB == sub_indx))) { /* CMD, NAME and CHSET cannot be zero length */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, trigvn_len, trigvn, STRLEN(trigger_subs[sub_indx]), trigger_subs[sub_indx]); } /* OPTIONS, PIECES and DELIM can be zero */ trig_len = 0; } if (NUM_SUBS > sub_indx) { tt_val[sub_indx] = ptr1; tt_val_len[sub_indx] = trig_len; tmp_len += trig_len; if (0 < trig_len) { if (MAX_BUFF_SIZE <= tmp_len) { /* Exceeding the temporary buffer is impossible, restart*/ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, trigvn_len, trigvn, STRLEN(trigger_subs[sub_indx]), trigger_subs[sub_indx]); } memcpy(ptr1, trigger_value.str.addr, trig_len); ptr1 += trig_len; } *ptr1++ = '\0'; tmp_len++; } } /* $get(^#t(GVN,trigger_count,"LHASH") for update_trigger_hash_value */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, *trigger_count, trigger_subs[LHASH_SUB], STRLEN(trigger_subs[LHASH_SUB])); if (!gvcst_get(mv_val_ptr)) return PUT_SUCCESS; kill_hash.hash_code = (uint4)MV_FORCE_UINT(mv_val_ptr); /* $get(^#t(GVN,trigger_count,"BHASH") for update_trigger_hash_value */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, *trigger_count, trigger_subs[BHASH_SUB], STRLEN(trigger_subs[BHASH_SUB])); if (!gvcst_get(mv_val_ptr)) return PUT_SUCCESS; set_hash.hash_code = (uint4)MV_FORCE_UINT(mv_val_ptr); /* update hash values from above */ if (VAL_TOO_LONG == (retval = update_trigger_hash_value(trigvn, trigvn_len, tt_val, tt_val_len, &set_hash, &kill_hash, count, index))) return VAL_TOO_LONG; /* fix the value ^#t("#TNAME",^#t(GVN,index,"#TRIGNAME")) to point to the correct "index" */ if (VAL_TOO_LONG == (retval = update_trigger_name_value(tt_val[TRIGNAME_SUB], tt_val_len[TRIGNAME_SUB], index))) return VAL_TOO_LONG; /* kill ^#t(GVN,COUNT) which was just shifted to trigger_index */ BUILD_HASHT_SUB_MSUB_CURRKEY(trigvn, trigvn_len, *trigger_count); gvcst_kill(TRUE); } /* Update #COUNT */ count--; MV_FORCE_UMVAL(trigger_count, count); SET_TRIGGER_GLOBAL_SUB_SUB_MVAL(trigvn, trigvn_len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT), *trigger_count, result); assert(PUT_SUCCESS == result); /* Size of count can only get shorter or stay the same */ } return PUT_SUCCESS; } void trigger_delete_all(mval *trigger_rec, uint4 *trig_stats) { int count; sgmnt_addrs *csa; mval curr_gbl_name; mval after_hash_cycle; int cycle; mval *mv_count_ptr; mval *mv_cycle_ptr; gd_region *reg, *reg_top; int4 result; gd_region *lgtrig_reg; mval trigger_cycle; mval trigger_count; boolean_t this_db_updated; uint4 triggers_deleted; boolean_t jnl_format_done; boolean_t delete_required; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(0 < dollar_tlevel); jnl_format_done = FALSE; lgtrig_reg = NULL; triggers_deleted = 0; for (reg = gd_header->regions, reg_top = reg + gd_header->n_regions; reg < reg_top; reg++) { if (IS_STATSDB_REGNAME(reg)) continue; GVTR_SWITCH_REG_AND_HASHT_BIND_NAME(reg); csa = cs_addrs; if (NULL == csa) /* not BG or MM access method */ continue; /* gv_target now points to ^#t in region "reg" */ DISALLOW_MULTIINST_UPDATE_IN_TP(dollar_tlevel, jnlpool_head, csa, first_sgm_info, TRUE); /* To write the LGTRIG logical jnl record, choose some region that has journaling enabled */ if (!reg->read_only && !jnl_format_done && JNL_WRITE_LOGICAL_RECS(csa)) lgtrig_reg = reg; if (!gv_target->root) continue; /* kill ^#t("#TNAME") */ BUILD_HASHT_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME)); if (0 != gvcst_data()) { /* Issue error if we dont have permissions to touch ^#t global */ if (reg->read_only) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_TRIGMODREGNOTRW, 2, REG_LEN_STR(reg)); gvcst_kill(TRUE); } /* Kill all descendents of ^#t(trigvn, ...) where trigvn is any global with a trigger, * but skip the ^#t("#...",...) entries. Setup ^#t("$") as the key for op_gvorder */ BUILD_HASHT_SUB_CURRKEY(LITERAL_MAXHASHVAL, STRLEN(LITERAL_MAXHASHVAL)); TREF(gv_last_subsc_null) = FALSE; /* We know its not null, but prior state is unreliable */ this_db_updated = FALSE; while (TRUE) { op_gvorder(&curr_gbl_name); /* quit:$length(curr_gbl_name)=0 */ if (0 == curr_gbl_name.str.len) break; count = cycle = 0; /* $get(^#t(curr_gbl_name,#COUNT)) */ BUILD_HASHT_SUB_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT)); if (TRUE == (delete_required = gvcst_get(&trigger_count))) /* inline assignment */ { mv_count_ptr = &trigger_count; count = MV_FORCE_UINT(mv_count_ptr); } /* $get(^#t(curr_gbl_name,#CYCLE)) */ BUILD_HASHT_SUB_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len, LITERAL_HASHCYCLE, STRLEN(LITERAL_HASHCYCLE)); if (!gvcst_get(&trigger_cycle)) { /* Found hasht record, there must be #CYCLE */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(12) MAKE_MSG_WARNING(ERR_TRIGDEFBAD), 6, curr_gbl_name.str.len, curr_gbl_name.str.addr, curr_gbl_name.str.len, curr_gbl_name.str.addr, LEN_AND_LIT("\"#CYCLE\""), ERR_TEXT, 2, RTS_ERROR_TEXT("#CYCLE field is missing")); send_msg_csa(CSA_ARG(csa) VARLSTCNT(12) MAKE_MSG_WARNING(ERR_TRIGDEFBAD), 6, curr_gbl_name.str.len, curr_gbl_name.str.addr, curr_gbl_name.str.len, curr_gbl_name.str.addr, LEN_AND_LIT("\"#CYCLE\""), ERR_TEXT, 2, RTS_ERROR_TEXT("#CYCLE field is missing")); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); } else { mv_cycle_ptr = &trigger_cycle; cycle = MV_FORCE_UINT(mv_cycle_ptr); cycle++; MV_FORCE_MVAL(&trigger_cycle, cycle); } if (!delete_required) { /* $order(^#t(curr_gbl_name,#LABEL)); should be the NULL string if #COUNT not found * Use #LABEL vs #CYCLE because MUPIP TRIGGER -UPGRADE unconditionally inserts it */ BUILD_HASHT_SUB_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len, LITERAL_HASHLABEL, STRLEN(LITERAL_HASHLABEL)); op_gvorder(&after_hash_cycle); /* quit:$length(after_hash_cycle)=0 */ if (0 != after_hash_cycle.str.len) { /* Found hasht record after #LABEL, but #COUNT is not defined; Force removal */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(12) MAKE_MSG_WARNING(ERR_TRIGDEFBAD), 6, curr_gbl_name.str.len, curr_gbl_name.str.addr, curr_gbl_name.str.len, curr_gbl_name.str.addr, LEN_AND_LIT("\"#COUNT\""), ERR_TEXT, 2, RTS_ERROR_TEXT("#COUNT field is missing. Skipped in results")); send_msg_csa(CSA_ARG(csa) VARLSTCNT(12) MAKE_MSG_WARNING(ERR_TRIGDEFBAD), 6, curr_gbl_name.str.len, curr_gbl_name.str.addr, curr_gbl_name.str.len, curr_gbl_name.str.addr, LEN_AND_LIT("\"#COUNT\""), ERR_TEXT, 2, RTS_ERROR_TEXT("#COUNT field is missing. Skipped in results")); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); delete_required = TRUE; } } if (delete_required) { /* Now that we know there is something to kill, check if we have permissions to touch ^#t global */ if (reg->read_only) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_TRIGMODREGNOTRW, 2, REG_LEN_STR(reg)); if (!jnl_format_done && JNL_WRITE_LOGICAL_RECS(csa)) { jnl_format(JNL_LGTRIG, NULL, trigger_rec, 0); jnl_format_done = TRUE; } /* kill ^#t(curr_gbl_name); kills ^#t(curr_gbl_name,"#TRHASH") as well */ BUILD_HASHT_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len); gvcst_kill(TRUE); if (0 < cycle) { /* set ^#t(curr_gbl_name,#CYCLE)=trigger_cycle */ SET_TRIGGER_GLOBAL_SUB_SUB_MVAL(curr_gbl_name.str.addr, curr_gbl_name.str.len, LITERAL_HASHCYCLE, STRLEN(LITERAL_HASHCYCLE), trigger_cycle, result); assert(PUT_SUCCESS == result); } this_db_updated = TRUE; triggers_deleted += count; } /* else there is nothing under the hasht record, leave #CYCLE alone */ /* get ready for op_gvorder() call for next trigger under ^#t */ BUILD_HASHT_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len); } if (this_db_updated) { csa->incr_db_trigger_cycle = TRUE; if (dollar_ztrigger_invoked) { /* increment db_dztrigger_cycle so that next gvcst_put/gvcst_kill in this transaction, * on this region, will re-read. See trigger_update.c for a comment on why it is okay * for db_dztrigger_cycle to be incremented more than once in the same transaction */ DBGTRIGR((stderr, "trigger_delete_all: CSA->db_dztrigger_cycle=%d\n", csa->db_dztrigger_cycle)); csa->db_dztrigger_cycle++; } } } if (!jnl_format_done && (NULL != lgtrig_reg)) { /* There was no journaled region that had a ^#t update, but found at least one journaled region * so write a LGTRIG logical jnl record there. */ GVTR_SWITCH_REG_AND_HASHT_BIND_NAME(lgtrig_reg); csa = cs_addrs; JNLPOOL_INIT_IF_NEEDED(csa, csa->hdr, csa->nl, SCNDDBNOUPD_CHECK_TRUE); /* see previous use for why it is needed */ assert(dollar_tlevel); T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_TRIGLOADFAIL); /* needed to set update_trans TRUE on this region * even if NO db updates happen to ^#t nodes. */ jnl_format(JNL_LGTRIG, NULL, trigger_rec, 0); jnl_format_done = TRUE; } if (triggers_deleted) { util_out_print_gtmio("All existing triggers (count = !UL) deleted", FLUSH, triggers_deleted); trig_stats[STATS_DELETED] += triggers_deleted; trig_stats[STATS_NOERROR_TRIGFILE]++; } else { util_out_print_gtmio("No matching triggers found for deletion", FLUSH); trig_stats[STATS_UNCHANGED_TRIGFILE]++; } } #endif /* GTM_TRIGGER */ fis-gtm-V7.0-005/sr_unix/trigger_delete_protos.h0000644000032200000250000000256614342376330020616 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2018 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. * * * ****************************************************************/ #ifndef MU_TRIG_DELETE_PROTOS_H_INCLUDED #define MU_TRIG_DELETE_PROTOS_H_INCLUDED void cleanup_trigger_hash(char *trigvn, int trigvn_len, char **values, uint4 *value_len, stringkey *set_hash, stringkey *kill_hash, boolean_t del_kill_hash, int match_index); void cleanup_trigger_name(char *trigvn, int trigvn_len, char *trigger_name, int trigger_name_len); STATICFNDCL int4 update_trigger_hash_value(char *trigvn, int trigvn_len, char **values, uint4 *value_len, stringkey *set_hash, stringkey *kill_hash, int old_trig_index, int new_trig_index); STATICFNDCL int4 update_trigger_name_value(char *trig_name, int trig_name_len, int new_trig_index); boolean_t trigger_delete_name(mval *trigger_rec, uint4 *trig_stats); int4 trigger_delete(char *trigvn, int trigvn_len, mval *trigger_count, int index); void trigger_delete_all(mval *trigger_rec, uint4 *trig_stats); #endif fis-gtm-V7.0-005/sr_unix/trigger_fill_xecute_buffer.h0000644000032200000250000000113514342376330021571 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef TRIGGER__FILL_XECUTE_BUFFER_H_INCLUDED #define TRIGGER__FILL_XECUTE_BUFFER_H_INCLUDED int trigger_fill_xecute_buffer(gv_trigger_t *trigdsc); #endif fis-gtm-V7.0-005/sr_unix/trigger_gbl_fill_xecute_buffer.c0000644000032200000250000002043614342376335022422 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2022 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" #ifdef GTM_TRIGGER #include "gdsroot.h" /* for gdsfhead.h */ #include "gdsbt.h" /* for gdsfhead.h */ #include "gdsfhead.h" #include "gvcst_protos.h" #include #include "gv_trigger.h" #include "mv_stent.h" /* for COPY_SUBS_TO_GVCURRKEY macro (within BUILD_HASHT_...) */ #include "gvsub2str.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include "format_targ_key.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include "op.h" #include "trigger.h" #include "trigger_gbl_fill_xecute_buffer.h" #include "gtm_trigger_trc.h" #include "mvalconv.h" #include "memcoherency.h" #include "t_retry.h" #include "gtmimagename.h" #include "filestruct.h" /* for FILE_INFO, needed by REG2CSA */ #include "have_crit.h" #include "repl_msg.h" #include "gtmsource.h" /* for jnlpool_addrs */ LITREF mval literal_ten; error_def(ERR_ASSERT); error_def(ERR_GTMASSERT); error_def(ERR_GTMASSERT2); error_def(ERR_GTMCHECK); error_def(ERR_MEMORY); error_def(ERR_OUTOFSPACE); error_def(ERR_TRIGDEFBAD); GBLREF sgmnt_data_ptr_t cs_data; GBLREF sgmnt_addrs *cs_addrs; GBLREF uint4 dollar_tlevel; GBLREF gd_region *gv_cur_region; GBLREF gv_key *gv_currkey; #ifdef DEBUG GBLREF boolean_t is_updproc; #endif GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF unsigned int t_tries; GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; LITREF char *trigger_subs[]; STATICDEF char *xecute_buff; STATICFNDCL CONDITION_HANDLER(trigger_gbl_fill_xecute_buffer_ch); /* The trigger_gbl_fill_xecute_buffer() routine below malloc()s storage for our trigger buffer buffer below but any one * of the various database calls can cause a restart that "loses" the buffer. So we wrap the call will this condition * handler to release the buffer if one was allocated before moving on to the next handler. */ STATICFNDEF CONDITION_HANDLER(trigger_gbl_fill_xecute_buffer_ch) { START_CH(TRUE); if (!DUMPABLE && (NULL != xecute_buff)) free(xecute_buff); NEXTCH; } char *trigger_gbl_fill_xecute_buffer(char *trigvn, int trigvn_len, mval *trig_index, mval *first_rec, int4 *xecute_len) { mval data_val; mval index, key_val, *val_ptr; int4 len, xecute_buff_len; int4 num; int4 trgindx; unsigned char util_buff[MAX_TRIG_UTIL_LEN]; int4 util_len; char *xecute_buff_ptr; mval xecute_index; DEBUG_ONLY(int gvt_cycle;) DEBUG_ONLY(int csd_cycle;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(0 < dollar_tlevel); /* Too be added later when it stops breaking MUPIP SELECT & $ZTRIGGER("SELECT"..) */ xecute_buff = NULL; ESTABLISH_RET(trigger_gbl_fill_xecute_buffer_ch, NULL); index = *trig_index; DBGTRIGR((stderr, "trigger_gbl_fill_xecute_buffer: entry $tlevel:%d\tindex:%d\t:crit:%d\tkv:%d\n", dollar_tlevel, mval2i(&index), have_crit(CRIT_HAVE_ANY_REG), key_val.str.len)); if (NULL != first_rec) { DBGTRIGR((stderr, "trigger_gbl_fill_xecute_buffer: NULL != first_rec\n")); xecute_buff_len = first_rec->str.len; assert(MAX_XECUTE_LEN >= xecute_buff_len); xecute_buff = malloc(xecute_buff_len); memcpy(xecute_buff, first_rec->str.addr, xecute_buff_len); } else { /* First check for a single record xecute string */ DBGTRIGR((stderr, "trigger_gbl_fill_xecute_buffer: First check for a single record xecute string\n")); BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, index, trigger_subs[XECUTE_SUB], STRLEN(trigger_subs[XECUTE_SUB])); if (gvcst_get(&key_val)) { xecute_buff_len = key_val.str.len; assert(MAX_XECUTE_LEN >= xecute_buff_len); xecute_buff = malloc(xecute_buff_len); memcpy(xecute_buff, key_val.str.addr, xecute_buff_len); *xecute_len = xecute_buff_len; DBGTRIGR((stderr, "trigger_gbl_fill_xecute_buffer: Found a single record xecute string of %d bytes\n", xecute_buff_len)); REVERT; return xecute_buff; } else { /* No single line trigger exists. See if multi-line trigger exists. The form is ^#t(gbl,indx,XECUTE,n) * so can be easily tested for with $DATA(). */ DBGTRIGR((stderr, "trigger_gbl_fill_xecute_buffer: No single record xecute string(%d), try for multiline\n", key_val.str.len)); op_gvdata(&data_val); if ((literal_ten.m[0] != data_val.m[0]) || (literal_ten.m[1] != data_val.m[1])) { /* The process' view of the triggers is likely stale. Restart to be safe. * Triggers can be invoked only by GT.M and Update process. We expect to see GT.M processes to * restart due to concurrent trigger changes. The Update Process should only restart if it is a * supplementary instance. Assert accordingly. Note similar asserts occur in t_end.c and * tp_tend.c. */ assert(!is_updproc || (jnlpool && jnlpool->repl_inst_filehdr->is_supplementary && !jnlpool->jnlpool_ctl->upd_disabled)); DBGTRIGR((stderr, "trigger_gbl_fill_xecute_buffer: multiline not found, retry\n")); /* Assert that the cycle has changed but in order to properly do the assert, we need a memory * barrier since cs_data->db_trigger_cycle could be stale in our cache. */ DEBUG_ONLY(SHM_READ_MEMORY_BARRIER); /* Vars in locals so can look at them in the core instead of at constantly changing numbers */ DEBUG_ONLY(gvt_cycle = gv_target->db_trigger_cycle); DEBUG_ONLY(csd_cycle = cs_data->db_trigger_cycle); assert(csd_cycle > gvt_cycle); if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); trgindx = mval2i(&index); SET_PARAM_STRING(util_buff, util_len, trgindx, ",\"XECUTE\""); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, trigvn_len, trigvn, util_len, util_buff); } } /* Multi-line triggers exist */ num = 0; i2mval(&xecute_index, num); BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY(trigvn, trigvn_len, index, trigger_subs[XECUTE_SUB], STRLEN(trigger_subs[XECUTE_SUB]), xecute_index); if (!gvcst_get(&key_val)) { /* There has to be an XECUTE string or else it is a retry situation (due to concurrent updates) */ DBGTRIGR((stderr, "trigger_gbl_fill_xecute_buffer: problem getting multiline record count, retry\n")); if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); trgindx = mval2i(&index); SET_PARAM_STRING(util_buff, util_len, trgindx, ",\"XECUTE\""); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, trigvn_len, trigvn, util_len, util_buff); } val_ptr = &key_val; xecute_buff_len = mval2i(val_ptr); assert((0 < xecute_buff_len) && (MAX_XECUTE_LEN >= xecute_buff_len)); xecute_buff_ptr = xecute_buff = malloc(xecute_buff_len); len = 0; while (len < xecute_buff_len) { i2mval(&xecute_index, ++num); BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY(trigvn, trigvn_len, index, trigger_subs[XECUTE_SUB], STRLEN(trigger_subs[XECUTE_SUB]), xecute_index); if (!gvcst_get(&key_val)) break; if (xecute_buff_len < (len + key_val.str.len)) { /* The DB string total is longer than the length stored at index 0 -- something is wrong. * Most likely a retry necessary due to concurrent changes. */ DBGTRIGR((stderr, "trigger_gbl_fill_xecute_buffer: problem getting multiline component, retry\n")); free(xecute_buff); if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); SET_PARAM_STRING(util_buff, util_len, num, ",\"XECUTE\""); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, trigvn_len, trigvn, util_len, util_buff); } memcpy(xecute_buff_ptr, key_val.str.addr, key_val.str.len); xecute_buff_ptr += key_val.str.len; len += key_val.str.len; } } *xecute_len = xecute_buff_len; REVERT; return xecute_buff; } #endif fis-gtm-V7.0-005/sr_unix/trigger_gbl_fill_xecute_buffer.h0000644000032200000250000000124414342376330022416 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef TRIGGER_GBL_FILL_XECUTE_BUFFER_H_INCLUDED #define TRIGGER_GBL_FILL_XECUTE_BUFFER_H_INCLUDED char *trigger_gbl_fill_xecute_buffer(char *trigvn, int trigvn_len, mval *trig_index, mval *first_rec, int4 *xecute_len); #endif fis-gtm-V7.0-005/sr_unix/trigger_incr_cycle.c0000644000032200000250000000416614342376335020056 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2022 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 "gdsroot.h" /* for gdsfhead.h */ #include "gdsbt.h" /* for gdsfhead.h */ #include "gdsfhead.h" /* For gvcst_protos.h */ #include "gvcst_protos.h" #include /* for gv_trigger.h */ #include "gv_trigger.h" #include "trigger.h" #include "trigger_incr_cycle.h" #include "gdsblk.h" #include "mv_stent.h" /* for COPY_SUBS_TO_GVCURRKEY macro */ #include "gvsub2str.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include "format_targ_key.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include "mvalconv.h" /* Needed for MV_FORCE_* */ #include "gtm_trigger_trc.h" GBLREF gd_region *gv_cur_region; GBLREF gv_key *gv_currkey; void trigger_incr_cycle(char *trigvn, int trigvn_len) { uint4 cycle; char *cycle_ptr, cycle_str[MAX_DIGITS_IN_INT + 1]; char *ptr, *ptr1; int4 result; mval trigger_cycle, *mv_trig_cycle_ptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; BUILD_HASHT_SUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHCYCLE, STRLEN(LITERAL_HASHCYCLE)); if (gvcst_get(&trigger_cycle)) { mv_trig_cycle_ptr = &trigger_cycle; cycle = MV_FORCE_INT(mv_trig_cycle_ptr); assert((0 < cycle) && (MAXPOSINT4 > cycle)); if (MAXPOSINT4 == cycle) cycle = 1; /* Reset; gv_trigger uses cycle as a simple equality; higher/lower is not a concern */ cycle++; INT2STR(cycle, cycle_str); cycle_ptr = cycle_str; } else cycle_ptr = INITIAL_CYCLE; DBGTRIGR((stderr, "trigger_incr_cycle(): %s #CYCLE=%s\n", trigvn, cycle_ptr)); SET_TRIGGER_GLOBAL_SUB_SUB_STR(trigvn, trigvn_len, LITERAL_HASHCYCLE, STRLEN(LITERAL_HASHCYCLE), cycle_ptr, STRLEN(cycle_ptr), result); assert(PUT_SUCCESS == result); } fis-gtm-V7.0-005/sr_unix/trigger_incr_cycle.h0000644000032200000250000000111214342376330020042 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef TRIGGER_INCR_CYCLE_H_INCLUDED #define TRIGGER_INCR_CYCLE_H_INCLUDED void trigger_incr_cycle(char *trigvn, int trigvn_len); #endif fis-gtm-V7.0-005/sr_unix/trigger_locate_andor_load.c0000644000032200000250000003024714342376335021374 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2020 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" #ifdef GTM_TRIGGER #include "error.h" #include "gdsroot.h" /* for gdsfhead.h */ #include "gdsbt.h" /* for gdsfhead.h */ #include "gdsfhead.h" #include "gvcst_protos.h" #include #include "gv_trigger.h" #include "gtm_trigger.h" #include "trigger.h" #include "min_max.h" #include "filestruct.h" /* for INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED (FILE_INFO) */ #include "gdscc.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "tp.h" #include "op.h" #include "op_tcommit.h" #include "tp_frame.h" #include "gvnh_spanreg.h" #include "trigger_read_andor_locate.h" #include "repl_msg.h" /* for gtmsource.h */ #include "gtmsource.h" /* for jnlpool_addrs_ptr_t */ GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF gd_addr *gd_header; GBLREF rtn_tabent *rtn_names, *rtn_names_end; GBLREF tp_frame *tp_pointer; GBLREF gv_key *gv_currkey; GBLREF gd_region *gv_cur_region; GBLREF sgm_info *sgm_info_ptr; GBLREF jnlpool_addrs_ptr_t jnlpool; LITREF mval literal_batch; error_def(ERR_TRIGNAMENF); /* Routine to locate the named trigger and if not loaded, to go ahead and load it. * * The following sitations can exist: * * 1. No trigger by the given name is loaded. For this situation, we need to locate and load the trigger. * 2. Trigger is loaded. Note this routine does no validation of the trigger but just returns whatever is * currently loaded. * * Note, this routine is for locating a trigger that is not to be run and merrily located so its embedded source * can be made available. Other trigger related routines are: * * a. trigger_source_read_andor_verify() - Verifies has current trigger object (primary usage op_setbrk()). * b. trigger_fill_xecute_buffer() - Same verification but makes sure source is available - typically used when * trigger is about to be driven. * * Note this routine has similar components to trigger_source_read_andor_verify() and its subroutines so updates * to that routine should also be check if they apply here and vice versa. This routine is lighter weight without * the overhead of being in a transaction unless it has to load a trigger in which case it calls the full routine * trigger_source_read_andor_verify(). Since a trigger's source is embedded in a trigger, we only need to locate * the trigger to have access to the source for $TEXT() type services. The primary issue this separate mechanism * solves is when an error occurs and trigger is on the M stack, error handling will attempt to locate the source * line in the trigger where it was as part of filling in the $STACK() variable. The mini-transaction created by * trigger_source_read_andor_verify() of course uses the critical section but that use, if the process is also * holding a lock perhaps in use by another process holding the lock we need, creates a deadlock. So we avoid * doing anything with triggers in the event of an error in this fashion. */ int trigger_locate_andor_load(mstr *trigname, rhdtyp **rtn_vec) { mstr_len_t origlen; char *ptr, *ptr_beg, *ptr_top; boolean_t runtime_disambiguator_specified; gd_region *reg; mstr regname, gbl; mident rtn_name; gd_region *save_gv_cur_region; gv_key_buf save_currkey; gv_namehead *gvt; gv_namehead *save_gv_target; gvnh_reg_t *gvnh_reg; gv_trigger_t *trigdsc; gvt_trigger_t *gvt_trigger; rtn_tabent *rttabent; sgm_info *save_sgm_info_ptr; jnlpool_addrs_ptr_t save_jnlpool; rhdtyp *rtn_vector; sgmnt_addrs *csa, *regcsa; sgmnt_data_ptr_t csd; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(NULL != trigname); assert((NULL != trigname->addr) && (0 != trigname->len)); if (NULL == gd_header) gvinit(); DBGTRIGR((stderr, "trigger_locate_andor_load: Entered with $tlevel=%d, $trigdepth=%d\n", dollar_tlevel, gtm_trigger_depth)); /* * Input parameter "trigname" is of the form * a) <21-BYTE-MAX-TRUNCATED-GBLNAME>##[RUNTIME-DISAMBIGUATOR][/REGION-NAME] OR * b) <28-BYTE-USER-SPECIFIED-TRIGNAME>#[RUNTIME-DISAMBIGUATOR][/REGION-NAME] * where * <21-BYTE-MAX-TRUNCATED-GBLNAME># OR <28-BYTE-USER-SPECIFIED-TRIGNAME> is the * auto-generated or user-specified trigger name we are searching for * RUNTIME-DISAMBIGUATOR is the unique string appended at the end by the runtime to distinguish * multiple triggers in different regions with the same auto-generated or user-given name * REGION-NAME is the name of the region in the gld where we specifically want to search for trigger names * [] implies optional parts * * Example usages are * x# : trigger routine user-named "x" * x#1# : trigger routine auto-named "x#1" * x#1#A : trigger routine auto-named "x#1" but also runtime disambiguated by "#A" at the end * x#/BREG : trigger routine user-named "x" in region BREG * x#A/BREG : trigger routine user-named "x", runtime disambiguated by "#A", AND in region BREG * x#1#/BREG : trigger routine auto-named "x#1" in region BREG * x#1#A/BREG : trigger routine auto-named "x#1", runtime disambiguated by "#A", AND in region BREG */ /* First lets locate the trigger. Try simple way first - lookup in routine name table. * But "find_rtn_tabent" function has no clue about REGION-NAME so remove /REGION-NAME (if any) before invoking it. */ regname.len = 0; reg = NULL; origlen = trigname->len; /* Save length in case need to restore it later */ for (ptr_beg = trigname->addr, ptr_top = ptr_beg + trigname->len, ptr = ptr_top - 1; ptr >= ptr_beg; ptr--) { /* If we see a '#' and have not yet seen a '/' we are sure no region-name disambiguator has been specified */ if ('#' == *ptr) break; if ('/' == *ptr) { trigname->len = ptr - trigname->addr; ptr++; regname.addr = ptr; regname.len = ptr_top - ptr; reg = find_region(®name); /* find region "regname" in "gd_header" */ if (NULL == reg) { /* Specified region-name is not present in current gbldir. * Treat non-existent region name as if trigger was not found. */ ISSUE_TRIGNAMENF_ERROR_IF_APPROPRIATE(trigname); return TRIG_FAILURE_RC; } break; } } if (NULL != *rtn_vec) { rtn_vector = *rtn_vec; rttabent = rtn_names; } else if (find_rtn_tabent(&rttabent, trigname)) rtn_vector = rttabent->rt_adr; else rtn_vector = NULL; DBGTRIGR((stderr, "trigger_locate_andor_load: routine was %sfound (1)\n", (NULL == rtn_vector)?"not ":"")); /* If we have the trigger routine header, do some validation on it, else keep looking */ SAVE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); runtime_disambiguator_specified = ('#' != trigname->addr[trigname->len - 1]); if (!runtime_disambiguator_specified && (NULL != reg)) { /* Region-name has been specified and no runtime-disambiguator specified. Need to further refine the * search done by find_rtn_tabent to focus on the desired region in case multiple routines with the same * trigger name (but different runtime-disambiguators) exist. */ rtn_name.len = MIN(trigname->len, MAX_MIDENT_LEN); rtn_name.addr = trigname->addr; if (!reg->open) gv_init_reg(reg, NULL); /* Open the region before obtaining "csa" */ regcsa = &FILE_INFO(reg)->s_addrs; assert('#' == rtn_name.addr[rtn_name.len - 1]); for ( ; rttabent <= rtn_names_end; rttabent++) { if ((rttabent->rt_name.len < rtn_name.len) || memcmp(rttabent->rt_name.addr, rtn_name.addr, rtn_name.len)) { /* Past the list of routines with same name as trigger but different runtime disambiguators */ rtn_vector = NULL; break; } rtn_vector = rttabent->rt_adr; trigdsc = (gv_trigger_t *)rtn_vector->trigr_handle; gvt_trigger = trigdsc->gvt_trigger; gvt = gvt_trigger->gv_target; /* Target region and trigger routine's region do not match, continue */ if (gvt->gd_csa != regcsa) continue; /* Check if global name associated with the trigger is indeed mapped to the corresponding region * by the gld. If not treat this case as if the trigger is invisible and move on */ gbl.addr = gvt->gvname.var_name.addr; gbl.len = gvt->gvname.var_name.len; TP_CHANGE_REG_IF_NEEDED(gvt->gd_csa->region); csa = cs_addrs; csd = csa->hdr; COMPUTE_HASH_MNAME(&gvt->gvname); GV_BIND_NAME_ONLY(gd_header, &gvt->gvname, gvnh_reg); /* does tp_set_sgm() */ if (((NULL == gvnh_reg->gvspan) && (gv_cur_region != reg)) || ((NULL != gvnh_reg->gvspan) && !gvnh_spanreg_ismapped(gvnh_reg, gd_header, reg))) continue; /* Target region and trigger routine's region match, break (this check is a formality) */ if (gvt->gd_csa == regcsa) break; } } csa = NULL; if (NULL == rtn_vector) { /* If runtime disambiguator was specified and routine is not found, look no further. * Otherwise, look for it in the #t global of any (or specified) region in current gbldir. */ if (0 < origlen) trigname->len = origlen; /* Restore length to include region disambiguator */ DBGTRIGR((stderr, "trigger_locate_andor_load: find trigger by name without disambiguator\n")); if (runtime_disambiguator_specified || (TRIG_FAILURE_RC == trigger_source_read_andor_verify(trigname, &rtn_vector))) { RESTORE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); ISSUE_TRIGNAMENF_ERROR_IF_APPROPRIATE(trigname); return TRIG_FAILURE_RC; } trigdsc = (gv_trigger_t *)rtn_vector->trigr_handle; assert(NULL != rtn_vector); } else { /* Have a routine header addr. From that we can get the gv_trigger_t descriptor and from that, the * gvt_trigger and other necessities. */ DBGTRIGR((stderr, "trigger_locate_andor_load: routine header found\n")); trigdsc = (gv_trigger_t *)rtn_vector->trigr_handle; gvt_trigger = trigdsc->gvt_trigger; /* We now know our base block now */ gvt = gv_target = gvt_trigger->gv_target; /* gv_target contains global name */ gbl.addr = gvt->gvname.var_name.addr; gbl.len = gvt->gvname.var_name.len; TP_CHANGE_REG_IF_NEEDED(gvt->gd_csa->region); csa = cs_addrs; csd = csa->hdr; if (runtime_disambiguator_specified && (NULL != reg)) { /* Runtime-disambiguator has been specified and routine was found. But region-name-disambiguator * has also been specified. Check if found routine is indeed in the specified region. If not * treat it as a failure to find the trigger. */ if (!reg->open) gv_init_reg(reg, NULL); if (&FILE_INFO(reg)->s_addrs != csa) { RESTORE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); ISSUE_TRIGNAMENF_ERROR_IF_APPROPRIATE(trigname); return TRIG_FAILURE_RC; } /* Check if global name is indeed mapped to this region by the gld. If not treat this case as * if the trigger is invisible and issue an error */ COMPUTE_HASH_MNAME(&gvt->gvname); GV_BIND_NAME_ONLY(gd_header, &gvt->gvname, gvnh_reg); /* does tp_set_sgm() */ if (((NULL == gvnh_reg->gvspan) && (gv_cur_region != reg)) || ((NULL != gvnh_reg->gvspan) && !gvnh_spanreg_ismapped(gvnh_reg, gd_header, reg))) { RESTORE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); ISSUE_TRIGNAMENF_ERROR_IF_APPROPRIATE(trigname); return TRIG_FAILURE_RC; } } assert(csd == cs_data); } DBGTRIGR((stderr, "trigger_locate_andor_load: leaving with source from rtnhdr 0x%lx\n", (*rtn_vec) ? (*((rhdtyp **)rtn_vec))->trigr_handle : NULL)); RESTORE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); assert(NULL != rtn_vector); assert(trigdsc == rtn_vector->trigr_handle); *rtn_vec = rtn_vector; return 0; } #endif /* GTM_TRIGGER */ fis-gtm-V7.0-005/sr_unix/trigger_parse.c0000644000032200000250000012351114342376335017052 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2022 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_ctype.h" #include "gtm_string.h" #include "cli.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include #include "gv_trigger.h" #include "gtm_trigger.h" #include "trigger.h" #include "trigger_parse_protos.h" #include "trigger_scan_string.h" #include "zshow.h" /* for zwr2format() prototype */ #include "min_max.h" #include "util.h" #include "subscript.h" #include "cli_parse.h" #include "compiler.h" #include "gtm_utf8.h" #include "gtmctype.h" GBLREF CLI_ENTRY *cmd_ary; GBLREF gd_region *gv_cur_region; GBLREF boolean_t is_updproc; GBLREF CLI_ENTRY trigger_cmd_ary[]; GBLREF volatile boolean_t timer_in_handler; #define BITS_PER_INT (SIZEOF(uint4) * 8) /* Number of bits in an integer */ #define MAX_PIECE_VALUE ((BITS_PER_INT * 1024) - 1) /* Largest value allowed in -pieces string */ #define MAX_PIECE_INT ((MAX_PIECE_VALUE + 1) / 32) /* Number of integers it takes to hold MAX_PIECE_VALUE bits */ #define MAX_PIECE_CHARS (MAX_PIECE_INT * 4) /* Number of 8-bit bytes in MAX_PIECE_INT integers */ #define MAX_LVN_COUNT MAX_GVSUBSCRIPTS /* Maximum number of "lvn=" in trigger subscript */ #define MAX_OPTIONS_LEN 1024 /* Maximum size of the "options" string */ #define MAX_DCHAR_LEN 1024 /* Maximum size of $C or $ZCH string */ #define MAX_DELIM_LEN 1024 /* Maximum size of the string for a delimiter - $C, $ZCH, "x", ... */ #define MAX_ERROR_MSG_LEN 72 #define TRIGR_PRECOMP_RTNNAME "trigcomptest#" #define CONV_CH(PTR, STR, LEN) \ { \ if (PRINTABLE(*PTR)) \ { \ STR[0] = '"'; \ STR[1] = *PTR; \ STR[2] = '"'; \ LEN = 3; \ } else \ format2zwr((sm_uc_ptr_t)PTR, 1, STR, &LEN); \ } #define ERROR_MSG_RETURN(STR, LEN, PTR) \ { \ assertpro(!is_updproc); /* LGTRIG records should not have parsing errors */ \ CONV_STR_AND_PRINT(STR, LEN, PTR); \ return FALSE; \ } #define ERROR_STR_RETURN(STR) \ { \ assertpro(!is_updproc); /* LGTRIG records should not have parsing errors */ \ util_out_print_gtmio(STR, FLUSH); \ return FALSE; \ } #define CONV_CH_AND_PRINT(STR, PTR) \ { \ int out_len; \ unsigned char out_str[64]; /* Plenty of room for $ZCH(xxx) */ \ \ out_len = 64; \ CONV_CH(PTR, out_str, out_len); \ util_out_print_gtmio(STR, FLUSH, out_len, out_str); \ } #define CONV_CH_AND_STR_AND_PRINT(STR, PTR1, LEN, PTR2) \ { \ int out_len1, out_len2; \ unsigned char out_str1[64]; /* Plenty of room for $ZCH(xxx) */ \ unsigned char out_str2[MAX_ZWR_EXP_RATIO * OUT_BUFF_SIZE]; \ \ out_len1 = 64; \ CONV_CH(PTR1, out_str1, out_len1); \ out_len2 = MAX_ZWR_EXP_RATIO * OUT_BUFF_SIZE; \ CONV_TO_ZWR(LEN, PTR2, out_len2, out_str2); \ util_out_print_gtmio(STR, FLUSH, out_len1, out_str1, out_len2, out_str2); \ } #define CONV_NUM_AND_STR_AND_PRINT(STR, NUM, LEN, PTR) \ { \ int out_len; \ unsigned char out_str[MAX_ZWR_EXP_RATIO * OUT_BUFF_SIZE]; \ \ out_len = MAX_ZWR_EXP_RATIO * OUT_BUFF_SIZE; \ CONV_TO_ZWR(LEN, PTR, out_len, out_str); \ util_out_print_gtmio(STR, FLUSH, NUM, out_len, out_str); \ } #define UPDATE_DST(PTR, LEN, HAVE_STAR, DST_PTR, DST_LEN, MAX_LEN) \ { \ if (!HAVE_STAR) \ { \ if (MAX_LEN < ++DST_LEN) \ { \ util_out_print_gtmio("Trigger definition too long", FLUSH); \ return FALSE; \ } \ *DST_PTR++ = *PTR++; \ } \ else PTR++; \ LEN--; \ } #define UPDATE_TRIG_PTRS(VAL, NEXT_VAL, LEN, MAX_OUTPUT_LEN) \ { \ NEXT_VAL = VAL + LEN; \ if (0 > --MAX_OUTPUT_LEN) \ { \ util_out_print_gtmio("Trigger definition too long", FLUSH); \ return FALSE; \ } \ *NEXT_VAL++ = '\0'; \ } #define PROCESS_NUMERIC(PTR, LEN, HAVE_STAR, DST_PTR, DST_LEN, MAX_LEN) \ { \ UPDATE_DST(PTR, LEN, HAVE_STAR, DST_PTR, DST_LEN, MAX_LEN); \ while (ISDIGIT_ASCII(*PTR)) \ { \ if (!HAVE_STAR) \ { \ if (MAX_LEN < ++DST_LEN) \ { \ util_out_print_gtmio("Subscript too long", FLUSH); \ return FALSE; \ } \ *DST_PTR++ = *PTR++; \ } else \ PTR++; \ LEN--; \ } \ } #define PROCESS_STRING(PTR, LEN, HAVE_STAR, DST_PTR, DST_LEN, MAX_LEN) \ { \ char *ptr1; \ int add_len; \ \ ptr1 = scan_to_end_quote(PTR, LEN, MAX_LEN); \ if (NULL == ptr1) \ { \ util_out_print_gtmio("Invalid string", FLUSH); \ return FALSE; \ } \ add_len = (int)(ptr1 - PTR); \ LEN -= add_len; \ if (!HAVE_STAR) \ { \ if (MAX_LEN < (DST_LEN + add_len)) \ { \ util_out_print_gtmio("Trigger definition too long", FLUSH); \ return FALSE; \ } \ memcpy(DST_PTR, PTR, add_len); \ DST_PTR += add_len; \ DST_LEN += add_len; \ } \ PTR = ptr1; \ } #define PROCESS_AND_GET_NUMERIC(PTR, LEN, HAVE_STAR, DST_PTR, DST_LEN, NUM, MAX_LEN) \ { \ char *ptr1; \ int add_len; \ \ ptr1 = PTR; \ A2I(ptr1, PTR + LEN, NUM); \ add_len = (int)(ptr1 - PTR); \ LEN -= add_len; \ if (!HAVE_STAR) \ { \ if (MAX_LEN < (DST_LEN + add_len)) \ { \ util_out_print_gtmio("Subscript too long", FLUSH); \ return FALSE; \ } \ memcpy(DST_PTR, PTR, add_len); \ DST_PTR += add_len; \ DST_LEN += add_len; \ } \ PTR = ptr1; \ } #define GET_CLI_STR(QUAL, MAX_OUTPUT_LEN, VALUE, RES) \ { \ unsigned short qual_len; \ \ qual_len = (unsigned short)MAX_OUTPUT_LEN; \ assert(MAX_OUTPUT_LEN == qual_len); /* make sure it fits in short */ \ if (FALSE == cli_get_str(QUAL, VALUE, &qual_len)) \ return FALSE; \ RES = qual_len; \ } #define GET_CLI_STR_AND_CHECK(QUAL, QUAL_PRESENT, MAX_OUTPUT_LEN, CHECK_FN, VAL, VAL_LEN, NEXT_VAL, ERR_MSG) \ { \ unsigned short qual_str_len; \ \ if (CLI_PRESENT == (QUAL_PRESENT = cli_present(QUAL))) \ { \ GET_CLI_STR(QUAL, MAX_OUTPUT_LEN, VAL, qual_str_len); \ VAL_LEN = qual_str_len; \ if (!CHECK_FN(VAL, &VAL_LEN)) \ { \ if (0 == STR_LIT_LEN(ERR_MSG)) \ return FALSE; \ else \ { \ ERROR_MSG_RETURN(ERR_MSG, VAL_LEN, VAL); \ } \ } \ } else \ VAL_LEN = 0; \ /* cli_get_str() will put the string in the buffer (based on max_output_len) \ * but the check_*() function might take another pass over the string in the \ * output buffer and consolidate it -- for example piece can reduce 2;3;4;5 \ * to 2:5. So don't adjust max_output_len until we know the actual length. \ */ \ MAX_OUTPUT_LEN -= VAL_LEN; \ UPDATE_TRIG_PTRS(VAL, NEXT_VAL, VAL_LEN, MAX_OUTPUT_LEN); \ } GBLREF char cli_err_str[]; GBLREF boolean_t gtm_cli_interpret_string; LITREF unsigned char lower_to_upper_table[]; LITREF mval gvtr_cmd_mval[GVTR_CMDTYPES]; error_def(ERR_INVSTRLEN); STATICFNDEF char *scan_to_end_quote(char *ptr, int len, int max_len) { int str_len = 0; if ((1 >= len) || ('"' != *ptr)) return NULL; /* Invalid string - it needs at least "" */ if (max_len < ++str_len) { util_out_print_gtmio("String too long", FLUSH); return NULL; } ptr++; len--; for ( ; 0 < len; len--, ptr++) { /* Scan until the closing quote */ if ('"' == *ptr) { if (1 == len) break; if ('"' == *(ptr + 1)) { if (max_len < ++str_len) { util_out_print_gtmio("String too long", FLUSH); return NULL; } ptr++; len--; } else break; } if (max_len < ++str_len) { util_out_print_gtmio("String too long", FLUSH); return NULL; } } return (('"' == *ptr) ? ptr + 1 : NULL); } STATICFNDEF boolean_t process_dollar_char(char **src_ptr, int *src_len, boolean_t have_star, char **d_ptr, int *dst_len) { int char_count; char *char_ptr; int len; char *dst_ptr; char dst_string[MAX_DCHAR_LEN]; int lcl_dst_len; mstr m_dst; mstr m_src; char *ptr; int q_len; char *tmp_dst_ptr; tmp_dst_ptr = dst_ptr = *d_ptr; ptr = *src_ptr; len = *src_len; lcl_dst_len = *dst_len; assert('$' == *ptr); UPDATE_DST(ptr, len, have_star, dst_ptr, lcl_dst_len, MAX_GVSUBS_LEN); if (0 == len) return FALSE; switch (*ptr) { case 'c': case 'C': UPDATE_DST(ptr, len, have_star, dst_ptr, lcl_dst_len, MAX_GVSUBS_LEN); if ((0 < len) && ('(' == *ptr)) break; else if ((3 < len) && ('H' == lower_to_upper_table[*ptr]) && ('A' == lower_to_upper_table[*(ptr + 1)]) && ('R' == lower_to_upper_table[*(ptr + 2)]) && ('(' == *(ptr + 3))) { ptr += 3; len -= 3; break; } else return FALSE; break; case 'z': case 'Z': UPDATE_DST(ptr, len, have_star, dst_ptr, lcl_dst_len, MAX_GVSUBS_LEN); if ((2 < len) && ('C' == lower_to_upper_table[*ptr]) && ('H' == lower_to_upper_table[*(ptr + 1)]) && ('(' == *(ptr + 2))) { ptr += 2; len -= 2; } else if ((4 < len) && ('C' == lower_to_upper_table[*ptr]) && ('H' == lower_to_upper_table[*(ptr + 1)]) && ('A' == lower_to_upper_table[*(ptr + 2)]) && ('R' == lower_to_upper_table[*(ptr + 3)]) && ('(' == *(ptr + 4))) { ptr += 4; len -= 4; } else return FALSE; if (MAX_GVSUBS_LEN < lcl_dst_len + 2) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } MEMCPY_LIT(dst_ptr, "CH"); dst_ptr += 2; lcl_dst_len += 2; break; default: return FALSE; } assert('(' == *ptr); UPDATE_DST(ptr, len, have_star, dst_ptr, lcl_dst_len, MAX_GVSUBS_LEN); while ((0 < len) && (')' != *ptr)) { UPDATE_DST(ptr, len, have_star, dst_ptr, lcl_dst_len, MAX_GVSUBS_LEN); } q_len = 0; if (!have_star) { if (MAX_GVSUBS_LEN < ++lcl_dst_len) { util_out_print_gtmio("$[Z]CHAR expression too long", FLUSH); return FALSE; } *dst_ptr++ = *ptr++; *dst_ptr = '\0'; m_src.addr = tmp_dst_ptr; m_src.len = (mstr_len_t)(dst_ptr - tmp_dst_ptr); m_dst.addr = dst_string; m_dst.len = 0; if (!zwr2format(&m_src, &m_dst)) return FALSE; lcl_dst_len = *dst_len; /* Reset length because we're creating the final version now */ if (MAX_GVSUBS_LEN < ++lcl_dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *tmp_dst_ptr++ = '"'; char_ptr = m_dst.addr; if (MAX_GVSUBS_LEN < (lcl_dst_len + m_dst.len)) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } for (char_count = 0; m_dst.len > char_count; char_count++) { if ('"' == *char_ptr) { if (MAX_GVSUBS_LEN < ++lcl_dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *tmp_dst_ptr++ = '"'; q_len++; } *tmp_dst_ptr++ = *char_ptr++; lcl_dst_len++; } if (MAX_GVSUBS_LEN < ++lcl_dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *tmp_dst_ptr++ = '"'; dst_ptr = tmp_dst_ptr; } else ptr++; assert(!have_star || ((dst_ptr == *d_ptr) && (lcl_dst_len == *dst_len))); *src_ptr = ptr; *src_len = len + 2 + q_len; /* Allow for the open and close quotes and any internal quotes */ *d_ptr = dst_ptr; *dst_len = lcl_dst_len; return TRUE; } STATICFNDEF boolean_t process_delim(char *delim_str, uint4 *delim_len) { int char_count; mstr dst; int dst_len; char *dst_ptr; char dst_string[MAX_DELIM_LEN + 1]; uint4 len; char *ptr; char *ptr1; int q_len; char src_string[MAX_DELIM_LEN + 1]; mstr src; char *src_ptr; if (MAX_DELIM_LEN < *delim_len) { util_out_print_gtmio("Delimiter too long", FLUSH); return FALSE; } ptr = delim_str; len = *delim_len; src_ptr = src_string; dst_len = 0; /* If ", scan to end quote * If $, look for char --> c or zchar --> zch * If _, leave it */ while (0 < len) { switch (*ptr) { case '"': PROCESS_STRING(ptr, len, FALSE, src_ptr, dst_len, MAX_DELIM_LEN); break; case '$': UPDATE_DST(ptr, len, FALSE, src_ptr, dst_len, MAX_DELIM_LEN); if (0 == len) { util_out_print_gtmio("Invalid entry in delimiter", FLUSH); return FALSE; } if ((3 < len) && ('C' == lower_to_upper_table[*ptr]) && ('H' == lower_to_upper_table[*(ptr + 1)]) && ('A' == lower_to_upper_table[*(ptr + 2)]) && ('R' == lower_to_upper_table[*(ptr + 3)])) { if (MAX_DELIM_LEN < ++dst_len) { util_out_print_gtmio("Trigger definition too long", FLUSH); return FALSE; } *src_ptr++ = 'C'; ptr += 4; len -= 4; } else if ((4 < len) && ('Z' == lower_to_upper_table[*ptr]) && ('C' == lower_to_upper_table[*(ptr + 1)]) && ('H' == lower_to_upper_table[*(ptr + 2)]) && ('A' == lower_to_upper_table[*(ptr + 3)]) && ('R' == lower_to_upper_table[*(ptr + 4)])) { if (MAX_DELIM_LEN < (dst_len + 3)) { util_out_print_gtmio("Trigger definition too long", FLUSH); return FALSE; } MEMCPY_LIT(src_ptr, "ZCH"); src_ptr += 3; dst_len += 3; ptr += 5; len -= 5; } else { UPDATE_DST(ptr, len, FALSE, src_ptr, dst_len, MAX_DELIM_LEN); } break; default: UPDATE_DST(ptr, len, FALSE, src_ptr, dst_len, MAX_DELIM_LEN); break; } } *src_ptr = '\0'; src.addr = src_string; src.len = (mstr_len_t)(src_ptr - src_string); dst.addr = dst_string; dst.len = 0; if (!zwr2format(&src, &dst)) { util_out_print_gtmio("Invalid delimiter", FLUSH); return FALSE; } if (MAX_DELIM_LEN < dst.len) { util_out_print_gtmio("Delimiter too long", FLUSH); return FALSE; } memcpy(delim_str, dst_string, dst.len); *delim_len = dst.len; return TRUE; } boolean_t check_trigger_name(char *name_str, uint4 *name_len) { uint4 len, lcl_name_len; unsigned char *ptr; /* To conform to the other uses of the check_* functions and their use in the GET_CLI_STR_AND_CHECK macro * name_len is passed in as a uint4 *, but in this case a uint4 would suffice. To mitigate the dereferencing * issues, a local copy is used. */ lcl_name_len = *name_len; if (MAX_USER_TRIGNAME_LEN < lcl_name_len) { util_out_print_gtmio("Trigger name length [!UL] exceeds maximum supported length of [!UL]", FLUSH, lcl_name_len, MAX_USER_TRIGNAME_LEN); return FALSE; } ptr = (unsigned char *)name_str; if (('%' != *ptr) && !ISALPHA_ASCII(*ptr)) { util_out_print_gtmio("Trigger name does not begin with an ascii alphabet or %", FLUSH); return FALSE; } for (len = lcl_name_len - 1, ptr++; (0 < len) && ISALNUM_ASCII(*ptr); ptr++, len--) ; if (len) util_out_print_gtmio("Trigger name has non-alphanumeric character", FLUSH); return (0 == len); } STATICFNDEF boolean_t process_options(char *option_str, uint4 option_len, boolean_t *isolation, boolean_t *noisolation, boolean_t *consistency, boolean_t *noconsistency) { char local_options[MAX_OPTIONS_LEN]; char *ptr, *strtokptr; if (MAX_OPTIONS_LEN < option_len) { util_out_print_gtmio("Too many options", FLUSH); return FALSE; } memcpy(local_options, option_str, option_len); *isolation = *noisolation = *consistency = *noconsistency = FALSE; ptr = local_options; for ( ; 0 < option_len; ptr++, option_len--) *ptr = TOUPPER(*ptr); ptr = STRTOK_R(local_options, ",", &strtokptr); while (NULL != ptr) { switch (*ptr) { case 'C': if (1 == STRLEN(ptr)) *consistency = TRUE; else { assert(0 == MEMCMP_LIT(ptr, HASHT_OPT_CONSISTENCY)); *consistency = TRUE; } break; case 'I': if (1 == STRLEN(ptr)) *isolation = TRUE; else { assert(0 == MEMCMP_LIT(ptr, HASHT_OPT_ISOLATION)); *isolation = TRUE; } break; case 'N': assert('O' == *(ptr + 1)); if ('C' == *(ptr + 2)) { assert(0 == MEMCMP_LIT(ptr, HASHT_OPT_NOCONSISTENCY)); *noconsistency = TRUE; } else { assert('I' == *(ptr + 2)); assert(0 == MEMCMP_LIT(ptr, HASHT_OPT_NOISOLATION)); *noisolation = TRUE; } break; default: assert(FALSE); /* Parsing should have found invalid command */ break; } ptr = STRTOK_R(NULL, ",", &strtokptr); } return !((*isolation && *noisolation) || (*consistency && *noconsistency)); } STATICFNDEF boolean_t process_subscripts(char *subscr_str, uint4 *subscr_len, char **next_str, char *out_str, int4 *out_max) { boolean_t alternation; char dst[MAX_GVSUBS_LEN]; int dst_len; char *dst_ptr; int len; boolean_t have_pattern; boolean_t have_range; boolean_t have_star; boolean_t have_dot; int i; int lvn_count; uint4 lvn_len[MAX_LVN_COUNT]; char *lvn_start; char *lvn_str[MAX_LVN_COUNT]; int multiplier; boolean_t newsub; int num1; int num2; char *ptr; char *ptr1; char *save_dst_ptr; int save_len; char *start_dst_ptr; uint4 start_len; uint4 subsc_count; uint4 tmp_len; boolean_t valid_sub; have_pattern = have_range = valid_sub = have_star = FALSE; ptr = subscr_str; start_len = len = *subscr_len; dst_len = 0; newsub = TRUE; subsc_count = lvn_count = 0; start_dst_ptr = dst_ptr = dst; while ((0 < len) && (')' != *ptr)) { if (ISDIGIT_ASCII(*ptr) || ('-' == *ptr)) { PROCESS_NUMERIC(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); newsub = FALSE; valid_sub = TRUE; } else if (ISALPHA_ASCII(*ptr) || ('%' == *ptr)) { if (!newsub) { util_out_print_gtmio("Invalid subscript", FLUSH); return FALSE; } lvn_start = ptr; tmp_len = 0; do { if (MAX_MIDENT_LEN < ++tmp_len) { util_out_print_gtmio("Variable name too long", FLUSH); return FALSE; } if (MAX_GVSUBS_LEN < ++dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *dst_ptr++ = *ptr++; len--; } while (ISALNUM_ASCII(*ptr)); if ('=' != *ptr) { util_out_print_gtmio("Invalid variable name in subscript", FLUSH); return FALSE; } for (i = 0; i < lvn_count; i++) { if ((lvn_len[i] == tmp_len) && (0 == strncmp(lvn_str[i], lvn_start, tmp_len))) { util_out_print_gtmio("Duplicate variable name in subscript", FLUSH); return FALSE; } } lvn_str[lvn_count] = lvn_start; lvn_len[lvn_count] = tmp_len; lvn_count++; if (MAX_GVSUBS_LEN < ++dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *dst_ptr++ = *ptr++; /* move past = */ start_dst_ptr = dst_ptr; len--; if ((0 < len) && ((',' == *ptr) || (')' == *ptr))) { util_out_print_gtmio("Variable name not followed by valid subscript", FLUSH); return FALSE; } continue; } else { switch (*ptr) { case '"': PROCESS_STRING(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); valid_sub = TRUE; newsub = FALSE; break; case '$': if (!process_dollar_char(&ptr, &len, have_star, &dst_ptr, &dst_len)) { util_out_print_gtmio("Invalid subscript", FLUSH); return FALSE; } newsub = FALSE; valid_sub = TRUE; break; case '?': if (have_range) { util_out_print_gtmio("Range and pattern match not valid in same subscript", FLUSH); return FALSE; } UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); ptr1 = ptr; alternation = FALSE; while ((0 < len) && ((',' != *ptr) || alternation) && ((')' != *ptr) || alternation) && (';' != *ptr)) { num1 = num2 = -1; have_dot = FALSE; if (ISDIGIT_ASCII(*ptr)) { PROCESS_AND_GET_NUMERIC(ptr, len, have_star, dst_ptr, dst_len, num1, MAX_GVSUBS_LEN); if (0 > num1) { util_out_print_gtmio("Alternation integer overflow", FLUSH); return FALSE; } } if ('.' == *ptr) { have_dot = TRUE; if (MAX_GVSUBS_LEN < ++dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *dst_ptr++ = *ptr++; len--; if (ISDIGIT_ASCII(*ptr)) { PROCESS_AND_GET_NUMERIC(ptr, len, have_star, dst_ptr, dst_len, num2, MAX_GVSUBS_LEN); if (0 > num2) { util_out_print_gtmio("Alternation integer overflow", FLUSH); return FALSE; } } if (-1 == num1) /* Pattern range without a defined beginning */ num1 = 0; } switch (*ptr) { case '(': if (!alternation && (0 <= num1)) { UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); alternation = TRUE; continue; } break; case ',': if (alternation && (-1 == num1) && (-1 == num2)) { UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); continue; } break; case ')': if (alternation && (-1 == num1) && (-1 == num2)) { UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); alternation = FALSE; continue; } break; default: break; } if ((0 <= num1) && (0 <= num2) && (num2 < num1)) { util_out_print_gtmio("Invalid pattern match range", FLUSH); return FALSE; } switch (TOUPPER(*ptr)) { case 'E': if (have_dot && (0 >= num1) && (-1 == num2)) { /* Treat ?.E the same as * */ have_star = TRUE; dst_ptr = start_dst_ptr; if (MAX_GVSUBS_LEN < ++dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *dst_ptr++ = '*'; } /* Note: we're dropping into the following case */ case 'A': case 'C': case 'K': case 'L': case 'N': case 'P': case 'U': case 'V': UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); break; case '"': PROCESS_STRING(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); break; /* Note: we're dropping into the default/error case */ default: CONV_CH_AND_PRINT("Unexpected character !AD in pattern code", ptr); return FALSE; } } have_pattern = TRUE; valid_sub = TRUE; newsub = FALSE; break; case ':': if (have_range) { util_out_print_gtmio("Range within a range not allowed", FLUSH); return FALSE; } if (have_pattern) { util_out_print_gtmio("Pattern not allowed as a range", FLUSH); return FALSE; } UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); if (ISDIGIT_ASCII(*ptr) || ('-' == *ptr)) { PROCESS_NUMERIC(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); } else if ('"' == *ptr) { PROCESS_STRING(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); } else if ('$' == *ptr) { if (!process_dollar_char(&ptr, &len, have_star, &dst_ptr, &dst_len)) { util_out_print_gtmio("Invalid range value", FLUSH); return FALSE; } } else if ((0 < len) && (',' != *ptr) && (';' != *ptr) && (')' != *ptr)) { util_out_print_gtmio("Invalid string range", FLUSH); return FALSE; } else { /* A range with no lower end - just scan the numeric or string */ ptr1 = ptr; if (ISDIGIT_ASCII(*ptr) || ('-' == *ptr)) { PROCESS_NUMERIC(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); } else if ('"' == *ptr1) { PROCESS_STRING(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); } else if ((0 < len) && ((',' != *ptr) && (';' != *ptr) && (')' != *ptr))) { util_out_print_gtmio("Range value must be integer or string", FLUSH); return FALSE; } else if (!valid_sub) { /* this is a single ":" - treat it just like a * */ have_star = TRUE; dst_ptr = start_dst_ptr; if (MAX_GVSUBS_LEN < ++dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *dst_ptr++ = '*'; } } valid_sub = TRUE; newsub = FALSE; have_range = TRUE; break; case '*': if (!have_star) { have_star = TRUE; dst_ptr = start_dst_ptr; if (MAX_GVSUBS_LEN < ++dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *dst_ptr++ = '*'; } ptr++; len--; if ((0 < len) && (',' != *ptr) && (';' != *ptr) && (')' != *ptr)) { util_out_print_gtmio("Invalid use of *", FLUSH); return FALSE; } else valid_sub = TRUE; newsub = FALSE; break; case ';': UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); /* Delete extraneous ; in the subscript */ if ((!have_star) && (newsub || ((0 < len) && ((',' == *ptr) || (';' == *ptr) || ISALPHA_ASCII(*ptr) || ('%' == *ptr) || (')' == *ptr))))) dst_ptr--; valid_sub = FALSE; have_pattern = have_range = FALSE; break; case ',': if (newsub) { util_out_print_gtmio("Empty subscript not allowed", FLUSH); return FALSE; } if (MAX_GVSUBSCRIPTS <= ++subsc_count) { util_out_print_gtmio("Too many subscripts", FLUSH); return FALSE; } if (MAX_GVSUBS_LEN < ++dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *dst_ptr++ = *ptr++; len--; start_dst_ptr = dst_ptr; newsub = TRUE; have_range = have_pattern = valid_sub = have_star = FALSE; break; default: CONV_CH_AND_PRINT("Invalid character !AD in subscript", ptr); return FALSE; } } } if ((0 == len) && (')' != *ptr)) { util_out_print_gtmio("Missing \")\" after global subscript", FLUSH); return FALSE; } if ((start_len == len) || newsub) { util_out_print_gtmio("Empty subscript not allowed", FLUSH); return FALSE; } if ((')' == *ptr) && (MAX_GVSUBSCRIPTS <= ++subsc_count)) { util_out_print_gtmio("Too many subscripts", FLUSH); return FALSE; } CHECK_FOR_ROOM_IN_OUTPUT_AND_COPY(dst, out_str, (int)(dst_ptr - dst), *out_max); *subscr_len = (uint4)(dst_ptr - dst); *next_str = ptr + 1; return TRUE; } STATICFNDEF boolean_t process_pieces(char *piece_str, uint4 *piece_len) { uint bit; uint4 bitmap[MAX_PIECE_INT]; uint bitval; uint bitword; boolean_t have_low_num; boolean_t have_num; boolean_t have_num_in_str; uint i; uint4 len; int low; int num; char *ptr; char *ptr1; boolean_t have_error = FALSE; int num_len; memset((void *)bitmap, 0, MAX_PIECE_CHARS); ptr = piece_str; len = *piece_len; have_num = have_low_num = FALSE; if ('"' == *ptr) { ptr++; len--; } while ((0 < len) && !have_error) { switch (*ptr) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': ptr1 = ptr; A2I(ptr1, ptr + len, num); if ((0 >= num) || (MAX_PIECE_VALUE < num)) { CONV_NUM_AND_STR_AND_PRINT("Invalid value !UL in PIECES - !AD", num, *piece_len, piece_str); have_error = TRUE; break; } len -= (int)(ptr1 - ptr); ptr = ptr1; bitmap[num / BITS_PER_INT] |= 1 << (num % BITS_PER_INT); have_num = TRUE; have_low_num = TRUE; break; case ';': if (!have_num) { CONV_STR_AND_PRINT("Expected an integer in PIECES - ", *piece_len, piece_str); have_error = TRUE; break; } have_num = FALSE; ptr++; len--; break; case ':': if (!have_low_num) { CONV_STR_AND_PRINT("Expected an integer for lower value in range in PIECES - ", *piece_len, piece_str); have_error = TRUE; break; } ptr++; len--; low = num; if (!ISDIGIT_ASCII(*ptr)) { CONV_STR_AND_PRINT("Expected an integer for upper value in range in PIECES - ", *piece_len, piece_str); have_error = TRUE; break; } ptr1 = ptr; A2I(ptr1, ptr + len, num); if ((0 >= num) || (MAX_PIECE_VALUE < num)) { CONV_NUM_AND_STR_AND_PRINT("Invalid value \"!UL\" in PIECES - !AD", num, *piece_len, piece_str); have_error = TRUE; break; } if (num <= low) { CONV_STR_AND_PRINT("Low value of range not less than high value in PIECES - ", *piece_len, piece_str); have_error = TRUE; break; } len -= (int)(ptr1 - ptr); ptr = ptr1; for (i = low + 1; i <= num; i++) bitmap[i / BITS_PER_INT] |= 1 << (i % BITS_PER_INT); have_num = TRUE; have_low_num = FALSE; break; case '"': if (1 == len) { len--; break; } default: CONV_CH_AND_STR_AND_PRINT("Invalid character !AD in PIECES - !AD", ptr, *piece_len, piece_str); have_error = TRUE; break; } } if ((0 != len) || !have_num || have_error) { if (!have_error) CONV_STR_AND_PRINT("Invalid PIECE entry - ", *piece_len, piece_str); return FALSE; } have_num = have_low_num = have_num_in_str = FALSE; ptr = piece_str; len = 0; for (i = 0; i < MAX_PIECE_INT; i++) { if ((0 == bitmap[i] && !have_low_num)) continue; bitword = bitmap[i]; bit = 0; while ((0 != bitword) || have_low_num) { bitval = bitword & 1; if (!have_low_num) { if (0 == bitval) { bitword = bitword >> 1; bit++; continue; } have_num = TRUE; have_low_num = TRUE; low = i * BITS_PER_INT + bit; if (have_num_in_str) { *ptr++ = ';'; len++; } num_len = 0; I2A(ptr, num_len, low); len += num_len; ptr += num_len; have_num_in_str = TRUE; } while ((1 == bitval) && (BITS_PER_INT > bit)) { bitword = bitword >> 1; bitval = bitword & 1; bit++; } if (BITS_PER_INT == bit) break; num = (0 == bit) ? i * BITS_PER_INT - 1 : i * BITS_PER_INT + bit - 1; have_low_num = FALSE; if (num == low) continue; *ptr++ = ':'; len++; num_len = 0; I2A(ptr, num_len, num); assert(num_len < MAX_DIGITS_IN_INT); len += MIN(num_len, MAX_DIGITS_IN_INT); assert(*piece_len >= len); ptr += num_len; } } *ptr = '\0'; *piece_len = len; return TRUE; } boolean_t process_xecute(char *xecute_str, uint4 *xecute_len, boolean_t multi_line) { uint4 dst_len; char dst_string[MAX_SRCLINE]; uint4 src_len; gv_trigger_t trigdsc; src_len = *xecute_len; if ((src_len == XTENDED_START_LEN) && (0 == memcmp(XTENDED_START, xecute_str, XTENDED_START_LEN))) return TRUE; if (!multi_line && (NULL != memchr(xecute_str, '\n', src_len))) { util_out_print_gtmio("Newline not allowed in XECUTE string", FLUSH); return FALSE; } if (!multi_line && MAX_SRCLINE < src_len + 1) /* allow room for leading blank */ { return FALSE; } if (0 == src_len) { util_out_print_gtmio("Empty XECUTE string is invalid", FLUSH); return FALSE; } if (!multi_line) { dst_string[0] = ' '; if (!trigger_scan_string(xecute_str, &src_len, dst_string + 1, &dst_len) || (1 != src_len)) { util_out_print_gtmio("Invalid XECUTE string", FLUSH); return FALSE; } memcpy(xecute_str, dst_string, ++dst_len); *xecute_len = dst_len; trigdsc.xecute_str.str.addr = dst_string; trigdsc.xecute_str.str.len = dst_len; } else { trigdsc.xecute_str.str.addr = xecute_str; trigdsc.xecute_str.str.len = src_len; } /* Test compile the string - first build up the parm list to trigger compile routine */ trigdsc.rtn_desc.rt_name.addr = TRIGR_PRECOMP_RTNNAME; trigdsc.rtn_desc.rt_name.len = SIZEOF(TRIGR_PRECOMP_RTNNAME) - 1; trigdsc.rtn_desc.rt_adr = NULL; return (0 == gtm_trigger_complink(&trigdsc, FALSE)); } boolean_t trigger_parse(char *input, uint4 input_len, char *trigvn, char **values, uint4 *value_len, int4 *max_len, boolean_t *multi_line_xecute) { boolean_t cli_status; int cmd_ind; int count; int eof; int extra; boolean_t found_zkill; boolean_t found_kill; uint4 len; int4 max_output_len; int parse_ret; boolean_t delim_present, name_present, pieces_present, xecute_present, zdelim_present; char *ptr; char *ptr1; char *ptr2; int4 rec_num; CLI_ENTRY *save_cmd_ary; boolean_t set_present; int trigvn_len; boolean_t in_multi_line_xecute, out_multi_line_xecute; max_output_len = *max_len; in_multi_line_xecute = out_multi_line_xecute = *multi_line_xecute; if (in_multi_line_xecute) { if ((XTENDED_STOP_LEN != input_len) || (0 != memcmp(XTENDED_STOP, input, XTENDED_STOP_LEN))) { ptr1 = values[XECUTE_SUB] + value_len[XECUTE_SUB]; if ((input_len + 1) < max_output_len) { memcpy(ptr1, input, input_len); ptr1 += input_len; *ptr1 = '\n'; max_output_len -= (input_len + 1); value_len[XECUTE_SUB] += input_len + 1; *max_len = max_output_len; return TRIG_SUCCESS; } else { *multi_line_xecute = FALSE; util_out_print_gtmio("Error : Trigger definition too long", FLUSH); return FALSE; } } out_multi_line_xecute = FALSE; if (!process_xecute(values[XECUTE_SUB], &value_len[XECUTE_SUB], TRUE)) { *multi_line_xecute = FALSE; ERROR_MSG_RETURN("Error : Parsing XECUTE string: ", value_len[XECUTE_SUB], values[XECUTE_SUB]); } *multi_line_xecute = out_multi_line_xecute; *max_len = max_output_len; return TRIG_SUCCESS; } ptr1 = input; len = (uint4)input_len; if ('^' != *ptr1++) { ERROR_MSG_RETURN("Error : Missing global name:\n", input_len, input); } ptr = ptr1; len--; if (('%' != *ptr1) && !ISALPHA_ASCII(*ptr1)) { ERROR_MSG_RETURN("Error : Invalid global name:\n", input_len, input); } ptr1++; while (ISALNUM_ASCII(*ptr1)) ptr1++; if (('(' != *ptr1) && !ISSPACE_ASCII(*ptr1)) { ERROR_MSG_RETURN("Error : Invalid global name:\n", input_len, input); } trigvn_len = (int)(ptr1 - ptr); /* If specified var name is global ^%Y*, the name is illegal to use in a TRIGGER */ if ((RESERVED_NAMESPACE_LEN <= trigvn_len) && (0 == MEMCMP_LIT(ptr, RESERVED_NAMESPACE))) { /* Triggers not allowed for ^%Y*. ERR_PCTYRESERVED error is better issued here but no easy way to fit it in * ERROR_MSG_RETURN macro so we copy its message text here. */ ERROR_MSG_RETURN("Error : ^%Y* global name not supported:\n", input_len, input); } if (MAX_MIDENT_LEN < trigvn_len) { trigvn_len = MAX_MIDENT_LEN; util_out_print_gtmio("Warning: global name truncated to ^!AD", FLUSH, trigvn_len, ptr); } memcpy(trigvn, ptr, trigvn_len); trigvn[trigvn_len] = '\0'; /* lookup global to get collation info */ len -= (uint4)trigvn_len; ptr2 = ptr1; if ('(' == *ptr1) { ptr1++; len--; if (!process_subscripts(ptr1, &len, &ptr2, values[GVSUBS_SUB], &max_output_len)) { ERROR_MSG_RETURN("", input_len, input); } } else len = 0; if (0 > --max_output_len) { util_out_print_gtmio("Error : Trigger definition too long", FLUSH); return TRIG_FAILURE; } values[GVSUBS_SUB][len] = '\0'; value_len[GVSUBS_SUB] = (uint4)len; save_cmd_ary = cmd_ary; cmd_ary = &trigger_cmd_ary[0]; gtm_cli_interpret_string = FALSE; cli_str_setup(input_len - (uint4)(ptr2 - input), ptr2); parse_ret = parse_triggerfile_cmd(); gtm_cli_interpret_string = TRUE; cmd_ary = save_cmd_ary; if (parse_ret) { ERROR_MSG_RETURN("Error : In parsing input:\n", input_len, input); } values[CMD_SUB] = values[GVSUBS_SUB] + value_len[GVSUBS_SUB] + 1; if (CLI_PRESENT == cli_present("COMMANDS")) { GET_CLI_STR("COMMANDS", max_output_len, values[CMD_SUB], value_len[CMD_SUB]); ptr = values[CMD_SUB]; count = 0; found_zkill = found_kill = FALSE; /* The following order (set, kill, zkill, ztkill) needs to be maintained to match * the corresponding bit value orders in gv_trig_cmd_table.h */ if (CLI_PRESENT == (set_present = cli_present("COMMANDS.SET"))) { ADD_STRING(count, ptr, gvtr_cmd_mval[GVTR_CMDTYPE_SET].str.len, gvtr_cmd_mval[GVTR_CMDTYPE_SET].str.addr, max_output_len); } if (CLI_PRESENT == cli_present("COMMANDS.KILL")) { found_kill = TRUE; ADD_COMMA_IF_NEEDED(count, ptr, max_output_len); ADD_STRING(count, ptr, gvtr_cmd_mval[GVTR_CMDTYPE_KILL].str.len, gvtr_cmd_mval[GVTR_CMDTYPE_KILL].str.addr, max_output_len); } if (CLI_PRESENT == cli_present("COMMANDS.ZKILL")) { found_zkill = TRUE; ADD_COMMA_IF_NEEDED(count, ptr, max_output_len); ADD_STRING(count, ptr, gvtr_cmd_mval[GVTR_CMDTYPE_ZKILL].str.len, gvtr_cmd_mval[GVTR_CMDTYPE_ZKILL].str.addr, max_output_len); } if (CLI_PRESENT == cli_present("COMMANDS.ZWITHDRAW") && !found_zkill) { ADD_COMMA_IF_NEEDED(count, ptr, max_output_len); ADD_STRING(count, ptr, gvtr_cmd_mval[GVTR_CMDTYPE_ZKILL].str.len, gvtr_cmd_mval[GVTR_CMDTYPE_ZKILL].str.addr, max_output_len); } if (CLI_PRESENT == cli_present("COMMANDS.ZTKILL")) { if (found_kill) ERROR_MSG_RETURN("Error : KILL and ZTKILL incompatible: ", value_len[CMD_SUB], values[CMD_SUB]); ADD_COMMA_IF_NEEDED(count, ptr, max_output_len); ADD_STRING(count, ptr, gvtr_cmd_mval[GVTR_CMDTYPE_ZTKILL].str.len, gvtr_cmd_mval[GVTR_CMDTYPE_ZTKILL].str.addr, max_output_len); } if (CLI_PRESENT == cli_present("COMMANDS.ZTRIGGER")) { ADD_COMMA_IF_NEEDED(count, ptr, max_output_len); ADD_STRING(count, ptr, gvtr_cmd_mval[GVTR_CMDTYPE_ZTRIGGER].str.len, gvtr_cmd_mval[GVTR_CMDTYPE_ZTRIGGER].str.addr, max_output_len); } *ptr = '\0'; value_len[CMD_SUB] = STRLEN(values[CMD_SUB]); } else value_len[CMD_SUB] = 0; UPDATE_TRIG_PTRS(values[CMD_SUB], values[DELIM_SUB], value_len[CMD_SUB], max_output_len); GET_CLI_STR_AND_CHECK("DELIM", delim_present, max_output_len, process_delim, values[DELIM_SUB], value_len[DELIM_SUB], values[TRIGNAME_SUB], "Error : Parsing DELIM string: "); GET_CLI_STR_AND_CHECK("NAME", name_present, max_output_len, check_trigger_name, values[TRIGNAME_SUB], value_len[TRIGNAME_SUB], values[OPTIONS_SUB], "Error : Parsing NAME string: "); if (CLI_PRESENT == cli_present("OPTIONS")) { boolean_t isolation, noisolation, consistency, noconsistency; GET_CLI_STR("OPTIONS", max_output_len, values[OPTIONS_SUB], value_len[OPTIONS_SUB]); if (!process_options(values[OPTIONS_SUB], value_len[OPTIONS_SUB], &isolation, &noisolation, &consistency, &noconsistency)) { ERROR_MSG_RETURN("Error : Inconsistent values in OPTIONS string: ", value_len[OPTIONS_SUB], values[OPTIONS_SUB]); } count = 0; ptr = values[OPTIONS_SUB]; if (isolation) { ADD_STRING(count, ptr, STRLEN(HASHT_OPT_ISOLATION), HASHT_OPT_ISOLATION, max_output_len); } else if (noisolation) { ADD_STRING(count, ptr, STRLEN(HASHT_OPT_NOISOLATION), HASHT_OPT_NOISOLATION, max_output_len); } if (consistency) { ADD_COMMA_IF_NEEDED(count, ptr, max_output_len); ADD_STRING(count, ptr, STRLEN(HASHT_OPT_CONSISTENCY), HASHT_OPT_CONSISTENCY, max_output_len); } else if (noconsistency) { ADD_COMMA_IF_NEEDED(count, ptr, max_output_len); ADD_STRING(count, ptr, STRLEN(HASHT_OPT_NOCONSISTENCY), HASHT_OPT_NOCONSISTENCY, max_output_len); } *ptr = '\0'; value_len[OPTIONS_SUB] = STRLEN(values[OPTIONS_SUB]); } else value_len[OPTIONS_SUB] = 0; UPDATE_TRIG_PTRS(values[OPTIONS_SUB], values[PIECES_SUB], value_len[OPTIONS_SUB], max_output_len); GET_CLI_STR_AND_CHECK("PIECES", pieces_present, max_output_len, process_pieces, values[PIECES_SUB], value_len[PIECES_SUB], values[ZDELIM_SUB], ""); GET_CLI_STR_AND_CHECK("ZDELIM", zdelim_present, max_output_len, process_delim, values[ZDELIM_SUB], value_len[ZDELIM_SUB], values[XECUTE_SUB], "Error : Parsing ZDELIM string: "); /* Since process_xecute() does a test compile of the xecute string, it changes the parsing table from MUPIP TRIGGER * to GT.M. To avoid problems with saving and restoring the parse state (which would have to be done with globals * that are static in cli_parse.c), it is much easier to put process_xecute() last in the list of qualifiers to check - * solving the problem. In other words, don't try to neaten things up by making the qualifiers alphabetical! */ if (CLI_PRESENT == (xecute_present = cli_present("XECUTE"))) { GET_CLI_STR("XECUTE", max_output_len, values[XECUTE_SUB], value_len[XECUTE_SUB]); out_multi_line_xecute |= (value_len[XECUTE_SUB] == XTENDED_START_LEN) && (0 == memcmp(XTENDED_START, values[XECUTE_SUB], XTENDED_START_LEN)); if (!process_xecute(values[XECUTE_SUB], &value_len[XECUTE_SUB], out_multi_line_xecute)) { ptr = values[XECUTE_SUB]; len = value_len[XECUTE_SUB]; ERROR_MSG_RETURN("Error : Parsing XECUTE string: ", len, ptr); } } else value_len[XECUTE_SUB] = 0; if (!in_multi_line_xecute) { max_output_len -= value_len[XECUTE_SUB]; UPDATE_TRIG_PTRS(values[XECUTE_SUB], values[CHSET_SUB], value_len[XECUTE_SUB], max_output_len); ptr = values[XECUTE_SUB] + value_len[XECUTE_SUB]; *ptr = '\0'; if (!xecute_present && !out_multi_line_xecute) { ERROR_STR_RETURN("Error: XECUTE value missing"); } if (0 == value_len[CMD_SUB]) { ERROR_STR_RETURN("Error: COMMANDS value missing"); } if (delim_present && zdelim_present) { ERROR_STR_RETURN("Error: Can't have both DELIM and ZDELIM in same entry"); } if ((delim_present || zdelim_present) && !set_present) { ERROR_STR_RETURN("Error: DELIM and ZDELIM need a commands=SET"); } if (pieces_present && (!delim_present && !zdelim_present)) { ERROR_STR_RETURN("Error: PIECES need either DELIM or ZDELIM"); } if (MAX_HASH_INDEX_LEN < (trigvn_len + 1 + value_len[GVSUBS_SUB] + 1 + value_len[DELIM_SUB] + 1 + value_len[ZDELIM_SUB] + 1 + value_len[XECUTE_SUB] + 1)) { ERROR_STR_RETURN("Error: Entry too large to properly index"); } } *multi_line_xecute = out_multi_line_xecute; *max_len = max_output_len; return TRIG_SUCCESS; } fis-gtm-V7.0-005/sr_unix/trigger_parse_protos.h0000644000032200000250000000305214342376330020455 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2020 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. * * * ****************************************************************/ #ifndef TRIGGER_PARSE_PROTOS_INCLUDED #define TRIGGER_PARSE_PROTOS_INCLUDED STATICFNDCL char *scan_to_end_quote(char *ptr, int len, int max_len); STATICFNDCL boolean_t process_dollar_char(char **src_ptr, int *src_len, boolean_t have_star, char **d_ptr, int *dst_len); STATICFNDCL boolean_t process_delim(char *delim_str, uint4 *delim_len); STATICFNDCL boolean_t process_options(char *option_str, uint4 option_len, boolean_t *isolation, boolean_t *noisolation, boolean_t *consistency, boolean_t *noconsistency); STATICFNDCL boolean_t process_subscripts(char *subscr_str, uint4 *subscr_len, char **next_str, char *out_str, int4 *out_max); STATICFNDCL boolean_t process_pieces(char *piece_str, uint4 *piece_len); boolean_t process_xecute(char *xecute_str, uint4 *xecute_len, boolean_t multi_line); boolean_t check_trigger_name(char *name_str, uint4 *name_len); boolean_t trigger_parse(char *input, uint4 input_len, char *trigvn, char **values, uint4 *value_len, int4 *max_len, boolean_t *multi_line_xecute); #endif /* TRIGGER_PARSE_PROTOS_INCLUDED */ fis-gtm-V7.0-005/sr_unix/trigger_read_andor_locate.h0000644000032200000250000000452514342376330021370 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2016 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. * * * ****************************************************************/ #ifndef TRIGGER_SOURCE_READ_ANDOR_VERIFY_H_INCLUDED #define TRIGGER_SOURCE_READ_ANDOR_VERIFY_H_INCLUDED #ifdef GTM_TRIGGER /* Header file shared by trigger_source_read_andor_verify() and trigger_locate_andor_load() since they * perform very similar function and share some macros and definitions. */ #define TRIG_FAILURE_RC -1 #define ISSUE_TRIGNAMENF_ERROR_IF_APPROPRIATE(TRIGNAME) \ { \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ if (!TREF(op_fntext_tlevel)) \ { \ CLEAR_IMPLICIT_TP_BEFORE_ERROR; \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TRIGNAMENF, 2, TRIGNAME->len, TRIGNAME->addr); \ } \ } /* If we have an implicit transaction and are about to fire an error, commit the transaction first so we can * get rid of the transaction connotation before error handling gets involved. Note we use op_tcommit() here * instead of op_trollback so we can verify the conditions that generated the error. If some restartable * condition caused the error, this will restart and retry the transaction. Note that since skip_INVOKE_RESTART * is not set before this op_tcommit, it with throw a restart rather than returning a restartable code. */ #define CLEAR_IMPLICIT_TP_BEFORE_ERROR \ if (dollar_tlevel && tp_pointer->implicit_trigger && (0 == gtm_trigger_depth)) \ { /* We have an implicit TP fence */ \ enum cdb_sc status; \ /* Eliminate transaction by commiting it (nothing was done) */ \ status = op_tcommit(); \ assert(cdb_sc_normal == status); \ } gd_region *find_region(mstr *regname); int trigger_locate_andor_load(mstr *trigname, rhdtyp **rtn_vec); int trigger_source_read_andor_verify(mstr *trigname, rhdtyp **rtn_vec); #endif /* GTM_TRIGGER */ #endif /* TRIGGER_SOURCE_READ_ANDOR_VERIFY_H_INCLUDED */ fis-gtm-V7.0-005/sr_unix/trigger_scan_string.c0000644000032200000250000000220014342376330020234 0ustar librarygtc/**************************************************************** * * * Copyright 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 "trigger_scan_string.h" boolean_t trigger_scan_string(char *src_ptr, uint4 *src_len, char *dst_ptr, uint4 *dst_len) { uint4 d_len, s_len; s_len = *src_len; if (1 >= s_len) { /* Invalid string - it needs at least "" */ return FALSE; } if ('"' != *src_ptr) { /* String needs to start with " */ return FALSE; } ++src_ptr; s_len--; d_len = 0; while (0 < s_len) { /* Scan until the closing quote */ if ('"' == *src_ptr) { if (1 == s_len) break; if ('"' == *(src_ptr + 1)) { src_ptr++; s_len--; } else break; } *dst_ptr++ = *src_ptr++; d_len++; s_len--; } *dst_len = d_len; *src_len = s_len; return ('"' == *src_ptr); } fis-gtm-V7.0-005/sr_unix/trigger_scan_string.h0000644000032200000250000000122214342376330020244 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef TRIGGER_SCAN_STRING_INCLUDED #define TRIGGER_SCAN_STRING_INCLUDED boolean_t trigger_scan_string(char *src_ptr, uint4 *src_len, char *dst_ptr, uint4 *dst_len); #endif /* TRIGGER_SCAN_STRING_INCLUDED */ fis-gtm-V7.0-005/sr_unix/trigger_select.c0000644000032200000250000007642514342376335017232 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2022 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" #ifdef GTM_TRIGGER #include #include "gtm_ctype.h" #include "gtm_string.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include #include "gv_trigger.h" #include "targ_alloc.h" #include "filestruct.h" #include "mv_stent.h" /* for COPY_SUBS_TO_GVCURRKEY macro */ #include "gvsub2str.h" /* for COPY_SUBS_TO_GVCURRKEY macro */ #include "format_targ_key.h" /* for COPY_SUBS_TO_GVCURRKEY macro */ #include "trigger.h" #include "trigger_gbl_fill_xecute_buffer.h" #include "trigger_scan_string.h" #include "trigger_select_protos.h" #include "trigger_user_name.h" #include "change_reg.h" #include "gvcst_protos.h" #include "op.h" /* for op_tstart etc. */ #include "mupip_exit.h" #include "zshow.h" #include "util.h" #include "compiler.h" #include "mvalconv.h" #include "op_tcommit.h" #include "gdscc.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "tp.h" #include "t_retry.h" #include "io_params.h" #include "min_max.h" /* Needed for MIN */ #include "gtmimagename.h" #include "gtmio.h" #include "have_crit.h" #include "hashtab_mname.h" #include "tp_frame.h" #include "tp_restart.h" #include "repl_msg.h" /* for gtmsource.h */ #include "gtmsource.h" /* for jnlpool_addrs_ptr_t */ GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF gd_region *gv_cur_region; GBLREF gv_namehead *gv_target; GBLREF gv_key *gv_currkey; GBLREF gd_addr *gd_header; GBLREF gv_key *gv_altkey; GBLREF io_pair io_curr_device; GBLREF io_pair io_std_device; /* standard device */ GBLREF sgm_info *sgm_info_ptr; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF int (*op_open_ptr)(mval *v, mval *p, mval *t, mval *mspace); GBLREF uint4 dollar_tlevel; GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; #ifdef DEBUG GBLREF boolean_t donot_INVOKE_MUMTSTART; #endif LITREF mval literal_zero; LITREF mval literal_ten; LITREF char *trigger_subs[]; #define TRIGGER_NAME_COMMENT "trigger name: " #define TRIGGER_CYCLE_COMMENT " cycle: " #define NAM_LEN(PTR, LEN) MIN(STRLEN((PTR)), (LEN)) #define COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(OUT_BUFF, OUT_PTR, VAL, VAL_LEN) \ { \ if ((INTCAST(OUT_PTR - OUT_BUFF) + VAL_LEN) > MAX_BUFF_SIZE) \ { \ util_out_print_gtmio("!AD", NOFLUSH_OUT, (unsigned int)(OUT_PTR - OUT_BUFF), OUT_BUFF); \ OUT_PTR = OUT_BUFF; \ } \ if (VAL_LEN > MAX_BUFF_SIZE) \ { \ util_out_print_gtmio("!AD", NOFLUSH_OUT, VAL_LEN, VAL); \ OUT_PTR = OUT_BUFF; \ } else \ { \ memcpy(OUT_PTR, (const void *)VAL, VAL_LEN); \ OUT_PTR += VAL_LEN; \ } \ } #define COPY_SUBSCRIPT(OUT_BUFF, OUT_PTR, CHPTR, LEN_LEFT, IS_TYPE) \ { \ COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(OUT_BUFF, OUT_PTR, CHPTR, 1); \ CHPTR++; \ LEN_LEFT--; \ while ((0 < LEN_LEFT) && IS_TYPE(*CHPTR)) \ { \ COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(OUT_BUFF, OUT_PTR, CHPTR, 1); \ CHPTR++; \ LEN_LEFT--; \ } \ } #define MAKE_ZWR_STR(STR, STR_LEN, OUT_START, OUT_STR) \ { \ int lcl_len; \ unsigned char tmp_buff[MAX_ZWR_EXP_RATIO * MAX_BUFF_SIZE]; \ \ lcl_len = MAX_ZWR_EXP_RATIO * MAX_BUFF_SIZE; \ format2zwr((sm_uc_ptr_t)STR, STR_LEN, tmp_buff, &lcl_len); \ COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(OUT_START, OUT_STR, tmp_buff, lcl_len); \ } #define CHECK_FOR_M_NAME(START, PTR, LEN, STR, ERR, MAX_LEN) \ { \ int lcl_len; \ int cur_len; \ \ assert(0 < MAX_LEN); \ if (!ISALPHA_ASCII(*PTR) && ('%' != *PTR)) \ { \ lcl_len = STRLEN(START); \ CONV_STR_AND_PRINT(STR, lcl_len, START); \ ERR = TRIG_FAILURE; \ continue; \ } \ PTR++; \ LEN--; \ cur_len = 1; \ while ((0 < LEN) && ISALNUM_ASCII(*PTR)) \ { \ if (MAX_LEN < ++cur_len) \ break; \ PTR++; \ LEN--; \ } \ } #define INVALID_NAME_ERROR(ERR_STR, PTR, ERR_VAR, POS) \ { \ int lcl_len; \ \ lcl_len = STRLEN(PTR); \ CONV_STR_AND_PRINT(ERR_STR, lcl_len, PTR); \ select_status = TRIG_FAILURE; \ } error_def(ERR_DBROLLEDBACK); error_def(ERR_MUNOACTION); error_def(ERR_MUNOACTION); error_def(ERR_MUPCLIERR); error_def(ERR_MUPCLIERR); error_def(ERR_NEEDTRIGUPGRD); error_def(ERR_TRIGDEFBAD); STATICDEF char *triggerfile_quals[] = { #define TRIGGER_SUBSDEF(SUBSTYPE, SUBSNAME, LITMVALNAME, TRIGFILEQUAL, PARTOFHASH) TRIGFILEQUAL, #include "trigger_subs_def.h" #undef TRIGGER_SUBSDEF }; STATICFNDEF void write_subscripts(char *out_rec, char **out_ptr, char **sub_ptr, int *sub_len) { char *out_p, *ptr; uint4 len_left, dst_len, len; char dst[MAX_GVSUBS_LEN]; len_left = *sub_len; ptr = *sub_ptr; out_p = *out_ptr; while (0 < len_left) { if (ISDIGIT_ASCII(*ptr) || ('-' == *ptr)) { COPY_SUBSCRIPT(out_rec, out_p, ptr, len_left, ISDIGIT_ASCII); } else if (ISALPHA_ASCII(*ptr) || ('%' == *ptr)) { COPY_SUBSCRIPT(out_rec, out_p, ptr, len_left, ISALNUM_ASCII); } else if ('"' == *ptr) { len = len_left; trigger_scan_string(ptr, &len_left, dst, &dst_len); MAKE_ZWR_STR(dst, dst_len, out_rec, out_p); len_left--; /* Need to skip the trailing " */ ptr += (len - len_left); } else { COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_p, ptr, 1); ptr++; len_left--; } } *sub_len = len_left; *sub_ptr = ptr; *out_ptr = out_p; } STATICFNDEF void write_out_trigger(char *gbl_name, uint4 gbl_name_len, int nam_indx) { mval data_val; char out_rec[MAX_BUFF_SIZE]; char *out_rec_ptr; char *ptr1, *ptr2, *ptrtop; mval mi, trigger_count, trigger_value, *protect_trig_mval; mval *mv_trig_cnt_ptr; boolean_t multi_line; boolean_t have_value, multi_record; int count; int indx, sub_indx; int4 skip_chars; int sub_len; char *sub_ptr; char *tmp_str_ptr; uint4 tmp_str_len; char cycle[MAX_DIGITS_IN_INT + 1]; int cycle_len; char *xecute_buff; int4 xecute_len; int trig_protected_mval_push_count; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!cs_addrs->hdr->hasht_upgrade_needed); /* all callers should have errored out otherwise */ BUILD_HASHT_SUB_SUB_CURRKEY(gbl_name, gbl_name_len, LITERAL_HASHCOUNT, LITERAL_HASHCOUNT_LEN); if (gvcst_get(&trigger_count)) { mv_trig_cnt_ptr = &trigger_count; count = MV_FORCE_INT(mv_trig_cnt_ptr); assert(0 < count); BUILD_HASHT_SUB_SUB_CURRKEY(gbl_name, gbl_name_len, LITERAL_HASHLABEL, STRLEN(LITERAL_HASHLABEL)); if (!gvcst_get(&trigger_value)) { /* There has to be a #LABEL */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, gbl_name_len, gbl_name, gbl_name_len, gbl_name, LEN_AND_LIT("\"#LABEL\"")); } skip_chars = 1; if ((trigger_value.str.len != STRLEN(HASHT_GBL_CURLABEL)) || (0 != memcmp(trigger_value.str.addr, HASHT_GBL_CURLABEL, trigger_value.str.len))) { if ((1 == trigger_value.str.len) && ('1' == *trigger_value.str.addr)) /* 1 == #LABEL - No leading blank in xecute string */ skip_chars = 0; } BUILD_HASHT_SUB_SUB_CURRKEY(gbl_name, gbl_name_len, LITERAL_HASHCYCLE, LITERAL_HASHCYCLE_LEN); if (!gvcst_get(&trigger_value)) { /* There has to be a #CYCLE */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, gbl_name_len, gbl_name, gbl_name_len, gbl_name, LEN_AND_LIT("\"#CYCLE\"")); } cycle_len = MIN(trigger_value.str.len, MAX_DIGITS_IN_INT); memcpy(cycle, trigger_value.str.addr, cycle_len); cycle[cycle_len] = '\0'; trig_protected_mval_push_count = 0; xecute_buff = NULL; INCR_AND_PUSH_MV_STENT(protect_trig_mval); /* Protect protect_trig_mval from garbage collection */ for (indx = 1; indx <= count; indx++) { if ((0 != nam_indx) && (indx != nam_indx)) continue; MV_FORCE_MVAL(&mi, indx); BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(gbl_name, gbl_name_len, mi, trigger_subs[TRIGNAME_SUB], STRLEN(trigger_subs[TRIGNAME_SUB])); out_rec_ptr = out_rec; if (!gvcst_get(&trigger_value)) { /* There has to be a #NAME */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, gbl_name_len, gbl_name, gbl_name_len, gbl_name, LEN_AND_LIT("\"#NAME\"")); } *out_rec_ptr++ = COMMENT_LITERAL; COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, TRIGGER_NAME_COMMENT, STR_LIT_LEN(TRIGGER_NAME_COMMENT)); COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, trigger_value.str.addr, trigger_value.str.len - 1); /* remove last # in name */ COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, SPANREG_REGION_LIT, SPANREG_REGION_LITLEN); COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, gv_cur_region->rname, gv_cur_region->rname_len); COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, ")", 1); COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, TRIGGER_CYCLE_COMMENT, STR_LIT_LEN(TRIGGER_CYCLE_COMMENT)); COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, cycle, STRLEN(cycle)); util_out_print_gtmio("!AD", FLUSH, (unsigned int)(out_rec_ptr - out_rec), out_rec); out_rec_ptr = out_rec; *out_rec_ptr++ = '+'; *out_rec_ptr++ = '^'; COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, gbl_name, gbl_name_len); BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(gbl_name, gbl_name_len, mi, trigger_subs[GVSUBS_SUB], STRLEN(trigger_subs[GVSUBS_SUB])); if (gvcst_get(&trigger_value)) { COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, "(", 1); sub_ptr = trigger_value.str.addr; sub_len = trigger_value.str.len; write_subscripts(out_rec, &out_rec_ptr, &sub_ptr, &sub_len); COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, ")", 1); } /* Assert that BHASH and LHASH are not part of NUM_SUBS calculation * (confirms the -2 done in the #define of NUM_SUBS). */ assert(BHASH_SUB == NUM_SUBS); assert(LHASH_SUB == (NUM_SUBS + 1)); for (sub_indx = 0; sub_indx < NUM_SUBS; sub_indx++) { if ((GVSUBS_SUB == sub_indx) || (CHSET_SUB == sub_indx)) continue; BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(gbl_name, gbl_name_len, mi, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx])); have_value = gvcst_get(protect_trig_mval); have_value = have_value && (protect_trig_mval->str.len); multi_record = FALSE; if (!have_value && (XECUTE_SUB == sub_indx)) { op_gvdata(&data_val); multi_record = (literal_ten.m[0] == data_val.m[0]) && (literal_ten.m[1] == data_val.m[1]); } if (have_value || multi_record) { if (TRIGNAME_SUB == sub_indx) { /* Output "-name=XYZ" only if it is user defined */ BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY(gbl_name, gbl_name_len, mi, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), mi); if (!trigger_user_name(protect_trig_mval->str.addr, protect_trig_mval->str.len)) continue; protect_trig_mval->str.len--; /* Don't include trailing # */ } COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, " ", 1); COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, triggerfile_quals[sub_indx], STRLEN(triggerfile_quals[sub_indx])); switch (sub_indx) { case DELIM_SUB: case ZDELIM_SUB: MAKE_ZWR_STR(protect_trig_mval->str.addr, protect_trig_mval->str.len, out_rec, out_rec_ptr); break; case XECUTE_SUB: /* After the buffer is malloc-ed by trigger_gbl_fill_xecute_buffer(), the * only exits from the inner loop have to go to the free() below. Exits * from the outer loop happen before the buffer is malloc-ed. So no leaks * by error returns. But just to make sure, let's assert that the buffer * pointer is NULL. */ assert(NULL == xecute_buff); xecute_buff = trigger_gbl_fill_xecute_buffer(gbl_name, gbl_name_len, &mi, multi_record ? NULL : protect_trig_mval, &xecute_len); multi_line = (NULL != memchr(xecute_buff, '\n', xecute_len)); assert(NULL != xecute_buff); if (multi_line) { COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, XTENDED_START, XTENDED_START_LEN); } else { tmp_str_ptr = xecute_buff + skip_chars; tmp_str_len = xecute_len - skip_chars; MAKE_ZWR_STR(tmp_str_ptr, tmp_str_len, out_rec, out_rec_ptr); } break; default: COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, protect_trig_mval->str.addr,protect_trig_mval->str.len); } } protect_trig_mval->mvtype = 0; /* can now be garbage collected in the next iteration */ } /* we had better have an XECUTE STRING, if not it is a restartable situation */ DEBUG_ONLY(if (NULL == xecute_buff) TREF(donot_commit) |= DONOTCOMMIT_TRIGGER_SELECT_XECUTE;) util_out_print_gtmio("!AD", FLUSH, (unsigned int)(out_rec_ptr - out_rec), out_rec); if (multi_line) { ptr1 = xecute_buff; ptrtop = xecute_buff + xecute_len; do { xecute_len = ptrtop - ptr1; ptr2 = memchr(ptr1, '\n', xecute_len); if (NULL == ptr2) ptr2 = ptrtop; /* if last line is not terminated by newline, make it so */ out_rec_ptr = out_rec; COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, ptr1, UINTCAST(ptr2 - ptr1)); util_out_print_gtmio("!AD", FLUSH, (unsigned int)(out_rec_ptr - out_rec), out_rec); ptr1 = ptr2 + 1; } while (ptr1 < ptrtop); out_rec_ptr = out_rec; COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, XTENDED_STOP, XTENDED_STOP_LEN); util_out_print_gtmio("!AD", FLUSH, (unsigned int)(out_rec_ptr - out_rec), out_rec); } if (NULL != xecute_buff) { free(xecute_buff); xecute_buff = NULL; } } DECR_AND_POP_MV_STENT(); } } STATICFNDEF void write_gbls_or_names(char *gbl_name, uint4 gbl_name_len, boolean_t trig_name) { char save_name[MAX_MIDENT_LEN], curr_name[MAX_MIDENT_LEN], curr_gbl[MAX_MIDENT_LEN]; boolean_t wildcard; mval mv_curr_nam; mval trig_gbl; mval mv_trigger_val; int indx; char *ptr; uint4 curr_name_len; int trigvn_len; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* If ^#t needs to be upgraded, issue error. Cannot read older ^#t format that newer version does not always understand */ if (cs_addrs->hdr->hasht_upgrade_needed) rts_error_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_NEEDTRIGUPGRD, 2, DB_LEN_STR(gv_cur_region)); assert((0 < gbl_name_len) && (MAX_MIDENT_LEN >= gbl_name_len)); STATIC_ANALYSIS_ONLY(gbl_name_len = MIN(gbl_name_len, MAX_MIDENT_LEN)); /* 4SCA: GV names from DB are <= MAX_MIDENT_LEN */ memcpy(save_name, gbl_name, gbl_name_len); wildcard = (NULL != (ptr = memchr(gbl_name, '*', gbl_name_len))); if (wildcard) { STATIC_ANALYSIS_ONLY(if (0 < gbl_name_len)) /* 4SCA: GV names from DB <= MAX_MIDENT_LEN and > 0*/ gbl_name_len--; assert(INTCAST(ptr - gbl_name) == gbl_name_len); } memcpy(curr_name, gbl_name, gbl_name_len); curr_name_len = gbl_name_len; while (TRUE) { if (trig_name) { /* $get(^#t("#TNAME",trigger_name)) */ BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), curr_name, curr_name_len); if (!gvcst_get(&mv_trigger_val)) { if (wildcard) { op_gvorder(&mv_curr_nam); if (0 == mv_curr_nam.str.len) break; memcpy(curr_name, mv_curr_nam.str.addr, mv_curr_nam.str.len); curr_name_len = mv_curr_nam.str.len; if (0 != memcmp(curr_name, save_name, gbl_name_len)) break; continue; } break; } ptr = mv_trigger_val.str.addr; trigvn_len = MIN(mv_trigger_val.str.len, MAX_MIDENT_LEN); STRNLEN(ptr, trigvn_len, trigvn_len); ptr += trigvn_len; if ((mv_trigger_val.str.len == trigvn_len) || ('\0' != *ptr)) { /* We expect $c(0) in the middle of addr. If we dont find it, this is a restartable situation */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, LEN_AND_LIT("\"#TNAME\""), curr_name_len, curr_name, mv_trigger_val.str.len, mv_trigger_val.str.addr); } ptr++; A2I(ptr, mv_trigger_val.str.addr + mv_trigger_val.str.len, indx); if (1 > indx) { /* We expect a valid index */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(8) ERR_TRIGDEFBAD, 6, LEN_AND_LIT("\"#TNAME\""), curr_name_len, curr_name, mv_trigger_val.str.len, mv_trigger_val.str.addr); } /* Use a local buffer to avoid possible garbage collection issues from write_out_trigger below */ memcpy(curr_gbl, mv_trigger_val.str.addr, trigvn_len); STR2MVAL(trig_gbl, curr_gbl, trigvn_len); } else { STR2MVAL(trig_gbl, curr_name, curr_name_len); indx = 0; } write_out_trigger(trig_gbl.str.addr, trig_gbl.str.len, indx); if (wildcard) { if (trig_name) { BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), curr_name, curr_name_len); } else { BUILD_HASHT_SUB_CURRKEY(curr_name, curr_name_len); } op_gvorder(&mv_curr_nam); if (0 == mv_curr_nam.str.len) break; memcpy(curr_name, mv_curr_nam.str.addr, mv_curr_nam.str.len); if (0 != memcmp(curr_name, save_name, gbl_name_len)) break; curr_name_len = mv_curr_nam.str.len; } else break; } } STATICFNDEF void dump_all_triggers(void) { mval curr_gbl_name, val; gd_region *reg; gv_namehead *save_gvtarget; int reg_index; char global[MAX_MIDENT_LEN]; int gbl_len; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(NULL != gd_header); save_gvtarget = gv_target; for (reg_index = 0, reg = gd_header->regions; reg_index < gd_header->n_regions; reg_index++, reg++) { if (IS_STATSDB_REGNAME(reg)) continue; GVTR_SWITCH_REG_AND_HASHT_BIND_NAME(reg); if (NULL == cs_addrs) /* not BG or MM access method */ continue; /* gv_target now points to ^#t in region "reg" */ if (0 != gv_target->root) { /* If ^#t needs to be upgraded, issue error. Cannot read older ^#t format */ if (cs_addrs->hdr->hasht_upgrade_needed) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_NEEDTRIGUPGRD, 2, DB_LEN_STR(gv_cur_region)); op_gvdata(&val); if ((literal_ten.m[0] == val.m[0]) && (literal_ten.m[1] == val.m[1])) { /* $DATA(^#t) is 10 - get first subscript (trigger's global) */ BUILD_HASHT_SUB_CURRKEY(LITERAL_MAXHASHVAL, STR_LIT_LEN(LITERAL_MAXHASHVAL)); while (TRUE) { op_gvorder(&curr_gbl_name); if (0 == curr_gbl_name.str.len) break; gbl_len = curr_gbl_name.str.len; memcpy(global, curr_gbl_name.str.addr, gbl_len); write_out_trigger(global, gbl_len, 0); BUILD_HASHT_SUB_CURRKEY(global, gbl_len); } } else assert((literal_zero.m[0] == val.m[0]) && (literal_zero.m[1] == val.m[1])); } } } /* returns TRUE (not TRIG_FAILURE) in case of failure */ boolean_t trigger_select_tpwrap(char *select_list, uint4 select_list_len, char *file_name, uint4 file_name_len) { mval op_val, op_pars; boolean_t select_status; io_pair save_io_curr_device; mval ts_mv; int loopcnt; DEBUG_ONLY(unsigned int lcl_t_tries;) enum cdb_sc failure; static readonly unsigned char open_params_list[] = { (unsigned char)iop_m, (unsigned char)iop_noreadonly, (unsigned char)iop_nowrap, (unsigned char)iop_stream, (unsigned char)iop_eol }; static readonly unsigned char use_params[] = { (unsigned char)iop_nowrap, (unsigned char)iop_eol }; static readonly unsigned char no_param = (unsigned char)iop_eol; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* make a local copy of the select list and use it to avoid string-pool problems */ if (MAX_BUFF_SIZE <= select_list_len) return TRUE; /* failure return */ gvinit(); op_pars.mvtype = MV_STR; op_val.mvtype = MV_STR; if (0 == file_name_len) { op_pars.str.len = SIZEOF(use_params); op_pars.str.addr = (char *)use_params; if (IS_MUPIP_IMAGE) { PRINTF("\n"); FFLUSH(NULL); op_val.str.len = io_std_device.out->trans_name->len; op_val.str.addr = io_std_device.out->trans_name->dollar_io; } else { op_val.str.len = io_curr_device.out->trans_name->len; op_val.str.addr = io_curr_device.out->trans_name->dollar_io; } } else { op_pars.str.len = SIZEOF(open_params_list); op_pars.str.addr = (char *)open_params_list; op_val.str.len = file_name_len; op_val.str.addr = (char *)file_name; (*op_open_ptr)(&op_val, &op_pars, (mval *)&literal_zero, NULL); } save_io_curr_device = io_curr_device; op_use(&op_val, &op_pars); TREF(ztrig_use_io_curr_device) = TRUE; select_status = TRIG_SUCCESS; ts_mv.mvtype = MV_STR; ts_mv.str.len = 0; ts_mv.str.addr = NULL; if (0 == dollar_tlevel) { /* If not already wrapped in TP, wrap it now implicitly */ assert(!donot_INVOKE_MUMTSTART); DEBUG_ONLY(donot_INVOKE_MUMTSTART = TRUE); /* Note down dollar_tlevel before op_tstart. This is needed to determine if we need to break from the for-loop * below after a successful op_tcommit of the trigger select . We cannot check that dollar_tlevel is zero * since the op_tstart done below can be a nested sub-transaction */ op_tstart((IMPLICIT_TSTART + IMPLICIT_TRIGGER_TSTART), TRUE, &ts_mv, 0); /* 0 ==> save no locals but RESTART OK */ /* The following for loop structure is similar to that in function "trigger_trgfile_tpwrap" and various * other places so any changes here might need to be reflected there as well. */ for (loopcnt = 0; ; loopcnt++) { assert(donot_INVOKE_MUMTSTART); /* Make sure still set */ DEBUG_ONLY(lcl_t_tries = t_tries); select_status = trigger_select_tpwrap_helper(select_list, select_list_len); if (0 == dollar_tlevel) break; assert(0 < t_tries); assert((CDB_STAGNATE == t_tries) || (lcl_t_tries == t_tries - 1)); failure = LAST_RESTART_CODE; assert(((cdb_sc_onln_rlbk1 != failure) && (cdb_sc_onln_rlbk2 != failure)) || !gv_target || !gv_target->root); assert((cdb_sc_onln_rlbk2 != failure) || !IS_GTM_IMAGE || TREF(dollar_zonlnrlbk)); if (cdb_sc_onln_rlbk2 == failure) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DBROLLEDBACK); /* else if (cdb_sc_onln_rlbk1 == status) we don't need to do anything other than trying again. Since this * is ^#t global, we don't need to GVCST_ROOT_SEARCH before continuing with the next restart because the * trigger load logic already takes care of doing INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED before doing the * actual trigger load */ /* We expect the above function to return with either op_tcommit or a tp_restart invoked. * In the case of op_tcommit, we expect dollar_tlevel to be 0 and if so we break out of the loop. * In the tp_restart case, we expect a maximum of 4 tries/retries and much lesser usually. * Additionally we also want to avoid an infinite loop so limit the loop to what is considered * a huge iteration count and assertpro if that is reached as it suggests an out-of-design situation. */ assertpro(TPWRAP_HELPER_MAX_ATTEMPTS >= loopcnt); } } else { select_status = trigger_select(select_list, select_list_len); assert(0 < dollar_tlevel); } TREF(ztrig_use_io_curr_device) = FALSE; if (0 != file_name_len) { op_val.mvtype = op_pars.mvtype = MV_STR; op_val.str.addr = (char *)file_name;; op_val.str.len = file_name_len; op_pars.str.len = SIZEOF(no_param); op_pars.str.addr = (char *)&no_param; op_close(&op_val, &op_pars); /* Return back to the current device */ io_curr_device = save_io_curr_device; } return (TRIG_FAILURE == select_status); } STATICFNDEF boolean_t trigger_select_tpwrap_helper(char *select_list, uint4 select_list_len) { enum cdb_sc cdb_status; boolean_t select_status; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ESTABLISH_RET(trigger_tpwrap_ch, TRIG_FAILURE); select_status = trigger_select(select_list, select_list_len); if (TRIG_SUCCESS == select_status) { GVTR_OP_TCOMMIT(cdb_status); if (cdb_sc_normal != cdb_status) t_retry(cdb_status); /* won't return */ } else { /* Record cannot be committed - undo everything */ assert(donot_INVOKE_MUMTSTART); DEBUG_ONLY(donot_INVOKE_MUMTSTART = FALSE); /* Print $ztrigger/mupip-trigger output before rolling back TP */ TP_ZTRIGBUFF_PRINT; OP_TROLLBACK(-1); /* returns but kills implicit transaction */ } REVERT; return select_status; } STATICFNDEF boolean_t trigger_select(char *select_list, uint4 select_list_len) { boolean_t dump_all; char save_select_list[MAX_BUFF_SIZE]; char *sel_ptr, *strtok_ptr, *prev_ptr, *ptr1, *ptr2; int gbl_len, prev_len; mname_entry gvname; int len, len1, badpos; boolean_t trig_name; gv_key_buf save_currkey; gd_region *save_gv_cur_region; gv_namehead *save_gv_target; sgm_info *save_sgm_info_ptr; jnlpool_addrs_ptr_t save_jnlpool; boolean_t select_status; gvnh_reg_t *gvnh_reg; gd_binding *map, *start_map, *end_map; gd_region *reg, *reg_start, *reg_top; int *reg_done, reg_array_size, reg_index; assert(dollar_tlevel); select_status = TRIG_SUCCESS; memcpy(save_select_list, select_list, select_list_len); save_select_list[select_list_len] = '\0'; dump_all = FALSE; prev_len = 0; if (0 == select_list_len) dump_all = TRUE; else { for (ptr1 = save_select_list, len = select_list_len; (NULL != (ptr2 = strchr(ptr1, '*'))) && (len > (len1 = INTCAST(ptr2 - ptr1))); ptr1 = ptr2 + 1) { /* look for either a real "dump-it-all" *, an error *, or a wildcard * */ /* A "*" anywhere in the select list (at a place where a global name would be) is the same as only a "*" */ len -= (len1 + 1); /* Length after the "*" -- len1 is length before the "*" */ assert((0 <= len1) && (0 <= len)); if (dump_all = ((0 == len1) && (0 == len) && (ptr1 == save_select_list)) || ((0 == len1) && (0 == len) && (',' == *(ptr2 - 1))) || ((0 == len1) && (0 < len) && (',' == *(ptr2 + 1))) || ((0 < len1) && (0 == len) && (',' == *(ptr2 - 1))) || ((0 < len1) && (0 < len) && (',' == *(ptr2 - 1)) && (',' == *(ptr2 + 1)))) break; } } if (dump_all) dump_all_triggers(); else if (0 < select_list_len) { len = select_list_len; sel_ptr = STRTOK_R(save_select_list, ",", &strtok_ptr); do { if (NULL == sel_ptr) /* Unlikely event that the first return is NULL */ break; trig_name = ('^' != *sel_ptr); ptr1 = sel_ptr; len1 = STRLEN(ptr1); if (!trig_name) { ptr1++; len1--; CHECK_FOR_M_NAME(sel_ptr, ptr1, len1, "Invalid global variable name in SELECT list: ", select_status, MAX_MIDENT_LEN); if ((0 != len1) && ((1 != len1) || ('*' != *ptr1))) { badpos = len1; INVALID_NAME_ERROR("Invalid global variable name in SELECT list: ", sel_ptr, select_status, badpos); continue; } gbl_len = NAM_LEN(sel_ptr + 1, (int)(ptr1 - sel_ptr) - 1); ptr1 = sel_ptr + 1; /* Skip only if the previous global is the same as the current */ if ((prev_len == gbl_len) && (0 == memcmp(prev_ptr, ptr1, gbl_len))) continue; SAVE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); prev_ptr = ptr1; prev_len = gbl_len; start_map = gv_srch_map(gd_header, ptr1, gbl_len, SKIP_BASEDB_OPEN_FALSE); ptr1[gbl_len - 1]++; end_map = gv_srch_map(gd_header, ptr1, gbl_len, SKIP_BASEDB_OPEN_FALSE); ptr1[gbl_len - 1]--; gvname.var_name.addr = ptr1; gvname.var_name.len = gbl_len; if (start_map != end_map) { /* Global specification involves multiple regions */ reg_start = &gd_header->regions[0]; reg_array_size = gd_header->n_regions; reg_done = malloc(reg_array_size * SIZEOF(*reg_done)); memset(reg_done, 0, reg_array_size * SIZEOF(*reg_done)); if ('*' == ptr1[gbl_len]) gvname.var_name.len++; for (map = start_map; map <= end_map; map++) { OPEN_BASEREG_IF_STATSREG(map); reg = map->reg.addr; GET_REG_INDEX(gd_header, reg_start, reg, reg_index); /* sets "reg_index" */ assert(reg_array_size > reg_index); if (!reg_done[reg_index]) { /* this region first encountered now */ if (IS_STATSDB_REGNAME(reg)) continue; GVTR_SWITCH_REG_AND_HASHT_BIND_NAME(reg); if (NULL == cs_addrs) /* not BG or MM access method */ continue; /* gv_target now points to ^#t in region "reg" */ if (0 != gv_target->root) write_gbls_or_names(gvname.var_name.addr, gvname.var_name.len, trig_name); reg_done[reg_index] = TRUE; } } free(reg_done); } else { /* Global specification involves only one region */ COMPUTE_HASH_MNAME(&gvname); GV_BIND_NAME_ONLY(gd_header, &gvname, gvnh_reg); /* skip selecting/dumping triggers if not BG or MM access method */ if (NULL != cs_addrs) { if ('*' == ptr1[gbl_len]) gvname.var_name.len++; SET_GVTARGET_TO_HASHT_GBL(gv_target->gd_csa); INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED; if (0 != gv_target->root) write_gbls_or_names(gvname.var_name.addr, gvname.var_name.len, trig_name); } } } else { if (len1 != (badpos = validate_input_trigger_name(ptr1, len1, NULL))) /* assignment is intended */ { /* is the input name valid */ INVALID_NAME_ERROR("Invalid name entry in SELECT list: ", sel_ptr, select_status, badpos); continue; } if (TRIGNAME_SEQ_DELIM == *(sel_ptr + (len1 - 1))) len1--; /* drop the trailing # sign */ gvname.var_name.addr = sel_ptr; gvname.var_name.len = len1; SAVE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); for (reg = gd_header->regions, reg_top = reg + gd_header->n_regions; reg < reg_top; reg++) { if (IS_STATSDB_REGNAME(reg)) continue; GVTR_SWITCH_REG_AND_HASHT_BIND_NAME(reg); if (NULL == cs_addrs) /* not BG or MM access method */ continue; /* gv_target now points to ^#t in region "reg" */ if (0 == gv_target->root) continue; write_gbls_or_names(gvname.var_name.addr, gvname.var_name.len, trig_name); } } RESTORE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); } while (NULL != (sel_ptr = STRTOK_R(NULL, ",", &strtok_ptr))); /* Embedded assignment is intended */ } return select_status; } #endif fis-gtm-V7.0-005/sr_unix/trigger_select_protos.h0000644000032200000250000000224014342376330020620 0ustar librarygtc/**************************************************************** * * * Copyright 2010, 2014 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. * * * ****************************************************************/ #ifndef TRIGGER_SELECT_PROTOS_INCLUDED #define TRIGGER_SELECT_PROTOS_INCLUDED STATICFNDCL void write_subscripts(char *out_rec, char **out_ptr, char **sub_ptr, int *sub_len); STATICFNDCL void write_out_trigger(char *gbl_name, uint4 gbl_name_len, int nam_indx); STATICFNDCL void write_gbls_or_names(char *gbl_name, uint4 gbl_name_len, boolean_t trig_name); STATICFNDCL void dump_all_triggers(void); boolean_t trigger_select_tpwrap(char *select_list, uint4 select_list_len, char *file_name, uint4 file_name_len); STATICFNDCL boolean_t trigger_select_tpwrap_helper(char *select_list, uint4 select_list_len); STATICFNDCL boolean_t trigger_select(char *select_list, uint4 select_list_len); #endif /* TRIGGER_SELECT_PROTOS_INCLUDED */ fis-gtm-V7.0-005/sr_unix/trigger_source_read_andor_verify.c0000644000032200000250000006312414342376335023005 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2021 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_stdio.h" #ifdef GTM_TRIGGER #include "gtmio.h" #include "error.h" #include "gdsroot.h" /* for gdsfhead.h */ #include "gdsbt.h" /* for gdsfhead.h */ #include "gdsfhead.h" #include "gvcst_protos.h" #include #include "gv_trigger.h" #include "gtm_trigger.h" #include "trigger.h" #include "trigger_fill_xecute_buffer.h" #include "trigger_gbl_fill_xecute_buffer.h" #include "trigger_read_andor_locate.h" #include "gtm_trigger_trc.h" #include "gvsub2str.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include "format_targ_key.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include "hashtab.h" /* for STR_HASH (in COMPUTE_HASH_MNAME) */ #include "targ_alloc.h" /* for SET_GVTARGET_TO_HASHT_GBL */ #include "filestruct.h" /* for INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED (FILE_INFO) */ #include "mvalconv.h" #include "gdscc.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "tp.h" /* for sgm_info */ #include "tp_frame.h" #include "tp_restart.h" #include "tp_set_sgm.h" #include "t_retry.h" #include "op.h" #include "op_tcommit.h" #include "memcoherency.h" #include "gtmimagename.h" #include "cdb_sc.h" #include "mv_stent.h" #include "gv_trigger_protos.h" #include "hashtab_mname.h" #include "hashtab_str.h" /* needed by trigger_update_protos.h */ #include "trigger_update_protos.h" /* for trigger_name_search prototype */ #include "change_reg.h" /* for "change_reg" prototype */ #include "gvnh_spanreg.h" #include "min_max.h" #include "io.h" #include "repl_msg.h" /* for gtmsource.h */ #include "gtmsource.h" /* for jnlpool_addrs_ptr_t */ GBLREF uint4 dollar_tlevel; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF gd_addr *gd_header; GBLREF gv_key *gv_currkey; GBLREF gd_region *gv_cur_region; GBLREF sgm_info *sgm_info_ptr; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF gv_namehead *gv_target; GBLREF int tprestart_state; GBLREF tp_frame *tp_pointer; GBLREF int4 gtm_trigger_depth; GBLREF trans_num local_tn; GBLREF unsigned int t_tries; GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; GBLREF rtn_tabent *rtn_names_end; #ifdef DEBUG GBLREF boolean_t donot_INVOKE_MUMTSTART; #endif LITREF mval literal_batch; STATICFNDCL CONDITION_HANDLER(trigger_source_raov_ch); STATICFNDCL int trigger_source_raov(mstr *trigname, gd_region *reg, rhdtyp **rtn_vec); STATICFNDCL int trigger_source_raov_tpwrap_helper(mstr *trigname, gd_region *reg, rhdtyp **rtn_vec); STATICFNDCL boolean_t trigger_source_raov_trigload(mstr *trigname, gv_trigger_t **ret_trigdsc, gd_region *reg); error_def(ERR_DBROLLEDBACK); error_def(ERR_TPRETRY); error_def(ERR_TRIGCOMPFAIL); error_def(ERR_TRIGNAMENF); /* Similar condition handler to gvtr_tpwrap_ch except we don't insist on first_sgm_info being set */ CONDITION_HANDLER(trigger_source_raov_ch) { int rc; START_CH(TRUE); if ((int)ERR_TPRETRY == SIGNAL) { /* This only happens at the outer-most TP layer so state should be normal now */ assert(TPRESTART_STATE_NORMAL == tprestart_state); tprestart_state = TPRESTART_STATE_NORMAL; rc = tp_restart(1, !TP_RESTART_HANDLES_ERRORS); assert(0 == rc); assert(TPRESTART_STATE_NORMAL == tprestart_state); /* No rethrows possible */ DBGTRIGR((stderr, "trigger_source_ch: Unwinding due to TP REstart\n")); UNWIND(NULL, NULL); } NEXTCH; } gd_region *find_region(mstr *regname) { gd_region *reg, *reg_top; mstr tmpstr; int comp; assert(NULL != gd_header); for (reg = gd_header->regions, reg_top = reg + gd_header->n_regions; reg < reg_top; reg++) { tmpstr.len = reg->rname_len; tmpstr.addr = (char *)reg->rname; MSTR_CMP(tmpstr, *regname, comp); if (0 == comp) return reg; } return NULL; } /* Routine to check on the named trigger. This routine is called from (at least) two places: the trigger_locate_andor_load() * where is it called when the attempted locating of a trigger failed so it must be loaded. The other time it is called from * op_setzbrk() to load and/or compile the trigger. In both cases, the trigger may be loaded or * its source loaded already so we perform a validation on it that it is the current trigger. If the trigger contents are * stale, Our actions depend on what mode we are in. If already in TP, we cause the trigger to be unloaded and signal a * restart. If not in TP, we just unload the trigger and work our full mojo on it to reload the current version. * * The following sitations can exist: * * 1. No trigger by the given name is loaded. For this situation, we need to locate and load the trigger and its source. * 2. Trigger is loaded but no source is in the trigger source buffer. For this situation, load the source and mark the * trigger as part of the transaction. * 3. Trigger and source both loaded. For this situation, mark the trigger as part of the transaction. * * If a TP fence is not in place, we provide an implcit wrapper that will catch our restarts and reinvoke the logic * that will reload the trigger from scratch. We do not verify that the trigger information in memory is fresh. * * If a TP FENCE is in place and we are in the final retry, we verify that the triggers are current and reload them if * not. This avoids the possibility of using stale triggers. * * Note the trigger_locate_andor_load() routine acts much like a stripped down verison of this routine and its subroutines * so changes here should be reflected there. * * Note, this routine is for loading trigger source when we are not driving triggers. The trigger_fill_xecute_buffer() * should be used when fetching source for trigger execution because it is lighter weight with built-in trigger refetch * logic since we are using the globals the triggers live in. In this case, the trigger access is adhoc for the $TEXT() * ZPRINT and ZBREAK uses. */ int trigger_source_read_andor_verify(mstr *trigname, rhdtyp **rtn_vec) { char *ptr, *ptr_beg, *ptr_top; enum cdb_sc failure; gd_region *reg; int src_fetch_status; mstr regname; DEBUG_ONLY(unsigned int lcl_t_tries;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(NULL != trigname); assert((NULL != trigname->addr) && (0 != trigname->len)); if (NULL == gd_header) gvinit(); DBGTRIGR((stderr, "trigger_source_read_andor_verify: Entered with $tlevel=%d, $trigdepth=%d\n", dollar_tlevel, gtm_trigger_depth)); /* * Input parameter "trigname" is of the form * a) <21-BYTE-MAX-TRUNCATED-GBLNAME>##[RUNTIME-DISAMBIGUATOR][/REGION-NAME] OR * b) <28-BYTE-USER-SPECIFIED-TRIGNAME>#[RUNTIME-DISAMBIGUATOR][/REGION-NAME] * where * <21-BYTE-MAX-TRUNCATED-GBLNAME># OR <28-BYTE-USER-SPECIFIED-TRIGNAME> is the * auto-generated or user-specified trigger name we are searching for * RUNTIME-DISAMBIGUATOR is the unique string appended at the end by the runtime to distinguish * multiple triggers in different regions with the same auto-generated or user-given name * REGION-NAME is the name of the region in the gld where we specifically want to search for trigger names * [] implies optional parts * * Example usages are * x# : trigger routine user-named "x" * x#1# : trigger routine auto-named "x#1" * x#1#A : trigger routine auto-named "x#1" but also runtime disambiguated by "#A" at the end * x#/BREG : trigger routine user-named "x" in region BREG * x#A/BREG : trigger routine user-named "x", runtime disambiguated by "#A", AND in region BREG * x#1#/BREG : trigger routine auto-named "x#1" in region BREG * x#1#A/BREG : trigger routine auto-named "x#1", runtime disambiguated by "#A", AND in region BREG */ /* First lets locate the trigger. Try simple way first - lookup in routine name table. * But "find_rtn_tabent" function has no clue about REGION-NAME so remove /REGION-NAME (if any) before invoking it. */ regname.len = 0; reg = NULL; for (ptr_beg = trigname->addr, ptr_top = ptr_beg + trigname->len, ptr = ptr_top - 1; ptr >= ptr_beg; ptr--) { /* If we see a '#' and have not yet seen a '/' we are sure no region-name disambiguator has been specified */ if ('#' == *ptr) break; if ('/' == *ptr) { trigname->len = ptr - trigname->addr; ptr++; regname.addr = ptr; regname.len = ptr_top - ptr; reg = find_region(®name); /* find region "regname" in "gd_header" */ if (NULL == reg) { /* Specified region-name is not present in current gbldir. * Treat non-existent region name as if trigger was not found. */ ISSUE_TRIGNAMENF_ERROR_IF_APPROPRIATE(trigname); return TRIG_FAILURE_RC; } break; } } /* First determination is if a TP fence is already in operation or not */ if (0 == dollar_tlevel) { /* We need a TP fence - provide one */ assert(!donot_INVOKE_MUMTSTART); DEBUG_ONLY(donot_INVOKE_MUMTSTART = TRUE); /* 0 ==> save no locals but RESTART OK. Note we do NOT mark this as a TRIGGER_IMPLICIT_TSTART because we * both have our own condition handler to take care of thrown restarts and because this is not a trigger * execution thing - just a load and possibly a trigger compile. */ op_tstart(IMPLICIT_TSTART, TRUE, &literal_batch, 0); for (;;) { /* Now that we are TP wrapped, fetch the trigger source lines from the ^#t global */ DEBUG_ONLY(lcl_t_tries = t_tries); src_fetch_status = trigger_source_raov_tpwrap_helper(trigname, reg, rtn_vec); if ((0 == src_fetch_status) || (TRIG_FAILURE_RC == src_fetch_status)) { assert(0 == dollar_tlevel); /* op_tcommit should have made sure of this */ break; } /* A restart has been signalled inside trigger fetch code for this possibly implicit TP wrapped * transaction. Redo source fetch logic. */ assert(ERR_TPRETRY == src_fetch_status); assert(CDB_STAGNATE >= t_tries); assert(0 < t_tries); assert((CDB_STAGNATE == t_tries) || (lcl_t_tries == t_tries - 1)); failure = LAST_RESTART_CODE; assert(((cdb_sc_onln_rlbk1 != failure) && (cdb_sc_onln_rlbk2 != failure)) || !gv_target || !gv_target->root); assert((cdb_sc_onln_rlbk2 != failure) || TREF(dollar_zonlnrlbk)); if (cdb_sc_onln_rlbk2 == failure) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DBROLLEDBACK); /* else if (cdb_sc_onln_rlbk1 == status) we don't need to do anything other than proceeding with the next * retry. Even though online rollback restart resets root block to zero for all gv_targets, ^#t root is * always established in gvtr_db_read_hasht (called below). We don't care about the root block being reset * for other gv_target because when they are referenced later in the process, op_gvname will be done and * that will anyways establish the root block numbers once again. */ } } else /* no return if TP restart */ src_fetch_status = trigger_source_raov(trigname, reg, rtn_vec); assert((0 == src_fetch_status) || (TRIG_FAILURE_RC == src_fetch_status)); DBGTRIGR((stderr, "trigger_source_read_andor_verify: leaving with source from 0x%lx\n", (*rtn_vec) ? (*((rhdtyp **)rtn_vec))->trigr_handle : NULL)); assert((0 != src_fetch_status) || (NULL != *rtn_vec)); /* Either got an error or rtnvector returned */ return src_fetch_status; } /* Now TP wrap and fetch the trigger source lines from the ^#t global */ STATICFNDEF int trigger_source_raov_tpwrap_helper(mstr *trigname, gd_region *reg, rhdtyp **rtn_vec) { enum cdb_sc cdb_status; int rc; DBGTRIGR((stderr, "trigger_source_tpwrap_helper: Entered\n")); ESTABLISH_RET(trigger_source_raov_ch, SIGNAL); assert(donot_INVOKE_MUMTSTART); rc = trigger_source_raov(trigname, reg, rtn_vec); assert((0 == rc) || (TRIG_FAILURE_RC == rc)); /* Finish it now verifying it completed successfully */ GVTR_OP_TCOMMIT(cdb_status); if (cdb_sc_normal != cdb_status) { DBGTRIGR((stderr, "trigger_source_tpwrap_helper: Commit failed - throwing TP restart\n")); t_retry(cdb_status); } REVERT; return rc; } /* Routine to do the dirty work of resolving a trigger name into a trigger and perform the missing parts of * loading the trigger, loading the source, verifying proper source/trigger is loaded and compiling if * desired. If we complete successfully, returns 0. Error returns caught by condition handlers can return other values. */ STATICFNDEF int trigger_source_raov(mstr *trigname, gd_region *reg, rhdtyp **rtn_vec) { boolean_t runtime_disambiguator_specified; gd_region *save_gv_cur_region; gv_key_buf save_currkey; gv_namehead *gvt; gv_namehead *save_gv_target; gvnh_reg_t *gvnh_reg; gv_trigger_t *trigdsc; gvt_trigger_t *gvt_trigger; int index; mident rtn_name; mstr gbl, xecute_buff; mval trig_index; rhdtyp *rtn_vector; rtn_tabent *rttabent; sgm_info *save_sgm_info_ptr; jnlpool_addrs_ptr_t save_jnlpool; sgmnt_addrs *csa, *regcsa; sgmnt_data_ptr_t csd; boolean_t db_trigger_cycle_mismatch, ztrig_cycle_mismatch, needs_reload = FALSE; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(dollar_tlevel); /* A TP wrap should have been done by the caller if needed */ DBGTRIGR((stderr, "trigger_source_raov: Entered\n")); /* Before we try to save anything, see if there is something to save and initialize stuff if not */ SAVE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); if (NULL != *rtn_vec) rtn_vector = *rtn_vec; else if (find_rtn_tabent(&rttabent, trigname)) rtn_vector = rttabent->rt_adr; else rtn_vector = NULL; DBGTRIGR((stderr, "trigger_source_raov: routine was %sfound\n", (NULL == rtn_vector)?"not ":"")); /* If region is specified, a null-runtime-disambiguator is treated as if runtime-disambiguator was not specified. * If region is NOT specified, a null-runtime-disambiguator is treated as if runtime-disambiguator was specified. */ runtime_disambiguator_specified = ('#' != trigname->addr[trigname->len - 1]); if (!runtime_disambiguator_specified && (NULL != reg)) { /* Region-name has been specified and no runtime-disambiguator specified. Need to further refine the * search done by find_rtn_tabent to focus on the desired region in case multiple routines with the same * trigger name (but different runtime-disambiguators) exist. */ rtn_name.len = MIN(trigname->len, MAX_MIDENT_LEN); rtn_name.addr = trigname->addr; if (!reg->open) gv_init_reg(reg, NULL); /* Open the region before obtaining "csa" */ regcsa = &FILE_INFO(reg)->s_addrs; assert('#' == rtn_name.addr[rtn_name.len - 1]); for ( ; rttabent <= rtn_names_end; rttabent++) { if ((rttabent->rt_name.len < rtn_name.len) || memcmp(rttabent->rt_name.addr, rtn_name.addr, rtn_name.len)) { /* Past the list of routines with same name as trigger but different runtime disambiguators */ rtn_vector = NULL; break; } rtn_vector = rttabent->rt_adr; trigdsc = (gv_trigger_t *)rtn_vector->trigr_handle; gvt_trigger = trigdsc->gvt_trigger; gvt = gvt_trigger->gv_target; /* Target region and trigger routine's region do not match, continue */ if (gvt->gd_csa != regcsa) continue; /* Check if global name associated with the trigger is indeed mapped to the corresponding region * by the gld. If not treat this case as if the trigger is invisible and move on */ gbl.addr = gvt->gvname.var_name.addr; gbl.len = gvt->gvname.var_name.len; TP_CHANGE_REG_IF_NEEDED(gvt->gd_csa->region); csa = cs_addrs; csd = csa->hdr; COMPUTE_HASH_MNAME(&gvt->gvname); GV_BIND_NAME_ONLY(gd_header, &gvt->gvname, gvnh_reg); /* does tp_set_sgm() */ if (((NULL == gvnh_reg->gvspan) && (gv_cur_region != reg)) || ((NULL != gvnh_reg->gvspan) && !gvnh_spanreg_ismapped(gvnh_reg, gd_header, reg))) continue; /* Target region and trigger routine's region match, break (this check is a formality) */ if (gvt->gd_csa == regcsa) break; } } csa = NULL; if (NULL == rtn_vector) { /* If runtime disambiguator was specified and routine is not found, look no further. * Otherwise, look for it in the #t global of any (or specified) region in current gbldir. */ DBGTRIGR((stderr, "trigger_source_raov: find trigger by name without disambiguator\n")); if (runtime_disambiguator_specified || (TRIG_FAILURE == trigger_source_raov_trigload(trigname, &trigdsc, reg))) { RESTORE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); ISSUE_TRIGNAMENF_ERROR_IF_APPROPRIATE(trigname); return TRIG_FAILURE_RC; } rtn_vector = trigdsc->rtn_desc.rt_adr; } else { /* Have a routine header addr. From that we can get the gv_trigger_t descriptor and from that, the * gvt_trigger and other necessities. */ DBGTRIGR((stderr, "trigger_source_raov: routine header found, now load it\n")); trigdsc = (gv_trigger_t *)rtn_vector->trigr_handle; gvt_trigger = trigdsc->gvt_trigger; /* We now know our base block now */ index = trigdsc - gvt_trigger->gv_trig_array + 1; /* We now know our trigger index value */ gvt = gv_target = gvt_trigger->gv_target; /* gv_target contains global name */ gbl.addr = gvt->gvname.var_name.addr; gbl.len = gvt->gvname.var_name.len; TP_CHANGE_REG_IF_NEEDED(gvt->gd_csa->region); csa = cs_addrs; csd = csa->hdr; if (runtime_disambiguator_specified && (NULL != reg)) { /* Runtime-disambiguator has been specified and routine was found. But region-name-disambiguator * has also been specified. Check if found routine is indeed in the specified region. If not * treat it as a failure to find the trigger. */ if (!reg->open) gv_init_reg(reg, NULL); if (&FILE_INFO(reg)->s_addrs != csa) { RESTORE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); ISSUE_TRIGNAMENF_ERROR_IF_APPROPRIATE(trigname); return TRIG_FAILURE_RC; } /* Check if global name is indeed mapped to this region by the gld. If not treat this case as * if the trigger is invisible and issue an error */ COMPUTE_HASH_MNAME(&gvt->gvname); GV_BIND_NAME_ONLY(gd_header, &gvt->gvname, gvnh_reg); /* does tp_set_sgm() */ if (((NULL == gvnh_reg->gvspan) && (gv_cur_region != reg)) || ((NULL != gvnh_reg->gvspan) && !gvnh_spanreg_ismapped(gvnh_reg, gd_header, reg))) { RESTORE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); ISSUE_TRIGNAMENF_ERROR_IF_APPROPRIATE(trigname); return TRIG_FAILURE_RC; } } assert(csd == cs_data); tp_set_sgm(); /* Ensure that we don't use stale triggers if we are in the third retry OR an explicit transaction */ if ((CDB_STAGNATE <= t_tries) || (!tp_pointer->implicit_tstart)) { ztrig_cycle_mismatch = (csa->db_dztrigger_cycle && (gvt->db_dztrigger_cycle != csa->db_dztrigger_cycle)); db_trigger_cycle_mismatch = (csa->db_trigger_cycle != gvt->db_trigger_cycle); needs_reload = (db_trigger_cycle_mismatch || ztrig_cycle_mismatch); DBGTRIGR((stderr, "trigger_source_raov: ztrig_cycle_mismatch=%d\tdb_trigger_cycle_mismatch=%d\treload?%d\n", ztrig_cycle_mismatch, db_trigger_cycle_mismatch, needs_reload)); if (needs_reload && (TRIG_FAILURE == trigger_source_raov_trigload(trigname, &trigdsc, reg)) && (NULL == rtn_vector->source_code)) { /* A reload failed (deleted or ^#t busted) and there is nothing cached, issue an error */ RESTORE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); ISSUE_TRIGNAMENF_ERROR_IF_APPROPRIATE(trigname); return TRIG_FAILURE_RC; } } /* Now that this TP has relied on this process' current trigger view, ensure that any later action in the same * TP that detects and reloads newer triggers (e.g. trigger invocation) restarts the entire TP transaction. */ gvt->trig_local_tn = local_tn; /* If this trigger is already compiled, we are done since they are compiled with embed_source */ if (NULL != trigdsc->rtn_desc.rt_adr) { DBGTRIGR((stderr, "trigger_source_raov: trigger already compiled, all done\n")); RESTORE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); *rtn_vec = rtn_vector; return 0; } /* Else load the trigger source as needed. If needs_reload is true then the source was loaded above */ if ((NULL == rtn_vector->source_code) && (!needs_reload)) { DBGTRIGR((stderr, "trigger_source_raov: get the source\n")); SET_GVTARGET_TO_HASHT_GBL(csa); INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED; assert(0 == trigdsc->xecute_str.str.len); /* Make sure not replacing/losing a buffer */ i2mval(&trig_index, index); xecute_buff.addr = trigger_gbl_fill_xecute_buffer(gbl.addr, gbl.len, &trig_index, NULL, (int4 *)&xecute_buff.len); trigdsc->xecute_str.str = xecute_buff; } } /* If the trigger is not already compiled, it needs to be since the routine header is the method for obtaining the * trigger descriptor. If routine is already compiled, we don't need to compile it again. */ if (NULL == trigdsc->rtn_desc.rt_adr) { DBGTRIGR((stderr, "trigger_source_raov: compile it\n")); if (0 != gtm_trigger_complink(trigdsc, TRUE)) { PRN_ERROR; /* Flush out any compiler messages for compile record */ RTS_ERROR_CSA_ABT(csa, 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)); rtn_vector = trigdsc->rtn_desc.rt_adr; } else { assert(rtn_vector && (NULL !=rtn_vector->source_code)); } RESTORE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); assert(rtn_vector); assert(trigdsc == rtn_vector->trigr_handle); *rtn_vec = rtn_vector; return 0; } /* Routine called when need triggers loaded for a given global */ STATICFNDEF boolean_t trigger_source_raov_trigload(mstr *trigname, gv_trigger_t **ret_trigdsc, gd_region *reg) { mval *val; char *ptr; int len; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; gv_namehead *gvt; gvt_trigger_t *gvt_trigger; mstr xecute_buff; mname_entry gvname; int index; mval trig_index; gv_trigger_t *trigdsc; uint4 cycle_start; gvnh_reg_t *gvnh_reg; boolean_t name_not_found; int trig_protected_mval_push_count; trig_protected_mval_push_count = 0; INCR_AND_PUSH_MV_STENT(val); /* Protect val from garbage collection */ assert(dollar_tlevel); DBGTRIGR((stderr, "trigger_source_raov_trigload: entry for %s\n", trigname->addr)); /* Find region trigger name is in. If "region-name" has been specified, find only in that region. */ name_not_found = !trigger_name_search(trigname->addr, trigname->len, val, ®); if (name_not_found) RETURN_AND_POP_MVALS(TRIG_FAILURE); /* Trigger name not found - nothing we can do */ /* Extract region name and trigger index number from result */ assert(NULL != reg); ptr = val->str.addr; len = MIN(val->str.len, MAX_MIDENT_LEN); /* Look for NULL within the MIN */ STRNLEN(ptr, len, len); ptr += len; if ((val->str.len == len) || ('\0' != *ptr)) { if (CDB_STAGNATE > t_tries) t_retry(cdb_sc_triggermod); /* Return an error instead of TRIGDEFBAD. The caller will throw the error */ RETURN_AND_POP_MVALS(TRIG_FAILURE); } ptr++; A2I(ptr, val->str.addr + val->str.len, index); if (1 > index) { /* Trigger indexes cannot be less than 1 */ if (CDB_STAGNATE > t_tries) t_retry(cdb_sc_triggermod); /* Return an error instead of TRIGDEFBAD. The caller will throw the error */ RETURN_AND_POP_MVALS(TRIG_FAILURE); } gvname.var_name.addr = val->str.addr; gvname.var_name.len = len; COMPUTE_HASH_MNAME(&gvname); GV_BIND_NAME_ONLY(gd_header, &gvname, gvnh_reg); /* does tp_set_sgm() */ if (NULL != gvnh_reg->gvspan) { assert(gvnh_spanreg_ismapped(gvnh_reg, gd_header, reg)); /* "trigger_name_search" would have ensured this */ GV_BIND_SUBSREG(gd_header, reg, gvnh_reg); /* sets gv_target/gv_cur_region/cs_addrs */ } else { /* gv_target/gv_cur_region/cs_addrs would have been set by GV_BIND_NAME_ONLY */ assert(gv_cur_region == reg); } gvt = gv_target; assert(cs_addrs == gvt->gd_csa); csa = gvt->gd_csa; csd = csa->hdr; assert(cs_data == csd); /* We now know the global/region we need to load triggers for - go for it */ cycle_start = csd->db_trigger_cycle; gvtr_db_read_hasht(csa); gvt_trigger = gvt->gvt_trigger; if (NULL == gvt_trigger) { if (CDB_STAGNATE > t_tries) t_retry(cdb_sc_triggermod); /* Return an error instead of TRIGDEFBAD. The caller will throw the error */ RETURN_AND_POP_MVALS(TRIG_FAILURE); } gvt->db_trigger_cycle = cycle_start; gvt->db_dztrigger_cycle = csa->db_dztrigger_cycle; gvt->trig_local_tn = local_tn; /* Mark this trigger as being referenced in this transaction */ DBGTRIGR((stderr, "trigger_source_raov_trigload: CSA->db_dztrigger_cycle=%d\n", csa->db_dztrigger_cycle)); DBGTRIGR((stderr, "trigger_source_raov_trigload: gvt->db_trigger_cycle updated to %d\n", gvt->db_trigger_cycle)); SET_GVTARGET_TO_HASHT_GBL(csa); INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED; trigdsc = &gvt_trigger->gv_trig_array[index - 1]; assert(0 == trigdsc->xecute_str.str.len); /* Make sure not replacing/losing a buffer */ i2mval(&trig_index, index); xecute_buff.addr = trigger_gbl_fill_xecute_buffer(gvname.var_name.addr, gvname.var_name.len, &trig_index, NULL, (int4 *)&xecute_buff.len); trigdsc->xecute_str.str = xecute_buff; *ret_trigdsc = trigdsc; RETURN_AND_POP_MVALS(TRIG_SUCCESS); } #endif /* GTM_TRIGGER */ fis-gtm-V7.0-005/sr_unix/trigger_subs_def.h0000644000032200000250000000440714342376330017534 0ustar librarygtc/**************************************************************** * * * Copyright 2011, 2014 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ /* Define trigger subscript types/order. Used to define enum trig_subs_t in trigger.h * and trigger_subs in mtables.c. * * Note : The order of lines below matters to a great extent. For example TRIGNAME needs to be before CMD * which in turn needs to be before XECUTE as that otherwise affects the output of MUPIP TRIGGER -SELECT. * There are other requirements like this in MUPIP TRIGGER -SELECT output format. * In addition, BHASH and LHASH need to be at the end of this list. The #define of NUM_SUBS relies on this. */ /* TRIGGER_SUBSDEF (trigsubstype, subsname, litmvalname, trigfilequal, partofhash ) */ TRIGGER_SUBSDEF (TRIGNAME_SUB, "TRIGNAME", literal_trigname, "-name=", TRSBS_IN_NONE ) TRIGGER_SUBSDEF (GVSUBS_SUB, "GVSUBS", literal_gvsubs, "", (TRSBS_IN_LHASH | TRSBS_IN_BHASH) ) TRIGGER_SUBSDEF (CMD_SUB, "CMD", literal_cmd, "-commands=", TRSBS_IN_NONE ) TRIGGER_SUBSDEF (OPTIONS_SUB, "OPTIONS", literal_options, "-options=", TRSBS_IN_NONE ) TRIGGER_SUBSDEF (DELIM_SUB, "DELIM", literal_delim, "-delim=", TRSBS_IN_BHASH ) TRIGGER_SUBSDEF (ZDELIM_SUB, "ZDELIM", literal_zdelim, "-zdelim=", TRSBS_IN_BHASH ) TRIGGER_SUBSDEF (PIECES_SUB, "PIECES", literal_pieces, "-pieces=", TRSBS_IN_BHASH ) TRIGGER_SUBSDEF (XECUTE_SUB, "XECUTE", literal_xecute, "-xecute=", (TRSBS_IN_LHASH | TRSBS_IN_BHASH) ) TRIGGER_SUBSDEF (CHSET_SUB, "CHSET", literal_chset, "", TRSBS_IN_NONE ) TRIGGER_SUBSDEF (BHASH_SUB, "BHASH", literal_bhash, "", TRSBS_IN_NONE ) TRIGGER_SUBSDEF (LHASH_SUB, "LHASH", literal_lhash, "", TRSBS_IN_NONE ) fis-gtm-V7.0-005/sr_unix/trigger_tpwrap_ch.c0000644000032200000250000000546014342376330017724 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2019 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 "gdsroot.h" /* for gdsfhead.h */ #include "gdsblk.h" #include "gdsbt.h" /* for gdsfhead.h */ #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "error.h" #include "jnl.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "tp_restart.h" #include "util.h" GBLREF sgm_info *first_sgm_info; GBLREF gv_namehead *reset_gv_target; GBLREF int tprestart_state; error_def(ERR_TPRETRY); #ifdef GTM_TRIGGER /* This code is modeled around "updproc_ch" in updproc.c and is used by the trigger_* modules */ CONDITION_HANDLER(trigger_tpwrap_ch) { int rc; START_CH(TRUE); if ((int)ERR_TPRETRY == SIGNAL) { assert(TPRESTART_STATE_NORMAL == tprestart_state); tprestart_state = TPRESTART_STATE_NORMAL; assert(NULL != first_sgm_info); /* This only happens at the outer-most layer so state should be normal now */ rc = tp_restart(1, !TP_RESTART_HANDLES_ERRORS); assert(0 == rc); assert(TPRESTART_STATE_NORMAL == tprestart_state); /* "reset_gv_target" might have been set to a non-default value if we are deep inside "gvcst_put" * when the restart occurs. Reset it before unwinding the gvcst_put C stack frame. Normally gv_target would * be set to what is in reset_gv_target (using the RESET_GV_TARGET macro) but that could lead to gv_target * and gv_currkey going out of sync depending on where in gvcst_put we got the restart (e.g. if we got it * in gvcst_root_search before gv_currkey was initialized but after gv_target was). Therefore we instead set * "reset_gv_target" back to its default value leaving "gv_target" untouched. This is ok to do so since as * part of the tp restart, gv_target and gv_currkey are anyways going to be reset to what they were at the * beginning of the TSTART and therefore are guaranteed to be back in sync. Not resetting "reset_gv_target" * would also cause an assert (on this being the invalid) in "gvtr_match_n_invoke" to fail in a restart case. */ DBGEHND((stderr, "trigger_tpwrap_ch: TP restart encountered\n")); reset_gv_target = INVALID_GV_TARGET; UNWIND(NULL, NULL); } DBGEHND((stderr, "trigger_tp_wrap_ch: Condition handler passing signal %d on to next handler\n", SIGNAL)); NEXTCH; } #endif fis-gtm-V7.0-005/sr_unix/trigger_trgfile.c0000644000032200000250000002506614342376335017402 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2021 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" #ifdef GTM_TRIGGER #include "gtm_stat.h" #include "gtm_string.h" #include "gtm_multi_thread.h" #include "gdsroot.h" /* for gdsfhead.h */ #include "gdsbt.h" /* for gdsfhead.h */ #include "gdsfhead.h" /* For gvcst_protos.h */ #include #include "gv_trigger.h" #include "io.h" #include "hashtab_str.h" #include "trigger_trgfile_protos.h" #include "trigger_delete_protos.h" #include "trigger.h" /* needed by "trigger_update_protos.h" for trig_stats_t prototype */ #include "trigger_update_protos.h" #include "file_input.h" #include "op.h" /* for op_tstart */ #include "op_tcommit.h" #include "gdscc.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "filestruct.h" /* needed for jnl.h */ #include "jnl.h" /* needed for tp.h */ #include "tp.h" #include "error.h" #include "tp_restart.h" #include "mupip_exit.h" #include "util.h" #include "stack_frame.h" #include "mv_stent.h" #include "tp_frame.h" #include "t_retry.h" #include "gtmimagename.h" #define TRIG_ERROR_RETURN \ { \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ if (lcl_implicit_tpwrap) \ { /* only if we were implicitly wrapped */ \ assert(dollar_tlevel); \ assert(donot_INVOKE_MUMTSTART); \ DEBUG_ONLY(donot_INVOKE_MUMTSTART = FALSE); \ /* Print $ztrigger/mupip-trigger output before rolling back TP */ \ TP_ZTRIGBUFF_PRINT; \ OP_TROLLBACK(-1); /* Unroll implicit TP */ \ REVERT; \ } \ return TRIG_FAILURE; \ } GBLREF sgm_info *first_sgm_info; GBLREF bool mupip_error_occurred; GBLREF gv_key *gv_currkey; GBLREF sgm_info *sgm_info_ptr; GBLREF sgmnt_data_ptr_t cs_data; GBLREF int tprestart_state; GBLREF io_pair io_curr_device; GBLREF gv_namehead *reset_gv_target; GBLREF uint4 dollar_tlevel; GBLREF unsigned int t_tries; GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; #ifdef DEBUG GBLREF boolean_t donot_INVOKE_MUMTSTART; #endif error_def(ERR_DBROLLEDBACK); error_def(ERR_ZFILNMBAD); STATICFNDEF boolean_t trigger_trgfile_tpwrap_helper(char *trigger_filename, uint4 trigger_filename_len, boolean_t noprompt, boolean_t lcl_implicit_tpwrap) { boolean_t trigger_error; uint4 i; io_pair io_save_device; io_pair io_trigfile_device; int len; int4 record_num; boolean_t trigger_status; enum cdb_sc cdb_status; uint4 trig_stats[NUM_STATS]; mval *trigger_rec; char *trigptr; char *values[NUM_SUBS]; unsigned short value_len[NUM_SUBS]; if (lcl_implicit_tpwrap) ESTABLISH_RET(trigger_tpwrap_ch, TRIG_FAILURE); /* Return through here is a failure */ io_save_device = io_curr_device; file_input_init(trigger_filename, trigger_filename_len, IOP_REWIND); if (mupip_error_occurred) { assert(!memcmp(&io_curr_device, &io_save_device, SIZEOF(io_curr_device))); io_curr_device = io_save_device; /* just in case in PRO */ TRIG_ERROR_RETURN; } io_trigfile_device = io_curr_device; record_num = 0; for (i = 0; NUM_STATS > i; i++) trig_stats[i] = 0; PUSH_MV_STENT(MVST_MVAL); /* protect the trigger content from stp_gcol */ trigger_rec = &mv_chain->mv_st_cont.mvs_mval; trigger_rec->mvtype = MV_STR; trigger_error = FALSE; while ((0 == io_curr_device.in->dollar.zeof) && (0 <= (len = file_input_get(&trigptr, 0)))) { io_curr_device = io_save_device; record_num++; if ((0 != len) && (COMMENT_LITERAL != trigptr[0])) util_out_print_gtmio("File !AD, Line !UL: ", NOFLUSH_OUT, trigger_filename_len, trigger_filename, record_num); trigger_rec->str.len = len; trigger_rec->str.addr = trigptr; trigger_status = trigger_update_rec(trigger_rec, noprompt, trig_stats, &io_trigfile_device, &record_num); trigger_error |= (TRIG_FAILURE == trigger_status); assert(!trigger_error || trig_stats[STATS_ERROR_TRIGFILE]); assert(trigger_error || !trig_stats[STATS_ERROR_TRIGFILE]); io_curr_device = io_trigfile_device; } POP_MV_STENT(); if ((-1 == len) && (!io_curr_device.in->dollar.zeof)) { io_curr_device = io_save_device; util_out_print_gtmio("File !AD, Line !UL: Line too long", FLUSH, trigger_filename_len, trigger_filename, ++record_num); io_curr_device = io_trigfile_device; } file_input_close(); io_curr_device = io_save_device; if (trigger_error) { util_out_print_gtmio("=========================================", FLUSH); util_out_print_gtmio("!UL trigger file entries have errors", FLUSH, trig_stats[STATS_ERROR_TRIGFILE]); util_out_print_gtmio("!UL trigger file entries have no errors", FLUSH, trig_stats[STATS_NOERROR_TRIGFILE] + trig_stats[STATS_UNCHANGED_TRIGFILE]); util_out_print_gtmio("=========================================", FLUSH); TRIG_ERROR_RETURN; /* rollback the trigger transaction due to errors and return */ } if (trig_stats[STATS_ADDED] + trig_stats[STATS_DELETED] + trig_stats[STATS_UNCHANGED_TRIGFILE] + trig_stats[STATS_MODIFIED]) { util_out_print_gtmio("=========================================", FLUSH); util_out_print_gtmio("!UL triggers added", FLUSH, trig_stats[STATS_ADDED]); util_out_print_gtmio("!UL triggers deleted", FLUSH, trig_stats[STATS_DELETED]); util_out_print_gtmio("!UL triggers modified", FLUSH, trig_stats[STATS_MODIFIED]); util_out_print_gtmio("!UL trigger file entries did update database trigger content", FLUSH, trig_stats[STATS_NOERROR_TRIGFILE]); util_out_print_gtmio("!UL trigger file entries did not update database trigger content", FLUSH, trig_stats[STATS_UNCHANGED_TRIGFILE]); util_out_print_gtmio("=========================================", FLUSH); } if (lcl_implicit_tpwrap) { GVTR_OP_TCOMMIT(cdb_status); /* commit the trigger transaction */ if (cdb_sc_normal != cdb_status) t_retry(cdb_status); /* won't return */ REVERT; } return TRIG_SUCCESS; } boolean_t trigger_trgfile_tpwrap(char *trigger_filename, uint4 trigger_filename_len, boolean_t noprompt) { boolean_t trigger_status = TRIG_FAILURE; mval ts_mv; int loopcnt, utilbuff_len; struct stat statbuf; DEBUG_ONLY(unsigned int lcl_t_tries;) enum cdb_sc failure; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ts_mv.mvtype = MV_STR; ts_mv.str.len = 0; ts_mv.str.addr = NULL; /* Do sanity checks on the filename and the file's accessibility. */ assert('\0' == trigger_filename[trigger_filename_len]); /* should have been made sure by caller */ if (-1 == Stat(trigger_filename, &statbuf)) { DEBUG_ONLY(TREF(gtmio_skip_tlevel_assert) = TRUE;) util_out_print_gtmio("Invalid file name: !AD: !AZ", FLUSH, trigger_filename_len, trigger_filename, STRERROR(errno)); DEBUG_ONLY(TREF(gtmio_skip_tlevel_assert) = FALSE;) TP_ZTRIGBUFF_PRINT; /* Print $ztrigger/mupip-trigger output before returning */ return TRUE; /* Failure */ } else if (!S_ISREG(statbuf.st_mode)) { DEBUG_ONLY(TREF(gtmio_skip_tlevel_assert) = TRUE;) util_out_print_gtmio("Invalid file name: !AD: Not a proper input file", FLUSH, trigger_filename_len, trigger_filename); DEBUG_ONLY(TREF(gtmio_skip_tlevel_assert) = FALSE;) TP_ZTRIGBUFF_PRINT; /* Print $ztrigger/mupip-trigger output before returning */ return TRUE; /* Failure */ } if (0 == dollar_tlevel) { /* If not already wrapped in TP, wrap it now implicitly */ assert(!donot_INVOKE_MUMTSTART); DEBUG_ONLY(donot_INVOKE_MUMTSTART = TRUE); /* Note down dollar_tlevel before op_tstart. This is needed to determine if we need to break from the for-loop * below after a successful op_tcommit of the $ZTRIGGER operation. We cannot check that dollar_tlevel is zero * since the op_tstart done below can be a nested sub-transaction */ op_tstart(IMPLICIT_TSTART, TRUE, &ts_mv, 0); /* 0 ==> save no locals but RESTART OK */ /* Note down length of unprocessed util_out buffer */ ASSERT_SAFE_TO_UPDATE_THREAD_GBLS; assert(NULL != TREF(util_outptr)); utilbuff_len = INTCAST(TREF(util_outptr) - TREF(util_outbuff_ptr)); assert(OUT_BUFF_SIZE >= utilbuff_len); /* The following for loop structure is similar to that in module trigger_update.c (function "trigger_update") * and module gv_trigger.c (function gvtr_db_tpwrap) so any changes here might need to be reflected there as well. */ for (loopcnt = 0; ; loopcnt++) { assert(donot_INVOKE_MUMTSTART); /* Make sure still set */ DEBUG_ONLY(lcl_t_tries = t_tries); TREF(ztrigbuffLen) = utilbuff_len; /* reset ztrig buffer at start of each try/retry */ TREF(util_outptr) = TREF(util_outbuff_ptr); /* Signal any unflushed text from previous try as gone */ trigger_status = trigger_trgfile_tpwrap_helper(trigger_filename, trigger_filename_len, noprompt, TRUE); if (0 == dollar_tlevel) break; assert(0 < t_tries); assert((CDB_STAGNATE == t_tries) || (lcl_t_tries == t_tries - 1)); failure = LAST_RESTART_CODE; assert(((cdb_sc_onln_rlbk1 != failure) && (cdb_sc_onln_rlbk2 != failure)) || !gv_target || !gv_target->root); assert((cdb_sc_onln_rlbk2 != failure) || !IS_GTM_IMAGE || TREF(dollar_zonlnrlbk)); if (cdb_sc_onln_rlbk2 == failure) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DBROLLEDBACK); /* else if (cdb_sc_onln_rlbk1 == status) we don't need to do anything other than trying again. Since this * is ^#t global, we don't need to GVCST_ROOT_SEARCH before continuing with the next restart because the * trigger load logic already takes care of doing INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED before doing the * actual trigger load */ /* We expect the above function to return with either op_tcommit or a tp_restart invoked. * In the case of op_tcommit, we expect dollar_tlevel to be 0 and if so we break out of the loop. * In the tp_restart case, we expect a maximum of 4 tries/retries and much lesser usually. * Additionally we also want to avoid an infinite loop so limit the loop to what is considered * a huge iteration count and assertpro if that is reached as it suggests an out-of-design situation. */ assertpro(TPWRAP_HELPER_MAX_ATTEMPTS >= loopcnt); } } else { trigger_status = trigger_trgfile_tpwrap_helper(trigger_filename, trigger_filename_len, noprompt, FALSE); assert(0 < dollar_tlevel); } return (TRIG_FAILURE == trigger_status); } #endif /* GTM_TRIGGER */ fis-gtm-V7.0-005/sr_unix/trigger_trgfile_protos.h0000644000032200000250000000151614342376330021002 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef TRIGGER_TRGFILE_PROTOS_INCLUDED #define TRIGGER_TRGFILE_PROTOS_INCLUDED STATICFNDCL boolean_t trigger_trgfile_tpwrap_helper(char *trigger_filename, uint4 trigger_filename_len, boolean_t noprompt, boolean_t lcl_implicit_tpwrap); boolean_t trigger_trgfile_tpwrap(char *trigger_filename, uint4 trigger_filename_len, boolean_t noprompt); #endif /* TRIGGER_TRGFILE_PROTOS_INCLUDED */ fis-gtm-V7.0-005/sr_unix/trigger_update.c0000644000032200000250000025231214342376335017224 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2022 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" #ifdef GTM_TRIGGER #include "gdsroot.h" /* for gdsfhead.h */ #include "gdsbt.h" /* for gdsfhead.h */ #include "gdsfhead.h" /* For gvcst_protos.h */ #include "dpgbldir.h" #include "gvcst_protos.h" #include #include "io.h" #include "iormdef.h" #include "hashtab_str.h" #include "wbox_test_init.h" #include "gv_trigger.h" #include "trigger_delete_protos.h" #include "trigger.h" #include "trigger_incr_cycle.h" #include "trigger_parse_protos.h" #include "trigger_update_protos.h" #include "trigger_compare_protos.h" #include "trigger_user_name.h" #include "gtm_trigger_trc.h" #include "gtm_string.h" #include "mv_stent.h" /* for COPY_SUBS_TO_GVCURRKEY macro */ #include "gvsub2str.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include "format_targ_key.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include "targ_alloc.h" /* for SET_GVTARGET_TO_HASHT_GBL */ #include "gdsblk.h" #include "gdscc.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "filestruct.h" /* needed for jnl.h */ #include "jnl.h" /* needed for tp.h */ #include "tp.h" #include "min_max.h" /* Needed for MIN */ #include "mvalconv.h" /* Needed for MV_FORCE_* */ #include "op.h" #include "util.h" #include "op_tcommit.h" #include "tp_restart.h" #include "error.h" #include "file_input.h" #include "stack_frame.h" #include "tp_frame.h" #include "t_retry.h" #include "gtmimagename.h" #include "hashtab_mname.h" #include "zshow.h" /* for "format2disp" prototype */ #include "compiler.h" #include "t_begin.h" #include "repl_msg.h" #include "gtmsource.h" #include "change_reg.h" /* for "change_reg" prototype */ #include "gvnh_spanreg.h" /* for "gvnh_spanreg_subs_gvt_init" prototype */ #include "mu_interactive.h" /* for prompt looping */ #include "is_file_identical.h" #include "anticipatory_freeze.h" #include "gtm_repl_multi_inst.h" /* for DISALLOW_MULTIINST_UPDATE_IN_TP */ #include "stringpool.h" GBLREF sgmnt_data_ptr_t cs_data; GBLREF uint4 dollar_tlevel; GBLREF boolean_t dollar_ztrigger_invoked; GBLREF sgm_info *first_sgm_info; GBLREF gd_region *gv_cur_region; GBLREF gv_key *gv_currkey; GBLREF gd_addr *gd_header; GBLREF io_pair io_curr_device; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnlpool_addrs_ptr_t jnlpool_head; GBLREF trans_num local_tn; GBLREF gv_namehead *reset_gv_target; GBLREF sgm_info *sgm_info_ptr; GBLREF int tprestart_state; GBLREF volatile boolean_t timer_in_handler; GBLREF unsigned int t_tries; GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; #ifdef DEBUG GBLREF boolean_t donot_INVOKE_MUMTSTART; #endif error_def(ERR_DBROLLEDBACK); error_def(ERR_NEEDTRIGUPGRD); error_def(ERR_REMOTEDBNOTRIG); error_def(ERR_TEXT); error_def(ERR_TPRETRY); error_def(ERR_TPRETRY); error_def(ERR_TRIGDEFBAD); error_def(ERR_TRIGLOADFAIL); error_def(ERR_TRIGMODREGNOTRW); error_def(ERR_TRIGNAMBAD); LITREF mval gvtr_cmd_mval[GVTR_CMDTYPES]; LITREF int4 gvtr_cmd_mask[GVTR_CMDTYPES]; LITREF mval literal_one; LITREF char *trigger_subs[]; static boolean_t mustprompt = TRUE; static boolean_t promptanswer = TRUE; #define MAX_COMMANDS_LEN 32 /* Need room for S,K,ZK,ZTK + room for expansion */ #define MAX_OPTIONS_LEN 32 /* Need room for NOI,NOC + room for expansion */ #define MAX_TRIGNAME_SEQ_NUM 999999 #define LITERAL_M "M" #define OPTIONS_I 1 #define OPTIONS_NOI 2 #define OPTIONS_C 4 #define OPTIONS_NOC 8 #define NO_NAME_CHANGE 0 #define NO_CMD_CHANGE 0 #define NO_OPTIONS_CHANGE 0 #define ADD_UPDATE_NAME 0x01 #define ADD_UPDATE_CMDS 0x02 #define ADD_UPDATE_OPTIONS 0x04 #define SUB_UPDATE_NAME 0x10 #define SUB_UPDATE_CMDS 0x20 #define DELETE_REC 0x80 /* Defines macros for types of triggers; one is SET type triggers, one is Non-SET type triggers */ #define OPR_KILL 0 #define OPR_SET 1 #define NUM_OPRS 2 #define OPR_SETKILL 2 #define SEQ_SUCCESS 0 #define MAX_HASH_LEN MAX_HASH_INDEX_LEN + 1 + MAX_DIGITS_IN_INT #define BUILD_COMMAND_BITMAP(BITMAP, COMMANDS) \ { \ char lcl_cmds[MAX_COMMANDS_LEN + 1]; \ char *lcl_ptr, *strtok_ptr; \ \ assert(MAX_COMMANDS_LEN >= STRLEN(COMMANDS)); \ memcpy(lcl_cmds, COMMANDS, STRLEN(COMMANDS) + 1); \ BITMAP = 0; \ lcl_ptr = STRTOK_R(lcl_cmds, ",", &strtok_ptr); \ while (NULL != lcl_ptr) \ { \ switch (*lcl_ptr) \ { \ case 'S': \ BITMAP |= gvtr_cmd_mask[GVTR_CMDTYPE_SET]; \ break; \ case 'K': \ BITMAP |= gvtr_cmd_mask[GVTR_CMDTYPE_KILL]; \ break; \ case 'Z': \ switch (*(lcl_ptr + 1)) \ { \ case 'K': \ BITMAP |= gvtr_cmd_mask[GVTR_CMDTYPE_ZKILL]; \ break; \ case 'T': \ switch(*(lcl_ptr + 2)) \ { \ case 'K': \ BITMAP |= gvtr_cmd_mask[GVTR_CMDTYPE_ZTKILL]; \ break; \ case 'R': \ BITMAP |= gvtr_cmd_mask[GVTR_CMDTYPE_ZTRIGGER]; \ break; \ default: \ /* Parsing should have found invalid command */ \ assertpro(FALSE && lcl_ptr[2]); \ break; \ } \ break; \ default: \ /* Parsing should have found invalid command */ \ assertpro(FALSE && lcl_ptr[1]); \ break; \ } \ break; \ default: \ /* Parsing should have found invalid command */ \ assertpro(FALSE && lcl_ptr[0]); \ break; \ } \ lcl_ptr = STRTOK_R(NULL, ",", &strtok_ptr); \ } \ } #define COMMAND_BITMAP_TO_STR(COMMANDS, BITMAP, LEN) \ { \ int count, cmdtype, lcl_len; \ char *lcl_ptr; \ \ count = 0; \ lcl_ptr = COMMANDS; \ lcl_len = LEN; \ for (cmdtype = 0; cmdtype < GVTR_CMDTYPES; cmdtype++) \ { \ if (gvtr_cmd_mask[cmdtype] & (BITMAP)) \ { \ ADD_COMMA_IF_NEEDED(count, lcl_ptr, lcl_len); \ ADD_STRING(count, lcl_ptr, gvtr_cmd_mval[cmdtype].str.len, gvtr_cmd_mval[cmdtype].str.addr, lcl_len); \ } \ } \ *lcl_ptr = '\0'; \ LEN = STRLEN(COMMANDS); \ } #define BUILD_OPTION_BITMAP(BITMAP, OPTIONS) \ { \ char lcl_options[MAX_OPTIONS_LEN + 1]; \ char *lcl_ptr, *strtok_ptr; \ \ assert(MAX_OPTIONS_LEN >= STRLEN(OPTIONS)); \ memcpy(lcl_options, OPTIONS, STRLEN(OPTIONS) + 1); \ BITMAP = 0; \ lcl_ptr = STRTOK_R(lcl_options, ",", &strtok_ptr); \ while (NULL != lcl_ptr) \ { \ switch (*lcl_ptr) \ { \ case 'C': \ BITMAP |= OPTIONS_C; \ break; \ case 'I': \ BITMAP |= OPTIONS_I; \ break; \ case 'N': \ assert('O' == *(lcl_ptr + 1)); \ switch (*(lcl_ptr + 2)) \ { \ case 'C': \ BITMAP |= OPTIONS_NOC; \ break; \ case 'I': \ BITMAP |= OPTIONS_NOI; \ break; \ default: \ /* Parsing should have found invalid command */ \ assertpro(FALSE && lcl_ptr[2]); \ break; \ } \ break; \ default: \ /* Parsing should have found invalid command */ \ assertpro(FALSE && lcl_ptr[0]); \ break; \ } \ lcl_ptr = STRTOK_R(NULL, ",", &strtok_ptr); \ } \ } #define OPTION_BITMAP_TO_STR(OPTIONS, BITMAP, LEN) \ { \ int count, lcl_len; \ char *lcl_ptr; \ \ count = 0; \ lcl_len = LEN; \ lcl_ptr = OPTIONS; \ if (OPTIONS_I & BITMAP) \ { \ ADD_COMMA_IF_NEEDED(count, lcl_ptr, lcl_len); \ ADD_STRING(count, lcl_ptr, STRLEN(HASHT_OPT_ISOLATION), HASHT_OPT_ISOLATION, lcl_len); \ } \ if (OPTIONS_NOI & BITMAP) \ { \ ADD_COMMA_IF_NEEDED(count, lcl_ptr, lcl_len); \ ADD_STRING(count, lcl_ptr, STRLEN(HASHT_OPT_NOISOLATION), HASHT_OPT_NOISOLATION, lcl_len); \ } \ if (OPTIONS_C & BITMAP) \ { \ ADD_COMMA_IF_NEEDED(count, lcl_ptr, lcl_len); \ ADD_STRING(count, lcl_ptr, STRLEN(HASHT_OPT_CONSISTENCY), HASHT_OPT_CONSISTENCY, lcl_len); \ } \ if (OPTIONS_NOC & BITMAP) \ { \ ADD_COMMA_IF_NEEDED(count, lcl_ptr, lcl_len); \ ADD_STRING(count, lcl_ptr, STRLEN(HASHT_OPT_NOCONSISTENCY), HASHT_OPT_NOCONSISTENCY, lcl_len); \ } \ *lcl_ptr = '\0'; \ LEN = STRLEN(OPTIONS); \ } #define TOO_LONG_REC_KEY_ERROR_MSG \ { \ UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); \ if (KEY_TOO_LONG == result) \ util_out_print_gtmio("Error : ^!AD trigger - key larger than max key size", \ FLUSH, disp_trigvn_len, disp_trigvn); \ else \ util_out_print_gtmio("Error : ^!AD trigger - value larger than max record size", \ FLUSH, disp_trigvn_len, disp_trigvn); \ } #define IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(RESULT) \ { \ if (PUT_SUCCESS != RESULT) \ { \ TOO_LONG_REC_KEY_ERROR_MSG; \ RETURN_AND_POP_MVALS(STATS_ERROR_TRIGFILE); \ } \ } #define TRIGGER_SAME_NAME_EXISTS_ERROR(OPNAME, DISP_TRIGVN_LEN, DISP_TRIGVN) \ { \ assert(dollar_tlevel); \ if (CDB_STAGNATE > t_tries) \ { /* Directly jump to final retry since we cannot issue this error accurately \ * unless we are in the final retry. Dont waste time in intermediate tries. \ * But before then record the fact that the intermediate tries had normal status. \ */ \ for ( ; t_tries < (CDB_STAGNATE - 1); t_tries++) \ t_fail_hist[t_tries] = cdb_sc_normal; \ t_retry(cdb_sc_triggermod); \ } \ UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); \ util_out_print_gtmio("Error : !AZ trigger on ^!AD not added as another trigger named !AD already exists", \ FLUSH, OPNAME, DISP_TRIGVN_LEN, DISP_TRIGVN, \ value_len[TRIGNAME_SUB], values[TRIGNAME_SUB]); \ RETURN_AND_POP_MVALS(STATS_ERROR_TRIGFILE); \ } /* This error macro is used for all definition errors where the target is ^#t(GVN,<#LABEL|#COUNT|#CYCLE>) */ #define HASHT_DEFINITION_RETRY_OR_ERROR(SUBSCRIPT, MOREINFO, CSA) \ { \ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) \ t_retry(cdb_sc_triggermod); \ else \ { \ HASHT_DEFINITION_ERROR(SUBSCRIPT, MOREINFO, CSA); \ } \ } #define HASHT_DEFINITION_ERROR(SUBSCRIPT, MOREINFO, CSA) \ { \ assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); \ rts_error_csa(CSA_ARG(CSA) VARLSTCNT(12) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, \ trigvn_len, trigvn, LEN_AND_LIT(SUBSCRIPT), \ ERR_TEXT, 2, RTS_ERROR_TEXT(MOREINFO)); \ } STATICFNDEF boolean_t validate_label(char *trigvn, int trigvn_len) { mval trigger_label; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; BUILD_HASHT_SUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHLABEL, STRLEN(LITERAL_HASHLABEL)); if (!gvcst_get(&trigger_label)) /* There has to be a #LABEL */ HASHT_DEFINITION_RETRY_OR_ERROR("\"#LABEL\"","#LABEL was not found", REG2CSA(gv_cur_region)) return ((trigger_label.str.len == STRLEN(HASHT_GBL_CURLABEL)) && (0 == memcmp(trigger_label.str.addr, HASHT_GBL_CURLABEL, trigger_label.str.len))); } STATICFNDEF int4 update_commands(char *trigvn, int trigvn_len, int trigger_index, char *new_trig_cmds, char *orig_db_cmds) { mval mv_trig_indx; uint4 orig_cmd_bm, new_cmd_bm; int4 result; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (!validate_label(trigvn, trigvn_len)) return INVALID_LABEL; BUILD_COMMAND_BITMAP(orig_cmd_bm, orig_db_cmds); BUILD_COMMAND_BITMAP(new_cmd_bm, new_trig_cmds); i2mval(&mv_trig_indx, trigger_index); SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[CMD_SUB], STRLEN(trigger_subs[CMD_SUB]), new_trig_cmds, STRLEN(new_trig_cmds), result); if (PUT_SUCCESS != result) return result; if ((gvtr_cmd_mask[GVTR_CMDTYPE_SET] & orig_cmd_bm) && !(gvtr_cmd_mask[GVTR_CMDTYPE_SET] & new_cmd_bm)) { /* SET was removed from the commands, so delete the SET specific attributes */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, mv_trig_indx, trigger_subs[DELIM_SUB], STRLEN(trigger_subs[DELIM_SUB])); gvcst_kill(TRUE); BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, mv_trig_indx, trigger_subs[ZDELIM_SUB], STRLEN(trigger_subs[ZDELIM_SUB])); gvcst_kill(TRUE); BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, mv_trig_indx, trigger_subs[PIECES_SUB], STRLEN(trigger_subs[PIECES_SUB])); gvcst_kill(TRUE); } return SUB_UPDATE_CMDS; } STATICFNDEF int4 update_trigger_name(char *trigvn, int trigvn_len, int trigger_index, char *db_trig_name, char *tf_trig_name, uint4 tf_trig_name_len) { mval mv_trig_indx; int4 result; uint4 retval; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; retval = NO_NAME_CHANGE; if (tf_trig_name_len && (tf_trig_name_len != STRLEN(db_trig_name) - 1) || memcmp(tf_trig_name, db_trig_name, tf_trig_name_len)) { if (!validate_label(trigvn, trigvn_len)) return INVALID_LABEL; i2mval(&mv_trig_indx, trigger_index); tf_trig_name[tf_trig_name_len++] = TRIGNAME_SEQ_DELIM; SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[TRIGNAME_SUB], STRLEN(trigger_subs[TRIGNAME_SUB]), tf_trig_name, tf_trig_name_len, result); if (PUT_SUCCESS != result) return result; cleanup_trigger_name(trigvn, trigvn_len, db_trig_name, STRLEN(db_trig_name)); retval = ADD_UPDATE_NAME; } return retval; } /* * Input: trigger_name and trigger_name_length * [optional] srch_reg (when non-NULL this is the only region to search) * * Output: returns TRUE if trigger name is found, false if not. * srch_reg set to the region the name was found in. * val is the "\0" string to which the name points * * This function is similar to check_unique_trigger_name_full(), but is only called from * trigger_source_read_andor_verify() */ boolean_t trigger_name_search(char *trigger_name, uint4 trigger_name_len, mval *val, gd_region **srch_reg) { boolean_t name_found; char *ptr, *ptr2; gd_region *reg, *reg_top; gd_region *save_gv_cur_region; gv_key_buf save_currkey; gv_namehead *save_gv_target; gvnh_reg_t *gvnh_reg; int len; mname_entry gvname; sgm_info *save_sgm_info_ptr; jnlpool_addrs_ptr_t save_jnlpool; /* Example trigger name could be x#1#A:BREG to indicate trigger on global ^x with an autogenerated name x#1 * that exists in multiple regions and hence had a runtime disambiguator of x#1#A. The :BREG is a region-level * disambiguator to indicate we want to focus on BREG region to search for triggers with the name x#1. * We dont expect the input trigger name to contain the runtime and region-level disambiguator but in case both * are present we treat it as if the runtime disambiguator was absent. */ DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Remove trailing # (if any) in trigger name before searching in ^#t as it is not stored in the ^#t("#TNAME",...) node */ assert('#' == trigger_name[trigger_name_len - 1]); if ('#' == trigger_name[trigger_name_len - 1]) trigger_name_len--; /* We only check user supplied names for uniqueness. With autogenerated names it is possible for * same name to exist in multiple regions (in case two globals with global name > 21 chars map to * different regions and have one trigger per global name installed with auto-generated names. * But even in that case, at most one auto-generated name per region is possible. So we have a limit * on the max # of duplicated auto-generated names. */ assert(0 < trigger_name_len); SAVE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); name_found = FALSE; reg = *srch_reg; if (NULL != reg) reg_top = reg + 1; /* make sure we dont go in the for loop more than once */ else { reg = gd_header->regions; reg_top = reg + gd_header->n_regions; assert(reg < reg_top); } for ( ; reg < reg_top; reg++) { if (IS_STATSDB_REGNAME(reg)) continue; GVTR_SWITCH_REG_AND_HASHT_BIND_NAME(reg); if (NULL == cs_addrs) /* not BG or MM access method */ continue; /* gv_target now points to ^#t in region "reg" */ if (0 == gv_target->root) continue; /* $get(^#t("#TNAME",trigger_name)) */ BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trigger_name, trigger_name_len); if (!gvcst_get(val)) continue; ptr = val->str.addr; ptr2 = memchr(ptr, '\0', val->str.len); /* Do it this way since "val" has multiple fields null separated */ if (NULL == ptr2) { /* We expect $c(0) in the middle of ptr. If we dont find it, this is a restartable situation */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TRIGNAMBAD, 4, LEN_AND_LIT("\"#TNAME\""), trigger_name_len, trigger_name); } len = ptr2 - ptr; assert(('\0' == *ptr2) && (val->str.len > len)); gvname.var_name.addr = val->str.addr; gvname.var_name.len = len; /* Check if global name is indeed mapped to this region by the gld. * If not treat this case as if the trigger is invisible to us i.e. move on to next region. */ COMPUTE_HASH_MNAME(&gvname); GV_BIND_NAME_ONLY(gd_header, &gvname, gvnh_reg); /* does tp_set_sgm() */ if (((NULL == gvnh_reg->gvspan) && (gv_cur_region != reg)) || ((NULL != gvnh_reg->gvspan) && !gvnh_spanreg_ismapped(gvnh_reg, gd_header, reg))) continue; *srch_reg = reg; name_found = TRUE; break; } RESTORE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); return name_found; } /* Returns TRUE if name is NOT found. FALSE if name is found. If name is found, "val" holds the value of the found node */ boolean_t check_unique_trigger_name_full(char **values, uint4 *value_len, mval *val, boolean_t *new_match, char *trigvn, int trigvn_len, stringkey *kill_trigger_hash, stringkey *set_trigger_hash) { boolean_t overall_name_found, this_name_found; gd_region *reg, *reg_top; gv_key_buf save_currkey; gd_region *save_gv_cur_region; gv_namehead *save_gv_target; sgm_info *save_sgm_info_ptr; jnlpool_addrs_ptr_t save_jnlpool; int set_index, kill_index; boolean_t db_matched_set, db_matched_kill, full_match, trigger_exists; mval setname, killname; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DEBUG_ONLY(if (WBTEST_HELPOUT_TRIGNAMEUNIQ == gtm_white_box_test_case_number) return TRUE;) /* We only check user supplied names for uniqueness. With autogenerated names it is possible for * same name to exist in multiple regions (in case two globals with global name > 21 chars map to * different regions and have one trigger per global name installed with auto-generated names. * But even in that case, at most one auto-generated name per region is possible. So we have a limit * on the max # of duplicated auto-generated names. */ *new_match = TRUE; if (0 == value_len[TRIGNAME_SUB]) return TRUE; SAVE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); overall_name_found = FALSE; for (reg = gd_header->regions, reg_top = reg + gd_header->n_regions; reg < reg_top; reg++) { if (IS_STATSDB_REGNAME(reg)) continue; GVTR_SWITCH_REG_AND_HASHT_BIND_NAME(reg); if (NULL == cs_addrs) /* not BG or MM access method */ continue; /* gv_target now points to ^#t in region "reg" */ if (0 == gv_target->root) continue; /* $get(^#t("#TNAME",trigger_name) */ BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), values[TRIGNAME_SUB], value_len[TRIGNAME_SUB]); this_name_found = gvcst_get(val); if (this_name_found) { overall_name_found = TRUE; trigger_exists = trigger_already_exists(trigvn, trigvn_len, values, value_len, set_trigger_hash, kill_trigger_hash, &set_index, &kill_index, &db_matched_set, &db_matched_kill, &full_match, &setname, &killname); if (!full_match) { *new_match = FALSE; break; } } } RESTORE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); return !overall_name_found; } STATICFNDEF int4 add_trigger_hash_entry(char *trigvn, int trigvn_len, char *cmd_value, int trigindx, boolean_t add_kill_hash, stringkey *kill_hash, stringkey *set_hash) { int hash_indx; char indx_str[MAX_DIGITS_IN_INT]; uint4 len; mval mv_hash; mval mv_indx, *mv_indx_ptr; char name_and_index[MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT]; int num_len; char *ptr; int4 result; boolean_t set_cmp; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!gv_cur_region->read_only); /* caller should have already checked this */ assert(cs_addrs->hasht_tree == gv_target); /* should have been set up by caller */ assert(gv_target->root); /* should have been ensured by caller */ set_cmp = (NULL != strchr(cmd_value, 'S')); mv_indx_ptr = &mv_indx; num_len = 0; I2A(indx_str, num_len, trigindx); assert(MAX_MIDENT_LEN >= trigvn_len); memcpy(name_and_index, trigvn, trigvn_len); ptr = name_and_index + trigvn_len; *ptr++ = '\0'; memcpy(ptr, indx_str, num_len); len = trigvn_len + 1 + num_len; if (set_cmp) { if (set_hash->hash_code != kill_hash->hash_code) { MV_FORCE_UMVAL(&mv_hash, set_hash->hash_code); if (gv_target->root) { BUILD_HASHT_SUB_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, "", 0); op_zprevious(&mv_indx); hash_indx = (0 == mv_indx.str.len) ? 1 : (mval2i(mv_indx_ptr) + 1); } else hash_indx = 1; i2mval(&mv_indx, hash_indx); SET_TRIGGER_GLOBAL_SUB_SUB_MSUB_MSUB_STR(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_indx, name_and_index, len, result); if (PUT_SUCCESS != result) return result; } /* else: the next block of code for kill hash processing will add this hashcode in ^#t("#TRHASH",...) */ } else set_hash->hash_code = 0; if (add_kill_hash) { MV_FORCE_UMVAL(&mv_hash, kill_hash->hash_code); if (gv_target->root) { BUILD_HASHT_SUB_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, "", 0); op_zprevious(&mv_indx); hash_indx = (0 == mv_indx.str.len) ? 1 : (mval2i(mv_indx_ptr) + 1); } else hash_indx = 1; i2mval(&mv_indx, hash_indx); SET_TRIGGER_GLOBAL_SUB_SUB_MSUB_MSUB_STR(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_indx, name_and_index, len, result); if (PUT_SUCCESS != result) return result; } else kill_hash->hash_code = 0; return PUT_SUCCESS; } STATICFNDEF boolean_t trigger_already_exists(char *trigvn, int trigvn_len, char **values, uint4 *value_len, /* input parm */ stringkey *set_trigger_hash, stringkey *kill_trigger_hash, /* input parm */ int *set_index, int *kill_index, boolean_t *set_cmp_result, /* output parm */ boolean_t *kill_cmp_result, boolean_t *full_match, /* output parm */ mval *setname, mval *killname) /* output parm */ { sgmnt_addrs *csa; boolean_t db_has_K; boolean_t db_has_S; int hash_indx; boolean_t kill_cmp, kill_found; int kill_indx; boolean_t set_cmp, set_found, set_name_match, kill_name_match; int set_indx; mval trigindx; unsigned char util_buff[MAX_TRIG_UTIL_LEN]; /* needed for HASHT_GVN_DEFINITION_RETRY_OR_ERROR macro */ int4 util_len; mval val; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(cs_addrs->hasht_tree == gv_target); /* should have been set up by caller */ assert(gv_target->root); /* should have been ensured by caller */ /* Test with BHASH or LHASH. * ^#t("GBL",1,"CMD") could contain one or more of "S,K,ZK,ZTK,ZTR". * Out of the 5 commands above, a S type trigger uses the "BHASH" hash value. * Everything else (K, ZK, ZTK, ZTR) uses the "LHASH" value. * An easy check of one of these 4 commands is a chek for the letter K or R. */ set_cmp = (NULL != strchr(values[CMD_SUB], 'S')); kill_cmp = ((NULL != strchr(values[CMD_SUB], 'K')) || (NULL != strchr(values[CMD_SUB], 'R'))); set_found = kill_found = set_name_match = kill_name_match = FALSE; csa = cs_addrs; if (set_cmp) { /* test for SET hash match if SET command specified */ set_found = search_triggers(trigvn, trigvn_len, values, value_len, set_trigger_hash, &hash_indx, &set_indx, TRUE); if (set_found) { i2mval(&trigindx, set_indx); BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigindx, trigger_subs[TRIGNAME_SUB], STRLEN(trigger_subs[TRIGNAME_SUB])); if (!gvcst_get(setname)) /* There has to be a name value */ HASHT_GVN_DEFINITION_RETRY_OR_ERROR(set_indx, ",\"TRIGNAME\"", csa); setname->str.len--; /* remove the # at the tail of the trigger name */ set_name_match = ((value_len[TRIGNAME_SUB] == setname->str.len) && !memcmp(setname->str.addr, values[TRIGNAME_SUB], value_len[TRIGNAME_SUB])); } } else set_indx = 0; *set_cmp_result = set_found; kill_indx = -1; if (kill_cmp || !set_found) { /* if SET is not found OR KILL is specified in commands, test for KILL hash match */ kill_found = search_triggers(trigvn, trigvn_len, values, value_len, kill_trigger_hash, &hash_indx, &kill_indx, FALSE); if (kill_found) { if (!set_found || (kill_indx != set_indx)) { i2mval(&trigindx, kill_indx); BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigindx, trigger_subs[CMD_SUB], STRLEN(trigger_subs[CMD_SUB])); if (!gvcst_get(&val)) /* There has to be a command string */ HASHT_GVN_DEFINITION_RETRY_OR_ERROR(kill_indx, ",\"CMD\"", csa); /* val.str.addr would contain something like the following * ^#t("GBL",1,"CMD")="S,K,ZK,ZTK,ZTR". * Out of the 5 commands above, a S type trigger uses the "BHASH" hash value. * Everything else (K, ZK, ZTK, ZTR) uses the "LHASH" value. */ db_has_S = (NULL != memchr(val.str.addr, 'S', val.str.len)); db_has_K = ((NULL != memchr(val.str.addr, 'K', val.str.len)) || (NULL != memchr(val.str.addr, 'R', val.str.len))); if (!kill_cmp) kill_found = (db_has_K && !db_has_S); /* $get(^#t(trigvn,trigindx,"TRIGNAME") */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigindx, trigger_subs[TRIGNAME_SUB], STRLEN(trigger_subs[TRIGNAME_SUB])); if (!gvcst_get(killname)) /* There has to be a name string */ HASHT_GVN_DEFINITION_RETRY_OR_ERROR(kill_indx, ",\"TRIGNAME\"", csa); killname->str.len--; /* remove the # at the tail of the trigger name */ kill_name_match = ((value_len[TRIGNAME_SUB] == killname->str.len) && !memcmp(killname->str.addr, values[TRIGNAME_SUB], value_len[TRIGNAME_SUB])); if (set_cmp && !set_found && !db_has_S) { *setname = *killname; set_indx = kill_indx; } } else *killname = *setname; } else { kill_indx = -1; if (!set_found) set_indx = 0; } if (set_cmp && (kill_indx == set_indx)) kill_indx = -1; } *kill_index = kill_indx; *kill_cmp_result = kill_found ? TRUE : set_found; *set_index = set_indx; /* If there is both a set and a kill and the set components don't match, there is no name match no matter if the kill * components match or not. If there is no set, then the name match is only based on the kill components. */ *full_match = (set_cmp ? set_name_match : kill_name_match); return (set_found || kill_found); } STATICFNDEF int4 add_trigger_cmd_attributes(char *trigvn, int trigvn_len, int trigger_index, char *trig_cmds, char **values, uint4 *value_len, boolean_t db_matched_set, boolean_t db_matched_kill, stringkey *kill_hash, stringkey *set_hash, uint4 db_cmd_bm, uint4 tf_cmd_bm) { char cmd_str[MAX_COMMANDS_LEN]; int cmd_str_len; mval mv_hash; mval mv_trig_indx; int4 result; uint4 tmp_bm; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (!validate_label(trigvn, trigvn_len)) return INVALID_LABEL; /* If the trigger file command string is contained in the database command and either * 1. the trigger file command has no SET components or * 2. the trigger file command matched a database SET component * then the trigger file command is already in the database, so return. */ if ((tf_cmd_bm == (db_cmd_bm & tf_cmd_bm)) && (!(tf_cmd_bm & gvtr_cmd_mask[GVTR_CMDTYPE_SET]) || db_matched_set)) return NO_CMD_CHANGE; assert(!db_matched_set || db_matched_kill); /* If merge would combine K and ZTK, it's an error */ if (((db_cmd_bm & gvtr_cmd_mask[GVTR_CMDTYPE_KILL]) && (tf_cmd_bm & gvtr_cmd_mask[GVTR_CMDTYPE_ZTKILL])) || ((db_cmd_bm & gvtr_cmd_mask[GVTR_CMDTYPE_ZTKILL]) && (tf_cmd_bm & gvtr_cmd_mask[GVTR_CMDTYPE_KILL]))) return K_ZTK_CONFLICT; if (!db_matched_set && db_matched_kill && (tf_cmd_bm & gvtr_cmd_mask[GVTR_CMDTYPE_SET]) && (db_cmd_bm & gvtr_cmd_mask[GVTR_CMDTYPE_SET])) { tmp_bm = (db_cmd_bm | tf_cmd_bm); if (tmp_bm == db_cmd_bm) { /* No change to commands in the KILL trigger entry in db. * SET trigger (if it exists and is in a different trigger) to be processed separately. */ return ADD_SET_NOCHNG_KILL_TRIG; } /* Commands are being added to the existing KILL trigger entry in db */ cmd_str_len = ARRAYSIZE(cmd_str); COMMAND_BITMAP_TO_STR(cmd_str, tmp_bm, cmd_str_len); i2mval(&mv_trig_indx, trigger_index); SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[CMD_SUB], STRLEN(trigger_subs[CMD_SUB]), cmd_str, cmd_str_len, result); assert(result == PUT_SUCCESS); return (result == PUT_SUCCESS) ? ADD_SET_MODIFY_KILL_TRIG : result; } cmd_str_len = ARRAYSIZE(cmd_str); COMMAND_BITMAP_TO_STR(cmd_str, db_cmd_bm | tf_cmd_bm, cmd_str_len); i2mval(&mv_trig_indx, trigger_index); SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[CMD_SUB], STRLEN(trigger_subs[CMD_SUB]), cmd_str, cmd_str_len, result); if (PUT_SUCCESS != result) return result; strcpy(trig_cmds, cmd_str); if ((gvtr_cmd_mask[GVTR_CMDTYPE_SET] & tf_cmd_bm) && !(gvtr_cmd_mask[GVTR_CMDTYPE_SET] & db_cmd_bm)) { /* need to add SET attributes */ if (0 < value_len[DELIM_SUB]) { SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[DELIM_SUB], STRLEN(trigger_subs[DELIM_SUB]), values[DELIM_SUB], value_len[DELIM_SUB], result); if (PUT_SUCCESS != result) return result; } if (0 < value_len[ZDELIM_SUB]) { SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[ZDELIM_SUB], STRLEN(trigger_subs[ZDELIM_SUB]), values[ZDELIM_SUB], value_len[ZDELIM_SUB], result); if (PUT_SUCCESS != result) return result; } if (0 < value_len[PIECES_SUB]) { SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[PIECES_SUB], STRLEN(trigger_subs[PIECES_SUB]), values[PIECES_SUB], value_len[PIECES_SUB], result); if (PUT_SUCCESS != result) return result; } if (!(gvtr_cmd_mask[GVTR_CMDTYPE_SET] & db_cmd_bm) && (gvtr_cmd_mask[GVTR_CMDTYPE_SET] & tf_cmd_bm)) { /* We gained an "S" so we need to add the set hash value */ result = add_trigger_hash_entry(trigvn, trigvn_len, values[CMD_SUB], trigger_index, FALSE, kill_hash, set_hash); if (PUT_SUCCESS != result) return result; MV_FORCE_UMVAL(&mv_hash, set_hash->hash_code); SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MVAL(trigvn, trigvn_len, mv_trig_indx, trigger_subs[BHASH_SUB], STRLEN(trigger_subs[BHASH_SUB]), mv_hash, result); if (PUT_SUCCESS != result) return result; } } return ADD_UPDATE_CMDS; } STATICFNDEF int4 add_trigger_options_attributes(char *trigvn, int trigvn_len, int trigger_index, char *trig_options, char **values, uint4 *value_len) { uint4 db_option_bm; mval mv_trig_indx; char option_str[MAX_OPTIONS_LEN]; int option_str_len; int4 result; uint4 tf_option_bm; uint4 tmp_bm; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; BUILD_OPTION_BITMAP(db_option_bm, trig_options); BUILD_OPTION_BITMAP(tf_option_bm, values[OPTIONS_SUB]); if (tf_option_bm == db_option_bm) /* If trigger file OPTIONS is contained in the DB OPTIONS, then trigger file entry is already in DB, just return */ return NO_OPTIONS_CHANGE; tmp_bm = tf_option_bm; if (!validate_label(trigvn, trigvn_len)) return INVALID_LABEL; option_str_len = ARRAYSIZE(option_str); OPTION_BITMAP_TO_STR(option_str, tmp_bm, option_str_len); i2mval(&mv_trig_indx, trigger_index); SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[OPTIONS_SUB], STRLEN(trigger_subs[OPTIONS_SUB]), option_str, option_str_len, result); if (PUT_SUCCESS != result) return result; strcpy(trig_options, option_str); return ADD_UPDATE_OPTIONS; } STATICFNDEF boolean_t subtract_trigger_cmd_attributes(char *trigvn, int trigvn_len, char *trig_cmds, char **values, uint4 *value_len, boolean_t db_matched_set, stringkey *kill_hash, stringkey *set_hash, int trigger_index, uint4 db_cmd_bm, uint4 tf_cmd_bm) { char cmd_str[MAX_COMMANDS_LEN]; int cmd_str_len; uint4 restore_set = 0; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (!db_matched_set && (gvtr_cmd_mask[GVTR_CMDTYPE_SET] & tf_cmd_bm)) { /* If the set compare failed, we don't want to consider the SET */ restore_set = gvtr_cmd_mask[GVTR_CMDTYPE_SET]; tf_cmd_bm &= ~restore_set; } if (0 == (db_cmd_bm & tf_cmd_bm)) return 0; /* If trigger file CMD does NOT overlap with the DB CMD, then no match. So no delete. Just return */ cmd_str_len = ARRAYSIZE(cmd_str); if (db_cmd_bm != (db_cmd_bm & tf_cmd_bm)) { /* combine cmds - subtract trigger file attributes from db attributes */ COMMAND_BITMAP_TO_STR(cmd_str, (db_cmd_bm & tf_cmd_bm) ^ db_cmd_bm, cmd_str_len); strcpy(trig_cmds, cmd_str); /* If we lost the "S", need to delete the set hash value */ if ((0 != (gvtr_cmd_mask[GVTR_CMDTYPE_SET] & db_cmd_bm)) && (0 == (gvtr_cmd_mask[GVTR_CMDTYPE_SET] & ((db_cmd_bm & tf_cmd_bm) ^ db_cmd_bm)))) cleanup_trigger_hash(trigvn, trigvn_len, values, value_len, set_hash, kill_hash, FALSE, trigger_index); } else { /* Both cmds are the same - candidate for delete */ trig_cmds[0] = '\0'; } return SUB_UPDATE_CMDS; } STATICFNDEF int4 modify_record(char *trigvn, int trigvn_len, char add_delete, int trigger_index, char **values, uint4 *value_len, mval *trigger_count, boolean_t db_matched_set, boolean_t db_matched_kill, stringkey *kill_hash, stringkey *set_hash, int set_kill_bitmask) { char db_cmds[MAX_COMMANDS_LEN + 1]; boolean_t name_matches, sub_cmds; int4 result; uint4 retval; mval trigindx; char trig_cmds[MAX_COMMANDS_LEN + 1]; char trig_name[MAX_USER_TRIGNAME_LEN + 2]; /* One spot for # delimiter and one for trailing 0 */ char trig_options[MAX_OPTIONS_LEN + 1]; unsigned char util_buff[MAX_TRIG_UTIL_LEN]; int4 util_len; int trig_cmds_len; int trig_name_len; int trig_options_len; mval val; uint4 db_cmd_bm, tf_cmd_bm; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; i2mval(&trigindx, trigger_index); /* get(^#t(GVN,trigindx,"CMD") */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigindx, trigger_subs[CMD_SUB], STRLEN(trigger_subs[CMD_SUB])); if (!gvcst_get(&val)) /* There has to be a command string */ HASHT_GVN_DEFINITION_RETRY_OR_ERROR(trigger_index, ",\"CMD\"", REG2CSA(gv_cur_region)); trig_cmds_len = MIN(val.str.len, MAX_COMMANDS_LEN); memcpy(trig_cmds, val.str.addr, trig_cmds_len); trig_cmds[trig_cmds_len] = '\0'; BUILD_COMMAND_BITMAP(db_cmd_bm, trig_cmds); BUILD_COMMAND_BITMAP(tf_cmd_bm, values[CMD_SUB]); /* If trigger file has specified SET and/or KILL triggers and each of them matched to different triggers in database, * filter out only the respective category of triggers to go forward with the command addition/deletion. */ if (OPR_KILL == set_kill_bitmask) tf_cmd_bm &= ~gvtr_cmd_mask[GVTR_CMDTYPE_SET]; else if (OPR_SET == set_kill_bitmask) tf_cmd_bm &= gvtr_cmd_mask[GVTR_CMDTYPE_SET]; /* get(^#t(GVN,trigindx,"OPTIONS") */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigindx, trigger_subs[OPTIONS_SUB], STRLEN(trigger_subs[OPTIONS_SUB])); if (gvcst_get(&val)) { trig_options_len = MIN(val.str.len, MAX_OPTIONS_LEN); memcpy(trig_options, val.str.addr, trig_options_len); } else trig_options_len = 0; trig_options[trig_options_len] = '\0'; /* get(^#t(GVN,trigindx,"TRIGNAME") */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigindx, trigger_subs[TRIGNAME_SUB], STRLEN(trigger_subs[TRIGNAME_SUB])); if (gvcst_get(&val)) { trig_name_len = MIN(val.str.len, ARRAYSIZE(trig_name)); memcpy(trig_name, val.str.addr, trig_name_len); } else trig_name_len = 0; trig_name[trig_name_len] = '\0'; if ('+' == add_delete) { /* Process -OPTIONS */ result = add_trigger_options_attributes(trigvn, trigvn_len, trigger_index, trig_options, values, value_len); assert((NO_OPTIONS_CHANGE == result) || (ADD_UPDATE_OPTIONS == result) /* 0 or 0x04 */ || (INVALID_LABEL == result) || (VAL_TOO_LONG == result) || (KEY_TOO_LONG == result)); /* < 0 */ if (0 > result) return result; assert((NO_OPTIONS_CHANGE == result) || (ADD_UPDATE_OPTIONS == result)); if (NO_OPTIONS_CHANGE != result) { /* Check if specified list of commands matches commands in database. If not cannot proceed */ if (tf_cmd_bm != db_cmd_bm) return OPTIONS_CMDS_CONFLICT; } retval = result; /* Process -NAME */ result = update_trigger_name(trigvn, trigvn_len, trigger_index, trig_name, values[TRIGNAME_SUB], value_len[TRIGNAME_SUB]); assert((NO_NAME_CHANGE == result) || (ADD_UPDATE_NAME == result) /* 0 or 0x01 */ || (INVALID_LABEL == result) || (VAL_TOO_LONG == result) || (KEY_TOO_LONG == result)); /* < 0 */ if (0 > result) return result; assert((NO_NAME_CHANGE == result) || (ADD_UPDATE_NAME == result)); if (NO_NAME_CHANGE != result) { /* Check if specified list of commands contains commands in database. If not cannot proceed */ if ((tf_cmd_bm &db_cmd_bm) != db_cmd_bm) return NAME_CMDS_CONFLICT; } retval |= result; /* Process -CMD */ result = add_trigger_cmd_attributes(trigvn, trigvn_len, trigger_index, trig_cmds, values, value_len, db_matched_set, db_matched_kill, kill_hash, set_hash, db_cmd_bm, tf_cmd_bm); assert((NO_CMD_CHANGE == result) || (ADD_UPDATE_CMDS == result) /* 0 or 0x02 */ || (ADD_SET_MODIFY_KILL_TRIG == result) || (ADD_SET_NOCHNG_KILL_TRIG == result) /* < 0 */ || (INVALID_LABEL == result) || (K_ZTK_CONFLICT == result) /* < 0 */ || (VAL_TOO_LONG == result) || (KEY_TOO_LONG == result)); /* < 0 */ if (0 > result) return result; assert((NO_CMD_CHANGE == result) || (ADD_UPDATE_CMDS == result)); retval |= result; } else { name_matches = (0 == value_len[TRIGNAME_SUB]) || ((value_len[TRIGNAME_SUB] == (STRLEN(trig_name) - 1)) && (0 == memcmp(values[TRIGNAME_SUB], trig_name, value_len[TRIGNAME_SUB]))); if (!name_matches) return 0; memcpy(db_cmds, trig_cmds, SIZEOF(trig_cmds)); sub_cmds = subtract_trigger_cmd_attributes(trigvn, trigvn_len, trig_cmds, values, value_len, db_matched_set, kill_hash, set_hash, trigger_index, db_cmd_bm, tf_cmd_bm); /* options are ignored in case of deletes so no need for "subtract_trigger_options_attributes()" */ if (!sub_cmds) return 0; if (0 == trig_cmds[0]) { result = trigger_delete(trigvn, trigvn_len, trigger_count, trigger_index); assert((VAL_TOO_LONG == result) || (KEY_TOO_LONG == result) /* < 0 */ || (PUT_SUCCESS == result)); /* == 0 */ if (0 > result) return result; return DELETE_REC; } retval = 0; if (sub_cmds) { result = update_commands(trigvn, trigvn_len, trigger_index, trig_cmds, db_cmds); if (SUB_UPDATE_CMDS != result) return result; retval |= result; } } return retval; } STATICFNDEF int4 gen_trigname_sequence(char *trigvn, uint4 trigvn_len, mval *trigger_count, char *user_trigname_str, uint4 user_trigname_len) { char name_and_index[MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT]; char *ptr1; int seq_num; int4 result; char *seq_ptr, *uniq_ptr; char trig_name[MAX_USER_TRIGNAME_LEN + 2]; uint4 trigname_len, uniq_ptr_len; char unique_seq_str[NUM_TRIGNAME_SEQ_CHARS + 1]; mval val, *val_ptr; int var_count, max_seq_num; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(MAX_USER_TRIGNAME_LEN >= user_trigname_len); assert(!gv_cur_region->read_only); /* caller should have already checked this */ assert(cs_addrs->hasht_tree == gv_target); /* should have been set up by caller */ if (0 == user_trigname_len) { /* autogenerated name -- might be long so take MIN */ trigname_len = MIN(trigvn_len, MAX_AUTO_TRIGNAME_LEN); memcpy(trig_name, trigvn, trigname_len); /* immediate prior statement ensures trigname_len is OK */ val_ptr = &val; if (gv_target->root) { /* $get(^#t("#TNAME",GVN,"#SEQNUM")) */ BUILD_HASHT_SUB_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), trig_name, trigname_len, LITERAL_HASHSEQNUM, STR_LIT_LEN(LITERAL_HASHSEQNUM)); seq_num = gvcst_get(val_ptr) ? mval2i(val_ptr) + 1 : 1; max_seq_num = MAX_TRIGNAME_SEQ_NUM; /* 4SCA: seq_num < MAX_TRIGNAME_SEQ_NUM which implies a max length of NUM_TRIGNAME_SEQ_CHARS */ if (max_seq_num < seq_num) return TOO_MANY_TRIGGERS; } else seq_num = 1; uniq_ptr = unique_seq_str; INT2STR(seq_num, uniq_ptr); uniq_ptr_len = STRLEN(uniq_ptr); /* 4SCA: Integer overflow not possible : MAX_TRIGNAME_SEQ_NUM, 999999, can only be NUM_TRIGNAME_SEQ_CHARS long */ if (NUM_TRIGNAME_SEQ_CHARS < uniq_ptr_len) return AUTO_NAME_GEN_FAIL; /* set ^#t("#TNAME",GVN,"#SEQNUM")++ */ SET_TRIGGER_GLOBAL_SUB_SUB_SUB_STR(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), trig_name, trigname_len, LITERAL_HASHSEQNUM, STR_LIT_LEN(LITERAL_HASHSEQNUM), uniq_ptr, uniq_ptr_len, result); if (PUT_SUCCESS != result) return result; /* set ^#t("#TNAME",GVN,"#TNCOUNT")++ */ BUILD_HASHT_SUB_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), trig_name, trigname_len, LITERAL_HASHTNCOUNT, STR_LIT_LEN(LITERAL_HASHTNCOUNT)); var_count = gvcst_get(val_ptr) ? mval2i(val_ptr) + 1 : 1; i2mval(&val, var_count); SET_TRIGGER_GLOBAL_SUB_SUB_SUB_MVAL(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), trig_name, trigname_len, LITERAL_HASHTNCOUNT, STR_LIT_LEN(LITERAL_HASHTNCOUNT), val, result); if (PUT_SUCCESS != result) return result; seq_ptr = user_trigname_str; memcpy(seq_ptr, trig_name, trigname_len); seq_ptr += trigname_len; *seq_ptr++ = TRIGNAME_SEQ_DELIM; memcpy(seq_ptr, uniq_ptr, uniq_ptr_len); seq_ptr += uniq_ptr_len; assert(MAX_USER_TRIGNAME_LEN >= (trigname_len + 1 + uniq_ptr_len)); user_trigname_len = MIN((trigname_len + 1 + uniq_ptr_len), MAX_USER_TRIGNAME_LEN); } else seq_ptr = user_trigname_str + user_trigname_len; ptr1 = name_and_index; memcpy(ptr1, trigvn, trigvn_len); ptr1 += trigvn_len; *ptr1++ = '\0'; MV_FORCE_STR(trigger_count); memcpy(ptr1, trigger_count->str.addr, trigger_count->str.len); SET_TRIGGER_GLOBAL_SUB_SUB_STR(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), user_trigname_str, user_trigname_len, name_and_index, trigvn_len + 1 + trigger_count->str.len, result); if (PUT_SUCCESS != result) return result; *seq_ptr++ = TRIGNAME_SEQ_DELIM; /* all trigger names end with a hash mark, so append one */ *seq_ptr = '\0'; return SEQ_SUCCESS; } boolean_t trigger_update_rec(mval *trigger_rec, boolean_t noprompt, uint4 *trig_stats, io_pair *trigfile_device, int4 *record_num) { char add_delete, *trigptr; char ans[2]; boolean_t multiline_parse_fail; mname_entry gvname; int4 max_len; boolean_t multi_line, multi_line_xecute; int4 rec_len; int4 rec_num; boolean_t status; char tfile_rec_val[MAX_BUFF_SIZE]; char trigvn[MAX_MIDENT_LEN + 1]; char disp_trigvn[MAX_MIDENT_LEN + SPANREG_REGION_LITLEN + MAX_RN_LEN + 1 + 1]; /* SPANREG_REGION_LITLEN for " (region ", MAX_RN_LEN for region name, * 1 for ")" and 1 for trailing '\0'. */ int disp_trigvn_len; uint4 trigvn_len; char *values[NUM_SUBS], *save_values[NUM_SUBS]; uint4 len, value_len[NUM_SUBS], save_value_len[NUM_SUBS]; stringkey kill_trigger_hash, set_trigger_hash; char tmp_str[MAX_HASH_LEN + 1]; char xecute_buffer[MAX_BUFF_SIZE + MAX_XECUTE_LEN]; unsigned char *dispbuff; mval multi_jrec, *trigjrec; char *trigjrecptr; int trigjreclen; io_pair io_save_device; int4 max_xecute_size; boolean_t no_error; gvnh_reg_t *gvnh_reg; char utilprefix[1024]; int utilprefixlen, displen; int reg_index, min_reg_index, max_reg_index; boolean_t first_gtmio; boolean_t jnl_format_done, new_name_check_done, new_name, first_error; trig_stats_t this_trig_status, overall_trig_status; gv_namehead *gvt; gvnh_spanreg_t *gvspan; hash128_state_t set_hash_state, kill_hash_state; uint4 set_hash_totlen, kill_hash_totlen; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* We are going to operate on the ^#t global which does not span regions. Reset gd_targ_gvnh_reg * leftover from previous GV_BIND_SUBSNAME_IF_GVSPAN call to not affect any op_zprevious etc. * (e.g. invocation trigger_update_rec -> trigupdrec_reg -> add_trigger_hash_entry -> op_zprevious) * so it focuses on gvcst_zprevious instead of gvcst_spr_zprevious for the ^#t global. * It is okay not to restore TREF(gd_targ_gvnh_reg) since it will be initialized as part of the * next op_gv* call done by the caller (be it mumps or mupip). */ TREF(gd_targ_gvnh_reg) = NULL; assert(dollar_tlevel); assert(0 > memcmp(LITERAL_HASHLABEL, LITERAL_MAXHASHVAL, MIN(STRLEN(LITERAL_HASHLABEL), STRLEN(LITERAL_MAXHASHVAL)))); assert(0 > memcmp(LITERAL_HASHCOUNT, LITERAL_MAXHASHVAL, MIN(STRLEN(LITERAL_HASHCOUNT), STRLEN(LITERAL_MAXHASHVAL)))); assert(0 > memcmp(LITERAL_HASHCYCLE, LITERAL_MAXHASHVAL, MIN(STRLEN(LITERAL_HASHCYCLE), STRLEN(LITERAL_MAXHASHVAL)))); assert(0 > memcmp(LITERAL_HASHTNAME, LITERAL_MAXHASHVAL, MIN(STRLEN(LITERAL_HASHTNAME), STRLEN(LITERAL_MAXHASHVAL)))); assert(0 > memcmp(LITERAL_HASHTNCOUNT, LITERAL_MAXHASHVAL, MIN(STRLEN(LITERAL_HASHTNCOUNT), STRLEN(LITERAL_MAXHASHVAL)))); rec_num = (NULL == record_num) ? 0 : *record_num; gvinit(); len = trigger_rec->str.len; if (NULL == trigfile_device) { /* Check if this is a multi-line trigger. In that case, use just the first line for the below processing. * The rest of the lines will later be copied over into values[XECUTE_SUB]. */ trigjrecptr = memchr(trigger_rec->str.addr, '\n', len); if (NULL != trigjrecptr) len = trigjrecptr - trigger_rec->str.addr; } else assert(NULL == memchr(trigger_rec->str.addr, '\n', len)); if ((0 == len) || (COMMENT_LITERAL == *trigger_rec->str.addr)) return TRIG_SUCCESS; assert(dollar_tlevel); if (('-' != *trigger_rec->str.addr) && ('+' != *trigger_rec->str.addr)) { trig_stats[STATS_ERROR_TRIGFILE]++; len = trigger_rec->str.len = MIN(len, MAX_SRCLINE); trigger_rec->str.addr[len] = 0; displen = ZWR_EXP_RATIO(len); dispbuff = malloc(displen); format2zwr((sm_uc_ptr_t)trigger_rec->str.addr, len, dispbuff, &displen); /* returns displayable string & displen */ util_out_print_gtmio("Error : missing +/- at start of line: !AD", FLUSH, displen, dispbuff); free(dispbuff); return TRIG_FAILURE; } add_delete = trigger_rec->str.addr[0]; len--; if ('-' == add_delete) { if ((1 == len) && ('*' == trigger_rec->str.addr[1])) { if ((NULL == trigfile_device) && (NULL != trigjrecptr)) { util_out_print_gtmio("Error : Newline not allowed in trigger name for delete operation", FLUSH); trig_stats[STATS_ERROR_TRIGFILE]++; return TRIG_FAILURE; } mustprompt = (noprompt)? FALSE : mustprompt; if (mustprompt) { util_out_print("This operation will delete all triggers.", FLUSH); promptanswer = mu_interactive("Triggers NOT deleted"); mustprompt = FALSE; } if (FALSE == promptanswer) return TRIG_SUCCESS; trigger_delete_all(trigger_rec, trig_stats); /* updates trig_stats[] appropriately */ return TRIG_SUCCESS; } else if ((0 == len) || ('^' != trigger_rec->str.addr[1])) { /* if the length < 0 let trigger_delete_name respond with the error message */ if ((NULL == trigfile_device) && (NULL != trigjrecptr)) { util_out_print_gtmio("Error : Newline not allowed in trigger name for delete operation", FLUSH); trig_stats[STATS_ERROR_TRIGFILE]++; return TRIG_FAILURE; } status = trigger_delete_name(trigger_rec, trig_stats); /* updates trig_stats[] appropriately */ return status; } } values[GVSUBS_SUB] = tfile_rec_val; /* GVSUBS will be the first entry set so initialize it */ max_len = (int4)SIZEOF(tfile_rec_val); trigptr = trigger_rec->str.addr + 1; multi_line_xecute = FALSE; if (!trigger_parse(trigptr, len, trigvn, values, value_len, &max_len, &multi_line_xecute)) { trig_stats[STATS_ERROR_TRIGFILE]++; return TRIG_FAILURE; } assert(dollar_tlevel && (('-' == trigger_rec->str.addr[0]) || ('+' == trigger_rec->str.addr[0]))); trigvn_len = STRLEN(trigvn); set_trigger_hash.str.addr = &tmp_str[0]; set_trigger_hash.str.len = MAX_HASH_LEN; build_set_cmp_str(trigvn, trigvn_len, values, value_len, &set_trigger_hash.str, multi_line_xecute); /* Note that we are going to compute the hash of the trigger string in bits and pieces. * So use the STR_PHASH* macros (the progressive variants), not the STR_HASH macros. */ STR_PHASH_INIT(set_hash_state, set_hash_totlen); STR_PHASH_PROCESS(set_hash_state, set_hash_totlen, set_trigger_hash.str.addr, set_trigger_hash.str.len); kill_trigger_hash.str.addr = &tmp_str[0]; kill_trigger_hash.str.len = MAX_HASH_LEN; build_kill_cmp_str(trigvn, trigvn_len, values, value_len, &kill_trigger_hash.str, multi_line_xecute); STR_PHASH_INIT(kill_hash_state, kill_hash_totlen); STR_PHASH_PROCESS(kill_hash_state, kill_hash_totlen, kill_trigger_hash.str.addr, kill_trigger_hash.str.len); first_gtmio = TRUE; utilprefixlen = ARRAYSIZE(utilprefix); trigjrec = trigger_rec; if (multi_line_xecute) { if (NULL != trigfile_device) { assert(SIZEOF(xecute_buffer) == MAX_BUFF_SIZE + MAX_XECUTE_LEN); /* Leave MAX_BUFF_SIZE to store other (excluding XECUTE) parts of the trigger definition. * This way we can copy over these once the multi-line XECUTE string is constructed and * use this to write the TLGTRIG/ULGTRIG journal record in case we do "jnl_format" later. */ values[XECUTE_SUB] = &xecute_buffer[MAX_BUFF_SIZE]; multi_jrec = *trigger_rec; trigjrec = &multi_jrec; trigjreclen = trigjrec->str.len + 1; /* 1 for newline */ assert(trigjreclen < MAX_BUFF_SIZE); trigjrecptr = &xecute_buffer[MAX_BUFF_SIZE - trigjreclen]; memcpy(trigjrecptr, trigjrec->str.addr, trigjreclen - 1); trigjrecptr[trigjreclen - 1] = '\n'; trigjrec->str.addr = trigjrecptr; assert(dollar_tlevel && (('-' == trigjrec->str.addr[0]) || ('+' == trigjrec->str.addr[0]))); value_len[XECUTE_SUB] = 0; max_xecute_size = MAX_XECUTE_LEN; io_save_device = io_curr_device; io_curr_device = *trigfile_device; multi_line = multi_line_xecute; /* We are in a multi-line trigger definition. Each trigger line should now correspond to an M source line. * The GT.M compiler does not accept any M source line > MAX_SRCLINE bytes. So issue error right away in * case source line is > MAX_SRCLINE. No point reading the full line and then issuing the error. * Note that MAX_SRCLINE also includes the newline at end of line hence the MAX_SRCLINE - 1 usage below. */ multiline_parse_fail = FALSE; while (multi_line) { rec_len = file_input_get(&trigptr, MAX_SRCLINE - 1); if (!io_curr_device.in->dollar.x) rec_num++; if (0 > rec_len) { assert((FILE_INPUT_GET_LINE2LONG == rec_len) || (FILE_INPUT_GET_ERROR == rec_len)); if (FILE_INPUT_GET_ERROR == rec_len) break; do { /* Read remainder of over-length line in as many MAX_SRCLINE chunks as needed */ rec_len = file_input_get(&trigptr, MAX_SRCLINE - 1); if (!io_curr_device.in->dollar.x) rec_num++; if (0 <= rec_len) break; /* reached end of line */ assert((FILE_INPUT_GET_LINE2LONG == rec_len) || (FILE_INPUT_GET_ERROR == rec_len)); if (FILE_INPUT_GET_ERROR == rec_len) break; } while (TRUE); if (FILE_INPUT_GET_ERROR == rec_len) break; if (!multiline_parse_fail) { UTIL_PRINT_PREFIX_IF_NEEDED(first_gtmio, utilprefix, &utilprefixlen); util_out_print_gtmio("Error : Multi-line trigger -XECUTE exceeds maximum " "M source line length of !UL", FLUSH, MAX_SRCLINE); value_len[XECUTE_SUB] = 1; values[XECUTE_SUB][0] = ' '; max_xecute_size = SIZEOF(xecute_buffer); } multiline_parse_fail = TRUE; } io_curr_device = io_save_device; /* In case we have to write an error message */ no_error = trigger_parse(trigptr, (uint4)rec_len, trigvn, values, value_len, &max_xecute_size, &multi_line); assert(dollar_tlevel && (('-' == trigjrec->str.addr[0]) || ('+' == trigjrec->str.addr[0]))); io_curr_device = *trigfile_device; if (!no_error) { /* An error occurred (e.g. Trigger definition too long etc.). * Consume remainder of multi-line trigger definition before moving on. * But before that replace XECUTE string constructed till now with a dummy one. */ assert(!multi_line); multi_line = TRUE; multiline_parse_fail = TRUE; } if (multiline_parse_fail) { value_len[XECUTE_SUB] = 1; values[XECUTE_SUB][0] = ' '; max_xecute_size = SIZEOF(xecute_buffer); } } trigjrec->str.len = trigjreclen + value_len[XECUTE_SUB]; if (NULL != record_num) *record_num = rec_num; if (0 > rec_len) { assert(FILE_INPUT_GET_ERROR == rec_len); io_curr_device = io_save_device; util_out_print_gtmio("Error : Multi-line trigger -XECUTE is not properly terminated", FLUSH); trig_stats[STATS_ERROR_TRIGFILE]++; return TRIG_FAILURE; } if (multiline_parse_fail) { /* error message has already been issued */ io_curr_device = io_save_device; trig_stats[STATS_ERROR_TRIGFILE]++; return TRIG_FAILURE; } io_curr_device = io_save_device; } else { assert(memchr(trigjrec->str.addr, '\n', trigjrec->str.len) == trigjrecptr); values[XECUTE_SUB] = trigjrecptr + 1; value_len[XECUTE_SUB] = trigjrec->str.addr + trigjrec->str.len - (trigjrecptr + 1); if ('\n' != values[XECUTE_SUB][value_len[XECUTE_SUB] - 1]) { util_out_print_gtmio("Error : Multi-line xecute in $ztrigger ITEM must end in newline", FLUSH); trig_stats[STATS_ERROR_TRIGFILE]++; return TRIG_FAILURE; } if (!process_xecute(values[XECUTE_SUB], &value_len[XECUTE_SUB], TRUE)) { CONV_STR_AND_PRINT("Error : Parsing XECUTE string: ", value_len[XECUTE_SUB], values[XECUTE_SUB]); trig_stats[STATS_ERROR_TRIGFILE]++; return TRIG_FAILURE; } /* trigjrec is already properly set up */ } STR_PHASH_PROCESS(kill_hash_state, kill_hash_totlen, values[XECUTE_SUB], value_len[XECUTE_SUB]); STR_PHASH_PROCESS(set_hash_state, set_hash_totlen, values[XECUTE_SUB], value_len[XECUTE_SUB]); } else if ((NULL == trigfile_device) && (NULL != trigjrecptr)) { /* If this is a not a multi-line xecute string, we dont expect newlines. The only exception is if it is * the last byte in the string. */ assert(memchr(trigjrec->str.addr, '\n', trigjrec->str.len) == trigjrecptr); *trigjrecptr++; if (trigjrecptr != (trigjrec->str.addr + trigjrec->str.len)) { util_out_print_gtmio("Error : Newline allowed only inside multi-line xecute in $ztrigger ITEM", FLUSH); trig_stats[STATS_ERROR_TRIGFILE]++; return TRIG_FAILURE; } } STR_PHASH_RESULT(set_hash_state, set_hash_totlen, set_trigger_hash.hash_code); STR_PHASH_RESULT(kill_hash_state, kill_hash_totlen, kill_trigger_hash.hash_code); gvname.var_name.addr = trigvn; gvname.var_name.len = trigvn_len; COMPUTE_HASH_MNAME(&gvname); GV_BIND_NAME_ONLY(gd_header, &gvname, gvnh_reg); gvspan = gvnh_reg->gvspan; if (NULL != gvspan) { gvnh_spanreg_subs_gvt_init(gvnh_reg, gd_header, NULL); reg_index = gvspan->min_reg_index; min_reg_index = gvspan->min_reg_index; max_reg_index = gvspan->max_reg_index; assert(0 <= reg_index); assert(reg_index < gd_header->n_regions); gvt = GET_REAL_GVT(gvspan->gvt_array[reg_index - min_reg_index]); assert(NULL != gvt); gv_target = gvt; gv_cur_region = gd_header->regions + reg_index; change_reg(); SET_DISP_TRIGVN(gv_cur_region, disp_trigvn, disp_trigvn_len, trigvn, trigvn_len); /* Save values[] and value_len[] arrays since they might be overwritten inside "trigupdrec_reg" * but we need the unmodified values for each call to that function. */ assert(SIZEOF(value_len) == SIZEOF(save_value_len)); memcpy(save_value_len, value_len, SIZEOF(value_len)); assert(SIZEOF(values) == SIZEOF(save_values)); memcpy(save_values, values, SIZEOF(values)); } else { memcpy(disp_trigvn, trigvn, trigvn_len); disp_trigvn_len = trigvn_len; disp_trigvn[disp_trigvn_len] = '\0'; /* null terminate just in case */ } jnl_format_done = FALSE; new_name_check_done = FALSE; first_error = TRUE; overall_trig_status = STATS_UNCHANGED_TRIGFILE; do { /* At this point gv_cur_region/cs_addrs/gv_target already point to the correct region. * For a spanning global, they point to one of the spanned regions in each iteration of the do-while loop below. */ this_trig_status = trigupdrec_reg(trigvn, trigvn_len, &jnl_format_done, trigjrec, &new_name_check_done, &new_name, &values[0], &value_len[0], add_delete, &kill_trigger_hash, &set_trigger_hash, &disp_trigvn[0], disp_trigvn_len, trig_stats, &first_gtmio, utilprefix, &utilprefixlen); assert((STATS_UNCHANGED_TRIGFILE == this_trig_status) || (STATS_NOERROR_TRIGFILE == this_trig_status) || (STATS_ERROR_TRIGFILE == this_trig_status)); if (STATS_ERROR_TRIGFILE == this_trig_status) { if (first_error) { trig_stats[STATS_ERROR_TRIGFILE]++; first_error = FALSE; } overall_trig_status = STATS_ERROR_TRIGFILE; } else if (STATS_UNCHANGED_TRIGFILE == overall_trig_status) overall_trig_status = this_trig_status; /* else if (STATS_NOERROR_TRIGFILE == overall_trig_status) : it is already what it should be */ /* else if (STATS_ERROR_TRIGFILE == overall_trig_status) : it is already what it should be */ if (NULL == gvspan) break; if (reg_index >= max_reg_index) break; do { reg_index++; assert(reg_index <= max_reg_index); assert(reg_index < gd_header->n_regions); gvt = GET_REAL_GVT(gvspan->gvt_array[reg_index - min_reg_index]); if (NULL == gvt) { assert(reg_index < max_reg_index); continue; } gv_target = gvt; gv_cur_region = gd_header->regions + reg_index; change_reg(); SET_DISP_TRIGVN(gv_cur_region, disp_trigvn, disp_trigvn_len, trigvn, trigvn_len); /* Restore values[] and value_len[] before next call to "trigupdrec_reg" */ assert(SIZEOF(value_len) == SIZEOF(save_value_len)); memcpy(value_len, save_value_len, SIZEOF(save_value_len)); assert(SIZEOF(values) == SIZEOF(save_values)); memcpy(values, save_values, SIZEOF(save_values)); break; } while (TRUE); } while (TRUE); if ((STATS_UNCHANGED_TRIGFILE == overall_trig_status) || (STATS_NOERROR_TRIGFILE == overall_trig_status)) { trig_stats[overall_trig_status]++; return TRIG_SUCCESS; } else { assert(STATS_ERROR_TRIGFILE == overall_trig_status); return TRIG_FAILURE; } } STATICFNDEF trig_stats_t trigupdrec_reg(char *trigvn, uint4 trigvn_len, boolean_t *jnl_format_done, mval *trigjrec, boolean_t *new_name_check_done, boolean_t *new_name_ptr, char **values, uint4 *value_len, char add_delete, stringkey *kill_trigger_hash, stringkey *set_trigger_hash, char *disp_trigvn, int disp_trigvn_len, uint4 *trig_stats, boolean_t *first_gtmio, char *utilprefix, int *utilprefixlen) { mval *trigname[NUM_OPRS]; /* names of matching kill and/or set trigger */ boolean_t new_name; sgmnt_addrs *csa; mval dummymval; boolean_t skip_set_trigger, trigger_exists; mval *trigger_count; boolean_t newtrigger; int set_index, kill_index, tmp_index; boolean_t db_matched_kill, db_matched_set, tmp_matched_kill, tmp_matched_set; boolean_t full_match, new_match; boolean_t kill_cmp, set_cmp; boolean_t is_set; int oprtype, oprstart, oprend, set_kill_bitmask; char *oprname[] = { "Non-SET", "SET", "SET and/or Non-SET"}; /* index 0 corresponds to OPR_KILL, * 1 to OPR_SET, * 2 if OPR_SETKILL */ char *opname; int4 updates; uint4 trigload_status; int num; boolean_t result; int sub_indx; int4 max_len; mval xecute_index, xecute_size; int4 offset; char *ptr1; mval mv_hash; char trig_name[MAX_USER_TRIGNAME_LEN + 2]; /* One spot for '#' delimiter and one for trailing '\0' */ int trig_protected_mval_push_count; csa = cs_addrs; if (NULL == csa) /* Remote region */ RTS_ERROR_CSA_ABT(csa, VARLSTCNT(6) ERR_REMOTEDBNOTRIG, 4, trigvn_len, trigvn, REG_LEN_STR(gv_cur_region)); if (gv_cur_region->read_only) rts_error_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_TRIGMODREGNOTRW, 2, REG_LEN_STR(gv_cur_region)); assert(cs_addrs == gv_target->gd_csa); DISALLOW_MULTIINST_UPDATE_IN_TP(dollar_tlevel, jnlpool_head, csa, first_sgm_info, TRUE); if (!*jnl_format_done && JNL_WRITE_LOGICAL_RECS(csa)) { /* Attach to jnlpool if replication is turned on. Normally SET or KILL of the ^#t records take care of this * but in case this is a NO-OP trigger operation that wont happen and we still want to write a * TLGTRIG/ULGTRIG journal record. Hence the need to do this. Also write a LGTRIG record in just one region * in case this is a global spanning multiple regions. */ JNLPOOL_INIT_IF_NEEDED(csa, csa->hdr, csa->nl, SCNDDBNOUPD_CHECK_TRUE); assert(dollar_tlevel && (('-' == trigjrec->str.addr[0]) || ('+' == trigjrec->str.addr[0]))); T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_TRIGLOADFAIL); /* needed to set update_trans TRUE on this region * even if NO db updates happen to ^#t nodes. */ jnl_format(JNL_LGTRIG, NULL, trigjrec, 0); *jnl_format_done = TRUE; } SET_GVTARGET_TO_HASHT_GBL(csa); INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED; if (csa->hdr->hasht_upgrade_needed) { /* ^#t needs to be upgraded first before reading/updating it. Cannot proceed. */ if (0 == gv_target->root) { csa->hdr->hasht_upgrade_needed = FALSE; /* Reset now that we know there is no ^#t global in this db. * Note: It is safe to do so even if we dont hold crit. */ } else RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_NEEDTRIGUPGRD, 2, DB_LEN_STR(gv_cur_region)); } if (!*new_name_check_done) { /* Make sure below call is done only ONCE for a global spanning multiple regions since this call goes * through all regions in the gld to figure out if a user-defined trigger name is unique. */ new_name = check_unique_trigger_name_full(values, value_len, &dummymval, &new_match, trigvn, trigvn_len, set_trigger_hash, kill_trigger_hash); *new_name_ptr = new_name; *new_name_check_done = TRUE; } else new_name = *new_name_ptr; skip_set_trigger = FALSE; trig_protected_mval_push_count = 0; /* Protect MVALs from garabage collection - NOTE: trigger_count is done last as that needs to be popped in one case below */ INCR_AND_PUSH_MV_STENT(trigname[OPR_SET]); INCR_AND_PUSH_MV_STENT(trigname[OPR_KILL]); INCR_AND_PUSH_MV_STENT(trigger_count); assert(('+' == add_delete) || ('-' == add_delete)); /* Has to be + or - */ if (gv_target->root) { BUILD_HASHT_SUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT)); if (gvcst_get(trigger_count)) { trigger_exists = trigger_already_exists(trigvn, trigvn_len, values, value_len, set_trigger_hash, kill_trigger_hash, &set_index, &kill_index, &db_matched_set, &db_matched_kill, &full_match, trigname[OPR_SET], trigname[OPR_KILL]); newtrigger = FALSE; } else { newtrigger = TRUE; trigger_exists = FALSE; } } else { newtrigger = TRUE; trigger_exists = FALSE; } set_cmp = (NULL != strchr(values[CMD_SUB], 'S')); kill_cmp = ((NULL != strchr(values[CMD_SUB], 'K')) || (NULL != strchr(values[CMD_SUB], 'R'))); updates = 0; trigload_status = STATS_UNCHANGED_TRIGFILE; if (trigger_exists) { if ((-1 != kill_index) && (set_index || set_cmp) && value_len[TRIGNAME_SUB]) { /* Cannot match two different triggers (corresponding to "kill_index" and "set_index") * with the same user defined name. Note that it is possible if set_index==0 that the * set type trigger does not exist yet but will be created by this call to trigger_update_rec. * Treat that case too as if the separate set trigger existed. */ UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); if (set_index) util_out_print_gtmio("Error : Input trigger on ^!AD with trigger name !AD" \ " cannot match two different triggers named !AD and !AD at the same time", FLUSH, disp_trigvn_len, disp_trigvn, value_len[TRIGNAME_SUB], values[TRIGNAME_SUB], trigname[OPR_KILL]->str.len, trigname[OPR_KILL]->str.addr, trigname[OPR_SET]->str.len, trigname[OPR_SET]->str.addr); else util_out_print_gtmio("Error : Input trigger on ^!AD with trigger name !AD" \ " cannot match a trigger named !AD and a to-be-created SET trigger" \ " at the same time", FLUSH, disp_trigvn_len, disp_trigvn, value_len[TRIGNAME_SUB], values[TRIGNAME_SUB], trigname[OPR_KILL]->str.len, trigname[OPR_KILL]->str.addr); RETURN_AND_POP_MVALS(STATS_ERROR_TRIGFILE); } assert(new_name || !new_match || full_match); if (!new_name && ('+' == add_delete) && !full_match) { opname = (!set_cmp ? oprname[OPR_KILL] : (!kill_cmp ? oprname[OPR_SET] : oprname[OPR_SETKILL])); TRIGGER_SAME_NAME_EXISTS_ERROR(opname, disp_trigvn_len, disp_trigvn); } oprstart = (-1 != kill_index) ? OPR_KILL : (OPR_KILL + 1); oprend = (0 != set_index) ? (OPR_SET + 1) : OPR_SET; assert(NUM_OPRS == (OPR_SET + 1)); assert(ARRAYSIZE(oprname) == (OPR_SET + 2)); set_kill_bitmask = OPR_SETKILL; for (oprtype = oprstart; oprtype < oprend; oprtype++) { assert((OPR_KILL == oprtype) || (OPR_SET == oprtype)); if (OPR_KILL == oprtype) { tmp_matched_set = FALSE; tmp_matched_kill = TRUE; tmp_index = kill_index; is_set = FALSE; if (0 != set_index) { /* SET & KILL triggers are separate. This is the KILL trigger only invocation */ assert(set_cmp); assert(kill_cmp); set_kill_bitmask = OPR_KILL; } opname = oprname[OPR_KILL]; } else { tmp_matched_set = db_matched_set; tmp_matched_kill = db_matched_kill; tmp_index = set_index; is_set = TRUE; if (OPR_KILL == oprstart) { assert(set_cmp); assert(kill_cmp); set_kill_bitmask = OPR_SET; } opname = oprname[OPR_SET]; } updates = modify_record(trigvn, trigvn_len, add_delete, tmp_index, values, value_len, trigger_count, tmp_matched_set, tmp_matched_kill, kill_trigger_hash, set_trigger_hash, set_kill_bitmask); if (0 > updates) { switch (updates) { case INVALID_LABEL: UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); util_out_print_gtmio("Error : Current trigger format not compatible to update " \ "the trigger on ^!AD named !AD", FLUSH, disp_trigvn_len, disp_trigvn, trigname[oprtype]->str.len, trigname[oprtype]->str.addr); RETURN_AND_POP_MVALS(STATS_ERROR_TRIGFILE); case KEY_TOO_LONG: UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); util_out_print_gtmio("Error : ^!AD trigger - key larger than max key size", FLUSH, disp_trigvn_len, disp_trigvn); RETURN_AND_POP_MVALS(STATS_ERROR_TRIGFILE); case VAL_TOO_LONG: UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); util_out_print_gtmio("Error : ^!AD trigger - value larger than record size", FLUSH, disp_trigvn_len, disp_trigvn); RETURN_AND_POP_MVALS(STATS_ERROR_TRIGFILE); case K_ZTK_CONFLICT: UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); util_out_print_gtmio("Error : Command options !AD incompatible with trigger on " \ "^!AD named !AD", FLUSH, value_len[CMD_SUB], values[CMD_SUB], disp_trigvn_len, disp_trigvn, trigname[oprtype]->str.len, trigname[oprtype]->str.addr); RETURN_AND_POP_MVALS(STATS_ERROR_TRIGFILE); case ADD_SET_NOCHNG_KILL_TRIG: assert(!is_set); UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); util_out_print_gtmio("!AZ trigger on ^!AD already present in trigger named !AD" \ " - no action taken", FLUSH, opname, disp_trigvn_len, disp_trigvn, trigname[oprtype]->str.len, trigname[oprtype]->str.addr); /* kill trigger is unchanged but set trigger (if present in a different trigger) * needs to be processed separately. */ break; case ADD_SET_MODIFY_KILL_TRIG: assert(!is_set); UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); util_out_print_gtmio("Modified !AZ trigger on ^!AD named !AD", FLUSH, opname, disp_trigvn_len, disp_trigvn, trigname[oprtype]->str.len, trigname[oprtype]->str.addr); trig_stats[STATS_MODIFIED]++; trigload_status = STATS_NOERROR_TRIGFILE; break; case OPTIONS_CMDS_CONFLICT: UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); util_out_print_gtmio("Error : Specified options and commands cannot both be different" \ " from those in trigger on ^!AD named !AD", FLUSH, disp_trigvn_len, disp_trigvn, trigname[oprtype]->str.len, trigname[oprtype]->str.addr); RETURN_AND_POP_MVALS(STATS_ERROR_TRIGFILE); case NAME_CMDS_CONFLICT: UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); util_out_print_gtmio("Error : Specified name !AD different from that of trigger" \ " on ^!AD named !AD but specified commands do not contain those in trigger", FLUSH, value_len[TRIGNAME_SUB], values[TRIGNAME_SUB], disp_trigvn_len, disp_trigvn, trigname[oprtype]->str.len, trigname[oprtype]->str.addr); RETURN_AND_POP_MVALS(STATS_ERROR_TRIGFILE); default: assertpro(FALSE && updates); break; } } else { skip_set_trigger = is_set; if ((updates & (ADD_UPDATE_NAME | ADD_UPDATE_CMDS | ADD_UPDATE_OPTIONS)) || (updates & (SUB_UPDATE_NAME | SUB_UPDATE_CMDS))) { trig_stats[STATS_MODIFIED]++; trigload_status = STATS_NOERROR_TRIGFILE; if (0 == trig_stats[STATS_ERROR_TRIGFILE]) { if (-1 == kill_index) opname = (!set_cmp ? oprname[OPR_KILL] : (!kill_cmp ? oprname[OPR_SET] : oprname[OPR_SETKILL])); UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); util_out_print_gtmio("Modified !AZ trigger on ^!AD named !AD", FLUSH, opname, disp_trigvn_len, disp_trigvn, trigname[oprtype]->str.len, trigname[oprtype]->str.addr); } } else if (updates & DELETE_REC) { trig_stats[STATS_DELETED]++; trigload_status = STATS_NOERROR_TRIGFILE; if (0 == trig_stats[STATS_ERROR_TRIGFILE]) { if (-1 == kill_index) opname = (!set_cmp ? oprname[OPR_KILL] : (!kill_cmp ? oprname[OPR_SET] : oprname[OPR_SETKILL])); UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); util_out_print_gtmio("Deleted !AZ trigger on ^!AD named !AD", FLUSH, opname, disp_trigvn_len, disp_trigvn, trigname[oprtype]->str.len, trigname[oprtype]->str.addr); } /* if KILL trigger deleted, search for possible new SET trigger index */ if (!is_set && (kill_index < set_index) && !(trigger_already_exists(trigvn, trigvn_len, values, value_len, set_trigger_hash, kill_trigger_hash, &set_index, &tmp_index, &db_matched_set, &db_matched_kill, &full_match, trigname[oprtype], trigname[oprtype]))) { /* SET trigger found previously is not found again */ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); util_out_print_gtmio("Error : Previously found SET trigger " \ "on ^!AD, named !AD but cannot find it again", FLUSH, disp_trigvn_len, disp_trigvn, trigname[oprtype]->str.len, trigname[oprtype]->str.addr); RETURN_AND_POP_MVALS(STATS_ERROR_TRIGFILE); } } else if ('+' == add_delete) { assert(0 == updates); if (0 == trig_stats[STATS_ERROR_TRIGFILE]) { if (-1 == kill_index) opname = (!set_cmp ? oprname[OPR_KILL] : (!kill_cmp ? oprname[OPR_SET] : oprname[OPR_SETKILL])); UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); util_out_print_gtmio("!AZ trigger on ^!AD already present " \ "in trigger named !AD - no action taken", FLUSH, opname, disp_trigvn_len, disp_trigvn, trigname[oprtype]->str.len, trigname[oprtype]->str.addr); } } else { assert(0 == updates); if (0 == trig_stats[STATS_ERROR_TRIGFILE]) { if (-1 == kill_index) opname = (!set_cmp ? oprname[OPR_KILL] : (!kill_cmp ? oprname[OPR_SET] : oprname[OPR_SETKILL])); UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); if (!value_len[TRIGNAME_SUB] || ((trigname[oprtype]->str.len == value_len[TRIGNAME_SUB]) && !memcmp(values[TRIGNAME_SUB], trigname[oprtype]->str.addr, value_len[TRIGNAME_SUB]))) { /* Trigger name matches input name or name was not specified (in which * case name is considered to match). So the command specified * does not exist for deletion. */ util_out_print_gtmio("!AZ trigger on ^!AD not present in trigger " \ "named !AD - no action taken", FLUSH, opname, disp_trigvn_len, disp_trigvn, trigname[oprtype]->str.len, trigname[oprtype]->str.addr); } else { util_out_print_gtmio("!AZ trigger on ^!AD matches trigger " \ "named !AD but not with specified name !AD " \ "- no action taken", FLUSH, opname, disp_trigvn_len, disp_trigvn, trigname[oprtype]->str.len, trigname[oprtype]->str.addr, value_len[TRIGNAME_SUB], values[TRIGNAME_SUB]); } } } } } if (0 == set_index) { if (set_cmp) { /* SET was specified in the CMD list but no trigger was found, so treat this * as a trigger not existing case for both '+' and '-' cases of add_delete. */ trigger_exists = FALSE; assert(!newtrigger); } else if (-1 != kill_index) skip_set_trigger = TRUE; } } if (newtrigger || !trigger_exists) { if ('-' == add_delete) { if (0 == trig_stats[STATS_ERROR_TRIGFILE]) { if (newtrigger) opname = (!set_cmp ? oprname[OPR_KILL] : (!kill_cmp ? oprname[OPR_SET] : oprname[OPR_SETKILL])); else if (-1 == kill_index) opname = (!set_cmp ? oprname[OPR_KILL] : (!kill_cmp ? oprname[OPR_SET] : oprname[OPR_SETKILL])); else opname = oprname[OPR_SET]; UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); /* At this point SET or KILL or both triggers specified might not exist hence the "and/or" */ util_out_print_gtmio("!AZ trigger on ^!AD does not exist - no action taken", FLUSH, opname, disp_trigvn_len, disp_trigvn); } skip_set_trigger = TRUE; } else { if (!new_name && !new_match) { opname = (!set_cmp ? oprname[OPR_KILL] : (!kill_cmp ? oprname[OPR_SET] : oprname[OPR_SETKILL])); TRIGGER_SAME_NAME_EXISTS_ERROR(opname, disp_trigvn_len, disp_trigvn); } if (newtrigger) { POP_MV_STENT(); trig_protected_mval_push_count--; trigger_count = (mval *)&literal_one; set_index = 1; } else { assert(!trigger_exists); assert(0 == set_index); num = mval2i(trigger_count); set_index = ++num; i2mval(trigger_count, num); } } } /* Since a specified trigger name will grow by 1, copy it to a long enough array */ if (((updates & ADD_UPDATE_NAME) && ('+' == add_delete)) || !skip_set_trigger) { memcpy(trig_name, values[TRIGNAME_SUB], value_len[TRIGNAME_SUB] + 1); values[TRIGNAME_SUB] = trig_name; result = gen_trigname_sequence(trigvn, trigvn_len, trigger_count, values[TRIGNAME_SUB], value_len[TRIGNAME_SUB]); if (SEQ_SUCCESS != result) { if (TOO_MANY_TRIGGERS == result) { UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); util_out_print_gtmio("Error : ^!AD trigger - Too many triggers", FLUSH, disp_trigvn_len, disp_trigvn); } else if (AUTO_NAME_GEN_FAIL == result) { UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); util_out_print_gtmio("Error : ^!AD trigger - Auto generated name failure", FLUSH, disp_trigvn_len, disp_trigvn); } else { TOO_LONG_REC_KEY_ERROR_MSG; } RETURN_AND_POP_MVALS(STATS_ERROR_TRIGFILE); } } if (trig_stats[STATS_ERROR_TRIGFILE]) { if ('+' == add_delete) { trig_stats[STATS_ADDED]++; trigload_status = STATS_NOERROR_TRIGFILE; } UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); util_out_print_gtmio("No errors processing trigger for global ^!AD", FLUSH, disp_trigvn_len, disp_trigvn); } else if (!skip_set_trigger) { if (!newtrigger && (-1 != kill_index)) { /* KILL commands were separately processed in KILL trigger. So consider only SET as being specified */ value_len[CMD_SUB] = 1; values[CMD_SUB][1] = '\0'; assert('S' == values[CMD_SUB][0]); } value_len[TRIGNAME_SUB] = STRLEN(values[TRIGNAME_SUB]); values[CHSET_SUB] = (gtm_utf8_mode) ? UTF8_NAME : LITERAL_M; value_len[CHSET_SUB] = STRLEN(values[CHSET_SUB]); /* set ^#t(GVN,"#LABEL") = HASHT_GBL_CURLABEL */ SET_TRIGGER_GLOBAL_SUB_SUB_STR(trigvn, trigvn_len, LITERAL_HASHLABEL, STRLEN(LITERAL_HASHLABEL), HASHT_GBL_CURLABEL, STRLEN(HASHT_GBL_CURLABEL), result); IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(result); /* set ^#t(GVN,"#COUNT") = trigger_count */ SET_TRIGGER_GLOBAL_SUB_SUB_MVAL(trigvn, trigvn_len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT), *trigger_count, result); IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(result); /* Assert that BHASH and LHASH are not part of NUM_SUBS calculation (confirms the -2 done in #define of NUM_SUBS) */ assert(BHASH_SUB == NUM_SUBS); assert(LHASH_SUB == (NUM_SUBS + 1)); for (sub_indx = 0; sub_indx < NUM_SUBS; sub_indx++) { if (0 >= value_len[sub_indx]) /* subscript index length is zero (no longer used), skip it */ continue; /* set ^#t(GVN,trigger_count,values[sub_indx]) = xecute string */ SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, *trigger_count, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), values[sub_indx], value_len[sub_indx], result); IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(result); } result = add_trigger_hash_entry(trigvn, trigvn_len, values[CMD_SUB], set_index, TRUE, kill_trigger_hash, set_trigger_hash); IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(result); MV_FORCE_UMVAL(&mv_hash, kill_trigger_hash->hash_code); SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MVAL(trigvn, trigvn_len, *trigger_count, trigger_subs[LHASH_SUB], STRLEN(trigger_subs[LHASH_SUB]), mv_hash, result); IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(result); MV_FORCE_UMVAL(&mv_hash, set_trigger_hash->hash_code); SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MVAL(trigvn, trigvn_len, *trigger_count, trigger_subs[BHASH_SUB], STRLEN(trigger_subs[BHASH_SUB]), mv_hash, result); IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(result); trigload_status = STATS_NOERROR_TRIGFILE; trig_stats[STATS_ADDED]++; UTIL_PRINT_PREFIX_IF_NEEDED(*first_gtmio, utilprefix, utilprefixlen); /* Recompute set_cmp and kill_cmp in case values[CMD_SUB] was modified above */ set_cmp = (NULL != strchr(values[CMD_SUB], 'S')); kill_cmp = ((NULL != strchr(values[CMD_SUB], 'K')) || (NULL != strchr(values[CMD_SUB], 'R'))); opname = (!set_cmp ? oprname[OPR_KILL] : (!kill_cmp ? oprname[OPR_SET] : oprname[OPR_SETKILL])); util_out_print_gtmio("Added !AZ trigger on ^!AD named !AD", FLUSH, opname, disp_trigvn_len, disp_trigvn, value_len[TRIGNAME_SUB] - 1, values[TRIGNAME_SUB]); /* -1 to remove # from tail of name */ } assert((STATS_UNCHANGED_TRIGFILE == trigload_status) || (STATS_NOERROR_TRIGFILE == trigload_status)); if ((0 == trig_stats[STATS_ERROR_TRIGFILE]) && (STATS_NOERROR_TRIGFILE == trigload_status)) { trigger_incr_cycle(trigvn, trigvn_len); /* ^#t records changed in this function, increment cycle */ csa->incr_db_trigger_cycle = TRUE; /* so that we increment csd->db_trigger_cycle at commit time */ if (dollar_ztrigger_invoked) { /* increment db_dztrigger_cycle so that next gvcst_put/gvcst_kill in this transaction, on this region, * will re-read triggers. Note that the below increment happens for every record added. So, even if a * single trigger file loaded multiple triggers on the same region, db_dztrigger_cycle will be incremented * more than one for same transaction. This is considered okay since we only need db_dztrigger_cycle to * be equal to a different value than gvt->db_dztrigger_cycle. */ csa->db_dztrigger_cycle++; DBGTRIGR((stderr, "trigupdrec_reg: dollar_ztrigger_invoked CSA->db_dztrigger_cycle=%d\n", csa->db_dztrigger_cycle)); } } RETURN_AND_POP_MVALS(trigload_status); } STATICFNDEF boolean_t trigger_update_rec_helper(mval *trigger_rec, boolean_t noprompt, uint4 *trig_stats) { enum cdb_sc cdb_status; boolean_t trigger_status; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ESTABLISH_RET(trigger_tpwrap_ch, TRIG_FAILURE); trigger_status = trigger_update_rec(trigger_rec, TRUE, trig_stats, NULL, NULL); if (TRIG_SUCCESS == trigger_status) { GVTR_OP_TCOMMIT(cdb_status); if (cdb_sc_normal != cdb_status) t_retry(cdb_status); /* won't return */ } else { /* Record cannot be committed - undo everything */ assert(donot_INVOKE_MUMTSTART); DEBUG_ONLY(donot_INVOKE_MUMTSTART = FALSE); /* Print $ztrigger/mupip-trigger output before rolling back TP */ TP_ZTRIGBUFF_PRINT; OP_TROLLBACK(-1); /* returns but kills implicit transaction */ } REVERT; return trigger_status; } boolean_t trigger_update(mval *trigger_rec) { uint4 i; uint4 trig_stats[NUM_STATS]; boolean_t trigger_status = TRIG_FAILURE; mval ts_mv; int loopcnt; DEBUG_ONLY(unsigned int lcl_t_tries;) enum cdb_sc failure; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; for (i = 0; NUM_STATS > i; i++) trig_stats[i] = 0; ts_mv.mvtype = MV_STR; ts_mv.str.len = 0; ts_mv.str.addr = NULL; if (0 == dollar_tlevel) { assert(!donot_INVOKE_MUMTSTART); DEBUG_ONLY(donot_INVOKE_MUMTSTART = TRUE); /* Note down dollar_tlevel before op_tstart. This is needed to determine if we need to break from the for-loop * below after a successful op_tcommit of the $ZTRIGGER operation. We cannot check that dollar_tlevel is zero * since the op_tstart done below can be a nested sub-transaction */ op_tstart(IMPLICIT_TSTART, TRUE, &ts_mv, 0); /* 0 ==> save no locals but RESTART OK */ /* The following for loop structure is similar to that in module trigger_trgfile.c (function * "trigger_trgfile_tpwrap") and module gv_trigger.c (function gvtr_db_tpwrap) so any changes here * might need to be reflected there as well. */ for (loopcnt = 0; ; loopcnt++) { assert(donot_INVOKE_MUMTSTART); /* Make sure still set */ DEBUG_ONLY(lcl_t_tries = t_tries); trigger_status = trigger_update_rec_helper(trigger_rec, TRUE, trig_stats); if (0 == dollar_tlevel) break; assert(0 < t_tries); assert((CDB_STAGNATE == t_tries) || (lcl_t_tries == t_tries - 1)); failure = LAST_RESTART_CODE; assert(((cdb_sc_onln_rlbk1 != failure) && (cdb_sc_onln_rlbk2 != failure)) || !gv_target || !gv_target->root); assert((cdb_sc_onln_rlbk2 != failure) || !IS_GTM_IMAGE || TREF(dollar_zonlnrlbk)); if (cdb_sc_onln_rlbk2 == failure) RTS_ERROR_CSA_ABT(gv_target->gd_csa, VARLSTCNT(1) ERR_DBROLLEDBACK); /* else if (cdb_sc_onln_rlbk1 == status) we don't need to do anything other than trying again. Since this * is ^#t global, we don't need to GVCST_ROOT_SEARCH before continuing with the next restart because the * trigger load logic already takes care of doing INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED before doing the * actual trigger load */ /* We expect the above function to return with either op_tcommit or a tp_restart invoked. * In the case of op_tcommit, we expect dollar_tlevel to be 0 and if so we break out of the loop. * In the tp_restart case, we expect a maximum of 4 tries/retries and much lesser usually. * Additionally we also want to avoid an infinite loop so limit the loop to what is considered * a huge iteration count and assertpro if that is reached as it suggests an out-of-design situation. */ assertpro(TPWRAP_HELPER_MAX_ATTEMPTS >= loopcnt); } } else { trigger_status = trigger_update_rec(trigger_rec, TRUE, trig_stats, NULL, NULL); assert(0 < dollar_tlevel); } return (TRIG_FAILURE == trigger_status); } #endif /* GTM_TRIGGER */ fis-gtm-V7.0-005/sr_unix/trigger_update_protos.h0000644000032200000250000000720114342376330020625 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2018 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. * * * ****************************************************************/ #ifndef TRIGGER_UPDATE_PROTOS_H_INCLUDED #define TRIGGER_UPDATE_PROTOS_H_INCLUDED STATICFNDEF boolean_t trigger_already_exists(char *trigvn, int trigvn_len, char **values, uint4 *value_len, /* input parm */ stringkey *set_trigger_hash, stringkey *kill_trigger_hash, /* input parm */ int *set_index, int *kill_index, boolean_t *set_cmp_result, /* output parm */ boolean_t *kill_cmp_result, boolean_t *full_match, /* output parm */ mval *setname, mval *killname); /* output parm */ STATICFNDCL int4 modify_record(char *trigvn, int trigvn_len, char add_delete, int trigger_index, char **values, uint4 *value_len, mval *trigger_count, boolean_t db_matched_set, boolean_t db_matched_kill, stringkey *kill_hash, stringkey *set_hash, int set_kill_bitmask); STATICFNDCL int4 gen_trigname_sequence(char *trigvn, uint4 trigvn_len, mval *trigger_count, char *trigname_seq_str, uint4 seq_len); STATICFNDCL int4 add_trigger_hash_entry(char *trigvn, int trigvn_len, char *cmd_value, int trigindx, boolean_t add_kill_hash, stringkey *kill_hash, stringkey *set_hash); STATICFNDCL int4 add_trigger_cmd_attributes(char *trigvn, int trigvn_len, int trigger_index, char *trig_cmds, char **values, uint4 *value_len, boolean_t db_matched_set, boolean_t db_matched_kill, stringkey *kill_hash, stringkey *set_hash, uint4 db_cmd_bm, uint4 tf_cmd_bm); STATICFNDCL int4 add_trigger_options_attributes(char *trigvn, int trigvn_len, int trigger_index, char *trig_options, char **values, uint4 *value_len); STATICFNDCL boolean_t subtract_trigger_cmd_attributes(char *trigvn, int trigvn_len, char *trig_cmds, char **values, uint4 *value_len, boolean_t set_cmp, stringkey *kill_hash, stringkey *set_hash, int trigger_index, uint4 db_cmd_bm, uint4 tf_cmd_bm); STATICFNDCL boolean_t validate_label(char *trigvn, int trigvn_len); STATICFNDCL int4 update_commands(char *trigvn, int trigvn_len, int trigger_index, char *new_trig_cmds, char *orig_db_cmds); STATICFNDCL int4 update_trigger_name(char *trigvn, int trigvn_len, int trigger_index, char *db_trig_name, char *tf_trig_name, uint4 tf_trig_name_len); STATICFNDCL boolean_t trigger_update_rec_helper(mval *trigger_rec, boolean_t noprompt, uint4 *trig_stats); boolean_t trigger_name_search(char *trigger_name, uint4 trigger_name_len, mval *val, gd_region **found_reg); boolean_t check_unique_trigger_name_full(char **values, uint4 *value_len, mval *val, boolean_t *new_match, char *trigvn, int trigvn_len, stringkey *kill_trigger_hash, stringkey *set_trigger_hash); boolean_t trigger_update_rec(mval *trigger_rec, boolean_t noprompt, uint4 *trig_stats, io_pair *trigfile_device, int4 *record_num); STATICFNDCL trig_stats_t trigupdrec_reg(char *trigvn, uint4 trigvn_len, boolean_t *jnl_format_done, mval *trigjrec, boolean_t *new_name_check_done, boolean_t *new_name_ptr, char **values, uint4 *value_len, char add_delete, stringkey *kill_trigger_hash, stringkey *set_trigger_hash, char *disp_trigvn, int disp_trigvn_len, uint4 *trig_stats, boolean_t *first_gtmio, char *utilprefix, int *utilprefixlen); boolean_t trigger_update(mval *trigger_rec); #endif fis-gtm-V7.0-005/sr_unix/trigger_upgrade.c0000644000032200000250000006510514342376335017373 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2022 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" #ifdef GTM_TRIGGER #include "gdsroot.h" /* for gdsfhead.h */ #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" /* for gdsfhead.h */ #include "gdsfhead.h" #include "gdsblk.h" #include "filestruct.h" #include "trigger.h" #include /* needed for gv_trigger.h */ #include "gv_trigger.h" #include "gtm_trigger.h" #include "gv_trigger_protos.h" #include "trigger_upgrade_protos.h" #include "tp_restart.h" #include "change_reg.h" #include "targ_alloc.h" #include "mv_stent.h" #include "op.h" #include "tp_frame.h" #include "gvcst_protos.h" #include "gvsub2str.h" #include "mvalconv.h" #include "op_tcommit.h" #include "format_targ_key.h" #include "repl_msg.h" #include "gtmsource.h" #include "gdscc.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "jnl.h" /* for jnl_format prototype and for tp.h */ #include "tp.h" /* needed for T_BEGIN_SETORKILL_NONTP_OR_TP */ #include "t_begin.h" #include "repl_sp.h" /* for F_CLOSE (used by JNL_FD_CLOSE) */ #include "gtmio.h" /* for CLOSEFILE_RESET macro */ #include "util.h" GBLREF gd_region *gv_cur_region; GBLREF uint4 dollar_tlevel; GBLREF gv_key *gv_currkey; GBLREF unsigned int t_tries; #ifdef DEBUG GBLREF boolean_t is_replicator; GBLREF boolean_t donot_INVOKE_MUMTSTART; #endif error_def(ERR_TRIGUPBADLABEL); #define LITERAL_TRIGJNLREC "; ^#t physical upgrade from #LABEL 2,3 to #LABEL 4 (no logical change)" #define LITERAL_TRIGJNLREC_LEN STR_LIT_LEN(LITERAL_TRIGJNLREC) LITDEF mval literal_trigjnlrec = DEFINE_MVAL_LITERAL(MV_STR | MV_NUM_APPROX, 0, 0, LITERAL_TRIGJNLREC_LEN, (char *)LITERAL_TRIGJNLREC, 0, 0); LITREF mval literal_batch; LITREF mval literal_curlabel; LITREF mval literal_hashlabel; LITREF mval literal_hashcycle; LITREF mval literal_hashcount; LITREF mval literal_hashtrhash; #define TRIGGER_SUBSDEF(SUBSTYPE, SUBSNAME, LITMVALNAME, TRIGFILEQUAL, PARTOFHASH) LITREF mval LITMVALNAME; #include "trigger_subs_def.h" #undef TRIGGER_SUBSDEF DEFINE_NSB_CONDITION_HANDLER(trigger_upgrade_ch) STATICFNDCL void gvtr_set_hasht_gblsubs(mval *subs_mval, mval *set_mval); STATICFNDCL void gvtr_kill_hasht_gblsubs(mval *subs_mval, boolean_t killall); STATICFNDEF void gvtr_set_hashtrhash(char *trigvn, int trigvn_len, uint4 hash_code, int trigindx); STATICFNDEF void gvtr_set_hasht_gblsubs(mval *subs_mval, mval *set_mval) { uint4 curend; boolean_t was_null = FALSE, is_null = FALSE; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; curend = gv_currkey->end; /* note down gv_currkey->end before changing it so we can restore it before function returns */ assert(KEY_DELIMITER == gv_currkey->base[curend]); assert(gv_target->gd_csa == cs_addrs); COPY_SUBS_TO_GVCURRKEY(subs_mval, gv_cur_region, gv_currkey, was_null, is_null); /* updates gv_currkey */ MV_FORCE_STR(set_mval); gvcst_put(set_mval); gv_currkey->end = curend; /* reset gv_currkey->end to what it was at function entry */ gv_currkey->base[curend] = KEY_DELIMITER; /* restore terminator for entire key so key is well-formed */ return; } STATICFNDEF void gvtr_kill_hasht_gblsubs(mval *subs_mval, boolean_t killall) { uint4 curend; boolean_t was_null = FALSE, is_null = FALSE; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; curend = gv_currkey->end; /* note down gv_currkey->end before changing it so we can restore it before function returns */ assert(KEY_DELIMITER == gv_currkey->base[curend]); assert(gv_target->gd_csa == cs_addrs); COPY_SUBS_TO_GVCURRKEY(subs_mval, gv_cur_region, gv_currkey, was_null, is_null); /* updates gv_currkey */ gvcst_kill(killall); gv_currkey->end = curend; /* reset gv_currkey->end to what it was at function entry */ gv_currkey->base[curend] = KEY_DELIMITER; /* restore terminator for entire key so key is well-formed */ return; } /* Set ^#t(,"#TRHASH",hash_code,nnn)=_$c(0)_trigindx where gv_currkey is ^#t(). * Note: This routine has code very similar to that in "add_trigger_hash_entry". There is just * not enough commonality to justify merging the two. */ STATICFNDEF void gvtr_set_hashtrhash(char *trigvn, int trigvn_len, uint4 hash_code, int trigindx) { uint4 curend; mval mv_indx, *mv_indx_ptr; mval mv_hash; int hash_indx, num_len; char name_and_index[MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT]; char *ptr; char indx_str[MAX_DIGITS_IN_INT]; uint4 len; int4 result; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; curend = gv_currkey->end; /* note down gv_currkey->end before changing it so we can restore it before function returns */ assert(KEY_DELIMITER == gv_currkey->base[curend]); assert(gv_target->gd_csa == cs_addrs); /* Set ^#t(,"#TRHASH",kill_hash_code,i). To do that, first determine the * highest i such that ^#t(,"#TRHASH",kill_hash_code,i) exists. Set * ^#t(,"#TRHASH",kill_hash_code,i+1)=kill_hash_code in that case. */ MV_FORCE_UMVAL(&mv_hash, hash_code); BUILD_HASHT_SUB_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, "", 0); op_zprevious(&mv_indx); mv_indx_ptr = &mv_indx; hash_indx = (0 == mv_indx.str.len) ? 1 : (mval2i(mv_indx_ptr) + 1); assert(0 <= hash_indx); i2mval(mv_indx_ptr, hash_indx); MV_FORCE_STR(mv_indx_ptr); /* Prepare the value of the SET */ num_len = 0; I2A(indx_str, num_len, trigindx); assert(MAX_MIDENT_LEN >= trigvn_len); memcpy(name_and_index, trigvn, trigvn_len); ptr = name_and_index + trigvn_len; *ptr++ = '\0'; memcpy(ptr, indx_str, num_len); len = trigvn_len + 1 + num_len; /* Do the SET */ SET_TRIGGER_GLOBAL_SUB_SUB_MSUB_MSUB_STR(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_indx, name_and_index, len, result); assert(PUT_SUCCESS == result); gv_currkey->end = curend; /* reset gv_currkey->end to what it was at function entry */ gv_currkey->base[curend] = KEY_DELIMITER; /* restore terminator for entire key so key is well-formed */ return; } /* Upgrade ^#t global in "reg" region */ void trigger_upgrade(gd_region *reg) { boolean_t est_first_pass, do_upgrade, is_defined; boolean_t was_null = FALSE, is_null = FALSE; int seq_num, trig_seq_num; int currlabel; mval tmpmval, xecuteimval, *gvname, *tmpmv, *tmpmv2; int4 result, tmpint4; uint4 curend, gvname_prev, xecute_curend; uint4 hash_code, kill_hash_code; int count, i, xecutei, tncount; char *trigname, *trigindex, *ptr; char name_and_index[MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT]; char trigvn[MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT], nullbyte[1]; uint4 trigname_len, name_index_len; int ilen; sgmnt_addrs *csa; jnl_private_control *jpc; uint4 sts; int close_res; hash128_state_t hash_state, kill_hash_state; uint4 hash_totlen, kill_hash_totlen; int trig_protected_mval_push_count; char buff[OUT_BUFF_SIZE]; # ifdef DEBUG int save_dollar_tlevel; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(gv_cur_region == reg); assert(!dollar_tlevel); /* caller should have ensured this. this is needed as otherwise things get complicated. */ assert(!is_replicator); /* caller should have ensured this. this is needed so we dont bump jnl_seqno (if replicating) */ csa = &FILE_INFO(reg)->s_addrs; assert(csa->hdr->hasht_upgrade_needed); /* If before-image journaling is turned on in this region (does not matter if replication is turned on or not), * once this transaction is done, we need to switch to new journal file and cut the back link because * otherwise it is possible for backward journal recovery (or rollback) or source server to encounter * the journal records generated in this ^#t-upgrade-transaction in which case they dont know to handle * it properly (e.g. rollback or backward recovery does not know to restore csa->hdr->hasht_upgrade_needed * if it rolls back this transaction). To achieve this, we set hold_onto_crit to TRUE and do the jnl link * cut AFTER the transaction commits but before anyone else can sneak in to do any more updates. * Since most often we expect databases to be journaled, we do this hold_onto_crit even for the non-journaled case. */ grab_crit(reg, WS_100); csa->hold_onto_crit = TRUE; DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); assert(!donot_INVOKE_MUMTSTART); DEBUG_ONLY(donot_INVOKE_MUMTSTART = TRUE); op_tstart(IMPLICIT_TSTART, TRUE, &literal_batch, 0); /* 0 ==> save no locals but RESTART OK */ ESTABLISH_NORET(trigger_upgrade_ch, est_first_pass); /* On a TP restart anywhere down below, this line is where the restart resumes execution from */ assert(donot_INVOKE_MUMTSTART); /* Make sure still set for every try/retry of TP transaction */ change_reg(); /* TP_CHANGE_REG wont work as we need to set sgm_info_ptr */ assert(NULL != cs_addrs); assert(csa == cs_addrs); SET_GVTARGET_TO_HASHT_GBL(csa); /* sets up gv_target */ assert(NULL != gv_target); INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED; /* Needed to do every retry in case restart was due to an online rollback. * This also sets up gv_currkey */ /* Do actual upgrade of ^#t global. * * Below is a sample layout of the label 2 ^#t global * ------------------------------------------------------- * ^#t("#TNAME","x")="a"_$C(0)_"1" (present in DEFAULT only) * ^#t("#TRHASH",89771515,1)="a"_$C(0)_"1" (present in DEFAULT only) * ^#t("#TRHASH",106937755,1)="a"_$C(0)_"1" (present in DEFAULT only) * ^#t("a",1,"BHASH")="106937755" * ^#t("a",1,"CHSET")="M" * ^#t("a",1,"CMD")="S" * ^#t("a",1,"LHASH")="89771515" * ^#t("a",1,"TRIGNAME")="x#" * ^#t("a",1,"XECUTE")=" do ^twork" * ^#t("a","#COUNT")="1" * ^#t("a","#CYCLE")="1" * ^#t("a","#LABEL")="2" * * Below is a sample layout of the label 3 ^#t global * ------------------------------------------------------- * ^#t("#LABEL")="3" (present only after upgrade, not regular trigger load) * ^#t("#TNAME","x")="a"_$C(0)_"1" (present in CURRENT region) * ^#t("a",1,"BHASH")="71945627" * ^#t("a",1,"CHSET")="M" * ^#t("a",1,"CMD")="S" * ^#t("a",1,"LHASH")="71945627" * ^#t("a",1,"TRIGNAME")="x#" * ^#t("a",1,"XECUTE")=" do ^twork" * ^#t("a","#COUNT")="1" * ^#t("a","#CYCLE")="2" * ^#t("a","#LABEL")="3" * ^#t("a","#TRHASH",71945627,1)="a"_$C(0)_"1" * * Key aspects of the format change * ---------------------------------- * 1) New ^#t("#LABEL")="3" to indicate the format of the ^#t global. This is in addition to * ^#t("a","#LABEL") etc. which is already there. This way we have a #LABEL for not just the installed * triggers but also for the name information stored in the #TNAME nodes. * 2) In the BHASH and LHASH fields. The hash computation is different so there are more chances of BHASH and LHASH * matching in which case we store only one #TRHASH entry (instead of two). So thre is fewer ^#t records in the new * format in most cases. * 3) ^#t("a","#LABEL") bumps from 2 to 3. Similarly ^#t("a","#CYCLE") bumps by one (to make sure triggers for this * global get re-read if and when we implement an -ONLINE upgrade). * 4) DEFAULT used to have ^#t("#TNAME",...) nodes corresponding to triggers across ALL regions in the gbldir and * other regions used to have NO ^#t("#TNAME",...) nodes whereas after the upgrade every region have * ^#t("#TNAME",...) nodes corresponding to triggers installed in that region. So it is safer to kill ^#t("#TNAME") * nodes and add them as needed. * 5) #TRHASH has moved from ^#t() to ^#t(). So it is safer to kill ^#t("#TRHASH") nodes and add them as needed. * * Below is a sample layout of the label 4 ^#t global * ------------------------------------------------------- * ^#t("#TNAME","x")="a"_$C(0)_"1" (present in CURRENT region) * ^#t("a",1,"BHASH")="71945627" * ^#t("a",1,"CHSET")="M" * ^#t("a",1,"CMD")="S" * ^#t("a",1,"LHASH")="71945627" * ^#t("a",1,"TRIGNAME")="x#" * ^#t("a",1,"XECUTE")=" do ^twork" * ^#t("a","#COUNT")="1" * ^#t("a","#CYCLE")="2" * ^#t("a","#LABEL")="4" * ^#t("a","#TRHASH",71945627,1)="a"_$C(0)_"1" * * Key aspects of the format change * ---------------------------------- * 1) Removed ^#t("#LABEL") as it is redundant information and trigger load does not include it * 2) Multiline triggers were incorrectly processed resulting in incorrect BHASH and LHASH values. Upgrade fixes this * 3) ^#t("a","#LABEL") bumps from 3 to 4. Similarly ^#t("a","#CYCLE") bumps by one (to make sure * triggers for this global get re-read if and when we implement an -ONLINE upgrade). */ tmpmv = &tmpmval; /* At all points maintain this relationship. The two are used interchangeably below */ if (gv_target->root) do_upgrade = TRUE; else do_upgrade = FALSE; /* The below logic assumes ^#t global does not have any integrity errors */ assert(do_upgrade); /* caller should have not invoked us otherwise */ if (do_upgrade) { /* kill ^#t("#TRHASH"), ^#t("#TNAME") and ^#t("#LABEL") first. Regenerate each again as we process ^#t(,...) */ csa->incr_db_trigger_cycle = TRUE; /* so that we increment csd->db_trigger_cycle at commit time. * this forces concurrent processes to read upgraded triggers. */ if (JNL_WRITE_LOGICAL_RECS(csa)) { /* Note that the ^#t upgrade is a physical layout change. But it has no logical change (i.e. users * see the same MUPIP TRIGGER -SELECT output as before). So write only a dummy LGTRIG journal * record for this operation. Hence write a string that starts with a trigger comment character ";". */ assert(!gv_cur_region->read_only); jnl_format(JNL_LGTRIG, NULL, (mval *)&literal_trigjnlrec, 0); } /* KILL ^#t("#LABEL") unconditionally */ BUILD_HASHT_SUB_CURRKEY(LITERAL_HASHLABEL, STRLEN(LITERAL_HASHLABEL)); if (0 != gvcst_data()) gvcst_kill(TRUE); /* KILL ^#t("#TNAME") unconditionally and regenerate */ BUILD_HASHT_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME)); if (0 != gvcst_data()) gvcst_kill(TRUE); /* KILL ^#t("#TRHASH") unconditionally and regenerate */ BUILD_HASHT_SUB_CURRKEY(LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH)); if (0 != gvcst_data()) gvcst_kill(TRUE); /* Loop through all global names for which ^#t() exists. The only first-level subscripts of ^#t starting * with # are #TNAME and #TRHASH in collation order. So after #TRHASH we expect to find subscripts that are * global names. Hence the HASHTRHASH code is placed AFTER the HASHTNAME code above. */ TREF(gd_targ_gvnh_reg) = NULL; /* needed so op_gvorder below goes through gvcst_order (i.e. focuses only * on the current region) and NOT through gvcst_spr_order (which does not * apply anyways in the case of ^#t). */ nullbyte[0] = '\0'; trig_protected_mval_push_count = 0; INCR_AND_PUSH_MV_STENT(gvname); /* Protect gvname from garbage collection */ do { op_gvorder(gvname); if (0 == gvname->str.len) break; assert(ARRAYSIZE(trigvn) > gvname->str.len); memcpy(&trigvn[0], gvname->str.addr, gvname->str.len); gvname->str.addr = &trigvn[0]; /* point away from stringpool to avoid stp_gcol issues */ /* Save gv_currkey->prev so it is restored before next call to op_gvorder (which cares about this field). * gv_currkey->prev gets tampered with in the for loop below (e.g. BUILD_HASHT_SUB_CURRKEY macro). * No need to do this for gv_currkey->end since the body of the for loop takes care of restoring it. */ gvname_prev = gv_currkey->prev; BUILD_HASHT_SUB_CURRKEY(gvname->str.addr, gvname->str.len); /* At this point, gv_currkey is ^#t() */ /* Increment ^#t(,"#CYCLE") */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_hashcycle, tmpmv); assert(is_defined); tmpint4 = mval2i(tmpmv); assert(0 <= tmpint4); tmpint4++; i2mval(tmpmv, tmpint4); gvtr_set_hasht_gblsubs((mval *)&literal_hashcycle, tmpmv); /* Read ^#t(,"#COUNT") */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_hashcount, tmpmv); if (is_defined) { tmpint4 = mval2i(tmpmv); assert(0 <= tmpint4); count = tmpint4; /* Get ^#t(,"#LABEL"), error out for invalid values. Upgrade disallowed for label 1 triggers */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_hashlabel, tmpmv); assert(is_defined); currlabel = mval2i(tmpmv); assert(0 <= currlabel); if ((V19_HASHT_GBL_LABEL_INT >= currlabel) || (HASHT_GBL_CURLABEL_INT <= currlabel)) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(8) ERR_TRIGUPBADLABEL, 6, currlabel, HASHT_GBL_CURLABEL_INT, gvname->str.len, gvname->str.addr, REG_LEN_STR(reg)); /* Set ^#t(,"#LABEL")=HASHT_GBL_CURLABEL */ gvtr_set_hasht_gblsubs((mval *)&literal_hashlabel, (mval *)&literal_curlabel); } else count = 0; /* Kill ^#t(,"#TRHASH") unconditionally and regenerate */ gvtr_kill_hasht_gblsubs((mval *)&literal_hashtrhash, TRUE); /* At this point, gv_currkey is ^#t() */ for (i = 1; i <= count; i++) { /* At this point, gv_currkey is ^#t() */ curend = gv_currkey->end; /* note gv_currkey->end before changing it so we can restore it later */ assert(KEY_DELIMITER == gv_currkey->base[curend]); assert(gv_target->gd_csa == cs_addrs); i2mval(tmpmv, i); COPY_SUBS_TO_GVCURRKEY(tmpmv, gv_cur_region, gv_currkey, was_null, is_null); /* At this point, gv_currkey is ^#t(,i) */ /* Compute new LHASH and BHASH hash values. * LHASH uses : GVSUBS, XECUTE * BHASH uses : GVSUBS, DELIM, ZDELIM, PIECES, XECUTE * So reach each of these pieces and compute hash along the way. */ STR_PHASH_INIT(hash_state, hash_totlen); STR_PHASH_PROCESS(hash_state, hash_totlen, gvname->str.addr, gvname->str.len); STR_PHASH_PROCESS(hash_state, hash_totlen, nullbyte, 1); /* Read in ^#t(,i,"GVSUBS") */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_gvsubs, tmpmv); if (is_defined) { STR_PHASH_PROCESS(hash_state, hash_totlen, tmpmval.str.addr, tmpmval.str.len); STR_PHASH_PROCESS(hash_state, hash_totlen, nullbyte, 1); } /* Copy over SET hash state (2-tuple ) to KILL hash state before adding * the PIECES, DELIM, ZDELIM portions (those are only part of the SET hash). */ kill_hash_state = hash_state; kill_hash_totlen = hash_totlen; /* Read in ^#t(,i,"PIECES") */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_pieces, tmpmv); if (is_defined) { STR_PHASH_PROCESS(hash_state, hash_totlen, tmpmval.str.addr, tmpmval.str.len); STR_PHASH_PROCESS(hash_state, hash_totlen, nullbyte, 1); } /* Read in ^#t(,i,"DELIM") */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_delim, tmpmv); if (is_defined) { STR_PHASH_PROCESS(hash_state, hash_totlen, tmpmval.str.addr, tmpmval.str.len); STR_PHASH_PROCESS(hash_state, hash_totlen, nullbyte, 1); } /* Read in ^#t(,i,"ZDELIM") */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_zdelim, tmpmv); if (is_defined) { STR_PHASH_PROCESS(hash_state, hash_totlen, tmpmval.str.addr, tmpmval.str.len); STR_PHASH_PROCESS(hash_state, hash_totlen, nullbyte, 1); } /* Read in ^#t(,i,"XECUTE"). * Note: The XECUTE portion of the trigger definition is used in SET and KILL hash. * But since we have started maintaining "hash_state" and "kill_hash_state" separately * (due to PIECES, DELIM, ZDELIM) we need to update the hash for both using same input string. */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_xecute, tmpmv); if (is_defined) { STR_PHASH_PROCESS(hash_state, hash_totlen, tmpmval.str.addr, tmpmval.str.len); STR_PHASH_PROCESS(kill_hash_state, kill_hash_totlen, tmpmval.str.addr, tmpmval.str.len); } else { /* Multi-record XECUTE string */ /* At this point, gv_currkey is ^#t(,i) */ xecute_curend = gv_currkey->end; /* note gv_currkey->end so we can restore it later */ assert(KEY_DELIMITER == gv_currkey->base[xecute_curend]); tmpmv2 = (mval *)&literal_xecute; COPY_SUBS_TO_GVCURRKEY(tmpmv2, gv_cur_region, gv_currkey, was_null, is_null); xecutei = 1; do { i2mval(&xecuteimval, xecutei); is_defined = gvtr_get_hasht_gblsubs(&xecuteimval, tmpmv); if (!is_defined) break; STR_PHASH_PROCESS(hash_state, hash_totlen, tmpmval.str.addr, tmpmval.str.len); STR_PHASH_PROCESS(kill_hash_state, kill_hash_totlen, tmpmval.str.addr, tmpmval.str.len); xecutei++; } while (TRUE); /* Restore gv_currkey to ^#t(,i) */ gv_currkey->end = xecute_curend; gv_currkey->base[xecute_curend] = KEY_DELIMITER; } STR_PHASH_RESULT(hash_state, hash_totlen, hash_code); STR_PHASH_RESULT(kill_hash_state, kill_hash_totlen, kill_hash_code); /* Set ^#t(,i,"LHASH") */ MV_FORCE_UMVAL(tmpmv, kill_hash_code); gvtr_set_hasht_gblsubs((mval *)&literal_lhash, tmpmv); /* Set ^#t(,i,"BHASH") */ MV_FORCE_UMVAL(tmpmv, hash_code); gvtr_set_hasht_gblsubs((mval *)&literal_bhash, tmpmv); /* Read in ^#t(,i,"TRIGNAME") to determine if #SEQNUM/#TNCOUNT needs to be maintained */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_trigname, tmpmv); assert(is_defined); assert('#' == tmpmval.str.addr[tmpmval.str.len - 1]); tmpmval.str.len--; if ((tmpmval.str.len <= ARRAYSIZE(name_and_index)) && (NULL != (ptr = memchr(tmpmval.str.addr, '#', tmpmval.str.len)))) { /* Auto-generated name. Need to maintain #SEQNUM/#TNCOUNT */ /* Take copy of trigger name into non-stringpool location to avoid stp_gcol issues */ trigname_len = ptr - tmpmval.str.addr; ptr++; name_index_len = (tmpmval.str.addr + tmpmval.str.len) - ptr; assert(ARRAYSIZE(name_and_index) >= (trigname_len + 1 + name_index_len)); trigname = &name_and_index[0]; trigindex = ptr; memcpy(trigname, tmpmval.str.addr, tmpmval.str.len); A2I(ptr, ptr + name_index_len, trig_seq_num); assert(0 < trig_seq_num); /* Auto generated seqnum in the DB must be valid */ /* At this point, gv_currkey is ^#t(,i) */ /* $get(^#t("#TNAME",,"#SEQNUM")) */ BUILD_HASHT_SUB_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), trigname, trigname_len, LITERAL_HASHSEQNUM, STR_LIT_LEN(LITERAL_HASHSEQNUM)); seq_num = gvcst_get(tmpmv) ? mval2i(tmpmv) : 0; assert(0 <= seq_num); if (trig_seq_num > seq_num) { /* Set ^#t("#TNAME",,"#SEQNUM") = trig_seq_num */ SET_TRIGGER_GLOBAL_SUB_SUB_SUB_STR(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), trigname, trigname_len, LITERAL_HASHSEQNUM, STR_LIT_LEN(LITERAL_HASHSEQNUM), trigindex, name_index_len, result); assert(PUT_SUCCESS == result); } /* set ^#t("#TNAME",,"#TNCOUNT")++ */ BUILD_HASHT_SUB_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), trigname, trigname_len, LITERAL_HASHTNCOUNT, STR_LIT_LEN(LITERAL_HASHTNCOUNT)); tncount = gvcst_get(tmpmv) ? mval2i(tmpmv) + 1 : 1; assert(0 < tncount); i2mval(tmpmv, tncount); SET_TRIGGER_GLOBAL_SUB_SUB_SUB_MVAL(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), trigname, trigname_len, LITERAL_HASHTNCOUNT, STR_LIT_LEN(LITERAL_HASHTNCOUNT), tmpmval, result); trigname_len += 1 + name_index_len; /* in preparation for ^#t("#TNAME") set below */ assert(PUT_SUCCESS == result); BUILD_HASHT_SUB_CURRKEY(gvname->str.addr, gvname->str.len); /* At this point, gv_currkey is ^#t() */ } else { /* Take copy of trigger name into non-stringpool location to avoid stp_gcol issues */ trigname = &name_and_index[0]; /* in preparation for ^#t("#TNAME") set below */ trigname_len = MIN(tmpmval.str.len, ARRAYSIZE(name_and_index)); assert(ARRAYSIZE(name_and_index) >= trigname_len); memcpy(trigname, tmpmval.str.addr, trigname_len); /* Restore gv_currkey to what it was at beginning of for loop iteration */ gv_currkey->end = curend; gv_currkey->base[curend] = KEY_DELIMITER; } /* At this point, gv_currkey is ^#t() */ if (kill_hash_code != hash_code) gvtr_set_hashtrhash(gvname->str.addr, gvname->str.len, kill_hash_code, i); /* Set ^#t(,"#TRHASH",hash_code,i) */ gvtr_set_hashtrhash(gvname->str.addr, gvname->str.len, hash_code, i); /* Set ^#t("#TNAME",)=_$c(0)_ */ /* The upgrade assumes that the region does not contain two triggers with the same name. * V62000 and before could potentially have this out of design case. Once implemented * the trigger integrity check will warn users of this edge case */ ptr = &trigvn[gvname->str.len]; *ptr++ = '\0'; ilen = 0; I2A(ptr, ilen, i); ptr += ilen; assert(ptr <= ARRAYTOP(trigvn)); SET_TRIGGER_GLOBAL_SUB_SUB_STR(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), trigname, trigname_len, trigvn, ptr - gvname->str.addr, result); assert(PUT_SUCCESS == result); BUILD_HASHT_SUB_CURRKEY(gvname->str.addr, gvname->str.len); /* At this point, gv_currkey is ^#t() */ } /* At this point, gv_currkey is ^#t() i.e. gv_currkey->end is correct but gv_currkey->prev * might have been tampered with. Restore it to proper value first. */ gv_currkey->prev = gvname_prev; gvname->mvtype = 0; /* can now be garbage collected in the next iteration */ } while (TRUE); } op_tcommit(); REVERT; /* remove our condition handler */ DEBUG_ONLY(donot_INVOKE_MUMTSTART = FALSE;) if (csa->hold_onto_crit) { assert(csa->now_crit); /* we should still hold crit */ /* Switch to new journal file and cut previous link if we did ^#t upgrade on a journaled region */ if (do_upgrade && JNL_WRITE_LOGICAL_RECS(csa)) { sts = set_jnl_file_close(); assert(SS_NORMAL == sts); /* because we should have done jnl_ensure_open already * in which case set_jnl_file_close has no way of erroring out. */ sts = jnl_file_open_switch(reg, 0, buff, OUT_BUFF_SIZE); if (sts) { jpc = csa->jnl; if (NOJNL != jpc->channel) JNL_FD_CLOSE(jpc->channel, close_res); /* sets jpc->channel to NOJNL */ assert(NOJNL == jpc->channel); jnl_send_oper(jpc, sts); } } csa->hold_onto_crit = FALSE; } else grab_crit(reg, WS_101); csa->hdr->hasht_upgrade_needed = FALSE; rel_crit(reg); return; /* if any errors happen during upgrade (e.g. TRANS2BIG), we will not reach here */ } #endif /* GTM_TRIGGER */ fis-gtm-V7.0-005/sr_unix/trigger_upgrade_protos.h0000644000032200000250000000110214342376330020764 0ustar librarygtc/**************************************************************** * * * Copyright 2014 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. * * * ****************************************************************/ #ifndef TRIGGER_UPGRADE_PROTOS_H_INCLUDED #define TRIGGER_UPGRADE_PROTOS_H_INCLUDED void trigger_upgrade(gd_region *reg); #endif fis-gtm-V7.0-005/sr_unix/trigger_user_name.c0000644000032200000250000001116514342376335017717 0ustar librarygtc/**************************************************************** * * * Copyright 2010, 2014 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 "gtm_ctype.h" #include "gtm_string.h" #include #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gv_trigger.h" #include "trigger.h" #include "trigger_user_name.h" #define NOLENGTH -1 /* A quick heuristic to determine if the trigger name is user defined or auto * generated. Detect an interior (not at the end) # sign in the subject string * This is intended for use on valid trigger names, do not use this for * validation */ boolean_t trigger_user_name(char *trigger_value, int trigger_value_len) { char *ptr; ptr = memchr(trigger_value, TRIGNAME_SEQ_DELIM, trigger_value_len); return ((NULL == ptr) || ((trigger_value_len - 1) == (int)(ptr - trigger_value))); } /* Function returns the length of the trigger name or the character position of * the failure. There are two valid types of names here defined by the following * PATCODES * - Delete by user defined name: ?1(1"%",1A).27(1A,1N).1(1"#",1"*") * - Delete by auto generated name: ?1(1"%",1A).20(1A,1N)1"#"1(1.6N.1"#",1"*") * Keep in mind that this function does not have any side-effects and does not strip off * a trailing # sign or wild card */ int validate_input_trigger_name(char *trigger_name, uint4 trigger_name_len, boolean_t *wildcard_ptr) { char *ptr, *tail; uint4 len, name_len, num_len, max_len; boolean_t wild, poundtail, firstdigit; if (0 == trigger_name_len) /* reject zero lengths, use -1 because returning 0 for 0 won't signal an error */ return NOLENGTH; name_len = num_len = 0; ptr = trigger_name; len = trigger_name_len; tail = ptr + (len - 1); assert (ptr >= trigger_name); if (MAX_MIDENT_LEN < trigger_name_len) /* reject strings with super long lengths */ return MAX_USER_TRIGNAME_LEN; if (!ISALPHA_ASCII(*ptr) && ('%' != *ptr)) /* first char must be alpha or '%' sign */ return INTCAST(ptr - trigger_name); name_len++; /* to record first alpha byte that has already been processed */ if ('*' == *tail) { /* strip the wild card to skip checking it */ wild = TRUE; tail--; len--; } else wild = FALSE; if (wildcard_ptr) *wildcard_ptr = wild; if (tail == ptr) /* special case to return sooner for a single character name */ return INTCAST(ptr - trigger_name) + 1 + ((wild) ? 1 : 0); if (trigger_user_name(trigger_name, trigger_name_len)) { /* user defined name, use MAX_USER_TRIGNAME_LEN as max_len */ max_len = MAX_USER_TRIGNAME_LEN; if (wild) name_len++; } else max_len = MAX_AUTO_TRIGNAME_LEN; /* auto generated name, use MAX_AUTO_TRIGNAME_LEN as max_len */ poundtail = (TRIGNAME_SEQ_DELIM == *tail); if (MAX_USER_TRIGNAME_LEN + ((poundtail) ? 1 : 0) < trigger_name_len) /* name, must be under 28 chars (MAX_USER_TRIGNAME_LEN), but increment * by one to forgive a trailing # sign, the 29th char */ return max_len; while (++ptr <= tail && TRIGNAME_SEQ_DELIM != *ptr) { if ((!ISALNUM_ASCII(*ptr)) || (max_len < ++name_len)) /* reject non-ALPHA-NUMERICS until first # sign or string end */ return INTCAST(ptr - trigger_name); if (ptr == tail) break; } assert (ptr >= trigger_name); if (tail <= ptr) /* if the above loop terminated on this we're done, add in the wild card as necessary */ return INTCAST(ptr - trigger_name) + 1 + ((wild) ? 1 : 0); if (wild) /* reject anything between the first # sign and wild card */ return INTCAST(ptr - trigger_name); firstdigit = TRUE; while ((++ptr <= tail) && (TRIGNAME_SEQ_DELIM != *ptr)) { /* validate the numeric portion of the auto generated name */ if (firstdigit) { /* reject number starting with 0 as that is not possible in auto generated name */ if ('0' == *ptr) return INTCAST(ptr - trigger_name); firstdigit = FALSE; } if ((!ISDIGIT_ASCII(*ptr)) || (NUM_TRIGNAME_SEQ_CHARS < ++num_len)) /* reject non-numeric or reject aaa#1234567 */ return INTCAST(ptr - trigger_name); if (ptr == tail) break; } if (0 == num_len) /* reject aaa## aka no numbers between adjacent '#' signs */ return INTCAST(ptr - trigger_name); assert (ptr >= trigger_name); /* anything after the second # sign, then (ptr - trigger_name) + 1 != trigger_name_len */ return INTCAST(ptr - trigger_name) + 1; } fis-gtm-V7.0-005/sr_unix/trigger_user_name.h0000644000032200000250000000130014342376330017705 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef TRIGGER_USER_NAME_H_INCLUDED #define TRIGGER_USER_NAME_H_INCLUDED boolean_t trigger_user_name(char *trigger_value, int trigger_value_len); int validate_input_trigger_name(char *trigger_name, uint4 trigger_name_len, boolean_t *wildcard_ptr); #endif fis-gtm-V7.0-005/sr_unix/trmdef.h0000755000032200000250000000125414342376330015500 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, Inc. * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #define TRM_MODIFIERS 0 #define TRM_EDITMODE 1 #define TRM_NOTYPEAHD 2 #define TRM_READSYNC 4 #define TRM_PROMPT 8 #define TRM_PASTHRU 16 #define TRM_ESCTRMOVR 32 #define TRM_ESCAPE 64 #define TRM_NOECHO 128 #define TRM_CONVERT 256 fis-gtm-V7.0-005/sr_unix/ttt.txt0000755000032200000250000006073514342376330015433 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2001, 2014 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This table defines the intermediate code (vax code) generated by an opcode. ; It drives tttgen.m to generate ttt.c. ; There are multiple versions of this file by platform family and ; changes to one should trigger review and likely changes to the others. ; The format is OC_:list of vax instructions,one to a line. ; The opcode must have a corresponding entry in opcode_def.h ; OC_ADD: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_add OC_BINDPARM: irepl val.2 calls val.1,xfer.xf_bindparm OC_BOOLFINI: incl val.1 OC_BOOLINIT: clrl val.0 OC_BREAK: jsb xfer.xf_break OC_CALL-BYTE: pushl val.2 jsb xfer.xf_callb brb jmp.1 OC_CALL-LONG: pushl val.2 jsb xfer.xf_calll jmp jmp.1 OC_CALL-WORD: pushl val.2 jsb xfer.xf_callw brw jmp.1 OC_CALLSP-BYTE: pushl val.2 jsb xfer.xf_callspb brb jmp.1 OC_CALLSP-LONG: pushl val.2 jsb xfer.xf_callspl jmp jmp.1 OC_CALLSP-WORD: pushl val.2 jsb xfer.xf_callspw brw jmp.1 OC_CAT: irepab val.2 pushab val.0 calls val.1,xfer.xf_cat OC_CLOSE: pushab val.2 pushab val.1 calls #2,xfer.xf_close OC_CLRTEST: bicb2 #1,r10 jsb xfer.xf_dt_false OC_CLRALSVARS: pushab val.1 calls #1,xfer.xf_clralsvars OC_COBOOL-MINT: tstl val.1 OC_COBOOL-MVAL: movab val.1,r1 jsb xfer.xf_mval2bool OC_COMINT-MVAL: movab val.1,r1 jsb xfer.xf_mval2mint movl r0,val.0 OC_COMMARG: pushl val.2 pushab val.1 jsb xfer.xf_commarg OC_COMVAL-MINT: movab val.0,r0 movl val.1,r1 jsb xfer.xf_mint2mval OC_CONUM: movab val.1,r1 jsb xfer.xf_mval2num OC_CONTAIN: movab val.1,r0 movab val.2,r1 jsb xfer.xf_contain OC_CURRHD: jsb xfer.xf_currhd movl r0,val.0 OC_CURRTN: movab val.0,r1 jsb xfer.xf_currtn OC_CVTPARM: pushab val.0 pushab val.2 pushl val.1 calls #3,xfer.xf_cvtparm OC_DIV: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_div OC_EQU: movab val.2,r1 movab val.1,r0 jsb xfer.xf_equ OC_EQUNUL: movab val.1,r0 jsb xfer.xf_equnul OC_EXCAL: irepab val.5 pushl val.4 pushl val.3 pushl val.2 pushl #0 jsb xfer.xf_exfun OC_EXFUN: irepab val.5 pushl val.4 pushl val.3 pushl val.2 pushab val.0 jsb xfer.xf_exfun OC_EXFUNRET: pushab val.1 calls #1,xfer.xf_exfunret OC_EXFUNRETALS: pushab val.1 calls #1,xfer.xf_exfunretals OC_EXTCALL: pushl val.2 pushl val.1 jsb xfer.xf_extcall OC_EXTEXCAL: irepab val.5 pushl val.4 pushl val.3 pushl #0 pushl val.2 pushl val.1 jsb xfer.xf_extexfun OC_EXTEXFUN: irepab val.5 pushl val.4 pushl val.3 pushab val.0 pushl val.2 pushl val.1 jsb xfer.xf_extexfun OC_EXTJMP: pushl val.2 pushl val.1 jsb xfer.xf_extjmp OC_EXP: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_exp OC_FETCH: irepl val.2 calls val.1,xfer.xf_fetch OC_FGNCAL: irepab val.6 pushl val.5 pushl val.4 pushab val.3 pushab val.2 pushl #0 calls val.1,xfer.xf_fnfgncal OC_FNASCII: pushab val.0 pushab val.1 pushl val.2 calls #3,xfer.xf_fnascii OC_FNCHAR: irepl val.2 pushab val.0 calls val.1,xfer.xf_fnchar OC_FNDATA: pushab val.0 pushab val.1 calls #2,xfer.xf_fndata OC_FNEXTRACT: pushab val.0 pushab val.1 pushl val.2 pushl val.3 calls #4,xfer.xf_fnextract OC_FNFGNCAL: irepab val.6 pushl val.5 pushl val.4 pushab val.3 pushab val.2 pushab val.0 calls val.1,xfer.xf_fnfgncal OC_FNFIND: pushab val.0 pushl val.3 pushab val.2 pushab val.1 calls #4,xfer.xf_fnfind OC_FNFNUMBER: pushab val.0 pushl val.4 pushl val.3 pushab val.2 pushab val.1 calls #5,xfer.xf_fnfnumber OC_FNGET: movab val.1,r1 movab val.0,r0 jsb xfer.xf_fnget OC_FNGET2: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnget2 OC_FNGVGET: pushab val.0 calls #1,xfer.xf_fngvget OC_FNGVGET1: pushab val.0 calls #1,xfer.xf_fngvget1 OC_FNGET1: pushab val.0 pushab val.1 calls #2,xfer.xf_fnget1 OC_FNINCR: pushab val.0 ;/* result of $INCR */ pushab val.2 ;/* r->operand[1] = increment */ pushab val.1 ;/* r->operand[0] = local-variable to increment */ calls #3,xfer.xf_fnincr OC_FNJ2: pushab val.0 pushl val.2 pushab val.1 calls #3,xfer.xf_fnj2 OC_FNJ3: pushab val.0 pushl val.3 pushl val.2 pushab val.1 calls #4,xfer.xf_fnj3 OC_FNLENGTH: pushab val.0 pushab val.1 calls #2,xfer.xf_fnlength OC_FNLVNAME: pushab val.0 pushl val.2 pushab val.1 calls #3,xfer.xf_fnlvname OC_FNLVNAMEO2: pushab val.2 pushab val.0 pushab val.1 calls #3,xfer.xf_fnlvnameo2 OC_FNLVPRVNAME: pushab val.0 pushab val.1 calls #2,xfer.xf_fnlvprvname OC_FNNAME: irepab val.4 pushab val.1 ; /* r->operand[0] */ pushl val.3 pushab val.0 ; /* result of $NAME */ calls val.2,xfer.xf_fnname OC_FNNEXT: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnnext OC_FNO2: pushab val.3 pushab val.0 pushab val.2 pushab val.1 calls #4,xfer.xf_fno2 OC_FNORDER: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnorder OC_FNP1: pushab val.0 pushl val.3 pushl val.2 pushab val.1 calls #4,xfer.xf_fnp1 OC_FNPIECE: pushab val.0 pushl val.4 pushl val.3 pushab val.2 pushab val.1 calls #5,xfer.xf_fnpiece OC_FNPOPULATION: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnpopulation OC_FNQLENGTH: pushab val.0 pushab val.1 calls #2,xfer.xf_fnqlength OC_FNQSUBSCR: pushab val.0 pushl val.2 pushab val.1 calls #3,xfer.xf_fnqsubscript OC_FNQUERY: irepab val.3 pushab val.2 pushab val.0 calls val.1,xfer.xf_fnquery OC_FNRANDOM: pushab val.0 pushl val.1 calls #2,xfer.xf_fnrandom OC_FNREVERSE: pushab val.0 pushab val.1 calls #2,xfer.xf_fnreverse OC_FNSTACK1: pushab val.0 pushl val.1 calls #2,xfer.xf_fnstack1 OC_FNSTACK2: pushab val.0 pushab val.2 pushl val.1 calls #3,xfer.xf_fnstack2 OC_FNTEXT: pushab val.0 pushab val.3 pushl val.2 pushab val.1 calls #4,xfer.xf_fntext OC_FNTRANSLATE: pushab val.0 pushab val.3 pushab val.2 pushab val.1 calls #4,xfer.xf_fntranslate OC_FNVIEW: irepab val.2 pushab val.0 calls val.1,xfer.xf_fnview OC_FNZASCII: pushab val.0 pushab val.1 pushl val.2 calls #3,xfer.xf_fnzascii OC_FNZAHANDLE: pushab val.0 pushab val.1 calls #2,xfer.xf_fnzahandle OC_FNZATRANSFORM: pushab val.0 pushl val.4 pushl val.3 pushl val.2 pushab val.1 calls #5,xfer.xf_fnzatransform OC_FNZBITAND: pushab val.2 pushab val.1 pushab val.0 calls #3,xfer.xf_fnzbitand OC_FNZBITCOUN: pushab val.1 pushab val.0 calls #2,xfer.xf_fnzbitcoun OC_FNZBITFIND: pushl val.3 pushl val.2 pushab val.1 pushab val.0 calls #4,xfer.xf_fnzbitfind OC_FNZBITGET: pushl val.2 pushab val.1 pushab val.0 calls #3,xfer.xf_fnzbitget OC_FNZBITLEN: pushab val.1 pushab val.0 calls #2,xfer.xf_fnzbitlen OC_FNZBITNOT: pushab val.1 pushab val.0 calls #2,xfer.xf_fnzbitnot OC_FNZBITOR: pushab val.2 pushab val.1 pushab val.0 calls #3,xfer.xf_fnzbitor OC_FNZBITSET: pushl val.3 pushl val.2 pushab val.1 pushab val.0 calls #4,xfer.xf_fnzbitset OC_FNZBITSTR: pushl val.2 pushl val.1 pushab val.0 calls #3,xfer.xf_fnzbitstr OC_FNZBITXOR: pushab val.2 pushab val.1 pushab val.0 calls #3,xfer.xf_fnzbitxor OC_FNZCALL: irepab val.2 pushab val.0 calls val.1,xfer.xf_fnzcall OC_FNZCHAR: irepl val.2 pushab val.0 calls val.1,xfer.xf_fnzchar OC_FNZCONVERT2: pushab val.0 ; /* destination mval */ pushab val.2 ; /* "U"/"L"/"T" */ pushab val.1 ; /* string */ calls #3,xfer.xf_fnzconvert2 OC_FNZCONVERT3: pushab val.0 ; /* Destination mval */ pushab val.3 ; /* target chset */ pushab val.2 ; /* src chset */ pushab val.1 ; /* string */ calls #4,xfer.xf_fnzconvert3 OC_FNZCOLLATE: pushab val.0 pushl val.3 pushl val.2 pushab val.1 calls #4,xfer.xf_fnzcollate OC_FNZDATA: pushab val.0 pushab val.1 calls #2,xfer.xf_fnzdata OC_FNZDATE: pushab val.0 pushab val.4 pushab val.3 pushab val.2 pushab val.1 calls #5,xfer.xf_fnzdate OC_FNZEXTRACT: pushab val.0 pushab val.1 pushl val.2 pushl val.3 calls #4,xfer.xf_fnzextract OC_FNZFILE: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnzfile OC_FNZFIND: pushab val.0 pushl val.3 pushab val.2 pushab val.1 calls #4,xfer.xf_fnzfind OC_FNZGETDVI: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fngetdvi OC_FNZGETJPI: pushab val.0 pushab val.2 pushl val.1 calls #3,xfer.xf_fngetjpi OC_FNZGETLKI: pushab val.0 pushab val.2 pushl val.1 calls #3,xfer.xf_fngetlki OC_FNZGETSYI: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fngetsyi OC_FNZJ2: pushab val.0 pushl val.2 pushab val.1 calls #3,xfer.xf_fnzj2 OC_FNZJOBEXAM: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnzjobexam OC_FNZLENGTH: pushab val.0 pushab val.1 calls #2,xfer.xf_fnzlength OC_FNZLKID: pushab val.0 pushl val.1 calls #2,xfer.xf_fnzlkid OC_FNZM: pushab val.0 pushl val.1 calls #2,xfer.xf_fnzm OC_FNZP1: pushab val.0 pushl val.3 pushl val.2 pushab val.1 calls #4,xfer.xf_fnzp1 OC_FNZPARSE: pushab val.0 pushab val.5 pushab val.4 pushab val.3 pushab val.2 pushab val.1 calls #6,xfer.xf_fnzparse OC_FNZPID: pushab val.0 pushl val.1 calls #2,xfer.xf_fnzpid OC_FNZPIECE: pushab val.0 pushl val.4 pushl val.3 pushab val.2 pushab val.1 calls #5,xfer.xf_fnzpiece OC_FNZPOPULATION: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnzpopulation OC_FNZPREVIOUS: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnzprevious OC_FNZPRIV: pushab val.0 pushab val.1 calls #2,xfer.xf_fnzpriv OC_FNZQGBLMOD: pushab val.0 calls #1,xfer.xf_fnzqgblmod OC_FNZSEA: pushab val.0 pushl val.3 pushl val.2 pushab val.1 calls #4,xfer.xf_fnzsearch OC_FNZSETPRV: pushab val.0 pushab val.1 calls #2,xfer.xf_fnzsetprv OC_FNZSIGPROC: pushab val.0 pushl val.2 pushl val.1 calls #3,xfer.xf_fnzsigproc OC_FNZSOCKET: irepab val.2 pushab val.0 calls val.1,xfer.xf_fnzsocket OC_FNZSUBSTR: pushab val.0 ; /* Destination mval */ pushl val.3 ; /* max byte length */ pushl val.2 ; /* starting character position */ pushab val.1 ; /* string */ calls #4,xfer.xf_fnzsubstr OC_FNZTRANSLATE: pushab val.0 pushab val.3 pushab val.2 pushab val.1 calls #4,xfer.xf_fnztranslate OC_FNZTRIGGER: pushab val.0 pushab val.3 pushab val.2 pushab val.1 calls #4,xfer.xf_fnztrigger OC_FNZTRNLNM: pushab val.0 pushab val.6 pushab val.5 pushab val.4 pushl val.3 pushab val.2 pushab val.1 calls #7,xfer.xf_fnztrnlnm OC_FNZWIDTH: pushab val.0 ; /* destination mval */ pushab val.1 ; /* string */ calls #2,xfer.xf_fnzwidth OC_FOLLOW: movab val.1,r0 movab val.2,r1 jsb xfer.xf_follow OC_FORCENUM: movab val.0,r0 movab val.1,r1 jsb xfer.xf_forcenum OC_FORCHK1: jsb xfer.xf_restartpc jsb xfer.xf_forchk1 OC_FORINIT: pushab val.3 pushab val.2 pushab val.1 jsb xfer.xf_forinit OC_FORLCLDO-BYTE: pushl val.2 jsb xfer.xf_forlcldob brb jmp.1 OC_FORLCLDO-LONG: pushl val.2 jsb xfer.xf_forlcldol jmp jmp.1 OC_FORLCLDO-WORD: pushl val.2 jsb xfer.xf_forlcldow brw jmp.1 OC_FORLOOP-BYTE:jsb xfer.xf_restartpc pushab jmp.1 pushab val.4 pushab val.3 pushab val.2 jsb xfer.xf_forloop OC_FORLOOP-LONG:jsb xfer.xf_restartpc pushab jmp.1 pushab val.4 pushab val.3 pushab val.2 jsb xfer.xf_forloop OC_FORLOOP-WORD:jsb xfer.xf_restartpc pushab jmp.1 pushab val.4 pushab val.3 pushab val.2 jsb xfer.xf_forloop OC_GETINDX: irepab val.2 calls val.1,xfer.xf_getindx movl r0,addr.0 OC_GETTRUTH: movab val.0,r1 jsb xfer.xf_gettruth OC_GVDATA: pushab val.0 calls #1,xfer.xf_gvdata OC_GVEXTNAM: irepab val.3 pushl val.2 ; /* hash_code */ calls val.1,xfer.xf_gvextnam OC_GVGET: pushab val.0 calls #1,xfer.xf_gvget OC_GVINCR: pushab val.0 ; /* result of $INCR */ pushab val.2 ; /* r->operand[1] = increment (global-variable to increment is gv_currkey so no operand[0]) calls #2,xfer.xf_gvincr OC_GVKILL: calls #0,xfer.xf_gvkill OC_GVNAKED: irepab val.3 pushl val.2 ; /* hash_code */ calls val.1,xfer.xf_gvnaked OC_GVNAME: irepab val.3 pushl val.2 ; /* hash_code */ calls val.1,xfer.xf_gvname OC_GVNEXT: pushab val.0 calls #1,xfer.xf_gvnext OC_GVO2: pushab val.1 pushab val.0 calls #2,xfer.xf_gvo2 OC_GVORDER: pushab val.0 calls #1,xfer.xf_gvorder OC_GVPUT: pushab val.1 calls #1,xfer.xf_gvput OC_GVQUERY: pushab val.0 calls #1,xfer.xf_gvquery OC_GVRECTARG: pushab val.1 calls #1,xfer.xf_gvrectarg OC_GVSAVTARG: pushab val.0 calls #1,xfer.xf_gvsavtarg OC_GVZWITHDRAW: calls #0,xfer.xf_gvzwithdraw OC_GVZWRITE: jsb xfer.xf_restartpc irepab val.4 pushl val.3 pushl val.2 calls val.1,xfer.xf_gvzwrite OC_HANG: jsb xfer.xf_restartpc pushab val.1 calls #1,xfer.xf_hang OC_HARDRET: jsb xfer.xf_hardret OC_IDIV: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_idiv OC_IGETSRC: pushab val.0 calls #1,xfer.xf_igetsrc OC_INDDEVPARMS: pushab val.0 pushl val.2 pushab val.1 jsb xfer.xf_inddevparms OC_INDFNNAME: pushab val.2 ; /* r->operand[1] = depth */ pushab val.1 ; /* r->operand[0] = name */ pushab val.0 ; /* r->dst */ jsb xfer.xf_indfnname OC_INDFUN: pushab val.0 pushl val.2 pushab val.1 jsb xfer.xf_indfun OC_INDGLVN: pushab val.0 pushab val.1 jsb xfer.xf_indglvn OC_INDINCR: pushab val.1 ; /* r->operand[0] = indirection expression */ pushab val.2 ; /* r->operand[1] = increment (ILIT) */ pushab val.0 ; /* r->dst */ jsb xfer.xf_indincr OC_INDLVADR: pushab val.1 jsb xfer.xf_indlvadr movl r0,addr.0 OC_INDLVARG: pushab val.0 pushab val.1 jsb xfer.xf_indlvarg OC_INDMERGE: pushab val.1 pushab val.2 jsb xfer.xf_indmerge OC_INDNAME: pushab val.2 pushab val.1 pushab val.0 calls #3,xfer.xf_indname OC_INDLVNAMADR: pushab val.1 jsb xfer.xf_indlvnamadr movl r0,addr.0 OC_INDO2: pushab val.2 pushab val.1 pushab val.0 calls #3,xfer.xf_indo2 OC_INDPAT: pushab val.0 pushab val.1 jsb xfer.xf_indpat OC_INDRZSHOW: pushab val.2 pushab val.1 jsb xfer.xf_indrzshow OC_INDSET: pushab val.2 pushab val.1 jsb xfer.xf_indset OC_INDTEXT: pushab val.0 pushab val.3 pushl val.2 pushab val.1 jsb xfer.xf_indtext OC_IOCONTROL: jsb xfer.xf_restartpc irepab val.2 calls val.1,xfer.xf_iocontrol OC_IRETMVAD: movab val.1,r1 jsb xfer.xf_iretmvad OC_IRETMVAL: pushab val.2 pushab val.1 jsb xfer.xf_iretmval OC_JMP-BYTE: brb jmp.1 OC_JMP-LONG: jmp jmp.1 OC_JMP-WORD: brw jmp.1 OC_JMPAT: jmp val.1 OC_JMPEQU-BYTE: beql jmp.1 OC_JMPEQU-LONG: bneq #6 jmp jmp.1 OC_JMPEQU-WORD: bneq #3 brw jmp.1 OC_JMPGEQ-BYTE: bgeq jmp.1 OC_JMPGEQ-LONG: blss #6 jmp jmp.1 OC_JMPGEQ-WORD: blss #3 brw jmp.1 OC_JMPGTR-BYTE: bgtr jmp.1 OC_JMPGTR-LONG: bleq #6 jmp jmp.1 OC_JMPGTR-WORD: bleq #3 brw jmp.1 OC_JMPLEQ-BYTE: bleq jmp.1 OC_JMPLEQ-LONG: bgtr #6 jmp jmp.1 OC_JMPLEQ-WORD: bgtr #3 brw jmp.1 OC_JMPLSS-BYTE: blss jmp.1 OC_JMPLSS-LONG: bgeq #6 jmp jmp.1 OC_JMPLSS-WORD: bgeq #3 brw jmp.1 OC_JMPNEQ-BYTE: bneq jmp.1 OC_JMPNEQ-LONG: bneq #6 jmp jmp.1 OC_JMPNEQ-WORD: beql #3 brw jmp.1 OC_JMPTCLR-BYTE: blbc r10,jmp.1 OC_JMPTCLR-LONG: blbs r10,#6 jmp jmp.1 OC_JMPTCLR-WORD: blbs r10,#3 brw jmp.1 OC_JMPTSET-BYTE: blbs r10,jmp.1 OC_JMPTSET-LONG: blbc r10,#6 jmp jmp.1 OC_JMPTSET-WORD: blbc r10,#3 brw jmp.1 OC_JOB: jsb xfer.xf_restartpc irepab val.7 pushab val.6 pushab val.5 pushab val.4 pushl val.3 pushab val.2 calls val.1,xfer.xf_job OC_KILL: pushab val.1 calls #1,xfer.xf_kill OC_KILLALIAS: pushl val.1 calls #1,xfer.xf_killalias OC_KILLALL: calls #0,xfer.xf_killall OC_KILLALIASALL: calls #0,xfer.xf_killaliasall OC_LABADDR: pushl val.2 pushab val.1 pushl val.3 calls #3,xfer.xf_labaddr movl r0,val.0 OC_LCKDECR: pushab val.1 calls #1,xfer.xf_lckdecr OC_LCKINCR: pushab val.1 calls #1,xfer.xf_lckincr OC_LDADDR-BYTE: movab jmp.1,addr.0 OC_LDADDR-LONG: movab jmp.1,addr.0 OC_LDADDR-WORD: movab jmp.1,addr.0 OC_LINEFETCH: irepl val.2 pushl val.1 jsb xfer.xf_linefetch OC_LINESTART: jsb xfer.xf_linestart OC_LKEXTNAME: irepab val.4 pushab val.3 pushab val.2 calls val.1,xfer.xf_lkname OC_LKINIT: calls #0,xfer.xf_lkinit OC_LKNAME: irepab val.4 pushab val.3 pushl val.2 calls val.1,xfer.xf_lkname OC_LOCK: pushab val.1 calls #1,xfer.xf_lock OC_LVPATWRITE: jsb xfer.xf_restartpc irepab val.3 pushl val.2 calls val.1,xfer.xf_lvpatwrite OC_LVZWITHDRAW: pushab val.1 calls #1,xfer.xf_lvzwithdraw OC_LVZWRITE: jsb xfer.xf_restartpc irepab val.2 calls val.1,xfer.xf_lvzwrite OC_M_SRCHINDX: irepab val.2 calls val.1,xfer.xf_m_srchindx movl r0,addr.0 OC_MERGE: calls #0,xfer.xf_merge OC_MERGE_GVARG: pushl #0 pushl val.1 calls #2,xfer.xf_merge_arg OC_MERGE_LVARG: pushab val.2 pushl val.1 calls #2,xfer.xf_merge_arg OC_MOD: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_flt_mod OC_MUL: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_mul OC_NEG: movab val.0,r0 movab val.1,r1 jsb xfer.xf_neg OC_NEWINTRINSIC: pushl val.1 jsb xfer.xf_newintrinsic OC_NEWVAR: pushl val.1 jsb xfer.xf_newvar OC_NULLEXP: calls #0,xfer.xf_nullexp movl r0,addr.0 OC_NUMCMP: movab val.1,r0 movab val.2,r1 jsb xfer.xf_numcmp OC_OPEN: jsb xfer.xf_restartpc pushab val.4 pushab val.3 pushab val.2 pushab val.1 calls #4,xfer.xf_open OC_PATTERN: movab val.1,r0 movab val.2,r1 jsb xfer.xf_pattern OC_PUTINDX: irepab val.2 calls val.1,xfer.xf_putindx movl r0,addr.0 OC_RDONE: jsb xfer.xf_restartpc pushab val.1 pushab val.0 calls #2,xfer.xf_rdone OC_READ: jsb xfer.xf_restartpc pushab val.1 pushab val.0 calls #2,xfer.xf_read OC_READFL: jsb xfer.xf_restartpc pushab val.2 pushl val.1 pushab val.0 calls #3,xfer.xf_readfl OC_RESTARTPC: jsb xfer.xf_restartpc OC_RET: jsb xfer.xf_ret OC_RETARG: movab val.1,r0 movl val.2,r1 jsb xfer.xf_retarg OC_RHDADDR: pushl val.2 pushab val.1 calls #2,xfer.xf_rhdaddr movl r0,val.0 OC_RHDADDR1: pushl #-1 pushab val.1 calls #2,xfer.xf_rhdaddr movl r0,val.0 ; ; Note if OC_RTERROR call changes, linetail.c and eval_expr.c will also need ; to change due to it dereferencing the backpoints to get to the opcode. ; OC_RTERROR: pushl val.2 pushl val.1 jsb xfer.xf_rterror OC_SETALS2ALS: pushl val.1 pushab val.2 calls #2,xfer.xf_setals2als OC_SETALSIN2ALSCT: pushab val.1 pushab val.2 calls #2,xfer.xf_setalsin2alsct OC_SETALSCTIN2ALS: pushl val.1 pushab val.2 calls #2,xfer.xf_setalsctin2als OC_SETALSCT2ALSCT: pushab val.1 pushab val.2 calls #2,xfer.xf_setalsct2alsct OC_SETFNRETIN2ALS: pushl val.1 pushab val.2 calls #2,xfer.xf_setfnretin2als OC_SETFNRETIN2ALSCT: pushab val.1 pushab val.2 calls #2,xfer.xf_setfnretin2alsct OC_SETEXTRACT: pushab val.0 pushl val.3 pushl val.2 pushab val.4 pushab val.1 calls #5,xfer.xf_setextract OC_SETP1: pushab val.0 pushl val.3 pushab val.4 pushl val.2 pushab val.1 calls #5,xfer.xf_setp1 OC_SETPIECE: pushab val.0 pushl val.4 pushl val.3 pushab val.5 pushab val.2 pushab val.1 calls #6,xfer.xf_setpiece OC_SETTEST: bisb2 #1,r10 jsb xfer.xf_dt_true OC_SETZBRK: pushl val.5 pushab val.4 pushl val.2 pushab val.1 pushab val.3 jsb xfer.xf_setzbrk ; /* calls #5,xfer.xf_setzbrk */ OC_SETZEXTRACT: pushab val.0 pushl val.3 pushl val.2 pushab val.4 pushab val.1 calls #5,xfer.xf_setzextract OC_SETZP1: pushab val.0 pushl val.3 pushab val.4 pushl val.2 pushab val.1 calls #5,xfer.xf_setzp1 OC_SETZPIECE: pushab val.0 pushl val.4 pushl val.3 pushab val.5 pushab val.2 pushab val.1 calls #6,xfer.xf_setzpiece OC_SORTS_AFTER: movab val.1,r0 movab val.2,r1 jsb xfer.xf_sorts_after OC_SRCHINDX: irepab val.2 calls val.1,xfer.xf_srchindx movl r0,addr.0 OC_STO: movab val.2,r1 movab val.1,r0 jsb xfer.xf_sto OC_STOLIT: movc3 #16,val.2,val.1 OC_STOTEMP: movab val.1,r1 movab val.0,r0 jsb xfer.xf_sto OC_SUB: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_sub OC_SVGET: pushab val.0 pushl val.1 calls #2,xfer.xf_svget OC_PSVPUT: pushab val.2 pushl val.1 jsb xfer.xf_psvput OC_SVPUT: pushab val.2 pushl val.1 calls #2,xfer.xf_svput OC_TIMTRU: movl r0,r10 OC_TCOMMIT: jsb xfer.xf_tcommit OC_TROLLBACK: pushl val.1 jsb xfer.xf_trollback OC_TRESTART: pushl val.1 jsb xfer.xf_trestart OC_TSTART: irepab val.4 pushl val.3 pushab val.2 pushl val.1 jsb xfer.xf_tstart OC_UNLOCK: calls #0,xfer.xf_unlock OC_USE: pushab val.2 pushab val.1 calls #2,xfer.xf_use OC_VIEW: irepab val.2 calls val.1,xfer.xf_view OC_VXCMPL: cmpl val.1,val.2 OC_WRITE: pushab val.1 calls #1,xfer.xf_write OC_WTEOL: pushl val.1 calls #1,xfer.xf_wteol OC_WTFF: calls #0,xfer.xf_wtff OC_WTONE: pushl val.1 calls #1,xfer.xf_wtone OC_WTTAB: pushl val.1 calls #1,xfer.xf_wttab OC_XKILL: irepab val.2 calls val.1,xfer.xf_xkill OC_XNEW: irepab val.2 pushl val.1 jsb xfer.xf_xnew OC_ZALLOCATE: pushab val.1 calls #1,xfer.xf_zallocate OC_ZATTACH: jsb xfer.xf_restartpc pushab val.1 calls #1,xfer.xf_zattach OC_ZCOMPILE: pushl val.2 pushab val.1 calls #2,xfer.xf_zcompile OC_ZCONT: jsb xfer.xf_zcont OC_ZDEALLOCATE: pushab val.1 calls #1,xfer.xf_zdeallocate OC_ZEDIT: jsb xfer.xf_restartpc pushab val.2 pushab val.1 calls #2,xfer.xf_zedit OC_ZG1: pushl val.1 jsb xfer.xf_zg1 OC_ZGOTO: pushl val.1 pushl val.4 pushab val.3 pushab val.2 jsb xfer.xf_zgoto OC_ZHALT: pushl val.1 pushl val.2 calls #2,xfer.xf_zhalt OC_ZHELP: pushab val.2 pushab val.1 calls #2,xfer.xf_zhelp OC_ZLINK: pushab val.2 pushab val.1 calls #2,xfer.xf_zlink OC_ZMESS: irepab val.3 pushl val.2 calls val.1,xfer.xf_zmess OC_ZPREVIOUS: pushab val.0 calls #1,xfer.xf_zprevious OC_ZPRINT: jsb xfer.xf_restartpc pushl val.5 pushab val.4 pushl val.3 pushab val.2 pushab val.1 calls #5,xfer.xf_zprint OC_ZSHOW: jsb xfer.xf_restartpc pushl #0 pushl val.2 pushab val.1 calls #3,xfer.xf_zshow OC_ZSHOWLOC: jsb xfer.xf_restartpc pushab val.3 pushl val.2 pushab val.1 calls #3,xfer.xf_zshow OC_ZSTEP: pushl #0 pushl val.1 calls #2,xfer.xf_zstep jsb xfer.xf_zcont OC_ZSTEPACT: pushab val.2 pushl val.1 calls #2,xfer.xf_zstep jsb xfer.xf_zcont OC_ZSYSTEM: jsb xfer.xf_restartpc pushab val.1 calls #1,xfer.xf_zsystem OC_ZTCOMMIT: pushl val.1 calls #1,xfer.xf_ztcommit OC_ZTRIGGER: calls #0,xfer.xf_ztrigger OC_ZTSTART: calls #0,xfer.xf_ztstart OC_ZWRITESVN: pushl val.1 calls #1,xfer.xf_zwritesvn OC_FNZWRITE: pushab val.0 ; /* destination mval */ pushab val.1 ; /* source (string) mval */ pushl val.2 ; /* conversion direction indicator */ calls #3,xfer.xf_fnzwrite OC_IGETDST: calls #0,xfer.xf_igetdst movl r0,addr.0 OC_INDGET1: pushab val.0 pushab val.1 calls #2,xfer.xf_indget1 OC_GLVNPOP: pushab val.1 calls #1,xfer.xf_glvnpop OC_GLVNSLOT: pushl val.1 calls #1,xfer.xf_glvnslot movl r0,addr.0 OC_INDSAVGLVN: pushl val.3 pushab val.2 pushab val.1 jsb xfer.xf_indsavglvn OC_INDSAVLVN: pushab val.2 pushab val.1 jsb xfer.xf_indsavlvn OC_RFRSHLVN: pushl val.2 pushab val.1 calls #2,xfer.xf_rfrshlvn movl r0,addr.0 OC_SAVGVN: irepab val.3 pushl val.2 ; /* hash_code */ calls val.1,xfer.xf_savgvn OC_SAVLVN: irepab val.2 calls val.1,xfer.xf_savlvn OC_SHARESLOT: pushl val.2 pushab val.1 calls #2,xfer.xf_shareslot OC_STOGLVN: pushab val.2 pushab val.1 calls #2,xfer.xf_stoglvn OC_RFRSHGVN: pushl val.2 pushab val.1 calls #2,xfer.xf_rfrshgvn OC_INDFNNAME2: pushab val.2 pushab val.1 pushab val.0 calls #3,xfer.xf_indfnname2 OC_INDGET2: pushab val.1 pushab val.0 calls #2,xfer.xf_indget2 OC_INDMERGE2: pushab val.1 calls #1,xfer.xf_indmerge2 OC_LITC: pushab val.1 ; /* opcode not in sr_unix_nsb/ttt or sr_vvms/ttt */ pushab val.0 calls #2,xfer.xf_litc OC_STOLITC: movc3 #16,val.2,val.1 ; /* opcode not in sr_unix_nsb/ttt or sr_vvms/ttt */ pushab val.1 calls #1,xfer.xf_stolitc OC_FNZPEEK: pushab val.0 pushab val.4 pushl val.3 pushl val.2 pushab val.1 calls #5,xfer.xf_fnzpeek OC_FNZSYSLOG: pushab val.0 ; /* destination mval */ pushab val.1 ; /* string */ calls #2,xfer.xf_fnzsyslog OC_ZRUPDATE: irepab val.2 calls val.1,xfer.xf_zrupdate OC_FNZTRANSLATE_FAST: pushab val.0 pushab val.2 pushab val.1 calls #3,xfer.xf_fnztranslate_fast OC_FNTRANSLATE_FAST: pushab val.0 pushab val.4 pushab val.3 pushab val.2 pushab val.1 calls #5,xfer.xf_fntranslate_fast OC_FNZAUDITLOG: pushab val.0 ; /* destination mval */ pushab val.1 ; /* string */ calls #2,xfer.xf_fnzauditlog fis-gtm-V7.0-005/sr_unix/upd_log_init.c0000755000032200000250000000460714342376330016673 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2018 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_string.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmrecv.h" #include "iosp.h" #include "repl_log.h" #include "repl_dbg.h" #include "gtm_stdio.h" #include "read_db_files_from_gld.h" #include "updproc.h" #define UPDPROC_LOG_FILE_SUFFIX ".updproc" #define UPDHELPER_READER_LOG_FILE_SUFFIX ".uhr_" #define UPDHELPER_WRITER_LOG_FILE_SUFFIX ".uhw_" GBLREF int updproc_log_fd, updhelper_log_fd; GBLREF FILE *updproc_log_fp, *updhelper_log_fp; GBLREF recvpool_addrs recvpool; GBLREF uint4 process_id; int upd_log_init(recvpool_user who) { char log_file[MAX_FN_LEN + 1], file_suffix_str[MAX_FN_LEN + 1], pid_str[11], *pid_end_ptr, *file_suffix; int status = SS_NORMAL; int *fd_addrs; FILE **fp_addrs; fd_addrs = (UPDPROC == who) ? &updproc_log_fd : &updhelper_log_fd; fp_addrs = (UPDPROC == who) ? &updproc_log_fp : &updhelper_log_fp; strcpy(log_file, recvpool.gtmrecv_local->log_file); if (0 != strncmp(log_file, DEVICE_PREFIX, STR_LIT_LEN(DEVICE_PREFIX))) { if (UPDPROC == who) file_suffix = UPDPROC_LOG_FILE_SUFFIX; else { if (UPD_HELPER_READER == who) strcpy(file_suffix_str, UPDHELPER_READER_LOG_FILE_SUFFIX); else /* UPD_HELPER_WRITER == who */ strcpy(file_suffix_str, UPDHELPER_WRITER_LOG_FILE_SUFFIX); pid_end_ptr = (char *)i2asc((uchar_ptr_t)pid_str, process_id); *pid_end_ptr = '\0'; strcat(file_suffix_str, pid_str); file_suffix = file_suffix_str; } strcat(log_file, file_suffix); } if (FD_INVALID == *fd_addrs || (UPDPROC == who && 0 != strcmp(log_file, recvpool.upd_proc_local->log_file))) { status = repl_log_init(REPL_GENERAL_LOG, fd_addrs, log_file); repl_log_fd2fp(fp_addrs, *fd_addrs); if (UPDPROC == who) strcpy(recvpool.upd_proc_local->log_file, log_file); } return(status); } fis-gtm-V7.0-005/sr_unix/urx_remove.c0000644000032200000250000001031514342376335016405 0ustar librarygtc/**************************************************************** * * * Copyright 2002, 2014 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 "gtm_stdio.h" #include #include "urx.h" /* Routine to run the unresolved chain in search of entries that point into the linkage section that is about to be released (for USHBIN type platforms) or for unresolves that point into the code section for non-USHBIN platforms (currently only used by triggers on non-USHBIN platforms). The The boundaries of the pertinent section is computed appropriately from the passed in routine header. Located entries are removed from the unresolved chains (routine and label chains). */ GBLREF urx_rtnref urx_anchor; #define IS_IN_RANGE(start, end, item) ((char *)(item) >= (char *)(start) && (char *)(item) < (char *)(end)) void urx_remove(rhdtyp *rtnhdr) { urx_rtnref *rtn, *rtnprev; urx_labref *lab, *labprev; urx_addr *addr, *addrprev, *savaddr; unsigned char *regstart, *regend; int deletes; DEBUG_ONLY(deletes = 0); #ifdef USHBIN_SUPPORTED /* All unresolved addresses will point into the linkage section */ regstart = (unsigned char *)rtnhdr->linkage_adr; regend = regstart + (SIZEOF(lnk_tabent) * rtnhdr->linkage_len); #else /* All unresolved addresses will point into the code section */ regstart = PTEXT_ADR(rtnhdr); regend = PTEXT_END_ADR(rtnhdr); #endif rtnprev = &urx_anchor; rtn = rtnprev->next; while (rtn) { /* For each unresolved routine.. */ addrprev = NULL; addr = rtn->addr; while (addr) { /* Run list of resolve addrs for this routine */ if (IS_IN_RANGE(regstart, regend, addr->addr)) { /* We will be deleting an element so addrprev will not be changing */ if (NULL == addrprev) rtn->addr = addr->next; /* First element being removed */ else addrprev->next = addr->next; savaddr = addr->next; free(addr); addr = savaddr; DEBUG_ONLY(++deletes); continue; } addrprev = addr; addr = addr->next; } /* Note that the structure of the urx_labref and urx_rtnref is critical here. The urx_rtnref serves as an anchor for the urx_labref chain by virtue of urx_rtnref's "lab" pointer being at the same offset as urx_labref's "next" pointer. */ labprev = (urx_labref *)rtn; lab = rtn->lab; while (lab) { addrprev = NULL; addr = lab->addr; while (addr) { if (IS_IN_RANGE(regstart, regend, addr->addr)) { /* We will be deleting an element so addrprev will not be changing */ if (NULL == addrprev) lab->addr = addr->next; /* First element being removed */ else addrprev->next = addr->next; savaddr = addr->next; free(addr); addr = savaddr; DEBUG_ONLY(++deletes); continue; } addrprev = addr; addr = addr->next; } if (NULL == lab->addr) { /* No references to this label left .. remove from unresolved chain */ labprev->next = lab->next; free(lab); lab = labprev->next; DEBUG_ONLY(++deletes); continue; } labprev = lab; lab = lab->next; } /* Note that it is possible to have a routine on the unresolved chain with no addr chain of unresolves for it yet there are labels unresolved. This would be the case if a routine contained a call to a non-existent label. It is not an error until/unless the call call is executed. The reverse is also true, it is possible to have an unresolved addr chain for the routine with no labels. This occurs when a call using indirection such as DO @LBL^RTN. In this case, there will be no unresolved label but there will be an unresolved routine. */ if (NULL == rtn->addr && NULL == rtn->lab) { /* This node has no reason to keep living */ rtnprev->next = rtn->next; free(rtn); rtn = rtnprev->next; DEBUG_ONLY(++deletes); continue; } rtnprev = rtn; rtn = rtn->next; } #ifdef DEBUG_URX PRINTF("urx_remove: Deleted %d entries\n", deletes); #endif } fis-gtm-V7.0-005/sr_unix/utf2hex.mpt0000755000032200000250000000207714342376330016161 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2006 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %UTF2HEX;GT.M %UTF2HEX utility - UTF-8 string to hexadecimal byte notation ;invoke with %S storing the UTF-8 string to return %U having the hexadecimal byte notation ;invoke at INT to execute interactively ;invoke at FUNC as an extrinsic function ; set %U=$$FUNC(%S) quit INT new %S read !,"String: ",%S set %U=$$FUNC(%S) quit FUNC(s) new l set l=$ZLENGTH(s) quit $$recurse(1,l) recurse(start,end); new l,m set l=end-start+1 if (l<1024) quit $$eval(start,end) set m=l\2 quit $$recurse(start,start+m-1)_$$recurse(start+m,end) eval(start,end); new u,a,i set u="" for i=start:1:end set a=$ZASCII(s,i),u=u_$$FUNC^%DH(a,2) quit u fis-gtm-V7.0-005/sr_unix/utfcgr.c0000644000032200000250000010610414342376330015501 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2018 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_stdio.h" #include "gtmimagename.h" #include "utfcgr.h" #include "gtm_utf8.h" GBLREF boolean_t gtm_utf8_mode; /* Macro to activate to see contents of cache at start and/or end of each utfcgr_scanforcharN() call. Just * uncomment the desired macros below. This trace macro resident here as it should only be used in utfcgr.c. */ /* #define DUMP_UTFCACHE_AT_START */ /* #define DUMP_UTFCACHE_AT_END */ /* This one is typically more "useful" */ #define DUMP_UTFCACHE(MVALPTR, UTFCGRP) \ MBSTART { /* Note assumption made that this is valid cache for this string so don't use before that's true */ \ utfcgr_entry *UTFCGREP; \ int i; \ \ DBGFPF((stderr, "**utfcgr_scanforcharN cache dump for mval 0x"lvaddr" with string address 0x"lvaddr \ " char len: %d byte len:%d\n", (MVALPTR), (MVALPTR)->str.addr, (MVALPTR)->str.char_len, \ (MVALPTR)->str.len)); \ DBGFPF((stderr, "**utfcgr_scanforcharN ngrps: %d, idx: %d, reference: %d - at line %d\n", (UTFCGRP)->ngrps, \ (UTFCGRP)->idx, (UTFCGRP)->reference, __LINE__)); \ for (UTFCGREP = &(UTFCGRP)->entry[0], i = 1; i <= (UTFCGRP)->ngrps; UTFCGREP++, i++) \ { \ DBGFPF((stderr, "**utfcgr_scanforcharN elem[%d] - typflags: %d, charcnt: %d, byteidx: %d\n", \ i, UTFCGREP->typflags, UTFCGREP->charcnt, UTFCGREP->byteidx)); \ } \ } MBEND #ifdef DUMP_UTFCACHE_AT_START # define DUMP_UTFCACHE_START(MVALPTR, UTFCGRPTR) DUMP_UTFCACHE(MVALPTR, UTFCGRPTR) #else # define DUMP_UTFCACHE_START(MVALPTR, UTFCGRPTR) #endif #ifdef DUMP_UTFCACHE_AT_END # define DUMP_UTFCACHE_END(MVALPTR, UTFCGRPTR) DUMP_UTFCACHE(MVALPTR, UTFCGRPTR) #else # define DUMP_UTFCACHE_END(MVALPTR, UTFCGRPTR) #endif #ifdef UTF8_SUPPORTED /* Routine to locate a reusable cache area in the utfcgr array. * * Parameter: * * - mv - mval to which the allocated slot is assigned. * * Return value: * * - Address of utfcgr struct to use (mv is set to use this struct). */ utfcgr *utfcgr_getcache(mval *mv) { utfcgr *utfcgrp; int tries; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; for (utfcgrp = (TREF(utfcgra)).utfcgrsteal, tries = TREF(utfcgr_string_lookmax); 0 < tries; utfcgrp += (TREF(utfcgra)).utfcgrsize, tries--) { /* Look for available slots within the number of slots we are allowed to look at */ if ((TREF(utfcgra)).utfcgrmax < utfcgrp) utfcgrp = (TREF(utfcgra)).utfcgrs; /* Wrap around to beginning */ if (utfcgrp->reference) { utfcgrp->reference = FALSE; continue; /* Ignore this slot for now */ } else break; /* Found one not reserved */ } if (0 == tries) { /* We stopped due to hitting our search limit so utfcgrp could be past our array */ if ((TREF(utfcgra)).utfcgrmax < utfcgrp) utfcgrp = (TREF(utfcgra)).utfcgrs; /* Wrap around to beginning */ } /* Doesn't matter if utfcgrsteal is off end - gets fixed when it is picked up for the next getcache() */ (TREF(utfcgra)).utfcgrsteal = (utfcgr *)((UINTPTR_T)utfcgrp + (TREF(utfcgra)).utfcgrsize); mv->utfcgr_indx = utfcgrp->idx + 1; /* Point supplied mval to new slot - always stored + 1 */ utfcgrp->last_str = mv->str; /* Save mstr info */ utfcgrp->ngrps = 1; /* Initialize group in use */ utfcgrp->reference = FALSE; return utfcgrp; } /* Routine to scan string in UTF8 mode finding a given character number using (and/or creating) lookaside * entries in the relevant string cache. * * Parameters: * * char_num - index of character to be found. * utf_parse_blk - parse block describing string and where scan starts/left-off. Note see utfcgr.h to see which fields in the * utf_parse_blk are used for input, return, or both. * * Returns: * - Return code is TRUE is character was found, FALSE if it wasn't. * - utf_parse_blk->scan_char_type describes what was found or not found: * a) Contains UTFCGR_EOL if ran off end of string before char_num char was found. * b) Contains UTFCGR_BADCHAR if ran into a BADCHAR and utf_parse_blk->stoponbadchar was set which also sets * utf_parse_blk->badcharstr and utf_parse_blk->badchartop to describe the buffer containing the badchar. If * utf_parse_blk->stoponbadchar is not set, this value can be returned with a successful return which also then sets * the "regular" return fields. * c) Contains the character type of the located character which also sets the "regular" return vars. * * Note - this routine recognizes BADCHARs only in the portion of the string it "skips" to get to the * required character position. So for example, if a string is 2 "good" characters followed by a badchar, * locating positions 1, 2, or 3 won't trigger a badchar error but locating a char in position 4 would. * * The utfcgr_entry array is a series of groups of like characters. The value of typflags in a given group is the type of * the characters in that group but the other fields define the END of the group. For example, take the following string: * * x="123"_$char(195,196)_"4" * * This would create 3 group entries as follows: * * utfcgr_entry[0]: * typeflags = UTFCGR_ASCII * charcnt = 3 * byteidx = 3 * utfcgr_entry[1]: * typeflags = UTFCGR_UTF * charcnt = 5 * byteidx = 7 * utfcgr_entry[2]: * typeflags = UTFCGR_ASCII * charcnt = 6 * byteidx = 8 * * When the utfcgr.entry array for a given string is full, scanning past what is defined in the cache is dealt with by * good 'ole brute force scanning. The u_parhscan counter tracks these but only in debug mode (for now). * * Notes: * * 1. This routine does not obey badchar_inhibit but rather behaves according to the utf_parse_blk->stoponbadchar flag in * the utf_parse_blk to have a bit of versatility. For example, if used eventually in an IO routine, we would pass TRUE * in utf_parse_blk->stoponbadchar regardless of the setting of badchar_inhibit. * * 2. When a cache is setup for a given string, it survives only until that cache entry is reused. * * 3. Stringpool garbage collection erases the cache because that is the only way to make sure the address/length * cache validation check is assured to always be correct without false positives. * * 4. Routine has counters and indexes. Typically, when we are talking about byte indexes, we are talking * about a 0 origin index since that's how the C language references them. When talking about characters * we are generally talking about counts and since characters are not directly addressable because they * are multi-byte in UTF8 mode, these generally have a 1 origin. */ boolean_t utfcgr_scanforcharN(int char_num, utfscan_parseblk *utf_parse_blk) { int char_idx, tcharcnt, tbyteidx, gcharcnt, gbytecnt, bytecnt, skip, bidx; int lchar_byteidx, lchar_charcnt; unsigned int utfcgrepcnt, utfcgridx, chartype, lchar_typflags; boolean_t noslots, lastcharbad, cachemod, scaneol; unsigned char *scantop, *scanptr; mval *mv; utfcgr *utfcgrp; utfcgr_entry *utfcgrep, *utfcgrep_prev, *utfcgrep0, *pcentmax, *pcuentmax; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(gtm_utf8_mode); /* Determine mval and its utfcgr index */ mv = utf_parse_blk->mv; /* Mval address related to this parse block */ DBGUTFC((stderr, " utfcgr_scanforcharN: Entry - char_num: %d - utf_parse_blk->(scan_byte_offset: %d, " "scan_char_count: %d, utfcgr_indx: %d)\n", char_num, utf_parse_blk->scan_byte_offset, utf_parse_blk->scan_char_count, utf_parse_blk->utfcgr_indx)); DBGUTFC((stderr, " .. for string at 0x"lvaddr" char_len: %d byte len: %d\n", utf_parse_blk->mv->str.addr, utf_parse_blk->mv->str.char_len, utf_parse_blk->mv->str.len)); assert(0 < char_num); assert(NULL != mv); /* Make sure an mval was specified */ char_idx = char_num - 1; scantop = (unsigned char *)mv->str.addr + mv->str.len; utf_parse_blk->scan_char_type = UTFCGR_NONE; /* Init return char type */ /* In the below parsing, the following values are involved in the parse and cache: * * char_num - Entry parameter specifying which character to locate. * pcentmax - Pointer to last available entry[] in this cache line. * pcuentmax - Pointer to last used entry[] + 1 in this cache line. * scanptr - Points to current scan byte in string. * scantop - Points to end of current scan string + 1. * tbyteidx - The byte index from start of string to current byte or start of current group depending on use. * tcharcnt - The character count from the start of the string to current character (but not including it) or * the start of the current group depending on use. * utfcgrep - Points to entry[] in a given utfcgrp string-cache line. * utfcgrp - Points to a given string-cache line (utfcgrs[]). * * Note - If we are looking for the "first" char in the string, just fast path down to the non-cache layer so we * don't confuse the cacheing logic by initializing a null group. Also avoid cacheing during compilation * to avoid confusing things. */ if (IS_MCODE_RUNNING && (UTFCGR_STRLEN_MIN < mv->str.len) && (0 < char_idx)) { /* This string meets minimum length requirements for having a cache attached to it */ utfcgridx = mv->utfcgr_indx - 1; /* Always stored in utfcgr_indx with 1 origin - adjust */ /* Point to cache entry for this string */ utfcgrp = (utfcgr *)((char *)(TREF(utfcgra)).utfcgrs + ((TREF(utfcgra)).utfcgrsize * utfcgridx)); tcharcnt = tbyteidx = 0; /* Init counters */ /* Validate cache entry for this string still intact (index valid, string addr/len the same) */ if ((TREF(gtm_utfcgr_strings) > utfcgridx) && (utfcgrp->last_str.addr == mv->str.addr) && (utfcgrp->last_str.len == mv->str.len)) { /* Cache validated */ DUMP_UTFCACHE_START(mv, utfcgrp); COUNT_UTF_EVENT(hit); utfcgrp->reference = TRUE; /* Mark element recently used */ pcentmax = &utfcgrp->entry[TREF(gtm_utfcgr_string_groups)]; /* Pointer to last entry + 1 in this row */ pcuentmax = &utfcgrp->entry[utfcgrp->ngrps]; /* Pointer to last used entry + 1 in this row */ assert(pcentmax >= pcuentmax); utfcgrep0 = &utfcgrp->entry[0]; /* To pickup from a previous scan here we need both a good offset and a non-negative index */ if ((0 < utf_parse_blk->scan_byte_offset) && (0 <= utf_parse_blk->utfcgr_indx) && (UTFCGR_NONE != utf_parse_blk->scan_char_type)) { /* Picking up from a previous scan - Set up scanning vars appropriately */ assert(mv->str.len > utf_parse_blk->scan_byte_offset); assert(utfcgrp->ngrps > utf_parse_blk->utfcgr_indx); utfcgrep = &utfcgrp->entry[utf_parse_blk->utfcgr_indx]; DEBUG_ONLY(utfcgrepcnt = utf_parse_blk->utfcgr_indx); if (utfcgrep != utfcgrep0) { /* This is not the first group so we can reference previous group */ tcharcnt = (utfcgrep - 1)->charcnt; tbyteidx = (utfcgrep - 1)->byteidx; } /* else, these values just stay at their initialized (0'd) state set earlier */ } else { utfcgrep = utfcgrep0; DEBUG_ONLY(utfcgrepcnt = 0); } /* See if char we want is in cache. Note - even if the char is in the cache, we still need to search for * it within the final character group. * * Note currently the loop below serially processes the entries but it might be possible to speed things up * further by implementing a binary search to find the group we want since the values associated with each * group and the ending values of this group and the start of the next. This would only be useful for long * strings with lots of mixing or lots of UTF8. */ for (; (utfcgrep < pcuentmax) && (char_num > utfcgrep->charcnt); tcharcnt = utfcgrep->charcnt, tbyteidx = utfcgrep->byteidx, utfcgrep++) { COUNT_UTF_EVENT(pskip); /* Counting "skipped" groups */ assert(0 != utfcgrep->typflags); /* Should be *some* indication of content */ assert(TREF(gtm_utfcgr_string_groups) >= ++utfcgrepcnt); /* Check we're not overflowing array */ if (utf_parse_blk->stoponbadchar && (UTFCGR_BADCHAR & utfcgrep->typflags)) { /* Found BADCHARs in the string and this scan can't tolerate them */ DBGUTFC((stderr, " utfcgr_scanforcharN: Returning due to BADCHAR\n")); utf_parse_blk->badcharstr = (unsigned char *)utf_parse_blk->mv->str.addr + tbyteidx; utf_parse_blk->badchartop = scantop; utf_parse_blk->scan_char_type = UTFCGR_BADCHAR; DUMP_UTFCACHE_END(mv, utfcgrp); return FALSE; } } /* At this point we have one of two cases: * * 1. The current block contains the character we want. * 2. The loop went past the last used entry. * * In case 1, we need a short/fast brute-force search of this character block for the character. We * do that in-line here. Note "search" is simply an index if this is an ASCII or BADBLOCK type * group of characters. * * In case 2, we set things up to fall into the continued search to locate the character * filling in additional cache entries if they are available. * * First up - Case 1, which we hope is the more common case (reduced scanning). */ if (utfcgrep < pcuentmax) { /* Case 1: We didn't exit loop due to overflow so char we want is in this block. * Locate the character and return its info. */ DBGUTFC((stderr, " utfcgr_scanforcharN: Char located in cache\n")); assert(0 != utfcgrep->typflags); if (UTFCGR_UTF != utfcgrep->typflags) { /* Group is single byte chars - index to char to return */ COUNT_UTF_EVENT(pabscan); /* Count groups we scan for located char */ assert(char_num >= tcharcnt); bidx = char_idx - tcharcnt; /* Simple offset into this group of char we seek */ utf_parse_blk->scan_byte_offset = tbyteidx + bidx; utf_parse_blk->scan_char_count = tcharcnt + bidx; utf_parse_blk->scan_char_len = 1; utf_parse_blk->scan_char_type = utfcgrep->typflags; } else { /* Group is multi-byte chars - search for char to return */ COUNT_UTF_EVENT(puscan); /* Count groups we're scanning for located char */ scanptr = (unsigned char *)mv->str.addr + tbyteidx; for (skip = char_idx - tcharcnt, gcharcnt = 0; ((0 < skip) && (scanptr < scantop)); skip--, scanptr += bytecnt, gcharcnt++) { /* Advance the string to locate the desired character. Since we are scanning * an alleged UTF8 string, no BADCHARs should exist in it - hence assertpro(). */ assertpro(UTF8_VALID(scanptr, scantop, bytecnt)); } /* Find length of char we end up on */ assertpro(UTF8_VALID(scanptr, scantop, bytecnt)); utf_parse_blk->scan_byte_offset = scanptr - (unsigned char *)mv->str.addr; utf_parse_blk->scan_char_count = tcharcnt + gcharcnt; utf_parse_blk->scan_char_len = bytecnt; utf_parse_blk->scan_char_type = UTFCGR_UTF; } utf_parse_blk->utfcgr_indx = utfcgrep - utfcgrep0; /* Saved as 0-origin since picked up same way */ DUMP_UTFCACHE_END(mv, utfcgrp); return TRUE; } /* Case 2 - We ran off the edge of the string or the cache. There are two subcases here: * * 2a. We ran out of text - nothing left to scan - character position doesn't exist in this string. * 2b. The cache doesn't describe the entire string so there is more to scan and potentially add * to the cache if the necessary entries exist. * * In case 2a, the character was not found so return UTFCGR_EOL as the reason code. * * In case 2b, set up our vars and fall into the same path as the below "start from scratch" scan * to continue building the cache. */ if (tbyteidx >= mv->str.len) { /* Case 2a - character position is not found (ran out of text) - update output fields */ DBGUTFC((stderr, " utfcgr_scanforcharN: Ran off end of scanned string - EOL\n")); utf_parse_blk->scan_byte_offset = mv->str.len; utf_parse_blk->scan_char_count = (pcuentmax - 1)->charcnt; utf_parse_blk->scan_char_len = 0; /* Since didn't find char this value not set */ utf_parse_blk->scan_char_type = UTFCGR_EOL; DUMP_UTFCACHE_END(mv, utfcgrp); return FALSE; } /* Case 2b - Set up for continued scan below. The current utfcgrep group is pointing to a new group entry. * Move it back 1 to point to the actual last group - at least temporarily. Note this also means a * temporary discontinuity between the scan variables tbyteidx and tcharcnt that is fixed (if needbe) * in the follow-on code paragraph. * * First check if we would be adding chars to the previous scan group or starting a new group, or, finally, * if all of the scan groups are used with no ability to save more. In this case, we use a different * scan as noted below that runs faster without all the group saving work. */ utfcgrep_prev = utfcgrep - 1; scanptr = (unsigned char *)mv->str.addr + utfcgrep_prev->byteidx; assert(scanptr < scantop); UTF_CHARTYPELEN(scanptr, scantop, chartype, bytecnt); /* Check type of last char in last cache group to the type of the next character to scan. If there's a * match, we can add it to the old group if the type is not UTF8, or if it is UTF8, then if there are * fewer than UTFCGR_MAX_UTF_LEN chars in the group. Else, we need to start a new group. */ COUNT_UTF_EVENT(parscan); /* Count partial scans we do */ if ((chartype == (lchar_typflags = utfcgrep_prev->typflags)) /* Note assignment */ && ((UTFCGR_UTF != lchar_typflags) || (UTFCGR_MAX_UTF_LEN > (utfcgrep_prev->charcnt - ((utfcgrep_prev != utfcgrep0) ? (utfcgrep_prev - 1)->charcnt : 0))))) { /* We can add chars to the previous last group - Restore scanning vars as if still in last group */ DBGUTFC((stderr, " utfcgr_scanforcharN: Adding newly scanned char to current group (type %d)\n", chartype)); utfcgrep = utfcgrep_prev; /* Fix things up to go back to prev group */ if (utfcgrep != utfcgrep0) { /* This is not the first entry[] so we can address the previous entry and set up our vars * as if the last group had not yet been terminated. */ tbyteidx = (utfcgrep - 1)->byteidx; tcharcnt = (utfcgrep - 1)->charcnt; gbytecnt = utfcgrep->byteidx - tbyteidx; gcharcnt = utfcgrep->charcnt - tcharcnt; } else { /* This is the first entry, use initial values */ tbyteidx = tcharcnt = 0; gbytecnt = utfcgrep->byteidx; gcharcnt = utfcgrep->charcnt; /* Start scan with chars already processed */ lchar_typflags = utfcgrep->typflags; } } else { /* Else, we are starting a new group - if one is available. The values are already initialized * correctly for the new group so fall into the (re)start of the scan after bumping relevant * counter(s). The one caveat is if there are no more slots available then we will fall into * a different (faster) scanner that doesn't try to save cache entries */ gbytecnt = 0; gcharcnt = 0; /* No chars added to this entry yet */ if (utfcgrep < pcentmax) { /* Account for new group when scanner loop went past pcuentmax above leaving * utfcgrep pointing to a new (unused) group. */ utfcgrp->ngrps++; DBGUTFC((stderr, " utfcgr_scanforcharN: NEW group for string 0x"lvaddr " (type %d) changing group count from %d to %d\n", mv->str.addr, chartype, utfcgrp->ngrps - 1, utfcgrp->ngrps)); utfcgrep->typflags = lchar_typflags = chartype; } else { DBGUTFC((stderr, " utfcgr_scanforcharN: No more group entries available\n")); } } } else { /* Cache was not validated - start from scratch (note tcharcnt and tbyteidx already initialized above) */ COUNT_UTF_EVENT(miss); utfcgrp = utfcgr_getcache(utf_parse_blk->mv); utfcgrep = utfcgrep0 = &utfcgrp->entry[0]; /* Point to first entry to add */ pcentmax = &utfcgrp->entry[TREF(gtm_utfcgr_string_groups)]; /* Pointer to last entry + 1 in this row */ utfcgrp->reference = TRUE; scanptr = (unsigned char *)mv->str.addr; lchar_typflags = UTFCGR_NONE; /* No type set yet */ gbytecnt = 0; gcharcnt = 0; /* No chars added to this entry yet */ bytecnt = 0; } /* (Re)Start the scan filling in scan blocks as we go. If we run out of scan blocks to fill in, drop down to the * scan that does not care about filling in blocks as it runs faster without the overhead. */ if (utfcgrep < pcentmax) { /* We have additional groups to use - fill them in as we either scan the string for the first time or * pick up where we left off last time. */ DBGUTFC((stderr, " utfcgr_scanforcharN: Entering main scan/parse\n")); noslots = cachemod = FALSE; for (; (scanptr < scantop) && (char_idx > (tcharcnt + gcharcnt));) { /* Scan one char at a time determining both type and length of each character */ cachemod = TRUE; /* We are actually adding to the scan */ UTF_CHARTYPELEN(scanptr, scantop, chartype, bytecnt); DBGUTFC((stderr, " utfcgr_scanforcharN: New char scanned - type: %d, current group type: %d\n", chartype, lchar_typflags)); if ((UTFCGR_BADCHAR == chartype) && utf_parse_blk->stoponbadchar) { /* Found BADCHAR in the string and this scan can't tolerate them */ DBGUTFC((stderr, " utfcgr_scanforcharN: Returning due to BADCHAR\n")); utf_parse_blk->badcharstr = scanptr; utf_parse_blk->badchartop = scantop; utf_parse_blk->scan_char_type = UTFCGR_BADCHAR; DUMP_UTFCACHE_END(mv, utfcgrp); return FALSE; } if ((chartype != lchar_typflags) || ((UTFCGR_UTF == lchar_typflags) && (UTFCGR_MAX_UTF_LEN < gcharcnt))) { /* Has the appearance of a change in character type or UTF8 len exceeded- check it */ if (UTFCGR_NONE == lchar_typflags) { /* First entry in a group so just set it and let it rip */ utfcgrep->typflags = lchar_typflags = chartype; DBGUTFC((stderr, " utfcgr_scanforcharN: Starting first group with type %d\n", chartype)); } else { /* We have had a character type change - close out current group and advance to the * next group if one is available. */ DBGUTFC((stderr, " utfcgr_scanforcharN: Character type change - old type %d, new " "type %d at string offset %d\n", lchar_typflags, chartype, tbyteidx + gbytecnt)); DBGUTFC((stderr, " utfcgr_scanforcharN: Closing group for string 0x"lvaddr " (oldtype %d type %d) with group count %d\n", mv->str.addr, utfcgrep->typflags, lchar_typflags, utfcgrp->ngrps)); assert((scanptr - (unsigned char *)mv->str.addr) == (tbyteidx + gbytecnt)); utfcgrep->byteidx = tbyteidx = tbyteidx + gbytecnt; utfcgrep->charcnt = tcharcnt = tcharcnt + gcharcnt; assert(lchar_typflags == utfcgrep->typflags); gbytecnt = gcharcnt = 0; if ((utfcgrep + 1) >= pcentmax) { /* No more slots available - fall to simpler scan */ noslots = TRUE; DBGUTFC((stderr, " utfcgr_scanforcharN: Last char group full - resorting " "to regular scan\n")); break; } DBGUTFC((stderr, " utfcgr_scanforcharN: New group for string 0x"lvaddr " (type %d) changing group count from %d to %d\n", mv->str.addr, chartype, utfcgrp->ngrps, utfcgrp->ngrps + 1)); utfcgrp->ngrps++; utfcgrep++; assert(utfcgrp->ngrps == ((utfcgrep - utfcgrep0) + 1)); utfcgrep->typflags = lchar_typflags = chartype; /* Slot available - store type */ } } /* Bump what needs bumping depending on character types */ gcharcnt++; /* Count char for this group */ switch(chartype) { case UTFCGR_ASCII: /* Both of these types are single-byte chars */ case UTFCGR_BADCHAR: scanptr++; gbytecnt++; break; case UTFCGR_UTF: /* Multi-byte chars */ scanptr += bytecnt; gbytecnt += bytecnt; break; default: assertpro(FALSE); } DBGUTFC((stderr, " utfcgr_scanforcharN: Bottom of scan loop after increment - tcharcnt: %d " "gcharcnt: %d tbyteidx: %d gbytecnt: %d\n", tcharcnt, gcharcnt, tbyteidx, gbytecnt)); } DBGUTFC((stderr, " utfcgr_scanforcharN: Scan loop exit - cache modded flag: %d\n", cachemod)); if (!noslots) { /* We didn't exit loop for lack of slots so we either reached our goal or ran out of text to parse. * Either one means we need to close the last group we were scanning and return either success * or failure. But before we close it, we can add the character to the group that we're looking * at (if there is one) and are going to return the position of to the buffer. */ assert((scanptr - (unsigned char *)mv->str.addr) == (tbyteidx + gbytecnt)); lchar_byteidx = tbyteidx + gbytecnt; /* Record info changed when fetch info on return char * and put it in cache. */ lchar_charcnt = tcharcnt + gcharcnt; DEBUG_ONLY(lchar_typflags = chartype); DBGUTFC((stderr, " utfcgr_scanforcharN: End-scan (!noslots) scan vars - lchar_byteidx: %d " " lchar_charcnt: %d lchar_typflags: %d\n", lchar_byteidx, lchar_charcnt, lchar_typflags)); if (scanptr < scantop) { /* The char to return info on is at lchar_byteidx. It's type is not currently known. There * is more text to scan so see what type/bytelen the character is. If we are able to, * once we determine this info, the char will be added to the cache to make the char easier * to find next time. */ UTF_CHARTYPELEN(scanptr, scantop, chartype, bytecnt); /* Get info on char to return */ DBGUTFC((stderr, " utfcgr_scanforcharN: scanning end char to add it to cache - new char" " type: %d, current type: %d, utfcgerp: 0x" lvaddr" - group: %d\n", chartype, utfcgrep->typflags, utfcgrep, (utfcgrep - utfcgrep0) + 1)); if ((chartype == utfcgrep->typflags) && ((scanptr + bytecnt) <= scantop)) { /* Char is same type and within same buffer - add to existing group */ scanptr += bytecnt; gcharcnt++; gbytecnt += bytecnt; cachemod = TRUE; /* Adding chars to cache */ DBGUTFC((stderr, " utfcgr_scanforcharN: char is same type as previous char - " "gbytecnt: %d, gcharcnt: %d, bytecnt: %d\n", gbytecnt, gcharcnt, bytecnt)); } else { /* Must create new group for this character to reside in if there is room */ if ((UTFCGR_BADCHAR == chartype) && (utf_parse_blk->stoponbadchar)) { /* But we have a BADCHAR we must report */ DBGUTFC((stderr, " utfcgr_scanforcharN: Returning due to BADCHAR\n")); /* Close the open group before return error */ utfcgrep->byteidx = tbyteidx + gbytecnt; utfcgrep->charcnt = tcharcnt + gcharcnt; utf_parse_blk->badcharstr = scanptr; utf_parse_blk->badchartop = scantop; utf_parse_blk->scan_char_type = UTFCGR_BADCHAR; DUMP_UTFCACHE_END(mv, utfcgrp); return FALSE; } DBGUTFC((stderr, " utfcgr_scanforcharN: character type change - old type %d, new " "type %d at string offset %d\n", utfcgrep->typflags , chartype, tbyteidx + gbytecnt)); if ((utfcgrep + 1) < pcentmax) { /* There is room - close old entry and allocate a new entry */ utfcgrep->byteidx = tbyteidx = tbyteidx + gbytecnt; utfcgrep->charcnt = tcharcnt = tcharcnt + gcharcnt; assert(utfcgrep->typflags == lchar_typflags); DBGUTFC((stderr, " utfcgr_scanforcharN: new group for string 0x"lvaddr " (type %d) changing group count from %d to %d\n", mv->str.addr, chartype, utfcgrp->ngrps, utfcgrp->ngrps + 1)); utfcgrep++; utfcgrp->ngrps++; assert(utfcgrp->ngrps == ((utfcgrep - utfcgrep0) + 1)); utfcgrep->typflags = chartype; /* Init type of new group */ gcharcnt = 1; gbytecnt = bytecnt; cachemod = TRUE; /* Adding chars to cache */ } else { /* Else - no room to store anything. Note if in debug mode, we need * to reset chartype back to lchar_typflags since we were unable to * cache the new char or create a new group for it. This allows later * asserts to work correctly. */ DBGUTFC((stderr, " utfcgr_scanforcharN: no room to create a new group -" " new char not added to cache\n")); DEBUG_ONLY(chartype = lchar_typflags); } } scaneol = FALSE; } else { /* We ran out of characters to scan - flag it */ scaneol = TRUE; DBGUTFC((stderr, " utfcgr_scanforcharN: Scan ended due to EOL\n")); } /* Cases: * * a. We hit the end of the scan by running out of text to parse so chartype is unchanged. * b. We looked at the char to return and it was the same type as the current group so char * is added to that group so chartype is again unchanged. * c. We looked at the char to return and it was a different type from the current group so we * created a new group to hold the new char so chartype has changed. * d. As in (c) but there were no new utfcgr_entrys available so no change to cache. * * If the cache was modified, close the last entry. This could be the entry last created by the * main scan loop or it could be a new group created to hold the char we are returning info about. */ if (cachemod) { /* We made some changes to the cache so close the current entry out */ DBGUTFC((stderr, " utfcgr_scanforcharN: changes made to cache this pass - close current" " group\n")); utfcgrep->byteidx = tbyteidx + gbytecnt; utfcgrep->charcnt = tcharcnt + gcharcnt; DBGUTFC((stderr, " utfcgr_scanforcharN: closing group 0x"lvaddr" for string 0x"lvaddr " (oldtype %d type %d) with group count %d - byteidx: %d charcnt: %d\n", utfcgrep, mv->str.addr, utfcgrep->typflags, chartype, utfcgrp->ngrps, utfcgrep->byteidx, utfcgrep->charcnt)); assert(utfcgrep->typflags == (scaneol ? lchar_typflags : chartype)); } else { /* This was just a "read" of existing cache - no updates necessary */ DBGUTFC((stderr, " utfcgr_scanforcharN: readonly use of cache - nothing to see here -" " move along\n")); } /* Fill in output values */ utf_parse_blk->scan_byte_offset = lchar_byteidx; utf_parse_blk->scan_char_count = lchar_charcnt; if (!scaneol && (char_idx == lchar_charcnt)) { /* We found our char */ utf_parse_blk->scan_char_len = bytecnt; utf_parse_blk->scan_char_type = chartype; utf_parse_blk->utfcgr_indx = utfcgrp->ngrps - 1; /* Save in utf_parse_blk as zero origin */ DUMP_UTFCACHE_END(mv, utfcgrp); assert((mv->str.addr + utf_parse_blk->scan_byte_offset + utf_parse_blk->scan_char_len) <= (char *)scantop); /* Verify entire char we pass back exists within string */ return TRUE; } else { /* The char was not found */ utf_parse_blk->scan_char_len = 0; utf_parse_blk->utfcgr_indx = -1; /* Bogus return value when not found */ utf_parse_blk->scan_char_type = UTFCGR_EOL; DUMP_UTFCACHE_END(mv, utfcgrp); return FALSE; } } /* This is when we are leaving the cache area but are continuing with the brute force scan */ DUMP_UTFCACHE_END(mv, utfcgrp); } /* Falling through to here means to continue the scan without saving any group entries because we ran out * of slots to put them in. We use the same scan as if there was no cache involved at all. */ COUNT_UTF_EVENT(parhscan); skip = char_idx - tcharcnt; /* Bytes left to scan to find target char */ } else { /* This string is too small to use cache on - do the simple scan in brute force mode */ COUNT_UTF_EVENT(small); scanptr = (unsigned char *)mv->str.addr + utf_parse_blk->scan_byte_offset; if (0 < utf_parse_blk->scan_byte_offset) { /* If an offset was specified, pickup the char offset we started looking at */ tcharcnt = utf_parse_blk->scan_char_count; assert(0 < tcharcnt); skip = char_idx - tcharcnt; } else { tcharcnt = 0; skip = char_idx; } } assert(0 <= skip); /* This scan is used two ways: * * 1. When the string is too small to have a cache associated with it. * 2. When the cache for a given string is full so all additional scanning needs to be as fast as possible. * * The code expects the following vars setup: * * scanptr - Points to characters to scan. * scantop - Points to end of scanning chars + 1. * skip - The number of chars to skip before returning the "next" character. * utf_parse_blk - Pointer to parse block where results are stored for return to user. */ DBGUTFC((stderr, " utfcgr_scanforcharN: Begin non-cacheing scan with skip=%d\n", skip)); lastcharbad = FALSE; for (; (0 < skip) && (scanptr < scantop); skip--, scanptr += bytecnt, tcharcnt++) { /* Advance the string to locate the desired character */ if ((!UTF8_VALID(scanptr, scantop, bytecnt)) && utf_parse_blk->stoponbadchar) { /* Found BADCHARs in the string and this scan can't tolerate them */ utf_parse_blk->badcharstr = scanptr; utf_parse_blk->badchartop = scantop; utf_parse_blk->scan_char_type = UTFCGR_BADCHAR; return FALSE; } } /* If we found the character, return it (or BADCHAR indicator) - else return char-not-found */ utf_parse_blk->scan_char_count = tcharcnt; if ((0 == skip) && (scanptr < scantop)) { /* Character position was found */ assert(tcharcnt == char_idx); if ((lastcharbad = !UTF8_VALID(scanptr, scantop, bytecnt)) && utf_parse_blk->stoponbadchar) /* Note assignment */ { /* Spotted a BADCHAR and we aren't tolerating those at this time */ utf_parse_blk->badcharstr = scanptr; utf_parse_blk->badchartop = scantop; utf_parse_blk->scan_char_type = UTFCGR_BADCHAR; return FALSE; } utf_parse_blk->scan_byte_offset = scanptr - (unsigned char *)mv->str.addr; utf_parse_blk->scan_char_len = bytecnt; utf_parse_blk->scan_char_type = (1 != bytecnt) ? UTFCGR_UTF : (lastcharbad ? UTFCGR_BADCHAR : UTFCGR_ASCII); utf_parse_blk->utfcgr_indx = -1; /* No cache, no index */ return TRUE; } else { /* Character position was not found - no usable values returned */ utf_parse_blk->scan_byte_offset = mv->str.len; /* Note utf_parse_blk->scan_char_count already set above */ utf_parse_blk->scan_char_len = 0; utf_parse_blk->scan_char_type = UTFCGR_EOL; return FALSE; } } #ifdef DEBUG void utfcgr_stats(void) { FPRINTF(stderr, "\nUTF cache hits: %d\n", u_hit); FPRINTF(stderr, "UTF cache misses: %d\n", u_miss); FPRINTF(stderr, "UTF brute force string scans: %d\n", u_small); FPRINTF(stderr, "Number of groups skipped %d\n", u_pskip); FPRINTF(stderr, "Number of UTF groups scanned for located char: %d\n", u_puscan); FPRINTF(stderr, "Number of non-UTF groups scanned for located char: %d\n", u_pabscan); FPRINTF(stderr, "Number of partial scans (partial cache hits): %d\n", u_parscan); FPRINTF(stderr, "Number of partial scans after filled slots: %d\n", u_parhscan); } #endif /* DEBUG */ #endif /* UTF8_SUPPORTED */ fis-gtm-V7.0-005/sr_unix/utfcgr.h0000644000032200000250000001774514342376330015522 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015 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. * * * ****************************************************************/ /* Header file for using UTF8 lookaside cache to aid in scanning of UTF8 strings. Similar * in concept to the fnpc.h lookaside cache for $PIECE() performance improvements except * here, we are logging mode changes between BADCHARs, UTF8 and ASCII character groups. */ #ifndef UTFCGR_INCLUDED #define UTFCGR_INCLUDED #include "utfcgr_trc.h" /* Debugging counters */ #ifdef DEBUG GBLREF uint4 process_id; GBLREF int u_miss; /* UTF cache misses (debug) */ GBLREF int u_hit; /* UTF cache hits (debug) */ GBLREF int u_small; /* UTF scanned small string brute force (debug) */ GBLREF int u_pskip; /* Number of UTF groups "skipped" (debug) */ GBLREF int u_puscan; /* Number of groups "scanned" for located char (debug) */ GBLREF int u_pabscan; /* Number of non-UTF groups we scan for located char (debug) */ GBLREF int u_parscan; /* Number of partial scans (partial cache hits) (debug) */ GBLREF int u_parhscan; /* Number of partial scans after filled slots (debug) */ # define COUNT_UTF_EVENT(X) ++u_##X; # define COUNT_UTF_INCR(X, Y) u_##X += Y; #else # define COUNT_UTF_EVENT(X) # define COUNT_UTF_INCR(X, Y) #endif /* Macro to determine the type and length of a character. * Arguments: * CPTR - pointer to first byte of character. * CTOPPTR - pointer to last byte of string (max possible part of char). * CTYPE - variable where determined type is stored. * CLEN - length in bytes of character. */ #define UTF_CHARTYPELEN(CPTR, CTOPPTR, CTYPE, CLEN) \ MBSTART { \ if (ASCII_MAX >= *(CPTR)) \ { /* We have an ASCII char */ \ CTYPE = UTFCGR_ASCII; \ CLEN = 1; \ } else \ /* We have a UTF8 or BADCHAR type char - bytelen is set appropriately */ \ CTYPE = ((UTF8_VALID((CPTR), (CTOPPTR), CLEN)) ? UTFCGR_UTF : UTFCGR_BADCHAR); \ } MBEND /* Define defaults and limits of the utfcgr structures. Note the defaults here are not chosen by any scientific * principles but are our current best guess at what will work for the largest group of customers. */ #define GTM_UTFCGR_STRINGS_DEFAULT 50 /* Default max number of strings to cache scan results for ($gtm_utfcgr_strings) */ #define GTM_UTFCGR_STRINGS_MAX 254 /* Value is a single byte and 255 is used as "invalid value" flag */ #define GTM_UTFCGR_STRING_GROUPS_DEFAULT 32 /* Default max char groups cached per string ($gtm_utfcgr_string_groups) */ #define UTFCGR_STRLEN_MIN 33 /* Minimum (byte) length string that creates a cache */ #define UTFCGR_MAX_UTF_LEN (UTFCGR_STRLEN_MIN * 2) /* Maximum byte length for a UTF8 group in cache - promotes scanning * as have to scan at most this many bytes in a group - allows approx * UTFCGR_STRLEN_MIN UTF8 chars in a string averaging 2 bytes each. */ #define UTFCGR_MAXLOOK_DIVISOR 5 /* Value to divide into TREF(gtm_utfcgr_strings) to get number of spins to locate * an available cache line (skipping slots with reserve flag set) before we * simply overwrite one. Used to compute TREF(utfcgr_string_lookmax). */ /* Flags for utfcgr.entry[].typflags. If no flags set, group is of unknown type. Note there's no specific purpose for making these * actual bit flags - they could just become values but the thought was there might be additional flags in the future where * it would matter. */ #define UTFCGR_NONE 0x00 /* Group has an as-yet undefined type */ #define UTFCGR_ASCII 0x01 /* Group is all ASCII */ #define UTFCGR_BADCHAR 0x02 /* Group is BADCHAR(s) */ #define UTFCGR_UTF 0x04 /* Group is all UTF8 (no BADCHARs) */ #define UTFCGR_EOL 0x08 /* Used in utfscan_parseblk->char_type to indicate ran into EOL during scan */ /* Structure for each character group in a given string. The typflags field describes the group of characters that start after * the end of the last character group until the start of the next character group. All of the characters are of the same type. * The charcnt and byteidx fields describe the start (in character count and byte index) of the *next* group of characters. * So entry[0] gives the flags for entry[0] but the count/offset for the start of entry[1] when parsing. */ typedef struct utfcgr_entry_struct { uint4 typflags:8; /* Byte full 'o flag bits for THIS group */ uint4 charcnt:24; /* Total count of characters at start of next group */ uint4 byteidx; /* Offset in bytes from start of entire string to start of next group */ } utfcgr_entry; /* Structure for each recently used string above the string length minimum that describes the string and * notes where its transition points are between BADCHAR, UTF8 and ASCII. We refer to these entries as * "groups" but note they are different than $PIECE() type pieces which have a given delimiter. Here, * the "groups" are delimited not by characters but by character TYPE. */ typedef struct utfcgr_struct { mstr last_str; /* The last string (addr/len) we used in cache */ unsigned short ngrps; /* Number of groups for which values are filled in */ unsigned short idx; /* The index of this group in the entry[] array */ boolean_t reference; /* Reference bit(s) to prevent overwrite if possible */ utfcgr_entry entry[1]; /* Table of char groups for this string. This is a variable dimension * field - dimension is in TREF(gtm_utfcgr_string_groups). */ } utfcgr; /* Structure for the entire allocation for UTF scan cache */ typedef struct { utfcgr *utfcgrsteal; /* Last stolen cache element */ utfcgr *utfcgrmax; /* (use addrs to avoid array indexing) */ utfcgr *utfcgrs; /* Ptr to variable dimension array which has TREF(gtm_utfcgr_strings) entries */ uint4 utfcgrsize; /* Size of 1 utfcgr entry (varies depending on TREF(gtm_utfcgr_string_groups) */ } utfcgr_area; /* This structure is for the scan descriptor used by the UTF scanning/parsing routines */ typedef struct { mval *mv; /* Addr of mval this scan targets. Note this mval should be known to * garbage collection so it is kept up-to-date across GC events (input but * note mv may be updated with mv->utfcgr_indx field set). */ boolean_t stoponbadchar; /* TRUE - stops scan at badchar and returns with next two fields set * FALSE - keeps scanning counting badchars as 1 byte (input) */ int scan_byte_offset; /* Byte offset (0 origin) where scan should start or where it ended (on return). * If 0, starts/ended at beginning and next two fields are ignored. All 3 fields * must be in sync or weird stuff can happen (input and output). */ int scan_char_count; /* Char count (1 origin) of the characters behind scan_byte_offset and does NOT * include the character at that offset. */ int utfcgr_indx; /* utfcgr_entry index (0 origin) where scan should start or where it ended on * return (input and output). */ int scan_char_len; /* Byte length of the character whose offset/index we are returning (input and * output but not updated when scan_char_type is UTFCGR_EOL). */ int scan_char_type; /* Character type of returned character position (output) */ unsigned char *badcharstr; /* Location of badchar if found while scanning (output). Note this field and the * next only updated when scan_char_type is UTFCGR_BADCHAR and we return FALSE. */ unsigned char *badchartop; /* End of string to pass to utf8_badchar() (output) */ } utfscan_parseblk; /* Entry point declarations */ utfcgr *utfcgr_getcache(mval *mv); boolean_t utfcgr_scanforcharN(int char_num, utfscan_parseblk *utf_parse_blk); #ifdef DEBUG void utfcgr_stats(void); #endif #endif fis-gtm-V7.0-005/sr_unix/utfcgr_trc.h0000644000032200000250000000212314342376330016352 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2018 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. * * * ****************************************************************/ #ifndef UTFCGR_TRC_INCLUDED #define UTFCGR_TRC_INCLUDED /* Debugging macros for UTF8 caching. These are separate from other macros so they can be * included regardless of whether UTF8_SUPPORTED is set or not. * * Uncomment below define to enable debugging macros */ /* #define DEBUG_UTF8CACHE */ #if defined(UTF8_SUPPORTED) && defined(DEBUG_UTF8CACHE) # define DBGUTFC(x) DBGFPF(x) # define DBGUTFC_ONLY(x) x # include "gtm_stdio.h" # include "gtmio.h" # include "have_crit.h" /* For DBGFPF/FFLUSH/INTRPT_IN_FFLUSH */ #else # define DBGUTFC(x) # define DBGUTFC_ONLY(x) #endif #endif fis-gtm-V7.0-005/sr_unix/util_exit_handler.c0000644000032200000250000000645114342376330017716 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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 "gdsroot.h" #include "gdsbt.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "error.h" #include "gt_timer.h" #include "util.h" #include "gv_rundown.h" #include "print_exit_stats.h" #include "repl_msg.h" /* needed for jnlpool_addrs_ptr_t */ #include "gtmsource.h" /* needed for jnlpool_addrs_ptr_t */ #include "secshr_db_clnup.h" #include "gtmimagename.h" #include "dpgbldir.h" #include "gtmcrypt.h" #include "interlock.h" #include "mlkdef.h" #include "mlk_ops.h" GBLREF boolean_t need_core; GBLREF boolean_t created_core; GBLREF boolean_t exit_handler_active; GBLREF boolean_t skip_exit_handler; GBLREF uint4 dollar_tlevel; GBLREF uint4 process_id; void util_exit_handler() { int stat; gd_region *r_top, *reg; sgmnt_addrs *csa; gd_addr *addr_ptr; if (exit_handler_active || skip_exit_handler) /* Skip exit handling if specified or if exit handler already active */ return; exit_handler_active = TRUE; SET_PROCESS_EXITING_TRUE; /* Set this BEFORE canceling timers as wcs_phase2_commit_wait relies on this */ if (IS_DSE_IMAGE) { /* Need to clear csa->hold_onto_crit in case it was set. This needs to be done before the call to * secshr_db_clnup() which, if we still hold it, will take care of releasing crit at the appropriate point. */ for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) { for (reg = addr_ptr->regions, r_top = reg + addr_ptr->n_regions; reg < r_top; reg++) { if (reg->open && !reg->was_open) { csa = &FILE_INFO(reg)->s_addrs; csa->hold_onto_crit = FALSE; /* need to do this before the rel_crit */ /* If this is an normal (non-error) exit (as determiend by the severity var), go ahead * and release crit if we are holding it as that secshr_db_clnup() from forcing an * unneeded cache recovery. However, if this *IS* an error condition, we leave crit * alone and let secshr_db_clnup() deal with it appropriately. */ if ((0 == severity) && !csa->lock_crit_with_db && LOCK_CRIT_HELD(csa)) { mlk_pvtctl pctl; MLK_PVTCTL_INIT(pctl, reg); REL_LOCK_CRIT(pctl, FALSE); } if ((0 == severity) && csa->now_crit) rel_crit(reg); } } } } CANCEL_TIMERS; /* Cancel all unsafe timers - No unpleasant surprises */ /* Note we call secshr_db_clnup() with the flag NORMAL_TERMINATION even in an error condition * here because we know at this point that we aren't in the middle of a transaction but we may * be holding crit in one or more regions and/or we could have other odds/ends to cleanup. */ secshr_db_clnup(NORMAL_TERMINATION); WITH_CH(exi_ch, gv_rundown(), 0); print_exit_stats(); util_out_close(); GTMCRYPT_CLOSE; if (need_core && !created_core) DUMP_CORE; } fis-gtm-V7.0-005/sr_unix/util_help.c0000644000032200000250000000534314342376330016177 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2021 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 "util_help.h" #include "gtm_limits.h" /* for GTM_PATH_MAX */ #include "gtm_stdio.h" /* for snprintf() */ #include "gtm_string.h" /* for strlen() */ #include "gtm_stdlib.h" /* for gtm_system_internal() */ #include "gtmimagename.h" /* for struct gtmImageName */ GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF boolean_t gtm_dist_ok_to_use; LITREF gtmImageName gtmImageNames[]; error_def(ERR_TEXT); error_def(ERR_GTMDISTUNVERIF); #define MUMPS_CMD_STRING_SIZE 8 + GTM_PATH_MAX #define EXEC_MUMPS "%s/mumps" #define HELP_CMD_STRING_SIZE 256 + GTM_PATH_MAX #define EXEC_GTMHELP "do ^GTMHELP(\"%s\",\"%s/%shelp.gld\")", #define UTIL_HELP_IMAGES 5 /* We need the first two entries for compatibility */ char *utilImageGLDs[UTIL_HELP_IMAGES] = { #define IMAGE_TABLE_ENTRY(A,B) B, IMAGE_TABLE_ENTRY (INVALID_IMAGE, "") IMAGE_TABLE_ENTRY (GTM_IMAGE, "gtm") IMAGE_TABLE_ENTRY (MUPIP_IMAGE, "mupip") IMAGE_TABLE_ENTRY (DSE_IMAGE, "dse") IMAGE_TABLE_ENTRY (LKE_IMAGE, "lke") #undef IMAGE_TABLE_ENTRY }; void util_help(void) { int rc; char *help_option; char mumps_cmd_string[MUMPS_CMD_STRING_SIZE]; char help_cmd_string[HELP_CMD_STRING_SIZE]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(1 >= TREF(parms_cnt)); assert(GTM_IMAGE < image_type && UTIL_HELP_IMAGES > image_type); if (!gtm_dist_ok_to_use) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_GTMDISTUNVERIF, 4, LEN_AND_STR(gtm_dist), gtmImageNames[image_type].imageNameLen, gtmImageNames[image_type].imageName); if (0 == TREF(parms_cnt)) help_option = utilImageGLDs[INVALID_IMAGE]; else { assert(TAREF1(parm_ary, TREF(parms_cnt) - 1)); assert((char *)-1L != (TAREF1(parm_ary, TREF(parms_cnt) - 1))); help_option = (TAREF1(parm_ary, TREF(parms_cnt) - 1)); } /* if help_cmd_string is not long enough, the following command will fail */ SNPRINTF(mumps_cmd_string, SIZEOF(mumps_cmd_string), EXEC_MUMPS, gtm_dist); SNPRINTF(help_cmd_string, SIZEOF(help_cmd_string), EXEC_GTMHELP help_option, gtm_dist, utilImageGLDs[image_type]); rc = gtm_system_internal(mumps_cmd_string, "-run", "%XCMD", help_cmd_string); if (WIFEXITED(rc)) rc = WEXITSTATUS(rc); if (0 != rc) rts_error_csa(NULL, VARLSTCNT(5) ERR_TEXT, 2, RTS_ERROR_TEXT("HELP command error"), rc); } fis-gtm-V7.0-005/sr_unix/util_help.h0000644000032200000250000000102314342376330016173 0ustar librarygtc/**************************************************************** * * * Copyright 2013 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. * * * ****************************************************************/ #ifndef UTIL_HELP_INCLUDED #define UTIL_HELP_INCLUDED void util_help(void); #endif fis-gtm-V7.0-005/sr_unix/util_in_open.c0000755000032200000250000000102714342376330016674 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "util.h" /* STUB */ void util_in_open(void *ptr) { return; } fis-gtm-V7.0-005/sr_unix/util_input.c0000755000032200000250000000760114342376330016410 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2018 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_ctype.h" #include #include "gtm_stdio.h" #include "gtm_string.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" #include "gtm_utf8.h" #endif #include "util.h" #ifdef UTF8_SUPPORTED GBLREF boolean_t gtm_utf8_mode; #endif #define MAX_LINE (32767+256) /* see cli.h */ /* Get one line of input from file stream fp and * trim any line terminators. * * buffersize is the size in bytes of the buffer * where the line is returned and includes the * terminating null. * buffersize must be less than or equal to MAX_LINE * for UTF8 * * If the return is NULL, there was an error * otherwise it is the start of the line. * If remove_leading_spaces, the return value * will point at the first non space, according * to isspace_asscii. Note that this may not be * the beginning of buffer. */ char *util_input(char *buffer, int buffersize, FILE *fp, boolean_t remove_leading_spaces) { size_t in_len; char *retptr; #ifdef UTF8_SUPPORTED int mbc_len, u16_off, non_space_off; int32_t mbc_dest_len; boolean_t found_non_space = FALSE; UFILE *u_fp; UChar *uc_fgets_ret, ufgets_Ubuffer[MAX_LINE]; UChar32 uc32_cp; UErrorCode errorcode; #endif #ifdef UTF8_SUPPORTED if (gtm_utf8_mode) { assert(MAX_LINE >= buffersize); ufgets_Ubuffer[0] = 0; u_fp = u_finit(fp, NULL, UTF8_NAME); if (NULL != u_fp) { do { /* no u_ferror */ uc_fgets_ret = u_fgets(ufgets_Ubuffer, (int32_t)(SIZEOF(ufgets_Ubuffer) / SIZEOF(UChar)) - 1, u_fp); } while (NULL == uc_fgets_ret && !u_feof(u_fp) && ferror(fp) && EINTR == errno); if (NULL == uc_fgets_ret) { if (!u_feof(u_fp)) util_out_print("Error reading from STDIN", TRUE); u_fclose(u_fp); return NULL; } in_len = u_strlen(ufgets_Ubuffer); in_len = trim_U16_line_term(ufgets_Ubuffer, (int4)in_len); for (non_space_off = u16_off = mbc_len = 0; u16_off < in_len && mbc_len < (buffersize - 1); ) { U16_NEXT(ufgets_Ubuffer, u16_off, in_len, uc32_cp); /* updates u16_off */ if (remove_leading_spaces && !found_non_space) if (U_ISSPACE(uc32_cp)) continue; else { found_non_space = TRUE; non_space_off = u16_off; U16_BACK_1(ufgets_Ubuffer, 0, non_space_off); /* get non space offset */ } mbc_len += U8_LENGTH(uc32_cp); } if (mbc_len >= (buffersize - 1)) { U16_BACK_1(ufgets_Ubuffer, 0, u16_off); in_len = u16_off >= 0 ? u16_off + 1 : 0; /* offset to length */ } errorcode = U_ZERO_ERROR; u_strToUTF8(buffer, buffersize, &mbc_dest_len, &ufgets_Ubuffer[non_space_off], (int4)in_len - non_space_off + 1, &errorcode); /* include null */ if (U_FAILURE(errorcode)) if (U_BUFFER_OVERFLOW_ERROR == errorcode) { /* truncate so null terminated */ buffer[buffersize - 1] = 0; retptr = buffer; } else retptr = NULL; else retptr = buffer; u_fclose(u_fp); } else retptr = NULL; } else { #endif buffer[0] = '\0'; do { FGETS(buffer, buffersize, fp, retptr); } while (NULL == retptr && !feof(fp) && ferror(fp) && EINTR == errno); if (NULL != retptr) { if (remove_leading_spaces) while (*retptr && ISSPACE_ASCII(*retptr)) retptr++; in_len = strlen(buffer); if ('\n' == buffer[in_len - 1]) buffer[in_len - 1] = '\0'; } else { if (!feof(fp)) util_out_print("Error reading from STDIN", TRUE); } #ifdef UTF8_SUPPORTED } #endif return retptr; } fis-gtm-V7.0-005/sr_unix/util_out_print_gtmio.c0000644000032200000250000001123014342376330020461 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2022 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" #ifdef GTM_TRIGGER #include "gtm_string.h" #include "gtm_multi_thread.h" #include "util.h" /* for FLUSH and util_out_print */ #include "util_out_print_vaparm.h" #include "op.h" /* for op_write prototype */ #include "io.h" /* needed for io_pair typedef */ #include "gtmimagename.h" /* for IS_MCODE_RUNNING */ GBLREF uint4 dollar_tlevel; #define ZTRIGBUFF_INIT_ALLOC 1024 /* start at 1K */ #define ZTRIGBUFF_INIT_MAX_GEOM_ALLOC 1048576 /* stop geometric growth at this value */ /* Used by MUPIP TRIGGER or $ZTRIGGER routines to buffer trigger output until TCOMMIT time * (as otherwise we might display stale output due to a restarted try). */ void util_out_print_gtmio(caddr_t message, int flush, ...) { va_list var; char *src, *dst, *newdst; int srclen, dstlen, dstalloc, newlen, ptrlen; caddr_t msg; int4 msglen; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ASSERT_SAFE_TO_UPDATE_THREAD_GBLS; /* we expect all trigger operations (SELECT, LOAD, etc.) to happen inside TP. exceptions should set TREF variable */ assert(dollar_tlevel || TREF(gtmio_skip_tlevel_assert)); va_start(var, flush); /* If "!AD" has been specified as the message, skip the util_out_print_vaparm call as it is possible * the input string length is > the maximum length util_out_* routines are designed to handle. * In that case, we dont need the parameter substitution anyways so do a memcpy instead. */ if (STRCMP(message, "!AD") || (FLUSH != flush)) { util_out_print_vaparm(message, NOFLUSH_OUT, var, MAXPOSINT4); /* WARNING: UNCHECKED faocnt - internal use only */ src = TREF(util_outbuff_ptr); assert(NULL != TREF(util_outptr)); srclen = INTCAST(TREF(util_outptr) - src) + 1; /* 1 is for '\n' */ assert(OUT_BUFF_SIZE >= srclen); } else { srclen = (int)va_arg(var, int4) + 1; src = (char *)va_arg(var, caddr_t); } if (FLUSH == flush) { dstalloc = TREF(ztrigbuffAllocLen); dstlen = TREF(ztrigbuffLen); /* Leave room for terminating '\0' (after the \n) for later use in FPRINTF in tp_ztrigbuff_print. * Hence the use of "<=" instead of a "<" in the "if (dstalloc <= (dstlen + srclen))" check below. */ if (dstalloc <= (dstlen + srclen)) { /* reallocate */ dst = TREF(ztrigbuff); do { if (!dstalloc) dstalloc = ZTRIGBUFF_INIT_ALLOC; /* Allocate a 1K buffer at start */ else if (ZTRIGBUFF_INIT_MAX_GEOM_ALLOC <= dstalloc) dstalloc += ZTRIGBUFF_INIT_MAX_GEOM_ALLOC; else dstalloc = dstalloc * 2; /* grow geometrically until a limit and linearly after that */ } while (dstalloc <= (dstlen + srclen)); newdst = malloc(dstalloc); if (dstlen) memcpy(newdst, dst, dstlen); TREF(ztrigbuff) = newdst; TREF(ztrigbuffAllocLen) = dstalloc; if (NULL != dst) free(dst); } dst = TREF(ztrigbuff); memcpy(dst + dstlen, src, srclen - 1); dst[dstlen + srclen - 1] = '\n'; TREF(ztrigbuffLen) += srclen; TREF(util_outptr) = TREF(util_outbuff_ptr); /* Signal text is flushed */ } va_end(TREF(last_va_list_ptr)); va_end(var); } void tp_ztrigbuff_print(void) { mval flushtxt; char *ptr, *ptrtop, *ptr2; int len; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* If mumps process doing trigger operations, use GTM IO routines. If not use util_out_print. The only * exception is if the MUPIP caller wants to use current IO device (i.e. it has it all set up). In that * case the TREF ztrig_use_io_curr_device would have been set to TRUE. */ if (IS_MCODE_RUNNING || TREF(ztrig_use_io_curr_device)) { ptr = TREF(ztrigbuff); assert('\n' == ptr[TREF(ztrigbuffLen) - 1]); ptrtop = ptr + TREF(ztrigbuffLen); flushtxt.mvtype = MV_STR; do { len = INTCAST(ptrtop - ptr); ptr2 = memchr(ptr, '\n', len); assert(NULL != ptr2); flushtxt.str.addr = ptr; flushtxt.str.len = ptr2 - ptr; op_write(&flushtxt); op_wteol(1); ptr = ptr2 + 1; } while (ptr < ptrtop); } else { /* Use util_out_print but since you pass TRUE for flush, use -1 to prevent duplicate newline */ assert('\n' == (TREF(ztrigbuff))[TREF(ztrigbuffLen) - 1]); assert(TREF(ztrigbuffLen) < TREF(ztrigbuffAllocLen)); (TREF(ztrigbuff))[TREF(ztrigbuffLen)] = '\0'; FPRINTF(stderr, "%s", TREF(ztrigbuff)); } } #endif fis-gtm-V7.0-005/sr_unix/util_out_print_vaparm.h0000755000032200000250000000112414342376330020641 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef UTIL_OUT_PRINT_VAPARM #define UTIL_OUT_PRINT_VAPARM void util_out_print_vaparm(caddr_t message, int flush, va_list var, int faocnt); #endif fis-gtm-V7.0-005/sr_unix/util_output.c0000644000032200000250000007724414342376330016620 0ustar librarygtc /**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include #include "gtm_stdio.h" #include "gtm_syslog.h" #include #include "gtm_multi_thread.h" #include "io.h" #include "error.h" #include "fao_parm.h" #include "min_max.h" #include "hashtab_mname.h" #include "util.h" #include "util_format.h" #include "util_out_print_vaparm.h" #include "gtmimagename.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "repl_shutdcode.h" #include "gtmsource.h" #include "gtmrecv.h" #include "repl_instance.h" #include "trans_log_name.h" #include "gtmio.h" #include "gtm_logicals.h" #include "have_crit.h" #include "gtm_multi_proc.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" #include "gtm_utf8.h" #endif GBLDEF boolean_t first_syslog = TRUE; /* Global for a process - not thread specific */ GBLDEF char facility[MAX_INSTNAME_LEN + 100]; #ifdef DEBUG GBLDEF boolean_t util_out_print_vaparm_flush; #endif GBLREF boolean_t blocksig_initialized, err_same_as_out, hup_on, is_rcvr_server, is_src_server, is_updproc, prin_in_dev_failure; GBLREF io_pair io_std_device; GBLREF gd_addr *gd_header; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF recvpool_addrs recvpool; GBLREF sigset_t block_sigsent; GBLREF uint4 is_updhelper, process_id; GBLREF VSIG_ATOMIC_T forced_exit; error_def(ERR_REPLINSTACC); error_def(ERR_TERMHANGUP); error_def(ERR_TEXT); #define ZTRIGBUFF_INIT_ALLOC 1024 /* start at 1K */ #define ZTRIGBUFF_INIT_MAX_GEOM_ALLOC 1048576 /* stop geometric growth at this value */ /* #GTM_THREAD_SAFE : The below macro (GETFAOVALDEF) is thread-safe because caller ensures serialization with locks */ #define GETFAOVALDEF(faocnt, var, type, result, defval) \ { \ assert(IS_PTHREAD_LOCKED_AND_HOLDER); \ if (faocnt > 0) \ { \ result = (type)va_arg(var, type); \ faocnt--; \ } else \ result = defval; \ } /* #GTM_THREAD_SAFE : The below macro (INSERT_MARKER) is thread-safe because caller ensures serialization with locks */ #define INSERT_MARKER \ { \ assert(IS_PTHREAD_LOCKED_AND_HOLDER); \ MEMCPY_LIT(offset, "-"); \ offset += STRLEN("-"); \ } /* #GTM_THREAD_SAFE : The below macro (BUILD_FACILITY) is thread-safe because caller ensures serialization with locks */ #define BUILD_FACILITY(strptr) \ { \ assert(IS_PTHREAD_LOCKED_AND_HOLDER); \ memcpy(offset, strptr, STRLEN(strptr)); \ offset += STRLEN(strptr); \ } /* * This routine implements a SUBSET of FAO directives, namely: * * !/ !_ !^ !! * * * !mAC !mAD !mAF !mAS !mAZ * * !mSB !mSW !mSL * * !mUB !mUW !mUL !m@UJ !m@UQ * * !mXB !mXW !mXL !mXJ !m@XJ !m@XQ * * !mZB !mZW !mZL * * !n*c * * !@ZJ !@XJ !@ZJ !@ZQ * * Where `m' is an optional field width, `n' is a repeat count, and `c' is a single character. * `m' or `n' may be specified as the '#' character, in which case the value is taken from the next parameter. * * FAO stands for "formatted ASCII output". The FAO directives may be considered equivalent to format * specifications and are documented with the VMS Lexical Fuction F$FAO in the OpenVMS DCL Dictionary. * * The @XH and @XJ types need special mention. XH and XJ are ascii formatting of addresses and integers respectively. BOTH are * ASCII formatted hexdecimal output of a 64 bit sign-extended value. The present implementation of util_output does not * support 'H'. This support was new in VMS 7.2 (and is one reason why GTM 4.2 requires VMS 7.2). The "@" designates an * "indirect" request meaning that the address of the 8 byte item is passed rather than the item itself. This is what allows * us to print 8 byte values in the non-Alpha 32 bit parameter worlds. These types are documented in the VMS System services * manual under SYS$FAO. There are several other types that are supported on VMS but only these two were added on Unix. * * Another variant of the 'J' type is !mXJ which the routine implements. This variant is used to print 'addresses' in platform * independent way. For examples of this type, see the definition and usages of the following messages: * * CALLERID * KILLBYSIGSINFO1 * KILLBYSIGSINFO2 * * One important caveat in using !mXJ variant is that the input value is expected to be 4-byte on 32-bit platforms and 8-byte * on 64-bit platforms. Passing an 8-byte quantity on a 32-bit platform can cause SIGSEGV. If a field is always 8-bytes on both * the 32 and 64 bit platforms (like transaction numbers), use 0x!16@XQ variant instead. * * In addition, this routine also implements another set of directives * * !RmAC !RmAD !RmAF !RmAS !RmAZ * * This implements the !mAx equivalent but does right-justification of the string instead of left-justification. */ /* * util_format - convert FAO format string to C PRINTF format string. * * input arguments: * message - one of the message strings from, for example, merrors.c * fao - list of values to be inserted into message according to * the FAO directives * size - size of buff * * output argument: * buff - will contain C PRINTF-style format statement with any * "A" (character) fields filled in from fao list * * output global value: * outparm[] - array of numeric arguments from fao list (character * arguments already incorporated into buff * */ /* #GTM_THREAD_SAFE : The below function (util_format) is thread-safe because caller ensures serialization with locks */ caddr_t util_format(caddr_t message, va_list fao, caddr_t buff, ssize_t size, int faocnt) { desc_struct *d; signed char schar; unsigned char type, type2; caddr_t c, ctop, outptr, outtop, outtop1, message_next, message_top; uchar_ptr_t ret_ptr; unsigned char uchar; short sshort, *s; unsigned short ushort; int i, nexti, length, field_width, repeat_count, int_val, chwidth, orig_chwidth, cwidth; unsigned int ch; UINTPTR_T addr_val; ssize_t chlen; uint4 stringlength; boolean_t indirect; qw_num_ptr_t val_ptr; unsigned char numa[22]; unsigned char *numptr, *prefix; boolean_t right_justify, isprintable, line_begin; int prefix_len; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(IS_PTHREAD_LOCKED_AND_HOLDER); VAR_COPY(TREF(last_va_list_ptr), fao); if (multi_proc_in_use) { /* If we are about to flush the output to stdout/stderr, we better hold a latch (since we dont want * parallel processes mixing their outputs in the same device. The only exception is if caller * "util_out_print_vaparm" has been called without "FLUSH" argument which means the output goes to * a string or syslog or unflushed buffer. It is ok to not hold the latch in those cases. Assert that. */ assert((process_id == multi_proc_shm_hdr->multi_proc_latch.u.parts.latch_pid) || (FLUSH != util_out_print_vaparm_flush)); /* By similar reasoning as the above assert, assert on the key prefix */ assert((NULL != multi_proc_key) || multi_proc_key_exception || (FLUSH != util_out_print_vaparm_flush)); prefix = multi_proc_key; prefix_len = (NULL == prefix) ? 0 : STRLEN((char *)prefix); line_begin = (TREF(util_outptr) == TREF(util_outbuff_ptr)); } else prefix = NULL; outptr = buff; outtop = outptr + size - 5; /* 5 bytes to prevent writing across border */ while (outptr < outtop) { if ((NULL != prefix) && line_begin) { if ((outptr + prefix_len + 3) >= outtop) break; memcpy(outptr, prefix, prefix_len); outptr += prefix_len; *outptr++ = ' '; *outptr++ = ':'; *outptr++ = ' '; line_begin = FALSE; } /* Look for the '!' that starts an FAO directive */ while ((schar = *message++) != '!') { if (schar == '\0') { va_end(TREF(last_va_list_ptr)); /* reset before using as dest in copy */ VAR_COPY(TREF(last_va_list_ptr), fao); return outptr; } *outptr++ = schar; if (outptr >= outtop) { va_end(TREF(last_va_list_ptr)); /* reset before using as dest in copy */ VAR_COPY(TREF(last_va_list_ptr), fao); return outptr; } } field_width = 0; /* Default values */ repeat_count = 1; right_justify = FALSE; if ('R' == *message) { right_justify = TRUE; ++message; } /* Look for a field width (or repeat count) */ if (*message == '#') { if (0 < faocnt) { field_width = repeat_count = va_arg(fao, int4); faocnt--; } ++message; } else { for (c = message; *c >= '0' && *c <= '9'; ++c) ; if ((length = (int)(c - message)) > 0) { /* Accept the width/repeat only if there are more directives for the length */ if (0 < faocnt) field_width = repeat_count = asc2i((uchar_ptr_t)message, length); else field_width = repeat_count = 0; message = c; } } if ((outtop - outptr) < field_width) { /* Requested width exceeds buffer size. Should not happen. Fix it */ assert((outtop - outptr) > field_width); field_width = (outtop - outptr); } if ('@' == *message) /* Indirectly addressed operand */ { indirect = TRUE; message++; } else indirect = FALSE; switch (type = *message++) { case '/': assert(!indirect); *outptr++ = '\n'; line_begin = TRUE; continue; case '_': assert(!indirect); *outptr++ = '\t'; continue; case '^': assert(!indirect); *outptr++ = '\f'; continue; case '!': assert(!indirect); *outptr++ = '!'; continue; case '*': assert(!indirect); if (repeat_count > 0) { message_top = message + strlen(message); assert(message < message_top); chlen = (!gtm_utf8_mode) ? 1 : ((caddr_t)UTF8_MBNEXT(message, message_top) - message); } else chlen = 0; while ((repeat_count-- > 0) && (outptr < outtop)) { memcpy(outptr, message, chlen); outptr += chlen; } message += chlen; continue; case 'A': assert(!indirect); switch(type2 = *message++) { case 'C': /* a string with length in the first byte */ GETFAOVALDEF(faocnt, fao, caddr_t, c, NULL); length = (int)(c ? (unsigned int)*c++ : 0); /* Cast ensures proper sign convert */ break; case 'D': case 'F': /* string with length and addr parameters. restrict to 1 MiB length */ GETFAOVALDEF(faocnt, fao, uint4, stringlength, 0); length = (MAX_STRLEN > stringlength) ? (int)stringlength : MAX_STRLEN; GETFAOVALDEF(faocnt, fao, caddr_t, c, NULL); break; case 'S': if (0 < faocnt) { d = (desc_struct *)va_arg(fao, caddr_t); faocnt--; c = d->addr; /* Unsigned conversion simplifies the MAX_STRLEN check */ stringlength = (uint4)d->len; length = (MAX_STRLEN > stringlength) ? (int)stringlength : MAX_STRLEN; } else { c = NULL; length = 0; } break; case 'Z': /* null teminated string */ GETFAOVALDEF(faocnt, fao, caddr_t, c, NULL); stringlength = c ? strlen(c) : 0; length = (MAX_STRLEN > stringlength) ? (int)stringlength : MAX_STRLEN; break; default: assert(FALSE); c = NULL; length = 0; } /* Check the parameter and count. The pointer can't be NULL with a non-zero length */ assert((NULL != c) || (0 == length)); /* Since gtmsecshr does not load ICU libraries (since dlopen() with LD_LIBRARY_PATH * does not work for root setuid executables), avoid calling gtm_wcswidth() and * U_ISPRINT() from gtmsecshr and thus non-zero widths used in util_out_print() * from gtmsecshr will not be treated as column widths but as character lengths. * This is a safe limitation since no message from gtmsecshr specifies width yet. */ assert(!gtm_utf8_mode || IS_GTMSECSHR_IMAGE || (NULL != gtm_wcswidth_fnptr)); cwidth = (!gtm_utf8_mode || IS_GTMSECSHR_IMAGE) ? length : (*gtm_wcswidth_fnptr)((unsigned char *)c, length, FALSE, 1); if (0 < field_width && cwidth > field_width) cwidth = field_width; assert(0 <= cwidth); /* since all unprintable and illegal characters are ignored */ assert(0 <= field_width); outtop1 = outtop - 1; if (right_justify) { for (i = field_width - cwidth; i > 0 && outptr < outtop1; --i) *outptr++ = ' '; } if (!gtm_utf8_mode) { chwidth = 1; /* for both printable and unprintable characters */ chlen = 1; } for (i = 0, ctop = c + length; c < ctop; c += chlen) { if (!gtm_utf8_mode) { ch = *c; isprintable = ((' ' <= ch) || ('~' >= ch)); /* Ignored in M mode for FAO !AD */ } else { chlen = (caddr_t)UTF8_MBTOWC(c, ctop, ch) - c; if (!IS_GTMSECSHR_IMAGE) { chwidth = (int)UTF8_WCWIDTH(ch); /* Note down chwidth (for debugging) from ICU before tampering with it */ DEBUG_ONLY(orig_chwidth = chwidth;) if (-1 != chwidth) isprintable = TRUE; else { isprintable = U_ISSPACE(ch); chwidth = 1; /* treat unprintable characters as having width=1 */ } } else { /* Assume printability for GTMSECSHR */ chwidth = (int)chlen; isprintable = TRUE; } } assert(('\0' != ch) || (2 != faocnt) || !STRNCMP_LIT(message, "!/!_!AD")); /* the assert above is to detect bytes in GT.M messages, which cause trouble; * however, we have no control over the content of user-supplied input: that may contain * bytes, which, as of this writing, we believe is confined, at least in our testing, * to reporting source syntax errors; therefore the assert tries to side-step any such * syntax errors by their signature, which is unique to the messages for SRCLINE and * EXTSRCLIN and. although DBG only, avoids a GBLDEF or gtm_threadglb_def */ assert((c + chlen) <= ctop); assert(0 < chlen); assert((0 < chwidth) || (0 == chwidth) && gtm_utf8_mode); nexti = i + chwidth; if (nexti > cwidth) /* adding next input char will cross requested width */ break; if ((outptr + chlen) > outtop1) /* adding next input char will cross output buffer limit */ break; if (!isprintable && (('F' == type2) UTF8_ONLY(|| (('D' == type2) && gtm_utf8_mode)))) { /* Since HPUX stops printing lines (via FPRINTF) when it * encounters a bad character, all platforms in utf8 mode * will behave as if !AF were specified and put a "." in place * of non-printable characters. SE 01/2007 */ *outptr++ = '.'; i = nexti; } else if ('\0' != ch) /* skip NULL bytes in the middle of the string */ { if (1 == chlen) *outptr++ = *c; else { memcpy(outptr, c, chlen); outptr += chlen; } i = nexti; } } /* Ensure we are still within limits */ assert(outptr <= outtop1); assert(i <= cwidth); assert(c <= ctop); if (!right_justify) { for (i = field_width - i; i > 0 && outptr < outtop1; --i) *outptr++ = ' '; } continue; default: /* Rest of numeric types come here */ assert('S' == type || 'U' == type || 'X' == type || 'Z' == type); numptr = numa; type2 = *message++; if (!indirect) { if ('S' == type) switch(type2) { case 'B': GETFAOVALDEF(faocnt, fao, int4, schar, 0); int_val = schar; break; case 'W': GETFAOVALDEF(faocnt, fao, int4, sshort, 0); int_val = sshort; break; case 'L': GETFAOVALDEF(faocnt, fao, int4, int_val, 0); break; case 'J': GTM64_ONLY( GETFAOVALDEF(faocnt, fao, UINTPTR_T, addr_val, 0); ) NON_GTM64_ONLY( GETFAOVALDEF(faocnt, fao, int4, int_val, 0); ) break; default: assert(FALSE); GTM64_ONLY(addr_val = (UINTPTR_T)NULL); NON_GTM64_ONLY(int_val = 0); } else { GTM64_ONLY( if ('J' == type2) {GETFAOVALDEF(faocnt, fao, UINTPTR_T, addr_val, 0);} else {GETFAOVALDEF(faocnt, fao, int4, int_val, 0);} ) NON_GTM64_ONLY(GETFAOVALDEF(faocnt, fao, int4, int_val, 0);) switch(type2) { case 'B': int_val = int_val & 0xFF; break; case 'W': int_val = int_val & 0xFFFF; break; case 'L': int_val = int_val & 0xFFFFFFFF; break; case 'J': NON_GTM64_ONLY(int_val = int_val & 0xFFFFFFFF;) break; default: int_val = 0; assert(FALSE); } } switch (type) { case 'S': /* Signed value. Give sign if need to */ if ('J' == type2) { GTM64_ONLY( if (0 > (INTPTR_T)addr_val) { *numptr++ = '-'; addr_val = -(addr_val); } ) NON_GTM64_ONLY( if (0 > int_val) { *numptr++ = '-'; int_val = -(int_val); } ) } else if (0 > int_val) { *numptr++ = '-'; int_val = -(int_val); } /* note fall into unsigned */ case 'U': case 'Z': /* zero filled */ NON_GTM64_ONLY(numptr = i2asc(numptr, int_val);) GTM64_ONLY( if ('J' == type2) numptr = i2ascl(numptr, addr_val); else numptr = i2asc(numptr, int_val); ) break; case 'X': /* Hex */ switch (type2) { /* length is number of ascii hex chars */ case 'B': length = SIZEOF(short); break; case 'W': length = SIZEOF(int4); break; case 'L': length = 2 * SIZEOF(int4); break; case 'J': length = 2 * SIZEOF(INTPTR_T); break; default: assert(FALSE); length = 0; } NON_GTM64_ONLY(i2hex(int_val, numptr, length);) GTM64_ONLY(i2hex(('J' == type2) ? addr_val : int_val, numptr, length);) numptr += length; break; default: assert(FALSE); } } else { if ('X' == type) /* Support XJ and XQ */ { assert('J' == type2 || 'Q' == type2); GETFAOVALDEF(faocnt, fao, qw_num_ptr_t, val_ptr, NULL); /* Addr of long type */ if (val_ptr) { if (0 != field_width) { i2hexl(*val_ptr, numptr, field_width); numptr += field_width; } else { length = i2hexl_nofill(*val_ptr, numptr, HEX16); numptr += length; } } } else /* support ZJ, ZQ, UQ and UJ */ { assertpro(('Z' == type) || ('U' == type)); assert('J' == type2 || 'Q' == type2); GETFAOVALDEF(faocnt, fao, qw_num_ptr_t, val_ptr, NULL); /* Addr of long type */ if (val_ptr) { ret_ptr = i2ascl(numptr, *val_ptr); length =(int)(ret_ptr - (uchar_ptr_t)numptr); if (0 != field_width) numptr += MIN(length, field_width); else numptr += length; } } } length = (int)(numptr - numa); /* Length of asciified number */ if ((0 < length) && (length < field_width)) { memset(outptr, (('Z' == type) ? '0' : ' '), field_width - length); outptr += field_width - length; } if ((field_width > 0) && (field_width < length)) { GTM64_ONLY( /* If this is an integer to be printed using format specifier X, display the least 4 bytes */ if (type == 'X' && type2 == 'J' && (length == (2 * SIZEOF(INTPTR_T)))) memcpy(outptr, numa + SIZEOF(INTPTR_T), length/2); else memset(outptr, '*', field_width); ) NON_GTM64_ONLY(memset(outptr, '*', field_width);) outptr += field_width; } else if (0 < length) { memcpy(outptr, numa, length); outptr += length; } } } va_end(TREF(last_va_list_ptr)); /* reset before using as dest in copy */ VAR_COPY(TREF(last_va_list_ptr), fao); return outptr; } /* #GTM_THREAD_SAFE : The below function (util_format) is thread-safe because caller ensures serialization with locks */ void util_out_close(void) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ASSERT_SAFE_TO_UPDATE_THREAD_GBLS; if ((NULL != TREF(util_outptr)) && (TREF(util_outptr) != TREF(util_outbuff_ptr))) util_out_print("", FLUSH); } /* #GTM_THREAD_SAFE : The below function (util_out_send_oper) is thread-safe because caller ensures serialization with locks */ void util_out_send_oper(char *addr, unsigned int len) /* 1st arg: address of system log message */ /* 2nd arg: length of system long message (not used in Unix implementation) */ { sigset_t savemask; char *img_type, *offset; char temp_inst_fn[MAX_FN_LEN + 1], fn[MAX_FN_LEN + 1]; mstr log_nam, trans_name; uint4 ustatus; int4 status; unsigned int bufsize, file_name_len, *fn_len; boolean_t ret, inst_from_gld; repl_inst_hdr replhdr; int fd; upd_helper_ctl_ptr_t upd_helper_ctl; upd_helper_entry_ptr_t helper, helper_top; intrpt_state_t prev_intrpt_state; assert(IS_PTHREAD_LOCKED_AND_HOLDER); if (first_syslog) { first_syslog = FALSE; offset = facility; BUILD_FACILITY("GTM"); INSERT_MARKER; switch (image_type) { case GTM_IMAGE: img_type = "MUMPS"; break; case MUPIP_IMAGE: if (is_src_server) img_type = "SRCSRVR"; else if (is_rcvr_server) img_type = "RCVSRVR"; else if (is_updproc) img_type = "UPDPROC"; else if (is_updhelper) { assert((UPD_HELPER_READER == is_updhelper) || (UPD_HELPER_WRITER == is_updhelper)); if (UPD_HELPER_READER == is_updhelper) img_type = "UPDREAD"; else img_type = "UPDWRITE"; } else img_type = "MUPIP"; break; case DSE_IMAGE: img_type = "DSE"; break; case LKE_IMAGE: img_type = "LKE"; break; case GTCM_SERVER_IMAGE: img_type = "GTCM"; break; case GTCM_GNP_SERVER_IMAGE: img_type = "GTCM_GNP"; break; case GTMSECSHR_IMAGE: img_type = "SECSHR"; break; default: assertpro(FALSE); } BUILD_FACILITY(img_type); if ((NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl) && (NULL != jnlpool->repl_inst_filehdr)) { /* Read instace file name from jnlpool */ INSERT_MARKER; BUILD_FACILITY((char *)jnlpool->repl_inst_filehdr->inst_info.this_instname); } else { /* Read instance name from instance file */ fn_len = &file_name_len; bufsize = MAX_FN_LEN + 1; SETUP_INST_INFO(gd_header, log_nam, inst_from_gld); /* set log_nam from GLD or environment variable */ trans_name.addr = temp_inst_fn; ret = FALSE; GET_INSTFILE_NAME(dont_sendmsg_on_log2long, return_on_error); /* We want the instance name as part of operator log messages, but if we cannot get it, * we will get by without it, so ignore any errors we might encounter trying to find the name */ if (ret) { OPENFILE(fn, O_RDONLY, fd); if (FD_INVALID != fd) { LSEEKREAD(fd, 0, &replhdr, SIZEOF(repl_inst_hdr), status); if (0 == status) { INSERT_MARKER; BUILD_FACILITY((char *)replhdr.inst_info.this_instname); } CLOSEFILE_RESET(fd, status); } } } DEFER_INTERRUPTS(INTRPT_IN_LOG_FUNCTION, prev_intrpt_state); (void)OPENLOG(facility, LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_USER); ENABLE_INTERRUPTS(INTRPT_IN_LOG_FUNCTION, prev_intrpt_state); } /* When syslog is processing and a signal occurs, the signal processing might eventually lead to another syslog * call. But in libc the first syslog has grabbed a lock (syslog_lock), and now the other syslog call will * block waiting for that lock which can't be released since the first syslog was interrupted by the signal. * We address this issue by deferring signals for the duration of the call; generic_signal_handler.c will also * skip send_msg invocations if the interrupt comes while INTRPT_IN_LOG_FUNCTION is set. */ DEFER_INTERRUPTS(INTRPT_IN_LOG_FUNCTION, prev_intrpt_state); SYSLOG(LOG_USER | LOG_INFO, "%s", addr); ENABLE_INTERRUPTS(INTRPT_IN_LOG_FUNCTION, prev_intrpt_state); } /* #GTM_THREAD_SAFE : The below function (util_out_print_vaparm) is thread-safe because caller ensures serialization with locks */ void util_out_print_vaparm(caddr_t message, int flush, va_list var, int faocnt) { char fmt_buff[OUT_BUFF_SIZE]; /* needs to be same size as that of the util out buffer */ caddr_t fmtc; int rc, count; char *fmt_top1, *fmt_top2; /* the top of the buffer after leaving 1 (and 2 bytes respectively) at the end */ int util_avail_len; mstr flushtxt; boolean_t use_gtmio; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (IS_GTMSECSHR_IMAGE && (FLUSH == flush)) flush = OPER; /* All gtmsecshr origin msgs go to operator log */ ASSERT_SAFE_TO_UPDATE_THREAD_GBLS; assert(NULL != TREF(util_outptr)); if (NULL != message) { util_avail_len = INTCAST(TREF(util_outbuff_ptr) + OUT_BUFF_SIZE - TREF(util_outptr) - 2); assert(0 <= util_avail_len); if (0 < util_avail_len) { DEBUG_ONLY(util_out_print_vaparm_flush = flush;) TREF(util_outptr) = util_format(message, var, TREF(util_outptr), util_avail_len, faocnt); DEBUG_ONLY(util_out_print_vaparm_flush = FLUSH;) } } use_gtmio = ((NULL != io_std_device.out) && (!IS_GTMSECSHR_IMAGE) && err_same_as_out); switch (flush) { case NOFLUSH_OUT: break; case RESET: break; case FLUSH: if (!use_gtmio) *(TREF(util_outptr))++ = '\n'; case OPER: case SPRINT: /* For all three of these actions we need to do some output buffer translation. In all cases a '%' * is translated to the escape version '%%'. For OPER and SPRINT, we also translate '\n' to a ', ' * since some syslog() implementations (like Tru64) stop processing the passed message on a newline. * Note that since the '%' -> '%%' or '\n' to ', ' translations imply an expansion in the buffer size * requirements, we could potentially overflow the buffer after the translation. In that case we will * stop copying just before the point of overflow is reached even though it means loss of the tail data. */ *(TREF(util_outptr)) = '\0'; fmt_top1 = fmt_buff + SIZEOF(fmt_buff) - 1; fmt_top2 = fmt_top1 - 1; for (TREF(util_outptr) = TREF(util_outbuff_ptr), fmtc = fmt_buff; (0 != *(TREF(util_outptr))) && (fmtc < fmt_top1); ) { if ('%' == *(TREF(util_outptr))) { if (fmtc >= fmt_top2) /* Check if there is room for 2 bytes. If not stop copying */ break; if (flush == SPRINT) *fmtc++ = '%'; /* give buffered users what they expect %% */ *fmtc++ = '%'; (TREF(util_outptr))++; } else if ('\n' == *(TREF(util_outptr)) && (OPER == flush || SPRINT == flush)) { if (fmtc >= fmt_top2) /* Check if there is room for 2 bytes. If not stop copying */ break; *fmtc++ = ','; *fmtc++ = ' '; (TREF(util_outptr))++; } else *fmtc++ = *(TREF(util_outptr))++; } assert(fmtc <= fmt_top1); *fmtc++ = '\0'; switch (flush) { case FLUSH: /* Before flushing something to a file/terminal, check if parent is still alive. * If not, return without flushing. This child will eventually return when it * does a IS_FORCED_MULTI_PROC_EXIT check at a logical point. */ if (multi_proc_in_use && (process_id != multi_proc_shm_hdr->parent_pid) && (getppid() != multi_proc_shm_hdr->parent_pid)) { SET_FORCED_MULTI_PROC_EXIT; /* Also signal sibling children to stop processing */ return; } if (err_same_as_out) { /* If err and out are conjoined, make sure that all messages that might have been * printed using PRINTF (unfortunately, we still have lots of such instances) are * flushed before we proceed. */ FFLUSH(stdout); FFLUSH(stderr); } if (use_gtmio) { flushtxt.addr = fmt_buff; flushtxt.len = INTCAST(TREF(util_outptr) - TREF(util_outbuff_ptr)); write_text_newline_and_flush_pio(&flushtxt); } else { /* We should start caring about FPRINTF's return status at some point. */ FPRINTF(stderr, "%s", fmt_buff); FFLUSH(stderr); } break; case OPER: util_out_send_oper(fmt_buff, UINTCAST(fmtc - fmt_buff)); break; case SPRINT: memcpy(TREF(util_outbuff_ptr), fmt_buff, fmtc - fmt_buff); break; } break; default: assert(FALSE); } switch (flush) { case NOFLUSH_OUT: break; case FLUSH: case RESET: case OPER: case SPRINT: /* Reset buffer information */ TREF(util_outptr) = TREF(util_outbuff_ptr); break; } } /* #GTM_THREAD_SAFE : The below function (util_out_print) is thread-safe because caller ensures serialization with locks */ /* Because this function passes MAXPOSINT4 as the faocnt, only internal callers are allowed. */ void util_out_print(caddr_t message, int flush, ...) { va_list var; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ASSERT_SAFE_TO_UPDATE_THREAD_GBLS; va_start(var, flush); util_out_print_vaparm(message, flush, var, MAXPOSINT4); /* WARNING: UNCHECKED faocnt - internal use only */ va_end(TREF(last_va_list_ptr)); va_end(var); } /* #GTM_THREAD_SAFE : The below function (util_out_print_args) is thread-safe because caller ensures serialization with locks */ void util_out_print_args(caddr_t message, int faocnt, int flush, ...) { va_list var; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ASSERT_SAFE_TO_UPDATE_THREAD_GBLS; va_start(var, flush); util_out_print_vaparm(message, flush, var, faocnt); va_end(TREF(last_va_list_ptr)); va_end(var); } /* Saves a copy of the current unflushed buffer in util_outbuff_ptr into dst. * The length of the allocated buffer at "dst" is passed in as *dst_len. * If that length is not enough to store the unflushed buffer, FALSE is returned right away. * If not, the length of the unflushed buffer is stored in dst_len, the actual unflushed buffer * is copeid over to "dst", and a TRUE is returned. */ boolean_t util_out_save(char *dst, int *dstlen_ptr) { int srclen, dstlen; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ASSERT_SAFE_TO_UPDATE_THREAD_GBLS; assert(NULL != TREF(util_outptr)); srclen = INTCAST(TREF(util_outptr) - TREF(util_outbuff_ptr)); assert(0 <= srclen); assert(OUT_BUFF_SIZE >= srclen); dstlen = *dstlen_ptr; assert(0 <= dstlen); if (srclen > dstlen) return FALSE; *dstlen_ptr = srclen; memcpy(dst, TREF(util_outbuff_ptr), srclen); return TRUE; } /* #GTM_THREAD_SAFE : The below function (util_out_flush) is thread-safe because caller ensures serialization with locks */ /* If there is something in the util_outptr buffer, flush it. Called and used only by PRN_ERROR macro. */ void util_cond_flush(void) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ASSERT_SAFE_TO_UPDATE_THREAD_GBLS; assert(((int)ERR_TERMHANGUP != SIGNAL) || ((tt == io_std_device.in->type) && prin_in_dev_failure)); if ((TREF(util_outptr) != TREF(util_outbuff_ptr)) && ((int)ERR_TERMHANGUP != SIGNAL)) /* if a PRINCIPAL device terminal */ util_out_print(NULL, FLUSH); /* disappeared, sending stuff to it with a PRN_ERROR just complicates things */ } #ifdef DEBUG /* White-box test only! Start a timer that prints something to the operator log with a period of * UTIL_OUT_SYSLOG_INTERVAL in attempt to interrupt util_outbuff construction and overwrite the * buffer's contents. */ void util_out_syslog_dump(void) { util_out_print("Just some white-box test message long enough to ensure that " "whatever under-construction util_out buffer is not damaged.\n", OPER); /* Resubmit itself for the purposes of the white-box test which expects periodic writes to the syslog. */ start_timer((TID)&util_out_syslog_dump, UTIL_OUT_SYSLOG_INTERVAL, util_out_syslog_dump, 0, NULL); } #endif fis-gtm-V7.0-005/sr_unix/util_output_cm.c0000755000032200000250000000375514342376330017276 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_multi_thread.h" #include "cmidef.h" #include "hashtab_mname.h" /* needed for cmmdef.h */ #include "cmmdef.h" #include "error.h" #include "util.h" #include "util_out_print_vaparm.h" #include "cmi.h" #include "fao_parm.h" #include "gvcmz.h" #define PROPER(X,status) if (CMI_ERROR(status)) { ((link_info *)(lnk->usr))->neterr = TRUE ; gvcmz_error(X, status);} static unsigned char outbuff[OUT_BUFF_SIZE]; static unsigned char *outptr; void util_cm_print(clb_struct *lnk, int code, char *message, int faocnt, int flush, ...) { va_list var; int4 status, i; size_t msglen ; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; VAR_START(var, flush); if (outptr == outbuff) *outptr++ = code; if (message) { ASSERT_SAFE_TO_UPDATE_THREAD_GBLS; util_out_print(NULL, RESET); /* Clear any pending messages */ util_out_print_vaparm(message, NOFLUSH_OUT, var, faocnt); msglen = (size_t)(TREF(util_outptr) - TREF(util_outbuff_ptr)); memcpy(outptr, TREF(util_outbuff_ptr), msglen); outptr += msglen; } va_end(TREF(last_va_list_ptr)); va_end(var); switch (flush) { case NOFLUSH_OUT: break; case FLUSH : *outptr++ = 0 ; lnk->mbf = outbuff ; lnk->cbl = outptr - outbuff ; lnk->ast = 0 ; status = cmi_write(lnk) ; PROPER(code, status) ; /* Note: fall into reset.. */ case RESET : outptr = outbuff ; break; default : break ; } return; } fis-gtm-V7.0-005/sr_unix/util_spawn.c0000755000032200000250000000201514342376330016373 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "gtm_stdlib.h" #include "cli.h" #include "util_spawn.h" void util_spawn(void) { char *cmd; int rc; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(1 >= TREF(parms_cnt)); if (0 == TREF(parms_cnt)) { cmd = GETENV("SHELL"); if (!cmd) cmd = "/bin/sh"; rc = SYSTEM(cmd); if (-1 == rc) PERROR("system : "); } else { assert(TAREF1(parm_ary, TREF(parms_cnt) - 1)); assert((char *)-1L != (TAREF1(parm_ary, TREF(parms_cnt) - 1))); rc = SYSTEM((TAREF1(parm_ary, TREF(parms_cnt) - 1))); if (-1 == rc) PERROR("system : "); } } fis-gtm-V7.0-005/sr_unix/v15_filestruct.h0000755000032200000250000000077214342376330017102 0ustar librarygtc/**************************************************************** * * * Copyright 2005 Fidelity Information Services, LLC. * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #define V15_GDS_LABEL "GDSDYNUNX02" /* GT.M V4 label */ fis-gtm-V7.0-005/sr_unix/versions.csh0000755000032200000250000000143514342376330016416 0ustar librarygtc################################################################# # # # Copyright (c) 2001-2020 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. # # # ################################################################# # ############################################ # # # versions - set default GT.M versions # # ############################################ # gtm_curpro is the current production version setenv gtm_curpro "V63013" # gtm_verno is the current production version setenv gtm_verno $gtm_curpro fis-gtm-V7.0-005/sr_unix/wait_for_disk_space.c0000644000032200000250000001705414342376330020213 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2018 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 /* for ENOSPC */ #include "gtmio.h" #include "have_crit.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "anticipatory_freeze.h" #include "jnl.h" #include "error.h" #include "gtmmsg.h" #include "wait_for_disk_space.h" #ifdef DEBUG GBLDEF uint4 lseekwrite_target; #endif GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF int4 exit_state; GBLREF int4 exi_condition; GBLREF int4 forced_exit_err; error_def(ERR_DSKNOSPCAVAIL); error_def(ERR_DSKNOSPCBLOCKED); error_def(ERR_DSKSPCAVAILABLE); error_def(ERR_ENOSPCQIODEFER); /* In case of ENOSPC, if anticipatory freeze scheme is in effect and this process has attached to the * journal pool, trigger an instance freeze in this case and wait for the disk space to be available * at which point unfreeze the instance. */ void wait_for_disk_space(sgmnt_addrs *csa, char *fn, int fd, off_t offset, char *buf, size_t count, int *save_errno) { boolean_t was_crit; gd_region *reg; int fn_len, tmp_errno; boolean_t freeze_cleared; char wait_comment[MAX_FREEZE_COMMENT_LEN]; sgmnt_addrs *repl_csa; jnlpool_addrs_ptr_t save_jnlpool; jnlpool_addrs_ptr_t local_jnlpool; /* needed by INST_FREEZE_ON_NOSPC_ENABLED */ intrpt_state_t prev_intrpt_state; # ifdef DEBUG uint4 lcl_lseekwrite_target; # endif DCL_THREADGBL_ACCESS; /* needed by ANTICIPATORY_FREEZE_AVAILABLE macro */ SETUP_THREADGBL_ACCESS; /* needed by ANTICIPATORY_FREEZE_AVAILABLE macro */ assert(!multi_thread_in_use); /* the below uses functions like "grab_lock" etc. which are not thread-safe */ # ifdef DEBUG /* Reset global to safe state after noting it down in a local (just in case there are errors in this function) */ lcl_lseekwrite_target = lseekwrite_target; lseekwrite_target = LSEEKWRITE_IS_TO_NONE; # endif save_jnlpool = jnlpool; if (csa->jnlpool && (jnlpool != csa->jnlpool)) jnlpool = csa->jnlpool; /* If anticipatory freeze scheme is not in effect, or if this database does not care about it, * or DSKNOSPCAVAIL is not configured as a custom error, return right away. */ if (!INST_FREEZE_ON_NOSPC_ENABLED(csa, local_jnlpool)) { if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; return; } fn_len = STRLEN(fn); assert(NULL != jnlpool); repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; was_crit = repl_csa->now_crit; reg = csa->region; if (!was_crit) { /* Setting the instance freeze requires the journal pool lock (grab_lock). However, we need to be careful * to avoid a deadlock. A deadlock is possible if we hold the io_in_prog_latch, and meanwhile another * process has grabbed crit in t_end/tp_tend, grabbed the journal pool lock, and is now waiting on the * io_in_prog_latch (e.g., via jnl_write). By doing a blocking wait in grab_lock here, we would * deadlock. Therefore, we must pass is_blocking_wait = FALSE to grab_lock. If grab_lock does not succeed, * we return right away and do not complete the jnl or db write operation for which we are waiting for disk * space. Since we do not hold crit, we can safely proceed without its completion. * On the other hand, this scenario is not possible if we hold crit on the region of interest. In this * case, a normal grab_lock is fine (is_blocking_wait = TRUE). */ if (csa->now_crit) grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY); else if (FALSE == grab_lock(jnlpool->jnlpool_dummy_reg, FALSE, GRAB_LOCK_ONLY)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_ENOSPCQIODEFER, 2, fn_len, fn); *save_errno = ERR_ENOSPCQIODEFER; if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; return; } } /* We either came into this function holding journal pool lock or grab_lock() succeeded */ assert((NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl)); assert(NULL != fn); /* if "csa" is non-NULL, fn better be non-NULL as well */ /* The "send_msg" of DSKNOSPCAVAIL done below will set instance freeze (the configuration file includes it). After that, we * will keep retrying the IO waiting for disk space to become available. If yes, we will clear the freeze. Until that is * done, we should not allow ourselves to be interrupted as otherwise interrupt code can try to write to the db/jnl (as * part of DB_LSEEKWRITE) and the first step there would be to wait for the freeze to be lifted off. Since we were the ones * who set the freeze in the first place, the auto-clearing of freeze (on disk space freeup) will no longer work in that * case. Hence the reason not to allow interrupts. */ DEFER_INTERRUPTS(INTRPT_IN_WAIT_FOR_DISK_SPACE, prev_intrpt_state); send_msg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_DSKNOSPCAVAIL, 2, fn_len, fn); /* this should set the instance freeze */ /* Make a copy of the freeze comment which would be set by the previous message. */ GENERATE_INST_FROZEN_COMMENT(wait_comment, MAX_FREEZE_COMMENT_LEN, ERR_DSKNOSPCAVAIL); tmp_errno = *save_errno; assert(ENOSPC == tmp_errno); /* Hang/retry waiting for the disk space situation to be cleared. */ for ( ; ENOSPC == tmp_errno; ) { if (!IS_REPL_INST_FROZEN) { /* Some other process cleared the instance freeze. But we still dont have our disk * space issue resolved so set the freeze flag again until space is available for us. */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_DSKNOSPCAVAIL, 2, fn_len, fn); } else if (exit_state != 0) { forced_exit_err_display(); EXIT(-exi_condition); } /* Sleep for a while before retrying the write. Do not use "hiber_start" as that * uses timers and if we are already in a timer handler now, nested timers wont work. */ SHORT_SLEEP(SLEEP_IORETRYWAIT); DEBUG_ONLY(CLEAR_FAKE_ENOSPC_IF_MASTER_DEAD); /* If some other process froze the instance and changed the comment, a retry of the * LSEEKWRITE may not be appropriate, so just loop waiting for the freeze to be lifted. */ if (IS_REPL_INST_FROZEN && (STRCMP(wait_comment, jnlpool->jnlpool_ctl->freeze_comment) != 0)) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_DSKNOSPCBLOCKED, 2, fn_len, fn); WAIT_FOR_REPL_INST_UNFREEZE(csa); } LSEEKWRITE(fd, offset, buf, count, tmp_errno); # ifdef DEBUG if (LSEEKWRITE_IS_TO_DB == lcl_lseekwrite_target) FAKE_ENOSPC(csa, fake_db_enospc, lcl_lseekwrite_target, tmp_errno); else if (LSEEKWRITE_IS_TO_JNL == lcl_lseekwrite_target) FAKE_ENOSPC(csa, fake_jnl_enospc, lcl_lseekwrite_target, tmp_errno); # endif } /* Report that we were able to continue whether we are still frozen or not. */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_DSKSPCAVAILABLE, 2, fn_len, fn); /* Only report if we were the process to set the current freeze comment; otherwise someone else reported it. */ if (STRCMP(wait_comment, jnlpool->jnlpool_ctl->freeze_comment) == 0) { CLEAR_ANTICIPATORY_FREEZE(freeze_cleared); /* sets freeze_cleared */ REPORT_INSTANCE_UNFROZEN(freeze_cleared); } *save_errno = tmp_errno; local_jnlpool = jnlpool; if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; ENABLE_INTERRUPTS(INTRPT_IN_WAIT_FOR_DISK_SPACE, prev_intrpt_state); if (!was_crit) rel_lock(local_jnlpool->jnlpool_dummy_reg); return; } fis-gtm-V7.0-005/sr_unix/wait_for_disk_space.h0000644000032200000250000000132214342376330020207 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2018 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. * * * ****************************************************************/ #ifndef WAIT_FOR_DISK_SPACE_H_INCLUDED #define WAIT_FOR_DISK_SPACE_H_INCLUDED void wait_for_disk_space(sgmnt_addrs *csa, char *fn, int fd, off_t offset, char *buf, size_t count, int *save_errno); #endif fis-gtm-V7.0-005/sr_unix/warn_db_sz.c0000644000032200000250000000306714342376330016343 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018-2020 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 "gdsroot.h" #include "gtmmsg.h" #include "send_msg.h" #include "warn_db_sz.h" #include "wbox_test_init.h" #include "gtm_string.h" #include "gtmimagename.h" #define SPCWARNTHRESHOLD 88 error_def(ERR_LOWSPC); void warn_db_sz(char *db_fname, block_id prev_blocks, block_id curr_blocks, block_id tot_blocks) { double new_sz_frac; double old_sz_frac; int diff; new_sz_frac = (WBTEST_ENABLED(WBTEST_DB_BLOCKS_WARN)) ? SPCWARNTHRESHOLD : (((double)curr_blocks) / ((double)tot_blocks)) * 100; if (new_sz_frac < SPCWARNTHRESHOLD) return; old_sz_frac = (((double)prev_blocks) / ((double)tot_blocks)) * 100; /*To check if we've crossed a 1% boundary*/ diff = ((int)new_sz_frac) - ((int)old_sz_frac); if (diff >= 1) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_LOWSPC, 5, STRLEN(db_fname), db_fname, 100 - ((int)new_sz_frac), &curr_blocks, &tot_blocks); if (IS_MUPIP_IMAGE) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_LOWSPC, 5, STRLEN(db_fname), db_fname, 100 - ((int)new_sz_frac), &curr_blocks, &tot_blocks); } } } fis-gtm-V7.0-005/sr_unix/warn_db_sz.h0000644000032200000250000000133414342376330016343 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018-2019 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. * * * ****************************************************************/ #ifndef WARN_DB_SZ_H_INCLUDED #define WARN_DB_SZ_H_INCLUDED #include "gdsroot.h" void warn_db_sz(char *db_fname, block_id prev_blocks, block_id curr_blocks, block_id tot_blocks); #endif /* WARN_DB_SZ_H_INCLUDED */ fis-gtm-V7.0-005/sr_unix/wcs_clean_dbsync.c0000644000032200000250000002717514342376330017521 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_fcntl.h" /* needed for silly aix's expansion of open to open64 */ #include "gtm_unistd.h" #include "gdsroot.h" #include "gtm_facility.h" #include "gdskill.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "jnl.h" #include "buddy_list.h" /* for tp.h */ #include "tp.h" /* for tp_region definition */ #include "gt_timer.h" /* for TID definition */ #include "timers.h" /* for TIM_DEFER_DBSYNC #define */ #include "gdsbgtr.h" /* for the BG_TRACE_PRO macros */ #include "gtmio.h" /* for the GET_LSEEK_FLAG macro */ #include "repl_msg.h" /* needed for gtmsource.h */ #include "gtmsource.h" /* needed for jnlpool_addrs typedef */ #include "wcs_clean_dbsync.h" #include "wcs_flu.h" #include "lockconst.h" #ifdef GTM_MALLOC_RENT # define GTM_MALLOC_NO_RENT_ONLY(X) #else # define GTM_MALLOC_NO_RENT_ONLY(X) X #endif GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF volatile int4 crit_count; GBLREF volatile boolean_t in_mutex_deadlock_check; GBLREF volatile int4 db_fsync_in_prog, jnl_qio_in_prog; GBLREF volatile int4 fast_lock_count; GBLREF volatile int4 gtmMallocDepth; /* Recursion indicator */ GBLREF boolean_t mupip_jnl_recover; #ifdef DEBUG GBLREF unsigned int t_tries; GBLREF volatile boolean_t timer_in_handler; #endif /* Sync the filehdr (and epoch in the journal file if before imaging). The goal is to sync the database, * but if we find us in a situation where we need to block on someone else, then we defer this to the next round. */ void wcs_clean_dbsync(TID tid, int4 hd_len, sgmnt_addrs **csaptr) { boolean_t dbsync_defer_timer; gd_region *reg, *save_region; jnl_private_control *jpc; jnl_buffer_ptr_t jbp; node_local_ptr_t cnl; sgmnt_addrs *csa, *check_csaddrs, *save_csaddrs; sgmnt_data_ptr_t csd, save_csdata; jnlpool_addrs_ptr_t save_jnlpool; DEBUG_ONLY(boolean_t save_ok_to_call_wcs_recover;) boolean_t is_mm; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; csa = *csaptr; assert(timer_in_handler); assert(csa->dbsync_timer); /* to ensure no duplicate dbsync timers */ CANCEL_DBSYNC_TIMER(csa); /* reset csa->dbsync_timer now that the dbsync timer has popped */ assert(!csa->dbsync_timer); reg = csa->region; /* Don't know how this can happen, but if region is closed, just return in PRO. */ if (!reg->open) { assert(FALSE); return; } is_mm = (dba_mm == reg->dyn.addr->acc_meth); save_region = gv_cur_region; /* Save for later restore. See notes about restore */ save_csaddrs = cs_addrs; save_csdata = cs_data; save_jnlpool = jnlpool; /* Save to see if we are in crit anywhere */ check_csaddrs = ((NULL == save_region || FALSE == save_region->open) ? NULL : (&FILE_INFO(save_region)->s_addrs)); /* Note the non-usage of TP_CHANGE_REG_IF_NEEDED macros since this routine can be timer driven. */ TP_CHANGE_REG(reg); csd = csa->hdr; cnl = csa->nl; jpc = csa->jnl; DEBUG_ONLY(jbp = NULL;) if (NULL != jpc) jbp = jpc->jnl_buff; /* Note: Use "jbp" below ONLY if "jpc" is non-NULL */ BG_TRACE_PRO_ANY(csa, n_dbsync_timers); assert(csa == cs_addrs); assert(!JNL_ALLOWED(csd) || (NULL != jpc)); assert((NULL == jpc) || (NULL != jbp)); /* Note that even if the active queue was emptied when this routine was called, due to * concurrent update activity, cnl->wcs_active_lvl can be non-zero when we reach here. We * defer syncing in this case to the next time the active queue becomes empty ( or when we * reach the next scheduled epoch_time -- in case of before-imaging) whichever is earlier. * * Note that if we are already in wcs_wtstart for this region, then invoking wcs_flu() won't * recurse on wcs_wtstart. In any case the interrupted wcs_wtstart invocation will take care * of the dbsync_timer once it is done. Therefore in this case too no need to do the dbsync. */ dbsync_defer_timer = FALSE; if (!cnl->wcs_active_lvl && !csa->in_wtstart) { /* Similar to wcs_stale, defer expensive IO flushing if any of the following is true. * 1) We are aquiring/releasing crit in any region (Strictly speaking it is enough * to check this in the current region, but doesn't harm us much). * Note that the function "mutex_deadlock_check" resets crit_count to 0 temporarily even though we * might actually be in the midst of acquiring crit. Therefore we should not interrupt mainline code * if we are in the "mutex_deadlock_check" as otherwise it presents reentrancy issues. * 2) We have crit on any region/jnlpool OR are in the middle of commit for this region (even though * we dont hold crit) OR are in wcs_wtstart (potentially holding write interlock and keeping another * process in crit waiting) OR we need to wait to obtain crit. At least one reason why we should not wait * to obtain crit is because the timeout mechanism for the critical section is currently (as of 2004 May) * driven by heartbeat on Tru64, AIX, Solaris and HPUX. The periodic heartbeat handler cannot pop as * it is a SIGALRM handler and cannot nest while we are already in a SIGALRM handler for the wcs_clean_dbsync. * Were this to happen, we could end up waiting for crit, not being able to interrupt the wait * with a timeout resulting in a hang until crit became available. * 3) We are in a "fast lock". * 4) We are in gtm_malloc. Don't want to recurse on malloc. * Other deadlock causing conditions that need to be taken care of * 1) We already have either the fsync_in_prog or the io_in_prog lock. * 2) We are currently doing a db_fsync on some region. * Note that wcs_clean_dbsync is always called in interrupt code and so we do not want to risk running a * "wcs_recover" inside the call to "grab_crit_immediate" hence the OK_FOR_WCS_RECOVER_FALSE usage below. */ dbsync_defer_timer = TRUE; if (!mupip_jnl_recover GTM_MALLOC_NO_RENT_ONLY(&& 0 == gtmMallocDepth) && (INTRPT_IN_CRIT_FUNCTION != intrpt_ok_state) && !in_mutex_deadlock_check && (0 == fast_lock_count) && (!db_fsync_in_prog) && (!jpc || (LOCK_AVAILABLE == jbp->fsync_in_prog_latch.u.parts.latch_pid)) && (0 == TREF(crit_reg_count)) && ((NULL == check_csaddrs) || !T_IN_COMMIT_OR_WRITE(check_csaddrs)) && !T_IN_COMMIT_OR_WRITE(csa) && (FALSE != grab_crit_immediate(reg, OK_FOR_WCS_RECOVER_FALSE, NOT_APPLICABLE))) { /* Note that if we are here, we have obtained crit using grab_crit_immediate. */ assert(csa->ti->early_tn == csa->ti->curr_tn); /* Do not invoke wcs_flu if the database has a newer journal file than what this process had open * when the dbsync timer was started in wcs_wtstart. This is because mainline (non-interrupt) code * in jnl_write_attempt/jnl_output_sp assumes that interrupt code will not update jpc structures to * point to latest journal file (i.e. will not do a jnl_ensure_open) but wcs_flu might invoke just * that. It is ok not to do a wcs_flu since whichever process did the journal switch would have * written the EPOCH record in the older generation journal file. Therefore there is no need to * start a new dbsync timer in this case. * * If journaling and writing EPOCHs, do a wcs_flu only if there has been at least one transaction * since the last time someone wrote an EPOCH. * * If NOT journaling or if NOT writing EPOCHs, do a wcs_flu only if there has been at least one * transaction since the last time someone did a wcs_flu. * * This way wcs_flu is not redundantly invoked and it ensures that the least number of epochs * (only the necessary ones) are written OR the least number of db file header flushes are done. * * If MM and not writing EPOCHs, we need to flush the fileheader out as that is not mmap'ed. */ /* Write idle/free epoch only if db curr_tn did not change since when the last dirty cache record was * written in wcs_wtstart to when the dbsync timer (5 seconds) popped. If the curr_tn changed it means * some other update happened in between and things are no longer idle so the previous idle dbsync * timer can be stopped. A new timer will be written when the later updates finish and leave the db * idle again. Note that there are some race conditions where we might not be accurate in writing idle * EPOCH only when necessary (since we dont hold crit at the time we record csa->dbsync_timer_tn). But * any error will always be on the side of caution so we might end up writing more idle EPOCHs than * necessary. Also, even if we dont write an idle EPOCH (for example because we found an update * happened later but that update turned out to be a duplicate SET which will not start an idle * EPOCH timer), journal recovery already knows to handle the case where an idle EPOCH did not get * written. So things will still work but it might just take a little longer than usual. */ if (csa->dbsync_timer_tn == csa->ti->curr_tn) { /* Note that it is possible in rare cases that an online rollback took csa->ti->curr_tn back * and the exact # of updates happened concurrently to take csa->ti->curr_tn back to where it * was to match csa->dbsync_timer_tn. In this case, we will be writing an epoch unnecessarily * but this is a very rare situation that is considered okay to write the epoch in that case * as it keeps the if check simple for the most frequent path. */ if ((NULL != jpc) ? (((NOJNL == jpc->channel) || !JNL_FILE_SWITCHED(jpc)) && (jbp->epoch_tn < csa->ti->curr_tn)) : (cnl->last_wcsflu_tn < csa->ti->curr_tn)) { wcs_flu(WCSFLU_FLUSH_HDR | WCSFLU_WRITE_EPOCH | WCSFLU_CLEAN_DBSYNC | WCSFLU_SPEEDUP_NOBEFORE); BG_TRACE_PRO_ANY(csa, n_dbsync_writes); /* If MM, file could have been remapped by wcs_flu above. * If so, cs_data needs to be reset. */ if (is_mm && (save_csaddrs == cs_addrs) && (save_csdata != cs_data)) save_csdata = cs_addrs->hdr; } } dbsync_defer_timer = FALSE; assert(!csa->hold_onto_crit); /* this ensures we can safely do unconditional rel_crit */ rel_crit(reg); DO_JNL_FSYNC_OUT_OF_CRIT_IF_NEEDED(reg, csa, jpc, jbp); /* Do equivalent of WCSFLU_SYNC_EPOCH out of crit */ } } if (dbsync_defer_timer) { assert(SIZEOF(INTPTR_T) == SIZEOF(csa)); /* Adding a new dbsync timer should typically be done in a deferred zone to avoid duplicate timer additions for the * same TID. But, in this case, we are guaranteed that timers won't pop as we are already in a timer handler. As * for the external interrupts, they should be okay to interrupt at this point since, unlike timer interrupts, * control won't return to mainline code. So, in either case, we can safely add the new timer. */ if (!csa->dbsync_timer) START_DBSYNC_TIMER(csa, TIM_DEFER_DBSYNC); } /* To restore to former glory, don't use TP_CHANGE_REG, 'coz we might mistakenly set cs_addrs and cs_data to NULL * if the region we are restoring to has been closed. Don't use tp_change_reg 'coz we might be ripping out the structures * needed in tp_change_reg in gv_rundown. */ gv_cur_region = save_region; cs_addrs = save_csaddrs; cs_data = save_csdata; jnlpool = save_jnlpool; return; } fis-gtm-V7.0-005/sr_unix/wcs_clean_dbsync.h0000755000032200000250000000211514342376330017514 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef WCS_CLEAN_DBSYNC_H_INCLUDED #define WCS_CLEAN_DBSYNC_H_INCLUDED #include "gt_timer.h" #include "have_crit.h" void wcs_clean_dbsync(TID tid, int4 hd_len, sgmnt_addrs **csaptr); #define START_DBSYNC_TIMER(CSA, TIM_DEFER_DBSYNC) \ { \ CSA->dbsync_timer = TRUE; \ CSA->dbsync_timer_tn = CSA->ti->curr_tn; \ start_timer((TID)CSA, TIM_DEFER_DBSYNC, &wcs_clean_dbsync, SIZEOF(CSA), (char *)&CSA); \ } #define CANCEL_DBSYNC_TIMER(CSA) \ { \ cancel_timer((TID)CSA); \ CSA->dbsync_timer = FALSE; \ } #endif fis-gtm-V7.0-005/sr_unix/wcs_clean_dbsync_timer.c0000755000032200000250000000411514342376330020711 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "gdsroot.h" #include "gtm_facility.h" #include "gdskill.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "jnl.h" #include "gt_timer.h" /* for TID definition */ #include "timers.h" /* for TIM_DEFER_DBSYNC #define */ #include "have_crit.h" #include "wcs_clean_dbsync.h" #include "gtm_reservedDB.h" GBLREF uint4 process_id; void wcs_clean_dbsync_timer(sgmnt_addrs *csa) { /* Don't start a timer if we are in wcs_flu(). We could possibly have rundown the region when * the timer pops. In any case wcs_flu() would take care of writing the epoch if needed. * Note that in VMS the check for wcsflu_pid is !cnl->wcsflu_pid while here is process_id != cnl->wcsflu_pid * This is because in VMS, if a process gets killed in the midst of a wcs_flu(), secshr_db_clnup takes care * of cleaning it up while there is currently no such facility in Unix. This means that in Unix, it is * possible we do two dbsyncs (in turn write 2 epochs records if before imaging) one when the wcs_wtstart of * process P1 empties the queue and another by the wcs_flu() of process P2 (waiting on P1 to finish * its wcs_wtstart). But this is considered infrequent enough to be better than skipping writing an * epoch due to incorrect cnl->wcsflu_pid. * * Note setting timer also bypassed if this is an statsDB as no flush is ever needed/used. */ if (!process_exiting && (process_id != csa->nl->wcsflu_pid) && (FALSE == csa->dbsync_timer)) START_DBSYNC_TIMER(csa, TIM_DEFER_DBSYNC); return; } fis-gtm-V7.0-005/sr_unix/wcs_flu.c0000644000032200000250000010440014342376330015646 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include #include "gtm_string.h" #include "gtm_time.h" #include "gtm_unistd.h" /* DB_FSYNC needs this */ #include "aswp.h" /* for ASWP */ #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" /* for SS_NORMAL */ #include "gdsbgtr.h" #include "jnl.h" #include "lockconst.h" /* for LOCK_AVAILABLE */ #include "interlock.h" #include "sleep_cnt.h" #include "performcaslatchcheck.h" #include "send_msg.h" #include "gt_timer.h" #include "is_file_identical.h" #include "gtmmsg.h" #include "wcs_backoff.h" #include "wcs_sleep.h" #include "wcs_flu.h" #include "wcs_recover.h" #include "wcs_phase2_commit_wait.h" #include "wbox_test_init.h" #include "wcs_mm_recover.h" #include "memcoherency.h" #include "gtm_c_stack_trace.h" #include "anticipatory_freeze.h" #include "eintr_wrappers.h" #include "wcs_wt.h" GBLREF bool in_backup; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF uint4 process_id; GBLREF volatile int4 db_fsync_in_prog; /* for DB_FSYNC macro usage */ GBLREF jnl_gbls_t jgbl; GBLREF bool in_mupip_freeze; #ifdef DEBUG GBLREF boolean_t in_mu_rndwn_file; GBLREF boolean_t is_src_server; GBLREF boolean_t mupip_jnl_recover; #endif error_def(ERR_DBFILERR); error_def(ERR_DBFSYNCERR); error_def(ERR_DBIOERR); error_def(ERR_GBLOFLOW); error_def(ERR_JNLFILOPN); error_def(ERR_OUTOFSPACE); error_def(ERR_SYSCALL); error_def(ERR_TEXT); error_def(ERR_WAITDSKSPACE); error_def(ERR_WCBLOCKED); error_def(ERR_WCSFLUFAIL); error_def(ERR_WRITERSTUCK); #define JNL_WRITE_EPOCH_REC(CSA, CNL, CLEAN_DBSYNC) \ MBSTART { \ jnl_write_epoch_rec(CSA); \ /* Note: Cannot easily use ? : syntax below as INCR_GVSTATS_COUNTER macro \ * is not an arithmetic expression but a sequence of statements. \ */ \ if (!CLEAN_DBSYNC) \ { \ INCR_GVSTATS_COUNTER(CSA, CNL, n_jrec_epoch_regular, 1); \ } else \ INCR_GVSTATS_COUNTER(CSA, CNL, n_jrec_epoch_idle, 1); \ } MBEND #define WAIT_FOR_CONCURRENT_WRITERS_TO_FINISH(FIX_IN_WTSTART, WAS_CRIT, REG, CSA, CNL) \ MBSTART { \ unsigned int lcnt; \ struct shmid_ds shm_buf; \ int save_errno; \ \ GTM_WHITE_BOX_TEST(WBTEST_BUFOWNERSTUCK_STACK, (CNL->in_wtstart), 1); \ if (WRITERS_ACTIVE(CNL)) \ { \ DEBUG_ONLY(int4 in_wtstart;) /* temporary for debugging purposes */ \ DEBUG_ONLY(int4 intent_wtstart;) /* temporary for debugging purposes */ \ \ assert(CSA->now_crit); \ SIGNAL_WRITERS_TO_STOP(CNL); /* to stop all active writers */ \ lcnt = 0; \ do \ { \ DEBUG_ONLY(in_wtstart = CNL->in_wtstart;) \ DEBUG_ONLY(intent_wtstart = CNL->intent_wtstart;) \ GTM_WHITE_BOX_TEST(WBTEST_BUFOWNERSTUCK_STACK, lcnt, (MAXGETSPACEWAIT * 2) - 1); \ GTM_WHITE_BOX_TEST(WBTEST_BUFOWNERSTUCK_STACK, CNL->wtstart_pid[0], process_id); \ if (MAXGETSPACEWAIT DEBUG_ONLY( * 2) == ++lcnt) \ { /* We have noticed the below assert to fail occasionally on some platforms (mostly \ * AIX and Linux). We suspect it is because of waiting for another writer that is \ * in jnl_fsync (as part of flushing a global buffer) which takes more than a minute \ * to finish. To avoid false failures (where the other writer finishes its job in \ * a little over a minute) we wait for twice the time in the debug version. \ */ \ GET_C_STACK_MULTIPLE_PIDS("WRITERSTUCK", CNL->wtstart_pid, MAX_WTSTART_PID_SLOTS, 1); \ assert((gtm_white_box_test_case_enabled) \ && ((WBTEST_BUFOWNERSTUCK_STACK == gtm_white_box_test_case_number) \ || (WBTEST_SLEEP_IN_WCS_WTSTART == gtm_white_box_test_case_number) \ || (WBTEST_DB_WRITE_HANG == gtm_white_box_test_case_number) \ || (WBTEST_EXPECT_IO_HANG == gtm_white_box_test_case_number))); \ CNL->wcsflu_pid = 0; \ SIGNAL_WRITERS_TO_RESUME(CNL); \ if (!WAS_CRIT) \ rel_crit(REG); \ /* Disable white box testing after the first time the \ WBTEST_BUFOWNERSTUCK_STACK mechanism has kicked in. This is because as \ part of the exit handling process, the control once agin comes to wcs_flu \ and at that time we do not want the WBTEST_BUFOWNERSTUCK_STACK white box \ mechanism to kick in.*/ \ GTM_WHITE_BOX_TEST(WBTEST_BUFOWNERSTUCK_STACK, gtm_white_box_test_case_enabled, FALSE); \ send_msg_csa(CSA_ARG(CSA) VARLSTCNT(5) ERR_WRITERSTUCK, 3, CNL->in_wtstart, \ DB_LEN_STR(REG)); \ return FALSE; \ } \ if (-1 == shmctl(udi->shmid, IPC_STAT, &shm_buf)) \ { \ save_errno = errno; \ if (1 == lcnt) \ { \ send_msg_csa(CSA_ARG(CSA) VARLSTCNT(4) ERR_DBFILERR, 2, \ DB_LEN_STR(REG)); \ send_msg_csa(CSA_ARG(CSA) VARLSTCNT(8) ERR_SYSCALL, 5, \ RTS_ERROR_LITERAL("shmctl()"), CALLFROM, save_errno); \ } \ } else if (1 == shm_buf.shm_nattch) \ { \ assert((FALSE == CSA->in_wtstart) && (0 <= CNL->in_wtstart)); \ CNL->in_wtstart = 0; /* fix improper value of in_wtstart if you are standalone */ \ FIX_IN_WTSTART = TRUE; \ CNL->intent_wtstart = 0;/* fix improper value of intent_wtstart if standalone */ \ } else \ wcs_sleep(lcnt); /* wait for any in wcs_wtstart to finish */ \ } while (WRITERS_ACTIVE(CNL)); \ SIGNAL_WRITERS_TO_RESUME(CNL); \ } \ } MBEND #define REL_CRIT_BEFORE_RETURN(CNL, REG) \ MBSTART { \ CNL->doing_epoch = FALSE; \ CNL->wcsflu_pid = 0; \ if (!was_crit) \ rel_crit(REG); \ } MBEND /* The below macro returns TRUE if there is some cache-record is likely still dirty in * a) active queue : (CNL->wcs_active_lvl || CRQ->fl) check OR * b) wip queue : (CRWIPQ->fl) check OR * c) in neither queue : (N_BTS != CNL->wc_in_free) check */ #define FLUSH_NOT_COMPLETE(CNL, CRQ, CRWIPQ, N_BTS) (CNL->wcs_active_lvl || CRQ->fl || CRWIPQ->fl || (N_BTS != CNL->wc_in_free)) /* Sets RET to FALSE if the caller needs to do a "return FALSE" after macro returns. Sets RET to TRUE otherwise. */ #define CLEAR_WIP_QUEUE_IF_NEEDED(ASYNCIO, WTSTART_OR_WTFINI_ERRNO, CNL, CRWIPQ, REG, RET) \ MBSTART { \ int wtfini_errno; \ \ RET = TRUE; \ if (ASYNCIO) \ { \ assert(ENOSPC != WTSTART_OR_WTFINI_ERRNO); \ WAIT_FOR_WIP_QUEUE_TO_CLEAR(CNL, CRWIPQ, ((cache_rec_ptr_t) NULL), REG, wtfini_errno); \ if (wtfini_errno) \ { \ if (ENOSPC != wtfini_errno) \ { \ assert(FALSE); \ REL_CRIT_BEFORE_RETURN(CNL, REG); \ RET = FALSE; \ } else \ { \ assert(!WTSTART_OR_WTFINI_ERRNO); \ if (!WTSTART_OR_WTFINI_ERRNO) \ WTSTART_OR_WTFINI_ERRNO = wtfini_errno; \ } \ } \ } \ } MBEND boolean_t wcs_flu(uint4 options) { boolean_t was_crit, ret; boolean_t fix_in_wtstart, flush_hdr, jnl_enabled, sync_epoch, write_epoch, need_db_fsync, in_commit; boolean_t flush_msync, speedup_nobefore, clean_dbsync, return_early, epoch_already_current, asyncio; boolean_t force_epoch; boolean_t latch_salvaged; unsigned int lcnt, pass; int n_bts, save_errno, wtstart_or_wtfini_errno; jnl_buffer_ptr_t jb; jnl_private_control *jpc; uint4 jnl_status, to_wait, to_msg; unix_db_info *udi; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; node_local_ptr_t cnl; file_control *fc; cache_que_head_ptr_t crq, crwipq; uint4 fsync_dskaddr; int4 rc; gd_region *reg; # ifdef DEBUG int wcs_wip_lvl, wcs_active_lvl, wc_in_free; /* copy of cnl noted down for debugging purposes */ # endif jnl_status = 0; flush_hdr = options & WCSFLU_FLUSH_HDR; write_epoch = options & WCSFLU_WRITE_EPOCH; sync_epoch = options & WCSFLU_SYNC_EPOCH; need_db_fsync = options & WCSFLU_FSYNC_DB; flush_msync = options & WCSFLU_MSYNC_DB; speedup_nobefore = options & WCSFLU_SPEEDUP_NOBEFORE; clean_dbsync = options & WCSFLU_CLEAN_DBSYNC; force_epoch = options & WCSFLU_FORCE_EPOCH; /* WCSFLU_IN_COMMIT bit is set if caller is "t_end" or "tp_tend" or a few other functions (currently only "view_dbop"). * This flag is an indication that we should NOT invoke "wcs_recover" if we enter this function while already holding * crit (because the caller will otherwise get confused by the persistent effects of "wcs_recover")). Instead we should * return the error as such so they can trigger appropriate error handling. This is necessary because t_end and tp_tend * could have pinned one or more cache-records (cr->in_cw_set non-zero) BEFORE invoking wcs_flu. And code AFTER the * wcs_flu in them relies on the fact that those cache records stay pinned. If wcs_flu invokes wcs_recover, it will * reset cr->in_cw_set to 0 for ALL cache-records so code AFTER the wcs_flu in the caller will fail because no buffer * is pinned at that point. As for "view_dbop", the reason is captured in a comment in the caller. */ in_commit = options & WCSFLU_IN_COMMIT; reg = gv_cur_region; udi = FILE_INFO(reg); csa = &udi->s_addrs; /* We do not want to do costly WCSFLU_SYNC_EPOCH inside crit. Only exception is if caller holds crit for a lot longer * than the current operation (e.g. DSE CRIT SEIZE etc.). csa->hold_onto_crit is TRUE in that case. Assert that. */ assert(!sync_epoch || csa->hold_onto_crit || !csa->now_crit); csd = csa->hdr; cnl = csa->nl; assert(cnl->glob_sec_init); /* If called from online rollback, we will have hold_onto_crit set to TRUE with the only exception when called from * gds_rundown in which case process_exiting will be TRUE anyways */ assert(!jgbl.onlnrlbk || csa->hold_onto_crit || process_exiting); assert(mupip_jnl_recover || !csa->nl->donotflush_dbjnl); assert(!csa->hold_onto_crit || csa->now_crit); assert((0 == memcmp(csd->label, GDS_LABEL, GDS_LABEL_SZ - 1)) || (0 == memcmp(csd->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1))); jpc = csa->jnl; if (!(was_crit = csa->now_crit)) /* Caution: assignment */ { DO_JNL_FSYNC_OUT_OF_CRIT_IF_NEEDED(reg, csa, jpc, jpc->jnl_buff); grab_crit_encr_cycle_sync(reg, WS_24); /* If it is safe to invoke "wcs_recover" (indicated by the in_commit variable being 0), do that right away * to fix any dead phase2 commits if needed. */ if (!in_commit && cnl->wcs_phase2_commit_pidcnt && !wcs_phase2_commit_wait(csa, NULL)) { /* Since phase2-commit-wait failed, set wc_blocked to TRUE if not already set. * Since we hold crit and know it is safe to invoke "wcs_recover", do invoke it. * But it is possible it returns right away (e.g. "is_src_server" is TRUE). But that * is okay since "cnl->wc_blocked" would stay set so someone else who gets crit * (other than the source server) would do the "wcs_recover" call. */ if (WC_BLOCK_RECOVER != cnl->wc_blocked) { SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER); BG_TRACE_PRO_ANY(csa, wcb_wcs_flu0); send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_WCBLOCKED, 6, LEN_AND_LIT("wcb_wcs_flu0"), process_id, &csa->ti->curr_tn, DB_LEN_STR(reg)); } wcs_recover(reg); assert(!cnl->wcs_phase2_commit_pidcnt || is_src_server); /* source server does not do "wcs_recover" */ if (cnl->wcs_phase2_commit_pidcnt) { REL_CRIT_BEFORE_RETURN(cnl, reg); send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_WCSFLUFAIL, 3, CALLFROM); return FALSE; } } } if (!FREEZE_LATCH_HELD(csa)) WAIT_FOR_REGION_TO_UNCHILL(csa, csd); /* jnl_enabled is an overloaded variable. It is TRUE only if JNL_ENABLED(csd) is TRUE * and if the journal file has been opened in shared memory. If the journal file hasn't * been opened in shared memory, we needn't (and shouldn't) do any journal file activity. */ jnl_enabled = (JNL_ENABLED(csd) && (0 != cnl->jnl_file.u.inode)); if (jnl_enabled) { jb = jpc->jnl_buff; /* If we are trying to flush a completed journal file, make sure there is nothing else to do and return. */ if (jb->last_eof_written) { assert(jb->fsync_dskaddr == jb->freeaddr); assert(jb->rsrv_freeaddr == jb->freeaddr); assert((dba_bg != csd->acc_meth) || !csd->jnl_before_image || (!cnl->wcs_active_lvl && !csa->acc_meth.bg.cache_state->cacheq_active.fl)); REL_CRIT_BEFORE_RETURN(cnl, reg); return TRUE; } /* Assert that we never flush the cache in the midst of a database commit. The only exception is MUPIP RUNDOWN */ assert((csa->ti->curr_tn == csa->ti->early_tn) || in_mu_rndwn_file); if (!jgbl.dont_reset_gbl_jrec_time) SET_GBL_JREC_TIME; /* needed before jnl_ensure_open */ /* Before writing to jnlfile, adjust jgbl.gbl_jrec_time (if needed) to maintain time order of jnl * records. This needs to be done BEFORE the jnl_ensure_open as that could write journal records * (if it decides to switch to a new journal file) */ ADJUST_GBL_JREC_TIME(jgbl, jb); assert(csa == cs_addrs); /* for jnl_ensure_open */ jnl_status = jnl_ensure_open(reg, csa); WBTEST_ASSIGN_ONLY(WBTEST_WCS_FLU_FAIL, jnl_status, ERR_JNLFILOPN); if (SS_NORMAL != jnl_status) { assert(ERR_JNLFILOPN == jnl_status); send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg)); if (JNL_ENABLED(csd)) { /* If journaling is still enabled, but we failed to open the journal file, * we don't want to continue processing. */ REL_CRIT_BEFORE_RETURN(cnl, reg); send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_WCSFLUFAIL, 3, CALLFROM); return FALSE; } jnl_enabled = FALSE; } } if (jnl_enabled) { assert(SS_NORMAL == jnl_status); cnl->doing_epoch = sync_epoch || write_epoch; epoch_already_current = (!force_epoch && (jb->post_epoch_freeaddr == jb->rsrv_freeaddr)); if (return_early = (speedup_nobefore && !csd->jnl_before_image)) { /* Finish easiest option first. This database has NOBEFORE image journaling and caller has asked for * processing to be speeded up in that case. Write only an epoch record, don't do heavyweight flush or fsync * of db.This will avoid bunching of IO at the epoch time like is the case with before-image journaling * where this is currently necessary for correctness. But for nobefore, there is no need to do this since * no backward recovery will be performed. Note that if db has journaling disabled OR enabled with before- * image journaling, we skip this portion of code and follow through to the rest of wcs_flu as if * WCSFLU_SPEEDUP_NOBEFORE was not specified. */ assert(!jgbl.mur_extract); /* Don't know of a case where journal extract calls us with skip_db_flush set */ assert(write_epoch); assert(flush_hdr); /* For Recovery/Rollback logic (even in case of NOBEFORE image journaling) to work correctly, the TN values * in the file header - jnl_eovtn and curr_tn - should be greater than eov_tn in the journal file header. * Note: eov_tn in the journal file header is the TN of the penultimate EPOCH and so should always be <= * current database transaction number. If this relation is not maintained by GT.M, Rollback/Recovery logic * can issue JNLDBTNNOMATCH error. To avoid this situation, flush and sync the DB file header. */ fileheader_sync(reg); assert(NULL != jpc); if (!jgbl.mur_extract && !epoch_already_current) { if (0 == jpc->pini_addr) jnl_write_pini(csa); JNL_WRITE_EPOCH_REC(csa, cnl, clean_dbsync); } else if (epoch_already_current) jb->next_epoch_time = MAXUINT4; } fsync_dskaddr = jb->fsync_dskaddr; /* take a local copy as it could change concurrently */ if (fsync_dskaddr != jb->rsrv_freeaddr) { assert((fsync_dskaddr <= jb->dskaddr) || WBTEST_ENABLED(WBTEST_JNL_FILE_LOST_DSKADDR)); if (SS_NORMAL != (jnl_status = jnl_flush(reg))) { assert(NOJNL == jpc->channel); /* jnl file lost */ REL_CRIT_BEFORE_RETURN(cnl, reg); send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_WCSFLUFAIL, 3, CALLFROM); return FALSE; } # ifdef DEBUG if (!gtm_white_box_test_case_enabled || (WBTEST_JNL_FILE_LOST_DSKADDR != gtm_white_box_test_case_number)) { assert(jb->rsrv_freeaddr == jb->dskaddr); assert(jb->rsrv_freeaddr == jb->freeaddr); } # endif jnl_fsync(reg, jb->dskaddr); assert(jb->fsync_dskaddr == jb->dskaddr); } if (return_early) { REL_CRIT_BEFORE_RETURN(cnl, reg); return TRUE; } } BG_TRACE_ANY(csa, total_buffer_flush); INCR_GVSTATS_COUNTER(csa, cnl, n_db_flush, 1); cnl->wcsflu_pid = process_id; if (dba_mm == csd->acc_meth) { if (WBTEST_ENABLED(WBTEST_WCS_FLU_FAIL) || ((FROZEN(csd) || flush_msync) && (csa->ti->last_mm_sync != csa->ti->curr_tn))) { #ifdef _AIX GTM_DB_FSYNC(csa, udi->fd, rc); #else rc = MSYNC((caddr_t)(MM_BASE_ADDR(csa)), (caddr_t)csa->db_addrs[1]); #endif if (!(WBTEST_ENABLED(WBTEST_WCS_FLU_FAIL)) && (0 == rc)) { /* Save when did last full sync */ csa->ti->last_mm_sync = csa->ti->curr_tn; } else { REL_CRIT_BEFORE_RETURN(cnl, reg); send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_TEXT("Error during file msync during flush")); send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_WCSFLUFAIL, 3, CALLFROM); return FALSE; } } } else { /* If not mupip rundown, wait for ALL active phase2 commits to complete first. * In case of mupip rundown, we know no one else is accessing shared memory so no point waiting. */ asyncio = csd->asyncio; assert(!in_mu_rndwn_file || (0 == cnl->wcs_phase2_commit_pidcnt)); /* We already did "wcs_phase2_commit_wait" for !was_crit && !in_commit case. Assert that below. */ assert(was_crit || in_commit || !cnl->wcs_phase2_commit_pidcnt); if (WBTEST_ENABLED(WBTEST_WCS_FLU_FAIL) || (cnl->wcs_phase2_commit_pidcnt && !wcs_phase2_commit_wait(csa, NULL))) { assert((WBTEST_CRASH_SHUTDOWN_EXPECTED == gtm_white_box_test_case_number) /* see wcs_phase2_commit_wait.c */ || (WBTEST_WCS_FLU_FAIL == gtm_white_box_test_case_number) || (WBTEST_MURUNDOWN_KILLCMT06 == gtm_white_box_test_case_number)); /* This is same as * WBTEST_CRASH_SHUTDOWN_EXPECTED */ REL_CRIT_BEFORE_RETURN(cnl, reg); send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_WCSFLUFAIL, 3, CALLFROM); return FALSE; /* We expect the caller to trigger cache-recovery which will fix this counter */ } /* Now that all concurrent commits are complete, wait for these dirty buffers to be flushed to disk. * Note that calling wcs_wtstart just once assumes that if we ask it to flush all the buffers, it will. * This may not be true in case of twins since "wcs_wtstart" has to wait for the twin link to be broken * by "wcs_wtfini" before it can issue the write of the newer twin. We handle that case by calling * "wcs_wtstart" again down below. */ WCS_OPS_TRACE(csa, process_id, wcs_ops_flu1, 0, 0, 0, 0, 0); n_bts = csd->n_bts; wtstart_or_wtfini_errno = wcs_wtstart(reg, n_bts, NULL, NULL); /* Flush it all */ /* At this point the cache should have been flushed except if some other process is in wcs_wtstart waiting * to flush the dirty buffer that it has already removed from the active queue. Wait for it to finish. */ fix_in_wtstart = FALSE; /* set to TRUE by the following macro if we needed to correct cnl->in_wtstart */ # ifdef DEBUG wcs_wip_lvl = cnl->wcs_wip_lvl; wcs_active_lvl = cnl->wcs_active_lvl; wc_in_free = cnl->wc_in_free; # endif crwipq = &csa->acc_meth.bg.cache_state->cacheq_wip; assert(asyncio || !crwipq->fl); WCS_OPS_TRACE(csa, process_id, wcs_ops_flu2, 0, 0, 0, 0, 0); WAIT_FOR_CONCURRENT_WRITERS_TO_FINISH(fix_in_wtstart, was_crit, reg, csa, cnl); CLEAR_WIP_QUEUE_IF_NEEDED(asyncio, wtstart_or_wtfini_errno, cnl, crwipq, reg, ret); if (!ret) { /* We expect caller to trigger cache-recovery which will fix the wip queue */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_WCSFLUFAIL, 3, CALLFROM); return FALSE; } WCS_OPS_TRACE(csa, process_id, wcs_ops_flu3, 0, 0, 0, 0, 0); crq = &csa->acc_meth.bg.cache_state->cacheq_active; /* At this point, we expect the cache to be flushed. Exceptions are * a) twinning : "wcs_wtstart" could be waiting for a twin to be broken by "wcs_wtfini" * OR "wcs_wtfini" could have reinserted a cr back in active queue because cr->epid * corresponded to a dead pid. * b) A concurrent writer (that was active before we did the "wcs_wtstart" above) had removed a * cache record from the active queue but could not flush it out * (e.g. cr->jnl_addr > jb->fsync_dskaddr) and so reinserted it back in the active queue. * c) ENOSPC errors * d) white-box cases which induce error codepaths. * All above exceptions except (b) can be accurately characterized. As for (b), we tried capturing "writers_active" * just before the wcs_wtstart above but it is possible that when we noted "writes_active", there were * no concurrent writers but one started soon afterwards and before we did our "wcs_wtstart". * Because of this, we do not assert anything below for any of the above 4 exceptions. */ # ifdef DEBUG /* White-box code to exercise error codepaths */ if (in_commit) GTM_WHITE_BOX_TEST(WBTEST_WCS_FLU_IOERR, cnl->wcs_active_lvl, 1); GTM_WHITE_BOX_TEST(WBTEST_ANTIFREEZE_OUTOFSPACE, cnl->wcs_active_lvl, 1); # endif if (FLUSH_NOT_COMPLETE(cnl, crq, crwipq, n_bts)) { /* Some cache-record is likely still dirty in either active queue or wip queue or in neither queue */ wtstart_or_wtfini_errno = wcs_wtstart(reg, n_bts, NULL, NULL); /* Flush it all */ WAIT_FOR_CONCURRENT_WRITERS_TO_FINISH(fix_in_wtstart, was_crit, reg, csa, cnl); WCS_OPS_TRACE(csa, process_id, wcs_ops_flu4, 0, 0, 0, 0, 0); CLEAR_WIP_QUEUE_IF_NEEDED(asyncio, wtstart_or_wtfini_errno, cnl, crwipq, reg, ret); if (!ret) { /* We expect caller to trigger cache-recovery which will fix the wip queue */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_WCSFLUFAIL, 3, CALLFROM); return FALSE; } WCS_OPS_TRACE(csa, process_id, wcs_ops_flu5, 0, 0, 0, 0, 0); # ifdef DEBUG if (in_commit) { GTM_WHITE_BOX_TEST(WBTEST_WCS_FLU_IOERR, cnl->wcs_active_lvl, 1); GTM_WHITE_BOX_TEST(WBTEST_WCS_FLU_IOERR, wtstart_or_wtfini_errno, ENOENT); } if (gtm_white_box_test_case_enabled && (WBTEST_ANTIFREEZE_OUTOFSPACE == gtm_white_box_test_case_number)) { /* Simulate an ENOSPC return from "wcs_wtstart" or "wcs_wtfini" (if asyncio is TRUE) */ cnl->wcs_active_lvl = 1; wtstart_or_wtfini_errno = ENOSPC; } # endif if (FLUSH_NOT_COMPLETE(cnl, crq, crwipq, n_bts)) /* give allowance in PRO */ { if (ENOSPC == wtstart_or_wtfini_errno) { /* wait for at least csd->wait_disk_space seconds, and give up if still not successful */ WCS_OPS_TRACE(csa, process_id, wcs_ops_flu6, 0, 0, 0, 0, 0); to_wait = csd->wait_disk_space; to_msg = (to_wait / 8) ? (to_wait / 8) : 1; /* send message 8 times */ while ((0 < to_wait) && (ENOSPC == wtstart_or_wtfini_errno)) { if ((to_wait == csd->wait_disk_space) || (0 == (to_wait % to_msg))) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_WAITDSKSPACE, 4, process_id, to_wait, DB_LEN_STR(reg), wtstart_or_wtfini_errno); gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_WAITDSKSPACE, 4, process_id, to_wait, DB_LEN_STR(reg), wtstart_or_wtfini_errno); } hiber_start(1000); to_wait--; wtstart_or_wtfini_errno = wcs_wtstart(reg, n_bts, NULL, NULL); /* Flush it all */ CLEAR_WIP_QUEUE_IF_NEEDED(asyncio, wtstart_or_wtfini_errno, cnl, crwipq, reg, ret); if (!ret) { /* We expect caller to trigger cache-recovery which will fix wip queue */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_WCSFLUFAIL, 3, CALLFROM); return FALSE; } if (!FLUSH_NOT_COMPLETE(cnl, crq, crwipq, n_bts)) break; } if ((to_wait <= 0) && FLUSH_NOT_COMPLETE(cnl, crq, crwipq, n_bts)) { /* not enough space became available after the wait */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_OUTOFSPACE, 3, DB_LEN_STR(reg), process_id); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(5) ERR_OUTOFSPACE, 3, DB_LEN_STR(reg), process_id); } } else { /* There are different cases we know of currently when this is possible all of which * we currently test with white-box test cases. * (a) If a process encountered an error in the midst of committing in phase2 and * secshr_db_clnup completed the commit for it and set wc_blocked to 2 * (even though it was OUT of crit) causing the wcs_wtstart calls done above to do * nothing. * (b) If a process performing multi-region TP transaction encountered an error in * phase1 of the commit, but at least one of the participating regions have completed * the phase1 and released crit, secshr_db_clnup will set wc_blocked to block_and * verify on all the regions (including those that will be OUTSIDE crit) that * participated in the commit. Hence, like (a), wcs_wtstart calls done above will * return immediately. But phase1 and phase2 commit errors are currently enabled * only through white-box testing. * (c) If a test does crash shutdown (kill -9) that hit the process in the middle of * wcs_wtstart which means the writes did not complete successfully. * (d) If WBTEST_WCS_FLU_IOERR/WBTEST_WCS_WTSTART_IOERR white box test case is set that * forces wcs_wtstart invocations to end up with I/O errors. */ WCS_OPS_TRACE(csa, process_id, wcs_ops_flu7, 0, 0, 0, wtstart_or_wtfini_errno, 0); # ifdef DEBUG switch (gtm_white_box_test_case_number) { case WBTEST_ANTIFREEZE_OUTOFSPACE: assert(asyncio); break; case WBTEST_ANTIFREEZE_JNLCLOSE: case WBTEST_BG_UPDATE_BTPUTNULL: case WBTEST_BG_UPDATE_DBCSHGET_INVALID: case WBTEST_BG_UPDATE_DBCSHGETN_INVALID: case WBTEST_BG_UPDATE_DBCSHGETN_INVALID2: case WBTEST_BG_UPDATE_PHASE2FAIL: case WBTEST_CRASH_SHUTDOWN_EXPECTED: case WBTEST_FORCE_WCS_GET_SPACE_CACHEVRFY: case WBTEST_MURUNDOWN_KILLCMT06: case WBTEST_WCS_FLU_IOERR: case WBTEST_WCS_WTSTART_IOERR: break; default: assert(FALSE && "not in known white box test"); } # endif if (0 == wtstart_or_wtfini_errno) { SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER); BG_TRACE_PRO_ANY(csa, wcb_wcs_flu1); send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_WCBLOCKED, 6, LEN_AND_LIT("wcb_wcs_flu1"), process_id, &csa->ti->curr_tn, DB_LEN_STR(reg)); } else { /* Encountered I/O error. Transfer control to error trap */ rts_error_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_DBIOERR, 4, REG_LEN_STR(reg), DB_LEN_STR(reg), wtstart_or_wtfini_errno); assert(FALSE); /* control should not come back here */ } if (in_commit) { /* We should NOT be invoking wcs_recover as otherwise the callers (t_end or tp_tend) * will get confused (see explanation above where variable "in_commit" gets set). */ assert(was_crit); /* so don't need to rel_crit */ cnl->doing_epoch = FALSE; cnl->wcsflu_pid = 0; send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_WCSFLUFAIL, 3, CALLFROM); return FALSE; } assert(!jnl_enabled || jb->fsync_dskaddr == jb->rsrv_freeaddr); assert(0 == wtstart_or_wtfini_errno); wcs_recover(reg); if (jnl_enabled) { fsync_dskaddr = jb->fsync_dskaddr; /* take a local copy as it could change concurrently */ if (fsync_dskaddr != jb->rsrv_freeaddr) { /* an INCTN record should have been written above */ assert(fsync_dskaddr <= jb->dskaddr); assert((jb->rsrv_freeaddr - fsync_dskaddr) >= INCTN_RECLEN); /* above assert has a >= instead of == due to possible * ALIGN record in between */ if (SS_NORMAL != (jnl_status = jnl_flush(reg))) { assert(NOJNL == jpc->channel); /* jnl file lost */ REL_CRIT_BEFORE_RETURN(cnl, reg); send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_WCSFLUFAIL, 3, CALLFROM); return FALSE; } assert(jb->freeaddr == jb->dskaddr); assert(jb->freeaddr == jb->rsrv_freeaddr); jnl_fsync(reg, jb->dskaddr); /* Use jb->fsync_dskaddr (instead of "fsync_dskaddr") below as the * shared memory copy is more uptodate (could have been updated by * "jnl_fsync" call above). */ assert(jb->fsync_dskaddr == jb->dskaddr); } } /* After the "wcs_recover" call above, it is possible a dirty cache-record which was * in the wip queue and corresponded to a dead pid got re-inserted into the wip * queue. In that case, the call to WAIT_FOR_WIP_QUEUE_CLEAR (which in turn calls * "wcs_wtfini") would reinsert this into the active queue. So we need to call * "wcs_wtstart"/WAIT_FOR_WIP_QUEUE_CLEAR once more to clean this out. Hence the * loop count of 2 below. */ for (lcnt = 0; lcnt < 2; lcnt++) { wtstart_or_wtfini_errno = wcs_wtstart(reg, n_bts, NULL, NULL); /* Flush it all */ WAIT_FOR_CONCURRENT_WRITERS_TO_FINISH(fix_in_wtstart, was_crit, reg, csa, cnl); CLEAR_WIP_QUEUE_IF_NEEDED(asyncio, wtstart_or_wtfini_errno, cnl, crwipq, reg, ret); if (!ret) { /* We expect caller to trigger cache-recovery which will fix wip queue */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_WCSFLUFAIL, 3, CALLFROM); return FALSE; } if (FLUSH_NOT_COMPLETE(cnl, crq, crwipq, n_bts)) { if (!lcnt) continue; /* Something wrong inspite of all these attempts */ REL_CRIT_BEFORE_RETURN(cnl, reg); assertpro(FALSE); } } } } } } if (flush_hdr) fileheader_sync(reg); if (jnl_enabled && write_epoch) { /* If need to write an epoch, * (1) get hold of the jnl io_in_prog lock. * (2) set need_db_fsync to TRUE in the journal buffer. * (3) release the jnl io_in_prog lock. * (4) write an epoch record in the journal buffer. * The next call to jnl_qio_start will do the fsync of the db before doing any jnl qio. * The basic requirement is that we shouldn't write the epoch out until we have synced the database. */ # ifdef DEBUG if (!gtm_white_box_test_case_enabled || (WBTEST_JNL_FILE_LOST_DSKADDR != gtm_white_box_test_case_number)) { assert(jb->rsrv_freeaddr == jb->fsync_dskaddr); assert(jb->rsrv_freeaddr == jb->freeaddr); } # endif /* If jb->need_db_fsync is TRUE at this point of time, it means we already have a db_fsync waiting * to happen. This means the epoch due to the earlier need_db_fsync hasn't yet been written out to * the journal file. But that means we haven't yet flushed the journal buffer which leads to a * contradiction. (since we have called jnl_flush earlier in this routine and also assert to the * effect jb->fsync_dskaddr == jb->rsrv_freeaddr a few lines above). */ assert(!jb->need_db_fsync); for (lcnt = 1; FALSE == (GET_SWAPLOCK(&jb->io_in_prog_latch)); lcnt++) { /* this is a long lock and hence should be a mutex */ if (MAXJNLQIOLOCKWAIT < lcnt) /* tried too long */ { GET_C_STACK_MULTIPLE_PIDS("MAXJNLQIOLOCKWAIT", cnl->wtstart_pid, MAX_WTSTART_PID_SLOTS, 1); assert(FALSE); REL_CRIT_BEFORE_RETURN(cnl, reg); assertpro(FALSE); } wcs_sleep(SLEEP_JNLQIOLOCKWAIT); /* since it is a short lock, sleep the minimum */ if ((MAXJNLQIOLOCKWAIT / 2 == lcnt) || (MAXJNLQIOLOCKWAIT == lcnt)) { latch_salvaged = performCASLatchCheck(&jb->io_in_prog_latch, TRUE); if (latch_salvaged) { /* jb->dskaddr & jb->dsk are updated while holding the io_in_prog_latch. * Since the latch was salvaged, the holder pid could have been killed * after jb->dskaddr has been updated but before jb->dsk has been updated * (in "jnl_sub_qio_start"). Fix the discrepancy if any. */ jb->dsk = (jb->dskaddr % jb->size); } } } if (csd->jnl_before_image && !epoch_already_current) jb->need_db_fsync = TRUE; /* for comments on need_db_fsync, see jnl_output_sp.c */ /* else the journal files do not support before images and hence can only be used for forward recovery. So skip * fsync of the database (jb->need_db_fsync = FALSE) because we don't care if the on-disk db is up-to-date or not. * Also skip the fsync if we aren't actually going to write an epoch. */ RELEASE_SWAPLOCK(&jb->io_in_prog_latch); assert(!(JNL_FILE_SWITCHED(jpc))); assert(jgbl.gbl_jrec_time); if (!jgbl.mur_extract && !epoch_already_current) { if (0 == jpc->pini_addr) jnl_write_pini(csa); JNL_WRITE_EPOCH_REC(csa, cnl, clean_dbsync); } else if (epoch_already_current) jb->next_epoch_time = MAXUINT4; } cnl->last_wcsflu_tn = csa->ti->curr_tn; /* record when last successful wcs_flu occurred */ REL_CRIT_BEFORE_RETURN(cnl, reg); /* sync the epoch record in the journal if needed. */ if (jnl_enabled && write_epoch && sync_epoch && (csa->ti->curr_tn == csa->ti->early_tn)) { /* Note that if we are in the midst of committing and came here through a bizarre * stack trace (like wcs_get_space etc.) we want to defer syncing to when we go out of crit. * Note that we are guaranteed to come back to wcs_wtstart since we are currently in commit-phase * and will dirty atleast one block as part of the commit for a wtstart timer to be triggered. */ jnl_wait(reg); } if (need_db_fsync && JNL_ALLOWED(csd)) { if (dba_mm != csd->acc_meth) { DB_FSYNC(reg, udi, csa, db_fsync_in_prog, save_errno); if (0 != save_errno) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFSYNCERR, 2, DB_LEN_STR(reg), save_errno); rts_error_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_DBFSYNCERR, 2, DB_LEN_STR(reg), save_errno); assert(FALSE); /* should not come here as the rts_error above should not return */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_WCSFLUFAIL, 3, CALLFROM); return FALSE; } } } return TRUE; } fis-gtm-V7.0-005/sr_unix/wcs_get_space.c0000644000032200000250000003254614342376330017025 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2007-2020 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_facility.h" #include "gdsroot.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "interlock.h" #include "jnl.h" #include "sleep_cnt.h" #include "gdsbgtr.h" #include "wbox_test_init.h" /* Include prototypes */ #include "send_msg.h" #include "wcs_get_space.h" #include "gtmmsg.h" #include "gt_timer.h" #include "wcs_sleep.h" #include "relqop.h" #include "error.h" /* for gtm_fork_n_core() prototype */ #include "gtm_rel_quant.h" #include "performcaslatchcheck.h" #include "wcs_backoff.h" #include "wcs_phase2_commit_wait.h" #include "wcs_recover.h" #include "gtm_c_stack_trace.h" #include "wcs_wt.h" #include "is_proc_alive.h" GBLDEF cache_rec_ptr_t get_space_fail_cr; /* gbldefed to be accessible in a pro core */ GBLDEF wcs_conflict_trace_t *get_space_fail_array; /* gbldefed to be accessilbe in a pro core */ GBLDEF int4 get_space_fail_arridx; /* gbldefed to be accessilbe in a pro core */ GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF gd_region *gv_cur_region; /* needed for the JNL_ENSURE_OPEN_WCS_WTSTART macro */ GBLREF int num_additional_processors; GBLREF uint4 process_id; GBLREF boolean_t multi_proc_in_use; error_def(ERR_DBFILERR); error_def(ERR_WAITDSKSPACE); error_def(ERR_GBLOFLOW); error_def(ERR_BUFSPCDELAY); error_def(ERR_BUFOWNERSTUCK); #define WCS_CONFLICT_TRACE_ARRAYSIZE 64 #define LCNT_INTERVAL DIVIDE_ROUND_UP(UNIX_GETSPACEWAIT, WCS_CONFLICT_TRACE_ARRAYSIZE) #define WCS_GET_SPACE_RETURN_FAIL(TRACEARRAY, CR) \ { \ /* A failure occurred. Ignored for WB test case */ \ assert(FALSE || (gtm_white_box_test_case_enabled \ && ((WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number) \ || (WBTEST_DB_WRITE_HANG == gtm_white_box_test_case_number) \ || (WBTEST_EXPECT_IO_HANG == gtm_white_box_test_case_number) \ || (WBTEST_FORCE_WCS_GET_SPACE_CACHEVRFY == gtm_white_box_test_case_number)))); \ get_space_fail_cr = CR; \ get_space_fail_array = TRACEARRAY; \ if (TREF(gtm_environment_init) DEBUG_ONLY(&& !(gtm_white_box_test_case_enabled \ && ((WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number) \ || (WBTEST_DB_WRITE_HANG == gtm_white_box_test_case_number) \ || (WBTEST_EXPECT_IO_HANG == gtm_white_box_test_case_number)) \ || (WBTEST_FORCE_WCS_GET_SPACE_CACHEVRFY == gtm_white_box_test_case_number)))) \ gtm_fork_n_core(); /* take a snapshot in case running in-house */ \ return FALSE; \ } #define GET_IO_LATCH_PID(CSA) (CSA->jnl ? CSA->jnl->jnl_buff->io_in_prog_latch.u.parts.latch_pid : -1) #define GET_FSYNC_LATCH_PID(CSA) (CSA->jnl ? CSA->jnl->jnl_buff->fsync_in_prog_latch.u.parts.latch_pid : -1) #define INVOKE_C_STACK_APPROPRIATE(CR, CSA, STUCK_CNT) \ { \ int4 io_latch_pid, fsync_latch_pid; \ \ if (CR && CR->epid) \ { \ GET_C_STACK_FROM_SCRIPT("WCS_GET_SPACE_RETURN_FAIL_CR", process_id, CR->epid, STUCK_CNT); \ } \ if (0 < (io_latch_pid = GET_IO_LATCH_PID(CSA))) \ { \ GET_C_STACK_FROM_SCRIPT("WCS_GET_SPACE_RETURN_FAIL_IO_PROG", process_id, io_latch_pid, STUCK_CNT); \ } \ if (0 < (fsync_latch_pid = GET_FSYNC_LATCH_PID(CSA))) \ { \ GET_C_STACK_FROM_SCRIPT("WCS_GET_SPACE_RETURN_FAIL_FSYNC_PROG", process_id, fsync_latch_pid, STUCK_CNT); \ } \ } \ /* go after a specific number of buffers or a particular buffer */ boolean_t wcs_get_space(gd_region *reg, int needed, cache_rec_ptr_t cr) { sgmnt_addrs *csa; sgmnt_data_ptr_t csd; node_local_ptr_t cnl; cache_que_head_ptr_t q0, base, crwipq = NULL; int4 count, dummy_errno, flsh_trigger, i, k, max_count, n, save_errno = 0; uint4 lcnt, size, to_wait, to_msg, this_idx; wcs_conflict_trace_t wcs_conflict_trace[WCS_CONFLICT_TRACE_ARRAYSIZE]; cache_rec cr_contents; boolean_t asyncio, ret; int curr_wc_in_free; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert((0 != needed) || (NULL != cr)); get_space_fail_arridx = 0; csa = &FILE_INFO(reg)->s_addrs; csd = csa->hdr; cnl = csa->nl; assert(dba_bg == csd->acc_meth); assert((0 == needed) || ((DB_CSH_RDPOOL_SZ <= needed) && (needed <= csd->n_bts))); WCS_OPS_TRACE(csa, process_id, wcs_ops_getspace1, 0, GDS_ANY_ABS2REL(csa,cr), 0, needed, csa->now_crit); if (FALSE == csa->now_crit) { assert(0 != needed); /* if needed == 0, then we should be in crit */ JNL_ENSURE_OPEN_WCS_WTSTART(csa, reg, needed, NULL, TRUE, dummy_errno); return TRUE; } asyncio = csd->asyncio; flsh_trigger = csd->flush_trigger; csd->flush_trigger = MAX(flsh_trigger - MAX(flsh_trigger / STEP_FACTOR, 1), MIN_FLUSH_TRIGGER(csd->n_bts)); /* Routine actually serves two purposes: * 1 - Free up required number of buffers or * 2 - Free up a specific buffer * Do a different kind of loop depending on which is our current calling. */ if (0 != needed) { BG_TRACE_ANY(csa, bufct_buffer_flush); curr_wc_in_free = cnl->wc_in_free; for (lcnt = 1; cnl->wc_in_free < needed; ++lcnt) { if (0 == lcnt % (BUF_OWNER_STUCK AIX_ONLY(* (asyncio ? 4 : 1)))) send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_BUFSPCDELAY, 3, needed, REG_LEN_STR(reg)); JNL_ENSURE_OPEN_WCS_WTSTART(csa, reg, needed, NULL, TRUE, save_errno); if (cnl->wc_in_free < needed) { if ((ENOSPC == save_errno) && (csa->hdr->wait_disk_space > 0)) { /* Not enough disk space to flush the buffers to regain them * so wait for it to become available, and if it takes too long, * just quit. Unfortunately, quitting would invoke the recovery * logic which should be of no help to this situation. Then what? */ lcnt = BUF_OWNER_STUCK AIX_ONLY(* (asyncio ? 4 : 1)); to_wait = cs_data->wait_disk_space; to_msg = (to_wait / 8) ? (to_wait / 8) : 1; /* output error message around 8 times */ while ((0 < to_wait) && (ENOSPC == save_errno)) { if ((to_wait == cs_data->wait_disk_space) || (0 == to_wait % to_msg)) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_WAITDSKSPACE, 4, process_id, to_wait, DB_LEN_STR(reg), save_errno); gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_WAITDSKSPACE, 4, process_id, to_wait, DB_LEN_STR(reg), save_errno); } hiber_start(1000); to_wait--; JNL_ENSURE_OPEN_WCS_WTSTART(csa, reg, needed, NULL, TRUE, save_errno); if (cnl->wc_in_free >= needed) break; } } wcs_sleep(lcnt); if (cnl->wc_in_free > curr_wc_in_free) { /* As long as we are making progress in freeing up buffers be patient */ lcnt = 1; curr_wc_in_free = cnl->wc_in_free; } } else return TRUE; BG_TRACE_ANY(csa, bufct_buffer_flush_loop); } if (cnl->wc_in_free >= needed DEBUG_ONLY( && !(gtm_white_box_test_case_enabled && ((WBTEST_FORCE_WCS_GET_SPACE_CACHEVRFY == gtm_white_box_test_case_number) && (0 == gtm_white_box_test_case_count++)))) ) return TRUE; } else { /* Wait for a specific buffer to be flushed. */ assert(csa->now_crit); /* must be crit to play with queues when not the writer */ BG_TRACE_PRO_ANY(csa, spcfc_buffer_flush); base = &csa->acc_meth.bg.cache_state->cacheq_active; if (asyncio) crwipq = &csa->acc_meth.bg.cache_state->cacheq_wip; /* If another process is concurrently finishing up phase2 of commit, wait for that to complete first. */ if (cr->in_tend && !wcs_phase2_commit_wait(csa, cr)) return FALSE; /* assumption is that caller will set wc_blocked and trigger cache recovery */ JNL_ENSURE_OPEN_WCS_WTSTART(csa, reg, 1, cr, TRUE, save_errno); if (asyncio && cr->epid) { /* if the buffer is in the WIP queue and still dirty then another process flushed it for * us (wait for the buffer to become non-dirty) */ WAIT_FOR_WIP_QUEUE_TO_CLEAR(cnl, crwipq, cr, reg, ret); if (ret) return FALSE; } for (lcnt = 1; 0 != cr->dirty; ++lcnt) { if (0 == (lcnt % UNIX_GETSPACEWAIT)) send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_BUFSPCDELAY, 3, needed, REG_LEN_STR(reg)); if (0 == (lcnt % LCNT_INTERVAL)) { this_idx = (lcnt / LCNT_INTERVAL); if (this_idx < WCS_CONFLICT_TRACE_ARRAYSIZE) { /* Trace until we run out of space. This is only used if we get to * WCS_GET_SPACE_RETURN_FAIL() below, which should be fairly infrequent, * and the results are only for debugging purposes. */ wcs_conflict_trace[this_idx].wcs_active_lvl = cnl->wcs_active_lvl; wcs_conflict_trace[this_idx].io_in_prog_pid = GET_IO_LATCH_PID(csa); wcs_conflict_trace[this_idx].fsync_in_prog_pid = GET_FSYNC_LATCH_PID(csa); } } get_space_fail_arridx = lcnt; max_count = ROUND_UP(cnl->wcs_active_lvl, csd->n_wrt_per_flu); /* Check if cache recovery is needed (could be set by another process in * secshr_db_clnup finishing off a phase2 commit). If so, no point invoking * wcs_wtstart as it will return right away. Instead return FALSE so * cache-recovery can be triggered by the caller. */ if (cnl->wc_blocked) { assert(gtm_white_box_test_case_enabled); return FALSE; } /* loop till the active queue is exhausted OR desired cr becomes non-dirty */ for (count = max_count; (0 != cr->dirty) && (0 != cnl->wcs_active_lvl) && count; count--) { BG_TRACE_PRO_ANY(csa, spcfc_buffer_flush_retries); JNL_ENSURE_OPEN_WCS_WTSTART(csa, reg, 0, NULL, TRUE, save_errno); if (asyncio) { DEBUG_ONLY(dbg_wtfini_lcnt = dbg_wtfini_wcs_get_space2); /* used by "wcs_wtfini" */ if (wcs_wtfini(reg, CHECK_IS_PROC_ALIVE_FALSE, cr)) return FALSE; } } /* Even if there was no record in the active queue, there might be something * in the wip queue (including our desired cr). So flush that out separately. */ if (asyncio) { DEBUG_ONLY(dbg_wtfini_lcnt = lcnt); /* used by "wcs_wtfini" */ if (wcs_wtfini(reg, CHECK_IS_PROC_ALIVE_TRUE_OR_FALSE(lcnt, \ UNIX_GETSPACEWAIT), cr)) return FALSE; } /* Usually we want to sleep only if we need to wait on someone else * i.e. (i) if we are waiting for another process' fsync to complete * We have seen jnl_fsync() to take more than a minute. * Hence we wait for a max. of 2 mins (UNIX_GETSPACEWAIT). * (ii) if some concurrent writer has taken this cache-record out. * (iii) if someone else is holding the io_in_prog lock. * Right now we know of only one case where there is no point in waiting * which is if the cache-record is out of the active queue and is dirty. * But since that is quite rare and we don't lose much in that case by * sleeping we do an unconditional sleep (only if cr is dirty). {BYPASSOK} */ if (!cr->dirty) return TRUE; else { DEBUG_ONLY(cr_contents = *cr;) /* Assert that if the cache-record is dirty, it better be in the * active queue or be in the process of getting flushed by a concurrent * writer or phase2 of the commit is in progress. If none of this is * true, it should have become non-dirty by now even though we found it * dirty a few lines above. Note that the cache-record could be in the * process of being released by a concurrent writer; This is done by * resetting 3 fields cr->epid, cr->dirty, cr->interlock; Since the write * interlock is the last field to be released, check that BEFORE dirty. */ assert(cr_contents.state_que.fl || cr_contents.epid || cnl->in_wtstart || cr_contents.in_tend || (LATCH_CLEAR != WRITE_LATCH_VAL(&cr_contents)) || !cr_contents.dirty); wcs_sleep(lcnt); } BG_TRACE_PRO_ANY(csa, spcfc_buffer_flush_loop); # ifdef DEBUG /* Reduce the wait time to encounter errors associated with * WBTEST_JNL_FILE_LOST_DSKADDR faster */ if (gtm_white_box_test_case_enabled && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number) && (0 < gtm_white_box_test_case_count) && (lcnt >= gtm_white_box_test_case_count)) lcnt = ((lcnt + UNIX_GETSPACEWAIT) % UNIX_GETSPACEWAIT) - 1; # endif } if (0 == cr->dirty) return TRUE; } if (ENOSPC == save_errno) rts_error_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_WAITDSKSPACE, 4, process_id, to_wait, DB_LEN_STR(reg), save_errno); else assert((WBTEST_DB_WRITE_HANG == gtm_white_box_test_case_number) || (WBTEST_EXPECT_IO_HANG == gtm_white_box_test_case_number) || (WBTEST_FORCE_WCS_GET_SPACE_CACHEVRFY == gtm_white_box_test_case_number)); INVOKE_C_STACK_APPROPRIATE(cr, csa, 2); WCS_GET_SPACE_RETURN_FAIL(wcs_conflict_trace, cr); /* Check before exiting if there are outstanding async I/Os when the child process exits */ if (multi_proc_in_use) { JNL_ENSURE_OPEN_WCS_WTSTART(csa, reg, 0, NULL, TRUE, save_errno); if (asyncio) { DEBUG_ONLY(dbg_wtfini_lcnt = dbg_wtfini_wcs_get_space3); /* used by "wcs_wtfini" */ if (wcs_wtfini(reg, CHECK_IS_PROC_ALIVE_FALSE, cr)) return FALSE; } } } fis-gtm-V7.0-005/sr_unix/wcs_write_in_progress_wait.c0000644000032200000250000000741614342376330021661 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2007-2020 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "interlock.h" #include "gdsbgtr.h" #include "sleep_cnt.h" #include "wbox_test_init.h" #include "copy.h" /* Include prototypes */ #include "caller_id.h" #include "send_msg.h" #include "wcs_sleep.h" #include "is_proc_alive.h" #include "wcs_write_in_progress_wait.h" #include "add_inter.h" #include "gtm_c_stack_trace.h" #include "wcs_wt.h" GBLREF gd_region *gv_cur_region; /* for the LOCK_HIST macro used in LOCK_BUFF_FOR_UPDATE macro */ GBLREF sgmnt_data_ptr_t cs_data; GBLREF uint4 process_id; /* for the LOCK_HIST macro used in LOCK_BUFF_FOR_UPDATE macro */ error_def (ERR_WRITEWAITPID); /* Waits for a concurrently running write (of a global buffer to disk) to complete. * * Returns TRUE if write completes within timeout of approx. 1 minute. * Returns FALSE otherwise. */ boolean_t wcs_write_in_progress_wait(node_local_ptr_t cnl, cache_rec_ptr_t cr, wbtest_code_t wbox_test_code) { uint4 lcnt; int4 n; assert(!TWINNING_ON(cs_data)); for (lcnt = 1; ; lcnt++) { /* the design here is that either this process owns the block, or the writer does. * if the writer does, it must be allowed to finish its write; then it will release the block * and the next LOCK will establish ownership */ LOCK_BUFF_FOR_UPDATE(cr, n, &cnl->db_latch); /* This destroys evidence of writer ownership, but this is really a test that * there was no prior owner. It will only be true if the writer has cleared it. */ if (OWN_BUFF(n)) break; else { GTM_WHITE_BOX_TEST(wbox_test_code, lcnt, (2 * BUF_OWNER_STUCK)); /* We have noticed the below assert to fail occasionally on some platforms * We suspect it is because of waiting for another writer that is in jnl_fsync * (as part of flushing a global buffer) which takes more than a minute to finish. * To avoid false failures (where the other writer finishes its job in a little over * a minute) we wait for twice the time in the debug version. */ # ifdef DEBUG if ((BUF_OWNER_STUCK == lcnt) && cr->epid) GET_C_STACK_FROM_SCRIPT("WRITEWAITPID", process_id, cr->epid, ONCE); # endif if (0 == lcnt % (BUF_OWNER_STUCK DEBUG_ONLY( * 2))) { /* sick of waiting */ if (0 == cr->dirty) { /* someone dropped something; assume it was the writer and go on */ LOCK_NEW_BUFF_FOR_UPDATE(cr); break; } else { if (cr->epid) { /* Getting the stack can take some time, so send to the syslog first and check * that we are still in the same state after. */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_WRITEWAITPID, 6, process_id, DEBUG_ONLY(TWICE) PRO_ONLY(ONCE), cr->epid, &(cr->blk), DB_LEN_STR(gv_cur_region)); GET_C_STACK_FROM_SCRIPT("WRITEWAITPID", process_id, cr->epid, DEBUG_ONLY(TWICE) PRO_ONLY(ONCE)); if (cr->dirty && cr->epid && !is_proc_alive(cr->epid, 0)) return FALSE; } assert((WBTEST_DB_WRITE_HANG == gtm_white_box_test_case_number) || (WBTEST_EXPECT_IO_HANG == gtm_white_box_test_case_number)); } } if (WRITER_STILL_OWNS_BUFF(cr, n)) wcs_sleep(lcnt); } } /* end of for loop to control buffer */ return TRUE; } fis-gtm-V7.0-005/sr_unix/wcs_write_in_progress_wait.h0000644000032200000250000000133014342376330021653 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2007-2018 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. * * * ****************************************************************/ #ifndef WCS_WRITE_IN_PROGRESS_WAIT_H_INCLUDED #define WCS_WRITE_IN_PROGRESS_WAIT_H_INCLUDED boolean_t wcs_write_in_progress_wait(node_local_ptr_t cnl, cache_rec_ptr_t cr, wbtest_code_t wbox_test_code); #endif fis-gtm-V7.0-005/sr_unix/wcs_wt.h0000644000032200000250000001423014342376330015520 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2021 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. * * * ****************************************************************/ #ifndef WCS_WT_H_INCLUDED #define WCS_WT_H_INCLUDED #include "sleep_cnt.h" #include "wcs_sleep.h" #define WT_LATCH_TIMEOUT_SEC (4 * 60) /* Define latch timeout as being 4 mins */ #define REINSERT_CR_AT_TAIL(csr, ahead, n, csa, csd, trace_cntr) \ MBSTART { \ n = INSQTI((que_ent_ptr_t)csr, (que_head_ptr_t)ahead); \ if (INTERLOCK_FAIL == n) \ { \ assert(FALSE); \ SET_TRACEABLE_VAR((cnl)->wc_blocked, WC_BLOCK_RECOVER); \ BG_TRACE_PRO_ANY(csa, trace_cntr); \ } \ } MBEND #define BREAK_TWIN(CSR, CSA) \ MBSTART { \ cache_rec_ptr_t cr_new; \ \ assert((CSR)->twin && (CSA)->now_crit); /* We need crit to break twin connections. */ \ assert(!(CSR)->bt_index); /* It has to be an OLDER twin. It cannot be a NEWER twin because \ * as long as the OLDER twin exists in the WIP queue, the NEWER \ * twin write would not have been issued by "wcs_wtstart". \ */ \ assert(!(CSR)->in_cw_set); /* no other process should be needing this buffer */ \ cr_new = (cache_rec_ptr_t)GDS_ANY_REL2ABS((CSA), (CSR)->twin); /* Get NEWER twin cr */ \ assert((void *)&((cache_rec_ptr_t)GDS_ANY_REL2ABS((CSA), cr_new->twin))->state_que == (void *)(CSR)); \ /* NEWER twin should be in ACTIVE queue, except in rare cases where processes were killed. */ \ assert(cr_new->dirty || WBTEST_ENABLED(WBTEST_CRASH_SHUTDOWN_EXPECTED)); \ (CSR)->cycle++; /* increment cycle whenever blk number changes (tp_hist needs it) */ \ (CSR)->blk = CR_BLKEMPTY; \ assert(CR_BLKEMPTY != cr_new->blk); /* NEWER twin should have a valid block number */ \ cr_new->twin = (CSR)->twin = 0; /* Break the twin link */ \ cr_new->backup_cr_is_twin = FALSE; \ } MBEND /* "wcs_wtfini" is called with a second parameter which indicates whether it has to do "is_proc_alive" check or not. * In places where we know for sure we do not need this check, we pass FALSE. In places where we would benefit from a check * we pass TRUE but since "wcs_wtfini" is usually called in a "wcs_sleep" loop using "lcnt" variable iterating from 1 to * SLEEP_ONE_MIN/BUF_OWNER_STUCK, we want to limit the heavyweight nature of the "is_proc_alive" check by doing it only * 32 times for every SLEEP_ONE_MIN iterations of the loop. Hence the MAX/32 calculation below. This approximates to * doing the system call once every 2 seconds. */ #define CHECK_IS_PROC_ALIVE_FALSE FALSE #define CHECK_IS_PROC_ALIVE_TRUE TRUE #define CHECK_IS_PROC_ALIVE_TRUE_OR_FALSE(lcnt, MAX) (0 == (lcnt % (MAX/32))) #ifdef DEBUG enum dbg_wtfini_lcnt_t { dbg_wtfini_db_csh_getn = 32768, /* a value greater than SLEEP_ONE_MIN (= 6000) and UNIX_GETSPACEWAIT (= 12000) * to distinguish this from other "lcnt". */ dbg_wtfini_wcs_recover = 32769, dbg_wtfini_wcs_get_space1 = 32770, dbg_wtfini_wcs_get_space2 = 32771, dbg_wtfini_wcs_wtstart = 32772, dbg_wtfini_wcs_get_space3 = 32773 }; GBLREF enum dbg_wtfini_lcnt_t dbg_wtfini_lcnt; /* "lcnt" value for WCS_OPS_TRACE tracking purposes */ #endif typedef struct wtstart_cr_list_struct { int numcrs; int listsize; /* number of items allocated for listcrs[] */ cache_rec_ptr_t *listcrs; } wtstart_cr_list_t; int wcs_wt_restart(unix_db_info *udi, cache_state_rec_ptr_t csr); int wcs_wtfini(gd_region *reg, boolean_t do_is_proc_alive_check, cache_rec_ptr_t cr2flush); void wcs_wtfini_nocrit(gd_region *reg, wtstart_cr_list_t *cr_list_ptr); void wcs_wterror(gd_region *reg, int4 save_errno); int4 wcs_wtstart(gd_region *region, int4 writes, wtstart_cr_list_t *cr_list_ptr, cache_rec_ptr_t cr2flush); int wcs_wtstart_fini(gd_region *region, int nbuffs, cache_rec_ptr_t cr2flush); error_def(ERR_AIOQUEUESTUCK); error_def(ERR_DBFILERR); /* Wait for wip queue to be cleared or a specific CR to become non-dirty. RET is non-zero if "wcs_wtfini" * returns a non-zero value in any iteration. We wait a max of ~1 minute if we do not see any progress in * the WIP queue count. If there is evidence of progress, time waited till now does not count towards the 1 * minute timeout i.e. the ~1 minute wait is started afresh. If we hit about a minute we send a message to the * syslog and keep trying. */ inline static int wait_for_wip_queue_to_clear(node_local_ptr_t cnl, cache_que_head_ptr_t crwipq, cache_rec_ptr_t cr, gd_region *reg) { int ret; unsigned int lcnt; int active_cnt, wip_cnt; ret = 0; /* initialize ret in case we do not invoke "wcs_wtfini" below */ DEBUG_ONLY(active_cnt = cnl->wcs_active_lvl;) /* for debugging purposes */ wip_cnt = cnl->wcs_wip_lvl; for (lcnt = 1; ; lcnt++) { if (!crwipq->fl) break; DEBUG_ONLY(dbg_wtfini_lcnt = lcnt); /* used by "wcs_wtfini" */ ret = wcs_wtfini(reg, CHECK_IS_PROC_ALIVE_TRUE_OR_FALSE(lcnt, MAX_WIP_QWAIT), cr); if (ret || (cr && (0 == cr->dirty))) break; if (lcnt >= (MAX_WIP_QWAIT AIX_ONLY(* 4))) { /* The number of 10 ms sleeps to reach 1 min. Issue error and restart the count */ send_msg_csa(CSA_ARG(REG2CSA(reg)) VARLSTCNT(7) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_AIOQUEUESTUCK, 2, (MAX_WIP_QWAIT AIX_ONLY(* 4)), (cr ? cr->blk : 0)); lcnt = 1; /* Can't sleep for zero seconds */ } wcs_sleep(lcnt); /* Iterations 10 and above sleep for 10 ms */ if ((wip_cnt != cnl->wcs_wip_lvl) && (NULL == cr)) /* only for non-specific crs */ { /* Change in WIP queue size. Restart wait. Note that "CNL->wcs_wip_lvl" could * have even increased since we last noted it in "wip_cnt". This is because * a concurrent process in "wcs_wtstart" could have moved some more records * from the active queue to the wip queue. Restart wait even in that case. */ lcnt = 1; wip_cnt = cnl->wcs_wip_lvl; } } return ret; } #endif fis-gtm-V7.0-005/sr_unix/wcs_wt_restart.c0000644000032200000250000000533214342376330017262 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016 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 #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "aio_shim.h" #include "gtmio.h" #include "anticipatory_freeze.h" #include "wcs_wt.h" #include "gdsbgtr.h" GBLREF uint4 process_id; /* Reissues a pending qio from the WIP queue. In case of EAGAIN and holding crit, it issues a SYNCIO instead. * And if the SYNCIO succeeds, a special value of SYNCIO_MORPH_SUCCESS is returned so caller can handle this * situation appropriately (by moving the cr from wip queue to active queue or out of all queues). */ int wcs_wt_restart(unix_db_info *udi, cache_state_rec_ptr_t csr) { int save_errno; blk_hdr_ptr_t bp, save_bp; sgmnt_data_ptr_t csd; cache_que_head_ptr_t ahead; sgmnt_addrs *csa; assert(0 > SYNCIO_MORPH_SUCCESS); /* save_errno should be positive in all cases except when == SYNCIO_MORPH_SUCCESS */ csa = &udi->s_addrs; BG_TRACE_PRO_ANY(csa, wcs_wt_restart_invoked); csd = csa->hdr; bp = (blk_hdr_ptr_t)(GDS_ANY_REL2ABS(csa, csr->buffaddr)); if (!csr->wip_is_encr_buf) save_bp = bp; else save_bp = (blk_hdr_ptr_t)GDS_ANY_ENCRYPTGLOBUF(bp, csa); DB_LSEEKWRITEASYNCRESTART(csa, udi, udi->fn, udi->fd, save_bp, csr, save_errno); assert(0 == save_errno IF_LIBAIO(|| EAGAIN == save_errno)); if (0 == save_errno) csr->epid = process_id; else if (EAGAIN == save_errno) { /* ASYNC IO could not be started */ BG_TRACE_PRO_ANY(csa, wcs_wt_restart_eagain); if (csa->now_crit && !csa->region->read_only) { /* Holding crit and have read-write access to the database. * Do synchronous IO given OS does not have enough memory temporarily. */ DB_LSEEKWRITE(csa, udi, udi->fn, udi->fd, csr->aiocb.aio_offset, save_bp, csr->aiocb.aio_nbytes, save_errno); assert(0 <= save_errno); if (0 == save_errno) { /* SYNCIO succeeded. Return special status */ save_errno = SYNCIO_MORPH_SUCCESS; } } else { /* Need to reinsert this back into the active queue. * Clearing csr->epid and returning 0 indicates this to caller. */ BG_TRACE_PRO_ANY(csa, wcs_wt_restart_reinsert); csr->epid = 0; save_errno = 0; } } return save_errno; } fis-gtm-V7.0-005/sr_unix/wcs_wterror.c0000644000032200000250000001360614342376330016573 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2020 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 #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsbml.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "interlock.h" #include "relqueopi.h" #include "gdsbgtr.h" #include "aio_shim.h" #include "gtmio.h" #include "is_proc_alive.h" #include "anticipatory_freeze.h" #include "add_inter.h" #include "wcs_wt.h" #include "compswap.h" #define DBIOERR_LOGGING_PERIOD 100 #define DSKSPACE_MSG_INTERVAL 60 /* 60 seconds, epoch time */ #ifdef USE_LIBAIO GBLREF char *aio_shim_errstr; #endif error_def(ERR_DBFILERR); error_def(ERR_DBIOERR); error_def(ERR_SYSCALL); error_def(ERR_ENOSPCQIODEFER); STATICDEF volatile uint4 eagain_error_count; /* This function is called from wcs_wtstart (for noasyncio and asyncio cases) and/or wcs_wtfini (for asyncio) when they each * encounter an error in a write to the database file on disk. It could be ENOSPC or some other IO error. Handle all of them * by sending periodic syslog messages etc. */ void wcs_wterror(gd_region *reg, int4 save_errno) { unix_db_info *udi; sgmnt_addrs *csa; node_local_ptr_t cnl; gtm_uint64_t dskspace_next_fire; udi = FILE_INFO(reg); csa = &udi->s_addrs; BG_TRACE_PRO_ANY(csa, wcs_wterror_invoked); cnl = csa->nl; if (ENOSPC == save_errno) { /* Determine whether or not to ignore this error, based on when the last ENOSPC error was reported. */ dskspace_next_fire = cnl->dskspace_next_fire; if ((dskspace_next_fire + DSKSPACE_MSG_INTERVAL <= time(NULL)) /* We use a CAS instruction to ensure that concurrent accesses to this location don't fire * multiple times by different processes; the first one to swap dskspace_next_fire is the * only one to report the ENOSPC error. A blind interlock_add() would not prevent this. */ && COMPSWAP_LOCK((sm_global_latch_ptr_t)&cnl->dskspace_next_fire, dskspace_next_fire, 0, time(NULL), 0)) { /* Report ENOSPC errors for first time and every minute after that. */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(9) ERR_DBIOERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_TEXT("Error during flush write"), save_errno); if (!IS_GTM_IMAGE) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(9) ERR_DBIOERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_TEXT("Error during flush write"), save_errno); } } } else if (EAGAIN == save_errno) { /* When using POSIX AIO we don't ever expect to see an EAGAIN error. */ assert(IF_LIBAIO_ELSE(NULL != aio_shim_errstr, FALSE)); /* If EAGAIN occurs from "io_submit", do not treat it as an ERROR. We know it can happen if more than the * allocated aio slots are issued as writes concurrently by this process. In that case, the count of * wcs_wterror_invoked in file header is enough to indicate how many times such events occured. */ # ifdef USE_LIBAIO if ((NULL != aio_shim_errstr) && STRCMP(aio_shim_errstr, "io_submit()")) { # endif eagain_error_count++; if (1 == (eagain_error_count % DBIOERR_LOGGING_PERIOD)) { /* See below; every 100th failed attempt, issue a warning. We cannot issue a DBIOERR in * the case of an EAGAIN because it is innocuous and can easily be retried -- a DBIOERR * will freeze the database forcing us to not perform a retry at all. */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(12) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, LEN_AND_STR(IF_LIBAIO_ELSE(aio_shim_errstr, "aio_write()")), CALLFROM, save_errno); } # ifdef USE_LIBAIO aio_shim_errstr = NULL; } # endif } else if (ERR_ENOSPCQIODEFER != save_errno) { cnl->wtstart_errcnt++; if (1 == (cnl->wtstart_errcnt % DBIOERR_LOGGING_PERIOD)) { /* Every 100th failed attempt, issue an operator log indicating an I/O error. * wcs_wtstart is typically invoked during periodic flush timeout and since there * cannot be more than 2 pending flush timers per region, number of concurrent * processes issuing the below send_msg should be relatively small even if there * are 1000s of processes. */ /* Below assert is to account for some white-box tests which exercise this code as * well as tests which could trigger a CRYPTOPFAILED inside the encryption plugin. * Neither of those are real IO errors. */ assert(gtm_white_box_test_case_enabled || (SET_REPEAT_MSG_MASK(SET_CRYPTERR_MASK(ERR_CRYPTOPFAILED)) == save_errno)); #ifdef USE_LIBAIO if (NULL == aio_shim_errstr) { # endif send_msg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_DBIOERR, 4, REG_LEN_STR(reg), DB_LEN_STR(reg), save_errno); #ifdef USE_LIBAIO } else { /* If the error string was set, then we can output the syscall that failed as well. */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(15) ERR_DBIOERR, 4, REG_LEN_STR(reg), DB_LEN_STR(reg), ERR_SYSCALL, 5, LEN_AND_STR(aio_shim_errstr), CALLFROM, save_errno); aio_shim_errstr = NULL; } # endif } } /* If (ERR_ENOSPCQIODEFER == save_errno): DB_LSEEKWRITE above encountered ENOSPC but could not * trigger a freeze as it did not hold crit. It is okay to return as this is not a critical write. * Eventually, some crit holding process will trigger a freeze and wait for space to be freed up. * Analogously, if we detected that encryption settings have changed during a transaction, it is OK * to skip this write because this transaction will be retried after encryption settings update in * t_retry or tp_restart. */ return; } fis-gtm-V7.0-005/sr_unix/wcs_wtfini.c0000644000032200000250000003717614342376330016377 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2021 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 #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "interlock.h" #include "relqueopi.h" #include "gdsbgtr.h" #include "aio_shim.h" #include "gtmio.h" #include "is_proc_alive.h" #include "anticipatory_freeze.h" #include "add_inter.h" #include "wcs_wt.h" #include "hashtab_int4.h" #include "performcaslatchcheck.h" #include "wcs_sleep.h" #include "caller_id.h" #include "rel_quant.h" #include "sleep_cnt.h" #include "gtm_c_stack_trace.h" #include "copy.h" #include "relqop.h" #define REQUEUE_TO_FREE 0 #define REQUEUE_TO_WIP 1 #define REQUEUE_TO_ACTIVE 2 #define WTFINI_PID_ALIVE_HT_INITIAL_SIZE 4 #ifdef DEBUG /* Every N successful writes, simulate an error in one of the writes. This is to exercise "wcs_wt_restart" code. * We do not want the frequency to be too low resulting in lot of rewrites. Neither do we want it too high as * that would mean no coverage of "wcs_wt_restart". Hence the particular value chosen below. */ #define FAKE_WTERROR_FREQUENCY 256 #endif STATICDEF hash_table_int4 wtfini_pid_ht; STATICDEF boolean_t wtfini_pid_ht_reinitialized = TRUE; #ifdef DEBUG STATICDEF int dbg_skip_wcs_wt_restart; #endif GBLREF volatile int4 fast_lock_count; GBLREF int4 wtfini_in_prog; GBLREF uint4 process_id; GBLREF gd_region *gv_cur_region; error_def(ERR_DBCCERR); /* Note: In case caller has read_only access to db, wcs_wtfini cleans up finished qios but does not initiate new ones. * Returns: 0 in case of SUCCESS. non-zero (== errno) in case of FAILURE. */ int wcs_wtfini(gd_region *reg, boolean_t do_is_proc_alive_check, cache_rec_ptr_t cr2flush) { boolean_t new_pid, pid_alive; cache_que_head_ptr_t ahead, whead; cache_rec_ptr_t cr; # ifdef DEBUG cache_rec_ptr_t cr_lo, cr_hi; # endif cache_state_rec_ptr_t csr, start_csr; int requeue, ret_value; int restart_errno, status; int aio_errno, aio_retval; int4 n; node_local_ptr_t cnl; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; unix_db_info *udi; unsigned int n_bts; int lcnt; uint4 epid; ht_ent_int4 *tabent; que_ent_ptr_t next, prev, qent; void_ptr_t retcsrptr; udi = FILE_INFO(reg); csa = &udi->s_addrs; csd = csa->hdr; assert(csd->asyncio); /* caller should have ensured this */ cnl = csa->nl; assert(dba_bg == csd->acc_meth); assert(csa->now_crit); /* Or else "bg_update_phase1" (which holds crit) would get confused if a concurrent non-crit * process is running "wcs_wtfini" at the same time since it makes assumptions on the state of * OLDER and NEWER twins based on cr->dirty etc. all of which could be concurrently changing * in case "wcs_wtfini" can be invoked outside of crit. */ BG_TRACE_PRO_ANY(csa, wcs_wtfini_invoked); wtfini_in_prog++; cnl->wtfini_in_prog = process_id; ahead = &csa->acc_meth.bg.cache_state->cacheq_active; whead = &csa->acc_meth.bg.cache_state->cacheq_wip; n_bts = csd->n_bts; # ifdef DEBUG cr_lo = csa->acc_meth.bg.cache_state->cache_array + csd->bt_buckets; cr_hi = cr_lo + n_bts; # endif ret_value = 0; CHECK_ERROR_IN_WORKER_THREAD(reg, udi); if (do_is_proc_alive_check) init_hashtab_int4(&wtfini_pid_ht, WTFINI_PID_ALIVE_HT_INITIAL_SIZE, HASHTAB_COMPACT, HASHTAB_SPARE_TABLE); for (lcnt = cr2flush ? 0 : n_bts, start_csr = NULL; lcnt >= 0; lcnt--) { /* we will be attempting to take a cr off the wip queue for processing. We do not need the wbuf_dqd protection * used by wcs_get_space() and wcs_wtstart() since wcs_wtfini has crit and will have wc_blocked set anyway * if we get killed. */ if (cr2flush) { /* asked to flush a specific cr: */ /* should be dirty and have had a write issued, i.e., in the wip queue */ csr = NULL; /* assume it's none until we find it */ if (cr2flush->dirty && cr2flush->epid) { /* the if check implies cr2flush is out of the active queue at this point and is either already in * the wip queue or about to be inserted into the wip queue. cr2flush->state_que.fl being non-zero * (checked after the grab_latch below) would imply it is in the wip queue. */ ++fast_lock_count; /* Disable wcs_stale for duration */ if (grab_latch(&whead->latch, WT_LATCH_TIMEOUT_SEC, WS_25, csa)) { cr = cr2flush; csr = (cache_state_rec_ptr_t)((sm_uc_ptr_t)cr + SIZEOF(cr->blkque)); /* now that we have the wip queue header lock ensure cr2flush is still on the wip queue */ if (cr2flush->dirty && cr2flush->epid && cr2flush->state_que.fl) { /* the entry is in the wip queue */ assert(cr2flush->dirty); assert(cr2flush->epid); assert(csr); assert(csr->state_que.bl); retcsrptr = remqh((que_ent_ptr_t)((sm_uc_ptr_t)&csr->state_que + csr->state_que.bl)); if ((cache_state_rec_ptr_t)retcsrptr != csr) { /* Did not get the csr we intended so something must be wrong with cache. * Kill -9 can cause this. Assert that we were doing a crash shutdown. */ assert(WBTEST_ENABLED(WBTEST_CRASH_SHUTDOWN_EXPECTED) || WBTEST_ENABLED(WBTEST_MURUNDOWN_KILLCMT06)); SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER); ret_value = ERR_DBCCERR; break; } csr->state_que.bl = (sm_off_t)0; csr->state_que.fl = (sm_off_t)0; } else /* The entry is still in the active queue waiting to be inserted into the wip * queue. */ csr = NULL; rel_latch(&whead->latch); } else csr = NULL; /* did not get the lock */ --fast_lock_count; assert(0 <= fast_lock_count); } /* else cr2flush is either in the active queue or in the free queue (i.e. not dirty). * In either case, we cannot handle it in this function so return. */ } else { csr = (cache_state_rec_ptr_t)REMQHI((que_head_ptr_t)whead); if (INTERLOCK_FAIL == (INTPTR_T)csr) { assert(FALSE); SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER); BG_TRACE_PRO_ANY(csa, wcb_wtfini_lckfail1); ret_value = ERR_DBCCERR; break; } } if (NULL == csr) break; /* empty queue */ assert(0 == csr->state_que.fl); assert(0 == csr->state_que.bl); if (csr == start_csr) { status = INSQHI((que_ent_ptr_t)csr, (que_head_ptr_t)whead); if (INTERLOCK_FAIL == status) { assert(FALSE); SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER); BG_TRACE_PRO_ANY(csa, wcb_wtfini_lckfail2); ret_value = ERR_DBCCERR; } break; /* looped the queue */ } # ifdef DEBUG cr = (cache_rec_ptr_t)((sm_uc_ptr_t)csr - SIZEOF(cr->blkque)); assert(cr >= cr_lo); assert(cr < cr_hi); # endif assert(csr->dirty); assert(CR_BLKEMPTY != csr->blk); AIO_SHIM_ERROR(&(csr->aiocb), aio_errno); assert(EBADF != aio_errno); if ((ENOSYS == aio_errno) || (EINVAL == aio_errno)) { assert(FALSE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("aio_error()"), CALLFROM, aio_errno); } restart_errno = 0; requeue = REQUEUE_TO_WIP; epid = csr->epid; if (EINPROGRESS == aio_errno) { if (do_is_proc_alive_check && (process_id != epid)) { /* Check if process is alive. But avoid calling "is_proc_alive" more than once per pid * (system call) by maintaining a hash table of pids that we have already called for. * Use hashtable if pid is found. If not use "is_proc_alive". * This way if there are thousands of cache-records in the WIP queue corresponding to dead * pids, we will not do thousands of "kill" system calls while holding crit in "wcs_wtfini". */ assert(SIZEOF(process_id) == SIZEOF(uint4)); /* hashtab_int4 routines do not handle 0 value so bump return value from is_proc_alive * (TRUE or FALSE) by 1 when storing and decrement by 1 after lookup. */ if (NULL != (tabent = lookup_hashtab_int4(&wtfini_pid_ht, (uint4 *)(&epid)))) pid_alive = (boolean_t)(UINTPTR_T)tabent->value - 1; else { pid_alive = is_proc_alive(epid, 0); new_pid = add_hashtab_int4(&wtfini_pid_ht, (uint4 *)&epid, (void *)(UINTPTR_T)(pid_alive + 1), &tabent); assert(new_pid); } /* If pid that issued the original write is no longer alive, we do not know if the aiocb * structure was fully initialized (e.g. aiocb.aio_nbytes, aiocb.aio_offset etc.) when the * pid died or not. So we cannot safely issue a rewrite (i.e. LSEEKWRITEASYNCRESTART) which * assumes these are initialized. Instead we need to issue a fresh write (LSEEKWRITEASYNCSTART). * The only function capable of issuing this is "wcs_wtstart" so put this cr back in active queue. */ if (!pid_alive) { WCS_OPS_TRACE(csa, process_id, wcs_ops_wtfini1, cr->blk, GDS_ANY_ABS2REL(csa,cr), cr->dirty, dbg_wtfini_lcnt, epid); if (!csr->bt_index && !csr->in_cw_set) { /* This is an older twin so we do not need the update anymore. * For comment on why "csr->in_cw_set" needs to be additionally checked, * see usage of "csr->in_cw_set" a little later for a descriptive comment. */ requeue = REQUEUE_TO_FREE; /* csr->epid = 0 will happen a little later as part of REQUEUE_TO_FREE handling */ } else { requeue = REQUEUE_TO_ACTIVE; csr->epid = 0; /* Clear this since the process that issued the write is dead */ } } } } else { /* Status is available for the I/O. Note: In case IO was canceled, "aio_return" will return -1. */ /* aio_errno == 0, if the request completed successfully. */ /* aio_errno > 0, A positive error number, if the asynchronous I/O operation failed. * This is the same value that would have been stored in the errno variable in the * case of the corresponding synchronous "write()" call. */ assert(EINTR != aio_errno); AIO_SHIM_RETURN(&(csr->aiocb), aio_retval); /* get the return value of the i/o */ # ifdef DEBUG /* Fake an error once in a while. But do not do that in AIX if we are inside "wcs_flu" as we * have seen a lot of test failures because "wcs_flu" takes more than 1 minute to reflush * the dirty cache-record after a fake-error inside "wcs_wtfini". */ if (FAKE_WTERROR_FREQUENCY == ++dbg_skip_wcs_wt_restart) dbg_skip_wcs_wt_restart = 0; if ((0 == dbg_skip_wcs_wt_restart) && (0 < aio_retval) AIX_ONLY(&& (cnl->wcsflu_pid != process_id))) { WCS_OPS_TRACE(csa, process_id, wcs_ops_wtfini2, cr->blk, GDS_ANY_ABS2REL(csa,cr), \ cr->dirty, dbg_wtfini_lcnt, aio_retval); aio_retval = 0; } # endif if (0 < aio_retval) { /* async IO completed successfully with no errors */ assert(0 == aio_errno); /* Mark this block as written */ csr->needs_first_write = FALSE; /* We can move this csr from the WIP queue to the FREE queue now that the write is complete. * There is one exception though. If the write of an OLDER twin completes fine (0 == csr->bt_index) * AND if csr->in_cw_set is still non-zero, it implies PHASE2 commit is in progress for this csr * concurrently by another pid and since "in_cw_set" is still non-zero, it implies the buffer is * likely needed by that pid (e.g. secshr_db_clnup/wcs_recover to complete the flush of the * before-image block to an online backup file (in case of an error in the midst of commit). * In that case, we should NOT touch csr->blk so keep csr in the WIP queue until "in_cw_set" clears. * * Now sometimes, the update process might have been killed, leaving "in_cw_set" to be non-zero. * We need to see whether the update process is still alive. If not, reset in_cw_set. */ assert(REQUEUE_TO_WIP == requeue); if (!csr->bt_index && csr->in_cw_set && !is_proc_alive(csr->in_cw_set, 0)) csr->in_cw_set = 0; if (csr->bt_index || !csr->in_cw_set) requeue = REQUEUE_TO_FREE; } else { /* aio_errno can be 0 if we faked an aio error (by setting "aio_retval = 0" above) OR * if the process that was doing the AIO got killed (and so the OS decided to abandon the IO). * Assert accordingly. */ assert((0 < aio_errno) || ((0 == aio_errno) && (!dbg_skip_wcs_wt_restart || (epid && !is_proc_alive(epid, 0))))); WCS_OPS_TRACE(csa, process_id, wcs_ops_wtfini3, cr->blk, GDS_ANY_ABS2REL(csa,cr), \ cr->dirty, dbg_wtfini_lcnt, aio_errno); /* Now that the IO is complete with some sort of error, handle the asyncio like is done in * wcs_wtstart for syncio. The only exception is ECANCELED which is because the async IO got * canceled. In this case it is not a real IO error. Same with dbg-only aio_errno of 0 which * is to test the "wcs_wt_restart" logic (nothing to do with "wcs_wterror"). */ if ((ECANCELED != aio_errno) && (0 != aio_errno)) { if (!ret_value) ret_value = aio_errno; wcs_wterror(reg, aio_errno); } /* If a NEWER twin has been formed (indicated by csr->bt_index being 0) and an error occurs in * the write of the OLDER twin, one would be tempted to ignore that error, move the OLDER twin * from the WIP queue to the FREE queue and focus on the NEWER twin. But it is possible the OLDER * twin is BEFORE an EPOCH whereas the NEWER twin is AFTER. In that case, we need the OLDER twin * flushed to disk to catch the state of the database as of the EPOCH. So we keep the OLDER twin * in the WIP queue until its write completes. */ /* Reissue the IO */ restart_errno = wcs_wt_restart(udi, csr); /* sets "csr->epid" */ if (SYNCIO_MORPH_SUCCESS == restart_errno) requeue = REQUEUE_TO_FREE; else if (!restart_errno && !csr->epid) { /* Case where IO was not reissued (either because we did not have crit or because we did * not have read-write access to db. Put it back in active queue. */ requeue = REQUEUE_TO_ACTIVE; } } } if (REQUEUE_TO_WIP == requeue) { status = INSQTI((que_ent_ptr_t)csr, (que_head_ptr_t)whead); if (INTERLOCK_FAIL == status) { assert(FALSE); SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER); BG_TRACE_PRO_ANY(csa, wcb_wtfini_lckfail3); ret_value = ERR_DBCCERR; break; } if (NULL == start_csr) start_csr = csr; } else if (REQUEUE_TO_FREE == requeue) { csr->flushed_dirty_tn = csr->dirty; csr->dirty = 0; cnl->wcs_buffs_freed++; csr->epid = 0; SUB_ENT_FROM_WIP_QUE_CNT(cnl); ADD_ENT_TO_FREE_QUE_CNT(cnl); if (csr->twin) BREAK_TWIN(csr, csa); CLEAR_BUFF_UPDATE_LOCK(csr, &cnl->db_latch); } else { assert(REQUEUE_TO_ACTIVE == requeue); CLEAR_BUFF_UPDATE_LOCK(csr, &cnl->db_latch); ahead = &csa->acc_meth.bg.cache_state->cacheq_active; REINSERT_CR_AT_TAIL(csr, ahead, n, csa, csd, wcb_wtfini_lckfail4); if (INTERLOCK_FAIL == n) { ret_value = ERR_DBCCERR; break; } SUB_ENT_FROM_WIP_QUE_CNT(cnl); ADD_ENT_TO_ACTIVE_QUE_CNT(cnl); WCS_OPS_TRACE(csa, process_id, wcs_ops_wtfini4, cr->blk, GDS_ANY_ABS2REL(csa,cr), cr->dirty, \ dbg_wtfini_lcnt, epid); } } cnl->wtfini_in_prog = 0; wtfini_in_prog--; assert(0 <= wtfini_in_prog); if (0 > wtfini_in_prog) wtfini_in_prog = 0; assert(!ret_value || ENOSPC == ret_value || ERR_DBCCERR == ret_value); return ret_value; } fis-gtm-V7.0-005/sr_unix/wcs_wtfini_nocrit.c0000644000032200000250000000356614342376330017751 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2020 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 #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "wcs_sleep.h" #include "filestruct.h" #include "gtmio.h" #include "wcs_wt.h" #include "sleep_cnt.h" #include "aio_shim.h" GBLREF uint4 process_id; error_def(ERR_AIOBUFSTUCK); error_def(ERR_DBFILERR); /* This function goes through the list of crs that is passed in and waits until all * of them have their AIO status available before returning. If the cr is no longer * dirty or another process has issued a write, the function goes to the next cr. */ void wcs_wtfini_nocrit(gd_region *reg, wtstart_cr_list_t *cr_list_ptr) { int lcnt, ret; cache_rec_ptr_t cr, *crbeg, *crtop, *crptr; crbeg = &cr_list_ptr->listcrs[0]; crtop = crbeg + cr_list_ptr->numcrs; for (crptr = crbeg; crptr < crtop; crptr++) { cr = *crptr; for (lcnt = 1; cr->dirty && (cr->epid == process_id); lcnt++) { /* dirty, i/o has been issued by our process and there is no i/o status available yet */ AIO_SHIM_ERROR(&cr->aiocb, ret); if (EINPROGRESS != ret) break; if (0 == lcnt % SLEEP_ONE_MIN) { /* let them know we have been waiting a while */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(11) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_AIOBUFSTUCK, 5, (lcnt / SLEEP_ONE_MIN), cr->epid, &(cr->blk), &(cr->blk), EINPROGRESS); } wcs_sleep(lcnt); /* so wait a while */ } } } fis-gtm-V7.0-005/sr_unix/wcs_wtstart.c0000644000032200000250000010124714342376330016576 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_signal.h" /* needed for VSIG_ATOMIC_T */ #include "gtm_stdio.h" #include "aswp.h" #include "copy.h" #include "error.h" #include "gdsroot.h" #include "gtm_facility.h" #include "gdskill.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsbml.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "jnl.h" #include "iosp.h" /* required for SS_NORMAL for use with msyncs */ #include "interlock.h" #include "io.h" #include "gdsbgtr.h" #include "aio_shim.h" #include "gtmio.h" #include "relqueopi.h" #include "gt_timer.h" #include "send_msg.h" #include "gtmmsg.h" #include "wcs_flu.h" #include "add_inter.h" #include "wcs_recover.h" #include "gtm_string.h" #include "have_crit.h" #include "gds_blk_downgrade.h" #include "deferred_signal_handler.h" #include "memcoherency.h" #include "wbox_test_init.h" #include "wcs_clean_dbsync.h" #include "anticipatory_freeze.h" #include "gtmcrypt.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "t_retry.h" #include "min_max.h" #include "gtmimagename.h" #include "util.h" #include "wcs_backoff.h" #include "wcs_wt.h" #include "performcaslatchcheck.h" #include "wcs_sleep.h" #include "caller_id.h" #include "rel_quant.h" #include "sleep_cnt.h" #include "gtm_c_stack_trace.h" #include "relqop.h" #ifdef DEBUG GBLREF int4 exit_state; STATICDEF int wcs_wtstart_count; /* White-box-test-activated macro to sleep in one of the predetermined places (based on the count variable) * inside wcs_wtstart. The sleep allows for the delivery of an interrupt in a specific window of code. */ # define SLEEP_ON_WBOX_COUNT(COUNT) \ { \ if (WBTEST_ENABLED(WBTEST_SLEEP_IN_WCS_WTSTART) \ && (COUNT == (gtm_white_box_test_case_count % 100))) \ { \ if ((gtm_white_box_test_case_count / 100) == ++wcs_wtstart_count) \ { /* Resetting this allows us to avoid redundant sleeps while having the \ * white-box logic variables still enabled (to avoid asserts). \ */ \ gtm_white_box_test_case_count = 0; \ DBGFPF((stderr, "WCS_WTSTART: STARTING SLEEP\n")); \ while (TRUE) \ { \ SHORT_SLEEP(999); \ if (0 < exit_state) \ DBGFPF((stderr, "exit_state is %d\n", exit_state)); \ } \ } \ } \ } #else # define SLEEP_ON_WBOX_COUNT(COUNT) #endif GBLREF uint4 process_id; GBLREF sm_uc_ptr_t reformat_buffer; GBLREF int reformat_buffer_len; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data *cs_data; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF uint4 dollar_tlevel; GBLREF uint4 update_trans; GBLREF uint4 mu_reorg_encrypt_in_prog; GBLREF sgmnt_addrs *reorg_encrypt_restart_csa; GBLREF bool in_mupip_freeze; GBLREF boolean_t wcs_noasyncio; #ifdef DEBUG GBLREF volatile int reformat_buffer_in_use; GBLREF volatile int4 gtmMallocDepth; #endif GBLREF volatile int4 fast_lock_count; error_def(ERR_DBCCERR); error_def(ERR_ENOSPCQIODEFER); error_def(ERR_GBLOFLOW); error_def(ERR_JNLFSYNCERR); error_def(ERR_JNLWRTDEFER); error_def(ERR_JNLWRTNOWWRTR); error_def(ERR_SYSCALL); error_def(ERR_TEXT); int4 wcs_wtstart(gd_region *region, int4 writes, wtstart_cr_list_t *cr_list_ptr, cache_rec_ptr_t cr2flush) { blk_hdr_ptr_t bp, save_bp; boolean_t need_jnl_sync, queue_empty, got_lock, bmp_status, do_asyncio, wtfini_called_once; cache_que_head_ptr_t ahead, whead; cache_state_rec_ptr_t csr, csrfirst; int4 err_status = 0, n, n1, n2, max_ent, max_writes, save_errno; size_t size ; jnl_buffer_ptr_t jb; jnl_private_control *jpc; node_local_ptr_t cnl; off_t blk_1_off, offset; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; sm_uc_ptr_t blk_ptr; uint4 saved_dsk_addr; unix_db_info *udi; cache_rec_ptr_t cr, cr_lo, cr_hi; static int4 error_message_loop_count = 0; uint4 index; boolean_t is_mm, was_crit; uint4 curr_wbox_seq_num; int try_sleep, rc; gd_region *sav_cur_region; sgmnt_addrs *sav_cs_addrs; sgmnt_data *sav_cs_data; jnlpool_addrs_ptr_t sav_jnlpool; jnlpool_addrs_ptr_t local_jnlpool; /* needed by INST_FREEZE_ON_ERROR_POLICY_CSA */ intrpt_state_t prev_intrpt_state; char *in, *out; int in_len; int4 gtmcrypt_errno = 0; gd_segment *seg; boolean_t use_new_key, skip_in_trans, skip_sync, sync_keys; que_ent_ptr_t next, prev; void_ptr_t retcsrptr; boolean_t keep_buff_lock, pushed_region; cache_rec_ptr_t older_twin; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (cr_list_ptr) cr_list_ptr->numcrs = 0; udi = FILE_INFO(region); csa = &udi->s_addrs; pushed_region = INST_FREEZE_ON_ERROR_POLICY_CSA(csa, local_jnlpool); if (pushed_region) PUSH_GV_CUR_REGION(region, sav_cur_region, sav_cs_addrs, sav_cs_data, sav_jnlpool); csd = csa->hdr; is_mm = (dba_mm == csd->acc_meth); assert(is_mm || (dba_bg == csd->acc_meth)); BG_TRACE_PRO_ANY(csa, wrt_calls); /* Calls to wcs_wtstart */ /* If this process is already in wcs_wtstart for this region, we won't interrupt it again */ cnl = csa->nl; if (csa->in_wtstart) { WCS_OPS_TRACE(csa, process_id, wcs_ops_wtstart1, 0, 0, 0, 0, 0); BG_TRACE_PRO_ANY(csa, wrt_busy); if (pushed_region) POP_GV_CUR_REGION(sav_cur_region, sav_cs_addrs, sav_cs_data, sav_jnlpool); return err_status; /* Already here, get out */ } /* Defer interrupts to protect against an inconsistent state caused by mismatch of such values as * cnl->intent_wtstart and cnl->in_wtstart. */ DEFER_INTERRUPTS(INTRPT_IN_WCS_WTSTART, prev_intrpt_state); INCR_INTENT_WTSTART(cnl); /* signal intent to enter wcs_wtstart */ /* the above interlocked instruction does the appropriate write memory barrier to publish this change to the world */ SHM_READ_MEMORY_BARRIER; /* need to do this to ensure uptodate value of cnl->wc_blocked is read */ if (WC_BLOCK_RECOVER == cnl->wc_blocked) { WCS_OPS_TRACE(csa, process_id, wcs_ops_wtstart2, 0, 0, 0, 0, 0); DECR_INTENT_WTSTART(cnl); BG_TRACE_PRO_ANY(csa, wrt_blocked); if (pushed_region) POP_GV_CUR_REGION(sav_cur_region, sav_cs_addrs, sav_cs_data, sav_jnlpool); ENABLE_INTERRUPTS(INTRPT_IN_WCS_WTSTART, prev_intrpt_state); return err_status; } SLEEP_ON_WBOX_COUNT(1); csa->in_wtstart = TRUE; /* Tell ourselves we're here and make the csa->in_wtstart (private copy) */ /* Ideally, we would like another SLEEP_ON_WBOX_COUNT here, but that could cause assert failures in concurrent wcs_wtstarts. * Because it is highly unlikely for an interrupt-deferred process to get killed at exactly this spot, do not test that. */ INCR_CNT(&cnl->in_wtstart, &cnl->wc_var_lock); /* and cnl->in_wtstart (shared copy) assignments as close as possible. */ if (FROZEN_CHILLED(csa) && !FREEZE_LATCH_HELD(csa)) { CAREFUL_DECR_CNT(cnl->in_wtstart, cnl->wc_var_lock); DECR_INTENT_WTSTART(cnl); csa->in_wtstart = FALSE; if (pushed_region) POP_GV_CUR_REGION(sav_cur_region, sav_cs_addrs, sav_cs_data, sav_jnlpool); ENABLE_INTERRUPTS(INTRPT_IN_WCS_WTSTART, prev_intrpt_state); /* Return non-zero in order to break wcs_wtstart_fini() out of its loop. Ignored elsewhere. */ return EAGAIN; } SLEEP_ON_WBOX_COUNT(2); SAVE_WTSTART_PID(cnl, process_id, index); assert((cnl->in_wtstart > 0) && csa->in_wtstart); max_ent = csd->n_bts; if (0 >= (max_writes = writes)) /* If specified writes to do, use that.. */ max_writes = csd->n_wrt_per_flu; /* else, max writes is how many blocks there are */ jpc = csa->jnl; assert(!JNL_ALLOWED(csd) ||( NULL != jpc)); /* if journaling is allowed, we better have non-null csa->jnl */ if (JNL_ENABLED(csd) && (NULL != jpc) && (NOJNL != jpc->channel)) { /* Before flushing the database buffers, give journal flushing a nudge. Any failures in writing to the * journal are not handled here since the main purpose of wcs_wtstart is to flush the database buffers * (not journal buffers). The journal issue will be caught later (in jnl_flush or some other jnl routine) * and appropriate errors, including triggering jnl_file_lost (if JNLCNTRL error) will be issued there. */ jnl_qio_start(jpc); } if (is_mm) { queue_empty = TRUE; n1 = 1; /* set to a non-zero value so dbsync timer canceling (if needed) can happen */ goto writes_completed; /* to avoid unnecessary IF checks in the more common case (BG) */ } ahead = &csa->acc_meth.bg.cache_state->cacheq_active; whead = &csa->acc_meth.bg.cache_state->cacheq_wip; cr_lo = csa->acc_meth.bg.cache_state->cache_array + csd->bt_buckets; cr_hi = cr_lo + csd->n_bts; assert(((sm_long_t)ahead & 7) == 0); queue_empty = FALSE; csa->wbuf_dqd++; /* Tell rundown we have an orphaned block in case of interrupt */ SLEEP_ON_WBOX_COUNT(3); was_crit = csa->now_crit; SLEEP_ON_WBOX_COUNT(4); skip_in_trans = FALSE; assert(!is_mm); /* MM should have bypassed this "for" loop completely */ wtfini_called_once = FALSE; WCS_OPS_TRACE(csa, process_id, wcs_ops_wtstart3, 0, 0, 0, 0, 0); for (n1 = n2 = 0, csrfirst = NULL; (n1 < max_ent) && (n2 < max_writes) && !cnl->wc_blocked; ++n1) { /* If not-crit, avoid REMQHI by peeking at the active queue and if it is found to have a 0 fl link, assume * there is nothing to flush and break out of the loop. This avoids unnecessary interlock usage (GTM-7635). * If holding crit, we cannot safely avoid the REMQHI so interlock usage is avoided only in the no-crit case. */ if (!was_crit && (0 == ahead->fl)) csr = NULL; keep_buff_lock = FALSE; if (cr2flush) { /* asked to flush a specific cr: */ /* should be dirty and not have had a write issued, i.e., in the active queue */ max_ent = 1; max_writes = 1; csr = NULL; /* assume it's none until we find it */ if (cr2flush->dirty && !cr2flush->epid) { /* if it is in the active queue */ ++fast_lock_count; /* Disable wcs_stale for duration */ if (grab_latch(&ahead->latch, WT_LATCH_TIMEOUT_SEC, WS_26, csa)) { cr = cr2flush; csr = (cache_state_rec_ptr_t)((sm_uc_ptr_t)cr + SIZEOF(cr->blkque)); if (csr->dirty && !csr->epid && csr->state_que.fl) { /* Now that we know csr is in the active queue, remove it. */ retcsrptr = remqh((que_ent_ptr_t)((sm_uc_ptr_t)&csr->state_que + csr->state_que.bl)); if ((cache_state_rec_ptr_t)retcsrptr != csr) { /* Did not get the csr we intended so something must be wrong with cache. * Kill -9 can cause this. Assert that we were doing a crash shutdown. */ assert(WBTEST_ENABLED(WBTEST_CRASH_SHUTDOWN_EXPECTED) || WBTEST_ENABLED(WBTEST_MURUNDOWN_KILLCMT06)); SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER); err_status = ERR_DBCCERR; break; } csr->state_que.fl = (sm_off_t)0; csr->state_que.bl = (sm_off_t)0; /* LOCK_BUFF_FOR_WRITE needs to happens AFTER the remqh just like * the non-cr2flush case because bg_update_phase2() relies on this * ordering for reinserting a cr into the active queue. */ LOCK_BUFF_FOR_WRITE(csr, n, &cnl->db_latch); assert(WRITE_LATCH_VAL(csr) >= LATCH_CLEAR); assert(WRITE_LATCH_VAL(csr) <= LATCH_CONFLICT); if (OWN_BUFF(n)) { assert(WRITE_LATCH_VAL(csr) > LATCH_CLEAR); assert(0 == n); keep_buff_lock = TRUE; } else csr = NULL; /* another process is taking care of this cr */ } else csr = NULL; /* no longer on the active queue */ rel_latch(&ahead->latch); } else csr = NULL; /* did not get the lock */ --fast_lock_count; assert(0 <= fast_lock_count); } } else { csr = (cache_state_rec_ptr_t)REMQHI((que_head_ptr_t)ahead); if (INTERLOCK_FAIL == (INTPTR_T)csr) { assert(FALSE); SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER); BG_TRACE_PRO_ANY(csa, wcb_wtstart_lckfail1); break; } cr = (cache_rec_ptr_t)((sm_uc_ptr_t)csr - SIZEOF(cr->blkque)); } if (NULL == csr) break; /* the queue is empty */ assert(!FROZEN_CHILLED(csa) || FREEZE_LATCH_HELD(csa)); if (csr == csrfirst) { /* completed a tour of the queue */ queue_empty = FALSE; assert(!keep_buff_lock); /* the if check and lock clear is for PRO just in case */ if (keep_buff_lock) CLEAR_BUFF_UPDATE_LOCK(csr, &cnl->db_latch); REINSERT_CR_AT_TAIL(csr, ahead, n, csa, csd, wcb_wtstart_lckfail2); if (INTERLOCK_FAIL == n) err_status = ERR_DBCCERR; break; } assert(!CR_NOT_ALIGNED(cr, cr_lo) && !CR_NOT_IN_RANGE(cr, cr_lo, cr_hi)); if (CR_BLKEMPTY == csr->blk) { /* must be left by t_commit_cleanup - removing it from the queue and the following * completes the cleanup */ WCS_OPS_TRACE(csa, process_id, wcs_ops_wtstart4, cr->blk, GDS_ANY_ABS2REL(csa,cr), cr->dirty, 0, 0); assert(0 != csr->dirty); assert(csr->data_invalid); csr->data_invalid = FALSE; csr->dirty = 0; assert(!keep_buff_lock); /* the if check and lock clear is for PRO just in case */ if (keep_buff_lock) CLEAR_BUFF_UPDATE_LOCK(csr, &cnl->db_latch); ADD_ENT_TO_FREE_QUE_CNT(cnl); assert(LATCH_CLEAR == WRITE_LATCH_VAL(csr)); queue_empty = !SUB_ENT_FROM_ACTIVE_QUE_CNT(cnl); continue; } /* If journaling, write only if the journal file is up to date and no jnl-switches occurred */ if (JNL_ENABLED(csd)) { /* this looks to be a long lock and hence should use a mutex */ jb = jpc->jnl_buff; need_jnl_sync = (csr->jnl_addr > jb->fsync_dskaddr); assert(!need_jnl_sync || ((NOJNL) != jpc->channel) || (cnl->wcsflu_pid != process_id)); got_lock = FALSE; if ((csr->jnl_addr > jb->dskaddr) || (need_jnl_sync && (NOJNL == jpc->channel || (FALSE == (got_lock = GET_SWAPLOCK(&jb->fsync_in_prog_latch)))))) { WCS_OPS_TRACE(csa, process_id, wcs_ops_wtstart5, cr->blk, GDS_ANY_ABS2REL(csa,cr), cr->dirty, \ need_jnl_sync, got_lock); if (need_jnl_sync) BG_TRACE_PRO_ANY(csa, n_jnl_fsync_tries); if (keep_buff_lock) CLEAR_BUFF_UPDATE_LOCK(csr, &cnl->db_latch); REINSERT_CR_AT_TAIL(csr, ahead, n, csa, csd, wcb_wtstart_lckfail3); if (INTERLOCK_FAIL == n) { err_status = ERR_DBCCERR; break; } if (NULL == csrfirst) csrfirst = csr; continue; } else if (got_lock) { saved_dsk_addr = jb->dskaddr; if (jpc->sync_io) { /* We need to maintain the fsync control fields irrespective of the type of IO, * because we might switch between these at any time. */ jb->fsync_dskaddr = saved_dsk_addr; } else { GTM_JNL_FSYNC(csa, jpc->channel, rc); if (-1 == rc) { assert(FALSE); send_msg_csa(CSA_ARG(csa) VARLSTCNT(9) ERR_JNLFSYNCERR, 2, JNL_LEN_STR(csd), ERR_TEXT, 2, RTS_ERROR_TEXT("Error with fsync"), errno); RELEASE_SWAPLOCK(&jb->fsync_in_prog_latch); if (keep_buff_lock) CLEAR_BUFF_UPDATE_LOCK(csr, &cnl->db_latch); REINSERT_CR_AT_TAIL(csr, ahead, n, csa, csd, wcb_wtstart_lckfail3); if (INTERLOCK_FAIL == n) { err_status = ERR_DBCCERR; break; } if (NULL == csrfirst) csrfirst = csr; continue; } else { jb->fsync_dskaddr = saved_dsk_addr; BG_TRACE_PRO_ANY(csa, n_jnl_fsyncs); } } RELEASE_SWAPLOCK(&jb->fsync_in_prog_latch); } } /* If twin exists then do not issue write of NEWER twin until OLDER twin has been removed from WIP queue. * The act of removal from the WIP queue clears "csr->twin" so checking just that is enough. */ if (csr->twin) { assert(csd->asyncio); /* Assert that ASYNCIO is turned ON as that is a necessity for twinning */ /* Check if crit can be obtained right away. If so, call "wcs_wtfini" after getting crit. * And recheck if the "twin" has been broken. If so proceed with the write. Else skip this write. * Do not call heavyweight "wcs_wtfini" more than once per "wcs_wtstart" call. * Also we are meddling with active queue now so we cannot risk a "wcs_recover" call inside * "grab_crit_immediate" hence the OK_FOR_WCS_RECOVER_FALSE usage below. */ if (!wtfini_called_once && (was_crit || grab_crit_immediate(region, OK_FOR_WCS_RECOVER_FALSE, NOT_APPLICABLE))) { if (csr->twin) { DEBUG_ONLY(dbg_wtfini_lcnt = dbg_wtfini_wcs_wtstart); /* used by "wcs_wtfini" */ older_twin = (csr->bt_index ? (cache_rec_ptr_t)GDS_ANY_REL2ABS(csa, csr->twin) : cr); assert(!older_twin->bt_index); wcs_wtfini(region, CHECK_IS_PROC_ALIVE_FALSE, older_twin); wtfini_called_once = TRUE; } if (!was_crit) rel_crit(region); } /* Note that in the most common case, csr will be the NEWER twin. But it is possible csr is the OLDER * twin too. For example, if the OLDER twin's write got aborted because the process that initiated * the write got killed and "wcs_wtfini" moved the csr back into the active queue. csr->bt_index * being non-zero indicates it is a NEWER twin in which case we need to wait for the twin link to be broken. */ if (csr->twin && csr->bt_index) { WCS_OPS_TRACE(csa, process_id, wcs_ops_wtstart6, cr->blk, GDS_ANY_ABS2REL(csa,cr), cr->dirty, \ cr->bt_index, 0); if (keep_buff_lock) CLEAR_BUFF_UPDATE_LOCK(csr, &cnl->db_latch); REINSERT_CR_AT_TAIL(csr, ahead, n, csa, csd, wcb_wtstart_lckfail3); if (INTERLOCK_FAIL == n) { err_status = ERR_DBCCERR; break; } if (NULL == csrfirst) csrfirst = csr; continue; } } csr->aio_issued = FALSE; /* set this to TRUE before csr->epid is set to a non-zero value. * To avoid out-of-order execution place this BEFORE the LOCK_BUFF_FOR_WRITE. * It does not hurt in the case we skip the "if (OWN_BUFF(n))" check. */ if (!keep_buff_lock) LOCK_BUFF_FOR_WRITE(csr, n, &cnl->db_latch); else assert(OWN_BUFF(n)); /* since we keep it we better own it */ assert(WRITE_LATCH_VAL(csr) >= LATCH_CLEAR); assert(WRITE_LATCH_VAL(csr) <= LATCH_CONFLICT); if (OWN_BUFF(n)) { /* sole owner */ assert(csr->dirty); assert(WRITE_LATCH_VAL(csr) > LATCH_CLEAR); assert(0 == n); /* We're going to write this block out now */ save_errno = 0; assert(FALSE == csr->data_invalid); /* check that buffer has valid data */ csr->epid = process_id; CR_BUFFER_CHECK1(region, csa, csd, cr, cr_lo, cr_hi); bp = (blk_hdr_ptr_t)(GDS_ANY_REL2ABS(csa, csr->buffaddr)); VALIDATE_BM_BLK(csr->blk, bp, csa, region, bmp_status); /* bmp_status holds bmp buffer's validity */ /* GDSV4 (0) version uses this field as a block length so should always be > 0. Assert that. * There is one exception in that we might have a crash test where a process was killed just before * populating a block in bg_update_phase2 when cr->data_invalid is still 0 but cr->in_tend is non-zero * (so the block-header is still null) in which case "wcs_recover" would have kept cr->dirty non-zero * even though the block-header is empty. So assert that. Note that gds_blk_downgrade has a safety * check for bver == 0 and returns immediately in that case so it is okay to call it with a 0 bver in pro. */ assert((((blk_hdr_ptr_t)bp)->bver) || WBTEST_ENABLED(WBTEST_CRASH_SHUTDOWN_EXPECTED) || WBTEST_ENABLED(WBTEST_MURUNDOWN_KILLCMT06)); if (IS_GDS_BLK_DOWNGRADE_NEEDED(csr->ondsk_blkver)) { /* Need to downgrade/reformat this block back to a previous format. */ assert(!csd->asyncio); /* asyncio & V4 format are not supported together */ assert((0 == reformat_buffer_in_use) || process_exiting); DEBUG_ONLY(reformat_buffer_in_use++;) DEBUG_DYNGRD_ONLY(PRINTF("WCS_WTSTART: Block %d being dynamically downgraded on write\n", \ csr->blk)); if (csd->blk_size > reformat_buffer_len) { /* Buffer not big enough (or does not exist) * .. get a new one releasing old if it exists */ assert(0 == gtmMallocDepth); /* should not be in a nested free/malloc */ if (reformat_buffer) free(reformat_buffer); /* Different blksized databases in use * .. keep only largest one */ reformat_buffer = malloc(csd->blk_size); reformat_buffer_len = csd->blk_size; } gds_blk_downgrade((v15_blk_hdr_ptr_t)reformat_buffer, (blk_hdr_ptr_t)bp); bp = (blk_hdr_ptr_t)reformat_buffer; size = (((v15_blk_hdr_ptr_t)bp)->bsiz + 1) & ~1; } else DEBUG_ONLY(if ((GDSV7 >= csr->ondsk_blkver) && (GDSV6 <= csr->ondsk_blkver))) size = (bp->bsiz + 1) & ~1; # ifdef DEBUG else { assert((GDSV7 >= csr->ondsk_blkver) && (GDSV6 <= csr->ondsk_blkver)); } # endif if (csd->write_fullblk) { /* See similiar logic in wcs_wtstart.c */ # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_FULLBLKWRT_DB) && (3 == csr->blk)) { DBGFPF((stdout,"Rounding the write size: %d with %d\n", size, \ ((FULL_DATABASE_WRITE == csd->write_fullblk && csr->needs_first_write) ? \ csd->blk_size : csa->fullblockwrite_len))); } # endif size = (int)ROUND_UP(size, (FULL_DATABASE_WRITE == csd->write_fullblk && csr->needs_first_write) ? csd->blk_size : csa->fullblockwrite_len); } # ifdef DEBUG else if (WBTEST_ENABLED(WBTEST_FULLBLKWRT_DB) && (3 == csr->blk)) DBGFPF((stdout, "Not rounding the write size\n")); # endif assert(size <= csd->blk_size); INCR_GVSTATS_COUNTER(csa, cnl, n_dsk_write, 1); save_bp = bp; /* Encryption settings in the database file header cannot change at this time because a concurrent * MUPIP REORG -ENCRYPT process should wait for all ongoing wcs_wtstarts to finish before * proceeding. Therefore, we can safely reference csd to (re)initialize the encryption handles based * on the hashes in the file header. */ use_new_key = USES_NEW_KEY(csd); if (IS_ENCRYPTED(csd->is_encrypted) || use_new_key) { seg = region->dyn.addr; assert(NULL != csa->encr_ptr); skip_sync = FALSE; sync_keys = FALSE; if (csa->encr_ptr->reorg_encrypt_cycle != cnl->reorg_encrypt_cycle) { assert(!mu_reorg_encrypt_in_prog); if (IS_NOT_SAFE_TO_SYNC_NEW_KEYS(dollar_tlevel, update_trans)) skip_sync = TRUE; else { sync_keys = TRUE; assert(NULL == reorg_encrypt_restart_csa); } } else if (NULL != reorg_encrypt_restart_csa) { /* The reorg_encrypt_cycle fields are identical (between csa->encr_ptr and cnl), but * the global variable reorg_encrypt_restart_csa indicates one of two possibilities. * a) We are in the middle of a transaction-retry due to cdb_sc_reorg_encrypt status * code and t_retry/tp_restart will take care of doing the reinitialization of * the new key handles. We cannot do the wcs_wtstart until then in case we encounter * a block with the new key. Skip this wcs_wtstart call as if the cycles were * different. * b) We are exiting i.e. "process_exiting" = TRUE. In that case, we are clearly not in * the middle of a transaction that will be committed. And so, we can safely go * ahead and (re)initialize the encryption handles. And proceed with the flush of * the buffers using uptodate encryption keys. */ if (process_exiting) sync_keys = TRUE; else skip_sync = TRUE; } if (skip_sync) { DBG_RECORD_BLOCK_ABORT(csd, csa, cnl, process_id); skip_in_trans = TRUE; } if (sync_keys) { /* Note: Below logic is very similar to "process_reorg_encrypt_restart" but we do * not invoke that function here because it assumes various things (e.g. non-NULL * "reorg_encrypt_restart_csa", no crit on any region etc.) all of which are not * guaranteed in some cases. */ INIT_DB_OR_JNL_ENCRYPTION(csa, csd, seg->fname_len, seg->fname, gtmcrypt_errno); save_errno = gtmcrypt_errno; if (0 == save_errno) COPY_ENC_INFO(csd, csa->encr_ptr, cnl->reorg_encrypt_cycle); reorg_encrypt_restart_csa = NULL; /* Reset this in case it is non-NULL */ } if (!skip_in_trans && (0 == save_errno)) { assert((unsigned char *)bp != reformat_buffer); DBG_ENSURE_PTR_IS_VALID_GLOBUFF(csa, csd, (sm_uc_ptr_t)bp); save_bp = (blk_hdr_ptr_t)GDS_ANY_ENCRYPTGLOBUF(bp, csa); DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(csa, csd, (sm_uc_ptr_t)save_bp); assert((bp->bsiz <= csd->blk_size) && (bp->bsiz >= SIZEOF(*bp))); in_len = MIN(csd->blk_size, bp->bsiz) - SIZEOF(*bp); if (BLK_NEEDS_ENCRYPTION(bp->levl, in_len)) { ASSERT_ENCRYPTION_INITIALIZED; memcpy(save_bp, bp, SIZEOF(blk_hdr)); in = (char *)(bp + 1); out = (char *)(save_bp + 1); if (use_new_key) { GTMCRYPT_ENCRYPT(csa, TRUE, csa->encr_key_handle2, in, in_len, out, bp, SIZEOF(blk_hdr), gtmcrypt_errno); } else { GTMCRYPT_ENCRYPT(csa, csd->non_null_iv, csa->encr_key_handle, in, in_len, out, bp, SIZEOF(blk_hdr), gtmcrypt_errno); } DBG_RECORD_BLOCK_WRITE(csd, csa, cnl, process_id, csr->blk, ((blk_hdr *)bp)->tn, 4, use_new_key, bp, save_bp, bp->bsiz, in_len); save_errno = gtmcrypt_errno; } else { memcpy(save_bp, bp, bp->bsiz); DBG_RECORD_BLOCK_WRITE(csd, csa, cnl, process_id, csr->blk, ((blk_hdr *)bp)->tn, 5, use_new_key, bp, save_bp, bp->bsiz, in_len); } } } else { DBG_RECORD_BLOCK_WRITE(csd, csa, cnl, process_id, csr->blk, ((blk_hdr *)bp)->tn, 6, use_new_key, bp, save_bp, bp->bsiz, 0); } /* If online rollback has forked off child processes to operate on each region, * we have seen ASYNC IOs issued from the child process do not finish for reasons unknown. * So we disable asyncio in the forward phase of offline/online rollback/recover. * This is easily identified currently by the global variable "wcs_noasyncio" being TRUE. */ # ifdef USE_NOAIO do_asyncio = FALSE; # else do_asyncio = csd->asyncio && !wcs_noasyncio; # endif if (udi->fd_opened_with_o_direct) { size = ROUND_UP2(size, DIO_ALIGNSIZE(udi)); assert(size <= csd->blk_size); } if (!skip_in_trans && (0 == save_errno)) { /* Due to csa->in_wtstart protection (at the beginning of this module), we are guaranteed * that the write below won't be interrupted by another nested wcs_wtstart */ #ifdef DEBUG /* Going to do a write below, check the size being written for full blk writes */ if (WBTEST_ENABLED(WBTEST_FULLBLKWRT_DB) && (3 == csr->blk)) { DBGFPF((stdout, "Region : %s Blk num : %ld ",region->rname, csr->blk)); DBGFPF((stdout, "needs_first_write : %d ", csr->needs_first_write)); DBGFPF((stdout, "size written: %ld ", size)); DBGFPF((stdout, "fullblkwrite_len : %ld\n", csa->fullblockwrite_len)); } #endif offset = BLK_ZERO_OFF(csd->start_vbn) + (off_t)csr->blk * csd->blk_size; if (!do_asyncio) { DB_LSEEKWRITE(csa, udi, udi->fn, udi->fd, offset, save_bp, size, save_errno); csr->needs_first_write = FALSE; } else { cr->wip_is_encr_buf = (save_bp != bp); DB_LSEEKWRITEASYNCSTART(csa, udi, udi->fn, udi->fd, offset, save_bp, size, cr, save_errno); if (EAGAIN == save_errno) { /* ASYNCIO IO could not be started due to OS not having enough memory temporarily */ BG_TRACE_PRO_ANY(csa, wcs_wtstart_eagain); if (was_crit) { /* Holding crit. Do synchronous IO as we need this flushed. */ do_asyncio = FALSE; BG_TRACE_PRO_ANY(csa, wcs_wtstart_eagain_incrit); DB_LSEEKWRITE(csa, udi, udi->fn, udi->fd, offset, \ save_bp, size, save_errno); csr->needs_first_write = FALSE; } /* else: We do not hold crit so flushing this is not critical. */ } else if (0 == save_errno) csr->aio_issued = TRUE; } } if ((blk_hdr_ptr_t)reformat_buffer == bp) { DEBUG_ONLY(reformat_buffer_in_use--;) assert((0 == reformat_buffer_in_use) || process_exiting); } /* Trigger I/O error if white box test case is turned on */ GTM_WHITE_BOX_TEST(WBTEST_WCS_WTSTART_IOERR, save_errno, ENOENT); if (skip_in_trans || (0 != save_errno)) { WCS_OPS_TRACE(csa, process_id, wcs_ops_wtstart7, cr->blk, GDS_ANY_ABS2REL(csa,cr), cr->dirty, \ skip_in_trans, save_errno); assert((ERR_ENOSPCQIODEFER != save_errno) || !was_crit || skip_in_trans); csr->epid = 0; /* before releasing update lock, clear epid */ CLEAR_BUFF_UPDATE_LOCK(csr, &cnl->db_latch); REINSERT_CR_AT_TAIL(csr, ahead, n, csa, csd, wcb_wtstart_lckfail4); if (INTERLOCK_FAIL == n) { err_status = ERR_DBCCERR; break; } err_status = save_errno; if (!skip_in_trans) { /* We have an error from the write. Could be disk space or a real error. Handle it. * Note: This write will be automatically retried after csd->flush_time[0] msec, if this * was called through a timer-pop, otherwise, error (return value from this function) * should be handled (including ignored) by the caller. */ wcs_wterror(region, save_errno); } else assert(0 == save_errno); break; } else if (do_asyncio) { n = INSQTI((que_ent_ptr_t)csr, (que_head_ptr_t)whead); if (INTERLOCK_FAIL == n) { assert(FALSE); csr->epid = 0; SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER); BG_TRACE_PRO_ANY(csa, wcb_wtstart_lckfail4); err_status = ERR_DBCCERR; break; } if (cr_list_ptr) /* we've been asked to return a list of crs where we issued i/o's */ { assert(cr_list_ptr->numcrs < cr_list_ptr->listsize); cr_list_ptr->listcrs[cr_list_ptr->numcrs++] = cr; } ADD_ENT_TO_WIP_QUE_CNT(cnl); } cnl->wtstart_errcnt = 0; /* Discard any previously noted I/O errors */ ++n2; BG_TRACE_ANY(csa, wrt_count); /* Detect whether queue has become empty. Defer action (calling wcs_clean_dbsync) * to end of routine, since we still hold the lock on the cache-record */ queue_empty = !SUB_ENT_FROM_ACTIVE_QUE_CNT(cnl); if (!do_asyncio) { csr->flushed_dirty_tn = csr->dirty; csr->epid = 0; ADD_ENT_TO_FREE_QUE_CNT(cnl); csr->dirty = 0; /* Even though asyncio is ON we may have done a synchronous I/O to get it done, e.g., * we were holding crit and got an asyncio error. If that is the case, check for * a twin. */ if (csd->asyncio && csr->twin) BREAK_TWIN(csr, csa); CLEAR_BUFF_UPDATE_LOCK(csr, &cnl->db_latch); /* Note we are still under protection of wbuf_dqd lock at this point. Reason we keep * it so long is so that all the counters are updated along with the queue being correct. * The result of not doing this previously is that wcs_recover was NOT called when we * got interrupted just prior to the counter adjustment leaving wcs_active_lvl out of * sync with the actual count on the queue which caused an assert failure in wcs_flu. SE 11/2000 */ } } else WCS_OPS_TRACE(csa, process_id, wcs_ops_wtstart8, cr->blk, GDS_ANY_ABS2REL(csa,cr), cr->dirty, n, 0); } csa->wbuf_dqd--; writes_completed: #ifdef DEBUG if (0 == n2) BG_TRACE_ANY(csa, wrt_noblks_wrtn); assert((cnl->in_wtstart > 0) && csa->in_wtstart); #endif SLEEP_ON_WBOX_COUNT(5); if (csa->dbsync_timer && n1) { /* If we already have a dbsync timer active AND we found at least one dirty cache record in the active queue * now, this means there has not been enough time period of idleness since the last update and so there is * no purpose to the existing timer. A new one would anyways be started whenever the last dirty cache * record in the current active queue is flushed. Cancel the previous one. */ CANCEL_DBSYNC_TIMER(csa); } CAREFUL_DECR_CNT(cnl->in_wtstart, cnl->wc_var_lock); /* Ideally, we would like another SLEEP_ON_WBOX_COUNT here, but that could cause assert failures in concurrent wcs_wtstarts. * Because it is highly unlikely for an interrupt-deferred process to get killed at exactly this spot, do not test that. */ CLEAR_WTSTART_PID(cnl, index); csa->in_wtstart = FALSE; /* This process can write again */ SLEEP_ON_WBOX_COUNT(6); DECR_INTENT_WTSTART(cnl); SLEEP_ON_WBOX_COUNT(7); if (queue_empty) /* Active queue has become empty. */ wcs_clean_dbsync_timer(csa); /* Start a timer to flush-filehdr (and write epoch if before-imaging) */ ENABLE_INTERRUPTS(INTRPT_IN_WCS_WTSTART, prev_intrpt_state); if (0 != gtmcrypt_errno) { /* Now that we have done all cleanup (reinserted the cache-record that failed the write and cleared cnl->in_wtstart * and cnl->intent_wtstart, go ahead and issue the error. */ GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, rts_error, seg->fname_len, seg->fname); } if (pushed_region) POP_GV_CUR_REGION(sav_cur_region, sav_cs_addrs, sav_cs_data, sav_jnlpool); return err_status; } fis-gtm-V7.0-005/sr_unix/wcs_wtstart_fini.c0000644000032200000250000002176014342376330017604 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2022 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 #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "wcs_sleep.h" #include "filestruct.h" #include "gtmio.h" #include "wcs_wt.h" #include "jnl.h" #include "sleep_cnt.h" #define SMALLNUMCRS 128 /* if a small number is requested use a local rather than the heap */ /* This function waits for "nbuffs" dirty buffers to be moved to the free queue (from the active or wip * queue) since function entry. If the active and wip queue become empty, it returns even if the "nbuffs" * target is not reached. It uses "cnl->wcs_buffs_freed" to help with this determination. Note that * "wcs_buffs_freed" can only be updated by "wcs_wtfini" (which holds crit) so we can expect to see a * monotonically increasing value of this shared counter. */ int wcs_wtstart_fini(gd_region *reg, int nbuffs, cache_rec_ptr_t cr2flush) { int numwritesneeded, numwritesissued; wtstart_cr_list_t cr_list; cache_que_head_ptr_t crq, crwipq; cache_rec_ptr_t *listcrs, smalllistcrs[SMALLNUMCRS], *heaplistcrs; boolean_t was_crit, wtstartalreadycalled = FALSE, proc_check, tried_to_flush = FALSE; gtm_uint64_t targetfreedlevel, basefreedlevel, prewtfinifreedlevel, lcl_buffs_freed; int4 err_status = 0, num_sleeps = 0; sgmnt_addrs *csa; node_local_ptr_t cnl; sgmnt_data_ptr_t csd; jnl_private_control *jpc; cache_rec_ptr_t older_twin, cr2use; #ifdef DEBUG gtm_uint64_t dbg_wcs_buffs_freed, prev_dbg_wcs_buffs_freed = 0; #endif csa = &FILE_INFO(reg)->s_addrs; csd = csa->hdr; cnl = csa->nl; crwipq = &csa->acc_meth.bg.cache_state->cacheq_wip; crq = &csa->acc_meth.bg.cache_state->cacheq_active; heaplistcrs = NULL; if (0 >= nbuffs) /* if number not specified, use default (same as "wcs_wtstart") */ nbuffs = csd->n_wrt_per_flu; basefreedlevel = cnl->wcs_buffs_freed; /* If we are asked to flush a specific cr AND it is the younger twin, check if the older twin needs * to have its io initiated and reaped first. */ older_twin = (cache_rec_ptr_t) NULL; assert((NULL == cr2flush) || csa->now_crit); assert(csd->asyncio); if (cr2flush && cr2flush->twin && cr2flush->bt_index) { older_twin = (cache_rec_ptr_t) GDS_ANY_REL2ABS(csa, cr2flush->twin); if (older_twin->dirty) nbuffs++; /* We need to flush the older sibling too. */ } targetfreedlevel = basefreedlevel + nbuffs; cr_list.listsize = nbuffs; if (SMALLNUMCRS < nbuffs) { heaplistcrs = (cache_rec_ptr_t *)malloc(SIZEOF(cache_rec_ptr_t) * nbuffs); cr_list.listcrs = heaplistcrs; } else /* if small number of buffers use the local */ cr_list.listcrs = smalllistcrs; while (((lcl_buffs_freed = cnl->wcs_buffs_freed) < targetfreedlevel) && (crq->fl || crwipq->fl)) { /* while we have not reached our free target and there are still things to free */ # ifdef DEBUG dbg_wcs_buffs_freed = cnl->wcs_buffs_freed; assert(dbg_wcs_buffs_freed >= prev_dbg_wcs_buffs_freed); prev_dbg_wcs_buffs_freed = dbg_wcs_buffs_freed; # endif numwritesissued = 0; numwritesneeded = targetfreedlevel - lcl_buffs_freed; assert(numwritesneeded <= nbuffs); while (0 < numwritesneeded) { /* issue the requested number of writes unless you run out of dirty buffers that don't have outstanding writes */ # ifdef DEBUG dbg_wcs_buffs_freed = cnl->wcs_buffs_freed; assert(dbg_wcs_buffs_freed >= prev_dbg_wcs_buffs_freed); prev_dbg_wcs_buffs_freed = dbg_wcs_buffs_freed; # endif assert(numwritesneeded <= nbuffs); /* wcs_wtstart() will initiate up to numwritesneeded writes (limited by the number of dirty buffers * available and buffers being blocked from having their writes initiated) */ /* If we are asked to flush a specific cr AND it is the younger twin, check if the older twin needs * to have its io initiated (via wcs_wtstart()) (possible if its write errored out and it transitioned * from the wip queue back to the active queue). If it does call wcs_wtstart with the older twin as it * must be flushed first before we can attempt to flush the newer twin. */ if (older_twin) { if (older_twin->dirty && !older_twin->epid) { wtstartalreadycalled = TRUE; /* this variable indicates if we ever called wcs_wtstart */ tried_to_flush = TRUE; /* this variable indicates if we called it in this iteration */ err_status = wcs_wtstart(reg, numwritesneeded, &cr_list, older_twin); } else { tried_to_flush = FALSE; /* remember that we did not try to initiate any ios */ cr_list.numcrs = 0; /* since wcs_wtstart was not called indicate 0 ios were initiated. */ assert(!err_status); /* we should only get here if err_status is 0 */ err_status = 0; /* for PRO ensure it is set to 0 */ } } else { wtstartalreadycalled = TRUE; tried_to_flush = TRUE; err_status = wcs_wtstart(reg, numwritesneeded, &cr_list, cr2flush); } if (err_status || cnl->wc_blocked) /* if we are blocked get out to allow cache recovery */ break; if (0 != cr_list.numcrs) { /* If we were able to initiate some writes wait for their statuses to complete */ wcs_wtfini_nocrit(reg, &cr_list); numwritesissued += cr_list.numcrs; num_sleeps = 0; /* making progress so reset the clock */ if (numwritesissued >= numwritesneeded) break; /* enough i/o statuses are back so lets reap them */ } else /* not able to initiate any i/os: * maybe out of i/o slots, buffer(s) may be stuck (jnl write, twin, bt_put) * or we are out of dirty buffers to initiate writes on but there are crs * in the WIP queue or we have previously issued the io for an older twin * and now need to wait for the io to complete (or tried_to_flush=FALSE case). */ break; /* we waited in wcs_wtfini_nocrit() above so check cnl->wcs_buffs_freed to see if any other processes * helped increase the number of freed buffers. */ numwritesneeded = targetfreedlevel - cnl->wcs_buffs_freed; } /* if we got an error from wcs_wtstart() or wcs_wtfini() or we were asked to flush a specific buffer * and it is no longer dirty or cache needs to be verified we are done. */ if (err_status || (cr2flush && !cr2flush->dirty) || cnl->wc_blocked) break; /* have i/o statuses on i/os we issued so call wcs_wtfini() to reap them all at once; * other's i/os may also be reaped */ prewtfinifreedlevel = cnl->wcs_buffs_freed; /* get a base line to see if wtfini is making progress */ was_crit = csa->now_crit; if (!was_crit) grab_crit(reg, WS_27); /* Reap our i/os (and maybe some others). * If we tried to flush, were unable to, and there is something on the wip queue: a process with an outstanding * i/o may have died and is blocking our ability to initiate i/os; ask wcs_wtfini() to see if all of the processes * that have outstanding writes are alive. */ proc_check = tried_to_flush && (0 == cr_list.numcrs) && crwipq->fl ? CHECK_IS_PROC_ALIVE_TRUE : CHECK_IS_PROC_ALIVE_FALSE; /* If we are waiting on the completion of the io of an older twin use its cr on the call to wcs_wtfini */ cr2use = (older_twin && older_twin->dirty & older_twin->epid) ? older_twin : cr2flush; err_status = wcs_wtfini(reg, proc_check, cr2use); if (older_twin && !older_twin->dirty) /* the older twin has been flushed so we are done with it. */ older_twin = (cache_rec_ptr_t) NULL; if (!was_crit) rel_crit(reg); if (prewtfinifreedlevel == cnl->wcs_buffs_freed) { wcs_sleep(1); /* wtfini did not make any progress so wait a while. * Note: The sleep time of 1 msec here is factored into the calculation * of MAX_WTSTART_FINI_SLEEPS (it has a MAXSLPTIME multiplicative factor). * Any changes to the 1 parameter need corresponding changes in the macro. */ num_sleeps++; } else num_sleeps = 0; /* we are making progress so reset the clock */ if (MAX_WTSTART_FINI_SLEEPS < num_sleeps) /* waited long enough; initiate cache recovery */ { SET_TRACEABLE_VAR(cnl->wc_blocked, WC_BLOCK_RECOVER); break; } if (err_status) break; } if (!wtstartalreadycalled) { jpc = csa->jnl; assert(!JNL_ALLOWED(csd) || ( NULL != jpc)); /* if journaling is allowed, we better have non-null csa->jnl */ /* wcs_wtstart(), which would call jnl_qio_start(), has not been called. Call jnl_qio_start() in case there are * journal buffers that need to be flushed. */ if (JNL_ENABLED(csd) && (NULL != jpc) && (NOJNL != jpc->channel)) jnl_qio_start(jpc); } if (NULL != heaplistcrs) free(heaplistcrs); return err_status; } fis-gtm-V7.0-005/sr_unix/xtrgtmtypes.awk0000644000032200000250000000677314342376330017174 0ustar librarygtc################################################################# # # # Copyright (c) 2010-2018 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. # # # ################################################################# # # Part of gengtmdeftypes. # # Each GT.M header file, after de-commenting, is run through this extractor to generate a list of GT.M # defined types we want to make sure we define later. This is done initially because once processing # begins on the combined pre-processed header files, we cannot tell the difference between system # supplied structures and GT.M supplied structures. This gives us the latter list which we want to # concentrate on in later processing. # BEGIN \ { bracelevel = 0; bracketlevel = 0; parenlevel = 0; prevfield = ""; intypedef = 0; } # # Main # { gsub("\\[.*\\]", " "); # Eliminate matched [*] stuff gsub("\".*\"", " "); # Eliminate double-quoted strings gsub("'.*'", " "); # Eliminate single-quoted strings gsub("\\[", " & "); # Remaining [ gets space around it (separate due to issues on AIX) gsub("\\]", " & "); # Remaining ] gets space around it gsub("[#;:,{}=*-//)(]", " & "); # Other special chars get spaces around them so can be recognized gsub("unsigned int", "unsigned-int"); # Turn types into single-token types gsub("unsigned long", "unsigned-long"); gsub("unsigned short", "unsigned-short"); gsub("unsigned char", "unsigned-short"); gsub("unsigned int", "unsigned-int"); gsub("unsigned int", "unsigned-int"); gsub("short unsigned", "unsigned-short"); gsub("signed int", "int"); gsub("signed char", "char"); if ("typedef" == $1 || intypedef) { # Either have a new typedef or we are already in one - ignore anything else if ("typedef" == $1) { if (intypedef) { printf("Error: nested typedef - not supported at line %d\n", NR); exit(1); } intypedef = 1; tokenssincetypedef = -1; # Since we increment it for the typedef as well typedeftype1 = ""; typedeftype2 = ""; parenssincetypedef = 0; isfnptr=0; } for (i=1; NF >= i; i++) { tokenssincetypedef++; # allow us to track topside if ("{" == $i) bracelevel++; else if ("}" == $i) bracelevel--; else if ("[" == $i) bracketlevel++; else if ("]" == $i) bracketlevel--; else if ("(" == $i) { parenlevel++; if (2 == tokenssincetypedef && 0 == parenssincetypedef && "*" == $(i+1)) { # Probable function pointer definition i++; # Get rid of "(" i++; # Get rid of "*" if ("volatile" == $i) i++; # ignore volatile prevfield = $i; prevfldnum = i; isfnptr = 1 typedeftype1 = "*fnptr" } parenssincetypedef++; } else if (")" == $i) parenlevel--; else if (";" == $i || "," == $i) { if (0 == bracelevel && 0 == bracketlevel && 0 == parenlevel) { if (prevfldnum == (i - 1) || isfnptr) # make sure aren't picking up garbage from earlier in line print prevfield,typedeftype1,typedeftype2; intypedef = 0; isfnptr = 0; } } else if (1 == tokenssincetypedef) typedeftype1 = $i; else if (2 == tokenssincetypedef) typedeftype2 = $i; if (0 == bracketlevel && 0 == parenlevel && "]" != $i && ")" != $i && "(" != $i) { prevfield = $i; prevfldnum = i; } } } } fis-gtm-V7.0-005/sr_unix/ygblstat.mpt0000644000032200000250000002624714342376330016427 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2017-2020 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %YGBLSTAT ; Gather and report statistics shared by GT.M processes ; Note: Application code is permitted to KILL %YGS (the entire ; tree), or to hide it with NEW. But application code must ; not modify nodes within it. ; ; As all strings contain only ASCII, code uses "Z" functions to ; ensure performance even in UTF-8 mode. As this is a utility ; program that is part of GT.M, it assumes it is compiled with ; short-circuit expression evaluation ($gtm_side_effects & ; $gtm_full_boolean set to 0 or not defined). help ; Usage: mumps -run %YGBLSTAT [--help] [--pid pidlist] [--reg reglist] [--stat statlist] ; Program to report statistics shared by processes that have ; opted in to share global database access statistics. All ; command line flags are optional and can appear in any order ; except --help. ; --help - Display usage information and exit (rest of ; command line is ignored). ; --pid pid - pid whose statistics are to be reported. ; "*" or default is all pids. ; --reg reglist - region whose statistics are to be reported/ ; "*" or default is all regions in $gtmgbldir. ; --stat statlist - comma separated list of statistics ; (subset) of ZSHOW "G" to print. Default is "*" ; for all statistics. Statistics are always ; output in the same order as ZSHOW "G" ; If invoked with DO %YGBLSTAT, prompts for inputs. Other entryrefs are: ; $$ORDERPID(pid,gld,reg) - Returns the next pid as specified by gld and reg, ; first pid if pid="", gld defaults to current global ; directory, reg defaults to next pid in any region ; $$SHOW(accum,stat) - Returns the statistic(s) specified by stat (defaults ; to "*"for all statistics) in string / vector of ; accumulated statistics in accum. ; $$STAT(pid,stat,gld,reg) - Returns the statistic(s) specified by stat (defaults ; to all statistics), for process pid (defaults to all ; processes), for region (defaults to all regions) in ; global directory gld (defaults to current global direcory). ; Note: this program uses external calls, which must be installed. version ;0;If the format of %YGS changes, increment the version between the semicolons to ensure %YGS gets reconstructed on a relink ; Main Program if $stack do PROMPTGO quit ; if invoked from another program, prompt for inputs, do work, and exit use $principal:(ctrap=$char(3):nocenable:exception="zgoto 0") ; terminate on Ctrl-C if invoked from shell set $etrap="set $etrap=""use $principal write $zstatus,! set $etrap=""""zshow """"""*"""""" zgoto 0"""" zhalt 254""" set $etrap=$etrap_" set tmp1=$piece($ecode,"","",2),tmp2=$text(@tmp1)" set $etrap=$etrap_" if $length(tmp2) write $text(+0),@$piece(tmp2,"";"",2),!" set $etrap=$etrap_" set $etrap=""zgoto 0"" zhalt +$extract(tmp1,2,$length(tmp1))" new cmdline,pid,reg set cmdline=$zcmdline set (pid,reg,stat)="*" ; Default is all processes, all regions, all statistics set reg="*" for quit:'$$trimleadingstr^%XCMD(.cmdline,"--") do ; process options . if $$trimleadingstr^%XCMD(.cmdline,"help") do MSGANDHALT("help") . else if $$trimleadingstr^%XCMD(.cmdline,"pid") set pid=$$trimleadingdelimstr^%XCMD(.cmdline) . else if $$trimleadingstr^%XCMD(.cmdline,"reg") set reg=$$trimleadingdelimstr^%XCMD(.cmdline) . else if $$trimleadingstr^%XCMD(.cmdline,"stat") set stat=$$trimleadingdelimstr^%XCMD(.cmdline) . else set $ecode=",U249," write $$STAT(pid,stat,,reg),! quit INITYGS ; Initialize statistics names and locations in %YGS new i,stat,zsh zshow "g":zsh set %YGS("*")="",zsh=$zpiece(zsh("G",0),"*",3) ; initialize & strip out global directory and region name for i=2:1:$zlength(zsh,",") do . set stat=$zpiece($zpiece(zsh,",",i),":",1) . set %YGS(i-1)=stat,%YGS(stat)=(i-2)*8+1 . set %YGS("*")=%YGS("*")_stat_"," set %YGS(0)=i-1,%YGS=((i-1)*8)_"|"_$piece($text(version),";",2) set $zpiece(%YGS(-1),$zchar(0),1+%YGS)="",%YGS(-2)=%YGS-1 set %YGS("*")=$zextract(%YGS("*"),1,$zlength(%YGS("*"))-1) quit MSGANDHALT(label) new j,tmp set $etrap="zgoto 0" for j=0:1 set tmp=$piece($text(@label+j),"; ",2) zhalt:""=tmp 2 write tmp,! ORDERPID(pid,gld,reg) ; Get the next pid as specified by gld and reg, first pid if pid="" ; Verify that the pid actually exists new i,nextpid,nextreg,tmp,$zgbldir set pid=$get(pid),reg=$zconvert($get(reg),"U") set:$data(gld) $zgbldir=gld ; return next pid in region if only one region specified, otherwise get lowest across regions ; if not running on simulated pids, verify that process exists if $zlength(reg)&("*"'=reg) set nextpid=pid do quit nextpid . for do quit:""=nextpid!$zlength($zsearch("/proc/"_nextpid))!$zlength($zsearch("/proc/"_nextpid)) . . set nextpid=$order(^%YGS(reg,nextpid)) set nextreg="",tmp=999999999999999999 ; start tmp at largest GT.M integer for set nextreg=$order(^%YGS(nextreg)) quit:""=nextreg set nextpid=pid do set:$length(nextpid)&(nextpid$piece($get(%YGS),"|",2) INITYGS set stat=$get(stat) if ""'=$get(wkstat) do . if %YGS<$zlength(wkstat) set wkstat=$zextract(wkstat,$zlength(wkstat)-%YGS(-2),$zlength(wkstat)) . else set:%YGS>$zlength(wkstat) wkstat=$zextract(%YGS(-1),1,%YGS-$zlength(wkstat))_wkstat ; protect against user error . if 1=$zlength(stat,","),""'=stat,"*"'=stat do ; only one statistic . . if $data(%YGS(stat)) set tmp=%YGS(stat),r=$&gblstat.toulong(.ret,$zextract(wkstat,tmp,tmp+7)) . . else set ret="" . else do . . set:'$zlength(stat)!("*"=stat) stat=%YGS("*") ; default is all statistics . . set ret="",tmp=%YGS("*") for i=1:1:%YGS(0) do . . . set nextstat=$zpiece(tmp,",",i) . . . ; Test must consider stats that are proper substrings like DEX/DEXA, WS1/WS15 etc so add terminal comma on both sides . . . do:(stat_",")[(nextstat_",") . . . . set offset=%YGS(nextstat) . . . . set r=$&gblstat.toulong(.num,$zextract(wkstat,offset,offset+7)) . . . . set ret=ret_nextstat_":"_num_"," . . set ret=$zextract(ret,1,$zlength(ret)-1) else set ret="" quit ret STAT(pid,stat,gld,reg) ; Report requested statistics ; If single pid specified, verify that it exists new i,nextpid,nextreg,nextstat,num,offset,r,ret,statszm1,tlen,tmp,wkstat if $data(gld) new $zgbldir set $zgbldir=gld set pid=$get(pid),reg=$zconvert($get(reg),"U"),stat=$get(stat),tlen=0 do:'$data(%YGS) INITYGS set statszm1=%YGS(-2) ; precalculate values to save recalculation ; Gather statistics - as a process may terminate after a $order() to get a pid ; and before its statistics are accessed, the access must be wrapped with $get() ; and the accessed data must tested for existence with $zlength(). ; Also test for existence of process relies on existence of /proc/ - $zsigproc() ; cannot be used because it requires the target process to have the same userid as the ; process executing the function. $zsearch() must be called twice to ensure that it works ; despite the context. if $zlength(pid)&(pid=+pid) set nextpid=pid do ; one process . if $zlength($zsearch("/proc/"_nextpid))!$zlength($zsearch("/proc/"_nextpid)) do ;process exists . . if $zlength(reg)&("*"'=reg) do ; one process, one region . . . set tmp=$get(^%YGS(reg,pid)),tlen=$zlength(tmp) . . . set wkstat=$select(tlen:$zextract(tmp,tlen-statszm1,tlen),1:"") . . else do ; one process, multiple regions . . . set nextreg="",wkstat=%YGS(-1) for set nextreg=$order(^%YGS(nextreg)) quit:""=nextreg do . . . . set nextstat=$get(^%YGS(nextreg,pid)),tmp=$zlength(nextstat),tlen=tlen+tmp . . . . set:tmp r=$&gblstat.accumulate(.wkstat,$zextract(nextstat,tmp-statszm1,tmp)) . else set wkstat="" ; process does not exist else set wkstat=%YGS(-1) if $zlength(reg)&("*"'=reg) do STATREG(reg) ; all processes one region ; all processes, all regions else set nextreg="" for set nextreg=$order(^%YGS(nextreg)) quit:""=nextreg do STATREG(nextreg) quit $select(tlen:$$SHOW(wkstat,stat),1:"") STATREG(reg) ; Accumulate statistics for all processes in a region ; Refer to comments above on need to use $get() wrapper & test $zlength() new nextpid,tmp set nextpid="" for set nextpid=$order(^%YGS(reg,nextpid)) quit:""=nextpid do . set nextstat=$get(^%YGS(reg,nextpid)),tlen=$zlength(nextstat) . set:tlen r=$&gblstat.accumulate(.wkstat,$zextract(nextstat,tlen-statszm1,tlen)) quit IN(pid,gdir,region) ; report on whether a process can be found in a region quit:'$get(pid) "" new gld,reg set gld=$get(gdir),reg=$zconvert($get(region),"U") if ""'=gld new $zgbldir set $zgbldir=gld set:""=reg reg="*" if "*"=reg do quit ""'=reg ; any region . for set reg=$order(^%YGS(reg)) quit:(""=reg) quit:$data(^%YGS(reg,pid)) quit $select('$data(^%YGS(reg)):"",1:$data(^%YGS(reg,pid))) ; Error message texts - termination by Ctrl-C must be last message U249 ;"-F-ILLEGALCMDLINE Illegal command line starting with --"_cmdline fis-gtm-V7.0-005/sr_unix/zbreaksp.h0000755000032200000250000000243114342376330016036 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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. * * * ****************************************************************/ typedef unsigned short zb_code; #define ZB_CODE_MASK 0xffff #define INST_TYPE zb_code /* The ZBreak command operates by finding the generated code for the op_linestart or op_linefetch for the source * line in question and changing the offset in the transfer table load address instruction from the op_linestart or * op_linefetch offset to the appropriate zbreak functionality opcode offset. * In some platforms(IA64 and ZOS) since the INSTRUCTION LAYOUT is complex we need following * macros for instruction manipulation. * EXTRACT_OFFSET_TO_M_OPCODE * FIX_OFFSET_WITH_ZBREAK_OFFSET * EXTRACT_AND_UPDATE_INST * These macros are called only when COMPLEX_INSTRUCTION_UPDATE is defined * If COMPLEX_INSTRUCTION_UPDATE is not defined portable code in the caller of these macros * is invoked. */ #undef COMPLEX_INSTRUCTION_UPDATE fis-gtm-V7.0-005/sr_unix/zcall_package.c0000755000032200000250000000106714342376330016774 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /*** STUB FILE ***/ #include "mdef.h" #include "zcall_package.h" void zcall_init(void) { } void zcall_halt(void) { } fis-gtm-V7.0-005/sr_unix/zcall_package.h0000755000032200000250000000117614342376330017002 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef ZCALL_PACKAGE_H_INCLUDED #define ZCALL_PACKAGE_H_INCLUDED void zcall_halt(void); void zcall_init(void); #endif fis-gtm-V7.0-005/sr_unix/zhist.c0000644000032200000250000001454714342376335015366 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2015 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_string.h" #include "min_max.h" #include #include "gtmlink.h" #include "error.h" #include "gtmio.h" #ifdef AUTORELINK_SUPPORTED /* This entire file */ GBLREF int object_file_des; /* Routine called from auto_relink_check() and op_rhdadd() to determine if the routine being called needs to be relinked. * To return TRUE, the following conditions must be met: * 1. Auto-relinking must be enabled in the directory the routine was located in (which may be different from the * directory of the current routine by the same name). * 2. This must be a newer version of the routine than what is currently linked. * 3. If the routine is on the stack, recursive relinks must be enabled. * * Parameters: * - zhist - address of zro_hist block of the routine to check if needs relinking * * Output: * - TRUE if new object code is (or may be) available - indicates op_rhd_ext() should relink * FALSE if currently linked code is up-to-date */ boolean_t need_relink(rhdtyp *rtnhdr, zro_hist *zhist) { zro_validation_entry *iter; uint4 cur_cycle, rtnnmlen; boolean_t norecurslink; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(NULL != zhist); /* Shouldn't be called unless relinking is possible */ /* If $ZROUTINES cycle has changed since validation list was created, signal relink but the relink only * actually happens if the file op_zlink() locates has a different object hashcode than the currently * linked routine - else it just rebuilds the history and calls it good. */ if (zhist->zroutines_cycle != TREF(set_zroutines_cycle)) { /* Check if we can return TRUE for this routine */ if ((LINK_NORECURSIVE == TREF(relink_allowed)) && on_stack(rtnhdr, NULL)) return FALSE; /* can't relink, or else we'll get LOADRUNNING */ return TRUE; } /* Traverse list corresponding to zro entries */ norecurslink = (LINK_NORECURSIVE == TREF(relink_allowed)); for (iter = &zhist->base[0]; iter != zhist->end; iter++) { DEBUG_ONLY(rtnnmlen = mid_len(&iter->relinkrec->rtnname_fixed)); assert((rtnnmlen == rtnhdr->routine_name.len) /* Verify have the right entry - compare names */ && (0 == memcmp(&iter->relinkrec->rtnname_fixed, rtnhdr->routine_name.addr, rtnnmlen))); cur_cycle = iter->relinkrec->cycle; if (cur_cycle != iter->cycle) { /* Check if we can return TRUE for this routine */ if (norecurslink && on_stack(rtnhdr, NULL)) return FALSE; /* can't relink, or else we'll get LOADRUNNING */ return TRUE; } } return FALSE; } /* Routine called from zro_search_hist() to copy a given search history block from stack memory to malloc'd memory * so it can be attached to the routine header of the linked routine. * * Parameters: * zhist_valent - Last search history entry + 1 * zhist_valent_base - First search history entry * * Return value: * Malloc'd block of search history header (zro_hist *) followed by the array of search history entries for * the given routine. */ zro_hist *zro_zhist_saverecent(zro_search_hist_ent *zhist_valent_end, zro_search_hist_ent *zhist_valent_base) { int hist_len; relinkrec_t *rec; zro_hist *lcl_recent_zhist; zro_validation_entry *zhent; zro_search_hist_ent *zhist_valent; mstr rtnname; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Malloc and return zhist copy */ assert(NULL != zhist_valent_end); assert(NULL != zhist_valent_base); hist_len = zhist_valent_end - zhist_valent_base; lcl_recent_zhist = (zro_hist *)malloc(SIZEOF(zro_hist) + (SIZEOF(zro_validation_entry) * hist_len)); lcl_recent_zhist->zroutines_cycle = TREF(set_zroutines_cycle); lcl_recent_zhist->end = &lcl_recent_zhist->base[0] + hist_len; assert(NULL == TREF(save_zhist)); TREF(save_zhist) = lcl_recent_zhist; ESTABLISH_RET(zro_ins_rec_fail_ch, NULL); for (zhist_valent = zhist_valent_base, zhent = &lcl_recent_zhist->base[0]; 0 < hist_len; zhist_valent++, zhent++, hist_len--) { rtnname.addr = zhist_valent->rtnname.c; rtnname.len = zhist_valent->rtnname_len; assert(NULL != zhist_valent->zro_valent.relinkctl_bkptr); rec = relinkctl_insert_record(zhist_valent->zro_valent.relinkctl_bkptr, &rtnname); assert(NULL != rec); zhist_valent->zro_valent.relinkrec = rec; zhist_valent->zro_valent.cycle = rec->cycle; memcpy((char *)zhent, (char *)&zhist_valent->zro_valent, SIZEOF(zro_validation_entry)); } REVERT; TREF(save_zhist) = NULL; return lcl_recent_zhist; } /* Condition handler called when relinkctl_insert_record() fails. We specifically need to (1) release the zro_hist structure * we allocated and (2) close the object file before driving the next condition handler. */ CONDITION_HANDLER(zro_ins_rec_fail_ch) { int rc; START_CH(TRUE); if (NULL != TREF(save_zhist)) { free(TREF(save_zhist)); TREF(save_zhist) = NULL; } CLOSE_OBJECT_FILE(object_file_des, rc); /* Close object file ignoring error (processing primary error) */ NEXTCH; } /* Routine called from zro_search_hist() to add a $ZROUTINES entry to the (local) search history for a given object file. * * Parameters: * zhist_valent - $ZROUTINES search history entry to be filled in. * obj_container - $ZROUTINES entry for a given object directory. * rtnname - mstr addr containing name of the routine. * */ void zro_record_zhist(zro_search_hist_ent *zhist_valent, zro_ent *obj_container, mstr *rtnname) { open_relinkctl_sgm *linkctl; int len; assert(NULL != obj_container); linkctl = obj_container->relinkctl_sgmaddr; assert(NULL != linkctl); zhist_valent->zro_valent.relinkctl_bkptr = linkctl; assert(NULL != zhist_valent); assert(rtnname->len < SIZEOF(mident_fixed)); memcpy(zhist_valent->rtnname.c, rtnname->addr, rtnname->len); zhist_valent->rtnname_len = rtnname->len; if (SIZEOF(zhist_valent->rtnname) > rtnname->len) memset((zhist_valent->rtnname.c + rtnname->len), '\0', SIZEOF(zhist_valent->rtnname) - rtnname->len); } #endif /* AUTORELINK_SUPPORTED */ fis-gtm-V7.0-005/sr_unix/zl_cmd_qlf.c0000644000032200000250000002033014342376330016315 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "cmd_qlf.h" #include "cli.h" #include "cli_parse.h" #include "parse_file.h" #include "min_max.h" #include "toktyp.h" /* Needed for "valid_mname.h" */ #include "valid_mname.h" #include "gtmmsg.h" #define SET_OBJ(NAME, LEN) \ MBSTART { \ memcpy(cmd_qlf.object_file.str.addr, NAME, LEN); \ cmd_qlf.object_file.str.addr[LEN] = 0; \ cmd_qlf.object_file.str.len = LEN; \ cmd_qlf.object_file.mvtype = MV_STR; \ } MBEND #define COMMAND "MUMPS " GBLREF char cli_err_str[]; GBLREF CLI_ENTRY *cmd_ary, mumps_cmd_ary[]; GBLREF command_qualifier cmd_qlf, glb_cmd_qlf; GBLREF mident routine_name, module_name, int_module_name; GBLREF unsigned char object_file_name[], source_file_name[]; GBLREF unsigned short object_name_len, source_name_len; STATICDEF uint4 save_qlf; error_def(ERR_COMPILEQUALS); error_def(ERR_FILEPARSE); error_def(ERR_NORTN); error_def(ERR_NOTMNAME); error_def(ERR_ZLNOOBJECT); void zl_cmd_qlf(mstr *quals, command_qualifier *qualif, char *srcstr, unsigned short *srclen, boolean_t last) { char cbuf[MAX_LINE], inputf[MAX_FN_LEN + 1]; CLI_ENTRY *save_cmd_ary; int ci, parse_ret, status; mident file; mstr fstr; parse_blk pblk; short object_name_mvtype; unsigned short clen = 0; int iclen; /* 4SCA on clen underflows */ DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(0 <= quals->len); if ((0 > quals->len) || ((MAX_LINE - SIZEOF(COMMAND)) < quals->len)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_COMPILEQUALS, 2, (0 < quals->len) ? MAX_LINE : 0, quals->addr); MEMCPY_LIT(cbuf, COMMAND); memcpy(cbuf + SIZEOF(COMMAND) - 1, quals->addr, quals->len); cbuf[SIZEOF(COMMAND) - 1 + quals->len] = 0; /* The caller of this function could have their own command parsing tables Nevertheless, we need to parse the string as if * it was a MUMPS compilation command. So we switch temporarily to the MUMPS parsing table "mumps_cmd_ary". Note that the * only rts_errors possible between save and restore of the cmd_ary are in compile_source_file and those are internally * handled by source_ch which will transfer control back to us (right after the the call to compile_source_file below) * and hence proper restoring of cmd_ary is guaranteed even in case of errors. */ save_cmd_ary = cmd_ary; cmd_ary = &mumps_cmd_ary[0]; cli_str_setup((uint4)(SIZEOF(COMMAND) + quals->len), cbuf); parse_ret = parse_cmd(); if (parse_ret) { if (last & save_qlf) glb_cmd_qlf.qlf = save_qlf; rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) parse_ret, 2, LEN_AND_STR(cli_err_str)); } object_name_mvtype = qualif->object_file.mvtype; get_cmd_qlf(qualif); qualif->object_file.mvtype |= object_name_mvtype; cmd_ary = save_cmd_ary; /* restore cmd_ary */ if (last && (!module_name.len || !*srclen)) { /* if we're calling this twice the first time would be without a real "INFILE" */ if (!*srclen) { *srclen = MAX_FN_LEN; status = cli_get_str("INFILE", srcstr, srclen); assert(status); } assert(*srclen); memset(&pblk, 0, SIZEOF(pblk)); pblk.buff_size = MAX_FN_LEN; pblk.buffer = inputf; pblk.fop = F_SYNTAXO; fstr.addr = srcstr; fstr.len = *srclen; status = parse_file(&fstr, &pblk); if (!(status & 1) || !pblk.b_name) { assert(!TREF(trigger_compile_and_link)); if (save_qlf) glb_cmd_qlf.qlf = save_qlf; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_FILEPARSE, 2, *srclen, srcstr, !(status & 1) ? status : ERR_NORTN); } file.addr = pblk.l_name; file.len = pblk.b_name; if ((0 == pblk.b_ext) && (MAX_FN_LEN >= (*srclen + SIZEOF(DOTM)))) { assert(NULL != pblk.l_name); assert((MAX_FN_LEN >= pblk.b_name) && (MAX_FN_LEN >= pblk.buff_size)); assert((pblk.buffer < pblk.l_name) && ((pblk.buffer + pblk.buff_size) > pblk.l_name)); /* pblk.buff_size is MAX_FN_LEN, but pblk.buffer is allocated with an extra byte for the trailing null */ assert((pblk.buffer + pblk.buff_size + 1) >= (pblk.l_name + pblk.b_name + SIZEOF(DOTM))); memcpy(&pblk.l_name[pblk.b_name], DOTM, SIZEOF(DOTM)); pblk.b_ext = (SIZEOF(DOTM) - 1); } source_name_len = pblk.b_dir + pblk.b_name + pblk.b_ext; source_name_len = MIN(source_name_len, MAX_FN_LEN); memcpy(source_file_name, pblk.buffer, source_name_len); source_file_name[source_name_len] = 0; memcpy(srcstr, source_file_name, source_name_len); srcstr[source_name_len] = 0; *srclen = source_name_len; if (!module_name.len && !(CQ_NAMEOFRTN & cmd_qlf.qlf) && !(CLI_PRESENT == cli_present("OBJECT"))) { clen = routine_name.len = MIN(file.len, MAX_MIDENT_LEN); memcpy(routine_name.addr, file.addr, clen); object_name_len = clen; memcpy(object_file_name, pblk.l_name, object_name_len); SET_OBJ(object_file_name, object_name_len); } } assert(!last || *srclen); /* routine_name is the internal name of the routine (with any leading '%' translated to '_') which by default is the * unpathed name of the source file. An -OBJECT qualif (without a NAMEOFOFRTN) makes routine_name from the name of the * object (which trigger compilation uses). A NAMEOFOFRTN qualifier (which trigger compilation uses) overrides the others. * int_module_name is the external symbol (without the % translation) that gets exposed (in the GTM context) */ if (CLI_PRESENT == cli_present("OBJECT")) { assert((cmd_qlf.object_file.str.len) && (MAX_FN_LEN >= cmd_qlf.object_file.str.len) && (cmd_qlf.object_file.str.addr)); object_name_len = MAX_FN_LEN; cli_get_str("OBJECT", (char *)object_file_name, &object_name_len); cmd_qlf.object_file.mvtype = MV_STR; assert((0 < object_name_len) && (MAX_FN_LEN >= object_name_len)); if (!(CQ_NAMEOFRTN & cmd_qlf.qlf)) { for (ci = object_name_len - 1; (0 < ci) && ('/' != object_file_name[ci]); ci--) ; /* scan back from end for rtn name & triggerness */ ci += (0 < ci) ? 1 : 0; assert(object_name_len >= ci); iclen = object_name_len - ci; assert((iclen >= 0) && (iclen <= object_name_len)); if ((2 <= iclen) && ('o' == object_file_name[ci + iclen - 1]) && ('.' == object_file_name[ci + iclen - 2])) iclen -= 2; /* Strip trailing ".o" */ SET_OBJ(object_file_name, object_name_len); assert(0 <= iclen); clen = object_name_len = (unsigned short) MIN(iclen, MAX_MIDENT_LEN); memcpy(routine_name.addr, &object_file_name[ci], clen); } } assert(!last || *srclen); if (CQ_NAMEOFRTN & cmd_qlf.qlf) { /* Routine name specified - name wins over object */ clen = MAX_MIDENT_LEN; cli_get_str("NAMEOFRTN", routine_name.addr, &clen); assert((0 < clen) && (MAX_MIDENT_LEN >= clen)); cmd_qlf.qlf &= ~CQ_NAMEOFRTN; /* Can only be used for first module in list */ if (CLI_PRESENT != cli_present("OBJECT")) { memcpy(object_file_name, routine_name.addr, clen); SET_OBJ(object_file_name, clen); } } assert(!last || *srclen); if (clen) { memcpy(module_name.addr, routine_name.addr, clen); if ('_' == *routine_name.addr) routine_name.addr[0] = '%'; routine_name.len = clen; if (last && !(pblk.fnb & F_WILD) && !TREF(trigger_compile_and_link) && !valid_mname(&routine_name)) { if (save_qlf) glb_cmd_qlf.qlf = save_qlf; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_NOTMNAME, 2, RTS_ERROR_MSTR(&routine_name), ERR_ZLNOOBJECT); } module_name.len = int_module_name.len = clen; memcpy(int_module_name.addr, routine_name.addr, clen); } else assert(!last || int_module_name.len); assert(!last || *srclen); if (!last) { save_qlf = glb_cmd_qlf.qlf; glb_cmd_qlf.qlf = cmd_qlf.qlf; # ifdef DEBUG if (!cmd_qlf.list_file.str.len && (CLI_PRESENT == cli_present("LIST"))) cmd_qlf.list_file.str.len = MAX_FN_LEN; if (!cmd_qlf.ceprep_file.str.len && (CLI_PRESENT == cli_present("CE_PREPROCESS"))) cmd_qlf.ceprep_file.str.len = MAX_FN_LEN; # endif } else if (save_qlf) glb_cmd_qlf.qlf = save_qlf; return; } fis-gtm-V7.0-005/sr_unix/zlmov_lnames.c0000644000032200000250000000510214342376335016716 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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_string.h" #include #include "arlinkdbg.h" #ifdef USHBIN_SUPPORTED /* Routine to copy label names out of a shared object's literal text pool for a version of an object * about to be released to allow label text to still be available for other routines who have links to * the label table which has pointers to these label names. The label names are copied into malloc'd * storage for the duration of that routine's life in this process. * * Parameter: * * - hdr - Routine header address whose label table needs to be saved. * * Future change (GTM-8144) would eliminate the need for this. */ void zlmov_lnames(rhdtyp *hdr) { lab_tabent *lab_ent, *lab_bot, *lab_top; uint4 size; char *lab_ptr; lab_bot = hdr->labtab_adr; lab_top = hdr->labtab_adr + hdr->labtab_len; size = 0; assert((NULL != lab_bot) && (0 == lab_bot->lab_name.len)); /* The first label is null label */ /* Compute size of label names */ for (lab_ent = lab_bot + 1; lab_ent < lab_top; lab_ent++) { assert((lab_ent->lab_name.addr >= (char *)hdr->literal_text_adr) && (lab_ent->lab_name.addr < (char *)(hdr->literal_text_adr + hdr->literal_text_len))); size += lab_ent->lab_name.len; } if (0 < size) { lab_ptr = (char *)malloc(size); DBGARLNK((stderr, "zlmov_lnames: Label names copied from rtn %.*s (rtnhdr 0x"lvaddr") to malloc'd space at 0x"lvaddr " len %d\n", hdr->routine_name.len, hdr->routine_name.addr, hdr, lab_ptr, size)); /* Store address of malloc'd label text block in the routine header so we can find it to release it on an unlink-all * (ZGOTO 0:entryref). */ hdr->lbltext_ptr = (unsigned char *)lab_ptr; for (lab_ent = lab_bot + 1; lab_ent < lab_top; lab_ent++) { memcpy(lab_ptr, lab_ent->lab_name.addr, lab_ent->lab_name.len); lab_ent->lab_name.addr = lab_ptr; lab_ptr += lab_ent->lab_name.len; } } else DBGARLNK((stderr, "zlmov_lnames: Label names for rtn %.*s (rtnhdr 0x"lvaddr" not copied (nothing to copy)\n", hdr->routine_name.len, hdr->routine_name.addr, hdr)); } #endif /* USHBIN_SUPPORTED */ fis-gtm-V7.0-005/sr_unix/zro_gettok.c0000755000032200000250000000215514342376330016402 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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" #ifdef AUTORELINK_SUPPORTED # include "rtnhdr.h" #endif #include "zroutines.h" int zro_gettok (char **lp, char *top, mstr *tok) { int toktyp; if (*lp >= top) toktyp = ZRO_EOL; else switch (**lp) { case ZRO_DEL: toktyp = ZRO_DEL; while (*lp < top && **lp == ZRO_DEL) (*lp)++; break; case ZRO_LBR: toktyp = ZRO_LBR; (*lp)++; break; case ZRO_RBR: toktyp = ZRO_RBR; (*lp)++; break; default: tok->addr = *lp; while (*lp < top && **lp != ZRO_DEL && **lp != ZRO_LBR && **lp != ZRO_RBR) (*lp)++; toktyp = ZRO_IDN; tok->len = INTCAST(*lp - tok->addr); break; } return toktyp; } fis-gtm-V7.0-005/sr_unix/zro_load.c0000644000032200000250000002650614342376330016027 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_string.h" #include "gtm_stat.h" #include "gtm_stdlib.h" #include "io.h" #include "iosp.h" #include "zroutines.h" #include "parse_file.h" #include "eintr_wrappers.h" #include "error.h" #include "zro_shlibs.h" #include "gtm_limits.h" #define GETTOK zro_gettok(&lp, top, &tok) error_def(ERR_DIRONLY); error_def(ERR_FILEPARSE); error_def(ERR_FSEXP); error_def(ERR_INVZROENT); error_def(ERR_MAXARGCNT); error_def(ERR_NOLBRSRC); error_def(ERR_QUALEXP); error_def(ERR_ZROSYNTAX); /* Routine to parse the value of $ZROUTINES and create the list of structures that define the (new) routine * search list order and define which (if any) directories can use auto-relink. * * Parameter: * str - string to parse (usually dollar_zroutines) * * Return code: * none */ void zro_load(mstr *str) { unsigned toktyp, status; boolean_t arlink_thisdir_enable, arlink_enabled; mstr tok, transtr; char *lp, *top; zro_ent array[ZRO_MAX_ENTS], *op, *zro_root_ptr; int oi, si, total_ents; struct stat outbuf; int stat_res; char tranbuf[MAX_FN_LEN + 1]; parse_blk pblk; size_t root_alloc_size; /* For SCI */ DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; arlink_enabled = FALSE; memset(array, 0, SIZEOF(array)); lp = str->addr; top = lp + str->len; while ((lp < top) && (ZRO_DEL == *lp)) /* Bypass leading blanks */ lp++; array[0].type = ZRO_TYPE_COUNT; array[0].count = 0; memset(&pblk, 0, SIZEOF(pblk)); pblk.buffer = tranbuf; toktyp = GETTOK; if (ZRO_EOL == toktyp) { /* Null string - set default - implies current working directory only */ array[0].count = 1; array[1].type = ZRO_TYPE_OBJECT; array[1].str.len = 0; array[2].type = ZRO_TYPE_COUNT; array[2].count = 1; array[3].type = ZRO_TYPE_SOURCE; array[3].str.len = 0; si = 4; } else { /* String supplied - parse it */ for (oi = 1;;) { if (ZRO_IDN != toktyp) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FSEXP); if (ZRO_MAX_ENTS <= (oi + 1)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_MAXARGCNT, 1, ZRO_MAX_ENTS); /* We have type ZRO_IDN (an identifier/name of some sort). See if token has a "*" (ZRO_ALF) at the end * of it indicating that it is supposed to (1) be a directory and not a shared library and (2) that the * user desires this directory to have auto-relink capability. */ arlink_thisdir_enable = FALSE; /* All platforms allow the auto-relink indicator on object directories but only autorelink able platforms * (#ifdef AUTORELINK_SUPPORTED is set) do anything with it. Other platforms just ignore it. Specifying * "*" at end of non-object directories causes an error further downstream (FILEPARSE) when the "*" is * not stripped off the file name - unless someone has managed to create a directory with a "*" suffix. */ if (ZRO_ALF == *(tok.addr + tok.len - 1)) { /* Auto-relink is indicated */ arlink_thisdir_enable = TRUE; --tok.len; /* Remove indicator from name so we can use it */ assert(0 <= tok.len); } if (SIZEOF(tranbuf) <= tok.len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, tok.len, tok.addr); /* Run specified directory through parse_file to fill in any missing pieces and get some info on it */ pblk.buff_size = MAX_FN_LEN; /* Don't count null terminator here */ pblk.fnb = 0; status = parse_file(&tok, &pblk); if (!(status & 1)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, tok.len, tok.addr, status); tranbuf[pblk.b_esl] = 0; /* Needed for some subsequent STAT_FILE */ STAT_FILE(tranbuf, &outbuf, stat_res); if (-1 == stat_res) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, tok.len, tok.addr, errno); if (S_ISREG(outbuf.st_mode)) { /* Regular file - a shared library file */ if (arlink_thisdir_enable) /* Auto-relink indicator on shared library not permitted */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, tok.len, tok.addr); array[oi].shrlib = zro_shlibs_find(tranbuf); array[oi].type = ZRO_TYPE_OBJLIB; si = oi + 1; } else { if (!S_ISDIR(outbuf.st_mode)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_INVZROENT, 2, tok.len, tok.addr); array[oi].type = ZRO_TYPE_OBJECT; array[oi + 1].type = ZRO_TYPE_COUNT; si = oi + 2; # ifdef AUTORELINK_SUPPORTED # ifdef DEBUG /* If env var gtm_test_autorelink_always is set in dbg version, treat every * object directory specified in $zroutines as if * has been additionally specified. */ if (TREF(gtm_test_autorelink_always)) arlink_thisdir_enable = TRUE; # endif if (arlink_thisdir_enable) { /* Only setup autorelink struct if it is enabled */ if (!TREF(is_mu_rndwn_rlnkctl)) { transtr.addr = tranbuf; transtr.len = pblk.b_esl; array[oi].relinkctl_sgmaddr = (void_ptr_t)relinkctl_attach(&transtr, NULL, 0); } else { /* If zro_load() is called as a part of MUPIP RUNDOWN -RELINKCTL, then we do not * want to do relinkctl_attach() on all relinkctl files at once because we leave * the function holding the linkctl lock, which might potentially cause a deadlock * if multiple processes are run concurrently with different $gtmroutines. However, * we need a way to tell mu_rndwn_rlnkctl() which object directories are autorelink- * enabled. For that we set a negative number to the presently unused count field of * object directory entries in the zro_ent linked list. If we ever decide to make * that value meaningful, then, perhaps, ensuring that this count remains negative * in case of MUPIP RUNDOWN -RELINKCTL but has the correct absolute value would do * the trick. */ array[oi].count = ZRO_DIR_ENABLE_AR; } } # endif } arlink_enabled |= arlink_thisdir_enable; /* Cumulative value of enabled dirs */ array[0].count++; array[oi].str = tok; toktyp = GETTOK; if (ZRO_LBR == toktyp) { if (ZRO_TYPE_OBJLIB == array[oi].type) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_NOLBRSRC); toktyp = GETTOK; if (ZRO_DEL == toktyp) toktyp = GETTOK; if ((ZRO_IDN != toktyp) && (ZRO_RBR != toktyp)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_QUALEXP); array[oi + 1].count = 0; for (;;) { if (ZRO_RBR == toktyp) break; if (ZRO_IDN != toktyp) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FSEXP); if (ZRO_MAX_ENTS <= si) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_MAXARGCNT, 1, ZRO_MAX_ENTS); if (SIZEOF(tranbuf) <= tok.len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, tok.len, tok.addr); pblk.buff_size = MAX_FN_LEN; pblk.fnb = 0; status = parse_file(&tok, &pblk); if (!(status & 1)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, tok.len, tok.addr, status); tranbuf[pblk.b_esl] = 0; STAT_FILE(tranbuf, &outbuf, stat_res); if (-1 == stat_res) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, tok.len, tok.addr, errno); if (!S_ISDIR(outbuf.st_mode)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_DIRONLY, 2, tok.len, tok.addr); array[oi + 1].count++; array[si].type = ZRO_TYPE_SOURCE; array[si].str = tok; si++; toktyp = GETTOK; if (ZRO_DEL == toktyp) toktyp = GETTOK; } toktyp = GETTOK; } else { if ((ZRO_TYPE_OBJLIB != array[oi].type) && ((ZRO_DEL == toktyp) || (ZRO_EOL == toktyp))) { if (ZRO_MAX_ENTS <= si) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_MAXARGCNT, 1, ZRO_MAX_ENTS); array[oi + 1].count = 1; array[si] = array[oi]; array[si].type = ZRO_TYPE_SOURCE; si++; } } if (ZRO_EOL == toktyp) break; if (ZRO_DEL == toktyp) toktyp = GETTOK; else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZROSYNTAX, 2, str->len, str->addr); oi = si; } } total_ents = si; if (TREF(zro_root)) { assert((TREF(zro_root))->type == ZRO_TYPE_COUNT); oi = (TREF(zro_root))->count; assert(oi); for (op = TREF(zro_root) + 1; 0 < oi--;) { /* Release space held by translated entries */ assert((ZRO_TYPE_OBJECT == op->type) || (ZRO_TYPE_OBJLIB == op->type)); if (op->str.len) free(op->str.addr); if (ZRO_TYPE_OBJLIB == (op++)->type) continue; /* i.e. no sources for shared library */ assert(ZRO_TYPE_COUNT == op->type); si = (op++)->count; for (; si-- > 0; op++) { assert(ZRO_TYPE_SOURCE == op->type); if (op->str.len) free(op->str.addr); } } free(TREF(zro_root)); } root_alloc_size = total_ents * SIZEOF(zro_ent); /* For SCI */ zro_root_ptr = (zro_ent *)malloc(root_alloc_size); assert(NULL != zro_root_ptr); memcpy((uchar_ptr_t)zro_root_ptr, (uchar_ptr_t)array, root_alloc_size); TREF(zro_root) = zro_root_ptr; assert(ZRO_TYPE_COUNT == (TREF(zro_root))->type); oi = (TREF(zro_root))->count; assert(oi); for (op = TREF(zro_root) + 1; 0 < oi--;) { assert((ZRO_TYPE_OBJECT == op->type) || (ZRO_TYPE_OBJLIB == op->type)); if (op->str.len) { pblk.buff_size = MAX_FN_LEN; pblk.fnb = 0; status = parse_file(&op->str, &pblk); if (!(status & 1)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, op->str.len, op->str.addr, status); op->str.addr = (char *)malloc(pblk.b_esl + 1); op->str.len = pblk.b_esl; memcpy(op->str.addr, pblk.buffer, pblk.b_esl); op->str.addr[pblk.b_esl] = 0; } if (ZRO_TYPE_OBJLIB == (op++)->type) continue; assert(ZRO_TYPE_COUNT == op->type); si = (op++)->count; for (; 0 < si--; op++) { assert(ZRO_TYPE_SOURCE == op->type); if (op->str.len) { pblk.buff_size = MAX_FN_LEN; pblk.fnb = 0; status = parse_file(&op->str, &pblk); if (!(status & 1)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_ZROSYNTAX, 2, str->len, str->addr, ERR_FILEPARSE, 2, op->str.len, op->str.addr, status); op->str.addr = (char *)malloc(pblk.b_esl + 1); op->str.len = pblk.b_esl; memcpy(op->str.addr, pblk.buffer, pblk.b_esl); op->str.addr[pblk.b_esl] = 0; } } } ARLINK_ONLY(TREF(arlink_enabled) = arlink_enabled); /* Set if any zro entry is enabled for autorelink */ (TREF(set_zroutines_cycle))++; /* Signal need to recompute zroutines histories for each linked routine */ } fis-gtm-V7.0-005/sr_unix/zro_search.c0000644000032200000250000002510314342376330016345 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_stat.h" #include "gtm_limits.h" #include #include "eintr_wrappers.h" #include "error.h" #include "lv_val.h" /* Needed for "fgncal.h" */ #include "fgncal.h" #include "min_max.h" #ifdef AUTORELINK_SUPPORTED # include "rtnhdr.h" /* Defines zro_hist * type for return */ # include "parse_file.h" /* Needed for zro_search_hist() */ #endif #ifdef DEBUG #include "toktyp.h" /* Needed for "valid_mname.h" */ #include "valid_mname.h" #endif #include "zroutines.h" #include "arlinkdbg.h" error_def(ERR_FILEPARSE); error_def(ERR_SYSCALL); error_def(ERR_WILDCARD); error_def(ERR_ZFILENMTOOLONG); error_def(ERR_ZLINKFILE); /* Routine to perform a search of the $ZROUTINES structures (zro_ent) for a given routine source and/or object. * * Run through the zro_ent structures (source and/or object depending on which args have values). If looking for * both, find a related pair (i.e. we don't find objects for unrelated sources or vice versa). The zro_ent * structure list is in the following format: * a. An object directory (which itself can be the source directory too if no source directories explicitly * specified - example a zro_ent such as "obj(src)" has one object and one source dir where a spec such as * "dir" is both an object and source directory). * b. 0 or more related source directories * This then repeats until the end of the list (the ZRO_TYPE_COUNT records tell how many records exist of the given * type). * * Parameters: * objstr - If NULL, do not search for object, else pointer to object file text string. * objdir - NULL if objstr is NULL, otherwise is return pointer to associated object directory * (Note objdir is set to NULL if object directory is not found). * srcstr - Like objstr, except for associated source program. * srcdir - Like objdir, except for associated source program directory. * skip_shlib - If TRUE, skip over shared libraries. If FALSE, probe shared libraries. * * No return value. */ void zro_search(mstr *objstr, zro_ent **objdir, mstr *srcstr, zro_ent **srcdir, boolean_t skip_shlib) { uint4 status; zro_ent *op, *sp, *op_result, *sp_result; char objfn[PATH_MAX], srcfn[PATH_MAX], *obp, *sbp, save_char; int objcnt, srccnt; struct stat outbuf; int stat_res; mstr rtnname; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (!TREF(zro_root)) zro_init(); assert((NULL != objstr) || (NULL != srcstr)); /* Must search for object or source or both */ assert((NULL == objstr) || (NULL != objdir)); /* If object text, then must have pointer for result */ assert((NULL == srcstr) || (NULL != srcdir)); /* If source text, then must have pointer for result */ assert(ZRO_TYPE_COUNT == (TREF(zro_root))->type); op_result = sp_result = NULL; objcnt = (TREF(zro_root))->count; assert(0 < objcnt); for (op = TREF(zro_root) + 1; !op_result && !sp_result && (0 < objcnt--); ) { assert((ZRO_TYPE_OBJECT == op->type) || (ZRO_TYPE_OBJLIB == op->type)); if (NULL != objstr) { if (ZRO_TYPE_OBJLIB == op->type) { if (!skip_shlib) { assert(op->shrlib); rtnname.len = objstr->len - (int)STR_LIT_LEN(DOTOBJ); memcpy(objfn, objstr->addr, rtnname.len); objfn[rtnname.len] = 0; rtnname.addr = objfn; # ifdef DEBUG save_char = rtnname.addr[0]; if ('_' == save_char) rtnname.addr[0] = '%'; /* Temporary adjustment for "valid_mname" to not assert fail */ assert(valid_mname(&rtnname)); if ('_' == save_char) rtnname.addr[0] = save_char; /* restore */ # endif if (NULL != (op->shrsym = (void *)fgn_getrtn(op->shrlib, &rtnname, SUCCESS))) /* Note assignment above */ op_result = op; } op++; continue; } if ((op->str.len + objstr->len + 2) > SIZEOF(objfn)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZFILENMTOOLONG, 2, op->str.len, op->str.addr); obp = &objfn[0]; if (op->str.len) { memcpy(obp, op->str.addr, op->str.len); obp += op->str.len; *obp++ = '/'; } memcpy(obp, objstr->addr, objstr->len); obp += objstr->len; *obp++ = 0; STAT_FILE(objfn, &outbuf, stat_res); if (-1 == stat_res) { if (errno != ENOENT) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("stat"), CALLFROM, errno); } else op_result = op; } if (srcstr) { sp = op + 1; if (ZRO_TYPE_OBJLIB == op->type) { op = sp; continue; } assert(ZRO_TYPE_COUNT == sp->type); srccnt = (sp++)->count; for ( ; !sp_result && srccnt-- > 0; sp++) { assert(sp->type == ZRO_TYPE_SOURCE); if (sp->str.len + srcstr->len + 2 > SIZEOF(srcfn)) /* Extra 2 for '/' & null */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZFILENMTOOLONG, 2, sp->str.len, sp->str.addr); sbp = &srcfn[0]; if (sp->str.len) { memcpy (sbp, sp->str.addr, sp->str.len); sbp += sp->str.len; *sbp++ = '/'; } memcpy(sbp, srcstr->addr, srcstr->len); sbp += srcstr->len; *sbp++ = 0; STAT_FILE(srcfn, &outbuf, stat_res); if (-1 == stat_res) { if (ENOENT != errno) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("stat"), CALLFROM, errno); } else { sp_result = sp; op_result = op; } } op = sp; } else { op++; assert(ZRO_TYPE_COUNT == op->type); op += op->count; op++; } } if (NULL != objdir) *objdir = op_result; if (NULL != srcdir) *srcdir = sp_result; return; } #ifdef AUTORELINK_SUPPORTED /* Routine to take a given object file path and create the autorelink search history for that object and return the zro_ent * structure associated with the object file with the following caveats: * 1. If the file is in a $ZROUTINES entry that is not autorelink-enabled, no history is returned. * 2. Directories that are not autorelink-enabled do not show up in the history. * 3. If the directory is not one that is currently in $ZROUTINES, no history is returned (special case of #2). * * Parameters: * * objstr - Address of mstr containing full path of the object file * objdir - Store *addr of the zroutines entry addr (if non-NULL). Stores NULL if not found. * * Return value: * * Address of search history block (if NULL, *objdir is also NULL) */ zro_hist *zro_search_hist(char *objnamebuf, zro_ent **objdir) { uint4 status; parse_blk pblk; zro_ent *op, *op_result; mstr objstr, dirpath, rtnname, zroentname; int objcnt; zro_search_hist_ent zhent_base[ZRO_MAX_ENTS]; zro_search_hist_ent *zhent; zro_hist *recent_zhist; char obj_file[MAX_FN_LEN + 1]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; objstr.addr = objnamebuf; assert(NULL != objstr.addr); objstr.len = STRLEN(objstr.addr); assert(0 < objstr.len); DBGARLNK((stderr, "\n\nzro_search_hist: Entered for %s\n", objnamebuf)); /* First parse our input string to isolate the directory name we will look up */ memset(&pblk, 0, SIZEOF(pblk)); pblk.buff_size = MAX_FN_LEN; pblk.buffer = obj_file; pblk.fop = F_SYNTAXO; /* Just a syntax parse */ status = parse_file(&objstr, &pblk); if (!(status & 1)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_FILEPARSE, 2, objstr.len, objstr.addr, status); if (pblk.fnb & F_WILD) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, objstr.len, objstr.addr, ERR_WILDCARD, 2, objstr.len, objstr.addr); dirpath.len = pblk.b_dir; /* Create mstr describing the object file's dirpath */ dirpath.addr = pblk.l_dir; /* Remove any trailing '/' in the directory name since the directories in the zro_ent blocks have none * so we need to match their style. Note this means if for whatever unfathomable reason there is a * zro_ent for the root directory, the length can be zero so allow for that. */ if ('/' == *(dirpath.addr + dirpath.len - 1)) dirpath.len--; assert((0 <= dirpath.len) && (NULL != dirpath.addr)); /* Build the routine-name mstr that contains only the routine name */ rtnname.addr = pblk.l_name; rtnname.len = pblk.b_name; assert((0 < rtnname.len) && (NULL != rtnname.addr)); CONVERT_FILENAME_TO_RTNNAME(rtnname); assert(valid_mname(&rtnname)); DBGARLNK((stderr, "zro_search_hist: Looking for dir %.*s for routine %.*s\n", dirpath.len, dirpath.addr, rtnname.len, rtnname.addr)); /* Now lets locate it in the parsed $ZROUTINES block list while creating some history */ if (!TREF(zro_root)) zro_init(); op_result = NULL; objcnt = (TREF(zro_root))->count; assert(0 < objcnt); zhent = &zhent_base[0]; /* History initialization */ for (op = TREF(zro_root) + 1; (0 < objcnt--);) { /* Once through each zro_ent block in our array only looking at object directory type entries */ assert((ZRO_TYPE_OBJECT == op->type) || (ZRO_TYPE_OBJLIB == op->type)); if (ZRO_TYPE_OBJLIB == op->type) continue; /* We only deal with object directories in this loop */ /* If this directory is autorelink enabled, add it to the history */ if (NULL != op->relinkctl_sgmaddr) zro_record_zhist(zhent++, op, &rtnname); /* In order to properly match the entries, we need to normalize them. The zro_ent name may or may not * have a trailing '/' in the name. The dirpath value won't have a trailing '/'. */ zroentname = op->str; DBGARLNK((stderr, "zro_search_hist: recsleft: %d current dirblk: %.*s\n", objcnt, zroentname.len, zroentname.addr)); if ((0 < zroentname.len) && ('/' == *(zroentname.addr + zroentname.len - 1))) zroentname.len--; if (MSTR_EQ(&dirpath, &zroentname)) { op_result = op; break; } /* Bump past source entries to next object entry */ op++; assert(ZRO_TYPE_COUNT == op->type); op += op->count; op++; } if (NULL != objdir) *objdir = op_result; /* If either of we didn't find the directory in the $ZROUTINES list (so it couldn't be auto-relink enabled or * we found the directory but it wasn't auto-relink enabled, we need return no history or zro_ent value. */ if ((NULL == op_result) || (NULL == op_result->relinkctl_sgmaddr)) return NULL; /* We found the routine in an auto-relink enabled directory so create/return the history and the zro_ent * structure we found it in. */ recent_zhist = zro_zhist_saverecent(zhent, &zhent_base[0]); return recent_zhist; } #endif fis-gtm-V7.0-005/sr_unix/zro_shlibs.c0000644000032200000250000000473414342376330016373 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2021 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_string.h" #include #include "lv_val.h" #include "fgncal.h" #include "parse_file.h" #include "zro_shlibs.h" #include "zroutines.h" #include "error.h" error_def(ERR_SYSCALL); error_def(ERR_TEXT); /* Routine to lookup given shlib_name to see if we already have it open. If yes, just * return its handle. Else, dlopen the shared library and return its handle. */ void *zro_shlibs_find(char *shlib_name) { open_shlib *oshlb; void *handle; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; for (oshlb = TREF(open_shlib_root); oshlb; oshlb = oshlb->next) { /* Brute force lookup of library - infrequent activity plus few libs mean we * won't make the effort to hash this (typically 0-5 libs max. */ if (0 == strcmp(shlib_name, oshlb->shlib_name)) { assert(oshlb->shlib_handle); return oshlb->shlib_handle; } } /* Library was not found. Open it and create a new entry */ handle = fgn_getpak(shlib_name, ERROR); oshlb = malloc(SIZEOF(open_shlib)); oshlb->shlib_handle = handle; strcpy(oshlb->shlib_name, shlib_name); oshlb->next = TREF(open_shlib_root); TREF(open_shlib_root) = oshlb; return handle; } /* Routine called to dlclose() all of the known libraries in our list so they are * detached allowing potentially new(er) versions to be linked in. */ void zro_shlibs_unlink_all(void) { open_shlib *oshlb, *oshlb_next; char *dlerr; int status, len; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; for (oshlb = TREF(open_shlib_root); oshlb; oshlb = oshlb_next) { /* Cycle through list close all libraries and releasing the elements */ oshlb_next = oshlb->next; status = dlclose(oshlb->shlib_handle); if (0 != status) { dlerr = dlerror(); len = STRLEN(dlerr); RTS_ERROR_ABT(VARLSTCNT(11) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("dlclose()"), CALLFROM, ERR_TEXT, 2, len, dlerr); } free(oshlb); } TREF(open_shlib_root) = NULL; zro_load(TADR(dollar_zroutines)); /* Reloads the shared libraries we need */ } fis-gtm-V7.0-005/sr_unix/zro_shlibs.h0000644000032200000250000000143614342376330016374 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2018 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. * * * ****************************************************************/ #ifndef ZRO_SHLIBS_INCLUDED #define ZRO_SHLIBS_INCLUDED typedef struct open_shlib_struct { struct open_shlib_struct *next; void *shlib_handle; char shlib_name[MAX_FN_LEN + 1]; } open_shlib; void *zro_shlibs_find(char *shlib_name); void zro_shlibs_unlink_all(void); #endif fis-gtm-V7.0-005/sr_unix/zroutinessp.h0000755000032200000250000000475114342376330016631 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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. * * * ****************************************************************/ #ifndef ZROUTINESSP_H_INCLUDED #define ZROUTINESSP_H_INCLUDED /* Parse token types returned by zro_gettok() used when parsing $ZROUTINES values */ #define ZRO_EOL 0 /* End of line */ #define ZRO_IDN 1 /* Identifier/name - directory or file name */ #define ZRO_DEL ' ' /* Delimiter (space) */ #define ZRO_LBR '(' /* Left parenthesis denoting source directory list start */ #define ZRO_RBR ')' /* Right parenthesis denoting source directory list end */ #define ZRO_ALF '*' /* Auto-relink flag/indicator */ /* Value to use in the count field of a zro_ent structure to mark a particular directory in $gtmroutines as autorelink-enabled for * subsequent traversal in MUPIP RUNDOWN -RELINKCTL. The value must be negative to be distinguishable from a real count. */ #define ZRO_DIR_ENABLE_AR -1 /* zro_ent fields are interpreted based on entry type: * ZRO_TYPE_COUNT --> count indicates number of entries following * this entry representing a list of source or object directories. * ZRO_TYPE_OBJECT, ZRO_TYPE_SOURCE --> str stores the path of the appropriate * object/source directory. * ZRO_TYPE_OBJLIB --> str and shrlib store the shared library file name and * its handle respectively. (the shrsym field is used as a place holder * to keep dlsym() value available for incr_link during each $ZROUTINES search). */ typedef struct zro_ent_type { uint4 type; int4 count; mstr str; /* Path name */ void_ptr_t shrlib; /* Result of dlopen(), if a shared library */ void_ptr_t shrsym; /* Placeholder for result of fgn_getrtn(), which we pass from zro_search() to * incr_link(). */ void_ptr_t relinkctl_sgmaddr; /* Shared memory control structure associated with this $ZRO entry */ } zro_ent; int zro_gettok(char **lp, char *top, mstr *tok); void zro_search(mstr *objstr, zro_ent **objdir, mstr *srcstr, zro_ent **srcdir, boolean_t skip); #ifdef AUTORELINK_SUPPORTED zro_hist *zro_search_hist(char *objnamebuf, zro_ent **objdir); #endif #endif /* ZROUTINESSP_H_INCLUDED */ fis-gtm-V7.0-005/sr_unix/zshow_devices.c0000644000032200000250000007031214342376330017064 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_stdio.h" #include "mlkdef.h" #include "zshow.h" #include "io.h" #include "iottdef.h" #include "trmdef.h" #include "iormdef.h" #include "gt_timer.h" #include "iosocketdef.h" #include "zshow_params.h" #include "nametabtyp.h" #include "mvalconv.h" #include "iosp.h" #include "mmemory.h" #include "trans_log_name.h" #include "view.h" #ifdef __MVS__ #include "gtm_zos_io.h" #include <_Ccsid.h> #endif #define ZS_ONE_OUT(V,TEXT) ((V)->str.len = 1, (V)->str.addr = (TEXT), zshow_output(output,&(V)->str)) #define ZS_STR_OUT(V,TEXT) ((V)->str.len = SIZEOF((TEXT)) - 1, (V)->str.addr = (TEXT), zshow_output(output,&(V)->str)) #define ZS_VAR_STR_OUT(V,TEXT) ((V)->str.len = STRLEN((TEXT)), (V)->str.addr = (TEXT), zshow_output(output,&(V)->str)) #define ZS_PARM_SP(V,TEXT) ((V)->str.len = dev_param_names[dev_param_index[zshow_param_index[(TEXT)].letter] + \ zshow_param_index[(TEXT)].offset ].len, \ (V)->str.addr = (char *)dev_param_names[dev_param_index[zshow_param_index[(TEXT)].letter] + \ zshow_param_index[(TEXT)].offset ].name, zshow_output(output,&(V)->str), ZS_ONE_OUT((V),space_text)) #define ZS_PARM_EQU(V,TEXT) ((V)->str.len = dev_param_names[dev_param_index[zshow_param_index[(TEXT)].letter] + \ zshow_param_index[(TEXT)].offset ].len, \ (V)->str.addr = (char *)dev_param_names[dev_param_index[zshow_param_index[(TEXT)].letter] + \ zshow_param_index[(TEXT)].offset ].name, zshow_output(output,&(V)->str), ZS_ONE_OUT((V),equal_text)) static readonly char space_text[] = {' '}; GBLREF boolean_t ctrlc_on, gtm_utf8_mode, hup_on; GBLREF io_log_name *io_root_log_name; GBLREF io_pair *io_std_device; GBLREF io_log_name *dollar_principal; GBLREF mstr dollar_prin_log; GBLREF mstr dollar_zpin; /* contains "< /" */ GBLREF mstr dollar_zpout; /* contains "> /" */ GBLREF int process_exiting; LITREF mstr chset_names[]; LITREF nametabent dev_param_names[]; LITREF uint4 dev_param_index[]; LITREF zshow_index zshow_param_index[]; static readonly char terminal_text[] = "TERMINAL "; static readonly char rmsfile_text[] = "RMS "; static readonly char fifo_text[] = "FIFO "; static readonly char pipe_text[] = "PIPE "; static readonly char socket_text[] = "SOCKET"; /* no trailing space */ static readonly char null_text[] = "NULL"; static readonly char devop[] = "OPEN "; static readonly char devcl[] = "CLOSED "; void zshow_devices(zshow_out *output) { io_log_name *l; /* logical name pointer */ io_log_name *savel; /* logical name pointer */ mval v; mval m; io_desc *tiod; d_rm_struct *rm_ptr; d_tt_struct *tt_ptr; d_socket_struct *dsocketptr; socket_struct *socketptr; io_termmask *mask_out; int4 i, j, ii, jj; boolean_t first; unsigned char delim_buff_sm[MAX_DELIM_LEN]; unsigned short delim_len_sm; char delim_mstr_buff[(MAX_DELIM_LEN * MAX_ZWR_EXP_RATIO) + 11]; mstr delim; int delim_len, tmpport; boolean_t same_encr_settings; static readonly char space8_text[] = " "; static readonly char filchar_text[] = "CHARACTERS"; static readonly char filesc_text[] = "ESCAPES"; static readonly char magtape_text[] = "MAGTAPE "; static readonly char mailbox_text[] = "MAILBOX "; static readonly char dollarc_text[] = "$C("; static readonly char equal_text[] = {'='}; static readonly char comma_text[] = {','}; static readonly char quote_text[] = {'"'}; static readonly char lparen_text[] = {'('}; static readonly char rparen_text[] = {')'}; static readonly char lb_text[] = {'['}; static readonly char rb_text[] = {']'}; static readonly char interrupt_text[] = "ZINTERRUPT "; static readonly char stdout_text[] = "0-out"; static readonly char input_key[] = "IKEY="; static readonly char input_iv[] = "IIV="; static readonly char output_key[] = "OKEY="; static readonly char output_iv[] = "OIV="; static readonly char key[] = "KEY="; static readonly char iv[] = "IV="; static readonly char hup_text[] = "HUPENABLE"; /* gtmsocket specific */ static readonly char at_text[] = {'@'}; static readonly char delimiter_text[] = "DELIMITER "; static readonly char nodelimiter_text[] = "NODELIMITER "; static readonly char local_text[] = "LOCAL="; static readonly char remote_text[] = "REMOTE="; static readonly char total_text[] = "TOTAL="; static readonly char current_text[] = "CURRENT="; static readonly char passive_text[] = "PASSIVE "; static readonly char active_text[] = "ACTIVE "; static readonly char descriptor_text[] = "DESC="; static readonly char trap_text[] = "TRAP "; static readonly char notrap_text[] = "NOTRAP "; static readonly char zdelay_text[] = "ZDELAY "; static readonly char znodelay_text[] = "ZNODELAY "; static readonly char zbfsize_text[] = "ZBFSIZE="; static readonly char zibfsize_text[] = "ZIBFSIZE="; static readonly char port_text[] = "PORT="; static readonly char ichset_text[] = "ICHSET="; static readonly char ochset_text[] = "OCHSET="; static readonly char tls_text[] = "TLS "; #ifdef __MVS__ static readonly char filetag_text[] = "FILETAG="; static readonly char untagged_text[] = "UNTAGGED"; static readonly char ebcdic_text[] = "EBCDIC"; static readonly char binary_text[] = "BINARY"; static readonly char processchset_text[] = "CHSET="; static readonly char text_text[] = " TEXT"; char csname[_CSNAME_LEN_MAX + 1], *csptr; #endif static readonly char zsh_socket_state[][11] = { "CONNECTED" ,"LISTENING" ,"BOUND" ,"CREATED" ,"INPROGRESS" ,"" }; static readonly char morereadtime_text[] = "MOREREADTIME="; char *charptr; # ifdef DEBUG DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # endif v.mvtype = MV_STR; savel = NULL; for (l = io_root_log_name; ((l != NULL) || (NULL != savel)); l = l->next) { /* If savel is set then process the output side of $principal */ if (NULL != savel) l = savel; if ((NULL != l) && (NULL != l->iod) && (n_io_dev_types == l->iod->type)) { assert(FALSE); continue; /* skip it on pro */ } if (NULL == l->iod) { assert(process_exiting); /* GTM-F-MEMORY occurred during device setup & we are creating zshow dump file */ assert(TREF(jobexam_counter)); continue; } if (l->iod->trans_name == l) { /* If it is an rm type we don't want to output the device if it is the stderr device for a pipe device */ if ((rm_ptr = (d_rm_struct*)l->iod->dev_sp) && (rm == l->iod->type) && rm_ptr->is_pipe && rm_ptr->stderr_parent) continue; if (l->iod->pair.in != l->iod->pair.out) { /* process the output side of $principal if savel is set */ if (NULL != savel) { tiod = l->iod->pair.out; savel = NULL; rm_ptr = (d_rm_struct*)tiod->dev_sp; if ('&' == tiod->trans_name->dollar_io[0]) { /* replace & with 0-out */ ZS_STR_OUT(&v, stdout_text); } else { /* prepend with 0-out and a space */ ZS_STR_OUT(&v, stdout_text); ZS_ONE_OUT(&v, space_text); v.str.addr = &tiod->trans_name->dollar_io[0]; v.str.len = tiod->trans_name->len; zshow_output(output,&v.str); } } else { /* plan to process the output side of $principal if it is std out */ if (l->iod->pair.out == io_std_device->out) savel = l; tiod = l->iod; v.str.addr = &l->dollar_io[0]; v.str.len = l->len; zshow_output(output,&v.str); } } else { tiod = l->iod; v.str.addr = &l->dollar_io[0]; v.str.len = l->len; zshow_output(output,&v.str); } ZS_ONE_OUT(&v, space_text); if (tiod->state == dev_open) { ZS_STR_OUT(&v, devop); switch(tiod->type) { case tt: ZS_STR_OUT(&v, terminal_text); tt_ptr = (d_tt_struct*)tiod->dev_sp; if (!ctrlc_on && io_std_device->out == tiod) /* and standard input */ { ZS_PARM_SP(&v, zshow_nocena); } if (tt_ptr->enbld_outofbands.mask) { ZS_PARM_EQU(&v, zshow_ctra); ZS_STR_OUT(&v,dollarc_text); first = TRUE; for ( i = 1, j = 0; j < 32 ; j++,i = i * 2) { if (i & tt_ptr->enbld_outofbands.mask) { if (!first) { ZS_ONE_OUT(&v, comma_text); }else { first = FALSE; } MV_FORCE_MVAL(&m,j); mval_write(output,&m,FALSE); } } ZS_ONE_OUT(&v, rparen_text); ZS_ONE_OUT(&v, space_text); } if ((int4)(tt_ptr->term_ctrl) & TRM_NOECHO) { ZS_PARM_SP(&v, zshow_noecho); } if (tt_ptr->term_ctrl & TRM_PASTHRU) { ZS_PARM_SP(&v, zshow_past); } else { ZS_PARM_SP(&v, zshow_nopast); } if (!(tt_ptr->term_ctrl & TRM_ESCAPE)) { ZS_PARM_SP(&v, zshow_noesca); } if (tt_ptr->term_ctrl & TRM_READSYNC) { ZS_PARM_SP(&v, zshow_reads); } else { ZS_PARM_SP(&v, zshow_noreads); } if (tt_ptr->term_ctrl & TRM_NOTYPEAHD) { ZS_PARM_SP(&v, zshow_notype); } else { ZS_PARM_SP(&v, zshow_type); } if (!tiod->wrap) { ZS_PARM_SP(&v, zshow_nowrap); } mask_out = &tt_ptr->mask_term; if (!tt_ptr->default_mask_term) { ZS_PARM_EQU(&v, zshow_term); ZS_STR_OUT(&v,dollarc_text); first = TRUE; for ( i = 0; i < 8 ;i++) { for ( j = 0; j < 32; j++) if (mask_out->mask[i] & (1 << j)) { if (!first) { ZS_ONE_OUT(&v, comma_text); } else first = FALSE; MV_FORCE_MVAL(&m,i * 32 + j); mval_write(output,&m,FALSE); } } ZS_ONE_OUT(&v, rparen_text); ZS_ONE_OUT(&v, space_text); } ZS_PARM_EQU(&v, zshow_width); MV_FORCE_MVAL(&m,(int)tiod->width); mval_write(output, &m, FALSE); ZS_ONE_OUT(&v, space_text); ZS_PARM_EQU(&v, zshow_leng); MV_FORCE_MVAL(&m,(int)tiod->pair.out->length); mval_write(output, &m, FALSE); ZS_ONE_OUT(&v, space_text); if (tiod->write_filter) { bool twoparms = FALSE; ZS_PARM_EQU(&v, zshow_fil); if (tiod->write_filter & CHAR_FILTER) { if (tiod->write_filter & ESC1) { twoparms = TRUE; ZS_ONE_OUT(&v,lparen_text); } ZS_STR_OUT(&v,filchar_text); if (twoparms) { ZS_ONE_OUT(&v, comma_text); ZS_ONE_OUT(&v, space_text); } } if (tiod->write_filter & ESC1) ZS_STR_OUT(&v,filesc_text); if (twoparms) ZS_ONE_OUT(&v,rparen_text); ZS_ONE_OUT(&v, space_text); } if (TT_EDITING & tt_ptr->ext_cap) ZS_PARM_SP(&v, zshow_edit); if (TT_NOINSERT & tt_ptr->ext_cap) ZS_PARM_SP(&v, zshow_noinse); if (TT_EMPTERM & tt_ptr->ext_cap) ZS_PARM_SP(&v, zshow_empterm); if (tt_ptr->canonical) ZS_STR_OUT(&v, "CANONICAL "); switch(tiod->ichset) { case CHSET_M: if (gtm_utf8_mode) { ZS_STR_OUT(&v, ichset_text); zshow_output(output, &chset_names[tiod->ichset]); ZS_ONE_OUT(&v, space_text); } break; case CHSET_UTF8: assert(gtm_utf8_mode); break; default: assertpro(tiod->ichset != tiod->ichset); } switch(tiod->ochset) { case CHSET_M: if (gtm_utf8_mode) { ZS_STR_OUT(&v, ochset_text); zshow_output(output, &chset_names[tiod->ochset]); ZS_ONE_OUT(&v, space_text); } break; case CHSET_UTF8: assert(gtm_utf8_mode); break; default: assertpro(tiod->ochset != tiod->ochset); } if (tt_ptr->mupintr) ZS_STR_OUT(&v, interrupt_text); if (hup_on) ZS_STR_OUT(&v, hup_text); break; case rm: /* we go to rm_ptr above for the rm type */ if (rm_ptr->fifo) ZS_STR_OUT(&v,fifo_text); else if (!rm_ptr->is_pipe) { ZS_STR_OUT(&v,rmsfile_text); if (rm_ptr->follow) { ZS_PARM_SP(&v, zshow_follow); } } else { ZS_STR_OUT(&v,pipe_text); if (rm_ptr->dev_param_pairs.num_pairs) { int ignore_stderr = FALSE; /* if one of the dev_param_pairs[i]->name is the STDERR then we don't want to output it if the device is closed. We'll check them all even though it is currently the last one - just to be safe. */ if (rm_ptr->stderr_child && (dev_open != rm_ptr->stderr_child->state)) ignore_stderr = TRUE; for ( i = 0; i < rm_ptr->dev_param_pairs.num_pairs; i++ ) { if (TRUE == ignore_stderr && 0 == STRCMP(rm_ptr->dev_param_pairs.pairs[i].name, "STDERR=")) continue; ZS_VAR_STR_OUT( &v,rm_ptr->dev_param_pairs.pairs[i].name); ZS_VAR_STR_OUT( &v,rm_ptr->dev_param_pairs.pairs[i].definition); ZS_ONE_OUT(&v, space_text); } } if (rm_ptr->independent) { ZS_PARM_SP(&v, zshow_independent); } if (rm_ptr->parse) { ZS_PARM_SP(&v, zshow_parse); } } if (rm_ptr->fixed) { ZS_PARM_SP(&v, zshow_fixed); } else if (rm_ptr->stream) { ZS_PARM_SP(&v, zshow_stream); } if (rm_ptr->read_only) { ZS_PARM_SP(&v, zshow_read); } if (gtm_utf8_mode && (IS_UTF_CHSET(tiod->ichset) || IS_UTF_CHSET(tiod->ochset))) { if (!rm_ptr->def_recsize) { ZS_PARM_EQU(&v, zshow_rec); MV_FORCE_MVAL(&m, (int)rm_ptr->recordsize); mval_write(output, &m, FALSE); ZS_ONE_OUT(&v, space_text); } if (!rm_ptr->def_width) { ZS_PARM_EQU(&v, zshow_width); MV_FORCE_MVAL(&m, (int)tiod->width); mval_write(output, &m, FALSE); ZS_ONE_OUT(&v, space_text); } } else if (tiod->width != DEF_RM_WIDTH) { ZS_PARM_EQU(&v, zshow_rec); MV_FORCE_MVAL(&m,(int)tiod->width); mval_write(output, &m, FALSE); ZS_ONE_OUT(&v, space_text); } if (!tiod->wrap) { ZS_PARM_SP(&v, zshow_nowrap); } switch(tiod->ichset) { case CHSET_M: if (gtm_utf8_mode) { ZS_STR_OUT(&v, ichset_text); zshow_output(output, &chset_names[tiod->ichset]); ZS_ONE_OUT(&v, space_text); } break; case CHSET_UTF8: assert(gtm_utf8_mode); break; case CHSET_UTF16: case CHSET_UTF16BE: case CHSET_UTF16LE: assert(gtm_utf8_mode); ZS_STR_OUT(&v, ichset_text); zshow_output(output, &chset_names[tiod->ichset]); ZS_ONE_OUT(&v, space_text); break; default: assertpro(tiod->ichset != tiod->ichset); } switch(tiod->ochset) { case CHSET_M: if (gtm_utf8_mode) { ZS_STR_OUT(&v, ochset_text); zshow_output(output, &chset_names[tiod->ochset]); ZS_ONE_OUT(&v, space_text); } break; case CHSET_UTF8: assert(gtm_utf8_mode); break; case CHSET_UTF16: case CHSET_UTF16BE: case CHSET_UTF16LE: assert(gtm_utf8_mode); ZS_STR_OUT(&v, ochset_text); zshow_output(output, &chset_names[tiod->ochset]); ZS_ONE_OUT(&v, space_text); break; default: assertpro(tiod->ochset != tiod->ochset); } #ifdef __MVS__ if (TAG_ASCII != tiod->file_tag) { ZS_STR_OUT(&v, filetag_text); switch ((unsigned int)tiod->file_tag) { case TAG_UNTAGGED: ZS_STR_OUT(&v, untagged_text); break; case TAG_EBCDIC: ZS_STR_OUT(&v, ebcdic_text); break; case TAG_BINARY: ZS_STR_OUT(&v, binary_text); break; default: if (-1 == __toCSName((__ccsid_t)tiod->file_tag, csname)) { /* no name so output number */ csptr = (char *)i2asc((uchar_ptr_t)csname, (unsigned int)tiod->file_tag); *csptr = '\0'; /* terminate */ } ZS_VAR_STR_OUT(&v, csname); } if (tiod->text_flag) ZS_STR_OUT(&v, text_text); ZS_ONE_OUT(&v, space_text); } if (tiod->file_chset != tiod->process_chset && (!(0 == tiod->file_chset && CHSET_ASCII == tiod->process_chset) && !(CHSET_ASCII == tiod->file_chset && CHSET_M == tiod->process_chset))) { /* suppress default cases */ ZS_STR_OUT(&v, processchset_text); zshow_output(output, &chset_names[tiod->process_chset]); ZS_ONE_OUT(&v, space_text); } #endif same_encr_settings = (rm_ptr->input_encrypted && rm_ptr->output_encrypted && MSTR_EQ(&rm_ptr->input_key, &rm_ptr->output_key) && MSTR_EQ(&rm_ptr->input_iv, &rm_ptr->output_iv)); if (rm_ptr->input_encrypted) { if (same_encr_settings) ZS_STR_OUT(&v, key); else ZS_STR_OUT(&v, input_key); if (NULL != rm_ptr->input_key.addr) { v.str.addr = rm_ptr->input_key.addr; v.str.len = rm_ptr->input_key.len; zshow_output(output, &v.str); } ZS_ONE_OUT(&v, space_text); if (same_encr_settings) ZS_STR_OUT(&v, iv); else ZS_STR_OUT(&v, input_iv); if (NULL != rm_ptr->input_iv.addr) { v.str.addr = rm_ptr->input_iv.addr; v.str.len = rm_ptr->input_iv.len; zshow_output(output, &v.str); } ZS_ONE_OUT(&v, space_text); } if (!same_encr_settings && rm_ptr->output_encrypted) { ZS_STR_OUT(&v, output_key); if (NULL != rm_ptr->output_key.addr) { v.str.addr = rm_ptr->output_key.addr; v.str.len = rm_ptr->output_key.len; zshow_output(output, &v.str); } ZS_ONE_OUT(&v, space_text); ZS_STR_OUT(&v, output_iv); if (NULL != rm_ptr->output_iv.addr) { v.str.addr = rm_ptr->output_iv.addr; v.str.len = rm_ptr->output_iv.len; zshow_output(output, &v.str); } ZS_ONE_OUT(&v, space_text); } break; case gtmsocket: delim.addr = delim_mstr_buff; delim_len = 0; ZS_STR_OUT(&v, socket_text); dsocketptr = (d_socket_struct *)tiod->dev_sp; ZS_ONE_OUT(&v, space_text); if (!tiod->wrap) { ZS_PARM_SP(&v, zshow_nowrap); } ZS_STR_OUT(&v, total_text); MV_FORCE_MVAL(&m, (int)dsocketptr->n_socket); mval_write(output, &m, FALSE); ZS_ONE_OUT(&v, space_text); ZS_STR_OUT(&v, current_text); MV_FORCE_MVAL(&m, (int)dsocketptr->current_socket); mval_write(output, &m, FALSE); ZS_ONE_OUT(&v, space_text); if (dsocketptr->mupintr) ZS_STR_OUT(&v, interrupt_text); output->flush = TRUE; zshow_output(output, 0); for(ii = 0; ii < dsocketptr->n_socket; ii++) { /* output each socket */ socketptr = dsocketptr->socket[ii]; ZS_STR_OUT(&v, space8_text); /* socket handle */ ZS_STR_OUT(&v, socket_text); ZS_ONE_OUT(&v, lb_text); MV_FORCE_MVAL(&m, ii); mval_write(output, &m, FALSE); ZS_ONE_OUT(&v, rb_text); ZS_ONE_OUT(&v, equal_text); v.str.addr = socketptr->handle; v.str.len = socketptr->handle_len; zshow_output(output, &v.str); ZS_ONE_OUT(&v, space_text); /* socket descriptor */ ZS_STR_OUT(&v, descriptor_text); MV_FORCE_MVAL(&m, socketptr->sd); mval_write(output, &m, FALSE); ZS_ONE_OUT(&v, space_text); /* socket state */ ZS_VAR_STR_OUT(&v, zsh_socket_state[socketptr->state]); ZS_ONE_OUT(&v, space_text); /* socket IO mode */ switch(tiod->ichset) { case CHSET_M: if (gtm_utf8_mode) { ZS_STR_OUT(&v, ichset_text); zshow_output(output, &chset_names[tiod->ichset]); ZS_ONE_OUT(&v, space_text); } break; case CHSET_UTF8: assert(gtm_utf8_mode); break; case CHSET_UTF16: case CHSET_UTF16BE: case CHSET_UTF16LE: assert(gtm_utf8_mode); ZS_STR_OUT(&v, ichset_text); zshow_output(output, &chset_names[tiod->ichset]); ZS_ONE_OUT(&v, space_text); break; default: assertpro(tiod->ichset != tiod->ichset); } switch(tiod->ochset) { case CHSET_M: if (gtm_utf8_mode) { ZS_STR_OUT(&v, ochset_text); zshow_output(output, &chset_names[tiod->ochset]); ZS_ONE_OUT(&v, space_text); } break; case CHSET_UTF8: assert(gtm_utf8_mode); break; case CHSET_UTF16: case CHSET_UTF16BE: case CHSET_UTF16LE: assert(gtm_utf8_mode); ZS_STR_OUT(&v, ochset_text); zshow_output(output, &chset_names[tiod->ochset]); ZS_ONE_OUT(&v, space_text); break; default: assertpro(tiod->ochset != tiod->ochset); } /* socket type */ if (socketptr->passive) { ZS_STR_OUT(&v, passive_text); } else { ZS_STR_OUT(&v, active_text); } ZS_ONE_OUT(&v, space_text); /* error trapping */ if (socketptr->ioerror) { ZS_STR_OUT(&v, trap_text); } else { ZS_STR_OUT(&v, notrap_text); } ZS_ONE_OUT(&v, space_text); /* address + port */ if ((socket_local != socketptr->protocol) && socketptr->passive) { ZS_STR_OUT(&v, port_text); tmpport = (int)socketptr->local.port; MV_FORCE_MVAL(&m, tmpport); mval_write(output, &m, FALSE); } else { ZS_STR_OUT(&v, remote_text); if (NULL != socketptr->remote.saddr_ip) { v.str.addr = socketptr->remote.saddr_ip; v.str.len = STRLEN(socketptr->remote.saddr_ip); } else { v.str.addr = ""; v.str.len = 0; } zshow_output(output, &v.str); if (socket_local != socketptr->protocol) { ZS_ONE_OUT(&v, at_text); tmpport = (int)socketptr->remote.port; MV_FORCE_MVAL(&m, tmpport); mval_write(output, &m, FALSE); } ZS_ONE_OUT(&v, space_text); if (socket_local == socketptr->protocol) { ZS_STR_OUT(&v, local_text); if (NULL != socketptr->local.sa) { charptr = ((struct sockaddr_un *) (socketptr->local.sa))->sun_path; ZS_VAR_STR_OUT(&v, charptr); } else if (NULL != socketptr->remote.sa) { /* CONNECT shows remote path as LOCAL */ charptr = ((struct sockaddr_un *) (socketptr->remote.sa))->sun_path; ZS_VAR_STR_OUT(&v, charptr); } } else if (NULL != socketptr->local.saddr_ip) { ZS_STR_OUT(&v, local_text); v.str.addr = socketptr->local.saddr_ip; v.str.len = STRLEN(socketptr->local.saddr_ip); zshow_output(output, &v.str); ZS_ONE_OUT(&v, at_text); tmpport = (int)socketptr->local.port; MV_FORCE_MVAL(&m, tmpport); mval_write(output, &m, FALSE); } } ZS_ONE_OUT(&v, space_text); output->flush = TRUE; zshow_output(output, 0); ZS_STR_OUT(&v, space8_text); ZS_STR_OUT(&v, space8_text); /* zdelay */ if (socketptr->nodelay) { ZS_STR_OUT(&v, znodelay_text); } else { ZS_STR_OUT(&v, zdelay_text); } ZS_ONE_OUT(&v, space_text); /* zbfsize */ ZS_STR_OUT(&v, zbfsize_text); MV_FORCE_MVAL(&m, (int4)(socketptr->buffer_size)); mval_write(output, &m, FALSE); ZS_ONE_OUT(&v, space_text); /* izbfsize */ ZS_STR_OUT(&v, zibfsize_text); MV_FORCE_MVAL(&m, socketptr->bufsiz); mval_write(output, &m, FALSE); ZS_ONE_OUT(&v, space_text); if (socketptr->tlsenabled && (NULL != socketptr->tlssocket)) { ZS_STR_OUT(&v, tls_text); } /* delimiters */ if (socketptr->n_delimiter > 0) { output->flush = TRUE; zshow_output(output, 0); ZS_STR_OUT(&v, space8_text); ZS_STR_OUT(&v, space8_text); ZS_STR_OUT(&v, delimiter_text); for (jj = 0; jj < socketptr->n_delimiter; jj++) { delim_len_sm = socketptr->delimiter[jj].len; memcpy(delim_buff_sm, socketptr->delimiter[jj].addr, delim_len_sm); delim_len = (MAX_DELIM_LEN * MAX_ZWR_EXP_RATIO) + 11; format2zwr(delim_buff_sm, delim_len_sm, (uchar_ptr_t)delim.addr, &delim_len); delim.len = (unsigned short)delim_len; assert(SIZEOF(delim_mstr_buff) >= delim_len); zshow_output(output, &delim); ZS_ONE_OUT(&v, space_text); } } else { ZS_STR_OUT(&v, nodelimiter_text); } /* readmoretime */ if (DEFAULT_MOREREAD_TIMEOUT != socketptr->moreread_timeout) { ZS_STR_OUT(&v, morereadtime_text); MV_FORCE_MVAL(&m, (int)socketptr->moreread_timeout); mval_write(output, &m, FALSE); } output->flush = TRUE; zshow_output(output, 0); } default: v.str.len = 0; break; } if (tiod->error_handler.len) { ZS_PARM_EQU(&v, zshow_exce); ZS_ONE_OUT(&v, quote_text); v.str = tiod->error_handler; zshow_output(output, &v.str); output->flush = TRUE; ZS_ONE_OUT(&v, quote_text); } else { output->flush = TRUE; zshow_output(output, 0); } } else { output->flush = TRUE; ZS_STR_OUT(&v, devcl); } } } } /* Called from op_fnview for DEVICE keyword to get device type and status * * * device_name pointer to mstr for device name, may be $ZPIN or $ZPOUT * * device output buffer : devicetype:open/closed * * device_len length of buffer * * Returns number of characters in output * */ int view_device(mstr *device_name, unsigned char *device, int device_len) { int4 len, len2, stat; mstr tn; /* translated name */ mstr devicename; io_desc *iod; io_log_name *nlog, *tl; d_rm_struct *rm_ptr; char buf1[MAX_TRANS_NAME_LEN]; /* buffer to hold translated name */ char *c1; /* used to compare $P name */ char *devicetype; int nlen; /* len of $P name */ io_log_name *tlp; /* logical record for translated name for $principal */ int nldone; /* 0 if not $ZPIN or $ZPOUT, 1 if $ZPIN and 2 if $ZPOUT */ nldone = len = 0; if ((io_std_device->in != io_std_device->out)) { tlp = dollar_principal ? dollar_principal : io_root_log_name->iod->trans_name; nlen = tlp->len; assert(dollar_zpout.len == dollar_zpin.len); if ((nlen + dollar_zpout.len) == device_name->len) { /* passed the length test now compare the 2 pieces, the first one the length of $P and the second $ZPIN or $ZPOUT */ c1 = (char *)tlp->dollar_io; if (!memvcmp(c1, nlen, device_name->addr, nlen)) { if (!memvcmp(dollar_zpin.addr, dollar_zpin.len, &(device_name->addr[nlen]), dollar_zpin.len)) nldone = 1; else if (!memvcmp(dollar_zpout.addr, dollar_zpout.len, &(device_name->addr[nlen]), dollar_zpout.len)) nldone = 2; } } } if (0 == nldone) nlog = get_log_name(device_name, NO_INSERT); else nlog = get_log_name(&dollar_prin_log, NO_INSERT); if (NULL == nlog) { stat = TRANS_LOG_NAME(device_name, &tn, buf1, SIZEOF(buf1), dont_sendmsg_on_log2long); if (SS_NORMAL == stat) { if (0 != (tl = get_log_name(&tn, NO_INSERT))) nlog = tl; } } if (NULL != nlog) { iod = nlog->iod; /* if iod is standard in device and it is a split device and it is $ZPOUT set iod to output device */ if ((2 == nldone) && (io_std_device->in == iod)) iod = io_std_device->out; switch (iod->type) { case tt: devicetype = terminal_text; break; case rm: rm_ptr = (d_rm_struct *)iod->dev_sp; if (rm_ptr->fifo) devicetype = fifo_text; else if (!rm_ptr->is_pipe) devicetype = rmsfile_text; else devicetype = pipe_text; break; case gtmsocket: devicetype = socket_text; break; case nl: devicetype = null_text; break; default: devicetype = NULL; } if (NULL != devicetype) { len = strlen(devicetype); if (' ' == devicetype[len - 1]) len--; assert((len + 1 + strlen(devcl)) <= device_len); /* +1 = : */ memcpy(device, devicetype, len); device[len] = ':'; len++; c1 = (iod->state == dev_open) ? devop : devcl; len2 = strlen(c1) - 1; /* not the trailing space */ assert(' ' == c1[len2]); memcpy(&device[len], c1, len2); len += len2; } } return len; } fis-gtm-V7.0-005/sr_unix/zshow_rctldump.c0000644000032200000250000000710414342376330017273 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2015 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" #ifdef AUTORELINK_SUPPORTED #include "gtm_string.h" #include "relinkctl.h" #include "util.h" #include "zshow.h" #define DUMP_ONE_LINE(OUTPUT, BUFF, NBYTES) \ { \ mstr line; \ \ if (NBYTES >= SIZEOF(BUFF)) \ NBYTES = SIZEOF(BUFF); /* Output from SNPRINTF was truncated. */ \ if (NULL != OUTPUT) \ { /* Caller is ZSHOW "R". Use zshow_output. */ \ line.len = NBYTES; \ line.addr = &BUFF[0]; \ OUTPUT->flush = TRUE; \ zshow_output(OUTPUT, &line); \ } else \ { /* Caller is MUPIP RCTLDUMP. Use util_out_print. */ \ util_out_print("!AD", FLUSH, NBYTES, BUFF); \ } \ } /* Implements ZSHOW "A". But also called by MUPIP RCTLDUMP ("output" parameter is NULL in this case) to do the same thing. */ void zshow_rctldump(zshow_out *output) { open_relinkctl_sgm *linkctl; relinkshm_hdr_t *shm_hdr; relinkrec_t *linkrec; relinkctl_data *hdr; rtnobjshm_hdr_t *rtnobj_shm_hdr; int i, j, recnum, n_records, nbytes; char buff[OUT_BUFF_SIZE]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; for (linkctl = TREF(open_relinkctl_list); NULL != linkctl; linkctl = linkctl->next) { hdr = linkctl->hdr; nbytes = SNPRINTF(buff, SIZEOF(buff), "Object Directory : %.*s", linkctl->zro_entry_name.len, linkctl->zro_entry_name.addr); DUMP_ONE_LINE(output, buff, nbytes); nbytes = SNPRINTF(buff, SIZEOF(buff), "Relinkctl filename : %s", linkctl->relinkctl_path); DUMP_ONE_LINE(output, buff, nbytes); n_records = hdr->n_records; nbytes = SNPRINTF(buff, SIZEOF(buff), "# of routines / max : %d / %d", n_records, hdr->relinkctl_max_rtn_entries); DUMP_ONE_LINE(output, buff, nbytes); nbytes = SNPRINTF(buff, SIZEOF(buff), "# of attached processes : %d", hdr->nattached); DUMP_ONE_LINE(output, buff, nbytes); nbytes = SNPRINTF(buff, SIZEOF(buff), "Relinkctl shared memory : shmid: %d shmlen: 0x%llx", hdr->relinkctl_shmid, hdr->relinkctl_shmlen); DUMP_ONE_LINE(output, buff, nbytes); shm_hdr = GET_RELINK_SHM_HDR(linkctl); for (i = 0, j = 1; i < NUM_RTNOBJ_SHM_INDEX; i++) { rtnobj_shm_hdr = &shm_hdr->rtnobj_shmhdr[i]; if (INVALID_SHMID != rtnobj_shm_hdr->rtnobj_shmid) { nbytes = SNPRINTF(buff, SIZEOF(buff), "Rtnobj shared memory #%2.d : shmid: %u shmlen: 0x%llx" " shmused: 0x%llx shmfree: 0x%llx objlen: 0x%llx", j, rtnobj_shm_hdr->rtnobj_shmid, rtnobj_shm_hdr->shm_len, rtnobj_shm_hdr->used_len, rtnobj_shm_hdr->shm_len - rtnobj_shm_hdr->used_len, rtnobj_shm_hdr->real_len); DUMP_ONE_LINE(output, buff, nbytes); j++; } } for (linkrec = linkctl->rec_base, recnum = 1; recnum <= n_records; linkrec++, recnum++) { nbytes = SNPRINTF(buff, SIZEOF(buff), " rec#%d: rtnname: %.*s cycle: %d objhash: 0x%llx" " numvers: %d objlen: 0x%llx shmlen: 0x%llx", recnum, mid_len(&linkrec->rtnname_fixed), &linkrec->rtnname_fixed.c, linkrec->cycle, linkrec->objhash, linkrec->numvers, linkrec->objLen, linkrec->usedLen); DUMP_ONE_LINE(output, buff, nbytes); } } } #endif fis-gtm-V7.0-005/sr_unix/zshow_zcalls.c0000755000032200000250000000343414342376330016736 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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_string.h" #include #include #include "gtm_stdlib.h" #include "copy.h" #include "lv_val.h" /* needed for "fgncal.h" */ #include "fgncal.h" #include "gtmxc_types.h" #include "mvalconv.h" #include "util.h" #include "zshow.h" #define DO_ONE_ITEM(OUTPUT, BUFF, NBYTES) \ { \ mstr line; \ \ if (NBYTES >= SIZEOF(BUFF)) \ NBYTES = SIZEOF(BUFF); /* Output from SNPRINTF was truncated. */ \ line.len = NBYTES; \ line.addr = &BUFF[0]; \ OUTPUT->flush = TRUE; \ zshow_output(OUTPUT, &line); \ } void zshow_zcalls(zshow_out *output) { struct extcall_package_list *package_ptr; struct extcall_entry_list *entry_ptr; int nbytes, totalbytes; char buff[OUT_BUFF_SIZE]; int len; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Go through loaded packages */ for (package_ptr = TREF(extcall_package_root); package_ptr; package_ptr = package_ptr->next_package) { /* Go through the package's entry points */ for (entry_ptr = package_ptr->first_entry; entry_ptr; entry_ptr = entry_ptr->next_entry) { nbytes = SNPRINTF(buff, SIZEOF(buff), "%s.%s",package_ptr->package_name.addr, entry_ptr->entry_name.addr); DO_ONE_ITEM(output, buff, nbytes); } } return; } fis-gtm-V7.0-005/sr_unix/ztimeout_routines.c0000644000032200000250000002677014342376330020031 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018-2022 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 "io.h" #include "iosp.h" #include "iotimer.h" #include "stringpool.h" #include "op.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gtm_fcntl.h" /* for AIX's silly open to open64 translations */ #include "gdsfhead.h" #include "gdscc.h" #include "filestruct.h" #include "buddy_list.h" /* for tp.h */ #include "jnl.h" #include "tp.h" #include "send_msg.h" #include "gtmmsg.h" /* for gtm_putmsg() prototype */ #include "change_reg.h" #include "setterm.h" #include "getzposition.h" #include "min_max.h" #include "mvalconv.h" #include "have_crit.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "time.h" #include "gt_timer.h" #include "ztimeout_routines.h" #include "deferred_events.h" #include "error_trap.h" #include "indir_enum.h" #include "zwrite.h" #include "xfer_enum.h" #include "fix_xfer_entry.h" #include "gvname_info.h" #include "op_merge.h" #include "zshow.h" #include "gtm_signal.h" #include "deferred_events_queue.h" #include "wbox_test_init.h" #include "gtmio.h" #include "compiler.h" #include "gtm_common_defs.h" #include "gtm_time.h" #ifdef DEBUG_DEFERRED_EVENT #include "funsvn.h" #include "nametabtyp.h" #include "namelook.h" #endif GBLREF boolean_t gtm_white_box_test_case_enabled, ztrap_explicit_null; GBLREF dollar_ecode_type dollar_ecode; GBLREF int dollar_truth, gtm_white_box_test_case_number; GBLREF stack_frame *frame_pointer; GBLREF volatile boolean_t dollar_zininterrupt; GBLREF volatile int4 outofband; LITREF mval literal_minusone, literal_null; #ifdef DEBUG_DEFERRED_EVENT LITREF unsigned char svn_index[]; LITREF nametabent svn_names[]; LITREF svn_data_type svn_data[]; #endif STATICDEF mstr vector; error_def(ERR_ZTIMEOUT); #define ZTIMEOUT_TIMER_ID (TID)&check_and_set_ztimeout #define ZTIMEOUT_QUEUE_ID &ztimeout_set #define MAX_FORMAT_LEN 250 void check_and_set_ztimeout(mval *inp_val) { boolean_t only_timeout; char *colon_ptr, *local_str_end, *local_str_val; int read_len; int4 msec_timeout = -1; /* timeout in milliseconds; default to no change */ int4 rc; intrpt_state_t prev_intrpt_state; mval *zt_sec_ptr, ztimeout_seconds, ztimeout_vector; sigset_t savemask; ABS_TIME cur_time, end_time; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; MV_FORCE_STR(inp_val); MV_FORCE_NUMD(inp_val); read_len = inp_val->str.len; local_str_val = inp_val->str.addr; local_str_end = local_str_val + read_len; for (colon_ptr = local_str_val; (colon_ptr < local_str_end) && (':' != *colon_ptr); colon_ptr++) ; only_timeout = (colon_ptr >= local_str_end); ztimeout_vector = (TREF(dollar_ztimeout)).ztimeout_vector; if (!only_timeout && (colon_ptr >= local_str_val)) { /* vector change */ if (ztimeout_vector.str.len && ztimeout_vector.str.addr) memcpy(&(TREF(dollar_ztimeout)).ztimeout_vector, &literal_null, SIZEOF(mval)); if (local_str_end > colon_ptr) { /* there's a vector to process */ read_len = local_str_end - colon_ptr - 1; if ((read_len + 1 > vector.len) || (MAX_SRCLINE < vector.len)) { /* don't have room for the new vector, so make room */ assert((vector.addr == (TREF(dollar_ztimeout)).ztimeout_vector.str.addr) || (NULL == (TREF(dollar_ztimeout)).ztimeout_vector.str.addr)); if (vector.len) free(vector.addr); vector.addr = (char *)malloc(read_len + 1); vector.len = read_len; } memcpy(vector.addr, colon_ptr + 1, read_len); DEBUG_ONLY(vector.addr[read_len] = 0); /* actually only needed for dbg printfs */ ztimeout_vector.str.addr = read_len ? vector.addr : NULL; ztimeout_vector.str.len = read_len; ztimeout_vector.mvtype = MV_STR; } if (ztimeout_vector.str.len) { /* make sure the vector is valid code */ op_commarg(&ztimeout_vector, indir_linetail); op_unwind(); } else { ztimeout_vector.str.addr = NULL; ztimeout_vector.str.len = 0; } } (TREF(dollar_ztimeout)).ztimeout_vector = ztimeout_vector; if (colon_ptr > local_str_val) { /* some form of timeout specified */ if (0 > inp_val->m[1]) /* Negative timeout specified, cancel the timer */ { # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_ZTIM_EDGE)) { LONG_SLEEP(4); /* allow prior ztimeout timer to pop */ DBGFPF((stdout, "# white box sleep over\n")); } # endif assert(INTRPT_IN_EVENT_HANDLING != intrpt_ok_state); DEFER_INTERRUPTS(INTRPT_IN_EVENT_HANDLING, prev_intrpt_state); ztimeout_clear_timer(); ENABLE_EVENT_INTERRUPTS(prev_intrpt_state); TREF(ztimeout_timer_on) = FALSE; DBGDFRDEVNT((stderr, "%d %s: check_and_set_ztimeout - canceling ID : %lX\n", __LINE__, __FILE__ , ZTIMEOUT_TIMER_ID)); /* All negative values transformed to -1 */ memcpy(&((TREF(dollar_ztimeout)).ztimeout_seconds), &literal_minusone, SIZEOF(mval)); } else { ztimeout_seconds.str.addr = local_str_val; ztimeout_seconds.str.len = only_timeout ? read_len : colon_ptr - local_str_val; ztimeout_seconds.mvtype = MV_STR; (TREF(dollar_ztimeout)).ztimeout_seconds = ztimeout_seconds; zt_sec_ptr = &ztimeout_seconds; /* compile of below macro requires explicit ptr */ MV_FORCE_MSTIMEOUT(zt_sec_ptr, msec_timeout, ZTIMEOUTSTR); assert(INTRPT_IN_EVENT_HANDLING != intrpt_ok_state); DEFER_INTERRUPTS(INTRPT_IN_EVENT_HANDLING, prev_intrpt_state); ztimeout_clear_timer(); ENABLE_EVENT_INTERRUPTS(prev_intrpt_state); if (0 < msec_timeout) { /* otherwise, below start_timer expires in 0 time, meaning immediately */ sys_get_curr_time(&cur_time); add_int_to_abs_time(&cur_time, msec_timeout, &(TREF(dollar_ztimeout)).end_time); } DBGDFRDEVNT((stderr, "%d %s: check_and_set_ztimeout - started timeout: %d msec\n", __LINE__, __FILE__, msec_timeout)); TREF(ztimeout_timer_on) = TRUE; start_timer(ZTIMEOUT_TIMER_ID, msec_timeout, &ztimeout_expired, 0, NULL); (TREF(dollar_ztimeout)).ztimeout_seconds.m[1] = 0; /* flags get_ztimeout to calulate time remaining */ } } DBGDFRDEVNT((stderr, "%d %s: check_and_set_ztimeout - scanned vector: %s\n",__LINE__, __FILE__, (TREF(dollar_ztimeout)).ztimeout_vector.str.len ? (TREF(dollar_ztimeout)).ztimeout_vector.str.addr : "NULL")); } void ztimeout_expired(void) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DBGDFRDEVNT((stderr, "%d %s: ztimeout expired - setting xfer handlers\n", __LINE__, __FILE__)); # ifdef DEBUG if (gtm_white_box_test_case_enabled && ((WBTEST_ZTIMEOUT_TRACE == gtm_white_box_test_case_number) || (WBTEST_ZTIME_DEFER_CRIT == gtm_white_box_test_case_number) || (WBTEST_ZTIM_EDGE == gtm_white_box_test_case_number))) DBGFPF((stderr, "# ztimeout expired, white box case %d setting xfer handlers\n", gtm_white_box_test_case_number)); # endif xfer_set_handlers(ztimeout, 0, FALSE); } void ztimeout_set(int4 dummy_param) { /* attempts to redirect the transfer table to the timeout event at the next opportunity, when we have a current mpc */ intrpt_state_t prev_intrpt_state; # ifdef DEBUG_DEFERRED_EVENT int index; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(INTRPT_IN_EVENT_HANDLING == intrpt_ok_state); assert(ztimeout == outofband); if (dollar_zininterrupt || ((0 < dollar_ecode.index) && ETRAP_IN_EFFECT) || have_crit(CRIT_HAVE_ANY_REG | CRIT_IN_COMMIT) || (jobinterrupt == (TREF(save_xfer_root_ptr))->ev_que.fl->outofband)) { /* not a good time, so save it */ outofband = no_event; TAREF1(save_xfer_root, ztimeout).event_state = queued; SAVE_XFER_QUEUE_ENTRY(ztimeout, 0); DBGDFRDEVNT((stderr, "%d %s: ztimeout_set - ZTIMEOUT queued; dec_indx %d, et: %d intrpt: %d, crit: %d\n", __LINE__, __FILE__, ETRAP_IN_EFFECT, dollar_zininterrupt, have_crit(CRIT_HAVE_ANY_REG | CRIT_IN_COMMIT))); # ifdef DEBUG # ifdef DEBUG_DEFERRED_EVENT if (ETRAP_IN_EFFECT) { index = namelook(svn_index, svn_names, "ZSTATUS", SIZEOF("ZSTATUS")); op_zwritesvn(svn_data[index].opcode); } # endif if (gtm_white_box_test_case_enabled && ((WBTEST_ZTIMEOUT_TRACE == gtm_white_box_test_case_number) || (WBTEST_ZTIME_DEFER_CRIT == gtm_white_box_test_case_number))) DBGFPF((stderr, "# ztimeout_set : white box case %d ZTIMEOUT Deferred\n", gtm_white_box_test_case_number)); # endif return; } DBGDFRDEVNT((stderr, "%d %s: ztimeout_set - NOT deferred\n", __LINE__, __FILE__)); outofband = ztimeout; TAREF1(save_xfer_root, ztimeout).event_state = pending; DEFER_INTO_XFER_TAB; DBGDFRDEVNT((stderr, "%d %s: ztimeout_set - pending xfer entries for ztimeout\n", __LINE__, __FILE__)); # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_ZTIM_EDGE == gtm_white_box_test_case_number)) DBGFPF((stderr, "# ztimeout_set: white box case %d set the xfer entries for ztimeout\n", gtm_white_box_test_case_number)); # endif } void ztimeout_action(void) { /* Driven at recognition point of ztimeout by async_action) */ intrpt_state_t prev_intrpt_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(INTRPT_IN_EVENT_HANDLING != intrpt_ok_state); DEFER_INTERRUPTS(INTRPT_IN_EVENT_HANDLING, prev_intrpt_state); assert(ztimeout == outofband); assert(pending == TAREF1(save_xfer_root, ztimeout).event_state); DBGDFRDEVNT((stderr, "%d %s: ztimeout_action - driving the ztimeout vector\n", __LINE__, __FILE__)); DBGEHND((stderr, "ztimeout_action: Resetting frame 0x"lvaddr" mpc/context with restart_pc/ctxt 0x"lvaddr "/0x"lvaddr " - frame has type 0x%04lx\n", frame_pointer, frame_pointer->restart_pc, frame_pointer->restart_ctxt, frame_pointer->type)); ztimeout_clear_timer(); DBGDFRDEVNT((stderr, "%d %s: ztimeout_action - changing pending to event_state: %d\n", __LINE__, __FILE__, TAREF1(save_xfer_root, ztimeout).event_state)); frame_pointer->mpc = frame_pointer->restart_pc; frame_pointer->ctxt = frame_pointer->restart_ctxt; ENABLE_INTERRUPTS(INTRPT_IN_EVENT_HANDLING, prev_intrpt_state); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZTIMEOUT); } void ztimeout_clear_timer(void) { /* called by ztimeout_action just before transfer to actual ztimeout action */ save_xfer_entry *entry; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; SHOWTIME(asccurtime); DBGDFRDEVNT((stderr, "%d %s: ztimeout_clear_timer - clearing ztimeout @ %s while %sin use\n", __LINE__, __FILE__, asccurtime, TREF(ztimeout_timer_on) ? "": "*NOT* ")); assert(INTRPT_IN_EVENT_HANDLING == intrpt_ok_state); entry = &TAREF1(save_xfer_root, ztimeout); if (queued == entry->event_state) { REMOVE_XFER_QUEUE_ENTRY(ztimeout); entry->event_state = not_in_play; } if (pending == entry->event_state) { # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_ZTIM_EDGE == gtm_white_box_test_case_number)) DBGFPF((stderr, "# ztimeout_clear_timer - white box case %d resetting the xfer entries for ztimeout\n", gtm_white_box_test_case_number)); assert(ztimeout == outofband); # endif } entry->event_state = active; /* required by the routine invoked on the next line */ (void)xfer_reset_if_setter(ztimeout); if (TREF(ztimeout_timer_on)) { cancel_timer(ZTIMEOUT_TIMER_ID); TREF(ztimeout_timer_on) = FALSE; DBGDFRDEVNT((stderr, "%d %s: ztimeout_clear_timer - state: %d\n", __LINE__, __FILE__, entry->event_state)); } entry->event_state = not_in_play; } fis-gtm-V7.0-005/sr_unix/ztimeout_routines.h0000644000032200000250000000314314342376335020030 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018-2021 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 typedef struct { mval ztimeout_vector; mval ztimeout_seconds; ABS_TIME end_time; } dollar_ztimeout_struct; void check_and_set_ztimeout(mval *inp_val); void ztimeout_action(void); void ztimeout_expired(void); void ztimeout_process(void); void ztimeout_clear_timer(void); int get_ztimeout(mval *result); /* TODO: should this go back in (say) op_tcommit? */ #define CALL_ZTIMEOUT_IF_DEFERRED \ MBSTART { \ GBLREF boolean_t ztrap_explicit_null; \ GBLREF dollar_ecode_type dollar_ecode; \ GBLREF volatile boolean_t dollar_zininterrupt; \ \ int4 event_type, param_val; \ \ if (ztimeout == (TREF(save_xfer_root_ptr))->ev_que.fl->outofband) \ { /*If ztimeout , check conditions before popping out */ \ if (!dollar_zininterrupt && ((0 == dollar_ecode.index) || !(ETRAP_IN_EFFECT)) \ && (!have_crit(CRIT_HAVE_ANY_REG | CRIT_IN_COMMIT))) \ { \ POP_XFER_QUEUE_ENTRY(&event_type, ¶m_val); \ xfer_set_handlers(event_type, param_val, TRUE); \ } \ } \ } MBEND fis-gtm-V7.0-005/sr_unix/gtmci.c0000644000032200000250000012536514342376334015330 0ustar librarygtc /**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_stdio.h" #include #ifdef GTM_PTHREAD # include "gtm_pthread.h" #endif #include "gtm_stat.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_limits.h" #include "cli.h" #include "stringpool.h" #include #include "stack_frame.h" #include "mvalconv.h" #include "gtmxc_types.h" #include "lv_val.h" #include "fgncal.h" #include "gtmci.h" #include "error.h" #include "startup.h" #include "mv_stent.h" #include "op.h" #include "gtm_startup.h" #include "gtmsecshr.h" #include "job_addr.h" #include "invocation_mode.h" #include "gtmimagename.h" #include "gtm_exit_handler.h" #include "gtm_savetraps.h" #include "code_address_type.h" #include "push_lvval.h" #include "send_msg.h" #include "gtmmsg.h" #include "common_startup_init.h" #include "gtm_threadgbl_init.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 "gtm_trigger.h" #endif #ifdef UTF8_SUPPORTED # include "gtm_icu_api.h" # include "gtm_utf8.h" # include "gtm_conv.h" GBLREF u_casemap_t gtm_strToTitle_ptr; /* Function pointer for gtm_strToTitle */ #endif #include "hashtab.h" #include "hashtab_str.h" #include "compiler.h" #include "have_crit.h" #include "callg.h" #include "min_max.h" #include "filestruct.h" #include "gdscc.h" #include "jnl.h" #include "gdskill.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "gtm_permissions.h" #include "gtm_post_startup_check_init.h" #if defined(__x86_64__) extern void opp_ciret(); #endif GBLREF parmblk_struct *param_list; GBLREF stack_frame *frame_pointer; GBLREF unsigned char *msp; GBLREF mv_stent *mv_chain; GBLREF int mumps_status; GBLREF void (*restart)(); GBLREF boolean_t gtm_startup_active; GBLREF volatile int *var_on_cstack_ptr; /* volatile so that nothing gets optimized out */ GBLREF rhdtyp *ci_base_addr; GBLREF mval dollar_zstatus; GBLREF unsigned char *fgncal_stack; GBLREF uint4 dollar_tlevel; GBLREF int process_exiting; #ifdef GTM_PTHREAD GBLREF boolean_t gtm_jvm_process; GBLREF pthread_t gtm_main_thread_id; GBLREF boolean_t gtm_main_thread_id_set; #endif GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF unsigned int gtm_dist_len; GBLREF boolean_t gtm_dist_ok_to_use; GTMTRIG_DBG_ONLY(GBLREF ch_ret_type (*ch_at_trigger_init)();) LITREF gtmImageName gtmImageNames[]; error_def(ERR_ACTLSTTOOLONG); error_def(ERR_CALLINAFTERXIT); error_def(ERR_CIMAXLEVELS); error_def(ERR_CINOENTRY); error_def(ERR_CIRCALLNAME); error_def(ERR_CITPNESTED); error_def(ERR_DISTPATHMAX); error_def(ERR_GTMDISTUNDEF); error_def(ERR_GTMSECSHRPERM); error_def(ERR_INVGTMEXIT); error_def(ERR_JOBLABOFF); error_def(ERR_MAXACTARG); error_def(ERR_MAXSTRLEN); error_def(ERR_SYSCALL); #define REVERT_AND_RETURN \ { \ REVERT; /* gtmci_ch */ \ return 0; \ } /* Unwind the M stack back to where the stack pointer (msp) was last saved */ #define FGNCAL_UNWIND \ { \ if (msp < fgncal_stack) \ fgncal_unwind(); \ } /* When passing arguments from Java, ensure that the expected types match the actual ones. If not, * use the arg_types array to pass back the information needed for a detailed error message. */ #define CHECK_FOR_TYPE_MISMATCH(INDEX, EXP_TYPE, ACT_TYPE) \ { \ if (EXP_TYPE != ACT_TYPE) \ { \ arg_types[3] = ACT_TYPE; \ arg_types[2] = EXP_TYPE; \ arg_types[1] = INDEX; \ arg_types[0] = -1; \ REVERT_AND_RETURN; \ } \ } /* When passing arguments from Java, ensure that the either of the expected types matches the actual one. * If not, use the arg_types array to pass back the information needed for a detailed error message. */ #define CHECK_FOR_TYPES_MISMATCH(INDEX, EXP_TYPE1, EXP_TYPE2, ACT_TYPE) \ { \ if ((EXP_TYPE1 != ACT_TYPE) && (EXP_TYPE2 != ACT_TYPE)) \ { \ arg_types[4] = ACT_TYPE; \ arg_types[3] = EXP_TYPE1; \ arg_types[2] = EXP_TYPE2; \ arg_types[1] = INDEX; \ arg_types[0] = -1; \ REVERT_AND_RETURN; \ } \ } /* When returning a typed value, ensure that the declared type matches the expected one. If not, * use the arg_types array to pass back the information needed for a detailed error message. */ #define CHECK_FOR_RET_TYPE_MISMATCH(INDEX, EXP_TYPE, ACT_TYPE) \ { \ if ((0 == INDEX) && (EXP_TYPE != ACT_TYPE)) \ { \ arg_types[3] = ACT_TYPE; \ arg_types[2] = EXP_TYPE; \ arg_types[1] = 0; \ arg_types[0] = -1; \ REVERT_AND_RETURN; \ } \ } /* Call-ins uses the fgncal_stack global as a marker in the stack for where to unwind the stack back to. This preserves * the call-in base frame(s) but removes any other frames left on the stack as well as the parameter related mv_stents * and any other mv_stents no longer needed. This macro saves the current value of fgncal_stack on the M stack in an * MVST_STCK_SP type mv_stent. Note MVST_STCK_SP is chosen (instead of MVST_STCK) because MVST_STCK_SP doesn't get removed * if the frame is rewritten by a ZGOTO for instance. * * Note: If call-in's use of setjmp/longjmp for returns is changed to use the trigger method of actually unwinding the M * frames and returning "normally", then the usage of fgncal_stack likely becomes superfluous so should be looked at. */ # define SAVE_FGNCAL_STACK \ { \ if (msp != fgncal_stack) \ { \ push_stck(fgncal_stack, 0, (void **)&fgncal_stack, MVST_STCK_SP); \ fgncal_stack = msp; \ } \ } static callin_entry_list* get_entry(const char* call_name, int internal) { /* Lookup in a hashtable for entry corresponding to routine name */ ht_ent_str *callin_entry; stringkey symkey; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; symkey.str.addr = (char *)call_name; symkey.str.len = STRLEN(call_name); COMPUTE_HASH_STR(&symkey); if (!internal) callin_entry = lookup_hashtab_str(TREF(callin_hashtab), &symkey); else callin_entry = lookup_hashtab_str(TREF(ci_filter_hashtab), &symkey); return (callin_entry ? callin_entry->value : NULL); } /* Java-specific version of call-in handler. */ int gtm_cij(const char *c_rtn_name, char **arg_blob, int count, int *arg_types, unsigned int *io_vars_mask, unsigned int *has_ret_value) { callin_entry_list *entry; mstr label, routine; int has_return, i, len; rhdtyp *base_addr; uint4 inp_mask, out_mask, mask; mval arg_mval, *arg_ptr; enum gtm_types arg_type; gtm_string_t *mstr_parm; parmblk_struct param_blk; void op_extcall(), op_extexfun(), flush_pio(void); volatile int *save_var_on_cstack_ptr; /* Volatile to match global var type */ int status; boolean_t added; stringkey symkey; ht_ent_str *syment; intrpt_state_t old_intrpt_state; char **arg_blob_ptr; int *java_arg_type; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; added = FALSE; /* A prior invocation of gtm_exit would have set process_exiting = TRUE. Use this to disallow gtm_ci to be * invoked after a gtm_exit */ if (process_exiting) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CALLINAFTERXIT); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CALLINAFTERXIT); return ERR_CALLINAFTERXIT; } if (!gtm_startup_active || !(frame_pointer->flags & SFF_CI)) { if ((status = gtm_init()) != 0) /* Note - sets fgncal_stack */ return status; } GTM_PTHREAD_ONLY(assert(gtm_main_thread_id_set && pthread_equal(gtm_main_thread_id, pthread_self()))); assert(NULL == TREF(temp_fgncal_stack)); FGNCAL_UNWIND; /* note - this is outside the establish since gtmci_ch calso calls fgncal_unwind() which, * if this failed, would lead to a nested error which we'd like to avoid */ ESTABLISH_RET(gtmci_ch, mumps_status); if (!c_rtn_name) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CIRCALLNAME); if (!TREF(ci_table)) /* Load the call-in table only once from env variable GTMCI. */ { TREF(ci_table) = citab_parse(FALSE); if (!TREF(callin_hashtab)) { TREF(callin_hashtab) = (hash_table_str *)malloc(SIZEOF(hash_table_str)); (TREF(callin_hashtab))->base = NULL; /* Need to initialize hash table. */ init_hashtab_str(TREF(callin_hashtab), CALLIN_HASHTAB_SIZE, HASHTAB_NO_COMPACT, HASHTAB_NO_SPARE_TABLE); assert((TREF(callin_hashtab))->base); } for (entry = TREF(ci_table); NULL != entry; entry = entry->next_entry) { /* Loop over the list and populate the hash table. */ symkey.str.addr = entry->call_name.addr; symkey.str.len = entry->call_name.len; COMPUTE_HASH_STR(&symkey); added = add_hashtab_str(TREF(callin_hashtab), &symkey, entry, &syment); assert(added); assert(syment->value == entry); } } if (!(entry = get_entry(c_rtn_name, FALSE))) /* c_rtn_name not found in the table. */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_CINOENTRY, 2, LEN_AND_STR(c_rtn_name)); lref_parse((unsigned char*)entry->label_ref.addr, &routine, &label, &i); /* The 3rd argument is NULL because we will get lnr_adr via TABENT_PROXY. */ if (!job_addr(&routine, &label, 0, (char **)&base_addr, NULL)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_JOBLABOFF); memset(¶m_blk, 0, SIZEOF(param_blk)); param_blk.rtnaddr = (void *)(ARLINK_ONLY(0) NON_ARLINK_ONLY(base_addr)); /* lnr_entry below is a pointer to the code offset for this label from the * beginning of text base(on USHBIN platforms) or from the beginning of routine * header (on NON_USHBIN platforms). * On NON_USHBIN platforms -- 2nd argument to EXTCALL is this pointer * On USHBIN -- 2nd argument to EXTCALL is the pointer to this pointer (&lnr_entry) */ /* Assign the address for line number entry storage, so that the adjacent address holds has_parms value. */ param_blk.labaddr = (void *)(ARLINK_ONLY(-1) NON_ARLINK_ONLY(&(TABENT_PROXY).LABENT_LNR_OFFSET)); if (MAX_ACTUALS < entry->argcnt) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXACTARG); if (entry->argcnt < count) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ACTLSTTOOLONG, 2, (int)label.len, label.addr); param_blk.argcnt = count; has_return = (gtm_void != entry->return_type); if (has_return) { /* Create mval slot for return value */ MV_INIT(&arg_mval); param_blk.retaddr = (void *)push_lvval(&arg_mval); arg_blob_ptr = &arg_blob[0] + GTM64_ONLY(1) NON_GTM64_ONLY(2); java_arg_type = arg_types + 1; } else { param_blk.retaddr = 0; arg_blob_ptr = &arg_blob[0]; java_arg_type = arg_types; } inp_mask = entry->input_mask; out_mask = entry->output_mask; *io_vars_mask = out_mask; if (*has_ret_value != has_return) { *has_ret_value = has_return; REVERT_AND_RETURN; } *has_ret_value = has_return; for (i = 0, mask = inp_mask; i < count; ++i, mask >>= 1, java_arg_type++, arg_blob_ptr += GTM64_ONLY(1) NON_GTM64_ONLY(2)) { /* Copy the arguments' values into mval containers. Since some arguments might be declared as output-only, * we need to go over all of them unconditionally, but only do the copying for the ones that are used for * the input direction (I or IO). The integer values passed to CHECK_FOR_TYPE_MISMATCH as a second argument * indicate the types to expect according to the call-in table definition, and are in correspondence with the * constants declared in GTMContainerType class in gtmji.jar: 0 for GTMBoolean, 1 for GTMInteger, and so on. */ arg_mval.mvtype = MV_XZERO; switch (entry->parms[i]) { case gtm_jboolean: CHECK_FOR_TYPE_MISMATCH(i + 1, 0, *java_arg_type); if (MASK_BIT_ON(mask)) i2mval(&arg_mval, *(int *)arg_blob_ptr); break; case gtm_jint: CHECK_FOR_TYPE_MISMATCH(i + 1, 1, *java_arg_type); if (MASK_BIT_ON(mask)) i2mval(&arg_mval, *(int *)arg_blob_ptr); break; case gtm_jlong: CHECK_FOR_TYPE_MISMATCH(i + 1, 2, *java_arg_type); if (MASK_BIT_ON(mask)) i82mval(&arg_mval, *(gtm_int64_t *)arg_blob_ptr); break; case gtm_jfloat: CHECK_FOR_TYPE_MISMATCH(i + 1, 3, *java_arg_type); if (MASK_BIT_ON(mask)) float2mval(&arg_mval, *(float *)arg_blob_ptr); break; case gtm_jdouble: CHECK_FOR_TYPE_MISMATCH(i + 1, 4, *java_arg_type); if (MASK_BIT_ON(mask)) double2mval(&arg_mval, *(double *)arg_blob_ptr); break; case gtm_jstring: CHECK_FOR_TYPES_MISMATCH(i + 1, 7, 5, *java_arg_type); if (MASK_BIT_ON(mask)) { mstr_parm = *(gtm_string_t **)arg_blob_ptr; arg_mval.mvtype = MV_STR; if (MAX_STRLEN < (uint4)mstr_parm->length) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXSTRLEN); arg_mval.str.len = (mstr_len_t)mstr_parm->length; arg_mval.str.addr = mstr_parm->address; s2pool(&arg_mval.str); } break; case gtm_jbyte_array: CHECK_FOR_TYPES_MISMATCH(i + 1, 8, 6, *java_arg_type); if (MASK_BIT_ON(mask)) { mstr_parm = *(gtm_string_t **)arg_blob_ptr; arg_mval.mvtype = MV_STR; if (MAX_STRLEN < (uint4)mstr_parm->length) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXSTRLEN); arg_mval.str.len = (mstr_len_t)mstr_parm->length; arg_mval.str.addr = mstr_parm->address; s2pool(&arg_mval.str); } break; case gtm_jbig_decimal: CHECK_FOR_TYPE_MISMATCH(i + 1, 9, *java_arg_type); if (MASK_BIT_ON(mask)) { mstr_parm = *(gtm_string_t **)arg_blob_ptr; arg_mval.mvtype = MV_STR; if (MAX_STRLEN < (uint4)mstr_parm->length) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXSTRLEN); arg_mval.str.len = (mstr_len_t)mstr_parm->length; arg_mval.str.addr = mstr_parm->address; s2pool(&arg_mval.str); } break; default: /* Indicate an invalid type. */ arg_types[1] = i + 1; arg_types[0] = -2; REVERT_AND_RETURN; } param_blk.args[i] = push_lvval(&arg_mval); } param_blk.mask = out_mask; param_blk.ci_rtn = (!has_return && param_blk.argcnt <= 0) ? (void (*)())CODE_ADDRESS_TYPE(op_extcall) : (void (*)())CODE_ADDRESS_TYPE(op_extexfun); /* The params block needs to be saved and restored across multiple GT.M environments. So, instead of storing it * explicitely, setting the global param_list to point to local param_blk will do the job. */ param_list = ¶m_blk; old_intrpt_state = intrpt_ok_state; intrpt_ok_state = INTRPT_OK_TO_INTERRUPT; /* Reset interrupt state for the new M session. */ save_var_on_cstack_ptr = var_on_cstack_ptr; var_on_cstack_ptr = NULL; /* Reset var_on_cstack_ptr for the new M environment. */ assert(frame_pointer->flags & SFF_CI); frame_pointer->mpc = frame_pointer->ctxt = PTEXT_ADR(frame_pointer->rvector); REVERT; /* Revert gtmci_ch. */ /* */ /* Drive the call_in routine */ /* */ ESTABLISH_RET(stop_image_conditional_core, mumps_status); dm_start(); /* Kick off execution */ REVERT; param_list = NULL; /* 4SCA: Stack address stored in global */ /* */ /* Return value processing */ /* */ intrpt_ok_state = old_intrpt_state; /* Restore the old interrupt state. */ var_on_cstack_ptr = save_var_on_cstack_ptr; /* Restore the old environment's var_on_cstack_ptr. */ if (1 != mumps_status) { /* dm_start() initializes mumps_status to 1 before execution. If mumps_status is not 1, * it is either the unhandled error code propaged by $ZT/$ET (from mdb_condition_handler) * or zero on returning from ZGOTO 0 (ci_ret_code_quit). */ return mumps_status; } ESTABLISH_RET(gtmci_ch, mumps_status); /* Convert mval args designated for output-only or input-output use to C types. */ arg_blob_ptr = &arg_blob[0]; for (i = 0; i <= count; ++i, arg_blob_ptr += GTM64_ONLY(1) NON_GTM64_ONLY(2)) { if (0 == i) /* Special case for return value. */ { if (!has_return) { arg_blob_ptr -= GTM64_ONLY(1) NON_GTM64_ONLY(2); continue; } arg_ptr = &((lv_val *)(param_blk.retaddr))->v; mask = 1; arg_type = entry->return_type; } else { arg_ptr = ¶m_blk.args[i - 1]->v; mask = out_mask; arg_type = entry->parms[i - 1]; out_mask >>= 1; } /* Do not process parameters that are either input-only(I) or output(O/IO) * parameters that are not modified by the M routine. */ if (MV_ON(mask, arg_ptr)) { /* Process all output (O/IO) parameters modified by the M routine */ switch (arg_type) { case gtm_jboolean: CHECK_FOR_RET_TYPE_MISMATCH(i, 0, *arg_types); *(gtm_int_t *)arg_blob_ptr = mval2double(arg_ptr) ? 1 : 0; break; case gtm_jint: CHECK_FOR_RET_TYPE_MISMATCH(i, 1, *arg_types); *(gtm_int_t *)arg_blob_ptr = mval2i(arg_ptr); break; case gtm_jlong: CHECK_FOR_RET_TYPE_MISMATCH(i, 2, *arg_types); *(gtm_int64_t *)arg_blob_ptr = mval2i8(arg_ptr); break; case gtm_jfloat: CHECK_FOR_RET_TYPE_MISMATCH(i, 3, *arg_types); *(gtm_float_t *)arg_blob_ptr = mval2double(arg_ptr); break; case gtm_jdouble: CHECK_FOR_RET_TYPE_MISMATCH(i, 4, *arg_types); *(gtm_double_t *)arg_blob_ptr = mval2double(arg_ptr); break; case gtm_jstring: CHECK_FOR_RET_TYPE_MISMATCH(i, 7, *arg_types); MV_FORCE_STR(arg_ptr); /* Since the ci_gateway.c code temporarily switches the character following the string's * content in memory to '\0' (for generation of a proper UTF string), ensure that the * whole string resides in the stringpool, and that we do have that one byte to play with. */ if (!IS_IN_STRINGPOOL(arg_ptr->str.addr, arg_ptr->str.len)) s2pool(&arg_ptr->str); ENSURE_STP_FREE_SPACE(1); (*(gtm_string_t **)arg_blob_ptr)->address = arg_ptr->str.addr; (*(gtm_string_t **)arg_blob_ptr)->length = arg_ptr->str.len; break; case gtm_jbyte_array: CHECK_FOR_RET_TYPE_MISMATCH(i, 8, *arg_types); MV_FORCE_STR(arg_ptr); (*(gtm_string_t **)arg_blob_ptr)->address = arg_ptr->str.addr; (*(gtm_string_t **)arg_blob_ptr)->length = arg_ptr->str.len; break; case gtm_jbig_decimal: /* We currently do not support output for big decimal. */ break; default: assertpro((arg_type >= gtm_jboolean) && (arg_type <= gtm_jbig_decimal)); } } } REVERT; assert(NULL == TREF(temp_fgncal_stack)); FGNCAL_UNWIND; return 0; } /* Common work-routine for gtm_ci() and gtm_cip() to drive callin */ int gtm_ci_exec(const char *c_rtn_name, void *callin_handle, int populate_handle, va_list temp_var, boolean_t internal_use) { va_list var; callin_entry_list *entry; mstr label, routine; int has_return, i; rhdtyp *base_addr; uint4 inp_mask, out_mask, mask; mval arg_mval, *arg_ptr; enum gtm_types arg_type; gtm_string_t *mstr_parm; char *gtm_char_ptr; parmblk_struct param_blk; void op_extcall(), op_extexfun(), flush_pio(void); volatile int *save_var_on_cstack_ptr; /* Volatile to match global var type */ int status; boolean_t added; stringkey symkey; ht_ent_str *syment; intrpt_state_t old_intrpt_state; boolean_t unwind_here = FALSE; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; VAR_COPY(var, temp_var); added = FALSE; /* A prior invocation of gtm_exit would have set process_exiting = TRUE. Use this to disallow gtm_ci to be * invoked after a gtm_exit */ if (process_exiting) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CALLINAFTERXIT); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CALLINAFTERXIT); return ERR_CALLINAFTERXIT; } if (internal_use) { TREF(comm_filter_init) = TRUE; if (!(frame_pointer->flags & SFF_CI)) unwind_here = TRUE; } if (!gtm_startup_active || !(frame_pointer->flags & SFF_CI)) { if ((status = gtm_init()) != 0) /* Note - sets fgncal_stack */ return status; if (NULL == lcl_gtm_threadgbl) /*This will happen when user did not call gtm_init() earlier*/ SETUP_THREADGBL_ACCESS; } assert(NULL == TREF(temp_fgncal_stack)); FGNCAL_UNWIND; /* note - this is outside the establish since gtmci_ch calso calls fgncal_unwind() which, * if this failed, would lead to a nested error which we'd like to avoid */ ESTABLISH_RET(gtmci_ch, mumps_status); if (!c_rtn_name) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CIRCALLNAME); if (!internal_use) { if (!TREF(ci_table)) /* load the call-in table only once from env variable GTMCI */ { TREF(ci_table) = citab_parse(internal_use); if (!TREF(callin_hashtab)) { TREF(callin_hashtab) = (hash_table_str *)malloc(SIZEOF(hash_table_str)); (TREF(callin_hashtab))->base = NULL; /* Need to initialize hash table */ init_hashtab_str(TREF(callin_hashtab), CALLIN_HASHTAB_SIZE, HASHTAB_NO_COMPACT, HASHTAB_NO_SPARE_TABLE); assert((TREF(callin_hashtab))->base); } for (entry = TREF(ci_table); NULL != entry; entry = entry->next_entry) { /* Loop over the list and populate the hash table */ symkey.str.addr = entry->call_name.addr; symkey.str.len = entry->call_name.len; COMPUTE_HASH_STR(&symkey); added = add_hashtab_str(TREF(callin_hashtab), &symkey, entry, &syment); assert(added); assert(syment->value == entry); } } } else /* Call from the filter command*/ { if (!TREF(ci_filter_table)) { TREF(ci_filter_table) = citab_parse(internal_use); if (!TREF(ci_filter_hashtab)) { TREF(ci_filter_hashtab) = (hash_table_str *)malloc(SIZEOF(hash_table_str)); (TREF(ci_filter_hashtab))->base = NULL; /* Need to initialize hash table */ init_hashtab_str(TREF(ci_filter_hashtab), CALLIN_HASHTAB_SIZE, HASHTAB_NO_COMPACT, HASHTAB_NO_SPARE_TABLE); assert((TREF(ci_filter_hashtab))->base); } for (entry = TREF(ci_filter_table); NULL != entry; entry = entry->next_entry) { /* Loop over the list and populate the hash table */ symkey.str.addr = entry->call_name.addr; symkey.str.len = entry->call_name.len; COMPUTE_HASH_STR(&symkey); added = add_hashtab_str(TREF(ci_filter_hashtab), &symkey, entry, &syment); assert(added); assert(syment->value == entry); } } } if (NULL == callin_handle) { if (!(entry = get_entry(c_rtn_name, internal_use))) /* c_rtn_name not found in the table */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_CINOENTRY, 2, LEN_AND_STR(c_rtn_name)); if (populate_handle) callin_handle = entry; } else entry = callin_handle; lref_parse((unsigned char*)entry->label_ref.addr, &routine, &label, &i); /* 3rd argument is NULL because we will get lnr_adr via TABENT_PROXY */ if (!job_addr(&routine, &label, 0, (char **)&base_addr, NULL)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_JOBLABOFF); memset(¶m_blk, 0, SIZEOF(param_blk)); param_blk.rtnaddr = (void *)(ARLINK_ONLY(0) NON_ARLINK_ONLY(base_addr)); /* lnr_entry below is a pointer to the code offset for this label from the * beginning of text base(on USHBIN platforms) or from the beginning of routine * header (on NON_USHBIN platforms). * On NON_USHBIN platforms -- 2nd argument to EXTCALL is this pointer * On USHBIN -- 2nd argument to EXTCALL is the pointer to this pointer (&lnr_entry) * * Assign the address for line number entry storage, so that the adjacent address holds has_parms value. */ param_blk.labaddr = (void *)(ARLINK_ONLY(-1) NON_ARLINK_ONLY(&(TABENT_PROXY).LABENT_LNR_OFFSET)); param_blk.argcnt = entry->argcnt; if (MAX_ACTUALS < param_blk.argcnt) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_MAXACTARG); has_return = (gtm_void == entry->return_type) ? 0 : 1; if (has_return) { /* Create mval slot for return value */ MV_INIT(&arg_mval); param_blk.retaddr = (void *)push_lvval(&arg_mval); va_arg(var, void *); /* advance va_arg */ } else param_blk.retaddr = NULL; inp_mask = entry->input_mask; out_mask = entry->output_mask; for (i = 0, mask = ~inp_mask; i < entry->argcnt; ++i, mask >>= 1) { /* Copy pass-by-value arguments - since only first MAX_ACTUALS could be O/IO, * any additional params will be treated as Input-only (I). * inp_mask is inversed to achieve this. */ arg_mval.mvtype = MV_XZERO; if (MASK_BIT_ON(mask)) { /* Output-only(O) params : advance va_arg pointer */ switch (entry->parms[i]) { case gtm_int: va_arg(var, gtm_int_t); break; case gtm_uint: va_arg(var, gtm_uint_t); break; case gtm_long: va_arg(var, gtm_long_t); break; case gtm_ulong: va_arg(var, gtm_ulong_t); break; case gtm_int_star: va_arg(var, gtm_int_t *); break; case gtm_uint_star: va_arg(var, gtm_uint_t *); break; case gtm_long_star: va_arg(var, gtm_long_t *); break; case gtm_ulong_star: va_arg(var, gtm_ulong_t *); break; case gtm_float: case gtm_double: va_arg(var, gtm_double_t); break; case gtm_float_star: va_arg(var, gtm_float_t *); break; case gtm_double_star: va_arg(var, gtm_double_t *); break; case gtm_char_star: va_arg(var, gtm_char_t *); break; case gtm_string_star: va_arg(var, gtm_string_t *); break; default: va_end(var); assertpro(FALSE); } } else { /* I/IO params: create mval for each native type param */ switch (entry->parms[i]) { case gtm_int: i2mval(&arg_mval, va_arg(var, gtm_int_t)); break; case gtm_uint: i2usmval(&arg_mval, va_arg(var, gtm_uint_t)); break; case gtm_long: # ifdef GTM64 i82mval(&arg_mval, (gtm_int64_t)va_arg(var, gtm_long_t)); # else i2mval(&arg_mval, (int)va_arg(var, gtm_long_t)); # endif break; case gtm_ulong: # ifdef GTM64 ui82mval(&arg_mval, (gtm_uint64_t)va_arg(var, gtm_ulong_t)); # else i2usmval(&arg_mval, (int)va_arg(var, gtm_ulong_t)); # endif break; case gtm_int_star: i2mval(&arg_mval, *va_arg(var, gtm_int_t *)); break; case gtm_uint_star: i2usmval(&arg_mval, *va_arg(var, gtm_uint_t *)); break; case gtm_long_star: # ifdef GTM64 i82mval(&arg_mval, (gtm_int64_t)*va_arg(var, gtm_long_t *)); # else i2mval(&arg_mval, (int)*va_arg(var, gtm_long_t *)); # endif break; case gtm_ulong_star: # ifdef GTM64 ui82mval(&arg_mval, (gtm_uint64_t)*va_arg(var, gtm_ulong_t *)); # else i2usmval(&arg_mval, (int)*va_arg(var, gtm_ulong_t *)); # endif break; case gtm_float: float2mval(&arg_mval, (gtm_float_t)va_arg(var, gtm_double_t)); break; case gtm_double: double2mval(&arg_mval, va_arg(var, gtm_double_t)); break; case gtm_float_star: float2mval(&arg_mval, *va_arg(var, gtm_float_t *)); break; case gtm_double_star: double2mval(&arg_mval, *va_arg(var, gtm_double_t *)); break; case gtm_char_star: arg_mval.mvtype = MV_STR; arg_mval.str.addr = va_arg(var, gtm_char_t *); arg_mval.str.len = STRLEN(arg_mval.str.addr); if (MAX_STRLEN < arg_mval.str.len) { va_end(var); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXSTRLEN); } s2pool(&arg_mval.str); break; case gtm_string_star: mstr_parm = va_arg(var, gtm_string_t *); arg_mval.mvtype = MV_STR; if (MAX_STRLEN < (uint4)mstr_parm->length) { va_end(var); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXSTRLEN); } arg_mval.str.len = (mstr_len_t)mstr_parm->length; arg_mval.str.addr = mstr_parm->address; s2pool(&arg_mval.str); break; default: va_end(var); assertpro(FALSE); /* should have been caught by citab_parse */ } } param_blk.args[i] = push_lvval(&arg_mval); } va_end(var); param_blk.mask = out_mask; param_blk.ci_rtn = (!has_return && param_blk.argcnt <= 0) ? (void (*)())CODE_ADDRESS_TYPE(op_extcall) : (void (*)())CODE_ADDRESS_TYPE(op_extexfun); /* The params block needs to be stored & restored across multiple * gtm environments. So instead of storing explicitely, setting the * global param_list to point to local param_blk will do the job. */ param_list = ¶m_blk; old_intrpt_state = intrpt_ok_state; intrpt_ok_state = INTRPT_OK_TO_INTERRUPT; /* reset interrupt state for the new M session */ save_var_on_cstack_ptr = var_on_cstack_ptr; var_on_cstack_ptr = NULL; /* reset var_on_cstack_ptr for the new M environment */ assert(frame_pointer->flags & SFF_CI); frame_pointer->mpc = frame_pointer->ctxt = PTEXT_ADR(frame_pointer->rvector); REVERT; /* gtmci_ch */ /* */ /* Drive the call_in routine */ /* */ ESTABLISH_RET(stop_image_conditional_core, mumps_status); dm_start(); /* Kick off execution */ REVERT; param_list = NULL; /* 4SCA: Stack address stored in global */ /* */ /* Return value processing */ /* */ intrpt_ok_state = old_intrpt_state; /* restore the old interrupt state */ var_on_cstack_ptr = save_var_on_cstack_ptr; /* restore the old environment's var_on_cstack_ptr */ if (1 != mumps_status) { /* dm_start() initializes mumps_status to 1 before execution. If mumps_status is not 1, * it is either the unhandled error code propaged by $ZT/$ET (from mdb_condition_handler) * or zero on returning from ZGOTO 0 (ci_ret_code_quit). */ if (internal_use) TREF(comm_filter_init) = FALSE; /*exiting from filters*/ if (ERR_TPRETRY == mumps_status) { ci_ret_code_quit(); /*Unwind the whole CI frame and frame_pointer points to the caller now*/ DRIVECH(mumps_status); } return mumps_status; } ESTABLISH_RET(gtmci_ch, mumps_status); /* Convert mval args passed by reference to C types */ for (i = 0; i <= entry->argcnt; ++i) { if (0 == i) /* Special case for return value */ { if (!has_return) continue; arg_ptr = &((lv_val *)(param_blk.retaddr))->v; mask = 1; arg_type = entry->return_type; } else { arg_ptr = ¶m_blk.args[i - 1]->v; mask = out_mask; arg_type = entry->parms[i - 1]; out_mask >>= 1; } /* Do not process parameters that are either input-only(I) or output(O/IO) * parameters that are not modified by the M routine. */ if (MV_ON(mask, arg_ptr)) { /* Process all output (O/IO) parameters modified by the M routine */ switch (arg_type) { case gtm_int_star: *va_arg(temp_var, gtm_int_t *) = mval2i(arg_ptr); break; case gtm_uint_star: *va_arg(temp_var, gtm_uint_t *) = mval2ui(arg_ptr); break; case gtm_long_star: *va_arg(temp_var, gtm_long_t *) = GTM64_ONLY(mval2i8(arg_ptr)) NON_GTM64_ONLY(mval2i(arg_ptr)); break; case gtm_ulong_star: *va_arg(temp_var, gtm_ulong_t *) = GTM64_ONLY(mval2ui8(arg_ptr)) NON_GTM64_ONLY(mval2ui(arg_ptr)); break; case gtm_float_star: *va_arg(temp_var, gtm_float_t *) = mval2double(arg_ptr); break; case gtm_double_star: *va_arg(temp_var, gtm_double_t *) = mval2double(arg_ptr); break; case gtm_char_star: gtm_char_ptr = va_arg(temp_var, gtm_char_t *); MV_FORCE_STR(arg_ptr); memcpy(gtm_char_ptr, arg_ptr->str.addr, arg_ptr->str.len); gtm_char_ptr[arg_ptr->str.len] = 0; /* trailing null */ break; case gtm_string_star: mstr_parm = va_arg(temp_var, gtm_string_t *); MV_FORCE_STR(arg_ptr); if (mstr_parm->length > arg_ptr->str.len) mstr_parm->length = arg_ptr->str.len; memcpy(mstr_parm->address, arg_ptr->str.addr, mstr_parm->length); break; default: va_end(temp_var); assertpro(FALSE); } } else { switch (arg_type) { case gtm_int_star: va_arg(temp_var, gtm_int_t *); break; case gtm_uint_star: va_arg(temp_var, gtm_uint_t *); break; case gtm_long_star: va_arg(temp_var, gtm_long_t *); break; case gtm_ulong_star: va_arg(temp_var, gtm_ulong_t *); break; case gtm_float_star: va_arg(temp_var, gtm_float_t *); break; case gtm_double_star: va_arg(temp_var, gtm_double_t *); break; case gtm_char_star: va_arg(temp_var, gtm_char_t *); break; case gtm_string_star: va_arg(temp_var, gtm_string_t *); break; case gtm_int: va_arg(temp_var, gtm_int_t); break; case gtm_uint: va_arg(temp_var, gtm_uint_t); break; case gtm_long: va_arg(temp_var, gtm_long_t); break; case gtm_ulong: va_arg(temp_var, gtm_ulong_t); break; case gtm_float: case gtm_double: va_arg(temp_var, gtm_double_t); break; default: va_end(temp_var); assertpro(FALSE); } } } va_end(temp_var); REVERT; assert(NULL == TREF(temp_fgncal_stack)); FGNCAL_UNWIND; /* Added below, since GTMCI only unwinds back to current CI frame, * to unwind the two frames (current + base) * in normal scenarios, gtm_exit is called */ if (internal_use) { TREF(comm_filter_init) = FALSE; /* Exiting from filters */ if (unwind_here) ci_ret_code_quit(); } return 0; } /* Initial call-in driver version - does name lookup on each call */ int gtm_ci(const char *c_rtn_name, ...) { va_list var; VAR_START(var, c_rtn_name); return gtm_ci_exec(c_rtn_name, NULL, FALSE, var, FALSE); } /* Fast path call-in driver version - Adds a struct parm that contains name resolution info after first call * to speed up dispatching. */ int gtm_cip(ci_name_descriptor* ci_info, ...) { va_list var; VAR_START(var, ci_info); return gtm_ci_exec(ci_info->rtn_name.address, ci_info->handle, TRUE, var, FALSE); } int gtm_ci_filter(const char *c_rtn_name, ...) { va_list var; VAR_START(var, c_rtn_name); return gtm_ci_exec(c_rtn_name, NULL, FALSE, var, TRUE); } #ifdef GTM_PTHREAD /* Java flavor of gtm_init() */ int gtm_jinit() { gtm_jvm_process = TRUE; return gtm_init(); } #endif /* Initialization routine - can be called directly by call-in caller or can be driven by gtm_ci*() implicitly. But * if other GT.M services are to be used prior to a gtm_ci*() call (like timers, gtm_malloc/free, etc), this routine * should be called first. */ int gtm_init() { rhdtyp *base_addr; unsigned char *transfer_addr; char *dist; char gtmsecshr_path[GTM_PATH_MAX]; unsigned int gtmsecshr_path_len; struct stat stat_buf; char file_perm[MAX_PERM_LEN]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (NULL == lcl_gtm_threadgbl) { /* This will likely need some attention before going to a threaded model */ assert(!gtm_startup_active); GTM_THREADGBL_INIT; } /* A prior invocation of gtm_exit would have set process_exiting = TRUE. Use this to disallow gtm_init to be * invoked after a gtm_exit */ if (process_exiting) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CALLINAFTERXIT); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CALLINAFTERXIT); return ERR_CALLINAFTERXIT; } if (!gtm_startup_active) { /* call-in invoked from C as base. GT.M hasn't been started up yet. */ common_startup_init(GTM_IMAGE); err_init(stop_image_conditional_core); UTF8_ONLY(gtm_strToTitle_ptr = >m_strToTitle); /* Ensure that $gtm_dist exists */ if (NULL == (dist = (char *)GETENV(GTM_DIST))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_GTMDISTUNDEF); /* Ensure that $gtm_dist is non-zero and does not exceed GTM_DIST_PATH_MAX */ gtm_dist_len = (unsigned int)strlen(dist); if (!gtm_dist_len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_GTMDISTUNDEF); else if (GTM_DIST_PATH_MAX <= gtm_dist_len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_DISTPATHMAX, 1, GTM_DIST_PATH_MAX); /* Verify that $gtm_dist/gtmsecshr is available with setuid root */ memcpy(gtmsecshr_path, dist, gtm_dist_len); gtmsecshr_path[gtm_dist_len] = '/'; memcpy(gtmsecshr_path + gtm_dist_len + 1, GTMSECSHR_EXECUTABLE, sizeof(GTMSECSHR_EXECUTABLE)); /* Includes null */ gtmsecshr_path_len = gtm_dist_len + sizeof(GTMSECSHR_EXECUTABLE); /* Includes null, so don't add 1 for the slash */ if (-1 == Stat(gtmsecshr_path, &stat_buf)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("stat for $gtm_dist/gtmsecshr"), CALLFROM, errno); /* Ensure that the call-in can execute $gtm_dist/gtmsecshr. This not sufficient for security purposes */ if ((ROOTUID != stat_buf.st_uid) || !(stat_buf.st_mode & S_ISUID)) { SNPRINTF(file_perm, SIZEOF(file_perm), "%04o", stat_buf.st_mode & PERMALL); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_GTMSECSHRPERM, 5, gtmsecshr_path_len, gtmsecshr_path, RTS_ERROR_STRING(file_perm), stat_buf.st_uid); } else { /* $gtm_dist validated */ gtm_dist_ok_to_use = TRUE; memcpy(gtm_dist, dist, gtm_dist_len); gtm_post_startup_check_init(); } cli_lex_setup(0, NULL); /* Initialize msp to the maximum so if errors occur during GT.M startup below, * the unwind logic in gtmci_ch() will get rid of the whole stack. */ msp = (unsigned char *)-1L; GTMTRIG_DBG_ONLY(ch_at_trigger_init = &mdb_condition_handler); } ESTABLISH_RET(gtmci_ch, mumps_status); if (!gtm_startup_active) { /* GT.M is not active yet. Create GT.M startup environment */ invocation_mode = MUMPS_CALLIN; init_gtm(); /* Note - this initializes fgncal_stackbase */ gtm_savetraps(); /* nullify default $ZTRAP handling */ assert(IS_VALID_IMAGE && (n_image_types > image_type)); /* assert image_type is initialized */ assert(gtm_startup_active); assert(frame_pointer->flags & SFF_CI); TREF(gtmci_nested_level) = 1; /* Now that GT.M is initialized. Mark the new stack pointer (msp) so that errors * while executing an M routine do not unwind stack below this mark. It important that * the call-in frames (SFF_CI) that hold nesting information (eg. $ECODE/$STACK data * of the previous stack) are kept from being unwound. */ SAVE_FGNCAL_STACK; } else if (!(frame_pointer->flags & SFF_CI)) { /* Nested call-in: setup a new CI environment (SFF_CI frame on top of base-frame). * Temporarily mark the beginning of the new stack so that initialization errors in * call-in frame do not unwind entries of the previous stack (see gtmci_ch). For the * duration that temp_fgncal_stack has a non-NULL value, it overrides fgncal_stack. */ TREF(temp_fgncal_stack) = msp; /* Generate CIMAXLEVELS error if gtmci_nested_level > CALLIN_MAX_LEVEL */ if (CALLIN_MAX_LEVEL < TREF(gtmci_nested_level)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_CIMAXLEVELS, 1, TREF(gtmci_nested_level)); /* Disallow call-ins within a TP boundary since TP restarts are not supported * currently across nested call-ins. When we implement TP restarts across call-ins, * this error needs be changed to a Warning or Notification. Tp allowed if a filter * call is being made from inside GT.M. */ if (dollar_tlevel && ((!TREF(comm_filter_init)) || (TREF(gtmci_nested_level)))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CITPNESTED); base_addr = make_cimode(); transfer_addr = PTEXT_ADR(base_addr); gtm_init_env(base_addr, transfer_addr); #if defined(__x86_64__) SET_CI_ENV(opp_ciret); #else SET_CI_ENV(ci_ret_code_exit); #endif gtmci_isv_save(); (TREF(gtmci_nested_level))++; /* Now that the base-frames for this call-in level have been created, we can create the mv_stent * to save the previous call-in level's fgncal_stack value and clear the override. When this call-in * level pops, fgncal_stack will be restored to the value for the previous level. When a given call * at *this* level finishes, this current value of fgncal_stack is where the stack is unrolled to to * be ready for the next call. */ SAVE_FGNCAL_STACK; TREF(temp_fgncal_stack) = NULL; /* Drop override */ } REVERT; assert(NULL == TREF(temp_fgncal_stack)); return 0; } /* routine exposed to call-in user to exit from active GT.M environment */ int gtm_exit() { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (!gtm_startup_active) return 0; /* GT.M environment not setup yet - quietly return */ ESTABLISH_RET(gtmci_ch, mumps_status); assert(NULL != frame_pointer); /* Do not allow gtm_exit() to be invoked from external calls */ if (!(SFF_CI & frame_pointer->flags) || !(MUMPS_CALLIN & invocation_mode) || (1 < TREF(gtmci_nested_level))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_INVGTMEXIT); /* Now get rid of the whole M stack - end of GT.M environment */ while (NULL != frame_pointer) { while ((NULL != frame_pointer) && !(frame_pointer->flags & SFF_CI)) { # ifdef GTM_TRIGGER if (SFT_TRIGR & frame_pointer->type) gtm_trigger_fini(TRUE, FALSE); else # endif op_unwind(); } if (NULL != frame_pointer) { /* unwind the current invocation of call-in environment */ assert(frame_pointer->flags & SFF_CI); ci_ret_code_quit(); } } gtm_exit_handler(); /* rundown all open database resource */ /* If libgtmshr was loaded via (or on account of) dlopen() and is later unloaded via dlclose() * the exit handler on AIX and HPUX still tries to call the registered atexit() handler causing * 'problems'. AIX 5.2 and later have the below unatexit() call to unregister the function if * our exit handler has already been called. Linux and Solaris don't need this, looking at the * other platforms we support to see if resolutions can be found. SE 05/2007 */ # ifdef _AIX unatexit(gtm_exit_handler); # endif REVERT; gtm_startup_active = FALSE; return 0; } /* Routine to fetch $ZSTATUS after an error has been raised */ void gtm_zstatus(char *msg, int len) { int msg_len; msg_len = (len <= dollar_zstatus.str.len) ? len - 1 : dollar_zstatus.str.len; memcpy(msg, dollar_zstatus.str.addr, msg_len); msg[msg_len] = 0; } #ifdef _AIX /* If libgtmshr was loaded via (or on account of) dlopen() and is later unloaded via dlclose() * the exit handler on AIX and HPUX still tries to call the registered atexit() handler causing * 'problems'. AIX 5.2 and later have the below unatexit() call to unregister the function if * our exit handler has already been called. Linux and Solaris don't need this, looking at the * other platforms we support to see if resolutions can be found. This routine will be called * by the OS when libgtmshr is unloaded. Specified with the -binitfini loader option on AIX * to be run when the shared library is unloaded. 06/2007 SE */ void gtmci_cleanup(void) { /* This code is only for callin cleanup */ if (MUMPS_CALLIN != invocation_mode) return; /* If we have already run the exit handler, no need to do so again */ if (gtm_startup_active) { gtm_exit_handler(); gtm_startup_active = FALSE; } /* Unregister exit handler .. AIX only for now */ unatexit(gtm_exit_handler); } #endif fis-gtm-V7.0-005/sr_unix/auto_zlink.c0000644000032200000250000002100514342376334016366 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2019 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_string.h" #include "urx.h" #include #include "stack_frame.h" #include "op.h" #include #include "arlinkdbg.h" #include "linktrc.h" #ifndef AUTORELINK_SUPPORTED # error "Routine should not be built by non-autorelink-enabled platforms" #endif GBLREF stack_frame *frame_pointer; /* Routine to locate the entry being linked from generated code and link it in and return to the glue code * code to drive it. * * Arguments: * * rtnhdridx - Index into linkage table for the routine that needs linking. */ void auto_zlink(int rtnhdridx) { mstr rname; mident_fixed rname_buff; mval rtn; rhdtyp *rhd; assert(0 <= rtnhdridx); /* rtnhdridx must never be negative */ assert(rtnhdridx <= frame_pointer->rvector->linkage_len); assert(NULL == frame_pointer->rvector->linkage_adr[rtnhdridx].ext_ref); rname = frame_pointer->rvector->linkage_names[rtnhdridx]; rname.addr += (INTPTR_T)frame_pointer->rvector->literal_text_adr; /* Perform relocation on name */ memcpy(rname_buff.c, rname.addr, rname.len); memset(rname_buff.c + rname.len, 0, SIZEOF(rname_buff) - rname.len); /* Clear rest of mident_fixed */ rname.addr = rname_buff.c; assert(rname.len <= MAX_MIDENT_LEN); assert(NULL == find_rtn_hdr(&rname)); rtn.mvtype = MV_STR; rtn.str.len = rname.len; rtn.str.addr = rname.addr; op_zlink(&rtn, NULL); /* op_zlink() takes care of '%' -> '_' translation of routine name */ if ('_' == rname_buff.c[0]) rname_buff.c[0] = '%'; if ((NULL == (rhd = find_rtn_hdr(&rname))) && (op_rhdaddr(&rtn, -1))) assert(FALSE && rname.addr); /* if the routine is not found, op_rhdaddr should give and error & return FALSE */ DBGARLNK((stderr, "auto_zlink: Linked in rtn %.*s to "lvaddr"\n", rname.len, rname.addr, find_rtn_hdr(&rname))); return; } /* Routine to check routine if autorelink is needed. * * Arguments: * * rtnhdridx - Index into linkage table for the routine that may need re-linking. * lbltblidx - Index into linkage table for the label table entry needed to get label offset. Main purpose of using this * routine is it is the indicator for when indirect code is using the lnk_proxy mechanism to pass in entries * because indirects have no linkage table of their own. */ void auto_relink_check(int rtnhdridx, int lbltblidx) { rhdtyp *rhd, *callerrhd; int nameoff, clen; unsigned char *cptr; boolean_t fnddot, valid_calling_seq; DEBUG_ONLY(int rtnname_len;) DEBUG_ONLY(char *rtnname_adr;); DEBUG_ONLY(mident_fixed rtnname;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(0 <= rtnhdridx); if ((frame_pointer->flags & SFF_INDCE) || (0 > lbltblidx)) return; /* Don't deal with indirects - just let them use the vars they received from op_rhd/labaddr */ assert(rtnhdridx <= frame_pointer->rvector->linkage_len); assert(lbltblidx <= frame_pointer->rvector->linkage_len); /* If routine hasn't been linked yet, drive auto_zlink() to pull it in and return (no further checking needed since * it was just linked). */ if (NULL == frame_pointer->rvector->linkage_adr[rtnhdridx].ext_ref) { auto_zlink(rtnhdridx); return; } callerrhd = frame_pointer->rvector; /* rtnhdr of routine doing the calling */ rhd = (rhdtyp *)callerrhd->linkage_adr[rtnhdridx].ext_ref; /* rtnhdr of routine being called */ # ifdef DEBUG /* Validate name of routine is as we expect it to be */ assert(NULL != rhd); rtnname_adr = (INTPTR_T)callerrhd->linkage_names[rtnhdridx].addr + (char *)callerrhd->literal_text_adr; rtnname_len = callerrhd->linkage_names[rtnhdridx].len; memcpy(rtnname.c, rtnname_adr, rtnname_len); if ('_' == rtnname.c[0]) rtnname.c[0] = '%'; assert((rtnname_len == rhd->routine_name.len) && (0 == memcmp(rtnname.c, rhd->routine_name.addr, rtnname_len))); # endif DBGARLNK((stderr, "auto_relink_check: rtn %.*s calling rtn: %.*s - arlink_enabled: %d arlink_loaded: %d\n", callerrhd->routine_name.len, callerrhd->routine_name.addr, rhd->routine_name.len, rhd->routine_name.addr, TREF(arlink_enabled), TREF(arlink_loaded))); explicit_relink_check(rhd, FALSE); } /* Routine called when have routine name and need to do an explicit autorelink check - either from above or from * $TEXT(), ZPRINT or any future users of get_src_line(). * * Argument: * * rhd - routine header of routine to be checked if needs to be relinked. * setproxy - TRUE if need to set old/new routine header into TABENT_PROXY * * Routine is already linked, but we need to check if a new version is available. This involves traversing the * "validation linked list", looking for changes in different $ZROUTINES entries. But we also need to base our * checks on the most recent version of the routine loaded. Note autorelink is only allowed when no ZBREAKs are * defined in the given routine. */ void explicit_relink_check(rhdtyp *rhd, boolean_t setproxy) { mval rtnname; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(NULL != rhd); assert(!rhd->rtn_relinked); /* Should never be calling recursively linked routine. All such calls should be going * to the new current routine instead of the recursively linked copy rtnhdr. */ /* Routine is already linked, but we need to check if a new version is available. This involves traversing the * "validation linked list", looking for changes in different $ZROUTINES entries. But we also need to base our * checks on the most recent version of the routine loaded. Note autorelink is only possible when no ZBREAKs are * defined in the given routine. * * Note the following section is very similar to code in op_rhdaddr.c. Any changes made here need to be echoed there */ if (!CURRENT_RHEAD_ADR(rhd)->has_ZBREAK) { rhd = rhd->current_rhead_adr; /* Update rhd to most currently linked version */ # ifdef DEBUG_ARLINK DBGARLNK((stderr, "explicit_relink_check: rtn: %.*s rtnhdr: 0x"lvaddr" zhist: 0x"lvaddr, rhd->routine_name.len, rhd->routine_name.addr, rhd, rhd->zhist)); if (NULL != rhd->zhist) { DBGARLNK((stderr, " zhist->cycle: %d zrtns cycle: %d\n", rhd->zhist->zroutines_cycle, TREF(set_zroutines_cycle))); } else { DBGARLNK((stderr, "\n")); } # endif if ((NULL != rhd->zhist) && need_relink(rhd, (zro_hist *)rhd->zhist)) { /* Relink appears to be needed. Note we have the routine name from the passed in header address so * only debug builds fetch/compare it against the expected value from the name table. */ rtnname.mvtype = MV_STR; rtnname.str = rhd->routine_name; DBGARLNK((stderr,"explicit_relink_check: Routine needs relinking: %.*s\n", rtnname.str.len, rtnname.str.addr)); op_zlink(&rtnname, NULL); /* op_zlink() takes care of '%' -> '_' translation of routine name */ /* Use the fastest way to pickup the routine header address of the current copy. The linker would have * updated the current rtnhdr address in our routine header so this provides the easiest way to find * the newly linked header whether the link was bypassed or not. The only exception to this is when * the current routine is a recursively linked copy routine whose current rtnhdr address always points * to itself but those routines cannot end up here so this is the best way. */ rhd = rhd->current_rhead_adr; /* Note it is possible for multiple processes to interfere with each other and cause the below assert * to fail. Should that happen, the assert can be removed but to date it has not failed so am leaving * it in. */ assert((NULL == rhd->zhist) || (((zro_hist *)(rhd->zhist))->zroutines_cycle == TREF(set_zroutines_cycle))); } else { DBGARLNK((stderr,"explicit_relink_check: Routine does NOT need relinking: %.*s\n", rhd->routine_name.len, rhd->routine_name.addr)); } } else { DBGARLNK((stderr,"explicit_relink_check: Routine relink bypassed - has ZBREAKs: %.*s\n", rhd->routine_name.len, rhd->routine_name.addr)); } if (setproxy) { DBGINDCOMP((stderr, "explicit_relink_check: Indirect call to routine %.*s resolved to 0x"lvaddr"\n", rhd->routine_name.len, rhd->routine_name.addr, rhd)); (TABENT_PROXY).rtnhdr_adr = rhd; } } fis-gtm-V7.0-005/sr_unix/make_mode.c0000644000032200000250000001524114342376334016135 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "gtm_string.h" #include "error.h" #include "dm_setup.h" #include #include "op.h" #include "compiler.h" #include #include "gtmci.h" #include "inst_flush.h" #include "obj_file.h" #include "gtm_text_alloc.h" #ifdef USHBIN_SUPPORTED /* From here down is only defined in a shared binary environment */ #include "make_mode.h" #ifdef __ia64 #if defined(__linux__) void dmode_table_init() __attribute__((constructor)); #else /* __hpux */ #pragma INIT "dmode_table_init" #endif /* __linux__ */ void dmode_table_init(void); #endif /* __ia64 */ /* This routine is called to create (currently two different) dynamic routines that * can be executed. One is a direct mode frame, the other is a callin base frame. They * are basically identical except in the entry points that they call. To this end, this * common routine is called for both with the entry points to be put into the generated * code being passed in as parameters. */ typedef struct dyn_modes_struct { char *rtn_name; int rtn_name_len; void (*func_ptr1)(void); void (*func_ptr2)(void); int (*func_ptr3)(void); } dyn_modes; static dyn_modes our_modes[2] = { { GTM_DMOD, SIZEOF(GTM_DMOD) - 1, dm_setup, mum_tstart, opp_ret }, { GTM_CIMOD, SIZEOF(GTM_CIMOD) - 1, ci_restart, ci_ret_code, opp_ret } }; #if defined(__ia64) /* On IA64, we want to use CODE_ADDRESS() macro, to dereference all the function pointers, before storing them in * global array. Now doing a dereference operation, as part of initialization, is not allowed by linux/gcc (HP'a aCC * was more tolerant towards this). So to make sure that the xfer_table is initialized correctly, before anyone * uses it, one needs to create a 'constructor/initializer' function, which is gauranted to be called as soon as * this module is loaded, and initialize the xfer_table correctly within that function. gcc provides the below * mechanism to do this */ static char dyn_modes_type[2][3] = { {'C','A','A'}, {'A','C','A'} }; void dmode_table_init() { /* our_modes[0].func_ptr1 = (void (*)())CODE_ADDRESS(our_modes[0].func_ptr1); our_modes[0].func_ptr2 = (void (*)())CODE_ADDRESS(our_modes[0].func_ptr2); our_modes[0].func_ptr3 = (int (*)())CODE_ADDRESS(our_modes[0].func_ptr3); our_modes[1].func_ptr1 = (void (*)())CODE_ADDRESS(our_modes[1].func_ptr1); our_modes[1].func_ptr2 = (void (*)())CODE_ADDRESS(our_modes[1].func_ptr2); our_modes[1].func_ptr3 = (int (*)())CODE_ADDRESS(our_modes[1].func_ptr3); */ } #endif /* __ia64 */ rhdtyp *make_mode (int mode_index) { rhdtyp *base_address; lab_tabent *lbl; lnr_tabent *lnr; CODEBUF_TYPE *code; dyn_modes *dmode; int algnd_rtnhdr_size = (int)ROUND_UP2(SIZEOF(rhdtyp), SECTION_ALIGN_BOUNDARY); int algnd_code_size = (int)ROUND_UP2(CODE_SIZE, NATIVE_WSIZE); int algnd_lbltab_size = (int)ROUND_UP2(SIZEOF(lab_tabent), NATIVE_WSIZE); int algnd_lnrtab_size = (int)ROUND_UP2(CODE_LINES * SIZEOF(lnr_tabent), NATIVE_WSIZE); assert((DM_MODE == mode_index) || (CI_MODE == mode_index)); base_address = (rhdtyp *)GTM_TEXT_ALLOC(algnd_rtnhdr_size + algnd_code_size + algnd_lbltab_size + algnd_lnrtab_size); memset(base_address, 0, algnd_rtnhdr_size + algnd_code_size + algnd_lbltab_size + algnd_lnrtab_size); dmode = &our_modes[mode_index]; base_address->routine_name.len = dmode->rtn_name_len; base_address->routine_name.addr = dmode->rtn_name; base_address->ptext_adr = (unsigned char *)base_address + algnd_rtnhdr_size; base_address->ptext_end_adr = (unsigned char *)base_address->ptext_adr + algnd_code_size; base_address->lnrtab_adr = (lnr_tabent *)base_address->ptext_end_adr; base_address->labtab_adr = (lab_tabent *)((unsigned char *)base_address + algnd_rtnhdr_size + algnd_code_size + algnd_lnrtab_size); base_address->lnrtab_len = CODE_LINES; base_address->labtab_len = 1; code = (CODEBUF_TYPE *)base_address->ptext_adr; /* start of executable code */ #ifdef __ia64 if (dyn_modes_type[mode_index][0] == 'C') { GEN_CALL_C(CODE_ADDRESS(dmode->func_ptr1)) /* line 0,1 */ } else { GEN_CALL_ASM(CODE_ADDRESS(dmode->func_ptr1)) /* line 0,1 */ } #else GEN_CALL(dmode->func_ptr1); /* line 0,1 */ #endif /* __ia64 */ #ifdef _AIX if (CI_MODE == mode_index) { /* Following 2 instructions are generated to call the routine stored in GTM_REG_ACCUM. * ci_restart would have loaded this register with the address of op_extcall/op_extexfun. * On other platforms, ci_start usually invokes op_ext* which will return directly * to the generated code. Since RS6000 doesn't support call instruction without altering * return address register (LR), the workaround is to call op_ext* not from ci_restart * but from this dummy code */ *code++ = RS6000_INS_MTLR | GTM_REG_ACCUM << RS6000_SHIFT_RS; *code++ = RS6000_INS_BRL; } #endif #ifdef __ia64 if (dyn_modes_type[mode_index][1] == 'C') { GEN_CALL_C(CODE_ADDRESS(dmode->func_ptr2)) } else { GEN_CALL_ASM(CODE_ADDRESS(dmode->func_ptr2)) } #else GEN_CALL(dmode->func_ptr2); #endif /* __ia64 */ #if defined (__ia64) if (DM_MODE == mode_index) { GEN_UNCOD_JUMP(-(2 * 5)); /* branch to dm_setup which is at the top of the direct mode frame. */ } #elif defined(__hpux) if (DM_MODE == mode_index) { *code++ = HPPA_INS_BEQ | (MAKE_COND_BRANCH_TARGET(-8) << HPPA_SHIFT_OFFSET); /* BEQ r0,r0, -8 */ *code++ = HPPA_INS_NOP; } #endif /* __ia64 */ #ifdef __ia64 if (dyn_modes_type[mode_index][2] == 'C') { GEN_CALL_C(CODE_ADDRESS(dmode->func_ptr3)); /* line 2 */ } else { GEN_CALL_ASM(CODE_ADDRESS(dmode->func_ptr3)); /* line 2 */ } #else GEN_CALL(dmode->func_ptr3); /* line 2 */ #endif /* __ia64 */ lnr = LNRTAB_ADR(base_address); *lnr++ = 0; /* line 0 */ *lnr++ = 0; /* line 1 */ IA64_ONLY(*lnr++ = 2 * CALL_SIZE + EXTRA_INST_SIZE;) /* line 2 */ NON_IA64_ONLY(*lnr++ = 2 * CALL_SIZE + EXTRA_INST * SIZEOF(int);) /* line 2 */ lbl = base_address->labtab_adr; lbl->lnr_adr = base_address->lnrtab_adr; base_address->current_rhead_adr = base_address; zlput_rname(base_address); inst_flush(base_address, algnd_rtnhdr_size + algnd_code_size + algnd_lbltab_size + algnd_lnrtab_size); return base_address; } #endif /* USHBIN_SUPPORTED */ fis-gtm-V7.0-005/sr_unix/bin_load.c0000644000032200000250000013515614342376334015773 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_ctype.h" #include "gtm_stdlib.h" #include "stringpool.h" #include "stp_parms.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "muextr.h" #include "error.h" #include "copy.h" #include "collseq.h" #include "util.h" #include "op.h" #include "gvsub2str.h" #include "mupip_exit.h" #include "file_input.h" #include "load.h" #include "mvalconv.h" #include "mu_gvis.h" #include "gtmmsg.h" #include "gtm_utf8.h" #include "io.h" #include "gtmcrypt.h" #include #include "gv_trigger.h" #include "gvcst_protos.h" /* for gvcst_root_search in GV_BIND_NAME_AND_ROOT_SEARCH macro */ #include "format_targ_key.h" #include "zshow.h" #include "hashtab_mname.h" #include "min_max.h" #include "mu_interactive.h" #include "mupip_load_reg_list.h" #include "gvt_inline.h" #define LAST_NEGATIVE_SUBSCRIPT 127 GBLREF bool mupip_DB_full; GBLREF bool mu_ctrly_occurred; GBLREF bool mu_ctrlc_occurred; GBLREF bool mupip_error_occurred; GBLREF spdesc stringpool; GBLREF gd_addr *gd_header; GBLREF gv_key *gv_altkey; GBLREF gv_key *gv_currkey; GBLREF gv_namehead *gv_target; GBLREF gd_region *gv_cur_region; GBLREF int4 gv_keysize; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF int onerror; GBLREF io_pair io_curr_device; GBLREF gd_region *db_init_region; GBLREF int4 error_condition; LITREF boolean_t mu_int_possub[16][16]; LITREF boolean_t mu_int_negsub[16][16]; LITREF boolean_t mu_int_exponent[256]; error_def(ERR_COLLATIONUNDEF); error_def(ERR_COLLTYPVERSION); error_def(ERR_CORRUPTNODE); error_def(ERR_DBBADNSUB); error_def(ERR_DBCMPNZRO); error_def(ERR_DBDUPNULCOL); error_def(ERR_DBFILERR); error_def(ERR_DBKEYMX); error_def(ERR_DBPRIVERR); error_def(ERR_DBRSIZMN); error_def(ERR_DBRSIZMX); error_def(ERR_FAILEDRECCOUNT); error_def(ERR_GBLOFLOW); error_def(ERR_GVFAILCORE); error_def(ERR_GVINVALID); error_def(ERR_GVIS); error_def(ERR_JNLFILOPN); error_def(ERR_KEY2BIG); error_def(ERR_LDBINFMT); error_def(ERR_LDSPANGLOINCMP); error_def(ERR_LOADCTRLY); error_def(ERR_LOADEOF); error_def(ERR_LOADINVCHSET); error_def(ERR_LOADRECCNT); error_def(ERR_MAXNRSUBSCRIPTS); error_def(ERR_MUNOFINISH); error_def(ERR_NULSUBSC); error_def(ERR_OLDBINEXTRACT); error_def(ERR_REC2BIG); error_def(ERR_RECLOAD); error_def(ERR_STATSDBNOTSUPP); error_def(ERR_TEXT); enum err_code { CORRUPTNODE, DBBADNSUB, DBCMPNZRO, DBKEYMX, DBRSIZMN, DBRSIZMX, GVINVALID, KEY2BIG, MAXNRSUBSCRIPTS, REC2BIG }; #define BIN_PUT 0 #define BIN_BIND 1 #define ERR_COR 2 #define BIN_KILL 3 #define BIN_PUT_GVSPAN 4 #define TEXT1 "Record discarded because" # define GC_BIN_LOAD_ERR(GTMCRYPT_ERRNO) \ { \ io_log_name *io_log; \ \ if (0 != GTMCRYPT_ERRNO) \ { \ io_log = io_curr_device.in->name; \ GTMCRYPT_REPORT_ERROR(GTMCRYPT_ERRNO, gtm_putmsg, io_log->len, io_log->dollar_io); \ mupip_error_occurred = TRUE; \ if (NULL != tmp_gvkey) \ { \ free(tmp_gvkey); \ tmp_gvkey = NULL; \ } \ if (NULL != encr_key_handles) \ { \ free(encr_key_handles); \ encr_key_handles = NULL; \ } \ return; \ } \ } #define DEFAULT_SN_HOLD_BUFF_SIZE MAX_IO_BLOCK_SIZE #define KILL_INCMP_SN_IF_NEEDED(GVNH_REG) \ { \ gd_region *dummy_reg; \ \ if (!sn_incmp_gbl_already_killed) \ { \ COPY_KEY(sn_savekey, gv_currkey); \ COPY_KEY(gv_currkey, sn_gvkey); \ /* The below macro finishes the task of GV_BIND_NAME_AND_ROOT_SEARCH \ * (e.g. setting gv_cur_region for spanning globals). \ */ \ GV_BIND_SUBSNAME_IF_GVSPAN(GVNH_REG, gd_header, gv_currkey, dummy_reg); \ bin_call_db(BIN_KILL, 0, 0, 0, 0, NULL); \ COPY_KEY(gv_currkey, sn_savekey); \ sn_incmp_gbl_already_killed = TRUE; \ } \ } #define DISPLAY_INCMP_SN_MSG \ { \ file_offset = file_offset_base + ((unsigned char *)rp - ptr_base); \ if (file_offset != last_sn_error_offset) \ { \ last_sn_error_offset = file_offset; \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_LDSPANGLOINCMP, 1, &file_offset); \ if (sn_gvkey->end && expected_sn_chunk_number) \ { \ sn_key_str_end = format_targ_key(&sn_key_str[0], MAX_ZWR_KEY_SZ, sn_gvkey, TRUE); \ util_out_print("!_!_Expected Spanning Global variable : !AD", TRUE, \ sn_key_str_end - &sn_key_str[0], sn_key_str); \ } \ sn_key_str_end = format_targ_key(&sn_key_str[0], MAX_ZWR_KEY_SZ, gv_currkey, TRUE); \ util_out_print("!_!_Global variable from record: !AD", TRUE, \ sn_key_str_end - &sn_key_str[0], sn_key_str); \ } \ } #define DISPLAY_FILE_OFFSET_OF_RECORD_AND_REST_OF_BLOCK \ { \ file_offset = file_offset_base + ((unsigned char *)rp - ptr_base); \ util_out_print("!_!_File offset : [0x!16@XQ]", TRUE, &file_offset); \ util_out_print("!_!_Rest of Block :", TRUE); \ zwr_out_print((char *)rp, btop - (unsigned char *)rp); \ util_out_print(0, TRUE); \ } #define DISPLAY_CURRKEY \ { \ sn_key_str_end = format_targ_key(&sn_key_str[0], MAX_ZWR_KEY_SZ, gv_currkey, TRUE); \ util_out_print("!_!_Key: !AD", TRUE, sn_key_str_end - &sn_key_str[0], sn_key_str); \ } #define DISPLAY_VALUE(STR) \ { \ util_out_print(STR, TRUE); \ zwr_out_print(v.str.addr, v.str.len); \ util_out_print(0, TRUE); \ } #define DISPLAY_PARTIAL_SN_HOLD_BUFF \ { \ util_out_print("!_!_Partial Value :", TRUE); \ zwr_out_print(sn_hold_buff, sn_hold_buff_pos); \ util_out_print(0, TRUE); \ } /* starting extract file format 3, we have an extra record for each gvn, that contains the * collation information of the database at the time of extract. This record is transparent * to the user, so the semantics of the command line options, 'begin' and 'end' to MUPIP LOAD * will remain same. The collation header is identified in the binary extract by the fact * that its size is 4 bytes and no valid data record can have length 4. */ gvnh_reg_t *bin_call_db(int, int, INTPTR_T, INTPTR_T, int, unsigned char*); void zwr_out_print(char * buff, int len); #define ZWR_BASE_STRIDE 1024 #define UOP_BASE_STRIDE 1024 void zwr_out_print(char * buff, int bufflen) { char zwrbuff[ZWR_BASE_STRIDE * MAX_ZWR_EXP_RATIO], savechar; int zwrlen; int buffpos, left, stride; int uopbuffpos, uopleft, uopstride; buffpos = 0; while (left = bufflen - buffpos) { if (buffpos) FPRINTF(stderr,"_"); stride = (left > ZWR_BASE_STRIDE) ? ZWR_BASE_STRIDE : left; zwrlen = ZWR_BASE_STRIDE * MAX_ZWR_EXP_RATIO; format2zwr((sm_uc_ptr_t)(buff + buffpos), stride, (unsigned char *) zwrbuff, &zwrlen); uopbuffpos = 0; while (uopleft = zwrlen - uopbuffpos) { uopstride = (uopleft > UOP_BASE_STRIDE) ? UOP_BASE_STRIDE : uopleft; savechar = *(zwrbuff + uopstride); *(zwrbuff + uopstride) = '\0'; FPRINTF(stderr,"%s", zwrbuff + uopbuffpos); *(zwrbuff + uopstride) = savechar; uopbuffpos += uopstride; } buffpos += stride; } FPRINTF(stderr,"\n"); } void bin_load(gtm_uint64_t begin, gtm_uint64_t end, char *line1_ptr, int line1_len) { unsigned char *ptr, *cp1, *cp2, *btop, *gvkey_char_ptr, *tmp_ptr, *tmp_key_ptr, *c, *ctop, *ptr_base; unsigned char hdr_lvl, src_buff[MAX_KEY_SZ + 1], dest_buff[MAX_ZWR_KEY_SZ], cmpc_str[MAX_KEY_SZ + 1], dup_key_str[MAX_KEY_SZ + 1], sn_key_str[MAX_KEY_SZ + 1], *sn_key_str_end; unsigned char *end_buff, *gvn_char, *subs, mych; unsigned short rec_len, next_cmpc, numsubs, num_subscripts; int len, current, last, max_key, max_rec, fmtd_key_len; int tmp_cmpc, sn_chunk_number, expected_sn_chunk_number = 0, sn_hold_buff_pos, sn_hold_buff_size; uint4 max_data_len, max_subsc_len, gblsize, data_len, num_of_reg = 0; ssize_t subsc_len, extr_std_null_coll; gtm_uint64_t iter, key_count, tmp_rec_count, global_key_count; DEBUG_ONLY(gtm_uint64_t saved_begin = 0); gtm_uint64_t first_failed_rec_count, failed_record_count; off_t last_sn_error_offset = 0, file_offset_base = 0, file_offset = 0; boolean_t need_xlation, new_gvn, utf8_extract; boolean_t is_hidden_subscript, ok_to_put = TRUE, putting_a_sn = FALSE, sn_incmp_gbl_already_killed = FALSE; rec_hdr *rp, *next_rp; mval v, tmp_mval, *val = NULL; mname_entry gvname; mstr mstr_src, mstr_dest, opstr; collseq *extr_collseq, *db_collseq, *save_gv_target_collseq; coll_hdr extr_collhdr, db_collhdr; gv_key *tmp_gvkey = NULL; /* null-initialize at start, will be malloced later */ gv_key *sn_gvkey = NULL; /* null-initialize at start, will be malloced later */ gv_key *sn_savekey = NULL; /* null-initialize at start, will be malloced later */ gv_key *save_orig_key = NULL; /* null-initialize at start, will be malloced later */ gv_key *orig_gv_currkey_ptr = NULL; char std_null_coll[BIN_HEADER_NUMSZ + 1], *sn_hold_buff = NULL, *sn_hold_buff_temp = NULL; int in_len, gtmcrypt_errno, n_index, encrypted_hash_array_len, null_iv_array_len; char *inbuf, *encrypted_hash_array_ptr, *curr_hash_ptr, *null_iv_array_ptr, null_iv_char; int4 index; gtmcrypt_key_t *encr_key_handles = NULL; boolean_t encrypted_version, mixed_encryption, valid_gblname; char index_err_buf[1024]; gvnh_reg_t *gvnh_reg; gd_region *dummy_reg, *reg_ptr = NULL; sub_num subtocheck; sgmnt_data_ptr_t csd; boolean_t discard_nullcoll_mismatch_record, update_nullcoll_mismatch_record; unsigned char subscript, *r_ptr; unsigned int null_subscript_cnt, k, sub_index[MAX_GVSUBSCRIPTS]; static unsigned char key_buffer[MAX_ZWR_KEY_SZ]; unsigned char *temp, coll_typr_char; boolean_t switch_db, mu_load_error = FALSE; gd_binding *map; ht_ent_mname *tabent; hash_table_mname *tab_ptr; char msg_buff[MAX_RECLOAD_ERR_MSG_SIZE]; gd_region **reg_list; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(4 == SIZEOF(coll_hdr)); gvinit(); reg_list = (gd_region **)malloc(gd_header->n_regions * SIZEOF(gd_region *)); for (index = 0; index < gd_header->n_regions; index++) reg_list[index] = NULL; v.mvtype = MV_STR; file_offset_base = 0; /* line1_ptr & line1_len are initialized as part of get_load_format using a read of the binary extract file which * did not go through file_input_bin_get. So initialize the internal static structures that file_input_bin_get * maintains as if that read happened through it. This will let us finish reading the binary extract header line. */ file_input_bin_init(line1_ptr, line1_len); len = file_input_bin_get((char **)&ptr, &file_offset_base, (char **)&ptr_base, DO_RTS_ERROR_FALSE); if (0 >= len) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_LDBINFMT); mupip_exit(ERR_LDBINFMT); } hdr_lvl = EXTR_HEADER_LEVEL(ptr); if (!( ((('4' == hdr_lvl) || ('5' == hdr_lvl)) && (V5_BIN_HEADER_SZ == len)) || ((('6' <= hdr_lvl) && ('9' >= hdr_lvl)) && (BIN_HEADER_SZ == len)) || (('4' > hdr_lvl) && (V3_BIN_HEADER_SZ == len)))) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_LDBINFMT); mupip_exit(ERR_LDBINFMT); } /* expecting the level in a single character */ assert(' ' == *(ptr + SIZEOF(BIN_HEADER_LABEL) - 3)); if (0 != memcmp(ptr, BIN_HEADER_LABEL, SIZEOF(BIN_HEADER_LABEL) - 2) || ('2' > hdr_lvl) || *(BIN_HEADER_VERSION_ENCR_IV) < hdr_lvl) { /* ignore the level check */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_LDBINFMT); mupip_exit(ERR_LDBINFMT); } /* check if extract was generated in UTF-8 mode */ utf8_extract = (0 == MEMCMP_LIT(&ptr[len - BIN_HEADER_LABELSZ], UTF8_NAME)) ? TRUE : FALSE; if ((utf8_extract && !gtm_utf8_mode) || (!utf8_extract && gtm_utf8_mode)) { /* extract CHSET doesn't match $ZCHSET */ if (utf8_extract) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_LOADINVCHSET, 2, LEN_AND_LIT("UTF-8")); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_LOADINVCHSET, 2, LEN_AND_LIT("M")); mupip_exit(ERR_LDBINFMT); } if ('4' >= hdr_lvl) { /* Binary extracts in V50000-to-V52000 (label=4) and pre-V50000 (label=3) could have a '\0' byte (NULL byte) * in the middle of the string. Replace it with ' ' (space) like it would be in V52000 binary extracts and above. */ for (c = ptr, ctop = c + len; c < ctop; c++) { if ('\0' == *c) *c = ' '; } } util_out_print("Label = !AD\n", TRUE, len, ptr); new_gvn = FALSE; if (hdr_lvl > '3') { if (hdr_lvl > '5') { memcpy(std_null_coll, ptr + BIN_HEADER_NULLCOLLOFFSET, BIN_HEADER_NUMSZ); std_null_coll[BIN_HEADER_NUMSZ] = '\0'; } else { memcpy(std_null_coll, ptr + V5_BIN_HEADER_NULLCOLLOFFSET, V5_BIN_HEADER_NUMSZ); std_null_coll[V5_BIN_HEADER_NUMSZ] = '\0'; } extr_std_null_coll = STRTOUL(std_null_coll, NULL, 10); if (0 != extr_std_null_coll && 1!= extr_std_null_coll) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_TEXT, 2, RTS_ERROR_TEXT("Corrupted null collation field in header"), ERR_LDBINFMT); mupip_exit(ERR_LDBINFMT); } } else extr_std_null_coll = 0; /* Encrypted versions to date. */ encrypted_version = ('5' <= hdr_lvl) && ('6' != hdr_lvl); /* Includes 5, 7, 8, and 9. */ if (encrypted_version) { encrypted_hash_array_len = file_input_bin_get((char **)&ptr, &file_offset_base, (char **)&ptr_base, DO_RTS_ERROR_TRUE); encrypted_hash_array_ptr = malloc(encrypted_hash_array_len); memcpy(encrypted_hash_array_ptr, ptr, encrypted_hash_array_len); n_index = encrypted_hash_array_len / GTMCRYPT_HASH_LEN; encr_key_handles = (gtmcrypt_key_t *)malloc(SIZEOF(gtmcrypt_key_t) * n_index); memset(encr_key_handles, 0, SIZEOF(gtmcrypt_key_t) * n_index); INIT_PROC_ENCRYPTION(gtmcrypt_errno); GC_BIN_LOAD_ERR(gtmcrypt_errno); mixed_encryption = FALSE; for (index = 0; index < n_index; index++) { curr_hash_ptr = encrypted_hash_array_ptr + index * GTMCRYPT_HASH_LEN; if (0 == memcmp(curr_hash_ptr, EMPTY_GTMCRYPT_HASH, GTMCRYPT_HASH_LEN)) { mixed_encryption = TRUE; continue; } GTMCRYPT_INIT_BOTH_CIPHER_CONTEXTS(NULL, curr_hash_ptr, 0, NULL, encr_key_handles[index], gtmcrypt_errno); GC_BIN_LOAD_ERR(gtmcrypt_errno); } if ('9' == hdr_lvl) { null_iv_array_len = file_input_bin_get((char **)&ptr, &file_offset_base, (char **)&ptr_base, DO_RTS_ERROR_TRUE); assert(n_index == null_iv_array_len); null_iv_array_ptr = malloc(null_iv_array_len); memcpy(null_iv_array_ptr, ptr, null_iv_array_len); } } if ('2' < hdr_lvl) { len = file_input_bin_get((char **)&ptr, &file_offset_base, (char **)&ptr_base, DO_RTS_ERROR_TRUE); if (SIZEOF(coll_hdr) != len) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_TEXT, 2, RTS_ERROR_TEXT("Corrupt collation header"), ERR_LDBINFMT); mupip_exit(ERR_LDBINFMT); } extr_collhdr = *((coll_hdr *)(ptr)); new_gvn = TRUE; } else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_OLDBINEXTRACT, 1, hdr_lvl - '0'); if (begin < 2) /* WARNING: begin can never be less than 2 */ begin = 2; #ifdef DEBUG if ((WBTEST_ENABLED(WBTEST_FAKE_BIG_KEY_COUNT)) && (2UL < begin)) saved_begin = begin, begin = 2; else if (WBTEST_ENABLED(WBTEST_FAKE_BIG_KEY_COUNT)) saved_begin = FAKE_BIG_KEY_COUNT; #endif for (iter = 2; iter < begin; iter++) { if (!(len = file_input_bin_get((char **)&ptr, &file_offset_base, (char **)&ptr_base, DO_RTS_ERROR_TRUE))) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_LOADEOF, 1, &begin); util_out_print("Error reading record number: !@UQ\n", TRUE, &iter); mupip_error_occurred = TRUE; return; } else if (len == SIZEOF(coll_hdr)) { extr_collhdr = *((coll_hdr *)(ptr)); assert(hdr_lvl > '2'); iter--; } } assert(NULL == tmp_gvkey); /* GVKEY_INIT macro relies on this */ GVKEY_INIT(tmp_gvkey, DBKEYSIZE(MAX_KEY_SZ)); /* tmp_gvkey will point to malloced memory after this */ assert(NULL == sn_gvkey); /* GVKEY_INIT macro relies on this */ GVKEY_INIT(sn_gvkey, DBKEYSIZE(MAX_KEY_SZ)); /* sn_gvkey will point to malloced memory after this */ assert(NULL == sn_savekey); /* GVKEY_INIT macro relies on this */ GVKEY_INIT(sn_savekey, DBKEYSIZE(MAX_KEY_SZ)); /* sn_gvkey will point to malloced memory after this */ assert(NULL == save_orig_key); /* GVKEY_INIT macro relies on this */ GVKEY_INIT(save_orig_key, DBKEYSIZE(MAX_KEY_SZ)); /* sn_gvkey will point to malloced memory after this */ assert(iter == begin); global_key_count = key_count = 0; max_data_len = max_subsc_len = 0; extr_collseq = db_collseq = NULL; gvnh_reg = NULL; need_xlation = FALSE; GTM_WHITE_BOX_TEST(WBTEST_FAKE_BIG_KEY_COUNT, key_count, saved_begin); GTM_WHITE_BOX_TEST(WBTEST_FAKE_BIG_KEY_COUNT, begin, saved_begin); iter = begin - 1; /* WARNING: iter can never be zero because begin can never be less than 2 */ util_out_print("Beginning LOAD at record number: !@UQ\n", TRUE, &begin); while (!mupip_DB_full) { if ((++iter > end) || (0 == iter)) break; next_cmpc = 0; if (mupip_error_occurred && ONERROR_STOP == onerror) break; mupip_error_occurred = FALSE; if (mu_ctrly_occurred) break; if (mu_ctrlc_occurred) { util_out_print("!AD:!_ Key cnt: !@UQ max subsc len: !UL max data len: !UL", TRUE, LEN_AND_LIT("LOAD TOTAL"), &key_count, max_subsc_len, max_data_len); tmp_rec_count = (iter == begin) ? iter : iter - 1; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) MAKE_MSG_INFO(ERR_LOADRECCNT), 1, &tmp_rec_count); mu_gvis(); util_out_print(0, TRUE); mu_ctrlc_occurred = FALSE; } if (!(len = file_input_bin_get((char **)&ptr, &file_offset_base, (char **)&ptr_base, DO_RTS_ERROR_TRUE)) || mupip_error_occurred) break; else if (len == SIZEOF(coll_hdr)) { extr_collhdr = *((coll_hdr *)(ptr)); assert(hdr_lvl > '2'); new_gvn = TRUE; /* next record will contain a new gvn */ iter--; /* Decrement as this record does not count as a record for loading purposes */ continue; } if (encrypted_version) { /* Getting index value from the extracted file. It indicates which database file this record belongs to */ GET_LONG(index, ptr); tmp_ptr = ptr + SIZEOF(int4); if (-1 != index) { switch (hdr_lvl) { case '9': /* Record is encrypted; ensure legitimate encryption handle index. */ if ((n_index <= index) || (0 > index)) { SNPRINTF(index_err_buf, SIZEOF(index_err_buf), "Encryption handle expected in the range [0; %d) but found %d", n_index, index); SNPRINTF(msg_buff, SIZEOF(msg_buff), "%" PRIu64, iter ); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) MAKE_MSG_SEVERE(ERR_RECLOAD), 2, LEN_AND_STR(msg_buff), ERR_TEXT, 2, RTS_ERROR_TEXT(index_err_buf), ERR_GVFAILCORE); gtm_fork_n_core(); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) MAKE_MSG_SEVERE(ERR_RECLOAD), 2, LEN_AND_STR(msg_buff), msg_buff, ERR_TEXT, 2, RTS_ERROR_TEXT(index_err_buf)); return; } # ifdef DEBUG /* Ensure that len is the length of one int, block header, and all records. */ GET_ULONG(data_len, &((blk_hdr *)tmp_ptr)->bsiz); assert(data_len + SIZEOF(int4) == len); # endif in_len = len - SIZEOF(int4) - SIZEOF(blk_hdr); inbuf = (char *)(tmp_ptr + SIZEOF(blk_hdr)); null_iv_char = null_iv_array_ptr[index]; assert(('1' == null_iv_char) || ('0' == null_iv_char)); GTMCRYPT_DECRYPT(NULL, ('0' == null_iv_char), encr_key_handles[index], inbuf, in_len, NULL, tmp_ptr, SIZEOF(blk_hdr), gtmcrypt_errno); GC_BIN_LOAD_ERR(gtmcrypt_errno); rp = (rec_hdr *)(tmp_ptr + SIZEOF(blk_hdr)); break; case '7': /* In version 7 the extract logic did not properly distinguish non-encrypted records * from encrypted ones in case both kinds were present. Specifically, extracts did * not precede unencrypted records with a '-1' to signify that no decryption is * required. Here we make the best attempt to recognize that situation and process * the record so long as the index appears legitimate. * * By now we know that the record is probably encrypted; however, there is a slight * chance that the encryption handle index is incorrect, although in a legitimate * range. In that case we will additionally check if the corresponding hash is * non-zero---which would only be the case if the region was encrypted---but only * when dealing with a mix of encrypted and unencrypted data. */ if ((n_index <= index) || (0 > index) || (mixed_encryption && (0 == memcmp(encrypted_hash_array_ptr + index * GTMCRYPT_HASH_LEN, EMPTY_GTMCRYPT_HASH, GTMCRYPT_HASH_LEN)))) { rp = (rec_hdr *)ptr; break; } /* CAUTION: Fall-through. */ case '5': /* CAUTION: Fall-through. */ case '8': /* Record is encrypted; ensure legitimate encryption handle index. */ assertpro((n_index > index) && (0 <= index)); in_len = len - SIZEOF(int4); inbuf = (char *)tmp_ptr; GTMCRYPT_DECRYPT_NO_IV(NULL, encr_key_handles[index], inbuf, in_len, NULL, gtmcrypt_errno); GC_BIN_LOAD_ERR(gtmcrypt_errno); rp = (rec_hdr *)tmp_ptr; break; } } else rp = (rec_hdr *)tmp_ptr; } else rp = (rec_hdr *)(ptr); btop = ptr + len; cp1 = (unsigned char *)(rp + 1); gvname.var_name.addr = (char*)cp1; while (*cp1++) ; gvname.var_name.len = INTCAST((char *)cp1 - gvname.var_name.addr - 1); if (('2' >= hdr_lvl) || new_gvn) { if ((HASHT_GBLNAME_LEN == gvname.var_name.len) && (0 == memcmp(gvname.var_name.addr, HASHT_GBLNAME, HASHT_GBLNAME_LEN))) continue; gvname.var_name.len = MIN(gvname.var_name.len, MAX_MIDENT_LEN); COMPUTE_HASH_MNAME(&gvname); if (mu_load_error) { switch_db = check_db_status_for_global(&gvname, MU_FMT_BINARY, &failed_record_count, iter, &first_failed_rec_count, reg_list, num_of_reg); if (!switch_db) continue; } gvnh_reg = bin_call_db(BIN_BIND, 0, (INTPTR_T)gd_header, (INTPTR_T)&gvname, 0, NULL); /* "gv_cur_region" will be set at this point in case the global does NOT span regions. * For globals that do span regions, "gv_cur_region" will be set just before the call to op_gvput. * This value of "gvnh_reg" will be in effect until all records of this global are processed. */ if (mupip_error_occurred) { if ((ERR_DBFILERR == error_condition) || (ERR_STATSDBNOTSUPP == error_condition)) { insert_reg_to_list(reg_list, db_init_region, &num_of_reg); first_failed_rec_count = iter; mu_load_error = TRUE; } else { failed_record_count = 0; SNPRINTF(msg_buff, SIZEOF(msg_buff), "%" PRIu64, iter); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_RECLOAD, 2, LEN_AND_STR(msg_buff)); } failed_record_count++; ONERROR_PROCESS; } mu_load_error= FALSE; first_failed_rec_count = 0; max_key = gvnh_reg->gd_reg->max_key_size; max_rec = gvnh_reg->gd_reg->max_rec_size; db_collhdr.act = gv_target->act; db_collhdr.ver = gv_target->ver; db_collhdr.nct = gv_target->nct; } GET_USHORT(rec_len, &rp->rsiz); if (0 != EVAL_CMPC(rp)) { bin_call_db(ERR_COR, CORRUPTNODE, iter, global_key_count, 0, NULL); bin_call_db(ERR_COR, DBCMPNZRO, 0, 0, gvname.var_name.len, (unsigned char*)(gvname.var_name.addr)); } else if (gvname.var_name.len > rec_len) { bin_call_db(ERR_COR, CORRUPTNODE, iter, global_key_count, 0, NULL); bin_call_db(ERR_COR, DBKEYMX, 0, 0, gvname.var_name.len, (unsigned char*)(gvname.var_name.addr)); } else if (mupip_error_occurred) { bin_call_db(ERR_COR, CORRUPTNODE, iter, global_key_count, 0, NULL); } if (mupip_error_occurred) { mu_gvis(); DISPLAY_FILE_OFFSET_OF_RECORD_AND_REST_OF_BLOCK; continue; } if (new_gvn) { global_key_count = 1; if ((db_collhdr.act != extr_collhdr.act) || (db_collhdr.ver != extr_collhdr.ver) || (db_collhdr.nct != extr_collhdr.nct) || (gvnh_reg->gd_reg->std_null_coll != extr_std_null_coll)) { if (extr_collhdr.act) { if (extr_collseq = ready_collseq((int)extr_collhdr.act)) { if (!do_verify(extr_collseq, extr_collhdr.act, extr_collhdr.ver)) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(8) ERR_COLLTYPVERSION, 2, extr_collhdr.act, extr_collhdr.ver, ERR_GVIS, 2, gv_altkey->end - 1, gv_altkey->base); mupip_exit(ERR_COLLTYPVERSION); } } else { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(7) ERR_COLLATIONUNDEF, 1, extr_collhdr.act, ERR_GVIS, 2, gv_altkey->end - 1, gv_altkey->base); mupip_exit(ERR_COLLATIONUNDEF); } } if (db_collhdr.act) { if (db_collseq = ready_collseq((int)db_collhdr.act)) { if (!do_verify(db_collseq, db_collhdr.act, db_collhdr.ver)) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(8) ERR_COLLTYPVERSION, 2, db_collhdr.act, db_collhdr.ver, ERR_GVIS, 2, gv_altkey->end - 1, gv_altkey->base); mupip_exit(ERR_COLLTYPVERSION); } } else { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(7) ERR_COLLATIONUNDEF, 1, db_collhdr.act, ERR_GVIS, 2, gv_altkey->end - 1, gv_altkey->base); mupip_exit(ERR_COLLATIONUNDEF); } } need_xlation = TRUE; } else need_xlation = FALSE; } new_gvn = FALSE; for ( ; rp < (rec_hdr*)btop; rp = (rec_hdr*)((unsigned char *)rp + rec_len)) { csd = cs_data; GET_USHORT(rec_len, &rp->rsiz); if (rec_len + (unsigned char *)rp > btop) { bin_call_db(ERR_COR, CORRUPTNODE, iter, global_key_count, 0, NULL); bin_call_db(ERR_COR, DBRSIZMX, 0, 0, gvname.var_name.len, (unsigned char*)(gvname.var_name.addr)); mu_gvis(); DISPLAY_FILE_OFFSET_OF_RECORD_AND_REST_OF_BLOCK; break; } cp1 = (unsigned char*)(rp + 1); cp2 = gv_currkey->base + EVAL_CMPC(rp); current = 1; for ( ; ; ) { last = current; current = *cp2++ = *cp1++; if (0 == last && 0 == current) break; if (cp1 > (unsigned char *)rp + rec_len || cp2 > (unsigned char *)gv_currkey + gv_currkey->top) { gv_currkey->end = cp2 - gv_currkey->base - 1; gv_currkey->base[gv_currkey->end] = 0; gv_currkey->base[gv_currkey->end - 1] = 0; bin_call_db(ERR_COR, CORRUPTNODE, (INTPTR_T)iter, (INTPTR_T)global_key_count, 0, NULL); mu_gvis(); DISPLAY_FILE_OFFSET_OF_RECORD_AND_REST_OF_BLOCK; break; } } if (mupip_error_occurred) break; gv_currkey->end = cp2 - gv_currkey->base - 1; if (need_xlation) { assert(hdr_lvl >= '3'); assert(extr_collhdr.act || db_collhdr.act || extr_collhdr.nct || db_collhdr.nct || (extr_std_null_coll != gvnh_reg->gd_reg->std_null_coll)); /* gv_currkey would have been modified/translated in the earlier put */ memcpy(gv_currkey->base, cmpc_str, next_cmpc); next_rp = (rec_hdr *)((unsigned char*)rp + rec_len); if ((unsigned char*)next_rp < btop) { next_cmpc = EVAL_CMPC(next_rp); assert(next_cmpc <= gv_currkey->end); memcpy(cmpc_str, gv_currkey->base, next_cmpc); } else next_cmpc = 0; /* length of the key might change (due to nct variation), * so get a copy of the original key from the extract */ memcpy(dup_key_str, gv_currkey->base, gv_currkey->end + 1); COPY_KEY(save_orig_key, gv_currkey); gvkey_char_ptr = dup_key_str; while (*gvkey_char_ptr++) ; gv_currkey->prev = 0; gv_currkey->end = gvkey_char_ptr - dup_key_str; assert(gv_keysize <= tmp_gvkey->top); while (*gvkey_char_ptr) { /* get next subscript (in GT.M internal subsc format) */ tmp_ptr = src_buff; while (*gvkey_char_ptr) *tmp_ptr++ = *gvkey_char_ptr++; subsc_len = tmp_ptr - src_buff; src_buff[subsc_len] = '\0'; if (extr_collseq) { /* undo the extract time collation */ TREF(transform) = TRUE; save_gv_target_collseq = gv_target->collseq; gv_target->collseq = extr_collseq; } else TREF(transform) = FALSE; /* convert the subscript to string format */ opstr.addr = (char *)dest_buff; opstr.len = MAX_ZWR_KEY_SZ; end_buff = gvsub2str(src_buff, &opstr, FALSE); /* transform the string to the current subsc format */ TREF(transform) = TRUE; tmp_mval.mvtype = MV_STR; tmp_mval.str.addr = (char *)dest_buff; tmp_mval.str.len = INTCAST(end_buff - dest_buff); tmp_gvkey->prev = 0; tmp_gvkey->end = 0; if (extr_collseq) gv_target->collseq = save_gv_target_collseq; mval2subsc(&tmp_mval, tmp_gvkey, gvnh_reg->gd_reg->std_null_coll); /* we now have the correctly transformed subscript */ tmp_key_ptr = gv_currkey->base + gv_currkey->end; memcpy(tmp_key_ptr, tmp_gvkey->base, tmp_gvkey->end + 1); gv_currkey->prev = gv_currkey->end; gv_currkey->end += tmp_gvkey->end; gvkey_char_ptr++; } } if (gv_currkey->end >= max_key) { bin_call_db(ERR_COR, CORRUPTNODE, iter, global_key_count, 0, NULL); bin_call_db(ERR_COR, KEY2BIG, gv_currkey->end, max_key, REG_LEN_STR(gvnh_reg->gd_reg)); mu_gvis(); DISPLAY_FILE_OFFSET_OF_RECORD_AND_REST_OF_BLOCK; continue; } /* Validate the global variable name */ gvn_char = gv_currkey->base; valid_gblname = VALFIRSTCHAR(*gvn_char); gvn_char++; /* need to post-increment here since above macro uses it multiple times */ for (; (*gvn_char && valid_gblname) ; gvn_char++) valid_gblname = valid_gblname && VALKEY(*gvn_char); valid_gblname = (valid_gblname && (KEY_DELIMITER == *gvn_char)); if (!valid_gblname) { mupip_error_occurred = TRUE; bin_call_db(ERR_COR, CORRUPTNODE, iter, global_key_count, 0, NULL); bin_call_db(ERR_COR, GVINVALID, 0, 0, (gvn_char - gv_currkey->base), gv_currkey->base); mu_gvis(); DISPLAY_FILE_OFFSET_OF_RECORD_AND_REST_OF_BLOCK; break; } /* Validate the subscripts */ subs = gvn_char + 1; num_subscripts = 0; while (mych = *subs++) /* WARNING: assignment */ { num_subscripts++; if (MAX_GVSUBSCRIPTS < num_subscripts) { mupip_error_occurred = TRUE; bin_call_db(ERR_COR, CORRUPTNODE, iter, global_key_count, 0, NULL); bin_call_db(ERR_COR, MAXNRSUBSCRIPTS, 0, 0, 0, NULL); mu_gvis(); DISPLAY_FILE_OFFSET_OF_RECORD_AND_REST_OF_BLOCK; break; } if (mu_int_exponent[mych]) /* Is it a numeric subscript? */ { if (mych > LAST_NEGATIVE_SUBSCRIPT) /* Is it a positive numeric subscript */ { while (mych = *subs++) /* WARNING: assignment */ { memcpy(&subtocheck, &mych, 1); if (!mu_int_possub[subtocheck.one][subtocheck.two]) { mupip_error_occurred = TRUE; bin_call_db(ERR_COR, CORRUPTNODE, iter, global_key_count, 0, NULL); bin_call_db(ERR_COR, DBBADNSUB, 0, 0, (gvn_char - gv_currkey->base), gv_currkey->base); mu_gvis(); DISPLAY_FILE_OFFSET_OF_RECORD_AND_REST_OF_BLOCK; break; } } } else /* It is a negative subscript */ { while ((mych = *subs++) && (STR_SUB_PREFIX != mych)) /* WARNING: assignment */ { memcpy(&subtocheck, &mych, 1); if (!mu_int_negsub[subtocheck.one][subtocheck.two]) { mupip_error_occurred = TRUE; bin_call_db(ERR_COR, CORRUPTNODE, iter, global_key_count, 0, NULL); bin_call_db(ERR_COR, DBBADNSUB, 0, 0, (gvn_char - gv_currkey->base), gv_currkey->base); mu_gvis(); DISPLAY_FILE_OFFSET_OF_RECORD_AND_REST_OF_BLOCK; break; } } if (!mupip_error_occurred && ((STR_SUB_PREFIX != mych) || (*subs))) { mupip_error_occurred = TRUE; bin_call_db(ERR_COR, CORRUPTNODE, iter, global_key_count, 0, NULL); mu_gvis(); DISPLAY_FILE_OFFSET_OF_RECORD_AND_REST_OF_BLOCK; break; } } } else /* It is a string subscript */ { /* string can have arbitrary content so move to the next subscript */ while (mych = *subs++) /* WARNING: assignment */ ; } if (mupip_error_occurred) break; } if (mupip_error_occurred) break; /* * Spanning node-related variables and their usage: * * expected_sn_chunk_number: 0 - looking for spanning nodes (regular nodes are OK, too) * !0 - number of the next chunk needed (implies we are building * a spanning node's value) * * While building a spanning node's value: * numsubs: the number of chunks needed to build the spanning node's value * gblsize: the expected size of the completed value * sn_chunk_number: The chunk number of the chunk from the current record from the extract * * Managing the value * sn_hold_buff: buffer used to accumulate the spanning node's value * sn_hold_buff_size: Allocated size of buffer * sn_hold_buff_pos: amount of the buffer used; where to place the next chunk * sn_hold_buff_temp: used when we have to increase the size of the buffer * * Controlling the placing of the key,value in the database: * ok_to_put: means we are ready to place the key,value in the database, i.e., we have the full value * (either of the spanning node or a regular node). * putting_a_sn: we are placing a spanning node in the database, i.e, use the key from sn_gvkey and * the value from sn_hold_buff. */ CHECK_HIDDEN_SUBSCRIPT(gv_currkey,is_hidden_subscript); v.str.addr = (char*)cp1; v.str.len =INTCAST(rec_len - (cp1 - (unsigned char *)rp)); if (expected_sn_chunk_number && !is_hidden_subscript) { /* we were expecting a chunk of an spanning node and we did not get one */ DISPLAY_INCMP_SN_MSG; util_out_print("!_!_Expected chunk number : !UL but found a non-spanning node", TRUE, expected_sn_chunk_number + 1); if (sn_hold_buff_pos) DISPLAY_PARTIAL_SN_HOLD_BUFF; KILL_INCMP_SN_IF_NEEDED(gvnh_reg); sn_hold_buff_pos = 0; expected_sn_chunk_number = 0; ok_to_put = TRUE; putting_a_sn = FALSE; numsubs = 0; } if (is_hidden_subscript) { /* it's a chunk and we were expecting one */ sn_chunk_number = SPAN_GVSUBS2INT((span_subs *) &(gv_currkey->base[gv_currkey->end - 4])); if (!expected_sn_chunk_number && is_hidden_subscript && sn_chunk_number) { /* we not expecting a payload chunk (as opposed to a control record) but we got one */ DISPLAY_INCMP_SN_MSG; util_out_print("!_!_Not expecting a spanning node chunk but found chunk : !UL", TRUE, sn_chunk_number + 1); if (v.str.len) DISPLAY_VALUE("!_!_Errant Chunk :"); continue; } if (0 == sn_chunk_number) { /* first spanning node chunk, get ctrl info */ if (0 != expected_sn_chunk_number) { DISPLAY_INCMP_SN_MSG; util_out_print("!_!_Expected chunk number : !UL but found chunk number : !UL", TRUE, expected_sn_chunk_number + 1, sn_chunk_number + 1); if (sn_hold_buff_pos) DISPLAY_PARTIAL_SN_HOLD_BUFF; KILL_INCMP_SN_IF_NEEDED(gvnh_reg); } /* start building a new spanning node */ sn_gvkey->end = gv_currkey->end - (SPAN_SUBS_LEN + 1); memcpy(sn_gvkey->base, gv_currkey->base, sn_gvkey->end); sn_gvkey->base[sn_gvkey->end] = 0; sn_gvkey->prev = gv_currkey->prev; sn_gvkey->top = gv_currkey->top; GET_NSBCTRL(v.str.addr, numsubs, gblsize); /* look for first payload chunk */ expected_sn_chunk_number = 1; sn_hold_buff_pos = 0; ok_to_put = FALSE; sn_incmp_gbl_already_killed = FALSE; } else { /* we only need to compare the key before the hidden subscripts */ if ((expected_sn_chunk_number == sn_chunk_number) && (sn_gvkey->end == gv_currkey->end - (SPAN_SUBS_LEN + 1)) && !memcmp(sn_gvkey->base,gv_currkey->base, sn_gvkey->end) && ((sn_hold_buff_pos + v.str.len) <= gblsize)) { if (NULL == sn_hold_buff) { sn_hold_buff_size = DEFAULT_SN_HOLD_BUFF_SIZE; sn_hold_buff = (char *)malloc(DEFAULT_SN_HOLD_BUFF_SIZE); } if ((sn_hold_buff_pos + v.str.len) > sn_hold_buff_size) { sn_hold_buff_size = sn_hold_buff_size * 2; sn_hold_buff_temp = (char *)malloc(sn_hold_buff_size); memcpy(sn_hold_buff_temp, sn_hold_buff, sn_hold_buff_pos); free (sn_hold_buff); sn_hold_buff = sn_hold_buff_temp; } memcpy(sn_hold_buff + sn_hold_buff_pos, v.str.addr, v.str.len); sn_hold_buff_pos += v.str.len; if (expected_sn_chunk_number == numsubs) { if (sn_hold_buff_pos != gblsize) { /* we don't have the expected size even though */ /* we have all the expected chunks. */ DISPLAY_INCMP_SN_MSG; util_out_print("!_!_Expected size : !UL actual size : !UL", TRUE, gblsize, sn_hold_buff_pos); if (sn_hold_buff_pos) DISPLAY_PARTIAL_SN_HOLD_BUFF; KILL_INCMP_SN_IF_NEEDED(gvnh_reg); expected_sn_chunk_number = 0; ok_to_put = FALSE; sn_hold_buff_pos = 0; } else { expected_sn_chunk_number = 0; ok_to_put = TRUE; putting_a_sn = TRUE; } } else expected_sn_chunk_number++; } else { DISPLAY_INCMP_SN_MSG; if ((sn_hold_buff_pos + v.str.len) <= gblsize) util_out_print("!_!_Expected chunk number : " "!UL but found chunk number : !UL", TRUE, expected_sn_chunk_number + 1, sn_chunk_number + 1); else util_out_print("!_!_Global value too large: expected size : " "!UL actual size : !UL chunk number : !UL", TRUE, gblsize, sn_hold_buff_pos + v.str.len, sn_chunk_number + 1); if (sn_hold_buff_pos) DISPLAY_PARTIAL_SN_HOLD_BUFF; if (v.str.len) DISPLAY_VALUE("!_!_Errant Chunk :"); KILL_INCMP_SN_IF_NEEDED(gvnh_reg); sn_hold_buff_pos = 0; expected_sn_chunk_number = 0; } } } else ok_to_put = TRUE; if (ok_to_put) { if (!need_xlation) COPY_KEY(save_orig_key, gv_currkey); orig_gv_currkey_ptr = gv_currkey; gv_currkey = save_orig_key; for (r_ptr = gv_currkey->base; *r_ptr != KEY_DELIMITER; r_ptr++) ; null_subscript_cnt = 0; coll_typr_char = (gvnh_reg->gd_reg->std_null_coll) ? SUBSCRIPT_STDCOL_NULL : STR_SUB_PREFIX; for (;;) { if (r_ptr >= (gv_currkey->base + gv_currkey->end)) break; if (KEY_DELIMITER == *r_ptr++) { subscript = *r_ptr; if (KEY_DELIMITER == subscript) break; if (SUBSCRIPT_ZERO == subscript || KEY_DELIMITER != *(r_ptr + 1)) { r_ptr++; continue; } sub_index[null_subscript_cnt++] = (r_ptr - gv_currkey->base); } } if (0 < null_subscript_cnt && !csd->null_subs) { temp = (unsigned char *)format_targ_key(&key_buffer[0], MAX_ZWR_KEY_SZ, gv_currkey, TRUE); fmtd_key_len = (int)(temp - key_buffer); key_buffer[fmtd_key_len] = '\0'; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_NULSUBSC, 4, LEN_AND_LIT(TEXT1), DB_LEN_STR(gv_cur_region), ERR_GVIS, 2, fmtd_key_len, &key_buffer[0]); ok_to_put = FALSE; } discard_nullcoll_mismatch_record = FALSE; update_nullcoll_mismatch_record = FALSE; if (0 < null_subscript_cnt && gv_target->root) { if (!val) { PUSH_MV_STENT(MVST_MVAL); val = &mv_chain->mv_st_cont.mvs_mval; } if (csd->std_null_coll ? SUBSCRIPT_STDCOL_NULL : STR_SUB_PREFIX != gv_currkey->base[sub_index[0]]) { for (k = 0; k < null_subscript_cnt; k++) gv_currkey->base[sub_index[k]] = coll_typr_char; if (gvcst_get(val)) discard_nullcoll_mismatch_record = TRUE; for (k = 0; k < null_subscript_cnt; k++) gv_currkey->base[sub_index[k]] = (csd->std_null_coll) ? STR_SUB_PREFIX : SUBSCRIPT_STDCOL_NULL; } else { if (gvcst_get(val)) update_nullcoll_mismatch_record = TRUE; } } if (discard_nullcoll_mismatch_record || update_nullcoll_mismatch_record) { temp = (unsigned char *)format_targ_key(key_buffer, MAX_ZWR_KEY_SZ, gv_currkey, TRUE); fmtd_key_len = (int)(temp - key_buffer); key_buffer[fmtd_key_len] = '\0'; if (discard_nullcoll_mismatch_record) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_DBDUPNULCOL, 4, LEN_AND_STR(key_buffer), v.str.len, v.str.addr); else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_DBDUPNULCOL, 4, LEN_AND_STR(key_buffer), val->str.len, val->str.addr); if (discard_nullcoll_mismatch_record) ok_to_put = FALSE; } gv_currkey = orig_gv_currkey_ptr; } if (ok_to_put) { if (gvnh_reg->gd_reg->null_subs) { for (k = 0; k < null_subscript_cnt; k++) gv_currkey->base[sub_index[k]] = csd->std_null_coll ? SUBSCRIPT_STDCOL_NULL : STR_SUB_PREFIX; } if (putting_a_sn) { gv_currkey->base[gv_currkey->end - (SPAN_SUBS_LEN + 1)] = 0; gv_currkey->end -= (SPAN_SUBS_LEN + 1); v.str.addr = sn_hold_buff; v.str.len = sn_hold_buff_pos; } if (v.str.len > max_rec) { bin_call_db(ERR_COR, CORRUPTNODE, iter, global_key_count, 0, NULL); bin_call_db(ERR_COR, REC2BIG, v.str.len, max_rec, REG_LEN_STR(gvnh_reg->gd_reg)); assert(mupip_error_occurred); break; } if (gvnh_reg->gvspan) bin_call_db(BIN_PUT_GVSPAN, 0, (INTPTR_T)&v, (INTPTR_T)gvnh_reg, 0, NULL); else bin_call_db(BIN_PUT, 0, (INTPTR_T)&v, 0, 0, NULL); if (mupip_error_occurred) { if ((ERR_JNLFILOPN == error_condition) || (ERR_DBPRIVERR == error_condition) || (ERR_GBLOFLOW == error_condition)) { first_failed_rec_count = iter; insert_reg_to_list(reg_list, gv_cur_region, &num_of_reg); mu_load_error = TRUE; failed_record_count++; break; } else { if (ERR_DBFILERR == error_condition) failed_record_count++; bin_call_db(ERR_COR, CORRUPTNODE, iter, global_key_count, 0, NULL); file_offset = file_offset_base + ((unsigned char *)rp - ptr_base); util_out_print("!_!_at File offset : [0x!16@XQ]", TRUE, &file_offset); DISPLAY_CURRKEY; DISPLAY_VALUE("!_!_Value :"); } ONERROR_PROCESS; } mu_load_error = FALSE; if (!is_hidden_subscript && (max_subsc_len < (gv_currkey->end + 1))) max_subsc_len = gv_currkey->end + 1; if (max_data_len < v.str.len) max_data_len = v.str.len; if (putting_a_sn) putting_a_sn = FALSE; else { if (!(discard_nullcoll_mismatch_record || update_nullcoll_mismatch_record)) { key_count++; global_key_count++; } } } } } if (encrypted_version) { assert(NULL != encrypted_hash_array_ptr); free(encrypted_hash_array_ptr); if ('9' == hdr_lvl) { assert(NULL != null_iv_array_ptr); free(null_iv_array_ptr); } } free(tmp_gvkey); free(sn_gvkey); free(save_orig_key); if (NULL != sn_hold_buff) free(sn_hold_buff); file_input_close(); tmp_rec_count = (iter == begin) ? iter : iter - 1; if (0 != first_failed_rec_count) { if (tmp_rec_count > first_failed_rec_count) SNPRINTF(msg_buff, SIZEOF(msg_buff), "%" PRIu64 " to %" PRIu64, first_failed_rec_count, tmp_rec_count); else SNPRINTF(msg_buff, SIZEOF(msg_buff), "%" PRIu64, tmp_rec_count); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_RECLOAD, 2, LEN_AND_STR(msg_buff)); } util_out_print("LOAD TOTAL!_!_Key Cnt: !@UQ Max Subsc Len: !UL Max Data Len: !UL", TRUE, &key_count, max_subsc_len, max_data_len); if (failed_record_count) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_FAILEDRECCOUNT, 1, &failed_record_count); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) MAKE_MSG_INFO(ERR_LOADRECCNT), 1, &tmp_rec_count); if (failed_record_count) mupip_exit(error_condition); if (mu_ctrly_occurred) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_LOADCTRLY); mupip_exit(ERR_MUNOFINISH); } } gvnh_reg_t *bin_call_db(int routine, int err_code, INTPTR_T parm1, INTPTR_T parm2, int strlen, unsigned char* str) { /* In order to duplicate the VMS functionality, which is to trap all errors in mupip_load_ch and * continue in bin_load after they occur, it is necessary to call these routines from a * subroutine due to the limitations of condition handlers and unwinding on UNIX */ gvnh_reg_t *gvnh_reg = NULL; gd_region *dummy_reg; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ESTABLISH_RET(mupip_load_ch, gvnh_reg); switch (routine) { case BIN_PUT_GVSPAN: /* The below macro finishes the task of GV_BIND_NAME_AND_ROOT_SEARCH * (e.g. setting gv_cur_region for spanning globals). */ gvnh_reg = (gvnh_reg_t *)parm2; GV_BIND_SUBSNAME_IF_GVSPAN(gvnh_reg, gd_header, gv_currkey, dummy_reg); /* WARNING: fall-through */ case BIN_PUT: op_gvput((mval *)parm1); break; case BIN_BIND: GV_BIND_NAME_AND_ROOT_SEARCH((gd_addr *)parm1, (mname_entry *)parm2, gvnh_reg); break; case ERR_COR: /* We use rts_error_csa instead of gtm_putmsg_csa here even though we are eating the error with * mupip_load_ch. This takes advantage of logic mupip_load_ch for cleaning up gv_target. * mupip_load_ch also sets the global mupip_error_occurred to TRUE, which is used in the main loop * of bin_load to indicate there was a problem with the current global node and to move onto the next * global node. */ switch(err_code) { case DBBADNSUB: RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBBADNSUB, 2, strlen, str); break; case DBCMPNZRO: RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBCMPNZRO, 2, strlen, str); break; case DBKEYMX: RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBKEYMX, 2, strlen, str); break; case DBRSIZMN: RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRSIZMN, 2, strlen, str); break; case DBRSIZMX: RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRSIZMX, 2, strlen, str); break; case GVINVALID: RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_GVINVALID, 2, strlen, str); break; case KEY2BIG: RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(6) ERR_KEY2BIG, 4, parm1, parm2, strlen, str); break; case MAXNRSUBSCRIPTS: RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_MAXNRSUBSCRIPTS); break; case REC2BIG: RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(6) ERR_REC2BIG, 4, parm1, parm2, strlen, str); break; default: RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_CORRUPTNODE, 2, parm1, parm2); } case BIN_KILL: gvcst_kill(FALSE); break; } REVERT; return gvnh_reg; } fis-gtm-V7.0-005/sr_unix/gtmci_ch.c0000644000032200000250000000322014342376334015763 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 #include "stack_frame.h" #include "mv_stent.h" #include "error.h" #include "error_trap.h" #include "fgncal.h" #include "gtmci.h" #include "util.h" GBLREF boolean_t created_core, dont_want_core; GBLREF dollar_ecode_type dollar_ecode; GBLREF int mumps_status; GBLREF int4 exi_condition; GBLREF unsigned char *fgncal_stack, *msp; error_def(ERR_ASSERT); error_def(ERR_GTMASSERT); error_def(ERR_GTMASSERT2); error_def(ERR_GTMCHECK); error_def(ERR_MEMORY); error_def(ERR_OUTOFSPACE); error_def(ERR_STACKOFLOW); CONDITION_HANDLER(gtmci_ch) { char src_buf[MAX_ENTRYREF_LEN]; mstr src_line; START_CH(TRUE); if (DUMPABLE) { gtm_dump(); TERMINATE; } src_line.len = 0; src_line.addr = &src_buf[0]; set_zstatus(&src_line, MAX_ENTRYREF_LEN, SIGNAL, NULL, FALSE); if (msp < FGNCAL_STACK) /* restore stack to the last marked position */ fgncal_unwind(); else TREF(temp_fgncal_stack) = NULL; /* If fgncal_unwind() didn't run to clear this, we have to */ if (TREF(comm_filter_init)) TREF(comm_filter_init) = FALSE; /* Exiting from filters */ mumps_status = SIGNAL; UNWIND(NULL, NULL); } fis-gtm-V7.0-005/sr_unix/ci_ret_code.c0000644000032200000250000000337014342376334016453 0ustar librarygtc/**************************************************************** * * * 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 #include "stack_frame.h" #include "error.h" #include "gtmci.h" #include "op.h" #include "error_trap.h" GBLREF stack_frame *frame_pointer; GBLREF unsigned char *msp; GBLREF int mumps_status; /* Routine executed at level 1 (SFF_CI frame) of the current gtm environment * to return control from M to gtm_ci(). the longjmp returns control to dm_start * which in turn returns to gtm_ci(). */ void ci_ret_code(void) { assert(active_ch->ch == &mdb_condition_handler); longjmp(active_ch->jmp, -1); } /* Routine executed at level 0 from the bottom-most frame (base frame) of the current gtm environment - called when M routine does zgoto 0 */ void ci_ret_code_exit(void) { assert(active_ch->ch == &mdb_condition_handler); ci_ret_code_quit(); mumps_status = 0; longjmp(active_ch->jmp, -1); } /* Exit from the current Call-in environment */ void ci_ret_code_quit(void) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (frame_pointer->flags & SFF_CI) op_unwind(); gtmci_isv_restore(); /* restore $ECODE/$STACK of previous level in the nested call-ins */ op_unwind(); /* base frame of this call-in environment */ (TREF(gtmci_nested_level))--; /* restore frame_pointer stored at msp (see base_frame.c) */ frame_pointer = *(stack_frame**)msp; msp += SIZEOF(frame_pointer); } fis-gtm-V7.0-005/sr_unix/make_mode.h0000644000032200000250000000146114342376334016141 0ustar librarygtc/**************************************************************** * * * Copyright 2003, 2009 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. * * * ****************************************************************/ #ifndef MAKE_MODE_INCLUDED #define MAKE_MODE_INCLUDED rhdtyp *make_mode (int mode_index); #define DM_MODE 0 #define CI_MODE 1 #define CODE_LINES 3 #if defined(__ia64) #define CODE_SIZE (CODE_LINES * CALL_SIZE + EXTRA_INST_SIZE) #else #define CODE_SIZE (CODE_LINES * CALL_SIZE + SIZEOF(uint4) * EXTRA_INST) #endif /* __ia64 */ #include #endif fis-gtm-V7.0-005/sr_unix/comp_lits.c0000644000032200000250000000406014342376334016202 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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 "compiler.h" #include #include "mdq.h" #include "stringpool.h" #include "obj_file.h" GBLREF mliteral literal_chain; GBLREF spdesc stringpool; GBLREF unsigned short source_name_len; GBLREF mident routine_name; GBLDEF uint4 lits_text_size, lits_mval_size; void comp_lits(rhdtyp *rhead) { size_t offset; uint4 cnt; uint4 align_pad; mliteral *p; struct linkage_entry *linkagep; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Literal text pool is formed in stringpool except for the file name/path of the * source module and routine name which will be emitted by emit_literals immediately * following the literal text pool and is considered part of that text pool. */ offset = (stringpool.free - stringpool.base); offset += PADLEN(offset, NATIVE_WSIZE); rhead->src_full_name.len = source_name_len; rhead->src_full_name.addr = (char *)offset; offset += source_name_len; offset += PADLEN(offset, NATIVE_WSIZE); rhead->routine_name.len = routine_name.len; rhead->routine_name.addr = (char *)offset; offset += routine_name.len; offset += PADLEN(offset, NATIVE_WSIZE); for (linkagep = TREF(linkage_first); NULL != linkagep; linkagep = linkagep->next) { /* Compute space for linkage name usage (symbol names associated with each linkage symbol) */ linkagep->lit_offset = offset; offset += linkagep->symbol->name_len - 1; offset += PADLEN(offset, NATIVE_WSIZE); } lits_text_size = UINTCAST(offset); offset = 0; dqloop(&literal_chain, que, p) if (p->rt_addr < 0) { p->rt_addr = offset; offset += SIZEOF(mval); } lits_mval_size = UINTCAST(offset); } fis-gtm-V7.0-005/sr_unix/gtmci_isv.c0000644000032200000250000000443014342376334016176 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_string.h" #include "op.h" #include #include "stack_frame.h" #include "gtmci.h" #include "svnames.h" #include "gtm_savetraps.h" #include "error_trap.h" #include "mv_stent.h" GBLREF dollar_ecode_type dollar_ecode; GBLREF dollar_stack_type dollar_stack; GBLREF stack_frame *frame_pointer; static dollar_ecode_type dollar_ecode_ci; static dollar_stack_type dollar_stack_ci; static dollar_ecode_type *dollar_ecode_addr; static dollar_stack_type *dollar_stack_addr; void gtmci_isv_save(void) { gtm_savetraps(); /* Save either etrap or ztrap as appropriate */ op_newintrinsic(SV_ESTACK); /* Save $ECODE/$STACK values of previous environment into SFF_CI frame of * the current call-in environment. When this frame unwinds, their old values * will be restored in dollar_ecode_ci and dollar_stack_ci (see unwind logic * in ci_ret_code_quit) */ assert(frame_pointer->flags & SFF_CI); dollar_ecode_addr = &dollar_ecode_ci; push_stck(&dollar_ecode, SIZEOF(dollar_ecode), (void**)&dollar_ecode_addr, MVST_STCK); dollar_stack_addr = &dollar_stack_ci; push_stck(&dollar_stack, SIZEOF(dollar_stack), (void**)&dollar_stack_addr, MVST_STCK); ecode_init(); } void gtmci_isv_restore(void) { /* free the allocated $ECODE/$STACK storage before exiting from the current * call-in environment. NOTE: any changes here should be reflected in ecode_init() */ if (dollar_ecode.begin) free(dollar_ecode.begin); if (dollar_ecode.array) free(dollar_ecode.array); if (dollar_stack.begin) free(dollar_stack.begin); if (dollar_stack.array) free(dollar_stack.array); /* Restore the old values from dollar_ecode_ci and dollar_stack_ci */ memcpy(&dollar_ecode, &dollar_ecode_ci, SIZEOF(dollar_ecode)); memcpy(&dollar_stack, &dollar_stack_ci, SIZEOF(dollar_stack)); } fis-gtm-V7.0-005/sr_unix/error_return.c0000644000032200000250000001303014342376334016736 0ustar librarygtc/**************************************************************** * * * Copyright 2010, 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 "gtm_stdlib.h" #include "gtm_stdio.h" #include "error.h" #include "error_trap.h" #include "dollar_zlevel.h" #include #include "stack_frame.h" #include "golevel.h" #include "io.h" #include "send_msg.h" #define BASE_FRAME(fp) ((fp->type & SFT_DM) || (fp->flags & SFF_CI)) GBLREF stack_frame *frame_pointer; GBLREF unsigned short proc_act_type; GBLREF dollar_ecode_type dollar_ecode; /* structure containing $ECODE related information */ GBLREF int mumps_status; GBLREF stack_frame *error_frame; GBLREF io_desc *gtm_err_dev; GBLREF mval dollar_zstatus; error_def(ERR_JIUNHNDINT); error_def(ERR_REPEATERROR); void error_return(void) { int parent_level, unwcnt; stack_frame *curframe, *cur_counted_frame, *parent_counted_frame; boolean_t rethrow_needed = FALSE, dev_act_err, transcendental, zintrframe; DBGEHND((stderr, "error_return: Entered\n")); assert((frame_pointer->flags & SFF_ETRAP_ERR) || (error_frame == frame_pointer)); unwcnt = 0; /* Determine counted frame at the current $zlevel */ for (curframe = frame_pointer; (NULL != curframe) && !(curframe->type & SFT_COUNT) && !BASE_FRAME(curframe); curframe = curframe->old_frame_pointer) unwcnt++; /* (don't need to worry about trigger frames here as they are counted and stop the loop) */ DBGEHND((stderr, "error_return: cur_counted_frame found with unwind count %d\n", unwcnt)); cur_counted_frame = curframe; NULLIFY_ERROR_FRAME; /* reset error_frame */ dev_act_err = ((NULL != cur_counted_frame) && (cur_counted_frame->flags & SFF_ETRAP_ERR) && (cur_counted_frame->flags & SFF_DEV_ACT_ERR)); zintrframe = (SFT_ZINTR & curframe->type); if (dev_act_err || (0 != dollar_ecode.index)) rethrow_needed = TRUE; /* If the top level error is a device exception error, we do not want to unwind upto the parent frame but instead * rethrow the error at the current level and use $ztrap/$etrap exception handling. In case even that fails, * we will come again to error_return at which point, we unwind to the parent frame. */ if (!dev_act_err && NULL != curframe) { /* We need to unwind this frame to either rethrow error or MUM_TSTART. If this lands us on a trigger frame, * whether that is ok or not depends on whether we will end up rethrowing the error or doing a MUM_TSTART. If we * are rethrowing the error, we must not land on a trigger frame but must unroll it too. In the MUM_TSTART case, * we are resuming at that point via a simulated QUIT so this is ok. */ do { curframe = curframe->old_frame_pointer; unwcnt++; # ifdef GTM_TRIGGER if (rethrow_needed && (SFT_TRIGR & curframe->type)) { /* With a rethrow, we cannot use a trigger base-frame - back up one frame prior to the frame */ curframe = *(stack_frame **)(curframe + 1); unwcnt++; } # endif transcendental = ((SFT_ZTRAP & curframe->type) || (SFT_DEV_ACT & curframe->type)); } while (transcendental && (NULL != curframe)); } /* Check a couple of conditions that could turn off rethrow_needed: * * 1. If we ended up on a callin frame which needs to just return to the C caller (error handling abandonded but the error * code is returned to the caller). * 2. If we ended up on a direct mode frame - errors just get printed here. * 3. If we are unwinding a ZINTERRUPT frame (error handling abandoned - send message to operator log). */ if (rethrow_needed && BASE_FRAME(curframe)) { /* If the computed frame is a base frame for call-ins, we cannot do rethrow and must use MUM_TSTART to return to * the call-in invocation code (gtm_ci()). */ rethrow_needed = FALSE; DBGEHND((stderr, "error_return: rethrow_needed set to FALSE due to call-in base frame")); } if (rethrow_needed && zintrframe) { /* If we are unwinding a ZINTERRUPT frame, error processing stops here but send a message to the operator log * to notify users of the un-handled error. */ rethrow_needed = FALSE; DBGEHND((stderr, "error_return: rethrow_needed set to FALSE due to unwinding errant zinterrupt frame")); send_msg(VARLSTCNT(3) ERR_JIUNHNDINT, 2, dollar_zstatus.str.len, dollar_zstatus.str.addr); } parent_counted_frame = curframe; /* Not parent frame for device errors -- still at error counted frame */ /* Exit if we are at the bottom of the stack */ parent_level = dollar_zlevel() - 1; if ((NULL == parent_counted_frame) || (1 > parent_level)) { EXIT(dollar_ecode.error_last_ecode); } assert(0 < parent_level); DBGEHND((stderr, "error_return: unwcnt: %d rethrow_needed: %d dev_act_err: %d\n", unwcnt, rethrow_needed, dev_act_err)); GOFRAMES(unwcnt, FALSE, FALSE); assert(parent_counted_frame == frame_pointer); assert(!proc_act_type); if (rethrow_needed) { rts_error(VARLSTCNT(1) ERR_REPEATERROR); assert(FALSE); /* the previous rts_error() should not return */ } /* zero the error device just to be safe */ assert(NULL == gtm_err_dev); gtm_err_dev = NULL; if (!zintrframe) { if (parent_counted_frame->flags & SFF_CI) /* Unhandled error in call-in: return to gtm_ci */ mumps_status = dollar_ecode.error_last_ecode; MUM_TSTART; } return; /* Continue in caller (likely getframe macro) */ } fis-gtm-V7.0-005/sr_unix/map_sym.c0000644000032200000250000000640214342376334015660 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "collseq.h" #include #include "lv_val.h" /* needed for "fgncal.h" */ #include "fgncal.h" #include "iosp.h" #include "io.h" #include "trans_log_name.h" #include "error.h" #include "gtmmsg.h" #define ERR_FGNSYM \ { \ fgn_closepak(handle, INFO); \ return FALSE; \ } error_def(ERR_COLLFNMISSING); /* Maps all the collation related symbols from the act shared library. * Return TRUE/FALSE based on mapping success. */ boolean_t map_collseq(mstr *fspec, collseq *ret_collseq) { mstr fspec_trans; char buffer[MAX_TRANS_NAME_LEN]; int status; void_ptr_t handle; boolean_t coll_lib_found; static MSTR_CONST(xform_sym_1, "gtm_ac_xform_1"); static MSTR_CONST(xback_sym_1, "gtm_ac_xback_1"); static MSTR_CONST(xform_sym, "gtm_ac_xform"); static MSTR_CONST(xback_sym, "gtm_ac_xback"); static MSTR_CONST(verify_sym, "gtm_ac_verify"); static MSTR_CONST(xutil_sym, "gtm_ac_xutil"); static MSTR_CONST(version_sym, "gtm_ac_version"); DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; coll_lib_found = FALSE; if (SS_NORMAL != (status = TRANS_LOG_NAME(fspec, &fspec_trans, buffer, SIZEOF(buffer), do_sendmsg_on_log2long))) return FALSE; if (NULL == (handle = fgn_getpak(buffer, INFO))) return FALSE; if ((ret_collseq->xform = fgn_getrtn(handle, &xform_sym_1, SUCCESS))) { if ((ret_collseq->xback = fgn_getrtn(handle, &xback_sym_1, SUCCESS))) { coll_lib_found = TRUE; ret_collseq->argtype = 1; } else { if (!TREF(skip_gtm_putmsg)) { /* Warn about the missing routine */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_COLLFNMISSING, 3, LEN_AND_LIT("gtm_ac_xback_1()"), ret_collseq->act); } ERR_FGNSYM; } } if ( FALSE == coll_lib_found) { if ((ret_collseq->xform = fgn_getrtn(handle, &xform_sym, SUCCESS))) { if ((ret_collseq->xback = fgn_getrtn(handle, &xback_sym, SUCCESS))) { coll_lib_found = TRUE; ret_collseq->argtype = 0; } else { if (!TREF(skip_gtm_putmsg)) { /* Warn about the missing routine */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_COLLFNMISSING, 3, LEN_AND_LIT("gtm_ac_xback()"), ret_collseq->act); } ERR_FGNSYM; } } else /* Neither xform_1 or xform is found */ ERR_FGNSYM; } assert(TRUE == coll_lib_found); if (!(ret_collseq->verify = fgn_getrtn(handle, &verify_sym, INFO))) ERR_FGNSYM; if (!(ret_collseq->version = fgn_getrtn(handle, &version_sym, INFO))) ERR_FGNSYM; /* * This is not fatal so don't warn, ERR_FGNSYM or close the dll. * The xutil routine is only needed for the -2/2 * zatransform collation functionality. Other operations do not use it. */ if (!(ret_collseq->xutil = fgn_getrtn(handle, &xutil_sym, SUCCESS))) { ret_collseq->xutil = NULL; } return TRUE; } fis-gtm-V7.0-005/sr_unix/errorsp.h0000644000032200000250000006206414342376334015722 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef ERRORSP_H_INCLUDED #define ERRORSP_H_INCLUDED #include #include "gtm_stdio.h" #include "have_crit.h" #include "gtmimagename.h" #include #include "stack_frame.h" #ifdef __MVS__ # define GTMCORENAME "gtmcore" #endif /* Condition handler stack definitions: * * Global: chnd - Base of condition handler stack. * chnd_end - Top of condition handler stack. * chnd_incr - Increment for next expansion. * ctxt - Top condition handler stack currently in use. * active_ch - Top of the inactive (i.e. available for invocation) condition handler stack. * * Note that START_CH used at the top of every condition handler defined "current_ch" as a stack variable * (i.e. "automatic" variable in some lingos). The current_ch var in a given (C) frame always points to the * condition handler entry active for that frame because active_ch can change in a nested handler situation. * Note also that current_ch has the form of an index into the condition handler array because the stack * can be extended via re-allocation. * * Condition handler stack starts off at a modest CONDSTK_INITIAL_INCR frames. If needed, (to cover trigger * and/or spanning node stack frames which nest handlers), it can expand up to MAX_CON * * Note, if ever call-ins fully support TP processing such that each trigger frame could have it's own callin * in addition to the other handlers for each trigger frame, the maximums may need to be re-visited. */ #ifdef DEBUG # define CONDSTK_INITIAL_INCR 5 /* Lower initial limit for DEBUG to exercise extensions. Note that values below 5 cause * issues with nested malloc()s when using certain gtmdbglvl values. */ #else # define CONDSTK_INITIAL_INCR 8 /* Initial increment value used when expanding condition handler stack */ #endif #define CONDSTK_MAX_INCR 128 /* Increment doubles each time expanded till hits this level */ #define CONDSTK_MAX_STACK 512 /* Actual max is approx 504 due to arithmetic progression */ #define CONDSTK_RESERVE 4 /* Reserve 4 frames for when process_exiting */ #define CONDITION_HANDLER(name) ch_ret_type name(int arg) /* Count of arguments the TPRETRY error will make available for tp_restart to use */ #define TPRESTART_ARG_CNT 6 typedef void ch_ret_type; /* Note that the condition_handler structure layout is relied upon by assembly code (see chnd_size, chnd_* in error.si). * Any changes here need corresponding changes in error.si. */ typedef struct condition_handler_struct { struct condition_handler_struct *save_active_ch; /* -> Previous active condition handler */ boolean_t ch_active; /* True when *THIS* condition handler is active (not usable) */ uint4 dollar_tlevel; /* $tlevel at time of ESTABLISH; needed at UNWIND time. * Used only in DEBUG code. But defined in PRO to keep error.si * simple (it keeps track of offsets of members in structures * and we dont want it to be conditional on PRO vs DBG). */ ch_ret_type (*ch)(); /* Condition handler address */ jmp_buf jmp; /* setjmp/longjmp buffer associated with ESTABLISH point */ intrpt_state_t intrpt_ok_state; /* intrpt_ok_state at time of ESTABLISH_RET/ESTABLISH */ } condition_handler; /* The values below usually expand as GBLREF. If CHEXPAND is defined, they will * expand as GBLDEF instead (as it does in gbldefs.c). */ #ifdef CHEXPAND # define GBLEXP GBLDEF #else # define GBLEXP GBLREF #endif GBLEXP condition_handler *chnd, *ctxt, *active_ch, *chnd_end; GBLEXP int severity, chnd_incr; /* Don't do these GBLREFs if doing above GBLDEFs as this means we are in gbldefs.c where the below * are also defined so a REF is likely to cause problems. */ #ifndef CHEXPAND GBLREF int4 error_condition; GBLREF err_ctl merrors_ctl; GBLREF void (*restart)(); GBLREF int process_exiting; #endif #define WARNING 0 #define SUCCESS 1 #define ERROR 2 #define INFO 3 #define SEVERE 4 #define SEV_MSK 7 #define IS_GTM_ERROR(err) \ (((err & 0x0FFFFFFF) & FACMASK(merrors_ctl.facnum)) && (MSGMASK(err, merrors_ctl.facnum) <= merrors_ctl.msg_cnt)) #define CHECKHIGHBOUND(hptr) assert(hptr < (chnd_end + (!process_exiting ? 0 : CONDSTK_RESERVE))) #define CHECKLOWBOUND(hptr) assert(hptr >= (&chnd[0] - 1)) /* Low check for chnd - 1 in case last handler setup new handler */ #define SIGNAL error_condition #define SEVERITY severity #define FATALFAILURE(X) (X > 4) #define CHANDLER_EXISTS (active_ch >= &chnd[0]) #define MAKE_MSG_WARNING(x) ((x) & ~SEV_MSK | WARNING) #define MAKE_MSG_SUCCESS(x) ((x) & ~SEV_MSK | SUCCESS) #define MAKE_MSG_ERROR(x) ((x) & ~SEV_MSK | ERROR) #define MAKE_MSG_INFO(x) ((x) & ~SEV_MSK | INFO) #define MAKE_MSG_SEVERE(x) ((x) & ~SEV_MSK | SEVERE) #define ERROR_RTN error_return /* The CHTRACEPOINT macros are in place for CH debugging if necessary */ #ifdef DEBUG # ifdef DEBUG_CH # define CHTRACEPOINT ch_trace_point(); # ifdef CHEXPAND void ch_trace_point() {return;} # endif # else # define CHTRACEPOINT # endif #else # define CHTRACEPOINT #endif #define PRN_ERROR util_cond_flush(); /* MUM_TSTART unwinds the current C-stack and restarts executing generated code from the top of the current M-stack. * With the introduction of call-ins, there could be multiple mdb_condition_handlers stacked up in chnd stack. * The active context should be reset to the youngest mdb_condition_handler created by the current gtm/call-in invocation. We have * two flavors depending on if triggers are enabled or not. * * Note, like the UNWIND macro below, these MUM_TSTART macros cannot disable interrupts across the longjmp() because the * assembler version ESTABLISH macro does not support re-enabling interrupts at this time. When that support is added, the * ENABLE_INTERRUPTS macro can be removed from these MUM_TSTART macros and handled instead in the longjmp() return of the * setjmp() call there. */ /* Note the 3rd assert makes sure we are NOT returning to a ANY frame which does not have a valid msp to support a return * since a call to op_gvput or op_kill does not save a return addr in the M stackframe but only in the C stackframe. But * if proc_act_type is non-zero we set an error frame flag and getframe instead detours to error_return which deals with * the module appropriately. */ #define MUM_TSTART { \ GBLREF unsigned short proc_act_type; \ GBLREF int process_exiting; \ \ intrpt_state_t prev_intrpt_state; \ \ assert(!multi_thread_in_use); \ assert(!process_exiting); \ CHTRACEPOINT; \ DEFER_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ for ( ;(ctxt > &chnd[0]) && (ctxt->ch != &mdb_condition_handler); ctxt--); \ CHECKLOWBOUND(ctxt); \ assert((ctxt->ch == &mdb_condition_handler) \ && (FALSE == ctxt->save_active_ch->ch_active)); \ /* Absolutely critical that this *never* occur hence assertpro() */ \ assertpro(!(SSF_NORET_VIA_MUMTSTART & frame_pointer->flags) || (0 != proc_act_type) \ || (SFF_ETRAP_ERR & frame_pointer->flags)); \ DBGEHND((stderr, "MUM_TSTART: Frame 0x"lvaddr" dispatched\n", frame_pointer)); \ ctxt->ch_active = FALSE; \ restart = mum_tstart; \ active_ch = ctxt; \ ENABLE_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ longjmp(ctxt->jmp, 1); \ } /* Assumed that when this macro is used, interrupts are disabled. One case where that is not done exists which is in * sr_unix/gtm_asm_establish.c (called by assembler routines). Once the assembler ESTABLISH macros support doing the * disable/enable of interrupts this routine can add an assert that interrupts are disabled. */ #define GTM_ASM_ESTABLISH { /* So named because gtm_asm_establish does exactly this */ \ GBLREF uint4 dollar_tlevel; \ \ assert(IS_PTHREAD_LOCKED_AND_HOLDER); \ CHTRACEPOINT; \ ctxt++; \ if (ctxt >= (chnd_end + (!process_exiting ? 0 : CONDSTK_RESERVE))) \ condstk_expand(); \ CHECKHIGHBOUND(ctxt); \ ctxt->save_active_ch = active_ch; \ ctxt->ch_active = FALSE; \ DEBUG_ONLY(ctxt->dollar_tlevel = dollar_tlevel;) \ active_ch = ctxt; \ } /* Currently the ESTABLISH_NOJMP macro is only used internal to this header file - if ever used outside this header file, it * needs to be protected with DEFER/ENABLE_INTERRUPTS macros. */ #define ESTABLISH_NOJMP(x) { \ GTM_ASM_ESTABLISH; \ ctxt->ch = x; \ } #define ESTABLISH_NOUNWIND(x) { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ ESTABLISH_NOJMP(x); \ ENABLE_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ } #define ESTABLISH_RET(x, ret) { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ ESTABLISH_NOJMP(x); \ /* Save "prev_intrpt_state" input parameter in "ctxt". This is later \ * needed if/when "setjmp" returns non-zero (through longjmp). At that \ * time, local variable values are not restored correctly on Solaris \ * most likely because the local variable "prev_intrpt_state" defined \ * in the ESTABLISH_RET macro block goes out-of-scope the moment the \ * macro returns whereas on other platforms it stays in-scope most \ * likely because this gets allocated in the outermost {...} of the \ * calling function. Just in case this behavior changes in the future, \ * we store it in a global var for all platforms (not just Solaris). \ */ \ ctxt->intrpt_ok_state = prev_intrpt_state; \ if (0 != setjmp(ctxt->jmp)) \ { \ prev_intrpt_state = ctxt->intrpt_ok_state; \ REVERT; \ /* The only way we should reach here is if a "longjmp" happened \ * inside the condition handler "x". In that case, we dont \ * know the state of the global variable "intrpt_ok_state" so \ * reset it to what it was before the DEFER_INTERRUPTS macro \ * call in the ESTABLISH_RET macro. \ */ \ if (!multi_thread_in_use) \ { \ assert(INTRPT_OK_TO_INTERRUPT <= prev_intrpt_state); \ assert(INTRPT_NUM_STATES > prev_intrpt_state); \ intrpt_ok_state = prev_intrpt_state; \ } \ return ret; \ } else \ ENABLE_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ } #ifdef __cplusplus /* must specify return value (if any) for C++ */ # define ESTABLISH(x, ret) ESTABLISH_RET(x, ret) #else # define ESTABLISH(x) { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ ESTABLISH_NOJMP(x); \ /* See ESTABLISH_RET macro comment for below assert */ \ ctxt->intrpt_ok_state = prev_intrpt_state; \ if (0 != setjmp(ctxt->jmp)) \ { \ prev_intrpt_state = ctxt->intrpt_ok_state; \ REVERT; \ /* See ESTABLISH_RET macro comment for below assert */ \ if (!multi_thread_in_use) \ { \ assert(INTRPT_OK_TO_INTERRUPT <= prev_intrpt_state); \ assert(INTRPT_NUM_STATES > prev_intrpt_state); \ intrpt_ok_state = prev_intrpt_state; \ } \ return; \ } else \ ENABLE_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ } # define ESTABLISH_NORET(x, did_long_jump) \ { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ did_long_jump = FALSE; \ ESTABLISH_NOJMP(x); \ /* See ESTABLISH_RET macro comment for below assert */ \ ctxt->intrpt_ok_state = prev_intrpt_state; \ if (0 != setjmp(ctxt->jmp)) \ { \ prev_intrpt_state = ctxt->intrpt_ok_state; \ did_long_jump = TRUE; \ /* See ESTABLISH_RET macro comment for below assert */ \ assert(INTRPT_OK_TO_INTERRUPT <= prev_intrpt_state); \ assert(INTRPT_NUM_STATES > prev_intrpt_state); \ /* Assert "intrpt_ok_state" and "prev_intrpt_state" are same in \ * "dbg" but restore "intrpt_ok_state" in "pro" just in case \ */ \ assert(prev_intrpt_state == intrpt_ok_state); \ intrpt_ok_state = prev_intrpt_state; \ } else \ ENABLE_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ } # define WITH_CH(HANDLER, OP, ERR_OP) \ MBSTART { \ boolean_t ERROR_SEEN; \ \ ESTABLISH_NORET(HANDLER, ERROR_SEEN); \ if (!ERROR_SEEN) \ { \ OP; \ REVERT; \ } else \ { \ REVERT; \ ERR_OP; \ } \ } MBEND #endif #define REVERT { \ intrpt_state_t prev_intrpt_state; \ \ assert(IS_PTHREAD_LOCKED_AND_HOLDER); \ CHTRACEPOINT; \ DEFER_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ active_ch = ctxt->save_active_ch; \ CHECKHIGHBOUND(active_ch); \ CHECKLOWBOUND(active_ch); \ ctxt--; \ CHECKLOWBOUND(ctxt); \ ENABLE_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ } #define CONTINUE { \ intrpt_state_t prev_intrpt_state; \ \ /* If threads are in use, a CONTINUE inside a condition-handler transitions \ * the thread from error-handling to a no-error state which can cause an \ * out-of-design situation because we assume that all threads which go \ * through any condition-handler exit and never resume execution. \ */ \ assert(!multi_thread_in_use); \ CHTRACEPOINT; \ DEFER_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ active_ch++; \ CHECKHIGHBOUND(active_ch); \ chnd[current_ch].ch_active = FALSE; \ ENABLE_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ return; \ } #define DRIVECH(x) { \ intrpt_state_t prev_intrpt_state; \ \ error_def(ERR_TPRETRY); /* BYPASSOK */ \ \ assert(IS_PTHREAD_LOCKED_AND_HOLDER); \ /* See comment before SET_FORCED_THREAD_EXIT in GTM_PTHREAD_EXIT macro \ * for why the below PTHREAD_EXIT_IF_FORCED_EXIT is necessary. \ */ \ if (multi_thread_in_use) \ PTHREAD_EXIT_IF_FORCED_EXIT; \ CHTRACEPOINT; \ if (ERR_TPRETRY != error_condition) \ ch_cond_core(); \ if (NULL != active_ch) \ { \ DEFER_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ while (active_ch >= &chnd[0]) \ { \ if (!active_ch->ch_active) \ break; \ active_ch--; \ } \ ENABLE_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ if (active_ch >= &chnd[0] && *active_ch->ch) \ (*active_ch->ch)(x); \ else \ ch_overrun(); \ } else \ { /* No condition handler has been ESTABLISHed yet. \ * Most likely error occuring at process startup. \ * Just print error and exit with error status. \ */ \ stop_image_ch(); \ } \ assert((SUCCESS == SEVERITY) || (INFO == SEVERITY)); \ } #define NEXTCH { \ assert(IS_PTHREAD_LOCKED_AND_HOLDER); \ CHTRACEPOINT; \ chnd[current_ch].ch_active = FALSE; \ DRIVECH(arg); \ /* If ever DRIVECH does a CONTINUE and returns back to us, we \ * need to do a CONTINUE as well so we re-establish ourselves \ * on the condition handler stack. This cancels out the \ * START_CH (done before the NEXTCH invocation) which would \ * have removed us from the condition handler stack. This way \ * we restore the condition handler stack to what it was at \ * entry into the current condition handler before returning \ * on a successfully handled condition. Assert that we should \ * never have been in a DUMPABLE state if DRIVECH returned. \ */ \ assert(!DUMPABLE); \ CONTINUE; \ } /* Should never unwind a condition handler established with ESTABLISH_NOUNWIND. Currently t_ch and dbinit_ch are the only ones. * Use function pointer (t_ch_fnptr global variable) instead of &tch (and likewise for dbinit_ch) as it will otherwise bloat * executables that don't need to pull in t_ch/dbinit_ch (e.g. gtcm_shmclean). */ #define UNWINDABLE(unw_ch) ((t_ch_fnptr != unw_ch->ch) && (dbinit_ch_fnptr != unw_ch->ch)) /* Note, since we are not initially changing the assembler ESTABLISH version to also include deferring/enabling of interrupts, * we cannot leave the interrupt block in effect during the longjmp(). But once that support is in place, we can do away with * re-enabling interrupts and let the longjmp() return from setjmp() take care of it. */ #define UNWIND(dummy1, dummy2) { \ GBLREF int process_exiting; \ GBLREF boolean_t ok_to_UNWIND_in_exit_handling; \ GBLREF volatile boolean_t in_wcs_recover; \ GBLREF uint4 dollar_tlevel; \ GBLREF ch_ret_type (*t_ch_fnptr)(); /* Function pointer to t_ch */ \ GBLREF ch_ret_type (*dbinit_ch_fnptr)();/* Function pointer to dbinit_ch */\ \ intrpt_state_t prev_intrpt_state; \ \ /* If threads are in use, an UNWIND inside a condition-handler transitions \ * the thread from error-handling to a no-error state which can cause an \ * out-of-design situation because we assume that all threads which go \ * through any condition-handler exit and never resume execution. \ */ \ assert(!multi_thread_in_use); \ assert(!process_exiting || ok_to_UNWIND_in_exit_handling); \ /* When we hit an error in the midst of commit, t_ch/t_commit_cleanup should be invoked \ * and clean it up before any condition handler on the stack unwinds. \ */ \ assert((0 == have_crit(CRIT_IN_COMMIT)) || in_wcs_recover); \ CHTRACEPOINT; \ DEFER_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ chnd[current_ch].ch_active = FALSE; \ active_ch++; \ CHECKHIGHBOUND(active_ch); \ ctxt = active_ch; \ ENABLE_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ assert(UNWINDABLE(active_ch)); \ assert(active_ch->dollar_tlevel == dollar_tlevel); \ longjmp(active_ch->jmp, -1); \ } /* This macro short-circuits the condition_handler for INFO or SUCCESS errors other than CTRLC and CTRLY and returns control * to the command after the rts_error_csa invocation, which is what the base condition handler (mdb_condition_handler or util_ch) * would do anyway, but if we are going to return control we don't want any condition handlers messing with state. * The base condition handlers and other code in the utilities have special logic to treat CTRLC and CTRLY as operator * actions with a semantic significance, so this macro does not intercept those. * In the MUMPS run-time, unless in a direct mode frame, we skip displaying the error because we don't want to mess with * the application's design for user interaction. */ #define START_CH(continue_on_success) /* info is a form of success */ \ GBLREF boolean_t ctrlc_on; \ \ error_def(ERR_CTRLC); /* BYPASSOK */ \ error_def(ERR_CTRLY); /* BYPASSOK */ \ \ int current_ch; \ intrpt_state_t prev_intrpt_state; \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ assert(IS_PTHREAD_LOCKED_AND_HOLDER); \ CHTRACEPOINT; \ DEFER_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ current_ch = (active_ch - chnd); \ active_ch->ch_active = TRUE; \ active_ch--; \ CHECKLOWBOUND(active_ch); \ ENABLE_INTERRUPTS(INTRPT_IN_CONDSTK, prev_intrpt_state); \ DBGEHND((stderr, "%s: Condition handler entered at line %d - arg: %d SIGNAL: %d\n", \ __FILE__, __LINE__, arg, SIGNAL)); \ if ((continue_on_success) \ && ((SUCCESS == SEVERITY) \ || ((INFO == SEVERITY) && ((int)ERR_CTRLY != SIGNAL) \ && ((int)ERR_CTRLC != SIGNAL)))) \ { \ if (ctrlc_on || !IS_GTM_IMAGE) \ PRN_ERROR; \ CONTINUE; \ } #define MDB_START void stop_image(void); void stop_image_conditional_core(void); void stop_image_no_core(void); #define TERMINATE { \ CHTRACEPOINT; \ if (SUPPRESS_DUMP) \ { \ if (0 == exi_condition) \ exi_condition = SIGQUIT;\ stop_image_no_core(); \ } else \ stop_image(); \ } #define SUPPRESS_DUMP (created_core || dont_want_core) #define DUMP_CORE gtm_dump_core(); #define MUMPS_EXIT { \ GBLREF int4 exi_condition; \ GBLREF int mumps_status; \ \ /* We are about to manipulate global variable "mumps_status" \ * and "exi_condition" so assert we are not inside threaded code. \ */ \ assert(!multi_thread_in_use); \ CHTRACEPOINT; \ mumps_status = SIGNAL; \ exi_condition = -mumps_status; \ EXIT(-exi_condition); \ } #define PROCDIE(x) UNDERSCORE_EXIT(x) /* No exit handler, no cleanup, just die */ error_def(ERR_ASSERT); error_def(ERR_GTMASSERT); error_def(ERR_GTMASSERT2); error_def(ERR_GTMCHECK); error_def(ERR_MEMORY); error_def(ERR_STACKOFLOW); error_def(ERR_OUTOFSPACE); #define DUMP (SIGNAL == (int)ERR_ASSERT \ || SIGNAL == (int)ERR_GTMASSERT \ || SIGNAL == (int)ERR_GTMASSERT2 \ || SIGNAL == (int)ERR_GTMCHECK /* BYPASSOK */ \ || SIGNAL == (int)ERR_MEMORY \ || SIGNAL == (int)ERR_STACKOFLOW) /* true if above or SEVERE and GTM error (perhaps add some "system" errors) */ #define DUMPABLE ((SEVERITY == SEVERE) && IS_GTM_ERROR(SIGNAL) \ && (SIGNAL != (int)ERR_OUTOFSPACE) \ DEBUG_ONLY(&& (WBTEST_ENABLED(WBTEST_SKIP_CORE_FOR_MEMORY_ERROR) \ ? (SIGNAL != (int)ERR_MEMORY) : TRUE))) unsigned char *set_zstatus(mstr *src, int max_len, int arg, unsigned char **ctxtp, boolean_t need_rtsloc); #define SET_ZSTATUS(ctxt) set_zstatus(&src_line_d, src_line_max, SIGNAL, ctxt, TRUE); #define MSG_OUTPUT (1) #define EXIT_HANDLER(x) #define SEND_CALLERID(callee) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_CALLERID, 3, LEN_AND_STR((callee)), caller_id(1)); #define UNIX_EXIT_STATUS_MASK 0xFF void err_init(void (*x)()); void condstk_expand(void); void gtm_dump(void); void gtm_dump_core(void); void gtm_fork_n_core(void); void ch_cond_core(void); void ch_overrun(void); void util_cond_flush(void); void stop_image_ch(void); CONDITION_HANDLER(dbopen_ch); CONDITION_HANDLER(gtmci_ch); CONDITION_HANDLER(gtmci_init_ch); CONDITION_HANDLER(gtmsecshr_cond_hndlr); CONDITION_HANDLER(gvcst_init_autoDB_ch); CONDITION_HANDLER(gvtr_tpwrap_ch); CONDITION_HANDLER(iob_io_error1); CONDITION_HANDLER(iob_io_error2); CONDITION_HANDLER(mu_cre_file_ch); CONDITION_HANDLER(mu_extract_handler); CONDITION_HANDLER(mu_extract_handler1); CONDITION_HANDLER(mu_extract_handler2); CONDITION_HANDLER(mu_rndwn_all_helper_ch); CONDITION_HANDLER(mu_rndwn_repl_instance_ch); CONDITION_HANDLER(mu_rndwn_replpool_ch); CONDITION_HANDLER(mupip_upgrade_ch); CONDITION_HANDLER(omi_dbms_ch); CONDITION_HANDLER(rc_dbms_ch); CONDITION_HANDLER(read_source_ch); CONDITION_HANDLER(source_ch); #ifdef GTM_TRIGGER CONDITION_HANDLER(gtm_trigger_ch); CONDITION_HANDLER(gtm_trigger_complink_ch); CONDITION_HANDLER(op_fnztrigger_ch); CONDITION_HANDLER(trigger_tpwrap_ch); #endif CONDITION_HANDLER(gvcst_redo_root_search_ch); CONDITION_HANDLER(gvcst_data_ch); CONDITION_HANDLER(gvcst_get_ch); CONDITION_HANDLER(gvcst_kill_ch); CONDITION_HANDLER(gvcst_order_ch); CONDITION_HANDLER(gvcst_put_ch); CONDITION_HANDLER(gvcst_query_ch); CONDITION_HANDLER(gvcst_queryget_ch); CONDITION_HANDLER(gvcst_zprevious_ch); CONDITION_HANDLER(gvcst_spr_data_ch); CONDITION_HANDLER(gvcst_spr_kill_ch); CONDITION_HANDLER(gvcst_spr_order_ch); CONDITION_HANDLER(gvcst_spr_zprevious_ch); CONDITION_HANDLER(gvcst_spr_query_ch); CONDITION_HANDLER(gvcst_spr_queryget_ch); CONDITION_HANDLER(op_fnzpeek_ch); CONDITION_HANDLER(op_fnzpeek_getpool_ch); CONDITION_HANDLER(timer_cancel_ch); CONDITION_HANDLER(trigger_upgrade_ch); #endif fis-gtm-V7.0-005/sr_unix/gv_trigger.c0000644000032200000250000022542614342376334016363 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2021 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_string.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include /* for rtn_tabent in gv_trigger.h */ #include "gv_trigger.h" #include "gtm_trigger.h" #include "error.h" #include "filestruct.h" #include "gdscc.h" #include "gdskill.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "targ_alloc.h" #include "hashtab_mname.h" /* for COMPUTE_HASH_MNAME */ #include "tp_set_sgm.h" #include "t_begin.h" #include "t_retry.h" #include "tp_restart.h" #include "gvcst_protos.h" #include "mv_stent.h" /* for COPY_SUBS_TO_GVCURRKEY macro & PUSH_MV_STENT macros */ #include "gvsub2str.h" /* for COPY_SUBS_TO_GVCURRKEY macro (gvsub2str prototype) */ #include "format_targ_key.h" /* for COPY_SUBS_TO_GVCURRKEY macro (format_targ_key prototype) */ #include "mvalconv.h" /* for mval2i */ #include "op.h" /* for op_tstart */ #include "toktyp.h" #include "patcode.h" #include "compiler.h" /* for MAX_SRCLINE */ #include "min_max.h" #include "stringpool.h" #include "subscript.h" #include "op_tcommit.h" /* for op_tcommit prototype */ #include "cdb_sc.h" #include "gv_trigger_protos.h" #include "trigger_fill_xecute_buffer.h" #include "strpiecediff.h" #include "gtm_utf8.h" /* for CHSET_M_STR and CHSET_UTF8_STR */ #include "trigger.h" /* for MAX_TRIGNAME_LEN */ #include "wbox_test_init.h" #include "have_crit.h" #include "gtm_trigger_trc.h" #include "gvt_inline.h" #ifdef GTM_TRIGGER GBLREF boolean_t is_dollar_incr; GBLREF boolean_t skip_dbtriggers; /* see gbldefs.c for description of this global */ GBLREF gd_region *gv_cur_region; GBLREF gv_key *gv_altkey; GBLREF gv_key *gv_currkey; GBLREF gv_namehead *gv_target; GBLREF gv_namehead *reset_gv_target; GBLREF int4 gtm_trigger_depth; GBLREF int4 tstart_trigger_depth; GBLREF uint4 update_trans; GBLREF sgm_info *first_sgm_info; GBLREF sgm_info *sgm_info_ptr; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF uint4 dollar_tlevel; GBLREF uint4 t_err; GBLREF int tprestart_state; GBLREF unsigned int t_tries; GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; GBLREF pid_t process_id; GBLREF trans_num local_tn; #ifdef DEBUG GBLREF uint4 dollar_trestart; GBLREF boolean_t donot_INVOKE_MUMTSTART; #endif error_def(ERR_DBROLLEDBACK); error_def(ERR_GVKILLFAIL); error_def(ERR_GVPUTFAIL); error_def(ERR_GVZTRIGFAIL); error_def(ERR_NEEDTRIGUPGRD); error_def(ERR_INDRMAXLEN); error_def(ERR_PATMAXLEN); error_def(ERR_TEXT); error_def(ERR_TPRETRY); error_def(ERR_TRIGDEFBAD); error_def(ERR_TRIGINVCHSET); error_def(ERR_TRIGIS); error_def(ERR_TRIGSUBSCRANGE); LITREF char ctypetab[NUM_CHARS]; LITREF int4 gvtr_cmd_mask[GVTR_CMDTYPES]; LITREF mval gvtr_cmd_mval[GVTR_CMDTYPES]; LITREF mval literal_zero; LITREF mval literal_hashcount; LITREF mval literal_hashcycle; LITREF mval literal_hashlabel; LITREF mval literal_null; #define TRIGGER_SUBSDEF(SUBSTYPE, SUBSNAME, LITMVALNAME, TRIGFILEQUAL, PARTOFHASH) LITREF mval LITMVALNAME; #include "trigger_subs_def.h" #undef TRIGGER_SUBSDEF #define GVTR_PARSE_POINT 1 #define GVTR_PARSE_LEFT 2 #define GVTR_PARSE_RIGHT 3 #define SAVE_RTN_NAME(SAVE_RTN_NAME, SAVE_RTN_NAME_LEN, TRIGDSC) \ { \ SAVE_RTN_NAME_LEN = MIN(MAX_TRIGNAME_LEN, TRIGDSC->rtn_desc.rt_name.len); \ assert(SAVE_RTN_NAME_LEN <= SIZEOF(SAVE_RTN_NAME)); \ memcpy(SAVE_RTN_NAME, TRIGDSC->rtn_desc.rt_name.addr, SAVE_RTN_NAME_LEN); \ } #define SAVE_VAR_NAME(SAVE_VAR_NAME, SAVE_VAR_NAME_LEN, GVT) \ { \ SAVE_VAR_NAME_LEN = MIN(MAX_MIDENT_LEN, GVT->gvname.var_name.len); \ assert(SAVE_VAR_NAME_LEN <= SIZEOF(SAVE_VAR_NAME)); \ memcpy(SAVE_VAR_NAME, GVT->gvname.var_name.addr, SAVE_VAR_NAME_LEN); \ } /* GVTR_HASHTGBL_READ_CLEANUP * Now that ^#t has been read/referenced, make sure this gvt is added to the gvt_tp_list, even if the gvt has no * triggers associated with it. * * Callers gvcst_put and gvcst_kill perform database operations on gvt and an accompanying tp_hist() automatically * ensures that gvt is added to gvt_tp_list. However, there is a window in time between gvtr_db_read_hasht() and * tp_hist() where a restart could occur. If we did not add this gvt to the gvt_tp_list, a restart would not undo this * gvt's trigger related cycles (in TP_INVALIDATE_TRIGGER_CYCLES_IF_NEEDED). * * If the caller is op_ztrigger, it is possible only the ^#t global gvtarget gets added as part of the * "gvtr_get_hasht_gblsubs" calls below and the triggering global does not get referenced anywhere else in the TP * transaction. Since ZTRIGGER command does no db operations on the triggering global, it is possible gvt does not get * added to the gvt_tp_list which means if a trollback/tprestart occurs we would not undo this gvt's trigger related * cycles. To avoid this issue, we add this gvt to the gvt_tp_list always. The macro anyways does nothing if this gvt * has already been added so we should be fine correctness and performance wise. */ #define GVTR_HASHTGBL_READ_CLEANUP(do_gvtr_cleanup) \ { \ /* Restore gv_target, gv_currkey & gv_altkey */ \ gv_target = save_gvtarget; \ /* This ADD_TO_GVT_TP_LIST could potentially happen BEFORE a gvcst_search of this gvt occurred in this \ * transaction. This means if gvt->clue.end is non-zero, gvcst_search would not get a chance to clear the \ * first_tp_srch_status fields (which it does using the GVT_CLEAR_FIRST_TP_SRCH_STATUS macro) because \ * gvt->read_local_tn would be set to local_tn as part of the ADD_TO_GVT_TP_LIST macro invocation. We \ * therefore pass the second parameter indicating that first_tp_srch_status needs to be cleared too if \ * gvt->read_local_tn gets synced to local_tn. All other callers of ADD_TO_GVT_TP_LIST (as of this writing) \ * happen AFTER a gvcst_search of this gvt occurred in this TP transaction. Therefore this is currently the \ * only place which uses TRUE for the second parameter. \ */ \ ADD_TO_GVT_TP_LIST(gv_target, RESET_FIRST_TP_SRCH_STATUS_TRUE); \ if (do_gvtr_cleanup) \ gvtr_free(gv_target); \ /* check no keysize expansion occurred inside gvcst_root_search */ \ assert(gv_currkey->top == save_gv_currkey->top); \ COPY_KEY(gv_currkey, save_gv_currkey); \ /* check no keysize expansion occurred inside gvcst_root_search */ \ assert(gv_altkey->top == save_gv_altkey->top); \ COPY_KEY(gv_altkey, save_gv_altkey); \ TREF(gv_last_subsc_null) = save_gv_last_subsc_null; \ TREF(gv_some_subsc_null) = save_gv_some_subsc_null; \ } #define GVTR_POOL2BUDDYLIST(GVT_TRIGGER, DST_MSTR) \ { \ int4 len; \ char *addr; \ mstr *dst_mstr; \ \ dst_mstr = DST_MSTR; \ addr = dst_mstr->addr; \ len = dst_mstr->len; \ dst_mstr->addr = (char *)get_new_element(GVT_TRIGGER->gv_trig_list, DIVIDE_ROUND_UP(len, GVTR_LIST_ELE_SIZE)); \ memcpy(dst_mstr->addr, addr, len); \ } #define GVTR_PROCESS_GVSUBS(PTR, END, SUBSDSC, COLON_IMBALANCE, GVT, SUBSCSTRLEN, SUBSCSTR) \ { \ uint4 status, rt_name_len, save_var_name_len; \ gv_trigger_t *lcl_trigdsc; \ char rt_name[MAX_TRIGNAME_LEN], save_var_name[MAX_MIDENT_LEN]; \ \ status = gvtr_process_gvsubs(PTR, END, SUBSDSC, COLON_IMBALANCE, GVT); \ if (status) \ { \ lcl_trigdsc = GVT->gvt_trigger->gv_trig_array; \ assert(NULL != lcl_trigdsc); \ assert((NULL != lcl_trigdsc->rtn_desc.rt_name.addr) \ && (0 != lcl_trigdsc->rtn_desc.rt_name.len)); \ SAVE_VAR_NAME(save_var_name, save_var_name_len, GVT); \ SAVE_RTN_NAME(rt_name, rt_name_len, lcl_trigdsc); \ GVTR_HASHTGBL_READ_CLEANUP(TRUE); \ if (ERR_PATMAXLEN == status) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) status, 0, ERR_TRIGIS, 2, rt_name_len, \ rt_name); \ else if (ERR_TRIGSUBSCRANGE == status) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(10) status, 4, save_var_name_len, save_var_name, \ SUBSCSTRLEN, SUBSCSTR, ERR_TRIGIS, 2, rt_name_len, rt_name); \ else /* error return from patstr */ \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status); \ } \ /* End of a range (in a set of ranges) or a subscript itself. \ * Either case, colon_imbalance can be safely reset. \ */ \ COLON_IMBALANCE = FALSE; \ } #define KEYSUB_S2POOL_IF_NEEDED(KEYSUB_MVAL, KEYSUB, THISSUB) \ { \ unsigned char str_buff[MAX_ZWR_KEY_SZ], *str_end; \ mstr opstr; \ \ KEYSUB_MVAL = lvvalarray[KEYSUB]; \ if (NULL == KEYSUB_MVAL) \ { \ PUSH_MV_STENT(MVST_MVAL); \ KEYSUB_MVAL = &mv_chain->mv_st_cont.mvs_mval; \ KEYSUB_MVAL->mvtype = 0; \ lvvalarray[KEYSUB] = KEYSUB_MVAL; \ THISSUB = keysub_start[KEYSUB]; \ opstr.addr = (char *)str_buff; \ opstr.len = MAX_ZWR_KEY_SZ; \ str_end = gvsub2str((unsigned char *)THISSUB, &opstr, FALSE); \ KEYSUB_MVAL->str.addr = (char *)str_buff; \ KEYSUB_MVAL->str.len = INTCAST(str_end - str_buff); \ KEYSUB_MVAL->mvtype = MV_STR; \ s2pool(&KEYSUB_MVAL->str); \ } \ } #define PUT_TRIGGER_ON_CMD_TYPE_QUEUE(trigdsc, gvt_trigger, type) \ { /* Add to type queue in gvt_trigger */ \ if (NULL == (gvt_trigger)->type##_triglist) \ { /* Initialize circular list of one element */ \ (gvt_trigger)->type##_triglist = (trigdsc); \ (trigdsc)->next_##type = trigdsc; \ } else \ { /* Add element to list - Note this is a simplistic add algorithm that doesn't make \ * any attempt to keep elements sorted. Since triggers are supposed to fire in random \ * order, that's a good thing. Adding elements A,B,C will end up with order A,C,B. \ */ \ (trigdsc)->next_##type = (gvt_trigger)->type##_triglist->next_##type; \ (gvt_trigger)->type##_triglist->next_##type = (trigdsc); \ } \ } #define SELECT_AND_RANDOMIZE_TRIGGER_CHAIN(gvt_trigger, trigstart, trig_list_offset, type) \ { \ trigstart = (gvt_trigger)->type##_triglist; \ if (NULL != trigstart) \ { /* Use arbitrary bit from sum of hodgepodge fields that change over time to see if should \ * rotate the trigger chain by 1 or 2 entries to increase pseudo-randomization. \ */ \ gvt_trigger->type##_triglist = trigstart = trigstart->next_##type; \ if (0 != (0x10 & (process_id + (INTPTR_T)stringpool.free + (INTPTR_T)&trigstart))) /* BYPASSOK */ \ /* Do a 2nd rotation.. */ \ gvt_trigger->type##_triglist = trigstart = trigstart->next_##type; \ } \ trig_list_offset = OFFSETOF(gv_trigger_t, next_##type); \ } /* This error macro is used for all definition errors where the target is ^#t(GVN,,) */ #define GVTR_HASHT_GVN_DEFINITION_RETRY_OR_ERROR(INDEX,SUBSCRIPT,CSA) \ { \ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) \ { \ GVTR_HASHTGBL_READ_CLEANUP(TRUE); \ t_retry(cdb_sc_triggermod); \ } else \ { \ assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); \ SAVE_VAR_NAME(save_var_name, save_var_name_len, gvt); \ GVTR_HASHTGBL_READ_CLEANUP(TRUE); \ /* format "INDEX,SUBSCRIPT" of ^#t(GVN,INDEX,SUBSCRIPT) in the error message */ \ SET_PARAM_STRING(util_buff, util_len, INDEX, SUBSCRIPT); \ rts_error_csa(CSA_ARG(CSA) VARLSTCNT(8) ERR_TRIGDEFBAD, 6, save_var_name_len, \ save_var_name, save_var_name_len, save_var_name, util_len, \ util_buff); \ } \ } /* This error macro is used for all definition errors where the target is ^#t(GVN,<#COUNT|#CYCLE>) */ #define HASHT_DEFINITION_RETRY_OR_ERROR(SUBSCRIPT,MOREINFO,CSA) \ { \ if (UPDATE_CAN_RETRY(t_tries, t_fail_hist[t_tries])) \ { \ GVTR_HASHTGBL_READ_CLEANUP(TRUE); \ t_retry(cdb_sc_triggermod); \ } else \ { \ HASHT_DEFINITION_ERROR(SUBSCRIPT,MOREINFO,CSA); \ } \ } #define HASHT_DEFINITION_ERROR(SUBSCRIPT,MOREINFO,CSA) \ { \ assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); \ SAVE_VAR_NAME(save_var_name, save_var_name_len, gvt); \ GVTR_HASHTGBL_READ_CLEANUP(TRUE); \ rts_error_csa(CSA_ARG(CSA) VARLSTCNT(12) ERR_TRIGDEFBAD, 6, save_var_name_len, \ save_var_name, save_var_name_len, save_var_name, \ LEN_AND_LIT(SUBSCRIPT), ERR_TEXT, 2, RTS_ERROR_TEXT(MOREINFO)); \ } /* This code is modeled around "updproc_ch" in updproc.c */ CONDITION_HANDLER(gvtr_tpwrap_ch) { int rc; START_CH(TRUE); if ((int)ERR_TPRETRY == SIGNAL) { assert(TPRESTART_STATE_NORMAL == tprestart_state); tprestart_state = TPRESTART_STATE_NORMAL; assert(NULL != first_sgm_info); /* This only happens at the outer-most layer so state should be normal now */ rc = tp_restart(1, !TP_RESTART_HANDLES_ERRORS); assert(0 == rc); assert(TPRESTART_STATE_NORMAL == tprestart_state); /* No rethrows possible */ /* gvtr* code could be called by gvcst_put which plays with "reset_gv_target". But the only gvcst* functions * called by the gvtr* code is "gvcst_get" which is guaranteed not to play with it. Therefore we should NOT * be tampering with it (i.e. trying to reset it like what "trigger_item_tpwrap_ch" does) as the caller expects * it to be untouched throughout the gvtr* activity. */ UNWIND(NULL, NULL); } NEXTCH; } STATICFNDEF void gvtr_db_tpwrap_helper(sgmnt_addrs *csa, int err_code, boolean_t root_srch_needed) { enum cdb_sc status, failure; # ifdef DEBUG gv_namehead *save_gv_target; # endif ESTABLISH(gvtr_tpwrap_ch); assert(dollar_tlevel); assert(gv_target != csa->hasht_tree); assert((ERR_GVPUTFAIL == err_code) || (ERR_GVKILLFAIL == err_code) || (ERR_GVZTRIGFAIL == err_code)); if (root_srch_needed) { assert(0 < t_tries); assert(ERR_GVPUTFAIL != err_code); /* for SET, gvcst_put does the root search anyways. So, don't do it here */ ASSERT_BEGIN_OF_FRESH_TP_TRANS; /* ensures gvcst_root_search is the first thing done in the restarted transaction */ GVCST_ROOT_SEARCH; /* any t_retry from gvcst_root_search will transfer control back to gvtr_tpwrap_ch */ root_srch_needed = FALSE; /* just to be safe */ } gvtr_db_read_hasht(csa); DEBUG_ONLY(save_gv_target = gv_target;) assert(save_gv_target == gv_target); /* ensure gv_target was not inadvertently modified by gvtr_db_read_hasht */ /* If no triggers are defined for this global and we came in as non-TP and did op_tstart in GVTR_INIT_AND_TPWRAP_IF_NEEDED * do the op_tcommit. However, do it only for t_tries = 0 for two reasons: * * (a) If in final retry of a non-TP set, we will be holding crit. op_tcommit releases crit which is an out-of-design * situation as that could cause restarts in final retry of the non-TP set. So, complete the non-TP update as a TP update * by deferring the op_tcommit until we reach GVTR_OP_TCOMMIT in gvcst_put/gvcst_kill. * * (b) Assume t_tries = 1 or 2 and we came in as non-TP. If we found no triggers defined and invoked op_tcommit, to resume * the non-TP set, we will invoke t_begin to reset global variables which also resets t_tries. Now, with t_tries = 0, * if a restart happens and we come back here (due to concurrent $ZTRIGGER and MUPIP TRIGGER) and find no triggers defined * for this global, we will again invoke op_tcommit. This can happen in a spinlock thereby not letting the final retry * optimistic -> pessimistic concurrency scheme kick in at all. One workaround would be to not call t_begin * after doing an op_tcommit. But, this would mean examining the various global variables that t_begin resets and * making sure it's okay NOT to do the t_begin. Instead, complete the non-TP update as a TP update by deferring the * op_tcommit until we reach GVTR_OP_TCOMMIT in gvcst_put/gvcst_kill. * * Note: Not doing op_tcommit will cause this non-TP SET to be completed as a TP SET even though no triggers were defined. * This would mean, a non-TP set (in a concurrent environment with trigger loads happening frequently) could end up * writing a TSET record in the corresponding regions' journal file. In a non-trigger environment, this will NOT * be an issue since we come here only if process' trigger cycle is different from database trigger cycle. * * The exception to the above case is when we are processing a ZTRIGGER command (denoted by the err_code passed * in being set to ERR_GVTRIGFAIL). In that case, we will be journaling a TZTRIG record of some form and is ok * even if the global does not exist. So we don't want to prematurely terminate the transaction in that case. */ if ((NULL == gv_target->gvt_trigger) && (0 == t_tries) && (ERR_GVZTRIGFAIL != err_code)) { /* No triggers exist for this global. TP wrap not needed any more for this transaction */ status = op_tcommit(); assert(cdb_sc_normal == status); /* if retry, an rts_error should have been issued and we should not reach here */ } REVERT; } /* Helper function used by "gvtr_db_read_hasht" and "trigger_upgrade" functions */ boolean_t gvtr_get_hasht_gblsubs(mval *subs_mval, mval *ret_mval) { uint4 curend; boolean_t was_null = FALSE, is_null = FALSE, is_defined; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; is_defined = FALSE; curend = gv_currkey->end; /* note down gv_currkey->end before changing it so we can restore it before function returns */ assert(KEY_DELIMITER == gv_currkey->base[curend]); assert(gv_target->gd_csa == cs_addrs); COPY_SUBS_TO_GVCURRKEY(subs_mval, gv_cur_region, gv_currkey, was_null, is_null); /* updates gv_currkey */ is_defined = gvcst_get(ret_mval); assert(!is_defined || (MV_STR & ret_mval->mvtype)); /* assert that gvcst_get() sets type of mval to MV_STR */ gv_currkey->end = curend; /* reset gv_currkey->end to what it was at function entry */ gv_currkey->base[curend] = KEY_DELIMITER; /* restore terminator for entire key so key is well-formed */ return is_defined; } STATICFNDEF uint4 gvtr_process_range(gv_namehead *gvt, gvtr_subs_t *subsdsc, int type, char *start, char *end) { mval tmpmval; gv_namehead *save_gvt; char keybuff[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)]; gv_key *out_key; uint4 len, len1, min, nelems; gvt_trigger_t *gvt_trigger; char *dststart, *dstptr, *srcptr, ch; boolean_t ret; int cmpres; /* This function has its input string pointing to the stringpool. It does some processing (mval2subsc etc.) * and expects the input string to be untouched during all of this so it sets the global to TRUE * to ensure no one else touches the stringpool in the meantime. */ DEBUG_ONLY(len = (uint4)(end - start)); assert(IS_IN_STRINGPOOL(start, len)); DBG_MARK_STRINGPOOL_UNUSABLE; if ('"' == *start) { /* Remove surrounding double-quotes as well as transform TWO CONSECUTIVE intermediate double-quotes into ONE. * We need to construct the transformed string. Since we cannot modify the input string and yet want to avoid * malloc/frees within this routine, we use the buddy_list allocate/free functions for this purpose. */ assert('"' == *(end - 1)); start++; end--; } len = UINTCAST(end - start); if (len || (GVTR_PARSE_POINT == type)) { gvt_trigger = gvt->gvt_trigger; if (len == 0) { tmpmval.mvtype = MV_STR; tmpmval.str.addr = ""; tmpmval.str.len = 0; } else { nelems = DIVIDE_ROUND_UP(len, GVTR_LIST_ELE_SIZE); dststart = (char *)get_new_element(gvt_trigger->gv_trig_list, nelems); dstptr = dststart; for (srcptr = start; srcptr < end; srcptr++) { ch = *srcptr; if ('"' == ch) { /* A double-quote in the middle of the string better be TWO consecutive double-quotes */ assert(((srcptr + 1) < end) && ('"' == srcptr[1])); srcptr++; } *dstptr++ = ch; } assert((dstptr - dststart) <= len); tmpmval.mvtype = MV_STR; tmpmval.str.addr = dststart; tmpmval.str.len = INTCAST(dstptr - dststart); } /* switch gv_target for mval2subsc */ save_gvt = gv_target; gv_target = gvt; out_key = (gv_key *)keybuff; out_key->end = 0; out_key->top = DBKEYSIZE(MAX_KEY_SZ); mval2subsc(&tmpmval, out_key, gv_cur_region->std_null_coll); gv_target = save_gvt; if (0 < len) { /* Now that mval2subsc is done, free up the allocated dststart buffer */ ret = free_last_n_elements(gvt_trigger->gv_trig_list, nelems); assert(ret); } } /* else it means an open range (where left or right side of range is unspecified) * null subscript on the right side of a range is treated as negative infinity and * on the left side of a range is treated as positive infinity. */ switch(type) { case GVTR_PARSE_POINT: assert(&subsdsc->gvtr_subs_type == &subsdsc->gvtr_subs_point.gvtr_subs_type); len = out_key->end; /* keep trailing 0 */ subsdsc->gvtr_subs_point.len = len; dststart = (char *)get_new_element(gvt_trigger->gv_trig_list, DIVIDE_ROUND_UP(len, GVTR_LIST_ELE_SIZE)); memcpy(dststart, out_key->base, len); subsdsc->gvtr_subs_point.subs_key = dststart; subsdsc->gvtr_subs_point.next_range = NULL; subsdsc->gvtr_subs_type = GVTR_SUBS_POINT; break; case GVTR_PARSE_LEFT: assert(&subsdsc->gvtr_subs_type == &subsdsc->gvtr_subs_range.gvtr_subs_type); if (len) { len = out_key->end; /* keep trailing 0 */ assert(len); dststart = (char *)get_new_element(gvt_trigger->gv_trig_list, DIVIDE_ROUND_UP(len, GVTR_LIST_ELE_SIZE)); memcpy(dststart, out_key->base, len); subsdsc->gvtr_subs_range.subs_key1 = dststart; subsdsc->gvtr_subs_range.len1 = len; } else subsdsc->gvtr_subs_range.len1 = GVTR_RANGE_OPEN_LEN; subsdsc->gvtr_subs_type = GVTR_SUBS_RANGE; break; case GVTR_PARSE_RIGHT: assert(&subsdsc->gvtr_subs_type == &subsdsc->gvtr_subs_range.gvtr_subs_type); assert(GVTR_SUBS_RANGE == subsdsc->gvtr_subs_type); if (len) { len = out_key->end; /* keep trailing 0 */ assert(len); dststart = (char *)get_new_element(gvt_trigger->gv_trig_list, DIVIDE_ROUND_UP(len, GVTR_LIST_ELE_SIZE)); memcpy(dststart, out_key->base, len); subsdsc->gvtr_subs_range.subs_key2 = dststart; subsdsc->gvtr_subs_range.len2 = len; len1 = subsdsc->gvtr_subs_range.len1; if (GVTR_RANGE_OPEN_LEN != len1) { /* Since both ends of the range are not open, do range check of post-collated subscripts */ min = MIN(len1, len); cmpres = memcmp(subsdsc->gvtr_subs_range.subs_key1, subsdsc->gvtr_subs_range.subs_key2, min); if ((0 < cmpres) || (0 == cmpres) && (len1 > len)) { /* Invalid range. Issue error */ DBG_MARK_STRINGPOOL_USABLE; return ERR_TRIGSUBSCRANGE; } } } else { /* right side of the range is open, check if left side is open too, if so reduce it to a "*" */ if (GVTR_RANGE_OPEN_LEN == subsdsc->gvtr_subs_range.len1) subsdsc->gvtr_subs_type = GVTR_SUBS_STAR; else subsdsc->gvtr_subs_range.len2 = GVTR_RANGE_OPEN_LEN; } break; default: assert(FALSE); break; } assert(&subsdsc->gvtr_subs_range.next_range == &subsdsc->gvtr_subs_point.next_range); subsdsc->gvtr_subs_range.next_range = NULL; DBG_MARK_STRINGPOOL_USABLE; return 0; } STATICFNDEF uint4 gvtr_process_pattern(char *ptr, uint4 len, gvtr_subs_t *subsdsc, gvt_trigger_t *gvt_trigger) { /* subscript is a pattern */ char source_buffer[MAX_SRCLINE]; mstr instr; ptstr pat_ptstr; uint4 status; assert('?' == *ptr); ptr++; len--; assert(0 < len); if (ARRAYSIZE(source_buffer) <= len) { assert(FALSE); return ERR_PATMAXLEN; } memcpy(source_buffer, ptr, len); instr.addr = source_buffer; instr.len = len; if (status = patstr(&instr, &pat_ptstr, NULL)) return status; assert(pat_ptstr.len <= MAX_PATOBJ_LENGTH); len = pat_ptstr.len * SIZEOF(uint4); ptr = (char *)get_new_element(gvt_trigger->gv_trig_list, DIVIDE_ROUND_UP(len, GVTR_LIST_ELE_SIZE)); memcpy(ptr, pat_ptstr.buff, len); assert(&subsdsc->gvtr_subs_type == &subsdsc->gvtr_subs_pattern.gvtr_subs_type); subsdsc->gvtr_subs_type = GVTR_SUBS_PATTERN; subsdsc->gvtr_subs_pattern.pat_mval.mvtype = MV_STR; subsdsc->gvtr_subs_pattern.pat_mval.str.addr = ptr; subsdsc->gvtr_subs_pattern.pat_mval.str.len = len; subsdsc->gvtr_subs_pattern.next_range = NULL; return 0; } STATICFNDEF uint4 gvtr_process_gvsubs(char *start, char *end, gvtr_subs_t *subsdsc, boolean_t colon_imbalance, gv_namehead *gvt) { uint4 status; if ('?' == start[0]) { assert(!colon_imbalance); if (status = gvtr_process_pattern(start, UINTCAST(end - start), subsdsc, gvt->gvt_trigger)) return status; } else if ('*' == start[0]) { /* subscript is a "*" */ assert(end == start + 1); assert(&subsdsc->gvtr_subs_type == &subsdsc->gvtr_subs_star.gvtr_subs_type); subsdsc->gvtr_subs_type = GVTR_SUBS_STAR; /* allow ANY value for this subscript */ subsdsc->gvtr_subs_star.next_range = NULL; } else { if (status = gvtr_process_range(gvt, subsdsc, colon_imbalance ? GVTR_PARSE_RIGHT : GVTR_PARSE_POINT, start, end)) { /* As of now we expect only ERR_TRIGSUBSCRANGE (which is triggered only for GVTR_PARSE_RIGHT) */ assert((ERR_TRIGSUBSCRANGE == status) && colon_imbalance); return status; } } return 0; } void gvtr_db_read_hasht(sgmnt_addrs *csa) { gv_namehead *save_gvtarget, *gvt; gv_key_buf save_currkey, save_altkey; unsigned char util_buff[MAX_TRIG_UTIL_LEN]; gv_key *save_gv_currkey; gv_key *save_gv_altkey; mval tmpmval, *ret_mval; boolean_t is_defined, was_null = FALSE, is_null = FALSE, zdelim_defined, delim_defined; boolean_t save_gv_last_subsc_null, save_gv_some_subsc_null; int4 tmpint4, util_len; gvt_trigger_t *gvt_trigger; uint4 trigidx, num_gv_triggers, num_pieces, len, cmdtype, index, minpiece, maxpiece; gv_trigger_t *gv_trig_array, *trigdsc, *trigtop; uint4 currkey_end, cycle, numsubs, cursub, numlvsubs, curlvsub; char ch, *ptr, *ptr_top, *ptr_start; boolean_t quote_imbalance, colon_imbalance, end_of_subscript; uint4 paren_imbalance; gvtr_subs_t *subsdsc; mname_entry *lvnamedsc; uint4 *lvindexdsc, status; int ctype; char save_rtn_name[MAX_TRIGNAME_LEN], save_var_name[MAX_MIDENT_LEN]; uint4 save_rtn_name_len, save_var_name_len; # ifdef DEBUG int cntset, icntset, cntkill, icntkill, cntztrig, icntztrig; gv_trigger_t *trigstart; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(dollar_tlevel); /* the code below is not designed to work in non-TP */ save_gvtarget = gv_target; SET_GVTARGET_TO_HASHT_GBL(csa); /* Save gv_currkey and gv_altkey */ assert(NULL != gv_currkey); assert((SIZEOF(gv_key) + gv_currkey->end + 1) <= SIZEOF(save_currkey)); save_gv_currkey = (gv_key *)&save_currkey.key; MEMCPY_KEY(save_gv_currkey, gv_currkey); assert(NULL != gv_altkey); assert((SIZEOF(gv_key) + gv_altkey->end + 1) <= SIZEOF(save_altkey)); save_gv_altkey = (gv_key *)&save_altkey.key; MEMCPY_KEY(save_gv_altkey, gv_altkey); save_gv_last_subsc_null = TREF(gv_last_subsc_null); save_gv_some_subsc_null = TREF(gv_some_subsc_null); DBGTRIGR((stderr, "gvtr_db_read_hasht: begin\n")); INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED; gv_currkey->prev = 0; if (0 == gv_target->root) { /* ^#t global does not exist. Return right away. */ DBGTRIGR((stderr, "gvtr_db_read_hasht: no triggers\n")); GVTR_HASHTGBL_READ_CLEANUP(TRUE); csa->hdr->hasht_upgrade_needed = FALSE; /* Reset now that we know there is no ^#t global in this db. * Note: It is safe to do so even if we dont hold crit. */ return; } if (csa->hdr->hasht_upgrade_needed) { /* ^#t needs to be upgraded first before reading it. Cannot proceed. */ GVTR_HASHTGBL_READ_CLEANUP(TRUE); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_NEEDTRIGUPGRD, 2, DB_LEN_STR(gv_cur_region)); } /* ^#t global exists. * Initialize gv_target->gvt_trigger from ^#t(,...) where is the global corresponding to save_gvtarget. * * In order to follow the code below, it would help to have the following ^#t global layout as an example. * The following example assumes the following ^#t global layout corresponding to triggers for the ^GBL global * In the following code, wherever "GBL" is mentioned in the comment, is meant for the general case. * * Input file * ----------- * ^GBL(acn=:,1) -zdelim="|" -pieces="2:5;4:6;8" -commands=Set,Kill,ZTKill -xecute="do ^XNAMEinGBL" -options=NOI,NOC * * ^#t global structure created by MUPIP TRIGGER * ---------------------------------------------- * ^#t("GBL","#LABEL")="3" # Format of the ^#t global for GBL. Currently set to 3. * # Will get bumped up in the future when the ^#t global format changes. * ^#t("GBL","#CYCLE")=<32-bit-decimal-number> # incremented every time any trigger changes happen in ^GBL global * ^#t("GBL","#COUNT")=1 # indicating there is 1 trigger currently defined for ^GBL global * ^#t("GBL",1,"CMD")="S,K,ZTK" * ^#t("GBL",1,"GVSUBS")="acn=:,1" * ^#t("GBL",1,"OPTIONS")="NOI,NOC" * ^#t("GBL",1,"DELIM")= # undefined in this case because -zdelim was specified * ^#t("GBL",1,"ZDELIM")="|" # would have been undefined in case -delim was specified * ^#t("GBL",1,"PIECES")="2:6;8" * ^#t("GBL",1,"TRIGNAME")="GBL#1" # routine name trigger will have * ^#t("GBL",1,"XECUTE")="do ^XNAMEinGBL" * ^#t("GBL",1,"CHSET")="UTF-8" # assuming the MUPIP TRIGGER was done with $ZCHSET of "UTF-8" */ /* ----------------------------------------------------------------------------- * Read ^#t("GBL","#LABEL") * ----------------------------------------------------------------------------- */ /* Check value of ^#t("GBL","#LABEL"). We expect it to indicate the current format of the ^#t global. * If not, it is an integ error in the ^#t global so issue an appropriate error. */ gvt = save_gvtarget; /* use smaller variable name as it is going to be used in lots of places below */ /* First add "GBL" subscript to gv_currkey i.e. ^#t("GBL") */ tmpmval.mvtype = MV_STR; tmpmval.str = gvt->gvname.var_name; /* copy gvname from gvt */ ret_mval = &tmpmval; COPY_SUBS_TO_GVCURRKEY(ret_mval, gv_cur_region, gv_currkey, was_null, is_null); /* updates gv_currkey */ /* At this point, gv_currkey points to ^#t("GBL") */ /* Now check for ^#t("CIF","#LABEL") to determine what format ^#t("GBL") is stored in (if at all it exists) */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_hashlabel, ret_mval); if (!is_defined) /* No triggers exist for "^GBL". Return */ { DBGTRIGR((stderr, "gvtr_db_read_hasht: no triggers for ^GBL\n")); GVTR_HASHTGBL_READ_CLEANUP(TRUE); return; } if ((STR_LIT_LEN(HASHT_GBL_CURLABEL) != ret_mval->str.len) || MEMCMP_LIT(ret_mval->str.addr, HASHT_GBL_CURLABEL)) HASHT_DEFINITION_RETRY_OR_ERROR("\"#LABEL\"","#LABEL field is not " HASHT_GBL_CURLABEL, csa); /* So we can go ahead and read other ^#t("GBL") records */ /* ----------------------------------------------------------------------------- * Now read ^#t("GBL","#CYCLE") * ----------------------------------------------------------------------------- */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_hashcycle, ret_mval); if (!is_defined) HASHT_DEFINITION_RETRY_OR_ERROR("\"#CYCLE\"","#CYCLE field is missing", csa); tmpint4 = mval2i(ret_mval); /* decimal values are truncated by mval2i so we will accept a #CYCLE of 1.5 as 1 */ if (0 >= tmpint4) /* ^#t("GBL","#CYCLE") is not a positive integer. Error out */ HASHT_DEFINITION_ERROR("\"#CYCLE\"","#CYCLE field is negative", csa); cycle = (uint4)tmpint4; /* Check if ^#t("GBL") has previously been read from the db. If so, check if cycle is same as ^#t("GBL","#CYCLE"). */ gvt_trigger = gvt->gvt_trigger; if (NULL != gvt_trigger) { DBGTRIGR((stderr, "gvtr_db_read_hasht: gvt_trigger->gv_trigger_cycle = %d \n",gvt_trigger->gv_trigger_cycle)); if (gvt_trigger->gv_trigger_cycle == cycle) { /* Since cycle is same, no need to reinitialize any triggers for "GBL". Can return safely. * Pass "FALSE" to GVTR_HASHTGBL_READ_CLEANUP macro to ensure gvt->gvt_trigger is NOT freed. */ DBGTRIGR((stderr, "gvtr_db_read_hasht: trigger has been read before\n")); GVTR_HASHTGBL_READ_CLEANUP(FALSE); return; } DBGTRIGR((stderr, "gvtr_db_read_hasht: cycle mismatch, re-initialize triggers\n")); /* Now that we have to reinitialize triggers for "GBL", free up previously allocated structure first */ gvtr_free(gvt); assert(NULL == gvt->gvt_trigger); } /* Force a cycle mismatch if a restart occurs during trigger content read */ gvt->db_trigger_cycle = gvt->gd_csa->db_trigger_cycle - 1; gvt_trigger = (gvt_trigger_t *)malloc(SIZEOF(gvt_trigger_t)); gvt_trigger->gv_trigger_cycle = 0; gvt_trigger->gv_trig_array = NULL; gvt_trigger->gv_trig_list = NULL; /* Set gvt->gvt_trigger to this malloced memory (after gv_trig_array has been initialized to NULL to avoid garbage * values). If we encounter an error below, we will remember to free this up the next time we are in this function */ gvt->gvt_trigger = gvt_trigger; /* ----------------------------------------------------------------------------- * Now read ^#t("GBL","#COUNT") * ----------------------------------------------------------------------------- */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_hashcount, ret_mval); if (!is_defined) HASHT_DEFINITION_RETRY_OR_ERROR("\"#COUNT\"","#COUNT field is missing", csa); tmpint4 = mval2i(ret_mval); /* decimal values are truncated by mval2i so we will accept a #COUNT of 1.5 as 1 */ if (0 >= tmpint4) /* ^#t("GBL","#COUNT") is not a positive integer. Error out */ HASHT_DEFINITION_ERROR("\"#COUNT\"","#COUNT field is negative", csa); num_gv_triggers = (uint4)tmpint4; gvt_trigger->num_gv_triggers = num_gv_triggers; /* We want a memory store for all the values that are going to be read in from the database. We dont know upfront * how much memory is needed so we use a buddy list (that expands to fit the needed memory). We need to create a * buddy_list with element size of 1 byte and ask for n-elements if we want n-bytes of contiguous memory. * Minimum value of elemSize for buddy_list is 8 due to alignment requirements of the returned memory location. * Therefore, we set elemSize to 8 bytes instead and will convert as much bytes as we need into as many * 8-byte multiple segments below. We anticipate 256 bytes for each trigger so we allocate space for "num_gv_triggers" * as the start of the buddy_list (to avoid repeated expansions of the buddy list in most cases). */ gvt_trigger->gv_trig_list = (buddy_list *)malloc(SIZEOF(buddy_list)); initialize_list(gvt_trigger->gv_trig_list, GVTR_LIST_ELE_SIZE, DIVIDE_ROUND_UP(GV_TRIG_LIST_INIT_ALLOC * num_gv_triggers, GVTR_LIST_ELE_SIZE)); trigdsc = (gv_trigger_t *)malloc(num_gv_triggers * SIZEOF(gv_trigger_t)); memset(trigdsc, 0, num_gv_triggers * SIZEOF(gv_trigger_t)); gvt_trigger->gv_trig_array = trigdsc; /* Now that everything is null-initialized, set gvt_trigger->gv_trig_array */ gvt_trigger->gv_target = gvt; /* From this point onwards, we assume the integrity of the ^#t global i.e. MUPIP TRIGGER would have * set ^#t fields to their appropriate value. So we dont do any more checks in case taking the wrong path. */ /* ------------------------------------------------------------------------------------ * Now read ^#t("GBL",,...) for going from 1 to num_gv_triggers * ------------------------------------------------------------------------------------ */ currkey_end = gv_currkey->end; /* note down gv_currkey->end before changing it so we can restore it before next iteration */ assert(KEY_DELIMITER == gv_currkey->base[currkey_end]); for (trigidx = 1; trigidx <= num_gv_triggers; trigidx++, trigdsc++) { /* All records to be read in this loop are of the form ^#t("GBL",1,...) so add "1" as a subscript to gv_currkey */ i2mval(&tmpmval, trigidx); assert(ret_mval == &tmpmval); COPY_SUBS_TO_GVCURRKEY(ret_mval, gv_cur_region, gv_currkey, was_null, is_null); /* updates gv_currkey */ /* Read in ^#t("GBL",1,"TRIGNAME")="GBL#1" */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_trigname, ret_mval); if (!is_defined) GVTR_HASHT_GVN_DEFINITION_RETRY_OR_ERROR(trigidx,",\"TRIGNAME\"", csa); trigdsc->rtn_desc.rt_name = ret_mval->str; /* Copy trigger name mident */ trigdsc->gvt_trigger = gvt_trigger; /* Save ptr to our main gvt_trigger struct for this trigger. With * this and given a gv_trigger_t, we can get to the gvt_trigger_t * block containing the gv_target pointer and thus get the trigger * csa, region and index for loading trigger source later without * having to look anything up again. */ /* Reserve extra space when triggername is placed in buddy list. This space is used by gtm_trigger() if * the trigger name is not unique within the process. One or two of the chars are appended to the trigger * name until an unused name is found. */ trigdsc->rtn_desc.rt_name.len += TRIGGER_NAME_RESERVED_SPACE; GVTR_POOL2BUDDYLIST(gvt_trigger, &trigdsc->rtn_desc.rt_name); trigdsc->rtn_desc.rt_name.len -= TRIGGER_NAME_RESERVED_SPACE; /* Not using the space yet */ /* Read in ^#t("GBL",1,"CMD")="S,K,ZK,ZTK,ZTR" */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_cmd, ret_mval); if (!is_defined) GVTR_HASHT_GVN_DEFINITION_RETRY_OR_ERROR(trigidx,",\"CMD\"", csa); /* Initialize trigdsc->cmdmask */ ptr = ret_mval->str.addr; ptr_top = ptr + ret_mval->str.len; assert(0 == trigdsc->cmdmask); for (ptr_start = ptr; ptr <= ptr_top; ptr++) { if ((ptr == ptr_top) || (',' == *ptr)) { len = UINTCAST(ptr - ptr_start); for (cmdtype = 0; cmdtype < GVTR_CMDTYPES; cmdtype++) { if ((len == gvtr_cmd_mval[cmdtype].str.len) && (0 ==memcmp(ptr_start, gvtr_cmd_mval[cmdtype].str.addr, len))) { trigdsc->cmdmask |= gvtr_cmd_mask[cmdtype]; break; } } assert(GVTR_CMDTYPES > cmdtype); ptr_start = ptr + 1; } } assert(0 != trigdsc->cmdmask); /* Read in ^#t("GBL",1,"GVSUBS")="acn=:,1" */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_gvsubs, ret_mval); if (is_defined) { /* At this point, we expect MUPIP TRIGGER to have ensured ret_mval is NOT the empty string "" */ numsubs = 0; numlvsubs = 0; ptr_start = ret_mval->str.addr; ptr_top = ptr_start + ret_mval->str.len; quote_imbalance = FALSE; paren_imbalance = 0; end_of_subscript = FALSE; for (ptr = ptr_start; ptr <= ptr_top; ptr++) { if (ptr == ptr_top) end_of_subscript = TRUE; else if ('"' == (ch = *ptr)) { if (!quote_imbalance) quote_imbalance = TRUE; /* start of double-quoted string */ else if (((ptr + 1) == ptr_top) || ('"' != ptr[1])) quote_imbalance = FALSE; else ptr++; /* skip past nested double-quote (TWO consecutive double-quotes) */ } else if (quote_imbalance) continue; else if ('(' == ch) paren_imbalance++; /* parens can be inside pattern match alternation expressions */ else if (')' == ch) { assert(paren_imbalance); /* should never go negative */ paren_imbalance--; } else if (paren_imbalance) continue; else if (',' == ch) end_of_subscript = TRUE; else if ('=' == ch) numlvsubs++; if (end_of_subscript) { /* End of current subscript */ assert(!quote_imbalance); assert(!paren_imbalance); numsubs++; end_of_subscript = FALSE; } } /* Initialize trigdsc->numsubs */ assert(numsubs); assert(MAX_GVSUBSCRIPTS > numsubs); trigdsc->numsubs = numsubs; /* Allocate trigdsc->subsarray */ trigdsc->subsarray = (gvtr_subs_t *)get_new_element(gvt_trigger->gv_trig_list, DIVIDE_ROUND_UP((numsubs * SIZEOF(gvtr_subs_t)), GVTR_LIST_ELE_SIZE)); cursub = 0; subsdsc = trigdsc->subsarray; /* Initialize trigdsc->numlvsubs */ assert(numlvsubs <= numsubs); trigdsc->numlvsubs = numlvsubs; if (numlvsubs) { curlvsub = 0; /* Allocate trigdsc->lvnamearray */ trigdsc->lvnamearray = (mname_entry *)get_new_element(gvt_trigger->gv_trig_list, DIVIDE_ROUND_UP((numlvsubs * SIZEOF(mname_entry)), GVTR_LIST_ELE_SIZE)); lvnamedsc = trigdsc->lvnamearray; /* Allocate trigdsc->lvindexarray */ trigdsc->lvindexarray = (uint4 *)get_new_element(gvt_trigger->gv_trig_list, DIVIDE_ROUND_UP((numlvsubs * SIZEOF(uint4)), GVTR_LIST_ELE_SIZE)); lvindexdsc = trigdsc->lvindexarray; } /* Initialize trigdsc->subsarray, trigdsc->lvindexarray & trigdsc->lvnamearray */ quote_imbalance = FALSE; colon_imbalance = FALSE; paren_imbalance = 0; end_of_subscript = FALSE; for (ptr = ptr_start; ptr <= ptr_top; ptr++) { assert(ptr_start <= ptr); if (ptr == ptr_top) end_of_subscript = TRUE; else if ('"' == (ch = *ptr)) { if (!quote_imbalance) quote_imbalance = TRUE; /* start of double-quoted string */ else if (((ptr + 1) == ptr_top) || ('"' != ptr[1])) quote_imbalance = FALSE; else ptr++; /* skip past nested double-quote (TWO consecutive double-quotes) */ } else if (quote_imbalance) continue; else if ('(' == ch) paren_imbalance++; /* parens can be inside pattern match alternation expressions */ else if (')' == ch) { assert(paren_imbalance); /* should never go negative */ paren_imbalance--; } else if (paren_imbalance) continue; else if (',' == ch) end_of_subscript = TRUE; else if ('=' == ch) { /* a local variable name has been specified for a subscript */ assert(curlvsub < numlvsubs); *lvindexdsc++ = cursub; len = UINTCAST(ptr - ptr_start); assert(len); # ifdef DEBUG /* Check validity of lvname */ ctype = ctypetab[ptr_start[0]]; assert((TK_PERCENT == ctype) || (TK_LOWER == ctype) || (TK_UPPER == ctype)); for (index = 1; index < len; index++) { ctype = ctypetab[ptr_start[index]]; assert((TK_LOWER == ctype) || (TK_UPPER == ctype) || (TK_DIGIT == ctype)); } # endif lvnamedsc->var_name.len = len; lvnamedsc->var_name.addr = (char *)get_new_element(gvt_trigger->gv_trig_list, DIVIDE_ROUND_UP(len, GVTR_LIST_ELE_SIZE)); memcpy(lvnamedsc->var_name.addr, ptr_start, len); lvnamedsc->marked = FALSE; COMPUTE_HASH_MNAME(lvnamedsc); lvnamedsc++; curlvsub++; assert(lvnamedsc == &trigdsc->lvnamearray[curlvsub]); assert(lvindexdsc == &trigdsc->lvindexarray[curlvsub]); ptr_start = &ptr[1]; } else if (';' == ch) { /* End of current range and beginning of new range within same subscript */ GVTR_PROCESS_GVSUBS(ptr_start, ptr, subsdsc, colon_imbalance, gvt, ret_mval->str.len, ret_mval->str.addr); /* Assert that irrespective of the type of the subscript, all of them have the * "next_range" member at the same offset so it is safe for us to use any one below. */ assert(&subsdsc->gvtr_subs_range.next_range == &subsdsc->gvtr_subs_point.next_range); assert(&subsdsc->gvtr_subs_pattern.next_range == &subsdsc->gvtr_subs_point.next_range); assert(&subsdsc->gvtr_subs_star.next_range == &subsdsc->gvtr_subs_point.next_range); subsdsc->gvtr_subs_range.next_range = (gvtr_subs_t *)get_new_element(gvt_trigger->gv_trig_list, DIVIDE_ROUND_UP(SIZEOF(gvtr_subs_t), GVTR_LIST_ELE_SIZE)); subsdsc = subsdsc->gvtr_subs_range.next_range; ptr_start = &ptr[1]; } else if (':' == ch) { /* Left side of range */ status = gvtr_process_range(gvt, subsdsc, GVTR_PARSE_LEFT, ptr_start, ptr); assert(0 == status); /* ERR_TRIGSUBSCRANGE is issued only in case of GVTR_PARSE_RIGHT */ assert(!colon_imbalance); colon_imbalance = TRUE; ptr_start = &ptr[1]; } if (end_of_subscript) { assert(!quote_imbalance); assert(!paren_imbalance); GVTR_PROCESS_GVSUBS(ptr_start, ptr, subsdsc, colon_imbalance, gvt, ret_mval->str.len, ret_mval->str.addr); cursub++; /* We cannot do subsdsc++ because subsdsc could at this point have followed a chain of * "next_range" pointers so could be different from what we had at the start of the loop. */ subsdsc = &trigdsc->subsarray[cursub]; assert(subsdsc == &trigdsc->subsarray[cursub]); ptr_start = &ptr[1]; end_of_subscript = FALSE; } } assert(cursub == numsubs); assert(!numlvsubs || (curlvsub == numlvsubs)); } /* Else ^#t("GBL","#COUNT") is set to 1 but ^#t("GBL",1,"GVSUBS") is undefined. * Possible if the trigger is defined for the entire ^GBL (i.e. NO subscripts) * All related 5 trigdsc fields are already initialized to either 0 or NULL (by the memset above). */ /* Read in ^#t("GBL",1,"OPTIONS")="NOI,NOC" */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_options, ret_mval); if (is_defined) { trigdsc->options = ret_mval->str; GVTR_POOL2BUDDYLIST(gvt_trigger, &trigdsc->options); } /* Read in ^#t("GBL",1,"DELIM")= */ delim_defined = gvtr_get_hasht_gblsubs((mval *)&literal_delim, ret_mval); if (delim_defined) trigdsc->is_zdelim = FALSE; else { /* Read in ^#t("GBL",1,"ZDELIM")="|" */ zdelim_defined = gvtr_get_hasht_gblsubs((mval *)&literal_zdelim, ret_mval); if (zdelim_defined) { assert(!delim_defined); /* DELIM and ZDELIM should NOT have been specified at same time */ trigdsc->is_zdelim = TRUE; } } if (delim_defined || zdelim_defined) /* order of || is important since latter is set only if former is FALSE */ { /* Initialize trigdsc->delimiter */ trigdsc->delimiter = *ret_mval; GVTR_POOL2BUDDYLIST(gvt_trigger, &trigdsc->delimiter.str); } /* Read in ^#t("GBL",1,"PIECES")="2:6;8" */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_pieces, ret_mval); if (is_defined) { /* First determine # of ';' separated piece-ranges */ num_pieces = 1; /* for last piece that does NOT have a ';' */ ptr_start = ret_mval->str.addr; ptr_top = ptr_start + ret_mval->str.len; for (ptr = ptr_start; ptr < ptr_top; ptr++) { if (';' == *ptr) num_pieces++; } trigdsc->numpieces = num_pieces; trigdsc->piecearray = (gvtr_piece_t *)get_new_element(gvt_trigger->gv_trig_list, DIVIDE_ROUND_UP((num_pieces * SIZEOF(gvtr_piece_t)), GVTR_LIST_ELE_SIZE)); index = 0; minpiece = 0; for (ptr = ptr_start; ptr <= ptr_top; ptr++) { if ((ptr == ptr_top) || (';' == *ptr)) { maxpiece = (uint4)asc2i((uchar_ptr_t)ptr_start, INTCAST(ptr - ptr_start)); if (!minpiece) minpiece = maxpiece; ptr_start = ptr + 1; trigdsc->piecearray[index].min = minpiece; trigdsc->piecearray[index].max = maxpiece; minpiece = 0; index++; } else if (':' == *ptr) { minpiece = (uint4)asc2i((uchar_ptr_t)ptr_start, INTCAST(ptr - ptr_start)); ptr_start = ptr + 1; } } assert(index == num_pieces); } /* Read in ^#t("GBL",1,"CHSET")="UTF-8". If CHSET does not match gtm_chset issue error. */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_chset, ret_mval); if (!is_defined) GVTR_HASHT_GVN_DEFINITION_RETRY_OR_ERROR(trigidx,",\"CHSET\"", csa); if ((!gtm_utf8_mode && ((STR_LIT_LEN(CHSET_M_STR) != ret_mval->str.len) || memcmp(ret_mval->str.addr, CHSET_M_STR, STR_LIT_LEN(CHSET_M_STR)))) || (gtm_utf8_mode && ((STR_LIT_LEN(CHSET_UTF8_STR) != ret_mval->str.len) || memcmp(ret_mval->str.addr, CHSET_UTF8_STR, STR_LIT_LEN(CHSET_UTF8_STR))))) { /* CHSET mismatch */ SAVE_VAR_NAME(save_var_name, save_var_name_len, gvt); SAVE_RTN_NAME(save_rtn_name, save_rtn_name_len, trigdsc); GVTR_HASHTGBL_READ_CLEANUP(TRUE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_TRIGINVCHSET, 6, save_rtn_name_len, save_rtn_name, save_var_name_len, save_var_name, ret_mval->str.len, ret_mval->str.addr); } /* Defer loading xecute string until time to compile it */ /* trigdsc->rtn_desc is already NULL-initialized as part of the memset above */ gv_currkey->end = currkey_end; /* remove the current and make way for the next in gv_currkey */ gv_currkey->base[currkey_end] = KEY_DELIMITER; /* restore terminator for entire key */ } /* Now that ALL triggers for this global have been read, separate the triggers into types. There is a link for each command * type in each trigger and an anchor for the list in gvt_trigger. Each list is circular for that type. Since triggers can * be for more than one command type (e.g. SET, KILL, etc), each trigger can be on more than one queue. Later when we need * to run the triggers of a given type, we can just pick the queue and run it. */ gv_trig_array = gvt_trigger->gv_trig_array; assert(NULL != gv_trig_array); gvt_trigger->set_triglist = gvt_trigger->kill_triglist = gvt_trigger->ztrig_triglist = NULL; DEBUG_ONLY(icntset = icntkill = icntztrig = 0); for (trigdsc = gv_trig_array, trigtop = trigdsc + num_gv_triggers; trigdsc < trigtop; trigdsc++) { if (0 != (trigdsc->cmdmask & gvtr_cmd_mask[GVTR_CMDTYPE_SET])) { PUT_TRIGGER_ON_CMD_TYPE_QUEUE(trigdsc, gvt_trigger, set); DEBUG_ONLY(++icntset); } if ((0 != (trigdsc->cmdmask & gvtr_cmd_mask[GVTR_CMDTYPE_KILL])) || (0 != (trigdsc->cmdmask & gvtr_cmd_mask[GVTR_CMDTYPE_ZKILL])) || (0 != (trigdsc->cmdmask & gvtr_cmd_mask[GVTR_CMDTYPE_ZTKILL]))) { PUT_TRIGGER_ON_CMD_TYPE_QUEUE(trigdsc, gvt_trigger, kill); DEBUG_ONLY(++icntkill); } if (0 != (trigdsc->cmdmask & gvtr_cmd_mask[GVTR_CMDTYPE_ZTRIGGER])) { PUT_TRIGGER_ON_CMD_TYPE_QUEUE(trigdsc, gvt_trigger, ztrig); DEBUG_ONLY(++icntztrig); } } gvt_trigger->gv_trig_top = trigdsc; /* Very top of the array */ # ifdef DEBUG /* Verify that the queues are well built */ { cntset = cntkill = cntztrig = 0; trigstart = gvt_trigger->set_triglist; trigtop = NULL; for (trigdsc = trigstart; (NULL != trigdsc) && (trigdsc != trigtop); trigdsc = trigdsc->next_set) { trigtop = trigstart; /* Stop when we get back here */ cntset++; } trigstart = gvt_trigger->kill_triglist; trigtop = NULL; for (trigdsc = trigstart; (NULL != trigdsc) && (trigdsc != trigtop); trigdsc = trigdsc->next_kill) { trigtop = trigstart; /* Stop when we get back here */ cntkill++; } trigstart = gvt_trigger->ztrig_triglist; trigtop = NULL; for (trigdsc = trigstart; (NULL != trigdsc) && (trigdsc != trigtop); trigdsc = trigdsc->next_ztrig) { trigtop = trigstart; /* Stop when we get back here */ cntztrig++; } assert(cntset == icntset); assert(cntkill == icntkill); assert(cntztrig == cntztrig); } # endif GVTR_HASHTGBL_READ_CLEANUP(FALSE); /* do NOT free gvt->gvt_trigger so pass FALSE */ DBGTRIGR((stderr, "gvtr_db_read_hasht: gvt_trigger->gv_trigger_cycle = cycle\n")); /* Now that ^#t has been read, we update "cycle" to the higher value. In case this transaction restarts, * we cannot be sure of the correctness of whatever we read so we need to undo the "cycle" update. * We take care of this by setting "gvt_triggers_read_this_tn" to TRUE and use this in "tp_clean_up". * Set gvt->trig_read_tn as well so this gvt is part of the list of gvts whose cycle gets restored in tp_clean_up. */ gvt_trigger->gv_trigger_cycle = cycle; TREF(gvt_triggers_read_this_tn) = TRUE; gvt->trig_read_tn = local_tn; return; } /* Determine if a given trigger matches the input key. * Expects key to be parsed into an array of pointers to the individual subscripts. * Also expects caller to invoke this function only if the # of subscripts in key is equal to that in the trigger. * Uses the function "gvsub2str" (in some cases) which in turn expects gv_target to be set appropriately. */ STATICFNDEF boolean_t gvtr_is_key_a_match(char *keysub_start[], gv_trigger_t *trigdsc, mval *lvvalarray[]) { uint4 numsubs, keysub; gvtr_subs_t *subsdsc, *substop, *subsdsc1; uint4 subs_type; char *thissub; mval *keysub_mval; uint4 thissublen, len1, len2, min; boolean_t thissub_matched, left_side_matched, right_side_matched; int cmpres; numsubs = trigdsc->numsubs; assert(numsubs); subsdsc = trigdsc->subsarray; for (keysub = 0; keysub < numsubs; keysub++, subsdsc++) { thissub_matched = FALSE; /* For each key subscript, check against the trigger subscript (or chain of subscripts in case of SETs of ranges) */ subsdsc1 = subsdsc; /* note down separately since we might need to follow chain of "next_range" within this subsc */ do { subs_type = subsdsc1->gvtr_subs_type; switch(subs_type) { case GVTR_SUBS_STAR: /* easiest case, * matches any subscript so skip to the next subscript */ thissub_matched = TRUE; break; case GVTR_SUBS_PATTERN: /* Determine match by reverse transforming subscript to string format and invoking * do_pattern. Before that check if transformation has already been done. If so use * that instead of redoing it. If not, do transformation and store it in M-stack * (to protect it from garbage collection) and also update lvvalarray to help with * preventing future recomputations of the same. */ KEYSUB_S2POOL_IF_NEEDED(keysub_mval, keysub, thissub); if (do_pattern(keysub_mval, &subsdsc1->gvtr_subs_pattern.pat_mval)) thissub_matched = TRUE; break; case GVTR_SUBS_POINT: thissub = keysub_start[keysub]; thissublen = UINTCAST(keysub_start[keysub + 1] - thissub); assert(0 < (int4)thissublen); /* ensure it is not negative (i.e. huge positive value) */ if ((thissublen == subsdsc1->gvtr_subs_point.len) && (0 == memcmp(subsdsc1->gvtr_subs_point.subs_key, thissub, thissublen))) thissub_matched = TRUE; break; case GVTR_SUBS_RANGE: thissub = keysub_start[keysub]; thissublen = UINTCAST(keysub_start[keysub + 1] - thissub); assert(0 < (int4)thissublen); /* ensure it is not negative (i.e. huge positive value) */ /* Check left side of range */ len1 = subsdsc1->gvtr_subs_range.len1; left_side_matched = TRUE; if (GVTR_RANGE_OPEN_LEN != len1) { min = MIN(len1, thissublen); cmpres = memcmp(subsdsc1->gvtr_subs_range.subs_key1, thissub, min); if ((0 < cmpres) || (0 == cmpres) && (thissublen < len1)) left_side_matched = FALSE; } /* Check right side of range */ len2 = subsdsc1->gvtr_subs_range.len2; if (left_side_matched) { right_side_matched = TRUE; if (GVTR_RANGE_OPEN_LEN != len2) { min = MIN(len2, thissublen); cmpres = memcmp(subsdsc1->gvtr_subs_range.subs_key2, thissub, min); if ((0 > cmpres) || (0 == cmpres) && (thissublen > len2)) right_side_matched = FALSE; } if (right_side_matched) thissub_matched = TRUE; } break; default: assert(FALSE); break; } if (thissub_matched) break; assert(&subsdsc1->gvtr_subs_range.next_range == &subsdsc1->gvtr_subs_point.next_range); assert(&subsdsc1->gvtr_subs_range.next_range == &subsdsc1->gvtr_subs_pattern.next_range); assert(&subsdsc1->gvtr_subs_range.next_range == &subsdsc1->gvtr_subs_star.next_range); subsdsc1 = subsdsc1->gvtr_subs_range.next_range; if (NULL == subsdsc1) /* this key subscript did NOT match with trigger */ return FALSE; } while (TRUE); } return TRUE; } void gvtr_free(gv_namehead *gvt) { gvt_trigger_t *gvt_trigger; gv_trigger_t *gv_trig_array, *trigdsc, *trigtop; buddy_list *gv_trig_list; uint4 num_gv_triggers; gvt_trigger = gvt->gvt_trigger; if (NULL == gvt_trigger) return; gv_trig_array = gvt_trigger->gv_trig_array; if (NULL != gv_trig_array) { num_gv_triggers = gvt_trigger->num_gv_triggers; assert(0 < num_gv_triggers); for (trigdsc = gv_trig_array, trigtop = trigdsc + num_gv_triggers; trigdsc < trigtop; trigdsc++) { if (NULL != trigdsc->rtn_desc.rt_adr) gtm_trigger_cleanup(trigdsc); } free(gv_trig_array); gvt_trigger->gv_trig_array = NULL; } gv_trig_list = gvt_trigger->gv_trig_list; if (NULL != gv_trig_list) /* free up intermediate mallocs */ { cleanup_list(gv_trig_list); free(gv_trig_list); gvt_trigger->gv_trig_list = NULL; } free(gvt_trigger); gvt->gvt_trigger = NULL; gvt->db_trigger_cycle = 0; /* Force triggers to be reloaded implicitly if not explicitly done by caller */ } /* Initializes triggers for global variable "gvt" from ^#t global. */ void gvtr_init(gv_namehead *gvt, uint4 cycle, boolean_t tp_is_implicit, int err_code) { sgmnt_addrs *csa; sgmnt_data_ptr_t csd; uint4 lcl_t_tries, save_t_tries, loopcnt; uint4 cycle_start; boolean_t root_srch_needed; enum cdb_sc failure; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(dollar_tlevel); /* A TP wrap should have been done by the caller if needed */ assert(!skip_dbtriggers); /* should not come here if triggers are not supposed to be invoked */ assert(gv_target == gvt); save_t_tries = t_tries; /* note down the value of t_tries at the entry to this function */ csa = gvt->gd_csa; assert(NULL != csa); /* database for this gvt better be opened at this point */ /* The directory tree corresponds to a non-existent global which does not have any triggers so in this case * return right away. No trigger initialization needed. */ if (gvt == csa->dir_tree) return; /* We know there are no triggers for the ^#t global so return right away in that case too. * In fact it is necessary to do this if "trigger_upgrade" is caller as we dont want a NEEDTRIGUPGRD error there. */ if (gvt == csa->hasht_tree) return; csd = csa->hdr; assert((gvt->db_trigger_cycle != cycle) || (gvt->db_dztrigger_cycle != csa->db_dztrigger_cycle)); root_srch_needed = FALSE; /* Check if TP was in turn an implicit TP (e.g. created by the GVTR_INIT_AND_TPWRAP_IF_NEEDED macro). */ if (tp_is_implicit) { /* If implicit TP, we need to go through gvtr_db_tpwrap_helper since we don't want any t_retry calls to end up * invoking MUM_TSTART. This way any restarts signaled during ^#t global reads will be handled internally in * gvtr_init without disturbing the caller gvcst_put/gvcst_kill in any manner. But this assumes that the ^#t * global is the FIRST global reference in this TP transaction. Otherwise the TP restart would have to transfer * control back to wherever that global reference occurred instead of this ^#t global read. Assert that below. */ cycle_start = csa->db_trigger_cycle; ASSERT_BEGIN_OF_FRESH_TP_TRANS; lcl_t_tries = t_tries; t_fail_hist[lcl_t_tries] = cdb_sc_normal; assert(donot_INVOKE_MUMTSTART); for (loopcnt = 0; ; loopcnt++) { /* In case of restart in gvtr_db_read_hasht (handled by gvtr_tpwrap_ch), tp_restart will call tp_clean_up * which will reset sgm_info_ptr to NULL and csa->sgm_info_ptr->tp_set_sgm_done to FALSE. Call tp_set_sgm * to set sgm_info_ptr and first_sgm_info as gvcst_get relies on this being set. Also re-inits * csa->db_trigger_cycle to the latest copy from csd->db_trigger_cycle which is important to detect if * triggers have changed. */ tp_set_sgm(); gvtr_db_tpwrap_helper(csa, err_code, root_srch_needed); root_srch_needed = FALSE; assert(t_tries >= lcl_t_tries); if (!dollar_tlevel) { /* Came in as non-tp. Did op_tstart in the caller (GVTR_INIT_AND_TPWRAP_IF_NEEDED). * op_tcommit was completed by gvtr_db_tpwrap_helper as no triggers were defined. * Can break out of the loop. */ assert(ERR_GVZTRIGFAIL != err_code); assert((0 == t_tries) && (NULL == gvt->gvt_trigger)); break; } else { failure = t_fail_hist[lcl_t_tries]; if ((lcl_t_tries == t_tries) && (cdb_sc_normal == failure)) { /* Read of ^#t completed successfully as there is no change in t_tries. * Safe to break out of the loop. */ break; } /* else we encountered a TP restart (which would have triggered a call to t_retry * which in turn would have done a rts_error(TPRETRY) which would have been caught {BYPASSOK} * by gvtr_tpwrap_ch which would in turn have unwound the C-stack upto the point * where the ESTABLISH is done in gvtr_tpwrap_helper and then returned from there). * In this case we have to keep retrying the read until there are no tp restarts or * an op_tcommit is done by gvtr_db_tpwrap_helper (in the event that no triggers * are defined). */ assert(dollar_trestart); /* Before restarting, check if the restart is due to online rollback. If so, based on whether the * rollback took the database back to a different logical state or not, we need to either issue * an error OR redo the root search of the original global as online rollback related restart * resets root block of all gv_targets to zero. */ assert(((cdb_sc_onln_rlbk1 != failure) && (cdb_sc_onln_rlbk2 != failure)) || !gv_target->root); assert((cdb_sc_onln_rlbk2 != failure) || TREF(dollar_zonlnrlbk)); if (cdb_sc_onln_rlbk1 == failure) { root_srch_needed = (ERR_GVPUTFAIL != err_code); } else if (cdb_sc_onln_rlbk2 == failure) { assert(tstart_trigger_depth == gtm_trigger_depth); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(1) ERR_DBROLLEDBACK); } /* update lcl_t_tries to reflect the fact that a restart happened */ lcl_t_tries = t_tries; t_fail_hist[lcl_t_tries] = cdb_sc_normal; } /* We expect the above function to return with either op_tcommit or a tp_restart invoked. * In the case of op_tcommit, we expect dollar_tlevel to be 0 and if so we break out of the loop. * In the tp_restart case, we expect a maximum of 4 tries/retries and much lesser usually. * Additionally we also want to avoid an infinite loop so limit the loop to what is considered * a huge iteration count and assertpro if that is reached as it suggests an out-of-design situation. */ assertpro(TPWRAP_HELPER_MAX_ATTEMPTS >= loopcnt); } /* It is possible we have restarted one or more times. If so, it is possible for csa->db_trigger_cycle * to have also been updated one or more times by t_retry() or tp_set_sgm but "cycle" would not have been * refreshed. Since we snapshoted cycle into start_cycle, we can check if csa->db_trigger_cycle has changed. * If it has, we update "cycle" so the correct value gets set into gvt->db_trigger_cycle below. */ if (cycle_start != csa->db_trigger_cycle) cycle = csa->db_trigger_cycle; } else gvtr_db_read_hasht(csa); /* Note that only gvt->db_trigger_cycle (and not CSA->db_trigger_cycle) should be touched here. * CSA->db_trigger_cycle should be left untouched and updated only in t_end/tp_tend in crit. * This way we are protected from the following situation which would arise if we incremented CSA here. * Let us say a TP transaction does TWO updates SET ^X=1,^Y=1 and then goes to commit. Let us say * before the ^X=1 set, csd had a cycle of 10 so that would have been copied over to X's gvt and to * csa. Let us say just before the ^Y=1 set, csd cycle got incremented to 11 (due to a concurrent MUPIP * TRIGGER which reloaded triggers for both ^X and ^Y). Now ^Y's gvt, csa and csd would all have a * cycle of 11. At commit time, we would check if csa and csd have the same cycle and they do so we * will assume no restart needed when actually a restart is needed because our view of ^X's triggers * is stale even though our view of ^Y's triggers is uptodate. Not touching csa's cycle here saves us * from this situation as csa's cycle will be 10 at commit time which will be different from csd's * cycle of 11 and in turn would cause a restart. This avoids us from otherwise having to go through * all the gvts that participated in this transaction and comparing their cycle with csd. */ gvt->db_trigger_cycle = cycle; gvt->db_dztrigger_cycle = csa->db_dztrigger_cycle; /* No more trigger reads for this global until next $ZTRIGGER() */ DBGTRIGR((stderr, "gvtr_init(): GVT->db_trigger_cycle=%d ->db_dztrigger_cycle=%d\n", gvt->db_trigger_cycle, gvt->db_dztrigger_cycle)); } /* Determines matching triggers for a given gv_currkey and uses "gtm_trigger" to invokes them with appropriate parameters. * Returns 0 for success and ERR_TPRETRY in case a retry is detected. */ int gvtr_match_n_invoke(gtm_trigger_parms *trigparms, gvtr_invoke_parms_t *gvtr_parms) { mident gbl; gvtr_cmd_type_t gvtr_cmd; gvt_trigger_t *gvt_trigger; boolean_t is_set_trigger, is_ztrig_trigger, ok_to_invoke_trigger; char *key_ptr, *key_start, *key_end; char *keysub_start[MAX_KEY_SZ + 1]; gv_key *save_gv_currkey; gv_key_buf save_currkey; gv_trigger_t *trigdsc, *trigstop, *trigstart; int gtm_trig_status, tfxb_status, num_triggers_invoked, trigmax, trig_list_offset; mstr *ztupd_mstr; mval *keysub_mval; mval *lvvalarray[MAX_GVSUBSCRIPTS + 1]; mval *ztupd_mval, dummy_mval; uint4 *lvindexarray; uint4 keysubs, keylen, numlvsubs, curlvsub, lvvalindex, cursub; char *thissub; mval *ret_mval; mval tmpmval; unsigned char util_buff[MAX_TRIG_UTIL_LEN]; int4 util_len; # ifdef DEBUG sgmnt_addrs *csa; sgmnt_data_ptr_t csd; sgm_info *si; gv_namehead *save_targ; int lcl_gtm_trigger_depth; # endif DEBUG_ONLY(csa = cs_addrs); DEBUG_ONLY(csd = cs_data); DEBUG_ONLY(si = sgm_info_ptr); DEBUG_ONLY(save_targ = gv_target); assert(gv_target != csa->dir_tree); assert(dollar_tlevel); /* Initialize trigger parms that dont depend on the context of the matching trigger */ gvtr_cmd = gvtr_parms->gvtr_cmd; gvt_trigger = gvtr_parms->gvt_trigger; trigparms->ztriggerop_new = &gvtr_cmd_mval[gvtr_cmd]; is_set_trigger = (GVTR_CMDTYPE_SET == gvtr_cmd); is_ztrig_trigger = (GVTR_CMDTYPE_ZTRIGGER == gvtr_cmd); if (is_set_trigger) { /* Check that minimal pre-filling of trigparms has already occurred in the caller */ assert(NULL != trigparms->ztoldval_new); assert(NULL != trigparms->ztvalue_new); assert(NULL != trigparms->ztdata_new); PUSH_MV_STENT(MVST_MVAL); /* protect $ztupdate from stp_gcol */ ztupd_mval = &mv_chain->mv_st_cont.mvs_mval; ztupd_mval->str.len = 0; ztupd_mval->mvtype = 0; /* keep mvtype set to 0 until mval is fully initialized (later below) */ ztupd_mstr = &ztupd_mval->str; trigparms->ztupdate_new = ztupd_mval; } else { /* KILL or ZTRIGGER type command */ ztupd_mval = &dummy_mval; trigparms->ztupdate_new = (mval *)&literal_zero; } trigparms->lvvalarray = lvvalarray; trigparms->ztvalue_changed = FALSE; /* Parse gv_currkey into array of subscripts to facilitate trigger matching. * Save contents of gv_currkey into local array as the global variable gv_currkey could change * in between invocations of the "gtm_trigger" function in the for loop below. Not just that, * gv_currkey could be even freed and remalloced as a bigger array (if a db with a bigger keysize * gets opened) inside any of the "gtm_trigger" invocations. So we should maintain pointers only * to the local copy (not the global gv_currkey) for use inside all iterations of the for loop. */ save_gv_currkey = (gv_key *)&save_currkey.key; assert(((char *)save_gv_currkey + SIZEOF(gv_key) + gv_currkey->end + 1) < (char *)(&save_currkey + 1)); MEMCPY_KEY(save_gv_currkey, gv_currkey); key_ptr = (char *)save_gv_currkey->base; DEBUG_ONLY(key_start = key_ptr;) key_end = key_ptr + save_gv_currkey->end; assert(KEY_DELIMITER == *key_end); assert(KEY_DELIMITER == *(key_end - 1)); keysubs = 0; keysub_start[0] = key_ptr; for ( ; key_ptr < key_end; key_ptr++) { if (KEY_DELIMITER == *key_ptr) { assert(ARRAYSIZE(keysub_start) > keysubs); assert(keysubs < ARRAYSIZE(lvvalarray)); lvvalarray[keysubs] = NULL; keysub_start[keysubs++] = key_ptr + 1; } } assert(keysubs); keysubs--; /* do not count global name as subscript */ assert(keysub_start[keysubs] == key_end); assert(NULL == lvvalarray[keysubs]); DEBUG_ONLY(keylen = INTCAST(key_end - key_start)); assert(!keysubs || keylen); /* Match & Invoke triggers. Take care to ensure they are invoked in an UNPREDICTABLE order. * Current implementation is to invoke triggers in a rotating order. For example, each command * type is in a circular queue - say A, B, C. Each time we come here to drive triggers for this * node, bump the gvt_trigger list pointer to the next element in the queue we are processing. So * if triggers A, B, C are on the SET queue, first time we will process A,B,C, next time B,C,A, * and the third, C,A,B. */ num_triggers_invoked = 0; if (is_set_trigger) { /* Chose the set command list to process */ SELECT_AND_RANDOMIZE_TRIGGER_CHAIN(gvt_trigger, trigstart, trig_list_offset, set); } else if (is_ztrig_trigger) { /* Chose the ztrig command list to process */ SELECT_AND_RANDOMIZE_TRIGGER_CHAIN(gvt_trigger, trigstart, trig_list_offset, ztrig); } else { /* Chose the kill command list to process */ SELECT_AND_RANDOMIZE_TRIGGER_CHAIN(gvt_trigger, trigstart, trig_list_offset, kill); } trigmax = gvt_trigger->num_gv_triggers; trigstop = NULL; /* So we can get through the first iteration */ for (trigdsc = trigstart; (NULL != trigdsc) && (trigdsc != trigstop); --trigmax, trigdsc = *(gv_trigger_t **)((char *)trigdsc + trig_list_offset)) /* Follow the designated list */ { DBGTRIGR((stderr, "gvtr_match_n_invoke: top of trigr scan loop (%d)\n", trigmax)); trigstop = trigstart; /* Stop when we get back to where we started */ assertpro(0 <= trigmax); /* Loop ender "just in case" */ assert(trigdsc >= gvt_trigger->gv_trig_array); assert(trigdsc < gvt_trigger->gv_trig_top); assert((trigdsc->cmdmask & gvtr_cmd_mask[gvtr_cmd]) || !is_set_trigger); if (!is_set_trigger && !is_ztrig_trigger && !(trigdsc->cmdmask & gvtr_cmd_mask[gvtr_cmd])) continue; /* Trigger is for different command. Currently only possible for KILL/ZKILL (asserted above) */ /* Check that global variables which could have been modified inside gvcst_put/gvcst_kill have been * reset to their default values before going into trigger code as that could cause a nested call to * gvcst_put/gvcst_kill and we dont want any non-default value of this global variable from the parent * gvcst_put/gvcst_kill to be reset by the nested invocation. */ assert(INVALID_GV_TARGET == reset_gv_target); if ((keysubs == trigdsc->numsubs) && (!keysubs || gvtr_is_key_a_match(keysub_start, trigdsc, lvvalarray))) { /* Note: lvvalarray could be updated above in case any trigger patterns * needed to have been checked for a key-match. Before invoking the trigger, * check if it specified any local variables for subscripts. If so, * initialize any that are not yet already done. */ numlvsubs = trigdsc->numlvsubs; if (numlvsubs) { lvindexarray = trigdsc->lvindexarray; for (curlvsub = 0; curlvsub < numlvsubs; curlvsub++) { lvvalindex = lvindexarray[curlvsub]; assert(lvvalindex < keysubs); /* lvval not already computed. Do so by reverse transforming subscript to * string format. Store mval containing string in M-stack (to protect it * from garbage collection). Also update lvvalarray to prevent future * recomputations of the same across the many triggers to be checked for * the given key. */ keysub_mval = lvvalarray[lvvalindex]; KEYSUB_S2POOL_IF_NEEDED(keysub_mval, lvvalindex, thissub); } } /* Check if trigger specifies a piece delimiter and pieces of interest. * If so, check if any of those pieces are different. If not, trigger should NOT be invoked. */ ok_to_invoke_trigger = TRUE; trigparms->ztdelim_new = (mval *)&literal_null; if (is_set_trigger) { assert(0 == ztupd_mval->mvtype); if (trigdsc->delimiter.str.len) { trigparms->ztdelim_new = (mval *)&trigdsc->delimiter; strpiecediff(&trigparms->ztoldval_new->str, &trigparms->ztvalue_new->str, &trigdsc->delimiter.str, trigdsc->numpieces, trigdsc->piecearray, !trigdsc->is_zdelim && gtm_utf8_mode, ztupd_mstr); if (!ztupd_mstr->len) { /* No pieces of interest changed. So dont invoke trigger. */ DBGTRIGR((stderr, "gvtr_match_n_invoke: Turning off ok_to_invoke_trigger #1\n")); ok_to_invoke_trigger = FALSE; } else { /* The string containing list of updated pieces is pointing * to a buffer allocated inside "strpiecediff". Since this * function could be called again by a nested trigger, move * this buffer to the stringpool. */ s2pool(ztupd_mstr); ztupd_mval->mvtype = MV_STR; /* now ztupd_mval->str is protected from stp_gcol */ } } } if (ok_to_invoke_trigger) { DBGTRIGR((stderr, "gvtr_match_n_invoke: Inside trigger drive block\n")); DEBUG_ONLY(lcl_gtm_trigger_depth = gtm_trigger_depth); if (NULL == trigdsc->rtn_desc.rt_adr) { /* Trigger xecute string not compiled yet, so load it */ /* Read in ^#t("GBL",1,"XECUTE")="do ^XNAMEinGBL" */ DBGTRIGR((stderr, "gvtr_match_n_invoke: Fetching trigger source\n")); tfxb_status = trigger_fill_xecute_buffer(trigdsc); if (0 != tfxb_status) { assert(ERR_TPRETRY == tfxb_status); ztupd_mval->mvtype = 0; /* so stp_gcol - if invoked somehow - can free up any space * currently occupied by this no-longer-necessary mval */ gvtr_parms->num_triggers_invoked = num_triggers_invoked; return tfxb_status; } assert(NULL != trigdsc->xecute_str.str.addr); if (MAX_XECUTE_LEN <= trigdsc->xecute_str.str.len) { assert(FALSE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_INDRMAXLEN, 1, MAX_XECUTE_LEN); } } gtm_trig_status = gtm_trigger(trigdsc, trigparms); /* note: the above call may update trigparms->ztvalue_new for SET type triggers */ assert(lcl_gtm_trigger_depth == gtm_trigger_depth); num_triggers_invoked++; ztupd_mval->mvtype = 0; /* so stp_gcol -if invoked somehow - can free up any space * currently occupied by this no-longer-necessary mval */ assert((0 == gtm_trig_status) || (ERR_TPRETRY == gtm_trig_status)); if (0 != gtm_trig_status) { gvtr_parms->num_triggers_invoked = num_triggers_invoked; return gtm_trig_status; } } /* At this time, gv_cur_region, cs_addrs, gv_target, gv_currkey could all be * different from whatever they were JUST before the gtm_trigger() call. All * of them would be restored to what they were after the gtm_trigger_fini call. * It is ok to be in this out-of-sync situation since we dont rely on these * global variables until the "gtm_trigger_fini" call. Any interrupt that * happens before then will anyways know to save/restore those variables in * this that are pertinent in its processing. */ } } assert(INVALID_GV_TARGET == reset_gv_target); assert(0 <= num_triggers_invoked); if (num_triggers_invoked) gtm_trigger_fini(FALSE, FALSE); /* Verify that gtm_trigger_fini restored gv_cur_region/cs_addrs/cs_data/gv_target/gv_currkey * properly (gtm_trigger could have changed these values depending on the M code that was invoked). */ assert(csa == cs_addrs); assert(csd == cs_data); assert(si == sgm_info_ptr); assert(gv_target == save_targ); assert(0 == memcmp(save_gv_currkey, gv_currkey, OFFSETOF(gv_key, base[0]) + gv_currkey->end)); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); gvtr_parms->num_triggers_invoked = num_triggers_invoked; return 0; } #endif /* GTM_TRIGGER */ fis-gtm-V7.0-005/sr_unix/exttab_parse.c0000644000032200000250000006774114342376334016711 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include #include "gtm_stdio.h" #include #include "gtm_stdlib.h" #include "gtm_ctype.h" #include "gtm_stat.h" #include "copy.h" #include "gtmxc_types.h" #include #include "lv_val.h" /* needed for "fgncal.h" */ #include "fgncal.h" #include "gtmci.h" #include "eintr_wrappers.h" #include "error.h" #include "gtm_malloc.h" #include "trans_log_name.h" #include "iosp.h" #include "gtm_limits.h" #include "restrict.h" GBLREF volatile boolean_t timer_in_handler; GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF boolean_t gtm_dist_ok_to_use; #define CR 0x0A /* Carriage return */ #define NUM_TABS_FOR_GTMERRSTR 2 #define MAX_SRC_LINE 1024 #define MAX_NAM_LEN 32 #define MAXIMUM_STARS 2 #define SPACE_BLOCK_SIZE ((NON_GTM64_ONLY(1024) GTM64_ONLY(2048)) - SIZEOF(storElem)) #define TABLEN 8 #define POINTER_SIZE 6 STATICDEF int ext_source_line_num; STATICDEF int ext_source_line_len; STATICDEF int ext_source_column; STATICDEF char ext_source_line[MAX_SRC_LINE]; STATICDEF char *ext_table_file_name; STATICDEF boolean_t star_found; STATICFNDCL void *get_memory(size_t n); STATICFNDCL char *exttab_scan_space(char *c); STATICFNDCL char *scan_ident(char *c); STATICFNDCL void scan_behavioral_words(char *c, struct extcall_entry_list *entry_ptr); STATICFNDCL char *scan_labelref(char *c); STATICFNDCL enum gtm_types scan_keyword(char **c); STATICFNDCL int scan_array_bound(char **b,int curr_type); STATICFNDCL char *read_table(char *b, int l, FILE *f); STATICFNDCL void put_mstr(mstr *src, mstr *dst); STATICFNDCL uint4 array_to_mask(boolean_t ar[MAX_ACTUALS], int n); STATICFNDCL void ext_stx_error(int in_error, ...); const int parm_space_needed[] = { 0, 0, SIZEOF(void *), SIZEOF(gtm_int_t), SIZEOF(gtm_uint_t), SIZEOF(gtm_long_t), SIZEOF(gtm_ulong_t), SIZEOF(gtm_float_t), SIZEOF(gtm_double_t), SIZEOF(gtm_int_t *) + SIZEOF(gtm_int_t), SIZEOF(gtm_uint_t *) + SIZEOF(gtm_uint_t), SIZEOF(gtm_long_t *) + SIZEOF(gtm_long_t), SIZEOF(gtm_ulong_t *) + SIZEOF(gtm_ulong_t), SIZEOF(gtm_string_t *) + SIZEOF(gtm_string_t), SIZEOF(gtm_float_t *) + SIZEOF(gtm_float_t), SIZEOF(gtm_char_t *), SIZEOF(gtm_char_t **) + SIZEOF(gtm_char_t *), SIZEOF(gtm_double_t *) + SIZEOF(gtm_double_t), SIZEOF(gtm_pointertofunc_t), SIZEOF(gtm_pointertofunc_t *) + SIZEOF(gtm_pointertofunc_t), SIZEOF(gtm_int_t *) + SIZEOF(gtm_int_t), SIZEOF(gtm_int_t *) + SIZEOF(gtm_int_t), SIZEOF(gtm_long_t *) + SIZEOF(gtm_long_t), SIZEOF(gtm_float_t *) + SIZEOF(gtm_float_t), SIZEOF(gtm_double_t *) + SIZEOF(gtm_double_t), SIZEOF(gtm_string_t), /* gtm_string_t contains a (gtm_long_t) and (gtm_char_t *) */ SIZEOF(gtm_string_t), SIZEOF(gtm_string_t) }; error_def(ERR_CIDIRECTIVE); error_def(ERR_CIENTNAME); error_def(ERR_CIMAXPARAM); error_def(ERR_CIPARTYPE); error_def(ERR_CIRCALLNAME); error_def(ERR_CIRPARMNAME); error_def(ERR_CIRTNTYP); error_def(ERR_CITABENV); error_def(ERR_CITABOPN); error_def(ERR_CIUNTYPE); error_def(ERR_COLON); error_def(ERR_EXTSRCLIN); error_def(ERR_EXTSRCLOC); error_def(ERR_LOGTOOLONG); error_def(ERR_TEXT); error_def(ERR_SYSCALL); error_def(ERR_ZCALLTABLE); error_def(ERR_ZCCOLON); error_def(ERR_ZCCLNUPRTNMISNG); error_def(ERR_ZCCSQRBR); error_def(ERR_ZCCTENV); error_def(ERR_ZCCTNULLF); error_def(ERR_ZCCTOPN); error_def(ERR_ZCENTNAME); error_def(ERR_ZCINVALIDKEYWORD); error_def(ERR_ZCMLTSTATUS); error_def(ERR_ZCPREALLNUMEX); error_def(ERR_ZCPREALLVALINV); error_def(ERR_ZCPREALLVALPAR); error_def(ERR_ZCRCALLNAME); error_def(ERR_ZCRPARMNAME); error_def(ERR_ZCRTENOTF); error_def(ERR_ZCRTNTYP); error_def(ERR_ZCUNAVAIL); error_def(ERR_ZCUNTYPE); error_def(ERR_ZCUSRRTN); /* Manage local get_memory'ed space (the space is never returned) */ STATICFNDEF void *get_memory(size_t n) { void *x; static void *heap_base = 0; static int space_remaining = 0; if (0 == n) return NULL; /* round up to 64 bits bytes */ n = ROUND_UP(n, 8); if (space_remaining < n) { if (SPACE_BLOCK_SIZE < n) return (void *)malloc(n); else { heap_base = (void *)malloc(SPACE_BLOCK_SIZE); assert(NULL != heap_base); /* 4SCA: NULL return */ space_remaining = SPACE_BLOCK_SIZE; } } assert(space_remaining >= n); x = heap_base; heap_base = (char *)heap_base + n; space_remaining -= (int)n; return x; } /* Skip white space */ STATICFNDEF char *exttab_scan_space(char *c) { for ( ; ISSPACE_ASCII(*c); c++, ext_source_column++) ; return c; } /* If this is an identifier (alphameric and underscore), then * return the address after the end of the identifier. * Otherwise, return zero */ STATICFNDEF char *scan_ident(char *c) { char *b; b = c; for ( ; ISALNUM_ASCII(*b) || ('_' == *b); b++, ext_source_column++) ; return (b == c) ? 0 : b; } /* Function for scaning through words in the call table * that change the way the external call behaves (eg. SIGSAFE) * scans through list of key words and fills out any array of booleans * inside the extcall_entry_list struct. Note behavioral words must be * white-space separated and that bad keywords are silently ignored. */ STATICFNDEF void scan_behavioral_words(char *c, struct extcall_entry_list *entry_ptr) { char *token,*temp_tok,*saveptr; int j; const static struct { gtm_ext_call_behavior bwords; const char *str; } conversion [] = { {SIGSAFE, "SIGSAFE"} }; /* scan until : or null if no list of behavioral words */ while(*c != ':') { if (*c == '\0') return; c++; } c++; token = STRTOK_R(c, " \t", &saveptr); while (NULL != token) /* Make Keyword case insensitive */ { temp_tok = token; while (*temp_tok) { *temp_tok = toupper((unsigned char) *temp_tok); temp_tok++; } for (j = 0; j < (SIZEOF(conversion) / SIZEOF(conversion[0])); j++) { if (0 == memcmp(token, conversion[j].str, STRLEN(conversion[j].str))) entry_ptr->ext_call_behaviors[j] = TRUE; } token = STRTOK_R(NULL, " \t", &saveptr); } } /* If this is a label (alphameric, underscore, caret, and percent (C9E12-002681)), then * return the address after the end of the label. * Otherwise, return zero. */ STATICFNDEF char *scan_labelref(char *c) { char *b = c; uint4 state = 0; for ( ; ; b++, ext_source_column++) { if (ISALNUM_ASCII(*b)) continue; if ('%' == *b) { if (1 & state) break; state++; continue; } if ('^' == *b) { if (2 & state) break; state = 2; continue; } break; } return (('(' != *b) && (' ' != *b)) ? 0 : b; } STATICFNDEF enum gtm_types scan_keyword(char **c) { const static struct { char nam[MAX_NAM_LEN]; enum gtm_types typ[MAXIMUM_STARS + 1]; /* One entry for each level of indirection eg [1] is type* */ } xctab[] = { /* typename type type * type ** */ {"void", {gtm_void, gtm_notfound, gtm_notfound} }, {"gtm_int_t", {gtm_int, gtm_int_star, gtm_notfound} }, {"gtm_jboolean_t", {gtm_jboolean, gtm_notfound, gtm_notfound} }, {"gtm_jint_t", {gtm_jint, gtm_notfound, gtm_notfound} }, {"xc_int_t", {gtm_int, gtm_int_star, gtm_notfound} }, {"int", {gtm_int, gtm_notfound, gtm_notfound} }, {"gtm_uint_t", {gtm_uint, gtm_uint_star, gtm_notfound} }, {"xc_uint_t", {gtm_uint, gtm_uint_star, gtm_notfound} }, {"uint", {gtm_uint, gtm_uint_star, gtm_notfound} }, {"gtm_long_t", {gtm_long, gtm_long_star, gtm_notfound} }, {"gtm_jlong_t", {gtm_jlong, gtm_notfound, gtm_notfound} }, {"xc_long_t", {gtm_long, gtm_long_star, gtm_notfound} }, {"long", {gtm_long, gtm_long_star, gtm_notfound} }, {"gtm_ulong_t", {gtm_ulong, gtm_ulong_star, gtm_notfound} }, {"xc_ulong_t", {gtm_ulong, gtm_ulong_star, gtm_notfound} }, {"ulong", {gtm_ulong, gtm_ulong_star, gtm_notfound} }, {"gtm_status_t", {gtm_status, gtm_notfound, gtm_notfound} }, {"xc_status_t", {gtm_status, gtm_notfound, gtm_notfound} }, {"gtm_char_t", {gtm_notfound, gtm_char_star, gtm_char_starstar} }, {"gtm_jstring_t", {gtm_jstring, gtm_notfound, gtm_notfound} }, {"gtm_jbyte_array_t", {gtm_jbyte_array, gtm_notfound, gtm_notfound} }, {"gtm_jbig_decimal_t", {gtm_jbig_decimal, gtm_notfound, gtm_notfound} }, {"xc_char_t", {gtm_notfound, gtm_char_star, gtm_char_starstar} }, {"char", {gtm_notfound, gtm_char_star, gtm_char_starstar} }, {"gtm_string_t", {gtm_notfound, gtm_string_star, gtm_notfound} }, {"xc_string_t", {gtm_notfound, gtm_string_star, gtm_notfound} }, {"string", {gtm_notfound, gtm_string_star, gtm_notfound} }, {"gtm_float_t", {gtm_float, gtm_float_star, gtm_notfound} }, {"gtm_jfloat_t", {gtm_jfloat, gtm_notfound, gtm_notfound} }, {"xc_float_t", {gtm_float, gtm_float_star, gtm_notfound} }, {"float", {gtm_float, gtm_float_star, gtm_notfound} }, {"gtm_double_t", {gtm_double, gtm_double_star, gtm_notfound} }, {"gtm_jdouble_t", {gtm_jdouble, gtm_notfound, gtm_notfound} }, {"xc_double_t", {gtm_double, gtm_double_star, gtm_notfound} }, {"double", {gtm_double, gtm_double_star, gtm_notfound} }, {"gtm_pointertofunc_t", {gtm_pointertofunc, gtm_pointertofunc_star, gtm_notfound} }, {"xc_pointertofunc_t", {gtm_pointertofunc, gtm_pointertofunc_star, gtm_notfound} } }; char *b = *c; char *d; int len, i, star_count; b = exttab_scan_space(b); d = scan_ident(b); if (!d) return gtm_notfound; len = (int)(d - b); for (i = 0 ; i < SIZEOF(xctab) / SIZEOF(xctab[0]) ; i++) { if ((0 == memcmp(xctab[i].nam, b, len)) && ('\0' == xctab[i].nam[len])) { /* got name */ /* scan stars */ for (star_count = 0; (MAXIMUM_STARS >= star_count); star_count++, d++) { d = exttab_scan_space(d); if ('*' != *d) break; star_found = TRUE; } assert(star_count <= MAXIMUM_STARS); *c = exttab_scan_space(d); return xctab[i].typ[star_count]; } } return gtm_notfound; } STATICFNDEF int scan_array_bound(char **b,int curr_type) { char number[MAX_DIGITS_IN_INT]; char *c; int index; const static int default_pre_alloc_value[] = { 0, /* unknown Type */ 0, /* void */ 0, /* status */ 0, /* int */ 0, /* uint */ 0, /* long */ 0, /* unsigned long */ 0, /* float */ 0, /* double */ 1, /* pointer to int */ 1, /* pointer to unsigned int */ 1, /* pointer to long */ 1, /* pointer to unsigned long */ 1, /* pointer to string */ 1, /* pointer to float */ 100, /* pointer to char */ 1, /* pointer to pointer of char */ 1, /* pointer to double */ 1, /* pointer to function */ 1, /* pointer to pointer to function */ 1, /* java int */ 1, /* java int */ 1, /* java long */ 1, /* java float */ 1, /* java double */ 1, /* java string */ 1, /* java byte array */ 1 /* java big decimal */ }; c = *b; /* Already found '[' */ for (index = 0, c++; ']' != *c; c++) { if ('\0' != *c) { if (ISDIGIT_ASCII((int)*c)) number[index++] = *c; else ext_stx_error(ERR_ZCPREALLNUMEX, ext_table_file_name); } else ext_stx_error(ERR_ZCCSQRBR, ext_table_file_name); } c++; /* Skip ']' */ *b = c; if (0 == index) return default_pre_alloc_value[curr_type]; number[index] = 0; return ATOI(number); } STATICFNDEF char *read_table(char *b, int l, FILE *f) { char *t; int fclose_res; ext_source_column = 0; FGETS(b, l, f, t); if (NULL == t) { if (0 != ferror(f)) { FCLOSE(f, fclose_res); /* Expand error message */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); } else assert(feof(f) != 0); } else { /* Unfortunately, fgets does not strip the NL, we will do it for it */ for (ext_source_line_len = 0; *t ; t++) { ext_source_line[ext_source_line_len++] = *t; if (CR == *t) { *t = 0; ext_source_line_num += 1; break; } } } return t; } /* Utility routine to store static mstr's which will remain for the duration of the process * These mstr's will have a trailing null attached so that UNIX system routines can operate * on them directly as const char *'s */ STATICFNDEF void put_mstr(mstr *src, mstr *dst) { ssize_t n; dst->len = src->len; n = (ssize_t)src->len; assert(n >= 0); dst->addr = (char *)get_memory((size_t)(n + 1)); assertpro(NULL != dst->addr); /* 4SCA: NULL return */ if (0 < n) memcpy(dst->addr, src->addr, dst->len); dst->addr[n] = 0; return; } /* Utility to convert an array of bool's to a bit mask */ STATICFNDEF uint4 array_to_mask(boolean_t ar[MAX_ACTUALS], int n) { uint4 mask = 0; int i; for (i = n - 1; 0 <= i; i--) { assert((ar[i] & ~1) == 0); mask = (mask << 1) | ar[i]; } return mask; } /* Note: Need condition handler to clean-up allocated structures and close intput file in the event of an error */ struct extcall_package_list *exttab_parse(mval *package) { boolean_t is_input[MAX_ACTUALS], is_output[MAX_ACTUALS], got_status; char *end, str_buffer[MAX_TABLINE_LEN], str_temp_buffer[MAX_TABLINE_LEN + 1], *tbp; enum gtm_types ret_tok, parameter_types[MAX_ACTUALS], pr; FILE *ext_table_file_handle; int fclose_res, i, keywordlen, len; int parameter_alloc_values[MAX_ACTUALS], parameter_count, ret_pre_alloc_val; mstr callnam, clnuprtn, rtnnam, trans, val; struct extcall_entry_list *entry_ptr; struct extcall_package_list *pak; void_ptr_t pakhandle; /* First, construct package name environment variable */ MEMCPY_LIT(str_buffer, PACKAGE_ENV_PREFIX); tbp = &str_buffer[SIZEOF(PACKAGE_ENV_PREFIX) - 1]; if (package->str.len) { /* guaranteed by compiler */ assert(package->str.len < MAX_NAME_LENGTH - SIZEOF(PACKAGE_ENV_PREFIX) - 1); *tbp++ = '_'; memcpy(tbp, package->str.addr, package->str.len); tbp += package->str.len; } *tbp = 0; /* Now we have the environment name, lookup file name */ ext_table_file_name = GETENV(str_buffer); if (NULL == ext_table_file_name) { /* Environment variable for the package not found */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZCCTENV, 2, LEN_AND_STR(str_buffer)); } Fopen(ext_table_file_handle, ext_table_file_name, "r"); if (NULL == ext_table_file_handle) { /* Package's external call table could not be found */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZCCTOPN, 2, LEN_AND_STR(ext_table_file_name)); } ext_source_line_num = 0; /* Pick-up name of shareable library */ tbp = read_table(LIT_AND_LEN(str_buffer), ext_table_file_handle); if (NULL == tbp) { /* External call table is a null file */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZCCTNULLF, 2, package->str.len, package->str.addr); } STRNCPY_STR(str_temp_buffer, str_buffer, MAX_TABLINE_LEN); val.addr = str_temp_buffer; val.len = STRLEN(str_temp_buffer); /* Need to copy the str_buffer into another temp variable since * TRANS_LOG_NAME requires input and output buffers to be different. * If there is an env variable present in the pathname, TRANS_LOG_NAME * expands it and return SS_NORMAL. Else it returns SS_NOLOGNAM. * Instead of checking 2 return values, better to check against SS_LOG2LONG * which occurs if the pathname is too long after any kind of expansion. */ if (SS_LOG2LONG == TRANS_LOG_NAME(&val, &trans, str_buffer, SIZEOF(str_buffer), dont_sendmsg_on_log2long)) { /* Env variable expansion in the pathname caused buffer overflow */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_LOGTOOLONG, 3, val.len, val.addr, SIZEOF(str_buffer) - 1); } pakhandle = fgn_getpak(str_buffer, INFO); if (NULL == pakhandle) { /* Unable to obtain handle to the shared library */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZCUNAVAIL, 2, package->str.len, package->str.addr); } pak = get_memory(SIZEOF(*pak)); pak->first_entry = 0; put_mstr(&package->str, &pak->package_name); pak->package_handle = pakhandle; pak->package_clnup_rtn = NULL; len = STRLEN("GTMSHLIBEXIT"); /* At this point, we have a valid package, pointed to by pak */ # ifdef DEBUG_EXTCALL FPRINTF(stderr, "GT.M external call opened package name: %s\n", pak->package_name.addr); # endif for (;;) { star_found = FALSE; tbp = read_table(LIT_AND_LEN(str_buffer), ext_table_file_handle); if (NULL == tbp) break; tbp = exttab_scan_space(str_buffer); /* Empty line? */ if (!*tbp) continue; /* No, must be entryref or keyword */ end = scan_ident(tbp); if (!end) ext_stx_error(ERR_ZCENTNAME, ext_table_file_name); keywordlen = end - tbp; end = exttab_scan_space(end); if ('=' == *end) { /* Keyword before '=' has a string of size == STRLEN("GTMSHLIBEXIT") */ if (keywordlen == len) { if (0 == MEMCMP_LIT(tbp, "GTMSHLIBEXIT")) { /* Skip past the '=' char */ tbp = exttab_scan_space(end + 1); if (*tbp) { /* We have a cleanup routine name */ clnuprtn.addr = tbp; clnuprtn.len = scan_ident(tbp) - tbp; clnuprtn.addr[clnuprtn.len] = 0; pak->package_clnup_rtn = (clnupfptr)fgn_getrtn(pak->package_handle, &clnuprtn, ERROR); } else ext_stx_error(ERR_ZCCLNUPRTNMISNG, ext_table_file_name); continue; } } ext_stx_error(ERR_ZCINVALIDKEYWORD, ext_table_file_name); continue; } if ('^' == *end) { end++; end = scan_ident(end); if (!end) ext_stx_error(ERR_ZCENTNAME, ext_table_file_name); } rtnnam.addr = tbp; rtnnam.len = INTCAST(end - tbp); tbp = exttab_scan_space(end); if (':' != *tbp++) ext_stx_error(ERR_ZCCOLON, ext_table_file_name); /* Get return type */ ret_tok = scan_keyword(&tbp); /* Check for legal return type */ switch (ret_tok) { case gtm_status: case gtm_void: case gtm_int: case gtm_uint: case gtm_long: case gtm_ulong: case gtm_char_star: case gtm_float_star: case gtm_string_star: case gtm_int_star: case gtm_uint_star: case gtm_long_star: case gtm_ulong_star: case gtm_double_star: case gtm_char_starstar: case gtm_pointertofunc: case gtm_pointertofunc_star: case gtm_jboolean: case gtm_jint: case gtm_jlong: case gtm_jfloat: case gtm_jdouble: case gtm_jstring: case gtm_jbyte_array: case gtm_jbig_decimal: break; default: ext_stx_error(ERR_ZCRTNTYP, ext_table_file_name); } got_status = (ret_tok == gtm_status); /* Get call name */ if ('[' == *tbp) { if (star_found) ret_pre_alloc_val = scan_array_bound(&tbp,ret_tok); else ext_stx_error(ERR_ZCPREALLVALPAR, ext_table_file_name); /* We should allow the pre-allocated value upto to the maximum string size (MAX_STRLEN) plus 1 for the * extra terminating NULL. Negative values would have been caught by scan_array_bound() above. */ if (ret_pre_alloc_val > MAX_STRLEN + 1) ext_stx_error(ERR_ZCPREALLVALINV, ext_table_file_name); } else ret_pre_alloc_val = -1; /* Fix C9E12-002681 */ if ('%' == *tbp) *tbp = '_'; end = scan_ident(tbp); if (!end) ext_stx_error(ERR_ZCRCALLNAME, ext_table_file_name); callnam.addr = tbp; callnam.len = INTCAST(end - tbp); tbp = exttab_scan_space(end); tbp = exttab_scan_space(tbp); for (parameter_count = 0;(MAX_ACTUALS > parameter_count) && (')' != *tbp); parameter_count++) { star_found = FALSE; /* Must have comma if this is not the first parameter, otherwise '(' */ if (((0 == parameter_count)?'(':',') != *tbp++) ext_stx_error(ERR_ZCRPARMNAME, ext_table_file_name); tbp = exttab_scan_space(tbp); /* Special case: () is ok */ if ((0 == parameter_count) && (*tbp == ')')) break; /* Looking for an I, an O or an IO */ is_input[parameter_count] = is_output[parameter_count] = FALSE; if ('I' == *tbp) { is_input[parameter_count] = TRUE; tbp++; } if ('O' == *tbp) { is_output[parameter_count] = TRUE; tbp++; } if (((FALSE == is_input[parameter_count]) && (FALSE == is_output[parameter_count])) ||(':' != *tbp++)) ext_stx_error(ERR_ZCRCALLNAME, ext_table_file_name); /* Scanned colon--now get type */ pr = scan_keyword(&tbp); if (gtm_notfound == pr) ext_stx_error(ERR_ZCUNTYPE, ext_table_file_name); if (gtm_status == pr) { /* Only one type "status" allowed per call */ if (got_status) ext_stx_error(ERR_ZCMLTSTATUS, ext_table_file_name); else got_status = TRUE; } parameter_types[parameter_count] = pr; if ('[' == *tbp) { if (star_found && !is_input[parameter_count]) parameter_alloc_values[parameter_count] = scan_array_bound(&tbp, pr); else ext_stx_error(ERR_ZCPREALLVALPAR, ext_table_file_name); /* We should allow the pre-allocated value upto to the maximum string size (MAX_STRLEN) plus 1 for * the extra terminating NULL. Negative values would have been caught by scan_array_bound() above. */ if (parameter_alloc_values[parameter_count] > MAX_STRLEN + 1) ext_stx_error(ERR_ZCPREALLVALINV, ext_table_file_name); } else parameter_alloc_values[parameter_count] = -1; tbp = exttab_scan_space(tbp); } entry_ptr = get_memory(SIZEOF(*entry_ptr)); memset(entry_ptr->ext_call_behaviors, 0, SIZEOF(entry_ptr->ext_call_behaviors)); /* note fills out part of entry_ptr */ scan_behavioral_words(tbp, entry_ptr); entry_ptr->next_entry = pak->first_entry; pak->first_entry = entry_ptr; entry_ptr->return_type = ret_tok; entry_ptr->ret_pre_alloc_val = ret_pre_alloc_val; entry_ptr->argcnt = parameter_count; entry_ptr->input_mask = array_to_mask(is_input, parameter_count); entry_ptr->output_mask = array_to_mask(is_output, parameter_count); entry_ptr->parms = get_memory(parameter_count * SIZEOF(entry_ptr->parms[0])); entry_ptr->param_pre_alloc_size = get_memory(parameter_count * SIZEOF(intszofptr_t)); entry_ptr->parmblk_size = (SIZEOF(void *) * parameter_count) + SIZEOF(intszofptr_t); for (i = 0 ; i < parameter_count; i++) { entry_ptr->parms[i] = parameter_types[i]; assert(gtm_void != parameter_types[i]); entry_ptr->parmblk_size += parm_space_needed[parameter_types[i]]; entry_ptr->param_pre_alloc_size[i] = parameter_alloc_values[i]; } put_mstr(&rtnnam, &entry_ptr->entry_name); put_mstr(&callnam, &entry_ptr->call_name); /* The reason for passing INFO severity is that PROFILE has several routines listed in * the external call table that are not in the shared library. PROFILE folks would * rather see info/warning messages for such routines at shared library open time, * than error out. These unimplemented routines, they say were not being called from * the application and wouldn't cause any application failures. If we fail to open * the shared libary, or we fail to locate a routine that is called from the * application, we issue rts_error message (in extab_parse.c). */ entry_ptr->fcn = fgn_getrtn(pak->package_handle, &entry_ptr->call_name, INFO); # ifdef DEBUG_EXTCALL FPRINTF(stderr, " package entry point: %s, address: %x\n", entry_ptr->entry_name.addr, entry_ptr->fcn); # endif } FCLOSE(ext_table_file_handle, fclose_res); return pak; } callin_entry_list* citab_parse (boolean_t internal_use) { int parameter_count, i, fclose_res; uint4 inp_mask, out_mask, mask; mstr labref, callnam; enum gtm_types ret_tok, parameter_types[MAX_ACTUALS], pr; char str_buffer[MAX_TABLINE_LEN], *tbp, *end, rcfpath[GTM_PATH_MAX]; FILE *ext_table_file_handle; callin_entry_list *entry_ptr = NULL, *save_entry_ptr = NULL; if (!internal_use) { ext_table_file_name = GETENV(CALLIN_ENV_NAME); if (!ext_table_file_name) /* environment variable not set */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_CITABENV, 2, LEN_AND_STR(CALLIN_ENV_NAME)); } else { assert(gtm_dist_ok_to_use); SNPRINTF(rcfpath, GTM_PATH_MAX, "%s/%s", gtm_dist, COMM_FILTER_FILENAME); ext_table_file_name = rcfpath; } Fopen(ext_table_file_handle, ext_table_file_name, "r"); if (!ext_table_file_handle) /* call-in table not found */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(11) ERR_CITABOPN, 2, LEN_AND_STR(ext_table_file_name), ERR_SYSCALL, 5, LEN_AND_LIT("fopen"), CALLFROM, errno); ext_source_line_num = 0; while (read_table(LIT_AND_LEN(str_buffer), ext_table_file_handle)) { if (!*(tbp = exttab_scan_space(str_buffer))) continue; if (!(end = scan_ident(tbp))) ext_stx_error(ERR_CIRCALLNAME, ext_table_file_name); callnam.addr = tbp; callnam.len = INTCAST(end - tbp); tbp = exttab_scan_space(end); if (':' != *tbp++) ext_stx_error(ERR_COLON, ext_table_file_name); ret_tok = scan_keyword(&tbp); /* return type */ switch (ret_tok) /* return type valid ? */ { case gtm_void: case gtm_char_star: case gtm_int_star: case gtm_uint_star: case gtm_long_star: case gtm_ulong_star: case gtm_float_star: case gtm_double_star: case gtm_string_star: case gtm_jboolean: case gtm_jint: case gtm_jlong: case gtm_jfloat: case gtm_jdouble: case gtm_jstring: case gtm_jbyte_array: case gtm_jbig_decimal: break; default: ext_stx_error(ERR_CIRTNTYP, ext_table_file_name); } labref.addr = tbp; if ((end = scan_labelref(tbp))) labref.len = INTCAST(end - tbp); else ext_stx_error(ERR_CIENTNAME, ext_table_file_name); tbp = exttab_scan_space(end); inp_mask = out_mask = 0; for (parameter_count = 0; (*tbp && ')' != *tbp); parameter_count++) { if (MAX_ACTUALS <= parameter_count) ext_stx_error(ERR_CIMAXPARAM, ext_table_file_name); /* must have comma if this is not the first parameter, otherwise '(' */ if (((0 == parameter_count)?'(':',') != *tbp++) ext_stx_error(ERR_CIRPARMNAME, ext_table_file_name); tbp = exttab_scan_space(tbp); if ((0 == parameter_count) && (*tbp == ')')) /* special case () */ break; /* looking for an I, a O or an IO */ mask = (1 << parameter_count); inp_mask |= ('I' == *tbp) ? (tbp++, mask) : 0; out_mask |= ('O' == *tbp) ? (tbp++, mask) : 0; if ((!(inp_mask & mask) && !(out_mask & mask)) || (':' != *tbp++)) ext_stx_error(ERR_CIDIRECTIVE, ext_table_file_name); switch ((pr = scan_keyword(&tbp))) /* valid param type? */ { case gtm_int: case gtm_uint: case gtm_long: case gtm_ulong: case gtm_float: case gtm_double: if (out_mask & mask) ext_stx_error(ERR_CIPARTYPE, ext_table_file_name); /* fall-thru */ case gtm_char_star: case gtm_int_star: case gtm_uint_star: case gtm_long_star: case gtm_ulong_star: case gtm_float_star: case gtm_double_star: case gtm_string_star: case gtm_jboolean: case gtm_jint: case gtm_jlong: case gtm_jfloat: case gtm_jdouble: case gtm_jstring: case gtm_jbyte_array: case gtm_jbig_decimal: break; default: ext_stx_error(ERR_CIUNTYPE, ext_table_file_name); } parameter_types[parameter_count] = pr; tbp = exttab_scan_space(tbp); } if (!*tbp) ext_stx_error(ERR_CIRPARMNAME, ext_table_file_name); entry_ptr = get_memory(SIZEOF(callin_entry_list)); entry_ptr->next_entry = save_entry_ptr; save_entry_ptr = entry_ptr; entry_ptr->return_type = ret_tok; entry_ptr->argcnt = parameter_count; entry_ptr->input_mask = inp_mask; entry_ptr->output_mask = out_mask; entry_ptr->parms = get_memory(parameter_count * SIZEOF(entry_ptr->parms[0])); for (i = 0 ; i < parameter_count; i++) entry_ptr->parms[i] = parameter_types[i]; put_mstr(&labref, &entry_ptr->label_ref); put_mstr(&callnam, &entry_ptr->call_name); } FCLOSE(ext_table_file_handle, fclose_res); return entry_ptr; } STATICFNDEF void ext_stx_error(int in_error, ...) { va_list args; char *ext_table_name; char buf[MAX_SRC_LINE], *b; int num_tabs, num_spaces; va_start(args, in_error); ext_table_name = va_arg(args, char *); va_end(args); num_tabs = ext_source_column/TABLEN; num_spaces = ext_source_column%TABLEN; b = &buf[0]; memset(buf, '\t', num_tabs+2); b += num_tabs+NUM_TABS_FOR_GTMERRSTR; memset(b, ' ', num_spaces); b += num_spaces; memcpy(b, "^-----", POINTER_SIZE); b += POINTER_SIZE; *b = 0; dec_err(VARLSTCNT(6) ERR_EXTSRCLIN, 4, ext_source_line_len, ext_source_line, b - &buf[0], &buf[0]); dec_err(VARLSTCNT(6) ERR_EXTSRCLOC, 4, ext_source_column, ext_source_line_num, LEN_AND_STR(ext_table_name)); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) in_error); } fis-gtm-V7.0-005/sr_unix/gvcst_spr_kill.c0000644000032200000250000001176414342376334017247 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_stdio.h" #include "gtmio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ #ifdef GTM_TRIGGER #include #include "gv_trigger.h" /* for IS_EXPLICIT_UPDATE_NOASSERT macro used by IS_OK_TO_INVOKE_GVCST_KILL macro */ #endif #include "io.h" #include "gvcst_protos.h" #include "change_reg.h" #include "op.h" #include "op_tcommit.h" #include "tp_frame.h" #include "tp_restart.h" #include "targ_alloc.h" #include "error.h" #include "stack_frame.h" #include "gtmimagename.h" #include "gvt_inline.h" LITREF mval literal_batch; GBLREF gv_key *gv_currkey; GBLREF gd_region *gv_cur_region; GBLREF uint4 dollar_tlevel; #ifdef DEBUG /* See comment in op_gvkill.c for why this GBLREF is necessary. */ GBLREF jnl_gbls_t jgbl; #endif DEFINE_NSB_CONDITION_HANDLER(gvcst_spr_kill_ch) void gvcst_spr_kill(void) { boolean_t spr_tpwrapped; boolean_t est_first_pass; int reg_index; gd_binding *start_map, *end_map, *map; gd_region *reg, *gd_reg_start; gd_addr *addr; gv_namehead *start_map_gvt; gvnh_reg_t *gvnh_reg; trans_num gd_targ_tn, *tn_array; # ifdef DEBUG int save_dollar_tlevel; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; start_map = TREF(gd_targ_map); /* set up by op_gvname/op_gvnaked/op_gvextnam done just before invoking op_gvkill */ start_map_gvt = gv_target; /* save gv_target corresponding to start_map so we can restore at end */ /* Find out if the next (in terms of $order) key maps to same map as currkey. If so, no spanning activity needed */ GVKEY_INCREMENT_ORDER(gv_currkey); end_map = gv_srch_map_linear(start_map, (char *)&gv_currkey->base[0], gv_currkey->end - 1); BACK_OFF_ONE_MAP_ENTRY_IF_EDGECASE(gv_currkey->base, gv_currkey->end - 1, end_map); GVKEY_UNDO_INCREMENT_ORDER(gv_currkey); if (start_map == end_map) { assert(gv_target == start_map_gvt); if (IS_OK_TO_INVOKE_GVCST_KILL(start_map_gvt)) gvcst_kill(TRUE); return; } /* Do any initialization that is independent of retries BEFORE the op_tstart */ addr = TREF(gd_targ_addr); assert(NULL != addr); gd_reg_start = &addr->regions[0]; tn_array = TREF(gd_targ_reg_array); gvnh_reg = TREF(gd_targ_gvnh_reg); assert(NULL != gvnh_reg); assert(NULL != gvnh_reg->gvspan); /* Now that we know the keyrange maps to more than one region, go through each of them and do the kill. * Since multiple regions are potentially involved, need a TP fence. */ DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); if (!dollar_tlevel) { spr_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0); ESTABLISH_NORET(gvcst_spr_kill_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); } else spr_tpwrapped = FALSE; assert(gv_cur_region == start_map->reg.addr); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); /* Do any initialization that is dependent on retries AFTER the op_tstart */ map = start_map; INCREMENT_GD_TARG_TN(gd_targ_tn); /* takes a copy of incremented "TREF(gd_targ_tn)" into local variable "gd_targ_tn" */ /* Verify that initializations that happened before op_tstart are still unchanged */ assert(addr == TREF(gd_targ_addr)); assert(tn_array == TREF(gd_targ_reg_array)); assert(gvnh_reg == TREF(gd_targ_gvnh_reg)); for ( ; map <= end_map; map++) { ASSERT_BASEREG_OPEN_IF_STATSREG(map); /* "gv_srch_map_linear" call above should have ensured that */ reg = map->reg.addr; GET_REG_INDEX(addr, gd_reg_start, reg, reg_index); /* sets "reg_index" */ assert((map != start_map) || (tn_array[reg_index] != gd_targ_tn)); assert(TREF(gd_targ_reg_array_size) > reg_index); if (tn_array[reg_index] == gd_targ_tn) continue; if (map != start_map) GV_BIND_SUBSREG(addr, reg, gvnh_reg); /* sets gv_target/gv_cur_region/cs_addrs */ assert(reg->open); if (IS_OK_TO_INVOKE_GVCST_KILL(gv_target)) gvcst_kill(TRUE); tn_array[reg_index] = gd_targ_tn; } if (gv_target != start_map_gvt) { /* Restore gv_cur_region/gv_target etc. */ gv_target = start_map_gvt; gv_cur_region = start_map->reg.addr; change_reg(); } DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); if (spr_tpwrapped) { op_tcommit(); REVERT; /* remove our condition handler */ } assert(save_dollar_tlevel == dollar_tlevel); return; } fis-gtm-V7.0-005/sr_unix/fgn_getinfo.c0000644000032200000250000001371414342376334016504 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_limits.h" #include #include #include "real_len.h" /* for COPY_DLERR_MSG */ #include "lv_val.h" /* needed for "fgncal.h" */ #include "fgncal.h" #include "util.h" #include "gtmmsg.h" #include "error.h" #include "restrict.h" /* Needed for restrictions */ #include "have_crit.h" /* Needed for defer interrupts */ error_def(ERR_DLLNOCLOSE); error_def(ERR_DLLNOOPEN); error_def(ERR_DLLNORTN); error_def(ERR_RESTRICTEDOP); error_def(ERR_TEXT); GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF unsigned int gtm_dist_len; GBLREF boolean_t gtm_dist_ok_to_use; /* below comments applicable only to tru64 */ /* dlsym() is bound to return short pointer because of -taso loader flag * dlopen() returns a long pointer. All the callers of fgn_getpak() should take care of this. * dlclose() uses the handle generated above. So, the same semantics apply. * dlerror() returns a char pointer, which is again long. */ /* --- end of tru64 comments --- */ /* fgn_getinfo.c - external call lookup package * * Version Common to all *NIX platforms execept zOS * Note :- There is another zOS-specific version of the same file. */ /* Lookup package. Return package handle if success, NULL otherwise. * package_name - DLL name * msgtype - message severity of the errors reported if any. * Note - Errors are not issued if msgtype is SUCCESS, which is to be used if the callers are not * interested in message report and not willing to have condition handler overhead. */ void_ptr_t fgn_getpak(char *package_name, int msgtype) { void_ptr_t ret_handle; char_ptr_t dummy_err_str; char err_str[MAX_ERRSTR_LEN]; /* needed as util_out_print doesn't handle 64bit pointers */ char librarypath[GTM_PATH_MAX], *lpath = NULL; intrpt_state_t prev_intrpt_state; assert(gtm_dist_ok_to_use); if ((RESTRICTED(library_load_path)) && (0 != memcmp(gtm_dist, package_name, gtm_dist_len))) { /* Restrictions in place and the path is not somewhere under $gtm_dist */ lpath = librarypath; SNPRINTF(lpath, GTM_PATH_MAX, GTM_PLUGIN_FMT_FULL, gtm_dist, strrchr(package_name, '/')); } else lpath = package_name; DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); ret_handle = dlopen(lpath, RTLD_LAZY); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); if (NULL == ret_handle) { if (SUCCESS != msgtype) { assert(!(msgtype & ~SEV_MSK)); if (RESTRICTED(library_load_path)) { SNPRINTF(err_str, MAX_ERRSTR_LEN, "dlopen(%s)", lpath); if ((INFO == msgtype)) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) MAKE_MSG_TYPE(ERR_RESTRICTEDOP, msgtype), 1, err_str); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) MAKE_MSG_TYPE(ERR_RESTRICTEDOP, msgtype), 1, err_str); } COPY_DLLERR_MSG(dummy_err_str, err_str); if ((INFO == msgtype)) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) MAKE_MSG_TYPE(ERR_DLLNOOPEN, msgtype), 2, LEN_AND_STR(lpath), ERR_TEXT, 2, LEN_AND_STR(err_str)); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) MAKE_MSG_TYPE(ERR_DLLNOOPEN, msgtype), 2, LEN_AND_STR(lpath), ERR_TEXT, 2, LEN_AND_STR(err_str)); } } return ret_handle; } /* Lookup an external function. Return function address if success, NULL otherwise. * package_handle - DLL handle returned by fgn_getpak * entry_name - symbol name to be looked up * msgtype - message severity of the errors reported if any. * Note: If msgtype is SUCCESS, errors are not issued. It is useful if the callers are not * interested in message report and not willing to have condition handler overhead (eg. zro_search). */ fgnfnc fgn_getrtn(void_ptr_t package_handle, mstr *entry_name, int msgtype) { void_ptr_t sym_addr; char_ptr_t dummy_err_str; void *short_sym_addr; char err_str[MAX_ERRSTR_LEN]; /* needed as util_out_print doesn't handle 64bit pointers */ if (!(sym_addr = dlsym(package_handle, entry_name->addr))) { if (SUCCESS != msgtype) { assert(!(msgtype & ~SEV_MSK)); COPY_DLLERR_MSG(dummy_err_str, err_str); if ((INFO == msgtype)) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) MAKE_MSG_TYPE(ERR_DLLNORTN, msgtype), 2, LEN_AND_STR(entry_name->addr), ERR_TEXT, 2, LEN_AND_STR(err_str)); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) MAKE_MSG_TYPE(ERR_DLLNORTN, msgtype), 2, LEN_AND_STR(entry_name->addr), ERR_TEXT, 2, LEN_AND_STR(err_str)); } } else { /* Tru64 - dlsym() is bound to return short pointer because of ld -taso flag used for GT.M */ #ifdef __osf__ short_sym_addr = sym_addr; if (short_sym_addr != sym_addr) { sym_addr = NULL; /* always report an error irrespective of msgtype - since this code should never * have executed and/or the DLL might need to be rebuilt with 32-bit options */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_DLLNORTN, 2, LEN_AND_STR(entry_name->addr), ERR_TEXT, 2, LEN_AND_LIT("Symbol is loaded above the lower 31-bit address space")); } #endif } return (fgnfnc)sym_addr; } void fgn_closepak(void_ptr_t package_handle, int msgtype) { char_ptr_t dummy_err_str; int status; char err_str[MAX_ERRSTR_LEN]; status = dlclose(package_handle); if (0 != status && SUCCESS != msgtype) { assert(!(msgtype & ~SEV_MSK)); COPY_DLLERR_MSG(dummy_err_str, err_str); if ((INFO == msgtype)) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) MAKE_MSG_TYPE(ERR_DLLNOCLOSE, msgtype), 0, ERR_TEXT, 2, LEN_AND_STR(err_str)); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) MAKE_MSG_TYPE(ERR_DLLNOCLOSE, msgtype), 0, ERR_TEXT, 2, LEN_AND_STR(err_str)); } } fis-gtm-V7.0-005/sr_unix/obj_code.c0000644000032200000250000003242214342376335015761 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_fcntl.h" #include "gtm_stdio.h" #include #include "gtm_stat.h" #include #include "gtm_stdlib.h" #include "gtm_unistd.h" #include "compiler.h" #include "obj_gen.h" #include #include "cmd_qlf.h" #include "cgp.h" #include "gtmio.h" #include "eintr_wrappers.h" #include "mmemory.h" #include "obj_file.h" #include "alloc_reg.h" #include "jmp_opto.h" #include "mlabel2xtern.h" #include "cg_var.h" #include "gtm_string.h" #include "objlabel.h" #include "stringpool.h" #include "min_max.h" #include "rtn_src_chksum.h" #include "mmrhash.h" #include "arlinkdbg.h" #include "incr_link.h" #include "have_crit.h" GBLREF uint4 lits_text_size, lits_mval_size; GBLREF int4 curr_addr, code_size; GBLREF char cg_phase; /* code generation phase */ GBLREF char cg_phase_last; /* previous code generation phase */ GBLREF boolean_t run_time; GBLREF command_qualifier cmd_qlf; GBLREF int4 mvmax, mlmax, mlitmax, sa_temps[], sa_temps_offset[]; GBLREF mlabel *mlabtab; GBLREF mline mline_root; GBLREF mvar *mvartab; GBLREF mident module_name, int_module_name; GBLREF int4 gtm_object_size; GBLREF int4 sym_table_size; GBLREF int4 linkage_size; GBLREF uint4 lnkrel_cnt; /* number of entries in linkage Psect to relocate */ GBLREF spdesc stringpool; GBLREF short object_name_len; GBLREF char object_file_name[]; GBLREF int object_file_des; /* The sections of the internal GT.M object (sans native object wrapper) are grouped * according to their type (R/O-retain, R/O-release, R/W-retain, R/W-release). The * "retain"/"release" refers to whether the sections in that segment are retained if the * module is replaced by an explicit ZLINK. * * The GT.M object layout (in the native .text section) is as follows: * * +---------------+ * | rhead | > - R/O - retain * +---------------+ Alternative layout if compiled with GTM_DYNAMIC_LITERALS: * | generated | \ \ * | code | \ \ * + - - - - - - - + | | * | line num tbl | |- R/O releasable | * + - - - - - - - + | |- R/O releasable * | lit text pool | | | * +---------------+ / | * | lkg name tbl | / | * +---------------+ / * | lit mval tbl | \ / * +---------------+ |- R/W releasable * | variable tbl | / > - R/W releasable * + - - - - - - - + * | label tbl | > - R/W retain * +---------------+ * | relocations | > - relocations for external syms (not kept after link) * +---------------+ * | symbol tbl | > - external symbol table (not kept after link) * +---------------+ * * Note in addition to the above layout, a "linkage section" is allocated at run time and is * also releasable. The linkage table and the linkage name table are co-indexed and provide the * routine or label names for the given entry. The linkage name table consists of unrelocated mstrs * as they are only ever used once so need not be resolved. * * If GTM_DYNAMIC_LITERALS is enabled, the literal mval table becomes part of the R/O-release section. * In the case of shared libraries, this spares each process from having to take a malloced copy the lit mval table * at link time (sr_unix/incr_link.c). This memory saving optimization is only available on USHBIN-supported platforms */ error_def(ERR_OBJFILERR); error_def(ERR_SYSCALL); error_def(ERR_TEXT); STATICFNDCL void cg_lab (mlabel *mlbl, char *do_emit); void obj_code (uint4 src_lines, void *checksum_ctx) { int i, status; uint4 lits_pad_size, object_pad_size, lnr_pad_size, lnkname_pad_size; int4 offset; int4 old_code_size; rhdtyp rhead; mline *mlx, *mly; gtm_uint16 objhash; var_tabent *vptr; intrpt_state_t prev_intrpt_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!run_time); obj_init(); /* Define the routine name global symbol */ define_symbol(GTM_MODULE_DEF_PSECT, (mstr *)&int_module_name); memset(&rhead, 0, SIZEOF(rhead)); alloc_reg(); jmp_opto(); /* Note that this initial setting of curr_addr is historical in that the routine header was * contiguous with the code. This is no longer true and this address would be more correct * if it were set to zero however if that is done and the M code contains a branch or recursive * DO to the top of this routine, that branch will trigger an assert failure in emit_code * because a branch to "location 0" has long-jump implications. Keeping the initial offset * non-zero avoids this problem. We had to add PTEXT_OFFSET to curr_addr to get code_size * after the CGP_APPROX_ADDR phase anyway. Note that in using this offset, the rtaddr field * of the triples has a PTEXT_OFFSET origin so this will need to be accounted for when the * various tables below are generated using the rtaddr field. SE 10/2002 */ curr_addr = PTEXT_OFFSET; cg_phase = CGP_APPROX_ADDR; cg_phase_last = CGP_NOSTATE; code_gen(); code_size = curr_addr; cg_phase = CGP_ADDR_OPT; comp_lits(&rhead); old_code_size = code_size; shrink_trips(); if ((cmd_qlf.qlf & CQ_MACHINE_CODE)) { cg_phase = CGP_ASSEMBLY; code_gen(); } if (!(cmd_qlf.qlf & CQ_OBJECT)) return; /* Executable code offset setup */ rhead.ptext_adr = (unsigned char *)PTEXT_OFFSET; rhead.ptext_end_adr = (unsigned char *)(size_t)code_size; /* Line number table offset setup */ rhead.lnrtab_adr = (lnr_tabent *)rhead.ptext_end_adr; rhead.lnrtab_len = src_lines; code_size += src_lines * SIZEOF(lnr_tabent); lnr_pad_size = PADLEN(code_size, SECTION_ALIGN_BOUNDARY); code_size += lnr_pad_size; /* Literal text section offset setup */ rhead.literal_text_adr = (unsigned char *)(size_t)code_size; rhead.literal_text_len = lits_text_size; code_size += lits_text_size; assert(0 == PADLEN(code_size, NATIVE_WSIZE)); /* Literal extern (linkage) names section (same # of entries as linkage table) */ rhead.linkage_names = (mstr *)(size_t)code_size; code_size += ((linkage_size / SIZEOF(lnk_tabent)) * SIZEOF(mstr)); lnkname_pad_size = PADLEN(code_size, NATIVE_WSIZE); code_size += lnkname_pad_size; /* Literal mval section offset setup */ rhead.literal_adr = (mval *)(size_t)code_size; rhead.literal_len = lits_mval_size / SIZEOF(mval); code_size += lits_mval_size; /* Padding so variable table starts on proper boundary */ lits_pad_size = PADLEN(code_size, SECTION_ALIGN_BOUNDARY); code_size += lits_pad_size; /* Variable table offset setup */ rhead.vartab_adr = (var_tabent *)(size_t)code_size; rhead.vartab_len = mvmax; code_size += mvmax * SIZEOF(var_tabent); /* Linkage section setup. No table in object but need its length. */ rhead.linkage_adr = NULL; rhead.linkage_len = linkage_size / SIZEOF(lnk_tabent); /* Label table offset setup */ rhead.labtab_adr = (lab_tabent *)(size_t)code_size; rhead.labtab_len = mlmax; code_size += mlmax * SIZEOF(lab_tabent); if (mlabtab) walktree((mvar *)mlabtab, cg_lab, NULL); /* Prior to calculating reloc and symbol size, run the linkage chains to fill out * the reloc table and symbol table so we get the same size we will create later. */ comp_linkages(); /* Relocation table offset setup */ rhead.rel_table_off = code_size; code_size += lnkrel_cnt * SIZEOF(struct relocation_info); /* Symbol text list offset setup */ rhead.sym_table_off = code_size; assert(OUTPUT_SYMBOL_SIZE == output_symbol_size()); code_size += OUTPUT_SYMBOL_SIZE; /* Pad to OBJECT_SIZE_ALIGNMENT byte boundary.(Perhaps need for building into shared library -MR) */ object_pad_size = PADLEN(code_size, OBJECT_SIZE_ALIGNMENT); code_size += object_pad_size; gtm_object_size = code_size; rhead.object_len = gtm_object_size; set_rtnhdr_checksum(&rhead, (gtm_rtn_src_chksum_ctx *)checksum_ctx); rhead.objlabel = MAGIC_COOKIE; rhead.temp_mvals = sa_temps[TVAL_REF]; rhead.temp_size = sa_temps_offset[TCAD_REF]; rhead.compiler_qlf = cmd_qlf.qlf; if (cmd_qlf.qlf & CQ_EMBED_SOURCE) rhead.routine_source_offset = TREF(routine_source_offset); /* Start the creation of the output object. On Linux, Solaris, and HPUX, we use ELF routines to push out native object * code wrapper so the wrapper is not part of the object code hash. However, on AIX, the native XCOFF wrapper is pushed * out by our own routines so is part of the hash. Note for AIX, the create_object_file() routine will duplicate the * hash initialization macro below after the native header is written out but before the GT.M object header is written. */ HASH128_STATE_INIT(TREF(objhash_state), 0); DEFER_INTERRUPTS(INTRPT_IN_OBJECT_FILE_COMPILE, prev_intrpt_state); create_object_file(&rhead); ENABLE_INTERRUPTS(INTRPT_IN_OBJECT_FILE_COMPILE, prev_intrpt_state); cg_phase = CGP_MACHINE; code_gen(); /* External entry definitions (line number table */ offset = (int4)(mline_root.externalentry->rtaddr - PTEXT_OFFSET); emit_immed((char *)&offset, SIZEOF(offset)); /* line 0 */ for (mlx = mline_root.child; mlx; mlx = mly) { if (mlx->table) { offset = (int4)(mlx->externalentry->rtaddr - PTEXT_OFFSET); emit_immed((char *)&offset, SIZEOF(offset)); } if (0 == (mly = mlx->child)) /* note the assignment */ { if (0 == (mly = mlx->sibling)) /* note the assignment */ { for (mly = mlx; ; ) { if (0 == (mly = mly->parent)) /* note the assignment */ break; if (mly->sibling) { mly = mly->sibling; break; } } } } } if (lnr_pad_size) /* emit padding so literal text pool starts on proper boundary */ emit_immed(PADCHARS, lnr_pad_size); /* Both literal text pool and literal mval table */ emit_literals(); /* Emit padding so variable table starts on proper boundary */ assert(STR_LIT_LEN(PADCHARS) >= lits_pad_size); if (lits_pad_size) emit_immed(PADCHARS, lits_pad_size); /* Variable table: */ vptr = (var_tabent *)mcalloc(mvmax * SIZEOF(var_tabent)); if (mvartab) walktree(mvartab, cg_var, (char *)&vptr); emit_immed((char *)vptr, mvmax * SIZEOF(var_tabent)); /* The label table */ if (mlabtab) walktree((mvar *)mlabtab, cg_lab, (char *)TRUE); resolve_sym(); /* Associate relocation entries for the same symbol/linkage-table slot together */ output_relocation(); /* Output relocation entries for the linkage section */ output_symbol(); /* Output the symbol table text pool */ /* If there is padding we need to do to fill out the object to the required boundary do it.. */ if (object_pad_size) { assert(STR_LIT_LEN(PADCHARS) >= object_pad_size); emit_immed(PADCHARS, object_pad_size); } DEFER_INTERRUPTS(INTRPT_IN_OBJECT_FILE_COMPILE, prev_intrpt_state); finish_object_file(); /* Flushes object file buffers and writes remaining native object structures */ ENABLE_INTERRUPTS(INTRPT_IN_OBJECT_FILE_COMPILE, prev_intrpt_state); /* Get our 128 bit hash though only the first 8 bytes of it get saved in the routine header */ gtmmrhash_128_result(TADR(objhash_state), gtm_object_size, &objhash); DBGARLNK((stderr, "obj_code: Computed hash value of 0x"lvaddr" for file %.*s\n", objhash.one, object_name_len, object_file_name)); if ((off_t)-1 == lseek(object_file_des, (off_t)(NATIVE_HDR_LEN + OFFSETOF(rhdtyp, objhash)), SEEK_SET)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_OBJFILERR, 2, object_name_len, object_file_name, errno); emit_immed((char *)&objhash.one, SIZEOF(gtm_uint64_t)); /* Update 8 bytes of objhash in the file header */ buff_flush(); /* Push it out */ CLOSE_OBJECT_FILE(object_file_des, status); if (-1 == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("close()"), CALLFROM, errno); /* Ready to make object visible. Rename from tmp name to real routine name */ RENAME_TMP_OBJECT_FILE(object_file_name); } /* Routine called to process a given label. Cheezy 2nd parm is due to general purpose * mechanism of the walktree routine that calls us. */ STATICFNDEF void cg_lab(mlabel *mlbl, char *do_emit) { lab_tabent lent; mstr glob_name; if (mlbl->ml && mlbl->gbl) { if (do_emit) { /* Output (2nd) pass, emit the interesting information */ lent.lab_name.len = mlbl->mvname.len; lent.lab_name.addr = (0 < lent.lab_name.len) /* Offset into literal text pool */ ? (char *)(mlbl->mvname.addr - (char *)stringpool.base) : NULL; lent.LABENT_LNR_OFFSET = (lnr_tabent *)(SIZEOF(lnr_tabent) * mlbl->ml->line_number); /* Offset into lnr table */ lent.has_parms = (NO_FORMALLIST != mlbl->formalcnt); /* Flag to indicate any formallist */ GTM64_ONLY(lent.filler = 0); /* Remove garbage due so hashes well */ UTF8_ONLY(lent.lab_name.char_len = 0); /* .. ditto .. */ emit_immed((char *)&lent, SIZEOF(lent)); } else { /* 1st pass, do the definition but no emissions */ mlabel2xtern(&glob_name, &int_module_name, &mlbl->mvname); define_symbol(GTM_CODE, &glob_name); } } return; } fis-gtm-V7.0-005/sr_unix/fgncalsp.h0000644000032200000250000001174114342376334016017 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef FGNCALSP_H_INCLUDED #define FGNCALSP_H_INCLUDED /* fgncalsp.h - UNIX foreign calls (d &package.label) */ #define MAX_NAME_LENGTH 255 /* Maximum length of file name */ #define PACKAGE_ENV_PREFIX "GTMXC" /* Prefix for environemnt variable containing * external call table name */ #define CALLIN_ENV_NAME "GTMCI" /* Call-in table environment variable */ #define MAX_ERRSTR_LEN 1024 /* Maximum length of the error string returned * by dlerror(). Couldn't find any system * defined length, 1024 is just arbitrary */ #define MAX_TABLINE_LEN 1024 /* Maximum length of a line estimated to be sufficient * to specify MAX_ACTUALS parameters in the * callin/xcall table */ #define UNKNOWN_SYSERR "unknown system error" #define COPY_DLLERR_MSG(err_ptr, err_buf) \ { \ int len = 0; \ \ if ((err_ptr = dlerror()) != NULL) \ { \ len = real_len(SIZEOF(err_buf) - 1, (uchar_ptr_t)err_ptr); \ strncpy(err_buf, err_ptr, len); \ err_buf[len] = '\0'; \ } else \ { /* Ensure we will not overrun err_buf limits */ \ assert(ARRAYSIZE(UNKNOWN_SYSERR) < ARRAYSIZE(err_buf)); \ STRCPY(err_buf, UNKNOWN_SYSERR); \ } \ } typedef int4 (*fgnfnc)(); typedef void (*clnupfptr)(); /* A chain of packages, each package has a list of "entries", that is external routine entry points. */ struct extcall_package_list { struct extcall_package_list *next_package; struct extcall_entry_list *first_entry; void_ptr_t package_handle; mstr package_name; clnupfptr package_clnup_rtn; }; enum gtm_types { gtm_notfound, gtm_void, gtm_status, gtm_int, gtm_uint, gtm_long, gtm_ulong, gtm_float, gtm_double, gtm_int_star, gtm_uint_star, gtm_long_star, gtm_ulong_star, gtm_string_star, gtm_float_star, gtm_char_star, gtm_char_starstar, gtm_double_star, gtm_pointertofunc, gtm_pointertofunc_star, gtm_jboolean, gtm_jint, gtm_jlong, gtm_jfloat, gtm_jdouble, gtm_jstring, gtm_jbyte_array, gtm_jbig_decimal }; typedef enum {SIGSAFE, XCBEHAVIORTAILENTRY} gtm_ext_call_behavior; enum callintogtm_fncs { gtmfunc_hiber_start, gtmfunc_hiber_start_any, gtmfunc_start_timer, gtmfunc_cancel_timer, gtmfunc_gtm_malloc, gtmfunc_gtm_free, gtmfunc_unknown_function }; /* There is one of these for each external routine. Each is owned by a package. */ struct extcall_entry_list { struct extcall_entry_list *next_entry; enum gtm_types return_type; /* function return value */ int ret_pre_alloc_val; /* amount of space to be pre-allocated for the return type */ uint4 input_mask; /* is it an input parameter lsb = 1st parm */ uint4 output_mask; /* is it an output parameter lsb = 1st parm */ int parmblk_size; /* size in bytes of parameter block to be allocated for call*/ int argcnt; /* number of arguments */ enum gtm_types *parms; /* pointer to parameter array */ int *param_pre_alloc_size; /* amount of space to be pre-allocated for the parameters */ fgnfnc fcn; /* address of runtime routine */ mstr entry_name; /* name of M entryref */ mstr call_name; /* corresponding name of C function */ boolean_t ext_call_behaviors[XCBEHAVIORTAILENTRY]; /* Array of set behaviors*/ }; /* A list of entries in the call-in table each indicating the signature of a call-in routine */ typedef struct callin_entry_list { mstr label_ref; /* labelref name of M routine */ mstr call_name; /* corresponding name of C function */ uint4 input_mask; /* input parameter? LSB = 1st parm */ uint4 output_mask; /* output parameter? LSB = 1st parm */ unsigned short argcnt; /* number of arguments */ enum gtm_types return_type; enum gtm_types *parms; /* parameter types */ struct callin_entry_list *next_entry; } callin_entry_list; /* parameter block that ci_restart uses to pass arguments to M routine */ typedef struct parmblk_struct { void (*ci_rtn)(void); int4 argcnt; void *rtnaddr, *labaddr, *retaddr; int4 mask; lv_val *args[MAX_ACTUALS]; } parmblk_struct; #include /* function prototypes */ void_ptr_t fgn_getpak(char *pak_name, int msgtype); fgnfnc fgn_getrtn(void_ptr_t pak_handle, mstr *sym_name, int msgtype); void fgn_closepak(void_ptr_t pak_handle, int msgtype); int fgncal_getint(mstr *inp); int fgncal_read_args(mstr *inp); void fgncal_getstr(mstr *inp, mstr *str); void fgncal_lkbind(mstr *inp); void fgn_glopref(mval *v); struct extcall_package_list *exttab_parse (mval *package); callin_entry_list *citab_parse (int internal); #endif fis-gtm-V7.0-005/sr_unix/incr_link.c0000644000032200000250000013737314342376334016177 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include #include #include "gtm_string.h" #include "gtm_unistd.h" #include "gtm_stdio.h" #include "gtm_stat.h" #include #include "compiler.h" #include "urx.h" #include "objlabel.h" #include "gtmio.h" #include "zroutines.h" #include "incr_link.h" #include "cachectl.h" #include "obj_file.h" #include "stringpool.h" #include "gtm_limits.h" #include "min_max.h" #include "gtmdbglvl.h" #include "cmd_qlf.h" /* needed for CQ_UTF8 */ #include "gtm_text_alloc.h" #include "send_msg.h" #include "cacheflush.h" #include "rtnobj.h" #include "zbreak.h" #include "interlock.h" #include "util.h" #include "arlinkdbg.h" /* Define linkage types */ typedef enum { LINK_SHRLIB = 1, /* 0001 Link routine from a shared library */ LINK_SHROBJ, /* 0002 Link routine from a shared object */ LINK_PPRIVOBJ /* 0003 Link a process-private object */ } linktype; #define RELOCATE(field, type, base) field = (type)((unsigned char *)(field) + (UINTPTR_T)(base)) #define RELREAD 50 /* number of relocation entries to buffer */ /* This macro will check if the file is an old non-shared-binary variant of GT.M code and if * so just return IL_RECOMPILE to signal a recompile. The assumption is that if we fall out of this * macro that there is truly a problem and other measures should be taken (e.g. call zl_error()). * At some point this code can be disabled with the NO_NONUSB_RECOMPILE varible defined. Rather * than keep old versions of control blocks around that will confuse the issue, we know that the * routine header of these versions started 10 32bit words into the object. Read in the eight * bytes from that location and check against the JSB_MARKER we still use today. */ #ifndef NO_NONUSB_RECOMPILE # define CHECK_NONUSB_RECOMPILE_RETURN \ { \ if (-1 != (status = (ssize_t)lseek(*file_desc, COFFHDRLEN, SEEK_SET))) \ { \ DOREADRC_OBJFILE(*file_desc, marker, SIZEOF(JSB_MARKER) - 1, status); \ } else \ status = errno; \ if ((0 == status) && (0 == MEMCMP_LIT(marker, JSB_MARKER))) \ { \ ZOS_FREE_TEXT_SECTION; \ zl_error_hskpng(linktyp, file_desc, RECENT_ZHIST); \ return IL_RECOMPILE; /* Signal recompile */ \ } \ } #else # define CHECK_NONUSB_RECOMPILE_RETURN /* No old recompile check is being generated */ #endif /* Define debugging macro for low-level relinking issues */ /* #define DEBUG_LOW_RELINK*/ #ifdef DEBUG_LOW_RELINK # define DBGLRL(x) DBGFPF(x) # define DBGLRL_ONLY(x) x #else # define DBGLRL(x) # define DBGLRL_ONLY(x) #endif /* At some point these statics (like all the others) need to move into gtm_threadgbl since these values should * *not* be shared amongst the threads of the future. */ static unsigned char *sect_ro_rel, *sect_rw_rel, *sect_rw_nonrel; static rhdtyp *hdr; GBLREF mident_fixed zlink_mname; GBLREF mach_inst jsb_action[JSB_ACTION_N_INS]; GBLREF uint4 gtmDebugLevel; GBLREF boolean_t gtm_utf8_mode; #ifdef DEBUG_ARLINK GBLREF mval dollar_zsource; #endif #ifdef __MVS__ GBLDEF unsigned char *text_section; GBLDEF boolean_t extended_symbols_present; /* If ZOS is ever revived, verify the following two fields used properly. For example, total_length is declared here and * passed to extract_text where it is instead called text_counter_ptr. The name "total_length" is also overloaded with * a local of the same name in $sr_os390/obj_filesp.c. */ GBLDEF int total_length; GBLDEF int text_counter; #endif LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; typedef struct res_list_struct { struct res_list_struct *next, *list; unsigned int addr, symnum; } res_list; error_def(ERR_DLLCHSETM); error_def(ERR_DLLCHSETUTF8); error_def(ERR_DLLVERSION); error_def(ERR_INVOBJFILE); error_def(ERR_LOADRUNNING); error_def(ERR_RLNKRECLATCH); error_def(ERR_SYSCALL); error_def(ERR_TEXT); error_def(ERR_ZLINKBYPASS); error_def(ERR_ZLINKFILE); STATICFNDCL void res_free(res_list *root); STATICFNDCL boolean_t addr_fix(int file, unsigned char *shdr, linktype linktyp, urx_rtnref *urx_lcl); STATICFNDCL void zl_error(void *recent_zhist, linktype linktyp, int4 *file, int4 err, int4 len, char *addr, int4 len2, char *addr2); STATICFNDCL void zl_error_hskpng(linktype linktyp, int4 *file, void *recent_zhist); /* incr_link - read and process a mumps object module. Link said module to currently executing image */ #ifdef AUTORELINK_SUPPORTED boolean_t incr_link(int *file_desc, zro_ent *zro_entry, zro_hist *recent_zhist, uint4 fname_len, char *fname) #else boolean_t incr_link(int *file_desc, zro_ent *zro_entry, uint4 fname_len, char *fname) #endif { rhdtyp *old_rhead; rtn_tabent *tabent_ptr; int sect_ro_rel_size, sect_rw_rel_size, name_buf_len, alloc_len, order, zerofd; uint4 lcl_compiler_qlf; boolean_t dynlits; ssize_t status, sect_rw_nonrel_size, sect_ro_rel_offset; size_t offset_correction, rtnname_off; lab_tabent *lbt_ent, *lbt_bot, *lbt_top, *olbt_ent, *olbt_bot, *olbt_top; mident_fixed module_name; pre_v5_mident *pre_v5_routine_name; urx_rtnref urx_lcl_anchor; unsigned char *shdr, *rel_base, *newaddr; mstr rtnname; mval *curlit, *littop; lab_tabent *curlbe, *lbetop; var_tabent *curvar, *vartop; char name_buf[PATH_MAX + 1], marker[SIZEOF(JSB_MARKER) - 1], *rw_rel_start; char rtnname_buf[MAX_MIDENT_LEN]; linktype linktyp; ZOS_ONLY(ESD symbol;) # ifdef _AIX FILHDR hddr; unsigned short magic; # endif # ifdef AUTORELINK_SUPPORTED relinkrec_t *rec; zro_validation_entry *zhent; open_relinkctl_sgm *linkctl; # ifdef DEBUG relinkrec_t *relinkrec; int rtnlen; char save_char, *rtnname2; # endif /* DEBUG */ # ifdef ZLINK_BYPASS va_list save_last_va_list_ptr; boolean_t util_copy_saved; char *save_util_outptr; int4 save_error_condition; # endif # endif /* AUTORELINK_SUPPORTED */ DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (NULL == file_desc) { /* Make sure file_desc can *always* be de-referenced */ zerofd = 0; file_desc = &zerofd; } # ifdef __MVS__ total_length = 0; extended_symbols_present = FALSE; text_counter = 0; memset(&symbol, 0 , SIZEOF(ESD)); assert(NULL == text_section); ZOS_FREE_TEXT_SECTION; # endif urx_lcl_anchor.len = 0; urx_lcl_anchor.addr = NULL; urx_lcl_anchor.lab = 0; urx_lcl_anchor.next = NULL; assert(NULL == hdr); assert(NULL == sect_ro_rel); assert(NULL == sect_rw_rel); assert(NULL == sect_rw_nonrel); hdr = NULL; shdr = NULL; sect_ro_rel = sect_rw_rel = sect_rw_nonrel = NULL; if (*file_desc) { /* This is a disk resident object we share if autorelink is enabled in that directory, or instead we * read/link into process private storage if autorelink is not enabled. */ assert(FD_INVALID != *file_desc); if ((NULL == zro_entry) || (NULL == zro_entry->relinkctl_sgmaddr)) { linktyp = LINK_PPRIVOBJ; /* No history if process private link */ ARLINK_ONLY(assert(NULL == recent_zhist)); } else { linktyp = LINK_SHROBJ; # if defined(AUTORELINK_SUPPORTED) && defined(DEBUG) /* Need a history if sharing the object */ assert(NULL != recent_zhist); /* Later call to rtnobj_shm_malloc will use recent_zhist->end - 1 as the zro_validation_entry * to get at the linkctl and relinkrec so assert that it is indeed linkctl corresponding to zro_entry. */ assert((recent_zhist->end - 1)->relinkctl_bkptr == (open_relinkctl_sgm *)zro_entry->relinkctl_sgmaddr); relinkrec = (recent_zhist->end - 1)->relinkrec; rtnlen = STRLEN(relinkrec->rtnname_fixed.c); assert(fname_len >= (STR_LIT_LEN(DOTOBJ) + rtnlen)); rtnname2 = fname + fname_len - 1; while (rtnname2 >= fname) { if ('/' == *rtnname2) { rtnname2++; break; } rtnname2--; } save_char = *rtnname2; if ('_' == save_char) *rtnname2 = '%'; /* Temporary adjustment for below assert to not fail */ assert(!memcmp(relinkrec->rtnname_fixed.c, rtnname2, rtnlen)); if ('_' == save_char) *rtnname2 = save_char; /* restore */ # endif /* AUTORELINK_SUPPORTED && DEBUG */ } NON_USHBIN_ONLY(assert(LINK_PPRIVOBJ == linktyp)); /* No autorelink in non-ushbin platform */ } else { /* With no file descriptor, this can only be linkage from a shared library and shared libraries have no history */ ARLINK_ONLY(assert(NULL == recent_zhist)); assert(NULL != zro_entry); linktyp = LINK_SHRLIB; } # ifdef DEBUG_ARLINK switch(linktyp) { case LINK_SHROBJ: case LINK_PPRIVOBJ: if (NULL != zro_entry) { DBGFPF((stderr, "incr_link: Requested to (re)link routine %.*s from %.*s\n", fname_len, fname, zro_entry->str.len, zro_entry->str.addr)); } else { DBGFPF((stderr, "incr_link: Requested to (re)link routine %.*s from %.*s\n", fname_len, fname, dollar_zsource.str.len, dollar_zsource.str.addr)); } break; default: /* case LINK_SHRLIB: */ assert(LINK_SHRLIB == linktyp); DBGFPF((stderr, "incr_link: Requested (re)link routine %.*s from shared library %.*s\n", fname_len, fname, zro_entry->str.len, zro_entry->str.addr)); break; } # endif /* Get the routine header where we can make use of it */ hdr = (rhdtyp *)malloc(SIZEOF(rhdtyp)); switch(linktyp) { # ifdef USHBIN_SUPPORTED case LINK_SHRLIB: /* Copy routine header to process private storage so we can make changes to it. Note that on * some platforms, the address returned by dlsym() is not the actual shared code address, but * normally an address to the linkage table, eg. TOC (AIX), PLT (HP-UX). Computing the actual * shared code address is platform dependent and is handled by the macro (see incr_link_sp.h). */ shdr = (unsigned char *)GET_RTNHDR_ADDR(zro_entry->shrsym); memcpy(hdr, shdr, SIZEOF(rhdtyp)); hdr->shlib_handle = zro_entry->shrlib; break; # endif case LINK_PPRIVOBJ: case LINK_SHROBJ: # ifdef _AIX /* Seek past native object headers to get GT.M object's routine header * To check if it is not an xcoff64 bit .o. */ DOREADRC(*file_desc, &hddr, SIZEOF(FILHDR), status); if (0 == status) { magic = hddr.f_magic; if (magic != U64_TOCMAGIC) { ZOS_FREE_TEXT_SECTION; zl_error_hskpng(linktyp, file_desc, RECENT_ZHIST); return IL_RECOMPILE; } } else zl_error(RECENT_ZHIST, linktyp, file_desc, ERR_INVOBJFILE, fname_len, fname, 0, NULL); # endif # ifdef __MVS__ /* In the GOFF .o on zOS, if the symbol name(name of the module) exceeds ESD_NAME_MAX_LENGTH (8), * then 2 extra extended records are emitted, which causes the start of text section to vary */ DOREADRC(*file_desc, &symbol, SIZEOF(symbol), status); /* This is HDR record */ if (0 == status) { DOREADRC(*file_desc, &symbol, SIZEOF(symbol), status); /* First symbol (ESD record) */ if (0 == status) { if (0x01 == symbol.ptv[1]) /* Means the extended records are there */ extended_symbols_present = TRUE; else { assert(0x0 == symbol.ptv[1]); extended_symbols_present = FALSE; } } } if (0 != status) zl_error(RECENT_ZHIST, linktyp, file_desc, ERR_INVOBJFILE, fname_len, fname, 0, NULL); # endif if (-1 != (status = (ssize_t)lseek(*file_desc, NATIVE_HDR_LEN, SEEK_SET))) { ZOS_ONLY(extract_text(*file_desc, &total_length)); DOREADRC_OBJFILE(*file_desc, hdr, SIZEOF(rhdtyp), status); } else status = errno; if (0 != status) { CHECK_NONUSB_RECOMPILE_RETURN; zl_error(RECENT_ZHIST, linktyp, file_desc, ERR_INVOBJFILE, fname_len, fname, 0, NULL); } break; default: assertpro(FALSE /* Invalid linkage type for this platform*/); } if ((0 != memcmp(hdr->jsb, (char *)jsb_action, SIZEOF(jsb_action))) || (0 != memcmp(&hdr->jsb[SIZEOF(jsb_action)], JSB_MARKER, MIN(STR_LIT_LEN(JSB_MARKER), SIZEOF(hdr->jsb) - SIZEOF(jsb_action))))) { if (LINK_SHRLIB != linktyp) /* Shared library cannot recompile so this is always an error */ { CHECK_NONUSB_RECOMPILE_RETURN; } zl_error(RECENT_ZHIST, linktyp, file_desc, ERR_INVOBJFILE, fname_len, fname, 0, NULL); } /* Binary version check. If no match, shlib gets error, otherwise signal recompile */ if (MAGIC_COOKIE != hdr->objlabel) { if (LINK_SHRLIB == linktyp) { if (MAGIC_COOKIE_V5 > hdr->objlabel) { /* The library was built using a version prior to V50FT01. The routine_name field of the * pre-V5 routine header was an 8-byte char array, so read the routine name in the old format */ int len; pre_v5_routine_name = (pre_v5_mident *)((char*)hdr + PRE_V5_RTNHDR_RTNOFF); for (len = 0; len < SIZEOF(pre_v5_mident) && pre_v5_routine_name->c[len]; len++) ; zl_error(RECENT_ZHIST, linktyp, NULL, ERR_DLLVERSION, len, &(pre_v5_routine_name->c[0]), zro_entry->str.len, zro_entry->str.addr); } # if defined(__osf__) || defined(__hppa) else if (MAGIC_COOKIE_V52 > hdr->objlabel) { /* Note: routine_name field has not been relocated yet, so compute its absolute * address in the shared library and use it */ v50v51_mstr *mstr5051; /* declare here so don't have to conditionally add above */ mstr5051 = (v50v51_mstr *)((char *)hdr + V50V51_RTNHDR_RTNMSTR_OFFSET); zl_error(RECENT_ZHIST, linktyp, NULL, ERR_DLLVERSION, mstr5051->len, ((char *)shdr + *(int4 *)((char *)hdr + V50V51_FTNHDR_LITBASE_OFFSET) + (int4)mstr5051->addr), zro_entry->str.len, zro_entry->str.addr); } # endif else /* V52 or later but not current version */ { /* Note: routine_name field has not been relocated yet, so compute its absolute * address in the shared library and use it */ zl_error(RECENT_ZHIST, linktyp, NULL, ERR_DLLVERSION, hdr->routine_name.len, (char *)shdr + (UINTPTR_T)hdr->literal_text_adr + (UINTPTR_T)hdr->routine_name.addr, zro_entry->str.len, zro_entry->str.addr); } } ZOS_FREE_TEXT_SECTION; zl_error_hskpng(linktyp, file_desc, RECENT_ZHIST); return IL_RECOMPILE; } if (((hdr->compiler_qlf & CQ_UTF8) && !gtm_utf8_mode) || (!(hdr->compiler_qlf & CQ_UTF8) && gtm_utf8_mode)) { /* Object file compiled with a different $ZCHSET is being used */ lcl_compiler_qlf = hdr->compiler_qlf; /* Will be cleaned up soon so save a copy */ if (LINK_SHRLIB == linktyp) /* Shared library cannot recompile so this is always an error */ { /* Note: routine_name field has not been relocated yet, so compute its absolute address * in the shared library and use it */ if ((lcl_compiler_qlf & CQ_UTF8) && !gtm_utf8_mode) { zl_error(NULL, linktyp, NULL, ERR_DLLCHSETUTF8, (int)hdr->routine_name.len, (char *)shdr + (UINTPTR_T)hdr->literal_text_adr + (UINTPTR_T)hdr->routine_name.addr, (int)zro_entry->str.len, zro_entry->str.addr); } else { zl_error(NULL, linktyp, NULL, ERR_DLLCHSETM, (int)hdr->routine_name.len, (char *)shdr + (UINTPTR_T)hdr->literal_text_adr + (UINTPTR_T)hdr->routine_name.addr, (int)zro_entry->str.len, zro_entry->str.addr); } } zl_error_hskpng(linktyp, file_desc, RECENT_ZHIST); if ((lcl_compiler_qlf & CQ_UTF8) && !gtm_utf8_mode) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_INVOBJFILE, 2, fname_len, fname, ERR_TEXT, 2, LEN_AND_LIT("Object compiled with CHSET=UTF-8 which is different from $ZCHSET")); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_INVOBJFILE, 2, fname_len, fname, ERR_TEXT, 2, LEN_AND_LIT("Object compiled with CHSET=M which is different from $ZCHSET")); } /* We need to check if this is a relink of an already linked routine. To do that, we need to be able to fetch * the routine name from the object file in question in order to look the routine up. */ switch(linktyp) { ARLINK_ONLY(case LINK_SHROBJ:); case LINK_PPRIVOBJ: rtnname = ((rhdtyp *)hdr)->routine_name; rtnname_off = (size_t)hdr->literal_text_adr + (size_t)rtnname.addr; /* Offset into object of rtnname */ ZOS_ONLY(assertpro(FALSE /* Read file pointer being reset - recode for ZOS */)); /* Read the routine name from the object file */ if (-1 != (status = (ssize_t)lseek(*file_desc, rtnname_off + NATIVE_HDR_LEN, SEEK_SET))) { DOREADRC_OBJFILE(*file_desc, rtnname_buf, rtnname.len, status); } else status = errno; if (0 != status) zl_error(RECENT_ZHIST, linktyp, file_desc, ERR_INVOBJFILE, fname_len, fname, 0, NULL); rtnname.addr = rtnname_buf; /* Reset read file-pointer for object file back to what it was */ status = (ssize_t)lseek(*file_desc, NATIVE_HDR_LEN + SIZEOF(rhdtyp), SEEK_SET); if (-1 == status) zl_error(RECENT_ZHIST, linktyp, file_desc, ERR_INVOBJFILE, fname_len, fname, 0, NULL); break; case LINK_SHRLIB: rtnname = ((rhdtyp *)shdr)->routine_name; /* Routine name not yet relocated so effectively do so */ rtnname.addr = (char *)shdr + (size_t)((rhdtyp *)shdr)->literal_text_adr + (size_t)rtnname.addr; break; default: assert(FALSE); } /* Now check if we know about this routine and if so, if the (new) object hash is the same as the one we currently * have linked. Note both new and old flavors of the routine must either both not be autorelink-enabled or must * both be autorelink-enabled and if autorelink-enabled, must be loaded from the same directory and thus into the same * set of shared memory structures to be able to bypass the link. This might could be eased in the future for certain * special cases. */ if (find_rtn_tabent(&tabent_ptr, &rtnname)) { old_rhead = (rhdtyp *)tabent_ptr->rt_adr; assert(NULL != old_rhead); # ifdef AUTORELINK_SUPPORTED zhent = (LINK_SHROBJ == linktyp) ? recent_zhist->end - 1 : NULL; if ((old_rhead->objhash == hdr->objhash) && (((NULL == zhent) && (NULL == old_rhead->relinkctl_bkptr)) /* Both non-relink */ || (NULL != zhent) && (zhent->relinkctl_bkptr == old_rhead->relinkctl_bkptr))) /* Both same relink */ # else if (old_rhead->objhash == ((rhdtyp *)hdr)->objhash) # endif { /* This is the same routine - abort re-link but if autorelink-enabled, use the new history. The reason * we do this is we should behave at this point as-if we *had* done the relink. That and the fact that * we don't want this routine to attempt to reload itself on every call dictates that we get rid of the * old history we had (with old cycle values) and instead use the new history with the latest cycle * numbers - as if this routine *had been* relinked. * * Note even a pseudo-relink should cancel all the breakpoints in the module. Note we don't address * issues with whether this would have been a recursive relink or not since when we don't actually do * the relink, there's no way to remove breakpoints when the last use of this routine leaves the stack * so eliminate the breakpoints now since newly linked routines are anyway not expected to have * breakpoints. */ zr_remove_zbrks(old_rhead, NOBREAKMSG); /* Info level message that link was bypassed. Since this could pollute the error buffer, save and * restore it across the info message we put out (it is either displayed or it isn't - no need to cache it. */ DBGARLNK((stderr, "incr_link: Bypassing (re)zlink for routine %.*s (old rhead 0x"lvaddr") - same objhash\n", old_rhead->routine_name.len, old_rhead->routine_name.addr, old_rhead)); # ifdef ZLINK_BYPASS /* #ifdef'd out for now due to issues with ERRWETRAP */ /* If this code is enabled please remove the ZLINKBYPASS message from merrors.msg's undocumented * section and document the message. */ SAVE_UTIL_OUT_BUFFER(save_util_outptr, save_last_va_list_ptr, util_copy_saved); save_error_condition = error_condition; error_condition = 0; rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_ZLINKBYPASS, 2, fname_len, fname); /* Info msg returns */ error_condition = save_error_condition; RESTORE_UTIL_OUT_BUFFER(save_util_outptr, save_last_va_list_ptr, util_copy_saved); # endif /* Note do this cleanup (release/reset) after the info message so these values still around if the error * causes an issue in debugging. */ free(hdr); /* Don't need this (new) copy of routine header */ hdr = NULL; # ifdef AUTORELINK_SUPPORTED if (NULL != old_rhead->zhist) free(old_rhead->zhist); /* Free up previously auto-relinked structures */ /* Save new history if supplied or NULL */ old_rhead->zhist = recent_zhist; # endif return IL_DONE; /* bypass link since we have already done the link before */ } } # ifdef AUTORELINK_SUPPORTED /* For shared object, time to pull it into shared memory */ if (LINK_SHROBJ == linktyp) { /* Currently, rtnobj_hdr_t->objLen is a 4-byte field so we cannot support object file sizes > 4Gb for now */ assert(4 == SIZEOF(hdr->object_len)); /* i.e. object file size is guaranteed to be < 4Gb */ assert(4 == SIZEOF(((rtnobj_hdr_t *)NULL)->objLen)); /* i.e. object file size is guaranteed to be < 4Gb */ /* Allocate buffer for routine object in shared memory. If one already exists, use it */ if ((size_t)MAXUINT4 <= ((size_t)hdr->object_len + OFFSETOF(rtnobj_hdr_t, userStorage))) { /* Currently, rtnobj_hdr_t->objLen is a 4-byte field so we cannot support object file * sizes > 4Gb for now. */ ZOS_FREE_TEXT_SECTION; zl_error_hskpng(linktyp, file_desc, RECENT_ZHIST); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, fname_len, fname, ERR_TEXT, 2, LEN_AND_LIT("Object file size > 4Gb cannot be auto-relinked")); } shdr = rtnobj_shm_malloc(recent_zhist, *file_desc, hdr->object_len, hdr->objhash); if (NULL == shdr) { /* Most likely lseek or read of object file into shared memory failed. Possible for example * if the object file is truncated. Issue error. */ zl_error(RECENT_ZHIST, linktyp, file_desc, ERR_INVOBJFILE, fname_len, fname, 0, NULL); } /* We need this link - continue setup */ assert(!memcmp(hdr, shdr, SIZEOF(rhdtyp))); /* Already read rtnhdr from disk, assert that is same as in shm */ (TREF(arlink_loaded))++; /* Count autorelink routines loaded */ } # endif /* Read in and/or relocate the pointers to the various sections. To understand the size calculations * being done note that the contents of the various xxx_adr pointers in the routine header are * initially the offsets from the start of the object. This is so we can address the various sections * via offset now while linking and via address later during runtime. * * Read-only releasable section */ dynlits = DYNAMIC_LITERALS_ENABLED(hdr); rw_rel_start = RW_REL_START_ADR(hdr); /* Marks end of R/O-release section and start of R/W-release section */ /* Assert that relinkctl_bkptr is NULL for LINK_SHRLIB and LINK_PPRIVOBJ. This is assumed by the link-bypass * code above (which returns IL_DONE) which uses "hdr->relinkctl_bkptr" without regard to linktyp of that routine. */ ARLINK_ONLY(assert((LINK_SHROBJ == linktyp) || (NULL == hdr->relinkctl_bkptr));) switch(linktyp) { case LINK_SHROBJ: hdr->shared_object = TRUE; /* Indicate this is linked as a shared object */ # ifdef AUTORELINK_SUPPORTED zhent = recent_zhist->end - 1; hdr->relinkctl_bkptr = zhent->relinkctl_bkptr; # endif /* Note - fall through */ case LINK_SHRLIB: rel_base = shdr; break; case LINK_PPRIVOBJ: sect_ro_rel_size = (unsigned int)((INTPTR_T)rw_rel_start - (INTPTR_T)hdr->ptext_adr); sect_ro_rel = GTM_TEXT_ALLOC(sect_ro_rel_size); /* R/O-release section should be aligned well at this point but make a debug level check to verify */ assert((INTPTR_T)sect_ro_rel == ((INTPTR_T)sect_ro_rel & ~(LINKAGE_PSECT_BOUNDARY - 1))); DOREADRC_OBJFILE(*file_desc, sect_ro_rel, sect_ro_rel_size, status); if (0 != status) zl_error(NULL, linktyp, file_desc, ERR_INVOBJFILE, fname_len, fname, 0, NULL); /* The offset correction is the amount that needs to be applied to a given storage area that * is no longer contiguous with the routine header. In this case, the code and other sections * are no longer contiguous with the routine header but the initial offsets in the routine * header make the assumption that they are. Therefore these sections have a base address equal * to the length of the routine header. The offset correction is what will adjust the base * address so that this offset is removed and the pointer can now truly point to the section * it needs to point to. * * An example may make this more clear. We have two blocks of storage: block A and block B. Now * block A has 2 fields that will ultimately point into various places in block B. These pointers * are initialized to be the offset from the start of block A to the position in block B. Now we * have two cases. In the first case block A and block B are contiguous. Therefore in order to * relocate the addresses in block A, all you have to do is add the base address of block A to * those addresses and they then properly address the areas in block B. Case 2 is that block A * and block B are not contiguous. In this case, to properly adjust the addresses in block A, we * need to do two things. Obviously we need the address for block B. But the offsets currently in * the addresses in block A assume that block A is the origin, not block B so the length of block A * must be subtracted from the offsets to provide the true offset into block B. Then we can add the * address of the block B to this address and have now have the addesses in block A properly address * the areas in block B. In this case, block A is the routine header, block B is the read-only * releasable section. Case one is when the input is from a shared library, case 2 when from a file. */ offset_correction = (size_t)hdr->ptext_adr; rel_base = sect_ro_rel - offset_correction; break; default: assert(FALSE /* Invalid link type */); } RELOCATE(hdr->ptext_adr, unsigned char *, rel_base); RELOCATE(hdr->ptext_end_adr, unsigned char *, rel_base); /* Initialize hdr->shared_ptext_adr appropriately for the link type */ switch(linktyp) { case LINK_SHROBJ: case LINK_SHRLIB: hdr->shared_ptext_adr = hdr->ptext_adr; break; case LINK_PPRIVOBJ: hdr->shared_ptext_adr = NULL; break; default: assert(FALSE /* Invalid link type */); } RELOCATE(hdr->lnrtab_adr, lnr_tabent *, rel_base); RELOCATE(hdr->literal_text_adr, unsigned char *, rel_base); RELOCATE(hdr->linkage_names, mstr *, rel_base); if (dynlits) RELOCATE(hdr->literal_adr, mval *, rel_base); /* Read-write releasable section */ sect_rw_rel_size = (int)((INTPTR_T)hdr->labtab_adr - (INTPTR_T)rw_rel_start); sect_rw_rel = malloc(sect_rw_rel_size); switch(linktyp) { case LINK_SHROBJ: case LINK_SHRLIB: memcpy(sect_rw_rel, shdr + (INTPTR_T)rw_rel_start, sect_rw_rel_size); break; case LINK_PPRIVOBJ: DOREADRC_OBJFILE(*file_desc, sect_rw_rel, sect_rw_rel_size, status); if (0 != status) zl_error(NULL, linktyp, file_desc, ERR_INVOBJFILE, fname_len, fname, 0, NULL); break; default: assert(FALSE /* Invalid link type */); } offset_correction = (size_t)rw_rel_start; rel_base = sect_rw_rel - offset_correction; if (!dynlits) RELOCATE(hdr->literal_adr, mval *, rel_base); RELOCATE(hdr->vartab_adr, var_tabent *, rel_base); /* Also read-write releasable is the linkage section which had no initial value and was thus * not resident in the object. The values in this section will be setup later by addr_fix() * and/or auto-zlink. Note we always allocate at least one element here just so we don't get * the potentially unaligned "null string" address provided by gtm_malloc() when a zero * length is requested. */ alloc_len = hdr->linkage_len * SIZEOF(lnk_tabent); hdr->linkage_adr = (lnk_tabent *)malloc((0 != alloc_len) ? alloc_len : SIZEOF(lnk_tabent)); assert(PADLEN(hdr->linkage_adr, SIZEOF(lnk_tabent) == 0)); assert(((UINTPTR_T)hdr->linkage_adr % SIZEOF(lnk_tabent)) == 0); memset((char *)hdr->linkage_adr, 0, (hdr->linkage_len * SIZEOF(lnk_tabent))); /* Relocations for read-write releasable section. Perform relocation on literal mval table and * variable table entries since they both point to the offsets from the beginning of the * literal text pool. The relocations for the linkage section is done in addr_fix() */ if (!dynlits) { for (curlit = hdr->literal_adr, littop = curlit + hdr->literal_len; curlit < littop; ++curlit) if (curlit->str.len) RELOCATE(curlit->str.addr, char *, hdr->literal_text_adr); } for (curvar = hdr->vartab_adr, vartop = curvar + hdr->vartab_len; curvar < vartop; ++curvar) { assert(0 < curvar->var_name.len); RELOCATE(curvar->var_name.addr, char *, hdr->literal_text_adr); } /* Fixup header's source path and routine names as they both point to the offsets from the * beginning of the literal text pool. */ hdr->src_full_name.addr += (INTPTR_T)hdr->literal_text_adr; hdr->routine_name.addr += (INTPTR_T)hdr->literal_text_adr; if (LINK_SHROBJ == linktyp) { /* For values in shared objects (but not shared libraries) put the name and * path in malloc'd space so in a core file we can have access to the names. */ newaddr = malloc(hdr->src_full_name.len + hdr->routine_name.len); memcpy(newaddr, hdr->src_full_name.addr, hdr->src_full_name.len); memcpy(newaddr + hdr->src_full_name.len, hdr->routine_name.addr, hdr->routine_name.len); hdr->src_full_name.addr = (char *)newaddr; hdr->routine_name.addr = (char *)(newaddr + hdr->src_full_name.len); } if (GDL_PrintEntryPoints & gtmDebugLevel) { /* Prepare name and address for announcement.. */ name_buf_len = (PATH_MAX > hdr->src_full_name.len) ? hdr->src_full_name.len : PATH_MAX; memcpy(name_buf, hdr->src_full_name.addr, name_buf_len); name_buf[name_buf_len] = '\0'; PRINTF("incr_link: %s loaded at 0x%08lx\n", name_buf, (long unsigned int) hdr->ptext_adr); } /* Read-write non-releasable section */ sect_rw_nonrel_size = hdr->labtab_len * SIZEOF(lab_tabent); sect_rw_nonrel = malloc(sect_rw_nonrel_size); switch(linktyp) { case LINK_SHROBJ: case LINK_SHRLIB: memcpy(sect_rw_nonrel, shdr + (INTPTR_T)hdr->labtab_adr, sect_rw_nonrel_size); break; case LINK_PPRIVOBJ: DOREADRC_OBJFILE(*file_desc, sect_rw_nonrel, sect_rw_nonrel_size, status); if (0 != status) zl_error(NULL, linktyp, file_desc, ERR_INVOBJFILE, fname_len, fname, 0, NULL); break; default: assert(FALSE /* Invalid link type */); } hdr->labtab_adr = (lab_tabent *)sect_rw_nonrel; /* Relocations for read-write non-releasable section. Perform relocation on label table entries. */ for (curlbe = hdr->labtab_adr, lbetop = curlbe + hdr->labtab_len; curlbe < lbetop; ++curlbe) { RELOCATE(curlbe->lab_name.addr, char *, hdr->literal_text_adr); RELOCATE(curlbe->lnr_adr, lnr_tabent *, hdr->lnrtab_adr); } /* Remaining initialization */ ARLINK_ONLY(assert(((NULL == recent_zhist) && (LINK_SHROBJ != linktyp)) || ((NULL != recent_zhist) && (LINK_SHROBJ == linktyp)))); ARLINK_ONLY(hdr->zhist = recent_zhist); hdr->current_rhead_adr = hdr; assert(hdr->routine_name.len < SIZEOF(zlink_mname.c)); memcpy(&zlink_mname.c[0], hdr->routine_name.addr, hdr->routine_name.len); zlink_mname.c[hdr->routine_name.len] = 0; /* Do address fix up with relocation and symbol entries from the object. Note that shdr will * never be dereferenced except under a test of the linktyp flag to indicate we are processing * a shared library or a shared object. */ if (!addr_fix(*file_desc, shdr, linktyp, &urx_lcl_anchor)) { urx_free(&urx_lcl_anchor); /* Decrement "refcnt" bump done by "rtnobj_shm_malloc" above */ ARLINK_ONLY(if (LINK_SHROBJ == linktyp) rtnobj_shm_free(hdr, LATCH_GRABBED_FALSE)); zl_error(RECENT_ZHIST, linktyp, file_desc, ERR_INVOBJFILE, fname_len, fname, 0, NULL); } /* Register new routine in routine name vector displacing old one and performing any necessary cleanup */ DBGARLNK((stderr, "incr_link: Registering new version of routine %.*s (rtnhdr 0x"lvaddr")\n", hdr->routine_name.len, hdr->routine_name.addr, hdr)); if (!zlput_rname(hdr)) { urx_free(&urx_lcl_anchor); /* Copy routine name to local variable because zl_error frees it. */ memcpy(&module_name.c[0], hdr->routine_name.addr, hdr->routine_name.len); /* Decrement "refcnt" bump done by "rtnobj_shm_malloc" above */ ARLINK_ONLY(if (LINK_SHROBJ == linktyp) rtnobj_shm_free(hdr, LATCH_GRABBED_FALSE)); zl_error(RECENT_ZHIST, linktyp, file_desc, ERR_LOADRUNNING, (int)hdr->routine_name.len, &module_name.c[0], 0, NULL); } /* Now that new routine is registered, no longer need to do rtnobj_shm_free. * That will be done by relinkctl_rundown as part of scanning the rtn_names[] array. */ /* Fix up of routine headers for old versions of routine so they point to the newest version; these headers are never * tied to routines active on the stack as zlput_rname has already given any such their own copy of the header */ old_rhead = hdr->old_rhead_adr; lbt_bot = hdr->labtab_adr; lbt_top = lbt_bot + hdr->labtab_len; while (old_rhead) { lbt_ent = lbt_bot; olbt_bot = old_rhead->labtab_adr; olbt_top = olbt_bot + old_rhead->labtab_len; for (olbt_ent = olbt_bot; olbt_ent < olbt_top; olbt_ent++) { /* Match new label entries with old label entries */ for (; lbt_ent < lbt_top; lbt_ent++) { MIDENT_CMP(&olbt_ent->lab_name, &lbt_ent->lab_name, order); if (0 >= order) break; } if ((lbt_ent < lbt_top) && !order) { /* Have a label name match. Update line pointer for this entry */ olbt_ent->lnr_adr = lbt_ent->lnr_adr; olbt_ent->has_parms = lbt_ent->has_parms; } else { /* This old label entry has no match. Mark as undefined */ olbt_ent->lnr_adr = NULL; olbt_ent->has_parms = 0; } DBGLRL((stderr, "incr_link: Routine %.*s (rtnhdr 0x"lvaddr") label %.*s (labtab_ent 0x"lvaddr ") set to "lvaddr"\n", hdr->routine_name.len, hdr->routine_name.addr, old_rhead, olbt_ent->lab_name.len, olbt_ent->lab_name.addr, olbt_ent, olbt_ent->lnr_adr)); } /* the copying below could be simpler and faster if the copied rhdtyp / rhead_struct fields formed a single block * within the definition in rthhdr.h so this could use 1 memcpy() rather than picking through the structure */ /* jsb should no longer be in play for this old header */ /* shlib_handle should characterize this old header */ old_rhead->src_full_name = hdr->src_full_name; old_rhead->compiler_qlf = hdr->compiler_qlf; /* added Jan. 2019 */ assert(old_rhead->objlabel == hdr->objlabel); /* added Jan. 2019 */ old_rhead->routine_name = hdr->routine_name; old_rhead->vartab_len = hdr->vartab_len; old_rhead->vartab_adr = hdr->vartab_adr; /* We used labtab_adr and labtab_len above to plug any old labels with matching new labels and deadend any old label * with no corresponding new label. If a there's a new label but no corresponding new label, any currently linked * routine either has no such label, has not used that label, in which case it will correctly discover the new label * from the new table when it later uses it, or it has already called op_labaddr.c which delivered an error that * indicated label was invalid, but left the label unresolved. In the latter case, a subsequent call to op_labaddr * will use the current label table and locate the new label. */ old_rhead->lnrtab_adr = hdr->lnrtab_adr; old_rhead->lnrtab_len = hdr->lnrtab_len; if (0 < old_rhead->literal_text_len) /* section added Jan. 2019; seems right, but no test case proving need */ { /* move any string literals used by current mvals to the string pool before we lose track of them */ assert(old_rhead->literal_text_adr); stp_move((char *)old_rhead->literal_text_adr, (char *)(old_rhead->literal_text_adr + old_rhead->literal_text_len)); } old_rhead->literal_text_adr = hdr->literal_text_adr; old_rhead->literal_text_len = hdr->literal_text_len; /* added Jan. 2019 */ /* shared_object should characterize this old header */ old_rhead->literal_adr = hdr->literal_adr; old_rhead->literal_len = hdr->literal_len; old_rhead->linkage_adr = hdr->linkage_adr; old_rhead->linkage_len = hdr->linkage_len; /* rtn_relinked should characterize this old header */ old_rhead->shared_ptext_adr = hdr->shared_ptext_adr; old_rhead->ptext_adr = hdr->ptext_adr; old_rhead->ptext_end_adr = hdr->ptext_end_adr; /* checksum should no longer be in play for this old header */ old_rhead->temp_mvals = hdr->temp_mvals; old_rhead->temp_size = hdr->temp_size; /* has_ZBREAK should no longer be in play for this old header */ old_rhead->current_rhead_adr = hdr; /* old_rhead_adr holds the chain of replaced headers we are currently following - see below * trigr_handle should no longer be in play for this old header * checksum_128 should no longer be in play for this old header * active_rhead_adr should characterize this old header * source code should characterize this old header */ ARLINK_ONLY(old_rhead->zhist = hdr->zhist); /* objhash should no longer be in play for this old header * lbltext_ptr should characterize this old header ? * object_len should characterize this old header * routine_source_offset should characterize this old header */ old_rhead->linkage_names = hdr->linkage_names; /* relinkctl_bkptr should characterize this old header */ old_rhead = (rhdtyp *)old_rhead->old_rhead_adr; } /* Add local unresolves to global chain freeing elements that already existed in the global chain */ urx_add(&urx_lcl_anchor); /* Resolve all unresolved entries in the global chain that reference this routine */ urx_resolve(hdr, (lab_tabent *)lbt_bot, (lab_tabent *)lbt_top); if (LINK_PPRIVOBJ == linktyp) cacheflush(hdr->ptext_adr, (hdr->ptext_end_adr - hdr->ptext_adr), BCACHE); DBGARLNK((stderr, "incr_link: (re)link for %.*s complete\n", hdr->routine_name.len, hdr->routine_name.addr)); /* zOS cleanups */ ZOS_FREE_TEXT_SECTION; /* Don't leave global pointers around to active blocks */ hdr = NULL; shdr = NULL; sect_ro_rel = sect_rw_rel = sect_rw_nonrel = NULL; return IL_DONE; } /* Routine to do address relocations/fixups for the M routine being linked as well as resolve those addresses that * can be resolved and create a chain of the external routine/label references that remain unresolved. * * Parameters: * file - file descriptor of open file. * shdr - address of shared header for shared object and shared library linked objects. * linktyp - type of linkage performed (enum linktype). * urx_lcl - address of urx_lcl structure to put our references in temporarily. */ STATICFNDEF boolean_t addr_fix(int file, unsigned char *shdr, linktype linktyp, urx_rtnref *urx_lcl) { res_list *res_root, *new_res, *res_temp, *res_temp1; unsigned char *symbols, *sym_temp, *sym_temp1, *symtop, *res_addr; struct relocation_info rel[RELREAD], *rel_ptr; int numrel, rel_read, string_size, i; unsigned int sym_size; ssize_t status; mident_fixed rtnid, labid; mstr rtn_str; rhdtyp *rtn; lab_tabent *label, *labtop; boolean_t labsym; urx_rtnref *urx_rp; urx_addr *urx_tmpaddr; res_root = NULL; numrel = (int)((hdr->sym_table_off - hdr->rel_table_off) / SIZEOF(struct relocation_info)); if ((numrel * SIZEOF(struct relocation_info)) != (hdr->sym_table_off - hdr->rel_table_off)) return FALSE; /* Size was not even multiple of relocation entries */ while (0 < numrel) { switch(linktyp) { case LINK_SHROBJ: case LINK_SHRLIB: /* All relocation entries already available */ rel_read = numrel; rel_ptr = (struct relocation_info *)((char *)shdr + hdr->rel_table_off); break; case LINK_PPRIVOBJ: /* Buffer the relocation entries */ rel_read = (numrel < RELREAD ? numrel : RELREAD); DOREADRC_OBJFILE(file, &rel[0], rel_read * SIZEOF(struct relocation_info), status); if (0 != status) { res_free(res_root); return FALSE; } rel_ptr = &rel[0]; break; default: assert(FALSE /* Invalid link type */); } numrel -= rel_read; for (; rel_read; --rel_read, ++rel_ptr) { new_res = (res_list *)malloc(SIZEOF(*new_res)); new_res->symnum = rel_ptr->r_symbolnum; new_res->addr = rel_ptr->r_address; new_res->next = new_res->list = NULL; /* Insert the relocation entry in symbol number order on the unresolved chain */ if (!res_root) res_root = new_res; else { res_temp1 = NULL; for (res_temp = res_root; res_temp && res_temp->symnum < new_res->symnum; res_temp = res_temp->next) res_temp1 = res_temp; if (!res_temp) res_temp1->next = new_res; else { /* More than one reference to this symbol. Chain multiple refs in list */ if (res_temp->symnum == new_res->symnum) { new_res->list = res_temp->list; res_temp->list = new_res; } else { if (res_temp1) { new_res->next = res_temp1->next; res_temp1->next = new_res; } else { assert(res_temp == res_root); new_res->next = res_root; res_root = new_res; } } } } } } if (!res_root) return TRUE; /* No unresolved symbols .. we have been successful */ /* Read in the symbol table text area. First word is length of following section */ switch(linktyp) { case LINK_SHROBJ: case LINK_SHRLIB: memcpy(&string_size, shdr + hdr->sym_table_off, SIZEOF(string_size)); symbols = shdr + hdr->sym_table_off + SIZEOF(string_size); string_size -= SIZEOF(string_size); break; case LINK_PPRIVOBJ: DOREADRC_OBJFILE(file, &string_size, SIZEOF(string_size), status); if (0 != status) { res_free(res_root); return FALSE; } string_size -= SIZEOF(string_size); symbols = malloc(string_size); DOREADRC_OBJFILE(file, symbols, string_size, status); if (0 != status) { free(symbols); res_free(res_root); return FALSE; } break; default: assert(FALSE /* Invalid link type */); } /* Match up unresolved entries with the null terminated symbol name entries from the * symbol text pool we just read in. */ sym_temp = sym_temp1 = symbols; symtop = symbols + string_size; for (i = 0; res_root; i++) { for (; i < res_root->symnum; i++) { /* Forward space symbols until our symnum index (i) matches the symbol * we are processing in res_root. */ for (; *sym_temp; sym_temp++) { /* Find end of *this* symbol we are bypassing */ if (sym_temp >= symtop) { if (LINK_PPRIVOBJ == linktyp) free(symbols); res_free(res_root); return FALSE; } } sym_temp++; sym_temp1 = sym_temp; } assert(i == res_root->symnum); /* Find end of routine name that we care about */ for (; *sym_temp1 != '.' && *sym_temp1; sym_temp1++) { if (sym_temp1 >= symtop) { if (LINK_PPRIVOBJ == linktyp) free(symbols); res_free(res_root); return FALSE; } } assert(sym_temp1 >= sym_temp); sym_size = (unsigned int)(sym_temp1 - sym_temp); assert(sym_size <= MAX_MIDENT_LEN); memcpy(&rtnid.c[0], sym_temp, sym_size); rtnid.c[sym_size] = 0; if ('_' == rtnid.c[0]) rtnid.c[0] = '%'; rtn_str.addr = &rtnid.c[0]; rtn_str.len = sym_size; rtn = find_rtn_hdr(&rtn_str); /* Routine already resolved? */ sym_size = 0; labsym = FALSE; /* If symbol is for a label, find the end of the label name */ if ('.' == *sym_temp1) { sym_temp1++; sym_temp = sym_temp1; for (; *sym_temp1; sym_temp1++) { if (sym_temp1 >= symtop) { if (LINK_PPRIVOBJ == linktyp) free(symbols); res_free(res_root); return FALSE; } } assert(sym_temp1 >= sym_temp); sym_size = (unsigned int)(sym_temp1 - sym_temp); assert(sym_size <= MAX_MIDENT_LEN); memcpy(&labid.c[0], sym_temp, sym_size); labid.c[sym_size] = 0; if ('_' == labid.c[0]) labid.c[0] = '%'; labsym = TRUE; } sym_temp1++; sym_temp = sym_temp1; if (rtn) { /* The routine part at least is known */ if (!labsym) res_addr = (unsigned char *)rtn; /* Resolve to routine header */ else { /* Look our target label up in the routines label table */ label = rtn->labtab_adr; labtop = label + rtn->labtab_len; for (; label < labtop && ((sym_size != (unsigned int)label->lab_name.len) || memcmp(&labid.c[0], label->lab_name.addr, sym_size)); label++) ; if (label < labtop) res_addr = (unsigned char *)&label->lnr_adr; /* Resolve to label entry address */ else res_addr = NULL; /* Label not found .. potential future problem. For now * just leave it unresolved */ } if (res_addr) { /* We can fully resolve this symbol now */ res_temp = res_root->next; while(res_root) { /* Resolve all entries for this known symbol */ ((lnk_tabent * )((char *)hdr->linkage_adr + res_root->addr))->ext_ref = (char_ptr_t)res_addr; res_temp1 = res_root->list; free(res_root); res_root = res_temp1; } res_root = res_temp; continue; } } /* This symbol is unknown. Put on the (local) unresolved extern chain -- either for labels or routines */ urx_rp = urx_putrtn(rtn_str.addr, (int)rtn_str.len, urx_lcl); /* Find/create unresolved node for routine */ res_temp = res_root->next; while(res_root) { /* Add unresolved addr entry to existing or new routine and/or label node. */ if (labsym) urx_putlab(&labid.c[0], sym_size, urx_rp, (char *)hdr->linkage_adr + res_root->addr); else { urx_tmpaddr = (urx_addr *)malloc(SIZEOF(urx_addr)); urx_tmpaddr->next = urx_rp->addr; urx_tmpaddr->addr = (INTPTR_T *)((char *)hdr->linkage_adr + res_root->addr); urx_rp->addr = urx_tmpaddr; } res_temp1 = res_root->list; free(res_root); res_root = res_temp1; } res_root = res_temp; } if (LINK_PPRIVOBJ == linktyp) free(symbols); return TRUE; } /* Release the resolution chain .. Called as part of an error since normal processing will * have already released all elements on this chain. * * Parameter: * root - anchor for system resolution chain */ STATICFNDEF void res_free(res_list *root) { res_list *temp; while (root) { while (root->list) { temp = root->list->list; free(root->list); root->list = temp; } temp = root->next; free(root); root = temp; } } /* Routine to perform cleanup and signal errors found in zlinking a mumps object module. * * Parameters: * linktyp - type of linkage performed (enum linktype). * file - pointer to file descriptor of open file. * err - error code of error to raise. * len/addr - length/address of 1st substitution string. * len2/addr2 - length/address of 2nd substitution string. */ STATICFNDEF void zl_error(void *recent_zhist, linktype linktyp, int4 *file, int4 err, int4 len, char *addr, int4 len2, char *addr2) { ZOS_FREE_TEXT_SECTION; zl_error_hskpng(linktyp, file, recent_zhist); /* 5 or 7 arguments */ if (0 == len2) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) err, 2, len, addr); else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) err, 4, len, addr, len2, addr2); } /* Routine to perform basic housekeeping for zl_error() but allowed to be called separately by other errors. * * Parameters: * linktyp - type of linkage performed (enum linktype). * file - pointer to file descriptor of open file. */ STATICFNDEF void zl_error_hskpng(linktype linktyp, int4 *file, void *recent_zhist) { int rc; if ((NULL != file) && (0 < *file)) { /* We have a file descriptor for both process private and shared object type links */ assert ((LINK_PPRIVOBJ == linktyp) || (LINK_SHROBJ == linktyp)); CLOSEFILE_RESET(*file, rc); /* resets "*file" to FD_INVALID, ignore any failure at this point as it is not the * the primary error. */ } if (NULL != hdr) { free(hdr); hdr = NULL; } if (NULL != sect_rw_rel) { free(sect_rw_rel); sect_rw_rel = NULL; } if (NULL != sect_rw_nonrel) { free(sect_rw_nonrel); sect_rw_nonrel = NULL; } if ((LINK_PPRIVOBJ == linktyp) && (NULL != sect_ro_rel)) { /* Only private process links have this area to free */ GTM_TEXT_FREE(sect_ro_rel); sect_ro_rel = NULL; } RELEASE_RECENT_ZHIST; } fis-gtm-V7.0-005/sr_unix/get_src_line.c0000644000032200000250000003600414342376334016651 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include #include "gtm_string.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_stat.h" #include "gtm_stdio.h" #include #include #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" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gv_trigger.h" #include "gtm_trigger.h" #include "cdb_sc.h" #include "tp_frame.h" /* for tp_frame */ #include "t_retry.h" #include "trigger_read_andor_locate.h" #include "gtm_trigger_trc.h" #include "zr_unlink_rtn.h" #include "stack_frame.h" #include "rtn_src_chksum.h" #include "cmd_qlf.h" #include "arlinkdbg.h" #define RT_TBL_SZ 20 STATICFNDCL boolean_t fill_src_tbl(routine_source **src_tbl_result, rhdtyp *rtn_vector); STATICFNDCL boolean_t fill_src_tbl_via_litpool(routine_source **src_tbl_result, rhdtyp *rtn_vector); STATICFNDCL boolean_t fill_src_tbl_via_mfile(routine_source **src_tbl_result, rhdtyp *rtn_vector); GBLREF uint4 dollar_tlevel; GBLREF unsigned int t_tries; GBLREF stack_frame *frame_pointer; GBLREF tp_frame *tp_pointer; LITDEF char litconst_space = ' '; LITREF mval literal_null; error_def(ERR_TXTSRCFMT); error_def(ERR_SYSCALL); int get_src_line(mval *routine, mval *label, int offset, mstr **srcret, rhdtyp **rtn_vec) { int srcrecs, *lt_ptr, size, line_indx, srcfilnamlen; boolean_t found, added, eof_seen, srcstat; mstr src; rhdtyp *rtn_vector; zro_ent *srcdir; mstr *base, *current, *top, tmprtnname; char buff[MAX_SRCLINE], *cptr, *srcfile_name; char srcnamebuf[SIZEOF(mident_fixed) + STR_LIT_LEN(DOTM)]; 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, *eol_srcstart, *prev_srcptr; gtm_rtn_src_chksum_ctx checksum_ctx; boolean_t is_trigger; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; srcstat = 0; *srcret = NULL; rtn_vector = NULL; 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)); DBGIFTRIGR((stderr, "get_src_line: entered $tlevel=%d and $t_tries=%d\n", dollar_tlevel, t_tries)); if (WANT_CURRENT_RTN(routine)) { /* We want $TEXT for the routine currently executing - may be a recursively relinked routine copy */ rtn_vector = CURRENT_RHEAD_ADR(frame_pointer->rvector); DBGARLNK((stderr, "get_src_line: Fetching source from current routine (rtnhdr 0x"lvaddr")\n", rtn_vector)); assert(rtn_vector); GTMTRIG_ONLY(is_trigger=(NULL != rtn_vector->trigr_handle)); DBGIFTRIGR((stderr, "get_src_line: entered $tlevel=%d and $t_tries=%d\n", dollar_tlevel, t_tries)); } if (is_trigger && (NULL == rtn_vector)) { /* Need source on a trigger. Get trigger loaded and its source becomes available since all triggers * are compiled with -EMBED_SOURCE. */ /* Though we should only come in here when the trigger is NOT already loaded, note that * trigger_locate_andor_load() may alter the length part of the mstr to remove the +BREG * region-name specification (the string component is unmodified). Pass in a copy of the mstr struct to * avoid modification to routine->str as it affects the caller which relies on this variable being * untouched. */ tmprtnname = routine->str; DBGTRIGR((stderr, "get_src_line: fetch source for %s\n", tmprtnname.addr)); rc = trigger_locate_andor_load(&tmprtnname, &rtn_vector); if (0 != rc) { if (NULL != rtn_vec) *rtn_vec = NULL; return SRCNOTAVAIL; } if (NULL == rtn_vector) { if (NULL != rtn_vec) *rtn_vec = NULL; return OBJMODMISS; } DBGARLNK((stderr, "get_src_line: Fetch trigger source from rtnhdr 0x"lvaddr"\n", rtn_vector)); } else if (NULL == rtn_vector) { assert(!is_trigger); 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) { if (NULL != rtn_vec) *rtn_vec = NULL; return OBJMODMISS; } } } # ifdef AUTORELINK_SUPPORTED if (!(WANT_CURRENT_RTN(routine)) && !is_trigger) { /* We have the routine now but double check if we need to load a newer one */ DEBUG_ONLY(rtn_vector->rtn_relinked = FALSE); /* meet expectations of assert in checker below */ explicit_relink_check(rtn_vector, TRUE); /* meant for code invokers, not code displayers */ rtn_vector = (TABENT_PROXY).rtnhdr_adr; assert(NULL != rtn_vector); DBGARLNK((stderr, "get_src_line: Fetching routine source for rtnhdr 0x"lvaddr"\n", rtn_vector)); } # endif if (NULL != rtn_vec) *rtn_vec = rtn_vector; if (!rtn_vector->src_full_name.len) { DBGARLNK((stderr, "get_src_line: Source not available\n")); return SRCNOTAVAIL; } src_tbl = rtn_vector->source_code; DBGIFTRIGR((stderr, "get_src_line: routine %lx has source_code 0x%lx (%d)\n", rtn_vector, src_tbl, (src_tbl)? src_tbl->srcrecs : 0)); if (NULL == src_tbl) { /* Load source from where it makes sense - note all triggers are compiled with -EMBED_SOURCE * so we'll always load source from the routine's literal pool. Verify if trigger, not calling * without source being available. */ assert(!is_trigger || (rtn_vector->compiler_qlf & CQ_EMBED_SOURCE)); srcstat = fill_src_tbl(&src_tbl, rtn_vector); src_tbl->srcstat = srcstat; rtn_vector->source_code = src_tbl; } else srcstat |= src_tbl->srcstat; DBGIFTRIGR((stderr, " get_src_line: $tlevel %d\t", dollar_tlevel)); lt_ptr = (int *)find_line_addr(rtn_vector, &label->str, 0, NULL); if (!lt_ptr) { DBGARLNK((stderr, "get_src_line: label not found\n")); srcstat |= LABELNOTFOUND; } else if (!(srcstat & (SRCNOTFND | SRCNOTAVAIL))) { line_indx = (int)(lt_ptr - (int *)LNRTAB_ADR(rtn_vector)); line_indx += offset; DBGIFTRIGR((stderr, "get_src_line: line_indx %d\n", line_indx)); 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)); */ } } # if defined(DEBUG_TRIGR) || defined(DEBUG_ARLINK) DBGFPF((stderr, "get_src_line: exiting with srcstat %d\n", srcstat)); # endif return srcstat; } void free_src_tbl(rhdtyp *rtn_vector) { routine_source *src_tbl; src_tbl = rtn_vector->source_code; if (NULL != src_tbl) { /* Release the source. Entries and source are malloc'd in two blocks on UNIX */ if (NULL != src_tbl->srcbuff) free(src_tbl->srcbuff); free(src_tbl); rtn_vector->source_code = NULL; } } STATICFNDEF boolean_t fill_src_tbl(routine_source **src_tbl_result, rhdtyp *rtn_vector) { if (rtn_vector->compiler_qlf & CQ_EMBED_SOURCE) return fill_src_tbl_via_litpool(src_tbl_result, rtn_vector); else return fill_src_tbl_via_mfile(src_tbl_result, rtn_vector); } STATICFNDEF boolean_t fill_src_tbl_via_litpool(routine_source **src_tbl_result, rhdtyp *rtn_vector) { int srcrecs, size; mstr *current, *top; routine_source *src_tbl; off_t srcsize; unsigned char *srcptr, *srcptr_max, *srcstart, *prev_srcptr; srcrecs = (int)rtn_vector->lnrtab_len; /* Each line of source (srcrecs of them) resides at end of lit pool (srcptr) */ USHBIN_ONLY(srcptr = rtn_vector->literal_text_adr + rtn_vector->routine_source_offset); USHBIN_ONLY(srcsize = rtn_vector->literal_text_len - rtn_vector->routine_source_offset); NON_USHBIN_ONLY(srcptr = (unsigned char *)rtn_vector->routine_source_offset); NON_USHBIN_ONLY(srcsize = (int)rtn_vector->routine_source_length); srcptr_max = srcptr + srcsize; src_tbl = (routine_source *)malloc(SIZEOF(routine_source) + ((srcrecs - 1) * SIZEOF(mstr))); src_tbl->srcbuff = NULL; src_tbl->srcrecs = srcrecs; for (current = src_tbl->srclines + 1, top = src_tbl->srclines + srcrecs ; current < top ; current++) { prev_srcptr = srcptr++; while ((srcptr < srcptr_max) && (*(srcptr - 1) != '\n')) /* find end of current line */ srcptr++; size = (int4)(srcptr - prev_srcptr); if (*(srcptr - 1) == '\n') size--; /* Strip trailing '\n' */ if (size) { current->len = size; current->addr = (char *)prev_srcptr; } else { current->len = 1; current->addr = (char *)&litconst_space; } } /* NOTE: no need to verify source checksum. no chance of mismatch */ *src_tbl_result = src_tbl; return FALSE; } STATICFNDEF boolean_t fill_src_tbl_via_mfile(routine_source **src_tbl_result, rhdtyp *rtn_vector) { boolean_t found, added, eof_seen, srcstat; char buff[MAX_SRCLINE], *cptr, *fgets_rc, *srcfile_name; char srcnamebuf[SIZEOF(mident_fixed) + STR_LIT_LEN(DOTM) + 1]; FILE *fp; gtm_rtn_src_chksum_ctx checksum_ctx; int fclose_res, fdd, fsd, line_indx, *lt_ptr, rc, srcfilnamlen, srcrecs; unsigned int size; mstr *base, *current, src, *top; off_t srcsize; routine_source *src_tbl; struct stat srcfile_stat; unsigned char *srcptr, *srcptr_max, *srcstart, *eol_srcstart, *prev_srcptr; zro_ent *srcdir; srcstat = 0; 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. */ Fopen(fp, srcfile_name, "r"); if (NULL == fp) { found = FALSE; 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)); src.addr[src.len] = 0; zro_search(NULL, NULL, &src, &srcdir, TRUE); if (srcdir) { OPENFILE(srcdir->str.addr, O_RDONLY, fdd); if (-1 == fdd) srcdir = NULL; else { /* having latched on to the directory, openat ensures no jiggering of path between find and open */ OPENAT(fdd, src.addr, O_RDONLY, fsd); if (-1 != fsd) { FDOPEN(fp, fsd, "r"); if (NULL != fp) { found = TRUE; 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; } } CLOSEFILE(fdd, fclose_res); } } } 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_CSA_ABT(NULL, 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 + 1) : NULL; base = src_tbl->srclines; srcptr = src_tbl->srcbuff; 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); if (src_tbl->srcbuff) free(src_tbl->srcbuff); free(src_tbl); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_TXTSRCFMT, 0, errno); assert(FALSE); } else { eof_seen = TRUE; assert(feof(fp)); size = 0; } } else { size = strlen(buff); prev_srcptr = srcptr; srcptr += size; if ((NULL == src_tbl->srcbuff) || (srcptr > srcptr_max)) { /* source file has been concurrently overwritten (and extended or truncated) */ srcstat |= CHECKSUMFAIL; eof_seen = TRUE; size = 0; } else { /* Read size fits in the destination buffer */ memcpy((void *)prev_srcptr, (void *)buff, (size_t)size); /* 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) { assert((prev_srcptr + size) <= srcptr_max); assert((0 <= size) && (MAXPOSINT4 >= size)); current->len = (mstr_len_t)size; current->addr = (char *)prev_srcptr; } else { current->addr = (char *)&litconst_space; current->len = 1; } } if (found) { if (srcrecs > 1) *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 (srcsize && ('\n' != *(src_tbl->srcbuff + srcsize - 1))) *(src_tbl->srcbuff + srcsize++) = '\n'; /* add \n in case last line was unterminated */ rtn_src_chksum_buffer(&checksum_ctx, src_tbl->srcbuff, srcsize); if (!eof_seen || !rtn_src_chksum_match(get_ctx_checksum(&checksum_ctx), get_rtnhdr_checksum(rtn_vector))) srcstat |= CHECKSUMFAIL; FCLOSE(fp, fclose_res); assert(!fclose_res); } *src_tbl_result = src_tbl; return srcstat; } fis-gtm-V7.0-005/sr_unix/obj_file.c0000644000032200000250000003553614342376335015777 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include #include "compiler.h" #include #include "obj_gen.h" #include "cgp.h" #include "mdq.h" #include "hashtab_str.h" #include "objlabel.h" #include "stringpool.h" #include "parse_file.h" #include "gtmio.h" #include "mmemory.h" #include "obj_file.h" #include "mmrhash.h" GBLREF char object_file_name[]; GBLREF short object_name_len; GBLREF int object_file_des; DEBUG_ONLY(GBLDEF int obj_bytes_written;) GBLREF boolean_t run_time; GBLREF int4 lits_text_size, lits_mval_size; GBLREF unsigned char *runtime_base; GBLREF mliteral literal_chain; GBLREF char source_file_name[]; GBLREF unsigned short source_name_len; GBLREF mident routine_name; GBLREF spdesc rts_stringpool, stringpool; GBLREF int4 linkage_size; GBLREF uint4 lnkrel_cnt; /* number of entries in linkage Psect to relocate */ GBLREF int4 sym_table_size; GBLREF hash_table_str *compsyms_hashtab; static char emit_buff[OBJ_EMIT_BUF_SIZE]; /* buffer for emit output */ static int emit_buff_used; /* number of chars in emit_buff */ static int symcnt; static struct rel_table *link_rel, *link_rel_end; /* Linkage relocation entries. */ error_def(ERR_OBJFILERR); error_def(ERR_STRINGOFLOW); STATICFNDCL void emit_link_reference(int4 refoffset, mstr *name); /* Routine to clear/delete the existing object file (used to delete existing temporary object file name * on an error. */ void drop_object_file(void) { int rc; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (0 < object_file_des) { rc = UNLINK(object_file_name); assert(!rc); CLOSE_OBJECT_FILE(object_file_des, rc); /* Resets "object_file_des" to FD_INVALID */ assert(!rc); rc = UNLINK(TADR(tmp_object_file_name)); /* Just in case the temp file was in play */ assert(!rc); } } /* * emit_link_reference * * Description: If not already defined, create relocation entry for * a linkage table entry to be resolved later. */ STATICFNDEF void emit_link_reference(int4 refoffset, mstr *name) { struct sym_table *sym; struct rel_table *newrel; sym = define_symbol(GTM_LINKAGE, name); assert(sym); if ((N_TEXT | N_EXT) != sym->n.n_type) { newrel = (struct rel_table *)mcalloc(sizeof(struct rel_table)); newrel->next = NULL; newrel->resolve = 0; newrel->r.r_address = refoffset; newrel->r.r_symbolnum = 0; if (NULL == link_rel) link_rel = newrel; else link_rel_end->next = newrel; link_rel_end = newrel; if (sym->resolve) newrel->resolve = sym->resolve; sym->resolve = newrel; } } /* * emit_immed * * Args: buffer of executable code, and byte count to be output. */ void emit_immed(char *source, uint4 size) { int4 write; if (run_time) { assert(rts_stringpool.base == stringpool.base); if (!IS_STP_SPACE_AVAILABLE_PRO(size)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_STRINGOFLOW); memcpy(stringpool.free, source, size); stringpool.free += size; } else { DEBUG_ONLY(obj_bytes_written += size); while (0 < size) { write = SIZEOF(emit_buff) - emit_buff_used; write = size < write ? size : write; memcpy(emit_buff + emit_buff_used, source, write); size -= write; source += write; emit_buff_used += write; if (0 != size) buff_emit(); } } } /* * buff_emit * * Output partially or completely filled buffer */ void buff_emit(void) { int stat; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Accumulate object code piece in the object hash with progressive murmurhash call */ gtmmrhash_128_ingest(TADR(objhash_state), emit_buff, emit_buff_used); DOWRITERC(object_file_des, emit_buff, emit_buff_used, stat); if (0 != stat) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_OBJFILERR, 2, object_name_len, object_file_name, stat); emit_buff_used = 0; } /* * buff_flush * * Flush the contents of emit_buff to the file. */ void buff_flush(void) { if (emit_buff_used) buff_emit(); } /* * define_symbol * * Args: psect index, symbol name. * * Description: Buffers a definition of a global symbol with the * given name in the given psect. */ struct sym_table *define_symbol(unsigned char psect, mstr *name) { boolean_t usehtab, added; int4 cmp; struct sym_table *sym, *sym1, *newsym; stringkey symkey; ht_ent_str *syment; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; usehtab = (SYM_HASH_CUTOVER < symcnt); DEBUG_ONLY(syment = NULL); if (!usehtab) { /* "Brute force" version of lookup for now (until SYM_HASH_CUTOVER symbols defined) */ assert(NULL == compsyms_hashtab || NULL == compsyms_hashtab->base); sym = TREF(defined_symbols); sym1 = NULL; while (sym) { /* Consider this a match only if type is N_EXT. If we are inserting an external reference symbol * for the current routine name, we may find a N_TEXT entry. But in this case, we want to add another * (N_EXT) entry so that the symbol table is correctly formed. */ if ((0 >= (cmp = memvcmp(name->addr, (int)name->len, &sym->name[0], sym->name_len - 1))) && (N_EXT == sym->n.n_type)) break; sym1 = sym; sym = sym->next; } if (sym && !cmp) return sym; } else { /* Hashtable lookup */ if (!compsyms_hashtab) { /* Allocate if not allocated yet */ compsyms_hashtab = (hash_table_str *)malloc(sizeof(hash_table_str)); assert(NULL != compsyms_hashtab); compsyms_hashtab->base = NULL; } if (!compsyms_hashtab->base) { /* Need to initialize hash table and load it with the elements so far */ init_hashtab_str(compsyms_hashtab, SYM_HASH_CUTOVER * 2, HASHTAB_NO_COMPACT, HASHTAB_NO_SPARE_TABLE); assert(compsyms_hashtab->base); for (sym = TREF(defined_symbols); sym; sym = sym->next) { if (N_EXT != sym->n.n_type) /* Consider this a match only if type is N_EXT. See comment above */ continue; symkey.str.addr = (char *)&sym->name[0]; symkey.str.len = sym->name_len - 1; COMPUTE_HASH_STR(&symkey); added = add_hashtab_str(compsyms_hashtab, &symkey, sym, &syment); assert(added); assert(syment->value); assert(syment->key.str.addr == (char *)&((struct sym_table *)syment->value)->name[0]); } } symkey.str = *name; /* Copy of the key */ COMPUTE_HASH_STR(&symkey); added = add_hashtab_str(compsyms_hashtab, &symkey, NULL, &syment); if (!added) { /* Hash entry exists for this symbol */ sym = (struct sym_table *)syment->value; assert(sym); assert(0 == memvcmp(name->addr, (int)name->len, &sym->name[0], sym->name_len - 1)); return sym; } } /* Didn't find it in existing symbols; create new symbol. */ newsym = (struct sym_table *)mcalloc(sizeof(struct sym_table) + name->len); newsym->name_len = name->len + 1; memcpy(&newsym->name[0], name->addr, name->len); newsym->name[name->len] = 0; newsym->n.n_type = N_EXT; if (GTM_CODE == psect) newsym->n.n_type |= N_TEXT; /* If symbol is in GTM_CODE, it is defined */ else lnkrel_cnt++; /* Otherwise it's external (only one reference in linkage Psect) */ newsym->resolve = 0; newsym->linkage_offset = -1; /* Don't assign linkage Psect offset unless it's used */ if (!usehtab) { /* Brute force -- add into the queue where the test failed (keeping sorted) */ newsym->next = sym; if (sym1) sym1->next = newsym; else TREF(defined_symbols) = newsym; } else { /* Hashtab usage -- add at beginning .. easiest since sorting doesn't matter for future lookups */ newsym->next = TREF(defined_symbols); TREF(defined_symbols) = newsym; assert(syment); syment->value = newsym; } ++symcnt; sym_table_size += newsym->name_len; return newsym; } /* Routine to resolve all the symbols of a given name to the same symbol number to associate relocation table * entries together properly so all references for a given name associate to the same linkage table entry. */ void resolve_sym (void) { uint4 symnum; struct sym_table *sym; struct rel_table *rel; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; for (sym = TREF(defined_symbols), symnum = 0; sym; sym = sym->next, symnum++) { for (rel = sym->resolve; rel; rel = rel->resolve) rel->r.r_symbolnum = symnum; } } /* Output the relocation table for the linkage section */ void output_relocation (void) { struct rel_table *rel; for (rel = link_rel; NULL != rel; rel = rel->next) emit_immed((char *)&rel->r, sizeof(rel->r)); } #ifdef DEBUG /* Size that output_symbol() below will eventually generate -- used to verify validity of OUTPUT_SYMBOL_SIZE macro */ int output_symbol_size(void) { uint4 string_length; struct sym_table *sym; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; string_length = sizeof(int4); sym = TREF(defined_symbols); while (sym) { string_length += sym->name_len; sym = sym->next; } return string_length; } #endif /* Symbol string table output. Consists of a series of null terminated * strings in symbol number order. */ void output_symbol(void) { uint4 string_length; struct sym_table *sym; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; string_length = sizeof(int4) + sym_table_size; assert(string_length == output_symbol_size()); emit_immed((char *)&string_length, sizeof(string_length)); sym = TREF(defined_symbols); while (sym) { emit_immed((char *)&sym->name[0], sym->name_len); sym = sym->next; } } /* * obj_init * * Description: Initialize symbol list, relocation information, linkage Psect list, linkage_size. */ void obj_init(void) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; lnkrel_cnt = symcnt = 0; link_rel = link_rel_end = NULL; TREF(defined_symbols) = NULL; TREF(linkage_first) = NULL; TREF(linkage_last) = NULL; sym_table_size = 0; linkage_size = MIN_LINK_PSECT_SIZE; /* Minimum size of linkage Psect, assuming no references from generated code */ return; } /* * comp_linkages * * Description: Define the symbols and relocation entries we will need to create * the linkage section when this module is linked in. */ void comp_linkages(void) { mstr name; struct linkage_entry *linkagep; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; for (linkagep = TREF(linkage_first); NULL != linkagep; linkagep = linkagep->next) { name.len = linkagep->symbol->name_len - 1; /* Don't count '\0' terminator */ name.addr = (char *)&linkagep->symbol->name[0]; emit_link_reference(linkagep->symbol->linkage_offset, &name); } return; } void emit_literals(void) { mstr name; uint4 offset, padsize; mliteral *p; struct linkage_entry *linkagep; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* emit the literal text pool which includes the source file path and routine name */ offset = (uint4)(stringpool.free - stringpool.base); emit_immed((char *)stringpool.base, offset); padsize = (uint4)(PADLEN(offset, NATIVE_WSIZE)); /* comp_lits() aligns literal area on NATIVE_WSIZE boundary */ if (padsize) { emit_immed(PADCHARS, padsize); offset += padsize; } emit_immed(source_file_name, source_name_len); offset += source_name_len; padsize = (uint4)(PADLEN(offset, NATIVE_WSIZE)); /* comp_lits() aligns routine_name on NATIVE_WSIZE boundary */ if (padsize) { emit_immed(PADCHARS, padsize); offset += padsize; } emit_immed(routine_name.addr, routine_name.len); offset += routine_name.len; padsize = (uint4)(PADLEN(offset, NATIVE_WSIZE)); /* comp_lits() aligns extern symbols on NATIVE_WSIZE boundary */ if (padsize) { emit_immed(PADCHARS, padsize); offset += padsize; } /* Emit the names associated with the linkage table */ for (linkagep = TREF(linkage_first); NULL != linkagep; linkagep = linkagep->next) { emit_immed((char *)linkagep->symbol->name, linkagep->symbol->name_len - 1); offset += linkagep->symbol->name_len - 1; padsize = (uint4)(PADLEN(offset, NATIVE_WSIZE)); /* comp_lits() aligns extern symbols on NATIVE_WSIZE boundary */ if (padsize) { emit_immed(PADCHARS, padsize); offset += padsize; } } assert(offset == lits_text_size); /* Emit the linkage name mstr table (never relocated - always offset/length pair). Same index as linkage table */ offset = 0; for (linkagep = TREF(linkage_first); (NULL != linkagep) && ((MAXPOSINT4 - (2 * sizeof(mstr))) > offset); linkagep = linkagep->next) { name.char_len = 0; /* No need for this length */ name.len = linkagep->symbol->name_len - 1; /* Don't count '\0' terminator */ name.addr = (char *)linkagep->lit_offset; emit_immed((char *)&name, sizeof(mstr)); offset += sizeof(mstr); } assert((ROUND_UP2((sm_long_t)(offset), NATIVE_WSIZE)) >= (sm_long_t)(offset)); assert(MAXPOSINT4 >= offset); padsize = (uint4)(PADLEN(offset, NATIVE_WSIZE)); if (padsize && (offset < (offset + padsize))) { emit_immed(PADCHARS, padsize); offset += padsize; } assert((MAXPOSINT4 - (3 * sizeof(mstr))) > offset); /* Should never happen */ assert(0 == (uint4)(PADLEN(offset, NATIVE_WSIZE))); /* Emit the literal mval list (also aligned by comp_lits() on NATIVE_WSIZE boundary */ offset = 0; dqloop(&literal_chain, que, p) { assert(p->rt_addr == offset); MV_FORCE_NUMD(&p->v); if (p->v.str.len) p->v.str.addr = (char *)(p->v.str.addr - (char *)stringpool.base); /* Initial offset */ else p->v.str.addr = NULL; p->v.fnpc_indx = (unsigned char)-1; if (!(MV_UTF_LEN & p->v.mvtype)) p->v.str.char_len = 0; assert(MAX_STRLEN >= p->v.str.char_len); emit_immed((char *)&p->v, sizeof(p->v)); offset += sizeof(p->v); } assert (offset == lits_mval_size); } /* * find_linkage * * Argument: the name of a global symbol * * Description: Returns the offset into the linkage Psect of a global symbol. */ int4 find_linkage(mstr* name) { struct linkage_entry *newlnk; struct sym_table *sym; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; sym = define_symbol(GTM_LITERALS, name); if (-1 == sym->linkage_offset) { /* Add new linkage psect entry at end of list. */ sym ->linkage_offset = linkage_size; newlnk = (struct linkage_entry *)mcalloc(sizeof(struct linkage_entry)); newlnk->symbol = sym; newlnk->next = NULL; if (NULL == TREF(linkage_first)) TREF(linkage_first) = newlnk; if (NULL != TREF(linkage_last)) (TREF(linkage_last))->next = newlnk; TREF(linkage_last) = newlnk; linkage_size += SIZEOF(lnk_tabent); } return sym->linkage_offset; } /* * literal_offset * * Argument: Offset of literal in literal Psect. * * Description: Return offset to literal from literal Psect base register. */ int literal_offset(UINTPTR_T offset) { /* If we have no offset assigned yet, assume a really big offset. */ if ((unsigned int)-1 == offset) offset = MAXPOSINT4; return (int)((run_time ? (offset - (UINTPTR_T)runtime_base) : offset)); } fis-gtm-V7.0-005/sr_unix/go_load.c0000644000032200000250000003723214342376334015624 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_string.h" #include "stringpool.h" #include "stp_parms.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "error.h" #include "muextr.h" #include "util.h" #include "mupip_exit.h" #include "mlkdef.h" #include "zshow.h" #include "file_input.h" #include "load.h" #include "mu_gvis.h" #include "mupip_put_gvdata.h" #include "mupip_put_gvn_fragment.h" #include "str2gvkey.h" #include "gtmmsg.h" #include "gtm_utf8.h" #include #include "gv_trigger.h" #include "mu_interactive.h" #include "wbox_test_init.h" #include "op.h" #include "io.h" #include "iormdef.h" #include "iosp.h" #include "gtmio.h" #include "io_params.h" #include "iotimer.h" #include "mupip_load_reg_list.h" GBLREF gd_addr *gd_header; GBLREF bool mupip_error_occurred; GBLREF bool mu_ctrly_occurred; GBLREF bool mu_ctrlc_occurred; GBLREF gv_key *gv_currkey; GBLREF int onerror; GBLREF io_pair io_curr_device; GBLREF sgmnt_addrs *cs_addrs; GBLREF spdesc stringpool; GBLREF gd_region *gv_cur_region; GBLREF gd_region *db_init_region; GBLREF int4 error_condition; LITREF mval literal_notimeout; LITREF mval literal_zero; error_def(ERR_LOADCTRLY); error_def(ERR_LOADEOF); error_def(ERR_LOADFILERR); error_def(ERR_MUNOFINISH); error_def(ERR_PREMATEOF); error_def(ERR_RECLOAD); error_def(ERR_TRIGDATAIGNORE); error_def(ERR_FAILEDRECCOUNT); error_def(ERR_LOADRECCNT); error_def(ERR_DBFILERR); error_def(ERR_STATSDBNOTSUPP); error_def(ERR_JNLFILOPN); error_def(ERR_DBPRIVERR); error_def(ERR_GBLOFLOW); #define GO_PUT_SUB 0 #define GO_PUT_DATA 1 #define GO_SET_EXTRACT 2 STATICFNDEF boolean_t get_mname_from_key(char *ptr, int key_length, char *key, gtm_uint64_t iter, gtm_uint64_t first_failed_rec_count, mname_entry *gvname); #define ISSUE_TRIGDATAIGNORE_IF_NEEDED(KEYLENGTH, PTR, HASHT_GBL, IGNORE) \ /* The ordering of the && below is important as the caller uses HASHT_GBL to be set to TRUE if the global pointed to \ * by PTR is ^#t. \ */ \ if ((HASHT_GBL = IS_GVKEY_HASHT_FULL_GBLNAME(KEYLENGTH, PTR)) && !IGNORE) \ { \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TRIGDATAIGNORE, 2, KEYLENGTH, PTR); \ IGNORE = TRUE; \ } \ #define CHECK_MUPIP_ERROR_OCCURRED(MUPIP_ERROR_OCCURRED, ERROR_CONDITION, REG_LIST, NUM_OF_REG, ITER, \ FAILED_RECORD_COUNT, FIRST_FAILED_REC_COUNT, MU_LOAD_ERROR, MSG_BUFF) \ { \ if (MUPIP_ERROR_OCCURRED) \ { \ if (ERR_JNLFILOPN == ERROR_CONDITION || ERR_DBPRIVERR == ERROR_CONDITION || \ ERR_GBLOFLOW == ERROR_CONDITION) \ { \ insert_reg_to_list(REG_LIST, gv_cur_region, &NUM_OF_REG); \ FIRST_FAILED_REC_COUNT = ITER; \ MU_LOAD_ERROR = TRUE; \ } else \ { \ FIRST_FAILED_REC_COUNT = 0; \ mu_gvis(); \ SNPRINTF(MSG_BUFF, SIZEOF(MSG_BUFF), "%" PRIu64, ITER ); \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_RECLOAD, 2, LEN_AND_STR(MSG_BUFF)); \ } \ FAILED_RECORD_COUNT++; \ ONERROR_PROCESS; \ } \ } \ STATICFNDEF boolean_t get_mname_from_key(char *ptr, int key_length, char *key, gtm_uint64_t iter, gtm_uint64_t first_failed_rec_count, mname_entry *gvname) { int length, key_name_len; char *ptr1, msg_buff[128]; gd_region *reg_ptr = NULL; gtm_uint64_t tmp_rec_count; if ('^' == *ptr) { length = (key_length == 1) ? key_length : key_length - 1; for (key_name_len = 0, ptr1 = ptr + 1; ((key_name_len < length) && ('(' != *ptr1)); key_name_len++, ptr1++) key[key_name_len] = *ptr1; key[key_name_len] = '\0'; } else { tmp_rec_count = iter - 1; if (tmp_rec_count > first_failed_rec_count) SNPRINTF(msg_buff, SIZEOF(msg_buff), "%" PRIu64 " to " "%" PRIu64, first_failed_rec_count, tmp_rec_count ); else SNPRINTF(msg_buff, SIZEOF(msg_buff), "%" PRIu64, tmp_rec_count ); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_RECLOAD, 2, LEN_AND_STR(msg_buff)); return FALSE; } gvname->var_name.addr = key; gvname->var_name.len = MIN(key_name_len , MAX_MIDENT_LEN); COMPUTE_HASH_MNAME(gvname); return TRUE; } void go_load(gtm_uint64_t begin, gtm_uint64_t end, unsigned char *rec_buff, char *line3_ptr, int line3_len, uint4 max_rec_size, int fmt, int utf8_extract, int dos) { boolean_t format_error = FALSE, hasht_ignored = FALSE, hasht_gbl = FALSE; boolean_t is_setextract, mu_load_error = FALSE, switch_db, go_format_val_read; char *add_off, *ptr, *val_off; gtm_uint64_t iter, tmp_rec_count, key_count, first_failed_rec_count, failed_record_count, index; DEBUG_ONLY(gtm_uint64_t saved_begin = 0); int add_len, len, keylength, keystate, val_len, val_len1, val_off1; mstr src, des; uint4 max_data_len, max_subsc_len, num_of_reg; char key[MAX_KEY_SZ], msg_buff[MAX_RECLOAD_ERR_MSG_SIZE]; gd_region **reg_list; mname_entry gvname; gvinit(); reg_list = (gd_region **) malloc(gd_header->n_regions * SIZEOF(gd_region *)); for (index = 0; index < gd_header->n_regions; index++) reg_list[index] = NULL; if ((MU_FMT_GO != fmt) && (MU_FMT_ZWR != fmt)) { assert((MU_FMT_GO == fmt) || (MU_FMT_ZWR == fmt)); mupip_exit(ERR_LOADFILERR); } if (begin < 3) /* WARNING: begin can never be less than 3 */ begin = 3; #ifdef DEBUG if ((WBTEST_ENABLED(WBTEST_FAKE_BIG_KEY_COUNT)) && (3UL < begin)) saved_begin = begin, begin = 3; else if (WBTEST_ENABLED(WBTEST_FAKE_BIG_KEY_COUNT)) saved_begin = FAKE_BIG_KEY_COUNT; #endif ptr = line3_ptr; len = line3_len; failed_record_count = 0; go_format_val_read = FALSE; for (iter = 3; iter < begin; iter++) { len = go_get(&ptr, 0, max_rec_size); if (len < 0) /* The IO device has signalled an end of file */ { ++iter; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_LOADEOF, 1, &begin); mupip_error_occurred = TRUE; util_out_print("Error reading record number: !@UQ\n", TRUE, &iter); return; } } assert(iter == begin); key_count = 0; des.len = max_data_len = max_subsc_len = num_of_reg = 0; des.addr = (char *)rec_buff; GTM_WHITE_BOX_TEST(WBTEST_FAKE_BIG_KEY_COUNT, key_count, saved_begin); GTM_WHITE_BOX_TEST(WBTEST_FAKE_BIG_KEY_COUNT, begin, saved_begin); iter = begin - 1; /* WARNING: iter can never be zero because begin can never be less than 3 */ util_out_print("Beginning LOAD at record number: !@UQ\n", TRUE, &begin); while (TRUE) { if ((++iter > end) || (0 == iter)) break; if (mupip_error_occurred && ONERROR_STOP == onerror) break; if (mu_ctrly_occurred) break; if (mu_ctrlc_occurred) { util_out_print("!AD:!_ Key cnt: !@UQ max subsc len: !UL max data len: !UL", TRUE, LEN_AND_LIT("LOAD TOTAL"), &key_count, max_subsc_len, max_data_len); tmp_rec_count = (iter == begin) ? iter : iter - 1; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) MAKE_MSG_INFO(ERR_LOADRECCNT), 1, &tmp_rec_count); mu_gvis(); util_out_print(0, TRUE); mu_ctrlc_occurred = FALSE; } if ((iter > begin) && (0 > (len = go_get(&ptr, MAX_STRLEN, max_rec_size) - dos))) /* WARNING assignment */ break; if (mupip_error_occurred) { mu_gvis(); break; } if ('\n' == *ptr) { if ('\n' == *(ptr + 1)) break; ptr++; } if (0 == len) continue; if (MU_FMT_GO != fmt) { /* Determine if the ZWR has $extract format */ if ('$' == *ptr) { keylength = zwrkeyvallen(ptr, len, &val_off, &val_len, &val_off1, &val_len1); ptr = ptr + 4; /* Skip first 4 character '$','z','e','(' */ is_setextract = TRUE; } else { /* Determine the ZWR key length. -1 (SIZEOF(=)) is needed since ZWR allows '^x(1,2)='*/ keylength = zwrkeyvallen(ptr, len, &val_off, &val_len, NULL, NULL); is_setextract = FALSE; } if (0 < val_len) { ISSUE_TRIGDATAIGNORE_IF_NEEDED(keylength, ptr, hasht_gbl, hasht_ignored); if (hasht_gbl) continue; } else mupip_error_occurred = TRUE; if (mu_load_error) { if (get_mname_from_key(ptr, keylength, key, iter, first_failed_rec_count, &gvname)) { switch_db = check_db_status_for_global(&gvname, fmt, &failed_record_count, iter, &first_failed_rec_count, reg_list, num_of_reg); if (!switch_db) continue; } } go_call_db(GO_PUT_SUB, ptr, keylength, 0, 0); if (mupip_error_occurred) { if ((ERR_DBFILERR == error_condition) || (ERR_STATSDBNOTSUPP == error_condition)) { insert_reg_to_list(reg_list, db_init_region, &num_of_reg); first_failed_rec_count = iter; mu_load_error = TRUE; }else { first_failed_rec_count = 0; mu_gvis(); SNPRINTF(msg_buff, SIZEOF(msg_buff), "%" PRIu64, iter); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_RECLOAD, 2, LEN_AND_STR(msg_buff)); mu_load_error = FALSE; } failed_record_count++; gv_target = NULL; gv_currkey->base[0] = '\0'; ONERROR_PROCESS; } mu_load_error = FALSE; first_failed_rec_count = 0; src.len = val_len; src.addr = val_off; if (src.len > max_rec_size) { util_out_print("Record too long - record number: !@UQ!/With content:!/!AD", TRUE, &iter, src.len, src.addr); format_error = TRUE; continue; } des.addr = (char *)rec_buff; if (FALSE == zwr2format(&src, &des)) { util_out_print("Format error in record number: !@UQ!/With content:!/!AD", TRUE, &iter, src.len, src.addr); format_error = TRUE; continue; } (is_setextract) ? go_call_db(GO_SET_EXTRACT, des.addr, des.len, val_off1, val_len1) : go_call_db(GO_PUT_DATA, (char *)rec_buff, des.len, 0, 0); CHECK_MUPIP_ERROR_OCCURRED(mupip_error_occurred, error_condition, reg_list, num_of_reg, iter, failed_record_count, first_failed_rec_count, mu_load_error, msg_buff); if (max_subsc_len < (gv_currkey->end + 1)) max_subsc_len = gv_currkey->end + 1; if (max_data_len < des.len) max_data_len = des.len; des.len = 0; } else { ISSUE_TRIGDATAIGNORE_IF_NEEDED(len, ptr, hasht_gbl, hasht_ignored); if (hasht_gbl) { if (0 > (len = go_get(&ptr, 0, max_rec_size) - dos)) /* WARNING assignment */ break; iter++; continue; } if (mu_load_error) { if (get_mname_from_key(ptr, len, key, iter, first_failed_rec_count, &gvname)) { switch_db = check_db_status_for_global(&gvname, fmt, &failed_record_count, iter, &first_failed_rec_count, reg_list, num_of_reg); if (!switch_db) { if ((++iter > end) || (0 == iter)) { iter--; /* Decrement as didn't load key */ break; } if (0 > (len = go_get(&ptr, 0, max_rec_size) - dos)) break; continue; } } } go_call_db(GO_PUT_SUB, ptr, len, 0, 0); if (mupip_error_occurred) { if ((ERR_DBFILERR == error_condition) || (ERR_STATSDBNOTSUPP == error_condition)) { insert_reg_to_list(reg_list, db_init_region, &num_of_reg); first_failed_rec_count = iter; mu_load_error = TRUE; if ((++iter > end) || (0 == iter)) { iter--; /* Decrement as didn't load key */ break; } if (0 > (len = go_get(&ptr, 0, max_rec_size) - dos)) /* WARNING assignment */ break; failed_record_count++; go_format_val_read = TRUE; } else { mu_gvis(); SNPRINTF(msg_buff, SIZEOF(msg_buff), "%" PRIu64, iter); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_RECLOAD, 2, LEN_AND_STR(msg_buff)); mu_load_error = FALSE; } failed_record_count++; gv_target = NULL; gv_currkey->base[0] = '\0'; ONERROR_PROCESS; } mu_load_error = FALSE; first_failed_rec_count = 0; if ((++iter > end) || (0 == iter)) { iter--; /* Decrement as didn't load key */ break; } if (0 > (len = go_get(&ptr, 0, max_rec_size) - dos)) /* WARNING assignment */ break; if (mupip_error_occurred) { mu_gvis(); SNPRINTF(msg_buff, SIZEOF(msg_buff), "%" PRIu64, iter); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_RECLOAD, 2, LEN_AND_STR(msg_buff)); break; } stringpool.free = stringpool.base; go_call_db(GO_PUT_DATA, ptr, len, 0, 0); CHECK_MUPIP_ERROR_OCCURRED(mupip_error_occurred, error_condition, reg_list, num_of_reg, iter, failed_record_count, first_failed_rec_count, mu_load_error, msg_buff); if (max_subsc_len < (gv_currkey->end + 1)) max_subsc_len = gv_currkey->end + 1; if (max_data_len < len) max_data_len = len; } key_count++; } file_input_close(); if (mu_ctrly_occurred) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) ERR_LOADCTRLY); mupip_exit(ERR_MUNOFINISH); } if (mupip_error_occurred && ONERROR_STOP == onerror) { tmp_rec_count = (go_format_val_read) ? iter - 1 : iter; failed_record_count-=(go_format_val_read) ? 1 : 0; } else tmp_rec_count = (iter == begin) ? iter : iter - 1; if (0 != first_failed_rec_count) { if (tmp_rec_count > first_failed_rec_count) SNPRINTF(msg_buff, SIZEOF(msg_buff), "%" PRIu64 " to %" PRIu64, first_failed_rec_count , tmp_rec_count); else SNPRINTF(msg_buff, SIZEOF(msg_buff), "%" PRIu64, tmp_rec_count ); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_RECLOAD, 2, LEN_AND_STR(msg_buff)); } util_out_print("LOAD TOTAL!_!_Key Cnt: !@UQ Max Subsc Len: !UL Max Data Len: !UL", TRUE, &key_count, max_subsc_len, max_data_len); if (failed_record_count) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_FAILEDRECCOUNT, 1, &failed_record_count); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) MAKE_MSG_INFO(ERR_LOADRECCNT), 1, &tmp_rec_count); if (failed_record_count) mupip_exit(error_condition); if (format_error) mupip_exit(ERR_LOADFILERR); } void go_call_db(int routine, char *parm1, int parm2, int val_off1, int val_len1) { /* In order to duplicate the VMS functionality, which is to trap all errors in mupip_load_ch * and continue in go_load after they occur, it is necessary to call these routines from a * subroutine due to the limitations of condition handlers and unwinding on UNIX. */ ESTABLISH(mupip_load_ch); switch(routine) { case GO_PUT_SUB: gv_currkey->end = 0; str2gvkey_gvfunc(parm1, parm2); break; case GO_PUT_DATA: mupip_put_gvdata(parm1, parm2); break; case GO_SET_EXTRACT: mupip_put_gvn_fragment(parm1, parm2, val_off1, val_len1); break; } REVERT; } /* The following is similar to file_get_input in file_input.c but avoids reallocation because the regex memory management issue */ int go_get(char **in_ptr, int max_len, uint4 max_rec_size) { int rd_len, ret_len; mval val; ESTABLISH_RET(mupip_load_ch, 0); /* one-time only reads if in TP to avoid TPNOTACID, otherwise use untimed reads */ for (ret_len = 0; ; ) { op_read(&val, (mval *)(dollar_tlevel ? &literal_zero : &literal_notimeout)); rd_len = val.str.len; if ((0 == rd_len) && io_curr_device.in->dollar.zeof) { REVERT; if (io_curr_device.in->dollar.x) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_PREMATEOF); return FILE_INPUT_GET_ERROR; } if ((max_len && (rd_len > max_len)) || ((ret_len + rd_len) > max_rec_size)) { REVERT; return FILE_INPUT_GET_LINE2LONG; } memcpy((unsigned char *)(*in_ptr + ret_len), val.str.addr, rd_len); ret_len += rd_len; if (!io_curr_device.in->dollar.x) break; } REVERT; return ret_len; } fis-gtm-V7.0-005/sr_unix/obj_fileu.c0000644000032200000250000001517314342376335016157 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2021 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 #include "gtm_stat.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_limits.h" #include "cmd_qlf.h" #include "gtmio.h" #include "parse_file.h" #include #include "obj_file.h" GBLREF command_qualifier cmd_qlf; GBLREF char object_file_name[]; GBLREF short object_name_len; GBLREF mident module_name; #define MKSTEMP_MASK "XXXXXX" #define MAX_MKSTEMP_RETRIES 100 error_def(ERR_FILEPARSE); error_def(ERR_OBJFILERR); error_def(ERR_SYSCALL); error_def(ERR_TEXT); /******************************************************************************************************************************** * * While these routines might rightly belong in obj_file.c, since these routines are needed both in sr_unix and in sr_unix_nsb, * it was decided to create this module to share routines that are the same across both unix and linux-i386 so the routines only * need to exist in one place. * ********************************************************************************************************************************/ /** * @brief Try to memcpy to tmp_object_file_name in a way which SCA will be happy with * * @param src Source buffer * @param cpy_len Number of bytes to copy * * @return None */ static inline void mcpy_tmp_object_file_name(const char *src, size_t cpy_len) { typedef char dst_buf[TLEN(tmp_object_file_name)]; typedef dst_buf *dst_buf_p; dst_buf_p tmp_object_file_name; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; tmp_object_file_name = (dst_buf_p) TADR(tmp_object_file_name); assert(NULL != src); assert(tmp_object_file_name != NULL); assert(sizeof(*tmp_object_file_name) >= cpy_len); memcpy((char *)tmp_object_file_name, src, cpy_len); assert(NULL != tmp_object_file_name); assert((sizeof(*tmp_object_file_name) - cpy_len) >= SIZEOF(MKSTEMP_MASK)); memcpy(((char *)tmp_object_file_name) + cpy_len, MKSTEMP_MASK, SIZEOF(MKSTEMP_MASK)); } /* Routine to create a temporary object file. This file is created in the directory it is supposed to reside in but is created * with a temporary name. When complete, it is renamed to what it was meant to be replacing the previous version. * * Parameters: * * object_fname - Character array of the object path/name. * object_fname_len - Length of that array in bytes. * * Return value: * File descriptor for the open object file. */ int mk_tmp_object_file(const char *object_fname, int object_fname_len) { int fdesc, status, umask_creat, umask_orig, retry; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Make sure room in buffer for addition of unique-ifying MKSTEMP_MASK on end of file name */ if ((object_fname_len + SIZEOF(MKSTEMP_MASK) - 1) > TLEN(tmp_object_file_name)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_OBJFILERR, 2, object_fname_len, object_fname, ERR_TEXT, 2, RTS_ERROR_TEXT("Object file name exceeds buffer size")); /* 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 { /*memcpy(TADR(tmp_object_file_name), object_fname, object_fname_len);*/ mcpy_tmp_object_file_name(object_fname, object_fname_len); /* Note memcpy() below purposely includes null terminator */ /*memcpy(TADR(tmp_object_file_name) + object_fname_len, MKSTEMP_MASK, SIZEOF(MKSTEMP_MASK));*/ MKSTEMP(TADR(tmp_object_file_name), fdesc); } while ((FD_INVALID == fdesc) && (EEXIST == errno) && (0 < --retry)); if (FD_INVALID == fdesc) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_OBJFILERR, 2, object_fname_len, object_fname, errno); umask_orig = umask(000); /* Determine umask (destructive) */ (void)umask(umask_orig); /* Reset umask */ umask_creat = 0666 & ~umask_orig; /* Change protections to be those generated by previous versions. In some future version, the permissions may * become tied to the permissions of the source but this works for now. */ status = FCHMOD(fdesc, umask_creat); if (-1 == status) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fchmod()"), CALLFROM, errno); return fdesc; } /* Routine to rename the most recent temporary object file (name stored in threadgbl tmp_object_file_name) to the name * passed in via parameter. * * Parameters: * object_fname - non-temporary name of object file * * Global input: * tmp_object_file_name - private/unique file created by mkstemp() in routine above. */ void rename_tmp_object_file(const char *object_fname) { int status; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; status = rename(TADR(tmp_object_file_name), object_fname); if (-1 == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("rename()"), CALLFROM, errno); memset(TADR(tmp_object_file_name), 0, GTM_PATH_MAX); } /* Routine to determine and initialize the fully qualified object name and path. * * Parameters: * - none * * Global inputs: * cmd_qlf.object_file - value from -object= option * module_name - routine name * Global outputs: * object_file_name - full path of object file * object_name_len - length of full path (not including null terminator) */ void init_object_file_name(void) { int status, rout_len; char obj_name[SIZEOF(mident_fixed) + 5]; mstr fstr; parse_blk pblk; fstr.len = (MV_DEFINED(&cmd_qlf.object_file) && (MAX_FN_LEN > cmd_qlf.object_file.str.len) ? cmd_qlf.object_file.str.len : 0); fstr.addr = cmd_qlf.object_file.str.addr; assert(!fstr.len || strlen(fstr.addr) == fstr.len); memset(&pblk, 0, SIZEOF(pblk)); pblk.buffer = object_file_name; pblk.buff_size = MAX_FN_LEN; rout_len = (int)module_name.len; memcpy(&obj_name[0], module_name.addr, rout_len); memcpy(&obj_name[rout_len], DOTOBJ, SIZEOF(DOTOBJ)); /* Includes null terminator */ pblk.def1_size = rout_len + SIZEOF(DOTOBJ) - 1; /* Length does not include null terminator */ pblk.def1_buf = obj_name; status = parse_file(&fstr, &pblk); if (0 == (status & 1)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_FILEPARSE, 2, fstr.len, fstr.addr, status); object_name_len = pblk.b_esl; object_file_name[object_name_len] = '\0'; } fis-gtm-V7.0-005/sr_unix/golevel.c0000644000032200000250000000516614342376334015656 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2021 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_stdio.h" #include #include "stack_frame.h" #include "tp_frame.h" #include "golevel.h" #include "dollar_zlevel.h" #include "error.h" GBLREF stack_frame *frame_pointer; error_def(ERR_ZGOTOTOOBIG); error_def(ERR_ZGOTOLTZERO); #ifdef GTM_TRIGGER void golevel(int4 level, boolean_t unwtrigrframe) #else void golevel(int4 level) #endif { stack_frame *fp, *fpprev; int4 unwframes, unwlevels, prevlvl; if (0 > level) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZGOTOLTZERO); unwlevels = dollar_zlevel() - level; if (0 > unwlevels) /* Couldn't get to the level we were trying to unwind to */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZGOTOTOOBIG); unwframes = 0; for (fp = frame_pointer; NULL != fp; fp = fpprev) { assert(0 <= unwlevels); fpprev = fp->old_frame_pointer; if (NULL == fpprev GTMTRIG_ONLY( && !(SFT_TRIGR & fp->type))) break; /* break on base frame not trigger related */ # ifdef GTM_TRIGGER /* Break if level reached -- note trigger base frame is type=counted but is not counted as * part of level our count is not decremented on a trigger base frame. */ if (SFT_COUNT & fp->type) { if (0 == unwlevels) break; /* break on reaching target level with a counted frame */ if (!(SFT_TRIGR & fp->type)) unwlevels--; } unwframes++; if (SFT_TRIGR & fp->type) { /* Unwinding a trigger base frame leaves a null frame_pointer so allow us to jump over the * base frame to the rich stack beneath.. */ assert(NULL == fpprev); fpprev = *(stack_frame **)(fp + 1); assert(NULL != fpprev); } # else if ((SFT_COUNT & fp->type) && (0 == unwlevels--)) break; /* break on reaching target level with a counted frame */ unwframes++; # endif } DBGEHND_ONLY(prevlvl = dollar_zlevel()); GOFRAMES(unwframes, unwtrigrframe, FALSE); DBGEHND((stderr, "golevel: Unwound from level %d to level %d which is %d frames ending in stackframe 0x"lvaddr" with" " type 0x%04lx\n", prevlvl, level, unwframes, frame_pointer, (frame_pointer ? frame_pointer->type : 0xffff))); return; } fis-gtm-V7.0-005/sr_unix/incr_link.h0000644000032200000250000000362314342376334016172 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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. * * * ****************************************************************/ #ifndef INCR_LINK_INCLUDED #define INCR_LINK_INCLUDED #define IL_DONE 1 /* calling convention descended from VMS */ #define IL_RECOMPILE 0 #ifdef USHBIN_SUPPORTED #include #endif #include "zroutinessp.h" /* need zro_ent typedef */ /* If autorelink is supported, the additional recent_zhist parameter (if non-NULL) points to a malloc'd search history buffer * which should be* released for any error. This macro provides the release capability. */ #ifdef AUTORELINK_SUPPORTED # define RECENT_ZHIST recent_zhist # define RELEASE_RECENT_ZHIST \ { \ if (NULL != recent_zhist) \ { \ free(recent_zhist); \ recent_zhist = NULL; \ } \ } #else # define RECENT_ZHIST NULL # define RELEASE_RECENT_ZHIST #endif #ifdef AUTORELINK_SUPPORTED # define INCR_LINK(FD, ZRO_ENT, ZRO_HIST, FNLEN, FNAME) incr_link(FD, ZRO_ENT, ZRO_HIST, FNLEN, FNAME) boolean_t incr_link(int *file_desc, zro_ent *zro_entry, zro_hist *recent_zhist_ptr, uint4 fname_len, char *fname); #else # define INCR_LINK(FD, ZRO_ENT, ZRO_HIST, FNLEN, FNAME) incr_link(FD, ZRO_ENT, FNLEN, FNAME) boolean_t incr_link(int *file_desc, zro_ent *zro_entry, uint4 fname_len, char *fname); #endif #ifdef __MVS__ #define ZOS_FREE_TEXT_SECTION \ if (NULL != text_section) \ { \ free(text_section); \ text_section = NULL; \ } #else #define ZOS_FREE_TEXT_SECTION #endif #endif /* INCR_LINK_INCLUDED */ fis-gtm-V7.0-005/sr_unix/gtm_compile.c0000644000032200000250000001062014342376334016507 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "stp_parms.h" #include "stringpool.h" #include "cmd_qlf.h" #include "iosp.h" #include "cli.h" #include #include "stack_frame.h" #include "mv_stent.h" #include "lv_val.h" #include "parse_file.h" #include "source_file.h" #include "gt_timer.h" #include "io.h" #include "getjobnum.h" #include "comp_esc.h" #include "get_page_size.h" #include "getzdir.h" #include "gtm_compile.h" #include "patcode.h" #include "print_exit_stats.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "alias.h" #include "gt_timers_add_safe_hndlrs.h" #include "zco_init.h" #include "min_max.h" GBLREF boolean_t run_time; GBLREF command_qualifier cmd_qlf; GBLREF IN_PARMS *cli_lex_in_ptr; GBLREF mident module_name; GBLREF mv_stent *mv_chain; GBLREF spdesc rts_stringpool, stringpool; GBLREF stack_frame *frame_pointer; GBLREF symval *curr_symval; GBLREF unsigned char *msp, *stackbase, *stacktop, *stackwarn; int gtm_compile(void) { boolean_t more; char ceprep_file[MAX_FN_LEN + 1], list_file[MAX_FN_LEN + 1], obj_file[MAX_FN_LEN + 1]; char source_file_string[MAX_FN_LEN + 1]; int cum_status, status; mstr orig_cmdstr; unsigned char *mstack_ptr; unsigned short len; size_t origlen; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; get_page_size(); stp_init(STP_INITSIZE); rts_stringpool = stringpool; io_init(TRUE); getjobnum(); getzdir(); run_time = TRUE; /* This and the next seem odd, but called routines expect it */ TREF(compile_time) = FALSE; mstack_ptr = (unsigned char *)malloc(USER_STACK_SIZE); msp = stackbase = mstack_ptr + (USER_STACK_SIZE - sizeof(char *)); mv_chain = (mv_stent *)msp; stackwarn = stacktop + (USER_STACK_SIZE / 4); msp -= sizeof(stack_frame); frame_pointer = (stack_frame *)msp; memset(frame_pointer, 0, sizeof(stack_frame)); frame_pointer->temps_ptr = (unsigned char *)frame_pointer; frame_pointer->mpc = CODE_ADDRESS(gtm_ret_code); frame_pointer->ctxt = GTM_CONTEXT(gtm_ret_code); frame_pointer->type = SFT_COUNT; frame_pointer->rvector = (rhdtyp *)malloc(sizeof(rhdtyp)); memset(frame_pointer->rvector, 0, sizeof(rhdtyp)); symbinit(); /* Variables for supporting $ZSEARCH sorting and wildcard expansion */ TREF(zsearch_var) = lv_getslot(curr_symval); LVVAL_INIT((TREF(zsearch_var)), curr_symval); /* command qualifier processing stuff */ zco_init(); assert(cli_lex_in_ptr); /* Save the original MUMPS command line qualifers, which starts with "MUMPS ". These are processed after, * and take precedence over, $ZCOMPILE. */ origlen = strlen(cli_lex_in_ptr->in_str); assert(STR_LIT_LEN("MUMPS ") < origlen); origlen -= STR_LIT_LEN("MUMPS "); assert(MAXPOSINT4 >= origlen); orig_cmdstr.addr = (char *)malloc(origlen); memcpy(orig_cmdstr.addr, (char *)(cli_lex_in_ptr->in_str + (STR_LIT_LEN("MUMPS "))), origlen); orig_cmdstr.len = (int)origlen; INIT_CMD_QLF_STRINGS(cmd_qlf, obj_file, list_file, ceprep_file, MAX_FN_LEN); len = module_name.len = 0; zl_cmd_qlf(&(TREF(dollar_zcompile)), &cmd_qlf, source_file_string, &len, FALSE); /* Init with default quals */ zl_cmd_qlf(&orig_cmdstr, &cmd_qlf, source_file_string, &len, TRUE); /* Override with the actual qualifers */ free(orig_cmdstr.addr); /* end command qualifier processing stuff */ ce_init(); /* initialize compiler escape processing */ prealloc_gt_timers(); gt_timers_add_safe_hndlrs(); /* Not sure why compiler needs timers but .. */ cum_status = SS_NORMAL; do { compile_source_file(len, source_file_string, TRUE); cum_status |= TREF(dollar_zcstatus); cmd_qlf.object_file.str.len = module_name.len = 0; len = MAX_FN_LEN; status = cli_get_str("INFILE", source_file_string, &len); } while (status); print_exit_stats(); SET_PROCESS_EXITING_TRUE; /* needed by remove_rms($principal) to avoid closing that */ io_rundown(NORMAL_RUNDOWN); return (SS_NORMAL == cum_status) ? SS_NORMAL : 1; } fis-gtm-V7.0-005/sr_unix/trigger_fill_xecute_buffer.c0000644000032200000250000002240514342376335021574 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2020 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" #ifdef GTM_TRIGGER #include "gdsroot.h" /* for gdsfhead.h */ #include "gdsbt.h" /* for gdsfhead.h */ #include "gdsfhead.h" #include "gvcst_protos.h" #include #include "gv_trigger.h" #include "trigger.h" #include "mv_stent.h" /* for COPY_SUBS_TO_GVCURRKEY macro */ #include "gvsub2str.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include "format_targ_key.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include "targ_alloc.h" /* for SET_GVTARGET_TO_HASHT_GBL */ #include "filestruct.h" /* for INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED (FILE_INFO) */ #include "mvalconv.h" #include "gdscc.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "tp.h" /* for sgm_info */ #include "tp_frame.h" #include "tp_restart.h" #include "tp_set_sgm.h" #include "t_retry.h" #include "op.h" #include "op_tcommit.h" #include "memcoherency.h" #include "gtmimagename.h" #include "trigger_fill_xecute_buffer.h" #include "trigger_gbl_fill_xecute_buffer.h" #include "gtm_trigger_trc.h" #include "repl_msg.h" #include "gtmsource.h" /* for jnlpool_addrs_ptr_t */ GBLREF sgmnt_data_ptr_t cs_data; GBLREF sgmnt_addrs *cs_addrs; GBLREF int4 gtm_trigger_depth; GBLREF gv_key *gv_currkey; GBLREF gd_region *gv_cur_region; GBLREF gv_namehead *gv_target; #ifdef DEBUG GBLREF boolean_t is_updproc; #endif GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF sgm_info *sgm_info_ptr; GBLREF boolean_t skip_INVOKE_RESTART; GBLREF int tprestart_state; GBLREF tp_frame *tp_pointer; GBLREF int4 tstart_trigger_depth; error_def(ERR_TPRETRY); STATICFNDCL CONDITION_HANDLER(trigger_fill_xecute_buffer_ch); STATICFNDCL void trigger_fill_xecute_buffer_read_trigger_source(gv_trigger_t *trigdsc); /* Similar condition handler to above without the tp-restart - just unwind and let caller do the restart */ CONDITION_HANDLER(trigger_fill_xecute_buffer_ch) { START_CH(TRUE); if ((int)ERR_TPRETRY == SIGNAL) { UNWIND(NULL, NULL); } NEXTCH; } int trigger_fill_xecute_buffer(gv_trigger_t *trigdsc) { int src_fetch_status; assert(!dollar_tlevel || (tstart_trigger_depth <= gtm_trigger_depth)); /* We have 3 cases to consider - all of which REQUIRE a TP fence to already be in effect. The reason for this is, if we * detect a restartable condition, we are going to cause this region's triggers to be unloaded which destroys the block * our parameter is pointing to so the restart logic MUST take place outside of this routine. * * 1. We have an active transaction due to an IMPLICIT TSTART done by trigger handling but the trigger level has not yet * been created. We don't need another TP wrapper in this case but we do need a condition handler to trap the thrown * retry to again prevent C stack unwind and return to the caller in the same shape that gtm_trigger would return. * 2. We have an active transaction due to an EXPLICIT M-code TSTART command. For this case, the trigger loads proceed * as normal with restarts handled in the regular automatic fashion. Note this case also covers the tp restarts done * by both the update process and mupip recover forward since those functions have their own way of intercepting and * dealing with restarts. To cover those cases, tp->implicit_tstart can be TRUE but tp_implicit_trigger MUST be * FALSE. * 3. We have an active transaction due to an IMPLICIT TSTART done by trigger handling and one or more triggers are * running. This becomes like case 2 since the restart will be handled by gtm_trigger and the proper thing will * be done. * * An extra note about case 3. Case 3 can be the identified case if in a nested trigger we are in trigger-no-mans-land * with a base frame for the nested trigger (having driven one of a set of parallel nested triggers) but no actual trigger * execution frame yet exists. This is really a case 1 situation with a nested trigger but it turns out that dealing with * like case 3 does the right thing because if/when mdb_condition_handler catches a thrown TPRETRY error, mdb_condition * handler will peal the nested trigger frame off before doing the restart which works for us and avoids issues of * multi-level implicit restarts we would otherwise have to handle. * * Note, this routine is for loading trigger source when the trigger is to be driven. The trigger_source_read_andor_verify() * routine should be used when fetching trigger source for reasons other than driving the triggers. This routine is lighter * weight but has a dependence on the restartability of the trigger-drive logic for getting the triggers reloaded as * necessary. */ assertpro(0 < dollar_tlevel); if (!tp_pointer->implicit_trigger /* Case 2 */ || (tp_pointer->implicit_tstart && tp_pointer->implicit_trigger && (tstart_trigger_depth != gtm_trigger_depth))) /* Case 3 */ { /* Test for Case 3/4 where we get to do very little: */ DBGTRIGR((stderr, "trigger_fill_xecute_buffer: Case 2/3\n")); assert((!tp_pointer->implicit_trigger) || (0 < gtm_trigger_depth)); trigger_fill_xecute_buffer_read_trigger_source(trigdsc); } else { /* Test for Case 1 where we only need a condition handler */ DBGTRIGR((stderr, "trigger_fill_xecute_buffer: Case 1\n")); assert(tp_pointer->implicit_tstart && tp_pointer->implicit_trigger); assert(tstart_trigger_depth == gtm_trigger_depth); ESTABLISH_RET(trigger_fill_xecute_buffer_ch, SIGNAL); trigger_fill_xecute_buffer_read_trigger_source(trigdsc); REVERT; } /* return our bounty to caller */ trigdsc->xecute_str.mvtype = MV_STR; return 0; /* Could return ERR_TPRETRY if return is via our condition handler */ } /* Workhorse of fetching source for given trigger. */ STATICFNDEF void trigger_fill_xecute_buffer_read_trigger_source(gv_trigger_t *trigdsc) { enum cdb_sc cdb_status; int4 index; mstr gbl, xecute_buff; mval trig_index; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; gvt_trigger_t *gvt_trigger; gv_namehead *gvt; gv_namehead *save_gv_target; gd_region *save_gv_cur_region; sgm_info *save_sgm_info_ptr; jnlpool_addrs_ptr_t save_jnlpool; gv_key_buf save_currkey; assert(0 < dollar_tlevel); assert(NULL != trigdsc); SAVE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); gvt_trigger = trigdsc->gvt_trigger; /* We now know our base block now */ index = trigdsc - gvt_trigger->gv_trig_array + 1; /* We now know our trigger index value */ i2mval(&trig_index, index); DBGTRIGR((stderr, "trigger_fill_xecute_buffer_read_trigger_source: entry $tlevel:%d\tindex:%d of %d\n", dollar_tlevel, index, gvt_trigger->num_gv_triggers)); gvt = gv_target = gvt_trigger->gv_target; /* gv_target contains global name */ gbl.addr = gvt->gvname.var_name.addr; gbl.len = gvt->gvname.var_name.len; /* Our situation is that while our desired gv_target has csa information, we don't know specifically * which global directory was in use so we can't run gv_bind_name() lest we find the given global * name in the wrong global directory thus running the wrong triggers. But we know this target is * properly formed since it had to be when it was recorded when the triggers were loaded. Because of * that, we can get the correct csa and gv_target and csa-region will point us to a region that will * work even if it isn't exactly the one we used to get to this trigger. */ TP_CHANGE_REG_IF_NEEDED(gvt->gd_csa->region); csa = cs_addrs; csd = csa->hdr; assert(csd == cs_data); tp_set_sgm(); /* See if we need to reload our triggers */ if ((csa->db_trigger_cycle != gvt->db_trigger_cycle) || (csa->db_dztrigger_cycle && (gvt->db_dztrigger_cycle != csa->db_dztrigger_cycle))) { /* The process' view of the triggers is likely stale. Restart to be safe. * Triggers can be invoked only by GT.M and Update process. We expect to see GT.M processes to * restart due to concurrent trigger changes. The Update Process should only restart if it is a * supplementary instance. Assert accordingly. Note similar asserts occur in t_end.c and * tp_tend.c. */ DBGTRIGR((stderr, "trigger_fill_xecute_buffer_read_trigger_source: stale trigger view\n")); assert(CDB_STAGNATE > t_tries); assert(!is_updproc || (jnlpool && jnlpool->repl_inst_filehdr->is_supplementary && !jnlpool->jnlpool_ctl->upd_disabled)); t_retry(cdb_sc_triggermod); } SET_GVTARGET_TO_HASHT_GBL(csa); INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED; assert(0 == trigdsc->xecute_str.str.len); /* Make sure not replacing/losing a buffer */ xecute_buff.addr = trigger_gbl_fill_xecute_buffer(gbl.addr, gbl.len, &trig_index, NULL, (int4 *)&xecute_buff.len); trigdsc->xecute_str.str = xecute_buff; /* Restore gv_target/gv_currkey which need to be kept in sync */ RESTORE_REGION_INFO(save_currkey, save_gv_target, save_gv_cur_region, save_sgm_info_ptr, save_jnlpool); return; } #endif /* GTM_TRIGGER */ fis-gtm-V7.0-005/sr_unix/gtm_filter_command.c0000644000032200000250000000603614342376334020050 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018-2021 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 "gtm_filter_command.h" #include "gtm_string.h" #include "mdef.h" #include "gtmmsg.h" #include "send_msg.h" #include "gtm_threadgbl.h" #include "gtm_threadgbl_init.h" #include #include "gtmci.h" #include "stack_frame.h" #include "mprof.h" #include "util.h" error_def(ERR_COMMFILTERERR); error_def(ERR_RESTRICTEDOP); error_def(ERR_NOFILTERNEST); GBLREF stack_frame *frame_pointer; GBLREF boolean_t is_tracing_on; #define CALL_ROUTINE_N_CHECK_ERR(ROUTINE, COMMAND, RET_COMM) \ { \ gtm_status_t stat; \ gtm_long_t quit_return; \ gtm_char_t err_str[2 * OUT_BUFF_SIZE]; /* Same as Max buffer for zstatus */ \ stat = gtm_ci_filter(ROUTINE, &quit_return, COMMAND, &RET_COMM); \ if (quit_return == -1) \ { \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_COMMFILTERERR, 4, LEN_AND_LIT(ROUTINE), \ LEN_AND_LIT("RESTRICTEDOP")); \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_RESTRICTEDOP, 1, COMMAND); \ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_COMMFILTERERR, 4, LEN_AND_LIT(ROUTINE), \ LEN_AND_LIT("RESTRICTEDOP")); \ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_RESTRICTEDOP, 1, COMMAND); \ returned_command.length = 0; \ } \ if (stat) \ { \ gtm_zstatus(&err_str[0], 2 * OUT_BUFF_SIZE); \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_COMMFILTERERR, 4, \ LEN_AND_LIT(ROUTINE), LEN_AND_STR(err_str)); \ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_COMMFILTERERR, 4, LEN_AND_LIT(ROUTINE), \ LEN_AND_STR(err_str)); \ returned_command.length = 0; \ if (frame_pointer->flags & SFF_CI) \ ci_ret_code_quit(); \ } \ } gtm_string_t gtm_filter_command(char * command, char * caller_name) { gtm_char_t com_str[MAX_STRLEN]; gtm_string_t returned_command; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; returned_command.address = (xc_char_t *)&com_str; returned_command.length = MAX_STRLEN; if(TREF(comm_filter_init)) /*Already in a filter, no nesting allowed*/ { TREF(comm_filter_init) = FALSE; send_msg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOFILTERNEST); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_NOFILTERNEST); } if (!STRCMP(caller_name, "PIPE")) CALL_ROUTINE_N_CHECK_ERR("gtmpipeopen", command, returned_command); if (!STRCMP(caller_name, "ZSYSTEM")) CALL_ROUTINE_N_CHECK_ERR("gtmzsystem", command, returned_command); if (is_tracing_on) TREF(prof_fp) = TREF(mprof_stack_curr_frame); return returned_command; } fis-gtm-V7.0-005/sr_unix/init_gtm.c0000644000032200000250000001630714342376334016032 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include "gtm_multi_thread.h" #include "startup.h" #include #include "stack_frame.h" #include "error.h" #include "cli.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "op.h" #include "tp_timeout.h" #include "ctrlc_handler.h" #include "mprof.h" #include "gtm_startup_chk.h" #include "gtm_compile.h" #include "gtm_startup.h" #include "jobchild_init.h" #include "cli_parse.h" #include "invocation_mode.h" #include "fnpc.h" #include "gtm_malloc.h" #include "stp_parms.h" #include "create_fatal_error_zshow_dmp.h" #include "mtables.h" #include "show_source_line.h" #include "patcode.h" #include "collseq.h" GBLREF boolean_t utf8_patnumeric; GBLREF int4 exi_condition; GBLREF mstr dollar_zchset; GBLREF int (*op_open_ptr)(mval *v, mval *p, mval *t, mval *mspace); GBLREF mstr dollar_zpatnumeric; GBLREF mstr default_sysid; GBLREF pattern *pattern_list; GBLREF pattern *curr_pattern; GBLREF pattern mumps_pattern; GBLREF uint4 *pattern_typemask; GBLREF void (*ctrlc_handler_ptr)(); GBLREF void (*mupip_exit_fp)(int4 errnum); /* Func ptr for mupip_exit() but in GTM, points to assert rtn */ GBLREF void (*show_source_line_fptr)(boolean_t warn); /* Func ptr for show_source_line() for same reason as below */ GBLREF void (*stx_error_fptr)(int in_error, ...); /* Function pointer for stx_error() so gtm_utf8.c can avoid pulling * stx_error() into gtmsecshr, and thus just about everything else * as well. */ GBLREF void (*stx_error_va_fptr)(int in_error, va_list args); /* Function pointer for stx_error() so rts_error.c * can avoid pulling stx_error() into gtmsecshr, * and thus just about everything else as well. */ GBLREF void (*tp_timeout_action_ptr)(void); GBLREF void (*tp_timeout_clear_ptr)(void); GBLREF void (*tp_timeout_start_timer_ptr)(int4 tmout_sec); GBLREF void (*unw_prof_frame_ptr)(void); #ifdef GTM_PTHREAD GBLREF pthread_t gtm_main_thread_id; GBLREF boolean_t gtm_main_thread_id_set; GBLREF boolean_t gtm_jvm_process; #endif GBLDEF boolean_t gtm_startup_active = FALSE; STATICFNDCL void assert_on_entry(int4 arg); error_def(ERR_COLLATIONUNDEF); void init_gtm(void) { /* initialize process characteristics and states, but beware as initialization occurs in other places as well * the function pointer initializations below happen in both the GT.M runtime and in mupip */ struct startup_vector svec; int i; int4 lct; DEBUG_ONLY(mval chkmval;) DEBUG_ONLY(mval chkmval_b;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* We believe much of our code depends on these relationships. */ assert(SIZEOF(int) == 4); assert(SIZEOF(int4) == 4); assert(SIZEOF(short) == 2); # ifdef OFF_T_LONG assert(SIZEOF(off_t) == 8); # else assert(SIZEOF(off_t) == 4); # endif assert(SIZEOF(sgmnt_data) == ROUND_UP(SIZEOF(sgmnt_data), DISK_BLOCK_SIZE)); # ifdef KEY_T_LONG assert(8 == SIZEOF(key_t)); # else assert(SIZEOF(key_t) == SIZEOF(int4)); # endif assert(SIZEOF(boolean_t) == 4); /* generated code passes 4 byte arguments, run time rtn might be expecting boolean_t arg */ assert(BITS_PER_UCHAR == 8); assert(SIZEOF(enum db_ver) == SIZEOF(int4)); assert(254 >= FNPC_MAX); /* The value 255 is reserved */ assert(SIZEOF(mval) == SIZEOF(mval_b)); assert(SIZEOF(chkmval.fnpc_indx) == SIZEOF(chkmval_b.fnpc_indx)); assert(OFFSETOF(mval, fnpc_indx) == OFFSETOF(mval_b, fnpc_indx)); DEBUG_ONLY(mtables_chk()); /* Validate mtables.c assumptions */ SFPTR(create_fatal_error_zshow_dmp_fptr, create_fatal_error_zshow_dmp); # ifdef GTM_PTHREAD assert(!gtm_main_thread_id_set); if (!gtm_main_thread_id_set) { gtm_main_thread_id = pthread_self(); gtm_main_thread_id_set = TRUE; } # endif tp_timeout_start_timer_ptr = tp_start_timer; tp_timeout_clear_ptr = tp_clear_timeout; tp_timeout_action_ptr = tp_timeout_action; ctrlc_handler_ptr = ctrlc_handler; if (MUMPS_UTILTRIGR != invocation_mode) op_open_ptr = op_open; unw_prof_frame_ptr = unw_prof_frame; stx_error_fptr = stx_error; stx_error_va_fptr = stx_error_va; show_source_line_fptr = show_source_line; /* For compile time optimization, we need to have the cache for $PIECE enabled */ for (i = 0; FNPC_MAX > i; i++) { /* Initialize cache structure for $[Z]PIECE function */ (TREF(fnpca)).fnpcs[i].indx = i; } (TREF(fnpca)).fnpcsteal = (TREF(fnpca)).fnpcs; /* Starting place to look for cache reuse */ (TREF(fnpca)).fnpcmax = &(TREF(fnpca)).fnpcs[FNPC_MAX - 1]; /* The last element */ curr_pattern = pattern_list = &mumps_pattern; pattern_typemask = mumps_pattern.typemask; initialize_pattern_table(); /* Initialize local collating sequence */ TREF(transform) = TRUE; lct = find_local_colltype(); if (lct != 0) { TREF(local_collseq) = ready_collseq(lct); if (!TREF(local_collseq)) { exi_condition = -ERR_COLLATIONUNDEF; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_COLLATIONUNDEF, 1, lct); EXIT(exi_condition); } } else TREF(local_collseq) = 0; if (gtm_utf8_mode) { dollar_zchset.len = STR_LIT_LEN(UTF8_NAME); dollar_zchset.addr = UTF8_NAME; if (utf8_patnumeric) { dollar_zpatnumeric.len = STR_LIT_LEN(UTF8_NAME); dollar_zpatnumeric.addr = UTF8_NAME; } } if (MUMPS_COMPILE == invocation_mode) /* MUMPS compile branches here and gets none of the below */ { TREF(transform) = FALSE; EXIT(gtm_compile()); } /* With the advent of reservedDBs, the ability to create a new database is not only in MUPIP but is in MUMPS too. * This means mucregini() (called by mu_cre_file()) is also in libgtmshr. But mucregini calls mupip_exit for certain * types of errors we won't run into with reservedDBs. So, if mupip.c has not already initialized this function * pointer for mupip_exit to be used in mucregini() (MUPIP calls this routine too), then initialize it to point to a * routine that will assert fail if it is used in MUMPS . */ if (NULL == mupip_exit_fp) mupip_exit_fp = assert_on_entry; /* This should be after cli_lex_setup() due to S390 A/E conversion in cli_lex_setup */ memset(&svec, 0, SIZEOF(svec)); svec.argcnt = SIZEOF(svec); svec.rtn_start = svec.rtn_end = malloc(SIZEOF(rtn_tabent)); memset(svec.rtn_start, 0, SIZEOF(rtn_tabent)); svec.user_strpl_size = STP_INITSIZE_REQUESTED; svec.ctrlc_enable = 1; svec.break_message_mask = 31; svec.labels = 1; svec.lvnullsubs = 1; svec.base_addr = (unsigned char *)1L; svec.zdate_form = 0; svec.sysid_ptr = &default_sysid; gtm_startup(&svec); gtm_startup_active = TRUE; } /* Routine to be driven by a function pointer when that function pointer should never be driven. * Does an assertpro() to stop things. */ void assert_on_entry(int4 arg) { assertpro(FALSE); } fis-gtm-V7.0-005/sr_unix/gtm_init_env.c0000644000032200000250000000225014342376334016672 0ustar librarygtc/**************************************************************** * * * 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 #include "stack_frame.h" #include "startup.h" #include "gtm_startup.h" #include "error.h" GBLREF stack_frame *frame_pointer; void gtm_init_env(rhdtyp *base_addr, unsigned char *transfer_addr) { assert(CURRENT_RHEAD_ADR(base_addr) == base_addr); ESTABLISH(mdb_condition_handler); base_frame(base_addr); #ifdef HAS_LITERAL_SECT new_stack_frame(base_addr, (unsigned char *)LINKAGE_ADR(base_addr), 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(base_addr, PTEXT_ADR(base_addr), transfer_addr); #endif REVERT; } fis-gtm-V7.0-005/sr_unix/op_currhd.c0000644000032200000250000000215014342376335016175 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2015 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 "op.h" #include #include "stack_frame.h" #include "linktrc.h" GBLREF stack_frame *frame_pointer; #ifdef AUTORELINK_SUPPORTED /* Routine to pick up the current routine header and stash it in lnk_proxy so the next indirect * call picks it up. We return 0 as the index into lnk_proxy to find the routine header. */ int op_currhd(void) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; TADR(lnk_proxy)->rtnhdr_adr = frame_pointer->rvector; DBGINDCOMP((stderr, "op_currhd: Routine reference resolved to 0x"lvaddr"\n", TADR(lnk_proxy)->rtnhdr_adr)); return 0; } #endif fis-gtm-V7.0-005/sr_unix/gtm_main.c0000644000032200000250000002005614342376334016007 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "startup.h" #include #include "stack_frame.h" #include "error.h" #include "cli.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gtmimagename.h" #include "op.h" #include "tp_timeout.h" #include "ctrlc_handler.h" #include "gtm_startup_chk.h" #include "gtm_startup.h" #include "jobchild_init.h" #include "cli_parse.h" #include "invocation_mode.h" #include "gtm_main.h" /* for "gtm_main" prototype */ #include "io.h" #include "common_startup_init.h" #include "gtm_threadgbl_init.h" #include "job.h" #include "restrict.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" #include "gtm_utf8.h" #include "gtm_conv.h" #endif #include "gtmcrypt.h" #ifdef GTM_TLS #include "gtm_tls.h" #endif GBLREF IN_PARMS *cli_lex_in_ptr; GBLREF char cli_token_buf[]; GBLREF char cli_err_str[]; GBLREF boolean_t gtm_dist_ok_to_use; GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF CLI_ENTRY mumps_cmd_ary[]; GBLREF boolean_t skip_dbtriggers; #if defined (GTM_TRIGGER) && (DEBUG) GBLREF ch_ret_type (*ch_at_trigger_init)(); #endif #ifdef UTF8_SUPPORTED GBLREF u_casemap_t gtm_strToTitle_ptr; /* Function pointer for gtm_strToTitle */ #endif GBLDEF CLI_ENTRY *cmd_ary = &mumps_cmd_ary[0]; /* Define cmd_ary to be the MUMPS specific cmd table */ #define GTMCRYPT_ERRLIT "during GT.M startup" #define GTMXC_gblstat "GTMXC_gblstat=%s/gtmgblstat.xc" #define MUMPS "MUMPS " #ifdef __osf__ /* On OSF/1 (Digital Unix), pointers are 64 bits wide; the only exception to this is C programs for which one may * specify compiler and link editor options in order to use (and allocate) 32-bit pointers. However, since C is * the only exception and, in particular because the operating system does not support such an exception, the argv * array passed to the main program is an array of 64-bit pointers. Thus the C program needs to declare argv[] * as an array of 64-bit pointers and needs to do the same for any pointer it sets to an element of argv[]. */ # pragma pointer_size (save) # pragma pointer_size (long) #endif GBLREF char **gtmenvp; error_def(ERR_CRYPTDLNOOPEN); error_def(ERR_CRYPTDLNOOPEN2); error_def(ERR_CRYPTINIT); error_def(ERR_CRYPTINIT2); error_def(ERR_RESTRICTEDOP); error_def(ERR_TEXT); error_def(ERR_TLSDLLNOOPEN); error_def(ERR_TLSINIT); int gtm_main (int argc, char **argv, char **envp) #ifdef __osf__ # pragma pointer_size (restore) #endif { char *ptr, *eq, **p; char gtmlibxc[GTM_PATH_MAX]; int eof, parse_ret; int gtmcrypt_errno; int status; size_t cplen; # ifdef GTM_SOCKET_SSL_SUPPORT char tlsid_env_name[MAX_TLSID_LEN * 2]; # endif DCL_THREADGBL_ACCESS; GTM_THREADGBL_INIT; gtmenvp = envp; gtm_dist_ok_to_use = TRUE; common_startup_init(GTM_IMAGE); GTMTRIG_DBG_ONLY(ch_at_trigger_init = &mdb_condition_handler); err_init(stop_image_conditional_core); UTF8_ONLY(gtm_strToTitle_ptr = >m_strToTitle); cli_lex_setup(argc, argv); /* put the arguments into buffer, then clean up the token buffer * cli_gettoken() copies all arguments except the first one argv[0] * into the buffer (cli_lex_in_ptr->in_str). * i.e. command line: "/usr/library/V990/mumps -run somefile" * the buffer cli_lex_in_ptr->in_str == "-run somefile" */ if (1 < argc) cli_gettoken(&eof); /* cli_gettoken() extracts the first token into cli_token_buf (in tok_extract()) * which should be done in parse_cmd(), So, reset the token buffer here to make * parse_cmd() starts from the first token */ cli_token_buf[0] = '\0'; /* insert the "MUMPS " in the parsing buffer the buffer is now: * cli_lex_in_ptr->in_str == "MUMPS -run somefile" * we didnot change argv[0] */ ptr = cli_lex_in_ptr->in_str; assert((NULL != ptr) && (cli_lex_in_ptr->buflen > (strlen(ptr) + sizeof(MUMPS)))); memmove(strlen(MUMPS) + ptr, ptr, strlen(ptr) + 1); /* BYPASSOK */ MEMCPY_LIT(ptr, MUMPS); /* reset the argument buffer pointer, it's changed in cli_gettoken() call above * do NOT reset to 0(NULL) to avoid fetching cmd line args into buffer again * cli_lex_in_ptr->tp is the pointer to indicate current position in the buffer * cli_lex_in_ptr->in_str */ cli_lex_in_ptr->tp = cli_lex_in_ptr->in_str; parse_ret = parse_cmd(); if (parse_ret && (EOF != parse_ret)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) parse_ret, 2, LEN_AND_STR(cli_err_str)); if (cli_present("DIRECT_MODE")) { if (!((ptr = GETENV(CHILD_FLAG_ENV)) && strlen(ptr)) && (RESTRICTED(dmode))) /* note assignment */ { /* first tell them it's a no-no without engaging the condition handling so we keep control */ dec_err(VARLSTCNT(3) MAKE_MSG_SEVERE(ERR_RESTRICTEDOP), 1, "mumps -direct"); stop_image_no_core(); /* then kill them off without further delay */ } invocation_mode = MUMPS_DIRECT; } else if (cli_present("RUN")) invocation_mode = MUMPS_RUN; gtm_chk_dist(argv[0]); /* this should be after cli_lex_setup() due to S390 A/E conversion in cli_lex_setup */ init_gtm(); SNPRINTF(gtmlibxc, GTM_PATH_MAX, GTMXC_gblstat, gtm_dist); PUTENV(status, gtmlibxc); # ifdef GTM_TLS if (MUMPS_COMPILE != invocation_mode) { if ((NULL != (ptr = (char *)getenv(GTM_PASSWD_ENV))) && (0 == strlen(ptr))) { INIT_PROC_ENCRYPTION(gtmcrypt_errno); if (0 != gtmcrypt_errno) { CLEAR_CRYPTERR_MASK(gtmcrypt_errno); assert(!IS_REPEAT_MSG_MASK(gtmcrypt_errno)); assert((ERR_CRYPTDLNOOPEN == gtmcrypt_errno) || (ERR_CRYPTINIT == gtmcrypt_errno)); if (ERR_CRYPTDLNOOPEN == gtmcrypt_errno) gtmcrypt_errno = ERR_CRYPTDLNOOPEN2; else if (ERR_CRYPTINIT == gtmcrypt_errno) gtmcrypt_errno = ERR_CRYPTINIT2; gtmcrypt_errno = SET_CRYPTERR_MASK(gtmcrypt_errno); GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, rts_error, SIZEOF(GTMCRYPT_ERRLIT) - 1, GTMCRYPT_ERRLIT); } } # ifdef GTM_SOCKET_SSL_SUPPORT /* The below logic is for prefetching the password for TLS identifiers that may have been set in the environment. * But, since SSL support for Socket devices is not yet implemented, this logic need not be enabled as of this * writing. When SSL support for socket devices is implemented, the surrounding #ifdef can be removed. */ if (NULL != getenv("gtmcrypt_config")) { /* Environment is configured for SSL/TLS (and/or encryption). Check if any environment variable of the form * `gtmtls_passwd_*' is set to NULL string. If so, nudge the SSL/TLS library to read password(s) from the * user. */ for (p = envp; *p; p++) { ptr = *p; if (0 == MEMCMP_LIT(ptr, GTMTLS_PASSWD_ENV_PREFIX)) { /* At least one environment variable of $gtmtls_passwd_* is found. */ eq = strchr(ptr, '='); if (0 != strlen(eq + 1)) break; /* Set to non-empty string. No need to initialize the library now. */ /* Set to empty string. */ if (NULL == tls_ctx) { if (SS_NORMAL != (status = gtm_tls_loadlibrary())) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TLSDLLNOOPEN, 0, ERR_TEXT, 2, LEN_AND_STR(dl_err)); } if (NULL == (tls_ctx = gtm_tls_init(GTM_TLS_API_VERSION, GTMTLS_OP_INTERACTIVE_MODE))) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_TLSINIT, 0, ERR_TEXT, 2, LEN_AND_STR(gtm_tls_get_error(NULL))); } } assert(NULL != tls_ctx); cplen = (eq - ptr); if (sizeof(tlsid_env_name) > (cplen + 1)) cplen = sizeof(tlsid_env_name) - 1; memcpy(tlsid_env_name, ptr, cplen); tlsid_env_name[cplen] = '\0'; gtm_tls_prefetch_passwd(tls_ctx, tlsid_env_name); } } } # endif } # endif dm_start(); return 0; } fis-gtm-V7.0-005/sr_unix/jobchild_init.c0000644000032200000250000001213014342376334017007 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_stdlib.h" #include "gtm_unistd.h" #include #include #include "startup.h" #include "gtm_startup.h" #include "stack_frame.h" #include "job_addr.h" #include "compiler.h" #include "indir_enum.h" #include "error.h" #include "util.h" #include "cli.h" #include "job.h" #include "gcall.h" #include "jobchild_init.h" #include "lv_val.h" /* needed for "callg.h" */ #include "callg.h" #include "invocation_mode.h" #include "gtmci.h" #include "send_msg.h" #include "have_crit.h" #define FILE_NAME_SIZE 255 #if defined(__x86_64__) extern void opp_ciret(); #endif LITDEF char interactive_mode_buf[] = "INTERACTIVE"; LITDEF char other_mode_buf[] = "OTHER"; GBLREF stack_frame *frame_pointer; GBLREF uint4 process_id; error_def(ERR_RUNPARAMERR); error_def(ERR_TEXT); error_def(ERR_SYSCALL); error_def(ERR_JOBSTARTCMDFAIL); error_def(ERR_JOBLABOFF); CONDITION_HANDLER(job_init_ch) { START_CH(TRUE); PRN_ERROR; NEXTCH; } /* Child process test and initialization. If this copy of GTM is a child process, then initialize the child. */ void jobchild_init(void) { job_params_type jparms; unsigned char *transfer_addr; /* Transfer data */ rhdtyp *base_addr; unsigned short i, arg_len; char run_file_name[FILE_NAME_SIZE + 2], *c; gcall_args job_arglist; mval job_args[MAX_ACTUALS]; mstr routine, label; int offset; int status; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ESTABLISH(job_init_ch); /* Check if environment variable ppid - job parent pid exists. If it does not, we are a regular * gtm process; else, we are a child process of a job command. */ if ((c = GETENV(CHILD_FLAG_ENV)) && strlen(c)) { /* We are a Jobbed process Get Job parameters and set up environment to run the Job command. */ /* read parameters into parameter structure - references CHILD_FLAG_ENV */ ojchildparms(&jparms, &job_arglist, job_args); /* Clear the environment variable so that subsequent child mumps processes can start normal initialization. */ PUTENV(status, CLEAR_CHILD_FLAG_ENV); if (status) { util_out_print("Unable to clear gtmj0 process !UL exiting.", TRUE, process_id); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); } /* Execute the command to be run before executing the actual M routine */ if (jparms.params.startup.len) { jparms.params.startup.buffer[jparms.params.startup.len] = '\0'; status = SYSTEM(jparms.params.startup.buffer); if (-1 == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_JOBSTARTCMDFAIL, 0, errno); else if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(2) ERR_JOBSTARTCMDFAIL, 0); } MSTR_DEF(routine_mstr, jparms.params.routine.len, jparms.params.routine.buffer); MSTR_DEF(label_mstr, jparms.params.label.len, jparms.params.label.buffer); if (!job_addr(&routine_mstr, &label_mstr, jparms.params.offset, (char **)&base_addr, (char **)&transfer_addr)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_JOBLABOFF); /* Set process priority */ if (jparms.params.baspri) { /* send message to system log if nice fails */ if (-1 == nice((int)jparms.params.baspri)) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("nice"), CALLFROM, errno); } /* Set up $ZMODE to "OTHER" */ (TREF(dollar_zmode)).mvtype = MV_STR; (TREF(dollar_zmode)).str.addr = (char *)other_mode_buf; (TREF(dollar_zmode)).str.len = SIZEOF(other_mode_buf) -1; } else { /* If we are not a child, setup a dummy mumps routine */ if (MUMPS_RUN == invocation_mode) { arg_len = FILE_NAME_SIZE; if (!cli_get_str("INFILE", run_file_name, &arg_len)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_RUNPARAMERR); lref_parse((uchar_ptr_t)run_file_name, &routine, &label, &offset); if (!job_addr(&routine, &label, offset, (char **)&base_addr, (char **)&transfer_addr)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_JOBLABOFF); } else if (MUMPS_CALLIN & invocation_mode) /* call-in mode */ { base_addr = make_cimode(); transfer_addr = PTEXT_ADR(base_addr); } else /* direct mode */ { base_addr = make_dmode(); transfer_addr = PTEXT_ADR(base_addr); } job_arglist.callargs = 0; /* Set up $ZMODE to "INTERACTIVE" */ (TREF(dollar_zmode)).mvtype = MV_STR; (TREF(dollar_zmode)).str.addr = (char *)interactive_mode_buf; (TREF(dollar_zmode)).str.len = SIZEOF(interactive_mode_buf) -1; } gtm_init_env(base_addr, transfer_addr); if (MUMPS_CALLIN & invocation_mode) { #if defined(__x86_64__) SET_CI_ENV(opp_ciret); #else SET_CI_ENV(ci_ret_code_exit); #endif } if (job_arglist.callargs) callg((INTPTR_T (*)(intszofptr_t cnt, ...))push_parm, (gparam_list *)&job_arglist); REVERT; } fis-gtm-V7.0-005/sr_unix/gtm_permissions.h0000644000032200000250000000434414342376334017445 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2018 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. * * * ****************************************************************/ #ifndef GTM_PERMISSIONS #define GTM_PERMISSIONS #include enum perm_target_types { PERM_FILE = 0x01, /* Request permissions for creating a new file */ PERM_IPC = 0x02, /* Request permissions for initializing IPCs (shm/sem) */ PERM_EXEC = 0x04 /* Request execute permissions, masked with the above. Currently only used with PERM_IPC */ }; #define MAX_PERM_LEN 12 #define MAX_PRINT_GID_LEN 128 #define GID_ELLIPSIS "..." #define GID_ELLIPSIS_LEN 4 #define PERMALL 07777 struct perm_diag_data { uid_t this_uid; gid_t this_gid; uid_t file_uid; gid_t file_gid; char file_perm[MAX_PERM_LEN]; gid_t lib_gid; char lib_perm[MAX_PERM_LEN]; char print_gid_list[MAX_PRINT_GID_LEN]; /* Can't imagine this getting any longer in the real world */ int print_gid_list_len; }; error_def(ERR_PERMGENFAIL); error_def(ERR_PERMGENDIAG); #define PERMGENDIAG_ARGS(pdd) \ ERR_PERMGENDIAG, 11, \ (pdd).this_uid, (pdd).this_gid, \ (pdd).print_gid_list_len, (pdd).print_gid_list, \ (pdd).file_uid, (pdd).file_gid, RTS_ERROR_STRING((pdd).file_perm), \ (pdd).lib_gid, RTS_ERROR_STRING((pdd).lib_perm) #define PERMGENDIAG_ARG_COUNT (13) GBLREF gid_t *gid_list; GBLREF int gid_list_len; #define GID_IN_GID_LIST(GID) ((NULL == gid_list) ? (gtm_init_gid_list(), gtm_gid_in_gid_list(GID)) : gtm_gid_in_gid_list(GID)) void gtm_init_gid_list(void); boolean_t gtm_gid_in_gid_list(gid_t); gid_t gtm_get_group_id(struct stat *stat_buff); boolean_t gtm_member_group_id(uid_t uid, gid_t gid, struct perm_diag_data *pdd); boolean_t gtm_permissions(struct stat *stat_buff, int *user_id, int *group_id, int *perm, enum perm_target_types target_type, struct perm_diag_data *pdd); #endif /* GTM_PERMISSIONS */ fis-gtm-V7.0-005/sr_unix/laberror.c0000644000032200000250000000366514342376334016033 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2021 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 "error.h" #include #include "stack_frame.h" GBLREF stack_frame *frame_pointer; error_def(ERR_LABELMISSING); /* Add simple declaration to suppress warning - routine only used by assembler routines so no need for * definition in header file. */ void laberror(int lblindx); /* Routine that allows assembler routines to more easily put out the LABELMISSING error message with the * appropriate label name argument. * * Argument: * * lblindx - index into linkage_adr and linkage_names */ void laberror(int lblindx) { mstr lblname; int skiplen; char *cptr, *maxcptr; # ifdef AUTORELINK_SUPPORTED assertpro(0 <= lblindx); assert(lblindx <= frame_pointer->rvector->linkage_len); lblname = frame_pointer->rvector->linkage_names[lblindx]; /* Make copy of possibly shared mstr */ lblname.addr += (INTPTR_T)frame_pointer->rvector->literal_text_adr; /* Relocate addr appropriately */ /* Label name is in form of "rtnname.labelname" so forward space past routine name and '.' */ maxcptr = lblname.addr + lblname.len; for (cptr = lblname.addr; ('.' != *cptr) && (cptr < maxcptr); cptr++) ; assert('.' == *cptr); skiplen = cptr - lblname.addr + 1; /* + 1 to skip past '.' separator */ assert(skiplen < lblname.len); lblname.len -= skiplen; lblname.addr += skiplen; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_LABELMISSING, 2, RTS_ERROR_MSTR(&lblname)); # else assertpro(FALSE); # endif return; } fis-gtm-V7.0-005/sr_unix/gtm_startup.c0000644000032200000250000003061514342376334016567 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "gtm_stdlib.h" #include "gtm_inet.h" #include "gtm_signal.h" #include "error.h" #include #include "stack_frame.h" #include "stringpool.h" #include "stp_parms.h" #include "mv_stent.h" #include "startup.h" #include "cmd_qlf.h" #include "lv_val.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdskill.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" #include "tp.h" #include "interlock.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtm_env_xlate_init.h" #include "callintogtmxfer.h" #include "xfer_enum.h" #include "cache.h" #include "op.h" #include "gt_timer.h" #include "io.h" #include "gtmio.h" #include "dpgbldir_sysops.h" /* for dpzgbini prototype */ #include "comp_esc.h" #include "ctrlc_handler.h" #include "get_page_size.h" #include "generic_signal_handler.h" #include "zcall_package.h" #include "getzdir.h" #include "getzmode.h" #include "getzprocess.h" #include "gtm_exit_handler.h" #include "gtmmsg.h" #include "getjobname.h" #include "jobchild_init.h" #include "error_trap.h" /* for ecode_init() prototype */ #include "zyerror_init.h" #include "trap_env_init.h" #include "zdate_form_init.h" #include "mstack_size_init.h" #include "dollar_system_init.h" #include "sig_init.h" #include "gtm_startup.h" #include "svnames.h" #include "jobinterrupt_process.h" #include "zco_init.h" #include "gtm_logicals.h" /* for DISABLE_ALIGN_STRINGS */ #include "suspsigs_handler.h" #include "logical_truth_value.h" #include "gtm_utf8.h" #include "gtm_icu_api.h" /* for u_strToUpper and u_strToLower */ #include "gtm_conv.h" #include "fix_xfer_entry.h" #include "zwrite.h" #include "alias.h" #include "cenable.h" #include "gtmimagename.h" #include "mprof.h" #include "gt_timers_add_safe_hndlrs.h" #include "continue_handler.h" #include "jobsp.h" /* For gcall.h */ #include "gcall.h" /* For ojchildparms() */ #include "common_startup_init.h" #include "trans_numeric.h" #ifdef UTF8_SUPPORTED #include "utfcgr.h" #endif GBLDEF void (*restart)() = &mum_tstart; #ifdef __MVS__ /* In zOS we cann't access function address directly, So creating function pointer * which points to mdb_condition_handler. We can use this function pointer to get * the address of mdb_condition_handler in ESTABLSH assembly macro. */ GBLDEF ch_ret_type (*mdb_condition_handler_ptr)(int arg) = &mdb_condition_handler; #endif GBLREF rtn_tabent *rtn_fst_table, *rtn_names, *rtn_names_top, *rtn_names_end; GBLREF int4 break_message_mask; GBLREF stack_frame *frame_pointer; GBLREF unsigned char *stackbase, *stacktop, *stackwarn, *msp; GBLREF unsigned char *fgncal_stack; GBLREF mv_stent *mv_chain; GBLREF xfer_entry_t xfer_table[]; GBLREF mval dollar_system; GBLREF mval dollar_zstatus; GBLREF bool compile_time; GBLREF spdesc stringpool; GBLREF spdesc rts_stringpool; GBLREF command_qualifier glb_cmd_qlf, cmd_qlf; GBLREF lv_val *zsrch_var, *zsrch_dir1, *zsrch_dir2; GBLREF symval *curr_symval; GBLREF boolean_t is_replicator; GBLREF void (*ctrlc_handler_ptr)(); GBLREF boolean_t mstr_native_align; GBLREF boolean_t gtm_utf8_mode; GBLREF casemap_t casemaps[]; GBLREF void (*cache_table_relobjs)(void); /* Function pointer to call cache_table_rebuild() */ GBLREF ch_ret_type (*ht_rhash_ch)(); /* Function pointer to hashtab_rehash_ch */ GBLREF ch_ret_type (*jbxm_dump_ch)(); /* Function pointer to jobexam_dump_ch */ GBLREF ch_ret_type (*stpgc_ch)(); /* Function pointer to stp_gcol_ch */ GBLREF enum gtmImageTypes image_type; GBLREF int init_xfer_table(void); GBLREF void (*primary_exit_handler)(void); OS_PAGE_SIZE_DECLARE #define MIN_INDIRECTION_NESTING 32 #define MAX_INDIRECTION_NESTING 256 void gtm_startup(struct startup_vector *svec) { /* initialize various process characteristics and states, but beware as initialization occurs in other places as well * svec is really a VMS vestige as it had a file to tailor process characteristics * while in UNIX, it's all done with environment variables * hence, various references to data copied from *svec could profitably be referenced directly */ boolean_t is_defined; char *temp; int4 temp_gtm_strpllim; mstr log_name; stack_frame *frame_pointer_lcl; static char other_mode_buf[] = "OTHER"; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(INVALID_IMAGE != image_type); assert(svec->argcnt == SIZEOF(*svec)); IA64_ONLY(init_xfer_table()); get_page_size(); cache_table_relobjs = &cache_table_rebuild; INIT_FNPTR_GLOBAL_VARIABLES; ht_rhash_ch = &hashtab_rehash_ch; jbxm_dump_ch = &jobexam_dump_ch; stpgc_ch = &stp_gcol_ch; rtn_fst_table = rtn_names = (rtn_tabent *)svec->rtn_start; rtn_names_end = rtn_names_top = (rtn_tabent *)svec->rtn_end; mstack_size_init(svec); /* mark the stack base so that if error occur during call-in gtm_init(), the unwind * logic in gtmci_ch() will get rid of the stack completely */ fgncal_stack = stackbase; mv_chain = (mv_stent *)msp; mv_chain->mv_st_type = MVST_STORIG; /* Initialize first (anchor) mv_stent so doesn't do anything */ mv_chain->mv_st_next = 0; mv_chain->mv_st_cont.mvs_storig = 0; break_message_mask = svec->break_message_mask; if (svec->user_strpl_size < STP_INITSIZE) svec->user_strpl_size = STP_INITSIZE; else if (svec->user_strpl_size > STP_MAXINITSIZE) svec->user_strpl_size = STP_MAXINITSIZE; stp_init(svec->user_strpl_size); assertpro(stringpool.base); rts_stringpool = stringpool; (TREF(tpnotacidtime)).mvtype = MV_NM | MV_INT; /* gtm_env_init set up a numeric value, now there's a stp: string it */ MV_FORCE_STRD(&(TREF(tpnotacidtime))); assert(6 >= (TREF(tpnotacidtime)).str.len); temp = malloc((TREF(tpnotacidtime)).str.len); memcpy(temp, (TREF(tpnotacidtime)).str.addr, (TREF(tpnotacidtime)).str.len); (TREF(tpnotacidtime)).str.addr = temp; TREF(compile_time) = FALSE; /* assert that is_replicator and run_time is properly set by gtm_imagetype_init invoked at process entry */ # ifdef DEBUG switch (image_type) { case GTM_IMAGE: assert(is_replicator && run_time); break; case MUPIP_IMAGE: assert(!is_replicator && !run_time); break; default: assert(FALSE); } # endif gtm_utf8_init(); /* Initialize the runtime for UTF8 */ /* Initialize alignment requirement for the runtime stringpool */ log_name.addr = DISABLE_ALIGN_STRINGS; log_name.len = STR_LIT_LEN(DISABLE_ALIGN_STRINGS); /* mstr_native_align = logical_truth_value(&log_name, FALSE, NULL) ? FALSE : TRUE; */ mstr_native_align = FALSE; /* TODO: remove this line and uncomment the above line */ /* See if $gtm_string_pool_limit is set */ log_name.addr = GTM_STRPLLIM; log_name.len = SIZEOF(GTM_STRPLLIM) - 1; temp_gtm_strpllim = trans_numeric(&log_name, &is_defined, TRUE); if (0 < temp_gtm_strpllim) stringpool.strpllim = (temp_gtm_strpllim < STP_GCOL_TRIGGER_FLOOR ? STP_GCOL_TRIGGER_FLOOR : 0) + temp_gtm_strpllim; getjobname(); getzprocess(); getzmode(); zcall_init(); cmd_qlf.qlf = glb_cmd_qlf.qlf; cache_init(); msp -= SIZEOF(stack_frame); frame_pointer_lcl = (stack_frame *)msp; memset(frame_pointer_lcl, 0, SIZEOF(stack_frame)); frame_pointer_lcl->temps_ptr = (unsigned char *)frame_pointer_lcl; frame_pointer_lcl->ctxt = GTM_CONTEXT(gtm_ret_code); frame_pointer_lcl->mpc = CODE_ADDRESS(gtm_ret_code); frame_pointer_lcl->type = SFT_COUNT; frame_pointer_lcl->rvector = (rhdtyp *)malloc(SIZEOF(rhdtyp)); memset(frame_pointer_lcl->rvector, 0, SIZEOF(rhdtyp)); frame_pointer = frame_pointer_lcl; symbinit(); /* Variables for supporting $ZSEARCH sorting and wildcard expansion */ TREF(zsearch_var) = lv_getslot(curr_symval); LVVAL_INIT((TREF(zsearch_var)), curr_symval); /* Initialize global pointer to control-C handler. Also used in iott_use */ ctrlc_handler_ptr = &ctrlc_handler; if (!IS_MUPIP_IMAGE) { sig_init(generic_signal_handler, ctrlc_handler_ptr, suspsigs_handler, continue_handler); atexit(gtm_exit_handler); primary_exit_handler = gtm_exit_handler; } io_init(IS_MUPIP_IMAGE); /* starts with nocenable for GT.M runtime, enabled for MUPIP */ if (!IS_MUPIP_IMAGE) { cenable(); /* cenable unless the environment indicates otherwise - 2 steps because this can report errors */ } jobinterrupt_init(); getzdir(); dpzgbini(); zco_init(); /* a base addr of 0 indicates a gtm_init call from an rpc server */ if ((GTM_IMAGE == image_type) && (NULL != svec->base_addr)) jobchild_init(); else { /* Trigger enabled utilities will enable through here */ (TREF(dollar_zmode)).mvtype = MV_STR; (TREF(dollar_zmode)).str.addr = other_mode_buf; (TREF(dollar_zmode)).str.len = SIZEOF(other_mode_buf) -1; } svec->frm_ptr = (unsigned char *)frame_pointer; dollar_zstatus.mvtype = MV_STR; dollar_zstatus.str.len = 0; dollar_zstatus.str.addr = NULL; ecode_init(); zyerror_init(); if (IS_MUMPS_IMAGE) trap_env_init(); zdate_form_init(svec); dollar_system_init(svec); init_callin_functable(); gtm_env_xlate_init(); ce_init(); /* initialize compiler escape processing */ prealloc_gt_timers(); gt_timers_add_safe_hndlrs(); /* Initialize zwrite subsystem. Better to do it now when we have storage to allocate than * if we fail and storage allocation may not be possible. To that end, pretend we have * seen alias acitivity so those structures are initialized as well. */ assert(FALSE == curr_symval->alias_activity); curr_symval->alias_activity = TRUE; lvzwr_init((enum zwr_init_types)0, (mval *)NULL); TREF(in_zwrite) = FALSE; curr_symval->alias_activity = FALSE; if ((GTM_IMAGE == image_type) && (NULL != svec->base_addr)) /* We are in the grandchild at this point. This call is made to greet local variables sent from the midchild. There * is no symbol table for locals before this point so we have to greet them here, after creating the symbol table. */ ojchildparms(NULL, NULL, NULL); if ((NULL != (TREF(mprof_env_gbl_name)).str.addr)) turn_tracing_on(TADR(mprof_env_gbl_name), TRUE, (TREF(mprof_env_gbl_name)).str.len > 0); return; } void gtm_utf8_init(void) { int utfcgr_size, alloc_size, i; # ifdef UTF8_SUPPORTED utfcgr *utfcgrp, *p; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (!gtm_utf8_mode) { /* UTF8 is not enabled (i.e. $ZCHSET="M"). All standard functions must be byte oriented */ FIX_XFER_ENTRY(xf_setextract, op_setzextract); FIX_XFER_ENTRY(xf_fnj2, op_fnzj2); FIX_XFER_ENTRY(xf_setpiece, op_setzpiece); /* If optimization of this happens, we need to move this to * expritem.c under 'update opcodes rather than mess with xfer table' */ FIX_XFER_ENTRY(xf_fnpopulation, op_fnzpopulation); FIX_XFER_ENTRY(xf_setp1, op_setzp1); /* If optimization of this happens, we need to move this to * expritem.c under 'update opcodes rather than mess with xfer table' */ FIX_XFER_ENTRY(xf_fnreverse, op_fnzreverse); return; } else { /* We are in UTF8 mode - allocate desired UTF8 parse cache and initialize it. This is effectively a 2 dimensional * structure where both dimensions are variable. */ # ifdef UTF8_SUPPORTED utfcgr_size = OFFSETOF(utfcgr, entry) + (SIZEOF(utfcgr_entry) * TREF(gtm_utfcgr_string_groups)); alloc_size = utfcgr_size * TREF(gtm_utfcgr_strings); (TREF(utfcgra)).utfcgrs = utfcgrp = (utfcgr *)malloc(alloc_size); memset((char *)utfcgrp, 0, alloc_size); /* Init to zeros */ for (i = 0, p = utfcgrp; TREF(gtm_utfcgr_strings) > i; i++, p = (utfcgr *)((INTPTR_T)p + utfcgr_size)) /* Initialize cache structure for UTF8 string scan lookaside cache */ p->idx = i; /* Initialize index value */ (TREF(utfcgra)).utfcgrsize = utfcgr_size; (TREF(utfcgra)).utfcgrsteal = utfcgrp; /* Starting place to look for cache reuse */ /* Pointer to the last usable utfcgr struct */ (TREF(utfcgra)).utfcgrmax = (utfcgr *)((UINTPTR_T)utfcgrp + ((TREF(gtm_utfcgr_strings) - 1) * utfcgr_size)); /* Spins to find non-(recently)-referenced cache slot before we overwrite an entry */ TREF(utfcgr_string_lookmax) = TREF(gtm_utfcgr_strings) / UTFCGR_MAXLOOK_DIVISOR; # endif /* UTF8_SUPPORTED */ } } fis-gtm-V7.0-005/sr_unix/lref_parse.c0000644000032200000250000000362014342376334016334 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "gtm_stdlib.h" #include #include "startup.h" #include "gtm_startup.h" error_def(ERR_RUNPARAMERR); /* parse an entry reference string into routine, label & offset */ void lref_parse(unsigned char *label_ref, mstr* routine, mstr* label, int* offset) { unsigned char ch, *c, *c1, *top; int i, label_len; routine->addr = label->addr = (char *)label_ref; *offset = 0; label_len = STRLEN((const char *)label_ref); assert(0 <= label_len); top = label_ref + label_len; for (i = 0, c = label_ref; i < label_len; i++) { ch = *c++; if (ch == '^' || ch == '+') { label->len = i; if (ch == '+') { *offset = (int)STRTOL((const char *)c, (char**)&c1, 10); if (c == c1 ||*c1 != '^') RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_RUNPARAMERR); c = c1 + 1; } assert(top > c); routine->addr = (char *)c; routine->len = INTCAST(top - c); break; } } if (routine->addr == (char *)label_ref) { routine->len = label_len; routine->addr = (char *)label_ref; label->len = 0; } if (!is_ident(routine)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_RUNPARAMERR); if (label->len && !is_ident(label)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_RUNPARAMERR); routine->len = routine->len > MAX_MIDENT_LEN ? MAX_MIDENT_LEN : routine->len; label->len = label->len > MAX_MIDENT_LEN ? MAX_MIDENT_LEN : label->len; } fis-gtm-V7.0-005/sr_unix/gtm_text_alloc.c0000644000032200000250000005721214342376334017225 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2007-2022 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. * * * ****************************************************************/ /* Storage manager for mmap() allocated storage used for executable code. * Uses power-of-two "buddy" system as described by Knuth. Allocations up to * size - SIZEOF(header) are managed by the buddy system. Larger * sizes are only "tracked" and then released via munmap() when they are freed. * * The algorithms used in this module are very similar to those used in * gtm_malloc.c with some changes and fewer of the generation options * since this is a more special purpose type allocation mechanism. */ #include "mdef.h" #include #include #include #include #include "gtm_signal.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_stdlib.h" #include "gtm_unistd.h" #include "eintr_wrappers.h" #include "mdq.h" #include "min_max.h" #include "error.h" #include "gtmmsg.h" #include "caller_id.h" #include "gtm_text_alloc.h" #include "gtmdbglvl.h" #include "gtmio.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "deferred_events.h" GBLREF int process_exiting; /* Process is on it's way out */ GBLREF uint4 gtmDebugLevel; GBLREF volatile int4 fast_lock_count; /* Stop stale/epoch processing while we have our parts exposed */ OS_PAGE_SIZE_DECLARE #ifdef COMP_GTA /* Only build this routine if it is going to be called */ /* This module is built in two different ways: (1) For z/OS the allocation and free routines will just call * __malloc31() and free() respectively since mmap() on z/OS does not support the necessary features as of this * writing (12/2008). (2) For all other platforms that use this module (Linux and Tru64 builds currently), the * module will expand with the mmap code. [SE 12/2008] */ /* The MAXTWO is set to pagesize and MINTWO to 5 sizes below that. Our systems have page * sizes of 16K, 8K, and 4K. */ #define MAXTWO gtm_os_page_size #define MINTWO TwoTable[0] /* Computed by gtaSmInit() */ #define MAXINDEX 5 /* Fields to help instrument our algorithm */ GBLREF boolean_t gtmSystemMalloc; /* Use the system's malloc() instead of our own */ GBLREF boolean_t malloccrit_issued; /* set at time of MALLOCCRIT */ GBLREF int gtmMallocErrorErrno; /* Errno at point of last failure */ GBLREF size_t gtmMallocErrorSize; /* Size of last failed malloc */ GBLREF size_t zmalloclim; /* ISV memory warning of MALLOCCRIT in bytes */ GBLREF size_t totalRmalloc; /* Total storage allocated through malloc() */ GBLREF size_t totalRallocGta; /* Total storage currently (real) mmap alloc'd */ GBLREF size_t totalAllocGta; /* Total mmap allocated (includes allocation overhead but not free space */ GBLREF size_t totalUsedGta; /* Sum of "in-use" portions (totalAllocGta - overhead) */ GBLREF unsigned char *gtmMallocErrorCallerid; /* Callerid of last failed malloc */ static int totalAllocs; /* Total alloc requests */ static int totalFrees; /* Total free requests */ static size_t rAllocMax; /* Maximum value of totalRallocGta */ static int allocCnt[MAXINDEX + 2]; /* Alloc count satisfied by each queue size */ static int freeCnt[MAXINDEX + 2]; /* Free count for element in each queue size */ static int elemSplits[MAXINDEX + 2]; /* Times a given queue size block was split */ static int elemCombines[MAXINDEX + 2]; /* Times a given queue block was formed by buddies being recombined */ static int freeElemCnt[MAXINDEX + 2]; /* Current count of elements on the free queue */ static int freeElemMax[MAXINDEX + 2]; /* Maximum number of blocks on the free queue */ error_def(ERR_INVDBGLVL); error_def(ERR_MALLOCCRIT); error_def(ERR_MEMORY); error_def(ERR_MEMORYRECURSIVE); error_def(ERR_SYSCALL); error_def(ERR_TEXT); error_def(ERR_TRNLOGFAIL); #define INCR_CNTR(x) ++x #define DECR_CNTR(x) --x #define INCR_SUM(x, y) x += y #define DECR_SUM(x, y) {x -= y; assert(0 <= x);} #define SET_MAX(max, tst) max = MAX(max, tst) #define SET_ELEM_MAX(idx) SET_MAX(freeElemMax[idx], freeElemCnt[idx]) #define CALLERID ((unsigned char *)caller_id(0)) #ifdef DEBUG # define TRACE_TXTALLOC(addr,len) {if (GDL_SmTrace & gtmDebugLevel) \ FPRINTF(stderr, "TxtAlloc at 0x"lvaddr" of %ld bytes from 0x"lvaddr"\n", addr, len, CALLERID);} # define TRACE_TXTFREE(addr,len) {if (GDL_SmTrace & gtmDebugLevel) \ FPRINTF(stderr, "TxtFree at 0x"lvaddr" of %ld bytes from 0x"lvaddr"\n", addr, len, CALLERID);} #else # define TRACE_TXTALLOC(addr, len) # define TRACE_TXTFREE(addr, len) #endif #ifdef __MVS__ static uint4 TwoTable[MAXINDEX + 2]; /* ******* z/OS expansion ******* */ #undef malloc #undef free #include #include "obj_file.h" /* This function is meant as a temporary replacement for the gtm_text_alloc code that uses mmap. * ABS 2008/12 - It is deficient in two regards: * 1) It abuses textElem - the abuse stems from account needs. It was hoped that we could simply * abuse textElem to hold the actual length of memory allocated and then use the size of textElem * as the offset to the original start of memory address that was malloc'ed. However, the * userStart of memory needs to be SECTION_ALIGN_BOUNDARY byte aligned. * action: don't use textElem * 2) SECTION_ALIGN_BOUNDARY is 16 bytes in 64bit world. Since __malloc31 is returning 8 byte * aligned memory, we really only needed a pad of 8 bytes. But that left no real mechanism to * return to the original start of memory. So we have a pad of 24 bytes. The first 8 bytes point * back to the start of memory. If the next 8 bytes are 16 byte aligned that is returned to the * caller. If not, then we store the start of memory there and return the next 8 bytes. This allows * us to free() the correct address. * action: remove SECTION_ALIGN_BOUNDARY as a restriction for all 64bit platforms except IA64 */ void *gtm_text_alloc(size_t size) { textElem *uStor; unsigned long *aligned, *memStart; int hdrSize, tSize, save_errno; hdrSize = SIZEOF(textElem); /* Pad the memory area for SECTION_ALIGN_BOUNDARY alignment required by comp_indr() */ tSize = (int)size + hdrSize + (SECTION_ALIGN_BOUNDARY * 2); uStor = __malloc31(tSize); if (NULL != uStor) { assert(((long)uStor & (long)-8) == (long)uStor); aligned = (unsigned long *)&uStor->userStorage.userStart; aligned++; /* Matching the alignment as required in comp_indr() */ aligned = (unsigned long *)ROUND_UP2((unsigned long)aligned, (unsigned long)SECTION_ALIGN_BOUNDARY); memStart = aligned - 1; *memStart = (unsigned long) uStor; assert((unsigned long)uStor == *memStart); uStor->realLen = tSize; INCR_SUM(totalRallocGta, tSize); INCR_SUM(totalAllocGta, tSize); INCR_SUM(totalUsedGta, tSize); INCR_CNTR(totalAllocs); SET_MAX(rAllocMax, totalRallocGta); TRACE_TXTALLOC(aligned, tSize); return (void *)aligned; } save_errno = errno; if (ENOMEM == save_errno) { assert(FALSE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_MEMORY, 2, tSize, CALLERID, save_errno); } /* On non-allocate related error, give more general error and assertpro(FALSE) */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(14) ERR_SYSCALL, 5, LEN_AND_LIT("gtm_text_alloc()"), CALLFROM, save_errno, 0, ERR_TEXT, 3, LEN_AND_LIT("Storage call made from"), CALLERID); assertpro(FALSE); } void gtm_text_free(void *addr) { int size; long *storage; textElem *uStor; storage = (long *)addr; storage--; uStor = (textElem *)*storage; size = uStor->realLen; free(uStor); DECR_SUM(totalRallocGta, size); DECR_SUM(totalAllocGta, size); DECR_SUM(totalUsedGta, size); INCR_CNTR(totalFrees); TRACE_TXTFREE(addr, size); } #else /* if not __MVS__ */ /* ******* Normal mmap() expansion ******* */ /* These routines for Unix are NOT thread or interrupt safe */ # define TEXT_ALLOC(SIZE, ADDR) \ MBSTART { \ int SAVE_ERRNO; \ \ ADDR = mmap(NULL, SIZE, (PROT_READ + PROT_WRITE + PROT_EXEC), (MAP_PRIVATE + MAP_ANONYMOUS), -1, 0); \ if (MAP_FAILED == ADDR) \ { \ --gtaSmDepth; \ --fast_lock_count; \ SAVE_ERRNO = errno; \ if (ENOMEM == SAVE_ERRNO) \ { \ assert(WBTEST_ENABLED(WBTEST_SKIP_CORE_FOR_MEMORY_ERROR)); \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_MEMORY, 2, SIZE, CALLERID, SAVE_ERRNO); \ } \ /* On non-allocate related error, give more general error and assertpro(FALSE) */ \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(14) ERR_SYSCALL, 5, LEN_AND_LIT("mmap()"), CALLFROM, \ SAVE_ERRNO, 0, ERR_CALLERID, 3, LEN_AND_LIT("TEXT_ALLOC"), CALLERID); \ assertpro(FALSE); \ } \ if (!gtmSystemMalloc && !malloccrit_issued /* totalRmalloc* not available for system malloc */ \ && (0 < zmalloclim) && ((SIZE + totalRmalloc + totalRallocGta) > zmalloclim)) \ { /* Boundary check on zmalloclim */ \ gtmMallocErrorSize = SIZE; \ gtmMallocErrorCallerid = CALLERID; \ gtmMallocErrorErrno = ERR_MALLOCCRIT; \ /* doubling here and halving in gtm_env_init accomodate the fact that \ * stringpool expansions are large, and if occurring, not easy to gracefully \ * continue from, which is also related to deferring the MALLOCCRIT error \ */ \ assertpro(!IS_GTMSECSHR_IMAGE); \ malloccrit_issued = TRUE; \ (*xfer_set_handlers_fnptr)(defer_error, ERR_MALLOCCRIT, FALSE); \ } \ } MBEND #define TEXT_FREE(ADDR, RSIZE) \ MBSTART { \ int RC, SAVE_ERRNO; \ RC = munmap(ADDR, RSIZE); \ if (-1 == RC) \ { \ --gtaSmDepth; \ --fast_lock_count; \ SAVE_ERRNO = errno; \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(14) ERR_SYSCALL, 5, LEN_AND_LIT("munmap"), CALLFROM, \ SAVE_ERRNO, 0, ERR_CALLERID, 3, LEN_AND_LIT("TEXT_FREE"), CALLERID); \ assertpro(FALSE); \ } \ } MBEND #define STE_FP(p) p->userStorage.links.fPtr #define STE_BP(p) p->userStorage.links.bPtr /* Following are values used in queueIndex in a storage element. Note that both * values must be less than zero for the current code to function correctly. */ #define QUEUE_ANCHOR -1 #define REAL_ALLOC -2 #ifdef DEBUG_SM # define DEBUGSM(x) (PRINTF x, FFLUSH(stdout)) # else # define DEBUGSM(x) #endif /* Define "routines" to enqueue and dequeue storage elements. Use define so we don't * have to depend on each implementation's compiler inlining to get efficient code here. */ #define ENQUEUE_STOR_ELEM(idx, elem) \ { \ textElem *qHdr, *fElem; \ qHdr = &freeStorElemQs[idx]; \ STE_FP(elem) = fElem = STE_FP(qHdr); \ STE_BP(elem) = qHdr; \ STE_FP(qHdr) = STE_BP(fElem) = elem; \ INCR_CNTR(freeElemCnt[idx]); \ SET_ELEM_MAX(idx); \ } #define DEQUEUE_STOR_ELEM(elem) \ { \ STE_FP(STE_BP(elem)) = STE_FP(elem); \ STE_BP(STE_FP(elem)) = STE_BP(elem); \ DECR_CNTR(freeElemCnt[elem->queueIndex]); \ } #define GET_QUEUED_ELEMENT(sizeIndex, uStor, qHdr) \ { \ qHdr = &freeStorElemQs[sizeIndex]; \ uStor = STE_FP(qHdr); /* First element on queue */ \ if (QUEUE_ANCHOR != uStor->queueIndex) /* Does element exist? (Does queue point to itself?) */ \ { \ DEQUEUE_STOR_ELEM(uStor); /* It exists, dequeue it for use */ \ } else \ uStor = gtaFindStorElem(sizeIndex); \ assert(0 == ((unsigned long)uStor & (TwoTable[sizeIndex] - 1))); /* Verify alignment */ \ } GBLREF readonly struct { unsigned char nullHMark[4]; unsigned char nullStr[1]; unsigned char nullTMark[4]; } NullStruct; static uint4 TwoTable[MAXINDEX + 2]; static textElem freeStorElemQs[MAXINDEX + 1]; /* Need full element as queue anchor for dbl-linked * list since ptrs not at top of element */ static volatile int4 gtaSmDepth; /* If we get nested... */ static boolean_t gtaSmInitialized; /* Initialized indicator */ /* Internal prototypes */ void gtaSmInit(void); textElem *gtaFindStorElem(int sizeIndex); int getSizeIndex(size_t size); error_def(ERR_TRNLOGFAIL); error_def(ERR_INVDBGLVL); error_def(ERR_MEMORY); error_def(ERR_SYSCALL); error_def(ERR_MEMORYRECURSIVE); error_def(ERR_CALLERID); error_def(ERR_TEXT); error_def(ERR_MALLOCMAXUNIX); /* Initialize the storage manangement system. Things to initialize: * * - Initialize size2Index table. This table is used to convert a malloc request size * to a storage queue index. * - Initialize queue anchor fwd/bkwd pointers to point to queue anchors so we * build a circular queue. This allows elements to be added and removed without * end-of-queue special casing. The queue anchor element is easily recognized because * it's queue index size will be set to a special value. * - Initialize debug mode. See if gtm_debug_level environment variable is set and * retrieve it's value if yes. */ void gtaSmInit(void) { char *ascNum; textElem *uStor; int i, sizeIndex, twoSize; /* WARNING!! Since this is early initialization, the assert(s) below are not well behaved if they do * indeed trip. The best that can be hoped for is they give a condition handler exhausted error on * GTM startup. Unfortunately, more intelligent responses are somewhat elusive since no output devices * are setup nor (potentially) most of the GTM runtime. */ /* Initialize the TwoTable fields for our given page size */ TwoTable[MAXINDEX + 1] = 0xFFFFFFFF; for (sizeIndex = MAXINDEX, twoSize = gtm_os_page_size; 0 <= sizeIndex; --sizeIndex, twoSize >>= 1) { assert(0 < twoSize); TwoTable[sizeIndex] = twoSize; assert((MAXINDEX == sizeIndex) || (TwoTable[sizeIndex] < TwoTable[sizeIndex + 1])); } /* Need to initialize the fwd/bck ptrs in the anchors to point to themselves */ for (uStor = &freeStorElemQs[0], i = 0; i <= MAXINDEX; ++i, ++uStor) { STE_FP(uStor) = STE_BP(uStor) = uStor; uStor->queueIndex = QUEUE_ANCHOR; } gtaSmInitialized = TRUE; } /* Recursive routine used to obtain an element on a given size queue. If no * elements of that size are available, we recursively call ourselves to get * an element of the next larger queue which we will then split in half to * get the one we need and place the remainder back on the free queue of its * new smaller size. If we run out of queues, we obtain a fresh new 'hunk' of * storage, carve it up into the largest block size we handle and process as * before. */ textElem *gtaFindStorElem(int sizeIndex) { unsigned char *uStorAlloc; textElem *uStor, *uStor2, *qHdr; int hdrSize; ++sizeIndex; if (MAXINDEX >= sizeIndex) { /* We have more queues to search */ GET_QUEUED_ELEMENT(sizeIndex, uStor, qHdr); /* We have a larger than necessary element now so break it in half and put the second half on the queue one size smaller than us */ INCR_CNTR(elemSplits[sizeIndex]); --sizeIndex; /* Dealing now with smaller element queue */ assert(0 <= sizeIndex && MAXINDEX >= sizeIndex); uStor2 = (textElem *)((unsigned long)uStor + TwoTable[sizeIndex]); uStor2->state = TextFree; uStor2->queueIndex = sizeIndex; assert(0 == ((unsigned long)uStor2 & (TwoTable[sizeIndex] - 1))); /* Verify alignment */ ENQUEUE_STOR_ELEM(sizeIndex, uStor2); /* Place on free queue */ } else { /* Nothing left to search, [real] allocation must occur */ TEXT_ALLOC((size_t)MAXTWO, uStorAlloc); uStor2 = (textElem *)uStorAlloc; /* Make addr "MAXTWO" byte aligned */ uStor = (textElem *)(((unsigned long)(uStor2) + MAXTWO - 1) & (unsigned long) -MAXTWO); INCR_SUM(totalRallocGta, MAXTWO); SET_MAX(rAllocMax, totalRallocGta); DEBUGSM(("debuggta: Allocating block at 0x%08lx\n", uStor)); uStor->state = TextFree; sizeIndex = MAXINDEX; } assert(sizeIndex >= 0 && sizeIndex <= MAXINDEX); uStor->queueIndex = sizeIndex; /* This is now a smaller block */ return uStor; } /* Routine to return an index into the TwoTable for a given size (round up to next power of two) */ int getSizeIndex(size_t size) { size_t testSize; int sizeIndex; testSize = MAXTWO; sizeIndex = MAXINDEX; /* Theory here is to hunt for first significant bit. Then if there is more to the word, bump back * to previous queue size. Note that in the following loop, the sizeIndex can go negative if the * value of size is less than MINTWO (which is queue index 0) but since we guarantee there will be a * remainder, we will increment back to 0. */ while (0 == (testSize & size)) { --sizeIndex; /* Try next smaller queue */ if (0 <= sizeIndex) /* .. if there is a queue */ testSize >>= 1; else /* Else leave loop with last valid testSize */ break; } if (0 != (size & (testSize - 1))) /* Is there a remainder? */ ++sizeIndex; /* .. if yes, round up a size */ return sizeIndex; } /* Obtain free storage of the given size */ void *gtm_text_alloc(size_t size) { unsigned char *retVal; textElem *uStor, *qHdr; size_t tSize; int sizeIndex, hdrSize; boolean_t reentered; /* Note that this if is also structured for maximum fallthru. The else will * be near the end of this entry point. */ if (gtaSmInitialized) { hdrSize = OFFSETOF(textElem, userStorage); /* Size of textElem header */ GTM64_ONLY(if (MAXUINT4 < (size + hdrSize)) assertpro(FALSE)); /* Only deal with < 4GB requests */ NON_GTM64_ONLY(if ((size + hdrSize) < size) assertpro(FALSE)); /* Check for wrap with 32 bit platforms */ assert(hdrSize < MINTWO); fast_lock_count++; ++gtaSmDepth; /* Nesting depth of memory calls */ reentered = (1 < gtaSmDepth); if (reentered) { --gtaSmDepth; assert(FALSE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MEMORYRECURSIVE); } INCR_CNTR(totalAllocs); if (0 != size) { tSize = size + hdrSize; /* Add in header size */ if (MAXTWO >= tSize) { /* Use our memory manager for smaller pieces */ sizeIndex = getSizeIndex(tSize); /* Get index to size we need */ assert(0 <= sizeIndex && MAXINDEX >= sizeIndex); GET_QUEUED_ELEMENT(sizeIndex, uStor, qHdr); tSize = TwoTable[sizeIndex]; uStor->realLen = (unsigned int)tSize; } else { /* Use regular mmap to obtain the piece */ TEXT_ALLOC(tSize, uStor); INCR_SUM(totalRallocGta, tSize); uStor->queueIndex = REAL_ALLOC; uStor->realLen = (unsigned int)tSize; sizeIndex = MAXINDEX + 1; } INCR_SUM(totalUsedGta, tSize); INCR_SUM(totalAllocGta, tSize); INCR_CNTR(allocCnt[sizeIndex]); uStor->state = TextAllocated; retVal = &uStor->userStorage.userStart; /* Assert we have an appropriate boundary */ assert(((long)retVal & (long)IA64_ONLY(-16)NON_IA64_ONLY(-8)) == (long)retVal); TRACE_TXTALLOC(retVal, tSize); } else /* size was 0 */ retVal = &NullStruct.nullStr[0]; --gtaSmDepth; --fast_lock_count; return retVal; } else /* Storage mgmt has not been initialized */ { gtaSmInit(); /* Reinvoke gtm_text_alloc now that we are initialized */ return (void *)gtm_text_alloc(size); } } /* Release the free storage at the given address */ void gtm_text_free(void *addr) { textElem *uStor, *buddyElem; int sizeIndex, hdrSize, saveIndex; size_t allocSize; if (process_exiting) /* If we are exiting, don't bother with frees. Process destruction can do it */ return; if (!gtaSmInitialized) /* Storage must be init'd before can free anything */ assertpro(FALSE); ++fast_lock_count; ++gtaSmDepth; /* Recursion indicator */ if (1 < gtaSmDepth) { --gtaSmDepth; assert(FALSE); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MEMORYRECURSIVE); } INCR_CNTR(totalFrees); if ((unsigned char *)addr != &NullStruct.nullStr[0]) { hdrSize = OFFSETOF(textElem, userStorage); uStor = (textElem *)((unsigned long)addr - hdrSize); /* Backup ptr to element header */ assert(TextAllocated == uStor->state); allocSize = uStor->realLen; sizeIndex = uStor->queueIndex; DECR_SUM(totalUsedGta, uStor->realLen); if (sizeIndex >= 0) { /* We can put the storage back on one of our simple queues */ assert(0 == ((unsigned long)uStor & (TwoTable[sizeIndex] - 1))); /* Verify alignment */ assert(0 <= sizeIndex && MAXINDEX >= sizeIndex); assert(uStor->realLen == TwoTable[sizeIndex]); uStor->state = TextFree; INCR_CNTR(freeCnt[sizeIndex]); DECR_SUM(totalAllocGta, TwoTable[sizeIndex]); /* First, if there are larger queues than this one, see if it has a buddy that it can combine with */ while (sizeIndex < MAXINDEX) { buddyElem = (textElem *)((unsigned long)uStor ^ TwoTable[sizeIndex]);/* Address of buddy */ assert(0 == ((unsigned long)buddyElem & (TwoTable[sizeIndex] - 1)));/* Verify alignment */ assert(TextAllocated == buddyElem->state || TextFree == buddyElem->state); assert(0 <= buddyElem->queueIndex && buddyElem->queueIndex <= sizeIndex); if (TextAllocated == buddyElem->state || buddyElem->queueIndex != sizeIndex) /* All possible combines done */ break; /* Remove buddy from its queue and make a larger element for a larger queue */ DEQUEUE_STOR_ELEM(buddyElem); if (buddyElem < uStor) /* Pick lower address buddy for top of new bigger block */ uStor = buddyElem; ++sizeIndex; assert(0 <= sizeIndex && MAXINDEX >= sizeIndex); INCR_CNTR(elemCombines[sizeIndex]); uStor->queueIndex = sizeIndex; } ENQUEUE_STOR_ELEM(sizeIndex, uStor); } else { assert(REAL_ALLOC == sizeIndex); /* Better be a real alloc type block */ INCR_CNTR(freeCnt[MAXINDEX + 1]); /* Count free of malloc */ TEXT_FREE(uStor, allocSize); DECR_SUM(totalRallocGta, allocSize); DECR_SUM(totalAllocGta, allocSize); } TRACE_TXTFREE(addr, allocSize); } --gtaSmDepth; --fast_lock_count; } #endif /* not __MVS__ */ /* Routine to print the end-of-process info -- either allocation statistics or malloc trace dump. * Note that the use of FPRINTF here instead of util_out_print is historical. The output was at one * time going to stdout and util_out_print goes to stderr. If necessary or desired, these could easily * be changed to use util_out_print instead of FPRINTF */ void printAllocInfo(void) { #ifndef STATIC_ANALYSIS textElem *eHdr, *uStor; int i; if (0 == totalAllocs) return; /* Nothing to report -- likely a utility that doesn't use mmap */ FPRINTF(stderr, "\nMmap small storage performance:\n"); FPRINTF(stderr, "Total allocs: %d, total frees: %d, total ralloc bytes: %ld, max ralloc bytes: %ld\n", totalAllocs, totalFrees, totalRallocGta, rAllocMax); FPRINTF(stderr, "Total (currently) allocated (includes overhead): %ld, Total (currently) used (no overhead): %ld\n", totalAllocGta, totalUsedGta); FPRINTF(stderr, "\nQueueSize Allocs Frees Splits Combines CurCnt MaxCnt\n"); FPRINTF(stderr, " Free Free\n"); FPRINTF(stderr, "---------------------------------------------------------------------\n"); { for (i = 0; i <= MAXINDEX + 1; ++i) { assert((ARRAYSIZE(TwoTable) > i) && (ARRAYSIZE(allocCnt) > i) && (ARRAYSIZE(freeCnt) > i) && (ARRAYSIZE(elemSplits) > i) && (ARRAYSIZE(elemCombines) > i) && (ARRAYSIZE(freeElemCnt) > i) && (ARRAYSIZE(freeElemMax) > i)); /* For SCI */ FPRINTF(stderr, "%9d %9d %9d %9d %9d %9d %9d\n", TwoTable[i], allocCnt[i], freeCnt[i], elemSplits[i], elemCombines[i], freeElemCnt[i], freeElemMax[i]); } } #endif } #endif /* COMP_GTA */ fis-gtm-V7.0-005/sr_unix/make_cimode.c0000644000032200000250000000306014342376334016445 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2003 Sanchez Computer Associates, 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 #include "gtmci.h" #include "make_mode.h" /* The code created and returned by make_cimode() is executed in the frame GTM$CI at level 1 of * every nested call-in environment. For every M routine being called-in from C, GTM$CI code * will setup argument registers/stack and executes the M routine. When the M routine returns * from its final QUIT, GTM$CI returns to gtm_ci(). make_cimode generates machine equivalents * for the following operations in that order: * * CALL ci_restart :setup register/stack arguments from 'param_list' and transfer control * to op_extcall/op_extexfun which return only after the M routine finishes and QUITs. * CALL ci_ret_code :transfer control from the M routine back to C (gtm_ci). Never returns. * CALL opp_ret :an implicit QUIT although it is never executed. * * Before GTM$CI executes, it is assumed that the global 'param_list' has been populated with * argument/return mval *. */ rhdtyp *make_cimode(void) { static rhdtyp *base_address = NULL; if (NULL == base_address) base_address = make_mode(CI_MODE); return base_address; } fis-gtm-V7.0-005/sr_unix/gtm_trigger.c0000644000032200000250000013004214342376334016523 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2022 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 #include "cmd_qlf.h" #include "compiler.h" #include "error.h" #include #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" + 1)); 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 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_\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 */ fis-gtm-V7.0-005/sr_unix/make_dmode.c0000644000032200000250000000107514342376334016301 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2003 Sanchez Computer Associates, 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 #include "make_mode.h" rhdtyp *make_dmode (void) { return make_mode(DM_MODE); } fis-gtm-V7.0-005/sr_unix/gtm_unlink_all.c0000644000032200000250000001325514342376334017216 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2015 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 "dollar_zlevel.h" #include "error_trap.h" #include "golevel.h" #include "cache.h" #include "cmd_qlf.h" #include "hashtab.h" #include "hashtab_objcode.h" #include "hashtab_mname.h" #include #include "stack_frame.h" #include "mprof.h" #include "gtm_unlink_all.h" #include "zbreak.h" #include "gtm_text_alloc.h" #include "parse_file.h" #include "zro_shlibs.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsbml.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "srcline.h" #include "gtmci.h" /* for GTM_CIMOD */ #include "dm_setup.h" /* for GTM_DMOD */ #include "urx.h" #include "stringpool.h" #include "zr_unlink_rtn.h" #ifdef GTM_TRIGGER #include "gv_trigger.h" #include "gtm_trigger.h" #include "gv_trigger_protos.h" #endif GBLREF boolean_t is_tracing_on; GBLREF gv_key *gv_currkey; GBLREF hash_table_objcode cache_table; GBLREF int dollar_truth; GBLREF int indir_cache_mem_size; GBLREF rtn_tabent *rtn_names, *rtn_names_end, *rtn_names_top, *rtn_fst_table; GBLREF stack_frame *frame_pointer; GBLREF gv_namehead *gv_target_list; /* Routine to do the following: * * 1. Stop M-Profiling. * 2. Unwind the M stack back to level 1 * 3. (re)Initialize $ECODE, $REFERENCE and $TEST. * 4. Remove all triggers. This includes not only the trigger routines but the trigger definitions and * linkages in the gvt_trigger struct anchored off of the gv_target. * 5. Unlink all M routines. This includes getting rid of their linkage entries, breakpoints, * and $TEXT() caches and re-initializing the routine table. * 6. Empty the indirect cache. * 7. Close the shared libraries associated with M programs and reopen them after the unroll if any * are present in $ZROUTINES. * * Currently called from op_zgoto() but could be called from elsewhere if needed in the future. * * Note this routine removes all loaded programs but the intial base-frame on the stack has the address of whatever * the first program is (GTM$DMOD in a direct mode situation or the starting program in a -run invocation). It is up * to the caller to set the base frame with this address so the stack has no unknown references in it. */ void gtm_unlink_all(void) { rtn_tabent *rtab; rhdtyp *rtnhdr, *rhdr, *next_rhdr; ht_ent_mname *tabent_mname; textElem *telem; ht_ent_objcode *tabent_obj, *topent; cache_entry *csp; mname_entry key; routine_source *src_tbl; gv_namehead *gvt; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Step 1: Stop M-Profiling */ if (is_tracing_on) turn_tracing_off(NULL); /* Step 2: Unwind M stack back to level 0 */ GOLEVEL(0, TRUE); assert(0 == dollar_zlevel()); /* Step 3: re-Initialize $ECODE, $REFERENCE, and $TEST */ NULLIFY_DOLLAR_ECODE; /* Clears $ECODE and results returned for $STACK */ if (NULL != gv_currkey) { /* Clears $REFERENCE */ gv_currkey->end = 0; gv_currkey->base[0] = KEY_DELIMITER; } dollar_truth = FALSE; /* aka $TEST */ /* Step 4: Remove all triggers */ # ifdef GTM_TRIGGER for (gvt = gv_target_list; gvt; gvt = gvt->next_gvnh) gvtr_free(gvt); # endif /* Step 5: Unlink all routines, remove $TEXT cache and remove breakpoints. Note that for the purposes of this section, * there is no difference between normal routines and trigger routines. Both are being removed completely so the code * below is a hodgepodge of code from zlput_rname and gtm_trigger_cleanup(). Note process in reverse order so we can * move rtn_names_end up leaving processed entries (whose keys no longer work) off the end of the table without moving * anything. This is necessary because removing ZBREAK points can call find_rtn_hdr so this table needs to remain in * a usable state while we are doing this. */ for (rtab = rtn_names_end; rtab > rtn_names; rtab--, rtn_names_end = rtab) { /* [0] is not used (for some reason) */ rtnhdr = rtab->rt_adr; if ((0 == strcmp(rtnhdr->routine_name.addr, GTM_DMOD)) || (0 == strcmp(rtnhdr->routine_name.addr, GTM_CIMOD))) { /* If the routine is GTM$DMOD or GTM$CI, it is allocated in one chunk by make_*mode(). Release it in * one chunk too. */ GTM_TEXT_FREE(rtnhdr); } else zr_unlink_rtn(rtnhdr, TRUE); } /* All programs have been removed. If this is the "first" table allocated which cannot be removed, just reinitialize * the table and we're done. If a new table, release it, recover the first table, initialize and we're done. */ if (rtn_names != rtn_fst_table) { free(rtn_names); rtn_names = rtn_fst_table; assert(NULL != rtn_fst_table); } memset(rtn_names, 0, SIZEOF(rtn_tabent)); rtn_names_end = rtn_names_top = rtn_names; /* Step 5: Empty the indirect cache */ for (tabent_obj = cache_table.base, topent = cache_table.top; tabent_obj < topent; tabent_obj++) { /* Run through the hashtable getting rid of all the entries. Not bothering with the cleanups * like are done in cache_table_rebuild since we are going to re-init the table. */ if (HTENT_VALID_OBJCODE(tabent_obj, cache_entry, csp)) { GTM_TEXT_FREE(csp); } } reinitialize_hashtab_objcode(&cache_table); /* Completely re-initialize the hash table */ indir_cache_mem_size = 0; /* Step 6: Close all M code shared libraries */ zro_shlibs_unlink_all(); } fis-gtm-V7.0-005/sr_port/0000755000032200000250000000000014342376334014046 5ustar librarygtcfis-gtm-V7.0-005/sr_port/tp_hist.c0000644000032200000250000006725614342376330015700 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdscc.h" #include "filestruct.h" #include "copy.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "hashtab_int8.h" #include "tp.h" #include "longset.h" /* needed for cws_insert.h */ #include "cws_insert.h" #include "memcoherency.h" #include "wbox_test_init.h" #ifdef UNIX #include "repl_msg.h" /* needed for gtmsource.h */ #include "gtmsource.h" /* needed for SYNC_ONLN_RLBK_CYCLES */ #endif #define REPOSITION_PTR(ptr, type, delta, begin, end) \ { \ assert((sm_uc_ptr_t)(ptr) >= (sm_uc_ptr_t)(begin)); \ assert((sm_uc_ptr_t)(ptr) <= (sm_uc_ptr_t)(end)); \ (ptr) = (type *)((sm_uc_ptr_t)(ptr) + delta); \ } #define REPOSITION_PTR_IF_NOT_NULL(ptr, type, delta, begin, end) \ { \ if (ptr) \ REPOSITION_PTR(ptr, type, delta, begin, end); \ } #define REPOSITION_GVNH_HIST(GVNH, CSA, DELTA, SI) \ { \ srch_blk_status *t1; \ \ if (GVNH->gd_csa == CSA) \ { /* reposition pointers only if global corresponds to current database file */ \ for (t1 = GVNH->hist.h; t1->blk_num; t1++) \ REPOSITION_PTR_IF_NOT_NULL(t1->first_tp_srch_status, struct srch_blk_status_struct, \ DELTA, SI->first_tp_hist, SI->last_tp_hist); \ } else \ assert((GVNH != gv_target) && (GVNH != cs_addrs->dir_tree)); \ } GBLREF gv_namehead *gv_target, *gvt_tp_list; GBLREF sgm_info *sgm_info_ptr; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF trans_num local_tn; /* transaction number for THIS PROCESS */ GBLREF int4 n_pvtmods, n_blkmods; GBLREF unsigned int t_tries; GBLREF uint4 t_err; GBLREF boolean_t is_updproc; GBLREF boolean_t mupip_jnl_recover; GBLREF boolean_t gvdupsetnoop; /* if TRUE, duplicate SETs update journal but not database (except for curr_tn++) */ GBLREF uint4 process_id; error_def(ERR_TRANS2BIG); error_def(ERR_GVKILLFAIL); error_def(ERR_GVPUTFAIL); void gds_tp_hist_moved(sgm_info *si, srch_hist *hist1); enum cdb_sc tp_hist(srch_hist *hist1) { int hist_index; srch_hist *hist; off_chain chain; srch_blk_status *t1, *t2; block_id blk; srch_blk_status *local_hash_entry; enum cdb_sc status = cdb_sc_normal; boolean_t is_mm, store_history, was_crit; sgm_info *si; ht_ent_int8 *tabent, *lookup_tabent; cw_set_element *cse; cache_rec_ptr_t cr; sgmnt_addrs *csa; node_local_ptr_t cnl; # ifdef DEBUG int4 wc_blocked; boolean_t ready2signal_gvundef_lcl; # endif gv_namehead *gvt; /* store local copy of global "gv_target" for faster access */ DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DEBUG_ONLY( /* Store global variable ready2signal_gvundef in a local variable and reset the global right away to ensure that * the global value does not incorrectly get carried over to the next call of "t_end". */ ready2signal_gvundef_lcl = TREF(ready2signal_gvundef); TREF(ready2signal_gvundef) = FALSE; ) is_mm = (dba_mm == cs_addrs->hdr->acc_meth); assert((NULL != sgm_info_ptr) && (cs_addrs->sgm_info_ptr == sgm_info_ptr)); si = sgm_info_ptr; csa = si->tp_csa; cnl = csa->nl; gvt = gv_target; store_history = (!gvt->noisolation || ERR_GVKILLFAIL == t_err || ERR_GVPUTFAIL == t_err); assert(hist1 != &gvt->hist); /* Ideally, store_history should be computed separately for blk_targets of gv_target->hist and hist1, * i.e. within the outer for loop below. But this is not needed since if t_err is ERR_GVPUTFAIL, * store_history is TRUE both for gv_target->hist and hist1 (which is cs_addrs->dir_tree if non-NULL) * (in the latter case, TRUE because cs_addrs->dir_tree always has NOISOLATION turned off) and if not, * we should have the same blk_target for both the histories and hence the same value for * !blk_target->noisolation and hence the same value for store_history. We assert this is the case below. */ assert(ERR_GVPUTFAIL == t_err && (NULL == hist1 || hist1 == &cs_addrs->dir_tree->hist && !cs_addrs->dir_tree->noisolation) || !hist1 || hist1->h[0].blk_target == gvt); /* We are going to reference fields from shared memory including cr->in_tend and t1->buffaddr->tn in that order (as part * of the TP_IS_CDB_SC_BLKMOD macro below). To ensure we read an uptodate value of cr->in_tend, we do a read memory * barrier here. Note that to avoid out-of-order reads, the read memory barrier needs to be done AFTER the reference * that sets t1->tn (in a caller function usually gvcst_search.c) and the reference that accesses t1->buffaddr->tn * (in this function below as part of the TP_IS_CDB_SC_BLKMOD macro). Therefore it is ok to do this at the start of * this function. If cr->in_tend changes concurrently AFTER we do the read memory barrier and we dont see the change * in the cr->in_tend reference below, then, as long as the values we see pass the cdb_sc_blkmod check, it is ok to * consider this transaction as good because whatever change happened concurrently occurred only after we reached this * part of the code which means that while the caller functions gvcst_put.c,gvcst_search.c etc. were traversing the * tree and accessing the buffers, they were guaranteed to have seen a consistent state of the database. */ SHM_READ_MEMORY_BARRIER; for (hist = &gvt->hist; hist != NULL && cdb_sc_normal == status; hist = (hist == &gvt->hist) ? hist1 : NULL) { /* this loop execute once or twice: 1st for gv_target and then for hist1, if any */ if (TREF(tprestart_syslog_delta)) n_blkmods = n_pvtmods = 0; for (t1 = hist->h; HIST_TERMINATOR != (blk = t1->blk_num); t1++) { if (si->start_tn > t1->tn) si->start_tn = t1->tn; /* Since this validation is done out of crit, it is crucial to perform the blkmod check * ahead of the cycle check. There is a subtle chance that, if we do the check the other * way round, the block may pass the cycle check (although it still has been modified) * and just before the blkmod check, the buffer gets reused for another block whose block * transaction number is less than the history's tn. The same care should be taken in * all the other out-of-crit places which are (as of 1/15/2000) * t_qread, gvcst_search, gvcst_put --- nars. * * Note that the correctness of this out-of-crit-validation mechanism also relies on the * fact that the block-transaction-number is modified before the contents of the block * in gvcst_blk_build and other places in bg_update and mm_update. This is needed because * our decision here is based on looking at the transaction number in the block-header * rather than the BT table (done in tp_tend and t_end). If the block-tn got updated * after the block contents get modified, then we may incorrectly decide (looking at the * tn) that the block hasn't changed when actually its contents have. */ chain = *(off_chain *)&blk; if (!chain.flag) { /* We need to ensure the shared copy hasn't changed since the beginning of the * transaction since not checking that can cause at least false UNDEFs. * e.g. Say earlier in this TP transaction we had gone down * a level-1 root-block 4 (say) to reach leaf-level block 10 (say) to get at a * particular key (^x). Let reorg have swapped the contents of block 10 with the * block 20. Before the start of this particular mini-action in TP, block 4 would * lead to block 20 (for the same key ^x). If block 20 happens to be in the cw_set * of this region for this transaction, we would be using that instead of the shared * memory copy. Now, the private copy of block 20 would not contain the key ^x. But * we shouldn't incorrectly conclude that ^x is not present in the database. For this * we should check whether the shared memory copy of block 20 is still not changed. */ assert(is_mm || t1->cr || t1->first_tp_srch_status); ASSERT_IS_WITHIN_TP_HIST_ARRAY_BOUNDS(t1->first_tp_srch_status, si); t2 = t1->first_tp_srch_status ? t1->first_tp_srch_status : t1; cr = t2->cr; /* Assert that cr->in_tend is never equal to our process_id since at this point we should * never have locked a buffer for phase2 commit. The only exception is if the previous * transaction by this process had a commit-time error and secshr_db_clnup finished the * transaction and had set cnl->wc_blocked to TRUE. It is possible in that case no process * has yet done a cache-recovery by the time we come to tp_hist as part of the next transaction * in which case cr->in_tend could still be pointing to our process_id. Note that * cnl->wc_blocked could be changing concurrently so need to note it down in a local * variable BEFORE checking the value of cr->in_tend. */ DEBUG_ONLY(wc_blocked = cs_addrs->nl->wc_blocked;) assert((NULL == cr) || (process_id != cr->in_tend) || wc_blocked); if (TP_IS_CDB_SC_BLKMOD(cr, t2)) { cse = t1->cse; /* Check if the cse already has a recompute list (i.e. NOISOLATION turned ON) * If so no need to restart the transaction even though the block changed. * We will detect this change in tp_tend and recompute the update array anyways. */ if ((NULL == cse) || !cse->recompute_list_head || cse->write_type) { assert((CDB_STAGNATE > t_tries) || (gtm_white_box_test_case_enabled && (WBTEST_TP_HIST_CDB_SC_BLKMOD == gtm_white_box_test_case_number))); if (TREF(tprestart_syslog_delta)) { n_blkmods++; if (t2->cse || t1->cse) n_pvtmods++; if (1 != n_blkmods) continue; } TP_TRACE_HIST_MOD(t2->blk_num, t2->blk_target, tp_blkmod_tp_hist, cs_addrs->hdr, t2->tn, ((blk_hdr_ptr_t)t2->buffaddr)->tn, t2->level); status = cdb_sc_blkmod; BREAK_IN_PRO__CONTINUE_IN_DBG; } else { /* In case of twinning, it is possible we (gvcst_search) could have been looking at * a newly formed twin buffer while setting the first_rec portion of the clue. * It is possible that the twin buffer was obtained by a db_csh_getn and that * it does not yet contain the records of the block we were interested in. This * means we could have been looking at a completely different block (the one that * was previously sitting in that global buffer) while constructing the first_rec * portion of the gv_target's clue which means first_rec is no longer reliable. * Hence reset it in this case to be safe. Note that tp_hist need not detect * all cases of blkmods accurately since this check is done outside of crit. Any * cases that are not detected here will get caught by tp_tend (which will invoke * "recompute_upd_array" function) so a similar reset of the first_rec needs to * happen there too. * * Even if there is no twinning, it is possible that if gvcst_search is reading * from a buffer that is being modified concurrently, it could pick up the first * half of first_rec from the pre-update copy of the block and the second half of * first_rec from the post-update copy of the block. Depending on the actual byte * sequences, it is possible that this mixed first_rec is LESSER than the correct * value. This could therefore cause DBKEYORD integ errors in case this is used * for the next transaction. It is therefore necessary to invalidate the first_rec * in this case (C9J07-003162). */ if (gvt->clue.end) GVT_CLUE_INVALIDATE_FIRST_REC(gvt); # ifdef DEBUG /* Assert that this is a leaf level block of a global with NOISOLATION * and that this is a SET operation (not a GET or KILL). The only exception * we know of to this is that the global corresponding to the incoming history * (t1) could have NOISOLATION turned OFF but yet contain a block which is * already part of the first_tp_hist structure (t2) for a different global that * has NOISOLATION turned ON. This is possible if MUPIP REORG swapped the block * AFTER the tp_hist was done for the NOISOLATION-ON-global but BEFORE the * tp_hist (the current one) for the NOISOLATION-OFF-global. This is definitely * a restartable situation that will be detected in tp_tend. Even though * we know t1->blk_target != t2->blk_target in this case, we choose to defer * the restart because we suspect this case is very infrequent that it is better * to avoid the performance hit of a check in this frequently used code. */ assert((ERR_GVPUTFAIL == t_err) && (0 == t1->level) && (t1->blk_target->noisolation || t2->blk_target->noisolation)); if (t1->blk_target != t2->blk_target) TREF(donot_commit) |= DONOTCOMMIT_TPHIST_BLKTARGET_MISMATCH; # endif } } /* Although t1->first_tp_srch_status (i.e. t2) is used for doing blkmod check, * we need to use BOTH t1 and t1->first_tp_srch_status to do the cdb_sc_lostcr check. * * Need to use t1 for the cdb_sc_lostcr check * ------------------------------------------- * We allow for recycled crs within a transaction but not within a mini-action (one that * ends up calling tp_hist e.g. SET/KILL/GET etc.). An example of how recycled crs within * a mini-action are possible is the M-kill. It does gvcst_search twice one for the left * path and one for the right path that identifies the subtree of blocks to be killed. * It is possible that the same block gets included in both the paths but with different * state of the "cr" implying a block recycle within the KILL and hence the need for a restart. * * Need to also use t1->first_tp_srch_status (i.e. t2) for the cdb_sc_lostcr check * -------------------------------------------------------------------------------- * tp_tend does not validate those blocks that have already been built privately * (i.e. cse->new_buff is non-NULL). Instead it relies on tp_hist (called for every mini-action) * to have done the validation right after the block was built. Hence we need to validate * t1->first_tp_srch_status (right now) against the current state of the cache to ensure whatever * shared memory buffer we used to privately build the block has not been recycled since. */ if ((t1 != t2) && t1->cr) { assert(((sm_long_t)GDS_REL2ABS(t1->cr->buffaddr) == (sm_long_t)t1->buffaddr) || (0 != cnl->onln_rlbk_pid) || MISMATCH_ROOT_CYCLES(csa, cnl)); if (t1->cycle != t1->cr->cycle) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_lostcr; break; } t1->cr->refer = TRUE; } if (cr) { assert(((sm_long_t)GDS_REL2ABS(cr->buffaddr) == (sm_long_t)t2->buffaddr) || (0 != cnl->onln_rlbk_pid) || MISMATCH_ROOT_CYCLES(csa, cnl)); if (t2->cycle != cr->cycle) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_lostcr; break; } cr->refer = TRUE; } } /* Note that blocks created within the transaction (chain.flag != 0) are NOT counted * in the read-set of this transaction. They are still counted against the write-set. * si->num_of_blks is the count of the read-set (the si->blks_in_use has the actual block numbers) * while si->cw_set_depth is the count of the write-set (si->first_cw_set has the block numbers). * We have a limit on the maximum number of buffers that a TP transaction can have both in * its read-set (64K buffers) and write-set (1/2 the number of global buffers). The former * limit check is done here. The latter is done in tp_cw_list.c. * * Since created blocks are not added into the hashtable, we expect the "first_tp_srch_status" * members of those block's srch_blk_status to be NULL. That is asserted below. * * For an existing block, we copy its search history into si->last_tp_hist. * For a created block, we dont since it doesn't need to be validated at commit. */ assert(!chain.flag || !t1->first_tp_srch_status); if (store_history && !chain.flag) { assert(si->cw_set_depth || !t1->cse); local_hash_entry = t1->first_tp_srch_status; DEBUG_ONLY(lookup_tabent = lookup_hashtab_int8(si->blks_in_use, (ublock_id *)&blk)); ASSERT_IS_WITHIN_TP_HIST_ARRAY_BOUNDS(local_hash_entry, si); if ((NULL == local_hash_entry) && add_hashtab_int8(si->blks_in_use, (gtm_uint8 *)&blk, (void *)(si->last_tp_hist), &tabent)) { /* not a duplicate block */ assert(NULL == lookup_tabent); if (++si->num_of_blks > si->tp_hist_size) { /* catch the case where MUPIP recover or update process gets into this situation */ assert(!mupip_jnl_recover && !is_updproc); delete_hashtab_int8(si->blks_in_use,(ublock_id *)&blk); si->num_of_blks--; assert(si->num_of_blks == si->tp_hist_size); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_TRANS2BIG, 2, REG_LEN_STR(gv_cur_region)); } /* Either history has a clue or not. * If yes, then it could have been constructed in an earlier * local_tn or the current local_tn. * If an earlier local_tn, then the logic in gvcst_search would * have nullified first_tp_srch_status members. * If the current local_tn, then this block should have been * added in the hashtable already and we shouldn't be * in this part of the code. * If no, then we t_qread all the blocks in the search path. The * blocks may have been read already in this local_tn or not. * If already read in this local_tn, then this block should have * been added in the hashtable already and we shouldn't be * in this part of the code. * If not already read in this local_tn, then t_qread of this block * should have returned a NULL for first_tp_srch_status. * In either case, the first_tp_srch_status member should be NULL. */ assert(NULL == t1->first_tp_srch_status); /* If history array is full, allocate more */ if (si->last_tp_hist - si->first_tp_hist == si->cur_tp_hist_size) gds_tp_hist_moved(si, hist1); memcpy(si->last_tp_hist, t1, SIZEOF(srch_blk_status)); t1->first_tp_srch_status = si->last_tp_hist; si->last_tp_hist++; /* Ensure that we are doing an M-kill if for a non-isolated global we end up * adding to the history a leaf-level block which has no corresponding cse. * To be more specific we are doing an M-kill that freed up the data block from * the B-tree, but that checking involves more coding. * The only exception to this is if we are in an M-set and the set was a duplicate * set. Since we do not have that information (gvcst_put the caller has it), * we do a less stringent check and ensure the optimization ("gvdupsetnoop" * global variable) is turned on at the very least. */ assert(!t1->blk_target->noisolation || t1->level || t1->cse || ((ERR_GVKILLFAIL == t_err) || ((ERR_GVPUTFAIL == t_err) && gvdupsetnoop))); } else { /* While it is almost always true that local_hash_entry is non-NULL here, there * is a very rare case when it can be NULL. That is when two histories are passed * each containing the same block. In that case, when the loop is executed * for the first history, the block would have been added into the hashtable. * During the second history's loop, the first_tp_srch_status member in * the srch_blk_status for the second history will be NULL although the * block is already in the hashtable. But that can be only because of a * call from gvcst_kill() or one of the $order,$data,$query routines. Among * these, gvcst_kill() is the only one that can create a condition which * will require us to update the "cse" field in the first_tp_hist array * (i.e. the block may have a null "cse" field in the first_tp_hist array * when it would have been modified as part of the kill in which case we * need to modify the first_tp_hist array appropriately). In that * rare case (i.e. local_hash_entry is NULL), we don't need to change * anything in the first_tp_hist array anyway since we would have updated * it during the previous for-loop for the first history. Hence the check * for if (local_hash_entry) below. */ /* Ensure that "first_tp_srch_status" reflects what is in the hash-table. * The only exception is when two histories are passed (explained above) */ assert(local_hash_entry && lookup_tabent && (local_hash_entry == lookup_tabent->value) || !local_hash_entry && (hist == hist1)); /* Ensure we don't miss updating "cse" */ assert((NULL != local_hash_entry) || ((srch_blk_status *)(tabent->value))->cse|| !t1->cse); assert(!local_hash_entry || !local_hash_entry->cse || !t1->cse || (t1->cse == local_hash_entry->cse) || (t1->cse->low_tlevel == local_hash_entry->cse)); if (local_hash_entry && t1->cse) { assert(is_mm || local_hash_entry->cse || ((t1->first_tp_srch_status == local_hash_entry) && (t1->cycle == local_hash_entry->cycle) && (t1->cr == local_hash_entry->cr) && (t1->buffaddr == local_hash_entry->buffaddr))); local_hash_entry->cse = t1->cse; } } } t1->cse = NULL; } # ifdef DEBUG /* Now that this history has been successfully validated, check that the current history transaction * numbers can never be older than their corresponding first_tp_srch_status values. */ if (cdb_sc_normal == status) { for (t1 = hist->h; HIST_TERMINATOR != (blk = t1->blk_num); t1++) { t2 = t1->first_tp_srch_status ? t1->first_tp_srch_status : t1; assert((t2->tn <= t1->tn) || (0 != cnl->onln_rlbk_pid) || MISMATCH_ROOT_CYCLES(csa, cnl)); /* Take this time to also check that gv_targets match between t1 and t2. * If they dont, the restart should be detected in tp_tend. * Set the flag donot_commit to catch the case this does not restart. */ if (t2->blk_target != t1->blk_target) TREF(donot_commit) |= DONOTCOMMIT_TPHIST_BLKTARGET_MISMATCH; } } # endif } if (MISMATCH_ROOT_CYCLES(csa, cnl)) { /* We want to sync the online rollback cycles ONLY under crit. So, grab_crit and sync the * cycles. While grab_crit_immediate would have been a better choice, the reason we don't use * grab_crit_immediate here is because if we don't sync cycles because we don't hold crit but * do invoke t_retry which increments $zonlnrlbk and on the subsequent retry we remain * unlucky in getting crit, we will end up incrementing $zonlnrlbk more than once for a * single online rollback event. In the worst case we will have $zonlnrlbk=3 which from * the user perspective is incorrect. So, sync the cycles the first time we detect the * online rollback */ assert(!csa->hold_onto_crit); was_crit = csa->now_crit; if (!was_crit) grab_crit(gv_cur_region, WS_51); status = cdb_sc_gvtrootmod2; if (MISMATCH_ONLN_RLBK_CYCLES(csa, cnl)) { assert(!mupip_jnl_recover); status = ONLN_RLBK_STATUS(csa, cnl); SYNC_ONLN_RLBK_CYCLES; SYNC_ROOT_CYCLES(NULL); } else SYNC_ROOT_CYCLES(csa); if (!was_crit) rel_crit(gv_cur_region); } if (si->start_tn <= cnl->last_wcs_recover_tn) { /* Note that it is possible that gvt->clue.end is non-zero even in the final retry (e.g. if we encounter * this gvt for the first time in the final retry). If so, t1->tn would be set (by "gvcst_search" done in * the caller) to the tn when the clue got set which could be stale compared to cnl->last_wcs_recover_tn * (if this gvt was never accessed after the most recent "wcs_recover"). In that case, t1->tn would get * copied over to si->start_tn above and we will reach here. We should restart in this situation * (i.e. cdb_sc_wcs_recover is a valid final retry restart code in TP). */ status = cdb_sc_wcs_recover; } /* If validation has succeeded, assert that if gtm_gvundef_fatal is non-zero, then we better not signal a GVUNDEF */ assert((cdb_sc_normal != status) || !TREF(gtm_gvundef_fatal) || !ready2signal_gvundef_lcl); ADD_TO_GVT_TP_LIST(gvt, RESET_FIRST_TP_SRCH_STATUS_FALSE); /* updates gvt->read_local_tn & adds gvt to gvt_tp_list * (all only if needed) */ CWS_RESET; return status; } void gds_tp_hist_moved(sgm_info *si, srch_hist *hist1) { gv_namehead *gvnh; ht_ent_int8 *tabent, *topent; sm_long_t delta; srch_blk_status *new_first_tp_hist, *t1; tlevel_info *tli; srch_blk_status *srch_stat; sgmnt_addrs *csa; assert(si->cur_tp_hist_size < si->tp_hist_size); si->cur_tp_hist_size <<= 1; new_first_tp_hist = (srch_blk_status *)malloc(SIZEOF(srch_blk_status) * si->cur_tp_hist_size); memcpy((uchar_ptr_t)new_first_tp_hist, (uchar_ptr_t)si->first_tp_hist, (sm_uc_ptr_t)si->last_tp_hist - (sm_uc_ptr_t)si->first_tp_hist); delta = (sm_long_t)((sm_uc_ptr_t)new_first_tp_hist - (sm_uc_ptr_t)si->first_tp_hist); for (tabent = si->blks_in_use->base, topent = si->blks_in_use->top; tabent < topent; tabent++) { if (HTENT_VALID_INT8(tabent, srch_blk_status, srch_stat)) { REPOSITION_PTR(srch_stat, srch_blk_status, delta, si->first_tp_hist, si->last_tp_hist); tabent->value = (void *)srch_stat; } } for (tli = si->tlvl_info_head; tli; tli = tli->next_tlevel_info) REPOSITION_PTR_IF_NOT_NULL(tli->tlvl_tp_hist_info, srch_blk_status, delta, si->first_tp_hist, si->last_tp_hist); csa = si->tp_csa; /* Adjust histories of gvtargets used in this TP transaction. Note that it is possible "gv_target" or "cs_addrs->dir_tree" * might not yet be part of this list even though they will be at the end of the current invocation of tp_hist. * In this case too gv_target's pointers need to be repositioned. */ for (gvnh = gvt_tp_list; NULL != gvnh; gvnh = gvnh->next_tp_gvnh) { assert(gvnh->read_local_tn == local_tn); REPOSITION_GVNH_HIST(gvnh, csa, delta, si); } if (local_tn != gv_target->read_local_tn) /* Note: gv_target could be "cs_addrs->dir_tree" at this point in some cases */ REPOSITION_GVNH_HIST(gv_target, csa, delta, si); /* cs_addrs->dir_tree (directory tree) history does not need any special repositioning. That is because either * (a) It is the same as gv_target in which case we would have done the repositioning in the previous statement OR * (b) It is different from gv_target in which case there are two subcases. * (1) It was accessed in this TP transaction. If so it would have been in gvt_tp_list and repositioning * would have already happened above. * (2) It was NOT accessed in this TP transaction. If so it will not have any "first_tp_srch_status" fields * that need repositioning either. Any non-zero "first_tp_srch_status" values are left over from * previous transactions and could coincidentally match our current tp_hist range so we can not * even add any asserts to validate this reasoning. */ assert(NULL == hist1 || hist1 != &gv_target->hist); /* ensure that gv_target isn't doubly repositioned */ if ((NULL != hist1) && (hist1 != &cs_addrs->dir_tree->hist)) /* ensure we don't reposition directory tree again */ { for (t1 = hist1->h; t1->blk_num; t1++) REPOSITION_PTR_IF_NOT_NULL(t1->first_tp_srch_status, struct srch_blk_status_struct, delta, si->first_tp_hist, si->last_tp_hist); } free(si->first_tp_hist); si->first_tp_hist = new_first_tp_hist; si->last_tp_hist = (srch_blk_status *)((sm_uc_ptr_t)si->last_tp_hist + delta); } fis-gtm-V7.0-005/sr_port/tp_get_cw.c0000755000032200000250000000325614342376331016173 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "gdsroot.h" #include "gdskill.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" GBLREF sgm_info *sgm_info_ptr; void tp_get_cw (cw_set_element *cs, int depth, cw_set_element **cs1) { cw_set_element *cs_tmp; /* to avoid double dereferencing in the TRAVERSE macro */ # ifdef DEBUG cw_set_element *cs_tmp1; cs_tmp = sgm_info_ptr->first_cw_set; TRAVERSE_TO_LATEST_CSE(cs_tmp); cs_tmp1 = cs; TRAVERSE_TO_LATEST_CSE(cs_tmp1); assert(cs_tmp1 == cs_tmp); # endif assert (depth < sgm_info_ptr->cw_set_depth); cs_tmp = (cw_set_element *)find_element(sgm_info_ptr->cw_set_list, depth); /* Above returns the first cse (least t_level) in the horizontal list. * Traverse the horizontal list to go to the latest - * since the usual transaction depth is not much (on an average 2), this does * not hamper performance so much to necessiate maintaining links to the head * and tail of horizontal list of cw_set_elements */ assert(cs_tmp); TRAVERSE_TO_LATEST_CSE(cs_tmp); *cs1 = cs_tmp; } fis-gtm-V7.0-005/sr_port/tp_cw_list.c0000644000032200000250000000507614342376331016366 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" GBLREF sgm_info *sgm_info_ptr; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF boolean_t is_updproc; GBLREF boolean_t mupip_jnl_recover; error_def(ERR_TRANS2BIG); void tp_cw_list(cw_set_element **cs) { cw_set_element *last_cse, *tempcs, *prev_last; boolean_t is_bg; sgmnt_data_ptr_t csd; /* Don't allow a single transaction to use more cw_set_elements than half of the global buffers or * more than 32K cw_set_elements. This because the "cw_index" field in "off_chain" structure is CW_INDEX_MAX_BITS-bits. */ csd = cs_addrs->hdr; is_bg = (dba_bg == csd->acc_meth); assert(is_bg || (dba_mm == csd->acc_meth)); if (((sgm_info_ptr->cw_set_depth + 2) > (1LL << CW_INDEX_MAX_BITS)) || (is_bg && ((sgm_info_ptr->cw_set_depth + 2) >= (csd->n_bts >> 1)))) { /* catch the case where MUPIP recover or update process gets into this situation */ assert(!mupip_jnl_recover && !is_updproc); RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_TRANS2BIG, 2, REG_LEN_STR(gv_cur_region)); } tempcs = (cw_set_element *)get_new_element(sgm_info_ptr->cw_set_list, 1); /* secshr_db_clnup relies on the cw_set_element (specifically the "mode" field) being initialized to a value * that is not "gds_t_committed". This needs to be done before setting sgm_info_ptr->first_cw_set. */ memset(tempcs, 0, SIZEOF(cw_set_element)); assert(gds_t_committed != tempcs->mode); /* ensure secshr_db_clnup's check will not be affected */ if (sgm_info_ptr->first_cw_set == NULL) sgm_info_ptr->first_cw_set = tempcs; else { last_cse = sgm_info_ptr->last_cw_set; assert(last_cse); for (; last_cse; last_cse = last_cse->high_tlevel) last_cse->next_cw_set = tempcs; } prev_last = sgm_info_ptr->last_cw_set; sgm_info_ptr->last_cw_set = *cs = tempcs; tempcs->prev_cw_set = prev_last; } fis-gtm-V7.0-005/sr_port/tp_frame.h0000755000032200000250000001074114342376331016017 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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. * * * ****************************************************************/ /* Definitions for implicit_flag passed to op_tstart. Combinations are or'd (or added) to form a bit-mask */ #define NORMAL_MCODE_TSTART 0 /* Nothing implicit about it - this is what the assembler opp_tstart() * routines pass. */ #define IMPLICIT_TSTART 1 /* For both trigger and non-trigger related implicit tstarts. Any direct * op_tstart done in C code should at least use this flag. This flag * is inherited by nested levels. */ #define IMPLICIT_TRIGGER_TSTART 2 /* This TP frame was implicitly created for a trigger allowing slightly * different error handling for TP restarts under some conditions. This * flag is inherited by nested levels. */ /* Macro to put a given lv_val on the TP local var restore list for the current tp frame. */ #define TP_SAVE_RESTART_VAR(lv, tf, mnamekey) \ { \ lv_val *var; \ tp_var *restore_ent; \ \ var = lv_getslot(LV_GET_SYMVAL(lv)); \ assert(var); \ restore_ent = (tp_var *)malloc(SIZEOF(tp_var)); \ restore_ent->current_value = (lv); \ restore_ent->save_value = var; \ memcpy(&restore_ent->key, (mnamekey), SIZEOF(mname_entry)); \ restore_ent->var_cloned = FALSE; \ restore_ent->next = (tf)->vars; \ assert(NULL == (lv)->tp_var); \ (lv)->tp_var = restore_ent; \ *var = *(lv); \ LV_CHILD(var) = NULL; /* initialize child to NULL until actual cloning occurs. \ * this is needed so stp_gcol does not DOUBLE count subtree */ \ /* Increment refcnts (due to "restore_ent->save_value") to prevent deletion \ * of "lv" through a "KILL *". Necessary because we need the "lv_val" of "lv" \ * untouched (i.e. not freed and/or reused after a "kill *"). \ */ \ INCR_TREFCNT(lv); \ INCR_CREFCNT(lv); \ assert(1 < (lv)->stats.trefcnt); \ assert(0 < (lv)->stats.crefcnt); \ (tf)->vars = restore_ent; \ } /* tp_var_struct moved to lv_val.h for include heirarchy purposes */ typedef struct tp_frame_struct { unsigned int serial : 1; unsigned int restartable : 1; unsigned int old_locks : 1; unsigned int dlr_t : 1; /* saved/restored only for OUTERMOST tstart */ unsigned int tp_save_all_flg : 1; unsigned int implicit_tstart : 1; /* TRUE if op_tstart was invoked by gvcst_put/gvcst_kill as part of * trigger processing. Field is inherited across nested op_tstarts */ unsigned int cannot_commit : 1; /* TRUE if at least one explicit (i.e. triggering) update in this * transaction ended up dropping back through the trigger boundary * with an unhandled error. In this case, we might have a bunch of * partly completed database work that, if committed, would break * the atomicity of triggers (an update and its triggers should go * either all together or none). It is ok to rollback such partial * transactions. Only commit is disallowed. This field is currently * maintained only on trigger supported platforms. */ unsigned int implicit_trigger : 1; /* The implicit tstart is for a trigger which means some minor * deviations in dealing with TP restarts in some circumstances. */ unsigned int filler : 24; unsigned char *restart_pc; struct stack_frame_struct *fp; struct mv_stent_struct *mvc; struct gv_namehead_struct *orig_gv_target; /* saved/restored only for OUTERMOST tstart */ struct gv_key_struct *orig_key; /* saved/restored only for OUTERMOST tstart */ struct gd_addr_struct *gd_header; /* saved/restored only for OUTERMOST tstart */ struct gd_region_struct *gd_reg; /* saved/restored only for OUTERMOST tstart */ struct symval_struct *sym; struct tp_var_struct *vars; mval zgbldir; /* saved/restored only for OUTERMOST tstart */ mval trans_id; mstr extnam_str; /* saved/restored only for OUTERMOST tstart */ struct tp_frame_struct *old_tp_frame; unsigned char *restart_ctxt; struct lv_val_struct *active_lv; /* saved/restored only for OUTERMOST tstart */ } tp_frame; fis-gtm-V7.0-005/sr_port/tp_change_reg.h0000755000032200000250000000115214342376331017003 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef TP_CHANGE_REG_H_INCLUDED #define TP_CHANGE_REG_H_INCLUDED void tp_change_reg(void); #endif fis-gtm-V7.0-005/sr_port/actuallist.c0000755000032200000250000001014414342376331016357 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "mdq.h" #include "fullbool.h" #include "advancewindow.h" #include "show_source_line.h" GBLREF boolean_t run_time; error_def(ERR_COMMAORRPAREXP); error_def(ERR_MAXACTARG); error_def(ERR_NAMEEXPECTED); error_def(ERR_SIDEEFFECTEVAL); int actuallist (oprtype *opr) { boolean_t se_warn; int i, j, mask, parmcount; oprtype ot; triple *counttrip, *masktrip, *ref0, *ref1, *ref2; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(TK_LPAREN == TREF(window_token)); advancewindow(); masktrip = newtriple(OC_PARAMETER); mask = 0; counttrip = newtriple(OC_PARAMETER); masktrip->operand[1] = put_tref(counttrip); ref0 = counttrip; if (TK_RPAREN == TREF(window_token)) parmcount = 0; else { for (parmcount = 1; ; parmcount++) { if (MAX_ACTUALS < parmcount) { stx_error (ERR_MAXACTARG); return FALSE; } if (TK_PERIOD == TREF(window_token)) { advancewindow (); if (TK_IDENT == TREF(window_token)) { ot = put_mvar(&(TREF(window_ident))); mask |= (1 << parmcount - 1); advancewindow(); } else if (TK_ATSIGN == TREF(window_token)) { if (!indirection(&ot)) return FALSE; ref2 = newtriple(OC_INDLVNAMADR); ref2->operand[0] = ot; ot = put_tref(ref2); mask |= (1 << parmcount - 1); } else { stx_error(ERR_NAMEEXPECTED); return FALSE; } } else if (TK_COMMA == TREF(window_token)) { ref2 = newtriple(OC_NULLEXP); ot = put_tref(ref2); } else if (EXPR_FAIL == expr(&ot, MUMPS_EXPR)) return FALSE; ref1 = newtriple(OC_PARAMETER); ref0->operand[1] = put_tref(ref1); ref1->operand[0] = ot; if (TK_COMMA == TREF(window_token)) { advancewindow (); if (TK_RPAREN == TREF(window_token)) { ref0 = ref1; ref2 = newtriple(OC_NULLEXP); ot = put_tref(ref2); ref1 = newtriple(OC_PARAMETER); ref0->operand[1] = put_tref(ref1); ref1->operand[0] = ot; parmcount++; break; } } else if (TREF(window_token) == TK_RPAREN) break; else { stx_error (ERR_COMMAORRPAREXP); return FALSE; } ref0 = ref1; } if ((1 < parmcount) && (TREF(side_effect_base))[TREF(expr_depth)]) { /* at least two arguments and at least one side effect - look for lvns needing protection */ assert(OLD_SE != TREF(side_effect_handling)); se_warn = SE_WARN_ON; for (i = 0, j = parmcount, ref0 = counttrip->operand[1].oprval.tref; --j; ref0 = ref0->operand[1].oprval.tref) { /* no need to do the last argument - can't have a side effect after it */ assert(OC_PARAMETER == ref0->opcode); assert((TRIP_REF == ref0->operand[0].oprclass) && (TRIP_REF == ref0->operand[1].oprclass)); if (!((1 << i++) & mask) && (OC_VAR == ref0->operand[0].oprval.tref->opcode)) { /* can only protect pass-by-value (not pass-by-reference) */ ref1 = maketriple(OC_STOTEMP); ref1->operand[0] = put_tref(ref0->operand[0].oprval.tref); ref0->operand[0].oprval.tref = ref1; dqins(ref0, exorder, ref1); /* NOTE:this violates information hiding */ if (se_warn) ISSUE_SIDEEFFECTEVAL_WARNING(ref0->src.column); } } /* the following asserts check we're getting only TRIP_REF or empty operands */ assert((NO_REF == ref0->operand[0].oprclass) || (TRIP_REF == ref0->operand[0].oprclass)); assert(((NO_REF == ref0->operand[0].oprclass) ? TRIP_REF : NO_REF) == ref0->operand[1].oprclass); } } advancewindow(); masktrip->operand[0] = put_ilit(mask); counttrip->operand[0] = put_ilit(parmcount); parmcount += 2; *opr = put_tref(masktrip); return parmcount; } fis-gtm-V7.0-005/sr_port/add_atom.c0000755000032200000250000000735114342376331015770 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "copy.h" #include "patcode.h" #include "min_max.h" /* This function is part of the MUMPS compiler. It adds one pattern atom to the string of compiled pattern atoms. * If the atom to be added can be "compressed" with the previous one, this function will allow compress() to do so. */ boolean_t add_atom(int *count, uint4 pattern_mask, pat_strlit *strlit_buff, boolean_t infinite, int *min, int *max, int *size, int *total_min, int *total_max, int lower_bound, int upper_bound, int altmin, int altmax, boolean_t *last_infinite_ptr, uint4 **fstchar_ptr, uint4 **outchar_ptr, uint4 **lastpatptr_ptr) { uint4 *patmaskptr; gtm_int64_t bound; int4 bytelen; if ((pattern_mask & PATM_STRLIT) && !strlit_buff->bytelen && *count) { /* A special case is a pattern like xxx?1N5.7""2A . Since there is an infinite number of empty strings between * any two characters in a string, a pattern atom that counts repetitions of the fixed string "" can be ignored. * That is, such an atom can only be ignored if it is not the only one in the pattern... */ return TRUE; } if (*count && !*(size - 1)) { /* If the previous atom was an n.m"", it should be removed. In such a case, the last four values * in the 'outchar' array are PATM_STRLIT (pattern mask), 0 (bytelen), 0 (charlen), flags (ASCII and no BADCHAR). */ assert(3 == PAT_STRLIT_PADDING); assert(PATM_STRLIT == *(*outchar_ptr - (PAT_STRLIT_PADDING + 1))); assert(0 == *(*outchar_ptr - 3)); /* bytelen */ assert(0 == *(*outchar_ptr - 2)); /* charlen */ assert(!((*(*outchar_ptr - 1)) & PATM_STRLIT_NONASCII)); /* flags - ascii */ assert(!((*(*outchar_ptr - 1)) & PATM_STRLIT_BADCHAR)); /* flags - no badchar */ *outchar_ptr -= (PAT_STRLIT_PADDING + 1); (*count)--; assert(0 == *count); min--; max--; size--; } if (pattern_mask & PATM_ALT) { lower_bound = BOUND_MULTIPLY(lower_bound, altmin, bound); upper_bound = BOUND_MULTIPLY(upper_bound, altmax, bound); } if (*count && pat_compress(pattern_mask, strlit_buff, infinite, *last_infinite_ptr, *lastpatptr_ptr)) { min--; max--; size--; *min = MIN(*min + lower_bound, PAT_MAX_REPEAT); *max = MIN(*max + upper_bound, PAT_MAX_REPEAT); } else { *min = MIN(lower_bound, PAT_MAX_REPEAT); *max = MIN(upper_bound, PAT_MAX_REPEAT); *lastpatptr_ptr = patmaskptr = *outchar_ptr; *last_infinite_ptr = infinite; (*outchar_ptr)++; if (*outchar_ptr - *fstchar_ptr > MAX_PATTERN_LENGTH) return FALSE; if ((pattern_mask & PATM_ALT) || !(pattern_mask & PATM_STRLIT)) { *patmaskptr++ = pattern_mask; *size = 1; } else { bytelen = strlit_buff->bytelen; *outchar_ptr += DIVIDE_ROUND_UP(bytelen, SIZEOF(uint4)) + PAT_STRLIT_PADDING; if (*outchar_ptr - *fstchar_ptr > MAX_PATTERN_LENGTH) return FALSE; *patmaskptr++ = pattern_mask; memcpy(patmaskptr, strlit_buff, bytelen + PAT_STRLIT_PADDING * SIZEOF(uint4)); *size = strlit_buff->charlen; } (*count)++; } *total_min += BOUND_MULTIPLY(*size, lower_bound, bound); if (*total_min > PAT_MAX_REPEAT) *total_min = PAT_MAX_REPEAT; *total_max += BOUND_MULTIPLY(*size, upper_bound, bound); if (*total_max > PAT_MAX_REPEAT) *total_max = PAT_MAX_REPEAT; return TRUE; } fis-gtm-V7.0-005/sr_port/add_inter.h0000755000032200000250000000115614342376331016153 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef ADD_INTER_DEFINED /* Declare parms for add_inter.c */ int4 add_inter (int val, sm_int_ptr_t addr, sm_global_latch_ptr_t latch); #define ADD_INTER_DEFINED #endif fis-gtm-V7.0-005/sr_port/advancewindow.c0000644000032200000250000002515314342376331017046 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "compiler.h" #include "cmd_qlf.h" #include "toktyp.h" #include "stringpool.h" #include "gtm_caseconv.h" #include "advancewindow.h" #include "show_source_line.h" #ifdef UTF8_SUPPORTED #include "gtm_utf8.h" #include "gtm_icu_api.h" /* U_ISPRINT() needs this header */ #endif GBLREF command_qualifier cmd_qlf; GBLREF int source_column; GBLREF spdesc stringpool; GBLREF boolean_t gtm_utf8_mode; GBLREF boolean_t run_time; LITREF char ctypetab[NUM_CHARS]; error_def(ERR_LITNONGRAPH); error_def(ERR_NUMOFLOW); static readonly unsigned char apos_ok[] = { 0,TK_NEXCLAIMATION,0,0,0,0,TK_NAMPERSAND,0 ,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0 ,0,0,0,0,TK_NLESS,TK_NEQUAL,TK_NGREATER,TK_NQUESTION ,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0 ,0,0,0,TK_NLBRACKET,0,TK_NRBRACKET,0,0 }; void advancewindow(void) { unsigned char *cp1, *cp2, *cp3, *cptop, *cptr; unsigned char *error, errtxt[(3 + 1) UTF8_ONLY(* GTM_MB_LEN_MAX)], x; /* up to 3 digits/byte & a comma */ char *tmp; int y, charlen; # ifdef UTF8_SUPPORTED uint4 ch; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; TREF(last_source_column) = source_column; source_column = (TREF(lexical_ptr) - (TREF(source_buffer)).addr + 1); TREF(window_token) = TREF(director_token); TREF(window_mval) = TREF(director_mval); (TREF(director_mval)).mvtype = 0; /* keeps mval from being GC'd since it is not useful until re-used */ tmp = (TREF(window_ident)).addr; /* More efficient to swap pointers between window_ident.addr & director_ident.addr */ TREF(window_ident) = TREF(director_ident); /* than to copy text from director_ident to window_ident */ (TREF(director_ident)).addr = tmp; x = *TREF(lexical_ptr); switch (y = ctypetab[x]) { case TK_CR: y = ctypetab[*(++(TREF(lexical_ptr)))]; if (TK_EOL != y) { /* Not looking like a , so it's an error */ y = TK_ERROR; break; } /* WARNING fall through - it is a */ case TK_EOL: TREF(director_token) = TK_EOL; return; /* if next character is terminator, avoid incrementing past it */ case TK_QUOTE: ENSURE_STP_FREE_SPACE((TREF(source_buffer)).len); cptop = (unsigned char *)((TREF(source_buffer)).addr + (TREF(source_buffer)).len - 1); cp2 = cp3 = stringpool.free; for (cp1 = (unsigned char *)TREF(lexical_ptr) + 1; cp1 <= cptop;) { # ifdef UTF8_SUPPORTED if (gtm_utf8_mode) cptr = (unsigned char *)UTF8_MBTOWC((sm_uc_ptr_t)cp1, (TREF(source_buffer)).addr + (TREF(source_buffer)).len, ch); # endif x = *cp1++; if ((SP > x) UTF8_ONLY(|| (gtm_utf8_mode && !(U_ISPRINT(ch))))) { TREF(last_source_column) = (cp1 - (unsigned char*)((TREF(source_buffer)).addr)); if (!run_time) { if ('\0' == x) { TREF(director_token) = TREF(window_token) = TK_ERROR; return; } if (!gtm_utf8_mode) charlen = 1; /* always one character in M mode */ # ifdef UTF8_SUPPORTED else { charlen = cptr - cp1 + 1; /* get the number of bytes in the utf8 character */ assert(GTM_MB_LEN_MAX >= charlen); } # endif for (cptr = cp1 - 1; charlen--;) /* stop when all bytes are done */ { /* cptr winds up back where it started due to the increments in the loop */ error = (unsigned char*)i2asc((uchar_ptr_t)errtxt, (unsigned int)*cptr++); *error++ = ','; } error--; /* do not include the last comma */ if (!(TREF(compile_time) && !(cmd_qlf.qlf & CQ_WARNINGS))) { show_source_line(TRUE); dec_err(VARLSTCNT(4) ERR_LITNONGRAPH, 2, (error - errtxt), errtxt); } } } if ('\"' == x) { UTF8_ONLY(assert(!gtm_utf8_mode || (cp1 == cptr))); if ('\"' == *cp1) cp1++; else break; } *cp2++ = x; # ifdef UTF8_SUPPORTED if (gtm_utf8_mode && (cptr > cp1)) { assert(4 > (cptr - cp1)); for (; cptr > cp1;) *cp2++ = *cp1++; } # endif assert(cp2 <= stringpool.top); } if (cp1 > cptop) { TREF(last_source_column) = (TREF(source_buffer)).len; TREF(director_token) = TREF(window_token) = TK_ERROR; return; } TREF(lexical_ptr) = (char *)cp1; TREF(director_token) = TK_STRLIT; (TREF(director_mval)).mvtype = MV_STR; (TREF(director_mval)).str.addr = (char *)cp3; (TREF(director_mval)).str.len = INTCAST(cp2 - cp3); CLEAR_MVAL_BITS(TADR(director_mval)); stringpool.free = cp2; s2n(&(TREF(director_mval))); # ifdef UTF8_SUPPORTED if (gtm_utf8_mode && !run_time) { /* UTF8 mode and not compiling an indirect gets an optimization to set the * (true) length of the string into the mval */ charlen = utf8_len_dec(&(TREF(director_mval)).str); if (0 > charlen) /* got a BADCHAR error */ TREF(director_token) = TK_ERROR; else { assert(charlen == (TREF(director_mval)).str.char_len); (TREF(director_mval)).mvtype |= MV_UTF_LEN; } } # endif return; case TK_LOWER: case TK_PERCENT: case TK_UPPER: cp2 = (unsigned char *)((TREF(director_ident)).addr); cp3 = cp2 + MAX_MIDENT_LEN; for (;;) { if (cp2 < cp3) *cp2++ = x; y = ctypetab[x = *++(TREF(lexical_ptr))]; /* note assignment */ if ((TK_UPPER != y) && (TK_DIGIT != y) && (TK_LOWER != y)) break; } (TREF(director_ident)).len = INTCAST(cp2 - (unsigned char*)(TREF(director_ident)).addr); TREF(director_token) = TK_IDENT; return; case TK_PERIOD: if (ctypetab[x = *(TREF(lexical_ptr) + 1)] != TK_DIGIT) /* note assignment */ break; case TK_DIGIT: (TREF(director_mval)).str.addr = TREF(lexical_ptr); (TREF(director_mval)).str.len = (TREF(source_buffer)).len; (TREF(director_mval)).mvtype = MV_STR; CLEAR_MVAL_BITS(TADR(director_mval)); TREF(lexical_ptr) = (char *)s2n(&(TREF(director_mval))); if (!((TREF(director_mval)).mvtype &= MV_NUM_MASK)) { TREF(last_source_column) += (TK_EOL == TREF(director_token)) ? -2 : 2; /* improve hints */ stx_error(ERR_NUMOFLOW); TREF(director_token) = TK_ERROR; return; } if (TREF(s2n_intlit)) { TREF(director_token) = TK_NUMLIT ; n2s(&(TREF(director_mval))); } else { TREF(director_token) = TK_INTLIT ; (TREF(director_mval)).str.len = INTCAST(TREF(lexical_ptr) - (TREF(director_mval)).str.addr); ENSURE_STP_FREE_SPACE((TREF(director_mval)).str.len); memcpy(stringpool.free, (TREF(director_mval)).str.addr, (TREF(director_mval)).str.len); assert (stringpool.free <= stringpool.top) ; } return; case TK_APOSTROPHE: if (32 <= (x = *++(TREF(lexical_ptr)))) /* note assignment */ { x -= 32; if (x < ARRAYSIZE(apos_ok)) { if (y = apos_ok[x]) { if (DEL < (x = *++(TREF(lexical_ptr)))) /* note assignment */ { TREF(director_token) = TK_ERROR; return; } if (TK_RBRACKET == ctypetab[x]) { (TREF(lexical_ptr))++; y = TK_NSORTS_AFTER; } TREF(director_token) = y; return; } } } TREF(director_token) = TK_APOSTROPHE; return; case TK_GREATER: case TK_LESS: if (TK_EQUAL == ctypetab[*(TREF(lexical_ptr) + 1)]) { ++(TREF(lexical_ptr)); y = ((TK_LESS == y) ? TK_NGREATER : TK_NLESS); } break; case TK_SEMICOLON: while (*++(TREF(lexical_ptr))) ; assert(TK_EOL == ctypetab[*TREF(lexical_ptr)]); TREF(director_token) = TK_EOL; return; /* if next character is terminator, avoid incrementing past it */ case TK_ASTERISK: if (DEL < (x = *(TREF(lexical_ptr) + 1))) /* note assignment */ { TREF(director_token) = TK_ERROR; return; } if (TK_ASTERISK == ctypetab[x]) { (TREF(lexical_ptr))++; y = TK_EXPONENT; } break; case TK_RBRACKET: if ((x = *(TREF(lexical_ptr) + 1)) > DEL) /* note assignment */ { TREF(director_token) = TK_ERROR; return; } if (TK_RBRACKET == ctypetab[x]) { (TREF(lexical_ptr))++; y = TK_SORTS_AFTER; } break; case TK_ATSIGN: if (DEL < (x = *(TREF(lexical_ptr) + 1))) /* note assignment */ { TREF(director_token) = TK_ERROR; return; } if (TK_HASH == ctypetab[x]) { (TREF(lexical_ptr))++; y = TK_ATHASH; } default: ; } (TREF(lexical_ptr))++; TREF(director_token) = y; return; } #ifdef GTM_TRIGGER /* The M standard does not allow the '#' character to appear inside mnames but in specific places, we want to allow this * so that triggers, which have the imbedded '#' character in their routine names, can be debugged and printed. The places * where this is allowed follow. * * 1. $TEXT() * 2. ZBREAK * 3. ZPRINT * * All other uses still prohibit '#' from being in an MNAME. Routines that need to allow # in a name can call this routine to * recombine the existing token and the look-ahead (director) token such that '#' is considered part of an mident. * * Update: Like '#', we need to allow '/' as well to allow for an overriding region name of up to 31 characters to be * specified (where the trigger needs to be searched. So handle that as well in this same function. */ void advwindw_hash_in_mname_allowed(void) { unsigned char *cp2, *cp3, x; unsigned char ident_buffer[(MAX_MIDENT_LEN * 2) + 1]; int ident_len, ch; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(TK_IDENT == TREF(window_token)); assert(TK_HASH == TREF(director_token)); /* First copy the existing token we want to expand into our safe-haven */ memcpy(ident_buffer, (TREF(window_ident)).addr, (TREF(window_ident)).len); /* Now parse further until we run out of [m]ident */ cp2 = ident_buffer + (TREF(window_ident)).len; cp3 = ident_buffer + ARRAYSIZE(ident_buffer); if (MAX_MIDENT_LEN == (TREF(window_ident)).len) cp2--; *cp2++ = '#'; /* We are only called if director token is '#' so put that char in buffer now */ /* Start processing with the token following the '#'. Allow '#' and/or '/' in addition to usual stuff. */ for (x = *TREF(lexical_ptr), ch = ctypetab[x]; ((TK_UPPER == ch) || (TK_DIGIT == ch) || (TK_LOWER == ch) || (TK_HASH == ch) || (TK_SLASH == ch)); x = *++(TREF(lexical_ptr)), ch = ctypetab[x]) { if (cp2 < cp3) *cp2++ = x; } (TREF(director_ident)).len = INTCAST(cp2 - ident_buffer); TREF(director_token) = TK_IDENT; memcpy((TREF(director_ident)).addr, ident_buffer, (TREF(director_ident)).len); advancewindow(); /* Makes the homogenized token the current token (again) and prereads next token */ } #endif fis-gtm-V7.0-005/sr_port/advancewindow.h0000755000032200000250000000120714342376331017050 0ustar librarygtc/**************************************************************** * * * 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. * * * ****************************************************************/ #ifndef ADVANCEWINDOW_included #define ADVANCEWINDOW_included void advancewindow(void); #ifdef GTM_TRIGGER void advwindw_hash_in_mname_allowed(void); #endif #endif /* ADVANCEWINDOW_included */ fis-gtm-V7.0-005/sr_port/aio_shim.h0000644000032200000250000001113214342376331016002 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2017 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. * * * ****************************************************************/ #ifndef AIO_SHIM_H_INCLUDED #define AIO_SHIM_H_INCLUDED #include "gtm_libaio.h" #ifdef USE_NOAIO #define AIO_SHIM_WRITE(UNUSED, AIOCBP, RET) /* no-op, N/A */ #define AIO_SHIM_RETURN(AIOCBP, RET) /* no-op, N/A */ #define AIO_SHIM_ERROR(AIOCBP, RET) /* no-op, N/A */ #define SIGNAL_ERROR_IN_WORKER_THREAD(gdi, err_str, errno) /* no-op, N/A */ #define CHECK_ERROR_IN_WORKER_THREAD(reg, udi) /* no-op, N/A */ #elif !defined(USE_LIBAIO) /* USE_NOAIO */ #define AIO_SHIM_WRITE(UNUSED, AIOCBP, RET) MBSTART { RET = aio_write(AIOCBP); } MBEND #define AIO_SHIM_RETURN(AIOCBP, RET) MBSTART { RET = aio_return(AIOCBP);} MBEND #define AIO_SHIM_ERROR(AIOCBP, RET) \ MBSTART { \ intrpt_state_t prev_intrpt_state; \ \ /* Defer interrupts around aio_error() to prevent pthread mutex deadlock. */ \ DEFER_INTERRUPTS(INTRPT_IN_AIO_ERROR, prev_intrpt_state); \ RET = aio_error(AIOCBP); \ ENABLE_INTERRUPTS(INTRPT_IN_AIO_ERROR, prev_intrpt_state); \ } MBEND #define SIGNAL_ERROR_IN_WORKER_THREAD(gdi, err_str, errno) /* no-op, N/A */ #define CHECK_ERROR_IN_WORKER_THREAD(reg, udi) /* no-op, N/A */ #else /* USE_LIBAIO */ #include "memcoherency.h" error_def(ERR_DBFILERR); error_def(ERR_SYSCALL); void aio_shim_destroy(gd_addr *gd); int aio_shim_write(gd_region *reg, struct aiocb *aiocbp); #define AIO_SHIM_WRITE(REG, AIOCBP, RET) MBSTART { RET = aio_shim_write(REG, AIOCBP); } MBEND #define AIO_SHIM_ERROR(AIOCBP, RET) MBSTART { RET = (AIOCBP)->status; } MBEND #define AIO_SHIM_RETURN(AIOCBP, RET) MBSTART { SHM_READ_MEMORY_BARRIER; RET = (AIOCBP)->res; } MBEND /* Need a memory barrier here so that we can * enforce publication safety of aiocbp->res * after the status has been set by the * AIOCBP_SET_FLDS macro. */ #define AIOCBP_SET_FLDS(AIOCBP, RES, RES2) \ MBSTART { \ /* Note the user may not call aio_return before aio_error has a non-EINPROGRESS value. \ * This means as long as aiocbp->res gets set before aiocbp->status, we are correctly \ * adhering to the spec. \ */ \ aiocbp->res = RES; \ /* to enforce publication safety here and in aio_shim_return() */ \ SHM_WRITE_MEMORY_BARRIER; \ aiocbp->status = RES2; \ } MBEND /* We will exit the worker thread and notify the main thread that an error * has occurred. This is so that we don't rts_error() at the same time that * commit logic is occurring, crashing and possibly causing database damage. * The next time a process calls wcs_wtfini() or aio_shim_write() the * database can safely issue an error. */ #define RECORD_ERROR_IN_WORKER_THREAD_AND_EXIT(gdi, err_str, errno) \ MBSTART { \ gdi->err_syscall = err_str; \ SHM_WRITE_MEMORY_BARRIER; \ gdi->save_errno = errno; \ pthread_exit(NULL); \ } MBEND /* If there was an error in the worker thread, we should rts_error() */ #define CHECK_ERROR_IN_WORKER_THREAD(reg, udi) \ MBSTART { \ struct gd_info *gdi, tmp; \ \ gdi = udi->owning_gd->thread_gdi; \ if ((NULL != gdi) && (gdi->save_errno != 0)) \ { /* We close everything related to the worker thread, in hopes that when it is retried later there \ * will be no more error \ */ \ SHM_READ_MEMORY_BARRIER; \ tmp = *gdi; \ aio_shim_destroy(udi->owning_gd); \ rts_error_csa(CSA_ARG(&udi->s_addrs) VARLSTCNT(12) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_SYSCALL, 5, \ LEN_AND_STR(tmp.err_syscall), CALLFROM, tmp.save_errno); \ } \ } MBEND struct gd_info { pthread_t pt; int exit_efd; /* eventfd notifies on thread shutdown */ int laio_efd; /* eventfd notifies on libaio completion */ aio_context_t ctx; /* kernel context associated with AIO */ volatile int num_ios; /* Number of IOs in flight */ /* Note that errno must be set before what */ volatile char *err_syscall; /* If an error occurred, what was it? */ volatile int save_errno; }; #endif /* USE_LIBAIO */ #endif /* AIO_SHIM_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/alias.h0000644000032200000250000003667714342376331015330 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2020 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. * * * ****************************************************************/ #ifndef ALIAS_H_ # define ALIAS_H_ #define DOLLAR_ZWRTAC "$ZWRTAC" /* Min and max for the number of stringpool garbage collections (SPGC) that will be done without having * done a lv_val garbage collection (aka LVGC aka als_lvval_gc). While I did give these numbers some * thought, they aren't far from semi-random. Since the LVGC is kind of expensive (requiring two * traversals of the lv_vals in a symbol table) I decided 2 SPGCs was good to spread the cost of one * LVGC over. For the max, I didn't want to let it get too high so the tuning algorithm didn't take a * long time to get back to a more frequent value when necessary yet 64 seems fairly infrequent. * These numbers are totally subject to future studies.. [SE 12/2008] */ #define MIN_SPGC_PER_LVGC 2 #define MAX_SPGC_PER_LVGC 64 #include "zwrite.h" /* Macro used intermittently to trace reference count changes */ /* #define DEBUG_REFCNT */ #ifdef DEBUG_REFCNT # define DBGRFCT(x) DBGFPF(x) # define DBGRFCT_ONLY(x) x #else # define DBGRFCT(x) # define DBGRFCT_ONLY(x) #endif /* Since DBGFPF calls flush_pio, if we are debugging, include the defining header file - and they need stack frame */ #if defined(DEBUG_ALIAS) || defined(DEBUG_REFCNT) # include "io.h" # include "stack_frame.h" #endif /* Macro to increment total refcount (and optionally trace it) */ #define INCR_TREFCNT(lv) \ { \ GBLREF stack_frame *frame_pointer; \ \ assert(LV_IS_BASE_VAR(lv)); \ DBGRFCT((stderr, "\nIncrement trefcnt for lv_val at 0x"lvaddr" from %d to %d by %s line %d at mpc 0x"lvaddr"\n", \ (lv), (lv)->stats.trefcnt, (lv)->stats.trefcnt + 1, __FILE__, __LINE__, frame_pointer->mpc)); \ ++(lv)->stats.trefcnt; \ assert(0 < (lv)->stats.trefcnt); \ assert((lv)->stats.trefcnt > (lv)->stats.crefcnt); \ } /* Macro to decrement total refcount (and optionally trace it) */ #define DECR_TREFCNT(lv) \ { \ GBLREF stack_frame *frame_pointer; \ \ assert(LV_IS_BASE_VAR(lv)); \ DBGRFCT((stderr, "\nDecrement trefcnt for lv_val at 0x"lvaddr" from %d to %d by %s line %d at mpc 0x"lvaddr"\n", \ (lv), (lv)->stats.trefcnt, (lv)->stats.trefcnt - 1, __FILE__, __LINE__, frame_pointer->mpc)); \ assert((lv)->stats.trefcnt > (lv)->stats.crefcnt); \ --(lv)->stats.trefcnt; \ assert(0 <= (lv)->stats.trefcnt); \ } /* Macro to increment container refcount (and optionally trace it) */ #define INCR_CREFCNT(lv) \ { \ GBLREF stack_frame *frame_pointer; \ \ assert(LV_IS_BASE_VAR(lv)); \ DBGRFCT((stderr, "\nIncrement crefcnt for lv_val at 0x"lvaddr" from %d to %d by %s line %d at mpc 0x"lvaddr"\n", \ (lv), (lv)->stats.crefcnt, (lv)->stats.crefcnt + 1, __FILE__, __LINE__, frame_pointer->mpc)); \ assert((lv)->stats.trefcnt > (lv)->stats.crefcnt); \ ++(lv)->stats.crefcnt; \ assert(0 < (lv)->stats.crefcnt); \ } /* Macro to decrement container refcount (and optionally trace it) */ #define DECR_CREFCNT(lv) \ { \ GBLREF stack_frame *frame_pointer; \ \ assert(LV_IS_BASE_VAR(lv)); \ DBGRFCT((stderr, "\nDecrement crefcnt for lv_val at 0x"lvaddr" from %d to %d by %s line %d at mpc 0x"lvaddr"\n", \ (lv), (lv)->stats.crefcnt, (lv)->stats.crefcnt - 1, __FILE__, __LINE__, frame_pointer->mpc)); \ --(lv)->stats.crefcnt; \ assert(0 <= (lv)->stats.crefcnt); \ assert((lv)->stats.trefcnt > (lv)->stats.crefcnt); \ } /* There are three flavors of DECR_BASE_REF depending on the activities we need to pursue. The first two flavors * DECR_BASE_REF and DECR_BASE_REF_RQ take 3 parms (hashtable entry and lv_val addresses) plus dotpsave which controls * whether lv_val gets TP-cloned or not. Both of these do hashtable entry maint in addition to lv_val maint but do it * in different ways. The DECR_BASE_REF macro's purpose is to leave the hashtable always pointing to a valid lv_val. * If the one we are decrementing goes to zero, we can just zap that one and leave it there but if the refcnt is not * zero, we can't do that. Since another command may follow the command doing the DECR_BASE_REF (e.g. KILL *), we can't * leave the hash table entry with a null value since gtm_fetch() won't be called to fill it in so we must allocate a * new lv_val for it. By contrast, with the DECR_BASE_REF_RQ macro, if the lv_val refcnt goes to zero, the lv_val is * requeued and in either case, the hte address is cleared to make way for a new value. The 3rd flavor is the * DECR_BASE_REF_NOSYM macro which is used for orphaned lv_vals not in a hashtable. This macro just puts lv_vals * that hit a refcnt of zero back on the freelist. */ /* Macro to decrement a base var reference and do appropriate cleanup */ #define DECR_BASE_REF(tabent, lvp, dotpsave) \ { /* Perform reference count maintenance for base var */ \ GBLREF stack_frame *frame_pointer; \ lv_val *dbr_lvp; \ symval *sym; \ \ assert(LV_IS_BASE_VAR(lvp)); \ assert(0 < (lvp)->stats.trefcnt); \ DECR_TREFCNT(lvp); \ sym = LV_GET_SYMVAL(lvp); \ if (0 == (lvp)->stats.trefcnt) \ { /* This lv_val can be effectively killed and remain in hte */ \ LVP_KILL_SUBTREE_IF_EXISTS(lvp, dotpsave); \ assert(NULL == (lvp)->tp_var); \ LVVAL_INIT(lvp, sym); \ } else \ { /* lv_val otherwise still in use -- put a new one in this hte */ \ assert((lvp)->stats.trefcnt >= (lvp)->stats.crefcnt); \ dbr_lvp = lv_getslot(sym); \ DBGRFCT((stderr, "DECR_BASE_REF: Resetting hte 0x"lvaddr" (var %.*s) from 0x"lvaddr" to 0x"lvaddr" at mpc: 0x" \ lvaddr" in %s at line %d\n", tabent, tabent->key.var_name.len, tabent->key.var_name.addr, (lvp), \ dbr_lvp, frame_pointer->mpc, __FILE__, __LINE__)); \ LVVAL_INIT(dbr_lvp, sym); \ tabent->value = dbr_lvp; \ } \ } /* Macro to decrement a base var reference and do appropriate cleanup except the tabent value is unconditionally * cleared and the lvval put on the free queue. Used when the tabent is about to be reused for a different lv. */ #define DECR_BASE_REF_RQ(tabent, lvp, dotpsave) \ { /* Perform reference count maintenance for base var */ \ GBLREF stack_frame *frame_pointer; \ \ DBGRFCT((stderr, "DECR_BASE_REF_RQ: Resetting hte 0x"lvaddr" (var %.*s) from 0x"lvaddr" to NULL for mpc 0x"lvaddr \ " in %s at line %d\n", tabent, tabent->key.var_name.len, tabent->key.var_name.addr, tabent->value, \ frame_pointer->mpc, __FILE__, __LINE__)); \ tabent->value = (void *)NULL; \ DECR_BASE_REF_NOSYM(lvp, dotpsave); \ } /* Macro to decrement a base var reference and do appropriate cleanup (if any) but no hash table * entry value cleanup is done like is done in the other DECR_BASE_REF* flavors. */ #define DECR_BASE_REF_NOSYM(lvp, dotpsave) \ { /* Perform reference count maintenance for base var */ \ assert(LV_IS_BASE_VAR(lvp)); \ assert(0 < (lvp)->stats.trefcnt); \ DECR_TREFCNT(lvp); \ if (0 == (lvp)->stats.trefcnt) \ { /* This lv_val is done .. requeue it after it is killed */ \ assert(0 == (lvp)->stats.crefcnt); \ LVP_KILL_SUBTREE_IF_EXISTS(lvp, dotpsave); \ LV_FREESLOT(lvp); \ } else \ assert((lvp)->stats.trefcnt >= (lvp)->stats.crefcnt); \ } #define LVP_KILL_SUBTREE_IF_EXISTS(lvp, dotpsave) \ { \ lvTree *lvt_child; \ \ assert(LV_IS_BASE_VAR(lvp)); \ assert(0 == (lvp)->stats.crefcnt); \ lvt_child = LV_GET_CHILD(lvp); \ if (NULL != lvt_child) \ { \ assert(((lvTreeNode *)(lvp)) == LVT_PARENT(lvt_child)); \ LV_CHILD(lvp) = NULL; \ DBGRFCT((stderr, "\nLVP_KILL_SUBTREE_IF_EXISTS: Base lv 0x"lvaddr" being killed has subtree 0x"lvaddr \ " that also needs killing - drive lv_killarray on it at %s at line %d\n", (lvp), lvt_child, \ __FILE__, __LINE__)); \ lv_killarray(lvt_child, dotpsave); \ } \ } /* Macro to decrement an alias container reference and do appropriate cleanup */ #define DECR_AC_REF(lvp, dotpsave) \ { \ if (MV_ALIASCONT & (lvp)->v.mvtype) \ { /* Killing an alias container, perform reference count maintenance */ \ \ GBLREF uint4 dollar_tlevel; \ \ lv_val *lvref = (lv_val *)(lvp)->v.str.addr; \ assert(0 == (lvp)->v.str.len); \ assert(!LV_IS_BASE_VAR(lvp)); \ assert(lvref); \ assert(LV_IS_BASE_VAR(lvref)); \ assert(0 < lvref->stats.crefcnt); \ assert(0 < lvref->stats.trefcnt); \ DBGRFCT((stderr, "\nDECR_AC_REF: Killing alias container 0x"lvaddr" pointing to lv_val 0x"lvaddr"\n", \ lvp, lvref)); \ if (dotpsave && dollar_tlevel && (NULL != lvref->tp_var) \ && !lvref->tp_var->var_cloned && (1 == lvref->stats.trefcnt)) \ /* Only clone (here) if target is going to be deleted by decrement */ \ TP_VAR_CLONE(lvref); \ DECR_CREFCNT(lvref); \ DECR_BASE_REF_NOSYM(lvref, dotpsave); \ } \ } /* Macro to mark nested symvals as having had alias activity. Mark nested symvals until we get * to a symval owning the lv_val specified. This loop will normally only run once except in the * case where the lv_val given is owned by a symval nested by an exclusive NEW. */ #define MARK_ALIAS_ACTIVE(lv_own_svlvl) \ { \ symval *sv; \ int4 lcl_own_svlvl; \ \ lcl_own_svlvl = lv_own_svlvl; \ for (sv = curr_symval; ((NULL != sv) && (sv->symvlvl >= lcl_own_svlvl)); sv = sv->last_tab) \ sv->alias_activity = TRUE; \ } /* The following *_CNTNRS_IN_TREE macros scan the supplied tree for container vars. Note that in debug mode, * even if "has_aliascont" is NOT set, we will still scan the array for containers but if one is found, then we will assert fail. * Note this means the processing is different for PRO and DBG builds in that this routine will not even be called in PRO if * the has_aliascont flag is not on in the base mval. But this allows us to check the integrity of the has_aliascont flag * in DBG because we will fail if ever a container is found in an array with the flag turned off. */ /* Macro to scan a lvTree for container vars and for each one, treat it as if it had been specified in a TP restart variable list * by setting it up to be cloned if deleted. This activity should nest so container vars that point to further trees should also * be scanned. */ #define TPSAV_CNTNRS_IN_TREE(lv_base) \ { \ GBLREF stack_frame *frame_pointer; \ lvTree *lvt; \ \ assert(LV_IS_BASE_VAR(lv_base)); \ if (lv_base->stats.tstartcycle != tstartcycle) \ { /* If haven't processed this lv_val for this transaction (or nested transaction) */ \ DBGRFCT((stderr, "\nTPSAV_CNTNRS_IN_TREE: Entered for lv 0x"lvaddr" - tstartcycle: %d lvtstartcycle: %d in %s " \ "at line %d\n", lv_base, tstartcycle, lv_base->stats.tstartcycle, __FILE__, __LINE__)); \ lv_base->stats.tstartcycle = tstartcycle; \ /* Note it is possible that this lv_val has the current tstart cycle if there has been a restart. We still need \ * to rescan the var anyway since one attempt can execute differently than a following attempt and thus create \ * different variables for us to find -- if it has aliases in it that is. \ */ \ if ((NULL != (lvt = LV_GET_CHILD(lv_base))) PRO_ONLY(&& (lv_base)->has_aliascont)) \ { \ DBGRFCT((stderr, "\n## TPSAV_CNTNRS_IN_TREE: Beginning processing lv_base at 0x"lvaddr" for mpc" \ " 0x"lvaddr" in %s at line %d\n", lv_base, frame_pointer->mpc, __FILE__, __LINE__)); \ als_prcs_tpsav_cntnr(lvt); \ DBGRFCT((stderr, "\n## TPSAV_CNTNRS_IN_TREE: Finished processing lv_base at 0x"lvaddr"\n", lv_base)); \ } \ } else \ DBGRFCT((stderr, "\n## TPSAV_CNTNRS_IN_TREE: Bypassing lv_base at 0x"lvaddr" as already processed in %s at line" \ "%d\n", lv_base, __FILE__, __LINE__)); \ } /* Macro to scan a tree for container vars, delete what they point to and unmark the container so it is no longer a container */ #define KILL_CNTNRS_IN_TREE(lv_base) \ { \ lvTree *lvt; \ \ assert(LV_IS_BASE_VAR(lv_base)); \ if ((NULL != (lvt = LV_GET_CHILD(lv_base))) PRO_ONLY(&& (lv_base)->has_aliascont)) \ als_prcs_kill_cntnr(lvt); \ } /* Macro to scan an lvval for containers pointing to other structures that need to be scanned in xnew pop processing */ #define XNEWREF_CNTNRS_IN_TREE(lv_base) \ { \ lvTree *lvt; \ \ assert(LV_IS_BASE_VAR(lv_base)); \ if ((NULL != (lvt = LV_GET_CHILD(lv_base))) PRO_ONLY(&& (lv_base)->has_aliascont)) \ als_prcs_xnewref_cntnr(lvt); \ } /* Macro to mark the base frame of the current var as having a container */ #define MARK_CONTAINER_ONBOARD(lv_base) \ { \ assert(LV_IS_BASE_VAR(lv_base)); \ lv_base->has_aliascont = TRUE; \ } #define CLEAR_ALIAS_RETARG \ { /* An error/restart has occurred while an alias return arg was in-flight. Return won't happen \ * now so we need to remove the extra counts that were added in unw_retarg() and dis-enchant \ * the alias container itself. \ */ \ lv_val *lvptr; \ \ GBLREF mval *alias_retarg; \ \ assert(NULL != alias_retarg); \ assert(alias_retarg->mvtype & MV_ALIASCONT); \ if (alias_retarg->mvtype & MV_ALIASCONT) \ { /* Protect the refs were are about to make in case ptr got banged up somehow */ \ lvptr = (lv_val *)alias_retarg->str.addr; \ assert(LV_IS_BASE_VAR(lvptr)); \ DECR_CREFCNT(lvptr); \ DECR_BASE_REF_NOSYM(lvptr, FALSE); \ } \ alias_retarg->mvtype = 0; /* Kill the temp var (no longer a container) */ \ alias_retarg = NULL; /* And no more in-flight return argument */ \ } void als_lsymtab_repair(hash_table_mname *table, ht_ent_mname *table_base_orig, int table_size_orig); void als_check_xnew_var_aliases(symval *oldsymtab, symval *cursymtab); void als_zwrhtab_init(void); void als_prcs_tpsav_cntnr(lvTree *lvt); void als_prcs_tprest_cntnr(lvTree *lvt); void als_prcs_tpunwnd_cntnr(lvTree *lvt); void als_prcs_kill_cntnr(lvTree *lvt); void als_prcs_xnewref_cntnr(lvTree *lvt); ht_ent_mname *als_lookup_base_lvval(lv_val *lvp); zwr_alias_var *als_getzavslot(void); int als_lvval_gc(void); DBGALS_ONLY(void als_lvmon_output(void);) #endif /* !ALIAS_H_ */ fis-gtm-V7.0-005/sr_port/alloc_reg.c0000644000032200000250000002236414342376331016145 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_stdio.h" #include "gtm_string.h" #include #include "cmd_qlf.h" #include "gtmdbglvl.h" #include "compiler.h" #include "mdq.h" #include "opcode.h" #include "alloc_reg.h" #include "cdbg_dump.h" #include "fullbool.h" GBLDEF int4 sa_temps[VALUED_REF_TYPES]; GBLDEF int4 sa_temps_offset[VALUED_REF_TYPES]; GBLREF int mvmax; GBLREF triple t_orig; GBLREF uint4 gtmDebugLevel; GBLREF command_qualifier cmd_qlf; LITDEF int4 sa_class_sizes[VALUED_REF_TYPES] = { 0 /* dummy for slot zero */ ,SIZEOF(mval*) /* TVAR_REF */ ,SIZEOF(mval) /* TVAL_REF */ ,SIZEOF(mint) /* TINT_REF */ ,SIZEOF(mval*) /* TVAD_REF */ ,SIZEOF(char*) /* TCAD_REF */ }; LITREF octabstruct oc_tab[]; #define MAX_TEMP_COUNT 1024 error_def(ERR_TMPSTOREMAX); STATICFNDCL void remove_backptr(triple *curtrip, oprtype *opnd, char (*tempcont)[MAX_TEMP_COUNT]); void alloc_reg(void) { triple *x, *y, *ref; tbp *b; oprtype *j; opctype opc, opx; char tempcont[VALUED_REF_TYPES][MAX_TEMP_COUNT], dest_type; int r, c, temphigh[VALUED_REF_TYPES]; unsigned int oct; int4 size; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; memset(&tempcont[0][0], 0, SIZEOF(tempcont)); memset(&temphigh[0], -1, SIZEOF(temphigh)); temphigh[TVAR_REF] = mvmax - 1; COMPDBG(PRINTF(" \n\n\n\n************************************ Begin alloc_reg scan *****************************\n");); dqloop(&t_orig, exorder, x) { COMPDBG(PRINTF(" ************************ Triple Start **********************\n");); COMPDBG(cdbg_dump_triple(x, 0);); opc = x->opcode; switch (opc) { case OC_NOOP: case OC_PARAMETER: continue; case OC_LINESTART: /* If the next triple is also a LINESTART, then this is a comment line. * Therefore eliminate this LINESTART */ opx = x->exorder.fl->opcode; if ((OC_LINESTART == opx) || (OC_LINEFETCH == opx)) { x->opcode = OC_NOOP; COMPDBG(PRINTF(" ** Converting triple to NOOP (rsn 1) **\n");); continue; /* continue, because 'normal' NOOP continues from this switch */ } /* There is a special case in the case of NOLINE_ENTRY being specified. If a blank line is followed * by a line with a label and that label generates fetch information, the generated triple sequence * will be LINESTART (from blank line), ILIT (count from PREVIOUS fetch), LINEFETCH. We will detect * that sequence here and change the LINESTART to a NOOP. */ if (!(cmd_qlf.qlf & CQ_LINE_ENTRY) && (OC_ILIT == opx) && (NULL != x->exorder.fl->exorder.fl) && (OC_LINEFETCH == x->exorder.fl->exorder.fl->opcode)) { x->opcode = OC_NOOP; COMPDBG(PRINTF(" ** Converting triple to NOOP (rsn 2) **\n");); continue; /* continue, because 'normal' NOOP continues from this switch */ } /* WARNING else fallthrough */ case OC_LINEFETCH: /* this code is a sad hack - it used to assert that there was no temp leak, but in non-short-circuit * mode there can be a leak, we weren't finding it and the customers were justifiably impatient * so the following code now makes the leak go away */ for (c = temphigh[TVAL_REF]; 0 <= c; c--) tempcont[TVAL_REF][c] = 0; /* prevent leaking TVAL temps */ if (OC_LINESTART == opc) break; /* WARNING else fallthrough */ case OC_FETCH: assert((TRIP_REF == x->operand[0].oprclass) && (OC_ILIT == x->operand[0].oprval.tref->opcode)); if (x->operand[0].oprval.tref->operand[0].oprval.ilit == mvmax) { x->operand[0].oprval.tref->operand[0].oprval.ilit = 0; x->operand[1].oprclass = NO_REF; } break; case OC_PASSTHRU: COMPDBG(PRINTF(" *** OC_PASSTHRU opcode being NOOP'd\n");); remove_backptr(x, &x->operand[0], tempcont); x->opcode = OC_NOOP; x->operand[0].oprclass = NO_REF; assert(NO_REF == x->operand[1].oprclass); assert(NO_REF == x->destination.oprclass); continue; case OC_STO: /* If we are storing a literal e.g. s x="hi", don't call op_sto, because we do not * need to check if the literal is defined. OC_STOLIT will be an in-line copy. * Bypass this if we have been requested to not do inline literals. */ if ((cmd_qlf.qlf & CQ_INLINE_LITERALS) && (TRIP_REF == x->operand[1].oprclass) && (OC_LIT == x->operand[1].oprval.tref->opcode)) opc = x->opcode = OC_STOLIT; break; case OC_EQU: /* Check to see if the operation is a x="" or a ""=x, if so (and this is a very common case) * use special opcode OC_EQUNUL, which takes one argument and just checks length for zero */ if ((TRIP_REF == x->operand[0].oprclass) && (OC_LIT == x->operand[0].oprval.tref->opcode) && (0 == x->operand[0].oprval.tref->operand[0].oprval.mlit->v.str.len)) { x->operand[0] = x->operand[1]; x->operand[1].oprclass = NO_REF; opc = x->opcode = OC_EQUNUL; } else if ((TRIP_REF == x->operand[1].oprclass) && (OC_LIT == x->operand[1].oprval.tref->opcode) && (0 == x->operand[1].oprval.tref->operand[0].oprval.mlit->v.str.len)) { x->operand[1].oprclass = NO_REF; opc = x->opcode = OC_EQUNUL; } break; case OC_GVSAVTARG: if (x->backptr.que.fl == &x->backptr) { /* jmp_opto removed all associated OC_GVRECTARGs - must get rid of this OC_GVSAVTARG */ opc = x->opcode = OC_NOOP; assert((NO_REF == x->operand[0].oprclass) && (NO_REF == x->operand[1].oprclass)); } /* WARNING fallthrough */ default: break; } if (NO_REF == (dest_type = x->destination.oprclass)) /* Note assignment */ { oct = oc_tab[opc].octype; if ((oct & OCT_VALUE) && (x->backptr.que.fl != &x->backptr) && !(oct & OCT_CGSKIP)) { if (!(oct & OCT_MVADDR) && (x->backptr.que.fl->que.fl == &x->backptr) && (OC_STO == (y = x->backptr.que.fl->bpt)->opcode) && (y->operand[1].oprval.tref == x) && (OC_VAR == y->operand[0].oprval.tref->opcode)) { x->destination = y->operand[0]; y->opcode = OC_NOOP; y->operand[0].oprclass = y->operand[1].oprclass = NO_REF; } else { oct &= OCT_VALUE | OCT_MVADDR; assert((OCT_MVAL == oct) || (OCT_MINT == oct) || ((OCT_MVADDR | OCT_MVAL) == oct) || (OCT_CDADDR == oct)); r = (OCT_MVAL == oct) ? TVAL_REF : (((OCT_MVADDR | OCT_MVAL) == oct) ? TVAD_REF : ((OCT_MINT == oct) ? TINT_REF : TCAD_REF)); for (c = 0; tempcont[r][c] && (MAX_TEMP_COUNT > c); c++) ; if (MAX_TEMP_COUNT <= c) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_TMPSTOREMAX); tempcont[r][c] = 1; x->destination.oprclass = r; x->destination.oprval.temp = c; if (c > temphigh[r]) temphigh[r] = c; if (OC_CDADDR == x->opcode) x->opcode = OC_NOOP; } } } else if (TRIP_REF == dest_type) { assert(x->destination.oprval.tref->destination.oprclass); x->destination = x->destination.oprval.tref->destination; } for (j = x->operand, y = x; j < ARRAYTOP(y->operand); ) { /* Loop through all the parameters of the current opcode. For each parameter that requires an intermediate * temporary, decrement (this is what remove_backptr does) the "reference count" -- opcodes yet to be * processed that still need the intermediate result -- and if that number is zero, mark the temporary * available. We can then reuse the temp to hold the results of subsequent opcodes. Note that remove_backptr * is essentially the resolve_tref() in resolve_ref.c. resolve_tref increments the "reference count", * while remove_backptr decrements it. */ if (TRIP_REF == j->oprclass) { ref = j->oprval.tref; if (OC_PARAMETER == ref->opcode) { y = ref; j = y->operand; continue; } remove_backptr(y, j, tempcont); } j++; } } for (r = 0; VALUED_REF_TYPES > r; r++) sa_temps[r] = temphigh[r] + 1; sa_temps_offset[TVAR_REF] = sa_temps[TVAR_REF] * sa_class_sizes[TVAR_REF]; size = sa_temps[TVAL_REF] * sa_class_sizes[TVAL_REF]; sa_temps_offset[TVAL_REF] = size; /* Since we need to align the temp region to the largest types, align even int temps to SIZEOF(char*) */ size += ROUND_UP2(sa_temps[TINT_REF] *sa_class_sizes[TINT_REF], SIZEOF(char *)); sa_temps_offset[TINT_REF] = size; size += sa_temps[TVAD_REF] * sa_class_sizes[TVAD_REF]; sa_temps_offset[TVAD_REF] = size; size += sa_temps[TCAD_REF] * sa_class_sizes[TCAD_REF]; sa_temps_offset[TCAD_REF] = size; } void remove_backptr(triple *curtrip, oprtype *opnd, char (*tempcont)[MAX_TEMP_COUNT]) { triple *ref; tbp *b; int r; assert(TRIP_REF == opnd->oprclass); assert(OC_PASSTHRU != opnd->oprval.tref->opcode); ref = opnd->oprval.tref; r = ref->destination.oprclass; if (NO_REF != r) { dqloop(&ref->backptr, que, b) { if (b->bpt == curtrip) { dqdel(b, que); break; } } if ((ref->backptr.que.fl == &ref->backptr) && (TVAR_REF != r)) tempcont[r][ref->destination.oprval.temp] = 0; } } fis-gtm-V7.0-005/sr_port/alloc_reg.h0000755000032200000250000000105414342376331016146 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef ALLOC_REG_INCLUDED #define ALLOC_REG_INCLUDED void alloc_reg(void); #endif /* ALLOC_REG_INCLUDED */ fis-gtm-V7.0-005/sr_port/anticipatory_freeze.h0000644000032200000250000005235714342376331020276 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2020 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. * * * ****************************************************************/ #ifndef ANTICIPATORY_FREEZE_H #define ANTICIPATORY_FREEZE_H #include "gtm_time.h" /* needed for GET_CUR_TIME */ #include "gdsroot.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "repl_msg.h" /* needed for gtmsource.h */ #include "gtmsource.h" /* needed for jnlpool_addrs typedef */ #include "sleep_cnt.h" /* needed for SLEEP_INSTFREEZEWAIT macro */ #include "wait_for_disk_space.h" /* needed by DB_LSEEKWRITE macro for prototype */ #include "gtmimagename.h" /* needed for IS_GTM_IMAGE */ #include "jnl.h" /* needed for REPL_ALLOWED */ #include "forced_exit_err_display.h" boolean_t is_anticipatory_freeze_needed(sgmnt_addrs *csa, int msg_id); void set_anticipatory_freeze(sgmnt_addrs *csa, int msg_id); boolean_t init_anticipatory_freeze_errors(void); /* Define function pointers to certain functions to avoid executables like gtmsecshr from unnecessarily * linking with these functions (which causes the database/replication stuff to be pulled in). */ typedef boolean_t (*is_anticipatory_freeze_needed_t)(sgmnt_addrs *csa, int msgid); typedef void (*set_anticipatory_freeze_t)(sgmnt_addrs *csa, int msg_id); GBLREF is_anticipatory_freeze_needed_t is_anticipatory_freeze_needed_fnptr; GBLREF set_anticipatory_freeze_t set_anticipatory_freeze_fnptr; GBLREF int pool_init; GBLREF boolean_t mupip_jnl_recover; #ifdef DEBUG GBLREF uint4 lseekwrite_target; #endif error_def(ERR_DSKNOSPCAVAIL); error_def(ERR_MUINSTFROZEN); error_def(ERR_MUINSTUNFROZEN); error_def(ERR_MUNOACTION); error_def(ERR_REPLINSTFREEZECOMMENT); error_def(ERR_REPLINSTFROZEN); error_def(ERR_REPLINSTUNFROZEN); error_def(ERR_TEXT); #define ENABLE_FREEZE_ON_ERROR \ { \ if (!IS_GTMSECSHR_IMAGE) \ { /* Set anticipatory freeze function pointers to be used later (in send_msg and rts_error) */ \ is_anticipatory_freeze_needed_fnptr = &is_anticipatory_freeze_needed; \ set_anticipatory_freeze_fnptr = &set_anticipatory_freeze; \ } \ } #define CHECK_IF_FREEZE_ON_ERROR_NEEDED(CSA, MSG_ID, FREEZE_NEEDED, FREEZE_MSG_ID, LCL_JNLPOOL) \ { \ GBLREF jnlpool_addrs_ptr_t jnlpool; \ \ if (!FREEZE_NEEDED && CSA && CUSTOM_ERRORS_LOADED_CSA(CSA, LCL_JNLPOOL) \ && (NULL != is_anticipatory_freeze_needed_fnptr)) \ { /* NOT gtmsecshr */ \ if (IS_REPL_INST_UNFROZEN_JPL(LCL_JNLPOOL) \ && (*is_anticipatory_freeze_needed_fnptr)((sgmnt_addrs *)CSA, MSG_ID)) \ { \ FREEZE_NEEDED = TRUE; \ FREEZE_MSG_ID = MSG_ID; \ } \ } \ } #define FREEZE_INSTANCE_IF_NEEDED(CSA, FREEZE_NEEDED, FREEZE_MSG_ID, LCL_JNLPOOL) \ { \ if (FREEZE_NEEDED) \ { \ assert(NULL != set_anticipatory_freeze_fnptr); \ (*set_anticipatory_freeze_fnptr)((sgmnt_addrs *)CSA, FREEZE_MSG_ID); \ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_REPLINSTFROZEN, 1, \ LCL_JNLPOOL->repl_inst_filehdr->inst_info.this_instname); \ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_REPLINSTFREEZECOMMENT, 1, \ LCL_JNLPOOL->jnlpool_ctl->freeze_comment); \ } \ } #define CLEAR_ANTICIPATORY_FREEZE(FREEZE_CLEARED) \ { \ GBLREF jnlpool_addrs_ptr_t jnlpool; \ \ if (IS_REPL_INST_FROZEN) \ { \ jnlpool->jnlpool_ctl->freeze = 0; \ FREEZE_CLEARED = TRUE; \ } \ } #define REPORT_INSTANCE_UNFROZEN(FREEZE_CLEARED) \ { \ GBLREF jnlpool_addrs_ptr_t jnlpool; \ \ if (FREEZE_CLEARED) \ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_REPLINSTUNFROZEN, 1, \ jnlpool->repl_inst_filehdr->inst_info.this_instname); \ } #define AFREEZE_MASK 0x01 /* Only use CUSTOM_ERRORS_AVAILABLE if you are specifically interested in whether the custom errors variable is set, * or if you know the journal pool isn't open (yet). * Otherwise, use INST_FREEZE_ON_ERROR_POLICY. * Only use CUSTOM_ERRORS_LOADED if you are in the code path towards checking a custom error. * Otherwise, use INST_FREEZE_ON_ERROR_POLICY. * Use INST_FREEZE_ON_ERROR_POLICY to select alternative journal pool attach/detach behavior. */ #define CUSTOM_ERRORS_AVAILABLE (0 != (TREF(gtm_custom_errors)).len) #define CUSTOM_ERRORS_LOADED ((NULL != jnlpool) && (NULL != jnlpool->jnlpool_ctl) \ && jnlpool->jnlpool_ctl->instfreeze_environ_inited) #define CUSTOM_ERRORS_LOADED_CSA(CSA, LCL_JNLPOOL) \ ( \ (LCL_JNLPOOL = JNLPOOL_FROM((sgmnt_addrs *)CSA)) \ && (NULL != LCL_JNLPOOL->jnlpool_ctl) \ && LCL_JNLPOOL->jnlpool_ctl->instfreeze_environ_inited \ ) #define INST_FREEZE_ON_ERROR_POLICY (CUSTOM_ERRORS_AVAILABLE || CUSTOM_ERRORS_LOADED) #define INST_FREEZE_ON_ERROR_POLICY_CSA(CSA, LCL_JNLPOOL) (CUSTOM_ERRORS_AVAILABLE \ || CUSTOM_ERRORS_LOADED_CSA(CSA, LCL_JNLPOOL)) /* INSTANCE_FREEZE_HONORED determines whether operations on a particular database can trigger an instance freeze. * INST_FREEZE_ON_ERROR_ENABLED() determines whether operations on a particular database can trigger an instance freeze in the * current operating environment. * INST_FREEZE_ON_MSG_ENABLED() determines whether the given message should trigger an instance freeze when associated with * the specified database. * INST_FREEZE_ON_NOSPC_ENABLED() determines whether an out-of-space condition associated with the specified database should * trigger an instance freeze. * Note that it is possible for these macros to be invoked while in "gvcst_cre_autoDB" in which case CSA->nl would be NULL * hence the check for NULL before trying to access onln_rlbk_pid. * These macros set LCL_JNLPOOL to the assocaited jnlpool if TRUE otherwise to NULL */ #define INSTANCE_FREEZE_HONORED(CSA, LCL_JNLPOOL) instance_freeze_honored(CSA, &(LCL_JNLPOOL)) #define INST_FREEZE_ON_ERROR_ENABLED(CSA, LCL_JNLPOOL) (INSTANCE_FREEZE_HONORED(CSA, LCL_JNLPOOL) \ && CUSTOM_ERRORS_LOADED_CSA(CSA, LCL_JNLPOOL) \ && (((sgmnt_addrs *)CSA)->hdr->freeze_on_fail)) #define INST_FREEZE_ON_MSG_ENABLED(CSA, MSG, LCL_JNLPOOL) (INST_FREEZE_ON_ERROR_ENABLED(CSA, LCL_JNLPOOL) \ && (NULL != is_anticipatory_freeze_needed_fnptr) \ && (*is_anticipatory_freeze_needed_fnptr)(CSA, MSG)) #define INST_FREEZE_ON_NOSPC_ENABLED(CSA, LCL_JNLPOOL) INST_FREEZE_ON_MSG_ENABLED(CSA, ERR_DSKNOSPCAVAIL, LCL_JNLPOOL) /* IS_REPL_INST_FROZEN is TRUE if we know that the instance is frozen. * IS_REPL_INST_UNFROZEN is TRUE if we know that the instance is not frozen. */ #define IS_REPL_INST_FROZEN IS_REPL_INST_FROZEN_JPL(jnlpool) #define IS_REPL_INST_UNFROZEN IS_REPL_INST_UNFROZEN_JPL(jnlpool) #define IS_REPL_INST_FROZEN_JPL(JNLPOOL) ((NULL != JNLPOOL) && (NULL != JNLPOOL->jnlpool_ctl) \ && JNLPOOL->jnlpool_ctl->freeze) #define IS_REPL_INST_UNFROZEN_JPL(JNLPOOL) ((NULL != JNLPOOL) && (NULL != JNLPOOL->jnlpool_ctl) \ && !JNLPOOL->jnlpool_ctl->freeze) #define INST_FROZEN_COMMENT "PID %d encountered %s; Instance frozen" #define MSGID_TO_ERRMSG(MSG_ID, ERRMSG) \ { \ const err_ctl *ctl; \ \ ctl = err_check(MSG_ID); \ assert(NULL != ctl); \ GET_MSG_INFO(MSG_ID, ctl, ERRMSG); \ } #define GENERATE_INST_FROZEN_COMMENT(BUF, BUF_LEN, MSG_ID) \ { \ GBLREF uint4 process_id; \ const err_msg *msginfo; \ \ MSGID_TO_ERRMSG(MSG_ID, msginfo); \ SNPRINTF(BUF, BUF_LEN, INST_FROZEN_COMMENT, process_id, msginfo->tag); \ } #ifdef DEBUG void clear_fake_enospc_if_master_dead(void); #define CLEAR_FAKE_ENOSPC_IF_MASTER_DEAD clear_fake_enospc_if_master_dead() #endif /* This is a version of the macro which waits for the instance freeze to be lifted off assuming the process has * already attached to the journal pool. We need to wait for the freeze only if the input database cares about * anticipatory freeze. Examples of those databases that don't care are non-replicated databases, databases with * "freeze_on_fail" field set to FALSE in the file header etc. Hence the use of INST_FREEZE_ON_ERROR_ENABLED below. * Note: Do not use "hiber_start" as that uses timers and if we are already in a timer handler now, nested timers * wont work. Since SHORT_SLEEP allows a max of 1000, we use 500 (half a second) for now. */ /* #GTM_THREAD_SAFE : The below macro (WAIT_FOR_REPL_INST_UNFREEZE) is thread-safe */ #define WAIT_FOR_REPL_INST_UNFREEZE(CSA) \ wait_for_repl_inst_unfreeze(CSA) /* This is a safer version of the WAIT_FOR_REPL_INST_UNFREEZE macro, which waits for the instance freeze * to be lifted off but is not sure if the process has access to the journal pool yet. * If it does not, then it assumes the instance is not frozen. */ /* #GTM_THREAD_SAFE : The below macro (WAIT_FOR_REPL_INST_UNFREEZE_SAFE) is thread-safe */ #define WAIT_FOR_REPL_INST_UNFREEZE_SAFE(CSA) \ { \ GBLREF jnlpool_addrs_ptr_t jnlpool; \ jnlpool_addrs_ptr_t local_jnlpool; \ \ assert(NULL != CSA); \ local_jnlpool = JNLPOOL_FROM(CSA); \ if (IS_REPL_INST_FROZEN_JPL(local_jnlpool)) \ WAIT_FOR_REPL_INST_UNFREEZE(CSA); \ } /* Below are similar macros like the above but with no CSA to specifically check for */ /* #GTM_THREAD_SAFE : The below macro (WAIT_FOR_REPL_INST_UNFREEZE_NOCSA) is thread-safe */ #define WAIT_FOR_REPL_INST_UNFREEZE_NOCSA \ { \ GBLREF jnlpool_addrs_ptr_t jnlpool; \ WAIT_FOR_REPL_INST_UNFREEZE_NOCSA_JPL(jnlpool); \ } #define WAIT_FOR_REPL_INST_UNFREEZE_NOCSA_JPL(JPL) \ wait_for_repl_inst_unfreeze_nocsa_jpl(JPL) #define WAIT_FOR_REPL_INST_UNFREEZE_NOCSA_SAFE \ { \ GBLREF jnlpool_addrs_ptr_t jnlpool; \ \ if (IS_REPL_INST_FROZEN) \ WAIT_FOR_REPL_INST_UNFREEZE_NOCSA; \ } /* GTM_DB_FSYNC/GTM_JNL_FSYNC are similar to GTM_FSYNC except that we don't do the fsync * (but instead hang) if we detect the instance is frozen. We proceed with the fsync once the freeze clears. * CSA is a parameter indicating which database it is that we want to fsync. * GTM_REPL_INST_FSYNC is different in that we currently don't care about instance freeze for replication * instance file writes. */ #define GTM_DB_FSYNC(CSA, FD, RC) \ { \ GBLREF jnlpool_addrs_ptr_t jnlpool; \ jnlpool_addrs_ptr_t local_jnlpool; \ node_local_ptr_t cnl; \ sgmnt_addrs *local_csa; \ \ local_csa = CSA; \ local_jnlpool = JNLPOOL_FROM(local_csa); \ assert(local_csa || (!local_jnlpool || !local_jnlpool->jnlpool_ctl)); \ if (NULL != local_csa) \ { \ WAIT_FOR_REPL_INST_UNFREEZE_SAFE(local_csa); \ cnl = local_csa->nl; \ if (NULL != cnl) \ INCR_GVSTATS_COUNTER(local_csa, cnl, n_db_fsync, 1); \ } \ GTM_FSYNC(FD, RC); \ } #define GTM_JNL_FSYNC(CSA, FD, RC) \ { \ GBLREF jnlpool_addrs_ptr_t jnlpool; \ jnlpool_addrs_ptr_t local_jnlpool; \ node_local_ptr_t cnl; \ \ local_jnlpool = JNLPOOL_FROM(CSA); \ assert(CSA || (!local_jnlpool || !local_jnlpool->jnlpool_ctl)); \ if (NULL != CSA) \ { \ WAIT_FOR_REPL_INST_UNFREEZE_SAFE(CSA); \ cnl = (CSA)->nl; \ if (NULL != cnl) \ INCR_GVSTATS_COUNTER((CSA), cnl, n_jnl_fsync, 1); \ } \ GTM_FSYNC(FD, RC); \ } #define GTM_REPL_INST_FSYNC(FD, RC) GTM_FSYNC(FD, RC) #define LSEEKWRITE_IS_TO_NONE 0 #define LSEEKWRITE_IS_TO_DB 1 #define LSEEKWRITE_IS_TO_JNL 2 #define LSEEKWRITE_IS_TO_DB_ASYNC 3 #define LSEEKWRITE_IS_TO_DB_ASYNC_RESTART 4 #ifdef DEBUG #define FAKE_ENOSPC(CSA, FAKE_WHICH_ENOSPC, LSEEKWRITE_TARGET, LCL_STATUS) \ MBSTART { \ GBLREF jnlpool_addrs_ptr_t jnlpool; \ GBLREF boolean_t multi_thread_in_use; \ \ if ((NULL != CSA) && !multi_thread_in_use) /* Do not manipulate fake-enospc (global variable) while in threaded code */ \ { \ if (WBTEST_ENABLED(WBTEST_RECOVER_ENOSPC)) \ { /* This test case is only used by mupip */ \ gtm_wbox_input_test_case_count++; \ if ((0 != gtm_white_box_test_case_count) \ && (gtm_white_box_test_case_count <= gtm_wbox_input_test_case_count)) \ { \ LCL_STATUS = ENOSPC; \ if (gtm_white_box_test_case_count == gtm_wbox_input_test_case_count) \ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TEXT, 2, \ LEN_AND_LIT("Turning on fake ENOSPC for exit status test")); \ } \ } else if (!IS_DSE_IMAGE /*DSE does not freeze so let it work as normal */ \ && (!CSA->jnlpool || (CSA->jnlpool == jnlpool)) \ && (jnlpool && jnlpool->jnlpool_ctl) && (NULL != ((sgmnt_addrs *)CSA)->nl) \ && ((sgmnt_addrs *)CSA)->nl->FAKE_WHICH_ENOSPC) \ { \ LCL_STATUS = ENOSPC; \ lseekwrite_target = LSEEKWRITE_TARGET; \ } \ } \ } MBEND #else #define FAKE_ENOSPC(CSA, FAKE_ENOSPC, LSEEKWRITE_TARGET, LCL_STATUS) #endif #ifdef DEBUG #define DB_HANG_TRIGGER 75 #define DB_LSEEKWRITE_HANG(CSA) (WBTEST_ENABLED(WBTEST_DB_WRITE_HANG) \ && (CSA) && ((struct sgmnt_addrs_struct *)(CSA))->nl \ && (DB_HANG_TRIGGER \ == INTERLOCK_ADD(&(((struct sgmnt_addrs_struct *)(CSA)) \ ->nl->db_writes), NULL, 1))) #else #define DB_LSEEKWRITE_HANG(CSA) FALSE #endif /* #GTM_THREAD_SAFE : The below macro (DB_LSEEKWRITE) is thread-safe */ #define DB_LSEEKWRITE(CSA, UDI, DB_FN, FD, OFFSET, BUFF, SIZE, STATUS) \ MBSTART { \ GBLREF uint4 process_id; \ sgmnt_addrs *CSA_LOCAL = CSA; \ \ assert(!CSA_LOCAL || !CSA_LOCAL->region || FILE_INFO(CSA_LOCAL->region)->grabbed_access_sem \ || !(CSA_LOCAL)->nl || !FROZEN_CHILLED(CSA_LOCAL) || FREEZE_LATCH_HELD(CSA_LOCAL)); \ DBG_CHECK_DIO_ALIGNMENT(UDI, OFFSET, BUFF, SIZE); \ DO_LSEEKWRITE(CSA_LOCAL, DB_FN, FD, OFFSET, BUFF, SIZE, STATUS, fake_db_enospc, DB_LSEEKWRITE_HANG(CSA), \ LSEEKWRITE_IS_TO_DB); \ } MBEND /* This is similar to DB_LSEEKWRITE except that this is used by GTMSECSHR and since that is root-owned we do not want * to pull in a lot of unnecessary things from the instance-freeze scheme so we directly invoke LSEEKWRITE instead of * going through DO_LSEEKWRITE. */ #define GTMSECSHR_DB_LSEEKWRITE(UDI, FD, OFFSET, BUFF, SIZE, STATUS) \ MBSTART { \ DBG_CHECK_DIO_ALIGNMENT(UDI, OFFSET, BUFF, SIZE); \ LSEEKWRITE(FD, OFFSET, BUFF, SIZE, STATUS); \ } MBEND /* #GTM_THREAD_SAFE : The below macro (DB_LSEEKWRITEASYNCSTART) is thread-safe */ #define DB_LSEEKWRITEASYNCSTART(CSA, UDI, DB_FN, FD, OFFSET, BUFF, SIZE, CR, STATUS) \ MBSTART { \ DBG_CHECK_DIO_ALIGNMENT(UDI, OFFSET, BUFF, SIZE); \ DO_LSEEKWRITEASYNC(CSA, DB_FN, FD, OFFSET, BUFF, SIZE, CR, STATUS, fake_db_enospc, LSEEKWRITE_IS_TO_DB_ASYNC); \ } MBEND /* #GTM_THREAD_SAFE : The below macro (DB_LSEEKWRITEASYNCRESTART) is thread-safe */ #define DB_LSEEKWRITEASYNCRESTART(CSA, UDI, DB_FN, FD, BUFF, CR, STATUS) \ MBSTART { \ DBG_CHECK_DIO_ALIGNMENT(UDI, CR->aiocb.aio_offset, BUFF, CR->aiocb.aio_nbytes); \ DO_LSEEKWRITEASYNC(CSA, DB_FN, FD, 0, BUFF, 0, CR, STATUS, fake_db_enospc, LSEEKWRITE_IS_TO_DB_ASYNC_RESTART); \ } MBEND #ifdef DEBUG #define JNL_HANG_TRIGGER 500 #define JNL_LSEEKWRITE_HANG(CSA) jnl_lseekwrite_hang(CSA) static inline boolean_t jnl_lseekwrite_hang(struct sgmnt_addrs_struct *csa) { return (WBTEST_ENABLED(WBTEST_JNL_WRITE_HANG) && csa && csa->nl && (JNL_HANG_TRIGGER == INTERLOCK_ADD(&csa->nl->jnl_writes, NULL, 1))); } #define LSEEKWRITE_HANG_SLEEP() lseekwrite_hang_sleep() static inline void lseekwrite_hang_sleep(void) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_TEXT, 2, LEN_AND_LIT("TEST-I-LSEEKWRITEHANGSTART")); \ SLEEP_USEC(180ULL * E_6, TRUE); /* Fixed 3 minutes, with restart. */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_TEXT, 2, LEN_AND_LIT("TEST-I-LSEEKWRITEHANGEND")); \ } #else #define JNL_LSEEKWRITE_HANG(CSA) FALSE #define LSEEKWRITE_HANG_SLEEP() /**/ #endif /* #GTM_THREAD_SAFE : The below macro (JNL_LSEEKWRITE) is thread-safe */ #define JNL_LSEEKWRITE(CSA, JNL_FN, FD, OFFSET, BUFF, SIZE, STATUS) \ DO_LSEEKWRITE(CSA, JNL_FN, FD, OFFSET, BUFF, SIZE, STATUS, fake_jnl_enospc, JNL_LSEEKWRITE_HANG(CSA), \ LSEEKWRITE_IS_TO_JNL) /* #GTM_THREAD_SAFE : The below macro (DO_LSEEKWRITE) is thread-safe */ #define DO_LSEEKWRITE(CSA, FNPTR, FD, OFFSET, BUFF, SIZE, STATUS, FAKE_WHICH_ENOSPC, HANG, LSEEKWRITE_TARGET) \ MBSTART { \ GBLREF uint4 process_id; \ int lcl_status; \ sgmnt_addrs *local_csa = CSA; \ intrpt_state_t prev_intrpt_state; \ \ if (NULL != local_csa) \ WAIT_FOR_REPL_INST_UNFREEZE_SAFE(local_csa); \ DEFER_INTERRUPTS(INTRPT_IN_DB_JNL_LSEEKWRITE, prev_intrpt_state); \ if (HANG) \ LSEEKWRITE_HANG_SLEEP(); \ LSEEKWRITE(FD, OFFSET, BUFF, SIZE, lcl_status); \ FAKE_ENOSPC(local_csa, FAKE_WHICH_ENOSPC, LSEEKWRITE_TARGET, lcl_status); \ if ((ENOSPC == lcl_status) && (NULL != local_csa)) \ { \ wait_for_disk_space(local_csa, (char *)FNPTR, FD, (off_t)OFFSET, (char *)BUFF, (size_t)SIZE, &lcl_status); \ assert((NULL == local_csa) || (NULL == local_csa->nl) || !local_csa->nl->FAKE_WHICH_ENOSPC \ || (ENOSPC != lcl_status)); \ } \ STATUS = lcl_status; \ ENABLE_INTERRUPTS(INTRPT_IN_DB_JNL_LSEEKWRITE, prev_intrpt_state); \ } MBEND /* #GTM_THREAD_SAFE : The below macro (DO_LSEEKWRITEASYNC) is thread-safe */ #define DO_LSEEKWRITEASYNC(CSA, FNPTR, FD, OFFSET, BUFF, SIZE, CR, STATUS, FAKE_WHICH_ENOSPC, LSEEKWRITE_TARGET) \ MBSTART { \ if (NULL != CSA) \ WAIT_FOR_REPL_INST_UNFREEZE_SAFE(CSA); \ switch (LSEEKWRITE_TARGET) \ { \ case LSEEKWRITE_IS_TO_DB_ASYNC: \ LSEEKWRITEASYNCSTART(CSA, FD, OFFSET, BUFF, SIZE, CR, STATUS); \ break; \ case LSEEKWRITE_IS_TO_DB_ASYNC_RESTART: \ LSEEKWRITEASYNCRESTART(CSA, FD, BUFF, CR, STATUS); \ break; \ default: \ assert(FALSE); \ break; \ } \ } MBEND /* Currently, writes to replication instance files do NOT trigger instance freeze behavior. * Neither does a pre-existing instance freeze affect replication instance file writes. * Hence this is defined as simple LSEEKWRITE. */ #define REPL_INST_LSEEKWRITE LSEEKWRITE #define REPL_INST_AVAILABLE(GD_PTR) (repl_inst_get_name((char *)replpool_id.instfilename, &full_len, \ SIZEOF(replpool_id.instfilename), return_on_error, GD_PTR)) /* Inline Functions */ static inline boolean_t instance_freeze_honored(sgmnt_addrs *csa, jnlpool_addrs_ptr_t *lcl_jnlpool_out) { jnlpool_addrs_ptr_t lcl_jnlpool; GBLREF jnlpool_addrs_ptr_t jnlpool; assert(NULL != csa); *lcl_jnlpool_out = lcl_jnlpool = JNLPOOL_FROM(csa); return ((NULL != lcl_jnlpool) && (NULL != lcl_jnlpool->jnlpool_ctl) && (REPL_ALLOWED(csa->hdr) || mupip_jnl_recover /* recover or rollback */ || ((NULL != csa->nl) && (csa->nl->onln_rlbk_pid)))); } static inline void wait_for_repl_inst_unfreeze_nocsa_jpl(jnlpool_addrs_ptr_t jpl) { GBLREF int4 exit_state; GBLREF int4 exi_condition; assert((NULL != jpl) && (NULL != jpl->jnlpool_ctl)); /* If this region is not replicated, do not care for instance freezes */ while (jpl->jnlpool_ctl->freeze) { if (exit_state != 0) { forced_exit_err_display(); EXIT(-exi_condition); } SHORT_SLEEP(SLEEP_INSTFREEZEWAIT); DEBUG_ONLY(CLEAR_FAKE_ENOSPC_IF_MASTER_DEAD); } } static inline void wait_for_repl_inst_unfreeze(sgmnt_addrs *csa) { gd_region *reg; jnlpool_addrs_ptr_t local_jnlpool; /* needed by INSTANCE_FREEZE_HONORED */ char time_str[CTIME_BEFORE_NL + 2]; /* for GET_CUR_TIME macro */ DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(NULL != csa); if (INSTANCE_FREEZE_HONORED(csa, local_jnlpool)) { #ifdef underconstruction if (TREF(in_mupip_integ) && !TREF(integ_cannotskip_crit)) { TREF(instance_frozen_crit_skipped) = TRUE; return; /* MUPIP INTEG. Don't wait any further for the freeze to be lifted. */ } #endif reg = csa->region; if (!IS_GTM_IMAGE) { GET_CUR_TIME(time_str); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_MUINSTFROZEN, 5, CTIME_BEFORE_NL, &time_str[0], local_jnlpool->repl_inst_filehdr->inst_info.this_instname, DB_LEN_STR(reg)); } WAIT_FOR_REPL_INST_UNFREEZE_NOCSA_JPL(local_jnlpool); if (!IS_GTM_IMAGE) { GET_CUR_TIME(time_str); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_MUINSTUNFROZEN, 5, CTIME_BEFORE_NL, &time_str[0], local_jnlpool->repl_inst_filehdr->inst_info.this_instname, DB_LEN_STR(reg)); } } } #endif /* #ifndef ANTICIPATORY_FREEZE_H */ fis-gtm-V7.0-005/sr_port/arit.h0000755000032200000250000000674214342376331015167 0ustar librarygtc/**************************************************************** * * * 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. * * * ****************************************************************/ /* arit.h : defines constants for arithmetic operations. gdsroot.h has a definition for the MAX_NUM_SUBSC_LEN that depends on the number of significant digits available. If it changes from 18, the define must be updated */ #define NUM_DEC_DG_1L 9 /* Number of decimal digits in a int4 */ #define NUM_DEC_DG_2L 18 /* Number of decimal digits in two longs */ #define EXP_IDX_BIAL (9+MV_XBIAS) /* Max. biased exponent index within a int4 */ #define EXP_IDX_BIAQ (18+MV_XBIAS) /* Max. biased exponent index within two longs */ #define EXPLO (-42+MV_XBIAS) /* Min. biased exponent */ #define EXPHI (48+MV_XBIAS) /* Max. biased exponent */ /* Note: The above values for EXPLO and EXPHI set up a range of 20 thru 109 which still leaves room for expansion * in the 7-bit exponent whose full range is 0 thru 127. One might be tempted to increase EXPLO/EXPHI to accommodate * the available range but should not because these values also affect how numeric subscripts are stored in the database. * * The entire numeric range currently supported by GT.M along with how it represents them internally as well as * inside the database (the first byte of the numeric subscript) is captured in the table below. * * -------------------------|---------------------------------------------------------------------------------------- * Numeric value | ... [-1E46, -1E-43] ... [0] ... [1E-43, 1E46] ... * mval.e representation | ... [ 109, 20] ... [0] ... [ 20, 109] ... * mval.sgn representation | ... [ 1, 1] ... [0] ... [ 0, 0] ... * | | | | | | | | * | | | | | | | | * | | | | | | | | * | v v v v v v v * subscript representation | [0x00, 0x11] [0x12, 0x6b] [0x6c,0x7F] [0x80] [0x81, 0x93] [0x94, 0xED] [0xEE, 0xFF] * in database | * -------------------------|---------------------------------------------------------------------------------------- * * Any increase in EXPHI will encroach the currently unused interval [0x00,0x11] and has to be done with caution * as a few of those are used for different purposes (0x01 to represent a null subscript in standard null collation, * 0x02 to be used for spanning node subscripts etc.) */ #define EXP_INT_OVERF (7+MV_XBIAS) /* Upper bound on MV_INT numbers */ #define EXP_INT_UNDERF (-3+MV_XBIAS) /* Lower bound on MV_INT numbers (includes * integers & fractions upto 3 decimal places */ #define MANT_LO 100000000 /* mantissa.1 >= MANT_LO */ #define MANT_HI 1000000000 /* mantissa.1 < MANT_HI , mantissa.0 < MANT_HI */ #define INT_HI 1000000 /* -INT_HI < mv_int < INT_HI */ fis-gtm-V7.0-005/sr_port/asc2i.c0000755000032200000250000000175214342376331015220 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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" GBLREF seq_num seq_num_zero; GBLREF seq_num seq_num_minus_one; int4 asc2i(uchar_ptr_t p, int4 len) { uchar_ptr_t c; int4 ret; ret = 0; for (c = p + len; c > p; p++) { if (*p > '9' || *p < '0') return -1; ret = ret * 10; ret += *p - '0'; } return ret; } qw_num asc2l(uchar_ptr_t p, int4 len) { uchar_ptr_t c; qw_num ret; QWASSIGN(ret, seq_num_zero); for (c = p + len; c > p; p++) { if (*p > '9' || *p < '0') return seq_num_minus_one; QWMULBYDW(ret, ret, 10); QWINCRBYDW(ret, *p - '0'); } return ret; } fis-gtm-V7.0-005/sr_port/asc_hex2i.c0000755000032200000250000000257014342376331016063 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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" LITREF unsigned char lower_to_upper_table[]; unsigned int asc_hex2i(uchar_ptr_t p, int len) { uchar_ptr_t c; unsigned char ch; int ret; ret = 0; for (c = p + len; c > p; p++) { if (('0' <= *p) && ('9' >= *p)) ret = (ret << 4) + (*p - '0'); else { ch = lower_to_upper_table[*p]; if (('A' <= ch) && ('F' >= ch)) ret = (ret << 4) + ch - 'A' + 10; else return (unsigned int)-1; } } return ret; } #ifndef VMS /* Routine identical to asc_hex2i() but with 8 byte accumulator and return type */ gtm_uint64_t asc_hex2l(uchar_ptr_t p, int len) { uchar_ptr_t c; unsigned char ch; gtm_uint64_t ret; ret = 0; for (c = p + len; c > p; p++) { if (('0' <= *p) && ('9' >= *p)) ret = (ret << 4) + (*p - '0'); else { ch = lower_to_upper_table[*p]; if (('A' <= ch) && ('F' >= ch)) ret = (ret << 4) + ch - 'A' + 10; else return (gtm_uint64_t)-1; } } return ret; } #endif fis-gtm-V7.0-005/sr_port/aswp.h0000755000032200000250000000153114342376331015171 0ustar librarygtc/**************************************************************** * * * Copyright 2005 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. * * * ****************************************************************/ #ifndef ASWP_H_INCLUDED #define ASWP_H_INCLUDED #ifdef __hppa int aswp3(sm_int_ptr_t /* location */, int4 /* value */, sm_global_latch_ptr_t /* latch */); #define ASWP(A,B,C) aswp3((sm_int_ptr_t)(A), B, C) #else int aswp(sm_int_ptr_t /* location */, int4 /* value */); int aswp_secshr(sm_int_ptr_t /* location */, int4 /* value */); #define ASWP(A,B,C) aswp((sm_int_ptr_t)(A), B) #endif #endif fis-gtm-V7.0-005/sr_port/azl_geturxlab.c0000755000032200000250000000140214342376331017052 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "urx.h" bool azl_geturxlab (addr, rp) char *addr; urx_rtnref *rp; { urx_labref *lp; assert (rp->lab); for (lp = rp->lab; lp; lp = lp->next) { urx_addr *ap; for (ap = lp->addr; ap; ap = ap->next) if (addr == (char *)ap->addr) break; if (ap) return TRUE; } return FALSE; } fis-gtm-V7.0-005/sr_port/azl_geturxrtn.c0000755000032200000250000000223714342376331017126 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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. * * * ****************************************************************/ /* This routine is no longer used in $sr_unix for autorelink-enabled platforms. If/when the * last use of this routine disappears (list as of 03/2015 is sr_i386 and sr_ia64) this * routine is eligible for nixing. */ #include "mdef.h" #include "urx.h" GBLREF urx_rtnref urx_anchor; bool azl_geturxrtn(char *addr, mstr *rname, urx_rtnref **rp) { assert(urx_anchor.len == 0); for (*rp = urx_anchor.next; *rp; *rp = (*rp)->next) { urx_addr *ap; for (ap = (*rp)->addr; ap; ap = ap->next) if (addr == (char *)ap->addr) break; if (ap) { rname->len = (*rp)->len; rname->addr = (char *)&(*rp)->name[0]; return TRUE; } } return FALSE; } fis-gtm-V7.0-005/sr_port/backup_block.c0000755000032200000250000001365714342376331016645 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_string.h" #include "gtm_stdio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdsblk.h" #include "shmpool.h" #include "memcoherency.h" #include "mupipbckup.h" GBLREF uint4 process_id; boolean_t backup_block(sgmnt_addrs *csa, block_id blk, cache_rec_ptr_t backup_cr, sm_uc_ptr_t backup_blk_p) { uint4 bsiz; int4 required; shmpool_buff_hdr_ptr_t sbufh_p; shmpool_blk_hdr_ptr_t sblkh_p; boolean_t is_bg; sgmnt_data_ptr_t csd; sm_uc_ptr_t bkp_src_blk; trans_num bkp_tn; # ifdef DEBUG char *backup_encrypt_hash_ptr, *db_encrypt_hash_ptr; boolean_t backup_uses_new_key, db_uses_new_key, backup_was_encrypted, db_was_encrypted; # endif sgmnt_data_ptr_t backup_csd; csd = csa->hdr; sbufh_p = csa->shmpool_buffer; backup_csd = &sbufh_p->shadow_file_header; if (backup_csd->trans_hist.total_blks <= blk) return TRUE; /* Block to be backed up was created AFTER the backup started. So no need to backup. */ is_bg = (dba_bg == csd->acc_meth); assert(is_bg || (dba_mm == csd->acc_meth)); /* Should have EITHER backup cr (BG mode) or buffer pointer (MM mode) */ assert((is_bg && (NULL != backup_cr) && (NULL == backup_blk_p)) || (!is_bg && (NULL == backup_cr) && (NULL != backup_blk_p))); if (is_bg) { /* Get buffer address from the cache record */ VMS_ONLY(assert(0 == backup_cr->shmpool_blk_off)); assert(backup_cr->in_cw_set);/* ensure the buffer has been pinned (from preemption in db_csh_getn) */ backup_blk_p = GDS_ANY_REL2ABS(csa, backup_cr->buffaddr); } bsiz = ((blk_hdr_ptr_t)(backup_blk_p))->bsiz; assert(bsiz <= sbufh_p->blk_size); /* Obtain block from shared memory pool. If we can't get the block, then backup will be effectively terminated. */ sblkh_p = shmpool_blk_alloc(csa->region, SHMBLK_BACKUP); if (((shmpool_blk_hdr_ptr_t)-1L) == sblkh_p) return FALSE; /* Backup died for whatever reason. Backup failure already dealt with in shmpool_blk_alloc() */ /* Fill the block we have been assigned in before marking it valid */ sblkh_p->blkid = blk; if (is_bg) { assert(NULL != backup_cr); sblkh_p->use.bkup.ondsk_blkver = backup_cr->ondsk_blkver; assert(((blk_hdr_ptr_t)backup_blk_p)->bsiz >= SIZEOF(blk_hdr)); } else /* For MM version, no dynamic conversions take place so just record block as we know it is */ sblkh_p->use.bkup.ondsk_blkver = csd->desired_db_format; bkp_src_blk = backup_blk_p; /* Adjust bsiz to be within database block size range. Asserts above will ensure this IS the case for DBG. */ if (bsiz < SIZEOF(blk_hdr)) bsiz = SIZEOF(blk_hdr); else if (bsiz > sbufh_p->blk_size) bsiz = sbufh_p->blk_size; /* If the data in the backup needs to be encrypted, fetch it from the encrypted twin buffer. It should be safe to look * directly at the file header for encryption settings because a concurrent MUPIP REORG -ENCRYPT cannot update the * encryption cycle until phase 2 of the commit is complete. Even though the encryption settings in the file header * might be different from those in the backup file header, as far as the current block is concerned, it should be * encrypted with the same key in both file headers (that is, it is not possible that the the encryption cycle has changed * multiple times since the backup has started and that is because the first reorg encrypt that changed the cycle would * have to reencrypt every block in the database and would have to invoke backup_block for each block so the cycle * difference could be at most only one and not more). So, no need to reencrypt. Assert accordingly. */ bkp_tn = ((blk_hdr *)bkp_src_blk)->tn; if (NEEDS_ANY_KEY(csd, bkp_tn)) { # ifdef DEBUG DBG_ENSURE_PTR_IS_VALID_GLOBUFF(csa, csd, backup_blk_p); backup_uses_new_key = NEEDS_NEW_KEY(backup_csd, bkp_tn); db_uses_new_key = NEEDS_NEW_KEY(csd, bkp_tn); backup_was_encrypted = IS_ENCRYPTED(backup_csd->is_encrypted); db_was_encrypted = IS_ENCRYPTED(csd->is_encrypted); if (backup_uses_new_key) backup_encrypt_hash_ptr = backup_csd->encryption_hash2; else if (backup_was_encrypted) backup_encrypt_hash_ptr = backup_csd->encryption_hash; else assert(FALSE); /* If db was unencrypted and unencryptable at start of backup, we should never be here */ if (db_uses_new_key) db_encrypt_hash_ptr = csd->encryption_hash2; else if (db_was_encrypted) db_encrypt_hash_ptr = csd->encryption_hash; else assert(FALSE); assert(memcmp(db_encrypt_hash_ptr, EMPTY_GTMCRYPT_HASH, GTMCRYPT_HASH_LEN)); /* The below assert implies we can safely copy the encrypted global buffer to the backup file */ assert(!memcmp(backup_encrypt_hash_ptr, db_encrypt_hash_ptr, GTMCRYPT_HASH_LEN)); # endif bkp_src_blk = GDS_ANY_ENCRYPTGLOBUF(backup_blk_p, csa); DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(csa, csd, bkp_src_blk); /* Copy block information to data portion of shmpool block just following header. No reencryption needed. */ memcpy((sblkh_p + 1), bkp_src_blk, bsiz); } else { /* Copy block information to data portion of shmpool block just following header. */ memcpy((sblkh_p + 1), bkp_src_blk, bsiz); } /* Need a write coherency fence here as we want to make sure the above info is stored and * reflected to other processors before we mark the block valid. */ SHM_WRITE_MEMORY_BARRIER; sblkh_p->valid_data = TRUE; /* And another write barrier to advertise its cleanliness to other processors */ SHM_WRITE_MEMORY_BARRIER; return TRUE; } fis-gtm-V7.0-005/sr_port/bg_update.h0000755000032200000250000000153714342376331016157 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef BG_UPDATE_H_INCLUDED #define BG_UPDATE_H_INCLUDED enum cdb_sc bg_update(cw_set_element *cs, trans_num ctn, trans_num effective_tn, sgm_info *si); enum cdb_sc bg_update_phase1(cw_set_element *cs, trans_num ctn, sgm_info *si); enum cdb_sc bg_update_phase2(cw_set_element *cs, trans_num ctn, trans_num effective_tn, sgm_info *si); #endif fis-gtm-V7.0-005/sr_port/bit_clear.c0000755000032200000250000000153714342376331016144 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "gdsroot.h" #include "bit_clear.h" static unsigned char bit_clear_mask[8] = {127, 191, 223, 239, 247, 251, 253, 254}; uint4 bit_clear (block_id bit, sm_uc_ptr_t base) { uint4 retval; sm_uc_ptr_t ptr; ptr = base + (bit / 8); retval = (1 << (bit & 7)) & *ptr; *ptr &= ~(1 << (bit & 7)); return (0 != retval); } fis-gtm-V7.0-005/sr_port/bit_clear.h0000755000032200000250000000126014342376331016142 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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. * * * ****************************************************************/ #ifndef BIT_CLEAR_DEFINED #include "gdsroot.h" /* Declare parms for bit_clear.c */ uint4 bit_clear (block_id bit, sm_uc_ptr_t base); #define BIT_CLEAR_DEFINED #endif fis-gtm-V7.0-005/sr_port/bit_set.c0000755000032200000250000000140314342376331015641 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "gdsroot.h" #include "bit_set.h" uint4 bit_set (block_id bit, sm_uc_ptr_t base) { int4 retval; sm_uc_ptr_t ptr; ptr = base + (bit / 8); retval = (1 << (bit & 7)) & *ptr; *ptr |= 1 << (bit & 7); return (0 != retval); } fis-gtm-V7.0-005/sr_port/bit_set.h0000755000032200000250000000125014342376331015646 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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. * * * ****************************************************************/ #ifndef BIT_SET_DEFINED #include "gdsroot.h" /* Declare parms for bit_set.c */ uint4 bit_set (block_id bit, sm_uc_ptr_t base); #define BIT_SET_DEFINED #endif fis-gtm-V7.0-005/sr_port/bm_find_blk.c0000755000032200000250000001004214342376331016435 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "gdsroot.h" #include "gdsblk.h" #include "gdsbml.h" #define MAX_FFS_SIZE 32 /* Returns the location of the first set bit in the field. */ /* The search starts at the hint and does not wrap */ int4 bm_find_blk(int4 hint, sm_uc_ptr_t base_addr, int4 total_bits, boolean_t *used) { int4 bits; sm_uc_ptr_t ptr, top; unsigned char valid; assert(hint < total_bits); total_bits *= BML_BITS_PER_BLK; /* Currently bm_find_blk has been coded to assume that if "hint" is * non-zero then it is less than total_bits. We better assert that. */ if (hint) { ptr = base_addr + ((hint * BML_BITS_PER_BLK) / 8); top = base_addr + ((total_bits + 7) / 8) - 1; if (ptr == top) { bits = total_bits % 8; if (bits == 6) valid = *ptr & 63; else if (bits == 4) valid = *ptr & 15; else if (bits == 2) valid = *ptr & 3; else valid = *ptr; } else valid = *ptr; switch (hint % (8 / BML_BITS_PER_BLK)) { case 0: break; case 1: valid = valid & 252; break; case 2: valid = valid & 240; break; case 3: valid = valid & 192; break; } if (valid) { if (valid & 1) { if (valid & 2) *used = TRUE; else *used = FALSE; return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK)); } else if (valid & 4) { if (valid & 8) *used = TRUE; else *used = FALSE; return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 1); } else if (valid & 16) { if (valid & 32) *used = TRUE; else *used = FALSE; return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 2); } else { if (valid & 128) *used = TRUE; else *used = FALSE; return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 3); } } ptr++; } else ptr = base_addr; for (top = base_addr + (total_bits +7) / 8 - 1; ptr < top; ptr++) { if (*ptr) { if (*ptr & 1) { if (*ptr & 2) *used = TRUE; else *used = FALSE; return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK)); } else if (*ptr & 4) { if (*ptr & 8) *used = TRUE; else *used = FALSE; return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 1); } else if (*ptr & 16) { if (*ptr & 32) *used = TRUE; else *used = FALSE; return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 2); } else { if (*ptr & 128) *used = TRUE; else *used = FALSE; return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 3); } } } if ((ptr == top) && *ptr) /* Special processing for last byte--may be only partially valid , if had hint may */ { bits = total_bits % 8; /* have already done last byte, then ptr will be greater than top */ if (bits == 6) valid = *ptr & 63; else if (bits == 4) valid = *ptr & 15; else if (bits == 2) valid = *ptr & 3; else valid = *ptr; if (valid) { if (valid & 1) { if (valid & 2) *used = TRUE; else *used = FALSE; return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK)); } else if (valid & 4) { if (valid & 8) *used = TRUE; else *used = FALSE; return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 1); } else if (valid & 16) { if (valid & 32) *used = TRUE; else *used = FALSE; return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 2); } else { if (valid & 128) *used = TRUE; else *used = FALSE; return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 3); } } } return NO_FREE_SPACE; } fis-gtm-V7.0-005/sr_port/bm_getfree.c0000755000032200000250000003536314342376331016323 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /************************************************************************************** * * MODULE NAME: BM_GETFREE.C * * CALLING SEQUENCE: block_id bm_getfree(hint, blk_used, cw_work, cs, cw_depth_ptr) * * DESCRIPTION: Takes a block id as a hint and tries to find a * free block, looking first at the hint, then in the same local * bitmap, and finally in every local map. If it finds a free block, * it looks for the bitmap in the working set, puts it in the working * set if it is not there, and updates the map used, and marks it with * the transaction number, and updates the boolean * pointed to by blk_used to indicate if the free block had been used previously. * * HISTORY: * ***************************************************************************************/ #include "mdef.h" #include "gtm_string.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsbml.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "min_max.h" #include "gdsblkops.h" #include "filestruct.h" #include "gdscc.h" #include "gdskill.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for ua_list for ENSURE_UPDATE_ARRAY_SPACE macro */ #include "iosp.h" #include "bmm_find_free.h" /* Include prototypes */ #include "wcs_mm_recover.h" #include "t_qread.h" #include "t_write_map.h" #include "bit_clear.h" #include "send_msg.h" #include "bm_getfree.h" #include "gdsfilext.h" #include "anticipatory_freeze.h" #include "interlock.h" GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF char *update_array, *update_array_ptr; GBLREF gd_region *gv_cur_region; GBLREF unsigned char rdfail_detail; GBLREF uint4 dollar_tlevel; GBLREF uint4 update_array_size, cumul_update_array_size; GBLREF unsigned int t_tries; GBLREF jnlpool_addrs_ptr_t jnlpool; error_def(ERR_DBBADFREEBLKCTR); error_def(ERR_DBMBMINCFREFIXED); block_id bm_getfree(block_id hint_arg, boolean_t *blk_used, unsigned int cw_work, cw_set_element *cs, int *cw_depth_ptr) { cw_set_element *cs1; sm_uc_ptr_t bmp; block_id bml, extension_size, hint, hint_cycled, hint_limit, lcnt, local_maps, offset; block_id_ptr_t b_ptr; gd_region *baseDBreg; int cw_set_top, depth; unsigned int n_decrements = 0; trans_num ctn; int4 free_bit, map_size, new_allocation, status; ublock_id total_blks; uint4 space_needed, was_crit; sgmnt_addrs *baseDBcsa; srch_blk_status blkhist; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; total_blks = (dba_mm == cs_data->acc_meth) ? cs_addrs->total_blks : cs_addrs->ti->total_blks; hint = (((ublock_id)hint_arg >= total_blks) ? 1 : hint_arg); /* avoid any chance of treating TP chain.flag as a sign */ hint_cycled = DIVIDE_ROUND_UP(total_blks, BLKS_PER_LMAP); hint_limit = DIVIDE_ROUND_DOWN(hint, BLKS_PER_LMAP); local_maps = hint_cycled + 2; /* for (up to) 2 wraps */ for (lcnt = local_maps + 1; lcnt ; lcnt--) /* loop control counts down for slight efficiency */ { bml = bmm_find_free(hint / BLKS_PER_LMAP, (sm_uc_ptr_t)MM_ADDR(cs_data), local_maps); if ((NO_FREE_SPACE == bml) || (bml >= hint_cycled)) { /* if no free space or might have looped to original map, extend */ if ((NO_FREE_SPACE != bml) && (hint_limit < hint_cycled)) { hint_cycled = hint_limit; hint = 1; continue; } was_crit = cs_addrs->now_crit; if (!was_crit) { /* We are working up to a file extension, which requires crit anyway, we need a consistent * view of our last minute checks, and in case we are updating statsdb_allocation we want * that to be atomic with the extension itself. Strictly speaking, statsdb_allocation isn't * associated with the current (statsdb) region, but barring a concurrent MUPIP SET this should * be sufficient. */ # ifdef DEBUG if ((WBTEST_ENABLED(WBTEST_MM_CONCURRENT_FILE_EXTEND) && dollar_tlevel && !MEMCMP_LIT(gv_cur_region->rname, "DEFAULT")) || (WBTEST_ENABLED(WBTEST_WSSTATS_PAUSE) && (10 == gtm_white_box_test_case_count) && !MEMCMP_LIT(gv_cur_region->rname, "DEFAULT"))) { /* Sync with copy in gdsfilext() * Unset the env shouldn't affect the parent, it reads env just once at process startup. */ if(WBTEST_ENABLED(WBTEST_WSSTATS_PAUSE)) unsetenv("gtm_white_box_test_case_enable"); SYSTEM("$gtm_dist/mumps -run $gtm_wbox_mrtn"); assert(1 == cs_addrs->nl->wbox_test_seq_num); /* should have been set by mubfilcpy */ /* signal mupip backup to stop sleeping in mubfilcpy */ cs_addrs->nl->wbox_test_seq_num = 2; } # endif while (!cs_addrs->now_crit) { grab_crit(gv_cur_region, WS_12); if (FROZEN_CHILLED(cs_addrs)) DO_CHILLED_AUTORELEASE(cs_addrs, cs_data); assert(FROZEN(cs_data) || !cs_addrs->jnlpool || (cs_addrs->jnlpool == jnlpool)); if (FROZEN(cs_data) || IS_REPL_INST_FROZEN) { rel_crit(gv_cur_region); while (FROZEN(cs_data) || IS_REPL_INST_FROZEN) { hiber_start(1000); if (FROZEN_CHILLED(cs_addrs) && CHILLED_AUTORELEASE(cs_addrs)) break; } } } } if (total_blks != cs_addrs->ti->total_blks) { /* File extension or MM switch detected. Rather than try to reset the loop, just recurse. */ CHECK_MM_DBFILEXT_REMAP_IF_NEEDED(cs_addrs, gv_cur_region); rel_crit(gv_cur_region); return (dba_mm == cs_data->acc_meth) ? FILE_EXTENDED : bm_getfree(hint_arg, blk_used, cw_work, cs, cw_depth_ptr); } if (IS_STATSDB_CSA(cs_addrs)) { /* Double the allocation size for statsdb regions to reduce tp_restart calls */ assert(cs_addrs->total_blks == cs_addrs->ti->total_blks); extension_size = cs_addrs->total_blks; } else extension_size = cs_data->extension_size; # ifdef DEBUG TREF(in_bm_getfree_gdsfilext) = TRUE; # endif status = GDSFILEXT(extension_size, total_blks, TRANS_IN_PROG_TRUE); # ifdef DEBUG TREF(in_bm_getfree_gdsfilext) = FALSE; # endif if (SS_NORMAL != status) { if (!was_crit) rel_crit(gv_cur_region); if (EXTEND_SUSPECT == status) continue; return status; } if (IS_STATSDB_CSA(cs_addrs)) { /* Save the new allocation size to the database file header. * Use this allocation size the next time we create statsdb for the region. */ new_allocation = (int4)(total_blks * 2); STATSDBREG_TO_BASEDBREG(gv_cur_region, baseDBreg); baseDBcsa = &FILE_INFO(baseDBreg)->s_addrs; assert(new_allocation > baseDBcsa->hdr->statsdb_allocation); baseDBcsa->hdr->statsdb_allocation = new_allocation; } if (!was_crit) rel_crit(gv_cur_region); if (dba_mm == cs_data->acc_meth) return (FILE_EXTENDED); hint = total_blks; total_blks = cs_addrs->ti->total_blks; hint_cycled = DIVIDE_ROUND_UP(total_blks, BLKS_PER_LMAP); local_maps = hint_cycled + 2; /* for (up to) 2 wraps */ /* * note that you can make an optimization of not going back over the whole database and going over * only the extended section. but since it is very unlikely that a free block won't be found * in the extended section and the fact that we are starting from the extended section in either * approach and the fact that we have an assertpro to check that we don't have a lot of * free blocks while doing an extend and the fact that it is very easy to make the change to do * a full-pass, the full-pass solution is currently being implemented */ lcnt = local_maps + 2; /* allow it one extra pass to ensure that it can take advantage of the entension */ n_decrements++; /* used only for debugging purposes */ continue; } bml *= BLKS_PER_LMAP; if (ROUND_DOWN2(hint, BLKS_PER_LMAP) != bml) { /* not within requested map */ if ((bml < hint) && (hint_cycled)) /* wrap? - second one should force an extend for sure */ hint_cycled = (hint_limit < hint_cycled) ? hint_limit: 0; hint = bml + 1; /* start at beginning */ } if (ROUND_DOWN2(total_blks, BLKS_PER_LMAP) == bml) { /* Can be cast because result of (total_blks - bml) should never be larger then BLKS_PER_LMAP */ assert(BLKS_PER_LMAP >= (total_blks - bml)); map_size = (int4)(total_blks - bml); } else map_size = BLKS_PER_LMAP; if (dollar_tlevel) { depth = cw_work; cw_set_top = *cw_depth_ptr; if (depth < cw_set_top) tp_get_cw(cs, cw_work, &cs1); for (; depth < cw_set_top; depth++, cs1 = cs1->next_cw_set) { /* do tp front to back because list is more efficient than tp_get_cw and forward pointers exist */ if (bml == cs1->blk) { TRAVERSE_TO_LATEST_CSE(cs1); break; } } if (depth >= cw_set_top) { assert(cw_set_top == depth); depth = 0; } } else { for (depth = *cw_depth_ptr - 1; depth >= cw_work; depth--) { /* do non-tp back to front, because of adjacency */ if (bml == (cs + depth)->blk) { cs1 = cs + depth; break; } } if (depth < cw_work) { assert(cw_work - 1 == depth); depth = 0; } } if (0 == depth) { ctn = cs_addrs->ti->curr_tn; if (!(bmp = t_qread(bml, (sm_int_ptr_t)&blkhist.cycle, &blkhist.cr))) return MAP_RD_FAIL; if ((BM_SIZE(BLKS_PER_LMAP) != ((blk_hdr_ptr_t)bmp)->bsiz) || (LCL_MAP_LEVL != ((blk_hdr_ptr_t)bmp)->levl)) { assert(CDB_STAGNATE > t_tries); rdfail_detail = cdb_sc_badbitmap; return MAP_RD_FAIL; } offset = 0; } else { bmp = cs1->old_block; b_ptr = (block_id_ptr_t)(cs1->upd_addr); b_ptr += cs1->reference_cnt - 1; offset = *b_ptr + 1; } if (offset < map_size) { /* offset can be downcast because to get here it must be less then map_size * which is constrained to never be larger then BLKS_PER_LMAP */ assert(offset == (int4)offset); free_bit = bm_find_blk((int4)offset, (sm_uc_ptr_t)bmp + SIZEOF(blk_hdr), map_size, blk_used); if (MAP_RD_FAIL == free_bit) return MAP_RD_FAIL; } else free_bit = NO_FREE_SPACE; if (NO_FREE_SPACE != free_bit) break; if ((hint = (bml + BLKS_PER_LMAP)) >= total_blks) /* if map is full, start at 1st blk in next map */ { /* wrap - second one should force an extend for sure */ hint = 1; if (hint_cycled) hint_cycled = (hint_limit < hint_cycled) ? hint_limit: 0; } if ((0 == depth) && cs_addrs->now_crit) /* if it's from the cw_set, its state is murky */ { assert(FALSE); send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(3) ERR_DBMBMINCFREFIXED, 1, bml); bit_clear(bml / BLKS_PER_LMAP, MM_ADDR(cs_data)); /* repair master map error */ } } /* If not in the final retry, it is possible that free_bit is >= map_size, e.g., if the buffer holding the bitmap block * gets recycled with a non-bitmap block in which case the bit that bm_find_blk returns could be greater than map_size. * But, this should never happen in final retry. */ if ((map_size <= free_bit) && (CDB_STAGNATE <= t_tries)) { /* Bad free bit. */ assert((NO_FREE_SPACE == free_bit) && (!lcnt)); /* All maps full, should have extended */ assertpro(FALSE); } if (0 != depth) { b_ptr = (block_id_ptr_t)(cs1->upd_addr); b_ptr += cs1->reference_cnt++; *b_ptr = free_bit; } else { space_needed = (BLKS_PER_LMAP + 1) * SIZEOF(block_id); if (dollar_tlevel) { ENSURE_UPDATE_ARRAY_SPACE(space_needed); /* have brackets for "if" for macros */ } BLK_ADDR(b_ptr, space_needed, block_id); memset(b_ptr, 0, space_needed); *b_ptr = (block_id)free_bit; blkhist.blk_num = bml; blkhist.buffaddr = bmp; /* cycle and cr have already been assigned from t_qread */ t_write_map(&blkhist, (uchar_ptr_t)b_ptr, ctn, 1); /* last parameter 1 is what cs->reference_cnt gets set to */ } return (bml + free_bit); } /* This routine returns whether the free_blocks counter in the file-header is ok (TRUE) or not (FALSE). * If not, it corrects it. This assumes cs_addrs, cs_data and gv_cur_region to point to the region of interest. * It also assumes that the master-map is correct and finds out non-full local bitmaps and counts the number of * free blocks in each of them and sums them up to determine the perceived correct free_blocks count. * The reason why this is ok is that even if the master-map incorrectly reports a local bitmap as full, our new free_blocks * count will effectively make the free space in that local-bitmap invisible and make a gdsfilext necessary and valid. * A later mupip integ will scavenge that invisible space for us. The worst that can therefore happen is that we will transiently * not be using up existing space. But we will always ensure that the free_blocks counter goes in sync with the master-map. */ boolean_t is_free_blks_ctr_ok(void) { block_id bml, free_bml, local_maps, total_blks, free_blocks; boolean_t blk_used; cache_rec_ptr_t cr; int4 free_bit, maxbitsthismap, cycle; sm_uc_ptr_t bmp; assert(&FILE_INFO(gv_cur_region)->s_addrs == cs_addrs && cs_addrs->hdr == cs_data && cs_addrs->now_crit); total_blks = (dba_mm == cs_data->acc_meth) ? cs_addrs->total_blks : cs_addrs->ti->total_blks; local_maps = DIVIDE_ROUND_UP(total_blks, BLKS_PER_LMAP); for (free_blocks = 0, free_bml = 0; free_bml < local_maps; free_bml++) { bml = bmm_find_free((uint4)free_bml, (sm_uc_ptr_t)MM_ADDR(cs_data), local_maps); if (bml < free_bml) break; free_bml = bml; bml *= BLKS_PER_LMAP; if (!(bmp = t_qread(bml, (sm_int_ptr_t)&cycle, &cr)) || (BM_SIZE(BLKS_PER_LMAP) != ((blk_hdr_ptr_t)bmp)->bsiz) || (LCL_MAP_LEVL != ((blk_hdr_ptr_t)bmp)->levl)) { assert(FALSE); /* In pro, we will simply skip counting this local bitmap. */ continue; } assert(free_bml <= (local_maps - 1)); /* Can be cast because result of (total_blks - bml) should never be larger then BLKS_PER_LMAP */ DEBUG_ONLY(if(free_bml == (local_maps - 1)) assert(BLKS_PER_LMAP >= (total_blks - bml))); maxbitsthismap = (free_bml != (local_maps - 1)) ? BLKS_PER_LMAP : (int4)(total_blks - bml); for (free_bit = 0; free_bit < maxbitsthismap; free_bit++) { free_bit = bm_find_blk(free_bit, (sm_uc_ptr_t)bmp + SIZEOF(blk_hdr), maxbitsthismap, &blk_used); assert(NO_FREE_SPACE <= free_bit); if (0 > free_bit) break; free_blocks++; } } assert(cs_addrs->ti->free_blocks == free_blocks); if (cs_addrs->ti->free_blocks != free_blocks) { send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(6) ERR_DBBADFREEBLKCTR, 4, DB_LEN_STR(gv_cur_region), &(cs_addrs->ti->free_blocks), &free_blocks); cs_addrs->ti->free_blocks = free_blocks; return FALSE; } return TRUE; } fis-gtm-V7.0-005/sr_port/bm_getfree.h0000755000032200000250000000130014342376331016310 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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. * * * ****************************************************************/ #ifndef BM_GETFREE_INCLUDED #define BM_GETFREE_INCLUDED block_id bm_getfree(block_id orig_hint, boolean_t *blk_used, unsigned int cw_work, cw_set_element *cs, int *cw_depth_ptr); boolean_t is_free_blks_ctr_ok(void); #endif /* BM_GETFREE_INCLUDED */ fis-gtm-V7.0-005/sr_port/bm_setmap.c0000755000032200000250000000707514342376331016172 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gdsblk.h" #include "gdsbml.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "gdsblkops.h" /* for CHECK_AND_RESET_UPDATE_ARRAY macro */ /* Include prototypes */ #include "t_qread.h" #include "t_end.h" #include "t_retry.h" #include "t_begin_crit.h" #include "t_write_map.h" #include "gvcst_map_build.h" #include "mm_read.h" GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF char *update_array, *update_array_ptr; GBLREF cw_set_element cw_set[]; GBLREF unsigned char rdfail_detail; GBLREF jnl_format_buffer *non_tp_jfb_ptr; error_def(ERR_DSEFAIL); void bm_setmap(block_id bml, block_id blk, int4 busy) { block_id bitnum; cw_set_element *cse; int lbm_status; /* local bitmap status of input "blk" i.e. BUSY or FREE or RECYCLED */ int4 reference_cnt; trans_num ctn; sm_uc_ptr_t bmp; srch_hist alt_hist; srch_blk_status blkhist; /* block-history to fill in for t_write_map which uses "blk_num", "buffaddr", "cr", "cycle" */ t_begin_crit(ERR_DSEFAIL); ctn = cs_addrs->ti->curr_tn; if (!(bmp = t_qread(bml, &blkhist.cycle, &blkhist.cr))) t_retry((enum cdb_sc)rdfail_detail); blkhist.blk_num = bml; blkhist.buffaddr = bmp; alt_hist.h[0].blk_num = 0; /* Need for calls to T_END for bitmaps */ CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ bitnum = blk - bml; assert(BLKS_PER_LMAP > bitnum); /* bitnum is a block index in a local map, but no restrictions are placed on blk and bml, * so verify that bitnum does not exceed the length of a local map */ /* Find out current status in order to determine if there is going to be a state transition */ assert(ROUND_DOWN2(blk, cs_data->bplmap) == bml); GET_BM_STATUS(bmp, bitnum, lbm_status); switch(lbm_status) { case BLK_BUSY: reference_cnt = busy ? 0 : -1; break; case BLK_FREE: case BLK_MAPINVALID: case BLK_RECYCLED: assert(BLK_MAPINVALID != lbm_status); reference_cnt = busy ? 1 : 0; break; default: assert(FALSE); break; } if (reference_cnt) { /* Initialize update array with non-zero bitnum only if reference_cnt is non-zero. */ assert(bitnum); *((block_id_ptr_t)update_array_ptr) = bitnum; update_array_ptr += SIZEOF(block_id); } /* Terminate update array unconditionally with zero bitnum. */ *((block_id_ptr_t)update_array_ptr) = 0; update_array_ptr += SIZEOF(block_id); t_write_map(&blkhist, (uchar_ptr_t)update_array, ctn, reference_cnt); if (JNL_ENABLED(cs_data)) { cse = (cw_set_element *)(&cw_set[0]); cse->new_buff = (unsigned char *)non_tp_jfb_ptr->buff; memcpy(cse->new_buff, bmp, ((blk_hdr_ptr_t)bmp)->bsiz); gvcst_map_build((block_id *)cse->upd_addr, (uchar_ptr_t)cse->new_buff, cse, cs_addrs->ti->curr_tn); cse->done = TRUE; } /* Call t_end till it succeeds or aborts (error will be reported) */ while ((trans_num)0 == t_end(&alt_hist, NULL, TN_NOT_SPECIFIED)) ; return; } fis-gtm-V7.0-005/sr_port/bm_update.h0000755000032200000250000000122114342376331016153 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef BM_UPDATE_H_INCLUDED #define BM_UPDATE_H_INCLUDED void bm_update(cw_set_element *cs, sm_uc_ptr_t lclmap, boolean_t is_mm); #endif fis-gtm-V7.0-005/sr_port/bml_busy.c0000755000032200000250000000256014342376331016031 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsbml.h" #include "gdsfhead.h" /* Include prototypes */ #include "bit_clear.h" #include "wbox_test_init.h" GBLREF boolean_t dse_running; uint4 bml_busy(block_id setbusy, sm_uc_ptr_t map) { uint4 ret, ret1; /* This function is specifically for local maps so the block index * setbusy should not be larger then BLKS_PER_LMAP */ assert(BLKS_PER_LMAP > setbusy); setbusy *= BML_BITS_PER_BLK; ret = bit_clear(setbusy, map); ret1 = bit_clear(setbusy + 1, map); /* In case of a valid snapshot, assert that only a RECYCLED or FREE block gets marked as BUSY (dse is an exception). */ assert((ret && ret1) || (ret && !ret1) || dse_running || (WBTEST_INVALID_SNAPSHOT_EXPECTED == gtm_white_box_test_case_number)); return ret; } fis-gtm-V7.0-005/sr_port/bml_find_busy.c0000755000032200000250000000666314342376331017041 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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 "gdsroot.h" #include "gdsblk.h" #include "gdsbml.h" #include "bml_find_busy.h" #define MAX_FFS_SIZE 32 /* Returns the location of the first clear bit in the field. */ /* The search starts at the hint and wraps if necessary. */ int4 bml_find_busy(int4 hint, uchar_ptr_t base_addr, int4 total_blks) { uchar_ptr_t ptr, top; unsigned char valid; int4 bits, hint_pos, total_bits, ret_val; hint_pos = hint * BML_BITS_PER_BLK; total_bits = total_blks * BML_BITS_PER_BLK; if (hint_pos >= total_bits) hint_pos = 0; hint_pos = hint_pos / 8; for (ptr = base_addr + hint_pos, top = base_addr + (total_bits + 7) / 8 - 1; ptr < top; ptr++) { if ((*ptr & 0x3) == 0) { ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK)); if (ret_val >= hint) return ret_val; } if ((*ptr & 0xc) == 0) { ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 1); if (ret_val >= hint) return ret_val; } if ((*ptr & 0x30) == 0) { ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 2); if (ret_val >= hint) return ret_val; } if ((*ptr & 0xc0) == 0) { ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 3); if (ret_val >= hint) return ret_val; } } bits = total_bits % 8; if (bits == 6) valid = *ptr | 0xc0; else if (bits == 4) valid = *ptr | 0xf0; else if (bits == 2) valid = *ptr | 0xfc; else valid = *ptr; if ((valid & 0x3) == 0) { ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK)); if (ret_val >= hint) return ret_val; } if ((valid & 0xc) == 0) { ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 1); if (ret_val >= hint) return ret_val; } if ((valid & 0x30) == 0) { ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 2); if (ret_val >= hint) return ret_val; } if ((valid & 0xc0) == 0) { ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 3); if (ret_val >= hint && ret_val <= total_blks) return ret_val; } for (ptr = base_addr, top = base_addr + hint_pos; ptr < top; ptr++) { if ((*ptr & 0x3) == 0) { return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK)); } else if ((*ptr & 0xc) == 0) { return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 1); } else if ((*ptr & 0x30) == 0) { return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 2); } else if ((*ptr & 0xc0) == 0) { return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 3); } } bits = hint % 4; if (bits == 3) valid = *ptr | 0xc0; else if (bits == 2) valid = *ptr | 0xf0; else if (bits == 1) valid = *ptr | 0xfc; else valid = *ptr; if ((valid & 0x3) == 0) return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK)); if ((valid & 0xc) == 0) return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 1); if ((valid & 0x30) == 0) return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 2); if ((valid & 0xc0) == 0) return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 3); return -1; } fis-gtm-V7.0-005/sr_port/bml_find_busy.h0000755000032200000250000000122714342376331017035 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef BML_FIND_BUSY_H_INCLUDED #define BML_FIND_BUSY_H_INCLUDED int4 bml_find_busy(int4 hint, uchar_ptr_t base_addr, int4 total_blks); #endif fis-gtm-V7.0-005/sr_port/bml_find_free.c0000755000032200000250000000505214342376331016767 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsbml.h" #include "gdsfhead.h" #define RETURN_IF_FREE(valid, ptr, base_addr) \ { \ int4 bits; \ \ if (valid) \ { \ if (valid > THREE_BLKS_BITMASK) \ bits = 3; \ else if (valid > TWO_BLKS_BITMASK) \ bits = 2; \ else if (valid > ONE_BLK_BITMASK) \ bits = 1; \ else \ bits = 0; \ return (int4)((ptr - base_addr) * (BITS_PER_UCHAR / BML_BITS_PER_BLK) + bits); \ } \ } /* Returns the location of the first set bit in the field. The search starts at the hint and wraps if necessary. * If a non-null update array is passed, that is also taken into account while figuring out if any free block is available. */ int4 bml_find_free(block_id hint, uchar_ptr_t base_addr, block_id total_bits) { uchar_ptr_t ptr, top; unsigned char valid; block_id bits; /* This function is specifically for local maps so the block indexes * hint and total_bits should not be larger then BLKS_PER_LMAP */ assert(BLKS_PER_LMAP >= total_bits); hint *= BML_BITS_PER_BLK; hint = hint / BITS_PER_UCHAR; total_bits *= BML_BITS_PER_BLK; if (hint > DIVIDE_ROUND_UP(total_bits, BITS_PER_UCHAR)) hint = 0; for (ptr = base_addr + hint, top = base_addr + DIVIDE_ROUND_UP(total_bits, BITS_PER_UCHAR) - 1; ptr < top; ptr++) { valid = *ptr; RETURN_IF_FREE(valid, ptr, base_addr); } if (*ptr) /* Special processing for last byte as may be only partially valid */ { bits = total_bits % BITS_PER_UCHAR; if (bits == 6) valid = *ptr & THREE_BLKS_BITMASK; else if (bits == 4) valid = *ptr & TWO_BLKS_BITMASK; else if (bits == 2) valid = *ptr & ONE_BLK_BITMASK; else valid = *ptr; RETURN_IF_FREE(valid, ptr, base_addr); } if (hint) { for (ptr = base_addr, top = base_addr + hint; ptr < top; ptr++) { valid = *ptr; RETURN_IF_FREE(valid, ptr, base_addr); } } return NO_FREE_SPACE; } fis-gtm-V7.0-005/sr_port/bml_free.c0000755000032200000250000000240014342376331015761 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsbml.h" #include "gdsfhead.h" /* Include prototypes */ #include "bit_set.h" #include "bit_clear.h" GBLREF boolean_t dse_running; uint4 bml_free(block_id setfree, sm_uc_ptr_t map) { uint4 ret, ret1; /* This function is specifically for local maps so the block index * setfree should not be larger then BLKS_PER_LMAP */ assert(BLKS_PER_LMAP > setfree); setfree *= BML_BITS_PER_BLK; ret = bit_set(setfree, map); ret1 = bit_clear(setfree + 1, map); /* Assert that only a BUSY or RECYCLED block gets marked as FREE (dse is an exception) */ assert((!ret && !ret1) || (ret && ret1) || dse_running); return ret; } fis-gtm-V7.0-005/sr_port/bml_init.c0000755000032200000250000000547214342376331016017 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 /* for DSK_WRITE_NOCACHE macro */ #include "gdsroot.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsbml.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF jnl_gbls_t jgbl; int4 bml_init(block_id bml) { blk_hdr_ptr_t ptr; uint4 size; uint4 status; trans_num blktn; size = BM_SIZE(cs_data->bplmap); /* Allocate full block. bml_newmap will set the write size. DSK_WRITE_NOCACHE will write part or all of it as appropriate */ ptr = (blk_hdr_ptr_t)malloc(cs_addrs->hdr->blk_size); /* We are about to create a local bitmap. Setting its block transaction number to the current database transaction * number gives us a clear history of when this bitmap got created. There are two exceptions. * 1) If before-image journaling, it implies the possibility of using backward recovery/rollback both of which can * take the database to a transaction number MUCH BEFORE the current database transaction number. In that case, the * recovered database will have DBTNTOOLG integrity errors since the bitmap block's transaction number will be greater * than the post-recovery database transaction number. Since we have no control over what transaction number backward * recovery can take the database to, we set the bitmap block transaction number to 0 (the least possible) for the * before-image journaling case. * 2) If in forward recovery, then the database current transaction number is not incremented in gdsfilext (the caller * of this function) so we have to create the local bitmap blocks with curr_tn-1 in order to avoid a DBTNTOOLG error. */ if (JNL_ENABLED(cs_data) && cs_addrs->jnl && cs_addrs->jnl->jnl_buff && cs_addrs->jnl->jnl_buff->before_images) blktn = 0; else if (jgbl.forw_phase_recovery && !JNL_ENABLED(cs_data)) /* forward recovery */ blktn = cs_data->trans_hist.curr_tn - 1; else blktn = cs_data->trans_hist.curr_tn; bml_newmap(ptr, size, blktn, cs_data->desired_db_format); /* status holds the status of any error return from DSK_WRITE_NOCACHE */ DSK_WRITE_NOCACHE(gv_cur_region, bml, (sm_uc_ptr_t)ptr, cs_data->desired_db_format, status); free(ptr); return status; } fis-gtm-V7.0-005/sr_port/bml_newmap.c0000755000032200000250000000213214342376331016331 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsbml.h" #include "gdsdbver.h" #include "gtm_multi_thread.h" /* #GTM_THREAD_SAFE : The below function (bml_newmap) is thread-safe */ void bml_newmap(blk_hdr_ptr_t ptr, uint4 size, trans_num curr_tn, enum db_ver ondsk_blkver) { sm_uc_ptr_t bptr; ptr->bver = ondsk_blkver; ptr->bsiz = size; ptr->levl = LCL_MAP_LEVL; ptr->tn = curr_tn; bptr = (sm_uc_ptr_t)ptr + SIZEOF(blk_hdr); size -= SIZEOF(blk_hdr); *bptr++ = THREE_BLKS_FREE; memset(bptr, FOUR_BLKS_FREE, size - 1); } fis-gtm-V7.0-005/sr_port/bml_recycled.c0000644000032200000250000000217714342376331016642 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2007-2019 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsbml.h" #include "gdsfhead.h" /* Include prototypes */ #include "bit_set.h" GBLREF boolean_t dse_running; uint4 bml_recycled(block_id setfree, sm_uc_ptr_t map) { uint4 ret, ret1; /* This function is specifically for local maps so the block index * setfree should not be larger then BLKS_PER_LMAP */ assert(BLKS_PER_LMAP > setfree); setfree *= BML_BITS_PER_BLK; ret = bit_set(setfree, map); ret1 = bit_set(setfree + 1, map); assert((!ret && !ret1) || dse_running); return ret; } fis-gtm-V7.0-005/sr_port/bml_status_check.c0000644000032200000250000000502614342376331017524 0ustar librarygtc/**************************************************************** * * * Copyright 2007, 2008 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 "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdsblk.h" #include "gdsbml.h" #include "gdscc.h" #include "mm_read.h" #include "bml_status_check.h" #define MAX_FFS_SIZE 32 GBLREF sgmnt_addrs *cs_addrs; /* Checks that a block that we have acquired is marked RECYCLED/FREE in the database * and that an existing block that we are updating is marked BUSY in the database. * For BG, the check is done only if the bitmap block is available in the cache AND is not being concurrently updated. */ void bml_status_check(cw_set_element *cs) { block_id bmlblk, blk; cache_rec_ptr_t bmlcr; blk_hdr_ptr_t bmlbuff; boolean_t is_mm; int4 bml_status; is_mm = (dba_mm == cs_addrs->hdr->acc_meth); assert(gds_t_create != cs->mode); if ((gds_t_acquired == cs->mode) || (gds_t_write == cs->mode)) { bmlblk = ROUND_DOWN2(cs->blk, BLKS_PER_LMAP); assert(IS_BITMAP_BLK(bmlblk)); blk = cs->blk - bmlblk; assert(blk); if (!is_mm) { bmlcr = db_csh_get(bmlblk); bmlbuff = ((NULL != bmlcr) && (CR_NOTVALID != (sm_long_t)bmlcr) && (0 > bmlcr->read_in_progress) && !bmlcr->in_tend) ? (blk_hdr_ptr_t)GDS_REL2ABS(bmlcr->buffaddr) : NULL; } else { bmlbuff = (blk_hdr_ptr_t)mm_read(bmlblk); /* mm_read would have incremented the GVSTATS n_dsk_read counter. But we dont want that to happen * because this function is invoked only in debug builds and yet we want the same counter * value for both pro and dbg builds. So undo that action immediately. */ INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_dsk_read, (gtm_uint64_t)-1);/* note the -1 causes the undo */ assert(NULL != bmlbuff); } if (NULL != bmlbuff) { assert(LCL_MAP_LEVL == bmlbuff->levl); assert(BM_SIZE(cs_addrs->hdr->bplmap) == bmlbuff->bsiz); GET_BM_STATUS(bmlbuff, blk, bml_status); assert(BLK_MAPINVALID != bml_status); assert((gds_t_acquired != cs->mode) || (BLK_BUSY != bml_status)); assert((gds_t_acquired == cs->mode) || (BLK_BUSY == bml_status)); } } } fis-gtm-V7.0-005/sr_port/bml_status_check.h0000644000032200000250000000120114342376331017520 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2007-2018 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. * * * ****************************************************************/ #ifndef BML_STATUS_CHECK_H_INCLUDED #define BML_STATUS_CHECK_H_INCLUDED void bml_status_check(cw_set_element *cs); #endif fis-gtm-V7.0-005/sr_port/bmm_find_free.c0000755000032200000250000000265414342376331016775 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "gdsroot.h" #include "gdsblk.h" #include "gdsbml.h" #include "min_max.h" #include "gtm_ffs.h" #include "bmm_find_free.h" #define MAX_FFS_SIZE 32 /* Returns the location of the first set bit in the field. */ /* The search starts at the hint and wraps if necessary. */ block_id bmm_find_free(block_id hint, uchar_ptr_t base_addr, block_id total_bits) { block_id start, top, width, answer; if (hint >= total_bits) hint = 0; for (start = hint, top = total_bits; top; start = 0, top = hint, hint = 0) { /* one or two passes through outer loop; second is a wrap to the beginning */ for (width = MIN(top, ROUND_DOWN2(start, MAX_FFS_SIZE) + MAX_FFS_SIZE) - start; width > 0; start += width, width = MIN(top - start, MAX_FFS_SIZE)) { answer = gtm_ffs(start, base_addr, width); if (NO_FREE_SPACE != answer) return answer; } } assert(NO_FREE_SPACE == answer); return NO_FREE_SPACE; } fis-gtm-V7.0-005/sr_port/bmm_find_free.h0000755000032200000250000000127014342376331016773 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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. * * * ****************************************************************/ #ifndef BMM_FIND_FREE_H_INCLUDED #define BMM_FIND_FREE_H_INCLUDED #include "gdsroot.h" block_id bmm_find_free(block_id hint, uchar_ptr_t base_addr, block_id total_bits); #endif fis-gtm-V7.0-005/sr_port/bmm_init.c0000755000032200000250000000145214342376331016012 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "gtm_string.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "gdsbml.h" GBLREF sgmnt_data *cs_data; void bmm_init(void) { assert(cs_data && cs_data->master_map_len); memset(MM_ADDR(cs_data), BMP_EIGHT_BLKS_FREE, MASTER_MAP_SIZE(cs_data)); return; } fis-gtm-V7.0-005/sr_port/bool_expr.c0000644000032200000250000000514514342376331016205 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" /* needed by INCREMENT_EXPR_DEPTH */ #include "compiler.h" #include "mdq.h" #include "mmemory.h" #include "opcode.h" #include "stringpool.h" LITREF octabstruct oc_tab[]; int bool_expr(boolean_t sense, oprtype *addr) /* * invoked to resolve expresions that are by definition coerced to Boolean, which include * IF arguments, $SELECT() arguments, and postconditionals for both commands and arguments * IF is the only one that comes in with the "TRUE" sense * *addr winds up as an pointer to a jump operand, which the caller fills in */ { mval *v; oprtype x; triple *t1, *t2; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; INCREMENT_EXPR_DEPTH; if (!eval_expr(&x)) { DECREMENT_EXPR_DEPTH; return FALSE; } assert(TRIP_REF == x.oprclass); if (OC_LIT == (x.oprval.tref)->opcode) { /* if its just a literal don't waste time */ DECREMENT_EXPR_DEPTH; return TRUE; } coerce(&x, OCT_BOOL); UNARY_TAIL(&x); for(t2 = t1 = x.oprval.tref; OCT_UNARY & oc_tab[t1->opcode].octype; t2 = t1, t1 = t1->operand[0].oprval.tref) ; if (OCT_ARITH & oc_tab[t1->opcode].octype) ex_tail(&t2->operand[0]); else if (OCT_BOOL & oc_tab[t1->opcode].octype) bx_boollit(t1); for (t1 = x.oprval.tref; OC_NOOP == t1->opcode; t1 = t1->exorder.bl) ; if ((OC_COBOOL == t1->opcode) && (OC_LIT == (t2 = t1->operand[0].oprval.tref)->opcode) && (TREF(curtchain) != t2->exorder.bl) && !(OCT_JUMP & oc_tab[t2->exorder.bl->opcode].octype)) { /* returning literals directly simplifies things for all callers, so swap the sense */ v = &t2->operand[0].oprval.mlit->v; unuse_literal(v); MV_FORCE_NUMD(v); PUT_LITERAL_TRUTH(MV_FORCE_BOOL(v), t2); dqdel(t1, exorder); DECREMENT_EXPR_DEPTH; return TRUE; } bx_tail(x.oprval.tref, sense, addr); CHKTCHAIN(TREF(curtchain), exorder, TRUE); /* defined away in mdq.h except with DEBUG_TRIPLES */ DECREMENT_EXPR_DEPTH; return TRUE; } fis-gtm-V7.0-005/sr_port/break.h0000755000032200000250000000132414342376331015303 0ustar librarygtc /**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #define BREAK_MASK (1 << 0) #define ZBREAK_MASK (1 << 1) #define DEVBREAK_MASK (1 << 2) #define ZSTBREAK_MASK (1 << 3) #define TRIGGER_ZBREAK_REMOVED_MASK (1 << 4) #define BREAK_MASK_END (1 << 5) fis-gtm-V7.0-005/sr_port/bt_get.c0000755000032200000250000000270014342376331015455 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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. * * * ****************************************************************/ /* This function returns a pointer to the bt_rec entry or 0 if not found. */ #include "mdef.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsbgtr.h" /* for the BG_TRACE_PRO macros */ GBLREF sgmnt_addrs *cs_addrs; bt_rec_ptr_t bt_get(block_id block) /* block = block # to get */ { register sgmnt_addrs *csa; bt_rec_ptr_t bt; int lcnt; csa = cs_addrs; assert(csa->now_crit); bt = csa->bt_header + (block % csa->hdr->bt_buckets); assert(bt->blk == BT_QUEHEAD); for (lcnt = csa->hdr->n_bts; lcnt > 0; lcnt--) { bt = (bt_rec_ptr_t)((sm_uc_ptr_t) bt + bt->blkque.fl); if (bt->blk == block) return bt; if (bt->blk == BT_QUEHEAD) return NULL; } SET_TRACEABLE_VAR(csa->nl->wc_blocked, WC_BLOCK_RECOVER); BG_TRACE_PRO_ANY(csa, wc_blocked_bt_get); return NULL; /* actually should return BT_INVALID or some such value but callers check only for NULL */ } fis-gtm-V7.0-005/sr_port/bt_init.c0000755000032200000250000000175714342376331015654 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" void bt_init(sgmnt_addrs *csa) { sgmnt_data_ptr_t csd; csd = csa->hdr; csa->ti = &csd->trans_hist; if (dba_mm != csd->acc_meth) { /* BT structures are NOT maintained for MM */ csa->bt_header = (bt_rec_ptr_t)((sm_uc_ptr_t) csd + csa->nl->bt_header_off); csa->bt_base = (bt_rec_ptr_t)((sm_uc_ptr_t) csd + csa->nl->bt_base_off); csa->th_base = (th_rec_ptr_t)((sm_uc_ptr_t) csd + csa->nl->th_base_off); } return; } fis-gtm-V7.0-005/sr_port/bt_malloc.c0000755000032200000250000000255314342376331016153 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" void bt_malloc(sgmnt_addrs *csa) { unsigned int n; sgmnt_data_ptr_t csd; csd = csa->hdr; /* check that the file header is quad word aligned */ if ((-(SIZEOF(uint4) * 2) & (sm_long_t)csd) != (sm_long_t)csd) GTMASSERT; if ((-(SIZEOF(uint4) * 2) & SIZEOF_FILE_HDR(csd)) != SIZEOF_FILE_HDR(csd)) GTMASSERT; csa->nl->bt_header_off = (n = (uint4)(SIZEOF_FILE_HDR(csd))); csa->nl->th_base_off = (n += csd->bt_buckets * SIZEOF(bt_rec)); /* hash table */ csa->nl->th_base_off += SIZEOF(que_ent); /* tnque comes after fl and bl of blkque */ csa->nl->bt_base_off = (n += SIZEOF(bt_rec)); /* th_queue anchor referenced above */ assert((n += (csd->n_bts * SIZEOF(bt_rec))) == (SIZEOF_FILE_HDR(csd)) + (BT_SIZE(csd))); /* DON'T use n after this */ bt_init(csa); bt_refresh(csa, TRUE); return; } fis-gtm-V7.0-005/sr_port/bt_put.c0000644000032200000250000001124114342376331015503 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdscc.h" #include "filestruct.h" #include "gdsbgtr.h" #include "send_msg.h" #include "relqop.h" #include "wcs_recover.h" #include "wcs_get_space.h" #include "jnl.h" #include "wbox_test_init.h" #ifdef DEBUG static int4 entry_count = 0; #endif GBLREF volatile boolean_t in_wcs_recover; /* TRUE if in "wcs_recover" */ GBLREF uint4 process_id; GBLREF jnl_gbls_t jgbl; error_def(ERR_BTFAIL); error_def(ERR_WCBLOCKED); bt_rec_ptr_t bt_put(gd_region *reg, block_id block) { bt_rec_ptr_t bt, q0, q1, hdr; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; cache_rec_ptr_t cr; th_rec_ptr_t th; trans_num lcl_tn; uint4 lcnt; node_local_ptr_t cnl; csa = (sgmnt_addrs *)&FILE_INFO(reg)->s_addrs; csd = csa->hdr; cnl = csa->nl; assert(csa->now_crit || csd->clustered); assert(dba_mm != csa->hdr->acc_meth); lcl_tn = csa->ti->curr_tn; hdr = csa->bt_header + (block % csd->bt_buckets); assert(BT_QUEHEAD == hdr->blk); for (lcnt = 0, bt = (bt_rec_ptr_t)((sm_uc_ptr_t)hdr + hdr->blkque.fl); ; bt = (bt_rec_ptr_t)((sm_uc_ptr_t)bt + bt->blkque.fl), lcnt++) { if (BT_QUEHEAD == bt->blk) { /* there is no matching bt */ assert(bt == hdr); bt = (bt_rec_ptr_t)((sm_uc_ptr_t)(csa->th_base) + csa->th_base->tnque.fl - SIZEOF(th->tnque)); if (CR_NOTVALID != bt->cache_index) { /* the oldest bt is still valid */ assert(!in_wcs_recover); cr = (cache_rec_ptr_t)GDS_ANY_REL2ABS(csa, bt->cache_index); if (cr->dirty) { /* get it written so it can be reused */ INCR_GVSTATS_COUNTER(csa, cnl, n_bt_scarce, 1); if (FALSE == wcs_get_space(reg, 0, cr)) { /* only reason we currently know why wcs_get_space could fail */ assert(csa->nl->wc_blocked || gtm_white_box_test_case_enabled); BG_TRACE_PRO_ANY(csa, wcb_bt_put); send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_WCBLOCKED, 6, LEN_AND_LIT("wcb_bt_put"), process_id, &lcl_tn, DB_LEN_STR(reg)); return NULL; } } bt->cache_index = CR_NOTVALID; cr->bt_index = 0; } q0 = (bt_rec_ptr_t)((sm_uc_ptr_t)bt + bt->blkque.fl); q1 = (bt_rec_ptr_t)remqt((que_ent_ptr_t)q0); if (EMPTY_QUEUE == (sm_long_t)q1) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(3) ERR_BTFAIL, 1, 1); bt->blk = block; bt->killtn = lcl_tn; insqt((que_ent_ptr_t)bt, (que_ent_ptr_t)hdr); th = (th_rec_ptr_t)remqh((que_ent_ptr_t)csa->th_base); assertpro(EMPTY_QUEUE != (sm_long_t)th); break; } if (bt->blk == block) { /* bt_put should never be called twice for the same block with the same lcl_tn. This is because * t_end/tp_tend update every block only once as part of each update transaction. Assert this. * The two exceptions are * a) Forward journal recovery which simulates a 2-phase M-kill where the same block * could get updated in both phases (example bitmap block gets updated for blocks created * within the TP transaction as well as for blocks that are freed up in the 2nd phase of * the M-kill) with the same transaction number. This is because although GT.M would have * updated the same block with different transaction numbers in the two phases, forward * recovery will update it with the same tn and instead increment the db tn on seeing the * following INCTN journal record(s). * b) Cache recovery (wcs_recover). It could call bt_put more than once for the same block * and potentially with the same tn. This is because the state of the queues is questionable * and there could be more than one cache record for a given block number. */ assert(in_wcs_recover || (bt->tn < lcl_tn) || (jgbl.forw_phase_recovery && !JNL_ENABLED(csa))); q0 = (bt_rec_ptr_t)((sm_uc_ptr_t)bt + bt->tnque.fl); th = (th_rec_ptr_t)remqt((que_ent_ptr_t)((sm_uc_ptr_t)q0 + SIZEOF(th->tnque))); assertpro(EMPTY_QUEUE != (sm_long_t)th); break; } if (0 == bt->blkque.fl) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(3) ERR_BTFAIL, 1, 2); if (lcnt >= csd->n_bts) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(3) ERR_BTFAIL, 1, 3); } insqt((que_ent_ptr_t)th, (que_ent_ptr_t)csa->th_base); bt->tn = lcl_tn; return bt; } fis-gtm-V7.0-005/sr_port/bt_refresh.c0000755000032200000250000000314014342376331016333 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "longset.h" #include "relqop.h" /* Refresh the database cache records in the shared memory. If init = TRUE, do the longset and initialize all the forward and * backward links. If init = FALSE, reset only the needed fields. */ void bt_refresh(sgmnt_addrs *csa, boolean_t init) { sgmnt_data_ptr_t csd; bt_rec_ptr_t ptr, top, bt1; csd = csa->hdr; assert(dba_bg == csd->acc_meth); if (init) longset((uchar_ptr_t)csa->bt_header, (csd->bt_buckets + csd->n_bts + 1) * SIZEOF(bt_rec), 0); for (ptr = csa->bt_header, top = ptr + csd->bt_buckets + 1; ptr < top; ptr++) ptr->blk = BT_QUEHEAD; for (ptr = csa->bt_base, bt1 = csa->bt_header, top = ptr + csd->n_bts; ptr < top ; ptr++, bt1++) { ptr->blk = BT_NOTVALID; ptr->cache_index = CR_NOTVALID; ptr->tn = ptr->killtn = 0; if (init) { insqt((que_ent_ptr_t)ptr, (que_ent_ptr_t)bt1); insqt((que_ent_ptr_t)((sm_uc_ptr_t)ptr + (2 * SIZEOF(sm_off_t))), (que_ent_ptr_t)csa->th_base); } } SET_OLDEST_HIST_TN(csa, csa->ti->curr_tn - 1); csa->ti->mm_tn = 0; return; } fis-gtm-V7.0-005/sr_port/buddy_list.c0000755000032200000250000001730614342376331016363 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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_string.h" #include "buddy_list.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "relqop.h" #define MAX_MEM_SIZE_IN_BITS 63 /* the unreachable maximum size of memory (in bits) */ void initialize_list(buddy_list *list, int4 elemSize, int4 initAlloc) { int4 i, maxPtrs; int4 temp; if (!initAlloc) initAlloc = 1; assert(0 < elemSize); assert(0 < initAlloc); elemSize = ROUND_UP2(elemSize, 8) ; /* make it 8 byte aligned */ temp = initAlloc; while (temp & (temp - 1)) /* floor it to a perfect power of 2 */ temp = temp & (temp - 1); if (initAlloc != temp) initAlloc = temp << 1; for (i = 0; initAlloc >> i; i++); list->initAllocBits = i - 1; list->elemSize = elemSize; list->initAlloc = initAlloc; list->cumulMaxElems = initAlloc; list->nElems = 0; /* Note: size_t typecast done below to enable 64-bit arithmetic so sizes > 4GB can be allocated */ list->ptrArray = (char **)malloc((size_t)SIZEOF(char *) * (MAX_MEM_SIZE_IN_BITS + 2)); /* +2 = +1 for holding the NULL pointer and +1 for ptrArray[0] */ memset(list->ptrArray, 0, SIZEOF(char *) * (MAX_MEM_SIZE_IN_BITS + 2)); list->ptrArrayCurr = list->ptrArray; /* Note: size_t typecast done below to enable 64-bit arithmetic so sizes > 4GB can be allocated */ list->nextFreePtr = list->ptrArray[0] = (char *)malloc((size_t)initAlloc * elemSize); list->free_que = NULL; /* initialize the list to have no free element queue */ DEBUG_ONLY(list->used_free_last_n_elements = FALSE;) DEBUG_ONLY(list->used_free_element = FALSE;) DEBUG_ONLY(list->nElems_greater_than_one = FALSE;) } /* Any changes to this routine need corresponding changes to the VERIFY_LIST_IS_REINITIALIZED macro (defined in buddy_list.h) */ void reinitialize_list(buddy_list *list) { assert(list); list->nElems = 0; list->cumulMaxElems = list->initAlloc; list->ptrArrayCurr = list->ptrArray; list->nextFreePtr = list->ptrArray[0]; list->free_que = NULL; /* reset the list to have no free element queue */ DEBUG_ONLY(list->used_free_last_n_elements = FALSE;) DEBUG_ONLY(list->used_free_element = FALSE;) DEBUG_ONLY(list->nElems_greater_than_one = FALSE;) } boolean_t free_last_n_elements(buddy_list *list, int4 num) { int4 rowElemsMax, rowElemsLeft, numLeft, nElems; char **ptrArrayCurr; assert(list); assert(!list->used_free_element); DEBUG_ONLY(list->used_free_last_n_elements = TRUE;) nElems = list->nElems; if (nElems >= num) { ptrArrayCurr = list->ptrArrayCurr; numLeft = num; rowElemsLeft = (int4)(list->nextFreePtr - ptrArrayCurr[0]) / list->elemSize; rowElemsMax = nElems - rowElemsLeft; while (numLeft >= rowElemsLeft && ptrArrayCurr != list->ptrArray) { assert(0 == rowElemsMax % list->initAlloc); numLeft -= rowElemsLeft; list->cumulMaxElems -= rowElemsMax; if (rowElemsMax != list->initAlloc) rowElemsMax = rowElemsMax >> 1; rowElemsLeft = rowElemsMax; ptrArrayCurr--; assert(rowElemsMax >= list->initAlloc); } list->nElems = nElems - num; list->ptrArrayCurr = ptrArrayCurr; list->nextFreePtr = ptrArrayCurr[0] + list->elemSize * (rowElemsLeft - numLeft); assert(list->ptrArrayCurr >= list->ptrArray); return TRUE; } else return FALSE; } char *get_new_element(buddy_list *list, int4 nElements) { char *retPtr; char **ptrArrayCurr; int4 cumulMaxElems, nElems, elemSize; if (0 >= nElements) { assert(FALSE); return NULL; } DEBUG_ONLY(if (1 < nElements) list->nElems_greater_than_one = TRUE;) nElems = list->nElems; cumulMaxElems = list->cumulMaxElems; elemSize = list->elemSize; if (nElems + nElements <= cumulMaxElems) { retPtr = list->nextFreePtr; list->nextFreePtr += nElements * elemSize; list->nElems = nElems + nElements; } else { do { ptrArrayCurr = ++list->ptrArrayCurr; /* Note: size_t typecast done below to enable 64-bit arithmetic so sizes > 4GB can be allocated */ if (!(retPtr = *ptrArrayCurr)) retPtr = *ptrArrayCurr = (char *)malloc((size_t)cumulMaxElems * elemSize); nElems = cumulMaxElems; cumulMaxElems *= 2; } while (nElems + nElements > cumulMaxElems); list->nElems = nElems + nElements; list->nextFreePtr = retPtr + elemSize * nElements; list->cumulMaxElems = cumulMaxElems; } return retPtr; } char *get_new_free_element(buddy_list *list) { char *elem; assert(!list->used_free_last_n_elements); assert(!list->nElems_greater_than_one); DEBUG_ONLY(list->used_free_element = TRUE;) /* Assert that each element has enough space to store a pointer. This will be used to maintain the singly linked list * of freed up elements in the buddy list. The head of this list will be list->free_que. */ assert(SIZEOF(char *) <= list->elemSize); elem = list->free_que; if (NULL != elem) { list->free_que = *(char **)elem; assert(elem != list->free_que); return elem; } return get_new_element(list, 1); } void free_element(buddy_list *list, char *elem) { assert(!list->used_free_last_n_elements); assert(!list->nElems_greater_than_one); DEBUG_ONLY(list->used_free_element = TRUE;) assert(elem); assert(elem != list->free_que); /* Add it to the singly linked list of freed up elements in the buddy_list */ *(char **)elem = list->free_que; list->free_que = elem; } char *find_element(buddy_list *list, int4 index) { int4 i, initAllocBits; assert(!list->nElems_greater_than_one); if (index >= list->nElems) return NULL; initAllocBits = list->initAllocBits; for (i = initAllocBits; index >> i; i++) /* not sure if "ceil_log2_32bit" would be faster here */ ; i = i - initAllocBits; return list->ptrArray[i] + list->elemSize * (index - list->initAlloc * (i ? 1 << (i-1) : 0)); } void cleanup_list(buddy_list *list) { char **curr; assert(list); if (!list || !(curr = list->ptrArray)) return; while(*curr) { free(*curr); curr++; } free(list->ptrArray); } /* Copy the first "numElems" elements of a buddy_list into a contiguous buffer pointed to by "dst". * This function assumes caller has allocated "dst" as appropriate. * Returns : The # of bytes copied onto "dst". */ size_t copy_list_to_buf(buddy_list *list, int4 numElems, char *dst) { int4 cumulMaxElems, curElems, copyElems, elemSize; size_t copysize, totalsize; char **ptrArrayCurr; assert(numElems); assert(list->nElems >= numElems); assert(!list->used_free_element); assert(!list->nElems_greater_than_one); cumulMaxElems = list->initAlloc; curElems = cumulMaxElems; elemSize = list->elemSize; ptrArrayCurr = list->ptrArray; totalsize = 0; do { if (numElems < curElems) { copyElems = numElems; numElems = 0; } else { copyElems = curElems; numElems = numElems - copyElems; } copysize = (size_t)copyElems * elemSize; memcpy(dst, *ptrArrayCurr, copysize); totalsize += copysize; if (!numElems) break; dst += copysize; curElems = cumulMaxElems; cumulMaxElems *= 2; ptrArrayCurr++; } while (TRUE); return totalsize; } fis-gtm-V7.0-005/sr_port/buddy_list.h0000755000032200000250000001106114342376331016360 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef BUDDY_LIST_H_INCLUDED #define BUDDY_LIST_H_INCLUDED /* This is a general purpose structure used for storing a list of similar-sized structure-elements with the need for * efficiently accessing the element once we know its index. * It maintains an array of pointers "ptrArray". * The first and second pointers i.e. ptrArray[0] and ptrArray[1] point to an array containing "initAlloc" elements. * Every other pointer ptrArray[i] points to an array containing double-the-number-of-elements * as the array pointed to by ptrArray[i-1]. * This also provides for iterating through the list of elements. */ typedef struct buddy_list_struct { char **ptrArray; /* the array of pointers */ int4 elemSize; /* the size of each structure-element */ int4 initAlloc; /* the number of structures in the ptrArray[0] and ptrArray[1] */ int4 initAllocBits; /* number of bits corresponding to initAlloc */ int4 nElems; /* the current number of elements used up in the total array */ int4 cumulMaxElems; /* the maximum number of elements that can be held from the first upto the current array */ char **ptrArrayCurr; /* = &ptrArray[i] where i is the current array index in use for allocation */ char *nextFreePtr; /* pointer to the next free element in the current array. Initially = ptrArray[0] */ char *free_que; /* pointer to the singly linked list of freed-up elements */ # ifdef DEBUG /* The following two debug-only variables are present to avoid mixing of usage of the functions "free_last_n_elements" * and "get_new_free_element"/"free_element()" in the same buddy list. This is because they both use completely * different schemes to free up elements (one reduces the # of elements in the buddy list whereas the other does not * touch the # of elements but maintains an internal singly linked list of freed up elements) and they cannot coexist. */ boolean_t used_free_last_n_elements; /* TRUE if "free_last_n_elements" was called in this buddy_list */ boolean_t used_free_element; /* TRUE if "get_new_free_element" or "free_element" was called */ boolean_t nElems_greater_than_one; /* TRUE if "get_new_element" was ever called with "nElements" > 1 */ # endif } buddy_list; void initialize_list(buddy_list *list, int4 elemSize, int4 initAlloc); char *get_new_element(buddy_list *list, int4 nElements); char *find_element(buddy_list *list, int4 index); void cleanup_list(buddy_list *list); void reinitialize_list(buddy_list *list); /* used for reusing already allocated storage */ boolean_t free_last_n_elements(buddy_list *list, int4 num); /* to free up the last contiguous "num" elements */ void free_element(buddy_list *list, char *elem); /* to free up an element and reuse it later */ char *get_new_free_element(buddy_list *list); /* gets a freed-up element if available otherwise gets a new one */ size_t copy_list_to_buf(buddy_list *list, int4 numElems, char *dst); #define PROBE_FREEUP_BUDDY_LIST(list) \ { \ if (NULL != list) \ FREEUP_BUDDY_LIST(list); \ } #define FREEUP_BUDDY_LIST(list) \ { \ assert(list); \ if (NULL != list) \ { \ cleanup_list(list); \ free(list); \ } \ } #define VERIFY_LIST_IS_REINITIALIZED(list) \ { /* The following code verifies the same fields initialized by the \ * function "reinitialize_list". Any changes to one should be reflected \ * in the other. \ */ \ assert((0 == list->nElems) || process_exiting); \ assert((list->cumulMaxElems == list->initAlloc) || process_exiting); \ assert((list->ptrArrayCurr == list->ptrArray) || process_exiting); \ assert((list->nextFreePtr == list->ptrArray[0])|| process_exiting); \ assert((NULL == list->free_que) || process_exiting); \ } #define REINITIALIZE_LIST(LST) \ { \ buddy_list *lcllist; \ \ lcllist = LST; \ assert((NULL == lcllist->free_que) || lcllist->nElems); \ if (lcllist->nElems) \ reinitialize_list(lcllist); \ else \ { /* No need to reinitialize. Verify \ * that list is already initialized */ \ VERIFY_LIST_IS_REINITIALIZED(lcllist); \ } \ } #endif fis-gtm-V7.0-005/sr_port/copy_stack_frame.c0000644000032200000250000000412314342376333017522 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_stdio.h" #include #include "stack_frame.h" #include "mprof.h" #include "error.h" #include "glvn_pool.h" GBLREF stack_frame *frame_pointer; GBLREF unsigned char *stackbase ,*stacktop, *msp, *stackwarn; error_def(ERR_STACKCRIT); error_def(ERR_STACKOFLOW); void copy_stack_frame(void) { register stack_frame *sf; unsigned char *msp_save; msp_save = msp; sf = (stack_frame *)(msp -= SIZEOF(stack_frame)); if (msp <= stackwarn) { if (msp <= stacktop) { msp = msp_save; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_STACKOFLOW); } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_STACKCRIT); } assert(msp < stackbase); assert((frame_pointer < frame_pointer->old_frame_pointer) || (NULL == frame_pointer->old_frame_pointer)); *sf = *frame_pointer; sf->old_frame_pointer = frame_pointer; sf->flags = 0; /* Don't propagate special flags */ sf->type &= SFT_ZINTR_OFF; /* Don't propagate special type - normally can't propagate but if $ZINTERRUPT frame is * rewritten by ZGOTO to a "regular" frame, this frame type *can* propagate. */ SET_GLVN_INDX(sf, GLVN_POOL_UNTOUCHED); sf->ret_value = NULL; sf->dollar_test = -1; /* initialize it with -1 for indication of not yet being used */ frame_pointer = sf; DBGEHND((stderr, "copy_stack_frame: Added stackframe at addr 0x"lvaddr" old-msp: 0x"lvaddr" new-msp: 0x"lvaddr"\n", sf, msp_save, msp)); assert((frame_pointer < frame_pointer->old_frame_pointer) || (NULL == frame_pointer->old_frame_pointer)); } void copy_stack_frame_sp(void) { copy_stack_frame(); new_prof_frame(TRUE); } fis-gtm-V7.0-005/sr_port/bx_relop.c0000755000032200000250000000470614342376331016033 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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 "compiler.h" #include "mdq.h" #include "opcode.h" #include "fullbool.h" LITREF octabstruct oc_tab[]; void bx_relop(triple *t, opctype cmp, opctype tst, oprtype *addr) /* work Boolean relational arguments * *t points to the Boolean operation * cmp and tst give (respectively) the opcode and the associated jump * *addr points the operand for the jump and is eventually used by logic back in the invocation stack to fill in a target location */ { oprtype *p; tbp *tripbp; triple *bini, *comv, *ref, *ref0, ref1; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ref = maketriple(tst); ref->operand[0] = put_indr(addr); dqins(t, exorder, ref); t->opcode = cmp; for (p = t->operand ; p < ARRAYTOP(t->operand); p++) { /* Some day investigate whether this is still needed */ if (TRIP_REF == p->oprclass) { ex_tail(p); RETURN_IF_RTS_ERROR; if ((NULL != TREF(boolchain_ptr)) && (OC_COMVAL == (comv = p->oprval.tref)->opcode)) { /* RELOP needed a BOOLINIT/BOOLFINI/COMVAL, which now must move to the boolchain */ assert(TREF(saw_side_effect) && (GTM_BOOL != TREF(gtm_fullbool))); assert(OC_BOOLINIT == comv->operand[0].oprval.tref->opcode); assert(OC_BOOLFINI == comv->exorder.bl->opcode); assert(comv->operand[0].oprval.tref == comv->exorder.bl->operand[0].oprval.tref); for (ref = (bini = comv->operand[0].oprval.tref)->exorder.fl; ref != comv; ref = ref->exorder.fl) { /* process to matching end of sequence so to conform with addr adjust at end of bx_boolop */ if (!(OCT_JUMP & oc_tab[ref->opcode].octype)) TRACK_JMP_TARGET(ref, ref); } TRACK_JMP_TARGET(comv, comv); dqdelchain(bini->exorder.bl, comv->exorder.fl, exorder); /* snip out the sequence */ ref0 = &ref1; /* anchor it */ ref0->exorder.fl = bini; ref0->exorder.bl = comv; ref = (TREF(boolchain_ptr))->exorder.bl; dqadd(ref, ref0, exorder); /* and insert it in new chain */ } } } return; } fis-gtm-V7.0-005/sr_port/bx_tail.c0000755000032200000250000000626014342376331015640 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "compiler.h" #include "opcode.h" #include "mdq.h" #include "mmemory.h" LITREF octabstruct oc_tab[]; /* structure of jmps is as follows: * * sense OC_AND OC_OR * * TRUE op1 op1 * jmpf next jmpt addr * op2 op2 * jmpt addr jmpt addr * * FALSE op1 op1 * jmpf addr jmpt next * op2 op2 * jmpf addr jmpf addr **/ void bx_tail(triple *t, boolean_t sense, oprtype *addr) /* * work a Boolean expression along to final form * triple *t; triple to be processed * boolean_t sense; code to be generated is jmpt or jmpf * oprtype *addr; address to jmp */ { opctype c; triple *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert((1 & sense) == sense); assert(OCT_BOOL & oc_tab[t->opcode].octype); assert(TRIP_REF == t->operand[0].oprclass); assert((TRIP_REF == t->operand[1].oprclass) || (NO_REF == t->operand[1].oprclass)); if (OCT_NEGATED & oc_tab[c = t->opcode].octype) sense = !sense; switch (c) { case OC_COBOOL: ex_tail(&t->operand[0]); RETURN_IF_RTS_ERROR; if (OC_GETTRUTH == t->operand[0].oprval.tref->opcode) { assert(NO_REF == t->operand[0].oprval.tref->operand[0].oprclass); t->operand[0].oprval.tref->opcode = OC_NOOP; /* must NOOP rather than delete as might be expr_start */ t->opcode = sense ? OC_JMPTSET : OC_JMPTCLR; t->operand[0] = put_indr(addr); return; } ref = maketriple(sense ? OC_JMPNEQ : OC_JMPEQU); ref->operand[0] = put_indr(addr); dqins(t, exorder, ref); return; case OC_COM: bx_tail(t->operand[0].oprval.tref, !sense, addr); RETURN_IF_RTS_ERROR; t->opcode = OC_NOOP; /* maybe operand or target, so noop, rather than dqdel the com */ t->operand[0].oprclass = NO_REF; return; case OC_NEQU: case OC_EQU: bx_relop(t, OC_EQU, sense ? OC_JMPNEQ : OC_JMPEQU, addr); break; case OC_NPATTERN: case OC_PATTERN: bx_relop(t, OC_PATTERN, sense ? OC_JMPNEQ : OC_JMPEQU, addr); break; case OC_NFOLLOW: case OC_FOLLOW: bx_relop(t, OC_FOLLOW, sense ? OC_JMPGTR : OC_JMPLEQ, addr); break; case OC_NSORTS_AFTER: case OC_SORTS_AFTER: bx_relop(t, OC_SORTS_AFTER, sense ? OC_JMPGTR : OC_JMPLEQ, addr); break; case OC_NCONTAIN: case OC_CONTAIN: bx_relop(t, OC_CONTAIN, sense ? OC_JMPNEQ : OC_JMPEQU, addr); break; case OC_NGT: case OC_GT: bx_relop(t, OC_NUMCMP, sense ? OC_JMPGTR : OC_JMPLEQ, addr); break; case OC_NLT: case OC_LT: bx_relop(t, OC_NUMCMP, sense ? OC_JMPLSS : OC_JMPGEQ, addr); break; case OC_NAND: case OC_AND: bx_boolop(t, FALSE, sense, sense, addr); RETURN_IF_RTS_ERROR; break; case OC_NOR: case OC_OR: bx_boolop(t, TRUE, !sense, sense, addr); RETURN_IF_RTS_ERROR; break; default: assertpro(FALSE && t); } return; } fis-gtm-V7.0-005/sr_port/cache.h0000755000032200000250000000422114342376331015261 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef CACHE_H #define CACHE_H /* Macros to add debugging to objcode cache for indirects. To enable, uncomment line below */ /*#define DEBUG_CACHE */ #ifdef DEBUG_CACHE # define DBGCACHE(x) DBGFPF(x) #else # define DBGCACHE(x) #endif typedef struct { mstr str; uint4 code; } icode_str; /* For indirect code source. */ typedef struct cache_ent { mstr obj; icode_str src; int refcnt; /* Number of indirect source code pointing to same cache entry */ int zb_refcnt; /* Number of zbreak action entry pointing to same cache entry */ } cache_entry; /* Following is the indirect routine header build as part of an indirect code object */ typedef struct ihead_struct { cache_entry *indce; int4 vartab_off; int4 vartab_len; int4 temp_mvals; int4 temp_size; int4 fixup_vals_off; /* literal mval table offset */ int4 fixup_vals_num; /* literal mval table's mval count */ } ihdtyp; #define ICACHE_TABLE_INIT_SIZE 64 /* Use 1K memory initially */ #define ICACHE_SIZE ROUND_UP2(SIZEOF(cache_entry), NATIVE_WSIZE) /* We allow cache_table to grow till we hit memory or entry maximums. If more memory is needed, we do compaction. * Current default limits (overrideable by environment variable): 128 entries, 128KB of object code on all platforms * except IA64 architecture which gets 256KB due to its less compact instruction forms. */ #define DEFAULT_INDRCACHE_KBSIZE 128 #define MAX_INDRCACHE_KBSIZE 2048 #define DEFAULT_INRDCACHE_ENTRIES 128 void indir_lits(ihdtyp *ihead); void cache_init(void); mstr *cache_get(icode_str *indir_src); void cache_put(icode_str *src, mstr *object); void cache_table_rebuild(void); void cache_stats(void); #endif fis-gtm-V7.0-005/sr_port/cache_cleanup.h0000755000032200000250000000212314342376331016767 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2004 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef CACHE_CLEANUP_DEFINED #define CACHE_CLEANUP_DEFINED #define IF_INDR_FRAME_CLEANUP_CACHE_ENTRY(frame_pointer) \ { \ /* See if unwinding an indirect frame*/ \ if (frame_pointer->flags & SFF_INDCE) \ cache_cleanup(frame_pointer); \ } #define IF_INDR_FRAME_CLEANUP_CACHE_ENTRY_AND_UNMARK(frame_pointer) \ { \ /* See if unwinding an indirect frame*/ \ if (frame_pointer->flags & SFF_INDCE) \ { \ cache_cleanup(frame_pointer); \ frame_pointer->flags &= SFF_INDCE_OFF; \ } \ } void cache_cleanup(stack_frame *sf); #endif fis-gtm-V7.0-005/sr_port/cache_get.c0000755000032200000250000000207014342376331016113 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2004 Sanchez Computer Associates, 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 "cache.h" #include "hashtab_objcode.h" GBLREF int cache_hits, cache_fails; GBLREF hash_table_objcode cache_table; /* cache_get - get cached indirect object code corresponding to input source and code from cache_table. * * If object code exists in cache, return pointer to object code mstr * otherwise, return NULL. */ mstr *cache_get(icode_str *indir_src) { cache_entry *csp; ht_ent_objcode *tabent; if (NULL != (tabent = lookup_hashtab_objcode(&cache_table, indir_src))) { cache_hits++; return &(((cache_entry *)tabent->value)->obj); } else { cache_fails++; return NULL; } } fis-gtm-V7.0-005/sr_port/cache_init.c0000755000032200000250000000140014342376331016273 0ustar librarygtc/**************************************************************** * * * 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 "cache.h" #include "hashtab_objcode.h" #include "hashtab.h" GBLREF hash_table_objcode cache_table; GBLREF int indir_cache_mem_size; void cache_init(void) { init_hashtab_objcode(&cache_table, ICACHE_TABLE_INIT_SIZE, HASHTAB_COMPACT, HASHTAB_SPARE_TABLE); indir_cache_mem_size = 0; } fis-gtm-V7.0-005/sr_port/cache_stats.c0000755000032200000250000000246514342376331016502 0ustar librarygtc/**************************************************************** * * * 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. * * * ****************************************************************/ /* Print cacheing stats for indirect code */ #include "mdef.h" #include "gtm_stdio.h" #include "cache.h" #include "hashtab_objcode.h" GBLREF int cache_hits, cache_fails; GBLREF hash_table_objcode cache_table; void cache_stats(void) { int total_attempts, ace; ht_ent_objcode *tabent, *topent; cache_entry *csp; total_attempts = cache_hits + cache_fails; FPRINTF(stderr,"\nIndirect code cache performance -- Hits: %d, Fails: %d, Hit Ratio: %d%%\n", cache_hits, cache_fails, total_attempts ? ((100 * cache_hits) / (cache_hits + cache_fails)) : 0); ace = 0; /* active cache entries */ for (tabent = cache_table.base, topent = cache_table.top; tabent < topent; tabent++) { if (HTENT_VALID_OBJCODE(tabent, cache_entry, csp)) { if (csp->refcnt || csp->zb_refcnt) ++ace; } } FPRINTF(stderr,"Indirect cache entries currently marked active: %d\n", ace); } fis-gtm-V7.0-005/sr_port/cache_table_rebuild.c0000755000032200000250000000320514342376331020132 0ustar librarygtc/**************************************************************** * * * 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 "gtm_string.h" #include "gtm_stdio.h" #include "io.h" #include "min_max.h" #include "cache.h" #include "hashtab_objcode.h" #include "cachectl.h" #include "gtm_text_alloc.h" #include "error.h" GBLREF hash_table_objcode cache_table; GBLREF int indir_cache_mem_size; error_def(ERR_MEMORY); error_def(ERR_VMSMEMORY); void cache_table_rebuild() { ht_ent_objcode *tabent, *topent; cache_entry *csp; DBGCACHE((stdout, "cache_table_rebuild: Rebuilding indirect lookaside cache\n")); for (tabent = cache_table.base, topent = cache_table.top; tabent < topent; tabent++) { if (HTENT_VALID_OBJCODE(tabent, cache_entry, csp)) { if ((0 == csp->refcnt) && (0 == csp->zb_refcnt)) { ((ihdtyp *)(csp->obj.addr))->indce = NULL; indir_cache_mem_size -= (ICACHE_SIZE + csp->obj.len); GTM_TEXT_FREE(csp); delete_hashtab_ent_objcode(&cache_table, tabent); } } } /* Only do compaction processing if we are not processing a memory type error (which * involves allocating a smaller table with storage we don't have. */ if (COMPACT_NEEDED(&cache_table) && error_condition != UNIX_ONLY(ERR_MEMORY) VMS_ONLY(ERR_VMSMEMORY)) compact_hashtab_objcode(&cache_table); } fis-gtm-V7.0-005/sr_port/cachectl.h0000755000032200000250000000124314342376331015765 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /* cachectl.h - values used as parameters to cacheflush * * ICACHE - flush instruction cache * DCACHE - flush data cache * BCACHE - flush both caches */ #define ICACHE 0x1 #define DCACHE 0x2 #define BCACHE (ICACHE|DCACHE) fis-gtm-V7.0-005/sr_port/cacheflush.c0000755000032200000250000000236214342376331016322 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /* cacheflush stub * * Most hardware platforms on which GT.M is implemented use separate * instruction and data caches. It is necessary to flush these caches * whenever we generate code in a data region in order to make sure * the generated code gets written from the data cache to memory and * subsequently loaded from memory into the instruction cache. * * Input: addr starting address of region to flush * nbytes size, in bytes, of region to flush * cache_select flag indicating whether to flus * I-cache, D-cache, or both * * This stub is for those platforms that don't use separate data and * instruction caches. */ #include "mdef.h" #include "cacheflush.h" int cacheflush (void *addr, long nbytes, int cache_select) { return 0; /* incr_link requires a zero return value for success */ } fis-gtm-V7.0-005/sr_port/cacheflush.h0000755000032200000250000000120614342376331016323 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef CACHEFLUSH_H_INCLUDED #define CACHEFLUSH_H_INCLUDED int cacheflush (void *addr, long nbytes, int cache_select); #endif fis-gtm-V7.0-005/sr_port/caller_id.c0000755000032200000250000000212314342376331016126 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ /* Stub for caller_id routine called from CRIT_TRACE macro to return the return address of our caller allowing CRIT_TRACE (used in various semaphore routines) to determine who was calling those semaphore routines and for what purpose and when. This is a system dependent type of operation and is generally implemented in assembly language. This stub is so we continue to function on those platforms we have not yet implemented this function. */ #include "mdef.h" #include "caller_id.h" caddr_t caller_id(unsigned int extra_frames) { return (caddr_t)((INTPTR_T)-1); } fis-gtm-V7.0-005/sr_port/caller_id.h0000755000032200000250000000121614342376331016135 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef CALLER_ID_H_INCLUDED #define CALLER_ID_H_INCLUDED #include caddr_t caller_id(unsigned int extra_frames); #endif fis-gtm-V7.0-005/sr_port/callg.h0000755000032200000250000000144214342376331015302 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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. * * * ****************************************************************/ #ifndef CALLG_H #define CALLG_H typedef struct gparam_list_struct { intszofptr_t n; void *arg[MAX_ACTUALS + PUSH_PARM_OVERHEAD]; } gparam_list; typedef INTPTR_T (*callgfnptr)(intszofptr_t cnt, ...); INTPTR_T callg(callgfnptr, gparam_list *); void callg_signal(void *); #endif fis-gtm-V7.0-005/sr_port/callg_signal.c0000755000032200000250000000115714342376331016635 0ustar librarygtc/**************************************************************** * * * 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 "lv_val.h" /* needed by "callg.h" */ #include "callg.h" void callg_signal(void *arg) { (void)callg((callgfnptr) rts_error, (void *)arg); } fis-gtm-V7.0-005/sr_port/ccp.h0000755000032200000250000002065614342376331014775 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2005 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. * * * ****************************************************************/ #ifndef CCP_H #define CCP_H /* requires gdsroot */ enum { CCP_VALBLK_TRANS_HIST, CCP_VALBLK_JNL_ADDR, CCP_VALBLK_EPOCH_TN, CCP_VALBLK_LST_ADDR }; typedef struct ccp_wait_struct { struct ccp_wait_struct *next; uint4 pid; } ccp_wait; typedef struct { ccp_wait *first,**last; } ccp_wait_head; typedef struct { short cond; unsigned short length; int4 lockid; } ccp_iosb; typedef struct mem_list_struct { char *addr; uint4 pages; struct mem_list_struct *next; struct mem_list_struct *prev; bool free; } mem_list; typedef struct ccp_db_header_struct { struct sgmnt_data_struct *glob_sec; /* address of file header */ struct gd_region_struct *greg; /* address like gv_cur_region */ struct sgmnt_addrs_struct *segment; /* like cs_addrs */ struct ccp_db_header_struct *next; /* pointer to next db_header in list */ mem_list *mem_ptr; /* memory control structure */ vms_lock_sb wm_iosb; /* iosb for write mode $enq*/ vms_lock_sb flush_iosb; /* iosb for 'buffers flushed' $enq*/ vms_lock_sb lock_iosb; /* iosb for MUMPS LOCK $enq*/ vms_lock_sb refcnt_iosb; /* iosb for reference count of gtm process in database $enq*/ ccp_iosb qio_iosb; /* iosb for $QIO used during file opens and misc operations*/ ccp_wait_head write_wait; /* list of pid's to wake upon entering write mode */ ccp_wait_head flu_wait; /* list of pid's to wake upon prior machine finishing flush */ ccp_wait_head exitwm_wait; /* list of pid's to wake after finishing exitwm */ ccp_wait_head reopen_wait; /* list of pid's to wake upon finishing close */ trans_num last_write_tn; /* t.n. for last write on this node */ trans_num master_map_start_tn; /* t.n. for last master map update*/ trans_num tick_tn; /* t.n. as of last clock tick*/ uint4 last_lk_sequence; /* sequence for last lock update */ short unsigned wc_rover; /* used when flushing dirty buffers so that we don't have to start at the beginning each time */ struct ccp_db_header_struct *tick_timer_id; /* unique values to use as timer id and pointer to db */ struct ccp_db_header_struct *quantum_timer_id; struct ccp_db_header_struct *stale_timer_id; struct ccp_db_header_struct *wmcrit_timer_id; struct ccp_db_header_struct *close_timer_id; struct ccp_db_header_struct *exitwm_timer_id; struct ccp_db_header_struct *extra_tick_id; date_time start_wm_time; /* Time entered write mode */ unsigned char drop_lvl; unsigned int quantum_expired : 1; /* quantum is up*/ unsigned int tick_in_progress : 1; /* the tick timer is running*/ unsigned int wmexit_requested: 1; /* we have received a request to relinquish write mode*/ unsigned int write_mode_requested: 1; /* processing to get write mode has commenced*/ unsigned int dirty_buffers_acquired : 1; /* set to zero when write mode is entered, and to one when ENQ on dirty buffers is acquired*/ unsigned int filler : 1; unsigned int stale_in_progress : 1; /* stale timer active */ unsigned int blocking_ast_received : 1; /* an exitwm blocking ast has been received */ unsigned int remote_wakeup : 1; /* wakeup needed for processes blocked on mumps locks */ unsigned int extra_tick_started : 1; /* started timer for extra tick prior to write mode release */ unsigned int extra_tick_done : 1; /* extra tick done */ unsigned int close_region : 1; /* closing region */ } ccp_db_header; typedef union { gds_file_id file_id; struct gd_region_struct *reg; ccp_db_header *h; struct FAB *fab; struct { unsigned char len; unsigned char txt[23]; } str; struct { gds_file_id fid; unsigned short cycle; } exreq; /* exit write mode request */ struct { unsigned short channel; unsigned char *context; } mbx; } ccp_action_aux_value; /* types used to discriminate ccp_action_aux_value union members */ #define CCTVNUL 0 /* no value */ #define CCTVSTR 1 /* string */ #define CCTVMBX 2 /* mailbox id */ #define CCTVFIL 3 /* file id */ #define CCTVDBP 4 /* data_base header pointer */ #define CCTVFAB 5 /* pointer to a FAB */ #define CCP_TABLE_ENTRY(A,B,C,D) A, typedef enum { #include "ccpact_tab.h" CCPACTION_COUNT } ccp_action_code; #undef CCP_TABLE_ENTRY typedef struct { ccp_action_code action; uint4 pid; ccp_action_aux_value v; } ccp_action_record; typedef struct { int4 fl; int4 bl; } ccp_relque; typedef struct { ccp_relque q; date_time request_time, process_time; ccp_action_record value; }ccp_que_entry; #define CCP_MBX_NAME "GTM$CCP$MBX" #define CCP_PRC_NAME "GT.CX_CONTROL" #define CCP_LOG_NAME "GTM$CCPLOG:CCPERR.LOG" enum ccp_state_code { CCST_CLOSED, /* Data base is closed */ CCST_OPNREQ, /* A request to open the data base is being processed */ CCST_RDMODE, /* The data base is in READ mode, all buffers have been written to disk */ CCST_WRTREQ, /* A Write Mode request on our machine is being processed */ CCST_WRTGNT, /* Write mode has been granted, but the last machine's dirty buffers are not available */ CCST_DRTGNT, /* Write mode has been granted and the dirty buffers are available */ CCST_WMXREQ, /* Another machine has requested write mode */ CCST_WMXGNT /* We have relinquished write mode, and are writing dirty buffers */ }; #define CCST_MASK_CLOSED (1 << CCST_CLOSED) #define CCST_MASK_OPNREQ (1 << CCST_OPNREQ) #define CCST_MASK_RDMODE (1 << CCST_RDMODE) #define CCST_MASK_WRTREQ (1 << CCST_WRTREQ) #define CCST_MASK_WRTGNT (1 << CCST_WRTGNT) #define CCST_MASK_DRTGNT (1 << CCST_DRTGNT) #define CCST_MASK_WMXREQ (1 << CCST_WMXREQ) #define CCST_MASK_WMXGNT (1 << CCST_WMXGNT) #define CCST_MASK_OPEN (~(CCST_MASK_CLOSED | CCST_MASK_OPNREQ)) #define CCST_MASK_WRITE_MODE (CCST_MASK_WRTGNT | CCST_MASK_DRTGNT) #define CCST_MASK_HAVE_DIRTY_BUFFERS (CCST_MASK_DRTGNT | CCST_MASK_WMXREQ | CCST_MASK_WMXGNT | CCST_MASK_RDMODE) #define CCP_SEGMENT_STATE(X,Y) (((1 << ((X)->ccp_state)) & (Y)) != 0) #define CCP_CLOSE_REGION 0 #define CCP_OPEN_REGION 1 #ifdef VMS #define CCP_FID_MSG(X,Y) (ccp_sendmsg(Y, &FILE_INFO(X)->file_id)) #else #define CCP_FID_MSG(X,Y) #endif bool ccp_act_request(ccp_action_record *rec); bool ccp_priority_request(ccp_action_record *rec); short ccp_sendmsg(ccp_action_code action, ccp_action_aux_value *aux_value); ccp_action_record *ccp_act_select(void); ccp_db_header *ccp_get_reg_by_fab(struct FAB *fb); ccp_db_header *ccp_get_reg(gds_file_id *name); void ccp_exit(void); void ccp_init(void); void ccp_quemin_adjust(char oper); void ccp_staleness(ccp_db_header **p); short ccp_sendmsg(ccp_action_code action, ccp_action_aux_value *aux_value); unsigned char *ccp_format_querec(ccp_que_entry *inrec, unsigned char *outbuf, unsigned short outbuflen); void cce_ccp_ch(); void cce_get_return_channel(ccp_action_aux_value *p); void cce_dbdump(void); void ccp_act_complete(void); void ccp_act_init(void); void ccp_add_reg(ccp_db_header *d); void ccp_close1(ccp_db_header *db); void ccp_dump(void); void ccp_ewmwtbf_interrupt(ccp_db_header **p); void ccp_exitwm1a(ccp_db_header *db); void ccp_exitwm1(ccp_db_header *db); void ccp_exitwm2a(ccp_db_header *db); void ccp_exitwm2(ccp_db_header *db); void ccp_exitwm3(ccp_db_header *db); void ccp_exitwm_attempt(ccp_db_header *db); void ccp_exitwm_blkast(ccp_db_header **pdb); void ccp_extra_tick(ccp_db_header **p); void ccp_gotdrt_tick(ccp_db_header *db); void ccp_lkdowake_blkast(ccp_db_header *db); void ccp_lkrqwake1(ccp_db_header *db); void ccp_mbx_start(void); void ccp_opendb1a(struct FAB *fb); void ccp_opendb1(ccp_db_header *db); void ccp_opendb1e(struct FAB *fb); void ccp_opendb2(ccp_db_header *db); void ccp_opendb3b(ccp_db_header *db); void ccp_opendb3c(ccp_db_header *db); void ccp_opendb3(ccp_db_header *db); void ccp_opendb(ccp_action_record *rec); void ccp_pndg_proc_add(ccp_wait_head *list, int4 pid); void ccp_pndg_proc_wake(ccp_wait_head *list); void ccp_request_write_mode(ccp_db_header *db); void ccp_reqwm_interrupt(ccp_db_header **pdb); void ccp_rundown(void); void ccp_signal_cont(uint4 arg1, ...); void ccp_tick_interrupt(ccp_db_header **p); void ccp_tick_start(ccp_db_header *db); void ccp_tr_checkdb(void); void ccp_writedb4a(ccp_db_header *db); void ccp_writedb4(ccp_db_header *db); void ccp_writedb5(ccp_db_header *db); #endif fis-gtm-V7.0-005/sr_port/ccp_cluster_lock_wake.h0000755000032200000250000000121414342376331020542 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef CCP_CLUSTER_LOCK_WAKE_H_INCLUDED #define CCP_CLUSTER_LOCK_WAKE_H_INCLUDED void ccp_cluster_lock_wake(gd_region *reg); #endif fis-gtm-V7.0-005/sr_port/ccpact.h0000755000032200000250000000176314342376331015463 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /* * CCPACT.H is now obsolete; the code has been moved into CCP.H. * The file can be NIXed once all inclusions of it have been removed. */ #if 0 #define CCP_TABLE_ENTRY(A,B,C,D) A, enum action_code_enum { #include "ccpact_tab.h" CCPACTION_COUNT }; #undef CCP_TABLE_ENTRY /* ccpact_tab auxvalue types to discriminate unions */ #define CCTVNUL 0 /* no value */ #define CCTVSTR 1 /* string */ #define CCTVMBX 2 /* mailbox id */ #define CCTVFIL 3 /* file id */ #define CCTVDBP 4 /* data_base header pointer */ #define CCTVFAB 5 /* pointer to a FAB */ #endif fis-gtm-V7.0-005/sr_port/ccpact_tab.h0000755000032200000250000000524414342376331016307 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /* CCP_TABLE_ENTRY (transaction routine auxvalue transaction enum, name, type, priority ) */ CCP_TABLE_ENTRY (CCTR_CHECKDB, ccp_tr_checkdb, CCTVNUL, CCPR_NOR) /* check files to see if still being accessed */ CCP_TABLE_ENTRY (CCTR_CLOSE, ccp_tr_close, CCTVFIL, CCPR_NOR) /* notify that process no longer has interest in region */ CCP_TABLE_ENTRY (CCTR_CLOSEJNL, ccp_tr_closejnl, CCTVDBP, CCPR_HI) /* journal file close due to oper ast */ CCP_TABLE_ENTRY (CCTR_DEBUG, ccp_tr_debug, CCTVSTR, CCPR_HI) /* request that ccp go into debug mode */ CCP_TABLE_ENTRY (CCTR_EWMWTBF, ccp_tr_ewmwtbf, CCTVDBP, CCPR_HI) /* exit write mode, wait for buffers to be written out */ CCP_TABLE_ENTRY (CCTR_EXITWM, ccp_tr_exitwm, CCTVFIL, CCPR_NOR) /* relinquish write mode */ CCP_TABLE_ENTRY (CCTR_EXWMREQ, ccp_tr_exwmreq, CCTVDBP, CCPR_NOR) /* request to relinquish write mode */ CCP_TABLE_ENTRY (CCTR_FLUSHLK, ccp_tr_flushlk, CCTVFIL, CCPR_NOR) /* wait for prior machine's flush to finish */ CCP_TABLE_ENTRY (CCTR_GOTDRT, ccp_tr_gotdrt, CCTVDBP, CCPR_HI) /* got enq for dirty buffers acquired */ CCP_TABLE_ENTRY (CCTR_LKRQWAKE, ccp_tr_lkrqwake, CCTVFIL, CCPR_NOR) /* request wake up lock wait procs whole cluster*/ CCP_TABLE_ENTRY (CCTR_NULL, ccp_tr_null, CCTVNUL, CCPR_NOR) /* noop */ CCP_TABLE_ENTRY (CCTR_OPENDB1, ccp_tr_opendb1, CCTVFAB, CCPR_HI) /* data base open, second phase */ CCP_TABLE_ENTRY (CCTR_OPENDB1A, ccp_tr_opendb1a, CCTVFAB, CCPR_HI) /* step two of first phase of opening */ CCP_TABLE_ENTRY (CCTR_OPENDB1B, ccp_tr_opendb1b, CCTVDBP, CCPR_HI) /* deadlock detected getting locks, retry */ CCP_TABLE_ENTRY (CCTR_OPENDB1E, ccp_tr_opendb1e, CCTVFAB, CCPR_HI) /* error opening database */ CCP_TABLE_ENTRY (CCTR_OPENDB3, ccp_tr_opendb3, CCTVDBP, CCPR_HI) /* data base open, third phase */ CCP_TABLE_ENTRY (CCTR_OPENDB3A, ccp_tr_opendb3a, CCTVDBP, CCPR_HI) /* journal file opening */ CCP_TABLE_ENTRY (CCTR_QUEDUMP, ccp_tr_quedump, CCTVSTR, CCPR_HI) /* request dump of queues */ CCP_TABLE_ENTRY (CCTR_STOP, ccp_tr_stop, CCTVNUL, CCPR_NOR) /* operator stop */ CCP_TABLE_ENTRY (CCTR_WRITEDB, ccp_tr_writedb, CCTVFIL, CCPR_NOR) /* request write mode */ CCP_TABLE_ENTRY (CCTR_WRITEDB1, ccp_tr_writedb1, CCTVDBP, CCPR_HI) /* request write mode - ENQ was granted */ fis-gtm-V7.0-005/sr_port/cdb_sc.h0000755000032200000250000001142214342376331015434 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #ifndef CDB_SC #define CDB_SC #include "gdsroot.h" /********************************* WARNING: *********************************** * Several of these codes are concurrently defined in GVCST_BLK_SEARCH.MAR, * * GVCST_SEARCH.MAR, MUTEX.MAR, and MUTEX_STOPREL.MAR. If their positions * * are changed here, their definitions must be modified there as well! * ********************************************************************************/ enum cdb_sc { #define CDB_SC_NUM_ENTRY(code, final_retry_ok, value) code = value, #define CDB_SC_UCHAR_ENTRY(code, final_retry_ok, is_wcs_code, value) code = value, #define CDB_SC_LCHAR_ENTRY(code, final_retry_ok, is_wcs_code, value) code = value, #include "cdb_sc_table.h" #undef CDB_SC_NUM_ENTRY #undef CDB_SC_UCHAR_ENTRY #undef CDB_SC_LCHAR_ENTRY }; GBLREF boolean_t is_final_retry_code_num[]; GBLREF boolean_t is_final_retry_code_uchar[]; GBLREF boolean_t is_final_retry_code_lchar[]; GBLREF int sizeof_is_final_retry_code_num; GBLREF int sizeof_is_final_retry_code_uchar; GBLREF int sizeof_is_final_retry_code_lchar; static inline boolean_t is_final_retry_code(enum cdb_sc status) { int idx; boolean_t val; assert(status <= 'z'); if (status < 'A') { /* numeric */ idx = status; assert(0 <= idx); assert((sizeof_is_final_retry_code_num / SIZEOF(boolean_t)) > idx); val = is_final_retry_code_num[idx]; } else if (status <= 'Z') { /* upper case */ idx = status - 'A'; assert(0 <= idx); assert((sizeof_is_final_retry_code_uchar / SIZEOF(boolean_t)) > idx); val = is_final_retry_code_uchar[idx]; } else { /* lower case */ idx = status - 'a'; assert(0 <= idx); assert((sizeof_is_final_retry_code_lchar / SIZEOF(boolean_t)) > idx); val = is_final_retry_code_lchar[idx]; } return val; } /* This macro is used in places that don't rely on t_retry() to handle the possibility of a retry in the final try. * For example, database trigger handling code assumes that a structural issue with a trigger global is due to a * concurrent update and not a broken entry in the DB. Once the final retry has been exhausted, the trigger code * path issues a TRIGDEFBAD error. is_final_retry_code enhances that check against CDB_STAGNATE to ensure that the * final retry has truly been exhausted. */ #define UPDATE_CAN_RETRY(TRIES, CURRSTATUS) \ ((CDB_STAGNATE > TRIES) || (is_final_retry_code(CURRSTATUS))) #define TP_TRACE_HIST_MOD(BLK_NUM, BLK_TARGET, N, CSD, HISTTN, BTTN, LEVEL) \ MBSTART { \ DEBUG_ONLY(GBLREF uint4 dollar_tlevel;) \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ if (TREF(tprestart_syslog_delta)) \ { /* next 4 lines of code are identical to TP_TRACE_HIST (below), but repetion saves an if when it matters */ \ assert(dollar_tlevel); \ TAREF1(t_fail_hist_blk, t_tries) = ((block_id)BLK_NUM); \ TAREF1(tp_fail_hist, t_tries) = (gv_namehead *)(((block_id)BLK_NUM & ~(-BLKS_PER_LMAP)) ? BLK_TARGET : NULL); \ TAREF1(tp_fail_hist_reg, t_tries) = gv_cur_region; \ (CSD)->tp_cdb_sc_blkmod[(N)]++; \ TREF(blkmod_fail_level) = (LEVEL); \ TREF(blkmod_fail_type) = (N); \ TAREF1(tp_fail_bttn, t_tries) = (BTTN); \ TAREF1(tp_fail_histtn, t_tries) = (HISTTN); \ } \ } MBEND #define NONTP_TRACE_HIST_MOD(BLK_SRCH_STAT, N) \ MBSTART { \ DEBUG_ONLY(GBLREF uint4 dollar_tlevel;) \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ assert(!dollar_tlevel); \ TREF(blkmod_fail_type) = (N); \ TREF(blkmod_fail_level) = (BLK_SRCH_STAT)->level; \ TAREF1(t_fail_hist_blk, t_tries) = (BLK_SRCH_STAT)->blk_num; \ } MBEND #define TP_TRACE_HIST(BLK_NUM, BLK_TARGET) \ MBSTART { \ DEBUG_ONLY(GBLREF uint4 dollar_tlevel;) \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ if (TREF(tprestart_syslog_delta)) \ { \ assert(dollar_tlevel); \ TAREF1(t_fail_hist_blk, t_tries) = ((block_id)BLK_NUM); \ TAREF1(tp_fail_hist, t_tries) = (gv_namehead *)(((block_id)BLK_NUM & ~(-BLKS_PER_LMAP)) ? BLK_TARGET : NULL); \ TAREF1(tp_fail_hist_reg, t_tries) = gv_cur_region; \ } \ } MBEND #endif fis-gtm-V7.0-005/sr_port/cdb_sc_table.h0000755000032200000250000002527314342376331016614 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2019 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. * * * ****************************************************************/ /* New entries should be added at the end to maintain backward compatibility */ /* Note: This is an exception where we have 132+ characters in a line */ /* * CDB_SC_NUM_ENTRY(code, final_retry_ok, value) * CDB_SC_UCHAR_ENTRY(code, final_retry_ok, is_wcs_code, value) * CDB_SC_LCHAR_ENTRY(code, final_retry_ok, is_wcs_code, value) * * is_wcs_code is TRUE if the cdb_sc code is a cache-related failure code. * final_retry_ok is TRUE If the cdb_sc code is possible in the final retry. * * cdb_sc codes with numeric values are internally generated codes which is never displayed to the user and hence * can never imply a database cache related problem. hence the macro CDB_SC_NUM_ENTRY has no is_wcs_code parameter. * * Currently the failure codes considered as cache failure codes are as follows. * cdb_sc_losthist, * cdb_sc_lostcr, * cdb_sc_cacheprob, * cdb_sc_lostbefor * cdb_sc_cyclefail, * cdb_sc_lostbmlhist, * cdb_sc_lostbmlcr, * cdb_sc_crbtmismatch, * cdb_sc_phase2waitfail, * cdb_sc_bmlmod and cdb_sc_blkmod need to be added here, but they present an interesting problem. This is because * if the database has an integrity error, we will get a cdb_sc_blkmod/bmlmod error for every transaction that reads * the block with the BLKTNTOOLG integrity error in which case we do not want to set wc_blocked and cause indefinite * cache-recoveries. But to do that we need to do a wcs_verify() after setting wc_blocked and if no problems are * detected, we should unset wc_blocked. that is a little tricky and is deferred until it is considered worthy. * * Currently the failure codes considered as final_retry_ok codes are as follows. * An explanation for why these are possible in the final retry is in t_retry, tp_restart & op_trestart (for cdb_sc_optrestart). * cdb_sc_jnlstatemod * cdb_sc_jnlclose * cdb_sc_helpedout * cdb_sc_needcrit * cdb_sc_onln_rlbk1 * cdb_sc_onln_rlbk2 * cdb_sc_instancefreeze * cdb_sc_gvtrootmod2 * cdb_sc_gvtrootnonzero * cdb_sc_reorg_encrypt * cdb_sc_optrestart * cdb_sc_phase2waitfail * cdb_sc_wcs_recover */ CDB_SC_NUM_ENTRY( cdb_sc_normal, FALSE, 0) /* 0 success */ CDB_SC_NUM_ENTRY( cdb_sc_endtree, FALSE, 1) /* 1 gvcst_lftsib or gvcst_rtsib searched past end of tree */ CDB_SC_NUM_ENTRY( cdb_sc_delete_parent, FALSE, 2) /* 2 gvcst_kill_blk succeeded, but signals gvcst_kill that block was completely deleted */ CDB_SC_NUM_ENTRY( cdb_sc_nolock, FALSE, 3) /* 3 mutex_lockwim was unable to obtain a lock */ CDB_SC_NUM_ENTRY( cdb_sc_needcrit, TRUE, 4) /* 4 on 4th attempt and need crit for this region -- restart transaction no penalty */ CDB_SC_NUM_ENTRY( cdb_sc_helpedout, TRUE, 5) /* 5 wcs_blocked when t_tries >= CDB_STAGNATE */ CDB_SC_NUM_ENTRY( cdb_sc_gbloflow, FALSE, 6) /* 6 t_end or tp_tend found database full and could not be extended */ CDB_SC_NUM_ENTRY( cdb_sc_oprnotneeded, FALSE, 7) /* 7 reorg operation was not required */ CDB_SC_NUM_ENTRY( cdb_sc_starrecord, FALSE, 8) /* 8 star record was found while reading the block */ CDB_SC_NUM_ENTRY( cdb_sc_extend, FALSE, 9) /* 9 extend requested when none seemed needed - from gdsfilext */ CDB_SC_NUM_ENTRY( cdb_sc_jnlclose, TRUE, 10) /* 10 journal file has been closed */ CDB_SC_UCHAR_ENTRY(cdb_sc_wcs_recover, TRUE, FALSE, 'A') /* 'A' tp_hist/t_end found concurrent "wcs_recover" invocation */ CDB_SC_UCHAR_ENTRY(cdb_sc_keyoflow, FALSE, FALSE, 'B') /* 'B' gvcst_expand_key or gvcst_search (3) found key overflow */ CDB_SC_UCHAR_ENTRY(cdb_sc_rmisalign, FALSE, FALSE, 'C') /* 'C' Record misaligned from nearly everyone */ CDB_SC_UCHAR_ENTRY(cdb_sc_r2small, FALSE, FALSE, 'D') /* 'D' gvcst_expand_key found record too small */ CDB_SC_UCHAR_ENTRY(cdb_sc_losthist, FALSE, TRUE, 'E') /* 'E' t_end/tp_tend (bg only) - tn could not be verified from history */ CDB_SC_UCHAR_ENTRY(cdb_sc_mapfail, FALSE, FALSE, 'F') /* 'F' Not currently used */ CDB_SC_UCHAR_ENTRY(cdb_sc_lostcr, FALSE, TRUE, 'G') /* 'G' gvcst_...sib, t_end/tp_tend/tp_hist - found cache buffer modified */ CDB_SC_UCHAR_ENTRY(cdb_sc_mkblk, FALSE, FALSE, 'H') /* 'H' Composing a local block failed, from gvcst_kill(3) gvcst_put(14) */ CDB_SC_UCHAR_ENTRY(cdb_sc_rdfail, FALSE, FALSE, 'I') /* 'I' t_qread found block number requested is outside size of file as described by fileheader */ CDB_SC_UCHAR_ENTRY(cdb_sc_badlvl, FALSE, FALSE, 'J') /* 'J' gvcst_search found a child block didn't have the next block level below its parent */ CDB_SC_UCHAR_ENTRY(cdb_sc_cacheprob, FALSE, TRUE, 'K') /* 'K' db_csh_get, ... found a cache control problem */ CDB_SC_UCHAR_ENTRY(cdb_sc_blkmod, FALSE, FALSE, 'L') /* 'L' t_end, or tp_tend found block modified */ CDB_SC_UCHAR_ENTRY(cdb_sc_uperr, FALSE, FALSE, 'M') /* 'M' t_ch received an unpredicatable error */ CDB_SC_UCHAR_ENTRY(cdb_sc_comfail, FALSE, FALSE, 'N') /* 'N' Not currently used */ CDB_SC_UCHAR_ENTRY(cdb_sc_lostbefor, FALSE, TRUE, 'O') /* 'O' t_end or tp_tend found the before image needed for journaling was removed from the cache */ CDB_SC_UCHAR_ENTRY(cdb_sc_committfail, FALSE, FALSE, 'P') /* 'P' Not currently used */ CDB_SC_UCHAR_ENTRY(cdb_sc_dbccerr, FALSE, FALSE, 'Q') /* 'Q' mutex found (in 1 of 3 places) an interlock instruction failure in critical mechanism */ CDB_SC_UCHAR_ENTRY(cdb_sc_critreset, FALSE, FALSE, 'R') /* 'R' mutex found (in 1 of 6 places) that the segment crit crash count has been incremented */ CDB_SC_UCHAR_ENTRY(cdb_sc_maxlvl, FALSE, FALSE, 'S') /* 'S' t_write_root or gvcst_search found maximum legal block level for database exceeded */ CDB_SC_UCHAR_ENTRY(cdb_sc_blockflush, FALSE, FALSE, 'T') /* 'T' t_end (hist, or bitmap) found an to update a buffer that is being flushed (GT.CX) */ CDB_SC_UCHAR_ENTRY(cdb_sc_cyclefail, FALSE, TRUE, 'U') /* 'U' t_end or tp_tend found a buffer in read(only) set was overwritten though tn static */ CDB_SC_UCHAR_ENTRY(cdb_sc_optrestart, TRUE, FALSE, 'V') /* 'V' TP restart explicitly signaled by the TRESTART command */ CDB_SC_UCHAR_ENTRY(cdb_sc_future_read, FALSE, FALSE, 'W') /* 'W' Not currently used */ CDB_SC_UCHAR_ENTRY(cdb_sc_badbitmap, FALSE, FALSE, 'X') /* 'X' bm_getfree found bitmap had bad size or level */ CDB_SC_UCHAR_ENTRY(cdb_sc_badoffset, FALSE, FALSE, 'Y') /* 'Y' gvcst_blk_search (in gvcst_search_blk or gvcst_search_tail) found a bad record offset */ CDB_SC_UCHAR_ENTRY(cdb_sc_blklenerr, FALSE, FALSE, 'Z') /* 'Z' gvcst_blk_search (in gvcst_search_blk or gvcst_search_tail) reached the end with no match */ CDB_SC_LCHAR_ENTRY(cdb_sc_bmlmod, FALSE, FALSE, 'a') /* 'a' t_end or tp_tend (mm or bg) found bit_map modified */ CDB_SC_LCHAR_ENTRY(cdb_sc_lostbmlhist, FALSE, TRUE, 'b') /* 'b' t_end or tp_tend (bg) - tn could not be verified from history */ CDB_SC_LCHAR_ENTRY(cdb_sc_lostbmlcr, FALSE, TRUE, 'c') /* 'c' t_end or tp_tend (bg) - found cache buffer modified */ CDB_SC_LCHAR_ENTRY(cdb_sc_lostoldblk, FALSE, FALSE, 'd') /* 'd' Not currently used */ CDB_SC_LCHAR_ENTRY(cdb_sc_blknumerr, FALSE, FALSE, 'e') /* 'e' t_qread or op_tcommit - block number is impossible */ CDB_SC_LCHAR_ENTRY(cdb_sc_blksplit, FALSE, FALSE, 'f') /* 'f' recompute_upd_array recognized that the block needs to be split */ CDB_SC_LCHAR_ENTRY(cdb_sc_toomanyrecompute, FALSE, FALSE, 'g') /* 'g' more than 25% of the blocks in read-set need to be recomputed */ CDB_SC_LCHAR_ENTRY(cdb_sc_jnlstatemod, TRUE, FALSE, 'h') /* 'h' csd->jnl_state changed or csd->jnl_before_image changed since start of the transaction */ CDB_SC_LCHAR_ENTRY(cdb_sc_needlock, FALSE, FALSE, 'i') /* 'i' Not currently used */ CDB_SC_LCHAR_ENTRY(cdb_sc_bkupss_statemod, FALSE, FALSE, 'j') /* 'j' t_end/tp_tend found that either online-backup-in-progress or snapshot state changed since start of transaction */ CDB_SC_LCHAR_ENTRY(cdb_sc_crbtmismatch, FALSE, TRUE, 'k') /* 'k' cr->blk and bt->blk does not match */ CDB_SC_LCHAR_ENTRY(cdb_sc_phase2waitfail, TRUE, TRUE, 'l') /* 'l' wcs_phase2_commit_wait timed out when called from t_qread */ CDB_SC_LCHAR_ENTRY(cdb_sc_inhibitkills, FALSE, FALSE, 'm') /* 'm' t_end/tp_tend found inhibit_kills counter greater than zero */ CDB_SC_LCHAR_ENTRY(cdb_sc_triggermod, FALSE, FALSE, 'n') /* 'n' csd->db_trigger_cycle changed since start of of transaction */ CDB_SC_LCHAR_ENTRY(cdb_sc_onln_rlbk1, TRUE, FALSE, 'o') /* 'o' csa->onln_rlbk_cycle changed since start of transaction */ CDB_SC_LCHAR_ENTRY(cdb_sc_onln_rlbk2, TRUE, FALSE, 'p') /* 'p' csa->db_onln_rlbkd_cycle changed since start of transaction */ CDB_SC_LCHAR_ENTRY(cdb_sc_truncate, FALSE, FALSE, 'q') /* 'q' t_qread tried to read a block beyond the end of a database that has been concurrently truncated */ CDB_SC_LCHAR_ENTRY(cdb_sc_gvtrootmod, FALSE, FALSE, 'r') /* 'r' gvcst_kill found a need to redo the gvcst_root_search */ CDB_SC_LCHAR_ENTRY(cdb_sc_instancefreeze, TRUE, FALSE, 's') /* 's' instance freeze detected in t_end/tp_tend, requires retry */ CDB_SC_LCHAR_ENTRY(cdb_sc_gvtrootmod2, TRUE, FALSE, 't') /* 't' t_end/tp_tend detected root blocks moved by reorg */ CDB_SC_LCHAR_ENTRY(cdb_sc_spansize, FALSE, FALSE, 'u') /* 'u' chunks of spanning node don't add up */ CDB_SC_LCHAR_ENTRY(cdb_sc_restarted, FALSE, FALSE, 'v') /* 'v' return value indicating t_retry has already happened */ CDB_SC_LCHAR_ENTRY(cdb_sc_tqreadnowait, FALSE, FALSE, 'w') /* 'w' update helper returning from t_qread instead of sleeping */ CDB_SC_LCHAR_ENTRY(cdb_sc_reorg_encrypt, TRUE, FALSE, 'x') /* 'x' cnl->reorg_encrypt_cycle changed since start of transaction */ CDB_SC_LCHAR_ENTRY(cdb_sc_gvtrootnonzero, TRUE, FALSE, 'y') /* 'y' gvcst_put2 found a need to redo the gvcst_root_search */ fis-gtm-V7.0-005/sr_port/cdbg_dump.c0000755000032200000250000002606414342376331016146 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_stdio.h" #include "gtm_string.h" #include "gtmdbglvl.h" #include "compiler.h" #include "opcode.h" #include "mvalconv.h" #include "cdbg_dump.h" #include "stringpool.h" #include "cache.h" #include "gtmio.h" #include "have_crit.h" #include "mdq.h" #include "cgp.h" LITDEF char *oprtype_names[] = { "Operand[0]", "Operand[1]", "Destination" }; /* This table needs to be synchronized with the operclass enum definition in compiler.h */ LITDEF char *oprtype_type_names[] = { "NIL", /* 0 */ "TVAR_REF", /* 1 */ "TVAL_REF", /* 2 */ "TINT_REF", /* 3 */ "TVAD_REF", /* 4 */ "TCAD_REF", /* 5 */ "MLIT_REF", /* 6 */ "MVAR_REF", /* 7 */ "TRIP_REF", /* 8 */ "TNXT_REF", /* 9 */ "TJMP_REF", /* 10 */ "INDR_REF", /* 11 */ "MLAB_REF", /* 12 */ "ILIT_REF", /* 13 */ "CDLT_REF", /* 14 */ "TEMP_REF", /* 15 */ "MFUN_REF", /* 16 */ "MNXL_REF", /* 17 */ "TSIZ_REF", /* 18 */ "OCNT_REF", /* 19 */ "CDIDX_REF" /* 20 */ }; LITDEF char *indents[11] = { "", " ", " ", " ", " ", " ", " ", " ", " ", " ", " " }; GBLREF char cg_phase; /* code generation phase */ GBLREF char *oc_tab_graphic[]; GBLREF int4 sa_temps_offset[]; GBLREF int4 sa_temps[]; GBLREF spdesc indr_stringpool; GBLREF triple t_orig; /* head of triples */ LITREF int4 sa_class_sizes[]; #define MAX_INDENT (32 * 1024) STATICDEF char *indent_str; STATICDEF int last_indent = 0; /* Routine to dump all triples on the current chain - also callable from debugger */ void cdbg_dump_triple_all(void) { triple *ct; dqloop(&t_orig, exorder, ct) { PRINTF("\n ********************** Triple Start **********************\n"); cdbg_dump_triple(ct, 0); } } /* Routine to dump a single triple and its followers */ void cdbg_dump_triple(triple *dtrip, int indent) { int len; PRINTF("%s Triple %s [opc %d at 0x%08lx] ex.fl: 0x%08lx ex.bl: 0x%08lx bp.fl:0x%08lx bp.bl:0x%08lx bpt:0x%08lx" " sline: %d colmn: %d rtaddr: %d\n", cdbg_indent(indent), oc_tab_graphic[dtrip->opcode], dtrip->opcode, (unsigned long)dtrip, (unsigned long)dtrip->exorder.fl, (unsigned long)dtrip->exorder.bl, (unsigned long)dtrip->backptr.que.fl, (unsigned long)dtrip->backptr.que.bl, (unsigned long)dtrip->backptr.bpt, dtrip->src.line, dtrip->src.column, dtrip->rtaddr); cdbg_dump_operand(indent + 1, &dtrip->operand[0], OP_0); cdbg_dump_operand(indent + 1, &dtrip->operand[1], OP_1); if (dtrip->destination.oprclass) cdbg_dump_operand(indent + 1, &dtrip->destination, OP_DEST); else if (CGP_ADDR_OPT == cg_phase) { switch(dtrip->opcode) { /* add opcodes as situations make the need clear */ case OC_GVRECTARG: PRINTF("%s ** Warning ** destination is NO_REF\n", cdbg_indent(indent + 1)); default: /* WARNING fallthrough */ break; } } FFLUSH(stdout); } /* Routine to dump a triple that's been shrunk by shrink_trips() */ void cdbg_dump_shrunk_triple(triple *dtrip, int old_size, int new_size) { PRINTF("Shrunken triple %s [0x%08lx] ex.fl: 0x%08lx ex.bl: 0x%08lx sline: %d colmn: %d rtaddr: %d\n", oc_tab_graphic[dtrip->opcode], (unsigned long)dtrip, (unsigned long)dtrip->exorder.fl, (unsigned long)dtrip->exorder.bl, dtrip->src.line, dtrip->src.column, dtrip->rtaddr); PRINTF(" old size: %d new size: %d shrinkage: %d\n", old_size, new_size, (old_size - new_size)); FFLUSH(stdout); } /* Routine to dump a triple operand (note possible recursion) */ void cdbg_dump_operand(int indent, oprtype *opr, int opnum) { triple *rtrip; int offset; int len; char *buff; char mid[(SIZEOF(mident_fixed) * 2) + 1]; /* Sized to hold an labels name rtn.lbl */ if (opr) PRINTF("%s %s [0x%08lx] Type: %s\n", cdbg_indent(indent), oprtype_names[opnum], (unsigned long)opr, oprtype_type_names[opr->oprclass]); else PRINTF("%s ** Warning ** Null opr passed as operand\n", cdbg_indent(indent)); if (!opr->oprclass) { FFLUSH(stdout); return; } /* We have a real oprclass, dump it's info */ switch(opr->oprclass) { case TVAR_REF: PRINTF("%s Temporary variable index %d\n", cdbg_indent(indent), opr->oprval.temp); break; case TCAD_REF: case TVAD_REF: PRINTF("%s %s reference - whatever it means: value is %d\n", cdbg_indent(indent), ((TCAD_REF == opr->oprclass) ? "TCAD_REF" : "TVAD_REF"), opr->oprval.temp); break; case MVAR_REF: if (opr->oprval.vref) { PRINTF("%s LS vref: 0x%08lx RS vref: 0x%08lx index: %d varname: %s fetched by: 0x%08lx\n", cdbg_indent(indent + 1),(unsigned long)opr->oprval.vref->lson, (unsigned long)opr->oprval.vref->rson, opr->oprval.vref->mvidx, cdbg_makstr(opr->oprval.vref->mvname.addr, &buff, opr->oprval.vref->mvname.len), (unsigned long)opr->oprval.vref->last_fetch); free(buff); /* allocated by cdbg_makstr */ } else PRINTF("%s ** Warning ** oprval.vref is NULL\n", cdbg_indent(indent)); break; case TINT_REF: case TVAL_REF: offset = sa_temps_offset[opr->oprclass]; offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; PRINTF("%s temp index: %d offset: 0x%08x\n", cdbg_indent(indent), opr->oprval.temp, offset); break; case ILIT_REF: PRINTF("%s ilit value: %d [0x%08x]\n", cdbg_indent(indent), opr->oprval.ilit, opr->oprval.ilit); break; case MLIT_REF: if (opr->oprval.mlit) PRINTF("%s lit-ref ml.fl: 0x%08lx ml.bl: 0x%08lx rtaddr: 0x%08lx\n", cdbg_indent(indent + 1), (unsigned long)opr->oprval.mlit->que.fl, (unsigned long)opr->oprval.mlit->que.bl, opr->oprval.mlit->rt_addr); else PRINTF("%s ** Warning ** oprval.mlit is NULL\n", cdbg_indent(indent)); cdbg_dump_mval(indent, &opr->oprval.mlit->v); break; case TJMP_REF: if (opr->oprval.tref) PRINTF("%s tjmp-ref jump list ptr: 0x%08lx\n", cdbg_indent(indent), (unsigned long)&opr->oprval.tref->jmplist); else PRINTF("%s ** Warning ** oprval.tref is NULL\n", cdbg_indent(indent)); break; case TRIP_REF: rtrip = opr->oprval.tref; cdbg_dump_triple(rtrip, indent + 1); break; case INDR_REF: cdbg_dump_operand(indent, opr->oprval.indr, opnum); break; case TSIZ_REF: if (opr->oprval.tsize) PRINTF("%s triple at 0x%08lx has size %d\n", cdbg_indent(indent), (unsigned long)opr->oprval.tsize->ct, opr->oprval.tsize->size); else PRINTF("%s ** Warning ** oprval.tsize is NULL\n", cdbg_indent(indent)); break; case OCNT_REF: PRINTF("%s offset from call to next triple: %d\n", cdbg_indent(indent), opr->oprval.offset); break; case MLAB_REF: case MFUN_REF: if (opr->oprval.lab) { len = opr->oprval.lab->mvname.len; memcpy(mid, opr->oprval.lab->mvname.addr, len); mid[len] = '\0'; PRINTF("%s ref type: %s mlabel name: %s\n", cdbg_indent(indent), oprtype_type_names[opr->oprclass], mid); } else PRINTF("%s ref type: %s ** Warning ** oprval.lab is NULL\n", cdbg_indent(indent), oprtype_type_names[opr->oprclass]); break; case CDLT_REF: if (opr->oprval.cdlt) { len = opr->oprval.cdlt->len; memcpy(mid, opr->oprval.cdlt->addr, len); mid[len] = '\0'; PRINTF("%s cdlt-ref mstr->%s", cdbg_indent(indent), mid); } else PRINTF("%s ref type: %s ** Warning ** oprval.cdlt is NULL\n", cdbg_indent(indent), oprtype_type_names[opr->oprclass]); break; case CDIDX_REF: if (opr->oprval.cdidx) { len = opr->oprval.cdidx->len; memcpy(mid, opr->oprval.cdidx->addr, len); mid[len] = '\0'; PRINTF("%s cdidx-ref mstr->%s", cdbg_indent(indent), mid); } else PRINTF("%s ref type: %s ** Warning ** oprval.cdidx is NULL\n", cdbg_indent(indent), oprtype_type_names[opr->oprclass]); break; default: PRINTF("%s %s bogus reference\n", cdbg_indent(indent), oprtype_type_names[opr->oprclass]); } FFLUSH(stdout); } /* Routine to dump a literal mval - note strings that either aren't setup completely yet or contain $ZCHAR() type * data can come out as garbage. Improvement would be to print value in $ZWRITE() format. */ void cdbg_dump_mval(int indent, mval *mv) { boolean_t first; double mvf; int4 mvd; PRINTF("%s Type: 0x%1x (", cdbg_indent(indent), mv->mvtype); first = TRUE; if (mv->mvtype & MV_NM) { PRINTF("Number"); first = FALSE; } if (mv->mvtype & MV_INT) { if (!first) PRINTF(", "); PRINTF("Integer"); first = FALSE; } if (mv->mvtype & MV_STR) { if (!first) PRINTF(", "); PRINTF("String"); FFLUSH(stdout); first = FALSE; } if (first) { PRINTF("Undefined MVAL)\n"); return; } PRINTF(") Sign: %d Exp: %d\n", mv->sgn, mv->e); if (mv->mvtype & MV_NUM_MASK) { if (mv->mvtype & MV_INT) { mvd = mval2i(mv); PRINTF("%s Integer value: %d\n", cdbg_indent(indent), mvd); } else { mvf = mval2double(mv); PRINTF("%s Double value: %f\n", cdbg_indent(indent), mvf); } } if (mv->mvtype & MV_STR) { if (!mv->str.len) PRINTF("%s String value: \n", cdbg_indent(indent)); else cdbg_dump_mstr(indent, &mv->str); } FFLUSH(stdout); } /* Dump value of a given mstr. Assumes length is non-zero */ void cdbg_dump_mstr(int indent, mstr *ms) { unsigned char *buffer, *strp; int len; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; len = ms->len; strp = (unsigned char *)ms->addr; # if defined(USHBIN_SUPPORTED) || defined(VMS) /* In shared binary mode, shrink_trips is called after indir_lits() changes the addresses * in the mvals to offsets. De-offset them if they don't point into the (indirect) * stringpool. This *ONLY* happens during an indirect compilation. */ assert(TREF(compile_time) || indr_stringpool.base <= indr_stringpool.free); if (!TREF(compile_time) && strp < indr_stringpool.base) strp += (UINTPTR_T)(indr_stringpool.base - SIZEOF(ihdtyp) - PADLEN(SIZEOF(ihdtyp), NATIVE_WSIZE)); # endif buffer = malloc(len + 1); memcpy(buffer, strp, len); buffer[len] = '\0'; PRINTF("%s String value: %s\n", cdbg_indent(indent), buffer); FFLUSH(stdout); free(buffer); } /* Provide string to do indenting of formatted output */ char *cdbg_indent(int indent) { if (10 >= indent) return (char *)indents[indent]; if (NULL == indent_str) indent_str = malloc(MAX_INDENT); if (MAX_INDENT < (indent * 2)) { FFLUSH(stdout); assertpro(MAX_INDENT < (indent * 2)); } if (indent > last_indent) memset(indent_str, ' ', indent * 2); indent_str[indent * 2] = 0; last_indent = indent; return indent_str; } /* Make a given addr/len string into a null terminate string */ char *cdbg_makstr(char *str, char **buf, int len) { *buf = malloc(len + 1); memcpy(*buf, str, len); (*buf)[len] = '\0'; return *buf; } fis-gtm-V7.0-005/sr_port/cdbg_dump.h0000755000032200000250000000206014342376331016141 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ #ifndef CDBG_DUMP_H #define CDBG_DUMP_H /* Values for 2nd arg of cdbg_dump_operand */ #define OP_0 0 /* operand[0] is passed */ #define OP_1 1 /* operand[1] is passed */ #define OP_DEST 2 /* destination is passed */ void cdbg_dump_triple_all(void); void cdbg_dump_triple(triple *dtrip, int indent); void cdbg_dump_shrunk_triple(triple *dtrip, int old_size, int new_size); void cdbg_dump_operand(int indent, oprtype *opr, int opnum); void cdbg_dump_mval(int indent, mval *mv); void cdbg_dump_mstr(int indent, mstr *ms); char *cdbg_indent(int indent); char *cdbg_makstr(char *str, char **buf, mstr_len_t len); #endif fis-gtm-V7.0-005/sr_port/ceil_log2.c0000644000032200000250000000357714342376331016062 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2019 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" /* ceil_log2_table[i] = # of bits needed to represent i */ static int ceil_log2_table[] = { 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4 }; /* Compute the ceiling(log_2(num)) where num is a 64-bit quantity */ int ceil_log2_64bit(gtm_uint64_t num) { int ret; assert(num); STATIC_ANALYSIS_ONLY(if (0 == num) return 0); /* 4SCA: No caller can pass in zero */ num--; ret = 0; if (((gtm_uint64_t)1 << 32) <= num) { ret += 32; num = num >> 32; } if ((1 << 16) <= num) { ret += 16; num = num >> 16; } if ((1 << 8) <= num) { ret += 8; num = num >> 8; } if ((1 << 4) <= num) { ret += 4; num = num >> 4; } assert(ARRAYSIZE(ceil_log2_table) > num); /* Now that "num" is a small number, use lookup table to speed up computation */ ret += ceil_log2_table[num]; return ret; } /* Compute the ceiling(log_2(num)) where num is a 32-bit quantity */ int ceil_log2_32bit(uint4 num) { int ret; assert(num); STATIC_ANALYSIS_ONLY(if (0 == num) return 0); /* 4SCA: No caller can pass in zero */ num--; ret = 0; if ((1 << 16) <= num) { ret += 16; num = num >> 16; } if ((1 << 8) <= num) { ret += 8; num = num >> 8; } if ((1 << 4) <= num) { ret += 4; num = num >> 4; } assert(ARRAYSIZE(ceil_log2_table) > num); /* Now that "num" is a small number, use lookup table to speed up computation */ ret += ceil_log2_table[num]; return ret; } fis-gtm-V7.0-005/sr_port/ceprep_file.c0000755000032200000250000000725714342376331016502 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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" #ifdef VMS #include static struct FAB ceprep_fab; /* file access block for compiler escape preprocessor output */ static struct RAB ceprep_rab; /* record access block for compiler escape preprocessor output */ #endif #include "gtm_string.h" #include "cmd_qlf.h" #include "compiler.h" #include "io.h" #include "io_params.h" #include "op.h" #include "comp_esc.h" GBLREF mident module_name; GBLREF io_pair io_curr_device; GBLREF command_qualifier cmd_qlf; LITREF mval literal_zero; static io_pair dev_in_use; void open_ceprep_file(void) { #ifdef VMS /* stub except for VMS */ static readonly struct { unsigned char newversion; unsigned char wrap; unsigned char width; int4 v_width; unsigned char eol; } open_params_list = { (unsigned char) iop_newversion, (unsigned char) iop_wrap, (unsigned char) iop_recordsize, (int4) MAX_SRCLINE, (unsigned char) iop_eol }; int mname_len; uint4 status; char charspace, ceprep_name_buff[MAX_MIDENT_LEN + SIZEOF(".MCI") - 1], fname[255]; mval file, params; struct NAM ceprep_nam; /* name block for file access block for compiler escape preprocessor output */ /* Create cepreprocessor output file. */ ceprep_fab = cc$rms_fab; ceprep_fab.fab$l_dna = ceprep_name_buff; mname_len = module_name.len; assert(mname_len <= MAX_MIDENT_LEN); if (0 == mname_len) { MEMCPY_LIT(ceprep_name_buff, "MDEFAULT.MCI"); ceprep_fab.fab$b_dns = SIZEOF("MDEFAULT.MCI") - 1; } else { memcpy(ceprep_name_buff, module_name.addr, mname_len); MEMCPY_LIT(&ceprep_name_buff[mname_len], ".MCI"); ceprep_fab.fab$b_dns = mname_len + SIZEOF(".MCI") - 1; } if (MV_DEFINED(&cmd_qlf.ceprep_file)) { ceprep_fab.fab$b_fns = cmd_qlf.ceprep_file.str.len; ceprep_fab.fab$l_fna = cmd_qlf.ceprep_file.str.addr; } ceprep_nam = cc$rms_nam; ceprep_nam.nam$l_esa = fname; ceprep_nam.nam$b_ess = SIZEOF(fname); ceprep_nam.nam$b_nop = (NAM$M_SYNCHK); ceprep_fab.fab$l_nam = &ceprep_nam; ceprep_fab.fab$l_fop = FAB$M_NAM; if (RMS$_NORMAL != (status = sys$parse(&ceprep_fab, 0, 0))) rts_error_csa(NULL VARLSTCNT(1) status); file.mvtype = params.mvtype = MV_STR; file.str.len = ceprep_nam.nam$b_esl; file.str.addr = fname; params.str.len = SIZEOF(open_params_list); params.str.addr = &open_params_list; op_open(&file, ¶ms, (mval *)&literal_zero, 0); params.str.len = 1; charspace = (char)iop_eol; params.str.addr = &charspace; dev_in_use = io_curr_device; op_use(&file, ¶ms); #endif return; } void close_ceprep_file(void) { #ifdef VMS /* stub except for VMS */ unsigned char charspace; mval param, ceprep_file; param.str.len = 1; charspace = (char)iop_eol; param.str.addr = &charspace; ceprep_file.mvtype = param.mvtype = MV_STR; ceprep_file.str.len = io_curr_device.in->trans_name->len; ceprep_file.str.addr = io_curr_device.in->trans_name->dollar_io; op_close(&ceprep_file, ¶m); io_curr_device = dev_in_use; #endif return; } void put_ceprep_line(void) { #ifdef VMS /* stub except for VMS */ mval out; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; out.mvtype = MV_STR; out.str.len = (TREF(source_buffer)).len - 1; out.str.addr = (TREF(source_buffer)).addr; op_write(&out); op_wteol(1); #endif return; } fis-gtm-V7.0-005/sr_port/cert_blk.h0000755000032200000250000000361714342376331016013 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2017 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. * * * ****************************************************************/ #ifndef CERT_BLK_DEFINED #define CERT_BLK_DEFINED enum { RTS_ERROR_ON_CERT_FAIL, /* 0 */ ASSERTPRO_ON_CERT_FAIL, /* 1 */ SEND_MSG_ON_CERT_FAIL /* 2 */ }; int cert_blk(gd_region *reg, block_id blk, blk_hdr_ptr_t bp, block_id root, int4 error_action, gv_namehead *gv_target); #define CERT_BLK_IF_NEEDED(certify_all_blocks, gv_cur_region, cs, blk_ptr, gv_target) \ { \ assert(gds_t_create != cs->mode); /* should have morphed into gds_t_acquired before getting here */ \ if (certify_all_blocks) \ { /* assertpro on integ error */ \ /* If cs->mode is of type kill_t_create, then it would have been assigned an arbitrary block# \ * inside of the transaction which could match a real block # in the database at this point. \ * For example, it could be identical to the root block of the global in which case cert_blk \ * will assume this is the root block when actually this is a block that is no longer part of \ * the tree. So pass an invalid block# that indicates it is a created block. \ */ \ cert_blk(gv_cur_region, \ ((kill_t_create != cs->mode) ? cs->blk : GDS_CREATE_BLK_MAX), (blk_hdr_ptr_t)blk_ptr, dollar_tlevel \ ? ((NULL != cs->blk_target) ? cs->blk_target->root : 0) \ : ((NULL != gv_target) ? gv_target->root : 0), ASSERTPRO_ON_CERT_FAIL, gv_target); \ } \ } #endif fis-gtm-V7.0-005/sr_port/cg_var.h0000755000032200000250000000112114342376331015453 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2004 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef CG_VAR_H_INCLUDED #define CG_VAR_H_INCLUDED void cg_var(mvar *v, var_tabent **p); void ind_cg_var(mvar *v, var_tabent **p); #endif fis-gtm-V7.0-005/sr_port/cgp.h0000755000032200000250000000204614342376331014772 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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. * * * ****************************************************************/ /* Phases the GT.M compiler can be in */ enum CGP_PHASE { CGP_NOSTATE = 0, /* 0 - compiler not running */ CGP_PARSE, /* 1 - compiler initialized - parsing into triples */ CGP_RESOLVE, /* 2 - resolve triple references to each other */ CGP_APPROX_ADDR, /* 3 - approximate addresses with pseudo-code-gen */ CGP_ADDR_OPT, /* 4 - address optimization and triple reduction */ CGP_ASSEMBLY, /* 5 - generate assembler listing */ CGP_MACHINE, /* 6 - generate machine code */ CGP_FINI, /* 7 - compile complete - cleanup */ CGP_MAXSTATE }; fis-gtm-V7.0-005/sr_port/change_reg.c0000644000032200000250000000315514342376331016275 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "change_reg.h" #include "tp_set_sgm.h" #include "repl_msg.h" /* needed for gtmsource.h */ #include "gtmsource.h" /* needed for jnlpool_addrs typedef */ GBLREF gd_region *gv_cur_region; GBLREF sgmnt_data_ptr_t cs_data; GBLREF sgmnt_addrs *cs_addrs; GBLREF uint4 dollar_tlevel; GBLREF jnlpool_addrs_ptr_t jnlpool; void change_reg(void) { if (!gv_cur_region) { cs_addrs = (sgmnt_addrs *)0; cs_data = (sgmnt_data_ptr_t)0; return; } switch (gv_cur_region->dyn.addr->acc_meth) { case dba_usr: case dba_cm: cs_addrs = (sgmnt_addrs *)0; cs_data = (sgmnt_data_ptr_t)0; break; case dba_mm: case dba_bg: cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; cs_data = cs_addrs->hdr; if (cs_addrs->jnlpool && (jnlpool != cs_addrs->jnlpool)) jnlpool = cs_addrs->jnlpool; if (dollar_tlevel) tp_set_sgm(); break; default: assertpro(gv_cur_region->dyn.addr->acc_meth != gv_cur_region->dyn.addr->acc_meth); } } fis-gtm-V7.0-005/sr_port/change_reg.h0000755000032200000250000000106014342376331016276 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef CHANGE_REG_INCLUDED #define CHANGE_REG_INCLUDED void change_reg(void); #endif /* CHANGE_REG_INCLUDED */ fis-gtm-V7.0-005/sr_port/chk2lev.m0000755000032200000250000000152714342376331015567 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2001 Sanchez Computer Associates, 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; chk2lev ;check for op codes which are missing slots New x,i Set x="" For Set x=$o(work(x)) Quit:x="" Do . If x#1 Do Quit . . Set x=x\1 . . If "OC_CO"'=$Extract(opx(x),1,5) Do . . . For i=x+.1:.1:x+.3 If '$Data(work(i)) Write !,"subcode ",i-x*10," is missing for op code ",x," (",opx(x),")",! . . Set x=x+.9 . If $Order(work(x))\1=x Write !,"triple code ",x," (",opx(x),") is present with subcodes",! Quit fis-gtm-V7.0-005/sr_port/chkop.m0000755000032200000250000000117114342376331015330 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2001 Sanchez Computer Associates, 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; chkop ;display undefined triple codes u "" n x w !,"missing triple op codes: ",! s x="" chkop1 s x=$o(opx(x)) i x="" g chkop9 i '$d(work(x)),'$d(work(x+.1)) w x,?7,opx(x),! g chkop1 chkop9 q fis-gtm-V7.0-005/sr_port/chktchain.c0000644000032200000250000000246014342376331016145 0ustar librarygtc/**************************************************************** * * * Copyright 2007 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 "compiler.h" /* Check that the triple chain is a well formed doubly linked list */ void chktchain(triple *head) { triple *tp, *tp2; /* tp2 is needed to detect cycles in the list (so we avoid looping indefinitely) that dont return to the head */ for (tp = head->exorder.fl, tp2 = tp->exorder.fl; tp != head; tp = tp->exorder.fl, tp2 = tp2->exorder.fl->exorder.fl) { if (tp->exorder.bl->exorder.fl != tp) GTMASSERT; /* if this assert fails, then recompile with DEBUG_TRIPLES to catch the issue sooner */ if (tp->exorder.fl->exorder.bl != tp) GTMASSERT; /* if this assert fails, then recompile with DEBUG_TRIPLES to catch the issue sooner */ if (tp == tp2) /* cycle found, but without returning to the head of the linked list */ GTMASSERT; /* if this assert fails, then recompile with DEBUG_TRIPLES to catch the issue sooner */ } } fis-gtm-V7.0-005/sr_port/cli_get_str_ele.c0000755000032200000250000000162314342376331017337 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2018 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 "cliif.h" LITREF unsigned char lower_to_upper_table[]; boolean_t cli_get_str_ele(char *inbuff, char *dst, unsigned short *dst_len, boolean_t upper_case) { *dst_len = 0; while (*inbuff && ',' != *inbuff) { if (upper_case) *dst++ = lower_to_upper_table[*inbuff++]; else *dst++ = *inbuff++; (*dst_len)++; } *dst = 0; return (*dst_len ? TRUE : FALSE); } fis-gtm-V7.0-005/sr_port/cli_port.c0000755000032200000250000002116514342376331016032 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_ctype.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_limits.h" #include #include "cli.h" #include "util.h" /* A lot of stuff that can be made portable across unix and vvms cli.c needs to be moved into this module. * For a start, cli_str_to_hex() is moved in. At least cli_get_str(), cli_get_int(), cli_get_num() can be moved in later. */ /* *------------------------------------------------------- * Check if string is a decimal number * * Return: * TRUE - only decimal digits * FALSE - otherwise * ------------------------------------------------------- */ int cli_is_dcm(char *p) { while (*p && ISDIGIT_ASCII(*p)) p++; if (*p) return (FALSE); else return (TRUE); } /* * -------------------------------------------------- * Convert string to hex. * * Return: * TRUE - OK * FALSE - Could not convert to hex * -------------------------------------------------- */ boolean_t cli_str_to_hex(char *str, uint4 *dst) { unsigned long result; int save_errno; save_errno = errno; errno = 0; result = STRTOUL(str, NULL, 16); if ( #if INT_MAX < LONG_MAX (UINT_MAX < result) || /* outside UINT range */ #endif (ERANGE == errno && ULONG_MAX == result) || (0 == result && 0 != errno)) { /* out of range or other error */ *dst = 0; errno = save_errno; return FALSE; } else { *dst = (uint4)result; errno = save_errno; return TRUE; } } /* * -------------------------------------------------- * Convert string to 64 bit hex. * * Return: * TRUE - OK * FALSE - Could not convert to hex * -------------------------------------------------- */ boolean_t cli_str_to_hex64(char *str, gtm_uint64_t *dst) { gtm_uint64_t result; int save_errno; save_errno = errno; errno = 0; result = STRTOU64L(str, NULL, 16); if ((ERANGE == errno && GTM_UINT64_MAX == result) || (0 == result && 0 != errno)) { /* out of range or other error */ *dst = 0; errno = save_errno; return FALSE; } else { *dst = result; errno = save_errno; return TRUE; } } /* * -------------------------------------------------- * Convert string to 64 bit unsigned int. * * Return: * TRUE - OK * FALSE - Could not convert to int * -------------------------------------------------- */ boolean_t cli_str_to_uint64(char *str, gtm_uint64_t *dst) { gtm_uint64_t result; int save_errno; save_errno = errno; errno = 0; result = STRTOU64L(str, NULL, 10); if ((ERANGE == errno && GTM_UINT64_MAX == result) || (0 == result && 0 != errno)) { /* out of range or other error */ *dst = 0; errno = save_errno; return FALSE; } else { *dst = result; errno = save_errno; return TRUE; } } /* * -------------------------------------------------- * Convert string to int. * * Return: * TRUE - OK * FALSE - Could not convert to int * -------------------------------------------------- */ boolean_t cli_str_to_int(char *str, int4 *dst) { long result; int save_errno; save_errno = errno; errno = 0; result = STRTOL(str, NULL, 10); if ( #if INT_MAX < LONG_MAX (INT_MIN > result || INT_MAX < result) || /* outside INT range */ #endif (ERANGE == errno && (LONG_MIN == result || LONG_MAX == result)) || (0 == result && 0 != errno)) { /* out of range or other error */ *dst = 0; errno = save_errno; return FALSE; } else { *dst = (int4)result; errno = save_errno; return TRUE; } } /* * -------------------------------------------------- * Convert string to 64 bit int. * * Return: * TRUE - OK * FALSE - Could not convert to int * -------------------------------------------------- */ boolean_t cli_str_to_int64(char *str, gtm_int64_t *dst) { gtm_int64_t result; int save_errno; save_errno = errno; errno = 0; result = STRTO64L(str, NULL, 10); if ((ERANGE == errno && (GTM_INT64_MIN == result || GTM_INT64_MAX == result)) || (0 == result && 0 != errno)) { /* out of range or other error */ *dst = 0; errno = save_errno; return FALSE; } else { *dst = result; errno = save_errno; return TRUE; } } /* * -------------------------------------------------- * Convert string to number. * * Return: * TRUE - OK * FALSE - Could not convert to number * -------------------------------------------------- */ boolean_t cli_str_to_num(char *str, int4 *dst) { long result; int save_errno, base; save_errno = errno; errno = 0; if (cli_is_dcm(str)) base = 10; else base = 16; result = STRTOL(str, NULL, base); if ( #if INT_MAX < LONG_MAX (INT_MIN > result || INT_MAX < result) || /* outside INT range */ #endif (ERANGE == errno && (LONG_MIN == result || LONG_MAX == result)) || (0 == result && 0 != errno)) { /* out of range or other error */ *dst = 0; errno = save_errno; return FALSE; } else { *dst = (int4)result; errno = save_errno; return TRUE; } } /* * -------------------------------------------------- * Convert string to 64 bit number. * * Return: * TRUE - OK * FALSE - Could not convert to number * -------------------------------------------------- */ boolean_t cli_str_to_num64(char *str, gtm_int64_t *dst) { gtm_int64_t result; int save_errno, base; save_errno = errno; errno = 0; if (cli_is_dcm(str)) base = 10; else base = 16; result = STRTO64L(str, NULL, base); if ((ERANGE == errno && (GTM_INT64_MIN == result || GTM_INT64_MAX == result)) || (0 == result && 0 != errno)) { /* out of range or other error */ *dst = 0; errno = save_errno; return FALSE; } else { *dst = result; errno = save_errno; return TRUE; } } int cli_parse_two_numbers(char *qual_name, const char delimiter, uint4 *first_num, uint4 *second_num) { /* Parse two unsigned base 10 numbers separated by the given delimiter. Eg. -LOG_INTERVAL=10,20 (on VMS, -LOG_INTERVAL="10,20"). * Both Unix and VMS accept the qualifier as a string. NOTE: On VMS, such qualifiers are quoted strings. * Both numbers are optional (eg. -LOG_INTERVAL=10, or -LOG_INTERVAL=",20", or -LOG_INTERVAL=,). * Return values: * CLI_2NUM_FIRST_SPECIFIED (binary 10), first number specified, second not * CLI_2NUM_SECOND_SPECIFIED (binary 01), first number not specified, second is * CLI_2NUM_BOTH_SPECIFIED (binary 11) (CLI_2NUM_FIRST_SPECIFIED | CLI_2NUM_SECOND_SPECIFIED), both specified * 0 (binary 00), error in parsing either number */ char *first_num_str, *second_num_str, *two_num_str_top, *num_endptr; char two_num_qual_str[128]; unsigned short two_num_qual_len; uint4 num, prev_value; int retval = 0; char *num_ch; two_num_qual_len = SIZEOF(two_num_qual_str); if (!cli_get_str(qual_name, two_num_qual_str, &two_num_qual_len)) { util_out_print("Error parsing !AZ qualifier", TRUE, qual_name); return 0; } first_num_str = two_num_qual_str; for (second_num_str = first_num_str, two_num_str_top = first_num_str + two_num_qual_len; second_num_str < two_num_str_top && delimiter != *second_num_str; second_num_str++) ; if (delimiter == *second_num_str) *second_num_str++ = '\0'; if (*first_num_str != '\0') /* VMS issues EINVAL if strtoul is passed null string */ { num = 0; prev_value = 0; for (num_ch = first_num_str; ((*num_ch >= '0' && *num_ch <= '9') && (*num_ch != '\0')); num_ch++) { num = num * 10 + (*num_ch - '0'); if (num < prev_value) break; prev_value = num; } if((0 > num || num > INT_MAX) || *num_ch != '\0') { util_out_print("Error parsing or invalid parameter for !AZ", TRUE, qual_name); return 0; } *first_num = num; retval |= CLI_2NUM_FIRST_SPECIFIED; } /* else, first number not specified */ if (second_num_str < two_num_str_top && *second_num_str != '\0') { num = 0; prev_value = 0; for (num_ch = second_num_str; ((*num_ch >= '0' && *num_ch <= '9') && (*num_ch != '\0')); num_ch++) { num = num * 10 + (*num_ch - '0'); if (num < prev_value) break; prev_value = num; } if((0 > num || num > INT_MAX) || *num_ch != '\0') { util_out_print("Error parsing or invalid parameter for LOG_INTERVAL", TRUE); return 0; } *second_num = num; retval |= CLI_2NUM_SECOND_SPECIFIED; } /* else, second number not specified */ return retval; } fis-gtm-V7.0-005/sr_port/cliif.h0000755000032200000250000000511214342376331015304 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #ifndef CLIIF_included #define CLIIF_included boolean_t cli_get_hex(char *entry, uint4 *dst); boolean_t cli_get_int(char *entry, int4 *dst); boolean_t cli_get_hex64(char *entry, gtm_uint64_t *dst); boolean_t cli_get_uint64(char *entry, gtm_uint64_t *dst); boolean_t cli_get_int64(char *entry, gtm_int64_t *dst); boolean_t cli_get_num(char *entry, int4 *dst); boolean_t cli_get_num64(char *entry, gtm_int64_t *dst); boolean_t cli_get_str(char *entry, char *dst, unsigned short *max_len); boolean_t cli_get_str_ele(char *inbuff, char *dst, unsigned short *dst_len, boolean_t upper_case); boolean_t cli_get_time(char *entry, uint4 *dst); boolean_t cli_get_value(char *entry, char val_buf[]); boolean_t cli_negated(char *entry); boolean_t cli_str_to_hex(char *str, uint4 *dst); boolean_t cli_str_to_hex64(char *str, gtm_uint64_t *dst); boolean_t cli_str_to_uint64(char *str, gtm_uint64_t *dst); boolean_t cli_str_to_int(char *str, int4 *dst); boolean_t cli_str_to_int64(char *str, gtm_int64_t *dst); boolean_t cli_str_to_num(char *str, int4 *dst); boolean_t cli_str_to_num64(char *str, gtm_int64_t *dst); int4 cli_t_f_n(char *entry); int4 cli_n_a_e(char *entry); int cli_get_string_token(int *eof); int cli_gettoken(int *eof); int cli_is_assign(char *p); int cli_is_dcm(char *p); int cli_is_hex(char *p); int cli_is_hex_explicit(char *p); int cli_is_qualif(char *p); int cli_look_next_string_token(int *eof); int cli_look_next_token(int *eof); int cli_present(char *entry); /***type int added***/ void cli_str_setup(uint4 length, char *addr); void cli_strupper(char *sp); int cli_parse_two_numbers(char *qual_name, const char delimiter, uint4 *first_num, uint4 *second_num); boolean_t cli_get_defertime(char *entry, int4 *dst); #ifdef __osf__ /* N.B. argv is passed in from main (in gtm.c) almost straight from the operating system. */ #pragma pointer_size (save) #pragma pointer_size (long) #endif void cli_lex_setup(int argc, char *argv[]); #ifdef __osf__ #pragma pointer_size (restore) #endif #define CLI_2NUM_FIRST_SPECIFIED 0x2 #define CLI_2NUM_SECOND_SPECIFIED 0x1 #endif /* CLIIF_included */ fis-gtm-V7.0-005/sr_port/cmd.c0000644000032200000250000002602514342376331014757 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "compiler.h" #include "mdq.h" #include "opcode.h" #include "toktyp.h" #include "nametabtyp.h" #include "mmemory.h" #include "advancewindow.h" #include "cmd.h" #include "namelook.h" #include "error.h" #define VMS_OS 01 #define UNIX_OS 02 #define ALL_SYS (VMS_OS | UNIX_OS) #ifdef UNIX /* command validation is a function of the OS */ # define VALID_CMD(i) (cmd_data[i].os_syst & UNIX_OS) # ifdef __hppa # define TRIGGER_OS 0 # else # define TRIGGER_OS UNIX_OS # endif #elif defined VMS # define VALID_CMD(i) (cmd_data[i].os_syst & VMS_OS) # define TRIGGER_OS 0 #else # error UNSUPPORTED PLATFORM #endif error_def(ERR_CMD); error_def(ERR_CNOTONSYS); error_def(ERR_EXPR); error_def(ERR_INVCMD); error_def(ERR_PCONDEXPECTED); error_def(ERR_SPOREOL); LITDEF nametabent cmd_names[] = { /* Must be in alpha order and in sync with cmd_index[], cmd_data[]; when inerting beware of legacy implications */ {1, "B"}, {5, "BREAK"} ,{1, "C"}, {5, "CLOSE"} ,{1, "D"}, {2, "DO"} ,{1, "E"}, {4, "ELSE"} ,{1, "F"}, {3, "FOR"} ,{1, "G"}, {4, "GOTO"} ,{1, "H"}, {4, "HALT"}, {4, "HANG"} ,{1, "I"}, {2, "IF"} ,{1, "J"}, {3, "JOB"} ,{1, "K"}, {4, "KILL"} ,{1, "L"}, {4, "LOCK"} ,{1, "M"}, {5, "MERGE"} ,{1, "N"}, {3, "NEW"} ,{1, "O"}, {4, "OPEN"} ,{1, "Q"}, {4, "QUIT"} ,{1, "R"}, {4, "READ"} ,{1, "S"}, {3, "SET"} ,{2, "TC"}, {7, "TCOMMIT"} ,{3, "TRE"}, {8, "TRESTART"} ,{3, "TRO"}, {9, "TROLLBACK"} ,{2, "TS"}, {6, "TSTART"} ,{1, "U"}, {3, "USE"} ,{1, "V"}, {4, "VIEW"} ,{1, "W"}, {5, "WRITE"} ,{1, "X"}, {6, "XECUTE"} ,{9, "ZALLOCATE"} ,{7, "ZATTACH"} ,{6, "ZBREAK"} ,{2, "ZC"} /* legacy abbreviation for ZCONTINUE */ ,{4, "ZCOM"} ,{9, "ZCONTINUE"} ,{8, "ZCOMPILE"} ,{11, "ZDEALLOCATE"} ,{5, "ZEDIT"} ,{5, "ZGOTO"} ,{2, "ZH"} /* legacy abbreviation for ZHELP */ ,{5, "ZHALT"} ,{5, "ZHELP"} ,{7, "ZINVCMD"} ,{5, "ZKILL"} ,{5, "ZLINK"} ,{8, "ZMESSAGE"} ,{6, "ZPRINT"} # ifdef AUTORELINK_SUPPORTED ,{8, "ZRUPDATE"} # endif ,{5, "ZSHOW"} ,{5, "ZSTEP"} ,{7, "ZSYSTEM"} ,{8, "ZTCOMMIT"} # ifdef GTM_TRIGGER ,{8, "ZTRIGGER"} # endif ,{7, "ZTSTART"} ,{6, "ZWATCH"} ,{9, "ZWITHDRAW"} ,{6, "ZWRITE"} }; /* * cmd_index is an array indexed by the first alphabet of the command-name * cmd_index[0] is for commands beginning with 'A' ... cmd_index[26] element for commands beginning with 'Z' * The cmd_index[n] holds the index of the first element in cmd_names * whose command-name begins with the same 'n'th letter of the alphabet - A is 1st letter but in C terms get the 0 index. * if there are no cmds_starting with the letter the index does not change * e.g. with no cmd starting with 'A' cmd0] and cmd[1] both have values of 0 * Example: * Say, [B]REAK is the command. * 'B'-'A' = 1. and cmd_index[1] = 0, and cmd_index[1+1]=2, so names for BREAK are within cmd_names[0] and cmd_names[1] * Say, [C]LOSE is the command. * 'C'-'A' = 2. and cmd_index[2] = 2 and cmd_index[2+1]=4, so names for CLOSE are within cmd_names[2] and cmd_names[3] * Say, [D]O is the command. * 'D'-'A' = 3. and cmd_index[3] = 4 and cmd_index[3+1]=6, so names for DO are within cmd_names[4] and cmd_names[5] * Say, [I]F is the command. * 'I'-'A' = 8. and cmd_index[8] = 15 and cmd_index[8+1]=17, so names for IF are within cmd_names[15] and cmd_names[16] * Say, [M]ERGE the command. * 'M'-'A' = 12. and cmd_index[12] = 23 and cmd_index[12+1]=25, so name for MERGE are with in cmd_names[23] and cmd_names[24] */ LITDEF unsigned char cmd_index[27] = { 0, 0, 2, 4, 6, 8, 10, 12, 15, /* a b c d e f g h i */ 17, 19, 21, 23 ,25, 27, 29, 29, 31, /* j k l m n o p q r */ 33, 35, 43, 45, 47, 49 ,51, 51, 77 /* s t u v w x y z ~ */ GTMTRIG_ONLY(+ 1) ARLINK_ONLY(+ 1) /* add ztrigger and zrupdate, respectively */ }; LITDEF struct { int (*fcn)(); unsigned int eol_ok:1; unsigned int pcnd_ok:1; char os_syst; } cmd_data[] = { {m_break, 1, 1, ALL_SYS}, {m_break, 1, 1, ALL_SYS} ,{m_close, 0, 1, ALL_SYS}, {m_close, 0, 1, ALL_SYS} ,{m_do, 1, 1, ALL_SYS}, {m_do, 1, 1, ALL_SYS} ,{m_else, 1, 0, ALL_SYS}, {m_else, 1, 0, ALL_SYS} ,{m_for, 0, 0, ALL_SYS}, {m_for, 0, 0, ALL_SYS} ,{m_goto, 0, 1, ALL_SYS}, {m_goto, 0, 1, ALL_SYS} ,{m_hcmd, 1, 1, ALL_SYS} ,{m_halt, 1, 1, ALL_SYS} ,{m_hang, 0, 1, ALL_SYS} ,{m_if, 1, 0, ALL_SYS}, {m_if, 1, 0, ALL_SYS} ,{m_job, 0, 1, ALL_SYS}, {m_job, 0, 1, ALL_SYS} ,{m_kill, 1, 1, ALL_SYS}, {m_kill, 1, 1, ALL_SYS} ,{m_lock, 1, 1, ALL_SYS}, {m_lock, 1, 1, ALL_SYS} ,{m_merge, 0, 1, ALL_SYS}, {m_merge, 0, 1, ALL_SYS} ,{m_new, 1, 1, ALL_SYS}, {m_new, 1, 1, ALL_SYS} ,{m_open, 0, 1, ALL_SYS}, {m_open, 0, 1, ALL_SYS} ,{m_quit, 1, 1, ALL_SYS}, {m_quit, 1, 1, ALL_SYS} ,{m_read, 0, 1, ALL_SYS}, {m_read, 0, 1, ALL_SYS} ,{m_set, 0, 1, ALL_SYS}, {m_set, 0, 1, ALL_SYS} ,{m_tcommit, 1, 1, ALL_SYS}, {m_tcommit, 1, 1, ALL_SYS} ,{m_trestart, 1, 1, ALL_SYS}, {m_trestart, 1, 1, ALL_SYS} ,{m_trollback, 1, 1, ALL_SYS}, {m_trollback, 1, 1, ALL_SYS} ,{m_tstart, 1, 1, ALL_SYS}, {m_tstart, 1, 1, ALL_SYS} ,{m_use, 0, 1, ALL_SYS}, {m_use, 0, 1, ALL_SYS} ,{m_view, 0, 1, ALL_SYS}, {m_view, 0, 1, ALL_SYS} ,{m_write, 0, 1, ALL_SYS}, {m_write, 0, 1, ALL_SYS} ,{m_xecute, 0, 1, ALL_SYS}, {m_xecute, 0, 1, ALL_SYS} ,{m_zallocate, 0, 1, ALL_SYS} ,{m_zattach, 1, 1, ALL_SYS} ,{m_zbreak, 0, 1, ALL_SYS} ,{m_zcontinue, 1, 1, ALL_SYS} ,{m_zcompile, 0, 1, ALL_SYS} ,{m_zcontinue, 1, 1, ALL_SYS} ,{m_zcompile, 0, 1, ALL_SYS} ,{m_zdeallocate, 1, 1, ALL_SYS} ,{m_zedit, 1, 1, ALL_SYS} ,{m_zgoto, 1, 1, ALL_SYS} ,{m_zhelp, 1, 1, ALL_SYS} ,{m_zhalt, 1, 1, ALL_SYS} ,{m_zhelp, 1, 1, ALL_SYS} ,{m_zinvcmd, 1, 1, ALL_SYS} ,{m_zwithdraw, 0, 1, ALL_SYS} ,{m_zlink, 1, 1, ALL_SYS} ,{m_zmessage, 0, 1, ALL_SYS} ,{m_zprint, 1, 1, ALL_SYS} # ifdef AUTORELINK_SUPPORTED ,{m_zrupdate, 0, 1, ALL_SYS} # endif ,{m_zshow, 1, 1, ALL_SYS} ,{m_zstep, 1, 1, ALL_SYS} ,{m_zsystem, 1, 1, ALL_SYS} ,{m_ztcommit, 1, 1, ALL_SYS} # ifdef GTM_TRIGGER ,{m_ztrigger, 0, 1, TRIGGER_OS} # endif ,{m_ztstart, 1, 1, ALL_SYS} ,{m_zwatch, 0, 1, 0} ,{m_zwithdraw, 0, 1, ALL_SYS} ,{m_zwrite, 1, 1, ALL_SYS} }; int cmd(void) { /* module driving parsing of commands */ /* All the commands are listed here. Two pairs of entries in general. * One for full command and one for short-hand notation. * For example, B and and BREAK. */ boolean_t shifting; char *c; int rval, x; int4 fetch_cnt; oprtype *cr; triple *fetch0, *fetch1, *oldchain, *ref0, *ref1, *temp_expr_start, tmpchain, *triptr; mval *v; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert((SIZEOF(cmd_names) / SIZEOF(nametabent)) == cmd_index[26]); while (TREF(expr_depth)) DECREMENT_EXPR_DEPTH; /* in case of prior errors */ (TREF(side_effect_base))[0] = FALSE; TREF(temp_subs) = FALSE; CHKTCHAIN(TREF(curtchain), exorder, FALSE); TREF(pos_in_chain) = *TREF(curtchain); if (TK_IDENT != TREF(window_token)) { stx_error(ERR_CMD); return FALSE; } assert(0 != (TREF(window_ident)).len); c = (TREF(window_ident)).addr; if ('%' == *c) { stx_error(ERR_CMD); return FALSE; } if (0 > (x = namelook(cmd_index, cmd_names, c, (TREF(window_ident)).len))) { /* if there's a postconditional, use ZINVCMD to let us peal off any arguments and get to the rest of the line */ if ((TK_COLON != TREF(director_token)) || (0 > (x = namelook(cmd_index, cmd_names, "ZINVCMD", 7)))) { /* the 2nd term of the above if should perform the assignment, but never be true - we're just paranoid */ stx_error(MAKE_MSG_TYPE(ERR_INVCMD, ERROR)); /* force INVCMD to an error so stx_error sees it as hard */ return FALSE; } stx_error(ERR_INVCMD); /* use warning form so stx_error treats it as provisional */ } if (!VALID_CMD(x)) { stx_error(ERR_CNOTONSYS); return FALSE; } oldchain = NULL; advancewindow(); fetch0 = (TREF(fetch_control)).curr_fetch_trip; fetch1 = (TREF(fetch_control)).curr_fetch_opr; if ((TK_COLON != TREF(window_token)) || !cmd_data[x].pcnd_ok) { assert(m_zinvcmd != cmd_data[x].fcn); cr = NULL; shifting = FALSE; } else { fetch_cnt = (TREF(fetch_control)).curr_fetch_count; advancewindow(); cr = (oprtype *)mcalloc(SIZEOF(oprtype)); if (!bool_expr(FALSE, cr)) { stx_error(ERR_PCONDEXPECTED); return FALSE; } /* the next block could be simpler if done earlier, but doing it here picks up any Boolean optimizations */ triptr = (TREF(curtchain))->exorder.bl; while (OC_NOOP == triptr->opcode) triptr = triptr->exorder.bl; if (OC_LIT == triptr->opcode) { v = &triptr->operand[0].oprval.mlit->v; if (0 == MV_FORCE_BOOL(v)) { /* it's FALSE, so no need for this parse - get ready to discard it */ dqinit(&tmpchain, exorder); oldchain = setcurtchain(&tmpchain); TREF(discard) = (NULL != oldchain); } unuse_literal(v); dqdel(triptr, exorder); /* if it's TRUE, so just pretend it never appeared */ } if (shifting = ((TREF(expr_start) != TREF(expr_start_orig)) && (OC_NOOP != (TREF(expr_start))->opcode))) { /* NOTE - assignment above */ temp_expr_start = TREF(expr_start); triptr = newtriple(OC_GVRECTARG); triptr->operand[0] = put_tref(temp_expr_start); } } if (TK_SPACE == TREF(window_token)) advancewindow(); else if ((TK_EOL != TREF(window_token)) || !cmd_data[x].eol_ok) { if (NULL != oldchain) { setcurtchain(oldchain); TREF(discard) = FALSE; } stx_error(ERR_SPOREOL); return FALSE; } for (;;) { if ((EXPR_FAIL == (rval = (*cmd_data[x].fcn)())) || (TK_COMMA != TREF(window_token))) /* NOTE assignment */ break; else { advancewindow(); if ((TK_SPACE == TREF(window_token)) || (TK_EOL == TREF(window_token))) { if (NULL != oldchain) { setcurtchain(oldchain); TREF(discard) = FALSE; } stx_error(ERR_EXPR); return FALSE; } } } if (NULL != oldchain) { /* for a literal 0 postconditional, we just throw the command & args away and return happiness */ (TREF(fetch_control)).curr_fetch_trip = fetch0; (TREF(fetch_control)).curr_fetch_opr = fetch1; (TREF(fetch_control)).curr_fetch_count = fetch_cnt; setcurtchain(oldchain); TREF(discard) = FALSE; return TRUE; } if ((EXPR_FAIL != rval) && cr) { if (fetch0 != (TREF(fetch_control)).curr_fetch_trip) { assert(OC_FETCH == (TREF(fetch_control)).curr_fetch_trip->opcode); *cr = put_tjmp((TREF(fetch_control)).curr_fetch_trip); } else { if (shifting) { /* the following appears to be a hack ensuring emit_code doesn't find any unmatched OC_GVRECTARG */ ref0 = newtriple(OC_JMP); ref1 = newtriple(OC_GVRECTARG); ref1->operand[0] = put_tref(temp_expr_start); *cr = put_tjmp(ref1); tnxtarg(&ref0->operand[0]); } else tnxtarg(cr); } } return rval; } fis-gtm-V7.0-005/sr_port/cmd.h0000755000032200000250000000326214342376331014765 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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. * * * ****************************************************************/ #ifndef CMD_INCLUDED #define CMD_INCLUDED int cmd(void); int m_break(void); int m_close(void); int m_do(void); int m_else(void); int m_xecute(void); int m_for(void); int m_goto(void); int m_goto_postcond(triple *oldchain, triple *tmpchain); int m_halt(void); int m_hang(void); int m_hcmd(void); int m_if(void); int m_job(void); int m_kill(void); int m_lock(void); int m_merge(void); int m_new(void); int m_open(void); int m_quit(void); int m_read(void); int m_set(void); int m_tcommit(void); int m_trestart(void); int m_trollback(void); int m_tstart(void); int m_use(void); int m_view(void); int m_write(void); int m_xecute(void); int m_zallocate(void); int m_zattach(void); int m_zbreak(void); int m_zcompile(void); int m_zcontinue(void); int m_zdeallocate(void); int m_zedit(void); int m_zgoto(void); int m_zhalt(void); int m_zhelp(void); int m_zinvcmd(void); int m_zlink(void); int m_zmessage(void); int m_zprint(void); #ifdef USHBIN_SUPPORTED int m_zrupdate(void); #endif int m_zshow(void); int m_zstep(void); int m_zsystem(void); int m_ztcommit(void); #ifdef GTM_TRIGGER int m_ztrigger(void); #endif int m_ztstart(void); int m_zwatch(void); int m_zwithdraw(void); int m_zwrite(void); #endif fis-gtm-V7.0-005/sr_port/cmd_qlf.h0000644000032200000250000000510114342376331015616 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef CMD_QLF_H_INCLUDED #define CMD_QLF_H_INCLUDED typedef struct { uint4 qlf; mval object_file; mval list_file; mval ceprep_file; mval rtnname; } command_qualifier; typedef struct { unsigned short page; /* page number */ unsigned short list_line; /* listing line number */ unsigned short lines_per_page; unsigned short space; /* spacing */ } list_params; /* command qualifer bit masks */ #define CQ_LIST (1 << 0) /* 0x0001 */ #define CQ_MACHINE_CODE (1 << 1) /* 0x0002 */ #define CQ_CROSS_REFERENCE (1 << 2) /* 0x0004 */ #define CQ_DEBUG (1 << 3) /* 0x0008 */ #define CQ_OBJECT (1 << 4) /* 0x0010 */ #define CQ_WARNINGS (1 << 5) /* 0x0020 */ #define CQ_IGNORE (1 << 6) /* 0x0040 */ #define CQ_LOWER_LABELS (1 << 7) /* 0x0080 */ #define CQ_LINE_ENTRY (1 << 8) /* 0x0100 */ #define CQ_CE_PREPROCESS (1 << 9) /* 0x0200 */ #define CQ_INLINE_LITERALS (1 << 10) /* 0x0400 */ #define CQ_ALIGN_STRINGS (1 << 11) /* 0x0800 */ #define CQ_UTF8 (1 << 12) /* 0x1000 */ #define CQ_NAMEOFRTN (1 << 13) /* 0x2000 */ #define CQ_DYNAMIC_LITERALS (1 << 14) /* 0x4000 */ #define CQ_EMBED_SOURCE (1 << 15) /* 0x8000 */ /* TODO: add CQ_ALIGN_STRINGS to the default list below when alignment is supported */ #define CQ_DEFAULT (CQ_WARNINGS | CQ_OBJECT | CQ_IGNORE | CQ_LOWER_LABELS | CQ_LINE_ENTRY | CQ_INLINE_LITERALS) #define LISTTAB 10 #define PG_WID 132 #define INIT_CMD_QLF_STRINGS(CMD_QLF, OBJ_FILE, LIST_FILE, CEPREP_FILE, SIZE) \ { \ CMD_QLF.object_file.str.addr = OBJ_FILE; \ CMD_QLF.object_file.str.len = SIZE; \ CMD_QLF.list_file.str.addr = LIST_FILE; \ CMD_QLF.list_file.str.len = SIZE; \ CMD_QLF.ceprep_file.str.addr = CEPREP_FILE; \ CMD_QLF.ceprep_file.str.len = SIZE; \ } typedef struct src_line_type { struct { struct src_line_type *fl,*bl; } que; mstr str; /* M source string */ int4 line; /* line number */ } src_line_struct; void zl_cmd_qlf(mstr *quals, command_qualifier *qualif, char *srcstr, unsigned short *srclen, boolean_t last); void get_cmd_qlf(command_qualifier *qualif); #endif /* CMD_QLF_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/cmerrors.msg0000755000032200000250000000232314342376331016412 0ustar librarygtc!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! ! Copyright (c) 2001-2018 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. ! ! ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .FACILITY GTCM,249/PREFIX=CMERR_ .TITLE CMERRORS Error Messages for GTCM, VAX/VMS EDITION ! ! List of known undocumented messages follows (along with a comment) (do not change "known undocumented" phrase) ! ----- Buffer to introduce new undocumented error messages without affecting UNUSEDMSGnnn match with corresponding line numbers. ! INVPROT /error/fao=0 REGNTFND /error/fao=0 CMINTQUE /fatal/fao=0 INVINTMSG /error/fao=0 CMEXCDASTLM /error/fao=0 CMSYSSRV /error/fao=0 .end fis-gtm-V7.0-005/sr_port/cmi.h0000755000032200000250000000127614342376331014775 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef CMI_INCLUDED #define CMI__INCLUDED cmi_status_t cmi_read(struct CLB *lnk); cmi_status_t cmi_write(struct CLB *lnk); #define CM_ERRBUFF_SIZE 90 + 1 #endif /* CMI_INCLUDED */ fis-gtm-V7.0-005/sr_port/cmidef.h0000755000032200000250000000300414342376331015443 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2004 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef CMIPORT_H_INCLUDED #define CMIPORT_H_INCLUDED #define CM_MSG_BUF_SIZE 512 /* Message buffer size */ #define CM_MAX_BUF_LEN ((unsigned short)0xFFFF) /* lnk->cbl is of type unsigned short, hence 64K-1 is the max currently */ /* * Connection States */ #define CM_CLB_IDLE 0 #define CM_CLB_READ 1 #define CM_CLB_WRITE 2 #define CM_CLB_CONNECT 3 #define CM_CLB_DISCONNECT 4 #define CM_CLB_WRITE_URG 5 #define CM_CLB_READ_URG 6 /* get platform specific stuff */ #include "cmidefsp.h" cmi_status_t cmi_read(struct CLB *c); cmi_status_t cmi_write(struct CLB *c); cmi_status_t cmi_open(struct CLB *c); cmi_status_t cmi_close(struct CLB *c); struct CLB *cmu_getclb(cmi_descriptor *node, cmi_descriptor *task); struct NTD *cmu_ntdroot(void); #ifndef RELQUE2PTR #define RELQUE2PTR(X) (((unsigned char *) &(X)) + ((int4) (X))) #endif #define PTR2RELQUE(DESTINATION,TARGET) (DESTINATION = (((unsigned char *) &(TARGET)) - ((unsigned char *) &(DESTINATION)))) #define QUEENT2CLB(QP, QH) (struct CLB *)((char *)(QP) - (char *)&(((struct CLB *)(0))->QH)) #endif /* CMI_INCLUDED */ fis-gtm-V7.0-005/sr_port/cmmdef.h0000755000032200000250000002253214342376331015456 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #define ZAREQUEST_SENT 16 #define LREQUEST_SENT 8 #define REQUEST_PENDING 4 #define REMOTE_ZALLOCATES 2 #define REMOTE_LOCKS 1 #define REMOTE_CLR_MASK (ZAREQUEST_SENT + LREQUEST_SENT + REMOTE_ZALLOCATES + REMOTE_LOCKS) #define CM_BUFFER_OVERHEAD 20 #define CM_BLKPASS 40 #define CMM_PROTOCOL_TYPE "GCM" #define S_PROTOCOL "VAXVMSGTM023GCM010 " #define S_HDRSIZE 1 #define S_PROTSIZE 33 #define S_REGINSIZE 6 #define S_LAFLAGSIZE 1 #define S_SUBLISTSIZE 1 #define CM_MINBUFSIZE 512 + CM_BUFFER_OVERHEAD #define CMLCK_REQUEUE 0 #define CM_LOCKS 0 #define CM_ZALLOCATES 0x80 #define CM_NOLKCANCEL 256 #define CM_WRITE 1 #define CM_READ 0 #define CM_NOOP 2 #define CMMS_E_ERROR 1 /* [0x01] */ #define CMMS_L_LKCANALL 2 /* [0x02] */ #define CMMS_L_LKCANCEL 3 /* [0x03] */ #define CMMS_L_LKDELETE 4 /* [0x04] */ #define CMMS_L_LKREQIMMED 5 /* [0x05] */ #define CMMS_L_LKREQNODE 6 /* [0x06] */ #define CMMS_L_LKREQUEST 7 /* [0x07] */ #define CMMS_L_LKRESUME 8 /* [0x08] */ #define CMMS_L_LKACQUIRE 9 /* [0x09] */ #define CMMS_L_LKSUSPEND 10 /* [0x0A] */ #define CMMS_M_LKABORT 11 /* [0x0B] */ #define CMMS_M_LKBLOCKED 12 /* [0x0C] */ #define CMMS_M_LKGRANTED 13 /* [0x0D] */ #define CMMS_M_LKDELETED 14 /* [0x0E] */ #define CMMS_M_LKSUSPENDED 15 /* [0x0F] */ #define CMMS_Q_DATA 16 /* [0x10] */ #define CMMS_Q_GET 17 /* [0x11] */ #define CMMS_Q_KILL 18 /* [0x12] */ #define CMMS_Q_ORDER 19 /* [0x13] */ #define CMMS_Q_PREV 20 /* [0x14] */ #define CMMS_Q_PUT 21 /* [0x15] */ #define CMMS_Q_QUERY 22 /* [0x16] */ #define CMMS_Q_ZWITHDRAW 23 /* [0x17] */ #define CMMS_R_DATA 24 /* [0x18] */ #define CMMS_R_GET 25 /* [0x19] */ #define CMMS_R_KILL 26 /* [0x1A] */ #define CMMS_R_ORDER 27 /* [0x1B] */ #define CMMS_R_PREV 28 /* [0x1C] */ #define CMMS_R_PUT 29 /* [0x1D] */ #define CMMS_R_QUERY 30 /* [0x1E] */ #define CMMS_R_ZWITHDRAW 31 /* [0x1F] */ #define CMMS_R_UNDEF 32 /* [0x20] */ #define CMMS_S_INITPROC 33 /* [0x21] */ #define CMMS_S_INITREG 34 /* [0x22] */ #define CMMS_S_TERMINATE 35 /* [0x23] */ #define CMMS_S_INTERRUPT 36 /* [0x24] */ #define CMMS_T_INITPROC 37 /* [0x25] */ #define CMMS_T_REGNUM 38 /* [0x26] */ #define CMMS_X_INQPROC 39 /* [0x27] */ #define CMMS_X_INQPRRG 40 /* [0x28] */ #define CMMS_X_INQREG 41 /* [0x29] */ #define CMMS_Y_STATPROCREC 42 /* [0x2A] */ #define CMMS_Y_STATPRRGREC 43 /* [0x2B] */ #define CMMS_Y_STATREGREC 44 /* [0x2C] */ #define CMMS_U_LKEDELETE 45 /* [0x2D] */ #define CMMS_U_LKESHOW 46 /* [0x2E] */ #define CMMS_V_LKESHOW 47 /* [0x2F] */ #define CMMS_E_TERMINATE 48 /* [0x30] */ #define CMMS_B_BUFRESIZE 49 /* [0x31] */ #define CMMS_B_BUFFLUSH 50 /* [0x32] */ #define CMMS_C_BUFRESIZE 51 /* [0x33] */ #define CMMS_C_BUFFLUSH 52 /* [0x34] */ #define CMMS_Q_INCREMENT 53 /* [0x35] */ /* Opcode for message type sent from client (to server) */ #define CMMS_R_INCREMENT 54 /* [0x36] */ /* Opcode for message type received by client (from server) */ #define CMM_QUERYGET_MIN_LEVEL "200" /* $query works as queryget only from version "V200" onwards */ #define CMM_INCREMENT_MIN_LEVEL "210" /* $INCREMENT works only from version "V210" onwards */ #define CMM_STDNULLCOLL_MIN_LEVEL "210" /* Standard null collation works only from version "V210" onwards */ #define CMM_LONGNAMES_MIN_LEVEL "210" /* long name works only from protocol "V210" onwards */ typedef struct cm_region_list_struct { que_ent regque; struct cm_region_list_struct *next; unsigned char regnum; unsigned char oper; unsigned short lks_this_cmd; bool reqnode; char filler[3]; struct cm_region_head_struct *reghead; struct cs_struct *cs; struct mlk_pvtblk_struct *blkd; struct mlk_pvtblk_struct *lockdata; uint4 pini_addr; } cm_region_list; typedef struct cs_struct { que_ent qent; cm_region_list *region_root; cm_region_list *current_region; struct CLB *clb_ptr; unsigned char state; unsigned char new_msg; unsigned char maxregnum; bool waiting_in_queue; struct timespec connect; /* Debugging tool -- time connection was established */ time_t lastact; /* Debugging tool -- time of last server action */ uint4 stats; unsigned short procnum; unsigned short transnum; unsigned short lk_cancel; unsigned short last_cancelled; /* hold transnum of last cancelled lock request */ struct /* hold info from interrupt cancel msg */ { /* laflag can be 0, x40, x80 */ unsigned char laflag; /* + 1 if valid */ unsigned char transnum; /* for lk_cancel */ } int_cancel; struct jnl_process_vector_struct *pvec; boolean_t query_is_queryget; /* based on client/server protocol levels, query == queryget */ boolean_t err_compat; /* based on client/server protocol levels (and platform type), * rts_error mechanism b/n client and server might be different */ boolean_t cli_supp_allowexisting_stdnullcoll;/* decided based on client's protocol levels */ boolean_t client_supports_long_names; /* based on client's levels */ cm_region_list *region_array[256]; /* [UCHAR_MAX + 1] speed up gtcm_find_region */ } connection_struct; typedef struct cm_region_head_struct { relque head; struct cm_region_head_struct *next; struct cm_region_head_struct *last; connection_struct *connect_ptr; struct gd_region_struct *reg; gtm_uint64_t wakeup; uint4 refcnt; hash_table_mname *reg_hash; } cm_region_head; typedef struct cm_lk_response_struct { struct cm_lk_response_struct *next; struct CLB *response; } cm_lk_response; typedef struct link_info_struct { unsigned char neterr; unsigned char lck_info; unsigned char lnk_active; char filler; struct mlk_pvtblk_struct *netlocks; unsigned short procnum; unsigned short buffered_count; unsigned short buffer_size; unsigned short buffer_used; unsigned char *buffer; boolean_t convert_byteorder; boolean_t query_is_queryget; /* based on client/server protocol levels, query == queryget */ boolean_t err_compat; /* based on client/server protocol levels (and platform type), * rts_error mechanism b/n client and server might be different */ cm_lk_response lk_response; boolean_t server_supports_dollar_incr; /* decided based on server protocol levels */ boolean_t server_supports_std_null_coll; /* decided based on server protocol levels */ boolean_t server_supports_long_names; /* decided based on server protocol levels */ } link_info; typedef struct { char code; char rnum; bool all; bool interactive; int4 pid; char nodelength; char node[32]; } clear_request; typedef struct { char code; char filler[3]; int4 status; int4 locknamelength; char lockname[256]; } clear_reply; typedef struct { char code; bool clear; } clear_confirm; typedef struct { char code; char rnum; bool all; bool wait; int4 pid; char nodelength; char node[32]; } show_request; typedef struct { char code; char line[256]; } show_reply; #define CM_CPU_OFFSET 0 #define CM_OS_OFFSET 3 #define CM_IMPLEMENTATION_OFFSET 6 #define CM_VERSION_OFFSET 9 #define CM_TYPE_OFFSET 12 #define CM_LEVEL_OFFSET 15 #define CM_ENDIAN_OFFSET 18 #define CM_FILLER_SIZE 14 typedef struct { char msg[S_PROTSIZE]; } protocol_msg; #define CM_PUT_USHORT(PTR, USVAL, CONVFLAG) \ { \ if (CONVFLAG) \ { \ unsigned short val = GTM_BYTESWAP_16(USVAL); \ PUT_USHORT(PTR, val); \ } \ else \ PUT_USHORT(PTR, USVAL); \ } #define CM_PUT_SHORT(PTR, SVAL, CONVFLAG) \ { \ if (CONVFLAG) \ { \ short val = GTM_BYTESWAP_16(SVAL); \ PUT_SHORT(PTR, val); \ } \ else \ PUT_SHORT(PTR, SVAL); \ } #define CM_PUT_ULONG(PTR, ULVAL, CONVFLAG) \ { \ if (CONVFLAG) \ { \ uint4 val = GTM_BYTESWAP_32(ULVAL); \ PUT_ULONG(PTR, val); \ } \ else \ PUT_ULONG(PTR, ULVAL); \ } #define CM_PUT_LONG(PTR, LVAL, CONVFLAG) \ { \ if (CONVFLAG) \ { \ int4 val = GTM_BYTESWAP_32(LVAL); \ PUT_LONG(PTR, val); \ } \ else \ PUT_LONG(PTR, LVAL); \ } #define CM_GET_USHORT(USVAR, PTR, CONVFLAG) \ { \ if (CONVFLAG) \ { \ unsigned short val; \ GET_USHORT(val, (PTR)); \ USVAR = GTM_BYTESWAP_16(val); \ } \ else \ GET_USHORT((USVAR), (PTR)); \ } #define CM_GET_SHORT(SVAR, PTR, CONVFLAG) \ { \ if (CONVFLAG) \ { \ short val; \ GET_SHORT(val, (PTR)); \ SVAR = GTM_BYTESWAP_16(val); \ } \ else \ GET_SHORT((SVAR), (PTR)); \ } #define CM_GET_ULONG(ULVAR, PTR, CONVFLAG) \ { \ if (CONVFLAG) \ { \ uint4 val; \ GET_ULONG(val, (PTR)); \ ULVAR = GTM_BYTESWAP_32(val); \ } \ else \ GET_ULONG((ULVAR), (PTR)); \ } #define CM_GET_LONG(LVAR, PTR, CONVFLAG) \ { \ if (CONVFLAG) \ { \ int4 val; \ GET_LONG(val, (PTR)); \ LVAR = GTM_BYTESWAP_32(val); \ } \ else \ GET_LONG((LVAR), (PTR)); \ } #define CM_GET_GVCURRKEY(PTR, LEN) PTR = gtcmtr_get_key(gv_currkey, PTR, LEN); fis-gtm-V7.0-005/sr_port/code_address_type.h0000644000032200000250000000203014342376331017667 0ustar librarygtc/**************************************************************** * * * Copyright 2007, 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. * * * ****************************************************************/ #ifndef CODE_ADDRESS_TYPE_INCLUDE #define CODE_ADDRESS_TYPE_INCLUDE #include "mdef.h" #ifdef __ia64 GBLREF int function_type(char*); #endif /* __ia64 */ #define GTM_C_RTN 1 #define GTM_ASM_RTN 2 #ifndef __ia64 #define CODE_ADDRESS_TYPE(func) &func #else /* __ia64 */ #define CODE_ADDRESS_TYPE(func) CODE_ADDRESS(func) #endif /* __ia64 */ #endif /* CODE_ADDRESS_TYPE_INCLUDE */ fis-gtm-V7.0-005/sr_port/coerce.c0000644000032200000250000000573614342376331015462 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "compiler.h" #include "opcode.h" #include "mdq.h" #include "mvalconv.h" #include "hashtab_str.h" GBLREF hash_table_str *complits_hashtab; LITREF octabstruct oc_tab[]; void coerce(oprtype *a, unsigned short new_type) /* ensure operand (*a) is of the desired type new_type */ { boolean_t litdltd; ht_ent_str *litent; mliteral *lit; opctype conv, old_op; stringkey litkey; triple *coerc, *ref; assert ((OCT_MVAL == new_type) || (OCT_MINT == new_type) || (OCT_BOOL == new_type)); assert (TRIP_REF == a->oprclass); ref = a->oprval.tref; old_op = ref->opcode; if (new_type & oc_tab[old_op].octype) return; switch (old_op) { case OC_FORCENUM: if ((ref->operand[0].oprval.tref != ref->exorder.bl) || (OC_LIT != ref->operand[0].oprval.tref->opcode)) break; /* WARNING possible fallthrough */ case OC_COMVAL: case OC_COMINT: assert(TRIP_REF == ref->operand[0].oprclass); if (OC_LIT == ref->operand[0].oprval.tref->opcode) { /* compiler generated literals should include their numeric form - no need to coerce */ MV_FORCE_NUMD(&ref->operand[0].oprval.tref->operand[0].oprval.mlit->v); } ref->opcode = OC_NOOP; /* dqdel of OC_FORCENUM causes chain troubles */ ref->operand[0].oprclass = NO_REF; ref = ref->operand[0].oprval.tref; old_op = ref->opcode; if (new_type & oc_tab[old_op].octype) return; break; case OC_LIT: if (OCT_MINT != new_type) break; lit = ref->operand[0].oprval.mlit; if (!(++lit->rt_addr)) { /* completely removing this otherwise unused literal as needs to be an ILIT instead */ if (NULL != complits_hashtab && NULL != complits_hashtab->base) { /* Deleted entry is in the hash table .. remove it */ litkey.str = lit->v.str; COMPUTE_HASH_STR(&litkey); DEBUG_ONLY(litent = lookup_hashtab_str(complits_hashtab, &litkey)); assert(litent); /* Literal is there .. better be found */ assert(litent->value == (void *)lit); litdltd = delete_hashtab_str(complits_hashtab, &litkey); assert(litdltd); } dqdel(lit, que); } ref->opcode = OC_ILIT; ref->operand[0].oprclass = ILIT_REF; ref->operand[0].oprval.ilit = MV_FORCE_INTD(&(lit->v)); /* All literals, neg/pos, are allowed */ return; default: break; } if (OCT_BOOL == new_type) conv = OC_COBOOL; else if (OCT_MINT == new_type) conv = OC_COMINT; else conv = OC_COMVAL; coerc = newtriple(conv); coerc->operand[0] = put_tref(ref); *a = put_tref(coerc); return; } fis-gtm-V7.0-005/sr_port/collseq.c0000755000032200000250000000416714342376331015664 0ustar librarygtc/**************************************************************** * * * 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 "gtm_string.h" #include "io.h" #include "iosp.h" #include "collseq.h" #include "error.h" #include "trans_log_name.h" #include "gtm_logicals.h" int find_local_colltype(void) { int lct, status; char transbuf[MAX_TRANS_NAME_LEN]; mstr lognam, transnam; lognam.len = SIZEOF(LCT_PREFIX) - 1; lognam.addr = LCT_PREFIX; status = TRANS_LOG_NAME(&lognam, &transnam, transbuf, SIZEOF(transbuf), do_sendmsg_on_log2long); if (SS_NORMAL != status) return 0; lct = asc2i((uchar_ptr_t)transnam.addr, transnam.len); return lct >= MIN_COLLTYPE && lct <= MAX_COLLTYPE ? lct : 0; } collseq *ready_collseq(int act) { unsigned char filespec[SIZEOF(CT_PREFIX) + 4]; /* '4' to hold the chars in the max allowable * collation sequence (255) plus the terminating null */ unsigned char *fsp; collseq temp_csp, *csp; mstr fspec; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Validate the alternative collating type (act) */ if (!(act >= 1 && act <= MAX_COLLTYPE)) return (collseq*)NULL; /* Search for record of the collating type already being mapped in. */ for (csp = TREF(collseq_list); csp != NULL && act != csp->act; csp = csp->flink) ; if (NULL == csp) { /* If not found, create a structure and attempt to map in the collating support package.*/ temp_csp.act = act; temp_csp.flink = TREF(collseq_list); memcpy(filespec, CT_PREFIX, SIZEOF(CT_PREFIX)); fsp = i2asc(&filespec[SIZEOF(CT_PREFIX) - 1], act); *fsp = 0; fspec.len = INTCAST(fsp - filespec); fspec.addr = (char *)filespec; if (!map_collseq(&fspec, &temp_csp)) return NULL; csp = (collseq *) malloc(SIZEOF(collseq)); *csp = temp_csp; TREF(collseq_list) = csp; } return csp; } fis-gtm-V7.0-005/sr_port/collseq.h0000755000032200000250000000713214342376331015664 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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. * * * ****************************************************************/ #ifndef COLLSEQ_H_INCLUDED #define COLLSEQ_H_INCLUDED #include "min_max.h" #define MAX_COLLTYPE 255 #define MIN_COLLTYPE 0 #define XFORM 0 #define XBACK 1 #define XUTIL 2 #define GTM_NULL_TEXT "GTM NULL COLL" #define STD_NULL_TEXT "STD NULL COLL" #define ALLOC_XFORM_BUFF(STR1LEN) \ { \ mstr_len_t lcl_len; \ \ if (0 == TREF(max_lcl_coll_xform_bufsiz)) \ { \ assert(NULL == TREF(lcl_coll_xform_buff)); \ TREF(max_lcl_coll_xform_bufsiz) = MAX_STRBUFF_INIT; \ TREF(lcl_coll_xform_buff) = (char *)malloc(TREF(max_lcl_coll_xform_bufsiz)); \ } \ lcl_len = STR1LEN; \ assert(MAX_STRLEN >= lcl_len); \ if (lcl_len > TREF(max_lcl_coll_xform_bufsiz)) \ { \ assert(NULL != TREF(lcl_coll_xform_buff)); \ free(TREF(lcl_coll_xform_buff)); \ assert(MAX_STRLEN >= TREF(max_lcl_coll_xform_bufsiz)); \ while (lcl_len > TREF(max_lcl_coll_xform_bufsiz)) \ TREF(max_lcl_coll_xform_bufsiz) *= 2; \ TREF(max_lcl_coll_xform_bufsiz) = MIN(MAX_STRLEN, TREF(max_lcl_coll_xform_bufsiz)); \ TREF(lcl_coll_xform_buff) = (char *)malloc(TREF(max_lcl_coll_xform_bufsiz)); \ } \ } #define STD_NULL_COLL_FALSE FALSE #define STD_NULL_COLL_TRUE TRUE /* Following two macros are currently used in replication filters, merge command and binary load to transform * GTM null subscripts collation to standard null subscript collation and vice versa */ #define GTM2STDNULLCOLL(key, len) \ { \ unsigned char *currptr, *ptrtop; \ \ currptr = (unsigned char *)(key); \ ptrtop = (currptr + (len)); \ assert(currptr < ptrtop); \ do { \ if ((STR_SUB_PREFIX == *currptr++) && (KEY_DELIMITER == *currptr)) \ *(currptr - 1) = SUBSCRIPT_STDCOL_NULL; \ assert(currptr <= ptrtop); \ while ((currptr < ptrtop) && (KEY_DELIMITER != *currptr++)) \ ; \ } while ((currptr < ptrtop) && (KEY_DELIMITER != *currptr)); \ assert(currptr <= ptrtop); \ } #define STD2GTMNULLCOLL(key, len) \ { \ unsigned char *currptr, *ptrtop; \ \ currptr = (unsigned char *)(key); \ ptrtop = (currptr + (len)); \ assert(currptr < ptrtop); \ do { \ if ((SUBSCRIPT_STDCOL_NULL == *currptr++) && (KEY_DELIMITER == *currptr)) \ *(currptr - 1) = STR_SUB_PREFIX; \ assert(currptr <= ptrtop); \ while ((currptr < ptrtop) && (KEY_DELIMITER != *currptr++)) \ ; \ } while ((currptr < ptrtop) && (KEY_DELIMITER != *currptr)); \ assert(currptr <= ptrtop); \ } typedef struct collseq_struct { struct collseq_struct *flink; int act; int4 (*xform)(); int4 (*xback)(); int4 (*xutil)(); /* for zatransform -2/2 prev/next functionality */ int4 (*version)(); int4 (*verify)(); int argtype; } collseq; boolean_t map_collseq(mstr *fspec, collseq *ret_collseq); collseq *ready_collseq(int act); int4 do_verify(collseq *csp, unsigned char type, unsigned char ver); int find_local_colltype(void); #endif /* COLLSEQ_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/comline.h0000755000032200000250000000112314342376331015642 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, Inc. * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #define MAX_RECALL 99 #define MAX_RECALL_NUMBER_LENGTH 2 /* i.e. maximum recallable strings 99 */ #define clmod(x) ((x + MAX_RECALL) % MAX_RECALL) fis-gtm-V7.0-005/sr_port/common_startup_init.c0000644000032200000250000000724114342376331020310 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2022 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_limits.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gt_timer.h" #include "gtm_env_init.h" #include "get_page_size.h" #include "getjobnum.h" #include "gtmimagename.h" #include "gtm_utf8.h" #include "min_max.h" #include "common_startup_init.h" #include "deferred_events.h" GBLREF boolean_t skip_dbtriggers; GBLREF boolean_t is_replicator; GBLREF boolean_t run_time; GBLREF boolean_t write_after_image; GBLREF boolean_t dse_running; GBLREF enum gtmImageTypes image_type; GBLREF boolean_t jnlpool_init_needed; GBLREF boolean_t span_nodes_disallowed; GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF unsigned int gtm_dist_len; void common_startup_init(enum gtmImageTypes img_type) { boolean_t is_gtcm; char *dist; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* First set the global variable image_type. */ image_type = img_type; /* Get the process ID. */ getjobnum(); /* Get the OS page size. */ get_page_size(); /* Read gtm_dist. */ if (NULL != (dist = GETENV(GTM_DIST))) { gtm_dist_len = strnlen(dist, PATH_MAX); assert(GTM_PATH_MAX > gtm_dist_len); memcpy(gtm_dist, dist, gtm_dist_len); gtm_dist[gtm_dist_len] = '\0'; } else gtm_dist[0] = '\0'; /* Setup global variables corresponding to signal blocks. */ set_blocksig(); /* Do common environment initialization. */ gtm_env_init(); /* GT.M typically opens journal pool during the first update (in gvcst_put, gvcst_kill or op_ztrigger). But, if * anticipatory freeze is enabled, we want to open journal pool for any reads done by GT.M as well (basically at the time * of first database open (in gvcst_init). So, set jnlpool_init_needed to TRUE if this is GTM_IMAGE. */ jnlpool_init_needed = (GTM_IMAGE == img_type); /* Set gtm_wcswidth_fnptr for util_output to work correctly in UTF-8 mode. This is needed only for utilities that can * operate on UTF-8 mode. */ /* these attempts to keep the gtmsecshr link from dragging lots of stuff in need investigation */ gtm_wcswidth_fnptr = (GTMSECSHR_IMAGE == img_type) ? NULL : gtm_wcswidth; xfer_set_handlers_fnptr = (GTMSECSHR_IMAGE == img_type) ? NULL : xfer_set_handlers; NON_GTMTRIG_ONLY(skip_dbtriggers = TRUE;) /* Do not invoke triggers for trigger non-supporting platforms. */ span_nodes_disallowed = (GTCM_GNP_SERVER_IMAGE == img_type) || (GTCM_SERVER_IMAGE == img_type); is_gtcm = ((GTCM_GNP_SERVER_IMAGE == img_type) || (GTCM_SERVER_IMAGE == img_type)); if (is_gtcm) skip_dbtriggers = TRUE; /* GT.CM OMI and GNP servers do not invoke triggers */ if (is_gtcm || (GTM_IMAGE == img_type)) { is_replicator = TRUE; /* can go through t_end() and write jnl records to the jnlpool for replicated db */ TREF(ok_to_see_statsdb_regs) = TRUE; run_time = TRUE; } else if (DSE_IMAGE == img_type) { dse_running = TRUE; write_after_image = TRUE; /* if block change is done, after image of the block needs to be written */ TREF(ok_to_see_statsdb_regs) = TRUE; } else if (MUPIP_IMAGE == img_type) { run_time = FALSE; TREF(ok_to_see_statsdb_regs) = FALSE; /* In general, MUPIP commands should not even see the statsdb regions. * Specific MUPIP commands will override this later. */ } return; } fis-gtm-V7.0-005/sr_port/common_startup_init.h0000644000032200000250000000362714342376331020321 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2014-2018 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. * * * ****************************************************************/ #ifndef COMMON_STARTUP_INIT_DEFINED #define COMMON_STARTUP_INIT_DEFINED void common_startup_init(enum gtmImageTypes img_type); #if (defined(DEBUG) || defined(TIMER_DEBUGGING)) # include "jnl_file_close_timer.h" GBLREF void (*jnl_file_close_timer_ptr)(void); # define INIT_JNL_FILE_CLOSE_TIMER_FNPTR jnl_file_close_timer_ptr = &jnl_file_close_timer #else # define INIT_JNL_FILE_CLOSE_TIMER_FNPTR #endif #ifdef TIMER_DEBUGGING # include "fake_enospc.h" # include "gt_timer.h" GBLREF void (*fake_enospc_ptr)(void); GBLREF void (*simple_timeout_timer_ptr)(TID tid, int4 hd_len, boolean_t **timedout); # define INIT_FAKE_ENOSPC_FNPTR fake_enospc_ptr = &fake_enospc # define INIT_SIMPLE_TIMEOUT_TIMER_FNPTR simple_timeout_timer_ptr = &simple_timeout_timer #else # define INIT_FAKE_ENOSPC_FNPTR # define INIT_SIMPLE_TIMEOUT_TIMER_FNPTR #endif #ifdef DEBUG # include "error.h" GBLREF ch_ret_type (*t_ch_fnptr)(); /* Function pointer to t_ch */ GBLREF ch_ret_type (*dbinit_ch_fnptr)(); /* Function pointer to dbinit_ch */ # define INIT_DBINIT_CH_FNPTR dbinit_ch_fnptr = &dbinit_ch # define INIT_T_CH_FNPTR t_ch_fnptr = &t_ch #else # define INIT_DBINIT_CH_FNPTR # define INIT_T_CH_FNPTR #endif #define INIT_FNPTR_GLOBAL_VARIABLES \ MBSTART { \ INIT_JNL_FILE_CLOSE_TIMER_FNPTR; \ INIT_FAKE_ENOSPC_FNPTR; \ INIT_SIMPLE_TIMEOUT_TIMER_FNPTR; \ INIT_DBINIT_CH_FNPTR; \ INIT_T_CH_FNPTR; \ } MBEND #endif fis-gtm-V7.0-005/sr_port/comp_esc.h0000755000032200000250000000140514342376331016007 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ struct ce_sentinel_desc { char *escape_sentinel; int4 escape_length; int4 (*user_routine)(); struct ce_sentinel_desc *next; }; int ce_init(void); void ce_substitute(struct ce_sentinel_desc *shp, int4 source_col, int4 *skip_ct); void close_ceprep_file(void); void open_ceprep_file(void); void put_ceprep_line(void); fis-gtm-V7.0-005/sr_port/compiler_ch.c0000644000032200000250000000401014342376333016470 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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 "error.h" #include "cgp.h" #include "cmd_qlf.h" #include "list_file.h" #include "source_file.h" #include #include "obj_file.h" #include "reinit_compilation_externs.h" #include "compiler.h" #include "util.h" #include "hashtab_str.h" #include "stp_parms.h" #include "stringpool.h" GBLREF boolean_t mstr_native_align, save_mstr_native_align; GBLREF char cg_phase; GBLREF command_qualifier cmd_qlf; GBLREF spdesc indr_stringpool, rts_stringpool, stringpool; error_def(ERR_ASSERT); error_def(ERR_ERRORSUMMARY); error_def(ERR_FORCEDHALT); error_def(ERR_GTMASSERT); error_def(ERR_GTMASSERT2); error_def(ERR_GTMCHECK); error_def(ERR_MEMORY); error_def(ERR_VMSMEMORY); error_def(ERR_STACKOFLOW); error_def(ERR_OUTOFSPACE); CONDITION_HANDLER(compiler_ch) { START_CH(TRUE); if (DUMPABLE) { NEXTCH; } if (CQ_WARNINGS & cmd_qlf.qlf) PRN_ERROR; COMPILE_HASHTAB_CLEANUP; reinit_compilation_externs(); mstr_native_align = save_mstr_native_align; if (CGP_MACHINE == cg_phase) drop_object_file(); if (CGP_NOSTATE < cg_phase) { if (CGP_RESOLVE > cg_phase) close_source_file(); if ((CGP_FINI > cg_phase) && ((CQ_LIST & cmd_qlf.qlf) || (CQ_CROSS_REFERENCE & cmd_qlf.qlf))) close_list_file(); } if (TREF(compile_time)) { run_time = TRUE; TREF(compile_time) = FALSE; TREF(transform) = TRUE; } if (indr_stringpool.base == stringpool.base) { indr_stringpool = stringpool; stringpool = rts_stringpool; } TREF(dollar_zcstatus) = -ERR_ERRORSUMMARY; UNWIND(NULL, NULL); } fis-gtm-V7.0-005/sr_port/cre_private_code_copy.c0000644000032200000250000000557314342376333020552 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2002-2015 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 "iosp.h" #include "error.h" #include #include "inst_flush.h" #include "private_code_copy.h" #include "stack_frame.h" #include "gtm_text_alloc.h" #include "gtm_string.h" #include "arlinkdbg.h" error_def(UNIX_ONLY(ERR_MEMORY) VMS_ONLY(ERR_VMSMEMORY);) CONDITION_HANDLER(cre_priv_ch) { START_CH(TRUE); if (SIGNAL == UNIX_ONLY(ERR_MEMORY) VMS_ONLY(ERR_VMSMEMORY)) { UNWIND(NULL, NULL); /* ignore "lack-of-memory" error, rather not set breakpoint than error out in such a case */ } NEXTCH; } uint4 cre_private_code_copy(rhdtyp *rtn) { unsigned char *new_ptext; int code_size; # ifdef USHBIN_SUPPORTED assert(NULL != rtn->shared_ptext_adr); /* Don't need private copy if not shared */ assert(rtn->shared_ptext_adr == rtn->ptext_adr); /* If already private, we shouldn't be calling this routine */ code_size = (int)(rtn->ptext_end_adr - rtn->ptext_adr) ; ESTABLISH_RET(cre_priv_ch, UNIX_ONLY(ERR_MEMORY) VMS_ONLY(ERR_VMSMEMORY)); new_ptext = GTM_TEXT_ALLOC(code_size); REVERT; DBGARLNK((stderr, "cre_private_code_copy: Creating private code copy for rtnhdr 0x"lvaddr" at 0x"lvaddr"\n", rtn, rtn->ptext_adr)); memcpy(new_ptext, rtn->ptext_adr, code_size); adjust_frames(rtn->ptext_adr, rtn->ptext_end_adr, new_ptext); do { DBGARLNK((stderr, "cre_private_code_copy: rtnhdr 0x"lvaddr" Previous values - ptext_adr: 0x"lvaddr " ptext_end_adr: 0x"lvaddr"\n", rtn, rtn->ptext_adr, rtn->ptext_end_adr)); rtn->ptext_adr = new_ptext; rtn->ptext_end_adr = new_ptext + code_size; DBGARLNK((stderr, "cre_private_code_copy: rtnhdr 0x"lvaddr" New values - ptext_adr: 0x"lvaddr " ptext_end_adr: 0x"lvaddr"\n", rtn, rtn->ptext_adr, rtn->ptext_end_adr)); /* Check for special case loop terminator. If this was a routine copy created when a routine in use was * recursively relinked, we do not want to follow its backchain and change those modules because this * routine copy is not part of that chain. The backpointer is only to find the original routine header * when this routine terminates. So if this routine is a recursive copy, stop the loop now. */ rtn = ((NULL != rtn->old_rhead_adr) && (rtn != rtn->old_rhead_adr->active_rhead_adr)) ? (rhdtyp *)rtn->old_rhead_adr : NULL; } while (NULL != rtn); inst_flush(new_ptext, code_size); DBGARLNK((stderr, "cre_private_code_copy: Complete\n")); # endif return SS_NORMAL; } fis-gtm-V7.0-005/sr_port/compile_pattern.c0000755000032200000250000000425514342376331017405 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "compiler.h" #include "stringpool.h" #include "opcode.h" #include "mdq.h" #include "advancewindow.h" #include "compile_pattern.h" #include "patcode.h" #include "fullbool.h" GBLREF spdesc stringpool; GBLREF int source_column; int compile_pattern(oprtype *opr, boolean_t is_indirect) { int status; ptstr retstr; mval retmval; mstr instr; triple *oldchain, *ref; save_se save_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (is_indirect) { if (SHIFT_SIDE_EFFECTS) { START_GVBIND_CHAIN(&save_state, oldchain); if (!indirection(opr)) { setcurtchain(oldchain); return FALSE; } ref = newtriple(OC_INDPAT); PLACE_GVBIND_CHAIN(&save_state, oldchain); } else { if (!indirection(opr)) return FALSE; ref = newtriple(OC_INDPAT); } ref->operand[0] = *opr; *opr = put_tref(ref); return TRUE; } else { instr.addr = ((TREF(source_buffer)).addr + source_column - 1); instr.len = STRLEN(instr.addr); status = patstr(&instr, &retstr, NULL); TREF(last_source_column) = (short int)(instr.addr - (TREF(source_buffer)).addr); assert(TREF(last_source_column)); if (status) { /* status == syntax error when non-zero */ stx_error(status); return FALSE; } memset(&retmval, 0, SIZEOF(mval)); retmval.mvtype = MV_STR; retmval.str.len = retstr.len * SIZEOF(uint4); ENSURE_STP_FREE_SPACE(retmval.str.len); retmval.str.addr = (char *)stringpool.free; memcpy(stringpool.free, &retstr.buff[0], retmval.str.len); stringpool.free += retmval.str.len; *opr = put_lit(&retmval); TREF(lexical_ptr) = instr.addr; advancewindow(); advancewindow(); return TRUE; } } fis-gtm-V7.0-005/sr_port/compile_pattern.h0000755000032200000250000000121414342376331017402 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef COMPILE_PATTERN_H_INCLUDED #define COMPILE_PATTERN_H_INCLUDED int compile_pattern(oprtype *z, boolean_t is_indirect); #endif fis-gtm-V7.0-005/sr_port/compiler.h0000755000032200000250000006562014342376331016042 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ #ifndef COMPILER_H_INCLUDED #define COMPILER_H_INCLUDED /* Values for oprclass - Changes made here need to be reflected in cdbg_dump opr_type_names table */ typedef enum { NO_REF, /* 0 */ TVAR_REF, /* 1 */ TVAL_REF, /* 2 */ TINT_REF, /* 3 */ TVAD_REF, /* 4 */ TCAD_REF, /* 5 - This and all _REFs before are VALUED_REF_TYPES (see alloc_reg.c) */ MLIT_REF, /* 6 */ MVAR_REF, /* 7 */ TRIP_REF, /* 8 */ TNXT_REF, /* 9 */ TJMP_REF, /* 10 */ INDR_REF, /* 11 */ MLAB_REF, /* 12 */ ILIT_REF, /* 13 */ CDLT_REF, /* 14 - apparently no longer used */ TEMP_REF, /* 15 - apparently no longer maintained */ MFUN_REF, /* 16 */ MNXL_REF, /* 17 refer to internalentry of child line */ TSIZ_REF, /* 18 ilit refering to size of given triple codegen */ OCNT_REF, /* 19 Offset from Call to Next Triple */ CDIDX_REF /* 20 Denotes index into a table containing a code address */ } operclass; #define VALUED_REF_TYPES 6 /* Types 0-5 are specific types used by alloc_reg() in array references * **** WARNING **** Do NOT reorder */ typedef unsigned int opctype; typedef struct mvarstruct { struct mvarstruct *lson, *rson; int4 mvidx; mident mvname; struct tripletype *last_fetch; } mvar; typedef struct mvaxstruct { struct mvaxstruct *last, *next; mvar *var; int4 mvidx; } mvax; typedef struct mlinestruct { struct mlinestruct *parent, *sibling, *child; struct tripletype *externalentry; uint4 line_number; /* ...operation on this line */ boolean_t table; /* put in table or not */ boolean_t block_ok; /* saw argumentless DO or not */ } mline; typedef struct mlabstruct { struct mlabstruct *lson, *rson; mline *ml; mident mvname; int formalcnt; boolean_t gbl; } mlabel; typedef struct mliteralstruct { struct { struct mliteralstruct *fl, *bl; } que; INTPTR_T rt_addr; int reference_count; /* Used in the hash table to track references */ mval v; } mliteral; typedef struct triplesize { struct tripletype *ct; int4 size; } tripsize; typedef struct oprtypestruct { operclass oprclass; union { struct oprtypestruct *indr; struct tripletype *tref; struct triplesize *tsize; mlabel *lab; mline *mlin; mliteral *mlit; mstr *cdlt; mstr *cdidx; mvar *vref; int4 temp; int4 ilit; int4 offset; unsigned char vreg; } oprval; } oprtype; typedef struct tbptype { struct { struct tbptype *fl, *bl; } que; struct tripletype *bpt; } tbp; typedef struct { uint4 line; uint4 column; } source_address; typedef struct tripletype { opctype opcode; struct { struct tripletype *fl, *bl; } exorder; tbp backptr, /* triples which reference this triple's value */ jmplist; /* triples which jump to this one */ source_address src; int rtaddr; /* relative run time address of triple */ oprtype operand[2], destination; } triple; typedef struct { uint4 octype; } octabstruct; /* Values for octype */ #define OCT_NULL 0 #define OCT_MVAL 1 #define OCT_MINT 2 #define OCT_MVADDR 4 #define OCT_CDADDR 8 #define OCT_VALUE (OCT_MVAL | OCT_MINT | OCT_CDADDR) #define OCT_BOOL 16 #define OCT_JUMP 32 #define OCT_EXPRLEAF 64 #define OCT_CGSKIP 128 #define OCT_COERCE 256 #define OCT_ARITH 512 #define OCT_UNARY 1024 #define OCT_NEGATED 2048 #define OCT_REL 4096 typedef struct { char name[20]; opctype bo_type; char uo_type; unsigned short opr_type; } toktabtype; typedef struct { triple *curr_fetch_trip; triple *curr_fetch_opr; int4 curr_fetch_count; } fetch_ctrl; /* These two structures really belong in glvn_pool.h, but gtmpcat doesn't know to include that file. So put them here for now. */ #include "callg.h" typedef struct { opctype sav_opcode; uint4 mval_top; /* mval just beyond ones used by this entry */ uint4 precursor; /* index of previous FOR slot at same level */ mval *lvname; gparam_list glvn_info; } glvn_pool_entry; typedef struct { uint4 capacity; /* total # allocated entries */ uint4 top; /* current available glvn_pool_entry slot */ uint4 for_slot[MAX_FOR_STACK + 1]; /* indices of most recent FOR slots */ uint4 share_slot; /* currently active slot */ opctype share_opcode; /* currently active opcode */ uint4 mval_capacity; /* total # allocated mvals */ uint4 mval_top; /* current available mval in mval_stack */ mval *mval_stack; /* stack of mvals */ glvn_pool_entry slot[1]; /* stack of entries */ } glvn_pool; #define VMS_OS 01 #define UNIX_OS 02 #define ALL_SYS (VMS_OS | UNIX_OS) #ifdef UNIX /* function and svn validation are a function of the OS */ # define VALID_FUN(i) (fun_data[i].os_syst & UNIX_OS) # define VALID_SVN(i) (svn_data[i].os_syst & UNIX_OS) # ifdef __hppa # define TRIGGER_OS 0 # else # define TRIGGER_OS UNIX_OS # endif #elif defined VMS # define VALID_FUN(i) (fun_data[i].os_syst & VMS_OS) # define VALID_SVN(i) (svn_data[i].os_syst & VMS_OS) # define TRIGGER_OS 0 #else # error UNSUPPORTED PLATFORM #endif #define MUMPS_INT 0 /* integer - they only type the compiler handles differently */ #define MUMPS_EXPR 1 /* expression */ #define MUMPS_STR 2 /* string */ #define MUMPS_NUM 3 /* numeric - potentially non-integer */ #define EXPR_FAIL 0 /* expression had syntax error - frequently represented by FALSE*/ #define EXPR_GOOD 1 /* expression ok, no indirection at root - frequently represented by TRUE */ #define EXPR_INDR 2 /* expression ok, indirection at root */ #define EXPR_SHFT 4 /* expression ok, involved shifted GV references */ #define CHARMAXARGS 256 #define MAX_FORARGS 127 #define MAX_SRCLINE 8192 /* maximum length of a program source or indirection line */ #define NO_FORMALLIST (-1) /* Some errors should not cause stx_error to issue an rts_error. These are the errors related to * a) Invalid Intrinsic Commands * b) Invalid Intrinsic Function Names * c) Invalid Intrinsic Special Variables * d) Invalid Deviceparameters for IO commands * These should cause an error at runtime if and only if that codepath is reached. * PostConditionals can cause this path to be avoided in which case we do not want to issue an error at compile time. * Therefore issue only a warning at compile-time and proceed with compilation as if this codepath will not be reached at runtime. */ error_def(ERR_BOOLSIDEFFECT); error_def(ERR_DEVPARINAP); error_def(ERR_DEVPARUNK); error_def(ERR_DEVPARVALREQ); error_def(ERR_FNOTONSYS); error_def(ERR_INVCMD); error_def(ERR_INVFCN); error_def(ERR_INVSVN); error_def(ERR_SVNONEW); error_def(ERR_SVNOSET); #define IS_STX_WARN(errcode) \ ((ERR_DEVPARINAP == errcode) || (ERR_DEVPARUNK == errcode) || (ERR_DEVPARVALREQ == errcode) \ || (ERR_FNOTONSYS == errcode) || (ERR_INVCMD == errcode) || (ERR_INVFCN == errcode) \ || (ERR_INVSVN == errcode) || (ERR_SVNONEW == errcode) || (ERR_SVNOSET == errcode)) /* This macro does an "stx_error" of the input errcode but before that it asserts that the input errcode is one * of the known error codes that are to be handled as a compile-time warning (instead of an error). It also set * the variable "parse_warn" to TRUE which is relied upon by the functions that invoke this macro. Note that when * triggers are included, warnings become errors so bypass the warning stuff. */ #ifdef GTM_TRIGGER # define STX_ERROR_WARN(errcode) \ { \ if (!TREF(trigger_compile_and_link)) \ parse_warn = TRUE; \ assert(IS_STX_WARN(errcode)); \ stx_error(errcode); \ if (TREF(trigger_compile_and_link)) \ return FALSE; \ } #else # define STX_ERROR_WARN(errcode) \ { \ parse_warn = TRUE; \ assert(IS_STX_WARN(errcode)); \ stx_error(errcode); \ } #endif #ifdef DEBUG # define COMPDBG(x) if (gtmDebugLevel & GDL_DebugCompiler) {x} #else # define COMPDBG(x) #endif /* Cutover from simple lists to hash table access - tuned by testing compilation of 24K+ Vista M source routines. */ #include "copy.h" #define LIT_HASH_CUTOVER DEBUG_ONLY(4) PRO_ONLY(32) #define SYM_HASH_CUTOVER DEBUG_ONLY(4) PRO_ONLY(16) #define COMPLITS_HASHTAB_CLEANUP \ { \ GBLREF hash_table_str *complits_hashtab; \ if (complits_hashtab && complits_hashtab->base) \ { /* Release hash table itself but leave hash table descriptor if exists */ \ free_hashtab_str(complits_hashtab); \ } \ } #define COMPSYMS_HASHTAB_CLEANUP \ { \ GBLREF hash_table_str *compsyms_hashtab; \ if (compsyms_hashtab && compsyms_hashtab->base) \ { /* Release hash table itself but leave hash table descriptor if exists */ \ free_hashtab_str(compsyms_hashtab); \ compsyms_hashtab->base = NULL; \ } \ } #define COMPILE_HASHTAB_CLEANUP \ COMPLITS_HASHTAB_CLEANUP; \ COMPSYMS_HASHTAB_CLEANUP; typedef struct { triple *expr_start; triple *expr_start_orig; boolean_t shift_side_effects; boolean_t saw_side_effect; triple tmpchain; } save_se; #define START_GVBIND_CHAIN(SS, OLDCHAIN) \ { \ (SS)->expr_start = TREF(expr_start); \ (SS)->expr_start_orig = TREF(expr_start_orig); \ (SS)->shift_side_effects = TREF(shift_side_effects); \ (SS)->saw_side_effect = TREF(saw_side_effect); \ TREF(expr_start) = NULL; \ TREF(expr_start_orig) = NULL; \ TREF(shift_side_effects) = FALSE; \ TREF(saw_side_effect) = FALSE; \ dqinit(&(SS)->tmpchain, exorder); \ OLDCHAIN = setcurtchain(&(SS)->tmpchain); \ } #define PLACE_GVBIND_CHAIN(SS, OLDCHAIN) \ { \ newtriple(OC_GVSAVTARG); \ TREF(expr_start) = (SS)->expr_start; \ TREF(expr_start_orig) = (SS)->expr_start_orig; \ TREF(shift_side_effects) = (SS)->shift_side_effects; \ TREF(saw_side_effect) = (SS)->saw_side_effect; \ setcurtchain(OLDCHAIN); \ assert(NULL != TREF(expr_start)); \ dqadd(TREF(expr_start), &(SS)->tmpchain, exorder); \ TREF(expr_start) = (SS)->tmpchain.exorder.bl; \ assert(OC_GVSAVTARG == (TREF(expr_start))->opcode); \ newtriple(OC_GVRECTARG)->operand[0] = put_tref(TREF(expr_start)); \ } /* note assignment below */ #define SHIFT_SIDE_EFFECTS ((TREF(saw_side_effect) = TREF(shift_side_effects)) && (GTM_BOOL == TREF(gtm_fullbool))) #define INITIAL_SIDE_EFFECT_DEPTH 33 /* initial allocation for expression nesting to track side effects */ /* note side effect for boolean shifting temporaries */ #define ENCOUNTERED_SIDE_EFFECT \ { /* Needs #include "show_source_line" and #include "fullbool.h" */ \ \ if (TREF(shift_side_effects)) \ { \ TREF(saw_side_effect) = TRUE; \ if (!run_time && (FULL_BOOL_WARN == TREF(gtm_fullbool))) \ { /* warnings requested by by gtm_fullbool and enabled by eval_expr */ \ show_source_line(TRUE); \ dec_err(VARLSTCNT(1) ERR_BOOLSIDEFFECT); \ } \ } \ } #define SE_WARN_ON (!run_time && (SE_WARN == TREF(side_effect_handling))) #define ISSUE_SIDEEFFECTEVAL_WARNING(COLUMN) \ { \ TREF(last_source_column) = (COLUMN); \ show_source_line(TRUE); \ dec_err(VARLSTCNT(1) ERR_SIDEEFFECTEVAL); \ } /* maintain array indexed by expr_depth to track side effects - for subscripts, actuallists, binary expressions and functions */ #define INCREMENT_EXPR_DEPTH \ { \ boolean_t *TMP_BASE; \ \ if (!(TREF(expr_depth))++) \ TREF(expr_start) = TREF(expr_start_orig) = NULL; \ else \ { /* expansion is unlikely as it's hard to nest expressions deeply, but we don't want a hard limit */ \ assertpro(TREF(expr_depth)); /* expr_depth doesn't handle rollover */ \ assert(TREF(expr_depth) <= TREF(side_effect_depth)); \ if (TREF(expr_depth) == TREF(side_effect_depth)) \ { \ TMP_BASE = TREF(side_effect_base); \ (TREF(side_effect_depth))++; \ TREF(side_effect_base) = malloc(SIZEOF(boolean_t) * TREF(side_effect_depth)); \ memcpy(TREF(side_effect_base), TMP_BASE, SIZEOF(boolean_t) * TREF(expr_depth)); \ free(TMP_BASE); \ (TREF(side_effect_base))[TREF(expr_depth)] = FALSE; \ } \ } \ assert(FALSE == (TREF(side_effect_base))[TREF(expr_depth)]); \ } /* complement of the above increment - uses the macro just below for assertpto and to clear the level we're leaving */ #define DECREMENT_EXPR_DEPTH \ { \ DISABLE_SIDE_EFFECT_AT_DEPTH; \ if (!(--(TREF(expr_depth)))) \ TREF(saw_side_effect) = TREF(shift_side_effects) = FALSE; \ } /* clear the current expr_depth level and propagate down */ #define DISABLE_SIDE_EFFECT_AT_DEPTH \ { \ unsigned int DEPTH; \ \ DEPTH = TREF(expr_depth); \ assertpro(DEPTH); /* expr_depth shouldn't underflow */ \ (TREF(side_effect_base))[DEPTH - 1] |= (TREF(side_effect_base))[DEPTH]; /* propagate down */ \ (TREF(side_effect_base))[DEPTH] = FALSE; \ } /* The following macro transfers subscripts from an array to the triple chain for gvn, lvn and name_glvn * it requires includes for fullbool.m, mdq.h, and show_source_line.h, and also GBLREF of runtime */ #define SUBS_ARRAY_2_TRIPLES(REF1, SB1, SB2, SUBSCRIPTS, XTRA) \ { \ boolean_t PROTECT_LVN, SE_NOTIFY; \ triple *REF2; \ \ if (PROTECT_LVN = (TREF(side_effect_base))[TREF(expr_depth)]) /* NOTE assignment */ \ SE_NOTIFY = SE_WARN_ON; \ while (SB2 < SB1) \ { \ if (PROTECT_LVN && (SB2 > (SUBSCRIPTS + XTRA)) && ((SB1 - SB2) > 1) \ && ((OC_VAR == SB2->oprval.tref->opcode) || (OC_GETINDX == SB2->oprval.tref->opcode))) \ { /* protect lvns from side effects: skip 1st (unsubscripted name), and last (nothing following) */ \ assert(OLD_SE != TREF(side_effect_handling)); \ REF2 = maketriple(OC_STOTEMP); \ REF2->operand[0] = *SB2; \ dqins(SB2->oprval.tref, exorder, REF2); /* NOTE:this violates information hiding */ \ if (SE_NOTIFY) \ ISSUE_SIDEEFFECTEVAL_WARNING(SB2->oprval.tref->src.column + 1); \ *SB2 = put_tref(REF2); \ } \ REF2 = newtriple(OC_PARAMETER); \ REF1->operand[1] = put_tref(REF2); \ REF1 = REF2; \ REF1->operand[0] = *SB2++; \ } \ } /* the macro below tucks a code reference into the for_stack so a FOR that's done can move on correctly when done */ #define FOR_END_OF_SCOPE(DEPTH, RESULT) \ { \ oprtype **Ptr; \ \ assert(0 <= DEPTH); \ assert(TREF(for_stack_ptr) < (oprtype **)TADR(for_stack) + MAX_FOR_STACK); \ Ptr = (oprtype **)TREF(for_stack_ptr) - DEPTH; \ assert(Ptr >= (oprtype **)TADR(for_stack)); \ if (NULL == *Ptr) \ *Ptr = (oprtype *)mcalloc(SIZEOF(oprtype)); \ RESULT = put_indr(*Ptr); \ } #define GOOD_FOR FALSE /* single level */ #define BLOWN_FOR (!TREF(xecute_literal_parse)) /* all levels, except only one for our funky friend xecute_literal_parse */ /* Marco to decrement or clear the for_stack, clear the for_temp array * and generate code to release run-time malloc'd mvals anchored in the for_saved_indx array * The corresponding FOR_PUSH macro is in m_for.c but this one is used in stx_error.c for error cases */ #define FOR_POP(ALL) \ { \ assert(TREF(for_stack_ptr) >= (oprtype **)TADR(for_stack)); \ assert(TREF(for_stack_ptr) <= (oprtype **)TADR(for_stack) + MAX_FOR_STACK); \ if (ALL) \ { \ (TREF(for_stack_ptr)) = (oprtype **)TADR(for_stack); \ *(TREF(for_stack_ptr)) = NULL; \ } else if (TREF(for_stack_ptr) > (oprtype **)TADR(for_stack)) \ --(TREF(for_stack_ptr)); \ } /* $TEXT(+n^rtn) fetches from the most recently ZLINK'd version of rtn. $TEXT(+n) fetches from the currently executing version. * The compiler converts $TEXT(+n) to $TEXT(+n^!), indicating at runtime $TEXT should fetch from the current executing routine. * '!' is not a legal routine name character. */ #define CURRENT_RTN_STRING "!" #define PUT_CURRENT_RTN put_str(LIT_AND_LEN(CURRENT_RTN_STRING)) /* Do we want the current routine? Return TRUE. * Otherwise, return FALSE; we want the latest version of whichever routine name is specified. */ #define WANT_CURRENT_RTN_MSTR(S) ((STR_LIT_LEN(CURRENT_RTN_STRING) == (S)->len) \ && (0 == MEMCMP_LIT((S)->addr, CURRENT_RTN_STRING))) #define WANT_CURRENT_RTN(R) WANT_CURRENT_RTN_MSTR(&(R)->str) #define NEWLINE_TO_NULL(C) \ { \ if ('\n' == (char)(C)) \ C = '\0'; \ } /* Macro to clear parts of given compiler mval which could end up in an object file (if they are literals). These bits may not * be (re)set by the s2n/n2s calls we do. If not, the mval could have random bits in it which, as far as the mval is concerned * is not a problem but interferes with getting a consistent object hash value when the same source is (re)compiled. */ #define CLEAR_MVAL_BITS(mvalptr) \ { \ ((mval_b *)(mvalptr))->sgne = 0; \ (mvalptr)->fnpc_indx = 0xff; \ UTF8_ONLY((mvalptr)->utfcgr_indx = 0xff); \ } /* Macro to put a literal truth value as an operand */ #define PUT_LITERAL_TRUTH(TV, TRIP_REF) \ MBSTART { \ LITREF mval literal_zero, literal_one; \ \ mval *V; \ \ V = (mval *)mcalloc(SIZEOF(mval)); \ *V = (TV) ? literal_one : literal_zero; \ assert((1 == literal_one.str.len) && (1 == literal_zero.str.len)); \ ENSURE_STP_FREE_SPACE(1); \ *(char *)stringpool.free = *V->str.addr; \ V->str.addr = (char *)stringpool.free; \ stringpool.free += 1; \ put_lit_s(V, TRIP_REF); \ } MBEND /* Macro to decide whether an invocation of unary tail has any promise */ #define UNARY_TAIL(OPR) \ MBSTART { \ assert(TRIP_REF == (OPR)->oprclass); \ if (OCT_UNARY & oc_tab[(OPR)->oprval.tref->opcode].octype) \ unary_tail(OPR); \ } MBEND /* the following structure and macros save and restore parsing state and as of this writing are only used by m_xecute */ typedef struct { boolean_t source_error_found; int source_column; int director_ident_len; int4 block_level; mstr_len_t source_len; mval director_mval; unsigned char ident_buffer[(MAX_MIDENT_LEN * 2) + 1]; char director_token; char *lexical_ptr; char window_token; triple pos_in_chain; } parse_save_block; #define SAVE_PARSE_STATE(SAVE_PARSE_PTR) \ MBSTART { \ SAVE_PARSE_PTR->block_level = TREF(block_level); \ SAVE_PARSE_PTR->director_ident_len = (TREF(director_ident)).len; \ memcpy(SAVE_PARSE_PTR->ident_buffer, (TREF(director_ident)).addr, SAVE_PARSE_PTR->director_ident_len); \ SAVE_PARSE_PTR->director_mval = TREF(director_mval); \ SAVE_PARSE_PTR->director_token = TREF(director_token); \ SAVE_PARSE_PTR->lexical_ptr = TREF(lexical_ptr); \ SAVE_PARSE_PTR->source_column = source_column; \ SAVE_PARSE_PTR->source_error_found = TREF(source_error_found); \ SAVE_PARSE_PTR->source_len = (TREF(source_buffer)).len; \ SAVE_PARSE_PTR->window_token = TREF(window_token); \ SAVE_PARSE_PTR->pos_in_chain = TREF(pos_in_chain); \ } MBEND #define RESTORE_PARSE_STATE(SAVE_PARSE_PTR) \ MBSTART { \ TREF(block_level) = SAVE_PARSE_PTR->block_level; \ (TREF(director_ident)).len = SAVE_PARSE_PTR->director_ident_len; \ memcpy((TREF(director_ident)).addr, SAVE_PARSE_PTR->ident_buffer, SAVE_PARSE_PTR->director_ident_len); \ TREF(director_mval) = SAVE_PARSE_PTR->director_mval; \ TREF(director_token) = SAVE_PARSE_PTR->director_token; \ TREF(lexical_ptr) = SAVE_PARSE_PTR->lexical_ptr; \ (TREF(source_buffer)).addr = (char *)&aligned_source_buffer; \ (TREF(source_buffer)).len = SAVE_PARSE_PTR->source_len; \ source_column = SAVE_PARSE_PTR->source_column; \ TREF(source_error_found) = SAVE_PARSE_PTR->source_error_found; \ TREF(window_token) = SAVE_PARSE_PTR->window_token; \ TREF(pos_in_chain) = SAVE_PARSE_PTR->pos_in_chain; \ } MBEND #define RETURN_IF_RTS_ERROR \ MBSTART { \ if (TREF(rts_error_in_parse)) \ return; \ } MBEND #define RETURN_EXPR_IF_RTS_ERROR \ MBSTART { \ if (TREF(rts_error_in_parse)) \ { \ TREF(rts_error_in_parse) = FALSE; \ DECREMENT_EXPR_DEPTH; \ return EXPR_FAIL; \ } \ } MBEND #define ALREADY_RTERROR (OC_RTERROR == (TREF(curtchain))->exorder.bl->exorder.bl->exorder.bl->opcode) /* Autorelink enabled platforms pass a different argument to glue code when calling a non-local M * routine. Use put_cdlt() to pass addresses of the items and use put_cdidx() when passing an ofset * into the linkage table where the items reside. Also macroize the opcode to use. */ #ifdef AUTORELINK_SUPPORTED # define PUT_CDREF put_cdidx # define OC_CDREF OC_CDIDX # define CDREF_REF CDIDX_REF #else # define PUT_CDREF put_cdlt # define OC_CDREF OC_CDLIT # define CDREF_REF CDLT_REF #endif int actuallist(oprtype *opr); int bool_expr(boolean_t sense, oprtype *addr); void bx_boollit(triple *t); void bx_boolop(triple *t, boolean_t jmp_type_one, boolean_t jmp_to_next, boolean_t sense, oprtype *addr); void bx_relop(triple *t, opctype cmp, opctype tst, oprtype *addr); void bx_tail(triple *t, boolean_t sense, oprtype *addr); void chktchain(triple *head); void code_gen(void); void coerce(oprtype *a, unsigned short new_type); int comp_fini(int status, mstr *obj, opctype retcode, oprtype *retopr, oprtype *dst, mstr_len_t src_len); void comp_init(mstr *src, oprtype *dst); void comp_indr(mstr *obj); boolean_t compiler_startup(void); void create_temporaries(triple *sub, opctype put_oc); triple *entryref(opctype op1, opctype op2, mint commargcode, boolean_t can_commarg, boolean_t labref, boolean_t textname); int eval_expr(oprtype *a); int expratom(oprtype *a); int exfunc(oprtype *a, boolean_t alias_target); int expritem(oprtype *a); int expr(oprtype *a, int m_type); void ex_tail(oprtype *opr); int extern_func(oprtype *a); int f_ascii(oprtype *a, opctype op); int f_char(oprtype *a, opctype op); int f_data(oprtype *a, opctype op); int f_extract(oprtype *a, opctype op); int f_find(oprtype *a, opctype op); int f_fnumber(oprtype *a, opctype op); int f_fnzbitfind(oprtype *a, opctype op); int f_fnzbitget(oprtype *a, opctype op); int f_fnzbitset(oprtype *a, opctype op); int f_fnzbitstr(oprtype *a, opctype op); int f_get(oprtype *a, opctype op); int f_get1(oprtype *a, opctype op); int f_incr(oprtype *a, opctype op); int f_justify(oprtype *a, opctype op); int f_length(oprtype *a, opctype op); int f_mint(oprtype *a, opctype op); int f_mint_mstr(oprtype *a, opctype op); int f_mstr(oprtype *a, opctype op); int f_name(oprtype *a, opctype op); int f_next(oprtype *a, opctype op); int f_one_mval(oprtype *a, opctype op); int f_order(oprtype *a, opctype op); int f_order1(oprtype *a, opctype op); int f_piece(oprtype *a, opctype op); int f_qlength(oprtype *a, opctype op); int f_qsubscript(oprtype *a, opctype op); int f_query (oprtype *a, opctype op); int f_reverse(oprtype *a, opctype op); int f_select(oprtype *a, opctype op); int f_stack(oprtype *a, opctype op); int f_text(oprtype *a, opctype op); int f_translate(oprtype *a, opctype op); int f_two_mstrs(oprtype *a, opctype op); int f_two_mval(oprtype *a, opctype op); int f_view(oprtype *a, opctype op); int f_zahandle(oprtype *a, opctype op); int f_zatransform(oprtype *a, opctype op); int f_zcall(oprtype *a, opctype op); int f_zchar(oprtype *a, opctype op); int f_zcollate(oprtype *a, opctype op); int f_zconvert(oprtype *a, opctype op); int f_zdate(oprtype *a, opctype op); int f_zdebug(oprtype *a, opctype op); int f_zechar(oprtype *a, opctype op); int f_zgetsyi(oprtype *a, opctype op); int f_zjobexam(oprtype *a, opctype op); int f_zparse(oprtype *a, opctype op); int f_zpeek(oprtype *a, opctype op); int f_zprevious(oprtype *a, opctype op); int f_zqgblmod(oprtype *a, opctype op); int f_zrupdate(oprtype *a, opctype op); int f_zsearch(oprtype *a, opctype op); int f_zsigproc(oprtype *a, opctype op); int f_zsocket(oprtype *a, opctype op); int f_zsqlexpr (oprtype *a, opctype op); int f_zsqlfield (oprtype *a, opctype op); int f_zsubstr(oprtype *a, opctype op); int f_ztrigger(oprtype *a, opctype op); int f_ztrnlnm(oprtype *a, opctype op); int f_zwidth(oprtype *a, opctype op); int f_zwrite(oprtype *a, opctype op); mlabel *get_mladdr(mident *c); mvar *get_mvaddr(mident *c); int glvn(oprtype *a); int gvn(void); void ind_code(mstr *obj); int indirection(oprtype *a); void ins_triple(triple *x); void int_label(void); int jobparameters (oprtype *c); boolean_t line(uint4 *lnc); int linetail(void); int lkglvn(boolean_t gblvn); int lref(oprtype *label, oprtype *offset, boolean_t no_lab_ok, mint commarg_code, boolean_t commarg_ok, boolean_t *got_some); int lvn(oprtype *a,opctype index_op,triple *parent); void make_commarg(oprtype *x, mint ind); oprtype make_gvsubsc(mval *v); triple *maketriple(opctype op); int name_glvn(boolean_t gblvn, oprtype *a); triple *newtriple(opctype op); int nref(void); void obj_code(uint4 src_lines, void *checksum_ctx); int one_job_param(char **parptr); int parse_until_rparen_or_space(void); oprtype put_ocnt(void); oprtype put_tsiz(void); oprtype put_cdlt(mstr *x); oprtype put_cdidx(mstr *x); oprtype put_ilit(mint x); oprtype put_indr(oprtype *x); oprtype put_lit(mval *x); oprtype put_lit_s(mval *x, triple *dst); oprtype put_mfun(mident *l); oprtype put_mlab(mident *l); oprtype put_mnxl(void); oprtype put_mvar(mident *x); oprtype put_str(char *pt, mstr_len_t n); oprtype put_tjmp(triple *x); oprtype put_tnxt(triple *x); oprtype put_tref(triple *x); boolean_t resolve_optimize(triple *curtrip); int resolve_ref(int errknt); void resolve_tref(triple *, oprtype *); triple *setcurtchain(triple *x); void unary_tail(oprtype *opr); /* VMS uses same code generator as USHBIN so treat as USHBIN for these compiler routines */ # if defined(USHBIN_SUPPORTED) || defined(VMS) void shrink_trips(void); boolean_t litref_triple_oprcheck(oprtype *operand); # else void shrink_jmps(void); # endif void start_for_fetches(void); void tnxtarg(oprtype *a); void tripinit(void); boolean_t unuse_literal(mval *x); void walktree(mvar *n,void (*f)(),char *arg); void wrtcatopt(triple *r, triple ***lpx, triple **lptop); int zlcompile(unsigned char len, unsigned char *addr); /***type int added***/ #endif /* COMPILER_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/compiler_startup.c0000644000032200000250000002406614342376333017615 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "compiler.h" #include "opcode.h" #include "cmd_qlf.h" #include "mdq.h" #include "cgp.h" #include "error.h" #include "mmemory.h" #include "stp_parms.h" #include "stringpool.h" #include "list_file.h" #include "source_file.h" #include "lb_init.h" #include "reinit_compilation_externs.h" #include "comp_esc.h" #include "resolve_blocks.h" #include "hashtab_str.h" #include #include "rtn_src_chksum.h" #include "gtmmsg.h" #include "iosp.h" /* for SS_NORMAL */ #include "start_fetches.h" #define HOPELESS_COMPILE 128 GBLREF int source_column; GBLREF boolean_t mstr_native_align, save_mstr_native_align; GBLREF char cg_phase; /* code generation phase */ GBLREF command_qualifier cmd_qlf; GBLREF hash_table_str *complits_hashtab; GBLREF int mlmax; GBLREF mcalloc_hdr *mcavailptr, *mcavailbase; GBLREF mline mline_root; GBLREF spdesc indr_stringpool, rts_stringpool, stringpool; GBLREF src_line_struct src_head; GBLREF triple t_orig; GBLREF unsigned char source_file_name[]; GBLREF unsigned short source_name_len; LITDEF char compile_terminated[] = "COMPILATION TERMINATED DUE TO EXCESS ERRORS"; error_def(ERR_ERRORSUMMARY); error_def(ERR_ZLINKFILE); error_def(ERR_ZLNOOBJECT); boolean_t compiler_startup(void) { #ifdef DEBUG void dumpall(); #endif boolean_t compile_w_err, need_source_lines, use_src_queue, creating_list_file; unsigned char err_buf[45]; unsigned char *cp, *cp2; int errknt; int4 n; uint4 line_count, total_source_len; mlabel *null_lab; src_line_struct *sl; mident null_mident; gtm_rtn_src_chksum_ctx checksum_ctx; mstr str; size_t mcallocated, alloc; size_t stlen; mcalloc_hdr *lastmca, *nextmca; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Although we have an invocation of compiler cleanups at the end of this module, there exist ways to avoid this * cleanup by working in direct mode, getting certain types of errors combined with an argumentless ZGOTO that unwinds * pretty much everything that can bypass that cleanup. So do a quick check if it is needed and if so, git-r-done * (test is part of the macro invocation). Note this is the easiest place to make this check rather than complicating * error handling to provide a similar effect. */ /* The stringpool switching pattern is to copy the stringpool structure to the holding structure for the currently active * pool and then copy the other pool structure into the stringpool structure. The initialization pattern is to stp_init * the stringpool structure and then copy it to the holding structure for the pool we're initializing. The indr_stringpool * might more appropriately be called cmplr_stringpool, because we use it for all compiles not just those for indirection */ if (rts_stringpool.base == stringpool.base) { rts_stringpool = stringpool; if (!indr_stringpool.base) { stp_init(STP_INITSIZE); indr_stringpool = stringpool; } else stringpool = indr_stringpool; } run_time = FALSE; TREF(compile_time) = TRUE; TREF(transform) = FALSE; TREF(dollar_zcstatus) = SS_NORMAL; reinit_compilation_externs(); memset(&null_mident, 0, SIZEOF(null_mident)); ESTABLISH_RET(compiler_ch, FALSE); /* Since the stringpool alignment is solely based on mstr_native_align, we need to initialize it based * on the ALIGN_STRINGS qualifier so that all strings in the literal text pool are aligned. * However, when a module is compiled at runtime, we need to preserve the existing runtime setting * (that was initialized at GT.M startup) once the compilation is done. save_mstr_native_align is used for * this purpose. */ /* If last compile errored out, it may have left stuff - find out how much space we have in mcalloc blocks */ for (mcallocated = 0, nextmca = mcavailptr; nextmca; nextmca = nextmca->link) mcallocated += nextmca->size; if (0 == mcallocated) mcallocated = MC_DSBLKSIZE - MCALLOC_HDR_SZ; /* Min size is one default block size */ COMPILE_HASHTAB_CLEANUP; save_mstr_native_align = mstr_native_align; /* mstr_native_align = (cmd_qlf.qlf & CQ_ALIGN_STRINGS) ? TRUE : FALSE; */ mstr_native_align = FALSE; /* TODO: remove this line and uncomment the above line */ cg_phase = CGP_NOSTATE; TREF(source_error_found) = errknt = 0; open_source_file(); rtn_src_chksum_init(&checksum_ctx); cg_phase = CGP_PARSE; creating_list_file = (cmd_qlf.qlf & CQ_LIST) || (cmd_qlf.qlf & CQ_CROSS_REFERENCE); need_source_lines = (cmd_qlf.qlf & CQ_EMBED_SOURCE) || creating_list_file; use_src_queue = (cmd_qlf.qlf & CQ_EMBED_SOURCE) || (creating_list_file && (cmd_qlf.qlf & CQ_MACHINE_CODE)); dqinit(&src_head, que); if (creating_list_file) open_list_file(); if (cmd_qlf.qlf & CQ_CE_PREPROCESS) open_ceprep_file(); tripinit(); null_lab = get_mladdr(&null_mident); null_lab->ml = &mline_root; mlmax++; (TREF(fetch_control)).curr_fetch_trip = (TREF(fetch_control)).curr_fetch_opr = newtriple(OC_LINEFETCH); (TREF(fetch_control)).curr_fetch_count = 0; TREF(code_generated) = FALSE; line_count = 1; total_source_len = 0; for (TREF(source_line) = 1; errknt <= HOPELESS_COMPILE; (TREF(source_line))++) { if (-1 == (n = read_source_file())) break; rtn_src_chksum_line(&checksum_ctx, (TREF(source_buffer)).addr, n); /* Save the source lines; a check later determines whether to include them in the object file */ /* Accumulate list of M source lines */ sl = (src_line_struct *)mcalloc(SIZEOF(src_line_struct)); dqrins(&src_head, que, sl); stlen = n + 1; sl->str.addr = mcalloc(stlen); /* == n+1 for zero termination */ sl->str.len = n; sl->line = TREF(source_line); assert(NULL != sl->str.addr); memcpy(sl->str.addr, (TREF(source_buffer)).addr, stlen); total_source_len += n; cp = (unsigned char *)((TREF(source_buffer)).addr + n - 1); NEWLINE_TO_NULL(*cp); /* avoid SPOREOL errors due to trailing newlines */ if (need_source_lines && creating_list_file && !(cmd_qlf.qlf & CQ_MACHINE_CODE)) { /* list now. for machine_code we intersperse machine code and M code, thus can't list M code yet */ list_line_number(); list_line((TREF(source_buffer)).addr); } TREF(source_error_found) = 0; lb_init(); if (cmd_qlf.qlf & CQ_CE_PREPROCESS) put_ceprep_line(); if (!line(&line_count)) { assert(TREF(source_error_found)); errknt++; } } rtn_src_chksum_digest(&checksum_ctx); close_source_file(); if (cmd_qlf.qlf & CQ_CE_PREPROCESS) close_ceprep_file(); cg_phase = CGP_RESOLVE; if (t_orig.exorder.fl == &t_orig) /* if no lines in routine, set up line 0 */ newtriple(OC_LINESTART); newtriple(OC_RET); /* always provide a default QUIT */ mline_root.externalentry = t_orig.exorder.fl; assert(indr_stringpool.base == stringpool.base); INVOKE_STP_GCOL(0); /* The above invocation of stp_gcol with a parameter of 0 is a critical part of compilation * (both routine compilations and indirect dynamic compilations). This collapses the indirect * (compilation) stringpool so that only the literals are left. This stringpool is then written * out to the compiled object as the literal pool for that compilation. Temporary stringpool * use for conversions or whatever are eliminated. Note the path is different in stp_gcol for * the indirect stringpool which is only used during compilations. */ if (cmd_qlf.qlf & CQ_EMBED_SOURCE) { /* Append source text to text pool */ ENSURE_STP_FREE_SPACE(total_source_len); DBG_MARK_STRINGPOOL_UNEXPANDABLE; TREF(routine_source_offset) = (uint4)(stringpool.free - stringpool.base); dqloop(&src_head, que, sl) { str = sl->str; s2pool(&str); /* changes str.addr, points it into stringpool */ } DBG_MARK_STRINGPOOL_EXPANDABLE; } start_fetches(OC_NOOP); resolve_blocks(); errknt = resolve_ref(errknt); compile_w_err = (errknt <= HOPELESS_COMPILE && (cmd_qlf.qlf & CQ_IGNORE)); if (cmd_qlf.qlf & CQ_LIST || cmd_qlf.qlf & CQ_CROSS_REFERENCE) { list_line(""); if (errknt) cp = i2asc(err_buf, errknt); else { cp = err_buf; *cp++ = 'n'; *cp++ = 'o'; } memcpy(cp, " error", SIZEOF(" error")); cp += SIZEOF(" error") - 1; if (1 != errknt) *cp++ = 's'; *cp = 0; list_line((char *)err_buf); if (errknt > HOPELESS_COMPILE) list_line((char *)compile_terminated); if (cmd_qlf.qlf & CQ_MACHINE_CODE && compile_w_err) list_head(1); } if ((!errknt || compile_w_err) && ((cmd_qlf.qlf & CQ_OBJECT) || (cmd_qlf.qlf &CQ_MACHINE_CODE))) { obj_code(line_count, &checksum_ctx); cg_phase = CGP_FINI; } else if (!compile_w_err) { TREF(dollar_zcstatus) = -ERR_ERRORSUMMARY; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_ZLINKFILE, 2, source_name_len, source_file_name, ERR_ZLNOOBJECT); } if (cmd_qlf.qlf & CQ_LIST || cmd_qlf.qlf & CQ_CROSS_REFERENCE) close_list_file(); COMPILE_HASHTAB_CLEANUP; reinit_compilation_externs(); /* Determine if need to remove any added mc blocks. Min value of mcallocated ensures we leave at least one block alone. */ for (alloc = 0, nextmca = mcavailptr; nextmca && (alloc < mcallocated); alloc += nextmca->size, lastmca = nextmca, nextmca = nextmca->link); if (nextmca) { /* Start freeing at the nextmca node since these are added blocks */ lastmca->link = NULL; /* Sever link to further blocks here */ /* Release any remaining blocks if any */ for (lastmca = nextmca; lastmca; lastmca = nextmca) { nextmca = lastmca->link; free(lastmca); } } assert ((FALSE == run_time) && (TRUE == TREF(compile_time))); run_time = TRUE; TREF(compile_time) = FALSE; TREF(transform) = TRUE; assert(indr_stringpool.base == stringpool.base); indr_stringpool = stringpool; stringpool = rts_stringpool; mstr_native_align = save_mstr_native_align; REVERT; return errknt ? TRUE : FALSE; } fis-gtm-V7.0-005/sr_port/compswap.h0000755000032200000250000000612214342376331016051 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ #ifndef COMPSWAP_H_INCLUDE #define COMPSWAP_H_INCLUDE /* COMPSWAP_LOCK/UNLOCK are the same for all platform except for __ia64, which needs slightly different versions to handle * memory consistency isues */ #ifdef UNIX boolean_t compswap_secshr(sm_global_latch_ptr_t lock, int compval, int newval1); # if (defined(_AIX) || (defined(__ia64) && defined(__linux__))) /* AIX or Linux Itanium */ boolean_t compswap_lock(sm_global_latch_ptr_t lock, int compval, int newval1); boolean_t compswap_unlock(sm_global_latch_ptr_t lock, int compval, int newval1); # define COMPSWAP_LOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) compswap_lock(LCK, CMPVAL1, NEWVAL1) # define COMPSWAP_UNLOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) compswap_unlock(LCK, CMPVAL1, NEWVAL1) # elif !defined(__ia64) boolean_t compswap(sm_global_latch_ptr_t lock, int compval, int newval1); # define COMPSWAP_LOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) compswap(LCK, CMPVAL1, NEWVAL1) # define COMPSWAP_UNLOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) compswap(LCK, CMPVAL1, NEWVAL1) # elif (defined(__HP_cc) || (defined(__hpux) && defined(__GNUC__))) /* Use compiler inline assembly macros for HP-UX/HP C or GCC on HPUX*/ /* This is assuming 32 bit lock storage, which right now seems to be PIDs * most of the time. PIDs are currently 32 bit values, but that could change * someday, so beware */ # include # define FENCE (_Asm_fence) (_UP_CALL_FENCE | _UP_SYS_FENCE | _DOWN_CALL_FENCE | _DOWN_SYS_FENCE) # define COMPSWAP_LOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) \ ( \ _Asm_mov_to_ar((_Asm_app_reg)_AREG_CCV, (uint64_t) CMPVAL1,FENCE), \ _Asm_cmpxchg((_Asm_sz)_SZ_W, (_Asm_sem)_SEM_ACQ,(uint32_t *)LCK, \ (uint64_t)NEWVAL1, (_Asm_ldhint)_LDHINT_NONE) == (uint64_t)CMPVAL1 ? 1 : 0 \ ) # define COMPSWAP_UNLOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) \ ( \ _Asm_mov_to_ar((_Asm_app_reg)_AREG_CCV,(uint64_t) CMPVAL1,FENCE), \ _Asm_cmpxchg((_Asm_sz)_SZ_W,(_Asm_sem)_SEM_REL,(uint32_t *)LCK, \ (uint64_t)NEWVAL1, (_Asm_ldhint)_LDHINT_NONE) == (uint64_t)CMPVAL1 ? 1 : 0 \ ) # else # error Unsupported Platform sr_port/compswap.h # endif /* __ia64 */ #else boolean_t compswap(sm_global_latch_ptr_t lock, int compval1, int compval2, int newval1, int newval2); boolean_t compswap_secshr(sm_global_latch_ptr_t lock, int compval1, int compval2, int newval1, int newval2); # define COMPSWAP_LOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) compswap(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) # define COMPSWAP_UNLOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) compswap(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) #endif #endif fis-gtm-V7.0-005/sr_port/convbaseutil.mpt0000644000032200000250000000566514342376331017277 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2018-2019 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ADDVAL(v,a1,a2,b) ;Computes val1+val2 for any arbitrary-length val1 and val2 and for base <= 16 ; v - transform array as set up by CONVERTBASE ; a1, a2 - addends; b - base of addends new c,i,l,l1,l2,s,t,t1,t2 ; c =carry; i - iterator; l - greater length of l1 & l2 - addend lengths; s - sum; t1 & t2 - temporaries set c=0,s="",t1=a1,t2=a2,l1=$length(t1),l2=$length(t2),l=$select(l2>l1:l2,1:l1) set:l1>2^0)&&1)*2^0 + ((m>>2^1)&&1)*2^1 + ((m>>2^2)&&1)*2^2 +...) set r=0 ; r - result set:$find("ABCDEF",m) m=v(m) for quit:'m set:m#2 r=$$ADDVAL(.v,r,x,b) set m=m\2,x=$$ADDVAL(.v,x,x,b) quit r CONVERTBASE(val,fb,tb) ; converts val from base fb to base tb quit:'($$VERIFYNUM(val,fb)) 0 new e,i,o,r,v ; e - exponent; i - iterator; o - offset r - result ; v - transform array used by subroutines for i=0:1:15 set:i<10 v(i)=i set:9 #define GET_LLONGP(X,Y) (*(caddr_t)(X) = *(caddr_t)(Y), \ *((caddr_t)(X)+1) = *((caddr_t)(Y)+1), \ *((caddr_t)(X)+2) = *((caddr_t)(Y)+2), \ *((caddr_t)(X)+3) = *((caddr_t)(Y)+3), \ *((caddr_t)(X)+4) = *((caddr_t)(Y)+4), \ *((caddr_t)(X)+5) = *((caddr_t)(Y)+5), \ *((caddr_t)(X)+6) = *((caddr_t)(Y)+6), \ *((caddr_t)(X)+7) = *((caddr_t)(Y)+7)) #define GET_LONGP(X,Y) (*(caddr_t)(X) = *(caddr_t)(Y), \ *((caddr_t)(X)+1) = *((caddr_t)(Y)+1), \ *((caddr_t)(X)+2) = *((caddr_t)(Y)+2), \ *((caddr_t)(X)+3) = *((caddr_t)(Y)+3)) #define GET_SHORTP(X,Y) (*(caddr_t)(X) = *(caddr_t)(Y), *((caddr_t)(X)+1) = *((caddr_t)(Y)+1)) /* Unsigned versions are same as the signed ones as we do char by char */ #define GET_LLONG(X,Y) (*(caddr_t)(&X) = *(caddr_t)(Y), \ *((caddr_t)(&X)+1) = *((caddr_t)(Y)+1), \ *((caddr_t)(&X)+2) = *((caddr_t)(Y)+2), \ *((caddr_t)(&X)+3) = *((caddr_t)(Y)+3), \ *((caddr_t)(&X)+4) = *((caddr_t)(Y)+4), \ *((caddr_t)(&X)+5) = *((caddr_t)(Y)+5), \ *((caddr_t)(&X)+6) = *((caddr_t)(Y)+6), \ *((caddr_t)(&X)+7) = *((caddr_t)(Y)+7)) #define GET_ULLONG(X,Y) GET_LLONG #define GET_LONG(X,Y) (*(caddr_t)(&X) = *(caddr_t)(Y), \ *((caddr_t)(&X)+1) = *((caddr_t)(Y)+1), \ *((caddr_t)(&X)+2) = *((caddr_t)(Y)+2), \ *((caddr_t)(&X)+3) = *((caddr_t)(Y)+3)) #define GET_ULONG GET_LONG #define GET_SHORT(X,Y) (*(caddr_t)(&X) = *(caddr_t)(Y), *((caddr_t)(&X)+1) = *((caddr_t)(Y)+1)) #define GET_USHORT GET_SHORT #define GET_CHAR(X,Y) (*(caddr_t)(&X) = *(caddr_t)(Y)) #define REF_CHAR(Y) (*(caddr_t)(Y)) #define PUT_ZERO(X) (memset((caddr_t)&(X), 0, SIZEOF(X))) #define PUT_LLONG(X,Y) (*(caddr_t)(X) = *(caddr_t)(&Y), \ *((caddr_t)(X)+1) = *((caddr_t)(&Y)+1), \ *((caddr_t)(X)+2) = *((caddr_t)(&Y)+2), \ *((caddr_t)(X)+3) = *((caddr_t)(&Y)+3), \ *((caddr_t)(X)+4) = *((caddr_t)(&Y)+4), \ *((caddr_t)(X)+5) = *((caddr_t)(&Y)+5), \ *((caddr_t)(X)+6) = *((caddr_t)(&Y)+6), \ *((caddr_t)(X)+7) = *((caddr_t)(&Y)+7)) #define PUT_ULLONG(X,Y) PUT_LLONG #define PUT_LONG(X,Y) (*(caddr_t)(X) = *(caddr_t)(&Y), \ *((caddr_t)(X)+1) = *((caddr_t)(&Y)+1), \ *((caddr_t)(X)+2) = *((caddr_t)(&Y)+2), \ *((caddr_t)(X)+3) = *((caddr_t)(&Y)+3)) #define PUT_ULONG PUT_LONG #define PUT_SHORT(X,Y) (*(caddr_t)(X) = *(caddr_t)(&Y), *((caddr_t)(X)+1) = *((caddr_t)(&Y)+1)) #define PUT_USHORT PUT_SHORT #define PUT_CHAR(X,Y) (*(caddr_t)(X) = *(caddr_t)(&Y)) #endif /*UNALIGNED_ACCESS_SUPPORTED*/ fis-gtm-V7.0-005/sr_port/copyright.txt0000644000032200000250000000054514342376331016620 0ustar librarygtc Copyright (c) XXXX-YYYY 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. fis-gtm-V7.0-005/sr_port/cre_jnl_file.c0000644000032200000250000004756114342376331016637 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2021 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_string.h" #include "gtm_stat.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "eintr_wrappers.h" #include "gtm_permissions.h" #if defined(__MVS__) #include "gtm_zos_io.h" #endif #include "gtm_file_stat.h" #include "gtm_rename.h" #include "error.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "gtmio.h" #include "util.h" #include "gtmmsg.h" #include "send_msg.h" #include "iosp.h" #include "repl_sp.h" #include "is_file_identical.h" #include "jnl_get_checksum.h" #include "gtmimagename.h" #include "get_fs_block_size.h" #include "wbox_test_init.h" #include "gt_timer.h" #include "anticipatory_freeze.h" /* Note : Now all system error messages are issued here. So callers do not need to issue them again */ #define STATUS_MSG(INFO, FROMLEN, FROM, TOLEN, TO) \ { \ if (SS_NORMAL != INFO->status2) \ { \ send_msg_csa(CSA_ARG(INFO->csa) VARLSTCNT(12) ERR_JNLCRESTATUS, 7, CALLFROM, \ FROMLEN, FROM, TOLEN, TO, INFO->status, \ PUT_SYS_ERRNO(INFO->status2)); \ if (!IS_GTM_IMAGE) \ gtm_putmsg_csa(CSA_ARG(INFO->csa) VARLSTCNT1(11) ERR_JNLCRESTATUS, 7, CALLFROM, \ FROMLEN, FROM, TOLEN, TO, INFO->status, \ PUT_SYS_ERRNO(INFO->status2)); \ } else if (SS_NORMAL != INFO->status) \ { \ send_msg_csa(CSA_ARG(INFO->csa) VARLSTCNT(10) ERR_JNLCRESTATUS, 7, CALLFROM, \ FROMLEN, FROM, TOLEN, TO, INFO->status); \ if (!IS_GTM_IMAGE) \ gtm_putmsg_csa(CSA_ARG(INFO->csa) VARLSTCNT(10) ERR_JNLCRESTATUS, 7, CALLFROM, \ FROMLEN, FROM, TOLEN, TO, INFO->status); \ } \ } #define RETURN_ON_ERROR(info) \ if (SYSCALL_ERROR(info->status) || SYSCALL_ERROR(info->status2)) \ { \ int status; \ F_CLOSE(channel, status);/* resets "channel" to FD_INVALID */ \ if (NULL != jrecbuf_base) \ { \ free(jrecbuf_base); \ jrecbuf_base = NULL; \ } \ return EXIT_ERR; \ } GBLREF jnl_gbls_t jgbl; GBLREF boolean_t mupip_jnl_recover; GBLREF jnl_process_vector *prc_vec; ZOS_ONLY(error_def(ERR_BADTAG);) error_def(ERR_FILENAMETOOLONG); error_def(ERR_FILERENAME); error_def(ERR_JNLCRESTATUS); error_def(ERR_JNLFNF); error_def(ERR_PREMATEOF); error_def(ERR_RENAMEFAIL); error_def(ERR_TEXT); /* Create a journal file from info. * If necessary, it renames journal file of same name. * Note: jgbl.gbl_jrec_time must be set by callers */ uint4 cre_jnl_file(jnl_create_info *info) { mstr filestr; int org_fn_len, rename_fn_len, fstat; char *org_fn, rename_fn[MAX_FN_LEN + 1]; boolean_t no_rename; assert(0 != jgbl.gbl_jrec_time); if (!info->no_rename) /* ***MAYBE*** rename is required */ { no_rename = FALSE; rename_fn_len = ARRAYSIZE(rename_fn); if (SS_NORMAL != (info->status = prepare_unique_name((char *)info->jnl, info->jnl_len, "", "", rename_fn, &rename_fn_len, jgbl.gbl_jrec_time, &info->status2))) { no_rename = TRUE; } else { filestr.addr = (char *)info->jnl; filestr.len = info->jnl_len; if (FILE_PRESENT != (fstat = gtm_file_stat(&filestr, NULL, NULL, FALSE, (uint4 *)&info->status))) { if (FILE_NOT_FOUND != fstat) { STATUS_MSG(info, info->jnl_len, info->jnl, info->fn_len, info->fn); return EXIT_ERR; } send_msg_csa(CSA_ARG(info->csa) VARLSTCNT(4) ERR_JNLFNF, 2, filestr.len, filestr.addr); if (!(IS_GTM_IMAGE)) gtm_putmsg_csa(CSA_ARG(info->csa) VARLSTCNT(4) ERR_JNLFNF, 2, filestr.len, filestr.addr); no_rename = TRUE; } /* Note if info->no_prev_link == TRUE, we do not keep previous link, though rename can happen */ if (JNL_ENABLED(info) && !info->no_prev_link) { memcpy(info->prev_jnl, rename_fn, rename_fn_len + 1); info->prev_jnl_len = rename_fn_len; } else assert(info->no_prev_link); } if (no_rename) { STATUS_MSG(info, info->jnl_len, info->jnl, info->fn_len, info->fn); if (ERR_FILENAMETOOLONG == info->status) return EXIT_ERR; /* Else it is an error from "gtm_file_stat" (invoked from "prepare_unique_name" above). * It usually means the rename we originally wanted is no longer required. So continue. */ info->status = info->status2 = SS_NORMAL; info->no_rename = TRUE; /* We wanted to rename, but not required anymore */ info->no_prev_link = TRUE; /* No rename => no prev_link */ } } /* else we know for sure rename is not required */ return (cre_jnl_file_common(info, rename_fn, rename_fn_len)); } /* This creates info->jnl and if (!info->no_rename) then it renames existing info->jnl to be rename_fn */ uint4 cre_jnl_file_common(jnl_create_info *info, char *rename_fn, int rename_fn_len) { jnl_file_header *header; unsigned char hdr_base[JNL_HDR_LEN + MAX_IO_BLOCK_SIZE]; struct_jrec_pfin *pfin_record; struct_jrec_pini *pini_record; struct_jrec_epoch *epoch_record; struct_jrec_eof *eof_record; unsigned char *create_fn, fn_buff[MAX_FN_LEN + STR_LIT_LEN(EXT_NEW) + 1]; int create_fn_len, cre_jnl_rec_size, status, write_size, jrecbufbase_size; fd_type channel; char *jrecbuf, *jrecbuf_base; gd_id jnlfile_id; struct stat stat_buf; int fstat_res; ZOS_ONLY(int realfiletag;) int stat_res; int user_id; int group_id; struct stat sb; int perm; struct perm_diag_data pdd; int idx; trans_num db_tn; uint4 temp_offset, temp_checksum, pfin_offset, eof_offset; uint4 jnl_fs_block_size; sgmnt_addrs *csa; jrecbuf = NULL; csa = info->csa; if (info->no_rename) { /* The only cases where no-renaming is possible are as follows * (i) MUPIP SET JOURNAL where the new journal file name is different from the current journal file name * (ii) For MUPIP BACKUP, MUPIP SET JOURNAL, GT.M Runtime and forw_phase_recovery, * in case the current journal file does not exist (due to some abnormal condition). * But in this case we cut the link and hence info->no_prev_link should be TRUE. * The assert below tries to capture this as much as possible without introducing any new global variables. */ assert((IS_MUPIP_IMAGE && !jgbl.forw_phase_recovery) || (IS_GTM_IMAGE && info->no_prev_link)); create_fn_len = info->jnl_len; create_fn = info->jnl; assert(0 == create_fn[create_fn_len]); } else { if (NULL != csa) cre_jnl_file_intrpt_rename(csa); /* deal with *_new.mjl files */ create_fn = &fn_buff[0]; create_fn_len = ARRAYSIZE(fn_buff); if (SS_NORMAL != (info->status = prepare_unique_name((char *)info->jnl, (int)info->jnl_len, "", EXT_NEW, (char *)create_fn, &create_fn_len, 0, &info->status2))) { STATUS_MSG(info, info->jnl_len, info->jnl, info->fn_len, info->fn); return EXIT_ERR; } } OPENFILE3_CLOEXEC((char *)create_fn, O_CREAT | O_EXCL | O_RDWR, 0600, channel); if (-1 == channel) { info->status = errno; STATUS_MSG(info, info->jnl_len, info->jnl, info->fn_len, info->fn); return EXIT_ERR; } # ifdef __MVS__ if (-1 == gtm_zos_set_tag(channel, TAG_BINARY, TAG_NOTTEXT, TAG_FORCE, &realfiletag)) TAG_POLICY_SEND_MSG((char *)create_fn, errno, realfiletag, TAG_BINARY); # endif FSTAT_FILE(channel, &stat_buf, fstat_res); if (-1 == fstat_res) { info->status = errno; STATUS_MSG(info, info->jnl_len, info->jnl, info->fn_len, info->fn); F_CLOSE(channel, status); return EXIT_ERR; } /* check database file again. It was checked earlier so should still be ok. */ STAT_FILE((sm_c_ptr_t)info->fn, &sb, stat_res); if (-1 == stat_res) { info->status = errno; STATUS_MSG(info, info->jnl_len, info->jnl, info->fn_len, info->fn); F_CLOSE(channel, status); return EXIT_ERR; } /* Setup new group and permissions if indicated by the security rules */ if (!gtm_permissions(&sb, &user_id, &group_id, &perm, PERM_FILE, &pdd)) { send_msg_csa(CSA_ARG(info->csa) VARLSTCNT(6+PERMGENDIAG_ARG_COUNT) ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("journal file"), RTS_ERROR_STRING(info->fn), PERMGENDIAG_ARGS(pdd)); if (IS_GTM_IMAGE) RTS_ERROR_CSA_ABT(info->csa, VARLSTCNT(6+PERMGENDIAG_ARG_COUNT) ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("journal file"), RTS_ERROR_STRING(info->fn), PERMGENDIAG_ARGS(pdd)); else gtm_putmsg_csa(CSA_ARG(info->csa) VARLSTCNT(6+PERMGENDIAG_ARG_COUNT) ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("journal file"), RTS_ERROR_STRING(info->fn), PERMGENDIAG_ARGS(pdd)); F_CLOSE(channel, status); return EXIT_ERR; } /* if group not the same then change group of temporary file */ if ((((INVALID_UID != user_id) && (user_id != stat_buf.st_uid)) || ((INVALID_GID != group_id) && (group_id != stat_buf.st_gid))) && (-1 == fchown(channel, user_id, group_id))) { info->status = errno; STATUS_MSG(info, info->jnl_len, info->jnl, info->fn_len, info->fn); F_CLOSE(channel, status); return EXIT_ERR; } if (-1 == FCHMOD(channel, perm)) { info->status = errno; STATUS_MSG(info, info->jnl_len, info->jnl, info->fn_len, info->fn); F_CLOSE(channel, status); /* resets "channel" to FD_INVALID */ return EXIT_ERR; } jnl_fs_block_size = get_fs_block_size(channel); /* We need to write the journal file header, followed by pini/epoch/pfin/eof records followed by 0-padding * to ensure the final size of the journal file is filesystem-block-size aligned. * To have a "jnl_fs_block_size" aligned buffer that is also a multiple of jnl_fs_block_size long, * we need to allocate the needed size + 2 * jnl_fs_block_size. This will leave up to jnl_fs_block_size * before the actual "used" part for alignment plus enough left after the "used" part to make up a buffer * to write that is a multiple of jnl_fs_block_size. */ jrecbufbase_size = PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN + EOF_RECLEN + (2 * jnl_fs_block_size); jrecbuf_base = malloc(jrecbufbase_size); jrecbuf = (char *)ROUND_UP2((uintszofptr_t)jrecbuf_base, jnl_fs_block_size); memset(jrecbuf, 0, jnl_fs_block_size); set_gdid_from_stat(&jnlfile_id, &stat_buf); info->checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)&jnlfile_id, SIZEOF(gd_id)); header = (jnl_file_header *)(ROUND_UP2((uintszofptr_t)hdr_base, jnl_fs_block_size)); /* We have already saved previous journal file name in info */ jfh_from_jnl_info(info, header); header->recover_interrupted = mupip_jnl_recover; header->last_eof_written = FALSE; assert(ROUND_UP2(JNL_HDR_LEN, jnl_fs_block_size) == JNL_HDR_LEN); assert((unsigned char *)header + JNL_HDR_LEN <= ARRAYTOP(hdr_base)); /* Although the real journal file header is REAL_JNL_HDR_LEN, we write the entire journal file header * including padding (64K in Unix) so we write 0-padding instead of some garbage. Not necessary * but makes analysis of journal file using dump utilities (like od) much easier. Also 0-initialization * might help us in the future when we add some fields in the file header. The cost of the extra padding * write is not so much since it is only once per journal file at creation time. All future writes of the * file header write only the real file header and not the 0-padding. */ JNL_DO_FILE_WRITE(csa, create_fn, channel, 0, header, JNL_HDR_LEN, info->status, info->status2); STATUS_MSG(info, info->jnl_len, info->jnl, info->fn_len, info->fn); RETURN_ON_ERROR(info); assert(DISK_BLOCK_SIZE >= EPOCH_RECLEN + EOF_RECLEN + PFIN_RECLEN + PINI_RECLEN); pini_record = (struct_jrec_pini *)&jrecbuf[0]; pini_record->prefix.jrec_type = JRT_PINI; pini_record->prefix.forwptr = pini_record->suffix.backptr = PINI_RECLEN; db_tn = info->csd->trans_hist.curr_tn; pini_record->prefix.tn = db_tn; pini_record->prefix.pini_addr = JNL_HDR_LEN; pini_record->prefix.time = jgbl.gbl_jrec_time; /* callers must set it */ pini_record->suffix.suffix_code = JNL_REC_SUFFIX_CODE; assert(prc_vec); memcpy((unsigned char*)&pini_record->process_vector[CURR_JPV], (unsigned char*)prc_vec, SIZEOF(jnl_process_vector)); /* Already process_vector[ORIG_JPV] is memset 0 */ pini_record->filler = 0; pini_record->prefix.checksum = INIT_CHECKSUM_SEED; temp_checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)pini_record, SIZEOF(struct_jrec_pini)); temp_offset = JNL_HDR_LEN; ADJUST_CHECKSUM(temp_checksum, temp_offset, temp_checksum); ADJUST_CHECKSUM(temp_checksum, info->checksum, pini_record->prefix.checksum); /* EPOCHs are written unconditionally even for NOBEFORE_IMAGE journaling */ epoch_record = (struct_jrec_epoch *)&jrecbuf[PINI_RECLEN]; epoch_record->prefix.jrec_type = JRT_EPOCH; epoch_record->prefix.forwptr = epoch_record->suffix.backptr = EPOCH_RECLEN; epoch_record->prefix.tn = db_tn; epoch_record->prefix.pini_addr = JNL_HDR_LEN; epoch_record->prefix.time = jgbl.gbl_jrec_time; epoch_record->blks_to_upgrd = info->blks_to_upgrd; epoch_record->free_blocks = info->free_blocks; epoch_record->total_blks = info->total_blks; epoch_record->fully_upgraded = info->csd->fully_upgraded; epoch_record->filler0 = 0; epoch_record->suffix.suffix_code = JNL_REC_SUFFIX_CODE; epoch_record->jnl_seqno = info->reg_seqno; for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) epoch_record->strm_seqno[idx] = info->csd->strm_reg_seqno[idx]; if (jgbl.forw_phase_recovery) { /* If MUPIP JOURNAL -ROLLBACK, might need some adjustment. See macro definition for comments */ MUR_ADJUST_STRM_REG_SEQNO_IF_NEEDED(info->csd, epoch_record->strm_seqno); } epoch_record->filler1 = 0; epoch_record->prefix.checksum = INIT_CHECKSUM_SEED; temp_checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)epoch_record, SIZEOF(struct_jrec_epoch)); temp_offset = JNL_HDR_LEN + PINI_RECLEN; ADJUST_CHECKSUM(temp_checksum, temp_offset, temp_checksum); ADJUST_CHECKSUM(temp_checksum, info->checksum, epoch_record->prefix.checksum); pfin_record = (struct_jrec_pfin *)&jrecbuf[PINI_RECLEN + EPOCH_RECLEN]; pfin_offset = JNL_HDR_LEN + PINI_RECLEN + EPOCH_RECLEN; eof_record = (struct_jrec_eof *)&jrecbuf[PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN]; eof_offset = JNL_HDR_LEN + PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN; cre_jnl_rec_size = PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN + EOF_RECLEN; pfin_record->prefix.jrec_type = JRT_PFIN; pfin_record->prefix.forwptr = pfin_record->suffix.backptr = PFIN_RECLEN; pfin_record->prefix.tn = db_tn; pfin_record->prefix.pini_addr = JNL_HDR_LEN; pfin_record->prefix.time = jgbl.gbl_jrec_time; pfin_record->suffix.suffix_code = JNL_REC_SUFFIX_CODE; pfin_record->filler = 0; pfin_record->prefix.checksum = INIT_CHECKSUM_SEED; temp_checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)pfin_record, SIZEOF(struct_jrec_pfin)); ADJUST_CHECKSUM(temp_checksum, pfin_offset, temp_checksum); ADJUST_CHECKSUM(temp_checksum, info->checksum, pfin_record->prefix.checksum); eof_record->prefix.jrec_type = JRT_EOF; eof_record->prefix.forwptr = eof_record->suffix.backptr = EOF_RECLEN; eof_record->prefix.tn = db_tn; eof_record->prefix.pini_addr = JNL_HDR_LEN; eof_record->prefix.time = jgbl.gbl_jrec_time; QWASSIGN(eof_record->jnl_seqno, info->reg_seqno); eof_record->suffix.suffix_code = JNL_REC_SUFFIX_CODE; eof_record->filler = 0; eof_record->prefix.checksum = INIT_CHECKSUM_SEED; temp_checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)eof_record, SIZEOF(struct_jrec_eof)); ADJUST_CHECKSUM(temp_checksum, eof_offset, temp_checksum); ADJUST_CHECKSUM(temp_checksum, info->checksum, eof_record->prefix.checksum); /* Assert that the journal file header and journal records are all in sync with respect to the db tn. */ assert(header->bov_tn == db_tn); assert(header->eov_tn == db_tn); /* Write the journal records, but also 0-fill the tail of the journal file enough to fill * an aligned filesystem-block-size boundary. Take into account that the file header is already written. */ write_size = ROUND_UP2((JNL_HDR_LEN + cre_jnl_rec_size), jnl_fs_block_size) - JNL_HDR_LEN; assert((jrecbuf + write_size) <= (jrecbuf_base + jrecbufbase_size)); /* Assert that initial virtual-size of the journal file (which is nothing but the journal allocation) * gives us enough space to keep the journal file header + padding + PINI/PFIN/EPOCH/EOF records. * Do the assertion in units of 512-byte blocks as that keeps the values well under 4G. Keeping the * units in bytes causes the highest autoswitchlimit value to round up to 4G effectively becoming 0 * on certain platforms and therefore failing the assert. */ assert(ROUND_UP2(header->virtual_size, jnl_fs_block_size/DISK_BLOCK_SIZE) > DIVIDE_ROUND_UP(JNL_HDR_LEN + write_size, DISK_BLOCK_SIZE)); JNL_DO_FILE_WRITE(csa, create_fn, channel, JNL_HDR_LEN, jrecbuf, write_size, info->status, info->status2); STATUS_MSG(info, info->jnl_len, info->jnl, info->fn_len, info->fn); RETURN_ON_ERROR(info); GTM_JNL_FSYNC(csa, channel, status); F_CLOSE(channel, status); /* resets "channel" to FD_INVALID */ /* Now that EOF record has been written, keep csa->jnl->jnl_buff->prev_jrec_time up to date. * One exception is if journaling is not yet turned on but is being turned on by the current caller. * In that case, dont worry about maintaining jb->prev_jrec_time. It will be maintained when this newly * created journal file is first opened. */ if ((NULL != csa) && (NULL != csa->nl)) /* this means database shared memory is accessible (i.e. region is open) */ { /* Keep jb->prev_jrec_time up to date */ SET_JNLBUFF_PREV_JREC_TIME(csa->jnl->jnl_buff, eof_record->prefix.time, DO_GBL_JREC_TIME_CHECK_TRUE); } free(jrecbuf_base); jrecbuf_base = NULL; # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_JNL_CREATE_FAIL)) return EXIT_ERR; # endif if (info->no_rename) return EXIT_NRM; /* Say, info->jnl = a.mjl * So system will have a.mjl and a.mjl_new for a crash before the following call * Following does rename of a.mjl to a.mjl_timestamp. * So system will have a.mjl_timestamp and a.mjl_new for a crash after this call */ WAIT_FOR_REPL_INST_UNFREEZE_SAFE(csa); /* wait for instance freeze before journal file renames */ if (SS_NORMAL != (info->status = gtm_rename((char *)info->jnl, (int)info->jnl_len, (char *)rename_fn, rename_fn_len, &info->status2))) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_RENAMEFAIL, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn); if (!(IS_GTM_IMAGE)) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_RENAMEFAIL, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn); STATUS_MSG(info, info->jnl_len, info->jnl, rename_fn, rename_fn_len); return EXIT_ERR; } /* Following does rename of a.mjl_new to a.mjl. * So system will have a.mjl_timestamp as previous generation and a.mjl as new/current journal file */ WAIT_FOR_REPL_INST_UNFREEZE_SAFE(csa); /* wait for instance freeze before journal file renames */ if (SS_NORMAL != (info->status = gtm_rename((char *)create_fn, create_fn_len, (char *)info->jnl, (int)info->jnl_len, &info->status2))) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_RENAMEFAIL, 4, create_fn_len, create_fn, info->jnl_len, info->jnl); if (!(IS_GTM_IMAGE)) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_RENAMEFAIL, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn); STATUS_MSG(info, create_fn_len, create_fn, info->jnl_len, info->jnl); return EXIT_ERR; } send_msg_csa(CSA_ARG(csa) VARLSTCNT (6) ERR_FILERENAME, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn); if (!(IS_GTM_IMAGE)) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT (6) ERR_FILERENAME, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn); # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_JNL_CREATE_INTERRUPT == gtm_white_box_test_case_number)) { DBGFPF((stderr, "CRE_JNL_FILE: started a wait\n")); LONG_SLEEP(600); assert(FALSE); /* Should be killed before that */ } # endif return EXIT_NRM; } fis-gtm-V7.0-005/sr_port/cre_jnl_file_intrpt_rename.c0000755000032200000250000001261514342376331021561 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2021 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_rename.h" #include "gtm_file_remove.h" #include "gtm_file_stat.h" #include "gtmmsg.h" #include "iosp.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "send_msg.h" #include "gtmimagename.h" #define EXT_NEW_TIME 30 /* GTM-9443 - A process that detects a temporary journal file, waits for a short * period of time, in order to check if its presence persists before deleting/renaming it. */ #define WAIT_FOR_EXT_NEW(FILESTR, USTATUS) \ { \ int lcnt; \ \ for (lcnt = EXT_NEW_TIME; lcnt; lcnt--) \ { \ SHORT_SLEEP(lcnt); \ if (FILE_PRESENT != gtm_file_stat(FILESTR, NULL, NULL, FALSE, USTATUS)) \ { /* Leave cre_jnl_file_intrpt_rename() when the EXT_NEW file doesn't persist */ \ return; \ } \ } \ } GBLREF gd_region *gv_cur_region; error_def(ERR_FILEPARSE); error_def(ERR_RENAMEFAIL); error_def(ERR_FILEDELFAIL); error_def(ERR_FILEDEL); error_def(ERR_FILERENAME); void cre_jnl_file_intrpt_rename(sgmnt_addrs *csa) { int fn_len; sm_uc_ptr_t fn; mstr filestr; int status1, status2, ext_new_jnl_fn_len; uint4 status, ustatus; unsigned char ext_new_jnl_fn[MAX_FN_LEN + STR_LIT_LEN(EXT_NEW) + 1]; assert(csa); assert(csa->hdr); /* We need either crit or standalone to ensure that there are no concurrent switch attempts. */ assert(csa->now_crit || (gv_cur_region && FILE_INFO(gv_cur_region)->grabbed_access_sem)); if (!csa->hdr) return; fn = csa->hdr->jnl_file_name; fn_len = csa->hdr->jnl_file_len; filestr.addr = (char *)fn; filestr.len = fn_len; ext_new_jnl_fn_len = ARRAYSIZE(ext_new_jnl_fn); status = prepare_unique_name((char *)fn, fn_len, "", EXT_NEW, (char *)ext_new_jnl_fn, &ext_new_jnl_fn_len, 0, &ustatus); /* We have allocated enough space in ext_new_jnl_fn array to store EXT_NEW suffix. * So no way the above "prepare_unique_name" call can fail. Hence the below assert. */ assert(SS_NORMAL == status); status1 = gtm_file_stat(&filestr, NULL, NULL, FALSE, &ustatus); if (FILE_STAT_ERROR == status1) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_FILEPARSE, 2, filestr.len, filestr.addr, ustatus); if (!(IS_GTM_IMAGE)) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_FILEPARSE, 2, filestr.len, filestr.addr, ustatus); return; } filestr.addr = (char *)ext_new_jnl_fn; filestr.len = ext_new_jnl_fn_len; status2 = gtm_file_stat(&filestr, NULL, NULL, FALSE, &ustatus); if (FILE_STAT_ERROR == status2) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_FILEPARSE, 2, filestr.len, filestr.addr, ustatus); if (!(IS_GTM_IMAGE)) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_FILEPARSE, 2, filestr.len, filestr.addr, ustatus); return; } if (FILE_NOT_FOUND == status1) { if (FILE_PRESENT == status2) { WAIT_FOR_EXT_NEW(&filestr, &ustatus); status = gtm_rename(filestr.addr, (int)filestr.len, (char *)fn, fn_len, &ustatus); if (SYSCALL_ERROR(status)) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_RENAMEFAIL, 4, filestr.len, filestr.addr, fn_len, fn, status); if (!(IS_GTM_IMAGE)) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_RENAMEFAIL, 4, filestr.len, filestr.addr, fn_len, fn, status); } else { send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_FILERENAME, 4, (int)filestr.len, filestr.addr, fn_len, fn); if (!(IS_GTM_IMAGE)) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_FILERENAME, 4, filestr.len, filestr.addr, fn_len, fn); } } } else { if (FILE_PRESENT == status2) { WAIT_FOR_EXT_NEW(&filestr, &ustatus); status = gtm_file_remove(filestr.addr, (int)filestr.len, &ustatus); if (SYSCALL_ERROR(status)) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_FILEDELFAIL, 2, filestr.len, filestr.addr, status); if (!(IS_GTM_IMAGE)) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_FILEDELFAIL, 2, filestr.len, filestr.addr, status); } else { send_msg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_FILEDEL, 2, filestr.len, filestr.addr); if (!(IS_GTM_IMAGE)) gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_FILEDEL, 2, filestr.len, filestr.addr); } } } } fis-gtm-V7.0-005/sr_port/create_byte_xlate_table.c0000644000032200000250000000340314342376331021041 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018 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 "gtm_string.h" #include "mdef.h" #include "compiler.h" #include "toktyp.h" #include "op.h" /** * Generates a translate table where each byte in the search string (srch) is mapped to the corresponding byte in the replace * string (repl). * * The result translate table contains the replace character at each search character's ASCII value; that is, if srch = "A", * rplc="B", xlate['A' = 97] = 'B'; * For characters that aren't to be replaced, NO_VALUE is set. For characters that are to be deleted, DELETE_VALUE is set. * * @param [in] srch string representing the search characters * @param [in] rplc string representing the replace characters * @param [out] the allocate translate table; expected to be unitt'ed memory of size SIZEOF(int4) * NUM_CHARS */ void create_byte_xlate_table(mval *srch, mval *rplc, int4 *xlate) { sm_uc_ptr_t stop, scur, rtop, rcur; memset(xlate, NO_VALUE, SIZEOF(int4) * NUM_CHARS); scur = (sm_uc_ptr_t)srch->str.addr; stop = scur + srch->str.len; rcur = (sm_uc_ptr_t)rplc->str.addr; rtop = rcur + rplc->str.len; for (; (scur < stop) && (rcur < rtop); scur++, rcur++ ) if (NO_VALUE == xlate[*scur]) xlate[*scur] = *rcur; for (; scur < stop; scur++) if (NO_VALUE == xlate[*scur]) xlate[*scur] = DELETE_VALUE; } fis-gtm-V7.0-005/sr_port/create_dummy_gbldir.c0000644000032200000250000001172614342376331020217 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "mlkdef.h" #include "filestruct.h" #include "gbldirnam.h" #include "hashtab_mname.h" #include "hashtab.h" #include "dpgbldir.h" #define DUMMY_GLD_ADD_MAP(MAP, MAPKEY_PTR, REG, WHICH_MAP) \ MBSTART { \ size_t map_size = DUMMY_GBLDIR_SIZE - sizeof(gd_addr); /* Calculated from malloc for "addr" + "map" init below */ \ \ assert((MAPKEY_PTR - (char *) MAP + (sizeof(WHICH_MAP) - 1)) <= map_size); \ MAP->gvkey.addr = MAPKEY_PTR; \ MEMCPY_LIT(MAPKEY_PTR, WHICH_MAP); \ MAP->reg.addr = REG; \ MAP->gvname_len = DUMMY_GBLDIR_MAP_GVN_SIZE(WHICH_MAP); \ MAP->gvkey_len = DUMMY_GBLDIR_MAP_KEY_SIZE(WHICH_MAP) - 1; \ MAPKEY_PTR += MAP->gvkey_len; \ MAP++; \ } MBEND #define GLD_REG_INIT(REG, RNAME, ADDR) \ MBSTART { \ REG->rname_len = STR_LIT_LEN(RNAME); \ MEMCPY_LIT(REG->rname, RNAME); \ REG->owning_gd = ADDR; \ } MBEND /* C function to create a dummy gld structure in memory. This code is very similar to M code in GDE * so the two need to be maintained in parallel. Currently the only caller of this function is "mu_gv_cur_reg_init" * and so the below code is simplified to work only with that caller. As more callers need this functionality, the * below code can be enhanced to be more generalized. */ gd_addr *create_dummy_gbldir(void) { gd_addr *addr; gd_region *basedb_reg, *statsdb_reg; gd_segment *basedb_seg, *statsdb_seg; gd_binding *map; char *mapkey_ptr; # ifdef DEBUG gd_binding *map_top; # endif /* The below code might need corresponding changes if ever the gld format changes hence the GDE_LABEL_LITERAL assert */ GTM64_ONLY(assert(!MEMCMP_LIT(GDE_LABEL_LITERAL, "GTCGBDUNX115"));) NON_GTM64_ONLY(assert(!MEMCMP_LIT(GDE_LABEL_LITERAL, "GTCGBDUNX015"));) addr = (gd_addr *)malloc(DUMMY_GBLDIR_SIZE); assert(NULL != addr); memset(addr, 0, DUMMY_GBLDIR_SIZE); addr->max_rec_size = 256; addr->maps = (gd_binding *)((UINTPTR_T)addr + SIZEOF(gd_addr)); addr->var_maps_len = DUMMY_GBLDIR_VAR_MAP_SIZE; addr->n_maps = DUMMY_GBLDIR_N_MAPS; addr->n_regions = DUMMY_GBLDIR_N_REGS; addr->n_segments = DUMMY_GBLDIR_N_REGS; addr->n_gblnames = 0; addr->link = 0; addr->is_dummy_gbldir = TRUE; addr->local_locks = 0; basedb_reg = (gd_region *)((char *)addr + SIZEOF(gd_addr) + DUMMY_GBLDIR_TOT_MAP_SIZE); statsdb_reg = basedb_reg + 1; addr->regions = basedb_reg; basedb_seg = (gd_segment *)((INTPTR_T)(basedb_reg + DUMMY_GBLDIR_N_REGS)); statsdb_seg = basedb_seg + 1; addr->segments = basedb_seg; basedb_reg->dyn.addr = basedb_seg; basedb_reg->statsDB_reg_index = 1; basedb_reg->reservedDBFlags = RDBF_NOSTATS; /* Keep statsdb region invisible at start by default. If opened db * indicates it is enabled for statsdb, then start using statsdb reg. */ statsdb_reg->dyn.addr = statsdb_seg; statsdb_reg->statsDB_reg_index = 0; statsdb_reg->reservedDBFlags = RDBF_STATSDB_MASK; /* Start MAPS section initialization */ mapkey_ptr = (char *)((UINTPTR_T)addr + SIZEOF(gd_addr) + DUMMY_GBLDIR_FIX_MAP_SIZE); map = (gd_binding *)((UINTPTR_T)addr + SIZEOF(gd_addr)); DEBUG_ONLY(map_top = map + DUMMY_GBLDIR_N_MAPS); DUMMY_GLD_ADD_MAP(map, mapkey_ptr, basedb_reg, DUMMY_GBLDIR_FIRST_MAP); /* add map "#)" */ DUMMY_GLD_ADD_MAP(map, mapkey_ptr, basedb_reg, DUMMY_GBLDIR_SECOND_MAP); /* add map "%" */ DUMMY_GLD_ADD_MAP(map, mapkey_ptr, basedb_reg, DUMMY_GBLDIR_THIRD_MAP); /* add map "%Y" */ DUMMY_GLD_ADD_MAP(map, mapkey_ptr, statsdb_reg, DUMMY_GBLDIR_FOURTH_MAP); /* add map "%Z" */ DUMMY_GLD_ADD_MAP(map, mapkey_ptr, basedb_reg, DUMMY_GBLDIR_FIFTH_MAP); /* add map ".." */ assert(map == map_top); /* MAPS sections (Fixed and Variable) initialization complete */ basedb_seg->acc_meth = dba_bg; statsdb_seg->acc_meth = dba_mm; addr->end = (UINTPTR_T)(basedb_seg + DUMMY_GBLDIR_N_REGS); GLD_REG_INIT(basedb_reg, "DEFAULT", addr); /* basedb region */ GLD_REG_INIT(statsdb_reg, "default", addr); /* statsdb region */ addr->id = (gd_id *)malloc(SIZEOF(gd_id)); memset(addr->id, 0, SIZEOF(gd_id)); addr->tab_ptr = (hash_table_mname *)malloc(SIZEOF(hash_table_mname)); init_hashtab_mname((hash_table_mname *)addr->tab_ptr, 0, HASHTAB_NO_COMPACT, HASHTAB_NO_SPARE_TABLE ); return addr; } fis-gtm-V7.0-005/sr_port/create_fatal_error_zshow_dmp.c0000644000032200000250000000471414342376331022132 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2022 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_signal.h" #include "gtm_string.h" #include "gtm_limits.h" #include "error.h" #include "jobexam_process.h" #include "gtmdbglvl.h" #include "create_fatal_error_zshow_dmp.h" GBLREF int4 exi_condition; GBLREF uint4 gtmDebugLevel; GBLREF volatile int4 gtmMallocDepth; GBLREF uint4 process_id; GBLREF int process_exiting; /* Create GTM_FATAL_ERROR* ZSHOW dump file for given fatal condition */ void create_fatal_error_zshow_dmp(int4 signal) { unsigned char dump_fn[GTM_PATH_MAX], *dump_fn_ptr; mval dump_fn_mval, dummy_mval; int4 save_SIGNAL; /* On UNIX this is exi_condition */ DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Push out our error now before we potentially overlay it in jobexam_process() */ PRN_ERROR; assert(process_exiting); if (0 == gtmMallocDepth && ((SIGBUS != exi_condition && SIGSEGV != exi_condition) || (GDL_ZSHOWDumpOnSignal & gtmDebugLevel))) { /* For this dumpable condition, create a ZSHOW "*" dump for review. First, build the name we * want the report to have. */ MEMCPY_LIT(dump_fn, GTMFATAL_ERROR_DUMP_FILENAME); dump_fn_ptr = dump_fn + (SIZEOF(GTMFATAL_ERROR_DUMP_FILENAME) - 1); dump_fn_ptr = i2asc(dump_fn_ptr, process_id); *dump_fn_ptr++ = '_'; /* Use bumped value of jobexam_counter but don't actually increment the counter. The actual increment * is done in jobexam_dump() as part of the default file name (not used here). */ dump_fn_ptr = i2asc(dump_fn_ptr, (TREF(jobexam_counter) + 1)); MEMCPY_LIT(dump_fn_ptr, GTMFATAL_ERROR_DUMP_FILETYPE); dump_fn_ptr += (SIZEOF(GTMFATAL_ERROR_DUMP_FILETYPE) - 1); dump_fn_mval.mvtype = MV_STR; dump_fn_mval.str.addr = (char *)dump_fn; dump_fn_mval.str.len = INTCAST(dump_fn_ptr - dump_fn); assert(GTM_PATH_MAX >= dump_fn_mval.str.len); /* Create dump file */ save_SIGNAL = SIGNAL; /* Signal might be modified by jobexam_process() */ jobexam_process(&dump_fn_mval, &dummy_mval, NULL); SIGNAL = save_SIGNAL; } } fis-gtm-V7.0-005/sr_port/create_fatal_error_zshow_dmp.h0000644000032200000250000000137714342376331022141 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2015 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. * * * ****************************************************************/ #ifndef CREATE_FATAL_ERROR_ZSHOW_DMP_H_ #define CREATE_FATAL_ERROR_ZSHOW_DMP_H_ #define GTMFATAL_ERROR_DUMP_FILENAME "GTM_FATAL_ERROR.ZSHOW_DMP_" #define GTMFATAL_ERROR_DUMP_FILETYPE ".txt" void create_fatal_error_zshow_dmp(int4 signal); #endif fis-gtm-V7.0-005/sr_port/create_temporaries.c0000644000032200000250000001047314342376331020071 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2020 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_string.h" #include "gtm_ctype.h" #include "compiler.h" #include "mdq.h" #include "opcode.h" #include "indir_enum.h" #include "nametabtyp.h" #include "toktyp.h" #include "funsvn.h" #include "mmemory.h" #include "advancewindow.h" #include "cmd.h" #include "svnames.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "alias.h" #include "glvn_pool.h" #ifdef UTF8_SUPPORTED #include "gtm_utf8.h" #endif /* This function adds triples to the execution chain to store the values of subscripts (in glvns in compound SETs) * in temporaries (using OC_STOTEMP opcode). This function is only invoked when * a) The SET is a compound SET (i.e. there are multiple targets specified on the left side of the SET command). * b) Subscripts are specified in glvns which are targets of the SET. * e.g. set a=0,(a,array(a))=1 * The expected result of the above command as per the M-standard is that array(0) (not array(1)) gets set to 1. * That is, the value of the subscript "a" should be evaluated at the start of the compound SET before any sets happen * and should be used in any subscripts that refer to the name "a". * In the above example, since it is a compound SET and "a" is used in a subscript, we need to store the value of "a" * before the start of the compound SET (i.e.a=0) in a temporary and use that as the subscript for "array". * If in the above example the compound set was instead specified as set a=1,array(a)=1, the value of 1 gets substituted * when used in "array(a)". * This is where the compound set acts differently from a sequence of multiple sets. This is per the M-standard. * In the above example, the subscript used was also a target within the compound SET. It is possible that the * subscript is not also an individual target within the same compound SET. Even in that case, this function * will be called to store the subscript in temporaries (as we dont know at compile time if a particular * subscript is also used as a target within a compound SET). */ void create_temporaries(triple *sub, opctype put_oc) { oprtype *sb1; triple *s0, *s1; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(TREF(temp_subs)); assert(NULL != sub); sb1 = &sub->operand[1]; if ((OC_GVNAME == put_oc) || (OC_PUTINDX == put_oc) || (OC_SRCHINDX == put_oc)) { sub = sb1->oprval.tref; /* global name */ assert(OC_PARAMETER == sub->opcode); sb1 = &sub->operand[1]; } else if (OC_GVEXTNAM == put_oc) { sub = sb1->oprval.tref; /* first env */ assert(OC_PARAMETER == sub->opcode); sb1 = &sub->operand[0]; assert(TRIP_REF == sb1->oprclass); s0 = sb1->oprval.tref; if ((OC_GETINDX == s0->opcode) || (OC_VAR == s0->opcode)) { s1 = maketriple(OC_STOTEMP); s1->operand[0] = *sb1; *sb1 = put_tref(s1); s0 = s0->exorder.fl; dqrins(s0, exorder, s1); } sb1 = &sub->operand[1]; sub = sb1->oprval.tref; /* second env */ assert(OC_PARAMETER == sub->opcode); sb1 = &sub->operand[0]; assert(TRIP_REF == sb1->oprclass); s0 = sb1->oprval.tref; if ((OC_GETINDX == s0->opcode) || (OC_VAR == s0->opcode)) { s1 = maketriple(OC_STOTEMP); s1->operand[0] = *sb1; *sb1 = put_tref(s1); s0 = s0->exorder.fl; dqrins(s0, exorder, s1); } sb1 = &sub->operand[1]; sub = sb1->oprval.tref; /* global name */ assert(OC_PARAMETER == sub->opcode); sb1 = &sub->operand[1]; } while (sb1->oprclass) { assert(TRIP_REF == sb1->oprclass); sub = sb1->oprval.tref; assert(OC_PARAMETER == sub->opcode); sb1 = &sub->operand[0]; assert(TRIP_REF == sb1->oprclass); s0 = sb1->oprval.tref; if ((OC_GETINDX == s0->opcode) || (OC_VAR == s0->opcode)) { s1 = maketriple(OC_STOTEMP); s1->operand[0] = *sb1; *sb1 = put_tref(s1); s0 = s0->exorder.fl; dqrins(s0, exorder, s1); } sb1 = &sub->operand[1]; } } fis-gtm-V7.0-005/sr_port/create_utf8_xlate_table.c0000644000032200000250000000734014342376331020770 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018-2020 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 "gtm_string.h" #include "mdef.h" #include "mmemory.h" #include "toktyp.h" #include "op.h" #ifdef UTF8_SUPPORTED #include "hashtab_int4.h" #include "hashtab.h" #include "gtm_utf8.h" #endif /** * Generates a translate table where each character in the search string (srch) is mapped to the corresponding character offset in * the replace string (repl). Note character, not byte. * * In the resulting hash_table_int4, the (void*)value in ht_ent_int4 is overloaded to represent the mapped character. Characters * with the mapping MAXPOSINT4 are to be deleted, and characters without an entry are to be left alone. * * Note, we malloc the hashtable here, and it must be freed by the caller. * * @param [in] srch string containing characters to search for * @param [in] rplc string containing the characters to replace with * @param [out] xlate pointer to integer array which this fills out with offsets into the rplc string of utf-8 characters * * @return a hash table with each character mapped to the replace character offset */ hash_table_int4 *create_utf8_xlate_table(mval *srch, mval *rplc, mstr *m_xlate) { char *rbase, *rcur, *rprev, *rtop, *scur, *sprev, *stop; hash_table_int4 *xlate_hash = NULL; ht_ent_int4 *tabent; int rcode, scode, xlate_len; int4 *xlate; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; xlate = (int4 *)m_xlate->addr; xlate_len = m_xlate->len; assertpro((xlate_len >= srch->str.char_len) && ((NUM_CHARS * SIZEOF(int4)) <= xlate_len)); memset(xlate, NO_VALUE, xlate_len); if (!((srch->mvtype & MV_UTF_LEN) && srch->str.len == srch->str.char_len)) { /* need hash table if srch is a not a sting of single bytes */ if (TREF(compile_time)) xlate_hash = (hash_table_int4*)mcalloc(SIZEOF(hash_table_int4)); else xlate_hash = (hash_table_int4*)malloc(SIZEOF(hash_table_int4)); assert(0 < srch->str.len * (100.0 / HT_LOAD_FACTOR)); init_hashtab_int4(xlate_hash, srch->str.len * (100.0 / HT_LOAD_FACTOR), HASHTAB_COMPACT, HASHTAB_SPARE_TABLE); } scur = srch->str.addr; stop = scur + srch->str.len; rbase = rcur = rplc->str.addr; rtop = rcur + rplc->str.len; while ((scur < stop) && (rcur < rtop)) { sprev = scur; scur = (char *)UTF8_MBTOWC(scur, stop, scode); rprev = rcur; rcur = (char *)UTF8_MBTOWC(rcur, rtop, rcode); if (1 == (scur - sprev)) { if (NO_VALUE == xlate[*sprev]) /* 1st replacement rules, so ignore any stragglers */ xlate[*sprev] = (rprev - rbase); } else { assert(NULL != xlate_hash); if(!lookup_hashtab_int4(xlate_hash, (uint4*)&scode)) /* first replacement rules again */ { /* Store the offset of that character in replace string */ add_hashtab_int4(xlate_hash, (uint4*)&scode, (void*)(rprev - rbase + 1), &tabent); } } } while (scur < stop) { /* if replacement character length is less than search character length, drop matches to remaining search characters */ sprev = scur; scur = (char *)UTF8_MBTOWC(scur, stop, scode); if (1 == (scur - sprev)) { if (NO_VALUE == xlate[*sprev]) xlate[*sprev] = DELETE_VALUE; } else { assert(NULL != xlate_hash); if(!lookup_hashtab_int4(xlate_hash, (uint4*)&scode)) add_hashtab_int4(xlate_hash, (uint4*)&scode, (void*)(MAXPOSINT4), &tabent); } } return xlate_hash; } fis-gtm-V7.0-005/sr_port/crit_wake.h0000755000032200000250000000115214342376331016166 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef CRIT_WAKE_H_INCLUDED #define CRIT_WAKE_H_INCLUDED int crit_wake(sm_uint_ptr_t pid); #endif fis-gtm-V7.0-005/sr_port/cryptdef.h0000755000032200000250000000226114342376331016040 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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. * * * ****************************************************************/ typedef struct { uint4 sid[16]; uint4 exp_date; unsigned char gtm_serial[8]; } gtm_id_block; typedef struct { gtm_id_block plaintext; unsigned char key[SIZEOF(gtm_id_block)]; gtm_id_block cryptext; } gtm_id_struct; #define CRYPT_CHKSYSTEM { if (!licensed) \ { \ assertpro((lkid & 4) != 4); \ lkid++; \ } \ } #if defined (DEBUG) || defined (NOLICENSE) #define LP_LICENSED(a,b,c,d,e,f,g,h,i,j) 1 #define LP_ACQUIRE(a,b,c,d) 1 #define LP_CONFIRM(a,b) 1 #else #define LP_LICENSED(a,b,c,d,e,f,g,h,i,j) lp_licensed(a,b,c,d,e,f,g,h,i,j) #define LP_ACQUIRE(a,b,c,d) lp_acquire(a,b,c,d) #define LP_CONFIRM(a,b) lp_confirm(a,b) #endif fis-gtm-V7.0-005/sr_port/ctrlc_handler_dummy.c0000755000032200000250000000100214342376331020222 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "ctrlc_handler_dummy.h" void ctrlc_handler_dummy(void) { } fis-gtm-V7.0-005/sr_port/ctrlc_handler_dummy.h0000755000032200000250000000117314342376331020240 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef CTRLC_HANDLER_DUMMY_H_INCLUDED #define CTRLC_HANDLER_DUMMY_H_INCLUDED void ctrlc_handler_dummy(void); #endif fis-gtm-V7.0-005/sr_port/cvtparm.c0000755000032200000250000000616114342376331015672 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "compiler.h" #include "stringpool.h" #include "io_params.h" #include "io.h" #include "iottdef.h" #include "cvtparm.h" #include "mvalconv.h" #include "cvttime.h" #include "cvtprot.h" GBLREF spdesc stringpool; LITREF unsigned char io_params_size[]; error_def(ERR_DEVPARTOOBIG); error_def(ERR_DEVPARPROT); #define IOP_DESC(a,b,c,d,e) {d, e} LITDEF dev_ctl_struct dev_param_control[] = { #include "iop.h" }; #undef IOP_DESC int4 cvtparm(int iocode, mval *src, mval *dst) { int4 status, nl, tim[2]; int siz, cnt, strlen; short ns; unsigned char *cp, msk; io_termmask lngmsk; assert(MV_DEFINED(src)); strlen = -1; siz = io_params_size[iocode]; ENSURE_STP_FREE_SPACE(siz + 1); switch(dev_param_control[iocode].source_type) { case IOP_SRC_INT: assert(siz == SIZEOF(int4) || siz == SIZEOF(short)); MV_FORCE_NUM(src); nl = MV_FORCE_INT(src); if (siz == SIZEOF(int4)) cp = (unsigned char *)&nl; else { assert (siz == SIZEOF(short)); ns = (short) nl; cp = (unsigned char *) &ns; } break; case IOP_SRC_STR: assert(siz == IOP_VAR_SIZE); MV_FORCE_STR(src); if (src->str.len > 255) /*one byte string lengths within a parameter string*/ return (int4) ERR_DEVPARTOOBIG; strlen = src->str.len; siz = strlen + SIZEOF(unsigned char); cp = (unsigned char *) src->str.addr; break; case IOP_SRC_MSK: MV_FORCE_STR(src); assert(siz == SIZEOF(int4)); nl = 0; for (cp = (unsigned char *) src->str.addr, cnt = src->str.len ; cnt > 0 ; cnt--) { if (*cp < 32) nl |= (1 << *cp++); } cp = (unsigned char *)&nl; break; case IOP_SRC_LNGMSK: MV_FORCE_STR(src); assert(siz == SIZEOF(io_termmask)); memset(&lngmsk, 0, SIZEOF(io_termmask)); for (cp = (unsigned char *) src->str.addr, cnt = src->str.len ; cnt > 0 ; cnt--) { msk = *cp++; nl = msk / 32; lngmsk.mask[nl] |= (1 << (msk - (nl * 32))); } cp = (unsigned char *) &lngmsk; break; case IOP_SRC_PRO: assert(siz == SIZEOF(unsigned char)); MV_FORCE_STR(src); nl = cvtprot(src->str.addr, src->str.len); if (nl == -1) return (int4) ERR_DEVPARPROT; msk = nl; cp = &msk; break; case IOP_SRC_TIME: status = cvttime(src, tim); if ((status & 1) == 0) return status; siz = SIZEOF(tim) ; cp = (unsigned char *) tim ; break; default: assert(FALSE); } dst->mvtype = MV_STR; dst->str.addr = (char *) stringpool.free; dst->str.len = siz; if (strlen >= 0) { *stringpool.free++ = strlen; siz -= SIZEOF(char); } memcpy(stringpool.free, cp, siz); stringpool.free += siz; return 0; } fis-gtm-V7.0-005/sr_port/cvtparm.h0000755000032200000250000000110014342376331015663 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef CVTPARM_INCLUDED #define CVTPARM_INCLUDED int4 cvtparm(int iocode, mval *src, mval *dst); #endif /* CVTPARM_INCLUDED */ fis-gtm-V7.0-005/sr_port/cvtprot.h0000755000032200000250000000115014342376331015715 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef CVTPROT_H_INCLUDED #define CVTPROT_H_INCLUDED int cvtprot(char *cp, short cnt); #endif fis-gtm-V7.0-005/sr_port/cvttime.h0000755000032200000250000000106414342376331015673 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef CVTTIME_INCLUDED #define CVTTIME_INCLUDED int4 cvttime(mval *src, int4 *tim); #endif /* CVTTIME_INCLUDED */ fis-gtm-V7.0-005/sr_port/cws_insert.h0000755000032200000250000000663514342376331016411 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef CWS_INSERT_H_INCLUDED #define CWS_INSERT_H_INCLUDED #include "hashtab.h" #include "hashtab_int8.h" GBLREF hash_table_int8 cw_stagnate; GBLREF boolean_t cw_stagnate_reinitialized; /* Usually a process does not need the cw_stagnate hash table as it is used only in the final retry. * But CWS_RESET (to ensure the hashtable is reinitialized) is done in a lot of places and almost every transaction. * To avoid unnecessary overhead in the reset, we start from a small initial value for CWS_INITIAL_SIZE. * When necessary hash rounties will expand automatically. */ #define CWS_INITIAL_SIZE 4 /* Initialize the cw_stagnate hash-table */ #define CWS_INIT \ { \ if (0 == cw_stagnate.size) \ { \ init_hashtab_int8(&cw_stagnate, CWS_INITIAL_SIZE, HASHTAB_COMPACT, HASHTAB_SPARE_TABLE); \ cw_stagnate_reinitialized = TRUE; \ } \ } /* macro CWS_INSERT appends a block_id onto an hashtable of block_id's * cw_stagnate is the address of the hashtable * it is initially NULL * CWS_INITIAL_SIZE is the initial size of the hash table * table is enlarged each time it fills up * The hashtable is allocated the first time this routine is* called, and the contents of it are reset by calling cws_reset * in t_begin, t_begin_crit and tp_hist. The hashtable expands by itself. */ GBLREF boolean_t mu_reorg_in_swap_blk; GBLREF int4 cws_reorg_remove_index; GBLREF block_id cws_reorg_remove_array[]; #define CWS_INSERT(block) \ { \ ht_ent_int8 *dummy; \ boolean_t new_entry; \ \ cw_stagnate_reinitialized = FALSE; \ new_entry = add_hashtab_int8(&cw_stagnate, (ublock_id *)(&block), HT_VALUE_DUMMY, &dummy); \ /* If in mu_swap_blk and a new entry was added to the hashtable, note this block number \ * in the cws_reorg_remove_array for later removal in the next iteration of the for \ * loop in "mu_swap_blk" (see mu_swap_blk.c for more detail). \ */ \ if (mu_reorg_in_swap_blk && new_entry) \ { \ CWS_REORG_INSERT(block); \ } \ } #define CWS_REORG_REMOVE_ARRAYSIZE ((4 * MAX_BT_DEPTH) + 2) /* see mu_swap_blk.c for detail on this calculation */ #define CWS_REORG_REMOVE_MAXINDEX (CWS_REORG_REMOVE_ARRAYSIZE - 1) #define CWS_REORG_INSERT(blk) \ { \ assert(cws_reorg_remove_index < CWS_REORG_REMOVE_MAXINDEX); \ cws_reorg_remove_array[++cws_reorg_remove_index] = (blk); \ } /* the use of the variable cw_stagnate_reinitialized to optimize CWS_RESET assumes that all calls to * add_hashtab_int8() are done through CWS_INSERT macro. */ #define CWS_RESET \ { /* if a transaction did not use cw_stagnate hash table, there is no need to reset it */ \ if (!cw_stagnate_reinitialized) \ { \ reinitialize_hashtab_int8(&cw_stagnate); \ cw_stagnate_reinitialized = TRUE; \ } \ } #endif fis-gtm-V7.0-005/sr_port/d.mpt0000755000032200000250000000152114342376331015012 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 1987, 2003 Sanchez Computer Associates, 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %D ;GT.M %D utility - write current date as [d]d-mmm-yy ;invoke at INT to set %DAT to the current date as [d]d-mmm-yy ;invoke at FUNC as an extrinsic special variable ;supplied for illustration and compatibility ;use of inline code is easy and efficient ; w $$FUNC() q INT s %DAT=$$FUNC() q FUNC() n h,monyear s monyear=$S($ZDATEFORM:"-MON-YEAR",1:"-MON-YY") s h=$h q +$zd(h,"DD")_$zd(h,monyear) fis-gtm-V7.0-005/sr_port/date.mpt0000755000032200000250000000545214342376331015513 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 1987, 2003 Sanchez Computer Associates, 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %DATE ;GT.M %DATE utility - returns $H format date in %DN ;invoke at INT with %DS to by pass interactive prompt ;formats accepted include: ; days as 1 or 2 digits ; months as 1 or 2 digits or as unique alpha prefixes ; years as 2 or 4 digits ; "T" or "t" for today with an optional +/- offset ; except for the +/- offset any non alphameric character(s) ; are accepted as delimiters ; numeric months must precede days ; alpha months may precede or follow days ; a missing year is defaulted to this year ; null input is defaulted to today ; n %DS f r !,"Date: ",%DS s %DN=$$FUNC(%DS) q:%DN'="" w " - invalid date" q INT s %DN=$$FUNC($g(%DS)) q FUNC(dt) n cp,dd,dir,ilen,mm,tok,tp,yy,dh,zd i $g(dt)="" q +$H s ilen=$l(dt)+1,cp=1 d advance i $e("TODAY",1,$l(tok))=$tr(tok,"today","TODAY") q $$incr(+$H) i $e("TOMORROW",1,$l(tok))=$tr(tok,"tomrw","TOMRW") q $$incr($H+1) i $e("YESTERDAY",1,$l(tok))=$tr(tok,"yestrday","YESTRDAY") q $$incr($H-1) i dir?1A s mm=$$amon(tok) q:mm=0 "" d advance s dd=tok q $$date i tok<1 q "" s mm=tok d advance i dir?1A s dd=mm,mm=$$amon(tok) q:mm=0 "" q $$date i mm<13 s dd=tok q $$date q "" ; advance f cp=cp:1:ilen q:$e(dt,cp)?1AN s dir=$e(dt,cp) i dir?1A f tp=cp+1:1:ilen q:$e(dt,tp)'?1A e i dir?1N f tp=cp+1:1:ilen q:$e(dt,tp)'?1N e s tok="" q s tok=$e(dt,cp,tp-1) s cp=tp q incr(h) f cp=cp:1:ilen q:"+-"[$e(dt,cp) i cp'=ilen s dd=$e(dt,cp) d advance i dir?.1N,cp=ilen q h+(dd_tok) q h ; amon(mm) s mm=$tr(mm,"abcdefghijklmnopqrstuvwxyz","ABCDEFGHIJKLMNOPQRSTUVWXYZ") i $l(mm)<3,"AJM"[tok,"JAPAU"'[mm q 0 n mon s mon="\JANUARY \FEBRUARY \MARCH \APRIL \MAY \JUNE \JULY \AUGUST \SEPTEMBER\OCTOBER \NOVEMBER \DECEMBER" q $f(mon,("\"_mm))+8\10 ; date() i dd<1 q "" d advance i dir'?.1N q "" s zd=$ZDATEFORM s yy=tok i yy="" s yy=$zd($h,"YEAR") i cp'=ilen q "" i $l(yy)<3 d . s dh=$H . s yy=yy+(100*$S('zd:19,(zd>1840)&($L(zd)=4):($E(zd,1,2)+$S($E(zd,3,4)'>yy:0,1:1)),1:$E($ZDATE(dh,"YEAR"),1,2))) ; 20th rolling current century i dd>$s(+mm'=2:$e(303232332323,mm)+28,yy#4:28,yy#100:29,yy#400:28,1:29) q "" n cc,dat s dat=yy-1841,mm=mm-1,cc=1 i dat<0 s dd=dd-1,cc=-1 s dat=dat\4*1461+(dat#4-$s(dat'<0:0,1:4)*365)+(mm*30)+$e(10112234455,mm)+dd-(yy-1800\100-(yy-1600\400)) i yy#4,mm>1 s dat=dat-cc i yy#100=0,mm<2,yy#400 s dat=dat+cc q dat fis-gtm-V7.0-005/sr_port/db_auto_upgrade.c0000644000032200000250000004510414342376331017337 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "gdscc.h" #include "gdsblkops.h" #include "filestruct.h" #include "io.h" #include "jnl.h" #include "mutex.h" #include "wcs_phase2_commit_wait.h" #include "gvcst_protos.h" /* for gvcst_init_sysops prototype */ #include "db_write_eof_block.h" GBLREF boolean_t dse_running; error_def(ERR_DBBADUPGRDSTATE); void db_auto_upgrade(gd_region *reg) { /* detect uninitialized file header fields for this version of GT.M and do a mini auto-upgrade, initializing such fields * to default values in the most recent GT.M version */ sgmnt_addrs *csa; sgmnt_data_ptr_t csd; off_t new_eof; unix_db_info *udi; # ifdef DEBUG gtm_uint64_t file_size; # endif int i; gtm_uint64_t *old_stats, *new_stats; node_local_ptr_t cnl; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(NULL != reg); if (NULL == reg) return; csa = &FILE_INFO(reg)->s_addrs; csd = csa->hdr; assert(NULL != csd); if (NULL == csd) return; assert(0 == memcmp(csd->label, GDS_LABEL, GDS_LABEL_SZ - 1)); /* This function is only for V7 DBs */ if (0 > csd->mutex_spin_parms.mutex_hard_spin_count) csd->mutex_spin_parms.mutex_hard_spin_count = MUTEX_HARD_SPIN_COUNT; if (0 > csd->mutex_spin_parms.mutex_sleep_spin_count) csd->mutex_spin_parms.mutex_sleep_spin_count = MUTEX_SLEEP_SPIN_COUNT; /* zero is a legitimate value for csd->mutex_spin_parms.mutex_spin_sleep_mask; so can't detect if need re-initialization */ INIT_NUM_CRIT_ENTRY_IF_NEEDED(csd); /* Auto upgrade based on minor database version number. This code currently only does auto upgrade and does not * do auto downgrade although that certainly is possible to implement if necessary. For now, if the current version * is at a lower level than the minor db version, we do nothing. * * Note the purpose of the minor_dbver field is so that some part of GT.M (either runtime, or conversion utility) some * time and several versions down the road from now knows by looking at this field what fields in the fileheader are * valid so it is important that the minor db version is incremented each time the fileheader is updated and that * this routine is correspondingly updated to initialize the new fields in prior versions of the header. SE 5/2006. */ if (GDSMVCURR > csd->minor_dbver) { /* In general, the method for adding new versions is: * 1) If there are no automatic updates for this version, it is optional to add the version to the switch * statement below. Those there are more for example at this time (through V53000). * 2) Update (or add) a case for the previous version to update any necessary fields. */ if (!csd->opened_by_gtmv53 && !csd->db_got_to_v5_once) { csd->opened_by_gtmv53 = TRUE; /* This is a case of a database that has been used by a pre-V53 version of GT.M that did not contain * the fix (C9H07-002873). At this point, the database might contain RECYCLED blocks that are a mix of * a) Those blocks that were RECYCLED at the time of the MUPIP UPGRADE from V4 to V5. * b) Those blocks that became RECYCLED due to M-kills in V5. * It is only (a) that we have to mark as FREE as it might contain too-full v4 format blocks. But there * is no way to distinguish the two. So we mark both (a) and (b) as FREE. This will mean no PBLKs written * for (b) and hence no backward journal recovery possible to a point before the start of the REORG UPGRADE. * We force a MUPIP REORG UPGRADE rerun (to mark RECYCLED blocks FREE) by setting fully_upgraded to FALSE. * Note that this does not need to be done for databases created by a V5 version (C9I05-002987). */ if (MASTER_MAP_SIZE_V4 == csd->master_map_len) { csd->fully_upgraded = FALSE; csd->reorg_upgrd_dwngrd_restart_block = 0; /* reorg upgrade should restart from block 0 */ /* Ensure reorg_db_fmt_start_tn and desired_db_format_tn are set to different * values so fresh reorg upgrade can set fully_upgraded to TRUE once it is done. */ csd->reorg_db_fmt_start_tn = 0; csd->desired_db_format_tn = 1; } else csd->db_got_to_v5_once = TRUE; /* db was created by V5 so safe to set this */ } /* When adding a new minor version, the following template should be maintained * a) Remove the penultimate 'break' * b) Remove the assert(FALSE) in the last case (most recent minor version) * c) If there are any file header fields added in the new minor version, initialize the fields to default values * in the last case * d) Add a new case with the new minor version * e) Add assert(FALSE) and break (like it was before) */ switch (csd->minor_dbver) { /* Note that handling for any fields introduced in a version will not go in the "switch-case" block * of code introduced for the new version but will go in the PREVIOUS "switch-case" block. */ case GDSMV63015: /* because we added this in case, after its number had alreay been used by V70000 */ case GDSMV70000: csd->creation_db_ver = GDSVCURR; /* required due to renumbering of db_vers due to V6 upgrades */ csd->creation_mdb_ver = GDSMV70000; /* required due to adding protection for possible V63015 */ csd->desired_db_format = GDSVCURR; /* required due to renumbering of db_vers due to V6 upgrades */ csd->minor_dbver = GDSMV70000; /* required due to adding protection for possible V63015 */ csd->statsdb_allocation = STATSDB_ALLOCATION; csd->offset = 0; csd->max_rec = 0; csd->i_reserved_bytes = 0; csd->db_got_to_V7_once = TRUE; csd->last_start_backup = (gtm_timet) 0; /* GTM-8681, but this is default value anyway */ /*the next four lines are because gvcst_init_sysops might have used an outdated assumption */ csd->max_update_array_size = csd->max_non_bm_update_array_size = (int4)(ROUND_UP2(MAX_NON_BITMAP_UPDATE_ARRAY_SIZE(csd), UPDATE_ARRAY_ALIGN_SIZE)); csd->max_update_array_size += (int4)(ROUND_UP2(MAX_BITMAP_UPDATE_ARRAY_SIZE(csd), UPDATE_ARRAY_ALIGN_SIZE)); case GDSMV70001: /* GT.M V70002 added proactive block split option */ csd->problksplit = DEFAULT_PROBLKSPLIT; break; /* so a new "case" needs to be added BEFORE the assert. */ case GDSMV70002: /* Nothing to do for this version since it is GDSMVCURR for now. */ assert(FALSE); break; default: /* Unrecognized version in the header */ assertpro(FALSE && csd->minor_dbver); } csd->minor_dbver = GDSMVCURR; } csd->last_mdb_ver = GDSMVCURR; if (csd->fully_upgraded && !csd->db_got_to_v5_once) { /* Database is fully upgraded but the db_got_to_v5_once field says different. * Don't know how that could happen, except with DSE which can change both the database file header fields */ assert(!dse_running); csd->db_got_to_v5_once = TRUE; /* fix it in PRO */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_DBBADUPGRDSTATE, 4, REG_LEN_STR(reg), DB_LEN_STR(reg)); } return; } void v6_db_auto_upgrade(gd_region *reg) { /* detect uninitialized file header fields for this version of GT.M and do a mini auto-upgrade, initializing such fields * to default values in the last GT.M V6 version */ sgmnt_addrs *csa; sgmnt_data_ptr_t csd; off_t new_eof; unix_db_info *udi; # ifdef DEBUG gtm_uint64_t file_size; # endif int i; gtm_uint64_t *old_stats, *new_stats; node_local_ptr_t cnl; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(NULL != reg); if (NULL == reg) return; csa = &FILE_INFO(reg)->s_addrs; csd = csa->hdr; assert(NULL != csd); /* Auto upgrade previously done by current ver, skip it. WARNING: Fields auto-upgraded to non-zero * values, like problksplit, need special handling to enabled switch V7 versions working with a V6 * database. There is no such special casing at the moment. Future versions will need to address it */ if ((NULL == csd) || (GDSMVCURR == csd->last_mdb_ver)) return; assert(0 == memcmp(csd->label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)); /* This function is only for V6 DBs */ if (0 > csd->mutex_spin_parms.mutex_hard_spin_count) csd->mutex_spin_parms.mutex_hard_spin_count = MUTEX_HARD_SPIN_COUNT; if (0 > csd->mutex_spin_parms.mutex_sleep_spin_count) csd->mutex_spin_parms.mutex_sleep_spin_count = MUTEX_SLEEP_SPIN_COUNT; /* zero is a legitimate value for csd->mutex_spin_parms.mutex_spin_sleep_mask; so can't detect if need re-initialization */ INIT_NUM_CRIT_ENTRY_IF_NEEDED(csd); /* Auto upgrade based on minor database version number. This code currently only does auto upgrade and does not * do auto downgrade although that certainly is possible to implement if necessary. For now, if the current version * is at a lower level than the minor db version, we do nothing. * * Note the purpose of the minor_dbver field is so that some part of GT.M (either runtime, or conversion utility) some * time and several versions down the road from now knows by looking at this field what fields in the fileheader are * valid so it is important that the minor db version is incremented each time the fileheader is updated and that * this routine is correspondingly updated to initialize the new fields in prior versions of the header. SE 5/2006. */ if (csd->minor_dbver <= BLK_ID_32_MVER) { /* In general, the method for adding new versions is: * 1) If there are no automatic updates for this version, it is optional to add the version to the switch * statement below. Those there are more for example at this time (through V53000). * 2) Update (or add) a case for the previous version to update any necessary fields. */ if (!csd->opened_by_gtmv53 && !csd->db_got_to_v5_once) { csd->opened_by_gtmv53 = TRUE; /* This is a case of a database that has been used by a pre-V53 version of GT.M that did not contain * the fix (C9H07-002873). At this point, the database might contain RECYCLED blocks that are a mix of * a) Those blocks that were RECYCLED at the time of the MUPIP UPGRADE from V4 to V5. * b) Those blocks that became RECYCLED due to M-kills in V5. * It is only (a) that we have to mark as FREE as it might contain too-full v4 format blocks. But there * is no way to distinguish the two. So we mark both (a) and (b) as FREE. This will mean no PBLKs written * for (b) and hence no backward journal recovery possible to a point before the start of the REORG UPGRADE. * We force a MUPIP REORG UPGRADE rerun (to mark RECYCLED blocks FREE) by setting fully_upgraded to FALSE. * Note that this does not need to be done for databases created by a V5 version (C9I05-002987). */ if (MASTER_MAP_SIZE_V4 == csd->master_map_len) { csd->fully_upgraded = FALSE; csd->reorg_upgrd_dwngrd_restart_block = 0; /* reorg upgrade should restart from block 0 */ /* Ensure reorg_db_fmt_start_tn and desired_db_format_tn are set to different * values so fresh reorg upgrade can set fully_upgraded to TRUE once it is done. */ csd->reorg_db_fmt_start_tn = 0; csd->desired_db_format_tn = 1; } else csd->db_got_to_v5_once = TRUE; /* db was created by V5 so safe to set this */ } /* When adding a new minor version, the following template should be maintained * a) Remove the penultimate 'break' * b) Remove the assert(FALSE) in the last case (most recent minor version) * c) If there are any file header fields added in the new minor version, initialize the fields to default values * in the last case * d) Add a new case with the new minor version * e) Add assert(FALSE) and break (like it was before) */ switch (csd->minor_dbver) { /* Note that handling for any fields introduced in a version will not go in the "switch-case" block * of code introduced for the new version but will go in the PREVIOUS "switch-case" block. */ case GDSMV50000: case GDSMV51000ALT: case GDSMV51000: /* Multi-site replication available */ case GDSMV52000: /* UTF8 */ case GDSMV53000: /* M-Itanium release */ gvstats_rec_upgrade(csa); /* Move GVSTATS information to new place in file header */ /* V53002 introduced wcs_phase2_commit_wait_spincnt */ if (0 == csd->wcs_phase2_commit_wait_spincnt) csd->wcs_phase2_commit_wait_spincnt = WCS_PHASE2_COMMIT_DEFAULT_SPINCNT; case GDSMV53003: /* ZSHOW "G" release */ csd->is_encrypted = FALSE; memset(csd->encryption_hash, 0, GTMCRYPT_RESERVED_HASH_LEN); case GDSMV53004: /* New encryption fields */ csd->db_trigger_cycle = 0; case GDSMV54000: /* First trigger version */ case GDSMV54002: /* GT.M V54002B introduced jnl_eov_tn for backward recovery */ csd->jnl_eovtn = csd->trans_hist.curr_tn; case GDSMV54002B: /* GT.M V55000 introduced strm_reg_seqno, save_strm_reg_seqno, intrpt_recov_resync_strm_seqno * AND obsoleted dualsite_resync_seqno. For new fields, we are guaranteed they are * zero (in formerly unused sections of the file header) so no need for any initialization. * For obsoleted fields, it would be good to clear them here so we don't run into issues later. */ csd->filler_seqno = 0; /* was "dualsite_resync_seqno" in pre-V55000 versions */ /* In addition, V55000 introduced before_trunc_total_blks for MUPIP REORG -TRUNCATE. * Since it is a new field no initialization necessary. */ case GDSMV55000: csd->freeze_on_fail = FALSE; csd->span_node_absent = TRUE; csd->maxkeysz_assured = FALSE; case GDSMV60000: case GDSMV60001: /* GT.M V60002 introduced mutex_spin_parms.mutex_que_entry_space_size */ NUM_CRIT_ENTRY(csd) = DEFAULT_NUM_CRIT_ENTRY; case GDSMV60002: /* GT.M V62001 introduced ^#t upgrade. Record this pending event in filehdr. */ csd->hasht_upgrade_needed = TRUE; case GDSMV62001: /* GT.M V62002 introduced database file preallocation. */ csd->defer_allocate = TRUE; /* GT.M V62002 incremented ^#t label. Record this pending event in filehdr. */ csd->hasht_upgrade_needed = TRUE; /* GT.M V62002 introduced epoch taper */ csd->epoch_taper = TRUE; csd->epoch_taper_time_pct = EPOCH_TAPER_TIME_PCT_DEFAULT; csd->epoch_taper_jnl_pct = EPOCH_TAPER_JNL_PCT_DEFAULT; case GDSMV62002: /* GT.M V63000 introduced non-null IV encryption and encryption on-the-fly. */ csd->non_null_iv = FALSE; csd->encryption_hash_cutoff = UNSTARTED; csd->encryption_hash2_start_tn = 0; memset(csd->encryption_hash2, 0, GTMCRYPT_RESERVED_HASH_LEN); SPIN_SLEEP_MASK(csd) = 0; /* previously unused, but was 7FF and it should now default to 0 */ case GDSMV63000: /* GT.M V63000A moved ftok_counter_halted and access_counter_halted from filehdr to node_local */ csd->filler_ftok_counter_halted = FALSE; csd->filler_access_counter_halted = FALSE; case GDSMV63000A: /* GT.M V63001 introduced asyncio but csd->asyncio could be set to TRUE by a MUPIP SET -ASYNCIO * command which did not come through here (because it needs standalone access). Therefore * do not set csd->asyncio to FALSE like is normally done for any newly introduced field. * csd->asyncio = FALSE; */ /* The database file would have a 512-byte EOF block. Enlarge it to be a GDS-block instead. */ udi = FILE_INFO(reg); new_eof = (off_t)BLK_ZERO_OFF(csd->start_vbn) + (off_t)csd->trans_hist.total_blks * csd->blk_size; DEBUG_ONLY(file_size = gds_file_size(reg->dyn.addr->file_cntl);) assert((file_size * DISK_BLOCK_SIZE) == (new_eof + DISK_BLOCK_SIZE)); db_write_eof_block(udi, udi->fd, csd->blk_size, new_eof, &TREF(dio_buff)); /* GT.M V63001 introduced reservedDBFlags */ csd->reservedDBFlags = 0; /* RDBF_AUTODB = FALSE, RDBF_NOSTATS = FALSE, RDBF_STATSDB = FALSE */ case GDSMV63001: /* GT.M V63003 introduced read-only databases */ csd->read_only = 0; case GDSMV63003: /* GT.M v63007 added stable user control of flush_trigger using flush_trigger_top */ csd->flush_trigger_top = FLUSH_FACTOR(csd->n_bts); /* more predictable than flush_trigger */ case GDSMV63007: /* GT.M V63012 added fullblkwrt option */ csd->write_fullblk = 0; case GDSMV63012: /* Copy the 62 pre GTM-8863 stats from the old header location to the new one */ cnl = csa->nl; old_stats = (gtm_uint64_t *) &csd->gvstats_rec_old_now_filler[0]; new_stats = (gtm_uint64_t *) &cnl->gvstats_rec; for (i = 0; i < SIZEOF(csd->gvstats_rec_old_now_filler)/SIZEOF(gtm_uint64_t); i++) new_stats[i] = old_stats[i]; case GDSMV63014: csd->desired_db_format = GDSV6; /* because it takes an action to move toward V7 */ if (0 == csd->statsdb_allocation) csd->statsdb_allocation = STATSDB_ALLOCATION; /* Initialize with statsdb default value */ /* we do not initialize new post GDSMV63014 field csd->last_start_backup here because if it has never been set the default (all zeroes) is OK, and if it has, we do not want to overwrite it on our way up */ csd->db_got_to_V7_once = FALSE; csd->offset = 0; csd->max_rec = 0; csd->i_reserved_bytes = 0; /*the next four lines are because gvcst_init_sysops might have used an outdated assumption */ csd->max_update_array_size = csd->max_non_bm_update_array_size = (int4)(ROUND_UP2(MAX_NON_BITMAP_UPDATE_ARRAY_SIZE(csd), UPDATE_ARRAY_ALIGN_SIZE)); csd->max_update_array_size += (int4)(ROUND_UP2(MAX_BITMAP_UPDATE_ARRAY_SIZE(csd), UPDATE_ARRAY_ALIGN_SIZE)); /* GT.M V70002 added proactive block split option */ csd->problksplit = DEFAULT_PROBLKSPLIT; break; case GDSMV63015: assert(FALSE); /* if this should come to pass, add appropriate code above the assert */ break; default: /* Unrecognized version in the header */ assertpro(FALSE && csd->minor_dbver); } csd->minor_dbver = BLK_ID_32_MVER; /* applies to all pre-V7 versions and expected here by workcheck */ } csd->last_mdb_ver = GDSMVCURR; if (csd->fully_upgraded && !csd->db_got_to_v5_once) { /* Database is fully upgraded but the db_got_to_v5_once field says different. * Don't know how that could happen, except with DSE which can change both the database file header fields */ assert(!dse_running); csd->db_got_to_v5_once = TRUE; /* fix it in PRO */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_DBBADUPGRDSTATE, 4, REG_LEN_STR(reg), DB_LEN_STR(reg)); } return; } fis-gtm-V7.0-005/sr_port/db_common_init.c0000755000032200000250000000422614342376331017176 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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. * * * ****************************************************************/ /* This is the routine used by both db_init() and mu_rndwn_file() for initializing appropriate structures. */ #include "mdef.h" #include "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "jnl.h" #include "wcs_clean_dbsync.h" /* for setting wcs_clean_dbsync pointer */ GBLREF jnl_process_vector *prc_vec; GBLREF void (*wcs_stale_fptr)(); GBLREF void (*wcs_clean_dbsync_fptr)(); void db_common_init(gd_region *reg, sgmnt_addrs *csa, sgmnt_data_ptr_t csd) { csa->bmm = MM_ADDR(csd); csa->reorg_last_dest = 0; /* For mupip reorg swap operation */ csa->region = reg; /* initialize the back-link */ reg->max_rec_size = csd->max_rec_size; reg->max_key_size = csd->max_key_size; reg->null_subs = csd->null_subs; reg->std_null_coll = csd->std_null_coll; reg->jnl_state = csd->jnl_state; reg->jnl_file_len = csd->jnl_file_len; /* journal file name length */ memcpy(reg->jnl_file_name, csd->jnl_file_name, reg->jnl_file_len); /* journal file name */ reg->jnl_alq = csd->jnl_alq; reg->jnl_deq = csd->jnl_deq; reg->jnl_buffer_size = csd->jnl_buffer_size; reg->jnl_before_image = csd->jnl_before_image; assert(!IS_AIO_DBGLDMISMATCH(reg->dyn.addr, csd)); /* Or else "gvcst_init" should have reopened the file */ bt_init(csa); /* Initialization of prc_vec is done even for no journaling. gtcm uses this always. Others might need it too. */ if (NULL == prc_vec) { prc_vec = (jnl_process_vector *)malloc(SIZEOF(jnl_process_vector)); jnl_prc_vector(prc_vec); } wcs_stale_fptr = &wcs_stale; wcs_clean_dbsync_fptr = &wcs_clean_dbsync; } fis-gtm-V7.0-005/sr_port/db_csh_get.c0000755000032200000250000000655414342376331016305 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsbgtr.h" #include "cache.h" #include "longset.h" /* needed for cws_insert.h */ #include "cws_insert.h" GBLREF sgmnt_addrs *cs_addrs; GBLREF unsigned int t_tries; GBLREF boolean_t mu_reorg_process; #define ENOUGH_TRIES_TO_FALL_BACK 17 /* This function returns a pointer to the cache_rec entry, NULL if not found, or CR_INVALID if the hash table is corrupt */ cache_rec_ptr_t db_csh_get(block_id block) /* block number to look up */ { register sgmnt_addrs *csa; sgmnt_data_ptr_t csd; cache_rec_ptr_t cr, cr_hash_base; int blk_hash, lcnt, ocnt, hmax; node_local_ptr_t cnl; # ifdef DEBUG cache_rec_ptr_t cr_low, cr_high; # endif csa = cs_addrs; csd = csa->hdr; cnl = csa->nl; assert(dba_mm != csd->acc_meth); hmax = csd->bt_buckets; blk_hash = (int)(block % hmax); /* This can be cast because it is constrained by hmax which currently fits into an int */ DEBUG_ONLY(cr_low = &csa->acc_meth.bg.cache_state->cache_array[0];) DEBUG_ONLY(cr_high = cr_low + csd->bt_buckets + csd->n_bts;) cr_hash_base = csa->acc_meth.bg.cache_state->cache_array + blk_hash; ocnt = 0; do { cr = cr_hash_base; assert((0 == cr->blk) || (BT_QUEHEAD == cr->blk)); lcnt = hmax; do { cr = (cache_rec_ptr_t)((sm_uc_ptr_t)cr + cr->blkque.fl); assert(!CR_NOT_ALIGNED(cr, cr_low) && !CR_NOT_IN_RANGE(cr, cr_low, cr_high)); if (BT_QUEHEAD == cr->blk) { /* We have reached the end of the queue, validate we have run the queue * back around to the same queue header or we'll need to retry because the * queue changed on us. */ if (cr == cr_hash_base) return (cache_rec_ptr_t)NULL; break; /* Retry - something changed */ } if ((CR_BLKEMPTY != cr->blk) && ((cr->blk % hmax) != blk_hash)) break; /* Retry - something changed */ assert(!csa->now_crit || (0 != cr->blkque.fl) && (0 != cr->blkque.bl)); if (cr->blk == block) { if (CDB_STAGNATE <= t_tries || mu_reorg_process) CWS_INSERT(block); /* setting refer outside of crit may not prevent its replacement, but that's an * inefficiency, not a tragedy because of concurrency checks in t_end or tp_tend; * the real problem is to ensure that the cache_rec layout is such that this * assignment does not damage other fields. */ cr->refer = TRUE; return cr; } lcnt--; } while (lcnt); ocnt++; /* We rarely expect to come here, hence it is considered better to recompute the maximum value of ocnt (for the * termination check) instead of storing it in a local variable at the beginning of the do loop */ } while (ocnt < (csa->now_crit ? 1 : ENOUGH_TRIES_TO_FALL_BACK)); INCR_GVSTATS_COUNTER(csa, cnl, n_buffer_scarce, 1); return (TRUE == csa->now_crit ? (cache_rec_ptr_t)CR_NOTVALID : (cache_rec_ptr_t)NULL); } fis-gtm-V7.0-005/sr_port/db_csh_getn.c0000644000032200000250000005144514342376331016457 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_signal.h" /* needed for VSIG_ATOMIC_T */ #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdskill.h" #include "gdscc.h" #include "filestruct.h" #include "interlock.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "hashtab_int8.h" #include "tp.h" #include "gdsbgtr.h" #include "min_max.h" #include "sleep_cnt.h" #include "send_msg.h" #include "relqop.h" #include "is_proc_alive.h" #include "cache.h" #include "longset.h" /* needed for cws_insert.h */ #include "cws_insert.h" #include "wcs_sleep.h" #include "wcs_get_space.h" #include "add_inter.h" #include "wbox_test_init.h" #include "have_crit.h" #include "memcoherency.h" #include "gtm_c_stack_trace.h" #include "anticipatory_freeze.h" #include "wcs_wt.h" GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF uint4 process_id; GBLREF unsigned int t_tries; GBLREF uint4 dollar_tlevel; GBLREF sgm_info *sgm_info_ptr; GBLREF boolean_t mu_reorg_process; #ifdef UNIX GBLREF uint4 update_trans; GBLREF jnlpool_addrs_ptr_t jnlpool; #endif #define TRACE_AND_SLEEP(ocnt) \ { \ if (1 == ocnt) \ { \ BG_TRACE_PRO(db_csh_getn_rip_wait); \ first_r_epid = latest_r_epid; \ } \ wcs_sleep(ocnt); \ } error_def(ERR_BUFRDTIMEOUT); error_def(ERR_INVALIDRIP); /* Note: does not check cr against bounds of cache, i.e. against [start_cr..midnite) */ #define WITHIN_POOLLIMIT_BOUNDS(cr, our_midnite, start_cr, gbuff_limit, max_ent) \ ((((our_midnite - gbuff_limit < start_cr) \ /* there is a wrap so must check if cr is in the tail */ \ && (cr >= our_midnite - gbuff_limit + max_ent))) \ || ((cr < our_midnite) && (cr >= our_midnite - gbuff_limit))) cache_rec_ptr_t db_csh_getn(block_id block) { cache_rec_ptr_t cr, hdr, midnite, our_midnite, q0, start_cr, poollimit_cr; bt_rec_ptr_t bt; gd_region *reg; unsigned int lcnt, ocnt; int max_ent, pass0, pass0cnt, pass1, pass2, pass3, rip; int4 flsh_trigger; uint4 first_r_epid, latest_r_epid; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; srch_blk_status *tp_srch_status; ht_ent_int8 *tabent; boolean_t asyncio, dont_flush_buff; intrpt_state_t prev_intrpt_state; # ifdef DEBUG cache_rec_ptr_t cr_old; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; csa = cs_addrs; csd = csa->hdr; assert(dba_mm != csd->acc_meth); assert(csa->now_crit); reg = gv_cur_region; assert(csa == &FILE_INFO(reg)->s_addrs); /* If this is an encrypted database, make sure our private cycle matches the shared cycle. Or else * if we need to call "wcs_wtstart" below, it cannot flush dirty buffers and will create a wc_blocked * situation (which is best avoided). */ assert((NULL == csa->encr_ptr) || (csa->nl->reorg_encrypt_cycle == csa->encr_ptr->reorg_encrypt_cycle)); max_ent = csd->n_bts; hdr = csa->acc_meth.bg.cache_state->cache_array + (block % csd->bt_buckets); start_cr = csa->acc_meth.bg.cache_state->cache_array + csd->bt_buckets; pass0 = csa->gbuff_limit; /* gbuff_limit set by VIEW "POOLLIMIT": */ pass0cnt = (0 == pass0) ? 0 : 3; /* Used both as a flag we are limiting and a counter for 2 limited trips, * over 3 passes in total. */ pass1 = max_ent; /* skip referred or dirty or read-into cache records */ pass2 = 2 * max_ent; /* skip referred cache records */ pass3 = 3 * max_ent; /* skip nothing */ midnite = start_cr + max_ent; /* "on the clock" - point at which we have to wrap or change tactics */ cr = (cache_rec_ptr_t)GDS_REL2ABS(csa->nl->cur_lru_cache_rec_off); if (pass0cnt) { our_midnite = csa->our_midnite; /* local copy of private "hand" for efficiency - only used if pass0cnt */ assert(start_cr < our_midnite); /* Try starting where we left off from the last invocation of db_csh_getn */ poollimit_cr = cr = (cache_rec_ptr_t)GDS_REL2ABS(csa->our_lru_cache_rec_off); assert(WITHIN_POOLLIMIT_BOUNDS(cr, our_midnite, start_cr, pass0, max_ent)); if (cr < our_midnite) { /* We can only set midnite = our_midnite if our interval is not wrapped, or if it is wrapped cr must be * before our_midnite (i.e. we wrapped previously). In either case, cr < our_midnite. */ midnite = our_midnite; } } assert((start_cr <= cr) && ((start_cr + max_ent) > cr)); dont_flush_buff = reg->read_only || (!(dollar_tlevel ? sgm_info_ptr->update_trans : update_trans) && (DBG_ASSERT(!csa->jnlpool || (csa->jnlpool == jnlpool)) IS_REPL_INST_FROZEN)); INCR_DB_CSH_COUNTER(csa, n_db_csh_getns, 1); asyncio = csd->asyncio; DEFER_INTERRUPTS(INTRPT_IN_DB_CSH_GETN, prev_intrpt_state); for (lcnt = 0; ; lcnt++) { if (lcnt > pass3) { BG_TRACE_PRO(wc_blocked_db_csh_getn_loopexceed); assert(FALSE); break; } cr++; if (cr >= midnite) /* == should work but >= is slightly safer and no more expensive */ { cr = start_cr; if (pass0cnt) { /* doing restricted looking */ if (midnite != our_midnite) { /* "ordinary" end of buffer - wrap and set up private stop */ assert((start_cr + max_ent) == midnite); midnite = our_midnite; } else { /* Here we perform retries on the local pool: * pass0cnt == 3: We start from where we last left off, walk to our_midnite * pass0cnt == 2: We start from our_midnite - pass0, and walk to our_midnite * pass0cnt == 1: We start from our_midnite - pass0, and walk to where we * last left off (where we started in pass0cnt == 3). * This way we end up doing two full passes over the entire local pool. */ pass0cnt--; if (2 == pass0cnt) { cr = our_midnite - pass0; /* in our restricted area */ if (cr < start_cr) { /* wrap before our_midnite */ cr += max_ent; midnite = start_cr + max_ent; } } else if (1 == pass0cnt) { cr = our_midnite - pass0; our_midnite = poollimit_cr + 1; if (cr < start_cr) { cr += max_ent; midnite = cr > poollimit_cr ? start_cr + max_ent : our_midnite; } else midnite = our_midnite; assert(cr < midnite); /* we must have already restarted the search from our_midnite - pass0 * (in pass0cnt == 2) */ assert(lcnt == pass0); } else { /* or the limited area did not suffice - adopt the normal clock */ cr = (cache_rec_ptr_t)GDS_REL2ABS(csa->nl->cur_lru_cache_rec_off); midnite = start_cr + max_ent; } lcnt = 0; } } } assert((start_cr <= cr) && ((start_cr + max_ent) > cr)); /* If ASYNCIO is enabled, once in a while check if there is anything in the wip queue ready to be freed. * A call to "wcs_wtfini" does just that. Note that "wcs_wtfini" can return FALSE in case of some queue * interlock issues. But in that case it would have set "cnl->wc_blocked" to WC_BLOCK_RECOVER and a * cache recovery will be issued by the next process that gets crit. We do not rely on the success of * the "wcs_wtfini" cleanup so proceed even in case of a FALSE return. Hence not checking the return * value. */ if (asyncio && ((lcnt == pass1) || (lcnt == pass2))) { DEBUG_ONLY(dbg_wtfini_lcnt = dbg_wtfini_db_csh_getn); /* used by "wcs_wtfini" */ /* do not do heavyweight "is_proc_alive" check inside crit */ wcs_wtfini(reg, CHECK_IS_PROC_ALIVE_FALSE, NULL); } if (cr->refer && (lcnt < pass2)) { /* in passes 1 & 2, set refer to FALSE and skip; in the third pass attempt reuse even if TRUE == refer */ cr->refer = FALSE; continue; } if (cr->in_cw_set || cr->in_tend) { /* some process already has this pinned for reading and/or updating. skip it. */ cr->refer = TRUE; continue; } if (CDB_STAGNATE <= t_tries || mu_reorg_process) { /* Prevent stepping on self when crit for entire transaction. * This is done by looking up in sgm_info_ptr->blk_in_use and cw_stagnate for presence of the block. * The following two hashtable lookups are not similar, since in TP, sgm_info_ptr->blks_in_use * is updated to the latest cw_stagnate list of blocks only in "tp_hist". * Also note that the lookup in sgm_info_ptr->blks_in_use reuses blocks that don't have cse's. * This is to allow big-read TP transactions which may use up more than the available global buffers. * There is one issue here in that a block that has been only read till now may be stepped upon here * but may later be needed for update. It is handled by updating the block's corresponding * entry in the set of histories (sgm_info_ptr->first_tp_hist[index] structure) to hold the * "cr" and "cycle" of the t_qread done for the block when it was intended to be changed for the * first time within the transaction since otherwise the transaction would restart due to a * cdb_sc_lostcr status. Note that "tn" (read_tn of the block) in the first_tp_hist will still * remain the "tn" when the block was first read within this transaction to ensure the block * hasn't been modified since the start of the transaction. Once we intend on changing the * block i.e. srch_blk_status->cse is non-NULL, we ensure in the code below not to step on it. * ["tp_hist" is the routine that updates the "cr", "cycle" and "tn" of the block]. * Note that usually in a transaction the first_tp_hist[] structure holds the "cr", "cycle", and "tn" * of the first t_qread of the block within that transaction. The above is the only exception. * Also note that for blocks in cw_stagnate (i.e. current TP mini-action), we don't reuse any of * them even if they don't have a cse. This is to ensure that the current action doesn't * encounter a restart due to cdb_sc_lostcr in "tp_hist" even in the fourth-retry. */ tp_srch_status = NULL; if (dollar_tlevel && (NULL != (tabent = lookup_hashtab_int8(sgm_info_ptr->blks_in_use, (ublock_id *)&cr->blk))) && (tp_srch_status = (srch_blk_status *)tabent->value) && (tp_srch_status->cse)) { /* this process is already using the block - skip it */ cr->refer = TRUE; continue; } if (NULL != lookup_hashtab_int8(&cw_stagnate, (ublock_id *)&cr->blk)) { /* this process is already using the block for the current gvcst_search - skip it */ cr->refer = TRUE; continue; } if (NULL != tp_srch_status) { /* About to reuse a buffer that is part of the read-set of the current TP transaction. * Reset clue as otherwise the next global reference of that global will use an outofdate clue. * Even though tp_srch_status is available after the sgm_info_ptr->blks_in_use hashtable check, * we don't want to reset the clue in case the cw_stagnate hashtable check causes the same cr * to be skipped from reuse. Hence the placement of this reset logic AFTER the cw_stagnate check. */ tp_srch_status->blk_target->clue.end = 0; } } if (cr->dirty) { /* Note that in Unix, it is possible that we see a stale value of cr->dirty (possible if a * concurrent "wcs_wtstart" has reset dirty to 0 but that update did not reach us yet). In this * case the call to "wcs_get_space" below will do the necessary memory barrier instructions * (through calls to "aswp") which will allow us to see the non-stale value of cr->dirty. * * It is also possible that cr->dirty is non-zero but < cr->flushed_dirty_tn. In this case, wcs_get_space * done below will return FALSE forcing a cache-rebuild which will fix this situation. * * In VMS, another process cannot be concurrently resetting cr->dirty to 0 as the resetting routine * is "wcs_wtfini" which is executed in crit which another process cannot be in as we are in crit now. */ if (dont_flush_buff) continue; if (lcnt < pass1) continue; # ifdef DEBUG /* If this cr is a newer twin check that the older twin has a 0 value of "in_cw_set" (bg_update_phase2 * should have ensured this). If this condition is not met, it is possible for * "wcs_get_space/wcs_wtstart_fini/wcs_wtstart/wcs_wtfini" to go into a livelock while trying to * flush the newer twin as that requires the older twin to be flushed and that cannot be cleaned * up (even if the async IO is complete) because of the non-zero "in_cw_set", particularly if * the non-zero value matches "process_id". */ if (cr->twin && cr->bt_index) { assert(TWINNING_ON(csd)); cr_old = (cache_rec_ptr_t)GDS_ANY_REL2ABS(csa, cr->twin); /* get old twin */ assert(!cr_old->bt_index); assert(process_id != cr_old->in_cw_set); } # endif BG_TRACE_PRO(db_csh_getn_flush_dirty); if (FALSE == wcs_get_space(reg, 0, cr)) { /* failed to flush it out - force a rebuild */ BG_TRACE_PRO(wc_blocked_db_csh_getn_wcsstarvewrt); /* only reason we currently know why wcs_get_space could fail */ assert(csa->nl->wc_blocked || gtm_white_box_test_case_enabled); break; } assert(0 == cr->dirty); } /* the cache-record is not free for reuse until the write-latch value becomes LATCH_CLEAR. * This resetting is done by "wcs_wtstart" which is out-of-crit. Therefore, we need to * wait for this value to be LATCH_CLEAR before reusing this cache-record. * Note that we are examining the write-latch-value without holding the interlock. It is ok to do * this because the only two routines that modify the latch value are "bg_update_phase1", "wcs_wtfini" * and "wcs_wtstart". The first two cannot be concurrently executing because we are in crit. * The last one will not update the latch value unless this cache-record is dirty. But in this * case we would have most likely gone through the if (cr->dirty) check above. Most likely * because there is one rare possibility where a concurrent "wcs_wtstart" has set cr->dirty * to 0 but not yet cleared the latch. In that case we wait for the latch to be cleared. * In all other cases, nobody is modifying the latch since when we got crit and therefore * it is safe to observe the value of the latch without holding the interlock. */ if (LATCH_CLEAR != WRITE_LATCH_VAL(cr)) { /* possible if a concurrent "wcs_wtstart" has set cr->dirty to 0 but not yet * cleared the latch. this should be very rare though. */ if (lcnt < pass2) continue; /* try to find some other cache-record to reuse until the 3rd pass */ for (ocnt = 1; (MAXWRTLATCHWAIT >= ocnt) && (LATCH_CLEAR != WRITE_LATCH_VAL(cr)); ocnt++) wcs_sleep(SLEEP_WRTLATCHWAIT); /* since it is a short lock, sleep the minimum */ if (MAXWRTLATCHWAIT <= ocnt) { BG_TRACE_PRO(db_csh_getn_wrt_latch_stuck); assert(FALSE); continue; } } /* Note that before setting up a buffer for the requested block, we should make sure the cache-record's * read_in_progress is set. This is so that no one else in t_qread gets access to this empty buffer. * By setting up a buffer, it is meant assigning cr->blk in addition to inserting the cr in the blkques * through "shuffqth" below. * Note that "t_qread" has special code to handle read_in_progress */ LOCK_BUFF_FOR_READ(cr, rip); /* lock is too long for a general form spin lock but too granular for a mutex */ if (0 != rip) { if (lcnt < pass2) { /* someone is reading into this cache record. leave it for two passes. * this is because if somebody is reading it, it is most likely to be referred to very soon. * if we replace this, we will definitely be causing a restart for the reader. * instead of that, see if some other cache record fits in for us. */ RELEASE_BUFF_READ_LOCK(cr); continue; } for (ocnt = 1; 0 != rip && BUF_OWNER_STUCK >= ocnt; ocnt++) { RELEASE_BUFF_READ_LOCK(cr); /* The owner has been unable to complete the read - check for some things before going to sleep. * Since cr->r_epid can be changing concurrently, take a local copy before using it below, * particularly before calling is_proc_alive as we don't want to call it with a 0 r_epid. */ latest_r_epid = cr->r_epid; if (cr->read_in_progress < -1) { BG_TRACE_PRO(db_csh_getn_out_of_design); /* outside of design; clear to known state */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_INVALIDRIP, 2, DB_LEN_STR(reg)); assert(cr->r_epid == 0); cr->r_epid = 0; INTERLOCK_INIT(cr); } else if (0 != latest_r_epid) { if (is_proc_alive(latest_r_epid, 0)) { # ifdef DEBUG if ((BUF_OWNER_STUCK / 2) == ocnt) GET_C_STACK_FROM_SCRIPT("BUFRDTIMEOUT", process_id, latest_r_epid, ONCE); # endif TRACE_AND_SLEEP(ocnt); } else { cr->r_epid = 0; INTERLOCK_INIT(cr); /* Process gone, release that process's lock */ } } else { TRACE_AND_SLEEP(ocnt); } LOCK_BUFF_FOR_READ(cr, rip); } if ((BUF_OWNER_STUCK < ocnt) && (0 != rip)) { BG_TRACE_PRO(db_csh_getn_buf_owner_stuck); if (0 != latest_r_epid) { assertpro(first_r_epid == latest_r_epid); GET_C_STACK_FROM_SCRIPT("BUFRDTIMEOUT", process_id, latest_r_epid, DEBUG_ONLY(TWICE) PRO_ONLY(ONCE)); RELEASE_BUFF_READ_LOCK(cr); send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_BUFRDTIMEOUT, 6, process_id, cr->blk, cr, first_r_epid, DB_LEN_STR(reg)); continue; } cr->r_epid = 0; INTERLOCK_INIT(cr); LOCK_BUFF_FOR_READ(cr, rip); assert(0 == rip); /* Since holding crit, we expect to get lock */ if (0 != rip) continue; /* We successfully obtained the lock so can fall out of this block */ } } assert(0 == rip); /* no other process "owns" the block */ if (CDB_STAGNATE <= t_tries || mu_reorg_process) { /* this should probably use cr->in_cw_set with a condition handler to cleanup */ CWS_INSERT(block); } assert(LATCH_CLEAR == WRITE_LATCH_VAL(cr)); /* got a block - set it up */ assert(0 == cr->epid); assert(0 == cr->r_epid); assert(NULL == TREF(block_now_locked)); TREF(block_now_locked) = cr; cr->r_epid = process_id; /* establish ownership */ cr->blk = block; /* We want cr->read_in_progress to be locked BEFORE cr->cycle is incremented. t_qread relies on this order. * Enforce this order with a write memory barrier. Not doing so might cause the incremented cr->cycle to be * seen by another process even though it sees the unlocked state of cr->read_in_progress. This could cause * t_qread to incorrectly return with an uptodate cr->cycle even though the buffer is still being read in * from disk and this could cause db integ errors as validation (in t_end/tp_tend which relies on cr->cycle) * will detect no problems even though there is one. Note this memory barrier is still needed even though * there is a memory barrier connotation in the LOCK_BUFF_FOR_READ() macro above. LOCK_BUFF_FOR_READ() does * a read type memory barrier whereas here, we need a write barrier. */ SHM_WRITE_MEMORY_BARRIER; cr->cycle++; cr->jnl_addr = 0; cr->refer = TRUE; if (cr->bt_index != 0) { /* Link between "cr" and "bt" was established at the time this "cr" was dirtied first and continued * to stay even when cr->dirty became 0. But now that this "cr" is going to point to a different block * (i.e. cr->blk is no longer the same as bt->blk) remove the link. */ bt = (bt_rec_ptr_t)GDS_REL2ABS(cr->bt_index); bt->cache_index = CR_NOTVALID; cr->bt_index = 0; } q0 = (cache_rec_ptr_t)((sm_uc_ptr_t)cr + cr->blkque.fl); shuffqth((que_ent_ptr_t)q0, (que_ent_ptr_t)hdr); assert(0 == cr->dirty); if (!pass0cnt) csa->nl->cur_lru_cache_rec_off = GDS_ABS2REL(cr); if (pass0 && pass0cnt) { /* pass0cnt != 0 implies we found a cr within our POOLLIMIT bounds. Assert that and update * our_lru_cache_rec_off in this case. If we fell through to the general pool, then never mind. */ assert(WITHIN_POOLLIMIT_BOUNDS(cr, csa->our_midnite, start_cr, pass0, max_ent)); csa->our_lru_cache_rec_off = GDS_ABS2REL(cr); } if (lcnt > pass1) csa->nl->cache_hits = 0; csa->nl->cache_hits++; if (csa->nl->cache_hits > csd->n_bts) { if (csd->flush_trigger_top != (flsh_trigger = csd->flush_trigger)) /* WARNING assignment */ csd->flush_trigger = MIN(flsh_trigger + MAX(flsh_trigger / STEP_FACTOR, 1), csd->flush_trigger_top); csa->nl->cache_hits = 0; } INCR_DB_CSH_COUNTER(csa, n_db_csh_getn_lcnt, lcnt); ENABLE_INTERRUPTS(INTRPT_IN_DB_CSH_GETN, prev_intrpt_state); return cr; } /* force a recover */ INCR_DB_CSH_COUNTER(csa, n_db_csh_getn_lcnt, lcnt); csa->nl->cur_lru_cache_rec_off = GDS_ABS2REL(cr); ENABLE_INTERRUPTS(INTRPT_IN_DB_CSH_GETN, prev_intrpt_state); return (cache_rec_ptr_t)CR_NOTVALID; } fis-gtm-V7.0-005/sr_port/db_csh_ini.c0000755000032200000250000000145614342376331016301 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" void db_csh_ini(sgmnt_addrs *csa) { assertpro(0 == ((INTPTR_T)csa->hdr & 7)); csa->acc_meth.bg.cache_state = (cache_que_heads_ptr_t)((sm_uc_ptr_t)csa->hdr + csa->nl->cache_off); assert(csa->acc_meth.bg.cache_state); return; } fis-gtm-V7.0-005/sr_port/db_csh_ref.c0000755000032200000250000000740414342376331016275 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "interlock.h" #include "lockconst.h" #include "longset.h" #include "relqop.h" #include "filestruct.h" #include "jnl.h" error_def(ERR_WCFAIL); GBLREF int4 process_id; #ifdef DEBUG GBLREF jnl_gbls_t jgbl; #endif /* Refresh the database cache records in the shared memory. If init = TRUE, do the longset and initialize all the latches and * forward and backward links. If init = FALSE, just increment the cycle and set the blk to CR_BLKEMPTY. */ void db_csh_ref(sgmnt_addrs *csa, boolean_t init) { sgmnt_data_ptr_t csd; node_local_ptr_t cnl; sm_uc_ptr_t bp, bp_top; cache_rec_ptr_t cr, cr_top, cr1; int4 buffer_size, rec_size; csd = csa->hdr; cnl = csa->nl; if (init) { SET_LATCH_GLOBAL(&cnl->wc_var_lock, LOCK_AVAILABLE); SET_LATCH_GLOBAL(&cnl->db_latch, LOCK_AVAILABLE); if (dba_mm == csd->acc_meth) return; longset((uchar_ptr_t)csa->acc_meth.bg.cache_state, SIZEOF(cache_que_heads) + (csd->bt_buckets + csd->n_bts - 1) * SIZEOF(cache_rec), 0); /* -1 since there is a cache_rec in cache_que_heads */ SET_LATCH_GLOBAL(&csa->acc_meth.bg.cache_state->cacheq_active.latch, LOCK_AVAILABLE); SET_LATCH_GLOBAL(&csa->acc_meth.bg.cache_state->cacheq_wip.latch, LOCK_AVAILABLE); } cr = cr1 = csa->acc_meth.bg.cache_state->cache_array; buffer_size = csd->blk_size; assert(buffer_size > 0); assert(0 == buffer_size % DISK_BLOCK_SIZE); rec_size = SIZEOF(cache_rec); for (cr_top = (cache_rec_ptr_t)((sm_uc_ptr_t)cr + rec_size * csd->bt_buckets); cr < cr_top; cr = (cache_rec_ptr_t)((sm_uc_ptr_t)cr + rec_size)) cr->blk = BT_QUEHEAD; cr_top = (cache_rec_ptr_t)((sm_uc_ptr_t)cr + rec_size * csd->n_bts); if (init) { cnl->cur_lru_cache_rec_off = GDS_ANY_ABS2REL(csa, cr); cnl->cache_hits = 0; } bp = (sm_uc_ptr_t)ROUND_UP((sm_ulong_t)cr_top, OS_PAGE_SIZE); bp_top = bp + (gtm_uint64_t)csd->n_bts * buffer_size; /* In case of an encrypted database, bp_top is actually the beginning of the encrypted global buffer array (an array * maintained parallely with the regular unencrypted global buffer array. */ if (USES_ENCRYPTION(csd->is_encrypted)) cnl->encrypt_glo_buff_off = (sm_off_t)((sm_uc_ptr_t)bp_top - (sm_uc_ptr_t)bp); for (; cr < cr_top; cr = (cache_rec_ptr_t)((sm_uc_ptr_t)cr + rec_size), cr1 = (cache_rec_ptr_t)((sm_uc_ptr_t)cr1 + rec_size)) { if (init) INTERLOCK_INIT(cr); cr->cycle++; /* increment cycle whenever buffer's blk number changes (for tp_hist) */ cr->blk = CR_BLKEMPTY; /* Typically db_csh_ref is invoked from db_init (when creating shared memory afresh) in which case cr->bt_index * will be 0 (due to the memset). But, if db_csh_ref is invoked from online rollback where we are invalidating the * cache but do not do the longset (above), bt_index can be a non-zero value. Assert that and set it to zero since * we are invalidating the cache record anyways. */ assert((0 == cr->bt_index) || jgbl.onlnrlbk); cr->bt_index = 0; if (init) { assert(bp <= bp_top); cr->buffaddr = GDS_ANY_ABS2REL(csa, bp); bp += buffer_size; insqt((que_ent_ptr_t)cr, (que_ent_ptr_t)cr1); } cr->refer = FALSE; } cnl->wc_in_free = csd->n_bts; return; } fis-gtm-V7.0-005/sr_port/db_header_conversion.c0000755000032200000250000001334514342376331020362 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2020-2022 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. * * * ****************************************************************/ /* These functions will translate a v6 header to v7 and back again */ #include "mdef.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "v6_gdsfhead.h" #include "db_header_conversion.h" /* Convert the header from v6 to v7 format */ void db_header_upconv(sgmnt_data_ptr_t v7) { v6_sgmnt_data_ptr_t v6 = (v6_sgmnt_data_ptr_t)v7; assert(SIZEOF(v6_sgmnt_data) == SIZEOF(sgmnt_data)); v7->master_map_len = v6->master_map_len; v7->start_vbn = v6->start_vbn; v7->last_inc_bkup_last_blk = v6->last_inc_bkup_last_blk; v7->last_com_bkup_last_blk = v6->last_com_bkup_last_blk; v7->last_rec_bkup_last_blk = v6->last_rec_bkup_last_blk; v7->reorg_restart_block = v6->reorg_restart_block; v7->reorg_upgrd_dwngrd_restart_block = v6->reorg_upgrd_dwngrd_restart_block; v7->blks_to_upgrd = v6->blks_to_upgrd; v7->blks_to_upgrd_subzero_error = v6->blks_to_upgrd_subzero_error; v7->before_trunc_total_blks = v6->before_trunc_total_blks; v7->after_trunc_total_blks = v6->after_trunc_total_blks; v7->before_trunc_free_blocks = v6->before_trunc_free_blocks; v7->encryption_hash_cutoff = v6->encryption_hash_cutoff; v7->trans_hist.curr_tn = v6->trans_hist.curr_tn; v7->trans_hist.early_tn = v6->trans_hist.early_tn; v7->trans_hist.last_mm_sync = v6->trans_hist.last_mm_sync; v7->trans_hist.mm_tn = v6->trans_hist.mm_tn; v7->trans_hist.lock_sequence = v6->trans_hist.lock_sequence; v7->trans_hist.ccp_jnl_filesize = v6->trans_hist.ccp_jnl_filesize; v7->trans_hist.total_blks = v6->trans_hist.total_blks; v7->trans_hist.free_blocks = v6->trans_hist.free_blocks; v7->last_start_backup = v6->last_start_backup; v7->problksplit = v6->problksplit; } /* Convert the header from v7 to v6 format */ void db_header_dwnconv(sgmnt_data_ptr_t v7) { v6_sgmnt_data_ptr_t v6 = (v6_sgmnt_data_ptr_t)v7; DEBUG_ONLY(block_id dbg_blkid); DEBUG_ONLY(gtm_int8 dbg_int8); assert(SIZEOF(v6_sgmnt_data) == SIZEOF(sgmnt_data)); DEBUG_ONLY(dbg_int8 = v7->master_map_len); assert((int4)(v7->master_map_len) == v7->master_map_len); v6->master_map_len = v7->master_map_len; DEBUG_ONLY(dbg_blkid = v7->start_vbn); assert((block_id_32)(v7->start_vbn) == v7->start_vbn); v6->start_vbn = v7->start_vbn; DEBUG_ONLY(dbg_blkid = v7->last_inc_bkup_last_blk); assert((block_id_32)(v7->last_inc_bkup_last_blk) == v7->last_inc_bkup_last_blk); v6->last_inc_bkup_last_blk = v7->last_inc_bkup_last_blk; DEBUG_ONLY(dbg_blkid = v7->last_com_bkup_last_blk); assert((block_id_32)(v7->last_com_bkup_last_blk) == v7->last_com_bkup_last_blk); v6->last_com_bkup_last_blk = v7->last_com_bkup_last_blk; DEBUG_ONLY(dbg_blkid = v7->last_rec_bkup_last_blk); assert((block_id_32)(v7->last_rec_bkup_last_blk) == v7->last_rec_bkup_last_blk); v6->last_rec_bkup_last_blk = v7->last_rec_bkup_last_blk; DEBUG_ONLY(dbg_blkid = v7->reorg_restart_block); assert((block_id_32)(v7->reorg_restart_block) == v7->reorg_restart_block); v6->reorg_restart_block = v7->reorg_restart_block; DEBUG_ONLY(dbg_blkid = v7->reorg_upgrd_dwngrd_restart_block); assert((block_id_32)(v7->reorg_upgrd_dwngrd_restart_block) == v7->reorg_upgrd_dwngrd_restart_block); v6->reorg_upgrd_dwngrd_restart_block = v7->reorg_upgrd_dwngrd_restart_block; DEBUG_ONLY(dbg_blkid = v7->blks_to_upgrd); assert((block_id_32)(v7->blks_to_upgrd) == v7->blks_to_upgrd); v6->blks_to_upgrd = v7->blks_to_upgrd; DEBUG_ONLY(dbg_blkid = v7->blks_to_upgrd_subzero_error); assert((block_id_32)(v7->blks_to_upgrd_subzero_error) == v7->blks_to_upgrd_subzero_error); v6->blks_to_upgrd_subzero_error = v7->blks_to_upgrd_subzero_error; DEBUG_ONLY(dbg_blkid = v7->before_trunc_total_blks); assert((block_id_32)(v7->before_trunc_total_blks) == v7->before_trunc_total_blks); v6->before_trunc_total_blks = v7->before_trunc_total_blks; DEBUG_ONLY(dbg_blkid = v7->after_trunc_total_blks); assert((block_id_32)(v7->after_trunc_total_blks) == v7->after_trunc_total_blks); v6->after_trunc_total_blks = v7->after_trunc_total_blks; DEBUG_ONLY(dbg_blkid = v7->before_trunc_free_blocks); assert((block_id_32)(v7->before_trunc_free_blocks) == v7->before_trunc_free_blocks); v6->before_trunc_free_blocks = v7->before_trunc_free_blocks; DEBUG_ONLY(dbg_blkid = v7->encryption_hash_cutoff); assert((block_id_32)(v7->encryption_hash_cutoff) == v7->encryption_hash_cutoff); v6->encryption_hash_cutoff = v7->encryption_hash_cutoff; v6->trans_hist.curr_tn = v7->trans_hist.curr_tn; v6->trans_hist.early_tn = v7->trans_hist.early_tn; v6->trans_hist.last_mm_sync = v7->trans_hist.last_mm_sync; v6->trans_hist.mm_tn = v7->trans_hist.mm_tn; v6->trans_hist.lock_sequence = v7->trans_hist.lock_sequence; v6->trans_hist.ccp_jnl_filesize = v7->trans_hist.ccp_jnl_filesize; DEBUG_ONLY(dbg_blkid = v7->trans_hist.total_blks); assert((block_id_32)(v7->trans_hist.total_blks) == v7->trans_hist.total_blks); v6->trans_hist.total_blks = v7->trans_hist.total_blks; DEBUG_ONLY(dbg_blkid = v7->trans_hist.free_blocks); assert((block_id_32)(v7->trans_hist.free_blocks) == v7->trans_hist.free_blocks); v6->trans_hist.free_blocks = v7->trans_hist.free_blocks; v6->offset = v7->offset; v6->max_rec = v7->max_rec; v6->i_reserved_bytes = v7->i_reserved_bytes; v6->last_start_backup = v7->last_start_backup; v6->problksplit = v7->problksplit; v6->db_got_to_V7_once = FALSE; } fis-gtm-V7.0-005/sr_port/db_header_conversion.h0000644000032200000250000000133414342376331020357 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2020 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. * * * ****************************************************************/ #ifndef DB_HEADER_CONVERSION_DEFINED #define DB_HEADER_CONVERSION_DEFINED #include "v6_gdsfhead.h" #include "gdsfhead.h" void db_header_upconv(sgmnt_data_ptr_t v7); void db_header_dwnconv(sgmnt_data_ptr_t v6); #endif fis-gtm-V7.0-005/sr_port/dbfilop.h0000755000032200000250000000106114342376331015634 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef DBFILOP_INCLUDED #define DBFILOP_INCLUDED uint4 dbfilop(file_control *fc); #endif /* DBFILOP_INCLUDED */ fis-gtm-V7.0-005/sr_port/ddphdr.h0000755000032200000250000002134714342376331015473 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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. * * * ****************************************************************/ #ifndef DDPHDR_H_INCLUDED #define DDPHDR_H_INCLUDED typedef int4 condition_code; #define MAX_ETHER_DATA_SIZE 1500 #define ETHERNET_HEADER_SIZE 14 /* Is size of Destination address + source address + protocol # */ #define ETHERADDR_LENGTH 6 #define MIN_ETH_RECV_BUFCNT 1 /* from OpenVMS I/O User's Reference Manual, system default */ #define MAX_ETH_RECV_BUFCNT 255 /* from OpenVMS I/O User's Reference Manual */ #define ETH_RCV_BUFCNT 64 /* GT.CM DDP default */ #define DDP_ETH_PROTO_TYPE ((unsigned short)0x3980) /* will be seen as 8039 on the wire */ #define DDP_MIN_MSG_LEN 64 #define DDP_PROTO_VERSION 0x02 /* MSM, DSM-11 use version 2; DSM 6.0 & 6.1, DDP-DOS V1.00.8+ use version 4 */ #define DDP_MSG_TERMINATOR 0xff #define DDP_CIRCUIT_NAME_LEN 3 #define DDP_VOLUME_NAME_LEN 3 #define DDP_UCI_NAME_LEN 3 /* Request codes */ #define DDPTR_USEREXIT 0x00 /* [00] */ /* Individual user sends message to agent to clean up for exit. Per DSM doc, * this code is for an ERROR ON RRB (read request buffer) */ #define DDPTR_LOCK 0x02 /* [02] */ /* Not implemented */ #define DDPTR_UNLOCK 0x04 /* [04] */ /* Not implemented */ #define DDPTR_ZALLOC 0x06 /* [06] */ #define DDPTR_ZDEALLOC 0x08 /* [08] */ #define DDPTR_GET 0x0a /* [10] */ #define DDPTR_PUT 0x0c /* [12] */ #define DDPTR_KILL 0x0e /* [14] */ #define DDPTR_ORDER 0x10 /* [16] */ #define DDPTR_QUERY 0x12 /* [18] */ #define DDPTR_DEFINE 0x14 /* [20] */ #define DDPTR_PREVIOUS 0x16 /* [22] */ #define DDPTR_JOB 0x18 /* [24] */ /* Not implemented */ #define DDPTR_RESPONSE 0x1a /* [26] */ #define DDPTR_ERRESPONSE 0x1c /* [28] */ #define DDPTR_RDRQBUF 0x1e /* [30] */ /* READ REQUEST BUFFER, not implemented */ #define DDPTR_ANNOUNCE 0x20 /* [32] */ /* Response codes other than DDPTR_RESPONSE and DDPTR_ERRESPONSE */ #define DDPTRX_ANNOUNCE 0x80 /* [128] */ /* Announcement that we are connecting */ #define DDPTRX_NOCONNECT 0x81 /* [129] */ /* Named volume set not connected */ #define DDPTRX_CONGESTION 0x82 /* [130] */ /* Agent congestion */ #define DDPTRX_RUNDOWN 0x83 /* [131] */ /* Client wishes to disconnect */ #define DDPTRX_SHUTDOWN 0x84 /* [132] */ /* Shutdown the server */ #define DDPTRX_DUMP 0x85 /* [133] */ /* Start trace */ /* GT.M extended reference form is ^|"globaldirectory"|global, DSM is ^["UCI","VOL"]global */ #define GTM_EXTREF_PREFIX "^|\"" #define GTM_EXTREF_SUFFIX "\"|" #define DSM_EXTREF_PREFIX "^[\"" #define DSM_UCI_VOL_SEPARATOR "\",\"" #define DSM_EXTREF_SUFFIX "\"]" #define DSM_EXTREF_FORM_LEN (STR_LIT_LEN(DSM_EXTREF_PREFIX) + \ DDP_UCI_NAME_LEN + \ STR_LIT_LEN(DSM_UCI_VOL_SEPARATOR) + \ DDP_VOLUME_NAME_LEN + \ STR_LIT_LEN(DSM_EXTREF_SUFFIX)) #define DDP_VOLSET_CONF_LOGICAL_PREFIX "GTMDDP_VOLCONF_" #define DDP_GROUP_LOGICAL_PREFIX "GTMDDP_GROUPS_" #define DDP_ETHER_DEV_PREFIX "GTMDDP_CONTROLLER_" #define DDP_MAXRECSIZE_PREFIX "GTMDDP_MAXRECSIZE_" #define DDP_ETH_RCV_BUFCNT_PREFIX "GTMDDP_ETHRCVBUFCNT_" #define DDP_MAXREQCREDITS_PREFIX "GTMDDP_MAXREQCREDITS_" #define DDP_CLIENT_CKTNAM_LOGI "GTMDDP_CIRCUIT_NAME" #define DDP_MAX_VOLSETS 16 #define DDP_TEXT_ID_LENGTH 3 #define DDP_ANNOUNCE_CODE_LEN 2 /* "WI" or "II" */ #define MAXIMUM_PROCESSES 256 #define DDP_MAX_GROUP 16 #define DDP_DEFAULT_GROUP_MASK 0x0001 /* member of group 0 */ #define DDP_MIN_RECSIZE 1024 #define DDP_LEAST_MAXREQCREDITS 1 #define DDP_LARGEST_MAXREQCREDITS 0xFF /* some constants that were derived from DSM packets */ #define DDP_GROUP_MASK 0x0101 #define DDP_ADVERTISE_INTERVAL 0x00 #define DDP_MAX_REQUEST_CREDITS 0x04 #define DDP_CPU_TYPE 0x01 #define DDP_SOFTWARE_VERSION 0x00 #define DDP_CPU_LOAD_RATING 0x00 #define DDP_AUTOCONFIGURE_VERSION 0x02 #define DDP_ANNOUNCE_FILLER3_LITERAL 0x01ff00ff #define DDP_GLOBAL_TYPE 0x02 /* DDP node status bit masks */ #define DDP_NODE_STATUS_READ 0x01 /* bit is 1 if read locked, 0 if read enabled */ #define DDP_NODE_STATUS_WRITE 0x02 /* bit is 1 if write locked, 0 if write enabled */ #define DDP_NODE_STATUS_STATE 0x04 /* bit is 1 if unreachable, 0 if reachable */ #define DDP_NODE_STATUS_CHANGE 0x10 /* bit is 1 if state change occurred on circuit */ #define DDP_NODE_STATUS_DISABLED 0x20 /* bit is 1 if disabled, 0 if enabled */ #define DDP_NODE_STATUS_ALL_CLEAR 0x00 /* read enabled + write enabled + reachable + no state change + circuit enabled */ #define DDP_LOG_ERROR(err_len, err_string) \ { \ char time_str[CTIME_BEFORE_NL + 2]; /* for GET_CUR_TIME macro*/ \ bool save_dec_nofac; \ \ GET_CUR_TIME(time_str); \ save_dec_nofac = dec_nofac; /* save for later restore */ \ dec_nofac = TRUE; /* don't need error mnemonic prefix, just print the message contents */ \ dec_err(VARLSTCNT(6) ERR_DDPLOGERR, 4, CTIME_BEFORE_NL, &time_str[0], (err_len), (err_string)); \ dec_nofac = save_dec_nofac; /* back to what it was */ \ } /* All structures that are DDP messages should be packed; do not let the compiler pad for structure member alignment */ #if defined(__alpha) # pragma member_alignment save # pragma nomember_alignment #endif typedef struct { unsigned short uci; unsigned short volset; } ddp_info; typedef struct { unsigned char trancode; unsigned char proto; unsigned short source_circuit_name; /* 5-bit format */ unsigned short source_job_number; unsigned short remote_circuit_name; /* 5-bit format */ unsigned short remote_job_number; /* 0000 if this is a request */ unsigned char message_number; unsigned char filler1; /* literal 00 */ unsigned short message_length; unsigned char hdrlen; unsigned char txt[1]; } ddp_hdr_t; #define DDP_MSG_HDRLEN 0x0f /* excluding txt field of ddp_hdr_t */ struct frame_hdr { unsigned short frame_length; unsigned char destination_address[ETHERADDR_LENGTH]; unsigned char source_address[ETHERADDR_LENGTH]; unsigned char protocol_type[2]; }; struct in_buffer_struct { struct frame_hdr fh; ddp_hdr_t dh; }; typedef struct { /* byte position in announce packet - 15 is the first position past the header, position starts from 0, header size is 15 bytes */ unsigned short filler0; /* position 15: literal 0x0000 */ unsigned char code[DDP_ANNOUNCE_CODE_LEN]; /* position 17: "WI", or "II" */ unsigned char ether_addr[ETHERADDR_LENGTH]; /* position 19: Ethernet physical address for this node */ unsigned short circuit_name; /* position 25: DDP Node (circuit) name */ unsigned short filler1; /* position 27: reserved for possible name extension */ unsigned short filler2; /* position 29: reserved for possible name extension */ unsigned short max_job_no; /* position 31: max job # */ unsigned short group_mask; /* position 33: DDP group number mask */ unsigned char advertise_interval; /* position 35: Advertise interval in seconds */ unsigned char max_request_credits; /* position 36: Maximum request credits */ unsigned char cpu_type; /* position 37: Remote CPU type */ unsigned char version; /* position 38: Version of software */ unsigned char cpu_load_rating; /* position 39: CPU load rating */ unsigned char proto_version; /* position 40: DDP protocol version in use */ unsigned char node_status; /* position 41: see comments re: DDP_NODE_STATUS_* */ unsigned char autoconfigure_version; /* position 42: DDP autoconfigure version */ unsigned short volset[DDP_MAX_VOLSETS]; /* position 43: 5 bit formal volset names */ /******************************************* begin unknown **************************************************/ unsigned char filler3[32 + 4 + 8]; /* position 75: at position 107 we write literal 0x01ff00ff */ /******************************************* end unknown **************************************************/ unsigned char terminator; /* position 119: DDP_MSG_TERMINATOR */ } ddp_announce_msg_t; #define DDP_ANNOUNCE_MSG_LEN 105 /* from ddp_announce_msg_t */ typedef struct { unsigned char naked_size; unsigned short uci; unsigned short vol; unsigned char global_type; unsigned char global_len; unsigned char global[1]; /* actually, global_len bytes of formatted global reference */ } ddp_global_request_t; /* immediately follows the message header (ddp_hdr) for global request. For SET, ASCII value follows */ #if defined(__alpha) # pragma member_alignment restore #endif #endif /* DDPHDR_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/defer_error_set.c0000755000032200000250000000206514342376331017366 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "xfer_enum.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "deferred_events.h" #include "op.h" #include "fix_xfer_entry.h" /* ------------------------------------------------------------------------ * Set flags and transfer table for synchronous handling of TTY write error * Should be called only from xfer_set_handlers. * ------------------------------------------------------------------------ */ GBLREF xfer_entry_t xfer_table[]; void defer_error_set(int4 error_status) { DEFER_INTO_XFER_TAB; } fis-gtm-V7.0-005/sr_port/deferred_events.c0000644000032200000250000004760114342376333017365 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_stdio.h" #include "io.h" #include #include "xfer_enum.h" #include "tp_timeout.h" #include "deferred_events.h" #include "interlock.h" #include "lockconst.h" #include "add_inter.h" #include "op.h" #include "fix_xfer_entry.h" #include "deferred_events_queue.h" #include "have_crit.h" #include "stack_frame.h" #include "error.h" #include "ztimeout_routines.h" /* ============================================================================= * EXTERNAL VARIABLES * ============================================================================= */ GBLREF boolean_t is_tracing_on; /* M profiling */ GBLREF boolean_t tp_timeout_set_xfer; GBLREF intrpt_state_t intrpt_ok_state; GBLREF io_pair io_std_device; GBLREF size_t gtmMallocErrorSize; /* Size of malloc limit violation */ GBLREF stack_frame *frame_pointer; GBLREF unsigned char *gtmMallocErrorCallerid; /* Callerid of malloc limit violation */ GBLREF unsigned char *restart_ctxt, *restart_pc; GBLREF void (*tp_timeout_action_ptr)(void); GBLREF volatile boolean_t dollar_zininterrupt; GBLREF volatile int4 fast_lock_count, outofband; /* fast_lock_count protects some non-reentrant code */ GBLREF xfer_entry_t xfer_table[]; /* transfer table */ error_def(ERR_CTRAP); error_def(ERR_CTRLC); error_def(ERR_MALLOCCRIT); error_def(ERR_TERMHANGUP); error_def(ERR_TERMWRITE); error_def(ERR_JOBINTRRQST); /* ---------------------------------------------------------------------------- * Establish only first received; queue others; discard multiples of the same * ---------------------------------------------------------------------------- */ /* ============================================================================= * EXPORTED FUNCTIONS * ============================================================================= */ /* ------------------------------------------------------------------ * *** INTERRUPT HANDLER *** * Sets up transfer table changes needed for: * - Synchronous handling of asynchronous events. * - Single-stepping and breakpoints * Parameters: * - event_type specifies the event being deferred * - param_val to store for in the event's array element for use by the corresponding event handler function when it gets to run * - popped_entry to indicate whether the even has just been popped from the event queue * * - the return value is TRUE if the event has been successfully set up, including if it is redundant, i.e. already set up * ------------------------------------------------------------------ */ boolean_t xfer_set_handlers(int4 event_type, int4 param_val, boolean_t popped_entry) { /* Keep track of what event types have come in and deal with them appropriately */ boolean_t already_ev_handling; int4 e_type, pv; intrpt_state_t prev_intrpt_state; save_xfer_entry *entry; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assertpro(DEFERRED_EVENTS > event_type); entry = &TAREF1(save_xfer_root, event_type); entry->param_val = param_val; if ((INTRPT_IN_EVENT_HANDLING == intrpt_ok_state) && !popped_entry && (no_event != event_type)) { /* events already in flux - stash this "as is" in the record for this event */ if (not_in_play == entry->event_state) entry->event_state = signaled; DBGDFRDEVNT((stderr, "%d %s: xfer_set_handlers - stashed: %d with state: %d\n", __LINE__, __FILE__, e_type, TAREF1(save_xfer_root, e_type).event_state)); return TRUE; /* return value currently only used by tp_timeout.c */ } /* WARNING! AIO sets multi_thread_in_use which disables DEFER_INTERRUPTS, treat it like an active event */ if (!(already_ev_handling = ((INTRPT_IN_EVENT_HANDLING == intrpt_ok_state) || multi_thread_in_use))) DEFER_INTERRUPTS(INTRPT_IN_EVENT_HANDLING, prev_intrpt_state); /* ensure ownership of the event mechanism */ if (dollar_zininterrupt && (jobinterrupt == event_type)) { /* ignore jobinterrupt flooding; expect too many to report with DBGDFRDEVNT; might also gather a count */ if (!already_ev_handling) ENABLE_EVENT_INTERRUPTS(prev_intrpt_state); return TRUE; } if (!already_ev_handling && (queued == entry->event_state)) { /* Fast path, if not already handling as queued */ if (!popped_entry && (no_event == outofband)) { /* this event is at the head of the queue so pop it here */ POP_XFER_QUEUE_ENTRY(&e_type, &pv); DBGDFRDEVNT((stderr, "%d %s: xfer_set_handlers - popped: %d with state: %d\n", __LINE__, __FILE__, e_type, TAREF1(save_xfer_root, e_type).event_state)); DEBUG_ONLY(popped_entry = TRUE); if ((no_event != e_type) && (e_type != event_type)) { if (not_in_play != TAREF1(save_xfer_root, e_type).event_state) { /* give previously enqueued event priority */ assert(queued == TAREF1(save_xfer_root, event_type).event_state); event_type = e_type; } else assert(FALSE); /* the dequeued item should be active, but is not!!! */ DBGDFRDEVNT((stderr, "%d %s: xfer_set_handlers - popped event_type = %d\n", __LINE__, __FILE__, event_type)); } } } if (!already_ev_handling) { assert(no_event == outofband || (event_type == outofband)); assert(!dollar_zininterrupt || (jobinterrupt != event_type)); if (entry != (TREF(save_xfer_root_ptr))->ev_que.fl) { /* no event in play so pend this one by jiggeriing the xfer_table */ entry->event_state = pending; outofband = event_type; entry->set_fn(param_val); DBGDFRDEVNT((stderr, "%d %s: xfer_set_handlers - xfer_table active for event type %d\n", __LINE__, __FILE__, event_type)); } else DBGDFRDEVNT((stderr, "%d %s: xfer_set_handlers - skipping [re]queued event type %d\n", __LINE__, __FILE__, event_type)); if (!already_ev_handling) ENABLE_EVENT_INTERRUPTS(prev_intrpt_state); return TRUE; } if (not_in_play != entry->event_state) { /* each event only gets one chance at a time and this one is active, so discard duplicate */ DBGDFRDEVNT((stderr, "%d %s: xfer_set_handlers - already in process event: %d with state: %d\n", __LINE__, __FILE__,event_type, entry->event_state)); if (!already_ev_handling) ENABLE_EVENT_INTERRUPTS(prev_intrpt_state); return TRUE; } if (!already_ev_handling && (no_event == outofband) && (!have_crit(CRIT_HAVE_ANY_REG | CRIT_IN_COMMIT))) { /* no competion or blocking interrupt: collect $200 and go straight to pending */ /* ------------------------------------------------------- * If table changed, it was not synchronized. * (Assumes these entries are all that would be changed) * -------------------------------------------------------- */ assert((xfer_table[xf_linefetch] == op_linefetch) || (xfer_table[xf_linefetch] == op_zstepfetch) || (xfer_table[xf_linefetch] == op_zst_fet_over) || (xfer_table[xf_linefetch] == op_mproflinefetch)); assert((xfer_table[xf_linestart] == op_linestart) || (xfer_table[xf_linestart] == op_zstepstart) || (xfer_table[xf_linestart] == op_zst_st_over) || (xfer_table[xf_linestart] == op_mproflinestart)); assert((xfer_table[xf_zbfetch] == op_zbfetch) || (xfer_table[xf_zbfetch] == op_zstzb_fet_over) || (xfer_table[xf_zbfetch] == op_zstzbfetch)); assert((xfer_table[xf_zbstart] == op_zbstart) || (xfer_table[xf_zbstart] == op_zstzb_st_over) || (xfer_table[xf_zbstart] == op_zstzbstart)); assert((xfer_table[xf_forchk1] == op_forchk1) || (xfer_table[xf_forchk1] == op_mprofforchk1)); assert((xfer_table[xf_forloop] == op_forloop)); assert(xfer_table[xf_ret] == opp_ret || xfer_table[xf_ret] == opp_zst_over_ret || xfer_table[xf_ret] == opp_zstepret); assert(xfer_table[xf_retarg] == op_retarg || xfer_table[xf_retarg] == opp_zst_over_retarg || xfer_table[xf_retarg] == opp_zstepretarg); /* ----------------------------------------------- * Now call the specified set function to swap in * the desired handlers (and set flags or whatever). * ----------------------------------------------- */ assert(entry != (TREF(save_xfer_root_ptr))->ev_que.fl); assert(!dollar_zininterrupt || (jobinterrupt != event_type)); entry->event_state = pending; /* jiggering the transfer table for this event */ outofband = event_type; entry->set_fn(param_val); DBGDFRDEVNT((stderr, "%d %s: xfer_set_handlers - set xfer_table for event type %d\n" ,__LINE__, __FILE__, event_type)); } else if (queued != entry->event_state) { /* queue it */ entry->event_state = queued; SAVE_XFER_QUEUE_ENTRY(event_type, param_val); if (outofband == event_type) outofband = no_event; DBGDFRDEVNT((stderr, "%d %s: xfer_set_handlers: event %d queued %s %d\n",__LINE__, __FILE__, event_type, ((jobinterrupt == event_type) ? "ahead of" : "behind"), outofband)); } if (!already_ev_handling) ENABLE_EVENT_INTERRUPTS(prev_intrpt_state); return (no_event != outofband); } boolean_t xfer_reset_if_setter(int4 event_type) { /* if the transfer table has been changed to activate event_type, return it to "normal," otherwise leve it alone */ boolean_t already_ev_handling, res; int4 dummy; intrpt_state_t prev_intrpt_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* WARNING! AIO sets multi_thread_in_use which disables DEFER_INTERRUPTS, treat it like an active event */ if (!(already_ev_handling = ((INTRPT_IN_EVENT_HANDLING == intrpt_ok_state) || multi_thread_in_use))) DEFER_INTERRUPTS(INTRPT_IN_EVENT_HANDLING, prev_intrpt_state); switch (outofband) { case no_event: /* no outofband to match up with */ switch (event_type) { case jobinterrupt: res = dollar_zininterrupt; /* might sh/could be an assert & fall through */ break; case ctrlc: case sighup: case tptimeout: /* the caller sets event_state to active */ case ztimeout: res = TRUE; break; default: res = FALSE; } default: res = (event_type == outofband); } if (res) { /* it's worth a try */ DBGDFRDEVNT((stderr, "%d %s: xfer_reset_if_setter: event_type %d is first\n", __LINE__, __FILE__, event_type)); if (res = (active == TAREF1(save_xfer_root, event_type).event_state)) /* WARNING: assignment */ res = (real_xfer_reset(event_type)); DBGDFRDEVNT((stderr, "%d %s: xfer_reset_if_setter: xfer_reset_handlers returned %d\n", __LINE__, __FILE__, res)); if (res && (not_in_play != TAREF1(save_xfer_root, tptimeout).event_state)) { /* give tptimeout some priority */ REMOVE_XFER_QUEUE_ENTRY(tptimeout); /* don't leave it in the queue */ if (pending != TAREF1(save_xfer_root, tptimeout).event_state) xfer_set_handlers(tptimeout, dummy, TRUE); /* param_val not used, hence dummy */ } } else DBGDFRDEVNT((stderr, "%d %s: xfer_reset_if_setter: event_type %d is but waiting event is %d\n", __LINE__, __FILE__, event_type, outofband)); if (!already_ev_handling) ENABLE_EVENT_INTERRUPTS(prev_intrpt_state); return res; } /* ------------------------------------------------------------------ * Reset transfer table to normal settings. * * - Intent: Put back all state that was or could have been changed * due to prior deferral(s). * - Might be preferable to implement this assumption if this routine * were changed to delegate responsibility as does the * corresponding set routine. * - If M profiling is active, some entries should be set to the * op_mprof* routines. * - Return value indicates whether reset type matches set type. * If it does not, this indicates an "abnormal" path. * - Should still reset the table in this case. * ------------------------------------------------------------------ */ boolean_t real_xfer_reset(int4 event_type) { boolean_t already_ev_handling, cur_outofband; int e_type; int4 param_val, status; intrpt_state_t prev_intrpt_state; save_xfer_entry *entry; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* ------------------------------------------------------------------ * Note: If reset routine can preempt path from handler to * set routine (e.g. clearing event before acting on it), * these assertions can fail. * Should not happen in current design. * ------------------------------------------------------------------ */ /* WARNING! AIO sets multi_thread_in_use which disables DEFER_INTERRUPTS, treat it like an active event */ if (!(already_ev_handling = ((INTRPT_IN_EVENT_HANDLING == intrpt_ok_state) || multi_thread_in_use))) DEFER_INTERRUPTS(INTRPT_IN_EVENT_HANDLING, prev_intrpt_state); assert(no_event != event_type); DBGDFRDEVNT((stderr, "%d %s: real_xfer_reset - event: %d\n", __LINE__, __FILE__, event_type)); assertpro(DEFERRED_EVENTS > event_type); assert(pending <= TAREF1(save_xfer_root, event_type).event_state); if ((pending == TAREF1(save_xfer_root, zstep_pending).event_state) && (TREF(zstep_action)).str.len) { (TREF(zstep_action)).mvtype = MV_STR; DEBUG_ONLY(cur_outofband = outofband); op_zstep(TAREF1(save_xfer_root, zstep_pending).param_val, &(TREF(zstep_action))); /* reinstate ZSTEP */ assert(outofband == cur_outofband); FIX_XFER_ENTRY(xf_forchk1, op_forchk1); /* zstep does not mess with or use xf_forchk1 */ } else { DEFER_OUT_OF_XFER_TAB(is_tracing_on); FIX_XFER_ENTRY(xf_ret, opp_ret); FIX_XFER_ENTRY(xf_retarg, op_retarg); } FIX_XFER_ENTRY(xf_forloop, op_forloop); DBGDFRDEVNT((stderr, "%d %s: real_xfer_reset cleared event_type: %d from event_state pending to %d\n", __LINE__, __FILE__, event_type, TAREF1(save_xfer_root, event_type).event_state)); REMOVE_XFER_QUEUE_ENTRY(event_type); outofband = no_event; if (!already_ev_handling) ENABLE_EVENT_INTERRUPTS(prev_intrpt_state); /* ------------------------------------------------------------------------- * Reset external event modules that need it. * (Should do this in a more modular fashion.) * None * ------------------------------------------------------------------------- */ return TRUE; } /* ------------------------------------------------------------------ * Perform action corresponding to the first async event that * was logged. * ------------------------------------------------------------------ */ /* This function can be invoked by op_*intrrpt* transfer table functions or by long-running functions that check for pending events. * The transfer table adjustments should be active only for a short duration between the occurrence of an outofband event * and the handling of it at a logical boundary where we have a captured mpc to allow and appropriate return to normal execution. * We don't expect to be running with those transfer table adjustmentss for more than one M-line. If "outofband" is set to 0, a * call to async_action below will do nothing and we will end up running with the op_*intrrpt* transfer table functions * indefinitely. In this case M-FOR loops are known to return incorrect results which might lead to application integrity issues. * It is therefore safer to assertpro, as we will at least have the core for analysis. */ void async_action(bool lnfetch_or_start) { boolean_t ours; intrpt_state_t prev_intrpt_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(INTRPT_IN_EVENT_HANDLING != intrpt_ok_state); DEFER_INTERRUPTS(INTRPT_IN_EVENT_HANDLING, prev_intrpt_state); if (jobinterrupt == outofband) { if (dollar_zininterrupt) { /* This moderately desparate hack deals with interrupt flooding creeping through little state windows */ assert(active == TAREF1(save_xfer_root, outofband).event_state); real_xfer_reset(jobinterrupt); DEBUG_ONLY(gtm_fork_n_core()); MUM_TSTART; } TAREF1(save_xfer_root, jobinterrupt).event_state = pending; /* jobinterrupt gets a pass from the assert below */ } else if (!lnfetch_or_start) { /* something other than a new line caugth this, so */ assert(pending >= TAREF1(save_xfer_root, outofband).event_state); TAREF1(save_xfer_root, outofband).event_state = pending; /* make it pending in case it was not there yet */ } DBGDFRDEVNT((stderr, "%d %s: async_action - pending event: %d active\n", __LINE__, __FILE__, outofband)); assertpro(DEFERRED_EVENTS > outofband); if (lnfetch_or_start) { frame_pointer->restart_pc = frame_pointer->mpc; frame_pointer->restart_ctxt = frame_pointer->ctxt; } ENABLE_EVENT_INTERRUPTS(prev_intrpt_state); /* opening a window of race */ if ((io_std_device.in->type == tt) && (no_event == TAREF1(save_xfer_root, defer_error).event_state)) iott_flush(io_std_device.in); switch (outofband) { case jobinterrupt: dollar_zininterrupt = TRUE; /* do this at every point to minimize nesting */ TAREF1(save_xfer_root, outofband).event_state = active; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_JOBINTRRQST); break; case ctrlc: /* these go from pending to active here */ TAREF1(save_xfer_root, outofband).event_state = active; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_CTRLC); break; case ctrap: TAREF1(save_xfer_root, outofband).event_state = active; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_CTRAP, 1, TAREF1(save_xfer_root, ctrap).param_val); break; case sighup: TAREF1(save_xfer_root, sighup).event_state = pending; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_TERMHANGUP); break; case (defer_error): TAREF1(save_xfer_root, outofband).event_state = active; ours = xfer_reset_if_setter(defer_error); assert(ours); if ((ERR_MALLOCCRIT == TAREF1(save_xfer_root, defer_error).param_val)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_MALLOCCRIT, 2, gtmMallocErrorSize, gtmMallocErrorCallerid); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_TERMWRITE, 0, TAREF1(save_xfer_root, defer_error).param_val); break; case tptimeout: /* these have their own action routines that do pending -> active */ (*tp_timeout_action_ptr)(); break; case ztimeout: ztimeout_action(); break; case (neterr_action): /* netrror_action currently set in assembly routines placed by mdb_condition_handler */ case (zstep_pending): case (zbreak_pending): assert(FALSE); /* ZStep/Zbreak events not not really asynchronous, so they don't come here */ case no_event: /* if table changed, it was not synchronized (assumes these entries are all that are be changed) */ DEFER_INTERRUPTS(INTRPT_IN_EVENT_HANDLING, prev_intrpt_state); if (((xfer_table[xf_linefetch] == op_linefetch) || (xfer_table[xf_linefetch] == op_zstepfetch) || (xfer_table[xf_linefetch] == op_zst_fet_over) || (xfer_table[xf_linefetch] == op_mproflinefetch)) && ((xfer_table[xf_linestart] == op_linestart) || (xfer_table[xf_linestart] == op_zstepstart) || (xfer_table[xf_linestart] == op_zst_st_over) || (xfer_table[xf_linestart] == op_mproflinestart)) && ((xfer_table[xf_zbfetch] == op_zbfetch) || (xfer_table[xf_zbfetch] == op_zstzb_fet_over) || (xfer_table[xf_zbfetch] == op_zstzbfetch)) && ((xfer_table[xf_zbstart] == op_zbstart) || (xfer_table[xf_zbstart] == op_zstzb_st_over) || (xfer_table[xf_zbstart] == op_zstzbstart)) && ((xfer_table[xf_forchk1] == op_forchk1) || (xfer_table[xf_forchk1] == op_mprofforchk1)) && (xfer_table[xf_forloop] == op_forloop) && ((xfer_table[xf_ret] == opp_ret) || (xfer_table[xf_ret] == opp_zst_over_ret) || (xfer_table[xf_ret] == opp_zstepret)) && ((xfer_table[xf_retarg] == op_retarg) || (xfer_table[xf_retarg] == opp_zst_over_retarg) || (xfer_table[xf_retarg] == opp_zstepretarg))) { DEBUG_ONLY(scan_xfer_queue_entries(TRUE)); /* verify all not_in_play event states */ ENABLE_EVENT_INTERRUPTS(prev_intrpt_state); return; /* there was no event, but the transfer table does not present any either */ } /* WARNING: potential fallthrough */ default: assertpro(FALSE && outofband); /* see above comment for why this is needed */ } } fis-gtm-V7.0-005/sr_port/deferred_events.h0000755000032200000250000000643414342376331017372 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ #ifndef DEFERRED_EVENTS_INCLUDED #define DEFERRED_EVENTS_NCLUDED #ifdef DEBUG /* Uncomment below to enable tracing of deferred events */ /* #define DEBUG_DEFERRED_EVENT */ #endif #ifdef DEBUG_DEFERRED_EVENT GBLREF volatile int4 fast_lock_count; #define DBGDFRDEVNT(x) if (0 == fast_lock_count) DBGFPF(x) /* fast_lock_count check prevents unsafe output */ #include "gtmio.h" #include "io.h" #include "gtm_time.h" /* If debugging timeout deferral, it is helpful to timestamp the messages. Encapsulate our debugging macro with * enough processing to be able to do that. */ GBLREF char asccurtime[10]; # define SHOWTIME(ASCCURTIME) \ MBSTART { \ time_t CT; \ struct tm *TM_STRUCT; \ size_t LEN; \ \ CT = time(NULL); \ GTM_LOCALTIME(TM_STRUCT, &CT); \ STRFTIME(ASCCURTIME, SIZEOF(ASCCURTIME), "%T", TM_STRUCT, LEN); \ } MBEND #else #define DBGDFRDEVNT(x) #define SHOWTIME(ASCCURTIME) #endif /* ------------------------------------------------------------------ * Sets up transfer table changes needed for: * - Synchronous handling of asynchronous events. * - Single-stepping and breakpoints * Return value indicates success (e.g. if first to attempt). * * Notes: * - mdb_condition_handler is different. * Should change it to use this function (CAREFULLY!). * - So are routines related to zbreak and zstep. * ==> Need to update them too (also carefully -- needs * a thorough redesign or rethinking). * ------------------------------------------------------------------ */ /* To prevent GTMSECSHR from pulling in the function xfer_set_handlers currently used in gtm_malloc_src.h and gtm_test_alloc.c, * and in turn the entire event codebase, we define a function-pointer variable and initialize it at startup to NULL only in * GTMSECSHR and thereby not pull in other unneeded / unwanted executables. */ boolean_t xfer_set_handlers(int4 event_type, int4 param, boolean_t popped_entry); typedef boolean_t (*xfer_set_handlers_fnptr_t)(int4 event_type, int4 param, boolean_t popped_entry); GBLREF xfer_set_handlers_fnptr_t xfer_set_handlers_fnptr; /* see comment above about this typedef */ /* other prototypes for transfer table callback functions, only called by routine that manages xfer_table. */ /* Reset transfer table to normal settings. * Puts back most things back that could have been changed, excepting timeouts waiting for a jobinterrupt to complete * Return value indicates success/failure representing whether the type of reset is the same as event type of set. */ /* This version resets the handlers only if they were set by the same event type. */ boolean_t xfer_reset_if_setter(int4 event_type); /* This version resets a handler */ boolean_t real_xfer_reset(int4 event_type); #endif /* DEFERRED_EVENTS_INCLUDED */ fis-gtm-V7.0-005/sr_port/deferred_events_queue.c0000644000032200000250000002204614342376331020563 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018-2021 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 "gtm_common_defs.h" #include "mdef.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "deferred_events.h" #include "error_trap.h" #include "mdq.h" #ifdef DEBUG #include "compiler.h" #endif GBLREF boolean_t ztrap_explicit_null; GBLREF dollar_ecode_type dollar_ecode; GBLREF volatile boolean_t dollar_zininterrupt; void set_events_from_signals(intrpt_state_t prev_intrpt_state) { /* act on signaled events stored in their event record while the event mechanism had a lock on the intrpt_ok state */ int4 event_type; save_xfer_entry *entry; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert((INTRPT_IN_EVENT_HANDLING == intrpt_ok_state) && (INTRPT_NUM_STATES > prev_intrpt_state)); for (event_type=1; event_type < DEFERRED_EVENTS; event_type++) { entry = &TAREF1(save_xfer_root, event_type); if ((signaled == entry->event_state) && (!dollar_zininterrupt || (jobinterrupt != event_type))) { xfer_set_handlers(event_type, entry->param_val, FALSE); DBGDFRDEVNT((stderr, "%d %s: set_events_from_signals - event type: %d, signaled: %d\n", __LINE__, __FILE__, event_type)); } } ENABLE_INTERRUPTS(INTRPT_IN_EVENT_HANDLING, prev_intrpt_state); } void save_xfer_queue_entry(int4 event_type, int4 param_val) { /* queue an entry; note jobinterrupt goes to the head of the queue */ save_xfer_entry *entry; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assertpro(DEFERRED_EVENTS > event_type); assert(NULL != TREF(save_xfer_root_ptr)); assert(queued == TAREF1(save_xfer_root, event_type).event_state); entry = &(TAREF1(save_xfer_root, event_type)); entry->param_val = param_val; DBGDFRDEVNT((stderr, "%d %s: save_xfer_queue_entry adding new node for %d.\n", __LINE__, __FILE__, event_type)); if ((jobinterrupt == event_type) || ((tptimeout == event_type) && (ztimeout == (TREF(save_xfer_root_ptr))->ev_que.fl->outofband))) dqins((TREF(save_xfer_root_ptr)), ev_que, entry); else dqrins((TREF(save_xfer_root_ptr)), ev_que, entry); assert(no_event != (TREF(save_xfer_root_ptr))->ev_que.fl->outofband); # ifdef DEBUG_DEFERRED_EVENT DBGDFRDEVNT((stderr, "%d %s: save_xfer_queue_entry outofband: %d, set_fn: %X, param_val: %d, entry: %X, ptr->fl: %X\n", __LINE__, __FILE__, entry->outofband, entry->set_fn, entry->param_val, &entry, (TREF(save_xfer_root_ptr))->ev_que.fl)); scan_xfer_queue_entries(FALSE); # endif return; } void pop_real_xfer_queue_entry(int4* event_type, int4* param_val) { /* pop a event queue entry */ save_xfer_entry *entry; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (no_event != (*event_type = (TREF(save_xfer_root_ptr))->ev_que.fl->outofband)) /* WARNING: assignment */ { entry = (TREF(save_xfer_root_ptr))->ev_que.fl; dqdel(entry, ev_que); *param_val = entry->param_val; if (*event_type == (TREF(save_xfer_root_ptr))->ev_que.fl->outofband) { /* This should not happen, but it did during testing. Fix it for PRO */ assert((TREF(save_xfer_root_ptr))->ev_que.fl == (TREF(save_xfer_root_ptr))->ev_que.bl); assert(entry == (TREF(save_xfer_root_ptr))->ev_que.fl); assert(FALSE); /* fix it in pro */ (TREF(save_xfer_root_ptr))->ev_que.fl = (TREF(save_xfer_root_ptr))->ev_que.bl = TREF(save_xfer_root_ptr); } } DBGDFRDEVNT((stderr, "%d %s: pop_real_xfer_queue_entry: %d\n", __LINE__, __FILE__, *event_type)); assertpro(DEFERRED_EVENTS > *event_type); } void pop_xfer_queue_entry(int4* event_type, int4* param_val) { /* wrapper for pop_real_xfer_queue_entry that deals with the juggling of timeouts with respect to jobinterrupts*/ boolean_t defer_tptimeout, defer_ztimeout; int4 next_event; save_xfer_entry *entry; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # ifdef DEBUG_DEFERRED_EVENT scan_xfer_queue_entries(FALSE); DBGDFRDEVNT((stderr, "%d %s: dzinin: %d, dec_idx: %d, dztr_null: %d, dztr_len: %d\n", __LINE__, __FILE__, dollar_zininterrupt, dollar_ecode.index, ztrap_explicit_null, (TREF(dollar_ztrap)).str.len)); # endif *event_type = no_event; if (dollar_zininterrupt || ((0 != dollar_ecode.index) && (ETRAP_IN_EFFECT))) { /* conditions indicate tptimeout and ztimeout should remain deferred */ for (next_event = no_event, defer_tptimeout = defer_ztimeout = FALSE; DEFERRED_EVENTS > next_event; next_event++) { /* don't pend tptimeout or ztimeout it they should remain deferred */ pop_real_xfer_queue_entry(event_type, param_val); DBGDFRDEVNT((stderr, "%d %s: pop_reset_xfer returned event %d\n", __LINE__, __FILE__, *event_type)); switch (*event_type) { case tptimeout: defer_tptimeout = TRUE; TAREF1(save_xfer_root, tptimeout).event_state = queued; continue; case ztimeout: defer_ztimeout = TRUE; TAREF1(save_xfer_root, ztimeout).event_state = queued; continue; case jobinterrupt: if (dollar_zininterrupt) continue; /* WARNING: possible fallthrough */ default: next_event = DEFERRED_EVENTS; /* leave loop */ DBGDFRDEVNT((stderr, "%d %s: pop_xfer_queue_entry %d\n", __LINE__, __FILE__, *event_type)); break; /* found other event behind one or both timers */ } } /* if we popped either or both of these, put them back; perhaps swapped, but OK to give priority to the tptimer*/ if (defer_tptimeout) { SAVE_XFER_QUEUE_ENTRY(tptimeout, (TAREF1(save_xfer_root, tptimeout)).param_val); DBGDFRDEVNT((stderr, "%d %s: requeued event %d\n", __LINE__, __FILE__, tptimeout)); } if (defer_ztimeout) /* should be behind any tptimeout */ { SAVE_XFER_QUEUE_ENTRY(ztimeout, (TAREF1(save_xfer_root, ztimeout)).param_val); DBGDFRDEVNT((stderr, "%d %s: requeued event %d\n", __LINE__, __FILE__, ztimeout)); } } else { /* things are straightforward */ pop_real_xfer_queue_entry(event_type, param_val); DBGDFRDEVNT((stderr, "%d %s: pop_reset_xfer returned event %d\n", __LINE__, __FILE__, *event_type)); } return; } void remove_xfer_queue_entry(int4 event_type) { /* given an event_type, if it's in the queue, remove it */ save_xfer_entry *entry; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(no_event != event_type); if ((TREF(save_xfer_root_ptr))->ev_que.fl != TREF(save_xfer_root_ptr)) { /* something queued */ entry = NULL; dqloop(TREF(save_xfer_root_ptr), ev_que, entry) { assert(NULL != entry); if (entry->outofband == event_type) { /* it's the one we're after */ DBGDFRDEVNT((stderr, "%d %s: remove_xfer_queue_entry event_type: %d\n", __LINE__, __FILE__, event_type)); dqdel(entry, ev_que); break; } assert((entry != entry->ev_que.fl) && (entry != entry->ev_que.bl)); if ((entry == entry->ev_que.fl) && (entry == entry->ev_que.bl)) { /* WARNING: The current entry's forward and back links point back to itself. The queue * is not well formed. Assume that this the final entry in the queue and repoint the * forward and back links to TREF(save_xfer_root_ptr). For now, we fix this situation * in PRO and assert fail in DBG. */ assert(FALSE); entry->ev_que.fl = entry->ev_que.bl = TREF(save_xfer_root_ptr); } } } } void scan_xfer_queue_entries(boolean_t check4players) { /* debug-only state checker */ save_xfer_entry *entry; int event_type; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; #ifdef DEBUG DBGDFRDEVNT((stderr, "%d %s: scan_xfer_queue_entries in play:\n", __LINE__, __FILE__)); for (event_type=no_event; event_type < DEFERRED_EVENTS; event_type++) { entry = &TAREF1(save_xfer_root, event_type); if (not_in_play != entry->event_state) { DBGDFRDEVNT((stderr, "%d %s: event type: %d, state: %d\n",__LINE__, __FILE__, event_type, entry->event_state)); if (check4players) assert(not_in_play == TAREF1(save_xfer_root, event_type).event_state); } } DBGDFRDEVNT((stderr, "%d %s: scan_xfer_queue_entries queued:\n", __LINE__, __FILE__)); dqloop((TREF(save_xfer_root_ptr)), ev_que, entry) { DBGDFRDEVNT((stderr, "%d %s: scan_xfer_queue_entries - event type: %d\n", __LINE__, __FILE__, entry->outofband)); } # else assertpro(FALSE); /* at this point this routine is just used for debug mode */ # endif return; } void empty_xfer_queue_entries(void) { /* clear the queue */ save_xfer_entry *entry; DCL_THREADGBL_ACCESS; DBGDFRDEVNT((stderr, "%d %s: empty_xfer_queue_entries event_type: %d\n", __LINE__, __FILE__, entry->outofband)); SETUP_THREADGBL_ACCESS; dqloop((TREF(save_xfer_root_ptr)), ev_que, entry) { if (not_in_play == entry->event_state) DBGDFRDEVNT((stderr, "%d %s: empty_xfer_queue_entries event_type: %d event_state: %d\n", __LINE__, __FILE__, entry->outofband, entry->event_state)); assert(queued == entry->event_state); dqdel(entry, ev_que); entry->event_state = not_in_play; } } fis-gtm-V7.0-005/sr_port/deferred_events_queue.h0000644000032200000250000000711614342376331020571 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018-2022 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. * * * ****************************************************************/ #ifndef DEFERRED_EVENTS_QUEUE_INCLUDED #define DEFERRED_EVENTS_QUEUE_INCLUDED #include "gtm_signal.h" #include "op.h" /* needed by outofband resolution of set event functions op_zstep and op_setzbrk */ #include "gvcmz_neterr.h" /* needed by outofband resolution of gtcmz_net_error */ #define MAXQUEUELOCKWAIT 10000 /* 10sec = 10000 1-msec waits */ GBLREF boolean_t blocksig_initialized; GBLREF sigset_t block_sigsent; #define OUTOFBAND_MSK 0x02000018 #define CTRLC_MSK 0x00000008 #define SIGHUP_MSK 0x00000010 #define CTRLC 3 #define CTRLD 4 #define CTRLY 25 #define MAXOUTOFBAND 31 #define OUTOFBAND_RESTARTABLE(event) (jobinterrupt == (event)) /* ------------------------------------------------------------------ * Perform action corresponding to the first async event that * was logged. * ------------------------------------------------------------------ */ void ctrap_set(int4); void ctrlc_set(int4); void jobinterrupt_set(int4 dummy); void tptimeout_set(int4 dummy_param); /* Used to setup tptimeout error via out-of-band */ void ztimeout_set(int4 dummy_param); void defer_error_set(int4); void async_action(bool); void outofband_clear(void); #define D_EVENT(a,b) a enum outofbands { #include "outofband.h" }; #undef D_EVENT enum event_in_play { not_in_play, /* 0 */ signaled, /* 1 */ queued, /* 2 */ pending, /* 3 */ active, /* 4 */ num_event_states /* 5 */ }; typedef struct save_xfer_entry_struct { struct { struct save_xfer_entry_struct *fl, *bl; } ev_que; void (*set_fn)(int4 param); int4 outofband; int4 param_val; volatile int4 event_state; } save_xfer_entry; void save_xfer_queue_entry(int4 event_type, int4 param_val); void pop_real_xfer_queue_entry(int4* event_type, int4* param_val); void pop_xfer_queue_entry(int4* event_type, int4* param_val); void remove_xfer_queue_entry(int4 event_type); void scan_xfer_queue_entries(boolean_t check4players); void empty_xfer_queue_entries(void); void set_events_from_signals(intrpt_state_t prev_intrpt_state); #define SAVE_XFER_QUEUE_ENTRY(EVENT_TYPE, PARAM_VAL) \ MBSTART { \ assert((INTRPT_IN_EVENT_HANDLING == intrpt_ok_state) \ || (multi_thread_in_use)); \ save_xfer_queue_entry(EVENT_TYPE, PARAM_VAL); \ } MBEND #define POP_XFER_QUEUE_ENTRY(EVENT_TYPE, PARAM_VAL) \ MBSTART { \ assert(INTRPT_IN_EVENT_HANDLING == intrpt_ok_state); \ pop_xfer_queue_entry(EVENT_TYPE, PARAM_VAL); \ } MBEND #define REMOVE_XFER_QUEUE_ENTRY(ID) \ MBSTART { \ assert(INTRPT_IN_EVENT_HANDLING == intrpt_ok_state); \ remove_xfer_queue_entry(ID); \ } MBEND #define EMPTY_XFER_QUEUE_ENTRIES \ MBSTART { \ assert(INTRPT_IN_EVENT_HANDLING == intrpt_ok_state); \ empty_xfer_queue_entries(); \ } MBEND /* while the other macros check the protection state coming in, this one checks for proper cleanup going out */ #define ENABLE_EVENT_INTERRUPTS(PREV_INTRPT_STATE) \ MBSTART { \ set_events_from_signals(PREV_INTRPT_STATE); \ assert(INTRPT_IN_EVENT_HANDLING != intrpt_ok_state); \ } MBEND #endif /* DEFERRED_EVENTS_QUEUE_INCLUDED */ fis-gtm-V7.0-005/sr_port/desired_db_format_set.c0000644000032200000250000001600514342376331020520 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2020 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_time.h" #include "gtm_string.h" #include "gdsroot.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 "iosp.h" /* for SS_NORMAL */ #include "util.h" /* Prototypes */ #include "gtmmsg.h" /* for gtm_putmsg prototype */ #include "desired_db_format_set.h" #include "send_msg.h" /* for send_msg */ #include "wcs_phase2_commit_wait.h" #define WCS_PHASE2_COMMIT_WAIT_LIT "wcb_phase2_commit_wait" LITREF char *gtm_dbversion_table[]; GBLREF inctn_opcode_t inctn_opcode; GBLREF jnl_gbls_t jgbl; GBLREF uint4 process_id; GBLREF inctn_detail_t inctn_detail; /* holds detail to fill in to inctn jnl record */ error_def(ERR_ASYNCIONOV4); error_def(ERR_COMMITWAITSTUCK); error_def(ERR_CRYPTNOV4); error_def(ERR_DBDSRDFMTCHNG); error_def(ERR_MMNODYNDWNGRD); error_def(ERR_MUDWNGRDTN); error_def(ERR_MUNOACTION); error_def(ERR_SNAPSHOTNOV4); error_def(ERR_WCBLOCKED); /* input parameter "command_name" is a string that is either "MUPIP REORG UPGRADE/DOWNGRADE" or "MUPIP SET VERSION" */ int4 desired_db_format_set(gd_region *reg, enum db_ver new_db_format, char *command_name) { boolean_t was_crit; char *db_fmt_str; char *wcblocked_ptr; int4 status; uint4 jnl_status; inctn_opcode_t save_inctn_opcode; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; trans_num curr_tn; jnl_private_control *jpc; jnl_buffer_ptr_t jbp; assert(reg->open); csa = &FILE_INFO(reg)->s_addrs; csd = csa->hdr; /* We do not allow databases to be encrypted if the version is V4. */ if (USES_ENCRYPTION(csd->is_encrypted) && (GDSV4 == new_db_format)) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_CRYPTNOV4, 2, DB_LEN_STR(reg)); return ERR_CRYPTNOV4; } /* We do not allow databases in ASYNCIO mode if the block format is V4 */ if (csd->asyncio && (GDSV4 == new_db_format)) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_ASYNCIONOV4, 6, DB_LEN_STR(reg), LEN_AND_LIT("ASYNCIO enabled"), LEN_AND_LIT("downgrade to V4")); return ERR_ASYNCIONOV4; } /* We don't allow databases to be downgraded when snapshots are in progress */ if (SNAPSHOTS_IN_PROG(csa->nl) && (GDSV4 == new_db_format)) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_SNAPSHOTNOV4, 3, csa->nl->num_snapshots_in_effect, DB_LEN_STR(reg)); return ERR_SNAPSHOTNOV4; } was_crit = csa->now_crit; if (FALSE == was_crit) grab_crit(reg, WS_1); /* if MM and desired_db_format is not V5, gvcst_init would have issued MMNODYNDWNGRD error. assert that. */ assert((dba_bg == csd->acc_meth) || ((dba_mm == csd->acc_meth) && (GDSV6 == csd->desired_db_format))); if (csd->desired_db_format == new_db_format) { /* no change in db_format. fix max_tn_warn if necessary and return right away. */ status = ERR_MUNOACTION; assert(csd->trans_hist.curr_tn <= csd->max_tn); if ((GDSV4 == new_db_format) && (MAX_TN_V4 < csd->max_tn)) { /* reset max_tn to MAX_TN_V4 only if V4 format and the new value will still be greater than curr_tn */ assertpro(MAX_TN_V4 >= csd->trans_hist.curr_tn); csd->max_tn = MAX_TN_V4; /* since max_tn changed above, max_tn_warn might also need to correspondingly change */ SET_TN_WARN(csd, csd->max_tn_warn); } if (FALSE == was_crit) rel_crit(reg); return status; } if (dba_mm == csd->acc_meth) { status = ERR_MMNODYNDWNGRD; gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(4) status, 2, REG_LEN_STR(reg)); if (FALSE == was_crit) rel_crit(reg); return status; } /* check if curr_tn is too high to downgrade */ curr_tn = csd->trans_hist.curr_tn; if ((GDSV4 == new_db_format) && (MAX_TN_V4 <= curr_tn)) { status = ERR_MUDWNGRDTN; gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) status, 3, &curr_tn, DB_LEN_STR(reg)); if (FALSE == was_crit) rel_crit(reg); return status; } /* Wait for concurrent phase2 commits to complete before switching the desired db format */ if (csa->nl->wcs_phase2_commit_pidcnt && !wcs_phase2_commit_wait(csa, NULL)) { /* Set wc_blocked so next process to get crit will trigger cache-recovery */ SET_TRACEABLE_VAR(csa->nl->wc_blocked, WC_BLOCK_RECOVER); wcblocked_ptr = WCS_PHASE2_COMMIT_WAIT_LIT; send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_WCBLOCKED, 6, LEN_AND_STR(wcblocked_ptr), process_id, &csd->trans_hist.curr_tn, DB_LEN_STR(reg)); status = ERR_COMMITWAITSTUCK; gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(7) status, 5, process_id, 1, csa->nl->wcs_phase2_commit_pidcnt, DB_LEN_STR(reg)); if (FALSE == was_crit) rel_crit(reg); return status; } if (JNL_ENABLED(csd)) { SET_GBL_JREC_TIME; /* needed for jnl_ensure_open, jnl_write_pini and jnl_write_aimg_rec */ jpc = csa->jnl; jbp = jpc->jnl_buff; /* Before writing to jnlfile, adjust jgbl.gbl_jrec_time if needed to maintain time order of jnl records. * This needs to be done BEFORE the jnl_ensure_open as that could write journal records * (if it decides to switch to a new journal file) */ ADJUST_GBL_JREC_TIME(jgbl, jbp); jnl_status = jnl_ensure_open(reg, csa); if (0 == jnl_status) { save_inctn_opcode = inctn_opcode; inctn_opcode = inctn_db_format_change; inctn_detail.blks2upgrd_struct.blks_to_upgrd_delta = csd->blks_to_upgrd; if (0 == jpc->pini_addr) jnl_write_pini(csa); jnl_write_inctn_rec(csa); inctn_opcode = save_inctn_opcode; } else gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(6) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg)); } csd->desired_db_format = new_db_format; csd->fully_upgraded = FALSE; csd->desired_db_format_tn = curr_tn; switch (new_db_format) { case GDSV4: csd->max_tn = MAX_TN_V4; break; case GDSV6: csd->max_tn = MAX_TN_V6; break; case GDSV7: csd->max_tn = MAX_TN_V7; assert(FALSE); break; default: assertpro((GDSV4 == new_db_format) || (GDSV6 == new_db_format) || (GDSV7 == new_db_format)); } SET_TN_WARN(csd, csd->max_tn_warn); /* if max_tn changed above, max_tn_warn also needs a corresponding change */ assert(curr_tn < csd->max_tn); /* ensure CHECK_TN macro below will not issue TNTOOLARGE rts_error */ CHECK_TN(csa, csd, curr_tn); /* can issue rts_error TNTOOLARGE */ /* increment csd->trans_hist.curr_tn */ assert(csd->trans_hist.early_tn == csd->trans_hist.curr_tn); csd->trans_hist.early_tn = csd->trans_hist.curr_tn + 1; INCREMENT_CURR_TN(csd); if (FALSE == was_crit) rel_crit(reg); status = SS_NORMAL; send_msg_csa(CSA_ARG(csa) VARLSTCNT(11) ERR_DBDSRDFMTCHNG, 9, DB_LEN_STR(reg), LEN_AND_STR(gtm_dbversion_table[new_db_format]), LEN_AND_STR(command_name), process_id, process_id, &curr_tn); return status; } fis-gtm-V7.0-005/sr_port/desired_db_format_set.h0000755000032200000250000000120214342376331020521 0ustar librarygtc/**************************************************************** * * * Copyright 2005 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. * * * ****************************************************************/ #ifndef DESIRED_DB_FORMAT_SET_DEFINED /* prototypes */ int4 desired_db_format_set(gd_region *reg, enum db_ver new_db_format, char *command_name); #define DESIRED_DB_FORMAT_SET_DEFINED #endif fis-gtm-V7.0-005/sr_port/deviceparameters.c0000755000032200000250000004013614342376331017541 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "compiler.h" #include "opcode.h" #include "toktyp.h" #include "nametabtyp.h" #include "io_params.h" #include "zshow_params.h" #include "advancewindow.h" #include "int_namelook.h" #include "cvtparm.h" #include "deviceparameters.h" error_def(ERR_DEVPARINAP); error_def(ERR_DEVPARUNK); error_def(ERR_DEVPARVALREQ); error_def(ERR_RPARENMISSING); LITREF unsigned char io_params_size[]; LITREF dev_ctl_struct dev_param_control[]; LITDEF nametabent dev_param_names[] = { /* Must be in alpha order and in sync with dev_param_index[], zshow_param_index[] and dev_param_data[] */ {4,"ALLO*"} /* in zshow_param_index */ ,{2,"AP*"} ,{6,"APPEND"} ,{2,"AT*"} ,{6,"ATTACH"} ,{9,"BIGRECORD"} /* dead VMS placeholder, used in the test system */ ,{2,"BL*"} ,{4,"BLOC*"} ,{9,"BLOCKSIZE"} ,{4,"CANO*"} ,{9,"CANONICAL"} ,{2,"CE*"} ,{7,"CENABLE"} ,{3,"CHS*"} ,{5,"CHSET"} ,{3,"CLE*"} ,{11,"CLEARSCREEN"} ,{4,"COMM*"} ,{7,"COMMAND"} ,{4,"CONN*"} ,{7,"CONNECT"} ,{4,"CONT*"} /*,{10,"CONTIGUOUS"}*/ ,{4,"CONV*"} ,{7,"CONVERT"} ,{2,"CT*"} ,{4,"CTRA"} ,{5,"CTRAP"} /*,{5,"CTRAP"}*/ ,{4,"DELE*"} ,{6,"DELETE"} ,{4,"DELI*"} ,{9,"DELIMITER"} ,{4,"DEST*" } ,{7,"DESTROY"} ,{3,"DET*"} /*,{6, "DETACH}*/ ,{3,"DOW*"} /*,{10,"DOWNSCROLL"}*/ ,{2,"EB*"} ,{6,"EBCDIC"} ,{2,"EC*"} ,{4,"ECHO"} ,{2,"ED*"} ,{4,"EDIT"} ,{4,"EMPT*"} ,{7,"EMPTERM"} ,{6,"ERASEL*"} ,{9,"ERASELINE*"} ,{2,"ES*"} /*,{6,"ESCAPE*"}*/ ,{3,"EXC*"} ,{4,"EXCE*"} /*,{9,"EXCEPTION"}*/ ,{3,"EXT*"} ,{4,"EXTE*"} /*,{9,"EXTENSION"}*/ ,{1,"F"} /* legacy abreviation for FIELD */ ,{4,"FFLF"} ,{3,"FIE*"} ,{3,"FIE*"} ,{5,"FIELD"} ,{3,"FIF*"} ,{4,"FIFO"} ,{3,"FIL*"} /*,{6,"FILTER"}*/ ,{3,"FIX*"} ,{5,"FIXED"} ,{3,"FLA*"} ,{4,"FLAG"} ,{3,"FLU*"} ,{5,"FLUSH"} ,{3,"FOL*"} ,{6,"FOLLOW"} ,{1,"G"} ,{5,"GROUP"} ,{3,"HOL*"} /* dead VMS placeholder */ ,{3,"HOS*"} ,{8,"HOSTSYNC"} ,{2,"HU*"} ,{9,"HUPENABLE"} ,{6,"ICHSET"} ,{4,"IKEY"} ,{4,"INDE*"} ,{11,"INDEPENDENT"} ,{8,"INREWIND"} ,{6,"INSEEK"} ,{2,"IN*"} ,{4,"INSE*"} /*,{6,"INSERT"}*/ ,{3,"IOE*"} /*,{7,"IOERROR*"}*/ ,{3,"KEY"} ,{3,"LAB*"} /* in zshow_param_index */ ,{2,"LE*"} ,{4,"LENG*"} /*,{6,"LENGTH"}*/ ,{2,"LI*"} ,{6,"LISTEN"} ,{4,"LOGF*"} /* dead VMS placeholder */ ,{4,"LOGQ*"} /* dead VMS placeholder */ ,{3,"LOW*"} /* dead VMS placeholder */ ,{1,"M"} ,{4,"MORE*"} ,{12,"MOREREADTIME"} ,{3,"NEW*"} ,{10,"NEWVERSION"} ,{4,"NOCA*"} ,{11,"NOCANONICAL"} ,{4,"NOCE*"} ,{6,"NOCENA*"} /*,{9,"NOCENABLE"}*/ ,{6,"NOCONV*"} ,{9,"NOCONVERT"} ,{6,"NODELI*"} ,{11,"NODELIMITER"} ,{6,"NODEST*"} ,{9,"NODESTROY"} ,{4,"NOEB*"} ,{8,"NOEBCDIC"} ,{4,"NOEC*"} ,{6,"NOECHO"} ,{4,"NOED*"} ,{6,"NOEDIT"} ,{6,"NOEMPT*"} ,{9,"NOEMPTERM"} ,{4,"NOES*"} ,{6,"NOESCA*"} /*,{8,"NOESCAPE"}*/ ,{6,"NOFFLF"} ,{5,"NOFIL*"} ,{8,"NOFILTER"} ,{5,"NOFIX*"} ,{7,"NOFIXED"} ,{4,"NOFLA*"} ,{5,"NOFLAG"} ,{5,"NOFOL*"} ,{8,"NOFOLLOW"} ,{5,"NOHOS*"} ,{6,"NOHOST*"} /*,{10,"NOHOSTSYNC"}*/ ,{4,"NOHU*"} ,{11,"NOHUPENABLE"} ,{4,"NOIN*"} ,{6,"NOINSE*"} /*,{8,"NOINSERT"}*/ ,{4,"NOPAG*"} ,{5,"NOPAGE"} ,{6,"NOPAST*"} ,{9,"NOPASTHRU"} ,{6,"NOREAD"} ,{10,"NOREADONLY"} ,{7,"NOREADS*"} ,{10,"NOREADSYNC"} ,{4,"NOSE*"} ,{12,"NOSEQUENTAIL"} ,{4,"NOST*"} ,{8,"NOSTREAM*"} ,{4,"NOTE"} ,{5,"NOTER*"} ,{12,"NOTERMINATOR"} ,{4,"NOTI*"} /* dead VMS placeholder */ ,{5,"NOTRA*"} /* dead VMS placeholder */ ,{5,"NOTRU*"} ,{10,"NOTRUNCATE"} ,{4,"NOTT*"} ,{6,"NOTTSY*"} ,{8,"NOTTSYNC"} ,{4,"NOTY*"} ,{6,"NOTYPE"} ,{4,"NOWA*"} ,{6,"NOWAIT"} ,{4,"NOWR"} ,{6,"NOWRAP"} ,{7,"NOWRITE*"} ,{2,"NU*"} ,{4,"NULL"} /* suspect this is only for internal use */ ,{1,"O"} /* legacy abreviation for OWNER */ ,{6,"OCHSET"} ,{4,"OKEY"} ,{7,"OPTIONS"} ,{9,"OUTREWIND"} ,{7,"OUTSEEK"} ,{2,"OV*"} ,{9,"OVERWRITE"} ,{2,"OW*"} /*,{5,"OWNER"}*/ ,{2,"P1"} ,{2,"P2"} ,{2,"P3"} ,{2,"P4"} ,{2,"P5"} ,{2,"P6"} ,{2,"P7"} ,{2,"P8"} ,{3,"PAD"} ,{3,"PAG*"} ,{4,"PAGE"} ,{4,"PARS*"} ,{5,"PARSE"} ,{4,"PAST*"} ,{7,"PASTHRU*"} ,{3,"PRM*"} ,{6,"PRMMBX"} /* in zshow_param_index */ ,{3,"PRO*"} ,{10,"PROTECTION"} ,{3,"QUE*"} /* dead VMS placeholder */ ,{4,"RCHK*"} /* in zshow_param_index */ ,{4,"READ"} ,{8,"READONLY"} ,{5,"READS*"} ,{8,"READSYNC"} ,{3,"REC*"} ,{10,"RECORDSIZE"} ,{3,"REN*"} ,{6,"RENAME"} ,{3,"REP*"} ,{7,"REPLACE"} ,{3,"REW*"} ,{6,"REWIND"} ,{3,"RFA"} /* dead VMS placeholder */ ,{3,"RFM"} /* dead VMS placeholder */ ,{1,"S"} /* legacy abreviation for WORLD */ ,{3,"SEE*"} ,{4,"SEEK"} ,{3,"SEQ*"} ,{10,"SEQUENTIAL"} ,{2,"SH"} ,{4,"SHAR*"} /*{6,"SHARED"}*/ ,{4,"SHEL*"} ,{5,"SHELL"} ,{2,"SO*"} ,{6,"SOCKET"} ,{3,"SPA*"} /* dead VMS placeholder */ ,{3,"SPO*"} /* dead VMS placeholder */ ,{2,"ST"} ,{3,"STR*"} /* legacy abreviations for STREAM*/ ,{4,"STDE*"} ,{6,"STDERR"} ,{2,"SU*"} /* dead VMS placeholder */ ,{6,"STREAM"} ,{2,"SY*"} ,{2,"TE*"} ,{4,"TERM*"} ,{10,"TERMINATOR"} ,{3,"TIM*"} /*,{7,"TIMEOUT"}*/ ,{3,"TRU*"} ,{8,"TRUNCATE"} ,{4,"TTSY*"} ,{6,"TTSYNC"} /* in zshow_param_index */ ,{4,"TYPE*"} ,{9,"TYPEAHEAD"} /* in zshow_param_index */ ,{2,"UI*"} ,{3,"UIC"} ,{2,"UP*"} ,{8,"UPSCROLL"} ,{2,"UR*"} /* dead VMS placeholder */ ,{2,"US*"} /* dead VMS placeholder */ ,{2,"VA*"} /*,{8,"VARIABLE"}*/ ,{1,"W"} /* legacy abreviation for WORLD */ ,{2,"WA*"} ,{4,"WAIT"} /* in zshow_param_index */ ,{2,"WC*"} ,{4,"WCHK"} /* in zshow_param_index */ ,{2,"WI*"} ,{5,"WIDTH"} ,{2,"WO*"} /*,{5,"WORLD"}*/ ,{2,"WR"} ,{4,"WRAP"} ,{5,"WRITE"} /* legacy abreviation for WRITEONLY */ ,{7,"WRITELB"} /* dead VMS placeholder */ ,{7,"WRITEOF"} /* dead VMS placeholder */ ,{9,"WRITEONLY*"} ,{7,"WRITETM"} /* dead VMS placeholder */ ,{1,"X"} ,{1,"Y"} ,{2,"ZB*"} ,{7,"ZBFSIZE"} ,{2,"ZD*"} ,{6,"ZDELAY"} ,{3,"ZEX*"} ,{10,"ZEXCEPTION"} ,{4,"ZFIL*"} ,{7,"ZFILTER"} ,{3,"ZFF"} ,{2,"ZI*"} ,{8,"ZIBFSIZE"} ,{4,"ZLEN*"} ,{7,"ZLENGTH"} ,{4,"ZLIS*"} ,{7,"ZLISTEN"} ,{8,"ZNODELAY"} /* ZNO* have to be spelled out fully */ ,{9,"ZNOFILTER"} ,{5,"ZNOFF"} ,{7,"ZNOWRAP"} ,{4,"ZWID*"} ,{6,"ZWIDTH"} ,{4,"ZWRA*"} ,{5,"ZWRAP"} }; /* Offset of letter in dev_param_names. Adding e.g. 1 entry there will add 1 to every entry corresponding to subsequent letters */ LITDEF uint4 dev_param_index[27] = { /* A B C D E F G H I J K L M N */ 0, 5, 9, 27, 35, 50, 66, 68, 73, 82, 82, 83, 91, 94, /* O P Q R S T U V W X Y Z end */ 162, 171, 190, 191, 206, 226, 236, 242, 243, 258, 259, 260, 283 }; /* Offset of string within letter in dev_param_names */ /* maintained in conjunction with zshow_params.h = offset in letter, letter */ /* Ie: a = 0 & z = 25, so (currently) BLOC is the third entry in the 'B' section of dev_param_names[] */ LITDEF zshow_index zshow_param_index[] = { /* ALLO BLOC COMMAND CONV CTRA DELE DEST EBCDIC EDIT EMPTERM EXCE EXTE */ {0,0}, {2,1}, {9,2}, {14,2}, {16,2}, {0,3}, {4,3}, {1,4}, {4,4}, {7,4}, {12,4}, {14,4}, /* FIE FIL FIXED FOLLOW HOST ICHSET INDEPENDENT INSE LAB LENG */ {4,5}, {7,5}, {9,5}, {15,5}, {1,7}, {0,8}, {3,8}, {7,8}, {0,11}, {2,11}, /* NOCENE NODEST NOECHO NOEDIT NOEMPTERM NOESCA NOFOLLOW NOHOST NOINSE */ {4,13}, {10,13}, {15,13}, {17,13}, {19,13}, {21,13}, {30,13}, {32,13}, {36,13}, /* NOPAST NOREADS NOTTSY NOTYPE NOWRAP */ {39,13}, {42,13}, {57,13}, {59,13}, {64,13}, /* OCHSET PAD PARSE PAST PRMMBX RCHK READ READS REC */ {1,14}, {8,15}, {12,15}, {13,15}, {17,15}, {0,17}, {1,17}, {3,17}, {5,17}, /* SHAR SHELL STDERR STREAM TERM TTSY TYPE UIC */ {6,18}, {8,18}, {16,18}, {18,18}, {1,19}, {7,19}, {8,19}, {1,20}, /* WAIT WCHK WIDTH WRITE */ {2,22}, {4,22}, {6,22}, {10,22} }; int deviceparameters(oprtype *c, char who_calls) { oprtype x; oprtype cat_list[n_iops]; int cat_cnt; mval tmpmval; triple *ref, *parm; int n; int status; char parstr[MAXDEVPARLEN]; char *parptr; boolean_t is_parm_list; boolean_t parse_warn; static readonly unsigned char dev_param_data[] = { /* must be in sync with dev_param_names[] */ iop_allocation ,iop_append ,iop_append ,iop_attach ,iop_attach ,iop_bigrecord ,iop_blocksize ,iop_blocksize ,iop_blocksize ,iop_canonical ,iop_canonical ,iop_cenable ,iop_cenable ,iop_chset ,iop_chset ,iop_clearscreen ,iop_clearscreen ,iop_command ,iop_command ,iop_connect ,iop_connect ,iop_contiguous ,iop_convert ,iop_convert ,iop_ctrap ,iop_ctrap ,iop_ctrap ,iop_delete ,iop_delete ,iop_delimiter ,iop_delimiter ,iop_destroy ,iop_destroy ,iop_detach ,iop_downscroll ,iop_ebcdic ,iop_ebcdic ,iop_echo ,iop_echo ,iop_editing ,iop_editing ,iop_empterm ,iop_empterm ,iop_eraseline ,iop_eraseline ,iop_escape ,iop_exception ,iop_exception ,iop_extension ,iop_extension ,iop_field ,iop_fflf ,iop_field ,iop_field ,iop_field ,iop_fifo, iop_fifo ,iop_filter ,iop_fixed ,iop_fixed ,iop_flag ,iop_flag ,iop_flush ,iop_flush ,iop_follow ,iop_follow ,iop_g_protection, iop_g_protection ,iop_hold ,iop_hostsync, iop_hostsync ,iop_hupenable ,iop_hupenable ,iop_ipchset ,iop_input_key ,iop_independent ,iop_independent ,iop_inrewind ,iop_inseek ,iop_insert ,iop_insert ,iop_ioerror ,iop_key ,iop_label ,iop_length ,iop_length ,iop_zlisten ,iop_zlisten /* Replaces iop_listen. LISTEN is now aliased to ZLISTEN. */ ,iop_logfile ,iop_logqueue ,iop_lowercase ,iop_m ,iop_morereadtime ,iop_morereadtime ,iop_newversion ,iop_newversion ,iop_nocanonical ,iop_nocanonical ,iop_nocenable ,iop_nocenable ,iop_noconvert ,iop_noconvert ,iop_nodelimiter ,iop_nodelimiter ,iop_nodestroy ,iop_nodestroy ,iop_noebcdic ,iop_noebcdic ,iop_noecho ,iop_noecho ,iop_noediting ,iop_noediting ,iop_noempterm ,iop_noempterm ,iop_noescape ,iop_noescape ,iop_nofflf ,iop_nofilter ,iop_nofilter ,iop_nofixed ,iop_nofixed ,iop_noflag ,iop_noflag ,iop_nofollow ,iop_nofollow ,iop_nohostsync ,iop_nohostsync ,iop_nohupenable ,iop_nohupenable ,iop_noinsert ,iop_noinsert ,iop_page ,iop_page ,iop_nopasthru ,iop_nopasthru ,iop_noreadonly ,iop_noreadonly ,iop_noreadsync ,iop_noreadsync ,iop_nosequential ,iop_nosequential ,iop_nostream ,iop_nostream ,iop_note ,iop_noterminator, iop_noterminator ,iop_notify ,iop_notrailer ,iop_notruncate ,iop_notruncate ,iop_nottsync ,iop_nottsync ,iop_nottsync ,iop_notypeahead ,iop_notypeahead ,iop_nowait ,iop_nowait ,iop_nowrap ,iop_nowrap ,iop_nowriteonly ,iop_nl ,iop_nl ,iop_o_protection ,iop_opchset ,iop_output_key ,iop_options ,iop_outrewind ,iop_outseek ,iop_noinsert ,iop_noinsert ,iop_o_protection ,iop_p1 ,iop_p2 ,iop_p3 ,iop_p4 ,iop_p5 ,iop_p6 ,iop_p7 ,iop_p8 ,iop_pad ,iop_page ,iop_page ,iop_parse ,iop_parse ,iop_pasthru ,iop_pasthru ,iop_prmmbx ,iop_prmmbx ,iop_o_protection ,iop_o_protection ,iop_queue ,iop_rdcheckdata ,iop_readonly ,iop_readonly ,iop_readsync ,iop_readsync ,iop_recordsize ,iop_recordsize ,iop_rename ,iop_rename ,iop_replace ,iop_replace ,iop_rewind ,iop_rewind ,iop_rfa ,iop_rfm ,iop_s_protection ,iop_seek ,iop_seek ,iop_sequential ,iop_sequential ,iop_shared ,iop_shared ,iop_shell ,iop_shell ,iop_socket ,iop_socket ,iop_space ,iop_spool ,iop_stream, iop_stream ,iop_stderr, iop_stderr ,iop_stream ,iop_submit ,iop_s_protection ,iop_terminator, iop_terminator, iop_terminator ,iop_timeout ,iop_truncate ,iop_truncate ,iop_ttsync ,iop_ttsync ,iop_typeahead ,iop_typeahead ,iop_uic ,iop_uic ,iop_upscroll ,iop_upscroll ,iop_urgent ,iop_user ,iop_nofixed ,iop_w_protection ,iop_wait ,iop_wait ,iop_wtcheckdata ,iop_wtcheckdata ,iop_width ,iop_width ,iop_w_protection ,iop_wrap ,iop_wrap ,iop_writeonly ,iop_writelb ,iop_writeof ,iop_writeonly ,iop_writetm ,iop_x ,iop_y ,iop_zbfsize, iop_zbfsize ,iop_zdelay, iop_zdelay ,iop_exception, iop_exception /* for ZEXCEPTION which is a synonym for EXCEPTION */ ,iop_filter, iop_filter /* for ZFILTER which is a synonym for FILTER */ ,iop_zff ,iop_zibfsize, iop_zibfsize ,iop_length, iop_length /* for ZLENGTH which is a synonym for LENGTH */ ,iop_zlisten, iop_zlisten ,iop_znodelay ,iop_nofilter /* for ZNOFILTER which is a synonym for NOFILTER */ ,iop_znoff ,iop_nowrap /* for ZNOWRAP which is a synonym for NOWRAP */ ,iop_width, iop_width /* for ZWIDTH which is a synonym for WIDTH */ ,iop_wrap, iop_wrap /* for ZWRAP which is a synonym for WRAP */ } ; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert((SIZEOF(dev_param_names) / SIZEOF(nametabent) == dev_param_index[26])); assert((SIZEOF(dev_param_data) / SIZEOF(unsigned char)) == dev_param_index[26]); is_parm_list = (TK_LPAREN == TREF(window_token)); if (is_parm_list) advancewindow(); cat_cnt = 0; parptr = parstr; parse_warn = FALSE; for (;;) { if ((TK_IDENT != TREF(window_token)) || (0 > (n = int_namelook(dev_param_index, dev_param_names, (TREF(window_ident)).addr, (TREF(window_ident)).len)))) { /* NOTE assignment above */ STX_ERROR_WARN(ERR_DEVPARUNK); /* sets "parse_warn" to TRUE */ break; } n = dev_param_data[n]; if (!(dev_param_control[n].valid_with & who_calls)) { STX_ERROR_WARN(ERR_DEVPARINAP); /* sets "parse_warn" to TRUE */ break; } advancewindow(); *parptr++ = n; if (io_params_size[n]) { if (TK_EQUAL != TREF(window_token)) { STX_ERROR_WARN(ERR_DEVPARVALREQ); /* sets "parse_warn" to TRUE */ break; } advancewindow(); if (EXPR_FAIL == expr(&x, MUMPS_EXPR)) return FALSE; assert(TRIP_REF == x.oprclass); if (OC_LIT == x.oprval.tref->opcode) { /* check to see if this string could overflow (5 is a int4 word plus a parameter code for safety) Must check before cvtparm, due to the fact that tmpmval could otherwise be garbage collected by a later putstr */ if (parptr - parstr + x.oprval.tref->operand[0].oprval.mlit->v.str.len + 5 > SIZEOF(parstr)) { cat_list[cat_cnt++] = put_str(parstr, INTCAST(parptr - parstr)); parptr = parstr; } assert(MLIT_REF == x.oprval.tref->operand[0].oprclass); status = cvtparm(n, &x.oprval.tref->operand[0].oprval.mlit->v, &tmpmval); if (status) { stx_error(status); return FALSE; } memcpy(parptr, tmpmval.str.addr, tmpmval.str.len); parptr += tmpmval.str.len; } else { if (parptr > parstr) { cat_list[cat_cnt++] = put_str(parstr, INTCAST(parptr - parstr)); parptr = parstr; } ref = newtriple(OC_CVTPARM); ref->operand[0] = put_ilit(n); ref->operand[1] = x; cat_list[cat_cnt++] = put_tref(ref); } } if (!is_parm_list) break; if (TK_COLON == TREF(window_token)) { advancewindow(); continue; } else if (TK_RPAREN == TREF(window_token)) { advancewindow(); break; } stx_error(ERR_RPARENMISSING); return FALSE; } if (parse_warn) { /* Parse the remaining arguments until the corresponding RIGHT-PAREN or SPACE or EOL is reached */ if (!parse_until_rparen_or_space()) return FALSE; if (TK_RPAREN == TREF(window_token)) advancewindow(); } *parptr++ = iop_eol; cat_list[cat_cnt++] = put_str(parstr,INTCAST(parptr - parstr)); if (cat_cnt <= 1) *c = cat_list[0]; else { ref = newtriple(OC_CAT); ref->operand[0] = put_ilit(cat_cnt + 1); *c = put_tref(ref); for (n = 0 ; n < cat_cnt ; n++) { parm = newtriple(OC_PARAMETER); ref->operand[1] = put_tref(parm); ref = parm; ref->operand[0] = cat_list[n]; } } return TRUE; } fis-gtm-V7.0-005/sr_port/deviceparameters.h0000755000032200000250000000116514342376331017545 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef DEVICEPARAMETERS_INCLUDED #define DEVICEPARAMETERS_INCLUDED int deviceparameters(oprtype *c, char who_calls); /***type int added***/ #endif /* DEVICEPARAMETERS_INCLUDED */ fis-gtm-V7.0-005/sr_port/devoptions.c0000644000032200000250000002007014342376331016400 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2022 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 #include "gtm_string.h" #include "error.h" #include "nametabtyp.h" #include "namelook.h" #include "io.h" #include "iosp.h" #include "io_params.h" #include "gtm_netdb.h" #include "gtm_socket.h" #include "gtm_un.h" #include "gtm_inet.h" #include "gtm_ipv6.h" #include "iosocketdef.h" #include "op.h" #include "mmemory.h" #include "mvalconv.h" #include "gtm_caseconv.h" #include "min_max.h" #include "gtm_time.h" #include "gtm_stdlib.h" error_def(ERR_DEVICEOPTION); #define OPTIONEND ',' #define OPTIONENDSTR "," #define OPTIONVALUE '=' #define OPTIONVALUESTR "=" #define DEVOPTIONITEM(A,B,C,D,E,F) {(SIZEOF(A) - 1), A} const nametabent devoption_names[] = { #include "devoptionstab.h" /* BYPASSOK */ }; #undef DEVOPTIONITEM const unsigned char devoption_indextab[] = { /* A B C D E F G H I J K L M N */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, /* O P Q R S T U V W X Y Z end */ 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5 }; #define DEVOPTIONITEM(A,B,C,D,E,F) B enum devoption_code { /* case labels */ #include "devoptionstab.h" /* BYPASSOK */ }; #undef DEVOPTIONITEM #define DEVOPTIONITEM(A,B,C,D,E,F) C static const enum io_dev_type devoption_device[] = { #include "devoptionstab.h" /* BYPASSOK */ }; #undef DEVOPTIONITEM #define DEVOPTIONITEM(A,B,C,D,E,F) D static const int devoption_command[] = { #include "devoptionstab.h" /* BYPASSOK */ }; #undef DEVOPTIONITEM #define DEVOPTIONITEM(A,B,C,D,E,F) E static const boolean_t devoption_defer[] = { #include "devoptionstab.h" /* BYPASSOK */ }; #undef DEVOPTIONITEM #define DEVOPTIONITEM(A,B,C,D,E,F) F static const int devoption_argtype[] = /* IOP_SRC_ */ { #include "devoptionstab.h" /* BYPASSOK */ }; #undef DEVOPTIONITEM #define ONEVALUEALLOWED "only one value allowed" #define UNKNOWNOPTION "unrecognized option" #define INVALIDNUMBER "invalid number" #define VALUEREQUIRED "value required" #define GET_OPTION_VALUE(OPTION, OPTIONLEN, RET, STATUS) \ MBSTART { /* OPTIONLEN not used since strtol stops on non digit */ \ STATUS = errno = 0; \ RET = STRTOL(OPTION, NULL, 0); \ if (0 != errno) \ { \ STATUS = errno; \ } \ } MBEND /* Parse OPTIONS device parameter arguments * * Note: while current options are all for socket devices, * * options for other device types may be added in * * the future. * * iod future: device descriptor if not socket * * socketptr socket descriptor or NULL if not socket * * optionstr OPTIONS device parameter * * caller where called from for error messages * * command future: IOP_command_OK from io_params.h * */ void devoptions(io_desc *iod, void *socketptrarg, mstr *optionstr, char *caller, int command) { int devopt_item, tmpnum, numret, index, index2, keywordend; int4 stat, len, len2; mstr keyword, options; boolean_t valuepresent; int optionvalue, valuelen, valuestart, local_errno; char *errortext; io_desc *socket_iod; d_socket_struct *dsocketptr; socket_struct *socketptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* future options may be for non socket devices */ if (socketptrarg) { socketptr = (socket_struct *)socketptrarg; dsocketptr = socketptr->dev; socket_iod = dsocketptr->iod; } else { if (NULL == iod) { /* nothing to do */ assert(socketptrarg && (NULL != iod)); return; } socketptr = NULL; dsocketptr = NULL; socket_iod = NULL; } options = *optionstr; /* option[=value],... */ for (index = 0; 0 < options.len ; options.len -= (index + 1), options.addr += (index + 1)) { /* comma separated options - need quotes if ever allow string values */ valuepresent = FALSE; keyword = options; for (index = 0; (index < options.len) && (OPTIONEND != options.addr[index]) ; index++) { if (OPTIONVALUE == keyword.addr[index]) { if (valuepresent) { /* only one value per option */ keywordend = valuestart - 1; /* remove = */ errortext = ONEVALUEALLOWED; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_DEVICEOPTION, 4, keywordend, keyword.addr, caller, errortext); } valuepresent = TRUE; valuestart = index + 1; keyword.len = index; /* continue scan for OPTIONEND */ } } if (valuepresent) valuelen = index - valuestart; else keyword.len = index; if ((devopt_item = namelook(devoption_indextab, devoption_names, keyword.addr, keyword.len)) < 0) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_DEVICEOPTION, 4, keyword.len, keyword.addr, caller, UNKNOWNOPTION); } switch (devopt_item) { case devopt_keepalive: if (!socketptr) break; /* ignore if not socket */ if (!valuepresent) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_DEVICEOPTION, 4, keyword.len, keyword.addr, caller, VALUEREQUIRED); } GET_OPTION_VALUE(&keyword.addr[valuestart], valuelen, optionvalue, local_errno); if (0 != local_errno) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_DEVICEOPTION, 4, keyword.len, keyword.addr, caller, INVALIDNUMBER); } socketptr->options_state.alive = SOCKOPTIONS_USER | SOCKOPTIONS_PENDING; socketptr->keepalive = optionvalue; break; case devopt_keepcnt: if (!socketptr) break; /* ignore if not socket */ if (!valuepresent) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_DEVICEOPTION, 4, keyword.len, keyword.addr, caller, VALUEREQUIRED); } GET_OPTION_VALUE(&keyword.addr[valuestart], valuelen, optionvalue, local_errno); if (0 != local_errno) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_DEVICEOPTION, 4, keyword.len, keyword.addr, caller, INVALIDNUMBER); } socketptr->options_state.cnt = SOCKOPTIONS_USER | SOCKOPTIONS_PENDING; socketptr->keepcnt = optionvalue; break; case devopt_keepidle: if (!socketptr) break; /* ignore if not socket */ if (!valuepresent) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_DEVICEOPTION, 4, keyword.len, keyword.addr, caller, VALUEREQUIRED); } GET_OPTION_VALUE(&keyword.addr[valuestart], valuelen, optionvalue, local_errno); if (0 != local_errno) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_DEVICEOPTION, 4, keyword.len, keyword.addr, caller, INVALIDNUMBER); } socketptr->options_state.idle = SOCKOPTIONS_USER | SOCKOPTIONS_PENDING; socketptr->keepidle = optionvalue; break; case devopt_keepintvl: if (!socketptr) break; /* ignore if not socket */ if (!valuepresent) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_DEVICEOPTION, 4, keyword.len, keyword.addr, caller, VALUEREQUIRED); } GET_OPTION_VALUE(&keyword.addr[valuestart], valuelen, optionvalue, local_errno); if (0 != local_errno) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_DEVICEOPTION, 4, keyword.len, keyword.addr, caller, INVALIDNUMBER); } socketptr->options_state.intvl = SOCKOPTIONS_USER | SOCKOPTIONS_PENDING; socketptr->keepintvl = optionvalue; break; case devopt_sndbuf: if (!socketptr) break; /* ignore if not socket */ if (!valuepresent) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_DEVICEOPTION, 4, keyword.len, keyword.addr, caller, VALUEREQUIRED); } GET_OPTION_VALUE(&keyword.addr[valuestart], valuelen, optionvalue, local_errno); if (0 != local_errno) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_DEVICEOPTION, 4, keyword.len, keyword.addr, caller, INVALIDNUMBER); } socketptr->options_state.sndbuf = SOCKOPTIONS_USER | SOCKOPTIONS_PENDING; socketptr->iobfsize = optionvalue; break; default: assert(TRUE || devopt_item); } } return; } fis-gtm-V7.0-005/sr_port/devoptionstab.h0000644000032200000250000000214114342376331017073 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2022 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. * * * ****************************************************************/ /* name symbol device where used defer argtype */ /* If argtype is zero, the item is either enabled or disabled */ DEVOPTIONITEM("KEEPALIVE", devopt_keepalive, gtmsocket, IOP_OPEN_OK|IOP_USE_OK, TRUE, IOP_SRC_INT), DEVOPTIONITEM("KEEPCNT", devopt_keepcnt, gtmsocket, IOP_OPEN_OK|IOP_USE_OK, TRUE, IOP_SRC_INT), DEVOPTIONITEM("KEEPIDLE", devopt_keepidle, gtmsocket, IOP_OPEN_OK|IOP_USE_OK, TRUE, IOP_SRC_INT), DEVOPTIONITEM("KEEPINTVL", devopt_keepintvl, gtmsocket, IOP_OPEN_OK|IOP_USE_OK, TRUE, IOP_SRC_INT), DEVOPTIONITEM("SNDBUF", devopt_sndbuf, gtmsocket, IOP_OPEN_OK|IOP_USE_OK, TRUE, IOP_SRC_INT) fis-gtm-V7.0-005/sr_port/dfa_calc.c0000755000032200000250000004612014342376331015731 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "copy.h" #include "patcode.h" #include "compiler.h" #include "gtm_string.h" /* the following macro checks that a 1 dimensional array reference is valid i.e. array[index] is within defined limits */ #define ASSERT_IF_1DIM_ARRAY_OVERFLOW(array, index) assert(index < ARRAYSIZE(array)) /* the following macro checks that a 2 dimensional array reference is valid i.e. array[row][col] is within defined limits The STATIC_ANALYSIS version works on the (speculative) theory that perhaps SCI might be happier if both indicies are checked together. */ #ifndef STATIC_ANALYSIS #define ASSERT_IF_2DIM_ARRAY_OVERFLOW(array, row, col) \ { \ assert((row) < ARRAYSIZE(array)); \ assert((col) < ARRAYSIZE(array[0])); \ } #else #define ASSERT_IF_2DIM_ARRAY_OVERFLOW(array, row, col) \ { \ if ((row >= ARRAYSIZE(array)) || (col >= ARRAYSIZE(array[0]))) \ assert(((row) < ARRAYSIZE(array)) && ((col) < ARRAYSIZE(array[0]))); \ } #endif /* Note: in various places, dfa_calc() makes a reference to the array 'typemask'. dfa_calc() is executed at compile-time. * The content of the array typemask is static, but, at run-time, the pointer that is used to access the typemask array * (pattern_typemask) may change whenever a program executes the command View "PATCODE":tablename. * As a result, the pattern masks that the GT.M compiler uses may differ from the ones that are in operation at run-time. */ LITREF uint4 typemask[PATENTS]; static uint4 classmask[CHAR_CLASSES] = { PATM_N, PATM_P, PATM_L, PATM_U, PATM_C, PATM_B, PATM_D, PATM_F, PATM_G, PATM_H, PATM_I, PATM_J, PATM_K, PATM_M, PATM_O, PATM_Q, PATM_R, PATM_S, PATM_T, PATM_V, PATM_W, PATM_X, PATM_UTF8_ALPHABET, PATM_UTF8_NONBASIC }; /* This procedure is part of the MUMPS compiler. The function of this procedure is to build the data structures that * will be used to drive the DFA engine that can evaluate certain pattern matches. Note that this routine operates * at compile-time, and that all data structures built in this procedure are compiled at the end into a terse string * of values that will be passed to do_pattern (through patstr). do_pattern(), which operates at run-time will * interpret this string of values and do the actual DFA work (DFA = Discrete Finite Automaton). * * Refer to the section in Aho & Sehti compiler book on going directly "From a regular expression to a DFA" for * a description of how the below algorithm works. */ int dfa_calc(struct leaf *leaves, int leaf_num, struct e_table *expand, uint4 **fstchar_ptr, uint4 **outchar_ptr) { uint4 *locoutchar; uint4 pattern_mask; unsigned char *textstring; int offset[MAX_DFA_STATES + 2]; int pos_offset[CHAR_CLASSES]; int fst[2][2], lst[2][2]; int4 charcls, maskcls, numexpand, count, clsnum, maxcls, clsposlis; int4 state_num, node_num, sym_num, expseq, seq; int4 letter_cond; struct node nodes; /* EdM: comment for reviewers: * 'states' is currently defined as a boolean_t. * In the original version it was a bool (== char). * Since comparisons on this array are done using * memcmp, and the only values assigned to elements * in this array are TRUE and FALSE (1 and 0), * we might consider declaring states as a 'char' * array after all... */ boolean_t states[MAX_DFA_STATES][MAX_DFA_STATES]; boolean_t fpos[MAX_DFA_STATES][MAX_DFA_STATES]; int d_trans[MAX_DFA_STATES][MAX_DFA_STATES]; int pos_lis[MAX_DFA_STATES][MAX_DFA_STATES]; struct c_trns_tb c_trans; locoutchar = *outchar_ptr; if (0 == leaf_num) return -1; if (leaf_num > 1) { pattern_mask = PATM_DFA; state_num = 1; leaves->nullable[leaf_num] = FALSE; leaves->letter[leaf_num][0] = DFABIT; leaves->letter[leaf_num][1] = -1; pos_offset[0] = 0; for (seq = 1; seq < CHAR_CLASSES; seq++) pos_offset[seq] = pos_offset[seq - 1] + expand->num_e[seq - 1]; memset(&nodes, 0, SIZEOF(nodes)); memset(&fpos[0][0], 0, SIZEOF(fpos)); memset(&states[0][0], 0, SIZEOF(states)); memset(&d_trans[0][0], 128, SIZEOF(d_trans)); memset(&pos_lis[0][0], 128, SIZEOF(pos_lis)); memset(&c_trans.c[0], 0, SIZEOF(c_trans.c)); memset(offset, 0, SIZEOF(offset)); memset(fst, 0, SIZEOF(fst)); memset(lst, 0, SIZEOF(lst)); charcls = 0; clsnum = 0; maxcls = 0; nodes.nullable[0] = leaves->nullable[0] & leaves->nullable[1]; states[state_num][charcls] = TRUE; for (maskcls = 0; leaves->letter[0][maskcls] >= 0; maskcls++) { if (!(leaves->letter[0][maskcls] & DFABIT)) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(fpos, charcls, charcls + 1); fpos[charcls][charcls + 1] = TRUE; lst[FST][FST] = charcls; lst[FST][LST] = charcls; assert(leaves->letter[0][maskcls] >= 0 && leaves->letter[0][maskcls] < SIZEOF(typemask)); seq = patmaskseq(typemask[leaves->letter[0][maskcls]]); if (seq < 0) seq = 0; for (numexpand = 1; expand->meta_c[seq][numexpand] != leaves->letter[0][maskcls]; numexpand++) ; ASSERT_IF_2DIM_ARRAY_OVERFLOW(expand->meta_c, seq, numexpand); assert(CHAR_CLASSES > seq); ASSERT_IF_1DIM_ARRAY_OVERFLOW(pos_lis, (pos_offset[seq] + numexpand)); for (count = 0; pos_lis[pos_offset[seq] + numexpand][count] >= 0; count++) ; ASSERT_IF_2DIM_ARRAY_OVERFLOW(pos_lis, (pos_offset[seq] + numexpand), count); pos_lis[pos_offset[seq] + numexpand][count] = charcls; charcls++; } else { seq = patmaskseq(leaves->letter[0][maskcls]); if (seq < 0) { seq = 0; expseq = 0; } else expseq = expand->num_e[seq]; for (numexpand = 0; numexpand < expseq; numexpand++) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(states, state_num, charcls); states[state_num][charcls] = TRUE; fst[FST][LST] = charcls; lst[FST][LST] = charcls; ASSERT_IF_1DIM_ARRAY_OVERFLOW(pos_lis, (pos_offset[seq] + numexpand)); for (count = 0; pos_lis[pos_offset[seq] + numexpand][count] >= 0; count++) ; ASSERT_IF_2DIM_ARRAY_OVERFLOW(pos_lis, (pos_offset[seq] + numexpand), count); pos_lis[pos_offset[seq] + numexpand][count] = charcls; charcls++; } } } fst[LST][FST] = charcls; fst[LST][LST] = charcls; lst[LST][FST] = charcls; if (!leaves->nullable[1]) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(nodes.last, 0, charcls); nodes.last[0][charcls] = TRUE; maxcls = charcls; } for (maskcls = 0; leaves->letter[1][maskcls] >= 0; maskcls++) { if (!(leaves->letter[1][maskcls] & DFABIT)) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(fpos, charcls, charcls + 1); fpos[charcls][charcls + 1] = TRUE; lst[LST][FST] = charcls; lst[LST][LST] = charcls; assert(leaves->letter[1][maskcls] >= 0 && leaves->letter[1][maskcls] < SIZEOF(typemask)); seq = patmaskseq(typemask[leaves->letter[1][maskcls]]); if (seq < 0) seq = 0; for (numexpand = 1; expand->meta_c[seq][numexpand] != leaves->letter[1][maskcls]; numexpand++) ; ASSERT_IF_2DIM_ARRAY_OVERFLOW(expand->meta_c, seq, numexpand); assert(CHAR_CLASSES > seq); ASSERT_IF_1DIM_ARRAY_OVERFLOW(pos_lis, (pos_offset[seq] + numexpand)); for (count = 0; pos_lis[pos_offset[seq] + numexpand][count] >= 0; count++) ; ASSERT_IF_2DIM_ARRAY_OVERFLOW(pos_lis, (pos_offset[seq] + numexpand), count); pos_lis[pos_offset[seq] + numexpand][count] = charcls; charcls++; } else { seq = patmaskseq(leaves->letter[1][maskcls]); if (seq < 0) { seq = 0; expseq = 0; } else expseq = expand->num_e[seq]; for (numexpand = 0; numexpand < expseq; numexpand++) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(nodes.last, 0, charcls); nodes.last[0][charcls] = TRUE; fst[LST][LST] = charcls; lst[LST][LST] = charcls; ASSERT_IF_1DIM_ARRAY_OVERFLOW(pos_lis, (pos_offset[seq] + numexpand)); for (count = 0; pos_lis[pos_offset[seq] + numexpand][count] >= 0; count++) ; ASSERT_IF_2DIM_ARRAY_OVERFLOW(pos_lis, (pos_offset[seq] + numexpand), count); pos_lis[pos_offset[seq] + numexpand][count] = charcls; charcls++; } } } if (leaves->nullable[0]) { assert(MAX_DFA_STATES > lst[FST][LST]); assert(MAX_DFA_STATES > fst[LST][LST]); for (numexpand = lst[FST][FST]; numexpand <= lst[FST][LST]; numexpand++) { for (count = fst[FST][FST]; count <= fst[FST][LST]; count++) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(fpos, numexpand, count); fpos[numexpand][count] = TRUE; } } for (numexpand = fst[LST][FST]; numexpand <= fst[LST][LST]; numexpand++) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(states, state_num, numexpand); states[state_num][numexpand] = TRUE; } } if (leaves->nullable[1]) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(nodes.last, 0, charcls - 1); nodes.last[0][charcls - 1] = TRUE; for (numexpand = lst[LST][FST]; numexpand <= lst[LST][LST]; numexpand++) { for (count = fst[LST][FST]; count <= fst[LST][LST]; count++) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(fpos, numexpand, count); fpos[numexpand][count] = TRUE; } } for (numexpand = lst[FST][FST]; numexpand <= lst[FST][LST]; numexpand++) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(nodes.last, 0, numexpand); nodes.last[0][numexpand] = TRUE; } maxcls = charcls; } for (numexpand = lst[FST][FST]; numexpand <= lst[FST][LST]; numexpand++) { for (count = fst[LST][FST]; count <= fst[LST][LST]; count++) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(fpos, numexpand, count); fpos[numexpand][count] = TRUE; } } if (!leaves->nullable[1]) clsnum = lst[LST][FST]; for (node_num = 1; node_num < leaf_num; node_num++) { ASSERT_IF_1DIM_ARRAY_OVERFLOW(nodes.nullable, node_num); ASSERT_IF_1DIM_ARRAY_OVERFLOW(leaves->nullable, node_num + 1); nodes.nullable[node_num] = nodes.nullable[node_num - 1] & leaves->nullable[node_num + 1]; if (leaves->nullable[node_num + 1]) { for (maskcls = 0; maskcls < charcls; maskcls++) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(nodes.last, node_num, maskcls); /* For SCI */ ASSERT_IF_2DIM_ARRAY_OVERFLOW(nodes.last, node_num -1, maskcls); nodes.last[node_num][maskcls] = nodes.last[node_num - 1][maskcls]; } } else { ASSERT_IF_2DIM_ARRAY_OVERFLOW(nodes.last, node_num, charcls); nodes.last[node_num][charcls] = TRUE; maxcls = charcls; } fst[LST][FST] = charcls; fst[LST][LST] = charcls; lst[LST][FST] = charcls; /* For SCI we need to factor out the loop condition so we can protect it with asserts Note this breaks if a 'continue' is ever added to short-circuit the loop */ ASSERT_IF_2DIM_ARRAY_OVERFLOW(leaves->letter, node_num + 1, 0); letter_cond = leaves->letter[node_num + 1][0]; for (maskcls = 0; letter_cond >= 0; maskcls++) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(leaves->letter, node_num + 1, maskcls); if (!(leaves->letter[node_num + 1][maskcls] & DFABIT)) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(fpos, charcls, charcls + 1); fpos[charcls][charcls + 1] = TRUE; lst[LST][FST] = charcls; lst[LST][LST] = charcls; assert((0 <= leaves->letter[node_num + 1][maskcls]) && (leaves->letter[node_num + 1][maskcls] < SIZEOF(typemask))); seq = patmaskseq(typemask[leaves->letter[node_num + 1][maskcls]]); if (seq < 0) seq = 0; for (numexpand = 1; expand->meta_c[seq][numexpand] != leaves->letter[node_num + 1][maskcls]; numexpand++) ; ASSERT_IF_2DIM_ARRAY_OVERFLOW(expand->meta_c, seq, numexpand); assert(CHAR_CLASSES > seq); ASSERT_IF_1DIM_ARRAY_OVERFLOW(pos_lis, (pos_offset[seq] + numexpand)); for (count = 0; pos_lis[pos_offset[seq] + numexpand][count] >= 0; count++) ; ASSERT_IF_2DIM_ARRAY_OVERFLOW(pos_lis, (pos_offset[seq] + numexpand), count); pos_lis[pos_offset[seq] + numexpand][count] = charcls; charcls++; } else { seq = patmaskseq(leaves->letter[node_num + 1][maskcls]); if (seq < 0) { seq = 0; expseq = 0; } else expseq = expand->num_e[seq]; for (numexpand = 0; numexpand < expseq; numexpand++) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(nodes.last, node_num, charcls); nodes.last[node_num][charcls] = TRUE; if (nodes.nullable[node_num - 1]) states[state_num][charcls] = TRUE; fst[LST][LST] = charcls; lst[LST][LST] = charcls; for (count = 0; pos_lis[pos_offset[seq] + numexpand][count] >= 0; count++) ; ASSERT_IF_2DIM_ARRAY_OVERFLOW(pos_lis, (pos_offset[seq] + numexpand), count); pos_lis[pos_offset[seq] + numexpand][count] = charcls; charcls++; } } ASSERT_IF_2DIM_ARRAY_OVERFLOW(leaves->letter, node_num + 1, maskcls + 1); /* anticipate ++maskcls */ letter_cond = leaves->letter[node_num + 1][maskcls + 1]; } if (nodes.nullable[node_num - 1]) { for (numexpand = fst[LST][FST]; numexpand <= fst[LST][LST]; numexpand++) states[state_num][numexpand] = TRUE; ASSERT_IF_1DIM_ARRAY_OVERFLOW(states[state_num], numexpand); } ASSERT_IF_1DIM_ARRAY_OVERFLOW(leaves->nullable, node_num + 1); if (leaves->nullable[node_num + 1]) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(nodes.last, node_num, charcls - 1); nodes.last[node_num][charcls - 1] = TRUE; for (numexpand = lst[LST][FST]; numexpand <= lst[LST][LST]; numexpand++) { for (count = fst[LST][FST]; count <= fst[LST][LST]; count++) fpos[numexpand][count] = TRUE; ASSERT_IF_2DIM_ARRAY_OVERFLOW(fpos, numexpand, count); } maxcls = charcls; } for (numexpand = clsnum; numexpand < maxcls; numexpand++) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(fpos, numexpand, count); for (count = fst[LST][FST]; count <= fst[LST][LST]; count++) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(nodes.last, node_num - 1, numexpand); if (nodes.last[node_num - 1][numexpand]) fpos[numexpand][count] = TRUE; } ASSERT_IF_2DIM_ARRAY_OVERFLOW(fpos, numexpand, count); } ASSERT_IF_1DIM_ARRAY_OVERFLOW(leaves->nullable, node_num + 1); if (!leaves->nullable[node_num + 1]) clsnum = lst[LST][FST]; } sym_num = charcls; state_num++; for (seq = 1; seq < state_num; seq++) { charcls = 0; ASSERT_IF_1DIM_ARRAY_OVERFLOW(offset, seq + 1); offset[seq + 1]++; offset[seq + 1] += offset[seq]; for (maskcls = 0; maskcls < CHAR_CLASSES; maskcls++) { if (expand->num_e[maskcls] > 0) { for (numexpand = 0; numexpand < expand->num_e[maskcls]; numexpand++) { for (maxcls = 0; pos_lis[charcls + numexpand][maxcls] >= 0; maxcls++) { clsposlis = pos_lis[charcls + numexpand][maxcls]; if (states[seq][clsposlis]) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(fpos, clsposlis, clsnum); for (clsnum = 0; clsnum <= sym_num; clsnum++) states[state_num][clsnum] |= fpos[clsposlis][clsnum]; ASSERT_IF_2DIM_ARRAY_OVERFLOW(states, state_num, clsnum); } } ASSERT_IF_2DIM_ARRAY_OVERFLOW(pos_lis, charcls + numexpand, maxcls); ASSERT_IF_1DIM_ARRAY_OVERFLOW(states[state_num], clsnum); ASSERT_IF_1DIM_ARRAY_OVERFLOW(states, state_num); for (count = 0; memcmp(states[count], states[state_num], (sym_num + 1) * SIZEOF(boolean_t)) && (count < state_num); count++) ; if (count > 0) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(d_trans, seq, charcls); if (0 == numexpand) { d_trans[seq][charcls] = count; ASSERT_IF_1DIM_ARRAY_OVERFLOW(c_trans.c, seq); for (clsnum = 0; (clsnum < c_trans.c[seq]) && (c_trans.trns[seq][clsnum] != count); clsnum++) ; ASSERT_IF_2DIM_ARRAY_OVERFLOW(c_trans.trns, seq, clsnum); ASSERT_IF_2DIM_ARRAY_OVERFLOW(c_trans.p_msk, seq, clsnum); if (clsnum == c_trans.c[seq]) { c_trans.p_msk[seq][clsnum] = classmask[maskcls]; c_trans.trns[seq][clsnum] = count; offset[seq + 1] += 2; c_trans.c[seq]++; } else c_trans.p_msk[seq][clsnum] |= classmask[maskcls]; } else if (d_trans[seq][charcls] != count) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(d_trans, seq, charcls + numexpand); d_trans[seq][charcls + numexpand] = count; offset[seq + 1] += 3; } if (count == state_num) { state_num++; if (state_num >= ARRAYSIZE(states)) return -1; } else memset(states[state_num], 0, (sym_num + 1) * SIZEOF(states[0][0])); } } charcls += expand->num_e[maskcls]; } } } *outchar_ptr += offset[state_num] + 2; if ((*outchar_ptr - *fstchar_ptr > MAX_DFA_SPACE) || ((offset[state_num] + 1) > (MAX_PATTERN_LENGTH / 2))) return -1; *locoutchar++ = pattern_mask; *locoutchar++ = offset[state_num]; for (seq = 1; seq < state_num; seq++) { charcls = 0; for (numexpand = 0; numexpand < CHAR_CLASSES; numexpand++) { if (expand->num_e[numexpand] > 1) { for (count = 1; count < expand->num_e[numexpand]; count++) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(d_trans, seq, charcls + count); if (d_trans[seq][charcls + count] >= 0) { *locoutchar++ = PATM_STRLIT; *locoutchar++ = expand->meta_c[numexpand][count]; *locoutchar++ = offset[d_trans[seq][charcls + count]]; } } } charcls += expand->num_e[numexpand]; } for (numexpand = 0; numexpand < c_trans.c[seq]; numexpand++) { *locoutchar++ = c_trans.p_msk[seq][numexpand]; *locoutchar++ = offset[c_trans.trns[seq][numexpand]]; } *locoutchar++ = (states[seq][sym_num]) ? PATM_ACS : PATM_DFA; } assert(MAX_DFA_SPACE >= (locoutchar - *fstchar_ptr)); return 1; } else { pattern_mask = 0; *outchar_ptr += 1; maskcls = 1; if (!(leaves->letter[0][0] & DFABIT)) { pattern_mask = PATM_STRLIT; for (maskcls = 0; leaves->letter[0][maskcls] >= 0; maskcls++) ; *outchar_ptr += PAT_STRLIT_PADDING + ((maskcls + SIZEOF(uint4) - 1) / SIZEOF(uint4)); } else { for (numexpand = 0; leaves->letter[0][numexpand] >= 0; numexpand++) pattern_mask |= leaves->letter[0][numexpand]; } if (*outchar_ptr - *fstchar_ptr > MAX_PATTERN_LENGTH) return -1; *locoutchar++ = pattern_mask; if (PATM_STRLIT & pattern_mask) { *locoutchar++ = maskcls; /* bytelen */ *locoutchar++ = maskcls; /* charlen */ *locoutchar++ = 0; /* both NONASCII and BADCHAR flags are absent since this is indeed a valid ASCII string or else we would not have come to dfa_calc (and hence there are no bad chars) */ assert(3 == PAT_STRLIT_PADDING); textstring = (unsigned char *)locoutchar; /* change pointer type */ for (numexpand = 0; numexpand < maskcls; numexpand++) { ASSERT_IF_2DIM_ARRAY_OVERFLOW(leaves->letter, 0, numexpand); *textstring++ = leaves->letter[0][numexpand]; } } return maskcls; } } fis-gtm-V7.0-005/sr_port/dh.mpt0000644000032200000250000000246414342376331015166 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 1987-2019 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %DH ;GT.M %DH utility - decimal to hexadecimal conversion program ;invoke with %DH in decimal and %DL digits to return %DH in hexadecimal ;invoke at INT to execute interactively ;invoke at FUNC as an extrinsic function ;if you make heavy use of this routine, consider $ZCALL ; set %DH=$$FUNC(%DH,$get(%DL,8)) quit INT new %DL read !,"Decimal: ",%DH read !,"Digits: ",%DL set:""=%DL %DL=8 set %DH=$$FUNC(%DH,%DL) q FUNC(d,l) new h,i,s set h="",l=$get(l,8),s=0 if "0"[$get(d) set $piece(s,0,l+1)="" quit s set:"-"=$extract(d) s="F",d=$extract(d,2,9999) if (18>$length(d)) for quit:'d set h=$extract("0123456789ABCDEF",(d#16)+1)_h,d=d\16 else set h=$$CONVERTBASE^%CONVBASEUTIL(d,10,16) set:(("F"=s)&("0"'=h)) h=$$CONVNEG^%CONVBASEUTIL(h,16) set i=$length(h) quit:l'>i h set $piece(s,s,l-i+1)="" quit s_h fis-gtm-V7.0-005/sr_port/dm_read.h0000755000032200000250000000104714342376331015614 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef DM_READ_INCLUDED #define DM_READ_INCLUDED void dm_read(mval *v); #endif /* DM_READ_INCLUDED */ fis-gtm-V7.0-005/sr_port/dse_getki.c0000644000032200000250000001534314342376333016155 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_string.h" #include "gtm_strings.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_ctype.h" #include "gtm_facility.h" #include "gtm_stdlib.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "util.h" #include "cli.h" #include "stringpool.h" #include "dse.h" #include "mvalconv.h" #include "op.h" #include "format_targ_key.h" #include "gvcst_protos.h" #ifdef GTM_TRIGGER #include "hashtab_mname.h" #include #include "targ_alloc.h" #endif #ifdef UTF8_SUPPORTED #include "gtm_utf8.h" #endif GBLREF gv_key *gv_currkey; GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF gv_namehead *gv_target; int dse_getki(char *dst, int *len, char *qual, int qual_len) { char buf[MAX_ZWR_KEY_SZ], *src, *temp_dst, *bot, *top, *tmp, slit[MAX_KEY_SZ + 1], key_buf[MAX_KEY_SZ + 1]; short int max_key; unsigned short buf_len; int dlr_num, dlr_len; int num; unsigned char *ptr; mval key_subsc; span_subs subs; buf_len = SIZEOF(buf); if (!cli_get_str(qual, buf, &buf_len)) return FALSE; bot = temp_dst = (char *)&key_buf[0]; top = &buf[buf_len]; src = &buf[0]; if ('^' != *src++) { util_out_print("Error: invalid key.", TRUE); return FALSE; } if ((('A' <= *src) && ('Z' >= *src)) || (('a' <= *src) && ('z' >= *src)) || ('%' == *src) || ('#' == *src)) /* first letter must be an alphabet or % or # */ { *temp_dst++ = *src++; } else { util_out_print("Error: invalid key.", TRUE); return FALSE; } for ( ; ('(' != *src) && (src < top); src++) { if ((('A' <= *src) && ('Z' >= *src)) || (('a' <= *src) && ('z' >= *src)) || (('0' <= *src) && ('9' >= *src))) { *temp_dst = *src; } else { util_out_print("Error: invalid key.", TRUE); return FALSE; } temp_dst++; } *temp_dst = '\0'; gv_target = dse_find_gvt(gv_cur_region, bot, (int)(temp_dst - bot)); SET_GV_CURRKEY_FROM_GVT(gv_target); /* gv_currkey is needed by GVCST_ROOT_SEARCH AND code after it */ GVCST_ROOT_SEARCH; /* sets up gv_target->collseq (needed by mval2subsc below ) */ bot = (char *)&gv_currkey->base[0]; temp_dst = (char *)&gv_currkey->base[0] + gv_currkey->end; max_key = gv_cur_region->max_key_size; if ('(' == *src) { src++; for (;;) { key_subsc.mvtype = MV_STR; if ('$' == *src) /* may be a $char() */ { src++; if ((dlr_len = parse_dlr_char(src, top, slit)) > 0) { key_subsc.str.addr = slit; key_subsc.str.len = STRLEN(slit); src += dlr_len; } else { util_out_print("Error: invalid key.", TRUE); return FALSE; } } else if ('#' == *src) { /*Special spanning global subscript*/ if ('S' != TOUPPER(*(src + 1)) && 'P' != TOUPPER(*(src + 2)) && 'A' != TOUPPER(*(src + 3)) && 'N' != TOUPPER(*(src + 4))) { util_out_print("Error: invalid key.", TRUE); return FALSE; } src = src + SPAN_SUBS_LEN + 1; for (num = 0, src++; ')' != *src; num = (num * DECIMAL_BASE + (int)(*src++ - ASCII_0))) ; ptr = gv_currkey->base + gv_currkey->end; num = num - 1; SPAN_INITSUBS(&subs, num); SPAN_SUBSCOPY_SRC2DST(ptr, (unsigned char *)&subs); ptr = ptr + SPAN_SUBS_LEN; *ptr++ = KEY_DELIMITER; *ptr = KEY_DELIMITER; gv_currkey->end = ptr - gv_currkey->base; break; } else if ('\"' != *src) /* numerical subscript */ { for (key_subsc.str.addr = src ; (')' != *src) && (',' != *src); src++) { if (src == top || (('0' > *src) || ('9' < *src)) && ('-' != *src) && ('.' != *src)) { util_out_print("Error: invalid key.", TRUE); return FALSE; } } key_subsc.str.len = INTCAST(src - key_subsc.str.addr); s2n(&key_subsc); key_subsc.mvtype &= MV_NUM_MASK; } else { src++; tmp = slit; for (;;) { if (src == top) { util_out_print("Error: invalid key.", TRUE); return FALSE; } if ('\"' == *src) if ('\"' != *++src) break; *tmp++ = *src++; } key_subsc.str.addr = slit; key_subsc.str.len = INTCAST(tmp - slit); } if ( 0 == key_subsc.str.len && NEVER == cs_addrs->hdr->null_subs) { util_out_print("Error: Null subscripts not allowed", TRUE); return FALSE; } mval2subsc(&key_subsc, gv_currkey, gv_cur_region->std_null_coll); if (gv_currkey->end >= max_key) ISSUE_GVSUBOFLOW_ERROR(gv_currkey, KEY_COMPLETE_TRUE); if (',' != *src) break; src++; } if (')' != *src++) { util_out_print("Error: invalid key.", TRUE); return FALSE; } temp_dst = (char *)&gv_currkey->base[0] + gv_currkey->end; } if (src != top) { util_out_print("Error: invalid key.", TRUE); return FALSE; } *len = (int)(temp_dst - bot + 1); assert(3 <= *len); assert(KEY_DELIMITER != gv_currkey->base[*len - 3]); assert(KEY_DELIMITER == gv_currkey->base[*len - 2]); assert(KEY_DELIMITER == gv_currkey->base[*len - 1]); memcpy(dst, &gv_currkey->base[0], *len); return TRUE; } int parse_dlr_char(char *src, char *top, char *dlr_subsc) { int indx = 0, dlr_len, dlr_val, harlen; char lcl_buf[MAX_KEY_SZ + 1]; char *tmp_buf, *strnext; boolean_t dlrzchar = FALSE; tmp_buf = src; if ('Z' == TOUPPER(*tmp_buf)) { dlrzchar = TRUE; tmp_buf++; } if ('C' != TOUPPER(*tmp_buf++)) return 0; if ('H' == TOUPPER(*tmp_buf)) { if (top - tmp_buf <= STR_LIT_LEN("har") || STRNCASECMP(tmp_buf, "har", STR_LIT_LEN("har"))) { if (!dlrzchar) return 0; tmp_buf++; } else tmp_buf += STR_LIT_LEN("har"); } else if (dlrzchar) return 0; if (*tmp_buf++ != '(') return 0; if (!ISDIGIT_ASCII(*tmp_buf)) return 0; while (tmp_buf != top) { if (ISDIGIT_ASCII(*tmp_buf)) lcl_buf[indx++] = *tmp_buf; else if (',' == *tmp_buf || ')' == *tmp_buf) { lcl_buf[indx] = '\0'; dlr_val = ATOI(lcl_buf); if (0 > dlr_val) return 0; if (!gtm_utf8_mode || dlrzchar) { if (255 < dlr_val) return 0; *dlr_subsc++ = dlr_val; } # ifdef UTF8_SUPPORTED else { strnext = (char *)UTF8_WCTOMB(dlr_val, dlr_subsc); if (strnext == dlr_subsc) return 0; dlr_subsc = strnext; } # endif indx = 0; if (')' == *tmp_buf) { *dlr_subsc = '\0'; break; } } else return 0; tmp_buf++; } if (tmp_buf == top) return 0; tmp_buf++; return (int)(tmp_buf - src); } fis-gtm-V7.0-005/sr_port/dm_setup.h0000755000032200000250000000117014342376331016036 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef DM_SETUP_H_INCLUDED #define DM_SETUP_H_INCLUDED #define GTM_DMOD "GTM$DMOD" void dm_setup(void); #endif fis-gtm-V7.0-005/sr_port/do.mpt0000755000032200000250000000304414342376331015173 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 1987-2019 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %DO ;GT.M %DO utility - decimal to octal conversion program ;invoke with %DO in decimal and %DL digits to return %DO in octal ;invoke at INT to execute interactively ;invoke at FUNC as an extrinsic function ;if you make heavy use of this routine, consider $ZCALL ; set %DO=$$FUNC(%DO,$get(%DL,12)) quit INT new %DL read !,"Decimal: ",%DO read !,"Digits: ",%DL set:""=%DL %DL=12 set %DO=$$FUNC(%DO,%DL) quit FUNC(d,l) new i,o,s set o="",l=$get(l,12),s=0 if "0"[$get(d) set $piece(s,0,l+1)="" quit s set:"-"=$extract(d) s="7",d=$extract(d,2,9999) if (18>$length(d)) for quit:'d set o=d#8_o,d=d\8 else set o=$$CONVERTBASE^%CONVBASEUTIL(d,10,8) set:(("7"=s)&("0"'=o)) o=$$CONVNEG^%CONVBASEUTIL(o,8) set i=$length(o) quit:l'>i o set $piece(s,s,l-i+1)="" quit s_o fis-gtm-V7.0-005/sr_port/do_indir_do.c0000644000032200000250000001132714342376333016466 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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_stdio.h" #include "gtm_string.h" #include "toktyp.h" #include #include "stack_frame.h" #include "indir_enum.h" #include "cmd_qlf.h" #include "gtm_caseconv.h" #include "op.h" #include "do_indir_do.h" #include "valid_mname.h" #include "linktrc.h" GBLREF stack_frame *frame_pointer; GBLREF command_qualifier cmd_qlf; GBLREF boolean_t is_tracing_on; int do_indir_do(mval *v, unsigned char argcode) { mval label; lnr_tabent USHBIN_ONLY(*)*addr; mident_fixed ident; rhdtyp *current_rhead; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (valid_labname(&v->str)) { memcpy(ident.c, v->str.addr, v->str.len); if (!(cmd_qlf.qlf & CQ_LOWER_LABELS)) lower_to_upper((uchar_ptr_t)ident.c, (uchar_ptr_t)ident.c, v->str.len); label.mvtype = MV_STR; label.str.len = v->str.len; label.str.addr = &ident.c[0]; # ifdef AUTORELINK_SUPPORTED DBGINDCOMP((stderr, "do_indir_do: routine resolved to 0x"lvaddr"\n", frame_pointer->rvector)); TADR(lnk_proxy)->rtnhdr_adr = frame_pointer->rvector; /* So op_labaddr has access to it */ op_labaddr(0, &label, 0); addr = &TADR(lnk_proxy)->lnr_adr; # else addr = op_labaddr(frame_pointer->rvector, &label, 0); # endif current_rhead = CURRENT_RHEAD_ADR(frame_pointer->rvector); if (argcode == indir_do) { /* If we aren't in an indirect, exfun_frame() is the best way to copy the stack frame as it does not * require re-allocation of the various tables (temps, linkage, literals, etc). But if we are in an * indirect, the various stackframe fields cannot be copied as the indirect has different values so * re-create the frame from the values in the routine header via new_stack_frame(). */ if (!(frame_pointer->flags & SFF_INDCE)) { if (!is_tracing_on) exfun_frame(); else exfun_frame_sp(); } else { if (!is_tracing_on) { new_stack_frame(CURRENT_RHEAD_ADR(frame_pointer->rvector), # ifdef HAS_LITERAL_SECT (unsigned char *)LINKAGE_ADR(current_rhead), # else PTEXT_ADR(current_rhead), # endif USHBIN_ONLY(LINE_NUMBER_ADDR(current_rhead, *addr)) /* On non-shared binary calculate the transfer address to be passed to * new_stack_frame as follows: * 1) get the number stored at addr; this is the offset to the line number * entry * 2) add the said offset to the address of the routine header; this is the * address of line number entry * 3) dereference the said address to get the line number of the actual * program * 4) add the said line number to the address of the routine header */ NON_USHBIN_ONLY((unsigned char *)((char *)current_rhead + *(int4 *)((char *)current_rhead + *addr)))); } else { new_stack_frame_sp(CURRENT_RHEAD_ADR(frame_pointer->rvector), # ifdef HAS_LITERAL_SECT (unsigned char *)LINKAGE_ADR(current_rhead), # else PTEXT_ADR(current_rhead), # endif USHBIN_ONLY(LINE_NUMBER_ADDR(current_rhead, *addr)) /* On non-shared binary calculate the transfer address to be passed to * new_stack_frame as follows: * 1) get the number stored at addr; this is the offset to the line * number entry * 2) add the said offset to the address of the routine header; this is * the address of line number entry * 3) dereference the said address to get the line number of the actual * program * 4) add the said line number to the address of the routine header */ NON_USHBIN_ONLY((unsigned char *)((char *)current_rhead + *(int4 *)((char *)current_rhead + *addr)))); } return TRUE; } } /* On non-shared binary calculate the mpc pointer similarly to the descriptions above. */ frame_pointer->mpc = USHBIN_ONLY(LINE_NUMBER_ADDR(current_rhead, *addr)) NON_USHBIN_ONLY((unsigned char *)((char *)current_rhead + *(int4 *)((char *)current_rhead + *addr))); # ifdef HAS_LITERAL_SECT frame_pointer->ctxt = (unsigned char *)LINKAGE_ADR(current_rhead); # else frame_pointer->ctxt = PTEXT_ADR(current_rhead); # endif return TRUE; } else return FALSE; } fis-gtm-V7.0-005/sr_port/do_indir_do.h0000755000032200000250000000114514342376331016471 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef DO_INDIR_DO_INCLUDED #define DO_INDIR_DO_INCLUDED int do_indir_do(mval *v, unsigned char argcode); /***type int added***/ #endif /* DO_INDIR_DO_INCLUDED */ fis-gtm-V7.0-005/sr_port/do_patalt.c0000644000032200000250000003376214342376331016171 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" /* for memset */ #include "copy.h" #include "patcode.h" #ifdef UTF8_SUPPORTED #include "gtm_utf8.h" #endif /* see corresponding GBLDEFs in gbldefs.c for comments on the caching mechanism */ GBLREF int4 curalt_depth; /* depth of alternation nesting */ GBLREF int4 do_patalt_calls[PTE_MAX_CURALT_DEPTH]; /* number of calls to do_patalt() */ GBLREF int4 do_patalt_hits[PTE_MAX_CURALT_DEPTH]; /* number of pte_csh hits in do_patalt() */ GBLREF int4 do_patalt_maxed_out[PTE_MAX_CURALT_DEPTH]; /* no. of pte_csh misses after maxing on allocation size */ GBLREF pte_csh *pte_csh_array[PTE_MAX_CURALT_DEPTH]; /* pte_csh array (per curalt_depth) */ GBLREF int4 pte_csh_cur_size[PTE_MAX_CURALT_DEPTH]; /* current pte_csh size (per curalt_depth) */ GBLREF int4 pte_csh_alloc_size[PTE_MAX_CURALT_DEPTH]; /* current allocated pte_csh size (per curalt_depth) */ GBLREF int4 pte_csh_entries_per_len[PTE_MAX_CURALT_DEPTH]; /* current number of entries per len */ GBLREF int4 pte_csh_tail_count[PTE_MAX_CURALT_DEPTH]; /* count of non 1-1 corresponding pte_csh_array members */ GBLREF pte_csh *cur_pte_csh_array; /* copy of pte_csh_array corresponding to curalt_depth */ GBLREF int4 cur_pte_csh_size; /* copy of pte_csh_cur_size corresponding to curalt_depth */ GBLREF int4 cur_pte_csh_entries_per_len; /* copy of pte_csh_entries_per_len corresponding to curalt_depth */ GBLREF int4 cur_pte_csh_tail_count; /* copy of pte_csh_tail_count corresponding to curalt_depth */ GBLREF boolean_t gtm_utf8_mode; error_def (ERR_PATALTER2LARGE); /* Example compiled pattern for an alternation pattern * Pattern = P0_P1 * ---------------- * P0 = 1.3(.N,2"-",.2A) * P1 = 1" " * * Compiled Pattern * ----------------- * 0x00000000 <-- fixed (1 if fixed length, 0 if not fixed length) * 0x00000027 <-- length of pattern stream (inclusive of itself) * * 0x02000000 P0 <-- pattern_mask[0] => alternation * 0x00000001 P0 <-- alt_rep_min[0] * 0x00000003 P0 <-- alt_rep_max[0] * 0x00000009 P0 <-- length of alternation pattern's choice[0] pattern (exclusive of itself) * 0x00000000 P0 <-- fixed * 0x00000002 P0 <-- length of pattern stream (inclusive of itself) * 0x40000001 P0 <-- pattern_mask[0] => DFABIT | PATM_N * 0x00000001 P0 <-- count * 0x00000000 P0 <-- tot_min * 0x00007fff P0 <-- tot_max * 0x00000000 P0 <-- min[0] * 0x00007fff P0 <-- max[0] * 0x00000001 P0 <-- size[0] * 0x0000000a P0 <-- length of alternation pattern's choice[1] pattern (exclusive of itself) * 0x00000001 P0 <-- fixed * 0x00000004 P0 <-- length of pattern stream (inclusive of itself) * 0x00000082 P0 <-- pattern_mask[0] = PATM_STR | PATM_P * 0x00000001 P0 <-- length of PATM_STR (exclusive of itself) * 0x0000002d P0 <-- PATM_STR[0] = '-' * 0x00000001 P0 <-- count * 0x00000002 P0 <-- tot_min * 0x00000002 P0 <-- tot_max * 0x00000002 P0 <-- min[0] // Note for fixed length, max[] array is absent // * 0x00000001 P0 <-- size[0] * 0x00000000 P0 <-- End of alternation pattern's choices ('\0') * * 0x00000082 P1 <-- pattern_mask[1] => PATM_STR | PATM_P (' ') * 0x00000001 P1 <-- length of PATM_STR (exclusive of itself) * 0x00000020 P1 <-- PATM_STR[0] = ' ' * * 0x00000002 <-- count * 0x00000001 <-- total_min * 0x00007fff <-- total_max * 0x00000000 <-- min[0] <-- Begin of min[2] array * 0x00000001 <-- min[1] * 0x00007fff <-- max[0] <-- Begin of max[2] array * 0x00000001 <-- max[1] * 0x00000001 <-- size[0] <-- Begin of size[2] array * 0x00000001 <-- size[1] */ /* returns index in cur_pte_csh_array that holds the desired tuple.. * return PTE_NOT_FOUND otherwise. */ static int pte_csh_present(char *patptr, char *strptr, int4 charlen, int repcnt) { int4 index; pte_csh *tmp_pte, *pte_top; assert(PTE_MAX_CURALT_DEPTH > curalt_depth); index = ((PTE_STRLEN_CUTOFF > charlen) ? charlen : PTE_STRLEN_CUTOFF) * cur_pte_csh_entries_per_len; assert(cur_pte_csh_size > index); tmp_pte = cur_pte_csh_array + index; pte_top = tmp_pte + ((PTE_STRLEN_CUTOFF > charlen) ? cur_pte_csh_entries_per_len : cur_pte_csh_tail_count); assert(pte_top <= (cur_pte_csh_array + cur_pte_csh_size)); for (; tmp_pte < pte_top; tmp_pte++) { if ((tmp_pte->strptr != strptr) || (tmp_pte->patptr != patptr) || (tmp_pte->charlen != charlen) || (tmp_pte->repcnt != repcnt)) { if (NULL != tmp_pte->strptr) continue; else break; /* the first NULL value means all further entries for this "charlen" are NULL */ } tmp_pte->count++; return (int)tmp_pte->match; } return (int)PTE_NOT_FOUND; } static void pte_csh_insert(char *patptr, char *strptr, int4 charlen, int repcnt, boolean_t match) { int4 index; pte_csh *tmp_pte, *pte_top, *min_pte, *free_pte; assert(PTE_MAX_CURALT_DEPTH > curalt_depth); assert(PTE_NOT_FOUND == pte_csh_present(patptr, strptr, charlen, repcnt)); index = ((PTE_STRLEN_CUTOFF > charlen) ? charlen : PTE_STRLEN_CUTOFF) * cur_pte_csh_entries_per_len; assert(cur_pte_csh_size > index); tmp_pte = cur_pte_csh_array + index; pte_top = tmp_pte + ((PTE_STRLEN_CUTOFF > charlen) ? cur_pte_csh_entries_per_len : cur_pte_csh_tail_count); assert(pte_top <= (cur_pte_csh_array + cur_pte_csh_size)); min_pte = tmp_pte; free_pte = NULL; for (; tmp_pte < pte_top; tmp_pte++) { if (NULL == tmp_pte->patptr) { min_pte = free_pte = tmp_pte; break; } else if (min_pte->count > tmp_pte->count) min_pte = tmp_pte; } if (NULL == free_pte) { for (tmp_pte = cur_pte_csh_array + index; tmp_pte < pte_top; tmp_pte++) tmp_pte->count = 1; /* reset count whenever new entry is made thereby causing history refresh. * i.e. permitting formerly busy but currently inactive patterns to be reused */ } min_pte->count = 0; /* give little priority to the rest by setting count to 1 less than the others */ min_pte->patptr = patptr; min_pte->strptr = strptr; min_pte->charlen = charlen; min_pte->repcnt = repcnt; min_pte->match = match; } int do_patalt(uint4 *firstalt, unsigned char *strptr, unsigned char *strtop, int4 repmin, int4 repmax, int totchar, int repcnt, int4 min_incr, int4 max_incr) { boolean_t fixed; int4 alt_tot_min, alt_tot_max, new_pte_csh_size, tmp_do_patalt_calls; uint4 *cur_alt, tempuint; uint4 *patptr; int match, alt_size, charlen, bytelen, pat_found; mval alt_pat, alt_str; pte_csh *tmp_pte; unsigned char *strtmp, *strnext; if (PTE_MAX_ENTRIES <= repcnt) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_PATALTER2LARGE, 1, PTE_MAX_ENTRIES); if (PTE_MAX_CURALT_DEPTH > curalt_depth) { /* try to find it in the current pattern evaluation cache (cur_pte_csh_array) itself */ tmp_do_patalt_calls = ++do_patalt_calls[curalt_depth]; pat_found = pte_csh_present((char *)firstalt, (char *)strptr, totchar, repcnt); if (PTE_NOT_FOUND != pat_found) { do_patalt_hits[curalt_depth]++; return pat_found; } else if ((tmp_do_patalt_calls > cur_pte_csh_size) && ((tmp_do_patalt_calls - do_patalt_hits[curalt_depth]) > (tmp_do_patalt_calls / PTE_CSH_MISS_FACTOR))) { /* lots of cache miss happening. try to increase pt_csh_array size */ do_patalt_hits[curalt_depth] = do_patalt_calls[curalt_depth] = 1; new_pte_csh_size = cur_pte_csh_size; if (cur_pte_csh_size < pte_csh_alloc_size[curalt_depth]) { new_pte_csh_size = (cur_pte_csh_size << 1); assert(cur_pte_csh_size <= pte_csh_alloc_size[curalt_depth]); } else if (PTE_MAX_ENTRIES > pte_csh_alloc_size[curalt_depth]) { new_pte_csh_size = (cur_pte_csh_size << 1); tmp_pte = malloc(SIZEOF(pte_csh) * new_pte_csh_size); free(cur_pte_csh_array); pte_csh_alloc_size[curalt_depth] = new_pte_csh_size; pte_csh_array[curalt_depth] = tmp_pte; cur_pte_csh_array = pte_csh_array[curalt_depth]; } else do_patalt_maxed_out[curalt_depth]++; if (new_pte_csh_size != cur_pte_csh_size) { memset(pte_csh_array[curalt_depth], 0, SIZEOF(pte_csh) * new_pte_csh_size); pte_csh_cur_size[curalt_depth] *= 2; pte_csh_entries_per_len[curalt_depth] *= 2; pte_csh_tail_count[curalt_depth] *= 2; UPDATE_CUR_PTE_CSH_MINUS_ARRAY(cur_pte_csh_size, cur_pte_csh_entries_per_len, cur_pte_csh_tail_count); } } } alt_pat.mvtype = MV_STR; alt_str.mvtype = MV_STR; alt_str.str.addr = (char *)strptr; patptr = firstalt; GET_LONG(alt_size, patptr); patptr++; for (match = FALSE; !match && alt_size; patptr++) { cur_alt = patptr; cur_alt++; GET_ULONG(tempuint, cur_alt); cur_alt++; cur_alt += tempuint; GET_LONG(alt_tot_min, cur_alt); cur_alt++; if (alt_tot_min <= totchar) { GET_LONG(tempuint, cur_alt); GET_LONG(fixed, patptr); /* Note that some patterns whose minimum and maximum length are the same need not have * "fixed" field 1. This is because alternations which have choices that all evaluate * to the same length (e.g. 5(2l,2e,"ab")) are currently not recognizable by do_patfixed * and hence go through do_pattern. */ assert(!fixed || (alt_tot_min == tempuint)); alt_tot_max = (tempuint < totchar) ? tempuint : totchar; alt_pat.str.addr = (char *)patptr; alt_pat.str.len = alt_size * SIZEOF(uint4); /* Note that the below zero min length avoiding code is actually an optimization. * This is because if we start from length 0, we will end up matching the input string and in case * the alternation pattern's max count is huge (e.g. PAT_MAX) we will end up recursing * in do_patalt() as many times each time matching a length of 0, without realizing we are * not progressing anywhere in the match by matching a huge number of empty strings. * This will effectively cause a combinatorial explosion to occur in case there are at least 2 choices * in the alternation pattern (which usually will be the case) since the choices that need to be * examined are 2 ** PAT_MAX. * Instead, if we start from length 1, every level of recursion we decrease the size of the problem * by matching a non-zero length of the input string and hence we can't progress much in the * recursion levels before starting to backtrack, thereby avoiding the explosion. * Note that we do have to consider zero length in case we haven't yet exhausted our minimum count of * the alternation pattern and we have a null input string remaining to be matched. * Hence the if check below. */ if (totchar && (0 == alt_tot_min)) alt_tot_min = 1; /* avoid zero min length when non-zero string still needs to be matched */ if (!gtm_utf8_mode) { /* each character is 1 byte so charlen and bytelen is same */ charlen = alt_tot_min; bytelen = alt_tot_min; } UTF8_ONLY( else { /* skip alt_tot_min characters */ strtmp = strptr; for (charlen = 0; charlen < alt_tot_min; charlen++) { assert(strtmp < strtop); strtmp = UTF8_MBNEXT(strtmp, strtop); } bytelen = (int)(strtmp - strptr); } ) UTF8_ONLY( if (gtm_utf8_mode) alt_str.mvtype |= MV_UTF_LEN; /* avoid recomputing "char_len" in do_pattern/do_patfixed */ ) for ( ; !match && (charlen <= alt_tot_max); charlen++) { alt_str.str.len = bytelen; UTF8_ONLY( if (gtm_utf8_mode) { assert(utf8_len(&alt_str.str) == charlen); alt_str.str.char_len = charlen; /* set "char_len" */ } ) match = charlen ? (fixed ? do_patfixed(&alt_str, &alt_pat) : do_pattern(&alt_str, &alt_pat)) : TRUE; /* max_incr and min_incr aid us in an earlier backtracking optimization. * for example, let us consider "abcdefghijklmnopqrstuvwxyz"?.13(1l,1e,1n,1u,1p,2l) * say the first do_patalt() call matches a substring (the beginning of the input string) "a" * with the first alternation choice 1l * say the recursive second do_patalt() call then matches a substring of the now beginning * input string "b" with the first alternation choice 1l again * the recursively called third do_patalt() now can rest assured that the remaining string * can't be matched by the alternation. This is because it has only 11 chances left * (note the maximum is .13) and each time the maximum length it can match is 2 (the * maximum length of all the alternation choices which is 2l) which leaves it with a * maximum of 22 characters while there are still 24 characters left in the input-string. * this optimization can cause a backtracking to occur at the 3rd level of call to do_patalt() * instead of going through the call trace 13 times and then determining at the leaf level. * since at each level, the choices examined are 6, we are saving nearly (6 to the power of 11) * choice examinations (11 for the levels that we avoid with the optimization) */ if (match && ((charlen < totchar) || (repcnt < repmin))) match &= ((repcnt < repmax) && ((totchar - charlen) <= (repmax - repcnt) * max_incr) && ((totchar - charlen) >= (repmin - repcnt) * min_incr)) ? do_patalt(firstalt, &strptr[bytelen], strtop, repmin, repmax, totchar - charlen, repcnt + 1, min_incr, max_incr) : FALSE; if (!match) { /* update "bytelen" to correspond to "charlen + 1" */ if (!gtm_utf8_mode) bytelen++; UTF8_ONLY( else { assert((strtmp < strtop) || (charlen == alt_tot_max)); if (strtmp < strtop) { strnext = UTF8_MBNEXT(strtmp, strtop); assert(strnext > strtmp); bytelen += (int)(strnext - strtmp); strtmp = strnext; } } ) } } } patptr += alt_size; GET_LONG(alt_size, patptr); } if (PTE_MAX_CURALT_DEPTH > curalt_depth) pte_csh_insert((char *)firstalt, (char *)strptr, totchar, repcnt, match); return match; } fis-gtm-V7.0-005/sr_port/do_patfixed.c0000755000032200000250000001255214342376331016505 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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 "patcode.h" #include "copy.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" /* needed by *TYPEMASK* macros defined in gtm_utf8.h */ #include "gtm_utf8.h" #endif GBLDEF char codelist[] = PATM_CODELIST; GBLREF boolean_t gtm_utf8_mode; GBLREF boolean_t run_time; GBLREF uint4 pat_allmaskbits; GBLREF uint4 *pattern_typemask; error_def(ERR_PATNOTFOUND); /* This procedure executes at "run-time". After a pattern in a MUMPS program has been compiled (by patstr and its * helper-procedures), this procedure is called to evaluate "fixed-length" patterns. * i.e. for each pattern atom, the lower-bound is equal to the upper-bound such as 3N2A5N. * For patterns with a variable length, procedure do_pattern() is called to do the evaluation. */ int do_patfixed(mval *str, mval *pat) { int4 count, tempint; int4 *min, *reptr, *rtop; int4 repeat; int letter; int repcnt; int bytelen, charlen, pbytelen, strbytelen; unsigned char *strptr, *strtop, *strnext, *pstr, *ptop, *pnext; uint4 code, tempuint, patstream_len; uint4 *patptr; boolean_t flags, pvalid, strvalid; UTF8_ONLY( wint_t utf8_codepoint; ) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* set up information */ MV_FORCE_STR(str); patptr = (uint4 *)pat->str.addr; DEBUG_ONLY( GET_ULONG(tempuint, patptr); assert(tempuint); /* ensure first uint4 is non-zero indicating fixed length pattern string */ ) patptr++; GET_ULONG(tempuint, patptr); DEBUG_ONLY(patstream_len = tempuint); patptr += tempuint; GET_LONG(count, patptr); assert(MAX_PATTERN_ATOMS > count); patptr++; GET_ULONG(tempuint, patptr); patptr++; if (!gtm_utf8_mode) charlen = str->str.len; UTF8_ONLY( else { MV_FORCE_LEN(str); /* to set str.char_len if not already done; also issues BADCHAR error if appropriate */ charlen = str->str.char_len; } ) if (tempuint != charlen) return FALSE; patptr++; min = (int4 *)patptr; rtop = min + count; /* Note: the compiler generates: rtop = min + SIZEOF(int4) * count */ /* attempt a match */ strptr = (unsigned char *)str->str.addr; strtop = &strptr[str->str.len]; patptr = (uint4 *)pat->str.addr; patptr += 2; for (reptr = min; reptr < rtop ; reptr++) { GET_LONG(repeat, reptr); GET_ULONG(code, patptr); assert(code); patptr++; if (!(code & PATM_STRLIT)) { /* meta character pat atom */ ENSURE_PAT_IN_TABLE(code); if (!gtm_utf8_mode) { for (repcnt = 0; repcnt < repeat; repcnt++) { if (!(code & pattern_typemask[*strptr++])) return FALSE; } } UTF8_ONLY( else { for (repcnt = 0; repcnt < repeat; repcnt++) { assert(strptr < strtop); /* PATTERN_TYPEMASK macro relies on this */ if (!(code & PATTERN_TYPEMASK(strptr, strtop, strnext, utf8_codepoint))) return FALSE; strptr = strnext; } } ) } else { /* STRLIT pat atom */ assert(3 == PAT_STRLIT_PADDING); GET_LONG(bytelen, patptr); /* get bytelen */ patptr++; GET_LONG(charlen, patptr); /* get charlen */ patptr++; GET_ULONG(flags, patptr); /* get falgs */ patptr++; assert(!(flags & PATM_STRLIT_BADCHAR)); /* ensure pattern atom length is within limits of the complete pattern stream */ assert((0 <= bytelen) && ((patptr + DIVIDE_ROUND_UP(bytelen, SIZEOF(*patptr))) <= ((uint4 *)(pat->str.addr) + patstream_len + 2))); pstr = (unsigned char *)patptr; if (1 == bytelen) { if (!gtm_utf8_mode) { for (repcnt = 0; repcnt < repeat; repcnt++) if (*pstr != *strptr++) return FALSE; patptr++; } UTF8_ONLY( else { for (repcnt = 0; repcnt < repeat; repcnt++) { if ((1 != (UTF8_VALID(strptr, strtop, bytelen), bytelen)) || (*pstr != *strptr++)) return FALSE; } patptr++; } ) } else if (bytelen > 0) { if (!gtm_utf8_mode) { for (repcnt = 0; repcnt < repeat; repcnt++) for (letter = 0, pstr = (unsigned char *)patptr; letter < bytelen; letter++) if (*pstr++ != *strptr++) return FALSE; patptr += DIVIDE_ROUND_UP(bytelen, SIZEOF(*patptr)); } UTF8_ONLY( else { pstr = (unsigned char *)patptr; ptop = pstr + bytelen; for (repcnt = 0; repcnt < repeat; repcnt++) { pstr = (unsigned char *)patptr; for ( ; pstr < ptop; ) { pvalid = UTF8_VALID(pstr, ptop, pbytelen); /* sets pbytelen */ assert(pvalid); strvalid = UTF8_VALID(strptr, strtop, strbytelen); /* sets strbytelen */ if (pbytelen != strbytelen) return FALSE; else { DEBUG_ONLY(strnext = strptr + pbytelen); pnext = pstr + pbytelen; do { if (*pstr++ != *strptr++) return FALSE; } while (pstr < pnext); assert(strptr == strnext); } } } patptr += DIVIDE_ROUND_UP(bytelen, SIZEOF(*patptr)); } ) } } } return TRUE; } fis-gtm-V7.0-005/sr_port/do_patsplit.c0000755000032200000250000003103614342376331016537 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "patcode.h" #include "copy.h" #include "min_max.h" #include "gtm_string.h" #ifdef UTF8_SUPPORTED #include "gtm_utf8.h" #endif GBLREF boolean_t gtm_utf8_mode; /* This routine tries to split the pattern-string P into substrings LFR where F is a fixed length pattern-atom of P * and L and R are the pattern substrings in P on the left and right side of F. * For pattern-strings that have more than one fixed-length pattern-atom, the one closest to the median is selected. * Once F is determined, do_patfixed() is invoked to determine all possible matches of F with the input string. * do_patsplit() has additional optimizations to try match the fixed length pattern in a restricted subset of the * input string taking into account the min and max of the left and the right pattern. * For each such match, an attempt is made to match L and R with the left and right side of the input string. * This is done using do_patfixed() or do_pattern() appropriately. * If any such attempt succeeds, we return TRUE. * If no attempt succeeds, we return FALSE. * If the pattern-string P doesn't contain any fixed-length pattern-atom, we return DO_PATSPLIT_FAIL */ int do_patsplit(mval *str, mval *pat) { boolean_t fixed[2]; /* fixed[0] is for the left, fixed[1] is for the right */ boolean_t right; /* 0 indicates processing left side, 1 indicates right side */ boolean_t match; /* match status of input pattern with input string */ gtm_int64_t bound; int4 index, fixed_index; /* index of our current fixed-length pattern-atom */ int4 alt, alt_rep_max, alt_rep_min, bytelen, charlen, charstoskip, count, deltalen, fixedcharlen, leftcharlen; int4 max[MAX_PATTERN_ATOMS], min[MAX_PATTERN_ATOMS], size[MAX_PATTERN_ATOMS]; int4 numchars, offset, rightcharlen, strbytelen, strcharlen, total_max, total_min; mval fixed_pat, fixed_str, left_pat, left_str, right_pat, right_str; ptstr fixed_ptstr, left_ptstr, right_ptstr; uint4 cnt[2], tot_min[2], tot_max[2]; /* index 0 is for left, index 1 is for right */ uint4 code, *fixed_patptr, flags, *patptr, *patptr_end, *patptr_start, *right_patptr, tempuint, *tmp_patptr; unsigned char *fixednext, *fixedptr, *maxfixedptr, *rightnext, *rightptr, *strptr, *strtop; MV_FORCE_STR(str); patptr = (uint4 *)pat->str.addr; # ifdef DEBUG GET_ULONG(tempuint, patptr); assert(!tempuint); # endif patptr++; patptr_start = patptr + 1; GET_ULONG(tempuint, patptr); patptr += tempuint; patptr_end = patptr; GET_LONG(count, patptr); patptr++; GET_LONG(total_min, patptr); patptr++; GET_LONG(total_max, patptr); patptr++; UTF8_ONLY( if (gtm_utf8_mode) { MV_FORCE_LEN(str); /* to set str.char_len if not already done */ assert(str->str.char_len >= total_min && str->str.char_len <= total_max); } ) assert(count <= MAX_PATTERN_ATOMS); memcpy(&min[0], patptr, SIZEOF(*patptr) * count); patptr += count; memcpy(&max[0], patptr, SIZEOF(*patptr) * count); patptr += count; memcpy(&size[0], patptr, SIZEOF(*patptr) * count); patptr = patptr_start; right = FALSE; /* start with left side */ fixed[right] = TRUE; tot_min[right] = tot_max[right] = 0; fixed_patptr = right_patptr = NULL; fixed_index = -1; for (index = 0; index < count; index++) { GET_ULONG(code, patptr); tmp_patptr = patptr; patptr++; if (code & PATM_ALT) { /* skip to the next pattern-atom */ GET_LONG(alt_rep_min, patptr); patptr++; GET_LONG(alt_rep_max, patptr); patptr++; GET_LONG(alt, patptr); patptr++; while(alt) { patptr += alt; GET_LONG(alt, patptr); patptr++; } fixed[right] = FALSE; /* patptr now points to the next patcode after the alternation */ } else if (code == PATM_DFA) { /* Discrete Finite Automaton pat atom */ assert(min[index] != max[index]); /* DFA should never be fixed length */ GET_LONG(bytelen, patptr); patptr++; patptr += bytelen; fixed[right] = FALSE; } else { if (code & PATM_STRLIT) { /* STRLIT pat atom */ assert(3 == PAT_STRLIT_PADDING); GET_LONG(bytelen, patptr); patptr++; GET_LONG(charlen, patptr); patptr++; GET_ULONG(flags, patptr); patptr++; patptr += DIVIDE_ROUND_UP(bytelen, SIZEOF(*patptr)); } if ((min[index] == max[index]) && (bound = (gtm_uint64_t)min[index] * size[index])) { /* fixed_length */ if (ABS(index - (count / 2)) < ABS(fixed_index - (count / 2))) { /* non-zero fixed length pattern with a fixed_index closer to the median of the array */ if (right) { /* update left's tot_min and tot_max to reflect the new fixed_index */ if (PAT_MAX_REPEAT < (tot_min[0] + tot_min[right] + BOUND_MULTIPLY(min[fixed_index], size[fixed_index], bound))) tot_min[0] = PAT_MAX_REPEAT; else /* check before assignment to statisfy static scan */ tot_min[0] += (tot_min[right] + BOUND_MULTIPLY(min[fixed_index], size[fixed_index], bound)); if (PAT_MAX_REPEAT < (tot_max[0] + tot_max[right] + BOUND_MULTIPLY(max[fixed_index], size[fixed_index], bound))) tot_max[0] = PAT_MAX_REPEAT; else /* check before assignment to statisfy static scan */ tot_max[0] += (tot_max[right] + BOUND_MULTIPLY(max[fixed_index], size[fixed_index], bound)); fixed[0] &= fixed[right]; } fixed_index = index; right = TRUE; fixed[right] = TRUE; tot_min[right] = tot_max[right] = 0; fixed_patptr = tmp_patptr; right_patptr = patptr; continue; } } else fixed[right] = FALSE; } if (PAT_MAX_REPEAT < (tot_min[right] + BOUND_MULTIPLY(min[index], size[index], bound))) tot_min[right] = PAT_MAX_REPEAT; else /* check before assignment to statisfy static scan */ tot_min[right] += BOUND_MULTIPLY(min[index], size[index], bound); if (PAT_MAX_REPEAT < (tot_max[right] + BOUND_MULTIPLY(max[index], size[index], bound))) tot_max[right] = PAT_MAX_REPEAT; else /* check before assignment to statisfy static scan */ tot_max[right] += BOUND_MULTIPLY(max[index], size[index], bound); } assert(index == count); if (-1 == fixed_index) return DO_PATSPLIT_FAIL; assert(fixed_index < count); assert((total_min == (tot_min[0] + tot_min[1] + BOUND_MULTIPLY(min[fixed_index], size[fixed_index], bound))) || (PAT_MAX_REPEAT == total_min)); assert((total_max == (tot_max[0] + tot_max[1] + BOUND_MULTIPLY(max[fixed_index], size[fixed_index], bound))) || (PAT_MAX_REPEAT == total_max)); cnt[0] = fixed_index; if (cnt[0]) { /* left section has at least one pattern atom. create its compilation string */ patptr = left_ptstr.buff; *patptr++ = fixed[0]; *patptr++ = (uint4)(fixed_patptr - patptr_start + 1); memcpy(patptr, patptr_start, (char *)fixed_patptr - (char *)patptr_start); patptr += fixed_patptr - patptr_start; *patptr++ = cnt[0]; *patptr++ = tot_min[0]; *patptr++ = tot_max[0]; for (index = 0; index < cnt[0]; index++) *patptr++ = min[index]; if (!fixed[0]) { for (index = 0; index < cnt[0]; index++) *patptr++ = max[index]; } for (index = 0; index < cnt[0]; index++) *patptr++ = size[index]; left_pat.mvtype = MV_STR; left_pat.str.len = INTCAST((char *)patptr - (char *)&left_ptstr.buff[0]); left_pat.str.addr = (char *)&left_ptstr.buff[0]; } /* create fixed length pattern atom's compilation string */ patptr = fixed_ptstr.buff; *patptr++ = TRUE; /* fixed length pattern */ *patptr++ = (uint4)(right_patptr - fixed_patptr + 1); memcpy(patptr, fixed_patptr, (char *)right_patptr - (char *)fixed_patptr); patptr += right_patptr - fixed_patptr; *patptr++ = 1; /* count */ fixedcharlen = min[fixed_index] * size[fixed_index]; /* tot_min and tot_max */ *patptr++ = fixedcharlen; *patptr++ = fixedcharlen; *patptr++ = min[fixed_index]; /* min[0] */ *patptr++ = size[fixed_index]; /* size[0] */ fixed_pat.mvtype = MV_STR; fixed_pat.str.len = INTCAST((char *)patptr - (char *)&fixed_ptstr.buff[0]); fixed_pat.str.addr = (char *)&fixed_ptstr.buff[0]; cnt[1] = count - fixed_index - 1; if (cnt[1]) { /* right section has at least one pattern atom. create its compilation string */ patptr = right_ptstr.buff; *patptr++ = fixed[1]; *patptr++ = (uint4)(patptr_end - right_patptr + 1); memcpy(patptr, right_patptr, (char *)patptr_end - (char *)right_patptr); patptr += patptr_end - right_patptr; *patptr++ = cnt[1]; *patptr++ = tot_min[1]; *patptr++ = tot_max[1]; for (index = fixed_index + 1; index < count; index++) *patptr++ = min[index]; if (!fixed[1]) { for (index = fixed_index + 1; index < count; index++) *patptr++ = max[index]; } for (index = fixed_index + 1; index < count; index++) *patptr++ = size[index]; right_pat.mvtype = MV_STR; right_pat.str.len = INTCAST((char *)patptr - (char *)&right_ptstr.buff[0]); right_pat.str.addr = (char *)&right_ptstr.buff[0]; } strbytelen = str->str.len; strptr = (unsigned char *)str->str.addr; strtop = strptr + strbytelen; if (!gtm_utf8_mode) strcharlen = str->str.len; UTF8_ONLY( else strcharlen = str->str.char_len; ) /* Determine "maxfixedptr" */ if (strcharlen > (tot_min[1] + tot_max[0] + fixedcharlen)) charstoskip = tot_max[0]; else charstoskip = strcharlen - tot_min[1] - fixedcharlen; if (!gtm_utf8_mode) strptr += charstoskip; UTF8_ONLY( else { for ( ; 0 < charstoskip; charstoskip--) { assert(strptr < strtop); /* below macro relies on this */ strptr = UTF8_MBNEXT(strptr, strtop); } } ) maxfixedptr = strptr; /* Determine "fixedptr" */ strptr = (unsigned char *)str->str.addr; if (strcharlen > (tot_min[0] + tot_max[1] + fixedcharlen)) charstoskip = strcharlen - tot_max[1] - fixedcharlen; else charstoskip = tot_min[0]; leftcharlen = charstoskip; if (!gtm_utf8_mode) strptr += charstoskip; UTF8_ONLY( else { for ( ; 0 < charstoskip; charstoskip--) { assert(strptr < strtop); /* below macro relies on this */ strptr = UTF8_MBNEXT(strptr, strtop); } } ) fixedptr = strptr; /* Set "left_str" */ left_str.mvtype = MV_STR; left_str.str.addr = str->str.addr; /* Set "right_str" */ right_str.mvtype = MV_STR; /* Set "fixed_str" */ fixed_str.mvtype = MV_STR; if (!gtm_utf8_mode) { fixed_str.str.len = fixedcharlen; rightptr = fixedptr + fixedcharlen; } UTF8_ONLY( else { left_str.mvtype |= MV_UTF_LEN; /* avoid recomputing "char_len" in do_patfixed below */ right_str.mvtype |= MV_UTF_LEN; /* avoid recomputing "char_len" in do_patfixed below */ fixed_str.mvtype |= MV_UTF_LEN; /* avoid recomputing "char_len" in do_patfixed below */ fixed_str.str.char_len = fixedcharlen; /* skip fixedcharlen characters */ assert(fixedptr == strptr); for (numchars = 0; numchars < fixedcharlen; numchars++) { assert(strptr < strtop); strptr = UTF8_MBNEXT(strptr, strtop); } rightptr = strptr; } ) deltalen = 0; /* Try to match the fixed pattern string and for each match, try matching the left and right input strings */ for (match = FALSE; !match && (fixedptr <= maxfixedptr); fixedptr = fixednext, rightptr = rightnext, leftcharlen++) { fixed_str.str.addr = (char *)fixedptr; fixed_str.str.len = INTCAST(rightptr - fixedptr); if (!gtm_utf8_mode) { fixednext = fixedptr + 1; rightnext = rightptr + 1; } UTF8_ONLY( else { assert(fixedptr < strtop); fixednext = UTF8_MBNEXT(fixedptr, strtop); assert((rightptr < strtop) || (fixedptr == maxfixedptr) && (fixednext > maxfixedptr)); if (rightptr < strtop) rightnext = UTF8_MBNEXT(rightptr, strtop); } ) if (!do_patfixed(&fixed_str, &fixed_pat)) continue; assert(cnt[0] || cnt[1]); /* fixed_pat takes only one pattern atom and non-zero rest are in cnt[0] and cnt[1] */ if (cnt[0]) { left_str.str.len = INTCAST(fixedptr - (unsigned char *)left_str.str.addr); UTF8_ONLY(left_str.str.char_len = leftcharlen;) match = fixed[0] ? do_patfixed(&left_str, &left_pat) : do_pattern(&left_str, &left_pat); if (!match) continue; } if (cnt[1]) { right_str.str.addr = (char *)rightptr; UTF8_ONLY(right_str.str.char_len = strcharlen - leftcharlen - fixedcharlen;) right_str.str.len = INTCAST(strtop - rightptr); match = (fixed[1] ? do_patfixed(&right_str, &right_pat) : do_pattern(&right_str, &right_pat)); } } return match; } fis-gtm-V7.0-005/sr_port/do_pattern.c0000755000032200000250000003223314342376331016354 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_string.h" #include "patcode.h" #include "copy.h" #include "min_max.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" /* needed by *TYPEMASK* macros defined in gtm_utf8.h */ #include "gtm_utf8.h" #endif GBLREF boolean_t gtm_utf8_mode; GBLREF boolean_t run_time; GBLREF char codelist[]; GBLREF int4 curalt_depth; /* depth of alternation nesting */ GBLREF int4 do_patalt_calls[PTE_MAX_CURALT_DEPTH]; /* number of calls to do_patalt() */ GBLREF int4 do_patalt_hits[PTE_MAX_CURALT_DEPTH]; /* number of pte_csh hits in do_patalt() */ GBLREF int4 pte_csh_cur_size[PTE_MAX_CURALT_DEPTH]; /* current pte_csh size (per curalt_depth) */ GBLREF int4 pte_csh_alloc_size[PTE_MAX_CURALT_DEPTH]; /* current allocated pte_csh size (per curalt_depth) */ GBLREF int4 pte_csh_entries_per_len[PTE_MAX_CURALT_DEPTH]; /* current number of entries per len */ GBLREF int4 pte_csh_tail_count[PTE_MAX_CURALT_DEPTH]; /* count of non 1-1 corresponding pte_csh_array members */ GBLREF int4 cur_pte_csh_size; /* copy of pte_csh_cur_size corresponding to curalt_depth */ GBLREF int4 cur_pte_csh_entries_per_len; /* copy of pte_csh_entries_per_len corresponding to curalt_depth */ GBLREF int4 cur_pte_csh_tail_count; /* copy of pte_csh_tail_count corresponding to curalt_depth */ GBLREF pte_csh *cur_pte_csh_array; /* copy of pte_csh_array corresponding to curalt_depth */ GBLREF pte_csh *pte_csh_array[PTE_MAX_CURALT_DEPTH]; /* pte_csh array (per curalt_depth) */ GBLREF uint4 pat_allmaskbits; GBLREF uint4 *pattern_typemask; /* This procedure executes at "run-time". After a pattern in a MUMPS program has been compiled (by patstr and * its helper-procedures), this procedure can be called to evaluate "variable-length" patterns. * Variable-length patterns are of the kind 3.5N2.A.5N i.e. for at least one pattern atom, * the lower-bound is different from the upper-bound. * For patterns with a fixed length, procedure do_patfixed() will be called to do the evaluation. * Variable length input patterns will be scanned at runtime to see if they have at least one fixed length pattern atom. * If yes, routine do_patsplit() will be invoked to determine the fixed length sub pattern atom that is closest to the * median of the pattern atoms. The input pattern would then be split into three, left, fixed and right. * The fixed pattern can be matched by a linear scan in the input string. Once that is done, the left and right * pattern atom positions are pivoted relative to the input string and do_pattern() is invoked on each of them recursively. * If no fixed length pattern atoms can be found, then do_pattern() calculates all possible permutations (it has certain * optimizations to prune the combinatorial search tree) and tries to see for each permutation if a match occurs. */ error_def(ERR_PATNOTFOUND); int do_pattern(mval *str, mval *pat) { int4 count, total_min, total_max; int4 alt_rep_min, alt_rep_max, min_incr, max_incr; int4 bytelen, charlen, length, pbytelen, strbytelen; boolean_t success, attempt, pvalid, strvalid; int atom, unit, idx, index, hasfixed; uint4 z_diff, *rpt, *rtop, rept; uint4 repeat[MAX_PATTERN_ATOMS]; uint4 *patidx[MAX_PATTERN_ATOMS]; unsigned char *stridx[MAX_PATTERN_ATOMS]; unsigned char *strptr, *strtop, *strnext, *pstr, *ptop, *pnext; uint4 code, tempuint; uint4 *dfa_ptr, dfa_val; uint4 *patptr; uint4 flags; int4 *min, *max, *size; int4 mintmp, maxtmp, sizetmp; int alt; boolean_t pte_csh_init; boolean_t match; UTF8_ONLY( wint_t utf8_codepoint; ) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* set up information */ MV_FORCE_STR(str); patptr = (uint4 *) pat->str.addr; GET_ULONG(tempuint, patptr); if (tempuint) { /* tempuint non-zero implies fixed length pattern string. this in turn implies we are not called from op_pattern.s * but instead called from gvzwr_fini(), gvzwr_var(), lvzwr_fini(), lvzwr_var() etc. * in this case, call do_patfixed() as the code below and code in do_patsplit() assumes we are dealing with a * variable length pattern string. changing all the callers to call do_patfixed() directly instead of this extra * redirection was considered, but not felt worth it since the call to do_pattern() is not easily macroizable * due to the expression-like usage in those places. */ return do_patfixed(str, pat); } patptr++; patidx[0] = patptr + 1; stridx[0] = (unsigned char *)str->str.addr; strtop = stridx[0] + str->str.len; GET_ULONG(tempuint, patptr); patptr += tempuint; GET_LONG(count, patptr); patptr++; GET_LONG(total_min, patptr); patptr++; GET_LONG(total_max, patptr); patptr++; /* "length" actually denotes character length; Get it from the appropriate field in the mstr */ if (!gtm_utf8_mode) length = str->str.len; UTF8_ONLY( else { MV_FORCE_LEN(str); /* to set str.char_len if not already done; also issues BADCHAR error if appropriate */ length = str->str.char_len; } ) if (length < total_min || length > total_max) return FALSE; min = (int4 *)patptr; patptr += count; max = (int4 *)patptr; patptr += count; if (MIN_SPLIT_N_MATCH_COUNT <= count) { hasfixed = FALSE; for (index = 0; index < count; index++) { GET_LONG(maxtmp, max + index); GET_LONG(mintmp, min + index); if (maxtmp == mintmp) { hasfixed = TRUE; break; } } if (hasfixed && (DO_PATSPLIT_FAIL != (match = do_patsplit(str, pat)))) return match; } size = (int4 *)patptr; memcpy(repeat, min, count * SIZEOF(*min)); rtop = &repeat[0] + count; count--; attempt = FALSE; idx = 0; pte_csh_init = FALSE; /* proceed to check string */ for (;;) { if (total_min == length) { /* attempt a match */ attempt = TRUE; strptr = stridx[idx]; patptr = patidx[idx]; rpt = &repeat[idx]; for (; rpt < rtop; rpt++) { GET_ULONG(code, patptr); patptr++; rept = *rpt; if (code & PATM_ALT) { /* pattern alternation */ GET_LONG(alt_rep_min, patptr); patptr++; GET_LONG(alt_rep_max, patptr); patptr++; GET_LONG(maxtmp, max + idx); GET_LONG(mintmp, min + idx); assert(alt_rep_min || !mintmp); assert(alt_rep_max || !maxtmp); min_incr = mintmp ? mintmp / alt_rep_min : 0; max_incr = maxtmp ? maxtmp / alt_rep_max : 0; if (rept) { if (FALSE == pte_csh_init) { PTE_CSH_INCR_CURALT_DEPTH(curalt_depth); pte_csh_init = TRUE; } if (do_patalt(patptr, strptr, strtop, alt_rep_min, alt_rep_max, rept, 1, min_incr, max_incr)) { if (!gtm_utf8_mode) strptr += rept; UTF8_ONLY( else { for (unit = 0; unit < rept; unit++) { assert(strptr < strtop); /* below macro relies on this */ strptr = UTF8_MBNEXT(strptr, strtop); } } ) } else goto CALC; } /* make sure that patptr points to the next patcode after the alternation */ GET_LONG(alt, patptr); patptr++; while(alt) { patptr += alt; GET_LONG(alt, patptr); patptr++; } } else if (!(code & PATM_STRLIT)) { /* meta character pat atom */ ENSURE_PAT_IN_TABLE(code); if (!gtm_utf8_mode) { for (unit = 0; unit < rept; unit++) { if (!(code & pattern_typemask[*strptr++])) goto CALC; } } UTF8_ONLY( else { for (unit = 0; unit < rept; unit++) { assert(strptr < strtop); /* PATTERN_TYPEMASK macro relies on this */ if (!(code & PATTERN_TYPEMASK(strptr, strtop, strnext, utf8_codepoint))) goto CALC; strptr = strnext; } } ) } else if (code == PATM_DFA) { /* Discrete Finite Automaton pat atom */ GET_LONG(bytelen, patptr); patptr++; dfa_ptr = patptr; for (unit = 0; unit < rept; ) { GET_ULONG(dfa_val, dfa_ptr); if (!(dfa_val & PATM_STRLIT)) { if (!gtm_utf8_mode) { success = (dfa_val & pattern_typemask[*strptr]); strnext = strptr + 1; } UTF8_ONLY( else { success = (dfa_val & PATTERN_TYPEMASK(strptr, strtop, strnext, utf8_codepoint)); } ) } else { dfa_ptr++; GET_ULONG(dfa_val, dfa_ptr); /* Only ASCII characters are currently allowed for DFA STRLITs. * Assert that below. */ assert(IS_ASCII(dfa_val)); if (!gtm_utf8_mode) { success = (dfa_val == *strptr); strnext = strptr + 1; } UTF8_ONLY( else { UTF8_VALID(strptr, strtop, strbytelen); success = ((1 == strbytelen) && (dfa_val == *strptr)); strnext = strptr + strbytelen; } ) } dfa_ptr++; if (success) { GET_ULONG(dfa_val, dfa_ptr); dfa_ptr = patptr + dfa_val; strptr = strnext; unit++; GET_ULONG(dfa_val, dfa_ptr); if (dfa_val == PATM_ACS) break; } else { dfa_ptr++; GET_ULONG(dfa_val, dfa_ptr); if ((dfa_val & PATM_DFA) == PATM_DFA) break; } } if (unit < rept) goto CALC; else { GET_ULONG(dfa_val, dfa_ptr); while (dfa_val < PATM_DFA) { if (dfa_val & PATM_STRLIT) dfa_ptr += 3; else dfa_ptr += 2; GET_ULONG(dfa_val, dfa_ptr); } if (dfa_val != PATM_ACS) goto CALC; } patptr += bytelen; } else { /* STRLIT pat atom */ assert(3 == PAT_STRLIT_PADDING); GET_LONG(bytelen, patptr); /* get bytelen */ patptr++; GET_LONG(charlen, patptr); /* get charlen */ patptr++; GET_ULONG(flags, patptr); /* get flags */ patptr++; if (bytelen == 1) { if (!gtm_utf8_mode) { for (unit = 0; unit < rept; unit++) { if (*(unsigned char *)patptr != *strptr++) goto CALC; } } UTF8_ONLY( else { for (unit = 0; unit < rept; unit++) { if ((1 != (UTF8_VALID(strptr, strtop, strbytelen), strbytelen)) || (*(unsigned char *)patptr != *strptr++)) goto CALC; } } ) patptr++; } else if (bytelen > 0) { if (!gtm_utf8_mode) { ptop = (unsigned char *)patptr + bytelen; for (unit = 0; unit < rept; unit++) { pstr = (unsigned char *)patptr; while (pstr < ptop) { if (*pstr++ != *strptr++) goto CALC; } } } UTF8_ONLY( else { pstr = (unsigned char *)patptr; ptop = pstr + bytelen; for (unit = 0; unit < rept; unit++) { pstr = (unsigned char *)patptr; for ( ; pstr < ptop; ) { pvalid = UTF8_VALID(pstr, ptop, pbytelen); /* sets pbytelen */ assert(pvalid); strvalid = UTF8_VALID(strptr, strtop, strbytelen); /* sets strbytelen */ if (pbytelen != strbytelen) goto CALC; DEBUG_ONLY(strnext = strptr + pbytelen); pnext = pstr + pbytelen; do { if (*pstr++ != *strptr++) goto CALC; } while (pstr < pnext); assert(strptr == strnext); } } } ) patptr += DIVIDE_ROUND_UP(bytelen, SIZEOF(*patptr)); } } idx++; stridx[idx] = strptr; patidx[idx] = patptr; } if (pte_csh_init) { /* surrounded by braces since the following is a multi-line macro */ PTE_CSH_DECR_CURALT_DEPTH(curalt_depth); } return TRUE; } else { /* calculate permutations */ attempt = FALSE; GET_LONG(maxtmp, max + count); GET_LONG(mintmp, min + count); GET_LONG(sizetmp, size + count); if (repeat[count] < maxtmp) { atom = unit = length - total_min; z_diff = maxtmp - mintmp; if (sizetmp > 1) { unit /= sizetmp; atom = unit * sizetmp; z_diff *= sizetmp; } if (atom > 0) { total_min += MIN(atom, z_diff); repeat[count] = MIN(repeat[count] + unit, maxtmp); if (total_min == length) continue; } } } CALC: unit = count; GET_LONG(sizetmp, size + unit); for ( ; ; ) { GET_LONG(mintmp, min + unit); total_min -= (repeat[unit] - mintmp) * sizetmp; repeat[unit] = mintmp; unit--; if (unit < 0) { if (pte_csh_init) { /* surrounded by braces since the following is a multi-line macro */ PTE_CSH_DECR_CURALT_DEPTH(curalt_depth); } return FALSE; } GET_LONG(maxtmp, max + unit); GET_LONG(sizetmp, size + unit); if (repeat[unit] < maxtmp) { total_min += sizetmp; repeat[unit]++; if (total_min <= length) { if (unit <= idx) { idx = unit; break; } if (!attempt) break; } } } } } fis-gtm-V7.0-005/sr_port/do_xform.h0000755000032200000250000000277614342376331016050 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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. * * * ****************************************************************/ #ifndef DO_XFORM_INCLUDED #define DO_XFORM_INCLUDED #define DO_XFORM_RETURN_IF_NULL_STRING(INPUT, OUTPUT, LENGTH) \ { \ if (!INPUT->len) \ { /* If input string is the null subscript, we want the output (of the collation transformation function) \ * to be a null subscript as well. This is because the null subscript has special meaning as far as \ * GT.M subscript collation is concerned. In case the collation routine decides to map it to a non-null \ * subscript the user might start seeing undesirable collation orders. To be safe and avoid such \ * issues, the null subscript is handled specially by mapping it to a null subscript internally by GT.M \ * (without passing this to the collation routine) and returning right away. \ */ \ OUTPUT->len = 0; \ *LENGTH = 0; \ return; \ } \ } void do_xform(collseq *csp, int fc_type, mstr *input, mstr *output, int *length); /* * fc_type would be either XFORM (0) or XBACK (1) */ #endif /* DO_XFORM_INCLUDED */ fis-gtm-V7.0-005/sr_port/dollar_quit.c0000644000032200000250000002245314342376333016536 0ustar librarygtc/**************************************************************** * * * Copyright 2010, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "gtm_string.h" #include #include "stack_frame.h" #include "op.h" #include "get_ret_targ.h" #include "xfer_enum.h" #include "dollar_quit.h" #if defined(__sparc) # include "sparc.h" #elif defined(__s390__) || defined(__MVS__) # include "s390.h" #elif defined(__hppa) # include "hppa.h" #elif defined(__ia64) # include "ia64.h" #endif GBLREF int process_exiting; /* Determine value to return for $QUIT: * * 0 - no return value requested * 1 - non-alias return value requested * 11 - alias return value requested * * Determination of parm/no-parm is made by calling get_ret_targ() which checks the stack frames back to * a counted frame whether the ret_value field has a return mval, signifying that a return value is required. * If a return value is required, determination of the type of return value is made by examining the * generated instruction stream at the return point and checking for an OC_EXFUNRET or OC_EXFUNRETALS * (non-alias and alias type return var processor respectively) opcode following the return point. This is * done by isolating the instruction that indexes into the transfer table, extracting the xfer-table index * and checking against known values for op_exfunret and op_exfunretals to determine type of return. No match * means no return value. * * Because this routine looks at the generated code stream at the return point, it is highly platform * dependent. * * Note: If generated code changes for a platform, this module needs to be revisited. */ int dollar_quit(void) { stack_frame *sf; int xfer_index; union { unsigned char *instr; unsigned short *instr_type; unsigned char *instr_type_8; unsigned char *xfer_offset_8; short *xfer_offset_16; int *xfer_offset_32; } ptrs; /* There was no return value - return 0 */ if (NULL == get_ret_targ(&sf)) return 0; /* There is a return value - see if they want a "regular" or alias type return argument */ sf = sf->old_frame_pointer; /* Caller's frame */ # ifdef __i386 { ptrs.instr = sf->mpc; /* First figure out the potential length of the lea* instruction loading compiler temp offset */ if (0x078d == *ptrs.instr_type) ptrs.instr += 3; /* Past the 2 byte lea plus 1 byte push */ else if (0x478d == *ptrs.instr_type) ptrs.instr += 4; /* Past the 3 byte lea plus 1 byte push */ else if (0x878d == *ptrs.instr_type) ptrs.instr += 7; /* Past the 6 byte lea plus 1 byte push */ else ptrs.instr = NULL; /* Note the "long format call opcode" check below assumes that both of the EXFUNRET[ALS] calls remain at a * greater-than-128 byte offset in the transfer table (which they currently are). */ if ((NULL != ptrs.instr) && (0x93FF == *ptrs.instr_type)) { ptrs.instr += SIZEOF(*ptrs.instr_type); xfer_index = *ptrs.xfer_offset_32 / SIZEOF(void *); } else xfer_index = -1; } # elif defined(__x86_64__) { ptrs.instr = sf->mpc; if (0x8d49 == *ptrs.instr_type) { ptrs.instr += 2; /* Past first part of instruction type */ if (0x7e == *ptrs.instr_type_8) ptrs.instr += 2; /* past last byte of instruction type plus 1 byte offset */ else if (0xbe == *ptrs.instr_type_8) ptrs.instr += 5; /* past last byte of instruction type plus 4 byte offset */ else ptrs.instr = NULL; } else ptrs.instr_type = NULL; if ((NULL != ptrs.instr) && (0x93FF == *ptrs.instr_type)) { /* Long format CALL */ ptrs.instr += SIZEOF(*ptrs.instr_type); xfer_index = *ptrs.xfer_offset_32 / SIZEOF(void *); } else xfer_index = -1; /* Not an xfer index */ } # elif defined(_AIX) { ptrs.instr = sf->mpc + 4; /* Past address load of compiler temp arg */ if (0xE97C == *ptrs.instr_type) { /* ld of descriptor address from xfer table */ ptrs.instr += SIZEOF(*ptrs.instr_type); xfer_index = *ptrs.xfer_offset_16 / SIZEOF(void *); } else xfer_index = -1; } # elif defined(__alpha) /* Applies to both VMS and Tru64 as have same codegen */ { ptrs.instr = sf->mpc + 4; /* Past address load of compiler temp arg */ if (UNIX_ONLY(0xA36C) VMS_ONLY(0xA36B) == *(ptrs.instr_type + 1)) /* Different code for reg diff */ /* ldl of descriptor address from xfer table - little endian - offset prior to opcode */ xfer_index = *ptrs.xfer_offset_16 / SIZEOF(void *); else xfer_index = -1; } # elif defined(__sparc) { ptrs.instr = sf->mpc + 4; /* Past address load of compiler temp arg */ if (0xC85C == *ptrs.instr_type) { /* ldx of rtn address from xfer table */ ptrs.instr += SIZEOF(*ptrs.instr_type); xfer_index = (*ptrs.xfer_offset_16 & SPARC_MASK_OFFSET) / SIZEOF(void *); } else xfer_index = -1; } # elif defined(__s390__) || defined(__MVS__) { format_RXY instr_LG; ZOS_ONLY(format_RR instr_RR;) union { int offset; struct { /* Used to reassemble the offset in the LG instruction */ int offset_unused:12; int offset_hi:8; int offset_low:12; } instr_LG_bits; } RXY; /* Need to forward space past address load of compiler temp arg. On zOS, the position of the mpc can * differ. If the origin point is an external call, we have to forward space past the BCR following * the call point. If the origin point is an internal call, the call point is a branch with no * following BCR. So zOS needs to determine if it has to jump over a BCR call first. */ ZOS_ONLY(memcpy(&instr_RR, sf->mpc, SIZEOF(instr_RR))); ptrs.instr = sf->mpc; ZOS_ONLY(if ((S390_OPCODE_RR_BCR == instr_RR.opcode) && (0 == instr_RR.r1) && (0 == instr_RR.r2)) ptrs.instr += 2); /* Past BCR 0,0 from external call */ ptrs.instr += 6; /* Past address load of compiler temp arg */ memcpy(&instr_LG, ptrs.instr, SIZEOF(instr_LG)); if ((S390_OPCODE_RXY_LG == instr_LG.opcode) && (S390_SUBCOD_RXY_LG == instr_LG.opcode2) && (GTM_REG_SAVE_RTN_ADDR == instr_LG.r1) && (GTM_REG_XFER_TABLE == instr_LG.b2)) { /* LG of rtn address from xfer table */ RXY.offset = 0; RXY.instr_LG_bits.offset_hi = instr_LG.dh2; RXY.instr_LG_bits.offset_low = instr_LG.dl2; xfer_index = RXY.offset / SIZEOF(void *); } else xfer_index = -1; } # elif defined(__hppa) { hppa_fmt_1 instr_LDX; union { int offset; struct { signed int high:19; unsigned int low:13; } instr_offset; } fmt_1; ptrs.instr = sf->mpc + 8; /* Past address load of compiler temp arg plus rtn call to load of xfer * table call with offset in delay slot */ memcpy(&instr_LDX, ptrs.instr, SIZEOF(instr_LDX)); if (((HPPA_INS_LDW >> HPPA_SHIFT_OP) == instr_LDX.pop) && (GTM_REG_XFER_TABLE == instr_LDX.b) && (R22 == instr_LDX.t)) { /* ldx of rtn address from xfer table */ fmt_1.instr_offset.low = instr_LDX.im14a; fmt_1.instr_offset.high = instr_LDX.im14b; xfer_index = fmt_1.offset / SIZEOF(void *); } else xfer_index = -1; } # elif defined(__ia64) { ia64_bundle xfer_ref_inst; /* Buffer to put built instruction into */ ia64_fmt_A4 adds_inst; /* The actual adds instruction computing xfer reference */ union { int offset; struct { # ifdef BIGENDIAN signed int sign:19; unsigned int imm6d:6; unsigned int imm7b:7; # else unsigned int imm7b:7; unsigned int imm6d:6; signed int sign:19; # endif } instr_offset; } imm14; ptrs.instr = sf->mpc + 16; /* Past address load of compiler temp arg */ # ifdef BIGENDIAN xfer_ref_inst.hexValue.aValue = GTM_BYTESWAP_64(((ia64_bundle *)ptrs.instr)->hexValue.aValue); xfer_ref_inst.hexValue.bValue = GTM_BYTESWAP_64(((ia64_bundle *)ptrs.instr)->hexValue.bValue); # else xfer_ref_inst.hexValue.aValue = ((ia64_bundle *)ptrs.instr)->hexValue.aValue; xfer_ref_inst.hexValue.bValue = ((ia64_bundle *)ptrs.instr)->hexValue.bValue; # endif adds_inst.hexValue = xfer_ref_inst.format.inst3; /* Extract instruction from bundle */ if ((8 == adds_inst.format.pop) && (2 == adds_inst.format.x2a) && (GTM_REG_XFER_TABLE == adds_inst.format.r3) && (IA64_REG_SCRATCH1 == adds_inst.format.r1)) { /* We have an xfer computation instruction. Find the offset to find which opcode */ imm14.instr_offset.imm7b = adds_inst.format.imm7b; /* Low order bits */ imm14.instr_offset.imm6d = adds_inst.format.imm6d; /* upper bits minus sign */ imm14.instr_offset.sign = adds_inst.format.sb; /* Sign bit propagated */ xfer_index = imm14.offset / SIZEOF(void *); } else xfer_index = -1; } # else # error Unsupported Platform # endif if (xf_exfunret == xfer_index) /* Need a QUIT with a non-alias return value */ return 1; else if (xf_exfunretals == xfer_index) /* Need a QUIT with an alias return value */ return 11; else { /* Something weird afoot - had parm block can can't locate EXFUNRET[ALS] opcode. This can happen if * a fatal error occurs during a call before the callee stack frame is actually pushed and we are * called during GTM_FATAL_ERROR.* file creation. Assert that this is the case, else, we just pretend * we didn't find a parm block.. */ assert(process_exiting); return 0; } } fis-gtm-V7.0-005/sr_port/dollar_quit.h0000644000032200000250000000102714342376331016533 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef DOLLAR_QUIT_INCLUDE #define DOLLAR_QUIT_INCLUDE int dollar_quit(void); #endif fis-gtm-V7.0-005/sr_port/dollar_system_init.c0000644000032200000250000000410614342376331020114 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2002-2021 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_string.h" #include "startup.h" #include "dollar_system_init.h" #include "gtm_logicals.h" #include "io.h" #include "iosp.h" #include "stringpool.h" #include "trans_log_name.h" GBLREF mval dollar_system, dollar_system_initial; GBLREF spdesc stringpool; error_def(ERR_LOGTOOLONG); error_def(ERR_TRNLOGFAIL); void dollar_system_init(struct startup_vector *svec) { int4 status; mstr val, tn; char buf[MAX_TRANS_NAME_LEN]; dollar_system.mvtype = MV_STR; dollar_system.str.len = STR_LIT_LEN("47,"); ENSURE_STP_FREE_SPACE(dollar_system.str.len); dollar_system.str.addr = (char *)stringpool.free; memcpy(stringpool.free, "47,", dollar_system.str.len); stringpool.free += dollar_system.str.len; val.addr = SYSID; val.len = STR_LIT_LEN(SYSID); if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &tn, buf, SIZEOF(buf), dont_sendmsg_on_log2long))) { dollar_system.str.len += tn.len; memcpy(stringpool.free, tn.addr, tn.len); stringpool.free += tn.len; } else if (SS_NOLOGNAM == status) { dollar_system.str.len += svec->sysid_ptr->len; memcpy(stringpool.free, svec->sysid_ptr->addr, svec->sysid_ptr->len); stringpool.free += svec->sysid_ptr->len ; } # ifdef UNIX else if (SS_LOG2LONG == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_LOGTOOLONG, 3, LEN_AND_LIT(SYSID), SIZEOF(buf) - 1); # endif else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_TRNLOGFAIL, 2, LEN_AND_LIT(SYSID), status); dollar_system_initial = dollar_system; assert(stringpool.free < stringpool.top); /* it's process initialization after all */ return; } fis-gtm-V7.0-005/sr_port/dollar_system_init.h0000755000032200000250000000115514342376331020125 0ustar librarygtc/**************************************************************** * * * Copyright 2002 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef DOLLAR_SYSTEM_INIT_H_INCLUDED #define DOLLAR_SYSTEM_INIT_H_INCLUDED void dollar_system_init(struct startup_vector *svec); #endif /* DOLLAR_SYSTEM_INIT_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/dollar_zlevel.c0000644000032200000250000000235014342376333017047 0ustar librarygtc/**************************************************************** * * * 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 #include "stack_frame.h" #include "dollar_zlevel.h" GBLREF stack_frame *frame_pointer; int dollar_zlevel() { int count; stack_frame *fp, *fpprev; for (count = 0, fp = frame_pointer; NULL != fp; fp = fpprev) { assert((fp < fp->old_frame_pointer) || (NULL == fp->old_frame_pointer)); fpprev = fp->old_frame_pointer; if (!(fp->type & SFT_COUNT)) continue; if (NULL == fpprev) { /* Next frame is some sort of base frame */ # ifdef GTM_TRIGGER if (fp->type & SFT_TRIGR) { /* Have a trigger baseframe, pick up stack continuation frame_pointer stored by base_frame() */ fpprev = *(stack_frame **)(fp + 1); continue; } else # endif break; /* Some other base frame that stops us */ } count++; } return (count); } fis-gtm-V7.0-005/sr_port/dollar_zlevel.h0000755000032200000250000000115114342376331017053 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef DOLLAR_ZLEVEL_H_INCLUDED #define DOLLAR_ZLEVEL_H_INCLUDED int dollar_zlevel(void); #endif fis-gtm-V7.0-005/sr_port/dollarx.c0000755000032200000250000001355714342376331015672 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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 #include "io.h" #include "iottdef.h" #include "dollarx.h" #include "patcode.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" /* needed by *TYPEMASK* macros defined in gtm_utf8.h */ #include "gtm_utf8.h" LITREF UChar32 u32_line_term[]; #endif GBLREF uint4 *pattern_typemask; GBLREF boolean_t gtm_utf8_mode; void dollarx(io_desc *io_ptr, unsigned char *str, unsigned char *strtop) { unsigned char *str1, *strnext, *strstart, *strcursor, *strprev; int4 esc_level, char_width, total; boolean_t utf8_term, utf8_active, utf8_crlast = FALSE; wint_t curr_char; utf8_active = (gtm_utf8_mode UTF8_ONLY(&& CHSET_M != io_ptr->ochset)) ? TRUE : FALSE; utf8_term = (utf8_active && tt == io_ptr->type) ? TRUE : FALSE; strstart = strcursor = str; if (io_ptr->write_filter) { esc_level = (io_ptr->write_filter & ESC_MASK); while (str < strtop) { if (START != io_ptr->esc_state) { assert (esc_level); str1 = iott_escape(str, strtop, io_ptr); str = str1; if ((FINI == io_ptr->esc_state) || ( BADESC == io_ptr->esc_state)) io_ptr->esc_state = START; continue; } if (!utf8_active) { curr_char = *str; strnext = str + 1; } #ifdef UTF8_SUPPORTED else strnext = UTF8_MBTOWC(str, strtop, curr_char); #endif if (io_ptr->write_filter & CHAR_FILTER) { switch(curr_char) { case NATIVE_LF: if (!utf8_crlast) { /* otherwise CR case will have handled */ io_ptr->dollar.y++; if (io_ptr->length) io_ptr->dollar.y %= io_ptr->length; } else utf8_crlast = FALSE; str = strnext; break; case NATIVE_CR: io_ptr->dollar.x = 0; if (utf8_active && gtmsocket != io_ptr->type) { /* CR implies LF for UTF except for socket which recongizes only NATIVE_LF as a terminator UTF or not. */ utf8_crlast = TRUE; io_ptr->dollar.y++; if (io_ptr->length) io_ptr->dollar.y %= io_ptr->length; } str = strstart = strcursor = strnext; break; case NATIVE_BS: /* if bs at beginning of string but x > 0 need image of line */ if (io_ptr->dollar.x > 0) #ifdef UTF8_SUPPORTED if (utf8_term) { /* get previous character relative to strcursor and back it up */ if (strstart < strcursor) { for ( ; strstart < strcursor; strcursor = strprev) { UTF8_LEADING_BYTE((strcursor - 1), strstart, strprev); UTF8_MBTOWC(strprev, strtop, curr_char); if (U_ISPRINT(curr_char)) break; } strcursor = strprev; /* back up cursor */ GTM_IO_WCWIDTH(curr_char, char_width); io_ptr->dollar.x -= char_width; } } else #endif io_ptr->dollar.x--; str = strnext; utf8_crlast = FALSE; break; case NATIVE_FF: io_ptr->dollar.x = io_ptr->dollar.y = 0; str = strstart = strcursor = strnext; utf8_crlast = FALSE; break; case NATIVE_ESC: utf8_crlast = FALSE; if (esc_level) { str1 = iott_escape(str, strtop, io_ptr); str = str1; if ((FINI == io_ptr->esc_state) || ( BADESC == io_ptr->esc_state)) io_ptr->esc_state = START; continue; } /*** Caution: FALL THROUGH ***/ default: utf8_crlast = FALSE; if (!gtm_utf8_mode) { if (!(pattern_typemask[*str] & PATM_C)) io_ptr->dollar.x++; str++; } UTF8_ONLY( else { assert(str < strtop); /* PATTERN_TYPEMASK macro relies on this */ if (utf8_term) { if (curr_char == u32_line_term[U32_LT_NL] || curr_char == u32_line_term[U32_LT_LS] || curr_char == u32_line_term[U32_LT_PS]) { /* a line terminator not handled above */ io_ptr->dollar.y++; if (io_ptr->length) io_ptr->dollar.y %= io_ptr->length; io_ptr->dollar.x = 0; strstart = strcursor = strnext; char_width = 0; } else GTM_IO_WCWIDTH(curr_char, char_width); if (0 < char_width) { io_ptr->dollar.x += char_width; strcursor = strnext; } } else if (U_ISPRINT(curr_char)) io_ptr->dollar.x++; str = strnext; } ) /* UTF8_ONLY */ break; } } else if (NATIVE_ESC == *str) { assert(esc_level); str1 = iott_escape(str, strtop, io_ptr); str = str1; if ((FINI == io_ptr->esc_state) || (BADESC == io_ptr->esc_state)) io_ptr->esc_state = START; } else { #ifdef UTF8_SUPPORTED if (utf8_term) { GTM_IO_WCWIDTH(curr_char, char_width); io_ptr->dollar.x += char_width; } else #endif io_ptr->dollar.x++; str = strnext; } } #ifdef UTF8_SUPPORTED } else if (utf8_active) { for (total = 0; str < strtop; str = strnext) { strnext = UTF8_MBTOWC(str, strtop, curr_char); if (utf8_term) { /* count display width */ GTM_IO_WCWIDTH(curr_char, char_width); total += char_width; } else total++; /* count number of UTF characters */ } io_ptr->dollar.x += total; #endif } else io_ptr->dollar.x += (unsigned int)(strtop - str); if (io_ptr->dollar.x > io_ptr->width && io_ptr->wrap) { io_ptr->dollar.y += (io_ptr->dollar.x / io_ptr->width); if (io_ptr->length) io_ptr->dollar.y %= io_ptr->length; io_ptr->dollar.x %= io_ptr->width; } } fis-gtm-V7.0-005/sr_port/dollarx.h0000755000032200000250000000113214342376331015661 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef DOLLARX_INCLUDED #define DOLLARX_INCLUDED void dollarx(io_desc *io_ptr, unsigned char *str, unsigned char *strtop); #endif /* DOLLARX_INCLUDED */ fis-gtm-V7.0-005/sr_port/dpgbldir.c0000644000032200000250000004147414342376331016010 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #ifdef DEBUG #include "gtm_ctype.h" #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gbldirnam.h" #include "hashtab_mname.h" #include "iosize.h" #include "dpgbldir.h" #include "filestruct.h" #include "aio_shim.h" #include "gtmio.h" #include "dpgbldir_sysops.h" #include "targ_alloc.h" #include "gtm_logicals.h" #include "zshow.h" #ifdef DEBUG #include "gtm_caseconv.h" #endif GBLREF gd_addr *gd_header; GBLREF gv_namehead *gv_target_list; LITREF char gde_labels[GDE_LABEL_NUM][GDE_LABEL_SIZE]; GBLDEF gdr_name *gdr_name_head; GBLDEF gd_addr *gd_addr_head; error_def(ERR_GDINVALID); /*+ Function: ZGBLDIR This function searches the list of global directory names for the specified names. If not found, it adds the new name to the list and calls GD_LOAD. A pointer to the global directory structure is returned, and the name entry is pointed at it. The global directory pointer is then returned to the caller. Syntax: gd_addr *zgbldir(mval *v) Prototype: ? Return: *gd_addr -- a pointer to the global directory structure Arguments: mval *v -- an mval that contains the name of the global directory to be accessed. Side Effects: NONE Notes: NONE -*/ gd_addr *zgbldir(mval *v) { gd_addr *gd_ptr; gdr_name *name; mstr temp_mstr, *tran_name; for (name = gdr_name_head; name; name = (gdr_name *)name->link) if (v->str.len == name->name.len && !memcmp(v->str.addr, name->name.addr, v->str.len)) return name->gd_ptr; if (!v->str.len) { temp_mstr.addr = GTM_GBLDIR; temp_mstr.len = SIZEOF(GTM_GBLDIR) - 1; tran_name = get_name(&temp_mstr); } else tran_name = get_name(&v->str); gd_ptr = gd_load(tran_name); name = (gdr_name *)malloc(SIZEOF(gdr_name)); if (name->name.len = v->str.len) /* Note embedded assignment */ { name->name.addr = (char *)malloc(v->str.len); memcpy(name->name.addr, v->str.addr, v->str.len); } /* Store translated global directory name as well */ assert(tran_name->len); name->exp_name = *tran_name; /* free up memory allocated for mstr field in get_name. * memory allocated for addr field of the mstr is needed as it has been copied over to "name->exp_name" */ free(tran_name); if (gdr_name_head) name->link = (gdr_name *)gdr_name_head; else name->link = 0; gdr_name_head = name; gdr_name_head->gd_ptr = gd_ptr; return gd_ptr; } /*+ Function: ZGBLDIR_NAME_LOOKUP_ONLY This function searches the list of global directory names for the specified names. If not found, it retruns NULL. Syntax: gd_addr *zgbldir_name_lookup_only(mval *v) Prototype: ? Return: *gd_addr -- a pointer to the global directory structure Arguments: mval *v -- an mval that contains the name of the global directory to be accessed. The name may require translation. Side Effects: NONE Notes: NONE -*/ gd_addr *zgbldir_name_lookup_only(mval *v) { gd_addr *gd_ptr; gdr_name *name; mstr temp_mstr, *tran_name; for (name = gdr_name_head; name; name = (gdr_name *)name->link) if (v->str.len == name->name.len && !memcmp(v->str.addr, name->name.addr, v->str.len)) return name->gd_ptr; return NULL; } /*+ Function: GD_LOAD Syntax: gd_addr *gd_load(mstr *gd_name) Open a global directory file and verify that it is a valid GD. Determine if it has already been opened. If not, setup and initialize the GT.M structures used to access the GD based on the information in the file, enter in the linked list of global directories and return a pointer to it. If already opened, return a pointer to it. Prototype: ? Return: gd_addr * (all errors are signalled) Arguments: gd_name is the name of the file to be opened Side Effects: None Notes: A) While checking may be done earlier for duplicate names, unique identification of files can require OS specific operations useable only after the file is open, so checks must be done within this function for duplicate files. -*/ gd_addr *gd_load(mstr *v) { void *file_ptr; /* is a temporary structure as the file open and manipulations are currently stubs */ header_struct *header, temp_head, disp_head; gd_addr *table, *gd_addr_ptr; gd_binding *map, *map_top, *next_stats_map; gd_region *reg, *reg_top, *first_stats_reg; uint4 t_offset, size; gd_gblname *gnam, *gnam_top; int i, n_regions, arraysize, disp_len; trans_num *array; # ifdef DEBUG boolean_t prevMapIsSpanning, currMapIsSpanning, gdHasSpanGbls; boolean_t isSpannedReg[512]; /* allow up to 256 (* 2 for implicit statsdb) regions in dbg logic */ gd_region *base_reg, *stats_reg; uint4 reg_index; unsigned char regname[MAX_MIDENT_LEN + 1]; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; file_ptr = open_gd_file(v); for (gd_addr_ptr = gd_addr_head; gd_addr_ptr; gd_addr_ptr = gd_addr_ptr->link) { /* if already open then return old structure */ if (comp_gd_addr(gd_addr_ptr, file_ptr)) { close_gd_file(file_ptr); return gd_addr_ptr; } } file_read(file_ptr, SIZEOF(header_struct), (uchar_ptr_t)&temp_head, 1); /* Read in header and verify is valid GD */ for (i = 0; i < GDE_LABEL_NUM; i++) { if (!memcmp(temp_head.label, gde_labels[i], GDE_LABEL_SIZE - 1)) break; } if (GDE_LABEL_NUM == i) { close_gd_file(file_ptr); disp_len = SIZEOF(disp_head.label); format2disp(temp_head.label, SIZEOF(temp_head.label), disp_head.label, &disp_len); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_GDINVALID, 6, v->len, v->addr, LEN_AND_LIT(GDE_LABEL_LITERAL), disp_len, disp_head.label); } size = LEGAL_IO_SIZE(temp_head.filesize); header = (header_struct *)malloc(size); file_read(file_ptr, size, (uchar_ptr_t)header, 1); /* Read in body of file */ table = (gd_addr *)((char *)header + SIZEOF(header_struct)); table->local_locks = (struct gd_region_struct *)((UINTPTR_T)table->local_locks + (UINTPTR_T)table); assert(table->var_maps_len == ((UINTPTR_T)table->regions - (UINTPTR_T)table->maps) - (table->n_maps * SIZEOF(gd_binding))); table->maps = (struct gd_binding_struct *)((UINTPTR_T)table->maps + (UINTPTR_T)table); table->regions = (struct gd_region_struct *)((UINTPTR_T)table->regions + (UINTPTR_T)table); table->segments = (struct gd_segment_struct *)((UINTPTR_T)table->segments + (UINTPTR_T)table); table->gblnames = (struct gd_gblname_struct *)((UINTPTR_T)table->gblnames + (UINTPTR_T)table); table->instinfo = (struct gd_inst_info_struct *)((UINTPTR_T)table->instinfo + (UINTPTR_T)table); if (table == (gd_addr *)table->instinfo) table->instinfo = NULL; table->end = (table->end + (UINTPTR_T)table); n_regions = table->n_regions; for (reg = table->regions, reg_top = reg + n_regions; reg < reg_top; reg++) { t_offset = reg->dyn.offset; reg->dyn.addr = (gd_segment *)((char *)table + t_offset); # ifdef DEBUG assert((reg - table->regions) <= ARRAYSIZE(isSpannedReg)); isSpannedReg[reg - table->regions] = FALSE; # endif reg->owning_gd = table; /* set backpointer from region to owning gbldir */ } IF_LIBAIO(table->thread_gdi = NULL;) # ifdef DEBUG prevMapIsSpanning = FALSE; currMapIsSpanning = FALSE; gdHasSpanGbls = FALSE; # endif for (map = table->maps, map_top = map + table->n_maps; map < map_top; map++) { t_offset = map->reg.offset; map->reg.addr = (gd_region *)((char *)table + t_offset); # ifdef DEBUG currMapIsSpanning = ((map->gvname_len + 1) != map->gvkey_len); if (currMapIsSpanning) gdHasSpanGbls = TRUE; if (currMapIsSpanning || prevMapIsSpanning) isSpannedReg[map->reg.addr - table->regions] = TRUE; prevMapIsSpanning = currMapIsSpanning; # endif t_offset = map->gvkey.offset; map->gvkey.addr = (char *)table + t_offset; assert('\0' == map->gvkey.addr[map->gvname_len]); assert('\0' == map->gvkey.addr[map->gvkey_len]); assert('\0' == map->gvkey.addr[map->gvkey_len - 1]); assert((map->gvname_len + 1) <= map->gvkey_len); } # ifdef DEBUG assert(table->has_span_gbls == gdHasSpanGbls); for (reg = table->regions, reg_top = reg + n_regions; reg < reg_top; reg++) { assert(reg->is_spanned == isSpannedReg[reg - table->regions]); /* Validate "reg->statsDB_reg_index" */ reg_index = reg->statsDB_reg_index; assert((INVALID_STATSDB_REG_INDEX != reg_index) && (reg_index < n_regions)); if (IS_STATSDB_REGNAME(reg)) { /* is a statsDB reg */ stats_reg = reg; base_reg = &table->regions[reg_index]; } else { /* is a base reg */ base_reg = reg; stats_reg = &table->regions[reg_index]; } assert(IS_BASEDB_REGNAME(base_reg)); assert(IS_STATSDB_REGNAME(stats_reg)); assert(base_reg->statsDB_reg_index == (stats_reg - table->regions)); assert(stats_reg->statsDB_reg_index == (base_reg - table->regions)); assert(base_reg->rname_len == stats_reg->rname_len); assert(ARRAYSIZE(regname) > base_reg->rname_len); upper_to_lower(regname, REG_STR_LEN(base_reg)); assert(!memcmp(regname, (stats_reg)->rname, (stats_reg)->rname_len)); /* BYPASSOK */ /* Since a statsdb region points to an MM database, setting defer_time=0 in that segment * automatically disables flush timers (wcs_stale) from being set up. GDE should have ensured this. */ assert(0 == stats_reg->dyn.addr->defer_time); /* The below assert is relied upon by "gvcst_init". If it fails, it most likely means SIZEOF("gvstats_rec_t") * has increased enough to cause GDE to calculate the statsdb blk size as 1536 instead. Fix macro accordingly. */ assert(STATSDB_BLK_SIZE == stats_reg->dyn.addr->blk_size); /* Similar asserts for a few other critical fields that are initialized in GDE & GT.M ("gvcst_init") */ assert(STATSDB_MAX_KEY_SIZE == stats_reg->max_key_size); assert(STATSDB_MAX_REC_SIZE == stats_reg->max_rec_size); assert(stats_reg->mumps_can_bypass); } for (gnam = table->gblnames, gnam_top = gnam + table->n_gblnames; gnam < gnam_top; gnam++) { assert(SIZEOF(gnam->gblname) == (MAX_MIDENT_LEN + 1)); assert('\0' == gnam->gblname[MAX_MIDENT_LEN]); } # endif table->link = gd_addr_head; table->is_dummy_gbldir = FALSE; gd_addr_head = table; fill_gd_addr_id(gd_addr_head, file_ptr); close_gd_file(file_ptr); table->tab_ptr = (hash_table_mname *)malloc(SIZEOF(hash_table_mname)); init_hashtab_mname(table->tab_ptr, 0, HASHTAB_NO_COMPACT, HASHTAB_NO_SPARE_TABLE); /* For most MUPIP commands (except those that can do logical database updates ("is_replicator" == TRUE) * or MUPIP RUNDOWN or MUPIP CREATE, hide the statsdb regions so the commands do not even know about them * let alone operate on them. All of them would have set TREF(ok_to_see_statsdb_regs) appropriately. */ if (!TREF(ok_to_see_statsdb_regs)) { first_stats_reg = NULL; /* Coalesce the regions first given that at least one stats region will always exist (GDE ensures this) */ for (reg = table->regions, reg_top = reg + n_regions; reg < reg_top; reg++) { reg->reservedDBFlags |= RDBF_NOSTATS; if ((NULL == first_stats_reg) && IS_STATSDB_REGNAME(reg)) { first_stats_reg = reg; break; } } if (NULL != first_stats_reg) { # ifdef DEBUG /* Once a stats reg has been seen, all following regions should be stats regions. Assert that */ for (reg = first_stats_reg; reg < reg_top; reg++) assert(IS_STATSDB_REGNAME(reg)); # endif n_regions = table->n_regions = first_stats_reg - table->regions; /* Coalesce the maps next. Move the non-statsdb-maps into one contiguous array */ map = table->maps; map_top = map + table->n_maps; assert(IS_BASEDB_REGNAME(map->reg.addr)); /* first map entry should be a nonstats region */ assert(IS_BASEDB_REGNAME(map_top[-1].reg.addr)); /* last map entry should be a nonstats region */ next_stats_map = NULL; for ( ; map < map_top; map++) { reg = map->reg.addr; if (IS_BASEDB_REGNAME(reg)) { if (NULL != next_stats_map) *next_stats_map++ = *map; } else if (NULL == next_stats_map) next_stats_map = map; } assert(NULL != next_stats_map); assert(next_stats_map < map_top); table->n_maps = next_stats_map - table->maps; } } if (table->has_span_gbls && (TREF(gd_targ_reg_array_size) < n_regions)) { array = TREF(gd_targ_reg_array); if (NULL != array) free(array); arraysize = n_regions * SIZEOF(*array); array = malloc(arraysize); memset(array, 0, arraysize); TREF(gd_targ_reg_array) = array; TREF(gd_targ_reg_array_size) = n_regions; } /* Assert that all runtime-only fields are null-initialized by GDE */ assert(!table->ygs_map_entry_changed); return table; } /*+ Function: GET_NEXT_GDR This function returns the next entry in the list of open global directories. If the input parameter is zero, the first entry is returned, otherwise the next entry in the list is returned. If the input parameter is not a member of the list, then zero will be returned. Syntax: gd_addr *get_next_gdr(gd_addr *prev) Prototype: ? Return: *gd_addr -- a pointer to the global directory structure Arguments: The previous global directory accessed; Side Effects: NONE Notes: NONE -*/ gd_addr *get_next_gdr(gd_addr *prev) { gd_addr *ptr; if (NULL == prev) return gd_addr_head; return prev->link; } /* Maintain list of regions for GTCM_SERVER */ void cm_add_gdr_ptr(gd_region *greg) { gd_addr *ga; ga = (gd_addr *)malloc(SIZEOF(gd_addr)); ga->end = 0; /* signifies a GT.CM gd_addr */ ga->regions = greg; ga->n_regions = 1; ga->link = gd_addr_head; gd_addr_head = ga; return; } void cm_del_gdr_ptr(gd_region *greg) { gd_addr *ga1, *ga2; for (ga1 = ga2 = gd_addr_head; ga1; ga1 = ga1->link) { if (ga1->regions == greg) { if (ga1 == gd_addr_head) gd_addr_head = ga1->link; else ga2->link = ga1->link; free(ga1); break; } ga2 = ga1; } return; } boolean_t get_first_gdr_name(gd_addr *current_gd_header, mstr *log_nam) { gdr_name *name; for (name = gdr_name_head; name; name = (gdr_name *)name->link) { if (name->gd_ptr == current_gd_header) { *log_nam = name->exp_name; return (TRUE); } } return FALSE; } void gd_rundown(void) /* Wipe out the global directory structures */ { gd_addr *gda_cur, *gda_next; gdr_name *gdn_cur, *gdn_next; for (gda_cur = gd_addr_head; NULL != gda_cur; gda_cur = gda_next) { gda_next = gda_cur->link; if (gda_cur->end) { gd_ht_kill(gda_cur->tab_ptr, TRUE); free(gda_cur->tab_ptr); /* free up hashtable malloced in gd_load() */ free(gda_cur->id); /* free up gd_id malloced in gd_load()/fill_gd_addr_id() */ free((char *)gda_cur - SIZEOF(header_struct)); /* free up global directory itself */ } else free(gda_cur); /* GT.CM gd_addr and hence header_struct wasn't malloced in cm_add_gdr_ptr */ } assert(NULL == gv_target_list); gd_header = gd_addr_head = (gd_addr *)NULL; for (gdn_cur = gdr_name_head; NULL != gdn_cur; gdn_cur = gdn_next) { gdn_next = (gdr_name *)gdn_cur->link; if (gdn_cur->name.len) free(gdn_cur->name.addr); free(gdn_cur); } gdr_name_head = (gdr_name *)NULL; } void gd_ht_kill(hash_table_mname *table, boolean_t contents) /* wipe out the hash table corresponding to a gld */ { ht_ent_mname *tabent, *topent; gvnh_reg_t *gvnh_reg; gv_namehead *gvt; gvnh_spanreg_t *gvspan; int i; if (contents) { for (tabent = table->base, topent = tabent + table->size; tabent < topent; tabent++) { if (HTENT_VALID_MNAME(tabent, gvnh_reg_t, gvnh_reg)) { gvspan = gvnh_reg->gvspan; if (NULL == gvspan) { gvt = gvnh_reg->gvt; DEBUG_ONLY(gvnh_reg->gvt = NULL;) /* or else targ_free() might assert fail */ TARG_FREE_IF_NEEDED(gvt); } else { /* this global spans more than one region. free up gvts corresponding to those regions */ for (i = 0; i < (gvspan->max_reg_index - gvspan->min_reg_index + 1); i++) { gvt = GET_REAL_GVT(gvspan->gvt_array[i]); if (NULL != gvt) { # ifdef DEBUG /* below is needed to ensure "targ_free" does not assert fail */ gvspan->gvt_array[i] = NULL; if (gvt == gvnh_reg->gvt) gvnh_reg->gvt = NULL; # endif TARG_FREE_IF_NEEDED(gvt); } } free(gvspan); } free(gvnh_reg); } } } free_hashtab_mname(table); /* We don't do a free(table) in this generic routine because it is called both by GT.M and GT.CM * and GT.CM retains the table for reuse while GT.M doesn't. GT.M fgncal_rundown() takes care of * this by freeing it up explicitly (after a call to ht_kill) in gd_rundown() [dpgbldir.c] */ return; } fis-gtm-V7.0-005/sr_port/dpgbldir.h0000755000032200000250000000770014342376331016012 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef DBGBLDIR_H_INCLUDED #define DBGBLDIR_H_INCLUDED typedef struct gvt_container_struct { gv_namehead **gvt_ptr; /* pointer to a location (either the "gvnh_reg_t->gvt" (for globals that * dont span regions) OR "gvnh_spanreg_t->gvt_array[]" (for globals that * span regions) that contains a pointer to the "gv_target" and that * needs to be updated if/when the gv_target gets re-allocated. */ gv_namehead **gvt_ptr2; /* only for spanning globals, this points to a SECOND location where the * gvt corresponding to the region (that maps the unsubscripted global * reference) is stored (i.e. gvnh_reg_t->gvt). And that needs to be * updated as well if/when the gv_target gets re-allocated. */ gd_region *gd_reg; /* region corresponding to the gv_target that is waiting for reg-open */ struct gvt_container_struct *next_gvtc; } gvt_container; boolean_t get_first_gdr_name(gd_addr *current_gd_header, mstr *log_nam); gd_addr *zgbldir(mval *v); gd_addr *zgbldir_name_lookup_only(mval *v); gd_addr *gd_load(mstr *v); gd_addr *get_next_gdr(gd_addr *prev); mstr *get_name(mstr *ms); void cm_add_gdr_ptr(gd_region *greg); void cm_del_gdr_ptr(gd_region *greg); void *open_gd_file(mstr *v); void gd_rundown(void); void gd_ht_kill(struct hash_table_mname_struct *table, boolean_t contents); GBLREF mstr extnam_str; GBLREF mval dollar_zgbldir; #define GET_CURR_GLD_NAME(GLDNAME) \ MBSTART { \ GLDNAME = extnam_str.len ? &extnam_str : &dollar_zgbldir.str; \ } MBEND /* Define constants for a dummy gld file */ #define IMPOS_GBLNAME_7 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF" /* 7-bytes of 0xFF */ #define IMPOS_GBLNAME_8 IMPOS_GBLNAME_7 "\xFF" /* 8-bytes of 0xFF */ #define IMPOSSIBLE_GBLNAME_31 IMPOS_GBLNAME_8 IMPOS_GBLNAME_8 IMPOS_GBLNAME_8 IMPOS_GBLNAME_7 #define DUMMY_GBLDIR_N_REGS 2 /* one for "DEFAULT" region and one for "default" (statsdb region) */ #define DUMMY_GBLDIR_N_MAPS 5 /* one each for local locks "#)", "%", "%Y", "%Z" and 0xFFFFFF... */ #define DUMMY_GBLDIR_FIRST_MAP "#)" /* local locks */ #define DUMMY_GBLDIR_SECOND_MAP "%" /* start of valid global name */ #define DUMMY_GBLDIR_THIRD_MAP "%Y" /* map for non-%Y global name */ #define DUMMY_GBLDIR_FOURTH_MAP "%Z" /* map for %Y global name */ #define DUMMY_GBLDIR_FIFTH_MAP IMPOSSIBLE_GBLNAME_31 /* last map always corresponds to impossible global name */ #define DUMMY_GBLDIR_MAP_GVN_SIZE(KEY) (SIZEOF(KEY)-1) /* SIZEOF already counts the null byte in the literal so remove it */ #define DUMMY_GBLDIR_MAP_KEY_SIZE(KEY) (SIZEOF(KEY)+1) /* +1 for second null byte (SIZEOF already counts the 1st null byte) */ #define DUMMY_GBLDIR_VAR_MAP_SIZE ROUND_UP2(DUMMY_GBLDIR_MAP_KEY_SIZE(DUMMY_GBLDIR_FIRST_MAP) \ + DUMMY_GBLDIR_MAP_KEY_SIZE(DUMMY_GBLDIR_SECOND_MAP) \ + DUMMY_GBLDIR_MAP_KEY_SIZE(DUMMY_GBLDIR_THIRD_MAP) \ + DUMMY_GBLDIR_MAP_KEY_SIZE(DUMMY_GBLDIR_FOURTH_MAP) \ + DUMMY_GBLDIR_MAP_KEY_SIZE(DUMMY_GBLDIR_FIFTH_MAP), 8) #define DUMMY_GBLDIR_FIX_MAP_SIZE (DUMMY_GBLDIR_N_MAPS * SIZEOF(gd_binding)) #define DUMMY_GBLDIR_TOT_MAP_SIZE (DUMMY_GBLDIR_FIX_MAP_SIZE + DUMMY_GBLDIR_VAR_MAP_SIZE) /* Note: The below definition of DUMMY_GBLDIR_SIZE does not include SIZEOF(header_struct) that is there in the actual gld file */ #define DUMMY_GBLDIR_SIZE (SIZEOF(gd_addr) + DUMMY_GBLDIR_TOT_MAP_SIZE \ + DUMMY_GBLDIR_N_REGS * SIZEOF(gd_region) \ + DUMMY_GBLDIR_N_REGS * SIZEOF(gd_segment)) #endif fis-gtm-V7.0-005/sr_port/dse.h0000644000032200000250000003031114342376331014765 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef DSE_H_INCLUDED #define DSE_H_INCLUDED error_def(ERR_DSEWCREINIT); #define BADDSEBLK (block_id)-1 #define DSE_DMP_TIME_FMT "DD-MON-YEAR 24:60:SS" #define DSEBLKCUR TRUE #define DSEBLKNOCUR FALSE #define DSEBMLOK FALSE #define DSENOBML TRUE #define PATCH_SAVE_SIZE 128 #define SPAN_START_BYTE 0x02 #define SPAN_BYTE_MAX 255 #define SPAN_BYTE_MIN 1 #define MAX_UTIL_LEN 80 /* The width of the char buffer used by various DSE files to write output to * before passing to util_out_print() * Set to 80 in order to match standard terminal width */ #define GET_CURR_TIME_IN_DOLLARH_AND_ZDATE(dollarh_mval, dollarh_buffer, zdate_mval, zdate_buffer) \ { /* gets current time in the mval "dollarh_mval" in dollarh format and in the mval "zdate_mval" in ZDATE format \ * the ZDATE format string used is DSE_DMP_TIME_FMT \ * the dollarh_buffer and zdate_buffer are buffers which the corresponding mvals use to store the actual time string \ */ \ GBLREF mval dse_dmp_time_fmt; \ GBLREF spdesc stringpool; \ LITREF mval literal_null; \ \ op_zhorolog(&dollarh_mval, FALSE); /* returns $H value in stringpool */ \ assert(SIZEOF(dollarh_buffer) >= dollarh_mval.str.len); \ /* if op_fnzdate (called below) calls stp_gcol, dollarh_mval might get corrupt because it is not known to stp_gcol. \ * To prevent problems, copy from stringpool to local buffer */ \ memcpy(dollarh_buffer, dollarh_mval.str.addr, dollarh_mval.str.len); \ dollarh_mval.str.addr = (char *)dollarh_buffer; \ stringpool.free -= dollarh_mval.str.len; /* now that we've made a copy, we don't need dollarh_mval in stringpool */ \ op_fnzdate(&dollarh_mval, &dse_dmp_time_fmt, (mval *)&literal_null, (mval *)&literal_null, &zdate_mval); \ /* op_fnzdate() returns zdate formatted string in stringpool */ \ assert(SIZEOF(zdate_buffer) >= zdate_mval.str.len); \ /* copy over stringpool string into local buffer to ensure zdate_mval will not get corrupt */ \ memcpy(zdate_buffer, zdate_mval.str.addr, zdate_mval.str.len); \ zdate_mval.str.addr = (char *)zdate_buffer; \ stringpool.free -= zdate_mval.str.len; /* now that we've made a copy, we don't need zdate_mval in stringpool anymore */ \ } typedef struct { block_id blk; char *bp; gd_region *region; char *comment; short int ver; } save_strct; enum dse_fmt { CLOSED_FMT = 0, GLO_FMT, ZWR_FMT, OPEN_FMT }; /* Grab crit for dse* functions taking into account -nocrit if specified */ #define DSE_GRAB_CRIT_AS_APPROPRIATE(WAS_CRIT, WAS_HOLD_ONTO_CRIT, NOCRIT_PRESENT, CS_ADDRS, GV_CUR_REGION) \ { \ if (!WAS_CRIT) \ { \ if (NOCRIT_PRESENT) \ CS_ADDRS->now_crit = TRUE; \ else \ grab_crit_encr_cycle_sync(GV_CUR_REGION, WS_55); \ WAS_HOLD_ONTO_CRIT = CS_ADDRS->hold_onto_crit; \ CS_ADDRS->hold_onto_crit = TRUE; \ } \ } /* Rel crit for dse* functions taking into account -nocrit if specified */ #define DSE_REL_CRIT_AS_APPROPRIATE(WAS_CRIT, WAS_HOLD_ONTO_CRIT, NOCRIT_PRESENT, CS_ADDRS, GV_CUR_REGION) \ { \ if (!WAS_CRIT) \ { \ assert(CS_ADDRS->hold_onto_crit); \ assert((TRUE == WAS_HOLD_ONTO_CRIT) || (FALSE == WAS_HOLD_ONTO_CRIT)); \ CS_ADDRS->hold_onto_crit = WAS_HOLD_ONTO_CRIT; \ if (NOCRIT_PRESENT) \ CS_ADDRS->now_crit = FALSE; \ else \ rel_crit(GV_CUR_REGION); \ } \ } #ifdef UNIX # define GET_CONFIRM(X, Y) \ { \ PRINTF("CONFIRMATION: "); \ FGETS((X), (Y), stdin, fgets_res); \ Y = strlen(X); \ } #else # define GET_CONFIRM(X, Y) \ { \ if (!cli_get_str("CONFIRMATION", (X), &(Y))) \ { \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_DSEWCINITCON); \ return; \ } \ } #endif #define GET_CONFIRM_AND_HANDLE_NEG_RESPONSE \ { \ int len; \ char confirm[256]; \ \ len = SIZEOF(confirm); \ GET_CONFIRM(confirm, len); \ if (confirm[0] != 'Y' && confirm[0] != 'y') \ { \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_DSEWCINITCON); \ return; \ } \ } #define DSE_WCREINIT(CS_ADDRS) \ { \ assert(CS_ADDRS->now_crit); \ if (dba_bg == CS_ADDRS->hdr->acc_meth) \ bt_refresh(CS_ADDRS, TRUE); \ db_csh_ref(CS_ADDRS, TRUE); \ send_msg_csa(CSA_ARG(CS_ADDRS) VARLSTCNT(4) ERR_DSEWCREINIT, 2, DB_LEN_STR(gv_cur_region)); \ } /* This macro is currently used only inside the BUILD_AIMG_IF_JNL_ENABLED_AND_T_END_WITH_EFFECTIVE_TN macro */ #define BUILD_ENCRYPT_TWINBUFF_IF_NEEDED(CSA, CSD, TN) \ { \ blk_hdr_ptr_t bp, save_bp; \ gd_segment *seg; \ int gtmcrypt_errno, req_enc_blk_size; \ boolean_t use_new_key; \ \ GBLREF gd_region *gv_cur_region; \ \ if (USES_ENCRYPTION(CSD->is_encrypted) && (TN < CSA->ti->curr_tn)) \ { /* BG and db encryption are enabled and the DSE update caused the block-header to potentially have a tn LESS \ * than before. At this point, the global buffer (corresponding to blkhist.blk_num) reflects the contents of \ * the block AFTER the dse update (bg_update would have touched this), whereas the corresponding encryption \ * global buffer reflects the contents of the block BEFORE the update. \ * \ * Normally, wcs_wtstart would take care of propagating the tn update from the regular global buffer to the \ * corresponding encryption buffer. But if before it gets a chance, a process goes to t_end as a part of a \ * subsequent transaction and updates this same block, then, since the blk-hdr-tn potentially decreased, it is \ * possible that the PBLK writing check (that compares blk-hdr-tn with the epoch_tn) will decide to write a \ * PBLK for this block, even though a PBLK was already written for this block as part of a previous \ * DSE CHANGE -BL -TN in the same epoch. In this case, since the db is encrypted, the logic will assume that \ * there were no updates to this block because the last time wcs_wtstart updated the encryption buffer and, \ * therefore, use that to write the PBLK, which is incorrect, for it does not yet contain the tn update. \ * \ * The consequence of this is that we would be writing an older before-image PBLK record to the journal file. \ * To prevent this situation, we update the encryption buffer here (before releasing crit) using logic similar \ * to that in wcs_wtstart, to ensure it is in sync with the regular global buffer. To prevent t_end from \ * releasing crit, we set CSA->hold_onto_crit to TRUE. \ * \ * Note that although we use cw_set[0] to access the global buffer corresponding to the block number being \ * updated, cw_set_depth at this point is 0 because t_end resets it. This is considered safe since cw_set is a \ * static array (as opposed to malloced memory) and hence is always available and valid until it gets \ * overwritten by subsequent updates. \ */ \ bp = (blk_hdr_ptr_t)GDS_ANY_REL2ABS(CSA, cw_set[0].cr->buffaddr); \ DBG_ENSURE_PTR_IS_VALID_GLOBUFF(CSA, CSD, (sm_uc_ptr_t)bp); \ save_bp = (blk_hdr_ptr_t)GDS_ANY_ENCRYPTGLOBUF(bp, CSA); \ DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(CSA, CSD, (sm_uc_ptr_t)save_bp); \ assert((bp->bsiz <= CSD->blk_size) && (bp->bsiz >= SIZEOF(*bp))); \ req_enc_blk_size = MIN(CSD->blk_size, bp->bsiz) - SIZEOF(*bp); \ if (BLK_NEEDS_ENCRYPTION(bp->levl, req_enc_blk_size)) \ { \ ASSERT_ENCRYPTION_INITIALIZED; \ memcpy(save_bp, bp, SIZEOF(blk_hdr)); \ use_new_key = USES_NEW_KEY(CSD); \ GTMCRYPT_ENCRYPT(CSA, (use_new_key ? TRUE : CSD->non_null_iv), \ (use_new_key ? CSA->encr_key_handle2 : CSA->encr_key_handle), \ (char *)(bp + 1), req_enc_blk_size, (char *)(save_bp + 1), \ bp, SIZEOF(blk_hdr), gtmcrypt_errno); \ if (0 != gtmcrypt_errno) \ { \ seg = gv_cur_region->dyn.addr; \ GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, seg->fname_len, seg->fname); \ } \ } else \ memcpy(save_bp, bp, bp->bsiz); \ } \ } /* This macro is used whenever t_end needs to be invoked with a 3rd parameter != TN_NOT_SPECIFIED. * Currently the only two usages of this are from DSE and hence this macro is placed in dse.h. */ #define BUILD_AIMG_IF_JNL_ENABLED_AND_T_END_WITH_EFFECTIVE_TN(CSA, CSD, CTN, HIST) \ { \ trans_num cTn; \ boolean_t was_hold_onto_crit; \ \ GBLREF gd_region *gv_cur_region; \ GBLREF boolean_t unhandled_stale_timer_pop; \ GBLREF sgmnt_addrs *cs_addrs; \ \ cTn = CTN; \ assert(CSA == cs_addrs); \ BUILD_AIMG_IF_JNL_ENABLED(CSD, cTn); \ was_hold_onto_crit = CSA->hold_onto_crit; \ CSA->hold_onto_crit = TRUE; /* need this so t_end doesn't release crit (see below comment for why) */ \ t_end(HIST, NULL, cTn); \ BUILD_ENCRYPT_TWINBUFF_IF_NEEDED(CSA, CSD, cTn); \ CSA->hold_onto_crit = was_hold_onto_crit; \ if (!was_hold_onto_crit) \ rel_crit(gv_cur_region); \ if (unhandled_stale_timer_pop) \ process_deferred_stale(); \ } #define CLEAR_DSE_COMPRESS_KEY \ MBSTART { \ GBLREF char patch_comp_key[MAX_KEY_SZ + 1]; \ GBLREF unsigned short patch_comp_count; \ \ patch_comp_count = patch_comp_key[0] = patch_comp_key[1] = 0; \ } MBEND void dse_adrec(void); void dse_adstar(void); void dse_all(void); boolean_t dse_b_dmp(void); void dse_cache(void); void dse_chng_bhead(void); void dse_chng_fhead(void); void dse_chng_rhead(void); void dse_close(void); void dse_crit(void); void dse_ctrlc_handler(int sig); void dse_ctrlc_setup(void); int dse_data(char *dst, int *len); void dse_dmp(void); void dse_dmp_fhead (void); void dse_eval(void); void dse_exhaus(int4 pp, int4 op); void dse_f_blk(void); void dse_f_free(void); void dse_f_key(void); void dse_f_reg(void); boolean_t dse_fdmp(sm_uc_ptr_t data, int len); boolean_t dse_fdmp_output(void *addr, int4 len); gv_namehead *dse_find_gvt(gd_region *reg, char *name, int name_len); void dse_find_roots(block_id index); void dse_flush(void); block_id dse_getblk(char *element, boolean_t nobml, boolean_t carry_curr); int dse_getki(char *dst, int *len, char *qual, int qual_len); void dse_help(void); void dse_integ(void); boolean_t dse_is_blk_free(block_id blk, sm_int_ptr_t cycle, cache_rec_ptr_ptr_t cr); int dse_is_blk_in(sm_uc_ptr_t rp, sm_uc_ptr_t r_top, short size); int dse_ksrch(block_id srch, block_id_ptr_t pp, int4 *off, char *targ_key, int targ_len); int4 dse_lm_blk_free(int4 blk, sm_uc_ptr_t base_addr); void dse_m_rest(block_id blk, unsigned char *bml_list, int4 bml_size, v_block_id_ptr_t blks_ptr, bool in_dir_tree); void dse_maps(void); void dse_open (void); int dse_order(block_id srch, block_id_ptr_t pp, int4 *op, char *targ_key, short int targ_len, bool dir_data_blk); void dse_over(void); void dse_page(void); boolean_t dse_r_dmp(void); void dse_range(void); void dse_remove(void); void dse_rest(void); void dse_rmrec(void); void dse_rmsb(void); void dse_save(void); void dse_shift(void); void dse_version(void); void dse_wcreinit (void); sm_uc_ptr_t dump_record(sm_uc_ptr_t rp, block_id blk, sm_uc_ptr_t bp, sm_uc_ptr_t b_top); int parse_dlr_char(char *src, char *top, char *dlr_subsc); #endif fis-gtm-V7.0-005/sr_port/dse.hlp0000644000032200000250000033230314342376331015327 0ustar librarygtc1 Operations Operations The GT.M Database Structure Editor, DSE, is primarily a tool for authorized GT.M consultants to examine and, under unusual circumstances, repair GT.M Database Structure (GDS) databases. With DSE, it is possible to see and change most of the attributes of a GT.M database. DSE gives all possible control over a database and therefore, it may cause irreparable damage when used without knowing the consequences. Therefore, you unless you have extensive experience, you should always get guidance from FIS or an equivalently knowledgeable support resource before running any DSE command that changes any attribute of any production database or other database you value. However, you can use those DSE commands that let you see the attributes of your database for collecting database metrics and monitoring status. GT.M installation procedure places the DSE utility program in a directory specified by the environment variable gtm_dist. Invoke DSE using the "dse" command at the shell prompt. If this does not work, consult your system manager to investigate setup and file access issues. Example: $gtm_dist/dse File/usr/name/mumps.dat Region DEFAULT DSE> DSE displays the DSE> prompt. You may also specify a command when entering DSE. By default, DSE starts with the region that stands first in the list of regions arranged in alphabetical order. In the above example, the first region is DEFAULT. You may also specify a command when entering DSE. Example: $gtm_dist/dse dump -fileheader This command displays the fileheader of the region that stands first in the list of regions arranged in alphabetical order and then returns to the shell prompt. To look at other regions, at the DSE prompt you must first issue a FIND -REGION= command. As previously mentioned, DSE provides control over most of the attributes of your database. With DSE, it is possible to examine them and,with a few exceptions, change them. All DSE commands are divided into two categories-Change commands and Inquiry commands. Change commands allow you to modify the attribute of your database, in most cases without any warning or error. As the low level tool of last resort, Change commands allow you to take certain actions that can cause extensive damage when undertaken without an extensive understanding of the underlying data structures on disk and in memory and with an imperfect understanding of the commands issued. Do not use the Change commands unless you know exactly what you are doing and have taken steps to protect yourself against mistakes, both inadvertent and resulting from an incomplete understanding of the commands you issue. Change commands are not required for normal operation, and are usually only used under the direction of FIS support to recover from the unanticipated consequences of failures not adequately planned for (for example, you should configure GT.M applications such that you never need a Change command to recover from a system crash). Inquiry commands let you see the attributes of your database. You may frequently use the inquiry commands for collecting your database metrics and status reporting. The list of Change commands is as follows: AD[D] AL[L] B[UFFER _FLUSH] CH[ANGE] CR[ITICAL] REM[OVE] RES[TORE] SH[IFT] W[CINIT] OV[ERWRITE] M[APS] -BU[SY] -F[REE] -M[ASTER] -R[ESTORE_ALL] The list of Inquiry commands is as follows: CL[OSE] D[UMP] EV[ALUATE] EX[IT] F[IND] H[ELP] I[NTEGRIT] M[APS] -BL[OCK] OP[EN] P[AGE] RA[NGE] SA[VE] SP[AWN] Although DSE can operate concurrently with other processes that access the same database file, FIS strongly recommends using DSE in standalone mode when using Change commands. Some DSE operations can adversely impact the database when they occur during active use of the database. Other DSE operations may be difficult to perform in a logically sound fashion because a DSE operator works on a block at a time, while normal database operations update all related blocks almost simultaneously. **Caution** When DSE attaches to a database with a version that does not match the DSE version, DSE issues an informational message and continues. At this point, you should exit DSE and find the version of DSE that matches the database. You should continue after this warning if and only if you are certain that the DSE is indeed from the GT.M version that has the database open (and hence the error results from a damaged database file header or shared memory that you intend to repair, following instructions from FIS). Use the DSE EXIT, or QUIT command to leave DSE. 1 Commands Commands The general format of DSE commands is: command [-qualifier[...]] [object[,...]] DSE interprets all numeric input as hexadecimal, except for time values, the values for the following qualifiers when used with CHANGE -FILEHEADER: -BLK_SIZE=, DECLOCATION=, -KEY_MAX_SIZE=, -RECORD_MAX_SIZE, -REFERENCE_COUNT=, -TIMERS_PENDING and -WRITES_PER_FLUSH, and the value for -VERSION= when used with the REMOVE and RESTORE commands. These conventions correspond to the displays provided by DSE and by MUPIP INTEG. 2 ADD ADD Adds a record to a block. The format of the ADD command for blocks with a level greater than zero (0) is: ADD [-B[LOCK]=[block] {-OFFSET=offset|-RECORD=record} -STAR -POINTER=block or ADD [-B[LOCK]=[block] {-OFFSET=offset|-RECORD=record} -KEY=key -POINTER=pointer The format of the ADD command for level 0 blocks is: ADD [-B[LOCK]=[block] {-OFFSET=offset|-RECORD=record} -KEY=key -DATA=string The ADD command requires either the -OFFSET or -RECORD qualifier to position the record in the block, and either the -KEY or the -STAR qualifier to define the key for the block. The -STAR qualifier is invalid at level 0 (a data block). The ADD command requires the -DATA qualifier at level 0 or the -POINTER qualifier at any other level to provide record content. 3 Qualifiers Qualifiers -B[LOCK]=block-number Specifies the block to receive the new record. On commands with no -BLOCK= qualifier, DSE uses the last block handled by a DSE operation. When no block has been accessed, that is, on the first block-oriented command, DSE uses block one (1). -D[ATA]=string Specifies the data field for records added to a data block. Use quotation marks around the string and escape codes of the form \a\b, where "a" and "b" are hexadecimal digits representing non-printing characters. \\ translates to a single backslash. \'\' translates to a NULL value. Incompatible with: -STAR,-POINTER -K[EY]=key Specifies the key of the new record. Enclose M-style global references, including the leading caret symbol (^), in quotation marks (" "). Incompatible with: -STAR -O[FFSET]=offset Adds the new record at the next record boundary after the specified offset. Incompatible with: -RECORD, -STAR -P[OINTER]=pointer Specifies the block pointer field for records added to an index block. The -POINTER qualifier cannot be used at level 0. Note this means that to add pointers at level 0 of the Directory Tree you must specify a string of bytes or temporarily change the block level. Incompatible with: -DATA -R[ECORD]=record-number Specifies a record number of the new record. Incompatible with: -OFFSET,-STAR -S[TAR] Adds a star record (that is, a record that identifies the last record in an indexed block) at the end of the specified block. The -STAR qualifier cannot be used at level 0. Incompatible with: -DATA,-KEY,-OFFSET,-RECORD 3 Examples Examples DSE>add -block=6F -record=57 -key="^Capital(""Mongolia"")" -data="Ulan Bator" This command adds a new record with key ^Capital("Mongolia") at the specified location. Note that this command is applicable to level 0 blocks only. Example: DSE>add -star -bl=59A3 -pointer=2 This command adds a star record in block 59A3. Note that this command is applicable to blocks > level 0. Example: DSE>add -block=3 -record=4 -key="^Fruits(4)" -data="Grapes" Suppose your database has 3 global nodes -- ^Fruits(1)="Apple", ^Fruits(2)="Banana", and ^Fruits(3)="Cherry", then the above command adds a new node ^Fruits(4)="Grapes" at record 4. Note that this command is applicable to level 0 blocks only. The interpreted output as a result of the above command looks like the following: Block 3 Size 4B Level 0 TN 4 V6 Rec:1 Blk 3 Off 10 Size 14 Cmpc 0 Key ^Fruits(1) 10 : | 14 0 0 0 46 72 75 69 74 73 0 BF 11 0 0 41 70 70 6C 65| | . . . . F r u i t s . . . . . A p p l e| Rec:2 Blk 3 Off 24 Size D Cmpc 8 Key ^Fruits(2) 24 : | D 0 8 0 21 0 0 42 61 6E 61 6E 61 | | . . . . ! . . B a n a n a | Rec:3 Blk 3 Off 31 Size D Cmpc 8 Key ^Fruits(3) 31 : | D 0 8 0 31 0 0 43 68 65 72 72 79 | | . . . . 1 . . C h e r r y | Rec:4 Blk 3 Off 3E Size D Cmpc 8 Key ^Fruits(4) 3E : | D 0 8 0 41 0 0 47 72 61 70 65 73 | | . . . . A . . G r a p e s | Example: $dse add -star -bl=1 -pointer=2 This command adds a star record in block 1. Note that this command is applicable to blocks > Level 0. Example: $ dse add -block=4 -key="^Vegetables" -pointer=7 -offset=10 This command creates a block with key ^Vegetables pointing to block 7. Example: DSE> add -record=2 -key="^foo" -data=\'\' This example adds a new node (set ^foo="") as the second record of the current database block. 2 ALL ALL Applies action(s) specified by a qualifier to all GDS regions defined by the current global directory. The format of the ALL command is: AL[L] [ -B[UFFER_FLUSH] -C[RITINIT] -D[UMP] -A[LL] -[NO]F[REEZE] -O[VERRIDE]] -REF[ERENCE] -REL[EASE] -REN[EW] -S[EIZE] -W[CINIT] ] o This is a very powerful command; use it with caution. o Be especially careful if you have an overlapping database structure (for example, overlapping regions accessed from separate application global directories). o If you use this type of database structure, you may need to construct special Global Directories that exclude overlapped regions to use with DSE. 3 Qualifiers Qualifiers -ALL Displays additional information on the database most of which is useful for FIS in diagnosing issues. Meaningful only with: -D[UMP] -BUFFER_FLUSH Flushes to disk the file header and all pooled buffers for all regions of the current global directory. Incompatible with: -RENEW -C[RITINIT] Initializes critical sections for all regions of the current directory. Incompatible with: -RENEW, -RELEASE, -SIEZE **Caution** Never use CRITINIT while concurrent updates are in progress as doing so may damage the database. -[D]UMP Displays fileheader information. Compatible with: -A[LL] -[NO]F[REEZE] Freezes or prevents updates all regions of the current global directory. o The FREEZE qualifier freezes all GDS regions except those previously frozen by another process . Regions frozen by a particular process are associated with that process . o A frozen region may be unfrozen for updates in one of two ways: The process which froze the region may unfreeze it with the -NOFREEZE qualifier; or another process may override the freeze in conjunction with the -OVERRIDE qualifier. o By default, the -NOFREEZE qualifier unfreezes only those GDS regions that were previously frozen by a process . Once a region is unfrozen, it may be updated by any process .To unfreeze all GDS regions of the Global Directory, use the -OVERRIDE qualifier. o DSE releases any FREEZE it holds when it exits, therefore, use the same DSE invocation or SPAWN to perform operations after executing the ALL -FREEZE command. Incompatible with: -RENEW -O[VERRIDE] Overrides the ALL -FREEZE or ALL -NOFREEZE operation. When used with -NOFREEZE, -OVERRIDE unfreezes all GDS regions, including those frozen by other users. When used with -FREEZE, -OVERRIDE freezes all GDS regions, including those frozen by other processes associating all such freezes with the current process. The current process must then use -NOFREEZE to unfreeze the database; any other process attempting a -NOFREEZE should also have to include the -OVERRIDE qualifier. Meaningful only with: [NO]FREEZE -REF[ERENCE] Resets the reference count field to 1 for all regions of the current global directory. o A Reference count is a file header element field that tracks how many processes are accessing the database with read/write permissions. o This qualifier is intended for use when DSE is the only process attached to the databases of the curent global directory. Using it when there are other users attached produces an incorrect value. Incompatible with: -RENEW -REL[EASE] Releases critical sections for all regions of the current global directory. Incompatible with: -CRITINIT, -RENEW, -SEIZE -REN[EW] Reinitializes the critical sections (-CRITICAL) and buffers (-WCINIT), resets reference counts (-REFERENCE_COUNT) to 1, and clears freeze (-NOFREEZE) for all regions of the current global directory . o -RENEW requires confirmation. o The RENEW action will cause all current accessors of the affected database regions to receive a fatal error on their next access attempt. o This operation is dangerous, drastic, and a last resort if multiple database have hangs that have not yielded to other resolution attempts; there is almost never a good reason to use this option. -S[EIZE] Seizes the critical section for all regions of the current global directory. The -SEIZE qualifier is useful when you encounter a DSEBLKRDFAIL error, generated when DSE is unable to read a block from the database. Incompatible with: -RENEW, -RELEASE, -CRITINIT -W[CINIT] Reinitializes the buffers for all regions of the current global directory. -WCINIT requires confirmation. **Caution** This operation is likely to cause database damage when used while concurrent updates are in progress. Incompatible with: -RENEW 3 Examples Examples Example: DSE> all flush -buffer_flush This command flushes the file header and cache buffers to disk for all regions. Example: DSE> ALL -CRITINIT This command initializes critical sections for all regions of the current directory. Example: DSE> ALL -FREEZE DSE> SPAWN "mumps -dir" The first command freezes all regions of the current global directory. The second command creates an child (shell) process and executes the "mumps -dir" command. Then type S ^A=1 at GTM prompt. Notice that the command hangs because of the DSE FREEZE in place. Example: DSE> ALL -NOFREEZE -OVERRIDE This command removes the FREEZE on all current region including the FREEZE placed by other users. Example: DSE> ALL -REFERENCE This command sets the reference count field in the file header(s) to 1. Example: DSE> ALL -RELEASE This command releases critical sections owned by the current process for all regions of the current global directory. Example: DSE> ALL -RENEW This command reinitializes critical sections, buffers, resets the reference count to 1, and clears freeze for all regions of the current global directory. Example: DSE> ALL -SEIZE This command seizes all critical sections for all regions of the current global directory. Example: DSE> ALL -WCINIT This command reinitializes the buffers for all regions of the current global directory. 2 Buffer_flush Buffer_flush Flushes the file header and the current region's buffers to disk. The format of the BUFFER_FLUSH command is: B[UFFER_FLUSH] The BUFFER_FLUSH command has no qualifiers. 2 CHange CHange The CHANGE command changes fields of a block, file, or record header. The format of the CHANGE command is: CH[ANGE] The CHANGE command either has a -FILEHEADER qualifier or an implicit or explicit -BLOCK qualifier, plus one or more of their associated qualifiers, to define the target of the change. -BL[OCK]=block-number and one or more of the following qualifiers: -BS[IZ]=block-size -L[EVEL]=level -TN[=transaction-number] -OF[FSET]=offset -RE[CORD]=record-number -CM[PC]=compression-count -RS[IZ]=record-size or -F[ILEHEADER] and one or more of the following qualifiers: -AB[ANDONED_KILLS]=value -AVG_BLKS_READ=Average-blocks-read -B_B[YTESTREAM]=transaction-number -B_C[OMPREHENSIVE]=transaction-number -B_D[ATABASE]=transaction-number -B_I[NCREMENTAL]=transaction-number -B_R[ECORD]=transaction-number -BLK_SIZE=block-size -BLO[CKS_FREE]=free-blocks -CU[RRENT_TN]=transaction-number -COM[MITWAIT_SPIN_COUNT]=boolean -DEC[LOCATION]=value -DEF[_COLLATION]=value -ENCRYPTION_HASH -FL[USH_TIME][=delta-time] -FR[EEZE]=value -FU[LLY_UPGRADED]=boolean -GV[STATSRESET] -HA[RD_SPIN_COUNT]=Mutex-hard-spin-count -HE[XLOCATION]=value -INT[ERRUPTED_RECOV]=boolean -JNL_YIELD_LIMIT=journal-yeild-limit -KE[Y_MAX_SIZE]=key-max-size -KI[LL_IN_PROG]=value -M[ACHINE_NAM]=value -N[ULL_SUBSCRIPTS]=value -NO[CRIT] -OV[ERRIDE] -Q[DBRUNDOWN] -RC_SRV_COUNT -RE_READ_TRIGGER=read-trigger -REC[ORD_MAX_SIZE]=record-max-size -REF[ERENCE_COUNT]=reference-count -REG[_SEQNO]=sequence-number -RESERVED_BYTES=reserved-bytes -SLEE[P_SPIN_COUNT]=mutex-sleep-spin-count -SPIN[_SLEEP_MASK]=mutex-spin-sleep-mask -STRM_NUM=stream-number STRM_REG_SEQNO=hexa -TIM[ERS_PENDING]=integer -TO[TAL_BLKS]=total-blocks -TR[IGGER_FLUSH]=trigger-flus -UPD_RESERVED_AREA=reserved-area -UPD_WRITER_TRIGGER_FACTOR=trigger-factor -W[RITES_PER_FLUSH]=writes-per-flush -WAIT_DISK=wait-disk -Zqgblmod_S[EQNO]=sequence-number -Zqgblmod_TN=sequence-number 3 BLock_Qualifiers BLock Qualifiers This section describes -BLOCK and all of its qualifiers. -BL[OCK]=block_number Specifies the block to modify. The -BLOCK qualifier is incompatible with the -FILEHEADER qualifier and all qualifiers related to -FILEHEADER. -BLOCK is the default qualifier. On commands with neither a -BLOCK nor a -FILEHEADER qualifier, DSE uses the last block handled by a DSE operation. When no block has been accessed, that is, on the first block-oriented command, DSE uses block one (1). Incompatible with: -FILEHEADER and qualifiers used with -FILEHEADER The following qualifiers operate on a block header. -BS[IZ]=block_size Changes the block size field of the specified block. o block_size is in hexadecimal form. o Decreasing the block size can result in loss of existing data. **Note** The block size must always be less than or equal to the block size in the file header. Use only with: -BLOCK, -LEVEL, -TN -L[EVEL]=level Changes the level field for the specified block. **Note** DSE lets you change the level of a bitmap block to -1 (the value of the level for a bitmap block) when the bitmap level gets corrupted and takes on an arbitrary value. Note that you should specify -1 in hexadecimal form, that is, FF. Use only with: -BLOCK, -BSIZ, -TN Example: DSE >change -level=FF -TN[=transaction_number] Changes the transaction number for the current block. o When a CHANGE command does not include a -TN=, DSE sets the transaction number to the current transaction number. o Manipulation of the block transaction number affects MUPIP BACKUP -BYTESTREAM, and -ONLINE. Use only with: -BLOCK, -BSIZ, -LEVEL -OF[FSET]=offset Specifies the offset, in bytes, of the target record within the block. If the offset does not point to the beginning of a record, DSE rounds down to the last valid record start (for example, CHANGE -OFFSET=10 starts at -OFFSET=A, if that was the last record). Use only with: -BLOCK, -CMPC, and -RSIZ. -RE[CORD]=record_number Specifies the record number of the target record. Use only with: -BLOCK, -CMPC, and -RSIZ. -CM[PC]=compression_count Change the compression count field of the specified record. o The compression count specifies the number of bytes at the beginning of a key that are common to the previous key in the same block. o Because compression counts propagate from the "front" of the block, this can potentially change the keys of all records following it in the block. If the goal is to change only a single record, it may be preferable to add a new record and remove the old one. Use only with: -BLOCK, -RECORD, -OFFSET, -RSIZE -RS[IZ]=record_size Changes the record size field of the specified record. **Caution** Changing -RSIZ impacts all records following it in the block. Use only with: -BLOCK, -RECORD, -CMPC, -OFFSET Example: DSE> change -record=3 -rsiz=3B -block=2 This command changes the record size of record 3 block 2 to 59 (Hex: 3B) bytes. 3 FIleheader_Qualifiers FIleheader Qualifiers This section describes the -FILEHEADER qualifier and the other qualifiers that operate on a file header. -FI[LEHEADER] Modifies a file header element that you specify with an associated qualifier. Incompatible with: -BSIZ, -CMPC, -TN, -LEVEL, -OFFSET, -RECORD, -RSIZ -AB[ANDONED_KILLS]=value Changes the value of the Abandoned Kills field. The value can be "NONE" or a decimal positive integer. Use only with: -FILEHEADER -BLK[_SIZE]=block_size Changes the decimal block size field of the current file. o DSE does not allow you to change the block size to any arbitrary value. It always rounds the block size to the next higher multiple of 512. o Use the CHANGE -BLK_SIZE qualifier only upon receiving instructions from FIS and only in conjunction with the -FILEHEADER qualifier. This DSE command cannot change the working block size of a database and is useful only under very limited and extrordinary circumstances. If you need to change the block size on a database file, unload the data with MUPIP EXTRACT (or an appropriate alternative), change the global directory with GDE to specify the new block size, recreate the database with MUPIP CREATE and reload the data with MUPIP LOAD (or appropriate alternative). Use only with: -FILEHEADER -BLO[CKS_FREE]=free blocks Changes the hexidecimal free blocks field of the current file. Use this to correct a value that MUPIP INTEG reports as needing a correction, but note that the "correct" value reported by INTEG may go out-of-date with the next update. It may be necessary to calculate a delta value from the INTEG report, FREEZE the region with DSE, DUMP the current -FILEHEADER value, then apply the delta and CHANGE the -BLOCKS_FREE, and finally turn -OFF the FREEZE. Use only with: -FILEHEADER -B[YTESTREAM]=transaction_number Changes the transaction number in the file header of the last incremental backup to the value specified. Use this qualifier only in conjunction with the -FILEHEADER qualifier. For compatibility issues with priot versions, this can still be specified as -B_COMPREHENSIVE. -D[ATABASE]=transaction_number Changes the hexidecimal transaction number in the file header of the last comprehensive backup to the value specified. Use this qualifier only in conjunction with the -FILEHEADER qualifier. For compatibility issues with prior versions, this can still be specified as -B_COMPREHENSIVE. -B_R[ECORD]=transaction_number Changes the hexidecimal transaction number in the file header field that maintains this information about the last -RECORD backup. -CO[RRUPT_FILE]=boolean Indicates whether or not a region completed a successful recovery with the MUPIP JOURNAL -RECOVER command. Possible values are: T[RUE] or F[ALSE]. Changing this flag does not correct or cause database damage. When CORRUPT_FILE is set to TRUE, the DSE DUMP command displays a message like the following: %GTM-W-DBFLCORRP, /home/gtmnode1/mumps.dat Header indicates database file is corrupt **Caution** After a CHANGE -FILEHEADER -CORRUPT=TRUE, the file is unavailable to future GT.M access other than DSE. Under normal conditions, there should never be a need to change this flag manually. A MUPIP SET -PARTIAL_BYPASS_RECOV sets this flag to false. Use only with: -FILEHEADER -COM[MITWAIT_SPIN_COUNT]=value Specifies the decimal number of times a GT.M process waiting for control of a block to complete a block update should spin before yielding the CPU when GT.M runs on SMP machines. When run on a uniprocessor system, GT.M ignores this parameter. On SMP systems, when a process needs a critical section that another process has, if critical sections are short (as they are by design in GT.M), spinning a little with the expectation that the process with the critical section will release it shortly provides a way to enhance performance at the cost of increased CPU usage. Eventually, a process awaiting a critical section yields the CPU if spinning for a little does not get it the needed critical section. Note that on heavily loaded systems, increasing COMMITWAIT_SPIN_COUNT may not trade off CPU for throughput, but may instead degrade both. If you set the COMMITWAIT_SPIN_COUNT to 0, the waiting process performs a sequence of small sleeps instead of the spins or yields. The default value is 16. Use only with: -FILEHEADER -CU[RRENT_TN]=transaction_number Changes the hexidecimal current transaction number for the current region. o Raising the -CURRENT_TN can correct "block transaction number too large" errors o This qualifier has implications for MUPIP BACKUP -INCREMENTAL and -ONLINE. o Used with the -BLOCK qualifier, CURRENT_TN places a transaction number in a block header. Use only with: -FILEHEADER -DECLOCATION Specifies an offset with the file header. If -VALUE is specified (in decimal), GT.M puts it at that location. Use only with: -FILEHEADER -E[NCRYPTION_HASH] Changes the hash of the password stored in the database file header if and when you change the hash library. **Caution** An incorrect hash renders the database useless. Use only with: -FILEHEADER -[NO]EPOCHTAPER Sets a flag that indicates whether or not epoch tapering should be done. The default value is -EPOCHTAPER. -FL[USH_TIME][=delta_time] Changes the flush_time default interval (in delta_time). o The time entered must be between zero and one hour. Input is interpreted as decimal. o A -FLUSH_TIME with no value resets the -FLUSH_TIME to the default value (one second for BG and 30 seconds for MM). o The units of delta_time are hours:minutes:seconds:centi-seconds (hundredths of a second). For example, to change the flush time interval to a second, delta_time would be 00:00:01:00. To change it to 30 minutes, delta_time would be 00:30:00:00. Valid values for the qualifier are one centi-second to one hour. Use only with: -FILEHEADER -FR[EEZE]=value Sets availability of the region for update. Possible values are: T[RUE] or F[ALSE]. Use to "freeze" (disable database writes) or "unfreeze" the database. Use only with: -FILEHEADER DSE releases -FREEZE when it EXITs. To hold the database(s), CHANGE -FILEHEADER -FREEZE=TRUE and then SPAWN to perform other operations. -FU[LLY_UPGRADED]=boolean Sets a flag that indicates whether or not the database was fully upgraded from V4 to V5 database format.. The value is either T[RUE] or F[ALSE]. Use only with: -FILEHEADER -GV[STATSRESET] Resets all the database file header global access statistics to 0. Note that this erases all statistics previously accumulated in the database file header. Use only with: -FILEHEADER -HEXLOCATION Specifies a hexadecimal offset with the file header. If -VALUE is specified, GT.M puts it at that location. Use only with: -FILEHEADER -INT[ERRUPTED_RECOV]=boolean Sets a flag that indicates whether or not a recovery with the MUPIP JOURNAL -RECOVER command was interrupted. The value is either T[RUE] or F[ALSE]. Use only with: -FILEHEADER -K[EY_MAX_SIZE]=key_max_size Changes the decimal value for the maximum allowable key size. Reducing KEY_MAX_SIZE can restrict access to existing data and cause GT.M to report errors. Do not create incompatible key and record sizes. Before permanently changing the key size using DSE, use GDE to check that the appropriate Global Directory contains the same key size for the region. This prepares for future MUPIP CREATEs and performs a consistency check on the key and record size values. Use only with: -FILEHEADER -KI[LL_IN_PROG]=value Changes the value of the KILLs in progress field. The value can be "NONE" or a positive decimal integer. Use only with: -FILEHEADER -N[ULL_SUBSCRIPTS]=value Controls whether GT.M accepts null subscripts in database keys. o value can either be T[RUE], F[ALSE], ALWAYS, NEVER, or EXISTING. See GDE book for more information on these values of null_subscript. o Prohibiting null subscripts can restrict access to existing data and cause GT.M to report errors. o The default value is never. o DSE cannot change the null subscript collation order. Instead, use GDE to change the null subscript collation order, MUPIP EXTRACT the current content, MUPIP CREATE the database file(s) with the updated collation and MUPIP LOAD the content. Use only with: -FILEHEADER -OV[ERRIDE] Releases or "steals" a FREEZE owned by another process. Use only with: -FREEZE -[NO]Q[DBRUNDOWN] Sets a flag that indicates whether or not the database is enabled for quick rundown. The default value is -NOQDBRUNDOWN. -REC[ORD_MAX_SIZE]=record_max_size Changes the decimal value for the maximum allowable record size. Use the -RECORD_MAX_SIZE qualifier only in conjunction with the -FILEHEADER qualifier. Reducing RECORD_MAX_SIZE can restrict access to existing data and cause GT.M to report errors. Do not create incompatible key and record sizes. Before making a permanent change to the records size using DSE, use GDE to check that the appropriate Global Directory contains the same record size for the region. This prepares for future MUPIP CREATEs and performs a consistency check on the key and record size values. -REF[ERENCE_COUNT]=reference_count Sets a field that tracks how many processes are accessing the database with read/write permissions. MUPIP INTEG and DSE use decimal numbers for -REFERENCE_COUNT. To accurately determine the proper reference count, restrict CHANGE -FILEHEADER -REFERENCE_COUNT to the case where the process running DSE has exclusive (standalone) access to the database file. When DSE has sole access to a database file the -REFERENCE_COUNT should be one (1). This is an informational field and does not have any effect on processing. -REG[_SEQNO]=sequence-number In an LMS environment, this sets the "Region Seqno" field. -RESYNC_S[EQNO]=sequence-number In an LMS environment, this sets the hexidecimal value of the "Resync Seqno" field. -RESYNC_T[N]=sequence-number In an LMS environment, this sets the hexidecimal value ofthe "Resync transaction" field. -SPIN_SLEEP_MASK]=hexadecimal-mask Changes the hexadecimal Spin sleep time mask that controls the maximum time in nanoseconds the process sleeps on a sleep spin; zero (0), the default causes the process to just yield to the OS scheduler. Use only with: -FILEHEADER -SLEE[P_SPIN_COUNT]=integer Changes the hexadecimal Mutex Sleep Spin Count that controls the number of times a process waiting on a shared resource (usually a database) suspends its activity after exhausting its Mutex Hard Spin Count and before enquing itself to be awakened by a process releasing the resource Use only with: -FILEHEADER -[NO]STD[NULLCOL] Changes the collation of empty string ("NULL") subscripts for the database file. Although it is not the default, STDNULLCOLL is required with certain other characteristics, and highly recommended in any case. If you change this when there are existing "NULL" subscripts the results may be problematic. FIS recommends you establish this characteristic with GDE and load data with a consistent setting. Use only with: -FILEHEADER -STRM_NUM=stream-number -STRM_R[EG_SEQNO]=str_num's_region_sequence_number Changes the hexadecimal values of Stream and its Reg Seqno. Use -STRM_NUM and -STRM_REG_SEQNO together as part of the same CHANGE -FILEHEADER command. Use only with: -FILEHEADER -TI[MERS_PENDING]=timers_pending Sets a field that tracks the decimal number of processes considering a timed flush. Proper values are 0, 1, and 2. Use the CHANGE -TIMERS_PENDING qualifier only upon receiving instructions from FIS. Use only with: -FILEHEADER -TO[TAL_BLKS]=total_blocks Changes the hexidecimal total blocks field of the current file. Use only with: -FILEHEADER **Caution** The total blocks field should always reflect the actual size of the database. Change this field only if it no longer reflects the database size. -TR[IGGER_FLUSH]=trigger_flush Sets the decimal value for the triggering threshold, in buffers, for flushing the cache-modified queue. Use the CHANGE -TRIGGER_FLUSH qualifier only upon receiving instructions from FIS, and only in conjunction with the -FILEHEADER qualifier. -WR[ITES_PER_FLUSH]=writes_per_flush Set the decimal number of block to write in each flush. The default value is 7. Use only with -FILEHEADER 3 Examples Examples Example: DSE> change -block=3 -bsiz=400 This command changes the size of block 3 to 1024 bytes. Example: DSE> change -block=4 -tn=10000 This command changes sets the transaction number to 65536 (Hex: 10000) for block 4. Example: DSE> change -block=2 -record=4 -CMPC=10 -key="^CUS(""Jones,Vic"")" This command changes the compression count of the key ^CUS(Jones,Vic) to 10. It is assumed that the key CUS(Jones,Tom) already exists. The following table illustrates how GT.M calculates the value of CMPC in this case. +----------------------------------------------------------------+ | RECORD KEY | COMPRESSION COUNT | RESULTING KEY in Record | |------------------+-------------------+-------------------------| | CUS(Jones,Tom) | 0 | CUS(Jones,Tom) | |------------------+-------------------+-------------------------| | CUS(Jones,Vic) | 10 | Vic) | |------------------+-------------------+-------------------------| | CUS(Jones,Sally) | 10 | Sally) | |------------------+-------------------+-------------------------| | CUS(Smith,John) | 4 | Smith,John) | +----------------------------------------------------------------+ Example: DSE> dump -fileheader This command displays fields of the file header. Example: DSE> change -fileheader -blk_siz=2048 This command changes the block size field of the fileheader to 2048 bytes. The block field must always be a multiples of 512 bytes. Example: DSE> change -fileheader -blocks_free=5B This command changes the blocks free fields of the file header to 91 (Hex: 5B). Example: Example: DSE> change -fileheader -b_record=FF This command sets the RECORD backup transaction to FF. Example: DSE> change -fileheader corrupt_file=FALSE This command sets the CORRUPT_FILE field to false. Example: DSE> change -fileheader -current_tn=1001D1BF817 This command changes the current transaction number to 1100000000023 (Hex: 1001D1BF817). After you execute this command, subsequent transaction numbers will be greater than 1001D1BF817. Example: DSE> change -fileheader -flush_time=00:00:02:00 This command changes the flush time field of the file header to 2 seconds. Example: DSE> change -fileheader -freeze=true This command makes the default region unavailable for updates. Example: DSE> change -fileheader -key_max_size=20 This command changes the maximum key size to 20. Note that the default max key size is 64. Example: DSE> CHANGE -FILEHEADER -NULL_SUBSCRIPTS="EXISTING" This command changes the Null Subscripts field of the file header to EXISTING. Note that DSE cannot change the null subscript collation order. See GDE book for more information on changing the null subscript collation. Example: DSE> change -fileheader -record_max_size=496 This command sets the maximum record size as 496 for the default region. Example: DSE> change -fileheader -reference_count=5 This command sets the reference count field of the file header to 5. Example: DSE> change -fileheader -timers_pending=2 This command sets the timers pending field of the file header to 2. Example: DSE> change -fileheader -TOTAL_BLKS=64 This command sets the total size of the database to 100 (Hex: 64) blocks. Example: DSE> change -fileheader -trigger_flush=1000 This command sets the Flush Trigger field of the file header to 1000. Note the default value of Flush Trigger is 960. Example: DSE> change -fileheader -writes_per_flush=10 This command changes the number of writes/flush field of the file header to 10. Note that the default value for the number of writes/flush is 7. Example: DSE> change -fileheader -zqgblmod_seqno=FF This command changes the ZGBLMOD_SEQNO field to 255(Hex: FF). 2 CAche CAche Operates on the cache of a database having BG access method. The format of the CACHE command is: CA[CHE] [ -ALL -RE[COVER] -SH[OW] -VE[RIFY] ] 3 Qualifiers Qualifiers -RE[COVER] [-ALL] Resets the cache of a database having BG access method to a "clean" state. o With -ALL specified, DSE includes all region of the current global directory for cache recovery. o Attempt DSE CACHE -RECOVER only if a DSE CACHE -VERIFY commands reports the cache is "NOT clean". -SH[OW] Displays the cache data structure information. All values are in 8-byte hexadecimal form. If the database has encryption turned on, SHOW additionally displays an element that gives information about the encrypted global buffer section in shared memory. -VE[RIFY] [-ALL] Verifies the integrity of the cache data structures as well as the internal consistency of any GDS blocks in the global buffers of the current region. o With -ALL specified, DSE performs cache verification on all regions of the current global directory. o It reports the time, the region and a boolean result indicating whether the cache is clean or NOT clean. If you see "NOT clean" in report, execute DSE CACHE -RECOVER as soon as possible to reset the cache in a clean state. 3 Examples Examples Example: DSE> CACHE -VERIFY This command checks the integrity of the cache data structures as well as the internal consistency of GDS blocks in the global buffers of the current region. Example: DSE> CACHE -VERIFY -ALL Time 26-FEB-2011 14:31:30 : Region DEFAULT : Cache verification is clean Execute CACHE recover command if Cache verification is "NOT" clean. This command reports the state of database cache for all regions. Example: DSE> CACHE -RECOVER This command reinitializes the cache data structures of the current region and reverts the cache of a database having BG access to "clean" state. Example: DSE> CACHE -SHOW File /home/jdoe/node1/areg.dat Region AREG Region AREG : Shared_memory = 0x00002B6845040000 Region AREG : node_local = 0x0000000000000000 Region AREG : critical = 0x0000000000010000 Region AREG : shmpool_buffer = 0x0000000000023000 Region AREG : lock_space = 0x0000000000125000 Region AREG : cache_queues_state = 0x000000000012A000 Region AREG : cache_que_header = 0x000000000012A030 : Numelems = 0x00000407 : Elemsize = 0x00000098 Region AREG : cache_record = 0x0000000000150458 : Numelems = 0x00000400 : Elemsize = 0x00000098 Region AREG : global_buffer = 0x0000000000177000 : Numelems = 0x00000400 : Elemsize = 0x00000400 Region AREG : db_file_header = 0x0000000000277000 Region AREG : bt_que_header = 0x00000000002B7000 : Numelems = 0x00000407 : Elemsize = 0x00000040 Region AREG : th_base = 0x00000000002C71D0 Region AREG : bt_record = 0x00000000002C7200 : Numelems = 0x00000400 : Elemsize = 0x00000040 Region AREG : shared_memory_size = 0x00000000002D8000 DSE> 2 CLose CLose The CLOSE command closes the currently open output file. The format of the CLOSE command is: CL[OSE] The CLOSE command has no qualifiers. 2 CRitical CRitical Displays and/or modifies the status and contents of the critical section for the current region. The format of the CRITICAL command is: CR[ITICAL] [ -A[LL] -I[NIT] -O[WNER] -REL[EASE] -REM[OVE] -RES[ET] -S[EIZE] ] o The critical section field identifies, by its process identification number (PID), the process presently managing updates to database. o Think of a critical section as a common segment of a train track. Just as a train moves through the common segment as quickly as possible, the same way a process moves as quickly as possible through any critical section so that other processes can use it. o By default, the CRITICAL command assumes the -OWNER qualifier, which displays the status of the critical section. 3 Qualifiers Qualifiers -A[LL] Display all ids of processes owning critical section from all regions. If there are no processes owning critical section in a region, ALL displays "the CRIT is currently unowned" message for each region. -I[NIT] Reinitializes the critical section. o The -INIT and -RESET qualifiers together cause all GT.M processes actively accessing that database file to signal an error. o FIS recommends against using -INIT without the -RESET parameter when other processes are actively accessing the region because it risks damaging the database. Use only with: -RESET -O[WNER] Displays the ID of the process at the head of the critical section. DSE displays a warning message when the current process owns the critical section. Use alone Example: DSE> critical -OWNER Write critical section is currently unowned -REL[EASE] Releases the critical section if the process running DSE owns the section. Use alone. -REM[OVE] Terminates any write ownership of the critical section. Use this when the critical section is owned by a process that is nonexistent or is known to no longer be running a GT.M image. Use alone. **Caution** Using CRITICAL -REMOVE when the write owner of a critical section is an active GT.M process may cause structural database damage. -RES[ET] Displays the number of times the critical section has been through an online reinitialization. Using -RESET with -INIT causes an error for processes that are attempting to get the critical section of the region. Under the guidance of FIS, use -RESET -INIT as a way to clear certain types of hangs. Use only with: -INIT -S[EIZE] Seizes the critical section (if available). o You can also use SEIZE to temporarily suspend database updates. o Subsequently, execute CRITICAL -RELEASE command to restore normal operation. 3 Examples Examples Example: DSE> critical -OWNER Write critical section owner is process id 4220 This command displays the ID of the process holding the critical section. Note that on catching a process ID on a lightly loaded (or unloaded) system (for example, text environment) is like catching lightening in a bottle. Therefore, you can artificially hold a critical section using the DSE CRIT -SEIZE command in one session and view the owner using a different session. 2 Dump Dump Displays blocks, records, or file headers. DUMP is one of the primary DSE examination commands. The format of the DUMP command is: D[UMP] [ -A[LL] -B[LOCK]=block_number -C[OUNT]=count -F[ILEHEADER] -G[LO] -G[VSTATS] -[NO]C[RIT] -[NO]H[EADER] -O[FFSET]=offset -R[ECORD]=record-number -U[PDPROC] -Z[WR] ] Use the error messages reported by MUPIP INTEG to determine what to DUMP and examine in the database. DUMP also can transfer records to a sequential file for future study and/or for input to MUPIP LOAD (see the section on OPEN). The DUMP command requires specification of an object using either -BLOCK, -HEADER, -RECORD, or -FILEHEADER. 3 Qualifiers Qualifiers -A[LL] When used with -FILEHEADER, the -A[LL] qualifier displays additional information on the database most of which is useful for FIS in diagnosing issues. A complete description of all the elements that show up with the DSE DUMP -FILEHEADER -ALL command are beyond the scope of this book. Meaningful only with: -FILEHEADER -B[LOCK]=block-number Specifies the starting block of the dump. For commands without an object qualifier, DSE uses the last block handled by a DSE operation. When no block has been accessed, (that is, on the first block-oriented command), DSE uses block one (1). Incompatible with: -ALL, -FILEHEADER and -UPDPROC. -C[OUNT]=count Specifies the number of blocks, block headers, or records to DUMP. Incompatible with: -ALL, -FILEHEADER and -UPDPROC. -F[ILEHEADER] Dumps file header information. A DSE dump of a database file header prints a 0x prefix for all fields printed in hexadecimal format. Refer to the "Introduction" section for a description of the file header fields. Use only with -ALL or -UPDPROC -G[LO] Dumps the specified record or blocks into the current output file in Global Output (GO) format. FIS strongly suggests using -ZWR rather than -GLO as the ZWR format handles all possible content values, including some that are problematic with -GLO.[The GLO format is not supported for UTF-8 mode - use the ZWR format with UTF-8 mode. Incompatible with: -ALL, -FILEHEADER, -UPDPROC and -ZWR. -G[VSTATS] Displays the access statistics for global variables and database file(s). -NO[CRIT] Allows DSE DUMP to work even if another process is holding a critical section. Since results in this mode may be inconsistent, it should only be used if the critical section mechanism is not operating normally. -[NO]H[EADER] Specifies whether the dump of the specified blocks or records is restricted to, or excludes, headers. -HEADER displays only the header, -NOHEADER displays the block or record with the header suppressed. DUMP without the -[NO]HEADER qualifier dumps both the block/record and the header. By default, DUMP displays all information in a block or record. Incompatible with: -ALL, -FILEHEADER, -GLO, -UPDPROC and -ZWR. -O[FFSET]=offset Specifies the offset, in bytes, of the starting record for the dump. If the offset does not point to the beginning of a record, DSE rounds down to the last valid record start (e.g., DUMP -OFF=10 starts at -OFF=A if that was the beginning of the record containing offset 10). Incompatible with: -ALL, -FILEHEADER, and -RECORD. -R[ECORD]=record_number Specifies the record number of the starting record of the dump. If you try to dump a record number that is larger than the last actual record in the block, a DSE error message provides the number of the last record in the block. Incompatible with: -ALL, -FILEHEADER, and -OFFSET. -U[PDPROC] Displays the helper process parameters with the fileheader elements. Use only with -FILEHEADER. -Z[WR] Dumps the specified record or blocks into the current output file in ZWRITE (ZWR) format. Incompatible with: -ALL, -GLO, -HEADER and -FILEHEADER. 3 Examples Examples Example: DSE> DUMP -FILEHEADER This command displays an output like the following: File /home/jdoe/.fis-gtm/V7.0-002_x86_64/g/gtm.dat Region DEFAULT File /home/jdoe/.fis-gtm/V7.0-002_x86_64/g/gtm.dat Region DEFAULT Date/Time 23-MAR-2022 03:54:59 [$H = 66191,14099] Access method BG Global Buffers 1024 Reserved Bytes 0 Block size (in bytes) 4096 Maximum record size 256 Starting VBN 8193 Maximum key size 64 Total blocks 0x0000000000000065 Null subscripts NEVER Free blocks 0x0000000000000062 Standard Null Collation FALSE Free space 0x00000000 Last Record Backup 0x0000000000000001 Extension Count 100 Last Database Backup 0x0000000000000001 Number of local maps 1 Last Bytestream Backup 0x0000000000000001 Lock space 0x00000028 In critical section 0x00000000 Timers pending 0 Cache freeze id 0x00000000 Flush timer 00:00:01:00 Freeze match 0x00000000 Flush trigger 960 Freeze online FALSE Freeze online autorelease FALSE Current transaction 0x0000000000000001 No. of writes/flush 7 Maximum TN 0xFFFFFFF803FFFFFF Certified for Upgrade to V7 Maximum TN Warn 0xFFFFFFD813FFFFFF Desired DB Format V7 Master Bitmap Size 8176 Blocks to Upgrade 0x0000000000000000 Create in progress FALSE Modified cache blocks 0 Reference count 1 Wait Disk 0 Journal State [inactive] ON Journal Before imaging TRUE Journal Allocation 2048 Journal Extension 2048 Journal Buffer Size 2312 Journal Alignsize 4096 Journal AutoSwitchLimit 8386560 Journal Epoch Interval 300 Journal Yield Limit 8 Journal Sync IO FALSE Journal File: /home/jdoe/.fis-gtm/V7.0-002_x86_64/g/gtm.mjl Mutex Hard Spin Count 128 Mutex Sleep Spin Count 128 Mutex Queue Slots 1024 KILLs in progress 0 Replication State OFF Region Seqno 0x0000000000000001 Zqgblmod Seqno 0x0000000000000000 Zqgblmod Trans 0x0000000000000000 Endian Format LITTLE Commit Wait Spin Count 16 Database file encrypted FALSE Inst Freeze on Error FALSE Spanning Node Absent TRUE Maximum Key Size Assured TRUE Defer allocation TRUE Spin sleep time mask 0x00000000 Async IO OFF WIP queue cache blocks 0 DB is auto-created FALSE DB shares gvstats TRUE LOCK shares DB critical section FALSE Read Only OFF Recover interrupted FALSE Full Block Write 0 StatsDB Allocation 2050 Note that the certain fileheader elements appear depending on the current state of database. For example, if Journaling is not enabled in the database, DSE does not display Journal data element fields. Example: $ dse dump -fileheader -updproc This command displays the fileheader elements along with the following helper process parameters: Upd reserved area [% global buffers] 50 Avg blks read per 100 records 200 Pre read trigger factor [% upd rsrvd] 50 Upd writer trigger [%flshTrgr] 33 2 EValuate EValuate Translates a hexadecimal number to decimal, and vice versa. The format of the EVALUATE command is: EV[ALUATE] [ -D[ECIMAL] -H[EXADECIMAL] -N[UMBER]=number ] The -DECIMAL and -HEXADECIMAL qualifiers specify the input base for the number. The -NUMBER qualifier is mandatory. By default, EVALUATE treats the number as having a hexadecimal base. 3 Qualifiers Qualifiers -D[ECIMAL] Specifies that the input number has a decimal base. Incompatible with: -HEXADECIMAL . -H[EXADECIMAL] Specifies that the input number has a hexadecimal base. Incompatible with: -DECIMAL -N[UMBER]=number Specifies the number to evaluate. Required. 3 Examples Examples Example: DSE> evaluate -number=10 -decimal Hex: A Dec: 10 This command displays the hexadecimal equivalent of decimal number 10. Example: DSE> evaluate -number=10 -hexadecimal Hex: 10 Dec: 16 This command displays the decimal equivalent of hexadecimal 10. Example: $ dse evaluate -number=10 Hex: 10 Dec: 16 This command displays the decimal equivalent of Hexadecimal 10. Note that if you do not specify an qualifier with -NAME, then EVALUATE assumes Hexadecimal input. 2 EXit EXit The EXIT command ends a DSE session. The format of the EXIT command is: EX[IT] The EXIT command has no qualifiers. 2 Find Find Locates a given block or region. The format of the FIND command is: F[IND] [ -B[LOCK]=block-number -E[XHAUSTIVE] -F[REEBLOCK] -H[INT] -K[EY]=key -[NO]C[RIT] -R[EGION][=region] -SI[BLINGS] -ST[ATS] ] o At the beginning of a DSE session, use the FIND -REGION command to select the target region. o The FIND command, except when used with the -FREEBLOCK and -REGION qualifiers, uses the index tree to locate blocks. FIND can locate blocks only within the index tree structure. If you need to locate keys independent of their attachment to the tree, use the RANGE command. 3 Qualifiers Qualifiers -B[LOCK]=block_number Specifies the block to find. On commands without the -BLOCK= qualifier, DSE uses the last block handled by a DSE operation. When no block has been accessed, that is, on the first block-oriented command, DSE uses block one (1). Incompatible with: -KEY, -REGION -E[XHAUSTIVE] Searches the entire index structure for the desired path or siblings. o FIND -EXHAUSTIVE locates blocks that are in the tree but not indexed correctly. o FIND -EXHAUSTIVE locates all paths to a "doubly allocated" block. **Note** A doubly allocated block may cause inappropriate mingling of data. As long as no KILLs occur, double allocation may not cause permanent loss of additional data. However, it may cause the application programs to generate errors and/or inappropriate results. When a block is doubly allocated, a KILL may remove data outside its proper scope. See "Maintaining Database Integrity Chapter" for more information on repairing doubly allocated blocks. Incompatible with: -KEY, -REGION, -FREEBLOCK -F[REEBLOCK] Finds the nearest free block to the block specified by -HINT. FREEBLOCK accepts bit maps as starting or ending points. o The -FREEBLOCK qualifier is incompatible with all other qualifiers except -BLOCK and -HINT. o The -HINT qualifier is required with the -FREEBLOCK qualifier. o FIND -FREEBLOCK relies on the bitmaps to locate its target, so be sure to fix any blocks incorrectly marked "FREE" before using this command. See MAP -BUSY for more information on fixing incorrectly marked free errors. Required with -HINT; compatible with -BLOCK and [NO]CRIT. -H[INT]=block_number Designates the starting point of a -FREEBLOCK search. FIND -FREE -HINT locates the "closest" free block to the hint. This provides a tool for locating blocks to add to the B-tree, or to hold block copies created with SAVE that would otherwise be lost when DSE exits. FIND -FREE relies on the bitmaps to locate its target, so be sure to fix any blocks incorrectly marked "FREE" before using this command. Required with: -FREEBLOCK; compatible with -BLOCK and [NO]CRIT. -K[EY]=key Searches the database for the block containing the specified key or if the key does not exist, the block that would contain it, if it existed. o Enclose an M-style key in quotation marks (" "). FIND -KEY is useful in locating properly indexed keys. The -KEY qualifier is incompatible with all other qualifiers. o FIND -KEY= uses the index to locate the level zero (0) block , or data block, containing the key. If the key does not exist, it uses the index to locate the block in which it would reside. Note that FIND only works with the index as currently composed. In other words, it cannot FIND the "right" place, only the place pointed to by the index at the time the command is issued. These two locations should be, and may well be, the same; however, remind yourself to search for, understand and take into account all information describing any current database integrity issues. o DSE accepts ^#t as a valid global name when specifying a key. Compatible only with [NO]CRIT. -[NO]C[RIT] Allows FIND to work even if another process is holding a critical section. As results in this mode may be inconsistent, it should only be used if the critical section mechanism is not operating normally -R[EGION][=region] Switches to the named Global Directory region. -REGION without a specified region, or -REGION="*", displays all existing regions in the database. Use Alone. -SI[BLINGS] Displays the block number of the specified block and its logical siblings in hexadecimal format. The logical siblings are the blocks, if any, that logically exist to the right and left of the given block in the database tree structure. Incompatible with: -FREEBLOCK, -HINT, -KEY, -REGION -ST[ATS] Switches to the named Global Directory shadow for the region's shared gvstats. Compatible only with R[EGION]. 3 Examples Examples Example: DSE> find -exhaustive -block=180 Directory path Path--blk:off 1:10 2:1E Global paths Path--blk:off 6:51 1A4:249 180 This command locates block 180 by looking through the B-tree index for any pointer to the block. This command finds even those blocks that are connected to the tree but the first key in the block does not match the index path. Example: DSE> find -free -hint=180 Next free block is D8F. This command locates the "closest" free block to block 180. You can use this command as a tool for locating blocks to add to the B-tree, or to hold block copies created with SAVE that would otherwise be lost when DSE exits. Example: DSE>find -key="^biggbl(1)" This command locates the key ^biggbl(1) in the database. Example: DSE> find -freeblock -hint=232 This commands starts to search for free block after block 232. Example: DSE> FIND -FREEBLOCK -HINT=232 -NOCRIT This command searches for freeblocks after block 232 even if another process is holding a critical section. Example: DSE> find -sibling -block=10 This command operates like FIND -BLOCK; however it reports the numbers of the blocks that logically fall before and after block 180 on the same level. This command produces an output like the following: Left sibling Current block Right sibling 0x0000000F 0x00000010 0x00000011 2 Help Help The HELP command explains DSE commands. The format of the HELP command is: -H[ELP] [help topic] 2 Integrit Integrit Checks the internal consistency of a single non-bitmap block. INTEGRIT reports errors in hexadecimal notation. The format of the INTEGRIT command is: I[NTEGRIT] -B[LOCK]=block-number **Note** Unlike MUPIP INTEG, this command only detects errors internal to a block and cannot detect errors such as indices incorrectly pointing to another block. 3 Qualifiers Qualifiers -B[LOCK]=block_number Specifies the block for DSE to check. On commands with no -BLOCK qualifier, DSE uses the last block handled by a DSE operation. When no block has been accessed, that is, on the first block-oriented command, DSE uses block one (1). -NO[CRIT] Allows DSE INTEG to work even if another process is holding a critical section. Since results in this mode may be inconsistent, it should only be used if the critical section mechanism is not operating normally. 2 Maps Maps Examines or updates bitmaps. The format of the MAPS command is: M[APS] [ -BL[OCK]=block-number -BU[SY] -F[REE] -M[ASTER] -R[ESTORE_ALL] ] MAPS can flag blocks as being either -BUSY or -FREE. The -MASTER qualifier reflects the current status of a local bitmap back into the master map. The -RESTORE_ALL qualifier rebuilds all maps and should be used with caution since it can destroy important information. By default, MAPS shows the status of the bitmap for the specified block. 3 Qualifiers_for_MAP Qualifiers for MAP -BL[OCK]=block_number Specifies the target block for MAPS. The -BLOCK qualifier is incompatible with the -RESTORE_ALL qualifier. On commands with no -BLOCK= or -RESTORE_ALL qualifier, DSE uses the last block handled by a DSE operation. When no block has been accessed, that is, on the first block-oriented command, DSE uses block one (1). Incompatible with: -RESTORE_ALL -BU[SY] Marks the current block as busy in the block's local map and appropriately updates the master bitmap. BUSY accepts bit map blocks. Compatible only with: -BLOCK -F[REE] Marks the current block as free in the block's local map and appropriately updates the master bitmap. Compatible only with: -BLOCK -M[ASTER] Sets the bit in the master bitmap associated with the current block's local map according to whether or not that local map is full. MASTER accepts bit map blocks. Use only with: -BLOCK. -R[ESTORE_ALL] Sets all local bitmaps and the master bitmap to reflect the blocks used in the database file. Use -RESTORE_ALL only if the database contents are known to be correct, but a large number of the bitmaps require correction. **Caution** The -RESTORE_ALL qualifier rebuilds all maps and should be used with a great deal of caution as it can destroy important information. Use alone. 3 Examples_for_MAPS Examples for MAPS Example: DSE> MAPS -BLOCK=20 -FREE This command flags block 20 as free. A sample DSE DUMP output block 0 is as follows: Block 0 Size 90 Level -1 TN 10B76A V5 Master Status: Free Space Low order High order Block 0: | XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | Block 20: | :XXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | Block 40: | XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | Block 60: | XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | Block 80: | XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | Block A0: | XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | Block C0: | XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | Block E0: | XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | Block 100: | XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | Block 120: | XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | Block 140: | XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | Block 160: | XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | Block 180: | XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | Block 1A0: | XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | Block 1C0: | XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | Block 1E0: | XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX | 'X' == BUSY '.' == FREE ':' == REUSABLE '?' == CORRUPT Note that BLOCK 20 is marked as REUSABLE, which means FREE but in need of a before-image journal record. Example: DSE> maps -block=20 -busy This command marks block 20 as busy. A sample DSE DUMP output of block 0 is as follows: Block 0 Size 90 Level -1 TN 1 V5 Master Status: Free Space Low order High order Block 0: | XXX..... ........ ........ ........ | Block 20: | X....... ........ ........ ........ | Block 40: | ........ ........ ........ ........ | Block 60: | ........ ........ ........ ........ | Block 80: | ........ ........ ........ ........ | Block A0: | ........ ........ ........ ........ | Block C0: | ........ ........ ........ ........ | Block E0: | ........ ........ ........ ........ | Block 100: | ........ ........ ........ ........ | Block 120: | ........ ........ ........ ........ | Block 140: | ........ ........ ........ ........ | Block 160: | ........ ........ ........ ........ | Block 180: | ........ ........ ........ ........ | Block 1A0: | ........ ........ ........ ........ | Block 1C0: | ........ ........ ........ ........ | Block 1E0: | ........ ........ ........ ........ | 'X' == BUSY '.' == FREE ':' == REUSABLE '?' == CORRUPT Note that the BLOCK 20 is marked as BUSY. 2 OPen OPen Use the OPEN command to open a file for sequential output of global variable data. The format of the OPEN command is: OP[EN] F[ILE]=file o OPEN a file to which you want to "dump" information. o If an OPEN command does not have a -FILE qualifier, DSE reports the name of the current output file. 3 Qualifiers_for_OPEN Qualifiers for OPEN -F[ILE]=file-name Specifies the file to open. 3 Examples Examples Example: DSE> OPEN Current output file: var.out This command displays the current output file. In this case, the output file is var.out. Example: DSE> OPEN -FILE=var1.out The command OPEN -FILE=var1.out sets the output file to var1.out. 2 OVerwrite OVerwrite Overwrites the specified string on the given offset in the current block. Use extreme caution when using this command. The format of the OVERWRITE command is: OV[ERWRITE] [ -D[ATA]=string -O[FFSET]=offset ] 3 Qualifiers_for_OVERWRITE Qualifiers for OVERWRITE -B[LOCK]=block number Directs DSE to OVERWRITE a specific block. If no block number is specified, the default is the current block. -D[ATA]=string Specifies the data to be written. Use quotation marks around the string and escape codes of the form \a or \ab, where "a" and "b" are hexadecimal digits representing non-printing characters. \\ translates to a single backslash. -O[FFSET]=offset Specifies the offset in the current block where the overwrite should begin. 3 Examples Examples Example: DSE>overwrite -block=31 -data="Malvern" -offset=CA This command overwrites the data at the specified location. 2 Page Page Sends one form feed to the output device. Use PAGE to add form feeds to a dump file, making the hard copy file easier to read. If you plan to use the dump file with MUPIP LOAD, do not use PAGE. The format of the PAGE command is: P[AGE] The PAGE command has no qualifiers. 2 RAnge RAnge The RANGE command finds all blocks in the database whose first key falls in the specified range of keys. The RANGE command may take a very long time unless the range specified by -FROM and -TO is small. Use FIND -KEY and/or FIND -KEY -EXHAUSTIVE first to quickly determine whether the key appears in the index tree. The format of the RANGE command is: RA[NGE] [ -F[ROM]=block-number -T[O]=block-number -I[NDEX] -LOS[T] -[NO]C[RIT] -[NO]BU[SY] -S[TAR] -LOW[ER]=key -U[PPER]=key ] 3 Qualifiers Qualifiers -F[ROM]=block_number Specifies a starting block number for the range search. DSE RANGE accept bit maps as starting or ending points. By default, RANGE starts processing at the beginning of the file. -T[O]=block-number Specifies an ending block number for the range search. DSE RANGE accept bit maps as starting or ending points. By default, RANGE stops processing at the end of the file. -I[NDEX] Restricts a search to index blocks. -LOS[T]=block_number Restricts a search to blocks not found by a FIND -BLOCK. -LOW[ER]=key Specifies the lower bound for the key range. -[NO]BU[SY]=busy/free Restricts a search to either BUSY or FREE blocks. -[NO]C[RIT] Allows DSE RANGE to work even if another process is holding a critical section. Since results in this mode may be inconsistent, it should only be used if the critical section mechanism is not operating normally. -S[TAR] Includes index blocks that contain a single star key. -U[PPER]=key Specifies the upper bound for the key range. 3 Examples Examples Example: DSE> range -lower="^abcdefgh" -upper="^abcdefghi" -from=A -to=CC This command searches for a specified keys between block 10 and block 204. Note that the range (between FROM and TO) of blocks must be valid blocks specified in hexadecimal. Example: DSE> range -lower="^abcdefgh" -upper="^abcdefghi" -from=A -to=CC -noindex This command searches only data blocks for the specified keys between block 10 and block 204. Example: DSE> range -lower="^abcdefgh" -upper="^abcdefghi" -from=A -to=CC -index This command searches only index blocks for the specified keys between block 10 and block 204. Example: DSE> range -lower="^abcdefgh" -upper="^abcdefghi" -lost This command includes lost blocks while searching for the specified keys and reports only blocks which are not currently indexed. Example: DSE> range -lower="^Fruits(15)" -upper="^Fruits(877)" -from=A -to=F Blocks in the specified key range: Block: 0000000A Level: 0 Block: 0000000B Level: 0 Block: 0000000C Level: 0 Block: 0000000D Level: 0 Block: 0000000E Level: 0 Block: 0000000F Level: 0 Found 6 blocks This command search for keys between ^Fruits(15) and ^Fruits(877). 2 REMove REMove Removes one or more records or a save buffer. The format of the REMOVE command is: REM[OVE] [ -B[LOCK]=block-number -C[OUNT]=count -O[FFSET]=offset -R[ECORD]=record-number -V[ERSION]=version-number ] The version number is specified in decimal. 3 Qualifiers Qualifiers -B[LOCK]=block_number Specifies the block associated with the record or buffer being deleted. On commands with no -BLOCK= qualifier, DSE uses the last block handled by a DSE operation. When no block has been accessed, that is, on the first block-oriented command, DSE uses block one (1). BLOCK accepts blocks higher than the current database size because they deal with set of saved block copies rather than the database and there are situations where a saved block may be outside the current database size (for example, due to a concurrent MUPIP REORG -TRUNCATE). -C[OUNT]=count Specifies the number of records to remove. By default, REMOVE deletes a single record. Incompatible with: -VERSION -O[FFSET]=offset Specifies the offset (in bytes) of the record to be removed. If the offset does not point to the beginning of a record, DSE rounds down to the beginning of the record containing the offset (for example, REMOVE -OFF=10 starts at OFF=A if that was the last prior record boundry). Incompatible with: -VERSION, -RECORD -R[ECORD]=record_number Specifies the number that identifies the record to remove. The -RECORD qualifier is incompatible with the -OFFSET and -VERSION qualifiers. Incompatible with: -VERSION, -OFFSET -V[ERSION]=version_number Specifies the version number, in decimal, of the save buffer to remove. If there are more than one version of a block, -VERSION is required; otherwise REMOVE works on that sole version. -VERSION is incompatible with all qualifiers except -BLOCK. If there is only one version of the specified -BLOCK= block in the current region, DSE REMOVE defaults to that version. Use only with: -BLOCK; decimal 2 REStore REStore The RESTORE command restores saved versions of blocks. RES[TORE] [ -B[LOCK]=block-number -F[ROM]=from -R[EGION]=region -V[ERSION]=version-number ] The version number is specified in decimal. 3 Qualifiers Qualifiers -B[LOCK]=block_number Specifies the block to restore. For commands with no -BLOCK= qualifier, DSE uses the last block handled by a DSE operation. When no block has been accessed, (i.e., on the first block-oriented command), DSE uses block one (1). BLOCK accepts blocks higher than the current database size because it deal with set of saved block copies rather than the database and there are situations where a saved block may be outside the current database size (for example, due to a concurrent MUPIP REORG -TRUNCATE). -F[ROM]=block_number Specifies the block number of the SAVE buffer to restore. DSE restores the block specified with -BLOCK qualifier with the block specified by the -FROM qualifier. If there is only one version of the specified -FROM= block, DSE RESTORE defaults to that version and it always restores the original block transaction number. By default, RESTORE uses the target block number as the -FROM block number. -R[EGION]=region Specifies the region of the saved buffer to restore. By default, RESTORE uses SAVE buffers from the current region. -V[ERSION]=version_number Specifies the decimal version number of the block to restore. The version number is required. 2 SAve SAve The SAVE command preserves versions of blocks, or displays a listing of saved versions for the current DSE session. SAVE can preserve 128 versions. Saved information is lost when DSE EXITs. Use with the RESTORE command to move SAVEd blocks to a permanent location, and as a safety feature use SAVE to retain copies of database blocks before changing them. The format of the SAVE command is: SA[VE] [ -B[LOCK]=block-number -C[OMMENT]=string -L[IST] -[NO]C[RIT] ] 3 Qualifiers Qualifiers -B[LOCK]=block_number Specifies the block to restore. On commands with no -BLOCK= qualifier, DSE uses the last block handled by a DSE operation. When no block has been accessed, that is, on the first block-oriented command, DSE uses block one (1). -C[OMMENT]=string Specifies a comment to save with the block. Enclose the comment in quotation marks (" "). Incompatible with: -LIST -L[IST] Lists saved versions of specified blocks. The -LIST qualifier is incompatible with the -COMMENT qualifier. By default, SAVE -LIST provides a directory of all SAVEd blocks. LIST may display blocks higher than the current database size because it deals with set of saved block copies rather than the database and there are situations where a saved block may be outside the current database size (for example, due to a concurrent MUPIP REORG -TRUNCATE); Incompatible with: -COMMENT -[NO]C[RIT] Allows DSE SAVE to work even if another process is holding a critical section. Since results in this mode may be inconsistent, it should only be used if the critical section mechanism is not operating normally. 2 SHift SHift Use the SHIFT command to shift data in a block, filling the block with zeros, or shortening the block. The format of the SHIFT command is: SH[IFT] [ -B[ACKWARD]=b_shift -BL[OCK]=block_number -F[ORWARD]=f_shift -O[FFSET]=offset ] b_shift must always be less than or equal to offset. This means that DSE SHIFT in the backward direction is restricted to the maximum of OFFSET number of bytes. This ensures that the shift does not cross block boundaries, either intentionally or unintentionally. 3 Qualifiers Qualifiers -B[ACKWARD]=shift Specifies the number of bytes to shift data in the direction of the block header. Incompatible with: -FORWARD -BL[OCK]=block_number Specifies the block number to perform the DSE SHIFT. -F[ORWARD]=shift Specifies the number of bytes to shift data toward the end of the block. Incompatible with: -BACKWARD -O[FFSET]=offset Specifies the starting offset, in bytes, of the portion of the block to shift. 2 SPawn SPawn Use the SPAWN command to fork a child process for access to the shell without terminating the current DSE environment. The format of the SPAWN command is: SP[AWN] [shell-command] o The SPAWN command accepts an optional command string for execution by the spawned sub-process. If the SPAWN has no command string parameter, the created sub-process issues a shell prompt and accepts any legal shell command. To terminate the sub-process, use the shell logout command. o The SPAWN command has no qualifiers. o DSE SPAWN works with an argument. If the argument contains spaces, enclose it with quotes. The SPAWN command has no qualifiers. DSE SPAWN works with an argument. If the argument contains spaces, enclose it with quotes. 3 Examples Examples Example: DSE> SPAWN "mumps -run ^GDE" This command suspends a DSE session and executes the shell command mumps -run ^GDE. 2 Wcinit Wcinit Use the WCINIT command to reinitialize the global buffers of the current region. Because it cleans out the cache, the WCINIT command should not be used except under the guidance of FIS. **Caution** A WCINIT command issued while normal database operations are in progress can cause catastrophic damage to the database. The format of the WCINIT command is: W[CINIT] o The WCINIT command has no qualifiers. o When you issue the WCINIT command, DSE issues the CONFIRMATION: prompt. You must verify the WCINIT command by responding with "YES." If you do not confirm the WCINIT, DSE issues the message: No action taken, enter yes at the CONFIRMATION prompt to initialize global buffers. o WCINIT operations are more safely performed by MUPIP RUNDOWN. Use this command only under instructions from FIS. 1 Summary Summary +------------------------------------------------------------------------+ | COMMAND | QUALIFIERS | COMMENTS | |-------------+----------------------------------+-----------------------| | AD[D] | -B[LOCK]=block number | - | |-------------+----------------------------------+-----------------------| | - | -D[ATA]=string | Incompatible with | | | | -POINTER, -STAR | |-------------+----------------------------------+-----------------------| | - | -K[EY]=key | Incompatible with | | | | -STAR | |-------------+----------------------------------+-----------------------| | - | -O[FFSET]=offset | Incompatible with | | | | -RECORD, -STAR | |-------------+----------------------------------+-----------------------| | - | -P[OINTER]=pointer | Incompatible with | | | | -DATA | |-------------+----------------------------------+-----------------------| | - | -R[ECORD]=record-number | Incompatible with | | | | -OFFSET, -STAR | |-------------+----------------------------------+-----------------------| | | | Incompatible with | | - | -S[TAR] | -DATA,-KEY, -OFFSET, | | | | -RECORD | |-------------+----------------------------------+-----------------------| | AL[L] | -A[LL] | Meaningful only with | | | | -DUMP | |-------------+----------------------------------+-----------------------| | - | -B[UFFER_FLUSH] | Incompatible with | | | | -RENEW | |-------------+----------------------------------+-----------------------| | | | Incompatible with | | - | -C[RITINIT] | -RENEW, -RELEASE, | | | | -SEIZE | |-------------+----------------------------------+-----------------------| | - | -D[UMP] | Use with: -ALL | |-------------+----------------------------------+-----------------------| | - | -[NO]F[REEZE] | Incompatible with | | | | -RENEW | |-------------+----------------------------------+-----------------------| | - | -O[VERRIDE] | Meaningful only with | | | | -[NO]FREEZE | |-------------+----------------------------------+-----------------------| | - | -REF[ERENCE] | Incompatible with | | | | -RENEW | |-------------+----------------------------------+-----------------------| | | | Incompatible with | | - | -REL[EASE] | -CRITINIT, | | | | -RENEW,-SEIZE | |-------------+----------------------------------+-----------------------| | - | -REN[EW] | Use alone | |-------------+----------------------------------+-----------------------| | | | Incompatible with | | - | -S[EIZE] | -RENEW, -RELEASE, | | | | -CRITINIT | |-------------+----------------------------------+-----------------------| | - | -W[CINIT] | Incompatible with | | | | -RENEW | |-------------+----------------------------------+-----------------------| | CA[CHE] | -ALL | Used with -RECOVER, | | | | -SHOW, and -VERIFY | |-------------+----------------------------------+-----------------------| | - | -RE[COVER] | Use only with -ALL. | |-------------+----------------------------------+-----------------------| | - | -SH[OW] | Use only with -ALL. | |-------------+----------------------------------+-----------------------| | - | -VE[RIFY] | Use only with -ALL. | |-------------+----------------------------------+-----------------------| | | | Incompatible with | | CH[ANGE] | -BL[OCK]=block number | -FILEHEADER and | | | | qualifiers used with | | | | -FILEHEADER | |-------------+----------------------------------+-----------------------| | - | -BS[IZ]=block-size | Use only with -BLOCK, | | | | -LEVEL, -TN | |-------------+----------------------------------+-----------------------| | - | -L[EVEL]=level | Use only with -BLOCK, | | | | -BSIZ, -TN | |-------------+----------------------------------+-----------------------| | - | -TN [=transaction number] | Use only with -BLOCK, | | | | -BSIZ, -LEVEL | |-------------+----------------------------------+-----------------------| | - | -OF[FSET]=offset | Use only with -BLOCK, | | | | -CMPC, -RSIZ | |-------------+----------------------------------+-----------------------| | - | -RE[CORD]=record number | Use only with -BLOCK, | | | | -CMPC, -RSIZ | |-------------+----------------------------------+-----------------------| | | | Use only with -BLOCK, | | - | -CM[PC]= compression count | -RECORD, -OFFSET, | | | | -RSIZ | |-------------+----------------------------------+-----------------------| | | | Use only with -CMPC | | - | -RS[IZ]=record size | -OFFSET, -RECORD, | | | | -BLOCK | |-------------+----------------------------------+-----------------------| | | | Incompatible with | | - | -F[ILEHEADER] | -BSIZ, -CMPC, -TN, | | | | -LEVEL, -OFFSET, | | | | -RECORD, -RSIZ | |-------------+----------------------------------+-----------------------| | - | AVG_BLKS_READ=Average blocks | - | | | read | | |-------------+----------------------------------+-----------------------| | - | B_B[YTESTREAM]=transaction | - | | | number | | |-------------+----------------------------------+-----------------------| | - | -B_C[OMPREHENSIVE]=transaction | Use only with | | | number | -FILEHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | B_D[ATABASE] = transaction | Use only with | | | number | -FILEHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | -B_I[NCREMENTAL] = transaction | Use only with | | | number | -FILEHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | -BLK[_SIZE]=block size | Use only with | | | | -FILEHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | -BLO[CKS_FREE]=free blocks | Use only with | | | | -FILEHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | -B_R[ECORD]=transaction number | Use only with | | | | -FILEHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | -CO[RRUPT_FILE]=value | Use only with | | | | -FILEHEADER | |-------------+----------------------------------+-----------------------| | - | -CU[RRENT_TN]=transaction number | Use only with | | | | -FILEHEADER | |-------------+----------------------------------+-----------------------| | - | DECL[OCATION]=value | Use only with | | | | -FILHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | DEF[_COLLATION]=value | Use only with | | | | -FILEHEADER; | |-------------+----------------------------------+-----------------------| | - | -ENCRYPTION_HASH | Use only with | | | | -FILEHEADER | |-------------+----------------------------------+-----------------------| | - | -FL[USH_TIME][=delta time] | Use only with | | | | -FILEHEADER | |-------------+----------------------------------+-----------------------| | - | -FR[EEZE]=value | Use only with | | | | -FILEHEADER | |-------------+----------------------------------+-----------------------| | - | -FU[LLY_UPGRADED]=boolean | Use only with | | | | -FILEHEADER | |-------------+----------------------------------+-----------------------| | - | -GV[STATSRESET] | Use only with | | | | -FILEHEADER | |-------------+----------------------------------+-----------------------| | - | -HARD_SPIN_COUNT=Mutex hard spin | Use only with | | | count | -FILEHEADER | |-------------+----------------------------------+-----------------------| | | -HEXL[OCATION]=value | Use only with | | | | -FILEHEADER;hexa | |-------------+----------------------------------+-----------------------| | - | -INT[ERRUPTED_RECOV]=boolean | | |-------------+----------------------------------+-----------------------| | - | -JNL_YIELD_LIMIT=journal yeild | | | | limit | | |-------------+----------------------------------+-----------------------| | - | -K[EY_MAX_SIZE]=key_max_size | Use only with | | | | -FILEHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | -M[ACHINE_NAM]=value | | |-------------+----------------------------------+-----------------------| | - | -N[ULL_SUBSCRIPTS]=value | Use only with | | | | -FILEHEADER | |-------------+----------------------------------+-----------------------| | - | -NO[CRIT] | | |-------------+----------------------------------+-----------------------| | - | -OV[ERRIDE] | | |-------------+----------------------------------+-----------------------| | - | -RC_SRV_COUNT | | |-------------+----------------------------------+-----------------------| | - | -RE_READ_TRIGGER=read trigger | | |-------------+----------------------------------+-----------------------| | - | -Q[UANTUM_INTERVAL] [=delta | Use only with | | | time] | -FILEHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | -REC[ORD_MAX_SIZE]=maximum | Use only with | | | record size | -FILEHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | -REF[ERENCE_COUNT]=reference | Use only with | | | count | -FILEHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | -REG[_SEQNO]=sequence number | Use only with | | | | -FILEHEADER; hexa | |-------------+----------------------------------+-----------------------| | - | -RESERVED_BYTES=reserved bytes | Use only with | | | | -FILEHEADER;decimal | |-------------+----------------------------------+-----------------------| | - | -[NO] RES[PONSE_INTERVAL] | Use only with | | | [=delta time] | -FILEHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | -SLEEP_SPIN_COUNT=mutex sleep | Use only with | | | spin count | -FILEHEADER; | |-------------+----------------------------------+-----------------------| | - | -SPIN_SLEEP_TIME=mutex sleep | | | | time | | |-------------+----------------------------------+-----------------------| | - | -[NO]S[TALENESS_TIMER] [=delta | Use only with | | | time] | -FILEHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | -TIC[K_INTERVAL] [=delta time] | Use only with | | | | -FILEHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | -TIM[ERS_PENDING]=timers pending | Use only with | | | | -FILEHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | -TO[TAL_BLKS]=total_blocks | Use only with | | | | -FILEHEADER | |-------------+----------------------------------+-----------------------| | - | -TR[IGGER_FLUSH]=trigger flush | Use only with | | | | -FILEHEADER | |-------------+----------------------------------+-----------------------| | - | -W[RITES_PER_FLUSH]=writes per | Use only with | | | flush | -FILEHEADER; decimal | |-------------+----------------------------------+-----------------------| | - | -WAIT_DISK=wait disk | - | |-------------+----------------------------------+-----------------------| | - | -Zqgblmod_S[EQNO] = sequence | Use only with | | | number | -FILEHEADER;hexa | |-------------+----------------------------------+-----------------------| | - | -Zqgblmod_TN=sequence_number | Use only with | | | | -FILEHEADER;hexa | |-------------+----------------------------------+-----------------------| | CL[OSE] | - | - | |-------------+----------------------------------+-----------------------| | CR[ITICAL] | -I[NIT] | Use only with -RESET | |-------------+----------------------------------+-----------------------| | - | -O[WNER] | Use alone | |-------------+----------------------------------+-----------------------| | - | -REL[EASE] | Use alone | |-------------+----------------------------------+-----------------------| | - | -REM[OVE] | Use alone | |-------------+----------------------------------+-----------------------| | - | -RES[ET] | Use only with -INIT | |-------------+----------------------------------+-----------------------| | - | -S[EIZE] | Use alone | |-------------+----------------------------------+-----------------------| | D[UMP] | -B[LOCK]=block_number | Incompatible with | | | | -FILEHEADER | |-------------+----------------------------------+-----------------------| | - | -C[OUNT]=count | Incompatible with | | | | -FILEHEADER | |-------------+----------------------------------+-----------------------| | - | -F[ILEHEADER] | Use alone | |-------------+----------------------------------+-----------------------| | - | -G[LO] | Incompatible with | | | | -FILEHEADER, -HEADER | |-------------+----------------------------------+-----------------------| | - | -G[VSTATS] | Use only with | | | | -FILEHEADER | |-------------+----------------------------------+-----------------------| | - | -[NO]H[EADER] | Incompatible with | | | | -FILEHEADER, -GLO | |-------------+----------------------------------+-----------------------| | - | -O[FFSET]=offset | Incompatible with | | | | -FILEHEADER, -RECORD | |-------------+----------------------------------+-----------------------| | - | -R[ECORD]=record_number | Incompatible with | | | | -FILEHEADER, -OFFSET | |-------------+----------------------------------+-----------------------| | EV[ALUATE] | -D[ECIMAL] | Incompatible with | | | | -HEXADECIMAL | |-------------+----------------------------------+-----------------------| | - | -H[EXADECIMAL] | Incompatible with | | | | -DECIMAL | |-------------+----------------------------------+-----------------------| | - | -N[UMBER]=number | Required | |-------------+----------------------------------+-----------------------| | EX[IT] | | - | |-------------+----------------------------------+-----------------------| | F[IND] | -B[LOCK]=block_number | Incompatible with | | | | -KEY, -REGION | |-------------+----------------------------------+-----------------------| | | | Incompatible with | | - | -E[XHAUSTIVE] | -KEY, -REGION, | | | | -FREEBLOCK | |-------------+----------------------------------+-----------------------| | | | Required with -HINT; | | - | -F[REEBLOCK] | compatible with | | | | -BLOCK | |-------------+----------------------------------+-----------------------| | - | -H[INT]=block_number | Required with | | | | -FREEBLOCK | |-------------+----------------------------------+-----------------------| | - | -K[EY]=key | Use alone | |-------------+----------------------------------+-----------------------| | - | -R[EGION][=region] | Use alone | |-------------+----------------------------------+-----------------------| | | | Incompatible with | | - | -SI[BLINGS] | -FREEBLOCK, -HINT, | | | | -KEY, -REGION | |-------------+----------------------------------+-----------------------| | H[ELP] | [help topic] | - | |-------------+----------------------------------+-----------------------| | I[NTEGRIT] | -B[LOCK]=block_number | - | |-------------+----------------------------------+-----------------------| | M[APS] | -BL[OCK]=block_number | Incompatible with | | | | -RESTORE_ALL | |-------------+----------------------------------+-----------------------| | - | -BU[SY] | Compatible only with | | | | -BLOCK | |-------------+----------------------------------+-----------------------| | - | -F[REE] | - | |-------------+----------------------------------+-----------------------| | - | -M[ASTER] | - | |-------------+----------------------------------+-----------------------| | - | -R[ESTORE_ALL] | Use alone | |-------------+----------------------------------+-----------------------| | OP[EN] | -F[ILE]=file | - | |-------------+----------------------------------+-----------------------| | | -B[LOCK]=block_number | | | OV[ERWRITE] | | - | | | -D[ATA]=string | | |-------------+----------------------------------+-----------------------| | - | -O[FFSET]=offset | - | |-------------+----------------------------------+-----------------------| | P[AGE] | - | - | |-------------+----------------------------------+-----------------------| | RA[NGE] | -F[ROM]=block_number | - | |-------------+----------------------------------+-----------------------| | - | -T[O]=block_number | - | |-------------+----------------------------------+-----------------------| | | -I[NDEX]=block_number | | | | | | | | -L[OST]=block_number | | | | | | | - | -[NOT]BUSY=busy/free | - | | | | | | | -S[TAR]=block_number | | | | | | | | -L[OWER]=key | | |-------------+----------------------------------+-----------------------| | - | -U[PPER]=key | - | |-------------+----------------------------------+-----------------------| | REM[OVE] | -B[LOCK]=block-number | - | |-------------+----------------------------------+-----------------------| | - | -C[OUNT]=count | Incompatible with | | | | -VERSION | |-------------+----------------------------------+-----------------------| | - | -O[FFSET]=offset | Incompatible with | | | | -VERSION, -RECORD | |-------------+----------------------------------+-----------------------| | - | -R[ECORD]=record-number | Incompatible with | | | | -VERSION, -OFFSET | |-------------+----------------------------------+-----------------------| | - | -V[ERSION]=version-number | Use only with -BLOCK; | | | | decimal | |-------------+----------------------------------+-----------------------| | RES[TORE] | -B[LOCK]=block-number | - | |-------------+----------------------------------+-----------------------| | - | -F[ROM]=block-number | - | |-------------+----------------------------------+-----------------------| | - | -R[EGION]=region | - | |-------------+----------------------------------+-----------------------| | - | -V[ERSION]=version-number | Required; decimal | |-------------+----------------------------------+-----------------------| | SA[VE] | -B[LOCK]=block-number | - | |-------------+----------------------------------+-----------------------| | - | -C[OMMENT]=string | Incompatible with | | | | -LIST | |-------------+----------------------------------+-----------------------| | - | -L[IST] | Incompatible with | | | | -COMMENT | |-------------+----------------------------------+-----------------------| | SH[IFT] | -B[ACKWARD]=shift | Incompatible with | | | | -FORWARD | |-------------+----------------------------------+-----------------------| | - | -F[ORWARD]=shift | Incompatible with | | | | -BACKWARD | |-------------+----------------------------------+-----------------------| | - | -O[FFSET]=offset | - | |-------------+----------------------------------+-----------------------| | SP[AWN] | [CLI command] | - | |-------------+----------------------------------+-----------------------| | W[CINIT] | - | - | +------------------------------------------------------------------------+ * Use these qualifiers only with instructions from FIS. 1 Copyright Copyright Copyright 2022 Fidelity National Information Services, Inc. and/or its subsidiaries. All rights reserved. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts and no Back-Cover Texts. GT.M(TM) is a trademark of Fidelity National Information Services, Inc. Other trademarks are the property of their respective owners. This document contains a description of GT.M and the operating instructions pertaining to the various functions that comprise the system. This document does not contain any commitment of FIS. FIS believes the information in this publication is accurate as of its publication date; such information is subject to change without notice. FIS is not responsible for any errors or defects. fis-gtm-V7.0-005/sr_port/dse_adrec.c0000644000032200000250000002072414342376331016125 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gdsdbver.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "gdscc.h" #include "cli.h" #include "copy.h" #include "filestruct.h" #include "jnl.h" #include "skan_offset.h" #include "skan_rnum.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" #include "t_write.h" #include "t_end.h" #include "t_begin_crit.h" #include "gvcst_blk_build.h" #include "util.h" #include "t_abort.h" #include "gtmmsg.h" GBLREF char patch_comp_key[MAX_KEY_SZ + 1], *update_array, *update_array_ptr; GBLREF cw_set_element cw_set[]; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF srch_hist dummy_hist; GBLREF uint4 update_array_size; GBLREF unsigned short patch_comp_count; error_def(ERR_AIMGBLKFAIL); error_def(ERR_CPBEYALLOC); error_def(ERR_DBRDONLY); error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEFAIL); error_def(ERR_DSEINVALBLKID); error_def(ERR_GVIS); error_def(ERR_REC2BIG); void dse_adrec(void) { blk_segment *bs1, *bs_ptr; block_id blk, blk_ptr; block_id_32 temp; boolean_t long_blk_id; char data[MAX_LINE], key[MAX_KEY_SZ + 1]; int data_len, key_len; int4 blk_seg_cnt, blk_size; long blk_id_size; sgmnt_addrs *csa; short int new_len, rsize, size; sm_uc_ptr_t b_top, key_top, lbp, new_bp, rp, r_top; srch_blk_status blkhist; unsigned short cc; csa = cs_addrs; if (gv_cur_region->read_only) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ if (BADDSEBLK == (blk = dse_getblk("BLOCK", DSENOBML, DSEBLKCUR))) /* WARNING: assignment */ return; if (CLI_PRESENT != cli_present("KEY")) { util_out_print("Error: key must be specified.", TRUE); return; } if (!dse_getki(&key[0], &key_len, LIT_AND_LEN("KEY"))) return; t_begin_crit(ERR_DSEFAIL); blk_size = csa->hdr->blk_size; blkhist.blk_num = blk; if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(1) ERR_DSEBLKRDFAIL); lbp = (uchar_ptr_t)malloc(blk_size); memcpy(lbp, blkhist.buffaddr, blk_size); if (((blk_hdr_ptr_t)lbp)->bver > BLK_ID_32_VER) /* Check blk version to see if using 32 or 64 bit block_id */ { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else t_abort(gv_cur_region, csa); free(lbp); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } if (((blk_hdr_ptr_t)lbp)->bsiz > blk_size) ((blk_hdr_ptr_t)lbp)->bsiz = blk_size; else if (((blk_hdr_ptr_t)lbp)->bsiz < SIZEOF(blk_hdr)) ((blk_hdr_ptr_t)lbp)->bsiz = SIZEOF(blk_hdr); b_top = lbp + ((blk_hdr_ptr_t)lbp)->bsiz; if (((blk_hdr_ptr_t)lbp)->levl) { if (CLI_PRESENT != cli_present("POINTER")) { util_out_print("Error: block pointer must be specified for this index block record.", TRUE); t_abort(gv_cur_region, csa); free(lbp); return; } if (BADDSEBLK == (blk_ptr = dse_getblk("POINTER", DSENOBML, DSEBLKNOCUR))) /* WARNING: assignment */ { t_abort(gv_cur_region, csa); free(lbp); return; } if (long_blk_id) { # ifdef BLK_NUM_64BIT MEMCP(&data[0], (char *)&blk_ptr, 0, SIZEOF(block_id_64), SIZEOF(block_id_64)); data_len = SIZEOF(block_id_64); # else t_abort(gv_cur_region, csa); free(lbp); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { if (blk_ptr != (block_id_32)blk_ptr){ util_out_print("Error: adding 64-bit pointer to pre-V7 block", TRUE); t_abort(gv_cur_region, csa); free(lbp); return; } temp = (block_id_32)blk_ptr; MEMCP(&data[0], (char *)&temp, 0, SIZEOF(block_id_32), SIZEOF(block_id_32)); data_len = SIZEOF(block_id_32); } } else { if (CLI_PRESENT != cli_present("DATA")) { util_out_print("Error: data must be specified for this data block record.", TRUE); t_abort(gv_cur_region, csa); free(lbp); return; } if (FALSE == dse_data(&data[0], &data_len)) { /* dse_data return of FALSE means cli_parse already issued an error */ t_abort(gv_cur_region, csa); free(lbp); return; } if (VMS_ONLY(key_len + ) data_len > csa->hdr->max_rec_size) { t_abort(gv_cur_region, csa); free(lbp); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(10) ERR_REC2BIG, 4, VMS_ONLY(key_len + ) data_len, (int4)csa->hdr->max_rec_size, REG_LEN_STR(gv_cur_region), ERR_GVIS, 2, LEN_AND_STR(key)); } } if (CLI_PRESENT == cli_present("RECORD")) { if (!(rp = skan_rnum(lbp, TRUE))) { /* a FALSE return from skan_rnum means either a cli parser problem or * a record beyond the end of the block both of which cause output */ t_abort(gv_cur_region, csa); free(lbp); return; } } else if (CLI_PRESENT == cli_present("OFFSET")) { if (!(rp = skan_offset(lbp, TRUE))) { /* a FALSE return from skan_offset means either a cli parser problem or * an offset beyond the end of the block both of which cause output */ t_abort(gv_cur_region, csa); free(lbp); return; } } else { util_out_print("Error: must specify a record number or offset for the record to be added.", TRUE); t_abort(gv_cur_region, csa); free(lbp); return; } new_bp = (uchar_ptr_t)malloc(blk_size); size = (key_len < patch_comp_count) ? key_len : patch_comp_count; for (cc = 0; cc < size && patch_comp_key[cc] == key[cc]; cc++) ; SET_CMPC((rec_hdr_ptr_t)new_bp, cc); new_len = key_len - cc + data_len + SIZEOF(rec_hdr); PUT_SHORT(&((rec_hdr_ptr_t)new_bp)->rsiz, new_len); MEMCP(new_bp, &key[cc], SIZEOF(rec_hdr), key_len - cc, blk_size); MEMCP(new_bp, &data[0], SIZEOF(rec_hdr) + key_len - cc, data_len, blk_size); if (rp < b_top) { GET_SHORT(rsize, &((rec_hdr_ptr_t)rp)->rsiz); if (rsize < SIZEOF(rec_hdr)) r_top = rp + SIZEOF(rec_hdr); else r_top = rp + rsize; if (r_top >= b_top) r_top = b_top; if (((blk_hdr_ptr_t)lbp)->levl) key_top = r_top - blk_id_size; else { for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top; ) if (!*key_top++ && !*key_top++) break; } if (EVAL_CMPC((rec_hdr_ptr_t)rp) > patch_comp_count) cc = patch_comp_count; else cc = EVAL_CMPC((rec_hdr_ptr_t)rp); size = key_top - rp - SIZEOF(rec_hdr); if (size > SIZEOF(patch_comp_key) - 2 - cc) size = SIZEOF(patch_comp_key) - 2 - cc; if (size < 0) size = 0; memcpy(&patch_comp_key[cc], rp + SIZEOF(rec_hdr), size); patch_comp_count = cc + size; size = (key_len < patch_comp_count) ? key_len : patch_comp_count; for (cc = 0; cc < size && patch_comp_key[cc] == key[cc]; cc++) ; SET_CMPC((rec_hdr_ptr_t)(new_bp + new_len), cc); rsize = patch_comp_count - cc + r_top - key_top + SIZEOF(rec_hdr); PUT_SHORT(&((rec_hdr_ptr_t)(new_bp + new_len))->rsiz, rsize); MEMCP(new_bp, &patch_comp_key[cc], new_len + SIZEOF(rec_hdr), patch_comp_count - cc, blk_size); MEMCP(new_bp, key_top, new_len + SIZEOF(rec_hdr) + patch_comp_count - cc, b_top - key_top, blk_size); new_len += patch_comp_count - cc + SIZEOF(rec_hdr) + b_top - key_top; } if (rp - lbp + new_len > blk_size) { util_out_print("Error: record too large for remaining space in block.", TRUE); t_abort(gv_cur_region, csa); free(lbp); free(new_bp); return; } memcpy(rp, new_bp, new_len); free(new_bp); ((blk_hdr_ptr_t)lbp)->bsiz += new_len + (unsigned int)(rp - b_top); BLK_INIT(bs_ptr, bs1); BLK_SEG(bs_ptr, (uchar_ptr_t)lbp + SIZEOF(blk_hdr), (int)((blk_hdr_ptr_t)lbp)->bsiz - SIZEOF(blk_hdr)); if (!BLK_FINI(bs_ptr, bs1)) { t_abort(gv_cur_region, csa); free(lbp); gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_AIMGBLKFAIL, 3, &blk, DB_LEN_STR(gv_cur_region)); return; } t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)lbp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); BUILD_AIMG_IF_JNL_ENABLED(cs_data, csa->ti->curr_tn); t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); free(lbp); return; } fis-gtm-V7.0-005/sr_port/dse_adstar.c0000644000032200000250000001141014342376331016315 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "gdscc.h" #include "cli.h" #include "copy.h" #include "filestruct.h" #include "jnl.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" #include "t_write.h" #include "t_end.h" #include "t_begin_crit.h" #include "gvcst_blk_build.h" #include "util.h" #include "t_abort.h" #include "gtmmsg.h" GBLREF char *update_array, *update_array_ptr; GBLREF cw_set_element cw_set[]; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF srch_hist dummy_hist; GBLREF uint4 update_array_size; error_def(ERR_AIMGBLKFAIL); error_def(ERR_DBRDONLY); error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEFAIL); error_def(ERR_DSEINVALBLKID); void dse_adstar(void) { blk_segment *bs1, *bs_ptr; block_id blk, blk_ptr; boolean_t long_blk_id; int4 blk_seg_cnt, blk_size; long blk_id_size; short rsize; srch_blk_status blkhist; uchar_ptr_t b_top, lbp; if (gv_cur_region->read_only) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ if (BADDSEBLK == (blk = dse_getblk("BLOCK", DSENOBML, DSEBLKCUR))) /* WARNING: assignment */ return; if (CLI_PRESENT != cli_present("POINTER")) { util_out_print("Error: block pointer must be specified.", TRUE); return; } if (BADDSEBLK == (blk_ptr = dse_getblk("POINTER", DSENOBML, DSEBLKNOCUR))) /* WARNING: assignment */ return; t_begin_crit(ERR_DSEFAIL); blk_size = cs_addrs->hdr->blk_size; blkhist.blk_num = blk; if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); lbp = (uchar_ptr_t)malloc(blk_size); memcpy(lbp, blkhist.buffaddr, blk_size); if (!((blk_hdr_ptr_t)lbp)->levl) { util_out_print("Error: cannot add a star record to a data block.", TRUE); free(lbp); t_abort(gv_cur_region, cs_addrs); return; } if (((blk_hdr_ptr_t)lbp)->bver > BLK_ID_32_VER) /* Check blk version to see if using 32 or 64 bit block_id */ { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else t_abort(gv_cur_region, cs_addrs); free(lbp); RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } if (((blk_hdr_ptr_t)lbp)->bsiz > blk_size) b_top = lbp + blk_size; else if (((blk_hdr_ptr_t)lbp)->bsiz < SIZEOF(blk_hdr)) b_top = lbp + SIZEOF(blk_hdr); else b_top = lbp + ((blk_hdr_ptr_t)lbp)->bsiz; if ((b_top - lbp) > (blk_size - SIZEOF(rec_hdr) - blk_id_size)) { util_out_print("Error: not enough free space in block for a star record.", TRUE); free(lbp); t_abort(gv_cur_region, cs_addrs); return; } assert((SIZEOF(rec_hdr) + blk_id_size) == (short)(SIZEOF(rec_hdr) + blk_id_size)); rsize = (short)(SIZEOF(rec_hdr) + blk_id_size); PUT_SHORT(&((rec_hdr_ptr_t)b_top)->rsiz, rsize); SET_CMPC((rec_hdr_ptr_t)b_top, 0); if (long_blk_id == TRUE) { # ifdef BLK_NUM_64BIT PUT_BLK_ID_64((b_top + SIZEOF(rec_hdr)), blk_ptr); # else t_abort(gv_cur_region, cs_addrs); free(lbp); RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { if (blk_ptr != (block_id_32)blk_ptr){ util_out_print("Error: attempting to add a 64-bit pointer to pre-V7 block.", TRUE); free(lbp); t_abort(gv_cur_region, cs_addrs); return; } PUT_BLK_ID_32((b_top + SIZEOF(rec_hdr)), blk_ptr); } ((blk_hdr_ptr_t)lbp)->bsiz += (unsigned int)(SIZEOF(rec_hdr) + blk_id_size); BLK_INIT(bs_ptr, bs1); BLK_SEG(bs_ptr, (uchar_ptr_t)lbp + SIZEOF(blk_hdr), (int)((blk_hdr_ptr_t)lbp)->bsiz - SIZEOF(blk_hdr)); if (!BLK_FINI(bs_ptr, bs1)) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_AIMGBLKFAIL, 3, &blk, DB_LEN_STR(gv_cur_region)); free(lbp); t_abort(gv_cur_region, cs_addrs); return; } t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)lbp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); BUILD_AIMG_IF_JNL_ENABLED(cs_data, cs_addrs->ti->curr_tn); t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); free(lbp); return; } fis-gtm-V7.0-005/sr_port/dse_all.c0000644000032200000250000001525314342376331015620 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_stdio.h" #include "util.h" #include "cli.h" #include "gdsroot.h" #include "gt_timer.h" #include "gtmmsg.h" #if defined(UNIX) #include "gtm_ipc.h" #endif #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "gdskill.h" #include "gdscc.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "dse.h" #ifdef UNIX # include "mutex.h" #endif #include "wcs_flu.h" #include "gtm_signal.h" GBLREF VSIG_ATOMIC_T util_interrupt; GBLREF block_id patch_curr_blk; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF short crash_count; GBLREF uint4 process_id; GBLREF gd_addr *original_header; GBLREF boolean_t dse_all_dump; /* TRUE if DSE ALL -DUMP is specified */ error_def(ERR_DBRDONLY); error_def(ERR_DSEWCINITCON); error_def(ERR_FREEZE); error_def(ERR_FREEZECTRL); void dse_all(void) { gd_region *reg; tp_region *region_list, *rg, *rg_last, *rg_new; /* A handy structure for maintaining a list of regions */ int i; sgmnt_addrs *old_addrs, *csa; sgmnt_data_ptr_t csd; gd_region *old_region; block_id old_block; int4 stat; boolean_t ref = FALSE; boolean_t crit = FALSE; boolean_t wc = FALSE; boolean_t flush = FALSE; boolean_t freeze = FALSE; boolean_t nofreeze = FALSE; boolean_t seize = FALSE; boolean_t release = FALSE; boolean_t dump = FALSE; boolean_t override = FALSE; boolean_t was_crit; boolean_t clear_corrupt = FALSE; UNIX_ONLY(char *fgets_res;) old_addrs = cs_addrs; old_region = gv_cur_region; old_block = patch_curr_blk; if (cli_present("RENEW") == CLI_PRESENT) { crit = ref = wc = nofreeze = TRUE; GET_CONFIRM_AND_HANDLE_NEG_RESPONSE; } else { if (cli_present("CRITINIT") == CLI_PRESENT) crit = TRUE; if (cli_present("REFERENCE") == CLI_PRESENT) ref = TRUE; if (cli_present("WCINIT") == CLI_PRESENT) { GET_CONFIRM_AND_HANDLE_NEG_RESPONSE; wc = TRUE; } if (cli_present("BUFFER_FLUSH") == CLI_PRESENT) flush = TRUE; if (cli_present("SEIZE") == CLI_PRESENT) seize = TRUE; if (cli_present("RELEASE") == CLI_PRESENT) release = TRUE; stat = cli_present("FREEZE"); if (stat == CLI_NEGATED) nofreeze = TRUE; else if (stat == CLI_PRESENT) { freeze = TRUE; nofreeze = FALSE; } if (cli_present("OVERRIDE") == CLI_PRESENT) override = TRUE; if (cli_present("DUMP") == CLI_PRESENT) dump = TRUE; if (cli_present("CLEARCORRUPT") == CLI_PRESENT) clear_corrupt = TRUE; } if (!dump && gv_cur_region->read_only) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); region_list = NULL; for (i = 0, reg = original_header->regions; i < original_header->n_regions; i++, reg++) { if (!IS_REG_BG_OR_MM(reg)) { util_out_print("Skipping region !AD: not BG or MM access", TRUE, REG_LEN_STR(reg)); continue; } if (!reg->open) { if (!IS_STATSDB_REG(reg)) util_out_print("Skipping region !AD as it is not bound to any namespace.", TRUE, REG_LEN_STR(reg)); continue; } if (dump) { gv_cur_region = reg; cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; dse_all_dump = TRUE; dse_dmp_fhead(); assert(!dse_all_dump); /* should have been reset by "dse_dmp_fhead" */ } else /* put on region list in order of ftok value so processed in same order that crits are obtained */ insert_region(reg, &(region_list), NULL, SIZEOF(tp_region)); } if (!dump) { /* Now run the list of regions in the sorted ftok order to execute the desired commands */ for (rg = region_list; NULL != rg; rg = rg->fPtr) { gv_cur_region = rg->reg; assert(IS_REG_BG_OR_MM(gv_cur_region)); cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; csd = cs_addrs->hdr; patch_curr_blk = get_dir_root(); if (crit) { gtm_mutex_init(gv_cur_region, NUM_CRIT_ENTRY(csd), TRUE); cs_addrs->nl->in_crit = 0; cs_addrs->hold_onto_crit = FALSE; /* reset this just before cs_addrs->now_crit is reset */ cs_addrs->now_crit = FALSE; } UPDATE_CRASH_COUNT(cs_addrs, crash_count); if (freeze) { while (REG_ALREADY_FROZEN == region_freeze(gv_cur_region, TRUE, override, FALSE, FALSE, FALSE)) { hiber_start(1000); if (util_interrupt) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) ERR_FREEZECTRL); break; } } if (freeze != !(FROZEN(csd))) { util_out_print("Region !AD is now FROZEN", TRUE, REG_LEN_STR(gv_cur_region)); } } was_crit = cs_addrs->now_crit; if (seize) { if (!was_crit) /* No point seizing crit if WE already have it held */ grab_crit_encr_cycle_sync(gv_cur_region, WS_56); cs_addrs->hold_onto_crit = TRUE; /* Need to do this AFTER grab_crit */ cs_addrs->dse_crit_seize_done = TRUE; } if (wc) { if (!was_crit && !seize) grab_crit_encr_cycle_sync(gv_cur_region, WS_57); DSE_WCREINIT(cs_addrs); if (!was_crit && (!seize || release)) rel_crit(gv_cur_region); } if (flush) wcs_flu(WCSFLU_FLUSH_HDR | WCSFLU_WRITE_EPOCH | WCSFLU_SYNC_EPOCH); if (release) { /* user wants crit to be released unconditionally so "was_crit" not checked like everywhere else */ if (cs_addrs->now_crit) { cs_addrs->dse_crit_seize_done = FALSE; cs_addrs->hold_onto_crit = FALSE; /* need to do this BEFORE rel_crit */ rel_crit(gv_cur_region); } else { assert(!cs_addrs->hold_onto_crit && !cs_addrs->dse_crit_seize_done); util_out_print("Current process does not own the Region: !AD.", TRUE, REG_LEN_STR(gv_cur_region)); } } if (nofreeze) { if (REG_ALREADY_FROZEN == region_freeze(gv_cur_region, FALSE, override, FALSE, FALSE, FALSE)) util_out_print("Region: !AD is frozen by another user, not releasing freeze", TRUE, REG_LEN_STR(gv_cur_region)); else util_out_print("Region !AD is now UNFROZEN", TRUE, REG_LEN_STR(gv_cur_region)); } if (ref) cs_addrs->nl->ref_cnt = 1; if (clear_corrupt) csd->file_corrupt = FALSE; } } cs_addrs = old_addrs; gv_cur_region = old_region; patch_curr_blk = old_block; return; } fis-gtm-V7.0-005/sr_port/dse_b_dmp.c0000644000032200000250000002441114342376331016125 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_signal.h" #include "gtm_fcntl.h" #include "gtm_stat.h" #include #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "gdsbml.h" #include "cli.h" #include "dse.h" #include "util.h" #include "eintr_wrappers.h" #include "filestruct.h" /* Include prototypes */ #include "t_qread.h" #define REUSABLE_CHAR ":" #define FREE_CHAR DOT_CHAR #define BUSY_CHAR "X" #define CORRUPT_CHAR "?" GBLREF gd_region *gv_cur_region; GBLREF int patch_is_fdmp, patch_rec_counter; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF VSIG_ATOMIC_T util_interrupt; LITREF char *gtm_dbversion_table[]; error_def(ERR_BITMAPSBAD); error_def(ERR_CTRLC); error_def(ERR_DSEBLKRDFAIL); #ifdef DEBUG /* Debug GT.M versions support the ability to decode a block image. This capability is useful to * display PBLKs from journal extracts. See test commit PBLK2 for more information on how to use * this debug-only feature. */ #define CAN_USE_IMAGE #endif boolean_t dse_b_dmp(void) { cache_rec_ptr_t cr; block_id blk, count, lmap_num; boolean_t bm_free, invalid_bitmap = FALSE, is_mm, was_crit, was_hold_onto_crit; enum db_ver ondsk_blkver; # ifndef BLK_NUM_64BIT gtm_int8 count2; # endif int4 bplmap, dummy_int, head, iter1, iter2, len, mapsize, nocrit_present, util_len, lmap_indx, mask2; sm_uc_ptr_t bp, b_top, mb, rp; unsigned char mask, util_buff[MAX_UTIL_LEN]; char image_fn[MAX_FN_LEN + 1]; unsigned short image_fn_len = SIZEOF(image_fn); int image_fd, image_status; struct stat image_stat_buf; boolean_t use_image = FALSE; head = cli_present("HEADER"); if (BADDSEBLK == (blk = dse_getblk("BLOCK", DSEBMLOK, DSEBLKCUR))) /* WARNING: assignment */ return FALSE; if (CLI_PRESENT == cli_present("COUNT")) { # ifdef BLK_NUM_64BIT if (!cli_get_hex64("COUNT", (gtm_uint8 *)&count)) return FALSE; # else if (!cli_get_hex64("COUNT", (gtm_uint8 *)&count2)) return FALSE; else { assert(count2 == (int4)count2); count = (int4)count2; } # endif if (count < 1) return FALSE; } else count = 1; #ifdef CAN_USE_IMAGE if (CLI_PRESENT == cli_present("IMAGE")) { if (FALSE == cli_get_str("IMAGE", image_fn, &image_fn_len)) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); image_fn[image_fn_len] = '\0'; image_fd = OPEN(image_fn, O_RDONLY); if (FD_INVALID == image_fd) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(13) ERR_SYSCALL, 5, LEN_AND_LIT("open"), CALLFROM, errno, 0, ERR_TEXT, 2, image_fn_len, image_fn); } FSTAT_FILE(image_fd, &image_stat_buf, image_status); if (-1 == image_status) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(13) ERR_SYSCALL, 5, LEN_AND_LIT("fstat"), CALLFROM, errno, 0, ERR_TEXT, 2, image_fn_len, image_fn); } bp = (sm_uc_ptr_t)mmap(NULL, image_stat_buf.st_size, PROT_READ, MAP_PRIVATE, image_fd, 0); if (NULL == bp) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(13) ERR_SYSCALL, 5, LEN_AND_LIT("mmap"), CALLFROM, errno, 0, ERR_TEXT, 2, image_fn_len, image_fn); } util_out_print(0, TRUE); use_image = TRUE; } #endif util_out_print(0, TRUE); bplmap = cs_addrs->hdr->bplmap; is_mm = (dba_mm == cs_addrs->hdr->acc_meth); mapsize = BM_SIZE(bplmap); patch_rec_counter = 1; was_crit = cs_addrs->now_crit; nocrit_present = (CLI_NEGATED == cli_present("CRIT")); DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); for ( ; ; ) { if (!use_image && !(bp = t_qread(blk, &dummy_int, &cr))) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); } #ifdef CAN_USE_IMAGE else if (use_image) util_out_print("Dumping contents of !AD", TRUE, image_fn_len, image_fn); #endif if (((blk / bplmap) * bplmap) != blk) { if (((blk_hdr_ptr_t) bp)->levl && patch_is_fdmp) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); util_out_print("Error: cannot perform GLO/ZWR dump on index block.", TRUE); return FALSE; } if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) b_top = bp + cs_addrs->hdr->blk_size; else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) b_top = bp + SIZEOF(blk_hdr); else b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; if (CLI_NEGATED != head && !patch_is_fdmp) { memcpy(util_buff, "Block ", 6); util_len = 6; util_len += i2hexl_nofill(blk, &util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], " Size ", 8); util_len += 8; util_len += i2hex_nofill(((blk_hdr_ptr_t)bp)->bsiz, &util_buff[util_len], MAX_HEX_INT); memcpy(&util_buff[util_len], " Level !UL TN ", 18); util_len += 18; util_len += i2hexl_nofill(((blk_hdr_ptr_t)bp)->tn, &util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len++], " ", 1); #ifdef CAN_USE_IMAGE if (use_image) ondsk_blkver = ((blk_hdr_ptr_t)bp)->bver; else #endif ondsk_blkver = (0 < ((blk_hdr_ptr_t)bp)->bsiz) ? ((blk_hdr_ptr_t)bp)->bver : cs_addrs->hdr->desired_db_format; len = STRLEN(gtm_dbversion_table[ondsk_blkver]); memcpy(&util_buff[util_len], gtm_dbversion_table[ondsk_blkver], len); util_len += len; memcpy(&util_buff[util_len], "!/", 2); util_len += 2; util_buff[util_len] = 0; util_out_print((caddr_t)util_buff, TRUE, ((blk_hdr_ptr_t) bp)->levl ); } rp = bp + SIZEOF(blk_hdr); if (CLI_PRESENT != head && (!patch_is_fdmp || ((blk_hdr_ptr_t) bp)->levl == 0)) { while (!util_interrupt && (rp = dump_record(rp, blk, bp, b_top))) patch_rec_counter += 1; } if (util_interrupt) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CTRLC); break; } if (CLI_NEGATED == head) util_out_print(0, TRUE); } else if (!patch_is_fdmp) { if (CLI_NEGATED != head) { if (0 == bplmap) { memcpy(util_buff, "Block ", 6); util_len = 6; util_len += i2hexl_nofill(blk, &util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], " Size ", 8); util_len += 8; /* Using MAX_HEX_SHORT for int value because to save line space * since the value should always fit in 2-bytes */ util_len += i2hex_nofill(mapsize, &util_buff[util_len], MAX_HEX_SHORT); memcpy(&util_buff[util_len], " Master Status: Cannot Determine (bplmap == 0)!/", 50); util_len += 50; util_buff[util_len] = 0; util_out_print((caddr_t)util_buff, TRUE ); } else { mb = cs_addrs->bmm + (blk / (8 * bplmap)); lmap_num = blk / bplmap; mask = 1 << (lmap_num - ((lmap_num / 8) * 8)); bm_free = mask & *mb; memcpy(util_buff, "Block ", 6); util_len = 6; util_len += i2hexl_nofill(blk, &util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], " Size ", 7); util_len += 7; util_len += i2hex_nofill(((blk_hdr_ptr_t)bp)->bsiz, &util_buff[util_len], MAX_HEX_INT); memcpy(&util_buff[util_len], " Level !SB TN ", 16); util_len += 16; util_len += i2hexl_nofill(((blk_hdr_ptr_t)bp)->tn, &util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], " ", 1); util_len++; len = STRLEN(gtm_dbversion_table[((blk_hdr_ptr_t)bp)->bver]); memcpy(&util_buff[util_len], gtm_dbversion_table[((blk_hdr_ptr_t)bp)->bver], len); util_len += len; util_buff[util_len] = 0; util_out_print((caddr_t)util_buff, FALSE, ((blk_hdr_ptr_t)bp)->levl ); util_len = 0; memcpy(&util_buff[util_len], " Master Status: !AD!/",23); util_len = 23; util_buff[util_len] = 0; util_out_print((caddr_t)util_buff, TRUE, bm_free ? 10 : 4, bm_free ? "Free Space" : "Full"); } } if (CLI_PRESENT != head) { util_out_print(" !_Low order High order", TRUE); lmap_indx = 0; while (lmap_indx < bplmap) { memcpy(util_buff, "Block ", 6); util_len = 6; i2hexl_blkfill(blk + lmap_indx, &util_buff[util_len], MAX_HEX_INT8); util_len += 16; memcpy(&util_buff[util_len], ":!_| ", 6); util_len += 6; util_buff[util_len] = 0; util_out_print((caddr_t)util_buff, FALSE); for (iter1 = 0; iter1 < 4; iter1++) { for (iter2 = 0; iter2 < 8; iter2++) { mask2 = dse_lm_blk_free(lmap_indx, bp + SIZEOF(blk_hdr)); if (!mask2) util_out_print("!AD", FALSE, 1, BUSY_CHAR); else if (BLK_FREE == mask2) util_out_print("!AD", FALSE, 1, FREE_CHAR); else if (BLK_RECYCLED == mask2) util_out_print("!AD", FALSE, 1, REUSABLE_CHAR); else { invalid_bitmap = TRUE; util_out_print("!AD", FALSE, 1, CORRUPT_CHAR); } if (++lmap_indx >= bplmap) break; } util_out_print(" ", FALSE); if (lmap_indx >= bplmap) break; } util_out_print("|", TRUE); if (util_interrupt) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CTRLC); } } util_out_print("!/'!AD' == BUSY '!AD' == FREE '!AD' == REUSABLE '!AD' == CORRUPT!/", TRUE,1, BUSY_CHAR, 1, FREE_CHAR, 1, REUSABLE_CHAR, 1, CORRUPT_CHAR); if (invalid_bitmap) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_BITMAPSBAD); } } } count--; if (count <= 0 || util_interrupt) break; blk++; if (blk >= cs_addrs->ti->total_blks) blk = 0; } DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return TRUE; } fis-gtm-V7.0-005/sr_port/dse_cache.c0000644000032200000250000002567714342376331016126 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2021 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_stdio.h" #include "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "cli.h" #include "gdsblk.h" #include "util.h" #include "dse.h" #include "stringpool.h" /* for GET_CURR_TIME_IN_DOLLARH_AND_ZDATE macro */ #include "op.h" /* for op_fnzdate and op_zhorolog prototype */ #include "wcs_recover.h" /* for wcs_recover prototype */ #include "wcs_phase2_commit_wait.h" #include "sleep_cnt.h" /* for SIGNAL_WRITERS_TO_STOP/RESUME and WAIT_FOR_WRITERS_TO_STOP macro */ #include "memcoherency.h" /* for SIGNAL_WRITERS_TO_STOP/RESUME and WAIT_FOR_WRITERS_TO_STOP macro */ #include "wcs_sleep.h" /* for SIGNAL_WRITERS_TO_STOP/RESUME and WAIT_FOR_WRITERS_TO_STOP macro */ GBLREF gd_region *gv_cur_region; GBLREF gd_addr *original_header; error_def(ERR_SIZENOTVALID4); #define DB_ABS2REL(X) (uintszofptr_t)((uintszofptr_t)(X) - (uintszofptr_t)csa->nl) #define CLEAN_VERIFY "verification is clean" #define UNCLEAN_VERIFY "verification is NOT clean (see operator log for details)" #define RECOVER_DONE "recovery complete (see operator log for details)" #define RECOVER_NOT_APPLIC "recovery not applicable with MM access method" #define OUT_LINE 256 + 1 error_def(ERR_SIZENOTVALID4); void dse_cache(void) { boolean_t all_present, change_present, recover_present, show_present, verify_present, was_crit, is_clean; boolean_t nocrit_present, offset_present, size_present, value_present; gd_region *reg, *r_top; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; mval dollarh_mval, zdate_mval; int4 size; uint4 offset, value, old_value, lcnt; char dollarh_buffer[MAXNUMLEN], zdate_buffer[SIZEOF(DSE_DMP_TIME_FMT)]; char temp_str[OUT_LINE], temp_str1[OUT_LINE]; sm_uc_ptr_t chng_ptr; cache_rec_ptr_t cr_que_lo; boolean_t is_mm, was_hold_onto_crit, wc_blocked_ok; uintszofptr_t section_offset; all_present = (CLI_PRESENT == cli_present("ALL")); recover_present = (CLI_PRESENT == cli_present("RECOVER")); verify_present = (CLI_PRESENT == cli_present("VERIFY")); change_present = (CLI_PRESENT == cli_present("CHANGE")); show_present = (CLI_PRESENT == cli_present("SHOW")); offset_present = (CLI_PRESENT == cli_present("OFFSET")); size_present = (CLI_PRESENT == cli_present("SIZE")); value_present = (CLI_PRESENT == cli_present("VALUE")); nocrit_present = (CLI_NEGATED == cli_present("CRIT")); assert(!nocrit_present || show_present); /* NOCRIT is only applicable for SHOW */ if (offset_present && !cli_get_hex("OFFSET", &offset)) return; if (size_present) { if (!cli_get_int("SIZE", &size)) return; if (!((SIZEOF(char) == size) || (SIZEOF(short) == size) || (SIZEOF(int4) == size))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_SIZENOTVALID4); } if (value_present && !cli_get_hex("VALUE", &value)) return; assert(change_present || recover_present || show_present || verify_present); for (reg = original_header->regions, r_top = reg + original_header->n_regions; reg < r_top; reg++) { if (!all_present && (reg != gv_cur_region)) continue; if (!reg->open || reg->was_open) continue; is_mm = (dba_mm == reg->dyn.addr->acc_meth); csa = &FILE_INFO(reg)->s_addrs; assert(is_mm || (csa->db_addrs[0] == (sm_uc_ptr_t)csa->nl)); was_crit = csa->now_crit; DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, csa, reg); if (verify_present || recover_present) { GET_CURR_TIME_IN_DOLLARH_AND_ZDATE(dollarh_mval, dollarh_buffer, zdate_mval, zdate_buffer); if (verify_present) { /* Before invoking wcs_verify, wait for any pending phase2 commits to finish. Need to wait as * otherwise ongoing phase2 commits can result in cache verification returning FALSE (e.g. due to * DBCRERR message indicating that cr->in_tend is non-zero). * Also, need to wait for concurrent writers to stop to avoid wcs_verify from incorrectly concluding * that there is a problem with the active queue. */ wc_blocked_ok = UNIX_ONLY(TRUE) VMS_ONLY(!is_mm); /* MM on VMS doesn't support wcs_recvoer */ if (wc_blocked_ok) SIGNAL_WRITERS_TO_STOP(csa->nl); /* done sooner to avoid any new writers starting up */ if (csa->nl->wcs_phase2_commit_pidcnt && !is_mm) { /* No need to check return value since even if it fails, we want to do cache verification */ wcs_phase2_commit_wait(csa, NULL); } if (wc_blocked_ok) WAIT_FOR_WRITERS_TO_STOP(csa->nl, lcnt, MAXWTSTARTWAIT / 4); /* reduced wait time for DSE */ is_clean = wcs_verify(reg, FALSE, FALSE); /* expect_damage is FALSE, caller_is_wcs_recover is * FALSE */ if (wc_blocked_ok) SIGNAL_WRITERS_TO_RESUME(csa->nl); } else { if (UNIX_ONLY(TRUE)VMS_ONLY(!is_mm)) { SET_TRACEABLE_VAR(csa->nl->wc_blocked, WC_BLOCK_RECOVER); /* No need to invoke function "wcs_phase2_commit_wait" as "wcs_recover" does that anyways */ wcs_recover(reg); assert(WC_UNBLOCK == csa->nl->wc_blocked); /* wcs_recover() should have cleared this */ } } assert(20 == STR_LIT_LEN(DSE_DMP_TIME_FMT)); /* if they are not the same, the !20AD below should change */ util_out_print("Time !20AD : Region !12AD : Cache !AZ", TRUE, zdate_mval.str.len, zdate_mval.str.addr, REG_LEN_STR(reg), verify_present ? (is_clean ? CLEAN_VERIFY : UNCLEAN_VERIFY) : UNIX_ONLY(RECOVER_DONE) VMS_ONLY(is_mm ? RECOVER_NOT_APPLIC : RECOVER_DONE)); } else if (offset_present) { if ((csa->nl->sec_size VMS_ONLY(* OS_PAGELET_SIZE)) < (offset + size)) util_out_print("Region !12AD : Error: offset + size is greater than region's max_offset = 0x!XL", TRUE, REG_LEN_STR(reg), (csa->nl->sec_size VMS_ONLY(* OS_PAGELET_SIZE))); else { chng_ptr = (sm_uc_ptr_t)csa->nl + offset; if (SIZEOF(char) == size) { SNPRINTF(temp_str, OUT_LINE, "!UB [0x!XB]"); old_value = *(sm_uc_ptr_t)chng_ptr; } else if (SIZEOF(short) == size) { SNPRINTF(temp_str, OUT_LINE, "!UW [0x!XW]"); old_value = *(sm_ushort_ptr_t)chng_ptr; } else if (SIZEOF(int4) == size) { SNPRINTF(temp_str, OUT_LINE, "!UL [0x!XL]"); old_value = *(sm_uint_ptr_t)chng_ptr; } if (value_present) { if (SIZEOF(char) == size) *(sm_uc_ptr_t)chng_ptr = value; else if (SIZEOF(short) == size) *(sm_ushort_ptr_t)chng_ptr = value; else if (SIZEOF(int4) == size) *(sm_uint_ptr_t)chng_ptr = value; } else value = old_value; if (show_present) { SNPRINTF(temp_str1, OUT_LINE, "Region !12AD : Location !UL [0x!XL] : Value = %s :" " Size = !UB [0x!XB]", temp_str); util_out_print(temp_str1, TRUE, REG_LEN_STR(reg), offset, offset, value, value, size, size); } else { SNPRINTF(temp_str1, OUT_LINE, "Region !12AD : Location !UL [0x!XL] : Old Value = %s : " "New Value = %s : Size = !UB [0x!XB]", temp_str, temp_str); util_out_print(temp_str1, TRUE, REG_LEN_STR(reg), offset, offset, old_value, old_value, value, value, size, size); } } } else { assert(show_present); /* this should be a DSE CACHE -SHOW command with no other qualifiers */ util_out_print("Region !AD : Shared_memory = 0x!XJ", TRUE, REG_LEN_STR(reg), csa->nl); util_out_print("Region !AD : node_local = 0x!XJ", TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->nl)); util_out_print("Region !AD : critical = 0x!XJ", TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->critical)); if (JNL_ALLOWED(csa)) { util_out_print("Region !AD : jnl_buffer_struct = 0x!XJ", TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->jnl->jnl_buff)); util_out_print("Region !AD : jnl_buffer_data = 0x!XJ", TRUE, REG_LEN_STR(reg), DB_ABS2REL(&csa->jnl->jnl_buff->buff[csa->jnl->jnl_buff->buff_off])); } util_out_print("Region !AD : shmpool_buffer = 0x!XJ", TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->shmpool_buffer)); util_out_print("Region !AD : lock_space = 0x!XJ", TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->mlkctl)); csd = csa->hdr; if (!is_mm) { util_out_print("Region !AD : cache_queues_state = 0x!XJ", TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->acc_meth.bg.cache_state)); cr_que_lo = &csa->acc_meth.bg.cache_state->cache_array[0]; util_out_print("Region !AD : cache_que_header = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL", TRUE, REG_LEN_STR(reg), DB_ABS2REL(cr_que_lo), csd->bt_buckets, SIZEOF(cache_rec)); util_out_print("Region !AD : cache_record = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL", TRUE, REG_LEN_STR(reg), DB_ABS2REL(cr_que_lo + csd->bt_buckets), csd->n_bts, SIZEOF(cache_rec)); section_offset = ROUND_UP2(DB_ABS2REL(cr_que_lo + csd->bt_buckets + csd->n_bts), OS_PAGE_SIZE); util_out_print("Region !AD : global_buffer = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL", TRUE, REG_LEN_STR(reg), section_offset, csd->n_bts, csd->blk_size); if (USES_ENCRYPTION(csd->is_encrypted)) { section_offset += (gtm_uint64_t)csd->n_bts * csd->blk_size; /* In case of an encrypted database, bp_top is actually the beginning of the encrypted * global buffer array (an array maintained parallely with the regular unencrypted global * buffer array). */ util_out_print("Region !AD : encrypted_globuff = 0x!XJ : Numelems = 0x!XL : Elemsize = " "0x!XL", TRUE, REG_LEN_STR(reg), section_offset, csd->n_bts, csd->blk_size); } util_out_print("Region !AD : db_file_header = 0x!XJ", TRUE, REG_LEN_STR(reg), DB_ABS2REL(csd)); util_out_print("Region !AD : bt_que_header = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL", TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->bt_header), csd->bt_buckets, SIZEOF(bt_rec)); util_out_print("Region !AD : th_base = 0x!XJ", TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->th_base)); util_out_print("Region !AD : bt_record = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL", TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->bt_base), csd->n_bts, SIZEOF(bt_rec)); util_out_print("Region !AD : shared_memory_size = 0x!XJ", TRUE, REG_LEN_STR(reg), csa->nl->sec_size VMS_ONLY(* OS_PAGELET_SIZE)); } else { util_out_print("Region !AD : shared_memory_size = 0x!XJ", TRUE, REG_LEN_STR(reg), csa->nl->sec_size VMS_ONLY(* OS_PAGELET_SIZE)); util_out_print("Region !AD : db_file_header = 0x!XJ", TRUE, REG_LEN_STR(reg), csd); } } DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, csa, reg); } return; } fis-gtm-V7.0-005/sr_port/dse_chng_bhead.c0000644000032200000250000001247614342376331017116 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_time.h" #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "gdsbml.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "cli.h" #include "send_msg.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" #include "t_write.h" #include "t_end.h" #include "t_begin_crit.h" #include "process_deferred_stale.h" #include "util.h" #include "t_abort.h" #include "gvcst_blk_build.h" /* for the BUILD_AIMG_IF_JNL_ENABLED macro */ #include "gtmmsg.h" #include "gtmcrypt.h" GBLREF cache_rec_ptr_t cr_array[]; /* Maximum number of blocks that can be in transaction */ GBLREF char *update_array, *update_array_ptr; GBLREF cw_set_element cw_set[]; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF srch_hist dummy_hist; GBLREF uint4 update_array_size; error_def(ERR_AIMGBLKFAIL); error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEFAIL); error_def(ERR_DBRDONLY); void dse_chng_bhead(void) { blk_hdr new_hdr; blk_segment *bs1, *bs_ptr; block_id blk; boolean_t chng_blk, ismap, was_hold_onto_crit; int4 blk_seg_cnt, blk_size; /* needed for BLK_INIT,BLK_SEG and BLK_FINI macros */ int4 x; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; srch_blk_status blkhist; trans_num tn; uint4 mapsize; csa = cs_addrs; if (gv_cur_region->read_only) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ chng_blk = FALSE; if (BADDSEBLK == (blk = dse_getblk("BLOCK", DSEBMLOK, DSEBLKCUR))) /* WARNING: assignment */ return; csd = csa->hdr; assert(csd == cs_data); blk_size = csd->blk_size; ismap = IS_BITMAP_BLK(blk); mapsize = BM_SIZE(csd->bplmap); t_begin_crit(ERR_DSEFAIL); blkhist.blk_num = blk; if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(1) ERR_DSEBLKRDFAIL); new_hdr = *(blk_hdr_ptr_t)blkhist.buffaddr; if (CLI_PRESENT == cli_present("LEVEL")) { if (!cli_get_hex("LEVEL", (uint4 *)&x)) { t_abort(gv_cur_region, csa); return; } if (ismap && (unsigned char)x != LCL_MAP_LEVL) { util_out_print("Error: invalid level for a bit map block.", TRUE); t_abort(gv_cur_region, csa); return; } if (!ismap && (x < 0 || x > MAX_BT_DEPTH + 1)) { util_out_print("Error: invalid level.", TRUE); t_abort(gv_cur_region, csa); return; } new_hdr.levl = (unsigned char)x; chng_blk = TRUE; if (new_hdr.bsiz < SIZEOF(blk_hdr)) new_hdr.bsiz = SIZEOF(blk_hdr); if (new_hdr.bsiz > blk_size) new_hdr.bsiz = blk_size; } if (CLI_PRESENT == cli_present("BSIZ")) { if (!cli_get_hex("BSIZ", (uint4 *)&x)) { t_abort(gv_cur_region, csa); return; } if (ismap && x != mapsize) { util_out_print("Error: invalid bsiz.", TRUE); t_abort(gv_cur_region, csa); return; } else if (x < SIZEOF(blk_hdr) || x > blk_size) { util_out_print("Error: invalid bsiz.", TRUE); t_abort(gv_cur_region, csa); return; } chng_blk = TRUE; new_hdr.bsiz = x; } if (!chng_blk) t_abort(gv_cur_region, csa); else { BLK_INIT(bs_ptr, bs1); BLK_SEG(bs_ptr, blkhist.buffaddr + SIZEOF(new_hdr), new_hdr.bsiz - SIZEOF(new_hdr)); if (!BLK_FINI(bs_ptr, bs1)) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_AIMGBLKFAIL, 3, &blk, DB_LEN_STR(gv_cur_region)); t_abort(gv_cur_region, csa); return; } t_write(&blkhist, (unsigned char *)bs1, 0, 0, new_hdr.levl, TRUE, FALSE, GDS_WRITE_KILLTN); BUILD_AIMG_IF_JNL_ENABLED(csd, csa->ti->curr_tn); t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); } if (CLI_PRESENT == cli_present("TN")) { if (!cli_get_hex64("TN", &tn)) return; t_begin_crit(ERR_DSEFAIL); CHECK_TN(csa, csd, csd->trans_hist.curr_tn); /* can issue rts_error TNTOOLARGE */ assert(csa->ti->early_tn == csa->ti->curr_tn); if (NULL == (blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) { gtm_putmsg_csa(CSA_ARG(csa) VARLSTCNT(1) ERR_DSEBLKRDFAIL); t_abort(gv_cur_region, csa); return; } if (new_hdr.bsiz < SIZEOF(blk_hdr)) new_hdr.bsiz = SIZEOF(blk_hdr); if (new_hdr.bsiz > blk_size) new_hdr.bsiz = blk_size; BLK_INIT(bs_ptr, bs1); BLK_SEG(bs_ptr, blkhist.buffaddr + SIZEOF(new_hdr), new_hdr.bsiz - SIZEOF(new_hdr)); BLK_FINI(bs_ptr, bs1); t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)blkhist.buffaddr)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); /* Pass the desired tn as argument to bg_update/mm_update below */ BUILD_AIMG_IF_JNL_ENABLED_AND_T_END_WITH_EFFECTIVE_TN(csa, csd, tn, &dummy_hist); } return; } fis-gtm-V7.0-005/sr_port/dse_chng_fhead.c0000644000032200000250000006417414342376331017124 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /******************************************************************************* * * MODULE NAME: CHANGE_FHEAD * * CALLING SEQUENCE: void change_fhead() * * DESCRIPTION: This module changes values of certain fields * of the file header. The only range-checking * takes place on input, not in this routine, allowing * the user maximum control. * * HISTORY: * *******************************************************************************/ #include "gtm_string.h" #include "mdef.h" #include "gtm_stdlib.h" #include "gtm_unistd.h" #include "gtm_stdio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "cli.h" #include "timersp.h" #include "jnl.h" #include "util.h" #include "gtm_caseconv.h" #include "gt_timer.h" #include "timers.h" #include "send_msg.h" #include "dse.h" #include "gtmmsg.h" #include "mutex.h" #include "gtmcrypt.h" GBLREF VSIG_ATOMIC_T util_interrupt; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data *cs_data; GBLREF gd_region *gv_cur_region; GBLREF uint4 process_id; GBLREF uint4 image_count; LITREF char *gtm_dbversion_table[]; #define OUT_LINE 256 + 1 error_def(ERR_FREEZE); error_def(ERR_BLKSIZ512); error_def(ERR_DBRDONLY); error_def(ERR_SIZENOTVALID8); error_def(ERR_FREEZECTRL); #define GET_VALUE_INTO_X \ base = 10; \ if (('0' == buf[0]) && ('X' == buf[1])) \ { \ buf_len -= 2; \ memmove(buf, buf+2, buf_len); \ buf[buf_len] = 0; \ base = 16; \ } \ x = STRTOUL(buf, NULL, base); \ if ((0 == x) && ('0' != buf[0])) \ x = -2; /* Indicates Error (Invalid value) */ void dse_chng_fhead(void) { block_id blkcnt; # ifndef BLK_NUM_64BIT block_id_64 blkcnt2; # endif boolean_t was_crit, was_hold_onto_crit, corrupt_file_present; boolean_t override = FALSE, max_tn_present, max_tn_warn_present, curr_tn_present, change_tn; char temp_str[OUT_LINE], temp_str1[OUT_LINE], buf[MAX_LINE], *fname_ptr, hash_buff[GTMCRYPT_HASH_LEN]; const char *freeze_msg[] = { "UNFROZEN", "FROZEN" }; gtm_uint64_t value, old_value; int gethostname_res, gtmcrypt_errno; int4 base, x, index_x, save_x, fname_len, nocrit_present, location_present, value_present, size_present, size; seq_num seq_no; sm_uc_ptr_t chng_ptr; trans_num tn, prev_tn, max_tn_old, max_tn_warn_old, curr_tn_old, max_tn_new, max_tn_warn_new, curr_tn_new; uint4 location; unsigned short buf_len; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (gv_cur_region->read_only) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); memset(temp_str, 0, OUT_LINE); memset(temp_str1, 0, OUT_LINE); memset(buf, 0, MAX_LINE); was_crit = cs_addrs->now_crit; nocrit_present = (CLI_NEGATED == cli_present("CRIT")); DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); if (CLI_PRESENT == cli_present("OVERRIDE")) override = TRUE; # ifdef VMS if (cs_data->freeze && (cs_data->freeze != process_id || cs_data->image_count != image_count) && !override) # endif # ifdef UNIX if (FROZEN(cs_data) && (cs_data->image_count != process_id) && !override) # endif { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); util_out_print("Region: !AD is frozen by another user, not releasing freeze.", TRUE, REG_LEN_STR(gv_cur_region)); rts_error_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_FREEZE, 2, REG_LEN_STR(gv_cur_region)); return; } prev_tn = cs_addrs->ti->curr_tn; location_present = FALSE; if (CLI_PRESENT == cli_present("LOCATION")) { location_present = TRUE; if (!cli_get_hex("LOCATION", &location)) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return; } } if (CLI_PRESENT == cli_present("HEXLOCATION")) { location_present = TRUE; if (!cli_get_hex("HEXLOCATION", &location)) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return; } } if (CLI_PRESENT == cli_present("DECLOCATION")) { location_present = TRUE; if (!cli_get_int("DECLOCATION", (int4 *)&location)) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return; } } size_present = FALSE; if (CLI_PRESENT == cli_present("SIZE")) { size_present = TRUE; if (!cli_get_int("SIZE", &size)) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return; } } value_present = FALSE; if (CLI_PRESENT == cli_present("VALUE")) { value_present = TRUE; if (!cli_get_hex64("VALUE", (gtm_uint64_t *)&value)) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return; } } if (CLI_PRESENT == cli_present("HEXVALUE")) { value_present = TRUE; if (!cli_get_hex64("HEXVALUE", &value)) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return; } } if (CLI_PRESENT == cli_present("DECVALUE")) { value_present = TRUE; if (!cli_get_uint64("DECVALUE", (gtm_uint64_t *)&value)) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return; } } if (TRUE == location_present) { if (FALSE == size_present) size = SIZEOF(int4); if (!((SIZEOF(char) == size) || (SIZEOF(short) == size) || (SIZEOF(int4) == size) || (SIZEOF(gtm_int64_t) == size))) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_SIZENOTVALID8); } if ((0 > (int4)size) || ((uint4)SGMNT_HDR_LEN < (uint4)location) || ((uint4)SGMNT_HDR_LEN < ((uint4)location + (uint4)size))) util_out_print("Error: Cannot modify any location outside the file-header", TRUE); else if (0 != location % size) util_out_print("Error: Location !UL [0x!XL] should be a multiple of Size !UL", TRUE, location, location, size, size); else { chng_ptr = (sm_uc_ptr_t)cs_data + location; if (SIZEOF(char) == size) { SNPRINTF(temp_str, OUT_LINE, "!UB [0x!XB]"); old_value = *(sm_uc_ptr_t)chng_ptr; } else if (SIZEOF(short) == size) { SNPRINTF(temp_str, OUT_LINE, "!UW [0x!XW]"); old_value = *(sm_ushort_ptr_t)chng_ptr; } else if (SIZEOF(int4) == size) { SNPRINTF(temp_str, OUT_LINE, "!UL [0x!XL]"); old_value = *(sm_uint_ptr_t)chng_ptr; } else if (SIZEOF(gtm_int64_t) == size) { SNPRINTF(temp_str, OUT_LINE, "!@UQ [0x!@XQ]"); old_value = *(qw_num_ptr_t)chng_ptr; } if (value_present) { if (SIZEOF(char) == size) *(sm_uc_ptr_t)chng_ptr = (unsigned char) value; else if (SIZEOF(short) == size) *(sm_ushort_ptr_t)chng_ptr = (unsigned short) value; else if (SIZEOF(int4) == size) *(sm_uint_ptr_t)chng_ptr = (unsigned int) value; else if (SIZEOF(gtm_int64_t) == size) *(qw_num_ptr_t)chng_ptr = value; } else value = old_value; SNPRINTF(temp_str1, OUT_LINE, "Location !UL [0x!XL] : Old Value = %s : New Value = %s : Size = !UB [0x!XB]", temp_str, temp_str); if (SIZEOF(int4) >= size) util_out_print(temp_str1, TRUE, location, location, (uint4)old_value, (uint4)old_value, (uint4)value, (uint4)value, size, size); else util_out_print(temp_str1, TRUE, location, location, &old_value, &old_value, &value, &value, size, size); } } # ifdef BLK_NUM_64BIT if ((CLI_PRESENT == cli_present("TOTAL_BLKS")) && (cli_get_hex64("TOTAL_BLKS", (gtm_uint8 *)&blkcnt))) cs_addrs->ti->total_blks = blkcnt; if ((CLI_PRESENT == cli_present("BLOCKS_FREE")) && (cli_get_hex64("BLOCKS_FREE", (gtm_uint8 *)&blkcnt))) cs_addrs->ti->free_blocks = blkcnt; # else if ((CLI_PRESENT == cli_present("TOTAL_BLKS")) && (cli_get_hex64("TOTAL_BLKS", (gtm_uint8 *)&blkcnt2))) { assert(blkcnt2 == (block_id_32)blkcnt2); blkcnt = (block_id_32)blkcnt2; cs_addrs->ti->total_blks = blkcnt; } if ((CLI_PRESENT == cli_present("BLOCKS_FREE")) && (cli_get_hex64("BLOCKS_FREE", (gtm_uint8 *)&blkcnt2))) { assert(blkcnt2 == (block_id_32)blkcnt2); blkcnt = (block_id_32)blkcnt2; cs_addrs->ti->free_blocks = blkcnt; } # endif if ((CLI_PRESENT == cli_present("BLK_SIZE")) && (cli_get_int("BLK_SIZE", &x))) { if (!(x % DISK_BLOCK_SIZE) && (0 != x)) cs_data->blk_size = x; else { cs_data->blk_size = ((x/DISK_BLOCK_SIZE) + 1) * DISK_BLOCK_SIZE; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_BLKSIZ512, 2, x, cs_data->blk_size); } } if ((CLI_PRESENT == cli_present("RECORD_MAX_SIZE")) && (cli_get_int("RECORD_MAX_SIZE", &x))) { cs_data->max_rec_size = x; gv_cur_region->max_rec_size = x; } if ((CLI_PRESENT == cli_present("KEY_MAX_SIZE")) && (cli_get_int("KEY_MAX_SIZE", &x))) { if (cs_data->max_key_size > x) cs_data->maxkeysz_assured = FALSE; cs_data->max_key_size = x; gv_cur_region->max_key_size = x; } if ((CLI_PRESENT == cli_present("INHIBIT_KILLS")) && (cli_get_int("INHIBIT_KILLS", &x))) { cs_addrs->nl->inhibit_kills = x; } if (CLI_PRESENT == cli_present("INTERRUPTED_RECOV")) { x = cli_t_f_n("INTERRUPTED_RECOV"); if (1 == x) cs_data->recov_interrupted = TRUE; else if (0 == x) cs_data->recov_interrupted = FALSE; } if ((CLI_PRESENT == cli_present("REFERENCE_COUNT")) && (cli_get_int("REFERENCE_COUNT", &x))) cs_addrs->nl->ref_cnt = x; if ((CLI_PRESENT == cli_present("RESERVED_BYTES")) && (cli_get_int("RESERVED_BYTES", &x))) cs_data->reserved_bytes = x; if ((CLI_PRESENT == cli_present("DEF_COLLATION")) && (cli_get_int("DEF_COLLATION", &x))) cs_data->def_coll = x; if (CLI_PRESENT == cli_present("NULL_SUBSCRIPTS")) { x = cli_n_a_e("NULL_SUBSCRIPTS"); if (-1 != x) gv_cur_region->null_subs = cs_data->null_subs = (unsigned char)x; } if (CLI_PRESENT == cli_present("CERT_DB_VER")) { buf_len = SIZEOF(buf); if (cli_get_str("CERT_DB_VER", buf, &buf_len)) { lower_to_upper((uchar_ptr_t)buf, (uchar_ptr_t)buf, buf_len); for (index_x=0; index_x < GDSVLAST ; index_x++) if (0 == STRCMP(buf, gtm_dbversion_table[index_x])) { cs_data->certified_for_upgrade_to = (enum db_ver)index_x; break; } if (GDSVLAST <= index_x) util_out_print("Invalid value for CERT_DB_VER qualifier", TRUE); } } if (CLI_PRESENT == cli_present("DB_WRITE_FMT")) { buf_len = SIZEOF(buf); if (cli_get_str("DB_WRITE_FMT", buf, &buf_len)) { lower_to_upper((uchar_ptr_t)buf, (uchar_ptr_t)buf, buf_len); for (index_x=0; index_x < GDSVLAST ; index_x++) if (0 == STRCMP(buf, gtm_dbversion_table[index_x])) { cs_data->desired_db_format = (enum db_ver)index_x; cs_data->fully_upgraded = FALSE; break; } if (GDSVLAST <= index_x) util_out_print("Invalid value for DB_WRITE_FMT qualifier", TRUE); } } /* ---------- Begin ------ CURRENT_TN/MAX_TN/WARN_MAX_TN processing -------- */ max_tn_old = cs_data->max_tn; if ((CLI_PRESENT == cli_present("MAX_TN")) && (cli_get_hex64("MAX_TN", &max_tn_new))) max_tn_present = TRUE; else { max_tn_present = FALSE; max_tn_new = max_tn_old; } max_tn_warn_old = cs_data->max_tn_warn; if ((CLI_PRESENT == cli_present("WARN_MAX_TN")) && (cli_get_hex64("WARN_MAX_TN", &max_tn_warn_new))) max_tn_warn_present = TRUE; else { max_tn_warn_present = FALSE; max_tn_warn_new = max_tn_warn_old; } curr_tn_old = cs_addrs->ti->curr_tn; if ((CLI_PRESENT == cli_present("CURRENT_TN")) && (cli_get_hex64("CURRENT_TN", &curr_tn_new))) curr_tn_present = TRUE; else { curr_tn_present = FALSE; curr_tn_new = curr_tn_old; } change_tn = TRUE; if (max_tn_present) { if (max_tn_new < max_tn_warn_new) { change_tn = FALSE; util_out_print("MAX_TN value cannot be less than the current/specified value of WARN_MAX_TN", TRUE); } } if (max_tn_warn_present) { if (!max_tn_present && (max_tn_warn_new > max_tn_new)) { change_tn = FALSE; util_out_print("WARN_MAX_TN value cannot be greater than the current/specified value of MAX_TN", TRUE); } if (max_tn_warn_new < curr_tn_new) { change_tn = FALSE; util_out_print("WARN_MAX_TN value cannot be less than the current/specified value of CURRENT_TN", TRUE); } } if (curr_tn_present) { if (!max_tn_warn_present && (curr_tn_new > max_tn_warn_new)) { change_tn = FALSE; util_out_print("CURRENT_TN value cannot be greater than the current/specified value of WARN_MAX_TN", TRUE); } } if (change_tn) { if (max_tn_present) cs_data->max_tn = max_tn_new; if (max_tn_warn_present) cs_data->max_tn_warn = max_tn_warn_new; if (curr_tn_present) cs_addrs->ti->curr_tn = cs_addrs->ti->early_tn = curr_tn_new; assert(max_tn_new == cs_data->max_tn); assert(max_tn_warn_new == cs_data->max_tn_warn); assert(curr_tn_new == cs_addrs->ti->curr_tn); assert(max_tn_new >= max_tn_warn_new); assert(max_tn_warn_new >= curr_tn_new); } else { assert(max_tn_old == cs_data->max_tn); assert(max_tn_warn_old == cs_data->max_tn_warn); assert(curr_tn_old == cs_addrs->ti->curr_tn); } /* ---------- End ------ CURRENT_TN/MAX_TN/WARN_MAX_TN processing -------- */ if (CLI_PRESENT == cli_present("REG_SEQNO") && cli_get_hex64("REG_SEQNO", (gtm_uint64_t *)&seq_no)) cs_data->reg_seqno = seq_no; UNIX_ONLY( if (CLI_PRESENT == cli_present("STRM_NUM")) { assert(CLI_PRESENT == cli_present("STRM_REG_SEQNO")); if (cli_get_int("STRM_NUM", &x) && (0 <= x) && (MAX_SUPPL_STRMS > x) && (CLI_PRESENT == cli_present("STRM_REG_SEQNO")) && cli_get_hex64("STRM_REG_SEQNO", (gtm_uint64_t *)&seq_no)) cs_data->strm_reg_seqno[x] = seq_no; } ) VMS_ONLY( if (CLI_PRESENT == cli_present("RESYNC_SEQNO") && cli_get_hex64("RESYNC_SEQNO", (gtm_uint64_t *)&seq_no)) cs_data->resync_seqno = seq_no; if (CLI_PRESENT == cli_present("RESYNC_TN") && cli_get_hex64("RESYNC_TN", &tn)) cs_data->resync_tn = tn; ) UNIX_ONLY( if (CLI_PRESENT == cli_present("ZQGBLMOD_SEQNO") && cli_get_hex64("ZQGBLMOD_SEQNO", (gtm_uint64_t *)&seq_no)) cs_data->zqgblmod_seqno = seq_no; if (CLI_PRESENT == cli_present("ZQGBLMOD_TN") && cli_get_hex64("ZQGBLMOD_TN", &tn)) cs_data->zqgblmod_tn = tn; ) if (CLI_PRESENT == cli_present("STDNULLCOLL")) { if ( -1 != (x = cli_t_f_n("STDNULLCOLL"))) gv_cur_region->std_null_coll = cs_data->std_null_coll = x; } corrupt_file_present = (CLI_PRESENT == cli_present("CORRUPT_FILE")); if (corrupt_file_present) { x = cli_t_f_n("CORRUPT_FILE"); if (1 == x) cs_data->file_corrupt = TRUE; else if (0 == x) cs_data->file_corrupt = FALSE; } if ((CLI_PRESENT == cli_present("TIMERS_PENDING")) && (cli_get_int("TIMERS_PENDING", &x))) cs_addrs->nl->wcs_timers = x - 1; change_fhead_timer("FLUSH_TIME", cs_data->flush_time, (dba_bg == cs_data->acc_meth ? TIM_FLU_MOD_BG : TIM_FLU_MOD_MM), FALSE); if ((CLI_PRESENT == cli_present("WRITES_PER_FLUSH")) && (cli_get_int("WRITES_PER_FLUSH", &x))) cs_data->n_wrt_per_flu = x; if ((CLI_PRESENT == cli_present("TRIGGER_FLUSH")) && (cli_get_int("TRIGGER_FLUSH", &x))) cs_data->flush_trigger = cs_data->flush_trigger_top = x; if ((CLI_PRESENT == cli_present("GOT2V5ONCE")) && (cli_get_int("GOT2V5ONCE", &x))) cs_data->db_got_to_v5_once = (boolean_t)x; change_fhead_timer("STALENESS_TIMER", cs_data->staleness, 5000, TRUE); change_fhead_timer("TICK_INTERVAL", cs_data->ccp_tick_interval, 100, TRUE); change_fhead_timer("QUANTUM_INTERVAL", cs_data->ccp_quantum_interval, 1000, FALSE); change_fhead_timer("RESPONSE_INTERVAL", cs_data->ccp_response_interval, 60000, FALSE); if ((CLI_PRESENT == cli_present("B_BYTESTREAM")) && (cli_get_hex64("B_BYTESTREAM", &tn))) cs_data->last_inc_backup = tn; if ((CLI_PRESENT == cli_present("B_COMPREHENSIVE")) && (cli_get_hex64("B_COMPREHENSIVE", &tn))) cs_data->last_com_backup = tn; if ((CLI_PRESENT == cli_present("B_DATABASE")) && (cli_get_hex64("B_DATABASE", &tn))) cs_data->last_com_backup = tn; if ((CLI_PRESENT == cli_present("B_INCREMENTAL")) && (cli_get_hex64("B_INCREMENTAL", &tn))) cs_data->last_inc_backup = tn; if ((CLI_PRESENT == cli_present("WAIT_DISK")) && (cli_get_int("WAIT_DISK", &x))) cs_data->wait_disk_space = (x >= 0 ? x : 0); if (((CLI_PRESENT == cli_present("HARD_SPIN_COUNT")) && cli_get_int("HARD_SPIN_COUNT", &x)) UNIX_ONLY( || ((CLI_PRESENT == cli_present("MUTEX_HARD_SPIN_COUNT")) && cli_get_int("MUTEX_HARD_SPIN_COUNT", &x))) ) /* Unix should be backward compatible, accept MUTEX_ prefix qualifiers as well */ { if (0 < x) cs_data->mutex_spin_parms.mutex_hard_spin_count = x; else util_out_print("Error: HARD SPIN COUNT should be a non zero positive number", TRUE); } if (((CLI_PRESENT == cli_present("SLEEP_SPIN_COUNT")) && cli_get_int("SLEEP_SPIN_COUNT", &x)) UNIX_ONLY( || ((CLI_PRESENT == cli_present("MUTEX_SLEEP_SPIN_COUNT")) && cli_get_int("MUTEX_SLEEP_SPIN_COUNT", &x))) ) /* Unix should be backward compatible, accept MUTEX_ prefix qualifiers as well */ { if (0 <= x) SLEEP_SPIN_CNT(cs_data) = x; else util_out_print("Error: SLEEP SPIN COUNT should be a non zero positive number", TRUE); } if (((CLI_PRESENT == cli_present("SPIN_SLEEP_MASK")) && cli_get_hex("SPIN_SLEEP_MASK", (uint4 *)&x)) UNIX_ONLY( || ((CLI_PRESENT == cli_present("MUTEX_SPIN_SLEEP_MASK")) && cli_get_hex("MUTEX_SPIN_SLEEP_MASK", (uint4 *)&x))) ) /* Unix should be backward compatible, accept MUTEX_ prefix qualifiers as well */ { if (x < 0) util_out_print("Error: SPIN SLEEP MASK should be less than 0x3FFFFFFF, permitting sleep just over a second", TRUE); else SPIN_SLEEP_MASK(cs_data) = x; } UNIX_ONLY( if ((CLI_PRESENT == cli_present("COMMITWAIT_SPIN_COUNT")) && cli_get_int("COMMITWAIT_SPIN_COUNT", &x)) { if (0 <= x) cs_data->wcs_phase2_commit_wait_spincnt = x; else util_out_print("Error: COMMITWAIT SPIN COUNT should be a positive number", TRUE); } ) if ((CLI_PRESENT == cli_present("B_RECORD")) && (cli_get_hex64("B_RECORD", &tn))) cs_data->last_rec_backup = tn; if ((CLI_PRESENT == cli_present("BLKS_TO_UPGRADE")) && (cli_get_hex("BLKS_TO_UPGRADE", (uint4 *)&x))) { cs_data->blks_to_upgrd = x; cs_data->fully_upgraded = FALSE; } if ((CLI_PRESENT == cli_present("MBM_SIZE")) && (cli_get_int("MBM_SIZE", &x))) { if (x < DIVIDE_ROUND_UP(BLK_ZERO_OFF(cs_data->start_vbn) - SGMNT_HDR_LEN, DISK_BLOCK_SIZE)) { /* if the start_vbn needs fixing, do that first */ cs_data->master_map_len = x * DISK_BLOCK_SIZE; cs_data->free_space = BLK_ZERO_OFF(cs_data->start_vbn) - (SGMNT_HDR_LEN + x); } else { x = DIVIDE_ROUND_UP(BLK_ZERO_OFF(cs_data->start_vbn) - SGMNT_HDR_LEN, DISK_BLOCK_SIZE); util_out_print("Error: Master map size more than !UL [0x!XL] would overrun the starting VBN", TRUE, x, x); } } if (cs_data->clustered) { if (cs_addrs->ti->curr_tn == prev_tn) { CHECK_TN(cs_addrs, cs_data, cs_addrs->ti->curr_tn);/* can issue rts_error TNTOOLARGE */ cs_addrs->ti->early_tn++; INCREMENT_CURR_TN(cs_data); } } if ((CLI_PRESENT == cli_present("RC_SRV_COUNT")) && (cli_get_int("RC_SRV_COUNT", &x))) cs_data->rc_srv_cnt = x; if (CLI_PRESENT == cli_present("FREEZE")) { x = cli_t_f_n("FREEZE"); if (1 == x) { while (REG_ALREADY_FROZEN == region_freeze(gv_cur_region, TRUE, override, FALSE, FALSE, FALSE)) { hiber_start(1000); if (util_interrupt) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) ERR_FREEZECTRL); break; } } } else if (0 == x) { if (REG_ALREADY_FROZEN == region_freeze(gv_cur_region, FALSE, override, FALSE, FALSE, FALSE)) { util_out_print("Region: !AD is frozen by another user, not releasing freeze.", TRUE, REG_LEN_STR(gv_cur_region)); } } if (x != !(FROZEN(cs_data))) util_out_print("Region !AD is now !AD", TRUE, REG_LEN_STR(gv_cur_region), LEN_AND_STR(freeze_msg[x])); cs_addrs->persistent_freeze = x; /* secshr_db_clnup() shouldn't clear the freeze up */ } if (CLI_PRESENT == cli_present("FULLY_UPGRADED") && cli_get_int("FULLY_UPGRADED", &x)) { cs_data->fully_upgraded = (boolean_t)x; if (x) cs_data->db_got_to_v5_once = TRUE; } if (CLI_PRESENT == cli_present("GVSTATSRESET")) CLRGVSTATS(cs_addrs); if (CLI_PRESENT == cli_present("ONLINE_NBB")) { buf_len = SIZEOF(buf); if (cli_get_str("ONLINE_NBB", buf, &buf_len)) { lower_to_upper((uchar_ptr_t)buf, (uchar_ptr_t)buf, buf_len); if (0 == STRCMP(buf, "NOT_IN_PROGRESS")) cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS; else { GET_VALUE_INTO_X; if (-1 > x) util_out_print("Invalid value for online_nbb qualifier", TRUE); else cs_addrs->nl->nbb = x; } } } if (CLI_PRESENT == cli_present("ABANDONED_KILLS")) { buf_len = SIZEOF(buf); if (cli_get_str("ABANDONED_KILLS", buf, &buf_len)) { lower_to_upper((uchar_ptr_t)buf, (uchar_ptr_t)buf, buf_len); if (0 == STRCMP(buf, "NONE")) cs_data->abandoned_kills = 0; else { GET_VALUE_INTO_X; if (-1 > x) util_out_print("Invalid value for abandoned_kills qualifier", TRUE); else cs_data->abandoned_kills = x; } } } if (CLI_PRESENT == cli_present("KILL_IN_PROG")) { buf_len = SIZEOF(buf); if (cli_get_str("KILL_IN_PROG", buf, &buf_len)) { lower_to_upper((uchar_ptr_t)buf, (uchar_ptr_t)buf, buf_len); if (0 == STRCMP(buf, "NONE")) cs_data->kill_in_prog = 0; else { GET_VALUE_INTO_X; if (-1 > x) util_out_print("Invalid value for kill_in_prog qualifier", TRUE); else cs_data->kill_in_prog = x; } } } if (CLI_PRESENT == cli_present("MACHINE_NAME")) { buf_len = SIZEOF(buf); if (cli_get_str("MACHINE_NAME", buf, &buf_len)) { lower_to_upper((uchar_ptr_t)buf, (uchar_ptr_t)buf, buf_len); if (0 == STRCMP(buf, "CURRENT")) { memset(cs_data->machine_name, 0, MAX_MCNAMELEN); GETHOSTNAME(cs_data->machine_name, MAX_MCNAMELEN, gethostname_res); } else if (0 == STRCMP(buf, "CLEAR")) memset(cs_data->machine_name, 0, MAX_MCNAMELEN); else util_out_print("Invalid value for the machine_name qualifier", TRUE); } else util_out_print("Error: cannot get value for !AD.", TRUE, LEN_AND_LIT("MACHINE_NAME")); } if (CLI_PRESENT == cli_present("ENCRYPTION_HASH")) { if (1 < cs_addrs->nl->ref_cnt) { util_out_print("Cannot reset encryption hash in file header while !XL other processes are " "accessing the database.", TRUE, cs_addrs->nl->ref_cnt - 1); return; } fname_ptr = (char *)gv_cur_region->dyn.addr->fname; fname_len = gv_cur_region->dyn.addr->fname_len; ASSERT_ENCRYPTION_INITIALIZED; /* Now generate the new hash to be placed in the database file header. */ GTMCRYPT_HASH_GEN(cs_addrs, fname_len, fname_ptr, 0, NULL, hash_buff, gtmcrypt_errno); if (0 != gtmcrypt_errno) GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, fname_len, fname_ptr); memcpy(cs_data->encryption_hash, hash_buff, GTMCRYPT_HASH_LEN); DEBUG_ONLY(GTMCRYPT_HASH_CHK(cs_addrs, cs_data->encryption_hash, fname_len, fname_ptr, gtmcrypt_errno)); assert(0 == gtmcrypt_errno); } # ifdef UNIX if (CLI_PRESENT == cli_present("JNL_YIELD_LIMIT") && cli_get_int("JNL_YIELD_LIMIT", &x)) { if (0 > x) util_out_print("YIELD_LIMIT cannot be NEGATIVE", TRUE); else if (MAX_YIELD_LIMIT < x) util_out_print("YIELD_LIMIT cannot be greater than !UL", TRUE, MAX_YIELD_LIMIT); else cs_data->yield_lmt = x; } if (CLI_PRESENT == cli_present("QDBRUNDOWN")) { cs_data->mumps_can_bypass = TRUE; util_out_print("Database file !AD now has quick database rundown flag set to TRUE", TRUE, DB_LEN_STR(gv_cur_region)); } else if (CLI_NEGATED == cli_present("QDBRUNDOWN")) { cs_data->mumps_can_bypass = FALSE; util_out_print("Database file !AD now has quick database rundown flag set to FALSE", TRUE, DB_LEN_STR(gv_cur_region)); } if (CLI_PRESENT == cli_present("EPOCHTAPER")) { cs_data->epoch_taper = TRUE; util_out_print("Database file !AD now has epoch taper flag set to TRUE", TRUE, DB_LEN_STR(gv_cur_region)); } else if (CLI_NEGATED == cli_present("EPOCHTAPER")) { cs_data->epoch_taper = FALSE; util_out_print("Database file !AD now has epoch taper flag set to FALSE", TRUE, DB_LEN_STR(gv_cur_region)); } if (CLI_PRESENT == cli_present("PROBLKSPLIT")) { cli_get_int("PROBLKSPLIT", &x); if (0 <= x) { cs_data->problksplit = x; util_out_print("Database file !AD now has proactive block split flag set to !12UL", TRUE, DB_LEN_STR(gv_cur_region), cs_data->problksplit); } else util_out_print("Error: PROBLKSPLIT should be an integer of zero (0) or greater", TRUE); } # endif if (CLI_PRESENT == cli_present(UNIX_ONLY("JNL_SYNCIO") VMS_ONLY("JNL_CACHE"))) { x = cli_t_f_n(UNIX_ONLY("JNL_SYNCIO") VMS_ONLY("JNL_CACHE")); if (1 == x) cs_data->jnl_sync_io = UNIX_ONLY(TRUE) VMS_ONLY(FALSE); else if (0 == x) cs_data->jnl_sync_io = UNIX_ONLY(FALSE) VMS_ONLY(TRUE); } if ((CLI_PRESENT == cli_present("AVG_BLKS_READ")) && (cli_get_int("AVG_BLKS_READ", &x))) { if (x <= 0) util_out_print("Invalid value for AVG_BLKS_READ qualifier", TRUE); else cs_data->avg_blks_per_100gbl = x; } if ((CLI_PRESENT == cli_present("PRE_READ_TRIGGER_FACTOR")) && (cli_get_int("PRE_READ_TRIGGER_FACTOR", &x))) { if ((x < 0) || (x > 100)) util_out_print("Invalid value for PRE_READ_TRIGGER_FACTOR qualifier", TRUE); else cs_data->pre_read_trigger_factor = x; } if ((CLI_PRESENT == cli_present("UPD_RESERVED_AREA")) && (cli_get_int("UPD_RESERVED_AREA", &x))) { if ((x < 0) || (x > 100)) util_out_print("Invalid value for UPD_RESERVED_AREA qualifier", TRUE); else cs_data->reserved_for_upd = x; } if ((CLI_PRESENT == cli_present("UPD_WRITER_TRIGGER_FACTOR")) && (cli_get_int("UPD_WRITER_TRIGGER_FACTOR", &x))) { if ((x < 0) || (x > 100)) util_out_print("Invalid value for UPD_WRITER_TRIGGER_FACTOR qualifier", TRUE); else cs_data->writer_trigger_factor = x; } DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return; } fis-gtm-V7.0-005/sr_port/dse_chng_rhead.c0000644000032200000250000001025714342376331017131 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "gdscc.h" #include "cli.h" #include "copy.h" #include "filestruct.h" #include "jnl.h" #include "skan_offset.h" #include "skan_rnum.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" #include "t_write.h" #include "t_end.h" #include "t_begin_crit.h" #include "gvcst_blk_build.h" #include "util.h" #include "t_abort.h" #include "gtmmsg.h" GBLREF char *update_array, *update_array_ptr; GBLREF cw_set_element cw_set[]; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF srch_hist dummy_hist; GBLREF unsigned short patch_comp_count; GBLREF uint4 update_array_size; error_def(ERR_AIMGBLKFAIL); error_def(ERR_DBRDONLY); error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEFAIL); void dse_chng_rhead(void) { blk_segment *bs1, *bs_ptr; block_id blk; boolean_t chng_rec; int4 blk_seg_cnt, blk_size; rec_hdr new_rec; sm_uc_ptr_t bp, b_top, cp, rp; srch_blk_status blkhist; uint4 x; if (gv_cur_region->read_only) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ if (BADDSEBLK == (blk = dse_getblk("BLOCK", DSENOBML, DSEBLKCUR))) /* WARNING: assignment */ return; t_begin_crit(ERR_DSEFAIL); blkhist.blk_num = blk; if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); bp = blkhist.buffaddr; blk_size = cs_addrs->hdr->blk_size; chng_rec = FALSE; b_top = bp + ((blk_hdr_ptr_t)bp)->bsiz; if (((blk_hdr_ptr_t)bp)->bsiz > blk_size || ((blk_hdr_ptr_t)bp)->bsiz < SIZEOF(blk_hdr)) chng_rec = TRUE; /* force rewrite to correct size */ if (CLI_PRESENT == cli_present("RECORD")) { if (!(rp = skan_rnum(bp, FALSE))) { t_abort(gv_cur_region, cs_addrs); return; } } else if (!(rp = skan_offset(bp, FALSE))) { t_abort(gv_cur_region, cs_addrs); return; } GET_SHORT(new_rec.rsiz, &((rec_hdr_ptr_t)rp)->rsiz); SET_CMPC(&new_rec, EVAL_CMPC((rec_hdr_ptr_t)rp)); if (CLI_PRESENT == cli_present("CMPC")) { if (!cli_get_hex("CMPC", &x)) { t_abort(gv_cur_region, cs_addrs); return; } if (x >= MAX_KEY_SZ) { util_out_print("Error: invalid cmpc.",TRUE); t_abort(gv_cur_region, cs_addrs); return; } if (x > patch_comp_count) util_out_print("Warning: specified compression count is larger than the current expanded key size.", TRUE); SET_CMPC(&new_rec, x); chng_rec = TRUE; } if (CLI_PRESENT == cli_present("RSIZ")) { if (!cli_get_hex("RSIZ", &x)) { t_abort(gv_cur_region, cs_addrs); return; } if (x < SIZEOF(rec_hdr) || x > blk_size) { util_out_print("Error: invalid rsiz.", TRUE); t_abort(gv_cur_region, cs_addrs); return; } new_rec.rsiz = x; chng_rec = TRUE; } if (chng_rec) { BLK_INIT(bs_ptr, bs1); cp = bp; cp += SIZEOF(blk_hdr); if (chng_rec) { BLK_SEG(bs_ptr, cp, rp - cp); BLK_SEG(bs_ptr, (uchar_ptr_t)&new_rec, SIZEOF(rec_hdr)); cp = rp + SIZEOF(rec_hdr); } if (b_top - cp) BLK_SEG(bs_ptr, cp, b_top - cp); if (!BLK_FINI(bs_ptr, bs1)) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_AIMGBLKFAIL, 3, &blk, DB_LEN_STR(gv_cur_region)); t_abort(gv_cur_region, cs_addrs); return; } t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)bp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); BUILD_AIMG_IF_JNL_ENABLED(cs_data, cs_addrs->ti->curr_tn); t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); } return; } fis-gtm-V7.0-005/sr_port/dse_crit.c0000644000032200000250000001436714342376331016016 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "cli.h" #include "lockconst.h" #include "wcs_recover.h" #include "dse.h" #include "tp_change_reg.h" /* for tp_change_reg() prototype */ #ifdef UNIX #include "mutex.h" #endif #include "util.h" GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF uint4 process_id; GBLREF short crash_count; GBLREF gd_addr *original_header; error_def(ERR_DBRDONLY); void dse_crit(void) { int util_len, dse_crit_count; char util_buff[MAX_UTIL_LEN]; boolean_t crash = FALSE, cycle = FALSE, owner = FALSE; gd_region *save_region, *r_local, *r_top; crash = ((cli_present("CRASH") == CLI_PRESENT) || (cli_present("RESET") == CLI_PRESENT)); cycle = (CLI_PRESENT == cli_present("CYCLE")); if (cli_present("SEIZE") == CLI_PRESENT || cycle) { if (gv_cur_region->read_only && !cycle) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); if (cs_addrs->now_crit) { util_out_print("!/Write critical section already seized.!/", TRUE); return; } UPDATE_CRASH_COUNT(cs_addrs, crash_count); grab_crit_encr_cycle_sync(gv_cur_region, WS_58); cs_addrs->hold_onto_crit = TRUE; /* need to do this AFTER grab_crit */ cs_addrs->dse_crit_seize_done = TRUE; util_out_print("!/Seized write critical section.!/", TRUE); if (!cycle) return; } if (cli_present("RELEASE") == CLI_PRESENT || cycle) { if (gv_cur_region->read_only && !cycle) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); if (!cs_addrs->now_crit) { util_out_print("!/Critical section already released.!/", TRUE); return; } UPDATE_CRASH_COUNT(cs_addrs, crash_count); if (cs_addrs->now_crit) { /* user wants crit to be released unconditionally so "was_crit" not checked like everywhere else */ assert(cs_addrs->hold_onto_crit && cs_addrs->dse_crit_seize_done); cs_addrs->dse_crit_seize_done = FALSE; cs_addrs->hold_onto_crit = FALSE; /* need to do this before the rel_crit */ rel_crit(gv_cur_region); util_out_print("!/Released write critical section.!/", TRUE); } # ifdef DEBUG else assert(!cs_addrs->hold_onto_crit && !cs_addrs->dse_crit_seize_done); # endif return; } if (cli_present("INIT") == CLI_PRESENT) { if (gv_cur_region->read_only) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); cs_addrs->hdr->image_count = 0; # ifdef CRIT_USE_PTHREAD_MUTEX crash = TRUE; # endif gtm_mutex_init(gv_cur_region, NUM_CRIT_ENTRY(cs_addrs->hdr), crash); cs_addrs->nl->in_crit = 0; cs_addrs->now_crit = FALSE; util_out_print("!/Reinitialized critical section.!/", TRUE); return; } if (cli_present("REMOVE") == CLI_PRESENT) { if (gv_cur_region->read_only) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); if (cs_addrs->nl->in_crit == 0) { util_out_print("!/The write critical section is unowned!/", TRUE); return; } # ifndef CRIT_USE_PTHREAD_MUTEX assert(LOCK_AVAILABLE != cs_addrs->critical->semaphore.u.parts.latch_pid); # endif cs_addrs->now_crit = TRUE; cs_addrs->nl->in_crit = process_id; UPDATE_CRASH_COUNT(cs_addrs, crash_count); /* user wants crit to be removed unconditionally so "was_crit" not checked (before rel_crit) like everywhere else */ if (dba_bg == cs_addrs->hdr->acc_meth) { wcs_recover(gv_cur_region); /* In case, this crit was obtained through a CRIT -SEIZE, csa->hold_onto_crit would have been set to * TRUE. Set that back to FALSE now that we are going to release control of crit. */ cs_addrs->hold_onto_crit = FALSE; /* need to do this before the rel_crit */ cs_addrs->dse_crit_seize_done = FALSE; rel_crit(gv_cur_region); util_out_print("!/Removed owner of write critical section!/", TRUE); } else { /* In case, this crit was obtained through a CRIT -SEIZE, csa->hold_onto_crit would have been set to * TRUE. Set that back to FALSE now that we are going to release control of crit. */ cs_addrs->hold_onto_crit = FALSE; /* need to do this before the rel_crit */ cs_addrs->dse_crit_seize_done = FALSE; rel_crit(gv_cur_region); util_out_print("!/Removed owner of write critical section!/", TRUE); util_out_print("!/WARNING: No recovery because database is MM.!/", TRUE); } return; } if (crash) { # ifndef CRIT_USE_PTHREAD_MUTEX memcpy(util_buff, "!/Critical section crash count is ", 34); util_len = 34; util_len += i2hex_nofill(cs_addrs->critical->crashcnt, (uchar_ptr_t)&util_buff[util_len], 8); memcpy(&util_buff[util_len], "!/", 2); util_len += 2; util_buff[util_len] = 0; util_out_print(util_buff, TRUE); # endif return; } if (cli_present("ALL") == CLI_PRESENT) { dse_crit_count = 0; save_region = gv_cur_region; for (r_local = original_header->regions, r_top = r_local + original_header->n_regions; r_local < r_top; r_local++) { if (!r_local->open || r_local->was_open) continue; gv_cur_region = r_local; tp_change_reg(); if (cs_addrs->nl->in_crit) { dse_crit_count++; util_out_print("Database !AD : CRIT Owned by pid [!UL]", TRUE, DB_LEN_STR(gv_cur_region), cs_addrs->nl->in_crit); } } if (0 == dse_crit_count) util_out_print("CRIT is currently unowned on all regions", TRUE); gv_cur_region = save_region; tp_change_reg(); return; } if (cs_addrs->nl->in_crit) { util_out_print("!/Write critical section owner is process id !UL", TRUE, cs_addrs->nl->in_crit); if (cs_addrs->now_crit) util_out_print("DSE (process id: !UL) owns the write critical section", TRUE, process_id); util_out_print(0, TRUE); } else util_out_print("!/Write critical section is currently unowned", TRUE); return; } fis-gtm-V7.0-005/sr_port/dse_data.c0000755000032200000250000000315014342376331015755 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "cli.h" #include "dse.h" int dse_data(char *dst, int *len) { unsigned short cli_len; char buf[MAX_LINE],*src,*bot,*top; cli_len = SIZEOF(buf); if (!cli_get_str("DATA",buf,&cli_len)) return FALSE; bot = dst; top = &buf[cli_len - 1]; src = &buf[0]; #ifdef VMS if (buf[0] == '"') src = &buf[1]; #endif for (; src <= top ;src++) { #ifdef VMS if (src == top && *src == '"') break; #endif if (*src == '\\') { src++; if (*src == '\\') { *dst++ = '\\'; continue; } if (*src >= '0' && *src <= '9') *dst = *src - '0'; else if (*src >= 'a' && *src <= 'f') *dst = *src - 'a' + 10; else if (*src >= 'A' && *src <= 'F') *dst = *src - 'A' +10; else continue; src++; if (*src >= '0' && *src <= '9') *dst = (*dst << 4) + *src - '0'; else if (*src >= 'a' && *src <= 'f') *dst = (*dst << 4) + *src - 'a' + 10; else if (*src >= 'A' && *src <= 'F') *dst = (*dst << 4) + *src - 'A' +10; dst++; } else *dst++ = *src; } *len = (int)(dst - bot); return TRUE; } fis-gtm-V7.0-005/sr_port/dse_dmp.c0000755000032200000250000000506514342376331015633 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "cli.h" #include "dse.h" #include "error.h" #include "util.h" GBLDEF enum dse_fmt dse_dmp_format = CLOSED_FMT; GBLREF boolean_t patch_is_fdmp; GBLREF int patch_fdmp_recs; #define MESS_OFF SIZEOF("; ") - 1 CONDITION_HANDLER(dse_dmp_handler) { START_CH(TRUE); PRN_ERROR; util_out_print("!/DSE is not able to complete the dump to file due to the above reason.!/", TRUE); UNWIND(NULL,NULL); } static char *format_label[] = {"; BAD", "; GLO", "; ZWR"}; /* CLOSE_FMT == 0, GLO_FMT == 1 and ZWR_FMT == 2 */ void dse_dmp(void) { boolean_t dmp_res, glo_present, zwr_present; patch_fdmp_recs = 0; glo_present = (CLI_PRESENT == cli_present("GLO")); zwr_present = (CLI_PRESENT == cli_present("ZWR")); if (glo_present || zwr_present) { if (CLOSED_FMT == dse_dmp_format) { util_out_print("Error: must open an output file before dump.", TRUE); return; } if (gtm_utf8_mode && (GLO_FMT == glo_present)) { util_out_print("Error: GLO format is not supported in UTF-8 mode. Use ZWR format.", TRUE); return; } if (OPEN_FMT == dse_dmp_format) { dse_dmp_format = (glo_present ? GLO_FMT : ZWR_FMT); if (!gtm_utf8_mode) dse_fdmp_output(LIT_AND_LEN("; DSE EXTRACT")); else dse_fdmp_output(LIT_AND_LEN("; DSE EXTRACT UTF-8")); dse_fdmp_output(STR_AND_LEN(format_label[dse_dmp_format])); } else if ((glo_present ? GLO_FMT : ZWR_FMT) != dse_dmp_format) { util_out_print("Error: current output file already contains !AD records.", TRUE, LEN_AND_STR(&format_label[dse_dmp_format][MESS_OFF])); return; } patch_is_fdmp = TRUE; ESTABLISH(dse_dmp_handler); } else patch_is_fdmp = FALSE; if (CLI_PRESENT == cli_present("RECORD") || CLI_PRESENT == cli_present("OFFSET")) dmp_res = dse_r_dmp(); else dmp_res = dse_b_dmp(); if (patch_is_fdmp) { REVERT; if (dmp_res) util_out_print("!UL !AD records written.!/", TRUE, patch_fdmp_recs, LEN_AND_STR(&format_label[dse_dmp_format][MESS_OFF])); } return; } fis-gtm-V7.0-005/sr_port/dse_dmp_fhead.c0000755000032200000250000007557314342376331016775 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /******************************************************************************* * * MODULE NAME: DSE_DMP_FHEAD * * CALLING SEQUENCE: void dse_dmp_fhead () * * DESCRIPTION: This module dumps certain fields of current file * header. * * HISTORY: * *******************************************************************************/ #include "mdef.h" #include "gtm_string.h" #include /* needed for handling of epoch_interval (EPOCH_SECOND2SECOND macro uses ceil) */ #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "cli.h" #include "util.h" #include "dse.h" #include "dse_puttime.h" #include "gtmmsg.h" #include "stringpool.h" /* for GET_CURR_TIME_IN_DOLLARH_AND_ZDATE macro */ #include "op.h" #include "shmpool.h" /* Needed for the shmpool structures */ #include "mutex.h" #include "db_snapshot.h" #define NEXT_EPOCH_TIME_SPACES " " /* 19 spaces, we have 19 character field width to output Next Epoch Time */ GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF boolean_t dse_all_dump; /* TRUE if DSE ALL -DUMP is specified */ GBLDEF mval dse_dmp_time_fmt = DEFINE_MVAL_STRING(MV_STR, 0, 0, STR_LIT_LEN(DSE_DMP_TIME_FMT), DSE_DMP_TIME_FMT, 0, 0); LITREF char *jrt_label[JRT_RECTYPES]; LITREF char *gtm_dbversion_table[]; #define SHOW_STAT(TEXT, VARIABLE) if (0 != csd->VARIABLE##_cntr) \ util_out_print(TEXT" 0x!XL Transaction = 0x!16@XQ", TRUE, (csd->VARIABLE##_cntr), \ (&csd->VARIABLE##_tn)); #define SHOW_DB_CSH_STAT(csd, COUNTER, TEXT1, TEXT2) \ if (csd->COUNTER.curr_count || csd->COUNTER.cumul_count) \ { \ util_out_print(TEXT1" 0x!XL "TEXT2" 0x!XL", TRUE, (csd->COUNTER.curr_count), \ (csd->COUNTER.cumul_count + csd->COUNTER.curr_count)); \ } #define SHOW_GVSTATS_STAT(cnl, COUNTER, TEXT1, TEXT2) \ { \ if (cnl->gvstats_rec.COUNTER) \ util_out_print(" " TEXT1 " : " TEXT2" 0x!16@XQ", TRUE, (&cnl->gvstats_rec.COUNTER)); \ } /* NEED_TO_DUMP is only for the qualifiers other than "BASIC" and "ALL". file_header is not dumped only if "NOBASIC" is explicitly specified */ #define NEED_TO_DUMP(string) \ (is_dse_all ? (CLI_PRESENT == cli_present("ALL")) \ : (CLI_PRESENT == cli_present(string) || CLI_PRESENT == cli_present("ALL") && CLI_NEGATED != cli_present(string))) void dse_dmp_fhead (void) { boolean_t is_mm, jnl_buff_open; unsigned char util_buff[MAX_UTIL_LEN], buffer[MAXNUMLEN]; int util_len, rectype, time_len, index, activeque_cnt, freeque_cnt, wipque_cnt, i, j; uint4 jnl_status; enum jnl_state_codes jnl_state; gds_file_id zero_fid; mval dollarh_mval, zdate_mval; char dollarh_buffer[MAXNUMLEN], zdate_buffer[SIZEOF(DSE_DMP_TIME_FMT)], fullblockwrite_buffer[255], *k; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; node_local_ptr_t cnl; jnl_private_control *jpc; jnl_buffer_ptr_t jb; shmpool_buff_hdr_ptr_t bptr; boolean_t db_shares_gvstats, is_dse_all; uint4 pid; boolean_t new_line; unsigned char outbuf[GTMCRYPT_HASH_HEX_LEN + 1]; shm_snapshot_t *ss_shm_ptr; time_t backup_start_tmp; struct tm backup_start_tm_tmp; char backup_start_buf[100]; is_dse_all = dse_all_dump; dse_all_dump = FALSE; csa = cs_addrs; csd = csa->hdr; cnl = csa->nl; jnl_state = (enum jnl_state_codes)csd->jnl_state; jnl_buff_open = (0 != cnl->jnl_file.u.inode); activeque_cnt = cnl->wcs_active_lvl; wipque_cnt = cnl->wcs_wip_lvl; freeque_cnt = cnl->wc_in_free; if (is_dse_all || (CLI_NEGATED != cli_present("BASIC"))) { util_out_print("!/File !AD", TRUE, gv_cur_region->dyn.addr->fname_len, &gv_cur_region->dyn.addr->fname[0]); util_out_print("Region !AD", TRUE, gv_cur_region->rname_len, &gv_cur_region->rname[0]); GET_CURR_TIME_IN_DOLLARH_AND_ZDATE(dollarh_mval, dollarh_buffer, zdate_mval, zdate_buffer); util_out_print("Date/Time !AD [$H = !AD]", TRUE, zdate_mval.str.len, zdate_mval.str.addr, dollarh_mval.str.len, dollarh_mval.str.addr); is_mm = (csd->acc_meth == dba_mm); util_out_print(" Access method !AD", FALSE, 2, is_mm ? "MM" : "BG"); util_out_print(" Global Buffers !12UL", TRUE, csd->n_bts); util_out_print(" Reserved Bytes !19UL", FALSE, csd->reserved_bytes); util_out_print(" Block size (in bytes) !12UL", TRUE, csd->blk_size); util_out_print(" Maximum record size !19UL", FALSE, csd->max_rec_size); util_out_print(" Starting VBN !12UL", TRUE, csd->start_vbn); util_out_print(" Maximum key size !19UL", FALSE, csd->max_key_size); util_out_print(" Total blocks 0x!16@XQ", TRUE, &(csa->ti->total_blks)); util_out_print(" Null subscripts !AD", FALSE, 12, (csd->null_subs == ALWAYS) ? " ALWAYS" : (csd->null_subs == ALLOWEXISTING) ? " EXISTING" : " NEVER" ); util_out_print(" Free blocks 0x!16@XQ", TRUE, &(csa->ti->free_blocks)); util_out_print(" Standard Null Collation !AD", FALSE, 11, (csd->std_null_coll) ? " TRUE" : " FALSE"); util_out_print(" Free space 0x!XL", TRUE, csd->free_space); util_out_print(" Last Record Backup 0x!16@XQ", FALSE, &csd->last_rec_backup); util_out_print(" Extension Count !12UL", TRUE, csd->extension_size); util_out_print(" Last Database Backup 0x!16@XQ", FALSE, &csd->last_com_backup); /* Eventually the number of local maps field will have to be extended to accommodate the increased * DB capacity */ if (csd->bplmap > 0) util_out_print(" Number of local maps !12UL", TRUE, (csa->ti->total_blks + csd->bplmap - 1) / csd->bplmap); else util_out_print(" Number of local maps ??", TRUE); util_out_print(" Last Bytestream Backup 0x!16@XQ", FALSE, &csd->last_inc_backup); util_out_print(" Lock space 0x!XL", TRUE, csd->lock_space_size / OS_PAGELET_SIZE); util_out_print(" In critical section 0x!XL", FALSE, cnl->in_crit); util_out_print(" Timers pending !12UL", TRUE, cnl->wcs_timers + 1); if (FROZEN_BY_ROOT == csd->freeze) util_out_print(" Cache freeze id FROZEN BY ROOT", FALSE); else util_out_print(" Cache freeze id 0x!XL", FALSE, (csd->freeze)? csd->freeze : 0); dse_puttime(csd->flush_time, " Flush timer !AD", TRUE); util_out_print(" Freeze match 0x!XL", FALSE, csd->image_count ? csd->image_count : 0); util_out_print(" Flush trigger !12UL", TRUE, csd->flush_trigger); util_out_print(" Freeze online !AD", FALSE, 11, (cnl->freeze_online) ? " TRUE" : " FALSE"); util_out_print(" Freeze online autorelease !AD", TRUE, 5, (cnl->freeze_online & CHILLED_AUTORELEASE_MASK) ? " TRUE" : "FALSE"); util_out_print(" Current transaction 0x!16@XQ", FALSE, &csa->ti->curr_tn); util_out_print(" No. of writes/flush !12UL", TRUE, csd->n_wrt_per_flu); util_out_print(" Maximum TN 0x!16@XQ", FALSE, &csd->max_tn); if (GDSVLAST > csd->certified_for_upgrade_to) util_out_print(" Certified for Upgrade to !AD", TRUE, LEN_AND_STR(gtm_dbversion_table[csd->certified_for_upgrade_to])); else /* out of range so print hex */ util_out_print(" Certified for Upgrade to 0x!XL", TRUE, csd->certified_for_upgrade_to); util_out_print(" Maximum TN Warn 0x!16@XQ", FALSE, &csd->max_tn_warn); if (GDSVLAST > csd->desired_db_format) util_out_print(" Desired DB Format !AD", TRUE, LEN_AND_STR(gtm_dbversion_table[csd->desired_db_format])); else /* out of range so print hex */ util_out_print(" Desired DB Format 0x!XL", TRUE, csd->desired_db_format); util_out_print(" Master Bitmap Size !12UL", FALSE, csd->master_map_len / DISK_BLOCK_SIZE); util_out_print(" Blocks to Upgrade 0x!16@XQ", TRUE, &(csd->blks_to_upgrd)); if (csd->def_coll) { util_out_print(" Default Collation !19UL", FALSE, csd->def_coll); util_out_print(" Collation Version !12UL", TRUE, csd->def_coll_ver); } util_out_print(" Create in progress !AD", FALSE, 12, (csd->createinprogress) ? " TRUE" : " FALSE"); util_out_print(" Modified cache blocks !12UL", TRUE, activeque_cnt); util_out_print(" Reference count !19UL", FALSE, cnl->ref_cnt); util_out_print(" Wait Disk !12UL", TRUE, csd->wait_disk_space); util_out_print(" Journal State !AD", (jnl_notallowed == jnl_state), 13, (jnl_notallowed != jnl_state) ? ((jnl_closed == jnl_state) ? " OFF" : (jnl_buff_open ? " ON" : "[inactive] ON")) : " DISABLED"); if (jnl_notallowed != jnl_state) { util_out_print(" Journal Before imaging !AD", TRUE, 5, (csd->jnl_before_image) ? " TRUE" : "FALSE"); util_out_print(" Journal Allocation !19UL", FALSE, csd->jnl_alq); util_out_print(" Journal Extension !12UL", TRUE, csd->jnl_deq); util_out_print(" Journal Buffer Size !19UL", FALSE, csd->jnl_buffer_size); util_out_print(" Journal Alignsize !12UL", TRUE, csd->alignsize / DISK_BLOCK_SIZE); util_out_print(" Journal AutoSwitchLimit !17UL", FALSE, csd->autoswitchlimit); util_out_print(" Journal Epoch Interval !12UL", TRUE, EPOCH_SECOND2SECOND(csd->epoch_interval)); util_out_print(" Journal Yield Limit !19UL", FALSE, csd->yield_lmt); util_out_print(" Journal Sync IO !AD", TRUE, 5, (csd->jnl_sync_io ? " TRUE" : "FALSE")); util_out_print(" Journal File: !AD", TRUE, JNL_LEN_STR(csd)); } if (BACKUP_NOT_IN_PROGRESS != cnl->nbb) util_out_print(" Online Backup NBB !19UL", TRUE, cnl->nbb); /* Mutex Stuff */ util_out_print(" Mutex Hard Spin Count !19UL", FALSE, csd->mutex_spin_parms.mutex_hard_spin_count); util_out_print(" Mutex Sleep Spin Count !12UL", TRUE, csd->mutex_spin_parms.mutex_sleep_spin_count); util_out_print(" Mutex Queue Slots !19UL", FALSE, NUM_CRIT_ENTRY(csd)); util_out_print(" KILLs in progress !12UL", TRUE, (csd->kill_in_prog + csd->abandoned_kills)); util_out_print(" Replication State !AD", FALSE, 13, (csd->repl_state == repl_closed ? " OFF" : (csd->repl_state == repl_open ? " ON" : " [WAS_ON] OFF"))); util_out_print(" Region Seqno 0x!16@XQ", TRUE, &csd->reg_seqno); util_out_print(" Zqgblmod Seqno 0x!16@XQ", FALSE, &csd->zqgblmod_seqno); util_out_print(" Zqgblmod Trans 0x!16@XQ", TRUE, &csd->zqgblmod_tn); util_out_print(" Endian Format !6AZ", FALSE, ENDIANTHISJUSTIFY); util_out_print(" Commit Wait Spin Count !12UL", TRUE, csd->wcs_phase2_commit_wait_spincnt); util_out_print(" Database file encrypted !AD", FALSE, 5, IS_ENCRYPTED(csd->is_encrypted) ? " TRUE" : "FALSE"); util_out_print(" Inst Freeze on Error !AD", TRUE, 5, csd->freeze_on_fail ? " TRUE" : "FALSE"); util_out_print(" Spanning Node Absent !AD", FALSE, 5, csd->span_node_absent ? " TRUE" : "FALSE"); util_out_print(" Maximum Key Size Assured !AD", TRUE, 5, csd->maxkeysz_assured ? " TRUE" : "FALSE"); util_out_print(" Defer allocation !AD", FALSE, 5, csd->defer_allocate ? " TRUE" : "FALSE"); util_out_print(" Spin sleep time mask 0x!8XL", TRUE, SPIN_SLEEP_MASK(csd)); util_out_print(" Async IO !AD", FALSE, 3, csd->asyncio ? " ON" : "OFF"); assert(!is_mm || (0 == (activeque_cnt + wipque_cnt + freeque_cnt))); util_out_print(" WIP queue cache blocks !12UL", TRUE, wipque_cnt); util_out_print(" DB is auto-created !AD", FALSE, 5, (RDBF_AUTODB & csd->reservedDBFlags) ? " TRUE" : "FALSE"); db_shares_gvstats = ! (RDBF_NOSTATS & csd->reservedDBFlags); util_out_print(" DB shares gvstats !AD", TRUE, 5, db_shares_gvstats ? " TRUE" : "FALSE"); util_out_print(" LOCK shares DB critical section !AD", FALSE, 5, csd->lock_crit_with_db ? " TRUE" : "FALSE"); util_out_print(" Read Only !AD", TRUE, 3, csd->read_only ? " ON" : "OFF"); util_out_print(" Recover interrupted !AD", FALSE, 5, (csd->recov_interrupted ? " TRUE" : "FALSE")); util_out_print(" Full Block Write !UL", TRUE, csd->write_fullblk); if(db_shares_gvstats) util_out_print(" StatsDB Allocation !19UL", TRUE, csd->statsdb_allocation); } if (CLI_PRESENT == cli_present("ALL")) { /* Only dump these if -/ALL as if part of above display */ if (!csd->last_start_backup) /* Handle backup timestamp: 0 == Never backed up */ { strncpy(backup_start_buf, " Never", sizeof(backup_start_buf)); } else { backup_start_tmp = (time_t) (csd->last_start_backup); /* this will 32 squash on AIX */ localtime_r(&backup_start_tmp, &backup_start_tm_tmp); asctime_r(&backup_start_tm_tmp, backup_start_buf); backup_start_buf[strlen(backup_start_buf)-1] = '\0'; } util_out_print(0, TRUE); util_out_print(" ", FALSE); util_out_print(" DB Current Minor Version !4UL", TRUE, csd->minor_dbver); util_out_print(" Blks Last Record Backup 0x!16@XQ", FALSE, &(csd->last_rec_bkup_last_blk)); util_out_print(" Last GT.M Minor Version !4UL", TRUE, csd->last_mdb_ver); util_out_print(" Blks Last Stream Backup 0x!16@XQ", FALSE, &(csd->last_inc_bkup_last_blk)); util_out_print(" DB Creation Version !AD", TRUE, LEN_AND_STR(gtm_dbversion_table[csd->creation_db_ver])); util_out_print(" Blks Last Comprehensive Backup 0x!16@XQ", FALSE, &(csd->last_com_bkup_last_blk)); util_out_print(" DB Creation Minor Version !4UL", TRUE, csd->creation_mdb_ver); util_out_print(" Last Record Backup Start !AD", TRUE, LEN_AND_STR(backup_start_buf)); util_out_print(0, TRUE); util_out_print(" Total Global Buffers 0x!XL", FALSE, csd->n_bts); util_out_print(" Phase2 commit pid count 0x!XL", TRUE, cnl->wcs_phase2_commit_pidcnt); util_out_print(" Dirty Global Buffers 0x!XL", FALSE, activeque_cnt); util_out_print(" Write cache timer count 0x!XL", TRUE, cnl->wcs_timers); new_line = FALSE; for (index = 0; MAX_WT_PID_SLOTS > index; index++) { pid = cnl->wt_pid_array[index]; if (0 != pid) { util_out_print(" wcs_timer pid [!2UL] !AD !14UL", new_line, index, new_line ? 0 : 7, new_line ? "" : " ", pid); new_line = !new_line; } } util_out_print(0, new_line); util_out_print(" Free Global Buffers 0x!XL", FALSE, freeque_cnt); util_out_print(" wcs_wtstart pid count 0x!XL", TRUE, cnl->in_wtstart); util_out_print(" Write Cache is Blocked !AD", FALSE, 5, (cnl->wc_blocked ? " TRUE" : "FALSE")); util_out_print(" wcs_wtstart intent cnt 0x!XL", TRUE, cnl->intent_wtstart); util_out_print(" Write Cache to be recovered !AD", TRUE, 5, ((WC_BLOCK_RECOVER == cnl->wc_blocked) ? " TRUE" : "FALSE")); util_out_print(0, TRUE); util_out_print(" Quick database rundown is active !AD", TRUE, 5, (csd->mumps_can_bypass ? " TRUE" : "FALSE")); util_out_print(" Access control counter halted !AD", FALSE, 5, cnl->access_counter_halted ? " TRUE" : "FALSE"); util_out_print(" FTOK counter halted !AD", TRUE, 5, cnl->ftok_counter_halted ? " TRUE" : "FALSE"); util_out_print(" Access control rundown bypasses !9UL", FALSE, cnl->dbrndwn_access_skip); util_out_print(" FTOK rundown bypasses !10UL", TRUE, cnl->dbrndwn_ftok_skip); util_out_print(" Epoch taper !AD", FALSE, 5, (csd->epoch_taper ? " TRUE" : "FALSE")); util_out_print(" Proactive Block Splitting !12UL", TRUE, csd->problksplit); new_line = FALSE; for (index = 0; MAX_WTSTART_PID_SLOTS > index; index++) { pid = cnl->wtstart_pid[index]; if (0 != pid) { util_out_print(" wcs_wtstart pid [!2UL] !AD !12UL", new_line, index, new_line ? 0 : 7, new_line ? "" : " ", pid); new_line = !new_line; } } /* Additional information regarding kills that are in progress, abandoned and inhibited */ util_out_print(0, TRUE); util_out_print(" Actual kills in progress !12UL", FALSE, csd->kill_in_prog); util_out_print(" Abandoned Kills !12UL", TRUE, csd->abandoned_kills); util_out_print(" Process(es) inhibiting KILLs !5UL", FALSE, cnl->inhibit_kills); util_out_print(" DB is a StatsDB !AD", TRUE, 5, IS_RDBF_STATSDB(csd) ? " TRUE" : "FALSE"); util_out_print(0, TRUE); util_out_print(" DB Trigger cycle of ^#t !12UL", TRUE, csd->db_trigger_cycle); util_out_print(0, TRUE); util_out_print(" MM defer_time !5SL", TRUE, csd->defer_time); /* Print various database encryption information */ util_out_print(0, TRUE); util_out_print(" DB is (re)encryptable !AD", TRUE, 5, TO_BE_ENCRYPTED(csd->is_encrypted) ? " TRUE" : "FALSE"); util_out_print(" DB encryption null IV mode !AD", TRUE, 5, (csd->non_null_iv ? "FALSE" : " TRUE")); util_out_print(" DB encryption hash cutoff !12SL", TRUE, csd->encryption_hash_cutoff); util_out_print(" DB encr hash2 start TN 0x!16@XQ", TRUE, &csd->encryption_hash2_start_tn); GET_HASH_IN_HEX(csd->encryption_hash, outbuf, GTMCRYPT_HASH_HEX_LEN); util_out_print(" Database file encryption hash !AD", TRUE, GTMCRYPT_HASH_HEX_LEN, outbuf); GET_HASH_IN_HEX(csd->encryption_hash2, outbuf, GTMCRYPT_HASH_HEX_LEN); util_out_print(" Database file encryption hash2 !AD", TRUE, GTMCRYPT_HASH_HEX_LEN, outbuf); } if (NEED_TO_DUMP("SUPPLEMENTARY")) { util_out_print(0, TRUE); assert(MAX_SUPPL_STRMS == ARRAYSIZE(csd->strm_reg_seqno)); for (index = 0; index < MAX_SUPPL_STRMS; index++) { if (csd->strm_reg_seqno[index]) util_out_print(" Stream !2UL: Reg Seqno 0x!16@XQ", TRUE, index, &csd->strm_reg_seqno[index]); } } if (NEED_TO_DUMP("ENVIRONMENT")) { util_out_print(" Full Block Write Len !12UL", TRUE, csa->fullblockwrite_len); } if (NEED_TO_DUMP("DB_CSH")) { util_out_print(0, TRUE); # define TAB_DB_CSH_ACCT_REC(COUNTER,TEXT1,TEXT2) SHOW_DB_CSH_STAT(csd, COUNTER, TEXT1, TEXT2) # include "tab_db_csh_acct_rec.h" # undef TAB_DB_CSH_ACCT_REC } if (NEED_TO_DUMP("GVSTATS")) { util_out_print(0, TRUE); # define TAB_GVSTATS_REC(COUNTER,TEXT1,TEXT2) SHOW_GVSTATS_STAT(cnl, COUNTER, TEXT1, TEXT2) # include "tab_gvstats_rec.h" # undef TAB_GVSTATS_REC } if (NEED_TO_DUMP("TPBLKMOD")) { util_out_print(0, TRUE); assert(n_tp_blkmod_types < ARRAYSIZE(csd->tp_cdb_sc_blkmod)); util_out_print(" TP blkmod nomod !12UL", TRUE, csd->tp_cdb_sc_blkmod[tp_blkmod_nomod]); util_out_print(" TP blkmod gvcst_srch !12UL", TRUE, csd->tp_cdb_sc_blkmod[tp_blkmod_gvcst_srch]); util_out_print(" TP blkmod t_qread !12UL", TRUE, csd->tp_cdb_sc_blkmod[tp_blkmod_t_qread]); util_out_print(" TP blkmod tp_tend !12UL", TRUE, csd->tp_cdb_sc_blkmod[tp_blkmod_tp_tend]); util_out_print(" TP blkmod tp_hist !12UL", TRUE, csd->tp_cdb_sc_blkmod[tp_blkmod_tp_hist]); } if (NEED_TO_DUMP("BG_TRC")) { util_out_print(0, TRUE); /* print out all the BG_TRACE accounting fields */ # define TAB_BG_TRC_REC(A,B) SHOW_STAT(A,B); # include "tab_bg_trc_rec.h" # undef TAB_BG_TRC_REC } jpc = csa->jnl; if (NEED_TO_DUMP("JOURNAL") && (JNL_ENABLED(csd) && (NULL != jpc) && (NULL != jpc->jnl_buff))) { jb = jpc->jnl_buff; util_out_print(0, TRUE); /* --------------------------- journal buffer --------------------------------- */ util_out_print(" Jnl Buffer Size 0x!XL", FALSE, jb->size); util_out_print(" ", FALSE); util_out_print(" Dskaddr 0x!XL", TRUE, jb->dskaddr); util_out_print(" Free 0x!XL", FALSE, jb->free); util_out_print(" ", FALSE); util_out_print(" Freeaddr 0x!XL", TRUE, jb->freeaddr); util_out_print(" Dsk 0x!XL", FALSE, jb->dsk); util_out_print(" ", FALSE); util_out_print(" Wrtsize 0x!XL", TRUE, jb->wrtsize); util_out_print(" Journal checksum seed 0x!XL", FALSE, csd->jnl_checksum); util_out_print(" ", FALSE); util_out_print(" Min_write_size 0x!XL", TRUE, jb->min_write_size); util_out_print(" bytcnt 0x!XL", FALSE, jb->bytcnt); util_out_print(" ", FALSE); util_out_print(" Max_write_size 0x!XL", TRUE, jb->max_write_size); util_out_print(" Reserved Free 0x!XL", FALSE, jb->rsrv_free); util_out_print(" ", FALSE); util_out_print(" Reserved Freeaddr 0x!XL", TRUE, jb->rsrv_freeaddr); util_out_print(" Alignsize 0x!XL", FALSE, jb->alignsize); util_out_print(" ", FALSE); util_out_print(" Next Align Rec Addr 0x!XL", TRUE, jb->next_align_addr); util_out_print(" Phase2 Commit Index1 0x!XL", FALSE, jb->phase2_commit_index1); util_out_print(" ", FALSE); util_out_print(" Phase2 Commit Index2 0x!XL", TRUE, jb->phase2_commit_index2); util_out_print(" Phase2 Commit MaxIndex 0x!XL", FALSE, JNL_PHASE2_COMMIT_ARRAY_SIZE); util_out_print(" ", FALSE); util_out_print(" Phase2Commit LatchPid !12UL", TRUE, jb->phase2_commit_latch.u.parts.latch_pid); util_out_print(" Before image !AD", FALSE, 5, (jb->before_images ? " TRUE" : "FALSE")); util_out_print(" ", FALSE); util_out_print(" Filesize !12UL", TRUE, jb->filesize); util_out_print(" Iosb.cond !12UW", FALSE, jb->iosb.cond); util_out_print(" ", FALSE); util_out_print(" qiocnt !12UL", TRUE, jb->qiocnt); util_out_print(" Iosb.length 0x!4XW", FALSE, jb->iosb.length); util_out_print(" ", FALSE); util_out_print(" errcnt !12UL", TRUE, jb->errcnt); util_out_print(" Iosb.dev_specific !12UL", FALSE, jb->iosb.dev_specific); util_out_print(" ", FALSE); time_len = exttime(jb->next_epoch_time, (char *)buffer, 0); assert(STR_LIT_LEN(NEXT_EPOCH_TIME_SPACES) >= time_len); util_out_print(" Next Epoch_Time!AD!AD", TRUE, STR_LIT_LEN(NEXT_EPOCH_TIME_SPACES) - time_len + 1, NEXT_EPOCH_TIME_SPACES, time_len - 1, buffer); /* -1 to avoid printing \ at end of $H * format time returned by exttime */ util_out_print(" Blocked Process !12UL", FALSE, jb->blocked); util_out_print(" ", FALSE); util_out_print(" Epoch_tn 0x!16@XQ", TRUE, &jb->epoch_tn); util_out_print(" Io_in_progress !AD", FALSE, 5, (jb->io_in_prog_latch.u.parts.latch_pid ? " TRUE" : "FALSE")); util_out_print(" ", FALSE); util_out_print(" Epoch_Interval !12UL", TRUE, EPOCH_SECOND2SECOND(jb->epoch_interval)); util_out_print(" Now_writer !12UL", FALSE, jb->io_in_prog_latch.u.parts.latch_pid); util_out_print(" ", FALSE); util_out_print(" Image_count !12UL", TRUE, jb->image_count); util_out_print(" fsync_in_prog !AD", FALSE, 5, (jb->fsync_in_prog_latch.u.parts.latch_pid ? " TRUE" : "FALSE")); util_out_print(" ", FALSE); util_out_print(" fsync pid !12SL", TRUE, (jb->fsync_in_prog_latch.u.parts.latch_pid)); util_out_print(" fsync addrs 0x!XL", FALSE, jb->fsync_dskaddr); util_out_print(" ", FALSE); util_out_print(" Need_db_fsync !AD", TRUE, 5, (jb->need_db_fsync ? " TRUE" : "FALSE")); util_out_print(" Filesystem block size 0x!XL", FALSE, jb->fs_block_size); util_out_print(" ", FALSE); util_out_print(" jnl solid tn 0x!16@XQ", TRUE, &csd->jnl_eovtn); for (rectype = JRT_BAD + 1; rectype < JRT_RECTYPES - 1; rectype++) { util_out_print(" Jnl Rec Type !5AZ 0x!XL ", FALSE, jrt_label[rectype], jb->reccnt[rectype]); rectype++; util_out_print(" Jnl Rec Type !5AZ 0x!XL", TRUE, jrt_label[rectype], jb->reccnt[rectype]); } if (rectype != JRT_RECTYPES) util_out_print(" Jnl Rec Type !5AZ 0x!XL", TRUE, jrt_label[rectype], jb->reccnt[rectype]); util_out_print(0, TRUE); util_out_print(" Recover interrupted !AD", FALSE, 5, (csd->recov_interrupted ? " TRUE" : "FALSE")); util_out_print(" ", FALSE); util_out_print(" INTRPT resolve time !12UL", TRUE, csd->intrpt_recov_tp_resolve_time); util_out_print(" INTRPT jnl_state !12UL", FALSE, csd->intrpt_recov_jnl_state); util_out_print(" ", FALSE); util_out_print(" INTRPT repl_state !12UL", TRUE, csd->intrpt_recov_repl_state); util_out_print(" INTRPT seqno 0x!16@XQ", TRUE, &csd->intrpt_recov_resync_seqno); for (index = 0; index < MAX_SUPPL_STRMS; index++) { if (csd->intrpt_recov_resync_strm_seqno[index]) util_out_print(" INTRPT strm_seqno : Stream # !2UL Stream Seqno 0x!16@XQ", TRUE, index, &csd->intrpt_recov_resync_strm_seqno[index]); } for (index = 0; index < MAX_SUPPL_STRMS; index++) { if (csd->intrpt_recov_resync_strm_seqno[index]) util_out_print(" SAVE strm_seqno : Stream # !2UL Region Seqno 0x!16@XQ", TRUE, index, &csd->save_strm_reg_seqno[index]); } } if (NEED_TO_DUMP("BACKUP")) { bptr = csa->shmpool_buffer; /* --------------------------- online backup buffer ---------------------------------- */ util_out_print(0, TRUE); util_out_print(" Free blocks !12UL", FALSE, bptr->free_cnt); //util_out_print(" ", FALSE); util_out_print(" Backup blocks !12UL", TRUE, bptr->backup_cnt); util_out_print(" Reformat blocks !12UL", FALSE, bptr->reformat_cnt); //util_out_print(" ", FALSE); util_out_print(" Total blocks !12UL", TRUE, bptr->total_blks); util_out_print(" Shmpool blocked !AD", FALSE, 5, (bptr->shmpool_blocked ? " TRUE" : "FALSE")); //util_out_print(" ", FALSE); util_out_print(" File Offset 0x!16@XQ", TRUE, &bptr->dskaddr); util_out_print(" Shmpool crit holder !12UL", FALSE, bptr->shmpool_crit_latch.u.parts.latch_pid); //util_out_print(" ", FALSE); util_out_print(" Backup_errno !12UL", TRUE, bptr->backup_errno); util_out_print(" Backup Process ID !12UL", FALSE, bptr->backup_pid); //util_out_print(" ", FALSE); util_out_print(" Backup TN 0x!16@XQ", TRUE, &bptr->backup_tn); util_out_print(" Inc Backup TN 0x!16@XQ", FALSE, &bptr->inc_backup_tn); //util_out_print(" ", FALSE); util_out_print(" Process Failed !12UL", TRUE, bptr->failed); util_out_print(" Allocs since check !12UL", FALSE, bptr->allocs_since_chk); //util_out_print(" ", FALSE); util_out_print(" Backup Image Count !12UL", TRUE, bptr->backup_image_count); util_out_print(" Temp File: !AD", TRUE, LEN_AND_STR(&bptr->tempfilename[0])); } if (NEED_TO_DUMP("MIXEDMODE")) { util_out_print(0, TRUE); util_out_print(" Database is Fully Upgraded : !AD", TRUE, 5, (csd->fully_upgraded ? " TRUE" : "FALSE")); util_out_print(" Database WAS ONCE Fully Upgraded from V4 : !AD", TRUE, 5, (csd->db_got_to_v5_once ? " TRUE" : "FALSE")); util_out_print(" Blocks to Upgrade subzero(negative) error : 0x!16@XQ", TRUE, &(csd->blks_to_upgrd_subzero_error)); util_out_print(" TN when Blocks to Upgrade last became 0 : 0x!16@XQ", TRUE, &csd->tn_upgrd_blks_0); util_out_print(" TN when Desired DB Format last changed : 0x!16@XQ", TRUE, &csd->desired_db_format_tn); util_out_print(" TN when REORG upgrd/dwngrd changed dbfmt : 0x!16@XQ", TRUE, &csd->reorg_db_fmt_start_tn); util_out_print(0, TRUE); util_out_print(" REORG upgrd/dwngrd will restart from blk : 0x!16@XQ", TRUE, &(csd->reorg_upgrd_dwngrd_restart_block)); } if (NEED_TO_DUMP("UPDPROC")) { util_out_print(0, TRUE); util_out_print(" Upd reserved area [% global buffers] !3UL", FALSE, csd->reserved_for_upd); util_out_print(" Avg blks read per 100 records !4UL", TRUE, csd->avg_blks_per_100gbl); util_out_print(" Pre read trigger factor [% upd rsrvd] !3UL", FALSE, csd->pre_read_trigger_factor); util_out_print(" Upd writer trigger [%flshTrgr] !3UL", TRUE, csd->writer_trigger_factor); } if (NEED_TO_DUMP("SNAPSHOT")) { util_out_print(0, TRUE); util_out_print(" Snapshot in progress !AD", FALSE, 5, (cnl->snapshot_in_prog ? " TRUE" : "FALSE")); util_out_print(" Number of active snapshots !12UL", TRUE, cnl->num_snapshots_in_effect); util_out_print(" Snapshot cycle !12UL", FALSE, cnl->ss_shmcycle); /* SS_MULTI: Note that if we have multiple snapshots, then we have to run through each active * snapshot region and dump their informations respectively */ ss_shm_ptr = (shm_snapshot_ptr_t)(SS_GETSTARTPTR(csa)); util_out_print(" Active snapshot PID !12UL", TRUE, ss_shm_ptr->ss_info.ss_pid); util_out_print(" Snapshot TN !20@UQ", FALSE, &(ss_shm_ptr->ss_info.snapshot_tn)); util_out_print(" Total blocks !20@UQ", TRUE, &(ss_shm_ptr->ss_info.total_blks)); util_out_print(" Free blocks !20@UQ", FALSE, &(ss_shm_ptr->ss_info.free_blks)); util_out_print(" Process failed !12UL", TRUE, ss_shm_ptr->failed_pid); util_out_print(" Failure errno !12UL", FALSE, ss_shm_ptr->failure_errno); util_out_print(" Snapshot shared memory identifier !12SL", TRUE, ss_shm_ptr->ss_info.ss_shmid); util_out_print(" Snapshot file name !AD", TRUE, LEN_AND_STR(ss_shm_ptr->ss_info.shadow_file)); } return; } fis-gtm-V7.0-005/sr_port/dse_eval.c0000755000032200000250000000243614342376331016001 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "cli.h" #include "util.h" #include "dse.h" void dse_eval(void) { int4 util_len; gtm_uint64_t num; char util_buff[MAX_UTIL_LEN]; if (cli_present("NUMBER") != CLI_PRESENT) return; if (cli_present("DECIMAL") == CLI_PRESENT) { if (!cli_get_uint64("NUMBER", (gtm_uint64_t *)&num)) return; } else if (!cli_get_hex64("NUMBER", &num)) return; memcpy(util_buff, "Hex: ", 6); util_len = 6; util_len += i2hexl_nofill(num, (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len]," Dec: !@UQ", 13); util_len += 13; util_buff[util_len] = 0; util_out_print(util_buff, TRUE, &num); return; } fis-gtm-V7.0-005/sr_port/dse_exhaus.c0000644000032200000250000001565314342376331016351 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_signal.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "dsefind.h" #include "copy.h" #include "util.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" GBLREF block_id patch_find_blk, patch_left_sib, patch_path[MAX_BT_DEPTH + 1], patch_right_sib; GBLREF boolean_t patch_exh_found, patch_find_root_search, patch_find_sibs; GBLREF global_root_list *global_roots_head; GBLREF int4 patch_offset[MAX_BT_DEPTH + 1]; GBLREF sgmnt_addrs *cs_addrs; GBLREF short int patch_path_count; GBLREF VSIG_ATOMIC_T util_interrupt; error_def(ERR_CTRLC); error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEINVALBLKID); void dse_exhaus(int4 pp, int4 op) { block_id last; cache_rec_ptr_t dummy_cr; char util_buff[MAX_UTIL_LEN]; int count, util_len; int4 dummy_int; global_dir_path *d_ptr, *temp; short temp_short; sm_uc_ptr_t bp, b_top, nrp, nr_top, ptr, rp, r_top; boolean_t long_blk_id; long blk_id_size; last = 0; patch_path_count++; if (!(bp = t_qread(patch_path[pp - 1], &dummy_int, &dummy_cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); if (((blk_hdr_ptr_t)bp)->bver > BLK_ID_32_VER) /* Check blk version to see if using 32 or 64 bit block_id */ { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) b_top = bp + cs_addrs->hdr->blk_size; else if (SIZEOF(blk_hdr) > ((blk_hdr_ptr_t) bp)->bsiz) b_top = bp + SIZEOF(blk_hdr); else b_top = bp + ((blk_hdr_ptr_t)bp)->bsiz; for (rp = bp + SIZEOF(blk_hdr); rp < b_top; rp = r_top) { if (util_interrupt) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CTRLC); break; } GET_SHORT(temp_short, &((rec_hdr_ptr_t)rp)->rsiz); r_top = rp + temp_short; if (r_top > b_top) r_top = b_top; if (blk_id_size > (r_top - rp)) break; if (((blk_hdr_ptr_t)bp)->levl) { if (long_blk_id == TRUE) # ifdef BLK_NUM_64BIT GET_BLK_ID_64(patch_path[pp], (r_top - SIZEOF(block_id_64))); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif else GET_BLK_ID_32(patch_path[pp], (r_top - SIZEOF(block_id_32))); } else { for (ptr = rp + SIZEOF(rec_hdr); (*ptr++ || *ptr++) && (ptr <= r_top);) ; if (long_blk_id == TRUE) # ifdef BLK_NUM_64BIT GET_BLK_ID_64(patch_path[pp], ptr); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif else GET_BLK_ID_32(patch_path[pp], ptr); } patch_offset[op] = (int4)(rp - bp); if (patch_path[pp] == patch_find_blk) { if (!patch_exh_found) { if (patch_find_sibs) util_out_print("!/!_Left sibling!_!_Current block!_!_Right sibling", TRUE); patch_exh_found = TRUE; } if (patch_find_sibs) { patch_left_sib = last; if (r_top < b_top) { nrp = r_top; GET_SHORT(temp_short, &((rec_hdr_ptr_t)rp)->rsiz); nr_top = nrp + temp_short; if (nr_top > b_top) nr_top = b_top; if (blk_id_size <= (nr_top - nrp)) { if (((blk_hdr_ptr_t)bp)->levl) if (long_blk_id == TRUE) # ifdef BLK_NUM_64BIT GET_BLK_ID_64(patch_right_sib, (nr_top - SIZEOF(block_id_64))); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif else GET_BLK_ID_32(patch_right_sib, (nr_top - SIZEOF(block_id_32))); else { for (ptr = rp + SIZEOF(rec_hdr); (*ptr++ || *ptr++) && (ptr <= nr_top);) ; if (long_blk_id == TRUE) # ifdef BLK_NUM_64BIT GET_BLK_ID_64(patch_right_sib, ptr); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif else GET_BLK_ID_32(patch_right_sib, ptr); } } } else patch_right_sib = 0; if (patch_left_sib) util_out_print("!_0x!16@XQ", FALSE, &patch_left_sib); else util_out_print("!_none!_", FALSE); util_out_print("!_0x!16@XQ!_", FALSE, &patch_find_blk); if (patch_right_sib) util_out_print("0x!16@XQ!/", TRUE, &patch_right_sib); else util_out_print("none!/", TRUE); } else /* !patch_find_sibs */ { patch_path_count--; util_out_print(" Directory path!/ Path--blk:off", TRUE); if (!patch_find_root_search) { d_ptr = global_roots_head->link->dir_path; while (d_ptr) { memcpy(util_buff, " ", 1); util_len = 1; util_len += i2hexl_nofill(d_ptr->block, (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], ":", 1); util_len += 1; /* Using MAX_HEX_SHORT for int value because to save line space * since the value should always fit in 2-bytes */ util_len += i2hex_nofill(d_ptr->offset, (uchar_ptr_t)&util_buff[util_len], MAX_HEX_SHORT); util_buff[util_len] = 0; util_out_print(util_buff, FALSE); temp = d_ptr; d_ptr = d_ptr->next; free(temp); } global_roots_head->link->dir_path = 0; util_out_print("!/!/ Global paths!/ Path--blk:off", TRUE); } for (count = 0; count < patch_path_count; count++) { memcpy(util_buff, " ", 1); util_len = 1; util_len += i2hexl_nofill(patch_path[count], (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], ":", 1); util_len += 1; /* Using MAX_HEX_SHORT for int value because to save line space * since the value should always fit in 2-bytes */ util_len += i2hex_nofill(patch_offset[count], (uchar_ptr_t)&util_buff[util_len], MAX_HEX_SHORT); util_buff[util_len] = 0; util_out_print(util_buff,FALSE); } memcpy(util_buff, " ", 1); util_len = 1; util_len += i2hexl_nofill(patch_path[count], (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); util_buff[util_len] = 0; util_out_print(util_buff, TRUE); patch_path_count++; } } if ((0 < patch_path[pp]) && (patch_path[pp] < cs_addrs->ti->total_blks) && (patch_path[pp] % cs_addrs->hdr->bplmap)) { if (1 < ((blk_hdr_ptr_t)bp)->levl) dse_exhaus(pp + 1, op + 1); else if ((1 == ((blk_hdr_ptr_t)bp)->levl) && patch_find_root_search) dse_find_roots(patch_path[pp]); } last = patch_path[pp]; } patch_path_count--; return; } fis-gtm-V7.0-005/sr_port/dse_exit.c0000755000032200000250000000221114342376331016012 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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" /* for EXIT() */ #ifdef DEBUG #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "gdsfhead.h" #endif #include "error.h" #include "iosp.h" #include "util.h" #include "dse_exit.h" GBLREF unsigned int t_tries; #ifdef DEBUG GBLREF sgmnt_addrs *cs_addrs; #endif void dse_exit(void) { /* reset t_tries (from CDB_STAGNATE to 0) as we are exiting and no longer going to be running transactions * and an assert in wcs_recover relies on this */ t_tries = 0; assert((NULL == cs_addrs) || !cs_addrs->now_crit || cs_addrs->hold_onto_crit); util_out_close(); EXIT(SS_NORMAL); } fis-gtm-V7.0-005/sr_port/dse_exit.h0000755000032200000250000000113314342376331016021 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef DSE_EXIT_H_INCLUDED #define DSE_EXIT_H_INCLUDED void dse_exit(void); #endif fis-gtm-V7.0-005/sr_port/dse_f_blk.c0000644000032200000250000005277114342376331016133 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_signal.h" #include "error.h" #include "gdsroot.h" #include "gdsdbver.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "dsefind.h" #include "cli.h" #include "copy.h" #include "util.h" #include "dse.h" /* Include prototypes*/ #include "t_qread.h" GBLDEF boolean_t patch_exh_found, patch_find_root_search, patch_find_sibs; GBLDEF block_id patch_find_blk, patch_left_sib, patch_right_sib; GBLDEF block_id patch_path[MAX_BT_DEPTH + 1], patch_path1[MAX_BT_DEPTH + 1]; GBLREF gd_region *gv_cur_region; GBLDEF global_root_list *global_roots_head, *global_roots_tail; GBLDEF int4 patch_offset[MAX_BT_DEPTH + 1], patch_offset1[MAX_BT_DEPTH + 1]; GBLREF sgmnt_addrs *cs_addrs; GBLDEF short int patch_dir_path_count, patch_path_count; static boolean_t was_crit, was_hold_onto_crit, nocrit_present; error_def(ERR_CTRLC); error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEINVALBLKID); void dse_f_blk(void) { block_id last, look; boolean_t exhaust, long_blk_id; cache_rec_ptr_t dummy_cr; char targ_key[MAX_KEY_SZ + 1], util_buff[MAX_UTIL_LEN]; global_dir_path *d_ptr, *dtemp; global_root_list *temp; int util_len, lvl, parent_lvl; int4 dummy_int; long blk_id_size; short int count, rsize, size; sm_uc_ptr_t blk_id, bp, b_top, key_top, rp, r_top, sp, srp, s_top; if (BADDSEBLK == (patch_find_blk = dse_getblk("BLOCK", DSENOBML, DSEBLKCUR))) /* WARNING: assignment */ return; patch_find_sibs = (CLI_PRESENT == cli_present("SIBLINGS")); was_crit = cs_addrs->now_crit; nocrit_present = (CLI_NEGATED == cli_present("CRIT")); DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); /* ESTABLISH is done here because dse_f_blk_ch() assumes we already have crit. */ ESTABLISH(dse_f_blk_ch); look = patch_find_blk; parent_lvl = 0; do { if (!(bp = t_qread(look, &dummy_int, &dummy_cr))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DSEBLKRDFAIL); if (((blk_hdr_ptr_t)bp)->bver > BLK_ID_32_VER) /* Check blk version to see if using 32 or 64 bit block_id */ { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) b_top = bp + cs_addrs->hdr->blk_size; else if (SIZEOF(blk_hdr) > ((blk_hdr_ptr_t) bp)->bsiz) b_top = bp + SIZEOF(blk_hdr); else b_top = bp + ((blk_hdr_ptr_t)bp)->bsiz; rp = bp + SIZEOF(blk_hdr); GET_SHORT(rsize, &((rec_hdr_ptr_t) rp)->rsiz); if (SIZEOF(rec_hdr) > rsize) r_top = rp + SIZEOF(rec_hdr); else r_top = rp + rsize; if (r_top > b_top) r_top = b_top; for (key_top = rp + SIZEOF(rec_hdr); (key_top < r_top) && *key_top++; ) ; lvl = ((blk_hdr_ptr_t)bp)->levl; if (lvl && (key_top > (blk_id = r_top - blk_id_size))) /* NOTE assignment */ key_top = blk_id; size = key_top - rp - SIZEOF(rec_hdr); if (SIZEOF(targ_key) < size) size = SIZEOF(targ_key); if (!lvl || size) break; /* data block OR index block with a non-* key found. break right away to do search */ if ((0 > lvl) || (lvl >= MAX_BT_DEPTH) || (parent_lvl && (parent_lvl != (lvl + 1)))) break; /* out-of-design level (integ error in db). do not descend anymore. do exhaustive search */ parent_lvl = lvl; /* while it is an index block with only a *-record keep looking in child blocks for key */ if (long_blk_id == TRUE) { # ifdef BLK_NUM_64BIT GET_BLK_ID_64(look, blk_id); # else DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else GET_BLK_ID_32(look, blk_id); } while (TRUE); patch_path_count = 1; patch_path[0] = get_dir_root(); patch_left_sib = patch_right_sib = 0; patch_find_root_search = TRUE; if ((exhaust = (CLI_PRESENT == cli_present("EXHAUSTIVE"))) || (0 >= size)) /* NOTE assignment */ { if (patch_exh_found = (patch_find_blk == patch_path[0])) /* NOTE assignment */ { if (patch_find_sibs) { util_out_print("!/!_Left sibling!_!_Current block!_!_Right sibling", TRUE); util_out_print("!_none!_!_!_0x!16@XQ!_none",TRUE, &patch_find_blk); } else { assert(1 == patch_path[0]); /* OK to assert because pro prints */ util_out_print("!/ Directory path!/ Path--blk:off!/!_1", TRUE); } } else { global_roots_head = (global_root_list *)malloc(SIZEOF(global_root_list)); global_roots_tail = global_roots_head; global_roots_head->link = NULL; global_roots_head->dir_path = NULL; dse_exhaus(1, 0); patch_find_root_search = FALSE; while (!patch_exh_found && global_roots_head->link) { patch_path[0] = global_roots_head->link->root; patch_path_count = 1; patch_left_sib = patch_right_sib = 0; if (patch_exh_found = (patch_find_blk == patch_path[0])) /* NOTE assignment */ { if (patch_find_sibs) { util_out_print("!/!_Left sibling!_!_Current block!_!_Right sibling", TRUE); util_out_print("!_none!_!_!_0x!16@XQ!_none",TRUE, &patch_find_blk); } else { patch_path_count--; util_out_print("!/ Directory path!/ Path--blk:off", TRUE); if (!patch_find_root_search) { d_ptr = global_roots_head->link->dir_path; while (d_ptr) { memcpy(util_buff, " ", 1); util_len = 1; util_len += i2hexl_nofill(d_ptr->block, (uchar_ptr_t)&util_buff[util_len], 16); memcpy(&util_buff[util_len], ":", 1); util_len += 1; util_len += i2hex_nofill(d_ptr->offset, (uchar_ptr_t)&util_buff[util_len], 4); memcpy(&util_buff[util_len], ",", 1); util_len += 1; util_buff[util_len] = 0; util_out_print(util_buff, FALSE); temp = (global_root_list *)d_ptr; d_ptr = d_ptr->next; free(temp); } global_roots_head->link->dir_path = 0; util_out_print("!/ Global tree path!/ Path--blk:off", TRUE); } for (count = 0; count < patch_path_count; count++) { memcpy(util_buff, " ", 1); util_len = 1; util_len += i2hexl_nofill(patch_path[count], (uchar_ptr_t)&util_buff[util_len], 16); memcpy(&util_buff[util_len], ":", 1); util_len += 1; util_len += i2hex_nofill(patch_offset[count], (uchar_ptr_t)&util_buff[util_len], 4); memcpy(&util_buff[util_len], ",", 1); util_len += 1; util_buff[util_len] = 0; util_out_print(util_buff, FALSE); } memcpy(util_buff, " ", 1); util_len = 1; util_len += i2hexl_nofill(patch_path[count], (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); util_buff[util_len] = 0; util_out_print(util_buff, TRUE); } } else dse_exhaus(1, 0); temp = global_roots_head; d_ptr = global_roots_head->link->dir_path; while (d_ptr) { dtemp = d_ptr; d_ptr = d_ptr->next; free(dtemp); } global_roots_head = global_roots_head->link; free(temp); } while (global_roots_head->link) { temp = global_roots_head; d_ptr = global_roots_head->link->dir_path; while (d_ptr) { dtemp = d_ptr; d_ptr = d_ptr->next; free(dtemp); } global_roots_head = global_roots_head->link; free(temp); } } if (!patch_exh_found) { if (exhaust) util_out_print("Error: exhaustive search fail.", TRUE); else util_out_print("Error: ordered search fail.", TRUE); } else util_out_print(0, TRUE); DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; return; } else /* !exhaust && size > 0 */ { if (!dse_is_blk_in(rp, r_top, size)) { util_out_print("Error: ordered search fail.", TRUE); DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; return; } } if (patch_find_sibs) { /* the cross-branch sib action could logically go in dse_order but is here 'cause it only gets used when needed */ util_out_print("!/!_Left sibling!_!_Current block!_!_Right sibling", TRUE); if (!patch_left_sib) { for (last = 0, lvl = (patch_find_root_search ? patch_dir_path_count : patch_path_count) - 1; 0 <= --lvl;) { if (!(sp = t_qread(patch_find_root_search ? patch_path[lvl] : patch_path1[lvl], &dummy_int, &dummy_cr))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DSEBLKRDFAIL); if (((blk_hdr_ptr_t)sp)->bver > BLK_ID_32_VER) /* Check to see if using 32 or 64 bit block_id */ { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } if (((blk_hdr_ptr_t)sp)->bsiz > cs_addrs->hdr->blk_size) s_top = sp + cs_addrs->hdr->blk_size; else if (SIZEOF(blk_hdr) > ((blk_hdr_ptr_t)sp)->bsiz) { util_out_print("Error: sibling search hit problem blk 0x!16@XQ", TRUE, patch_find_root_search ? &(patch_path[lvl]) : &(patch_path1[lvl])); lvl = -1; break; } else s_top = sp + ((blk_hdr_ptr_t)sp)->bsiz; srp = sp + SIZEOF(blk_hdr); GET_SHORT(rsize, &((rec_hdr_ptr_t)srp)->rsiz); srp += rsize; if (long_blk_id == TRUE) { # ifdef BLK_NUM_64BIT GET_BLK_ID_64(look, srp - SIZEOF(block_id_64)); # else DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else GET_BLK_ID_32(look, srp - SIZEOF(block_id_32)); if ((patch_find_root_search ? patch_path[lvl + 1] : patch_path1[lvl + 1]) != look) break; } if (0 <= lvl) { for (lvl++; (srp < s_top) && ((patch_find_root_search ? patch_path[lvl] : patch_path1[lvl]) != look);) { last = look; GET_SHORT(rsize, &((rec_hdr_ptr_t)srp)->rsiz); srp += rsize; if (srp > s_top) break; if (long_blk_id == TRUE) { # ifdef BLK_NUM_64BIT GET_BLK_ID_64(look, srp - SIZEOF(block_id_64)); # else DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else GET_BLK_ID_32(look, srp - SIZEOF(block_id_32)); } if ((patch_find_root_search ? patch_path[lvl] : patch_path1[lvl]) != look) { util_out_print("Error: sibling search hit problem blk 0x!16@XQ", TRUE, patch_find_root_search ? &(patch_path[lvl]) : &(patch_path1[lvl])); last = 0; lvl = (patch_find_root_search ? patch_dir_path_count : patch_path_count); } else if (last >= cs_addrs->ti->total_blks) { /* should never come here as block was previously OK, but this is dse so be careful */ util_out_print("Error: sibling search got 0x!16@XQ which exceeds total blocks 0x!16@XQ", TRUE, &last, &(cs_addrs->ti->total_blks)); last = 0; lvl = (patch_find_root_search ? patch_dir_path_count : patch_path_count); } for (lvl++; lvl < (patch_find_root_search ? patch_dir_path_count : patch_path_count); lvl++) { if (!(sp = t_qread(last, &dummy_int, &dummy_cr))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DSEBLKRDFAIL); if (((blk_hdr_ptr_t)sp)->bver > BLK_ID_32_VER) /* Check if using 32 or 64 bit block_id */ { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } if (((blk_hdr_ptr_t)sp)->bsiz > cs_addrs->hdr->blk_size) s_top = sp + cs_addrs->hdr->blk_size; else if (SIZEOF(blk_hdr) > ((blk_hdr_ptr_t)sp)->bsiz) { util_out_print("Error: sibling search hit problem blk 0x!16@XQ", TRUE, &last); last = 0; break; } else s_top = sp + ((blk_hdr_ptr_t)sp)->bsiz; if (0 >= (signed char)(((blk_hdr_ptr_t)sp)->levl)) { util_out_print("Error: sibling search reached level 0", TRUE); last = 0; break; } if (long_blk_id == TRUE) { # ifdef BLK_NUM_64BIT GET_BLK_ID_64(last, s_top - SIZEOF(block_id_64)); # else DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else GET_BLK_ID_32(last, s_top - SIZEOF(block_id_32)); if (last >= cs_addrs->ti->total_blks) { util_out_print( "Error: sibling search got 0x!16@XQ which exceeds total blocks 0x!16@XQ", TRUE, &last, &(cs_addrs->ti->total_blks)); break; } } } patch_left_sib = last; } if (patch_left_sib) util_out_print("!_0x!16@XQ", FALSE, &patch_left_sib); else util_out_print("!_none!_!_", FALSE); util_out_print("!_0x!16@XQ!_", FALSE, &patch_find_blk); if (!patch_right_sib) { for (lvl = (patch_find_root_search ? patch_dir_path_count : patch_path_count) - 1; 0 <= --lvl;) { if (!(sp = t_qread(patch_find_root_search ? patch_path[lvl] : patch_path1[lvl], &dummy_int, &dummy_cr))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DSEBLKRDFAIL); if (((blk_hdr_ptr_t)sp)->bver > BLK_ID_32_VER) /* Check to see if using 32 or 64 bit block_id */ { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } if (((blk_hdr_ptr_t)sp)->bsiz > cs_addrs->hdr->blk_size) s_top = sp + cs_addrs->hdr->blk_size; else if (SIZEOF(blk_hdr) > ((blk_hdr_ptr_t)sp)->bsiz) { util_out_print("Error: sibling search hit problem blk 0x!16@XQ", TRUE, patch_find_root_search ? &(patch_path[lvl]) : &(patch_path1[lvl])); lvl = -1; break; } else s_top = sp + ((blk_hdr_ptr_t)sp)->bsiz; if (long_blk_id == TRUE) { # ifdef BLK_NUM_64BIT GET_BLK_ID_64(look, s_top - SIZEOF(block_id_64)); # else DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else GET_BLK_ID_32(look, s_top - SIZEOF(block_id_32)); if (look >= cs_addrs->ti->total_blks) { util_out_print("Error: sibling search got 0x!16@XQ which exceeds total blocks 0x!16@XQ", TRUE, &look, &(cs_addrs->ti->total_blks)); lvl = -1; break; } if ((patch_find_root_search ? patch_path[lvl + 1] : patch_path1[lvl + 1]) != look) break; } if (0 <= lvl) { srp = sp + SIZEOF(blk_hdr); for (lvl++; (srp < s_top) && ((patch_find_root_search ? patch_path[lvl] : patch_path1[lvl]) != last);) { last = look; GET_SHORT(rsize, &((rec_hdr_ptr_t)srp)->rsiz); srp += rsize; if (srp > s_top) break; if (long_blk_id == TRUE) { # ifdef BLK_NUM_64BIT GET_BLK_ID_64(look, srp - SIZEOF(block_id_64)); # else DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else GET_BLK_ID_32(look, srp - SIZEOF(block_id_32)); if (look >= cs_addrs->ti->total_blks) { util_out_print( "Error: sibling search got 0x!16@XQ which exceeds total blocks 0x!16@XQ", TRUE, &look, &(cs_addrs->ti->total_blks)); break; } } if ((patch_find_root_search ? patch_path[lvl] : patch_path1[lvl]) != last) { util_out_print("Error: sibling search hit problem blk 0x!16@XQ", TRUE, patch_find_root_search ? &(patch_path[lvl]) : &(patch_path1[lvl])); look = 0; lvl = (patch_find_root_search ? patch_dir_path_count : patch_path_count); } for (lvl++; lvl < (patch_find_root_search ? patch_dir_path_count : patch_path_count); lvl++) { if (!(sp = t_qread(look, &dummy_int, &dummy_cr))) /* NOTE assignment */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DSEBLKRDFAIL); if (((blk_hdr_ptr_t)sp)->bver > BLK_ID_32_VER) /* Check if using 32 or 64 bit block_id */ { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } if (!(((blk_hdr_ptr_t)sp)->bsiz > cs_addrs->hdr->blk_size) && (SIZEOF(blk_hdr) > ((blk_hdr_ptr_t)sp)->bsiz)) { util_out_print("Error: sibling search hit problem blk 0x!XL", TRUE, look); look = 0; break; } if (0 >= (signed char)(((blk_hdr_ptr_t)sp)->levl)) { util_out_print("Error: sibling search reached level 0", TRUE); look = 0; break; } srp = sp + SIZEOF(blk_hdr); GET_SHORT(rsize, &((rec_hdr_ptr_t)srp)->rsiz); srp += rsize; if (long_blk_id == TRUE) { # ifdef BLK_NUM_64BIT GET_BLK_ID_64(look, srp - SIZEOF(block_id_64)); # else DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else GET_BLK_ID_32(look, srp - SIZEOF(block_id_32)); if (look >= cs_addrs->ti->total_blks) { util_out_print("Error: sibling search got 0x!XL which exceeds total blocks 0x!XL", TRUE, look, cs_addrs->ti->total_blks); look = 0; break; } } } else look = 0; patch_right_sib = look; } if (patch_right_sib) util_out_print("0x!16@XQ!/", TRUE, &patch_right_sib); else util_out_print("none!/", TRUE); DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; return; } util_out_print("!/ Directory path!/ Path--blk:off", TRUE); patch_dir_path_count--; for (count = 0; count < patch_dir_path_count; count++) { memcpy(util_buff, " ", 1); util_len = 1; util_len += i2hexl_nofill(patch_path[count], (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], ":", 1); util_len += 1; /* Using MAX_HEX_SHORT for int value because to save line space * since the value should always fit in 2-bytes */ util_len += i2hex_nofill(patch_offset[count], (uchar_ptr_t)&util_buff[util_len], MAX_HEX_SHORT); memcpy(&util_buff[util_len], ",", 1); util_len += 1; util_buff[util_len] = 0; util_out_print(util_buff, FALSE); } if (!patch_find_root_search) { assert(patch_path_count); /* OK to assert since pro works as desired */ util_out_print("!/ Global tree path!/ Path--blk:off", TRUE); } if (patch_path_count) { patch_path_count--; for (count = 0; count < patch_path_count; count++) { memcpy(util_buff, " ", 1); util_len = 1; util_len += i2hexl_nofill(patch_path1[count], (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], ":", 1); util_len += 1; /* Using MAX_HEX_SHORT for int value because to save line space * since the value should always fit in 2-bytes */ util_len += i2hex_nofill(patch_offset1[count], (uchar_ptr_t)&util_buff[util_len], MAX_HEX_SHORT); memcpy(&util_buff[util_len], ",", 1); util_len += 1; util_buff[util_len] = 0; util_out_print(util_buff, FALSE); } } else assert(patch_find_root_search); /* OK to assert since pro works as desired */ memcpy(util_buff, " ", 1); util_len = 1; util_len += i2hexl_nofill(patch_find_root_search ? patch_path[count] : patch_path1[count], (uchar_ptr_t)&util_buff[util_len], 16); memcpy(&util_buff[util_len], "!/", 2); util_len += 2; util_buff[util_len] = 0; util_out_print(util_buff, TRUE); DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); REVERT; return; } /* Control-C condition handler */ CONDITION_HANDLER(dse_f_blk_ch) { START_CH(TRUE); if (ERR_CTRLC == SIGNAL) DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); NEXTCH; } fis-gtm-V7.0-005/sr_port/dse_f_free.c0000644000032200000250000000631514342376331016275 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "cli.h" #include "util.h" #include "gdsbml.h" #include "bmm_find_free.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; error_def(ERR_DSEBLKRDFAIL); void dse_f_free(void) { block_id blk, lmap_hint, master_bit, mmap_hint, total_blks; boolean_t in_last_bmap, was_crit, was_hold_onto_crit; cache_rec_ptr_t dummy_cr; char util_buff[MAX_UTIL_LEN]; int4 bplmap, dummy_int; int4 lmap_bit, nocrit_present, util_len; sm_uc_ptr_t lmap_base; if (0 == cs_addrs->hdr->bplmap) { util_out_print("Cannot perform free block search: bplmap field of file header is zero.", TRUE); return; } bplmap = cs_addrs->hdr->bplmap; if (BADDSEBLK == (blk = dse_getblk("HINT", DSEBMLOK, DSEBLKNOCUR))) /* WARNING: assignment */ return; mmap_hint = blk / bplmap; master_bit = bmm_find_free(mmap_hint, cs_addrs->bmm, (cs_addrs->ti->total_blks + bplmap - 1)/ bplmap); if (-1 == master_bit) { util_out_print("Error: database full.", TRUE); return; } in_last_bmap = (master_bit == (cs_addrs->ti->total_blks / bplmap)); was_crit = cs_addrs->now_crit; nocrit_present = (CLI_NEGATED == cli_present("CRIT")); DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); if (!(lmap_base = t_qread(master_bit * bplmap, &dummy_int, &dummy_cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); if (master_bit == mmap_hint) lmap_hint = blk - ((blk / bplmap) * bplmap); else lmap_hint = 0; if (in_last_bmap) total_blks = (cs_addrs->ti->total_blks - master_bit); else total_blks = bplmap; lmap_bit = bml_find_free(lmap_hint, lmap_base + SIZEOF(blk_hdr), total_blks); if (-1 == lmap_bit) { memcpy(util_buff, "Error: bit map in block ", 24); util_len = 24; util_len += i2hexl_nofill(master_bit * bplmap, (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], " incorrectly marked free in master map.", 39); util_len += 39; util_buff[util_len] = 0; util_out_print(util_buff, TRUE); DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return; } memcpy(util_buff, "!/Next free block is ", 21); util_len = 21; util_len += i2hexl_nofill((master_bit * bplmap) + lmap_bit, (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], ".!/", 3); util_len += 3; util_buff[util_len] = 0; util_out_print(util_buff, TRUE); DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return; } fis-gtm-V7.0-005/sr_port/dse_f_key.c0000755000032200000250000001463114342376331016147 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "cli.h" #include "util.h" #include "dse.h" #include "print_target.h" #include "gv_trigger_common.h" /* for IS_GVKEY_HASHT_GBLNAME macro */ GBLREF short int patch_path_count; GBLREF block_id ksrch_root; GBLREF bool patch_find_root_search; GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF gd_addr *original_header; STATICFNDCL void print_reg_if_mismatch(char *key, int keylen); /* If current region does not match region where TARG_KEY maps to, and if -nogbldir was not specified, * issue an info message indicating the mapping region. */ STATICFNDEF void print_reg_if_mismatch(char *key, int keylen) { gd_binding *map; gd_region *reg; assert(3 <= keylen); assert(KEY_DELIMITER == key[keylen - 1]); assert(KEY_DELIMITER == key[keylen - 2]); assert(KEY_DELIMITER != key[keylen - 3]); /* If input key is ^#t, then do not call gv_srch_map as it does not belong to a particular region, * i.e. the gld does not map globals beginning with #. There is no reg mismatch possible in that case. * So skip this processing entirely. */ if (!IS_GVKEY_HASHT_GBLNAME(keylen -2, key)) { map = gv_srch_map(original_header, key, keylen - 2, SKIP_BASEDB_OPEN_FALSE); /* -2 to remove two trailing 0s */ reg = map->reg.addr; if (gv_cur_region != reg) { /* At this point, gv_target and gv_target->collseq are already setup (by the dse_getki call in caller). * This is needed by the "print_target" --> "gvsub2str" call below. */ util_out_print("Key ^", FALSE); print_target((unsigned char *)key); util_out_print(" maps to Region !AD; Run \"find -region=!AD\" before looking for this node", TRUE, REG_LEN_STR(reg), REG_LEN_STR(reg)); } } } void dse_f_key(void) { block_id path[MAX_BT_DEPTH + 1], root_path[MAX_BT_DEPTH + 1]; boolean_t found, nocrit_present, nogbldir_present, was_crit, was_hold_onto_crit; char targ_key[MAX_KEY_SZ + 1], targ_key_root[MAX_KEY_SZ + 1], *key_top, util_buff[MAX_UTIL_LEN]; int size, size_root, root_path_count, count, util_len; int4 offset[MAX_BT_DEPTH + 1], root_offset[MAX_BT_DEPTH + 1]; if (!dse_getki(&targ_key[0], &size, LIT_AND_LEN("KEY"))) return; patch_path_count = 1; root_path[0] = get_dir_root(); for (key_top = &targ_key[0]; key_top < ARRAYTOP(targ_key); ) if (!*key_top++) break; size_root = (int)(key_top - &targ_key[0] + 1); memcpy(&targ_key_root[0], &targ_key[0], size_root); targ_key_root[size_root - 1] = targ_key_root[size_root] = 0; patch_find_root_search = TRUE; was_crit = cs_addrs->now_crit; nocrit_present = (CLI_NEGATED == cli_present("CRIT")); /* -NOGBLDIR is currently supported only in Unix */ UNIX_ONLY(nogbldir_present = (CLI_NEGATED == cli_present("GBLDIR"));) VMS_ONLY(nogbldir_present = TRUE;) DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); if (!dse_ksrch(root_path[0], &root_path[1], &root_offset[0], &targ_key_root[0], size_root)) { util_out_print("!/Key not found, no root present.", TRUE); if (!nogbldir_present) print_reg_if_mismatch(&targ_key[0], size); util_out_print("", TRUE); DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return; } root_path_count = patch_path_count; patch_path_count = 1; path[0] = ksrch_root; patch_find_root_search = FALSE; if (!dse_ksrch(path[0], &path[1], &offset[0], &targ_key[0], size)) { memcpy(util_buff, "!/Key not found, would be in block ", 36); util_len = 36; util_len += i2hexl_nofill(path[patch_path_count - 2], (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], ".", 1); util_len += 1; util_buff[util_len] = 0; util_out_print(util_buff, FALSE); patch_path_count -= 1; } else { memcpy(util_buff, "!/Key found in block ", 22); util_len = 22; util_len += i2hexl_nofill(path[patch_path_count - 1], (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], ".", 1); util_len += 1; util_buff[util_len] = 0; util_out_print(util_buff, FALSE); } util_out_print("!/ Directory path!/ Path--blk:off", TRUE); for (count = 0; count < root_path_count ;count++) { memcpy(util_buff, " ", 1); util_len = 1; util_len += i2hexl_nofill(root_path[count], (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], ":", 1); util_len += 1; /* Using MAX_HEX_SHORT for int value because to save line space * since the value should always fit in 2-bytes */ util_len += i2hex_nofill(root_offset[count], (uchar_ptr_t)&util_buff[util_len], MAX_HEX_SHORT); memcpy(&util_buff[util_len], ", ", 1); util_len += 1; util_buff[util_len] = 0; util_out_print(util_buff, FALSE); } util_out_print("!/ Global tree path!/ Path--blk:off", TRUE); if (patch_path_count) { for (count = 0; count < patch_path_count ;count++) { memcpy(util_buff, " ", 1); util_len = 1; util_len += i2hexl_nofill(path[count], (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], ":", 1); util_len += 1; /* Using MAX_HEX_SHORT for int value because to save line space * since the value should always fit in 2-bytes */ util_len += i2hex_nofill(offset[count], (uchar_ptr_t)&util_buff[util_len], MAX_HEX_SHORT); memcpy(&util_buff[util_len], ", ", 1); util_len += 1; util_buff[util_len] = 0; util_out_print(util_buff, FALSE); } util_out_print(0, TRUE); } else { memcpy(util_buff, " ", 1); util_len = 1; util_len += i2hexl_nofill(root_path[count], (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], "!/", 2); util_len += 2; util_buff[util_len] = 0; util_out_print(util_buff, TRUE); } if (!nogbldir_present) print_reg_if_mismatch(&targ_key[0], size); DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return; } fis-gtm-V7.0-005/sr_port/dse_f_reg.c0000644000032200000250000000773014342376331016133 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "util.h" #include "cli.h" #include "dse.h" #include "gtmmsg.h" #include "gvcst_protos.h" GBLREF block_id patch_curr_blk; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_data_ptr_t cs_data; GBLREF sgmnt_addrs *cs_addrs; GBLREF short crash_count; GBLREF mval dollar_zgbldir; GBLREF gd_addr *original_header; GBLREF gv_namehead *gv_target; GBLREF gv_key *gv_currkey; error_def(ERR_DSENOTOPEN); error_def(ERR_NOGTCMDB); error_def(ERR_NOREGION); error_def(ERR_NOUSERDB); void dse_f_reg(void) { char rn[MAX_RN_LEN]; unsigned short rnlen; int i; boolean_t found; gd_region *regptr, *statsDBreg; rnlen = SIZEOF(rn); if (!cli_get_str("REGION", rn, &rnlen)) return; if (('*' == rn[0]) && (1 == rnlen)) { util_out_print("List of global directory:!_!AD!/", TRUE, dollar_zgbldir.str.len, dollar_zgbldir.str.addr); for (i = 0, regptr = original_header->regions; i < original_header->n_regions; i++, regptr++) { if (0 == regptr->dyn.addr->fname_len) continue; /* skip inactive statsDB */ util_out_print("!/File !_!AD", TRUE, regptr->dyn.addr->fname_len, ®ptr->dyn.addr->fname[0]); util_out_print("Region!_!AD", TRUE, REG_LEN_STR(regptr)); } return; } assert(rn[0]); for (i = 0; i < rnlen; i++) /* Region names are always upper-case ASCII */ rn[i] = TOUPPER(rn[i]); found = FALSE; for (i = 0, regptr = original_header->regions; i < original_header->n_regions ;i++, regptr++) { if (found = !memcmp(®ptr->rname[0], &rn[0], MAX_RN_LEN)) break; } if (!found) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NOREGION, 2, rnlen, rn); return; } assert(!IS_STATSDB_REG(regptr)); if (CLI_PRESENT == cli_present("STATS")) { /* Go to corresponding STATSDB if present */ if (!(RDBF_NOSTATS & regptr->reservedDBFlags)) { BASEDBREG_TO_STATSDBREG(regptr, statsDBreg); assert(NULL != statsDBreg); if (!statsDBreg->open) gv_init_reg(statsDBreg, NULL); regptr = statsDBreg; } else { util_out_print("Error: Region: !AD does not have an associated stats region", TRUE, REG_LEN_STR(regptr)); return; } } if (regptr == gv_cur_region) { util_out_print("Error: already in region: !AD", TRUE, REG_LEN_STR(gv_cur_region)); return; } /* reg_cmcheck would have already been called for ALL regions at region_init time. In Unix, this would have set * reg->dyn.addr->acc_meth to dba_cm if it is remote database. So we can safely use this to check if the region * is dba_cm or not. */ if (dba_cm == regptr->dyn.addr->acc_meth) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_NOGTCMDB, 4, LEN_AND_LIT("DSE"), rnlen, rn); return; } if (!regptr->open) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_DSENOTOPEN, 2, rnlen, rn); return; } if (cs_addrs->now_crit) util_out_print("Warning: now leaving region in critical section: !AD", TRUE, REG_LEN_STR(gv_cur_region)); gv_cur_region = regptr; gv_target = NULL; /* to prevent out-of-sync situations between gv_target and cs_addrs */ assert(IS_REG_BG_OR_MM(gv_cur_region)); cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; cs_data = cs_addrs->hdr; UPDATE_CRASH_COUNT(cs_addrs, crash_count); util_out_print("!/File !_!AD", TRUE, DB_LEN_STR(gv_cur_region)); util_out_print("Region!_!AD!/", TRUE, REG_LEN_STR(gv_cur_region)); patch_curr_blk = get_dir_root(); gv_init_reg(gv_cur_region, NULL); return; } fis-gtm-V7.0-005/sr_port/dse_fdmp.c0000755000032200000250000001242514342376331015777 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_string.h" #include "mlkdef.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "gtmctype.h" #include "cli.h" #include "dse.h" #include "gvsub2str.h" #include "gdsblk.h" #include "zshow.h" #define COUNT_TRAILING_ZERO(NUM, WCP, TRAIL_ZERO) \ { \ if (0 == NUM) \ *WCP++ = '0'; \ for (TRAIL_ZERO = 0; (NUM > 0) && (0 == (NUM % 10)); TRAIL_ZERO++, NUM /= 10) \ ; \ } #define OUTPUT_NUMBER(NUM, WCP, TRAIL_ZERO) \ { \ for (rev_num = 0; NUM > 0; rev_num = (rev_num * 10 + NUM % 10), NUM /= 10) \ ; \ for (; rev_num > 0; *WCP++ = (rev_num % 10 + ASCII_0), rev_num /= 10) \ ; \ for (; TRAIL_ZERO > 0 ; *WCP++ = '0', TRAIL_ZERO--) \ ; \ } GBLREF enum dse_fmt dse_dmp_format; GBLREF gd_region *gv_cur_region; GBLREF char patch_comp_key[MAX_KEY_SZ + 1]; static unsigned char *work_buff; static unsigned int work_buff_length; boolean_t dse_fdmp(sm_uc_ptr_t data, int len) { unsigned char *key_char_ptr, *work_char_ptr; int dest_len; unsigned char *ret_addr; boolean_t is_snblk = FALSE; span_subs *ss_ptr; /*spanning node key pointer */ unsigned int snbid, offset, trail_zero, rev_num, num; unsigned short blk_sz; mstr opstr; if (work_buff_length < ZWR_EXP_RATIO(gv_cur_region->max_rec_size)) { work_buff_length = ZWR_EXP_RATIO(gv_cur_region->max_rec_size); if (work_buff) free (work_buff); work_buff = (unsigned char *)malloc(work_buff_length); } work_char_ptr = work_buff; *work_char_ptr++ = '^'; for (key_char_ptr = (uchar_ptr_t)patch_comp_key; *key_char_ptr ; key_char_ptr++) { if (PRINTABLE(*key_char_ptr)) *work_char_ptr++ = *key_char_ptr; else return FALSE; } key_char_ptr++; if (*key_char_ptr) { if (SPAN_START_BYTE != *key_char_ptr) /*Global has subscript*/ { *work_char_ptr++ = '('; for (;;) { opstr.addr = (char *)work_char_ptr; opstr.len = work_buff_length; work_char_ptr = gvsub2str(key_char_ptr, &opstr, TRUE); /* Removed unnecessary checks for printable characters (PRINTABLE()) here * since the data being written into files (OPENed files) would have been * passed through ZWR translation which would have taken care of converting * to $CHAR() or $ZCHAR() */ for (; *key_char_ptr ; key_char_ptr++) ; key_char_ptr++; /* Check if this is spanning node if yes break out of the loop */ if (SPAN_START_BYTE == *key_char_ptr && (int)*(key_char_ptr + 1) >= SPAN_BYTE_MIN && (int)*(key_char_ptr + 2) >= SPAN_BYTE_MIN) { is_snblk = TRUE; break; } if (*key_char_ptr) *work_char_ptr++ = ','; else break; } *work_char_ptr++ = ')'; } else /*Spanning node without subscript*/ is_snblk = TRUE; if (is_snblk) { ss_ptr = (span_subs *)key_char_ptr; snbid = SPAN_GVSUBS2INT(ss_ptr); key_char_ptr = key_char_ptr + SPAN_SUBS_LEN + 1; /* Move out of special subscript of spanning node */ blk_sz = gv_cur_region->dyn.addr->blk_size; /* Decide the offset of the content of a block inside the value of spanning node*/ offset = (snbid) ? (blk_sz - (SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + gv_cur_region->dyn.addr->reserved_bytes + (key_char_ptr - (uchar_ptr_t)patch_comp_key + 1))) * (snbid - 1) : 0 ; ret_addr =(unsigned char *)memmove((void *)(work_buff+4), (void *)work_buff, (work_char_ptr - work_buff)); assert(*ret_addr == '^'); *work_buff = '$'; *(work_buff + 1) = 'z'; *(work_buff + 2) = 'e'; *(work_buff + 3) = '('; /* length of "$ze(" is 4, so move the work_char_ptr by 4*/ work_char_ptr = work_char_ptr + 4; *work_char_ptr++ = ','; /* Dump the offset of the content of a block inside the value of spanning node */ num = snbid ? offset : 0; COUNT_TRAILING_ZERO(num, work_char_ptr, trail_zero); num = offset; OUTPUT_NUMBER(num, work_char_ptr, trail_zero); *work_char_ptr++ = ','; /* Dump the length of the content of a block */ num = snbid ? len : 0; COUNT_TRAILING_ZERO(num, work_char_ptr, trail_zero); num = snbid ? len : 0; OUTPUT_NUMBER(num, work_char_ptr, trail_zero); *work_char_ptr++ = ')'; } } assert(MAX_ZWR_KEY_SZ >= work_char_ptr - work_buff); if (GLO_FMT == dse_dmp_format) { if (!dse_fdmp_output(work_buff, (int4)(work_char_ptr - work_buff))) return FALSE; if (!dse_fdmp_output(data, len)) return FALSE; } else { assert(ZWR_FMT == dse_dmp_format); *work_char_ptr++ = '='; if (is_snblk && !snbid) { *work_char_ptr++ = '"'; *work_char_ptr++ = '"'; dest_len = 0; } else { dest_len = work_buff_length; format2zwr(data, len, work_char_ptr, &dest_len); } if (!dse_fdmp_output(work_buff, (int4)(work_char_ptr + dest_len - work_buff))) return FALSE; } return TRUE; } fis-gtm-V7.0-005/sr_port/dse_find_gvt.c0000644000032200000250000000421514342376331016644 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2017 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 "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "dse.h" #include "min_max.h" #include "targ_alloc.h" #include "hashtab_mname.h" GBLREF gd_addr *original_header; gv_namehead *dse_find_gvt(gd_region *reg, char *name, int name_len) { boolean_t added; gd_gblname *gname; gv_namehead *gvt; hash_table_mname *gvt_hashtab; ht_ent_mname *tabent; mname_entry gvent; sgmnt_addrs *csa; assert(reg->open); assert(IS_REG_BG_OR_MM(reg)); csa = &FILE_INFO(reg)->s_addrs; gvt_hashtab = (hash_table_mname *)csa->miscptr; if (NULL == gvt_hashtab) { gvt_hashtab = (hash_table_mname *)malloc(SIZEOF(hash_table_mname)); csa->miscptr = (void *)gvt_hashtab; init_hashtab_mname(gvt_hashtab, 0, HASHTAB_NO_COMPACT, HASHTAB_NO_SPARE_TABLE); } gvent.var_name.addr = name; gvent.var_name.len = MIN(name_len, MAX_MIDENT_LEN); COMPUTE_HASH_MNAME(&gvent); if (NULL != (tabent = lookup_hashtab_mname(gvt_hashtab, &gvent))) gvt = (gv_namehead *)tabent->value; else { gvt = (gv_namehead *)targ_alloc(reg->max_key_size, &gvent, reg); added = add_hashtab_mname(gvt_hashtab, &gvt->gvname, gvt, &tabent); assert(added); } if (original_header->n_gblnames) { /* If a "collation" sequence is specified for current global name in the GBLNAME section of the .GLD file, * setup gvt with that info */ gname = gv_srch_gblname(original_header, gvt->gvname.var_name.addr, gvt->gvname.var_name.len); if (NULL != gname) { gvt->act_specified_in_gld = TRUE; gvt->act = gname->act; gvt->ver = gname->ver; } } return gvt; } fis-gtm-V7.0-005/sr_port/dse_find_roots.c0000644000032200000250000000621514342376331017214 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "gdsdbver.h" #include "dsefind.h" #include "copy.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" GBLREF global_root_list *global_roots_tail; GBLREF sgmnt_addrs *cs_addrs; GBLREF block_id patch_path[MAX_BT_DEPTH + 1]; GBLREF int4 patch_offset[MAX_BT_DEPTH + 1]; GBLREF short int patch_path_count; error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEINVALBLKID); void dse_find_roots(block_id index) { boolean_t long_blk_id; cache_rec_ptr_t dummy_cr; global_dir_path *d_ptr; int count; int4 dummy_int; long blk_id_size; short temp_short; sm_uc_ptr_t bp, b_top, rp, r_top, key_top; if (!(bp = t_qread(index,&dummy_int,&dummy_cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); if (((blk_hdr_ptr_t)bp)->bver > BLK_ID_32_VER) /* Check blk version to see if using 32 or 64 bit block_id */ { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) b_top = bp + cs_addrs->hdr->blk_size; else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) b_top = bp + SIZEOF(blk_hdr); else b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; for (rp = bp + SIZEOF(blk_hdr); rp < b_top ;rp = r_top) { GET_SHORT(temp_short,&((rec_hdr_ptr_t)rp)->rsiz); r_top = rp + temp_short; if (r_top > b_top) r_top = b_top; if ((r_top - rp) < blk_id_size) break; global_roots_tail->link = (global_root_list *)malloc(SIZEOF(global_root_list)); global_roots_tail = global_roots_tail->link; global_roots_tail->link = 0; for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top; ) if (!*key_top++ && !*key_top++) break; if (long_blk_id == TRUE) # ifdef BLK_NUM_64BIT GET_BLK_ID_64(global_roots_tail->root, key_top); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif else GET_BLK_ID_32(global_roots_tail->root, key_top); global_roots_tail->dir_path = (global_dir_path *)malloc(SIZEOF(global_dir_path)); d_ptr = global_roots_tail->dir_path; for (count = 0; ; count++) { d_ptr->block = patch_path[count]; d_ptr->offset = patch_offset[count]; if (count < patch_path_count - 1) d_ptr->next = (global_dir_path *)malloc(SIZEOF(global_dir_path)); else { d_ptr->next = 0; assert((rp - bp) == (int4)(rp - bp)); d_ptr->offset = (int4)(rp - bp); break; } d_ptr = d_ptr->next; } } return; } fis-gtm-V7.0-005/sr_port/dse_flush.c0000644000032200000250000000262114342376331016164 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" /* needed for jnl.h */ #include "jnl.h" /* needed for the WCSFLU_* macros */ #include "wcs_flu.h" #include "dse.h" GBLREF gd_region *gv_cur_region; GBLREF short crash_count; GBLREF sgmnt_addrs *cs_addrs; error_def(ERR_DBRDONLY); error_def(ERR_DSEONLYBGMM); void dse_flush(void) { if (gv_cur_region->read_only) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); switch (gv_cur_region->dyn.addr->acc_meth) { case dba_bg: case dba_mm: UPDATE_CRASH_COUNT(cs_addrs, crash_count); wcs_flu(WCSFLU_FLUSH_HDR | WCSFLU_WRITE_EPOCH | WCSFLU_SYNC_EPOCH); break; default: rts_error_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_DSEONLYBGMM, 2, LEN_AND_LIT("BUFFER_FLUSH")); break; } return; } fis-gtm-V7.0-005/sr_port/dse_getblk.c0000644000032200000250000000337014342376331016315 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "cli.h" #include "dse.h" #include "gtmmsg.h" GBLREF block_id patch_curr_blk; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; error_def(ERR_BLKINVALID); error_def(ERR_CANTBITMAP); block_id dse_getblk(char *element, boolean_t nobml, boolean_t carry_curr) { block_id blk; #ifndef BLK_NUM_64BIT block_id_64 blk2; #endif #ifdef BLK_NUM_64BIT if (cli_get_hex64(element, (gtm_uint8 *)(&blk))) CLEAR_DSE_COMPRESS_KEY; else blk = patch_curr_blk; #else if (cli_get_hex64(element, (gtm_uint8 *)(&blk2))) { assert(blk2 == (block_id_32)blk2); /* Verify that blk2 won't overflow an int4 */ blk = (block_id_32)blk2; CLEAR_DSE_COMPRESS_KEY; } else blk = patch_curr_blk; #endif if ((blk < 0) || (blk >= cs_addrs->ti->total_blks)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_BLKINVALID, 4, &blk, DB_LEN_STR(gv_cur_region), &(cs_addrs->ti->total_blks)); return BADDSEBLK; } if (nobml && IS_BITMAP_BLK(blk)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CANTBITMAP); return BADDSEBLK; } if (carry_curr) patch_curr_blk = blk; return blk; } fis-gtm-V7.0-005/sr_port/ecode_add.c0000644000032200000250000001726314342376333016111 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "gtm_string.h" /* for memcpy() */ #include "min_max.h" /* for MIN macro */ #include /* for stack_frame.h */ #include "stack_frame.h" /* for stack_frame type */ #include "error_trap.h" #include "get_command_line.h" /* for get_command_line() prototype */ #include "dollar_zlevel.h" /* for dollar_zlevel() prototype */ GBLREF dollar_ecode_type dollar_ecode; /* structure containing $ECODE related information */ GBLREF dollar_stack_type dollar_stack; /* structure containing $STACK related information */ GBLREF stack_frame *error_frame; /* "frame_pointer" at the time of adding the current ECODE */ GBLREF stack_frame *frame_pointer; #define INCR_ECODE_INDEX(ecode_index, str, strlen) \ { \ memcpy(dollar_ecode.end, str, strlen); \ dollar_ecode.array[ecode_index].ecode_str.addr = dollar_ecode.end; \ dollar_ecode.array[ecode_index].ecode_str.len = strlen; \ /* -1 below for not calculating the terminating ',' as part of this ECODE, \ * but instead calculate that as part of the beginning of the next ECODE */ \ dollar_ecode.end += strlen - 1; \ ecode_index++; \ } #define DECR_ECODE_INDEX(ecode_index) \ { \ ecode_index--; \ space_left += dollar_ecode.array[ecode_index].ecode_str.len - 1; \ } /* returns TRUE if able to fit in string held by tmpmval into dollar_stack * returns FALSE otherwise */ static boolean_t fill_dollar_stack_info(mval *mvalptr, mstr *mstrptr) { ssize_t space_left; if (mvalptr->str.len) { space_left = dollar_stack.top - dollar_stack.end; if (mvalptr->str.len > space_left) { dollar_stack.incomplete = TRUE; /* we stop storing $STACK(level) info once we reach a frame level * that can't be fitted in the available space */ return FALSE; } memcpy(dollar_stack.end, mvalptr->str.addr, mvalptr->str.len); mstrptr->addr = dollar_stack.end; mstrptr->len = mvalptr->str.len; dollar_stack.end += mstrptr->len; assert(dollar_stack.end <= dollar_stack.top); } else mstrptr->len = 0; return TRUE; } /* returns TRUE if able to fit in one $STACK(level,...) of information in global variable structure "dollar_stack". * returns FALSE otherwise */ static boolean_t fill_dollar_stack_level(int array_level, int frame_level, int cur_zlevel) { mstr *mstrptr; mval tmpmval; dollar_stack_struct *dstack; assert(FALSE == dollar_stack.incomplete); /* we should not have come here if previous $STACK levels were incomplete */ dstack = &dollar_stack.array[array_level]; /* fill in $STACK(level) */ if (frame_level) get_frame_creation_info(frame_level, cur_zlevel, &tmpmval); else get_command_line(&tmpmval, FALSE); /* FALSE to indicate we want actual (not processed) command line */ /* note that tmpmval at this point will most likely point to the stringpool. but we rely on stp_gcol to free it up */ mstrptr = &dstack->mode_str; if (FALSE == fill_dollar_stack_info(&tmpmval, mstrptr)) return FALSE; /* fill in $STACK(level,"ECODE") */ dstack->ecode_ptr = (frame_level == (cur_zlevel - 1)) ? &dollar_ecode.array[dollar_ecode.index - 1] : NULL; /* fill in $STACK(level,"PLACE") */ get_frame_place_mcode(frame_level, DOLLAR_STACK_PLACE, cur_zlevel, &tmpmval); mstrptr = &dstack->place_str; if (FALSE == fill_dollar_stack_info(&tmpmval, mstrptr)) return FALSE; /* fill in $STACK(level,"MCODE") */ get_frame_place_mcode(frame_level, DOLLAR_STACK_MCODE, cur_zlevel, &tmpmval); mstrptr = &dstack->mcode_str; if (FALSE == fill_dollar_stack_info(&tmpmval, mstrptr)) return FALSE; return TRUE; } boolean_t ecode_add(mstr *str) /* add "str" to $ECODE and return whether SUCCESS or FAILURE as TRUE/FALSE */ { int ecode_index, stack_index; boolean_t shrink; int cur_zlevel, level; char eclostmid_buf[MAX_DIGITS_IN_INT + STR_LIT_LEN(",Z,")], *dest; ssize_t space_left, eclostmid_len; error_def(ERR_ECLOSTMID); dest = &eclostmid_buf[0]; *dest++ = ','; *dest++ = 'Z'; dest = (char *)i2asc((unsigned char *)dest, ERR_ECLOSTMID); *dest++ = ','; eclostmid_len = dest - &eclostmid_buf[0]; assert(SIZEOF(eclostmid_buf) >= eclostmid_len); assert(str->len < DOLLAR_ECODE_ALLOC); space_left = dollar_ecode.top - dollar_ecode.end; ecode_index = dollar_ecode.index; shrink = FALSE; if (space_left < str->len) { shrink = TRUE; assert(1 == shrink); /* since we need a value of 1 (instead of any non-zero) for usage below */ space_left -= eclostmid_len - 1;/* note : space_left can become negative but code below handles that */ } if (ecode_index >= (DOLLAR_ECODE_MAXINDEX - shrink)) { assert(DOLLAR_ECODE_MAXINDEX >= ecode_index); if (DOLLAR_ECODE_MAXINDEX == ecode_index) { DECR_ECODE_INDEX(ecode_index); shrink = TRUE; } if (shrink) { DECR_ECODE_INDEX(ecode_index); assert((DOLLAR_ECODE_MAXINDEX - 2) == ecode_index); } } assert(ecode_index < DOLLAR_ECODE_MAXINDEX); for ( ; space_left < (int)str->len; ) /* note explicit typecasting to make sure it is a signed comparison */ { ecode_index--; if (1 > ecode_index) /* if ecode_index == -1 ==> str->len > DOLLAR_ECODE_ALLOC so nothing can be done in PRO */ return FALSE; /* if ecode_index == 0 ==> first ECODE needs to be overlaid. we do not want to do that. */ space_left += dollar_ecode.array[ecode_index].ecode_str.len - 1; } for (stack_index = 0; stack_index < dollar_stack.index; stack_index++) { if (dollar_stack.array[stack_index].ecode_ptr > &dollar_ecode.array[ecode_index]) return FALSE; /* do not want to overlay any ECODE that $STACK(level,"ECODE") is pointing to */ } assert(0 <= ecode_index); if (dollar_ecode.index != ecode_index) { dollar_ecode.end = dollar_ecode.array[ecode_index].ecode_str.addr; dollar_ecode.index = ecode_index; } if (shrink) { INCR_ECODE_INDEX(dollar_ecode.index, &eclostmid_buf[0], (mstr_len_t)eclostmid_len); } INCR_ECODE_INDEX(dollar_ecode.index, str->addr, str->len); if ((1 == dollar_ecode.index) || ((!dollar_stack.incomplete) && (2 == dollar_ecode.index) && (dollar_ecode.first_ecode_error_frame == error_frame))) { /* need to fill in $STACK entries if either the first ECODE or if an error in the first ECODE error-handler. * do not fill in nested error $STACK info if the first ECODE's $STACK info itself was incompletely filled in */ if (1 == dollar_ecode.index) { /* first ECODE. note down error_frame info in "first_ecode_error_frame" as well as $STACK(level) info */ dollar_ecode.first_ecode_error_frame = frame_pointer; assert(0 == dollar_stack.index); } cur_zlevel = dollar_zlevel(); assert(dollar_stack.index <= cur_zlevel); for (level = dollar_stack.index; level < MIN(cur_zlevel, DOLLAR_STACK_MAXINDEX); ) { /* we do not store $STACK(level) info for levels > 256 */ if (fill_dollar_stack_level(level, level, cur_zlevel)) level++; /* update array_level only if we had enough space to fill in all of above */ else break; } if ((2 == dollar_ecode.index) && (cur_zlevel == dollar_stack.index) && (DOLLAR_STACK_MAXINDEX > cur_zlevel)) { /* if nested error occurred at the same frame_level as the first error, * store $STACK information for the nested error in $STACK(frame_level+1) */ assert(level == dollar_stack.index); if (fill_dollar_stack_level(level, cur_zlevel - 1, cur_zlevel)) level++; } dollar_stack.index = level; } return TRUE; } fis-gtm-V7.0-005/sr_port/dse_integ.c0000644000032200000250000000463614342376331016161 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "cli.h" #include "util.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" #include "cert_blk.h" GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; error_def(ERR_DSEBLKRDFAIL); void dse_integ(void) { block_id blk; boolean_t was_crit, was_hold_onto_crit; cache_rec_ptr_t dummy_cr; char util_buff[MAX_UTIL_LEN]; int util_len; int4 dummy_int, nocrit_present; sm_uc_ptr_t bp; unsigned char *r_ptr; char key_buff[MAX_KEY_SZ + 1]; int key_len; gv_namehead *gvt = NULL; if (BADDSEBLK == (blk = dse_getblk("BLOCK", DSEBMLOK, DSEBLKCUR))) /* WARNING: assignment */ return; memcpy(util_buff, "!/Checking integrity of block ", 30); util_len = 30; util_len += i2hexl_nofill(blk, (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], ":", 1); util_len += 1; util_buff[util_len] = 0; util_out_print(util_buff, TRUE); was_crit = cs_addrs->now_crit; nocrit_present = (CLI_NEGATED == cli_present("CRIT")); DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); if (!(bp = t_qread(blk, &dummy_int, &dummy_cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); if (0 == ((blk_hdr_ptr_t)bp)->levl) { r_ptr = (unsigned char *)((sm_uc_ptr_t)bp + SIZEOF(blk_hdr)) + SIZEOF(rec_hdr); for (key_len = 0; KEY_DELIMITER != *r_ptr; r_ptr++) key_buff[key_len++] = *r_ptr;; gvt = dse_find_gvt(gv_cur_region, (char *)key_buff, (key_len)); } if (TRUE == cert_blk(gv_cur_region, blk, (blk_hdr_ptr_t)bp, 0, RTS_ERROR_ON_CERT_FAIL, gvt)) util_out_print("!/ No errors detected.!/", TRUE); else util_out_print(NULL, TRUE); DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return; } fis-gtm-V7.0-005/sr_port/dse_is_blk_free.c0000755000032200000250000000261714342376331017317 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "gdsroot.h" #include "gdsblk.h" #include "gdsbml.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" GBLREF sgmnt_addrs *cs_addrs; error_def(ERR_DSEBLKRDFAIL); boolean_t dse_is_blk_free (block_id blk, sm_int_ptr_t cycle, cache_rec_ptr_ptr_t cr) { sm_uc_ptr_t bp; int4 status; block_id index, offset; index = (blk / cs_addrs->hdr->bplmap) * cs_addrs->hdr->bplmap; offset = blk - index; if (!(bp = t_qread (index, cycle, cr))) rts_error_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) ERR_DSEBLKRDFAIL); assert(offset == (int4)offset); /* offset is an index to an lmap and should never be larger then BLKS_PER_LMAP */ status = dse_lm_blk_free((int4)offset, bp + SIZEOF(blk_hdr)); return (0 != status); /* status == 00 => busy; 01, 11, 10 (not currently legal) => free */ } fis-gtm-V7.0-005/sr_port/dse_is_blk_in.c0000755000032200000250000000414514342376331017002 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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 "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "dsefind.h" #include "dse.h" GBLREF block_id patch_find_blk, patch_path[MAX_BT_DEPTH + 1], patch_path1[MAX_BT_DEPTH + 1]; GBLREF boolean_t patch_find_root_search; GBLREF int4 patch_offset[MAX_BT_DEPTH + 1], patch_offset1[MAX_BT_DEPTH + 1]; GBLREF short int patch_dir_path_count, patch_path_count; GBLREF sgmnt_addrs *cs_addrs; int dse_is_blk_in(sm_uc_ptr_t rp, sm_uc_ptr_t r_top, short size) { char targ_key[MAX_KEY_SZ + 1]; sm_uc_ptr_t key_top; memcpy(targ_key, rp + SIZEOF(rec_hdr), size); if ((patch_find_blk != patch_path[0]) && !dse_order(patch_path[0], &patch_path[1], patch_offset, targ_key, size, 0)) return FALSE; patch_dir_path_count = patch_path_count; if (!patch_find_root_search || (patch_find_blk != patch_path[patch_path_count - 1])) { if ((0 >= patch_path[patch_path_count - 1]) || (patch_path[patch_path_count] > cs_addrs->ti->total_blks)) return FALSE; patch_find_root_search = FALSE; for (key_top = rp + SIZEOF(rec_hdr); (*key_top++ || *key_top++) && (key_top < r_top);) ; size = key_top - rp - SIZEOF(rec_hdr); if (0 > size) size = 0; else if (SIZEOF(targ_key) < size) size = SIZEOF(targ_key); memcpy(targ_key, rp + SIZEOF(rec_hdr), size); patch_path1[0] = patch_path[patch_path_count - 1]; patch_path[patch_path_count - 1] = 0; patch_path_count = 1; if ((patch_find_blk != patch_path1[0]) && !dse_order(patch_path1[0], &patch_path1[1], patch_offset1, targ_key, size, 0)) return FALSE; } else patch_path_count = 0; return TRUE; } fis-gtm-V7.0-005/sr_port/dse_ksrch.c0000644000032200000250000001017114342376331016154 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "copy.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" #include "mmemory.h" GBLDEF block_id ksrch_root; GBLREF boolean_t patch_find_root_search; GBLREF char patch_comp_key[MAX_KEY_SZ + 1]; GBLREF short int patch_path_count; GBLREF sgmnt_addrs *cs_addrs; GBLREF unsigned short patch_comp_count; error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEINVALBLKID); int dse_ksrch(block_id srch, block_id_ptr_t pp, int4 *off, char *targ_key, int targ_len) { boolean_t long_blk_id; cache_rec_ptr_t dummy_cr; int rsize, tmp_cmpc; int4 cmp, dummy_int; long blk_id_size; sm_uc_ptr_t blk_id, bp, b_top, key_top, rp, r_top; ssize_t size; unsigned short cc, dummy_short; if (!(bp = t_qread(srch, &dummy_int, &dummy_cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); if (((blk_hdr_ptr_t)bp)->bver > BLK_ID_32_VER) /* Check blk version to see if using 32 or 64 bit block_id */ { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) b_top = bp + cs_addrs->hdr->blk_size; else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) b_top = bp + SIZEOF(blk_hdr); else b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; CLEAR_DSE_COMPRESS_KEY; *off = 0; for (rp = bp + SIZEOF(blk_hdr); rp < b_top; rp = r_top) { *off = (int4)(rp - bp); GET_SHORT(dummy_short, &((rec_hdr_ptr_t)rp)->rsiz); rsize = dummy_short; if (rsize < SIZEOF(rec_hdr)) r_top = rp + SIZEOF(rec_hdr); else r_top = rp + rsize; if (r_top > b_top) r_top = b_top; if ((r_top - rp) < ((((blk_hdr_ptr_t)bp)->levl ? blk_id_size : MIN_DATA_SIZE) + SIZEOF(rec_hdr))) { *pp = 0; break; } for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top ; ) if (!*key_top++ && !*key_top++) break; if (((blk_hdr_ptr_t)bp)->levl && (key_top > (blk_id = (r_top - blk_id_size)))) key_top = blk_id; if (EVAL_CMPC((rec_hdr_ptr_t)rp) > patch_comp_count) cc = patch_comp_count; else cc = EVAL_CMPC((rec_hdr_ptr_t)rp); size = (ssize_t)(key_top - rp - SIZEOF(rec_hdr)); if (size > MAX_KEY_SZ - cc) size = MAX_KEY_SZ - cc; if (size < 0) size = 0; memcpy(&patch_comp_key[cc], rp + SIZEOF(rec_hdr), size); assert((cc + size) == (unsigned short)(cc + size)); patch_comp_count = (unsigned short)(cc + size); if (long_blk_id == TRUE) # ifdef BLK_NUM_64BIT GET_BLK_ID_64(*pp, key_top); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif else GET_BLK_ID_32(*pp, key_top); cmp = memvcmp(targ_key, targ_len, &patch_comp_key[0], patch_comp_count); if (0 > cmp) break; if (!cmp) { if (0 != ((blk_hdr_ptr_t)bp)->levl) break; if (patch_find_root_search) { for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top; ) if (!*key_top++ && !*key_top++) break; if (long_blk_id == TRUE) # ifdef BLK_NUM_64BIT GET_BLK_ID_64(ksrch_root, key_top); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif else GET_BLK_ID_32(ksrch_root, key_top); } return TRUE; } } patch_path_count++; if (((blk_hdr_ptr_t) bp)->levl && (*pp > 0) && (*pp < cs_addrs->ti->total_blks) && (*pp % cs_addrs->hdr->bplmap) && dse_ksrch(*pp, pp + 1, off + 1, targ_key, targ_len)) return TRUE; return FALSE; } fis-gtm-V7.0-005/sr_port/dse_lm_blk_free.c0000755000032200000250000000342514342376331017312 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "gdsbml.h" #include "cli.h" #include "dse.h" /** * Returns a bit mask of the status of a block in a local bitmap * * Takes an index for a block in a local bit map (not the block number) and a pointer to a local bit map. * Returns the 2-bit status of the block corresponding to block index in the local bit map. * * @param[in] blk The index of a block in it's local bit map (block_id % BLKS_PER_LMAP) * @param[in] base_addr Pointer to the start of the local bit map * @return A bit mask of the status of the block at index blk in local bit map base_addr */ int4 dse_lm_blk_free(int4 blk, sm_uc_ptr_t base_addr) { sm_uc_ptr_t ptr; unsigned char valid; int4 bits; /* blk is an index into a local bit map and should never be larger the BLKS_PER_LMAP */ assert(blk <= BLKS_PER_LMAP); ptr = base_addr + (blk * BML_BITS_PER_BLK) / 8; valid = *ptr; switch (blk % (8 / BML_BITS_PER_BLK)) { case 0: break; case 1: valid = valid >> BML_BITS_PER_BLK; break; case 2: valid = valid >> 2 * BML_BITS_PER_BLK; break; case 3: valid = valid >> 3 * BML_BITS_PER_BLK; break; } bits = valid & 3; return bits; } fis-gtm-V7.0-005/sr_port/dse_m_rest.c0000644000032200000250000001300114342376331016326 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /******************************************************************************* * * MODULE NAME: PATCH_M_REST * * CALLING SEQUENCE: void dse_m_rest (blk, bml_list, bml_size) * block_id blk; * unsigned char *bml_list; * int4 bml_size; * * DESCRIPTION: This is a recursive routine kicked off by PATCH_MAPS * in the RESTORE_ALL function. It reconstructs a * a local copy of all the local bit maps. * * HISTORY: * *******************************************************************************/ #include "mdef.h" #include "gtm_string.h" #include "gdsroot.h" #include "gdsdbver.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "copy.h" #include "util.h" #include "gdsbml.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" GBLREF sgmnt_addrs *cs_addrs; error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEINVALBLKID); void dse_m_rest (block_id blk, /* block number */ unsigned char *bml_list, /* start of local list of local bit maps */ int4 bml_size, /* size of each entry in *bml_list */ v_block_id_ptr_t blks_ptr, /* total free blocks */ bool in_dir_tree) { block_id next, bml_index; boolean_t long_blk_id; /* does the current block use 64-bit block ids */ cache_rec_ptr_t dummy_cr; int4 dummy_int, bplmap, util_len; long blk_id_size; short level, rsize; sm_uc_ptr_t bp, b_top, rp, r_top, bml_ptr, np, ptr; unsigned char util_buff[MAX_UTIL_LEN]; if (!(bp = t_qread (blk, &dummy_int, &dummy_cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) b_top = bp + cs_addrs->hdr->blk_size; else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) b_top = bp + SIZEOF(blk_hdr); else b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; if (((blk_hdr_ptr_t)bp)->bver > BLK_ID_32_VER) /* Check blk version to see if using 32 or 64 bit block_id */ { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } level = ((blk_hdr_ptr_t)bp)->levl; bplmap = cs_addrs->hdr->bplmap; for (rp = bp + SIZEOF(blk_hdr); rp < b_top ;rp = r_top) { if (in_dir_tree || level > 1) /* reread block because it may have been flushed from read */ { if (!(np = t_qread(blk,&dummy_int,&dummy_cr))) /* cache due to LRU buffer scheme and reads in recursive */ RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); /* calls to dse_m_rest. */ if (np != bp) { b_top = np + (b_top - bp); rp = np + (rp - bp); r_top = np + (r_top - bp); bp = np; if (((blk_hdr_ptr_t)bp)->bver > BLK_ID_32_VER) /* Check if using 32 or 64 bit block_id */ { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } } } GET_SHORT(rsize,&((rec_hdr_ptr_t)rp)->rsiz); r_top = rp + rsize; if (r_top > b_top) r_top = b_top; if ((r_top - rp) < (SIZEOF(rec_hdr) + blk_id_size)) break; if (in_dir_tree && level == 0) { for (ptr = rp + SIZEOF(rec_hdr); ; ) { if (*ptr++ == 0 && *ptr++ == 0) break; } if (long_blk_id == TRUE) # ifdef BLK_NUM_64BIT GET_BLK_ID_64(next,ptr); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif else GET_BLK_ID_32(next,ptr); } else { if (long_blk_id == TRUE) # ifdef BLK_NUM_64BIT GET_BLK_ID_64(next, r_top - SIZEOF(block_id_64)); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif else GET_BLK_ID_32(next, r_top - SIZEOF(block_id_32)); } if ((next < 0) || (next >= cs_addrs->ti->total_blks) || ((next / bplmap) * bplmap == next)) { memcpy(util_buff,"Invalid pointer in block ",25); util_len = 25; util_len += i2hexl_nofill(blk, &util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], " record offset ",15); util_len += 15; assert((rp - bp) == (int)(rp - bp)); /* (rp - bp) is a record offset in a block and should fit in an int */ /* Using MAX_HEX_SHORT for int value because to save line space * since the value should always fit in 2-bytes */ util_len += i2hex_nofill((int)(rp - bp), &util_buff[util_len], MAX_HEX_SHORT); util_buff[util_len] = 0; util_out_print((char*)util_buff,TRUE); continue; } bml_index = next / bplmap; bml_ptr = bml_list + (bml_index * bml_size); if (bml_busy(next - ((next / bplmap) * bplmap), bml_ptr + SIZEOF(blk_hdr))) { *blks_ptr = *blks_ptr - 1; if (((blk_hdr_ptr_t) bp)->levl > 1) dse_m_rest (next, bml_list, bml_size, blks_ptr, in_dir_tree); else if (in_dir_tree) { assert(((blk_hdr_ptr_t) bp)->levl == 0 || ((blk_hdr_ptr_t) bp)->levl == 1); dse_m_rest (next, bml_list, bml_size, blks_ptr, ((blk_hdr_ptr_t)bp)->levl); } } } return; } fis-gtm-V7.0-005/sr_port/dse_maps.c0000644000032200000250000001730214342376331016005 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_time.h" #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "gdsbml.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "cli.h" #include "util.h" #include "send_msg.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" #include "bit_set.h" #include "bit_clear.h" #include "t_begin_crit.h" #include "t_write.h" #include "t_end.h" #include "longset.h" /* needed for cws_insert.h */ #include "cws_insert.h" #include "process_deferred_stale.h" #include "gvcst_blk_build.h" GBLREF boolean_t unhandled_stale_timer_pop; GBLREF char *update_array, *update_array_ptr; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF short crash_count; GBLREF srch_hist dummy_hist; GBLREF uint4 update_array_size; error_def(ERR_DBRDONLY); error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEFAIL); void dse_maps(void) { blk_segment *bs1, *bs_ptr; block_id blk, blk_index, bml_blk, bml_index, bml_list_size, total_blks; boolean_t was_crit; cache_rec_ptr_t dummy_cr; char util_buff[MAX_UTIL_LEN]; int util_len; int4 blk_seg_cnt, blk_size; /* needed for BLK_INIT, BLK_SEG and BLK_FINI macros */ int4 blks_in_bitmap, bml_size, bplmap, dummy_int; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; sm_uc_ptr_t bp; srch_blk_status blkhist; uchar_ptr_t blk_ptr; unsigned char *bml_list; csa = cs_addrs; if (CLI_PRESENT == cli_present("BUSY") || CLI_PRESENT == cli_present("FREE") || CLI_PRESENT == cli_present("MASTER") || CLI_PRESENT == cli_present("RESTORE_ALL")) { if (gv_cur_region->read_only) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); } CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ assert(&FILE_INFO(gv_cur_region)->s_addrs == csa); was_crit = csa->now_crit; UPDATE_CRASH_COUNT(csa, crash_count); csd = csa->hdr; assert(csd == cs_data); bplmap = csd->bplmap; if (0 == bplmap) { util_out_print("Cannot perform map updates: bplmap field of file header is zero.", TRUE); return; } blk_size = csd->blk_size; if (CLI_PRESENT == cli_present("RESTORE_ALL")) { total_blks = csa->ti->total_blks; assert(ROUND_DOWN2(blk_size, 2 * SIZEOF(int4)) == blk_size); bml_size = BM_SIZE(bplmap); bml_list_size = ((total_blks + bplmap - 1) / bplmap) * bml_size; bml_list = (unsigned char *)malloc(bml_list_size); for (blk_index = 0, bml_index = 0; blk_index < total_blks; blk_index += bplmap, bml_index++) bml_newmap((blk_hdr_ptr_t)(bml_list + bml_index * bml_size), bml_size, csa->ti->curr_tn, csd->desired_db_format); if (!was_crit) { grab_crit_encr_cycle_sync(gv_cur_region, WS_59); csa->hold_onto_crit = TRUE; /* need to do this AFTER grab_crit */ } blk = get_dir_root(); assert(blk < bplmap); csa->ti->free_blocks = total_blks - DIVIDE_ROUND_UP(total_blks, bplmap); bml_busy(blk, bml_list + SIZEOF(blk_hdr)); csa->ti->free_blocks = csa->ti->free_blocks - 1; dse_m_rest(blk, bml_list, bml_size, &csa->ti->free_blocks, TRUE); for (blk_index = 0, bml_index = 0; blk_index < total_blks; blk_index += bplmap, bml_index++) { t_begin_crit(ERR_DSEFAIL); CHECK_TN(csa, csd, csd->trans_hist.curr_tn); /* can issue rts_error TNTOOLARGE */ CWS_RESET; CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ assert(csa->ti->early_tn == csa->ti->curr_tn); blk_ptr = bml_list + (bml_index * bml_size); blkhist.blk_num = blk_index; if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(1) ERR_DSEBLKRDFAIL); BLK_INIT(bs_ptr, bs1); BLK_SEG(bs_ptr, blk_ptr + SIZEOF(blk_hdr), bml_size - SIZEOF(blk_hdr)); BLK_FINI(bs_ptr, bs1); t_write(&blkhist, (unsigned char *)bs1, 0, 0, LCL_MAP_LEVL, TRUE, FALSE, GDS_WRITE_KILLTN); BUILD_AIMG_IF_JNL_ENABLED(csd, csa->ti->curr_tn); t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); } /* Fill in master map */ for (blk_index = 0, bml_index = 0; blk_index < total_blks; blk_index += bplmap, bml_index++) { /* (total_blks - blk_index) is used to determine the number of blks in the last lmap of the DB * so the value should never be larger then BLKS_PER_LMAP and thus fit in a int4 */ assert((blk_index + bplmap <= total_blks) || (BLKS_PER_LMAP >= (total_blks - blk_index))); blks_in_bitmap = (blk_index + bplmap <= total_blks) ? bplmap : (int4)(total_blks - blk_index); assert(1 < blks_in_bitmap); /* the last valid block in the database should never be a bitmap block */ if (NO_FREE_SPACE != bml_find_free(0, (bml_list + bml_index * bml_size) + SIZEOF(blk_hdr), blks_in_bitmap)) bit_set(blk_index / bplmap, csa->bmm); else bit_clear(blk_index / bplmap, csa->bmm); if (blk_index > csa->nl->highest_lbm_blk_changed) csa->nl->highest_lbm_blk_changed = blk_index; } if (!was_crit) { csa->hold_onto_crit = FALSE; /* need to do this before the rel_crit */ rel_crit(gv_cur_region); } if (unhandled_stale_timer_pop) process_deferred_stale(); free(bml_list); csd->kill_in_prog = csd->abandoned_kills = 0; return; } if (CLI_PRESENT == cli_present("FREE")) { if (BADDSEBLK == (blk = dse_getblk("BLOCK", DSENOBML, DSEBLKCUR))) /* WARNING: assignment */ return; bml_blk = blk / bplmap * bplmap; bm_setmap(bml_blk, blk, FALSE); return; } if (BADDSEBLK == (blk = dse_getblk("BLOCK", DSEBMLOK, DSEBLKCUR))) /* WARNING: assignment */ return; if (CLI_PRESENT == cli_present("BUSY")) { bml_blk = blk / bplmap * bplmap; bm_setmap(bml_blk, blk, TRUE); return; } if (CLI_PRESENT == cli_present("MASTER")) { if (!was_crit) grab_crit_encr_cycle_sync(gv_cur_region, WS_60); bml_blk = blk / bplmap * bplmap; if (dba_mm == csd->acc_meth) bp = MM_BASE_ADDR(csa) + (off_t)bml_blk * blk_size; else { assert(dba_bg == csd->acc_meth); if (!(bp = t_qread(bml_blk, &dummy_int, &dummy_cr))) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(1) ERR_DSEBLKRDFAIL); } if ((csa->ti->total_blks / bplmap) * bplmap == bml_blk) total_blks = (csa->ti->total_blks - bml_blk); else total_blks = bplmap; if (NO_FREE_SPACE == bml_find_free(0, bp + SIZEOF(blk_hdr), total_blks)) bit_clear(bml_blk / bplmap, csa->bmm); else bit_set(bml_blk / bplmap, csa->bmm); if (bml_blk > csa->nl->highest_lbm_blk_changed) csa->nl->highest_lbm_blk_changed = bml_blk; if (!was_crit) rel_crit(gv_cur_region); return; } MEMCPY_LIT(util_buff, "!/Block "); util_len = SIZEOF("!/Block ") - 1; util_len += i2hexl_nofill(blk, (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], " is marked !AD in its local bit map.!/", SIZEOF(" is marked !AD in its local bit map.!/") - 1); util_len += SIZEOF(" is marked !AD in its local bit map.!/") - 1; util_buff[util_len] = 0; if (!was_crit) grab_crit_encr_cycle_sync(gv_cur_region, WS_61); util_out_print(util_buff, TRUE, 4, dse_is_blk_free(blk, &dummy_int, &dummy_cr) ? "free" : "busy"); if (!was_crit) rel_crit(gv_cur_region); return; } fis-gtm-V7.0-005/sr_port/dse_order.c0000644000032200000250000001243514342376331016162 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "gdsdbver.h" #include "copy.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" #include "mmemory.h" GBLREF block_id patch_find_blk, patch_left_sib, patch_right_sib; GBLREF block_id patch_path[MAX_BT_DEPTH + 1], patch_path1[MAX_BT_DEPTH + 1]; GBLREF boolean_t patch_find_root_search; GBLREF char patch_comp_key[MAX_KEY_SZ + 1]; GBLREF sgmnt_addrs *cs_addrs; GBLREF short int patch_path_count; GBLREF unsigned short patch_comp_count; error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEINVALBLKID); int dse_order(block_id srch, block_id_ptr_t pp, int4 *op, char *targ_key, short int targ_len, bool dir_data_blk) { block_id last; boolean_t long_blk_id; cache_rec_ptr_t dummy_cr; int4 dummy_int; long blk_id_size; short int rsize, size; sm_uc_ptr_t bp, b_top, key_top, ptr, rp, r_top; unsigned short cc; last = 0; patch_path_count++; if (!(bp = t_qread(srch, &dummy_int, &dummy_cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); if (((blk_hdr_ptr_t)bp)->bver > BLK_ID_32_VER) { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } if (((blk_hdr_ptr_t)bp)->bsiz > cs_addrs->hdr->blk_size) b_top = bp + cs_addrs->hdr->blk_size; else if (SIZEOF(blk_hdr) > ((blk_hdr_ptr_t)bp)->bsiz) b_top = bp + SIZEOF(blk_hdr); else b_top = bp + ((blk_hdr_ptr_t)bp)->bsiz; CLEAR_DSE_COMPRESS_KEY; for (rp = bp + SIZEOF(blk_hdr); rp < b_top ;rp = r_top, last = *pp) { GET_SHORT(rsize, &((rec_hdr_ptr_t)rp)->rsiz); if (SIZEOF(rec_hdr) > rsize) r_top = rp + SIZEOF(rec_hdr); else r_top = rp + rsize; if ((r_top > b_top) || ((r_top == b_top) && ((blk_hdr*)bp)->levl)) { if (((SIZEOF(rec_hdr) + blk_id_size) != (b_top - rp)) || EVAL_CMPC((rec_hdr *)rp)) return FALSE; if (dir_data_blk && !(((blk_hdr_ptr_t)bp)->levl)) { for (ptr = rp + SIZEOF(rec_hdr); (*ptr++ || *ptr++) && (ptr <= b_top);) ; if (long_blk_id == TRUE) # ifdef BLK_NUM_64BIT GET_BLK_ID_64(*pp,ptr); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif else GET_BLK_ID_32(*pp,ptr); } else if (long_blk_id == TRUE) # ifdef BLK_NUM_64BIT GET_BLK_ID_64(*pp,b_top - SIZEOF(block_id_64)); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif else GET_BLK_ID_32(*pp,b_top - SIZEOF(block_id_32)); break; } else { if ((blk_id_size + SIZEOF(rec_hdr)) > (r_top - rp)) break; if (dir_data_blk && !((blk_hdr_ptr_t)bp)->levl) { for (ptr = rp + SIZEOF(rec_hdr); (*ptr++ || *ptr++) && (ptr <= r_top);) ; key_top = ptr; } else key_top = r_top - blk_id_size; if (EVAL_CMPC((rec_hdr_ptr_t)rp) > patch_comp_count) cc = patch_comp_count; else cc = EVAL_CMPC((rec_hdr_ptr_t)rp); size = key_top - rp - SIZEOF(rec_hdr); if ((SIZEOF(patch_comp_key) - 2 - cc) < size) size = SIZEOF(patch_comp_key) - 2 - cc; if (0 > size) size = 0; memcpy(&patch_comp_key[cc], rp + SIZEOF(rec_hdr), size); patch_comp_count = cc + size; if (long_blk_id == TRUE) # ifdef BLK_NUM_64BIT GET_BLK_ID_64(*pp, key_top); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif else GET_BLK_ID_32(*pp, key_top); if (0 >= memvcmp(targ_key, targ_len, patch_comp_key, patch_comp_count)) break; } } assert((rp - bp) == (int4)(rp - bp)); *op = (int4)(rp - bp); if ((*pp == patch_find_blk) && !dir_data_blk) { patch_left_sib = last; if (r_top < b_top) { rp = r_top; GET_SHORT(rsize, &((rec_hdr_ptr_t)r_top)->rsiz); r_top = rp + rsize; if (r_top > b_top) r_top = b_top; if (blk_id_size <= (r_top - rp)) { if (long_blk_id == TRUE) # ifdef BLK_NUM_64BIT GET_BLK_ID_64(patch_right_sib, r_top - SIZEOF(block_id_64)); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif else GET_BLK_ID_32(patch_right_sib, r_top - SIZEOF(block_id_32)); } } return TRUE; } if ((*pp > 0) && (*pp < cs_addrs->ti->total_blks) && (*pp % cs_addrs->hdr->bplmap)) { if ((1 < ((blk_hdr_ptr_t)bp)->levl) && dse_order(*pp, pp + 1, op + 1, targ_key, targ_len, 0)) return TRUE; else if ((1 == ((blk_hdr_ptr_t)bp)->levl) && patch_find_root_search) return dse_order(*pp, pp + 1, op + 1, targ_key, targ_len, 1); else if ((0 == ((blk_hdr_ptr_t)bp)->levl) && patch_find_root_search) { patch_find_root_search = FALSE; return TRUE; } } patch_path_count--; return FALSE; } fis-gtm-V7.0-005/sr_port/dse_over.c0000644000032200000250000001345114342376331016021 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_iconv.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "gdscc.h" #include "cli.h" #include "filestruct.h" #include "jnl.h" #include "io.h" #include "iosp.h" #include "util.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" #include "t_write.h" #include "t_end.h" #include "t_begin_crit.h" #include "gvcst_blk_build.h" #include "ebc_xlat.h" #include "t_abort.h" #include "stringpool.h" #include "gtm_conv.h" #include "gtm_utf8.h" #include "gtmmsg.h" GBLREF char *update_array, *update_array_ptr; GBLREF cw_set_element cw_set[]; GBLREF gd_region *gv_cur_region; GBLREF gtm_chset_t dse_over_chset; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF spdesc stringpool; GBLREF srch_hist dummy_hist; GBLREF UConverter *chset_desc[]; GBLREF uint4 update_array_size; #if defined(KEEP_zOS_EBCDIC) || defined(VMS) GBLREF iconv_t dse_over_cvtcd; #endif LITREF mstr chset_names[]; error_def(ERR_AIMGBLKFAIL); error_def(ERR_DBRDONLY); error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEFAIL); void dse_over(void) { static char *data = NULL; static int data_size; blk_segment *bs1, *bs_ptr; block_id blk; char chset_name[MAX_CHSET_NAME + 1]; int cvt_len, data_len, size; int4 blk_seg_cnt, blk_size; mstr chset_mstr, cvt_src; srch_blk_status blkhist; uchar_ptr_t lbp; uint4 offset; unsigned char *cvt_src_ptr, *cvt_dst_ptr; unsigned int insize, outsize; unsigned short name_len = 0; if (gv_cur_region->read_only) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ blk_size = cs_addrs->hdr->blk_size; if (BADDSEBLK == (blk = dse_getblk("BLOCK", DSEBMLOK, DSEBLKCUR))) /* WARNING: assignment */ return; if (CLI_PRESENT == cli_present("OCHSET")) { name_len = MAX_CHSET_NAME; if (cli_get_str("OCHSET", chset_name, &name_len) && 0 != name_len) { chset_name[name_len] = '\0'; # if defined(KEEP_zOS_EBCDIC) || defined(VMS) if ( (iconv_t)0 != dse_over_cvtcd ) { ICONV_CLOSE_CD(dse_over_cvtcd); } if (!strcmp(chset_name, INSIDE_CH_SET)) dse_over_cvtcd = (iconv_t)0; else ICONV_OPEN_CD(dse_over_cvtcd, INSIDE_CH_SET, chset_name); # else chset_mstr.addr = chset_name; chset_mstr.len = name_len; SET_ENCODING(dse_over_chset, &chset_mstr); get_chset_desc(&chset_names[dse_over_chset]); # endif } } else { # ifdef KEEP_zOS_EBCDIC if ((iconv_t)0 != dse_over_cvtcd ) { ICONV_CLOSE_CD(dse_over_cvtcd); dse_over_cvtcd = (iconv_t)0; /* default ASCII, no conversion */ } # else dse_over_chset = CHSET_M; # endif } if (CLI_PRESENT != cli_present("OFFSET")) { util_out_print("Error: offset must be specified.", TRUE); return; } if (!cli_get_hex("OFFSET", &offset)) return; if (offset < SIZEOF(blk_hdr)) { util_out_print("Error: offset too small.", TRUE); return; } if (CLI_PRESENT != cli_present("DATA")) { util_out_print("Error: data must be specified.", TRUE); return; } t_begin_crit(ERR_DSEFAIL); blkhist.blk_num = blk; if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); size = ((blk_hdr_ptr_t)blkhist.buffaddr)->bsiz; if (size < SIZEOF(blk_hdr)) size = SIZEOF(blk_hdr); else if (size >= blk_size) size = blk_size; if (offset >= size) { util_out_print("Error: offset too large.", TRUE); t_abort(gv_cur_region, cs_addrs); return; } if (NULL == data) { data = malloc(MAX_LINE); data_size = MAX_LINE; } if (FALSE == dse_data(&data[0], &data_len)) { t_abort(gv_cur_region, cs_addrs); return; } # if defined(KEEP_zOS_EBCDIC) || defined(VMS) cvt_src_ptr = cvt_dst_ptr = (unsigned char *)data; insize = outsize = (unsigned int)data_len; if ((iconv_t)0 != dse_over_cvtcd) ICONVERT(dse_over_cvtcd, &cvt_src_ptr, &insize, &cvt_dst_ptr, &outsize); /* in-place conversion */ # else cvt_src.len = (unsigned int)data_len; cvt_src.addr = data; if (CHSET_M != dse_over_chset) { cvt_len = gtm_conv(chset_desc[dse_over_chset], chset_desc[CHSET_UTF8], &cvt_src, NULL, NULL); if (cvt_len > data_size) { free(data); data = malloc(cvt_len); data_size = cvt_len; } memcpy(data, stringpool.free, cvt_len); data_len = cvt_len; } # endif if (offset + data_len > size) { util_out_print("Error: data will not fit in block at given offset.", TRUE); t_abort(gv_cur_region, cs_addrs); return; } lbp = (uchar_ptr_t)malloc(blk_size); memcpy (lbp, blkhist.buffaddr, blk_size); memcpy(lbp + offset, &data[0], data_len); BLK_INIT(bs_ptr, bs1); BLK_SEG(bs_ptr, (uchar_ptr_t)lbp + SIZEOF(blk_hdr), (int)((blk_hdr_ptr_t)lbp)->bsiz - SIZEOF(blk_hdr)); if (!BLK_FINI(bs_ptr, bs1)) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_AIMGBLKFAIL, 3, &blk, DB_LEN_STR(gv_cur_region)); free(lbp); t_abort(gv_cur_region, cs_addrs); return; } t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)lbp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); BUILD_AIMG_IF_JNL_ENABLED(cs_data, cs_addrs->ti->curr_tn); t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); return; } fis-gtm-V7.0-005/sr_port/dse_page.c0000755000032200000250000000123714342376331015764 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "util.h" #include "dse.h" void dse_page(void) { util_out_print("!^",TRUE); return; } fis-gtm-V7.0-005/sr_port/dse_puttime.h0000755000032200000250000000120314342376331016535 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef DST_PUTTIME_H_INCLUDED #define DST_PUTTIME_H_INCLUDED void dse_puttime(int_ptr_t time, char *c, bool flush); #endif fis-gtm-V7.0-005/sr_port/dse_r_dmp.c0000644000032200000250000000576414342376331016157 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_signal.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "cli.h" #include "dse.h" #include "util.h" #include "skan_offset.h" #include "skan_rnum.h" /* Include prototypes */ #include "t_qread.h" GBLREF gd_region *gv_cur_region; GBLREF int patch_is_fdmp, patch_rec_counter; GBLREF sgmnt_addrs *cs_addrs; GBLREF VSIG_ATOMIC_T util_interrupt; error_def(ERR_CTRLC); error_def(ERR_DSEBLKRDFAIL); boolean_t dse_r_dmp(void) { block_id blk; boolean_t was_crit, was_hold_onto_crit; cache_rec_ptr_t dummy_cr; int4 count, dummy_int, nocrit_present; sm_uc_ptr_t bp, b_top, rp; if (BADDSEBLK == (blk = dse_getblk("BLOCK", DSENOBML, DSEBLKCUR))) /* WARNING: assignment */ return FALSE; if (CLI_PRESENT == cli_present("COUNT")) { if (!cli_get_hex("COUNT", (uint4 *)&count)) return FALSE; } else count = 1; was_crit = cs_addrs->now_crit; nocrit_present = (CLI_NEGATED == cli_present("CRIT")); DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); if (!(bp = t_qread(blk, &dummy_int, &dummy_cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) b_top = bp + cs_addrs->hdr->blk_size; else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) b_top = bp + SIZEOF(blk_hdr); else b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; if (((blk_hdr_ptr_t) bp)->levl && patch_is_fdmp) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); util_out_print("Error: cannot perform GLO/ZWR dump on index block.", TRUE); return FALSE; } if (CLI_PRESENT == cli_present("RECORD")) { if (!(rp = skan_rnum (bp, FALSE))) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return FALSE; } } else if (!(rp = skan_offset (bp, FALSE))) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); return FALSE; } util_out_print(0, TRUE); for ( ; 0 < count; count--) { if (util_interrupt || !(rp = dump_record(rp, blk, bp, b_top))) break; patch_rec_counter += 1; } DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); if (util_interrupt) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CTRLC); else if (CLI_NEGATED == cli_present("HEADER")) util_out_print(0, TRUE); return TRUE; } fis-gtm-V7.0-005/sr_port/dse_range.c0000644000032200000250000001717514342376331016151 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_signal.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "gdsdbver.h" #include "cli.h" #include "copy.h" #include "min_max.h" #include "util.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" GBLREF block_id patch_find_blk, patch_path[MAX_BT_DEPTH + 1]; GBLREF boolean_t patch_find_root_search; GBLREF gd_region *gv_cur_region; GBLREF short int patch_path_count; GBLREF sgmnt_addrs *cs_addrs; GBLREF VSIG_ATOMIC_T util_interrupt; error_def(ERR_CTRLC); error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEINVALBLKID); void dse_range(void) { block_id from, to, blk, blk_child; boolean_t busy_matters, free, got_lonely_star, index, lost, low, star, up, was_crit, was_hold_onto_crit, long_blk_id; cache_rec_ptr_t dummy_cr; char level, lower[MAX_KEY_SZ + 1], targ_key[MAX_KEY_SZ + 1], upper[MAX_KEY_SZ + 1]; int cnt, dummy, lower_len, upper_len; int4 dummy_int, nocrit_present; long blk_id_size; short int rsize, size, size1; sm_uc_ptr_t bp, b_top, key_bot, key_top, key_top1, rp, r_top; if (CLI_PRESENT == cli_present("FROM")) { if (BADDSEBLK == (from = dse_getblk("FROM", DSEBMLOK, DSEBLKNOCUR))) /* WARNING: assignment */ return; } else from = 1; if (CLI_PRESENT == cli_present("TO")) { if (BADDSEBLK == (to = dse_getblk("TO", DSEBMLOK, DSEBLKNOCUR))) /* WARNING: assignment */ return; } else to = cs_addrs->ti->total_blks - 1; if (low = (CLI_PRESENT == cli_present("LOWER"))) /* WARNING: assignment */ { if (!dse_getki(&lower[0], &lower_len, LIT_AND_LEN("LOWER"))) return; } if (up = (CLI_PRESENT == cli_present("UPPER"))) /* WARNING: assignment */ { if (!dse_getki(&upper[0], &upper_len, LIT_AND_LEN("UPPER"))) return; } star = (CLI_PRESENT == cli_present("STAR")); if (!low && !up && !star) { util_out_print("Must specify star, or a lower or upper key limit.", TRUE); return; } index = (CLI_PRESENT == cli_present("INDEX")); lost = (CLI_PRESENT == cli_present("LOST")); dummy = cli_present("BUSY"); if (CLI_PRESENT == dummy) { busy_matters = TRUE; free = FALSE; } else if (CLI_NEGATED == dummy) busy_matters = free = TRUE; else busy_matters = free = FALSE; patch_path[0] = get_dir_root(); cnt = 0; was_crit = cs_addrs->now_crit; nocrit_present = (CLI_NEGATED == cli_present("CRIT")); DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); for (blk = from; blk <= to ;blk++) { if (util_interrupt) { DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CTRLC); break; } if (!(blk % cs_addrs->hdr->bplmap)) continue; if (!(bp = t_qread(blk, &dummy_int, &dummy_cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); if (((blk_hdr_ptr_t)bp)->bver > BLK_ID_32_VER) { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } level = ((blk_hdr_ptr_t)bp)->levl; if (index && (0 == level)) continue; if (busy_matters && (free != dse_is_blk_free(blk, &dummy_int, &dummy_cr))) continue; if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) b_top = bp + cs_addrs->hdr->blk_size; else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) b_top = bp + SIZEOF(blk_hdr); else b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; rp = bp + SIZEOF(blk_hdr); GET_SHORT(rsize, &((rec_hdr_ptr_t) rp)->rsiz); if (rsize < SIZEOF(rec_hdr)) r_top = rp + SIZEOF(rec_hdr); else r_top = rp + rsize; if (r_top >= b_top) r_top = b_top; got_lonely_star = FALSE; if (((blk_hdr_ptr_t)bp)->levl) { key_top = r_top - blk_id_size; if (star && (r_top == b_top)) got_lonely_star = TRUE; } else { if (!up && !low) continue; for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top ; ) if (!*key_top++ && !*key_top++) break; } if (!got_lonely_star) { key_bot = rp + SIZEOF(rec_hdr); size = key_top - key_bot; if (size <= 0) continue; if (size > SIZEOF(targ_key)) size = SIZEOF(targ_key); if (lost) { for (key_top1 = rp + SIZEOF(rec_hdr); key_top1 < r_top ; ) if (!*key_top1++) break; size1 = key_top1 - rp - SIZEOF(rec_hdr); if (size1 > SIZEOF(targ_key)) size1 = SIZEOF(targ_key); patch_find_root_search = TRUE; patch_path_count = 1; patch_find_blk = blk; if (dse_is_blk_in(rp, r_top, size1)) continue; } if (low && memcmp(lower, key_bot, MIN(lower_len, size)) > 0) continue; if (up && memcmp(upper, key_bot, MIN(upper_len, size)) < 0) continue; } else { got_lonely_star = FALSE; if (lost) { if (long_blk_id == TRUE) { # ifdef BLK_NUM_64BIT GET_BLK_ID_64(blk_child, key_top); # else DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else GET_BLK_ID_32(blk_child, key_top); if (!(bp = t_qread(blk_child, &dummy_int, &dummy_cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); if (((blk_hdr_ptr_t)bp)->bver > BLK_ID_32_VER) { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } if (((blk_hdr_ptr_t)bp)->bsiz > cs_addrs->hdr->blk_size) b_top = bp + cs_addrs->hdr->blk_size; else if (((blk_hdr_ptr_t)bp)->bsiz < SIZEOF(blk_hdr)) b_top = bp + SIZEOF(blk_hdr); else b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; rp = bp + SIZEOF(blk_hdr); GET_SHORT(rsize, &((rec_hdr_ptr_t) rp)->rsiz); if (rsize < SIZEOF(rec_hdr)) r_top = rp + SIZEOF(rec_hdr); else r_top = rp + rsize; if (r_top >= b_top) r_top = b_top; if (((blk_hdr_ptr_t)bp)->levl) key_top = r_top - blk_id_size; for (key_top1 = rp + SIZEOF(rec_hdr); key_top1 < r_top ; ) if (!*key_top1++) break; size1 = key_top1 - rp - SIZEOF(rec_hdr); if (size1 > 0) { if (size1 > SIZEOF(targ_key)) size1 = SIZEOF(targ_key); patch_find_root_search = TRUE; patch_path_count = 1; patch_find_blk = blk; if (dse_is_blk_in(rp, r_top, size1)) continue; } } } if (!cnt++) util_out_print("!/Blocks in the specified key range:", TRUE); util_out_print("Block: 0x!16@XQ Level: !2UL", TRUE, &blk, level); } DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); if (cnt) util_out_print("Found !UL blocks", TRUE, cnt); else util_out_print("None found.", TRUE); return; } fis-gtm-V7.0-005/sr_port/dse_rest.c0000644000032200000250000001366314342376331016030 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "gtm_string.h" #include "mdef.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "gdscc.h" #include "dse.h" #include "cli.h" #include "filestruct.h" #include "jnl.h" #include "util.h" /* Include prototypes */ #include "t_qread.h" #include "t_write.h" #include "t_end.h" #include "t_begin_crit.h" #include "process_deferred_stale.h" #include "gvcst_blk_build.h" #include "t_abort.h" #include "gtmmsg.h" GBLREF block_id patch_curr_blk; GBLREF char *update_array, *update_array_ptr; GBLREF cw_set_element cw_set[]; GBLREF gd_addr *original_header; GBLREF gd_region *gv_cur_region; GBLREF save_strct patch_save_set[PATCH_SAVE_SIZE]; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF srch_hist dummy_hist; GBLREF uint4 patch_save_count, update_array_size; error_def(ERR_AIMGBLKFAIL); error_def(ERR_DBRDONLY); error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEFAIL); error_def(ERR_DSENOTOPEN); error_def(ERR_NOREGION); void dse_rest(void) { blk_segment *bs1, *bs_ptr; block_id to, from; #ifndef BLK_NUM_64BIT block_id_64 from2; #endif char util_buff[MAX_UTIL_LEN], rn[MAX_RN_LEN + 1]; gd_binding *map; gd_region *region; int i, util_len; int4 blk_seg_cnt, blk_size; srch_blk_status blkhist; uchar_ptr_t lbp; uint4 found_index, version; unsigned short rn_len; if (gv_cur_region->read_only) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ if (cli_get_int("VERSION", (int4 *)&version)) { if (0 == version) { util_out_print("Error: no such version.", TRUE); return; } } else version = 0; if (BADDSEBLK == (to = dse_getblk("BLOCK", DSEBMLOK, DSEBLKCUR))) /* WARNING: assignment */ return; if (CLI_PRESENT == cli_present("FROM")) { /* don't use dse_getblk because we're working out of the save set, not the db */ #ifdef BLK_NUM_64BIT if (!cli_get_hex64("FROM", (gtm_uint8 *)(&from))) from = patch_curr_blk; #else if (!cli_get_hex64("FROM", (gtm_uint8 *)(&from2))) from = patch_curr_blk; else { assert(from2 == (block_id_32)from2); from = (block_id_32)from2; } #endif } else from = to; if (CLI_PRESENT == cli_present("REGION")) { rn_len = SIZEOF(rn); if (!cli_get_str("REGION", rn, &rn_len)) return; for (i = 0; i < rn_len; i++) rn[i] = TOUPPER(rn[i]); /* Region names are always upper-case ASCII and thoroughly NUL terminated */ for ( ; i < ARRAYSIZE(rn); i++) rn[i] = 0; found_index = 0; for (i = 0, region = original_header->regions; i < original_header->n_regions ;i++, region++) if (found_index = !memcmp(®ion->rname[0], &rn[0], MAX_RN_LEN)) /* WARNING: assignment */ break; if (!found_index) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NOREGION, 2, rn_len, rn); return; } if (!region->open) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_DSENOTOPEN, 2, rn_len, rn); return; } } else region = gv_cur_region; found_index = 0; for (i = 0; i < patch_save_count; i++) { if ((patch_save_set[i].blk == from) && (patch_save_set[i].region == region)) { if (version == patch_save_set[i].ver) { assert(version); found_index = i + 1; break; } if (!version) { if (found_index) { util_out_print("Error: save version number must be specified.", TRUE); return; } found_index = i + 1; } } } if (0 == found_index) { if (version) util_out_print("Error: Version !UL of block !XL not found in set of saved blocks", TRUE, version, from); else util_out_print("Error: Block !XL not found in set of saved blocks", TRUE, from); return; } if (!version) { i = found_index - 1; version = patch_save_set[i].ver; } memcpy(util_buff, "!/Restoring block ", 18); util_len = 18; util_len += i2hexl_nofill(to, (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); memcpy(&util_buff[util_len], " from version !UL", 17); util_len += 17; util_buff[util_len] = 0; assert(ARRAYSIZE(util_buff) >= util_len); util_out_print(util_buff, FALSE, version); if (to != from) { memcpy(util_buff, " of block ", 10); util_len = 10; util_len += i2hexl_nofill(from, (uchar_ptr_t)&util_buff[util_len], MAX_HEX_INT8); util_buff[util_len] = 0; assert(ARRAYSIZE(util_buff) >= util_len); util_out_print(util_buff, FALSE); } if (region != gv_cur_region) util_out_print(" in region !AD", FALSE, LEN_AND_STR(rn)); util_out_print("!/", TRUE); t_begin_crit(ERR_DSEFAIL); if (to >= cs_addrs->ti->total_blks) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); blk_size = cs_addrs->hdr->blk_size; blkhist.blk_num = to; if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); lbp = (uchar_ptr_t)patch_save_set[i].bp; BLK_INIT(bs_ptr, bs1); BLK_SEG(bs_ptr, (uchar_ptr_t)lbp + SIZEOF(blk_hdr), (int)((blk_hdr_ptr_t)lbp)->bsiz - SIZEOF(blk_hdr)); if (!BLK_FINI(bs_ptr, bs1)) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_AIMGBLKFAIL, 3, &to, DB_LEN_STR(gv_cur_region)); t_abort(gv_cur_region, cs_addrs); return; } t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)lbp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); BUILD_AIMG_IF_JNL_ENABLED_AND_T_END_WITH_EFFECTIVE_TN(cs_addrs, cs_data, ((blk_hdr_ptr_t)lbp)->tn, &dummy_hist); return; } fis-gtm-V7.0-005/sr_port/dse_rmrec.c0000644000032200000250000001437614342376331016165 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "gdsdbver.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "gdscc.h" #include "cli.h" #include "copy.h" #include "filestruct.h" #include "jnl.h" #include "util.h" #include "skan_offset.h" #include "skan_rnum.h" #include "dse.h" /* Include prototypes */ #include "t_qread.h" #include "t_write.h" #include "t_end.h" #include "t_begin_crit.h" #include "gvcst_blk_build.h" #include "t_abort.h" #include "gtmmsg.h" GBLREF char patch_comp_key[MAX_KEY_SZ + 1], *update_array, *update_array_ptr; GBLREF cw_set_element cw_set[]; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF srch_hist dummy_hist; GBLREF uint4 update_array_size; GBLREF unsigned short patch_comp_count; error_def(ERR_AIMGBLKFAIL); error_def(ERR_DBRDONLY); error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEFAIL); error_def(ERR_DSEINVALBLKID); void dse_rmrec(void) { blk_segment *bs1, *bs_ptr; block_id blk; boolean_t long_blk_id; char comp_key[MAX_KEY_SZ + 1]; int4 blk_seg_cnt, blk_size, count; long blk_id_size; short int size, i, rsize; srch_blk_status blkhist; uchar_ptr_t lbp, b_top, rp, r_top, key_top, rp_base; unsigned short cc, cc_base; if (gv_cur_region->read_only) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ if (BADDSEBLK == (blk = dse_getblk("BLOCK", DSENOBML, DSEBLKCUR))) /* WARNING: assignment */ return; if (CLI_PRESENT == cli_present("COUNT")) { if (!cli_get_hex("COUNT", (uint4 *)&count) || count < 1) return; } else count = 1; t_begin_crit(ERR_DSEFAIL); blk_size = cs_addrs->hdr->blk_size; blkhist.blk_num = blk; if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); lbp = (uchar_ptr_t)malloc(blk_size); memcpy(lbp, blkhist.buffaddr, blk_size); if (((blk_hdr_ptr_t)lbp)->bver > BLK_ID_32_VER) { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else t_abort(gv_cur_region, cs_addrs); free(lbp); RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } if (((blk_hdr_ptr_t)lbp)->bsiz > cs_addrs->hdr->blk_size) b_top = lbp + cs_addrs->hdr->blk_size; else if (((blk_hdr_ptr_t)lbp)->bsiz < SIZEOF(blk_hdr)) b_top = lbp + SIZEOF(blk_hdr); else b_top = lbp + ((blk_hdr_ptr_t)lbp)->bsiz; if (CLI_PRESENT == cli_present("RECORD")) { if (!(rp = rp_base = skan_rnum(lbp, FALSE))) { free(lbp); t_abort(gv_cur_region, cs_addrs); return; } } else if (!(rp = rp_base = skan_offset(lbp, FALSE))) { free(lbp); t_abort(gv_cur_region, cs_addrs); return; } memcpy(&comp_key[0], &patch_comp_key[0], SIZEOF(patch_comp_key)); cc_base = patch_comp_count; for ( ; ; ) { GET_SHORT(rsize, &((rec_hdr_ptr_t)rp)->rsiz); if (rsize < SIZEOF(rec_hdr)) r_top = rp + SIZEOF(rec_hdr); else r_top = rp + rsize; if (r_top >= b_top) { if (count) { if (((blk_hdr_ptr_t) lbp)->levl) util_out_print("Warning: removed a star record from the end of this block.", TRUE); ((blk_hdr_ptr_t)lbp)->bsiz = (unsigned int)(rp_base - lbp); BLK_INIT(bs_ptr, bs1); BLK_SEG(bs_ptr, (uchar_ptr_t)lbp + SIZEOF(blk_hdr), (int)((blk_hdr_ptr_t)lbp)->bsiz - SIZEOF(blk_hdr)); if (!BLK_FINI(bs_ptr, bs1)) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_AIMGBLKFAIL, 3, &blk, DB_LEN_STR(gv_cur_region)); free(lbp); t_abort(gv_cur_region, cs_addrs); return; } t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)lbp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); BUILD_AIMG_IF_JNL_ENABLED(cs_data, cs_addrs->ti->curr_tn); t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); free(lbp); return; } r_top = b_top; } if (((blk_hdr_ptr_t)lbp)->levl) key_top = r_top - blk_id_size; else { for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top; ) if (!*key_top++ && !*key_top++) break; } if (EVAL_CMPC((rec_hdr_ptr_t)rp) > patch_comp_count) cc = patch_comp_count; else cc = EVAL_CMPC((rec_hdr_ptr_t)rp); size = key_top - rp - SIZEOF(rec_hdr); if (size > SIZEOF(patch_comp_key) - 2 - cc) size = SIZEOF(patch_comp_key) - 2 - cc; if (size < 0) size = 0; memcpy(&patch_comp_key[cc], rp + SIZEOF(rec_hdr), size); patch_comp_count = cc + size; if (--count >= 0) { rp = r_top; continue; } size = (patch_comp_count < cc_base) ? patch_comp_count : cc_base; for (i = 0; i < size && patch_comp_key[i] == comp_key[i]; i++) ; SET_CMPC((rec_hdr_ptr_t)rp_base, i); rsize = r_top - key_top + SIZEOF(rec_hdr) + patch_comp_count - i; PUT_SHORT(&((rec_hdr_ptr_t)rp_base)->rsiz, rsize); memcpy(rp_base + SIZEOF(rec_hdr), &patch_comp_key[i], patch_comp_count - i); memmove(rp_base + SIZEOF(rec_hdr) + patch_comp_count - i, key_top, b_top - key_top); ((blk_hdr_ptr_t)lbp)->bsiz = (unsigned int)(rp_base + rsize - lbp + b_top - r_top); BLK_INIT(bs_ptr, bs1); BLK_SEG(bs_ptr, (uchar_ptr_t)lbp + SIZEOF(blk_hdr), ((blk_hdr_ptr_t)lbp)->bsiz - SIZEOF(blk_hdr)); if (!BLK_FINI(bs_ptr, bs1)) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_AIMGBLKFAIL, 3, &blk, DB_LEN_STR(gv_cur_region)); free(lbp); t_abort(gv_cur_region, cs_addrs); return; } t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)lbp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); BUILD_AIMG_IF_JNL_ENABLED(cs_data, cs_addrs->ti->curr_tn); t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); free(lbp); return; } } fis-gtm-V7.0-005/sr_port/dse_rmsb.c0000755000032200000250000000537014342376331016015 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "dse.h" #include "cli.h" #include "util.h" GBLREF block_id patch_curr_blk; GBLREF gd_region *gv_cur_region; GBLREF save_strct patch_save_set[PATCH_SAVE_SIZE]; GBLREF sgmnt_addrs *cs_addrs; GBLREF uint4 patch_save_count; void dse_rmsb(void) { block_id blk; #ifndef BLK_NUM_64BIT block_id_64 blk2; #endif char util_buff[MAX_UTIL_LEN]; int util_len; uint4 found_index, version; unsigned int i; if (cli_get_int("VERSION", (int4 *)&version)) { if (0 == version) { util_out_print("Error: no such version.", TRUE); return; } } else version = 0; /* don't use dse_getblk - working out of the save set, not the db */ #ifdef BLK_NUM_64BIT if (!cli_get_hex64("BLOCK", (gtm_uint8 *)&blk)) blk = patch_curr_blk; #else if (!cli_get_hex64("BLOCK", (gtm_uint8 *)&blk2)) blk = patch_curr_blk; else { assert(blk2 == (block_id_32)blk2); blk = (block_id_32)blk2; } #endif found_index = 0; for (i = 0; i < patch_save_count; i++) { if ((patch_save_set[i].blk == blk) && (patch_save_set[i].region == gv_cur_region)) { if (version == patch_save_set[i].ver) { assert(version); found_index = i + 1; break; } if (!version) { if (found_index) { util_out_print("Error: save version number must be specified.", TRUE); return; } found_index = i + 1; } } } if (0 == found_index) { if (version) util_out_print("Error: Version !UL of block !XL not found in set of saved blocks", TRUE, version, blk); else util_out_print("Error: Block !XL not found in set of saved blocks", TRUE, blk); return; } if (!version) { i = found_index - 1; version = patch_save_set[i].ver; } util_len = SIZEOF("!/Removing version !UL of block "); memcpy(util_buff, "!/Removing version !UL of block ", util_len); util_len += i2hexl_nofill(blk, (uchar_ptr_t)&util_buff[util_len-1], MAX_HEX_INT8); util_buff[util_len-1] = 0; assert(ARRAYSIZE(util_buff) >= util_len); util_out_print(util_buff, TRUE, version); free(patch_save_set[i].bp); memmove(&patch_save_set[i], &patch_save_set[i + 1], (--patch_save_count - i) * SIZEOF(save_strct)); return; } fis-gtm-V7.0-005/sr_port/dse_save.c0000644000032200000250000001215714342376331016006 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "gtm_string.h" #include "mdef.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "dse.h" #include "cli.h" #include "util.h" /* Include prototypes */ #include "t_qread.h" #include "gtmmsg.h" #define MAX_COMMENT_LEN 100 GBLDEF save_strct patch_save_set[PATCH_SAVE_SIZE]; GBLDEF uint4 patch_save_count = 0; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEMAXBLKSAV); void dse_save(void) { block_id blk; #ifndef BLK_NUM_64BIT block_id_64 blk2; #endif boolean_t was_crit, was_hold_onto_crit; cache_rec_ptr_t dummy_cr; char buff[MAX_COMMENT_LEN], *ptr, util_buff[MAX_UTIL_LEN]; int i, j, util_len; int4 dummy_int, nocrit_present; sm_uc_ptr_t bp; unsigned short buff_len; assert(PATCH_SAVE_SIZE < MAXUINT4); memset(util_buff, 0, MAX_UTIL_LEN); if (CLI_PRESENT == cli_present("LIST")) { #ifdef BLK_NUM_64BIT if (cli_get_hex64("BLOCK", (gtm_uint8 *)&blk)) { #else if (cli_get_hex64("BLOCK", (gtm_uint8 *)&blk2)) { assert(blk2 == (block_id_32)blk2); blk = (block_id_32)blk2; #endif util_len = SIZEOF("!/Saved versions of block "); memcpy(util_buff, "!/Saved versions of block ", util_len); util_len += i2hexl_nofill(blk, (uchar_ptr_t)&util_buff[util_len-1], MAX_HEX_INT8); util_buff[util_len-1] = 0; util_out_print(util_buff, TRUE); for (i = j = 0; i < patch_save_count; i++) if (patch_save_set[i].blk == blk) { j++; if (*patch_save_set[i].comment) util_out_print("Version !UL Region !AD Comment: !AD!/", TRUE, patch_save_set[i].ver, REG_LEN_STR(patch_save_set[i].region), LEN_AND_STR(patch_save_set[i].comment)); else util_out_print("Version !UL Region !AD!/", TRUE, patch_save_set[i].ver, REG_LEN_STR(patch_save_set[i].region)); } if (!j) util_out_print("None.!/", TRUE); return; } util_out_print("!/Save history:!/", TRUE); for (i = j = 0; i < patch_save_count; i++) { util_len = SIZEOF("Block "); memcpy(util_buff, "Block ", util_len); util_len += i2hexl_nofill(patch_save_set[i].blk, (uchar_ptr_t)&util_buff[util_len-1], MAX_HEX_INT8); util_buff[util_len-1] = 0; util_out_print(util_buff, TRUE); j++; if (*patch_save_set[i].comment) { util_out_print("Version !UL Region !AD Comment: !AD!/", TRUE, patch_save_set[i].ver, REG_LEN_STR(patch_save_set[i].region), LEN_AND_STR(patch_save_set[i].comment)); } else { util_out_print("Version !UL Region !AD!/", TRUE, patch_save_set[i].ver, REG_LEN_STR(patch_save_set[i].region)); } } if (!j) util_out_print(" None.!/", TRUE); return; } if (BADDSEBLK == (blk = dse_getblk("BLOCK", DSEBMLOK, DSEBLKCUR))) /* WARNING: assignment */ return; if (ARRAYSIZE(patch_save_set) <= patch_save_count) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_DSEMAXBLKSAV, 1, PATCH_SAVE_SIZE); return; } j = 1; for (i = 0; i < patch_save_count; i++) if (patch_save_set[i].blk == blk && patch_save_set[i].region == gv_cur_region && patch_save_set[i].ver >= j) j = patch_save_set[i].ver + 1; util_len = SIZEOF("!/Saving version !UL of block "); memcpy(util_buff, "!/Saving version !UL of block ", util_len); util_len += i2hexl_nofill(blk, (uchar_ptr_t)&util_buff[util_len-1], MAX_HEX_INT8); util_buff[util_len-1] = 0; assert(ARRAYSIZE(util_buff) >= util_len); util_out_print(util_buff, TRUE, j); patch_save_set[patch_save_count].ver = j; patch_save_set[patch_save_count].blk = blk; patch_save_set[patch_save_count].region = gv_cur_region; patch_save_set[patch_save_count].bp = (char *)malloc(cs_addrs->hdr->blk_size); if (blk >= cs_addrs->ti->total_blks) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); was_crit = cs_addrs->now_crit; nocrit_present = (CLI_NEGATED == cli_present("CRIT")); DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); if (!(bp = t_qread(blk, &dummy_int, &dummy_cr))) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); memcpy(patch_save_set[patch_save_count].bp, bp, cs_addrs->hdr->blk_size); DSE_REL_CRIT_AS_APPROPRIATE(was_crit, was_hold_onto_crit, nocrit_present, cs_addrs, gv_cur_region); buff_len = MAX_COMMENT_LEN - 1; if ((CLI_PRESENT == cli_present("COMMENT")) && cli_get_str("COMMENT", buff, &buff_len)) { ptr = &buff[buff_len]; *ptr = 0; j = (unsigned int)(ptr - &buff[0] + 1); patch_save_set[patch_save_count].comment = (char *)malloc(j); memcpy(patch_save_set[patch_save_count].comment, &buff[0], j); } else patch_save_set[patch_save_count].comment = ""; patch_save_count++; return; } fis-gtm-V7.0-005/sr_port/dse_shift.c0000644000032200000250000001052514342376331016162 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "gdscc.h" #include "dse.h" #include "cli.h" #include "filestruct.h" #include "jnl.h" #include "util.h" /* Include prototypes */ #include "t_qread.h" #include "t_write.h" #include "t_end.h" #include "t_begin_crit.h" #include "gvcst_blk_build.h" #include "t_abort.h" #include "gtmmsg.h" GBLREF char *update_array, *update_array_ptr; GBLREF cw_set_element cw_set[]; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF srch_hist dummy_hist; GBLREF uint4 update_array_size; error_def(ERR_AIMGBLKFAIL); error_def(ERR_DBRDONLY); error_def(ERR_DSEBLKRDFAIL); error_def(ERR_DSEFAIL); void dse_shift(void) { blk_segment *bs1, *bs_ptr; block_id blk; boolean_t forward; int4 blk_seg_cnt, blk_size, size; sm_uc_ptr_t bp; srch_blk_status blkhist; uchar_ptr_t lbp; uint4 offset, shift; if (gv_cur_region->read_only) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ if (BADDSEBLK == (blk = dse_getblk("BLOCK", DSENOBML, DSEBLKCUR))) /* WARNING: assignment */ return; if (CLI_PRESENT != cli_present("OFFSET")) { util_out_print("Error: offset must be specified.", TRUE); return; } if (!cli_get_hex("OFFSET", &offset)) return; shift = 0; if (CLI_PRESENT == cli_present("FORWARD")) { if (!cli_get_hex("FORWARD", &shift)) return; forward = TRUE; lbp = (unsigned char *)malloc((size_t)shift); } else if (CLI_PRESENT == cli_present("BACKWARD")) { if (!cli_get_hex("BACKWARD", &shift)) return; if (shift > offset) { util_out_print("Error: shift greater than offset not allowed.", TRUE); return; } forward = FALSE; lbp = (unsigned char *)0; } if (!shift) { util_out_print("Error: must specify amount to shift.", TRUE); if (lbp) free(lbp); return; } blk_size = cs_addrs->hdr->blk_size; t_begin_crit(ERR_DSEFAIL); blkhist.blk_num = blk; if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) { if (lbp) free(lbp); RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEBLKRDFAIL); } bp = blkhist.buffaddr; size = ((blk_hdr *)bp)->bsiz; if (size < 0) size = 0; else if (size > cs_addrs->hdr->blk_size) size = cs_addrs->hdr->blk_size; if (offset < SIZEOF(blk_hdr) || offset > size) { util_out_print("Error: offset not in range of block.", TRUE); t_abort(gv_cur_region, cs_addrs); if (lbp) free(lbp); return; } BLK_INIT(bs_ptr, bs1); if (forward) { if (shift + size >= cs_addrs->hdr->blk_size) { util_out_print("Error: block not large enough to accommodate shift.", TRUE); t_abort(gv_cur_region, cs_addrs); if (lbp) free(lbp); return; } memset(lbp, 0, shift); BLK_SEG(bs_ptr, bp + SIZEOF(blk_hdr), offset - SIZEOF(blk_hdr)); BLK_SEG(bs_ptr, lbp, shift); if (size - offset) BLK_SEG(bs_ptr, bp + offset, size - offset); } else { if (shift > offset) shift = offset - SIZEOF(blk_hdr); if (offset - shift > SIZEOF(blk_hdr)) BLK_SEG(bs_ptr, bp + SIZEOF(blk_hdr), offset - shift - SIZEOF(blk_hdr)); if (size - offset) BLK_SEG(bs_ptr, bp + offset, size - offset); } if (!BLK_FINI(bs_ptr, bs1)) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_AIMGBLKFAIL, 3, &blk, DB_LEN_STR(gv_cur_region)); t_abort(gv_cur_region, cs_addrs); if (lbp) free(lbp); return; } t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)bp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); BUILD_AIMG_IF_JNL_ENABLED(cs_data, cs_addrs->ti->curr_tn); t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); return; } fis-gtm-V7.0-005/sr_port/dse_wcreinit.c0000644000032200000250000000333114342376331016666 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "cli.h" #include "dse.h" #ifdef UNIX # include "gtm_stdio.h" #endif GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF short crash_count; error_def(ERR_DBRDONLY); error_def(ERR_DSEINVLCLUSFN); error_def(ERR_DSEONLYBGMM); error_def(ERR_DSEWCINITCON); void dse_wcreinit (void) { unsigned char *c; uint4 large_block; boolean_t was_crit; # ifdef UNIX char *fgets_res; # endif if (gv_cur_region->read_only) RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); if (cs_addrs->hdr->clustered) { rts_error_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) ERR_DSEINVLCLUSFN); return; } UPDATE_CRASH_COUNT(cs_addrs, crash_count); GET_CONFIRM_AND_HANDLE_NEG_RESPONSE if (!IS_CSD_BG_OR_MM(cs_addrs->hdr)) { rts_error_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_DSEONLYBGMM, 2, LEN_AND_LIT("WCINIT")); return; } was_crit = cs_addrs->now_crit; if (!was_crit) grab_crit_encr_cycle_sync(gv_cur_region, WS_62); DSE_WCREINIT(cs_addrs); if (!was_crit) rel_crit (gv_cur_region); return; } fis-gtm-V7.0-005/sr_port/dsefind.h0000755000032200000250000000132314342376331015632 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ typedef struct global_dir_path_struct { block_id block; int4 offset; struct global_dir_path_struct *next; }global_dir_path; typedef struct global_root_list_struct { block_id root; global_dir_path *dir_path; struct global_root_list_struct *link; }global_root_list; fis-gtm-V7.0-005/sr_port/dump_lockhist.c0000755000032200000250000000377714342376331017075 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_stdio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmio.h" #include "have_crit.h" GBLREF gd_region *gv_cur_region; GBLREF int process_id; /* Routine to dump the lock history array on demand starting with most recent and working backwards */ void dump_lockhist(void) { #ifdef DEBUG /* should only be called conditional on DEBUG, but play it safe */ int4 lockIdx, lockIdx_first; node_local_ptr_t locknl; locknl = FILE_INFO(gv_cur_region)->s_addrs.nl; FPRINTF(stderr, "\nProcess lock history (in reverse order) -- Current pid: %d\n", process_id); /* Print headers */ FPRINTF(stderr, "Func LockAddr Caller Pid Retry TrIdx\n"); FPRINTF(stderr, "----------------------------------------------------------\n"); for (lockIdx_first = lockIdx = locknl->lockhist_idx; ;) { if (NULL != locknl->lockhists[lockIdx].lock_addr) { FPRINTF(stderr, "%.4s %16lx %16lx %6d %6d %d\n", locknl->lockhists[lockIdx].lock_op, locknl->lockhists[lockIdx].lock_addr, locknl->lockhists[lockIdx].lock_callr, locknl->lockhists[lockIdx].lock_pid, locknl->lockhists[lockIdx].loop_cnt, lockIdx); } if (--lockIdx < 0) /* If we have fallen off the short end.. */ lockIdx = LOCKHIST_ARRAY_SIZE - 1; /* .. move to the tall end */ if (lockIdx == lockIdx_first) break; /* Completed the loop */ } FFLUSH(stderr); #endif } fis-gtm-V7.0-005/sr_port/dump_record.c0000644000032200000250000002202414342376333016514 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_signal.h" #include "gtmctype.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "cli.h" #include "copy.h" #include "util.h" #include "dse.h" #include "print_target.h" #include "op.h" #include "gvcst_protos.h" #ifdef GTM_TRIGGER #include "hashtab_mname.h" #include /* needed for gv_trigger.h */ #include "targ_alloc.h" #endif #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" #include "gtm_utf8.h" #endif GBLDEF bool wide_out; GBLDEF char patch_comp_key[MAX_KEY_SZ + 1]; GBLREF gd_region *gv_cur_region; GBLREF gv_namehead *gv_target; GBLDEF int patch_rec_counter; GBLREF int patch_is_fdmp; GBLREF int patch_fdmp_recs; GBLREF sgmnt_addrs *cs_addrs; GBLDEF unsigned short patch_comp_count; GBLREF VSIG_ATOMIC_T util_interrupt; error_def(ERR_DSEINVALBLKID); #define NUM_BYTES_PER_LINE 20 sm_uc_ptr_t dump_record(sm_uc_ptr_t rp, block_id blk, sm_uc_ptr_t bp, sm_uc_ptr_t b_top) { block_id blk_id; boolean_t rechdr_displayed = FALSE, long_blk_id; char key_buf[MAX_KEY_SZ + 1], *temp_ptr, *temp_key, util_buff[MAX_UTIL_LEN]; char *prefix_str, *space_str, *dot_str, *format_str; int tmp_cmpc, buf_len, field_width, fastate, chwidth = 0; int4 util_len, head; long blk_id_size; sm_uc_ptr_t r_top, key_top, cptr0, cptr1, cptr_top, cptr_base = NULL, cptr_next = NULL; ssize_t chlen; uint4 ch; unsigned short cc, size; if (rp >= b_top) return NULL; head = cli_present("HEADER"); GET_SHORT(size, &((rec_hdr_ptr_t)rp)->rsiz); cc = EVAL_CMPC((rec_hdr_ptr_t)rp); if(((blk_hdr_ptr_t)bp)->bver > BLK_ID_32_VER) /* Check blk version to see if using 32 or 64 bit block_id */ { # ifdef BLK_NUM_64BIT long_blk_id = TRUE; blk_id_size = SIZEOF(block_id_64); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif } else { long_blk_id = FALSE; blk_id_size = SIZEOF(block_id_32); } if ((CLI_NEGATED != head) && !patch_is_fdmp) { MEMCPY_LIT(util_buff, "Rec:"); util_len = SIZEOF("Rec:") - 1; util_len += i2hex_nofill(patch_rec_counter, (uchar_ptr_t)&util_buff[util_len], 4); MEMCPY_LIT(&util_buff[util_len], " Blk "); util_len += SIZEOF(" Blk ") - 1; util_len += i2hexl_nofill(blk, (uchar_ptr_t)&util_buff[util_len], 16); MEMCPY_LIT(&util_buff[util_len], " Off "); util_len += SIZEOF(" Off ") - 1; util_len += i2hex_nofill((int)(rp - bp), (uchar_ptr_t)&util_buff[util_len], 4); MEMCPY_LIT(&util_buff[util_len], " Size "); util_len += SIZEOF(" Size ") - 1; util_len += i2hex_nofill(size, (uchar_ptr_t)&util_buff[util_len], 4); MEMCPY_LIT(&util_buff[util_len], " Cmpc "); util_len += SIZEOF(" Cmpc ") - 1; util_len += i2hex_nofill(cc, (uchar_ptr_t)&util_buff[util_len], 3); MEMCPY_LIT(&util_buff[util_len], " "); util_len += SIZEOF(" ") - 1; util_buff[util_len] = 0; util_out_print(util_buff, FALSE); } r_top = rp + size; if (r_top > b_top) r_top = b_top; else if (r_top < rp + SIZEOF(rec_hdr)) r_top = rp + SIZEOF(rec_hdr); if (cc > patch_comp_count) cc = patch_comp_count; if (((blk_hdr_ptr_t)bp)->levl) key_top = r_top - blk_id_size; else { for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top;) if (!*key_top++ && !*key_top++) break; } size = key_top - rp - SIZEOF(rec_hdr); if (size > SIZEOF(patch_comp_key) - 2 - cc) size = SIZEOF(patch_comp_key) - 2 - cc; memcpy(&patch_comp_key[cc], rp + SIZEOF(rec_hdr), size); patch_comp_count = cc + size; patch_comp_key[patch_comp_count] = patch_comp_key[patch_comp_count + 1] = '\0'; if (patch_is_fdmp) { if (dse_fdmp(key_top, (int)(r_top - key_top))) patch_fdmp_recs++; } else { if ((r_top - blk_id_size) >= key_top) { if(long_blk_id == TRUE) # ifdef BLK_NUM_64BIT GET_BLK_ID_64(blk_id, key_top); # else RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DSEINVALBLKID); # endif else GET_BLK_ID_32(blk_id, key_top); if ((((blk_hdr_ptr_t)bp)->levl) || (((ublock_id)blk_id) <= cs_addrs->ti->total_blks)) { MEMCPY_LIT(util_buff, "Ptr "); util_len = SIZEOF("Ptr ") - 1; util_len += i2hexl_nofill(blk_id, (uchar_ptr_t)&util_buff[util_len], 16); MEMCPY_LIT(&util_buff[util_len], " "); util_len += SIZEOF(" ") - 1; util_buff[util_len] = 0; util_out_print(util_buff, FALSE); } } util_out_print("Key ", FALSE); if ((r_top == b_top) && ((blk_hdr_ptr_t)bp)->levl && !EVAL_CMPC((rec_hdr_ptr_t)rp) && ((r_top - rp) == (SIZEOF(rec_hdr) + blk_id_size))) { /* *-key */ assert('\0' == patch_comp_key[0]); assert('\0' == patch_comp_key[1]); util_out_print("*", FALSE); /* it is ok not to set gv_target in this case as "print_target" does not need it */ } else if (patch_comp_key[0]) { util_out_print("^", FALSE); temp_ptr = patch_comp_key; temp_key = key_buf; /* Copy from temp_ptr to temp_key until the first zero byte (copy that as well) */ for (buf_len = 0; (*temp_key++ = *temp_ptr++); buf_len++) ; *temp_key = KEY_DELIMITER; /* Set the second zero byte */ gv_target = dse_find_gvt(gv_cur_region, key_buf, buf_len); /* sets up gv_target */ SET_GV_CURRKEY_FROM_GVT(gv_target); /* gv_currkey is needed by GVCST_ROOT_SEARCH */ GVCST_ROOT_SEARCH; /* sets up gv_target->collseq (needed by print_target --> gvsub2str) */ } print_target((uchar_ptr_t)patch_comp_key); util_out_print(0, TRUE); if (CLI_PRESENT != head) { prefix_str = " |"; if (wide_out) { format_str = " !AD"; dot_str = " ."; space_str = " "; field_width = 4; } else { format_str = " !AD"; dot_str = " ."; space_str = " "; field_width = 3; } fastate = 0; for (cptr0 = rp; cptr0 < r_top; cptr0 += NUM_BYTES_PER_LINE) { if (util_interrupt) {/* return, rather than signal ERR_CTRLC so that the calling routine * can deal with that signal and do the appropriate cleanup */ return NULL; } util_len = 8; i2hex_blkfill((int)(cptr0 - bp), (uchar_ptr_t)util_buff, 8); MEMCPY_LIT(&util_buff[util_len], " : |"); util_len += SIZEOF(" : |") - 1; util_buff[util_len] = 0; util_out_print(util_buff, FALSE); /* Dump hexadecimal byte values */ for (cptr1 = cptr0; cptr1 < (cptr0 + NUM_BYTES_PER_LINE); cptr1++) { if (cptr1 < r_top) { i2hex_blkfill(*(sm_uc_ptr_t)cptr1, (uchar_ptr_t)util_buff, field_width); util_buff[field_width] = 0; util_out_print(util_buff, FALSE); } else util_out_print(space_str, FALSE); } util_out_print("|", TRUE); util_out_print(prefix_str, FALSE); /* Display character/wide-character glyphs */ for (cptr1 = cptr0, cptr_top = cptr0 + NUM_BYTES_PER_LINE; cptr1 < cptr_top; cptr1++) { if (!rechdr_displayed && (cptr1 == (rp + SIZEOF(rec_hdr)))) rechdr_displayed = TRUE; assert(rechdr_displayed || (cptr1 < (rp + SIZEOF(rec_hdr)))); assert(!rechdr_displayed || (cptr1 >= (rp + SIZEOF(rec_hdr)))); switch (fastate) { case 0: /* prints single-byte characters or intepret multi-byte characters */ if (cptr1 >= r_top) util_out_print(space_str, FALSE); else if (!gtm_utf8_mode || IS_ASCII(*cptr1) || !rechdr_displayed) { /* single-byte characters */ if (PRINTABLE(*(sm_uc_ptr_t)cptr1)) util_out_print(format_str, FALSE, 1, cptr1); else util_out_print(dot_str, FALSE); } #ifdef UTF8_SUPPORTED else { /* multi-byte characters */ cptr_next = UTF8_MBTOWC(cptr1, r_top, ch); chlen = cptr_next - cptr1; if (WEOF == ch || !U_ISPRINT(ch)) { /* illegal or non-printable characters */ cptr1--; fastate = 1; } else { /* multi-byte printable characters */ cptr_base = cptr1; chwidth = UTF8_WCWIDTH(ch); assert(chwidth >= 0 && chwidth <= 2); cptr1--; fastate = 2; } } #endif break; case 1: /* illegal or non-printable characters */ util_out_print(dot_str, FALSE); if (--chlen <= 0) fastate = 0; break; case 2: /* printable multi-byte characters */ if (chlen-- > 1) /* fill leading bytes with spaces */ util_out_print(space_str, FALSE); else { util_out_print("!AD", FALSE, field_width - chwidth, space_str); if (0 < chwidth) util_out_print("!AD", FALSE, cptr_next - cptr_base, cptr_base); fastate = 0; } break; } } util_out_print("|", TRUE); } } if (CLI_NEGATED != head) util_out_print(0, TRUE); } return (r_top == b_top) ? NULL : r_top; } fis-gtm-V7.0-005/sr_port/dumpfhead.mpt0000644000032200000250000002355614342376331016535 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2016-2021 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Routine to demonstrate reading of fileheader fields. ; dumpfhead(dbname) new $etrap new (dbname,debug) set $etrap="quit" ; defer error handling to the caller if '$data(debug) new debug set debug=0 do getfields(.record,dbname) zwrite record quit ; ; Routine to grab the fields and return them in the passed-in array ; getfields(fldarray,dbname) new $etrap set $etrap="quit:$quit """" quit" ; Defer error handling to the caller new BigEndian,Endian,FALSE,Hex,HexDigits,LittleEndian,NullChar,TRUE new endian,fhead,fldstring,flddim,fldidx,fldlen,fldname,fldnum,fldoff new fldraw,fldtype,gtmhdwr,gtmos,gtmver,gtmzv,label,offset,platformendian,saveIO new skey,typeconvert,x,$zgbldir kill fldarray ; Clean array for new insertions ; ; Initialize field type translation to simple types ; set typeconvert("boolean_t")="int" set typeconvert("char")="string" set typeconvert("int")="int" set typeconvert("int1")="int" set typeconvert("int2")="int" set typeconvert("int4")="int" set typeconvert("int64_t")="hex" set typeconvert("gtm_int8")="int" set typeconvert("gtm_timet")="gtm_timet" ; pass through unchanged to signal dumping as a date string set typeconvert("long")="hex" set typeconvert("short")="int" set typeconvert("size_t")="hex" set typeconvert("ssize_t")="hex" set typeconvert("uint1")="uint" set typeconvert("uint2")="uint" set typeconvert("uint64_t")="hex" set typeconvert("unsigned")="uint" set typeconvert("unsigned-char")="string" set typeconvert("unsigned-int")="uint" set typeconvert("unsigned-int1")="uint" set typeconvert("unsigned-long")="hex" set typeconvert("unsigned-short")="uint" ; ; Other initializations ; set Hex(0)=1 for x=1:1:16 set Hex(x)=Hex(x-1)*16 set HexDigits="0123456789ABCDEF" set TRUE=1,FALSE=0,BigEndian=1,LittleEndian=0 set platformendian("AIX","RS6000")=BigEndian set platformendian("Linux","x86")=LittleEndian set platformendian("Linux","x86_64")=LittleEndian set platformendian("Solaris","SPARC")=BigEndian set gtmzv=$ZVersion,gtmver=$ZPiece(gtmzv," ",2),gtmos=$ZPiece(gtmzv," ",3),gtmhdwr=$ZPiece(gtmzv," ",4) set Endian=platformendian(gtmos,gtmhdwr) set NullChar=$zchar(0) ; 0x00 ; ; Read database fileheader ; set saveIO=$IO open dbname:(readonly:fixed:recordsize=8192:chset="M":nowrap:exception="do badopen") use dbname:exception="goto eof:$zeof,filerr" read fhead#8192 ; Read 8K of fileheader close dbname use saveIO if $zlength(fhead)'=8192 do . write "GETFIELDS: Error - unable to read fileheader from ",dbname,! . write:0'=$data(fhead) "GETFIELDS: Read only ",$zlength(fhead)," bytes",! . set $etrap="zgoto 0" . zhalt 1 ; ; Determine if this is a valid database header. Note that we don't ; even need to go into the ^gtmtypes database because the header is ; always at offset 0, with len=12 ; set label=$$formatfld($zextract(fhead,1,12),"char") if ((label'["GDSDYNUNX03")&(label'["GDSDYNUNX04")) do . write "GETFIELDS: Error - invalid fileheader format from ",dbname,! . set $etrap="zgoto 0" . zhalt 1 ; ; Now parse out the fields ; set $zgbldir="$gtm_dist/gtmhelp.gld" if (label["GDSDYNUNX04") set skey="sgmnt_data" else if (label["GDSDYNUNX03") set skey="v6_sgmnt_data" for fldnum=1:1:^gtmtypes(skey,0) do . set fldtype=^gtmtypes(skey,fldnum,"type") . quit:(0=$data(typeconvert(fldtype))) ; Ignore unknown types (likely a sub-struct header) . set fldname=^gtmtypes(skey,fldnum,"name") . if (label["GDSDYNUNX03") set fldname=$EXTRACT(fldname,4,$LENGTH(fldname)) . quit:(fldname["filler") . set fldoff=^gtmtypes(skey,fldnum,"off") . set fldlen=^gtmtypes(skey,fldnum,"len") . set flddim=$get(^gtmtypes(skey,fldnum,"dim"),1) . if (1=flddim) do . . set fldarray(fldname)=$$formatfld($zextract(fhead,fldoff+1,fldoff+fldlen),fldtype) . else do ; array handling, made to be similar to %PEEKBYNAME() output . . set fldlen=fldlen\flddim . . set fldstring=$$formatfld($zextract(fhead,fldoff+1,fldoff+fldlen),fldtype) . . for fldidx=1:1:flddim-1 do . . . set offset=fldoff+(fldlen*fldidx) . . . set fldstring=fldstring_","_$$formatfld($zextract(fhead,offset+1,offset+fldlen),fldtype) . . set fldarray(fldname)=fldstring quit:$quit "" quit eof close dbname badopen write "The file """,dbname,""" had error: " set $etrap="zgoto 0" zmessage +$zstatus zhalt 1 ; ; Routine to format fields into their proper format. ; formatfld(fldval,fldtype) set fldtype=typeconvert(fldtype) quit:("int"=fldtype) $$BinToSInt(fldval) quit:("uint"=fldtype) $$BinToUInt(fldval) quit:("hex"=fldtype) $$BinToHex(fldval) quit:("gtm_timet"=fldtype) $$BinToDateStr(fldval,0) ; Turn an epoch date into a ctime-like string quit fldval ; ; This routine converts a Unix epoch value to an horolog, and returns a "ctime"-like ; printable string such as "Sun Aug 15 13:37:32 2021". It is set up to be called ; automatically to dump values typed as "gtm_timet", but could be called for regular ; time_t values, or anything else storing an epoch value. ; ; The problem complicating things is that epoch dates are reckoned in the GMT/UCT/Zulu timezone ; while M uses Horolog dates reckoned in the local timezone. Furthermore, ; we do not, a priori, know the offset of the local timezone from GMT. (The ; TZ environment variable may not exist, and does not directly supply that ; information if it does). ; ; Our strategy here then is to get both the current Epoch & Horolog times ; and use that to compute the GMT offset which we can then apply before ; converting our target epoch date to Horolog and rendering a date string. ; ; The basic identity we use here is that Epoch "0" == Horolog "47117,0" for GMT ; ; As a special case the epoch value '0' is rendered as "Never" ; ; If a GMT date string is desired, set the parameter gmt to 1. This is probably ; actually more useful in many real-world cases. ; ; @param bin -- A binary epoch value ; @param gmt -- A flag value: 0 to return a date string in local time, 1 to return it in GMT ; @return -- A date string in the format "Sun Aug 15 13:37:32 2021 [GMT]" ; BinToDateStr(bin,gmt) new epoch,i,binlen,horo,zulu,horod,horos,horoepoch,daysec,horobase,offset new zlocal,zlocald,zlocals,daynames,monnames,datestr set daysec=86400 set horobase=47117 set offset=0 set epoch=0,binlen=$ZLength(bin) if Endian=BigEndian for i=binlen:-1:1 set epoch=$zascii(bin,i)*Hex((binlen-i)*2)+epoch else for i=1:1:binlen set epoch=$zascii(bin,i)*Hex((i-1)*2)+epoch ; If the file has never been (successfully) backed up and has a "0" epoch, render that as "Never" quit:(epoch=0)&(gmt=0) "Never" ; Now we have our incoming epoch value in usable format ; Next, capture the current system Horolog & Epoch dates, taking care that both ; fall within the same second. (Also convert the zulu time from microseconds to seconds) for i=1 set horo=$Horolog,zulu=$Zut\1000000 quit:horo=$Horolog ; Isolate the horolog day & second pieces and generate an epoch date from them. ; The difference between this constructed epoch and the real epoch is the timezone offset. set horod=$piece(horo,",",1) set horos=$piece(horo,",",2) set horoepoch=((horod-horobase)*daysec)+horos set:gmt=0 offset=horoepoch-zulu ; Apply the offset to the incoming epoch and create an horolog from it set zlocal=epoch+offset set zlocald=(zlocal\daysec)+47117 set zlocals=zlocal#daysec set horolocal=zlocald_","_zlocals ; If we do not specify day and month names, we get upper case strings set daynames="Sun,Mon,Tue,Wed,Thu,Fri,Sat" set monnames="Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec" set datestr=$zdate(horolocal,"DAY MON DD 24:60:SS YEAR",monnames,daynames)_" GMT" set:gmt=0 datestr=$zdate(horolocal,"DAY MON DD 24:60:SS YEAR",monnames,daynames) set:0=$extract(datestr,9) $extract(datestr,9)=" " quit datestr ; ; Convert binary data of indeterminate length to a (printable) unsigned decimal value ; BinToUInt(bin) new num,i,binlen set num=0,binlen=$ZLength(bin) if Endian=BigEndian for i=binlen:-1:1 set num=$zascii(bin,i)*Hex((binlen-i)*2)+num else for i=1:1:binlen set num=$zascii(bin,i)*Hex((i-1)*2)+num quit num ; ; Convert binary data of indeterminate length to a (printable) unsigned decimal value ; BinToSInt(bin) new num,i,binlen,sgntst,sign,fix set num=0,binlen=$zlength(bin),sign=1,fix=0 set sgntst=NullChar_bin if Endian=BigEndian do ; See if we need to deal with a sign . do:($zbitget(sgntst,1)) . . ; . . ; Sign is set, need to generate twos-complement and remember to add "-" sign. Since we can't actually do the . . ; +1 required by twos complement right now, remember to do it later after we do the numeric conversion. . . ; . . set bin=$zextract($zbitnot(sgntst),2,binlen+1) . . set sign=-1,fix=1 . for i=binlen:-1:1 set num=$zascii(bin,i)*Hex((binlen-i)*2)+num else do . do:($zbitget(sgntst,((binlen-1)*8)+1)) ; Sign is set, twos-complement and sign fixup. . . set bin=$zextract($zbitnot(sgntst),2,binlen+1) . . set sign=-1,fix=1 . for i=1:1:binlen set num=$zascii(bin,i)*Hex((i-1)*2)+num quit (num+fix)*sign ; Important to apply fix before sign ; ; Convert binary data of indeterminate length to a (printable) hexadecimal value ; BinToHex(bin,noendian) new hex,i,binlen,num set hex="",binlen=$zlength(bin) if ((Endian=BigEndian)!($get(noendian,FALSE))) do . for i=binlen:-1:1 do . . set num=$zascii(bin,i),hex=$zextract(HexDigits,(num#16)+1)_hex,num=num\16,hex=$zextract(HexDigits,num+1)_hex else do . for i=1:1:binlen do . . set num=$zascii(bin,i),hex=$zextract(HexDigits,(num#16)+1)_hex,num=num\16,hex=$zextract(HexDigits,num+1)_hex quit "0x"_hex fis-gtm-V7.0-005/sr_port/dumptable.c0000755000032200000250000000257114342376331016174 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "gtm_string.h" #include "compiler.h" #include "list_file.h" #include "dumptable.h" static readonly char argtype[][6] = { "","TVAR ","TVAL ","TINT ","TVAD " ,"TCAD ","VREG ","MLIT ","MVAR " ,"TRIP ","TNXT ","TJMP ","INDR " ,"MLAB ","ILIT ","CDLT ","TEMP " }; static readonly char start8[] = " stored at r8 + "; static readonly char start9[] = " stored at r9 + "; GBLREF int4 sa_temps[]; GBLREF int4 sa_temps_offset[]; void dumptable(void) { char outbuf[256]; int i; unsigned char *c; for (i=1; i <= TCAD_REF ; i++) { c = (unsigned char *)&outbuf[0]; memcpy(c,argtype[i],5); c += 5; *c++ = ' '; c = i2asc(c,sa_temps[i]); if (i == TVAR_REF) { memcpy(c, &start8[0], SIZEOF(start8) - 1); c += SIZEOF(start8) - 1; } else { memcpy(c, &start9[0], SIZEOF(start9) - 1); c += SIZEOF(start9) - 1; } c = i2asc(c,sa_temps_offset[i]); *c++ = 0; list_tab(); list_line(outbuf); } } fis-gtm-V7.0-005/sr_port/dumptable.h0000755000032200000250000000110414342376331016170 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef DUMPTABLE_INCLUDED #define DUMPTABLE_INCLUDED void dumptable(void); /***type int added***/ #endif /* DUMPTABLE_INCLUDED */ fis-gtm-V7.0-005/sr_port/eb_muldiv.c0000755000032200000250000001716614342376331016173 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* eb_muldiv - emulate extended precision (18-digit) multiplication and division */ #include "mdef.h" #include "arit.h" #include "eb_muldiv.h" #define FLO_HI 1e9 #define FLO_LO 1e8 #define FLO_BIAS 1e3 #define DFLOAT2MINT(X,DF) (X[1] = DF, X[0] = (DF - (double)X[1])*FLO_HI) #define RADIX 10000 /* eb_int_mul - multiply two GT.M INT's * * input * v1, u1 - GT.M INT's * * output * if the product will fit into a GT.M INT format: * function result = FALSE => no promotion necessary * p[] = INT product of (v1*m1) * else (implies overflow out of INT format): * function result = TRUE => promotion to extended precision necessary * p[] = undefined */ bool eb_int_mul (int4 v1, int4 u1, int4 p[]) { double pf; int4 tp[2], promote; promote = TRUE; /* promote if overflow or too many significant fractional digits */ pf = (double)u1*(double)v1/FLO_BIAS; if ((pf < FLO_HI) && (pf > -FLO_HI)) { DFLOAT2MINT(tp, pf); if (tp[0] == 0) /* don't need extra precision */ { promote = FALSE; p[0] = tp[0]; p[1] = tp[1]; } } return promote; } /* eb_mul - multiply two GT.M extended precision numeric values * * input * v[], u[] - GT.M extended precision numeric value mantissas * * output * function result = scale factor of result * p[] = GT.M extended precision mantissa of (u*v) */ int4 eb_mul (int4 v[], int4 u[], int4 p[]) /* p = u*v */ { short i, j, k; int4 acc, carry, m1[5], m2[5], prod[9], scale; /* Throughout, larger index => more significance. */ for (i = 0 ; i < 9 ; i++) prod[i] = 0; /* Break up 2-4-(3/1)-4-4 */ m1[0] = v[0] % RADIX; m2[0] = u[0] % RADIX; m1[1] = (v[0]/RADIX) % RADIX; m2[1] = (u[0]/RADIX) % RADIX; m1[2] = (v[1] % (RADIX/10))*10 + v[0]/(RADIX*RADIX); m2[2] = (u[1] % (RADIX/10))*10 + u[0]/(RADIX*RADIX); m1[3] = (v[1]/(RADIX/10)) % RADIX; m2[3] = (u[1]/(RADIX/10)) % RADIX; m1[4] = v[1]/((RADIX/10)*RADIX); m2[4] = u[1]/((RADIX/10)*RADIX); for (j = 0 ; j <= 4 ; j++) { if (m2[j] != 0) { for (i = 0, carry = 0 ; i <= 4 ; i++) { acc = m1[i]*m2[j] + prod[i+j] + carry; prod[i+j] = acc % RADIX; carry = acc / RADIX; } k = i + j; /* Introduce new variable for SCI */ if ( 9 > k) prod[k] = carry; else if (0 != carry) assert(FALSE); } } if (prod[8] >= RADIX/10) { /* Assemble back 4-4-1/3-4-2 */ scale = 0; /* no scaling needed */ p[0] = ((prod[6]%1000)*RADIX + prod[5])*(RADIX/ 100) + (prod[4]/ 100); p[1] = ( prod[8] *RADIX + prod[7])*(RADIX/1000) + (prod[6]/1000); } else /* prod[8] < RADIX/10 [means not normalized] */ { /* Assemble back 3-4-2/2-4-3 */ scale = -1; /* to compensate for normalization */ p[0] = ((prod[6]%100)*RADIX + prod[5])*(RADIX/ 10) + (prod[4]/ 10); p[1] = ( prod[8] *RADIX + prod[7])*(RADIX/100) + (prod[6]/100); } return scale; } /* eb_mvint_div - divide to GT.M INT's * * input * v, u - INT's to be divided * * output * if the quotient will fit into a GT.M INT: * function value = FALSE => no promotion necessary * q[] = INT quotient of (v/u) * else (implies overflow out of GT.M INT forat): * function value = TRUE => promotion to extended precision necessary * q[] = undefined */ bool eb_mvint_div (int4 v, int4 u, int4 q[]) { double qf; int4 tq[2], promote; promote = TRUE; /* promote if overflow or too many significant fractional digits */ qf = (double)v*FLO_BIAS/(double)u; if ((qf < FLO_HI) && (qf > -FLO_HI)) { DFLOAT2MINT(tq, qf); if (tq[0] == 0) /* don't need extra word of precision */ { promote = FALSE; q[0] = tq[0]; q[1] = tq[1]; } } return promote; } /* eb_int_div - integer division of two GT.M INT's * * input * v1, u1 - GT.M INT's to be divided * * output * if result fits into a GT.M INT: * function value = FALSE => no promotion necessary * q[] = INT result of (v1\u1) * else (implies some sort of overflow): * function result = TRUE => promotion to extended precision necessary * q[] = undefined */ bool eb_int_div (int4 v1, int4 u1, int4 q[]) { double qf; qf= (double)v1*FLO_BIAS/(double)u1; if (qf < FLO_HI && qf > -FLO_HI) { DFLOAT2MINT(q,qf); q[1]= (q[1]/MV_BIAS)*MV_BIAS; return FALSE; } else { return TRUE; } } /* eb_div - divide two GT.M extended precision numeric values * * input * x[], y[] - GT.M extended precision numeric value mantissas * * output * function result = scale factor of result * q[] = GT.M extended precision mantissa of (y/x) */ int4 eb_div (int4 x[], int4 y[], int4 q[]) /* q = y/x */ { int4 borrow, carry, i, j, scale, prod, qx[5], xx[5], yx[10]; for (i = 0 ; i < 5 ; i++) yx[i] = 0; if (x[1] < y[1] || (x[1] == y[1] && x[0] <= y[0])) /* i.e., if x <= y */ { /* Break y apart 3-4-2/2-4-3 */ scale = 1; yx[5] = (y[0]%(RADIX/10))*10; yx[6] = (y[0]/(RADIX/10))%RADIX; yx[7] = (y[1]%(RADIX/100))*(RADIX/100) + y[0]/((RADIX/10)*RADIX); yx[8] = (y[1]/(RADIX/100))%RADIX; yx[9] = y[1]/((RADIX/100)*RADIX); } else { /* Break y apart 4-4-1/3-4-2 */ scale = 0; yx[5] = (y[0]%(RADIX/100))*100; yx[6] = (y[0]/(RADIX/100))%RADIX; yx[7] = (y[1]%(RADIX/1000))*(RADIX/10) + y[0]/((RADIX/100)*RADIX); yx[8] = (y[1]/(RADIX/1000))%RADIX; yx[9] = y[1]/((RADIX/1000)*RADIX); } /* Break x apart 4-4-1/3-4-2 */ xx[0] = (x[0]%(RADIX/100))*100; xx[1] = (x[0]/(RADIX/100))%RADIX; xx[2] = (x[1]%(RADIX/1000))*(RADIX/10) + x[0]/((RADIX/100)*RADIX); xx[3] = (x[1]/(RADIX/1000))%RADIX; xx[4] = x[1]/((RADIX/1000)*RADIX); assert((yx[9] <= xx[4]) || (0 < xx[4])); if (0 < xx[4]) { /* The added IF should not be a performance hit since xx[4] was just set */ for (i = 4; (i >= 0) && (0 < xx[4]); i--) { qx[i] = (yx[i+5]*RADIX + yx[i+4]) / xx[4]; if (qx[i] != 0) { /* Multiply x by qx[i] and subtract from remainder. */ for (j = 0, borrow = 0 ; j <= 4 ; j++) { prod = qx[i]*xx[j] + borrow; borrow = prod/RADIX; yx[i+j] -= (prod%RADIX); if (yx[i+j] < 0) { yx[i+j] += RADIX; borrow ++; } } yx[i+5] -= borrow; while (yx[i+5] < 0) { qx[i] --; /* estimate too high */ for (j = 0, carry = 0 ; j <= 4 ; j++) { yx[i+j] += (xx[j] + carry); carry = yx[i+j]/RADIX; yx[i+j] %= RADIX; } yx[i+5] += carry; } } assert (0 <= qx[i] && qx[i] < RADIX); /* make sure in range */ assert (yx[i+5] == 0); /* check that remainder doesn't overflow */ } } else { /* Unlikely case */ for (i = 0 ; i < 5 ; i++) qx[i] = 0; } /* Assemble q 4-4-1/3-4-2 */ q[0] = ((qx[2]%1000)*RADIX + qx[1])*100 + (qx[0]/ 100); q[1] = ( qx[4] *RADIX + qx[3])* 10 + (qx[2]/1000); assert ( (FLO_LO <= q[1] && q[1] < FLO_HI) || (q[1] == 0 && q[0] == 0 && y[1] == 0 && y[0] == 0) ); return scale; } /* eb_int_mod - INT modulus of two GT.M INT's * * input * v1, u1 - GT.M INT's * * output * p[] = INT value of (v1 mod u1) == (v1 - (u1*floor(v1/u1))) */ void eb_int_mod (int4 v1, int4 u1, int4 p[]) { int4 quo, rat, neg; if (u1 == 0 || v1 == 0) { p[1]= 0; } else { quo = v1/u1; rat = v1 != quo*u1; neg = (v1 < 0 && u1 > 0) || (v1 > 0 && u1 < 0); p[1] = v1 - u1*(quo - (neg && rat)); } return; } fis-gtm-V7.0-005/sr_port/eb_muldiv.h0000755000032200000250000000156414342376331016173 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef EB_MULDIV_H_INCLUDED #define EB_MULDIV_H_INCLUDED bool eb_int_mul (int4 v1, int4 u1, int4 p[]); int4 eb_mul (int4 v[], int4 u[], int4 p[]); /* p = u*v */ bool eb_mvint_div (int4 v, int4 u, int4 q[]); bool eb_int_div (int4 v1, int4 u1, int4 q[]); int4 eb_div (int4 x[], int4 y[], int4 q[]); /* q = y/x */ void eb_int_mod (int4 v1, int4 u1, int4 p[]); #endif fis-gtm-V7.0-005/sr_port/ebc_xlat.c0000755000032200000250000001276314342376331016004 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2003 Sanchez Computer Associates, 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. * * * ****************************************************************/ /**************************************************************** * * * This module provides translation functions between the * * ASCII and EBCDIC code sets. * * * ****************************************************************/ #include "mdef.h" #include "ebc_xlat.h" /* Translation tables */ /* These were generated using iconv between "ISO8859-1" (ASCII) */ /* and "IBM-1047" (EBCDIC)); */ /* EBCDIC to ASCII */ LITDEF unsigned char e2a[256] = { /* 00 - 07: */ 0x0, 0x1, 0x2, 0x3, 0x9c, 0x9, 0x86, 0x7f, /* 08 - 0f: */ 0x97, 0x8d, 0x8e, 0xb, 0xc, 0xd, 0xe, 0xf, /* 10 - 17: */ 0x10, 0x11, 0x12, 0x13, 0x9d, 0xa, 0x8, 0x87, /* 18 - 1f: */ 0x18, 0x19, 0x92, 0x8f, 0x1c, 0x1d, 0x1e, 0x1f, /* 20 - 27: */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x17, 0x1b, /* 28 - 2f: */ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x5, 0x6, 0x7, /* 30 - 37: */ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x4, /* 38 - 3f: */ 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /* 40 - 47: */ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, /* 48 - 4f: */ 0xe7, 0xf1, 0xa2, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* 50 - 57: */ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, /* 58 - 5f: */ 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, /* 60 - 67: */ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, /* 68 - 6f: */ 0xc7, 0xd1, 0xa6, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /* 70 - 77: */ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, /* 78 - 7f: */ 0xcc, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /* 80 - 87: */ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 88 - 8f: */ 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /* 90 - 97: */ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, /* 98 - 9f: */ 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /* a0 - a7: */ 0xb5, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* a8 - af: */ 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0x5b, 0xde, 0xae, /* b0 - b7: */ 0xac, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, /* b8 - bf: */ 0xbd, 0xbe, 0xdd, 0xa8, 0xaf, 0x5d, 0xb4, 0xd7, /* c0 - c7: */ 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* c8 - cf: */ 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /* d0 - d7: */ 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, /* d8 - df: */ 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xf9, 0xfa, 0xff, /* e0 - e7: */ 0x5c, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* e8 - ef: */ 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /* f0 - f7: */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* f8 - ff: */ 0x38, 0x39, 0xb3, 0xdb, 0xdc, 0xd9, 0xda, 0x9f }; /* ASCII to EBCDIC */ LITDEF unsigned char a2e[256] = { /* 00 - 07: */ 0x0, 0x1, 0x2, 0x3, 0x37, 0x2d, 0x2e, 0x2f, /* 08 - 0f: */ 0x16, 0x5, 0x15, 0xb, 0xc, 0xd, 0xe, 0xf, /* 10 - 17: */ 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, /* 18 - 1f: */ 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, /* 20 - 27: */ 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, /* 28 - 2f: */ 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, /* 30 - 37: */ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 38 - 3f: */ 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, /* 40 - 47: */ 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 48 - 4f: */ 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, /* 50 - 57: */ 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, /* 58 - 5f: */ 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, /* 60 - 67: */ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 68 - 6f: */ 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, /* 70 - 77: */ 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, /* 78 - 7f: */ 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x7, /* 80 - 87: */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x6, 0x17, /* 88 - 8f: */ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x9, 0xa, 0x1b, /* 90 - 97: */ 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x8, /* 98 - 9f: */ 0x38, 0x39, 0x3a, 0x3b, 0x4, 0x14, 0x3e, 0xff, /* a0 - a7: */ 0x41, 0xaa, 0x4a, 0xb1, 0x9f, 0xb2, 0x6a, 0xb5, /* a8 - af: */ 0xbb, 0xb4, 0x9a, 0x8a, 0xb0, 0xca, 0xaf, 0xbc, /* b0 - b7: */ 0x90, 0x8f, 0xea, 0xfa, 0xbe, 0xa0, 0xb6, 0xb3, /* b8 - bf: */ 0x9d, 0xda, 0x9b, 0x8b, 0xb7, 0xb8, 0xb9, 0xab, /* c0 - c7: */ 0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9e, 0x68, /* c8 - cf: */ 0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77, /* d0 - d7: */ 0xac, 0x69, 0xed, 0xee, 0xeb, 0xef, 0xec, 0xbf, /* d8 - df: */ 0x80, 0xfd, 0xfe, 0xfb, 0xfc, 0xba, 0xae, 0x59, /* e0 - e7: */ 0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9c, 0x48, /* e8 - ef: */ 0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57, /* f0 - f7: */ 0x8c, 0x49, 0xcd, 0xce, 0xcb, 0xcf, 0xcc, 0xe1, /* f8 - ff: */ 0x70, 0xdd, 0xde, 0xdb, 0xdc, 0x8d, 0x8e, 0xdf }; void asc_to_ebc(unsigned char *estring_out, unsigned char *astring_in, int len) { int i; unsigned char *in_ptr, *out_ptr; for (i = 0, in_ptr = astring_in, out_ptr = estring_out; i < len; i++, in_ptr++, out_ptr++) *out_ptr = a2e[*in_ptr]; } void ebc_to_asc(unsigned char *astring_out, unsigned char *estring_in, int len) { int i; unsigned char *in_ptr, *out_ptr; for (i = 0, in_ptr = estring_in, out_ptr = astring_out; i < len; i++, in_ptr++, out_ptr++) *out_ptr = e2a[*in_ptr]; } fis-gtm-V7.0-005/sr_port/ebc_xlat.h0000755000032200000250000000134714342376331016005 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef EBC_XLAT_H_INCLUDED #define EBC_XLAT_H_INCLUDED void asc_to_ebc(unsigned char *estring_out, unsigned char *astring_in, int len); void ebc_to_asc(unsigned char *astring_out, unsigned char *estring_in, int len); #endif fis-gtm-V7.0-005/sr_port/ecode_init.c0000644000032200000250000000347214342376333016321 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 #include "stack_frame.h" #include "error.h" /* for ERROR_RTN */ #include "error_trap.h" GBLREF dollar_ecode_type dollar_ecode; /* structure containing $ECODE related information */ GBLREF dollar_stack_type dollar_stack; /* structure containing $STACK related information */ /* NOTE: every malloc'd storage here should be free'd in a nested call-in environment. * gtmci_isv_restore (in gtmci_isv.c) needs to be reflected for any future mallocs added here. */ void ecode_init(void) { dollar_ecode.begin = (char *)malloc(DOLLAR_ECODE_ALLOC); dollar_ecode.top = dollar_ecode.begin + DOLLAR_ECODE_ALLOC; dollar_ecode.array = (dollar_ecode_struct *)malloc(SIZEOF(dollar_ecode_struct) * DOLLAR_ECODE_MAXINDEX); dollar_ecode.error_rtn_addr = NON_IA64_ONLY(CODE_ADDRESS(ERROR_RTN)) IA64_ONLY(CODE_ADDRESS_C(ERROR_RTN)); dollar_ecode.error_rtn_ctxt = GTM_CONTEXT(ERROR_RTN); dollar_ecode.error_return_addr = (error_ret_fnptr)ERROR_RETURN; dollar_stack.begin = (char *)malloc(DOLLAR_STACK_ALLOC); dollar_stack.top = dollar_stack.begin + DOLLAR_STACK_ALLOC; dollar_stack.array = (dollar_stack_struct *)malloc(SIZEOF(dollar_stack_struct) * DOLLAR_STACK_MAXINDEX); NULLIFY_DOLLAR_ECODE; /* this macro resets dollar_ecode.{end,index}, dollar_stack.{begin,index,incomplete} and * first_ecode_error_frame to point as if no error occurred at all */ } fis-gtm-V7.0-005/sr_port/ecode_get.c0000755000032200000250000000215614342376331016134 0ustar librarygtc/**************************************************************** * Copyright 2001, 2007 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 "gtm_string.h" #include "stringpool.h" #include "error_trap.h" GBLREF dollar_ecode_type dollar_ecode; /* structure containing $ECODE related information */ void ecode_get(int level, mval *val) { mstr tmpmstr; val->mvtype = MV_STR; assert(-1 == level); /* currently -1 is the only valid negative level argument to ecode_get() */ if (dollar_ecode.index) { assert(dollar_ecode.end > dollar_ecode.begin); val->str.addr = dollar_ecode.begin; val->str.len = INTCAST(dollar_ecode.end - dollar_ecode.begin + 1); /* to account for terminating ',' */ s2pool(&val->str); } else { assert(dollar_ecode.end == dollar_ecode.begin); val->str.len = 0; } } fis-gtm-V7.0-005/sr_port/emit_code.c0000644000032200000250000016500314342376333016146 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_string.h" #include #include "gtm_fcntl.h" #include "gtm_stat.h" #include "gtm_stdio.h" #include "compiler.h" #include "opcode.h" #include "mdq.h" #include "vxi.h" #include "vxt.h" #include "cgp.h" #include "obj_gen.h" #include #include "obj_file.h" #include "list_file.h" #include "min_max.h" #include #ifdef UNIX #include "xfer_enum.h" #endif #include "hashtab_mname.h" #include "stddef.h" /* Required to find out variable length argument runtime function calls*/ #if defined(__x86_64__) || defined(__ia64) # include "code_address_type.h" # ifdef XFER # undef XFER # endif /* XFER */ # define XFER(a,b) #b # include "xfer_desc.i" DEFINE_XFER_TABLE_DESC; GBLDEF int call_4lcldo_variant; /* used in emit_jmp for call[sp] and forlcldo */ #endif /* __x86_64__ || __ia64 */ #define MVAL_INT_SIZE DIVIDE_ROUND_UP(SIZEOF(mval), SIZEOF(UINTPTR_T)) #ifdef DEBUG # include "vdatsize.h" /* VAX DISASSEMBLER TEXT */ static const char vdat_bdisp[VDAT_BDISP_SIZE + 1] = "B^"; static const char vdat_wdisp[VDAT_WDISP_SIZE + 1] = "W^"; static const char vdat_r9[VDAT_R9_SIZE + 1] = "(r9)"; static const char vdat_r8[VDAT_R8_SIZE + 1] = "(r8)"; static const char vdat_gr[VDAT_GR_SIZE + 1] = "G^"; static const char vdat_immed[VDAT_IMMED_SIZE + 1] = "I^#"; static const char vdat_r11[VDAT_R11_SIZE + 1] = "(R11)"; static const char vdat_gtmliteral[VDAT_GTMLITERAL_SIZE + 1] = "GTM$LITERAL"; static const char vdat_def[VDAT_DEF_SIZE + 1] = "@"; IA64_ONLY(GBLDEF char asm_mode = 0; /* 0 - disassembly mode. 1 - decode mode */) GBLDEF unsigned char *obpt; /* output buffer index */ GBLDEF unsigned char outbuf[ASM_OUT_BUFF]; /* assembly language output buffer */ static int vaxi_cnt = 1; /* Vax instruction count */ /* Disassembler text: */ LITREF char *xfer_name[]; LITREF char vxi_opcode[][6]; GBLREF char *oc_tab_graphic[]; #endif LITREF octabstruct oc_tab[]; /* op-code table */ LITREF short ttt[]; /* triple templates */ GBLREF boolean_t run_time; GBLREF int sa_temps_offset[]; GBLREF int sa_temps[]; LITREF int sa_class_sizes[]; GBLDEF CODE_TYPE code_buf[NUM_BUFFERRED_INSTRUCTIONS]; GBLDEF int code_idx; #ifdef DEBUG GBLDEF struct inst_count generated_details[MAX_CODE_COUNT], calculated_details[MAX_CODE_COUNT]; GBLDEF int4 generated_count, calculated_count; #endif /* DEBUG */ GBLDEF int calculated_code_size, generated_code_size; GBLDEF int jmp_offset; /* Offset to jump target */ GBLDEF int code_reference; /* Offset from pgm start to current loc */ /* On x86_64, the smaller offsets are encoded in 1 byte (4 bytes otherwise). But for some cases, * the offsets may be different during APPROX_ADDR and MACHINE phases, hence generating different size instruction. * to solve this even the smaller offsets need to be encoded in 4 bytes so that same size instructions are generated * in both APPROX_ADDR and MACHINE phase. the variable force_32 is used for this purpose. */ X86_64_ONLY(GBLDEF boolean_t force_32 = FALSE;) X86_64_ONLY(GBLDEF emit_base_info_struct emit_base_info;) GBLREF int curr_addr; GBLREF char cg_phase; /* code generation phase */ GBLREF char cg_phase_last; /* the previous code generation phase */ DEBUG_ONLY(static boolean_t opcode_emitted;) static int stack_depth = 0; /* Variables for counting the arguments */ static int vax_pushes_seen, vax_number_of_arguments; static struct push_list { struct push_list *next; unsigned char value[PUSH_LIST_SIZE]; } *current_push_list_ptr, *push_list_start_ptr; static int push_list_index; static boolean_t ocnt_ref_seen = FALSE; static oprtype *ocnt_ref_opr; static triple *current_triple; error_def(ERR_MAXARGCNT); error_def(ERR_SRCNAM); error_def(ERR_UNIMPLOP); /* Create generated intermediate code (similar to VAX instructions) for the given triple and then, * ultimately, the corresponding native platform generated code. */ void trip_gen(triple *ct) { oprtype **sopr, *opr; /* triple operand */ oprtype *saved_opr[MAX_ARGS]; uint4 oct; short tp; /* template pointer */ const short *tsp; /* template short pointer */ triple *ttp; /* temp triple pointer */ int irep_index; oprtype *irep_opr; const short *repl; /* temp irep ptr */ short repcnt; int off; # if !defined(TRUTH_IN_REG) && (!(defined(__osf__) || defined(__x86_64__) || defined(Linux390))) assertpro(FALSE); # endif DEBUG_ONLY(opcode_emitted = FALSE); current_triple = ct; /* save for possible use by internal rtns */ tp = ttt[ct->opcode]; if (0 >= tp) { assert(FALSE && ct->opcode); stx_error(ERR_UNIMPLOP); return; } code_idx = 0; vax_pushes_seen = 0; vax_number_of_arguments = 0; if (cg_phase_last != cg_phase) { cg_phase_last = cg_phase; if (CGP_APPROX_ADDR == cg_phase) push_list_init(); else reset_push_list_ptr(); } code_reference = ct->rtaddr; oct = oc_tab[ct->opcode].octype; sopr = &saved_opr[0]; *sopr++ = &ct->destination; for (ttp = ct, opr = ttp->operand ; opr < ARRAYTOP(ttp->operand); ) { if (opr->oprclass) { if ((TRIP_REF == opr->oprclass) && (OC_PARAMETER == opr->oprval.tref->opcode)) { ttp = opr->oprval.tref; opr = ttp->operand; continue; } *sopr++ = opr; if (ARRAYTOP(saved_opr) <= sopr) /* user-visible max args is MAX_ARGS - 3 */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_MAXARGCNT, 1, MAX_ARGS - 3); } opr++; } *sopr = 0; jmp_offset = 0; if ((OCT_JUMP & oct) || (OC_LDADDR == ct->opcode) || (OC_FORLOOP == ct->opcode)) { assert(ct->operand[0].oprval.tref); if (0 == ct->operand[0].oprval.tref->rtaddr) /* forward reference */ { jmp_offset = LONG_JUMP_OFFSET; assert(CGP_APPROX_ADDR == cg_phase); } else jmp_offset = ct->operand[0].oprval.tref->rtaddr - ct->rtaddr; switch(ct->opcode) { case OC_CALL: case OC_FORLCLDO: case OC_CALLSP: # ifdef __x86_64__ tsp = (short *)&ttt[ttt[tp]]; if ((-128 <= tsp[CALL_4LCLDO_XFER]) && (127 >= tsp[CALL_4LCLDO_XFER])) off = jmp_offset - XFER_BYTE_INST_SIZE; else off = jmp_offset - XFER_LONG_INST_SIZE; if ((-128 <= (off - BRB_INST_SIZE)) && (127 >= (off - BRB_INST_SIZE))) call_4lcldo_variant = BRB_INST_SIZE; /* used by emit_jmp */ else { call_4lcldo_variant = JMP_LONG_INST_SIZE; /* used by emit_jmp */ tsp = (short *)&ttt[ttt[tp + 1]]; if ((-128 <= tsp[CALL_4LCLDO_XFER]) && (127 >= tsp[CALL_4LCLDO_XFER])) off = jmp_offset - XFER_BYTE_INST_SIZE; else off = jmp_offset - XFER_LONG_INST_SIZE; if ((-32768 > (off - JMP_LONG_INST_SIZE)) && (32767 < (off - JMP_LONG_INST_SIZE))) tsp = (short *)&ttt[ttt[tp + 2]]; } break; # else off = (jmp_offset - CALL_INST_SIZE)/INST_SIZE; /* [kmk] */ if ((-128 <= off) && (127 >= off)) tsp = &ttt[ttt[tp]]; else if ((-32768 <= off) && (32767 >= off)) tsp = &ttt[ttt[tp + 1]]; else tsp = &ttt[ttt[tp + 2]]; break; # endif /* __x86_64__ */ case OC_JMP: case OC_JMPEQU: case OC_JMPGEQ: case OC_JMPGTR: case OC_JMPLEQ: case OC_JMPNEQ: case OC_JMPLSS: case OC_JMPTSET: case OC_JMPTCLR: case OC_LDADDR: case OC_FORLOOP: tsp = &ttt[ttt[tp]]; break; default: assertpro(FALSE && ct->opcode); } } else if (OCT_COERCE & oct) { switch (oc_tab[ct->operand[0].oprval.tref->opcode].octype & (OCT_VALUE | OCT_BOOL)) { case OCT_MVAL: tp = ttt[tp]; break; case OCT_MINT: tp = ttt[tp + 3]; break; case OCT_BOOL: tp = ttt[tp + 4]; break; default: assertpro(FALSE && (oc_tab[ct->operand[0].oprval.tref->opcode].octype & (OCT_VALUE | OCT_BOOL))); break; } tsp = &ttt[tp]; } else tsp = &ttt[tp]; for (; *tsp != VXT_END;) { if ((VXT_IREPAB == *tsp) || (VXT_IREPL == *tsp)) { repl = tsp; repl += 2; repcnt = *repl++; assert(1 != repcnt); for (irep_index = repcnt, irep_opr = &ct->operand[1]; irep_index > 2; --irep_index) { assert(TRIP_REF == irep_opr->oprclass); irep_opr = &irep_opr->oprval.tref->operand[1]; } if (TRIP_REF == irep_opr->oprclass) { repl = tsp; do { tsp = repl; tsp = emit_vax_inst((short *)tsp, &saved_opr[0], --sopr); # ifdef DEBUG if (CGP_ASSEMBLY == cg_phase) emit_asmlist(ct); # endif } while (sopr > &saved_opr[repcnt]); } else { sopr = &saved_opr[repcnt]; tsp = repl; } } else { assert((NULL != tsp) && (511 >= *tsp)); tsp = emit_vax_inst((short *)tsp, &saved_opr[0], sopr); # ifdef DEBUG if (CGP_ASSEMBLY == cg_phase) emit_asmlist(ct); # endif } /* else */ } /* for */ if (CGP_APPROX_ADDR == cg_phase) if (0 < vax_pushes_seen) add_to_vax_push_list(vax_pushes_seen); } #ifdef DEBUG /* Create assembler listing for given triple showing both intermediate (VAX) code and generated native code */ void emit_asmlist(triple *ct) { int offset; unsigned char *c; obpt -= 2; *obpt = ' '; /* erase trailing comma */ if (!opcode_emitted) { opcode_emitted = TRUE; offset = (int)(&outbuf[0] + 60 - obpt); if (1 <= offset) { /* tab to position 60 */ memset(obpt, ' ', offset); obpt += offset; } else { /* leave at least 2 spaces */ memset(obpt, ' ', 2); obpt += 2; } *obpt++ = ';'; for (c = (unsigned char*)oc_tab_graphic[ct->opcode]; *c;) *obpt++ = *c++; } emit_eoi(); format_machine_inst(); } void emit_eoi (void) { IA64_ONLY(if (0 == asm_mode) {) *obpt++ = '\0'; list_tab(); list_line((char *)outbuf); IA64_ONLY(}) return; } #endif /* Create generated code for the given intermediate language opcodes (similar to VAX instructions) */ short *emit_vax_inst (short *inst, oprtype **fst_opr, oprtype **lst_opr) { /* fst_opr and lst_opr are triple operands */ static short last_vax_inst = 0; short sav_in, save_inst; boolean_t oc_int; oprtype *opr; triple *ct; int cnt, cnttop, reg, words_to_move, reg_offset, save_reg_offset, targ_reg; int branch_idx, branch_offset, loop_top_idx, instr; code_idx = 0; switch (cg_phase) { case CGP_ASSEMBLY: # ifdef DEBUG list_chkpage(); obpt = &outbuf[0]; memset(obpt, SP, SIZEOF(outbuf)); i2asc((uchar_ptr_t)obpt, vaxi_cnt++); obpt += 7; if ((VXT_IREPAB != *inst) && (VXT_IREPL != *inst)) instr = *inst; else instr = (*inst == VXT_IREPAB) ? VXI_PUSHAB : VXI_PUSHL; memcpy(obpt, &vxi_opcode[instr][0], 6); obpt += 10; *obpt++ = SP; *obpt++ = SP; /***** WARNING - FALL THRU *****/ # endif case CGP_ADDR_OPT: case CGP_APPROX_ADDR: case CGP_MACHINE: switch ((sav_in = *inst++)) { case VXI_BEQL: emit_jmp(GENERIC_OPCODE_BEQ, &inst, GTM_REG_COND_CODE); break; case VXI_BGEQ: emit_jmp(GENERIC_OPCODE_BGE, &inst, GTM_REG_COND_CODE); break; case VXI_BGTR: emit_jmp(GENERIC_OPCODE_BGT, &inst, GTM_REG_COND_CODE); break; case VXI_BLEQ: emit_jmp(GENERIC_OPCODE_BLE, &inst, GTM_REG_COND_CODE); break; case VXI_BLSS: emit_jmp(GENERIC_OPCODE_BLT, &inst, GTM_REG_COND_CODE); break; case VXI_BNEQ: emit_jmp(GENERIC_OPCODE_BNE, &inst, GTM_REG_COND_CODE); break; case VXI_BLBC: case VXI_BLBS: assert(VXT_REG == *inst); inst++; # ifdef TRUTH_IN_REG reg = GTM_REG_CODEGEN_TEMP; NON_GTM64_ONLY(GEN_LOAD_WORD(reg, gtm_reg(*inst++), 0)); GTM64_ONLY( GEN_LOAD_WORD_4(reg, gtm_reg(*inst++), 0)); # else /* For platforms, where the $TRUTH value is not carried in a register and * must be fetched from a global variable by subroutine call. */ assert(0x5a == *inst); /* VAX r10 or $TEST register */ inst++; emit_call_xfer(SIZEOF(intszofptr_t) * xf_dt_get); reg = GTM_REG_R0; /* function return value */ # endif /* Generate a cmp instruction using the return value of the previous call, * which will be in EAX. */ X86_64_ONLY(GEN_CMP_EAX_IMM32(0)); if (VXI_BLBC == sav_in) { X86_64_ONLY(emit_jmp(GENERIC_OPCODE_BEQ, &inst, 0)); NON_X86_64_ONLY(emit_jmp(GENERIC_OPCODE_BLBC, &inst, reg)); } else { assert(VXI_BLBS == sav_in); X86_64_ONLY(emit_jmp(GENERIC_OPCODE_BNE, &inst, 0)); NON_X86_64_ONLY(emit_jmp(GENERIC_OPCODE_BLBS, &inst, reg)); } break; case VXI_BRB: emit_jmp(GENERIC_OPCODE_BR, &inst, 0); break; case VXI_BRW: emit_jmp(GENERIC_OPCODE_BR, &inst, 0); break; case VXI_BICB2: # ifdef TRUTH_IN_REG GEN_CLEAR_TRUTH; # endif assert(VXT_LIT == *inst); inst++; assert(1 == *inst); inst++; assert(VXT_REG == *inst); inst++; inst++; break; case VXI_BISB2: # ifdef TRUTH_IN_REG GEN_SET_TRUTH; # endif assert(VXT_LIT == *inst); inst++; assert(1 == *inst); inst++; assert(VXT_REG == *inst); inst++; inst++; break; case VXI_CALLS: oc_int = TRUE; if (VXT_LIT == *inst) { inst++; cnt = (int4)*inst++; } else { assert(VXT_VAL == *inst); inst++; opr = *(fst_opr + *inst); assert(TRIP_REF == opr->oprclass); ct = opr->oprval.tref; if (ct->destination.oprclass) opr = &ct->destination; # ifdef __vms /* This is a case where VMS puts the argument count in a special register so * handle that differently here. */ if (TRIP_REF == opr->oprclass) { assert(OC_ILIT == ct->opcode); cnt = ct->operand[0].oprval.ilit; code_buf[code_idx++] = ALPHA_INS_LDA | ALPHA_REG_AI << ALPHA_SHIFT_RA | ALPHA_REG_ZERO << ALPHA_SHIFT_RB | (cnt & ALPHA_MASK_DISP) << ALPHA_SHIFT_DISP; inst++; } else { assert(TINT_REF == opr->oprclass); oc_int = FALSE; opr = *(fst_opr + *inst++); reg = get_arg_reg(); emit_trip(opr, TRUE, ALPHA_INS_LDL, reg); emit_push(reg); } # else /* All other platforms put argument counts in normal parameter * registers and go through this path instead. */ if (TRIP_REF == opr->oprclass) { assert(OC_ILIT == ct->opcode); cnt = ct->operand[0].oprval.ilit; reg = get_arg_reg(); IA64_ONLY(LOAD_IMM14(reg, cnt)); NON_IA64_ONLY(GEN_LOAD_IMMED(reg, cnt)); cnt++; inst++; } else { assert(TINT_REF == opr->oprclass); oc_int = FALSE; opr = *(fst_opr + *inst++); reg = get_arg_reg(); emit_trip(opr, TRUE, GENERIC_OPCODE_LOAD, reg); } emit_push(reg); # endif } assert(VXT_XFER == *inst); inst++; emit_call_xfer((int)*inst++); if (oc_int) { if (0 != cnt) emit_pop(cnt); } else { /* During the commonization of emit_code.c I discovered that TINT_REF is * not currently used in the compiler so this may be dead code but I'm * leaving this path in here anyway because I don't want to put it back * in if we find we need it. (4/2003 SE) */ emit_trip(opr, TRUE, GENERIC_OPCODE_LOAD, CALLS_TINT_TEMP_REG); emit_pop(1); } break; case VXI_CLRL: assert(VXT_VAL == *inst); inst++; GEN_CLEAR_WORD_EMIT(CLRL_REG); break; case VXI_CMPL: assert(VXT_VAL == *inst); inst++; GEN_LOAD_WORD_EMIT(CMPL_TEMP_REG); assert(VXT_VAL == *inst); inst++; X86_64_ONLY(GEN_LOAD_WORD_EMIT(GTM_REG_CODEGEN_TEMP)); NON_X86_64_ONLY(GEN_LOAD_WORD_EMIT(GTM_REG_COND_CODE)); X86_64_ONLY(GEN_CMP_REGS(CMPL_TEMP_REG, GTM_REG_CODEGEN_TEMP)); NON_X86_64_ONLY(GEN_SUBTRACT_REGS(CMPL_TEMP_REG, GTM_REG_COND_CODE, GTM_REG_COND_CODE)); break; case VXI_INCL: assert(VXT_VAL == *inst); inst++; save_inst = *inst++; emit_trip(*(fst_opr + save_inst), TRUE, GENERIC_OPCODE_LOAD, GTM_REG_ACCUM); GEN_ADD_IMMED(GTM_REG_ACCUM, 1); emit_trip(*(fst_opr + save_inst), TRUE, GENERIC_OPCODE_STORE, GTM_REG_ACCUM); break; case VXI_JMP: if (VXT_VAL == *inst) { inst++; emit_trip(*(fst_opr + *inst++), FALSE, GENERIC_OPCODE_LOAD, GTM_REG_CODEGEN_TEMP); GEN_JUMP_REG(GTM_REG_CODEGEN_TEMP); } else emit_jmp(GENERIC_OPCODE_BR, &inst, 0); break; case VXI_JSB: assert(VXT_XFER == *inst); inst++; emit_call_xfer((int)*inst++); /* Callee may have popped some values so we can't count on anything left on the stack. */ stack_depth = 0; break; case VXI_MOVAB: if (VXT_JMP == *inst) { inst += 2; emit_pcrel(); NON_RISC_ONLY(IGEN_LOAD_ADDR_REG(GTM_REG_ACCUM)) RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_ADDR_REG(GTM_REG_ACCUM)); assert(VXT_ADDR == *inst); inst++; emit_trip(*(fst_opr + *inst++), FALSE, GENERIC_OPCODE_STORE, GTM_REG_ACCUM); } else if ((VXT_ADDR == *inst) || (VXT_VAL == *inst)) { boolean_t addr; addr = (VXT_VAL == *inst); inst++; save_inst = *inst++; assert(VXT_REG == *inst); inst++; emit_trip(*(fst_opr + save_inst), addr, GENERIC_OPCODE_LDA, gtm_reg(*inst++)); } else assertpro(FALSE && *inst); break; case VXI_MOVC3: /* The MOVC3 instruction is only used to copy an mval from one place to another * so that is the expansion we will generate. */ assert(VXT_LIT == *inst); inst += 2; assert(VXT_VAL == *inst); inst++; emit_trip(*(fst_opr + *inst++), TRUE, GENERIC_OPCODE_LDA, MOVC3_SRC_REG); assert(VXT_VAL == *inst); inst++; emit_trip(*(fst_opr + *inst++), TRUE, GENERIC_OPCODE_LDA, MOVC3_TRG_REG); # if defined(__MVS__) || defined(Linux390) /* The MVC instruction on zSeries facilitates memory copy(mval in this case) in a single * instruction instead of multiple 8/4 byte copies. */ GEN_MVAL_COPY(MOVC3_SRC_REG, MOVC3_TRG_REG, SIZEOF(mval)); # elif defined(__linux) && defined(__x86_64__) /* The latest x86-64 processors have optimized REP MOVSQ to be the fastest way to move * medium/small strings (like mvals which are always at least 8 byte aligned). This also * significantly reduces the codegen. */ GEN_MVAL_COPY(MOVC3_SRC_REG, MOVC3_TRG_REG, MVAL_QWORD_SIZE); # else /* For any non-special-cased platforms, the most efficient expansion is to generate a * series of load and store instructions. Do the loads first then the stores to keep the * pipelines flowing and not stall waiting for any given load or store to complete. Because * some platforms do not have enough argument registers to contain an entire MVAL and * because an mval may grow from its present size and affect other platforms some day, * we put the whole mval copy code gen thing in a loop so we can do this regardless of how * big it gets. */ for (words_to_move = MVAL_INT_SIZE, reg_offset = 0; words_to_move;) { reg = MACHINE_FIRST_ARG_REG; save_reg_offset = reg_offset; for (cnt = 0, cnttop = MIN(words_to_move, MACHINE_REG_ARGS) ; cnt < cnttop; cnt++, reg_offset += SIZEOF(UINTPTR_T)) { X86_64_ONLY(targ_reg = GET_ARG_REG(cnt)); NON_X86_64_ONLY(targ_reg = reg + cnt); NON_GTM64_ONLY(GEN_LOAD_WORD(targ_reg, MOVC3_SRC_REG, reg_offset)); GTM64_ONLY(GEN_LOAD_WORD_8(targ_reg, MOVC3_SRC_REG, reg_offset)); } reg = MACHINE_FIRST_ARG_REG; for (cnt = 0; cnt < cnttop; cnt++, save_reg_offset += SIZEOF(UINTPTR_T), words_to_move--) { X86_64_ONLY(targ_reg = GET_ARG_REG(cnt)); NON_X86_64_ONLY(targ_reg = reg + cnt); NON_GTM64_ONLY(GEN_STORE_WORD(targ_reg, MOVC3_TRG_REG, save_reg_offset)); GTM64_ONLY(GEN_STORE_WORD_8(targ_reg, MOVC3_TRG_REG, save_reg_offset)); } } # endif break; case VXI_MOVL: if (VXT_REG == *inst) { inst++; if (0x5f < *inst) /* OC_CURRHD: any mode >= 6 (deferred), any register */ { inst++; NON_GTM64_ONLY(GEN_LOAD_WORD(GTM_REG_ACCUM, GTM_REG_FRAME_POINTER, 0)); GTM64_ONLY(GEN_LOAD_WORD_8(GTM_REG_ACCUM, GTM_REG_FRAME_POINTER, 0)); assert(VXT_ADDR == *inst); inst++; emit_trip(*(fst_opr + *inst++), FALSE, GENERIC_OPCODE_STORE, GTM_REG_ACCUM); } else { boolean_t addr; assert(0x50 == *inst); /* register mode: (from) r0 */ inst++; if ((VXT_VAL == *inst) || (VXT_ADDR == *inst)) { addr = (VXT_VAL == *inst); inst++; emit_trip(*(fst_opr + *inst++), addr, GENERIC_OPCODE_STORE, MOVL_RETVAL_REG); } else if (VXT_REG == *inst) { inst++; # ifdef TRUTH_IN_REG if (0x5a == *inst) /* to VAX r10 or $TEST */ { NON_GTM64_ONLY(GEN_STORE_WORD(MOVL_RETVAL_REG, GTM_REG_DOLLAR_TRUTH, 0)); GTM64_ONLY(GEN_STORE_WORD_4(MOVL_RETVAL_REG, GTM_REG_DOLLAR_TRUTH, 0)); } else { GEN_MOVE_REG(gtm_reg(*inst), MOVL_RETVAL_REG); } # else if (0x5a == *inst) /* to VAX r10 or $TEST */ { reg = get_arg_reg(); GEN_MOVE_REG(reg, MOVL_RETVAL_REG); emit_push(reg); emit_call_xfer(SIZEOF(intszofptr_t) * xf_dt_store); } else { GEN_MOVE_REG(gtm_reg(*inst), MOVL_RETVAL_REG); } # endif inst++; } else assertpro(FALSE && *inst); } } else if (VXT_VAL == *inst) { inst++; save_inst = *inst++; assert(VXT_REG == *inst); inst++; assert(0x51 == *inst); /* register mode: R1 */ inst++; emit_trip(*(fst_opr + save_inst), TRUE, GENERIC_OPCODE_LOAD, MOVL_REG_R1); } else assertpro(FALSE && *inst); break; case VXT_IREPAB: assert(VXT_VAL == *inst); inst += 2; reg = get_arg_reg(); emit_trip(*lst_opr, TRUE, GENERIC_OPCODE_LDA, reg); emit_push(reg); break; case VXI_PUSHAB: reg = get_arg_reg(); if (VXT_JMP == *inst) { inst += 2; emit_pcrel(); NON_RISC_ONLY(IGEN_LOAD_ADDR_REG(reg)) RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_ADDR_REG(reg)); } else if ((VXT_VAL == *inst) || (VXT_GREF == *inst)) { inst++; emit_trip(*(fst_opr + *inst++), TRUE, GENERIC_OPCODE_LDA, reg); } else assertpro(FALSE && *inst); emit_push(reg); break; case VXT_IREPL: assert(VXT_VAL == *inst); inst += 2; reg = get_arg_reg(); emit_trip(*lst_opr, TRUE, GENERIC_OPCODE_LOAD, reg); emit_push(reg); break; case VXI_PUSHL: reg = get_arg_reg(); if (VXT_LIT == *inst) { inst++; GEN_LOAD_IMMED(reg, *inst); inst++; } else if (VXT_ADDR == *inst) { inst++; emit_trip(*(fst_opr + *inst++), FALSE, GENERIC_OPCODE_LOAD, reg); } else if (VXT_VAL == *inst) { inst++; emit_trip(*(fst_opr + *inst++), TRUE, GENERIC_OPCODE_LOAD, reg); } else assertpro(FALSE && *inst); emit_push(reg); break; case VXI_TSTL: assert((VXT_VAL == *inst) || (VXT_REG == *inst)); if (VXT_VAL == *inst) { inst++; emit_trip( *(fst_opr + *inst++), TRUE, GENERIC_OPCODE_LOAD, X86_64_ONLY(GTM_REG_CODEGEN_TEMP) NON_X86_64_ONLY(GTM_REG_COND_CODE) ); X86_64_ONLY(GEN_CMP_IMM32(GTM_REG_CODEGEN_TEMP, 0)); } else if (VXT_REG == *inst) { inst++; X86_64_ONLY(assert(I386_REG_RAX == gtm_reg(*inst)); /* Same as R0 */) X86_64_ONLY(GEN_CMP_EAX_IMM32(0)); NON_X86_64_ONLY(GEN_MOVE_REG(GTM_REG_COND_CODE, gtm_reg(*inst))); inst++; } break; default: assertpro(FALSE && sav_in); break; } break; default: assertpro(FALSE && cg_phase); break; } assert(NUM_BUFFERRED_INSTRUCTIONS > code_idx); if (CGP_MACHINE == cg_phase) { generated_code_size += code_idx; # ifdef DEBUG if (MAX_CODE_COUNT > generated_count) { generated_details[generated_count].size = code_idx; generated_details[generated_count++].sav_in = sav_in; } # endif /* DEBUG */ emit_immed ((char *)&code_buf[0], (uint4)(INST_SIZE * code_idx)); } else if (CGP_ASSEMBLY != cg_phase) { if (CGP_APPROX_ADDR == cg_phase) { calculated_code_size += code_idx; # ifdef DEBUG if (MAX_CODE_COUNT > calculated_count) { calculated_details[calculated_count].size = code_idx; calculated_details[calculated_count++].sav_in = sav_in; } # endif /* DEBUG */ } curr_addr += (INST_SIZE * code_idx); } code_reference += (INST_SIZE * code_idx); jmp_offset -= (INST_SIZE * code_idx); last_vax_inst = sav_in; return inst; } #ifndef __x86_64__ /* For x86_64, this is defined in emit_code_sp.c */ void emit_jmp(uint4 branchop, short **instp, int reg) { uint4 branchop_opposite; int src_reg; int skip_idx; NON_RISC_ONLY(int tmp_code_idx;) int branch_offset; /* assert(jmp_offset != 0); */ /* assert commented since jmp_offset could be zero in CGP_ADDR_OPT phase after a jump to the immediately following * instruction is nullified (as described below) */ /* size of this particular instruction */ jmp_offset -= (int)((char *)&code_buf[code_idx] - (char *)&code_buf[0]); # if !(defined(__MVS__) || defined(Linux390)) /* The code_buff on zSeries is filled with 2 byte chunks */ assert(0 == (jmp_offset & 3)); # endif branch_offset = jmp_offset / INST_SIZE; /* Some platforms have a different origin for the offset */ EMIT_JMP_ADJUST_BRANCH_OFFSET; switch (cg_phase) { # ifdef DEBUG case CGP_ASSEMBLY: *obpt++ = 'x'; *obpt++ = '^'; *obpt++ = '0'; *obpt++ = 'x'; obpt += i2hex_nofill(INST_SIZE * branch_offset, (uchar_ptr_t)obpt, 8); *obpt++ = ','; *obpt++ = ' '; /***** WARNING - FALL THRU *****/ # endif case CGP_ADDR_OPT: case CGP_APPROX_ADDR: case CGP_MACHINE: assert(VXT_JMP == **instp); *instp += 1; assert(1 == **instp); (*instp)++; if (0 == branch_offset) { /* This is a jump to the immediately following instruction. Nullify the jump * and don't generate any instruction (not even a NOP) */ /* code_buf[code_idx++] = GENERIC_OPCODE_NOP; */ } else if (EMIT_JMP_SHORT_CODE_CHECK) { /* Short jump immediate operand - some platforms also do a compare */ EMIT_JMP_SHORT_CODE_GEN; } else { /* Potentially longer jump sequence */ skip_idx = -1; if (EMIT_JMP_OPPOSITE_BR_CHECK) { /* This jump sequence is longer and is not conditional so if we need a conditional * jump, create the opposite conditional jump to jump around the longer jump to * the target thereby preserving the original semantics. */ EMIT_JMP_GEN_COMPARE; switch (branchop) { case GENERIC_OPCODE_BEQ: branchop_opposite = GENERIC_OPCODE_BNE; break; case GENERIC_OPCODE_BGE: branchop_opposite = GENERIC_OPCODE_BLT; break; case GENERIC_OPCODE_BGT: branchop_opposite = GENERIC_OPCODE_BLE; break; case GENERIC_OPCODE_BLE: branchop_opposite = GENERIC_OPCODE_BGT; break; case GENERIC_OPCODE_BLT: branchop_opposite = GENERIC_OPCODE_BGE; break; case GENERIC_OPCODE_BNE: branchop_opposite = GENERIC_OPCODE_BEQ; break; # ifdef __alpha case GENERIC_OPCODE_BLBC: branchop_opposite = GENERIC_OPCODE_BLBS; break; case GENERIC_OPCODE_BLBS: branchop_opposite = GENERIC_OPCODE_BLBC; break; # endif default: assertpro(FALSE && branchop); break; } RISC_ONLY( skip_idx = code_idx++; /* Save index of branch inst. Set target offset later */ code_buf[skip_idx] = IGEN_COND_BRANCH_REG_OFFSET(branchop_opposite, reg, 0); branch_offset--; ) NON_RISC_ONLY( skip_idx = code_idx; /* Save index of branch inst. Set target offset later */ IGEN_COND_BRANCH_REG_OFFSET(branchop_opposite, reg, 0) branch_offset -= NUM_INST_IGEN_COND_BRANCH_REG_OFFSET; ) # ifdef DELAYED_BRANCH code_buf[code_idx++] = GENERIC_OPCODE_NOP; branch_offset--; # endif } if (EMIT_JMP_LONG_CODE_CHECK) { /* This is more common unconditional branch generation and should be mutually * exclusive to EMIT_JMP_OPPOSITE_BR_CHECK. Some platforms will have the "short" * branch generation up top be more common but that form does not cover unconditional * jumps (Examples: AIX and HP-UX) */ assert(!(EMIT_JMP_OPPOSITE_BR_CHECK)); NON_RISC_ONLY(IGEN_UCOND_BRANCH_REG_OFFSET(branchop, branch_offset)) RISC_ONLY( code_buf[code_idx++] = IGEN_UCOND_BRANCH_REG_OFFSET(branchop, 0, branch_offset); ) # ifdef DELAYED_BRANCH code_buf[code_idx++] = GENERIC_OPCODE_NOP; # endif } else { if (EMIT_JMP_OPPOSITE_BR_CHECK) { /* VAX conditional long jump generates two native branch instructions - * one conditional branch (above) and one PC relative branch (below). * The second branch instruction also needs adjustment of the origin. */ EMIT_JMP_ADJUST_BRANCH_OFFSET; } GEN_PCREL; emit_base_offset(GTM_REG_CODEGEN_TEMP, (INST_SIZE * branch_offset)); RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_ADDR_REG(GTM_REG_CODEGEN_TEMP)); NON_RISC_ONLY(IGEN_LOAD_ADDR_REG(GTM_REG_CODEGEN_TEMP)) GEN_JUMP_REG(GTM_REG_CODEGEN_TEMP); } if (-1 != skip_idx) { /* Fill in the offset from our opposite jump instruction to here .. the * place to bypass the jump. */ branch_offset = BRANCH_OFFSET_FROM_IDX(skip_idx, code_idx); RISC_ONLY(code_buf[skip_idx] |= IGEN_COND_BRANCH_OFFSET(branch_offset)); NON_RISC_ONLY( tmp_code_idx = code_idx; code_idx = skip_idx; IGEN_COND_BRANCH_REG_OFFSET(branchop_opposite, reg, branch_offset) code_idx = tmp_code_idx; ) } } break; default: assertpro(FALSE && cg_phase); break; } } #endif /* !__x86_64__ */ /* Emit code that generates a relative pc based jump target. The last instruction is not * complete so the caller may finish it with whatever instruction is necessary. */ void emit_pcrel(void) { int branch_offset; jmp_offset -= INTCAST((char *)&code_buf[code_idx] - (char *)&code_buf[0]); switch (cg_phase) { # ifdef DEBUG case CGP_ASSEMBLY: *obpt++ = 'x'; *obpt++ = '^'; *obpt++ = '0'; *obpt++ = 'x'; obpt += i2hex_nofill(jmp_offset + code_reference, (uchar_ptr_t)obpt, 8); *obpt++ = ','; *obpt++ = ' '; /***** WARNING - FALL THRU *****/ # endif case CGP_ADDR_OPT: case CGP_APPROX_ADDR: case CGP_MACHINE: branch_offset = jmp_offset / INST_SIZE; GEN_PCREL; EMIT_JMP_ADJUST_BRANCH_OFFSET; /* Account for different branch origins on different platforms */ emit_base_offset(GTM_REG_CODEGEN_TEMP, INST_SIZE * branch_offset); break; default: assertpro(FALSE && cg_phase); break; } } /* Emit the code for a given triple */ void emit_trip(oprtype *opr, boolean_t val_output, uint4 generic_inst, int trg_reg) { boolean_t inst_emitted; unsigned char reg, op_mod, op_reg; int offset, immediate; int upper_idx, lower_idx; triple *ct; int low, extra, high; GTM64_ONLY(int next_ptr_offset = 8;) if (TRIP_REF == opr->oprclass) { ct = opr->oprval.tref; if (ct->destination.oprclass) opr = &ct->destination; /* else lit or error */ } inst_emitted = FALSE; switch (cg_phase) { case CGP_ADDR_OPT: case CGP_APPROX_ADDR: switch (opr->oprclass) { case TRIP_REF: assert(0 == ct->destination.oprclass); assert(val_output); switch (ct->opcode) { case OC_LIT: assert(MLIT_REF == ct->operand[0].oprclass); if (run_time) reg = GTM_REG_PV; else reg = GTM_REG_LITERAL_BASE; if (CGP_ADDR_OPT == cg_phase) { /* We want the expansion to be proper sized this time. Note * that this won't be true so much on the initial CGP_ADDR_OPT * pass but will be true on the shrink_trips() pass after the * literals are compiled. */ offset = literal_offset(ct->operand[0].oprval.mlit->rt_addr); /* Need non-zero base reg for AIX */ X86_64_ONLY(force_32 = TRUE); emit_base_offset(reg, offset); X86_64_ONLY(force_32 = FALSE); } else { /* Gross expansion ok first time through */ /* Non-0 base reg for AIX */ X86_64_ONLY(force_32 = TRUE); emit_base_offset(reg, LONG_JUMP_OFFSET); X86_64_ONLY(force_32 = FALSE); } X86_64_ONLY(IGEN_LOAD_ADDR_REG(trg_reg)) # if !(defined(__MVS__) || defined(Linux390)) NON_X86_64_ONLY(code_idx++); # else IGEN_LOAD_ADDR_REG(trg_reg); # endif inst_emitted = TRUE; break; case OC_CDLIT: emit_base_offset(GTM_REG_PV, find_linkage(ct->operand[0].oprval.cdlt)); if (GENERIC_OPCODE_LDA == generic_inst) { RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_LINKAGE(trg_reg)); NON_RISC_ONLY(IGEN_LOAD_LINKAGE(trg_reg)); inst_emitted = TRUE; } else { RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_LINKAGE(GTM_REG_CODEGEN_TEMP)); NON_RISC_ONLY(IGEN_LOAD_LINKAGE(GTM_REG_CODEGEN_TEMP)); emit_base_offset(GTM_REG_CODEGEN_TEMP, 0); } break; case OC_CDIDX: assert(GENERIC_OPCODE_LOAD == generic_inst); /* Fetch linkage table offset for symbol and convert to index */ immediate = find_linkage(ct->operand[0].oprval.cdidx) / SIZEOF(lnk_tabent); EMIT_TRIP_ILIT_GEN; inst_emitted = TRUE; break; case OC_ILIT: assert(GENERIC_OPCODE_LOAD == generic_inst); immediate = ct->operand[0].oprval.ilit; EMIT_TRIP_ILIT_GEN; inst_emitted = TRUE; break; case OC_TRIPSIZE: /* This tiples value is calculated in the shrink_jmp/shrink_trips * phase. It is a parameter to (currently only) op_exfun and is the * length of the generated jump instruction. op_exfun needs this * length to adjust the return address in the created stackframe * so it does not have to parse instructions at the return address * to see what return signature was created. We will add asserts to * this generation in later phases after the true value has been * calculated. At this point, it is zero. */ immediate = ct->operand[0].oprval.tsize->size; EMIT_TRIP_ILIT_GEN; inst_emitted = TRUE; break; default: assertpro(FALSE && ct->opcode); break; } break; case TINT_REF: case TVAL_REF: assert(val_output); offset = sa_temps_offset[opr->oprclass]; offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; GTM64_ONLY( if (4 == sa_class_sizes[opr->oprclass]) { next_ptr_offset = 4; REVERT_GENERICINST_TO_WORD(generic_inst); } ) NON_GTM64_ONLY(assertpro((0 <= offset) && (MAX_OFFSET >= offset))); emit_base_offset(GTM_REG_FRAME_TMP_PTR, offset); break; case TCAD_REF: case TVAD_REF: case TVAR_REF: offset = sa_temps_offset[opr->oprclass]; offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; GTM64_ONLY( if (4 == sa_class_sizes[opr->oprclass]) { next_ptr_offset = 4; REVERT_GENERICINST_TO_WORD(generic_inst); } ) NON_GTM64_ONLY(assertpro((0 <= offset) && (MAX_OFFSET >= offset))); if (TVAR_REF == opr->oprclass) reg = GTM_REG_FRAME_VAR_PTR; else reg = GTM_REG_FRAME_TMP_PTR; emit_base_offset(reg, offset); if (val_output) { if (GENERIC_OPCODE_LDA == generic_inst) { NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(trg_reg)); RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_NATIVE_REG(trg_reg)); if (TVAR_REF == opr->oprclass) { emit_base_offset(trg_reg, offsetof(ht_ent_mname, value)); NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(trg_reg)); RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_NATIVE_REG(trg_reg)); } inst_emitted = TRUE; } else { NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(GTM_REG_CODEGEN_TEMP)); RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_NATIVE_REG(GTM_REG_CODEGEN_TEMP)); emit_base_offset(GTM_REG_CODEGEN_TEMP, 0); } } break; case OCNT_REF: /* This ref's value is calculated in emit_call_xfer(). This value is related to TSIZ_REF * in that it is used for the same reason to different calls (op_call, op_callsp, * op_forlcldo, and their mprof counterparts). It is the offset needed to be * added to the return address from the calls to these routines to bypass a * generated jump sequence. In this case however, the jump sequence is being * generated as part of the OC_CALL, OC_CALLSP or OC_FORLCLDO triple itself. * There is no separate jump triple so the TSIZ_REF triple cannot be used. * So this operand is the OFFSET from the CALL to the NEXT TRIPLE. The operation * is that when this routine sees this type of reference, it will set a flag * and record the operand address and go ahead and generate the value that it has. The * next transfer table generation that occurs will see the set flag and will compute * the address from the return address of that transfer table call to the next triple * and update this triple's value. Since our originating triple has a JUMP type, * it will be updated in shrink_jmp/shirnk_trips() until all necessary shrinkage * is done so the final phase will have the correct value and we only have to * generate an immediate value. */ immediate = opr->oprval.offset; EMIT_TRIP_ILIT_GEN; inst_emitted = TRUE; ocnt_ref_seen = TRUE; ocnt_ref_opr = opr; break; default: assert(FALSE && opr->oprclass); break; /* NOTREACHED */ } if (!inst_emitted) { NON_RISC_ONLY(IGEN_GENERIC_REG(generic_inst, trg_reg)); RISC_ONLY(code_buf[code_idx++] |= IGEN_GENERIC_REG(generic_inst, trg_reg)); } break; # ifdef DEBUG case CGP_ASSEMBLY: offset = 0; switch (opr->oprclass) { case TRIP_REF: assert(0 == ct->destination.oprclass); assert(val_output); switch (ct->opcode) { case OC_LIT: assert(MLIT_REF == ct->operand[0].oprclass); offset = literal_offset(ct->operand[0].oprval.mlit->rt_addr); memcpy(obpt, &vdat_def[0], VDAT_DEF_SIZE); obpt += VDAT_DEF_SIZE; memcpy(obpt, &vdat_gtmliteral[0], VDAT_GTMLITERAL_SIZE); obpt += VDAT_GTMLITERAL_SIZE; *obpt++ = '+'; *obpt++ = '0'; *obpt++ = 'x'; obpt += i2hex_nofill(offset, (uchar_ptr_t)obpt, 8); if (run_time) reg = GTM_REG_PV; else reg = GTM_REG_LITERAL_BASE; X86_64_ONLY(force_32 = TRUE); emit_base_offset(reg, offset); X86_64_ONLY(force_32 = FALSE); NON_RISC_ONLY(IGEN_LOAD_ADDR_REG(trg_reg)) RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_ADDR_REG(trg_reg)); inst_emitted = TRUE; break; case OC_CDLIT: if (val_output) { memcpy(obpt, &vdat_gr[0], VDAT_GR_SIZE); obpt += VDAT_GR_SIZE; } memcpy(obpt, ct->operand[0].oprval.cdlt->addr, ct->operand[0].oprval.cdlt->len); obpt += ct->operand[0].oprval.cdlt->len; emit_base_offset(GTM_REG_PV, find_linkage(ct->operand[0].oprval.cdlt)); if (GENERIC_OPCODE_LDA == generic_inst) { RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_LINKAGE(trg_reg)); NON_RISC_ONLY(IGEN_LOAD_LINKAGE(trg_reg)); inst_emitted = TRUE; } else { RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_LINKAGE(GTM_REG_CODEGEN_TEMP)); NON_RISC_ONLY(IGEN_LOAD_LINKAGE(GTM_REG_CODEGEN_TEMP)); emit_base_offset(GTM_REG_CODEGEN_TEMP, 0); } break; case OC_CDIDX: assert(GENERIC_OPCODE_LOAD == generic_inst); /* Fetch linkage table offset for symbol and convert to index */ immediate = find_linkage(ct->operand[0].oprval.cdidx) / SIZEOF(lnk_tabent); memcpy(obpt, &vdat_immed[0], VDAT_IMMED_SIZE); obpt += VDAT_IMMED_SIZE; obpt = i2asc((uchar_ptr_t)obpt, immediate); EMIT_TRIP_ILIT_GEN; inst_emitted = TRUE; break; case OC_ILIT: assert(GENERIC_OPCODE_LOAD == generic_inst); immediate = ct->operand[0].oprval.ilit; memcpy(obpt, &vdat_immed[0], VDAT_IMMED_SIZE); obpt += VDAT_IMMED_SIZE; obpt = i2asc((uchar_ptr_t)obpt, immediate); EMIT_TRIP_ILIT_GEN; inst_emitted = TRUE; break; case OC_TRIPSIZE: immediate = ct->operand[0].oprval.tsize->size; assert(0 < immediate); assert(MAX_BRANCH_CODEGEN_SIZE > immediate); EMIT_TRIP_ILIT_GEN; inst_emitted = TRUE; break; default: assertpro(FALSE && ct->opcode); break; } break; case TINT_REF: case TVAL_REF: assert(val_output); offset = sa_temps_offset[opr->oprclass]; offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; NON_GTM64_ONLY(assertpro((0 <= offset) && (MAX_OFFSET >= offset))); if (127 > offset) { memcpy(obpt, &vdat_bdisp[0], VDAT_BDISP_SIZE); obpt += VDAT_BDISP_SIZE; } else { memcpy(obpt, &vdat_wdisp[0], VDAT_WDISP_SIZE); obpt += VDAT_WDISP_SIZE; } obpt = i2asc((uchar_ptr_t)obpt, offset); memcpy(obpt, &vdat_r9[0], VDAT_R9_SIZE); obpt += VDAT_R9_SIZE; /* * for 64 bit platforms, By default the loads/stores * are of 8 bytes, but if the value being dealt with * is a word, then the opcode in generic_inst is * changed to ldw/stw(4 byte load/stores) */ GTM64_ONLY( if (4 == sa_class_sizes[opr->oprclass]) { next_ptr_offset = 4; REVERT_GENERICINST_TO_WORD(generic_inst); } ) emit_base_offset(GTM_REG_FRAME_TMP_PTR, offset); break; case TCAD_REF: case TVAD_REF: case TVAR_REF: offset = sa_temps_offset[opr->oprclass]; offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; if (val_output) { memcpy(obpt, &vdat_def[0], VDAT_DEF_SIZE); obpt += VDAT_DEF_SIZE; } if (127 > offset) { memcpy(obpt, &vdat_bdisp[0], VDAT_BDISP_SIZE); obpt += VDAT_BDISP_SIZE; } else { memcpy(obpt, &vdat_wdisp[0], VDAT_WDISP_SIZE); obpt += VDAT_WDISP_SIZE; } obpt = i2asc((uchar_ptr_t)obpt, offset); if (TVAR_REF == opr->oprclass) { memcpy(obpt, &vdat_r8[0], VDAT_R8_SIZE); obpt += VDAT_R8_SIZE; } else { memcpy(obpt, &vdat_r9[0], VDAT_R9_SIZE); obpt += VDAT_R9_SIZE; } NON_GTM64_ONLY(assertpro((0 <= offset) && (MAX_OFFSET >= offset))); if (TVAR_REF == opr->oprclass) reg = GTM_REG_FRAME_VAR_PTR; else reg = GTM_REG_FRAME_TMP_PTR; GTM64_ONLY( if (4 == sa_class_sizes[opr->oprclass]) { next_ptr_offset = 4; REVERT_GENERICINST_TO_WORD(generic_inst); } ) emit_base_offset(reg, offset); if (val_output) /* indirection */ { if (GENERIC_OPCODE_LDA == generic_inst) { RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_NATIVE_REG(trg_reg)); NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(trg_reg)); if (TVAR_REF == opr->oprclass) { emit_base_offset(trg_reg, offsetof(ht_ent_mname, value)); NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(trg_reg)); RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_NATIVE_REG(trg_reg)); } inst_emitted = TRUE; } else { RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_NATIVE_REG(GTM_REG_CODEGEN_TEMP)); NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(GTM_REG_CODEGEN_TEMP)); emit_base_offset(GTM_REG_CODEGEN_TEMP, 0); } } break; case OCNT_REF: immediate = opr->oprval.offset; assert(0 < immediate); assert(MAX_BRANCH_CODEGEN_SIZE > immediate); EMIT_TRIP_ILIT_GEN; inst_emitted = TRUE; ocnt_ref_seen = TRUE; ocnt_ref_opr = opr; break; default: assertpro(FALSE && opr->oprclass); break; } if (!inst_emitted) { RISC_ONLY(code_buf[code_idx++] |= IGEN_GENERIC_REG(generic_inst, trg_reg)); NON_RISC_ONLY(IGEN_GENERIC_REG(generic_inst, trg_reg)); } *obpt++ = ','; *obpt++ = ' '; break; # endif case CGP_MACHINE: switch (opr->oprclass) { case TRIP_REF: assert(0 == ct->destination.oprclass); assert(val_output); switch (ct->opcode) { case OC_LIT: assert(MLIT_REF == ct->operand[0].oprclass); offset = literal_offset(ct->operand[0].oprval.mlit->rt_addr); if (run_time) reg = GTM_REG_PV; else reg = GTM_REG_LITERAL_BASE; X86_64_ONLY(force_32 = TRUE); emit_base_offset(reg, offset); X86_64_ONLY(force_32 = FALSE); RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_ADDR_REG(trg_reg)); NON_RISC_ONLY(IGEN_LOAD_ADDR_REG(trg_reg)); inst_emitted = TRUE; break; case OC_CDLIT: emit_base_offset(GTM_REG_PV, find_linkage(ct->operand[0].oprval.cdlt)); if (GENERIC_OPCODE_LDA == generic_inst) { RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_LINKAGE(trg_reg)); NON_RISC_ONLY(IGEN_LOAD_LINKAGE(trg_reg)); inst_emitted = TRUE; } else { RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_LINKAGE(GTM_REG_CODEGEN_TEMP)); NON_RISC_ONLY(IGEN_LOAD_LINKAGE(GTM_REG_CODEGEN_TEMP)); emit_base_offset(GTM_REG_CODEGEN_TEMP, 0); } break; case OC_CDIDX: assert(GENERIC_OPCODE_LOAD == generic_inst); /* Fetch linkage table offset for symbol and convert to index */ immediate = find_linkage(ct->operand[0].oprval.cdidx) / SIZEOF(lnk_tabent); EMIT_TRIP_ILIT_GEN; inst_emitted = TRUE; break; case OC_ILIT: assert(GENERIC_OPCODE_LOAD == generic_inst); immediate = ct->operand[0].oprval.ilit; EMIT_TRIP_ILIT_GEN; inst_emitted = TRUE; break; case OC_TRIPSIZE: immediate = ct->operand[0].oprval.tsize->size; # if !defined(__osf__) && !defined(__hppa) /* Legitimate but odd call to next M line on Tru64 and HPUX-HPPA gives an * immediate value of zero because of how offsets are calculated on * these platforms so bypass the assert for them. */ assert(0 < immediate); # endif assert(MAX_BRANCH_CODEGEN_SIZE > immediate); EMIT_TRIP_ILIT_GEN; inst_emitted = TRUE; break; default: assertpro(FALSE && ct->opcode); break; } break; case TINT_REF: case TVAL_REF: assert(val_output); offset = sa_temps_offset[opr->oprclass]; offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; GTM64_ONLY( if (4 == sa_class_sizes[opr->oprclass]) { next_ptr_offset = 4; REVERT_GENERICINST_TO_WORD(generic_inst); } ) NON_GTM64_ONLY(assertpro((0 <= offset) && (MAX_OFFSET >= offset))); emit_base_offset(GTM_REG_FRAME_TMP_PTR, offset); break; case TCAD_REF: case TVAD_REF: case TVAR_REF: offset = sa_temps_offset[opr->oprclass]; offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; GTM64_ONLY( if (4 == sa_class_sizes[opr->oprclass]) { next_ptr_offset = 4; REVERT_GENERICINST_TO_WORD(generic_inst); } ) NON_GTM64_ONLY(assertpro((0 <= offset) && (MAX_OFFSET >= offset))); if (TVAR_REF == opr->oprclass) reg = GTM_REG_FRAME_VAR_PTR; else reg = GTM_REG_FRAME_TMP_PTR; emit_base_offset(reg, offset); if (val_output) /* indirection */ { if (GENERIC_OPCODE_LDA == generic_inst) { RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_NATIVE_REG(trg_reg)); NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(trg_reg)); if (TVAR_REF == opr->oprclass) { emit_base_offset(trg_reg, offsetof(ht_ent_mname, value)); NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(trg_reg)); RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_NATIVE_REG(trg_reg)); } inst_emitted = TRUE; } else { RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_NATIVE_REG(GTM_REG_CODEGEN_TEMP)); NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(GTM_REG_CODEGEN_TEMP)); emit_base_offset(GTM_REG_CODEGEN_TEMP, 0); } } break; case OCNT_REF: immediate = opr->oprval.offset; assert(0 <= immediate); assert(MAX_BRANCH_CODEGEN_SIZE > immediate); EMIT_TRIP_ILIT_GEN; inst_emitted = TRUE; ocnt_ref_seen = TRUE; ocnt_ref_opr = opr; break; default: assertpro(FALSE && opr->oprclass); break; } /* If we haven't emitted a finished instruction already, finish it now */ if (!inst_emitted) { RISC_ONLY(code_buf[code_idx++] |= IGEN_GENERIC_REG(generic_inst, trg_reg)); NON_RISC_ONLY(IGEN_GENERIC_REG(generic_inst, trg_reg)); } break; default: assertpro(FALSE && cg_phase); break; } } /* get_arg_reg * * Determines the argument position of the current argument and returns the number of the register to use for the * value of the argument. If it's not one of the arguments passed in machine registers, get_arg_reg defaults to the * accumulator emulator register. * * NOTE: because shrink_jmps does not always process emulated VAX instructions that generate arguments, it is crucial * that get_arg_reg() and emit_push() predict the same number of instructions during the CGP_APPROX_ADDR phase as are * actually generated during subsequent phases. In order to ensure this, they emulate instruction generation backwards * during the CGP_APPROX_ADDR and CGP_ADDR_OPT phases relative to the other phases. For example: * * CGP_APPROX_ADDR and CGP_ADDR_OPT phases: * arg1 <- first argument, . . ., argN <- N th argument * if more than N, series of: * LOAD GTM_REG_ACCUM, next argument * STORE GTM_REG_ACCUM, STACK_WORD_SIZE*(i-N)(sp) * * other phases: * if more than N, series of: * LOAD GTM_REG_ACCUM, next argument * STORE GTM_REG_ACCUM, STACK_WORD_SIZE*(i-N)(sp) * argN <- N th argument, . . ., arg1 <- first argument * where STACK_WORD_SIZE is 8(Alpha) or 4(other platforms). * * While this technique correctly predicts the number of arguments, it does not guarantee to start any of the * individual argument instruction sequences, except the first, during the CGP_APPROX_ADDR phase at the same * code_reference address as it will for subsequent phases. This is because, although it predicts (or should) * the same number of instructions during the CGP_APPROX_ADDR phase for an overall sequence of argument pushes, * it does not do so in the same order as subsequent phases. Because we do not use PC-relative addressing for * data on this platform, this difference should be benign (the subsequent xfer table call should be synchronized * with respect to code_reference address across all phases). */ int get_arg_reg(void) { int arg_reg_i = GTM_REG_ACCUM; /* Default never used due to the assert in the switch below */ switch (cg_phase) { case CGP_APPROX_ADDR: case CGP_ADDR_OPT: if (MACHINE_REG_ARGS > vax_pushes_seen) arg_reg_i = GET_ARG_REG(vax_pushes_seen); else arg_reg_i = GTM_REG_ACCUM; break; case CGP_ASSEMBLY: case CGP_MACHINE: if (0 == vax_pushes_seen) /* first push of a series */ vax_number_of_arguments = next_vax_push_list(); if (MACHINE_REG_ARGS >= vax_number_of_arguments) arg_reg_i = GET_ARG_REG(vax_number_of_arguments - 1); else arg_reg_i = GTM_REG_ACCUM; break; default: assertpro(FALSE && cg_phase); break; } return arg_reg_i; } /* VAX reg to local machine reg */ int gtm_reg(int vax_reg) { int reg; switch (vax_reg & 0x0f) /* mask out VAX register mode field */ { case 0: reg = GTM_REG_R0; break; case 1: reg = GTM_REG_R1; break; case 8: reg = GTM_REG_FRAME_VAR_PTR; break; case 9: reg = GTM_REG_FRAME_TMP_PTR; break; # ifdef TRUTH_IN_REG case 10: /* The value of $TEST is maintained in r10 for the VAX GT.M * implementation. On platforms with an insufficient number of * non-volatile (saved) registers, the value of $TEST is maintained * only in memory; when sufficient registers are available, though, * we keep $TEST in one of them. */ reg = GTM_REG_DOLLAR_TRUTH; break; # endif case 11: reg = GTM_REG_XFER_TABLE; break; case 12: reg = GTM_REG_FRAME_POINTER; break; /* VMS ap */ default: assertpro(FALSE && (0x0f & vax_reg)); break; } return reg; } void emit_push(int reg) { int arg_reg_i; int stack_offset; switch (cg_phase) { case CGP_APPROX_ADDR: case CGP_ADDR_OPT: if (MACHINE_REG_ARGS <= vax_pushes_seen) { RISC_ONLY(code_idx++); /* for STORE instruction */ NON_RISC_ONLY( assert(GTM_REG_ACCUM == reg); stack_offset = STACK_ARG_OFFSET((vax_number_of_arguments - MACHINE_REG_ARGS - 1)); GEN_STORE_ARG(reg, stack_offset); /* Store arg on stack */ ) } break; case CGP_ASSEMBLY: case CGP_MACHINE: if (MACHINE_REG_ARGS >= vax_number_of_arguments) assert(reg == GET_ARG_REG(vax_number_of_arguments - 1)); else { assert(GTM_REG_ACCUM == reg); stack_offset = STACK_ARG_OFFSET((vax_number_of_arguments - MACHINE_REG_ARGS - 1)); GEN_STORE_ARG(reg, stack_offset); /* Store arg on stack */ } break; default: assertpro(FALSE && cg_phase); break; } if ((CGP_MACHINE == cg_phase) || (CGP_ASSEMBLY == cg_phase)) { vax_number_of_arguments--; /* actually, it's the number of arguments remaining */ assert(0 <= vax_number_of_arguments); } vax_pushes_seen++; stack_depth++; return; } void emit_pop(int count) { int stack_adjust; assert(stack_depth >= count); stack_depth -= count; /* It's possible we lost count after a jsb (see VXI_JSB). */ if (0 > stack_depth) stack_depth = 0; return; } void add_to_vax_push_list(int pushes_seen) { /* Make sure there's enough room */ if (MAX_ARGS < pushes_seen) /* user-visible max args is MAX_ARGS - 3 */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_MAXARGCNT, 1, MAX_ARGS - 3); push_list_index++; if (PUSH_LIST_SIZE <= push_list_index) { push_list_index = 0; if (0 == current_push_list_ptr->next) { current_push_list_ptr->next = (struct push_list *)malloc(SIZEOF(*current_push_list_ptr)); current_push_list_ptr->next->next = 0; } current_push_list_ptr = current_push_list_ptr->next; } current_push_list_ptr->value[push_list_index] = pushes_seen; } int next_vax_push_list(void) { push_list_index++; if (PUSH_LIST_SIZE <= push_list_index) { push_list_index=0; assertpro(current_push_list_ptr->next); current_push_list_ptr = current_push_list_ptr->next; } return (current_push_list_ptr->value[push_list_index]); } void push_list_init(void) { push_list_index = -1; if (0 == push_list_start_ptr) { push_list_start_ptr = (struct push_list *)malloc(SIZEOF(*current_push_list_ptr)); push_list_start_ptr->next = 0; } current_push_list_ptr = push_list_start_ptr; } void reset_push_list_ptr(void) { push_list_index = -1; current_push_list_ptr = push_list_start_ptr; } /* Generate code for a transfer table (xfer_table) call into the (shared) runtime library */ void emit_call_xfer(int xfer) { int offset; unsigned char *c; # ifdef DEBUG if (CGP_ASSEMBLY == cg_phase) { memcpy(obpt, &vdat_def[0], VDAT_DEF_SIZE); obpt += VDAT_DEF_SIZE; if (127 > xfer) { memcpy(obpt, &vdat_bdisp[0], VDAT_BDISP_SIZE); obpt += VDAT_BDISP_SIZE; } else { memcpy(obpt, &vdat_wdisp[0], VDAT_WDISP_SIZE); obpt += VDAT_WDISP_SIZE; } offset = (int)(xfer / SIZEOF(char *)); for (c = (unsigned char *)xfer_name[offset]; *c ; ) *obpt++ = *c++; memcpy(obpt, &vdat_r11[0], VDAT_R11_SIZE); obpt += VDAT_R11_SIZE; *obpt++ = ','; *obpt++ = ' '; } # endif assert(0 == (xfer & 0x3)); offset = (int)(xfer / SIZEOF(char *)); # ifdef __x86_64__ /* Set RAX to 0 for variable argument function calls. This is part of the ABI. * The RAX represents the # of floating of values being passed */ if (GTM_C_VAR_ARGS_RTN == xfer_table_desc[offset]) { GEN_LOAD_IMMED(I386_REG_RAX, 0); } # endif /* __x86_64__ */ # ifdef __ia64 if (GTM_ASM_RTN == xfer_table_desc[offset]) { GEN_XFER_TBL_CALL_FAKE(xfer); } else { GEN_XFER_TBL_CALL_DIRECT(xfer); } # else GEN_XFER_TBL_CALL(xfer); # endif /* __ia64 */ /* In the normal case we will return */ if (!ocnt_ref_seen) return; /* fast test for return .. we hope */ /* If ocnt_ref_seen is set, then we need to compute the value to be used by a recent * OCNT_REF parameter. This parameter is (currently as of 6/2003) used by op_call, op_callsp, * op_forlcldo, and their mprof counterparts and is the number of bytes those entry points * should add to the return address that they will store as the return point in the new stack * frame that they create. This parameter is basically the size of the generated code for the * jump that follows the call to the above routines that is generates by the associated * triples OC_CALL, OC_CALLSP, and OC_FORLCLDO respectively. Since this jump can be variable in * size and the only other way for these routines to know what form the jump takes is to parse * the instructions at run time, this routine in the compiler will calculate that information and * allow it to be passed in as a parameter. The OCNT_REF handler in emit_trip() has set the * ocnt_ref_seen flag to bring us here. We now calculate the current PC address and subtract it * from the PC address of the next triple. */ assert(OC_CALL == current_triple->opcode || OC_CALLSP == current_triple->opcode || OC_FORLCLDO == current_triple->opcode); offset = current_triple->exorder.fl->rtaddr - (code_reference + (code_idx * INST_SIZE)); /* If in assembly or machine (final) phases, make sure have reasonable offset. The offset may be * negative in the early phases so don't check during them. For other phases, put a govenor on * the values so we don't affect the codegen sizes which can mess up shrink_trips. During the * triple shrink phase, the triple distances can vary widely and cause the codegen to change * sizes. Note this still allows an assert fail for 0 if a negative number was being produced. */ if ((CGP_MACHINE == cg_phase) || (CGP_ASSEMBLY == cg_phase)) assert(0 <= offset && MAX_BRANCH_CODEGEN_SIZE > offset); else offset = MAX(0, MIN(128, offset)); ocnt_ref_opr->oprval.offset = offset; ocnt_ref_seen = FALSE; } fis-gtm-V7.0-005/sr_port/ecode_set.c0000755000032200000250000000571714342376331016156 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "error.h" #include "error_trap.h" #include "merrors_ansi.h" /* ECODE_MAX_LEN is the maximum length of the string representation of "errnum"'s ECODE. * This is arrived at as follows : ",M,Z,". Each number can be at most MAX_NUM_SIZE. * The three ","s and the letters "M" and "Z" add up to 5 characters. * In addition we give a buffer for overflow in the production version (just in case). */ #define BUFFER_FOR_OVERFLOW 15 /* give some buffer in case overflow happens in PRO */ #define ECODE_MAX_LEN ((2 * MAX_DIGITS_IN_INT) + STR_LIT_LEN(",M,Z,")) #define ECODE_MAX_LEN_WITH_BUFFER ((ECODE_MAX_LEN) + (BUFFER_FOR_OVERFLOW)) error_def(ERR_SETECODE); void ecode_set(int errnum) { mval tmpmval; const err_ctl *ectl; mstr ecode_mstr; char ecode_buff[ECODE_MAX_LEN_WITH_BUFFER]; char *ecode_ptr; int ansi_error; int severity; /* If this routine was called with error code SETECODE, * an end-user just put a correct value into $ECODE, * and it shouldn't be replaced by this routine. */ if (ERR_SETECODE == errnum) return; /* When the value of $ECODE is non-empty, error trapping is invoked. When the severity level does not warrant * error trapping, no value should be copied into $ECODE. Note: the message is verified it IS a GTM message before * checking the severity code so system error numbers aren't misinterpreted. */ severity = errnum & SEV_MSK; if ((NULL != err_check(errnum)) && ((INFO == severity) || (SUCCESS == severity))) return; /* Get ECODE string from error-number. If the error has an ANSI standard code, return ,Mnnn, (nnn is ANSI code). * Always return ,Zxxx, (xxx is GT.M code). Note that the value of $ECODE must start and end with a comma */ ecode_ptr = &ecode_buff[0]; *ecode_ptr++ = ','; if (ectl = err_check(errnum)) { ansi_error = (((errnum & 0x0FFFFFFF) & FACMASK(ectl->facnum)) && (MSGMASK(errnum, ectl->facnum) <= ectl->msg_cnt)) ? error_ansi[MSGMASK(errnum, ectl->facnum) - 1] : 0; if (0 < ansi_error) { *ecode_ptr++ = 'M'; ecode_ptr = (char *)i2asc((unsigned char *)ecode_ptr, ansi_error); *ecode_ptr++ = ','; } } *ecode_ptr++ = 'Z'; ecode_ptr = (char *)i2asc((unsigned char *)ecode_ptr, errnum); *ecode_ptr++ = ','; ecode_mstr.addr = &ecode_buff[0]; ecode_mstr.len = INTCAST(ecode_ptr - ecode_mstr.addr); assert(ecode_mstr.len <= ECODE_MAX_LEN); assertpro(ECODE_MAX_LEN_WITH_BUFFER >= ecode_mstr.len); ecode_add(&ecode_mstr); } fis-gtm-V7.0-005/sr_port/eintr_wrappers.h0000755000032200000250000002572714342376331017300 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* Define macros to do system calls and restart as appropriate * * FCNTL, FCNTL3 Loop until fcntl call succeeds or fails with other than EINTR. * TCFLUSH Loop until tcflush call succeeds or fails with other than EINTR. * Tcsetattr Loop until tcsetattr call succeeds or fails with other than EINTR. */ #ifndef EINTR_WRP_Included #define EINTR_WRP_Included #include #include #include "have_crit.h" #include "gt_timer.h" #include "gtm_stdio.h" #if defined(DEBUG) #include "io.h" #include "wcs_sleep.h" #include "deferred_signal_handler.h" #include "wbox_test_init.h" #endif #define ACCEPT_SOCKET(SOCKET, ADDR, LEN, RC) \ { \ do \ { \ RC = ACCEPT(SOCKET, ADDR, LEN); \ } while (-1 == RC && EINTR == errno); \ } #define CHG_OWNER(PATH, OWNER, GRP, RC) \ { \ do \ { \ RC = CHOWN(PATH, OWNER, GRP); \ } while (-1 == RC && EINTR == errno); \ } #define CLOSE(FD, RC) \ { \ intrpt_state_t prev_intrpt_state; \ \ do \ { \ DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); \ RC = close(FD); \ ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); \ } while (-1 == RC && EINTR == errno); \ } #define CLOSEDIR(DIR, RC) \ { \ do \ { \ RC = closedir(DIR); \ } while (-1 == RC && EINTR == errno); \ } #define CONNECT_SOCKET(SOCKET, ADDR, LEN, RC) \ RC = gtm_connect(SOCKET, ADDR, LEN) #define CREATE_FILE(PATHNAME, MODE, RC) \ { \ do \ { \ RC = CREAT(PATHNAME, MODE); \ } while (-1 == RC && EINTR == errno); \ } #define DOREAD_A_NOINT(FD, BUF, SIZE, RC) \ { \ do \ { \ RC = DOREAD_A(FD, BUF, SIZE); \ } while (-1 == RC && EINTR == errno); \ } #define DUP2(FDESC1, FDESC2, RC) \ { \ do \ { \ RC = dup2(FDESC1, FDESC2); \ } while (-1 == RC && EINTR == errno); \ } #define FCLOSE(STREAM, RC) \ { \ intrpt_state_t prev_intrpt_state; \ \ do \ { \ DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); \ RC = fclose(STREAM); \ ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); \ } while (-1 == RC && EINTR == errno); \ } #define FCNTL2(FDESC, ACTION, RC) \ { \ do \ { \ RC = fcntl(FDESC, ACTION); \ } while (-1 == RC && EINTR == errno); \ } #define FCNTL3(FDESC, ACTION, ARG, RC) \ { \ do \ { \ RC = fcntl(FDESC, ACTION, ARG); \ } while (-1 == RC && EINTR == errno); \ } #define FGETS_FILE(BUF, LEN, FP, RC) \ { \ do \ { \ FGETS(BUF, LEN, FP, RC); \ } while (NULL == RC && !feof(FP) && ferror(FP) && EINTR == errno); \ } #define FSTAT_FILE(FDESC, INFO, RC) \ { \ intrpt_state_t prev_intrpt_state; \ \ do \ { \ DEFER_INTERRUPTS(INTRPT_IN_FSTAT, prev_intrpt_state); \ RC = fstat(FDESC, INFO); \ ENABLE_INTERRUPTS(INTRPT_IN_FSTAT, prev_intrpt_state); \ } while (-1 == RC && EINTR == errno); \ } #define FSTATVFS_FILE(FDESC, FSINFO, RC) \ { \ do \ { \ FSTATVFS(FDESC, FSINFO, RC); \ } while (-1 == RC && EINTR == errno); \ } #define FTRUNCATE(FDESC, LENGTH, RC) \ { \ do \ { \ RC = ftruncate(FDESC, LENGTH); \ } while (-1 == RC && EINTR == errno); \ } /* GTM_FREAD is an EINTR-safe versions of "fread". Retries on EINTR. Returns number of elements read in NREAD. * If NREAD < NELEMS, if error then copies errno into RC, if eof then sets RC to 0. Note: RC is not initialized otherwise. * Macro is named GTM_FREAD instead of FREAD because AIX defines a macro by the same name in fcntl.h. */ #define GTM_FREAD(BUFF, ELEMSIZE, NELEMS, FP, NREAD, RC) \ MBSTART { \ size_t elems_to_read, elems_read; \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_EINTR_WRAPPERS, prev_intrpt_state); \ elems_to_read = NELEMS; \ for (;;) \ { \ elems_read = fread(BUFF, ELEMSIZE, elems_to_read, FP); \ assert(elems_read <= elems_to_read); \ elems_to_read -= elems_read; \ if (0 == elems_to_read) \ break; \ RC = feof(FP); \ if (RC) \ { /* Reached EOF. No error. */ \ RC = 0; \ break; \ } \ RC = ferror(FP); \ assert(RC); \ clearerr(FP); /* reset error set by the "fread" */ \ /* In case of EINTR, retry "fread" */ \ if (EINTR != errno) \ break; \ } \ NREAD = NELEMS - elems_to_read; \ ENABLE_INTERRUPTS(INTRPT_IN_EINTR_WRAPPERS, prev_intrpt_state); \ } MBEND #define GTM_FSYNC(FD, RC) \ { \ do \ { \ RC = fsync(FD); \ } while (-1 == RC && EINTR == errno); \ } /* GTM_FWRITE is an EINTR-safe versions of "fwrite". Retries on EINTR. Returns number of elements written in NWRITTEN. * If NWRITTEN < NELEMS, copies errno into RC. Note: RC is not initialized otherwise. * Macro is named GTM_FWRITE instead of FWRITE because AIX defines a macro by the same name in fcntl.h. */ #define GTM_FWRITE(BUFF, ELEMSIZE, NELEMS, FP, NWRITTEN, RC) \ RC = gtm_fwrite(BUFF, ELEMSIZE, NELEMS, FP, &(NWRITTEN)); static inline size_t gtm_fwrite(void *buff, size_t elemsize, size_t nelems, FILE *fp, size_t *nwritten) { size_t elems_to_write, elems_written, rc = 0; intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_EINTR_WRAPPERS, prev_intrpt_state); elems_to_write = nelems; for ( ; (0 != elems_to_write) && (0 != elemsize) ; ) { elems_written = fwrite(buff, elemsize, elems_to_write, fp); assert(elems_written <= elems_to_write); elems_to_write -= elems_written; if (0 == elems_to_write) break; assert(!feof(fp)); rc = ferror(fp); assert(rc); clearerr(fp); /* reset error set by the "fwrite" */ /* In case of EINTR, retry "fwrite" */ if (EINTR != errno) break; } *nwritten = nelems - elems_to_write; ENABLE_INTERRUPTS(INTRPT_IN_EINTR_WRAPPERS, prev_intrpt_state); return rc; } #define LSTAT_FILE(PATH, INFO, RC) \ { \ do \ { \ RC = LSTAT(PATH, INFO); \ } while ((uint4)-1 == RC && EINTR == errno); \ } #define MSGSND(MSGID, MSGP, MSGSZ, FLG, RC) \ { \ do \ { \ RC = msgsnd(MSGID, MSGP, MSGSZ, FLG); \ } while (-1 == RC && EINTR == errno); \ } #define OPENAT(PATH, FLAGS, MODE, RC) \ { \ do \ { \ RC = openat(PATH, FLAGS, MODE); \ } while ((-1 == RC) && (EINTR == errno)); \ } #define OPEN_PIPE(FDESC, RC) \ { \ do \ { \ RC = pipe(FDESC); \ } while (-1 == RC && EINTR == errno); \ } /* posix_fallocate returns zero on success, or an error * number on failure. Note that errno is not set. */ #define POSIX_FALLOCATE(FD, BUF, SIZE, RC) \ { \ do \ { \ RC = posix_fallocate(FD, BUF, SIZE); \ } while (EINTR == RC); \ } #define READ_FILE(FD, BUF, SIZE, RC) \ { \ do \ { \ RC = read(FD, BUF, SIZE); \ } while (-1 == RC && EINTR == errno); \ } #define RECV(SOCKET, BUF, LEN, FLAGS, RC) \ { \ do \ { \ RC = (int)recv(SOCKET, BUF, (int)(LEN), FLAGS); \ } while (-1 == RC && EINTR == errno); \ } #define RECVFROM_SOCK(SOCKET, BUF, LEN, FLAGS, \ ADDR, ADDR_LEN, RC) \ { \ do \ { \ RC = RECVFROM(SOCKET, BUF, LEN, \ FLAGS, ADDR, ADDR_LEN); \ } while (-1 == RC && EINTR == errno); \ } #define SELECT(FDS, INLIST, OUTLIST, XLIST, TIMEOUT, RC) \ { \ struct timeval eintr_select_timeval; \ do \ { \ eintr_select_timeval = *(TIMEOUT); \ RC = select(FDS, INLIST, OUTLIST, \ XLIST, &eintr_select_timeval); \ } while (-1 == RC && EINTR == errno); \ } #define SEND(SOCKET, BUF, LEN, FLAGS, RC) \ { \ do \ { \ RC = send(SOCKET, BUF, LEN, FLAGS); \ } while (-1 == RC && EINTR == errno); \ } #define SENDTO_SOCK(SOCKET, BUF, LEN, FLAGS, \ ADDR, ADDR_LEN, RC) \ { \ do \ { \ RC = SENDTO(SOCKET, BUF, LEN, FLAGS, \ ADDR, ADDR_LEN); \ } while (-1 == RC && EINTR == errno); \ } #define STAT_FILE(PATH, INFO, RC) \ { \ do \ { \ RC = Stat(PATH, INFO); \ } while ((uint4)-1 == RC && EINTR == errno); \ } #if defined(DEBUG) #define SYSCONF(PARM, RC) \ { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_SYSCONF, prev_intrpt_state); \ if (gtm_white_box_test_case_enabled \ && (WBTEST_SYSCONF_WRAPPER == gtm_white_box_test_case_number)) \ { \ DBGFPF((stderr, "will sleep indefinitely now\n")); \ while (TRUE) \ LONG_SLEEP(60); \ } \ RC = sysconf(PARM); \ ENABLE_INTERRUPTS(INTRPT_IN_SYSCONF, prev_intrpt_state); \ } #else #define SYSCONF(PARM, RC) \ { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_SYSCONF, prev_intrpt_state); \ RC = sysconf(PARM); \ ENABLE_INTERRUPTS(INTRPT_IN_SYSCONF, prev_intrpt_state); \ } #endif #define TCFLUSH(FDESC, REQUEST, RC) \ { \ do \ { \ RC = tcflush(FDESC, REQUEST); \ } while (-1 == RC && EINTR == errno); \ } #define Tcsetattr(FDESC, WHEN, TERMPTR, RC, ERRNO) \ { \ GBLREF sigset_t block_ttinout; \ sigset_t oldset; \ int rc; \ SIGPROCMASK(SIG_BLOCK, &block_ttinout, &oldset, rc); \ do \ { \ RC = tcsetattr(FDESC, WHEN, TERMPTR); \ } while (-1 == RC && EINTR == errno); \ ERRNO = errno; \ SIGPROCMASK(SIG_SETMASK, &oldset, NULL, rc); \ } #define TRUNCATE_FILE(PATH, LENGTH, RC) \ { \ do \ { \ RC = TRUNCATE(PATH, LENGTH); \ } while (-1 == RC && EINTR == errno); \ } #define WAIT(STATUS, RC) \ { \ do \ { \ RC = wait(STATUS); \ } while (-1 == RC && EINTR == errno); \ } #define WAITPID(PID, STATUS, OPTS, RC) \ { \ /* Ensure that the incoming PID is non-zero. We currently don't know of any places where we want to invoke \ * waitpid with child PID being 0 as that would block us till any of the child spawned by this parent process \ * changes its state unless invoked with WNOHANG bit set. Make sure not waiting on current pid \ */ \ assert(0 != PID); \ assert(getpid() != PID); \ do \ { \ RC = waitpid(PID, STATUS, OPTS); \ } while (-1 == RC && EINTR == errno); \ } #endif fis-gtm-V7.0-005/sr_port/emit_code.h0000644000032200000250000000362514342376333016154 0ustar librarygtc/**************************************************************** * * * 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. * * * ****************************************************************/ #ifndef EMIT_CODE_INCLUDED #define EMIT_CODE_INCLUDED #include #ifdef DEBUG void emit_asmlist(triple *ct); void emit_eoi(void); #endif void trip_gen(triple *ct); short *emit_vax_inst(short *inst, oprtype **fst_opr, oprtype **lst_opr); void emit_jmp(uint4 branchop, short **instp, int reg); void emit_pcrel(void); void emit_trip(oprtype *opr, boolean_t val_output, uint4 alpha_inst, int ra_reg); void emit_push(int reg); void emit_pop(int count); void add_to_vax_push_list(int pushes_seen); int next_vax_push_list(void); void push_list_init(void); void reset_push_list_ptr(void); void emit_call_xfer(int xfer); int get_arg_reg(void); int gtm_reg(int vax_reg); #ifdef __x86_64__ # define NUM_BUFFERRED_INSTRUCTIONS 100 # define CODE_TYPE char #elif defined(__ia64) # define CODE_TYPE ia64_bundle # define NUM_BUFFERRED_INSTRUCTIONS 25 #elif defined(__MVS__) || defined(Linux390) # define CODE_TYPE uint2 # define NUM_BUFFERRED_INSTRUCTIONS 100 #else # define CODE_TYPE uint4 # define NUM_BUFFERRED_INSTRUCTIONS 25 #endif #define ASM_OUT_BUFF 256 #define PUSH_LIST_SIZE 500 #if defined(__vms) || defined(_AIX) || defined(__sparc) || defined(__hpux) || (defined(__linux__) && defined(__ia64)) \ || defined(__MVS__) # define TRUTH_IN_REG #elif defined(__osf__) || (defined(__linux__) && defined(__x86_64__)) || defined(Linux390) \ || (defined(__CYGWIN__) && defined(__x86_64__)) # undef TRUTH_IN_REG #else # error UNSUPPORTED PLATFORM #endif #endif fis-gtm-V7.0-005/sr_port/entryref.c0000644000032200000250000003140014342376333016045 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" #include "mlabel2xtern.h" #include "mrout2xtern.h" #include "gtmimagename.h" #include #include "stack_frame.h" STATICFNDCL oprtype insert_extref(mident *rtnname); STATICFNDCL triple *insert_extref_fast(opctype op1, opctype op2, mident *rtnname, mident *labname); #define CONVERT_MUTIL_NAME_PERCENT_TO_UNDERSCORE(RTNNAME) \ { \ if ((RTNNAME)->addr[0] == '%') \ (RTNNAME)->addr[0] = '_'; \ } #define IS_SAME_RTN(NAME1, NAME2) (MIDENT_EQ(NAME1, NAME2)) #define INSERT_EXTREF_NOMORPH_AND_RETURN(RTNNAME) \ { \ oprtype routine, rte1; \ triple *ref; \ \ rte1 = put_str((RTNNAME)->addr, (RTNNAME)->len); \ CONVERT_MUTIL_NAME_PERCENT_TO_UNDERSCORE((RTNNAME)); \ routine = PUT_CDREF((RTNNAME)); \ ref = newtriple(OC_RHDADDR); \ ref->operand[0] = rte1; \ ref->operand[1] = routine; \ routine = put_tref(ref); \ return routine; \ } GBLREF stack_frame *frame_pointer; GBLREF mident routine_name; error_def(ERR_LABELEXPECTED); error_def(ERR_RTNNAME); /* Compiler entry point to parse an entry ref generating the necessary triples for just the actual call (does not handle * routine parameters). * * Parameters: * * op1 - Opcode to use if this is a local call (call within current routine). * op2 - Opcode to use if this is, or needs to be treated as, an external call. This latter is for auto-relink so if a * new version of a routine exists, we effectively treat it as an external call since the call goes to a different * flavor of the routine. * commargcode - What type of command this is for (code from indir_* enum in indir.h) * can_commarg - Indicates whether or not routine is allowed to call commarg to deal with indirects. Currently the only * routine to set this to false is m_zgoto.c since it already deals with indirects its own way. * labref - Only TRUE for calls from exfunc(). When TRUE, label offsets are not allowed. * textname - Only TRUE for ZGOTO related calls where the routine/label names are passed as text instead of resolved to * linkage table entries. */ triple *entryref(opctype op1, opctype op2, mint commargcode, boolean_t can_commarg, boolean_t labref, boolean_t textname) { oprtype offset, label, routine, rte1; char rtn_text[SIZEOF(mident_fixed)], lab_text[SIZEOF(mident_fixed)]; mident rtnname, labname; mstr rtn_str, lbl_str; triple *ref, *next, *rettrip; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* There is no current case where both can_commarg and textname parameters are TRUE. If they start to exist, the code in * this routine needs to be revisited for proper operation as the textname conditions were assumed not to happen if * can_commarg was FALSE (which it is in the one known use of textname TRUE - in m_zgoto). Assert this now. */ assert(!(can_commarg && textname)); /* Initialize our routine and label identifier midents */ rtnname.len = labname.len = 0; rtnname.addr = &rtn_text[0]; labname.addr = &lab_text[0]; label.oprclass = NO_REF; /* Discover what sort of entryref we have */ switch (TREF(window_token)) { case TK_INTLIT: int_label(); /* caution: fall through */ case TK_IDENT: memcpy(labname.addr, (TREF(window_ident)).addr, (TREF(window_ident)).len); labname.len = (TREF(window_ident)).len; advancewindow(); if ((TK_PLUS != TREF(window_token)) && (TK_CIRCUMFLEX != TREF(window_token)) && !IS_MCODE_RUNNING && can_commarg) { /* Only label specified - implies current routine */ rettrip = newtriple(op1); rettrip->operand[0] = put_mlab(&labname); return rettrip; } assert(NO_REF == label.oprclass); break; case TK_ATSIGN: if(!indirection(&label)) return NULL; if (!(!can_commarg || (TK_PLUS == TREF(window_token)) || (TK_CIRCUMFLEX == TREF(window_token)))) { /* a single indirect arg like @ARG - not @LBL^[@]rtn or @LBL+1, or +15 etc. */ rettrip = ref = maketriple(OC_COMMARG); ref->operand[0] = label; ref->operand[1] = put_ilit(commargcode); ins_triple(ref); return rettrip; } assert(0 == labname.len); break; case TK_PLUS: if (labref) { /* extrinsics require a label */ stx_error(ERR_LABELEXPECTED); return NULL; } /* WARNING fallthrough possible */ default: assert(0 == labname.len); assert(NO_REF == label.oprclass); break; } if (!labref && (TK_PLUS == TREF(window_token))) { /* Have line offset allowed and specified */ advancewindow(); if (EXPR_FAIL == expr(&offset, MUMPS_INT)) return NULL; } else offset.oprclass = NO_REF; if (TK_CIRCUMFLEX == TREF(window_token)) { /* Have a routine name specified */ advancewindow(); switch (TREF(window_token)) { case TK_IDENT: MROUT2XTERN((TREF(window_ident)).addr, rtnname.addr, (TREF(window_ident)).len); rtnname.len = (TREF(window_ident)).len; advancewindow(); if (!IS_MCODE_RUNNING) { /* Triples for normal compiled code. */ if (!textname) { /* Triples for DO, GOTO, extrinsic functions ($$). Resolve routine and label names * to addresses for most calls. */ if ((NO_REF == label.oprclass) && (NO_REF == offset.oprclass)) { /* Do LABEL^RTN comes here (LABEL is *not* indirect *and* no offset) */ rettrip = insert_extref_fast(op1, op2, &rtnname, &labname); return rettrip; } else /* Label or offset was indirect so can't use linkage table to address * the pieces - and no autorelink either */ routine = insert_extref(&rtnname); } else { /* Triples for ZGOTO. Pass routine and label names as text literals */ if ((NO_REF == label.oprclass) && (NO_REF == offset.oprclass)) { /* Both label and routine supplied but no offset */ rettrip = maketriple(op2); rettrip->operand[0] = put_str(rtnname.addr, rtnname.len); ref = newtriple(OC_PARAMETER); ref->operand[0] = put_str(labname.addr, labname.len); ref->operand[1] = put_ilit(0); rettrip->operand[1] = put_tref(ref); ins_triple(rettrip); return rettrip; } else /* Routine only (no label - may have offset) */ routine = put_str(rtnname.addr, rtnname.len); } } else { /* Triples for indirect code (at indirect compile time) */ routine = put_str(rtnname.addr, rtnname.len); if (!textname) { /* If not returning text name, convert text name to routine header address */ ref = newtriple(OC_RHDADDR1); ref->operand[0] = routine; routine = put_tref(ref); } } break; case TK_ATSIGN: if (!indirection(&routine)) return NULL; if (!textname) { /* If not returning text name, convert text name to routine header address */ ref = newtriple(OC_RHDADDR1); ref->operand[0] = routine; routine = put_tref(ref); } break; default: stx_error(ERR_RTNNAME); return NULL; } } else { if ((NO_REF == label.oprclass) && (0 == labname.len) && (NO_REF == offset.oprclass)) { stx_error(ERR_LABELEXPECTED); return NULL; } if (!textname) routine = put_tref(newtriple(OC_CURRHD)); else { /* If we need a name, the mechanism to retrieve it differs between normal and indirect compilation. * For normal compile, use routine name set when started compile. Routine name can vary. For textname=TRUE * callers (zgoto) fetch name from frame_pointer at runtime. */ routine = put_str("", 0); } } if (NO_REF == offset.oprclass) /* No offset supplied - supply default */ offset = put_ilit(0); if (NO_REF == label.oprclass) /* No label indirect - value resides in labname so make a proper parameter out of it */ label = put_str(labname.addr, labname.len); ref = textname ? newtriple(OC_PARAMETER) : newtriple(OC_LABADDR); ref->operand[0] = label; next = newtriple(OC_PARAMETER); ref->operand[1] = put_tref(next); next->operand[0] = offset; if (!textname) next->operand[1] = routine; /* Not needed if giving text names */ rettrip = next = newtriple(op2); next->operand[0] = routine; next->operand[1] = put_tref(ref); return rettrip; } #ifdef AUTORELINK_SUPPORTED /* Routine used in an autorelink-enabled USHBIN build (UNIX Shared BInary) to generate the triples to reference (call, extrinsic * or goto) an external routine. This version of this routine generates calls slightly different than its counterpart below for * non-USHBIN or platforms not supported for autorelink in that any call where the routine name is specified, even if the * routine name is for the current routine, are treated as external calls with an autorelink-needed check. Note indirects do not * come through here. * * Parameters: * * op1 - Opcode to use for local routine (used by this routine only for local-only [colon-suffixed] labels). * op2 - Opcode to use for external call. * rtnname - Text name of routine being referenced. * labname - Text label being referenced in given routine (may be NULL string) * * Note this routine sets up the complete reference (label and routine) so returning from entryref() is expected after calling * this routine. */ STATICFNDEF triple *insert_extref_fast(opctype op1, opctype op2, mident *rtnname, mident *labname) { triple *rettrip; mstr lbl_str; mlabel *mlab; /* Do LABEL^RTN comes here (LABEL is *not* indirect) */ if (IS_SAME_RTN(rtnname, &routine_name)) { /* If same routine as current routine, see if this label is a local-only label (colon-suffixed). If so, * treat this as a local call. */ mlab = get_mladdr(labname); if (!mlab->gbl) { /* Is a local label - generate local call */ rettrip = newtriple(op1); rettrip->operand[0] = put_mlab(labname); return rettrip; } } /* Else create external call reference so routine can be checked for auto-relink at call */ rettrip = maketriple(op2); CONVERT_MUTIL_NAME_PERCENT_TO_UNDERSCORE(rtnname); rettrip->operand[0] = PUT_CDREF(rtnname); mlabel2xtern(&lbl_str, rtnname, labname); rettrip->operand[1] = PUT_CDREF(&lbl_str); ins_triple(rettrip); return rettrip; } /* This routine is called to generate a triple for a given routine reference when the label is indirect and the routine * isn't. Since an indirect is involved, this form generates individual triples for resolving the routine and label * addresses with this routine generating only the routine resolution triples. Since this is for a USHBIN build, all * references where a routine name is provided generate an external call type call so auto-relinking can occur if it * is enabled. */ STATICFNDEF oprtype insert_extref(mident *rtnname) { /* Generate routine reference triple */ INSERT_EXTREF_NOMORPH_AND_RETURN(rtnname); } #else /* Routine used in a NON_USHBIN build to generate triples for a label reference (call, function invocation or goto) a routine. * At this point, it could be an internal or external routine. Note indirects do not come here. * * Parameters: * * same as described in the USHBIN version of this routine above except both op1 and op2 are used. */ STATICFNDEF triple *insert_extref_fast(opctype op1, opctype op2, mident *rtnname, mident *labname) { triple *rettrip; mstr lbl_str; /* Do LABEL^RTN comes here (LABEL is *not* indirect) */ if (IS_SAME_RTN(rtnname, &routine_name)) { /* If same routine as current routine, we can morph the call into an internal call that merrily * references only the label. */ rettrip = newtriple(op1); rettrip->operand[0] = put_mlab(labname); } else { /* Create an external reference to LABEL^RTN */ rettrip = maketriple(op2); CONVERT_MUTIL_NAME_PERCENT_TO_UNDERSCORE(rtnname); rettrip->operand[0] = PUT_CDREF(rtnname); mlabel2xtern(&lbl_str, rtnname, labname); rettrip->operand[1] = PUT_CDREF(&lbl_str); ins_triple(rettrip); } return rettrip; } /* The NON_USHBIN flavor of thisroutine is similar to the USHBIN flavor except if the routine being called is the * same as the current routine, we morph it into a local (same-routine) type call (no auto-relinking). */ STATICFNDEF oprtype insert_extref(mident *rtnname) { if (!IS_SAME_RTN(rtnname, &routine_name)) { /* Generate external routine reference triple */ INSERT_EXTREF_NOMORPH_AND_RETURN(rtnname); } else /* Generate internal routine reference triple */ return put_tref(newtriple(OC_CURRHD)); } #endif fis-gtm-V7.0-005/sr_port/err_check.c0000755000032200000250000000410014342376331016132 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "error.h" VMS_ONLY(LITREF)UNIX_ONLY(GBLREF) err_ctl merrors_ctl; VMS_ONLY(LITREF)UNIX_ONLY(GBLREF) err_ctl cmerrors_ctl; VMS_ONLY(LITREF)UNIX_ONLY(GBLREF) err_ctl cmierrors_ctl; VMS_ONLY(LITREF)UNIX_ONLY(GBLREF) err_ctl gdeerrors_ctl; #ifdef VMS LITREF err_ctl laerrors_ctl; /* Roger thinks that this one is obsolete */ LITREF err_ctl lperrors_ctl; /* Roger thinks that this one may be obsolete */ #endif STATICDEF const err_ctl *all_errors[] = { &merrors_ctl, &gdeerrors_ctl, &cmierrors_ctl, &cmerrors_ctl, #ifdef VMS &laerrors_ctl, &lperrors_ctl, #endif NULL }; /* Returns the error control struct corresponding to the errornum if it is valid, otherwise * returns NULL */ const err_ctl *err_check(int errnum) { /* errnum structure: * ___________________________________________ * | 0fff1 FACILITY 1 MSG_IDX SEV| * |___________________________________________| * 31 27 15 3 0 * * fff - flag bits since GTM-7759 */ const err_ctl *fac; int errtype; int msg_id; /* Error message number once facility and severity are stripped */ if (0 > errnum) return NULL ; for (errtype = 0; all_errors[errtype]; errtype++) { fac = all_errors[errtype]; msg_id = MSGMASK(errnum, fac->facnum); /* These conditions ensure: The facility bits are identical, the message index * doesn't exceed the array size and is larger than zero */ if ((((errnum & 0x0FFFFFFF) >> MSGFAC) == (FACMASK(fac->facnum) >> MSGFAC)) && (msg_id <= fac->msg_cnt) && (1 <= msg_id)) return fac; } return NULL ; } fis-gtm-V7.0-005/sr_port/error.h0000755000032200000250000001214714342376331015355 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef ERROR_H_INCLUDED #define ERROR_H_INCLUDED typedef struct err_msg_struct { char *tag; char *msg; int parm_count; int mu_int_sev_idx; } err_msg; typedef struct err_ctl_struct { int facnum; char *facname; const err_msg *fst_msg; /* For VMS, this pointer is not used, and its value will typically be NULL */ int msg_cnt; const int *undocmsg; int undocmsg_cnt; } err_ctl; #include "wbox_test_init.h" /* needed for DUMPABLE macro which uses WBTEST_ENABLED */ #include "errorsp.h" #define ERROR_RETURN error_return /* The message id is a 32 bit number encoded as: * 0ggg1fffffffffff1ccccccccccccsss * 33222222222211111111110000000000 * 10987654321098765432109876543210 * Where: * ggg Are 3 fla'g' bits * fffffffffff Is an 11 bit unsigned number denoting the 'f'acility * cccccccccccc Is a 12 bit unsigned number denoting the ordinal 'c'ount value of the message within the facility * sss Is a 3 bit unsigned number denoting the message 's'everity * * Currently the only flag value defined is 001 to denote that the error should be syslogged (GTM-7759) * It would be possible to define one more flag bit, but probably something would break if a message id * mapped to a negative integer. */ #define FCNTL 1 #define MSGCNTL 27 #define MSGFAC 16 #define MSGNBIT 15 #define MSGSEVERITY 3 #define MSGNUM 3 #define MSGMUSTLOG 1 #define FACMASK(fac) (FCNTL << MSGCNTL | 1 << MSGNBIT | (fac) << MSGFAC) #define MSGMASK(msg,fac) (((msg & 0x0FFFFFFF) & ~FACMASK(fac)) >> MSGSEVERITY) #define SEVMASK(msg) ((msg) & 7) #define MSGFLAG(msg) ((msg & 0x70000000) >> 28) /* to change default severity of msg to type */ #define MAKE_MSG_TYPE(msg, type) ((msg) & ~SEV_MSK | (type)) /* Define SET_ERROR_CONDITION macro to set global variables "error_condition" as well as "severity" at the same time. * If the two are not kept in sync, it is possible "severity" reflects INFO (from an older error) whereas * "error_condition" is set to TPRETRY which means we would handle it as a TPRETRY INFO type error and that means the * caller that issues rts_error of TPRETRY will see control being returned to it which goes into an out-of-design situation * and could cause SIG-11 (GTM-8083). */ #define SET_ERROR_CONDITION(MSGID) \ { \ error_condition = MSGID; \ severity = (NULL == err_check(MSGID)) ? ERROR : SEVMASK(MSGID); \ } /* Macro used intermittently to trace various error handling invocations */ /* #define DEBUG_ERRHND */ #ifdef DEBUG_ERRHND # define DBGEHND(x) DBGFPF(x) # define DBGEHND_ONLY(x) x # include "gtm_stdio.h" # include "gtmio.h" #else # define DBGEHND(x) # define DBGEHND_ONLY(x) #endif const err_ctl *err_check(int err); CONDITION_HANDLER(ccp_ch); CONDITION_HANDLER(ccp_exi_ch); CONDITION_HANDLER(compiler_ch); CONDITION_HANDLER(cre_priv_ch); CONDITION_HANDLER(dbinit_ch); CONDITION_HANDLER(dse_dmp_handler); CONDITION_HANDLER(dse_f_blk_ch); CONDITION_HANDLER(exi_ch); CONDITION_HANDLER(fgncal_ch); CONDITION_HANDLER(fntext_ch); CONDITION_HANDLER(fnzsrch_ch); CONDITION_HANDLER(gds_rundown_ch); CONDITION_HANDLER(gtcm_ch); CONDITION_HANDLER(gtcm_exi_ch); CONDITION_HANDLER(gtm_env_xlate_ch); CONDITION_HANDLER(gtm_maxstr_ch); CONDITION_HANDLER(gtmio_ch); CONDITION_HANDLER(gtmrecv_ch); CONDITION_HANDLER(gtmrecv_fetchresync_ch); CONDITION_HANDLER(gtmsource_ch); CONDITION_HANDLER(gvcmy_open_ch); CONDITION_HANDLER(gvcmz_netopen_ch); CONDITION_HANDLER(gvcst_remove_statsDB_linkage_ch); CONDITION_HANDLER(gvcst_statsDB_init_ch); CONDITION_HANDLER(gvcst_statsDB_open_ch); CONDITION_HANDLER(gvzwrite_ch); CONDITION_HANDLER(hashtab_rehash_ch); CONDITION_HANDLER(io_init_ch); CONDITION_HANDLER(iob_io_error); CONDITION_HANDLER(jnl_file_autoswitch_ch); CONDITION_HANDLER(job_init_ch); CONDITION_HANDLER(jobexam_dump_ch); CONDITION_HANDLER(mdb_condition_handler); CONDITION_HANDLER(mu_freeze_ch); CONDITION_HANDLER(mu_int_ch); CONDITION_HANDLER(mu_int_reg_ch); CONDITION_HANDLER(mu_rndwn_file_ch); CONDITION_HANDLER(mupip_load_ch); CONDITION_HANDLER(mupip_recover_ch); CONDITION_HANDLER(mupip_set_jnl_ch); CONDITION_HANDLER(mur_multi_rehash_ch); CONDITION_HANDLER(ojch); CONDITION_HANDLER(region_init_ch); CONDITION_HANDLER(replication_ch); CONDITION_HANDLER(stp_gcol_ch); CONDITION_HANDLER(t_ch); CONDITION_HANDLER(terminate_ch); CONDITION_HANDLER(tp_restart_ch); CONDITION_HANDLER(trans_code_ch); CONDITION_HANDLER(updproc_ch); CONDITION_HANDLER(util_base_ch); CONDITION_HANDLER(util_ch); CONDITION_HANDLER(zro_ins_rec_fail_ch); CONDITION_HANDLER(zshow_ch); CONDITION_HANDLER(zyerr_ch); CONDITION_HANDLER(op_fnzatransform_ch); CONDITION_HANDLER(gvn2gds_ch); void mum_tstart(); #endif fis-gtm-V7.0-005/sr_port/error_trap.h0000755000032200000250000001535514342376331016407 0ustar librarygtc/**************************************************************** * Copyright (c) 2001-2017 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. * * * ****************************************************************/ #ifndef ERROR_TRAP_H #define ERROR_TRAP_H #define IS_ETRAP (&(TREF(dollar_etrap)).str == err_act) #define ETRAP_IN_EFFECT (!ztrap_explicit_null && (0 == (TREF(dollar_ztrap)).str.len)) #define NULLIFY_TRAP(TRAP) {(TRAP).mvtype = MV_STR; (TRAP).str.len = 0;} #define DOLLAR_ECODE_MAXINDEX 32 /* maximum of 32 ecodes in $ECODE */ #define DOLLAR_STACK_MAXINDEX 256 /* maximum of 256 levels will be stored for $STACK(level) */ #define DOLLAR_ECODE_ALLOC (1 << 15) /* 32K chunk memory malloced for $ECODE */ #define DOLLAR_STACK_ALLOC (1 << 15) /* 32K chunk memory malloced for $STACK(level) */ #define STACK_ZTRAP_EXPLICIT_NULL -1 /* used to indicate an explicit SET $ZTRAP = "" */ typedef void (*error_ret_fnptr)(void); typedef struct dollar_ecode { mstr ecode_str; } dollar_ecode_struct; typedef struct dollar_stack /* contents of a single $STACK(level) entry */ { mstr mode_str; /* $STACK(level) */ dollar_ecode_struct *ecode_ptr; /* $STACK(level,"ECODE") */ mstr mcode_str; /* $STACK(level,"MCODE") */ mstr place_str; /* $STACK(level,"PLACE") */ } dollar_stack_struct; typedef enum stack_mode { DOLLAR_STACK_INVALID, DOLLAR_STACK_ECODE, /* $STACK(level,"ECODE") */ DOLLAR_STACK_PLACE, /* $STACK(level,"PLACE") */ DOLLAR_STACK_MCODE, /* $STACK(level,"MCODE") */ DOLLAR_STACK_MODE /* $STACK(level) i.e. how this frame got created */ } stack_mode_t; typedef struct { char *begin; /* beginning of malloced memory holding the complete $ECODE */ char *end; /* pointer to where next $ECODE can be added */ char *top; /* allocated end of malloced memory holding the complete $ECODE */ dollar_ecode_struct *array; /* array of DOLLAR_ECODE_MAXINDEX dollar_ecode_struct structures */ uint4 index; /* current count of number of filled structures in array */ int4 error_last_ecode; /* last error code number */ unsigned char *error_last_b_line; /* ptr to beginning of line where error occurred */ struct stack_frame_struct *first_ecode_error_frame; /* "frame_pointer" at the time of adding the first ECODE */ unsigned char *error_rtn_addr; /* CODE_ADDRESS(ERROR_RTN) */ unsigned char *error_rtn_ctxt; /* GTM_CONTEXT(ERROR_RTN) */ error_ret_fnptr error_return_addr; /* CODE_ADDRESS(ERROR_RETURN) */ } dollar_ecode_type; typedef struct { char *begin; /* beginning of malloced memory holding all $STACK(level) detail */ char *end; /* pointer to where next $STACK(level) detail can be added */ char *top; /* allocated end of malloced memory holding all $STACK(level) detail */ dollar_stack_struct *array; /* array of DOLLAR_STACK_MAXINDEX dollar_stack_struct structures */ uint4 index; /* current count of number of filled structures in array */ boolean_t incomplete; /* TRUE if we were not able to fit in all $STACK info */ } dollar_stack_type; /* reset all $ECODE related variables to correspond to $ECODE = NULL state */ #define NULLIFY_DOLLAR_ECODE \ { \ GBLREF dollar_ecode_type dollar_ecode; \ GBLREF dollar_stack_type dollar_stack; \ \ dollar_ecode.end = dollar_ecode.begin; \ dollar_ecode.index = 0; \ dollar_stack.end = dollar_stack.begin; \ dollar_stack.index = 0; \ dollar_stack.incomplete = FALSE; \ dollar_ecode.first_ecode_error_frame = NULL; \ } /* nullify "error_frame" */ #define NULLIFY_ERROR_FRAME \ { \ GBLREF stack_frame *error_frame; \ \ DBGEHND((stderr, "%s: Nullifying previous error_frame (was 0x"lvaddr")\n", __FILE__, error_frame)); \ error_frame = NULL; \ } /* Set "error_frame" to point to "frame_pointer" and mark it as an error frame type. This is an indication that * whenever we unwind back to this frame, we need to transfer control to error_rtn_addr/ctxt (taken care of by getframe). */ #define SET_ERROR_FRAME(fp) \ { \ GBLREF stack_frame *error_frame; \ \ fp->flags |= SFF_ETRAP_ERR; \ error_frame = fp; \ DBGEHND((stderr, "%s: Setting error_frame as 0x"lvaddr"\n", __FILE__, fp)); \ } /* invoke the function error_return() if the necessity of error-rethrow is detected. Note the extra * "&" value on the various assert(FALSE) type statements. This allows us to know which of these asserts * failed. */ #define INVOKE_ERROR_RET_IF_NEEDED \ { \ GBLREF dollar_ecode_type dollar_ecode; \ GBLREF stack_frame *error_frame; \ \ if (NULL != error_frame) \ { \ if (error_frame == frame_pointer) \ { \ if (dollar_ecode.index) /* non-zero implies non-NULL $ECODE */ \ { /* this is an error frame and $ECODE is non-NULL during QUIT out of this frame. \ * rethrow the error at lower level */ \ (*dollar_ecode.error_return_addr)(); \ /* While error_return does not usually return in UNIX, it can if we are \ * unwinding a job-interrupt frame because error rethrowing terminates when a \ * job-interrupt frame is unwound which instead of re-throwing in the \ * interrupted frame sends an error to the operator log. But in VMS, we dont \ * do the latter so it is possible if the current frame is of type SFT_DM that \ * we don't rethrow and don't do a MUM_TSTART either. Assert accordingly. \ */ \ VMS_ONLY(assert(SFT_DM & frame_pointer->type);) \ } else \ { \ assert(FALSE & 2); \ NULLIFY_ERROR_FRAME; /* don't know how we reached here. reset it in PRO */ \ } \ } else if (error_frame < frame_pointer) \ { \ assert(FALSE & 3); \ NULLIFY_ERROR_FRAME; /* don't know how we reached here. reset it in PRO */ \ } \ } \ } void ecode_init(void); void ecode_get(int level, mval *result); /* return $ECODE (if "level" < 0) or $STACK(level,"ECODE") in "result" */ void ecode_set(int errnum); /* convert "errnum" to error-string and call ecode_add() */ boolean_t ecode_add(mstr *str); /* add "str" to $ECODE */ void error_return(void); #ifdef VMS void error_return_vms(void); #endif void get_dollar_stack_info(int level, stack_mode_t mode, mval *result); void get_frame_creation_info(int level, int cur_zlevel, mval *result); void get_frame_place_mcode(int level, stack_mode_t mode, int cur_zlevel, mval *result); #endif /* ERROR_TRAP_H */ fis-gtm-V7.0-005/sr_port/eval_expr.c0000644000032200000250000002361414342376331016202 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "compiler.h" #include "opcode.h" #include "mdq.h" #include "toktyp.h" #include "advancewindow.h" #include "compile_pattern.h" #include "fullbool.h" #include "show_source_line.h" #include "stringpool.h" #include "gtm_string.h" #include "gtm_utf8.h" GBLREF boolean_t run_time; GBLREF triple t_orig; error_def(ERR_EXPR); error_def(ERR_MAXARGCNT); error_def(ERR_RHMISSING); error_def(ERR_SIDEEFFECTEVAL); LITREF octabstruct oc_tab[]; LITREF toktabtype tokentable[]; GBLREF spdesc stringpool; /** * Given a start token that represents a non-unary operation, consumes tokens and constructs an appropriate triple tree. * Adds the triple tree to the chain of execution. * @input[out] a A pointer that will be set to the last token seen * @returns An integer flag of; EXPR_INDR or EXPR_GOOD or EXPR_FAIL * @par Side effects * - Calls advance window multiple times, and consumes tokens accordingly * - Calls expratom multiple times, which (most notably) adds literals to a hash table * - Calls ins_triple, which adds triples to the execution chain */ int eval_expr(oprtype *a) /* process an expression into the operand at *a */ { boolean_t ind_pat, saw_local, saw_se, se_warn, replaced; int op_count, se_handling; opctype bin_opcode; oprtype optyp_1, optyp_2, *optyp_ptr; tbp *catbp, *tripbp; triple *argtrip, *parm, *ref, *ref1, *t1, *t2; mliteral *m1, *m2; mval tmp_mval; int i = 0; unsigned short type; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; CHKTCHAIN(TREF(curtchain), exorder, TRUE); /* defined away in mdq.h except with DEBUG_TRIPLES */ if (!expratom(&optyp_1)) { /* If didn't already add an error of our own, do so now to catch all expression errors */ if (!ALREADY_RTERROR) stx_error(ERR_EXPR); return EXPR_FAIL; } se_handling = TREF(side_effect_handling); se_warn = SE_WARN_ON; while (bin_opcode = tokentable[TREF(window_token)].bo_type) /* NOTE assignment NOT condition */ { type = tokentable[TREF(window_token)].opr_type; if (oc_tab[bin_opcode].octype & OCT_BOOL) { if (!TREF(shift_side_effects)) { assert(FALSE == TREF(saw_side_effect)); for (ref = (TREF(curtchain))->exorder.bl; oc_tab[ref->opcode].octype & OCT_BOOL; ref = ref->exorder.bl) ; assert(ref->exorder.bl != ref); if (&t_orig == ref->exorder.fl) { ref1 = maketriple(OC_NOOP); dqins(ref, exorder, ref1); } assert(&t_orig != ref->exorder.fl); TREF(expr_start) = TREF(expr_start_orig) = ref; CHKTCHAIN(TREF(curtchain), exorder, TRUE); /* defined away in mdq.h except with DEBUG_TRIPLES */ } switch (bin_opcode) { case OC_NAND: case OC_AND: case OC_NOR: case OC_OR: TREF(shift_side_effects) = TRUE; default: break; } } coerce(&optyp_1, type); if (OC_CAT == bin_opcode) { ref1 = ref = maketriple(OC_CAT); catbp = &ref->backptr; /* borrow backptr to track args */ saw_se = saw_local = FALSE; for (op_count = 2; (MAX_ARGS - 3) >= op_count ; op_count++) /* op_count = first operand plus destination */ { /* If we can, concat string literals at compile-time rather than runtime */ replaced = FALSE; if ((OC_PARAMETER == ref1->opcode) && (TRIP_REF == ref1->operand[0].oprclass) && (OC_LIT == ref1->operand[0].oprval.tref->opcode) && (TRIP_REF == optyp_1.oprclass) && (OC_LIT == optyp_1.oprval.tref->opcode)) { /* Copy the string over */ m1 = ref1->operand[0].oprval.tref->operand[0].oprval.mlit; m2 = optyp_1.oprval.tref->operand[0].oprval.mlit; tmp_mval.mvtype = MV_STR; tmp_mval.str.char_len = m1->v.str.char_len + m2->v.str.char_len; tmp_mval.str.len = m1->v.str.len + m2->v.str.len; ENSURE_STP_FREE_SPACE(tmp_mval.str.len); tmp_mval.str.addr = (char *)stringpool.free; memcpy(tmp_mval.str.addr, m1->v.str.addr, m1->v.str.len); memcpy(tmp_mval.str.addr + m1->v.str.len, m2->v.str.addr, m2->v.str.len); stringpool.free = (unsigned char *)tmp_mval.str.addr + tmp_mval.str.len; s2n(&tmp_mval); /* things rely on the compiler doing literals with complete types */ ref1->operand[0] = put_lit(&tmp_mval); unuse_literal(&m1->v); unuse_literal(&m2->v); dqdel(optyp_1.oprval.tref, exorder); optyp_1 = ref1->operand[0]; replaced = TRUE; op_count--; } else { parm = newtriple(OC_PARAMETER); ref1->operand[1] = put_tref(parm); ref1 = parm; ref1->operand[0] = optyp_1; } if (se_handling && !replaced) { /* the following code deals with protecting lvn values from change by a following * side effect and thereby produces a standard evaluation order. It is similar to code in * expritem for function arguments, but has slightly different and easier circumstances */ assert(OLD_SE != TREF(side_effect_handling)); assert(0 < TREF(expr_depth)); t1 = optyp_1.oprval.tref; t2 = (oc_tab[t1->opcode].octype & OCT_COERCE) ? t1->operand[0].oprval.tref : t1; /* need to step back past coerce of side effects in order to detect them */ if (((OC_VAR == t2->opcode) || (OC_GETINDX == t2->opcode)) && (t1 == t2)) saw_local = TRUE; /* left operand is an lvn */ if (saw_local) { if ((TREF(side_effect_base))[TREF(expr_depth)]) saw_se = TRUE; if (saw_se || (OC_VAR == t2->opcode) || (OC_GETINDX == t2->opcode)) { /* chain stores args to manage later insert of temps to hold lvn */ tripbp = &ref1->backptr; assert((tripbp == tripbp->que.fl) && (tripbp == tripbp->que.bl)); tripbp->bpt = ref1; dqins(catbp, que, tripbp); } } } if (TK_UNDERSCORE != TREF(window_token)) { if (!saw_se) /* suppressed standard or lucked out on ordering */ saw_local = FALSE; /* just clear the backptrs - shut off other processing */ /* This code checks to see if the only parameter for this OC_CAT is a string literal, and if it is, then it simply returns the literal*/ assert(1 < op_count); if ((2 == op_count) && (OC_PARAMETER == ref1->opcode) && (TRIP_REF == ref1->operand[0].oprclass) && (OC_LIT == ref1->operand[0].oprval.tref->opcode)) { /* We need to copy some things from the original first */ ref1->operand[0].oprval.tref->src = ref->src; t1 = ref1->operand[0].oprval.tref; ref = t1; optyp_1 = put_tref(t1); break; } dqloop(catbp, que, tripbp) { /* work chained arguments which are in reverse order */ argtrip = tripbp->bpt; assert(NULL != argtrip); dqdel(tripbp, que); tripbp->bpt = NULL; if (!saw_local) continue; /* some need to insert temps */ for (optyp_ptr = &argtrip->operand[0]; INDR_REF == optyp_ptr->oprclass; optyp_ptr = optyp_ptr->oprval.indr) ; /* INDR_REFs used by e.g. extrinsics finally end up at a TRIP_REF */ t1 = optyp_ptr->oprval.tref; if ((OC_VAR == t1->opcode) || (OC_GETINDX == t1->opcode)) { /* have an lvn that needs a temp because threat from some side effect */ argtrip = maketriple(OC_STOTEMP); argtrip->operand[0] = put_tref(t1); dqins(t1, exorder, argtrip); /* NOTE: violates infomation hiding */ optyp_ptr->oprval.tref = argtrip; if (se_warn) ISSUE_SIDEEFFECTEVAL_WARNING(t1->src.column + 1); } } /* end of side effect processing */ assert((catbp == catbp->que.fl) && (catbp == catbp->que.bl) && (NULL == catbp->bpt)); assert(op_count > 1); ref->operand[0] = put_ilit(op_count); ins_triple(ref); break; } advancewindow(); if (!expratom(&optyp_1)) { stx_error(ERR_RHMISSING); return EXPR_FAIL; } coerce(&optyp_1, type); } if ((MAX_ARGS - 3) < op_count) { stx_error(ERR_MAXARGCNT, 1, MAX_ARGS - 3); return EXPR_FAIL; } } else { if ((TK_QUESTION == TREF(window_token)) || (TK_NQUESTION == TREF(window_token))) { ind_pat = FALSE; if (TK_ATSIGN == TREF(director_token)) { ind_pat = TRUE; advancewindow(); } if (!compile_pattern(&optyp_2, ind_pat)) return EXPR_FAIL; } else { advancewindow(); CHKTCHAIN(TREF(curtchain), exorder, TRUE); /* defined away in mdq.h except with DEBUG_TRIPLES */ if (!expratom(&optyp_2)) { stx_error(ERR_RHMISSING); return EXPR_FAIL; } } CHKTCHAIN(TREF(curtchain), exorder, TRUE); /* defined away in mdq.h except with DEBUG_TRIPLES */ coerce(&optyp_2, type); ref1 = optyp_1.oprval.tref; if (((OC_VAR == ref1->opcode) || (OC_GETINDX == ref1->opcode)) && (TREF(side_effect_base))[TREF(expr_depth)]) { /* this section is to protect lvns from changes by a following side effect extrinsic or function * by inserting a temporary to capture the lvn evaluation before it's changed by a "later" or * "to-the-right" side effect; a preexisting coerce or temporary might already to the job; * indirects may already have been shifted to evaluate early */ assert(OLD_SE != TREF(side_effect_handling)); ref = maketriple(OC_STOTEMP); ref->operand[0] = optyp_1; optyp_1 = put_tref(ref); dqins(ref1, exorder, ref); /* NOTE: another violation of information hiding */ if (se_warn) ISSUE_SIDEEFFECTEVAL_WARNING(ref1->src.column + 1); } ref = newtriple(bin_opcode); ref->operand[0] = optyp_1; ref->operand[1] = optyp_2; } optyp_1 = put_tref(ref); } *a = optyp_1; return (OC_INDGLVN == (TREF(curtchain))->exorder.bl->opcode) ? EXPR_INDR : EXPR_GOOD; } fis-gtm-V7.0-005/sr_port/ex_tail.c0000755000032200000250000001407314342376331015644 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "compiler.h" #include "mdq.h" #include "mmemory.h" #include "op.h" #include "opcode.h" #include "fullbool.h" #include "stringpool.h" #include "toktyp.h" #include "flt_mod.h" LITREF mval literal_minusone, literal_one, literal_zero; LITREF octabstruct oc_tab[]; error_def(ERR_NUMOFLOW); void ex_tail(oprtype *opr) /* work a non-leaf operand toward final form * contains code to do arthimetic on literals at compile time * and code to bracket Boolean expressions with BOOLINIT and BOOLFINI */ { boolean_t stop, tv; mval *v, *v0, *v1; opctype c; oprtype *i; triple *bftrip, *bitrip, *t, *t0, *t1, *t2; uint bexprs, j, oct; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(TRIP_REF == opr->oprclass); UNARY_TAIL(opr); /* this is first because it can change opr and thus whether we should even process the tail */ RETURN_IF_RTS_ERROR; CHKTCHAIN(TREF(curtchain), exorder, TRUE); /* defined away in mdq.h except with DEBUG_TRIPLES */ t = opr->oprval.tref; /* Refind t since UNARY_TAIL may have shifted it */ c = t->opcode; oct = oc_tab[c].octype; if ((OCT_EXPRLEAF & oct) || (OC_NOOP == c)) return; assert(TRIP_REF == t->operand[0].oprclass); assert((TRIP_REF == t->operand[1].oprclass) || (NO_REF == t->operand[1].oprclass)); if (!(OCT_BOOL & oct)) { for (i = t->operand, j = 0; ARRAYTOP(t->operand) > i; i++, j++) { if (TRIP_REF == i->oprclass) { for (t0 = i->oprval.tref; OCT_UNARY & oc_tab[t0->opcode].octype; t0 = t0->operand[0].oprval.tref) ; if (OCT_BOOL & oc_tab[t0->opcode].octype) bx_boollit(t0); ex_tail(i); /* chained Boolean or arithmetic */ RETURN_IF_RTS_ERROR; } } while (OCT_ARITH & oct) /* really a sneaky if that allows us to use breaks */ { /* Consider moving this to a separate module (say, ex_arithlit) for clarity and modularity */ /* binary arithmetic operations might be on literals, which can be performed at compile time */ for (i = t->operand, j = 0; ARRAYTOP(t->operand) > i; i++, j++) { if (OC_LIT != t->operand[j].oprval.tref->opcode) break; /* from for */ } if (ARRAYTOP(t->operand) > i) break; /* from while */ for (t0 = t->operand[0].oprval.tref; TRIP_REF == t0->operand[0].oprclass; t0 = t0->operand[0].oprval.tref) dqdel(t0, exorder); for (t1 = t->operand[1].oprval.tref; TRIP_REF == t1->operand[0].oprclass; t1 = t1->operand[0].oprval.tref) dqdel(t1, exorder); v0 = &t0->operand[0].oprval.mlit->v; MV_FORCE_NUMD(v0); v1 = &t1->operand[0].oprval.mlit->v; MV_FORCE_NUMD(v1); if (!(MV_NM & v1->mvtype) || !(MV_NM & v0->mvtype)) { /* if we don't have a useful number we can't do useful math */ TREF(last_source_column) += (TK_EOL == TREF(director_token)) ? -2 : 2; /* improve hints */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NUMOFLOW); assert(TREF(rts_error_in_parse)); return; } v = (mval *)mcalloc(SIZEOF(mval)); switch (c) { case OC_ADD: op_add(v0, v1, v); break; case OC_DIV: case OC_IDIV: case OC_MOD: if (!(MV_NM & v1->mvtype) || (0 != v1->m[1])) { if (OC_DIV == c) op_div(v0, v1, v); else if (OC_MOD == c) flt_mod(v0, v1, v); else op_idiv(v0, v1, v); } else /* divide by literal 0 is a technique so let it go to run time*/ v = NULL; /* flag value to get out of nested switch */ break; case OC_EXP: op_exp(v0, v1, v); if ((1 == v->sgn) && (MV_INT & v->mvtype) && (0 == v->m[0])) v = NULL; /* flag value from op_exp indicates DIVZERO, so leave to run time */ break; case OC_MUL: op_mul(v0, v1, v); break; case OC_SUB: op_sub(v0, v1, v); break; default: assertpro(FALSE && t1->opcode); break; } RETURN_IF_RTS_ERROR; if ((NULL == v) || (!v->mvtype)) break; /* leave divide by zero or missing mvtype from NUMOFLOW to cause run time errors */ unuse_literal(v0); /* drop original literals only after deciding whether to defer */ unuse_literal(v1); dqdel(t0, exorder); dqdel(t1, exorder); n2s(v); s2n(v); /* compiler must leave literals with both numeric and string */ t->opcode = OC_LIT; /* replace the original operator triple with new literal */ put_lit_s(v, t); t->operand[1].oprclass = NO_REF; assert(opr->oprval.tref == t); return; } if ((OC_COMINT == c) && (OC_BOOLINIT == (t0 = t->operand[0].oprval.tref)->opcode)) /* WARNING assignment */ opr->oprval.tref = t0; return; } /* the following code deals with Booleans where the expression is not directly managing flow - those go through bool_expr */ for (t1 = t; ; t1 = t2) { assert(TRIP_REF == t1->operand[0].oprclass); t2 = t1->operand[0].oprval.tref; if (!(OCT_BOOL & oc_tab[t2->opcode].octype)) break; } bitrip = maketriple(OC_BOOLINIT); DEBUG_ONLY(bitrip->src = t->src); dqrins(t1, exorder, bitrip); t2 = t->exorder.fl; assert((OC_COMVAL == t2->opcode) || (OC_COMINT == t2->opcode)); /* may need to change COMINT to COMVAL in bx_boolop */ assert(&t2->operand[0] == opr); /* check next operation ensures an expression */ bftrip = maketriple(OC_BOOLFINI); DEBUG_ONLY(bftrip->src = t->src); bftrip->operand[0] = put_tref(bitrip); opr->oprval.tref = bitrip; dqins(t, exorder, bftrip); i = (oprtype *)mcalloc(SIZEOF(oprtype)); bx_tail(t, FALSE, i); RETURN_IF_RTS_ERROR; if (OC_COMINT == (t2 = bftrip->exorder.fl)->opcode) /* after bx_tail/bx_boolop it's safe to delete any OC_COMINT left */ dqdel(t2, exorder); *i = put_tnxt(bftrip); CHKTCHAIN(TREF(curtchain), exorder, TRUE); /* defined away in mdq except with DEBUG_TRIPLES */ return; } fis-gtm-V7.0-005/sr_port/exfun_frame.c0000644000032200000250000000515014342376333016511 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_stdio.h" #include "gtm_string.h" #include #include "stack_frame.h" #include "mprof.h" #include "error.h" #include "glvn_pool.h" GBLREF stack_frame *frame_pointer; GBLREF unsigned char *msp, *stackbase, *stackwarn, *stacktop; error_def(ERR_STACKCRIT); error_def(ERR_STACKOFLOW); void exfun_frame (void) { register stack_frame *sf; unsigned char *msp_save; msp_save = msp; sf = (stack_frame *)(msp -= SIZEOF(stack_frame)); /* Note imbedded assignment */ assert(sf < frame_pointer); if (msp <= stackwarn) { if (msp <= stacktop) { msp = msp_save; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_STACKOFLOW); } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_STACKCRIT); } assert (msp < stackbase); assert((frame_pointer < frame_pointer->old_frame_pointer) || (NULL == frame_pointer->old_frame_pointer)); *sf = *frame_pointer; msp -= sf->rvector->temp_size; if (msp <= stackwarn) { if (msp <= stacktop) { msp = msp_save; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_STACKOFLOW); } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_STACKCRIT); } sf->temps_ptr = msp; assert(msp < stackbase); memset(msp, 0, sf->rvector->temp_size); SET_GLVN_INDX(sf, GLVN_POOL_UNTOUCHED); sf->ret_value = NULL; sf->dollar_test = -1; sf->old_frame_pointer = frame_pointer; sf->flags = 0; /* Don't propagate special flags */ sf->type &= SFT_ZINTR_OFF; /* Don't propagate special type - normally can't propagate but if $ZINTERRUPT frame is * rewritten by ZGOTO to a "regular" frame, this frame type *can* propagate. */ frame_pointer = sf; assert((frame_pointer < frame_pointer->old_frame_pointer) || (NULL == frame_pointer->old_frame_pointer)); DBGEHND((stderr, "exfun_frame: Added stackframe at addr 0x"lvaddr" old-msp: 0x"lvaddr" new-msp: 0x"lvaddr" for routine " "%.*s (rtnhdr 0x"lvaddr")\n", sf, msp_save, msp, sf->rvector->routine_name.len, sf->rvector->routine_name.addr, sf->rvector)); return; } void exfun_frame_sp(void) { exfun_frame(); new_prof_frame (TRUE); } void exfun_frame_push_dummy_frame(void) { exfun_frame(); new_prof_frame (FALSE); } fis-gtm-V7.0-005/sr_port/exfunc.c0000755000032200000250000001146414342376331015510 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "compiler.h" #include "opcode.h" #include "toktyp.h" #include "mdq.h" #include "advancewindow.h" #define INDIR_DUMMY -1 error_def(ERR_ACTOFFSET); int exfunc(oprtype *a, boolean_t alias_target) { triple *calltrip, *calltrip_opr1_tref, *counttrip, *funret, *labelref, *masktrip; triple *oldchain, *ref0, *routineref, tmpchain, *triptr; # if defined(USHBIN_SUPPORTED) || defined(VMS) triple *tripsize; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(TK_DOLLAR == TREF(window_token)); advancewindow(); dqinit(&tmpchain, exorder); oldchain = setcurtchain(&tmpchain); calltrip = entryref(OC_EXFUN, OC_EXTEXFUN, INDIR_DUMMY, TRUE, TRUE, FALSE); setcurtchain(oldchain); if (!calltrip) return FALSE; if (OC_EXFUN == calltrip->opcode) { assert(MLAB_REF == calltrip->operand[0].oprclass); # if defined(USHBIN_SUPPORTED) || defined(VMS) ref0 = newtriple(OC_PARAMETER); ref0->operand[0] = put_tsiz(); /* Need size of following code gen triple here */ calltrip->operand[1] = put_tref(ref0); tripsize = ref0->operand[0].oprval.tref; assert(OC_TRIPSIZE == tripsize->opcode); # else ref0 = calltrip; # endif } else { assert(TRIP_REF == calltrip->operand[1].oprclass); calltrip_opr1_tref = calltrip->operand[1].oprval.tref; if (OC_EXTEXFUN == calltrip->opcode) { if (OC_CDREF == calltrip_opr1_tref->opcode) assert(CDREF_REF == calltrip_opr1_tref->operand[0].oprclass); else { assert(OC_LABADDR == calltrip_opr1_tref->opcode); assert(TRIP_REF == calltrip_opr1_tref->operand[1].oprclass); assert(OC_PARAMETER == calltrip_opr1_tref->operand[1].oprval.tref->opcode); assert(TRIP_REF == calltrip_opr1_tref->operand[1].oprval.tref->operand[0].oprclass); assert(OC_ILIT == calltrip_opr1_tref->operand[1].oprval.tref->operand[0].oprval.tref->opcode); assert(ILIT_REF == calltrip_opr1_tref->operand[1].oprval.tref->operand[0].oprval.tref->operand[0].oprclass); if (0 != calltrip_opr1_tref->operand[1].oprval.tref->operand[0].oprval.tref->operand[0].oprval.ilit) { stx_error(ERR_ACTOFFSET); return FALSE; } } } else /* indirect: $$@(glvn)[(actuallist)]; note disabiguating parens around glvn specifying dlabel*/ { assert(OC_COMMARG == calltrip->opcode); assert(TRIP_REF == calltrip->operand[1].oprclass); assert(OC_ILIT == calltrip_opr1_tref->opcode); assert(ILIT_REF == calltrip_opr1_tref->operand[0].oprclass); assert(INDIR_DUMMY == calltrip_opr1_tref->operand[0].oprval.ilit); assert(calltrip->exorder.fl == &tmpchain); routineref = maketriple(OC_CURRHD); labelref = maketriple(OC_LABADDR); ref0 = maketriple(OC_PARAMETER); dqrins(calltrip, exorder, routineref); dqrins(calltrip, exorder, labelref); dqrins(calltrip, exorder, ref0); labelref->operand[0] = calltrip->operand[0]; labelref->operand[1] = put_tref(ref0); ref0->operand[0] = calltrip->operand[1]; ref0->operand[0].oprval.tref->operand[0].oprval.ilit = 0; ref0->operand[1] = put_tref(routineref); calltrip->operand[0] = put_tref(routineref); calltrip->operand[1] = put_tref(labelref); calltrip->opcode = OC_EXTEXFUN; } ref0 = newtriple(OC_PARAMETER); ref0->operand[0] = calltrip->operand[1]; calltrip->operand[1] = put_tref(ref0); } if (TK_LPAREN != TREF(window_token)) { masktrip = newtriple(OC_PARAMETER); counttrip = newtriple(OC_PARAMETER); masktrip->operand[0] = put_ilit(0); counttrip->operand[0] = put_ilit(0); masktrip->operand[1] = put_tref(counttrip); ref0->operand[1] = put_tref(masktrip); } else if (!actuallist(&ref0->operand[1])) return FALSE; triptr = oldchain->exorder.bl; dqadd(triptr, &tmpchain, exorder); /*this is a violation of info hiding*/ if (OC_EXFUN == calltrip->opcode) { assert(MLAB_REF == calltrip->operand[0].oprclass); triptr = newtriple(OC_JMP); triptr->operand[0] = put_mfun(&calltrip->operand[0].oprval.lab->mvname); calltrip->operand[0].oprclass = ILIT_REF; /* dummy placeholder */ # if defined(USHBIN_SUPPORTED) || defined(VMS) tripsize->operand[0].oprval.tsize->ct = triptr; # endif } /* If target is an alias, use special container-expecting routine OC_EXFUNRETALS, else regular OC_EXFUNRET */ funret = newtriple((alias_target ? OC_EXFUNRETALS : OC_EXFUNRET)); funret->operand[0] = *a = put_tref(calltrip); return TRUE; } fis-gtm-V7.0-005/sr_port/exp.mpt0000755000032200000250000000146614342376331015373 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 1989,2001 Sanchez Computer Associates, 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %EXP ;GT.M %EXP utility - raise 1st argument to the power of the 2nd ;invoke with %I and %J (%I**%J) to obtain result in %I ;invoke at INT to execute interactively ;invoke at FUNC as an extrinsic function ; s %I=$$FUNC(%I,%J) q INT n %I,%J r !,"Power: ",%J r !,"Number: ",%I w !,%I," raised to ",%J," is ",$$FUNC(%I,%J),! q FUNC(i,j) n f,w i i<0,j#1 q "" q i**j fis-gtm-V7.0-005/sr_port/expr.c0000755000032200000250000000345614342376331015200 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" /* needed by INCREMENT_EXPR_DEPTH */ #include "compiler.h" #include "opcode.h" #include "fullbool.h" #include "mdq.h" int expr(oprtype *a, int m_type) { int rval; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; INCREMENT_EXPR_DEPTH; CHKTCHAIN(TREF(curtchain), exorder, TRUE); /* defined away in mdq.h except with DEBUG_TRIPLES */ if (EXPR_FAIL == (rval = eval_expr(a))) /* NOTE assignment */ { DECREMENT_EXPR_DEPTH; return FALSE; } coerce(a, (MUMPS_INT == m_type) ? OCT_MINT : OCT_MVAL); ex_tail(a); /* There is a chance this will return a OCT_MVAL when we want OCT_MINT; force it again */ RETURN_EXPR_IF_RTS_ERROR; coerce(a, (MUMPS_INT == m_type) ? OCT_MINT : OCT_MVAL); /* Investigate whether ex_tail can do a better job */ if (TREF(expr_start) != TREF(expr_start_orig) && (OC_NOOP != (TREF(expr_start))->opcode)) { assert((OC_GVSAVTARG == (TREF(expr_start))->opcode)); if ((OC_GVSAVTARG == (TREF(expr_start))->opcode) && ((GTM_BOOL == TREF(gtm_fullbool)) || !TREF(saw_side_effect))) { if ((OC_GVRECTARG != (TREF(curtchain))->exorder.bl->opcode) || ((TREF(curtchain))->exorder.bl->operand[0].oprval.tref != TREF(expr_start))) newtriple(OC_GVRECTARG)->operand[0] = put_tref(TREF(expr_start)); } } DECREMENT_EXPR_DEPTH; return rval; } fis-gtm-V7.0-005/sr_port/expratom.c0000755000032200000250000000135614342376331016056 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" int expratom(oprtype *a) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; switch(TREF(window_token)) { case TK_IDENT: case TK_CIRCUMFLEX: case TK_ATSIGN: return glvn(a); default: return expritem(a); } } fis-gtm-V7.0-005/sr_port/expritem.c0000644000032200000250000006365114342376331016057 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "compiler.h" #include "mdq.h" #include "opcode.h" #include "toktyp.h" #include "svnames.h" #include "nametabtyp.h" #include "funsvn.h" #include "advancewindow.h" #include "stringpool.h" #include "namelook.h" #include "fullbool.h" #include "show_source_line.h" #include "op.h" GBLREF bool devctlexp; GBLREF boolean_t run_time; error_def(ERR_EXPR); error_def(ERR_FCNSVNEXPECTED); error_def(ERR_FNOTONSYS); error_def(ERR_INVFCN); error_def(ERR_INVSVN); error_def(ERR_NUMOFLOW); error_def(ERR_RPARENMISSING); error_def(ERR_SIDEEFFECTEVAL); error_def(ERR_VAREXPECTED); LITREF toktabtype tokentable[]; LITREF mval literal_null; LITREF octabstruct oc_tab[]; /* note that svn_index array provides indexes into this array for each letter of the * alphabet so changes here should be reflected there. */ LITDEF nametabent svn_names[] = { /* Must be in alpha order and in sync with svn_index[], svn_data[]; when inerting beware of legacy implications */ { 1, "D" }, { 6, "DEVICE" } ,{ 2, "EC" }, { 5, "ECODE" } ,{ 2, "ES" }, { 6, "ESTACK" } ,{ 2, "ET" }, { 5, "ETRAP" } ,{ 1, "H" }, { 7, "HOROLOG" } ,{ 1, "I" }, { 2, "IO" } ,{ 1, "J" }, { 3, "JOB" } ,{ 1, "K" }, { 3, "KEY" } ,{ 1, "P" }, { 9, "PRINCIPAL" } ,{ 1, "Q" }, { 4, "QUIT" } ,{ 1, "R" }, { 9, "REFERENCE" } ,{ 2, "ST" }, { 5, "STACK" } ,{ 1, "S" }, { 7, "STORAGE" } ,{ 2, "SY" }, { 6, "SYSTEM" } ,{ 1, "T" }, { 4, "TEST" } ,{ 2, "TL"}, { 6, "TLEVEL"} ,{ 2, "TR"}, { 8, "TRESTART"} ,{ 1, "X" } ,{ 1, "Y" } ,{ 2, "ZA" } ,{10, "ZALLOCSTOR" } ,{ 6, "ZAUDIT" } ,{ 2, "ZB" } ,{ 2, "ZC" } ,{ 6, "ZCHSET" } ,{ 6, "ZCLOSE" } ,{ 8, "ZCMDLINE" } ,{ 8, "ZCOMPILE" } ,{ 8, "ZCSTATUS" } ,{ 2, "ZD" } /* legacy abbreviation for $ZDIRECTORY */ ,{ 9, "ZDATEFORM" } ,{10, "ZDIRECTORY" } ,{ 2, "ZE" } /* legacy abbreviation for $ZERROR */ ,{ 7, "ZEDITOR" } ,{ 4, "ZEOF" } ,{ 6, "ZERROR" } ,{ 7, "ZGBLDIR" } ,{ 8, "ZHOROLOG" } ,{12, "ZININTERRUPT" } ,{10, "ZINTERRUPT"} ,{ 3, "ZIO" } ,{ 4, "ZJOB" } ,{ 4, "ZKEY" } ,{ 6, "ZLEVEL" } ,{10, "ZMALLOCLIM" } ,{13, "ZMAXTPTIMEOUT" } ,{ 8, "ZMLKHASH" } ,{ 5, "ZMODE" } ,{ 9, "ZONLNRLBK" } ,{ 2, "ZP" } /* legacy abbreviation for $ZPOSITION */ ,{11, "ZPATNUMERIC" } ,{ 4, "ZPIN" } ,{ 9, "ZPOSITION" } ,{ 5, "ZPOUT" } ,{ 7, "ZPROMPT" } ,{ 5, "ZQUIT" } ,{10, "ZREALSTORE" } ,{ 8, "ZRELDATE" } ,{ 9, "ZROUTINES" } ,{ 2, "ZS" } /* legacy abbreviation for $ZSTATUS */ ,{ 7, "ZSOURCE" } ,{ 7, "ZSTATUS" } ,{ 5, "ZSTEP"} ,{ 9, "ZSTRPLLIM" } ,{ 7, "ZSYSTEM" } ,{ 2, "ZT" } /* legacy abbreviation for $ZTRAP */ ,{ 6, "ZTCODE" } ,{ 6, "ZTDATA" } ,{ 7, "ZTDELIM"} ,{ 6, "ZTEXIT" } ,{ 8, "ZTIMEOUT" } ,{ 7, "ZTLEVEL" } ,{ 6, "ZTNAME" } ,{10, "ZTOLDVALUE" } ,{ 5, "ZTRAP" } ,{10, "ZTRIGGEROP" } ,{ 7, "ZTSLATE" } ,{ 8, "ZTUPDATE" } ,{ 7, "ZTVALUE" } ,{10, "ZTWORMHOLE" } ,{ 9, "ZUSEDSTOR" } ,{ 3, "ZUT" } ,{ 8, "ZVERSION" } ,{ 7, "ZYERROR" } }; /* Indexes into svn_names array for each letter of the alphabet */ LITDEF unsigned char svn_index[27] = { 0, 0, 0, 0, 2, 8, 8, 8, 10, /* a b c d e f g h i */ 12, 14 ,16, 16, 16, 16, 16, 18, 20, /* j k l m n o p q r */ 22, 28, 34 ,34, 34, 34, 35, 36, 101 /* s t u v w x y z ~ */ }; /* These entries correspond to the entries in the svn_names array */ LITDEF svn_data_type svn_data[] = { { SV_DEVICE, FALSE, ALL_SYS }, { SV_DEVICE, FALSE, ALL_SYS } ,{ SV_ECODE, TRUE, ALL_SYS }, { SV_ECODE, TRUE, ALL_SYS } ,{ SV_ESTACK, FALSE, ALL_SYS }, { SV_ESTACK, FALSE, ALL_SYS } ,{ SV_ETRAP, TRUE, ALL_SYS }, { SV_ETRAP, TRUE, ALL_SYS } ,{ SV_HOROLOG, FALSE, ALL_SYS }, { SV_HOROLOG, FALSE, ALL_SYS } ,{ SV_IO, FALSE, ALL_SYS }, { SV_IO, FALSE, ALL_SYS } ,{ SV_JOB, FALSE, ALL_SYS }, { SV_JOB, FALSE, ALL_SYS } ,{ SV_KEY, FALSE, ALL_SYS }, { SV_KEY, FALSE, ALL_SYS } ,{ SV_PRINCIPAL, FALSE, ALL_SYS }, { SV_PRINCIPAL, FALSE, ALL_SYS } ,{ SV_QUIT, FALSE, ALL_SYS }, { SV_QUIT, FALSE, ALL_SYS } ,{ SV_REFERENCE, FALSE, ALL_SYS }, { SV_REFERENCE, FALSE, ALL_SYS } ,{ SV_STACK, FALSE, ALL_SYS }, { SV_STACK, FALSE, ALL_SYS } ,{ SV_STORAGE, FALSE, ALL_SYS }, { SV_STORAGE, FALSE, ALL_SYS } ,{ SV_SYSTEM, TRUE, ALL_SYS }, { SV_SYSTEM, TRUE, ALL_SYS } ,{ SV_TEST, FALSE, ALL_SYS }, { SV_TEST, FALSE, ALL_SYS } ,{ SV_TLEVEL, FALSE, ALL_SYS }, { SV_TLEVEL, FALSE, ALL_SYS } ,{ SV_TRESTART, FALSE, ALL_SYS }, { SV_TRESTART, FALSE, ALL_SYS } ,{ SV_X, TRUE, ALL_SYS } ,{ SV_Y, TRUE, ALL_SYS } ,{ SV_ZA, FALSE, ALL_SYS } ,{ SV_ZALLOCSTOR, FALSE, ALL_SYS } ,{ SV_ZAUDIT, FALSE, ALL_SYS } ,{ SV_ZB, FALSE, ALL_SYS } ,{ SV_ZC, FALSE, ALL_SYS } ,{ SV_ZCHSET, FALSE, ALL_SYS } ,{ SV_ZCLOSE, FALSE, UNIX_OS } ,{ SV_ZCMDLINE, FALSE, ALL_SYS } ,{ SV_ZCOMPILE, TRUE, ALL_SYS } ,{ SV_ZCSTATUS, FALSE, ALL_SYS} ,{ SV_ZDIR, TRUE, ALL_SYS } ,{ SV_ZDATE_FORM, TRUE, ALL_SYS } ,{ SV_ZDIR, TRUE, ALL_SYS } ,{ SV_ZERROR, TRUE, ALL_SYS } ,{ SV_ZEDITOR, FALSE, ALL_SYS } ,{ SV_ZEOF, FALSE, ALL_SYS } ,{ SV_ZERROR, TRUE, ALL_SYS } ,{ SV_ZGBLDIR, TRUE, ALL_SYS } ,{ SV_ZHOROLOG, FALSE, ALL_SYS } ,{ SV_ZININTERRUPT, FALSE, ALL_SYS} ,{ SV_ZINTERRUPT, TRUE, ALL_SYS} ,{ SV_ZIO, FALSE, ALL_SYS } ,{ SV_ZJOB, FALSE, ALL_SYS } ,{ SV_ZKEY, FALSE , ALL_SYS } ,{ SV_ZLEVEL, FALSE, ALL_SYS } ,{ SV_ZMALLOCLIM, TRUE, ALL_SYS} ,{ SV_ZMAXTPTIME, TRUE, ALL_SYS } ,{ SV_ZMLKHASH, FALSE, ALL_SYS } ,{ SV_ZMODE, FALSE, ALL_SYS } ,{ SV_ZONLNRLBK, FALSE, UNIX_OS } ,{ SV_ZPOS, FALSE, ALL_SYS } ,{ SV_ZPATNUMERIC, FALSE, ALL_SYS } ,{ SV_ZPIN, FALSE, ALL_SYS } ,{ SV_ZPOS, FALSE, ALL_SYS } ,{ SV_ZPOUT, FALSE, ALL_SYS } ,{ SV_PROMPT, TRUE, ALL_SYS } ,{ SV_ZQUIT, TRUE, ALL_SYS } ,{ SV_ZREALSTOR, FALSE, ALL_SYS } ,{ SV_ZRELDATE, FALSE, ALL_SYS } ,{ SV_ZROUTINES, TRUE, ALL_SYS } ,{ SV_ZSTATUS, TRUE, ALL_SYS } ,{ SV_ZSOURCE, TRUE, ALL_SYS } ,{ SV_ZSTATUS, TRUE, ALL_SYS } ,{ SV_ZSTEP, TRUE, ALL_SYS } ,{ SV_ZSTRPLLIM, TRUE, ALL_SYS } ,{ SV_ZSYSTEM, FALSE, ALL_SYS } ,{ SV_ZTRAP, TRUE, ALL_SYS } ,{ SV_ZTCODE, FALSE, TRIGGER_OS } ,{ SV_ZTDATA, FALSE, TRIGGER_OS } ,{ SV_ZTDELIM, FALSE, TRIGGER_OS } ,{ SV_ZTEXIT, TRUE, ALL_SYS } ,{ SV_ZTIMEOUT, TRUE, UNIX_OS} ,{ SV_ZTLEVEL, FALSE, TRIGGER_OS} ,{ SV_ZTNAME, FALSE, TRIGGER_OS } ,{ SV_ZTOLDVAL, FALSE, TRIGGER_OS } ,{ SV_ZTRAP, TRUE, ALL_SYS } ,{ SV_ZTRIGGEROP, FALSE, TRIGGER_OS} ,{ SV_ZTSLATE, TRUE, TRIGGER_OS} ,{ SV_ZTUPDATE, FALSE, TRIGGER_OS } ,{ SV_ZTVALUE, TRUE, TRIGGER_OS } ,{ SV_ZTWORMHOLE, TRUE, TRIGGER_OS } ,{ SV_ZUSEDSTOR, FALSE, ALL_SYS } ,{ SV_ZUT, FALSE, ALL_SYS } ,{ SV_ZVERSION, FALSE, ALL_SYS } ,{ SV_ZYERROR, TRUE, ALL_SYS } }; /* note that fun_index array provides indexes into this array for each letter of the * alphabet so changes here should be reflected there. * "*" is used below only after 8 characters. */ LITDEF nametabent fun_names[] = { /* Must be in alpha order & in sync with fun_index[], fun_data[], fun_parse; when inerting beware of legacy implications */ {1, "A"}, {5, "ASCII"} ,{1, "C"}, {4, "CHAR"} ,{1, "D"}, {4, "DATA"} ,{1, "E"}, {7, "EXTRACT"} ,{1, "F"}, {4, "FIND"} ,{2, "FN"}, {7, "FNUMBER"} ,{1, "G"}, {3, "GET"} ,{1, "I"}, {4, "INCR"}, {9, "INCREMENT"} ,{1, "J"}, {7, "JUSTIFY"} ,{1, "L"}, {6, "LENGTH"} ,{1, "N"} /* legacy abbreviation for $NEXT() */ ,{2, "NA"}, {4, "NAME"} ,{4, "NEXT"} ,{1, "O"}, {5, "ORDER"} ,{1, "P"}, {5, "PIECE"} ,{1, "Q"} /* legacy abbreviation for $QUERY() */ ,{2, "QL"}, {7, "QLENGTH"} ,{2, "QS"}, {10, "QSUBSCRIPT"} ,{5, "QUERY"} ,{1, "R"}, {6, "RANDOM"} ,{2, "RE"}, {7, "REVERSE"} ,{1, "S"}, {6, "SELECT"} ,{2, "ST"}, {5, "STACK"} ,{1, "T"}, {4, "TEXT"} ,{2, "TR"}, {9, "TRANSLATE"} ,{1, "V"} ,{4, "VIEW"} ,{2, "ZA"} /* legacy abbreviation for $ZASCII() */ ,{8, "ZAHANDLE"} ,{6, "ZASCII"} ,{11,"ZATRANSFORM"} ,{9, "ZAUDITLOG"} ,{7, "ZBITAND"} ,{9, "ZBITCOUNT"} ,{8, "ZBITFIND"} ,{7, "ZBITGET"} ,{10, "ZBITLENGTH"} ,{7, "ZBITNOT"} ,{6, "ZBITOR"} ,{7, "ZBITSET"} ,{7, "ZBITSTR"} ,{7, "ZBITXOR"} ,{5, "ZCHAR"} ,{3, "ZCO"} ,{8, "ZCOLLATE"} ,{8, "ZCONVERT"} ,{2, "ZD"} /* legacy abbreviation for $ZDATE() */ ,{5, "ZDATA"} ,{5, "ZDATE"} ,{8, "ZEXTRACT"} ,{5, "ZFIND"} ,{7, "ZGETJPI"} ,{10,"ZINCREMENT"} ,{2, "ZJ"} /* legacy abbreviation for $ZJUSTIFY() */ ,{8, "ZJOBEXAM"} ,{8, "ZJUSTIFY"} ,{7, "ZLENGTH"} ,{8, "ZMESSAGE"} ,{2, "ZP"} /* legacy abbreviation for $ZPREVIOUS() */ ,{6, "ZPARSE"} ,{5, "ZPEEK"} ,{6, "ZPIECE"} ,{9, "ZPREVIOUS"} ,{8, "ZQGBLMOD"} ,{7, "ZSEARCH"} ,{8, "ZSIGPROC"} ,{7, "ZSOCKET"} ,{7, "ZSUBSTR"} ,{7, "ZSYSLOG"} ,{10,"ZTRANSLATE"} ,{8, "ZTRIGGER"} ,{7, "ZTRNLNM"} ,{6, "ZWIDTH"} ,{6, "ZWRITE"} }; /* Index into fun_names array where entries that start with each letter of the alphabet begin. */ LITDEF unsigned char fun_index[27] = { 0, 2, 2, 4, 6, 8, 12, 14, 14, /* a b c d e f g h i */ 17, 19, 19, 21, 21, 25, 27, 29, 35, /* j k l m n o p q r */ 39, 43, 47, 47, 49, 49, 49, 49, 96 /* s t u v w x y z ~ */ }; /* Each entry corresponds to an entry in fun_names */ LITDEF fun_data_type fun_data[] = { { OC_FNASCII, ALL_SYS }, { OC_FNASCII, ALL_SYS } ,{ OC_FNCHAR, ALL_SYS }, { OC_FNCHAR, ALL_SYS } ,{ OC_FNDATA, ALL_SYS }, { OC_FNDATA, ALL_SYS } ,{ OC_FNEXTRACT, ALL_SYS }, { OC_FNEXTRACT, ALL_SYS } ,{ OC_FNFIND, ALL_SYS }, { OC_FNFIND, ALL_SYS } ,{ OC_FNFNUMBER, ALL_SYS }, { OC_FNFNUMBER, ALL_SYS } ,{ OC_FNGET, ALL_SYS }, { OC_FNGET, ALL_SYS } ,{ OC_FNINCR, ALL_SYS }, { OC_FNINCR, ALL_SYS }, { OC_FNINCR, ALL_SYS } ,{ OC_FNJ2, ALL_SYS }, { OC_FNJ2, ALL_SYS } ,{ OC_FNLENGTH, ALL_SYS }, { OC_FNLENGTH, ALL_SYS } ,{ OC_FNNEXT, ALL_SYS } ,{ OC_FNNAME, ALL_SYS }, { OC_FNNAME, ALL_SYS } ,{ OC_FNNEXT, ALL_SYS } ,{ OC_FNORDER, ALL_SYS }, {OC_FNORDER, ALL_SYS } ,{ OC_FNPIECE, ALL_SYS }, { OC_FNPIECE, ALL_SYS } ,{ OC_FNQUERY, ALL_SYS } ,{ OC_FNQLENGTH, ALL_SYS }, { OC_FNQLENGTH, ALL_SYS } ,{ OC_FNQSUBSCR, ALL_SYS }, { OC_FNQSUBSCR, ALL_SYS } ,{ OC_FNQUERY, ALL_SYS } ,{ OC_FNRANDOM, ALL_SYS }, { OC_FNRANDOM, ALL_SYS } ,{ OC_FNREVERSE, ALL_SYS }, { OC_FNREVERSE, ALL_SYS } ,{ OC_PASSTHRU, ALL_SYS }, { OC_PASSTHRU, ALL_SYS } /* f_select creates a STOTEMP and STOs anchored by a PASSTHRU */ ,{ OC_FNSTACK1, ALL_SYS }, { OC_FNSTACK1, ALL_SYS } ,{ OC_FNTEXT, ALL_SYS }, { OC_FNTEXT, ALL_SYS } ,{ OC_FNTRANSLATE, ALL_SYS }, { OC_FNTRANSLATE, ALL_SYS } ,{ OC_FNVIEW, ALL_SYS } ,{ OC_FNVIEW, ALL_SYS } ,{ OC_FNZASCII, ALL_SYS } ,{ OC_FNZAHANDLE, ALL_SYS } ,{ OC_FNZASCII, ALL_SYS } ,{ OC_FNZATRANSFORM, ALL_SYS } ,{ OC_FNZAUDITLOG, ALL_SYS } ,{ OC_FNZBITAND, ALL_SYS } ,{ OC_FNZBITCOUN, ALL_SYS } ,{ OC_FNZBITFIND, ALL_SYS } ,{ OC_FNZBITGET, ALL_SYS } ,{ OC_FNZBITLEN, ALL_SYS } ,{ OC_FNZBITNOT, ALL_SYS } ,{ OC_FNZBITOR, ALL_SYS } ,{ OC_FNZBITSET, ALL_SYS } ,{ OC_FNZBITSTR, ALL_SYS } ,{ OC_FNZBITXOR, ALL_SYS } ,{ OC_FNZCHAR, ALL_SYS } ,{ OC_FNZCONVERT2, UNIX_OS } ,{ OC_FNZCOLLATE, UNIX_OS } ,{ OC_FNZCONVERT2, UNIX_OS } ,{ OC_FNZDATE, ALL_SYS } ,{ OC_FNZDATA, ALL_SYS } ,{ OC_FNZDATE, ALL_SYS } ,{ OC_FNZEXTRACT, ALL_SYS } ,{ OC_FNZFIND, ALL_SYS } ,{ OC_FNZGETJPI, ALL_SYS } ,{ OC_FNINCR, ALL_SYS } ,{ OC_FNZJ2, ALL_SYS } ,{ OC_FNZJOBEXAM, ALL_SYS } ,{ OC_FNZJ2, ALL_SYS } ,{ OC_FNZLENGTH, ALL_SYS } ,{ OC_FNZM, ALL_SYS } ,{ OC_FNZPREVIOUS, ALL_SYS } ,{ OC_FNZPARSE, ALL_SYS } ,{ OC_FNZPEEK, UNIX_OS } ,{ OC_FNZPIECE, ALL_SYS } ,{ OC_FNZPREVIOUS, ALL_SYS } ,{ OC_FNZQGBLMOD, ALL_SYS } ,{ OC_FNZSEA, ALL_SYS } ,{ OC_FNZSIGPROC, ALL_SYS } ,{ OC_FNZSOCKET, ALL_SYS } ,{ OC_FNZSUBSTR, ALL_SYS } ,{ OC_FNZSYSLOG, UNIX_OS } ,{ OC_FNZTRANSLATE, ALL_SYS } ,{ OC_FNZTRIGGER, TRIGGER_OS } ,{ OC_FNZTRNLNM, ALL_SYS } ,{ OC_FNZWIDTH, ALL_SYS } ,{ OC_FNZWRITE, ALL_SYS } }; /* Each entry corresponds to an entry in fun_names */ GBLDEF int (*fun_parse[])(oprtype *, opctype) = /* contains addresses so can't be a LITDEF */ { f_ascii, f_ascii, f_char, f_char, f_data, f_data, f_extract, f_extract, f_find, f_find, f_fnumber, f_fnumber, f_get, f_get, f_incr, f_incr, f_incr, f_justify, f_justify, f_length, f_length, f_next, f_name, f_name, f_next, f_order, f_order, f_piece, f_piece, f_query, f_qlength, f_qlength, f_qsubscript, f_qsubscript, f_query, f_mint, f_mint, f_reverse, f_reverse, f_select, f_select, f_stack, f_stack, f_text, f_text, f_translate, f_translate, f_view, f_view, f_ascii, f_zahandle, f_ascii, f_zatransform, f_one_mval, f_two_mval, /* $ZBITAND */ f_one_mval, /* $ZBITCOUNT */ f_fnzbitfind, f_fnzbitget, f_one_mval, /* $ZBITLENGTH*/ f_one_mval, /* $ZBITNOT */ f_two_mval, /* $ZBITOR */ f_fnzbitset, f_fnzbitstr, f_two_mval, /* $ZBITXOR */ f_zchar, f_zconvert, f_zcollate, f_zconvert, f_zdate, f_data, /* $ZDATA reuses parser for $DATA since only runtime execution differs */ f_zdate, f_extract, f_find, f_mint_mstr, /* $ZGETJPI */ f_incr, f_justify, f_zjobexam, f_justify, f_length, f_mint, /* $ZMESSAGE */ f_zprevious, f_zparse, f_zpeek, f_piece, f_zprevious, f_zqgblmod, f_zsearch, f_zsigproc, f_zsocket, f_extract, /* $ZSUBSTR */ f_one_mval, /* $ZSYSLOG */ f_translate, f_ztrigger, f_ztrnlnm, f_zwidth, f_zwrite, }; int expritem(oprtype *a) /* process an expression item into the operand at *a */ { boolean_t parse_warn, saw_local, saw_se, se_warn; int i, index, sv_opcode; mval v; oprtype *j, *k, x1; tbp argbp, *funcbp, *tripbp; triple *argtrip, *functrip, *ref, *t1, *t2, *t3; unsigned char type; unsigned int argcnt; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; CHKTCHAIN(TREF(curtchain), exorder, TRUE); /* defined away in mdq.h except with DEBUG_TRIPLES */ assert(svn_index[26] == (SIZEOF(svn_names)/SIZEOF(nametabent))); assert(SIZEOF(svn_names)/SIZEOF(nametabent) == SIZEOF(svn_data)/SIZEOF(svn_data_type)); /* are all SVNs covered? */ assert(fun_index[26] == (SIZEOF(fun_names)/SIZEOF(nametabent))); assert(SIZEOF(fun_names)/SIZEOF(nametabent) == SIZEOF(fun_data)/SIZEOF(fun_data_type)); /* are all functions covered? */ if (i = tokentable[TREF(window_token)].uo_type) /* NOTE assignment */ { type = tokentable[TREF(window_token)].opr_type; advancewindow(); if ((OC_NEG == i) && ((TK_NUMLIT == TREF(window_token)) || (TK_INTLIT == TREF(window_token)))) { assert(MV_IS_NUMERIC(&(TREF(window_mval)))); if ((TREF(window_mval)).mvtype & MV_INT) (TREF(window_mval)).m[1] = -(TREF(window_mval)).m[1]; else (TREF(window_mval)).sgn = 1; if (TK_NUMLIT == TREF(window_token)) n2s(&(TREF(window_mval))); } else { if (!expratom(&x1)) return FALSE; coerce(&x1, type); ref = newtriple((opctype)i); ref->operand[0] = x1; *a = put_tref(ref); return TRUE; } } switch (i = TREF(window_token)) /* NOTE assignment */ { case TK_INTLIT: n2s(&(TREF(window_mval))); case TK_NUMLIT: case TK_STRLIT: *a = put_lit(&(TREF(window_mval))); advancewindow(); return TRUE; case TK_LPAREN: advancewindow(); if (eval_expr(a) && TK_RPAREN == TREF(window_token)) { advancewindow(); return TRUE; } stx_error(ERR_RPARENMISSING); return FALSE; case TK_DOLLAR: parse_warn = saw_se = FALSE; if ((TK_DOLLAR == TREF(director_token)) || (TK_AMPERSAND == TREF(director_token))) { ENCOUNTERED_SIDE_EFFECT; TREF(temp_subs) = TRUE; saw_se = TRUE; advancewindow(); if ((TK_DOLLAR == TREF(window_token)) ? (EXPR_FAIL == exfunc(a, FALSE)) : (EXPR_FAIL == extern_func(a))) return FALSE; } else { advancewindow(); if (TK_IDENT != TREF(window_token)) { stx_error(ERR_FCNSVNEXPECTED); return FALSE; } if (TK_LPAREN == TREF(director_token)) { index = namelook(fun_index, fun_names, (TREF(window_ident)).addr, (TREF(window_ident)).len); if ((-1 != index) && !gtm_utf8_mode) { /** When possible, update opcodes rather than mess with xfer table */ switch (fun_data[index].opcode) { case OC_FNASCII: index = namelook(fun_index, fun_names, "zascii", 6); break; case OC_FNCHAR: index = namelook(fun_index, fun_names, "zchar", 5); break; case OC_FNEXTRACT: index = namelook(fun_index, fun_names, "zextract", 8); break; case OC_FNFIND: index = namelook(fun_index, fun_names, "zfind", 5); break; case OC_FNLENGTH: index = namelook(fun_index, fun_names, "zlength", 7); break; case OC_FNPIECE: index = namelook(fun_index, fun_names, "zpiece", 6); break; case OC_FNTRANSLATE: index = namelook(fun_index, fun_names, "ztranslate", 10); break; default: break; } } if (index < 0) { STX_ERROR_WARN(ERR_INVFCN); /* sets "parse_warn" to TRUE */ } else { assert(SIZEOF(fun_names) / SIZEOF(fun_data_type) > index); if (!VALID_FUN(index)) { STX_ERROR_WARN(ERR_FNOTONSYS); /* sets "parse_warn" to TRUE */ } else if ((OC_FNINCR == fun_data[index].opcode) || (OC_FNZCALL == fun_data[index].opcode)) { /* $INCR is used. This can operate on undefined local variables * and make them defined. If used in a SET where the left and right * side of the = operator use this variable (as a subscript on the left * and as input to the $INCR function on the right), we want an UNDEF * error to show up which means we need to set "temp_subs" to TRUE. */ ENCOUNTERED_SIDE_EFFECT; TREF(temp_subs) = TRUE; saw_se = TRUE; } } advancewindow(); if (((OC_FNZCHAR == fun_data[index].opcode) || (OC_FNCHAR == fun_data[index].opcode)) && (MV_NUM_APPROX == ((MV_NM | MV_NUM_APPROX) & (TREF(director_mval)).mvtype))) { /* [z]f_char need an error based s2n failing to create a valid numeric argument */ assert((0 == (TREF(director_mval)).e) && !val_iscan(&(TREF(director_mval)))); TREF(last_source_column) += (TK_EOL == TREF(director_token)) ? -2 : 2; /* improve hints */ stx_error(ERR_NUMOFLOW); } advancewindow(); if (!parse_warn) { assert(OPCODE_COUNT > fun_data[index].opcode); if (!(boolean_t)((*fun_parse[index])(a, fun_data[index].opcode))) return FALSE; } else { *a = put_lit((mval *)&literal_null); /* Parse remaining arguments until reaching the corresponding RIGHT-PAREN/SPACE/EOL */ if (!parse_until_rparen_or_space()) return FALSE; } if (TK_RPAREN != TREF(window_token)) { stx_error(ERR_RPARENMISSING); return FALSE; } advancewindow(); } else { index = namelook(svn_index, svn_names, (TREF(window_ident)).addr, (TREF(window_ident)).len); if (0 > index) { STX_ERROR_WARN(ERR_INVSVN); /* sets "parse_warn" to TRUE */ } else { assert(SIZEOF(svn_names) / SIZEOF(svn_data_type) > index); if (!VALID_SVN(index)) { STX_ERROR_WARN(ERR_FNOTONSYS); /* sets "parse_warn" to TRUE */ } } advancewindow(); if (!parse_warn) { sv_opcode = svn_data[index].opcode; assert(SV_NUM_SV > sv_opcode); switch (sv_opcode) { case SV_TEST: *a = put_tref(newtriple(OC_GETTRUTH)); break; case SV_ZCHSET: /* making these literals at compile time assumes code is */ case SV_ZRELDATE: /* generated for/in the environment where it runs, which */ case SV_ZVERSION: /* relies on appropriate code management */ op_svget(sv_opcode, &v); assert(MVTYPE_IS_NUMERIC(v.mvtype) && MVTYPE_IS_STRING(v.mvtype)); ENSURE_STP_FREE_SPACE(v.str.len); memcpy((char *)stringpool.free, v.str.addr, v.str.len); v.str.addr = (char *)stringpool.free; stringpool.free += v.str.len; *a = put_lit(&v); break; case SV_X: case SV_Y: devctlexp = TRUE; /* WARNING fallthrough */ default: ref = newtriple(OC_SVGET); ref->operand[0] = put_ilit(sv_opcode); *a = put_tref(ref); break; } } else *a = put_lit((mval *)&literal_null); return TRUE; } } if (saw_se && (OLD_SE != TREF(side_effect_handling))) { assert(TREF(expr_depth) <= TREF(side_effect_depth)); (TREF(side_effect_base))[TREF(expr_depth)] = TRUE; } functrip = t1 = a->oprval.tref; if (parse_warn || !(TREF(side_effect_base))[TREF(expr_depth)] || (NO_REF == functrip->operand[1].oprclass)) return TRUE; /* 1 argument gets a pass */ switch (functrip->opcode) { case OC_EXFUN: /* relies on protection from actuallist */ case OC_EXTEXFUN: /* relies on protection from actuallist */ case OC_FNFGNCAL: /* relies on protection from actuallist */ case OC_EXFUNRET: /* arguments are addresses/locations rather than values */ case OC_FNGET: /* $get() gets a pass because protects itself */ case OC_FNINCR: /* $increment() gets a pass because its ordering needs no protection */ case OC_FNNEXT: /* only has 1 arg, but uses 2 for lvn interface */ case OC_FNORDER: /* may have 1 or 2 args, internally uses 1 extra for lvn arg, but protects itself */ case OC_FNZPREVIOUS: /* only has 1 arg, but uses 2 for lvn interface */ case OC_INDINCR: /* $increment() gets a pass because its ordering needs no protection */ return TRUE; } /* default falls through */ assert(0 < TREF(expr_depth)); /* This block protects lvn evaluations in earlier arguments from changes caused by side effects in later * arguments by capturing the prechange value in a temporary; coerce or preexisting temporary might already * do the job and indirect local evaluations may already have shifted to occur earlier. This algorithm is similar * to one in eval_expr for concatenation, but it must deal with possible arguments in both operands for * both the initial triple and the last parameter triple, and the possibility of empty operand[0] in some * functions so they have not been combined. We should have least one side effect (see compiler.h) and two * arguments to bother - to know side effect, we have an array malloc'd and high water marked to avoid a limit * on expression nesting depth, anchored by TREF(side_effect_base) and indexed by TREF(expr_depth) so * ENCOUNTERED_SIDE_EFFECT can mark the prior level; f_select mallocs and free its own array */ assert(OLD_SE != TREF(side_effect_handling)); funcbp = &functrip->backptr; /* borrow backptr to track args */ tripbp = &argbp; dqinit(tripbp, que); tripbp->bpt = NULL; assert(NULL == funcbp->bpt); assert((funcbp == funcbp->que.fl) && (funcbp == funcbp->que.bl)); saw_se = saw_local = FALSE; for (argtrip = t1; ; argtrip = t1) { /* work functrip,oprval.tref arguments forward */ if (argtrip != functrip) tripbp = &argtrip->backptr; assert(NULL == tripbp->bpt); for (j = argtrip->operand; j < ARRAYTOP(argtrip->operand); j++) { /* process all (two) operands */ t1 = j->oprval.tref; if (NO_REF == j->oprclass) continue; /* some functions leave holes in their arguments */ if (((ARRAYTOP(argtrip->operand) - 1) == j) && (TRIP_REF == j->oprclass) && (OC_PARAMETER == t1->opcode)) break; /* only need to deal with last operand[1] */ for (k = j; INDR_REF == k->oprclass; k = k->oprval.indr) ; /* INDR_REFs used by e.g. extrinsics finally end up at a TRIP_REF */ if (TRIP_REF != k->oprclass) continue; /* something else - not to worry */ /* may need to step back past coerce of side effects */ t3 = k->oprval.tref; t2 = (oc_tab[t3->opcode].octype & OCT_COERCE) ? t3->operand[0].oprval.tref : t3; if ((OC_VAR == t2->opcode) || (OC_GETINDX == t2->opcode)) { /* it's an lvn */ if ((t3 != t2) || ((ARRAYTOP(argtrip->operand) - 1) == j)) continue; /* but if it's the last or there's a coerce */ saw_local = TRUE; /* left operand may need protection */ } if (!saw_local) continue; /* no local yet to worry about */ saw_se = TRUE; if (NULL != tripbp->bpt) { /* this one's already flagged */ assert((ARRAYTOP(argtrip->operand) - 1) == j); continue; } /* chain stores args to manage later insert of temps to hold left values */ assert((tripbp == tripbp->que.fl) && (tripbp == tripbp->que.bl)); tripbp->bpt = argtrip; dqins(funcbp, que, tripbp); } if ((NULL == t1) || (OC_PARAMETER != t1->opcode)) break; /* end of arg list */ assert(argtrip->operand[1].oprval.tref == t1); } if (!saw_se) /* might have lucked out on ordering */ saw_local = FALSE; /* just clear the backptrs - shut off other processing */ saw_se = FALSE; se_warn = SE_WARN_ON; dqloop(funcbp, que, tripbp) { /* work chained arguments which are in reverse order */ argtrip = tripbp->bpt; assert(NULL != argtrip); dqdel(tripbp, que); tripbp->bpt = NULL; if (!saw_local) continue; /* found some need to insert temps */ for (j = &argtrip->operand[1]; j >= argtrip->operand; j--) { /* match to the operand - usually 0 but have to cover 1 as well */ for (k = j; INDR_REF == k->oprclass; k = k->oprval.indr) ; /* INDR_REFs used by e.g. extrinsics finally end up at a TRIP_REF */ assert((TRIP_REF == k->oprclass) || (NO_REF == k->oprclass)); t1 = k->oprval.tref; if ((NO_REF == k->oprclass) || (OC_PARAMETER == t1->opcode) || (oc_tab[t1->opcode].octype & OCT_COERCE)) continue; if ((OC_VAR == t1->opcode) || (OC_GETINDX == t1->opcode)) { /* have an operand that needs a temp because threat from some side effect */ ref = maketriple(OC_STOTEMP); ref->operand[0] = put_tref(t1); dqins(t1, exorder, ref); /* NOTE:this violates infomation hiding */ k->oprval.tref = ref; if (se_warn) ISSUE_SIDEEFFECTEVAL_WARNING(t1->src.column + 1); } else saw_se = TRUE; } } assert((funcbp == funcbp->que.fl) && (funcbp == funcbp->que.bl) && (NULL == funcbp->bpt)); return TRUE; /* end of order of evaluation processing for functions*/ case TK_COLON: stx_error(ERR_EXPR); return FALSE; } /* case default: intentionally omitted as it simply uses the below return FALSE */ return FALSE; } fis-gtm-V7.0-005/sr_port/ext2jnl.c0000755000032200000250000002613014342376331015602 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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 /* for offsetof() macro */ #ifdef VMS #include /* required for gtmsource.h */ #endif #include "gtm_ctype.h" #include "mlkdef.h" #include "gtm_string.h" #include "subscript.h" #include "gdsroot.h" /* for filestruct.h */ #include "gdsbt.h" /* for gdsfhead.h */ #include "gtm_facility.h" /* for fileinfo.h */ #include "fileinfo.h" /* for gdsfhead.h */ #include "gdsfhead.h" /* for filestruct.h */ #include "filestruct.h" /* for jnl.h */ #include "jnl.h" #include "repl_dbg.h" #include "copy.h" #include "zshow.h" #include "mvalconv.h" #include "str2gvkey.h" #include "repl_filter.h" #include "repl_msg.h" #include "gtmsource.h" #include "gdsblk.h" GBLREF char *ext_stop; GBLREF gv_key *gv_currkey; GBLREF boolean_t is_src_server; GBLREF repl_msg_ptr_t gtmsource_msgp; GBLREF int gtmsource_msgbufsiz; GBLREF volatile boolean_t timer_in_handler; static boolean_t in_tp; static int4 num_records; /* callers please set up the proper condition-handlers */ /* expects a null-terminated ext_buff. does the equivalent but inverse of jnl2ext */ unsigned char *ext2jnlcvt(char *ext_buff, int4 ext_len, unsigned char **tr, int *tr_bufsiz, seq_num saved_jnl_seqno, seq_num saved_strm_seqno) { char *ext_next; unsigned char *rec, *rectop, *tmp, *origbuf, *temp_rec; int tmpbufsiz, tmpsize; rec = *tr; rectop = rec + *tr_bufsiz; temp_rec = rec; for ( ; (NULL != (ext_next = strchr(ext_buff, '\n'))); ) { *ext_next++ = '\0'; if (MAX_JNL_REC_SIZE > (rectop - rec)) { /* Remaining space not enough to hold ONE max-sized jnl record. Expand linearly */ tmpsize = *tr_bufsiz; tmpbufsiz = tmpsize + (EXT2JNLCVT_EXPAND_FACTOR * MAX_JNL_REC_SIZE); origbuf = *tr; tmpsize = rec - origbuf; if (is_src_server) { /* In the case of the source server, the pointer "tr" passed in is actually * 8 bytes after gtmsource_msgp and so the malloc/realloc needs to happen * 8 bytes before. Also compression buffers need to be reallocated so hardcode * all of this even though it is violation of information hiding in this generic * routine. The alternative is to return an out-of-space status and bubble it up * through all the callers until the caller of "repl_filter" and do the reallocation * there and reinvoke through the same caller graph to come back here and resume * operation. That is tricky and not considered worth the effort since there are only * two callers of this function (one through source server and one through the receiver * server). Hence this choice. */ assert((unsigned char *)>msource_msgp->msg[0] == *tr); assert(*tr_bufsiz == gtmsource_msgbufsiz); UNIX_ONLY(gtmsource_alloc_msgbuff(tmpbufsiz, FALSE);) VMS_ONLY(gtmsource_alloc_msgbuff(tmpbufsiz);) *tr_bufsiz = gtmsource_msgbufsiz; *tr = >msource_msgp->msg[0]; rec = *tr + tmpsize; rectop = (unsigned char *)gtmsource_msgp + gtmsource_msgbufsiz; } else { tmp = malloc(tmpbufsiz); memcpy(tmp, origbuf, tmpsize); free(origbuf); *tr = tmp; *tr_bufsiz = tmpbufsiz; rec = tmp + tmpsize; rectop = tmp + tmpbufsiz; } } rec = (unsigned char *)ext2jnl(ext_buff, (jnl_record *)rec, saved_jnl_seqno, saved_strm_seqno); assert(0 == (INTPTR_T)rec % JNL_REC_START_BNDRY); if (ext_stop == ext_buff) break; ext_buff = ext_next; } assertpro(rec != temp_rec); ext_stop = ext_buff; return rec; } /* expects a single null-terminated ptr (equivalent to one line in the journal extract file) */ char *ext2jnl(char *ptr, jnl_record *rec, seq_num saved_jnl_seqno, seq_num saved_strm_seqno) { unsigned char *pool_save, ch, chtmp; char *val_off, *strtokptr; int keylength, keystate, len, i, reclen, temp_reclen, val_len; bool keepgoing; mstr src, des; muextract_type exttype; enum jnl_record_type rectype; jrec_suffix *suffix; uint4 nodeflags; DEBUG_ONLY(uint4 tcom_num = 0;) ext_stop = ptr + strlen(ptr) + 1; exttype = (muextract_type)MUEXTRACT_TYPE(ptr); assert((exttype >= 0) && (exttype < MUEXT_MAX_TYPES)); switch(exttype) { case MUEXT_SET: if (in_tp) { if (0 == num_records) rec->prefix.jrec_type = JRT_TSET; else rec->prefix.jrec_type = JRT_USET; num_records++; } else rec->prefix.jrec_type = JRT_SET; break; case MUEXT_KILL: if (in_tp) { if (0 == num_records) rec->prefix.jrec_type = JRT_TKILL; else rec->prefix.jrec_type = JRT_UKILL; num_records++; } else rec->prefix.jrec_type = JRT_KILL; break; case MUEXT_ZKILL: if (in_tp) { if (0 == num_records) rec->prefix.jrec_type = JRT_TZKILL; else rec->prefix.jrec_type = JRT_UZKILL; num_records++; } else rec->prefix.jrec_type = JRT_ZKILL; break; # ifdef GTM_TRIGGER case MUEXT_ZTWORM: assertpro(in_tp); if (0 == num_records) rec->prefix.jrec_type = JRT_TZTWORM; else rec->prefix.jrec_type = JRT_UZTWORM; num_records++; break; case MUEXT_LGTRIG: assertpro(in_tp); if (0 == num_records) rec->prefix.jrec_type = JRT_TLGTRIG; else rec->prefix.jrec_type = JRT_ULGTRIG; num_records++; break; case MUEXT_ZTRIG: assertpro(in_tp); if (0 == num_records) rec->prefix.jrec_type = JRT_TZTRIG; else rec->prefix.jrec_type = JRT_UZTRIG; num_records++; break; # endif case MUEXT_TSTART: in_tp = TRUE; num_records = 0; return (char *)rec; break; case MUEXT_TCOMMIT: rec->prefix.jrec_type = JRT_TCOM; DEBUG_ONLY( /* External filter format has only ONE TSTART..TCOM. The journal record received from the external filter * SHOULD also have only ONE TSTART..TCOM */ tcom_num++; assert(1 == tcom_num); ) rec->jrec_tcom.num_participants = 1; /* Only ONE TSTART..TCOM in the external filter format */ in_tp = FALSE; break; case MUEXT_PINI: case MUEXT_PFIN: case MUEXT_EOF: case MUEXT_ZTSTART: case MUEXT_ZTCOMMIT: assert(FALSE); ext_stop = ptr; return (char *)rec; break; case MUEXT_NULL: rec->prefix.jrec_type = JRT_NULL; break; default: assert(FALSE); ext_stop = ptr; return (char *)rec; break; } rectype = (enum jnl_record_type)rec->prefix.jrec_type; ptr = STRTOK_R(ptr, "\\", &strtokptr); /* get the rec-type field */ assert(NULL != ptr); ptr = STRTOK_R(NULL, "\\", &strtokptr); /* get the time field */ assert(NULL != ptr); ptr = STRTOK_R(NULL, "\\", &strtokptr); /* get the tn field */ assert(NULL != ptr); rec->prefix.tn = asc2i((uchar_ptr_t)ptr, STRLEN(ptr)); ptr = STRTOK_R(NULL, "\\", &strtokptr); /* get the pid field */ assert(NULL != ptr); ptr = STRTOK_R(NULL, "\\", &strtokptr); /* get the client pid field */ assert(NULL != ptr); ptr = STRTOK_R(NULL, "\\", &strtokptr); /* get the token/jnl_seqno field */ assert(NULL != ptr); rec->jrec_null.jnl_seqno = saved_jnl_seqno; ptr = STRTOK_R(NULL, "\\", &strtokptr); /* get the strm_num field */ assert(NULL != ptr); ptr = STRTOK_R(NULL, "\\", &strtokptr); /* get the strm_seqno field */ assert(NULL != ptr); rec->jrec_null.strm_seqno = saved_strm_seqno; switch(exttype) { case MUEXT_NULL: rec->jrec_null.prefix.forwptr = rec->jrec_null.suffix.backptr = NULL_RECLEN; rec->jrec_null.suffix.suffix_code = JNL_REC_SUFFIX_CODE; return ((char_ptr_t)rec) + NULL_RECLEN; case MUEXT_TCOMMIT: ptr = STRTOK_R(NULL, "\\", &strtokptr); /* get the participants */ ptr = STRTOK_R(NULL, "\\", &strtokptr); /* get the jnl_tid */ rec->jrec_tcom.jnl_tid[0] = 0; if (NULL != ptr) strcpy(rec->jrec_tcom.jnl_tid, ptr); num_records = 0; rec->jrec_tcom.prefix.forwptr = rec->jrec_tcom.suffix.backptr = TCOM_RECLEN; rec->jrec_tcom.suffix.suffix_code = JNL_REC_SUFFIX_CODE; return ((char_ptr_t)rec) + TCOM_RECLEN; default: break; } assert(IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(rectype)); ptr = STRTOK_R(NULL, "\\", &strtokptr); /* get the update_num field */ assert(NULL != ptr); assert(OFFSETOF(struct_jrec_upd, update_num) == OFFSETOF(struct_jrec_ztworm, update_num)); rec->jrec_set_kill.update_num = num_records; if ((MUEXT_ZTWORM != exttype) && (MUEXT_LGTRIG != exttype)) { ptr = STRTOK_R(NULL, "\\", &strtokptr); /* get the nodeflags field */ assert(NULL != ptr); rec->jrec_set_kill.mumps_node.nodeflags = asc2i((uchar_ptr_t)ptr, STRLEN(ptr)); } ptr += (strlen(ptr) + 1); /* get the key-value and data also; can't use strtok since there might be '\\' in the subscript */ assert(NULL != ptr); if ((MUEXT_ZTWORM != exttype) && (MUEXT_LGTRIG != exttype)) { assert(IS_SET_KILL_ZKILL_ZTRIG(rectype)); len = STRLEN(ptr); val_off = ptr; keylength = zwrkeyvallen(ptr, len, &val_off, &val_len, NULL, NULL); /* determine length of key */ REPL_DPRINT2("ext2jnl source:KEY=DATA:%s\n", ptr); assert(keylength <= len); str2gvkey_nogvfunc(ptr, keylength, gv_currkey); rec->jrec_set_kill.mumps_node.length = gv_currkey->end; memcpy(rec->jrec_set_kill.mumps_node.text, gv_currkey->base, gv_currkey->end); temp_reclen = (int)(FIXED_UPD_RECLEN + rec->jrec_set_kill.mumps_node.length + SIZEOF(jnl_str_len_t)); if (IS_KILL_ZKILL_ZTRIG(rectype)) { temp_reclen += JREC_SUFFIX_SIZE; reclen = ROUND_UP2(temp_reclen, JNL_REC_START_BNDRY); memset((char_ptr_t)rec + temp_reclen - JREC_SUFFIX_SIZE, 0, reclen - temp_reclen); suffix = (jrec_suffix *)((char_ptr_t)rec + reclen - JREC_SUFFIX_SIZE); rec->prefix.forwptr = suffix->backptr = reclen; suffix->suffix_code = JNL_REC_SUFFIX_CODE; return (char_ptr_t)rec + reclen; } /* we have to get the data value now */ src.len = val_len; src.addr = val_off; } else { /* ZTWORMHOLE or LGTRIG jnl record */ assert(IS_ZTWORM(rectype) || IS_LGTRIG(rectype)); src.addr = ptr; src.len = STRLEN(ptr); assert(FIXED_ZTWORM_RECLEN == FIXED_LGTRIG_RECLEN); temp_reclen = (int)(FIXED_ZTWORM_RECLEN); } des.len = 0; des.addr = (char_ptr_t)rec + temp_reclen + SIZEOF(jnl_str_len_t); REPL_DPRINT3("ext2jnl JNL Format (before zwr2format): src : Len %d :: DATA:%s\n", src.len, src.addr); REPL_DPRINT3("ext2jnl JNL Format (before zwr2format): des : Len %d :: DATA:%s\n", des.len, des.addr); if (!zwr2format(&src, &des)) { assert(FALSE); return (char_ptr_t)rec; } REPL_DPRINT3("ext2jnl JNL Format : src : Len %d :: DATA:%s\n", src.len, src.addr); REPL_DPRINT3("ext2jnl JNL Format : des : Len %d :: DATA:%s\n", des.len, des.addr); PUT_MSTR_LEN((char_ptr_t)rec + temp_reclen, des.len); temp_reclen += SIZEOF(jnl_str_len_t) + des.len + JREC_SUFFIX_SIZE; reclen = ROUND_UP2(temp_reclen, JNL_REC_START_BNDRY); memset((char_ptr_t)rec + temp_reclen - JREC_SUFFIX_SIZE, 0, reclen - temp_reclen); suffix = (jrec_suffix *)((char_ptr_t)rec + reclen - JREC_SUFFIX_SIZE); rec->prefix.forwptr = suffix->backptr = reclen; suffix->suffix_code = JNL_REC_SUFFIX_CODE; return (char_ptr_t)rec + reclen; } fis-gtm-V7.0-005/sr_port/extern_func.c0000755000032200000250000000601114342376331016530 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2017 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" error_def(ERR_RTNNAME); /* Maximum size of external routine reference of the form label^routine */ #define MAX_EXTREF (2 * MAX_MIDENT_LEN + STR_LIT_LEN("^")) /* compiler parse to AVT module for external functions ($&) */ int extern_func(oprtype *a) { boolean_t have_ident; char *extref; int cnt, actcnt; mstr extentry, package; oprtype *nxtopr; triple *calltrip, *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(TK_AMPERSAND == TREF(window_token)); advancewindow(); cnt = 0; extref = ((TREF(source_buffer)).addr + TREF(last_source_column) - 1); package.len = 0; package.addr = NULL; if (have_ident = (TK_IDENT == TREF(window_token))) /* NOTE assignment */ { if (TK_PERIOD == TREF(director_token)) { /* if ident is a package reference, then take it off */ package.addr = extref; package.len = INTCAST(TREF(lexical_ptr) - extref - 1); extref = TREF(lexical_ptr); advancewindow(); /* get to . */ advancewindow(); /* to next token */ if (have_ident = (TK_IDENT == TREF(window_token))) /* NOTE assignment */ advancewindow(); } else advancewindow(); } if (TK_CIRCUMFLEX == TREF(window_token)) { advancewindow(); if (TK_IDENT == TREF(window_token)) { have_ident = TRUE; advancewindow(); } } if (!have_ident) { stx_error(ERR_RTNNAME); return FALSE; } extentry.len = INTCAST((TREF(source_buffer)).addr + TREF(last_source_column) - 1 - extref); extentry.len = INTCAST(extentry.len > MAX_EXTREF ? MAX_EXTREF : extentry.len); extentry.addr = extref; calltrip = maketriple(a ? OC_FNFGNCAL : OC_FGNCAL); nxtopr = &calltrip->operand[1]; ref = newtriple(OC_PARAMETER); ref->operand[0] = put_str(package.addr, package.len); *nxtopr = put_tref(ref); nxtopr = &ref->operand[1]; cnt++; ref = newtriple(OC_PARAMETER); ref->operand[0] = put_str(extentry.addr, extentry.len); *nxtopr = put_tref(ref); nxtopr = &ref->operand[1]; cnt++; if (TK_LPAREN != TREF(window_token)) { ref = newtriple(OC_PARAMETER); ref->operand[0] = put_ilit(0); *nxtopr = put_tref(ref); nxtopr = &ref->operand[1]; cnt++; ref = newtriple(OC_PARAMETER); ref->operand[0] = put_ilit(0); *nxtopr = put_tref(ref); nxtopr = &ref->operand[1]; cnt++; } else { if (!(actcnt = actuallist(nxtopr))) return FALSE; cnt += actcnt; } cnt++; /* dst mval, or 0 */ calltrip->operand[0] = put_ilit(cnt); ins_triple(calltrip); if (a) *a = put_tref(calltrip); return TRUE; } fis-gtm-V7.0-005/sr_port/f_ascii.c0000755000032200000250000000410714342376331015611 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "compiler.h" #include "toktyp.h" #include "advancewindow.h" #include "op.h" #include "opcode.h" #include "gtm_utf8.h" #include "mdq.h" GBLREF boolean_t gtm_utf8_mode; int f_ascii(oprtype *a, opctype op) { triple *r; mval tmp_mval; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) return FALSE; if (TK_COMMA != TREF(window_token)) r->operand[1] = put_ilit(1); else { advancewindow(); if (EXPR_FAIL == expr(&(r->operand[1]), MUMPS_INT)) return FALSE; } if ((OC_LIT == r->operand[0].oprval.tref->opcode) && (OC_ILIT == r->operand[1].oprval.tref->opcode) && (!gtm_utf8_mode || valid_utf_string(&r->operand[0].oprval.tref->operand[0].oprval.mlit->v.str))) { if (OC_FNASCII == r->opcode) { op_fnascii(r->operand[1].oprval.tref->operand[0].oprval.ilit, &r->operand[0].oprval.tref->operand[0].oprval.mlit->v, &tmp_mval); } else { op_fnzascii(r->operand[1].oprval.tref->operand[0].oprval.ilit, &r->operand[0].oprval.tref->operand[0].oprval.mlit->v, &tmp_mval); } r->operand[1].oprval.tref->operand[0].oprclass = NO_REF; r->operand[1].oprval.tref->opcode = OC_NOOP; /* sideline abandoned triples */ unuse_literal(&r->operand[0].oprval.tref->operand[0].oprval.mlit->v); /* knock out abandoned use of a literal */ r->operand[0].oprval.tref->operand[0].oprclass = NO_REF; r->operand[0].oprval.tref->opcode = OC_NOOP; *a = put_lit(&tmp_mval); a->oprval.tref->src = r->src; return TRUE; } ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_data.c0000755000032200000250000000340314342376331015430 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mdq.h" #include "fullbool.h" error_def(ERR_VAREXPECTED); int f_data(oprtype *a, opctype op) { triple *oldchain, *r; save_se save_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(OC_FNDATA == op || OC_FNZDATA == op); r = maketriple(op); switch (TREF(window_token)) { case TK_IDENT: if (!lvn(&(r->operand[0]), OC_SRCHINDX, 0)) return FALSE; ins_triple(r); break; case TK_CIRCUMFLEX: if (!gvn()) return FALSE; r->opcode = OC_GVDATA; ins_triple(r); break; case TK_ATSIGN: if (SHIFT_SIDE_EFFECTS) { START_GVBIND_CHAIN(&save_state, oldchain); if (!indirection(&(r->operand[0]))) { setcurtchain(oldchain); return FALSE; } r->operand[1] = put_ilit((mint)(OC_FNDATA == op ? indir_fndata : indir_fnzdata)); ins_triple(r); PLACE_GVBIND_CHAIN(&save_state, oldchain); } else { if (!indirection(&(r->operand[0]))) return FALSE; r->operand[1] = put_ilit((mint)(OC_FNDATA == op ? indir_fndata : indir_fnzdata)); ins_triple(r); } r->opcode = OC_INDFUN; break; default: stx_error(ERR_VAREXPECTED); return FALSE; } *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_extract.c0000755000032200000250000000702214342376331016172 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "compiler.h" #include "mmemory.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" #include "op.h" #include "gtm_utf8.h" #include "xfer_enum.h" #include "stringpool.h" #if defined(__ia64) #define OP_FNEXTRACT op_fnextract2 #else #define OP_FNEXTRACT op_fnextract #endif GBLREF boolean_t gtm_utf8_mode; /* $EXTRACT, $ZEXTRACT, and $ZSUBSTR use this compiler routine as all have similar function and identical invocation signatures */ int f_extract(oprtype *a, opctype op) { triple *first, *last, *r; mval tmp_mval; oprtype *newop; static mstr scratch_space = {0, 0, 0}; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) return FALSE; first = newtriple(OC_PARAMETER); last = newtriple(OC_PARAMETER); r->operand[1] = put_tref(first); first->operand[1] = put_tref(last); if (TK_COMMA != TREF(window_token)) { first->operand[0] = put_ilit(1); last->operand[0] = put_ilit((OC_FNZSUBSTR == op) ? MAXPOSINT4 : 1); } else { advancewindow(); if (EXPR_FAIL == expr(&(first->operand[0]), MUMPS_INT)) return FALSE; if (TK_COMMA != TREF(window_token)) last->operand[0] = (OC_FNZSUBSTR == op) ? put_ilit(MAXPOSINT4) : first->operand[0]; else { advancewindow(); if (EXPR_FAIL == expr(&(last->operand[0]), MUMPS_INT)) return FALSE; } } /* This code tries to execute $EXTRACT at compile time if all parameters are literals */ if ((OC_LIT == r->operand[0].oprval.tref->opcode) && (OC_ILIT == first->operand[0].oprval.tref->opcode) && (OC_ILIT == last->operand[0].oprval.tref->opcode) && (!gtm_utf8_mode || valid_utf_string(&r->operand[0].oprval.tref->operand[0].oprval.mlit->v.str))) { /* We don't know how much space we will use; but we know it will be <= the size of the current string */ if (scratch_space.len < r->operand[0].oprval.tref->operand[0].oprval.mlit->v.str.len) { if (scratch_space.addr != 0) free(scratch_space.addr); scratch_space.addr = malloc(r->operand[0].oprval.tref->operand[0].oprval.mlit->v.str.len); scratch_space.len = r->operand[0].oprval.tref->operand[0].oprval.mlit->v.str.len; } tmp_mval.str.addr = scratch_space.addr; if ((OC_FNEXTRACT == op) || (OC_FNZEXTRACT == op)) { OP_FNEXTRACT(last->operand[0].oprval.tref->operand[0].oprval.ilit, first->operand[0].oprval.tref->operand[0].oprval.ilit, &r->operand[0].oprval.tref->operand[0].oprval.mlit->v, &tmp_mval); } else { assert(OC_FNZSUBSTR == op); op_fnzsubstr(&r->operand[0].oprval.tref->operand[0].oprval.mlit->v, first->operand[0].oprval.tref->operand[0].oprval.ilit, last->operand[0].oprval.tref->operand[0].oprval.ilit, &tmp_mval); } s2pool(&tmp_mval.str); newop = (oprtype *)mcalloc(SIZEOF(oprtype)); *newop = put_lit(&tmp_mval); /* Copies mval so stack var tmp_mval not an issue */ assert(TRIP_REF == newop->oprclass); newop->oprval.tref->src = r->src; *a = put_tref(newop->oprval.tref); return TRUE; } ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_find.c0000755000032200000250000000250714342376331015443 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" error_def(ERR_COMMA); int f_find(oprtype *a, opctype op) { triple *delimiter, *r, *start; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) return FALSE; if (TK_COMMA != TREF(window_token)) { stx_error(ERR_COMMA); return FALSE; } advancewindow(); delimiter = newtriple(OC_PARAMETER); start = newtriple(OC_PARAMETER); r->operand[1] = put_tref(delimiter); delimiter->operand[1] = put_tref(start); if (EXPR_FAIL == expr(&(delimiter->operand[0]), MUMPS_STR)) return FALSE; if (TK_COMMA != TREF(window_token)) start->operand[0] = put_ilit(1); else { advancewindow(); if (EXPR_FAIL == expr(&(start->operand[0]), MUMPS_INT)) return FALSE; } ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_fnumber.c0000755000032200000250000000272514342376331016163 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "compiler.h" #include "opcode.h" #include "toktyp.h" #include "mdq.h" #include "advancewindow.h" error_def(ERR_COMMA); int f_fnumber(oprtype *a, opctype op) { triple *r, *ref, *ref1; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&r->operand[0], MUMPS_NUM)) return FALSE; if (TK_COMMA != TREF(window_token)) { stx_error(ERR_COMMA); return FALSE; } advancewindow(); ref = newtriple(OC_PARAMETER); r->operand[1] = put_tref(ref); if (EXPR_FAIL == expr(&ref->operand[0], MUMPS_STR)) return FALSE; ref1 = newtriple(OC_PARAMETER); ref->operand[1] = put_tref(ref1); if (TK_COMMA == TREF(window_token)) { advancewindow(); if (EXPR_FAIL == expr(&ref1->operand[1], MUMPS_INT)) return FALSE; ref1->operand[0] = put_ilit((mint)(1)); /* flag that the 3rd argument is real */ } else ref1->operand[0] = ref1->operand[1] = put_ilit((mint)0); /* flag no 3rd argument and give it default value */ ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_fnzbitfind.c0000755000032200000250000000243714342376331016662 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" error_def(ERR_COMMA); int f_fnzbitfind(oprtype *a, opctype op) { triple *r, *parm; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_EXPR)) /* bitstring */ return FALSE; if (TK_COMMA != TREF(window_token)) { stx_error(ERR_COMMA); return FALSE; } parm = newtriple(OC_PARAMETER); r->operand[1] = put_tref(parm); advancewindow(); if (EXPR_FAIL == expr(&(parm->operand[0]), MUMPS_INT)) /* truthval */ return FALSE; if (TK_COMMA != TREF(window_token)) parm->operand[1] = put_ilit(1); else { advancewindow(); if (EXPR_FAIL == expr(&(parm->operand[1]), MUMPS_INT)) /* position */ return FALSE; } ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_fnzbitget.c0000755000032200000250000000177314342376331016523 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "toktyp.h" #include "advancewindow.h" error_def(ERR_COMMA); int f_fnzbitget(oprtype *a, opctype op) { triple *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_EXPR)) /* bitstring */ return FALSE; if (TK_COMMA != TREF(window_token)) { stx_error(ERR_COMMA); return FALSE; } advancewindow(); if (EXPR_FAIL == expr(&(r->operand[1]), MUMPS_INT)) /* position */ return FALSE; ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_fnzbitset.c0000755000032200000250000000243314342376331016531 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" error_def(ERR_COMMA); int f_fnzbitset(oprtype *a, opctype op) { triple *r, *parm; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_EXPR)) /* bitstring */ return FALSE; if (TK_COMMA != TREF(window_token)) { stx_error(ERR_COMMA); return FALSE; } parm = newtriple(OC_PARAMETER); r->operand[1] = put_tref(parm); advancewindow(); if (EXPR_FAIL == expr(&(parm->operand[0]), MUMPS_INT)) /* position */ return FALSE; if (TK_COMMA != TREF(window_token)) { stx_error(ERR_COMMA); return FALSE; } advancewindow(); if (EXPR_FAIL == expr(&(parm->operand[1]), MUMPS_INT)) /* truthval */ return FALSE; ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_fnzbitstr.c0000755000032200000250000000174514342376331016553 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "toktyp.h" #include "advancewindow.h" int f_fnzbitstr(oprtype *a, opctype op) { triple *r, *parm; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_INT)) /* size */ return FALSE; if (TK_COMMA != TREF(window_token)) r->operand[1] = put_ilit(0); else { advancewindow(); if (EXPR_FAIL == expr(&(r->operand[1]), MUMPS_INT)) /* position */ return FALSE; } ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_get.c0000755000032200000250000000635514342376331015307 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mmemory.h" #include "mdq.h" #include "advancewindow.h" #include "fullbool.h" #include "glvn_pool.h" #include "show_source_line.h" GBLREF boolean_t run_time; GBLREF int source_column; error_def(ERR_SIDEEFFECTEVAL); error_def(ERR_VAREXPECTED); int f_get(oprtype *a, opctype op) { boolean_t ok, used_glvn_slot; oprtype control_slot, def_opr, *def_oprptr, indir; short int column; triple *oldchain, *opptr, *r, *triptr; save_se save_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; oldchain = NULL; used_glvn_slot = FALSE; r = maketriple(OC_NOOP); /* We'll fill in the opcode later, when we figure out what it is */ switch (TREF(window_token)) { case TK_IDENT: r->opcode = OC_FNGET; ok = lvn(&r->operand[0], OC_SRCHINDX, 0); break; case TK_CIRCUMFLEX: r->opcode = OC_FNGVGET; ok = gvn(); break; case TK_ATSIGN: r->opcode = OC_INDGET2; if (SHIFT_SIDE_EFFECTS) START_GVBIND_CHAIN(&save_state, oldchain); if (ok = indirection(&indir)) /* NOTE: assignment */ { used_glvn_slot = TRUE; INSERT_INDSAVGLVN(control_slot, indir, ANY_SLOT, 1); r->operand[0] = control_slot; } break; default: ok = FALSE; break; } if (!ok) { if (NULL != oldchain) setcurtchain(oldchain); stx_error(ERR_VAREXPECTED); return FALSE; } opptr = r; ins_triple(r); if (used_glvn_slot) { /* house cleaning for the indirection */ triptr = newtriple(OC_GLVNPOP); triptr->operand[0] = control_slot; } if (TK_COMMA == TREF(window_token)) { /* two argument form with a specified default value */ advancewindow(); column = source_column; def_oprptr = (oprtype *)mcalloc(SIZEOF(oprtype)); def_opr = put_indr(def_oprptr); DISABLE_SIDE_EFFECT_AT_DEPTH; /* doing this here let's us know specifically if direction had SE threat */ if (EXPR_FAIL == expr(def_oprptr, MUMPS_EXPR)) { if (NULL != oldchain) setcurtchain(oldchain); return FALSE; } if (SE_WARN_ON && (TREF(side_effect_base))[TREF(expr_depth)]) ISSUE_SIDEEFFECTEVAL_WARNING(column - 1); if (OC_FNGET == r->opcode) r->opcode = OC_FNGET1; else if (OC_FNGVGET == r->opcode) r->opcode = OC_FNGVGET1; else assert(OC_INDGET2 == r->opcode); r = newtriple(OC_FNGET2); r->operand[0] = put_tref(opptr); r->operand[1] = def_opr; } else if (OC_INDGET2 == r->opcode) { /* indirect always acts like a two argument form so force that along with an empty string default */ r = newtriple(OC_FNGET2); r->operand[0] = put_tref(opptr); r->operand[1] = put_str(0, 0); } if (NULL != oldchain) PLACE_GVBIND_CHAIN(&save_state, oldchain); /* shift chain back to "expr_start" */ *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_get1.c0000644000032200000250000000344614342376331015363 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2020 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_string.h" #include "compiler.h" #include "toktyp.h" #include "opcode.h" #include "indir_enum.h" #include "mdq.h" #include "op.h" #include "fullbool.h" error_def(ERR_VAREXPECTED); int f_get1(oprtype *a, opctype op) { triple *oldchain, *r; save_se save_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(OC_NOOP); /* We'll fill in the opcode later, when we figure out what it is */ switch (TREF(window_token)) { case TK_IDENT: r->opcode = OC_FNGET1; if (!lvn(&r->operand[0], OC_SRCHINDX, 0)) return FALSE; break; case TK_CIRCUMFLEX: r->opcode = OC_FNGVGET1; if (!gvn()) return FALSE; break; case TK_ATSIGN: r->opcode = OC_INDFUN; r->operand[1] = put_ilit((mint)indir_get); if (SHIFT_SIDE_EFFECTS) { /* with short-circuited booleans move indirect processing to expr_start */ START_GVBIND_CHAIN(&save_state, oldchain); if (!indirection(&r->operand[0])) { setcurtchain(oldchain); return FALSE; } ins_triple(r); PLACE_GVBIND_CHAIN(&save_state, oldchain); *a = put_tref(r); return TRUE; } if (!indirection(&(r->operand[0]))) return FALSE; break; default: stx_error(ERR_VAREXPECTED); return FALSE; } ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_incr.c0000755000032200000250000001165214342376331015457 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mdq.h" #include "advancewindow.h" #include "fullbool.h" #include "show_source_line.h" error_def(ERR_VAREXPECTED); int f_incr(oprtype *a, opctype op) { boolean_t ok; oprtype *increment; triple incrchain, *oldchain, *r, *savptr, targchain, tmpexpr, *triptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); /* may need to evaluate the increment (2nd arg) early and use result later: prepare to juggle triple chains */ dqinit(&targchain, exorder); /* a place for the operation and the target */ dqinit(&tmpexpr, exorder); /* a place to juggle the shifted chain in case it's active */ triptr = maketriple(OC_NOOP); dqrins(&tmpexpr, exorder, triptr); triptr = TREF(expr_start); savptr = TREF(expr_start_orig); /* but make sure expr_start_orig == expr_start since this is a new chain */ assert(&tmpexpr != tmpexpr.exorder.bl); TREF(expr_start_orig) = TREF(expr_start) = &tmpexpr; oldchain = setcurtchain(&targchain); /* save the result of the first argument 'cause it evaluates 2nd */ switch (TREF(window_token)) { case TK_IDENT: /* $INCREMENT() performs an implicit $GET() on a first argument lvn so we use OC_PUTINDX because * we know only at runtime whether to signal an UNDEF error (depending on whether we have * VIEW "NOUNDEF" or "UNDEF" state; op_putindx creates the local variable unconditionally, even if * we have "UNDEF" state, in which case any error in op_fnincr causes an op_kill of that local variable */ ok = (lvn(&(r->operand[0]), OC_PUTINDX, 0)); break; case TK_CIRCUMFLEX: ok = gvn(); r->opcode = OC_GVINCR; r->operand[0] = put_ilit(0); /* dummy fill since emit_code does not like empty operand[0] */ break; case TK_ATSIGN: ok = indirection(&r->operand[0]); r->opcode = OC_INDINCR; break; default: ok = FALSE; break; } if (!ok) { setcurtchain(oldchain); return FALSE; } TREF(expr_start) = triptr; /* restore original shift chain */ TREF(expr_start_orig) = savptr; increment = &r->operand[1]; if (TK_COMMA != TREF(window_token)) *increment = put_ilit(1); /* default optional increment to 1 */ else { dqinit(&incrchain, exorder); /* a place for the increment */ setcurtchain(&incrchain); /* increment expr must evaluate before the glvn in $INCR(glvn,expr) */ advancewindow(); if (EXPR_FAIL == expr(increment, MUMPS_NUM)) { setcurtchain(oldchain); return FALSE; } setcurtchain(&targchain); dqadd(&targchain, &incrchain, exorder); /* dir before targ - this is a violation of info hiding */ } coerce(increment, OCT_MVAL); ins_triple(r); if (&tmpexpr != tmpexpr.exorder.bl->exorder.bl) { /* one or more OC_GVNAME may have shifted so add to the end of the shift chain */ assert(TREF(shift_side_effects)); assert(&tmpexpr != tmpexpr.exorder.bl); dqadd(TREF(expr_start), &tmpexpr, exorder); /* this is a violation of info hiding */ TREF(expr_start) = tmpexpr.exorder.bl; if (OC_GVSAVTARG == (TREF(expr_start))->opcode) { triptr = newtriple(OC_GVRECTARG); /* restore result of last gvn to preserve $reference (the naked) */ triptr->operand[0] = put_tref(TREF(expr_start)); } else assert(OC_NOOP == (TREF(expr_start))->opcode); } if (!TREF(shift_side_effects) || (GTM_BOOL != TREF(gtm_fullbool)) || (OC_INDINCR != r->opcode) || (NULL == TREF(expr_start))) { /* put it on the end of the main chain as there's no reason to play more with the ordering */ setcurtchain(oldchain); triptr = (TREF(curtchain))->exorder.bl; dqadd(triptr, &targchain, exorder); /* this is a violation of info hiding */ } else /* need full side effects or indirect 1st argument so put everything on the shift chain */ { /* add the chain after "expr_start" which may be much before "curtchain" */ newtriple(OC_GVSAVTARG); setcurtchain(oldchain); assert(NULL != TREF(expr_start)); assert(&targchain != targchain.exorder.bl); dqadd(TREF(expr_start), &targchain, exorder); /* this is a violation of info hiding */ TREF(expr_start) = targchain.exorder.bl; assert(OC_GVSAVTARG == (TREF(expr_start))->opcode); triptr = newtriple(OC_GVRECTARG); /* restore the result of the last gvn to preserve $referece (the naked) */ triptr->operand[0] = put_tref(TREF(expr_start)); } /* $increment() args need to avoid side effect processing but that's handled in expritem so eval_expr gets $i()'s SE flag */ *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_justify.c0000755000032200000250000000234714342376331016222 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" error_def(ERR_COMMA); int f_justify(oprtype *a, opctype op) { triple *r, *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&r->operand[0], MUMPS_STR)) return FALSE; if (TK_COMMA != TREF(window_token)) { stx_error(ERR_COMMA); return FALSE; } advancewindow(); if (EXPR_FAIL == expr(&r->operand[1], MUMPS_INT)) return FALSE; if (TK_COMMA == TREF(window_token)) { r->opcode = OC_FNJ3; ref = newtriple(OC_PARAMETER); ref->operand[0] = r->operand[1]; r->operand[1] = put_tref(ref); advancewindow(); if (EXPR_FAIL == expr(&ref->operand[1], MUMPS_INT)) return FALSE; } ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_length.c0000755000032200000250000000211414342376331015776 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" int f_length(oprtype *a, opctype op) { triple *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert((OC_FNLENGTH == op) || (OC_FNZLENGTH == op)); r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) return FALSE; if (TK_COMMA == TREF(window_token)) { advancewindow(); r->opcode = (OC_FNLENGTH == op) ? OC_FNPOPULATION : OC_FNZPOPULATION; /* Not good information hiding */ if (EXPR_FAIL == expr(&(r->operand[1]), MUMPS_STR)) return FALSE; } ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_mint.c0000755000032200000250000000125314342376331015467 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" int f_mint(oprtype *a, opctype op) { triple *r; r = maketriple(op); if (EXPR_FAIL == expr(&r->operand[0], MUMPS_INT)) return FALSE; ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_mint_mstr.c0000755000032200000250000000174214342376331016537 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" error_def(ERR_COMMA); int f_mint_mstr(oprtype *a, opctype op) { triple *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&r->operand[0], MUMPS_INT)) return FALSE; if (TK_COMMA != TREF(window_token)) { stx_error(ERR_COMMA); return FALSE; } advancewindow(); if (EXPR_FAIL == expr(&r->operand[1], MUMPS_STR)) return FALSE; ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_mstr.c0000755000032200000250000000125314342376331015505 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" int f_mstr(oprtype *a, opctype op) { triple *r; r = maketriple(op); if (EXPR_FAIL == expr(&r->operand[0], MUMPS_STR)) return FALSE; ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_name.c0000755000032200000250000000473014342376331015443 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "indir_enum.h" #include "advancewindow.h" #include "subscript.h" #include "fullbool.h" #include "show_source_line.h" GBLREF boolean_t run_time; GBLREF int source_column; error_def(ERR_SIDEEFFECTEVAL); error_def(ERR_VAREXPECTED); int f_name(oprtype *a, opctype op) { boolean_t gbl; oprtype *depth; short int column; triple *r, *s; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); gbl = FALSE; switch (TREF(window_token)) { case TK_CIRCUMFLEX: gbl = TRUE; advancewindow(); /* caution fall through */ case TK_IDENT: if (!name_glvn(gbl, &r->operand[1])) return FALSE; depth = &r->operand[0]; break; case TK_ATSIGN: r->opcode = OC_INDFNNAME2; /* chomps extra subscripts of resulting string */ s = maketriple(OC_INDFNNAME); if (!indirection(&(s->operand[0]))) return FALSE; s->operand[1] = put_ilit(MAX_LVSUBSCRIPTS + 1); /* first, get all the subscripts. r will chomp them */ coerce(&s->operand[1], OCT_MVAL); ins_triple(s); depth = &r->operand[0]; r->operand[1] = put_tref(s); break; default: stx_error(ERR_VAREXPECTED); return FALSE; } /* allow for optional default value */ if (TK_COMMA != TREF(window_token)) { *depth = put_ilit(MAX_LVSUBSCRIPTS + 1); /* default to maximum number of subscripts allowed by law */ /* ideally this should be MAX(MAX_LVSUBSCRIPTS, MAX_GVSUBSCRIPTS) but they are the same so take the easy path */ assert(MAX_LVSUBSCRIPTS == MAX_GVSUBSCRIPTS); /* add assert to ensure our assumption is valid */ } else { DISABLE_SIDE_EFFECT_AT_DEPTH; /* doing this here let's us know specifically if direction had SE threat */ advancewindow(); column = source_column; if (EXPR_FAIL == expr(depth, MUMPS_STR)) return FALSE; if (SE_WARN_ON && (OC_INDFNNAME2 == r->opcode)) ISSUE_SIDEEFFECTEVAL_WARNING(column - 1); } coerce(depth, OCT_MVAL); ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_next.c0000755000032200000250000000341414342376331015477 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mdq.h" #include "fullbool.h" error_def(ERR_LVORDERARG); error_def(ERR_VAREXPECTED); int f_next(oprtype *a, opctype op) { triple *oldchain, *r, *r1; save_se save_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); switch (TREF(window_token)) { case TK_IDENT: if (TK_LPAREN != TREF(director_token)) { stx_error(ERR_LVORDERARG); return FALSE; } if (!lvn(&(r->operand[0]), OC_SRCHINDX,r)) return FALSE; ins_triple(r); break; case TK_CIRCUMFLEX: if (!gvn()) return FALSE; r->opcode = OC_GVNEXT; ins_triple(r); break; case TK_ATSIGN: if (SHIFT_SIDE_EFFECTS) { START_GVBIND_CHAIN(&save_state, oldchain); if (!indirection(&(r->operand[0]))) { setcurtchain(oldchain); return FALSE; } r->operand[1] = put_ilit((mint)indir_fnnext); ins_triple(r); PLACE_GVBIND_CHAIN(&save_state, oldchain); } else { if (!indirection(&(r->operand[0]))) return FALSE; r->operand[1] = put_ilit((mint)indir_fnnext); ins_triple(r); } r->opcode = OC_INDFUN; break; default: stx_error(ERR_VAREXPECTED); return FALSE; } *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_one_mval.c0000755000032200000250000000136614342376331016325 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "toktyp.h" int f_one_mval(oprtype *a, opctype op) { triple *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_EXPR)) return FALSE; ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_order.c0000755000032200000250000001723514342376331015642 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "fnorder.h" #include "mdq.h" #include "mmemory.h" #include "advancewindow.h" #include "mvalconv.h" #include "fullbool.h" #include "glvn_pool.h" #include "show_source_line.h" GBLREF boolean_t run_time; GBLREF int source_column; error_def(ERR_ORDER2); error_def(ERR_SIDEEFFECTEVAL); error_def(ERR_VAREXPECTED); LITDEF opctype order_opc[LAST_OBJECT][LAST_DIRECTION] = { /* FORWARD BACKWARD TBD */ { OC_GVORDER, OC_ZPREVIOUS, OC_GVO2 }, /* GLOBAL */ { OC_FNORDER, OC_FNZPREVIOUS, OC_FNO2 }, /* LOCAL */ { OC_FNLVNAME, OC_FNLVPRVNAME, OC_FNLVNAMEO2 }, /* LOCAL_NAME */ { OC_INDFUN, OC_INDFUN, OC_INDO2 } /* INDIRECT */ }; int f_order(oprtype *a, opctype op) { boolean_t ok, used_glvn_slot; enum order_dir direction; enum order_obj object; int4 intval; opctype gv_oc; oprtype control_slot, dir_opr, *dir_oprptr, *next_oprptr; short int column; triple *oldchain, *r, *sav_dirref, *sav_gv1, *sav_gvn, *sav_lvn, *sav_ref, *share, *triptr; triple *chain2, *obp, tmpchain2; save_se save_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; oldchain = sav_dirref = NULL; /* default to no direction and no shifting indirection */ used_glvn_slot = FALSE; r = maketriple(OC_NOOP); /* We'll fill in the opcode later, when we figure out what it is */ switch (TREF(window_token)) { case TK_IDENT: if (TK_LPAREN == TREF(director_token)) { object = LOCAL; ok = lvn(&r->operand[0], OC_SRCHINDX, r); /* 2nd arg causes us to mess below with return from lvn */ } else { object = LOCAL_NAME; ok = TRUE; r->operand[0] = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); advancewindow(); } next_oprptr = &r->operand[1]; break; case TK_CIRCUMFLEX: object = GLOBAL; sav_gv1 = TREF(curtchain); ok = gvn(); sav_gvn = (TREF(curtchain))->exorder.bl; if (OC_GVRECTARG == sav_gvn->opcode) { /* because of shifting if we need to find it, look in the expr_start chain */ assert(TREF(shift_side_effects)); assert(((sav_gvn->operand[0].oprval.tref) == TREF(expr_start)) && (NULL != TREF(expr_start_orig))); sav_gv1 = TREF(expr_start_orig); sav_gvn = TREF(expr_start); } next_oprptr = &r->operand[0]; break; case TK_ATSIGN: object = INDIRECT; if (SHIFT_SIDE_EFFECTS) START_GVBIND_CHAIN(&save_state, oldchain); ok = indirection(&r->operand[0]); next_oprptr = &r->operand[1]; break; default: ok = FALSE; break; } if (!ok) { if (NULL != oldchain) setcurtchain(oldchain); stx_error(ERR_VAREXPECTED); return FALSE; } if (TK_COMMA != TREF(window_token)) direction = FORWARD; /* default direction */ else { /* two argument form: ugly logic for direction */ advancewindow(); column = source_column; dir_oprptr = (oprtype *)mcalloc(SIZEOF(oprtype)); dir_opr = put_indr(dir_oprptr); sav_ref = newtriple(OC_GVSAVTARG); DISABLE_SIDE_EFFECT_AT_DEPTH; /* doing this here let's us know specifically if direction had SE threat */ if (EXPR_FAIL == expr(dir_oprptr, MUMPS_EXPR)) { if (NULL != oldchain) setcurtchain(oldchain); return FALSE; } assert(TRIP_REF == dir_oprptr->oprclass); triptr = dir_oprptr->oprval.tref; sav_dirref = newtriple(OC_GVSAVTARG); /* for explicit direction, $R at this point set by lit: 1st or var: 2nd arg */ triptr = newtriple(OC_GVRECTARG); triptr->operand[0] = put_tref(sav_ref); if (OC_LIT == triptr->opcode) { /* if direction is a literal - pick it up and stop flailing about */ if (MV_IS_TRUEINT(&triptr->operand[0].oprval.mlit->v, &intval) && (1 == intval || -1 == intval)) direction = (1 == intval) ? FORWARD : BACKWARD; else { /* bad direction */ if (NULL != oldchain) setcurtchain(oldchain); stx_error(ERR_ORDER2); return FALSE; } } else { direction = TBD; switch (object) { case GLOBAL: /* The direction may have had a side effect, so take copies of subscripts */ *next_oprptr = *dir_oprptr; for (; sav_gvn != sav_gv1; sav_gvn = sav_gvn->exorder.bl) { /* hunt down the gv opcode */ gv_oc = sav_gvn->opcode; if ((OC_GVNAME == gv_oc) || (OC_GVNAKED == gv_oc) || (OC_GVEXTNAM == gv_oc)) break; } assert((OC_GVNAME == gv_oc) || (OC_GVNAKED == gv_oc) || (OC_GVEXTNAM == gv_oc)); TREF(temp_subs) = TRUE; create_temporaries(sav_gvn, gv_oc); break; case LOCAL: /* Additionally need to move srchindx triple to after potential side effect */ triptr = newtriple(OC_PARAMETER); triptr->operand[0] = *next_oprptr; triptr->operand[1] = *(&dir_opr); *next_oprptr = put_tref(triptr); sav_lvn = r->operand[0].oprval.tref; assert((OC_SRCHINDX == sav_lvn->opcode) || (OC_VAR == sav_lvn->opcode)); if (OC_SRCHINDX == sav_lvn->opcode) { dqdel(sav_lvn, exorder); ins_triple(sav_lvn); TREF(temp_subs) = TRUE; create_temporaries(sav_lvn, OC_SRCHINDX); } assert(&r->operand[1] == next_oprptr); assert(TRIP_REF == next_oprptr->oprclass); assert(OC_PARAMETER == next_oprptr->oprval.tref->opcode); assert(TRIP_REF == next_oprptr->oprval.tref->operand[0].oprclass); sav_lvn = next_oprptr->oprval.tref->operand[0].oprval.tref; if ((OC_VAR == sav_lvn->opcode) || (OC_GETINDX == sav_lvn->opcode)) { /* lvn excludes the last subscript from srchindx and attaches it to the "parent" * now we find it is an lvn and needs protection too */ triptr = maketriple(OC_STOTEMP); triptr->operand[0] = put_tref(sav_lvn); dqins(sav_lvn, exorder, triptr); /* NOTE: violation of info hiding */ next_oprptr->oprval.tref->operand[0].oprval.tref = triptr; } break; case INDIRECT: /* Save and restore the variable lookup for true left-to-right evaluation */ *next_oprptr = *dir_oprptr; used_glvn_slot = TRUE; dqinit(&tmpchain2, exorder); chain2 = setcurtchain(&tmpchain2); INSERT_INDSAVGLVN(control_slot, r->operand[0], ANY_SLOT, 1); setcurtchain(chain2); obp = sav_ref->exorder.bl; /* insert before second arg */ dqadd(obp, &tmpchain2, exorder); r->operand[0] = control_slot; break; case LOCAL_NAME: /* left argument is a string - side effect can't screw it up */ *next_oprptr = *dir_oprptr; break; default: assert(FALSE); } ins_triple(r); if (used_glvn_slot) { triptr = newtriple(OC_GLVNPOP); triptr->operand[0] = control_slot; } if (SE_WARN_ON && (TREF(side_effect_base))[TREF(expr_depth)]) ISSUE_SIDEEFFECTEVAL_WARNING(column - 1); DISABLE_SIDE_EFFECT_AT_DEPTH; /* usual side effect processing doesn't work for $ORDER() */ } } if (TBD != direction) ins_triple(r); if (NULL != sav_dirref) { triptr = newtriple(OC_GVRECTARG); triptr->operand[0] = put_tref(sav_dirref); } r->opcode = order_opc[object][direction]; /* finally - the op code */ if (NULL != oldchain) PLACE_GVBIND_CHAIN(&save_state, oldchain); /* shift chain back to "expr_start" */ if (OC_FNLVNAME == r->opcode) *next_oprptr = put_ilit(0); /* Flag not to return aliases with no value */ if (OC_INDFUN == r->opcode) *next_oprptr = put_ilit((mint)((FORWARD == direction) ? indir_fnorder1 : indir_fnzprevious)); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_order1.c0000755000032200000250000000372414342376331015721 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mdq.h" #include "advancewindow.h" #include "fullbool.h" error_def(ERR_VAREXPECTED); int f_order1(oprtype *a, opctype op) { triple *oldchain, *r; save_se save_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); switch (TREF(window_token)) { case TK_IDENT: if (TK_LPAREN != TREF(director_token)) { r->opcode = OC_FNLVNAME; r->operand[0] = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); r->operand[1] = put_ilit(0); /* FALSE - do not return aliased vars with no value */ ins_triple(r); advancewindow(); break; } if (!lvn(&(r->operand[0]), OC_SRCHINDX, r)) return FALSE; ins_triple(r); break; case TK_CIRCUMFLEX: if (!gvn()) return FALSE; r->opcode = OC_GVORDER; ins_triple(r); break; case TK_ATSIGN: if (SHIFT_SIDE_EFFECTS) { START_GVBIND_CHAIN(&save_state, oldchain); if (!indirection(&(r->operand[0]))) { setcurtchain(oldchain); return FALSE; } r->operand[1] = put_ilit((mint)indir_fnorder1); ins_triple(r); PLACE_GVBIND_CHAIN(&save_state, oldchain); } else { if (!indirection(&(r->operand[0]))) return FALSE; r->operand[1] = put_ilit((mint)indir_fnorder1); ins_triple(r); } r->opcode = OC_INDFUN; break; default: stx_error(ERR_VAREXPECTED); return FALSE; } *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_qlength.c0000755000032200000250000000141014342376331016155 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" int f_qlength(oprtype *a, opctype op) { triple *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) return FALSE; ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_qsubscript.c0000755000032200000250000000166214342376331016723 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" int f_qsubscript(oprtype *a, opctype op) { triple *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) return FALSE; if (TK_COMMA != TREF(window_token)) return FALSE; advancewindow(); if (EXPR_FAIL == expr(&(r->operand[1]), MUMPS_INT)) return FALSE; ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_query.c0000755000032200000250000000636014342376331015671 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mdq.h" #include "fullbool.h" error_def(ERR_VAREXPECTED); int f_query(oprtype *a, opctype op) { triple *oldchain, *r, *r0, *r1; save_se save_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TK_IDENT == TREF(window_token)) { if (!lvn(a, OC_FNQUERY, 0)) return FALSE; assert(TRIP_REF == a->oprclass); if (OC_FNQUERY == a->oprval.tref->opcode) { assert(OC_FNQUERY == a->oprval.tref->opcode); assert(TRIP_REF == a->oprval.tref->operand[0].oprclass); assert(OC_ILIT == a->oprval.tref->operand[0].oprval.tref->opcode); assert(ILIT_REF == a->oprval.tref->operand[0].oprval.tref->operand[0].oprclass); assert(0 < a->oprval.tref->operand[0].oprval.tref->operand[0].oprval.ilit); a->oprval.tref->operand[0].oprval.tref->operand[0].oprval.ilit += 2; assert(TRIP_REF == a->oprval.tref->operand[1].oprclass); assert(OC_PARAMETER == a->oprval.tref->operand[1].oprval.tref->opcode); assert(TRIP_REF == a->oprval.tref->operand[1].oprval.tref->operand[0].oprclass); r0 = a->oprval.tref->operand[1].oprval.tref->operand[0].oprval.tref; assert(OC_VAR == r0->opcode); assert(MVAR_REF == r0->operand[0].oprclass); r1 = maketriple(OC_PARAMETER); r1->operand[0] = put_str(r0->operand[0].oprval.vref->mvname.addr, r0->operand[0].oprval.vref->mvname.len); r1->operand[1] = a->oprval.tref->operand[1]; a->oprval.tref->operand[1] = put_tref (r1); dqins (a->oprval.tref->exorder.fl, exorder, r1); } else { assert(OC_VAR == a->oprval.tref->opcode); r0 = newtriple(OC_FNQUERY); r0->operand[0] = put_ilit (3); r0->operand[1] = put_tref(newtriple(OC_PARAMETER)); r0->operand[1].oprval.tref->operand[0] = put_str(a->oprval.tref->operand[0].oprval.vref->mvname.addr, a->oprval.tref->operand[0].oprval.vref->mvname.len); r1 = r0->operand[1].oprval.tref; r1->operand[1] = *a; *a = put_tref (r0); } } else { r = maketriple(op); switch (TREF(window_token)) { case TK_CIRCUMFLEX: if (!gvn()) return FALSE; r->opcode = OC_GVQUERY; ins_triple(r); break; case TK_ATSIGN: if (SHIFT_SIDE_EFFECTS) { START_GVBIND_CHAIN(&save_state, oldchain); if (!indirection(&(r->operand[0]))) { setcurtchain(oldchain); return FALSE; } r->operand[1] = put_ilit((mint)indir_fnquery); ins_triple(r); PLACE_GVBIND_CHAIN(&save_state, oldchain); } else { if (!indirection(&(r->operand[0]))) return FALSE; r->operand[1] = put_ilit((mint)indir_fnquery); ins_triple(r); } r->opcode = OC_INDFUN; break; default: stx_error(ERR_VAREXPECTED); return FALSE; } *a = put_tref(r); } return TRUE; } fis-gtm-V7.0-005/sr_port/f_reverse.c0000755000032200000250000000142014342376331016167 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" /* #include "opcode.h" */ #include "toktyp.h" int f_reverse(oprtype *a, opctype op) { triple *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) return FALSE; ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_select.c0000644000032200000250000002634414342376331016004 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "compiler.h" #include "opcode.h" #include "toktyp.h" #include "mdq.h" #include "mmemory.h" #include "advancewindow.h" #include "fullbool.h" #include "stringpool.h" #include "gtmdbglvl.h" GBLREF triple t_orig; GBLREF uint4 gtmDebugLevel; error_def(ERR_COLON); error_def(ERR_SELECTFALSE); LITREF octabstruct oc_tab[]; typedef struct _save_for_select { triple *expr_start; triple *expr_start_orig; boolean_t shift_side_effects; boolean_t saw_side_effect; boolean_t *side_effect_base; uint4 expr_depth; uint4 side_effect_depth; } save_for_select; #define SELECT_CLEANUP \ MBSTART { \ free(TREF(side_effect_base)); \ TREF(side_effect_base) = save_state->side_effect_base; \ TREF(side_effect_depth) = save_state->side_effect_depth; \ TREF(expr_depth) = save_state->expr_depth; \ TREF(saw_side_effect) = save_state->saw_side_effect; \ TREF(shift_side_effects) = save_state->shift_side_effects; \ TREF(expr_start_orig) = save_state->expr_start_orig; \ TREF(expr_start) = save_state->expr_start; \ } MBEND int f_select(oprtype *a, opctype op) /* drive parsing for the $select function * a is an operand the caller places to access the result * op is actuallly an OC_PASSTHRU to anchor the list of STO'd of values which Boolean controlled jumps navigate * the return is TRUE for success and FALSE for a failure */ { boolean_t first_time, got_true, se_saw_side, shifting, throwing; opctype old_op; oprtype *cnd, endtrip, target, tmparg; triple dmpchain, *loop_expr_start, *oldchain, *r, *ref, *savechain, tmpchain, *triptr; mval *v; save_for_select *save_state, ss; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # ifdef DEBUG if (GDL_DebugCompiler & gtmDebugLevel) CHKTCHAIN(TREF(curtchain), exorder, (NULL != TREF(expr_start))); # endif save_state = &ss; save_state->expr_start = TREF(expr_start); save_state->expr_start_orig = TREF(expr_start_orig); save_state->shift_side_effects = TREF(shift_side_effects); if ((save_state->shift_side_effects) && (GTM_BOOL != TREF(gtm_fullbool))) TREF(saw_side_effect) = TRUE; /* this will stop shifting of side effects in FULL_BOOL mode */ save_state->saw_side_effect = TREF(saw_side_effect); save_state->expr_depth = TREF(expr_depth); save_state->side_effect_base = TREF(side_effect_base); save_state->side_effect_depth = TREF(side_effect_depth); se_saw_side = FALSE; TREF(expr_depth) = 0; TREF(side_effect_depth) = INITIAL_SIDE_EFFECT_DEPTH; TREF(side_effect_base) = malloc(SIZEOF(boolean_t) * TREF(side_effect_depth)); memset((char *)(TREF(side_effect_base)), 0, SIZEOF(boolean_t) * TREF(side_effect_depth)); if (shifting = (save_state->shift_side_effects) && (NULL != save_state->expr_start) && ((!save_state->saw_side_effect) || (GTM_BOOL == TREF(gtm_fullbool)))) { /* shift in progress; WARNING assignment above */ TREF(expr_depth) = 1; /* Don't want to hit bottom with each expression, so start at 1 rather than 0 */ dqinit(&tmpchain, exorder); oldchain = setcurtchain(&tmpchain); } r = maketriple(op); endtrip = put_tjmp(r); first_time = TRUE; got_true = throwing = FALSE; savechain = NULL; for (;;) { cnd = (oprtype *)mcalloc(SIZEOF(oprtype)); if (shifting) { /* aleady preparing to shift everything, so no need for additional juggling */ TREF(expr_start) = TREF(expr_start_orig) = NULL; TREF(shift_side_effects) = TREF(saw_side_effect) = FALSE; } loop_expr_start = TREF(expr_start); if (!bool_expr(FALSE, cnd)) /* process a Boolean */ { /* bad Boolean */ SELECT_CLEANUP; if (shifting) setcurtchain(oldchain); else if ((NULL != savechain) && (((TREF(curtchain)) != &t_orig) || (!ALREADY_RTERROR))) setcurtchain(savechain); /* error means return to original chain */ return FALSE; } if (TK_COLON != TREF(window_token)) /* next comes a colon */ { /* syntax problem */ SELECT_CLEANUP; if (shifting) setcurtchain(oldchain); else if ((NULL != savechain) && (((TREF(curtchain)) != &t_orig) || (!ALREADY_RTERROR))) setcurtchain(savechain); /* error means return to original chain */ stx_error(ERR_COLON); return FALSE; } advancewindow(); /* past the colon */ assert(!got_true || (&dmpchain == TREF(curtchain))); for (triptr = (TREF(curtchain))->exorder.bl; !got_true; triptr = triptr->exorder.bl) { /* get back, get back to where we once belonged - to find an indicator of the actual result */ if (OC_NOOP == triptr->opcode) continue; /* keep looking */ if (OC_LIT != triptr->opcode) break; /* Boolean was not a literal */ v = &triptr->operand[0].oprval.mlit->v; /* Boolean was a literal, so optimize it */ dqdel(triptr, exorder); dqinit(&dmpchain, exorder); /* both got_true and throwing use dumping */ unuse_literal(v); if (0 == MV_FORCE_BOOL(v)) { /* Boolean FALSE: discard the corresponding value */ assert(NULL == savechain); savechain = setcurtchain(&dmpchain); throwing = TRUE; } else { /* Boolean TRUE: take this argument and disregard any following arguments */ assert(!throwing && (NULL == savechain)); got_true = TRUE; } break; } TREF(shift_side_effects) = TREF(saw_side_effect) = FALSE; TREF(expr_start) = TREF(expr_start_orig) = NULL; /* FALSE may bypass expr, so, again, discourage shifting */ if (EXPR_FAIL == expr(&tmparg, MUMPS_EXPR)) /* now a corresponding value */ { /* bad expression */ SELECT_CLEANUP; if (shifting) setcurtchain(oldchain); else if ((NULL != savechain) && (((TREF(curtchain)) != &t_orig) || (!ALREADY_RTERROR))) setcurtchain(savechain); /* error means return to original chain */ return FALSE; } assert(TRIP_REF == tmparg.oprclass); if (throwing) { /* finished a useless argument so see what's next */ TREF(expr_start) = loop_expr_start; if (!got_true) { /* discarded arg with a literal FALSE; stop throwing as later arguments may have value */ setcurtchain(savechain); DEBUG_ONLY(savechain = NULL;); throwing = FALSE; } /* even if no more arguments (there's usually 1 TRUE), do above to keep uniform state management */ if (TK_COMMA != TREF(window_token)) break; advancewindow(); continue; } assert(!throwing && (&dmpchain != TREF(curtchain))); old_op = tmparg.oprval.tref->opcode; if (first_time) { /* setup differs */ if (got_true && (OC_LIT == old_op)) { /* if the value is also a literal, turn the OC_PASSTHRU into the return value */ assert((OC_PASSTHRU == r->opcode) && (NO_REF == r->operand[0].oprclass)); r = tmparg.oprval.tref; } else { /* build a home for the result */ if ((OC_LIT == old_op) || (OC_FNTEXT == old_op) || (OCT_MVADDR & oc_tab[old_op].octype)) { /* need a temp for these - OP_FNTEXT because it may later become an OC_LIT */ ref = newtriple(OC_STOTEMP); ref->operand[0] = tmparg; tmparg = put_tref(ref); } r->operand[0] = target = tmparg; } first_time = FALSE; } else { /* add to the list of possible results */ assert(OC_PASSTHRU == r->opcode); ref = newtriple(OC_STO); ref->operand[0] = target; ref->operand[1] = tmparg; if (OC_PASSTHRU == old_op) { assert(TRIP_REF == tmparg.oprval.tref->operand[0].oprclass); ref = newtriple(OC_STO); ref->operand[0] = target; ref->operand[1] = put_tref(tmparg.oprval.tref->operand[0].oprval.tref); } } if (TK_COMMA != TREF(window_token)) break; /* argument list end */ advancewindow(); if (got_true) { /* Boolean literal TRUE; now we have the value, start throwing out upcoming useless arguments */ assert((&dmpchain == dmpchain.exorder.fl) && (&dmpchain == dmpchain.exorder.bl)); assert(NULL == savechain); savechain = setcurtchain(&dmpchain); /* discard arguments after a compile time TRUE */ loop_expr_start = TREF(expr_start); TREF(expr_start) = TREF(expr_start_orig) = NULL; throwing = TRUE; continue; } if (OC_PASSTHRU == r->opcode) { /* Not the case where the 1st argument has both a literal Boolean and a literal result */ ref = newtriple(OC_JMP); /* jump to end in case the value turns out to be (the first) TRUE */ ref->operand[0] = endtrip; tnxtarg(cnd); } } if (got_true) { /* FALSE throwing cleans up after itself */ if (throwing) { /* if we might have discarded things, return the chains to normal */ setcurtchain(savechain); TREF(expr_start) = loop_expr_start; } } else { /* if we didn't find a TRUE at compile time, then insert a possible error in case there's no TRUE at run time */ assert(!throwing); if (!first_time) { /* if we ended with a runtime evaluation make sure it has its jump */ ref = newtriple(OC_JMP); /* jump to end in case the value turns out to be (the first) TRUE */ ref->operand[0] = endtrip; tnxtarg(cnd); } else { /* if all values were literals and FALSE, supply a dummy evaluation so we reach the error gracefully */ PUT_LITERAL_TRUTH(FALSE, r); r->opcode = OC_LIT; ins_triple(r); } tmparg = put_ilit(ERR_SELECTFALSE); ref = newtriple(OC_RTERROR); ref->operand[0] = tmparg; ref->operand[1] = put_ilit(FALSE); /* flag as not a subroutine reference */ } if (OC_PASSTHRU == r->opcode) ins_triple(r); /* 1st arg was not literal:literal */ se_saw_side = TREF(saw_side_effect); /* note this down before it gets reset by DECREMENT_EXPR_DEPTH */ if (shifting) DECREMENT_EXPR_DEPTH; /* clean up */ assert(!TREF(expr_depth)); save_state->saw_side_effect |= se_saw_side; /* this & next feed state to evaluations containing this $select */ save_state->side_effect_base[save_state->expr_depth] |= (TREF(side_effect_base))[TREF(expr_depth)]; if (shifting) { /* get the tmpchain into the shiftchain and add OC_GVSAVTARG / OC_GVRECTARG */ shifting = (((1 < save_state->expr_depth) && TREF(saw_side_effect)) /*see if something abandoned shift */ || ((save_state->expr_start != save_state->expr_start_orig) && (OC_NOOP != save_state->expr_start->opcode))); SELECT_CLEANUP; assert(&tmpchain == TREF(curtchain)); newtriple(shifting ? OC_GVSAVTARG : OC_NOOP); /* must have one of these two at expr_start */ setcurtchain(oldchain); assert(NULL != save_state->expr_start); TREF(expr_start) = save_state->expr_start; assert(&t_orig != TREF(expr_start)); dqadd(TREF(expr_start), &tmpchain, exorder); TREF(expr_start) = tmpchain.exorder.bl; if (shifting) { /* only play this game if it has not been abandoned */ assert(OC_GVSAVTARG == (TREF(expr_start))->opcode); triptr = newtriple(OC_GVRECTARG); triptr->operand[0] = put_tref(TREF(expr_start)); } # ifdef DEBUG if (GDL_DebugCompiler & gtmDebugLevel) { CHKTCHAIN(TREF(curtchain), exorder, TRUE); CHKTCHAIN(TREF(expr_start_orig), exorder, FALSE); } # endif } else SELECT_CLEANUP; CHKTCHAIN(TREF(curtchain), exorder, (NULL != TREF(expr_start))); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_stack.c0000755000032200000250000000176214342376331015632 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" int f_stack(oprtype *a, opctype op) { triple *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_INT)) return FALSE; if (TK_COMMA == TREF(window_token)) { advancewindow(); r->opcode = OC_FNSTACK2; /*This isn't very good information hiding*/ if (EXPR_FAIL == expr(&(r->operand[1]), MUMPS_STR)) return FALSE; } ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_text.c0000755000032200000250000001240414342376331015504 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "cmd_qlf.h" #include "advancewindow.h" #include "gtm_caseconv.h" #include "gtm_string.h" #include "gtm_common_defs.h" static readonly mstr zero_mstr; GBLREF boolean_t run_time; GBLREF command_qualifier cmd_qlf; GBLREF mident routine_name; STATICDEF char *suppressed_values[] = {"GTM$DMOD", "GTM$CI"}; error_def(ERR_RPARENMISSING); error_def(ERR_RTNNAME); error_def(ERR_TEXTARG); int f_text(oprtype *a, opctype op) { char *c; int implicit_offset = 0, len; triple *label, *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); switch (TREF(window_token)) { case TK_CIRCUMFLEX: implicit_offset = 1; /* CAUTION - fall-through */ case TK_PLUS: r->operand[0] = put_str(zero_mstr.addr, 0); /* Null label - top of routine */ break; case TK_INTLIT: int_label(); /* CAUTION - fall through */ case TK_IDENT: if (!(cmd_qlf.qlf & CQ_LOWER_LABELS)) lower_to_upper((uchar_ptr_t)(TREF(window_ident)).addr, (uchar_ptr_t)(TREF(window_ident)).addr, (TREF(window_ident)).len); r->operand[0] = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); advancewindow(); break; case TK_ATSIGN: if (!indirection(&(r->operand[0]))) return FALSE; r->opcode = OC_INDTEXT; break; default: stx_error(ERR_TEXTARG); return FALSE; } /* The assert below can be useful when working on $TEXT parsing issues but causes problems in debug builds with * bad syntax asserts. Hence it is normally commented out. Uncomment to re-enable for $TEXT parsing issues. */ /* assert((TK_PLUS == TREF(window_token)) || (TK_CIRCUMFLEX == TREF(window_token)) || (TK_RPAREN == TREF(window_token)) * || (TK_EOL == TREF(window_token))); */ if ((OC_INDTEXT != r->opcode) || (TK_PLUS == TREF(window_token)) || (TK_CIRCUMFLEX == TREF(window_token))) { /* Need another parm chained in to deal with offset and routine name except for the case where an * indirect specifies the entire argument. */ label = newtriple(OC_PARAMETER); r->operand[1] = put_tref(label); } if (TK_PLUS != TREF(window_token)) { if ((OC_INDTEXT != r->opcode) || (TK_CIRCUMFLEX == TREF(window_token))) /* Set default offset (0 or 1 as computed above) when offset not specified */ label->operand[0] = put_ilit(implicit_offset); else { /* Fill in indirect text for case where indirect specifies entire operand */ r->opcode = OC_INDFUN; r->operand[1] = put_ilit((mint)indir_fntext); } } else { /* Process offset */ advancewindow(); if (EXPR_FAIL == expr(&(label->operand[0]), MUMPS_INT)) return FALSE; } if (TK_CIRCUMFLEX != TREF(window_token)) { /* No routine specified - default to current routine */ if (OC_INDFUN != r->opcode) label->operand[1] = PUT_CURRENT_RTN; /* tell op_fntext to pick up current routine version */ } else { /* Routine has been specified - pull it */ advancewindow(); switch (TREF(window_token)) { case TK_IDENT: # ifdef GTM_TRIGGER if (TK_HASH == TREF(director_token)) /* Coagulate tokens as necessary (and available) to allow '#' in the routine name */ advwindw_hash_in_mname_allowed(); # endif if (TK_DOLLAR == TREF(director_token)) /* the item has a $ in it */ { /* violate information hiding to special case illegal names GT.M can return from $STACK() et al */ c = TREF(lexical_ptr) - STR_LIT_LEN("GTM$"); advancewindow(); /* parse to $ */ if (0 == memcmp(c, "GTM$", STR_LIT_LEN("GTM$"))) { /* parse past GTM$DMOD or GTM$CI to prevent RPARENMISSING error */ advancewindow(); /* parse to end of ident */ len = TREF(lexical_ptr) - c - (TK_EOL == TREF(director_token) ? 0 : 1); for (implicit_offset = 0; ARRAYSIZE(suppressed_values) > implicit_offset; implicit_offset++) { /* reuse of implicit_offset */ if ((STRLEN(suppressed_values[implicit_offset]) == len) && (0 == memcmp(c, suppressed_values[implicit_offset], len))) { label->operand[1] = put_str(suppressed_values[implicit_offset], len); break; } } if (ARRAYSIZE(suppressed_values) == implicit_offset) (TREF(last_source_column))--; /* if no match (error) adjust for extra parse */ } else implicit_offset = ARRAYSIZE(suppressed_values); if (ARRAYSIZE(suppressed_values) == implicit_offset) { /* give the error that would arise had we just ignored the $ */ stx_error(ERR_RPARENMISSING); return FALSE; } } else label->operand[1] = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); advancewindow(); break; case TK_ATSIGN: if (!indirection(&label->operand[1])) return FALSE; r->opcode = OC_INDTEXT; break; default: stx_error(ERR_RTNNAME); return FALSE; } } ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_translate.c0000755000032200000250000001607314342376331016523 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "gtm_string.h" #include "mdef.h" #include "compiler.h" #include "mdq.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" #include "stringpool.h" #include "min_max.h" #include "mmemory.h" #include "op.h" #ifdef UTF8_SUPPORTED #include "hashtab_int4.h" #include "hashtab.h" #include "gtm_utf8.h" #endif GBLREF boolean_t badchar_inhibit, gtm_utf8_mode; LITREF mval literal_null; int f_translate(oprtype *a, opctype op) { boolean_t more_args; hash_table_int4 *xlate_hash; int4 i, maxLengthString; mval dst_mval, *rplc_mval, *srch_mval, *xlateTable[2]; sm_uc_ptr_t nextptr; triple *args[4]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; args[0] = maketriple(op); if (EXPR_FAIL == expr(&(args[0]->operand[0]), MUMPS_EXPR)) return FALSE; for (i = 1 , more_args = TRUE ; i < 3 ; i++) { args[i] = newtriple(OC_PARAMETER); if (more_args) { if (TK_COMMA != TREF(window_token)) more_args = FALSE; else { advancewindow(); if (EXPR_FAIL == expr(&(args[i]->operand[0]), MUMPS_EXPR)) return FALSE; } } if (!more_args) args[i]->operand[0] = put_lit((mval *)&literal_null); args[i - 1]->operand[1] = put_tref(args[i]); } assert((TRIP_REF == args[0]->operand[1].oprclass) && (TRIP_REF == args[0]->operand[1].oprval.tref->operand[0].oprclass) && (TRIP_REF == args[0]->operand[1].oprval.tref->operand[1].oprclass)); /* If the second and third parameters are literals, pre-calculate the translation table and store it in the stringpool */ if ((OC_LIT == args[1]->operand[0].oprval.tref->opcode) && (OC_LIT == args[2]->operand[0].oprval.tref->opcode)) { /* we only do this if we have search and reolace literals */ srch_mval = &args[1]->operand[0].oprval.tref->operand[0].oprval.mlit->v; rplc_mval = &args[2]->operand[0].oprval.tref->operand[0].oprval.mlit->v; assert(MV_STR & srch_mval->mvtype); assert(MV_STR & rplc_mval->mvtype); xlateTable[0] = (mval *)mcalloc(SIZEOF(mval)); xlateTable[0]->mvtype = 0; /* so stp_gcol, which may be invoked below by ENSURE..., does not get confused */ maxLengthString = (NUM_CHARS * SIZEOF(int4)); if (!gtm_utf8_mode || (OC_FNZTRANSLATE == op)) { /* just doing bytes - no need for a hash table */ if (OC_LIT == args[0]->operand[0].oprval.tref->opcode) /* if lit src, dst can't be longer */ maxLengthString += args[0]->operand[0].oprval.tref->operand[0].oprval.mlit->v.str.len; ENSURE_STP_FREE_SPACE(maxLengthString); xlateTable[0]->str.addr = (char *)stringpool.free; xlateTable[0]->mvtype = MV_STR; xlateTable[0]->str.len = NUM_CHARS * SIZEOF(int4); stringpool.free += NUM_CHARS * SIZEOF(int4); create_byte_xlate_table(srch_mval, rplc_mval, (int *)xlateTable[0]->str.addr); if (OC_LIT == args[0]->operand[0].oprval.tref->opcode) { /* the source is a literal too - lets do it all at compile time */ op_fnztranslate_fast(&args[0]->operand[0].oprval.tref->operand[0].oprval.mlit->v, xlateTable[0], &dst_mval); unuse_literal(&args[0]->operand[0].oprval.tref->operand[0].oprval.mlit->v); dqdel(args[0]->operand[0].oprval.tref, exorder); args[0]->opcode = OC_LIT; put_lit_s(&dst_mval, args[0]); args[0]->operand[1].oprclass = NO_REF; } else { args[0]->opcode = OC_FNZTRANSLATE_FAST; args[0]->operand[1] = put_lit(xlateTable[0]); } unuse_literal(&args[1]->operand[0].oprval.tref->operand[0].oprval.mlit->v); unuse_literal(&args[2]->operand[0].oprval.tref->operand[0].oprval.mlit->v); dqdel(args[1]->operand[0].oprval.tref, exorder); dqdel(args[2]->operand[0].oprval.tref, exorder); dqdel(args[1], exorder); dqdel(args[2], exorder); } else if (gtm_utf8_mode && valid_utf_string(&srch_mval->str) && valid_utf_string(&rplc_mval->str)) { /* actual UTF-8 characters, so need hashtable rather than just than code table */ unuse_literal(&args[1]->operand[0].oprval.tref->operand[0].oprval.mlit->v); if (!badchar_inhibit) MV_FORCE_LEN(srch_mval); /* needed only to validate for BADCHARs */ else MV_FORCE_LEN_SILENT(srch_mval); /* but need some sorta valid length */ maxLengthString = xlateTable[0]->str.len = MAX(srch_mval->str.char_len, maxLengthString); if (OC_LIT == args[0]->operand[0].oprval.tref->opcode) /* if lit src, dst can't be longer */ maxLengthString /* because compile puts hash in stp */ += (args[0]->operand[0].oprval.tref->operand[0].oprval.mlit->v.str.len * MAX_CHAR_LEN); ENSURE_STP_FREE_SPACE(maxLengthString); xlateTable[0]->str.addr = (char *)stringpool.free; xlateTable[0]->mvtype = MV_STR; stringpool.free += xlateTable[0]->str.len; xlate_hash = create_utf8_xlate_table(srch_mval, rplc_mval, &xlateTable[0]->str); if (NULL != xlate_hash) { nextptr = copy_hashtab_to_buffer_int4(xlate_hash, stringpool.free, NULL); xlateTable[1] = (mval *)mcalloc(SIZEOF(mval)); xlateTable[1]->str.addr = (char *)stringpool.free; xlateTable[1]->mvtype = MV_STR; xlateTable[1]->str.len = nextptr - stringpool.free; stringpool.free = nextptr; } else xlateTable[1] = (mval *)&literal_null; if ((OC_LIT == args[0]->operand[0].oprval.tref->opcode) && valid_utf_string(&args[0]->operand[0].oprval.tref->operand[0].oprval.mlit->v.str)) { /* the source is a literal too - lets do it all at compile time */ op_fntranslate_fast(&args[0]->operand[0].oprval.tref->operand[0].oprval.mlit->v, rplc_mval, xlateTable[0], xlateTable[1], &dst_mval); unuse_literal(&args[0]->operand[0].oprval.tref->operand[0].oprval.mlit->v); dqdel(args[0]->operand[0].oprval.tref, exorder); args[0]->opcode = OC_LIT; put_lit_s(&dst_mval, args[0]); args[0]->operand[1].oprclass = NO_REF; unuse_literal(&args[2]->operand[0].oprval.tref->operand[0].oprval.mlit->v); dqdel(args[1]->operand[0].oprval.tref, exorder); dqdel(args[2]->operand[0].oprval.tref, exorder); dqdel(args[1], exorder); dqdel(args[2], exorder); } else { /* op_fntranslate_fast arguments; src, rplc, m_xlate, xlate_hash, so need one more triple */ args[0]->opcode = OC_FNTRANSLATE_FAST; /* note no Z */ assert(OC_PARAMETER == args[1]->opcode); args[1]->operand[0] = args[2]->operand[0]; /* Promote the rplc string to the second argument */ assert(OC_PARAMETER == args[2]->opcode); args[2]->operand[0] = put_lit(xlateTable[0]); args[3] = newtriple(OC_PARAMETER); args[3]->operand[0] = put_lit(xlateTable[1]); /* bind up triple structure */ args[0]->operand[1] = put_tref(args[1]); args[1]->operand[1] = put_tref(args[2]); args[2]->operand[1] = put_tref(args[3]); } } } ins_triple(args[0]); *a = put_tref(args[0]); return TRUE; } fis-gtm-V7.0-005/sr_port/f_two_mstrs.c0000755000032200000250000000174214342376331016564 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" error_def(ERR_COMMA); int f_two_mstrs(oprtype *a, opctype op) { triple *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&r->operand[0], MUMPS_STR)) return FALSE; if (TK_COMMA != TREF(window_token)) { stx_error(ERR_COMMA); return FALSE; } advancewindow(); if (EXPR_FAIL == expr(&r->operand[1], MUMPS_STR)) return FALSE; ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_two_mval.c0000755000032200000250000000172314342376331016352 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "toktyp.h" #include "advancewindow.h" error_def(ERR_COMMA); int f_two_mval(oprtype *a, opctype op) { triple *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_EXPR)) return FALSE; if (TK_COMMA != TREF(window_token)) { stx_error(ERR_COMMA); return FALSE; } advancewindow(); if (EXPR_FAIL == expr(&(r->operand[1]), MUMPS_EXPR)) return FALSE; ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_view.c0000755000032200000250000000271314342376331015474 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" error_def(ERR_FCHARMAXARGS); int f_view(oprtype *a, opctype op) { int argc; oprtype *argp, argv[CHARMAXARGS]; triple *curr, *last, *root; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; argp = &argv[0]; argc = 0; if (EXPR_FAIL == expr(argp, MUMPS_EXPR)) return FALSE; assert(TRIP_REF == argp->oprclass); argc++; argp++; for (;;) { if (TK_COMMA != TREF(window_token)) break; advancewindow(); if (EXPR_FAIL == expr(argp, MUMPS_EXPR)) return FALSE; assert(TRIP_REF == argp->oprclass); argc++; argp++; if (argc >= CHARMAXARGS - 1) { stx_error(ERR_FCHARMAXARGS); return FALSE; } } root = last = maketriple(op); root->operand[0] = put_ilit(argc + 1); argp = &argv[0]; for (; argc > 0 ;argc--, argp++) { curr = newtriple(OC_PARAMETER); curr->operand[0] = *argp; last->operand[1] = put_tref(curr); last = curr; } ins_triple(root); *a = put_tref(root); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zahandle.c0000644000032200000250000000322214342376331016301 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mdq.h" #include "fullbool.h" error_def(ERR_NAMEEXPECTED); error_def(ERR_VAREXPECTED); int f_zahandle(oprtype *a, opctype op) { triple *oldchain, *r; save_se save_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); switch (TREF(window_token)) { case TK_IDENT: if (!lvn(&(r->operand[0]), OC_GETINDX, 0)) return FALSE; ins_triple(r); break; case TK_CIRCUMFLEX: stx_error(ERR_NAMEEXPECTED); return FALSE; case TK_ATSIGN: if (SHIFT_SIDE_EFFECTS) { START_GVBIND_CHAIN(&save_state, oldchain); if (!indirection(&(r->operand[0]))) { setcurtchain(oldchain); return FALSE; } r->operand[1] = put_ilit((mint)indir_fnzahandle); ins_triple(r); PLACE_GVBIND_CHAIN(&save_state, oldchain); } else { if (!indirection(&(r->operand[0]))) return FALSE; r->operand[1] = put_ilit((mint)indir_fnzahandle); ins_triple(r); } r->opcode = OC_INDFUN; break; default: stx_error(ERR_VAREXPECTED); return FALSE; } *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zatransform.c0000644000032200000250000000505514342376331017067 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2016 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "stringpool.h" #include "advancewindow.h" /* $ZATRANSFORM(strexp, int1, int2) * Convert an MVAL to it's GDS internal collated form for comparison using either the follows or sorts-after operators * * Has the following input parameters: * * expr - REQUIRED - an expression containing valid MVAL or an MVAL in database internal key format (GDS). * int1 - REQUIRED - collation algorithm index. * int2 - OPTIONAL - if not specified or 0 translation is from MVAL to GDS. Else, translation is from GDS to * MVAL. * int3 - OPTIONAL - if not specified or 0 treat the a canonical numeric strexpr as if it were a numeric * subscript; else treat it only as a string * * Return value - the converted string * * Note this function is meant as a proxy to mval2subs to convert a string to/from an internal subscript representation * Note that if the collation isn't available, we raise a COLLATIONUNDEF error. */ int f_zatransform(oprtype *a, opctype op) { triple *gvn, *coll, *optional; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; gvn = maketriple(op); if (EXPR_FAIL == expr(&(gvn->operand[0]), MUMPS_STR)) return FALSE; if (TK_COMMA != TREF(window_token)) return FALSE; advancewindow(); /* 2nd parameter (required) */ coll = newtriple(OC_PARAMETER); gvn->operand[1] = put_tref(coll); if (EXPR_FAIL == expr(&(coll->operand[0]), MUMPS_INT)) return FALSE; /* 3rd parameter (optional), defaults to 0 */ optional = newtriple(OC_PARAMETER); coll->operand[1] = put_tref(optional); if (TK_COMMA != TREF(window_token)) optional->operand[0] = put_ilit(0); else { advancewindow(); if (EXPR_FAIL == expr(&(optional->operand[0]), MUMPS_INT)) return FALSE; } /* 4th parameter (optional), defaults to 0 */ if (TK_COMMA != TREF(window_token)) optional->operand[1] = put_ilit(0); else { advancewindow(); if (EXPR_FAIL == expr(&(optional->operand[1]), MUMPS_INT)) return FALSE; } ins_triple(gvn); *a = put_tref(gvn); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zcall.c0000755000032200000250000000315014342376331015623 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" error_def(ERR_FCHARMAXARGS); int f_zcall(oprtype *a, opctype op) { int argc; oprtype *argp, argv[CHARMAXARGS]; triple *curr,*ref, *last, *root; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; argp = &argv[0]; argc = 0; if (EXPR_FAIL == expr(argp, MUMPS_EXPR)) return FALSE; assert(TRIP_REF == argp->oprclass); argc++; argp++; for (;;) { if (TK_COMMA != TREF(window_token)) break; advancewindow(); if (TK_COMMA == TREF(window_token) || TK_RPAREN == TREF(window_token)) { ref = newtriple(OC_NULLEXP); *argp = put_tref(ref); } else { if (EXPR_FAIL == expr(argp, MUMPS_EXPR)) return FALSE; assert(TRIP_REF == argp->oprclass); } argc++; argp++; if (argc >= CHARMAXARGS) { stx_error(ERR_FCHARMAXARGS); return FALSE; } } root = last = maketriple(op); root->operand[0] = put_ilit(argc + 1); argp = &argv[0]; for (; argc > 0 ;argc--, argp++) { curr = newtriple(OC_PARAMETER); curr->operand[0] = *argp; last->operand[1] = put_tref(curr); last = curr; } ins_triple(root); *a = put_tref(root); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zchar.c0000644000032200000250000000445614342376331015634 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2020 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_string.h" #include "compiler.h" #include "opcode.h" #include "toktyp.h" #include "stringpool.h" #include "gtm_iconv.h" #include "io.h" #include "iosp.h" #include "mdq.h" #ifdef __MVS__ #include "gtm_unistd.h" #endif #include "advancewindow.h" GBLREF spdesc stringpool; error_def(ERR_FCHARMAXARGS); error_def(ERR_TEXT); int f_zchar(oprtype *a, opctype op) { boolean_t all_lits; char *c; unsigned char *tmp_ptr; int argc, i; unsigned int tmp_len; mval v; oprtype argv[CHARMAXARGS], *argp; triple *curr, *last, *root; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; all_lits = TRUE; argp = &argv[0]; argc = 0; for (;;) { if (EXPR_FAIL == expr(argp, MUMPS_INT)) return FALSE; assert(TRIP_REF == argp->oprclass); if (OC_ILIT != argp->oprval.tref->opcode) all_lits = FALSE; argc++; argp++; if (TK_COMMA != TREF(window_token)) break; advancewindow(); if (argc >= CHARMAXARGS) { stx_error(ERR_FCHARMAXARGS); return FALSE; } } if (all_lits) { ENSURE_STP_FREE_SPACE(argc + 1); v.mvtype = MV_STR; v.str.addr = c = (char *)stringpool.free; argp = &argv[0]; for (; argc > 0 ;argc--, argp++) { i = argp->oprval.tref->operand[0].oprval.ilit; if ((i >= 0) && (i < 256)) /* only true for single byte character set */ *c++ = i; dqdel(argp->oprval.tref, exorder); } *c = '\0'; v.str.len = INTCAST(c - v.str.addr); stringpool.free = (unsigned char *)c; CLEAR_MVAL_BITS(&v); *a = put_lit(&v); return TRUE; } root = maketriple(op); root->operand[0] = put_ilit(argc + 1); last = root; argp = &argv[0]; for (; argc > 0; argc--, argp++) { curr = newtriple(OC_PARAMETER); curr->operand[0] = *argp; last->operand[1] = put_tref(curr); last = curr; } ins_triple(root); *a = put_tref(root); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zcollate.c0000644000032200000250000000362614342376331016340 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2016 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "stringpool.h" #include "advancewindow.h" /* $ZCOLLATE(strexp, int1, int2) * * Parameters: * * strexpr - string experssion containing global variable name (GVN) or a GVN in database internal key format (GDS). * int1 - collatio algorithm index. * int2 - if not specified or 0 translation is from GVN to GDS. Else, translation is from GDS to GVN. * * Return value - the converted name. * * Note that GDS is the internal database key format and GVN is a formatted global variable name with subscripts. * Note that if the collation isn't available, we raise a COLLATIONUNDEF error. */ int f_zcollate(oprtype *a, opctype op) { triple *gvn, *coll; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; gvn = maketriple(op); if (EXPR_FAIL == expr(&(gvn->operand[0]), MUMPS_STR)) return FALSE; if (TK_COMMA != TREF(window_token)) return FALSE; advancewindow(); /* 2nd parameter (required) */ coll = newtriple(OC_PARAMETER); gvn->operand[1] = put_tref(coll); if (EXPR_FAIL == expr(&(coll->operand[0]), MUMPS_INT)) return FALSE; /* 3rd parameter (optional), defaults to 0 */ if (TK_COMMA != TREF(window_token)) coll->operand[1] = put_ilit(0); else { advancewindow(); if (EXPR_FAIL == expr(&(coll->operand[1]), MUMPS_INT)) return FALSE; } ins_triple(gvn); *a = put_tref(gvn); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zconvert.c0000755000032200000250000000607414342376331016400 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2006-2018 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 "compiler.h" #ifdef UTF8_SUPPORTED #include "toktyp.h" #include "opcode.h" #include "advancewindow.h" #include "gtm_conv.h" error_def(ERR_BADCASECODE); error_def(ERR_BADCHSET); error_def(ERR_COMMA); /* $ZCONVERT(): 3 parameters (3rd optional) - all are string expressions. * For 2 argument $ZCONVERT, if 2nd argument is a literal, must be one of * "U", "L", or "T" (case independent) or else raise BADCASECODE error. * For 3 argument $ZCONVERT, if 2nd or 3rd arguments are literals, they * must be one of "UTF-8", "UTF-16LE", or "UTF-16BE" (case independent) * or else raise BADCHSET error. */ int f_zconvert(oprtype *a, opctype op) { triple *r, *mode, *mode2; mstr *tmpstr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) return FALSE; if (TK_COMMA != TREF(window_token)) { stx_error(ERR_COMMA); return FALSE; } advancewindow(); /* 2nd parameter (required) */ mode = newtriple(OC_PARAMETER); r->operand[1] = put_tref(mode); if (EXPR_FAIL == expr(&(mode->operand[0]), MUMPS_STR)) return FALSE; /* Check for 3rd parameter */ if (TK_COMMA != TREF(window_token)) { /* 3rd parameter does not exist. Do checks for 2 arument $zconvert */ if (mode->operand[0].oprval.tref->opcode == OC_LIT && -1 == verify_case((tmpstr = &mode->operand[0].oprval.tref->operand[0].oprval.mlit->v.str))) { stx_error(ERR_BADCASECODE, 2, tmpstr->len, tmpstr->addr); return FALSE; } } else { /* 3rd parameter exists .. reel it in after error checking 2nd parm */ r->opcode = OC_FNZCONVERT3; if (mode->operand[0].oprval.tref->opcode == OC_LIT && 0 >= verify_chset((tmpstr = &mode->operand[0].oprval.tref->operand[0].oprval.mlit->v.str))) { stx_error(ERR_BADCHSET, 2, tmpstr->len, tmpstr->addr); return FALSE; } advancewindow(); mode2 = newtriple(OC_PARAMETER); mode->operand[1] = put_tref(mode2); if (EXPR_FAIL == expr(&(mode2->operand[0]), MUMPS_STR)) return FALSE; if (mode2->operand[0].oprval.tref->opcode == OC_LIT && 0 >= verify_chset((tmpstr = &mode2->operand[0].oprval.tref->operand[0].oprval.mlit->v.str))) { stx_error(ERR_BADCHSET, 2, tmpstr->len, tmpstr->addr); return FALSE; } } ins_triple(r); *a = put_tref(r); return TRUE; } #else /* UTF8 is not supported */ int f_zconvert(oprtype *a, opctype op) { GTMASSERT; } #endif fis-gtm-V7.0-005/sr_port/f_zdate.c0000755000032200000250000000247414342376331015635 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" LITREF mval literal_null ; int f_zdate(oprtype *a, opctype op) /* op is not used */ { boolean_t more_args; int i; triple *args[4]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; args[0] = maketriple(OC_FNZDATE); if (EXPR_FAIL == expr(&(args[0]->operand[0]), MUMPS_EXPR)) return FALSE; for (i = 1 , more_args = TRUE ; i < 4 ; i++) { args[i] = newtriple(OC_PARAMETER); if (more_args) { if (TK_COMMA != TREF(window_token)) more_args = FALSE; else { advancewindow(); if (EXPR_FAIL == expr(&(args[i]->operand[0]), MUMPS_EXPR)) return FALSE; } } if (!more_args) args[i]->operand[0] = put_lit((mval *)&literal_null); args[i - 1]->operand[1] = put_tref(args[i]); } ins_triple(args[0]); *a = put_tref(args[0]); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zgetsyi.c0000755000032200000250000000171514342376331016221 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" int f_zgetsyi(oprtype *a, opctype op) { triple *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) return FALSE; if (TK_COMMA != TREF(window_token)) r->operand[1] = put_str("",0); else { advancewindow(); if (EXPR_FAIL == expr(&r->operand[1], MUMPS_STR)) return FALSE; } ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zjobexam.c0000755000032200000250000000277214342376331016346 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" /* $ZJOBEXAM([strexp1[,strexp2]]) * Parameters: * strexp1 - (optional) output device/file specification * strexp2 - (optional) zshow information code specification */ int f_zjobexam(oprtype *a, opctype op) { triple *args[2]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; args[0] = maketriple(op); if (TK_RPAREN == TREF(window_token)) args[0]->operand[0] = put_str("",0); /* No argument specified - default to null */ else if (EXPR_FAIL == expr(&(args[0]->operand[0]), MUMPS_STR)) return FALSE; /* Improper string argument */ /* Look for optional 2nd argument */ args[1] = newtriple(OC_PARAMETER); if (TK_COMMA == TREF(window_token)) { advancewindow(); if (EXPR_FAIL == expr(&(args[1]->operand[0]), MUMPS_STR)) return FALSE; } else args[1]->operand[0] = put_str("",0); args[0]->operand[1] = put_tref(args[1]); ins_triple(args[0]); *a = put_tref(args[0]); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zparse.c0000755000032200000250000000237514342376331016032 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" int f_zparse(oprtype *a, opctype op) { boolean_t again; int i; triple *last, *r, *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; last = r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) return FALSE; again = TRUE; for (i = 0; i < 4 ;i++) { ref = newtriple(OC_PARAMETER); last->operand[1] = put_tref(ref); if (again && TK_COMMA == TREF(window_token)) { advancewindow(); if (TK_COMMA == TREF(window_token)) ref->operand[0] = put_str("", 0); else if (EXPR_FAIL == expr(&ref->operand[0], MUMPS_STR)) return FALSE; } else { again = FALSE; ref->operand[0] = put_str("", 0); } last = ref; } ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zpeek.c0000644000032200000250000000425514342376331015640 0ustar librarygtc/**************************************************************** * * * Copyright 2013 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" error_def(ERR_COMMA); #ifdef UNIX /* Compile 4 parameter $ZPEEK(baseadr,offset,length<,format>) function where: * * structid - A string containing a set of mnemonics that identify the structure to fetch from (see op_fnzpeek.c) * offset - Offset into the block (error if negative). * length - Length to return (error if negative or > MAX_STRLEN). * format - Option parm contains single char formatting code (see op_fnzpeek.c) */ int f_zpeek(oprtype *a, opctype op) { oprtype x; triple *offset, *length, *format, *r; mval mv; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) /* Structure identifier string */ return FALSE; if (TK_COMMA != TREF(window_token)) { stx_error(ERR_COMMA); return FALSE; } advancewindow(); offset = newtriple(OC_PARAMETER); r->operand[1] = put_tref(offset); if (EXPR_FAIL == expr(&(offset->operand[0]), MUMPS_INT)) return FALSE; if (TK_COMMA != TREF(window_token)) { stx_error(ERR_COMMA); return FALSE; } advancewindow(); length = newtriple(OC_PARAMETER); offset->operand[1] = put_tref(length); if (EXPR_FAIL == expr(&(length->operand[0]), MUMPS_INT)) return FALSE; format = newtriple(OC_PARAMETER); length->operand[1] = put_tref(format); if (TK_COMMA != TREF(window_token)) format->operand[0] = put_str("C", 1); /* Default format if none specified */ else { advancewindow(); if (EXPR_FAIL == expr(&(format->operand[0]), MUMPS_STR)) return FALSE; } ins_triple(r); *a = put_tref(r); return TRUE; } #else /* VMS - function not supported here */ int f_zpeek(oprtype *a, opctype op) { GTMASSERT; } #endif fis-gtm-V7.0-005/sr_port/f_zprevious.c0000755000032200000250000000361314342376331016570 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mdq.h" #include "advancewindow.h" #include "fullbool.h" error_def(ERR_VAREXPECTED); int f_zprevious(oprtype *a, opctype op) { triple *oldchain, *r; save_se save_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); switch (TREF(window_token)) { case TK_IDENT: if (TK_LPAREN != TREF(director_token)) { r->opcode = OC_FNLVPRVNAME; r->operand[0] = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); ins_triple(r); advancewindow(); break; } if (!lvn(&(r->operand[0]), OC_SRCHINDX, r)) return FALSE; ins_triple(r); break; case TK_CIRCUMFLEX: if (!gvn()) return FALSE; r->opcode = OC_ZPREVIOUS; ins_triple(r); break; case TK_ATSIGN: if (SHIFT_SIDE_EFFECTS) { START_GVBIND_CHAIN(&save_state, oldchain); if (!indirection(&(r->operand[0]))) { setcurtchain(oldchain); return FALSE; } r->operand[1] = put_ilit((mint)indir_fnzprevious); ins_triple(r); PLACE_GVBIND_CHAIN(&save_state, oldchain); } else { if (!indirection(&(r->operand[0]))) return FALSE; r->operand[1] = put_ilit((mint)indir_fnzprevious); ins_triple(r); } r->opcode = OC_INDFUN; break; default: stx_error(ERR_VAREXPECTED); return FALSE; } *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zqgblmod.c0000755000032200000250000000306414342376331016341 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mdq.h" #include "fullbool.h" error_def(ERR_VAREXPECTED); int f_zqgblmod(oprtype *a, opctype op) { triple *oldchain, *r; save_se save_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); switch (TREF(window_token)) { case TK_CIRCUMFLEX: if (!gvn()) return FALSE; r->opcode = OC_FNZQGBLMOD; ins_triple(r); break; case TK_ATSIGN: r->opcode = OC_INDFUN; if (SHIFT_SIDE_EFFECTS) { START_GVBIND_CHAIN(&save_state, oldchain); if (!indirection(&(r->operand[0]))) { setcurtchain(oldchain); return FALSE; } r->operand[1] = put_ilit((mint)indir_fnzqgblmod); ins_triple(r); PLACE_GVBIND_CHAIN(&save_state, oldchain); } else { if (!indirection(&(r->operand[0]))) return FALSE; r->operand[1] = put_ilit((mint)indir_fnzqgblmod); ins_triple(r); } break; default: stx_error(ERR_VAREXPECTED); return FALSE; } *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zsearch.c0000755000032200000250000000213114342376331016153 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" int f_zsearch(oprtype *a, opctype op) { triple *r, *rop; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) return FALSE; rop = newtriple(OC_PARAMETER); r->operand[1] = put_tref(rop); if (TK_COMMA != TREF(window_token)) rop->operand[0] = put_ilit(0); else { advancewindow(); if (EXPR_FAIL == expr(&(rop->operand[0]), MUMPS_INT)) return FALSE; } rop->operand[1] = put_ilit(1); /* This is an M-function call */ ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zsigproc.c0000755000032200000250000000226114342376331016360 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" error_def(ERR_COMMA); int f_zsigproc(oprtype *a, opctype op) { triple *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); /* First argument is integer process id */ if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_INT)) return FALSE; /* Improper process id argument */ if (TK_COMMA != TREF(window_token)) { /* 2nd argument (for now) required */ stx_error(ERR_COMMA); return FALSE; } advancewindow(); /* 2nd argument is the signal number to send */ if (EXPR_FAIL == expr(&(r->operand[1]), MUMPS_INT)) return FALSE; /* Improper signal number argument */ ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zsocket.c0000644000032200000250000000414414342376331016201 0ustar librarygtc/**************************************************************** * * * Copyright 2014 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" error_def(ERR_MAXARGCNT); #define MAX_ZSOCKET_ARGS 6 /* argc, dst, device, keyword, arg1, arg2 */ int f_zsocket(oprtype *a, opctype op) { int argc; char tok_temp; oprtype *argp, argv[MAX_ZSOCKET_ARGS]; triple *curr, *last, *root; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; argp = &argv[0]; argc = 0; if (TK_COMMA == TREF(window_token)) { /* empty first argument is for socket stringpool */ curr = newtriple(OC_NULLEXP); *argp = put_tref(curr); } else { if (EXPR_FAIL == expr(argp, MUMPS_STR)) /* device name */ return FALSE; } assert(TRIP_REF == argp->oprclass); argc++; argp++; advancewindow(); if (EXPR_FAIL == expr(argp, MUMPS_STR)) /* which item */ return FALSE; assert(TRIP_REF == argp->oprclass); argc++; argp++; for (;;) { if (TK_COMMA != TREF(window_token)) break; advancewindow(); tok_temp = TREF(window_token); if ((2 == argc) && ((TK_COMMA == tok_temp) || (TK_RPAREN == tok_temp))) { /* missing third argument is for default index */ curr = newtriple(OC_NULLEXP); *argp = put_tref(curr); } else if (EXPR_FAIL == expr(argp, MUMPS_EXPR)) return FALSE; assert(TRIP_REF == argp->oprclass); argc++; argp++; if (MAX_ZSOCKET_ARGS < argc) { stx_error(ERR_MAXARGCNT, 1, MAX_ZSOCKET_ARGS); return FALSE; } } root = last = maketriple(op); root->operand[0] = put_ilit(argc + 1); argp = &argv[0]; for (; argc > 0 ;argc--, argp++) { curr = newtriple(OC_PARAMETER); curr->operand[0] = *argp; last->operand[1] = put_tref(curr); last = curr; } ins_triple(root); *a = put_tref(root); return TRUE; } fis-gtm-V7.0-005/sr_port/f_ztrigger.c0000644000032200000250000000267514342376331016363 0ustar librarygtc/**************************************************************** * * * Copyright 2010, 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 "compiler.h" #include "toktyp.h" #include "opcode.h" #include "advancewindow.h" LITREF mval literal_null ; int f_ztrigger(oprtype *a, opctype op) { triple *r, *arg1, *arg2; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); arg1 = newtriple(OC_PARAMETER); arg2 = newtriple(OC_PARAMETER); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) return FALSE; if (TK_COMMA == TREF(window_token)) { /* Looking for a 2nd argument */ advancewindow(); if (EXPR_FAIL == expr(&(arg1->operand[0]), MUMPS_STR)) return FALSE; if (TK_COMMA == TREF(window_token)) { advancewindow(); if (EXPR_FAIL == expr(&(arg2->operand[0]), MUMPS_STR)) return FALSE; } else arg2->operand[0] = put_lit((mval *)&literal_null); } else { arg1->operand[0] = put_lit((mval *)&literal_null); arg2->operand[0] = put_lit((mval *)&literal_null); } r->operand[1] = put_tref(arg1); arg1->operand[1] = put_tref(arg2); ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_ztrnlnm.c0000755000032200000250000000400714342376331016224 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" int f_ztrnlnm(oprtype *a, opctype op) { boolean_t again; int i; triple *last, *r, *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; last = r = maketriple(op); if (EXPR_FAIL == expr(&r->operand[0], MUMPS_STR)) return FALSE; ref = newtriple(OC_PARAMETER); last->operand[1] = put_tref(ref); if (TK_COMMA == TREF(window_token)) { advancewindow(); if ((TK_COMMA == TREF(window_token)) || (TK_RPAREN == TREF(window_token))) ref->operand[0] = put_str("", 0); else if (EXPR_FAIL == expr(&ref->operand[0], MUMPS_STR)) return FALSE; } else ref->operand[0] = put_str("", 0); last = ref; ref = newtriple(OC_PARAMETER); last->operand[1] = put_tref(ref); if (TK_COMMA == TREF(window_token)) { advancewindow(); if ((TK_COMMA == TREF(window_token)) || (TK_RPAREN == TREF(window_token))) ref->operand[0] = put_ilit(0); else if (EXPR_FAIL == expr(&ref->operand[0], MUMPS_INT)) return FALSE; } else ref->operand[0] = put_ilit(0); last = ref; again = TRUE; for (i = 0; i < 3; i++) { ref = newtriple(OC_PARAMETER); last->operand[1] = put_tref(ref); if (again && (TK_COMMA == TREF(window_token))) { advancewindow(); if ((TK_COMMA == TREF(window_token)) || (TK_RPAREN == TREF(window_token))) ref->operand[0] = put_str("",0); else if (EXPR_FAIL == expr(&ref->operand[0], MUMPS_STR)) return FALSE; } else { again = FALSE; ref->operand[0] = put_str("",0); } last = ref; } ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zwidth.c0000755000032200000250000000200214342376331016022 0ustar librarygtc/**************************************************************** * * * Copyright 2006, 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 "compiler.h" #include "advancewindow.h" /* $ZWIDTH(): Single parameter - string expression */ int f_zwidth(oprtype *a, opctype op) { triple *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) return FALSE; ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/f_zwrite.c0000644000032200000250000000435714342376331016051 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2015 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 "compiler.h" #include "mmemory.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mdq.h" #include "fullbool.h" #include "gtm_utf8.h" #include "advancewindow.h" #include "op.h" #include "stringpool.h" /* $ZWRITE(): Single parameter - string expression */ int f_zwrite(oprtype *a, opctype op) { mval tmp_mval; oprtype *newop; triple *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r = maketriple(op); if (EXPR_FAIL == expr(&(r->operand[0]), MUMPS_STR)) return FALSE; if (TK_COMMA != TREF(window_token)) r->operand[1] = put_ilit(0); else { advancewindow(); if (EXPR_FAIL == expr(&(r->operand[1]), MUMPS_INT)) return FALSE; } /* This code tries to execute $ZWRITE at compile time if all parameters are literals */ if ((OC_LIT == r->operand[0].oprval.tref->opcode) && (ILIT_REF == r->operand[1].oprval.tref->operand->oprclass) && (!gtm_utf8_mode || valid_utf_string(&r->operand[0].oprval.tref->operand[0].oprval.mlit->v.str))) { /* We don't know how much space we will use; but we know it's based on the size of the current string */ op_fnzwrite(r->operand[1].oprval.tref->operand[0].oprval.ilit, &r->operand[0].oprval.tref->operand[0].oprval.mlit->v, &tmp_mval); newop = (oprtype *)mcalloc(SIZEOF(oprtype)); *newop = put_lit(&tmp_mval); /* Copies mval so stack var tmp_mval not an issue */ assert(TRIP_REF == newop->oprclass); newop->oprval.tref->src = r->src; *a = put_tref(newop->oprval.tref); return TRUE; } ins_triple(r); *a = put_tref(r); return TRUE; } fis-gtm-V7.0-005/sr_port/fao_parm.h0000755000032200000250000000246014342376331016005 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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. * * * ****************************************************************/ typedef struct { unsigned short len; unsigned char fill1; unsigned char fill2; char *addr; } desc_struct; /* Currently, the maximum number of argument placeholders in a message is 16. Certain types of placeholders (such as !AD) require * two arguments, length and address, to be passed to the corresponding output function (normally, rts_error, send_msg, or putmsg). * We are being safe and taking the maximum number of placeholders as 17, doubling the number for length-address types. */ #define MAX_FAO_PARMS 34 /* Since @... type parameters involve 8-byte values, we need an additional slot per each such value on 32-bit platforms, define the * number of INTPTR_T-typed slots appropriately for both 64- and 32-bit architectures. */ #ifdef GTM64 # define NUM_OF_FAO_SLOTS MAX_FAO_PARMS #else # define NUM_OF_FAO_SLOTS ((MAX_FAO_PARMS + 1) / 2 * 3) #endif fis-gtm-V7.0-005/sr_port/fgn_glopref.c0000644000032200000250000000171414342376333016504 0ustar librarygtc/**************************************************************** * * * 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. * * * ****************************************************************/ /* fgn_glopref : prefixes the global variable name with ^ for locks. */ #include "mdef.h" #include "gtm_string.h" #include "stringpool.h" #include #include "lv_val.h" /* needed by "fgncal.h" */ #include "fgncal.h" GBLREF spdesc stringpool ; void fgn_glopref(mval *v) { unsigned char *p; ENSURE_STP_FREE_SPACE(v->str.len + 1); p = stringpool.free; *stringpool.free++ = '^'; memcpy(stringpool.free,v->str.addr,v->str.len); stringpool.free += v->str.len ; v->str.addr = (char *)p; v->str.len++; } fis-gtm-V7.0-005/sr_port/fgncal.h0000755000032200000250000000312014342376331015445 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef FGNCAL_H_INCLUDED #define FGNCAL_H_INCLUDED mval *fgncal_lookup(mval *x); void fgncal_unwind(void); void fgncal_rundown(void); #include "fgncalsp.h" /* Checks whether the last bit of the passed mask M is 1. This is how we decide whether an argument is of input-only, input-output, * or output-only type. To be specific, if an argument is of input-only type, its bit will be set in the input mask but not the * output mask; if it is output-only, then the bit is only set in the output mask; finally, if it is input-output, then its bit is * set in the both masks. Note that after checking the mask bit for one argument, the mask needs to be binary shifted, such that the * last bit contains the status of the next argument, and so on. */ #define MASK_BIT_ON(M) (M & 1) /* Checks whether V is defined and marked for use in a specific input/output direction, depending on whether M is an input or output * mask (see the comment for MASK_BIT_ON), which is what determines if the actual or default value should be used. */ #define MV_ON(M, V) (MASK_BIT_ON(M) && MV_DEFINED(V)) #endif fis-gtm-V7.0-005/sr_port/fgncal_lookup.c0000644000032200000250000000224214342376333017034 0ustar librarygtc/**************************************************************** * * * 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 "toktyp.h" #include "lv_val.h" /* needed by "fgncal.h" */ #include "fgncal.h" #include "valid_mname.h" #include GBLREF symval *curr_symval; mval *fgncal_lookup(mval *x) { mval *ret_val; ht_ent_mname *tabent; var_tabent targ_key; mident ident; MV_FORCE_DEFINED(x); assert(MV_IS_STRING(x)); ret_val = NULL; ident = x->str; if (ident.len > MAX_MIDENT_LEN) ident.len = MAX_MIDENT_LEN; if (valid_mname(&ident)) { targ_key.var_name = ident; COMPUTE_HASH_MNAME(&targ_key); targ_key.marked = FALSE; if (add_hashtab_mname_symval(&curr_symval->h_symtab, &targ_key, NULL, &tabent)) lv_newname(tabent, curr_symval); ret_val = (mval *) tabent->value; } return ret_val; } fis-gtm-V7.0-005/sr_port/fgncal_unwind.c0000644000032200000250000000445514342376333017037 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "stack_frame.h" #include "tp_frame.h" #include "error.h" #include "error_trap.h" #include "mv_stent.h" #include "op.h" #include "fgncal.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 "gtm_trigger.h" #endif /* On UNIX, the temp_fgncal_stack threadgbl can override fgncal_stack but VMS does not have * this support so define the FGNCAL_STACK macro here such that they are the same. */ #ifdef UNIX # include "gtmci.h" /* Contains FGNCAL_STACK macro */ #elif defined(VMS) # define FGNCAL_STACK fgncal_stack #else # error "Unsupported platform" #endif GBLDEF unsigned char *fgncal_stack; GBLREF unsigned char *stackbase, *stacktop, *stackwarn, *msp; GBLREF mv_stent *mv_chain; GBLREF stack_frame *frame_pointer; error_def(ERR_STACKUNDERFLO); void fgncal_unwind(void) { mv_stent *mvc; unsigned char *local_fgncal_stack; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert((msp <= stackbase) && (msp > stacktop)); assert((mv_chain <= (mv_stent *)stackbase) && (mv_chain > (mv_stent *)stacktop)); assert((frame_pointer <= (stack_frame*)stackbase) && (frame_pointer > (stack_frame *)stacktop)); local_fgncal_stack = FGNCAL_STACK; while (frame_pointer && (frame_pointer < (stack_frame *)local_fgncal_stack)) { # ifdef GTM_TRIGGER if (SFT_TRIGR & frame_pointer->type) gtm_trigger_fini(TRUE, FALSE); else # endif op_unwind(); } for (mvc = mv_chain; mvc < (mv_stent *)local_fgncal_stack; mvc = (mv_stent *)(mvc->mv_st_next + (char *) mvc)) unw_mv_ent(mvc); mv_chain = mvc; msp = local_fgncal_stack; UNIX_ONLY(TREF(temp_fgncal_stack) = NULL); if (msp > stackbase) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_STACKUNDERFLO); } fis-gtm-V7.0-005/sr_port/file_head_read.h0000755000032200000250000000114614342376331017114 0ustar librarygtc/**************************************************************** * * * Copyright 2003, 2005 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. * * * ****************************************************************/ #ifndef FILE_HEAD_READ_INCLUDED #define FILE_HEAD_READ_INCLUDED boolean_t file_head_read(char *, sgmnt_data_ptr_t, int4); #endif /* FILE_HEAD_READ_INCLUDED */ fis-gtm-V7.0-005/sr_port/file_head_write.h0000755000032200000250000000125214342376331017331 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2016 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. * * * ****************************************************************/ #ifndef FILE_HEAD_WRITE_INCLUDED #define FILE_HEAD_WRITE_INCLUDED boolean_t file_head_write(char *, sgmnt_data_ptr_t, int4); #endif /* FILE_HEAD_WRITE_INCLUDED */ fis-gtm-V7.0-005/sr_port/fileinfo.h0000755000032200000250000000220314342376331016007 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef FILE_INFO_H_INCLUDED #define FILE_INFO_H_INCLUDED #define FI_USR_SZ 31 #define FI_TRM_SZ 7 typedef struct { gtm_facility fac; /* facility */ short dat[4]; /* date (quadword) */ char usr[FI_USR_SZ]; /* user name */ char trm[FI_TRM_SZ]; /* terminal identification */ char filler[2]; /* used for longword alignment */ }file_info; #define FI_NUM_ENT 5 typedef struct { int4 cnt; /* number of entries inserted into ent. * ent is a circular queue so * ent[ cnt % FI_NUM_ENT] * is always the next location to insert. */ file_info ent[FI_NUM_ENT]; /* entries */ }file_log; #endif fis-gtm-V7.0-005/sr_port/find_line_addr.c0000644000032200000250000000460514342376333017137 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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_string.h" #include #include "cmd_qlf.h" #include "gtm_caseconv.h" #include "min_max.h" GBLREF command_qualifier cmd_qlf; error_def(ERR_LABELONLY); int4* find_line_addr(rhdtyp *routine, mstr *label, int4 offset, mident **lent_name) { lab_tabent *base, *top, *ptr; rhdtyp *real_routine; mident_fixed target_label; mident lname; lnr_tabent *line_table, *first_line; int stat, n; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (!routine) return NULL; real_routine = CURRENT_RHEAD_ADR(routine); first_line = LNRTAB_ADR(real_routine); if (!label->len || !*label->addr) { /* No label specified. Return the first line */ base = LABTAB_ADR(real_routine); /* Get the null label entry and note whether it has a formallist. */ (TABENT_PROXY).has_parms = base->has_parms; assert(0 == base->lab_name.len); if (lent_name) *lent_name = &base->lab_name; line_table = first_line; } else { lname.len = (label->len <= MAX_MIDENT_LEN) ? label->len : MAX_MIDENT_LEN; if (cmd_qlf.qlf & CQ_LOWER_LABELS) lname.addr = label->addr; else { lower_to_upper((uchar_ptr_t)&target_label.c[0], (uchar_ptr_t)label->addr, lname.len); lname.addr = &target_label.c[0]; } ptr = base = LABTAB_ADR(real_routine); top = base + real_routine->labtab_len; for ( ; ; ) { n = (int)(top - base) / 2; ptr = base + n; MIDENT_CMP(&lname, &ptr->lab_name, stat); if (0 == stat) { /* Note whether the label has a formallist. */ (TABENT_PROXY).has_parms = ptr->has_parms; if (lent_name) *lent_name = &ptr->lab_name; line_table = LABENT_LNR_ENTRY(real_routine, ptr); break; } else if (0 < stat) base = ptr; else top = ptr; if (n < 1) return NULL; } } line_table += offset; if ((first_line > line_table) || (first_line + real_routine->lnrtab_len <= line_table)) return NULL; return line_table; } fis-gtm-V7.0-005/sr_port/find_line_start.c0000644000032200000250000000405214342376333017356 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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 GBLREF unsigned char *stackbase, *stacktop; unsigned char *find_line_start(unsigned char *in_addr, rhdtyp *routine) { unsigned char *result; lab_tabent *max_label, *label_table, *last_label; lnr_tabent *line_table, *last_line; int4 in_addr_offset; result = (unsigned char *)0; if (!ADDR_IN_CODE(in_addr, routine)) return result; routine = CURRENT_RHEAD_ADR(routine); USHBIN_ONLY( assert(routine->labtab_adr); assert(routine->lnrtab_adr); ); NON_USHBIN_ONLY( assert(routine->labtab_ptr >= 0); assert(routine->lnrtab_ptr >= 0); ); assert(routine->labtab_len >= 0); assert(routine->lnrtab_len >= 0); label_table = LABTAB_ADR(routine); last_label = label_table + routine->labtab_len; max_label = label_table++; while (label_table < last_label) { /* Find first label that goes past the input addr. The previous label is then the target line */ if (in_addr > LABEL_ADDR(routine, label_table)) { if (max_label->LABENT_LNR_OFFSET <= label_table->LABENT_LNR_OFFSET) max_label = label_table; } label_table++; } line_table = LABENT_LNR_ENTRY(routine, max_label); /* Used as offset !! */ in_addr_offset = (int4)(in_addr - CODE_BASE_ADDR(routine)); last_line = LNRTAB_ADR(routine); last_line += routine->lnrtab_len; for( ; ++line_table < last_line ;) { /* Find first line that is > input addr. The previous line is the target line */ if (in_addr_offset <= *line_table) { result = LINE_NUMBER_ADDR(routine, (line_table - 1)); break; } } if (line_table >= last_line) result = LINE_NUMBER_ADDR(routine, (line_table - 1)); return result; } fis-gtm-V7.0-005/sr_port/find_mvstent.c0000644000032200000250000000760214342376333016716 0ustar librarygtc/**************************************************************** * * * Copyright 2011, 2013 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 "gt_timer.h" #include "io.h" #include "iotimer.h" #include "iosocketdef.h" #include #include "mv_stent.h" #include "find_mvstent.h" #include "stack_frame.h" GBLREF mv_stent *mv_chain; GBLREF stack_frame *frame_pointer; GBLREF unsigned char *stackbase, *stacktop, *msp, *stackwarn; /* Find and optionally clear the mv_stent keeping interrupted device information for us */ mv_stent *io_find_mvstent(io_desc *io_ptr, boolean_t clear_mvstent) { mv_stent *mvc, *mv_zintdev; assert(msp <= stackbase && msp > stacktop); assert(mv_chain <= (mv_stent *)stackbase && mv_chain > (mv_stent *)stacktop); assert(frame_pointer <= (stack_frame *)stackbase && frame_pointer > (stack_frame *)stacktop); mv_zintdev = NULL; for (mvc = mv_chain; mvc < (mv_stent *)frame_pointer ; mvc = (mv_stent *)(mvc->mv_st_next + (char *)mvc)) { if (MVST_ZINTDEV == mvc->mv_st_type && io_ptr == mvc->mv_st_cont.mvs_zintdev.io_ptr) { mv_zintdev = mvc; break; } assertpro(mvc->mv_st_next); } if (mv_zintdev && clear_mvstent) { if (mv_chain == mv_zintdev) POP_MV_STENT(); /* just pop if top of stack */ else { mv_zintdev->mv_st_cont.mvs_zintdev.io_ptr = NULL; mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; } } return mv_zintdev; } /* Find and optionally clear the mv_stent keeping information for interrupted * timed non IO commands. Unlike IO commands there is no associated structure * so the restart_pc is used to identify which instance of the command is of * interest */ mv_stent *find_mvstent_cmd(zintcmd_ops match_command, unsigned char *match_restart_pc, unsigned char *match_restart_ctxt, boolean_t clear_mvstent) { mv_stent *mvc, *mv_zintcmd; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(msp <= stackbase && msp > stacktop); assert(mv_chain <= (mv_stent *)stackbase && mv_chain > (mv_stent *)stacktop); assert(frame_pointer <= (stack_frame *)stackbase && frame_pointer > (stack_frame *)stacktop); assert((0 < match_command) && (ZINTCMD_LAST > match_command)); mv_zintcmd = NULL; if ((0 >= TAREF1(zintcmd_active, match_command).count) /* at least one mv_stent for this command */ || (match_restart_pc != TAREF1(zintcmd_active, match_command).restart_pc_last) || (match_restart_ctxt != TAREF1(zintcmd_active, match_command).restart_ctxt_last)) return mv_zintcmd; /* not ours so no need to search stack */ for (mvc = mv_chain; mvc < (mv_stent *)frame_pointer ; mvc = (mv_stent *)(mvc->mv_st_next + (char *)mvc)) { if (MVST_ZINTCMD == mvc->mv_st_type && match_command == mvc->mv_st_cont.mvs_zintcmd.command && match_restart_pc == mvc->mv_st_cont.mvs_zintcmd.restart_pc_check && match_restart_ctxt == mvc->mv_st_cont.mvs_zintcmd.restart_ctxt_check) { mv_zintcmd = mvc; break; } assertpro(mvc->mv_st_next); } if (mv_zintcmd && clear_mvstent) { /* restore previous zintcmd_active values before clearing MV_STENT entry */ TAREF1(zintcmd_active, match_command).restart_pc_last = mv_zintcmd->mv_st_cont.mvs_zintcmd.restart_pc_prior; TAREF1(zintcmd_active, match_command).restart_ctxt_last = mv_zintcmd->mv_st_cont.mvs_zintcmd.restart_ctxt_prior; TAREF1(zintcmd_active, match_command).count--; assert(0 <= TAREF1(zintcmd_active, match_command).count); if (mv_chain == mvc) POP_MV_STENT(); /* just pop if top of stack */ else { mv_zintcmd->mv_st_cont.mvs_zintcmd.command = ZINTCMD_NOOP; mv_zintcmd->mv_st_cont.mvs_zintcmd.restart_pc_check = NULL; } } return mv_zintcmd; } fis-gtm-V7.0-005/sr_port/find_mvstent.h0000644000032200000250000000227314342376331016720 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef FIND_MVSTENT_H #define FIND_MVSTENT_H mv_stent *find_mvstent_cmd(zintcmd_ops match_command, unsigned char *match_restart_pc, unsigned char *match_restart_ctxt, boolean_t clear_mvstent); /* Keep track of active interrupted commands in global to avoid unneeded searches of stack */ typedef struct { int count; /* number of active MVST_ZINTCMD entries for this command */ unsigned char *restart_pc_last; /* most recent on MVST stack */ unsigned char *restart_ctxt_last; } zintcmd_active_info; #endif fis-gtm-V7.0-005/sr_port/find_rtn_hdr.c0000644000032200000250000000424414342376333016655 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_string.h" #include #include "min_max.h" #include "stack_frame.h" #include "compiler.h" /* for WANT_CURRENT_RTN_MSTR macro */ GBLREF rtn_tabent *rtn_names, *rtn_names_end; GBLREF stack_frame *frame_pointer; rhdtyp *find_rtn_hdr(mstr *name) { rtn_tabent *rtabent; if (WANT_CURRENT_RTN_MSTR(name)) /* want the *current* version on the stack, not the *newest* ZLINK'd version */ return CURRENT_RHEAD_ADR(frame_pointer->rvector); if (find_rtn_tabent(&rtabent, name)) return rtabent->rt_adr; else return NULL; } /* * Returns TRUE if a rtn_tabent exists for routine , i.e. if * is currently ZLINK'd. * Returns FALSE otherwise. * In either case, also "returns" (via ) the rtn_tabent * corresponding to the first routine name greater than or equal to * . This is useful for looking up trigger routines that * include runtime disambiguators in their names. */ boolean_t find_rtn_tabent(rtn_tabent **res, mstr *name) { rtn_tabent *bot, *top, *mid; int4 comp; mident rtn_name; mident_fixed rtn_name_buff; boolean_t ret; int len; len = name->len; rtn_name.len = MIN(MAX_MIDENT_LEN, len); rtn_name.addr = name->addr; bot = rtn_names + 1; /* Exclude the first NULL entry */ top = rtn_names_end + 1;/* Include the last entry */ for ( ; ; ) { if (bot == top) break; assert(bot < top); mid = bot + (top - bot) / 2; assert(mid >= bot); MIDENT_CMP(&mid->rt_name, &rtn_name, comp); if (0 == comp) { *res = mid; return TRUE; } else if (0 > comp) { bot = mid + 1; continue; } else { assert(mid < top); top = mid; continue; } } *res = bot; return FALSE; } fis-gtm-V7.0-005/sr_port/five_2_ascii.c0000755000032200000250000000130114342376331016527 0ustar librarygtc/**************************************************************** * * * Copyright 2002 Sanchez Computer Associates, 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 "five_2_ascii.h" unsigned char *five_2_ascii(unsigned short *inval, unsigned char *cp) { int4 val; val = *inval; *cp++ = (val >> 11) + '@'; *cp++ = ((val >> 6) & 0x1f) + '@'; *cp++ = ((val >> 1) & 0x1f) + '@'; return cp; } fis-gtm-V7.0-005/sr_port/five_2_ascii.h0000755000032200000250000000115414342376331016542 0ustar librarygtc/**************************************************************** * * * Copyright 2002 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef FIVE_2_ASCII_H_INCLUDED #define FIVE_2_ASCII_H_INCLUDED unsigned char *five_2_ascii(unsigned short *inval, unsigned char *cp); #endif /* FIVE_2_ASCII_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/five_bit.c0000755000032200000250000000166714342376331016013 0ustar librarygtc/**************************************************************** * * * Copyright 2002 Sanchez Computer Associates, 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 "five_bit.h" /* five_bit - convert 3-character string into 5-bit character representation */ unsigned short five_bit(unsigned char *src) /* src is pointer to 3-character string to be converted to 5-bit format */ { int index; unsigned short result; /* Or low-order 5 bits of each character together into high-order 15 bits of result. */ for (index = 0, result = 0; index < 3; index++, src++) result = (result << 5) | (*src & 0x1f); result <<= 1; return result; } fis-gtm-V7.0-005/sr_port/five_bit.h0000755000032200000250000000110614342376331016004 0ustar librarygtc/**************************************************************** * * * Copyright 2002 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef FIVE_BIT_H_INCLUDED #define FIVE_BIT_H_INCLUDED unsigned short five_bit(unsigned char *src); #endif /* FIVE_BIT_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/fix_pages.h0000755000032200000250000000112014342376331016156 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef FIX_PAGES_INCLUDED #define FIX_PAGES_INCLUDED void fix_pages(unsigned char * bot, unsigned char * top); #endif /* FIX_PAGES_INCLUDED */ fis-gtm-V7.0-005/sr_port/fix_xfer_entry.h0000644000032200000250000000432714342376331017255 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2007-2021 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. * * * ****************************************************************/ #ifndef FIX_XFER_ENTRY_INCLUDED #define FIX_XFER_ENTRY_INCLUDED GBLREF xfer_entry_t xfer_table[]; #if defined(__x86_64__) #include "xfer_desc.i" #endif /* macro to change a transfer table entry */ #define FIX_XFER_ENTRY(indx, func) \ MBSTART { \ xfer_table[indx] = (xfer_entry_t)&func; \ } MBEND /* macro to insert interrupt vectors in the transfer table at appropriate places */ #define DEFER_INTO_XFER_TAB \ MBSTART { \ FIX_XFER_ENTRY(xf_linefetch, op_fetchintrrpt); \ FIX_XFER_ENTRY(xf_linestart, op_startintrrpt); \ FIX_XFER_ENTRY(xf_zbfetch, op_fetchintrrpt); \ FIX_XFER_ENTRY(xf_zbstart, op_startintrrpt); \ FIX_XFER_ENTRY(xf_forchk1, op_startintrrpt); \ FIX_XFER_ENTRY(xf_forloop, op_forintrrpt); \ } MBEND /* macro to return *some* vectors to "normal" from interrupting adjustments; ret and retarg fall out side this; * for adjustments are unnecessary in some cases, but benign */ #define DEFER_OUT_OF_XFER_TAB(IS_TRACING) \ MBSTART { \ if (IS_TRACING) \ { /* M-profiling in effect */ \ FIX_XFER_ENTRY(xf_linefetch, op_mproflinefetch); \ FIX_XFER_ENTRY(xf_linestart, op_mproflinestart); \ FIX_XFER_ENTRY(xf_forchk1, op_mprofforchk1); \ } else \ { \ FIX_XFER_ENTRY(xf_linefetch, op_linefetch); \ FIX_XFER_ENTRY(xf_linestart, op_linestart); \ FIX_XFER_ENTRY(xf_forchk1, op_forchk1); \ } \ FIX_XFER_ENTRY(xf_zbfetch, op_zbfetch); \ FIX_XFER_ENTRY(xf_zbstart, op_zbstart); \ } MBEND #endif /* FIX_XFER_ENTRY_INCLUDED */ fis-gtm-V7.0-005/sr_port/fl.mpt0000755000032200000250000000102514342376331015167 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 1992,2001 Sanchez Computer Associates, 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %FL ;GT.M %FL utility - first lines lister ;invoke ^%FL to get interaction ; d FL^%RO q fis-gtm-V7.0-005/sr_port/flt_mod.c0000644000032200000250000000616514342376331015643 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* flt_mod.(u, v) = u - (v*floor.(u/v)) where x-1 < floor.x <= x ^ int.x */ #include "mdef.h" #include "arit.h" #include "op.h" #include "eb_muldiv.h" #include "promodemo.h" #include "flt_mod.h" LITREF mval literal_zero; LITREF int4 ten_pwr[]; error_def(ERR_DIVZERO); void flt_mod (mval *u, mval *v, mval *q) { int exp; int4 z, x; mval w; /* temporary mval for division result */ mval y; /* temporary mval for extended precision promotion to prevent modifying caller's data */ mval *u_orig; /* original (caller's) value of u */ u_orig = u; MV_FORCE_NUM(u); MV_FORCE_NUM(v); if ((v->mvtype & MV_INT) != 0 && v->m[1] == 0) rts_error_csa(NULL, VARLSTCNT(1) ERR_DIVZERO); /* BYPASSRTSABT */ if ((u->mvtype & MV_INT & v->mvtype) != 0) { /* Both are INT's; use shortcut. */ q->mvtype = MV_NM | MV_INT; eb_int_mod(u->m[1], v->m[1], q->m); return; } else if ((u->mvtype & MV_INT) != 0) { /* u is INT; promote to extended precision for compatibility with v. */ y = *u; promote(&y); /* y will be normalized, but not in canonical form */ u = &y; /* this is why we need u_orig */ } else if ((v->mvtype & MV_INT) != 0) { /* v is INT; promote to extended precision for compatibility with u. */ y = *v; promote(&y); v = &y; } /* At this point, both u and v are in extended precision format. */ /* Set w = floor(u/v). */ op_div (u, v, &w); if ((w.mvtype & MV_INT) != 0) promote(&w); exp = w.e; if (exp <= MV_XBIAS) { /* Magnitude of w, floor(u/v), is < 1. */ if (u->sgn != v->sgn && w.m[1] != 0 && exp >= EXPLO) { /* Signs differ (=> floor(u/v) < 0) and (w != 0) and (no underflow) => floor(u/v) == -1 */ w.sgn = 1; w.e = MV_XBIAS + 1; w.m[1] = MANT_LO; w.m[0] = 0; } else { /* Signs same (=> floor(u/v) >= 0) or (w == 0) or (underflow) => floor(u/v) == 0 */ *q = *u_orig; /* u - floor(u/v)*v == u - 0*v == u */ return; } } else if (exp < EXP_IDX_BIAL) { z = ten_pwr[EXP_IDX_BIAL - exp]; x = (w.m[1]/z)*z; if (u->sgn != v->sgn && (w.m[1] != x || w.m[0] != 0)) { w.m[0] = 0; w.m[1] = x + z; if (w.m[1] >= MANT_HI) { w.m[0] = w.m[0]/10 + (w.m[1]%10)*MANT_LO; w.m[1] /= 10; w.e++; } } else { w.m[0] = 0; w.m[1] = x; } } else if (exp < EXP_IDX_BIAQ) { z = ten_pwr[EXP_IDX_BIAQ - exp]; x = (w.m[0]/z)*z; if (u->sgn != v->sgn && w.m[0] != x) { w.m[0] = x + z; if (w.m[0] >= MANT_HI) { w.m[0] -= MANT_HI; w.m[1]++; } } else { w.m[0] = x; } } op_mul (&w, v, &w); /* w = w*v = floor(u/v)*v */ op_sub (u_orig, &w, q); /* q = u - w = u - floor(u/v)*v */ } fis-gtm-V7.0-005/sr_port/flt_mod.h0000755000032200000250000000115614342376331015646 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef FLT_MOD_H_INCLUDED #define FLT_MOD_H_INCLUDED void flt_mod (mval *u, mval *v, mval *q); #endif fis-gtm-V7.0-005/sr_port/flush_jmp.c0000644000032200000250000002134414342376333016204 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_stdio.h" #include "gtm_string.h" #include #include "mv_stent.h" #include "objlabel.h" #include "cache.h" #include "stack_frame.h" #include "cache_cleanup.h" #include "op.h" #include "unwind_nocounts.h" #include "flush_jmp.h" #include "error.h" #include "tp_frame.h" #ifdef GTM_TRIGGER # include "gtm_trigger_trc.h" #endif GBLREF symval *curr_symval; GBLREF stack_frame *error_frame; GBLREF stack_frame *frame_pointer; GBLREF mv_stent *mv_chain; GBLREF unsigned char *stackbase,*stacktop,*msp,*stackwarn; GBLREF tp_frame *tp_pointer; LITREF boolean_t mvs_save[]; STATICFNDCL void fix_tphold_mvc(char *target, char *srcstart, char *srcend); error_def(ERR_STACKCRIT); error_def(ERR_STACKOFLOW); void flush_jmp (rhdtyp *rtn_base, unsigned char *context, unsigned char *transfer_addr) { mv_stent *mv_st_ent, *mv_st_prev; char *top; unsigned char *msp_save; int4 shift, size, mv_st_type; USHBIN_ONLY(rhdtyp *old_rtnhdr;) unwind_nocounts(); /* We are going to mutate the current frame from the program it was running to the program we want it to run. * If the current frame is marked for indr cache cleanup, do that cleanup now and unmark the frame. */ IF_INDR_FRAME_CLEANUP_CACHE_ENTRY_AND_UNMARK(frame_pointer); DBGEHND((stderr, "flush_jmp: Retargetting stack frame 0x"lvaddr" for transfer address 0x"lvaddr"\n", frame_pointer, transfer_addr)); /* Also unmark the SFF_ETRAP_ERR bit in case it is set. This way we ensure control gets transferred to * the mpc below instead of "error_return" (which is what getframe will do in case the bit is set). * It is ok to clear this bit because the global variable "error_frame" will still be set to point to * this frame so whenever we unwind out of this, we will rethrow the error at the parent frame. */ assert(!(frame_pointer->flags & SFF_ETRAP_ERR) || (NULL == error_frame) || (error_frame == frame_pointer)); assert(!(SFT_TRIGR & frame_pointer->type)); frame_pointer->flags &= SFF_ETRAP_ERR_OFF; /* clear SFF_ETRAP_ERR bit */ frame_pointer->flags &= SSF_NORET_VIA_MUMTSTART_OFF; /* clear SSF_NORET_VIA_MUMTSTART since this frame is being rewritten */ GTMTRIG_ONLY(DBGTRIGR((stderr, "flush_jmp: Disabling SSF_NORET_VIA_MUMTSTART_OFF in frame 0x"lvaddr"\n", frame_pointer))); USHBIN_ONLY(old_rtnhdr = frame_pointer->rvector); frame_pointer->rvector = rtn_base; /* Now that fp->rvector has been overwritten to new routine, check if the older routine had a "rtn_relinked" flag set * and if so that cleanup can be performed now. */ USHBIN_ONLY(CLEANUP_COPIED_RECURSIVE_RTN(old_rtnhdr)); /* cleanup if needed */ frame_pointer->vartab_ptr = (char *)VARTAB_ADR(rtn_base); frame_pointer->vartab_len = frame_pointer->rvector->vartab_len; frame_pointer->mpc = transfer_addr; frame_pointer->ctxt = context; #ifdef HAS_LITERAL_SECT frame_pointer->literal_ptr = (int4 *)LITERAL_ADR(rtn_base); #endif frame_pointer->temp_mvals = frame_pointer->rvector->temp_mvals; size = rtn_base->temp_size; frame_pointer->temps_ptr = (unsigned char *)frame_pointer - size; size += rtn_base->vartab_len * SIZEOF(ht_ent_mname *); frame_pointer->l_symtab = (ht_ent_mname **)((char *)frame_pointer - size); assert(frame_pointer->type & SFT_COUNT); assert((unsigned char *)mv_chain > stacktop && (unsigned char *)mv_chain <= stackbase); while (((char *)mv_chain < (char *)frame_pointer) && !mvs_save[mv_chain->mv_st_type]) { assert(MVST_TRIGR != mv_chain->mv_st_type); /* Should never unwind a trigger frame here */ msp = (unsigned char *)mv_chain; op_oldvar(); } if ((char *)mv_chain > (char *)frame_pointer) { msp_save = msp; msp = (unsigned char *)frame_pointer->l_symtab; if (msp <= stackwarn) { if (msp <= stacktop) { msp = msp_save; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_STACKOFLOW); } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_STACKCRIT); } memset(msp, 0, size); DBGEHND((stderr, "flush_jmp: Old msp: 0x"lvaddr" New msp: 0x"lvaddr"\n", msp_save, msp)); return; } /* We kept one or more mv_stents for this frame. We may need to shift the stack to get room to create an l_symtab * for this re-purposed frame. In the above loop, we stopped searching the mv_stent chain at the first mv_stent we * knew we had to keep. Since we are moving things around anyway, see if there are any mv_stents associated * with this frame which don't need to be kept and can reclaim. */ mv_st_ent = mv_chain; mv_st_prev = (mv_stent *)((char *)mv_st_ent + mv_st_ent->mv_st_next); top = (char *)mv_st_ent + mvs_size[mv_st_ent->mv_st_type]; while ((char *)mv_st_prev < (char *)frame_pointer) { mv_st_type = mv_st_prev->mv_st_type; assert(MVST_TRIGR != mv_st_type); /* Should never unwind a trigger frame here */ if (!mvs_save[mv_st_type]) { /* Don't need to keep this mv_stent. Remove it from the chain */ DBGEHND((stderr, "flush_jmp: Removing no-save mv_stent addr 0x"lvaddr" and type %d\n", mv_st_prev, mv_st_type)); unw_mv_ent(mv_st_prev); mv_st_ent->mv_st_next += mv_st_prev->mv_st_next; mv_st_prev = (mv_stent *)((char *)mv_st_prev + mv_st_prev->mv_st_next); continue; } /* We found a previous mv_stent we need to keep. If we had an interveening mv_stent we don't need to * keep, migrate the new keeper mv_stent adjacent to the previous keeper. */ if (mv_st_prev != (mv_stent *)top) { DBGEHND((stderr, "flush_jmp: Migrating keeper mv_stent from 0x"lvaddr" to 0x"lvaddr" type %d\n", mv_st_prev, top, mv_st_type)); if (MVST_TPHOLD == mv_st_type) { /* If we are moving an MVST_TPHOLD mv_stent, find it in the tpstack and fix its * address there too. Else we won't unwind to the correct place on a restart. */ fix_tphold_mvc(top, (char *)mv_st_prev, ((char *)mv_st_prev + mvs_size[MVST_TPHOLD])); } memmove(top, mv_st_prev, mvs_size[mv_st_type]); } DBGEHND((stderr, "flush_jmp: Updating offsets for mv_stent at addr 0x"lvaddr" type %d\n", mv_st_ent, mv_st_ent->mv_st_type)); mv_st_ent->mv_st_next = mvs_size[mv_st_ent->mv_st_type]; mv_st_ent = (mv_stent *)top; mv_st_ent->mv_st_next += (unsigned int)((char *)mv_st_prev - top); top += mvs_size[mv_st_ent->mv_st_type]; mv_st_prev = (mv_stent *)((char *)mv_st_ent + mv_st_ent->mv_st_next); } shift = (int4)((char *)frame_pointer - top - size); DBGEHND_ONLY(msp_save = msp); if (shift) { if ((unsigned char *)mv_chain + shift <= stackwarn) { if ((unsigned char *)mv_chain + shift <= stacktop) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_STACKOFLOW); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_STACKCRIT); } DBGEHND((stderr, "flush_jmp: Shifting %d bytes of stack from 0x"lvaddr" to 0x"lvaddr" by %d bytes\n", INTCAST(top - (char *)mv_chain), mv_chain, (mv_chain + shift), shift)); /* Since we are moving one or more mv_stents, it is no more difficult to check the range against the * tp_frame chain than it is to loop through the mv_stents checking each one since the tp_frame stack * is usually no more than 1-3 deep. */ fix_tphold_mvc(((char *)mv_chain + shift), (char *)mv_chain, ((char *)mv_chain + (top - (char *)mv_chain))); memmove((char *)mv_chain + shift, mv_chain, top - (char *)mv_chain); mv_chain = (mv_stent *)((char *)mv_chain + shift); mv_st_ent = (mv_stent *)((char *)mv_st_ent + shift); mv_st_ent->mv_st_next -= shift; msp = (unsigned char *)mv_chain; } memset(frame_pointer->l_symtab, 0, size); DBGEHND((stderr, "flush_jmp: Old msp: 0x"lvaddr" New msp: 0x"lvaddr"\n", msp_save, msp)); return; } /* Routine to fix up the TPHOLD mv_stent address in the tp_stack when flush_jmp shifts the stack */ STATICFNDEF void fix_tphold_mvc(char *target, char *srcstart, char *srcend) { tp_frame *tf; DBGEHND((stderr, "fix_tphold_mvc: entered with target: 0x"lvaddr" srcstart: 0x"lvaddr" srcend: 0x"lvaddr"\n", target, srcstart, srcend)); for (tf = tp_pointer; ((NULL != tf) && ((char *)tf->fp > srcstart)); tf = tf->old_tp_frame) { if (((char *)tf->mvc >= srcstart) && ((char *)tf->mvc < srcend)) { DBGEHND((stderr, "fix_tphold_mvc: Modifying tp_frame mv_stent value from 0x"lvaddr" to 0x"lvaddr " level %d\n", tf->mvc, ((char *)tf->mvc + (target - srcstart)), tf->mvc->mv_st_cont.mvs_tp_holder.tphold_tlevel)); tf->mvc = (mv_stent *)((char *)tf->mvc + (target - srcstart)); } } } fis-gtm-V7.0-005/sr_port/flush_jmp.h0000755000032200000250000000124114342376331016204 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef FLUSH_JMP_H_INCLUDED #define FLUSH_JMP_H_INCLUDED void flush_jmp (rhdtyp *rtn_base, unsigned char *context, unsigned char *transfer_addr); #endif fis-gtm-V7.0-005/sr_port/flush_pio.c0000755000032200000250000001231614342376331016205 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "io.h" #include "io_params.h" #include "gtmio.h" #include "iottdef.h" #include "iormdef.h" #include "iosocketdef.h" #include "send_msg.h" #include "error.h" #include "svnames.h" #include "gtm_string.h" GBLREF boolean_t in_prin_gtmio, prin_out_dev_failure; GBLREF io_log_name *dollar_principal, *io_root_log_name; GBLREF io_pair io_curr_device, io_std_device; STATICDEF int writing_to_pio = 0; #define SET_MUPINTR(MUPINTR, TYPE) \ MUPINTR = ((TYPE *)io_std_device.out->dev_sp)->mupintr; /* Functions for optionally writing to and flushing the principal device. We do not flush if an error has already occurred, to give * user's $ZT or EXCEPTION a chance to execute. Keep in mind that some utility programs do not have devices to flush. */ error_def(ERR_NOPRINCIO); void flush_pio(void) { if (io_std_device.out && !prin_out_dev_failure) (io_std_device.out->disp_ptr->flush)(io_std_device.out); } void write_text_newline_and_flush_pio(mstr *text) { boolean_t mupintr, encrypted; char *msg_start, c; int i, status, msg_length; io_log_name *tl; io_pair save_io_curr_device; mval dev, zstatus; /* We have already tried to stop the image, so returning here should not be allowed. */ if (prin_out_dev_failure) return; /* If we are not yet stopping the image, make sure we are not nesting on this function. */ if (3 > writing_to_pio) { writing_to_pio++; if ((!prin_out_dev_failure) && (!in_prin_gtmio)) { /* It is unsafe to continue using the principal device if we are in the middle of some I/O on it or have * already encountered an error. */ assert(NULL != io_std_device.out); switch (io_std_device.out->type) { case tt: SET_MUPINTR(mupintr, d_tt_struct); encrypted = FALSE; break; case rm: SET_MUPINTR(mupintr, d_rm_struct); /* Encryption only supported for files, pipes, and FIFOs. */ encrypted = IS_GTM_IMAGE ? ((d_rm_struct *)io_std_device.out->dev_sp)->output_encrypted : FALSE; break; case gtmsocket: SET_MUPINTR(mupintr, d_socket_struct); encrypted = FALSE; break; default: assert(FALSE); mupintr = encrypted = FALSE; } /* Do not use GT.M I/O if got MUPIP-interrupted. */ if (!mupintr) { /* The wteol() call may invoke write() under the covers, so the current device needs to be correct * for all calls below. */ save_io_curr_device = io_curr_device; io_curr_device = io_std_device; if ((NULL != text) && (0 != text->len)) { if (0 < io_std_device.out->dollar.x) { /* If we are going to write something before the newline and $X is currently * non-zero, then we need to insert one newline first. */ (io_std_device.out->disp_ptr->wteol)(1, io_std_device.out); } msg_start = text->addr; msg_length = text->len; /* Find all newlines and form feeds and convert them into appropriate GT.M I/O functions. */ for (i = 0; i < msg_length; i++) { c = *(msg_start + i); if ('\n' == c) { text->len = i - (text->addr - msg_start); (io_std_device.out->disp_ptr->write)(text); (io_std_device.out->disp_ptr->wteol)(1, io_std_device.out); text->addr += text->len + 1; } else if ('\f' == c) { text->len = i - (text->addr - msg_start); (io_std_device.out->disp_ptr->write)(text); (io_std_device.out->disp_ptr->wtff)(); text->addr += text->len + 1; } } text->len = i - (text->addr - msg_start); if (0 != text->len) { /* If we still have something to write, potentially after a form feed or newline, do * write it and follow up with a newline. */ (io_std_device.out->disp_ptr->write)(text); (io_std_device.out->disp_ptr->wteol)(1, io_std_device.out); } else if ('\f' != c) (io_std_device.out->disp_ptr->wteol)(1, io_std_device.out); } else (io_std_device.out->disp_ptr->wteol)(1, io_std_device.out); (io_std_device.out->disp_ptr->flush)(io_std_device.out); io_curr_device = save_io_curr_device; writing_to_pio--; return; } } else encrypted = FALSE; /* Encrypted messages can only be printed using GT.M I/O, and since mixing encrypted and plain-text messages results * in unencryptable content. */ if (!encrypted) { if ((NULL != text) && (0 != text->len)) { status = FPRINTF(stderr, "%.*s", text->len, text->addr); if (0 <= status) { c = *(text->addr + text->len - 1); if ('\f' != c) status = FPRINTF(stderr, "\n"); } } else status = FPRINTF(stderr, "\n"); if (0 <= status) { FFLUSH(stderr); writing_to_pio--; return; } } } prin_out_dev_failure = TRUE; assert(0 < writing_to_pio); writing_to_pio--; } fis-gtm-V7.0-005/sr_port/fnname.h0000755000032200000250000000105314342376331015462 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, Inc. * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #define FNLCL 0 #define FNGBL 1 #define FNEXTGBL1 2 #define FNEXTGBL2 4 #define FNVBAR 8 #define FNNAKGBL 32 fis-gtm-V7.0-005/sr_port/fnorder.h0000755000032200000250000000152614342376331015662 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef FNORDER_H_INCLUDED #define FNORDER_H_INCLUDED enum order_obj { GLOBAL = 0, LOCAL, LOCAL_NAME, INDIRECT, LAST_OBJECT }; enum order_dir { FORWARD = 0, BACKWARD, TBD, LAST_DIRECTION }; STATICFNDCL boolean_t set_opcode(triple *r, oprtype *result, oprtype *result_ptr, oprtype *second_opr, enum order_obj object); #endif fis-gtm-V7.0-005/sr_port/fnpc.h0000755000032200000250000000573514342376331015157 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef FNPC_INCLUDED #define FNPC_INCLUDED /* Note, FNPC_MAX should never exceed 254 since the value 255 is used to flag "invalid entry" */ #define FNPC_STRLEN_MIN 15 #define FNPC_MAX 50 #define FNPC_ELEM_MAX 81 #ifdef DEBUG GBLREF uint4 process_id; /* $[Z]PIECE() statistics */ GBLREF int c_miss; /* cache misses (debug) */ GBLREF int c_hit; /* cache hits (debug) */ GBLREF int c_small; /* scanned small string brute force */ GBLREF int c_small_pcs; /* chars scanned by small scan */ GBLREF int c_pskip; /* number of pieces "skipped" */ GBLREF int c_pscan; /* number of pieces "scanned" */ GBLREF int c_parscan; /* number of partial scans (partial cache hits) */ /* Flag we are doing SET $[Z]PIECE() and its statistics fields */ GBLREF boolean_t setp_work; /* The work we are doing is for set $piece */ GBLREF int cs_miss; /* cache misses (debug) */ GBLREF int cs_hit; /* cache hits (debug) */ GBLREF int cs_small; /* scanned small string brute force */ GBLREF int cs_small_pcs; /* chars scanned by small scan */ GBLREF int cs_pskip; /* number of pieces "skipped" */ GBLREF int cs_pscan; /* number of pieces "scanned" */ GBLREF int cs_parscan; /* number of partial scans (partial cache hits) */ GBLREF int c_clear; /* cleared due to (possible) value change */ # define COUNT_EVENT(x) if (setp_work) ++cs_##x; else ++c_##x; # define INCR_COUNT(x,y) if (setp_work) cs_##x += y; else c_##x += y; #else # define COUNT_EVENT(x) # define INCR_COUNT(x,y) #endif /* The delimiter argument to op_fnp1, opfnzp1, op_setp1, and op_setzp1 is * passed as an integer but contains 1-4 chars (zero filled). The UTF * versions are interested in all of them but the non-UTF versions are * only interested in the first char. */ typedef union { int unichar_val; unsigned char unibytes_val[4]; } delimfmt; typedef struct fnpc_struct { mstr last_str; /* The last string (addr/len) we used in cache */ int delim; /* delimiter used in $[z]piece */ int npcs; /* Number of pieces for which values are filled in */ int indx; /* The index of this piece */ boolean_t byte_oriented; /* True if byte oriented; False if (UTF) char oriented */ unsigned int pstart[FNPC_ELEM_MAX + 1]; /* Where each piece starts (last elem holds end of last piece) */ } fnpc; typedef struct { fnpc *fnpcsteal; /* Last stolen cache element */ fnpc *fnpcmax; /* (use addrs to avoid array indexing) */ fnpc fnpcs[FNPC_MAX]; } fnpc_area; #ifdef DEBUG void fnpc_stats(void); #endif #endif fis-gtm-V7.0-005/sr_port/fnpc_stats.c0000644000032200000250000000366114342376331016361 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2007-2015 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 "fnpc.h" #include "gtm_stdio.h" #ifdef DEBUG void fnpc_stats(void) { FPRINTF(stderr, "process id: %d\n", process_id); FPRINTF(stderr, "fnpc cache clears: %d\n", c_clear); FPRINTF(stderr, "Reference Piece:\n"); FPRINTF(stderr, " fnpc cache miss: %d\n", c_miss); FPRINTF(stderr, " fnpc cache hit: %d\n", c_hit); FPRINTF(stderr, " fnpc pieces skipped: %d\n", c_pskip); FPRINTF(stderr, " fnpc pieces scanned: %d\n", c_pscan); FPRINTF(stderr, " fnpc partial scans: %d\n", c_parscan); FPRINTF(stderr, " small string scans: %d\n", c_small); FPRINTF(stderr, " small str pcs scnd: %d\n", c_small_pcs); FPRINTF(stderr, "Set Piece:\n"); FPRINTF(stderr, " fnpc cache miss: %d\n", cs_miss); FPRINTF(stderr, " fnpc cache hit: %d\n", cs_hit); FPRINTF(stderr, " fnpc pieces skipped: %d\n", cs_pskip); FPRINTF(stderr, " fnpc pieces scanned: %d\n", cs_pscan); FPRINTF(stderr, " fnpc partial scans: %d\n", cs_parscan); FPRINTF(stderr, " small string scans: %d\n", cs_small); FPRINTF(stderr, " small str pcs scnd: %d\n", cs_small_pcs); } #endif fis-gtm-V7.0-005/sr_port/fntext_ch.c0000755000032200000250000000323614342376331016200 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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 "error.h" #include "op.h" /* for OP_TROLLBACK */ error_def(ERR_ASSERT); error_def(ERR_GTMASSERT); error_def(ERR_GTMASSERT2); error_def(ERR_GTMCHECK); error_def(ERR_MEMORY); error_def(ERR_OUTOFSPACE); error_def(ERR_STACKOFLOW); error_def(ERR_TPRETRY); error_def(ERR_VMSMEMORY); GBLREF uint4 dollar_tlevel; CONDITION_HANDLER(fntext_ch) { int tlevel; START_CH(TRUE); # ifdef GTM_TRIGGER tlevel = TREF(op_fntext_tlevel); TREF(op_fntext_tlevel) = 0; # endif if (!DUMPABLE && (SIGNAL != ERR_TPRETRY)) { # ifdef GTM_TRIGGER if (tlevel) { /* $TEXT was done on a trigger routine. Check if $tlevel is different from ESTABLISH time to UNWIND time */ tlevel--; /* get real tlevel */ if (tlevel != dollar_tlevel) { assert(tlevel < dollar_tlevel); OP_TROLLBACK(tlevel - dollar_tlevel); } } # endif UNWIND(NULL, NULL); /* As per the standard, $TEXT returns null string if there are errors while */ /* loading/linking with the entryref. So, we ignore non-fatal errors. */ } else { NEXTCH; /* But, we don't want to ignore fatal errors as these may be indicative of serious */ /* issues that may need investigation. Also, TP restarts need to be handled properly. */ } } fis-gtm-V7.0-005/sr_port/follow.h0000755000032200000250000000113614342376331015522 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef FOLLOW_H_INCLUDED #define FOLLOW_H_INCLUDED int follow(mval *, mval *); #endif fis-gtm-V7.0-005/sr_port/format2disp.c0000644000032200000250000000512114342376331016440 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2018 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 "patcode.h" #include "zshow.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" /* U_ISPRINT() needs this header */ #include "gtm_utf8.h" #endif GBLREF uint4 *pattern_typemask; GBLREF boolean_t gtm_utf8_mode; /* Routine to convert a string to displayable format. That is any nonprintable * characters are replaced by a dot (.) and the rest are used as is. No double-quote * additions happen like in format2zwr. Used currently by the trigger routines. * This function ensures the displayed part never goes more than "displen". * A "..." is inserted at the end if input string happens to be more than displen * can accommodate. The truncate length is returned in "displen" in that case. */ void format2disp(char *src, int src_len, char *dispbuff, int *displen) { char *c, *c_top, *nextc, *dst, *dst_top, *disptop; int chlen, dstlen, i; unsigned char ch; uint4 codepoint; boolean_t isctl, isill; dst = dispbuff; dstlen = *displen; if (0 > dstlen) dstlen = 0; disptop = dst + dstlen; assert(3 < dstlen); /* we expect caller to ensure this */ dstlen = (3 > dstlen)? 0 : ((dstlen < src_len) ? dstlen - 3 : dstlen); /* if neded adjust dstlen to account for ellipsis */ dst_top = dst + dstlen; for (c = src, c_top = c + src_len; c < c_top; ) { if (!gtm_utf8_mode) { ch = *c; isctl = (0 != (pattern_typemask[ch] & PATM_C)); isill = FALSE; chlen = 1; } # ifdef UTF8_SUPPORTED else { nextc = (char *)UTF8_MBTOWC(c, c_top, codepoint); isill = (WEOF == codepoint) ? (codepoint = *c, TRUE) : FALSE; isctl = (!isill ? !U_ISPRINT(codepoint) : TRUE); chlen = (int)(nextc - c); } # endif if ((dst + chlen) > dst_top) break; if (isctl) { /* control character */ for (i = 0; i < chlen; i++) *dst++ = '.'; c = c + chlen; } else { /* printable character (1 byte or > 1 byte) */ for (i = 0; i < chlen; i++) *dst++ = *c++; } } /* Add "..." if applicable */ if (c < c_top) { if (dst < disptop) *dst++ = '.'; if (dst < disptop) *dst++ = '.'; if (dst < disptop) *dst++ = '.'; } *displen = dst - dispbuff; } fis-gtm-V7.0-005/sr_port/format2zwr.c0000755000032200000250000001746414342376331016343 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_string.h" #include "mlkdef.h" #include "zshow.h" #include "patcode.h" #include "compiler.h" /* for CHARMAXARGS */ #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" /* U_ISPRINT() needs this header */ #include "gtm_utf8.h" #endif GBLREF uint4 *pattern_typemask; GBLREF boolean_t gtm_utf8_mode; /* Routine to convert a string to ZWRITE format. Used by the utiltities. * NOTE: this routine does almost the same formatting as mval_write(). The reason * for not using mval_write() is because it is much more complex than we need * here. Moreover, this version is more efficient due to the availability of * pre-allocated destination buffer */ int format2zwr(sm_uc_ptr_t src, int src_len, unsigned char *des, int *des_len) { sm_uc_ptr_t cp; uint4 ch; int fastate = 0, ncommas, dstlen, chlen, max_len; boolean_t isctl, isill; uchar_ptr_t srctop, strnext, tmpptr; max_len = *des_len; assert(0 < max_len); dstlen = *des_len = 0; if (src_len > 0) { srctop = src + src_len; fastate = 0; max_len--; /* Make space for the trailing quote or paren */ /* deals with the other characters */ for (cp = src; (cp < srctop) && (max_len > dstlen); cp += chlen) { if (!gtm_utf8_mode) { ch = *cp; isctl = ((pattern_typemask[ch] & PATM_C) != 0); isill = FALSE; chlen = 1; } #ifdef UTF8_SUPPORTED else { strnext = UTF8_MBTOWC(cp, srctop, ch); isill = (WEOF == ch) ? (ch = *cp, TRUE) : FALSE; if (!isill) isctl = !U_ISPRINT(ch); chlen = (int)(strnext - cp); } #endif switch(fastate) { case 0: /* beginning of the string */ case 1: /* beginning of a new substring followed by a graphic character */ if (isill) { assert(max_len > (dstlen + STR_LIT_LEN(DOLLARZCH) + MAX_ZWR_ZCHAR_DIGITS + ((dstlen) ? 2 : 0))); if (max_len > (dstlen + STR_LIT_LEN(DOLLARZCH) + MAX_ZWR_ZCHAR_DIGITS + ((dstlen) ? 2 : 0))) { if (dstlen > 0) { des[dstlen++] = '"'; des[dstlen++] = '_'; } MEMCPY_LIT(des + dstlen, DOLLARZCH); dstlen += STR_LIT_LEN(DOLLARZCH); I2A(des, dstlen, ch); fastate = 3; ncommas = 0; } else cp = srctop; /* Not enough space, terminate the loop */ break; } else if (isctl) { assert(max_len > (dstlen + STR_LIT_LEN(DOLLARCH) + MAX_ZWR_DCHAR_DIGITS + ((dstlen) ? 2 : 0))); if (max_len > (dstlen + STR_LIT_LEN(DOLLARCH) + MAX_ZWR_DCHAR_DIGITS + ((dstlen) ? 2 : 0))) { if (dstlen > 0) { /* close previous string with quote and prepare for concatenation */ des[dstlen++] = '"'; des[dstlen++] = '_'; } MEMCPY_LIT(des + dstlen, DOLLARCH); dstlen += STR_LIT_LEN(DOLLARCH); I2A(des, dstlen, ch); fastate = 2; ncommas = 0; } else cp = srctop; /* Not enough space, terminate the loop */ } else { /* graphic characters */ assert(max_len > (dstlen + 2 + ((!gtm_utf8_mode) ? 1 : chlen))); if (max_len > (dstlen + 2 + ((!gtm_utf8_mode) ? 1 : chlen))) { if (0 == fastate) /* the initial quote in the beginning */ { des[dstlen++] = '"'; fastate = 1; } if ('"' == ch) des[dstlen++] = '"'; if (!gtm_utf8_mode) des[dstlen++] = ch; else { memcpy(&des[dstlen], cp, chlen); dstlen += chlen; } } else cp = srctop; /* Not enough space, terminate the loop */ } break; case 2: /* subsequent characters following a non-graphic character in the form of $CHAR(x,) */ if (isill) { assert(max_len > (dstlen + STR_LIT_LEN(CLOSE_PAREN_DOLLARZCH) + MAX_ZWR_ZCHAR_DIGITS)); if (max_len > (dstlen + STR_LIT_LEN(CLOSE_PAREN_DOLLARZCH) + MAX_ZWR_ZCHAR_DIGITS)) { MEMCPY_LIT(des + dstlen, CLOSE_PAREN_DOLLARZCH); dstlen += STR_LIT_LEN(CLOSE_PAREN_DOLLARZCH); I2A(des, dstlen, ch); fastate = 3; } else cp = srctop; /* Not enough space, terminate the loop */ } else if(isctl) { ncommas++; assert(max_len > (dstlen + STR_LIT_LEN(CLOSE_PAREN_DOLLARCH) + MAX_ZWR_DCHAR_DIGITS)); if (max_len > (dstlen + STR_LIT_LEN(CLOSE_PAREN_DOLLARCH) + MAX_ZWR_DCHAR_DIGITS)) { if (CHARMAXARGS == ncommas) { ncommas = 0; MEMCPY_LIT(des + dstlen, CLOSE_PAREN_DOLLARCH); dstlen += STR_LIT_LEN(CLOSE_PAREN_DOLLARCH); } else { MEMCPY_LIT(des + dstlen, COMMA); dstlen += STR_LIT_LEN(COMMA); } I2A(des, dstlen, ch); } else cp = srctop; /* Not enough space, terminate the loop */ } else { assert(max_len > (dstlen + STR_LIT_LEN(CLOSE_PAREN_QUOTE) + 1 + ((!gtm_utf8_mode) ? 1 : chlen))); if (max_len > (dstlen + STR_LIT_LEN(CLOSE_PAREN_QUOTE) + 1 + ((!gtm_utf8_mode) ? 1 : chlen))) { MEMCPY_LIT(des + dstlen, CLOSE_PAREN_QUOTE); dstlen += STR_LIT_LEN(CLOSE_PAREN_QUOTE); if (!gtm_utf8_mode) des[dstlen++] = ch; else { memcpy(&des[dstlen], cp, chlen); dstlen += chlen; } if ('"' == ch) des[dstlen++] = '"'; fastate = 1; } else cp = srctop; /* Not enough space, terminate the loop */ } break; case 3: /* subsequent characters following an illegal character in the form of $ZCHAR(x,) */ if(isill) { assert(max_len > (dstlen + STR_LIT_LEN(CLOSE_PAREN_DOLLARZCH) + MAX_ZWR_ZCHAR_DIGITS)); if (max_len > (dstlen + STR_LIT_LEN(CLOSE_PAREN_DOLLARZCH) + MAX_ZWR_ZCHAR_DIGITS)) { ncommas++; if (CHARMAXARGS == ncommas) { ncommas = 0; MEMCPY_LIT(des + dstlen, CLOSE_PAREN_DOLLARZCH); dstlen += STR_LIT_LEN(CLOSE_PAREN_DOLLARZCH); } else { MEMCPY_LIT(des + dstlen, COMMA); ++dstlen; } I2A(des, dstlen, ch); } else cp = srctop; /* Not enough space, terminate the loop */ } else if (isctl) { assert(max_len > (dstlen + STR_LIT_LEN(CLOSE_PAREN_DOLLARCH) + MAX_ZWR_DCHAR_DIGITS)); if (max_len > (dstlen + STR_LIT_LEN(CLOSE_PAREN_DOLLARCH) + MAX_ZWR_DCHAR_DIGITS)) { MEMCPY_LIT(des + dstlen, CLOSE_PAREN_DOLLARCH); dstlen += STR_LIT_LEN(CLOSE_PAREN_DOLLARCH); I2A(des, dstlen, ch); fastate = 2; } else cp = srctop; /* Not enough space, terminate the loop */ } else { assert(max_len > (dstlen + STR_LIT_LEN(CLOSE_PAREN_QUOTE) + ((!gtm_utf8_mode) ? 1 : chlen) + ('"' == ch))); if (max_len > (dstlen + STR_LIT_LEN(CLOSE_PAREN_QUOTE) + ((!gtm_utf8_mode) ? 1 : chlen) + ('"' == ch))) { MEMCPY_LIT(des + dstlen, CLOSE_PAREN_QUOTE); dstlen += STR_LIT_LEN(CLOSE_PAREN_QUOTE); if (!gtm_utf8_mode) des[dstlen++] = ch; else { memcpy(&des[dstlen], cp, chlen); dstlen += chlen; } if ('"' == ch) des[dstlen++] = '"'; fastate = 1; } else cp = srctop; /* Not enough space, terminate the loop */ } break; default: assert(FALSE); break; } } /* close up */ switch(fastate) { case 1: des[dstlen++] = '"'; break; case 2: case 3: des[dstlen++] = ')'; break; default: assert(FALSE); break; } } else { des[0] = des[1] = '"'; dstlen = 2; } assertpro(max_len > dstlen); *des_len = dstlen; return 0; } fis-gtm-V7.0-005/sr_port/format_key_lv_val.c0000755000032200000250000000506514342376331017723 0ustar librarygtc/**************************************************************** * * * 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. * * * ****************************************************************/ /* *-------------------------------------------------------------------------- * Descriptipn: * Given a non-null lv_val *, this will format the subscripted local variable key * starting from the base variable lv_val *. * Input: * lvpin: Pointer to the last subscript's lv_val * buff : Buffer where key will be formatted * size : Size of buff * Return Value: * End address upto which buffer was used to format the key * (Needed for length calculation in caller) *-------------------------------------------------------------------------- */ #include "mdef.h" #include "lv_val.h" #include "gtm_string.h" #include "mvalconv.h" #include "promodemo.h" /* for "demote" prototype used in LV_NODE_GET_KEY */ unsigned char *format_key_lv_val(lv_val *lvpin, unsigned char *buff, int size) { boolean_t is_base_var; int cnt, cntfmt; lv_val *lv, *base_lv; mval tempmv; lvTree *lvt; lvTreeNode *node, *nodep[MAX_LVSUBSCRIPTS]; unsigned char *endbuff; if (NULL == lvpin) return buff; lv = lvpin; is_base_var = LV_IS_BASE_VAR(lv); base_lv = !is_base_var ? LV_GET_BASE_VAR(lv) : lv; cntfmt = 0; while (lv != base_lv) { assert(!LV_IS_BASE_VAR(lv)); nodep[cntfmt++] = (lvTreeNode *)lv; lvt = LV_GET_PARENT_TREE(lv); assert(NULL != lvt); assert(lvt->base_lv == base_lv); lv = (lv_val *)LVT_PARENT(lvt); assert(NULL != lv); } endbuff = format_lvname(base_lv, buff, size); size -= (int)(endbuff - buff); buff = endbuff; if (cntfmt) { if (size < 1) return buff; *buff++ = '('; size--; } for (cnt = cntfmt - 1; cnt >= 0; cnt--) { node = nodep[cnt]; LV_NODE_GET_KEY(node, &tempmv); /* Get node key into "tempmv" depending on the structure type of "node" */ MV_FORCE_STRD(&tempmv); if (size < tempmv.str.len) { /* copy as much space as we have */ memcpy(buff, tempmv.str.addr, size); buff += size; return buff; } memcpy(buff, tempmv.str.addr, tempmv.str.len); size -= tempmv.str.len; buff += tempmv.str.len; if (cnt) { if (size < 1) return buff; *buff++ = ','; size--; } } if (cntfmt) { if (size < 1) return buff; *buff++ = ')'; size--; } return buff; } fis-gtm-V7.0-005/sr_port/format_key_mvals.c0000755000032200000250000000404014342376331017552 0ustar librarygtc/**************************************************************** * * * 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. * * * ****************************************************************/ /* *-------------------------------------------------------------------------- * Descriptipn: * Given the lvname_info *, which contains all the mvals of all * subscripts and the name itself, of a local variable node, * this will format the entire local variable key * Input: * lvnp: Pointer to the structure of a local variable * buff: Buffer where key will be formatted * size: Size of buff * Return Value: * End address upto which buffer was used to format the key * (Needed for the length calculation in caller) *-------------------------------------------------------------------------- */ #include "mdef.h" #include "gtm_string.h" #include "lv_val.h" /* needed by "lv_nameinfo.h" */ unsigned char *format_key_mvals(unsigned char *buff, int size, lvname_info *lvnp) { int cnt; mval *keys; int n, subcnt; unsigned char *endbuff; cnt = (int)lvnp->total_lv_subs - 1; endbuff = format_lvname(lvnp->start_lvp, buff, size); size -= (int)(endbuff - buff); buff = endbuff; if (cnt > 0 && size > 0) { *buff++ = '('; size--; subcnt = 0; for (n = 0; ; ) { keys = lvnp->lv_subs[subcnt++]; MV_FORCE_STR(keys); if (size > (keys)->str.len) { memcpy(buff, (keys)->str.addr, (keys)->str.len); buff += (keys)->str.len; size -= (keys)->str.len; } else { /* copy as much space as we have */ memcpy(buff, (keys)->str.addr, size); buff += size; break; } if (++n < cnt && size > 0) { *buff++ = ','; size--; } else { if (size > 0) { *buff++ = ')'; size--; } break; } } } return buff; } fis-gtm-V7.0-005/sr_port/format_lvname.c0000644000032200000250000000351614342376333017050 0ustar librarygtc/**************************************************************** * * * 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. * * * ****************************************************************/ /* *--------------------------------------------------------------------------------- * Description: * Given lv_val * of the local variable name, format the local variable name string * * Input Parameter: * start: Pointer to the local variable name * buff: Buffer where key will be formatted * size: Size of buff * * Return Value: * End address upto which buffer was used to format the key * (Needed for the length calculation in caller) *--------------------------------------------------------------------------------- */ #include "mdef.h" #include "gtm_string.h" #include #include "stack_frame.h" #include "lv_val.h" #include "min_max.h" GBLREF stack_frame *frame_pointer; unsigned char *format_lvname(lv_val *startlv, unsigned char *buff, int size) { int i, len; ht_ent_mname **j; mident *vent; if (!startlv) return buff; if ((startlv >= (lv_val *)frame_pointer->temps_ptr) && (startlv <= (lv_val *)(frame_pointer->temps_ptr + frame_pointer->rvector->temp_size))) return buff; for (i = 0, j = frame_pointer->l_symtab; i < frame_pointer->vartab_len; i++, j++) { if (*j && (lv_val *)((*j)->value) == startlv) break; } if (i >= frame_pointer->vartab_len) return buff; vent = &(((var_tabent *)frame_pointer->vartab_ptr)[i].var_name); assert(vent->len <= MAX_MIDENT_LEN); len = MIN(size, vent->len); memcpy(buff, vent->addr, len); return buff + len; } fis-gtm-V7.0-005/sr_port/format_targ_key.c0000755000032200000250000000713014342376331017370 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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 "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "format_targ_key.h" #include "gvsub2str.h" /* return a pointer that points after the last char added */ unsigned char *format_targ_key(unsigned char *out_char_ptr, int4 max_size, gv_key *key, boolean_t dollarc) { unsigned char ch, *gvkey_char_ptr, *out_top, *work_char_ptr, work_buff[MAX_ZWR_KEY_SZ], *work_top; boolean_t is_string; mstr opstr; unsigned char *gvkey_top_ptr; assert(12 < max_size); out_top = out_char_ptr + max_size - 2; /* - 2, as could add comma left-paren or TWO double quotes between checks */ gvkey_char_ptr = key->base; gvkey_top_ptr = gvkey_char_ptr + key->end; /* Ensure input key is well-formed (i.e. double null terminated) */ assert(KEY_DELIMITER == *(gvkey_top_ptr - 1)); assert(KEY_DELIMITER == *gvkey_top_ptr); for (*out_char_ptr++ = '^'; (*out_char_ptr = *gvkey_char_ptr++) && (gvkey_char_ptr <= gvkey_top_ptr); out_char_ptr++) ; /* The following asserts (in the for loop) assume that a global name will be able to fit in completely into any key. */ assert(gvkey_char_ptr <= gvkey_top_ptr); if (0 == *gvkey_char_ptr) /* no subscipts */ return (out_char_ptr); *out_char_ptr++ = '('; for ( ; gvkey_char_ptr <= gvkey_top_ptr; ) { assert(gvkey_char_ptr <= gvkey_top_ptr); if (0x01 == *gvkey_char_ptr) /* this must be a null string which was adjusted by op_gvorder */ { *out_char_ptr++ = '"'; *out_char_ptr++ = '"'; } else { is_string = FALSE; if ((STR_SUB_PREFIX == *gvkey_char_ptr) && !dollarc) { is_string = TRUE; *out_char_ptr++ = '"'; } opstr.addr = (char *)work_buff; opstr.len = MAX_ZWR_KEY_SZ; work_top = gvsub2str(gvkey_char_ptr, &opstr, dollarc); if (!is_string) { for (work_char_ptr = work_buff; work_char_ptr < work_top;) { if (out_char_ptr >= out_top) { assert(FALSE); return (NULL); } *out_char_ptr++ = *work_char_ptr++; } } else { /* replace double-quote with TWO double-quotes since this subs is already double-quote-enclosed */ for (work_char_ptr = work_buff; work_char_ptr < work_top;) { if (out_char_ptr >= out_top) { assert(FALSE); return (NULL); } *out_char_ptr++ = (ch = *work_char_ptr++); if ('"' == ch) { if (out_char_ptr >= out_top) { assert(FALSE); return (NULL); } *out_char_ptr++ = ch; } } if (out_char_ptr >= out_top) { assert(FALSE); return (NULL); } *out_char_ptr++ = '"'; } } if (out_char_ptr >= out_top) { assert(FALSE); return (NULL); } /* Advance until next null separator */ for ( ; *gvkey_char_ptr++ && (gvkey_char_ptr <= gvkey_top_ptr); ) ; if (gvkey_char_ptr > gvkey_top_ptr) { assert(FALSE); return (NULL); } if (*gvkey_char_ptr) *out_char_ptr++ = ','; else break; } if((out_char_ptr >= out_top) || (gvkey_char_ptr > gvkey_top_ptr)) { assert(FALSE); return (NULL); } *out_char_ptr++ = ')'; return (out_char_ptr); } fis-gtm-V7.0-005/sr_port/format_targ_key.h0000755000032200000250000000123314342376331017373 0ustar librarygtc/**************************************************************** * * * 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. * * * ****************************************************************/ #ifndef FORMAT_TARG_KEY_INCLUDED #define FORMAT_TARG_KEY_INCLUDED unsigned char *format_targ_key(unsigned char *out_char_ptr, int4 max_size, gv_key *key, boolean_t dollarc); #endif /* FORMAT_TARG_KEY_INCLUDED */ fis-gtm-V7.0-005/sr_port/freecnt.mpt0000755000032200000250000000210314342376331016212 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 1989,2001 Sanchez Computer Associates, 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %FREECNT;GT.M %FREECNT utility - display database free blocks ; n rn,fn,fb,tb,%ZL i '$d(%zdebug) n $et s $et="zg "_$zl_":ERR^%FREECNT" u $p:(ctrap=$c(3):exc="zg "_$zl_":EXIT^%FREECNT") s rn=$view("GVFIRST") d head,show f s rn=$view("gvnext",rn) q:rn="" d show d EXIT q head ; w "Region",?16,"Free",?25,"Total",?40,"Database file",!,"------",?16,"----",?25,"-----",?40,"-------------",! q show ; s fn=$v("GVFILE",rn),fb=$v("FREEBLOCKS",rn),tb=$v("TOTALBLOCKS",rn) w rn,?12,$j(fb,8),?22,$j(tb,8)," (",$j(fb/tb*100.0,5,1),"%)",?40,fn,! q ERR w !,$p($zs,",",2,99),! s $ec="" ; Warning: Fall-through EXIT u $p:(ctrap="":exc="") q fis-gtm-V7.0-005/sr_port/fullbool.h0000644000032200000250000000311214342376331016027 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2018 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. * * * ****************************************************************/ #ifndef FULLBOOL_H_INCLUDED #define FULLBOOL_H_INCLUDED enum gtm_bool_type { GTM_BOOL = 0, /* original GT.M short-circuit Boolean evaluation with naked maintenance */ FULL_BOOL, /* standard behavior - evaluate everything with a side effect */ FULL_BOOL_WARN /* like FULL_BOOL but give compiler warnings when it makes a difference */ }; enum gtm_se_type { OLD_SE = 0, /* ignore side effect implications */ STD_SE, /* reorder argument processing for left-to-right side effects */ SE_WARN /* like STD but give compiler warnings when it makes a difference */ }; #define TRACK_JMP_TARGET(T, REF0) \ MBSTART { /* T is triple to tag; REF0 is the new target triple with which it's tagged */ \ tripbp = &T->jmplist; /* borrow jmplist to track jmp targets */ \ assert(NULL == tripbp->bpt); \ assert((tripbp == tripbp->que.fl) && (tripbp == tripbp->que.bl)); \ tripbp->bpt = REF0; /* point to the new location */ \ dqins(TREF(bool_targ_ptr), que, tripbp); /* queue jmplist for clean-up */ \ } MBEND #endif /* FULLBOOL_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/funsvn.h0000755000032200000250000000113114342376331015532 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ typedef struct { opctype opcode; bool can_set; char os_syst; } svn_data_type; typedef struct{ opctype opcode; char os_syst; } fun_data_type; fis-gtm-V7.0-005/sr_port/g.mpt0000755000032200000250000000446614342376331015030 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 1987, 2009 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %G ;GT.M %G utility - global lister ; n %in,%ZL,%ZD i '$d(%zdebug) n $et s $et="zg "_$zl_":ERR^%G" u $p:(ctrap=$c(3):exc="zg "_$zl_":LOOP^%G") f d q:$l(%ZD) . r !,"Output device: : ",%ZD,! . i '$l(%ZD) s %ZD=$p q . i %ZD="^" q . i %ZD="?" d q . . w !!,"Select the device you want for output" . . w !,"If you wish to exit enter a carat (^)",! . . s %ZD="" . i $zparse(%ZD)="" w " no such device" s %ZD="" q . o %ZD:(newversion:block=2048:record=2044:exception="g noopen"):0 . i '$t w !,%ZD," is not available" s %ZD="" q . q noopen . w !,$p($ZS,",",2,999),! c %ZD s %ZD="" q:%ZD="^" d base q base f r !,"List ^",%in,! q:%in="" d . i $e(%in)="?",$l(%in)=1 d help q . i (%in="?D")!(%in="?d") d ^%GD u $p:(ctrap=$c(3):exc="zg "_($zl-2)_":LOOP^%G") q . s:%in="*" %in="?.E(*)" . s:$p(%in,"(")="*" $p(%in,"(")="?.E" . s:$e(%in)'="^" %in="^"_%in . n $et s $et="ZG "_$ZL_":badzwr" . u %ZD zwr @%in u $p . q badzwr . u $p w !,$p($zs,",",3,99),! . s $ec="" d EXIT q help w !,"VALID INPUT",!! w !,?3,"",?16,"to leave the %G utility ",! w !,?4,"?D",?16,"to display existing globals in your directory ",! w !,"[global name]",?16,"the MUMPS name for the global e.g. ABC, or" w !?16,"a MUMPS pattern to match selected globals e.g. ?1""A"".E, or" w !?16,"""*"" as a wildcard for all globals" w !?16,"the global name may be followed by: " w !?16,"subscript(s) in parentheses" w !?16,"a subscript is a MUMPS expression e.g. ""joe"",10,$e(a,1)," w !?16,"a ""*"" as a subscript causes all descendents to be included," w !?16,"or by a range of subscripts in parentheses" w !?16,"expressed as [expr]:[expr] e.g 1:10 ""a"":""d""",! q ERR u $p w !,$p($zs,",",2,99),! s $ecode="" ; Warning - Fall-through EXIT i $d(%ZD),%ZD'=$p c %ZD u $p:(ctrap="":exc="") q LOOP if 1'=$zeof d base q fis-gtm-V7.0-005/sr_port/gbldef.mpt0000755000032200000250000001023414342376331016013 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2001-2020 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %gbldef ; ; ;Global Collation Control ; kill(gname) new $ETRAP,blkidlen,label,reg set $ETRAP="goto error" if $TLEVEL zmessage 150383202 ; TPNOSUPPORT error if '$$edit(.gname) quit 0 if $zfind($view("REGION",gname),",") zmessage 150383194:gname ; ISSPANGBL if "BGMM"'[$view("GVACCESS_METHOD",$view("REGION",gname)) do . zmessage 150376418:$view("REGION",gname) ; DBREMOTE if $data(@gname) zmessage 150373626 ; Error if there is data in the global set @gname="" kill @gname ; make sure that the global has a root set reg=$view("REGION",gname),label=$extract($$^%PEEKBYNAME("sgmnt_data.label",reg),1,11) set blkidlen=$select(label="GDSDYNUNX03":4,1:8) ; if DB is V6 use 4 byte length else use 8 set gname=$zextract(gname,2,32) ; remove circumflex, take at most 31 chars view "YDIRTVAL":$zextract($view("YDIRTREE",gname),1,blkidlen),"YDIRTREE":gname quit 1 ; set(gname,nct,act) new $ETRAP,blkidlen,label,reg,ver set $ETRAP="goto error" if $TLEVEL zmessage 150383202 ; TPNOSUPPORT error if '$$edit(.gname) q 0 if $zfind($view("REGION",gname),",") zmessage 150383194:gname ; ISSPANGBL error if "BGMM"'[$view("GVACCESS_METHOD",$view("REGION",gname)) do . zmessage 150376418:$view("REGION",gname) ; DBREMOTE error if $data(@gname) zmessage 150373626 ; Error if there is data in the global set act=+$g(act),nct=+$g(nct) set:nct nct=1 if (act>255)!(act<0) zmessage 150374290:act ; collation type specified is illegal set ver=$view("YCOLLATE",act) if ver<0 zmessage 150376282:act ; doesn't find coll type, or can't get version set @gname="" kill @gname ; make sure that the global has a root set reg=$view("REGION",gname),label=$extract($$^%PEEKBYNAME("sgmnt_data.label",reg),1,11) set blkidlen=$select(label="GDSDYNUNX03":4,1:8) ; if DB is V6 use 4 byte length else use 8 set gname=$zextract(gname,2,32) ; remove circumflex, take at most 31 chars view "YDIRTVAL":$zextract($view("YDIRTREE",gname),1,blkidlen)_$zchar(1,nct,act,ver),"YDIRTREE":gname quit 1 ; get(gname,reg) new t,tl,$ETRAP,nct,act,ver,ret,dir,dl,error,label,blkidend set $ETRAP="goto error" if '$$edit(.gname) quit 0 set:$get(reg)="" reg=$piece($view("REGION",gname),",",1) set label=$extract($$^%PEEKBYNAME("sgmnt_data.label",reg),1,11) set blkidend=$select(label="GDSDYNUNX03":5,1:9) ; if DB is V6 use 4 byte length else use 8 set gname=$zextract(gname,2,32) if "BGMM"'[$view("GVACCESS_METHOD",reg) zmessage 150376418:reg ; DBREMOTE ; ----------------------------------- ; first check in directory tree set dir=$view("YDIRTREE",gname,reg),dl=$zlength(dir) ; the next line only works for V7 DBs and will need to be modified to work with V6 as well set t=$zextract(dir,blkidend,999),tl=$zlength(t) ; remove circumflex, take at most 31 chars if tl,(tl'=4)!($ascii(t,1)'=1) zmessage 150374058 if tl set nct=$ascii(t,2),act=$ascii(t,3),ver=$ascii(t,4) quit nct_","_act_","_ver ; ----------------------------------- ; db directory tree has no collation information. next check for gld. set t=$view("YGLDCOLL","^"_gname) if t'="0" set nct=$piece(t,",",1),act=$piece(t,",",2),ver=$piece(t,",",3) quit nct_","_act_","_ver ; ----------------------------------- ; no collation information was found in gld. check for coll info from db file hdr set nct=0 do quit:(act=0) 0 . do getzpeek("sgmnt_data.def_coll",reg,.act) . do getzpeek("sgmnt_data.def_coll_ver",reg,.ver) quit nct_","_act_","_ver ; getzpeek(field,region,result) new xstr set result=$$^%PEEKBYNAME(field,region) quit edit(gname) if $zextract(gname)'="^" set gname="^"_gname if $zextract(gname,2)'="%",$zextract(gname,2)'?1A zmessage 150373218 ; LKNAMEXPECTED if gname'?1"^"1E.AN zmessage 150373218 quit 1 ; error set $ECODE="",error=1 quit 0 fis-gtm-V7.0-005/sr_port/gbldefs.c0000644000032200000250000020006614342376333015623 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* General repository for global variable definitions. This keeps us from * pulling in modules and all their references when all we wanted was the * global data def. * * Note, all GBLDEF fields are automatically cleared to zeroes. No initialization to * zero (0) or NULL or any other value that translates to zeroes is necessary on these * fields and should be avoided as it creates extra linker and image startup processing * that can only slow things down even if only by a little. */ #include "mdef.h" #include "gtm_inet.h" #include "gtm_iconv.h" #include "gtm_socket.h" #include "gtm_unistd.h" #include "gtm_limits.h" #include "gtm_un.h" #include "gtm_pwd.h" #include "gtm_signal.h" #include #include "cache.h" #include "gtm_multi_thread.h" #include "hashtab_addr.h" #include "hashtab_int4.h" #include "hashtab_int8.h" #include "hashtab_mname.h" #include "hashtab_str.h" #include "hashtab_objcode.h" /* The define of CHEXPAND below causes error.h to create GBLDEFs */ #define CHEXPAND #include "error.h" #include #include "gdsroot.h" #include "gdskill.h" #include "ccp.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "comline.h" #include "compiler.h" #include "cmd_qlf.h" #include "io.h" #include "iosp.h" #include "jnl.h" #include "lv_val.h" #include "mdq.h" #include "mprof.h" #include "mv_stent.h" #include "stack_frame.h" #include "stp_parms.h" #include "stringpool.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "tp_frame.h" #include "mlkdef.h" #include "zshow.h" #include "zwrite.h" #include "zbreak.h" #include "mmseg.h" #include "gtmsiginfo.h" #include "gtmimagename.h" #include "gt_timer.h" #include "iosocketdef.h" /* needed for socket_pool and MAX_N_SOCKETS */ #include "ctrlc_handler_dummy.h" #include "unw_prof_frame_dummy.h" #include "op.h" #include "gtmsecshr.h" #include "error_trap.h" #include "patcode.h" /* for pat_everything and sizeof_pat_everything */ #include "source_file.h" /* for REV_TIME_BUFF_LEN */ #include "mupipbckup.h" #include "dpgbldir.h" #include "mmemory.h" #include "have_crit.h" #include "alias.h" #include "ztimeout_routines.h" /* FOR REPLICATION RELATED GLOBALS */ #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" /* FOR MERGE RELATED GLOBALS */ #include "gvname_info.h" #include "op_merge.h" #include "cli.h" #include "invocation_mode.h" #include "fgncal.h" #include "repl_sem.h" #include "gtm_zlib.h" #include "anticipatory_freeze.h" #include "mu_rndwn_all.h" #include "jnl_typedef.h" #include "repl_ctl.h" #include "gds_blk_upgrade.h" /* for UPGRADE_IF_NEEDED flag */ #include "cws_insert.h" /* for CWS_REORG_ARRAYSIZE */ #include "gtm_multi_proc.h" #include "fnpc.h" #ifdef UTF8_SUPPORTED #include "gtm_icu_api.h" #include "gtm_utf8.h" #include "gtm_conv.h" #include "utfcgr.h" #endif #include "gtmcrypt.h" #include "gdsblk.h" #include "muextr.h" #include "gtmxc_types.h" #ifdef GTM_TLS #include "gtm_tls_interface.h" #endif #ifdef GTM_TRIGGER #include "gv_trigger.h" #include "gtm_trigger.h" #endif #ifdef DEBUG #include "wcs_wt.h" #endif #define DEFAULT_ZERROR_STR "Unprocessed $ZERROR, see $ZSTATUS" #define DEFAULT_ZERROR_LEN (SIZEOF(DEFAULT_ZERROR_STR) - 1) #include "gtm_libaio.h" #include "deferred_events_queue.h" #include "deferred_events.h" #include "dm_audit_log.h" GBLDEF gd_region *db_init_region; GBLDEF sgmnt_data_ptr_t cs_data; GBLDEF sgmnt_addrs *cs_addrs; GBLDEF sgmnt_addrs *cs_addrs_list; /* linked list of csa corresponding to all currently open databases */ GBLDEF unsigned short proc_act_type; GBLDEF volatile bool ctrlc_pending; GBLDEF bool undef_inhibit; GBLDEF bool out_of_time; GBLDEF io_pair io_curr_device; /* current device */ GBLDEF io_pair io_std_device; /* standard device */ GBLDEF io_log_name *dollar_principal; /* pointer to log name GTM$PRINCIPAL if defined */ GBLDEF boolean_t prin_dm_io; /* used by op_dmode so mdb_condition_handler and iorm_readfl know it's active */ GBLDEF boolean_t prin_in_dev_failure; /* used in I/O to perform NOPRINCIO detection on input */ GBLDEF boolean_t prin_out_dev_failure; /* used in I/O to perform NOPRINCIO detection on output */ GBLDEF boolean_t wcs_noasyncio; /* used in wcs_wtstart to disable asyncio in the forward rollback/recover phase */ GBLDEF io_desc *active_device; GBLDEF bool pin_shared_memory; GBLDEF bool hugetlb_shm_enabled; GBLDEF bool error_mupip, file_backed_up, gv_replopen_error, gv_replication_error, incremental, jobpid, online, record, std_dev_outbnd, in_mupip_freeze, in_backup, view_debug1, view_debug2, view_debug3, view_debug4, mupip_error_occurred, dec_nofac; GBLDEF boolean_t is_updproc, mupip_jnl_recover, suspend_lvgcol, run_time, unhandled_stale_timer_pop, gtcm_connection, is_replicator, /* TRUE => this process can write jnl records to the jnlpool for replicated db */ dollar_truth = TRUE, gtm_stdxkill, /* TRUE => Use M Standard X-KILL - FALSE use historical GTM X-KILL (default) */ in_timed_tn; /* TRUE => Timed TP transaction in progress */ GBLDEF uint4 is_updhelper; /* = UPD_HELPER_READER if reader helper, = UPD_HELPER_WRITER if writer helper, * = 0 otherwise. */ GBLDEF volatile boolean_t tp_timeout_set_xfer; /* TRUE => A timeout succeeded in setting xfer table intercepts. This flag stays * a true global unless each thread gets its own xfer table. */ GBLDEF VSIG_ATOMIC_T forced_exit; /* Asynchronous signal/interrupt handler sets this variable to TRUE, * hence the VSIG_ATOMIC_T type in the definition. */ GBLDEF intrpt_state_t intrpt_ok_state = INTRPT_OK_TO_INTERRUPT; /* any other value implies it is not ok to interrupt */ GBLDEF unsigned char *msp, *stackbase, *stacktop, *stackwarn; GBLDEF int4 backup_close_errno, backup_write_errno, mubmaxblk, forced_exit_err, exit_state, restore_read_errno; GBLDEF volatile int4 outofband; GBLDEF int mumps_status = SS_NORMAL; GBLDEF uint4 stp_array_size; GBLDEF gvzwrite_datablk *gvzwrite_block; GBLDEF lvzwrite_datablk *lvzwrite_block; GBLDEF io_log_name *io_root_log_name; GBLDEF mliteral literal_chain; GBLDEF mstr *comline_base, *err_act, **stp_array, extnam_str, env_gtm_env_xlate; GBLDEF MSTR_CONST(default_sysid, "gtm_sysid"); GBLDEF mval dollar_zgbldir, dollar_zsource = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0), dollar_zstatus, ztrap_pop2level = DEFINE_MVAL_STRING(MV_NM | MV_INT, 0, 0, 0, 0, 0, 0), dollar_system, dollar_system_initial, dollar_estack_delta = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0), dollar_zerror = DEFINE_MVAL_STRING(MV_STR, 0, 0, DEFAULT_ZERROR_LEN, DEFAULT_ZERROR_STR, 0, 0), dollar_zyerror, dollar_ztexit = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0); GBLDEF uint4 dollar_zjob; GBLDEF mval dollar_zinterrupt; GBLDEF volatile boolean_t dollar_zininterrupt; GBLDEF boolean_t dollar_ztexit_bool; /* Truth value of dollar_ztexit when coerced to boolean */ GBLDEF boolean_t dollar_zquit_anyway; GBLDEF mv_stent *mv_chain; GBLDEF sgm_info *first_sgm_info; /* List of participating regions in the TP transaction with NO ftok ordering */ GBLDEF sgm_info *first_tp_si_by_ftok; /* List of participating regions in the TP transaction sorted on ftok order */ GBLDEF spdesc indr_stringpool, rts_stringpool, stringpool; GBLDEF stack_frame *frame_pointer; GBLDEF stack_frame *zyerr_frame; GBLDEF symval *curr_symval; GBLDEF tp_frame *tp_pointer; GBLDEF tp_region *halt_ptr, *grlist; GBLDEF trans_num local_tn; /* transaction number for THIS PROCESS (starts at 0 each time) */ GBLDEF trans_num tstart_local_tn; /* copy of global variable "local_tn" at op_tstart time */ GBLDEF gv_namehead *gv_target; GBLDEF gv_namehead *gv_target_list; /* List of ALL gvts that were allocated (in targ_alloc) by this process */ GBLDEF gv_namehead *gvt_tp_list; /* List of gvts that were referenced in the current TP transaction */ GBLDEF gvt_container *gvt_pending_list; /* list of gvts that need to be re-examined/re-allocated when region is opened */ GBLDEF buddy_list *gvt_pending_buddy_list;/* buddy_list for maintaining memory for gv_targets to be re-examined/allocated */ GBLDEF buddy_list *noisolation_buddy_list; /* a buddy_list for maintaining the globals that are noisolated */ GBLDEF int4 exi_condition; GBLDEF uint4 gtmDebugLevel; GBLDEF boolean_t gtmSystemMalloc; GBLDEF int process_exiting; GBLDEF int4 dollar_zsystem; GBLDEF int4 dollar_zeditor; GBLDEF rtn_tabent *rtn_fst_table, *rtn_names, *rtn_names_top, *rtn_names_end; GBLDEF int4 break_message_mask; GBLDEF bool rc_locked; GBLDEF boolean_t certify_all_blocks; /* If flag is set all blocks are checked after they are * written to the database. Upon error we stay critical * and report. This flag can be set via the MUMPS command * VIEW "GDSCERT":1. */ GBLDEF gd_addr *original_header; GBLDEF hash_table_str *complits_hashtab; GBLDEF hash_table_str *compsyms_hashtab; GBLDEF mem_list *mem_list_head; GBLDEF boolean_t debug_mupip; GBLDEF unsigned char t_fail_hist[CDB_MAX_TRIES]; /* type has to be unsigned char and not enum cdb_sc to ensure single byte */ GBLDEF cache_rec_ptr_t cr_array[((MAX_BT_DEPTH * 2) - 1) * 2]; /* Maximum number of blocks that can be in transaction */ GBLDEF unsigned int cr_array_index; GBLDEF boolean_t need_core; /* Core file should be created */ GBLDEF boolean_t created_core; /* core file was created */ GBLDEF unsigned int core_in_progress; /* creating core NOW if > 0 */ GBLDEF boolean_t dont_want_core; /* Higher level flag overrides need_core set by lower level rtns */ GBLDEF boolean_t exit_handler_active; /* recursion prevention */ GBLDEF boolean_t skip_exit_handler; /* set for processes that are usually forked off and so should not do gds_rundown */ GBLDEF boolean_t block_saved; GBLDEF gtm_chset_t dse_over_chset = CHSET_M; LITDEF MIDENT_DEF(zero_ident, 0, NULL); /* the null mident */ GBLDEF int4 aligned_source_buffer[MAX_SRCLINE / SIZEOF(int4) + 1]; GBLDEF src_line_struct src_head; GBLDEF int source_column; GBLDEF bool devctlexp; GBLDEF char cg_phase; /* code generation phase */ /* Previous code generation phase: Only used by emit_code.c to initialize the push list at the * beginning of each phase (bug fix: C9D12-002478) */ GBLDEF char cg_phase_last; GBLDEF int cmd_cnt; GBLDEF command_qualifier glb_cmd_qlf = { CQ_DEFAULT }, cmd_qlf = { CQ_DEFAULT }; #ifdef __osf__ #pragma pointer_size (save) #pragma pointer_size (long) #endif GBLDEF char **cmd_arg; #ifdef __osf__ #pragma pointer_size (restore) #endif GBLDEF boolean_t oldjnlclose_started; /* DEFERRED EVENTS */ GBLDEF bool licensed = TRUE; GBLDEF volatile int4 fast_lock_count; /* Used in wcs_stale */ /* REPLICATION RELATED GLOBALS */ GBLDEF gtmsource_options_t gtmsource_options; GBLDEF gtmrecv_options_t gtmrecv_options; GBLDEF boolean_t is_tracing_on; GBLDEF void (*tp_timeout_start_timer_ptr)(int4 tmout_sec) = tp_start_timer_dummy; GBLDEF void (*tp_timeout_clear_ptr)(void) = tp_clear_timeout_dummy; GBLDEF void (*tp_timeout_action_ptr)(void) = tp_timeout_action_dummy; GBLDEF void (*ctrlc_handler_ptr)(void) = ctrlc_handler_dummy; GBLDEF int (*op_open_ptr)(mval *v, mval *p, mval *t, mval *mspace) = op_open_dummy; GBLDEF void (*unw_prof_frame_ptr)(void) = unw_prof_frame_dummy; /* Initialized only in gtm_startup() */ GBLDEF void (*jnl_file_close_timer_ptr)(void); GBLDEF void (*fake_enospc_ptr)(void); GBLDEF void (*simple_timeout_timer_ptr)(TID tid, int4 hd_len, boolean_t **timedout); #ifdef UTF8_SUPPORTED GBLDEF u_casemap_t gtm_strToTitle_ptr; /* Function pointer for gtm_strToTitle */ #endif GBLDEF boolean_t mu_reorg_process; /* set to TRUE by MUPIP REORG */ GBLDEF boolean_t mu_reorg_in_swap_blk; /* set to TRUE for the duration of the call to "mu_swap_blk" */ GBLDEF boolean_t mu_rndwn_process; GBLDEF gv_key *gv_currkey_next_reorg; GBLDEF gv_namehead *reorg_gv_target; GBLDEF struct sockaddr_un gtmsecshr_sock_name; GBLDEF struct sockaddr_un gtmsecshr_cli_sock_name; GBLDEF key_t gtmsecshr_key; GBLDEF int gtmsecshr_sockpath_len; GBLDEF int gtmsecshr_cli_sockpath_len; GBLDEF mstr gtmsecshr_pathname; GBLDEF int server_start_tries; GBLDEF int gtmsecshr_sockfd = FD_INVALID; GBLDEF boolean_t gtmsecshr_sock_init_done; GBLDEF char muext_code[MUEXT_MAX_TYPES][2] = { # define MUEXT_TABLE_ENTRY(muext_rectype, code0, code1) {code0, code1}, # include "muext_rec_table.h" # undef MUEXT_TABLE_ENTRY }; GBLDEF int patch_is_fdmp; GBLDEF int patch_fdmp_recs; GBLDEF boolean_t horiz_growth; GBLDEF int4 prev_first_off, prev_next_off; /* these two globals store the values of first_off and next_off in cse, * when there is a blk split at index level. This is to permit rollback * to intermediate states */ GBLDEF sm_uc_ptr_t min_mmseg; GBLDEF sm_uc_ptr_t max_mmseg; GBLDEF mmseg *mmseg_head; GBLDEF ua_list *first_ua, *curr_ua; GBLDEF char *update_array, *update_array_ptr; GBLDEF int gv_fillfactor = 100, rc_set_fragment; /* Contains offset within data at which data fragment starts */ GBLDEF uint4 update_array_size, cumul_update_array_size; /* the current total size of the update array */ GBLDEF kill_set *kill_set_tail; GBLDEF int pool_init; GBLDEF boolean_t is_src_server; GBLDEF boolean_t is_rcvr_server; GBLDEF jnl_format_buffer *non_tp_jfb_ptr; GBLDEF boolean_t dse_running; GBLDEF jnlpool_addrs_ptr_t jnlpool; GBLDEF jnlpool_addrs_ptr_t jnlpool_head; GBLDEF recvpool_addrs recvpool; GBLDEF int recvpool_shmid = INVALID_SHMID; GBLDEF int gtmsource_srv_count; GBLDEF int gtmrecv_srv_count; /* The following _in_prog counters are needed to prevent deadlocks while doing jnl-qio (timer & non-timer). */ GBLDEF volatile int4 db_fsync_in_prog; GBLDEF volatile int4 jnl_qio_in_prog; GBLDEF gtmsiginfo_t signal_info; #ifndef MUTEX_MSEM_WAKE GBLDEF int mutex_sock_fd = FD_INVALID; GBLDEF struct sockaddr_un mutex_sock_address; GBLDEF struct sockaddr_un mutex_wake_this_proc; GBLDEF int mutex_wake_this_proc_len; GBLDEF int mutex_wake_this_proc_prefix_len; GBLDEF fd_set mutex_wait_on_descs; #endif GBLDEF void (*call_on_signal)(); GBLDEF enum gtmImageTypes image_type; /* initialized at startup i.e. in dse.c, lke.c, gtm.c, mupip.c, gtmsecshr.c etc. */ GBLDEF parmblk_struct *param_list; /* call-in parameters block (defined in unix/fgncalsp.h)*/ GBLDEF unsigned int invocation_mode = MUMPS_COMPILE; /* how mumps has been invoked */ GBLDEF char cli_err_str[MAX_CLI_ERR_STR] = ""; /* Parse Error message buffer */ GBLDEF char *cli_err_str_ptr; GBLDEF boolean_t gtm_pipe_child; GBLDEF io_desc *gtm_err_dev; GBLDEF int num_additional_processors; GBLDEF int gtm_errno = -1; /* holds the errno (unix) in case of an rts_error */ GBLDEF int4 error_condition; GBLDEF int4 pre_drvlongjmp_error_condition; GBLDEF global_tlvl_info *global_tlvl_info_head; GBLDEF buddy_list *global_tlvl_info_list; GBLDEF boolean_t job_try_again; GBLDEF volatile int4 gtmMallocDepth; /* Recursion indicator */ GBLDEF d_socket_struct *socket_pool; GBLDEF boolean_t mu_star_specified; GBLDEF backup_reg_list *mu_repl_inst_reg_list; GBLDEF volatile int suspend_status = NO_SUSPEND; GBLDEF gv_namehead *reset_gv_target = INVALID_GV_TARGET; GBLDEF VSIG_ATOMIC_T util_interrupt; GBLDEF sgmnt_addrs *kip_csa; GBLDEF boolean_t need_kip_incr; GBLDEF int merge_args; GBLDEF merge_glvn_ptr mglvnp; GBLDEF boolean_t ztrap_new; GBLDEF int4 wtfini_in_prog; #ifdef DEBUG /* Items for $piece stats */ GBLDEF int c_miss; /* cache misses (debug) */ GBLDEF int c_hit; /* cache hits (debug) */ GBLDEF int c_small; /* scanned small string brute force */ GBLDEF int c_small_pcs; /* chars scanned by small scan */ GBLDEF int c_pskip; /* number of pieces "skipped" */ GBLDEF int c_pscan; /* number of pieces "scanned" */ GBLDEF int c_parscan; /* number of partial scans (partial cache hits) */ GBLDEF int cs_miss; /* cache misses (debug) */ GBLDEF int cs_hit; /* cache hits (debug) */ GBLDEF int cs_small; /* scanned small string brute force */ GBLDEF int cs_small_pcs; /* chars scanned by small scan */ GBLDEF int cs_pskip; /* number of pieces "skipped" */ GBLDEF int cs_pscan; /* number of pieces "scanned" */ GBLDEF int cs_parscan; /* number of partial scans (partial cache hits) */ GBLDEF int c_clear; /* cleared due to (possible) value change */ GBLDEF boolean_t setp_work; #ifdef UTF8_SUPPORTED /* Items for UTF8 cache */ GBLDEF int u_miss; /* UTF cache misses (debug) */ GBLDEF int u_hit; /* UTF cache hits (debug) */ GBLDEF int u_small; /* UTF scanned small string brute force (debug) */ GBLDEF int u_pskip; /* Number of UTF groups "skipped" (debug) */ GBLDEF int u_puscan; /* Number of groups "scanned" for located char (debug) */ GBLDEF int u_pabscan; /* Number of non-UTF groups we scan for located char (debug) */ GBLDEF int u_parscan; /* Number of partial scans (partial cache hits) (debug) */ GBLDEF int u_parhscan; /* Number of partial scans after filled slots (debug) */ #endif /* UTF8_SUPPORTED */ #endif /* DEBUG */ GBLDEF z_records zbrk_recs; GBLDEF ipcs_mesg db_ipcs; /* For requesting gtmsecshr to update ipc fields */ GBLDEF gd_region *ftok_sem_reg; /* Last region for which ftok semaphore is grabbed */ GBLDEF int gtm_non_blocked_write_retries; /* number of retries for non-blocked write to pipe */ GBLDEF boolean_t write_after_image; /* true for after-image jnlrecord writing by recover/rollback */ GBLDEF int4 write_filter; GBLDEF boolean_t need_no_standalone; GBLDEF int4 zdir_form = ZDIR_FORM_FULLPATH; /* $ZDIR shows full path including DEVICE and DIRECTORY */ GBLDEF mval dollar_zdir = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0); GBLDEF int * volatile var_on_cstack_ptr; /* volatile pointer to int; volatile so that nothing gets optimized out */ GBLDEF hash_table_int8 cw_stagnate; GBLDEF boolean_t cw_stagnate_reinitialized; GBLDEF uint4 pat_everything[] = { 0, 2, PATM_E, 1, 0, PAT_MAX_REPEAT, 0, PAT_MAX_REPEAT, 1 }; /* pattern = ".e" */ GBLDEF mstr_len_t sizeof_pat_everything = SIZEOF(pat_everything); GBLDEF uint4 *pattern_typemask; GBLDEF pattern *pattern_list; GBLDEF pattern *curr_pattern; /* UTF8 related data */ GBLDEF boolean_t gtm_utf8_mode; /* Is GT.M running with UTF8 Character Set; Set only after ICU initialization */ GBLDEF boolean_t is_gtm_chset_utf8; /* Is gtm_chset environment variable set to UTF8 */ GBLDEF boolean_t utf8_patnumeric; /* Should patcode N match non-ASCII numbers in pattern match ? */ GBLDEF boolean_t badchar_inhibit; /* Suppress malformed UTF-8 characters by default */ GBLDEF MSTR_DEF(dollar_zchset, 1, "M"); GBLDEF MSTR_DEF(dollar_zpatnumeric, 1, "M"); GBLDEF MSTR_DEF(dollar_zpin, 3, "< /"); GBLDEF MSTR_DEF(dollar_zpout, 3, "> /"); GBLDEF MSTR_DEF(dollar_prin_log, 1, "0"); /* Standard MUMPS pattern-match table. * This table holds the current pattern-matching attributes of each ASCII character. * Bits 0..23 of each entry correspond with the pattern-match characters, A..X. */ GBLDEF pattern mumps_pattern = { (void *) 0, /* flink */ (void *) 0, /* typemask */ (void *) 0, /* pat YZ name array */ (void *) 0, /* pat YZ name-length array */ -1, /* number of YZ patcodes */ 1, /* namlen */ {'M', '\0'} /* name */ }; /* mapbit is used by pattab.c and patstr.c. Note that patstr.c uses only entries until PATM_X */ GBLDEF readonly uint4 mapbit[] = { PATM_A, PATM_B, PATM_C, PATM_D, PATM_E, PATM_F, PATM_G, PATM_H, PATM_I, PATM_J, PATM_K, PATM_L, PATM_M, PATM_N, PATM_O, PATM_P, PATM_Q, PATM_R, PATM_S, PATM_T, PATM_U, PATM_V, PATM_W, PATM_X, PATM_YZ1, PATM_YZ2, PATM_YZ3, PATM_YZ4 }; LITDEF uint4 typemask[PATENTS] = { PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 00-07 : ASCII characters */ PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 08-0F : ASCII characters */ PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 10-17 : ASCII characters */ PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 18-1F : ASCII characters */ PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex 20-27 : ASCII characters */ PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex 28-2F : ASCII characters */ PATM_N, PATM_N, PATM_N, PATM_N, PATM_N, PATM_N, PATM_N, PATM_N, /* hex 30-37 : ASCII characters */ PATM_N, PATM_N, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex 38-3F : ASCII characters */ PATM_P, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, /* hex 40-47 : ASCII characters */ PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, /* hex 48-4F : ASCII characters */ PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, /* hex 50-57 : ASCII characters */ PATM_U, PATM_U, PATM_U, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex 58-5F : ASCII characters */ PATM_P, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, /* hex 60-67 : ASCII characters */ PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, /* hex 68-6F : ASCII characters */ PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, /* hex 70-77 : ASCII characters */ PATM_L, PATM_L, PATM_L, PATM_P, PATM_P, PATM_P, PATM_P, PATM_C, /* hex 78-7F : ASCII characters */ PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 80-87 : non-ASCII characters */ PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 88-8F : non-ASCII characters */ PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 90-97 : non-ASCII characters */ PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 98-9F : non-ASCII characters */ PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex A0-A7 : non-ASCII characters */ PATM_P, PATM_P, PATM_L, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex A8-AF : non-ASCII characters */ PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex B0-B7 : non-ASCII characters */ PATM_P, PATM_P, PATM_L, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex B8-BF : non-ASCII characters */ PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, /* hex C0-C7 : non-ASCII characters */ PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, /* hex C8-CF : non-ASCII characters */ PATM_P, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, /* hex D0-D7 : non-ASCII characters */ PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_P, PATM_L, /* hex D8-DF : non-ASCII characters */ PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, /* hex E0-E7 : non-ASCII characters */ PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, /* hex E8-EF : non-ASCII characters */ PATM_P, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, /* hex F0-F7 : non-ASCII characters */ PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_P, PATM_C /* hex F8-FF : non-ASCII characters */ }; GBLDEF uint4 pat_allmaskbits; /* universal set of valid pattern bit codes for currently active pattern table */ /* globals related to caching of pattern evaluation match result. * for a given tuple, we store the evaluation result. * in the above, * uniquely identifies a substring of the input string. * identifies the pattern atom that we are matching with * identifies the recursion depth of do_patalt() for this pattern atom ("repcnt" in code) * note that is a necessity in the above because the same alternation pattern atom can have different * match or not-match status for the same input string depending on the repetition count usage of the pattern atom * after a series of thoughts on an efficient structure for storing pattern evaluation, finally arrived at a simple * array of structures wherein for a given length (strlen) we have a fixed number of structures available. * we allocate an array of structures, say, 1024 structures. * this is a simple 1-1 mapping, wherein * for length 0, the available structures are the first 32 structures of the array, * for length 1, the available structures are the second 32 structures of the array. * ... * for length 47, the available structures are the 47th 32 structures of the array. * for length 48 and above, the available structures are all the remaining structures of the array. * whenever any new entry needs to be cached and there is no room among the available structures, we preempt the * most unfrequently used cache entry (to do this we do keep a count of every entry's frequency of usage) * the assumption is that substrings of length > 48 (an arbitrary reasonable small number) won't be used * so frequently so that they have lesser entries to fight for among themselves than lower values of length. * with the above caching in place, the program segment below took 15 seconds. * it was found that if the array size is increased to 16384 (as opposed to 1024 as above) and the available * structures for each length increased proportionally (i.e. 16 times = 16*32 structures instead of 32 as above) * the performance improved to the extent of taking 3 seconds. * but this raised an interesting question, that of "size" vs. "time" tradeoff. * with increasing array size, we get better "time" performance due to better caching. * but that has an overhead of increased "size" (memory) usage. * to arrive at a compromise, a dynamic algorithm emerged. the process will allocate a small array * beginning at 1024 entries and grow to a max of 16384 entries as and when it deems the hit ratio is not good. * the array only grows, i.e. there is no downsizing algorithm at play. * the dynamic algorithm addresses to an extent both the "size" and "time" issues and finishes the below in 1 second. * #defines for the dynamic algorithm growth can be found in patcode.h */ GBLDEF int4 curalt_depth = -1; /* depth of alternation nesting */ GBLDEF int4 do_patalt_calls[PTE_MAX_CURALT_DEPTH]; /* number of calls to do_patalt() */ GBLDEF int4 do_patalt_hits[PTE_MAX_CURALT_DEPTH]; /* number of pte_csh hits in do_patalt() */ GBLDEF int4 do_patalt_maxed_out[PTE_MAX_CURALT_DEPTH]; /* no. of pte_csh misses after maxing on allocation size */ GBLDEF pte_csh *pte_csh_array[PTE_MAX_CURALT_DEPTH]; /* pte_csh array (per curalt_depth) */ GBLDEF int4 pte_csh_cur_size[PTE_MAX_CURALT_DEPTH]; /* current pte_csh size (per curalt_depth) */ GBLDEF int4 pte_csh_alloc_size[PTE_MAX_CURALT_DEPTH]; /* current allocated pte_csh size (per curalt_depth) */ GBLDEF int4 pte_csh_entries_per_len[PTE_MAX_CURALT_DEPTH]; /* current number of entries per len */ GBLDEF int4 pte_csh_tail_count[PTE_MAX_CURALT_DEPTH]; /* count of non 1-1 corresponding pte_csh_array members */ GBLDEF pte_csh *cur_pte_csh_array; /* copy of pte_csh_array corresponding to curalt_depth */ GBLDEF int4 cur_pte_csh_size; /* copy of pte_csh_cur_size corresponding to curalt_depth */ GBLDEF int4 cur_pte_csh_entries_per_len; /* copy of pte_csh_entries_per_len corresponding to curalt_depth */ GBLDEF int4 cur_pte_csh_tail_count; /* copy of pte_csh_tail_count corresponding to curalt_depth */ GBLDEF readonly char *before_image_lit[] = {"NOBEFORE_IMAGES", "BEFORE_IMAGES"}; GBLDEF readonly char *jnl_state_lit[] = {"DISABLED", "OFF", "ON"}; GBLDEF readonly char *repl_state_lit[] = {"OFF", "ON", "WAS_ON"}; GBLDEF boolean_t crit_sleep_expired; /* mutex.mar: signals that a timer waiting for crit has expired */ GBLDEF uint4 crit_deadlock_check_cycle; /* compared to csa->crit_check_cycle to determine if a given region * in a transaction legitimately has crit or not */ GBLDEF node_local_ptr_t locknl; /* if non-NULL, indicates node-local of interest to the LOCK_HIST macro */ GBLDEF boolean_t in_mutex_deadlock_check; /* if TRUE, mutex_deadlock_check() is part of our current C-stack trace */ /* $ECODE and $STACK related variables. * error_frame and skip_error_ret should ideally be part of dollar_ecode structure. since sr_avms/opp_ret.m64 uses these * global variables and it was felt risky changing it to access a member of a structure, they are kept as separate globals */ GBLDEF stack_frame *error_frame; /* ptr to frame where last error occurred or was rethrown */ GBLDEF boolean_t skip_error_ret; /* set to TRUE by golevel(), used and reset by op_unwind() */ GBLDEF dollar_ecode_type dollar_ecode; /* structure containing $ECODE related information */ GBLDEF dollar_stack_type dollar_stack; /* structure containing $STACK related information */ GBLDEF boolean_t ztrap_explicit_null; /* whether $ZTRAP was explicitly set to NULL in the current frame */ GBLDEF int4 gtm_object_size; /* Size of entire gtm object for compiler use */ GBLDEF int4 linkage_size; /* Size of linkage section during compile */ GBLDEF uint4 lnkrel_cnt; /* number of entries in linkage Psect to relocate */ GBLDEF int4 sym_table_size; /* size of the symbol table during compilation */ GBLDEF boolean_t stop_non_mandatory_expansion, non_mandatory_expansion; /* Used in stringpool managment */ GBLDEF jnl_fence_control jnl_fence_ctl; GBLDEF jnl_process_vector *prc_vec; /* for current process */ GBLDEF jnl_process_vector *originator_prc_vec; /* for client/originator */ LITDEF char *jrt_label[JRT_RECTYPES] = { #define JNL_TABLE_ENTRY(rectype, extract_rtn, label, update, fixed_size, is_replicated) label, #include "jnl_rec_table.h" /* BYPASSOK */ #undef JNL_TABLE_ENTRY }; LITDEF int jrt_update[JRT_RECTYPES] = { #define JNL_TABLE_ENTRY(rectype, extract_rtn, label, update, fixed_size, is_replicated) update, #include "jnl_rec_table.h" /* BYPASSOK */ #undef JNL_TABLE_ENTRY }; LITDEF boolean_t jrt_fixed_size[JRT_RECTYPES] = { #define JNL_TABLE_ENTRY(rectype, extract_rtn, label, update, fixed_size, is_replicated) fixed_size, #include "jnl_rec_table.h" /* BYPASSOK */ #undef JNL_TABLE_ENTRY }; LITDEF boolean_t jrt_is_replicated[JRT_RECTYPES] = { #define JNL_TABLE_ENTRY(rectype, extract_rtn, label, update, fixed_size, is_replicated) is_replicated, #include "jnl_rec_table.h" /* BYPASSOK */ #undef JNL_TABLE_ENTRY }; LITDEF char *jnl_file_state_lit[JNL_FILE_STATES] = { "JNL_FILE_UNREAD", "JNL_FILE_OPEN", "JNL_FILE_CLOSED", "JNL_FILE_EMPTY" }; /* Change the initialization if struct_jrec_tcom in jnl.h changes */ GBLDEF struct_jrec_tcom tcom_record = {{JRT_TCOM, TCOM_RECLEN, 0, 0, 0, 0}, {0}, 0, 0, 0, "", {TCOM_RECLEN, JNL_REC_SUFFIX_CODE}}; GBLDEF jnl_gbls_t jgbl; GBLDEF short crash_count; GBLDEF trans_num start_tn; GBLDEF cw_set_element cw_set[CDB_CW_SET_SIZE]; GBLDEF unsigned char cw_set_depth, cw_map_depth; GBLDEF unsigned int t_tries; GBLDEF uint4 t_err; GBLDEF uint4 update_trans; /* Bitmask indicating among other things whether this region was updated; * See gdsfhead.h for UPDTRNS_* bitmasks * Bit-0 is 1 if cw_set_depth is non-zero or if it is a duplicate set * (cw_set_depth is zero in that case). * Bit-1 is unused for non-TP. * Bit-2 is 1 if transaction commit in this region is beyond point of rollback. */ GBLDEF boolean_t is_uchar_wcs_code[] = /* uppercase failure codes that imply database cache related problem */ { /* if any of the following failure codes are seen in the final retry, wc_blocked will be set to trigger cache recovery */ #define CDB_SC_NUM_ENTRY(code, final_retry_ok, value) #define CDB_SC_UCHAR_ENTRY(code, final_retry_ok, is_wcs_code, value) is_wcs_code, #define CDB_SC_LCHAR_ENTRY(code, final_retry_ok, is_wcs_code, value) #include "cdb_sc_table.h" /* BYPASSOK */ #undef CDB_SC_NUM_ENTRY #undef CDB_SC_UCHAR_ENTRY #undef CDB_SC_LCHAR_ENTRY }; GBLDEF int sizeof_is_uchar_wcs_code = SIZEOF(is_uchar_wcs_code); /* 4SCA */ GBLDEF boolean_t is_lchar_wcs_code[] = /* lowercase failure codes that imply database cache related problem */ { /* if any of the following failure codes are seen in the final retry, wc_blocked will be set to trigger cache recovery */ #define CDB_SC_NUM_ENTRY(code, final_retry_ok, value) #define CDB_SC_UCHAR_ENTRY(code, final_retry_ok, is_wcs_code, value) #define CDB_SC_LCHAR_ENTRY(code, final_retry_ok, is_wcs_code, value) is_wcs_code, #include "cdb_sc_table.h" /* BYPASSOK */ #undef CDB_SC_NUM_ENTRY #undef CDB_SC_UCHAR_ENTRY #undef CDB_SC_LCHAR_ENTRY }; GBLDEF int sizeof_is_lchar_wcs_code = SIZEOF(is_lchar_wcs_code); /* 4SCA */ GBLDEF boolean_t is_final_retry_code_num[] = /* failure codes that are possible in final retry : numeric */ { #define CDB_SC_NUM_ENTRY(code, final_retry_ok, value) final_retry_ok, #define CDB_SC_UCHAR_ENTRY(code, final_retry_ok, is_wcs_code, value) #define CDB_SC_LCHAR_ENTRY(code, final_retry_ok, is_wcs_code, value) #include "cdb_sc_table.h" /* BYPASSOK */ #undef CDB_SC_NUM_ENTRY #undef CDB_SC_UCHAR_ENTRY #undef CDB_SC_LCHAR_ENTRY }; GBLDEF int sizeof_is_final_retry_code_num = SIZEOF(is_final_retry_code_num); /* 4SCA */ GBLDEF boolean_t is_final_retry_code_uchar[] = /* failure codes that are possible in final retry : upper case */ { #define CDB_SC_NUM_ENTRY(code, final_retry_ok, value) #define CDB_SC_UCHAR_ENTRY(code, final_retry_ok, is_wcs_code, value) final_retry_ok, #define CDB_SC_LCHAR_ENTRY(code, final_retry_ok, is_wcs_code, value) #include "cdb_sc_table.h" /* BYPASSOK */ #undef CDB_SC_NUM_ENTRY #undef CDB_SC_UCHAR_ENTRY #undef CDB_SC_LCHAR_ENTRY }; GBLDEF int sizeof_is_final_retry_code_uchar = SIZEOF(is_final_retry_code_uchar); /* 4SCA */ GBLDEF boolean_t is_final_retry_code_lchar[] = /* failure codes that are possible in final retry : lower case */ { #define CDB_SC_NUM_ENTRY(code, final_retry_ok, value) #define CDB_SC_UCHAR_ENTRY(code, final_retry_ok, is_wcs_code, value) #define CDB_SC_LCHAR_ENTRY(code, final_retry_ok, is_wcs_code, value) final_retry_ok, #include "cdb_sc_table.h" /* BYPASSOK */ #undef CDB_SC_NUM_ENTRY #undef CDB_SC_UCHAR_ENTRY #undef CDB_SC_LCHAR_ENTRY }; GBLDEF int sizeof_is_final_retry_code_lchar = SIZEOF(is_final_retry_code_lchar); /* 4SCA */ GBLDEF boolean_t gvdupsetnoop = TRUE; /* if TRUE, duplicate SETs do not change GDS block (and therefore no PBLK journal * records will be written) although the database transaction number will be * incremented and logical SET journal records will be written. By default, this * behavior is turned ON. GT.M has a way of turning it off with a VIEW command. */ GBLDEF volatile boolean_t in_wcs_recover; /* TRUE if in "wcs_recover", used by "bt_put" and "generic_exit_handler" */ GBLDEF boolean_t in_gvcst_incr; /* set to TRUE by gvcst_incr, set to FALSE by gvcst_put * distinguishes to gvcst_put, if the current db operation is a SET or $INCR */ GBLDEF mval *post_incr_mval; /* mval pointing to the post-$INCR value */ GBLDEF mval increment_delta_mval; /* mval holding the INTEGER increment value, set by gvcst_incr, * used by gvcst_put/gvincr_recompute_upd_array which is invoked by t_end */ GBLDEF boolean_t is_dollar_incr; /* valid only if gvcst_put is in the call-stack (i.e. t_err == ERR_GVPUTFAIL); * is a copy of "in_gvcst_incr" just before it got reset to FALSE */ GBLDEF int indir_cache_mem_size; /* Amount of memory currently in use by indirect cache */ GBLDEF hash_table_objcode cache_table; GBLDEF int cache_hits, cache_fails; /* The alignment feature is disabled due to some issues in stringpool garbage collection. * TODO: When we sort out stringpool issues, change mstr_native_align to TRUE below */ GBLDEF boolean_t mstr_native_align; GBLDEF boolean_t save_mstr_native_align; GBLDEF mvar *mvartab; GBLDEF mvax *mvaxtab,*mvaxtab_end; GBLDEF mlabel *mlabtab; GBLDEF mline mline_root; GBLDEF mline *mline_tail; GBLDEF triple t_orig; GBLDEF int mvmax, mlmax, mlitmax; static char routine_name_buff[SIZEOF(mident_fixed)], module_name_buff[SIZEOF(mident_fixed)]; static char int_module_name_buff[SIZEOF(mident_fixed)]; GBLDEF MIDENT_DEF(routine_name, 0, &routine_name_buff[0]); GBLDEF MIDENT_DEF(module_name, 0, &module_name_buff[0]); GBLDEF MIDENT_DEF(int_module_name, 0, &int_module_name_buff[0]); GBLDEF char rev_time_buf[REV_TIME_BUFF_LEN]; GBLDEF unsigned short source_name_len; GBLDEF short object_name_len; GBLDEF unsigned char source_file_name[MAX_FN_LEN + 1]; GBLDEF unsigned char object_file_name[MAX_FN_LEN + 1]; GBLDEF int object_file_des; GBLDEF int4 curr_addr, code_size; GBLDEF mident_fixed zlink_mname; GBLDEF sm_uc_ptr_t reformat_buffer; GBLDEF int reformat_buffer_len; GBLDEF volatile int reformat_buffer_in_use; /* used only in DEBUG mode */ GBLDEF boolean_t mu_reorg_upgrd_dwngrd_in_prog; /* TRUE if MUPIP REORG UPGRADE/DOWNGRADE is in progress */ GBLDEF boolean_t mu_reorg_nosafejnl; /* TRUE if NOSAFEJNL explicitly specified */ GBLDEF trans_num mu_reorg_upgrd_dwngrd_blktn; /* tn in blkhdr of current block processed by MUPIP REORG {UP,DOWN}GRADE */ GBLDEF inctn_opcode_t inctn_opcode = inctn_invalid_op; GBLDEF inctn_detail_t inctn_detail; /* holds detail to fill in to inctn jnl record */ GBLDEF uint4 region_open_count; /* Number of region "opens" we have executed */ GBLDEF uint4 gtm_blkupgrade_flag = UPGRADE_IF_NEEDED; /* by default upgrade only if necessary */ GBLDEF boolean_t disk_blk_read; GBLDEF boolean_t gtm_dbfilext_syslog_disable; /* by default, log every file extension message */ GBLDEF int4 cws_reorg_remove_index; /* see mu_swap_blk.c for comments on the need for these two */ GBLDEF block_id cws_reorg_remove_array[CWS_REORG_REMOVE_ARRAYSIZE]; GBLDEF uint4 log_interval; GBLDEF uint4 gtm_principal_editing_defaults; /* ext_cap flags if tt */ GBLDEF enum db_ver gtm_db_create_ver; /* database creation version */ GBLDEF boolean_t in_repl_inst_edit; /* used by an assert in repl_inst_read/repl_inst_write */ GBLDEF boolean_t in_repl_inst_create; /* used by repl_inst_read/repl_inst_write */ GBLDEF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; /* whether a particular replication semaphore is being held * by the current process or not. */ GBLDEF boolean_t detail_specified; /* Set to TRUE if -DETAIL is specified in MUPIP REPLIC -JNLPOOL or -EDITINST */ GBLDEF boolean_t in_mupip_ftok; /* Used by an assert in repl_inst_read */ GBLDEF uint4 section_offset; /* Used by PRINT_OFFSET_PREFIX macro in repl_inst_dump.c */ GBLDEF uint4 mutex_per_process_init_pid; /* pid that invoked "mutex_per_process_init" */ GBLDEF boolean_t gtm_quiet_halt; /* Suppress FORCEDHALT message */ #ifdef UTF8_SUPPORTED /* UTF8 line terminators. In addition to the following * codepoints, the sequence CR LF is considered a single * line terminator. */ LITDEF UChar32 u32_line_term[] = { 0x000A, /* Line Feed */ 0x000D, /* Carraige Return */ 0x0085, /* Next Line - EBCDIC mostly */ 0x000C, /* Form Feed */ UTF_LINE_SEPARATOR, /* Line Separator */ UTF_PARA_SEPARATOR, /* Paragraph Separator */ 0x0000 }; /* Given the first byte in a UTF-8 representation, the following array returns the total number of bytes in the encoding * 00-7F : 1 byte * C2-DF : 2 bytes * E0-EF : 3 bytes * F0-F4 : 4 bytes * All others: 1 byte * For details on the UTF-8 encoding see gtm_utf8.h */ LITDEF unsigned int utf8_bytelen[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00-1F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20-3F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40-5F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60-7F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80-9F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0-BF */ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* C0-DF */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* E0-FF */ }; /* Given the first byte in a UTF-8 representation, the following array returns the number of bytes to follow * 00-7F : 0 byte * C2-DF : 1 byte * E0-EF : 2 bytes * F0-F4 : 3 bytes * All others: -1 bytes * For details on the UTF-8 encoding see gtm_utf8.h */ LITDEF signed int utf8_followlen[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00-1F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20-3F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40-5F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60-7F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-9F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-BF */ -1,-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* C0-DF */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-FF */ }; GBLDEF gtm_wcswidth_fnptr_t gtm_wcswidth_fnptr; /* see comment in gtm_utf8.h about this typedef */ #endif GBLDEF uint4 gtm_max_sockets; /* Maximum sockets per socket device supported by this process */ GBLDEF d_socket_struct *newdsocket; /* Commonly used temp socket area */ GBLDEF boolean_t dse_all_dump; /* TRUE if DSE ALL -DUMP is specified */ GBLDEF int socketus_interruptus; /* How many times socket reads have been interrutped */ GBLDEF int4 pending_errtriplecode; /* if non-zero contains the error code to invoke ins_errtriple with */ GBLDEF uint4 process_id; GBLDEF uid_t user_id = INVALID_UID, effective_user_id = INVALID_UID; GBLDEF gid_t group_id = INVALID_GID, effective_group_id = INVALID_GID; GBLDEF struct passwd getpwuid_struct = {NULL, NULL, INVALID_UID, INVALID_GID, NULL, NULL, NULL}; /* cached copy of "getpwuid" to try avoid future system calls for the same "uid" */ GBLDEF uint4 image_count; /* not used in UNIX but defined to preserve VMS compatibility */ GBLDEF size_t totalRmalloc; /* Total storage currently (real) malloc'd (includes extent blocks) */ GBLDEF size_t totalAlloc; /* Total allocated (includes allocation overhead but not free space */ GBLDEF size_t totalUsed; /* Sum of user allocated portions (totalAlloc - overhead) */ GBLDEF size_t totalRallocGta; /* Total storage currently (real) mmap alloc'd */ GBLDEF size_t totalAllocGta; /* Total mmap allocated (includes allocation overhead but not free space */ GBLDEF size_t totalUsedGta; /* Sum of "in-use" portions (totalAllocGta - overhead) */ GBLDEF volatile char *outOfMemoryMitigation; /* Cache that we will freed to help cleanup if run out of memory */ GBLDEF uint4 outOfMemoryMitigateSize; /* Size of above cache (in Kbytes) */ GBLDEF int mcavail; GBLDEF mcalloc_hdr *mcavailptr, *mcavailbase; GBLDEF uint4 max_cache_memsize; /* Maximum bytes used for indirect cache object code */ GBLDEF uint4 max_cache_entries; /* Maximum number of cached indirect compilations */ GBLDEF void (*cache_table_relobjs)(void); /* Function pointer to call cache_table_rebuild() */ GBLDEF ch_ret_type (*ht_rhash_ch)(); /* Function pointer to hashtab_rehash_ch */ GBLDEF ch_ret_type (*jbxm_dump_ch)(); /* Function pointer to jobexam_dump_ch */ GBLDEF ch_ret_type (*stpgc_ch)(); /* Function pointer to stp_gcol_ch */ #ifdef DEBUG GBLDEF ch_ret_type (*t_ch_fnptr)(); /* Function pointer to t_ch */ GBLDEF ch_ret_type (*dbinit_ch_fnptr)(); /* Function pointer to dbinit_ch */ #endif GBLDEF cache_rec_ptr_t pin_fail_cr; /* Pointer to the cache-record that we failed while pinning */ GBLDEF cache_rec pin_fail_cr_contents; /* Contents of the cache-record that we failed while pinning */ GBLDEF cache_rec_ptr_t pin_fail_twin_cr; /* Pointer to twin of the cache-record that we failed to pin */ GBLDEF cache_rec pin_fail_twin_cr_contents; /* Contents of twin of the cache-record that we failed to pin */ GBLDEF bt_rec_ptr_t pin_fail_bt; /* Pointer to bt of the cache-record that we failed to pin */ GBLDEF bt_rec pin_fail_bt_contents; /* Contents of bt of the cache-record that we failed to pin */ GBLDEF int4 pin_fail_in_crit; /* Holder of crit at the time we failed to pin */ GBLDEF int4 pin_fail_wc_in_free; /* Number of write cache records in free queue when we failed to pin */ GBLDEF int4 pin_fail_wcs_active_lvl; /* Number of entries in active queue when we failed to pin */ GBLDEF int4 pin_fail_ref_cnt; /* Reference count when we failed to pin */ GBLDEF int4 pin_fail_in_wtstart; /* Count of processes in wcs_wtstart when we failed to pin */ GBLDEF int4 pin_fail_phase2_commit_pidcnt; /* Number of processes in phase2 commit when we failed to pin */ GBLDEF zwr_hash_table *zwrhtab; /* How we track aliases during zwrites */ GBLDEF uint4 zwrtacindx; /* When creating $ZWRTACxxx vars for ZWRite, this holds xxx */ GBLDEF uint4 tstartcycle; /* lv_val cycle for tstart operations */ GBLDEF uint4 lvtaskcycle; /* lv_val cycle for misc lv_val related tasks */ GBLDEF int4 SPGC_since_LVGC; /* stringpool GCs since the last lv_val GC */ GBLDEF int4 LVGC_interval = MIN_SPGC_PER_LVGC; /* dead data GC is done every LVGC_interval stringpool GCs */ GBLDEF lv_xnew_var *xnewvar_anchor; /* Anchor for unused lv_xnew_var blocks */ GBLDEF lv_xnew_ref *xnewref_anchor; /* Anchor for unused lv_xnew_ref blocks */ GBLDEF mval *alias_retarg; /* Points to an alias return arg created by a "QUIT *" recorded here so * symtab-unwind logic can find it and modify it if necessary if a * symtab popped during the return and the retarg points to an lv_val * that is going to be destroyed. */ #ifdef DEBUG_ALIAS GBLDEF boolean_t lvmon_enabled; /* Enable lv_val monitoring */ #endif GBLDEF block_id gtm_tp_allocation_clue; /* block# hint to start allocation for created blocks in TP */ GBLDEF int4 gtm_zlib_cmp_level; /* zlib compression level specified at process startup */ GBLDEF int4 repl_zlib_cmp_level; /* zlib compression level currently in use in replication pipe. * This is a source-server specific variable and is non-zero only * if compression is enabled and works in the receiver server as well. */ GBLDEF zlib_cmp_func_t zlib_compress_fnptr; GBLDEF zlib_uncmp_func_t zlib_uncompress_fnptr; GBLDEF mlk_stats_t mlk_stats; /* Process-private M-lock statistics */ /* Initialized blockalrm, block_ttinout and block_sigsent can be used by all threads */ GBLDEF boolean_t blocksig_initialized; /* set to TRUE when blockalrm and block_sigsent are initialized */ GBLDEF sigset_t blockalrm; GBLDEF sigset_t block_ttinout; GBLDEF sigset_t block_sigsent; /* block all signals that can be sent externally * (SIGINT, SIGQUIT, SIGTERM, SIGTSTP, SIGCONT) */ GBLDEF sigset_t block_worker; /* block all signals for use by the linux AIO worker thread, except for a few * fatal signals that can be internally generated inside the thread. This way * any signal externally sent always gets handled by the main process and not * by the worker thread. */ GBLDEF sigset_t block_sigusr; GBLDEF char *gtm_core_file; GBLDEF char *gtm_core_putenv; #ifdef __MVS__ GBLDEF char *gtm_utf8_locale_object; GBLDEF boolean_t gtm_tag_utf8_as_ascii = TRUE; #endif /* Encryption-related fields. */ LITDEF char gtmcrypt_repeat_msg[] = "Please look at prior messages related to encryption for more details"; GBLDEF char *gtmcrypt_badhash_size_msg; GBLDEF boolean_t gtmcrypt_initialized; /* Set to TRUE if gtmcrypt_init() completes successfully */ GBLDEF char dl_err[MAX_ERRSTR_LEN]; GBLDEF mstr pvt_crypt_buf; /* Temporary buffer if in-place encryption / decryption is not an option */ LITDEF gtm_string_t null_iv = {0, ""}; GBLDEF uint4 mu_reorg_encrypt_in_prog; /* Reflects whether MUPIP REORG -ENCRYPT is in progress */ GBLDEF sgmnt_addrs *reorg_encrypt_restart_csa; /* Pointer to the region which caused a transaction restart due to a * concurrent MUPIP REORG -ENCRYPT */ #ifdef DEBUG /* Following definitions are related to white_box testing */ GBLDEF boolean_t gtm_white_box_test_case_enabled; GBLDEF int gtm_white_box_test_case_number; GBLDEF int gtm_white_box_test_case_count; GBLDEF int gtm_wbox_input_test_case_count; /* VMS allows maximum 31 characters for external identifer */ GBLDEF boolean_t stringpool_unusable; /* Set to TRUE by any function that does not expect any of its function * callgraph to use/expand the stringpool. */ GBLDEF boolean_t stringpool_unexpandable; /* Set to TRUE by any function for a small period when it has ensured * enough space in the stringpool so it does not expect any more garbage * collections or expansions. */ GBLDEF boolean_t donot_INVOKE_MUMTSTART; /* Set to TRUE whenever an implicit TSTART is done in gvcst_put/kill as * part of an explicit + trigger update. In this situation, we don't expect * MUM_TSTART macro to be invoked at all (see skip_INVOKE_RESTART below * for description on why this is needed). So we keep this debug-only * flag turned on throughout the gvcst_put/kill. An assert in * mdb_condition_handler (invoked by INVOKE_RESTART macro when it does * an rts_error) checks it never gets invoked while this is set. */ GBLDEF char asccurtime[10]; /* used in deferred_events.h by macro SHOWTIME timestamping debug output */ #endif GBLDEF boolean_t block_is_free; /* Set to TRUE if the caller wants to let t_qread know that the block it is * attempting to read is actually a FREE block */ GBLDEF int4 gv_keysize; GBLDEF gd_addr *gd_header; #ifdef GTM_TRIGGER GBLDEF int4 gtm_trigger_depth; /* 0 if no trigger, 1 if inside trigger; 2 if inside nested trigger etc. */ GBLDEF int4 tstart_trigger_depth; /* gtm_trigger_depth at the time of the outermost "op_tstart" * (i.e. explicit update). This should be used only if dollar_tlevel * is non-zero as it is not otherwise maintained. */ GBLDEF uint4 trigger_name_cntr; /* Counter from which trigger names are constructed */ GBLDEF boolean_t *ztvalue_changed_ptr; /* -> boolean in current gtm_trigger_parms signaling if ztvalue has * been updated */ GBLDEF boolean_t ztwormhole_used; /* TRUE if $ztwormhole was used by trigger code */ GBLDEF mstr *dollar_ztname; GBLDEF mval *dollar_ztdata, *dollar_ztdelim, *dollar_ztoldval, *dollar_ztriggerop, *dollar_ztupdate, *dollar_ztvalue, dollar_ztwormhole = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0), dollar_ztslate = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0); GBLDEF int tprestart_state; /* When triggers restart, multiple states possible. See tp_restart.h */ GBLDEF boolean_t skip_INVOKE_RESTART; /* set to TRUE if caller of op_tcommit/t_retry does not want it to * use the INVOKE_RESTART macro (which uses an rts_error to trigger * the restart) instead return code. The reason we don't want to do * rts_error is that this is an implicit tstart situation where we * did not do opp_tstart.s so we don't want control to be transferred * using the MUM_TSTART macro by mdb_condition_handler (which assumes * opp_tstart.s invocation). */ GBLDEF boolean_t goframes_unwound_trigger; /* goframes() unwound a trigger base frame during its unwinds */ GBLDEF symval *trigr_symval_list; /* List of availalable symvals for use in (nested) triggers */ GBLDEF boolean_t dollar_ztrigger_invoked; /* $ZTRIGGER() was invoked on at least one region in this transaction */ # ifdef DEBUG GBLDEF gv_trigger_t *gtm_trigdsc_last; /* For debugging purposes - parms gtm_trigger called with */ GBLDEF gtm_trigger_parms *gtm_trigprm_last; GBLDEF ch_ret_type (*ch_at_trigger_init)(); /* Condition handler in effect when gtm_trigger called */ # endif GBLDEF boolean_t explicit_update_repl_state; /* Initialized just before an explicit update invokes any triggers. * Set to 1 if triggering update is to a replicated database. * Set to 0 if triggering update is to a non-replicated database. * Value stays untouched across nested trigger invocations. */ #endif GBLDEF boolean_t skip_dbtriggers; /* Set to FALSE by default (i.e. triggers are invoked). Set to TRUE * unconditionally by MUPIP LOAD as it always skips triggers. Also set * to TRUE by journal recovery/update-process only when they encounter * updates done by MUPIP LOAD so they too skip trigger processing. In the * case of update process, this keeps primary/secondary in sync. In the * case of journal recovery, this keeps the db and jnl in sync. */ GBLDEF boolean_t expansion_failed; /* used by string pool when trying to expand */ GBLDEF boolean_t retry_if_expansion_fails; /* used by string pool when trying to expand */ GBLDEF boolean_t mupip_exit_status_displayed; /* TRUE if mupip_exit has already displayed a message for non-zero status. * Used by mur_close_files to ensure some message gets printed in case * of abnormal exit status (in some cases mupip_exit might not have been * invoked but we will still go through mur_close_files e.g. if exit is * done directly without invoking mupip_exit). */ GBLDEF boolean_t implicit_trollback; /* Set to TRUE by OP_TROLLBACK macro before calling op_trollback. Set * to FALSE by op_trollback. Used to indicate op_trollback as to * whether it is being called from generated code (opp_trollback.s) * or from C runtime code. */ #ifdef DEBUG GBLDEF boolean_t ok_to_UNWIND_in_exit_handling; /* see gtm_exit_handler.c for comments */ GBLDEF boolean_t skip_block_chain_tail_check; GBLDEF boolean_t in_mu_rndwn_file; /* TRUE if we are in mu_rndwn_file (holding standalone access) */ #endif GBLDEF char gvcst_search_clue; /* The following are replication related global variables. Ideally if we had a repl_gbls_t structure (like jnl_gbls_t) * this would be a member in that. But since we don't have one and since we need to initialize this specificially to a * non-zero value (whereas usually everything else accepts a 0 default value), this is better kept as a separate global * variable instead of inside a global variable structure. */ GBLDEF int4 strm_index = INVALID_SUPPL_STRM; /* # of the supplementary stream if one exists. * If this process is an update process running on a supplementary * replication instance and the journal pool allows updates (that * is the instance was started as a root primary), the value of * this variable will be anywhere from 1 to 15 once the receiver * establishes connection with a non-supplementary source server. * If this process is a mumps process running on a supplementary * replication instance and the journal pool allows updates (that * is the instance was started as a root primary), the value of * this variable will be 0 to indicate this is the local stream. * Otherwise, this variable is set to -1 (INVALID_SUPPL_STRM) * This variable is used by t_end/tp_tend to determine if strm_seqno * field in the journal record needs to be filled in or not. * It is filled in only if this variable is 0 to 15. */ GBLDEF repl_conn_info_t *this_side, *remote_side; /* Replication related global variables END */ GBLDEF seq_num gtmsource_save_read_jnl_seqno; GBLDEF gtmsource_state_t gtmsource_state = GTMSOURCE_DUMMY_STATE; GBLDEF boolean_t gv_play_duplicate_kills; /* A TRUE value implies KILLs of non-existent nodes will continue to * write jnl records and increment the db curr_tn even though they don't * touch any GDS blocks in the db (i.e. treat it as a duplicate kill). * Set to TRUE for the update process & journal recovery currently. * Set to FALSE otherwise. */ GBLDEF boolean_t donot_fflush_NULL; /* Set to TRUE whenever we don't want gtm_putmsg to fflush(NULL). BYPASSOK * As of Jan 2012, mu_rndwn_all is the only user of this functionality. */ GBLDEF boolean_t jnlpool_init_needed; /* TRUE if jnlpool_init should be done at database init time (eg., for * anticipatory freeze supported configurations). The variable is set * explicitly by interested commands (eg., MUPIP REORG). */ GBLDEF char repl_instfilename[MAX_FN_LEN + 1]; /* save first instance */ GBLDEF char repl_inst_name[MAX_INSTNAME_LEN]; /* for syslog */ GBLDEF gd_addr *repl_inst_from_gld; /* if above obtained from directory */ GBLDEF boolean_t span_nodes_disallowed; /* Indicates whether spanning nodes are not allowed. For example, * they are not allowed for GT.CM OMI and GNP. */ GBLDEF boolean_t argumentless_rundown; GBLDEF is_anticipatory_freeze_needed_t is_anticipatory_freeze_needed_fnptr; GBLDEF set_anticipatory_freeze_t set_anticipatory_freeze_fnptr; GBLDEF boolean_t is_jnlpool_creator; GBLDEF char gtm_dist[GTM_PATH_MAX]; /* Value of $gtm_dist env variable */ GBLDEF unsigned int gtm_dist_len = 0; /* Length of validated $gtm_dist */ GBLDEF boolean_t gtm_dist_ok_to_use = FALSE; /* Whether or not we can use $gtm_dist */ GBLDEF semid_queue_elem *keep_semids; /* Access semaphores that should be kept because shared memory is up */ GBLDEF boolean_t dmterm_default; /* Retain default line terminators in the direct mode */ GBLDEF boolean_t in_jnl_file_autoswitch; /* Set to TRUE for a short window inside jnl_file_extend when we are about * to autoswitch; used by jnl_write. */ #ifdef GTM_PTHREAD GBLDEF pthread_t gtm_main_thread_id; /* ID of the main GT.M thread. */ GBLDEF boolean_t gtm_main_thread_id_set; /* Indicates whether the thread ID is set. This is not set just for a jvm * process but also otherwise. This is necessary now for the linux kernel * interface to AIO. */ GBLDEF boolean_t gtm_jvm_process; /* Indicates whether we are running with JVM or stand-alone. */ #endif GBLDEF size_t zmalloclim; /* ISV memory warning of MALLOCCRIT in bytes */ GBLDEF boolean_t malloccrit_issued; /* MEMORY error limit set at time of MALLOCCRIT */ GBLDEF xfer_set_handlers_fnptr_t xfer_set_handlers_fnptr; /* see comment in deferred_events.h about this typedef */ GBLDEF boolean_t ipv4_only; /* If TRUE, only use AF_INET. Reflects the value of the gtm_ipv4_only * environment variable, so is process wide. */ GBLDEF void (*stx_error_fptr)(int in_error, ...); /* Function pointer for stx_error() so gtm_utf8.c can avoid pulling * stx_error() into gtmsecshr, and thus just about everything else as well. */ GBLDEF void (*stx_error_va_fptr)(int in_error, va_list args); /* Function pointer for stx_error() so rts_error can avoid pulling * stx_error() into gtmsecshr, and thus just about everything else * as well. */ GBLDEF void (*show_source_line_fptr)(boolean_t warn); /* Func pointer for show_source_line() - same purpose as stx_error_fptr */ #ifdef GTM_TLS GBLDEF gtm_tls_ctx_t *tls_ctx; /* Process private pointer to SSL/TLS context. Any SSL/TLS connections that * the process needs to create will be created from this context. * Currently, SSL/TLS is implemented only for replication, but keep it here * so that it can be used when SSL/TLS support is implemented for GT.M * Socket devices. */ #endif GBLDEF lv_val *active_lv; GBLDEF boolean_t in_prin_gtmio = FALSE; /* Flag to indicate whether we are processing a GT.M I/O function. */ GBLDEF boolean_t err_same_as_out; GBLDEF boolean_t multi_proc_in_use; /* TRUE => parallel processes active ("gtm_multi_proc"). False otherwise */ GBLDEF multi_proc_shm_hdr_t *multi_proc_shm_hdr; /* Pointer to "multi_proc_shm_hdr_t" structure in shared memory * created by "gtm_multi_proc". */ GBLDEF unsigned char *multi_proc_key; /* NULL for parent process; Non-NULL for child processes forked off * in "gtm_multi_proc" (usually a null-terminated pointer to the * region name) */ #ifdef DEBUG GBLDEF boolean_t multi_proc_key_exception; /* If TRUE, multi_proc_key can be NULL even if multi_proc_use is TRUE. * If FALSE, multi_proc_key shold be non-NULL if multi_proc_use is TRUE. * else an assert in util_format will fail. */ #endif GBLDEF boolean_t multi_thread_in_use; /* TRUE => threads are in use. FALSE => not in use */ GBLDEF boolean_t thread_mutex_initialized; /* TRUE => "thread_mutex" variable is initialized */ GBLDEF pthread_mutex_t thread_mutex; /* mutex structure used to ensure serialization in case we need * to execute some code that is not thread-safe. Note that it is * more typical to use different mutexes for different things that * need concurrency protection, e.g., memory allocation, encryption, * token hash table, message buffers, etc. If the single mutex becomes * a bottleneck this needs to be revisited. */ GBLDEF pthread_t thread_mutex_holder; /* pid/tid of the thread that has "thread_mutex" currently locked */ GBLDEF pthread_key_t thread_gtm_putmsg_rname_key; /* points to region name corresponding to each running thread */ GBLDEF boolean_t thread_block_sigsent; /* TRUE => block external signals SIGINT/SIGQUIT/SIGTERM/SIGTSTP/SIGCONT */ GBLDEF boolean_t in_nondeferrable_signal_handler; /* TRUE if we are inside "generic_signal_handler". Although this * is a dbg-only variable, the GBLDEF needs to stay outside of * a #ifdef DEBUG because this is used inside gtm_malloc_dbg * which is even used by a non-debug mumps link. */ GBLDEF boolean_t forced_thread_exit; /* TRUE => signal threads to exit (likely because some thread already * exited with an error or the main process got a SIGTERM etc.) */ GBLDEF int next_task_index; /* "next" task index waiting for a thread to be assigned */ GBLDEF int gtm_mupjnl_parallel; /* Maximum # of concurrent threads or procs to use in "gtm_multi_thread" * or in forward phase of mupip recover. * 0 => Use one thread/proc per region. * 1 => Serial execution (no threads) * 2 => 2 threads concurrently run * etc. * Currently only mupip journal commands use this. */ GBLDEF boolean_t ctrlc_on; /* TRUE in cenable mode; FALSE in nocenable mode */ GBLDEF boolean_t hup_on; /* TRUE if SIGHUP is enabled; otherwise FALSE */ #ifdef DEBUG GBLDEF int gtm_db_counter_sem_incr; /* Value used to bump the counter semaphore by every process. * Default is 1. Higher values exercise the ERANGE code better * when the ftok/access/jnlpool counter semaphore overflows. */ GBLDEF boolean_t forw_recov_lgtrig_only; /* TRUE if jgbl.forw_phase_recovery is TRUE AND the current TP transaction * being played consists of only *LGTRIG* records (no SET/KILL etc.) * Used by an assert in op_tcommit. */ GBLDEF boolean_t in_mu_cre_file; /* TRUE only if inside "mu_cre_file" function (used by an assert). * This is MUPIP CREATE only (db does not exist at that point and so * threads are unlikely there) AND is dbg-only and hence okay to be a * gbldef instead of a threadgbldef. */ GBLDEF enum dbg_wtfini_lcnt_t dbg_wtfini_lcnt; /* "lcnt" value for WCS_OPS_TRACE tracking purposes. This is dbg-only * and hence it is okay to be a gbldef instead of a threadgbldef. */ #endif GBLDEF sgm_info *sgm_info_ptr; GBLDEF tp_region *tp_reg_free_list; /* Ptr to list of tp_regions that are unused */ GBLDEF tp_region *tp_reg_list; /* Ptr to list of tp_regions for this transaction */ #ifdef USE_LIBAIO GBLDEF char *aio_shim_errstr; /* If an error occurred (mostly but not limited to EAGAIN), * what triggered it? */ GBLDEF char io_setup_errstr[IO_SETUP_ERRSTR_ARRAYSIZE]; /* The original nr_events used by the client is necessary * to understand why io_setup() failed on occasion. We set * up an error string of the form io_setup(nr_events). */ #endif GBLDEF void (*mupip_exit_fp)(int4 errnum); /* Function pointer to mupip_exit() in MUPIP but points to a routine * that assert fails if run from non-MUPIP builds. */ GBLDEF boolean_t gtm_nofflf; /* GTM-9136 TRUE only to suppress LF after FF in "write #" */ GBLDEF void (*primary_exit_handler)(void); /* If non-NULL, may be used to reestablish atexit() handler */ GBLDEF dm_audit_info audit_conn[MAX_AUD_CONN]; /* AUdit logging connection information */ fis-gtm-V7.0-005/sr_port/gbldefs_usr_share.c0000755000032200000250000000265714342376331017705 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2002-2017 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. * * * ****************************************************************/ /* General repository for global variable definitions used both in DDPGVUSR and GTMSHR. gvusr_init should setup these globals */ /* for use in DDPGVUSR.*/ /***************** DO NOT MOVE THESE GLOBALS INTO GBLDEFS.C; IT WILL CAUSE DDPGVUSR.EXE BLOAT *****************/ #include "mdef.h" #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" GBLDEF gd_region *gv_cur_region; GBLDEF gv_key *gv_altkey, *gv_currkey; GBLDEF boolean_t caller_id_flag = TRUE; #ifdef INT8_SUPPORTED GBLDEF const seq_num seq_num_zero = 0; GBLDEF const seq_num seq_num_one = 1; GBLDEF const seq_num seq_num_minus_one = (seq_num)-1; #else GBLDEF const seq_num seq_num_zero = {0, 0}; GBLDEF const seq_num seq_num_minus_one = {(uint4)-1, (uint4)-1}; # ifdef BIGENDIAN GBLDEF const seq_num seq_num_one = {0, 1}; # else GBLDEF const seq_num seq_num_one = {1, 0}; # endif #endif fis-gtm-V7.0-005/sr_port/gc.mpt0000755000032200000250000000572614342376331015173 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 1989-2019 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %GC ;GT.M %GC utility - global copy ; new %GI,%GO,%SC,d,x ; $etrap set %ZL=$zlevel;,$etrap="zgoto "_$zlevel_":ERR^%GC" set d("io")=$io use $principal write !,"Global copy",! set $zstatus="" if '$data(%zdebug) new $etrap set $etrap="zgoto "_$zlevel_":err^"_$text(+0) do . zshow "d":d ; save original $p settings . set x=$piece($piece(d("D",1),"CTRA=",2)," ") . set:""=x x="""""" . set d("use")="$principal:(ctrap="_x . set x=$piece(d("D",1),"EXCE=",2),x=$zwrite($extract(x,2,$length(x)-1)) . set:""=x x="""""" . set d("use")=d("use")_":exception="_x_":"_$select($find(d("D",1),"NOCENE"):"nocenable",1:"cenable")_")" . use $principal:(ctrap=$char(3,4):exception="halt:$zeof!($zstatus[""TERMWRITE"") "_$etrap:nocenable) RESTART read !,"Show copied nodes ? ",%SC set %SC=($translate(%SC,"yes","YES")=$extract("YES",1,$length(%SC))) for read !,"From global ^",%GI quit:%GI="" do COPY use:$data(d("use")) @d("use") use:$data(d("io")) d("io") quit COPY new c,ix if $extract(%GI)="?" set ix=%GI do help quit set:$extract(%GI)'="^" %GI="^"_%GI do quit:""=%GI . new $etrap . set $etrap="write !,$piece($zstatus,"","",2,99),! set $ecode="""",%GI=""""",x=$qlength(%GI) if '$data(@%GI) write !,"Global ",%GI," does not exist." quit for read !,"To global ^",%GO,! quit:$extract(%GO)'="?" set ix=%GO do help quit:%GO="" set:$e(%GO)'="^" %GO="^"_%GO do quit:""=%GO . new $etrap . set $etrap="write !,$piece($zstatus,"","",2,99),! set $ecode="""",(%GI,%GO)=""""",x=$qlength(%GO) if $data(@%GO) write !,"Global ",%GO," already exists." quit merge @%GO=@%GI zwrite:$get(%SC) @%GO ; comment out the next 2 lines if you don't want to spend the time to get a count if $data(@%GI)'[0,$increment(c) for set %GI=$query(@%GI) quit:%GI="" if $increment(c) write "Total ",c," nodes copied.",! quit help if $length(ix)=2,"Dd"[$extract(ix,2) do ^%GD use $principal:flush quit write !!,"This routine copies a node and all its descendents" write !,"from one global variable to another" write !,"""From global"" requests the source for the copy," write !,"""To global"" requests the destination" write !,"Use standard MUMPS gvn syntax to specify the node names" write !,"?D invokes %GD to get a global directory" write !," drops you back to the prior prompt or out of %GC" write ! quit err if $io=$principal,(($zstatus["IOEOF")!($zstatus["TERMWRITE")) halt use:$data(d("use")) @d("use") use:$data(d("io")) d("io") set $ecode="" write !,$piece($zstatus,",",2,99),! quit fis-gtm-V7.0-005/sr_port/gce.mpt0000755000032200000250000000515614342376331015335 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 1987-2018 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %GCE ;GT.M %GCE utility - global change every occurrence ; n c,g,gn,m,n,ns,os,s,sc,sn,%ZD,%ZG,x i ('$d(%ZQ)) n %ZQ s %ZQ=0 i '$d(%zdebug) n $et s $et="zg "_$zl_":ERR^%GCE" u $p:(ctrap=$c(3):exc="zg "_$zl_":LOOP^%GCE") w !,"Global Change Every occurrence",! d base q base f d main q:'%ZG i $d(%ZD),%ZD'=$p c %ZD u $p:(ctrap="":exc="") q main k %ZG d CALL^%GSEL i '%ZG q s gn="" r !,"Old string: ",os i os="" w !,"No string to find - no search done.",! s %ZG=0 q i os?.E1C.E w !,"The Old string contains control characters" r !,"New string: ",ns i ns?.E1C.E w !,"The New string contains control characters" f r !,"Show changed nodes ? ",x,! q:$e(x)'="?" d help i $l(x) s sc=$e("NO",1,$l(x))'=$e($tr(x,"no","NO"),1,2) e s sc=1 i sc f d q:$l(%ZD) . r !,"Output device: : ",%ZD,! . i '$l(%ZD) s %ZD=$p q . i %ZD="^" q . i %ZD="?" d q . . w !!,"Select the device you want for output" . . w !,"If you wish to exit enter a carat (^)",! . . s %ZD="" . i $zparse(%ZD)="" w " no such device" s %ZD="" q . o %ZD:(newversion:block=2048:record=2044:exception="g noopen"):0 . i '$t w !,%ZD," is not available" s %ZD="" q . q noopen . w !,$p($ZS,",",2,999),! c %ZD s %ZD="" i sc q:%ZD="^" e s %ZD=$p i %ZD'=$p d . u %ZD w $zd($h,"DD-MON-YEAR 24:60:SS"),!,"Global Change Every occurrence of:",!,">",os,"<",!,"To:",!,">",ns,"<",! u $p f s gn=$o(%ZG(gn)) q:gn="" d search q search n wrotegn s wrotegn=0 i '%ZQ s wrotegn=1 w:$x>70 ! w gn,?$x\10+1*10 s g=gn,(m,n)=0 u %ZD i ($d(@g)#10=1) s n=1 d:@g[os change f s g=$q(@g) q:g="" s n=n+1 d:@g[os change i m w !!,m," changes made in total ",n," nodes.",! e w:'%ZQ !!,"No changes made in total ",n," nodes.",! u $p q change i 'wrotegn s wrotegn=1 w:$x>70 ! w gn,?$x\10+1*10 w:sc !,g,!,"Was : ",@g s s=@g,sn="",c=$l(s,os) f i=1:1:c-1 s sn=sn_$p(s,os,i)_ns s @g=sn_$p(s,os,c),m=m+i w:sc !,"Now : ",@g,! q help w !,"Answer No to this prompt if you do not wish a trail of the changes" q q ERR i $d(%ZD),%ZD'=$p c %ZD u $p w !,$p($zs,",",2,99),! u $p:(ctrap="":exc="") s $ec="" q LOOP i $d(%ZD),%ZD'=$p c %ZD d base q QUIET n %ZQ s %ZQ=1 d %GCE q fis-gtm-V7.0-005/sr_port/gd.mpt0000755000032200000250000000140414342376331015161 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 1987,2001 Sanchez Computer Associates, 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %GD ;GT.M %GD utility - global directory ; n %ZG,%ZL i '$d(%zdebug) n $et s $et="zg "_$zl_":ERR^%GD" u $p:(ctrap=$c(3):exc="k %ZG zg "_$zl_":LOOP^%GD") w !,"Global Directory",! d GD^%GSEL,EXIT q ERR u $p w !,$p($zs,",",2,99),! s $ec="" ; Warning: Fall-through! EXIT u $p:(ctrap="":exc="") q LOOP d GD^%GSEL,EXIT q fis-gtm-V7.0-005/sr_port/gde.hlp0000644000032200000250000032410314342376331015312 0ustar librarygtc1 Overview Overview The GT.M Global Directory Editor (GDE) is a utility for creating, examining, and modifying a global directory. GDE is a program written in M and you can invoke it from the shell with $gtm_dist/mumps -run ^GDE. If you invoke it from the shell, GDE returns a status indicating success (0) or an issue (non-zero). Because GDE is an M program, you can also invoke GDE from a GT.M process with DO ^GDE. If you invoke GDE with a DO and modify the map of global directly currently opened by that process, you must HALT and restart the process for the process to pick up the revised mapping. FIS expects users normally run GDE from the shell --$gtm_dist/mumps -run GDE. The input to GDE can be a command file. In a production environment, FIS recommends using command files to define database configurations and putting them under version control. **Caution** A global directory stores database attributes and mapping rules. Processes use mapping rules to determine which database file contains a global variable node. MUPIP CREATE uses database attributes to create new database file(s). Once MUPIP CREATE applies the database attributes to create a database file, GT.M does not use the attributes until the next MUPIP CREATE. If you use MUPIP SET (or DSE) to change the attributes of a database file, always perform an equivalent change to any global directory used for a subsequent MUPIP CREATE. Conversely, if you change attributes with GDE, existing database files must be explicitly changed with MUPIP SET or DSE. 2 Identifying_the_Current_Global_Directory Identifying the Current Global Directory At process startup, the environment variable gtmgbldir identifies the global directory to the process. M application code can access and change the global directory through the $ZGBLDIR intrinsic special variable, which is initialized from $gtmgbldir at process startup. M application code can also use extended global references with the || or {} syntax. Note that $gtmgbldir / $ZGBLDIR are pathnames. If they do not start with a "/", then the pathname is relative and GT.M searches for the global directory starting in the current working directory. To change the Global Directory used by processes, specify a new value for gtmgbldir. Example: $ export gtmgbldir=/home/jdoe/node1/prod.gld When you invoke GDE and no Global Directory exists for gtmgbldir, GDE creates a minimal default Global Directory that is a starting point or template for building global directories for your specific needs. To retain the default Global Directory, exit GDE without making any changes. Example: $ export gtmgbldir=/home/jdoe/node1/prod.gld 2 Creating_a_Default_Global_Directory Creating a Default Global Directory When you invoke GDE and no Global Directory exists for gtmgbldir, GDE produces a default Global Directory that contains a minimal set of required components and values for database characteristics. It can be used for purposes such as development and testing work. A default Global Directory also serves as a starting point or template for building custom global directories. To retain the default Global Directory, quit GDE without making any changes. Example: $ gtmgbldir=/usr/accntg/jones/mumps.gld $ export gtmgbldir $ $gtm_dist/mumps -dir GTM>do ^GDE %GDE-I-GDUSEDEFS, Using defaults for Global Directory /usr/accntg/jones/mumps.gld GDE> EXIT %GDE-I-VERIFY, Verification OK %GDE-I-GDCREATE, Creating Global Directory file /usr/accntg/jones/mumps.gld 2 Mapping_Global_Variables_in_a_Global_Directory Mapping Global Variables in a Global Directory Mapping is the process of connecting a global variable name or a subtree or a subscript range to a database file. A complete mapping has the following four components: o NAME o REGION o SEGMENT o FILE These components may be defined in any order, but the final result must be a complete logical path from name to file: NAME(s) --> REGION --> SEGMENT --> FILE The default Global Directory contains one complete mapping that comprises these entries for name, region, segment, and file. * --> DEFAULT --> DEFAULT --> mumps.dat (NAME) (REGION) (SEGMENT) (FILE) The * wildcard identifies all possible global names. Subsequent edits create entries for individual global names or name prefixes. Regions and segments store information used to control the creation of the file. The characteristics stored with the region and segment are passed to MUPIP only when creating the database file using the CREATE command, so subsequent changes to these characteristics in the Global Directory have no effect on an existing database. On EXIT, GDE validates the global directory to ensure that every legal global variable node maps to exactly one region; that every region has at least one global variable node mapping to it and that it maps to exactly one segment; that every segment has exactly one region mapping to it; and that the attributes for each region and segment are internally consistent. GDE will not create a structurally unsound global directory, and will not exit until it validates the global directory. Informational messages advise you of structural inconsistencies. 2 Examining_the_Default_Global_Directory Examining the Default Global Directory A Global Directory looks like this: *** TEMPLATES *** Std Inst Def Rec Key Null Null Freeze Qdb Epoch LOCK Region Coll Size Size Subs Coll Jnl on Err Rndwn Taper AutoDB Stats Crit ---------------------------------------------------------------------------------------------------------------------- 0 256 64 NEVER N N N N Y N Y Sep Segment Active Acc Typ Block Alloc Exten Options ------------------------------------------------------------------------------ * BG DYN 4096 100 100 GLOB =1024 LOCK = 40 RES = 0 ENCR = OFF MSLT =1024 DALL = YES AIO = OFF FBWR = 0 MM DYN 4096 100 100 DEFER LOCK = 40 MSLT =1024 DALL = YES FBWR = 0 *** NAMES *** Global Region ------------------------------------------------------------------------------ * DEFAULT *** REGIONS *** Std Inst Dynamic Def Rec Key Null Null Freeze Qdb Epoch LOCK Region Segment Coll Size Size Subs Coll Jnl on Err Rndwn Taper AutoDB Stats Crit ------------------------------------------------------------------------------------------------------------------------------------------- DEFAULT DEFAULT 0 256 64 NEVER N N N N Y N Y Sep *** SEGMENTS *** Segment File (def ext: .dat)Acc Typ Block Alloc Exten Options ---------------------------------------------------------------------------------------------- DEFAULT mumps.dat BG DYN 4096 100 100 GLOB=1024 LOCK= 40 RES = 0 ENCR= OFF MSLT=1024 DALL= YES AIO = OFF FBWR= 0 *** MAP *** - - - - - - - - - - Names - - - - - - - - - - From Up to Region / Segment / File(def ext: .dat) -------------------------------------------------------------------------------------------------------------------------- % ... REG = DEFAULT SEG = DEFAULT FILE = mumps.dat LOCAL LOCKS REG = DEFAULT SEG = DEFAULT FILE = mumps.dat There are five primary sections in a Global Directory: o TEMPLATES o NAMES o REGIONS o SEGMENTS o MAP The function of each section in the Global Directory is described as follows: TEMPLATES This section of the Global Directory provides a default value for every database or file parameter passed to GT.M as part of a region or segment definition. GDE uses templates to complete a region or segment definition where one of these necessary values is not explicitly defined. GDE provides initial default values when creating a new Global Directory. You can then change any of the values using the appropriate -REGION or -SEGMENT qualifiers with the TEMPLATE command. NAMES An M program sees a monolithic global variable namespace. The NAMES section of the Global Directory partitions the namespace so that a global name or a global name with a subscript range reside in different database files. An M global can reside in one more database file, each database file can store many M globals. REGIONS The REGIONS section lists all of the regions in the Global Directory. Each region defines common properties for a set of M global variables or nodes; therefore, multiple sets of names from the NAMES section map onto a single region. You assign these values by specifying the appropriate qualifier when you create or modify individual regions. If you do not specify a value for a particular parameter, GDE assigns the default value from the TEMPLATES section. SEGMENTS This section of the Global Directory lists currently defined segments. While regions specify properties of global variables, segments specify the properties of files. There is a one-to-one mapping between regions and segments. You assign these values by specifying the appropriate qualifier when you create or modify individual segments. If you do not specify a value for a particular parameter, GDE assigns the default value from the TEMPLATES section. MAP This section of the Global Directory lists the current mapping of names to region to segment to file. In the default Global Directory, there are two lines in this section: one specifies the destination for all globals, the other one is for M LOCK resources with local variable names. If you add any new mapping component definitions (that is, any new names, regions, or segments), this section displays the current status of that mapping. Any components of the mapping not currently defined display "NONE". Because GDE requires all elements of a mapping to be defined, you will not be able to EXIT (and save) your Global Directory until you complete all mappings. 2 Global_Directory_Abbreviations Global Directory Abbreviations GDE uses the following abbreviations to display the output of a global directory. The following list show global directory abbreviations with the associated qualifiers. For a description of the function of individual qualifiers, see "GDE Command Summary". Abbreviation Full Form -------------------------------------------------- Acc -ACCESS_METHOD AIO -[NO]ASYNCIO Alloc -ALLOCATION AutoDB -[NO]AUTODB AutoSwitch -AUTOSWITCHLIMIT Block -BLOCK_SIZE Buff -BUFFER_SIZE Dall -[NO]DEFER_ALLOCATE Def Coll -COLLATION_DEFAULT Epoch Taper -[NO]EPOCHTAPER Exten -EXTENSION_COUNT File -FILE_NAME GLOB -GLOBAL_BUFFER_COUNT Inst Freeze On Error -[NO]INST_FREEZE_ON_ERROR JNL -[NO]JOURNAL Key Size -KEY_SIZE LOCK -LOCK_SPACE LOCK Crit -[NO]LOCK_CRIT MSLT -MUTEX_SLOTS Null Subs -[NO]NULL_SUBSCRIPTS Qdb Rndwn -[NO]QDBRUNDOWN Std Null Coll -[NO]STDNULLCOLL Rec Size -RECORD_SIZE RES -RESERVED_BYTES Region -REGION Stats -[NO[STATS Typ -DYNAMIC_SEGMENT 2 Customizing_a_Global_Directory Customizing a Global Directory Once you have installed GT.M and verified its operation, create Global Directories based on your needs. To create customized Global Directories, use the appropriate GDE commands and qualifiers to build each desired Global Directory. The GDE commands are described later in this chapter. You can also create a text file of GDE commands with a standard text editor and process this file with GDE. In a production environment, this gives better configuration management than interactive usage with GDE. 3 Adding_a_Journaling_Information_Section Adding a Journaling Information Section If you select the -JOURNAL option when you ADD or CHANGE a region in a Global Directory, the following section is added to your Global Directory and displays when you invoke SHOW. The columns provided display the values you selected with the journal options, or defaults provided by FIS for any options not explicitly defined. *** JOURNALING INFORMATION *** Region Jnl File (def ext: .mjl) Before Buff Alloc Exten AutoSwitch ------------------------------------------------------------------------------------------ DEFAULT $gtmdir/$gtmver/g/gtm.mjl Y 2308 2048 2048 8386560 1 Using_GDE Using GDE The default installation procedure places the GDE utility into a directory assigned to the environment variable gtm_dist. To invoke GDE: from within GTM, use the command: GTM>do ^GDE from the shell, enter: $ mumps -r GDE GDE displays informational messages like the following, and then the GDE> prompt: %GDE-I-LOADGD, loading Global Directory file /prod/mumps.gld %GDE-I-VERIFY, Verification OK GDE> If this does not work, contact your system manager to investigate setup and file access issues. **Note** Even when invoked from within GT.M, GDE always uses the gtmgbldir environment variable to identify its target To leave GDE: 1. Use the GDE EXIT command to save all changes and return to the caller. GDE> EXIT 2. Use the GDE QUIT command to discard all changes and return to the caller. This will not save any changes. GDE> QUIT 2 Guidelines_for_Mapping Guidelines for Mapping This section lists the parameters that apply to defining each component of a mapping. NAMES The NAMES section contains mappings of M global name spaces. More than one name space can map to a single region but a single name space can only map to one region. A name space: o Is case sensitive. o Must begin with an alphabetic character or a percent sign (%). o Can be a discrete "global" name, for example, aaa corresponds to the global variable ^aaa. o Can be a global name ending with a wild card ("*"), for example, abc* represents the set of global nodes which have abc as the starting prefix. o Can be a subtree of a global name, for example, abc(1) represents a subtree of the global ^abc. o Can be a subscript range, for example, abc(1:10) represents all nodes starting from ^abc(1) up to (but not including) to ^abc(10). o A global name can be one to 31 alphanumeric characters. However, the combined length of a global and its subscripts is limited to 1,019 bytes (the maximum key size supported by GT.M). Note that the byte length of the subscripted global specification can exceed the maximum KeySize specified for its region. o Maps to only one region in the Global Directory. REGIONS The REGIONS section contain mappings of database region. A region is a logical structure that holds information about a portion of a database, such as key-size and record-size. A key is the internal representation of a global variable name. In this chapter the terms global variable name and key are used interchangeably. A record refers to a key and its data. A Global Directory must have at least one region. A region only maps to a single segment. More than one name may map to a region. A region name: o Can include alphanumerics, dollar signs ($), and underscores ( _ ). o Can have from 1 to 31 characters. GDE automatically converts region names to uppercase, and uses DEFAULT for the default region name. SEGMENTS The SEGMENTS section contains mappings for segments. A segment defines file-related database storage characteristics. A segment must map to a single file. A segment can be mapped by only one region. GT.M uses a segment to define a physical file and access method for the database stored in that file. A segment-name: o Can include alphanumerics, dollar signs ($), and underscores ( _ ) o Can have from one to 31 characters GDE automatically converts segment names to uppercase. GDE uses DEFAULT for the default segment name. FILE Files are the structures provided by UNIX for the storage and retrieval of information. Files used by GT.M must be random-access files resident on disk. By default, GDE uses the file-name mumps.dat for the DEFAULT segment. GDE adds the .dat to the file name when you do not specify an extension. Avoid non-graphic and punctuation characters with potential semantic significance to the file system in file names as they will produce operational difficulties. 3 Example_of_a_Basic_Mapping Example of a Basic Mapping To complete this procedure, you must have already opened a Global Directory. o ADD a new global variable name. GDE> add -name cus -region=cusreg This maps the global name cus to the region cusreg. o ADD region cusreg, if it does not exist. GDE> add -region cusreg -dynamic=cusseg This creates the region cusreg and connects it to the segment cusseg. -d[ynamic] is a required qualifier that takes the associated segment-name as a value. o ADD segment cusreg, if it does not exist, and link it to a file. GDE> add -segment cusseg -file=cus.dat This creates the segment cusseg and connects it to the file cus.dat. To review the information you have added to the Global Directory, use the SHOW command. To perform a consistency check of the configuration, use the VERIFY command. To exit the Global Directory and save your changes, use the EXIT command. GDE performs an automatic verification. If successful, the mappings and database specifications become part of the Global Directory, available for access by processes, utilities, and the run-time system. Only MUPIP CREATE uses the database specifications; run-time processes and other utility functions use only the map. 1 Commands Commands This section describes GDE commands. GDE allows abbreviations of commands. The section describing each command provides the minimum abbreviation for that command and a description of any qualifiers that are not object-related. The section discussing the object-type describes all the associated object-related qualifiers. Command Syntax: The general format of GDE commands is: command [-object-type] [object-name] [-qualifier] where: -object-type Indicates whether the command operates on a -N[AME] space, -R[EGION], or -S[EGMENT]. object-name Specifies the name of the N[AME] space, R[EGION], or S[EGMENT]. Objects of different types may have the same name. Name spaces may include the wildcard operator (*) as a suffix. -qualifier Indicates an object qualifier. The format description for each individual command specifies required qualifiers for that command. The @, EXIT, HELP, LOG, QUIT, SETGD, and SPAWN commands do not use this general format. For the applicable format, refer to the section explaining each of these commands. Comments on command lines start with an exclamation mark (!) and run to the end of line. **Caution** An exclamation mark not enclosed in quotation marks ("")(for example in a subscript) causes GDE to ignore the rest of that input line. 2 at-sign at-sign The @ command executes a GDE command file. Use the @ command to execute GDE commands stored in a text file. The format of the @ command is: @file-name The file-name specifies the command file to execute. Use the file-name alone for a file in the current working directory or specify the relative path or the full path. GDE executes each line of the command file as if it were entered at the terminal. Example: GDE> @standard This command executes the GDE commands in the file to standard in the current working directory. standard should contain GDE commands; comments should start with an exclamation mark (!). 2 Add Add The ADD command inserts a new name, region, or segment into the Global Directory. The format of the ADD command is one of the following: A[DD] -G[BLNAME] global-name [-GBLNAME-qualifier ...] A[DD] -N[AME] namespace -R[EGION]=region-name A[DD] -R[EGION] region-name -D[YNAMIC]=segment-name [-REGION-qualifier...] A[DD] -S[EGMENT] segment-name [-SEGMENT-qualifier...] -F[ILE_NAME]=file-name The ADD command requires specification of an object-type and object-name. GDE supplies default values from the templates for qualifiers not explicitly supplied in the command. namespace specifies a global name or a global name with subscript(s) or a global name with a subscript range in the form of global[[*]|[(from-subscript:[to-subscript])]]. Name spaces and file-names are case-sensitive; other objects are not case-sensitive. 3 Name Name Maps a namespace to a region in the global directory. The format of the ADD -NAME command is: A[DD]-N[AME] namespace -R[EGION]=region-name o You can map a global and its subtrees to different regions. o You can also use colon (:) to map ranges of subscripted names and their subtrees to a region. Ranges are closed on the left and open on the right side of the colon. For example, add -name PRODAGE(0:10) -region DECADE0 maps ^PRODAGE(0) to ^PRODAGE(9), assuming the application always uses integer subscripts, to region DECADE0. o You can also use $CHAR() and $ZCHAR() to specify unprintable characters as subscripts. "" (an empty string) or no value (e.g. 20: or :20 or :) specify open-ended ranges, which span, on the left, from the first subscript ("") to, on the right, the last possible string. o Regions that contain global variables sharing the same unsubscripted name that span regions must use standard null collation; attempting to use the deprecated original null collation produces an error. Example: GDE> add -name IMPL -region=OTHERMUMPS ! Map MUMPS implementations to OTHERMUMPS GDE> add -name IMPL("GT.M") -region=MYMUMPS ! While mapping GT.M to MYMUMPS These examples map an entire subtree of a global to a region. Example: GDE> add -name PRODAGE(0:10) -region=DECADE0 ! Ranges are closed on the left and open on the right GDE> add -name PRODAGE(10:20) -region=DECADE1 ! PRODAGE(10) maps to DECADE1 GDE> add -name PRODAGE(20:30) -region=DECADE2 This example uses a colon (:) to map ranges of subscripted names and their subtrees to a region. Note that ranges are specific numbers or strings - GDE does not support wildcards (using "*") in ranges. Example: GDE> add -name=PRODAGE(:10) -region=DECADE0 ! This line and the next are equivalent GDE> add -name PRODAGE("":10) -region=DECADE0 ! numbers up to, but not including, 10 GDE> add -name PRODAGE(20:) -region=DECADE2 ! 20 thru all numbers (> 20) + strings GDE> add -name PRODAGE(20:"") -region=DECADE2 ! same as the add just above These examples demonstrate the use of $CHAR() and $ZCHAR() to specify unprintable characters; Notice that the arguments are positive integers (exponential - E syntax not allowed), and valid code points for $CHAR() or in range for $ZCHAR(), both with respect to the current $ZCHSET. Also, "" (an empty string) or no value (e.g. 20: or :20 or :) specify open-ended ranges, which span, on the left, from the first subscript ("") to, on the right, the last possible string. Example: GDE> add -name MODELNUM -region=NUMERIC GDE> add -name MODELNUM($char(0):) -region=STRING This example map numeric subscripts and strings to separate regions. Example: GDE> add -name DIVISION("Europe","a":"m") -region EUROPEAL GDE> add -name DIVISION("Europe","m":"z") -region EUROPEM GDE> add -name DIVISION("Australia") -region AUSTRALIA GDE> add -name DIVISION("USA","South","a":"m") -region USSAL GDE> add -name DIVISION("USA","South","m":"{") -region USSMZ GDE> add -name DIVISION("USA","WestCoast") -region USWC This example maps global variables with the same unsubscripted name at multiple subscript levels. Example: GDE> add -name x -region=REG1 GDE> add -name x(5) -region=REG1 GDE> add -name x(5,10:) -region=REG2 GDE> add -name x(5:20) -region=REG2 GDE> add -name x(20) -region=REG2 GDE> add -name x(20,40) -region=REG2 GDE> add -name x(20,40,50:) -region=REG3 GDE> add -name x(20,40:) -region=REG3 GDE> add -name x(20:) -region=REG3 This example performs the following mapping: o from ^x, upto but not including ^x(5,10), maps to REG1 o from ^x(5,10), upto but not including ^x(20,40,50), maps to to REG2 o from ^x(20,40,50) through the last subscript in ^x maps to REG 3 3 Segment Segment Maps a segment to a database file. The syntax of the ADD -SEGMENT command is: A[DD]-S[EGMENT] segment-name [-SEGMENT-qualifier...] -F[ILE_NAME]=file-name Example: GDE> add -segment temp -file_name=scratch This command creates a segment-name TEMP and maps it to the file scratch.dat in the current working directory. However, if you were to specify scratch as the file-name, in other words, an environment variable, each process uses the file using the translation of that environment variable at run-time. 3 Region Region Maps a region to a segment. The syntax of the ADD -REGION command is: A[DD]-R[EGION] region-name -D[YNAMIC]=segment-name [-REGION-qualifier...] 3 Gblname Gblname Provides a mechanism to specify the collation for global variables sharing the same unsubscripted name. Specifying a collation is necessary for globals that span multiple regions and and use an alternate collation. Because the global name EURCentral (described in the Introduction section) uses an alternate collation, it requires an entry in the GBLNAME section. The format of the ADD -GBLNAME command is: A[DD] -G[BLNAME] -C[OLLATION]=collation_number o Because string subscripts are subject to collation (the unsubscripted portion of a global variable name and numeric subscripts are not), GDE needs to know the collation sequence number associated with each unsubscripted global variable name. M standard collation (the default) has a collation number of zero (0). As a consequence, when you use alternative collation(s) (other than 0), the collation transforms must be available to GDE in the same way as they are to other GT.M components. All of a global (all nodes sharing the same unsubscripted global name) must have a single collation, which is implicitly the case for globals that do not span multiple regions. o Globals that do not span multiple regions and do not have any collation characteristics defined in the GBLNAME section of the global directory take on the default collation characteristics defined in the database region to which they map. On the other hand, globals that span multiple regions have their collation implicitly (collation 0), or explicitly, established by the GBLNAME section of the global directory and cannot adopt a differing collation based on the region collation characteristic. Because GT.M determines collation for globals spanning multiple regions by the GBLNAME characteristic, which cannot change once the database files are created, GDE reports collation on many error messages. Example: GDE> add -gblname EURCentral -collation=1 GDE> show -gblname *** GBLNAMES *** Global Coll Ver ------------------------------------------------------------------------------ EURCentral 1 0 2 Change Change The CHANGE command alters the name-to-region or region-to-segment mapping and /or the environment for a region or segment. It may also alter the association of a global directory with a replication instance file. The format of the CHANGE command is: C[HANGE] -G[BLNAME] -C[OLLATION]=collation_number C[HANGE] -I[NSTANCE] -F[ILE_NAME]={repl_inst_filename|""} C[HANGE] -N[AME] namespace -R[EGION]=new-region C[HANGE] -R[EGION] region-name [-REGION-qualifier...] C[HANGE] -S[EGMENT] segment-name [-SEGMENT-qualifier...] The CHANGE command requires specification of an object-type and object-name. Once you exit GDE, mapping changes, including Instance mapping, take effect for any subsequent image activation (for example, the next RUN or the mumps -direct command). Changes to database parameters only take effect for new database files created with subsequent MUPIP CREATE commands that use the modified Global Directory. Use the MUPIP SET command (or in some cases DSE) to change characteristics of existing database files. Example: GDE> change -region master -dynamic=temp -key=100 This command changes the region master to use the segment temp and establishes a maximum KEY_SIZE of 100 characters for the next creation of a file for this region. The segment change takes effect the first time the system uses the Global Directory after the GDE session EXITs, while the KEY_SIZE change takes effect after the next MUPIP CREATE that creates a new database file for segment temp. 2 Delete Delete The DELETE command removes a name, region, or segment from the Global Directory. The DELETE command does not delete any actual data. However, GT.M does not access database files that do not have mapped global variables except through extended references using an alternative global directory that does not map to them. Note that GT.M replication does not support global updates made with extended references, unless they actually map to a database file that is a part of the replicated instance. The format of the DELETE command is: D[ELETE] -G[BLNAME] global-name D[ELETE] -N[AME] namespace D[ELETE] -R[EGION] region-name D[ELETE] -S[EGMENT] segment-name The DELETE command requires specification of an object-type and object-name. Deleting a name removes the namespace-to-region mapping. Deleting a region unmaps all names mapped to the region. Deleting a segment unmaps the region mapped to the segment. You may map the deleted names to another region or the deleted region to another segment using the CHANGE command. The default namespace (*) cannot be deleted. Example: GDE> del -name T* This command deletes the explicit mapping of all global names starting with the letter "T." This command does not delete any global variables. However, it may make preexisting globals starting with the letter "T" invisible, at least while using this global directory, because the T* global names map to the default namespace going forward. 2 Exit Exit The EXIT command writes all changes made in the current GDE editing session to the Global Directory and terminates the current editing session. The format of the EXIT command is: E[XIT] GDE performs a full verification test (VERIFY) on the data. If the verification succeeds, GDE writes the new Global Directory to file system and issues a verification message. If the verification fails, GDE displays a listing of all unverifiable mappings and waits for corrections. Make appropriate corrections, or leave the Global Directory in its original, unedited state by using the QUIT command. If you have not made any changes to the Global Directory, GDE does not save a new Global Directory unless the original global directory had an older format which GDE has automatically upgraded. Note that while GDE upgrades older global directories to the current version, there is no facility to downgrade global directories to prior versions, so you should always save copies of any global directories that might be needed to retrieve archival data. 2 Help Help The HELP command displays online information about GDE commands and qualifiers. The format of the HELP command is: H[ELP] [topic...] where topic specifies the GDE command for which you want information. If you omit the topic, GDE prompts you for it. 2 LOCks LOCks The LOCKS command specifies the region into which GT.M maps "local" locks(those with resource names not starting with a caret symbol ^). GDE maps locks on resource names, starting with a caret symbol, to the database region mapped for the global variable name matching the resource name. The format of the LOCKS command is: LOC[KS] -R[EGION]=region-name The LOCKS -REGION= qualifier allows specification of a region for local locks. By default, GDE maps local locks to the DEFAULT region . Example: GDE> lock -region=main This command maps all locks on resource names that don't start with the caret symbol, "^" to the region main. **Caution** GT.M associates LOCKs for global names with the database region holding the corresponding unsubscripted global name. Suppose a global called ^EURWest spans multiple regions in multiple global directories, a command like LOCK ^EURWest may not work in the same way as it would do if ^EURWest did not span multiple regions. Before using a command like LOCK ^EURWest where ^EURWest spans multiple regions in multiple directories, ensure that the corresponding unsubscripted ^EURWest map to the same region in all the global directories. Alternatively, you can use LOCK globalname (with no leading up-arrow) and control LOCK interactions with the LOCKS global directory characteristic or use transaction processing to eliminate the use of LOCKs to protect global access. 2 LOG LOG The LOG command creates a log file of all GDE commands and displays for the current editing session. Because the system places an exclamation point (!) (i.e., the comment symbol) before all display lines that are not entered by the user. In the log, the log can be used with the @ symbol as a command procedure. The format of the LOG command is: LOG LOG -ON[=file-name] LOG -OF[F] The LOG command, without a qualifier, reports the current status of GDE logging. The LOG command displays a message showing whether logging is in effect and the specification of the current log file for the GDE session. The log facility can be turned on and off using the -ON or -OFF qualifiers any time during a GDE session. However, GDE closes the log files only when the GDE session ends. The -ON qualifier has an optional argument of a file, which must identify a legal UNIX file. If LOG -ON has no file-argument, GDE uses the previous log file for the editing session. If no log file has previously been specified during this editing session, GDE uses the default log file GDELOG.LOG. Example: GDE> log -on="standard.log" This command turns on logging of the session and directs the output to standard.log. 2 Quit Quit The QUIT command ends the current editing session without saving any changes to the Global Directory. GDE does not update the Global Directory file. The format of the QUIT command is: Q[UIT] If the session made changes to the Global Directory, GDE issues a message warning that the Global Directory has not been updated. 2 Rename Rename The RENAME command allows you to change a namespace, the name of a region, or the name of a segment. The format of the RENAME command is: R[ENAME] -G[BLNAME] old-global-name new-global-name R[ENAME] -N[AME] old-name new-name R[ENAME] -R[EGION] old-region-name new-region-name R[ENAME] -S[EGMENT] old-segment-name new-segment-name The RENAME command requires specification of an object-type and two object-names. When renaming a region, GDE transfers all name mappings to the new region. When renaming a segment, GDE transfers the region mapping to the new segment. Example: GDE> rename -segment stable table This command renames segment stable to table and shifts any region mapped to stable so it is mapped to table. 2 SEtgd SEtgd The SETGD command closes out edits on one Global Directory and opens edits on another. The format of the SETGD command is: SE[TGD] -F[ILE]=file-name [-Q[UIT]] The -FILE=file-name specifies a different Global Directory file. When you provide a file-name without a full or relative pathname GDE uses the current working directory; if the file is missing an extension, then GDE defaults the type to .gld. The -QUIT qualifier specifies that any changes made to the current Global Directory are not written and are lost when you change Global Directories. SETGD changes the Global Directory that GDE is editing. If the current Global Directory has not been modified, or the -QUIT qualifier appears in the command, the change simply occurs. However, if the current Global Directory has been modified, GDE verifies the Global Directory, and if the verification is successful, writes that Global Directory. If the verification is not successful, the SETGD fails. Example: GDE> SETGD -f="temp" This changes the Global Directory being edited to temp. The quotation marks around the file name identifies the name of the file unequivocally to UNIX. If the -f is the final qualifier on the line, then the quotation marks are unnecessary. 2 SHow SHow The SHOW command displays information contained in the Global Directory about names, regions, and segments. The format of the SHOW command is: SH[OW] SH[OW] -A[LL] SH[OW] -C[OMMAND] -F[ILE]=[gde-command-file] SH[OW] -G[BLNAME] SH[OW] -I[NSTANCE] SH[OW] -M[AP] [-R[EGION]=region-name] SH[OW] -N[AME] [namespace] SH[OW] -R[EGION] [region-name] SH[OW] -S[EGMENT] [segment-name] SH[OW] -T[EMPLATE] -COMMAND: Displays GDE commands that recreate the current Global Directory state. -F[ILE]=gde-command-file: Optionally specifies a file to hold the GDE commands produced by -COMMAND. -FILE must must always appear after -COMMAND. Please consider using command files produced with the SHOW -COMMAND -FILE for creating new regions and segments in a global directory as the defaults come from the templates. If you inadvertently upgrade a global directory, you can use SHOW -COMMAND to create a file of commands that you can input to GDE with the prior GT.M release to recreate the prior global directory file. SHOW -COMMAND displays the GDE commands for creating names, regions, and segments of the current global directory state in a target environment. However, it does not always include the same template settings (SHOW -TEMPLATE) of the current global directory. SHOW -COMMAND creates an appropriate set of templates that minimize other adjustments to recreate the current global directory. If the current GDE template settings (SHOW -TEMPLATE) are important for your application, you need set them again after applying the commands from GDE SHOW -COMMAND in the target environment. **Note** When GDE encounters an error while executing the @command-file command, it stops processing the command file and returns to the operator prompt, which gives the operator the option of compensating for the error. If you subsequently issue @command-file command again in the same session for the same command-file, GDE resumes processing it at the line after the last error. -ALL: Displays the entire Global Directory. This qualifier corresponds to displaying "all" sections of the SHOW report: ***TEMPLATES***, ***NAMES***, ***REGIONS***, ***SEGMENTS***, ***MAP***, ***INSTANCE***. By default, SHOW displays -ALL. -GBLNAME, -INSTANCE, -MAP, -NAME, -REGION, -SEGMENT, and -TEMPLATE are qualifiers that cause GDE to display selected portions of the Global Directory as follows: -INSTANCE: Displays the current Instance Mapping, if any. This qualifier corresponds to the section of the SHOW report titled: ***INSTANCE*** -MAP: Displays the current mapping of all names, regions, segments, and files. This qualifier corresponds to the section of the SHOW report titled ***MAP***. The output of a SHOW -MAP may be restricted to a particular region by specifying a -REGION qualifier with a region name argument. -TEMPLATE: Displays the current region and segment templates. This qualifier corresponds to the section of the SHOW report titled: ***TEMPLATES*** If you want to print the Global Directory, create a log file by executing LOG -ON= before executing the SHOW command. The -LOG command captures all the commands entered and output. You can print the log file if you want a hard copy record. If you want to export the current Global Directory state, create a GDE command file with the SHOW -COMMAND -FILE=gde-command-file and run it in the target environment. 2 Template Template The TEMPLATE command maintains a set of -REGION and -SEGMENT qualifier values for use as templates when ADDing regions and segments. When an ADD command omits qualifiers, GDE uses the template values as defaults. GDE maintains a separate set of -SEGMENT qualifier values for each ACCESS_METHOD. When GDE modifies the ACCESS_METHOD, it activates the appropriate set of TEMPLATEs and sets all unspecified qualifiers to the template defaults for the new ACCESS_METHOD. Use the GDE SHOW command to display qualifier values for all ACCESS_METHODs. The format of the TEMPLATE command is: T[EMPLATE] -R[EGION] [-REGION-qualifier...] T[EMPLATE] -S[EGMENT] [-SEGMENT-qualifier...] The TEMPLATE command requires specification of an object-type. Example: GDE> template -segment -allocation=200000 This command modifies the segment template so that any segments ADDed after this time produce database files with an ALLOCATION of 200,000 GDS blocks. 2 Verify Verify The VERIFY command validates information entered into the current Global Directory. It checks the name-to-region mappings to ensure all names map to a region. The VERIFY command checks region-to-segment mappings to ensure each region maps to a segment, each segment maps to only one region, and the segment maps to a UNIX file. The EXIT command implicitly performs a VERIFY -ALL. The format of the VERIFY command is: V[ERIFY] V[ERIFY] -A[LL] V[ERIFY] -G[BLNAME] V[ERIFY] -M[AP] V[ERIFY] -N[AME] [namespace] V[ERIFY] -R[EGION] [region-name] V[ERIFY] -S[EGMENT] [segment-name] V[ERIFY] -T[EMPLATE] The object-type is optional. -MAP, -TEMPLATE, and -ALL are special qualifiers used as follows: -ALL Checks all map and template data. -MAP Checks that all names map to a region, all regions map to a segment, and all segments map to a file. -TEMPLATE Checks that all templates currently are consistent and useable. VERIFY with no qualifier, VERIFY -MAP, and VERIFY -ALL each check all current information. Example: GDE> verify -region regis This command verifies the region regis. 1 Qualifiers Qualifiers The -NAME, -REGION, and -SEGMENT qualifiers each have additional qualifiers used to further define or specify characteristics of a name, region, or segment. For more information, refer to the additional topics. 2 Name_Qualifiers Name Qualifiers The following -NAME qualifier can be used with the ADD or CHANGE commands. -REGION=region-name Specifies the name of a region. Region names are not case-sensitive, but are represented as uppercase by GDE. The minimum length is one alphabetic character. The maximum length is 31 alphanumeric characters. Example: GDE> add -name a* -region=areg This command creates the namespace a*, if it does not exist, and maps it to the region areg. Summary +------------------------------------------------------------------+ | GDE NAME Qualifiers | |------------------------------------------------------------------| | QUALIFIER | DEFAULT | MINIMUM | MAXIMUM | |------------------------------------+---------+---------+---------| | -R[EGION]=region-name (characters) | (none) | 1A | 16A/N | +------------------------------------------------------------------+ 2 Region_Qualifiers Region Qualifiers The following -REGION qualifiers can be used with the ADD, CHANGE, or TEMPLATE commands. -[NO]AU[TODB] Specifies whether GT.M should implicitly create a database file for the region if none exists when a process attempts to access it. Because it carries lower operational risk and provides better operational control, the common practice is to create database files with MUPIP CREATE. However, AUTODB may simplify operations when you have scratch or temporary databases which are best deleted and recreated as a part of standard operation procedures. The default is NOAUTODB. -C[OLLATION_DEFAULT]=number Specifies the number of the collation sequence definition to be used as the default for this database file. The number can be any integer from 0 to 255. The number you assign as a value must match the number of a defined collation sequence that resides in the shared library pointed to by the environment variable gtm_collate_n. For information on defining this environment variable and creating an alternate collation sequence, refer to the "Internationalization" chapter in the GT.M Programmer's Guide. The minimum COLLATION_DEFAULT number is zero, which is the standard M collation sequence. The maximum COLLATION_DEFAULT number is 255. By default, GDE uses zero (0) as the COLLATION_DEFAULT. -D[YNAMIC_SEGMENT]=segment-name Specifies the name of the segment to which the region is mapped. Segment-names are not case-sensitive, but are displayed as uppercase by GDE. The minimum length is one alphabetic character. The maximum length is 31 alphanumeric characters. -[NO]EPOCHTAPER Tries to minimize epoch duration by reducing the number of buffers to flush by GT.M and the file system (via an fsync()) as the epoch (time-based or due to a journal file auto-switch) approaches. By default, EPOCHTAPER is enabled. Epoch tapering reduces the impact of I/O activity during an epoch event. Application that experience high load and/or need to reduce latency may benefit from epoch tapering. -[NO]INST[_FREEZE_ON_ERROR] Controls whether custom errors in a region should automatically cause an Instance Freeze. This qualifier modifies the value of "Inst Freeze on Error" file header element. -[NO]J[OURNAL][=journal-option-list] This qualifier establishes characteristics for the journal file on newly created databases. -NOJOURNAL specifies that updates to the database file are not journaled. -NOJOURNAL does not accept an argument assignment. -JOURNAL specifies that journaling is allowed. -JOURNAL takes one or more arguments in a journal-option-list. The journal-option-list contains keywords separated with commas (,) enclosed in parentheses ( ) with file-names quoted (for example, change -region test -journal=(before,file="foo") . If the list contains only one keyword, the parentheses and quotes are optional. Although you do not have to establish the criteria for your journaling process at this point, it is efficient to do so, even if you are not entirely sure you will use journaling. The options available for -JOURNAL set up the environment, so it is ready for you to enable with MUPIP SET -JOURNAL. You can also change or add any of the established options at that time. The journal-option-list includes: * A[LLOCATION]=blocks * AUTOSWITCHLIMIT=blocks * [NO]BE[FORE_IMAGE] * BU[FFER_SIZE]=pages * E[XTENSION]=blocks * F[ILE_NAME]=file-specification-name The following section describes some -JOURNAL options. -AU[TOSWITCHLIMIT]=blocks Specifies the limit on the size of a journal file. When the journal file size reaches the limit, GT.M automatically switches to a new journal file with a back-pointer to the prior journal file. -[NO]BE[FORE_IMAGE] [NO]BEFORE_IMAGE controls whether the journal should include before-image records. The BEFORE_IMAGE option is required if you plan to consider "roll-back" (Backward) recovery of the associated database file or if you plan to use certain database replication options. -F[ILE_NAME]="file-name" Specifies the name of the journal file. Unless the name is the sole journaling option, and is the last parameter on the line, it should always be enclosed in quotation marks in this context. Journal file-specifications-names are limited to 255 characters. By default, GDE derives the file-specification-name from the database "file-name". By default, GDE uses a journal file extension of .mjl. Journal Options Summary With GDE, you can create the journal files and define the journal parameters; however, you must use MUPIP SET to explicitly turn it ON, and you must specify BEFORE/NOBEFORE at that time. Example: CHANGE -REGION DEFAULT -JOURNAL=(ALLOCATION=2048,AUTOSWITCHLIMIT=8386560,BEFORE_IMAGE,BUFFER_SIZE=2312,EXTENSION=2048) Summary -K[EY_SIZE]=size in bytes Specifies the maximum size of keys, in bytes, which can be stored in the region. The KEY_SIZE must be less than the RECORD_SIZE. GDE rejects the command if the KEY_SIZE is inappropriate for the RECORD_SIZE. The minimum KEY_SIZE is three bytes. The maximum KEY_SIZE is 1,019 bytes. When determining the maximum key size, applications should consider the following: * GT.M uses packed decimal representation for numeric subscripts which may be larger or smaller than the original representation. * GT.M substitutes an element terminator for the caret (^), any comma (,), and any right parenthesis ()). * GT.M adds an extra byte for every string element, including the global name. For example, the key ^ACN ("Name", "Type") internally occupies 17 bytes. By default, GDE uses a KEY_SIZE of 64 bytes -[NO]L[OCK_CRIT] GT.M uses critical sections for LOCK management and various database activities. LOCK_CRIT in GDE specifies that GT.M should use separate critical sections for LOCK management and database activity. NOLOCK_CRIT specifies the use of the database critical section for both LOCK and database activity. Because, in the current implementation, FIS has not identified any reason to share resource management between LOCKs and database actions, we have no recommendations other than to choose what seems to work better for your application. By default, GDE uses LOCK_CRIT - separate resource management for LOCKs and database actions. The -[NO]L[OCK_CRIT] setting appears with a different name in other utilities and report output. The following table shows the equivalent setting of the [NO]L[OCK_CRIT] GDE qualifier in other utilities: +------------------------------------------------------------------------------------------+ |Utility/Report |LOCK and DB share the SAME critical| LOCK and DB have SEPARATE critical | | | section | section (default) | |---------------+-----------------------------------+--------------------------------------| |GDE Region |-NOLOCK_CRIT |-LOCK_CRIT | |Qualifier | | | |---------------+-----------------------------------+--------------------------------------| |DSE DUMP |LOCK shares DB critical section : |LOCK shares DB critical section : | |-FILEHEADER |TRUE |FALSE | |---------------+-----------------------------------+--------------------------------------| |MUPIP SET |-LCK_SHARES_DB_CRIT |-NOLCK_SHARES_DB_CRIT | |---------------+-----------------------------------+--------------------------------------| |MUPIP DUMPFHEAD|("sgmnt_data.lock_crit_with_db")=1 |("sgmnt_data.lock_crit_with_db")=0 | +------------------------------------------------------------------------------------------+ -[NO]N[ULL_SUBSCRIPTS]=[ALWAYS|NEVER|EXISTING] Indicates whether GT.M allows null subscripts for global variables stored in the region (that is, whether GT.M permits references such as ^aaa("",1)). ALWAYS indicates that the null subscripts for global variables are allowed. NEVER indicates that null subscripts for global variables are not allowed. EXISTING indicates that null subscripts for global variable can be accessed and updated, but not created anew. By default, regions have -NULL_SUBSCRIPTS=NEVER. -[NO]Q[DBRUNDOWN] Shortens normal process shutdown when a large number of processes accessing a database file need to shutdown almost simultaneously, for example, in benchmarking scenarios or emergencies. When a terminating GT.M process observes that a large number of processes are attached to a database file and QDBRUNDOWN is enabled, it bypasses checking whether it is the last process accessing the database. Such a check occurs in a critical section and bypassing it also bypasses the usual RUNDOWN actions which accelerates process shutdown removing a possible impediment to process startup. By default, QDBRUNDOWN is disabled. Note that with QDBRUNDOWN there is a possibility that the last process to exit might leave the database shared memory and IPC resources in need of cleanup. Except after the number of concurrent processes exceeds 32Ki, QDBRUNDOWN minimizes the prossibility of abandoned resources, but it cannot eliminate it. When using QDBRUNDOWN, use an explicit MUPIP RUNDOWN of the database file after the last process exits, to ensure the cleanup of database shared memory and IPC resources; not doing so risk database damage. When a database has QDBRUNDOWN enabled, if the number of attached processes ever exceeds 32Ki, GT.M stops tracking the number of attached processes, which means that it cannot recognize when the number reaches zero (0) and the shared resources can be released. The process that detects this event issues a NOMORESEMCNT in the system log. This means an orderly, safe shutdown requires a MUPIP JOURNAL -ROLLBACK -BACKWARD for replicated databases, a MUPIP JOURNAL -RECOVER -BACKWARD for unreplicated journaled databases and a MUPIP RUNDOWN for journal-free databases. -R[ECORD_SIZE]=size in bytes Specifies the maximum size (in bytes) of a global variable node's value that can be stored in a region. If the size of a global exceeds one database block, GT.M implicitly spans that global across multiple database blocks. In the event a global variable node spans multiple blocks, and the process is not already within a TP transaction, the GT.M run-time system automatically and transparently performs the entire operation within an implicit TP transaction (as it does for Triggers). The minimum RECORD_SIZE is zero. A RECORD_SIZE of zero only allows a global variable node that does not have a value. A typical use of a global variable node with RECORD_SIZE of zero is for creating indices (where the presence of a node is all that is required). The maximum RECORD_SIZE is 1,048,576 bytes (1MiB). By default, GDE uses a RECORD_SIZE of 256 bytes. -[NO][STA[TS] Specifies whether GT.M should permit processes to share their database access statistics for other processes to monitor. When on, this characteristic causes GT.M to create a small MM database for the associated region to hold the shared statistics. There may be operational or security reasons to prohibit sharing of statistics. For example, GT.M does not share statistics on database files that exist solely to support GT.M features. Note that a process disables itself from maintaining the shared statistics when it fails to open a statsDB. It does not, however, disable subsequently starting processes from maintaining the shared statistics. By default, GDE uses STATS. -[NO]STD[NULLCOLL] Determines whether GT.M null subscripts collate in conformance to the M standard. If STDNULLCOLL is specified, subscripts of globals in the database follow the M standard where the null subscript collates before all other subscripts. If NOSTDNULLCOLL is specified, null subscripts collate between numeric and string subscripts. FIS strongly recommends that you use STDNULL and against using this non-standard null collation, which is the default for historical reasons. The following table summarizes GDE region qualifiers. It provides their abbreviations, defaults (as provided by FIS), and allowable minimum and maximum values. +-----------------------------------------------------------------------------+ | GDE REGION Qualifiers | |-----------------------------------------------------------------------------| | QUALIFIER | DEFAULT | MINIMUM | MAXIMUM | |--------------------------------------------+----------+---------+-----------| |-[NO]AU[TODB] |Disabled |- |- | |--------------------------------------------+----------+---------+-----------| |-C[OLLATION_DEFAULT]=number (integer) |0 |0 |255 | |--------------------------------------------+----------+---------+-----------| |-D[YNAMIC_SEGMENT] =segment-name (char) |- |1 |31 | |--------------------------------------------+----------+---------+-----------| |-[NO]EPOCHTAPER |ENABLED |- |- | |--------------------------------------------+----------+---------+-----------| |-[NO]INST[_FREEZE_ON_ERROR] |DISABLED |- |- | |--------------------------------------------+----------+---------+-----------| |-[NO]J[OURNAL] [=journal-option-list] |-NOJ |- |- | |--------------------------------------------+----------+---------+-----------| |-K[EY_SIZE]=size in bytes (integer) |64 |3 |1,019 | |--------------------------------------------+----------+---------+-----------| | |Disabled | | | |-[NO]L[OCK_CRIT] |(not |- |- | | |shared) | | | |--------------------------------------------+----------+---------+-----------| |-N[ULL_SUBSCRIPTS]=[ALWAYS|NEVER|EXISTING] |NEVER |- |- | |--------------------------------------------+----------+---------+-----------| |-[NO]Q[DBRUNDOWN] |Disabled |- |- | |--------------------------------------------+----------+---------+-----------| |-R[ECORD_SIZE]=size in bytes (integer) |256 |0 |1,048,576 | | | | |(1 MiB) | |--------------------------------------------+----------+---------+-----------| |-[NO]STA[TS] |ENABLED |- |- | |--------------------------------------------+----------+---------+-----------| |-[NO]STD[NULLCOLL] |No |- |- | +-----------------------------------------------------------------------------+ 2 Segment_Qualifiers Segment Qualifiers The following -SEGMENT qualifiers can be used with the ADD, CHANGE, or TEMPLATE commands. -AC[CESS_METHOD]=code Specifies the access method or the GT.M buffering strategy for storing and retrieving data from the global database file. o code can have 2 values - Buffered Global (BG) or Memory Mapped (MM). The default value is BG. o With BG, the global buffer pool manages the buffers (the OS/file system may also provide additional buffering). You get the choice of using BEFORE_IMAGE or NOBEFORE_IMAGE journaling for your database. * BG supports both forward and backward recovery and rollback to recover a database without a restore. * BG is a likely choice when you need faster recovery times from system failures. o With MM, GT.M bypasses the global buffer pool and relies entirely on the OS/file system to manage the data traffic between memory and disk. GT.M has no control over the timing of disk updates, therefore there is a greater reliance on the OS/file system for database performance. * MM supports NOBEFORE_IMAGE journaling only. GT.M issues an error if you use MM with BEFORE_IMAGE Journaling. MM supports MUPIP JOURNAL -RECOVER -FORWARD and MUPIP JOURNAL -ROLLBACK -FORWARD. With MM, MUPIP JOURNAL -RECOVER -BACKWARD only generates lost and broken transaction files but cannot recover the database. * Depending on your file system, MM may be an option when you need performance advantage in situations where the above restrictions are acceptable. o GDE maintains a separate set of segment qualifier values for each ACCESS_METHOD. o When GDE modifies the ACCESS_METHOD, it activates the appropriate set of TEMPLATEs and sets all unspecified qualifiers to the default values of the new ACCESS_METHOD. Example: GDE> change -segment DEFAULT -access_method=MM This command sets MM as the access method or the GT.M buffering strategy for storing and retrieving database for segment DEFAULT. -AL[LOCATION]=blocks Specifies the number of blocks GT.M allocates to a disk file when MUPIP creates the file. For GDS files, the number of bytes allocated is the size of the database file header plus the ALLOCATION size times the BLOCK_SIZE. * The minimum ALLOCATION is 10 blocks. * The maximum ALLOCATION is 1,040,187,392 blocks. * By default, GDE uses an ALLOCATION of 100 blocks. * The maximum size of a database file is 1,040,187,392(992Mi) blocks. * The default ALLOCATION was chosen for initial development and experimentation with GT.M. Because file fragmentation impairs performance, make the initial allocation for production files and large projects large enough to hold the anticipated contents of the file for a length of time consistent with your UNIX file reorganization schedule. --[NO]AS[YNCIO] Determines whether an access method BG database file uses asynchronous I/O rather than using synchronous I/O through the file system cache. With ASYNCIO, GT.M assumes responsibility for writing database updates directly to secondary storage, essentially bypassing the file system and its cache. This can yield improved behavior if the file system has trouble handling GT.M database I/O, particularly file synchronization (fsync). ASYNCIO eliminates some memory activities and may improve performance in some configurations. Some notes and observations: o As asynchronous IO dispenses with the UNIX file buffer cache, GT.M global buffers are the sole caching mechanism. To make asynchronous IO perform well, you will likely need to increase the number of global buffers considerably. Assign adequate database global buffers to compensate for async I/O bypassing the file system cache. With GT.M's limit of 2GiB per shared memory segment, a database segment with 4KiB blocks has a limit of almost two million global buffers. o A large number of global buffers potentially implies a large number of dirty global buffers to be flushed at an epoch. You should investigate the impact on application response time of GT.M epoch tapering vs. turning off epoch tapering and using a separate stand-alone process that executes a line of code such as: for set x="" for set x=$view("gvnext",x) quit:""=x view "dbflush":x,"dbsync":x,"epoch":x hang n where n is a number that causes each region to be flushed at an appropriate interval. If you choose this option, remember to turn off epoch tapering, and to set the epoch interval in the file header to be large enough to prevent application processes from performing epochs, and consider scripted timely switching of journal files by other than application processes (switching journal files involves an epoch). o On AIX, consider mounting file systems with the CIO mount option. The CIO mount option drops support for the file buffer cache (unused by asynchronous IO), and also eliminates a lock that is a potential bottleneck to GT.M performance on the AIX jfs2 filesystem. o If a process encounters a situation where it needs to perform an asynchronous write, but has no available slots with which to manage an additional one, it either falls back to synchronous writing if the write is blocking other actions, and otherwise defers the write until a slot becomes available as other writes complete. Linux allocates the structures on a system-wide basis with the setting of /proc/sys/fs/aio-max-nr. FIS recommends setting /proc/sys/fs/aio-max-nr to 1048576. o For Linux x86_64, set the environment variable gtm_aio_nr_events: the gtm_aio_nr_events environment variable controls the number of structures a process has per global directory to manage asynchronous writes, and therefore determines the number of concurrent writes a process can manage across all regions within a global directory. If not specified, the value controlled by gtm_aio_nr_events defaults to 128. If a process encounters a situation where it needs to perform an asynchronous write, but has no available slots with which to manage an additional one, it either falls back to synchronous writing if the write is blocking other actions, and otherwise defers the write until a slot becomes available as other writes complete. The default value for gtm_aio_nr_events (that is, 128) should be sufficient for most applications. Change the value for the gtm_aio_nr_events environment variable based on benchmarking. o Monitor the number of database writes errors for each global directory with set x="" for set x=$view("gvnext",x) quit:""=x $$^%PEEKBYNAME("sgmnt_data.wcs_wterror_invoked_cntr",x). If there are database write errors, your application may benefit from altering the number of gtm_aio_nr_events. o While database write errors may indicate a problem with database writes if your storage system is starting to degrade, in a well-functioning environment, they indicate that a write attempt was unable to start an asynchronous write because it was unable to obtain a free resource of the type associated with /proc/sys/fs/aio-max-nr and gtm_aio_nr_events. In such a case, GT.M either defers the write in hopes that a resource will come available for a future attempt, or, if the write is blocking the application, GT.M performs a synchronous direct I/O. A synchronous direct I/O tends to lengthen response times and reduce throughput, so if you see non-trivial counts of such errors, you should revisit your settings for the resource. o Limited experience with solid-state storage (SSDs) on Linux in the GT.M development environment suggests a considerable difference in asynchronous IO performance on the same underlying hardware, with f2fs performing better than xfs, which in turn performed better than ext4. While there is reason to hope that ASYNCIO can provide better and more uniform performance, to this point we have limited information on performance comparisons, so FIS recommends well thought out benchmarking of your application in a suitable test environment. Please consider the above observations in this light. By default GDE uses NOASYNCIO. On segments with an access method of MM, GT.M ignores this setting. -BL[OCK_SIZE]=size Specifies the size, in bytes, of each database block in the file system. The BLOCK_SIZE must be a multiple of 512. If the BLOCK_SIZE is not a multiple of 512, GDE rounds up the BLOCK_SIZE to the next highest multiple of 512 and issues a warning message. If the specified BLOCK_SIZE is less than the minimum, GDE uses the minimum BLOCK_SIZE. If the specified BLOCK_SIZE is greater than the maximum, GDE issues an error message. A BLOCK_SIZE that is equal to the page size used by your UNIX implementation serves well for most applications, and is a good starting point. You should determine the block sizes for your application through performance timing and benchmarking. In general, larger block sizes are more efficient from the perspective of the input/output subsystem. However, larger block sizes use more system resources (CPU and shared memory) and may increase collision and retry rates for transaction processing. **Note** Global nodes that span blocks incur some overhead and optimum application performance is likely to be obtained from a BLOCK_SIZE that accommodates the majority of nodes within a single block. If you adjust the BLOCK_SIZE, you should also adjust GLOBAL_BUFFER_COUNT. GDE does not allow you to change the block size to an arbitrary number. It always rounds the block size to the next higher multiple of 512, because the database block size must always be a multiple of 512. The minimum BLOCK_SIZE is 512 bytes. The maximum BLOCK_SIZE is 65,024 bytes. **Note** FIS recommends against using databases with block sizes larger than 16KiB. If a specific global variable has records that have large record sizes, FIS recommends placing that global variable in a file by itself with large block sizes and using more appropriate block sizes for other global variables. 4KiB and 8KiB are popular database block sizes. By default, GDE uses a BLOCK_SIZE of 1024 bytes. --[NO]DEFER_ALLOCATE With -DEFER_ALLOCATE, GT.M instructs the file system to create the database file as a sparse file. Before using -DEFER_ALLOCATE, ensure that your underlying file system supports sparse files. By default UNIX file systems, and GT.M, use sparse (or lazy) allocation, which defers actual allocation until blocks are first written. o Utilities such as du report typically show lower disk space usage for a database file with -DEFER_ALLOCATE because GT.M instructs the file system to defer disk space allocation to the time when there is an actual need. With -NODEFER_ALLOCATE, such utilities report higher disk space usage count as GT.M instructs the file system to preallocate disk space without waiting for a need to arise. o -DEFER_ALLOCATE makes database file extensions lighter weight. However, disk activity may tend towards causing fragmentation. o To switch an existing database file so it immediately preallocates all blocks, first use MUPIP SET -NODEFER_ALLOCATE to set the switch in the database file header, followed by MUPIP EXTEND -BLOCKS=n, where n >= 0. Failures to preallocate space produce a PREALLOCATEFAIL error. o The default is DEFER_ALLOCATE. -[NO]ENcryption Specifies whether or not the database file for a segment is flagged for encryption. Note that MUPIP CREATE acquires an encryption key for this file and puts a cryptographic hash of the key in the database file header. -EX[TENSION_COUNT]=blocks Specifies the number of extra GDS blocks of disk space by which the file should extend. The extend amount is interpreted as the number of usable GDS blocks to create with the extension. To calculate the number of host operating system blocks added with each extension, multiply the number of GDS blocks by (GDS BLOCK_SIZE/host BLOCK_SIZE); add one local bitmap block for each 512 blocks added in each extension to the amount from step 1. If the extension is not a multiple of 512, remember to roundup when figuring the number of bitmap blocks. When a MUPIP EXTEND command does not include a -BLOCKS= qualifier, EXTEND uses the extension size in the database header. The extension amount may be changed with the MUPIP SET command. The minimum EXTENSION is zero blocks. When a database file with automatic extension disabled (EXTENSION_COUNT=0) starts to get full, GT.M records the FREEBLSLOW warning in the system log. So as to not compromise performance, GT.M checks whenever the master bit map must be updated to show that a local bit map is full, and issues the warning if there are fewer than 512 free blocks or if the number of free blocks is less than total blocks/32. This means that for databases whose size is 512 blocks or less the warning comes at the last successful update before the database becomes full. The maximum EXTENSION is 1,048,575 blocks. By default, GDE uses an EXTENSION of 100 blocks. Like allocation, the default extension amount was chosen for initial development and experimentation. Use larger extensions for larger actual applications. Because multiple file extensions adversely affect performance, set up extensions appropriate to the file allocation. -F[ILE_NAME]=file-name Specifies the file for a segment. The maximum file name length is 255 characters. By default, GDE uses a file-name of mumps followed by the default extension, which is .dat. You can specify any filename and extension of your choice for a database file as long as it is valid on your operating system. -FU[LLBLKWRT]={0|1|2} Determines whether GT.M writes only valid database blocks contents, or a full block including meaningless trailing content. Full block writes are more efficient with some secondary storage because they avoid read-before-write. o When -FULLBLKWRT=2, a process writes all newly allocated database blocks in their entirety regardless of their actual valid contents. This relieves some file systems from tracking as much unallocated space and thus reduces file system metadata maintenance. o When -FULLBLKWRT=1, a process writes entire file system blocks in their entirety regardless of their actual valid contents, on some file systems, this avoids reading in advance of most writes and thus reduces file system load and increases response time. o When -FULLBLKWRT=0 (the default), a process writes only valid data. FIS advises using the same value of -FULLBLKWRT for all processes. **Note** When the file system block size and the database block size are the same there is no difference between the settings of 1 and 2. -G[LOBAL_BUFFER_COUNT]=size Specifies the number of global buffers for a file. Global buffers reside in shared memory and are part of the database caching mechanisms. Global buffers do not apply to MM databases. Choose the settings for this qualifier carefully. Small numbers of global buffers tend to throttle database performance. However, if your system has limited memory and the database file traffic is not heavy enough to hold the cache in RAM, increasing GLOBAL_BUFFER_COUNT may trigger paging. If database global buffers are paged out, it will result in poor performance. Therefore, do not increase this factor to a large value without careful observation. The proper number of GLOBAL_BUFFERs depends on the application and the amount of primary memory available on the system. Most production databases exhibit a direct relationship between the number of GLOBAL_BUFFERs and performance. However, the relationship is not linear, but asymptotic, so that increases past some point have progressively less benefit. This point of diminishing returns depends on the application. For most applications, FIS expects the optimum number of GLOBAL_BUFFERs to be between 1K and 64K. Because transaction processing can be involved in an update and a transaction is limited to half the GLOBAL_BUFFER_COUNT, the value for GLOBAL_BUFFER_COUNT should therefore be at least 32 plus twice the number of the blocks required by the largest global variable node in your application. Generally, you should increase the number of GLOBAL_BUFFERs for production GDS database files. This is because GT.M uses the shared memory database cache associated with each GDS file for the majority of caching. The minimum GLOBAL_BUFFER_COUNT for BG is 64 blocks. The maximum for GLOBAL_BUFFER_COUNT for BG is 2,097,151 blocks, but may vary depending on your platform. By default, GDE uses a GLOBAL_BUFFER_COUNT that is appropriate for initial development use on each platform, but probably too small for production applications. **Note** If global buffers are "paged out," improvements in system performance resulting from more global buffers will be more than offset by the dramatic slowdown that results from globals buffers that are "paged out." Out of the requested allocation, GT.M always reserves 32 global buffers for BG access method for read-only use to ensure that non-dirty global buffers are always available. -L[OCK_SPACE]=integer Specifies the number of pages of space to use for the lock database stored with this segment. The size of a page is always 512 bytes. As GT.M runs out of space to store LOCK control information, LOCKs become progressively less efficient. If a single process consumes all the LOCK space, it cannot continue, and any other processes cannot proceed using LOCKs. The minimum LOCK_SPACE is 10 pages. The maximum LOCK_SPACE is 262,144 pages. By default, GDE uses a LOCK_SPACE of 40 pages. LOCK_SPACE usage depends on the number of locks and the number of processes waiting for locks. To estimate lock space needs, here is a rule of thumb: o 1.5KiB overhead for the lock space, plus o 640 bytes for each lock base name, plus o 128 bytes for each subscript, plus o 128 bytes for each waiting process. Generally, you would limit LOCK_SPACE only when memory is scarce or you want to be made aware of unexpected levels of LOCK usage. For most other cases, there is no reason to limit the LOCK_SPACE. If you are introducing new code, FIS recommends using TSTART and TCOMMIT as a more efficient alternate for most LOCKs because it pushes the responsibility for Isolation onto GT.M, which internally manages them with optimistic algorithms. -M[UTEX_SLOTS]=integer Specifies the number of mutex slots for a database file. GT.M uses mutex slots to manage database contention. FIS recommends you configure the slots to cover the maximum number of processes you expect to concurrently access the database file, as an insufficient number of slots can lead to much steeper and more severe degradation of performance under heavy loads. The minimum is 1Ki and the maximum is 32Ki. -R[ESERVED_BYTES]=size Specifies the size to be reserved in each database block. RESERVED_BYTES is generally used to reserve room for compatibility with other implementations of M or to observe communications protocol restrictions. RESERVED_BYTES may also be used as a user-managed fill factor. The minimum RESERVED_BYTES is zero bytes. The maximum Reserved_Bytes = Block Size - Key Size - 40(block/record header overheads) By default, GDE uses a RESERVED_BYTES size of zero bytes. Summary The following table summarizes GDE segment qualifiers. It provides abbreviations, defaults (as provided by FIS), and allowable minimum and maximum values. +------------------------------------------------------------------------+ | GDE SEGMENT Qualifiers | |------------------------------------------------------------------------| | QUALIFIER | DEFAULT | MIN | MAX | |------------------------------------------------------------------------| | * May vary by platform | |------------------------------------------------------------------------| | -AC[CESS_METHOD]=BG|MM | BG | - | - | |------------------------------+-----------+-----+-----------------------| | -AL[LOCATION]=size (blocks) | 100 | 10 | 1,040,187,392(992Mi) | |------------------------------+-----------+-----+-----------------------| | -[NO]AS[YNCIO] | FALSE | - | - | |------------------------------+-----------+-----+-----------------------| | -BL[OCK_SIZE]=size (bytes) | 1,024 | 512 | 65,024 | |------------------------------+-----------+-----+-----------------------| | -[NO]DEFER_[ALLOCATE] | TRUE | - | - | |------------------------------+-----------+-----+-----------------------| | -[NO]E[NCRYPTION] | FALSE | - | - | |------------------------------+-----------+-----+-----------------------| | -EX[TENSION_COUNT]=size | 100 | 0 | 1,048,575 | | (blocks) | | | | |------------------------------+-----------+-----+-----------------------| | -F[ILE_NAME]=file-name | mumps.dat | - | 255 | | (chars) | | | | |------------------------------+-----------+-----+-----------------------| | -FU[LLBLKWRT]={0|1|2} | 0 | 0 | 2 | |------------------------------+-----------+-----+-----------------------| | -G[LOBAL_BUFFER_COUNT]=size | 1024 | 64 | 2,097,151* | | (blocks) | | | | |------------------------------+-----------+-----+-----------------------| | -L[OCK_SPACE]=size (pages) | 40 | 10 | 262,144 | |------------------------------+-----------+-----+-----------------------| | -M[UTEX_SLOTS]=integer | 1,024 | 64 | 32,768 | |------------------------------+-----------+-----+-----------------------| | -R[ESERVED_BYTES]=size | 0 | 0 | Block Size - Key Size | | (bytes) | | | - 32 | +------------------------------------------------------------------------+ 2 Gblname_Qualifiers Gblname Qualifiers The following -GBLNAME qualifier can be used with the ADD, CHANGE, or TEMPLATE commands. -C[OLLATION]=collation_number Specifies the collation number for a global name; a value of 0 specifies standard M collation. The first time that a GT.M processes accesses a global variable name in a database file, it determines the collation sequence as follows: o If a Global Variable Tree (GVT) exists (that is, global variable nodes exist, or have previously existed, even if they have been KILL'd), use the existing collation: o If there is a collation specified in the Directory Tree (DT) for that variable, use it after confirming that this matches the collation in the global directory. o else (that is, there is no collation specified in the DT): + If there is collation specified for that global variable in the global directory use it + else if there is a default for that database file, use it + else (that is, neither exists), use standard M collation o else (that is, a GVT does not exist, which in turn means there is no DT): o If there is collation specified for that global variable in the global directory use it o else, if there is a default for that database file, use it o else (that is, neither exists), use standard M collation 2 Instance_Qualifier Instance Qualifier The following -INSTANCE qualifier is used with the CHANGE command. -F[ILE_NAME=[repl_inst_filename|""] o -FILE_NAME=repl_inst_filename maps a replication instance file with the global directory. -FILE_NAME="" removes the mapping of a global directory with a replication instance file. o When a global directory is use, the mapping set with CHANGE -INSTANCE FILE_NAME=repl_inst_filename overrides any setting of the gtm_repl_instance environment variable. However, other utilities (MUPIP, LKE, and DSE) use the setting of the gtm_repl_instance environment variable. 1 Summary Summary The following table summarizes GDE commands, abbreviations, object types, required object names, and optional qualifiers. +------------------------------------------------------------------------+ | GDE Command Summary | |------------------------------------------------------------------------| | Command | Specified | Required Object Name/[Optional] Qualifier | | | Object Type | | |------------------------------------------------------------------------| | * -ALL is the default for the SHOW and VERIFY commands. | |------------------------------------------------------------------------| | @ | N/A | file-name | |------------+-------------+---------------------------------------------| | | | global-name | | A[DD] | -G[BLNAME] | | | | | -C[OLLATION]=collation | |------------+-------------+---------------------------------------------| | | | namespace | | - | -N[AME] | | | | | -R[EGION]=region-name | |------------+-------------+---------------------------------------------| | | | region-name | | - | -R[EGION] | | | | | -D[YNAMIC]=segment-name | | | | [-REGION-qualifier...] | |------------+-------------+---------------------------------------------| | | | segment-name | | - | -S[EGMENT] | | | | | -F[ILE_NAME]=file-name | | | | [-SEGMENT-qualifier...] | |------------+-------------+---------------------------------------------| | | | global-name | | C[HANGE] | -G[BLNAME] | | | | | -C[OLLATION]=collation | |------------+-------------+---------------------------------------------| | | | replication-instance | | - | -I[NSTANCE] | | | | | -F[ILE_NAME]=replication_instance_file | |------------+-------------+---------------------------------------------| | | | namespace | | - | -N[AME] | | | | | -R[EGION]=new-region | |------------+-------------+---------------------------------------------| | | | region-name | | - | -R[EGION] | | | | | [-REGION-qualifier...] | |------------+-------------+---------------------------------------------| | | | segment-name | | - | -S[EGMENT] | | | | | [-SEGMENT-qualifier] | |------------+-------------+---------------------------------------------| | | | global-name | | D[ELETE] | -G[BLNAME] | | | | | -C[OLLATION]=collation | |------------+-------------+---------------------------------------------| | - | -N[AME] | namespace | |------------+-------------+---------------------------------------------| | - | -R[EGION] | region-name | |------------+-------------+---------------------------------------------| | - | -S[EGMENT] | segment-name | |------------+-------------+---------------------------------------------| | E[XIT] | N/A | N/A | |------------+-------------+---------------------------------------------| | HE[LP] | N/A | Keyword | |------------+-------------+---------------------------------------------| | LOC[KS] | N/A | -R[EGION]=region-name | |------------+-------------+---------------------------------------------| | | | [-ON][=file-name] | | LOG | N/A | | | | | [-OF[F]] | |------------+-------------+---------------------------------------------| | Q[UIT] | N/A | N/A | |------------+-------------+---------------------------------------------| | | | global-name | | R[ENAME] | -G[BLNAME] | | | | | -C[OLLATION]=collation | |------------+-------------+---------------------------------------------| | - | -N[AME] | old-name new-name | |------------+-------------+---------------------------------------------| | - | -R[EGION] | old-reg-name new-reg-name | |------------+-------------+---------------------------------------------| | - | -S[EGMENT] | old-seg-name new-seg-name | |------------+-------------+---------------------------------------------| | SE[TGD] | N/A | -F[ILE]=file-name [-Q[UIT]] | |------------+-------------+---------------------------------------------| | SH[OW] | -A[LL]* | N/A | |------------+-------------+---------------------------------------------| | - | -C[OMMAND] | -F[ILE_NAME]=output-file | |------------+-------------+---------------------------------------------| | | | global-name | | - | -G[BLNAME] | | | | | -C[OLLATION]=collation | |------------+-------------+---------------------------------------------| | | | replication-instance | | - | -I[NSTANCE] | | | | | -F[ILE_NAME]=replication_instance_file | |------------+-------------+---------------------------------------------| | - | -M[AP] | [R[EGION]=region-name] | |------------+-------------+---------------------------------------------| | - | -N[AME] | [namespace] | |------------+-------------+---------------------------------------------| | - | -R[EGION] | [region-name] | |------------+-------------+---------------------------------------------| | - | -S[EGMENT] | [segment-name] | |------------+-------------+---------------------------------------------| | - | T[EMPLATE] | N/A | |------------+-------------+---------------------------------------------| | T[EMPLATE] | -R[EGION] | [-REGION-qualifier...] | |------------+-------------+---------------------------------------------| | - | -S[EGMENT] | [ -SEGMENT-qualifier...] | |------------+-------------+---------------------------------------------| | V[ERIFY] | -A[LL]* | N/A | |------------+-------------+---------------------------------------------| | | | global-name | | - | -G[BLNAME] | | | | | -C[OLLATION]=collation | |------------+-------------+---------------------------------------------| | - | -N[AME] | [namespace] | |------------+-------------+---------------------------------------------| | - | -R[EGION] | [region-name] | |------------+-------------+---------------------------------------------| | - | -S[EGMENT] | [segment-name] | |------------+-------------+---------------------------------------------| | - | -M[AP] | N/A | |------------+-------------+---------------------------------------------| | - | -T[EMPLATE] | N/A | +------------------------------------------------------------------------+ 2 Qualifier_Summary Qualifier Summary The following table summarizes all qualifiers for the ADD, CHANGE, and TEMPLATE commands. The defaults are those supplied by FIS. +-----------------------------------------------------------------------------------------------------+ | GDE Command Qualifiers | |-----------------------------------------------------------------------------------------------------| | QUALIFIER | DEF | MIN | MAX | NAM | REG | SEG | |-----------------------------------------------------------------------------------------------------| | * DEFAULT is the default region- and segment-name | | | | ** MUMPS is the default file-name | | | | *** May vary by platform | | | | **** -NONULL_SUBSCRIPTS | |-----------------------------------------------------------------------------------------------------| |-AC[CESS_METHOD]=code |BG |- |- |- |- |X | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-AL[LOCATION]=size(blocks) |100 |10 |1,040,187,392(992Mi) |- |- |X | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-[NO]AS[YNCIO] |FALSE |- |- |- |- |X | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-[NO]AU[TODB] |FALSE |- |- |- |X |- | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-BL[OCK_SIZE]=size(bytes) |1024 |512 |65024 |- |- |X | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-C[OLLATION_DEFAULT]=id-number(integer) |0 |0 |255 |- |X |- | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-[NO]DEFER_ALLOCATE |TRUE |- |- |- |- |X | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-D[YNAMIC_SEGMENT]=segment-name(chars) |* |1A |16A/N |- |X |- | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-[NO]ENCRYPTION |FALSE |- |- |- |- |X | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-[NO]EPOCHTAPER |TRUE |- |- |- |- |X | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-EX[TENSION_COUNT]=size(blks) |100 |0 |65535 |- |- |X | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-F[ILE_NAME]=file-name (chars) |** |1A |255A/N |- |- |X | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-FU[LLBLKWRT]={0|1|2} |0 |0 |2 |- |- |X | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-G[LOBAL_BUFFER_COUNT]=size(blocks) |1,024 |64 |2,147,483,647 *** |- |- |X | | |*** | | | | | | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-K[EY_SIZE]=size(bytes) |64 |3 |1,019 |- |X |- | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-[NO]L[OCK_CRIT] |FALSE |- |- |- |X |- | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-L[OCK_SPACE]=size(pages) |40 |10 |65,536 |- |- |X | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-M[UTEX_SLOTS]=integer |1,024 |1,024 |32,768 |- |- |X | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-[NO]INST[_FREEZE_ON_ERROR] |FALSE |- |- |- |X |- | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-[NO]Q[DBRUNDOWN] |FALSE |- |- |- |X |- | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-[NO]J[OURNAL]=option-list |-NOJ |- |- |- |X |- | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-N[ULL_SUBSCRIPTS]=[ALWAYS|NEVER|EXISTING] |NEVER |- |- |- |X |- | | |or ****| | | | | | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-[NO]STDNULLCOLL[=TRUE|FALSE] |FALSE |- |- |- |X |- | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-R[ECORD_SIZE]=size (bytes) |256 |7 |1,048,576 |- |X |- | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-R[EGION] region-name (chars) |* |1A |16A/N |X |- |- | |--------------------------------------------+-------+-------+----------------------+-----+-----+-----| |-R[ESERVED_BYTES]=size(bytes) |0 |0 |blocksize |- |- |X | +-----------------------------------------------------------------------------------------------------+ 1 Copyright Copyright Copyright 2022 Fidelity National Information Services, Inc. and/or its subsidiaries. All rights reserved. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts and no Back-Cover Texts. GT.M(TM) is a trademark of Fidelity National Information Services, Inc. Other trademarks are the property of their respective owners. This document contains a description of GT.M and the operating instructions pertaining to the various functions that comprise the system. This document does not contain any commitment of FIS. FIS believes the information in this publication is accurate as of its publication date; such information is subject to change without notice. FIS is not responsible for any errors or defects. fis-gtm-V7.0-005/sr_port/gde.m0000755000032200000250000001403214342376331014763 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2001-2022 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; gde: ;base module - d DEBUG^GDE to debug n ; clear calling process M variable state (if any) so it does not interfere with GDE variable names s (debug,runtime)=0 DBG: ;transfer point for DEBUG and "runtime" %gde ; Save parent process context before GDE tampers with it for its own necessities. ; Most of it is stored in the "gdeEntryState" local variable in subscripted nodes. ; Exceptions are local collation related act,ncol,nct values which have to be stored in in unsubscripted variables ; to prevent COLLDATAEXISTS error as part of the $$set^%LCLCOL below. n gdeEntryState,gdeEntryStateAct,gdeEntryStateNcol,gdeEntryStateNct s gdeEntryStateAct=$$get^%LCLCOL s gdeEntryStateNcol=$$getncol^%LCLCOL s gdeEntryStateNct=$$getnct^%LCLCOL ; Set local collation to what GDE wants to operate. Errors while doing so will have to exit GDE right away. ; Prepare special $etrap to issue error in case VIEW "YLCT" call to set local collation fails below ; Need to use this instead of the gde $etrap (set a few lines later below) as that expects some initialization ; to have happened whereas we are not yet there since setting local collation is a prerequisite for that init. s $et="w !,$p($zs,"","",3,999) s $ecode="""" zm 150503603:$zparse(""$gtmgbldir"","""",""*.gld"") quit" v "YLCT":0:1:0 ; sets local variable alternate collation = 0, null collation = 1, numeric collation = 0 ; since GDE creates null subscripts, we don't want user level setting of gtm_lvnullsubs to affect us in any way s gdeEntryState("nullsubs")=$v("LVNULLSUBS") v "LVNULLSUBS" s gdeEntryState("zlevel")=$zlevel-1 s gdeEntryState("io")=$io s $et=$s(debug:"b:$zs'[""%GDE""!allerrs ",1:"")_"g:(""%GDE%NONAME""[$p($p($zs,"","",3),""-"")) SHOERR^GDE d ABORT^GDE" s io=$io,useio="io",comlevel=0,combase=$zl,resume(comlevel)=$zl_":INTERACT" i $$set^%PATCODE("M") d GDEINIT^GDEINIT,GDEMSGIN^GDEMSGIN,GDFIND^GDESETGD,CREATE^GDEGET:create,LOAD^GDEGET:'create i debug s prompt="DEBUGDE>",uself="logfile" e s prompt="GDE>",uself="logfile:(ctrap=$c(3,25,26):exception=""d CTRL^GDE"")" e s useio="io:(ctrap=$c(3,25,26):exception=""d CTRL^GDE"")" u @useio ; comline is set to $ZCMDLINE on entry. If the entry zlevel is 0, set the resume point to exit i $l(comline) s:'gdeEntryState("zlevel") resume(comlevel)=$zl_":EXIT^GDEEXIT" d comline,EXIT^GDEEXIT i runtime s prompt="GD_SHOW>",verb="SHOW",x="" f s x=$o(syntab(x)) q:'$l(x) i x'="SHOW" k syntab(x) INTERACT f u io:ctrap=$c(25,26) w !,prompt," " r comline u @useio d comline:$l(comline) q GDELOG new $etrap set $etrap="w !,$zmessage(gdeerr(""GDELOGFAIL"")),! d GETOUT^GDEEXIT h" if $view("YLGDE"),$zauditlog(comline) quit comline: do GDELOG f cp=1:1 s c=$e(comline,cp) q:(c'=" ")&(c'=TAB) ; remove extraneous whitespace at beginning of line s ntoken="",ntoktype="TKEOL" s:runtime comline="/"_comline d GETTOK^GDESCAN i ntoktype="TKEOL" q ; if comline begins with a ! don't even bother parsing this line anymore i log u @uself w comline,! u @useio i runtime n NAME,REGION,SEGMENT,gqual,lquals zg:"/QUIT"[$tr(comline,lower,upper) combase-1 d SHOW^GDEPARSE q i ntoktype="TKAT" s resume(comlevel+1)=$zl d comfile q d GDEPARSE^GDEPARSE q CTRL i $p($zs,",",3,999)["%GTM-E-CTRAP, Character trap $C(3) encountered" do zg @resume(comlevel) . i comlevel>0 d comeof ; if we take a ctrl-c in a command file then get out of that command file i $p($zs,",",3,999)["%GTM-E-CTRAP, Character trap $C(25) encountered" d GETOUT^GDEEXIT h i $p($zs,",",3,999)["%GTM-E-CTRAP, Character trap $C(26) encountered" d EXIT^GDEEXIT i $p($zs,",",3,999)="%GTM-E-IOEOF, Attempt to read past an end-of-file" d . s $ecode="" ; clear IOEOF condition (not an error) so later GDE can exit with 0 status . d comexit i $zeof d EXIT^GDEEXIT d ABORT ; comexit: i 'update d QUIT^GDEQUIT i $$ALL^GDEVERIF,$$GDEPUT^GDEPUT s $zstatus="" e w $p($zm(gdeerr("VERIFY")\2*2),"!AD")_"FAILED" w ! d GETOUT^GDEEXIT h DBGCOMX u $i:exception="" s $et="" zm gdeerr("VERIFY"):"FAILED" w ! d GETOUT^GDEEXIT h comfile: d GETTOK^GDESCAN i ntoktype="TKEOL" zm gdeerr("QUALREQD"):"file specification" d TFSPEC^GDEPARSE ; remove trailing whitespaces in filename n i f i=$zl(value):-1 s c=$ze(value,i) q:(c'=" ")&(c'=TAB) ; remove trailing 0s s value=$ze(value,1,i) s (comfile,comfile(comlevel+1))=$zparse(value,"","",".COM") i '$l($zsearch(comfile)),'$l($zsearch(comfile)) zm gdeerr("FILENOTFND"):comfile e o comfile:(read:exc="zg "_$zl_":comeof") zm gdeerr("EXECOM"):comfile d SCRIPT comeof c comfile s comlevel=$select(comlevel>1:comlevel-1,1:0) i comlevel>0 s comfile=comfile(comlevel) zm gdeerr("EXECOM"):comfile e u @useio i $p($zs,",",3)'["%GTM-E-IOEOF",$p($zs,",",3)'["FILENOTFND" w !,$p($zs,",",3,9999),! e s ($ecode,$zstatus)="" ; clear IOEOF condition (not an error) so later GDE can exit with 0 status q SCRIPT: s comlevel=comlevel+1 f u comfile r comline u @useio d comline:$l(comline) ;this loop is terminated by the comfile exception at eof SHOERR w !,$p($zs,",",3,999),! s comlevel=$s(comlevel>1:comlevel-1,1:0) s $ecode="" zg @resume(comlevel) q ABORT s abortzs=$zs,abort="GDEDUMP.DMP",$et="" o abort:(newversion:noreadonly) u abort zsh "*" c abort u @useio ; make GDECHECK error fatal except native UNIX i $d(gdeerr) zm gdeerr("GDECHECK") Write $ZMessage($Select((256>abortzs):+abortzs,1:+abortzs\8*8+4)),! e w $zs d GETOUT^GDEEXIT h DEBUG ;entry point to debug gde n ; clear calling process M variable state (if any) so it does not interfere with GDE variable names s allerrs=0,debug=1,runtime=0 u 0:(ctrap="":exception="") zb DBGCOMX,ABORT g DBG fis-gtm-V7.0-005/sr_port/gdeadd.m0000755000032200000250000000520014342376331015431 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2001, 2013 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; add: ;implement the verb: ADD NAME i $d(nams(NAME)) zm gdeerr("OBJDUP"):"Name":$$namedisp^GDESHOW(NAME,0) i '$d(lquals("REGION")) zm gdeerr("QUALREQD"):"Region" ; check if changing (i.e. adding) a name (with ranges) poses issues with overlap amongst other existing name ranges d namerangeoverlapcheck^GDEPARSE(.NAME,lquals("REGION")) s update=1,nams=nams+1 m nams(NAME)=NAME s nams(NAME)=lquals("REGION") i $d(namrangeoverlap) d namcoalesce^GDEMAP q REGION i $d(regs(REGION)) zm gdeerr("OBJDUP"):"Region":REGION i '$d(lquals("DYNAMIC_SEGMENT")) zm gdeerr("QUALREQD"):"Dynamic_segment" i '$$RQUALS^GDEVERIF(.lquals) zm gdeerr("OBJNOTADD"):"Region":REGION s update=1,s="",regs=regs+1 f s s=$o(tmpreg(s)) q:'$l(s) s regs(REGION,s)=tmpreg(s) f s s=$o(lquals(s)) q:'$l(s) s regs(REGION,s)=lquals(s) i $d(segs),$d(regs(REGION,"DYNAMIC_SEGMENT")),$d(segs(regs(REGION,"DYNAMIC_SEGMENT"),"ACCESS_METHOD")) d . i "MM"=segs(regs(REGION,"DYNAMIC_SEGMENT"),"ACCESS_METHOD"),'$d(lquals("BEFORE_IMAGE")) s regs(REGION,"BEFORE_IMAGE")=0 . i "USER"[segs(regs(REGION,"DYNAMIC_SEGMENT"),"ACCESS_METHOD"),'$d(lquals("JOURNAL")) s regs(REGION,"JOURNAL")=0 q SEGMENT i $d(segs(SEGMENT)) zm gdeerr("OBJDUP"):"Segment":SEGMENT i '$d(lquals("FILE_NAME")) zm gdeerr("QUALREQD"):"File" s am=$s($d(lquals("ACCESS_METHOD")):lquals("ACCESS_METHOD"),1:tmpacc) i '$$SQUALS^GDEVERIF(am,.lquals) zm gdeerr("OBJNOTADD"):"Segment":SEGMENT s update=1,s="",segs=segs+1 s segs(SEGMENT,"ACCESS_METHOD")=am i "MM"=am s s="" f s s=$o(regs(s)) q:'$l(s) d . i regs(s,"DYNAMIC_SEGMENT")=SEGMENT,'$d(lquals("BEFORE_IMAGE")) s regs(s,"BEFORE_IMAGE")=0 d seg f s s=$o(lquals(s)) q:'$l(s) s segs(SEGMENT,s)=lquals(s) q seg: s segs(SEGMENT,"FILE_NAME")=lquals("FILE_NAME") f s s=$o(tmpseg(am,s)) q:'$l(s) s segs(SEGMENT,s)=tmpseg(am,s) q GBLNAME i $d(gnams(GBLNAME)) zm gdeerr("OBJDUP"):"Global Name":GBLNAME i '$d(lquals("COLLATION")) zm gdeerr("QUALREQD"):"Collation" ; check if changing (i.e. adding) collation for GBLNAME poses issues with existing names & ranges d gblnameeditchecks^GDEPARSE(GBLNAME,lquals("COLLATION")) i $d(namrangeoverlap) d namcoalesce^GDEMAP s update=1,gnams=gnams+1 s gnams(GBLNAME,"COLLATION")=lquals("COLLATION") q fis-gtm-V7.0-005/sr_port/gdechang.m0000644000032200000250000000536114342376331015766 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2001-2017 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; change: ;implement the verb: CHANGE NAME i '$d(nams(NAME)) zm gdeerr("OBJNOTFND"):"Name":$$namedisp^GDESHOW(NAME,0) i '$d(lquals("REGION")) zm gdeerr("QUALREQD"):"Region" ; check if changing the mapping of a name (with ranges) poses issues with overlap amongst other existing name ranges d namerangeoverlapcheck^GDEPARSE(.NAME,lquals("REGION")) s update=1 m nams(NAME)=NAME s nams(NAME)=lquals("REGION") i $d(namrangeoverlap) d namcoalesce^GDEMAP q REGION i '$d(regs(REGION)) zm gdeerr("OBJNOTFND"):"Region":REGION i '$$RQUALS^GDEVERIF(.lquals) zm gdeerr("OBJNOTCHG"):"region":REGION s update=1,s="" f s s=$o(lquals(s)) q:'$l(s) s regs(REGION,s)=lquals(s) q SEGMENT i '$d(segs(SEGMENT)) zm gdeerr("OBJNOTFND"):"Segment":SEGMENT s am=$s($d(lquals("ACCESS_METHOD")):lquals("ACCESS_METHOD"),1:segs(SEGMENT,"ACCESS_METHOD")) i '$$SQUALS^GDEVERIF(am,.lquals) zm gdeerr("OBJNOTCHG"):"segment":SEGMENT s update=1,s="" s segs(SEGMENT,"ACCESS_METHOD")=am f s s=$o(lquals(s)) q:'$l(s) s segs(SEGMENT,s)=lquals(s) f s s=$o(tmpseg(am,s)) q:'$l(s) d . i '$l(tmpseg(am,s)) s segs(SEGMENT,s)="" q . i '$l(segs(SEGMENT,s)) s segs(SEGMENT,s)=tmpseg(am,s) i ("MM"=am)!("USER"[am) d . s s="" f s s=$o(regs(s)) q:'$l(s) d . . i "MM"=am,(regs(s,"DYNAMIC_SEGMENT")=SEGMENT),'$d(lquals("BEFORE_IMAGE")) s regs(s,"BEFORE_IMAGE")=0 . . i "USER"[am,(regs(s,"DYNAMIC_SEGMENT")=SEGMENT),'$d(lquals("JOURNAL")) s regs(s,"JOURNAL")=0 q GBLNAME i '$d(gnams(GBLNAME)) zm gdeerr("OBJNOTFND"):"Global Name":GBLNAME i '$d(lquals("COLLATION")) zm gdeerr("QUALREQD"):"Collation" i gnams(GBLNAME,"COLLATION")=lquals("COLLATION") zm gdeerr("OBJNOTCHG"):"gblname":GBLNAME ; check if changing collation for GBLNAME poses issues with existing names & ranges d gblnameeditchecks^GDEPARSE(GBLNAME,lquals("COLLATION")) i $d(namrangeoverlap) d namcoalesce^GDEMAP s update=1 s gnams(GBLNAME,"COLLATION")=lquals("COLLATION") q INSTANCE i '$d(lquals("FILE_NAME")) zm gdeerr("QUALREQD"):"File Name" i $zl(lquals("FILE_NAME"))=0 d:$d(inst("FILE_NAME")) q . kill inst . s update=1,inst=0 i $d(inst("FILE_NAME")),inst("FILE_NAME")=lquals("FILE_NAME") d . i inst("FILE_NAME")'=$g(inst("envvar")) zm gdeerr("OBJNOTCHG"):"instance":"" s update=1 s inst=1,inst("FILE_NAME")=lquals("FILE_NAME") q fis-gtm-V7.0-005/sr_port/gdedelet.m0000755000032200000250000000246014342376331016003 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2001, 2013 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; delete: ;implement the verb: DELETE NAME i '$d(nams(NAME)) zm gdeerr("OBJNOTFND"):"Name":$$namedisp^GDESHOW(NAME,0) i NAME="*" zm gdeerr("LVSTARALON") ; deleting a name should not cause any new range overlap issues so no need to call "namerangeoverlapcheck" here s update=1 k nams(NAME) s nams=nams-1 q REGION i '$d(regs(REGION)) zm gdeerr("OBJNOTFND"):"Region":REGION s update=1 k regs(REGION) s regs=regs-1 q SEGMENT i '$d(segs(SEGMENT)) zm gdeerr("OBJNOTFND"):"Segment":SEGMENT s update=1 k segs(SEGMENT) s segs=segs-1 q GBLNAME i '$d(gnams(GBLNAME)) zm gdeerr("OBJNOTFND"):"Global Name":GBLNAME ; check if changing (i.e. deleting) collation for GBLNAME poses issues with existing names & ranges i $d(gnams(GBLNAME,"COLLATION")) d . d gblnameeditchecks^GDEPARSE(GBLNAME,0) . i $d(namrangeoverlap) d namcoalesce^GDEMAP s update=1 k gnams(GBLNAME) s gnams=gnams-1 q fis-gtm-V7.0-005/sr_port/gdeerrors.msg0000755000032200000250000001664014342376331016561 0ustar librarygtc!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! ! Copyright (c) 2001-2022 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. ! ! ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .FACILITY GDE, 248 /PREFIX = GDE_ .TITLE GDEERRORS Error Messages for GDE ! ! List of known undocumented messages follows (along with a comment) (do not change "known undocumented" phrase) ! ----- Buffer to introduce new undocumented error messages without affecting UNUSEDMSGnnn match with corresponding line numbers. ! BLKSIZ512 /info/fao=4 EXECOM /info/fao=2 FILENOTFND /error/fao=2 GDCREATE /info/fao=2 GDECHECK /info/fao=0 GDUNKNFMT /info/fao=2 GDUPDATE /info/fao=2 GDUSEDEFS /info/fao=2 ILLCHAR /error/fao=2 INPINTEG /fatal/fao=0 KEYTOOBIG /info/fao=4 KEYSIZIS /info/fao=2 KEYWRDAMB /error/fao=4 KEYWRDBAD /error/fao=4 LOADGD /info/fao=2 LOGOFF /info/fao=2 LOGON /info/fao=2 LVSTARALON /error/fao=0 MAPBAD /info/fao=8 MAPDUP /info/fao=10 NAMENDBAD /error/fao=2 NOACTION /info/fao=2 RPAREN /error/fao=0 NOEXIT /info/fao=0 NOLOG /info/fao=2 NOVALUE /error/fao=2 NONEGATE /error/fao=2 OBJDUP /error/fao=4 OBJNOTADD /error/fao=4 OBJNOTCHG /error/fao=4 OBJNOTFND /error/fao=4 OBJREQD /error/fao=2 PREFIXBAD /error/fao=6 QUALBAD /error/fao=2 QUALDUP /error/fao=2 QUALREQD /error/fao=2 RECTOOBIG /info/fao=6 RECSIZIS /info/fao=2 REGIS /info/fao=2 SEGIS /info/fao=4 VALTOOBIG /info/fao=6 VALTOOLONG /error/fao=6 VALTOOSMALL /info/fao=6 VALUEBAD /error/fao=4 VALUEREQD /error/fao=2 VERIFY /info/fao=2 BUFSIZIS /info/fao=2 BUFTOOSMALL /info/fao=4 MMNOBEFORIMG /info/fao=0 NOJNL /info/fao=2 GDREADERR /info/fao=2 GDNOTSET /info/fao=0 INVGBLDIR /info/fao=4 WRITEERROR /info/fao=2 NONASCII /error/fao=4 GDECRYPTNOMM /error/fao=2 JNLALLOCGROW /info/fao=8 KEYFORBLK /info/fao=6 STRMISSQUOTE /error/fao=2 GBLNAMEIS /info/fao=2 NAMSUBSEMPTY /error/fao=1 NAMSUBSBAD /error/fao=3 NAMNUMSUBSOFLOW /error/fao=3 NAMNUMSUBNOTEXACT /error/fao=3 MISSINGDELIM /error/fao=6 NAMRANGELASTSUB /error/fao=2 NAMSTARSUBSMIX /error/fao=2 NAMLPARENNOTBEG /error/fao=2 NAMRPARENNOTEND /error/fao=2 NAMONECOLON /error/fao=2 NAMRPARENMISSING /error/fao=2 NAMGVSUBSMAX /error/fao=3 NAMNOTSTRSUBS /error/fao=3 NAMSTRSUBSFUN /error/fao=3 NAMSTRSUBSLPAREN /error/fao=3 NAMSTRSUBSCHINT /error/fao=3 NAMSTRSUBSCHARG /error/fao=4 GBLNAMCOLLUNDEF /error/fao=3 NAMRANGEORDER /error/fao=3 NAMRANGEOVERLAP /error/fao=5 NAMGVSUBOFLOW /error/fao=5 GBLNAMCOLLRANGE /error/fao=1 STDNULLCOLLREQ /info/fao=4 GBLNAMCOLLVER /error/fao=5 GDEASYNCIONOMM /error/fao=2 NOPERCENTY <^%Y* is a reserved global name in GT.M>/error/fao=0 GDELOGFAIL /error/fao=0 .end fis-gtm-V7.0-005/sr_port/gdeexit.m0000755000032200000250000000512114342376331015654 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2001-2017 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; exit: ;implement the verb: EXIT EXIT i 'update d QUIT^GDEQUIT ; At $zlevel 1 and 2 the EXIT command is implied and therefore must exit i '$$ALL^GDEVERIF s $zstatus=gdeerr("NOEXIT") zm gdeerr("NOEXIT") d:3>$zlevel GETOUT^GDEEXIT q i '$$GDEPUT^GDEPUT q ; zm is issued in GDEPUT.m d GETOUT^GDEEXIT zg 0 GETOUT ; Routine executed just before exiting from GDE. This tries to restore the mumps process context ; to what it was at entry into GDE and then does a quit to the parent mumps program. ; This context would have been saved in the "gdeEntryState" variable. It is possible this variable ; is hidden due to argumentless/exclusive "new"s that happened inside GDE much after entry into GDE. ; In that case, there is nothing available to do the restore so skip the restore and "halt" out of ; the process to be safe (or else the parent mumps program could get confused). ; i '$data(gdeEntryState) zg 0 n nullsubs s nullsubs=+gdeEntryState("nullsubs") u gdeEntryState("io"):(exception="") ; restore $io with no exception (as otherwise it would be CTRL^GDE) v $select(nullsubs=0:"NOLVNULLSUBS",nullsubs=1:"LVNULLSUBS",nullsubs=2:"NEVERLVNULLSUBS") ; restore LVNULLSUBS setting ; Use unsubscripted variables for local collation related act,ncol,nct values as otherwise we could get ; COLLDATAEXISTS error if we are restoring the local collation (before exit from GDE) as part of the $$set^%LCLCOL below. ; For the same reason store zlevel info in an unsubscripted variable as it is needed for the zgoto at the end. n gdeEntryStateZlvl s gdeEntryStateZlvl=+gdeEntryState("zlevel") k (gdeEntryStateZlvl,gdeEntryStateAct,gdeEntryStateNcol,gdeEntryStateNct) i $$set^%LCLCOL(gdeEntryStateAct,gdeEntryStateNcol,gdeEntryStateNct) ; restores local variable collation characteristics ; If GDE was invoked from the shell, exit to shell with proper exit status else use ZGOTO to go to parent mumps invocation if gdeEntryStateZlvl=0 set $etrap="zgoto 0" zhalt +$zstatus zg gdeEntryStateZlvl ; this should exit GDE and return control to parent mumps process invocation zg 0 ; to be safe in case control ever reaches here fis-gtm-V7.0-005/sr_port/gdehelp.m0000755000032200000250000000110714342376331015633 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2001 Sanchez Computer Associates, 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; help: ;implement the verb: HELP HELP n helpline s helpline=$e(comline,cp-$l(ntoken),999) u io:ctrap=$c(3,25) zh helpline:helpfile u @useio q fis-gtm-V7.0-005/sr_port/gdeinit.m0000644000032200000250000005671014342376331015655 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2001-2020 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; gdeinit: ;set up local variables and arrays GDEINIT view "BADCHAR" s renpref="" s log=0,logfile="GDELOG.LOG",BOL="" s ONE=$c(1),TRUE=ONE,ZERO=$c(0),FALSE=ZERO,TAB=$c(9) s endian("x86","SCO")=FALSE,endian("x86","UWIN")=FALSE,endian("x86","Linux")=FALSE,endian("x86","CYGWIN")=FALSE s endian("x86_64","Linux")=FALSE s glo("SCO")=384,glo("UWIN")=1024,glo("Linux")=1024,glo("CYGWIN")=1024 s endian("IA64","HP-UX")=TRUE,glo("HP-UX")=1024 s endian("IA64","Linux")=FALSE,glo("Linux")=1024 s endian("MIPS","A25")=TRUE,glo("A25")=1024 s endian("RS6000","AIX")=TRUE,glo("AIX")=1024 s endian("S390","OS390")=TRUE,endian("S390X","Linux")=TRUE,glo("OS390")=1024 s HEX(0)=1 s gtm64=$p($zver," ",4) i "/RS6000/x86_64/x86/S390/S390X"[("/"_gtm64) s encsupportedplat=TRUE,gtm64=$s("x86"=gtm64:FALSE,1:TRUE) e s (encsupportedplat,gtm64)=FALSE i (gtm64=TRUE) d . f x=1:1:16 s HEX(x)=HEX(x-1)*16 i x#2=0 s TWO(x*4)=HEX(x) e f x=1:1:8 s HEX(x)=HEX(x-1)*16 i x#2=0 s TWO(x*4)=HEX(x) f i=25:1:30 s TWO(i)=TWO(i-1)*2 s TWO(31)=TWO(32)*.5 s lower="abcdefghijklmnopqrstuvwxyz",upper="ABCDEFGHIJKLMNOPQRSTUVWXYZ" s endian=endian($p($zver," ",4),$p($zver," ",3)) s ver=$p($zver," ",3) s defglo=glo(ver) s comline=$zcmdline s typevalue("STR2NUM","TNULLSUB","A")=1 s typevalue("STR2NUM","TNULLSUB","AL")=1 s typevalue("STR2NUM","TNULLSUB","ALW")=1 s typevalue("STR2NUM","TNULLSUB","ALWA")=1 s typevalue("STR2NUM","TNULLSUB","ALWAY")=1 s typevalue("STR2NUM","TNULLSUB","ALWAYS")=1 s typevalue("NUM2STR","TNULLSUB",1)="ALWAYS" s typevalue("STR2NUM","TNULLSUB","E")=2 s typevalue("STR2NUM","TNULLSUB","EX")=2 s typevalue("STR2NUM","TNULLSUB","EXI")=2 s typevalue("STR2NUM","TNULLSUB","EXIS")=2 s typevalue("STR2NUM","TNULLSUB","EXIST")=2 s typevalue("STR2NUM","TNULLSUB","EXISTI")=2 s typevalue("STR2NUM","TNULLSUB","EXISTIN")=2 s typevalue("STR2NUM","TNULLSUB","EXISTING")=2 s typevalue("NUM2STR","TNULLSUB",2)="EXISTING" s typevalue("STR2NUM","TNULLSUB","N")=0 s typevalue("STR2NUM","TNULLSUB","NE")=0 s typevalue("STR2NUM","TNULLSUB","NEV")=0 s typevalue("STR2NUM","TNULLSUB","NEVE")=0 s typevalue("STR2NUM","TNULLSUB","NEVER")=0 s typevalue("NUM2STR","TNULLSUB",0)="NEVER" s typevalue("STR2NUM","TNULLSUB","F")=0 s typevalue("STR2NUM","TNULLSUB","FA")=0 s typevalue("STR2NUM","TNULLSUB","FAL")=0 s typevalue("STR2NUM","TNULLSUB","FALS")=0 s typevalue("STR2NUM","TNULLSUB","FALSE")=0 s typevalue("STR2NUM","TNULLSUB","T")=1 s typevalue("STR2NUM","TNULLSUB","TR")=1 s typevalue("STR2NUM","TNULLSUB","TRU")=1 s typevalue("STR2NUM","TNULLSUB","TRUE")=1 s typevalue("STR2NUM","TACCMETH","BG")=0 s typevalue("STR2NUM","TACCMETH","MM")=1 s nommbi=1 ; this is used in GDEVERIF and should be removed along with the code when support is added d UNIX d syntabi ; ; Explanation of some of the SIZEOF local variable subscripts. ; SIZEOF("gd_segment") ; --> size of the "gd_segment" structure (defined in gdsfhead.h) ; i (gtm64=FALSE) d . s SIZEOF("am_offset")=336 ; --> offset of "acc_meth" field in the "gd_segment" structure . s SIZEOF("file_spec")=256 ; --> maximum size (in bytes) of a file name specified in gde command line . s SIZEOF("gd_contents")=80 ; --> size of the "gd_addr" structure (defined in gdsfhead.h) . s SIZEOF("gd_header")=16 ; --> 16-byte header structure at offset 0 of .gld (12 byte label, 4-byte filesize) . s SIZEOF("gd_map")=16 ; --> size of the "gd_binding" structure (defined in gdsfhead.h) . s SIZEOF("gd_region")=416 ; --> size of the "gd_region" structure (defined in gdsfhead.h) . s SIZEOF("gd_region_padding")=0 ; --> padding at end of "gd_region" structure (4-bytes for 64-bit platforms) . s SIZEOF("gd_segment")=372 ; --> size of the "gd_segment" structure (defined in gdsfhead.h) e d . s SIZEOF("am_offset")=340 ; --> offset of "acc_meth" field in the "gd_segment" structure . s SIZEOF("file_spec")=256 ; --> maximum size (in bytes) of a file name specified in gde command line . s SIZEOF("gd_contents")=120 ; --> size of the "gd_addr" structure (defined in gdsfhead.h) . s SIZEOF("gd_header")=16 ; --> 16-byte header structure at offset 0 of .gld (12 byte label, 4-byte filesize) . s SIZEOF("gd_map")=24 ; --> size of the "gd_binding" structure (defined in gdsfhead.h) . s SIZEOF("gd_region")=432 ; --> size of the "gd_region" structure (defined in gdsfhead.h) . s SIZEOF("gd_region_padding")=8 ; --> padding at end of "gd_region" structure (4-bytes for 64-bit platforms) . s SIZEOF("gd_segment")=384 ; --> size of the "gd_segment" structure (defined in gdsfhead.h) d gvstats s SIZEOF("blk_hdr")=16 s SIZEOF("dsk_blk")=512 s SIZEOF("gd_gblname")=40 s SIZEOF("gd_inst_info")=SIZEOF("file_spec") ; --> size of the "gd_inst_info" structure (defined in gdsfhead.h) s SIZEOF("max_str")=1048576 s SIZEOF("mident")=32 s SIZEOF("reg_jnl_deq")=4 s SIZEOF("rec_hdr")=4 ;GTM-6941 s MAXGVSUBS=31 ; needs to be equal to (MAX_GVSUBSCRIPTS-1) in mdef.h at all times s MAXNAMLN=SIZEOF("mident")-1,MAXREGLN=32,MAXSEGLN=32 ; maximum name length allowed is 31 characters s MAXSTRLEN=(2**20) ; needs to be equal to MAX_STRLEN in mdef.h at all times s (PARNAMLN,PARREGLN,PARSEGLN)=MAXNAMLN ; ; tokens are used for parsing and error reporting s tokens("TKIDENT")="identifier" s tokens("TKEOL")="end-of-line" s tokens("""")="TKSTRLIT",tokens("TKSTRLIT")="string literal" s tokens("@")="TKAT",tokens("TKAT")="at sign" s tokens(",")="TKCOMMA",tokens("TKCOMMA")="comma" s tokens("=")="TKEQUAL",tokens("TKEQUAL")="equal sign" s tokens("(")="TKLPAREN",tokens("TKLPAREN")="left parenthesis" s tokens(")")="TKRPAREN",tokens("TKRPAREN")="right parenthesis" s tokens("!")="TKEXCLAM",tokens("TKEXCLAM")="exclamation point" s tokens("-")="TKDASH",tokens("TKDASH")="dash" s tokens("")="TKEOL" ; for parsing purposes ; tokendelim is used for parsing; it is defined for all characters that can terminate a valid token during parsing for c=" ",TAB,"=",",",")","" s tokendelim(c)="" ; In the Unix shell command line, space is by default the separator for multiple qualifiers. ; GDE handles separators similarly. ; spacedelim is used for parsing when we want to terminate token parsing only if we see a white space (a subset of "tokendelim") ; this is needed in case we are parsing say a filename and we don't want a "-" or "=" in the file name to terminate the parse. for c=" ",TAB,"" s spacedelim(c)="" k c ; maximums and mimimums ; gblname s mingnam("COLLATION")=0 s maxgnam("COLLATION")=255 ; instance s maxinst("FILE")=SIZEOF("file_spec") ; region s minreg("ALIGNSIZE")=4096,maxreg("ALIGNSIZE")=4194304 ; geq RECORD_SIZE s minreg("ALLOCATION")=2048,maxreg("ALLOCATION")=8388607 s minreg("AUTODB")=0,maxreg("AUTODB")=1 s minreg("AUTOSWITCHLIMIT")=16384,maxreg("AUTOSWITCHLIMIT")=8388607 s minreg("BEFORE_IMAGE")=0,maxreg("BEFORE_IMAGE")=1 s minreg("BUFFER_SIZE")=2307,maxreg("BUFFER_SIZE")=1048576 s minreg("COLLATION_DEFAULT")=0,maxreg("COLLATION_DEFAULT")=255 s minreg("EPOCH_INTERVAL")=1,maxreg("EPOCH_INTERVAL")=32767 s minreg("EPOCHTAPER")=0,maxreg("EPOCHTAPER")=1 s minreg("EXTENSION")=0,maxreg("EXTENSION")=1073741823 s minreg("INST_FREEZE_ON_ERROR")=0,maxreg("INST_FREEZE_ON_ERROR")=1 s minreg("JOURNAL")=0,maxreg("JOURNAL")=1 s minreg("KEY_SIZE")=3 s maxreg("KEY_SIZE")=1019 ; = max value of KEY->end that returns TRUE for CAN_APPEND_HIDDEN_SUBS(KEY) in gdsfhead.h s minreg("LOCK_CRIT")=0,maxreg("LOCK_CRIT")=1 s minreg("NULL_SUBSCRIPTS")=0,maxreg("NULL_SUBSCRIPTS")=2 s minreg("QDBRUNDOWN")=0,maxreg("QDBRUNDOWN")=1 s minreg("RECORD_SIZE")=0,maxreg("RECORD_SIZE")=SIZEOF("max_str") s minreg("STATS")=0,maxreg("STATS")=1 s minreg("STDNULLCOLL")=0,maxreg("STDNULLCOLL")=1 s minreg("SYNC_IO")=0,maxreg("SYNC_IO")=1 s minreg("YIELD_LIMIT")=0,maxreg("YIELD_LIMIT")=2048 ; Store defaults in "dflreg". "tmpreg" serves this purpose for the most part but it is user-editable (using the ; TEMPLATE command) but for things where we want to use the GT.M-default value (not user-edited default value) ; we use "dflreg". Currently the only parameter we care about is KEY_SIZE which is used for setting statsdb characteristics s dflreg("KEY_SIZE")=64 ; should be maintained in parallel with STATSDB_KEY_SIZE in gdsfhead.h ; segments ; First define segment characteristics (minimum and maximum) that are identical to BG and MM access methods ; Then define overrides specific to BG and MM n minsegcommon,maxsegcommon s minsegcommon("ALLOCATION")=10,maxsegcommon("ALLOCATION")=TWO(30)-TWO(25) ; supports 992M blocks s minsegcommon("BLOCK_SIZE")=SIZEOF("dsk_blk"),maxsegcommon("BLOCK_SIZE")=HEX(4)-SIZEOF("dsk_blk") s minsegcommon("EXTENSION_COUNT")=0,maxsegcommon("EXTENSION_COUNT")=HEX(5)-1 s minsegcommon("LOCK_SPACE")=10,maxsegcommon("LOCK_SPACE")=262144 s minsegcommon("MUTEX_SLOTS")=64 ; keep this in sync with MIN_CRIT_ENTRY in gdsbt.h s maxsegcommon("MUTEX_SLOTS")=32768 ; keep this in sync with MAX_CRIT_ENTRY in gdsbt.h s minsegcommon("RESERVED_BYTES")=0,maxsegcommon("RESERVED_BYTES")=HEX(4)-SIZEOF("dsk_blk") s minsegcommon("FULLBLKWRT")=0,maxsegcommon("FULLBLKWRT")=2 ; bg m minseg("BG")=minsegcommon,maxseg("BG")=maxsegcommon ; copy over all common stuff into BG access method first ; now add BG specific overrides; GTM64_WC_MAX_BUFFS defined in gdsbt.h s minseg("BG","GLOBAL_BUFFER_COUNT")=64,maxseg("BG","GLOBAL_BUFFER_COUNT")=$select(gtm64=TRUE:2097151,1:65536) ; mm m minseg("MM")=minsegcommon,maxseg("MM")=maxsegcommon ; copy over all common stuff into MM access method first ; now add MM specific overrides s minseg("MM","DEFER")=0,maxseg("MM","DEFER")=86400 ; Now define default segment characteristics ; This is particularly needed for fields that are only available in more recent .gld formats ; So if we are reading an older format .gld file, we can use these as the default values. s defseg("ALLOCATION")=100 s defseg("ASYNCIO")=0 s defseg("FULLBLKWRT")=0 s defseg("BLOCK_SIZE")=4096 s defseg("BUCKET_SIZE")="" s defseg("DEFER_ALLOCATE")=1 s defseg("ENCRYPTION_FLAG")=0 s defseg("EXTENSION_COUNT")=100 s defseg("FILE_TYPE")="DYNAMIC" s defseg("LOCK_SPACE")=40 s defseg("MUTEX_SLOTS")=1024 ; keep this in sync with DEFAULT_NUM_CRIT_ENTRY in gdsbt.h s defseg("RESERVED_BYTES")=0 s defseg("WINDOW_SIZE")="" q ;----------------------------------------------------------------------------------------------------------------------------------- ; gde command language syntax table syntabi: s syntab("ADD","GBLNAME")="" s syntab("ADD","GBLNAME","COLLATION")="REQUIRED" s syntab("ADD","GBLNAME","COLLATION","TYPE")="TNUMBER" s syntab("ADD","NAME")="" s syntab("ADD","NAME","REGION")="REQUIRED" s syntab("ADD","NAME","REGION","TYPE")="TREGION" s syntab("ADD","REGION")="" s syntab("ADD","REGION","AUTODB")="NEGATABLE" s syntab("ADD","REGION","COLLATION_DEFAULT")="REQUIRED" s syntab("ADD","REGION","COLLATION_DEFAULT","TYPE")="TNUMBER" s syntab("ADD","REGION","DYNAMIC_SEGMENT")="REQUIRED" s syntab("ADD","REGION","DYNAMIC_SEGMENT","TYPE")="TSEGMENT" s syntab("ADD","REGION","EPOCHTAPER")="NEGATABLE" s syntab("ADD","REGION","INST_FREEZE_ON_ERROR")="NEGATABLE" s syntab("ADD","REGION","JOURNAL")="NEGATABLE,REQUIRED,LIST" s syntab("ADD","REGION","JOURNAL","ALLOCATION")="REQUIRED" s syntab("ADD","REGION","JOURNAL","ALLOCATION","TYPE")="TNUMBER" s syntab("ADD","REGION","JOURNAL","AUTOSWITCHLIMIT")="REQUIRED" s syntab("ADD","REGION","JOURNAL","AUTOSWITCHLIMIT","TYPE")="TNUMBER" s syntab("ADD","REGION","JOURNAL","BUFFER_SIZE")="REQUIRED" s syntab("ADD","REGION","JOURNAL","BUFFER_SIZE","TYPE")="TNUMBER" s syntab("ADD","REGION","JOURNAL","BEFORE_IMAGE")="NEGATABLE" s syntab("ADD","REGION","JOURNAL","EXTENSION")="REQUIRED" s syntab("ADD","REGION","JOURNAL","EXTENSION","TYPE")="TNUMBER" s syntab("ADD","REGION","JOURNAL","FILE_NAME")="REQUIRED" s syntab("ADD","REGION","JOURNAL","FILE_NAME","TYPE")="TFSPEC" s syntab("ADD","REGION","KEY_SIZE")="REQUIRED" s syntab("ADD","REGION","KEY_SIZE","TYPE")="TNUMBER" s syntab("ADD","REGION","LOCK_CRIT")="NEGATABLE" s syntab("ADD","REGION","NULL_SUBSCRIPTS")="NEGATABLE,REQUIRED" s syntab("ADD","REGION","NULL_SUBSCRIPTS","TYPE")="TNULLSUB" s syntab("ADD","REGION","QDBRUNDOWN")="NEGATABLE" s syntab("ADD","REGION","RECORD_SIZE")="REQUIRED" s syntab("ADD","REGION","RECORD_SIZE","TYPE")="TNUMBER" s syntab("ADD","REGION","STATS")="NEGATABLE" s syntab("ADD","REGION","STDNULLCOLL")="NEGATABLE" s syntab("ADD","SEGMENT")="" s syntab("ADD","SEGMENT","ACCESS_METHOD")="REQUIRED" s syntab("ADD","SEGMENT","ACCESS_METHOD","TYPE")="TACCMETH" s syntab("ADD","SEGMENT","ALLOCATION")="REQUIRED" s syntab("ADD","SEGMENT","ALLOCATION","TYPE")="TNUMBER" s syntab("ADD","SEGMENT","ASYNCIO")="NEGATABLE" s syntab("ADD","SEGMENT","FULLBLKWRT")="REQUIRED" s syntab("ADD","SEGMENT","FULLBLKWRT","TYPE")="TNUMBER" s syntab("ADD","SEGMENT","BLOCK_SIZE")="REQUIRED" s syntab("ADD","SEGMENT","BLOCK_SIZE","TYPE")="TNUMBER" s syntab("ADD","SEGMENT","BUCKET_SIZE")="REQUIRED" s syntab("ADD","SEGMENT","BUCKET_SIZE","TYPE")="TNUMBER" s syntab("ADD","SEGMENT","DEFER")="NEGATABLE" s syntab("ADD","SEGMENT","DEFER_ALLOCATE")="NEGATABLE" s syntab("ADD","SEGMENT","ENCRYPTION_FLAG")="NEGATABLE" s syntab("ADD","SEGMENT","EXTENSION_COUNT")="REQUIRED" s syntab("ADD","SEGMENT","EXTENSION_COUNT","TYPE")="TNUMBER" s syntab("ADD","SEGMENT","FILE_NAME")="REQUIRED" s syntab("ADD","SEGMENT","FILE_NAME","TYPE")="TFSPEC" s syntab("ADD","SEGMENT","GLOBAL_BUFFER_COUNT")="REQUIRED" s syntab("ADD","SEGMENT","GLOBAL_BUFFER_COUNT","TYPE")="TNUMBER" s syntab("ADD","SEGMENT","LOCK_SPACE")="REQUIRED" s syntab("ADD","SEGMENT","LOCK_SPACE","TYPE")="TNUMBER" s syntab("ADD","SEGMENT","MUTEX_SLOTS")="REQUIRED" s syntab("ADD","SEGMENT","MUTEX_SLOTS","TYPE")="TNUMBER" s syntab("ADD","SEGMENT","RESERVED_BYTES")="REQUIRED" s syntab("ADD","SEGMENT","RESERVED_BYTES","TYPE")="TNUMBER" s syntab("ADD","SEGMENT","WINDOW_SIZE")="REQUIRED" s syntab("ADD","SEGMENT","WINDOW_SIZE","TYPE")="TNUMBER" s syntab("CHANGE","GBLNAME")="" s syntab("CHANGE","GBLNAME","COLLATION")="REQUIRED" s syntab("CHANGE","GBLNAME","COLLATION","TYPE")="TNUMBER" s syntab("CHANGE","INSTANCE")="" s syntab("CHANGE","INSTANCE","FILE_NAME")="REQUIRED" s syntab("CHANGE","INSTANCE","FILE_NAME","TYPE")="TFSPEC" s syntab("CHANGE","NAME")="" s syntab("CHANGE","NAME","REGION")="REQUIRED" s syntab("CHANGE","NAME","REGION","TYPE")="TREGION" s syntab("CHANGE","REGION")="" s syntab("CHANGE","REGION","AUTODB")="NEGATABLE" s syntab("CHANGE","REGION","COLLATION_DEFAULT")="REQUIRED" s syntab("CHANGE","REGION","COLLATION_DEFAULT","TYPE")="TNUMBER" s syntab("CHANGE","REGION","DYNAMIC_SEGMENT")="REQUIRED" s syntab("CHANGE","REGION","DYNAMIC_SEGMENT","TYPE")="TSEGMENT" s syntab("CHANGE","REGION","EPOCHTAPER")="NEGATABLE" s syntab("CHANGE","REGION","INST_FREEZE_ON_ERROR")="NEGATABLE" s syntab("CHANGE","REGION","JOURNAL")="NEGATABLE,REQUIRED,LIST" s syntab("CHANGE","REGION","JOURNAL","ALLOCATION")="REQUIRED" s syntab("CHANGE","REGION","JOURNAL","ALLOCATION","TYPE")="TNUMBER" s syntab("CHANGE","REGION","JOURNAL","AUTOSWITCHLIMIT")="REQUIRED" s syntab("CHANGE","REGION","JOURNAL","AUTOSWITCHLIMIT","TYPE")="TNUMBER" s syntab("CHANGE","REGION","JOURNAL","BEFORE_IMAGE")="NEGATABLE" s syntab("CHANGE","REGION","JOURNAL","BUFFER_SIZE")="REQUIRED" s syntab("CHANGE","REGION","JOURNAL","BUFFER_SIZE","TYPE")="TNUMBER" s syntab("CHANGE","REGION","JOURNAL","EXTENSION")="REQUIRED" s syntab("CHANGE","REGION","JOURNAL","EXTENSION","TYPE")="TNUMBER" s syntab("CHANGE","REGION","JOURNAL","FILE_NAME")="REQUIRED" s syntab("CHANGE","REGION","JOURNAL","FILE_NAME","TYPE")="TFSPEC" s syntab("CHANGE","REGION","KEY_SIZE")="REQUIRED" s syntab("CHANGE","REGION","KEY_SIZE","TYPE")="TNUMBER" s syntab("CHANGE","REGION","LOCK_CRIT")="NEGATABLE" s syntab("CHANGE","REGION","NULL_SUBSCRIPTS")="NEGATABLE,REQUIRED" s syntab("CHANGE","REGION","NULL_SUBSCRIPTS","TYPE")="TNULLSUB" s syntab("CHANGE","REGION","QDBRUNDOWN")="NEGATABLE" s syntab("CHANGE","REGION","RECORD_SIZE")="REQUIRED" s syntab("CHANGE","REGION","RECORD_SIZE","TYPE")="TNUMBER" s syntab("CHANGE","REGION","STATS")="NEGATABLE" s syntab("CHANGE","REGION","STDNULLCOLL")="NEGATABLE" s syntab("CHANGE","SEGMENT")="" s syntab("CHANGE","SEGMENT","ACCESS_METHOD")="REQUIRED" s syntab("CHANGE","SEGMENT","ACCESS_METHOD","TYPE")="TACCMETH" s syntab("CHANGE","SEGMENT","ALLOCATION")="REQUIRED" s syntab("CHANGE","SEGMENT","ALLOCATION","TYPE")="TNUMBER" s syntab("CHANGE","SEGMENT","ASYNCIO")="NEGATABLE" s syntab("CHANGE","SEGMENT","FULLBLKWRT")="REQUIRED" s syntab("CHANGE","SEGMENT","FULLBLKWRT","TYPE")="TNUMBER" s syntab("CHANGE","SEGMENT","BLOCK_SIZE")="REQUIRED" s syntab("CHANGE","SEGMENT","BLOCK_SIZE","TYPE")="TNUMBER" s syntab("CHANGE","SEGMENT","BUCKET_SIZE")="REQUIRED" s syntab("CHANGE","SEGMENT","BUCKET_SIZE","TYPE")="TNUMBER" s syntab("CHANGE","SEGMENT","DEFER")="NEGATABLE" s syntab("CHANGE","SEGMENT","DEFER_ALLOCATE")="NEGATABLE" s syntab("CHANGE","SEGMENT","ENCRYPTION_FLAG")="NEGATABLE" s syntab("CHANGE","SEGMENT","EXTENSION_COUNT")="REQUIRED" s syntab("CHANGE","SEGMENT","EXTENSION_COUNT","TYPE")="TNUMBER" s syntab("CHANGE","SEGMENT","FILE_NAME")="REQUIRED" s syntab("CHANGE","SEGMENT","FILE_NAME","TYPE")="TFSPEC" s syntab("CHANGE","SEGMENT","GLOBAL_BUFFER_COUNT")="REQUIRED" s syntab("CHANGE","SEGMENT","GLOBAL_BUFFER_COUNT","TYPE")="TNUMBER" s syntab("CHANGE","SEGMENT","LOCK_SPACE")="REQUIRED" s syntab("CHANGE","SEGMENT","LOCK_SPACE","TYPE")="TNUMBER" s syntab("CHANGE","SEGMENT","MUTEX_SLOTS")="REQUIRED" s syntab("CHANGE","SEGMENT","MUTEX_SLOTS","TYPE")="TNUMBER" s syntab("CHANGE","SEGMENT","RESERVED_BYTES")="REQUIRED" s syntab("CHANGE","SEGMENT","RESERVED_BYTES","TYPE")="TNUMBER" s syntab("CHANGE","SEGMENT","WINDOW_SIZE")="REQUIRED" s syntab("CHANGE","SEGMENT","WINDOW_SIZE","TYPE")="TNUMBER" s syntab("TEMPLATE","REGION")="" s syntab("TEMPLATE","REGION","AUTODB")="NEGATABLE" s syntab("TEMPLATE","REGION","COLLATION_DEFAULT")="REQUIRED" s syntab("TEMPLATE","REGION","COLLATION_DEFAULT","TYPE")="TNUMBER" s syntab("TEMPLATE","REGION","DYNAMIC_SEGMENT")="REQUIRED" s syntab("TEMPLATE","REGION","DYNAMIC_SEGMENT","TYPE")="TSEGMENT" s syntab("TEMPLATE","REGION","EPOCHTAPER")="NEGATABLE" s syntab("TEMPLATE","REGION","INST_FREEZE_ON_ERROR")="NEGATABLE" s syntab("TEMPLATE","REGION","JOURNAL")="NEGATABLE,REQUIRED,LIST" s syntab("TEMPLATE","REGION","JOURNAL","ALLOCATION")="REQUIRED" s syntab("TEMPLATE","REGION","JOURNAL","ALLOCATION","TYPE")="TNUMBER" s syntab("TEMPLATE","REGION","JOURNAL","AUTOSWITCHLIMIT")="REQUIRED" s syntab("TEMPLATE","REGION","JOURNAL","AUTOSWITCHLIMIT","TYPE")="TNUMBER" s syntab("TEMPLATE","REGION","JOURNAL","BEFORE_IMAGE")="NEGATABLE" s syntab("TEMPLATE","REGION","JOURNAL","BUFFER_SIZE")="REQUIRED" s syntab("TEMPLATE","REGION","JOURNAL","BUFFER_SIZE","TYPE")="TNUMBER" s syntab("TEMPLATE","REGION","JOURNAL","EXTENSION")="REQUIRED" s syntab("TEMPLATE","REGION","JOURNAL","EXTENSION","TYPE")="TNUMBER" s syntab("TEMPLATE","REGION","JOURNAL","FILE_NAME")="REQUIRED" s syntab("TEMPLATE","REGION","JOURNAL","FILE_NAME","TYPE")="TFSPEC" s syntab("TEMPLATE","REGION","KEY_SIZE")="REQUIRED" s syntab("TEMPLATE","REGION","KEY_SIZE","TYPE")="TNUMBER" s syntab("TEMPLATE","REGION","LOCK_CRIT")="NEGATABLE" s syntab("TEMPLATE","REGION","NULL_SUBSCRIPTS")="NEGATABLE,REQUIRED" s syntab("TEMPLATE","REGION","NULL_SUBSCRIPTS","TYPE")="TNULLSUB" s syntab("TEMPLATE","REGION","QDBRUNDOWN")="NEGATABLE" s syntab("TEMPLATE","REGION","RECORD_SIZE")="REQUIRED" s syntab("TEMPLATE","REGION","RECORD_SIZE","TYPE")="TNUMBER" s syntab("TEMPLATE","REGION","STATS")="NEGATABLE" s syntab("TEMPLATE","REGION","STDNULLCOLL")="NEGATABLE" s syntab("TEMPLATE","SEGMENT")="" s syntab("TEMPLATE","SEGMENT","ACCESS_METHOD")="REQUIRED" s syntab("TEMPLATE","SEGMENT","ACCESS_METHOD","TYPE")="TACCMETH" s syntab("TEMPLATE","SEGMENT","ALLOCATION")="REQUIRED" s syntab("TEMPLATE","SEGMENT","ALLOCATION","TYPE")="TNUMBER" s syntab("TEMPLATE","SEGMENT","ASYNCIO")="NEGATABLE" s syntab("TEMPLATE","SEGMENT","FULLBLKWRT")="REQUIRED" s syntab("TEMPLATE","SEGMENT","FULLBLKWRT","TYPE")="TNUMBER" s syntab("TEMPLATE","SEGMENT","BLOCK_SIZE")="REQUIRED" s syntab("TEMPLATE","SEGMENT","BLOCK_SIZE","TYPE")="TNUMBER" s syntab("TEMPLATE","SEGMENT","BUCKET_SIZE")="REQUIRED" s syntab("TEMPLATE","SEGMENT","BUCKET_SIZE","TYPE")="TNUMBER" s syntab("TEMPLATE","SEGMENT","DEFER")="NEGATABLE" s syntab("TEMPLATE","SEGMENT","DEFER_ALLOCATE")="NEGATABLE" s syntab("TEMPLATE","SEGMENT","ENCRYPTION_FLAG")="NEGATABLE" s syntab("TEMPLATE","SEGMENT","EXTENSION_COUNT")="REQUIRED" s syntab("TEMPLATE","SEGMENT","EXTENSION_COUNT","TYPE")="TNUMBER" s syntab("TEMPLATE","SEGMENT","FILE_NAME")="REQUIRED" s syntab("TEMPLATE","SEGMENT","FILE_NAME","TYPE")="TFSPEC" s syntab("TEMPLATE","SEGMENT","GLOBAL_BUFFER_COUNT")="REQUIRED" s syntab("TEMPLATE","SEGMENT","GLOBAL_BUFFER_COUNT","TYPE")="TNUMBER" s syntab("TEMPLATE","SEGMENT","LOCK_SPACE")="REQUIRED" s syntab("TEMPLATE","SEGMENT","LOCK_SPACE","TYPE")="TNUMBER" s syntab("TEMPLATE","SEGMENT","MUTEX_SLOTS")="REQUIRED" s syntab("TEMPLATE","SEGMENT","MUTEX_SLOTS","TYPE")="TNUMBER" s syntab("TEMPLATE","SEGMENT","RESERVED_BYTES")="REQUIRED" s syntab("TEMPLATE","SEGMENT","RESERVED_BYTES","TYPE")="TNUMBER" s syntab("TEMPLATE","SEGMENT","WINDOW_SIZE")="REQUIRED" s syntab("TEMPLATE","SEGMENT","WINDOW_SIZE","TYPE")="TNUMBER" s syntab("DELETE","GBLNAME")="" s syntab("DELETE","NAME")="" s syntab("DELETE","REGION")="" s syntab("DELETE","SEGMENT")="" s syntab("EXIT")="" s syntab("HELP")="" s syntab("LOCKS","REGION")="REQUIRED" s syntab("LOCKS","REGION","TYPE")="TREGION" s syntab("LOG","OFF")="" s syntab("LOG","ON")="OPTIONAL" s syntab("LOG","ON","TYPE")="TFSPEC" s syntab("QUIT")="" s syntab("RENAME","GBLNAME")="" s syntab("RENAME","NAME")="" s syntab("RENAME","REGION")="" s syntab("RENAME","SEGMENT")="" s syntab("SETGD","FILE")="REQUIRED" s syntab("SETGD","FILE","TYPE")="TFSPEC" s syntab("SETGD","QUIT")="" s syntab("SHOW")="" s syntab("SHOW","ALL")="" s syntab("SHOW","COMMANDS")="" s syntab("SHOW","COMMANDS","FILE")="OPTIONAL" s syntab("SHOW","COMMANDS","FILE","TYPE")="TFSPEC" s syntab("SHOW","GBLNAME")="" s syntab("SHOW","INSTANCE")="" s syntab("SHOW","MAP")="" s syntab("SHOW","MAP","REGION")="REQUIRED" s syntab("SHOW","MAP","REGION","TYPE")="TREGION" s syntab("SHOW","NAME")="" s syntab("SHOW","REGION")="" s syntab("SHOW","SEGMENT")="" s syntab("SHOW","TEMPLATE")="" s syntab("SPAWN")="" s syntab("VERIFY","ALL")="" s syntab("VERIFY","GBLNAME")="" s syntab("VERIFY","MAP")="" s syntab("VERIFY","NAME")="" s syntab("VERIFY","REGION")="" s syntab("VERIFY","SEGMENT")="" s syntab("VERIFY","TEMPLATE")="" q UNIX: s hdrlab="GTCGBDUNX015" ; must be concurrently maintained in gbldirnam.h!!! i (gtm64=TRUE) s hdrlab="GTCGBDUNX115" ; the high order digit is a 64-bit flag s tfile="$gtmgbldir" s accmeth="\BG\MM" s helpfile="$gtm_dist/gdehelp.gld" s defdb="mumps.dat" s defgld="mumps.gld",defgldext="*.gld" s defreg="DEFAULT" s defseg="DEFAULT" s dbfilpar="1E" s filexfm="filespec" s sep="TKDASH" q gvstats ; Obtain the gvstats size from the output of ZSHOW "G" n zshow zsh "G":zshow s SIZEOF("gvstats")=($length(zshow("G",0),",")-2)*8 ; --> Field count, minus GLD/REG fields, times 8 bytes per stat. q fis-gtm-V7.0-005/sr_port/gdelocks.m0000755000032200000250000000100714342376331016015 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2001 Sanchez Computer Associates, 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; locks: ;implement the verb: LOCKS LOCKS s update=1 s nams("#")=gqual("value") q fis-gtm-V7.0-005/sr_port/gdelog.m0000755000032200000250000000134714342376331015472 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2001 Sanchez Computer Associates, 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; log: ;implement the verb: LOG LOG i gqual="OFF" s log=0 zm gdeerr("LOGOFF"):logfile q s log=1 i $d(gqual("value")) s logfile=$zparse(gqual("value"),"","",".LOG") o logfile:(newversion:noreadonly) zm gdeerr("LOGON"):logfile q INQUIRE i 'log zm gdeerr("NOLOG"):logfile e zm gdeerr("LOGON"):logfile q fis-gtm-V7.0-005/sr_port/gdemap.m0000755000032200000250000011065414342376331015470 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2010-2017 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; map: ;create maps for put and show, names for get and show CREATEGLDMAP ; create map for GDEPUT to write to .gld file k lexnams n suffix,s1,s2 d NAM2MAP s s1="",$zpi(s1,$zch(255),SIZEOF("mident"))="" f s s2=s1,s1=$o(map(s1),-1),map(s2)=map(s1) q:s1="$" k map(s1) s map("%")=map("$") f s2="#","$" k map(s2) q ;---------------------------------------------------------------------------------------------------------------------------------- NAM2MAP ; ; transform nams() array to map() array ; n lexnams s s="" s map("$")=nams("*"),map("#")=nams("#") ; initialize map() array for "*" name and local-locks ("#" name). f s s=$o(nams(s)) q:'$zl(s) d lexins(s) ; initializes lexnams() array ; convert names into maps. more general names coming ahead of more specific names ; i.e. if names are of the form, ABCD, ABCD(1:10), ABCD(3), ABCD(3,7), ABCD(3,5:10), ABC*, AB* AND A* ; process them in the order A*, AB*, ABC*, ABCD, ABCD(1:10), ABCD(3), ABCD(3,5:10), ABCD(3,7) s i=1 f s i=$o(lexnams(i)) q:'$zl(i) d starins(i) ; Insert rest of the names (no * in them). Do unsubscripted names first and then the subscripted names. ; This order is important as otherwise the mappings would end up in the wrong region. s s="" f s s=$o(lexnams(0,s),-1) q:'$zl(s) d pointins(s,lexnams(0,s)) ; insert unsubscripted names s i=0 f s i=$o(lexnams(i),-1) q:'$zl(i) d . s s="" f s s=$o(lexnams(i,s),-1) q:'$zl(s) d pointins(s,lexnams(i,s)) ; insert subscripted names ; remove map entries that are contiguous and are mapped to same region. keeps map() array size minimal s s1=$o(map(""),-1) f s s2=s1,s1=$o(map(s1),-1) q:s2="$" i map(s1)=map(s2) k map(s2) q ;---------------------------------------------------------------------------------------------------------------------------------- SHOWNAM ; to show names, we transform the NAMES to MAP and then reverse transform the MAP back to NAMES. ; this ensure we optimize the names (remove redundant name specifications) before showing it. n lexnams,map d NAM2MAP s s1="",$zpi(s1,$zch(255),SIZEOF("mident"))="" f s s2=s1,s1=$o(map(s1),-1),map(s2)=map(s1) q:s1="$" k map(s1) k map("#") i '$$MAP2NAM(.map) zm gdeerr("GDECHECK")\2*2 q ;---------------------------------------------------------------------------------------------------------------------------------- MAP2NAM(list1) n maxMap,currMap,currMapLen,prevMap,prevMapLen,currReg,prevReg,nextReg,prevPrefix,currPrefix,gblname,coll,key,key2 n namSpc,namSpc2,currNam,currNamLen,i,startMap,midentSize,prevPrevMap,list,mapsublvl,mapisplusplus,nextMap m list=list1 s currMap=$o(list("")) q:currMap'="#)" 0 s currMap=$o(list(currMap)) i currMap="%" s list("$")=list("%") ; if "$" is missing, assign it the same value as "%" e q:currMap'="$" 0 s midentSize=$s(SIZEOF("mident")=8:8,1:SIZEOF("mident")-1) s $zpi(maxMap,$zch(255),midentSize+1)="",currMap=$o(list(""),-1) ; In pre-V61 maps, we would have a 32-byte $zch(255) as the final map. But V61 onwards, we have only 31-bytes of $zch(255) ; If ever we read a pre-V61 gld file, treat the 32-byte as if it was a 31-byte $zch(255) i currMap'=maxMap i maxMap_$zch(255)=currMap s list(maxMap)=list(currMap) k list(currMap) s currMap=maxMap i currMap'=maxMap q 0 k nams ; ---------------------------------------------------------------------------------------------------- ; Stage 1 : Replace all subscripted global entries in the map with unsubscripted global name entries. ; This simplifies processing in later steps. This is in turn divided into sub-stages. ; ------------------------------------------------------------------------------------------------ ; Substage (a) : Do some preparatory processing for subscripted map entries. ; 1) Calculate the level (# of subscripts) of each map entry. ; 2) Note down the map entry based on its level in the "mapsublvl" array. ; 3) Note down the ending offset of the map minus the last subscript. ; 4) Note down if the map entry is of the ++ form (in the "mapisplusplus" array). ; 5) Add a map entry corresponding to the unsubscripted global name if not already present. ; All of these are used in later substages. n mapsublvl s mapsublvl(0)="" ; to be used as a loop terminator later s currMap="" f s currMap=$o(list(currMap)) q:currMap="" d . s mapsublvl=$zl(currMap,ZERO)-1 . i mapsublvl=0 q . n offset,startoff . s offset=0 f i=1:1:mapsublvl s offset=$zfind(currMap,ZERO,offset) i i=1 s startoff=offset . s mapsublvl(mapsublvl,currMap)="" ; Note down map entries based on their subscript level . s list(currMap,"SUBLEVEL")=mapsublvl ; subscript level of this map entry . s list(currMap,"PRFXOFF")=offset-1 ; offset of the prefix which is the map minus the last subscript . s list(currMap,"GBLNAMEOFF")=startoff-2 ; ending offset of the unsubscripted global name . s currMapLen=$zl(currMap) . s list(currMap,"MAPLEN")=currMapLen ; length of the full map entry . i $$isplusplus(currMap,currMapLen) s mapisplusplus(mapsublvl,currMap)="" . s gblname=$ze(currMap,1,startoff-2) . i '$d(list(gblname)) s list(gblname)=list($o(list(gblname))) ; ------------------------------------------------------------------------------------------------ ; Substage (b) : Process all map entries at the same mapsublvl starting from the highest level. ; Add name entries corresponding to map entries of one level. ; If necessary add map entries of a parent (lower) level as part of removing map entries at one level. ; Also remove any intervals that map to same region and become contiguous due to the above removal. ; Process map entries of the ++ form first. This is easy and once this is done, all we are left ; with are non-++ map entries at a given level and this is much easier to handle. n lvl s lvl="" f s lvl=$o(mapsublvl(lvl),-1) q:lvl=0 d . ; Case (i) : process ++ entries (if any) at this level first . n map,mapLen . s map="" f s map=$o(mapisplusplus(lvl,map),-1) q:map="" d . . s mapLen=list(map,"MAPLEN"),currReg=list(map) . . s gblname=$ze(map,1,list(map,"GBLNAMEOFF")),coll=+$g(gnams(gblname,"COLLATION")) . . s key=$ze(map,1,mapLen-1),namSpc=$zcollate(key_ZERO_ZERO,coll,1) d add2nams(namSpc,currReg,"POINT") . . i '$d(list(key)) m list(key)=list(map) s mapsublvl(lvl,key)="",list(key,"MAPLEN")=list(key,"MAPLEN")-1 . . e d . . . ; if we reach here, this means we are going to remove a ++ map entry and did not add anything instead in "list" . . . ; in this case check if the map entries surrounding the ++ entry map to the same region. if so remove the first one . . . d killPrevMapIfPossible(.list,map) . . d killmap(.list,map) ; remove the ++ map entry and associated structures . ; Case (ii) : process non-++ entries (if any) at this level next . n prfxoff1,prfxoff2,parentMap,parentMapLen,offset . n trailingrange ; this indicates whether we need to add a trailing range as part of removing a map entry . s trailingrange=1,map="" f s map=$o(mapsublvl(lvl,map),-1) q:map="" d . . s currReg=list(map) . . s gblname=$ze(map,1,list(map,"GBLNAMEOFF")),coll=+$g(gnams(gblname,"COLLATION")) . . i trailingrange d . . . ; Case (iii) : Check if a namespace for the trailing range needs to be added . . . ; e.g. If x(1,2) is the current map entry, check the parent level map entry (i.e. x(1)) to see the region it falls in. . . . ; If the name (not map) x(1) maps to the exact same region as the region following the map entry x(1,2), then there is . . . ; no need to add the trailing namespace since a bigger namespace would get added as part of processing the lower-level . . . ; subscripted map entry x(1). If x(1) maps to a different region, then we need to add the range x(1,2:) so it . . . ; overrides the x(1) namespace that will get added while processing the lower-level subscripted map entry x(1). . . . s nextReg=list($o(list(map))),parentMap=$ze(map,1,list(map,"PRFXOFF")-1),prevReg=list($o(list(parentMap))) . . . i nextReg=prevReg q ; x(1) and x(1,2:) map to same region . . . ; add range x(1,2:) . . . s mapLen=list(map,"MAPLEN") . . . s key=$ze(map,1,mapLen),namSpc=$zcollate(key_ZERO_ZERO,coll,1) . . . s nullsub="""""",namSpc=$ze(namSpc,1,$zl(namSpc)-1)_":"_nullsub_")" . . . d add2nams(namSpc,nextReg,"RANGE") . . s prevMap=$o(list(map),-1) . . s prfxoff1=+$g(list(prevMap,"PRFXOFF")),prfxoff2=list(map,"PRFXOFF"),parentMap=$ze(map,1,prfxoff2-1) . . i (prfxoff1=prfxoff2)&($ze(prevMap,1,prfxoff1)=$ze(map,1,prfxoff2)) d q . . . ; Case (iv) : These are TWO adjacent map entries with different last subscripts (every other subscript identical). . . . ; Can be easily replaced by a RANGE type namespace. . . . ; For example x(1,2) is the current map entry and x(1,1) is the previous map entry. . . . ; Then we can add the name space x(1,1:2) and remove the x(1,2) map entry. . . . ; Similar to the "trailingrange" case, we can check if the parent level map entry maps to same region as the current . . . ; range and if so skip this range altogether. . . . i list($o(list(parentMap)))'=currReg d . . . . s prevMapLen=list(prevMap,"MAPLEN"),namSpc=$zcollate(prevMap_ZERO_ZERO,coll,1) . . . . n lastSubs . . . . s mapLen=list(map,"MAPLEN"),lastSubs=$ze(map,prfxoff2,mapLen) . . . . s key=gblname_lastSubs,namSpc2=$zcollate(key_ZERO_ZERO,coll,1) . . . . s namSpc2=$ze(namSpc2,$zl(gblname)+3,$zl(namSpc2)-1) ; extract out the last/ONLY subscript . . . . s namSpc=$ze(namSpc,1,$zl(namSpc)-1)_":"_namSpc2_")" . . . . d add2nams(namSpc,currReg,"RANGE") . . . s trailingrange=0 . . . d killmap(.list,map) . . s nextMap=$o(list(map)) . . i 'trailingrange d i '$d(list(map)) q . . . ; Case (v) : Check if "map" maps to same region as the next map (possible because we skipped the . . . ; "do killPrevMapIfPossible" call in Case (iv)). If so remove "map". . . . i currReg=list(nextMap) d killmap(.list,map) . . . s trailingrange=1 ; now that we know adjacent map entries cannot be replaced by a RANGE type, set this first . . ; A few cases are possible here. . . ; Case (vi) : The current map is say x(1,2) and the NEXT map is x(1)++. In this case, the map entry x(1)++ . . ; can be removed as it is equivalent to x(1,) (where is the maximum subscript possible) . . ; and the range x(1,2:) has already been added. In addition IF map entry x(1,2) maps to the same . . ; region as the map entry AFTER x(1)++, then the map entry x(1,2) can also be removed without adding . . ; a x(1) namespace. And we can move on to the next iteration. IF not, follow through to Case (vii). . . i $d(mapisplusplus(lvl-1,nextMap))&((parentMap_ONE)=nextMap) d i '$d(list(map)) q . . . d killPrevMapIfPossible(.list,nextMap) ; this COULD remove list(map) . . . d killmap(.list,nextMap) . . ; Case (vii) : The current map is say x(1,2). The previous map could be x(1) OR could be x(k) where k < 1 or just x . . ; If previous map is x(1), then we only need to add the NAMESPACE x(1) and delete the MAP x(1,2) . . ; If previous map is x(k) where k<1 or just x, then add MAP x(1), add NAMESPACE x(1) and delete MAP x(1,2) . . ; As to adding NAMESPACE x(1), similar to the "trailingrange" case, one might be tempted to check if parent level map . . ; entry maps to the same region as x(1) and if so skip this NAMESPACE altogether. But the x(1) name addition is . . ; actually an override to a potential range (e.g. x(0:2) name) at the same level. Therefore dont optimize here. . . i '$d(list(parentMap)) d . . . s list(parentMap)=currReg ; add MAP x(1) . . . i lvl=1 q . . . s list(parentMap,"SUBLEVEL")=lvl-1,list(parentMap,"MAPLEN")=$zl(parentMap) . . . s list(parentMap,"GBLNAMEOFF")=list(map,"GBLNAMEOFF") . . . s offset=0 f i=1:1:lvl-1 s offset=$zfind(parentMap,ZERO,offset) . . . s list(parentMap,"PRFXOFF")=offset-1 . . . s mapsublvl(lvl-1,parentMap)="" ; complete addition of MAP x(1) . . e d . . . ; the MAP x(1) already existed and we are about to delete the MAP x(1,2). . . . ; check if the MAP after x(1,2) maps to same region as MAP x(1). If so, MAP x(1) can be removed . . . d killPrevMapIfPossible(.list,map) . . s namSpc=$zcollate(parentMap_ZERO_ZERO,coll,1) . . d add2nams(namSpc,currReg,"POINT") ; add NAMESPACE x(1) . . d killmap(.list,map) ; delete MAP x(1,2) ; ------------------------------------------------------------------------------ ; Stage 2 : Replace all unsubscripted map entries having ")" at the end (e.g. "abc)") with entries not having the ")". ; Also remove any intervals that map to same region and become contiguous due to the above removal. ; This simplifies processing in later steps. s currMap=maxMap,nextReg="" f q:currMap="$" s prevMap=$o(list(currMap),-1) d s currMap=prevMap,nextReg=currReg . s currReg=list(currMap) . i currReg=nextReg k list(currMap) . ; If currMap contains ")" (e.g. "abc)"), it means one of two possibilities. . ; a) prevMap is "abc". In this case, just add name "abc" (assuming it was not already added in previous stages) . ; And delete the "abc)" map entry from later processing. . ; b) prevMap is not "abc". In this case, add name "abc" (assuming it was not already added in previous stages) . ; In addition, replace the "abc)" map entry with "abc" for later processing. . s currMapLen=$zl(currMap) . i $ze(currMap,currMapLen)'=")" q . s namSpc=$ze(currMap,1,currMapLen-1) . i '$d(nams(namSpc)) s nams(namSpc)=currReg . k list(currMap) . i prevMap'=namSpc s list(namSpc)=currReg q . ; if we reach here, this means we removed a ")" map entry and did not add anything instead in "list" . ; so update "currReg" to correspond to next map (the map entry we processed in the previous for loop iteration) . s currReg=nextReg ; ------------------------------------------------------------------------------ ; Stage 3 : Now that unsubscripted namespaces are out of the way, add * namespaces as applicable s currMap=maxMap f q:currMap="$" s prevMap=$o(list(currMap),-1) d s currMap=prevMap . s currReg=list(currMap) . s currMapLen=$zl(currMap) . s prevMapLen=$zl(prevMap) . i (currMap=maxMap)&(prevMap="$") q ; "$" and maxMap are mapped to same region. Skip processing . ; The map entry "currMap" exists and "prevMap" is the previous map entry. . ; Determine the namespaces that potentially lie between the two map entries. . ; And add them to the "nams" array. . f i=1:1:currMapLen i $ze(currMap,i)'=$ze(prevMap,i) q . s matchLen=i-1 ; the length of the maximal common prefix between prevMap and currMap . ; Case (3a) : matchLen == prevMapLen . ; In this case we are guaranteed that prevMapLen < currMapLen, and we need to add only ONE namespace. . ; Example prevMap="ag", currMap="agk". Here, matchLen=2, prevMapLen=2, currMapLen=3. Add only "ag*". . i (matchLen=prevMapLen)&(prevMapLenprevMapLen d . . s currPrefix=prevPrefix_"z",prevPrefix=$ze(prevMap,1,i) . . f q:currPrefix=prevPrefix s namSpc=currPrefix_"*",nams(namSpc)=currReg,currPrefix=$$lexprev(currPrefix) . . ; Do optimization check at each sub-namespaces level. If it succeeds, stop processing any higher level sub-namespaces. . . s startMap=$o(list(currPrefix)) . . i list(startMap)=currReg s i=prevMapLen q ; set i to force quit out of for loop . s namSpc=currPrefix_"*",nams(namSpc)=currReg ; ------------------------------------------------------------------------------ ; Stage 4 : Remove redundant unsubscripted namespaces. As for subscripted namespaces, we are guaranteed they are not ; redundant because of the way Stage 1 processed them. ; Example : If "a*" and "ab*" both map to the same region, the namespace "ab*" can be safely removed. ; But if "a*" maps to AREG, "aa*" maps to BREG, and "aaa*" maps to AREG, we cannot remove "aaa*" because ; "a*" and "aaa*" maps to the same reg. This is because there is a more restrictive mapping "aa*" which ; maps to a different region than "aaa*". That should prevail. ; Similarly if "ab*" and "abc" both map to the same region, the namespace "abc" can be safely removed. ; But if "abc*" also is mapped and to a different region than "abc", then "abc" cannot be removed. ; With SIZEOF("mident")=32, we allow a max of 31-byte global name specifications. ; But with SIZEOF("mident")=8 (for older versions with no longnames support, we allow a max of 8-byte global names. ; Handle this 1-byte discrepancy for the 8-byte case by setting the variable midentSize accordingly. s midentSize=$s(SIZEOF("mident")=8:9,1:SIZEOF("mident")) n starName s nams("*")=list("$") ; Also take this opportunity to update "nams" variable to contain # of elements in "nams" array s currNam="",nams=0 f s currNam=$o(nams(currNam)) q:currNam="" s nams=nams+1 d . i (+$g(nams(currNam,"NSUBS"))) q ; subscripted namespace; dont try to optimize . s currNamLen=$zl(currNam) . s currReg=nams(currNam) . s killed=0,quitLoop=0,starName=($ze(currNam,currNamLen)="*") . ; If processing a non-"*" name that is already at the max gvname length, remove corresponding "*" name if any exists . ; with the same long name. This is because the non-"*" name overrides the "*" name at the max length (no . ; other names are possible other than the max length name). . i ('starName&(currNamLen=(midentSize-1))&($d(nams(currNam_"*")))) k nams(currNam_"*") . f i=$s(starName:(currNamLen-2),1:currNamLen):-1:0 d i (""'=prevReg) s:currReg=prevReg nams=nams-1,killed=1 q . . s currPrefix=$ze(currNam,1,i)_"*" . . s prevReg=$g(nams(currPrefix)) . k:killed nams(currNam) . ; Replace namespaces of the form "...*" where ... is 31 characters long (max-mident) with the "*" removed. . i 'killed,currNamLen' 0 and N is the byte length of the name including the * ; Amongst the non-* names, unsubscripted names go into lexnams(N,...) where N = 0 ; Amongst the non-* names, subscripted names go into lexnams(N,...) where N < 0 and N is the # of subscripts ; Within subscripted names with the same # of subscripts, we want to process ranges ahead of points ; Hence the isrange-(subslvl*2) calculation below. ; This lets us process the names in that order (N>0 first, N=0 next, N=-1,-2,... last) later in the caller function. ; That order is important to ensure correct mappings of names. n l,isrange,subslvl ; if range subscript, "NSUBS" node is actually 1 more than the subscript level (# of commas). s isrange=($g(nams(s,"TYPE"))="RANGE"),subslvl=$g(nams(s,"NSUBS"))-isrange ; check for subscripted name first; check for * in name afterwards; doing it the other way could give false results ; since it is possible for * to be inside a string subscript s l=$s(s["(":isrange-(subslvl*2),s["*":$zl(s),1:0) s lexnams(l,s)=nams(s) q starins:(i) n j,s,reg,next s j="" f s j=$o(lexnams(i,j),-1) q:'$zl(j) d . s s=$ze(j,1,$zl(j)-1) . s reg=lexnams(i,j) . s next=$$lexnext(s) . i $zl(next),'$d(map(next)) s map(next)=map($o(map(next),-1)) . s map(s)=reg q pointins:(s,reg) n keylo,keyhi,nsubs,hasrange,gblname,coll s hasrange=($g(nams(s,"TYPE"))="RANGE") s nsubs=+$g(nams(s,"NSUBS")) i 'hasrange d . ; is a point specification . ; e.g. X -> In this case X and X) are the bounding points . ; e.g. X(1) -> In this case X(1) and X(1)++ are the bounding points . ; where ++ denotes the immediately "next" valid key (byte sequence) . i 'nsubs s keylo=s,keyhi=$$alphnext(s) . e d . . ; For a subscripted gvn, the next key is obtained by appending a 0x01 byte at the very end (before the two null bytes) . . ; Such a key is referred to be in the ++ form. . . ; For a numeric subscript, we are guaranteed, 0x01 is never present (as it means the mantissa was 00 which would . . ; actually have been ignored during subscript representation). . . ; For a string subscript, it is possible to have 0x01 in the middle of the subscript representation. But in that . . ; case we expect the 0x01 to be followed by a 0x01 or 0x02 (indicating 0x00 or 0x01 bytes in the original string . . ; subscript). So if we see a 0x01 at the end of the subscript we need to scan back until we dont see a 0x01 and . . ; determine if the 0x01 run length is odd or even and accordingly decide if the last 0x01 is a lone byte or not. . . ; If it is a lone byte, it means this was added as part of the "next" key determination in pointins. . . ; There are two exceptions to this and that is . . ; a) if 0x01 run length is 1 and the immediately preceding byte is 0x00. . . ; In this case, this corresponds to the null ("") string subscript. . . ; b) if 0x01 run length is 2 and the immediately preceding byte is 0x00. . . ; In this case, this is a ++ form key (++ of the "" null subscript) . . ; This logic will be used in MAP2NAM. . . s gblname=nams(s,"SUBS",0),coll=+$g(gnams(gblname,"COLLATION")) . . s keylo=$$gvn2gdsnotrailingnulls("^"_s,coll),keyhi=keylo_$zch(1) . i $zl(keyhi),'$d(map(keyhi)) s map(keyhi)=map($o(map(keyhi),-1)) e d . ; is a range specification . ; e.g. X(1:"abc") -> In this case X(1) and X("abc") are the bounding points . ; e.g. X("":"") -> In this case X("") and X) are the bounding points . ; e.g. X(2,"":"") -> In this case X(2,"") and X(2)++ are the bounding points . ; e.g. X("abc","z":"") -> In this case X("abc","z") and X("abc")++ are the bounding points . ; where ++ denotes the immediately "next" valid key (byte sequence) . n range,rangelo,rangehi,rlo,rhi,nullsub . s rlo=nams(s,"SUBS",nsubs-1),rhi=nams(s,"SUBS",nsubs) . s nsubs=nams(s,"NSUBS"),range=nams(s,"GVNPREFIX") . s gblname=nams(s,"SUBS",0),coll=+$g(gnams(gblname,"COLLATION")) . s rangelo="^"_range_rlo_")",keylo=$$gvn2gdsnotrailingnulls(rangelo,coll) . s nullsub="""""" . i (rhi'=nullsub) d . . s rangehi="^"_range_rhi_")",keyhi=$$gvn2gdsnotrailingnulls(rangehi,coll) . e d . . ; if "" is right side of range, this is equivalent to the "next" parent level subscript. . . ; use that to construct the subscript level representation as that is easier than trying . . ; to represent the max-possible-key at this level (using a sequence of 1019 0xFFs or so) . . n gvn . . i nsubs=2 s keyhi=$$alphnext(gblname) ; special case in case range is at FIRST subscript level . . e s gvn="^"_$ze(range,1,$zl(range)-1)_")",keyhi=$$gvn2gdsnotrailingnulls(gvn,coll),keyhi=keyhi_$zch(1) . i $zl(keyhi),'$d(map(keyhi)) d . . n prevMap . . s prevMap=$o(map(keyhi),-1) . . s map(keyhi)=map(prevMap) . . ; check for sub-range and if so adjust mapping region of previous map entry accordingly . . i (prevMap]keylo) s map(prevMap)=reg s map(keylo)=reg q gvn2gds(gvn,coll) ; return subscript (gds) representation for input "gvn". ; checks for GVSUBOFLOW error and if so issues GDE-specific GVSUBOFLOW error n key n savetrap s savetrap=$etrap n $etrap s $etrap="goto gvsuboflowerr" s key=$zcollate(gvn,coll) q key gvsuboflowerr n len i $zstatus'["GVSUBOFLOW" q ; dont know how a non-GVSUBOFLOW error occured. let parent frame handle it like any other error s $ecode="",len=$zl(gvn) s $etrap=savetrap ; Do not attempt to print the full "gvn" value as it might exceed the buffer allocated by zmessage. ; So print first 100 bytes and last 100 bytes with a "..." in between zm gdeerr("NAMGVSUBOFLOW"):$ze(gvn,2,100):$ze(gvn,len-100,len):coll ; 2 (instead of 1) to skip ^ at start of gvn q gvn2gdsnotrailingnulls:(gvn,coll) ; return subscript (gds) representation for input "gvn". Removes trailing double null-byte n key s key=$$gvn2gds(gvn,coll) q $ze(key,1,$zl(key)-2) alphnext:(s) q $s($zl(s)=MAXNAMLN:$$lexnext(s),1:s_")") ; lexnext:(s) n len,last,succ s len=$zl(s),last=$ze(s,len) i last="z" f s len=len-1,last=$ze(s,len) q:last'="z"!'len i 'len q "" s s=$ze(s,1,len-1),succ=$zch($za(last)+1) i succ?1AN q s_succ i "A"]succ q s_"A" i "a"]succ q s_"a" q "" lexprev:(s) n len,last,prior s len=$zl(s),last=$ze(s,len) s s=$ze(s,1,len-1),prior=$zch($za(last)-1) i prior?1AN q s_prior i prior]"Z" q s_"Z" i prior]"9" q:len=1 "%" q s_"9" q "" setinbetween(keylo1,keylo2,keyhi1,keyhi2,keylo1inbetween,keyhi1inbetween) ; Allow sub-ranges (i.e. a smaller range completely inside a bigger range) but not allow overlapping ranges. ; An easy check for this is IF ; (a) keylo1 and keyhi1 are both in between keylo2 and keyhi2 OR ; (b) keylo1 and keyhi1 are both NOT in between keylo2 and keyhi2 ; In this case we allow. If not, we dont allow this range. s keylo1inbetween=((keylo1=keylo2)!(keylo1]keylo2))&(keyhi2]keylo1) s keyhi1inbetween=(keyhi1]keylo2)&((keyhi1=keyhi2)!(keyhi2]keyhi1)) ; adjust keylo1inbetween and keyhi1inbetween to take into account a few edge cases i (keylo1=keylo2) s keylo1inbetween=$s((keyhi1]keyhi2):0,1:1),keyhi1inbetween=keylo1inbetween i (keyhi1=keyhi2) s keylo1inbetween=$s((keylo1]keylo2):1,1:0),keyhi1inbetween=keylo1inbetween q namcoalesce n quitLoop,coll,nam1,nam2,namnew,keylo1,keylo2,keyhi1,keyhi2,keylo1inbetween,keyhi1inbetween,prefix,nsubs2,nsubs1 n tmpnam1,tmpnam2,range ; ASSERT : i '$d(namrangeoverlap) zsh "*" h s nam1="" f s nam1=$o(namrangeoverlap(nam1)) q:nam1="" d i quitLoop s nam1="" . s nam2=nam1 . s quitLoop=0 . s coll=+$g(gnams(nams(nam1,"SUBS",0),"COLLATION")) . f s nam2=$o(namrangeoverlap(nam2)) q:nam2="" d q:quitLoop . . ; if subscripts dont match before the range, there is no chance of a range overlap issue . . s range=nams(nam1,"GVNPREFIX") i range'=nams(nam2,"GVNPREFIX") q . . ; Below code is similar to "namerangeoverlapcheck2^GDEPARSE". See there for comments . . m tmpnam1=nams(nam1) s tmpnam1=nams(nam1) . . m tmpnam2=nams(nam2) s tmpnam2=nams(nam2) . . d getrangelohikey^GDEPARSE(.tmpnam1,.keylo1,.keyhi1,coll,range) . . d getrangelohikey^GDEPARSE(.tmpnam2,.keylo2,.keyhi2,coll,range) . . d setinbetween(keylo1,keylo2,keyhi1,keyhi2,.keylo1inbetween,.keyhi1inbetween) . . ; the above sets keylo1inbetween and keyhi1inbetween . . ; first check overlap case . . s prefix=nams(nam1,"GVNPREFIX"),nsubs1=nams(nam1,"NSUBS"),nsubs2=nams(nam2,"NSUBS") . . i (keylo1inbetween'=keyhi1inbetween) d . . . ; we are guaranteed both nam1 and nam2 map to same region or else NAMRANGEOVERLAP error would have been issued before . . . ; ASSERT : i nams(nam1)'=nams(nam2) zsh "*" h . . . s quitLoop=1 . . . ; this is a case of overlapping ranges where both ranges map to same region. . . . ; create a new super-range that encompasses both overlapping ranges . . . i keylo1inbetween d . . . . ; merge the ranges [keylo1,keyhi1] and [keylo2,keyhi2] into one super-range [keylo2,keyhi1] . . . . s namnew=prefix_nams(nam2,"SUBS",nsubs2-1)_":"_nams(nam1,"SUBS",nsubs1)_")" . . . . d add2nams("^"_namnew,nams(nam1),"RANGE") . . . . s namrangeoverlap(namnew)="" . . . e d . . . . ; merge the ranges [keylo1,keyhi1] and [keylo2,keyhi2] into one super-range [keylo1,keyhi2] . . . . s namnew=prefix_nams(nam1,"SUBS",nsubs1-1)_":"_nams(nam2,"SUBS",nsubs2)_")" . . . . d add2nams("^"_namnew,nams(nam1),"RANGE") . . . . s namrangeoverlap(namnew)="" . . . k nams(nam1),namrangeoverlap(nam1) . . . k nams(nam2),namrangeoverlap(nam2) . . ; next check if [keylo1,keyhi1] is completely inside [keylo2,keyhi2] . . e i (keylo1inbetween) d . . . s quitLoop=1 . . . ; keylo1 and keyhi1 are both in between keylo2 and keyhi2 . . . ; i.e. [keylo1,keyhi1] lies completely inside [keylo2,keyhi2] . . . i nams(nam1)=nams(nam2) d . . . . ; a sub-range lies completely inside a super-range and both map to same region . . . . ; safely delete the sub-range . . . . k nams(nam1),namrangeoverlap(nam1) . . . e d . . . . ; a sub-range lies completely inside a super-range and both map to different regions . . . . ; split range [keylo2,keyhi2] into two sub-ranges [keylo2,keylo1] and [keyhi1,keyhi2] . . . . ; create range [keylo2,keylo1] . . . . i keylo2'=keylo1 d . . . . . s namnew=prefix_nams(nam2,"SUBS",nsubs2-1)_":"_nams(nam1,"SUBS",nsubs1-1)_")" . . . . . d add2nams("^"_namnew,nams(nam2),"RANGE") . . . . . s namrangeoverlap(namnew)="" . . . . ; create range [keyhi1,keyhi2] . . . . i keyhi1'=keyhi2 d . . . . . s namnew=prefix_nams(nam1,"SUBS",nsubs1)_":"_nams(nam2,"SUBS",nsubs2)_")" . . . . . d add2nams("^"_namnew,nams(nam2),"RANGE") . . . . . s namrangeoverlap(namnew)="" . . . . ; kill range [keylo2,keyhi2] . . . . k nams(nam2),namrangeoverlap(nam2) . . ; next check if [keylo2,keyhi2] is completely inside [keylo1,keyhi1] . . e i ((keylo2=keylo1)!(keylo2]keylo1))&(keyhi1]keylo2) d . . . i nams(nam1)=nams(nam2) d . . . . ; a sub-range lies completely inside a super-range and both map to same region . . . . ; safely delete the sub-range . . . . k nams(nam2),namrangeoverlap(nam2) . . . . ; since only nam2 is killed, and nam1 is untouched, continue in the nam2 loop (i.e. dont set quitLoop to 1) . . . e d . . . . ; a sub-range lies completely inside a super-range and both map to different regions . . . . ; split range [keylo1,keyhi1] into two sub-ranges [keylo1,keylo2] and [keyhi2,keyhi1] . . . . ; create range [keylo1,keylo2] . . . . i keylo1'=keylo2 d . . . . . s namnew=prefix_nams(nam1,"SUBS",nsubs1-1)_":"_nams(nam2,"SUBS",nsubs2-1)_")" . . . . . d add2nams("^"_namnew,nams(nam1),"RANGE") . . . . . s namrangeoverlap(namnew)="" . . . . ; create range [keyhi2,keyhi1] . . . . i keyhi2'=keyhi1 d . . . . . s namnew=prefix_nams(nam2,"SUBS",nsubs2)_":"_nams(nam1,"SUBS",nsubs1)_")" . . . . . d add2nams("^"_namnew,nams(nam1),"RANGE") . . . . . s namrangeoverlap(namnew)="" . . . . ; kill range [keylo1,keyhi1] . . . . k nams(nam1),namrangeoverlap(nam1) . . . . s quitLoop=1 . . ; next check if [keylo1,keyhi1] is immediately followed by [keylo2,keyhi2] and map to same region . . ; in this case merge the two into one super-range [keylo1,keyhi2] . . e i (keyhi1=keylo2)&(nams(nam1)=nams(nam2)) d . . . s quitLoop=1 . . . ; create range [keylo1,keyhi2] . . . s namnew=prefix_nams(nam1,"SUBS",nsubs1-1)_":"_nams(nam2,"SUBS",nsubs2)_")" . . . d add2nams("^"_namnew,nams(nam1),"RANGE") . . . s namrangeoverlap(namnew)="" . . . ; kill range [keylo1,keyhi1] . . . k nams(nam1),namrangeoverlap(nam1) . . . ; kill range [keylo2,keyhi2] . . . k nams(nam2),namrangeoverlap(nam2) . . ; next check if [keylo2,keyhi2] is immediately followed by [keylo1,keyhi1] and map to same region . . ; in this case merge the two into one super-range [keylo2,keyhi1] . . e i (keyhi2=keylo1)&(nams(nam1)=nams(nam2)) d . . . s quitLoop=1 . . . ; create range [keylo2,keyhi1] . . . s namnew=prefix_nams(nam2,"SUBS",nsubs2-1)_":"_nams(nam1,"SUBS",nsubs1)_")" . . . d add2nams("^"_namnew,nams(nam2),"RANGE") . . . s namrangeoverlap(namnew)="" . . . ; kill range [keylo2,keyhi2] . . . k nams(nam2),namrangeoverlap(nam2) . . . ; kill range [keylo1,keyhi1] . . . k nams(nam1),namrangeoverlap(nam1) k namrangeoverlap ; now that all sub-ranges and/or overlapping range coalesces have been automatically taken care of q isplusplus(currMap,currMapLen) n c,i ; currMap should be a map entry corresponding to a subscripted name (i.e. contain ZERO in it) ; Now that we know this is a subscripted gvn, check if the last subscript is of the ++ form. ; At a high level, this is the case if the last byte is 0x01. But there is more to it which can be ; found in a comment below (search for MAP2NAM in a comment above that also mentions the ++ form). f i=currMapLen:-1:1 s c=$ze(currMap,i) q:c'=ONE s i=(currMapLen-i) ; cases for the $s below are "", ""++, 0x01 0x01 byte sequence in a string subscript, unpaired 0x01 at end of subscript ; if c=ZERO, then i is guaranteed to be either 1 or 2, nothing more. (1 implies "", 2 implies ""++) q $s((c=ZERO):$s((1=i):0,1:1),(0=(i#2)):0,1:1) q fis-gtm-V7.0-005/sr_port/gdemsgin.m0000755000032200000250000000672514342376331016033 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2001-2022 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; gdemsgin: ;message initialization - equate global symbols to numeric values GDEMSGIN s gdeerr("BLKSIZ512")=150503435 s gdeerr("EXECOM")=150503443 s gdeerr("FILENOTFND")=150503451 s gdeerr("GDCREATE")=150503459 s gdeerr("GDECHECK")=150503467 s gdeerr("GDUNKNFMT")=150503475 s gdeerr("GDUPDATE")=150503483 s gdeerr("GDUSEDEFS")=150503491 s gdeerr("ILLCHAR")=150503498 s gdeerr("INPINTEG")=150503506 s gdeerr("KEYTOOBIG")=150503515 s gdeerr("KEYSIZIS")=150503523 s gdeerr("KEYWRDAMB")=150503530 s gdeerr("KEYWRDBAD")=150503538 s gdeerr("LOADGD")=150503547 s gdeerr("LOGOFF")=150503555 s gdeerr("LOGON")=150503563 s gdeerr("LVSTARALON")=150503570 s gdeerr("MAPBAD")=150503579 s gdeerr("MAPDUP")=150503587 s gdeerr("NAMENDBAD")=150503594 s gdeerr("NOACTION")=150503603 s gdeerr("RPAREN")=150503610 s gdeerr("NOEXIT")=150503619 s gdeerr("NOLOG")=150503627 s gdeerr("NOVALUE")=150503634 s gdeerr("NONEGATE")=150503642 s gdeerr("OBJDUP")=150503650 s gdeerr("OBJNOTADD")=150503658 s gdeerr("OBJNOTCHG")=150503666 s gdeerr("OBJNOTFND")=150503674 s gdeerr("OBJREQD")=150503682 s gdeerr("PREFIXBAD")=150503690 s gdeerr("QUALBAD")=150503698 s gdeerr("QUALDUP")=150503706 s gdeerr("QUALREQD")=150503714 s gdeerr("RECTOOBIG")=150503723 s gdeerr("RECSIZIS")=150503731 s gdeerr("REGIS")=150503739 s gdeerr("SEGIS")=150503747 s gdeerr("VALTOOBIG")=150503755 s gdeerr("VALTOOLONG")=150503762 s gdeerr("VALTOOSMALL")=150503771 s gdeerr("VALUEBAD")=150503778 s gdeerr("VALUEREQD")=150503786 s gdeerr("VERIFY")=150503795 s gdeerr("BUFSIZIS")=150503803 s gdeerr("BUFTOOSMALL")=150503811 s gdeerr("MMNOBEFORIMG")=150503819 s gdeerr("NOJNL")=150503827 s gdeerr("GDREADERR")=150503835 s gdeerr("GDNOTSET")=150503843 s gdeerr("INVGBLDIR")=150503851 s gdeerr("WRITEERROR")=150503859 s gdeerr("NONASCII")=150503866 s gdeerr("GDECRYPTNOMM")=150503874 s gdeerr("GDEASYNCIONOMM")=150504106 s gdeerr("JNLALLOCGROW")=150503883 s gdeerr("KEYFORBLK")=150503891 s gdeerr("STRMISSQUOTE")=150503898 s gdeerr("GBLNAMEIS")=150503907 s gdeerr("NAMSUBSEMPTY")=150503914 s gdeerr("NAMSUBSBAD")=150503922 s gdeerr("NAMNUMSUBSOFLOW")=150503930 s gdeerr("NAMNUMSUBNOTEXACT")=150503938 s gdeerr("MISSINGDELIM")=150503946 s gdeerr("NAMRANGELASTSUB")=150503954 s gdeerr("NAMSTARSUBSMIX")=150503962 s gdeerr("NAMLPARENNOTBEG")=150503970 s gdeerr("NAMRPARENNOTEND")=150503978 s gdeerr("NAMONECOLON")=150503986 s gdeerr("NAMRPARENMISSING")=150503994 s gdeerr("NAMGVSUBSMAX")=150504002 s gdeerr("NAMNOTSTRSUBS")=150504010 s gdeerr("NAMSTRSUBSFUN")=150504018 s gdeerr("NAMSTRSUBSLPAREN")=150504026 s gdeerr("NAMSTRSUBSCHINT")=150504034 s gdeerr("NAMSTRSUBSCHARG")=150504042 s gdeerr("GBLNAMCOLLUNDEF")=150504050 s gdeerr("NAMRANGEORDER")=150504058 s gdeerr("NAMRANGEOVERLAP")=150504066 s gdeerr("NAMGVSUBOFLOW")=150504074 s gdeerr("GBLNAMCOLLRANGE")=150504082 s gdeerr("STDNULLCOLLREQ")=150504091 s gdeerr("GBLNAMCOLLVER")=150504098 s gdeerr("NOPERCENTY")=150504114 s gdeerr("GDELOGFAIL")=150504122 q fis-gtm-V7.0-005/sr_port/gdeparse.m0000644000032200000250000005364214342376331016025 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2010-2019 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; gdeparse: ;command parser GDEPARSE n verb,NAME,GBLNAME,REGION,SEGMENT,gqual,lquals d matchtok("TKIDENT","Verb") s verb=token d checkkw(.verb,"verb","syntab") d @verb q qual(qual,ent,s) i ntoktype="TKEOL" zm gdeerr("QUALREQD"):ent d matchtok(sep,ent),matchtok("TKIDENT",ent) s qual=token d checkkw(.qual,ent,s) s t=@s@(qual) i negated,t'["NEGATABLE" zm gdeerr("NONEGATE"):qual i t["REQUIRED",ntoktype'="TKEQUAL",'negated zm gdeerr("VALUEREQD"):qual i "NEGATABLE"[t!negated,ntoktype="TKEQUAL" zm gdeerr("NOVALUE"):$s(negated:"NO"_qual,1:qual) i t["NEGATABLE" s qual("value")='negated i ntoktype="TKEQUAL",t'["LIST" s qual("value")=$$getvalue(s,qual) q i ntoktype="TKEQUAL" d list(qual) q getvalue:(s,qual) d matchtok("TKEQUAL","Value") i ntoktype="TKEOL" zm gdeerr("VALUEREQD"):qual d @@s@(qual,"TYPE") q value ; list:(lhead) n listparsing s listparsing=TRUE s tmp=lqual,v=lqual("value") i $ze(comline,cp)="(" d GETTOK^GDESCAN n sep s sep=ntoktype d getlitm i sep="TKLPAREN" s sep=ntoktype f q:ntoktype="TKRPAREN" zm:"TKRPAREN|TKCOMMA"'[ntoktype gdeerr("RPAREN") d getlitm i ntoktype="TKRPAREN" d GETTOK^GDESCAN s lqual=tmp,lqual("value")=v q TNUMBER d GETTOK^GDESCAN i $l(token)'=$zl(token) zm gdeerr("NONASCII"):token:"number" ; error if the token has non-ascii numbers i token'?1.N zm gdeerr("VALUEBAD"):token:"number" s value=token q TFSPEC n filespec i ntoktype="TKSTRLIT" s filespec=ntoken e d TFSPECP d GETTOK^GDESCAN i $zl(filespec)>(SIZEOF("file_spec")-1) zm gdeerr("VALUEBAD"):filespec:"file specification" i '$zl($zparse(filespec,"","","","SYNTAX_ONLY")) zm gdeerr("VALUEBAD"):filespec:"file specification" s @("value="_$s($zl(filexfm):filexfm,1:filespec)) ; do system specific file name translation q TFSPECP ; scan filespec token by token n c,cp1,i ; unix filenames must be quoted to avoid / conflicts with qualifiers ; first undo any whitespace that was skipped f i=1:1 s c=$ze(comline,cp-i) q:(c'=" ")&(c'=TAB) s cp1=cp-(i-1)-$zl(ntoken) s i=$zl(comline)-cp1+1 ; in Unix any byte is considered acceptable in the file name at the end of the line s filespec=$ze(comline,cp1,cp1+i-1),cp=cp1+i q TACCMETH do GETTOK^GDESCAN zmessage:(("TKIDENT"'=toktype)&("TKSTRLIT"'=toktype)) gdeerr("VALUEBAD"):token:qual set value=$zconvert(token,"U") zmessage:'$data(typevalue("STR2NUM","TACCMETH",value)) gdeerr("VALUEBAD"):token:qual quit TNULLSUB do GETTOK^GDESCAN zmessage:(("TKIDENT"'=toktype)&("TKSTRLIT"'=toktype)) gdeerr("VALUEBAD"):token:qual set value=$zconvert(token,"U") zmessage:'$data(typevalue("STR2NUM","TNULLSUB",value)) gdeerr("VALUEBAD"):token:qual set value=typevalue("STR2NUM","TNULLSUB",value) quit TREGION n REGION d REGION s value=REGION q TSEGMENT n SEGMENT d SEGMENT s value=SEGMENT q GBLNAME k GBLNAME n c i ntoktype="TKEOL" zm gdeerr("OBJREQD"):"gblname" d GETTOK^GDESCAN s GBLNAME=token i GBLNAME'?1(1"%",1A).AN zm gdeerr("VALUEBAD"):GBLNAME:"gblname" i $l(GBLNAME)'=$zl(GBLNAME) zm gdeerr("NONASCII"):GBLNAME:"gblname" ; error if the name is non-ascii i $zl(GBLNAME)>PARNAMLN zm gdeerr("VALTOOLONG"):GBLNAME:PARNAMLN:"gblname" q INSTANCE i ntoktype="TKEOL" zm gdeerr("OBJREQD"):"instance" q NAME k NAME n c,len,j,k,tokname,starti,endi,subcnt,nsubs,gblname,type,rangeprefix,nullsub,lsub i ntoktype="TKEOL" zm gdeerr("OBJREQD"):"name" m nsubs=NAMEsubs ; before GETTOK overwrites it s type=NAMEtype ; before GETTOK overwrites it d GETTOK^GDESCAN s tokname=token i "%Y"=$ze(tokname,1,2) zm gdeerr("NOPERCENTY") i (MAXGVSUBS<(nsubs-1-$select(type="RANGE":1,1:0))) zm gdeerr("NAMGVSUBSMAX"):tokname:MAXGVSUBS ; parse subscripted tokname (potentially with ranges) to ensure individual pieces are well-formatted ; One would be tempted to use $NAME to do automatic parsing of subscripts for well-formedness, but there are issues ; with it. $NAME does not issue error in various cases (unsubscripted global name longer than 31 characters, ; numeric subscript mantissa more than 18 digits etc.). And since we want these cases to error out as well, we parse ; the subscript explicitly below. s len=$zl(tokname) s j=$g(nsubs(1)) s gblname=$ze(tokname,1,j-2) s NAME=gblname i $l(NAME)'=$zl(NAME) zm gdeerr("NONASCII"):NAME:"name" ; error if the name is non-ascii s NAME("SUBS",0)=gblname i $ze(gblname,j-2)="*" s type="STAR" s NAME("TYPE")=type i ("*"'=gblname)&(gblname'?1(1"%",1A).AN.1"*") zm gdeerr("VALUEBAD"):gblname:"name" i (j-2)>PARNAMLN zm gdeerr("VALTOOLONG"):gblname:PARNAMLN:"name" i j=(len+2) s NAME("NSUBS")=0 q ; no subscripts to process. done. ; have subscripts to process i type="STAR" zm gdeerr("NAMSTARSUBSMIX"):tokname i $ze(tokname,len)'=")" zm gdeerr("NAMENDBAD"):tokname s NAME=NAME_"(" s nullsub="""""" f subcnt=1:1:nsubs-1 d . s k=nsubs(subcnt+1) . s sub=$ze(tokname,j,k-2) . i (sub="") d . . ; allow empty subscripts only on left or right side of range . . i (type="RANGE") d . . . i (subcnt=(nsubs-2)) s sub=nullsub q ; if left side of range is empty, replace with null subscript . . . i (subcnt=(nsubs-1)) s sub=nullsub q ; if right side of range is empty, replace with null subscript . i (sub="") zm gdeerr("NAMSUBSEMPTY"):subcnt ; null subscript . s c=$ze(sub,1) . i (c="""")!(c="$") set sub=$$strsub(sub,subcnt) ; string subscript . e set sub=$$numsub(sub,subcnt) ; numeric subscript . i (type="RANGE")&(subcnt=(nsubs-2)) s rangeprefix=NAME,lsub=sub . s NAME("SUBS",subcnt)=sub,NAME=NAME_sub,j=k . s NAME=NAME_$s(subcnt=(nsubs-1):")",(type="RANGE")&(subcnt=(nsubs-2)):":",1:",") s NAME("NSUBS")=nsubs-1,NAME("NAME")=NAME i type="RANGE" d . ; check if both subscripts are identical; if so morph the RANGE subscript into a POINT type. . ; the only exception is if the range is of the form :. In this case, it is actually a range . ; meaning every possible value in that subscript. . i ((NAME("SUBS",nsubs-1)=lsub)&(lsub'=nullsub)) d q . . s NAME("NAME")=rangeprefix_lsub_")",NAME("NSUBS")=nsubs-2,NAME("TYPE")="POINT",NAME=NAME("NAME") . . k NAME("SUBS",nsubs-1) . s NAME("GVNPREFIX")=rangeprefix ; subscripted gvn minus the last subscript . ; note the below (which does out-of-order check) also does the max-key-size checks for both sides of the range . d namerangeoutofordercheck(.NAME,+$g(gnams(gblname,"COLLATION"))) e d . ; ensure input NAME is within maximum key-size given current gblname value of collation . n coll,key . s coll=+$g(gblname,"COLLATION") . s key=$$gvn2gds^GDEMAP("^"_NAME,coll) . d keylencheck(NAME,key,coll) q namerangeoutofordercheck:(nam,coll) n rlo,rhi,nsubs,nullsub,rangelo,rangehi,keylo,keyhi,range s nullsub="""""" s nsubs=nam("NSUBS") s rlo=nam("SUBS",nsubs-1),rhi=nam("SUBS",nsubs) ; if rhi==nullsub then the range is guaranteed to be in order by definition so skip check in that case i (rhi'=nullsub) d . s range=nam("GVNPREFIX") . s rangelo="^"_range_rlo_")",rangehi="^"_range_rhi_")" . s keylo=$$gvn2gds^GDEMAP(rangelo,coll),keyhi=$$gvn2gds^GDEMAP(rangehi,coll) . d keylencheck(rangelo,keylo,coll) . d keylencheck(rangehi,keyhi,coll) . i keylo]keyhi zm gdeerr("NAMRANGEORDER"):$$namedisp^GDESHOW(nam("NAME"),0):coll q keylencheck(gvn,key,coll) n text s text="subscripted name in the database using collation #"_coll i $zl(key)>maxreg("KEY_SIZE") zm gdeerr("VALTOOLONG"):gvn:maxreg("KEY_SIZE"):text q gblnameeditchecks(gblname,newcoll) ; Check if setting collation of "gblname" to "newcoll" ; (a) creates out-of-order ranges in existing names ; (b) creates range overlaps in existing names ; (c) creates subscript representations that exceed the key-size design-maximum (1019) ; (d) check if "newcoll" is a valid collation sequence ; If "gblname" is "*", check all EXISTING name-specifications across all EXISTING global-names. ; In this case, "newcoll" is ignored since there is no particular gblname we are interested in. n nam,tmpnam,key ; Test (d) d chkcoll(newcoll,gblname) ; Test (a) s nam="" f s nam=$o(nams(nam)) q:'$zl(nam) d . ; for some unsubscripted name specifications (e.g. "*", "#"), nams(nam) might be an unsubscripted node. skip in that case . i '$d(nams(nam,"TYPE")) q . i (nams(nam,"NSUBS")=0) q ; if unsubscripted name, then no more checks needed . i ("*"'=gblname)&(nams(nam,"SUBS",0)'=gblname) q . i gblname="*" s newcoll=+$g(gnams(nams(nam,"SUBS",0),"COLLATION")) . i nams(nam,"TYPE")'="RANGE" d q . . ; No need of test (a) since this name is not a range. But do test (c). . . ; This takes care of Test (c) for subscripted non-range name specifications. . . s key=$$gvn2gds^GDEMAP("^"_nam,newcoll) . . d keylencheck(nam,key,newcoll) . k tmpnam . m tmpnam=nams(nam) . s tmpnam=nams(nam) . ; The below also takes care of Test (c) for subscripted range name specifications . d namerangeoutofordercheck(.tmpnam,newcoll) ; Test (b) i gblname="*" d . d namerangeoverlapcheck("") e d namerangeoverlapcheck("","","",gblname,newcoll) q getrangelohikey(nam,keylo,keyhi,coll,range) n nsubs,rlo,rhi,nullsub,rlen s nullsub="""""" s nsubs=nam("NSUBS") s rlo=nam("SUBS",nsubs-1),rhi=nam("SUBS",nsubs) s rlo="^"_range_rlo_")",keylo=$$gvn2gds^GDEMAP(rlo,coll) i (rhi'=nullsub) s rhi="^"_range_rhi_")",keyhi=$$gvn2gds^GDEMAP(rhi,coll) e d . ; rhi==nullsub implies max possible subscript at that level which means the lexically next subscript at one higher level . s rlen=$zl(range) . i $ze(range,rlen)="(" s keyhi=$ze(range,1,rlen-1)_ONE_ZERO_ZERO q . s rhi="^"_$ze(range,1,rlen-1)_")",keyhi=$$gvn2gds^GDEMAP(rhi,coll) . s rlen=$zl(keyhi),keyhi=$ze(keyhi,1,rlen-2)_ONE_ZERO_ZERO q q namerangeoverlapcheck2:(nam1,reg1,nam2,coll) n keylo1,keyhi1,keylo2,keyhi2,range,reg2,maxkey,keylo1inbetween,keyhi1inbetween,overlap s reg2=nam2 s range=nam1("GVNPREFIX") i range'=nam2("GVNPREFIX") q ; if subscripts don't match before the range, there is no chance of a range overlap issue i '$data(coll) s coll=+$g(gnams(nam1("SUBS",0),"COLLATION")) d getrangelohikey(.nam1,.keylo1,.keyhi1,coll,range) d getrangelohikey(.nam2,.keylo2,.keyhi2,coll,range) d setinbetween^GDEMAP(keylo1,keylo2,keyhi1,keyhi2,.keylo1inbetween,.keyhi1inbetween) ; the above sets keylo1inbetween and keyhi1inbetween s overlap=0 i (keylo1inbetween'=keyhi1inbetween) d . ; if regions match, no range overlap error needs to be issued but coalesce is needed for sure . s overlap=1 . i reg1=reg2 q ; if regions match, no need for range overlap error, but need coalesce . zm gdeerr("NAMRANGEOVERLAP"):$$namedisp^GDESHOW(nam1("NAME"),0):$$namedisp^GDESHOW(nam2("NAME"),0):coll ; else check for a few sub-range cases e i (keylo1inbetween) s overlap=1 ; keylo1 and keyhi1 are both in between keylo2 and keyhi2 ; else if keylo2 is in between keylo1 and keyhi1, this means another sub-range case e i ((keylo2=keylo1)!(keylo2]keylo1))&(keyhi1]keylo2) s overlap=1 ; else check if [keylo1,keyhi1] is immediately followed by [keylo2,keyhi2] and map to same region e i (keyhi1=keylo2)&(reg1=reg2) s overlap=1 ; else check if [keylo2,keyhi2] is immediately followed by [keylo1,keyhi1] and map to same region e i (keyhi2=keylo1)&(reg1=reg2) s overlap=1 ; namrangeoverlap array indicates there are ranges with overlaps mapping to same region i (overlap=1) s namrangeoverlap(nam1("NAME"))="",namrangeoverlap(nam2("NAME"))="" q namerangeoverlapcheck(newname,newreg,oldname,gblname,newcoll) i (newname'="")&(newname("TYPE")'="RANGE") q ; if newname is specified and is not a RANGE, there is no overlap possibility k namrangeoverlap ; normally we expect this array to be killed once a command completes cleanly ; but in case of errors, it is possible this exists. In that case, just clean it now. n nam1,tmpnam1,nam2,tmpnam2,reg s nam1="" f s nam1=$o(nams(nam1)) q:""=nam1 d . i nam1=$g(oldname) q ; if oldname is defined, assume as if that has been deleted from the "nams" array . i '$d(nams(nam1,"TYPE")) q ; for unsubscripted name specifications, "nam1" is an unsubscripted node . i nams(nam1,"TYPE")'="RANGE" q . i $data(gblname)&(nams(nam1,"SUBS",0)'=gblname) q ; if called in with a specific gblname, skip other gblname ranges . k tmpnam1 . m tmpnam1=nams(nam1) . s tmpnam1=nams(nam1) . i newname'="" d namerangeoverlapcheck2(.newname,newreg,.tmpnam1) q . s reg=tmpnam1 . s nam2="" . f s nam2=$o(nams(nam2)) q:nam1=nam2 d . . i '$d(nams(nam2,"TYPE")) q ; for unsubscripted name specifications, "nam2" is an unsubscripted node . . i nams(nam2,"TYPE")'="RANGE" q . . i $data(gblname)&(nams(nam2,"SUBS",0)'=gblname) q ; if called in with a specific gblname, skip other gblname ranges . . k tmpnam2 . . m tmpnam2=nams(nam2) . . s tmpnam2=nams(nam2) . . i '$d(newcoll) d . . . d namerangeoverlapcheck2(.tmpnam1,reg,.tmpnam2) . . e d namerangeoverlapcheck2(.tmpnam1,reg,.tmpnam2,newcoll) q chkcoll(coll,gblname,collver) i coll=0 q ; 0 is always a good collation sequence i (coll<0)!(coll>maxgnam("COLLATION")) zm gdeerr("GBLNAMCOLLRANGE"):coll n savetrap s savetrap=$etrap n $etrap s $etrap="goto collundeferr" v "YCHKCOLL":coll s $etrap=savetrap i $d(collver) d . i (0=$view("YCOLLATE",coll,collver)) d . . n ver . . s ver=$view("YCOLLATE",coll) . . i $view("YCOLLATE",coll,ver) zm gdeerr("GBLNAMCOLLVER"):gblname:coll:collver:ver q collundeferr i $zstatus'["COLLATIONUNDEF" q ; don't know how a non-COLLATIONUNDEF error can occur. ; let parent frame handle this like any other error s $ecode="" s $etrap=savetrap zm gdeerr("GBLNAMCOLLUNDEF"):coll:gblname q strsub:(sub,subcnt) new state,xstr,len,iszchar,istart,x,y ; iszchar and istart are initialized in lower level invocations ; but needed outside that frame too hence the new done here (in parent) new retsub ; the subscript that is returned after doing $c() transformations new i,previ,doublequote ; check if string subscript is properly formatted. done using a DFA. set state=0,len=$zlength(sub),doublequote="""",retsub="" for i=1:1:len set c=$zextract(sub,i) do @state ; check if state is terminating zmessage:((state'=2)&(state'=6)) gdeerr("NAMNOTSTRSUBS"):subcnt:sub set:(state=2) retsub=retsub_$zextract(sub,previ,i-1) ; if retsub is a canonical number, strip off the double quotes and return it as a number quit $select(retsub=+retsub:retsub,1:doublequote_retsub_doublequote) 0 ; i c=doublequote s state=1,previ=i+1 e i c="$" s state=3 e zm gdeerr("NAMNOTSTRSUBS"):subcnt:sub q 1 ; i c=doublequote s state=2 ; else state stays at 1 q 2 ; i c=doublequote s state=1 e i c="_" s state=0,retsub=retsub_$ze(sub,previ,i-2) ; previ would be reset when we execute the label "0" (state=0) e zm gdeerr("NAMNOTSTRSUBS"):subcnt:sub q 3 ; ; the only $ functions allowed are $C, $CHAR, $ZCH, $ZCHAR. check for those. n j,fn s j=$zf(sub,"(",i) s fn=$ze(sub,i,$s(j'=0:j-2,1:$zl(sub))) s fn=$tr(fn,lower,upper) i ((fn="C")!(fn="CHAR")) s iszchar=0 e i ((fn="ZCH")!(fn="ZCHAR")) s iszchar=1 e zm gdeerr("NAMSTRSUBSFUN"):subcnt:sub i j=0 zm gdeerr("NAMSTRSUBSLPAREN"):subcnt:sub ; no "(" found following $ s i=j-1,state=4 q 4 ; s istart=i i c'?1N zm gdeerr("NAMSTRSUBSCHINT"):subcnt:sub s state=5 q 5 ; i c="," d numcheck(istart,i) s state=4 q i c=")" d numcheck(istart,i) s state=6 q i c'?1N zm gdeerr("NAMSTRSUBSCHINT"):subcnt:sub ; else state stays at 5 q 6 ; i c="_" s state=0 e zm gdeerr("NAMNOTSTRSUBS"):subcnt:sub q numcheck(istart,i); n num,dollarc s num=$ze(sub,istart,i-1) d chknumoflow(subcnt,num) d chknumexact(subcnt,num,num) ; check if string subscript has $c() usages. If so, check if $zl($c(NNN)) for each number NNN is non-zero i (iszchar&'$zl($zch(num)))!('iszchar&'$zl($c(num))) zm gdeerr("NAMSTRSUBSCHARG"):subcnt:sub:num ; now that we know $zch()/$c() is passed a valid number, add this to the string subscript to be returned s dollarc=$s(iszchar:$zch(num),1:$c(num)),retsub=retsub_dollarc i dollarc="""" s retsub=retsub_dollarc ; if double-quote is specified as a $c() expression, use two double-quotes ; to indicate this is a double-quote inside the string subscript q numsub:(sub,subcnt) n mantissa ; check if a valid subscript. if not error right away i sub'?.(.1"+",.1"-").N.1(1".".N).1(1"E"1(.1"+",.1"-")1.N)!(sub=".") zm gdeerr("NAMSUBSBAD"):subcnt:sub ; check if number too big to be represented in GT.M. If so issue NAMNUMSUBSOFLOW error d chknumoflow(subcnt,sub) ; check if mantissa contains more digits than GT.M can store. If so issue NAMNUMSUBNOTEXACT error s mantissa=$p(sub,"E") s mantissa=$ztr(mantissa,"-+.") ; remove all -, + and . so we get just the mantissa out d chknumexact(subcnt,mantissa,sub) ; now that we know this is a valid numeric subscript, make it canonical (if needed) e.g. 1E+000 -> 1 etc. q +sub chknumexact(subcnt,mantissa,sub) n i,j s j=$zl(mantissa) f i=1:1:j q:$ze(mantissa,i)'=0 ; remove leading 0s. keep at least one 0 i iPARREGLN zm gdeerr("VALTOOLONG"):REGION:PARREGLN:renpref_"region" q SEGMENT k SEGMENT i ntoktype="TKEOL" zm gdeerr("OBJREQD"):renpref_"segment" d GETTOK^GDESCAN s SEGMENT=$tr(token,lower,upper) i '$zl(SEGMENT) zm gdeerr("VALUEBAD"):token:renpref_"segment" i $l(SEGMENT)'=$zl(SEGMENT) zm gdeerr("NONASCII"):SEGMENT:"segment" ; error if the name of the segment is non-ascii i SEGMENT=defseg q s x=$ze(SEGMENT) i x'?1A d prefixbaderr(SEGMENT,"segment") i $ze(SEGMENT,2,999)'?.(1AN,1"_",1"$") zm gdeerr("VALUEBAD"):SEGMENT:"segment" i $zl(SEGMENT)>PARSEGLN zm gdeerr("VALTOOLONG"):SEGMENT:PARSEGLN:renpref_"segment" q prefixbaderr:(name,str) n namestr s namestr="name" zm gdeerr("PREFIXBAD"):name:renpref_str:namestr q matchtok:(tok,ent) d GETTOK^GDESCAN i toktype=tok q i tok=sep zm gdeerr("MISSINGDELIM"):tokens(sep):ent:token q zm gdeerr("VALUEBAD"):token:ent q checkkw(kw,ent,kwlist) n x1,x2 s kw=$tr(kw,lower,upper) i $ze(kw,1,2)="NO" s negated=1,kw=$ze(kw,3,999) e s negated=0 s x1="" f s x1=$o(@kwlist@(x1)) q:kw=$ze(x1,1,$zl(kw))!'$zl(x1) i '$zl(x1) zm gdeerr("KEYWRDBAD"):kw:ent s x2=x1 s x2=$o(@kwlist@(x2)) i ('$zl(x2))&(kw=$ze(x2,1,$zl(kw))) zm gdeerr("KEYWRDAMB"):kw:ent s kw=x1 q getqual: d qual(.lqual,"qualifier","syntab("""_verb_""","""_gqual_""")") i '$d(lquals(lqual)) s lquals(lqual)=$g(lqual("value")) e zm gdeerr("QUALDUP"):lqual q getlitm: d qual(.lqual,"qualifier","syntab("""_verb_""","""_gqual_""","""_lhead_""")") i '$d(lquals(lqual)) s lquals(lqual)=$g(lqual("value")) e zm gdeerr("QUALDUP"):lqual q ;----------------------------------------------------------------------------------------------------------------------------------- ADD CHANGE d qual(.gqual,"object","syntab("""_verb_""")"),@gqual f q:ntoktype="TKEOL" d getqual d @gqual^@("GDE"_$ze(verb,1,5)) q RENAME n old,new d qual(.gqual,"object","syntab("""_verb_""")") n renpref s renpref="old " d @gqual m old=@gqual ; merge needed since subscripted nodes might also be involved s renpref="new " d @gqual m new=@gqual ; merge needed since subscripted nodes might also be involved s renpref="" d matchtok("TKEOL","End of line") d @gqual^GDERENAM(.old,.new) ; pass by reference since old and new could have subscripted nodes (in case of -NAME) q TEMPLATE d qual(.gqual,"object","syntab("""_verb_""")") f q:ntoktype="TKEOL" d getqual d @gqual^GDETEMPL q DELETE d qual(.gqual,"object","syntab("""_verb_""")"),@gqual,matchtok("TKEOL","End of line"),@gqual^GDEDELET q LOCKS d qual(.gqual,"object","syntab("""_verb_""")"),matchtok("TKEOL","End of line"),LOCKS^GDELOCKS q LOG i ntoktype="TKEOL" d INQUIRE^GDELOG q d qual(.gqual,"object","syntab("""_verb_""")"),matchtok("TKEOL","End of line"),LOG^GDELOG q SHOW i ntoktype="TKEOL" d ALL^GDESHOW q d qual(.gqual,"object","syntab("""_verb_""")") s t="|NAME|GBLNAME|REGION|SEGMENT"[("|"_gqual) i t,ntoktype="TKEOL" d @("ALL"_$ze(gqual,1,5))^GDESHOW q n mapreg i gqual="MAP",ntoktype'="TKEOL" d getqual s mapreg=$g(lquals("REGION")) i 't,"COMMANDS"=gqual,ntoktype'="TKEOL" d getqual s cfile=$g(lquals("FILE")) i 't,"INSTANCE"=gqual,ntoktype="TKEOL" d INSTANCE^GDESHOW q d @gqual:t,matchtok("TKEOL","End of line"),@gqual^GDESHOW q VERIFY i ntoktype="TKEOL" s x=$$ALL^GDEVERIF q d qual(.gqual,"object","syntab("""_verb_""")") i "ALL|MAP"[gqual s x=$$ALL^GDEVERIF q n verified s verified=1 i ntoktype="TKEOL" d . d @("ALL"_$ze(gqual,1,3))^GDEVERIF e i "NAMEGBLNAMEREGIONSEGMENTINSTANCE"[gqual d . d @gqual,@gqual^GDEVERIF e zm gdeerr("NOVALUE"):gqual i $d(verified) zm gdeerr("VERIFY"):$s(verified:"OK",1:"FAILED") w ! q EXIT QUIT d matchtok("TKEOL","End of line") d ^@("GDE"_$tr(verb,lower,upper)) q SETGD f d q:ntoktype="TKEOL" . d qual(.gqual,"object","syntab("""_verb_""")") s:gqual="FILE" tfile=gqual("value") s:gqual="QUIT" update=0 d GDESETGD^GDESETGD q HELP SPAWN d ^@("GDE"_$tr(verb,lower,upper)) q fis-gtm-V7.0-005/sr_port/gdequit.m0000755000032200000250000000105014342376331015662 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2001, 2013 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; quit: ;implement the verb: QUIT QUIT zm gdeerr("NOACTION"):$zparse(tfile,"",defgldext) d GETOUT^GDEEXIT h fis-gtm-V7.0-005/sr_port/gderenam.m0000755000032200000250000000431414342376331016010 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2001, 2013 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; rename: ;implement the verb: RENAME NAME(old,new) i old=new q i old="*" zm gdeerr("LVSTARALON") i '$d(nams(old)) d error1 i $d(nams(new)) d error2 ; check if changing a name (with ranges) poses issues with overlap amongst other existing name ranges d namerangeoverlapcheck^GDEPARSE(.new,nams(old),old) s update=1 m nams(new)=new s nams(new)=nams(old) k nams(old) i $d(namrangeoverlap) d namcoalesce^GDEMAP q REGION(old,new) i old=new q i '$d(regs(old)) d error1 i $d(regs(new)) d error2 s update=1,s="" f s s=$o(regs(old,s)) q:'$l(s) s regs(new,s)=regs(old,s) k regs(old) f s s=$o(nams(s)) q:'$l(s) i nams(s)=old s nams(s)=new q SEGMENT(old,new) i old=new q i '$d(segs(old)) d error1 i $d(segs(new)) d error2 n lquals s update=1,am=segs(old,"ACCESS_METHOD"),s="" f s s=$o(segs(old,s)) q:'$l(s) s segs(new,s)=segs(old,s),lquals(s)=segs(old,s) i '$$SQUALS^GDEVERIF(am,.lquals) k segs(new) zm gdeerr("OBJNOTCHG"):"segment":old s segs(new,"ACCESS_METHOD")=am k segs(old) f s s=$o(regs(s)) q:'$l(s) i regs(s,"DYNAMIC_SEGMENT")=old s regs(s,"DYNAMIC_SEGMENT")=new q GBLNAME(old,new) i old=new q i '$d(gnams(old)) d error1 ; check if changing (i.e. deleting) collation for "old" GBLNAME poses issues with existing names & ranges i $d(gnams(old,"COLLATION")) d . d gblnameeditchecks^GDEPARSE(old,0) . i $d(namrangeoverlap) d namcoalesce^GDEMAP i $d(gnams(new)) d error2 ; check if changing (i.e. adding) collation for "new" GBLNAME poses issues with existing names & ranges d gblnameeditchecks^GDEPARSE(new,+$get(gnams(old,"COLLATION"))) i $d(namrangeoverlap) d namcoalesce^GDEMAP s update=1 m gnams(new)=gnams(old) k gnams(old) q error1: zm gdeerr("OBJNOTFND"):"Old "_$tr(gqual,upper,lower):old q error2: zm gdeerr("OBJDUP"):"New "_$tr(gqual,upper,lower):new q fis-gtm-V7.0-005/sr_port/gdescan.m0000755000032200000250000001477014342376331015641 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2001, 2013 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; gdescan: ;scanner used by gdeparse GETTOK n c,tmptok,tokisname ; If -name has been seen, then change GETTOK to fetch next token (which is the actual name specification) ; by taking double-quotes into account. Same case with the next TWO tokens (not just one) in case of a ; RENAME command where two names are specified after the -name. ; Otherwise use regular token scanning and search only for delimiters (e.g. " ", "," etc.) i ($get(toktype)=sep)&(ntoktype="TKIDENT")&$data(verb)&('$data(gqual))&$data(syntab(verb,"NAME")) d . ; check if the current token is -NAME. if so use special rules to parse the next token . s tmptok=ntoken . d checkkw^GDEPARSE(.tmptok,"object","syntab("""_verb_""")") . i tmptok="NAME" s tokisname=1 e i ($get(verb)="RENAME")&(($get(toktype)=sep)!$data(gqual)) s tokisname=1 s token=ntoken,toktype=ntoktype ; s dbgtoken($incr(dbgtokcnt))=token ; if uncommented, helps debug the GDE token parser d skipwhitespace s c=$ze(comline,cp) i ($c(10)=c)!($c(13)=c) s c="",cp=$zl(comline),ntoktype="TKEOL" d @ntoktype q s ntoktype=$s($d(tokens(c)):tokens(c),1:"TKIDENT") ; If "gqual" is not yet filled in, it means we are still parsing either the "verb" or "gqual" but not "lqual". ; If we see a double-quote at the start while in this state, parse the token using TKIDENT (and not TKSTRLIT ; as we dont expect strings in this context). i (ntoktype="TKSTRLIT")&('$data(gqual)) s ntoktype="TKIDENT" d TKIDENTspacedelims q ; Similarly if tokisname is TRUE, then parse the next token as a name-specification i $data(tokisname) d TKIDENTspacedelims q d @ntoktype q shotoks: ; for debugging only w !," toktype: ",toktype,?24," token: '",token,"'" w ?48," ntoktype: ",ntoktype,?72,"ntoken: '",ntoken,"'" q skipwhitespace n i f i=0:1 s c=$ze(comline,cp+i) q:(c'=" ")&(c'=TAB) s cp=cp+i q TKIDENT ; if not parsing a list, a "=" is followed by a token that could have special characters (like "=" or "," or "-" etc.) ; in this case we dont want these special characters to terminate the parse. Only a whitespace should terminate it. ; if parsing inside a list, a "," or ")" or "=" could terminate the parse. So we cannot use the whitespace-only parse ; logic in that case. i (toktype="TKEQUAL")&'$data(listparsing) d TKIDENTspacedelims q ; by similar logic, if "gqual" is not yet filled in, and we did not see a - as the previous token, it means we are ; parsing the "gqual". In that case, end the parse only when whitespace is encountered, not if "=" or "," is seen. i (toktype'=sep)&'$data(gqual) d TKIDENTspacedelims q n i d tokscan(.tokendelim) q TKIDENTspacedelims d tokscan(.spacedelim) q tokscan:(delim) n i,c i '$data(tokisname) d . f i=0:1 s c=$ze(comline,cp+i) q:$data(delim(c)) e d . ; About to parse the token following a -name. Take double-quotes into account. . ; Any delimiter that comes inside a double-quote does NOT terminate the scan/parse. . ; Implement the following DFA (Deterministic Finite Automaton) . ; State 0 --> next char is a double-quote --> State 1 . ; State 0 --> next char is NOT a double-quote --> State 0 . ; State 1 --> next char is a double-quote --> State 2 . ; State 1 --> next char is NOT a double-quote --> State 1 . ; State 2 --> next char is a double-quote --> State 1 . ; State 2 --> next char is NOT a double-quote --> State 0 . ; Also note down (in NAMEsubs) the columns where LPAREN, COMMA and COLON appear. Later used in NAME^GDEPARSE . n quotestate,parenstate,errstate,quitloop . s quotestate=0,parenstate=0,errstate="" . k NAMEsubs ; this records the column where subscript delimiters COMMA or COLON appear in the name specification . k NAMEtype . s NAMEtype="POINT",NAMEsubs=0,quitloop=0 . f i=0:1 s c=$ze(comline,cp+i) q:(c="") d q:quitloop . . i c="""" s quotestate=$s(quotestate=1:2,1:1) . . e s quotestate=$s(quotestate=2:0,1:quotestate) i 'quotestate d . . . i $data(delim(c)) s quitloop=1 q . . . i (parenstate=2) i '$zl(errstate) s errstate="NAMRPARENNOTEND" . . . i (c="(") d . . . . i parenstate s parenstate=parenstate+2 q ; nested parens . . . . s parenstate=1 . . . . s NAMEsubs($incr(NAMEsubs))=(i+2) . . . i (c=",") d . . . . i 'parenstate i '$zl(errstate) s errstate="NAMLPARENNOTBEG" . . . . i (1'=parenstate) q ; nested parens . . . . i NAMEtype="RANGE" i '$zl(errstate) s errstate="NAMRANGELASTSUB" . . . . s NAMEsubs($incr(NAMEsubs))=(i+2) . . . i c=":" d . . . . i 'parenstate i '$zl(errstate) s errstate="NAMLPARENNOTBEG" . . . . i NAMEtype="RANGE" i '$zl(errstate) s errstate="NAMONECOLON" . . . . s NAMEsubs($incr(NAMEsubs))=(i+2),NAMEtype="RANGE" . . . i c=")" d . . . . i 'parenstate i '$zl(errstate) s errstate="NAMLPARENNOTBEG" . . . . i (1'=parenstate) s parenstate=parenstate-2 q ; nested parens . . . . s parenstate=2 . . . . s NAMEsubs($incr(NAMEsubs))=(i+2) . i quotestate i '$zl(errstate) s errstate="STRMISSQUOTE" . i (1=parenstate)!(2") i $x'<(x(3)-1) w !,BOL w ?x(3),$s(regs(s,"BEFORE_IMAGE"):"Y",1:"N"),?x(4),$j(regs(s,"BUFFER_SIZE"),5) w ?x(5),$j(regs(s,"ALLOCATION"),10) w ?x(6),$j(regs(s,"EXTENSION"),10),?x(7),$j(regs(s,"AUTOSWITCHLIMIT"),13) w !,BOL q regionc: n s,q,val,synval,tmpval,type w !,"DELETE "_delim_"REGION "_defreg ; delete DEFAULT SEGMENT at same time DEFAULT REGION is deleted to avoid potential KEYSIZIS issues when ; playing the GDE SHOW -COMMANDS output in a fresh GDE session (GTM-7954). w !,"DELETE "_delim_"SEGMENT "_defseg s s="" f s s=$o(regs(s)) q:'$l(s) d . w !,"ADD "_delim_"REGION ",s . s q="" . f s q=$o(syntab("TEMPLATE","REGION",q)) q:""=q d . . s synval=syntab("TEMPLATE","REGION",q) . . s val=regs(s,q) . . s tmpval=$get(tmpreg(q)) . . i synval["LIST" d q . . . ; special processing since this option can in turn be an option-list . . . n l,list,lval,lsynval,ltype . . . i (synval["NEGATABLE")&(0=val) d q . . . . i tmpval=val q . . . . w " "_delim_"NO"_q . . . s list="",l="" . . . f s l=$o(syntab("TEMPLATE","REGION",q,l)) q:""=l d . . . . s lval=regs(s,l) . . . . i $get(tmpreg(l))=lval q . . . . s ltype=$get(syntab("TEMPLATE","REGION",q,l,"TYPE")) ; note: possible this is "" for e.g. "BEFORE_IMAGE") . . . . i (ltype'="")&($data(typevalue("NUM2STR",ltype))) s list=list_","_l_"="_typevalue("NUM2STR",ltype,lval) q . . . . s lsynval=syntab("TEMPLATE","REGION",q,l) . . . . i lsynval["NEGATABLE" s list=list_","_$s(lval:"",1:"NO")_l q . . . . d listadd(.list,l,lval) . . . i list="" q . . . w " "_delim_q_"=("_$ze(list,2,MAXSTRLEN)_")" ; strip out leading "," from list hence the 2 in $ze . . i tmpval=val q . . s type=$get(syntab("TEMPLATE","REGION",q,"TYPE")) ; note: possible this is "" . . i (type'="")&($data(typevalue("NUM2STR",type))) w " "_delim_q_"="_typevalue("NUM2STR",type,val) q . . i synval["NEGATABLE" w " "_delim_$s(val:"",1:"NO")_q q . . d qualadd(" ",delim,q,val) w !,BOL q listadd:(list,l,lval) i l="FILE_NAME" d . ; FILE_NAME has to be inside double-quotes or else GDE will parse the remainder of the line as the file name . s list=list_","_l_"="""_$$namedisp(lval,0)_"""" ; since file name can have control characters use "namedisp" e s list=list_","_l_"="_lval q qualadd:(prefix,delim,qual,val) i qual="FILE_NAME" d . ; FILE_NAME has to be inside double-quotes or else GDE will parse the remainder of the line as the file name . w " "_delim_q_"="""_$$namedisp(val,0)_"""" ; since file name can have control characters use "namedisp" e w prefix_delim_q_"="_val q SEGMENT i '$d(segs(SEGMENT)) zm gdeerr("OBJNOTFND"):"Segment":SEGMENT q d s2 i log s BOL="!" u @uself w BOL d s2 w ! u @useio s BOL="" q s2: d seghd s s=SEGMENT s am=segs(s,"ACCESS_METHOD") d oneseg q ALLSEGME d s1 i log s BOL="!" u @uself w BOL d s1 w ! u @useio s BOL="" q s1: d seghd s s="" f s s=$o(segs(s)) q:'$l(s) s am=segs(s,"ACCESS_METHOD") d oneseg q oneseg: w !,BOL,?x(1),s,?x(2),$$namedisp(segs(s,"FILE_NAME"),1) i $x'<(x(3)-1) w !,BOL w ?x(3),segs(s,"ACCESS_METHOD") i am="USER" q w ?x(4),$s(segs(s,"FILE_TYPE")="DYNAMIC":"DYN",1:"STA") w ?x(5),$j(segs(s,"BLOCK_SIZE"),5),?x(6),$j(segs(s,"ALLOCATION"),10),?x(7),$j(segs(s,"EXTENSION_COUNT"),5) d @am q BG w ?x(8),"GLOB=",$j(segs(s,"GLOBAL_BUFFER_COUNT"),4) w !,BOL,?x(8),"LOCK=",$j(segs(s,"LOCK_SPACE"),4) w !,BOL,?x(8),"RES =",$j(segs(s,"RESERVED_BYTES"),4) ; For non-encryption platforms, always show FLAG as OFF. w !,BOL,?x(8),"ENCR=",$s((encsupportedplat=TRUE&segs(s,"ENCRYPTION_FLAG")):" ON",1:" OFF") w !,BOL,?x(8),"MSLT=",$j(segs(s,"MUTEX_SLOTS"),4) w !,BOL,?x(8),"DALL=",$s(segs(s,"DEFER_ALLOCATE"):" YES",1:" NO") w !,BOL,?x(8),"AIO =",$s(segs(s,"ASYNCIO"):" ON",1:" OFF") w !,BOL,?x(8),"FBWR=",$j(segs(s,"FULLBLKWRT"),4) q MM w ?x(8),$s(segs(s,"DEFER"):"DEFER",1:"NODEFER") w !,BOL,?x(8),"LOCK=",$j(segs(s,"LOCK_SPACE"),4) w !,BOL,?x(8),"RES =",$j(segs(s,"RESERVED_BYTES"),4) w !,BOL,?x(8),"ENCR= OFF" w !,BOL,?x(8),"MSLT=",$j(segs(s,"MUTEX_SLOTS"),4) w !,BOL,?x(8),"DALL=",$s(segs(s,"DEFER_ALLOCATE"):" YES",1:" NO") w !,BOL,?x(8),"FBWR=",$j(segs(s,"FULLBLKWRT"),4) q segmentc: n s,q,val,synval,tmpval,type,am s s="" f s s=$o(segs(s)) q:'$l(s) d . s am=segs(s,"ACCESS_METHOD") . w !,"ADD "_delim_"SEGMENT ",s . i tmpacc'=am w " "_delim_"ACCESS_METHOD=",am . s q="" . f s q=$o(syntab("TEMPLATE","SEGMENT",q)) q:""=q d . . i q="ACCESS_METHOD" q ; already processed . . s synval=syntab("TEMPLATE","SEGMENT",q) . . i synval["LIST" d ABORT^GDE ; segmentc is not designed to handle LIST in segment qualifiers. . . s val=segs(s,q) . . s tmpval=$get(tmpseg(am,q)) . . i tmpval=val q . . s type=$get(syntab("TEMPLATE","SEGMENT",q,"TYPE")) ; note: possible this is "" . . i (type'="")&($data(typevalue("NUM2STR",type))) w " "_delim_q_"="_typevalue("NUM2STR",type,val) q . . i synval["NEGATABLE" w " "_delim_$s(val:"",1:"NO")_q q . . d qualadd(" ",delim,q,val) w !,BOL q MAP n map,mapdisp,mapdispmaxlen i '$d(mapreg) n mapreg s mapreg="" e i '$d(regs(mapreg)) zm gdeerr("OBJNOTFND"):"Region":mapreg q d NAM2MAP^GDEMAP,m1 i log s BOL="!" u @uself w BOL d m1 w ! u @useio s BOL="" q m1: n l1,s1,s2 s mapdispmaxlen=0 d mapdispcalc d maphd s s1=$o(map("$")) i s1'="%" s map("%")=map("$"),s1="%" f s s2=s1,s1=$o(map(s2)) q:'$zl(s1) d onemap(s1,s2) d onemap("...",s2) i $d(nams("#")) s s2="LOCAL LOCKS",map(s2)=nams("#") d onemap("",s2) k map(s2) q onemap:(s1,s2) i $l(mapreg),mapreg'=map(s2) q s l1=$zl(s1) i $zl(s2)=l1,$ze(s1,l1)=0,$ze(s2,l1)=")",$ze(s1,1,l1-1)=$ze(s2,1,l1-1) q i '$d(mapdisp(s1)) s mapdisp(s1)=s1 ; e.g. "..." or "LOCAL LOCKS" i '$d(mapdisp(s2)) s mapdisp(s2)=s2 ; e.g. "..." or "LOCAL LOCKS" w !,BOL,?x(1),mapdisp(s2),?x(2),mapdisp(s1),?x(3),"REG = ",map(s2) i '$d(regs(map(s2),"DYNAMIC_SEGMENT")) d q . w !,BOL,?x(3),"SEG = NONE",!,BOL,?x(3),"FILE = NONE" s j=regs(map(s2),"DYNAMIC_SEGMENT") w !,BOL,?x(3),"SEG = ",j i '$d(segs(j,"ACCESS_METHOD")) w !,BOL,?x(3),"FILE = NONE" e s s=segs(j,"FILE_NAME") w !,BOL,?x(3),"FILE = ",$$namedisp(s,1) q TEMPLATE d t1 i log s BOL="!" u @uself w BOL d t1 w ! u @useio s BOL="" q t1: d tmpreghd w !,BOL,?x(1),"" w ?x(3),$j(tmpreg("COLLATION_DEFAULT"),4) w ?x(4),$j(tmpreg("RECORD_SIZE"),7) w ?x(5),$j(tmpreg("KEY_SIZE"),5) w ?x(6),$s(tmpreg("NULL_SUBSCRIPTS")=1:"ALWAYS",tmpreg("NULL_SUBSCRIPTS")=2:"EXISTING",1:"NEVER") w ?x(7),$s(tmpreg("STDNULLCOLL"):"Y",1:"N") w ?x(8),$s(tmpreg("JOURNAL"):"Y",1:"N") w ?x(9),$s(tmpreg("INST_FREEZE_ON_ERROR"):"Y",1:"N") w ?x(10),$s(tmpreg("QDBRUNDOWN"):"Y",1:"N") w ?x(11),$s(tmpreg("EPOCHTAPER"):"Y",1:"N") w ?x(12),$s(tmpreg("AUTODB"):"Y",1:"N") w ?x(13),$s(tmpreg("STATS"):"Y",1:"N") w ?x(14),$s(tmpreg("LOCK_CRIT"):"Sep",1:"DB") i tmpreg("JOURNAL") d tmpjnlhd,tmpjnlbd d tmpseghd w !,BOL,?x(1),"" w ?x(2),$s(tmpacc="BG":" *",1:"") w ?x(3),"BG" w ?x(4),$s(tmpseg("BG","FILE_TYPE")="DYNAMIC":"DYN",1:"STA") w ?x(5),$j(tmpseg("BG","BLOCK_SIZE"),5) w ?x(6),$j(tmpseg("BG","ALLOCATION"),10) w ?x(7),$j(tmpseg("BG","EXTENSION_COUNT"),5) w ?x(8),"GLOB =",$j(tmpseg("BG","GLOBAL_BUFFER_COUNT"),4) w !,BOL,?x(8),"LOCK =",$j(tmpseg("BG","LOCK_SPACE"),4) w !,BOL,?x(8),"RES =",$j(tmpseg("BG","RESERVED_BYTES"),4) w !,BOL,?x(8),"ENCR =",$s((encsupportedplat=TRUE&tmpseg("BG","ENCRYPTION_FLAG")):" ON",1:" OFF") w !,BOL,?x(8),"MSLT =",$j(tmpseg("BG","MUTEX_SLOTS"),4) w !,BOL,?x(8),"DALL =",$s(tmpseg("BG","DEFER_ALLOCATE"):" YES",1:" NO") w !,BOL,?x(8),"AIO =",$s(tmpseg("BG","ASYNCIO"):" ON",1:" OFF") w !,BOL,?x(8),"FBWR =",$j(tmpseg("BG","FULLBLKWRT"),4) w !,BOL,?x(1),"" w ?x(2),$s(tmpacc="MM":" *",1:"") w ?x(3),"MM" w ?x(4),$s(tmpseg("MM","FILE_TYPE")="DYNAMIC":"DYN",1:"STA") w ?x(5),$j(tmpseg("MM","BLOCK_SIZE"),5) w ?x(6),$j(tmpseg("MM","ALLOCATION"),10) w ?x(7),$j(tmpseg("MM","EXTENSION_COUNT"),5) w ?x(8),$s(tmpseg("MM","DEFER"):"DEFER",1:"NODEFER") w !,BOL,?x(8),"LOCK =",$j(tmpseg("MM","LOCK_SPACE"),4) w !,BOL,?x(8),"MSLT =",$j(tmpseg("MM","MUTEX_SLOTS"),4) w !,BOL,?x(8),"DALL =",$s(tmpseg("MM","DEFER_ALLOCATE"):" YES",1:" NO") w !,BOL,?x(8),"FBWR =",$j(tmpseg("MM","FULLBLKWRT"),4) q tmpjnlbd: w !,BOL,?x(1),"",?x(2),$s($zl(tmpreg("FILE_NAME")):$$namedisp(tmpreg("FILE_NAME"),1),1:"") i $x'<(x(3)-1) w !,BOL w ?x(3),$s(tmpreg("BEFORE_IMAGE"):"Y",1:"N"),?x(4),$j(tmpreg("BUFFER_SIZE"),5) w ?x(5),$j(tmpreg("ALLOCATION"),10) w ?x(6),$j(tmpreg("EXTENSION"),10),?x(7),$j(tmpreg("AUTOSWITCHLIMIT"),13) w !,BOL q templatec: n q,synval,tmpval,type,cmd,defercnt,defercmd,i,am,s,freq,freqx,val ; compute template values that are most common across regions and store these into tmpreg s s="" f s s=$o(regs(s)) q:'$l(s) d . s q="" . f s q=$o(tmpreg(q)) q:'$l(q) d . . i tmpreg(q)="" q ; if this qualifier has a "" template value, then skip processing it (e.g. "FILE_NAME") . . s val=regs(s,q) . . s freq=$incr(freq(q,val)) . . s freqx(q,freq)=val f s q=$o(tmpreg(q)) q:'$l(q) d . i tmpreg(q)="" q ; if this qualifier has a "" template value, then skip processing it (e.g. "FILE_NAME") . s freq=$o(freqx(q,""),-1) . s val=freqx(q,freq) . s tmpreg(q)=val ; compute template values that are most common across segments and store these into tmpseg k freq,freqx s s="" f s s=$o(segs(s)) q:'$l(s) d . s am=segs(s,"ACCESS_METHOD") . s q="" . f s q=$o(tmpseg(am,q)) q:'$l(q) d . . s val=segs(s,q) . . s freq=$incr(freq(am,q,val)) . . s freqx(am,q,freq)=val s am="" f s am=$o(freqx(am)) q:'$l(am) d . s q="" . f s q=$o(tmpseg(am,q)) q:'$l(q) d . . s freq=$o(freqx(am,q,""),-1) . . s val=freqx(am,q,freq) . . s tmpseg(am,q)=val ; use more optimal templates (tmpreg/tmpseg) while generating template commands below ; --------------------------------------------------------- ; dump TEMPLATE -REGION section ; --------------------------------------------------------- s q="" s cmd="TEMPLATE "_delim_"REGION ",defercmd="",defercnt=0 f s q=$o(syntab("TEMPLATE","REGION",q)) q:""=q d . s synval=syntab("TEMPLATE","REGION",q) . s tmpval=$get(tmpreg(q)) . i (tmpval="") q ; if this qualifier does not have a non-default template value, never mind . i synval["LIST" d q . . ; special processing since this option can in turn be an option-list . . n l,list,lsynval,ltype . . i (synval["NEGATABLE")&(0=tmpval) s defercmd($incr(defercnt))=cmd_delim_"NO"_q . . s list="",l="" . . f s l=$o(syntab("TEMPLATE","REGION",q,l)) q:""=l d . . . s lval=tmpreg(l) . . . s ltype=$get(syntab("TEMPLATE","REGION",q,l,"TYPE")) ; note: possible this is "" for e.g. "BEFORE_IMAGE") . . . i (ltype'="")&($data(typevalue("NUM2STR",ltype))) s list=list_","_l_"="_typevalue("NUM2STR",ltype,lval) q . . . s lsynval=syntab("TEMPLATE","REGION",q,l) . . . i lsynval["NEGATABLE" s list=list_","_$s(lval:"",1:"NO")_l q . . . i (lval="") q ; if this qualifier is not applicable to this platform, never mind . . . s list=list_","_l_"="_lval . . i list="" q . . w !,cmd_delim_q_"=("_$ze(list,2,MAXSTRLEN)_")" ; strip out leading "," from list hence the 2 in $ze . s type=$get(syntab("TEMPLATE","REGION",q,"TYPE")) ; note: possible this is "" . i (type'="")&($data(typevalue("NUM2STR",type))) w !,cmd_delim_q_"="_typevalue("NUM2STR",type,tmpval) q . i synval["NEGATABLE" w !,cmd_delim_$s(tmpval:"",1:"NO")_q q . w ! . d qualadd(cmd,delim,q,tmpval) w !,BOL f i=1:1:defercnt write !,defercmd(i) ; finish off deferred work (if any) w !,BOL ; --------------------------------------------------------- ; dump TEMPLATE -SEGMENT -ACCESS_METHOD=MM,BG,USER section ; --------------------------------------------------------- s cmd="TEMPLATE "_delim_"SEGMENT " s am="" f s am=$o(tmpseg(am)) q:""=am d . w !,cmd,delim_"ACCESS_METHOD=",am . s q="" . f s q=$o(syntab("TEMPLATE","SEGMENT",q)) q:""=q d . . i q="ACCESS_METHOD" q ; already processed . . s synval=syntab("TEMPLATE","SEGMENT",q) . . i synval["LIST" d ABORT^GDE ; segmentc is not designed to handle LIST in segment qualifiers. . . s tmpval=$get(tmpseg(am,q)) . . i (tmpval="") q ; if this qualifier does not have a non-default template value, never mind . . s type=$get(syntab("TEMPLATE","SEGMENT",q,"TYPE")) ; note: possible this is "" . . i (type'="")&($data(typevalue("NUM2STR",type))) w !,cmd_delim_l_"="_typevalue("NUM2STR",type,tmpval) q . . i synval["NEGATABLE" w !,cmd_delim_$s(tmpval:"",1:"NO")_q q . . w ! . . d qualadd(cmd,delim,q,tmpval) . w !,BOL w !,cmd,delim_"ACCESS_METHOD="_tmpacc w !,BOL q ;----------------------------------------------------------------------------------------------------------------------------------- namehd: s x(0)=9,x(1)=1,x(2)=$s(x(1)+2+namedispmaxlen'>36:36,1:namedispmaxlen+x(1)+2) w !,BOL,!,BOL,?x(0),"*** NAMES ***",!,BOL,?x(1),"Global",?x(2),"Region" w !,BOL,?x(1),$tr($j("",$s(x(2)=36:78,1:x(2)+32))," ","-") q gblnamehd: s x(0)=9,x(1)=1,x(2)=36,x(3)=42 w !,BOL,!,BOL,?x(0),"*** GBLNAMES ***",!,BOL,?x(1),"Global",?x(2),"Coll",?x(3),"Ver" w !,BOL,?x(1),$tr($j("",78)," ","-") q insthd: s x(0)=43,x(1)=1 w !,BOL,!,BOL,?x(0),"*** INSTANCE ***" w !,BOL," Instance File (def ext: .repl)" w !,BOL,?x(1),$tr($j("",57)," ","-") q regionhd: s x(0)=32,x(1)=1,x(2)=33,x(3)=65,x(4)=71 s x(5)=79,x(6)=85,x(7)=95,x(8)=100,x(9)=104,x(10)=111,x(11)=117,x(12)=123,x(13)=130,x(14)=136,x(15)=141 w !,BOL,!,BOL,?x(0),"*** REGIONS ***" w !,BOL,?x(7),"Std" w ?x(9),"Inst" w !,BOL,?x(2),"Dynamic",?x(3)," Def" w ?x(4)," Rec" w ?x(5)," Key" w ?x(6),"Null" w ?x(7),"Null" w ?x(9),"Freeze" w ?x(10),"Qdb" w ?x(11),"Epoch" w ?x(14),"LOCK" w !,BOL w ?x(1),"Region" w ?x(2),"Segment" w ?x(3),"Coll" w ?x(4)," Size" w ?x(5)," Size" w ?x(6),"Subs" w ?x(7),"Coll" w ?x(8),"Jnl" w ?x(9),"on Err" w ?x(10),"Rndwn" w ?x(11),"Taper" w ?x(12),"AutoDB" w ?x(13),"Stats" w ?x(14),"Crit" w !,BOL,?x(1),$tr($j("",139)," ","-") q jnlhd: s x(0)=26,x(1)=1,x(2)=33,x(3)=59,x(4)=65,x(5)=71,x(6)=82,x(7)=91 w !,BOL,!,BOL,?x(0),"*** JOURNALING INFORMATION ***" w !,BOL,?x(1),"Region",?x(2),"Jnl File (def ext: .mjl)" w ?x(3),"Before",?x(4),$j("Buff",5),?x(5),$j("Alloc",10) w ?x(6),$j("Exten",10),?x(7),$j("AutoSwitch",13) w !,BOL,?x(1),$tr($j("",104)," ","-") q seghd: s x(0)=32,x(1)=1,x(2)=33,x(3)=53,x(4)=57,x(5)=61,x(6)=67,x(7)=78,x(8)=88 w !,BOL,!,BOL,?x(0),"*** SEGMENTS ***" w !,BOL,?x(1),"Segment",?x(2),"File (def ext: .dat)",?x(3),"Acc",?x(4),"Typ",?x(5),"Block",?x(6),$j("Alloc",10) w ?x(7),"Exten",?x(8),"Options" w !,BOL,?x(1),$tr($j("",94)," ","-") q maphd: s x="*** MAP"_$s($l(mapreg):" for region "_mapreg,1:"")_" ***" s mapdispmaxlen=$s(mapdispmaxlen<32:32,1:mapdispmaxlen+2) s x(0)=80-$l(x)*.5,x(1)=1,x(2)=x(1)+mapdispmaxlen,x(3)=x(2)+mapdispmaxlen+1 w !,BOL,!,BOL,?x(0),x w !,BOL,?x(1)," - - - - - - - - - - Names - - - - - - - - - -" w !,BOL,?x(1),"From",?x(2),"Up to",?x(3),"Region / Segment / File(def ext: .dat)" w !,BOL,?x(1),$tr($j("",$s(x(3)=66:122,1:x(3)+38))," ","-") q tmpreghd: s x(0)=32,x(1)=1,x(2)=19,x(3)=44,x(4)=50 s x(5)=58,x(6)=64,x(7)=74,x(8)=79,x(9)=83,x(10)=90,x(11)=96,x(12)=102,x(13)=109,x(14)=115,x(15)=120 w !,BOL,!,BOL,?x(0),"*** TEMPLATES ***" w !,BOL,?x(7),"Std" w ?x(9),"Inst" w !,BOL w ?x(3),$j("Def",4) w ?x(4),$j("Rec",7) w ?x(5),$j("Key",5) w ?x(6),"Null" w ?x(7),"Null" w ?x(9),"Freeze" w ?x(10),"Qdb" w ?x(11),"Epoch" w ?x(14),"LOCK" w !,BOL,?x(1),"Region" w ?x(3),$j("Coll",4) w ?x(4),$j("Size",7) w ?x(5),$j("Size",5) w ?x(6),"Subs" w ?x(7),"Coll" w ?x(8),"Jnl" w ?x(9),"on Err" w ?x(10),"Rndwn" w ?x(11),"Taper" w ?x(12),"AutoDB" w ?x(13),"Stats" w ?x(14),"Crit" w !,BOL,?x(1),$tr($j("",118)," ","-") q tmpjnlhd: s x(0)=26,x(1)=1,x(2)=18,x(3)=44,x(4)=51,x(5)=57,x(6)=68,x(7)=74 w !,BOL,?x(2),"Jnl File (def ext: .mjl)" w ?x(3),"Before",?x(4),$j("Buff",5),?x(5),$j("Alloc",10) w ?x(6),$j("Exten",10),?x(7),$j("AutoSwitch",13) w !,BOL,?x(1),$tr($j("",90)," ","-") q tmpseghd: s x(0)=32,x(1)=1,x(2)=18,x(3)=38,x(4)=42,x(5)=46,x(6)=52,x(7)=63,x(8)=72 w !,BOL,!,BOL,?x(1),"Segment",?x(2),"Active",?x(3),"Acc",?x(4),"Typ",?x(5),"Block",?x(6),$j("Alloc",10) w ?x(7),"Exten",?x(8),"Options" w !,BOL,?x(1),$tr($j("",78)," ","-") q namscollatecalc:(namesarray) ; we want subscripted names printed in collating order of subscripts (so x(2) gets printed before x(10)) ; whereas they would stored in the "nams" array as nams("x(2)") preceded by nams("x(10)") ; so determine the gds representation of the names and sort based on that (this automatically includes collation too). new s,type,nsubs,gvn,key,coll,keylen,i kill namscollate set s="" for set s=$order(namesarray(s)) quit:'$zlength(s) do . set type=$get(namesarray(s,"TYPE")),nsubs=$get(namesarray(s,"NSUBS")) . if (""=type)!(0=nsubs) set namscollate(s)=s quit ; if not a subscripted name process right away . if "POINT"=type set gvn="^"_namesarray(s,"NAME") . else set gvn="^"_namesarray(s,"GVNPREFIX")_namesarray(s,"SUBS",nsubs-1)_")" ; type="RANGE" . set coll=+$get(gnams(namesarray(s,"SUBS",0),"COLLATION")) . set key=$$gvn2gds^GDEMAP(gvn,coll) . set key=$zextract(key,1,$zlength(key)-2) ; remove trailing 00 00 . if "RANGE"=type do . . ; Some processing needed so X(2,4:5) (a range) comes AFTER X(2,4,5:"") (a point within X(2,4)). . . ; Add a 01 at the end of the left subscript of a range. . . set key=key_ONE . ; ASSERT : i $d(namscollate(key)) s $etrap="zg 0" zsh "*" zhalt 1 ; assert that checks for duplicate keys . set namscollate(key)=s quit namedisp(name,addquote) ; returns a name that is displayable (i.e. if it contains control characters, they are replaced by $c() etc.) ; if addquote=0, no surrounding double-quotes are added. ; if addquote=1 and control characters are seen (which will cause _$c(...) to be added) ; we will surround string with double-quotes before returning. n namezwrlen,namezwr,namedisplen,namedisp,ch,quotestate,starti,i,seenquotestate3 s namezwr=$zwrite(name) ; this will convert all control characters to $c()/$zc() notation ; But $zwrite will introduce more double-quotes than we want to display; so remove them ; e.g. namezwr = "MODELNUM("""_$C(0)_""":"""")" s namezwrlen=$zl(namezwr),namedisp="",doublequote="""" s namedisp="",namedisplen=0,quotestate=0 f i=1:1:namezwrlen s ch=$ze(namezwr,i) d . i (quotestate=0) d q . . i (ch=doublequote) s quotestate=1,starti=i+1 q . . ; We expect ch to be "$" here . . s quotestate=3 . i (quotestate=1) d q . . i ch'=doublequote q . . s quotestate=2 s namedisp=namedisp_$ze(namezwr,starti,i-1),namedisplen=namedisplen+(i-starti),starti=i+1 q . i (quotestate=2) d q . . ; At this point ch can be either doublequote or "_" . . s quotestate=$s(ch=doublequote:1,1:0) . . i ch="_" d q . . . i (($ze(namedisp,namedisplen)'=doublequote)!($ze(namedisp,namedisplen-1)=doublequote)) d q . . . . s starti=(i-1) ; include previous double-quote . . . ; remove extraneous ""_ before $c() . . . s namedisp=$ze(namedisp,1,namedisplen-1),namedisplen=namedisplen-1,starti=i+1 . i (quotestate=3) d q . . s seenquotestate3=1 . . i (ch=doublequote) s quotestate=1 q . . i ((ch="_")&($ze(namezwr,i+1,i+3)=(doublequote_doublequote_doublequote))&($ze(namezwr,i+4)'=doublequote)) d q . . . ; remove extraneous _"" after $c() . . . s namedisp=namedisp_$ze(namezwr,starti,i-1),namedisplen=namedisplen+(i-starti),starti=i+4,quotestate=1,i=i+3 q i addquote&$d(seenquotestate3) s namedisp=doublequote_namedisp_doublequote ; 2 and 3 are the only terminating states; check that. that too 3 only if addquote is 1. ; ASSERT : i '((quotestate=2)!(addquote&(quotestate=3))) s $etrap="zg 0" zsh "*" zhalt 1 q namedisp namedisplaycalc:(name) ; if name is subscripted, make sure control characters are displayed in $c() notation n namedisplen,namedisp i +$g(nams(name,"NSUBS"))=0 s namsdisp(name)=name q ; unsubscripted case; return right away s namedisp=$$namedisp(name,0) s namsdisp(name)=namedisp,namedisplen=$zwidth(namedisp) i namedispmaxlenbsiz; assert(MAX_BLK_SZ >= bsiz); if (SIZEOF(blk_hdr) > bsiz) { /* Input block size is lower than this function can handle. Return right away. * See comment in wcs_wtstart.c against similar assert (as below) for when this is possible. */ assert(0 == bsiz); assert(gtm_white_box_test_case_enabled && ((WBTEST_CRASH_SHUTDOWN_EXPECTED == gtm_white_box_test_case_number) || (WBTEST_MURUNDOWN_KILLCMT06 == gtm_white_box_test_case_number))); return; } assert(GDSVCURR == gds_blk_src->bver); assert(0 == ((long)gds_blk_trg & 0x7)); /* Buffer alignment checks (8 byte) */ assert(0 == ((long)gds_blk_src & 0x7)); trg_p = (sm_uc_ptr_t)gds_blk_trg + SIZEOF(v15_blk_hdr); src_p = (sm_uc_ptr_t)gds_blk_src + SIZEOF(blk_hdr); tn = gds_blk_src->tn; assert((MAX_TN_V4 >= tn) || dse_running); levl = gds_blk_src->levl; movesize = bsiz - SIZEOF(blk_hdr); if ((sm_uc_ptr_t)gds_blk_trg != (sm_uc_ptr_t)gds_blk_src) { /* Normal case, downgrade is to a new buffer. Our simple check is quicker than just always doing memmove() would be. But assert they are at least one block away just in case... */ DEBUG_ONLY( if ((sm_uc_ptr_t)gds_blk_trg > (sm_uc_ptr_t)gds_blk_src) assert((sm_uc_ptr_t)gds_blk_trg >= ((sm_uc_ptr_t)gds_blk_src + bsiz)); else assert((sm_uc_ptr_t)gds_blk_src >= ((sm_uc_ptr_t)gds_blk_trg + bsiz)); ) memcpy(trg_p, src_p, movesize); } else memmove(trg_p, src_p, movesize); gds_blk_trg->bsiz = bsiz - SPACE_NEEDED; gds_blk_trg->levl = levl; v15tn = (v15_trans_num) tn; gds_blk_trg->tn = v15tn; } fis-gtm-V7.0-005/sr_port/gds_blk_downgrade.h0000755000032200000250000000143514342376331017661 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2021 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. * * * ****************************************************************/ #ifndef GDS_BLK_UPGRADE_INCLUDED #define GDS_BLK_UPGRADE_INCLUDED void gds_blk_downgrade(v15_blk_hdr_ptr_t gds_blk_trg, blk_hdr_ptr_t gds_blk_src); #define IS_GDS_BLK_DOWNGRADE_NEEDED(ondskblkver) FALSE /* (GDSV7 <= (ondskblkver)) if v7 -> V6 downgrade were to exist */ #endif fis-gtm-V7.0-005/sr_port/gds_blk_upgrade.c0000755000032200000250000001150114342376331017324 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2021 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_string.h" #include "gdsroot.h" #include "v15_gdsroot.h" #include "gdsbml.h" #include "gdsblk.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsdbver.h" #include "gds_blk_upgrade.h" #include "iosp.h" #include "copy.h" #include "cdb_sc.h" #include "gdskill.h" #include "muextr.h" #include "mupip_reorg.h" #define SPACE_NEEDED (SIZEOF(blk_hdr) - SIZEOF(v15_blk_hdr)) GBLREF boolean_t gtm_blkupgrade_override; GBLREF sgmnt_data_ptr_t cs_data; GBLREF uint4 gtm_blkupgrade_flag; /* control whether dynamic upgrade is attempted or not */ GBLREF unsigned char rdfail_detail; error_def(ERR_DYNUPGRDFAIL); error_def(ERR_GTMCURUNSUPP); /* REORG encrypt does not pass the ondsk_blkver down through dsk_read, so update it only when provided by the caller */ #define SET_ON_DISK_BLKVER_IF_NEEDED(PTR, VER) if (NULL != (PTR)) *(PTR) = VER; int4 gds_blk_upgrade(sm_uc_ptr_t gds_blk_src, sm_uc_ptr_t gds_blk_trg, int4 blksize, enum db_ver *ondsk_blkver) { boolean_t apply_offset; blk_hdr_ptr_t bp, v6bp; block_id blkid; int blks_created, lvls_increased; /* TODO: share these back to mupip_upgrade for reporting */ int4 sz_needed, *rec_offsets, num_recs, index; rec_hdr_ptr_t rp, rtop; v15_blk_hdr_ptr_t v15bp; v15_trans_num v15tn; uint4 v15bsiz, v15levl; assert(gds_blk_src); assert(gds_blk_trg); assert(0 == ((long)gds_blk_src & 0x7)); /* code assumes 8 byte alignment */ assert(0 == ((long)gds_blk_trg & 0x7)); v6bp = (blk_hdr_ptr_t)gds_blk_src; bp = (blk_hdr_ptr_t)gds_blk_trg; if (GDSV7 == v6bp->bver) { SET_ON_DISK_BLKVER_IF_NEEDED(ondsk_blkver, GDSV7); return SS_NORMAL; } /* Should be a V[56] block here */ assert(GDSV6 == v6bp->bver); if ((0 == v6bp->levl) || (LCL_MAP_LEVL == v6bp->levl)) { /* TODO: better done in dsk_read? directory tree lvl 0 blocks pre-upgraded so must be data block or local bitmap */ memmove(gds_blk_trg, gds_blk_src, v6bp->bsiz); bp->bver = GDSV7; SET_ON_DISK_BLKVER_IF_NEEDED(ondsk_blkver, GDSV7); return SS_NORMAL; } /* Unless modified, the block is not getting upgraded */ SET_ON_DISK_BLKVER_IF_NEEDED(ondsk_blkver, GDSV6); /* TODO: fix call below; all work done by mu_split or a new version of it ? */ if (cdb_sc_normal != (rdfail_detail = mu_split(v6bp->levl, 0, 0, &blks_created, &lvls_increased))) rp = (rec_hdr_ptr_t)(v6bp + SIZEOF(blk_hdr)); rtop = (rec_hdr_ptr_t)(v6bp + v6bp->bsiz); rec_offsets = malloc((blksize/bstar_rec_size(BLKID_32)) * SIZEOF(int4)); /* TODO: change to high water marking */ apply_offset = (GDSV6p == bp->bver); /* does block need offset master map move? */ num_recs = 0; while (rp < rtop) /* TODO: should this loop apply offsets? */ { /* walk the records to find how many, and hence how much space we needed */ rec_offsets[num_recs++] = rp - (rec_hdr_ptr_t)v6bp; rp += rp->rsiz; } sz_needed = num_recs * 4; /* Need an extra 4-bytes per record to expand block pointers */ if (v6bp != bp) { /* The src and trg are different buffers, so copy the block to the target buffer */ memcpy(gds_blk_trg, gds_blk_src, blksize); } assert((sz_needed + v6bp->bsiz) <= blksize); /* mu_split should have ensured this */ #ifdef notnow if ((sz_needed + v6bp->bsiz) > blksize) { /* Would exceed maximum block size. Can't dynamically upgrade. Apply offset if needed and return. */ if (apply_offset) { rp = (rec_hdr_ptr_t)(bp + SIZEOF(blk_hdr)); rtop = (rec_hdr_ptr_t)(bp + bp->bsiz); while (rp < rtop) { READ_BLK_ID(BLKID_32, &blkid, (sm_uc_ptr_t)(rp + rp->rsiz - SIZEOF_BLK_ID(BLKID_32))); blkid -= cs_data->offset; WRITE_BLK_ID(BLKID_32, blkid, (sm_uc_ptr_t)(rp + rp->rsiz - SIZEOF_BLK_ID(BLKID_32))); rp += rp->rsiz; } bp->bver = GDSV6m; /* Set the OFFSET_FLAG since the block is staying V6 format */ } return SS_NORMAL; } #endif for (index = (num_recs - 1); index >= 0; index--) { /* Extend pointers to 64-bit and apply offset if needed */ sz_needed -= 4; rp = (rec_hdr_ptr_t)(bp + rec_offsets[index]); READ_BLK_ID(BLKID_32, &blkid, (sm_uc_ptr_t)(rp + rp->rsiz - SIZEOF_BLK_ID(BLKID_32))); blkid -= (apply_offset ? cs_data->offset : 0); rp->rsiz += 4; WRITE_BLK_ID(BLKID_64, blkid, (sm_uc_ptr_t)(rp + rp->rsiz - SIZEOF_BLK_ID(BLKID_64))); memmove((rp + sz_needed), rp, rp->rsiz); } bp->bver = GDSV7m; bp->bsiz += (num_recs * 4); free(rec_offsets); rec_offsets = NULL; return SS_NORMAL; } fis-gtm-V7.0-005/sr_port/gds_blk_upgrade.h0000755000032200000250000000621214342376331017334 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2021 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 "gdsblk.h" #ifndef GDS_BLK_UPGRADE_INCLUDED #define GDS_BLK_UPGRADE_INCLUDED #ifdef DEBUG_DB_UPGRADE /* define to get output for debugging */ #define DBGUPGRADE(X) #else #define DBGUPGRADE(X) #endif #define UPGRADE_IF_NEEDED 0 /* default */ #define UPGRADE_NEVER 1 #define UPGRADE_ALWAYS 2 int4 gds_blk_upgrade(sm_uc_ptr_t gds_blk_src, sm_uc_ptr_t gds_blk_trg, int4 bsiz, enum db_ver *ondsk_blkver); GBLREF uint4 gtm_blkupgrade_flag; /* control whether dynamic upgrade is attempted or not */ GBLREF boolean_t dse_running; static inline void blk_ptr_adjust(sm_uc_ptr_t buff, block_id offset) { /* buff contains a prior index block needing its offset adjusted */ block_id blk_num, *blk_ptr; sm_uc_ptr_t blkEnd, recBase; unsigned short temp_ushort; blkEnd = buff + ((blk_hdr_ptr_t)buff)->bsiz; assert(IS_64_BLK_ID(buff) == FALSE); DBG_VERIFY_ACCESS(blkEnd - 1); for (recBase = buff + SIZEOF(blk_hdr) ; recBase < blkEnd; recBase += ((rec_hdr_ptr_t)recBase)->rsiz) { /* iterate through block updating pointers with the offset */ if (blkEnd <= (recBase + SIZEOF(rec_hdr))) { assert(FALSE); buff = NULL; } GET_USHORT(temp_ushort, &(((rec_hdr_ptr_t)recBase)->rsiz)); /* collation info in directory tree which was already converted, so following is safe */ blk_ptr = (block_id *)(recBase + (int4)temp_ushort - SIZEOF_BLK_ID(FALSE)); GET_BLK_ID_32(blk_num, blk_ptr); PUT_BLK_ID_32(blk_ptr, blk_num - offset); } } /* The following macro was gutted in V7+ as dsk_read uses the inline fuction above to adjst block_ids, but does not actually * upgrade block_id fileds from 4 to 8 bytes, as managing that as part of the read was deemed to complex and not necessary * The macro remains in mur_blocks_free.c as a marker for possible future conversions and the text below retained for guidance * * See if block needs to be converted to current version. Assume buffer is at least short aligned. * Note: csd->fully_upgraded is not derived within the macro but instead passed in as a parameter to ensure whichever * function (dsk_read currently) references this does that once and copies the value into a local variable that is used * in all further usages. This way multiple usages are guaranteed to see the same value. Using csd->fully_upgraded in * each of those cases could cause different values to be seen (since csd can be concurrently updated). */ #define GDS_BLK_UPGRADE_IF_NEEDED(blknum, srcbuffptr, trgbuffptr, curcsd, ondskblkver, upgrdstatus, fully_upgraded) \ { \ if (NULL != (void *)(ondskblkver)) \ *(ondskblkver) = ((blk_hdr_ptr_t)(srcbuffptr))->bver; \ upgrdstatus = SS_NORMAL; \ } #endif fis-gtm-V7.0-005/sr_port/gds_map_moved.c0000755000032200000250000000663714342376331017032 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "gds_map_moved.h" #include "hashtab_mname.h" GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF gv_namehead *gv_target_list; void gds_map_moved(sm_uc_ptr_t new_base, sm_uc_ptr_t old_base, sm_uc_ptr_t old_top, size_t mmap_sz) { int hist_index; sm_long_t adj; srch_hist *hist, *hist1, *hist2; gd_region *baseDBreg, *reg; gv_namehead *gvt; sgmnt_addrs *baseDBcsa, *csa; sm_uc_ptr_t buffaddr, gvstats_rec_p; csa = cs_addrs; assert(csa->now_crit); assert(cs_data == csa->hdr); assert((NULL == csa->sgm_info_ptr) || (csa->hdr == csa->sgm_info_ptr->tp_csd)); assert(csa->bmm == MM_ADDR(cs_data)); assert(csa->ti == &cs_data->trans_hist); csa->db_addrs[1] = new_base + mmap_sz - 1; /* The following adjustment needs to be done only if new_base is different from old_base */ if (new_base == old_base) return; adj = (sm_long_t)(new_base - old_base); assert(0 != adj); /* Scan the list of gvts allocated by this process in its lifetime and see if they map to the current csa. * If so adjust their clues (in the histories) as appropriate to reflect the remapping of the database file. */ for (gvt = gv_target_list; NULL != gvt; gvt = gvt->next_gvnh) { if ((csa == gvt->gd_csa) && (0 < gvt->clue.end)) { hist1 = &gvt->hist; hist2 = gvt->alt_hist; for (hist = hist1; (NULL != hist); hist = (hist == hist1) ? hist2 : NULL) { for (hist_index = 0; (ARRAYSIZE(hist->h) > hist_index) && (HIST_TERMINATOR != hist->h[hist_index].blk_num); hist_index++) { assert(MAX_BT_DEPTH >= hist_index); buffaddr = hist->h[hist_index].buffaddr; if ((old_base <= buffaddr) && (old_top > buffaddr)) hist->h[hist_index].buffaddr += adj; else if ((hist == hist2) && (0 < gvt->clue.end)) { /* alt_hist is not updated when clue is set so the buffaddr can * point to a prior instance of the file's mapping. So, reset alt_hist. */ hist->h[hist_index].blk_num = HIST_TERMINATOR; } } } } } reg = gv_cur_region; assert(&FILE_INFO(reg)->s_addrs == csa); if (IS_STATSDB_REGNAME(reg)) { /* This is a statsdb. Adjust the baseDB's gvstats_rec_p pointer to the stats record in this statsdb */ STATSDBREG_TO_BASEDBREG(reg, baseDBreg); assert(baseDBreg->open); if (baseDBreg->open) /* be safe in pro even though we have an assert above */ { baseDBcsa = &FILE_INFO(baseDBreg)->s_addrs; gvstats_rec_p = (sm_uc_ptr_t)baseDBcsa->gvstats_rec_p; if ((old_base <= gvstats_rec_p) && (old_top > gvstats_rec_p)) { gvstats_rec_p += adj; baseDBcsa->gvstats_rec_p = (gvstats_rec_t *)gvstats_rec_p; } } } return; } fis-gtm-V7.0-005/sr_port/gds_map_moved.h0000755000032200000250000000121614342376331017023 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ #ifndef GDS_MAP_MOVED_INCLUDED #define GDS_MAP_MOVED_INCLUDED void gds_map_moved(sm_uc_ptr_t new_base, sm_uc_ptr_t old_base, sm_uc_ptr_t old_top, size_t mmap_sz); #endif /* GDS_MAP_MOVED_INCLUDED */ fis-gtm-V7.0-005/sr_port/gds_rundown.h0000644000032200000250000000331214342376331016544 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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. * * * ****************************************************************/ #ifndef GDS_RUNDOWN_INCLUDED #define GDS_RUNDOWN_INCLUDED #define CLEANUP_UDI_FALSE FALSE #define CLEANUP_UDI_TRUE TRUE int4 gds_rundown(boolean_t cleanup_udi); #define CAN_BYPASS(SEMVAL, CSA, INST_IS_FROZEN) \ (INST_IS_FROZEN || FROZEN_CHILLED(CSA) \ || (IS_GTM_IMAGE && (CSA)->hdr->mumps_can_bypass && (PROC_FACTOR * (num_additional_processors + 1) < SEMVAL)) \ || (((2 * DB_COUNTER_SEM_INCR) < SEMVAL) && (IS_LKE_IMAGE || IS_DSE_IMAGE))) #define CANCEL_DB_TIMERS(region, csa, cancelled_dbsync_timer) \ { \ if (csa->timer) \ { \ cancel_timer((TID)region); \ if (NULL != csa->nl) \ { \ DECR_CNT(&csa->nl->wcs_timers, &csa->nl->wc_var_lock); \ REMOVE_WT_PID(csa); \ } \ csa->canceled_flush_timer = TRUE; \ csa->timer = FALSE; \ } \ if (csa->dbsync_timer) \ { \ CANCEL_DBSYNC_TIMER(csa); \ cancelled_dbsync_timer = TRUE; \ } \ } /* A multiplicative factor to the # of processors used in determining if a GT.M process in gds_rundown can bypass semaphores */ #ifdef DEBUG # define PROC_FACTOR 2 #else # define PROC_FACTOR 20 #endif #endif /* GDS_RUNDOWN_INCLUDED */ fis-gtm-V7.0-005/sr_port/gdsbgtr.h0000755000032200000250000000232114342376331015651 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2016 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. * * * ****************************************************************/ /* Define macros to keep statistics of certain parts of code we pass * through, some only if we are in debug mode. * * Although the incremented counters are generally in shared storage, * we will not do interlock adds to them because even though there * may be some interference, most will succeed and we are only looking * for trends from these numbers anyway, not exact counts. */ #define BG_TRACE_PRO_ANY(C, X) {C->hdr->X##_cntr++; C->hdr->X##_tn = C->hdr->trans_hist.curr_tn ;} #define BG_TRACE_PRO(Q) BG_TRACE_PRO_ANY(cs_addrs, Q) #ifdef DEBUG #define BG_TRACE_ANY(C, X) BG_TRACE_PRO_ANY(C, X) #define BG_TRACE(Q) BG_TRACE_ANY(cs_addrs, Q) #else #define BG_TRACE_ANY(C, X) #define BG_TRACE(Q) #endif fis-gtm-V7.0-005/sr_port/gdsblk.h0000755000032200000250000002204014342376331015463 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #ifndef GDSBLK_H_INCLUDED #define GDSBLK_H_INCLUDED /* gdsblk.h */ #include #include "gdsdbver.h" #define BML_LEVL ((unsigned char)-1) #define CST_BSIZ(b) ((b)->bsiz - SIZEOF(blk_hdr)) #define CST_RSIZ(r) ((r)->rsiz - SIZEOF(rec_hdr)) #define CST_TOR(r) ((uchar_ptr_t)(r) + (r)->rsiz) #define CST_TOB(b) ((uchar_ptr_t)(b) + (b)->bsiz) #define CST_RIB(r,b) ((uchar_ptr_t)(r) + (r)->rsiz <= CST_TOB(b)) #define CST_1ST_REC(b) ((rec_hdr_ptr_t)((uchar_ptr_t)(b) + SIZEOF(blk_hdr))) #define CST_NXT_REC(r) ((uchar_ptr_t)(r) + (r)->rsiz) #define CST_BOK(r) ((uchar_ptr_t)(r) + SIZEOF(rec_hdr)) #define CST_USAR(b, r) ((b)->bsiz - ((uchar_ptr_t(r) + (r)->rsiz - (uchar_ptr_t)(b))) #define CST_KSIZ (gv_curr_key->end - gv_curr_key->base + 1) #define IS_LEAF(b) (0 == ((blk_hdr_ptr_t)(b))->levl) #define IS_BML(b) (BML_LEVL == ((blk_hdr_ptr_t)(b))->levl) #define GAC_RSIZE(rsize,r,tob) if ((uchar_ptr_t)(r) + ((rsize) = ((rec_hdr_ptr_t)(r))->rsiz) > tob) return(-1) #define MIN_DATA_SIZE 1 + 2 /* 1 byte of key + 2 nulls for terminator */ #define MAX_EXTN_COUNT 1048575 #define MIN_EXTN_COUNT 0 #define STDB_ALLOC_MAX 8388607 /* Based on JNL_ALLOC_MAX */ #define STDB_ALLOC_MIN 128 /* 128 Blocks */ #define MAX_DB_BLK_SIZE ((1 << 16) - 512) /* 64Kb - 512 (- 512 to take care of VMS's max I/O capabilities) */ /* Note: EVAL_CMPC not to be confused with the previously existing GET_CMPC macro in mu_reorg.h! * The maximum key size in V5 was 255 bytes, including the two null bytes at the end. Two distinct keys * could have a common prefix of at most 252.The 255 limit was imposed because the record header was * 3 bytes and had only 1 byte for compression count. But on UNIX the record header is actually 4 bytes * (see pragmas below), leaving an unused filler byte. To accommodate larger keys while * maintaining compatibility with V5, we can overload the previously unused values of cmpc * to get compression counts of up to 1020, which will support keys up to 1023 bytes. * As in V5 compression counts of between 0 and 252, inclusive, are stored in the first byte. * rp->cmpc values of 253, 254, and 255 indicate information is stored in the second byte. * * There are several constants used below. They are very specific and ONLY used here. We don't #define them * because that implies you can change one and the logic will still work. But that's not the case. * 253 --- cmpc value just beyond the highest possible in V5 (252). 253 = OLD_MAX_KEY_SZ - two terminators. * 256 --- Number of distinct compression counts that can be stored when cmpc is either 253, 254, or 255. * Corresponds to the one extra byte, which can store 256 values. * 0x03FF --- Equals 1023. We want to avoid having EVAL_CMPC return huge values due to concurrent changes * to block contents. This limits the result, preventing unintentionally large memcpy's in e.g. gvcst_put. */ #ifdef UNIX # define EVAL_CMPC(RP) ((253 > (RP)->cmpc) \ ? (RP)->cmpc \ : (253 + ((RP)->cmpc - 253) * 256 + (int)(RP)->cmpc2) & 0x03FF) # define EVAL_CMPC2(RP, VAR) \ { /* Usage note: make sure VAR is an int */ \ VAR = (RP)->cmpc; \ if (253 <= VAR) \ VAR = (253 + ((VAR) - 253) * 256 + (int)(RP)->cmpc2) & 0x03FF; \ } # define SET_CMPC(RP, VAL) \ { \ int lcl_cmpc_val; \ \ lcl_cmpc_val = (VAL); \ if (253 > lcl_cmpc_val) \ (RP)->cmpc = (unsigned char)lcl_cmpc_val; \ else \ { \ (RP)->cmpc = (unsigned char)(253 + (lcl_cmpc_val - 253) / 256); \ (RP)->cmpc2 = (unsigned char)((lcl_cmpc_val - 253) & 0xFF); \ } \ } #else # define EVAL_CMPC(RP) ((int)(RP)->cmpc) # define EVAL_CMPC2(RP, VAR) \ { /* Usage note: make sure VAR is an int */ \ VAR = (RP)->cmpc; \ } # define SET_CMPC(RP, VAL) \ { \ (RP)->cmpc = (unsigned char)(VAL); \ } #endif /* The following macro picks up a record from a block using PREC as the pointer to the record and validates * that the record length, NRECLEN, meets base criteria (is not 0 and does not exceed the top of the * block as identified by PTOP) and return NBLKID on the assumption the block is an index block. * LONG_BLK_ID tells whether the block that PREC is part of uses 32 or 64 bit block_ids. */ #define GET_AND_CHECK_RECLEN(STATUS, NRECLEN, PREC, PTOP, NBLKID, LONG_BLK_ID) \ { \ sm_uc_ptr_t PVAL; \ \ STATUS = cdb_sc_normal; \ GET_USHORT(NRECLEN, &((rec_hdr_ptr_t)PREC)->rsiz); \ if (NRECLEN == 0) \ STATUS = cdb_sc_badoffset; \ else if ((PREC + NRECLEN) > PTOP) \ STATUS = cdb_sc_blklenerr; \ else \ { \ PVAL = PREC + NRECLEN - SIZEOF_BLK_ID(LONG_BLK_ID); \ READ_BLK_ID(LONG_BLK_ID, &NBLKID, PVAL); \ } \ } /* The following macro picks up a the level from a block using PBLKBASE as the pointer to the header and validates * that the block level, NLEVL, meets base criteria (is not greater than MAX_BT_DEPTH and matches the expected * level as identified by DESIREDLVL) */ #define GET_AND_CHECK_LEVL(STATUS, NLEVL, DESIREDLVL, PBLKBASE) \ { \ STATUS = cdb_sc_normal; \ NLEVL = ((blk_hdr_ptr_t)PBLKBASE)->levl; \ if (MAX_BT_DEPTH < (int)NLEVL) \ STATUS = cdb_sc_maxlvl; \ else if (ANY_ROOT_LEVL == DESIREDLVL) \ { \ if (0 == (int)NLEVL) \ STATUS = cdb_sc_badlvl; \ } else if (DESIREDLVL !=(int)NLEVL) \ STATUS = cdb_sc_badlvl; \ } #if defined(__alpha) && defined(__vms) # pragma member_alignment save # pragma nomember_alignment #endif /* Version 4 block header */ typedef struct { unsigned short bsiz; /* block size */ unsigned char levl; /* block level. level 0 is data level. level 1 is * first index level. etc. */ uint4 tn; /* transaction number when block was written */ } v15_blk_hdr; /* Current block header */ typedef struct { unsigned short bver; /* block version - overlays V4 block size */ unsigned char filler; unsigned char levl; /* block level. level 0 is data level. level 1 is * first index level. etc. */ unsigned int bsiz; /* number of currently used bytes in the block */ trans_num tn; /* transaction number when block was written */ } blk_hdr; typedef struct { unsigned short rsiz; /* size of the record in bytes */ unsigned char cmpc; /* compression count of the record that allows for values up to 252 * (See EVAL_CMPC comments at beginning of file for explanation) */ # ifdef UNIX unsigned char cmpc2; /* extra byte allows compression count up to 1020 */ # endif } rec_hdr; #if defined(__alpha) && defined(__vms) # pragma member_alignment restore #endif /* Define pointer types to above structures */ #ifdef DB64 # ifdef __osf__ # pragma pointer_size(save) # pragma pointer_size(long) # else # error UNSUPPORTED PLATFORM # endif #endif typedef v15_blk_hdr *v15_blk_hdr_ptr_t; /* From jnl format 15 used in last GT.M V4 version */ typedef blk_hdr *blk_hdr_ptr_t; typedef rec_hdr *rec_hdr_ptr_t; #ifdef DB64 # ifdef __osf__ # pragma pointer_size(restore) # endif #endif /* Macros/Inline functions for bstar records */ #define BSTAR_REC_SIZE_32 INTCAST((SIZEOF(rec_hdr) + SIZEOF(block_id_32))) #define BSTAR_REC_SIZE_64 INTCAST((SIZEOF(rec_hdr) + SIZEOF(block_id_64))) #define BSTAR_REC_SIZE BSTAR_REC_SIZE_64 static inline int bstar_rec_size(boolean_t long_blk_id) { return (long_blk_id ? BSTAR_REC_SIZE_64 : BSTAR_REC_SIZE_32); } static inline void bstar_rec(sm_uc_ptr_t r, boolean_t long_blk_id) { ((rec_hdr_ptr_t)r)->rsiz = (unsigned short)bstar_rec_size(long_blk_id); SET_CMPC((rec_hdr_ptr_t)r, 0); } #define MAX_RESERVE_B(X, Y) ((X)->blk_size - (X)->max_key_size - SIZEOF(blk_hdr) - SIZEOF(rec_hdr) \ - SIZEOF_BLK_ID(Y) - bstar_rec_size(Y)) /* anything past key can span */ #define CHKRECLEN(r,b,n) ((unsigned int)((n) + (uchar_ptr_t)(r) - (uchar_ptr_t)(b)) <= (unsigned int)((blk_hdr_ptr_t)(b))->bsiz) /********************************************************************* read record size from REC_BASE (temp_ushort must be defined) *********************************************************************/ #define GET_RSIZ(REC_SIZE, REC_BASE) \ GET_USHORT(temp_ushort, &(((rec_hdr_ptr_t)(REC_BASE))->rsiz)); \ REC_SIZE = temp_ushort int4 bm_find_blk(int4 hint, sm_uc_ptr_t base_addr, int4 total_bits, boolean_t *used); void bm_setmap(block_id bml, block_id blk, int4 busy); void bml_newmap(blk_hdr_ptr_t ptr, uint4 size, trans_num curr_tn, enum db_ver ondsk_blkver); /* End of gdsblk.h */ #endif fis-gtm-V7.0-005/sr_port/gdsblkops.h0000755000032200000250000003045014342376331016211 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef GDSBLK_OPS_H_INCLUDED #define GDSBLK_OPS_H_INCLUDED /* Block segment array holds the list of memcpys needed to build the updated block. The first entry in the array holds the length of the updated block and the address of the last entry in the array that holds valid data. The arrays are processed at t_end time, and the copies are done starting with the last element in the array */ /* HEADER-FILE-DEPENDENCIES : min_max.h */ typedef struct { sm_uc_ptr_t addr; sm_ulong_t len; } blk_segment; #define BLK_SEG_ARRAY_SIZE 20 /* although kills may have MAX_BT_DEPTH * 2 - 1 elements, each element is limited to key_size and gives a smaller max than puts */ /* *************************************************************************** * The following is the splitup of the calculation of maximum-update-array-size for one non-TP action (for a PUT) * * BLK_INIT, BLK_FINI space ---> CDB_CW_SET_SIZE * (BLK_SEG_ARRAY_SIZE * SIZEOF(blk_segment)) * BLK_ADDR leaf-level space---> 2 * cs_data->blk_size (for current and new sibling) * BLK_ADDR index-level space--> (MAX_BT_DEPTH - 1) * (2 * (MAX_KEY_SZ + SIZEOF(rec_hdr) + SIZEOF(block_id)) + SIZEOF(rec_hdr)) * 2 extra space ---> cs_data->blk_size + BSTAR_REC_SIZE (needed in case of global-variable creation) * Bitmap BLK_ADDR space ---> (MAX_BT_DEPTH + 1) * (SIZEOF(block_id) * (BLKS_PER_LMAP + 1)) * * In the above calculations SIZEOF(block_id) and BSTAR_REC_SIZE are placeholders for the Macro/Function calls * that will return different values depending on whether the DB is V6 or V7 */ #define UPDATE_ELEMENT_ALIGN_SIZE 8 #define UPDATE_ARRAY_ALIGN_SIZE (1 << 14) /* round up the update array to a 16K boundary */ #define MAX_BITMAP_UPDATE_ARRAY_SIZE(csd) \ ((MAX_BT_DEPTH + 1) \ * ROUND_UP2(SIZEOF_BLK_ID(BLK_ID_32_VER < csd->desired_db_format) * (BLKS_PER_LMAP + 1), \ UPDATE_ELEMENT_ALIGN_SIZE)) #define MAX_NON_BITMAP_UPDATE_ARRAY_SIZE(csd) \ (CDB_CW_SET_SIZE * ROUND_UP2(BLK_SEG_ARRAY_SIZE * SIZEOF(blk_segment), UPDATE_ELEMENT_ALIGN_SIZE) \ + 2 * ROUND_UP2(csd->blk_size, UPDATE_ELEMENT_ALIGN_SIZE) \ + (MAX_BT_DEPTH - 1) * \ (3 * ROUND_UP2(SIZEOF(rec_hdr), UPDATE_ELEMENT_ALIGN_SIZE) \ + 2 * ROUND_UP2(SIZEOF_BLK_ID(BLK_ID_32_VER < csd->desired_db_format), UPDATE_ELEMENT_ALIGN_SIZE)\ + 2 * ROUND_UP2(MAX_KEY_SZ, 8)) \ + ROUND_UP2(csd->blk_size, UPDATE_ELEMENT_ALIGN_SIZE) \ + ROUND_UP2(bstar_rec_size(BLK_ID_32_VER < csd->desired_db_format), UPDATE_ELEMENT_ALIGN_SIZE)) #define UA_SIZE(X) (uint4)(X->max_update_array_size) #define UA_NON_BM_SIZE(X) (uint4)(X->max_non_bm_update_array_size) #define ENSURE_UPDATE_ARRAY_SPACE(space_needed) \ { \ GBLREF ua_list *first_ua, *curr_ua; \ GBLREF char *update_array, *update_array_ptr; \ GBLREF uint4 update_array_size, cumul_update_array_size; \ GBLREF uint4 dollar_tlevel; \ ua_list *tmpua; \ \ assert((0 != update_array_size) && (NULL != update_array)); \ if (ROUND_DOWN2(update_array + update_array_size - update_array_ptr, UPDATE_ELEMENT_ALIGN_SIZE) < (space_needed)) \ { /* the space remaining is too small for safety - chain on a new array */ \ assert((NULL != first_ua) && (NULL != curr_ua)); \ if ((NULL == curr_ua) || (NULL == curr_ua->next_ua)) \ { \ /* care should be taken to ensure things will work right even if malloc() errors out with ERR_MEMORY. \ * that is why multiple assignments are not done in a single line that has a malloc() call in it. \ * in addition, the field taking in the result of malloc() should be initialized to NULL before the \ * call. This is because tp_clean_up() (invoked in case of error handling) relies on the integrity of \ * this update array linked list in order to do its cleanup. Not following the above rules will cause \ * difficult-to-debug memory related problems (even corruption) */ \ tmpua = (ua_list *)malloc(SIZEOF(ua_list)); \ memset(tmpua, 0, SIZEOF(ua_list)); /* initialize tmpua->update_array and tmpua->next_ua to NULL */ \ /* it is important that all parameters in the MIN-MAX calculation below be unsigned numbers */ \ tmpua->update_array_size = MIN(MAX(cumul_update_array_size, (space_needed)), BIG_UA); \ tmpua->update_array = (char *)malloc(tmpua->update_array_size); \ /* update globals only after above mallocs succeed */ \ cumul_update_array_size += tmpua->update_array_size; \ if (NULL == curr_ua) /* in PRO, don't take chances, reset first_ua/curr_ua to newly created upd array */\ { \ /* if already in TP, we will lose all updates until now if we reset first_ua. do not proceed */ \ assertpro(!dollar_tlevel || (NULL == first_ua)); \ first_ua = curr_ua = tmpua; \ } else \ curr_ua = curr_ua->next_ua = tmpua; \ } else \ { /* No need to do malloc as curr_ua->next_ua could be REUSED */ \ curr_ua = curr_ua->next_ua; \ /* No need to reset cumul_update_array_size as no ua_list is deleted/added from/to the first_ua \ * linked list */ \ } \ assert((NULL != first_ua) && (NULL != curr_ua)); \ update_array_size = curr_ua->update_array_size; \ update_array = update_array_ptr = curr_ua->update_array; \ } \ } /* The following macro resets update_array_ptr to point to update_array. * This needs to be done before using any BLK_* macros as part of a non-TP transaction. */ #define RESET_UPDATE_ARRAY \ { \ GBLREF char *update_array, *update_array_ptr; \ \ assert(NULL != update_array); \ /* reset update_array to the start */ \ update_array_ptr = update_array; \ } /* the following macro does what RESET_UPDATE_ARRAY does and additionally does some integrity checks */ #define CHECK_AND_RESET_UPDATE_ARRAY \ { \ GBLREF uint4 dollar_tlevel; \ GBLREF unsigned char cw_set_depth; \ \ assert(!dollar_tlevel); /* TP should never use this update_array */ \ assert(0 == cw_set_depth); /* ensure we never reset an active update_array */ \ RESET_UPDATE_ARRAY; \ } /* *************************************************************************** * BLK_INIT(BNUM, ARRAY) allocates: * blk_segment ARRAY[BLK_SEG_ARRAY_SIZE] * at the next octaword-aligned location in the update array and sets * BNUM = &ARRAY[1] */ #define BLK_INIT(BNUM, ARRAY) \ { \ GBLREF char *update_array, *update_array_ptr; \ \ update_array_ptr = (char*)ROUND_UP2((INTPTR_T)update_array_ptr, UPDATE_ELEMENT_ALIGN_SIZE); \ (ARRAY) = (blk_segment*)update_array_ptr; \ update_array_ptr += (BLK_SEG_ARRAY_SIZE * SIZEOF(blk_segment)); \ assert((update_array + update_array_size) - update_array_ptr >= 0); \ (BNUM) = (ARRAY + 1); \ blk_seg_cnt = SIZEOF(blk_hdr); \ } /* *************************************************************************** * BLK_SEG(BNUM, ADDR, LEN) adds a new entry to the blk_segment array */ #define BLK_SEG(BNUM, ADDR, LEN) \ { \ sm_ulong_t lcl_len1; \ GBLREF uint4 dollar_tlevel; \ \ /* Note that name of len variable "lcl_len1" should be different \ * from the name used in the REORG_BLK_SEG macro as otherwise \ * the below assignment will leave lcl_len1 uninitialized. \ */ \ lcl_len1 = (LEN); \ /* The function "gvcst_blk_build" (which uses this update array to build the block) relies on "len" to \ * be positive. This macro is called from both non-TP and TP code. In non-TP, we know of a few callers \ * (dse etc.) that could pass in a negative length to the BLK_SEG macro. Those are okay since we are \ * guaranteed the validation (in t_end inside of crit) would catch this case and force a restart thus \ * avoiding a call to gvcst_blk_build. But in TP, validations happen before commit time in tp_hist and \ * it has a few optimizations where for globals with NOISOLATION turned ON, it allows validation to \ * succeed even if the block contents changed concurrently. This means update arrays with negative \ * lengths could find their way to gvcst_blk_build even after tp_hist. Since those lengths are passed \ * directly to memmove which treats it as an unsigned quantity, it means huge memmoves that are likely \ * to cause memory corruption and/or SIG-11. Therefore it is absolutely necessary that if we are in TP \ * the caller does not pass in a negative length. Assert that. \ */ \ assert(!dollar_tlevel || (0 <= (sm_long_t)lcl_len1)); \ (BNUM)->addr = (ADDR); \ (BNUM)->len = lcl_len1; \ blk_seg_cnt += (int)lcl_len1; \ assert((char *)BNUM - update_array_ptr < 0); \ (BNUM)++; \ } /* *************************************************************************** * BLK_FINI(BNUM,ARRAY) finishes the update array by * BNUM->addr = 0 * BNUM-- * if the blk_seg_cnt is within range, then * ARRAY[0].addr = BNUM (address of last entry containing data) * ARRAY[0].len = blk_seg_cnt (total size of all block segments) * and it returns the value of blk_seg_cnt, * otherwise, it returns zero and the caller should invoke t_retry */ #define BLK_FINI(BNUM,ARRAY) \ ( \ (BNUM--)->addr = (uchar_ptr_t)0, \ (blk_seg_cnt <= blk_size && blk_seg_cnt >= SIZEOF(blk_hdr)) \ ? (ARRAY)[0].addr = (uchar_ptr_t)(BNUM), (ARRAY)[0].len = blk_seg_cnt \ : 0 \ ) /* *************************************************************************** * BLK_ADDR(X,Y,Z) allocates a space of length Y in the update array * and sets pointer X (of type Z) to the beginning of that space */ #ifdef DEBUG #define BLK_ADDR(X,Y,Z) \ ( \ update_array_ptr = (char*)(((INTPTR_T)update_array_ptr + 7) & ~7), \ assert((update_array + update_array_size - Y) - update_array_ptr >= 0), \ (X) = (Z*)update_array_ptr, update_array_ptr += (INTPTR_T)Y \ ) #else #define BLK_ADDR(X,Y,Z) \ ( \ update_array_ptr = (char*)(((INTPTR_T)update_array_ptr + 7) & ~7), \ (X) = (Z*)update_array_ptr, update_array_ptr += (INTPTR_T)Y \ ) #endif /* ******************************************************************************** * * REORG_BLK_SEG(BNUM, ADDR, LEN, BLK_SRCH_STAT) is the same as the BLK_SEG macro except that it takes a private copy of the * input memory and adds that to the update array. This is necessary in case of MUPIP REORG for two operations (Coalesce and * Swap). In either cases, the contents of one block will rely on the contents of itself and another block. This is not * currently supported by t_end where the assumption is that all contents needed to build a buffer are available in that buffer * itself (this greatly simplifies the process of pinning of buffers in shared memory). To avoid cross-links to other buffers, * we need to take a copy of the other buffer's contents from shared memory into private memory before adding it to the update * array. This macro should be called only by MUPIP REORG. An assert has been added to that effect. * */ #define REORG_BLK_SEG(BNUM, ADDR, LEN, BLK_SRCH_STAT) \ { \ char *lcl_ptr; \ sm_ulong_t lcl_len; \ \ GBLREF boolean_t mu_reorg_process; \ \ assert(mu_reorg_process); \ lcl_len = (LEN); \ if ((0 > (sm_long_t)lcl_len) || ((blk_seg_cnt + lcl_len) > blk_size)) \ { \ assert(CDB_STAGNATE > t_tries); \ NONTP_TRACE_HIST_MOD(BLK_SRCH_STAT, t_blkmod_mu_clsce); \ return cdb_sc_blkmod; \ } \ BLK_ADDR(lcl_ptr, lcl_len, char); \ memcpy(lcl_ptr, (ADDR), lcl_len); \ BLK_SEG((BNUM), (sm_uc_ptr_t)lcl_ptr, lcl_len); \ } #endif fis-gtm-V7.0-005/sr_port/gdsbml.h0000755000032200000250000001271614342376331015476 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #define SIXTEEN_BLKS_FREE 0x55555555 #define FOUR_BLKS_FREE 0x55 #define THREE_BLKS_FREE 0x54 #define LCL_MAP_LEVL 0xFF #define BLK_BUSY 0x00 #define BLK_FREE 0x01 #define BLK_MAPINVALID 0x02 #define BLK_RECYCLED 0x03 #define BML_BITS_PER_BLK 2 #define THREE_BLKS_BITMASK 0x3F #define TWO_BLKS_BITMASK 0x0F #define ONE_BLK_BITMASK 0x03 #define BMP_EIGHT_BLKS_FREE 255 /* returns the bitmap status (BLK_BUSY|BLK_FREE|etc.) of the "blknum"th block within the local bitmap block "bp" in bml_status */ #define GET_BM_STATUS(bp, blknum, bml_status) \ { \ sm_uc_ptr_t ptr; \ \ ptr = ((sm_uc_ptr_t)(bp) + SIZEOF(blk_hdr) + ((blknum * BML_BITS_PER_BLK) / BITS_PER_UCHAR)); \ bml_status = (*ptr >> ((blknum * BML_BITS_PER_BLK) % BITS_PER_UCHAR)) & ((1 << BML_BITS_PER_BLK) - 1); \ } /* sets bitmap status (BLK_BUSY|BLK_FREE etc.) for the "blknum"th block within the local bitmap block "bp" from new_bml_status */ #define SET_BM_STATUS(bp, blknum, new_bml_status) \ { \ sm_uc_ptr_t ptr; \ \ assert(2 == BML_BITS_PER_BLK); \ ptr = ((sm_uc_ptr_t)(bp) + SIZEOF(blk_hdr) + ((blknum * BML_BITS_PER_BLK) / BITS_PER_UCHAR)); \ *ptr = (*ptr & ~(0x03 << ((blknum * BML_BITS_PER_BLK) % BITS_PER_UCHAR))) \ | ((new_bml_status & 0x03) << ((blknum * BML_BITS_PER_BLK) % BITS_PER_UCHAR)); \ } #define BM_MINUS_BLKHDR_SIZE(bplm) ((bplm) / (BITS_PER_UCHAR / BML_BITS_PER_BLK)) #define BM_SIZE(bplm) (SIZEOF(blk_hdr) + BM_MINUS_BLKHDR_SIZE(bplm)) #define VALIDATE_BM_BLK(blk, bp, csa, region, status) \ { \ error_def(ERR_DBBMLCORRUPT); /* BYPASSOK */ \ \ assert(BITS_PER_UCHAR % BML_BITS_PER_BLK == 0); /* assert this for the BM_MINUS_BLKHDR_SIZE macro */ \ if (IS_BITMAP_BLK(blk) && ((LCL_MAP_LEVL != (bp)->levl) || (BM_SIZE(csa->hdr->bplmap) != (bp)->bsiz)) \ UNIX_ONLY(DEBUG_ONLY(|| (gtm_white_box_test_case_enabled \ && (WBTEST_ANTIFREEZE_DBBMLCORRUPT == gtm_white_box_test_case_number))))) \ { \ send_msg_csa(CSA_ARG(csa) VARLSTCNT(9) ERR_DBBMLCORRUPT, 7, DB_LEN_STR(region), \ &(blk), (bp)->bsiz, (bp)->levl, &(bp)->tn, &csa->ti->curr_tn); \ status = FALSE; \ } else \ status = TRUE; \ } #define NO_FREE_SPACE -1 #define MAP_RD_FAIL -2 #define EXTEND_SUSPECT -3 #define FILE_EXTENDED -4 #define FINAL_RETRY_FREEZE_PROG -5 #define FINAL_RETRY_INST_FREEZE -6 #define GET_CDB_SC_CODE(gdsfilext_code, status) \ { \ if (MAP_RD_FAIL == gdsfilext_code) \ status = (enum cdb_sc)rdfail_detail; \ else if (EXTEND_SUSPECT == gdsfilext_code) \ status = (enum cdb_sc)cdb_sc_extend; \ else if (NO_FREE_SPACE == gdsfilext_code) \ status = cdb_sc_gbloflow; \ else if (FINAL_RETRY_FREEZE_PROG == gdsfilext_code) \ status = cdb_sc_needcrit; \ else if (FINAL_RETRY_INST_FREEZE == gdsfilext_code) \ status = cdb_sc_instancefreeze; \ } #define MASTER_MAP_BITS_PER_LMAP 1 #define DETERMINE_BML_FUNC_COMMON(FUNC, CS, CSA) \ MBSTART { \ FUNC = (CS->reference_cnt > 0) ? bml_busy : (CSA->hdr->db_got_to_v5_once ? bml_recycled : bml_free); \ } MBEND # define DETERMINE_BML_FUNC(FUNC, CS, CSA) \ MBSTART { \ GBLREF boolean_t mu_reorg_upgrd_dwngrd_in_prog; \ \ if (CSA->nl->trunc_pid) \ { /* A truncate is in progress. If we are acquiring a block, we need to update cnl->highest_lbm_with_busy_blk to \ * avoid interfering with the truncate. If we are truncate doing a t_recycled2free mini-transaction, we need to \ * select bml_free. \ */ \ if (cs->reference_cnt > 0) \ { \ FUNC = bml_busy; \ CSA->nl->highest_lbm_with_busy_blk = MAX(CS->blk, CSA->nl->highest_lbm_with_busy_blk); \ } else if (cs->reference_cnt < 0) \ { \ if (CSA->hdr->db_got_to_v5_once && (CSE_LEVEL_DRT_LVL0_FREE != CS->level)) \ FUNC = bml_recycled; \ else \ { /* always set the block as free when gvcst_bmp_mark_free a level-0 block in DIR tree; reset \ * level since t_end will call bml_status_check which has an assert for bitmap block level \ */ \ FUNC = bml_free; \ CS->level = LCL_MAP_LEVL; \ } \ } else /* cs->reference_cnt == 0 */ \ { \ if (CSA->hdr->db_got_to_v5_once && mu_reorg_upgrd_dwngrd_in_prog) \ FUNC = bml_recycled; \ else \ FUNC = bml_free; \ } \ } else /* Choose bml_func as it was chosen before truncate feature. */ \ DETERMINE_BML_FUNC_COMMON(FUNC, CS, CSA); \ } MBEND int4 bml_find_free(block_id hint, uchar_ptr_t base_addr, block_id total_bits); int4 bml_init(block_id bml); uint4 bml_busy(block_id setbusy, sm_uc_ptr_t map); uint4 bml_free(block_id setfree, sm_uc_ptr_t map); uint4 bml_recycled(block_id setfree, sm_uc_ptr_t map); fis-gtm-V7.0-005/sr_port/gdsbt.h0000644000032200000250000013511014342376331015320 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ #ifndef GDSBT_H #define GDSBT_H /* this requires gdsroot.h */ #include #ifdef MUTEX_MSEM_WAKE #ifdef POSIX_MSEM # include "gtm_semaphore.h" #else # include #endif #endif #include /* for _POSIX_HOST_NAME_MAX */ #if defined(SUNOS) && !defined(_POSIX_HOST_NAME_MAX) # include /* for MAXHOSTNAMELEN (Solaris 9) */ #endif #include "gvstats_rec.h" #define CR_NOTVALID (-1L) #define GTM64_WC_MAX_BUFFS (2*1024*1024)-1 /* to fit in an int4 */ #define WC_MAX_BUFFS 64*1024 #define WC_DEF_BUFFS 128 #define WC_MIN_BUFFS 64 #define MAX_LOCK_SPACE 262144 /* need to change these whenever global directory defaults change */ #define MIN_LOCK_SPACE 10 #define MAX_REL_NAME 36 #define MAX_MCNAMELEN 256 /* We do not support hostname truncation */ #if defined(_POSIX_HOST_NAME_MAX) # if MAX_MCNAMELEN <= _POSIX_HOST_NAME_MAX /* _POSIX_HOST_NAME_MAX excludes terminating NULL */ # error MAX_MCNAMELEN is not greater than _POSIX_HOST_NAME_MAX. # endif #elif defined(MAXHOSTNAMELEN) # if MAX_MCNAMELEN < MAXHOSTNAMELEN /* MAXHOSTNAMELEN includes terminating NULL */ # error MAX_MCNAMELEN is less than MAXHOSTNAMELEN. # endif #else # error _POSIX_HOST_NAME_MAX or MAXHOSTNAMELEN not defined. #endif #define GDS_LABEL_SZ 12 #define MAX_DB_WTSTARTS 2 /* Max number of "flush-timer driven" simultaneous writers in wcs_wtstart */ #define MAX_WTSTART_PID_SLOTS 4 * MAX_DB_WTSTARTS /* Max number of PIDs for wcs_wtstart to save */ #define MAX_KIP_PID_SLOTS 8 #define MAX_WT_PID_SLOTS 4 #define BT_FACTOR(X) (X) #define FLUSH_FACTOR(X) ((X)-(X)/16) #define BT_QUEHEAD (-2) #define BT_NOTVALID (-1) #define BT_MAXRETRY 3 #define BT_SIZE(X) ((((sgmnt_data_ptr_t)X)->bt_buckets + 1 + ((sgmnt_data_ptr_t)X)->n_bts) * SIZEOF(bt_rec)) /* parameter is *sgmnt_data*/ /* note that the + 1 above is for the th_queue head which falls between the hash table and the */ /* actual bts */ #define HEADER_UPDATE_COUNT 1024 #define LAST_WBOX_SEQ_NUM 1000 #define WC_UNBLOCK 0 #define WC_BLOCK_ONLY 1 #define WC_BLOCK_RECOVER 2 typedef struct { trans_num curr_tn; trans_num early_tn; trans_num last_mm_sync; /* Last tn where a full mm sync was done */ trans_num mm_tn; /* Used to see if CCP must update master map */ uint4 lock_sequence; /* Used to see if CCP must update lock section */ uint4 ccp_jnl_filesize; /* Passes size of journal file if extended */ volatile block_id total_blks; /* Placed here so can be passed to other machines on cluster */ volatile block_id free_blocks; } th_index; typedef struct { struct { sm_off_t fl; sm_off_t bl; /* self-relative queue entry */ } blkque, tnque; /* for block number hash, lru queue */ trans_num tn; /* transaction #*/ trans_num killtn; /* last transaction when this block was updated as part of an M-kill */ block_id blk; /* block #*/ int4 cache_index; bool flushing; /* buffer is being flushed after a machine switch on a cluster */ char filler[3]; /* maintain 8 byte alignment */ } bt_rec; /* block table record */ /* This structure is used to access the transaction queue. It points at all but the * first two longwords of a bt_rec. CAUTION: there is no such thing as a queue of * th_recs, they are always bt_recs, and the extra two longwords are always there */ typedef struct { struct { sm_off_t fl; sm_off_t bl; } tnque; trans_num tn; trans_num killtn; /* last transaction when this block was updated as part of an M-kill */ block_id blk; int4 cache_index; bool flushing; char filler[3]; /* maintain 8 byte alignment */ } th_rec; /* This structure is used to maintain all cache records. The BT queue contains * a history of those blocks that have been updated. */ /* * Definitions for GT.M Mutex Control * * See MUTEX.MAR for VMS functional implementation */ typedef struct { struct { sm_off_t fl, bl; } que; int4 pid; void *super_crit; /* * Make sure that the size of mutex_que_entry is a multiple of 8 bytes * for quadword alignment requirements of remqhi and insqti. */ int4 mutex_wake_instance; int4 filler1; /* for dword alignment */ #ifdef MUTEX_MSEM_WAKE # ifdef POSIX_MSEM sem_t mutex_wake_msem; /* Not two ints .. somewhat larger */ # else msemaphore mutex_wake_msem; /* Two ints (incidentally two int4s) */ # endif #endif } mutex_que_entry; typedef struct { struct { sm_off_t fl, bl; } que; global_latch_t latch; } mutex_que_head; typedef struct { FILL8DCL(uint4, crit_cycle, 1); global_latch_t semaphore; CACHELINE_PAD(8 + SIZEOF(global_latch_t), 2) /* 8 for the FILL8DCL */ FILL8DCL(latch_t, crashcnt, 3); global_latch_t crashcnt_latch; CACHELINE_PAD(8 + SIZEOF(global_latch_t), 4) /* 8 for the FILL8DCL */ compswap_time_field stuckexec; CACHELINE_PAD(SIZEOF(compswap_time_field), 5) FILL8DCL(latch_t, queslots, 6); CACHELINE_PAD(SIZEOF(latch_t) + SIZEOF(latch_t), 7) mutex_que_head prochead; CACHELINE_PAD(SIZEOF(mutex_que_head), 8) mutex_que_head freehead; CACHELINE_PAD(SIZEOF(mutex_que_head), 9) } mutex_struct; typedef struct { FILL8DCL(uint4, crit_cycle, 1); FILL8DCL(uint4, stuck_cycle, 2); pthread_mutex_t mutex; } pth_mutex_struct; typedef struct { int4 mutex_hard_spin_count; int4 mutex_sleep_spin_count; int4 mutex_spin_sleep_mask; /* mask for maximum spin sleep time */ int4 mutex_que_entry_space_size; /* total number of entries */ } mutex_spin_parms_struct; enum crit_ops { crit_ops_gw = 1, /* grab [write] crit */ crit_ops_rw, /* rel [write] crit */ crit_ops_nocrit /* did a rel_crit when now_crit flag was off */ }; typedef struct { caddr_t call_from; enum crit_ops crit_act; int4 epid; trans_num curr_tn; } crit_trace; typedef struct { sm_off_t cr_off; trans_num cr_tn; uint4 process_id; block_id blk; uint4 cycle; } dskread_trace; enum wcs_ops_trace_t { wcs_ops_flu1, wcs_ops_flu2, wcs_ops_flu3, wcs_ops_flu4, wcs_ops_flu5, wcs_ops_flu6, wcs_ops_flu7, wcs_ops_wtstart1, wcs_ops_wtstart2, wcs_ops_wtstart3, wcs_ops_wtstart4, wcs_ops_wtstart5, wcs_ops_wtstart6, wcs_ops_wtstart7, wcs_ops_wtstart8, wcs_ops_wtfini1, wcs_ops_wtfini2, wcs_ops_wtfini3, wcs_ops_wtfini4, wcs_ops_getspace1 }; typedef struct { trans_num db_tn; uint4 process_id; uint4 wc_in_free; uint4 wcs_active_lvl; uint4 wcs_wip_lvl; enum wcs_ops_trace_t type; block_id blk; sm_off_t cr_off; trans_num cr_dirty; uint4 detail1; uint4 detail2; } wcs_ops_trace_t; #define OP_LOCK_SIZE 4 /* Structure to hold lock history */ typedef struct { sm_int_ptr_t lock_addr; /* Address of actual lock */ caddr_t lock_callr; /* Address of (un)locker */ int4 lock_pid; /* Process id of (un)locker */ int4 loop_cnt; /* iteration count of lock retry loop */ char lock_op[OP_LOCK_SIZE]; /* Operation performed (either OBTN or RLSE) */ } lockhist; enum ftok_ops { ftok_ops_lock = 1, ftok_ops_release }; typedef struct { enum ftok_ops ftok_oper; uint4 process_id; trans_num cr_tn; } ftokhist; #define DSKREAD_OPS_ARRAY_SIZE 512 #define WCS_OPS_ARRAY_SIZE 1024 #define CRIT_OPS_ARRAY_SIZE 512 #define LOCKHIST_ARRAY_SIZE 512 #define FTOK_OPS_ARRAY_SIZE 512 /* * Enable the GTM_CRYPT_UPDATES_REPORT define below to activate logging of encryption-related operations in shared memory. Those * operations currently include a write and read of an encrypted block (wcs_wtstart and dsk_read, respectively), update retry or * abort due to a concurrent change of encryption cycle (t_end and tp_tend, wcs_wtstart, respectively), update of encryption * settings (mupip_reorg_encrypt), and receipt of new encryption settings (t_retry and tp_restart). Add more macros and macro * callers as necessary. The final report is printed via the DBG_PRINT_BLOCK_INFOS macro (gtm_fork_n_core). */ #ifdef DEBUG /* #define GTM_CRYPT_UPDATES_REPORT 1 */ #endif #ifdef GTM_CRYPT_UPDATES_REPORT # define BLK_INFO_ARRAY_SIZE 10000 /* Static array size for all encryption-related updates. */ typedef struct { uint4 blk_num; uint4 operation; trans_num dbtn; trans_num blktn; boolean_t use_new_key; uint4 subtype; uint4 blk_size; uint4 blk_encr_len; char pre_block[32]; char post_block[32]; } block_update_t; typedef struct { uint4 is_encrypted; uint4 reorg_encrypt_cycle; char hash[GTMCRYPT_HASH_LEN]; char hash2[GTMCRYPT_HASH_LEN]; block_id encryption_hash_cutoff; } trans_update_t; typedef union { block_update_t block_update; trans_update_t trans_update; } info_t; typedef struct blk_info_struct { uint4 type; uint4 pid; uint4 is_encrypted; int4 csa_reorg_encrypt_cycle; uint4 cnl_reorg_encrypt_cycle; block_id encryption_hash_cutoff; trans_num encryption_hash2_start_tn; char hash[GTMCRYPT_HASH_LEN]; char hash2[GTMCRYPT_HASH_LEN]; char region[100]; char where[100]; info_t info; } blk_info; # define DBG_PRINT_BLOCK_INFOS(CNL) \ { \ int i, j, block_index, start, end; \ blk_info *blk_info_ptr; \ \ if ((CNL)->blk_info_cnt <= BLK_INFO_ARRAY_SIZE) \ { \ start = 0; \ end = start + (CNL)->blk_info_cnt; \ } else \ { \ end = (CNL)->blk_info_cnt; \ start = end - BLK_INFO_ARRAY_SIZE; \ } \ for (i = start; i < end; i++) \ { \ block_index = i % BLK_INFO_ARRAY_SIZE; \ blk_info_ptr = &((CNL)->blk_infos[block_index]); \ if (0 == blk_info_ptr->type) \ { \ if (0 == blk_info_ptr->info.block_update.operation) \ FPRINTF(stderr, "%d: BLOCK READ", block_index); \ else if (1 == blk_info_ptr->info.block_update.operation) \ FPRINTF(stderr, "%d: BLOCK WRITE", block_index); \ } else if (1 == blk_info_ptr->type) \ FPRINTF(stderr, "%d: BLOCK RETRY", block_index); \ else if (2 == blk_info_ptr->type) \ FPRINTF(stderr, "%d: BLOCK ABORT", block_index); \ else if (3 == blk_info_ptr->type) \ FPRINTF(stderr, "%d: CRYPT UPDATE", block_index); \ else if (4 == blk_info_ptr->type) \ FPRINTF(stderr, "%d: CRYPT RECEIVE", block_index); \ FPRINTF(stderr, " on %s in %s:", blk_info_ptr->region, blk_info_ptr->where); \ FPRINTF(stderr, \ "\n\tpid: %u\n" \ "\tis_encrypted: %u\n" \ "\tcsa_reorg_encrypt_cycle: %d\n" \ "\tcnl_reorg_encrypt_cycle: %u\n" \ "\tencryption_hash_cutoff: %d\n" \ "\tencryption_hash2_start_tn: %ld", \ blk_info_ptr->pid, \ blk_info_ptr->is_encrypted, \ blk_info_ptr->csa_reorg_encrypt_cycle, \ blk_info_ptr->cnl_reorg_encrypt_cycle, \ blk_info_ptr->encryption_hash_cutoff, \ blk_info_ptr->encryption_hash2_start_tn); \ FPRINTF(stderr, "\n\tencryption_hash: "); \ for (j = 0; j < GTMCRYPT_HASH_LEN; j += 2) \ FPRINTF(stderr, "%02X", (unsigned char)blk_info_ptr->hash[j / 2]); \ FPRINTF(stderr, "\n\tencryption_hash2: "); \ for (j = 0; j < GTMCRYPT_HASH_LEN; j += 2) \ FPRINTF(stderr, "%02X", (unsigned char)blk_info_ptr->hash2[j / 2]); \ if (0 == blk_info_ptr->type) \ { \ FPRINTF(stderr, \ "\n\tblk_num : %u\n" \ "\toperation : %d\n" \ "\tdbtn : 0x%llx\n" \ "\tblktn : 0x%llx\n" \ "\tuse_new_key : %d\n" \ "\tsubtype : %d\n" \ "\tblk_size : %d\n" \ "\tblk_encr_len: %d\n", \ blk_info_ptr->info.block_update.blk_num, \ blk_info_ptr->info.block_update.operation, \ blk_info_ptr->info.block_update.dbtn, \ blk_info_ptr->info.block_update.blktn, \ blk_info_ptr->info.block_update.use_new_key, \ blk_info_ptr->info.block_update.subtype, \ blk_info_ptr->info.block_update.blk_size, \ blk_info_ptr->info.block_update.blk_encr_len); \ } else if (4 == blk_info_ptr->type) \ { \ FPRINTF(stderr, \ "\n\tis_encrypted: %u\n" \ "\treorg_encrypt_cycle: %u\n" \ "\tencryption_hash_cutoff: %d", \ blk_info_ptr->info.trans_update.is_encrypted, \ blk_info_ptr->info.trans_update.reorg_encrypt_cycle, \ blk_info_ptr->info.trans_update.encryption_hash_cutoff); \ FPRINTF(stderr, "\n\thash: "); \ for (j = 0; j < GTMCRYPT_HASH_LEN; j += 2) \ FPRINTF(stderr, "%02X", \ (unsigned char)blk_info_ptr->info.trans_update.hash[j / 2]); \ FPRINTF(stderr, "\n\thash2: "); \ for (j = 0; j < GTMCRYPT_HASH_LEN; j += 2) \ FPRINTF(stderr, "%02X", \ (unsigned char)blk_info_ptr->info.trans_update.hash2[j / 2]); \ FPRINTF(stderr, "\n"); \ } else \ FPRINTF(stderr, "\n"); \ FFLUSH(stderr); \ } \ } # define DBG_RECORD_COMMON_STUFF(BLK_INFO_PTR, TYPE, CSD, CSA, CNL, PID) \ blk_info *blk_info_ptr; \ \ BLK_INFO_PTR = &((CNL)->blk_infos[(CNL)->blk_info_cnt++ % BLK_INFO_ARRAY_SIZE]); \ BLK_INFO_PTR->type = TYPE; \ BLK_INFO_PTR->pid = PID; \ BLK_INFO_PTR->is_encrypted = (CSD)->is_encrypted; \ BLK_INFO_PTR->csa_reorg_encrypt_cycle = \ (NULL == (CSA)->encr_ptr) ? -1 : (CSA)->encr_ptr->reorg_encrypt_cycle; \ BLK_INFO_PTR->cnl_reorg_encrypt_cycle = (CNL)->reorg_encrypt_cycle; \ BLK_INFO_PTR->encryption_hash_cutoff = (CSD)->encryption_hash_cutoff; \ BLK_INFO_PTR->encryption_hash2_start_tn = (CSD)->encryption_hash2_start_tn; \ memcpy(BLK_INFO_PTR->hash, (CSD)->encryption_hash, GTMCRYPT_HASH_LEN); \ memcpy(BLK_INFO_PTR->hash2, (CSD)->encryption_hash2, GTMCRYPT_HASH_LEN); \ strcpy(BLK_INFO_PTR->where, __FILE__); \ if ((NULL != (CSA)->region) && (NULL != (CSA)->region->dyn.addr)) \ { \ memcpy(BLK_INFO_PTR->region, (char *)(CSA)->region->dyn.addr->fname, \ (CSA)->region->dyn.addr->fname_len); \ BLK_INFO_PTR->region[(CSA)->region->dyn.addr->fname_len] = '\0'; \ } else \ strcpy(BLK_INFO_PTR->region, "UNKNOWN"); \ # define DBG_RECORD_BLOCK_UPDATE(CSD, CSA, CNL, PID, BLK, OPER, BLKTN, SUBTYPE, NEW_KEY, PRE_BLK, POST_BLK, BLK_SIZE, BLK_ENC_LEN) \ { \ DBG_RECORD_COMMON_STUFF(blk_info_ptr, 0, CSD, CSA, CNL, PID); \ blk_info_ptr->info.block_update.blk_num = BLK; \ blk_info_ptr->info.block_update.operation = OPER; \ blk_info_ptr->info.block_update.dbtn = CSD->trans_hist.curr_tn; \ blk_info_ptr->info.block_update.blktn = BLKTN; \ blk_info_ptr->info.block_update.use_new_key = NEW_KEY; \ blk_info_ptr->info.block_update.subtype = SUBTYPE; \ memcpy(blk_info_ptr->info.block_update.pre_block, PRE_BLK, 32); \ memcpy(blk_info_ptr->info.block_update.post_block, POST_BLK, 32); \ blk_info_ptr->info.block_update.blk_size = BLK_SIZE; \ blk_info_ptr->info.block_update.blk_encr_len = BLK_ENC_LEN; \ } # define DBG_RECORD_BLOCK_READ(CSD, CSA, CNL, PID, BLK, BLKTN, SUBTYPE, NEW_KEY, PRE_BLK, POST_BLK, BLK_SIZE, BLK_ENCR_LEN) \ { \ DBG_RECORD_BLOCK_UPDATE(CSD, CSA, CNL, PID, BLK, 0, BLKTN, SUBTYPE, NEW_KEY, PRE_BLK, POST_BLK, BLK_SIZE, BLK_ENCR_LEN);\ } # define DBG_RECORD_BLOCK_WRITE(CSD, CSA, CNL, PID, BLK, BLKTN, SUBTYPE, NEW_KEY, PRE_BLK, POST_BLK, BLK_SIZE, BLK_ENCR_LEN) \ { \ DBG_RECORD_BLOCK_UPDATE(CSD, CSA, CNL, PID, BLK, 1, BLKTN, SUBTYPE, NEW_KEY, PRE_BLK, POST_BLK, BLK_SIZE, BLK_ENCR_LEN);\ } # define DBG_RECORD_BLOCK_RETRY(CSD, CSA, CNL, PID) \ { \ DBG_RECORD_COMMON_STUFF(blk_info_ptr, 1, CSD, CSA, CNL, PID); \ } # define DBG_RECORD_BLOCK_ABORT(CSD, CSA, CNL, PID) \ { \ DBG_RECORD_COMMON_STUFF(blk_info_ptr, 2, CSD, CSA, CNL, PID); \ } # define DBG_RECORD_CRYPT_UPDATE(CSD, CSA, CNL, PID) \ { \ DBG_RECORD_COMMON_STUFF(blk_info_ptr, 3, CSD, CSA, CNL, PID); \ } # define DBG_RECORD_CRYPT_RECEIVE(CSD, CSA, CNL, PID, TRANS_INFO) \ { \ DBG_RECORD_COMMON_STUFF(blk_info_ptr, 4, CSD, CSA, CNL, PID); \ blk_info_ptr->info.trans_update.is_encrypted = TRANS_INFO->is_encrypted; \ blk_info_ptr->info.trans_update.reorg_encrypt_cycle = TRANS_INFO->reorg_encrypt_cycle; \ blk_info_ptr->info.trans_update.encryption_hash_cutoff = TRANS_INFO->encryption_hash_cutoff; \ memcpy(blk_info_ptr->info.trans_update.hash, TRANS_INFO->encryption_hash, GTMCRYPT_HASH_LEN); \ memcpy(blk_info_ptr->info.trans_update.hash2, TRANS_INFO->encryption_hash2, GTMCRYPT_HASH_LEN); \ } #else # define DBG_PRINT_BLOCK_INFOS(CNL) # define DBG_RECORD_BLOCK_READ(CSD, CSA, CNL, PID, BLK, BLKTN, SUBTYPE, USE_NEW_KEY, PRE_BLOCK, POST_BLOCK, BLK_SIZE, BLK_ENCR_LEN) # define DBG_RECORD_BLOCK_WRITE(CSD, CSA, CNL, PID, BLK, BLKTN, SUBTYPE, USE_NEW_KEY, PRE_BLOCK, POST_BLOCK, BLK_SIZE, BLK_ENCR_LEN) # define DBG_RECORD_BLOCK_RETRY(CSD, CSA, CNL, PID) # define DBG_RECORD_BLOCK_ABORT(CSD, CSA, CNL, PID) # define DBG_RECORD_CRYPT_UPDATE(CSD, CSA, CNL, PID) # define DBG_RECORD_CRYPT_RECEIVE(CSD, CSA, CNL, PID, TRANS_INFO) #endif /* Mapped space local to each node on the cluster */ typedef struct node_local_struct { unsigned char label[GDS_LABEL_SZ]; /* 12 signature for GDS shared memory */ unsigned char fname[MAX_FN_LEN + 1]; /* 256 filename of corresponding database */ char now_running[MAX_REL_NAME]; /* 36 current active GT.M version stamp */ char machine_name[MAX_MCNAMELEN]; /* 256 machine name for clustering */ sm_off_t bt_header_off; /* (QW alignment) offset to hash table */ sm_off_t bt_base_off; /* bt first entry */ sm_off_t th_base_off; sm_off_t cache_off; sm_off_t cur_lru_cache_rec_off; /* current LRU cache_rec pointer offset */ sm_off_t critical; sm_off_t jnl_buff; sm_off_t shmpool_buffer; /* Shared memory buffer pool area */ sm_off_t lock_addrs; sm_off_t hdr; /* Offset to file-header (BG mode ONLY!) */ volatile int4 in_crit; int4 in_reinit; unsigned short ccp_cycle; unsigned short filler; /* Align for ccp_cycle. Not changing to int * as that would perturb to many things at this point */ boolean_t ccp_crit_blocked; int4 ccp_state; boolean_t ccp_jnl_closed; boolean_t glob_sec_init; uint4 wtstart_pid[MAX_WTSTART_PID_SLOTS]; /* Maintain pids of wcs_wtstart processes */ volatile int4 wc_blocked; /* WC_UNBLOCK = do not block write cache * WC_BLOCK_ONLY = block write cache and do not attempt any * cleanup (used for region freeze operations) * WC_BLOCK_RECOVER = block write cache, verify, and recover * if necessary (used if cache is left in possibly inconsistent * state) * Set to WC_BLOCK_RECOVER by process that knows it is * leaving the cache in a possibly inconsistent state. Next * process grabbing crit will do cache recovery. In MM mode, * it is used to call wcs_recover during a file extension. * Setting to WC_BLOCK_ONLY or WC_BLOCK_RECOVER stops all * concurrent writers from working on the cache. */ global_latch_t wc_var_lock; /* latch used for access to various wc_* ref counters */ CACHELINE_PAD(SIZEOF(global_latch_t), 1) /* Keep these two latches in separate cache lines */ global_latch_t db_latch; /* latch for interlocking on hppa and tandem */ CACHELINE_PAD(SIZEOF(global_latch_t), 2) int4 cache_hits; int4 wc_in_free; /* number of write cache records in free queue */ /* All counters below (declared using CNTR4DCL) are 2 or 4-bytes, depending on platform, but always stored in 4 bytes. * CACHELINE_PAD doesn't use SIZEOF because misses any padding added by CNTR4DCL. We want to keep the counters in * separate cachelines on load-lock/store-conditional platforms particularly and on other platforms too, just to be safe. */ volatile CNTR4DCL(wcs_timers, 1); /* number of write cache timers in use - 1 */ CACHELINE_PAD(4, 3) volatile CNTR4DCL(wcs_active_lvl, 2); /* number of entries in active queue */ CACHELINE_PAD(4, 4) volatile CNTR4DCL(wcs_staleness, 3); CACHELINE_PAD(4, 5) volatile CNTR4DCL(ref_cnt, 4); /* reference count. How many people are using the database */ CACHELINE_PAD(4, 6) volatile CNTR4DCL(intent_wtstart, 5); /* Count of processes that INTEND to enter wcs_wtstart code */ CACHELINE_PAD(4, 7) volatile CNTR4DCL(in_wtstart, 6); /* Count of processes that are INSIDE wcs_wtstart code */ CACHELINE_PAD(4, 8) volatile CNTR4DCL(wcs_phase2_commit_pidcnt, 7); /* number of processes actively finishing phase2 commit */ CACHELINE_PAD(4, 9) volatile CNTR4DCL(wcs_wip_lvl, 8); /* number of entries in wip queue */ CACHELINE_PAD(4, 10) volatile int4 wtfini_in_prog; /* whether wcs_wtfini() is in progress at this time */ boolean_t freezer_waited_for_kip; /* currently used only in dbg code */ int4 mm_extender_pid; /* pid of the process executing gdsfilext in MM mode */ block_id highest_lbm_blk_changed; /* Records highest local bit map block that * changed so we know how much of master bit * map to write out. Modified only under crit */ block_id nbb; /* Next backup block -- for online backup */ int4 lockhist_idx; /* (DW alignment) "circular" index into lockhists array */ int4 crit_ops_index; /* "circular" index into crit_ops_array */ int4 dskread_ops_index; /* "circular" index into dskread_ops_array */ int4 ftok_ops_index; /* "circular" index into ftok_ops_array */ int4 wcs_ops_index; /* "circular" index into wcs_ops_array */ lockhist lockhists[LOCKHIST_ARRAY_SIZE]; /* Keep lock histories here */ crit_trace crit_ops_array[CRIT_OPS_ARRAY_SIZE]; /* space for CRIT_TRACE macro to record info */ dskread_trace dskread_ops_array[DSKREAD_OPS_ARRAY_SIZE]; /* space for DSKREAD_TRACE macro to record info */ wcs_ops_trace_t wcs_ops_array[WCS_OPS_ARRAY_SIZE]; /* space for WCS_OPS_TRACE macro to record info */ unique_file_id unique_id; uint4 owner_node; volatile int4 wcsflu_pid; /* pid of the process executing wcs_flu in BG mode */ int4 creation_date_time4; /* Lower order 4-bytes of database's creation time to be * compared at sm attach time */ int4 inhibit_kills; /* inhibit new KILLs while MUPIP BACKUP, INTEG or FREEZE are * waiting for kill-in-progress to become zero */ boolean_t remove_shm; /* can this shm be removed by the last process to rundown */ union { gds_file_id jnl_file_id; /* needed on UNIX to hold space */ unix_file_id u; /* from gdsroot.h even for VMS */ } jnl_file; /* Note that in versions before V4.3-001B, "jnl_file" used to be a member of sgmnt_data. * Now it is a filler there and rightly used here since it is non-zero only when shared memory is active. */ boolean_t donotflush_dbjnl; /* whether database and journal can be flushed to disk or not (TRUE for mupip recover) */ int4 n_pre_read; char replinstfilename[MAX_FN_LEN + 1];/* 256 : Name of the replication instance file corresponding to this db */ char statsdb_fname[MAX_FN_LEN + 1]; /* Is empty-string if IS_RDBF_STATSDB(csd) is FALSE. * Is name of the statsdb corresponding to this basedb otherwise. */ gvstats_rec_t gvstats_rec; trans_num last_wcsflu_tn; /* curr_tn when last wcs_flu was done on this database */ trans_num last_wcs_recover_tn; /* csa->ti->curr_tn of most recent "wcs_recover" */ sm_off_t encrypt_glo_buff_off; /* offset from unencrypted global buffer to its encrypted counterpart */ global_latch_t snapshot_crit_latch; /* To be acquired by any process that wants to clean up an orphaned snapshot or * initiate a new snapshot */ long ss_shmid; /* Identifier of the shared memory for the snapshot that started * recently. */ uint4 ss_shmcycle; /* incremented everytime a new snapshot creates a new shared memory identifier */ boolean_t snapshot_in_prog; /* Tells GT.M if any snapshots are in progress */ uint4 num_snapshots_in_effect; /* how many snapshots are currently in place for this region */ uint4 wbox_test_seq_num; /* used to coordinate with sequential testing steps */ uint4 freeze_online; /* for online db freezing, a.k.a. chill. */ uint4 kip_pid_array[MAX_KIP_PID_SLOTS]; /* Processes actively doing kill (0 denotes empty slots) */ gtm_uint64_t sec_size; /* Upon going to larger shared memory sizes, we realized that this does not */ /* need to be in the file header but the node local since it can be calculated */ /* from info in the file header. */ int4 jnlpool_shmid; /* copy of jnlpool->repl_inst_filehdr->jnlpool_shmid to prevent mixing of multiple * journal pools within the same database. */ uint4 trunc_pid; /* Operating truncate. */ block_id highest_lbm_with_busy_blk; /* Furthest lmap block known to have had a busy block during truncate. */ ftokhist ftok_ops_array[FTOK_OPS_ARRAY_SIZE]; volatile uint4 root_search_cycle; /* incremented online rollback ends and mu_swap_root */ volatile uint4 onln_rlbk_cycle; /* incremented everytime an online rollback ends */ volatile uint4 db_onln_rlbkd_cycle; /* incremented everytime an online rollback takes the database back in time */ volatile uint4 onln_rlbk_pid; /* process ID of currently running online rollback. */ uint4 dbrndwn_ftok_skip; /* # of processes that skipped FTOK semaphore in gds_rundown due to too many MUMPS * processes */ uint4 dbrndwn_access_skip; /* # of processes that skipped access control semaphore in gds_rundown due to a * concurrent online rollback or too many MUMPS processes */ boolean_t fastinteg_in_prog; /* Tells GT.M if fast integrity is in progress */ uint4 wtstart_errcnt; /* Note that although the below fields are dbg-only, they are defined for pro since we want to keep the shared * memory layout the same for both pro and dbg. There is some code that relies on this assumption. */ boolean_t fake_db_enospc; /* used only by dbg versions to simulate ENOSPC scenarios in the database file */ boolean_t fake_jnl_enospc; /* used only by dbg versions to simulate ENOSPC scenarios in the journal file */ uint4 jnl_writes; /* used only by dbg versions to count the number of journal write operations */ uint4 db_writes; /* used only by dbg versions to count the number of database write operations */ boolean_t doing_epoch; /* set when performing an epoch */ uint4 epoch_taper_start_dbuffs; /* wcs_active_lvl at start of taper */ boolean_t epoch_taper_need_fsync; uint4 wt_pid_array[MAX_WT_PID_SLOTS]; /* Processes with active wcs_timers (0 denotes empty slots) * Note: Unreliable - For Diagnostic Purposes only */ uint4 reorg_encrypt_pid; /* indicates whether a MUPIP REORG -ENCRYPT is in progress */ uint4 reorg_encrypt_cycle; /* reflects the cycle of database encryption status in a series of * MUPIP REORG -ENCRYPTs */ uint4 mupip_extract_count; /* count of currently running MUPIP EXTRACTs; to be improved with GTM-8488 */ /* Below 4 values are cached from the original DB file header that created the shared memory segment. Used by DSE only */ enum db_acc_method saved_acc_meth; int4 saved_blk_size; uint4 saved_lock_space_size; int4 saved_jnl_buffer_size; /* Miscellaneous flag */ trans_num update_underway_tn; boolean_t lastwriterbypas_msg_issued; /* whether a LASTWRITERBYPAS message has been once issued for this db */ boolean_t first_writer_seen; /* Has a process with read-write access to the database opened it yet */ boolean_t first_nonbypas_writer_seen; /* TRUE when first writer is seen that also does not bypass ftok/access */ boolean_t ftok_counter_halted; /* The ftok semaphore counter reached 32K at some point in time */ boolean_t access_counter_halted; /* The access semaphore counter reached 32K at some point in time */ boolean_t statsdb_created; /* TRUE if a statsdb has been created for this basedb */ uint4 statsdb_fname_len; /* length of "cnl->statsdb_fname" */ boolean_t statsdb_rundown_clean; /* TRUE if statsdb "gds_rundown"/"mu_rndwn_file" was clean. * This means statsdb file can be removed on a clean basedb rundown. */ int statsdb_cur_error; /* failure code for last statsDB error - used in throttling messages */ uint4 statsdb_error_cycle; /* for count down of repeated errors - used in throttling messages */ # ifdef GTM_CRYPT_UPDATES_REPORT blk_info blk_infos[BLK_INFO_ARRAY_SIZE]; uint4 blk_info_cnt; int4 filler_8byte_align2; # endif global_latch_t freeze_latch; /* Protect freeze/freeze_online field updates */ gtm_uint64_t wcs_buffs_freed; /* this is a count of the number of buffers transitioned to the free "queue" */ volatile gtm_uint64_t dskspace_next_fire; global_latch_t lock_crit; /* mutex for LOCK processing */ volatile block_id tp_hint; } node_local; #define COPY_STATSDB_FNAME_INTO_STATSREG(statsDBreg, statsDBfname, statsDBfname_len) \ MBSTART { \ unsigned int fnameLen; \ \ assert(ARRAYSIZE(statsDBreg->dyn.addr->fname) >= ARRAYSIZE(statsDBfname)); \ fnameLen = MIN(statsDBfname_len, ARRAYSIZE(statsDBreg->dyn.addr->fname) - 1); \ assert('\0' == statsDBfname[fnameLen]); \ memcpy(statsDBreg->dyn.addr->fname, statsDBfname, fnameLen + 1); /* copy trailing '\0' too */ \ statsDBreg->dyn.addr->fname_len = fnameLen; \ } MBEND #define COPY_BASEDB_FNAME_INTO_STATSDB_HDR(statsDBreg, baseDBreg, statsDBcsd) \ MBSTART { \ unsigned int fname_len; \ \ assert(IS_STATSDB_REG(statsDBreg)); \ assert(ARRAYSIZE(baseDBreg->dyn.addr->fname) <= ARRAYSIZE(statsDBcsd->basedb_fname)); \ fname_len = MIN(baseDBreg->dyn.addr->fname_len, ARRAYSIZE(baseDBreg->dyn.addr->fname) - 1); \ assert(fname_len); \ assert('\0' == baseDBreg->dyn.addr->fname[fname_len]); \ memcpy(statsDBcsd->basedb_fname, baseDBreg->dyn.addr->fname, fname_len + 1); /* copy trailing '\0' too */ \ statsDBcsd->basedb_fname_len = fname_len; \ } MBEND #define UNLINK_STATSDB_AT_BASEDB_RUNDOWN(CNL) \ MBSTART { \ if (CNL->statsdb_created && CNL->statsdb_rundown_clean) \ { \ assert(CNL->statsdb_fname_len); /* "gvcst_init" would not have set CNL->statsdb_created otherwise */ \ assert('\0' == CNL->statsdb_fname[CNL->statsdb_fname_len]); \ rc = UNLINK(CNL->statsdb_fname); \ assert(0 == rc); \ /* If error removing statsdb, ignore as we want to continue rundown of basedb (more important) */ \ CNL->statsdb_created = FALSE; \ } \ } MBEND #define ADD_ENT_TO_ACTIVE_QUE_CNT(CNL) (INCR_CNT((sm_int_ptr_t)(&CNL->wcs_active_lvl), \ (sm_global_latch_ptr_t)(&CNL->wc_var_lock))) #define SUB_ENT_FROM_ACTIVE_QUE_CNT(CNL) (DECR_CNT((sm_int_ptr_t)(&CNL->wcs_active_lvl), \ (sm_global_latch_ptr_t)(&CNL->wc_var_lock))) #define ADD_ENT_TO_WIP_QUE_CNT(CNL) (INCR_CNT((sm_int_ptr_t)(&CNL->wcs_wip_lvl), \ (sm_global_latch_ptr_t)(&CNL->wc_var_lock))) #define SUB_ENT_FROM_WIP_QUE_CNT(CNL) (DECR_CNT((sm_int_ptr_t)(&CNL->wcs_wip_lvl), \ (sm_global_latch_ptr_t)(&CNL->wc_var_lock))) #define ADD_ENT_TO_FREE_QUE_CNT(CNL) (INCR_CNT((sm_int_ptr_t)(&CNL->wc_in_free), \ (sm_global_latch_ptr_t)(&CNL->wc_var_lock))) #define SUB_ENT_FROM_FREE_QUE_CNT(CNL) (DECR_CNT((sm_int_ptr_t)(&CNL->wc_in_free), \ (sm_global_latch_ptr_t)(&CNL->wc_var_lock))) #define DSKREAD_TRACE(CSA, CR_OFF, CR_TN, PID, BLK, CYCLE) \ MBSTART { \ int4 doidx; \ node_local_ptr_t cnl; \ assert((NULL != CSA)&& (NULL != (CSA->nl))); \ cnl = CSA->nl; \ doidx = ++cnl->dskread_ops_index; \ if (DSKREAD_OPS_ARRAY_SIZE <= doidx) \ cnl->dskread_ops_index = doidx = 0; \ cnl->dskread_ops_array[doidx].cr_off = CR_OFF; \ cnl->dskread_ops_array[doidx].cr_tn = CR_TN; \ cnl->dskread_ops_array[doidx].process_id = PID; \ cnl->dskread_ops_array[doidx].blk = BLK; \ cnl->dskread_ops_array[doidx].cycle = CYCLE; \ } MBEND #ifdef DEBUG /* The following macro does not use a separate semaphore to protect its maintenance of the shared memory * value crit_ops_index (which would complicate precisely the situation it was created to examine) therefore, * in order to to maximize the chances of gathering meaningful data, it seems better placed after grab_crit * and before rel_crit. Also we will increment the index first and cache it so we can shorten our exposure window. */ #define CRIT_TRACE(CSA, X) \ MBSTART { \ int4 coidx; \ node_local_ptr_t cnl; \ boolean_t in_ast; \ unsigned int ast_status; \ \ assert((NULL != CSA) && (NULL !=(CSA->nl))); \ cnl = CSA->nl; \ coidx = ++cnl->crit_ops_index; \ if (CRIT_OPS_ARRAY_SIZE <= coidx) \ cnl->crit_ops_index = coidx = 0; \ cnl->crit_ops_array[coidx].call_from = (caddr_t)caller_id(0); \ cnl->crit_ops_array[coidx].epid = process_id; \ cnl->crit_ops_array[coidx].crit_act = (X); \ cnl->crit_ops_array[coidx].curr_tn = (NULL != CSA->hdr) ? \ CSA->hdr->trans_hist.curr_tn : 0; \ } MBEND /* The following macro checks that curr_tn and early_tn are equal right before beginning a transaction commit. * The only exception we know of is if a process in the midst of commit had been killed (kill -9 or STOP/ID) * after having incremented early_tn but before it finished the commit (and therefore incremented curr_tn). * In that case another process that did a rundown (and executed secshr_db_clnup) at around the same time * could have cleaned up the CRIT lock (sensing that the crit holder pid is no longer alive) making the crit * lock available for other processes. To check if that is the case, we need to go back the crit_ops_array and check that * a) the most recent crit operation was a grab crit done by the current pid (crit_act == crit_ops_gw) AND * b) the immediately previous crit operation should NOT be a release crit crit_ops_rw but instead should be a crit_ops_gw * c) there are two exceptions to this and they are * (i) that there could be one or more crit_ops_nocrit actions from processes that tried releasing crit * even though they don't own it (cases we know of are in gds_rundown and in t_end/tp_tend if * t_commit_cleanup completes the transaction after a mid-commit error). * (ii) there could be one or more crit_ops_gw/crit_ops_rw pair of operations by a pid in between. */ #define ASSERT_CURR_TN_EQUALS_EARLY_TN(csa, currtn) \ MBSTART { \ GBLREF uint4 process_id; \ \ assert((currtn) == csa->ti->curr_tn); \ if (csa->ti->early_tn != (currtn)) \ { \ int4 coidx, lcnt; \ node_local_ptr_t cnl; \ uint4 expect_gw_pid = 0; \ \ cnl = csa->nl; \ assert(NULL != (node_local_ptr_t)cnl); \ coidx = cnl->crit_ops_index; \ assert(CRIT_OPS_ARRAY_SIZE > coidx); \ assert(crit_ops_gw == cnl->crit_ops_array[coidx].crit_act); \ assert(process_id == cnl->crit_ops_array[coidx].epid); \ for (lcnt = 0; CRIT_OPS_ARRAY_SIZE > lcnt; lcnt++) \ { \ if (coidx) \ coidx--; \ else \ coidx = CRIT_OPS_ARRAY_SIZE - 1; \ if (crit_ops_nocrit == cnl->crit_ops_array[coidx].crit_act) \ continue; \ if (crit_ops_rw == cnl->crit_ops_array[coidx].crit_act) \ { \ assert(0 == expect_gw_pid); \ expect_gw_pid = cnl->crit_ops_array[coidx].epid; \ } else if (crit_ops_gw == cnl->crit_ops_array[coidx].crit_act) \ { \ if (!expect_gw_pid) \ break; /* found lone grab-crit */ \ assert(expect_gw_pid == cnl->crit_ops_array[coidx].epid); \ expect_gw_pid = 0;/* found paired grab-crit. continue search */ \ } \ } \ assert(CRIT_OPS_ARRAY_SIZE > lcnt); /* assert if did not find lone grab-crit */ \ } \ } MBEND /* * The following macro places lock history entries in an array for debugging. * NOTE: Users of this macro, set either of the following prior to using this macro. * (i) gv_cur_region to the region whose history we are storing. * (ii) global variable "locknl" to correspond to the node-local of the region whose history we are storing. * If "locknl" is non-NULL, it is used to store the lock history. If not only then is gv_cur_region used. */ #define LOCK_HIST(OP, LOC, ID, CNT) \ MBSTART { \ GBLREF node_local_ptr_t locknl; \ \ int lockidx; \ node_local_ptr_t lcknl; \ \ if (NULL == locknl) \ { \ assert(NULL != gv_cur_region); \ lcknl = FILE_INFO(gv_cur_region)->s_addrs.nl; \ assert(NULL != lcknl); \ } else \ lcknl = locknl; \ lockidx = ++lcknl->lockhist_idx; \ if (LOCKHIST_ARRAY_SIZE <= lockidx) \ lcknl->lockhist_idx = lockidx = 0; \ GET_LONGP(&lcknl->lockhists[lockidx].lock_op[0], (OP)); \ lcknl->lockhists[lockidx].lock_addr = (sm_int_ptr_t)(LOC); \ lcknl->lockhists[lockidx].lock_callr = (caddr_t)caller_id(0); \ lcknl->lockhists[lockidx].lock_pid = (int4)(ID); \ lcknl->lockhists[lockidx].loop_cnt = (int4)(CNT); \ } MBEND #define WCS_OPS_TRACE(CSA, PID, TYPE, BLK, CR_OFF, CR_DIRTY, DETAIL1, DETAIL2) \ MBSTART { \ int4 wtidx; \ node_local_ptr_t cnl; \ boolean_t donotflush_dbjnl; \ \ assert((NULL != CSA) && (NULL != (CSA->nl))); \ cnl = CSA->nl; \ donotflush_dbjnl = cnl->donotflush_dbjnl; \ assert((FALSE == cnl->donotflush_dbjnl) || (TRUE == cnl->donotflush_dbjnl)); \ wtidx = ++cnl->wcs_ops_index; \ if (WCS_OPS_ARRAY_SIZE <= wtidx) \ cnl->wcs_ops_index = wtidx = 0; \ cnl->wcs_ops_array[wtidx].db_tn = CSA->ti->curr_tn; \ cnl->wcs_ops_array[wtidx].process_id = PID; \ cnl->wcs_ops_array[wtidx].wc_in_free = cnl->wc_in_free; \ cnl->wcs_ops_array[wtidx].wcs_active_lvl = cnl->wcs_active_lvl; \ cnl->wcs_ops_array[wtidx].wcs_wip_lvl = cnl->wcs_wip_lvl; \ cnl->wcs_ops_array[wtidx].type = TYPE; \ cnl->wcs_ops_array[wtidx].blk = BLK; \ cnl->wcs_ops_array[wtidx].cr_off = CR_OFF; \ cnl->wcs_ops_array[wtidx].cr_dirty = CR_DIRTY; \ cnl->wcs_ops_array[wtidx].detail1 = DETAIL1; \ cnl->wcs_ops_array[wtidx].detail2 = DETAIL2; \ assert((FALSE == cnl->donotflush_dbjnl) || (TRUE == cnl->donotflush_dbjnl)); \ assert(donotflush_dbjnl == cnl->donotflush_dbjnl); \ } MBEND #define DUMP_LOCKHIST() dump_lockhist() #else #define CRIT_TRACE(CSA, X) #define ASSERT_CURR_TN_EQUALS_EARLY_TN(csa, currtn) #define LOCK_HIST(OP, LOC, ID, CNT) #define WCS_OPS_TRACE(CSA, PID, TYPE, BLK, CR_OFF, CR_TN, DETAIL1, DETAIL2) #define DUMP_LOCKHIST() #endif #define FTOK_TRACE(CSA, CR_TN, FTOK_OPER, PID) \ MBSTART { \ node_local_ptr_t cnl; \ int4 foindx; \ assert(NULL != CSA); \ if (cnl = (CSA->nl)) \ { \ foindx = ++cnl->ftok_ops_index; \ if (FTOK_OPS_ARRAY_SIZE <= cnl->ftok_ops_index) \ foindx = cnl->ftok_ops_index = 0; \ cnl->ftok_ops_array[foindx].process_id = PID; \ cnl->ftok_ops_array[foindx].cr_tn = CR_TN; \ cnl->ftok_ops_array[foindx].ftok_oper = FTOK_OPER; \ } \ } MBEND #define BT_NOT_ALIGNED(bt, bt_base) (!IS_PTR_ALIGNED((bt), (bt_base), SIZEOF(bt_rec))) #define BT_NOT_IN_RANGE(bt, bt_lo, bt_hi) (!IS_PTR_IN_RANGE((bt), (bt_lo), (bt_hi))) #define MIN_SLEEP_CNT 0 /* keep this in sync with any minseg("SLEEP_CNT) in gdeinit.m */ #define MAX_SLEEP_CNT E_6 /* keep this in sync with any maxseg("SLEEP_CNT") in gdeinit.m */ #define DEFAULT_SLEEP_CNT 0 /* keep this in sync with any tmpseg("SLEEP_CNT") in gdeget.m */ #define SLEEP_SPIN_CNT(CSD) (CSD)->mutex_spin_parms.mutex_sleep_spin_count #define MAX_SPIN_SLEEP_MASK 0x3FFFFFFF #define SPIN_SLEEP_MASK(CSD) (CSD)->mutex_spin_parms.mutex_spin_sleep_mask #define HARD_SPIN_COUNT(CSD) (CSD)->mutex_spin_parms.mutex_hard_spin_count #define MIN_CRIT_ENTRY 64 /* keep this in sync with gdeinit.m minseg("MUTEX_SLOTS") */ #define MAX_CRIT_ENTRY 32768 /* keep this in sync with gdeinit.m maxseg("MUTEX_SLOTS") */ #define DEFAULT_NUM_CRIT_ENTRY 1024 /* keep this in sync with gdeget.m tmpseg("MUTEX_SLOTS") */ #ifdef CRIT_USE_PTHREAD_MUTEX #define CRIT_SPACE(ENTRIES) SIZEOF(pth_mutex_struct) #else #define CRIT_SPACE(ENTRIES) ((ENTRIES) * SIZEOF(mutex_que_entry) + SIZEOF(mutex_struct)) #endif #define NUM_CRIT_ENTRY(CSD) (CSD)->mutex_spin_parms.mutex_que_entry_space_size #define JNLPOOL_CRIT_SPACE CRIT_SPACE(DEFAULT_NUM_CRIT_ENTRY) #define NODE_LOCAL_SIZE (ROUND_UP(SIZEOF(node_local), OS_PAGE_SIZE)) #define NODE_LOCAL_SPACE(CSD) (ROUND_UP(CRIT_SPACE(NUM_CRIT_ENTRY(CSD)) + NODE_LOCAL_SIZE, OS_PAGE_SIZE)) #define MIN_NODE_LOCAL_SPACE (ROUND_UP(CRIT_SPACE(MIN_CRIT_ENTRY) + NODE_LOCAL_SIZE, OS_PAGE_SIZE)) #define DEFAULT_PROBLKSPLIT 5 /* proactively split blocks if contains more records that this in the block */ /* In order for gtmsecshr not to pull in OTS library, NODE_LOCAL_SIZE_DBS is used in secshr_db_clnup instead of NODE_LOCAL_SIZE */ #define NODE_LOCAL_SIZE_DBS (ROUND_UP(SIZEOF(node_local), DISK_BLOCK_SIZE)) #define INIT_NUM_CRIT_ENTRY_IF_NEEDED(CSD) \ MBSTART { \ /* The layout of shared memory depends on the number of mutex queue entries specified in the file header. Thus in \ * order to set, for example, csa->critical or csa->shmpool_buffer, we need to know this number. However, this \ * number can be zero if we have not yet done db_auto_upgrade. So go ahead and upgrade to the value that will \ * eventually be used, which is DEFAULT_NUM_CRIT_ENTRY. \ */ \ /* Be safe in PRO and check if we need to initialize crit entries, even for GDSMV60002 and later. */ \ if (0 == NUM_CRIT_ENTRY(CSD)) \ NUM_CRIT_ENTRY(CSD) = DEFAULT_NUM_CRIT_ENTRY; \ } MBEND #define ETGENTLE 2 #define ETSLOW 8 #define ETQUICK 16 #define ETFAST 64 #define EPOCH_TAPER_TIME_PCT_DEFAULT 32 #define EPOCH_TAPER_JNL_PCT_DEFAULT 13 #define EPOCH_TAPER_IF_NEEDED(CSA, CSD, CNL, REG, DO_FSYNC, BUFFS_PER_FLUSH, FLUSH_TARGET) \ MBSTART { \ jnl_tm_t now; \ uint4 epoch_vector, jnl_autoswitchlimit, jnl_space_remaining, jnl_space_taper_interval; \ uint4 next_epoch_time, relative_overall_taper, relative_space_taper, relative_time_taper; \ uint4 time_taper_interval, tmp_epoch_taper_start_dbuffs; \ int4 time_remaining; \ jnl_buffer_ptr_t etjb; \ etjb = CSA->jnl->jnl_buff; \ /* Determine if we are in the time-based epoch taper */ \ relative_time_taper = 0; \ JNL_SHORT_TIME(now); \ next_epoch_time = etjb->next_epoch_time; \ if (next_epoch_time > now) /* if no db updates next_epoch_time can be in the past */ \ { \ time_remaining = next_epoch_time - now; \ /* taper during last epoch_taper_time_pct of interval */ \ time_taper_interval = etjb->epoch_interval * CSD->epoch_taper_time_pct / 128; \ if ((0 <= time_remaining) && (time_remaining < time_taper_interval)) \ relative_time_taper = MAX(MIN(129 - ((time_remaining * 128) / time_taper_interval), 128), 0); \ } \ /* Determine if we are in the journal autoswitch (space-based) epoch taper) */ \ relative_space_taper = 0; \ jnl_autoswitchlimit = CSD->autoswitchlimit; \ jnl_space_remaining = MAX(1,jnl_autoswitchlimit - (etjb->dskaddr / DISK_BLOCK_SIZE)); \ jnl_space_taper_interval = (jnl_autoswitchlimit * CSD->epoch_taper_jnl_pct) / 128; \ if (jnl_space_remaining < jnl_space_taper_interval) \ relative_space_taper = MAX(MIN(129 - ((jnl_space_remaining * 128) / jnl_space_taper_interval), 128), 0); \ relative_overall_taper = MAX(relative_time_taper, relative_space_taper); \ if (relative_overall_taper) \ { \ /* This starting point only needs to be approximate so no locking is needed */ \ if (0 == CNL->epoch_taper_start_dbuffs) \ CNL->epoch_taper_start_dbuffs = CNL->wcs_active_lvl; \ tmp_epoch_taper_start_dbuffs = MAX(1,CNL->epoch_taper_start_dbuffs); /* stable value for all calculations */ \ if ((relative_overall_taper > 64) && (relative_overall_taper < 96)) \ CNL->epoch_taper_need_fsync = TRUE; \ if (DO_FSYNC && (relative_overall_taper > 96) && CNL->epoch_taper_need_fsync) \ { \ CNL->epoch_taper_need_fsync = FALSE; \ INCR_GVSTATS_COUNTER(CSA, CNL, n_db_fsync, 1); \ fsync(FILE_INFO(REG)->fd); \ } \ FLUSH_TARGET = MIN(tmp_epoch_taper_start_dbuffs, MAX(1,(tmp_epoch_taper_start_dbuffs * \ (129 - relative_overall_taper)) / 128)); \ if (CNL->wcs_active_lvl > FLUSH_TARGET) \ { \ if (relative_overall_taper > 96) \ epoch_vector = \ (((CNL->wcs_active_lvl - FLUSH_TARGET) * 128 / FLUSH_TARGET) > 64) ? ETFAST : ETQUICK; \ else if (relative_overall_taper > 64) \ epoch_vector = \ (((CNL->wcs_active_lvl - FLUSH_TARGET) * 128 / FLUSH_TARGET) > 64) ? ETQUICK : ETSLOW; \ else \ epoch_vector = (relative_overall_taper > 32) ? ETSLOW : ETGENTLE; \ BUFFS_PER_FLUSH = CSD->n_wrt_per_flu * epoch_vector; \ } \ } \ else \ { \ CNL->epoch_taper_start_dbuffs = 0; \ CNL->epoch_taper_need_fsync = FALSE; \ } \ } MBEND /* Define pointer types for above structures that may be in shared memory and need 64 bit pointers. */ #ifdef DB64 # ifdef __osf__ # pragma pointer_size(save) # pragma pointer_size(long) # else # error UNSUPPORTED PLATFORM # endif #endif typedef bt_rec *bt_rec_ptr_t; typedef th_rec *th_rec_ptr_t; typedef th_index *th_index_ptr_t; typedef mutex_struct *mutex_struct_ptr_t; typedef pth_mutex_struct *pth_mutex_struct_ptr_t; typedef mutex_spin_parms_struct *mutex_spin_parms_ptr_t; typedef mutex_que_entry *mutex_que_entry_ptr_t; typedef node_local *node_local_ptr_t; #ifdef CRIT_USE_PTHREAD_MUTEX typedef pth_mutex_struct_ptr_t CRIT_PTR_T; #else typedef mutex_struct_ptr_t CRIT_PTR_T; #endif #define OLDEST_HIST_TN(CSA) (DBG_ASSERT(CSA->hdr) DBG_ASSERT(CSA->hdr->acc_meth != dba_mm) \ ((th_rec_ptr_t)((sm_uc_ptr_t)CSA->th_base + CSA->th_base->tnque.fl))->tn) #define SET_OLDEST_HIST_TN(CSA, TN) (DBG_ASSERT(CSA->hdr) DBG_ASSERT(CSA->hdr->acc_meth != dba_mm) \ ((th_rec_ptr_t)((sm_uc_ptr_t)CSA->th_base + CSA->th_base->tnque.fl))->tn = TN) #ifdef DB64 # ifdef __osf__ # pragma pointer_size(restore) # endif #endif #include "cdb_sc.h" bt_rec_ptr_t bt_get(block_id block); void dump_lockhist(void); void wait_for_block_flush(bt_rec *bt, block_id block); #endif fis-gtm-V7.0-005/sr_port/gdscc.h0000644000032200000250000002776414342376331015317 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #ifndef GDSCC_H_INCLUDED #define GDSCC_H_INCLUDED /* this requires gdsroot.h gtm_facilit.h fileinfo.h gdsbt.h gdsfhead.h gdir.h gdskey.h */ #include /* BIG_UA is the maximum size of a single update array specified as an unsigned quantity (usages rely on this). It is 16MB. */ #define BIG_UA (uint4)16777216 #define CDB_CW_SET_SIZE (MAX_BT_DEPTH * 3 + 1 + 2) /* CDB_CW_SET_SIZE = 36 * 3 for all the levels (including updated block, newly created sibling and possible bitmap update) * 1 extra for the root level (to take care of gds_t_write_root case) * 2 in the case of creation of a new global variable (1 index block with a * key and 1 data block * containing the key) */ #define CDB_T_CREATE 0 #define CDB_T_WRITE 1 #define CDB_T_WRITE_ROOT 2 /* The following defines the write_type for a block that is going to be updated. * GDS_WRITE_PLAIN is the default type for most updates. * GDS_WRITE_BLOCK_SPLIT is set in case of a block update due to a block split. * GDS_WRITE_KILLTN requires a little more explanation. * * The TP commit logic ("tp_tend") makes use of an optimization referred to as the "indexmod" optimization. * This optimization tries to avoid a restart in the case where a TP transaction does a SET to a data block and later finds * at TCOMMIT time that the index block which was part of the SET had been updated by a concurrent SET (or a REORG split * operation) to a different data block (that also had the same index block as an ancestor) which resulted in a block split * causing the index block to be updated. In this case there is no reason to restart. The index block could have been * modified by other operations as well (e.g. M-kill, REORG coalesce or swap operations or any DSE command or a block split * operation that caused the height of the global variable tree to increase [C9B11-001813]). In these cases, we don't want * this optimization to take effect as we can't be sure everything that was relied upon for the TP transaction was still valid. * These disallowed operations are generically referred to as "kill" type of operations. This optimization is implemented by * having a field "killtn" (name derived from "kill" type of operations) in the bt (block-table) structure for each block. * This field is assigned the same value as the "tn" whenever an index block gets updated due to one of the disallowed operations. * Otherwise it stays untouched (i.e. killtn <= tn at all times). It is the "killtn" (and not "tn") that is used in the * cdb_sc_blkmod validation check in tp_tend if we are validating a index block. Since KILLs, MUPIP REORG and/or DSE operations * are usually rare compared to SET activity, most of the cases we expect the indexmod optimization to be in effect and * therefore help reduce the # of TP restarts due to index block changes. Note that each SET/GET/KILL operation in TP goes * through an intermediate validation routine tp_hist which does the cdb_sc_blkmod validation using "tn" (not "killtn"). * Only if that passed, do we relax the commit time validation for index blocks. * * The operations that are not allowed to use this optimization (M-kill, REORG or DSE) are supposed to make sure they * set the write_type of the cw-set-element to GDS_WRITE_KILLTN. Failing to do so cause "killtn" in the bt to NOT be uptodate * which in turn can cause false validation passes (in the cdb_sc_blkmod check) causing GT.M processes to incorrectly commit * when they should not. This can lead to GT.M/application level data integrity errors. */ #define GDS_WRITE_PLAIN 0 #define GDS_WRITE_KILLTN 1 #define GDS_WRITE_BLOCK_SPLIT 2 /* blk_prior_state's last bit indicates whether the block was free before update * BLOCK FREE: 0b*******1, BLOCK NOT FREE: 0b*******0 */ #define BIT_SET_FREE(X) ((X) |= 0x00000001) #define BIT_CLEAR_FREE(X) ((X) &= 0xfffffffe) #define WAS_FREE(X) ((X) & 0x00000001) /* blk_prior_state's last but one bit indicates whether the block was recycled before update * BLOCK RECYCLED: 0b******1*, BLOCK NOT RECYCLED: 0b******0* */ #define BIT_SET_RECYCLED_AND_CLEAR_FREE(X) ((X) = ((X) & 0xfffffffc) + 0x00000002) #define BIT_CLEAR_RECYCLED_AND_SET_FREE(X) ((X) = ((X) & 0xfffffffc) + 0x00000001) #define BIT_CLEAR_RECYCLED(X) ((X) &= 0xfffffffd) #define BIT_SET_RECYCLED(X) ((X) |= 0x00000002) #define WAS_RECYCLED(X) (((X) & 0x00000002)) /* blk_prior_state's last but two bit indicates whether the block was in directory tree or global variable tree * IN_GV_TREE: 0b*****1**, IN_DIR_TREE: 0b*****0** */ #define IN_GV_TREE 4 #define IN_DIR_TREE 0 #define BIT_SET_DIR_TREE(X) ((X) &= 0xfffffffb) #define BIT_SET_GV_TREE(X) ((X) |= 0x00000004) #define KEEP_TREE_STATUS 0x00000004 /* macro to traverse to the end of an horizontal cw_set_element list */ #define TRAVERSE_TO_LATEST_CSE(x) \ { \ GBLREF uint4 dollar_tlevel; \ \ assert(dollar_tlevel); \ if (x) \ for ( ; (x)->high_tlevel; x = (x)->high_tlevel) \ ; \ } typedef uint4 block_offset; typedef int4 block_index; /* If a new mode is added to the table below, make sure pre-existing mode usages in the current codebase are examined to see * if the new mode needs to be added there as well. For example, there is code in tp_incr_commit.c and tp_incr_clean_up.c * where gds_t_create and kill_t_create are used explicitly. If the new mode is yet another *create* type, then it might need * to be added in those places as well. */ enum gds_t_mode { gds_t_noop = 0, /* there is code that initializes stuff to 0 relying on it being equal to gds_t_noop */ gds_t_create, gds_t_write, gds_t_write_recycled, /* modify a recycled block (currently only done by MUPIP REORG UPGRADE/DOWNGRADE) */ gds_t_acquired, gds_t_writemap, gds_t_committed, /* t_end relies on this particular placement */ gds_t_write_root, /* t_end relies on this being AFTER gds_t_committed */ gds_t_busy2free, /* t_end relies on this being AFTER gds_t_committed */ gds_t_recycled2free, /* t_end relies on this being AFTER gds_t_committed */ n_gds_t_op, /* tp_tend and other routines rely on this being BEFORE kill_t* modes and AFTER all gds_t_* modes */ kill_t_create, /* tp_tend relies on this being AFTER n_gds_t_op */ kill_t_write, /* tp_tend relies on this being AFTER n_gds_t_op */ }; typedef struct key_value_struct { gv_key_buf keybuf; mstr value; struct key_value_struct *next; } key_cum_value; /* Create/write set element. This is used to describe modification of a database block */ typedef struct cw_set_element_struct { trans_num tn; /* transaction number for bit maps */ sm_uc_ptr_t old_block; /* Address of 'before-image' of block to be over-written */ cache_rec_ptr_t cr; struct cw_set_element_struct *next_cw_set; struct cw_set_element_struct *prev_cw_set; /* linked list (vertical) of cw_set_elements with one link per block */ struct cw_set_element_struct *high_tlevel; struct cw_set_element_struct *low_tlevel; /* linked list (horizontal) of cw_set elements for a given block with * different transaction levels. Latest cw_set_elements (for a given block) * are inserted at the beginning of the horizontal list */ off_jnl_t jnl_freeaddr; /* journal update address */ uint4 write_type; /* can be GDS_WRITE_PLAIN or GDS_WRITE_KILLTN or GDS_WRITE_BLOCK_SPLIT * or bit-wise-or of both */ key_cum_value *recompute_list_head; /* pointer to a list of keys (with values) that need to be recomputed */ key_cum_value *recompute_list_tail; /* pointer to a list of keys (with values) that need to be recomputed */ enum gds_t_mode mode; /* Create, write, or write root */ block_id blk; /* Block number or a hint block number for creates */ unsigned char *upd_addr; /* Address of the block segment array containing update info * for this block */ unsigned char *new_buff; /* Address of a buffer created for each global mentioned inside of a * transaction more then once (for tp) */ gv_namehead *blk_target; /* address of the "gv_target" associated with a new_buff * used to invalidate clues that point to malloc'ed copies */ int4 cycle; /* When a block splits a new block must be created and the parent must be updated to * to have a record pointing to the new block. The created block number will not be * known until the last possible moment. Thus it is not possible to completely modify * the parent. The following 2 fields are used in such a case. "ins_off" tells where * the created block's number should be put in the parent block. "index" tells which * element of the create/write set is being created. */ block_offset first_off; block_offset ins_off; /* Insert block number offset */ block_offset next_off; block_index index; /* Insert block number index */ int4 reference_cnt; /* Relevant only for a bitmap block. * > 0 => # of non-bitmap blocks to be allocated in this bitmap; * < 0 => # of non-bitmap blocks to be freed up in this bitmap; * == 0 => change to bitmap block without any non-bitmap block change * Used to update csd->free_blocks when the bitmap block is built */ int4 level; /* Block level for newly created blocks */ boolean_t done; /* Has this update been done already? */ boolean_t first_copy; /* If overlaying same buffer, set if first copy needed */ /* just an optimisation - avoids copying first few bytes, if anyway * we are just overlaying the new_buff in the same transaction */ boolean_t forward_process; /* Need to process update array from front when doing kills */ uint4 t_level; /* transaction level associated with cw element, for incremental rollback */ enum db_ver ondsk_blkver; /* Actual block version from block header as it exists on disk. * If "cse->mode" is gds_t_write_root, this is uninitialized. * If "cse->mode" is gds_t_create/gds_t_acquired, this is GDSVCURR. * Otherwise, this is set to cr->ondsk_blkver (cr is got from the history). * Whenever "cse->old_block" is reset, this needs to be reset too (except * in the case of gds_t_create/gds_t_acquired). */ int4 old_mode; /* Saved copy of "cse->mode" before being reset to gds_t_committed. * Is negated at end of bg_update_phase1 to indicate (to secshr_db_clnup) * that phase1 is complete. Is negated back to the postive value at end * of bg_update_phase2. Since this can take on negative values, its type * is int4 (signed) and not enum gds_t_mode (which is unsigned). */ /* The following two fields aid in rolling back the transactions. 'undo_next_off' holds the * original next_off in the blk buffer that would be if another nested transaction was not * started. 'undo_offset' holds the offset at which 'undo_next_off' should be applied in case * of an undo due to trollback. * A 'kill' might change the next_off field at most in two places in the blk buffer. So, is * an array of size two. */ block_offset undo_next_off[2]; block_offset undo_offset[2]; uint4 blk_checksum; /*blk_prior_state:the block was in global variable tree/directory tree and was free/busy before update*/ uint4 blk_prior_state; } cw_set_element; #ifdef DEBUG GBLREF uint4 dollar_tlevel; #endif /* See comment in tp_tend where this macro is used for details */ #define IS_BG_PHASE2_COMMIT_IN_CRIT(CSE, MODE) \ ((gds_t_writemap == MODE) || (CSE->recompute_list_head && (gds_t_write == MODE))) #endif fis-gtm-V7.0-005/sr_port/gdsdbver.h0000755000032200000250000000547114342376331016026 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2021 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. * * * ****************************************************************/ #ifndef GDSDBVER_H_INCLUDED #define GDSDBVER_H_INCLUDED /* Database version related definitions */ /* Values for bytes 10 and 11 of the GDS Label (first field in the database file header) */ /* Update conditional at getfields^dumpfhead if a new version is added */ #ifdef VMS # define GDS_V20 "02" # define GDS_X21 "03" # define GDS_V23 "04" # define GDS_V24 "05" # define GDS_V25 "06" # define GDS_V254 "07" # define GDS_V255 "08" # define GDS_V30 "09" # define GDS_V40 "10" # define GDS_V50 "11" #else # define GDS_V40 "02" # define GDS_V50 "03" # define GDS_V70 "04" #endif #define GDS_CURR (GDS_CURR_NO_PAREN) #define GDS_CURR_NO_PAREN GDS_V70 #define MAX_DB_VER_LEN (2) #define GDSVCURR ((enum db_ver)(GDSVLAST - 1)) #define BLK_ID_32_VER ((enum db_ver)GDSV6p) /* The last version to use 32-bit block IDs */ #define BLKID_64 TRUE #define BLKID_32 FALSE #define IS_64_BLK_ID(X) (BLK_ID_32_VER < ((blk_hdr_ptr_t)(X))->bver) /* Return TRUE if passed block pointer uses 64-bit block_id */ /* Database major version as an enum quantity. Used to index the dbversion table in mtables.c */ enum db_ver { GDSNOVER = -1, GDSV4 = 0, GDSV5 = 1, GDSV6 = 1, /*GDSV5 and GDSV6 have same value because block format is same for these two version*/ GDSV6p = 2, /*GDSV6 upgrading to GDSV7 - has mixed index pointers and needs offsets applied */ GDSV7m = 3, /*GDSV7 upgraded from GDSV6 but has non-GDSV7 start_vbn; future versions may need to adjust asserts using */ GDSV7 = 4, /*GDSV7 switched to using 64-bit block IDs, so index block and star record format changed*/ GDSVLAST /* when changing this also update gtm_dbversion_table in mtables */ }; #define GDSMVCURR ((enum mdb_ver)(GDSMVLAST - 1)) #define BLK_ID_32_MVER ((enum mdb_ver)(GDSMV63014)) /* ideally this s.b. (GDSMVLAST - 2) but V6->V7 has some quirks */ /* Database minor version as an enum quantity. This is an ever increasing number that may skip actual * releases as it is only added to when a file-header field is added or changed or if there is a * significant API difference in the version. This number can be incremented only by adding an entry * at the end, just before GDSMVLAST. Note these entries need corresponding updates in * db_auto_upgrade.c. */ #define ENUM_ENTRY(ENUMNAME) ENUMNAME enum mdb_ver { #include "gdsdbver_sp.h" }; #undef ENUM_ENTRY #endif fis-gtm-V7.0-005/sr_port/gdsdbver_sp.h0000644000032200000250000000666514342376331016533 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2022 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. * * * ****************************************************************/ ENUM_ENTRY(GDSMV4), /* 0 - Applies to all V4 versions (no minor versions defined) */ ENUM_ENTRY(GDSMV50000), /* 1 - minor versions introduced */ ENUM_ENTRY(GDSMV51000), /* 2 - multi-site available (for databases created by V51000 - see V51000ALT */ ENUM_ENTRY(GDSMV51000ALT), /* 3 - upgrade from a previous version upgraded to this value for V51000 due to bug */ ENUM_ENTRY(GDSMV52000), /* 4 - UTF8 .. no real header changes but db contents could be unusable by previous versions */ ENUM_ENTRY(GDSMV53000), /* 5 - M-Itanium release. secshr_ops_array and index copied from sgmnt_data to node_local. */ ENUM_ENTRY(GDSMV53003), /* 6 - ZSHOW "G" release: Db Statistics rearranged in file header */ ENUM_ENTRY(GDSMV53004), /* 7 - new fields(is_encrypted, encryption_hash) for encryption */ ENUM_ENTRY(GDSMV54000), /* 8 - ew fields(db_trigger_cycle) for triggers */ ENUM_ENTRY(GDSMV54002), /* 9 - new statistical counter field for ZTRIGGER command */ ENUM_ENTRY(GDSMV54002B), /* 10 - new fields(turn_around_point, jnl_eovtn) for backward recovery */ ENUM_ENTRY(GDSMV55000), /* 11 - new fields(strm_reg_seqno, save_strm_reg_seqno, intrpt_recov_resync_strm_seqno) * for supplementary instances. * New fields(before_trunc_total_blks, after_trunc_total_blks, before_trunc_free_blocks * before_trunc_file_size) for fixing interrupted MUPIP REORG -TRUNCATE. */ ENUM_ENTRY(GDSMV60000), /* 12 - new freeze_on_fail field for anticipatory freeze; wc_blocked field moved to shared memory */ ENUM_ENTRY(GDSMV60001), /* 13 */ ENUM_ENTRY(GDSMV60002), /* 14 - new field mutex_spin_parms.mutex_que_entry_space_size for configurable mutex queue size */ ENUM_ENTRY(GDSMV62001), /* 15 - New field hasht_upgrade_needed for ^#t upgrade */ ENUM_ENTRY(GDSMV62002), /* 16 - new field defer_allocate needed for database file preallocation and ^#t upgrade */ ENUM_ENTRY(GDSMV63000), /* 17 - new field non_null_iv to indicate IV mode for encrypted blocks */ ENUM_ENTRY(GDSMV63000A), /* 18 - move fields ftok_counter_halted and access_counter_halted from fileheader to nodelocal */ ENUM_ENTRY(GDSMV63001), /* 19 - new "asyncio" option; New reservedDBFlags field */ ENUM_ENTRY(GDSMV63003), /* 20 - new field read_only to indicate a read-only database */ ENUM_ENTRY(GDSMV63007), /* 21 - reuse abandoned field for use controlled stable flush_trigger_top */ ENUM_ENTRY(GDSMV63012), /* 22 - new fullblklwrt option */ ENUM_ENTRY(GDSMV63014), /* 23 - GTM-8863 stats added to file header: GVSTATS moved, upsized */ ENUM_ENTRY(GDSMV63015), /* 24 - safety entry in case we are forced to release another V6.3 version */ ENUM_ENTRY(GDSMV70000), /* 25 - Changed GT.M to use 64-bit block numbers, required significant changes to the header*/ ENUM_ENTRY(GDSMV70001), /* 26 - GTM-9131 new statsdb_allocation option & GTM-8681 Backup Timestamp in file header */ ENUM_ENTRY(GDSMV70002), /* 27 - GTM-9426 - Automatically split database blocks based upon restarts... */ ENUM_ENTRY(GDSMVLAST) fis-gtm-V7.0-005/sr_port/gdsfhead.h0000644000032200000250000074261414342376331015777 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ #ifndef GDSFHEAD_H_INCLUDED #define GDSFHEAD_H_INCLUDED /* gdsfhead.h */ /* this requires gdsroot.h gtm_facility.h fileinfo.h gdsbt.h */ #include #include "gdsdbver.h" #include "gtm_unistd.h" #include "gtm_limits.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_time.h" #include "send_msg.h" #include "iosp.h" #include "repl_instance.h" #include "gtmcrypt.h" /* for gtmcrypt_key_t */ #include "gtm_libaio.h" #include "gtm_reservedDB.h" #include "proc_wait_stat.h" #include "region_freeze_multiproc.h" #define CACHE_STATE_OFF SIZEOF(que_ent) error_def(ERR_ACTCOLLMISMTCH); error_def(ERR_DBCRERR); error_def(ERR_DBENDIAN); error_def(ERR_DBFLCORRP); error_def(ERR_DBROLLEDBACK); error_def(ERR_ERRCALL); error_def(ERR_GVIS); error_def(ERR_GVSUBOFLOW); error_def(ERR_MMFILETOOLARGE); error_def(ERR_NCTCOLLSPGBL); error_def(ERR_OFRZAUTOREL); error_def(ERR_OFRZCRITREL); error_def(ERR_OFRZCRITSTUCK); error_def(ERR_REPLINSTACC); error_def(ERR_REPLINSTMISMTCH); error_def(ERR_REPLINSTNOSHM); error_def(ERR_REPLREQROLLBACK); error_def(ERR_SCNDDBNOUPD); error_def(ERR_SRVLCKWT2LNG); error_def(ERR_SSATTACHSHM); error_def(ERR_SSFILOPERR); error_def(ERR_STACKCRIT); error_def(ERR_STACKOFLOW); error_def(ERR_TEXT); error_def(ERR_TNTOOLARGE); error_def(ERR_TNWARN); error_def(ERR_TPRETRY); error_def(ERR_UNIMPLOP); #define FULL_FILESYSTEM_WRITE 1 #define FULL_DATABASE_WRITE 2 /* Cache record */ typedef struct cache_rec_struct { struct { sm_off_t fl; sm_off_t bl; } blkque, /* cache records whose block numbers hash to the same location */ state_que; /* cache records in same state (either wip or active) */ union { short semaphore; volatile int4 latch; /* int required for atomic swap on Unix */ /* volatile required as this value is referenced outside of the lock in db_csh_getn() */ } interlock; block_id blk; uint4 refer; /* reference bit for the clock algorithm */ enum db_ver ondsk_blkver; /* Actual block version from block header as it exists on disk (prior to any dynamic conversion that may have occurred when read in). */ /* Keep our 64 bit fields up front */ /* this point should be quad-word aligned */ trans_num dirty; /* block has been modified since last written to disk; used by bt_put, db_csh_getn * mu_rndwn_file wcs_recover, secshr_db_clnup, wr_wrtfin_all and extensively by the ccp */ trans_num flushed_dirty_tn; /* value of dirty at the time of flushing */ trans_num tn; sm_off_t bt_index; /* offset to corresponding bt_rec when this cache-record was last modified (bt->blk * and cr->blk would be identical). Maintained as a non-zero value even if cr->dirty * becomes non-zero later (due to a flush). Additionally, if this cache-record is a * twin, cr->bt_index helps distinguish the OLDER and NEWER twins. bt_index is * ZERO for the OLDER twin and NON-ZERO for the newer TWIN. */ sm_off_t buffaddr; /* offset to buffer holding actual data */ sm_off_t twin; /* If non-zero, this points to another cache-record which contains the same block but * a different state of the block. A twin for a block is created if a * process holding crit and attempting to commit updates to a block finds that block * being concurrently written to disk. If asyncio=OFF, this field is mostly zero. * In addition to the above, this field is also non-zero in an exception case where * cr->stopped is TRUE in which case the "twin" points to the cache_rec holding the * before-image for wcs_recover to backup. */ off_jnl_t jnl_addr; /* offset from bg_update to prevent wcs_wtstart from writing a block ahead of the journal */ boolean_t stopped; /* TRUE indicates to wcs_recover that secshr_db_clnup built the block */ global_latch_t rip_latch; /* for read_in_progress - note contains extra 16 bytes for HPPA. Usage note: this latch is used on those platforms where read_in_progress is not directly updated by atomic routines/instructions. As such there needs be no cache line padding between this field and read_in_progress. */ uint4 data_invalid; /* non-zero pid from bg_update indicates we are in the middle of a "gvcst_blk_build" * and so the block contents are not clean. secshr_db_clnup/wcs_recover check this * field and take appropriate action to recover this cr. */ int4 epid; /* set by wcs_wtstart to id the write initiator; cleared by wcs_wtstart/wcs_wtfini * used by t_commit_cleanup, secshr_db_clnup and wcs_recover */ int4 cycle; /* relative stamp indicates changing versions of the block for concurrency checking */ int4 r_epid; /* set by db_csh_getn, cleared by t_qread, bg_update, wcs_recover or secshr_db_clnup * used to check for process leaving without releasing the buffer * must be word aligned on the VAX */ struct aiocb aiocb; /* Used for asynchronous I/O if BG access method and csd->asyncio is TRUE */ CNTR4DCL(read_in_progress, 10); /* -1 for normal and 0 for rip used by t_qread and checked by others */ uint4 in_tend; /* non-zero pid from bg_update indicates secshr_db_clnup should finish update */ uint4 in_cw_set; /* non-zero pid from t_end, tp_tend or bg_update protects block from db_csh_getn; * returned to 0 by t_end, tp_tend or t_commit_cleanup */ bool wip_is_encr_buf;/* TRUE if cache-record is WIP (write-in-progress) and its encrypted global buffer * (not unencrypted global buffer) was used to issue the write in "wcs_wtstart". * Used by "wcs_wtfini" to reissue the write. This field is not maintained * when the cr is not in WIP status i.e. this is used only by the * DB_LSEEKWRITEASYNCSTART and DB_LSEEKWRITEASYNCRESTART macros. */ bool backup_cr_is_twin; /* TRUE if cr corresponding to the before-image (used by BG_BACKUP_BLOCK) * is the twin of this cache-record. */ bool aio_issued; /* set to TRUE after the asyncio has been issued in wcs_wtstart. * set to FALSE before cr->epid is set to a non-zero value in wcs_wtstart. * ONLY if epid is non-zero AND aio_issued is TRUE, can the write be considered * in progress. This is used by wcs_recover to decide whether to place a cr into * the active or wip queue. */ bool needs_first_write; /* If this block needs to be written to disk for the first time, * note it (only applicable for fullblockwrites) */ } cache_rec; /* A note about cache line separation of the latches contained in these blocks. Because this block is duplicated many (potentially tens+ of) thousands of times in a running system, we have decided against providing cacheline padding so as to force each cache record into a separate cacheline (due to it containing a latch and/or atomic counter field) to prevent processes from causing interference with each other. We decided that the probability of two processes working on adjacent cache records simultaneously was low enough that the interference was minimal whereas increasing the cache record size to prevent that interference could cause storage problems on some platforms where processes are already running near the edge. */ /* cache_state record */ typedef struct { struct { sm_off_t fl; sm_off_t bl; } state_que; /* WARNING from this point, this structure must be identical to a cache_rec */ union { short semaphore; volatile int4 latch; /* int required for atomic swap on Unix */ /* volatile required as this value is referenced outside of the lock in db_csh_getn() */ } interlock; block_id blk; uint4 refer; /* reference bit for the LRU algorithm */ enum db_ver ondsk_blkver; /* Actual block version from block header as it exists on disk (prior to any dynamic conversion that may have occurred when read in). */ /* Keep our 64 bit fields up front */ /* this point should be quad-word aligned */ trans_num dirty; /* block has been modified since last written to disk; used by bt_put, db_csh_getn * mu_rndwn_file wcs_recover, secshr_db_clnup, wr_wrtfin_all and extensively by the ccp */ trans_num flushed_dirty_tn; /* value of dirty at the time of flushing */ trans_num tn; sm_off_t bt_index; /* offset to corresponding bt_rec when this cache-record was last modified (bt->blk * and cr->blk would be identical). Maintained as a non-zero value even if cr->dirty * becomes non-zero later (due to a flush). Additionally, if this cache-record is a * twin, cr->bt_index helps distinguish the OLDER and NEWER twins. bt_index is * ZERO for the OLDER twin and NON-ZERO for the newer TWIN. */ sm_off_t buffaddr; /* offset to buffer holding actual data*/ sm_off_t twin; /* If non-zero, this points to another cache-record which contains the same block but * a different state of the block. A twin for a block is created if a * process holding crit and attempting to commit updates to a block finds that block * being concurrently written to disk. If asyncio=OFF, this field is mostly zero. * In addition to the above, this field is also non-zero in an exception case where * cr->stopped is TRUE in which case the "twin" points to the cache_rec holding the * before-image for wcs_recover to backup. */ off_jnl_t jnl_addr; /* offset from bg_update to prevent wcs_wtstart from writing a block ahead of the journal */ boolean_t stopped; /* TRUE indicates to wcs_recover that secshr_db_clnup built the block */ global_latch_t rip_latch; /* for read_in_progress - note contains extra 16 bytes for HPPA. Usage note: this latch is used on those platforms where read_in_progress is not directly updated by atomic routines/instructions. As such there needs be no cache line padding between this field and read_in_progress. */ uint4 data_invalid; /* non-zero pid from bg_update indicates t_commit_cleanup/wcs_recover should invalidate */ int4 epid; /* set by wcs_start to id the write initiator; cleared by wcs_wtstart/wcs_wtfini * used by t_commit_cleanup, secshr_db_clnup and wcs_recover */ int4 cycle; /* relative stamp indicates changing versions of the block for concurrency checking */ int4 r_epid; /* set by db_csh_getn, cleared by t_qread, bg_update, wcs_recover or secshr_db_clnup * used to check for process leaving without releasing the buffer * must be word aligned on the VAX */ struct aiocb aiocb; /* Used for asynchronous I/O if BG access method and csd->asyncio is TRUE */ CNTR4DCL(read_in_progress, 10); /* -1 for normal and 0 for rip used by t_qread and checked by others */ uint4 in_tend; /* non-zero pid from bg_update indicates secshr_db_clnup should finish update */ uint4 in_cw_set; /* non-zero pid from t_end, tp_tend or bg_update protects block from db_csh_getn; * returned to 0 by t_end, tp_tend or t_commit_cleanup */ bool wip_is_encr_buf;/* TRUE if cache-record is WIP (write-in-progress) and its encrypted global buffer * (not unencrypted global buffer) was used to issue the write in "wcs_wtstart". * Used by "wcs_wtfini" to reissue the write. This field is not maintained * when the cr is not in WIP status i.e. this is used only by the * DB_LSEEKWRITEASYNCSTART and DB_LSEEKWRITEASYNCRESTART macros. */ bool backup_cr_is_twin; /* TRUE if cr corresponding to the before-image (used by BG_BACKUP_BLOCK) * is the twin of this cache-record. */ bool aio_issued; /* set to TRUE after the asyncio has been issued in wcs_wtstart. * set to FALSE before cr->epid is set to a non-zero value in wcs_wtstart. * ONLY if epid is non-zero AND aio_issued is TRUE, can the write be considered * in progress. This is used by wcs_recover to decide whether to place a cr into * the active or wip queue. */ bool needs_first_write; /* If this block needs to be written to disk for the first time, * note it (only applicable for fullblockwrites) */ } cache_state_rec; #define CR_BLKEMPTY -1 #define MBR_BLKEMPTY -1 #define FROZEN_BY_ROOT (uint4)(0xFFFFFFFF) #define BACKUP_NOT_IN_PROGRESS 0x7FFFFFFFFFFFFFFF #define DB_CSH_RDPOOL_SZ 0x20 /* These many non-dirty buffers exist at all points in time in shared memory */ typedef struct { cache_que_head cacheq_wip, /* write-in-progress queue */ cacheq_active; /* active queue */ cache_rec cache_array[1]; /*the first cache record*/ } cache_que_heads; /* Define pointer types to some previously defined structures */ #ifdef DB64 # ifdef __osf__ # pragma pointer_size(save) # pragma pointer_size(long) # else # error UNSUPPORTED PLATFORM # endif #endif typedef cache_que_head *cache_que_head_ptr_t; typedef cache_rec *cache_rec_ptr_t; typedef cache_rec **cache_rec_ptr_ptr_t; typedef cache_state_rec *cache_state_rec_ptr_t; typedef cache_que_heads *cache_que_heads_ptr_t; gtm_uint64_t verify_queue(que_head_ptr_t qhdr); #ifdef DB64 # ifdef __osf__ # pragma pointer_size(restore) # endif #endif #ifdef DEBUG_QUEUE #define VERIFY_QUEUE(base) (void)verify_queue(base) #define VERIFY_QUEUE_LOCK(base,latch,CSA) (void)verify_queue_lock(base,latch,CSA) #else #define VERIFY_QUEUE(base) #define VERIFY_QUEUE_LOCK(base,latch,CSA) #endif #define BLK_ZERO_OFF(start_vbn) (((start_vbn) - 1) * DISK_BLOCK_SIZE) #ifdef GTM64 # define CHECK_LARGEFILE_MMAP(REG, MMAP_SZ) #else # define CHECK_LARGEFILE_MMAP(REG, MMAP_SZ) \ { \ assert(SIZEOF(gtm_uint64_t) == SIZEOF(MMAP_SZ)); \ assert(0 < MMAP_SZ); \ if (MAXUINT4 < (gtm_uint64_t)(MMAP_SZ)) \ rts_error_csa(CSA_ARG(REG2CSA(REG)) VARLSTCNT(6) ERR_MMFILETOOLARGE, 4, REG_LEN_STR(REG), \ DB_LEN_STR(REG)); \ } # endif # define MMAP_FD(FD, SIZE, OFFSET, READ_ONLY) mmap((caddr_t)NULL, SIZE, MM_PROT_FLAGS(READ_ONLY), GTM_MM_FLAGS, FD, OFFSET) # define MSYNC(BEGPTR, ENDPTR) (BEGPTR ? DBG_ASSERT(BEGPTR < ENDPTR) msync(BEGPTR, (ENDPTR - BEGPTR), MS_SYNC) \ : 0) # define MM_PROT_FLAGS(READ_ONLY) (READ_ONLY ? PROT_READ : (PROT_READ | PROT_WRITE)) # define MM_BASE_ADDR(CSA) (sm_uc_ptr_t)CSA->db_addrs[0] # define SET_MM_BASE_ADDR(CSA, CSD) # ifdef _AIX # define MEM_MAP_SYSCALL "shmat()" # define MEM_UNMAP_SYSCALL "shmdt()" # else # define MEM_MAP_SYSCALL "mmap()" # define MEM_UNMAP_SYSCALL "munmap()" # endif #define GVKEY_INIT(GVKEY, KEYSIZE) \ ((GVKEY) = gvkey_init(GVKEY, KEYSIZE)) #define GVKEY_FREE_IF_NEEDED(GVKEY) \ MBSTART { \ if (NULL != GVKEY) \ { \ free(GVKEY); \ GVKEY = NULL; \ } \ } MBEND #define GVKEYSIZE_INIT_IF_NEEDED \ MBSTART { \ int keySIZE; \ \ GBLREF int4 gv_keysize; \ GBLREF gv_key *gv_altkey; \ GBLREF gv_key *gv_currkey; \ \ if (!gv_keysize) \ { \ keySIZE = DBKEYSIZE(MAX_KEY_SZ); \ /* Have space to store at least MAX_MIDENT_LEN bytes as otherwise name-level $order \ * (see op_gvorder/op_zprevious) could have buffer overflow issues in gv_currkey->base. \ * Do ROUND_UP2(x,4) to keep an assert in GVKEY_INIT macro happy. \ */ \ assert((MAX_MIDENT_LEN + 3) < keySIZE); \ assert(keySIZE); \ gv_keysize = keySIZE; \ GVKEY_INIT(gv_currkey, keySIZE); \ GVKEY_INIT(gv_altkey, keySIZE); \ } else \ { \ assert((NULL != gv_currkey) && (NULL != gv_altkey) && gv_keysize \ && (DBKEYSIZE(MAX_KEY_SZ) == gv_keysize) \ && (gv_keysize == gv_currkey->top) && (gv_keysize == gv_altkey->top)); \ } \ } MBEND /* Transform KEY to look at the immediately next key at the same subscript level as input KEY (like $order(KEY)). * For example if input KEY is ^x(1,2), come up with a key ^x(1,2++). */ #define GVKEY_INCREMENT_ORDER(KEY) \ MBSTART { \ int end; \ unsigned char *base = KEY->base; \ \ end = KEY->end; \ assert(KEY_DELIMITER == base[end - 1]); \ assert(KEY_DELIMITER == base[end]); \ assert(end + 1 < KEY->top); \ base[end - 1] = 1; \ base[end + 1] = KEY_DELIMITER; \ KEY->end = end + 1; \ } MBEND /* Undo work done by GVKEY_INCREMENT_ORDER */ #define GVKEY_UNDO_INCREMENT_ORDER(KEY) \ MBSTART { \ int end; \ \ assert(1 < KEY->end); \ end = KEY->end - 1; \ assert(1 == KEY->base[end - 1]); \ assert(KEY_DELIMITER == KEY->base[end]); \ assert(KEY_DELIMITER == KEY->base[end + 1]); \ assert(end + 1 < KEY->top); \ KEY->base[end - 1] = KEY_DELIMITER; \ KEY->base[end + 0] = KEY_DELIMITER; \ KEY->end = end; \ } MBEND /* Transform KEY to look at the immediately previous key at the same subscript level as input KEY (like $order(KEY)). * For example if input KEY is ^x(1,2), come up with a key ^x(1,2--). */ #define GVKEY_DECREMENT_ORDER(KEY) \ MBSTART { \ int end; \ \ end = KEY->end; \ assert(1 < end); \ assert(KEY_DELIMITER == KEY->base[end - 1]); \ assert(KEY_DELIMITER == KEY->base[end]); \ assert(0xFF != KEY->base[end - 2]); \ assert((end + 1) < KEY->top); \ KEY->base[end - 2] -= 1; \ KEY->base[end - 1] = 0xFF; \ KEY->base[end + 1] = KEY_DELIMITER; \ KEY->end = end + 1; \ } MBEND /* Undo work done by GVKEY_DECREMENT_ORDER */ #define GVKEY_UNDO_DECREMENT_ORDER(KEY) \ MBSTART { \ int end; \ \ assert(2 < KEY->end); \ end = KEY->end - 1; \ assert(0xFF == KEY->base[end - 1]); \ assert(KEY_DELIMITER != KEY->base[end - 2]); \ assert(KEY_DELIMITER == KEY->base[end]); \ assert(KEY_DELIMITER == KEY->base[end + 1]); \ assert((end + 1) < KEY->top); \ KEY->base[end - 2] += 1; \ KEY->base[end - 1] = KEY_DELIMITER; \ KEY->end = end; \ } MBEND /* Transform KEY to look at the immediately next KEY at any subscript level (like $query(KEY)). * For example if input KEY is ^x(1,2), come up with a key ^x(1,2,1) assuming that is the next node. */ #define GVKEY_INCREMENT_QUERY(KEY) \ MBSTART { \ int end; \ \ end = KEY->end; \ assert(KEY_DELIMITER == KEY->base[end - 1]); \ assert(KEY_DELIMITER == KEY->base[end]); \ assert(end + 2 < KEY->top); \ KEY->base[end] = 1; \ KEY->base[end + 1] = KEY_DELIMITER; \ KEY->base[end + 2] = KEY_DELIMITER; \ KEY->end += 2; \ } MBEND /* Transform KEY to look at the immediately previous key at the PREVIOUS subscript level as input KEY. * For example if input KEY is ^x(1,2), come up with a key ^x(1++). */ #define GVKEY_INCREMENT_PREVSUBS_ORDER(KEY) \ MBSTART { \ assert(KEY->prev); \ assert(KEY->end > KEY->prev); \ assert(KEY_DELIMITER == KEY->base[KEY->prev - 1]); \ assert(KEY_DELIMITER != KEY->base[KEY->prev]); \ assert(KEY_DELIMITER == KEY->base[KEY->end - 1]); \ assert(KEY_DELIMITER == KEY->base[KEY->end]); \ assert(KEY->end + 1 <= KEY->top); \ KEY->base[KEY->prev - 1] = 1; \ } MBEND /* Undo work done by GVKEY_INCREMENT_PREVSUBS_ORDER */ #define GVKEY_UNDO_INCREMENT_PREVSUBS_ORDER(KEY) \ MBSTART { \ assert(KEY->prev); \ assert(KEY->end > KEY->prev); \ assert(1 == KEY->base[KEY->prev - 1]); \ assert(KEY_DELIMITER != KEY->base[KEY->prev]); \ assert(KEY_DELIMITER == KEY->base[KEY->end - 1]); \ assert(KEY_DELIMITER == KEY->base[KEY->end]); \ assert(KEY->end + 1 <= KEY->top); \ KEY->base[KEY->prev - 1] = KEY_DELIMITER; \ } MBEND /* Transform KEY to look at the "" subscript at same subscript level as input KEY. * For example if input KEY is ^x(1,2), come up with a key ^x(1,""). */ #define GVKEY_SET_SUBS_ZPREVIOUS(KEY, SAVECH) \ MBSTART { \ assert(KEY->prev); \ assert(KEY->end > KEY->prev); \ assert(KEY_DELIMITER == KEY->base[KEY->prev - 1]); \ assert(KEY_DELIMITER != KEY->base[KEY->prev]); \ assert(KEY_DELIMITER == KEY->base[KEY->end - 1]); \ assert(KEY_DELIMITER == KEY->base[KEY->end]); \ assert(KEY->end + 1 <= KEY->top); \ SAVECH = KEY->base[KEY->prev]; \ KEY->base[KEY->prev] = 1; \ } MBEND /* Undo work done by GVKEY_SET_SUBS_ZPREVIOUS */ #define GVKEY_UNDO_SET_SUBS_ZPREVIOUS(KEY, SAVECH) \ MBSTART { \ assert(KEY->prev); \ assert(KEY->end > KEY->prev); \ assert(1 == KEY->base[KEY->prev]); \ assert(KEY_DELIMITER == KEY->base[KEY->prev - 1]); \ assert(KEY_DELIMITER == KEY->base[KEY->end - 1]); \ assert(KEY_DELIMITER == KEY->base[KEY->end]); \ assert(KEY->end + 1 <= KEY->top); \ KEY->base[KEY->prev] = SAVECH; \ } MBEND /* This macro is used whenever we have found a MAP where a key KEYBASE maps to (using "gv_srch_map*" functions). * If the MAP entry one before EXACTLY matches the KEY, then some callers might want to see MAP-1 instead of MAP. * It is upto the caller to decide which one they want. By default they get MAP from gv_srch_map and can invoke * this macro to get MAP-1 in that special case. */ #define BACK_OFF_ONE_MAP_ENTRY_IF_EDGECASE(KEYBASE, KEYLEN, MAP) \ MBSTART { \ if (!memcmp(KEYBASE, ((MAP) - 1)->gvkey.addr, KEYLEN)) \ { /* KEYBASE starts at "MAP" which means, all keys of interest (just one before \ * the incremented key) can never map to "MAP" so back off one map entry. \ */ \ (MAP)--; \ OPEN_BASEREG_IF_STATSREG(MAP); \ } \ } MBEND /* The below macro is invoked whenever we are about to return a map entry that corresponds to a statsdb region. * This means the caller is about to use this map entry to access a statsdb region. But it is possible this statsdb * region does not map to a real database file (e.g. if the corresponding baseDBreg has NOSTATS defined in the .gld * OR if the basedb has NOSTATS defined in its file header). In all such cases, we do not want the caller to error out * so we repoint the statsdb map entry to point to a basedb and since we don't expect to see ^%YGS nodes in the basedb * the access will return without any error (e.g. $GET will return "" etc. but no error). */ #define OPEN_BASEREG_IF_STATSREG(MAP) \ { \ gd_region *baseDBreg, *statsDBreg; \ \ statsDBreg = MAP->reg.addr; \ if (IS_STATSDB_REGNAME(statsDBreg)) \ { \ STATSDBREG_TO_BASEDBREG(statsDBreg, baseDBreg); \ if (!baseDBreg->open) \ gv_init_reg(baseDBreg, NULL); \ if (!statsDBreg->open && IS_ACC_METH_BG_OR_MM(baseDBreg->dyn.addr->acc_meth)) \ { /* statsDB did not get opened as part of baseDB open above. Possible if gtm_statshare \ * is not set to 1. But user could still do a ZWR ^%YGS which would try to open \ * statsDB in caller (who is not equipped to handle errors) so do the open of the \ * statsDB now and silently handle errors like is done in "gvcst_init". Any errors \ * will cause the baseDB to have NOSTATS set which would make the map entry point to \ * the baseDB thereby avoiding any user-visible errors even if they do ZWR ^%YGS. \ * Indicate we want to do just "gvcst_init" of the statsDB, not the ^%YGS addition \ * by passing DO_STATSDB_INIT_FALSE. \ */ \ gvcst_init_statsDB(baseDBreg, DO_STATSDB_INIT_FALSE); \ } \ /* If baseDB has NOSTATS defined in its file header, repoint map entry to non-statsDB region. \ * This will prevent FILENOTFOUND errors on the statsdb file (.gst file) on access to ^%YGS \ * nodes that map to non-existent statsdb regions. \ */ \ if (RDBF_NOSTATS & baseDBreg->reservedDBFlags) \ MAP->reg.addr = baseDBreg; \ } \ } /* The below macro is modeled pretty much like OPEN_BASEREG_IF_STATSREG except that this asserts that the * other macro is not needed. And that whatever that macro sets up is already set up that way. */ #ifdef DEBUG #define ASSERT_BASEREG_OPEN_IF_STATSREG(MAP) \ { \ gd_region *baseDBreg, *statsDBreg; \ \ statsDBreg = MAP->reg.addr; \ if (IS_STATSDB_REGNAME(statsDBreg)) \ { \ STATSDBREG_TO_BASEDBREG(statsDBreg, baseDBreg); \ assert(baseDBreg->open); \ assert(statsDBreg->open); \ } \ } #else #define ASSERT_BASEREG_OPEN_IF_STATSREG(MAP) #endif /* Calculate the # of subscripts in "KEY" and stores that in "NSUBS" */ #define GET_NSUBS_IN_GVKEY(PTR, LEN, NSUBS) \ MBSTART { \ unsigned char *ptr, *ptrtop; \ int nSubs; \ \ ptr = (unsigned char *)PTR; \ ptrtop = ptr + LEN; \ assert(ptr < ptrtop); \ nSubs = 0; \ for ( ; ptr < ptrtop; ptr++) \ if (KEY_DELIMITER == *ptr) \ nSubs++; \ NSUBS = nSubs; \ } MBEND #define WAS_OPEN_TRUE TRUE #define WAS_OPEN_FALSE FALSE #define SKIP_ASSERT_TRUE TRUE #define SKIP_ASSERT_FALSE FALSE /* Below macro sets open, opening and was_open fields of a given region after the corresponding * database for that region is opened. Also, if the region was not already open, the macro * invokes GVKEYSIZE_INIT_IF_NEEDED to allocate gv_currkey/gv_altkey if not already done. */ #define SET_REGION_OPEN_TRUE(REG, WAS_OPEN) \ MBSTART { \ DEBUG_ONLY(GBLREF int4 gv_keysize;) \ \ assert(!REG->was_open); \ assert(!REG->open); \ REG->open = TRUE; \ REG->opening = FALSE; \ if (WAS_OPEN) \ { \ REG->was_open = TRUE; \ TREF(was_open_reg_seen) = TRUE; \ assert(DBKEYSIZE(REG->max_key_size) <= gv_keysize); \ } else \ GVKEYSIZE_INIT_IF_NEEDED; /* sets up "gv_keysize", "gv_currkey" and "gv_altkey" in sync */ \ } MBEND #define REG_ACC_METH(REG) (REG->dyn.addr->acc_meth) #define IS_REG_BG_OR_MM(REG) IS_ACC_METH_BG_OR_MM(REG_ACC_METH(REG)) #define IS_CSD_BG_OR_MM(CSD) IS_ACC_METH_BG_OR_MM(CSD->acc_meth) #define IS_ACC_METH_BG_OR_MM(ACC_METH) ((dba_bg == ACC_METH) || (dba_mm == ACC_METH)) #define SET_CSA_DIR_TREE(csa, keysize, reg) \ MBSTART { \ if (NULL == csa->dir_tree) \ { \ assert(IS_REG_BG_OR_MM(reg)); \ csa->dir_tree = targ_alloc(keysize, NULL, reg); \ GTMTRIG_ONLY(assert(NULL == csa->hasht_tree)); \ } else \ assert((csa->dir_tree->gd_csa == csa) && (DIR_ROOT == csa->dir_tree->root)); \ } MBEND #define FREE_CSA_DIR_TREE(csa) \ MBSTART { \ sgmnt_addrs *lcl_csa; \ gv_namehead *dir_tree, *hasht_tree; \ \ lcl_csa = csa; \ GTMTRIG_ONLY( \ hasht_tree = lcl_csa->hasht_tree; \ if (NULL != hasht_tree) \ { \ assert(hasht_tree->gd_csa == csa); \ /* assert that TARG_FREE_IF_NEEDED will happen below */ \ assert(1 == hasht_tree->regcnt); \ TARG_FREE_IF_NEEDED(hasht_tree); \ lcl_csa->hasht_tree = NULL; \ } \ ) \ dir_tree = lcl_csa->dir_tree; \ assert(NULL != dir_tree); \ /* assert that TARG_FREE_IF_NEEDED will happen below */ \ assert(1 == dir_tree->regcnt); \ TARG_FREE_IF_NEEDED(dir_tree); \ lcl_csa->dir_tree = NULL; \ } MBEND #define ADD_TO_GVT_PENDING_LIST_IF_REG_NOT_OPEN(REG, GVT_PTR, GVT_PTR2) \ MBSTART { \ gvt_container *gvtc; \ DEBUG_ONLY(gv_namehead *gvt;) \ \ GBLREF buddy_list *gvt_pending_buddy_list; \ GBLREF gvt_container *gvt_pending_list; \ \ /* For dba_cm or dba_user, don't add to pending list because those regions \ * will never be opened by the client process (this process). \ */ \ if (!REG->open && IS_REG_BG_OR_MM(REG)) \ { /* Record list of all gv_targets that have been allocated BEFORE the \ * region has been opened. Once the region gets opened, we will re-examine \ * this list and reallocate them (if needed) since they have now been \ * allocated using the region's max_key_size value which could potentially \ * be different from the max_key_size value in the corresponding database \ * file header. \ */ \ assert(NULL != gvt_pending_buddy_list); /* should have been allocated by caller */ \ DEBUG_ONLY(gvt = *GVT_PTR;) \ assert(NULL == is_gvt_in_pending_list(gvt)); /* do not add duplicates */ \ gvtc = (gvt_container *)get_new_free_element(gvt_pending_buddy_list); \ gvtc->gvt_ptr = GVT_PTR; \ gvtc->gvt_ptr2 = GVT_PTR2; \ gvtc->gd_reg = REG; \ gvtc->next_gvtc = (struct gvt_container_struct *)gvt_pending_list; \ gvt_pending_list = gvtc; \ } \ } MBEND #define PROCESS_GVT_PENDING_LIST(GREG, CSA) \ MBSTART { \ GBLREF gvt_container *gvt_pending_list; \ \ if (NULL != gvt_pending_list) \ { /* Now that the region has been opened, check if there are any gv_targets that were \ * allocated for this region BEFORE the open. If so, re-allocate them if necessary. \ */ \ process_gvt_pending_list(GREG, CSA); \ } \ } MBEND #define TARG_FREE_IF_NEEDED(GVT) \ MBSTART { \ GVT->regcnt--; \ if (!GVT->regcnt) \ targ_free(GVT); \ } MBEND #define T_COMMIT_CRIT_PHASE0 1 /* csa->t_commit_crit gets set to this when reserving space for jnl record writing. * The actual journal write happens after releasing crit in phase2 of commit for BG. */ #define T_COMMIT_CRIT_PHASE1 2 /* csa->t_commit_crit gets set to this during bg_update_phase1 for BG */ #define T_COMMIT_CRIT_PHASE2 3 /* csa->t_commit_crit gets set to this during bg_update_phase2 for BG */ /* macro to check if we hold crit or are committing (with or without crit) */ #define T_IN_CRIT_OR_COMMIT(CSA) ((CSA)->now_crit || (CSA)->t_commit_crit) /* Macro to check if we hold crit or are committing (with or without crit) or are in wcs_wtstart for this region. * This is used in timer handling code to determine if it is ok to interrupt. We do not want to interrupt if holding * crit or in the midst of commit or in wcs_wtstart (in the last case, we could be causing another process HOLDING CRIT * on the region to wait in bg_update_phase1 if we hold the write interlock). */ #define T_IN_CRIT_OR_COMMIT_OR_WRITE(CSA) (T_IN_CRIT_OR_COMMIT(CSA) || (CSA)->in_wtstart) /* Macro to check if we are committing (with or without crit) or are in wcs_wtstart for this region. This is used in timer * handling code to determine if it is ok to interrupt. We do not want to interrupt if we are in the midst of commit or * in wcs_wtstart (in the second case, we could be causing another process HOLDING CRIT on the region to wait in * bg_update_phase1 if we hold the write interlock). */ #define T_IN_COMMIT_OR_WRITE(CSA) ((CSA)->t_commit_crit || (CSA)->in_wtstart) /* Macro to check if a database commit is past the point where it can be successfully rolled back. * If t_commit_crit is T_COMMIT_CRIT_PHASE2, "bg_update_phase2" could have happened on at least one block * and so we cannot roll back easily. If t_commit_crit is T_COMMIT_CRIT_PHASE1, "bg_update_phase1" could * have started on at least one block and so we cannot roll back easily. If it is T_COMMIT_CRIT_PHASE0, * only journal records have been written to the journal buffer and those should be easily rolled back. */ #define T_UPDATE_UNDERWAY(CSA) (T_COMMIT_CRIT_PHASE1 <= (CSA)->t_commit_crit) /* Below macro sets csa->t_commit_crit to T_COMMIT_CRIT_PHASE1 to signal start of phase1 of db commit. * In addition, it also sets cnl->update_underway_tn which indicates to "mutex_salvage" that in case this process * gets kill -9ed between now and end of commit, and replication is turned on in this region, the * csa->jnl->jnl_buff->phase2_commit_array[] entry corresponding to this commit should be set up such that * when "jnl_phase2_salvage" is called later, it will write a JRT_NULL record on behalf of this transaction. * If a kill -9 happens between begin of commit and now, then a JRT_INCTN record will instead be written. * For non-journaled regions, cnl->update_underway_tn does not matter currently. */ #define SET_T_COMMIT_CRIT_PHASE1(CSA, CNL, TN) \ MBSTART { \ CSA->t_commit_crit = T_COMMIT_CRIT_PHASE1; /* phase1 : lock database buffers */ \ CNL->update_underway_tn = TN; \ } MBEND #define SET_REG_SEQNO(CSA, SEQNO, SUPPLEMENTARY, STRM_INDEX, NEXT_STRM_SEQNO, SKIP_ASSERT) \ { \ assert(SKIP_ASSERT || (CSA->hdr->reg_seqno < SEQNO)); \ CSA->hdr->reg_seqno = SEQNO; \ if (SUPPLEMENTARY) \ CSA->hdr->strm_reg_seqno[STRM_INDEX] = NEXT_STRM_SEQNO; \ } /* the file header has relative pointers to its data structures so each process will malloc * one of these and fill it in with absolute pointers upon file initialization. */ #define GDS_REL2ABS(x) (((sm_uc_ptr_t)cs_addrs->mlkctl + (sm_off_t)(x))) #define GDS_ABS2REL(x) (sm_off_t)(((sm_uc_ptr_t)(x) - (sm_uc_ptr_t)cs_addrs->mlkctl)) #define GDS_ANY_REL2ABS(CSA, x) (((sm_uc_ptr_t)((CSA)->mlkctl) + (sm_off_t)(x))) #define GDS_ANY_ABS2REL(CSA, x) (sm_off_t)(((sm_uc_ptr_t)(x) - (sm_uc_ptr_t)(CSA)->mlkctl)) #define GDS_ANY_ENCRYPTGLOBUF(w,x) ((sm_uc_ptr_t)(w) + (sm_off_t)(x->nl->encrypt_glo_buff_off)) #define ASSERT_IS_WITHIN_SHM_BOUNDS(ptr, csa) \ assert((NULL == (ptr)) || (((ptr) >= csa->db_addrs[0]) && ((0 == csa->db_addrs[1]) || ((ptr) < csa->db_addrs[1])))) #ifdef DEBUG #define DBG_ENSURE_PTR_IS_VALID_GLOBUFF(CSA, CSD, PTR) \ MBSTART { \ cache_rec_ptr_t cache_start; \ long bufindx; \ sm_uc_ptr_t bufstart; \ \ cache_start = &(CSA)->acc_meth.bg.cache_state->cache_array[0]; \ cache_start += CSD->bt_buckets; \ bufstart = (sm_uc_ptr_t)GDS_ANY_REL2ABS((CSA), cache_start->buffaddr); \ assert((PTR) >= bufstart); \ bufindx = (PTR - bufstart) / CSD->blk_size; \ assert(bufindx < CSD->n_bts); \ assert((bufstart + (bufindx * CSD->blk_size)) == (PTR)); \ } MBEND #define DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(CSA, CSD, PTR) \ MBSTART { \ cache_rec_ptr_t cache_start; \ long bufindx; \ sm_uc_ptr_t bufstart; \ \ cache_start = &(CSA)->acc_meth.bg.cache_state->cache_array[0]; \ cache_start += CSD->bt_buckets; \ bufstart = (sm_uc_ptr_t)GDS_ANY_REL2ABS((CSA), cache_start->buffaddr); \ bufstart += (gtm_uint64_t)CSD->blk_size * CSD->n_bts; \ assert((PTR) >= bufstart); \ bufindx = (PTR - bufstart) / CSD->blk_size; \ assert(bufindx < CSD->n_bts); \ assert((bufstart + (bufindx * (gtm_uint64_t)CSD->blk_size)) == (PTR)); \ } MBEND #define DBG_ENSURE_OLD_BLOCK_IS_VALID(cse, is_mm, csa, csd) \ MBSTART { \ cache_rec_ptr_t cache_start; \ long bufindx; \ sm_uc_ptr_t bufstart; \ GBLREF boolean_t dse_running, write_after_image; \ GBLREF uint4 process_id; \ \ assert((gds_t_write != cse->mode) && (gds_t_write_recycled != cse->mode) && gds_t_writemap != cse->mode \ || (NULL != cse->old_block)); /* don't miss writing a PBLK */ \ if (NULL != cse->old_block) \ { \ if (!is_mm) \ { \ cache_start = &csa->acc_meth.bg.cache_state->cache_array[0]; \ cache_start += csd->bt_buckets; \ bufstart = (sm_uc_ptr_t)GDS_ANY_REL2ABS(csa, cache_start->buffaddr); \ bufindx = (cse->old_block - bufstart) / csd->blk_size; \ assert(bufindx < csd->n_bts); \ assert(cse->blk == cache_start[bufindx].blk); \ assert(dse_running || write_after_image || (process_id == cache_start[bufindx].in_cw_set)); \ } else \ assert(cse->old_block == MM_BASE_ADDR(csa) + (off_t)cse->blk * csd->blk_size); \ } \ } MBEND /* Check if a given address corresponds to a global buffer (BG) in database shared memory AND if * we are in phase2 of commit. If so check whether the corresponding cache-record is pinned. * Used by gvcst_blk_build to ensure the update array points to valid contents even though we don't hold crit. */ #define DBG_BG_PHASE2_CHECK_CR_IS_PINNED(csa, seg) \ MBSTART { \ cache_rec_ptr_t cache_start; \ long bufindx; \ sm_uc_ptr_t bufstart, bufend, bufaddr; \ \ GBLREF uint4 process_id; \ \ if ((seg)->len && (T_COMMIT_CRIT_PHASE2 == csa->t_commit_crit) && (dba_bg == csa->hdr->acc_meth)) \ { \ cache_start = &csa->acc_meth.bg.cache_state->cache_array[0]; \ cache_start += csa->hdr->bt_buckets; \ bufstart = (sm_uc_ptr_t)GDS_ANY_REL2ABS(csa, cache_start->buffaddr); \ bufend = bufstart + ((gtm_uint64_t)csa->hdr->n_bts * csa->hdr->blk_size); \ bufaddr = (sm_uc_ptr_t)(seg)->addr; \ /* Check if given address is within database shared memory range */ \ if ((bufaddr >= bufstart) && (bufaddr < bufend)) \ { \ bufindx = (bufaddr - bufstart) / csa->hdr->blk_size; \ assert(bufindx < csa->hdr->n_bts); \ /* Assert that we have the cache-record pinned */ \ assert(process_id == cache_start[bufindx].in_cw_set); \ } \ } \ } MBEND /* Macro to check that we have not pinned any more buffers than we are updating. * This check is done only for BG access method and in dbg mode. * This is invoked by t_end/tp_tend just before beginning phase2 of commit. */ #define DBG_CHECK_PINNED_CR_ARRAY_CONTENTS(csd, is_mm, crarray, crarrayindex) \ MBSTART { \ GBLREF boolean_t write_after_image; \ GBLREF uint4 process_id; \ cache_rec *cr; \ int4 crindex, bplmap; \ \ if (!is_mm) \ { \ bplmap = csd->bplmap; \ for (crindex = 0; crindex < crarrayindex; crindex++) \ { \ cr = crarray[crindex]; \ if (process_id == cr->in_cw_set) \ { /* We have pinned that cache-record implies we are planning on updating it \ * (so should have set in_tend). There are 2 exceptions though. \ * \ * a) Since bitmap blocks are done with phase2 inside of crit, they should not \ * show up in the pinned array list at end of phase1 for GT.M. But DSE is an \ * exception as it could operate on a bitmap block as if it is updating a \ * non-bitmap block (i.e. without invoking gvcst_map_build). MUPIP JOURNAL \ * RECOVER also could do the same thing while applying an AIMG record. In both \ * cases, "write_after_image" would be TRUE. \ * \ * b) In case this is a twinned cache-record, for the older twin "in_cw_set" \ * will be set to non-zero, but "in_tend" will be set to FALSE. Since we are \ * outside of crit at this point, it is possible cr->twin field might be 0 \ * (could have gotten cleared by wcs_wtfini concurrently) so we cannot assert \ * on the twin field but "cr->bt_index" should still be 0 since we have not \ * yet finished the update on the newer twin so use that instead. Also twinning \ * is enabled only if csd->asyncio is TRUE so check that as well. \ */ \ assert((process_id == cr->in_tend) \ || ((0 == (cr->blk % bplmap)) && write_after_image) \ || (csd->asyncio && !cr->bt_index)); \ } \ } \ } \ } MBEND #else #define DBG_ENSURE_PTR_IS_VALID_GLOBUFF(CSA, CSD, PTR) #define DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(CSA, CSD, PTR) #define DBG_ENSURE_OLD_BLOCK_IS_VALID(cse, is_mm, csa, csd) #define DBG_BG_PHASE2_CHECK_CR_IS_PINNED(csa, bufaddr) #define DBG_CHECK_PINNED_CR_ARRAY_CONTENTS(csd, is_mm, crarray, crarrayindex) #endif /* The TP_CHANGE_REG macro is a replica of the tp_change_reg() routine to be used for performance considerations. * The TP_CHANGE_REG_IF_NEEDED macro tries to optimize on processing if reg is same as gv_cur_region. But it can be * used only if the region passed is not NULL and if gv_cur_region, cs_addrs and cs_data are known to be in sync. * Note that timers can interrupt the syncing and hence any routines that are called by timers should be safe * and use the TP_CHANGE_REG macro only. */ #define TP_CHANGE_REG(reg) \ MBSTART { \ GBLREF jnlpool_addrs_ptr_t jnlpool; \ gv_cur_region = reg; \ if (NULL == gv_cur_region || FALSE == gv_cur_region->open) \ { \ cs_addrs = (sgmnt_addrs *)0; \ cs_data = (sgmnt_data_ptr_t)0; \ } else \ { \ switch (REG_ACC_METH(reg)) \ { \ case dba_mm: \ case dba_bg: \ cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; \ cs_data = cs_addrs->hdr; \ if (cs_addrs->jnlpool && (jnlpool != cs_addrs->jnlpool)) \ jnlpool = cs_addrs->jnlpool; \ break; \ case dba_usr: \ case dba_cm: \ cs_addrs = (sgmnt_addrs *)0; \ cs_data = (sgmnt_data_ptr_t)0; \ break; \ default: \ assertpro(FALSE); \ break; \ } \ } \ } MBEND #define TP_CHANGE_REG_IF_NEEDED(reg) \ MBSTART { \ GBLREF jnlpool_addrs_ptr_t jnlpool; \ assert(reg); \ if (reg != gv_cur_region) \ { \ gv_cur_region = reg; \ switch (REG_ACC_METH(reg)) \ { \ case dba_mm: \ case dba_bg: \ assert(reg->open); \ cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; \ cs_data = cs_addrs->hdr; \ assert((&FILE_INFO(gv_cur_region)->s_addrs == cs_addrs) \ && cs_addrs->hdr == cs_data); \ if (cs_addrs->jnlpool && (jnlpool != cs_addrs->jnlpool)) \ jnlpool = cs_addrs->jnlpool; \ break; \ case dba_usr: \ case dba_cm: \ cs_addrs = (sgmnt_addrs *)0; \ cs_data = (sgmnt_data_ptr_t)0; \ break; \ default: \ assertpro(FALSE); \ break; \ } \ } \ } MBEND #define PUSH_GV_CUR_REGION(REG, SAV_REG, SAV_CS_ADDRS, SAV_CS_DATA, SAV_JNLPOOL) \ MBSTART { \ SAV_REG = gv_cur_region; \ SAV_CS_ADDRS = cs_addrs; \ SAV_CS_DATA = cs_data; \ SAV_JNLPOOL = jnlpool; \ TP_CHANGE_REG(REG); \ } MBEND #define POP_GV_CUR_REGION(SAV_REG, SAV_CS_ADDRS, SAV_CS_DATA, SAV_JNLPOOL) \ MBSTART { \ gv_cur_region = SAV_REG; \ cs_addrs = SAV_CS_ADDRS; \ cs_data = SAV_CS_DATA; \ jnlpool = SAV_JNLPOOL; \ } MBEND /* The TP_TEND_CHANGE_REG macro is a special macro used in tp_tend.c to optimize out the unnecessary checks in * the TP_CHANGE_REG_IF_NEEDED macro. Also it sets cs_addrs and cs_data to precomputed values instead of recomputing * them from the region by dereferencing through a multitude of pointers. It does not check if gv_cur_region is * different from the input region. It assumes it is different enough % of times that the cost of the if check * is not worth the additional unconditional sets. */ #define TP_TEND_CHANGE_REG(si) \ MBSTART { \ gv_cur_region = si->gv_cur_region; \ cs_addrs = si->tp_csa; \ cs_data = si->tp_csd; \ } MBEND #define GTCM_CHANGE_REG(reghead) \ MBSTART { \ GBLREF cm_region_head *curr_cm_reg_head; \ GBLREF gd_region *gv_cur_region; \ GBLREF sgmnt_data *cs_data; \ GBLREF sgmnt_addrs *cs_addrs; \ \ curr_cm_reg_head = (reghead); \ gv_cur_region = curr_cm_reg_head->reg; \ assert(IS_REG_BG_OR_MM(gv_cur_region)); \ cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; \ cs_data = cs_addrs->hdr; \ } MBEND /* Macro to be used whenever cr->data_invalid needs to be set */ #define SET_DATA_INVALID(cr) \ MBSTART { \ uint4 in_tend, data_invalid; \ \ DEBUG_ONLY(in_tend = cr->in_tend); \ DEBUG_ONLY(data_invalid = cr->data_invalid); \ assert((process_id == in_tend) || (0 == in_tend) && (0 == data_invalid)); \ assert((0 == in_tend) \ || (process_id == in_tend) && ((0 == data_invalid) || (process_id == data_invalid))); \ cr->data_invalid = process_id; \ } MBEND /* Macro to be used whenever cr->data_invalid needs to be re-set */ #define RESET_DATA_INVALID(cr) \ MBSTART { \ uint4 data_invalid; \ \ DEBUG_ONLY(data_invalid = cr->data_invalid); \ assert(process_id == data_invalid); \ cr->data_invalid = 0; \ } MBEND /* Macro to be used whenever cr->in_cw_set needs to be set (PIN) inside a TP transaction */ #define TP_PIN_CACHE_RECORD(cr, si) \ MBSTART { \ assert(0 <= si->cr_array_index); \ assert(si->cr_array_index < si->cr_array_size); \ PIN_CACHE_RECORD(cr, si->cr_array, si->cr_array_index); \ } MBEND GBLREF cache_rec_ptr_t pin_fail_cr; /* Pointer to the cache-record that we failed while pinning */ GBLREF cache_rec pin_fail_cr_contents; /* Contents of the cache-record that we failed while pinning */ GBLREF cache_rec_ptr_t pin_fail_twin_cr; /* Pointer to twin of the cache-record that we failed to pin */ GBLREF cache_rec pin_fail_twin_cr_contents; /* Contents of twin of the cache-record that we failed to pin */ GBLREF bt_rec_ptr_t pin_fail_bt; /* Pointer to bt of the cache-record that we failed to pin */ GBLREF bt_rec pin_fail_bt_contents; /* Contents of bt of the cache-record that we failed to pin */ GBLREF int4 pin_fail_in_crit; /* Holder of crit at the time we failed to pin */ GBLREF int4 pin_fail_wc_in_free; /* Number of write cache records in free queue when we failed to pin */ GBLREF int4 pin_fail_wcs_active_lvl; /* Number of entries in active queue when we failed to pin */ GBLREF int4 pin_fail_ref_cnt; /* Reference count when we failed to pin */ GBLREF int4 pin_fail_in_wtstart; /* Count of processes in wcs_wtstart when we failed to pin */ GBLREF int4 pin_fail_phase2_commit_pidcnt; /* Number of processes in phase2 commit when we failed to pin */ /* Macro to be used whenever cr->in_cw_set needs to be set (PIN) outside of a TP transaction */ #define PIN_CACHE_RECORD(cr, crarray, crarrayindex) \ MBSTART { \ uint4 in_tend, data_invalid, in_cw_set; \ \ DEBUG_ONLY(in_tend = cr->in_tend); \ DEBUG_ONLY(data_invalid = cr->data_invalid); \ assert((process_id == in_tend) || (0 == in_tend)); \ assert((process_id == data_invalid) || (0 == data_invalid)); \ in_cw_set = cr->in_cw_set; \ if (0 != in_cw_set) \ { \ pin_fail_cr = cr; \ pin_fail_cr_contents = *cr; \ if (cr->bt_index) \ { \ pin_fail_bt = (bt_rec_ptr_t)GDS_ANY_REL2ABS(cs_addrs, cr->bt_index); \ pin_fail_bt_contents = *pin_fail_bt; \ } \ if (cr->twin) \ { \ pin_fail_twin_cr = (cache_rec_ptr_t)GDS_ANY_REL2ABS(cs_addrs, cr->twin); \ pin_fail_twin_cr_contents = *pin_fail_twin_cr; \ } \ pin_fail_in_crit = cs_addrs->nl->in_crit; \ pin_fail_wc_in_free = cs_addrs->nl->wc_in_free; \ pin_fail_wcs_active_lvl = cs_addrs->nl->wcs_active_lvl; \ pin_fail_ref_cnt = cs_addrs->nl->ref_cnt; \ pin_fail_in_wtstart = cs_addrs->nl->in_wtstart; \ pin_fail_phase2_commit_pidcnt = cs_addrs->nl->wcs_phase2_commit_pidcnt; \ assertpro(0 == in_cw_set); \ } \ /* If twinning, we should never set in_cw_set on an OLDER twin. Assert that. */ \ assert(!cr->twin || cr->bt_index); \ /* stuff it in the array before setting in_cw_set */ \ crarray[crarrayindex] = cr; \ crarrayindex++; \ cr->in_cw_set = process_id; \ } MBEND /* Macro to be used whenever cr->in_cw_set needs to be re-set (UNPIN) in TP or non-TP) */ #define UNPIN_CACHE_RECORD(cr) \ MBSTART { \ uint4 in_tend, data_invalid, in_cw_set; \ \ in_cw_set = cr->in_cw_set; \ if (process_id == cr->in_cw_set) /* reset in_cw_set only if we hold it */ \ { \ DEBUG_ONLY(in_tend = cr->in_tend); \ DEBUG_ONLY(data_invalid = cr->data_invalid); \ assert((process_id == in_tend) || (0 == in_tend)); \ assert((process_id == data_invalid) || (0 == data_invalid)); \ cr->in_cw_set = 0; \ } \ } MBEND /* Macro to reset cr->in_cw_set for the entire cr_array in case of a retry (TP or non-TP) */ #define UNPIN_CR_ARRAY_ON_RETRY(crarray, crarrayindex) \ MBSTART { \ int4 lcl_crarrayindex; \ cache_rec_ptr_ptr_t cr_ptr; \ cache_rec_ptr_t cr; \ uint4 in_tend, data_invalid, in_cw_set; \ \ lcl_crarrayindex = crarrayindex; \ if (lcl_crarrayindex) \ { \ cr_ptr = (cache_rec_ptr_ptr_t)&crarray[lcl_crarrayindex-1]; \ while (lcl_crarrayindex--) \ { \ cr = *cr_ptr; \ DEBUG_ONLY(in_tend = cr->in_tend); \ DEBUG_ONLY(data_invalid = cr->data_invalid); \ DEBUG_ONLY(in_cw_set = cr->in_cw_set); \ assert(!data_invalid); \ assert(!in_tend); \ assert(process_id == in_cw_set); \ UNPIN_CACHE_RECORD(cr); \ cr_ptr--; \ } \ crarrayindex = 0; \ } \ } MBEND #define RESET_CR_IN_TEND_AFTER_PHASE2_COMMIT(CR, CSA, CSD) \ { \ cache_rec_ptr_t cr_old; \ DEBUG_ONLY(cache_rec lcl_cr_curr); \ DEBUG_ONLY(cache_rec lcl_cr_old); \ \ GBLREF uint4 process_id; \ \ if (CR->backup_cr_is_twin) \ { /* We created a twin in "bg_update_phase1". If we had pinned the older twin (Note: it is possible in some cases \ * that we do not pin the older twin (e.g. in tp_tend if cse->new_buff is non-NULL), let us unpin the older twin\ * before unpinning the newer twin. Not doing so could cause "db_csh_getn" to identify the new twin as a buffer \ * ready for replacement but "wcs_get_space/wcs_wtstart_fini/wcs_wtstart/wcs_wtfini" would not be able to flush \ * the new twin until the old twin has its "in_cw_set" cleared resulting in a livelock. \ */ \ assert(TWINNING_ON(CSD)); \ cr_old = (cache_rec_ptr_t)GDS_ANY_REL2ABS(CSA, CR->twin); /* get old twin */ \ DEBUG_ONLY(lcl_cr_old = *cr_old); /* In case the following assert trips capture the CR contents */ \ DEBUG_ONLY(lcl_cr_curr = *CR); \ assert(!cr_old->bt_index || !CR->twin); \ assert(!cr_old->in_cw_set || cr_old->dirty || !CR->twin); \ assert(CR->bt_index || !CR->twin); \ assert((process_id == cr_old->in_cw_set) || (0 == cr_old->in_cw_set) || !CR->twin); \ if(CR->twin) \ { \ UNPIN_CACHE_RECORD(cr_old); \ } \ } \ /* A concurrent process reading this block will wait for in_tend to become FALSE and then proceed with its \ * database operation. Later it will reach t_end/tp_tend doing validations at which point it will need to set in_cw_set.\ * It expects in_cw_set to be 0 at that point. Therefore in_cw_set needs to be reset to 0 BEFORE resetting in_tend. \ * Need a write memory barrier to ensure that these two updates are seen in that order by any other concurrent process. \ */ \ assert(process_id == CR->in_cw_set); \ UNPIN_CACHE_RECORD(CR); \ assert(!CR->in_cw_set); \ SHM_WRITE_MEMORY_BARRIER; \ assert(process_id == CR->in_tend); /* should still be valid */ \ CR->in_tend = 0; \ } /* Macro to check that UNPIN of cr is complete (i.e. cr->in_cw_set has been reset to 0) for the entire cr_array in case of a * commit (TP or non-TP). Usually in_cw_set is set for all cache-records that we are planning on updating before we start phase1. * After updating each cse in phase2, we reset the corresponding cse->cr->in_cw_set. Therefore on a successful commit, after * completing all cses in phase2, we don't expect any pinned cr->in_cw_set at all. */ #ifdef DEBUG #define ASSERT_CR_ARRAY_IS_UNPINNED(CSD, CRARRAY, CRARRAYINDEX) \ MBSTART { \ GBLREF uint4 process_id; \ \ int4 lcl_crarrayindex; \ cache_rec_ptr_ptr_t cr_ptr, cr_start; \ cache_rec_ptr_t cr; \ \ lcl_crarrayindex = CRARRAYINDEX; \ if (lcl_crarrayindex) \ { \ cr_ptr = (cache_rec_ptr_ptr_t)&CRARRAY[lcl_crarrayindex]; \ cr_start = &CRARRAY[0]; \ while (--cr_ptr >= cr_start) \ { \ cr = *cr_ptr; \ assert(process_id != cr->in_cw_set); \ } \ } \ } MBEND #else #define ASSERT_CR_ARRAY_IS_UNPINNED(CSD, CRARRAY, CRARRAYINDEX) #endif /* Every process that initializes journal pool has two aspects of validation. * 1. Validate whether this process can do logical updates to the journal pool that is initialized. If not, the process should * issue SCNDDBNOUPD error. Examples of this would be a GT.M update that happens on a non-supplementary Receiver Side. * * 2. Validate whether the process is attached to the correct journal pool. If not, REPLINSTMISMTCH should be issued. This check * ensures that we only do updates to region that is tied to the journal pool that we initialized. * * The below macro definitions indicate which of the above 2 checks is done by the process for a particular region until now. * The macro VALIDATE_INITIALIZED_JNLPOOL does the actual validation. */ #define JNLPOOL_NOT_VALIDATED 0x00 #define REPLINSTMISMTCH_CHECK_DONE 0x01 #define SCNDDBNOUPD_CHECK_DONE 0x02 #define JNLPOOL_VALIDATED 0x03 #define SCNDDBNOUPD_CHECK_FALSE 0x00 #define SCNDDBNOUPD_CHECK_TRUE 0x01 #define VALIDATE_INITIALIZED_JNLPOOL(CSA, CNL, REG, JNLPOOL_USER, SCNDDBNOUPD_CHECK_NEEDED) \ MBSTART { \ GBLREF boolean_t is_updproc; \ GBLREF jnlpool_addrs_ptr_t jnlpool; \ \ unsigned char instfilename_copy[MAX_FN_LEN + 1]; \ sm_uc_ptr_t jnlpool_instfilename; \ int4 jnlpool_shmid; \ uint4 jnlpool_validate_check; \ boolean_t do_REPLINSTMISMTCH_check; \ \ jnlpool_validate_check = CSA->jnlpool_validate_check; \ assert(JNLPOOL_VALIDATED >= jnlpool_validate_check); \ if (JNLPOOL_VALIDATED != jnlpool_validate_check) \ { \ do_REPLINSTMISMTCH_check = (!(REPLINSTMISMTCH_CHECK_DONE & jnlpool_validate_check) \ && ((GTMRELAXED != JNLPOOL_USER) || !IS_GTM_IMAGE)); \ if (!(SCNDDBNOUPD_CHECK_DONE & jnlpool_validate_check) && SCNDDBNOUPD_CHECK_NEEDED) \ { \ if (jnlpool->jnlpool_ctl->upd_disabled && !is_updproc) \ { /* Updates are disabled in this journal pool. Issue error. Do NOT detach from journal pool \ * as that would cause us not to honor instance freeze (in case gtm_custom_errors env var is \ * non-null) for database reads that this process later does (for example reading a block \ * might require us to flush a dirty buffer to disk which should pause if the instance is \ * frozen). \ */ \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_SCNDDBNOUPD); \ } \ CSA->jnlpool_validate_check |= SCNDDBNOUPD_CHECK_DONE; \ } \ if (do_REPLINSTMISMTCH_check) \ { \ jnlpool_instfilename = (sm_uc_ptr_t)jnlpool->jnlpool_ctl->jnlpool_id.instfilename; \ if (STRCMP(CNL->replinstfilename, jnlpool_instfilename) \ || (CNL->jnlpool_shmid != jnlpool->repl_inst_filehdr->jnlpool_shmid)) \ { \ /* Replication instance filename or jnlpool shmid mismatch. Two possibilities. \ * (a) Database has already been bound with a replication instance file name that is different \ * from the instance file name used by the current process. \ * (b) Database has already been bound with a jnlpool shmid and another jnlpool is about to \ * be bound with the same database. Disallow this mixing of multiple jnlpools. \ * Issue error. But before that detach from journal pool. \ * Copy replication instance file name in journal pool to temporary memory before detaching. \ * Actually case (b) subsumes (a) so we assert that below. But in pro we handle both cases \ * just in case. \ */ \ assert(CNL->jnlpool_shmid != jnlpool->repl_inst_filehdr->jnlpool_shmid); \ assert(SIZEOF(instfilename_copy) == SIZEOF(jnlpool->jnlpool_ctl->jnlpool_id.instfilename)); \ memcpy(&instfilename_copy[0], jnlpool_instfilename, SIZEOF(instfilename_copy)); \ assert(SIZEOF(jnlpool_shmid) == SIZEOF(CNL->jnlpool_shmid)); \ jnlpool_shmid = jnlpool->repl_inst_filehdr->jnlpool_shmid; \ assert(NULL != jnlpool->jnlpool_ctl); \ if (INVALID_SHMID == CNL->jnlpool_shmid) \ rts_error_csa(CSA_ARG(CSA) VARLSTCNT(4) ERR_REPLINSTNOSHM, 2, DB_LEN_STR(REG)); \ else \ rts_error_csa(CSA_ARG(CSA) VARLSTCNT(10) ERR_REPLINSTMISMTCH, 8, \ LEN_AND_STR(instfilename_copy), jnlpool_shmid, DB_LEN_STR(REG), \ LEN_AND_STR(CNL->replinstfilename), CNL->jnlpool_shmid); \ } \ CSA->jnlpool_validate_check |= REPLINSTMISMTCH_CHECK_DONE; \ CSA->jnlpool = jnlpool; \ } \ } \ } MBEND #define JNLPOOL_INIT_IF_NEEDED(CSA, CSD, CNL, SCNDDBNOUPD_CHECK_NEEDED) \ MBSTART { \ GBLREF boolean_t is_replicator; \ GBLREF gd_region *gv_cur_region; \ GBLREF jnlpool_addrs_ptr_t jnlpool_head; \ GBLREF jnlpool_addrs_ptr_t jnlpool; \ jnlpool_addrs_ptr_t lcl_jnlpool = NULL, jnlpool_save; \ gd_id replfile_gdid, *tmp_gdid; \ replpool_identifier replpool_id; \ unsigned int full_len; \ int4 status = -1; \ boolean_t jnlpool_found = FALSE; \ \ if (REPL_ALLOWED(CSD) && is_replicator) \ { \ jnlpool_save = jnlpool; \ if (CSA->jnlpool && ((jnlpool_addrs_ptr_t)(CSA->jnlpool))->pool_init) \ { \ lcl_jnlpool = jnlpool = (jnlpool_addrs_ptr_t)CSA->jnlpool; \ jnlpool_found = TRUE; \ } else if (IS_GTM_IMAGE && REPL_INST_AVAILABLE(CSA->gd_ptr)) \ { \ status = filename_to_id(&replfile_gdid, replpool_id.instfilename); \ if (SS_NORMAL != status) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_REPLINSTACC, 2, full_len, replpool_id.instfilename,\ ERR_TEXT, 2, RTS_ERROR_LITERAL("could not get file id"), status); \ for (lcl_jnlpool = jnlpool_head; NULL != lcl_jnlpool; lcl_jnlpool = lcl_jnlpool->next) \ { \ if (lcl_jnlpool->pool_init) \ { \ tmp_gdid = &FILE_ID(lcl_jnlpool->jnlpool_dummy_reg); \ if (!gdid_cmp(tmp_gdid, &replfile_gdid)) \ { \ jnlpool = CSA->jnlpool = lcl_jnlpool; \ jnlpool_found = TRUE; \ break; \ } \ } \ } \ } else \ { \ lcl_jnlpool = jnlpool; \ if (lcl_jnlpool && lcl_jnlpool->pool_init) \ jnlpool_found = TRUE; \ } \ if (!jnlpool_found || (NULL == lcl_jnlpool) || !lcl_jnlpool->pool_init) \ { \ jnlpool_init((jnlpool_user)GTMPROC, (boolean_t)FALSE, (boolean_t *)NULL, CSA->gd_ptr); \ if (jnlpool && jnlpool->pool_init) \ CSA->jnlpool = jnlpool; \ } \ assert(jnlpool && jnlpool->pool_init); \ VALIDATE_INITIALIZED_JNLPOOL(CSA, CNL, gv_cur_region, GTMPROC, SCNDDBNOUPD_CHECK_NEEDED); \ } \ } MBEND #define ASSERT_VALID_JNLPOOL(CSA) \ MBSTART { \ GBLREF jnlpool_addrs_ptr_t jnlpool; \ \ assert(CSA && CSA->critical && CSA->nl); /* should have been setup in mu_rndwn_replpool */ \ assert(jnlpool && jnlpool->jnlpool_ctl); \ assert(CSA->critical == (CRIT_PTR_T)((sm_uc_ptr_t)jnlpool->jnlpool_ctl + JNLPOOL_CTL_SIZE)); \ assert(CSA->nl == (node_local_ptr_t) ((sm_uc_ptr_t)CSA->critical + JNLPOOL_CRIT_SPACE \ + SIZEOF(mutex_spin_parms_struct))); \ assert(jnlpool->jnlpool_ctl->filehdr_off); \ assert(jnlpool->jnlpool_ctl->srclcl_array_off > jnlpool->jnlpool_ctl->filehdr_off); \ assert(jnlpool->jnlpool_ctl->sourcelocal_array_off > jnlpool->jnlpool_ctl->srclcl_array_off); \ assert(jnlpool->repl_inst_filehdr == (repl_inst_hdr_ptr_t)((sm_uc_ptr_t)jnlpool->jnlpool_ctl \ + jnlpool->jnlpool_ctl->filehdr_off)); \ assert(jnlpool->gtmsrc_lcl_array == (gtmsrc_lcl_ptr_t)((sm_uc_ptr_t)jnlpool->jnlpool_ctl \ + jnlpool->jnlpool_ctl->srclcl_array_off)); \ assert(jnlpool->gtmsource_local_array == (gtmsource_local_ptr_t)((sm_uc_ptr_t)jnlpool->jnlpool_ctl \ + jnlpool->jnlpool_ctl->sourcelocal_array_off)); \ } MBEND #ifdef CRIT_USE_PTHREAD_MUTEX #define UPDATE_CRASH_COUNT(CSA, CRASH_COUNT) \ MBSTART { \ /* Do Nothing */ \ } MBEND #else #define UPDATE_CRASH_COUNT(CSA, CRASH_COUNT) \ MBSTART { \ if ((CSA) && (CSA)->critical) \ CRASH_COUNT = (CSA)->critical->crashcnt; \ } MBEND #endif /* Explanation for why we need the following macro. * * Normally a cdb_sc_blkmod check is done using the "bt". This is done in t_end and tp_tend. * But that is possible only if we hold crit. There are a few routines (TP only) that need * to do this check outside of crit (e.g. tp_hist, gvcst_search). For those, the following macro * is defined. This macro compares transaction numbers directly from the buffer instead of * going through the bt or blk queues. This is done to speed up processing. One consequence * is that we might encounter a situation where the buffer's contents hasn't been modified, * but the block might actually have been changed e.g. if asyncio=ON, a twin buffer might have been * created or the "blk" field in the cache-record corresponding to this buffer might have * been made CR_BLKEMPTY etc. In these cases, we rely on the fact that the cycle for the * buffer would have been incremented thereby saving us in the cdb_sc_lostcr check which will * always FOLLOW (never PRECEDE) this check. * * Note that in case of BG, it is possible that the buffer could be in the process of being updated * (phase2 outside of crit). In this case we have to restart as otherwise we could incorrectly * validate an inconsistent state of the database as okay. For example, say our search path * contains a level-1 root-block and a level-0 data block. If both of these blocks were * concurrently being updated in phase2 (outside of crit) by another process, it is possible * (because of the order in which blocks are committed) that the data block contents get * modified first but the index block is still unchanged. If we traversed down the tree at * this instant, we are looking at a search path that contains a mix of pre-update and post-update * blocks and should never validate this traversal as okay. In this case, the cache record * corresponding to the index block would have its "in_tend" flag non-zero indicating update is pending. * * The order of the check should be cr->in_tend BEFORE the buffaddr->tn check. Doing it otherwise * would mean it is posible for the buffaddr->tn check to succeed and before the cr->in_tend * check is done the buffer gets rebuilt (from start to finish in phase2). This would result * in us falsely validating this transaction as okay when in fact we should have restarted. * * Because we rely on the fact that cr->in_tend is reset to 0 AFTER t1->buffaddr->tn is updated, and * since these could be updated concurrently, and since this macro is used outside of crit, we need to * ensure a read memory barrier is done. Currently, the only two places which use this macro are tp_hist.c * and gvcst_search.c. Out of this, the latter uses this only as a performance measure and not for correctness. * But the former uses this for correctness. In fact tp_tend.c relies on tp_hist.c doing a proper validation. * Therefore the read memory barrier is essential in tp_hist.c and not needed in gvcst_search.c. See tp_hist.c * for use of the read memory barrier and a comment describing why it is ok to do it only once per function * invocation (instead of using it once per block that gets validated). * * There are two variants of this macro. * TP_IS_CDB_SC_BLKMOD : That calculates the blktn by doing t1->buffaddr->tn explicitly. * TP_IS_CDB_SC_BLKMOD3 : This is provided the blktn as input so can avoid the explicit calculation. */ #define TP_IS_CDB_SC_BLKMOD(cr, t1) (((NULL != (cr)) && (cr)->in_tend) || ((t1)->tn <= ((blk_hdr_ptr_t)(t1)->buffaddr)->tn)) #define TP_IS_CDB_SC_BLKMOD3(cr, t1, blktn) (((NULL != (cr)) && (cr)->in_tend) || ((t1)->tn <= blktn)) /* The following macros describe the Master Bit Map (MBM): * MM_ADDR(SGD) -> returns a pointer the MBM using a pointer to sgmnt_data structure * * MASTER_MAP_BLOCKS_V# -> the number of database blocks the MBM uses * -This constrains the total number of blocks in the database with the * total possible number of blocks determined by the formula * MASTER_MAP_BLOCKS_V# * 512 * 8 * 512 = POSSIBLE_BLOCKS * where * MASTER_MAP_BLOCKS_V# -> number of blocks the MBM uses * 512 -> number of bytes per block * -corresponds to the DISK_BLOCK_SIZE macro * 8 -> number of Local Bit Maps (LBM) per byte of MBM * -the MBM can track 1 LBM per bit, unlike the LBM which requires 2 bits per data block * 512 -> number of blocks per LBM * -since the first block in a LBM is the LBM itself replace this with 511 if calculating * the number of usable data blocks * * MASTER_MAP_SIZE_V# -> the size of the MBM on disk */ #define MM_ADDR(SGD) ((sm_uc_ptr_t)(((sgmnt_data_ptr_t)SGD) + 1)) #define MASTER_MAP_BLOCKS_V4 32LL /* 32 gives 67M possible blocks */ #define MASTER_MAP_BLOCKS_V5_OLD 64LL /* V5 database previous master map block size (134M) */ #define MASTER_MAP_BLOCKS_V5 112LL /* 112 gives 234M possible blocks */ #define MASTER_MAP_BLOCKS_V6 496LL /* 496 gives 992Mi (1038M) possible blocks */ #define MASTER_MAP_BLOCKS_V7 8176LL /* 8176 gives 16Gi (17.1B) possible blocks */ #define MASTER_MAP_BLOCKS_DFLT MASTER_MAP_BLOCKS_V7 /* this represents the default for the current version */ #define MASTER_MAP_BLOCKS_MAX MASTER_MAP_BLOCKS_V7 /* this represents the version with the most possible blocks */ #define MASTER_MAP_SIZE_V4 (MASTER_MAP_BLOCKS_V4 * DISK_BLOCK_SIZE) /* MUST be a multiple of DISK_BLOCK_SIZE */ #define MASTER_MAP_SIZE_V5_OLD (MASTER_MAP_BLOCKS_V5_OLD * DISK_BLOCK_SIZE) #define MASTER_MAP_SIZE_V5 (MASTER_MAP_BLOCKS_V5 * DISK_BLOCK_SIZE) /* MUST be a multiple of DISK_BLOCK_SIZE */ #define MASTER_MAP_SIZE_V6 (MASTER_MAP_BLOCKS_V6 * DISK_BLOCK_SIZE) #define MASTER_MAP_SIZE_V7 (MASTER_MAP_BLOCKS_V7 * DISK_BLOCK_SIZE) #define MASTER_MAP_SIZE_DFLT (MASTER_MAP_BLOCKS_DFLT * DISK_BLOCK_SIZE) /* MUST be a multiple of DISK_BLOCK_SIZE */ #define MASTER_MAP_SIZE_MAX (MASTER_MAP_BLOCKS_MAX * DISK_BLOCK_SIZE) /* MUST be a multiple of DISK_BLOCK_SIZE */ #define MASTER_MAP_SIZE(SGD) (((sgmnt_data_ptr_t)SGD)->master_map_len) #define SGMNT_HDR_LEN SIZEOF(sgmnt_data) #define SIZEOF_FILE_HDR(SGD) (SGMNT_HDR_LEN + MASTER_MAP_SIZE(SGD)) #define SIZEOF_FILE_HDR_DFLT (SGMNT_HDR_LEN + MASTER_MAP_SIZE_DFLT) #define SIZEOF_FILE_HDR_V7 (SGMNT_HDR_LEN + MASTER_MAP_SIZE_V7) #define SIZEOF_FILE_HDR_V6 (SGMNT_HDR_LEN + MASTER_MAP_SIZE_V6) #define SIZEOF_FILE_HDR_V5 (SGMNT_HDR_LEN + MASTER_MAP_SIZE_V5) #define SIZEOF_FILE_HDR_MIN (SGMNT_HDR_LEN + MASTER_MAP_SIZE_V4) #define SIZEOF_FILE_HDR_MAX (SGMNT_HDR_LEN + MASTER_MAP_SIZE_MAX) #define MM_BLOCK (SGMNT_HDR_LEN / DISK_BLOCK_SIZE + 1) /* gt.m numbers blocks from 1 */ /* This macro defines which block (of size DISK_BLOCK_SIZE not the database block size) the trans_hist field of * the header resides in. This can be calculated by taking the offset of trans_hist, dividing by 512 and adding 1. * TH_BLOCK = (offsetof(trans_hist) / DISK_BLOCK_SIZE) + 1 * Currently the offset of trans_hist is 4920 and DISK_BLOCK_SIZE is 512, so * TH_BLOCK = (4920/512) + 1 = 10.61 */ #define TH_BLOCK 10 #define JNL_NAME_SIZE 256 /* possibly expanded when opened. Macro value should not change as it is used in db file hdr */ #define JNL_NAME_EXP_SIZE 1024 /* MAXPATHLEN, before jnl_buffer in shared memory */ #define BLKS_PER_LMAP 512 #define MAXTOTALBLKS_V4 (MASTER_MAP_SIZE_V4 * 8 * BLKS_PER_LMAP) #define MAXTOTALBLKS_V5 (MASTER_MAP_SIZE_V5 * 8 * BLKS_PER_LMAP) #define MAXTOTALBLKS_V6 (MASTER_MAP_SIZE_V6 * 8 * BLKS_PER_LMAP) #define MAXTOTALBLKS_V7 (MASTER_MAP_SIZE_V7 * 8 * BLKS_PER_LMAP) #define MAXTOTALBLKS_MAX (MASTER_MAP_SIZE_MAX * 8 * BLKS_PER_LMAP) #define MAXTOTALBLKS(SGD) (MASTER_MAP_SIZE(SGD) * 8 * BLKS_PER_LMAP) #define IS_BITMAP_BLK(blk) (ROUND_DOWN2(blk, BLKS_PER_LMAP) == blk) /* TRUE if blk is a bitmap */ /* V4 - 8K fileheader (= 16 blocks) + 16K mastermap (= 32 blocks) + 1 * V5 - 8K fileheader (= 16 blocks) + 56K mastermap (= 112 blocks) + 1 * V6 - 8K fileheader (= 16 blocks) + 248K mastermap (= 496 blocks) + 1 * v7 - 8k fileheader (= 16 blocks) + 4186k mastermap (= 8176 blocks) + 1 */ #define START_VBN_V4 49 #define START_VBN_V5 129 #define START_VBN_V6 513 #define START_VBN_V7 8193 #define START_VBN_CURRENT START_VBN_V7 #define STEP_FACTOR 64 /* the factor by which flush_trigger is incremented/decremented */ #define MIN_FLUSH_TRIGGER(n_bts) ((n_bts) / 8) /* the minimum flush_trigger as a function of n_bts */ #define MAX_WRT_PER_FLU 64 /* arbitrary, but probably sensible limit */ #define MIN_FILLFACTOR 30 #define MAX_FILLFACTOR 100 #ifdef DEBUG_DYNGRD # define DEBUG_DYNGRD_ONLY(X) X #else # define DEBUG_DYNGRD_ONLY(X) #endif #define SAVE_WTSTART_PID(cnl, pid, index) \ MBSTART { \ for (index = 0; index < MAX_WTSTART_PID_SLOTS; index++) \ if (0 == cnl->wtstart_pid[index]) \ break; \ if (MAX_WTSTART_PID_SLOTS > index) \ cnl->wtstart_pid[index] = pid; \ } MBEND #define CLEAR_WTSTART_PID(cnl, index) \ MBSTART { \ if (MAX_WTSTART_PID_SLOTS > index) \ cnl->wtstart_pid[index] = 0; \ } MBEND #define WRITERS_ACTIVE(cnl) ((0 < cnl->intent_wtstart) || (0 < cnl->in_wtstart)) #define SIGNAL_WRITERS_TO_STOP(cnl) \ MBSTART { \ SET_TRACEABLE_VAR((cnl)->wc_blocked, WC_BLOCK_RECOVER); \ /* memory barrier needed to broadcast this information to other processors */ \ SHM_WRITE_MEMORY_BARRIER; \ } MBEND #define WAIT_FOR_WRITERS_TO_STOP(cnl, lcnt, maxiters) \ MBSTART { /* We need to ensure that an uptodate value of cnl->intent_wtstart is read in the\ * WRITERS_ACTIVE macro every iteration of the loop hence the read memory barrier. \ */ \ SHM_READ_MEMORY_BARRIER; \ for (lcnt=1; WRITERS_ACTIVE(cnl) && (lcnt <= maxiters); lcnt++) \ { /* wait for any processes INSIDE or at ENTRY of wcs_wtstart to finish */ \ wcs_sleep(lcnt); \ SHM_READ_MEMORY_BARRIER; \ } \ } MBEND #define SIGNAL_WRITERS_TO_RESUME(cnl) \ MBSTART { \ SET_TRACEABLE_VAR((cnl)->wc_blocked, WC_UNBLOCK); \ /* memory barrier needed to broadcast this information to other processors */ \ SHM_WRITE_MEMORY_BARRIER; \ } MBEND #define INCR_INTENT_WTSTART(cnl) \ MBSTART { \ INCR_CNT(&cnl->intent_wtstart, &cnl->wc_var_lock); /* signal intent to enter wcs_wtstart */ \ if (0 >= cnl->intent_wtstart) \ { /* possible if wcs_verify had reset this flag */ \ INCR_CNT(&cnl->intent_wtstart, &cnl->wc_var_lock); \ /* wcs_verify cannot possibly have reset this flag again because it does this only \ * after wcs_recover waits for a maximum of 1 minute (for this flag to become zero) \ * before giving up. Therefore for that to happen, we should have been context \ * switched out for 1 minute after the second INCR_CNT but before the below assert) \ * We believe that is an extremely unlikely condition so don't do anything about it. \ * In the worst case this will get reset to 0 by the next wcs_verify or INCR_CNT \ * (may need multiple INCR_CNTs depending on how negative a value this is) whichever \ * happens sooner. \ */ \ assert(0 < cnl->intent_wtstart); \ } \ } MBEND #define DECR_INTENT_WTSTART(cnl) \ MBSTART { \ if (0 < cnl->intent_wtstart) \ DECR_CNT(&cnl->intent_wtstart, &cnl->wc_var_lock); \ /* else possible if wcs_verify had reset this flag */ \ } MBEND #define ENSURE_JNL_OPEN(csa, reg) \ MBSTART { \ boolean_t was_crit; \ jnl_private_control *jpc; \ sgmnt_data_ptr_t csd; \ uint4 jnl_status; \ \ assert(cs_addrs == csa); \ assert(gv_cur_region == reg); \ assert(FALSE == reg->read_only); \ csd = csa->hdr; \ if (JNL_ENABLED(csd)) \ { \ was_crit = csa->now_crit; \ if (!was_crit) \ grab_crit(reg, WS_2); \ jnl_status = JNL_ENABLED(csd) ? jnl_ensure_open(reg, csa) : 0; \ if (!was_crit) \ rel_crit(reg); \ if (0 != jnl_status) \ { \ jpc = csa->jnl; \ assert(NULL != jpc); \ if (SS_NORMAL != jpc->status) \ rts_error_csa(CSA_ARG(csa) VARLSTCNT(7) jnl_status, 4, JNL_LEN_STR(csd), \ DB_LEN_STR(gv_cur_region), jpc->status); \ else \ rts_error_csa(CSA_ARG(csa) VARLSTCNT(6) jnl_status, 4, JNL_LEN_STR(csd), \ DB_LEN_STR(gv_cur_region)); \ } \ } \ } MBEND #define JNL_ENSURE_OPEN_WCS_WTSTART(csa, reg, num_bufs, cr2flush, CHILLWAIT, RET) \ MBSTART { \ if ((CHILLWAIT) && !FREEZE_LATCH_HELD(csa)) \ WAIT_FOR_REGION_TO_UNCHILL(csa, csa->hdr); \ ENSURE_JNL_OPEN(csa, reg); \ if (csa->hdr->asyncio) \ RET = wcs_wtstart_fini(reg, num_bufs, cr2flush); \ else \ RET = wcs_wtstart(reg, num_bufs, NULL, cr2flush); \ } MBEND /* Macros to effect changes in the blks_to_upgrd field of the file-header. * We should hold crit on the region in all cases except for one when we are in MUPIP CREATE (but we are still standalone here). * Therefore we need not use any interlocks to update this field. This is asserted below. * Although we can derive "csd" from "csa", we pass them as two separate arguments for performance reasons. * Use local variables to record shared memory information for debugging purposes in case of an assert failure. */ #define INCR_BLKS_TO_UPGRD(csa, csd, delta) \ MBSTART { \ block_id new_blks_to_upgrd; \ block_id cur_blks_to_upgrd; \ block_id cur_delta; \ \ assert((csd)->createinprogress || (csa)->now_crit); \ cur_delta = (delta); \ assert((csa)->hdr == (csd)); \ assert(0 != cur_delta); \ cur_blks_to_upgrd = (csd)->blks_to_upgrd; \ assert(0 <= (csd)->blks_to_upgrd); \ new_blks_to_upgrd = cur_delta + cur_blks_to_upgrd; \ assert(0 <= new_blks_to_upgrd); \ (csd)->blks_to_upgrd = new_blks_to_upgrd; \ if (0 >= new_blks_to_upgrd) \ { \ if (0 == new_blks_to_upgrd) \ (csd)->tn_upgrd_blks_0 = (csd)->trans_hist.curr_tn; \ else \ { /* blks_to_upgrd counter in the fileheader should never hold a \ * negative value. Note down the negative value in a separate \ * field for debugging and set the counter to 0. \ */ \ (csd)->blks_to_upgrd = 0; \ (csd)->blks_to_upgrd_subzero_error -= (new_blks_to_upgrd); \ } \ } else \ (csd)->fully_upgraded = FALSE; \ } MBEND #define DECR_BLKS_TO_UPGRD(csa, csd, delta) INCR_BLKS_TO_UPGRD((csa), (csd), -(delta)) /* Interlocked queue instruction constants ... */ #define QI_STARVATION 3 #define EMPTY_QUEUE 0L #define QUEUE_WAS_EMPTY 1 #define INTERLOCK_FAIL -1L #define QUEUE_INSERT_SUCCESS 1 typedef trans_num bg_trc_rec_tn; typedef int4 bg_trc_rec_cntr; typedef struct { int4 curr_count; /* count for this invocation of shared memory */ int4 cumul_count; /* count from the creation of database (not including this invocation) */ } db_csh_acct_rec; #define TAB_DB_CSH_ACCT_REC(A,B,C) A, enum db_csh_acct_rec_type { #include "tab_db_csh_acct_rec.h" n_db_csh_acct_rec_types }; #undef TAB_DB_CSH_ACCT_REC #include "gvstats_rec.h" #include "probecrit_rec.h" #define GVSTATS_SET_CSA_STATISTIC(CSA, COUNTER, VALUE) \ MBSTART { \ if (0 == (RDBF_NOSTATS & (CSA)->reservedDBFlags)) \ (CSA)->gvstats_rec_p->COUNTER = VALUE; \ } MBEND #define INCR_GVSTATS_COUNTER(CSA, CNL, COUNTER, INCREMENT) \ MBSTART { \ if (0 == (RDBF_NOSTATS & (CSA)->reservedDBFlags)) \ { \ (CSA)->gvstats_rec_p->COUNTER += INCREMENT; /* private or shared stats */ \ (CNL)->gvstats_rec.COUNTER += INCREMENT; /* database stats */ \ } \ } MBEND #define SYNC_RESERVEDDBFLAGS_REG_CSA_CSD(REG, CSA, CSD, CNL) \ MBSTART { \ uint4 reservedDBFlags; \ \ if (TREF(ok_to_see_statsdb_regs)) \ { \ reservedDBFlags = CSD->reservedDBFlags; /* sgmnt_data is flag authority */ \ /* If this is a base DB (i.e. not a statsdb), but we could not successfully create the statsdb \ * (e.g. $gtm_statsdir issues etc.) then disable RDBF_STATSDB in the region. So this db continues \ * without statistics gathering. \ */ \ if (!IS_RDBF_STATSDB(CSD) && (NULL != CNL) && !CNL->statsdb_fname_len) \ reservedDBFlags &= (~RDBF_STATSDB); \ REG->reservedDBFlags = CSA->reservedDBFlags = reservedDBFlags; \ } \ /* else : "gd_load" would have already set RDBF_NOSTATS etc. so don't override that */ \ } MBEND /* Although this macro is called CLRGVSTATS, it clears a number of other fields as well. Originally, these were * mostly grouped together in the header and could be cleared as a block. Now that the gvstats area has moved to the end * of the header, the clear must be done in several steps: * Clear everything from the end of the encryption section up to the start of the abandoned gvstats area * (gvstats_rec_old_now_filler). This skips clearing the area previously occupied by gvstats, which is now available for reuse, * but continues to clear bg_trc_rec_tn, all bg_trc_rec_cntr, & all db_csh_acct_rec as well as any intervening filler. * Clear tp_cdb_sc_blkmod explicitly. * Clear the new gvstats area explicitly. * * This means we MUST NOT insert anything in the file header between the encryption section and the start of * gvstats_rec_old_now_filler nor move either of those end points without appropriately adjusting this macro. */ #define CLRGVSTATS(CSA) \ MBSTART { \ char *CHPTR; \ int CLRLEN; \ sgmnt_data_ptr_t CSD; \ \ CSD = CSA->hdr; \ CHPTR = (char *)CSD->filler_encrypt + SIZEOF(CSD->filler_encrypt); \ CLRLEN = (char *)&CSD->gvstats_rec_old_now_filler[0] - CHPTR; \ memset(CHPTR, 0, CLRLEN); \ memset((char *)&CSD->tp_cdb_sc_blkmod, 0, SIZEOF(CSD->tp_cdb_sc_blkmod)); \ memset((char *)&CSD->gvstats_rec, 0, SIZEOF(CSD->gvstats_rec)); \ gvstats_rec_csd2cnl(CSA); /* we update gvstats in cnl */ \ } MBEND #if defined(DEBUG) || defined(DEBUG_DB_CSH_COUNTER) # define INCR_DB_CSH_COUNTER(csa, counter, increment) \ if (csa->read_write || dba_bg == csa->hdr->acc_meth) \ csa->hdr->counter.curr_count += increment; #else # define INCR_DB_CSH_COUNTER(csa, counter, increment) #endif enum tp_ntp_blkmod_type /* used for accounting in cs_data->tp_cdb_sc_blkmod[] */ { /* TP transactions */ tp_blkmod_nomod = 0, tp_blkmod_gvcst_srch, tp_blkmod_t_qread, tp_blkmod_tp_tend, tp_blkmod_tp_hist, n_tp_blkmod_types, /* NON-TP transactions */ t_blkmod_nomod, t_blkmod_gvcst_srch, t_blkmod_gvcst_expand_key, t_blkmod_t_qread, t_blkmod_t_end1, t_blkmod_t_end2, t_blkmod_t_end3, t_blkmod_t_end4, /* MUPIP specific */ t_blkmod_mu_clsce, t_blkmod_mu_reduce_level, t_blkmod_mu_split, t_blkmod_mu_swap_blk, t_blkmod_reorg_funcs, n_nontp_blkmod_types }; /* Below is a list of macro bitmasks used to set the global variable "donot_commit". This variable should normally be 0. * But in rare cases, we could end up in situations where we know it is a restartable situation but decide not to * restart right away (because of interface issues that the function where this is detected cannot signal a restart * or because we don't want to take a performance hit to check this restartable situation in highly frequented code if * the restart will anyway be detected before commit. In this cases, this variable will take on non-zero values. * The commit logic will assert that this variable is indeed zero after validation but before proceeding with commit. */ #define DONOTCOMMIT_TPHIST_BLKTARGET_MISMATCH (1 << 0) /* Restartable situation seen in tp_hist */ #define DONOTCOMMIT_GVCST_DELETE_BLK_CSE_TLEVEL (1 << 1) /* Restartable situation seen in gvcst_delete_blk */ #define DONOTCOMMIT_JNLGETCHECKSUM_NULL_CR (1 << 2) /* Restartable situation seen in jnl_get_checksum.h */ #define DONOTCOMMIT_GVCST_KILL_ZERO_TRIGGERS (1 << 3) /* Restartable situation seen in gvcst_kill */ #define DONOTCOMMIT_GVCST_BLK_BUILD_TPCHAIN (1 << 4) /* Restartable situation seen in gvcst_blk_build */ #define DONOTCOMMIT_T_QREAD_BAD_PVT_BUILD (1 << 5) /* Restartable situation due to bad private build in t_qread */ #define DONOTCOMMIT_GVCST_SEARCH_LEAF_BUFFADR_NOTSYNC (1 << 6) /* Restartable situation seen in gvcst_search */ #define DONOTCOMMIT_GVCST_SEARCH_BLKTARGET_MISMATCH (1 << 7) /* Restartable situation seen in gvcst_search */ #define DONOTCOMMIT_GVCST_PUT_SPLIT_TO_RIGHT (1 << 8) /* Restartable situation seen in gvcst_put */ #define DONOTCOMMIT_T_WRITE_CSE_DONE (1 << 9) /* Restartable situation seen in t_write */ #define DONOTCOMMIT_T_WRITE_CSE_MODE (1 << 10) /* Restartable situation seen in t_write */ #define DONOTCOMMIT_TRIGGER_SELECT_XECUTE (1 << 11) /* Restartable situation seen in trigger_select */ #define DONOTCOMMIT_JNL_FORMAT (1 << 12) /* Restartable situation seen in jnl_format */ #define DONOTCOMMIT_COPY_PREV_KEY_TO_GVT_CLUE (1 << 13) /* Restartable situation seen in COPY_PREV_KEY_TO_GVT_CLUE */ #define DONOTCOMMIT_DSK_READ_EMPTY_BUT_NOT_FREE (1 << 14) /* Restartable situation seen in dsk_read */ #define TAB_BG_TRC_REC(A,B) B, enum bg_trc_rec_type { #include "tab_bg_trc_rec.h" n_bg_trc_rec_types }; #undef TAB_BG_TRC_REC #define UPGRD_WARN_INTERVAL (60 * 60 * 24) /* Once every 24 hrs */ /* The following structure is used to determine the endianess of a database header. */ typedef union { struct { unsigned short little_endian; unsigned short big_endian; } shorts; uint4 word32; } endian32_struct; #ifdef BIGENDIAN # define ENDIANCHECKTHIS big_endian #else # define ENDIANCHECKTHIS little_endian #endif #define CHECK_DB_ENDIAN(CSD,FNLEN,FNNAME) \ MBSTART { \ endian32_struct check_endian; \ check_endian.word32 = (CSD)->minor_dbver; \ if (!check_endian.shorts.ENDIANCHECKTHIS) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_DBENDIAN, 4, FNLEN, FNNAME, ENDIANOTHER, \ ENDIANTHIS); \ } MBEND #define FROZEN(CSD) ((CSD)->freeze || FALSE) #define FROZEN_HARD(CSA) ((CSA)->hdr->freeze && !(CSA)->nl->freeze_online) #define FROZEN_CHILLED(CSA) ((CSA)->hdr->freeze && (CSA)->nl->freeze_online) #define FREEZE_LATCH_HELD(CSA) (process_id == (CSA)->nl->freeze_latch.u.parts.latch_pid) #define CHILLED_AUTORELEASE_MASK 0x02 #define CHILLED_AUTORELEASE_REPORT_MASK 0x04 #define CHILLED_AUTORELEASE(CSA) (((CSA)->nl->freeze_online & CHILLED_AUTORELEASE_MASK) || FALSE) #define CHILLED_AUTORELEASE_REPORTED(CSA) (((CSA)->nl->freeze_online & CHILLED_AUTORELEASE_REPORT_MASK) || FALSE) #define DO_CHILLED_AUTORELEASE(CSA, CSD) \ MBSTART { \ GBLREF uint4 process_id; \ boolean_t was_latch; \ \ if (CHILLED_AUTORELEASE(CSA) && !CHILLED_AUTORELEASE_REPORTED(CSA)) \ { \ was_latch = FREEZE_LATCH_HELD(CSA); \ if (!was_latch) \ { /* Return value of "grab_latch" does not need to be checked because we pass \ * in GRAB_LATCH_INDEFINITE_WAIT as the timeout. \ */ \ grab_latch(&(CSA)->nl->freeze_latch, GRAB_LATCH_INDEFINITE_WAIT, WS_63, CSA); \ } \ if (CHILLED_AUTORELEASE(CSA) && !CHILLED_AUTORELEASE_REPORTED(CSA)) \ { \ (CSD)->freeze = FALSE; \ (CSD)->image_count = 0; \ (CSA)->nl->freeze_online = CHILLED_AUTORELEASE_MASK | CHILLED_AUTORELEASE_REPORT_MASK; \ send_msg_csa(CSA_ARG(CSA) VARLSTCNT(9) ERR_OFRZAUTOREL, 2, REG_LEN_STR((CSA)->region), \ ERR_ERRCALL, 3, CALLFROM); \ } \ if (!was_latch) \ rel_latch(&(CSA)->nl->freeze_latch); \ } \ } MBEND /* This is the structure describing a segment. It is used as a database file header (for MM or BG access methods). * The overloaded fields for MM and BG are n_bts, bt_buckets. */ /* ***NOTE*** If the field minor_dbver is updated, please also update gdsdbver.h and db_auto_upgrade.c appropriately * (see db_auto_upgrade for reasons and description). SE 5/2006 */ /* Fields prepened with v6_ are the fields from the V6 header version. They were just renamed in order to simplify * support for conversion between V6 and V7 headers. For the actual fields that are used in the code look at * the FIELDS EXTENDED IN V7 HEADER group. Once V6 DBs are no longer supported the v6_ fields can be converted in * to their new 8-byte size and the FIELDS EXTENDED IN V7 HEADER group can be converted to filler. */ typedef struct sgmnt_data_struct { /************* MOSTLY STATIC DATABASE STATE FIELDS **************************/ unsigned char label[GDS_LABEL_SZ]; int4 blk_size; /* Block size for the file. Static data defined at db creation time */ int4 v6_master_map_len; /* Length of master map in V6 header */ int4 bplmap; /* Blocks per local map (bitmap). static data defined at db creation time */ block_id_32 v6_start_vbn; /* starting virtual block number in V6 header */ enum db_acc_method acc_meth; /* Access method (BG or MM) */ uint4 max_bts; /* Maximum number of bt records allowed in file */ int4 n_bts; /* number of cache record/blocks */ int4 bt_buckets; /* Number of buckets in bt table */ int4 reserved_bytes; /* Database blocks will always leave this many bytes unused */ int4 max_rec_size; /* maximum record size allowed for this file */ int4 max_key_size; /* maximum key size allowed for this file */ uint4 lock_space_size; /* Number of bytes to be used for locks (in database for bg) */ uint4 extension_size; /* Number of gds data blocks to extend by */ uint4 def_coll; /* Default collation type for new globals */ uint4 def_coll_ver; /* Default collation type version */ boolean_t std_null_coll; /* 0 -> GT.M null collation,i,e, null subs collate between numeric and string * 1-> standard null collation i.e. null subs collate before numeric and string */ int null_subs; uint4 free_space; /* bytes in fileheader not currently used between master map & start_vbn */ mutex_spin_parms_struct mutex_spin_parms; int4 max_update_array_size; /* maximum size of update array needed for one non-TP set/kill */ int4 max_non_bm_update_array_size;/* maximum size of update array excepting bitmaps */ boolean_t file_corrupt; /* If set, it shuts the file down. No process (except DSE) can * successfully map this section after the flag is set to TRUE. Processes * that already have it mapped should produce an error the next time that * they use the file. The flag can only be reset by the DSE utility. */ enum mdb_ver minor_dbver; /* Minor DB version field that is incremented when minor changes to this * file-header or API changes occur. See note at top of sgmnt_data. */ uint4 jnl_checksum; uint4 wcs_phase2_commit_wait_spincnt; /* # of spin iterations before sleeping while waiting for phase2 commits */ enum mdb_ver last_mdb_ver; /* Minor DB version of the GT.M version that last accessed this database. * Maintained only by GT.M versions V5.3-003 and greater. */ /* The structure is 128-bytes in size at this point */ /************* FIELDS SET AT CREATION TIME ********************************/ char filler_created[52]; /* Now unused .. was "file_info created" */ boolean_t createinprogress; /* TRUE only if MUPIP CREATE is in progress. FALSE otherwise */ int4 creation_time4; /* Lower order 4-bytes of time when the database file was created */ uint4 reservedDBFlags; /* Bit mask field containing the reserved DB flags (field copied from gd_region) */ /************* FIELDS USED BY TN WARN PROCESSING *************************/ trans_num max_tn; /* Hardstop TN for this database */ trans_num max_tn_warn; /* TN for next TN_RESET warning for this database */ /************* FIELDS SET BY MUPIP BACKUP/REORG *************************/ trans_num last_inc_backup; trans_num last_com_backup; trans_num last_rec_backup; block_id_32 v6_last_inc_bkup_last_blk; /* Last block in the DB at time of last incremental backup (V6 header)*/ block_id_32 v6_last_com_bkup_last_blk; /* Last block in the DB at time of last comprehensive backup (V6 header)*/ block_id_32 v6_last_rec_bkup_last_blk; /* Last block in the DB at time of last record-ed backup (V6 header)*/ block_id_32 v6_reorg_restart_block; /* This is a V6 header field */ gtm_timet last_start_backup; /* Last successful backup start (was filler). gtm_timet dumped as string */ /************* FIELDS SET WHEN DB IS OPEN ********************************/ char now_running[MAX_REL_NAME];/* for active version stamp */ uint4 filler_owner_node; /* 4-byte filler - since owner_node is maintained on VMS only */ uint4 image_count; /* for db freezing. Set to "process_id" on Unix and "image_count" on VMS */ uint4 freeze; /* for db freezing. Set to "getuid" on Unix and "process_id" on VMS */ int4 kill_in_prog; /* counter for multi-crit kills that are not done yet */ int4 abandoned_kills; uint4 unused_freeze_online_filler; /* see field in node_local */ char filler_320[4]; /************* FIELDS USED IN V4 <==> V5 COMPATIBILITY MODE ****************/ trans_num tn_upgrd_blks_0; /* TN when blks_to_upgrd becomes 0. * Never set = 0 => we have not achieved this yet, * Always set = 1 => database was created as V5 (or current version) */ trans_num desired_db_format_tn; /* Database tn when last db format change occurred */ trans_num reorg_db_fmt_start_tn; /* Copy of desired_db_format_tn when MUPIP REORG UPGRADE/DOWNGRADE started */ block_id_32 v6_reorg_upgrd_dwngrd_restart_block; /* Block numbers lesser than this were last upgraded/downgraded by * MUPIP REORG UPGRADE|DOWNGRADE before being interrupted. * This is a V6 header field. */ block_id_32 v6_blks_to_upgrd; /* Blocks not at current block version level (V6 header)*/ block_id_32 v6_blks_to_upgrd_subzero_error; /* number of times "blks_to_upgrd" potentially became negative (V6 header)*/ enum db_ver desired_db_format; /* Output version for database blocks (normally current version) */ boolean_t fully_upgraded; /* Set to TRUE by MUPIP REORG UPGRADE when ALL blocks (including RECYCLED blocks) * have been examined and upgraded (if necessary) and blks_to_upgrd is set to 0; * If set to TRUE, this guarantees all blocks in the database are upgraded. * "blks_to_upgrd" being 0 does not necessarily guarantee the same since the * counter might have become incorrect (due to presently unknown reasons). * set to FALSE whenever desired_db_format changes or the database is * updated with V4 format blocks (by MUPIP JOURNAL). */ boolean_t db_got_to_v5_once; /* Set to TRUE by the FIRST MUPIP REORG UPGRADE (since MUPIP UPGRADE was run * to upgrade the file header to V5 format) when it completes successfully. * The FIRST reorg upgrade marks all RECYCLED blocks as FREE. Successive reorg * upgrades keep RECYCLED blocks as they are while still trying to upgrade them. * This is because ONLY the FIRST reorg upgrade could see RECYCLED blocks in V4 * format that are too full (lack the additional space needed by the V5 block * header) to be upgraded to V5 format. Once these are marked FREE, all future * block updates happen in V5 format in the database buffers so even if they * are written in V4 format to disk, they are guaranteed to be upgradeable. * This field marks that transition in the db and is never updated thereafter. */ boolean_t opened_by_gtmv53; /* Set to TRUE the first time this database is opened by GT.M V5.3-000 and higher */ char filler_384[12]; /************* FIELDS RELATED TO DB TRANSACTION HISTORY *****************************/ char v6_trans_hist[56]; /* This is the V6 transaction history. It is a char[] since the V7 header * doesn't need to know how to read this field and this keeps from having * to define the v6_th_index struct outside of v6_gdsfhead.h */ /************* FIELDS RELATED TO WRITE CACHE FLUSHING *******************************/ int4 write_fullblk; int4 statsdb_allocation; int4 flush_time[2]; int4 flush_trigger; int4 n_wrt_per_flu; /* Number of writes per flush call. Overloaded for BG and MM */ int4 wait_disk_space; /* seconds to wait for diskspace before giving up on a db block write */ int4 defer_time; /* defer write * 0 => immediate, * -1 => indefinite defer, * >0 => defer_time * flush_time[0] is actual defer time * default value = 1 => a write-timer every csd->flush_time[0] seconds */ int4 flush_trigger_top; /* biggest value for flush_trigger */ boolean_t mumps_can_bypass; /* Allow mumps processes to bypass flushing, access control, and ftok semaphore * in "gds_rundown". This was done to improve shutdown performance. */ boolean_t epoch_taper; /* If TRUE, GT.M tries to reduce dirty buffers as epoch approaches */ uint4 epoch_taper_time_pct; /* in the last pct we start tapering for time */ uint4 epoch_taper_jnl_pct; /* in the last pct we start tapering for jnl */ boolean_t asyncio; /* If TRUE, GT.M uses async I/O */ /************* FIELDS Used for update process performance improvement. Some may go away in later releases ********/ uint4 reserved_for_upd; /* Percentage (%) of blocks reserved for update process disk read */ uint4 avg_blks_per_100gbl; /* Number of blocks read on average for 100 global key read */ uint4 pre_read_trigger_factor;/* Percentage (%) of blocks reserved for prereader disk read */ uint4 writer_trigger_factor; /* For update process writers flush trigger */ /************* FIELDS USED ONLY BY UNIX ********************************/ int4 semid; /* Since int may not be of fixed size, int4 is used */ int4 shmid; /* Since int may not be of fixed size, int4 is used */ gtm_time8 gt_sem_ctime; /* time of creation of semaphore */ gtm_time8 gt_shm_ctime; /* time of creation of shared memory */ uint4 problksplit; /* Split db blocks due to restarts - */ /* 0: don't; >0: don't if records in block are <= */ char filler_unixonly[36]; /* to ensure this section has 64-byte multiple size */ /************* ACCOUNTING INFORMATION ********************************/ int4 filler_n_retries[CDB_MAX_TRIES];/* Now moved to TAB_GVSTATS_REC section */ int4 filler_n_puts; /* Now moved to TAB_GVSTATS_REC section */ int4 filler_n_kills; /* Now moved to TAB_GVSTATS_REC section */ int4 filler_n_queries; /* Now moved to TAB_GVSTATS_REC section */ int4 filler_n_gets; /* Now moved to TAB_GVSTATS_REC section */ int4 filler_n_order; /* Now moved to TAB_GVSTATS_REC section */ int4 filler_n_zprevs; /* Now moved to TAB_GVSTATS_REC section */ int4 filler_n_data; /* Now moved to TAB_GVSTATS_REC section */ uint4 filler_n_puts_duplicate; /* Now moved to TAB_GVSTATS_REC section */ uint4 filler_n_tp_updates; /* Now moved to TAB_GVSTATS_REC section */ uint4 filler_n_tp_updates_duplicate; /* Now moved to TAB_GVSTATS_REC section */ char filler_accounting_64_align[4]; /* to ensure this section has 64-byte multiple size */ /************* CCP/RC RELATED FIELDS (CCP STUFF IS NOT USED CURRENTLY BY GT.M) *************/ int4 staleness[2]; /* timer value */ int4 ccp_tick_interval[2]; /* quantum to release write mode if no write occurs and others are queued * These three values are all set at creation by mupip_create */ int4 ccp_quantum_interval[2];/* delta timer for ccp quantum */ int4 ccp_response_interval[2];/* delta timer for ccp mailbox response */ boolean_t ccp_jnl_before; /* used for clustered to pass if jnl file has before images */ boolean_t clustered; /* FALSE (clustering is currently unsupported) */ boolean_t unbacked_cache; /* FALSE for clustering. TRUE otherwise */ int4 rc_srv_cnt; /* Count of RC servers accessing database */ int4 dsid; /* DSID value, non-zero when being accessed by RC */ int4 rc_node; char filler_ccp_rc[8]; /* to ensure this section has 64-byte multiple size */ /************* REPLICATION RELATED FIELDS ****************/ seq_num reg_seqno; /* the jnl seqno of the last update to this region -- 8-byte aligned */ seq_num pre_multisite_resync_seqno; /* previous resync-seqno field now moved to the replication instance file */ trans_num zqgblmod_tn; /* db tn corresponding to zqgblmod_seqno - used in losttrans handling */ seq_num zqgblmod_seqno; /* minimum resync seqno of ALL -fetchresync rollbacks that happened on a secondary * (that was formerly a root primary) AFTER the most recent * MUPIP REPLIC -LOSTTNCOMPLETE command */ int4 repl_state; /* state of replication whether open/closed/was_open */ boolean_t multi_site_open; /* Set to TRUE the first time a process opens the database using * a GT.M version that supports multi-site replication. FALSE until then */ seq_num filler_seqno; /* formerly dualsite_resync_seqno but removed once dual-site support was dropped */ char filler_repl[16]; /* to ensure this section has 64-byte multiple size */ /************* TP RELATED FIELDS ********************/ int4 filler_n_tp_retries[12]; /* Now moved to TAB_GVSTATS_REC section */ int4 filler_n_tp_retries_conflicts[12]; /* Now moved to TAB_GVSTATS_REC section */ int4 tp_cdb_sc_blkmod[8]; /* Notes down the number of times each place got a cdb_sc_blkmod in tp. * Only first 4 array entries are updated now, but space is allocated * for 4 more if needed in the future. */ /************* JOURNALLING RELATED FIELDS ****************/ uint4 jnl_alq; uint4 jnl_deq; int4 jnl_buffer_size; /* in 512-byte pages */ boolean_t jnl_before_image; int4 jnl_state; /* journaling state: same as enum jnl_state_codes in jnl.h */ uint4 jnl_file_len; /* journal file name length */ uint4 autoswitchlimit; /* limit in disk blocks (max 4GB) when jnl should be auto switched */ int4 epoch_interval; /* Time between successive epochs in epoch-seconds */ uint4 alignsize; /* alignment size for JRT_ALIGN */ int4 jnl_sync_io; /* drives sync I/O ('direct' if applicable) for journals, if set (UNIX) */ int4 yield_lmt; /* maximum number of times a process yields to get optimal jnl writes */ boolean_t turn_around_point; trans_num jnl_eovtn; /* last tn for a closed jnl; otherwise epoch tn from the epoch before last */ char filler_jnl[8]; /* to ensure this section has 64-byte multiple size */ /************* INTERRUPTED RECOVERY RELATED FIELDS ****************/ seq_num intrpt_recov_resync_seqno;/* resync/fetchresync jnl_seqno of interrupted rollback */ jnl_tm_t intrpt_recov_tp_resolve_time;/* since-time for the interrupted recover */ boolean_t recov_interrupted; /* whether a MUPIP JOURNAL RECOVER/ROLLBACK on this db got interrupted */ int4 intrpt_recov_jnl_state; /* journaling state at start of interrupted recover/rollback */ int4 intrpt_recov_repl_state;/* replication state at start of interrupted recover/rollback */ /************* TRUNCATE RELATED FIELDS ****************/ block_id_32 v6_before_trunc_total_blks; /* Used in recover_truncate to detect interrupted truncate (V6 header)*/ block_id_32 v6_after_trunc_total_blks; /* All these fields are used to repair interrupted truncates (V6 header)*/ block_id_32 v6_before_trunc_free_blocks; /* V6 header field */ char filler_1k[28]; /************* POTENTIALLY LARGE CHARACTER ARRAYS **************/ unsigned char jnl_file_name[JNL_NAME_SIZE]; /* journal file name */ unsigned char reorg_restart_key[OLD_MAX_KEY_SZ + 1]; /* 1st key of a leaf block where reorg was done last time. * Note: In mu_reorg we don't save keys longer than OLD_MAX_KEY_SZ */ char machine_name[MAX_MCNAMELEN]; /************* ENCRYPTION-RELATED FIELDS **************/ /* Prior to the introduction of encryption_hash and, subsequently, other encryption fields, this space was occupied by a * char filler_2k[256]. Now that the encryption fields consume a part of that space, the filler has been reduced in size. */ char encryption_hash[GTMCRYPT_RESERVED_HASH_LEN]; char encryption_hash2[GTMCRYPT_RESERVED_HASH_LEN]; boolean_t non_null_iv; block_id_32 v6_encryption_hash_cutoff; /* Points to the first block to be encrypted by MUPIP REORG -ENCRYPT with * encryption_hash2. The value of -1 indicates that no (re)encryption is * happening. This is a V6 header field */ trans_num encryption_hash2_start_tn; /* Indicates the lowest transaction number at which a block is encrypted * with encryption_hash2. */ char filler_encrypt[80]; /***************************************************/ /* The CLRGVSTATS macro wipes out everything from here through the GVSTATS fields up to gvstats_rec_old_now_filler * starting from the end of the space reserved for the encryption_hash above - DO NOT insert anything in this range or * move those two end points without appropriately adjusting that macro */ /************* BG_TRC_REC RELATED FIELDS ***********/ # define TAB_BG_TRC_REC(A,B) bg_trc_rec_tn B##_tn; # include "tab_bg_trc_rec.h" # undef TAB_BG_TRC_REC char bg_trc_rec_tn_filler [1200 - (SIZEOF(bg_trc_rec_tn) * n_bg_trc_rec_types)]; # define TAB_BG_TRC_REC(A,B) bg_trc_rec_cntr B##_cntr; # include "tab_bg_trc_rec.h" # undef TAB_BG_TRC_REC char bg_trc_rec_cntr_filler[600 - (SIZEOF(bg_trc_rec_cntr) * n_bg_trc_rec_types)]; /************* DB_CSH_ACCT_REC RELATED FIELDS ***********/ # define TAB_DB_CSH_ACCT_REC(A,B,C) db_csh_acct_rec A; # include "tab_db_csh_acct_rec.h" # undef TAB_DB_CSH_ACCT_REC char db_csh_acct_rec_filler_4k[248 - (SIZEOF(db_csh_acct_rec) * n_db_csh_acct_rec_types)]; /************* FORMER GVSTATS_REC RELATED FIELDS ***********/ /* gvstats_rec has been moved to the end of the header, */ /* leaving filler here. This can be reused in the future */ char gvstats_rec_old_now_filler[496]; char gvstats_rec_filler_4k_plus_512[16]; char filler_4k_plus_512[184]; /* Note: this filler array should START at offset 4K+512. * So any additions of new fields should happen at the END of this * filler array and the filler array size correspondingly adjusted. */ /************* FIELDS FOR V6 TO V7 UPGRADE *********************************/ block_id offset; /* offset produced by mmb extension; interim pointer adjustment */ int4 max_rec; /* pessimistic extimate of what bkl_size could hold */ int4 i_reserved_bytes; /* for mangement of index splits; could be retained as a characteristic */ boolean_t db_got_to_V7_once; /* set TRUE by MUPIP REORG UPGRADE once it completes all work on region */ char filler[4]; /* Filler to make 8-byte alignment explicit */ /************* FIELDS EXTENDED IN V7 HEADER ********************************/ /* The sub-headers indicate where these fields were originally located */ /*MOSTLY STATIC DATABASE STATE FIELDS*/ gtm_int8 master_map_len; /* Length of master map */ block_id start_vbn; /* starting virtual block number. */ /*FIELDS SET BY MUPIP BACKUP/REORG*/ block_id last_inc_bkup_last_blk; /* Last block in the database at time of last incremental backup */ block_id last_com_bkup_last_blk; /* Last block in the database at time of last comprehensive backup */ block_id last_rec_bkup_last_blk; /* Last block in the database at time of last record-ed backup */ block_id reorg_restart_block; /* FIELDS USED IN V4 <==> V5 COMPATIBILITY MODE */ block_id reorg_upgrd_dwngrd_restart_block; /* Block numbers lesser than this were last upgraded/downgraded by * MUPIP REORG UPGRADE|DOWNGRADE before being interrupted */ block_id blks_to_upgrd; /* Blocks not at current block version level */ block_id blks_to_upgrd_subzero_error; /* number of times "blks_to_upgrd" potentially became negative */ /* TRUNCATE RELATED FIELDS */ block_id before_trunc_total_blks; /* Used in recover_truncate to detect interrupted truncate */ block_id after_trunc_total_blks; /* All these fields are used to repair interrupted truncates */ block_id before_trunc_free_blocks; /* ENCRYPTION-RELATED FIELDS */ block_id encryption_hash_cutoff; /* Points to the first block to be encrypted by MUPIP REORG -ENCRYPT with * encryption_hash2. The value of -1 indicates that no (re)encryption is * happening. */ /* FIELDS RELATED TO DB TRANSACTION HISTORY */ th_index trans_hist; /* transaction history - if moved from 10th filehdr block, change TH_BLOCK */ /************* INTERRUPTED RECOVERY RELATED FIELDS continued ****************/ seq_num intrpt_recov_resync_strm_seqno[MAX_SUPPL_STRMS];/* resync/fetchresync jnl_seqno of interrupted rollback * corresponding to each non-supplementary stream. */ /************* DB CREATION AND UPGRADE CERTIFICATION FIELDS ***********/ enum db_ver creation_db_ver; /* Major DB version at time of creation */ enum mdb_ver creation_mdb_ver; /* Minor DB version at time of creation */ enum db_ver certified_for_upgrade_to; /* Version the database is certified for upgrade to */ int4 filler_5k; /************* SECSHR_DB_CLNUP RELATED FIELDS (now moved to node_local) ***********/ int4 secshr_ops_index_filler; int4 secshr_ops_array_filler[255]; /* taking up 1k */ /********************************************************/ compswap_time_field next_upgrd_warn; /* Time when we can send the next upgrade warning to the operator log */ uint4 is_encrypted; /* Encryption state of the database as a superimposition of IS_ENCRYPTED and * TO_BE_ENCRYPTED flags. */ uint4 db_trigger_cycle; /* incremented every MUPIP TRIGGER command that changes ^#t global contents */ /************* SUPPLEMENTARY REPLICATION INSTANCE RELATED FIELDS ****************/ seq_num strm_reg_seqno[MAX_SUPPL_STRMS]; /* the jnl seqno of the last update to this region for a given * supplementary stream -- 8-byte aligned */ seq_num save_strm_reg_seqno[MAX_SUPPL_STRMS]; /* a copy of strm_reg_seqno[] before it gets changed in * "mur_process_intrpt_recov". Used only by journal recovery. * See comment in "mur_get_max_strm_reg_seqno" function for * purpose of this field. Must also be 8-byte aligned. */ /************* MISCELLANEOUS FIELDS ****************/ boolean_t freeze_on_fail; /* Freeze instance if failure of this database observed */ boolean_t span_node_absent; /* Database does not contain the spanning node */ boolean_t maxkeysz_assured; /* All the keys in the database are less than MAX_KEY_SIZE */ boolean_t hasht_upgrade_needed; /* ^#t global needs to be upgraded from V62000 to post-V62000 format */ boolean_t defer_allocate; /* If FALSE: Use fallocate() preallocate space from the disk */ boolean_t filler_ftok_counter_halted; /* Used only in V6.3-000. Kept as a filler just to be safe */ boolean_t filler_access_counter_halted; /* Used only in V6.3-000. Kept as a filler just to be safe */ boolean_t lock_crit_with_db; /* flag controlling LOCK crit mechanism; see interlock.h */ uint4 basedb_fname_len; /* byte length of filename stored in "basedb_fname[]" */ unsigned char basedb_fname[256]; /* full path filaneme of corresponding baseDB if this is a statsDB */ boolean_t read_only; /* If TRUE, GT.M uses a process-private mmap instead of IPC */ /************* GVSTATS_REC RELATED FIELDS ***********/ /* gvstats_rec has outgrown its previous space. * Note also that we are pushing the 8k barrier on sgmnt_data now, but are not critically pressed for * space in the future because the former GVSTATS area (above) will be available for reuse. */ gvstats_rec_csd_t gvstats_rec; /* As of GTM-8863 1304 bytes == 163 counters */ char filler_8k[1464 - SIZEOF(gvstats_rec_csd_t)]; /********************************************************/ /* Master bitmap immediately follows. Tells whether the local bitmaps have any free blocks or not. */ } sgmnt_data; #ifdef DB64 # ifdef __osf__ # pragma pointer_size(save) # pragma pointer_size(long) # else # error UNSUPPORTED PLATFORM # endif #endif typedef sgmnt_data *sgmnt_data_ptr_t; #ifdef DB64 # ifdef __osf__ # pragma pointer_size(restore) # endif #endif typedef struct { FILL8DCL(cache_que_heads_ptr_t, cache_state, 1); /* pointer to beginnings of state queues */ } sgbg_addrs; typedef struct { int filler; } sgmm_addrs; #define MAX_NM_LEN MAX_MIDENT_LEN #define MIN_RN_LEN 1 #define MAX_RN_LEN MAX_MIDENT_LEN #define MIN_SN_LEN 1 #define MAX_SN_LEN MAX_MIDENT_LEN #define STR_SUB_PREFIX 0x0FF #define SUBSCRIPT_STDCOL_NULL 0x01 #define STR_SUB_ESCAPE 0X01 #define SPANGLOB_SUB_ESCAPE 0X02 #define STR_SUB_MAXVAL 0xFF #define SUBSCRIPT_ZERO 0x080 #define SUBSCRIPT_BIAS 0x0BE #define NEG_MNTSSA_END 0x0FF #define KEY_DELIMITER 0X00 #ifdef BIGENDIAN typedef struct { unsigned int two : 4; unsigned int one : 4; } sub_num; #else typedef struct { unsigned int one : 4; unsigned int two : 4; } sub_num; #endif #define MIN_DB_BLOCKS 10 /* this should be maintained in conjunction with the mimimum allocation in GDEINIT.M */ #define MIN_GBUFF_LIMIT 32 /* minimum gbuff limit */ #define REORG_GBUFF_LIMIT "64" /* default gbuff_limit for REORG */ /* definition for NULL_SUBSCRIPTS */ #define NEVER 0 #define ALWAYS 1 #define ALLOWEXISTING 2 #define OFFSET(x,y) ((uchar_ptr_t)x - (uchar_ptr_t)y) #define FC_READ 0 #define FC_WRITE 1 #define FC_OPEN 2 #define FC_CLOSE 3 #define DO_BADDBVER_CHK(REG, TSD) \ MBSTART { \ if (MEMCMP_LIT(TSD->label, GDS_LABEL) && MEMCMP_LIT(TSD->label, V6_GDS_LABEL)) \ { \ if (memcmp(TSD->label, GDS_LABEL, GDS_LABEL_SZ - 3)) \ rts_error_csa(CSA_ARG(REG2CSA(REG)) VARLSTCNT(4) ERR_DBNOTGDS, 2, \ DB_LEN_STR(REG)); \ else \ rts_error_csa(CSA_ARG(REG2CSA(REG)) VARLSTCNT(4) ERR_BADDBVER, 2, \ DB_LEN_STR(REG)); \ } \ } MBEND /* one thing to watch out for is if online rollback is in progress, file_corrupt may be set * between mur_open_files() & mur_close_files(). In this case, we overload the TRUE (1) value * of "file_corrupt" to '2' (TRUE + 1) to indicate a temporary situation and spin for a bit, * rereading the header, if we see that. If this doesn't clear (perhaps there are lots of * rollbacks being spawned), we do what we would have done under the original logic and * declare ERR_DBFLCORRP */ #define DO_DB_HDR_CHECK(REG, TSD) \ MBSTART { \ GBLREF boolean_t mupip_jnl_recover; \ uint4 gtm_errcode = 0; \ int slp_cnt = 0; \ int err = 0; \ boolean_t is_logged = FALSE; \ \ if (TSD->createinprogress) \ gtm_errcode = ERR_DBCREINCOMP; \ if (TSD->file_corrupt && !mupip_jnl_recover && !TREF(in_mupip_integ) && \ !(IS_MUPIP_IMAGE && TREF(skip_file_corrupt_check))) \ { \ while (TSD->file_corrupt == (TRUE + 1)) /* temp condition during onl rlbck */ \ { \ if (!is_logged) \ { \ send_msg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_TEXT, 2, \ LEN_AND_LIT("Spinning for 0-15 seconds until " \ "clean DB header")); \ is_logged = TRUE; \ } \ wcs_sleep(1); \ if (15000 < slp_cnt++) \ { \ send_msg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_TEXT, 2, \ LEN_AND_LIT("Spin wait for clean DB header failed")); \ break; \ } \ READ_DB_FILE_HEADER(REG, TSD, err); \ if (err) \ { \ REVERT; \ return -1; \ } \ } \ if (TSD->file_corrupt) /* Didn't clear (or wasn't temporary) */ \ { \ gtm_errcode = ERR_DBFLCORRP; \ } \ } \ if ((dba_mm == TSD->acc_meth) && TSD->blks_to_upgrd) \ gtm_errcode = ERR_MMNODYNUPGRD; \ if (0 != gtm_errcode) \ { \ if (IS_DSE_IMAGE) \ { \ gtm_errcode = MAKE_MSG_WARNING(gtm_errcode); \ gtm_putmsg_csa(CSA_ARG(REG2CSA(REG)) VARLSTCNT(4) gtm_errcode, 2, \ DB_LEN_STR(REG)); \ } else \ rts_error_csa(CSA_ARG(REG2CSA(REG)) VARLSTCNT(4) gtm_errcode, 2, \ DB_LEN_STR(REG)); \ } \ } MBEND #define REG2CSA(REG) (((REG) && (REG)->dyn.addr && (REG)->dyn.addr->file_cntl) ? (&FILE_INFO(REG)->s_addrs) : NULL) #define JCTL2CSA(JCTL) (((JCTL) && (JCTL->reg_ctl)) ? (JCTL->reg_ctl->csa) : NULL) /* A structure to store the encryption settings when a reorg_encrypt_cycle change is detected in shared memory in the middle of a * transaction. We updated the private copy of encryption settings and (re)initialize handles, if needed, based on this information * before restarting the transaction. Note that this structure should be populated at a safe time, such as while holding crit or * having otherwise ensured that MUPIP REORG -ENCRYPT cannot cross the boundary of another reorg_encrypt_cycle (such as in * dsk_read.c and wcs_wtstart.c); however, read access does not require crit (such as in jnl_format.c). */ typedef struct { uint4 reorg_encrypt_cycle; uint4 is_encrypted; boolean_t non_null_iv; uint4 filler_0; /* Make 8 byte alignment explicit */ block_id encryption_hash_cutoff; trans_num encryption_hash2_start_tn; char encryption_hash[GTMCRYPT_HASH_LEN]; char encryption_hash2[GTMCRYPT_HASH_LEN]; boolean_t issued_db_init_crypt_warning; /* Indicates whether we issued a warning-severity encryption-setup-related * message in db_init for a non-mumps process */ uint4 filler_1; /* Make 8 byte alignment explicit */ } enc_info_t; /* Macro to copy the encryption information into an enc_info_t structure. */ #define COPY_ENC_INFO_INT(SRC, DST, REORG_ENCRYPT_CYCLE) \ MBSTART { \ (DST)->reorg_encrypt_cycle = REORG_ENCRYPT_CYCLE; \ (DST)->is_encrypted = (SRC)->is_encrypted; \ (DST)->non_null_iv = (SRC)->non_null_iv; \ (DST)->encryption_hash_cutoff = (SRC)->encryption_hash_cutoff; \ (DST)->encryption_hash2_start_tn = (SRC)->encryption_hash2_start_tn; \ memcpy((DST)->encryption_hash, (SRC)->encryption_hash, GTMCRYPT_HASH_LEN); \ memcpy((DST)->encryption_hash2, (SRC)->encryption_hash2, GTMCRYPT_HASH_LEN); \ DEBUG_ONLY((DST)->filler_0 = 0;) \ DEBUG_ONLY((DST)->filler_1 = 0;) \ } MBEND \ /* Macro to copy the encryption information into an enc_info_t structure. */ #define COPY_ENC_INFO(SRC, DST, REORG_ENCRYPT_CYCLE) \ MBSTART { \ DEBUG_ONLY(enc_info_t before); \ \ DEBUG_ONLY(COPY_ENC_INFO_INT(SRC, &before, REORG_ENCRYPT_CYCLE)); \ /* This is to have the following memcmp succeed; normally, the issued_db_init_crypt_warning \ * field is never updated once set. \ */ \ DEBUG_ONLY(before.issued_db_init_crypt_warning = (DST)->issued_db_init_crypt_warning); \ COPY_ENC_INFO_INT(SRC, DST, REORG_ENCRYPT_CYCLE); \ /* This macro is not necessarily invoked while holding crit, yet none of its usages should be \ * subject to concurrent database file header changes of encrypted settings, so assert that. \ * The only exception is "encryption_hash_cutoff" which could be concurrently changed (by a \ * MUPIP REORG ENCRYPT) but is not critical encryption information so skip that part. \ */ \ DEBUG_ONLY(before.encryption_hash_cutoff = DST->encryption_hash_cutoff;) \ DEBUG_ONLY(assert(!memcmp(&before, DST, SIZEOF(enc_info_t)))); \ } MBEND \ #define INITIALIZE_CSA_ENCR_PTR(CSA, CSD, UDI, DO_CRYPT_INIT, CRYPT_WARNING, DEFER_COPY) \ MBSTART { \ GBLREF bool in_mupip_freeze; \ \ if (DO_CRYPT_INIT) \ { \ CSA->encr_ptr = (enc_info_t *)malloc(SIZEOF(enc_info_t)); \ /* It should be safe to copy encryption key information from CSD to CSA because only a concurrent \ * REORG -ENCRYPT may be changing these fields, and it takes the ftok access control semaphore on \ * a live (non-standalone) database before changing things. But the caller of this macro is expected to \ * hold either the ftok access control semaphore (udi->grabbed_fotk_sem) OR the database access control \ * semaphore (udi->grabbed_access_sem) in case of standalone access. This ensures a safe copy. The only \ * exception is if caller is "db_init" and we are DSE, LKE, or MUPIP FREEZE, as they can bypass getting \ * the ftok semaphore, but in those cases we do not rely much on the encryption settings and in the \ * places where we do rely, we expect the users know what they are doing with these admin tools. \ * Assert accordingly. \ */ \ assert(UDI->grabbed_ftok_sem || UDI->grabbed_access_sem || IS_DSE_IMAGE || IS_LKE_IMAGE \ || (IS_MUPIP_IMAGE && in_mupip_freeze)); \ if (!(DEFER_COPY)) \ { \ COPY_ENC_INFO(CSD, CSA->encr_ptr, CSA->nl->reorg_encrypt_cycle); \ CSA->encr_ptr->issued_db_init_crypt_warning = CRYPT_WARNING; \ } else \ { /* Defer the copy until needed later, as detected by a mismatch in reorg_encrypt_cycle. */ \ memset(CSA->encr_ptr, 0, SIZEOF(enc_info_t)); \ CSA->encr_ptr->reorg_encrypt_cycle = -1; \ } \ } else \ CSA->encr_ptr = NULL; \ } MBEND /* Encryption key reinitialization cannot be safely done if we are in the middle of a TP transaction that has already * done some updates to a journaled database (old keys would have been used for prior calls to "jnl_format" in this * transaction) as otherwise we would be a mix of journal records encrypted using old and new keys in the same TP * transaction. Just to be safe, we do the same for read-only TP transaction ("dollar_tlevel" global variable covers * both these cases) as well as a non-TP transaction that is read_write ("update_trans" global variable covers this case). * In all these cases, we know the caller is capable of restarting the transaction which will sync up the cycles at a safe * point (start of the retry). * * If both these global variables are zero, it is possible this is a non-TP transaction that is read-only OR a non-transaction. * In the latter case, it is not just safe but essential to sync new keys since callers might be relying on this. * In the former case, it is thankfully safe to sync so we do the sync if both these variables are zero. * * If it is unsafe to sync keys, the caller of this macro has to cause a restart of the ongoing transaction (caller "t_qread") * OR skip doing encrypt/decrypt operations (caller "wcs_wtstart") */ #define IS_NOT_SAFE_TO_SYNC_NEW_KEYS(DOLLAR_TLEVEL, UPDATE_TRANS) (DOLLAR_TLEVEL || UPDATE_TRANS) #define SIGNAL_REORG_ENCRYPT_RESTART(REORG_ENCRYPT_IN_PROG, REORG_ENCRYPT_CSA, CNL, CSA, CSD, STATUS, PID) \ MBSTART { \ assert(!REORG_ENCRYPT_IN_PROG); \ DBG_RECORD_BLOCK_RETRY(CSD, CSA, CNL, PID); \ COPY_ENC_INFO(CSD, (CSA)->encr_ptr, (CNL)->reorg_encrypt_cycle); \ assert(NULL == REORG_ENCRYPT_CSA); \ REORG_ENCRYPT_CSA = CSA; \ STATUS = cdb_sc_reorg_encrypt; \ } MBEND typedef struct file_control_struct { sm_uc_ptr_t op_buff; gtm_int64_t op_pos; int op_len; void *file_info; /* Pointer for OS specific struct */ char op; } file_control; typedef struct header_struct_struct { char label[12]; unsigned filesize; /* size of file excluding GDE info */ /* removed unused file_log struct */ } header_struct; typedef struct gdr_name_struct { mstr name; mstr exp_name; struct gdr_name_struct *link; struct gd_addr_struct *gd_ptr; } gdr_name; typedef struct gd_inst_info_struct { char instfilename[MAX_FN_LEN + 1]; /* + 1 for null */ } gd_inst_info; typedef struct gd_addr_struct { struct gd_region_struct *local_locks; int4 max_rec_size; uint4 n_maps; uint4 n_regions; uint4 n_segments; uint4 n_gblnames; /* could be 0 if no global has any collation characteristics defined */ uint4 var_maps_len; /* length (in bytes) of variable MAPS sections in .gld file */ struct gd_binding_struct *maps; struct gd_region_struct *regions; struct gd_segment_struct *segments; struct gd_gblname_struct *gblnames; struct gd_inst_info_struct *instinfo; struct gd_addr_struct *link; struct hash_table_mname_struct *tab_ptr; gd_id *id; UINTPTR_T end; uint4 has_span_gbls; /* has at least one global which spans multiple regions */ bool ygs_map_entry_changed; /* used by "gvcst_init_statsDB" */ bool is_dummy_gbldir; /* TRUE if this structure is created by "create_dummy_gbldir". * FALSE if this structure is created by "gd_load". */ #ifdef GTM64 char filler[2]; /* filler to store runtime structures without changing gdeget/gdeput.m */ #else char filler[6]; #endif struct gd_info *thread_gdi; /* has information on the multiplexing thread - only used on linux AIO */ } gd_addr; typedef gd_addr *(*gd_addr_fn_ptr)(); typedef struct gd_segment_struct { unsigned short sname_len; unsigned char sname[MAX_SN_LEN + 1]; unsigned short fname_len; unsigned char fname[MAX_FN_LEN + 1]; unsigned short blk_size; unsigned short full_blkwrt; uint4 ext_blk_count; uint4 allocation; struct CLB *cm_blk; unsigned char defext[4]; char defer_time; /* Was passed in cs_addrs */ unsigned char file_type; unsigned char buckets; /* Was passed in FAB */ unsigned char windows; /* Was passed in FAB */ uint4 lock_space; uint4 global_buffers; /* Was passed in FAB */ uint4 reserved_bytes; /* number of bytes to be left in every database block */ uint4 mutex_slots; /* copied over to NUM_CRIT_ENTRY(CSD) */ boolean_t defer_allocate; /* If FALSE: Use fallocate() preallocate space from the disk */ enum db_acc_method acc_meth; file_control *file_cntl; struct gd_region_struct *repl_list; uint4 is_encrypted; boolean_t asyncio; /* copied over to csd->asyncio at db creation time */ boolean_t read_only; char filler[12]; /* filler to store runtime structures without changing gdeget/gdeput.m */ } gd_segment; typedef union { int4 offset; /* relative offset to segment */ gd_segment *addr; /* absolute address of segment */ } gd_seg_addr; typedef struct gd_region_struct { unsigned short rname_len; unsigned char rname[MAX_RN_LEN + 1]; unsigned short max_key_size; uint4 max_rec_size; gd_seg_addr dyn; gd_seg_addr stat; bool open; bool lock_write; /* Field is not currently used by GT.M */ char null_subs; /* 0 ->NEVER(previous NO), 1->ALWAYS(previous YES), 2->ALLOWEXISTING * i.e. will allow read null subs but prohibit set */ unsigned char jnl_state; /* deleted gbl_lk_root and lcl_lk_root, obsolete fields */ uint4 jnl_alq; uint4 jnl_deq; uint4 jnl_autoswitchlimit; uint4 jnl_alignsize; /* not used, reserved */ int4 jnl_epoch_interval; /* not used, reserved */ int4 jnl_sync_io; /* not used, reserved */ int4 jnl_yield_lmt; /* not used, reserved */ int4 jnl_buffer_size; bool jnl_before_image; bool opening; bool read_only; bool was_open; unsigned char cmx_regnum; unsigned char def_coll; bool std_null_coll; /* 0 -> GT.M null collation,i,e, null subs collate between numeric and string * 1-> standard null collation i.e. null subs collate before numeric and string */ bool freeze_on_fail; bool mumps_can_bypass; /* Allow mumps processes to bypass flushing, access control, and ftok semaphore * in "gds_rundown". This was done to improve shutdown performance. */ unsigned char jnl_file_len; unsigned char jnl_file_name[JNL_NAME_SIZE]; char align_filler[2]; /* filler to align the sizes */ int4 node; int4 sec_size; uint4 is_spanned; /* This is one of the regions that some spanning global maps to */ uint4 statsDB_reg_index; /* If this is a base region, this is the region index of the * corresponding statsDB region. * If this is a statsDB region, this is the region index of the * corresponding base region. */ bool epoch_taper; unsigned char reservedDBFlags; /* Flags for reservedDB types and/or features */ bool lock_crit_with_db; /* controls whether LOCK crit is separate (0) or shared with DB (1) */ /* All fields before this point are relied upon by GDE. All fields after this point are relied upon only by * the runtime logic (i.e. it is one big filler/padding area as far as GDE is concerned). */ bool statsDB_setup_started; gd_addr *owning_gd; bool statsDB_setup_completed; char filler[39]; /* filler to store runtime structures without changing gdeget/gdeput.m */ } gd_region; typedef struct sgmnt_addrs_struct { sgmnt_data_ptr_t hdr; sm_uc_ptr_t bmm; sm_uc_ptr_t wc; bt_rec_ptr_t bt_header; bt_rec_ptr_t bt_base; th_rec_ptr_t th_base; th_index_ptr_t ti; node_local_ptr_t nl; CRIT_PTR_T critical; struct shmpool_buff_hdr_struct *shmpool_buffer; /* 1MB chunk of shared memory that we micro manage */ sm_uc_ptr_t db_addrs[2]; struct mlk_ctldata_struct *mlkctl; uint4 mlkctl_len; /* The size of the cnl lock space */ struct gv_namehead_struct *dir_tree; # ifdef GTM_TRIGGER struct gv_namehead_struct *hasht_tree; # endif struct sgmnt_addrs_struct *next_fenced; /* NULL if db has journaling turned off (or disabled) * Otherwise (db has journaling turned on), it is * NULL if this db was not updated in this TP/ZTP * non-NULL if this db was updated in this TP/ZTP * The non-NULL value points to the next csa that * has a non-NULL next_fenced value i.e. a linked list * of csas. The end of the list is JNL_FENCE_LIST_END * (cannot use NULL due to special meaning described * above and hence using a macro which evaluates to -1). */ struct jnl_private_control_struct *jnl; struct sgm_info_struct *sgm_info_ptr; gd_region *region; /* the region corresponding to this csa */ struct hash_table_mname_struct *gvt_hashtab; /* NON-NULL only if regcnt > 1; * Maintains all gv_targets mapped to this db file */ void *miscptr; /* pointer to rctl for this region (if jgbl.forw_phase_recovery) * pointer to gvt_hashtab for this region (if DSE_IMAGE) * pointer to repl_rctl for this region (if source server) * NULL in all other cases. */ struct sgmnt_addrs_struct *next_csa; /* points to csa of NEXT database that has been opened by this process */ gtmcrypt_key_t encr_key_handle; gtmcrypt_key_t encr_key_handle2; enc_info_t *encr_ptr; /* Copy of encryption info from the database file header */ struct snapshot_context_struct *ss_ctx; union { sgmm_addrs mm; sgbg_addrs bg; /* May add new pointers here for other methods or change to void ptr */ } acc_meth; gvstats_rec_t *gvstats_rec_p; /* Pointer to either the stats in this structure or the stats in a shared * global gvstats_rec_t. All access to process stats should be through this ptr. */ gvstats_rec_t gvstats_rec; trans_num dbsync_timer_tn;/* copy of csa->ti->curr_tn when csa->dbsync_timer became TRUE. * used to check if any updates happened in between when we flushed all * dirty buffers to disk and when the idle flush timer (5 seconds) popped. */ /* 8-byte aligned at this point on all platforms (32-bit, 64-bit or Tru64 which is a mix of 32-bit and 64-bit pointers) */ cache_rec_ptr_t our_midnite; /* anchor if we are using a gbuff_limit */ size_t fullblockwrite_len; /* Length of a full block write */ sm_off_t our_lru_cache_rec_off; /* last used cache pointer for when we are using a gbuff_limit */ block_id total_blks; /* Last we knew, file was this big. Used to signal MM processing file was * extended and needs to be remapped. In V55000 was used with BG to detect * file truncates. It is no longer used for that purpose: it was not necessary * in the first place because bitmap block validations in t_end/tp_tend prevent * updates from trying to commit past the end of the file. * See mu_truncate.c for more details. */ block_id prev_free_blks; /* The following uint4's are treated as bools but must be 4 bytes to avoid interaction between * bools in interrupted routines and possibly lost data */ volatile uint4 timer; /* This process has a timer for this region */ volatile uint4 in_wtstart; /* flag we are busy writing */ volatile uint4 now_crit; /* This process has the critical write lock */ volatile uint4 wbuf_dqd; /* a write buffer dequeued - extra cleanup required if process dies with this on */ uint4 stale_defer; /* Stale processing deferred this region */ boolean_t freeze; volatile boolean_t dbsync_timer; /* whether a timer to sync the filehdr (and write epoch) is active */ block_id reorg_last_dest; /* last destinition block used for swap */ boolean_t jnl_before_image; boolean_t read_write; boolean_t orig_read_write; /* copy of "csa->read_write" at dbfilopn time (needed at gds_rundown time * to know real permissions in case csa->read_write gets reset (e.g. for statsdb) */ boolean_t persistent_freeze; /* if true secshr_db_clnup() won't unfreeze this region */ /* The following 3 fields are in cs_addrs instead of in the file-header since they are a function * of the journal-record sizes that can change with journal-version-numbers (for the same database). */ int4 pblk_align_jrecsize; /* maximum size of a PBLK record with corresponding ALIGN record */ int4 min_total_tpjnl_rec_size; /* minimum journal space requirement for a TP transaction */ int4 min_total_nontpjnl_rec_size; /* minimum journal space requirement for a non-TP transaction */ int4 jnl_state; /* journaling state: it can be 0, 1 or 2 (same as enum jnl_state_codes in jnl.h) */ int4 repl_state; /* state of replication whether open/closed/was_open */ uint4 crit_check_cycle; /* Used to mark which regions in a transaction legiticamtely have crit */ int4 backup_in_prog; /* true if online backup in progress for this region (used in op_tcommit/tp_tend) */ boolean_t snapshot_in_prog; /* true if snapshots are in progress for this region */ int4 ref_cnt; /* count of number of times csa->nl->ref_cnt was incremented by this process */ int4 fid_index; /* index for region ordering based on unique_id */ char filler[4]; int4 regnum; /* Region number (region open counter) used by journaling so all tokens have a unique prefix per region (and all regions have same prefix) */ int4 n_pre_read_trigger; /* For update process to keep track of progress and when to trigger pre-read */ uint4 jnlpool_validate_check; /* See the comment above VALIDATE_INITIALIZED_JNLPOOL for details on this field */ int4 regcnt; /* # of regions that have this as their csa */ boolean_t t_commit_crit; /* FALSE by default. Non-zero value if in the middle of database commit. * This assumes the following additional values. * = T_COMMIT_CRIT_PHASE0 if commit started and jnl records are being written * = T_COMMIT_CRIT_PHASE1 just before calling "bg_update_phase1/mm_update" * = T_COMMIT_CRIT_PHASE2 just before calling "bg_update_phase2" */ boolean_t wcs_pidcnt_incremented; /* set to TRUE if we incremented cnl->wcs_phase2_commit_pidcnt. * used by secshr_db_clnup to decrement the shared counter. */ boolean_t incr_db_trigger_cycle; /* set to FALSE by default. set to TRUE if trigger state change (in ^#t) occurs for * any global in this database which means an increment to csa->db_trigger_cycle and * csd->db_trigger_cycle. Currently used by MUPIP TRIGGER/$ZTRIGGER(), MUPIP RECOVER * and UPDATE PROCESS */ uint4 db_trigger_cycle; /* mirror of csd->db_trigger_cycle; used to detect concurrent ^#t global changes */ uint4 db_dztrigger_cycle; /* incremented on every $ZTRIGGER() operation. Due to the presence of $ZTRIGGER() * and ZTRIGGER command the 'd' prefix for ztrigger in db_dztrigger_cycle is used * to denote the '$' in $ZTRIGGER() */ boolean_t hold_onto_crit; /* TRUE currently for dse if a CRIT -SEIZE has been done on this region. * Set to FALSE by a DSE CRIT -RELEASE done on this region. Will also be TRUE in * case of ONLINE ROLLBACK. Any code that can be invoked by both DSE and ROLLBACK * should use csa->hold_onto_crit. */ boolean_t dse_crit_seize_done; /* TRUE if DSE does a CRIT -SEIZE for this region. Set to FALSE when CRIT -RELEASE * or CRIT -REMOVE is done. Other than the -SEIZE and -RELEASE window, if any other * DSE module sets csa->hold_onto_crit to TRUE (like dse_b_dmp) but encounters a * runtime error before getting a chance to do a rel_crit, preemptive_db_clnup * should know to release crit even if hold_onto_crit is set to TRUE and so will * rely on this variable */ int4 gbuff_limit; /* desired limit on global buffers; see db_csh_getn, op_view and op_fnview */ uint4 root_search_cycle; /* local copy of cnl->root_search_cycle */ uint4 onln_rlbk_cycle; /* local copy of cnl->onln_rlbk_cycle */ uint4 db_onln_rlbkd_cycle; /* local copy of cnl->db_onln_rlbkd_cycle */ uint4 reservedDBFlags; /* Bit mask field containing the reserved DB flags (field copied from gd_region) */ boolean_t read_only_fs; /* TRUE if the region is read_only and the header was not updated due to EROFS */ boolean_t crit_probe; /* flag for indicating the process is doing a crit probe on this region */ boolean_t canceled_flush_timer; /* a flush timer was canceled even though dirty buffers might still exist */ probecrit_rec_t probecrit_rec; /* fields defined in tab_probecrit_rec.h and initialized in probecrit_rec.h */ boolean_t lock_crit_with_db; /* copy of sgmnt_data field controlling LOCK crit mechanizm - see interlock.h */ boolean_t needs_post_freeze_online_clean; /* Perform cleanup of online freeze */ boolean_t needs_post_freeze_flushsync; /* Perform post-freeze flush/sync */ block_id tp_hint; /* last tp (allocation) hint for this process in this region */ boolean_t tp_in_use; /* Indices whether or not tp structures are initialized for the given region */ boolean_t statsDB_setup_completed; /* TRUE if ^%YGS node has been added to this statsDB file. * Is a copy of reg->statsDB_setup_completed but is present in "csa" * too to handle was_open regions. */ gd_inst_info *gd_instinfo; /* global directory not gtm_repl_instance */ gd_addr *gd_ptr; /* global directory for region */ struct jnlpool_addrs_struct *jnlpool; /* NULL until put, kill, or other function requiring jnlpool */ struct mlk_shrhash_struct *mlkhash; /* Pointer to shared lock hash array. Set by GRAB_LOCK_CRIT(). */ int mlkhash_shmid; /* Shared memory id of attached lock hash array, or INVALID_SHMID * if internal. Set by GRAB_LOCK_CRIT_AND_SYNC(). */ } sgmnt_addrs; typedef struct gd_binding_struct { union { char *addr; uint4 offset; } gvkey; /* Any input key GREATER THAN OR EQUAL TO "gvkey" lies OUTSIDE this map */ union { gd_region *addr; uint4 offset; } reg; uint4 gvname_len; /* the unsubscripted global name length */ uint4 gvkey_len; /* the subscripted global name length excluding the second terminating null byte. * Is equal to "gvname_len" + 1 if there are no subscripts. */ } gd_binding; typedef struct gd_gblname_struct { unsigned char gblname[MAX_NM_LEN + 1]; uint4 act; /* alternative collation sequence # */ uint4 ver; /* version of collation library used at gld creation time */ } gd_gblname; #define INVALID_STATSDB_REG_INDEX (MAXUINT4) /* this has to be maintained in parallel with TWO(32)-1 in gdeput.m */ /* Define macros that provide a connection between statsdb initialization code in GDE & GT.M * sr_unix/gdeput.m calculates STATSDB_BLK_SIZE by * SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + max_padded_key_size + SIZEOF(gvstats_rec_t) * all rounded up to the next highest multiple of 512 (if the computed size is not already a multiple thereof). * The max_padded_key_size involves a $zcollate() M step which we can't do here, but the results should be, * in effect, a constant value (possibly differing on AIX vs Linux since the max possible PID is involved in the calculation). * Allowing for that, the value computed here should track GDE's unless something fundamental changes, in which case, * we still have an assert to catch a value mismatch */ /* As it happens, both AIX & Linux currently round up from (50 & 49) to 56 */ #if defined(_AIX) #define MAX_PADDED_STATSDB_KEY_SIZE (56) #elif defined(__linux) #define MAX_PADDED_STATSDB_KEY_SIZE (56) #else #error UNSUPPORTED PLATFORM #endif /* the BLK_SIZE computed by GDE for every statsdb region (sync with sr_unix/gdeput.m) */ #define STATSDB_BLK_SIZE ROUND_UP((SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + MAX_PADDED_STATSDB_KEY_SIZE+SIZEOF(gvstats_rec_t)),512) #define STATSDB_ALLOCATION 2050 /* the ALLOCATION computed by GDE for every statsdb region (sync with sr_unix/gdeput.m) */ #define STATSDB_MAX_KEY_SIZE 64 /* the MAX_KEY_SIZE computed by GDE for every statsdb region (sync with sr_port/gdeinit.m)*/ #define STATSDB_MAX_REC_SIZE (STATSDB_BLK_SIZE - SIZEOF(blk_hdr)) /* the MAX_REC_SIZE computed by GDE for every statsdb region */ /* The following struct is built into a separate list for each transaction because it is not thrown away if a transaction restarts. * The list keeps growing so we can lock down all the necessary regions in the correct order in case one attempt doesn't get very * far while later attempts get further. Items will be put on the list sorted in unique_id order so that they will always be * grab-crit'd in the same order thus avoiding deadlocks. * The structure and the insert_region function that maintains it are also abused/used by mupip and by view_arg_convert on behalf * of op_view - the secondary adopters us a different anchor from the tp_reg_list GBLREF and a hence different list. */ /* The structure backup_region_list defined in mupipbckup.h needs to have its first four fields * identical to the first three fields in this structure */ typedef struct tp_region_struct { struct tp_region_struct *fPtr; /* Next in list */ gd_region *reg; /* Region pointer. Note that it is not necessarily unique since multiple * regions could point to the same physical file (with all but one of them * having reg->was_open set to TRUE.and hence have the same tp_region structure. */ gd_id file_id; int4 fid_index; /* copy of csa->fid_index for this region */ } tp_region; typedef struct { unsigned short offset; unsigned short match; } srch_rec_status; typedef struct srch_blk_status_struct { cache_rec_ptr_t cr; sm_uc_ptr_t buffaddr; block_id blk_num; trans_num tn; srch_rec_status prev_rec, curr_rec; int4 cycle; int4 level; struct cw_set_element_struct *cse; struct srch_blk_status_struct *first_tp_srch_status; /* In TP, this points to an entry in the si->first_tp_hist array * that contains the srch_blk_status structure of this block the * first time it was referenced in this TP transaction. So basically * gvt->hist contains pointers to the first_tp_hist array. At * tp_clean_up time, the first_tp_hist array is cleared but all * pointers to it are not cleaned up then. That instead happens * when the gvt->clue gets used first in the next TP transaction, * at which point we are guaranteed local_tn is much higher than * gvt->read_local_tn which is an indication to complete this * deferred cleanup. * In non-TP, this field is maintained in most but not all places * (e.g. if gvcst_search uses the clue and does not go to t_qread, * this field is not maintained) so do not rely on this in non-TP. */ struct gv_namehead_struct *blk_target; } srch_blk_status; /* Defines for "cycle" member in srch_blk_status. * For histories pointing to shared-memory buffers, * "cycle" will be CYCLE_SHRD_COPY in MM and some positive number in BG. * For histories pointing to privately-built blocks, * "cycle" will be CYCLE_PVT_COPY for both BG and MM. */ #define CYCLE_PVT_COPY -1 #define CYCLE_SHRD_COPY -2 typedef struct { int4 depth; int4 filler; srch_blk_status h[MAX_BT_DEPTH + 1]; } srch_hist; #define SUPER_HIST_SIZE 2 * MAX_BT_DEPTH + 2 /* can be increased in the future to accommodate more than 2 histories */ /* Currently used only by MUPIP REORG in order to pass 3 search histories to t_end. */ typedef struct { int4 depth; /* t_end's validations don't depend on this field */ int4 filler; srch_blk_status h[SUPER_HIST_SIZE]; } super_srch_hist; #define MERGE_SUPER_HIST(SUPER_HIST, HIST1, HIST2) \ MBSTART { /* Possible enhancement: do memcpy instead of loop */ \ srch_hist *hist; \ srch_blk_status *t0, *t1; \ \ (SUPER_HIST)->depth = 1 + (HIST1)->depth + (HIST2)->depth; \ assert(SUPER_HIST_SIZE > (SUPER_HIST)->depth); \ t0 = (SUPER_HIST)->h; \ for (hist = (HIST1); (NULL != hist); hist = (hist == (HIST1)) ? (HIST2) : NULL) \ { \ for (t1 = hist->h; t1->blk_num; t1++) \ { \ *t0 = *t1; \ t0++; \ } \ } \ t0->blk_num = 0; \ } MBEND typedef struct gv_key_struct { unsigned short top; /* Offset to top of buffer allocated for the key */ unsigned short end; /* End of the current key. Offset to the second null */ unsigned short prev; /* Offset to the start of the previous subscript. * This is used for global nakeds. */ unsigned char base[]; /* Base of the key */ } gv_key; /* Same as above, but with no "base" field. For use in contexts where flexible arrays are disallowed. */ typedef struct { unsigned short top; unsigned short end; unsigned short prev; } gv_key_nobase; typedef union { gv_key_nobase key; char buf[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)]; struct { char gv_key_data[SIZEOF(gv_key_nobase)]; char base[DBKEYSIZE(MAX_KEY_SZ)]; } split; } gv_key_buf; /* The direction that the newly added record went after a block split at a given level */ enum split_dir { NEWREC_DIR_FORCED, /* direction forced due to one of the sides being too-full i.e. no choice */ NEWREC_DIR_LEFT, /* new record went into the end of the left block after the split */ NEWREC_DIR_RIGHT, /* new record went into the beginning of the right block after the split */ }; /* Any change to this structure should also have a corresponding [re]initialization in mupip_recover.c * in the code where we play the records in the forward phase i.e. go through each of the jnl_files * and within if (mur_options.update), initialize necessary fields of gv_target before proceeding with mur_forward(). */ typedef struct gv_namehead_struct { gv_key *first_rec, *last_rec; /* Boundary recs of clue's data block */ struct gv_namehead_struct *next_gvnh; /* Used to chain gv_target's together */ struct gv_namehead_struct *prev_gvnh; /* Used to chain gv_target's together */ struct gv_namehead_struct *next_tp_gvnh; /* Used to chain gv_targets participating in THIS TP transaction */ sgmnt_addrs *gd_csa; /* Pointer to Segment corresponding to this key */ srch_hist *alt_hist; /* alternate history. initialized once per gv_target */ struct collseq_struct *collseq; /* pointer to a linked list of user supplied routine addresses * for internationalization */ trans_num read_local_tn; /* local_tn of last reference for this global */ GTMTRIG_ONLY(trans_num trig_local_tn;) /* local_tn of last trigger driven for this global */ GTMTRIG_ONLY(trans_num trig_read_tn;) /* local_tn when triggers for this global (^#t records) were read from db */ gv_key *prev_key; /* Points to fully expanded previous key. Used by $zprevious. * Valid only if clue->end is non-zero. */ boolean_t noisolation; /* whether isolation is turned on or off for this global */ char filler_8byte_align0[4]; block_id root; /* Root of global variable tree */ mname_entry gvname; /* the name of the global */ srch_hist hist; /* block history array */ int4 regcnt; /* number of global directories whose hash-tables point to this gv_target. * 1 by default. > 1 if the same name in TWO DIFFERENT global directories * maps to the same physical file (i.e. two regions in different global * directories have the same physical file). */ uint4 nct; /* numerical collation type for internalization */ uint4 act; /* alternative collation type for internalization */ uint4 ver; boolean_t act_specified_in_gld; /* this gvt's global name had its "act" specified in the .gld in its * -GBLNAME section. */ boolean_t nct_must_be_zero; /* this gvt is part of a multi-region spanning global and hence * its "nct" cannot be anything but zero. */ boolean_t split_cleanup_needed; char last_split_direction[MAX_BT_DEPTH - 1]; /* maintain last split direction for each level in the GVT */ char filler_8byte_align1[2]; block_id last_split_blk_num[MAX_BT_DEPTH - 1]; # ifdef GTM_TRIGGER struct gvt_trigger_struct *gvt_trigger; /* pointer to trigger info for this global * (is non-NULL only if db_trigger_cycle is non-zero) */ uint4 db_trigger_cycle; /* copy of csd->db_trigger_cycle when triggers for this global were * last read/initialized from ^#t global (in gvtr_init) */ uint4 db_dztrigger_cycle; /* copy of csa->db_dztrigger_cycle when triggers for this global were * last read/initialized from ^#t global (in gvtr_init) */ boolean_t trig_mismatch_test_done; /* whether update process has checked once if there is a mismatch * in trigger definitions between originating and replicating instance */ GTM64_ONLY(uint4 filler_8byte_align2;) /* needed for 8-byte alignment of clue, which targ_alloc relies on */ # endif unsigned short filler_clue_end_align[1]; /* avoid padding after clue */ gv_key_nobase clue; /* Clue key, must be last in struct because of gv_key flexible array. */ } gv_namehead; /* Below structure is allocated for every global name that spans across multiple regions in each global directory */ typedef struct gvnh_spanreg_struct { int start_map_index; /* index into the global directory "maps" array corresponding to the * FIRST map entry with "gvkey" member containing subscripted * keys of the parent (unsubscripted) global name. */ int end_map_index; /* index into the global directory "maps" array corresponding to the * LAST map entry with "gvkey" member containing subscripted * keys of the parent (unsubscripted) global name. * "start_map_index" and "end_map_index" serve as two bounds of * the array within which a binary search is done to find which * map entry contains a given input key. */ int min_reg_index; /* index into the global directory "regions" array such that ALL * regions that the parent global name spans across lie AFTER * this index in the "regions" array. */ int max_reg_index; /* index into the global directory "regions" array corresponding to * the LAST region gv_target pointer for the spans of this global. * The count (max_reg_index-min_reg_index)+1 effectively determines the * size of the array allocated to store the corresponding * gv_targets for each unique region the parent global name spans. */ gv_namehead *gvt_array[1]; /* array of gv_targets corresponding to each region the global name spans. * Although the array is defined to be size 1, the actual size allocated * depends on (max_reg_index-min_reg_index)+1 and having this defined as * an array lets us access the entire allocated size with an array index. * Set to INVALID_GV_TARGET for array indices that correspond to regions * which the parent global does NOT span across. */ } gvnh_spanreg_t; /* Below structure is allocated for every global name in each global directory * (irrespective of whether it spans across multiple regions or not). */ typedef struct gvnh_reg_struct { gv_namehead *gvt; /* Pointer to gv_target for the unsubscripted global name */ gd_region *gd_reg; /* Region corresponding to the global directory map entry where * the unsubscripted global name was found. */ gvnh_spanreg_t *gvspan; /* Pointer to a structure containing details of what regions are spanned * by this global name. Set to NULL for globals that don't span regions. */ uint4 act; /* Copy of alternative collation SEQUENCE defined in GBLNAMES section of gbldir */ uint4 ver; /* Copy of collation library VERSION defined in GBLNAMES section of gbldir */ } gvnh_reg_t; #define GVNH_REG_INIT(ADDR, HASHTAB, GD_MAP, GVT, REG, GVNH_REG, TABENT) \ MBSTART { \ boolean_t added, gbl_spans_regions; \ char *gvent_name; \ gd_binding *spanmap; \ int res, gvent_len; \ \ GBLREF jnl_gbls_t jgbl; \ GBLREF gv_key *gv_currkey; \ \ GVNH_REG = (gvnh_reg_t *)malloc(SIZEOF(gvnh_reg_t)); \ GVNH_REG->gvt = GVT; \ GVNH_REG->gd_reg = REG; \ /* If GD_MAP is NULL, it implies callers like MUPIP JOURNAL -RECOVER or GT.CM GNP server \ * which don't have a .gld context to map input global names to regions, but instead know \ * which regions to play input updates. If GD_MAP is non-NULL, it implies the caller has \ * a .gld with map entries and wants to do more initialization inside this macro if the \ * input global spans multiple regions. Note that it is possible that even though GD_MAP is NULL, \ * ADDR could be non-NULL. This is necessary in case we need the gld file for GBLNAME section like \ * for MUPIP JOURNAL -RECOVER even though it operates on a per-region basis only. The GBLNAME section \ * is necessary to set correct collation properties in the directory tree if journal recover creates \ * the directory tree. \ */ \ assert((NULL != ADDR) || (NULL == GD_MAP)); \ if (NULL != GD_MAP) \ { /* check if global spans multiple regions and if so initialize "gvnh_reg->gvspan" */ \ gvnh_spanreg_init(GVNH_REG, ADDR, GD_MAP); \ gbl_spans_regions = (NULL != GVNH_REG->gvspan); \ } else \ { /* GT.CM GNP or MUPIP JOURNAL -RECOVER/ROLLBACK */ \ GVNH_REG->gvspan = NULL; \ /* If GT.CM GNP, value of ADDR will be NULL so no need to search the global directory. \ * Otherwise (i.e. if MUPIP JOURNAL RECOVER/ROLLBACK), find from the gld whether the global \ * name spans regions. This is necessary to do unconditional collation initialization (as if \ * the gld specified it) for globals that span multiple regions inside the \ * COPY_ACT_FROM_GLD_TO_GVNH_REG_AND_GVT macro. An exception is ^#t (actually all globals that \ * begin with ^# but ^#t is the only one currently). This does not map to a single region in \ * the gld map and so gv_srch_map should never be invoked for such globals. This global can \ * never span regions so treat it accordingly. \ */ \ if ((NULL == ADDR) || IS_MNAME_HASHT_GBLNAME(GVT->gvname.var_name)) \ gbl_spans_regions = FALSE; \ else \ { \ assert(jgbl.forw_phase_recovery); \ gvent_name = GVT->gvname.var_name.addr; \ gvent_len = GVT->gvname.var_name.len; \ spanmap = gv_srch_map(ADDR, gvent_name, gvent_len, SKIP_BASEDB_OPEN_FALSE); \ res = memcmp(gvent_name, &(spanmap->gvkey.addr[0]), gvent_len); \ assert((0 != res) || (gvent_len <= spanmap->gvname_len)); \ gbl_spans_regions = !((0 > res) || ((0 == res) && (gvent_len < spanmap->gvname_len))); \ } \ } \ COPY_ACT_FROM_GLD_TO_GVNH_REG_AND_GVT(ADDR, GVT, gbl_spans_regions, GVNH_REG, REG); \ /* Add to hash table after all potential error conditions have been checked. If it was the other way \ * around, we could end up in a situation where an error is issued but pointers are set up incorrectly \ * so a future global reference will no longer error out and will accept out-of-design updates. \ * The only drawback of this approach is it might have a memory leak since allocated structures will \ * no longer have a pointer but that is considered acceptable since these errors are very unlikely \ * and the alternative (to set up condition handlers etc.) is not considered worth the effort now. \ */ \ added = add_hashtab_mname((hash_table_mname *)HASHTAB, &GVT->gvname, GVNH_REG, &TABENT); \ assert(added || (IS_STATSDB_REG(REG) && (STATSDB_GBLNAME_LEN == GVT->gvname.var_name.len) \ && (0 == memcmp(GVT->gvname.var_name.addr, STATSDB_GBLNAME, STATSDB_GBLNAME_LEN)))); \ } MBEND /* Below macro is used whenever we need to print region of interest in case of a global that spans multiple regions */ #define SPANREG_REGION_LIT " (region " #define SPANREG_REGION_LITLEN STR_LIT_LEN(SPANREG_REGION_LIT) #define INVALID_GV_TARGET (gv_namehead *)(INTPTR_T)(-1) /* Below macro is used to get the "gvnh_reg->gvspan->gvt_array[]" contents taking into account some * might be set to INVALID_GV_TARGET (done only in DEBUG mode). In that case, we actually want to return NULL * as there is NO gvt defined in that slot. */ #ifdef DEBUG #define GET_REAL_GVT(gvt) ((INVALID_GV_TARGET == gvt) ? NULL : gvt) #else #define GET_REAL_GVT(gvt) gvt #endif typedef struct gvsavtarg_struct { gd_region *gv_cur_region; gv_namehead *gv_target; gvnh_reg_t *gd_targ_gvnh_reg; gd_binding *gd_targ_map; boolean_t gv_last_subsc_null; boolean_t gv_some_subsc_null; uint4 prev; uint4 end; } gvsavtarg_t; #define GVSAVTARG_ALIGN_BNDRY 8 #define GVSAVTARG_FIXED_SIZE (SIZEOF(gvsavtarg_t)) /* Following three macros define the mechanism to restore gv_target under normal and error conditions. * RESET_GV_TARGET should be used to restore gv_target from the global, reset_gv_target, only when we * are sure that this function is the first one in the call stack to have saved gv_target. * If the module that needs the restoration mechanism is not the first one to save gv_target in the call * stack, then one of the last two macros should be used. * RESET_GV_TARGET_LCL is used to restore gv_target from the local variable used to save gv_target. * RESET_GV_TARGET_LCL_AND_CLR_GBL is used at the end of the module, when there are no more gv_target * restorations needed. This resets gv_target and invalidates reset_gv_target. * * This mechanism ensures that, when there are multiple functions in a given call stack that save and * restore gv_target, only the bottom most function gets to store its value in the global, reset_gv_target. * In case of rts errors, if the error is not SUCCESS or INFO, then gv_target gets restored to reset_gv_target * (in preemptive_db_clnup()). For SUCCESS or INFO, no restoration is necessary because CONTINUE from the condition * handlers would take us through the normal path for gv_target restoration. */ #define SKIP_GVT_GVKEY_CHECK 0 #define DO_GVT_GVKEY_CHECK 1 #define DO_GVT_GVKEY_CHECK_RESTART 2 /* do GVT_GVKEY check but skip gvt/csa check since we are in a TP transaction * and about to restart, gv_target and cs_addrs will anyways get back in sync * as part of the tp_restart process. This flag should be used only in TP * as non-TP restart does not do this reset/sync. */ #define RESET_GV_TARGET(GVT_GVKEY_CHECK) \ MBSTART { \ assert(INVALID_GV_TARGET != reset_gv_target); \ gv_target = reset_gv_target; \ reset_gv_target = INVALID_GV_TARGET; \ if (GVT_GVKEY_CHECK) \ DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); \ } MBEND #define RESET_GV_TARGET_LCL(SAVE_TARG) \ MBSTART { \ gv_target = SAVE_TARG; \ } MBEND #define RESET_GV_TARGET_LCL_AND_CLR_GBL(SAVE_TARG, GVT_GVKEY_CHECK) \ MBSTART { \ GBLREF uint4 dollar_tlevel; \ \ gv_target = SAVE_TARG; \ if (!gbl_target_was_set) \ { \ assert(SAVE_TARG == reset_gv_target || INVALID_GV_TARGET == reset_gv_target); \ DEBUG_ONLY( \ if (DO_GVT_GVKEY_CHECK == (GVT_GVKEY_CHECK)) \ { \ DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); \ } else \ { \ assert((SKIP_GVT_GVKEY_CHECK == (GVT_GVKEY_CHECK)) \ || (dollar_tlevel && (DO_GVT_GVKEY_CHECK_RESTART == (GVT_GVKEY_CHECK)))); \ DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_FALSE); \ } \ ) \ reset_gv_target = INVALID_GV_TARGET; \ } \ } MBEND /* No point doing the gvtarget-gvcurrkey in-sync check or the gvtarget-csaddrs in-sync check if we are anyways going to exit. * There is no way op_gvname (which is where these design assumptions get actually used) is going to be called from now onwards. */ GBLREF int process_exiting; GBLREF trans_num local_tn; GBLREF gv_namehead *gvt_tp_list; #define RESET_FIRST_TP_SRCH_STATUS_FALSE FALSE #define RESET_FIRST_TP_SRCH_STATUS_TRUE TRUE #define GVT_CLEAR_FIRST_TP_SRCH_STATUS(GVT) \ MBSTART { \ srch_blk_status *srch_status; \ \ assert(GVT->clue.end); /* or else first_tp_srch_status will be reset as part of traversal */ \ assert(GVT->read_local_tn != local_tn); \ for (srch_status = &(GVT)->hist.h[0]; HIST_TERMINATOR != srch_status->blk_num; srch_status++) \ srch_status->first_tp_srch_status = NULL; \ } MBEND #define ADD_TO_GVT_TP_LIST(GVT, RESET_FIRST_TP_SRCH_STATUS) \ MBSTART { \ if (GVT->read_local_tn != local_tn) \ { /* Set read_local_tn to local_tn; Also add GVT to list of gvtargets referenced in this TP transaction. */ \ if (GVT->clue.end && RESET_FIRST_TP_SRCH_STATUS) \ GVT_CLEAR_FIRST_TP_SRCH_STATUS(GVT); \ GVT->read_local_tn = local_tn; \ GVT->next_tp_gvnh = gvt_tp_list; \ gvt_tp_list = GVT; \ } else \ { /* Check that GVT is already part of the list of gvtargets referenced in this TP transaction */ \ DBG_CHECK_IN_GVT_TP_LIST(GVT, TRUE); /* TRUE => we check that GVT IS present in the gvt_tp_list */ \ } \ } MBEND /* Although the below macros are used only in DBG code, they are passed as parameters so need to be defined for pro code too */ #define CHECK_CSA_FALSE FALSE #define CHECK_CSA_TRUE TRUE #ifdef DEBUG #define DBG_CHECK_IN_GVT_TP_LIST(gvt, present) \ MBSTART { \ gv_namehead *gvtarg; \ \ GBLREF gv_namehead *gvt_tp_list; \ GBLREF uint4 dollar_tlevel; \ \ for (gvtarg = gvt_tp_list; NULL != gvtarg; gvtarg = gvtarg->next_tp_gvnh) \ { \ if (gvtarg == gvt) \ break; \ } \ assert(!present || (NULL != gvtarg)); \ assert(present || (NULL == gvtarg) || (process_exiting && !dollar_tlevel)); \ } MBEND #define DBG_CHECK_GVT_IN_GVTARGETLIST(gvt) \ MBSTART { \ gv_namehead *gvtarg; \ \ GBLREF gd_region *gv_cur_region; \ GBLREF gv_namehead *gv_target_list; \ \ for (gvtarg = gv_target_list; NULL != gvtarg; gvtarg = gvtarg->next_gvnh) \ { \ if (gvtarg == gvt) \ break; \ } \ /* For dba_cm or dba_usr type of regions, gv_target_list is not maintained so \ * if gv_target is not part of gv_target_list, assert region is not BG or MM. \ * The only exception is if the region was dba_cm but later closed due to an error on \ * the server side (in which case access method gets reset back to BG. (e.g. gvcmz_error.c) \ */ \ assert((NULL != gvtarg) || (dba_cm == REG_ACC_METH(gv_cur_region)) \ || (dba_usr == REG_ACC_METH(gv_cur_region)) \ || ((FALSE == gv_cur_region->open) && (dba_bg == REG_ACC_METH(gv_cur_region)))); \ } MBEND /* If CHECK_CSADDRS input parameter is CHECK_CSA_TRUE, then check that GV_CURRKEY, GV_TARGET and CS_ADDRS are all in sync. * If CHECK_CSADDRS input parameter is CHECK_CSA_FALSE, then only check GV_CURRKEY and GV_TARGET are in sync (skip CS_ADDRS check). * The hope is that most callers of this macro use CHECK_CSA_TRUE (i.e. a stricter check). * * The DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSADDRS) macro is used at various points in the database code to check that * gv_currkey, gv_target and cs_addrs are in sync. This is because op_gvname relies on this in order to avoid a gv_bind_name * function call (if incoming key matches gv_currkey from previous call, it uses gv_target and cs_addrs right * away instead of recomputing them). The only exception is if we were interrupted in the middle of TP transaction by an * external signal which resulted in us terminating right away. In this case, we are guaranteed not to make a call to op_gvname * again (because we are exiting) so it is ok not to do this check if process_exiting is TRUE. * * Update: The above comment is no longer true. As part of GTM-2168, the fast path in op_gvname was removed but we have not * removed code that does the below macro invocation since this is dbg-only and is anyways otherwise true. If it is a hassle * ensuring the below anywhere, it should be okay to remove it then. */ #define DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSADDRS) \ dbg_check_gvtarget_gvcurrkey_in_sync(CHECK_CSADDRS) /* Do checks on the integrity of various fields in gv_target. targ_alloc initializes these and they are supposed to * stay that way. The following code is very similar to that in targ_alloc so needs to be maintained in sync. This * macro expects that gv_target->gd_csa is non-NULL (could be NULL for GT.CM GNP client) so any callers of this macro * should ensure they do not invoke it in case of NULL gd_csa. */ #define DBG_CHECK_GVTARGET_INTEGRITY(GVT) \ dbg_check_gvtarget_integrity(GVT) /* Do checks on the integrity of GVKEY */ # define DBG_CHECK_GVKEY_VALID(GVKEY) \ MBSTART { \ unsigned char ch, prevch, *ptr, *pend; \ \ assert(GVKEY->end < GVKEY->top); \ ptr = &GVKEY->base[0]; \ pend = ptr + GVKEY->end; \ assert(KEY_DELIMITER == *pend); \ assert((ptr == pend) || (KEY_DELIMITER == *(pend - 1))); \ prevch = KEY_DELIMITER; \ while (ptr < pend) \ { \ ch = *ptr++; \ assert((KEY_DELIMITER != prevch) || (KEY_DELIMITER != ch)); \ prevch = ch; \ } \ /* Do not check GVKEY->prev as it is usually not set. */ \ } MBEND #else # define DBG_CHECK_IN_GVT_TP_LIST(gvt, present) # define DBG_CHECK_GVT_IN_GVTARGETLIST(gvt) # define DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSADDRS) # define DBG_CHECK_GVTARGET_INTEGRITY(GVT) # define DBG_CHECK_GVKEY_VALID(GVKEY) #endif /* The below GBLREFs are for the following macro */ GBLREF gv_namehead *gv_target; GBLREF sgmnt_addrs *cs_addrs; #define DBG_CHECK_GVTARGET_CSADDRS_IN_SYNC assert(process_exiting || (NULL == gv_target) || (gv_target->gd_csa == cs_addrs)) /* Indicate incompleteness of (potentially subscripted) global name by adding a "*" (without closing ")") at the end */ #define GV_SET_LAST_SUBSCRIPT_INCOMPLETE(BUFF, END) \ MBSTART { \ if (NULL == (char *)(END)) \ { /* The buffer passed to format_targ_key was not enough \ * for the transformation. We don't expect this. Handle \ * it nevertheless by adding ",*" at end. \ */ \ assert(FALSE); \ END = ((unsigned char *)ARRAYTOP(BUFF)) - 1; \ assert((char *)(END) > (char *)(BUFF)); \ *(END)++ = '*'; \ } else \ { /* Overflow occurred while adding the global name OR \ * after adding the last subscript OR in the middle of \ * adding a subscript (not necessarily last). In all \ * cases, add a '*' at end to indicate incompleteness. \ */ \ if (')' == END[-1]) \ (END)--; \ /* ensure we have space to write 1 byte */ \ assert((char *)(END) + 1 <= ((char *)ARRAYTOP(BUFF))); \ *(END)++ = '*'; \ } \ } MBEND #define KEY_COMPLETE_FALSE FALSE #define KEY_COMPLETE_TRUE TRUE #define ISSUE_GVSUBOFLOW_ERROR(GVKEY, IS_KEY_COMPLETE) \ MBSTART { \ GBLREF gv_key *gv_currkey; \ unsigned char *endBuff, fmtBuff[MAX_ZWR_KEY_SZ]; \ \ /* Assert that input key to format_targ_key is double null terminated */ \ assert(KEY_DELIMITER == GVKEY->base[GVKEY->end]); \ endBuff = format_targ_key(fmtBuff, ARRAYSIZE(fmtBuff), GVKEY, TRUE); \ if (!IS_KEY_COMPLETE) \ GV_SET_LAST_SUBSCRIPT_INCOMPLETE(fmtBuff, endBuff); /* Note: might update "endBuff" */ \ if (GVKEY == gv_currkey) \ gv_currkey->end = 0; /* to show the key is not valid */ \ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_GVSUBOFLOW, 0, ERR_GVIS, 2, \ endBuff - fmtBuff, fmtBuff); \ } MBEND #define COPY_SUBS_TO_GVCURRKEY(mvarg, reg, gv_currkey, was_null, is_null) \ MBSTART { \ GBLREF mv_stent *mv_chain; \ GBLREF unsigned char *msp, *stackwarn, *stacktop; \ mval temp; \ unsigned char buff[MAX_ZWR_KEY_SZ], *end; \ int len; \ mstr opstr; \ size_t stlen; \ \ was_null |= is_null; \ if (mvarg->mvtype & MV_SUBLIT) \ { \ is_null = ((STR_SUB_PREFIX == *(unsigned char *)mvarg->str.addr) \ && (KEY_DELIMITER == *(mvarg->str.addr + 1))); \ if (gv_target->collseq || gv_target->nct) \ { \ /* collation transformation should be done at the server's end for CM regions */ \ assert(dba_cm != REG_ACC_METH(reg)); \ TREF(transform) = FALSE; \ opstr.addr = (char *)buff; \ opstr.len = MAX_ZWR_KEY_SZ; \ end = gvsub2str((uchar_ptr_t)mvarg->str.addr, &opstr, FALSE); \ TREF(transform) = TRUE; \ temp.mvtype = MV_STR; \ temp.str.addr = (char *)buff; \ temp.str.len = (mstr_len_t)(end - buff); \ mval2subsc(&temp, gv_currkey, reg->std_null_coll); \ } else \ { \ len = mvarg->str.len; \ if (gv_currkey->end + len - 1 >= gv_currkey->top) \ ISSUE_GVSUBOFLOW_ERROR(gv_currkey, KEY_COMPLETE_FALSE); \ stlen = len; \ assert(((gv_currkey->base) + (gv_currkey->end) + stlen) <= \ ((gv_currkey->base) + gv_currkey->top)); \ memcpy((gv_currkey->base + gv_currkey->end), mvarg->str.addr, stlen); \ if (is_null && 0 != reg->std_null_coll) \ gv_currkey->base[gv_currkey->end] = SUBSCRIPT_STDCOL_NULL; \ gv_currkey->prev = gv_currkey->end; \ gv_currkey->end += len - 1; \ } \ } else \ { \ MV_FORCE_DEFINED(mvarg); \ mval2subsc(mvarg, gv_currkey, reg->std_null_coll); \ is_null = (MV_IS_STRING(mvarg) && (0 == mvarg->str.len)); \ } \ } MBEND #define EXPAND_PREV_KEY_FALSE FALSE #define EXPAND_PREV_KEY_TRUE TRUE /* Need a special value to indicate the prev_key was not computed in the last gvcst_search in a clue. * Store an impossible keysize value as the key->end there. The below macro computes such a value. */ #define PREV_KEY_NOT_COMPUTED DBKEYSIZE(MAX_KEY_SZ) #define COPY_PREV_KEY_TO_GVT_CLUE(GVT, EXPAND_PREV_KEY) \ copy_prev_key_to_gvt_clue(GVT, EXPAND_PREV_KEY) /* Copy GVKEY to GVT->CLUE. Take care NOT to copy cluekey->top to GVKEY->top as they correspond * to the allocation sizes of two different memory locations and should stay untouched. */ #define COPY_CURR_AND_PREV_KEY_TO_GVTARGET_CLUE(GVT, GVKEY, EXPAND_PREV_KEY) \ copy_curr_and_prev_key_to_gvtarget_clue(GVT, GVKEY, EXPAND_PREV_KEY) /* If SRC_KEY->end == 0, make sure to copy the first byte of SRC_KEY->base */ #define MEMCPY_KEY(TARG_KEY, SRC_KEY) \ MBSTART { \ assert(DBKEYSIZE(MAX_KEY_SZ) >= (SRC_KEY)->end + 1) +0, \ memcpy((TARG_KEY), (SRC_KEY), OFFSETOF(gv_key, base[0]) + (SRC_KEY)->end + 1); \ } MBEND #define COPY_KEY(TARG_KEY, SRC_KEY) \ MBSTART { \ assert((TARG_KEY)->top >= (SRC_KEY)->end); \ /* ensure proper alignment before dereferencing SRC_KEY->end */ \ assert(0 == (((UINTPTR_T)(SRC_KEY)) % SIZEOF((SRC_KEY)->end))); \ /* WARNING: depends on the first two bytes of gv_key structure being key top field */ \ assert((2 == SIZEOF((TARG_KEY)->top)) && ((sm_uc_ptr_t)(TARG_KEY) == (sm_uc_ptr_t)(&(TARG_KEY)->top))); \ memcpy(((sm_uc_ptr_t)(TARG_KEY) + 2), ((sm_uc_ptr_t)(SRC_KEY) + 2), OFFSETOF(gv_key, base[0]) + (SRC_KEY)->end - 1); \ } MBEND /* Macro to denote special value of first_rec when it is no longer reliable */ #define GVT_CLUE_FIRST_REC_UNRELIABLE (short)0xffff /* Macro to denote special value of last_rec when it is the absolute maximum (in case of *-keys all the way down) */ #define GVT_CLUE_LAST_REC_MAXKEY (short)0xffff /* Macro to reset first_rec to a special value to indicate it is no longer reliable * (i.e. the keyrange [first_rec, clue] should not be used by gvcst_search. * Note that [clue, last_rec] is still a valid keyrange and can be used by gvcst_search. */ #define GVT_CLUE_INVALIDATE_FIRST_REC(GVT) \ MBSTART { \ assert(GVT->clue.end); \ *((short *)GVT->first_rec->base) = GVT_CLUE_FIRST_REC_UNRELIABLE; \ } MBEND #ifdef DEBUG /* Macro to check that the clue is valid. Basically check that first_rec <= clue <= last_rec. Also check that * all of them start with the same global name in case of a GVT. A clue that does not satisfy these validity * checks implies the possibility of DBKEYORD errors (e.g. C9905-001119 in VMS). */ #define DEBUG_GVT_CLUE_VALIDATE(GVT) \ MBSTART { \ mname_entry *gvent; \ unsigned short klen; \ gv_namehead *gvt; \ \ /* Verify that clue->first_rec <= clue.base <= clue->last_rec. \ * The only exception is if first_rec has been reset to an unreliable value. \ */ \ gvt = GVT; /* copy into local variable to avoid evaluating input multiple times */ \ klen = MIN(gvt->clue.end, gvt->first_rec->end); \ assert(klen); \ assert((0 <= memcmp(((gv_key *)&(gvt->clue))->base, gvt->first_rec->base, klen)) \ || (GVT_CLUE_FIRST_REC_UNRELIABLE == *((short *)gvt->first_rec->base))); \ klen = MIN(gvt->clue.end, gvt->last_rec->end); \ assert(klen); \ assert(0 <= memcmp(gvt->last_rec->base, ((gv_key *)&(gvt->clue))->base, klen)); \ if (DIR_ROOT != gvt->root) \ { /* Not a directory tree => a GVT tree, check that first_rec/last_rec have at least gvname in it */ \ gvent = &gvt->gvname; \ if (GVT_CLUE_FIRST_REC_UNRELIABLE != *((short *)gvt->first_rec->base)) \ { \ assert((0 == memcmp(gvent->var_name.addr, gvt->first_rec->base, gvent->var_name.len)) \ && (KEY_DELIMITER == gvt->first_rec->base[gvent->var_name.len])); \ } \ if (GVT_CLUE_LAST_REC_MAXKEY != *((short *)gvt->last_rec->base)) \ { \ assert((0 == memcmp(gvent->var_name.addr, gvt->last_rec->base, gvent->var_name.len)) \ && (KEY_DELIMITER == gvt->last_rec->base[gvent->var_name.len])); \ } \ } \ } MBEND #else #define DEBUG_GVT_CLUE_VALIDATE(GVT) #endif /* Bit masks for the update_trans & si->update_trans variables */ #define UPDTRNS_DB_UPDATED_MASK (1 << 0) /* 1 if this region was updated by this non-TP/TP transaction */ #define UPDTRNS_JNL_LOGICAL_MASK (1 << 1) /* 1 if logical jnl record was written in this region's * journal file by this TP transaction. Maintained only for TP. */ #define UPDTRNS_JNL_REPLICATED_MASK (1 << 2) /* 1 if there is at least one logical jnl record written in this * region's journal file by this TP transaction that needs to be * replicated across. 0 if all updates done to this region was * inside of a trigger. Maintained only for TP. */ #define UPDTRNS_TCOMMIT_STARTED_MASK (1 << 3) /* 1 if non-TP or TP transaction is beyond the point of rolling * back by "t_commit_cleanup" and can only be rolled forward by * "secshr_db_clnup". */ #define UPDTRNS_ZTRIGGER_MASK (1 << 4) /* 1 if ZTRIGGER command was done in this transaction. This allows * the transaction to be committed even if it had no updates. * Maintained only for TP. */ #define UPDTRNS_VALID_MASK (UPDTRNS_DB_UPDATED_MASK | UPDTRNS_JNL_LOGICAL_MASK \ | UPDTRNS_JNL_REPLICATED_MASK | UPDTRNS_TCOMMIT_STARTED_MASK \ | UPDTRNS_ZTRIGGER_MASK) /* The enum codes below correspond to code-paths that can increment the database curr_tn * without having a logical update. Journaling currently needs to know all such code-paths */ typedef enum { inctn_invalid_op = 0, /* 0 : */ /* the following opcodes do NOT populate the global variable "inctn_detail" */ inctn_gvcstput_extra_blk_split, /* 1 : */ inctn_mu_reorg, /* 2 : */ inctn_wcs_recover, /* 3 : */ /* the following opcodes populate "inctn_detail.blks2upgrd_struct" */ inctn_gdsfilext_gtm, /* 4 : */ inctn_gdsfilext_mu_reorg, /* 5 : */ inctn_db_format_change, /* 6 : written when cs_data->desired_db_format changes */ /* the following opcodes populate "inctn_detail.blknum_struct" */ inctn_bmp_mark_free_gtm, /* 7 : */ inctn_bmp_mark_free_mu_reorg, /* 8 : */ inctn_blkmarkfree, /* 9 : a RECYCLED block being marked free by MUPIP REORG UPGRADE/DOWNGRADE */ inctn_blkupgrd, /* 10 : written whenever a GDS block is upgraded by MUPIP REORG UPGRADE if * a) SAFEJNL is specified OR * b) NOSAFEJNL is specified and the block is not undergoing a fmt change */ inctn_blkupgrd_fmtchng, /* 11 : written whenever a GDS block is upgraded by MUPIP REORG UPGRADE -NOSAFEJNL * and if that block is undergoing a fmt change i.e. (V4 -> V5) OR (V5 -> V4). * This differentiation (inctn_blkupgrd vs inctn_blkupgrd_fmtch) is necessary * because in the latter case we will not be writing a PBLK record and hence * have no record otherwise of a block fmt change if it occurs (note that a * PBLK journal record's "ondsk_blkver" field normally helps recovery * determine if a fmt change occurred or not). */ inctn_blkdwngrd, /* 12 : similar to inctn_blkupgrd except that this is for DOWNGRADE */ inctn_blkdwngrd_fmtchng, /* 13 : similar to inctn_blkupgrd_fmtchng except that this is for DOWNGRADE */ inctn_blkreencrypt, /* 14 : written whenever a GDS block is (re)encrypted using MUPIP REORG -ENCRYPT */ inctn_jnlphase2salvage, /* 15 : used by "jnl_phase2_salvage" to salvage a dead phase2 jnl commit */ /* the following opcodes do NOT populate the global variable "inctn_detail" */ inctn_opcode_total /* 16 : MAX. All additions of inctn opcodes should be done BEFORE this line */ } inctn_opcode_t; /* macros to check curr_tn */ #define TN_HEADROOM_V4 (2 * MAXTOTALBLKS_V4) #define TN_HEADROOM_V6 (2 * MAXTOTALBLKS_V6) #define TN_HEADROOM_V7 (2 * MAXTOTALBLKS_V7) #define MAX_TN_V4 ((trans_num)(MAXUINT4 - TN_HEADROOM_V4)) #define MAX_TN_V6 (MAXUINT8 - TN_HEADROOM_V6) #define MAX_TN_V7 (MAXUINT8 - TN_HEADROOM_V7) #define MAX_TN_DFLT MAX_TN_V7 /* Max TN of current version */ #define MAX_TN_ANY MAX_TN_V6 /* Greatest MAX_TN of all versions*/ #define TN_INVALID (MAXUINT8) /* impossible db tn */ #define HEADROOM_FACTOR 4 /* the following macro checks that curr_tn < max_tn_warn <= max_tn. * if not, it adjusts max_tn_warn accordingly to ensure the above. * if not possible, it issues TNTOOLARGE error. */ #define CHECK_TN(CSA, CSD, TN) \ MBSTART { \ assert((CSA)->hdr == (CSD)); \ assert((TN) <= (CSD)->max_tn_warn); \ assert((CSD)->max_tn_warn <= (CSD)->max_tn); \ if ((TN) >= (CSD)->max_tn_warn) \ { \ trans_num trans_left; \ \ if ((CSA)->hdr->max_tn <= (TN)) \ { \ rts_error_csa(CSA_ARG(CSA) VARLSTCNT(5) ERR_TNTOOLARGE, 3, DB_LEN_STR((CSA)->region), \ &(CSA)->hdr->max_tn); \ assert(FALSE); /* should not come here */ \ } \ /* from here on messing with CSD out of crit might be slightly off but extremely unlikely & harmless */ \ assert((CSD)->max_tn > (TN)); \ trans_left = (CSD)->max_tn - (TN); \ send_msg_csa(CSA_ARG(CSA) VARLSTCNT(6) ERR_TNWARN, 4, DB_LEN_STR((CSA)->region), &trans_left, \ &(CSD)->max_tn); \ (CSD)->max_tn_warn = (TN) + 1 + ((trans_left - 1) >> 1); \ assert((TN) < (CSD)->max_tn_warn); \ assert((CSD)->max_tn_warn <= (CSD)->max_tn); \ } \ } MBEND #define INCREMENT_CURR_TN(CSD) \ MBSTART { \ assert((CSD)->trans_hist.curr_tn < (CSD)->max_tn_warn); \ assert((CSD)->max_tn_warn <= (CSD)->max_tn); \ (CSD)->trans_hist.curr_tn++; \ assert((CSD)->trans_hist.curr_tn == (CSD)->trans_hist.early_tn); \ } MBEND #define SET_TN_WARN(CSD, ret_warn_tn) \ MBSTART { \ trans_num headroom; \ \ if (GDSV7 == (CSD)->desired_db_format) \ headroom = TN_HEADROOM_V7; \ else if((GDSV5 == (CSD)->desired_db_format) || (GDSV6 == (CSD)->desired_db_format)) \ headroom = TN_HEADROOM_V6; \ headroom *= HEADROOM_FACTOR; \ (ret_warn_tn) = (CSD)->trans_hist.curr_tn; \ if ((headroom < (CSD)->max_tn) && ((ret_warn_tn) < ((CSD)->max_tn - headroom))) \ (ret_warn_tn) = (CSD)->max_tn - headroom; \ assert((CSD)->trans_hist.curr_tn <= (ret_warn_tn)); \ assert((ret_warn_tn) <= (CSD)->max_tn); \ } MBEND #define HIST_TERMINATOR 0 #define HIST_SIZE(h) ( (SIZEOF(int4) * 2) + (SIZEOF(srch_blk_status) * ((h).depth + 1)) ) /* Start of lock space in a bg file, therefore also doubles as overhead size for header, bt and wc queues F = # of wc blocks */ #define LOCK_SPACE_SIZE(X) (ROUND_UP2(((sgmnt_data_ptr_t)X)->lock_space_size, OS_PAGE_SIZE)) /* In case of an encrypted database, we maintain both encrypted and decrypted versions of the block in shared memory * in parallel arrays of global buffers hence the doubling calculation below. Although this doubles the shared memory * size requirements for encrypted databases (when compared to the same unencrypted database), it helps in other ways. * By ensuring that this encrypted global buffer array contents are identical to the encrypted on-disk block contents * of database blocks at all times, we can avoid allocating process private memory to store encrypted before-images * (to write to a journal file). Instead processes can use the encrypted global buffer directly for this purpose. * In user environments where process-private memory is very costly compared to database shared memory (e.g. where * 1000s of GT.M processes run against the same database) the above approach is expected to use less total memory. */ #define CACHE_CONTROL_SIZE(X) \ (ROUND_UP((ROUND_UP((X->bt_buckets + X->n_bts) * SIZEOF(cache_rec) + SIZEOF(cache_que_heads), OS_PAGE_SIZE) \ + ((gtm_uint64_t)X->n_bts * X->blk_size * (USES_ENCRYPTION(X->is_encrypted) ? 2 : 1))), OS_PAGE_SIZE)) OS_PAGE_SIZE_DECLARE /* structure to identify a given system wide shared section to be ours (replic section) */ typedef struct { unsigned char label[GDS_LABEL_SZ]; char pool_type; char now_running[MAX_REL_NAME]; int4 repl_pool_key_filler; /* makes sure the size of the structure is a multiple of 8 */ char instfilename[MAX_FN_LEN + 1]; /* Identify which instance file this shared pool corresponds to */ } replpool_identifier; #if defined(__osf__) && defined(__alpha) # pragma pointer_size(save) # pragma pointer_size(long) #endif typedef replpool_identifier *replpool_id_ptr_t; #if defined(__osf__) && defined(__alpha) # pragma pointer_size(restore) #endif /* Macro to increment the count of processes that are doing two phase commit. * This is invoked just BEFORE starting phase1 of the commit. */ #define INCR_WCS_PHASE2_COMMIT_PIDCNT(csa, cnl) \ MBSTART { \ assert(!csa->wcs_pidcnt_incremented); \ INCR_CNT(&cnl->wcs_phase2_commit_pidcnt, &cnl->wc_var_lock); \ csa->wcs_pidcnt_incremented = TRUE; \ } MBEND /* Macro to decrement the count of processes that are doing two phase commit. * This is invoked just AFTER finishing phase2 of the commit. */ #define DECR_WCS_PHASE2_COMMIT_PIDCNT(csa, cnl) \ MBSTART { \ assert(csa->wcs_pidcnt_incremented); \ csa->wcs_pidcnt_incremented = FALSE; \ DECR_CNT(&cnl->wcs_phase2_commit_pidcnt, &cnl->wc_var_lock); \ } MBEND /* Insert the process_id into the list of process ids actively doing a kill */ #define INSERT_KIP_PID(local_csa) \ MBSTART { \ int idx; \ uint4 pid; \ uint4 *kip_pid_arr_ptr; \ GBLREF uint4 process_id; \ \ kip_pid_arr_ptr = local_csa->nl->kip_pid_array; \ assert(local_csa->now_crit); \ for (idx = 0; idx < MAX_KIP_PID_SLOTS; idx++) \ { \ pid = kip_pid_arr_ptr[idx]; \ if ((0 == pid) || (process_id == pid)) \ { \ kip_pid_arr_ptr[idx] = process_id; \ break; \ } \ } \ } MBEND /* Remove the process_id from the list of process ids actively doing a kill */ #define REMOVE_KIP_PID(local_csa) \ MBSTART { \ int idx; \ uint4 *kip_pid_arr_ptr; \ GBLREF uint4 process_id; \ \ kip_pid_arr_ptr = local_csa->nl->kip_pid_array; \ for (idx = 0; idx < MAX_KIP_PID_SLOTS; idx++) \ { \ if (process_id == kip_pid_arr_ptr[idx]) \ { \ kip_pid_arr_ptr[idx] = 0; \ break; \ } \ } \ } MBEND /* Insert the process_id into the list of process ids with active wcs_timers * Note: Unreliable - For Diagnostic Purposes Only */ #define INSERT_WT_PID(local_csa) \ MBSTART { \ int idx; \ uint4 *wt_pid_arr_ptr; \ GBLREF uint4 process_id; \ \ wt_pid_arr_ptr = local_csa->nl->wt_pid_array; \ for (idx = 0; idx < MAX_WT_PID_SLOTS; idx++) \ { \ /* Unreliable, as there is a race for the empty slot. */ \ if (0 == wt_pid_arr_ptr[idx]) \ wt_pid_arr_ptr[idx] = process_id; \ if (process_id == wt_pid_arr_ptr[idx]) \ break; \ } \ } MBEND /* Remove the process_id from the list of process ids with active wcs_timers * Note: Unreliable - For Diagnostic Purposes Only */ #define REMOVE_WT_PID(local_csa) \ MBSTART { \ int idx; \ uint4 *wt_pid_arr_ptr; \ GBLREF uint4 process_id; \ \ wt_pid_arr_ptr = local_csa->nl->wt_pid_array; \ for (idx = 0; idx < MAX_WT_PID_SLOTS; idx++) \ { \ if (process_id == wt_pid_arr_ptr[idx]) \ { \ wt_pid_arr_ptr[idx] = 0; \ break; \ } \ } \ } MBEND #define DECR_KIP(CSD, CSA, KIP_CSA) \ MBSTART { \ sgmnt_data_ptr_t local_csd; \ sgmnt_addrs *local_csa; \ \ /* Instead of using CSA and CSD directly in DECR_CNT, assign it to \ * local variables as the caller can potentially pass the global \ * kip_csa as the second argument(which also happens to be the \ * the third argument which will be reset to NULL below) thereby \ * leading to SEG faults in the calls to DECR_CNT. Similar \ * modifications are in INCR_KIP and their CAREFUL counterparts */ \ local_csd = CSD; \ local_csa = CSA; \ assert(NULL != KIP_CSA); \ KIP_CSA = NULL; \ DECR_CNT(&local_csd->kill_in_prog, &local_csa->nl->wc_var_lock); \ REMOVE_KIP_PID(local_csa); \ } MBEND #define INCR_KIP(CSD, CSA, KIP_CSA) \ MBSTART { \ sgmnt_data_ptr_t local_csd; \ sgmnt_addrs *local_csa; \ \ local_csd = CSD; \ local_csa = CSA; \ assert(NULL == KIP_CSA); \ INCR_CNT(&local_csd->kill_in_prog, &local_csa->nl->wc_var_lock); \ INSERT_KIP_PID(local_csa); \ KIP_CSA = CSA; \ } MBEND /* Since abandoned_kills counter is only incremented in secshr_db_clnup it does not have its equivalent DECR_ABANDONED_KILLS */ #define INCR_ABANDONED_KILLS(CSD, CSA) INCR_CNT(&CSD->abandoned_kills, &CSA->nl->wc_var_lock) #define INCR_INHIBIT_KILLS(CNL) INCR_CNT(&CNL->inhibit_kills, &CNL->wc_var_lock) #define DECR_INHIBIT_KILLS(CNL) \ MBSTART { \ if (0 < CNL->inhibit_kills) \ DECR_CNT(&CNL->inhibit_kills, &CNL->wc_var_lock); \ } MBEND /* Unless the pipeline architecture of the machine precludes it, there is a chance for another process to slip in between the IF and * the decrement, but this macro would only be used in relatively unlikely circumstances. */ #define CAREFUL_DECR_CNT(CNT,LATCH) \ MBSTART { \ if (0 < CNT) \ DECR_CNT(&CNT, &LATCH); \ } MBEND /* Commands like MUPIP BACKUP, MUPIP INTEG -REG or MUPIP FREEZE wait for kills-in-prog flag to become zero. * While these process wait for ongoing block-freeing KILLs (or reorg actions that free up blocks) to complete, * new block-freeing KILLs (or reorg actions that free up blocks) are deferred using inhibit_kills counter. * New block-freeing KILLs/REORG will wait for a maximum period of 1 minute until inhibit_kills counter is 0. * In case of timeout, they will proceed after resetting the inhibit_kills to 0. The reset is done in case * the inhibit_kills was orphaned (i.e. the process that set it got killed before it got a chance to reset). */ #define WAIT_ON_INHIBIT_KILLS(CNL, MAXKILLINHIBITWAIT) \ MBSTART { \ int4 sleep_counter; \ \ GBLREF boolean_t need_kip_incr; \ GBLREF uint4 dollar_tlevel; \ \ assert(dollar_tlevel || need_kip_incr); \ for (sleep_counter = 1; (0 < CNL->inhibit_kills); ++sleep_counter) \ { \ if (MAXKILLINHIBITWAIT <= sleep_counter) \ { \ CNL->inhibit_kills = 0; \ SHM_WRITE_MEMORY_BARRIER; \ break; \ } \ wcs_sleep(sleep_counter); \ } \ } MBEND /* Wait for a region freeze to be turned off. Note that we don't hold CRIT at this point. Ideally we would have * READ memory barriers between each iterations of sleep to try and get the latest value of the "freeze" field from * the concurrently updated database shared memory. But since region-freeze is a perceivably rare event, we choose * not to do the memory barriers. The consequence of this decision is that it might take more iterations for us to * see updates to the "freeze" field than it would have if we did the memory barrier each iteration. But since we * don't hold crit at this point AND since freeze is a rare event, we don't mind the extra wait. */ #define MAXHARDCRITS 31 #define WAIT_FOR_REGION_TO_UNFREEZE(CSA) \ MBSTART { \ int lcnt1; \ \ assert(!CSA->now_crit); \ for (lcnt1 = 1; ; lcnt1++) \ { \ if (!FROZEN_HARD(CSA)) \ break; \ if (MAXHARDCRITS < lcnt1) \ wcs_backoff(lcnt1); \ } \ } MBEND #define WAIT_FOR_REGION_TO_UNCHILL(CSA, CSD) \ MBSTART { \ int lcnt1; \ boolean_t crit_stuck = FALSE; \ \ assert((CSA)->hdr == (CSD)); \ for (lcnt1 = 1; ; lcnt1++) \ { \ if (!FROZEN_CHILLED(CSA)) \ break; \ if (CHILLED_AUTORELEASE(CSA)) \ { \ DO_CHILLED_AUTORELEASE(CSA, CSD); \ break; \ } \ else if ((CSA)->now_crit && !crit_stuck) \ { \ send_msg_csa(CSA_ARG(CSA) VARLSTCNT(4) ERR_OFRZCRITSTUCK, 2, REG_LEN_STR((CSA)->region)); \ crit_stuck = TRUE; \ } \ if (MAXHARDCRITS < lcnt1) \ wcs_backoff(lcnt1); \ } \ if (crit_stuck) \ send_msg_csa(CSA_ARG(CSA) VARLSTCNT(4) ERR_OFRZCRITREL, 2, REG_LEN_STR((CSA)->region)); \ } MBEND /* Since this macro is called from "t_retry", we need to ensure encryption cycles are synced as part of * the grab_crit, hence the "grab_crit_encr_cycle_sync" usage. Other callers of this macro like "mupip_extend" * don't need that functionality but it does not hurt them so we leave it at that instead of forking this * macro into two versions (one using "grab_crit" and another using "grab_crit_encr_cycle_sync"). */ #define GRAB_UNFROZEN_CRIT(reg, csa, wstate) \ MBSTART { \ int lcnt; \ \ assert(&FILE_INFO(reg)->s_addrs == csa); \ assert(csa->now_crit); \ for (lcnt = 0; ; lcnt++) \ { \ if (!FROZEN_HARD(csa)) \ break; \ rel_crit(reg); \ WAIT_FOR_REGION_TO_UNFREEZE(csa); \ grab_crit_encr_cycle_sync(reg, wstate); \ } \ assert(!FROZEN_HARD(csa) && csa->now_crit); \ } MBEND /* remove "csa" from list of open regions (cs_addrs_list) */ #define REMOVE_CSA_FROM_CSADDRSLIST(CSA) \ MBSTART { \ GBLREF sgmnt_addrs *cs_addrs_list; \ \ sgmnt_addrs *tmpcsa, *prevcsa; \ \ assert(NULL != CSA); \ assert(NULL == CSA->nl); \ prevcsa = NULL; \ for (tmpcsa = cs_addrs_list; NULL != tmpcsa; tmpcsa = tmpcsa->next_csa) \ { \ if (CSA == tmpcsa) \ break; \ prevcsa = tmpcsa; \ } \ /* tmpcsa could not be equal to CSA in case CSA was never added to this list \ * (possible in case of errors during gvcst_init). In dbg, the only case we \ * know of this is if an external signal causes exit processing before db_init \ * completes. Assert accordingly. \ */ \ assert((tmpcsa == CSA) || process_exiting); \ if (tmpcsa == CSA) \ { \ if (NULL != prevcsa) \ prevcsa->next_csa = CSA->next_csa; \ else \ cs_addrs_list = CSA->next_csa; \ } \ } MBEND #define RESET_SHMID_CTIME(X) \ MBSTART { \ (X)->shmid = INVALID_SHMID; \ (X)->gt_shm_ctime.ctime = 0; \ } MBEND #define RESET_SEMID_CTIME(X) \ MBSTART { \ (X)->semid = INVALID_SEMID; \ (X)->gt_sem_ctime.ctime = 0; \ } MBEND #define RESET_IPC_FIELDS(X) \ MBSTART { \ RESET_SHMID_CTIME(X); \ RESET_SEMID_CTIME(X); \ } MBEND GBLREF boolean_t multi_thread_in_use; /* TRUE => threads are in use. FALSE => not in use */ /* #GTM_THREAD_SAFE : The below macro (DB_FSYNC) is thread-safe */ #define DB_FSYNC(reg, udi, csa, db_fsync_in_prog, save_errno) \ MBSTART { \ int rc; \ \ BG_TRACE_PRO_ANY(csa, n_db_fsyncs); \ if (csa->now_crit) \ BG_TRACE_PRO_ANY(csa, n_db_fsyncs_in_crit); \ /* If inside thread, do not touch global variable "db_fsync_in_prog" due to concurrency issues. \ * Besides, no need to maintain this variable inside thread since SIGALRMs are blocked and \ * this is primarily used by "wcs_clean_dbsync" (the idle epoch timer code) anyways. \ */ \ if (!multi_thread_in_use) \ db_fsync_in_prog++; \ save_errno = 0; \ GTM_DB_FSYNC(csa, udi->fd, rc); \ if (-1 == rc) \ save_errno = errno; \ if (!multi_thread_in_use) \ db_fsync_in_prog--; \ assert(0 <= db_fsync_in_prog); \ } MBEND #define STANDALONE(x) mu_rndwn_file(x, TRUE) #define DBFILOP_FAIL_MSG(status, msg) gtm_putmsg_csa(CSA_ARG(REG2CSA(gv_cur_region)) VARLSTCNT(5) msg, 2, \ DB_LEN_STR(gv_cur_region), status); #define CR_NOT_ALIGNED(cr, cr_base) (!IS_PTR_ALIGNED((cr), (cr_base), SIZEOF(cache_rec))) #define CR_NOT_IN_RANGE(cr, cr_lo, cr_hi) (!IS_PTR_IN_RANGE((cr), (cr_lo), (cr_hi))) /* Examine that cr->buffaddr is indeed what it should be. If not, this macro fixes its value by * recomputing from the cache_array. * NOTE: We rely on bt_buckets, n_bts and blk_size fields of file header being correct/not corrupt */ #define CR_BUFFER_CHECK(reg, csa, csd, cr) \ MBSTART { \ cache_rec_ptr_t cr_lo, cr_hi; \ \ cr_lo = (cache_rec_ptr_t)csa->acc_meth.bg.cache_state->cache_array + csd->bt_buckets; \ cr_hi = cr_lo + csd->n_bts; \ CR_BUFFER_CHECK1(reg, csa, csd, cr, cr_lo, cr_hi); \ } MBEND /* A more efficient macro than CR_BUFFER_CHECK when we have cr_lo and cr_hi already available */ #define CR_BUFFER_CHECK1(reg, csa, csd, cr, cr_lo, cr_hi) \ MBSTART { \ INTPTR_T bp, bp_lo, bp_top, cr_top; \ \ cr_top = GDS_ANY_ABS2REL(csa, cr_hi); \ bp_lo = ROUND_UP(cr_top, OS_PAGE_SIZE); \ bp = bp_lo + ((cr) - (cr_lo)) * csd->blk_size; \ if (bp != cr->buffaddr) \ { \ send_msg_csa(CSA_ARG(csa) VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), \ cr, &(cr->blk), RTS_ERROR_TEXT("cr->buffaddr"), \ cr->buffaddr, bp, CALLFROM); \ cr->buffaddr = bp; \ } \ DEBUG_ONLY(bp_top = bp_lo + (gtm_uint64_t)csd->n_bts * csd->blk_size;) \ assert(IS_PTR_IN_RANGE(bp, bp_lo, bp_top) && IS_PTR_ALIGNED(bp, bp_lo, csd->blk_size)); \ } MBEND #define FILE_CNTL_INIT_IF_NULL(SEG) \ MBSTART { \ file_control *lcl_fc; \ sgmnt_addrs *csa; \ \ lcl_fc = SEG->file_cntl; \ if (NULL == lcl_fc) \ { \ MALLOC_INIT(lcl_fc, SIZEOF(file_control)); \ SEG->file_cntl = lcl_fc; \ } \ if (NULL == lcl_fc->file_info) \ { \ MALLOC_INIT(lcl_fc->file_info, SIZEOF(unix_db_info)); \ SEG->file_cntl->file_info = lcl_fc->file_info; \ csa = &((unix_db_info *)(lcl_fc->file_info))->s_addrs; \ csa->gvstats_rec_p = &csa->gvstats_rec; \ } \ } MBEND #define FILE_CNTL_INIT(SEG) \ MBSTART { \ file_control *lcl_fc; \ sgmnt_addrs *csa; \ \ MALLOC_INIT(lcl_fc, SIZEOF(file_control)); \ MALLOC_INIT(lcl_fc->file_info, SIZEOF(unix_db_info)); \ SEG->file_cntl = lcl_fc; \ csa = &((unix_db_info *)(lcl_fc->file_info))->s_addrs; \ csa->gvstats_rec_p = &csa->gvstats_rec; \ } MBEND #define FILE_CNTL_FREE(SEG) \ MBSTART { \ file_control *lcl_fc; \ unix_db_info *udi; \ \ lcl_fc = SEG->file_cntl; \ if (NULL != lcl_fc) \ { \ udi = (unix_db_info *)lcl_fc->file_info; \ if (NULL != udi) \ { \ free(udi); \ lcl_fc->file_info = NULL; \ } \ free(lcl_fc); \ SEG->file_cntl = NULL; \ } \ } MBEND #define IS_DOLLAR_INCREMENT ((is_dollar_incr) && (ERR_GVPUTFAIL == t_err)) #define AVG_BLKS_PER_100_GBL 200 #define PRE_READ_TRIGGER_FACTOR 50 #define UPD_RESERVED_AREA 50 #define UPD_WRITER_TRIGGER_FACTOR 33 #define ONLY_SS_BEFORE_IMAGES(CSA) (CSA->snapshot_in_prog && !CSA->backup_in_prog && !(JNL_ENABLED(CSA) && CSA->jnl_before_image)) #define SET_SNAPSHOTS_IN_PROG(X) MBSTART { (X)->snapshot_in_prog = TRUE; START_JNL_FILE_CLOSE_TIMER_IF_NEEDED; } MBEND #define CLEAR_SNAPSHOTS_IN_PROG(X) ((X)->snapshot_in_prog = FALSE) # define SNAPSHOTS_IN_PROG(X) ((X)->snapshot_in_prog) /* Creates a new snapshot context. Called by GT.M (or utilities like update process, MUPIP LOAD which uses * GT.M runtime. As a side effect sets csa->snapshot_in_prog to TRUE if the context creation went fine. */ # define SS_INIT_IF_NEEDED(CSA, CNL) \ MBSTART { \ int ss_shmcycle; \ boolean_t status; \ snapshot_context_ptr_t lcl_ss_ctx; \ \ lcl_ss_ctx = SS_CTX_CAST(CSA->ss_ctx); \ assert(NULL != lcl_ss_ctx); \ CNL->fastinteg_in_prog ? SET_FAST_INTEG(lcl_ss_ctx) : SET_NORM_INTEG(lcl_ss_ctx); \ ss_shmcycle = CNL->ss_shmcycle; \ SET_SNAPSHOTS_IN_PROG(CSA); \ assert(lcl_ss_ctx->ss_shmcycle <= ss_shmcycle); \ if (lcl_ss_ctx->ss_shmcycle != ss_shmcycle) \ { /* Process' view of snapshot is stale. Create/Update snapshot context */ \ status = ss_create_context(lcl_ss_ctx, ss_shmcycle); \ if (!status) \ { /* snapshot context creation failed. Reset private copy of snapshot_in_prog so that we don't \ * read the before images in t_end or op_tcommit \ */ \ SS_RELEASE_IF_NEEDED(CSA, CNL); \ CLEAR_SNAPSHOTS_IN_PROG(CSA); \ } \ assert(!status || (SNAPSHOT_INIT_DONE == lcl_ss_ctx->cur_state)); \ assert(status || (SHADOW_FIL_OPEN_FAIL == lcl_ss_ctx->cur_state) \ || (SNAPSHOT_SHM_ATTACH_FAIL == lcl_ss_ctx->cur_state) \ || (SNAPSHOT_NOT_INITED == lcl_ss_ctx->cur_state)); \ } else if ((SHADOW_FIL_OPEN_FAIL == lcl_ss_ctx->cur_state) \ || (SNAPSHOT_SHM_ATTACH_FAIL == lcl_ss_ctx->cur_state)) \ { /* Previous attempt at snapshot context creation failed (say, snapshot file open failed) and the error \ * has been reported in the shared memory. However, the snapshot is not yet complete. So, set \ * snapshot_in_prog to FALSE since the ongoing snapshot is not valid (as indicated by us in the prior \ * transaction/retry inside crit) \ * Note that we will be doing this 'if' check unconditionally until MUPIP INTEG detects the error in \ * shared memory which can be avoided by making GT.M itself set CNL->snapshot_in_prog to FALSE when it \ * detects inside crit that snapshot initialization failed for this process and hence the ongoing \ * snapshot is no longer valid. This way we don't wait for MUPIP INTEG to detect and terminate the \ * snapshots \ */ \ SS_RELEASE_IF_NEEDED(CSA, CNL); \ CLEAR_SNAPSHOTS_IN_PROG(CSA); \ } \ } MBEND #ifdef DEBUG # define DBG_ENSURE_SNAPSHOT_GOOD_TO_GO(LCL_SS_CTX, CNL) \ MBSTART { \ shm_snapshot_ptr_t ss_shm_ptr; \ \ assert(SNAPSHOTS_IN_PROG(CNL)); \ assert(NULL != LCL_SS_CTX); \ ss_shm_ptr = LCL_SS_CTX->ss_shm_ptr; \ assert(NULL != ss_shm_ptr); \ assert(SNAPSHOT_INIT_DONE == LCL_SS_CTX->cur_state); \ assert(0 == LCL_SS_CTX->failure_errno); \ assert((-1 != CNL->ss_shmid) && \ (LCL_SS_CTX->attach_shmid == CNL->ss_shmid)); \ assert(NULL != LCL_SS_CTX->start_shmaddr); \ assert(0 == STRCMP(LCL_SS_CTX->shadow_file, ss_shm_ptr->ss_info.shadow_file)); \ assert(-1 != LCL_SS_CTX->shdw_fd); \ } MBEND #else # define DBG_ENSURE_SNAPSHOT_GOOD_TO_GO(LCL_SS_CTX, CNL) #endif /* Destroy an existing snapshot. Called by GT.M (or utilities like update process, MUPIP LOAD which uses * GT.M runtime. Assumes that csa->snapshot_in_prog is TRUE and as a side effect sets csa->snapshot_in_prog * to FALSE if the context is destroyed */ # define SS_RELEASE_IF_NEEDED(CSA, CNL) \ MBSTART { \ int ss_shmcycle; \ snapshot_context_ptr_t lcl_ss_ctx; \ \ if (SNAPSHOTS_IN_PROG(CSA)) \ { \ lcl_ss_ctx = SS_CTX_CAST((CSA)->ss_ctx); \ assert(NULL != lcl_ss_ctx); \ ss_shmcycle = (CNL)->ss_shmcycle; \ if (!SNAPSHOTS_IN_PROG(CNL) || (lcl_ss_ctx->ss_shmcycle != ss_shmcycle)) \ { \ ss_destroy_context(lcl_ss_ctx); \ CLEAR_SNAPSHOTS_IN_PROG(CSA); \ } \ } \ } MBEND /* No need to write before-image in case the block is FREE. In case the database had never been fully upgraded from V4 to V5 format * (after the MUPIP UPGRADE), all RECYCLED blocks can basically be considered FREE (i.e. no need to write before-images since * backward journal recovery will never be expected to take the database to a point BEFORE the mupip upgrade). * Logic to check if before image of a given block has to be read or not are slightly complicated if snapshots are present * For snapshots, we might want to read the before images of FREE blocks. Also, if the block that we are reading * is already before imaged by some other GT.M process then we do not needed to read the before image of such a block. But, such * a condition is applicable ONLY if snapshots alone are in progress as we might want the same block for BACKUP if it is in * progress. * Note: The below condition, to before image FREE blocks, is needed only if INTEG is the snapshot initiator. When we add * bitmasks or some alternate mechanism to optionalize the before image'ing of FREE blocks, this condition must be tweaked * accordingly. For now, INTEG is the only snapshot initiator. */ # define BEFORE_IMAGE_NEEDED(read_before_image, CS, csa, csd, blk_no, retval) \ MBSTART { \ retval = (read_before_image && csd->db_got_to_v5_once); \ retval = retval && (!WAS_FREE(CS->blk_prior_state) || SNAPSHOTS_IN_PROG(csa)); \ retval = retval && (!ONLY_SS_BEFORE_IMAGES(csa) || !ss_chk_shdw_bitmap(csa, SS_CTX_CAST(csa->ss_ctx), blk_no));\ } MBEND # define CHK_AND_UPDATE_SNAPSHOT_STATE_IF_NEEDED(CSA, CNL, SS_NEED_TO_RESTART) \ MBSTART { \ GBLREF uint4 process_id; \ \ uint4 lcl_failure_errno; \ ss_proc_status cur_state; \ shm_snapshot_ptr_t ss_shm_ptr; \ snapshot_context_ptr_t lcl_ss_ctx; \ boolean_t csa_snapshot_in_prog, cnl_snapshot_in_prog; \ \ assert(CSA->now_crit); \ csa_snapshot_in_prog = SNAPSHOTS_IN_PROG(CSA); \ cnl_snapshot_in_prog = SNAPSHOTS_IN_PROG(CNL); \ if (csa_snapshot_in_prog || cnl_snapshot_in_prog) \ { \ lcl_ss_ctx = SS_CTX_CAST(CSA->ss_ctx); \ ss_shm_ptr = (shm_snapshot_ptr_t)(SS_GETSTARTPTR(CSA)); \ assert(lcl_ss_ctx->ss_shmcycle <= CNL->ss_shmcycle); \ if (!cnl_snapshot_in_prog || ss_shm_ptr->failure_errno) \ { /* No on going snapshots or on going snapshot is invalid. Even if we encountered error during snapshot \ * context creation outside crit, we ignore it as the snapshot is no more active/valid. \ */ \ CLEAR_SNAPSHOTS_IN_PROG(CSA); \ } else if (lcl_ss_ctx->ss_shmcycle == CNL->ss_shmcycle) \ { /* Neither new snapshots started nor existing ones completed. However, it's possible that we might have \ * encountered error during snapshot context creation outside crit. If the values noted outside crit \ * matches with the global values, then the error is genuine. If not, then we might have done operations\ * (shm attach and file open) when things in the shared memory were in flux in which case we need to \ * restart \ */ \ lcl_failure_errno = lcl_ss_ctx->failure_errno; \ assert(!ss_shm_ptr->failure_errno); \ SS_NEED_TO_RESTART = FALSE; \ cur_state = lcl_ss_ctx->cur_state; \ switch(cur_state) \ { \ case SNAPSHOT_INIT_DONE: \ /* Most common case. Ensure the local values of snapshot context matches with the \ * values stored in shared memory */ \ assert(csa_snapshot_in_prog); \ DBG_ENSURE_SNAPSHOT_GOOD_TO_GO(lcl_ss_ctx, CNL); \ break; \ case SNAPSHOT_SHM_ATTACH_FAIL: \ assert(0 != lcl_failure_errno); \ assert(FALSE == SNAPSHOTS_IN_PROG(CSA)); \ if (lcl_ss_ctx->nl_shmid == CNL->ss_shmid) \ { /* Error encountered outside crit is genuine. Indicate MUPIP INTEG that the \ * snapshot is no more valid \ */ \ send_msg_csa(CSA_ARG(CSA) VARLSTCNT(4) ERR_SSATTACHSHM, 1, \ lcl_ss_ctx->nl_shmid, lcl_failure_errno); \ ss_shm_ptr->failure_errno = lcl_failure_errno; \ ss_shm_ptr->failed_pid = process_id; \ } else /* snapshot context creation done while things were in flux */ \ SS_NEED_TO_RESTART = TRUE; \ break; \ case SHADOW_FIL_OPEN_FAIL: \ assert(0 != lcl_failure_errno); \ assert(FALSE == SNAPSHOTS_IN_PROG(CSA)); \ if (0 == STRCMP(lcl_ss_ctx->shadow_file, ss_shm_ptr->ss_info.shadow_file)) \ { /* Error encountered outside crit is genuine. Indicate MUPIP INTEG that the \ * snapshot is no more valid \ */ \ send_msg_csa(CSA_ARG(CSA) VARLSTCNT(7) ERR_SSFILOPERR, 4, LEN_AND_LIT("open"), \ LEN_AND_STR(lcl_ss_ctx->shadow_file), lcl_failure_errno); \ ss_shm_ptr->failure_errno = lcl_failure_errno; \ ss_shm_ptr->failed_pid = process_id; \ } else /* snapshot context creation done while things were in flux */ \ SS_NEED_TO_RESTART = TRUE; \ break; \ default: \ assert(FALSE); \ } \ } else /* A new snapshot has started after we grabbed crit in t_end. Need to restart */ \ SS_NEED_TO_RESTART = TRUE; \ } \ } MBEND # define WRITE_SNAPSHOT_BLOCK(csa, cr, mm_blk_ptr, blkid, lcl_ss_ctx) \ MBSTART { \ assert(NULL != lcl_ss_ctx); \ /* write this block to the snapshot shadow file only if this was not already \ * before imaged. If error happens while writing to the snapshot file, then \ * ss_write_block will mark the appropriate error in the shared memory \ * which INTEG will later query and report accordingly. So, just continue \ * as if nothing happened. \ */ \ if (!ss_chk_shdw_bitmap(csa, lcl_ss_ctx, blkid)) \ if (!ss_write_block(csa, blkid, cr, mm_blk_ptr, lcl_ss_ctx)) \ assert(FALSE); \ } MBEND /* Determine if the state of 'backup in progress' has changed since we grabbed crit in t_end.c/tp_tend.c */ #define CHK_AND_UPDATE_BKUP_STATE_IF_NEEDED(CNL, CSA, NEW_BKUP_STARTED) \ MBSTART { \ if (CSA->backup_in_prog != (BACKUP_NOT_IN_PROGRESS != CNL->nbb)) \ { \ if (!CSA->backup_in_prog) \ NEW_BKUP_STARTED = TRUE; \ CSA->backup_in_prog = !CSA->backup_in_prog; \ } \ } MBEND #define BLK_HDR_EMPTY(bp) ((0 == (bp)->bsiz) && (0 == (bp)->tn)) typedef enum { REG_FREEZE_SUCCESS, REG_ALREADY_FROZEN, REG_HAS_KIP, REG_FLUSH_ERROR, REG_JNL_OPEN_ERROR, REG_JNL_SWITCH_ERROR } freeze_status; /* This structure holds state captured on entry into gvcst_redo_root_search which it restores on error or exit */ typedef struct redo_root_search_context_struct { unsigned char t_fail_hist[CDB_MAX_TRIES]; unsigned int t_tries; unsigned int prev_t_tries; inctn_opcode_t inctn_opcode; trans_num start_tn; uint4 update_trans; uint4 t_err; boolean_t hold_onto_crit; gv_key_buf currkey; gv_key *gv_currkey; # ifdef DEBUG unsigned char t_fail_hist_dbg[T_FAIL_HIST_DBG_SIZE]; unsigned int t_tries_dbg; # endif } redo_root_search_context; #define SET_GV_CURRKEY_FROM_GVT(GVT) \ MBSTART { \ mname_entry *gvent; \ int end; \ \ GBLREF gv_key *gv_currkey; \ GBLREF boolean_t mu_reorg_process; \ GBLREF gv_namehead *reorg_gv_target; \ \ assert((GVT != reorg_gv_target) || mu_reorg_process); \ gvent = &GVT->gvname; \ memcpy(gv_currkey->base, gvent->var_name.addr, gvent->var_name.len); \ end = gvent->var_name.len + 1; \ gv_currkey->end = end; \ gv_currkey->base[end - 1] = 0; \ gv_currkey->base[end] = 0; \ } MBEND #define SET_WANT_ROOT_SEARCH(CDB_STATUS, WANT_ROOT_SEARCH) \ MBSTART { \ GBLREF uint4 t_tries; \ GBLREF gv_namehead *gv_target; \ \ if (cdb_sc_normal == cdb_status) \ cdb_status = LAST_RESTART_CODE; \ /* Below IF check is a special case where t_retry/tp_restart detects mismatched root cycles in 2nd to 3rd retry \ * transition. In this case, the CDB_STATUS can be anything but we still need to redo the root search \ */ \ if ((CDB_STAGNATE == t_tries) && !gv_target->root) \ WANT_ROOT_SEARCH = TRUE; \ switch(CDB_STATUS) \ { \ case cdb_sc_onln_rlbk1: \ case cdb_sc_gvtrootmod: \ case cdb_sc_gvtrootmod2: \ WANT_ROOT_SEARCH = TRUE; \ break; \ case cdb_sc_onln_rlbk2: \ /* Database was taken back to a different logical state and we are an implicit TP \ * transaction. Issue DBROLLEDBACK error that the application programmer can catch and do \ * the necessary stuff. \ */ \ assert(gtm_trigger_depth == tstart_trigger_depth); \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_DBROLLEDBACK); \ default: \ break; \ } \ } MBEND #define REDO_ROOT_SEARCH_IF_NEEDED(WANT_ROOT_SEARCH, CDB_STATUS) \ MBSTART { \ DEBUG_ONLY(GBLREF tp_frame *tp_pointer;) \ GBLREF uint4 t_err; \ GBLREF uint4 update_trans; \ uint4 save_update_trans, save_t_err; \ \ CDB_STATUS = cdb_sc_normal; \ if (WANT_ROOT_SEARCH) \ { /* We are implicit transaction and one of two things has happened: \ * 1. Online rollback updated the database but did NOT take us to a different logical state in which \ * case we've already done the restart, but the root is now reset to zero. \ * 2. mu_swap_root concurrently moved root blocks. We've restarted and reset root to zero. \ * In either case, do root search to establish the new root. \ */ \ NON_GTMTRIG_ONLY(assert(FALSE)); \ assert(tp_pointer->implicit_tstart); \ assert(NULL != gv_target); \ save_t_err = t_err; \ save_update_trans = update_trans; \ GVCST_ROOT_SEARCH_DONOT_RESTART(CDB_STATUS); \ t_err = save_t_err; \ update_trans = save_update_trans; \ if (cdb_sc_normal == CDB_STATUS) \ WANT_ROOT_SEARCH = FALSE; \ } \ } MBEND #define ASSERT_BEGIN_OF_FRESH_TP_TRANS \ MBSTART { \ GBLREF sgm_info *first_sgm_info; \ GBLREF sgm_info *sgm_info_ptr; \ \ assert((NULL == first_sgm_info) || ((sgm_info_ptr == first_sgm_info) && (NULL == first_sgm_info->next_sgm_info))); \ assert((NULL == first_sgm_info) || (0 == sgm_info_ptr->num_of_blks)); \ } MBEND #define GVCST_ROOT_SEARCH \ MBSTART { /* gvcst_root_search is invoked to establish the root block of a \ * given global (pointed to by gv_target). We always expect the root \ * block of the directory tree to be 1 and so must never come here \ * with gv_target pointing to directory tree. Assert that. \ */ \ GBLREF gv_namehead *gv_target; \ \ assert((NULL != gv_target) && (DIR_ROOT != gv_target->root)); \ if (!gv_target->root) \ gvcst_root_search(FALSE); \ } MBEND /* Same as GVCST_ROOT_SEARCH, but tells gvcst_root_search NOT to restart but to return the status code back to the caller */ #define GVCST_ROOT_SEARCH_DONOT_RESTART(STATUS) \ MBSTART { \ GBLREF gv_namehead *gv_target; \ \ assert((NULL != gv_target) && (DIR_ROOT != gv_target->root)); \ STATUS = cdb_sc_normal; \ if (!gv_target->root) \ STATUS = gvcst_root_search(TRUE); \ } MBEND #define GVCST_ROOT_SEARCH_AND_PREP(est_first_pass) \ MBSTART { \ /* Before beginning a spanning node (gvcst_xxx) or spanning region (gvcst_spr_xxx) try in a gvcst \ * routine, make sure the root is established. If we've restarted issue DBROLLEDBACK appropriately. \ */ \ GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; /* for LAST_RESTART_CODE */ \ GBLREF stack_frame *frame_pointer; \ GBLREF uint4 dollar_tlevel; \ \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ assert(dollar_tlevel); \ ASSERT_BEGIN_OF_FRESH_TP_TRANS; \ if (est_first_pass && (cdb_sc_onln_rlbk2 == LAST_RESTART_CODE)) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_DBROLLEDBACK); \ tp_set_sgm(); \ GVCST_ROOT_SEARCH; \ } MBEND #define GV_BIND_NAME_ONLY(ADDR, TARG, GVNH_REG) GVNH_REG = gv_bind_name(ADDR, TARG) #define GV_BIND_NAME_AND_ROOT_SEARCH(ADDR, TARG, GVNH_REG) \ MBSTART { \ enum db_acc_method acc_meth; \ \ GBLREF gd_region *gv_cur_region; \ GBLREF gv_namehead *gv_target; \ \ GV_BIND_NAME_ONLY(ADDR, TARG, GVNH_REG); \ /* Skip GVCST_ROOT_SEARCH in case of spanning global. \ * See comment at end of gv_bind_name.c for details. \ */ \ if (NULL == GVNH_REG->gvspan) \ { \ acc_meth = REG_ACC_METH(gv_cur_region); \ if (IS_ACC_METH_BG_OR_MM(acc_meth)) \ GVCST_ROOT_SEARCH; \ } else if (!gv_target->root && gv_target->act_specified_in_gld && gv_target->act) \ { /* gv_target->root is ZERO which means we have still not done a \ * gvcst_root_search which implies "act_in_gvt" function has not yet been \ * invoked. But this global has a non-zero act specified in gld. Invoke \ * "act_in_gvt" now as this might be needed for transforming string subscripts \ * as part of op_gvname etc. \ */ \ act_in_gvt(gv_target); /* note: this could issue COLLTYPVERSION error */ \ } \ } MBEND #define GET_REG_INDEX(ADDR, REG_START, REG, REG_INDEX) \ MBSTART { \ assert((REG >= REG_START) && (REG < &ADDR->regions[ADDR->n_regions])); \ REG_INDEX = REG - REG_START; \ } MBEND #define ACT_NOT_SPECIFIED (MAXUINT4) #define DO_NCT_CHECK_FOR_SPANGBLS(GVT, GVNH_REG, REG) \ MBSTART { \ if ((NULL != GVNH_REG->gvspan) && !GVT->nct_must_be_zero) \ { \ if (GVT->nct) \ rts_error_csa(CSA_ARG(GVT->gd_csa) VARLSTCNT(6) ERR_NCTCOLLSPGBL, 4, DB_LEN_STR(REG), \ GVT->gvname.var_name.len, GVT->gvname.var_name.addr); \ GVT->nct_must_be_zero = TRUE; \ } \ } MBEND /* Copy "act" from the .gld file (GBLNAME section) to the GVNH_REG structure and in turn the GVT structure */ #define COPY_ACT_FROM_GLD_TO_GVNH_REG_AND_GVT(ADDR, GVT, GBL_SPANS_REG, GVNH_REG, REG) \ MBSTART { \ gd_gblname *gname; \ \ if ((NULL != ADDR) && (((gd_addr *)ADDR)->n_gblnames)) \ { /* have some global names with collation characteristics. check if current global \ * name is part of that list. If so initialize its collation properties. \ */ \ gname = gv_srch_gblname(ADDR, GVT->gvname.var_name.addr, GVT->gvname.var_name.len); \ } else \ gname = NULL; \ if (NULL != gname) \ { /* Transfer global's collation characteristics into gvnh_reg. \ * But before that check for error scenarios. \ */ \ GVNH_REG->act = gname->act; \ GVNH_REG->ver = gname->ver; \ } else if (GBL_SPANS_REG) \ { /* This global spans multiple regions. And the user did not specify a collation for \ * this global in the GBLNAME section of the gld. In this case force collation to 0 \ * for this global. Not doing so could cause any non-zero default collation properties \ * in the spanned global db file header to result in different parts of this global \ * exist with different collation representations in different .dat files creating \ * an out-of-design situation since the collation property of a spanning global is used \ * in op_gvname/op_gvextnam/op_gvnaked to come with the subscript representation even \ * before determining which region a given subscripted key maps to. \ */ \ GVNH_REG->act = 0; \ GVNH_REG->ver = 0; \ } else \ GVNH_REG->act = ACT_NOT_SPECIFIED; \ COPY_ACT_FROM_GVNH_REG_TO_GVT(GVNH_REG, GVT, REG); \ } MBEND /* Copy "act" from the GVNH_REG structure to the GVT structure. * Currently the GLD (and in turn GVNH_REG) don't have a way to set "nct" for a gblname. * Therefore only "act" gets copied over currently. This macro needs to change if/when * "nct" support for gblname gets added to the gld. */ #define COPY_ACT_FROM_GVNH_REG_TO_GVT(GVNH_REG, GVT, REG) \ MBSTART { \ uint4 gldact; \ \ DO_NCT_CHECK_FOR_SPANGBLS(GVT, GVNH_REG, REG); \ gldact = GVNH_REG->act; \ /* Exclude DSE from ERR_ACTCOLLMISMTCH errors (see gvcst_root_search.c comment for details) */ \ if (((ACT_NOT_SPECIFIED != gldact) && (gldact != GVT->act) && !IS_DSE_IMAGE) \ && ((GVT->root) || GVT->act_specified_in_gld)) \ { /* GVT->root case : Global already exists and GVT/GLD act do not match. \ * GVT->act_specified_in_gld case : \ * Global directory defines one alternate collation sequence for global name \ * but gv_target already has a different (and non-zero) alternate collation \ * sequence defined (from another global directory's GBLNAME characteristics \ * or from the "Default Collation" field of the database file header. \ * In either case, error out. \ */ \ rts_error_csa(CSA_ARG(cs_addrs) VARLSTCNT(8) ERR_ACTCOLLMISMTCH, 6, \ GVT->gvname.var_name.len, GVT->gvname.var_name.addr, \ gldact, DB_LEN_STR(REG), GVT->act); \ } \ if (!GVT->act_specified_in_gld && (ACT_NOT_SPECIFIED != gldact)) \ { \ GVT->act_specified_in_gld = TRUE; \ GVT->act = gldact; \ GVT->ver = GVNH_REG->ver; \ } \ GVT->nct_must_be_zero = (NULL != GVNH_REG->gvspan); \ } MBEND /* This macro finishes the task of GV_BIND_NAME_AND_ROOT_SEARCH (in terms of setting gv_cur_region) * in case the global spans multiple regions. */ #define GV_BIND_SUBSNAME_IF_GVSPAN(GVNH_REG, GD_HEADER, GVKEY, REG) \ MBSTART { \ GBLREF gd_region *gv_cur_region; \ \ assert(NULL != GVNH_REG); \ if (NULL != GVNH_REG->gvspan) \ { \ GV_BIND_SUBSNAME(GVNH_REG, GD_HEADER, GVKEY, REG); \ } else \ TREF(gd_targ_gvnh_reg) = NULL; \ } MBEND #define GV_BIND_SUBSNAME_FROM_GVNH_REG_IF_GVSPAN(GVNH_REG, GD_HEADER, GVKEY) \ MBSTART { \ gvnh_reg_t *gvnhReg; /* use unique name to avoid name collisions with macro caller */ \ DEBUG_ONLY( \ ht_ent_mname *tabent; \ GBLREF gv_namehead *gv_target; \ gvnh_reg_t *tmp_gvnhReg; \ ) \ \ gvnhReg = GVNH_REG; /* set by op_gvname in previous call */ \ DEBUG_ONLY( \ tabent = lookup_hashtab_mname((hash_table_mname *)((GD_HEADER)->tab_ptr), &gv_target->gvname); \ assert(NULL != tabent); \ tmp_gvnhReg = (gvnh_reg_t *)tabent->value; \ assert(NULL != tmp_gvnhReg); \ if (NULL != tmp_gvnhReg->gvspan) \ assert(tmp_gvnhReg == gvnhReg); \ else \ assert(NULL == gvnhReg); \ ) \ /* A non-NULL value of gvnh_reg indicates a spanning global as confirmed by the assert below */ \ assert((NULL == gvnhReg) || (TREF(spangbl_seen) && (NULL != gvnhReg->gvspan))); \ if (NULL != gvnhReg) \ gv_bind_subsname(GD_HEADER, GVKEY, GVNH_REG); \ } MBEND /* This macro is similar to GV_BIND_SUBSNAME_IF_GVSPAN except we know for sure this is a spanning global */ #define GV_BIND_SUBSNAME(GVNH_REG, GD_HEADER, GVKEY, REG) \ MBSTART { /* This is a global that spans multiple regions. Re-bind the subscripted reference \ * just in case this maps to a different region than the unsubscripted global name. \ */ \ TREF(gd_targ_gvnh_reg) = GVNH_REG; \ gv_bind_subsname(GD_HEADER, GVKEY, GVNH_REG); \ /* gv_target/gv_cur_region/cs_addrs/cs_data are now initialized even for non-NULL gvnh_reg->gvspan */ \ /* In addition TREF(gd_targ_map) is set as well */ \ REG = gv_cur_region; /* adjust "REG" in case gv_cur_region was modified in the above call */ \ } MBEND /* sets gv_target to correspond to REG region of spanning global. also sets gv_target->root if not already non-zero. * also sets global variables gv_cur_region/cs_addrs/cs_data to correspond to input region. */ #define GV_BIND_SUBSREG(ADDR, REG, GVNH_REG) \ MBSTART { \ gv_namehead *gvt; \ gvnh_spanreg_t *gvspan; \ int min_reg_index, reg_index; \ DEBUG_ONLY(enum db_acc_method acc_meth;) \ \ GBLREF gv_namehead *gv_target; \ \ if (!REG->open) \ gv_init_reg(REG, ADDR); \ gvspan = GVNH_REG->gvspan; \ assert(NULL != gvspan); \ min_reg_index = gvspan->min_reg_index; \ GET_REG_INDEX(ADDR, &ADDR->regions[0], REG, reg_index); /* sets "reg_index" */ \ assert(reg_index >= min_reg_index); \ assert(reg_index <= gvspan->max_reg_index); \ gvt = gvspan->gvt_array[reg_index - min_reg_index]; \ /* Assert that this region is indeed mapped to by the spanning global */ \ assert(INVALID_GV_TARGET != gvt); \ if (NULL == gvt) \ { \ gvt = targ_alloc(REG->max_key_size, &GVNH_REG->gvt->gvname, REG); \ COPY_ACT_FROM_GVNH_REG_TO_GVT(GVNH_REG, gvt, REG); \ /* See comment in GVNH_REG_INIT macro for why the below assignment is \ * placed AFTER all error conditions (in above macro) have passed. \ */ \ gvspan->gvt_array[reg_index - min_reg_index] = gvt; \ } \ gv_target = gvt; \ /* Even though gv_cur_region might already be equal to "REG", need to invoke \ * "change_reg" in order to do the "tp_set_sgm" in case of TP. \ */ \ gv_cur_region = REG; \ change_reg(); \ DEBUG_ONLY(acc_meth = REG_ACC_METH(gv_cur_region);) \ assert(IS_ACC_METH_BG_OR_MM(acc_meth)); \ GVCST_ROOT_SEARCH; \ } MBEND /* When invoking grab_lock or grab_gtmsource_srv_latch, use one of the following parameters. * GRAB_LOCK_ONLY : use when code expects an online rollback, but handles it separately (like updproc.c) * GRAB_GTMSOURCE_SRV_LATCH_ONLY : Same as above (just that this is used for grab_gtmsource_srv_latch instead of grab_lock) * ASSERT_NO_ONLINE_ROLLBACK : use when holding some other lock (like crit) that prevents online rollback * HANDLE_CONCUR_ONLINE_ROLLBACK : use when not blocking online rollback, but handling with this macro (only source server) */ #define GRAB_LOCK_ONLY 0x01 #define GRAB_GTMSOURCE_SRV_LATCH_ONLY 0x01 #define ASSERT_NO_ONLINE_ROLLBACK 0x02 #define HANDLE_CONCUR_ONLINE_ROLLBACK 0x03 /* caller should have THREADGBL_ACCESS and gbl access to t_tries and t_fail_hist */ # define LAST_RESTART_CODE ( (0 < t_tries) ? t_fail_hist[TREF(prev_t_tries)] : (enum cdb_sc)cdb_sc_normal ) # define SYNC_ONLN_RLBK_CYCLES \ MBSTART { \ GBLREF sgmnt_addrs *cs_addrs_list; \ GBLREF jnlpool_addrs_ptr_t jnlpool_head; \ GBLREF boolean_t mu_reorg_process; \ \ sgmnt_addrs *lcl_csa; \ jnlpool_addrs_ptr_t lcl_jnlpool; \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ if (!TREF(in_gvcst_bmp_mark_free) || mu_reorg_process) \ { \ for (lcl_csa = cs_addrs_list; NULL != lcl_csa; lcl_csa = lcl_csa->next_csa) \ { \ lcl_csa->onln_rlbk_cycle = lcl_csa->nl->onln_rlbk_cycle; \ lcl_csa->db_onln_rlbkd_cycle = lcl_csa->nl->db_onln_rlbkd_cycle; \ /* Online rollback increments csa->hdr->db_trigger_cycle forcing a mismatch to reload triggers */ \ lcl_csa->db_trigger_cycle = lcl_csa->hdr->db_trigger_cycle; \ } \ for (lcl_jnlpool = jnlpool_head; NULL != lcl_jnlpool; lcl_jnlpool = lcl_jnlpool->next) \ { \ if (NULL != lcl_jnlpool->jnlpool_ctl) \ { \ lcl_csa = &FILE_INFO(lcl_jnlpool->jnlpool_dummy_reg)->s_addrs; \ lcl_csa->onln_rlbk_cycle = lcl_jnlpool->jnlpool_ctl->onln_rlbk_cycle; \ } \ } \ } \ } MBEND # define SYNC_ROOT_CYCLES(CSA) \ MBSTART { /* NULL CSA acts as a flag to do all regions */ \ GBLREF sgmnt_addrs *cs_addrs_list; \ GBLREF boolean_t mu_reorg_process; \ \ sgmnt_addrs *lcl_csa; \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ for (lcl_csa = cs_addrs_list; NULL != lcl_csa; lcl_csa = lcl_csa->next_csa) \ if (NULL == (CSA) || (CSA) == lcl_csa) \ lcl_csa->root_search_cycle = lcl_csa->nl->root_search_cycle; \ } MBEND # define MISMATCH_ROOT_CYCLES(CSA, CNL) ((CSA)->root_search_cycle != (CNL)->root_search_cycle) # define MISMATCH_ONLN_RLBK_CYCLES(CSA, CNL) ((CSA)->onln_rlbk_cycle != (CNL)->onln_rlbk_cycle) # define ONLN_RLBK_STATUS(CSA, CNL) \ (((CSA)->db_onln_rlbkd_cycle != (CNL)->db_onln_rlbkd_cycle) ? cdb_sc_onln_rlbk2 : cdb_sc_onln_rlbk1) # define ABORT_TRANS_IF_GBL_EXIST_NOMORE(LCL_T_TRIES, TN_ABORTED) \ MBSTART { \ DEBUG_ONLY(GBLREF unsigned int t_tries;) \ DEBUG_ONLY(GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES];) \ GBLREF gd_region *gv_cur_region; \ GBLREF sgmnt_addrs *cs_addrs; \ GBLREF gv_namehead *gv_target; \ \ DEBUG_ONLY(enum cdb_sc failure;) \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ assert(0 < t_tries); \ assert((CDB_STAGNATE == t_tries) || (LCL_T_TRIES == t_tries - 1)); \ DEBUG_ONLY(failure = LAST_RESTART_CODE); \ assert(NULL != gv_target); \ TN_ABORTED = FALSE; \ if (!gv_target->root) \ { /* online rollback took us back to a prior logical state where the global that existed when we came into \ * mu_reorg or mu_extr_getblk, no longer exists. Consider this as the end of the tree and return to the \ * caller with the appropriate code. The caller knows to continue with the next global \ */ \ assert(cdb_sc_onln_rlbk2 == failure || TREF(rlbk_during_redo_root)); \ /* abort the current transaction */ \ t_abort(gv_cur_region, cs_addrs); \ TN_ABORTED = TRUE; \ } \ } MBEND #define SET_GV_ALTKEY_TO_GBLNAME_FROM_GV_CURRKEY \ MBSTART { \ uchar_ptr_t dst_ptr, src_ptr; \ \ GBLREF gv_key *gv_currkey, *gv_altkey; \ GBLREF int4 gv_keysize; \ \ assert(gv_altkey->top == gv_currkey->top); \ assert(gv_altkey->top == gv_keysize); \ assert(gv_currkey->end < gv_currkey->top); \ dst_ptr = gv_altkey->base; \ src_ptr = gv_currkey->base; \ for ( ; *src_ptr; ) \ *dst_ptr++ = *src_ptr++; \ *dst_ptr++ = 0; \ *dst_ptr = 0; \ gv_altkey->end = dst_ptr - gv_altkey->base; \ assert(gv_altkey->end < gv_altkey->top); \ } MBEND typedef struct span_subs_struct { unsigned char b_ctrl; unsigned char b_first; unsigned char b_sec; } span_subs; #define ASCII_0 '0' #define SPAN_BLKID_BASE 255 #define DECIMAL_BASE 10 /* SPAN_SUBS_LEN macro define the length of the special subscript of spanning node in terms of number of characters. * The format of special spanning node subscript is '#-X-X', where X ranges from 1 to 255. Note that X never takes the * value of '0' because '0' is used as subscript deliminiter. */ #define SPAN_SUBS_LEN 3 #define SPAN_PREFIX "#SPAN" #define SPAN_PREFIX_LEN (SIZEOF(SPAN_PREFIX) - 1) #define ASGN_SPAN_PREFIX(B) {*(B) = '#'; *(B + 1) = 'S'; *(B + 2) = 'P'; *(B + 3) = 'A'; *(B + 4) = 'N';} #define SPAN_GVSUBS2INT(A) (((A)->b_first - 1) * SPAN_BLKID_BASE + (A)->b_sec - 1) #define SPAN_INT2GVSUBS(A, B) {(A)->b_ctrl = 0x02; (A)->b_first = B / SPAN_BLKID_BASE + 1 ; (A)->b_sec = B % SPAN_BLKID_BASE + 1;} #define SPAN_DECLSUBS(A) (span_subs A) #define SPAN_INITSUBS(A, B) SPAN_INT2GVSUBS(A, B) #define SPAN_INCRSUBS(A) SPAN_INT2GVSUBS(A, SPAN_GVSUBS2INT(A) + 1) #define SPAN_INCRBYSUBS(A, B) SPAN_INT2GVSUBS(A, SPAN_GVSUBS2INT(A) + B) #define SPAN_DECRSUBS(A, B) SPAN_INT2GVSUBS(A, SPAN_GVSUBS2INT(A) - 1) #define SPAN_DECRBYSUBS(A, B) SPAN_INT2GVSUBS(A, SPAN_GVSUBS2INT(A) - B) /* The following macro assumes that the length of the special subscript for spanning node is '3'. The assert in the macro verfies * that this assumption is true. In future, if the length of the special subscript for spanning node changes, the following macro * needs to be fixed accordingly. */ #define SPAN_SUBSCOPY_SRC2DST(DST, SRC) \ MBSTART { \ assert(SPAN_SUBS_LEN == 3); \ *((DST) + 0) = *((SRC) + 0); \ *((DST) + 1) = *((SRC) + 1); \ *((DST) + 2) = *((SRC) + 2); \ } MBEND /*#include "mv_stent.h"*/ typedef struct { boolean_t span_status; boolean_t enable_jnl_format; boolean_t enable_trigger_read_and_fire; boolean_t ztval_gvcst_put_redo; mval *val_forjnl; int4 blk_reserved_bytes; # ifdef GTM_TRIGGER unsigned char *save_msp; unsigned char *save_mv_chain; /* actually mv_stent ptr */ mval *ztold_mval; mval *ztval_mval; # endif } span_parms; # define DEFINE_NSB_CONDITION_HANDLER(gvcst_xxx_ch) \ CONDITION_HANDLER(gvcst_xxx_ch) \ { \ int rc; \ \ START_CH(TRUE); \ if ((int)ERR_TPRETRY == SIGNAL) \ { \ rc = tp_restart(1, !TP_RESTART_HANDLES_ERRORS); \ /*DEBUG_ONLY(printf("gvcst_xxx_ch: Unwinding due to TP Restart\n");)*/ \ UNWIND(NULL, NULL); \ } \ NEXTCH; \ } /* Check if the value of a primary node is a dummy value: $c(0). If so, it might be a spanning node */ # define IS_SN_DUMMY(len, addr) ((1 == (len)) && ('\0' == *(unsigned char *)(addr))) # define RTS_ERROR_IF_SN_DISALLOWED \ MBSTART { \ GBLREF boolean_t span_nodes_disallowed; \ \ if (span_nodes_disallowed) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_UNIMPLOP, 0, ERR_TEXT, 2, \ LEN_AND_LIT("GT.CM Server does not support spanning nodes")); \ } MBEND # define IF_SN_DISALLOWED_AND_NO_SPAN_IN_DB(STATEMENT) \ MBSTART { /* We've encountered a spanning node dummy value. Check if spanning nodes are disallowed (e.g., GT.CM). \ * If csd->span_node_absent is TRUE we know the value really is $c(0) and we return that. Otherwise, \ * this is potentially a spanning node. We cannot do an op_tstart to check so we issue an error instead. \ */ \ GBLREF sgmnt_data_ptr_t cs_data; \ GBLREF boolean_t span_nodes_disallowed; \ \ if (span_nodes_disallowed) \ { \ if (!cs_data->span_node_absent) \ { \ RTS_ERROR_IF_SN_DISALLOWED; \ } else \ { \ STATEMENT; \ } \ } \ } MBEND #define MAX_NSBCTRL_SZ 30 /* Upper bound on size of ctrl value. 2*10 digs + 1 comma + 1 null + overhead */ /* Control node value is 6 bytes, 2 for unsigned short numsubs, 4 for uint4 gblsize. Each is little-endian. */ #ifdef BIGENDIAN # define PUT_NSBCTRL(BYTES, NUMSUBS, GBLSIZE) \ MBSTART { \ unsigned short swap_numsubs; \ uint4 swap_gblsize; \ \ swap_numsubs = GTM_BYTESWAP_16(NUMSUBS); \ swap_gblsize = GTM_BYTESWAP_32(GBLSIZE); \ PUT_USHORT((unsigned char *)BYTES, swap_numsubs); \ PUT_ULONG((unsigned char *)BYTES + 2, swap_gblsize); \ } MBEND # define GET_NSBCTRL(BYTES, NUMSUBS, GBLSIZE) \ MBSTART { \ unsigned short swap_numsubs; \ uint4 swap_gblsize; \ \ GET_USHORT(swap_numsubs, (unsigned char *)BYTES); \ GET_ULONG(swap_gblsize, (unsigned char *)BYTES + 2); \ NUMSUBS = GTM_BYTESWAP_16(swap_numsubs); \ GBLSIZE = GTM_BYTESWAP_32(swap_gblsize); \ } MBEND #else # define PUT_NSBCTRL(BYTES, NUMSUBS, GBLSIZE) \ MBSTART { \ PUT_USHORT((unsigned char *)BYTES, NUMSUBS); \ PUT_ULONG((unsigned char *)BYTES + 2, GBLSIZE); \ } MBEND # define GET_NSBCTRL(BYTES, NUMSUBS, GBLSIZE) \ MBSTART { \ GET_USHORT(NUMSUBS, (unsigned char *)BYTES); \ GET_ULONG(GBLSIZE, (unsigned char *)BYTES + 2); \ } MBEND #endif #define CHECK_HIDDEN_SUBSCRIPT_AND_RETURN(found, gv_altkey, is_hidden) \ MBSTART { \ if (found) \ { \ CHECK_HIDDEN_SUBSCRIPT(gv_altkey, is_hidden); \ if (!is_hidden) \ return found; \ } else \ return found; \ } MBEND #define CHECK_HIDDEN_SUBSCRIPT(KEY, IS_HIDDEN) \ MBSTART { \ sm_uc_ptr_t keyloc; \ \ keyloc = (KEY)->base + (KEY)->end - 5; \ if ((KEY)->end >= 5 && 0 == *keyloc && 2 == *(keyloc+1)) \ IS_HIDDEN = TRUE; \ else \ IS_HIDDEN = FALSE; \ } MBEND #define SAVE_GV_CURRKEY(SAVE_KEY_BUF) \ MBSTART { \ assert(NULL != gv_currkey); \ assert((SIZEOF(gv_key) + gv_currkey->end + 1) <= SIZEOF(SAVE_KEY_BUF)); \ memcpy(SAVE_KEY_BUF.buf, gv_currkey, SIZEOF(gv_key) + gv_currkey->end + 1); \ } MBEND #define RESTORE_GV_CURRKEY(SAVE_KEY_BUF) \ MBSTART { \ assert(gv_currkey->top == SAVE_KEY_BUF.key.top); \ memcpy(gv_currkey, SAVE_KEY_BUF.buf, SIZEOF(gv_key) + SAVE_KEY_BUF.key.end + 1); \ } MBEND #define SAVE_GV_ALTKEY(SAVE_KEY_BUF) \ MBSTART { \ assert(NULL != gv_altkey); \ assert((SIZEOF(gv_key) + gv_altkey->end + 1) <= SIZEOF(SAVE_KEY_BUF)); \ memcpy(SAVE_KEY_BUF.buf, gv_altkey, SIZEOF(gv_key) + gv_altkey->end + 1); \ } MBEND #define RESTORE_GV_ALTKEY(SAVE_KEY_BUF) \ MBSTART { \ assert(gv_altkey->top == SAVE_KEY_BUF.key.top); \ memcpy(gv_altkey, SAVE_KEY_BUF.buf, SIZEOF(gv_key) + SAVE_KEY_BUF.key.end + 1); \ } MBEND #define SAVE_GV_CURRKEY_LAST_SUBSCRIPT(SAVE_KEY_BUF, PREV, OLDEND) \ MBSTART { \ PREV = gv_currkey->prev; \ OLDEND = gv_currkey->end; \ assert('\0' == gv_currkey->base[oldend]); \ if (PREV <= OLDEND) \ memcpy(SAVE_KEY_BUF.buf, &gv_currkey->base[PREV], OLDEND - PREV + 1); \ } MBEND #define RESTORE_GV_CURRKEY_LAST_SUBSCRIPT(SAVE_KEY_BUF, PREV, OLDEND) \ MBSTART { \ gv_currkey->prev = PREV; \ gv_currkey->end = OLDEND; \ if (PREV <= OLDEND) \ memcpy(&gv_currkey->base[PREV], SAVE_KEY_BUF.buf, OLDEND - PREV + 1); \ assert('\0' == gv_currkey->base[OLDEND]); \ } MBEND #define CAN_APPEND_HIDDEN_SUBS(KEY) (((KEY)->end + 5 <= MAX_KEY_SZ) && ((KEY)->end + 5 <= (KEY)->top)) #define APPEND_HIDDEN_SUB(KEY) \ MBSTART { \ assert(CAN_APPEND_HIDDEN_SUBS(KEY)); \ (KEY)->end += 4; \ REPLACE_HIDDEN_SUB_TO_LOWEST(KEY, KEY); \ } MBEND #define REPLACE_HIDDEN_SUB_TO_LOWEST(KEY1, KEY2) \ MBSTART { \ int end; \ \ end = (KEY1)->end; \ (KEY2)->base[end - 4] = 2; \ (KEY2)->base[end - 3] = 1; \ (KEY2)->base[end - 2] = 1; \ (KEY2)->base[end - 1] = 0; \ (KEY2)->base[end - 0] = 0; \ assert(end < (KEY2)->top); \ (KEY2)->end = end; \ } MBEND #define REPLACE_HIDDEN_SUB_TO_HIGHEST(KEY1, KEY2) \ MBSTART { \ int end; \ end = (KEY1)->end; \ (KEY2)->base[end - 4] = 2; \ (KEY2)->base[end - 3] = 0xFF; \ (KEY2)->base[end - 2] = 0xFF; \ (KEY2)->base[end - 1] = 1; \ (KEY2)->base[end + 0] = 0; \ (KEY2)->base[end + 1] = 0; \ assert((end + 1) < (KEY2)->top); \ (KEY2)->end = end + 1; \ } MBEND #define NEXT_HIDDEN_SUB(KEY, I) \ MBSTART { \ int end; \ \ end = gv_currkey->end - 4; \ (KEY)->base[end++] = 2; \ (KEY)->base[end++] = 1 + ((I + 1) / 0xFF); \ (KEY)->base[end++] = 1 + ((I + 1) % 0xFF); \ (KEY)->base[end++] = 0; \ (KEY)->base[end] = 0; \ } MBEND #define RESTORE_CURRKEY(KEY, OLDEND) \ MBSTART { \ (KEY)->end = OLDEND; \ (KEY)->base[OLDEND - 1] = 0; \ (KEY)->base[OLDEND] = 0; \ } MBEND #define COMPUTE_CHUNK_SIZE(KEY, BLKSZ, RESERVED) \ (BLKSZ - RESERVED - ((KEY)->end + 1) - SIZEOF(blk_hdr) - SIZEOF(rec_hdr)) /* In MM mode, AIX increases native DB file size to adjust to the next nearest multiple of OS_PAGE_SIZE if the file is mapped * using shmat() and last portion of the file is accessed. To take into account this adjustment, following macro adjust the * value of DB file size determined using block count, constant DB HEADER SIZE and constant MASTER MAP size and EOF block. * Since we can change the DB access method using MUPIP SET and we can move the database from AIX to non-AIX platfrom, * following macro is not just AIX specific. */ #define ALIGN_DBFILE_SIZE_IF_NEEDED(SZ, NATIVE_SZ) \ SZ = (SZ == NATIVE_SZ) ? SZ : ROUND_UP(SZ, (OS_PAGE_SIZE / DISK_BLOCK_SIZE)); #define SYNCIO_MORPH_SUCCESS -1 /* a special negative value to indicate an asyncio was morphed into a syncio * because the asyncio returned EAGAIN inside crit and we wanted to get the io done. */ #define ASYNCIO_ON "ASYNCIO=ON" #define ASYNCIO_OFF "ASYNCIO=OFF" #define TWINNING_ON(CSD) (CSD->asyncio) /* twinning is turned ON only when asyncio is ON */ /* Sets udi->owning_gd to the right value at db open time. */ #define SYNC_OWNING_GD(reg) \ MBSTART { \ /* Either udi->owning_gd was already set to the right value, or it is NULL. \ * This could be because gvcst_init() was called previously, as in mupip_set() \ */ \ assert((NULL != reg->owning_gd) && (NULL == FILE_INFO(reg)->owning_gd) \ || (FILE_INFO(reg)->owning_gd == reg->owning_gd) \ || IS_STATSDB_REG(reg)); \ FILE_INFO(reg)->owning_gd = reg->owning_gd; \ } MBEND #define WAIT_FOR_WIP_QUEUE_TO_CLEAR(CNL, CRWIPQ, CR, REG, RET) \ RET = wait_for_wip_queue_to_clear(CNL, CRWIPQ, CR, REG) #define IS_AIO_ON(X) (X->asyncio) #define IS_AIO_ON_SEG(SEG) IS_AIO_ON(SEG) #define IS_AIO_ON_CSD(CSD) IS_AIO_ON(CSD) #define IS_AIO_DBGLDMISMATCH(SEG, TSD) (SEG->asyncio != TSD->asyncio) /* This macro is invoked to avoid a DBGLDMISMATCH error. Main purpose is to copy the asyncio setting */ #define COPY_AIO_SETTINGS(DST, SRC) DST->asyncio = SRC->asyncio /* Wait a max of 1 minute for pending async ios on a FD (e.g. database file) to be cleared. * Set TIMEDOUT variable to TRUE if we timed out. If not (i.e. async io cancellation is success) set it to FALSE. */ #ifdef USE_NOAIO #define WAIT_FOR_AIO_TO_BE_DONE(FD, TIMEDOUT) /* NO op, N/A */ #else #define WAIT_FOR_AIO_TO_BE_DONE(FD, TIMEDOUT) \ MBSTART { \ unsigned int lcnt, ret, save_errno; \ \ TIMEDOUT = TRUE; \ for (lcnt = 1; lcnt < SLEEP_ONE_MIN; lcnt++) \ { \ ret = aio_cancel(FD, NULL); \ save_errno = errno; \ assertpro(-1 != ret); \ assert((AIO_NOTCANCELED == ret) || (AIO_CANCELED == ret) || (AIO_ALLDONE == ret)); \ if (AIO_NOTCANCELED != ret) \ { \ TIMEDOUT = FALSE; \ break; \ } \ wcs_sleep(lcnt); \ } \ } MBEND #endif #define OK_FOR_WCS_RECOVER_FALSE FALSE #define OK_FOR_WCS_RECOVER_TRUE TRUE #define LOG_ERROR_FALSE FALSE #define LOG_ERROR_TRUE TRUE #define SKIP_BASEDB_OPEN_FALSE FALSE #define SKIP_BASEDB_OPEN_TRUE TRUE void assert_jrec_member_offsets(void); bt_rec_ptr_t bt_put(gd_region *r, block_id block); void bt_que_refresh(gd_region *greg); void bt_init(sgmnt_addrs *cs); void bt_malloc(sgmnt_addrs *csa); void bt_refresh(sgmnt_addrs *csa, boolean_t init); void db_common_init(gd_region *reg, sgmnt_addrs *csa, sgmnt_data_ptr_t csd); void grab_crit(gd_region *reg, wait_state state); boolean_t grab_crit_encr_cycle_sync(gd_region *reg, wait_state state); boolean_t grab_crit_immediate(gd_region *reg, boolean_t ok_for_wcs_recover, wait_state state); boolean_t grab_lock(gd_region *reg, boolean_t is_blocking_wait, uint4 onln_rlbk_action); void gv_init_reg(gd_region *reg, gd_addr *addr); void gvcst_init(gd_region *greg, gd_addr *addr); enum cdb_sc gvincr_compute_post_incr(srch_blk_status *bh); enum cdb_sc t_recompute_upd_array(srch_blk_status *bh, struct cw_set_element_struct *cse, cache_rec_ptr_t cr); enum cdb_sc gvincr_recompute_upd_array(srch_blk_status *bh, struct cw_set_element_struct *cse, cache_rec_ptr_t cr); boolean_t mupfndfil(gd_region *reg, mstr *mstr_addr, boolean_t log_error); boolean_t region_init(bool cm_regions); freeze_status region_freeze(gd_region *region, boolean_t freeze, boolean_t override, boolean_t wait_for_kip, uint4 online, boolean_t flush_sync); freeze_status region_freeze_main(gd_region *region, boolean_t freeze, boolean_t override, boolean_t wait_for_kip, uint4 online, boolean_t flush_sync, freeze_reg_mp_state *pfrms); freeze_status region_freeze_post(gd_region *region); void rel_crit(gd_region *reg); void rel_lock(gd_region *reg); boolean_t wcs_verify(gd_region *reg, boolean_t expect_damage, boolean_t caller_is_wcs_recover); void wcs_stale(TID tid, int4 hd_len, gd_region **region); void bmm_init(void); block_id bmm_find_free(block_id hint, uchar_ptr_t base_addr, block_id total_bits); bool reg_cmcheck(gd_region *reg); gd_binding *gv_srch_map(gd_addr *addr, char *key, int key_len, boolean_t skip_basedb_open); gd_binding *gv_srch_map_linear(gd_binding *start_map, char *key, int key_len); gd_binding *gv_srch_map_linear_backward(gd_binding *start_map, char *key, int key_len); gd_gblname *gv_srch_gblname(gd_addr *addr, char *key, int key_len); gvnh_reg_t *gv_bind_name(gd_addr *addr, mname_entry *targ); void gv_bind_subsname(gd_addr *addr, gv_key *key, gvnh_reg_t *gvnh_reg); void db_csh_ini(sgmnt_addrs *cs); void db_csh_ref(sgmnt_addrs *cs_addrs, boolean_t init); cache_rec_ptr_t db_csh_get(block_id block); cache_rec_ptr_t db_csh_getn(block_id block); enum cdb_sc tp_hist(srch_hist *hist1); tp_region *insert_region(gd_region *reg, tp_region **reg_list, tp_region **reg_free_list, int4 size); sm_uc_ptr_t get_lmap(block_id blk, unsigned char *bits, sm_int_ptr_t cycle, cache_rec_ptr_ptr_t cr); bool ccp_userwait(struct gd_region_struct *reg, uint4 state, int4 *timadr, unsigned short cycle); void ccp_closejnl_ast(struct gd_region_struct *reg); bt_rec *ccp_bt_get(sgmnt_addrs *cs_addrs, int4 block); unsigned char *mval2subsc(mval *in_val, gv_key *out_key, boolean_t std_null_coll); int4 dsk_read(block_id blk, sm_uc_ptr_t buff, enum db_ver *ondisk_blkver, boolean_t blk_free); gtm_uint64_t gds_file_size(file_control *fc); uint4 jnl_flush(gd_region *reg); void jnl_fsync(gd_region *reg, uint4 fsync_addr); void jnl_oper_user_ast(gd_region *reg); void jnl_wait(gd_region *reg); void view_jnlfile(mval *dst, gd_region *reg); void jnl_write_pfin(sgmnt_addrs *csa); void jnl_write_pini(sgmnt_addrs *csa); void jnl_write_epoch_rec(sgmnt_addrs *csa); void jnl_write_inctn_rec(sgmnt_addrs *csa); void fileheader_sync(gd_region *reg); gd_addr *create_dummy_gbldir(void); /* These prototypes should ideally be included in gvstats_rec.h but they require "sgmnt_addrs" type * to be defined which is done in this header file, hence the prototyping is done here instead. */ void gvstats_rec_csd2cnl(sgmnt_addrs *csa); void gvstats_rec_cnl2csd(sgmnt_addrs *csa); void gvstats_rec_upgrade(sgmnt_addrs *csa); void act_in_gvt(gv_namehead *gvt); gtm_uint64_t verify_queue_lock(que_head_ptr_t qhdr, sgmnt_addrs *csa); #define FILE_TYPE_REPLINST "replication instance" #define FILE_TYPE_DB "database" #define NO_STATS_OPTIN 0 #define ALL_STATS_OPTIN 1 #define SOME_STATS_OPTIN 2 #include "gdsfheadsp.h" /* End of gdsfhead.h */ #endif fis-gtm-V7.0-005/sr_port/gdsfilext.h0000755000032200000250000000170714342376331016215 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef GDSFILEXT_H_INCLUDED #define GDSFILEXT_H_INCLUDED #ifdef UNIX int4 gdsfilext(block_id blocks, block_id filesize, boolean_t trans_in_prog); # define GDSFILEXT(BLOCKS, FILESIZE, TRANS_IN_PROG) gdsfilext(BLOCKS, FILESIZE, TRANS_IN_PROG) #else int4 gdsfilext(uint4 blocks, uint4 filesize); # define GDSFILEXT(BLOCKS, FILESIZE, DUMMY) gdsfilext(BLOCKS, FILESIZE) #endif #define TRANS_IN_PROG_FALSE FALSE #define TRANS_IN_PROG_TRUE TRUE #endif fis-gtm-V7.0-005/sr_port/gdskill.h0000644000032200000250000000313014342376333015644 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef GDSKILL_H_INCLUDED #define GDSKILL_H_INCLUDED #include /* Since small memory is allocated in powers of two, keep the kill_set * structure size about 8 bytes under 1k mark (current size of * header used by memory management system) */ #define BLKS_IN_KILL_SET 251 /* Note that currently GDS_MAX_BLK_BITS is 62. This 62 bit block field allows for a 4Exa GDS block database, but the * current actual maximum block size is limited by the size of the master map. */ typedef struct { #ifdef BIGENDIAN gtm_uint8 flag : 1; /* Block was created by this TP transaction (not real block yet) */ gtm_uint8 level : 1; /* Block level (zero or non-zero) */ gtm_uint8 block : GDS_MAX_BLK_BITS; /* Block number */ #else gtm_uint8 block : GDS_MAX_BLK_BITS; /* Block number */ gtm_uint8 level : 1; /* Block level (zero or non-zero) */ gtm_uint8 flag : 1; /* Block was created by this TP transaction (not real block yet) */ #endif } blk_ident; typedef struct kill_set_struct { struct kill_set_struct *next_kill_set; int4 used; blk_ident blk[BLKS_IN_KILL_SET]; } kill_set; #endif fis-gtm-V7.0-005/sr_port/gdsroot.h0000755000032200000250000002020714342376331015701 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #ifndef GDSROOT_H #define GDSROOT_H #include "copy.h" #include #define DIR_ROOT 1 #define CDB_STAGNATE 3 #define CDB_MAX_TRIES (CDB_STAGNATE + 2) /* used in defining arrays, usage requires it must be at least 1 more than CSB_STAGNATE*/ #define T_FAIL_HIST_DBG_SIZE 32 #define MAX_BT_DEPTH 11 #define CSE_LEVEL_DRT_LVL0_FREE (MAX_BT_DEPTH + 2) /* used to indicate a level-0 block in GV tree will be freed */ #define GLO_NAME_MAXLEN 33 /* 1 for length, 4 for prefix, 15 for dvi, 1 for $, 12 for fid */ #define MAX_NUM_SUBSC_LEN 10 /* one for exponent, nine for the 18 significant digits */ /* Define padding in a gv_key structure to hold * 1) A number : This way we are ensured a key buffer overflow will never occur while converting a number * from mval representation to subscript representation. * 2) 16 byte of a string : This way if we are about to overflow the max-key-size, we are more likely to fit the * overflowing key in the padding space and so can give a more user-friendly GVSUBOFLOW message which * includes the overflowing subscript in most practical situations. */ #define MAX_GVKEY_PADDING_LEN (MAX_NUM_SUBSC_LEN + 16) #define EXTEND_WARNING_FACTOR 3 /* Define macro to compute the maximum key size required in the gv_key structure based on the database's maximum key size. * Align it to 4-byte boundary as this macro is mostly used by targ_alloc which allocates 3 keys one for gv_target->clue, * one for gv_target->first_rec and one for gv_target->last_rec. The alignment ensures all 3 fields start at aligned boundary. * In the following macro, we can ideally use the ROUND_UP2 macro but since this is used in a typedef (of "key_cum_value") * in gdscc.h, we cannot use that macro as it contains GTMASSERT expressions to do the 2-power check. To avoid this, [BYPASSOK] * we use the ROUND_UP macro (which has no checks). It is ok to do that instead of the more-efficient ROUND_UP2 macro * as the second parameter is a constant so all this should get evaluated at compile-time itself. */ #define DBKEYSIZE(KSIZE) (ROUND_UP((KSIZE + MAX_GVKEY_PADDING_LEN), 4)) /* Possible states for TREF(in_mu_swap_root_state) (part of MUPIP REORG -TRUNCATE) */ #define MUSWP_NONE 0 /* default; not in mu_swap_root */ #define MUSWP_INCR_ROOT_CYCLE 1 /* moving a root block; need to increment root_search_cycle */ #define MUSWP_FREE_BLK 2 /* freeing a directory block; need to write leaf blocks to snapshot file */ #define MUSWP_DIRECTORY_SWAP 3 /* moving a directory block; just checked by cert_blk */ typedef gtm_uint64_t trans_num; typedef uint4 trans_num_4byte; typedef int4 block_id_32; /* block_id type used pre-V7 kept for compatibility with old DBs * allows for GDS block #s to have 32 bits but see GDS_MAX_BLK_BITS below */ typedef uint4 ublock_id_32; /* unsigned type of the same length as block_id_32 */ typedef gtm_int8 block_id_64; /* block_id type used in V7+ * allows for GDS block #s to have 64 bits but see GDS_MAX_BLK_BITS below */ typedef gtm_uint8 ublock_id_64; /* unsigned type of the same length as block_id_64 */ typedef block_id_64 block_id; /* default block id type used in current release * NOTE: This definition is duplicated in rc_cpt_ops.h * and if changed should be changed there as well * NOTE: GDEVERIF.m assumes block_id to be int8. * NOTE: GBLDEF.m assumes block_id to be int8. */ typedef ublock_id_64 ublock_id; /* unsigned type of the same length as the default block_id in the current release */ #define BLK_NUM_64BIT /* Enables 64-bit block ID handling in DSE */ /* Returns size of block_id type based on result of IS_64_BLK_ID (See gdsdbver.h) */ #define SIZEOF_BLK_ID(X) ((X) ? SIZEOF(block_id_64) : SIZEOF(block_id_32)) /* These are memory access macros relabeled for explicit block_id references */ #define PUT_BLK_ID_32(X,Y) \ { \ assert((block_id_32)(Y) == (Y)); \ PUT_LONG(X,(block_id_32)Y); \ } #define GET_BLK_ID_32(X,Y) GET_LONG(X,Y) #define GET_BLK_ID_32P(X,Y) GET_LONGP(X,Y) #define PUT_BLK_ID_64(X,Y) PUT_LLONG(X,Y) #define GET_BLK_ID_64(X,Y) GET_LLONG(X,Y) #define GET_BLK_ID_64P(X,Y) GET_LLONGP(X,Y) /* These are memory access macros for general case block_id references * instead of a specific block_id width */ #define PUT_BLK_ID(X,Y) PUT_BLK_ID_64(X,Y) #define GET_BLK_ID(X,Y) GET_BLK_ID_64(X,Y) #define GET_BLK_IDP(X,Y) GET_BLK_ID_64P(X,Y) static inline void WRITE_BLK_ID(boolean_t long_blk_id, block_id blkid, sm_uc_ptr_t ptr) { if (long_blk_id) PUT_BLK_ID_64(ptr, blkid); else PUT_BLK_ID_32(ptr, blkid); } static inline void READ_BLK_ID(boolean_t long_blk_id, block_id* blkid, sm_uc_ptr_t ptr) { if (long_blk_id) GET_BLK_ID_64(*blkid, ptr); else GET_BLK_ID_32(*blkid, ptr); } /* This is the byte swap macro used for endian changing relabeled for block_id references */ #define BLK_ID_32_BYTESWAP(X) GTM_BYTESWAP_32(X) #define BLK_ID_64_BYTESWAP(X) GTM_BYTESWAP_64(X) #define BLK_ID_BYTESWAP(X) BLK_ID_64_BYTESWAP(X) #define GDS_MAX_BLK_BITS 62 /* see blk_ident structure in gdskill.h for why this cannot be any greater */ #define GDS_MAX_VALID_BLK (1<inode != (B)->inode) \ ? ((A)->inode > (B)->inode ? 1 : -1) \ : ((A)->device != (B)->device) \ ? ((A)->device > (B)->device ? 1 : -1) \ : 0) #define is_gdid_gdid_identical(A, B) (0 == gdid_cmp(A, B) ? TRUE: FALSE) #define VALFIRSTCHAR(X) (ISALPHA_ASCII(X) || ('%' == X)) #define VALFIRSTCHAR_WITH_TRIG(X) (ISALPHA_ASCII(X) || ('%' == X) GTMTRIG_ONLY(|| (HASHT_GBL_CHAR1 == X))) #define VALKEY(X) (ISALPHA_ASCII(X) || ISDIGIT_ASCII(X)) /* Prototypes below */ block_id get_dir_root(void); boolean_t get_full_path(char *orig_fn, unsigned int orig_len, char *full_fn, unsigned int *full_len, unsigned int max_len, uint4 *status); void gvinit(void); #endif fis-gtm-V7.0-005/sr_port/ged.mpt0000755000032200000250000001346414342376331015337 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 1989,2001 Sanchez Computer Associates, 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %ged ;%ged - global editor n (%zdebug) s %("lo")="abcdefghijklmnopqrstuvwxyz" s %("up")="ABCDEFGHIJKLMNOPQRSTUVWXYZ" s %("$zso")=$zso s %("$io")=$io s $zso=$select($zv'["VMS":".",1:"[]")_"_"_$j_".ged" s %("zlbeg")=$zl beg ; n $et s $et=$s($d(%zdebug):"b",1:"s $ec="""" zg "_$zl_":beg") u 0:(ctrap=$c(3)) f r !,"Edit ^",%("global"),! q:%("global")="" d . i $e(%("global"))'="^" s %("global")="^"_%("global") . if (($e(%("global"),2)="%")&($l(%("global"))=2)) d q .. w !,"%ged will not edit: ",%("global") .. q . if ($e(%("global"),2)="?") d help q . d convert . d main . k @%("local"),@(%("a")_$e(%("global"),3,%("glbl lngth"))) . zwi %("add"),%("change"),%("kill"),%("s") . quit s $zso=%("$zso") u %("$io"):(ctrap="":exception="") q ; main ; s %("glbl lngth")=$f(%("global"),"(")-2 if %("glbl lngth")=-2 s %("glbl lngth")=$l(%("global")) s %("local")=$e(%("global"),2,%("glbl lngth")) d zwr s %=%("global") o $zso u $zso f r % q:$zeof s @(%("a")_$e(%,3,9999)) s %("s")=$g(%("s"))+1 c $zso zed $zso o $zso:(read:rewind) d stor c $zso:delete o $zso c $zso:delete w !,"node : ",%("global") w !,"selected : ",+$g(%("s")) w !,"changed : ",+$g(%("change")) w !,"added : ",+$g(%("add")) w !,"killed : ",+$g(%("kill")) q ; zwr ; n $et s $et=$s($d(%zdebug):"b",1:"s $ec="""" goto continue") o $zso:(newversion:exc="":noreadonly) u $zso zwr @%("global") continue c $zso q badzwr ; c $zso:delete u 0 w !,"invalid global description: ",%("global") q stor ; s %("storlvl")=$zl u $zso f r % quit:$zeof d istor do work q istor ; n $et s $et="do istorerr" i '$l($tr(%," ")) q i $e(%)'="^" s %="^"_% i $e(%,1,%("glbl lngth"))=$e(%("global"),1,%("glbl lngth")) do . s %=$e(%,2,99999) . s @% . quit else do . set %("cur io")=$io . u 0 w !,"%ged will not edit: ",% . use %("cur io") . quit q ; istorerr; close $zso use 0 w !,"invalid syntax: ",%,!,"return to continue: " r %:30,! zed $zso open $zso:(read:rewind) s $ec="" zg %("storlvl"):stor work ; s %=%("local") if $d(@%)'[0 d check ; Set top node f s %=$q(@%) q:%="" d check d kill q check ; if ($d(@("^"_%))[0)&($d(@(%("a")_$e(%,2,9999)))[0) d add q if ($d(@("^"_%))[0)&($d(@(%("a")_$e(%,2,9999)))'[0) d addcheck q if ($d(@("^"_%))'[0)&($d(@(%("a")_$e(%,2,9999)))[0) d conflict q if @("^"_%)=@% d withdraw q if @(%("a")_$e(%,2,9999))=@("^"_%) d change q d checkerr q ; add ; s @("^"_%)=@% s %("add")=$g(%("add"))+1 d withdraw q ; change ; s @("^"_%)=@% s %("change")=$g(%("change"))+1 d withdraw q ; withdraw; if $d(@(%("a")_$e(%,2,9999)))'[0 zwithdraw @(%("a")_$e(%,2,9999)) q ; conflict; u 0 w !,"WARNING: The original value for node ^",%," was not stored before" w !,"the edit session and may have changed while in the editor.",! s %("%local")=%("a")_$e(%,2,9999) w !,"old value : ",$s($d(@%("%local"))'[0:@%("%local"),1:"") w !,"current value : ",@("^"_%) w !,"edit value : ",@(%),!! w !,"Do you still wish to use the edit value? [n] " r %("ans"):30 s %("ans")=$tr(%("ans"),%("lo"),%("up")) if "\NO"[("\"_%("ans")) d withdraw q if "\YES"[("\"_%("ans")) d change q d withdraw q addcheck; u 0 w !,"WARNING: Node ^",%," was deleted while in the editor",! s %("%local")=%("a")_$e(%,2,9999) w !,"old value : ",$s($d(@%("%local"))'[0:@%("%local"),1:"") w !,"current value : " w !,"edit value : ",@(%),!! w !,"Do you still wish to use the edit value? [n] " r %("ans"):30 s %("ans")=$tr(%("ans"),%("lo"),%("up")) if "\NO"[("\"_%("ans")) d withdraw q if "\YES"[("\"_%("ans")) d add q d withdraw q checkerr; u 0 w !,"WARNING: Node ^",%," was modified while in editor",! s %("%local")=%("a")_$e(%,2,9999) w !,"old value : ",$s($d(@%("%local"))'[0:@%("%local"),1:"") w !,"current value : ",@("^"_%) w !,"edit value : ",@(%),!! w !,"Do you still wish to use the edit value? [n] " r %("ans"):30 s %("ans")=$tr(%("ans"),%("lo"),%("up")) if "\NO"[("\"_%("ans")) d withdraw q if "\YES"[("\"_%("ans")) d add q d withdraw q ; kill ; n $et s $et="s $ec="""" q" s %=(%("a")_$e(%("local"),2,9999)) if $d(@%)'[0 d killcheck f s %=$q(@%) q:%="" d killcheck q ; killcheck; if $d(@("^"_%("b")_$e(%,2,9999)))[0 s %("kill")=$g(%("kill"))+1 q if @("^"_%("b")_$e(%,2,9999))=@% zwi @("^"_%("b")_$e(%,2,9999)) s %("kill")=$g(%("kill"))+1 q u 0 w !,"killed node has changed: " w !,"node : ^",%("b")_$e(%,2,9999) w !,"old value : ",@(%("a")_$e(%,2,9999)) w !,"current value : ",@("^"_%("b")_$e(%,2,9999)) q ; nofile c $zso:delete u 0 w !,"No changes made",! q ; convert; s %("b")=$e(%("global"),2) s %("a")=$a(%("global"),2) s %("a")=%("a")+1 s %("a")=$s(%("a")=91:65,%("a")=123:97,%("a")=38:65,1:%("a")) s %("a")=$c(%("a")) q ; help i "dD"[$e(%("global"),3),$l(%("global"))=3 d ^%GD q w !,"VALID INPUT",!! w !,?3,"",?16,"to leave the %GED utility ",! w !,?4,"?D",?16,"to display existing globals in your directory ",! w !,"[global name]",?16,"the MUMPS name for the global e.g. ABC" w !?16,"the global name may be followed by: " w !?16,"subscript(s) in parentheses" w !?16,"a subscript is a MUMPS expression e.g. ""joe"",10,$e(a,1)," w !?16,"a ""*"" as a subscript causes all descendents to be included," w !?16,"or by a range of subscripts in parentheses" w !?16,"expressed as [expr]:[expr] e.g 1:10 ""a"":""d""" w !?16,"a MUMPS pattern to match selected subscripts: ^TEST(?1.3N)" q fis-gtm-V7.0-005/sr_port/gendash.m0000755000032200000250000000133514342376331015637 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2001 Sanchez Computer Associates, 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; gendash ;generate sub-indices for dash type ttt's n (work,ttt,opcdcnt) f x=0:1:opcdcnt-1 d gd1 q gd1 i $d(work(x)) s ttt(x)=work(x) q i $o(work(x))\1'=x s ttt(x)=0 q s ttt(x)=ttt f i=x+.1:.1 d proc q:$o(work(i))\1'=x q proc i $d(work(i)) s ttt(ttt)=work(i) e s ttt(ttt)=0 s ttt=ttt+1 q fis-gtm-V7.0-005/sr_port/genout.m0000755000032200000250000000263614342376331015534 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2001, 2015 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; genout ;output the results o outfile:newv n knt s knt=0 s vms=$zversion["VMS" i vms s cfile=$ztrnlnm("gtm$src")_"copyright.txt" i 'vms s cfile=$ztrnlnm("gtm_tools")_"/copyright.txt" s xxxx="2001" s yyyy=$zdate($H,"YYYY") o cfile:read u outfile w "/****************************************************************",! f i=1:1 u cfile r line q:$zeof d . i (1<$zl(line,"XXXX")) d . . s str=$zpiece(line,"XXXX",1)_xxxx_$zpiece(line,"XXXX",2) . . s str=$zpiece(str,"YYYY",1)_yyyy_$zpiece(str,"YYYY",2) . e d . . s str=line . u outfile w " *"_str_"*",! c cfile u outfile w " ****************************************************************/",!! f i="mdef.h","vxi.h","vxt.h","xfer_enum.h" w "#include """,i,"""",! w "LITDEF short ttt[",ttt,"] = {",! f i=0:1:ttt-2 d prnt w ttt(ttt-1),"};",! c outfile q prnt i knt=0 w !,"/*",$j(i,5)," */",$c(9) w ttt(i),"," s knt=knt+1 i knt>7!(ttt(i)="VXT_END") s knt=0 q fis-gtm-V7.0-005/sr_port/get_cmd_qlf.c0000755000032200000250000001105214342376331016455 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_string.h" #include "cmd_qlf.h" #include "cli.h" #include "min_max.h" GBLDEF list_params lst_param; GBLREF command_qualifier glb_cmd_qlf; GBLREF boolean_t gtm_utf8_mode; void get_cmd_qlf(command_qualifier *qualif) { static readonly char upper[] = "UPPER"; static readonly char lower[] = "LOWER"; mstr *s; int4 temp_int; unsigned short len; unsigned char inbuf[255]; qualif->qlf = glb_cmd_qlf.qlf; qualif->object_file.mvtype = qualif->list_file.mvtype = qualif->ceprep_file.mvtype = 0; if (gtm_utf8_mode) qualif->qlf |= CQ_UTF8; /* Mark as being compiled in UTF8 mode */ if (CLI_PRESENT == cli_present("OBJECT")) { qualif->qlf |= CQ_OBJECT; qualif->object_file.mvtype = MV_STR; s = &qualif->object_file.str; /* 4SCA: object_file is allocated MAX_FN_LEN bytes */ len = s->len; if (FALSE == cli_get_str("OBJECT", s->addr, &len)) s->len = 0; else s->len = len; } else if (TRUE == cli_negated("OBJECT")) qualif->qlf &= ~CQ_OBJECT; if (CLI_PRESENT == cli_present("CROSS_REFERENCE")) /* CROSS_REFERENCE is undocumented and apparently not useful */ qualif->qlf |= CQ_CROSS_REFERENCE; else if (TRUE == cli_negated("CROSS_REFERENCE")) qualif->qlf &= ~CQ_CROSS_REFERENCE; if (TRUE == cli_present("IGNORE")) qualif->qlf |= CQ_IGNORE; else if (TRUE == cli_negated("IGNORE")) qualif->qlf &= ~CQ_IGNORE; if (CLI_PRESENT == cli_present("DEBUG")) /* the only other appearance of CQ_DEBUG is in cmd_qlf.h */ qualif->qlf |= CQ_DEBUG; else if (TRUE == cli_negated("DEBUG")) qualif->qlf &= ~CQ_DEBUG; if (TRUE == cli_negated("LINE_ENTRY")) /* NOLINE_ENTRY appears implies colon syntax on all labels */ qualif->qlf &= ~CQ_LINE_ENTRY; if (TRUE == cli_negated("INLINE_LITERALS")) qualif->qlf &= ~CQ_INLINE_LITERALS; if (TRUE == cli_negated("ALIGN_STRINGS")) /* ALIGN_STRINGS is undocument and unimplemented */ qualif->qlf &= ~CQ_ALIGN_STRINGS; if (TRUE == cli_negated("WARNINGS")) qualif->qlf &= ~CQ_WARNINGS; else if (CLI_PRESENT == cli_present("WARNINGS")) qualif->qlf |= CQ_WARNINGS; #ifdef DEBUG if (CLI_PRESENT == cli_present("MACHINE_CODE")) qualif->qlf |= CQ_MACHINE_CODE; else if (TRUE == cli_negated("MACHINE_CODE")) qualif->qlf &= ~CQ_MACHINE_CODE; #else qualif->qlf &= ~CQ_MACHINE_CODE; #endif if (TRUE == cli_negated("LIST")) qualif->qlf &= (~CQ_LIST & ~CQ_MACHINE_CODE); else if (CLI_PRESENT == cli_present("LIST")) { qualif->qlf |= CQ_LIST; qualif->list_file.mvtype = MV_STR; s = &qualif->list_file.str; /* 4SCA: list_file is allocated MAX_FN_LEN bytes */ len = s->len; if (FALSE == cli_get_str("LIST", s->addr, &len)) s->len = 0; else s->len = len; } else if (!(qualif->qlf & CQ_LIST)) qualif->qlf &= ~CQ_MACHINE_CODE; if (FALSE == cli_get_int("LENGTH",&temp_int)) temp_int = 66; lst_param.lines_per_page = temp_int; if ((FALSE == cli_get_int("SPACE",&temp_int)) || (0 >= temp_int) || (temp_int >= lst_param.lines_per_page)) temp_int = 1; lst_param.space = temp_int; if (CLI_PRESENT == cli_present("LABELS")) { len = SIZEOF(inbuf); if (cli_get_str("LABELS", (char *)&inbuf[0], &len)) { if (len == SIZEOF(upper) - 1) { if (!memcmp(upper, &inbuf[0], len)) qualif->qlf &= ~CQ_LOWER_LABELS; else if (!memcmp(lower, &inbuf[0], len)) qualif->qlf |= CQ_LOWER_LABELS; } } } if (CLI_PRESENT == cli_present("NAMEOFRTN")) qualif->qlf |= CQ_NAMEOFRTN; if (CLI_PRESENT == cli_present("CE_PREPROCESS")) { qualif->qlf |= CQ_CE_PREPROCESS; qualif->ceprep_file.mvtype = MV_STR; s = &qualif->ceprep_file.str; /* 4SCA: ceprep_file is allocated MAX_FN_LEN bytes */ len = s->len; if (FALSE == cli_get_str("CE_PREPROCESS", s->addr, &len)) s->len = 0; else s->len = len; } else if (TRUE == cli_negated("CE_PREPROCESS")) qualif->qlf &= ~CQ_CE_PREPROCESS; # ifdef USHBIN_SUPPORTED if (CLI_PRESENT == cli_present("DYNAMIC_LITERALS")) qualif->qlf |= CQ_DYNAMIC_LITERALS; # endif if (CLI_PRESENT == cli_present("EMBED_SOURCE")) qualif->qlf |= CQ_EMBED_SOURCE; else if (cli_negated("EMBED_SOURCE")) qualif->qlf &= ~CQ_EMBED_SOURCE; } fis-gtm-V7.0-005/sr_port/get_command_line.h0000755000032200000250000000115314342376331017503 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2002 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GET_COMMAND_LINE_INCLUDED #define GET_COMMAND_LINE_INCLUDED void get_command_line(mval *result, boolean_t zcmd_line); #endif /* GET_COMMAND_LINE_INCLUDED */ fis-gtm-V7.0-005/sr_port/get_dir_root.c0000755000032200000250000000114114342376331016667 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "gdsroot.h" block_id get_dir_root(void) { return DIR_ROOT; } fis-gtm-V7.0-005/sr_port/get_dlr_device.c0000755000032200000250000000177414342376331017162 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "io.h" #include "stringpool.h" GBLREF io_pair io_curr_device; void get_dlr_device(mval *v) { mstr x; char *cp, *cend; x.len = DD_BUFLEN; /* Default length, if dollar.device[] array is used. */ x.addr = NULL; (io_curr_device.in->disp_ptr->dlr_device)(&x); assert((0 >= x.len) || (NULL != x.addr)); v->mvtype = MV_STR; v->str.addr = cp = x.addr; cend = cp + x.len; for ( ; cp < cend && *cp ; cp++) ; v->str.len = INTCAST(cp - v->str.addr); s2pool(&v->str); } fis-gtm-V7.0-005/sr_port/get_dlr_key.c0000755000032200000250000000154014342376331016502 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "io.h" #include "stringpool.h" GBLREF io_pair io_curr_device; void get_dlr_key(mval *v) { mstr x; char buff[128], *cp, *cend; x.len = SIZEOF(buff); x.addr = buff; (io_curr_device.in->disp_ptr->dlr_key)(&x); v->mvtype = MV_STR; v->str.addr = cp = x.addr; cend = cp + x.len; for ( ; cp < cend && *cp ; cp++) ; v->str.len = INTCAST(cp - v->str.addr); s2pool(&v->str); } fis-gtm-V7.0-005/sr_port/get_dlr_zkey.c0000644000032200000250000000205314342376331016671 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "io.h" #include "stringpool.h" GBLREF io_pair io_curr_device; void get_dlr_zkey(mval *v) { mstr x; char buff[128], *cp, *cend; if (rm == io_curr_device.in->type) { x.len = SIZEOF(buff); x.addr = buff; (io_curr_device.in->disp_ptr->dlr_zkey)(&x); v->mvtype = MV_STR; v->str.addr = cp = x.addr; cend = cp + x.len; for ( ; cp < cend && *cp ; cp++) ; v->str.len = INTCAST(cp - v->str.addr); s2pool(&v->str); } else { (io_curr_device.in->disp_ptr->dlr_zkey)(&v->str); v->mvtype = MV_STR; } } fis-gtm-V7.0-005/sr_port/get_dollar_stack_info.c0000755000032200000250000000264214342376331020532 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "stringpool.h" #include "error_trap.h" GBLREF spdesc stringpool; GBLREF dollar_stack_type dollar_stack; /* structure containing $STACK related information */ void get_dollar_stack_info(int level, stack_mode_t mode, mval *result) { assert(0 <= level); assert(level < dollar_stack.index); switch (mode) { case DOLLAR_STACK_MODE: result->str = dollar_stack.array[level].mode_str; break; case DOLLAR_STACK_MCODE: result->str = dollar_stack.array[level].mcode_str; break; case DOLLAR_STACK_PLACE: result->str = dollar_stack.array[level].place_str; break; case DOLLAR_STACK_ECODE: if (NULL != dollar_stack.array[level].ecode_ptr) result->str = dollar_stack.array[level].ecode_ptr->ecode_str; else { result->str.len = 0; return; } break; default: assertpro(FALSE && mode); } s2pool(&result->str); assert(!result->str.len || IS_AT_END_OF_STRINGPOOL(result->str.addr, result->str.len)); return; } fis-gtm-V7.0-005/sr_port/get_frame_creation_info.c0000644000032200000250000000476114342376333021051 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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 #include "stack_frame.h" #include "stringpool.h" #include "error_trap.h" #define CREATEDBY_DO 0 #define CREATEDBY_XECUTE 1 #define CREATEDBY_FUNCTION 2 #define CREATEDBY_ZINTR 3 #define CREATEDBY_TRIGGER 4 #define CREATEDBY_ZTIMEOUT 5 GBLREF stack_frame *frame_pointer; GBLREF spdesc stringpool; #ifdef UNIX LITDEF mstr createdby_text[6] = {{0, LEN_AND_LIT("DO")}, {0, LEN_AND_LIT("XECUTE")}, {0, LEN_AND_LIT("$$")}, {0, LEN_AND_LIT("ZINTR")}, {0, LEN_AND_LIT("TRIGGER")}, {0, LEN_AND_LIT("ZTIMEOUT")}}; #endif #ifdef VMS LITDEF mstr createdby_text[4] = {{LEN_AND_LIT("DO")}, {LEN_AND_LIT("XECUTE")}, {LEN_AND_LIT("$$")}, {LEN_AND_LIT("ZINTR")}}; #endif void get_frame_creation_info(int level, int cur_zlevel, mval *result) { int count; stack_frame *fp; assert(0 < level); assert(level < cur_zlevel); count = cur_zlevel; for (fp = frame_pointer; ; fp = fp->old_frame_pointer) { if (NULL == fp->old_frame_pointer) { if (fp->type & SFT_TRIGR) /* Have a trigger baseframe, pick up stack continuation frame_pointer stored by base_frame() */ fp = *(stack_frame **)(fp + 1); else { /* Something wrong, just return null or assert if debug mode */ assert(FALSE); result->str.len = 0; return; } } assert(NULL != fp); if (!(fp->type & SFT_COUNT)) continue; count--; if (count == level) break; } assert(fp && (fp->type & SFT_COUNT)); if (fp && (fp->type & SFT_ZINTR)) result->str = createdby_text[CREATEDBY_ZINTR]; else if (fp && (fp->flags & SFF_INDCE)) result->str = createdby_text[CREATEDBY_XECUTE]; else if (fp && (fp->type & SFT_ZTIMEOUT)) result->str = createdby_text[CREATEDBY_ZTIMEOUT]; # ifdef GTM_TRIGGER else if (fp && (fp->old_frame_pointer->type & SFT_TRIGR)) result->str = createdby_text[CREATEDBY_TRIGGER]; # endif else if (fp && fp->ret_value) result->str = createdby_text[CREATEDBY_FUNCTION]; else result->str = createdby_text[CREATEDBY_DO]; } fis-gtm-V7.0-005/sr_port/get_frame_place_mcode.c0000644000032200000250000001214014342376333020453 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_string.h" #include #include "stack_frame.h" #include "stringpool.h" #include "error_trap.h" #include "objlabel.h" #include "cache.h" #include "cache_cleanup.h" #include "mu_gv_stack_init.h" #include "gtmimagename.h" #include "op.h" /* for op_fntext() prototype */ GBLREF stack_frame *frame_pointer; GBLREF stack_frame *error_frame; GBLREF spdesc stringpool; GBLREF dollar_ecode_type dollar_ecode; /* structure containing $ECODE related information */ GBLREF enum gtmImageTypes image_type; void get_frame_place_mcode(int level, stack_mode_t mode, int cur_zlevel, mval *result) { int count; stack_frame *fp; unsigned char pos_str[MAX_ENTRYREF_LEN]; mval label; mval routine; int ips; int offset; int s1, s2; boolean_t indirect_frame; ihdtyp *irtnhdr; cache_entry *indce; INTPTR_T *vp; unsigned char *fpmpc; assert(DOLLAR_STACK_PLACE == mode || DOLLAR_STACK_MCODE == mode); assert(0 <= level); assert(level < cur_zlevel); count = cur_zlevel; for (fp = frame_pointer; ; fp = fp->old_frame_pointer) { if (NULL == fp->old_frame_pointer) { if (fp->type & SFT_TRIGR) /* Have a trigger baseframe, pick up stack continuation frame_pointer stored by base_frame() */ fp = *(stack_frame **)(fp + 1); else { /* Something wrong, just return null or assert if debug mode */ assert(FALSE); result->str.len = 0; return; } } assert(NULL != fp); if (!(fp->type & SFT_COUNT)) continue; count--; if (count == level) break; } fpmpc = fp->mpc; if (ADDR_IN_CODE(fpmpc, fp->rvector)) { result->str.addr = (char *)&pos_str[0]; result->str.len = INTCAST(symb_line(fpmpc, &pos_str[0], MAX_ENTRYREF_LEN, 0, fp->rvector) - &pos_str[0]); indirect_frame = FALSE; } else { indirect_frame = TRUE; pos_str[0] = '@'; result->str.addr = (char *)&pos_str[0]; result->str.len = 1; } if (DOLLAR_STACK_PLACE == mode) { if (result->str.len) { s2pool(&result->str); assert(IS_AT_END_OF_STRINGPOOL(result->str.addr, result->str.len)); } } if (DOLLAR_STACK_MCODE == mode) { if (!indirect_frame) { if (IS_GTM_IMAGE || (0 < level)) { label.mvtype = MV_STR; routine.mvtype = MV_STR; result->mvtype = MV_STR; label.str.len = result->str.len; label.str.addr = (char *)&pos_str[0]; routine.str.len = 0; for (ips = 0, s1 = s2 = -1; ips < result->str.len; ips++) { if ('+' == pos_str[ips]) { assert((-1 == s1) && (-1 == s2)); s1 = ips; } if ('^' == pos_str[ips]) { s2 = ips; break; } } if (s2 >= 0) { routine.str.addr = (char *)&pos_str[s2 + 1]; routine.str.len = result->str.len - s2 - 1; label.str.len = s2; } offset = 0; if (s1 >= 0) { label.str.len = s1; if (s2 < 0) s2 = result->str.len; for (ips = s1 + 1; (ips < s2) && (MAX_ENTRYREF_LEN > ips); ips++) offset = offset * 10 + pos_str[ips] - '0'; } op_fntext(&label, offset, &routine, result); } else { /* Utility base frame does not have source code */ result->str.addr = UTIL_BASE_FRAME_CODE; result->str.len = STRLEN(UTIL_BASE_FRAME_CODE); } } else { /* We think we are looking for an indirect invocation (mpc not in routine expected) but if the * indirect flag is not on, then it's something else and trying to dig out indirect information * is going to cause this process to explode. So some simple validation first and if it is not * an indirect, deal with that fact. */ if (fp->flags & SFF_INDCE) { /* This is a real indirect - dig a bit to find the cache header and the actual code. * Note, this code picked up from cache_cleanup(). any changes here might need to be * reflected there. */ vp = (INTPTR_T *)fp->ctxt; assert(NULL != vp); vp--; assertpro(((GTM_OMAGIC << 16) + OBJ_LABEL) == *vp); /* Validate backward linkage */ vp--; irtnhdr = (ihdtyp *)((char *)vp + *vp); indce = irtnhdr->indce; assert(NULL != indce); assert(0 < indce->refcnt); /* currently used in the M stack better have a non-zero refcnt */ s2pool(&indce->src.str); result->str = indce->src.str; assert(IS_AT_END_OF_STRINGPOOL(result->str.addr, result->str.len)); } else { /* Not a real indirect. The mpc may have been reset by error handling to various assembler * routines or it just may be broken. Whatever the reason, the value to return is that the * code address and thus the code itself is not available so make an appropriate return. */ result->str.addr = "N/A"; result->str.len = SIZEOF("N/A") - 1; } } } return; } fis-gtm-V7.0-005/sr_port/get_fs_block_size.c0000644000032200000250000000362714342376331017672 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2016 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 "have_crit.h" #include "eintr_wrappers.h" #include "get_fs_block_size.h" #include "gtm_statvfs.h" #ifdef DEBUG #include "is_fstype_nfs.h" #endif OS_PAGE_SIZE_DECLARE uint4 get_fs_block_size(int fd) { struct statvfs bufvfs; int status; uint4 gtm_fs_block_size; unsigned long sys_fs_block_size; FSTATVFS(fd, &bufvfs, status); assert(-1 != status); assert(SIZEOF(sys_fs_block_size) == SIZEOF(bufvfs.f_frsize)); /* If fstatvfs call fails, we don't know what the underlying filesystem size is. * We found some NFS implementations return bufvfs.f.frsize values that are inappropriate. * Instead of erroring out at this point, we assume a safe value (the OS page size) and continue as much as we can. */ assert(DISK_BLOCK_SIZE <= bufvfs.f_frsize); assert((OS_PAGE_SIZE >= bufvfs.f_frsize) || is_fstype_nfs(fd)); sys_fs_block_size = ((-1 == status) || (OS_PAGE_SIZE < bufvfs.f_frsize) || (DISK_BLOCK_SIZE > bufvfs.f_frsize)) ? OS_PAGE_SIZE : bufvfs.f_frsize; /* Fit file system block size in a 4-byte unsigned integer as that is the size in jnl_buffer. * Assert that we never get a block size > what can be held in a 4-byte unsigned integer. */ gtm_fs_block_size = (uint4)sys_fs_block_size; assert(gtm_fs_block_size == sys_fs_block_size); assert(MAX_IO_BLOCK_SIZE >= gtm_fs_block_size); assert(MAX_IO_BLOCK_SIZE % gtm_fs_block_size == 0); return gtm_fs_block_size; } fis-gtm-V7.0-005/sr_port/get_fs_block_size.h0000644000032200000250000000112014342376331017661 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef GET_FS_BLOCK_SIZE_INCLUDED #define GET_FS_BLOCK_SIZE_INCLUDED uint4 get_fs_block_size(int fd); #endif /* GET_FS_BLOCK_SIZE_INCLUDED */ fis-gtm-V7.0-005/sr_port/get_lmap.c0000755000032200000250000000333114342376331016002 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "gdsroot.h" #include "gdsblk.h" #include "gdsbml.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" /* Include prototypes */ #include "t_qread.h" /* get_lmap.c: Reads local bit map and returns buffer address, two bit local bit-map value corresponding to the block, cycle and cr Input Parameter: blk: block id of the block whose bit map this routine is to fetch Output Parameter: bits: two bit local bit map cycle: Cycle value found in t_qread cr: Cache Record value found in t_qread Returns: buffer address of local bitmap block Null: if t_qread fails */ sm_uc_ptr_t get_lmap (block_id blk, unsigned char *bits, sm_int_ptr_t cycle, cache_rec_ptr_ptr_t cr) { sm_uc_ptr_t ptr, bp; block_id index, offset; error_def(ERR_DSEBLKRDFAIL); index = ROUND_DOWN2(blk, BLKS_PER_LMAP); offset = blk - index; bp = t_qread (index, cycle, cr); if (bp) { ptr = bp + SIZEOF(blk_hdr) + (offset * BML_BITS_PER_BLK) / 8; *bits = *ptr; switch (blk % (8 / BML_BITS_PER_BLK)) { case 0: break; case 1: *bits = *bits >> BML_BITS_PER_BLK; break; case 2: *bits = *bits >> 2 * BML_BITS_PER_BLK; break; case 3: *bits = *bits >> 3 * BML_BITS_PER_BLK; break; } *bits = *bits & 3; } return bp; } fis-gtm-V7.0-005/sr_port/get_log_name.c0000755000032200000250000000336714342376331016643 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "io.h" #include "mmemory.h" GBLREF io_log_name *io_root_log_name; error_def(ERR_INVSTRLEN); #define LOGNAME_LEN 255 io_log_name *get_log_name(mstr *v, bool insert) { io_log_name *l, *prev, *new; int4 index, stat, v_len; unsigned char buf[LOGNAME_LEN]; assert(0 != io_root_log_name); assert(0 == io_root_log_name->len); v_len = v->len; if (0 >= v_len) return io_root_log_name; assert(0 <= (uint4)v_len); if (LOGNAME_LEN < v_len) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_INVSTRLEN, 2, v_len, LOGNAME_LEN); return NULL; } memcpy(buf, v->addr, v_len); for (prev = io_root_log_name, l = prev->next; NULL != l; prev = l, l = l->next) { if ((NULL != l->iod) && (n_io_dev_types == l->iod->type)) { assert(FALSE); continue; /* skip it on pro */ } stat = memvcmp(l->dollar_io, l->len, buf, v_len); if (0 == stat) return l; if (0 < stat) break; } if (insert == INSERT) { assert(0 != prev); new = (io_log_name *)malloc(sizeof(*new) + v_len); memset(new, 0, sizeof(*new) - 1); new->len = v_len; memcpy(new->dollar_io, buf, v_len); new->dollar_io[v_len] = 0; prev->next = new; new->next = l; return new; } assert(NO_INSERT == insert); return 0; } fis-gtm-V7.0-005/sr_port/get_mladdr.c0000755000032200000250000000301614342376331016314 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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 "gtm_string.h" #include "compiler.h" #include "cmd_qlf.h" #include "mmemory.h" #include "gtm_caseconv.h" #include "min_max.h" #include "stringpool.h" GBLREF mlabel *mlabtab; GBLREF command_qualifier cmd_qlf; mlabel *get_mladdr(mident *lab_name) { mident_fixed upper_ident; mident *lname, upper_lname; mlabel **p; int4 x; mstr lab_str; lname = lab_name; if (!(cmd_qlf.qlf & CQ_LOWER_LABELS)) { lower_to_upper((uchar_ptr_t)&upper_ident.c[0], (uchar_ptr_t)lab_name->addr, lab_name->len); upper_lname.len = lab_name->len; upper_lname.addr = &upper_ident.c[0]; lname = &upper_lname; } for (p = &mlabtab; *p; ) { MIDENT_CMP(&(*p)->mvname, lname, x); if (x < 0) p = &((*p)->rson); else if (x > 0) p = &((*p)->lson); else return *p; } lab_str.len = lname->len; lab_str.addr = lname->addr; s2pool_align(&lab_str); *p = (mlabel *) mcalloc(SIZEOF(mlabel)); (*p)->mvname.len = lab_str.len; (*p)->mvname.addr = lab_str.addr; assert(!(*p)->lson && !(*p)->rson); (*p)->formalcnt = NO_FORMALLIST; (*p)->gbl = TRUE; return *p; } fis-gtm-V7.0-005/sr_port/get_mmseg.c0000755000032200000250000000344714342376331016171 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 #include "mmseg.h" GBLREF sm_uc_ptr_t min_mmseg; GBLREF sm_uc_ptr_t max_mmseg; GBLREF mmseg *mmseg_head; OS_PAGE_SIZE_DECLARE /* === get virtual address if available === */ caddr_t get_mmseg(size_t size) { mmseg *curr, *newone; sm_uc_ptr_t begin_hint, end_hint; #if defined(__osf__) && defined(__alpha) /* caddr_t should be 64 bits */ assert(8 == SIZEOF(caddr_t)); #endif if (!mmseg_head) { #if defined(__osf__) && defined(__alpha) /* Some simple initial value for Tru64 UNIX, these numbers * really should be from testing */ min_mmseg = (sm_uc_ptr_t)0x4000000000L; max_mmseg = (sm_uc_ptr_t)0x3E000000000L; #endif return (caddr_t)min_mmseg; } if ((mmseg_head->begin > min_mmseg) && (size < mmseg_head->begin - min_mmseg)) return (caddr_t)min_mmseg; curr = mmseg_head; while (curr) { end_hint = curr->next ? (sm_uc_ptr_t)curr->next->begin : max_mmseg; end_hint = (sm_uc_ptr_t)(ROUND_DOWN2((long)end_hint, OS_PAGE_SIZE)); begin_hint = (sm_uc_ptr_t)(ROUND_UP2((long)(curr->end), OS_PAGE_SIZE)); if (((unsigned long)begin_hint > (unsigned long)min_mmseg) && ((unsigned long)size < (unsigned long)end_hint - (unsigned long)begin_hint)) return (caddr_t)begin_hint; curr = curr->next; } /* Our managable virtual address space is used up, let system decide */ return (caddr_t)NULL; } fis-gtm-V7.0-005/sr_port/get_mumps_code.h0000755000032200000250000000076614342376331017222 0ustar librarygtc/**************************************************************** * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ void get_mumps_code(stack_frame *fp, mval *place, mval *mumpscode); fis-gtm-V7.0-005/sr_port/get_mvaddr.c0000755000032200000250000000273214342376331016332 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "gtm_string.h" #include "compiler.h" #include "mmemory.h" #include "min_max.h" #include "stringpool.h" GBLREF mvar *mvartab; GBLREF mvax *mvaxtab, *mvaxtab_end; GBLREF int mvmax; mvar *get_mvaddr(mident *var_name) { mvar **p; mvax *px; mstr vname; int x; p = &mvartab; while (*p) { MIDENT_CMP(&(*p)->mvname, var_name, x); if (x < 0) p = &((*p)->rson); else if (x > 0) p = &((*p)->lson); else return *p; } /* variable doesn't exist - create a new mvar in mvartab */ vname.len = var_name->len; vname.addr = var_name->addr; s2pool_align(&vname); *p = (mvar *)mcalloc(SIZEOF(mvar)); (*p)->mvname.len = vname.len; (*p)->mvname.addr = vname.addr; (*p)->mvidx = mvmax++; (*p)->lson = (*p)->rson = NULL; (*p)->last_fetch = NULL; px = (mvax *)mcalloc(SIZEOF(mvax)); px->var = *p; px->last = px->next = 0; px->mvidx = (*p)->mvidx; if (mvaxtab_end) { px->last = mvaxtab_end; mvaxtab_end->next = px; mvaxtab_end = px; } else mvaxtab = mvaxtab_end = px; return *p; } fis-gtm-V7.0-005/sr_port/get_page_size.h0000755000032200000250000000124114342376331017022 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ #ifndef GET_PAGE_SIZE_INCLUDED #define GET_PAGE_SIZE_INCLUDED void get_page_size(void); void get_hugepage_size(void); #endif /* GET_PAGE_SIZE_INCLUDED */ fis-gtm-V7.0-005/sr_port/get_reference.c0000755000032200000250000000410414342376331017006 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "gdsroot.h" /* needed for gdsfhead.h */ #include "gtm_facility.h" /* needed for gdsfhead.h */ #include "fileinfo.h" /* needed for gdsfhead.h */ #include "gdsbt.h" /* needed for gdsfhead.h */ #include "gdsfhead.h" #include "stringpool.h" #include "format_targ_key.h" #include "get_reference.h" /* for get_reference() prototype */ GBLREF gv_key *gv_currkey; GBLREF spdesc stringpool; GBLREF mstr extnam_str; void get_reference(mval *var) { char *end, *start; char extnamdelim[] = "^|\"\"|"; char *extnamsrc, *extnamtop; int maxlen; /* you need to return a double-quote for every single-quote. assume worst case. */ maxlen = MAX_ZWR_KEY_SZ + (!extnam_str.len ? 0 : ((extnam_str.len * 2) + SIZEOF(extnamdelim))); ENSURE_STP_FREE_SPACE(maxlen); var->mvtype = MV_STR; start = var->str.addr = (char *)stringpool.free; var->str.len = 0; if (gv_currkey && gv_currkey->end) { if (extnam_str.len) { *start++ = extnamdelim[0]; *start++ = extnamdelim[1]; *start++ = extnamdelim[2]; extnamsrc = &extnam_str.addr[0]; extnamtop = extnamsrc + extnam_str.len; for ( ; extnamsrc < extnamtop; ) { *start++ = *extnamsrc; if ('"' == *extnamsrc++) /* caution : pointer increment side-effect */ *start++ = '"'; } *start++ = extnamdelim[3]; } end = (char *)format_targ_key((unsigned char *)start, MAX_ZWR_KEY_SZ, gv_currkey, TRUE); if (extnam_str.len) /* Note: the next vertical bar overwrites the caret that * was part of he original name of the global variable */ *start = extnamdelim[4]; var->str.len = INTCAST(end - var->str.addr); stringpool.free += var->str.len; } } fis-gtm-V7.0-005/sr_port/get_reference.h0000755000032200000250000000110714342376331017013 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GET_REFERENCE_H_INCLUDED #define GET_REFERENCE_H_INCLUDED void get_reference(mval *var); #endif /* GET_REFERENCE_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/get_ret_targ.c0000644000032200000250000000220214342376333016653 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include #include "stack_frame.h" #include "get_ret_targ.h" GBLREF stack_frame *frame_pointer; GBLREF unsigned char *msp, *stackbase, *stacktop; /* return the target of the return for this frame; return NULL if not called as extrinsic */ mval *get_ret_targ(stack_frame **retsf) { stack_frame *sf; assert((stackbase >= msp) && (stacktop < msp)); assert(((stack_frame *)stackbase >= frame_pointer) && ((stack_frame *)stacktop < frame_pointer)); for (sf = frame_pointer; NULL != sf; sf = sf->old_frame_pointer) if (SFT_COUNT & sf->type) /* a counted frame; look no further */ { if (NULL != retsf) *retsf = sf; return sf->ret_value; } return NULL; } fis-gtm-V7.0-005/sr_port/get_ret_targ.h0000755000032200000250000000116714342376331016672 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GET_RET_TARG_H_INCLUDED #define GET_RET_TARG_H_INCLUDED mval *get_ret_targ(stack_frame **retsf); #endif fis-gtm-V7.0-005/sr_port/get_root.h0000755000032200000250000000113214342376331016036 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GET_ROOT_H_INCLUDED #define GET_ROOT_H_INCLUDED int get_root(void); #endif fis-gtm-V7.0-005/sr_port/get_spec.c0000644000032200000250000000321214342376331015776 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "spec_type.h" #include "get_spec.h" GBLREF gv_key *gv_altkey; GBLREF gv_namehead *gv_target; GBLREF gd_region *gv_cur_region; error_def(ERR_GVIS); error_def(ERR_INVSPECREC); static readonly int spec_len[MAX_COLL_TYPE + 1]={0,COLL_SPEC_LEN}; /*Length of each type of collation. *Must be updated if new collation types are added*/ uchar_ptr_t get_spec(uchar_ptr_t spec_rec_addr, int spec_rec_len, unsigned char spec_type) { uchar_ptr_t ptr, top; for (ptr = spec_rec_addr, top = ptr + spec_rec_len; ptr < top; ptr += spec_len[*ptr]) { if (*ptr == spec_type) return ptr; assert(spec_len[*ptr]); /* below gives an error in pro to stop indefinite loop */ if ((MAX_COLL_TYPE < *ptr) || (0 == spec_len[*ptr])) { /* no match or 0 length for increment */ gv_target->root = 0; RTS_ERROR_CSA_ABT(REG2CSA(gv_cur_region), VARLSTCNT(6) ERR_INVSPECREC, 0, ERR_GVIS, 2, gv_altkey->end - 1, gv_altkey->base); } } return (uchar_ptr_t)NULL; } fis-gtm-V7.0-005/sr_port/get_spec.h0000755000032200000250000000115714342376331016014 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GET_SPEC_INCLUDED #define GET_SPEC_INCLUDED uchar_ptr_t get_spec(uchar_ptr_t spec_rec_addr, int spec_rec_len, unsigned char spec_type); #endif /* GET_SPEC_INCLUDED */ fis-gtm-V7.0-005/sr_port/get_symb_line.c0000644000032200000250000000601314342376333017031 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_string.h" #include #include "stack_frame.h" #include "error_trap.h" /* for error_ret() declaration */ #include "gtmimagename.h" #define PROCESS_EXITING "-EXITING" GBLREF stack_frame *frame_pointer; GBLREF stack_frame *error_frame; GBLREF dollar_ecode_type dollar_ecode; /* structure containing $ECODE related information */ GBLREF int process_exiting; LITREF gtmImageName gtmImageNames[]; unsigned char *get_symb_line(unsigned char *out, int max_len, unsigned char **b_line, unsigned char **ctxt) { boolean_t line_reset; stack_frame *fp; unsigned char *addr, *out_addr; unsigned char *fpmpc, *fpctxt; line_reset = FALSE; for (fp = frame_pointer; fp; fp = fp->old_frame_pointer) { # ifdef GTM_TRIGGER if ((NULL == fp->old_frame_pointer) && (fp->type & SFT_TRIGR)) /* Have a trigger baseframe, pick up stack continuation frame_pointer stored by base_frame() */ fp = *(stack_frame **)(fp + 1); # endif fpmpc = fp->mpc; fpctxt = fp->ctxt; if (ADDR_IN_CODE(fpmpc, fp->rvector)) { if (NULL != ctxt) *ctxt = fpctxt; if (line_reset) addr = fpmpc + 1; else addr = fpmpc; out_addr = symb_line(addr, out, max_len, b_line, fp->rvector); assert(out < out_addr); return out_addr; } else { if ((fp->type & SFT_ZTRAP) || (fp->type & SFT_DEV_ACT)) line_reset = TRUE; } } /* At this point, we were unable to discover what was executing from the M stack. It is possible this error * occurred either while GT.M was shutting down thus the stack could be completely unwound. Or this may not * even be a mumps process (could be update process driving a trigger). Or this could have nothing to do with * generated code and could occur in most any of the utility modules - especially replication processes. Do * what we can to indicate where the problem occurred. */ if (process_exiting || !IS_MCODE_RUNNING) { /* Show which image is running */ memcpy(out, gtmImageNames[image_type].imageName, gtmImageNames[image_type].imageNameLen); out_addr = out + gtmImageNames[image_type].imageNameLen; if (process_exiting) { /* Add the information that this process was exiting */ MEMCPY_LIT(out_addr, PROCESS_EXITING); out_addr += STR_LIT_LEN(PROCESS_EXITING); } return out_addr; } /* At this point we know the process was not exiting, was executing M code, but we *still* couldn't find where * on the M stack. This is an out of design situation so we cause an assert failure. */ assertpro(fp); return NULL; /* For the compiler */ } fis-gtm-V7.0-005/sr_port/getjobname.c0000755000032200000250000000404014342376331016323 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_string.h" #include "stringpool.h" #include "mvalconv.h" #include "getjobnum.h" #include "getjobname.h" #ifdef DEBUG #include "wbox_test_init.h" #include "gt_timer.h" #endif GBLREF uint4 process_id; GBLDEF mval dollar_job; static char djbuff[10]; /* storage for dollar job's string form */ void getjobname(void) { getjobnum(); i2usmval(&dollar_job, process_id); n2s(&dollar_job); assert(dollar_job.str.len <= SIZEOF(djbuff)); memcpy(djbuff,dollar_job.str.addr,dollar_job.str.len); dollar_job.str.addr = djbuff; # ifdef DEBUG /* The below white-box code was previously in INVOKE_INIT_SECSHR_ADDRS but when it was removed, the white-box * code was moved over to "getjobname" which was (and continues to be) invoked just before when INVOKE_INIT_SECSHR_ADDRS * used to be invoked. */ if (WBTEST_ENABLED(WBTEST_SLAM_SECSHR_ADDRS)) { /* For this white box test, we're going to send ourselves a SIGTERM termination signal at a specific point * in the processing to make sure it succeeds without exploding during database initialization. To test the * condition GTM-8455 fixes. */ kill(process_id, SIGTERM); hiber_start(20 * 1000); /* Wait up to 20 secs - don't use wait_any as the heartbeat timer * will kill this wait in 0-7 seconds or so. */ /* We sent, we waited, wait expired - weird - funky condition is for identification purposes (to identify the * actual assert). We should be dead or dying, not trying to resume. */ assert(WBTEST_SLAM_SECSHR_ADDRS == 0); } # endif } fis-gtm-V7.0-005/sr_port/getjobname.h0000755000032200000250000000114114342376331016327 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GETJOBNAME_H_INCLUDED #define GETJOBNAME_H_INCLUDED void getjobname(void); #endif fis-gtm-V7.0-005/sr_port/getjobnum.h0000755000032200000250000000113614342376331016212 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GETJOBNUM_H_INCLUDED #define GETJOBNUM_H_INCLUDED void getjobnum(void); #endif fis-gtm-V7.0-005/sr_port/getprime.c0000755000032200000250000000124214342376331016025 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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" int4 getprime (int4 n) /* Returns first prime # >= n */ { int m, p; for (m = n | 1 ; ; m += 2) { for (p = 3 ; ; p += 2) { if (p * p > m) return m; if (((m / p) * p) == m) break; } } } fis-gtm-V7.0-005/sr_port/getstorage.h0000755000032200000250000000106714342376331016367 0ustar librarygtc/**************************************************************** * * * 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. * * * ****************************************************************/ #ifndef GETSTORAGE_INCLUDED #define GETSTORAGE_INCLUDED int4 getstorage(void); #endif /* GETSTORAGE_INCLUDED */ fis-gtm-V7.0-005/sr_port/getzdir.c0000755000032200000250000000160614342376331015665 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "gtm_unistd.h" #include "gtm_string.h" #include "getzdir.h" #include "setzdir.h" GBLREF mval dollar_zdir; void getzdir(void) { mval cwd; setzdir(NULL, &cwd); if (cwd.str.len > dollar_zdir.str.len) { if (NULL != dollar_zdir.str.addr) free(dollar_zdir.str.addr); dollar_zdir.str.addr = malloc(cwd.str.len); } dollar_zdir.str.len = cwd.str.len; memcpy(dollar_zdir.str.addr, cwd.str.addr, cwd.str.len); return; } fis-gtm-V7.0-005/sr_port/getzdir.h0000755000032200000250000000104414342376331015666 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GETZDIR_INCLUDED #define GETZDIR_INCLUDED void getzdir(void); #endif /* GETZDIR_INCLUDED */ fis-gtm-V7.0-005/sr_port/getzmode.h0000755000032200000250000000105014342376331016031 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GETZMODE_INCLUDED #define GETZMODE_INCLUDED void getzmode(void); #endif /* GETZMODE_INCLUDED */ fis-gtm-V7.0-005/sr_port/getzposition.c0000644000032200000250000000165614342376333016757 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "stringpool.h" #include #include "stack_frame.h" #include "getzposition.h" GBLREF spdesc stringpool; void getzposition (mval *v) { ENSURE_STP_FREE_SPACE(MAX_ENTRYREF_LEN); v->mvtype = MV_STR; v->str.addr = (char *) stringpool.free; stringpool.free = get_symb_line (stringpool.free, MAX_ENTRYREF_LEN, 0, 0); v->str.len = INTCAST((char *)stringpool.free - v->str.addr); return; } fis-gtm-V7.0-005/sr_port/getzposition.h0000755000032200000250000000107314342376331016756 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GETZPOSITION_INCLUDED #define GETZPOSITION_INCLUDED void getzposition(mval *v); #endif /* GETZPOSITION_INCLUDED */ fis-gtm-V7.0-005/sr_port/getzprocess.h0000755000032200000250000000106414342376331016570 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GETZPROCESS_INCLUDED #define GETZPROCESS_INCLUDED void getzprocess(void); #endif /* GETZPROCESS_INCLUDED */ fis-gtm-V7.0-005/sr_port/gi.mpt0000755000032200000250000001076014342376331015173 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 1991-2019 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %GI ;Load globals into database ;Possible enhancements: ;selection and/or exclusion by key list, range and/or wildcard ;optional confirmation by global name ;callable entry point ; new c,chset,d,dos,fmt,g,i,n,q,sav,saveread,x,y,%ZD set d("io")=$io use $principal write !,"Global Input Utility",! set $zstatus="" if '$data(%zdebug) new $etrap set $etrap="zgoto "_$zlevel_":err^"_$text(+0) do . zshow "d":d ; save original $p settings . set x=$piece($piece(d("D",1),"CTRA=",2)," ") . set:""=x x="""""" . set d("use")="$principal:(ctrap="_x . set x=$piece(d("D",1),"EXCE=",2),x=$zwrite($extract(x,2,$length(x)-1)) . set:""=x x="""""" . set d("use")=d("use")_":exception="_x_":"_$select($find(d("D",1),"NOCENE"):"nocenable",1:"cenable")_")" . use $principal:(ctrap=$char(3,4):exception="halt:$zeof!($zstatus[""TERMWRITE"") "_$etrap:nocenable) for do quit:$length(%ZD) . read !,"Input device: : ",%ZD . if '$length(%ZD) set %ZD=$principal quit . quit:"^"=%ZD . if "?"=%ZD do quit . . write !!,"Select the device you want for input" . . write !,"If you wish to exit enter a caret (^)",! . . set %ZD="" . if $zparse(%ZD)="" write " no such device" set %ZD="" quit . open:$principal'=%ZD %ZD:(readonly:rewind:stream:recordsize=2044:ichset="M":exception="goto noopen"):0 . if '$test write !,%ZD," is not available" set %ZD="" . quit noopen . write !,$piece($ZS,",",2,999),! . close %ZD . set ($zstatus,%ZD)="" if "^"=%ZD do err quit write !! set sav="",(g,n)=0 if $principal'=%ZD use %ZD:(exception="zgoto "_$zlevel_":eof":ctrap=$C(3,4)) do quit:"^"=x . read x,y . use $principal . set dos=($zchar(13)=$extract(x,$length(x))) ; the label selects dos/not for entire file . set:dos x=$extract(x,1,$length(x)-1),y=$extract(y,1,$length(y)-1) . write !,x,!,y,! . set chset=$select(x["UTF-8":"UTF-8",1:"M") . if $zchset'=chset write "Extract CHSET ",chset," doesn't match current $ZCHSET ",$zchset,! . read !,"OK ? ",x,!! . if "^"=x do err quit . if $length(x),$extract("NO",1,$length(x))=$translate(x,"no","NO") set x="^" do err quit . set fmt=$get(y)["ZWR" else do if "^"=x do err quit ; if input is $p, no label . set chset=$zchset,dos=0,fmt=1 . read !,"Format ? ",x,!! . quit:"^"=x . if $length(x) set x=$translate($zconvert(x,"U"),"L") if $extract("GO",1,$length(x))=x set fmt=0 quit set x=$$read if 'fmt do . if x?1"^"1(1"%",1A).30AN.1(1"("1.E1")")1"=".E set fmt=1 quit ; looks like ZWR . set y=$$read . do for set x=$$read,y=$$read if "*"'[$extract(x) do ; GLO . . quit:(""=x)&(""=y) . . set @x=y . . set n=n+1,x=$piece(x,"(") . . if x'=sav,x'="^" do . . . set g=g+1,sav=x . . . if $principal'=%ZD use $principal write:$x>70 ! write x,?$x\10+1*10 if (fmt) do for set x=$$read do ; ZWR . quit:""=x . set (i,q)=1,y="" ; find first equal-sign not in quotes . for set c=i,i=$find(x,"=",i) do:$extract(x,c,i-2)["""" quit:q . . for c=c:1:i-2 set:""""=$extract(c) q='q . set y=$extract(x,1,i-2) . if 8193>$zlength(x) set @x . else set:8192<$length(y) y=$zwrite(y,1) set @y=$zwrite($extract(x,i,$length(x)),1) . set n=n+1,x=$piece(y,"(") . if x'=sav,"^"'=x do . . set g=g+1,sav=x . . if $principal'=%ZD use $principal write:$x>70 ! write x,?$x\10+1*10 eof ; err set $ecode="" if $data(%ZD),%ZD'=$principal close %ZD use:$data(d("use")) @d("use") use:$data(d("io")) d("io") if ""'=$zstatus,($zstatus'["CTRAP")&($zstatus'["IOEOF") write !,"ERROR: ",$zstatus quit:'$get(n) write !!,"Restored ",n," node",$select(n=1:"",1:"s") write " in ",g," global",$select(g=1:".",1:"s.") quit read() ; concatenate reads that fill the buffer; also centralize the USE and dos stripping new i,e,x use %ZD set e=0,x=$get(saveread),i=1+(""'=x),saveread="" for i=i:1 do quit:e . if $principal=$io read "> ",x(i),! . else read x(i) . if $zeof,$increment(e) quit . if ""=x(i),$increment(e) quit . if 0=i#2,$increment(e) set saveread=x(i) quit . set x=x_x(i) . if 2044'=$zlength(x(i)),$increment(e) quit set:dos x=$extract(x,1,$length(x)-1) quit x fis-gtm-V7.0-005/sr_port/global_map.c0000755000032200000250000000631614342376331016315 0ustar librarygtc/**************************************************************** * * * 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 "gtm_string.h" #include "global_map.h" #include "min_max.h" /* "map[]" is assumed to be an array of mstrs whose last mstr has a NULL value for its "addr" field. * "map[]" contains even number of mstrs, each consecutive pair of mstrs (starting from map[0]) point to a range of names. * Also "map[]" is an array of strings arranged in non-decreasing alphabetical order. * Thus "map[]" is an array representing a set of ranges starting from the smallest to the largest. * "beg" and "end" are the end-points of the range (both inclusive) to be inserted into this sorted range set. * Note that a point is represented as a range whose begin and end are the same. */ void global_map(mstr map[], mstr *beg, mstr *end) { mstr *left, *right, rangestart, rangeend, tmpmstr; int rslt; DEBUG_ONLY( MSTRP_CMP(beg, end, rslt); assert(0 >= rslt); ) for (left = map; left->addr; left++) { MSTRP_CMP(left, beg, rslt); if (0 <= rslt) break; } /* left now points to the first mstr in the array that is >= beg */ for (right = left; right->addr; right++) { MSTRP_CMP(right, end, rslt); if (0 < rslt) break; } /* right now points to the first mstr in the array that is > end */ if (left == right) { /* the usual case where {beg, end} has no or complete intersections with any existing range in map[]. * in the case of complete intersection return. * in case of no intersection, insert {beg, end} maintaining sorted order by shifting all higher existing ranges * two positions to the right */ if ((right - map) & 1) return; rangestart = *beg; rangeend = *end; while (left->addr) { /* {rangestart, rangeend} is the current range to be inserted */ tmpmstr = *left; *left++ = rangestart; rangestart = tmpmstr; tmpmstr = *left; *left++ = rangeend; rangeend = tmpmstr; } *left++ = rangestart; *left++ = rangeend; left->addr = 0; return; } /* case where {beg, end} has partial intersections with existing ranges in map[]. * replace intersecting ranges with one union range e.g. replace {1, 10} {5, 15} {12, 20} with {1, 20} */ if (0 == ((left - map) & 1)) *left++ = *beg; if (0 == ((right - map) & 1)) *(--right) = *end; if (left == right) /* possible if {beg, end} is exactly equal to an existing range in map[] */ return; do { *left++ = *right; } while ((right++)->addr); /* note that replacing atleast 2 existing ranges with {begin, end} into one union range will cause a reduction * in the number of ranges in map[] effectively causing higher-valued ranges on the right to shift left. * In that case, we have to be also left-shift the null-valued mstr.addr, hence the ++ is done in the check * for (right++)->addr in the "while" above instead of having "*left++ = *right++" in the loop. */ return; } fis-gtm-V7.0-005/sr_port/global_map.h0000755000032200000250000000111414342376331016311 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GLOBAL_MAP_INCLUDED #define GLOBAL_MAP_INCLUDED void global_map(mstr map[], mstr *beg, mstr *end); #endif /* GLOBAL_MAP_INCLUDED */ fis-gtm-V7.0-005/sr_port/glvn.c0000755000032200000250000000312114342376331015155 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "compiler.h" #include "opcode.h" #include "toktyp.h" #include "mdq.h" #include "fullbool.h" error_def(ERR_VAREXPECTED); int glvn(oprtype *a) { oprtype x1; save_se save_state; triple *oldchain, *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; switch (TREF(window_token)) { case TK_IDENT: if (!lvn(a,OC_GETINDX,0)) return FALSE; return TRUE; case TK_CIRCUMFLEX: if (!gvn()) return FALSE; *a = put_tref(newtriple(OC_GVGET)); return TRUE; case TK_ATSIGN: if (SHIFT_SIDE_EFFECTS) { START_GVBIND_CHAIN(&save_state, oldchain); if (!indirection(&x1)) { setcurtchain(oldchain); return FALSE; } ref = newtriple(OC_INDGLVN); PLACE_GVBIND_CHAIN(&save_state, oldchain); } else { if (!indirection(&x1)) return FALSE; ref = newtriple(OC_INDGLVN); } if (TREF(expr_depth)) (TREF(side_effect_base))[TREF(expr_depth)] = (OLD_SE != TREF(side_effect_handling)); ref->operand[0] = x1; *a = put_tref(ref); return TRUE; default: stx_error(ERR_VAREXPECTED); return FALSE; } } fis-gtm-V7.0-005/sr_port/glvn_pool.c0000644000032200000250000000620214342376333016210 0ustar librarygtc/**************************************************************** * * * Copyright 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include #include "gtm_stdio.h" #include "min_max.h" #include "lv_val.h" #include #include "mv_stent.h" #include "compiler.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "opcode.h" #include "glvn_pool.h" #include "parm_pool.h" /* for CAPACITY_ROUND_UP2 macro */ void glvn_pool_init(void) { glvn_pool *pool; uint4 capacity, mval_capacity, slotoff; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; capacity = INIT_GLVN_POOL_CAPACITY; mval_capacity = INIT_GLVN_POOL_MVAL_CAPACITY; slotoff = (uint4)OFFSETOF(glvn_pool, slot[0]); pool = (glvn_pool *)malloc(ROUND_UP(slotoff, (capacity + 1) * SIZEOF(glvn_pool_entry))); pool->mval_stack = (mval *)malloc(mval_capacity * SIZEOF(mval)); pool->capacity = capacity; pool->top = 0; pool->mval_capacity = mval_capacity; pool->mval_top = 0; memset(pool->for_slot, (int)GLVN_POOL_EMPTY, (MAX_FOR_STACK + 1) * SIZEOF(uint4)); TREF(glvn_pool_ptr) = pool; } void glvn_pool_expand_slots(void) { glvn_pool *pool, *old_pool; uint4 capacity, slotoff; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; old_pool = TREF(glvn_pool_ptr); capacity = 2 * old_pool->capacity; assert(capacity <= MAX_EXPECTED_CAPACITY); /* Don't expect more than this in the test system */ slotoff = (uint4)OFFSETOF(glvn_pool, slot[0]); pool = (glvn_pool *)malloc(ROUND_UP(slotoff, (capacity + 1) * SIZEOF(glvn_pool_entry))); memcpy(pool, old_pool, slotoff + old_pool->top * SIZEOF(glvn_pool_entry)); pool->capacity = capacity; TREF(glvn_pool_ptr) = pool; free(old_pool); } void glvn_pool_expand_mvals(void) { glvn_pool *pool; glvn_pool_entry *slot, *top; int i, n; mval *mval_stack, *old_mval_stack; uint4 mval_capacity; INTPTR_T shift; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; pool = TREF(glvn_pool_ptr); mval_capacity = 2 * pool->mval_capacity; assert(mval_capacity <= MAX_EXPECTED_MVAL_CAPACITY); /* Don't expect more than this in the test system */ old_mval_stack = pool->mval_stack; mval_stack = (mval *)malloc(mval_capacity * SIZEOF(mval)); memcpy(mval_stack, old_mval_stack, pool->mval_top * SIZEOF(mval)); shift = (INTPTR_T)mval_stack - (INTPTR_T)old_mval_stack; for (slot = pool->slot, top = slot + pool->top - 1; slot < top; slot++) { /* Touch up glvn_info pointers, but leave lvn start alone */ n = slot->glvn_info.n; assert(n <= MAX_ACTUALS); if (FIRST_SAVED_ARG(slot)) slot->lvname = (mval *)(shift + (char *)slot->lvname); for (i = FIRST_SAVED_ARG(slot); i < n; i++) slot->glvn_info.arg[i] = (void *)(shift + (char *)slot->glvn_info.arg[i]); } pool->mval_stack = mval_stack; pool->mval_capacity = mval_capacity; free(old_mval_stack); } fis-gtm-V7.0-005/sr_port/glvn_pool.h0000644000032200000250000001557714342376331016232 0ustar librarygtc/**************************************************************** * * * Copyright 2012, 2013 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. * * * ****************************************************************/ #ifndef GLVN_POOL_H_INCLUDED #define GLVN_POOL_H_INCLUDED #include "callg.h" #include "compiler.h" #include "lv_val.h" /* Here we provide tools for saving identifying information of local or global variables. For local variables this includes * the variable name itself, as well as copies of each subscript. For global variables we save each argument that needs to * be later passed into op_gvname/op_gvnaked/op_gvextnam. Note that deferring the op_gvnaked call allows us to achieve * correct naked indicator flow. For example, in the command SET @"^(subs)"=rhs, the ^() needs to be evaluated relative to * the expression on the right hand side, and the right hand side needs to happen AFTER the subscripts have been evaluated. * The structure involved - the "glvn pool" - consists of a stack of entries, each corresponding to a single glvn, and * a parallel stack of mvals, each corresponding to some parent entry. Both stacks are expandable, doubling in size whenever * they run out of space. The glvn pool entries can be popped in three different ways. For SET, it's convenient to save the * glvn pool state, i.e. the top of the array of still-relevant entries. After completion of the set, the pool is restored to * this state and younger entries popped. With FOR, it's more convenient defer popping until another FOR loop at the same * nesting level starts. At that time the previously used FOR slot is recycled and everything younger than it is popped. * Finally, when a non-indirect frame is unwound, all younger pool entries are popped. */ #define ANY_SLOT 0 #define FIRST_SAVED_ARG(SLOT) ((OC_SAVLVN == (SLOT)->sav_opcode) ? 1 : 0) /* To avoid breaking gtmpcat, we retain the 'for_ctrl_stack' field of the frame_pointer struct instead of replacing it * with a uint4 'glvn_indx' field. The macros manipulate this field. Also for the convenience of gtmpcat: we declare the * glvn_pool and glvn_pool_entry structs in compiler.h instead of here. This is because gtmpcat does not know to include * glvn_pool.h when going through gtm_threadgbl_defs.h. * When gtmpcat is changed to accommodate a new name, these macros and the stack frame struct need to be changed, * and the glvn_pool structs in compiler.h should probably be moved into this file. */ #define GLVN_INDX(FP) ((uint4)(UINTPTR_T)((FP)->for_ctrl_stack)) #define GLVN_POOL_EMPTY ((uint4)-1) /* this must be an "impossible" value for a real pool */ #define GLVN_POOL_SLOTS_AVAILABLE ((TREF(glvn_pool_ptr))->capacity - (TREF(glvn_pool_ptr))->top) #define GLVN_POOL_MVALS_AVAILABLE ((TREF(glvn_pool_ptr))->mval_capacity - (TREF(glvn_pool_ptr))->mval_top) #define INIT_GLVN_POOL_CAPACITY 8 #define INIT_GLVN_POOL_MVAL_CAPACITY 64 #define GLVN_POOL_UNTOUCHED 0 #define MAX_EXPECTED_CAPACITY 2048 #define MAX_EXPECTED_MVAL_CAPACITY 2048 #define SLOT_NEEDS_REWIND(INDX) (((TREF(glvn_pool_ptr))->top <= (INDX)) && (GLVN_POOL_EMPTY != (INDX))) #define SLOT_OPCODE(INDX) ((TREF(glvn_pool_ptr))->slot[INDX].sav_opcode) #define SET_GLVN_INDX(FP, VALUE) \ { \ (FP)->for_ctrl_stack = (unsigned char *)(UINTPTR_T)(VALUE); \ } #define GLVN_POOL_EXPAND_IF_NEEDED \ { \ if (!TREF(glvn_pool_ptr)) \ glvn_pool_init(); \ else if (!GLVN_POOL_SLOTS_AVAILABLE) \ glvn_pool_expand_slots(); \ } #define ENSURE_GLVN_POOL_SPACE(SPC) \ { \ if (GLVN_POOL_MVALS_AVAILABLE < (uint4)(SPC)) \ { \ glvn_pool_expand_mvals(); \ assert(GLVN_POOL_MVALS_AVAILABLE >= (uint4)(SPC)); \ } \ } #define GET_GLVN_POOL_STATE(SLOT, M) \ { /* Find current available SLOT and mval. */ \ uint4 INDX, MVAL_INDX; \ \ INDX = (TREF(glvn_pool_ptr))->share_slot; \ SLOT = &(TREF(glvn_pool_ptr))->slot[INDX]; \ MVAL_INDX = SLOT->mval_top; \ M = &(TREF(glvn_pool_ptr))->mval_stack[MVAL_INDX]; \ } #define DRAIN_GLVN_POOL_IF_NEEDED \ { \ int I; \ uint4 INDX, FINDX; \ glvn_pool_entry *SLOT; \ \ if ((GLVN_POOL_UNTOUCHED != GLVN_INDX(frame_pointer)) && !(frame_pointer->flags & SFF_INDCE)) \ { /* Someone used an ugly FOR control variable or did an indirect set. */ \ INDX = (GLVN_POOL_EMPTY != GLVN_INDX(frame_pointer)) ? GLVN_INDX(frame_pointer) : 0; \ op_glvnpop(INDX); \ for (I = 1; I <= MAX_FOR_STACK; I++) \ { /* rewind the for_slot array */ \ FINDX = (TREF(glvn_pool_ptr))->for_slot[I]; \ if (SLOT_NEEDS_REWIND(FINDX)) \ { /* reset to precursor */ \ SLOT = &(TREF(glvn_pool_ptr))->slot[FINDX]; \ assert(!SLOT_NEEDS_REWIND(SLOT->precursor)); \ (TREF(glvn_pool_ptr))->for_slot[I] = SLOT->precursor; \ } else /* no higher FOR levels were used by current frame */ \ break; \ } \ } \ } #define INSERT_INDSAVGLVN(CTRL, V, RECYCLE, DO_REF) \ { \ triple *PUSH, *SAV, *PAR; \ \ PUSH = newtriple(OC_GLVNSLOT); \ PUSH->operand[0] = put_ilit((mint)(RECYCLE)); \ CTRL = put_tref(PUSH); \ SAV = newtriple(OC_INDSAVGLVN); \ SAV->operand[0] = V; \ PAR = newtriple(OC_PARAMETER); \ SAV->operand[1] = put_tref(PAR); \ PAR->operand[0] = CTRL; \ PAR->operand[1] = put_ilit((mint)(DO_REF)); /* flag to suppress global reference here */ \ } void glvn_pool_init(void); /* invoked via GLVN_POOL_EXPAND_IF_NEEDED macro */ void glvn_pool_expand_slots(void); /* invoked via GLVN_POOL_EXPAND_IF_NEEDED macro */ void glvn_pool_expand_mvals(void); /* invoked via ENSURE_GLVN_POOL_SPACE macro */ void op_glvnpop(uint4 indx); /* Used by [SET and $ORDER()] */ uint4 op_glvnslot(uint4 recycle); /* Used by [FOR, SET and $ORDER()] */ void op_indsavglvn(mval *target, uint4 slot, uint4 do_ref); /* Used by [SET and $ORDER()] */ void op_indsavlvn(mval *target, uint4 slot); /* Used by [FOR] */ void op_rfrshgvn(uint4 indx, opctype oc); /* Used by [SET and $ORDER()] */ lv_val *op_rfrshlvn(uint4 indx, opctype oc); /* Used by [FOR, SET and $ORDER()] */ void op_savgvn(UNIX_ONLY_COMMA(int argcnt) int hash_code_dummy, mval *val_arg, ...); /* Used by [SET and $ORDER()] */ void op_savlvn(UNIX_ONLY_COMMA(int argcnt) lv_val *start, ...); /* Used by [FOR, SET and $ORDER()] */ void op_shareslot(uint4 indx, opctype opcode); /* Used by [FOR, SET and $ORDER()] */ void op_stoglvn(uint4 indx, mval *value); /* Used by [SET] */ #endif /* GLVN_POOL_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/go.mpt0000755000032200000250000000566414342376331015210 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 1987-2018 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %GO ;Extracts global data to standard global output (GO) file ;Invoke ^%GO to get interaction ;possible enhancements: ;selection by key list, range and/or wildcard, rather than global name ;callable entry point ; w !,"Global Output Utility",! i '$d(%zdebug) n $et s $et="zg "_$zl_":ERR^%GO" u $p:(ctrap=$c(3):exc="zg "_$zl_":EXIT^%GO") n g,gn,m,n,%ZD,%ZG,%ZH,fmt,fmtdone d ^%GSEL i %ZG=0 w !,"No globals selected" q r !,"Header Label: ",%ZH,! r !,"Output Format: GO or ZWR: ",fmt,! i (fmt="")!($e("ZWR",1,$l(fmt))=$tr(fmt,"zwr","ZWR")) s fmt=1 e s fmt=0 f d q:$l(%ZD) . r !,"Output device: : ",%ZD,! . i '$l(%ZD) s %ZD=$p q . i %ZD="^" q . i %ZD="?" d q . . w !!,"Select the device you want for output" . . w !,"If you wish to exit enter a carat (^)",! . . s %ZD="" . i $zparse(%ZD)="" w " no such device" s %ZD="" q . o %ZD:(newversion:block=2048:record=2044:exception="g noopen"):0 . i '$t w !,%ZD," is not available" s %ZD="" q . q noopen . w !,$p($ZS,",",2,999),! c %ZD s %ZD="" q:%ZD="^" w !! i '$l(%ZH) s %ZH="%GO Global Output Utility" u %ZD w %ZH w:"M"'=$ZCHSET " ",$ZCHSET w !,"GT.M ",$zd($h,"DD-MON-YEAR 24:60:SS") w:fmt " ZWR" w ! s gn="",(m,n)=0 f s gn=$o(%ZG(gn)) q:gn="" s g=gn d . s fmtdone=0 . u $p w:$x>70 ! w gn,?$x\10+1*10 u %ZD i $p=%ZD w ! . s m=m+1 . i $d(@g)'[0 d s n=n+1 . . i fmt zwr @g s fmtdone=1 . . e w g,!,@g,! . f s g=$q(@g) q:g="" d . . i fmt zwr:'fmtdone @g ; don't zwr if already done for unsubscripted global. It takes care for subscripts too . . e w g,!,@g,! . . s n=n+1 u %ZD w !! u $p w !!,"Total of ",n," node",$s(n=1:"",1:"s") w " in ",m," global",$s(m=1:".",1:"s."),!! c:%ZD'=$p %ZD u $p:(ctrap="":exc="") q fw(s) ; variables used in this function are: fwlen, s, cc, fastate, isctl, i, thistime ; initialize this procedure s fwlen=$l(s) i fwlen=0 w ! q i s=+s w s,! q s cc=$e(s) i cc?1C w "$C(",$a(cc) s fastate=2 e w """",cc w:cc="""" cc s fastate=1 ; start the loop to deal with the whole string. f i=2:1:fwlen s cc=$e(s,i,i),isctl=cc?1C d . s thistime=1 . if fastate=1 d . . if (isctl) w """_$C(",$a(cc) s fastate=2,thistime=0 . . else w cc w:cc="""" cc . if (fastate=2)&thistime d . . if (isctl)!(cc="""") w ",",$a(cc) . . else w ")_""",cc s fastate=1 if fastate=1 w """",! else w ")",! q ; ERR u $p w !,$p($zs,",",2,99),! ; Warning - Fall-though s $ec="" EXIT i $d(%ZD),%ZD'=$p c %ZD u $p:(ctrap="":exc="") q fis-gtm-V7.0-005/sr_port/goerrorframe.c0000644000032200000250000000333114342376333016703 0ustar librarygtc/**************************************************************** * * * 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 "gtm_stdio.h" #include #include "stack_frame.h" #include "tp_frame.h" #include "golevel.h" #include "error.h" #include "error_trap.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 "gtm_trigger.h" #endif GBLREF stack_frame *frame_pointer; GBLREF stack_frame *error_frame; void goerrorframe() { stack_frame *fp, *fpprev; int4 unwind; for (unwind = 0, fp = frame_pointer; fp < error_frame; fp = fpprev) { fpprev = fp->old_frame_pointer; # ifdef GTM_TRIGGER if (SFT_TRIGR & fpprev->type) { fpprev = *(stack_frame **)(fpprev + 1); unwind++; /* Skipping over trigger frame but it needs unwinding too */ } # endif unwind++; assert(fpprev); } assert(fp == error_frame); DBGEHND((stderr, "goerrorframe: Unwinding %d frames\n", unwind)); GOFRAMES(unwind, FALSE, FALSE); assert(error_frame == frame_pointer); /* Now that we (the caller mdb_condition_handler) are going to rethrow an error, ensure that the * SFF_ETRAP_ERR bit is set in "error_frame" in case it got reset by flush_jmp. */ SET_ERROR_FRAME(error_frame); assert(error_frame->flags & SFF_ETRAP_ERR); return; } fis-gtm-V7.0-005/sr_port/goframes.c0000644000032200000250000000725414342376333016024 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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 "gtm_stdio.h" #include /* needed for golevel.h */ #include "error.h" #include "op.h" #include "stack_frame.h" /* needed for golevel.h */ #include "tp_frame.h" /* needed for golevel.h */ #include "golevel.h" #ifdef GTM_TRIGGER #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 "get_ret_targ.h" GBLREF boolean_t goframes_unwound_trigger; #endif GBLREF mval *alias_retarg; GBLREF stack_frame *frame_pointer; GBLREF boolean_t skip_error_ret; GBLREF tp_frame *tp_pointer; LITREF mval literal_null; #ifdef GTM_TRIGGER void goframes(int4 frames, boolean_t unwtrigrframe, boolean_t fromzgoto) #else void goframes(int4 frames) #endif { mval *ret_targ; GTMTRIG_ONLY(goframes_unwound_trigger = FALSE); for (ret_targ = NULL; frames--; ) { while (tp_pointer && tp_pointer->fp <= frame_pointer) { OP_TROLLBACK(-1); } if (0 == frames) { ret_targ = (mval *)get_ret_targ(NULL); /* If alias_retarg is non-NULL, *ret_targ would have been already initialized so no need to set it. * Setting it to literal_null in that case would cause reference counts to not be decremented later * in op_unwind/mdb_condition_handler so it is actually necessary to skip it in that case. */ if ((NULL != ret_targ) && (NULL == alias_retarg)) { *ret_targ = literal_null; ret_targ->mvtype |= MV_RETARG; } } skip_error_ret = TRUE; # ifdef GTM_TRIGGER if (!(SFT_TRIGR & frame_pointer->type)) { /* Normal frame unwind */ DBGTRIGR((stderr, "goframes: unwinding regular frame at %016lx\n", frame_pointer)); op_unwind(); DBGTRIGR((stderr, "goframes: after regular frame unwind: frame_pointer 0x%016lx ctxt value: 0x%016lx\n", frame_pointer, ctxt)); } else { /* Trigger base frame unwind (special case) */ DBGTRIGR((stderr, "goframes: unwinding trigger base frame at %016lx\n", frame_pointer)); gtm_trigger_fini(TRUE, fromzgoto); goframes_unwound_trigger = TRUE; } # else /* If triggers are not enabled, just a normal unwind */ DBGEHND((stderr, "goframes: unwinding regular frame at %016lx\n", frame_pointer)); op_unwind(); # endif assert(FALSE == skip_error_ret); /* op_unwind() should have read and reset this */ skip_error_ret = FALSE; /* be safe in PRO versions */ } # ifdef GTM_TRIGGER if (unwtrigrframe && (SFT_TRIGR & frame_pointer->type)) { /* If we landed on a trigger base frame after unwinding everything, we are in the same boat as if we had run into * one while we were unwinding. We cannot return this frame to (for example) zgoto which is going to morph it into * something else (unwtrigrframe only set when ZGOTO with entryref specified). So if the flag says we should never * land on a trigger frame, go ahead and unwind that one too. */ DBGTRIGR((stderr, "goframes: unwinding trailing trigger base frame at %016lx\n", frame_pointer)); gtm_trigger_fini(TRUE, fromzgoto); goframes_unwound_trigger = TRUE; } # endif return; } fis-gtm-V7.0-005/sr_port/golevel.h0000755000032200000250000000355414342376331015663 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GOLEVEL_H_INCLUDED #define GOLEVEL_H_INCLUDED /* golevel() will unwind up to the counted frame corresponding to level specified by the parm. The first flag indicates * whether or not it is ok to land on a $ZINTERRUPT frame or not (which it is not ok when unwinding a $ZINTERRUPT). Also, * In a trigger environment, an additional flag that determines if landing on a trigger frame means the unwind should * continue or not is supplied. This flag is passed to goframes() which does the actual unwind of a specific number of * rames (counted and uncounted). Note goframes is used by both golevel and goerrorframe. */ #ifdef GTM_TRIGGER #define GOLEVEL(level, unwtrigrframe) golevel(level, unwtrigrframe) #define GOFRAMES(frames, unwtrigrframe, fromzgoto) goframes(frames, unwtrigrframe, fromzgoto) void golevel(int4 level, boolean_t unwtrigrframe); void goframes(int4 frames, boolean_t unwtrigrframe, boolean_t fromzgoto); #else #define GOLEVEL(level, unwtrigrframe) golevel(level) #define GOFRAMES(frames, unwtrigrframe, fromzgoto) goframes(frames) void golevel(int4 level); /* unwind upto the counted frame corresponding to frame level "level" */ void goframes(int4 frames); /* unwind "frames" number of frames */ #endif void goerrorframe(void); /* unwind upto (but not including) the frame pointed to by the "error_frame" * global */ #endif fis-gtm-V7.0-005/sr_port/gsel.mpt0000755000032200000250000001433214342376331015525 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 1987-2018 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %GSEL ;GT.M %GSEL utility - global select into a local array ;invoke ^%GSEL to create %ZG - a local array of existing globals, interactively ; NEW add,beg,cnt,d,end,g,gd,gdf,k,out,pat,stp,nfe DO init,main USE:$DATA(d("use")) @d("use") USE:$DATA(d("io")) d("io") QUIT GD ; NEW add,beg,cnt,d,end,g,gd,gdf,k,out,pat,stp,nfe SET cnt=0,(out,gd,gdf)=1 DO main IF gdf SET %ZG="*" DO setup,it WRITE !,"Total of ",cnt," global",$SELECT(cnt=1:".",1:"s."),! USE:$DATA(d("use")) @d("use") USE:$DATA(d("io")) d("io") QUIT CALL ;invoke %GSEL without clearing %ZG (%ZG stores the list of globals from %GSEL searches) NEW add,beg,cnt,d,end,g,gd,gdf,k,out,pat,stp,nfe SET (cnt,gd)=0 IF $DATA(%ZG)>1 SET g="" FOR SET g=$ORDER(%ZG(g)) QUIT:'$LENGTH(g) SET cnt=cnt+1 IF $GET(%ZG)'?.N SET out=0 DO setup,it SET %ZG=cnt QUIT SET out=1 DO main USE:$DATA(d("use")) @d("use") USE:$DATA(d("io")) d("io") QUIT init ; KILL %ZG SET (cnt,gd)=0,out=1 QUIT main ; SET d("io")=$IO if '$DATA(%zdebug) NEW $ETRAP SET $ETRAP="ZGOTO "_$ZLEVEL_":ERR^"_$TEXT(+0) DO . NEW x . ZSHOW "d":d ; save original $p settings . SET x=$PIECE($PIECE(d("D",1),"CTRA=",2)," ") . SET:""=x x="""""" . SET d("use")="$PRINCIPAL:(CTRAP="_x_":EXCEPTION=",x=$PIECE(d("D",1),"EXCE=",2),x=$ZWRITE($EXTRACT(x,2,$LENGTH(x)-1)) . SET:""=x x="""""" . SET d("use")=d("use")_x_":"_$SELECT($FIND(d("D",1),"NOCENE"):"NOCENABLE",1:"CENABLE")_")" . USE $PRINCIPAL:(CTRAP=$CHAR(3,4):EXCEPTION="":NOCENABLE) FOR DO inter QUIT:'$LENGTH(%ZG) SET %ZG=cnt QUIT inter ; SET nfe=0 READ !,"Global ^",%ZG,! QUIT:'$LENGTH(%ZG) IF $EXTRACT(%ZG)="?",$LENGTH(%ZG)=1 DO help QUIT IF (%ZG="?D")!(%ZG="?d") DO cur QUIT IF $EXTRACT(%ZG)="?" SET nfe=1 DO nonfatal QUIT DO setup IF nfe>0 SET nfe=0,gdf=0 QUIT DO it WRITE !,$SELECT(gd:"T",1:"Current t"),"otal of ",cnt," global",$SELECT(cnt=1:".",1:"s."),! QUIT setup ;Handles the base case of no range NEW g1,et IF gd SET add=1,cnt=0,g=%ZG KILL %ZG SET %ZG=g ELSE IF "'-"[$EXTRACT(%ZG) SET add=0,g=$EXTRACT(%ZG,2,999) ELSE SET add=1,g=%ZG SET g1=$TRANSLATE(g,"?%*","aaa") ;Substitute wildcards for valid characters DO . SET et=$ETRAP NEW $ETRAP,$ESTACK SET $ETRAP="SET $ECODE="""",$ETRAP=et DO setup2",g1=$QSUBSCRIPT(g1,1) . SET:$FIND(g,"(")'=0 $EXTRACT(g,$FIND(g,"(")-1,$LENGTH(g))="" . SET:$EXTRACT(g)="^" $EXTRACT(g)="" IF "?"=$EXTRACT(g,$FIND(g,":")) SET nfe=1 DO nonfatal QUIT SET g=$TRANSLATE(g,"?","%"),beg=$PIECE(g,":",1),end=$PIECE(g,":",2) IF end=beg SET end="" QUIT setup2 ;Handles the case of a range argument NEW p,q,x,di,beg1 SET p=$LENGTH(g1,":") IF p<2 SET nfe=2 DO nonfatal QUIT ELSE IF p>2 SET q=$LENGTH(g1,"""")-1 FOR x=2:2:q SET $PIECE(g1,"""",x)=$TRANSLATE($PIECE(g1,"""",x),":","a") DO . SET beg=$PIECE(g1,":",1),end=$PIECE(g1,":",2) . NEW $ETRAP,$ESTACK SET $ETRAP="SET nfe=2 DO nonfatal",beg1=$QSUBSCRIPT(beg,0),end=$QSUBSCRIPT(end,0) QUIT:nfe>0 SET di=$LENGTH(beg),beg=$EXTRACT(g,1,di),end=$EXTRACT(g,di+2,$LENGTH(g)) SET:$FIND(beg,"(")'=0 $EXTRACT(beg,$FIND(beg,"(")-1,$LENGTH(beg))="" SET:$EXTRACT(beg)="^" $EXTRACT(beg)="" SET:$FIND(end,"(")'=0 $EXTRACT(end,$FIND(end,"(")-1,$LENGTH(end))="" SET:$EXTRACT(end)="^" $EXTRACT(end)="" SET g=beg_":"_end QUIT it ; SET gdf=0 IF end'?."*",end']beg QUIT SET g=beg DO pat IF pat["""" DO start FOR DO search QUIT:'$LENGTH(g) DO save IF pat["""",'$LENGTH(end) QUIT SET beg=stp SET:'$LENGTH(g) g=stp SET pat=".E",stp="^"_$EXTRACT(end)_$TRANSLATE($EXTRACT(end,2,9999),"%","z") DO start FOR DO search QUIT:'$LENGTH(g) DO save SET g=end DO pat IF pat["""" SET:beg]g g=beg DO start FOR DO search QUIT:'$LENGTH(g) DO save QUIT pat ; NEW tmpstp SET:"%"=$EXTRACT(g) g="!"_$EXTRACT(g,2,9999) SET pat=g FOR QUIT:$LENGTH(g,"%")<2 DO .SET g=$PIECE(g,"%",1)_"#"_$PIECE(g,"%",2,999),pat=$PIECE(pat,"%",1)_"""1E1"""_$PIECE(pat,"%",2,999) FOR QUIT:$LENGTH(g,"*")<2 DO .SET g=$PIECE(g,"*",1)_"$"_$PIECE(g,"*",2,999),pat=$PIECE(pat,"*",1)_""".E1"""_$PIECE(pat,"*",2,999) SET:"!"=$EXTRACT(g) g="%"_$EXTRACT(g,2,9999),pat="%"_$EXTRACT(pat,2,9999) IF pat["""" SET pat="1""^"_pat_"""" SET tmpstp="z",$PIECE(tmpstp,"z",30)="z" SET g="^"_$PIECE($PIECE(g,"#"),"$"),stp=g_$EXTRACT(tmpstp,$LENGTH(g)-1,31) QUIT start ; SET:"^"=g g="^%" IF g?@pat,$DATA(@g) DO save QUIT search ; FOR SET g=$ORDER(@g) SET:g]stp g="" QUIT:g?@pat!'$LENGTH(g) QUIT save ; IF add,'$DATA(%ZG(g)) SET %ZG(g)="",cnt=cnt+1 DO prt:out IF 'add,$DATA(%ZG(g)) KILL %ZG(g) SET cnt=cnt-1 DO prt:out QUIT prt ; WRITE:$X>70 ! WRITE g,?$X\10+1*10 QUIT help ; WRITE !,?2,"",?25,"to leave",!,?2,"""*""",?25,"for all" WRITE !,?2,"global",?25,"for 1 global" WRITE !,?2,"global1:global2",?25,"for a range" WRITE !,?2,"""*"" as a wildcard",?25,"permitting any number of characters" WRITE !,?2,"""%"" as a wildcard",?25,"for a single character in positions other than the first" WRITE !,?2,"""?"" as a wildcard",?25,"for a single character in positions other than the first" QUIT:gd WRITE !,?2,"""'"" as the 1st character",!,?25,"to remove globals from the list" WRITE !,?2,"?D",?25,"for the currently selected globals",! QUIT cur ; SET g="" FOR SET g=$ORDER(%ZG(g)) QUIT:'$LENGTH(g) WRITE:$X>70 ! WRITE g,?($X\10+1*10) WRITE !,$SELECT(gd:"T",1:"Current t"),"otal of ",cnt," global",$SELECT(cnt=1:".",1:"s."),! QUIT nonfatal NEW ecde SET $ECODE="" IF nfe=1 SET ecde="U257" ELSE IF nfe=2 SET ecde="U258" WRITE $TEXT(+0),@$PIECE($TEXT(@ecde),";",2),! QUIT ERR ; USE:$DATA(d("use")) @d("use") USE:$DATA(d("io")) d("io") SET $ECODE="" QUIT LOOP DO main USE $PRINCIPAL:(ctrap=$CHAR(3,4):exc="") QUIT ; Error message texts U257 ;"-E-ILLEGALUSE Illegal use of ""?"". Only valid as 1st character when ""?D"" or ""?d""" U258 ;"-E-INVALIDGBL Search string either uses invalid characters or is improperly formated" fis-gtm-V7.0-005/sr_port/gtm_assert.c0000755000032200000250000000314614342376331016366 0ustar librarygtc/**************************************************************** * * * 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. * * * ****************************************************************/ /* gtm_assert - invoked via the "GTMASSERT" macro. * * gtm_assert raises the ERR_GTMASSERT error condition which is * intended to be a replacement for the ubiquitous ERR_GTMCHECK. BYPASSOK * It differs from ERR_GTMCHECK in that it indicates the module BYPASSOK * name and line number of its invocation so one can determine * exactly which ERR_GTMASSERT caused the termination. * * The "GTMASSERT" macro differs from the "assert" macro in that * it is significant regardless of the definition or lack thereof * of the macro "DEBUG" and is therefore valid for PRO images as * well as for DBG and BTA images. * * Note that the assertpro() macro is preferred in most instances where a specific condition * is being tested. */ #include "mdef.h" #include "send_msg.h" LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; error_def(ERR_GTMASSERT); void gtm_assert(int file_name_len, char file_name[], int line_no) { send_msg (VARLSTCNT(7) ERR_GTMASSERT, 5, gtm_release_name_len, gtm_release_name, file_name_len, file_name, line_no); rts_error (VARLSTCNT(7) ERR_GTMASSERT, 5, gtm_release_name_len, gtm_release_name, file_name_len, file_name, line_no); } fis-gtm-V7.0-005/sr_port/gtm_assert2.c0000644000032200000250000000267014342376331016446 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ /* gtm_assert2 - invoked via the "assertpro" macro. * * gtm_assert2 is driven by the assertpro() macro which is intended to be a replacement for * most instances of the GTMASSERT macro which gave no indication of the cause of the GTMASSERT * error. The GTMASSERT2 message output from this routine also contains the failing assertpro() * text very similar to what the ASSERT error does. */ #include "mdef.h" #include "send_msg.h" LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; error_def(ERR_GTMASSERT2); int gtm_assert2(int condlen, char *condtext, int file_name_len, char file_name[], int line_no) { send_msg(VARLSTCNT(9) ERR_GTMASSERT2, 7, gtm_release_name_len, gtm_release_name, file_name_len, file_name, line_no, condlen, condtext); rts_error(VARLSTCNT(9) ERR_GTMASSERT2, 7, gtm_release_name_len, gtm_release_name, file_name_len, file_name, line_no, condlen, condtext); return 0; /* Required for assertpro() macro which assumes (syntactically) this rtn returns a result */ } fis-gtm-V7.0-005/sr_port/gtm_bintim.h0000755000032200000250000000111114342376331016342 0ustar librarygtc/**************************************************************** * * * Copyright 2003 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GTM_BINTIM_INCLUDED #define GTM_BINTIM_INCLUDED int gtm_bintim (char *s, jnl_proc_time *timep); #endif /* GTM_BINTIM_INCLUDED */ fis-gtm-V7.0-005/sr_port/gtm_byteswap_64.c0000755000032200000250000000157414342376331017237 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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" qw_num gtm_byteswap_64(qw_num num64) { #ifndef INT8_SUPPORTED qw_num swap_qw; uint32_t swap_uint32; QWASSIGN(swap_qw, num64); swap_uint32 = GTM_BYTESWAP_32(swap_qw.value[lsb_index]); swap_qw.value[lsb_index] = GTM_BYTESWAP_32(swap_qw.value[msb_index]); swap_qw.value[msb_index] = swap_uint32; return (swap_qw); #else GTMASSERT; /* should use GTM_BYTESWAP_64 macro, not gtm_byteswap_64 function */ return 0; #endif } fis-gtm-V7.0-005/sr_port/gtm_c_stack_trace.h0000644000032200000250000000504114342376331017650 0ustar librarygtc/**************************************************************** * * * Copyright 2009, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #ifndef GTM_C_STACK_TRACE_H #define GTM_C_STACK_TRACE_H #define ONCE 1 #define TWICE 2 #ifdef VMS #define GET_C_STACK_MULTIPLE_PIDS(MESSAGE, CNL_PID_ARRAY, MAX_PID_SLOTS, STUCK_CNT) #define GET_C_STACK_FROM_SCRIPT(MESSAGE, WAITINGPID, BLOCKINGPID, COUNT) #define GET_C_STACK_FOR_KIP(KIP_PIDS_ARR_PTR, TRYNUM, MAX_TRY, STUCK_CNT, MAX_PID_SLOTS) #elif defined(UNIX) void gtm_c_stack_trace(char *message, pid_t waiting_pid, pid_t blocking_pid, uint4 count); #define GET_C_STACK_MULTIPLE_PIDS(MESSAGE, CNL_PID_ARRAY, MAX_PID_SLOTS, STUCK_CNT) \ { \ uint4 index; \ pid_t pid; \ GBLREF uint4 process_id; \ \ for (index = 0; MAX_PID_SLOTS > index; index++) \ { \ pid = CNL_PID_ARRAY[index]; \ if (0 != pid) \ GET_C_STACK_FROM_SCRIPT(MESSAGE, process_id, pid, STUCK_CNT); \ } \ } #define GET_C_STACK_FROM_SCRIPT(MESSAGE, WAITINGPID, BLOCKINGPID, COUNT) \ { \ gtm_c_stack_trace(MESSAGE, WAITINGPID, BLOCKINGPID, COUNT); \ } #define GET_C_STACK_FOR_KIP(KIP_PIDS_ARR_PTR, TRYNUM, MAX_TRY, STUCK_CNT, MAX_PID_SLOTS) \ { \ boolean_t invoke_c_stack = FALSE; \ char *kip_wait_string = NULL; \ \ DEBUG_ONLY( \ /* If we had waited for half the max time, get a C stack trace on the processes currently \ * doing the kill \ */ \ if ((MAX_TRY / 2) == TRYNUM) \ { \ invoke_c_stack = TRUE; \ kip_wait_string = "KILL_IN_PROG_HALFWAIT"; \ } \ ) \ /* If we had waited for max time, get a C stack trace on the processes currently doing the kill \ * irrespective of whether it's pro or dbg \ */ \ if (MAX_TRY <= TRYNUM) \ { \ invoke_c_stack = TRUE; \ kip_wait_string = "KILL_IN_PROG_WAIT"; \ } \ if (invoke_c_stack) \ GET_C_STACK_MULTIPLE_PIDS(kip_wait_string, KIP_PIDS_ARR_PTR, MAX_PID_SLOTS, STUCK_CNT); \ } #else #error UNSUPPORTED PLATFORM #endif #endif fis-gtm-V7.0-005/sr_port/gtm_caseconv.h0000755000032200000250000000143114342376331016666 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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. * * * ****************************************************************/ #ifndef GTM_CASECONV_included #define GTM_CASECONV_included void lower_to_upper(uchar_ptr_t d, uchar_ptr_t s, int4 len); void upper_to_lower(uchar_ptr_t d, uchar_ptr_t s, int4 len); void str_to_title(uchar_ptr_t d, uchar_ptr_t s, int4 len); #endif /*GTM_CASECONV_included*/ fis-gtm-V7.0-005/sr_port/gtm_common_defs.h0000644000032200000250000001165514342376331017364 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2021 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. * * * ****************************************************************/ #ifndef GTM_COMMON_DEFS_H #define GTM_COMMON_DEFS_H #if defined(__ia64) || defined(__x86_64__) || defined(__sparc) || defined(__s390__) || defined (_AIX) # define GTM64 #endif #ifdef GTM64 # define GTM64_ONLY(X) X # define NON_GTM64_ONLY(X) #else # define GTM64_ONLY(X) # define NON_GTM64_ONLY(X) X #endif #ifndef __vms # define readonly # define GBLDEF # define GBLREF extern # define LITDEF const # define LITREF extern const # define error_def(x) LITREF int x #else # ifdef __cplusplus # define GBLDEF # define GBLREF extern # define LITDEF const # define LITREF extern const # else # define GBLDEF globaldef # define GBLREF globalref # define LITDEF const globaldef # define LITREF const globalref # endif #endif /* Use GBLDEF to define STATICDEF for variables and STATICFNDEF, STATICFNDCL for functions. Define STATICDEF to "GBLDEF". This way * we know such usages are intended to be "static" but yet can effectively debug these variables since they are externally * visible. For functions, do not use the "static" keyword to make them externally visible. Note that a STATICREF for variables * does not make sense since statics are supposed to be used only within one module. */ #define STATICDEF static #define STATICFNDCL static #define STATICFNDEF static #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif #ifndef NULL # define NULL ((void *) 0) #endif #if defined(__ia64) || defined(__MVS__) # define INTCAST(X) ((int)(X)) # define UINTCAST(X) ((uint4)(X)) # define STRLEN(X) ((int)(strlen(X))) # define USTRLEN(X) ((unsigned int)(strlen(X))) # define OFFSETOF(X,Y) ((int)(offsetof(X,Y))) #else # define INTCAST(X) X # define UINTCAST(X) X # define STRLEN(X) strlen(X) # define USTRLEN(X) strlen(X) # define OFFSETOF(X,Y) offsetof(X,Y) #endif #ifndef __vms # define DIR_SEPARATOR '/' # define UNALIAS "unalias -a; " #endif /* the LITERAL version of the macro should be used over STRING whenever possible for efficiency reasons */ #define STR_LIT_LEN(LITERAL) (SIZEOF(LITERAL) - 1) #define LITERAL_AND_LENGTH(LITERAL) (LITERAL), (SIZEOF(LITERAL) - 1) #define LENGTH_AND_LITERAL(LITERAL) (SIZEOF(LITERAL) - 1), (LITERAL) #define STRING_AND_LENGTH(STRING) (STRING), (STRLEN((char *)(STRING))) #define LENGTH_AND_STRING(STRING) (strlen((char *)(STRING))), (STRING) #define LEN_AND_LIT(LITERAL) LENGTH_AND_LITERAL(LITERAL) #define LIT_AND_LEN(LITERAL) LITERAL_AND_LENGTH(LITERAL) #define STR_AND_LEN(STRING) STRING_AND_LENGTH(STRING) #define LEN_AND_STR(STRING) LENGTH_AND_STRING(STRING) #define ARRAYSIZE(arr) SIZEOF(arr)/SIZEOF(arr[0]) /* # of elements defined in the array */ #define ARRAYTOP(arr) (&arr[0] + ARRAYSIZE(arr)) /* address of the TOP of the array (first byte AFTER * array limits).Use &arr[0] + size instead of * &arr[size] to avoid compiler warning. */ #define MEMCMP_LIT(SOURCE, LITERAL) memcmp(SOURCE, LITERAL, SIZEOF(LITERAL) - 1) #define MEMCPY_LIT(TARGET, LITERAL) memcpy(TARGET, LITERAL, SIZEOF(LITERAL) - 1) #define DIVIDE_ROUND_UP(VALUE, MODULUS) (((VALUE) + ((MODULUS) - 1)) / (MODULUS)) #define DIVIDE_ROUND_DOWN(VALUE, MODULUS) ((VALUE) / (MODULUS)) #define ROUND_UP(VALUE, MODULUS) (DIVIDE_ROUND_UP(VALUE, MODULUS) * (MODULUS)) #define ROUND_DOWN(VALUE, MODULUS) (DIVIDE_ROUND_DOWN(VALUE, MODULUS) * (MODULUS)) /* Macros to enable block macros to be used in any context taking a single statement. See MALLOC_* macros below for examples of use. * Note that if the macro block does a "break" or "continue" and expects it to transfer control to the calling context * (i.e. OUTSIDE the macro block because a for/while/switch exists outside the macro block), these macros should not be used. * This means that a "break" or "continue" inside a for/while/switch statement in the macro block which transfers control to * within the macro block is not an issue. */ #define MBSTART do #define MBEND while (FALSE) #define MALLOC_CPY_LIT(DST, SRC) \ MBSTART { \ char *mcs_ptr; \ int mcs_len; \ \ mcs_len = SIZEOF(SRC); \ mcs_ptr = malloc(mcs_len); \ memcpy(mcs_ptr, SRC, mcs_len); \ DST = mcs_ptr; \ } MBEND #define MALLOC_INIT(DST, SIZ) \ MBSTART { \ void *lcl_ptr; \ \ lcl_ptr = malloc(SIZ); \ memset(lcl_ptr, 0, SIZ); \ DST = lcl_ptr; \ } MBEND /* Shared between GT.M and external plugins */ #define EXT_NEW "_%YGTM" #endif /* GTM_COMMON_DEFS_H */ fis-gtm-V7.0-005/sr_port/gtm_connect.c0000755000032200000250000000350614342376331016516 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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. * * * ****************************************************************/ /* get socket routines address */ #include "mdef.h" #include "gtm_netdb.h" #include "gtm_unistd.h" #include #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_string.h" #include "gtm_select.h" int gtm_connect(int socket, struct sockaddr *address, size_t address_len) { int res, sockerror; GTM_SOCKLEN_TYPE sockerrorlen; fd_set writefds; res = connect(socket, address, (GTM_SOCKLEN_TYPE)address_len); if ((-1 == res) && ((EINTR == errno) || (EINPROGRESS == errno) #if (defined(__osf__) && defined(__alpha)) || defined(__sun) || defined(__vms) || (EWOULDBLOCK == errno) #endif )) {/* connection attempt will continue so wait for completion */ do { /* a plain connect will usually timeout after 75 seconds with ETIMEDOUT */ FD_ZERO(&writefds); FD_SET(socket, &writefds); res = select(socket + 1, NULL, &writefds, NULL, NULL); if (-1 == res && EINTR == errno) continue; if (0 < res) { /* check for socket error */ sockerrorlen = SIZEOF(sockerror); res = getsockopt(socket, SOL_SOCKET, SO_ERROR, &sockerror, &sockerrorlen); if (0 == res && 0 != sockerror) { /* return socket error */ res = -1; errno = sockerror; } } break; } while (TRUE); } else if (-1 == res && EISCONN == errno) res = 0; /* socket is already connected */ return(res); } fis-gtm-V7.0-005/sr_port/gtm_ctype.h0000755000032200000250000000706514342376331016222 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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. * * * ****************************************************************/ /* gtm_ctype.h - interlude to system header file. */ #ifndef GTM_CTYPEH #define GTM_CTYPEH #if defined(__osf__) && defined(__alpha) /* On Tru64, contains declarations of arrays of 64-bit pointers * in system library routines. The following pragma's are necessary * to ensure that references to those arrays declare them as 64-bit * pointer arrays, even if the including C program is compiled with * 32-bit pointer options. */ #pragma pointer_size (save) #pragma pointer_size (long) #endif #include /* The behavior of the system-defined ISxxxxx macros can vary based on the current locale and platform. * In the "C" locale, characters are classified according to the rules of the US-ASCII 7-bit coded character set. * In non-"C" locales (for example, UTF-8 mode) the result of ISXXXXX might not be what is expected * For example, ISALNUM(240) will return TRUE in UTF8 mode and FALSE in M mode (C locale) on Solaris. * Therefore define ISxxxxx_ASCII variant macros that additionally do check for ASCII. * Callers that need to check for a ISxxxxx property within the ASCII range should use the ISxxxxx_ASCII variants. * This makes the return value consistent across all platforms and independent of the locale. * We do not expect any callers of the non-ASCII macros in the GT.M codebase. */ #ifdef ISALNUM #undef ISALNUM #endif #define ISALNUM isalnum #define ISALNUM_ASCII(CH) (IS_ASCII(CH) && ISALNUM(CH)) #ifdef ISALPHA #undef ISALPHA #endif #define ISALPHA isalpha #define ISALPHA_ASCII(CH) (IS_ASCII(CH) && ISALPHA(CH)) #ifdef ISCNTRL #undef ISCNTRL #endif #define ISCNTRL iscntrl #define ISCNTRL_ASCII(CH) (IS_ASCII(CH) && ISCNTRL(CH)) #ifdef ISDIGIT #undef ISDIGIT #endif #define ISDIGIT isdigit #define ISDIGIT_ASCII(CH) (IS_ASCII(CH) && ISDIGIT(CH)) #ifdef ISGRAPH #undef ISGRAPH #endif #define ISGRAPH isgraph #define ISGRAPH_ASCII(CH) (IS_ASCII(CH) && ISGRAPH(CH)) #ifdef ISLOWER #undef ISLOWER #endif #define ISLOWER islower #define ISLOWER_ASCII(CH) (IS_ASCII(CH) && ISLOWER(CH)) #ifdef ISPRINT #undef ISPRINT #endif #define ISPRINT isprint #define ISPRINT_ASCII(CH) (IS_ASCII(CH) && ISPRINT(CH)) #ifdef ISPUNCT #undef ISPUNCT #endif #define ISPUNCT ispunct #define ISPUNCT_ASCII(CH) (IS_ASCII(CH) && ISPUNCT(CH)) #ifdef ISSPACE #undef ISSPACE #endif #define ISSPACE isspace #define ISSPACE_ASCII(CH) (IS_ASCII(CH) && ISSPACE(CH)) #ifdef ISUPPER #undef ISUPPER #endif #define ISUPPER isupper #define ISUPPER_ASCII(CH) (IS_ASCII(CH) && ISUPPER(CH)) #ifdef ISXDIGIT #undef ISXDIGIT #endif #define ISXDIGIT isxdigit #define ISXDIGIT_ASCII(CH) (IS_ASCII(CH) && ISXDIGIT(CH)) LITREF unsigned char lower_to_upper_table[]; LITREF unsigned char upper_to_lower_table[]; #ifdef TOLOWER #undef TOLOWER #endif /* this macro works only on lower-case ASCII characters and leaves others as-is */ #define TOLOWER(C) upper_to_lower_table[(unsigned char)C] #ifdef TOUPPER #undef TOUPPER #endif /* this macro works only on upper-case ASCII characters and leaves others as-is */ #define TOUPPER(C) lower_to_upper_table[(unsigned char)C] #if defined(__osf__) && defined(__alpha) #pragma pointer_size (restore) #endif #endif fis-gtm-V7.0-005/sr_port/gtm_dirent.h0000755000032200000250000000123114342376331016350 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /* gtm_dirent.h - interlude to system header file. */ #ifndef GTM_DIRENTH #define GTM_DIRENTH #include #define OPENDIR opendir #define READDIR(dir, rddr_res) (rddr_res = readdir(dir)) #endif fis-gtm-V7.0-005/sr_port/gtm_env_init.c0000755000032200000250000004507314342376331016705 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2004-2022 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_string.h" #include "gtm_stdlib.h" #include /* For offsetof macro */ #include "gtm_logicals.h" #include "gtm_multi_thread.h" #include "logical_truth_value.h" #include "trans_numeric.h" #include "trans_log_name.h" #include "gtmdbglvl.h" #include "iosp.h" #include "wbox_test_init.h" #include "gtm_env_init.h" /* for gtm_env_init() and gtm_env_init_sp() prototype */ #include "gt_timer.h" #include "io.h" #include "iosocketdef.h" #include "gtm_malloc.h" #include "cache.h" #include "gdsroot.h" /* needed for gdsfhead.h */ #include "gdskill.h" /* needed for gdsfhead.h */ #include "gdsbt.h" /* needed for gdsfhead.h */ #include "gdsfhead.h" /* needed for MAXTOTALBLKS_MAX macro */ #include "mvalconv.h" #include "fullbool.h" #include "trace_table.h" #include "parse_trctbl_groups.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdscc.h" #include "filestruct.h" #include "buddy_list.h" /* needed for tp.h */ #include "jnl.h" #include "tp.h" #include "cli.h" #include "getstorage.h" #ifdef DEBUG # define INITIAL_DEBUG_LEVEL GDL_Simple #else # define INITIAL_DEBUG_LEVEL GDL_None #endif #ifdef FULLBLOCKWRITES # define DEFAULT_FBW_FLAG 1 #else # define DEFAULT_FBW_FLAG 0 #endif #define SIZEOF_prombuf ggl_prombuf /* gtm_dirtree_collhdr_always is only used in dbg code and hence doesn't need checking in the D9I10002703 subtest * Hence this env var is not defined in gtm_logicals.h as that is what the D9I10002703 subtest looks at for a list of env vars. */ #define GTM_DIRTREE_COLLHDR_ALWAYS "$gtm_dirtree_collhdr_always" GBLREF boolean_t dollar_zquit_anyway; /* if TRUE compile QUITs to not care whether or not they're from an extrinsic */ GBLREF uint4 gtmDebugLevel; /* Debug level (0 = using default sm module so with a DEBUG build, even level 0 implies basic debugging) */ GBLREF boolean_t gtmSystemMalloc; /* Use the system's malloc() instead of our own */ GBLREF boolean_t certify_all_blocks; GBLREF uint4 gtm_blkupgrade_flag; /* controls whether dynamic block upgrade is attempted or not */ GBLREF boolean_t gtm_dbfilext_syslog_disable; /* control whether db file extension message is logged or not */ GBLREF uint4 gtm_max_sockets; /* Maximum sockets in a socket device that can be created by this process */ GBLREF bool undef_inhibit; GBLREF uint4 outOfMemoryMitigateSize; /* Reserve that we will freed to help cleanup if run out of memory */ GBLREF uint4 max_cache_memsize; /* Maximum bytes used for indirect cache object code */ GBLREF uint4 max_cache_entries; /* Maximum number of cached indirect compilations */ GBLREF block_id gtm_tp_allocation_clue; /* block# hint to start allocation for created blocks in TP */ GBLREF boolean_t gtm_stdxkill; /* Use M Standard exclusive kill instead of historical GTM */ GBLREF boolean_t ztrap_new; /* Each time $ZTRAP is set it is automatically NEW'd */ GBLREF size_t gtm_max_storalloc; /* Used for testing: creates an allocation barrier */ GBLREF boolean_t gtm_nofflf; /* Used to control "write #" behavior ref GTM-9136 */ GBLREF size_t zmalloclim; /* ISV memory warning of MALLOCCRIT in bytes */ GBLREF boolean_t malloccrit_issued; /* MEMORY error limit set at time of MALLOCCRIT */ GBLREF bool pin_shared_memory; /* pin shared memory into physical memory on creation */ GBLREF bool hugetlb_shm_enabled; /* allocate shared memory backed by huge pages */ void gtm_env_init(void) { boolean_t ret, is_defined; char buf[MAX_TRANS_NAME_LEN]; double time; int status2, i, j; int4 status; mstr val, trans; size_t tmp_malloc_limit; uint4 tdbglvl, tmsock, reservesize, memsize, cachent, trctblsize, trctblbytes; uint4 max_threads, max_procs; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (!TREF(gtm_env_init_started)) { TREF(gtm_env_init_started) = TRUE; /* See if a debug level has been specified. Do this first since gtmDebugLevel needs * to be initialized before any mallocs are done in the system. */ gtmDebugLevel = INITIAL_DEBUG_LEVEL; val.addr = GTM_DEBUG_LEVEL_ENVLOG; val.len = SIZEOF(GTM_DEBUG_LEVEL_ENVLOG) - 1; if (tdbglvl = trans_numeric(&val, &is_defined, TRUE)) /* Note assignment!! */ { /* Some kind of debugging was asked for.. */ tdbglvl |= GDL_Simple; /* Make sure simple debugging turned on if any is */ if ((GDL_SmChkFreeBackfill | GDL_SmChkAllocBackfill) & tdbglvl) tdbglvl |= GDL_SmBackfill; /* Can't check it unless it's filled in */ if (GDL_SmStorHog & tdbglvl) tdbglvl |= GDL_SmBackfill | GDL_SmChkAllocBackfill; gtmDebugLevel |= tdbglvl; gtmSystemMalloc = ((GDL_UseSystemMalloc & gtmDebugLevel) || FALSE); if (gtmSystemMalloc) gtmDebugLevel &= !GDL_SmAllMallocDebug; } /* gtm_pinshm environment/logical */ val.addr = GTM_PINSHM; val.len = SIZEOF(GTM_PINSHM) - 1; pin_shared_memory = logical_truth_value(&val, FALSE, &is_defined); /* gtm_hugepages environment/logical */ val.addr = GTM_HUGETLB_SHM; val.len = SIZEOF(GTM_HUGETLB_SHM) - 1; hugetlb_shm_enabled = logical_truth_value(&val, FALSE, &is_defined); /* gtm_boolean environment/logical */ val.addr = GTM_BOOLEAN; val.len = SIZEOF(GTM_BOOLEAN) - 1; TREF(gtm_fullbool) = trans_numeric(&val, &is_defined, TRUE); switch (TREF(gtm_fullbool)) { case GTM_BOOL: /* original GT.M short-circuit Boolean evaluation with naked maintenance */ case FULL_BOOL: /* standard behavior - evaluate everything with a side effect */ case FULL_BOOL_WARN: /* like FULL_BOOL but give compiler warnings when it makes a difference */ break; default: TREF(gtm_fullbool) = GTM_BOOL; } /* gtm_side_effects environment/logical */ val.addr = GTM_SIDE_EFFECT; val.len = SIZEOF(GTM_SIDE_EFFECT) - 1; TREF(side_effect_handling) = trans_numeric(&val, &is_defined, TRUE); switch (TREF(side_effect_handling)) { case OLD_SE: /* ignore side effect implications */ case STD_SE: /* reorder argument processing for left-to-right side effects */ case SE_WARN: /* like STD but give compiler warnings when it makes a difference */ break; default: TREF(side_effect_handling) = OLD_SE; /* default is: ignore side effect implications */ } if ((OLD_SE != TREF(side_effect_handling)) && (GTM_BOOL == TREF(gtm_fullbool))) /* side effect implies full bool */ TREF(gtm_fullbool) = FULL_BOOL; /* NOUNDEF environment/logical */ val.addr = GTM_NOUNDEF; val.len = SIZEOF(GTM_NOUNDEF) - 1; assert(FALSE == undef_inhibit); /* should have been set to FALSE at global variable definition time */ ret = logical_truth_value(&val, FALSE, &is_defined); if (is_defined) undef_inhibit = ret; /* if the logical is not defined, we want undef_inhibit to take its default value */ /* gtm_trace_gbl_name environment; it controls implicit MPROF testing */ val.addr = GTM_MPROF_TESTING; val.len = SIZEOF(GTM_MPROF_TESTING) - 1; if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &trans, buf, SIZEOF(buf), do_sendmsg_on_log2long))) { /* Note assignment above */ if (SIZEOF(buf) >= trans.len) { if (('0' == (char)(*trans.addr)) || (0 == trans.len)) { (TREF(mprof_env_gbl_name)).str.len = 0; /* this malloc is just so that mprof_env_gbl_name.str.addr is not NULL for subsequent * checks in gtm_startup.c and gtm$startup.c */ (TREF(mprof_env_gbl_name)).str.addr = malloc(1); } else { (TREF(mprof_env_gbl_name)).str.len = trans.len; (TREF(mprof_env_gbl_name)).str.addr = malloc(trans.len); memcpy((TREF(mprof_env_gbl_name)).str.addr, trans.addr, trans.len); } (TREF(mprof_env_gbl_name)).mvtype = MV_STR; } } else (TREF(mprof_env_gbl_name)).str.addr = NULL; val.addr = GTM_POOLLIMIT; val.len = SIZEOF(GTM_POOLLIMIT) - 1; if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &trans, buf, SIZEOF(buf), do_sendmsg_on_log2long))) { /* Note assignment above */ if (SIZEOF(buf) >= trans.len) { if (('0' == (char)(*trans.addr)) || (0 == trans.len)) (TREF(gbuff_limit)).str.len = 0; else { (TREF(gbuff_limit)).str.len = trans.len; (TREF(gbuff_limit)).str.addr = malloc(trans.len); memcpy((TREF(gbuff_limit)).str.addr, trans.addr, trans.len); } } } else (TREF(gbuff_limit)).str.len = 0; (TREF(gbuff_limit)).mvtype = MV_STR; # ifdef DEBUG /* GTM_GVUNDEF_FATAL environment/logical */ val.addr = GTM_GVUNDEF_FATAL; val.len = SIZEOF(GTM_GVUNDEF_FATAL) - 1; assert(FALSE == TREF(gtm_gvundef_fatal)); /* should have been set to FALSE by gtm_threadgbl_defs */ ret = logical_truth_value(&val, FALSE, &is_defined); if (is_defined) TREF(gtm_gvundef_fatal) = ret; /* if logical is not defined, gtm_gvundef_fatal takes the default value */ /* GTM_DIRTREE_COLLHDR_ALWAYS environment/logical */ val.addr = GTM_DIRTREE_COLLHDR_ALWAYS; val.len = SIZEOF(GTM_DIRTREE_COLLHDR_ALWAYS) - 1; assert(FALSE == TREF(gtm_dirtree_collhdr_always)); /* should have been set to FALSE by gtm_threadgbl_defs */ ret = logical_truth_value(&val, FALSE, &is_defined); if (is_defined) TREF(gtm_dirtree_collhdr_always) = ret; /* if logical is not defined, the TREF takes the default value */ # endif /* Initialize variable that controls TP allocation clue (for created blocks) */ val.addr = GTM_TP_ALLOCATION_CLUE; val.len = SIZEOF(GTM_TP_ALLOCATION_CLUE) - 1; gtm_tp_allocation_clue = (block_id)trans_numeric_64(&val, &is_defined, TRUE); if (!is_defined) gtm_tp_allocation_clue = (block_id)MAXTOTALBLKS_MAX; /* GDS Block certification */ val.addr = GTM_GDSCERT; val.len = SIZEOF(GTM_GDSCERT) - 1; ret = logical_truth_value(&val, FALSE, &is_defined); if (is_defined) certify_all_blocks = ret; /* if the logical is not defined, we want to take default value */ /* Initialize null subscript's collation order */ val.addr = LCT_STDNULL; val.len = SIZEOF(LCT_STDNULL) - 1; ret = logical_truth_value(&val, FALSE, &is_defined); if (is_defined) TREF(local_collseq_stdnull) = ret; /* Initialize eXclusive Kill variety (GTM vs M Standard) */ val.addr = GTM_STDXKILL; val.len = SIZEOF(GTM_STDXKILL) - 1; ret = logical_truth_value(&val, FALSE, &is_defined); if (is_defined) gtm_stdxkill = ret; /* Initialize variables for white box testing. Even though these white-box test variables only control the * flow of the DBG builds, the PRO builds check on these variables (for example, in tp_restart.c to decide * whether to fork_n_core or not) so need to do this initialization for PRO builds as well. */ wbox_test_init(); /* Initialize variable that controls dynamic GT.M block upgrade */ val.addr = GTM_BLKUPGRADE_FLAG; val.len = SIZEOF(GTM_BLKUPGRADE_FLAG) - 1; gtm_blkupgrade_flag = trans_numeric(&val, &is_defined, TRUE); /* Initialize whether database file extensions need to be logged in the operator log */ val.addr = GTM_DBFILEXT_SYSLOG_DISABLE; val.len = SIZEOF(GTM_DBFILEXT_SYSLOG_DISABLE) - 1; ret = logical_truth_value(&val, FALSE, &is_defined); if (is_defined) gtm_dbfilext_syslog_disable = ret; /* if the logical is not defined, we want to take default value */ /* Initialize maximum sockets in a single socket device createable by this process */ gtm_max_sockets = MAX_N_SOCKET; val.addr = GTM_MAX_SOCKETS; val.len = SIZEOF(GTM_MAX_SOCKETS) - 1; if ((tmsock = trans_numeric(&val, &is_defined, TRUE)) && MAX_MAX_N_SOCKET > tmsock) /* Note assignment!! */ gtm_max_sockets = tmsock; /* Initialize TCP_KEEPIDLE and by implication SO_KEEPALIVE */ val.addr = GTM_SOCKET_KEEPALIVE_IDLE; val.len = SIZEOF(GTM_SOCKET_KEEPALIVE_IDLE) - 1; TREF(gtm_socket_keepalive_idle) = trans_numeric(&val, &is_defined, TRUE); if (0 > TREF(gtm_socket_keepalive_idle)) TREF(gtm_socket_keepalive_idle) = 0; /* Initialize storage to allocate and keep in our back pocket in case run out of memory */ outOfMemoryMitigateSize = GTM_MEMORY_RESERVE_DEFAULT; val.addr = GTM_MEMORY_RESERVE; val.len = SIZEOF(GTM_MEMORY_RESERVE) - 1; if (reservesize = trans_numeric(&val, &is_defined, TRUE)) /* Note assignment!! */ outOfMemoryMitigateSize = reservesize; /* Initialize indirect cache limits (max memory, max entries) */ max_cache_memsize = DEFAULT_INDRCACHE_KBSIZE * BIN_ONE_K; val.addr = GTM_MAX_INDRCACHE_MEMORY; val.len = SIZEOF(GTM_MAX_INDRCACHE_MEMORY) - 1; if (memsize = trans_numeric(&val, &is_defined, TRUE)) /* Note assignment!! */ max_cache_memsize = ((MAX_INDRCACHE_KBSIZE > memsize) ? memsize : MAX_INDRCACHE_KBSIZE) * BIN_ONE_K; max_cache_entries = DEFAULT_INRDCACHE_ENTRIES; val.addr = GTM_MAX_INDRCACHE_COUNT; val.len = SIZEOF(GTM_MAX_INDRCACHE_COUNT) - 1; if (cachent = trans_numeric(&val, &is_defined, TRUE)) /* Note assignment!! */ max_cache_entries = cachent; /* Initialize ZQUIT to control funky QUIT compilation */ val.addr = GTM_ZQUIT_ANYWAY; val.len = SIZEOF(GTM_ZQUIT_ANYWAY) - 1; ret = logical_truth_value(&val, FALSE, &is_defined); if (is_defined) dollar_zquit_anyway = ret; /* Initialize ZPROMPT to desired GTM prompt or default */ val.addr = GTM_PROMPT; val.len = SIZEOF(GTM_PROMPT) - 1; if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &trans, buf, SIZEOF(buf), do_sendmsg_on_log2long))) { /* Non-standard prompt requested */ assert(SIZEOF(buf) > trans.len); if (SIZEOF_prombuf >= trans.len) { (TREF(gtmprompt)).len = trans.len; memcpy((TREF(gtmprompt)).addr, trans.addr, trans.len); } } /* Initialize tpnotacidtime */ (TREF(tpnotacidtime)).m[1] = TPNOTACID_DEFAULT_TIME; val.addr = GTM_TPNOTACIDTIME; val.len = SIZEOF(GTM_TPNOTACIDTIME) - 1; if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &trans, buf, SIZEOF(buf), do_sendmsg_on_log2long))) { assert(SIZEOF(buf) > trans.len); *(char *)(buf + trans.len) = 0; errno = 0; time = strtod(buf, NULL); if ((ERANGE != errno) && (TPNOTACID_MAX_TIME >= time)) (TREF(tpnotacidtime)).m[1] = time * MILLISECS_IN_SEC; } /* gtm_startup completes initialization of the tpnotacidtime mval */ /* Initialize $gtm_tprestart_log_first */ val.addr = GTM_TPRESTART_LOG_FIRST; val.len = STR_LIT_LEN(GTM_TPRESTART_LOG_FIRST); TREF(tprestart_syslog_first) = trans_numeric(&val, &is_defined, TRUE); if (0 > TREF(tprestart_syslog_first)) TREF(tprestart_syslog_first) = 0; /* Initialize $gtm_tprestart_log_delta */ val.addr = GTM_TPRESTART_LOG_DELTA; val.len = STR_LIT_LEN(GTM_TPRESTART_LOG_DELTA); TREF(tprestart_syslog_delta) = trans_numeric(&val, &is_defined, TRUE); if (0 > TREF(tprestart_syslog_delta)) TREF(tprestart_syslog_delta) = 0; /* Initialize $gtm_nontprestart_log_first */ val.addr = GTM_NONTPRESTART_LOG_FIRST; val.len = STR_LIT_LEN(GTM_NONTPRESTART_LOG_FIRST); TREF(nontprestart_log_first) = trans_numeric(&val, &is_defined, TRUE); if (0 > TREF(nontprestart_log_first)) TREF(nontprestart_log_first) = 0; /* Initialize $gtm_nontprestart_log_delta */ val.addr = GTM_NONTPRESTART_LOG_DELTA; val.len = STR_LIT_LEN(GTM_NONTPRESTART_LOG_DELTA); TREF(nontprestart_log_delta) = trans_numeric(&val, &is_defined, TRUE); if (0 > TREF(nontprestart_log_delta)) TREF(nontprestart_log_delta) = 0; /* See if this is a GT.M Development environment, not a production environment */ if (GETENV("gtm_environment_init")) TREF(gtm_environment_init) = TRUE; /* in-house */ /* See if a trace table is desired. If we have been asked to trace one or more groups, we also * see if a specific size has been specified. A default size is provided. */ val.addr = GTM_TRACE_GROUPS; val.len = SIZEOF(GTM_TRACE_GROUPS) - 1; if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &trans, buf, SIZEOF(buf), do_sendmsg_on_log2long))) { /* Trace-group(s) have been declared - figure out which ones */ assert(SIZEOF(buf) > trans.len); parse_trctbl_groups(&trans); if (0 != TREF(gtm_trctbl_groups)) { /* At least one valid group was specified */ val.addr = GTM_TRACE_TABLE_SIZE; val.len = SIZEOF(GTM_TRACE_TABLE_SIZE) - 1; trctblsize = trans_numeric(&val, &is_defined, TRUE); if (0 < (trctblsize = (0 < trctblsize) ? trctblsize : TRACE_TABLE_SIZE_DEFAULT)) /* assignment! */ { if (TRACE_TABLE_SIZE_MAX < trctblsize) trctblsize = TRACE_TABLE_SIZE_MAX; trctblbytes = trctblsize * SIZEOF(trctbl_entry); TREF(gtm_trctbl_start) = malloc(trctblbytes); TREF(gtm_trctbl_end) = TREF(gtm_trctbl_start) + trctblsize; TREF(gtm_trctbl_cur) = TREF(gtm_trctbl_start) - 1; /* So doesn't skip 1st entry */ memset(TREF(gtm_trctbl_start), 0, trctblbytes); } } } # ifdef UNIX /* Initialize jnl_extract_nocol */ val.addr = GTM_EXTRACT_NOCOL; val.len = STR_LIT_LEN(GTM_EXTRACT_NOCOL); TREF(jnl_extract_nocol) = trans_numeric(&val, &is_defined, TRUE); # endif /* Initialize dollar_zmaxtptime */ val.addr = GTM_ZMAXTPTIME; val.len = SIZEOF(GTM_ZMAXTPTIME) - 1; if ((status = trans_numeric(&val, &is_defined, TRUE)) && (0 <= status) && (TPTIMEOUT_MAX_TIME >= status)) TREF(dollar_zmaxtptime) = status; /* NOTE assignment above */ /* See if $gtm_ztrap_new/GTM_ZTRAP_NEW has been specified */ val.addr = ZTRAP_NEW; val.len = SIZEOF(ZTRAP_NEW) - 1; ztrap_new = logical_truth_value(&val, FALSE, NULL); /* Initialize dollar_zmalloclim and malloccrit_issued */ tmp_malloc_limit = (size_t)getstorage(); val.addr = GTM_MALLOC_LIMIT; val.len = SIZEOF(GTM_MALLOC_LIMIT) - 1; assert(0 == malloccrit_issued); zmalloclim = (size_t)trans_numeric_64(&val, &is_defined, TRUE); if (!is_defined || IS_GTMSECSHR_IMAGE) { zmalloclim = 0; /* default is 0; exclude gtmsecshr */ malloccrit_issued = TRUE; } else if (0 > zmalloclim) /* negative gives half the OS limit */ zmalloclim = tmp_malloc_limit / 2; /* see gtm_malloc_src.h MALLOC macro comment on halving */ else if (zmalloclim > tmp_malloc_limit) zmalloclim = tmp_malloc_limit; else if (zmalloclim < MIN_MALLOC_LIM) zmalloclim = MIN_MALLOC_LIM; val.addr = GTM_MUPJNL_PARALLEL; /* See if $gtm_mupjnl_parallel is set */ val.len = SIZEOF(GTM_MUPJNL_PARALLEL) - 1; gtm_mupjnl_parallel = trans_numeric(&val, &is_defined, TRUE); if (!is_defined) gtm_mupjnl_parallel = 1; /* gtm_nofflf for GTM-9136. Default is FALSE */ val.addr = GTM_NOFFLF; val.len = SIZEOF(GTM_NOFFLF) - 1; ret = logical_truth_value(&val, FALSE, &is_defined); if (is_defined) gtm_nofflf = ret; /* Platform specific initializations */ gtm_env_init_sp(); } } fis-gtm-V7.0-005/sr_port/gtm_env_init.h0000755000032200000250000000117614342376331016706 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2004-2018 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. * * * ****************************************************************/ #ifndef GTM_ENV_INIT_DEFINED void gtm_env_init(void); void gtm_env_init_sp(void); #define GTM_ENV_INIT_DEFINED #endif fis-gtm-V7.0-005/sr_port/gtm_env_xlate_init.c0000644000032200000250000000336314342376331020073 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_limits.h" #include "iosp.h" #include "trans_log_name.h" #include "gtm_logicals.h" #include "error.h" #include "gtm_env_xlate_init.h" #include "stringpool.h" GBLREF mstr env_gtm_env_xlate; error_def(ERR_LOGTOOLONG); error_def(ERR_TRNLOGFAIL); void gtm_env_xlate_init(void) { int4 status; mstr val, tn; char buf[GTM_PATH_MAX]; val.addr = GTM_ENV_XLATE; val.len = STR_LIT_LEN(GTM_ENV_XLATE); env_gtm_env_xlate.len = 0; /* default */ if (SS_NORMAL != (status = TRANS_LOG_NAME(&val, &tn, buf, SIZEOF(buf), dont_sendmsg_on_log2long))) { if (SS_NOLOGNAM == status) return; else if (SS_LOG2LONG == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_LOGTOOLONG, 3, LEN_AND_LIT(GTM_ENV_XLATE), SIZEOF(buf) - 1); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_TRNLOGFAIL, 2, LEN_AND_LIT(GTM_ENV_XLATE), status); } if (0 == tn.len) return; env_gtm_env_xlate.len = tn.len; env_gtm_env_xlate.addr = (char *)malloc(tn.len); memcpy(env_gtm_env_xlate.addr, buf, tn.len); return; } fis-gtm-V7.0-005/sr_port/gtm_env_xlate_init.h0000755000032200000250000000127214342376331020100 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2006 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. * * * ****************************************************************/ #ifndef GTM_ENV_XLATE_INIT_H #define GTM_ENV_XLATE_INIT_H #define GTM_ENV_XLATE_ROUTINE_NAME "gtm_env_xlate" void gtm_env_xlate_init(void); mval* gtm_env_translate(mval* val1, mval* val2, mval* val_xlated); #endif /* GTM_ENV_XLATE_INIT_H */ fis-gtm-V7.0-005/sr_port/gtm_facility.h0000755000032200000250000000121714342376331016673 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GTM_FACILITY_H_INCLUDED #define GTM_FACILITY_H_INCLUDED typedef enum { mcompile_v1y0, mupip_create_v1y0 } gtm_facility; #endif fis-gtm-V7.0-005/sr_port/gtm_fcntl.h0000755000032200000250000000306514342376331016200 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ /* gtm_fcntl.h - interlude to system header file. */ #ifndef GTM_FCNTLH #define GTM_FCNTLH #include #ifndef GTM_FD_TRACE # define CREAT creat # define OPEN open # define OPEN3 open #else /* Note we no longer redefine open to gtm_open because of the problems it creates requiring includes to be specified in * a specific order (anything that includes this include must come BEFORE gdsfhead to avoid errors). This ordering was * nearly impossible when trace flags were specified in error.h or gtm_trigger_src.h. At the time of this removal (10/2012), * a search was completed to verify no open() calls existed that needed this support but that does not prevent new calls from * being added. Therefore, attentiveness is required. */ # define CREAT gtm_creat # define OPEN gtm_open # define OPEN3 gtm_open3 # undef creat /* in case this is already defined by (at least AIX and HPUX seem to do this) */ # define creat gtm_creat #endif int gtm_open(const char *pathname, int flags); int gtm_open3(const char *pathname, int flags, mode_t mode); int gtm_creat(const char *pathname, mode_t mode); #endif fis-gtm-V7.0-005/sr_port/gtm_fetch.c0000644000032200000250000000670414342376333016160 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2020 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 #include #include "stack_frame.h" #include "lookup_variable_htent.h" #include "op.h" #include "lv_val.h" #ifdef DEBUG #include "gdsroot.h" /* all of below until gdsfhead.h is needed by the gv_target/cs_addrs assert */ #include "gdskill.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "mvalconv.h" #include "alias.h" #include "gvt_inline.h" #endif GBLREF stack_frame *frame_pointer; GBLREF symval *curr_symval; #ifdef DEBUG GBLREF int process_exiting; GBLREF gv_namehead *gv_target; GBLREF sgmnt_addrs *cs_addrs; #endif void gtm_fetch(unsigned int cnt_arg, unsigned int indxarg, ...) { ht_ent_mname **htepp; stack_frame *fp; unsigned int cnt, indx; va_list var; # ifdef DEBUG static int als_lvval_gc_frequency, fetch_invocation; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!TREF(compile_time)); /* Should not be in compile time */ assert(!process_exiting); /* Verify that no process unwound the exit frame and continued */ assert(!TREF(expand_prev_key)); /* Verify that this global variable never stays TRUE outside of a $zprevious action */ DEBUG_ONLY(DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE);) /* surrounding DEBUG_ONLY needed because gdsfhead.h is * not included for pro builds and so the macro and its * parameters would be undefined in that case causing a * compile-time error. */ assert(!TREF(in_zwrite)); /* Verify in_zwrite was not left on */ DEBUG_ONLY(SET_ACTIVE_LV(NULL, TRUE, actlv_gtm_fetch);) # ifdef DEBUG if (0 == als_lvval_gc_frequency) { mval tmpmval, *random_mval = &tmpmval; op_fnrandom(1024, random_mval); als_lvval_gc_frequency = 8 + MV_FORCE_INT(random_mval); } if (++fetch_invocation == als_lvval_gc_frequency) { als_lvval_gc(); fetch_invocation = 0; if (als_lvval_gc_frequency < 1024) als_lvval_gc_frequency *= 2; } # endif VAR_START(var, indxarg); cnt = cnt_arg; /* need to preserve stack copy on i386 */ fp = frame_pointer; if (0 < cnt) { /* All generated code comes here to verify instantiation of a given set of variables from the local variable table */ indx = indxarg; for ( ; ; ) { htepp = &fp->l_symtab[indx]; if (NULL == *htepp) *htepp = lookup_variable_htent(indx); assert(NULL != (*htepp)->value); assert(LV_IS_BASE_VAR((*htepp)->value)); assert(NULL != LV_SYMVAL((lv_val *)((*htepp)->value))); if (0 < --cnt) indx = va_arg(var, int4); else break; } } else { /* GT.M calls come here to verify instantiation of the entire local variable table */ indx = fp->vartab_len; htepp = &fp->l_symtab[indx]; for (; indx > 0;) { --indx; --htepp; if (NULL == *htepp) *htepp = lookup_variable_htent(indx); else if (NULL == (*htepp)->value) lv_newname(*htepp, curr_symval); /* Alias processing may have removed the lv_val */ } } va_end(var); } fis-gtm-V7.0-005/sr_port/gtm_ffs.c0000755000032200000250000000271114342376331015640 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "gdsroot.h" #include "gtm_ffs.h" #define BITS_PER_UCHAR 8 block_id gtm_ffs (block_id offset, uchar_ptr_t addr, block_id size) { uchar_ptr_t c; block_id i, j, top; c = addr + (offset / BITS_PER_UCHAR); if (i = (offset & (BITS_PER_UCHAR - 1))) { /* partial byte starting at offset */ for (j = 0; (i < BITS_PER_UCHAR) && (j < size); j++, i++) { if (*c & (1 << i)) return (offset + j); } c++; } assert(c == (addr + (offset + BITS_PER_UCHAR - 1) / BITS_PER_UCHAR)); for (i = ROUND_UP2(offset, BITS_PER_UCHAR), top = ROUND_DOWN2(size + offset, BITS_PER_UCHAR); i < top; c++, i += BITS_PER_UCHAR) { /* full bytes offset to end */ if (*c) { for (j = 0; j < BITS_PER_UCHAR; j++) { if (*c & (1 << j)) return (i + j); } } } for (j = 0, top = size + offset; i < top; j++, i++) { /* partial byte at end */ assert(j < BITS_PER_UCHAR); if (*c & (1 << j)) return i; } return -1; } fis-gtm-V7.0-005/sr_port/gtm_ffs.h0000755000032200000250000000126114342376331015644 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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. * * * ****************************************************************/ #ifndef GTM_FFS_INCLUDED #define GTM_FFS_INCLUDED #include "gdsroot.h" block_id gtm_ffs(block_id offset, uchar_ptr_t addr, block_id size); #endif /* GTM_FFS_INCLUDED */ fis-gtm-V7.0-005/sr_port/gtm_file_remove.h0000755000032200000250000000121714342376331017363 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2018 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. * * * ****************************************************************/ #ifndef GTM_FILE_REMOVE_H_INCLUDED #define GTM_FILE_REMOVE_H_INCLUDED int4 gtm_file_remove(char *fn, int fn_len, uint4 *ustatus); #endif fis-gtm-V7.0-005/sr_port/gtm_file_stat.h0000755000032200000250000000427414342376331017047 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2002 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GTM_FILE_STAT_INCLUDED #define GTM_FILE_STAT_INCLUDED /* Note that FILE_READONLY also implies FILE_PRESENT and the callers can use this information if necessary */ #define FILE_NOT_FOUND 0 #define FILE_PRESENT 1 #define FILE_READONLY 2 #define FILE_STAT_ERROR 4 /* Copy the filename from "src" to "dest" using the following rules. * (i) removing the version information * (ii) doing compression of contiguous directory delimiters (']' or '>' followed by '<' or '['). * (iii) transform every '<' to a '[' and every '>' to a ']' * e.g. * src = user:[library.]gtmshr.exe;23 * dst = user:[library.v990.pro]gtmshr.exe * * Note that without (iii) we would have got the following mixed notation for "dst" which is incorrect * as the '[' is balanced by a '>' (at the end of ".pro>") instead of a corresponding ']'. * * e.g. src = user:[library.]gtmshr.exe;23 * dst = user:[library.v990.pro>gtmshr.exe */ #ifdef VMS #define fncpy_nover(src, src_len, dest, dest_len) \ { \ unsigned char *sptr, *dptr; \ for (sptr = (unsigned char *)src, dptr = dest; sptr < (src + src_len) && (';' != *sptr); ) \ { \ if (('>' == *sptr || ']' == *sptr) && ('<' == *(sptr + 1) || '[' == *(sptr + 1))) \ sptr += 2; \ else if ('<' == *sptr) \ { \ *dptr++ = '['; \ sptr++; \ } else if ('>' == *sptr) \ { \ *dptr++ = ']'; \ sptr++; \ } else \ *dptr++ = *sptr++; \ } \ dest_len = dptr - (unsigned char *)(dest); \ *(dptr) = 0; \ } #endif int gtm_file_stat(mstr *file, mstr *def, mstr *ret, boolean_t check_prv, uint4 *status); #endif /* GTM_FILE_STAT_INCLUDED */ fis-gtm-V7.0-005/sr_port/gtm_iconv.h0000755000032200000250000000124214342376331016203 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /* gtm_iconv.h - interlude to system header file. */ #ifndef GTM_ICONVH #define GTM_ICONVH #ifdef USING_ICONV #define _OSF_SOURCE #include #undef _OSF_SOURCE #define ICONV_OPEN iconv_open #endif #endif fis-gtm-V7.0-005/sr_port/gtm_inet.h0000755000032200000250000000153414342376331016030 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ /* gtm_inet.h - interlude to system header file. */ #ifndef GTM_INETH #define GTM_INETH #ifdef VMS #include #endif #if defined(_AIX) || defined (__MVS__) #include #endif #include #ifndef __MVS__ #include #endif #ifdef NeedInAddrPort typedef uint32_t in_addr_t; #endif #define INET_ADDR inet_addr #define INET_NTOA inet_ntoa #endif fis-gtm-V7.0-005/sr_port/gtm_ipv6.h0000644000032200000250000001055114342376331015751 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ /* gtm_ipv6.h - interlude to system header file. */ #ifndef GTM_IPV6H #define GTM_IPV6H #include /* Make sure we have AI_V4MAPPED/AI_NUMERICSERV defined if available */ GBLREF boolean_t ipv4_only; /* If TRUE, only use AF_INET. */ /* ai_canonname must be set NULL for AIX. Otherwise, freeaddrinfo() freeing the ai_canonname will hit SIG-11 * other field which were not initialized as 0 will also causes getaddrinfo()to fail * Setting AI_PASSIVE will give you a wildcard address if addr is NULL, i.e. INADDR_ANY or IN6ADDR_ANY * AI_NUMERICSERV is to pass the numeric port to address, it is to inhibit the name resolution to improve efficience * AI_ADDRCONFIG: IPv4 addresses are returned only if the local system has at least one IPv4 address configured; IPv6 addresses are only returned if the local system has at least one IPv6 address configured. For now we only use IPv6 address. So not use this flag here. * AI_V4MAPPED: IPv4 mapped addresses are acceptable * (Note: for snail, AI_V4MAPPED is defined but AI_NUMERICSERV is not defined) */ #if (defined(__hppa) || defined(__vms) || defined(__osf__)) #define GTM_IPV6_SUPPORTED FALSE #else #define GTM_IPV6_SUPPORTED TRUE #endif #if !GTM_IPV6_SUPPORTED #define SERVER_HINTS(hints, af) \ { \ assert(AF_INET6 != af); \ memset(&hints, 0, SIZEOF(struct addrinfo)); \ hints.ai_family = AF_INET; \ hints.ai_socktype = SOCK_STREAM; \ hints.ai_protocol = IPPROTO_TCP; \ hints.ai_flags = AI_PASSIVE; \ } #define CLIENT_HINTS(hints) \ { \ memset(&hints, 0, SIZEOF(struct addrinfo)); \ hints.ai_family = AF_INET; \ hints.ai_socktype = SOCK_STREAM; \ hints.ai_protocol = IPPROTO_TCP; \ hints.ai_flags = 0; \ } #define CLIENT_HINTS_AF(hints, af) \ { \ assert(AF_INET == af); \ memset(&hints, 0, SIZEOF(struct addrinfo)); \ hints.ai_family = AF_INET; \ hints.ai_socktype = SOCK_STREAM; \ hints.ai_protocol = IPPROTO_TCP; \ hints.ai_flags = 0; \ } #elif (defined(AI_V4MAPPED) && defined(AI_NUMERICSERV)) #define SERVER_HINTS(hints, af) \ { \ memset(&hints, 0, SIZEOF(struct addrinfo)); \ hints.ai_family = af; \ hints.ai_socktype = SOCK_STREAM; \ hints.ai_protocol = IPPROTO_TCP; \ hints.ai_flags = AI_V4MAPPED | AI_PASSIVE | AI_NUMERICSERV; \ } #define CLIENT_HINTS(hints) \ { \ memset(&hints, 0, SIZEOF(struct addrinfo)); \ hints.ai_family = (ipv4_only ? AF_INET : AF_UNSPEC); \ hints.ai_socktype = SOCK_STREAM; \ hints.ai_protocol = IPPROTO_TCP; \ hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG; \ } #define CLIENT_HINTS_AF(hints, af) \ { \ memset(&hints, 0, SIZEOF(struct addrinfo)); \ hints.ai_family = af; \ hints.ai_socktype = SOCK_STREAM; \ hints.ai_protocol = IPPROTO_TCP; \ hints.ai_flags = AI_V4MAPPED; \ } #else #error "Ok, so we do have non-AI_V4MAPPED/AI_NUMERICSERV machines with IPv6 support" #define SERVER_HINTS(hints, af) \ { \ memset(&hints, 0, SIZEOF(struct addrinfo)); \ hints.ai_family = af; \ hints.ai_socktype = SOCK_STREAM; \ hints.ai_protocol = IPPROTO_TCP; \ hints.ai_flags = AI_PASSIVE; \ } #define CLIENT_HINTS(hints) \ { \ memset(&hints, 0, SIZEOF(struct addrinfo)); \ hints.ai_family = (ipv4_only ? AF_INET : AF_UNSPEC); \ hints.ai_socktype = SOCK_STREAM; \ hints.ai_protocol = IPPROTO_TCP; \ hints.ai_flags = AI_ADDRCONFIG; \ } #define CLIENT_HINTS_AF(hints, af) \ { \ memset(&hints, 0, SIZEOF(struct addrinfo)); \ hints.ai_family = AF_INET; \ hints.ai_socktype = SOCK_STREAM; \ hints.ai_protocol = IPPROTO_TCP; \ hints.ai_flags = 0; \ } #endif #define FREEADDRINFO(ai_ptr) \ { \ if(ai_ptr) \ freeaddrinfo(ai_ptr); \ } union gtm_sockaddr_in46 { struct sockaddr_in ipv4; # if GTM_IPV6_SUPPORTED struct sockaddr_in6 ipv6; # endif }; #endif fis-gtm-V7.0-005/sr_port/gtm_libaio.h0000644000032200000250000000656014342376331016331 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2018 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. * * * ****************************************************************/ #ifndef GTM_LIBAIO_H_INCLUDED #define GTM_LIBAIO_H_INCLUDED /* Enable LIBAIO only on Linux. */ #if defined(__linux__) #define USE_LIBAIO #elif defined(__CYGWIN__) #define USE_NOAIO #endif #ifdef USE_NOAIO /* AIO NOT SUPPORTED */ /* minimal just to satisfy mur_init.c and mur_read_file.h. * More would be needed if MUR_USE_AIO were defined */ struct aiocb { int aio_fildes; volatile void *aio_buf; size_t aio_nbytes; off_t aio_offset; size_t aio_bytesread; int aio_errno; }; #define IF_LIBAIO(x) /* NONE */ #define IF_LIBAIO_ELSE(x, y) y #elif !defined(USE_LIBAIO) #include #define IF_LIBAIO(x) /* NONE */ #define IF_LIBAIO_ELSE(x, y) y #else /* USE_LIBAIO */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #define GTM_AIO_NR_EVENTS_DEFAULT 128 /* Represents the default queue size for in-flight IO's * used by the kernel. */ #define IO_SETUP_ERRSTR_ARRAYSIZE (MAX_TRANS_NAME_LEN + 11) /* We add 12 to the MAX_TRANS_NAME_LEN to make space for the * message, "io_setup(%d)\x00", where "%d" represents a * number that was parsed by trans_numeric(), hence the * MAX_TRANS_NAME_LEN usage. */ #define IO_SETUP_FMT "io_setup(%d)" /* This struct mimics the structure of struct iocb, but adds a few fields * to the end for our own use. See ::struct iocb. * Note: that Linux v4.14 typedef'ed aio_rw_flags like so. * typedef int __bitwise __kernel_rwf_t; */ struct aiocb { /* kernel-internel structure, mirrors struct iocb */ __u64 aio_data; #if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : defined(__LITTLE_ENDIAN) __u32 aio_key; /* the kernel sets aio_key to the req # */ int __bitwise aio_rw_flags; /* RWF_* flags */ #elif defined(__BYTE_ORDER) ? __BYTE_ORDER == __BIG_ENDIAN : defined(__BIG_ENDIAN) int __bitwise aio_rw_flags; /* RWF_* flags */ __u32 aio_key; /* the kernel sets aio_key to the req # */ #else #error edit for your odd byteorder. #endif __u16 aio_lio_opcode; __s16 aio_reqprio; __u32 aio_fildes; __u64 aio_buf; __u64 aio_nbytes; __s64 aio_offset; __u64 aio_reserved2; __u32 aio_flags; __u32 aio_resfd; /* personal implementation-specific definitions */ volatile int res; /* If status is not EINPROGRESS, then denotes the * return value of the IO that just finished. The * return value is analagous to that of the return * value for a synchronous read()/write() syscall. */ volatile int status; /* status of the IO in flight */ }; #define IF_LIBAIO(x) x #define IF_LIBAIO_ELSE(x,y) x #ifdef PADDED /* linux/aio_abi.h provides PADDED until Linux v4.14 to define the above struct, but * this collides with our personal #define which means something completely different. */ #undef PADDED #endif #endif /* USE_LIBAIO */ #endif /* GTM_LIBAIO_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/gtm_limits.h0000644000032200000250000000712314342376331016367 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2002-2019 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. * * * ****************************************************************/ /* Interlude to */ #ifndef GTM_LIMITSH #define GTM_LIMITSH #include #ifdef __hpux #include #endif /* The value 1023 for PATH_MAX is derived using pathconf("path", _PC_PATH_MAX) on z/OS and * we figure other POSIX platforms are at least as capable if they don't define PATH_MAX. * Since we can't afford to call a function on each use of PATH_MAX/GTM_PATH_MAX, this * value is hardcoded here. * * Note on Linux (at least), PATH_MAX is actually defined in . We would include * that here unconditionally but on AIX, param.h includes limits.h. Note that regardless of where * it gets defined, PATH_MAX needs to be defined prior to including stdlib.h. This is because in a * pro build, at least Linux verifies the 2nd parm of realpath() is PATH_MAX bytes or more. * Since param.h sets PATH_MAX to 4K on Linux, this can cause structures defined as GTM_PATH_MAX * to raise an error when used in the 2nd argument of realpath(). */ #ifndef PATH_MAX # ifdef __linux__ # include # else # define PATH_MAX 1023 # endif #endif /* Now define our version which includes space for a terminating NULL byte */ #define GTM_PATH_MAX (PATH_MAX + 1) /* The maximum path to the GT.M distribution is complicated by the paths underneath $gtm_dist. * At the top level, there is libgtmshr.{so,sl,dll} which is roughly 12 characters plus 1 for the * slash. The path length of gtmsecshrdir/gtmsecshr doesn't come into play because * $gtm_dist/gtmsecshr will change directory to $gtm_dist/gtmsecshrdir (13 characters including the * leading slash) and then exec gtmsecshr, avoiding the maximum path issue. Going to "UTF-8" mode adds * another 5 characters ("utf8/") to the path name. The encryption library path, * $gtm_dist/plubin/libgtmcrypt.so is a symlink to some much longer named files which the code will * realpath() before dlopen()ing. As it stands, the longest path is 47 characters (including the "UTF-8" * directory. Thus PATH_MAX - 50 characters should be a good compromise for today and future expansion. * Just in case, the build script verify that nothing past $gtm_dist is more than 50 characters long. */ #define GTM_DIST_PATH_MAX (GTM_PATH_MAX - 50) #if defined(LLONG_MAX) /* C99 and others */ #define GTM_INT64_MIN LLONG_MIN #define GTM_INT64_MAX LLONG_MAX #define GTM_UINT64_MAX ULLONG_MAX #elif defined(LONG_LONG_MAX) #define GTM_INT64_MIN LONG_LONG_MIN #define GTM_INT64_MAX LONG_LONG_MAX #define GTM_UINT64_MAX ULONG_LONG_MAX #elif defined(LONGLONG_MAX) #define GTM_INT64_MIN LONGLONG_MIN #define GTM_INT64_MAX LONGLONG_MAX #define GTM_UINT64_MAX ULONGLONG_MAX #elif defined(__INT64_MAX) /* OpenVMS Alpha */ #define GTM_INT64_MIN __INT64_MIN #define GTM_INT64_MAX __INT64_MAX #define GTM_UINT64_MAX __UINT64_MAX #elif defined(INTMAX_MAX) /* HP-UX */ #define GTM_INT64_MIN INTMAX_MIN #define GTM_INT64_MAX INTMAX_MAX #define GTM_UINT64_MAX UINTMAX_MAX #elif LONG_MAX != INT_MAX /* Tru64 */ #define GTM_INT64_MIN LONG_MIN #define GTM_INT64_MAX LONG_MAX #define GTM_UINT64_MAX ULONG_MAX #else #error Unable to determine 64 bit MAX in gtm_limits.h #endif #endif fis-gtm-V7.0-005/sr_port/gtm_malloc.c0000755000032200000250000000364114342376331016334 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ /* gtm_malloc -- the default version The bulk of the GTM storage manager code now sits in gtm_malloc_src.h and is included twice: once in gtm_malloc.c and again in gtm_malloc_dbg.c. The reason for this is that the production modules built for distribution in the field can have both forms of storage mgmt available in the event that it becomes necessary to chase a corruption issue. It can now be done without resorting to a "debug" version. Several different levels of debugging will also be made available to catch various problems. If the DEBUG flag is not defined (indicating a pro build), the gtm_malloc module will expand without all the asserts and special checking making for a compact and efficient storage manager. The gtm_malloc_dbg module will expand AS IF DEBUG had been specified supplying an alternate assert filled version of storage mgmnt with several different levels of storage validation available. If the DEBUG flag is defined (debug or beta build), the gtm_malloc module will expand with all debugging information intact and the gtm_malloc_dbg module will expand as call backs to the gtm_malloc module since it makes little sense to expand the identical module twice. */ #define GTM_MALLOC_BUILD /* so mdq.h won't include unnecessary and probelmatic trigger checking for gtm_malloc */ #include "caller_id.h" #include "gtm_malloc_src.h" #undef GTM_MALLOC_BUILD fis-gtm-V7.0-005/sr_port/gtm_malloc.h0000755000032200000250000001003414342376331016333 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2022 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. * * * ****************************************************************/ #ifndef GTM_MALLOC_H__included #define GTM_MALLOC_H__included #define GTM_MEMORY_RESERVE_DEFAULT 64 /* 64K reserve "backpocket-cache" released on out-of-memory error */ #define MIN_MALLOC_LIM 2500000 /* SWAG of memory size for a modest routine */ typedef size_t gtm_msize_t; /* Each allocated block has the following structure. The actual address returned to the user for 'malloc' and supplied by the * user for 'free' is actually the storage beginning at the 'userStorage.userStart' area. This holds true even for storage * that is truely malloc'd. Note that true allocated length is kept even in the pro header. */ typedef struct storElemStruct { /* While the following chars and short are not the best for performance, they enable us to keep the header size to * 8 bytes in a pro build. This is important since our minimum allocation size is 16 bytes leaving 8 bytes for data. * Also I have not researched what they are, there are a bunch of 8 byte allocates in GT.M that if we were to go to * a 16 byte header would make the minimum block size 32 bytes thus doubling the storage requirements for these small * blocks. SE 03/2002 [Note 16 byte header is the norm in 64 bit] */ signed char queueIndex; /* Index into TwoTable for this size of element */ unsigned char state; /* State of this block */ unsigned short extHdrOffset; /* For MAXTWO sized elements: offset to the * header that describes the extent. */ GTM64_ONLY(char filler[4];) /* Explicit filler to align the length - may be repurposed */ gtm_msize_t realLen; /* Real (total) length of allocation */ # ifdef DEBUG struct storElemStruct *fPtr; /* Next storage element on free/allocated queue */ struct storElemStruct *bPtr; /* Previous storage element on free/allocated queue */ unsigned char *allocatedBy; /* Who allocated storage */ gtm_msize_t allocLen; /* Requested length of allocation */ gtm_msize_t smTn; /* Storage management transaction number allocated at */ unsigned char headMarker[GTM64_ONLY(8)NON_GTM64_ONLY(4)]; /* Header that should not be modified during usage */ union { struct storElemStruct *deferFreeNext; /* Pointer to next deferred free block */ unsigned char userStart; /* First byte of user useable storage */ } userStorage; # else union /* In production mode, the links are used only when element is free */ { struct storElemStruct *deferFreeNext; /* Pointer to next deferred free block */ struct /* Free block information */ { struct storElemStruct *fPtr; /* Next storage element on free queue */ struct storElemStruct *bPtr; /* Previous storage element on free queue */ } links; unsigned char userStart; /* First byte of user useable storage */ } userStorage; # endif } storElem; size_t gtm_bestfitsize(size_t); void verifyFreeStorage(void); void verifyAllocatedStorage(void); void raise_gtmmemory_error(void); void printMallocInfo(void); void printMallocDump(void); /* When verifying the storage chains, check the allocated chain first in case overruns for allocated storage * have damaged the free chains. This way we find the culprit rather than the symptom. Of course, in the case * where free'd storage is continued to be used, this method breaks down but it is hoped the chosen way finds * the greater number of issues rather than their symptoms. */ #define VERIFY_STORAGE_CHAINS \ { \ GBLREF uint4 gtmDebugLevel; \ if (GDL_SmAllocVerf & gtmDebugLevel) \ verifyAllocatedStorage(); \ if (GDL_SmFreeVerf & gtmDebugLevel) \ verifyFreeStorage(); \ } #endif /* GTM_MALLOC_H__included */ fis-gtm-V7.0-005/sr_port/gtm_malloc_dbg.c0000755000032200000250000000672314342376331017154 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ /* gtm_malloc_dbg -- the debugging version The bulk of the GTM storage manager code now sits in gtm_malloc_src.h and is included twice: once in gtm_malloc.c and again in gtm_malloc_dbg.c. The reason for this is that the production modules built for distribution in the field can have both forms of storage mgmt available in the event that it becomes necessary to chase a corruption issue. It can now be done without resorting to a "debug" version. Several different levels of debugging will also be made available to catch various problems. If the DEBUG flag is not defined (indicating a pro build), the gtm_malloc module will expand without all the asserts and special checking making for a compact and efficient storage manager. The gtm_malloc_dbg module will expand AS IF DEBUG had been specified supplying an alternate assert filled version of storage mgmnt with several different levels of storage validation available. If the DEBUG flag is defined (debug or beta build), the gtm_malloc module will expand with all debugging information intact and the gtm_malloc_dbg module will expand as call backs to the gtm_malloc module since it makes little sense to expand the identical module twice. */ # define GTM_MALLOC_BUILD /* so mdq.h won't include unnecessary and probelmatic trigger checking for gtm_malloc */ #ifndef DEBUG /* We have a PRO build -- generate a full debug version with debug versions of our global names */ # define gtmSmInit gtmSmInit_dbg # define gtm_malloc_main gtm_malloc_dbg # define gtm_free_main gtm_free_dbg # define findStorElem findStorElem_dbg # define processDeferredFrees processDeferredFrees_dbg # define release_unused_storage release_unused_storage_dbg # define raise_gtmmemory_error raise_gtmmemory_error_dbg # define gtm_bestfitsize gtm_bestfitsize_dbg # define DEBUG # define PRO_BUILD # define GTM_MALLOC_DEBUG # include "caller_id.h" # include "gtm_malloc_src.h" #else /* We have a DEBUG build -- Nobody should call gtm_malloc_dbg directly */ # include "mdef.h" # include "gtm_malloc.h" /* Include some defs for these rtns to keep the compiler quiet for this routine. Nobody should be calling these directly so we don't want them where they can get included anywhere else. Note the real versions of these routines are defined and only used/callable from the gtm_malloc_src.h include so when we define them here for completeness in a dbg build, we change the return signature to not return anything (saves us from having to put a "return" after the assertpro). These are just "catchalls" in case the expansion functioned incorrectly. */ void gtm_malloc_dbg(size_t size, int stack_level); void gtm_free_dbg(void *addr, int stack_level); void gtm_malloc_dbg(size_t size, int stack_level) { assertpro(FALSE && "gtm_malloc_dbg called directly"); } void gtm_free_dbg(void *addr, int stack_level) { assertpro(FALSE && "gtm_free_dbg called directly"); } #endif #undef GTM_MALLOC_BUILD fis-gtm-V7.0-005/sr_port/gtm_malloc_src.h0000755000032200000250000017413414342376331017216 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* Storage manager for "smaller" pieces of storage. Uses power-of-two * "buddy" system as described by Knuth. Currently manages pieces of * size 2K - SIZEOF(header). * * This include file is included in both gtm_malloc.c and gtm_malloc_dbg.c. * See the headers of those modules for explanations of how the storage * manager build is actually accomplished. * * Debugging is controlled via the "gtmdbglvl" environment variable in the Unix environment. * If this variable is set to a non-zero value, the debugging environment * is enabled. The debugging features turned on will correspond to the bit * values defined gtmdbglvl.h. Note that this mechanism is versatile enough * that non-storage-managment debugging is also hooked in here. The * debugging desired is a mask for the features desired. For example, if the * value 4 is set, then tracing is enabled. If the value is set to 6, then * both tracing and statistics are enabled. Because the code is expanded * twice in a "Pro" build, these debugging features are available even * in a pro build and can thus be enabled in the field without the need for * a "debug" version to be installed in order to chase a corruption or other * problem. */ #include "mdef.h" /* If this is a pro build (meaning PRO_BUILD is defined), avoid the memcpy() override. That code is only * appropriate for a pure debug build. */ #ifdef PRO_BUILD # define BYPASS_MEMCPY_OVERRIDE /* Instruct gtm_string.h not to override memcpy() */ #endif /* We are the redefined versions so use real versions in this module */ #undef malloc #undef free #include #include #include #include #include #if !defined(__MVS__) # include #endif #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_signal.h" #include "gtm_multi_thread.h" #include "gtmdbglvl.h" #include "io.h" #include "iosp.h" #include "min_max.h" #include "mdq.h" #include "error.h" #include "trans_log_name.h" #include "gtmmsg.h" #include "print_exit_stats.h" #include "mmemory.h" #include "gtm_logicals.h" #include "cache.h" #include "gtm_malloc.h" #include "have_crit.h" #include "gtm_env_init.h" #include "gtmio.h" #include "deferred_signal_handler.h" #include "deferred_events_queue.h" #include "deferred_events.h" /* This routine is compiled twice, once as debug and once as pro and put into the same pro build. The alternative * memory manager is selected with the debug flags (any non-zero gtmdbglvl setting invokes debug memory manager in * a pro build). So the global variables (defined using the STATICD macro) have to be two different fields. * One for pro, one for dbg. The fields have different values and different sizes between the two compiles but * exist in the same build. They cannot coexist. That is why STATICD is defined to be static for PRO and GBLDEF for DBG. * This is the reason why we cannot use the STATICDEF macro here because that is defined to be a GBLDEF for PRO and DBG. * * To debug this routine effectively, normally static routines are turned into GBLDEFs. Also, for vars that * need one copy, define GBLRDEF to GBLDEF for debug and GBLREF for pro. This is because the pro builds always * have a debug version in them satisfiying the GBLREF but the debug builds won't have any pro code in them so * the define must be in the debug version. Also note that we cannot use the STATICDEF macro (instead of the * STATICD below) since that evaluates to a GBLDEF in both PRO and DBG which */ #ifdef DEBUG # define STATICD GBLDEF # define STATICR extern # define GBLRDEF GBLDEF #else # define STATICD static # define STATICR static # define GBLRDEF GBLREF #endif #ifdef GTM64 # define gmaAdr "%016lx" # define gmaFill " " # define gmaLine "--------" #else # define gmaAdr "%08lx" # define gmaFill " " # define gmaLine " " #endif /* Tail call optimization eliminates the gtm_malloc/gtm_free stack level in pro, but we have to account for it in debug. */ #if !defined(DEBUG) || defined(PRO_BUILD) #define TAIL_CALL_LEVEL 0 #else #define TAIL_CALL_LEVEL 1 #endif /* #GTM_THREAD_SAFE : The below macro (MALLOC) is thread-safe because caller ensures serialization with locks */ # define MALLOC(SIZE, ADDR) \ MBSTART{ \ intrpt_state_t PREV_INTRPT_STATE; \ size_t TMP; \ \ assert(IS_PTHREAD_LOCKED_AND_HOLDER); \ DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, PREV_INTRPT_STATE); \ ADDR = (void *)malloc(SIZE); \ ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, PREV_INTRPT_STATE); \ if (NULL == (void *)ADDR) \ { \ gtmMallocErrorSize = SIZE; \ gtmMallocErrorCallerid = CALLERID; \ gtmMallocErrorErrno = errno; \ raise_gtmmemory_error(); \ } \ if (!gtmSystemMalloc && !malloccrit_issued /* totalRmalloc* not available for system malloc */ \ && (0 < zmalloclim) && ((SIZE + totalRmalloc + totalRallocGta) > zmalloclim)) \ { /* Boundary check on zmalloclim */ \ gtmMallocErrorSize = SIZE; \ gtmMallocErrorCallerid = CALLERID; \ gtmMallocErrorErrno = ERR_MALLOCCRIT; \ /* doubling here and halving in gtm_env_init accomodate the fact that \ * stringpool expansions are large, and if occurring, not easy to gracefully \ * continue from, which is also related to deferring the MALLOCCRIT error \ */ \ assertpro(!IS_GTMSECSHR_IMAGE); \ malloccrit_issued = TRUE; \ (*xfer_set_handlers_fnptr)(defer_error, ERR_MALLOCCRIT, FALSE); \ } \ } MBEND # define FREE(size, addr) free(addr); #define MAXBACKFILL (16 * 1024) /* Maximum backfill of large structures */ #define MAXTWO 2048 /* Maximum size we allocate from queues */ /* How many "MAXTWO" elements to allocate at one time. This minimizes the waste since our subblocks must * be aligned on a suitable power of two boundary for the buddy-system to work properly. */ #define ELEMS_PER_EXTENT 16 #define MAXDEFERQUEUES 10 #ifdef DEBUG # define STOR_EXTENTS_KEEP 1 /* Keep only one extent in debug for maximum testing */ # define MINTWO NON_GTM64_ONLY(64) GTM64_ONLY(128) # define MAXINDEX NON_GTM64_ONLY(5) GTM64_ONLY(4) # define STE_FP(p) p->fPtr # define STE_BP(p) p->bPtr #else # define STOR_EXTENTS_KEEP 5 # define MINTWO NON_GTM64_ONLY(16) GTM64_ONLY(32) # define MAXINDEX NON_GTM64_ONLY(7) GTM64_ONLY(6) # define STE_FP(p) p->userStorage.links.fPtr # define STE_BP(p) p->userStorage.links.bPtr #endif /* Following are values used in queueIndex in a storage element. Note that both * values must be less than zero for the current code to function correctly. */ #define QUEUE_ANCHOR -1 #define REAL_MALLOC -2 /* Define number of malloc and free calls we will keep track of */ #define MAXSMTRACE 128 #ifdef DEBUG # define INCR_CNTR(x) ++x # define INCR_SUM(x, y) x += y # define DECR_CNTR(x) --x # define DECR_SUM(x, y) x -= y # define SET_MAX(max, tst) {max = MAX(max, tst);} # define SET_ELEM_MAX(qtype, idx) SET_MAX(qtype##ElemMax[idx], qtype##ElemCnt[idx]) # define TRACE_MALLOC(addr, len, tn) \ { \ if (GDL_SmTrace & gtmDebugLevel) \ DBGFPF((stderr, "Malloc at 0x%lx of %ld bytes from 0x%lx (tn=%ld)\n", addr, len, CALLERID, tn)); \ } # define TRACE_FREE(addr, len, tn) \ { \ if (GDL_SmTrace & gtmDebugLevel) \ DBGFPF((stderr, "Free at 0x%lx of %d bytes from 0x%lx (tn=%ld)\n", addr, len, CALLERID, tn)); \ } #else # define INCR_CNTR(x) # define INCR_SUM(x, y) # define DECR_CNTR(x) # define DECR_SUM(x, y) # define SET_MAX(max, tst) # define SET_ELEM_MAX(qtype, idx) # define TRACE_MALLOC(addr, len, tn) # define TRACE_FREE(addr, len, tn) #endif #ifdef DEBUG_SM # define DEBUGSM(x) (PRINTF x, FFLUSH(stdout)) # else # define DEBUGSM(x) #endif /* Macro to return an index into the TwoTable for a given size (round up to next power of two) * Use the size2Index table to get the proper index. This table is indexed by the number of * storage "blocks" being requested. A storage block is the size of the smallest power of two * block we can allocate (size MINTWO). */ #ifdef DEBUG # define GetSizeIndex(size) (size ? size2Index[(size - 1) / MINTWO] : assert(FALSE)) #else # define GetSizeIndex(size) (size2Index[(size - 1) / MINTWO]) #endif #define CALLERID ((unsigned char *)caller_id(stack_level)) /* Define "routines" to enqueue and dequeue storage elements. Use define so we don't * have to depend on each implementation's compiler inlining to get efficient code here. */ /* #GTM_THREAD_SAFE : The below macro (ENQUEUE_STOR_ELEM) is thread-safe because caller ensures serialization with locks */ #define ENQUEUE_STOR_ELEM(qtype, idx, elem) \ { \ storElem *qHdr, *fElem; \ \ assert(IS_PTHREAD_LOCKED_AND_HOLDER); \ qHdr = &qtype##StorElemQs[idx]; \ STE_FP(elem) = fElem = STE_FP(qHdr); \ STE_BP(elem) = qHdr; \ STE_FP(qHdr) = STE_BP(fElem) = elem; \ INCR_CNTR(qtype##ElemCnt[idx]); \ SET_ELEM_MAX(qtype, idx); \ } /* #GTM_THREAD_SAFE : The below macro (DEQUEUE_STOR_ELEM) is thread-safe because caller ensures serialization with locks */ #define DEQUEUE_STOR_ELEM(qtype, elem) \ { \ assert(IS_PTHREAD_LOCKED_AND_HOLDER); \ STE_FP(STE_BP(elem)) = STE_FP(elem); \ STE_BP(STE_FP(elem)) = STE_BP(elem); \ DECR_CNTR(qtype##ElemCnt[elem->queueIndex]); \ } /* #GTM_THREAD_SAFE : The below macro (GET_QUEUED_ELEMENT) is thread-safe because caller ensures serialization with locks */ #define GET_QUEUED_ELEMENT(sizeIndex, uStor, qHdr, sEHdr) \ { \ assert(IS_PTHREAD_LOCKED_AND_HOLDER); \ qHdr = &freeStorElemQs[sizeIndex]; \ uStor = STE_FP(qHdr); /* First element on queue */ \ if (QUEUE_ANCHOR != uStor->queueIndex) /* Does element exist? (Does queue point to itself?) */ \ { \ DEQUEUE_STOR_ELEM(free, uStor); /* It exists, dequeue it for use */ \ if (MAXINDEX == sizeIndex) \ { /* Allocating a MAXTWO block. Increment use counter for this subblock's block */ \ sEHdr = (storExtHdr *)((char *)uStor + uStor->extHdrOffset); \ ++sEHdr->elemsAllocd; \ } \ } else \ uStor = findStorElem(sizeIndex, stack_level + 1); /* Not in tail call position */ \ assert(0 == ((unsigned long)uStor & (TwoTable[sizeIndex] - 1))); /* Verify alignment */ \ } #ifdef INT8_SUPPORTED # define ChunkSize 8 # define ChunkType gtm_int64_t # define ChunkValue 0xdeadbeefdeadbeefLL #else # define ChunkSize 4 # define ChunkType int4 # define ChunkValue 0xdeadbeef #endif #define AddrMask (ChunkSize - 1) /* States that storage can be in (although the possibilities are limited with only one byte of information) */ enum ElemState {Allocated = 0x42, Free = 0x24}; /* At the end of each super-block is this header which is used to track when all of the elements that * a block of real allocated storage was broken into have become free. At that point, we can return * the chunk to the OS. */ typedef struct storExtHdrStruct { struct { struct storExtHdrStruct *fl, *bl; /* In case we need to visit the entire list */ } links; unsigned char *extentStart; /* First byte of real extent (not aligned) */ storElem *elemStart; /* Start of array of MAXTWO elements */ int elemsAllocd; /* MAXTWO sized element count. When 0 this block is free */ } storExtHdr; /* Structure where malloc and free call trace information is kept */ typedef struct { unsigned char *smAddr; /* Addr allocated or released */ unsigned char *smCaller; /* Who called malloc/free */ gtm_msize_t smSize; /* Size allocated or freed */ gtm_msize_t smTn; /* What transaction it was */ } smTraceItem; /* Our extent must be aligned on a MAXTWO byte boundary hence we allocate one more extent than * we actually want so we can be guarranteed usable storage. However if that allocation actually * starts on a MAXTWO boundary (on guarranteed 8 byte boundary), then we get an extra element. * Here we define our extent size and provide an initial sanity value for "extent_used". If the * allocator ever gets this extra block, this field will be increased by the size of one element * to compensate. */ #define EXTENT_SIZE ((MAXTWO * (ELEMS_PER_EXTENT + 1)) + SIZEOF(storExtHdr)) static unsigned int extent_used = ((MAXTWO * ELEMS_PER_EXTENT) + SIZEOF(storExtHdr)); #ifdef DEBUG /* For debug builds, keep track of the last MAXSMTRACE mallocs and frees. */ GBLDEF volatile int smLastMallocIndex; /* Index to entry of last malloc-er */ GBLDEF volatile int smLastFreeIndex; /* Index to entry of last free-er */ GBLDEF smTraceItem smMallocs[MAXSMTRACE]; /* Array of recent allocators */ GBLDEF smTraceItem smFrees[MAXSMTRACE]; /* Array of recent releasers */ GBLDEF volatile unsigned int smTn; /* Storage management (wrappable) transaction number */ GBLDEF unsigned int outOfMemorySmTn; /* smTN when ran out of memory */ #endif GBLREF uint4 gtmDebugLevel; /* Debug level (0 = using default sm module so with * a DEBUG build, even level 0 implies basic debugging) */ GBLREF boolean_t gtmSystemMalloc; /* Use the system's malloc() instead of our own */ GBLREF boolean_t retry_if_expansion_fails; GBLREF int process_exiting; /* Process is on it's way out */ GBLREF volatile int4 gtmMallocDepth; /* Recursion indicator. Volatile so it gets stored immediately */ GBLREF volatile void *outOfMemoryMitigation; /* Reserve that we will freed to help cleanup if run out of memory */ GBLREF uint4 outOfMemoryMitigateSize; /* Size of above reserve in Kbytes */ GBLREF int mcavail; GBLREF mcalloc_hdr *mcavailptr, *mcavailbase; GBLREF size_t totalRallocGta; /* Size allocated by gtm_text_alloc if at all */ GBLREF void (*cache_table_relobjs)(void); /* Function pointer to call cache_table_rebuild() */ GBLREF ch_ret_type (*ht_rhash_ch)(); /* Function pointer to hashtab_rehash_ch */ GBLREF ch_ret_type (*jbxm_dump_ch)(); /* Function pointer to jobexam_dump_ch */ GBLREF ch_ret_type (*stpgc_ch)(); /* Function pointer to stp_gcol_ch */ /* This var allows us to call ourselves but still have callerid info */ GBLREF volatile int4 fast_lock_count; /* Stop stale/epoch processing while we have our parts exposed */ OS_PAGE_SIZE_DECLARE #define SIZETABLEDIM MAXTWO/MINTWO STATICD int size2Index[SIZETABLEDIM]; GBLRDEF boolean_t gtmSmInitialized; /* Initialized indicator */ GBLRDEF size_t gtmMallocErrorSize; /* Size of last failed malloc */ GBLRDEF unsigned char *gtmMallocErrorCallerid; /* Callerid of last failed malloc */ GBLRDEF int gtmMallocErrorErrno; /* Errno at point of last failure */ GBLREF size_t zmalloclim; /* ISV memory warning of MALLOCCRIT in bytes */ GBLREF boolean_t malloccrit_issued; /* set at time of MALLOCCRIT */ GBLRDEF readonly struct { unsigned char nullHMark[4]; unsigned char nullStr[1]; unsigned char nullTMark[4]; } NullStruct #ifdef DEBUG /* Note, tiz important the first 4 bytes of this are same as markerChar defined below as that is the value both nullHMark * and nullTMark are asserted against to validate against corruption. */ = {{0xde, 0xad, 0xbe, 0xef}, {0x00}, {0xde, 0xad, 0xbe, 0xef}} #endif ; #ifdef DEBUG /* Arrays allocated with size of MAXINDEX + 2 are sized to hold an extra * entry for "real malloc" type allocations. Note that the arrays start with * the next larger element with GTM64 due to increased overhead from the * 8 byte pointers. */ STATICD readonly uint4 TwoTable[MAXINDEX + 2] = { # ifndef GTM64 64, # endif 128, 256, 512, 1024, 2048, 0xFFFFFFFF}; /* Powers of two element sizes */ # ifdef GTM64 STATICD readonly unsigned char markerChar[8] = {0xde, 0xad, 0xbe, 0xef, 0xef, 0xbe, 0xad, 0xde}; # else STATICD readonly unsigned char markerChar[4] = {0xde, 0xad, 0xbe, 0xef}; # endif #else STATICD readonly uint4 TwoTable[MAXINDEX + 2] = { # ifndef GTM64 16, # endif 32, 64, 128, 256, 512, 1024, 2048, 0xFFFFFFFF}; #endif STATICD storElem freeStorElemQs[MAXINDEX + 1]; /* Need full element as queue anchor for dbl-linked * list since ptrs not at top of element. */ STATICD storExtHdr storExtHdrQ; /* List of storage blocks we allocate here */ STATICD uint4 curExtents; /* Number of current extents */ #ifdef DEBUG STATICD storElem allocStorElemQs[MAXINDEX + 2]; /* The extra element is for queueing "real" malloc'd entries */ # ifdef INT8_SUPPORTED STATICD readonly unsigned char backfillMarkC[8] = {0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef}; # else STATICD readonly unsigned char backfillMarkC[4] = {0xde, 0xad, 0xbe, 0xef}; # endif #endif GBLREF size_t totalRmalloc; /* Total storage currently (real) malloc'd (includes extent blocks) */ GBLREF size_t totalAlloc; /* Total allocated (includes allocation overhead but not free space */ GBLREF size_t totalUsed; /* Sum of user allocated portions (totalAlloc - overhead) */ #ifdef DEBUG /* Define variables used to instrument how our algorithm works */ STATICD uint4 totalMallocs; /* Total malloc requests */ STATICD uint4 totalFrees; /* Total free requests */ STATICD uint4 totalExtents; /* Times we allocated more storage */ STATICD uint4 maxExtents; /* Highwater mark of extents */ STATICD size_t rmallocMax; /* Maximum value of totalRmalloc */ STATICD uint4 mallocCnt[MAXINDEX + 2]; /* Malloc count satisfied by each queue size */ STATICD uint4 freeCnt[MAXINDEX + 2]; /* Free count for element in each queue size */ STATICD uint4 elemSplits[MAXINDEX + 2]; /* Times a given queue size block was split */ STATICD uint4 elemCombines[MAXINDEX + 2]; /* Times a given queue block was formed by buddies being recombined */ STATICD uint4 freeElemCnt[MAXINDEX + 2]; /* Current count of elements on the free queue */ STATICD uint4 allocElemCnt[MAXINDEX + 2]; /* Current count of elements on the allocated queue */ STATICD uint4 freeElemMax[MAXINDEX + 2]; /* Maximum number of blocks on the free queue */ STATICD uint4 allocElemMax[MAXINDEX + 2]; /* Maximum number of blocks on the allocated queue */ #endif error_def(ERR_INVMEMRESRV); error_def(ERR_MALLOCCRIT); error_def(ERR_MEMORYRECURSIVE); error_def(ERR_MEMORY); error_def(ERR_SYSCALL); error_def(ERR_MALLOCMAXUNIX); /* Internal prototypes */ void gtmSmInit(void); storElem *findStorElem(int sizeIndex, int stack_level); void release_unused_storage(void); void *gtm_malloc_main(size_t, int stack_level); void gtm_free_main(void *, int stack_level); #ifdef DEBUG void backfill(unsigned char *ptr, gtm_msize_t len); boolean_t backfillChk(unsigned char *ptr, gtm_msize_t len); #else void *gtm_malloc_dbg(size_t, int stack_level); void gtm_free_dbg(void *, int stack_level); void raise_gtmmemory_error_dbg(void); size_t gtm_bestfitsize_dbg(size_t); #endif error_def(ERR_INVMEMRESRV); error_def(ERR_MEMORY); error_def(ERR_MEMORYRECURSIVE); error_def(ERR_SYSCALL); /* Initialize the storage manangement system. Things to initialize: * * - Initialize size2Index table. This table is used to convert a malloc request size * to a storage queue index. * - Initialize queue anchor fwd/bkwd pointers to point to queue anchors so we * build a circular queue. This allows elements to be added and removed without * end-of-queue special casing. The queue anchor element is easily recognized because * it's queue index size will be set to a special value. * - Initialize debug mode. See if gtm_debug_level environment variable is set and * retrieve it's value if yes. */ /* #GTM_THREAD_SAFE : The below function (gtmSmInit) is thread-safe because caller ensures serialization with locks */ void gtmSmInit(void) /* Note renamed to gtmSmInit_dbg when included in gtm_malloc_dbg.c */ { char *ascNum; storElem *uStor; int i, sizeIndex, testSize, blockSize, save_errno; intrpt_state_t prev_intrpt_state; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(IS_PTHREAD_LOCKED_AND_HOLDER); /* If this routine is entered and environment vars have not yet been processed with a call to gtm_env_init(), * then do this now. Since this will likely trigger a call to this routine *again*, verify if we still need * to do this and if not, just return. * * Note that in a pro build, this routine (like several others in this module) has two flavors (pro and dbg) * with the debug flavor being named gtmSmInitdbg(). If driving gtm_env_init() tells us that we want to use * the debug storage manager (i.e. gtmDebugLevel is non-zero), then we need to not finish this initialization * and return to gtm_malloc() which also notes this change and drives gtm_malloc_dbg() which then drives the * correct initialization routine. */ if (!TREF(gtm_env_init_started)) { gtm_env_init(); if (gtmSmInitialized PRO_ONLY(|| (0 != gtmDebugLevel))) return; /* A nested call took care of this already so we're done! */ } /* WARNING!! Since this is early initialization, the following asserts are not well behaved if they do * indeed trip. The best that can be hoped for is they give a condition handler exhausted error on * GTM startup. Unfortunately, more intelligent responses are somewhat elusive since no output devices * are setup nor (potentially) most of the GTM runtime. */ assert(MINTWO == TwoTable[0]); # if defined(__linux__) && !defined(__i386) /* This will make sure that all the memory allocated using 'malloc' will be in heap and no 'mmap' is used. * This is needed to make sure that the offset calculation that we do at places(que_ent, chache_que, etc..) * using 2 'malloc'ed memory can be hold in an integer. Though this will work without any problem as the * current GT.M will not allocate memory more than 4GB, we should find a permanant solution by migrating those * offset fields to long and make sure all other related application logic works fine. */ mallopt(M_MMAP_MAX, 0); # endif /* __linux__ && !__i386 */ /* Check that the storage queue offset in a storage element has sufficient reach * to cover an extent. */ assert(((extent_used - SIZEOF(storExtHdr)) <= ((1 << (SIZEOF(uStor->extHdrOffset) * 8)) - 1))); /* Initialize size table used to get a storage queue index */ sizeIndex = 0; testSize = blockSize = MINTWO; for (i = 0; i < SIZETABLEDIM; i++, testSize += blockSize) { if (testSize > TwoTable[sizeIndex]) ++sizeIndex; size2Index[i] = sizeIndex; } /* Need to initialize the fwd/bck ptrs in the anchors to point to themselves */ for (uStor = &freeStorElemQs[0], i = 0; i <= MAXINDEX; ++i, ++uStor) { STE_FP(uStor) = STE_BP(uStor) = uStor; uStor->queueIndex = QUEUE_ANCHOR; } # ifdef DEBUG for (uStor = &allocStorElemQs[0], i = 0; i <= (MAXINDEX + 1); ++i, ++uStor) { STE_FP(uStor) = STE_BP(uStor) = uStor; uStor->queueIndex = QUEUE_ANCHOR; } # endif dqinit(&storExtHdrQ, links); /* One last task before we consider ourselves initialized. Allocate the out-of-memory mitigation storage * that we will hold onto but not use. If we get an out-of-memory error, this storage will be released back * to the OS for it or GTM to use as necessary while we try to go about an orderly shutdown of our process. * The term "release" here means a literal release. The thinking is we don't know whether GTM's small storage * manager will make use of this storage (32K at a time) or if a larger malloc() will be done by libc for * buffers or what not so we will just give this chunk back to the OS to use as it needs it. */ if (0 < outOfMemoryMitigateSize) { assert(NULL == outOfMemoryMitigation); DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); outOfMemoryMitigation = malloc(outOfMemoryMitigateSize * 1024); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); if (NULL == outOfMemoryMitigation) { save_errno = errno; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_INVMEMRESRV, 2, RTS_ERROR_LITERAL("$gtm_memory_reserve"), save_errno); EXIT(save_errno); } } gtmSmInitialized = TRUE; } /* Recursive routine used to obtain an element on a given size queue. If no * elements of that size are available, we recursively call ourselves to get * an element of the next larger queue which we will then split in half to * get the one we need and place the remainder back on the free queue of its * new smaller size. If we run out of queues, we obtain a fresh new 'hunk' of * storage, carve it up into the largest block size we handle and process as * before. */ /* #GTM_THREAD_SAFE : The below function (findStorElem) is thread-safe because caller ensures serialization with locks */ storElem *findStorElem(int sizeIndex, int stack_level) /* Note renamed to findStorElem_dbg when included in gtm_malloc_dbg.c */ { unsigned char *uStorAlloc; storElem *uStor, *uStor2, *qHdr; storExtHdr *sEHdr; int hdrSize; unsigned int i; assert(IS_PTHREAD_LOCKED_AND_HOLDER); ++sizeIndex; DEBUG_ONLY(hdrSize = OFFSETOF(storElem, userStorage)); /* Size of storElem header */ if (MAXINDEX >= sizeIndex) { /* We have more queues to search */ GET_QUEUED_ELEMENT(sizeIndex, uStor, qHdr, sEHdr); /* We have a larger than necessary element now so break it in half and put * the second half on the queue one size smaller than us. */ INCR_CNTR(elemSplits[sizeIndex]); --sizeIndex; /* Dealing now with smaller element queue */ assert(sizeIndex >= 0 && sizeIndex < MAXINDEX); uStor2 = (storElem *)((unsigned long)uStor + TwoTable[sizeIndex]); uStor2->state = Free; uStor2->queueIndex = sizeIndex; assert(0 == ((unsigned long)uStor2 & (TwoTable[sizeIndex] - 1))); /* Verify alignment */ # ifdef DEBUG memcpy(uStor2->headMarker, markerChar, SIZEOF(uStor2->headMarker)); /* Put header tag in place */ /* Backfill entire block being freed so usage of it will cause problems */ if (GDL_SmBackfill & gtmDebugLevel) backfill((unsigned char *)uStor2 + hdrSize, TwoTable[sizeIndex] - hdrSize); # endif ENQUEUE_STOR_ELEM(free, sizeIndex, uStor2); /* Place on free queue */ } else { /* Nothing left to search, [real]malloc a new ALIGNED block of storage and put it on our queues */ ++curExtents; SET_MAX(maxExtents, curExtents); INCR_CNTR(totalExtents); /* Allocate size for one more subblock than we want. This guarrantees us that we can put our subblocks * on a power of two boundary necessary for buddy alignment. */ MALLOC(EXTENT_SIZE, uStorAlloc); uStor2 = (storElem *)uStorAlloc; /* Make addr "MAXTWO" byte aligned */ uStor = (storElem *)(((unsigned long)(uStor2) + MAXTWO - 1) & (unsigned long) -MAXTWO); totalRmalloc += EXTENT_SIZE; SET_MAX(rmallocMax, totalRmalloc); sEHdr = (storExtHdr *)((char *)uStor + (ELEMS_PER_EXTENT * MAXTWO)); DEBUGSM(("debugsm: Allocating extent at 0x%08lx\n", uStor)); /* If the storage given to us was aligned, we have ELEMS_PER_EXTENT+1 blocks, else we have * ELEMS_PER_EXTENT blocks. We won't put the first element on the queue since that block is * being returned to be split. */ if (uStor == uStor2) { i = 0; /* The storage was suitably aligned, we get an extra block free */ sEHdr = (storExtHdr *)((char *)sEHdr + MAXTWO); extent_used = EXTENT_SIZE; /* New max for sanity checks */ } else i = 1; /* The storage was not aligned. Have planned number of blocks with some waste */ assert(((char *)sEHdr + SIZEOF(*sEHdr)) <= ((char *)uStorAlloc + EXTENT_SIZE)); for (uStor2 = uStor; ELEMS_PER_EXTENT > i; ++i) { /* Place all but first entry on the queue */ uStor2 = (storElem *)((unsigned long)uStor2 + MAXTWO); assert(0 == ((unsigned long)uStor2 & (TwoTable[MAXINDEX] - 1))); /* Verify alignment */ uStor2->state = Free; uStor2->queueIndex = MAXINDEX; uStor2->extHdrOffset = (char *)sEHdr - (char *)uStor2; assert(extent_used > uStor2->extHdrOffset); # ifdef DEBUG memcpy(uStor2->headMarker, markerChar, SIZEOF(uStor2->headMarker)); /* Backfill entire block on free queue so we can detect trouble * with premature usage or overflow from something else */ if (GDL_SmBackfill & gtmDebugLevel) backfill((unsigned char *)uStor2 + hdrSize, TwoTable[MAXINDEX] - hdrSize); # endif ENQUEUE_STOR_ELEM(free, MAXINDEX, uStor2); /* Place on free queue */ } uStor->extHdrOffset = (char *)sEHdr - (char *)uStor; uStor->state = Free; sizeIndex = MAXINDEX; /* Set up storage block header */ sEHdr->extentStart = uStorAlloc; sEHdr->elemStart = uStor; sEHdr->elemsAllocd = 1; dqins(&storExtHdrQ, links, sEHdr); } assert(sizeIndex >= 0 && sizeIndex <= MAXINDEX); uStor->queueIndex = sizeIndex; /* This is now a smaller block */ return uStor; } /* Note, if the below declaration changes, corresponding changes in gtmxc_types.h needs to be done. */ /* Obtain free storage of the given size */ /* #GTM_THREAD_SAFE : The below function (gtm_malloc) is thread-safe; serialization is ensured with locks */ void *gtm_malloc_main(size_t size, int stack_level) /* Note renamed to gtm_malloc_dbg when included in gtm_malloc_dbg.c */ { unsigned char *retVal; storElem *uStor, *qHdr; storExtHdr *sEHdr; gtm_msize_t tSize, hdrSize; int sizeIndex, i; unsigned char *trailerMarker; boolean_t reentered, was_holder; intrpt_state_t prev_intrpt_state; void *rval; if (gtmSystemMalloc) { if (0 == size) return &NullStruct.nullStr[0]; return system_malloc(size); } # ifndef DEBUG /* If we are not expanding for DEBUG, check now if DEBUG has been turned on. * If it has, we are in the wrong module Jack. This IF is structured so that * if this is the normal (default/optimized) case we will fall into the code * and handle the rerouting at the end. */ if (!(gtmDebugLevel & GDL_SmAllMallocDebug)) { # endif /* Note that this if is also structured for maximum fallthru. The else will * be near the end of this entry point. */ if (gtmSmInitialized) { PTHREAD_MUTEX_LOCK_IF_NEEDED(was_holder); /* get exclusive thread lock in case threads are in use */ hdrSize = OFFSETOF(storElem, userStorage); /* Size of storElem header */ assertpro((size + hdrSize) >= size); /* Check for wrap in all platforms */ assert((hdrSize + SIZEOF(markerChar)) < MINTWO); fast_lock_count++; ++gtmMallocDepth; /* Nesting depth of memory calls */ reentered = (1 < gtmMallocDepth); if (reentered) { --gtmMallocDepth; assert(FALSE); PTHREAD_MUTEX_UNLOCK_IF_NEEDED(was_holder); /* release exclusive thread lock if needed */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_MEMORYRECURSIVE); } INCR_CNTR(totalMallocs); INCR_CNTR(smTn); /* Validate null string not overwritten */ assert(0 == memcmp(&NullStruct.nullHMark[0], markerChar, SIZEOF(NullStruct.nullHMark))); assert(0 == memcmp(&NullStruct.nullTMark[0], markerChar, SIZEOF(NullStruct.nullHMark))); DEBUG_ONLY(VERIFY_STORAGE_CHAINS); /* Verify the storage chains before we play */ if (0 != size) { tSize = size + hdrSize; /* Add in header size */ # ifdef DEBUG tSize += SIZEOF(markerChar); /* Add in room for trailer label */ /* If being a storage hog, we want to make sure we have plenty of room for * filler. For strings up to MAXTWO in length, we pad with an additional 50% * of storage with a minimum of 32 bytes and a maximum of 256 bytes. For larger * strings, we pad with 256 bytes. Since selecting GDL_SmStorHog also turns on * GDL_SmBackfill and GDL_SmChkAllocBackfill, this padding will be backfilled and * checked during allocate storage validation calls. */ if (GDL_SmStorHog & gtmDebugLevel) { if (MAXTWO >= size) tSize += (MIN(MAX(size / 2, 32), 256)); else tSize += 256; } # endif /* The difference between $ZALLOCSTOR and $ZUSEDSTOR (totalAlloc and totalUsed global vars) is * that when you allocate, say 16 bytes, that comes out of a 32 byte chunk (with the pro storage * mgr) with the rest being unusable. In a debug build (or a pro build with $gtmdbglvl set to * something non-zero), $ZUSEDSTOR is incremented by 16 bytes (the requested allocation) while * $ZALLOCSTOR is incremented by 32 bytes (the actual allocation). But, in a pro build using * the pro memory manager, we do not track the user-allocated size anywhere. We know it when * we do the allocation of course, but when it comes time to free it, we no longer know what * the user requested size was. We only know that it came out of a 32 byte block. In order for * the free to be consistent with the allocation, we have to use the one value we know at both * malloc and free times - 32 bytes. The net result is that $ZALLOCSTOR and $ZUSEDSTOR report * the same value in a pro build with the pro stmgr while they will be quite different in a * debug build or a pro build with $gtmdbglvl engaged. The difference between them shows the * allocation overhead of gtm_malloc itself. */ if (MAXTWO >= tSize) { /* Use our memory manager for smaller pieces */ sizeIndex = GetSizeIndex(tSize); /* Get index to size we need */ assert(sizeIndex >= 0 && sizeIndex <= MAXINDEX); GET_QUEUED_ELEMENT(sizeIndex, uStor, qHdr, sEHdr); tSize = TwoTable[sizeIndex]; uStor->realLen = tSize; } else { /* Use regular malloc to obtain the piece */ MALLOC(tSize, uStor); totalRmalloc += tSize; SET_MAX(rmallocMax, totalRmalloc); uStor->queueIndex = REAL_MALLOC; uStor->realLen = tSize; DEBUG_ONLY(sizeIndex = MAXINDEX + 1); /* Just so the ENQUEUE below has a queue since * we use -1 as the "real" queueindex for * malloc'd storage and we don't record allocated * storage in other than debug mode. */ } totalUsed += DEBUG_ONLY(size) PRO_ONLY(tSize); totalAlloc += tSize; INCR_CNTR(mallocCnt[sizeIndex]); uStor->state = Allocated; # ifdef DEBUG /* Fill in extra debugging fields in header */ uStor->allocatedBy = CALLERID; /* Who allocated us */ uStor->allocLen = size; /* User requested size */ memcpy(uStor->headMarker, markerChar, SIZEOF(uStor->headMarker)); trailerMarker = (unsigned char *)&uStor->userStorage.userStart + size; /* Where to put trailer */ memcpy(trailerMarker, markerChar, SIZEOF(markerChar)); /* Small trailer */ if (GDL_SmInitAlloc & gtmDebugLevel) /* Initialize the space we are allocating */ backfill((unsigned char *)&uStor->userStorage.userStart, size); if (GDL_SmBackfill & gtmDebugLevel) { /* Use backfill method of after-allocation metadata */ backfill(trailerMarker + SIZEOF(markerChar), (uStor->realLen - size - hdrSize - SIZEOF(markerChar))); } uStor->smTn = smTn; /* Transaction number */ ENQUEUE_STOR_ELEM(alloc, sizeIndex, uStor); # endif retVal = &uStor->userStorage.userStart; assert(((long)retVal & (long)-8) == (long)retVal); /* Assert we have an 8 byte boundary */ } else /* size was 0 */ retVal = &NullStruct.nullStr[0]; # ifdef DEBUG /* Record this transaction in debugging history */ ++smLastMallocIndex; if (MAXSMTRACE <= smLastMallocIndex) smLastMallocIndex = 0; smMallocs[smLastMallocIndex].smAddr = retVal; smMallocs[smLastMallocIndex].smSize = size; smMallocs[smLastMallocIndex].smCaller = CALLERID; smMallocs[smLastMallocIndex].smTn = smTn; # endif TRACE_MALLOC(retVal, size, smTn); --gtmMallocDepth; --fast_lock_count; DEFERRED_EXIT_HANDLING_CHECK; PTHREAD_MUTEX_UNLOCK_IF_NEEDED(was_holder); /* release exclusive thread lock if needed */ return retVal; } else /* Storage mgmt has not been initialized */ { PTHREAD_MUTEX_LOCK_IF_NEEDED(was_holder); /* get thread lock in case threads are in use */ gtmSmInit(); PTHREAD_MUTEX_UNLOCK_IF_NEEDED(was_holder); /* release exclusive thread lock if needed */ /* Reinvoke gtm_malloc now that we are initialized. * * Note, in a pro build, if the call to gtmSmInit() drives gtm_init_env() which discovers * we should be be doing debug builds, drive the debug flavor of gtm_malloc instead which * will do its own initialization if it still needs to (see top of gtmSmInit() above). */ # ifndef DEBUG if (gtmDebugLevel & GDL_SmAllMallocDebug) return (void *)gtm_malloc_dbg(size, stack_level + TAIL_CALL_LEVEL); # endif return (void *)gtm_malloc_main(size, stack_level + TAIL_CALL_LEVEL); } # ifndef DEBUG } else { /* We have a non-DEBUG module but debugging is turned on so redirect the call to the appropriate module. */ return (void *)gtm_malloc_dbg(size, stack_level + TAIL_CALL_LEVEL); } # endif } /* Note, if the below declaration changes, corresponding changes in gtmxc_types.h needs to be done. */ /* Release the free storage at the given address */ /* #GTM_THREAD_SAFE : The below function (gtm_free) is thread-safe; serialization is ensured with locks */ void gtm_free_main(void *addr, int stack_level) /* Note renamed to gtm_free_dbg when included in gtm_malloc_dbg.c */ { storElem *uStor, *buddyElem; storExtHdr *sEHdr; unsigned char *trailerMarker; int sizeIndex, hdrSize, saveIndex, dqIndex, freedElemCnt; gtm_msize_t saveSize, allocSize; boolean_t was_holder; intrpt_state_t prev_intrpt_state; if (gtmSystemMalloc) { if (&NullStruct.nullStr[0] == addr) return; system_free(addr); return; } # ifndef DEBUG /* If we are not expanding for DEBUG, check now if DEBUG has been turned on. * If it has, we are in the wrong module Jack. This IF is structured so that * if this is the normal (optimized) case we will fall into the code and * handle the rerouting at the end. */ if (GDL_None == gtmDebugLevel) { # endif assertpro(gtmSmInitialized); /* Storage must be init'd before can free anything */ /* If we are exiting, don't bother with frees. Process destruction can do it *UNLESS* we are handling an * out of memory condition with the proviso that we can't return memory if we are already nested. */ if (process_exiting && (0 != gtmMallocDepth || (ERR_MEMORY != error_condition))) return; PTHREAD_MUTEX_LOCK_IF_NEEDED(was_holder); /* get exclusive thread lock in case threads are in use */ ++fast_lock_count; ++gtmMallocDepth; /* Recursion indicator */ if (1 < gtmMallocDepth) { --gtmMallocDepth; assert(FALSE); PTHREAD_MUTEX_UNLOCK_IF_NEEDED(was_holder); /* release exclusive thread lock if needed */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_MEMORYRECURSIVE); } INCR_CNTR(smTn); /* Bump the transaction number */ /* Validate null string not overwritten */ assert(0 == memcmp(&NullStruct.nullHMark[0], markerChar, SIZEOF(NullStruct.nullHMark))); assert(0 == memcmp(&NullStruct.nullTMark[0], markerChar, SIZEOF(NullStruct.nullHMark))); /* verify chains before we attempt dequeue */ DEBUG_ONLY(VERIFY_STORAGE_CHAINS); INCR_CNTR(totalFrees); if ((unsigned char *)addr != &NullStruct.nullStr[0]) { hdrSize = OFFSETOF(storElem, userStorage); uStor = (storElem *)((unsigned long)addr - hdrSize); /* Backup ptr to element header */ sizeIndex = uStor->queueIndex; # ifdef DEBUG if (GDL_SmInitAlloc & gtmDebugLevel) /* Initialize the space we are de-allocating */ backfill((unsigned char *)&uStor->userStorage.userStart, uStor->allocLen); TRACE_FREE(addr, uStor->allocLen, smTn); saveSize = uStor->allocLen; /* Extra checking for debugging. Note that these sanity checks are only done in debug * mode. The thinking is that we will bypass the checks in the general case for speed but * if we really need to chase a storage related problem, we should switch to the debug version * in the field to turn on these and other checks. */ assert(Allocated == uStor->state); assert(0 == memcmp(uStor->headMarker, markerChar, SIZEOF(uStor->headMarker))); trailerMarker = (unsigned char *)&uStor->userStorage.userStart + uStor->allocLen;/* Where trailer was put */ assert(0 == memcmp(trailerMarker, markerChar, SIZEOF(markerChar))); if (GDL_SmChkAllocBackfill & gtmDebugLevel) { /* Use backfill check method for after-allocation metadata */ assert(backfillChk(trailerMarker + SIZEOF(markerChar), (uStor->realLen - uStor->allocLen - hdrSize - SIZEOF(markerChar)))); } /* Remove element from allocated queue unless element is from a reentered malloc call. In that case, just * manipulate the counters. */ if (NULL != uStor->fPtr) { if (0 <= uStor->queueIndex) { DEQUEUE_STOR_ELEM(alloc, uStor); } else { /* Shenanigans so that counts are maintained properly in debug mode */ saveIndex = uStor->queueIndex; uStor->queueIndex = MAXINDEX + 1; DEQUEUE_STOR_ELEM(alloc, uStor); uStor->queueIndex = saveIndex; } } else DECR_CNTR(allocElemCnt[((0 <= uStor->queueIndex) ? uStor->queueIndex : MAXINDEX + 1)]); # endif totalUsed -= DEBUG_ONLY(uStor->allocLen) PRO_ONLY(uStor->realLen); if (sizeIndex >= 0) { /* We can put the storage back on one of our simple queues */ assert(0 == ((unsigned long)uStor & (TwoTable[sizeIndex] - 1))); /* Verify alignment */ assert(sizeIndex >= 0 && sizeIndex <= MAXINDEX); uStor->state = Free; DEBUG_ONLY(uStor->smTn = smTn); /* For freed blocks, set Tn when were freed */ INCR_CNTR(freeCnt[sizeIndex]); assert(uStor->realLen == TwoTable[sizeIndex]); totalAlloc -= TwoTable[sizeIndex]; /* First, if there are larger queues than this one, see if it has a buddy that it can * combine with. */ while (sizeIndex < MAXINDEX) { buddyElem = (storElem *)((unsigned long)uStor ^ TwoTable[sizeIndex]);/* Address of buddy */ assert(0 == ((unsigned long)buddyElem & (TwoTable[sizeIndex] - 1)));/* Verify alignment */ assert(buddyElem->state == Allocated || buddyElem->state == Free); assert(buddyElem->queueIndex >= 0 && buddyElem->queueIndex <= sizeIndex); if (buddyElem->state == Allocated || buddyElem->queueIndex != sizeIndex) /* All possible combines done */ break; /* Remove buddy from its queue and make a larger element for a larger queue */ DEQUEUE_STOR_ELEM(free, buddyElem); if (buddyElem < uStor) /* Pick lower address buddy for top of new bigger block */ uStor = buddyElem; ++sizeIndex; assert(sizeIndex >= 0 && sizeIndex <= MAXINDEX); INCR_CNTR(elemCombines[sizeIndex]); uStor->queueIndex = sizeIndex; } # ifdef DEBUG /* Backfill entire block being freed so usage of it will cause problems */ if (GDL_SmBackfill & gtmDebugLevel) backfill((unsigned char *)uStor + hdrSize, TwoTable[sizeIndex] - hdrSize); # endif ENQUEUE_STOR_ELEM(free, sizeIndex, uStor); if (MAXINDEX == sizeIndex) { /* Freeing/Coagulating a MAXTWO block. Decrement use counter for this element's block */ sEHdr = (storExtHdr *)((char *)uStor + uStor->extHdrOffset); --sEHdr->elemsAllocd; assert(0 <= sEHdr->elemsAllocd); /* Check for an extent being ripe for return to the system. Requirements are: * 1) All subblocks must be free (elemsAllocd == 0). * 2) There must be more than STOR_EXTENTS_KEEP extents already allocated. * If these conditions are met, we will dequeue each individual element from * it's queue and release the entire extent in a (real) free. */ if (STOR_EXTENTS_KEEP < curExtents && 0 == sEHdr->elemsAllocd) { /* Release this extent */ DEBUGSM(("debugsm: Extent being freed from 0x%08lx\n", sEHdr->elemStart)); DEBUG_ONLY(freedElemCnt = 0); for (uStor = sEHdr->elemStart; (char *)uStor < (char *)sEHdr; uStor = (storElem *)((char *)uStor + MAXTWO)) { DEBUG_ONLY(++freedElemCnt); assert(Free == uStor->state); assert(MAXINDEX == uStor->queueIndex); DEQUEUE_STOR_ELEM(free, uStor); DEBUGSM(("debugsm: ... element removed from free q 0x%08lx\n", uStor)); } assert(ELEMS_PER_EXTENT <= freedElemCnt); /* one loop to free them all */ assert((char *)uStor == (char *)sEHdr); dqdel(sEHdr, links); FREE(EXTENT_SIZE, sEHdr->extentStart); totalRmalloc -= EXTENT_SIZE; --curExtents; assert(curExtents); } } } else { assert(REAL_MALLOC == sizeIndex); /* Better be a real malloc type block */ INCR_CNTR(freeCnt[MAXINDEX + 1]); /* Count free of malloc */ allocSize = saveSize = uStor->realLen; # ifdef DEBUG /* Backfill entire block being freed so usage of it will cause problems */ if (GDL_SmBackfill & gtmDebugLevel) backfill((unsigned char *)uStor, allocSize); # endif FREE(allocSize, uStor); totalRmalloc -= allocSize; totalAlloc -= allocSize; } } else saveSize = 0; /* 4SCA: Using the null struct so this isn't important */ # ifdef DEBUG /* Make trace entry for this free */ ++smLastFreeIndex; if (MAXSMTRACE <= smLastFreeIndex) smLastFreeIndex = 0; smFrees[smLastFreeIndex].smAddr = addr; smFrees[smLastFreeIndex].smSize = saveSize; smFrees[smLastFreeIndex].smCaller = CALLERID; smFrees[smLastFreeIndex].smTn = smTn; # endif --gtmMallocDepth; --fast_lock_count; PTHREAD_MUTEX_UNLOCK_IF_NEEDED(was_holder); /* release exclusive thread lock if needed */ # ifndef DEBUG } else { /* If not a debug module and debugging is enabled, reroute call to * the debugging version. */ gtm_free_dbg(addr, stack_level); /* Not in tail call position */ } # endif DEFERRED_EXIT_HANDLING_CHECK; } /* When an out-of-storage type error is encountered, besides releasing our memory reserve, we also * want to release as much unused storage within various GTM queues that we can find. */ /* #GTM_THREAD_SAFE : The below function (release_unused_storage) is thread-safe because caller ensures serialization with locks */ void release_unused_storage(void) /* Note renamed to release_unused_storage_dbg when included in gtm_malloc_dbg.c */ { mcalloc_hdr *curhdr, *nxthdr; assert(IS_PTHREAD_LOCKED_AND_HOLDER); /* Release compiler storage if we aren't in the compiling business currently */ if (NULL != mcavailbase && mcavailptr == mcavailbase && mcavail == mcavailptr->size) { /* Buffers are unused and subject to release */ for (curhdr = mcavailbase; curhdr; curhdr = nxthdr) { nxthdr = curhdr->link; gtm_free(curhdr); } mcavail = 0; mcavailptr = mcavailbase = NULL; } /* If the cache_table_rebuild() routine is available in this executable, call it through its function pointer. */ if (NULL != cache_table_relobjs) (*cache_table_relobjs)(); /* Release object code in indirect cache */ } /* Raise ERR_MEMORY. Separate routine since is called from hashtable logic in place of the * previous HTEXPFAIL error message. As such, it checks and properly deals with which flavor is running * (debug or non-debug). */ /* #GTM_THREAD_SAFE : The below function (raise_gtmmemory_error) is thread-safe because caller ensures serialization with locks */ void raise_gtmmemory_error(void) /* Note renamed to raise_gtmmemory_error_dbg when included in gtm_malloc_dbg.c */ { boolean_t was_holder; void *addr; assert(IS_PTHREAD_LOCKED_AND_HOLDER); # ifndef DEBUG /* If we are not expanding for DEBUG, check now if DEBUG has been turned on. * If it has, we are in the wrong module Jack. This IF is structured so that * if this is the normal (optimized) case we will fall into the code and * handle the rerouting at the end. * * Note: The DEBUG expansion of this code in a pro build actually has a different * entry point name (raise_gtmmeory_error_dbg) and if malloc debugging options are * on in pro, we need to call that version, but since efficiency in pro trumps * clarity, we put the redirecting call at the bottom of the if-else block to * avoid disrupting the instruction pipeline. */ if (GDL_None == gtmDebugLevel) { # endif if (NULL != (addr = (void *)outOfMemoryMitigation) /* WARNING assignment */ && !(ht_rhash_ch == active_ch->ch || jbxm_dump_ch == active_ch->ch || stpgc_ch == active_ch->ch)) { /* Free our reserve only if not in certain condition handlers (on UNIX) since it is * going to unwind this error and ignore it. */ outOfMemoryMitigation = NULL; free(addr); DEBUG_ONLY(if (0 == outOfMemorySmTn) outOfMemorySmTn = smTn); /* Must decr gtmMallocDepth after release above but before the * call to release_unused_storage() below. */ --gtmMallocDepth; release_unused_storage(); } else --gtmMallocDepth; --fast_lock_count; DEFERRED_EXIT_HANDLING_CHECK; was_holder = TRUE; /* caller (gtm_malloc/gtm_free) got the thread lock so release it before the rts_error */ PTHREAD_MUTEX_UNLOCK_IF_NEEDED(was_holder); /* release exclusive thread lock if needed */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_MEMORY, 2, gtmMallocErrorSize, gtmMallocErrorCallerid, gtmMallocErrorErrno); # ifndef DEBUG } else /* If not a debug module and debugging is enabled, reroute call to the debugging version. */ raise_gtmmemory_error_dbg(); # endif } /* Return the maximum size that would fully utilize a storage block given the input size. If the size will not * fit in one of the buddy list queue elems, it is returned unchanged. Otherwise, the size of the buddy list queue * element minus the overhead will be returned as the best fit size. */ /* #GTM_THREAD_SAFE : The below function (gtm_bestfitsize) is thread-safe because caller ensures serialization with locks */ size_t gtm_bestfitsize(size_t size) { size_t tSize; int hdrSize, sizeIndex; assert(IS_PTHREAD_LOCKED_AND_HOLDER); if (gtmSystemMalloc) return size; # ifndef DEBUG /* If we are not expanding for DEBUG, check now if DEBUG has been turned on. * If it has, we are in the wrong module Jack. This IF is structured so that * if this is the normal (optimized) case we will fall into the code and * handle the rerouting at the end. * * Note: The DEBUG expansion of this code in a pro build actually has a different * entry point name (gtm_bestfitsize_dbg) and if malloc debugging options are * on in pro, we need to call that version, but since efficiency in pro trumps * clarity, we put the redirecting call at the bottom of the if-else block to * avoid disrupting the instruction pipeline. */ if (GDL_None == gtmDebugLevel) { # endif hdrSize = OFFSETOF(storElem, userStorage); /* Size of storElem header */ tSize = size + hdrSize DEBUG_ONLY(+ SIZEOF(markerChar)); if (MAXTWO >= tSize) { /* Allocation would fit in a buddy list queue */ sizeIndex = GetSizeIndex(tSize); tSize = TwoTable[sizeIndex]; return (tSize - hdrSize DEBUG_ONLY(- SIZEOF(markerChar))); } return size; # ifndef DEBUG } /* If not a debug module and debugging is enabled, reroute call to * the debugging version. */ return gtm_bestfitsize_dbg(size); # endif } /* Note that the DEBUG define takes on an additional meaning in this module. Not only are routines defined within * intended for DEBUG builds but they are also only generated ONCE rather than twice like most of the routines are * in this module. */ #ifdef DEBUG /* Backfill the requested area with marker text. We do this by doing single byte * stores up to the point where we can do aligned stores of the native register * length. Then fill the area as much as possible and finish up potentially with * a few single byte unaligned bytes at the end. */ /* #GTM_THREAD_SAFE : The below function (backfill) is thread-safe because caller ensures serialization with locks */ void backfill(unsigned char *ptr, gtm_msize_t len) { #ifndef STATIC_ANALYSIS unsigned char *c; ChunkType *chunkPtr; gtm_msize_t unalgnLen, chunkCnt; assert(IS_PTHREAD_LOCKED_AND_HOLDER); if (0 != len) { len = MIN(len, MAXBACKFILL); /* Restrict backfill for performance */ /* Process unaligned portion first */ unalgnLen = (gtm_msize_t)ptr & AddrMask; /* Past an alignment point */ if (unalgnLen) { unalgnLen = ChunkSize - unalgnLen; /* How far to go to get to alignment point */ unalgnLen = MIN(unalgnLen, len); /* Make sure not going too far */ c = backfillMarkC; assert((0 < len) && (0 < unalgnLen) && (unalgnLen <= len)); len -= unalgnLen; do { *ptr++ = *c++; --unalgnLen; } while(unalgnLen); } /* Now, do aligned portion */ assert(0 == ((gtm_msize_t)ptr & AddrMask)); /* Verify aligned */ chunkCnt = len / ChunkSize; chunkPtr = (ChunkType *)ptr; while (chunkCnt--) { *chunkPtr++ = ChunkValue; len -= SIZEOF(ChunkType); } /* Do remaining unaligned portion if any */ if (len) { ptr = (unsigned char *)chunkPtr; c = backfillMarkC; do { *ptr++ = *c++; --len; } while(len); } } #endif } /* ** still under ifdef DEBUG ** */ /* Check the given backfilled area that it was filled in exactly as * the above backfill routine would have filled it in. Again, do any * unaligned single chars first, then aligned native length areas, * then any stragler unaligned chars. */ /* #GTM_THREAD_SAFE : The below function (backfillChk) is thread-safe because caller ensures serialization with locks */ boolean_t backfillChk(unsigned char *ptr, gtm_msize_t len) { #ifndef STATIC_ANALYSIS unsigned char *c; ChunkType *chunkPtr; gtm_msize_t unalgnLen, chunkCnt; assert(IS_PTHREAD_LOCKED_AND_HOLDER); if (0 != len) { len = MIN(len, MAXBACKFILL); /* Restrict backfill check for performance */ /* Process unaligned portion first */ unalgnLen = (gtm_msize_t)ptr & AddrMask; /* Past an alignment point */ if (unalgnLen) { unalgnLen = ChunkSize - unalgnLen; /* How far to go to get to alignment point */ unalgnLen = MIN(unalgnLen, len); /* Make sure not going too far */ c = backfillMarkC; assert((0 < len) && (0 < unalgnLen) && (unalgnLen <= len)); len -= unalgnLen; do { if (*ptr++ == *c++) --unalgnLen; else return FALSE; } while(unalgnLen); } /* Now, do aligned portion */ assert(0 == ((gtm_msize_t)ptr & AddrMask)); /* Verify aligned */ chunkCnt = len / ChunkSize; chunkPtr = (ChunkType *)ptr; while (chunkCnt--) { if (*chunkPtr++ == ChunkValue) len -= SIZEOF(ChunkType); else return FALSE; } /* Do remaining unaligned portion if any */ if (len) { ptr = (unsigned char *)chunkPtr; c = backfillMarkC; do { if (*ptr++ == *c++) --len; else return FALSE; } while(len); } } #endif return TRUE; } /* ** still under ifdef DEBUG ** */ /* Routine to run the free storage chains to verify that everything is in the correct place */ /* #GTM_THREAD_SAFE : The below function (verifyFreeStorage) is thread-safe because caller ensures serialization with locks */ void verifyFreeStorage(void) { storElem *eHdr, *uStor; uint4 i; int hdrSize; if (gtmSystemMalloc) return; assert(IS_PTHREAD_LOCKED_AND_HOLDER); hdrSize = OFFSETOF(storElem, userStorage); /* Looping for each free queue */ for (eHdr = &freeStorElemQs[0], i = 0; i <= MAXINDEX; ++i, ++eHdr) { for (uStor = STE_FP(eHdr); uStor->queueIndex != QUEUE_ANCHOR; uStor = STE_FP(uStor)) { assert(((MAXINDEX + 1) >= i)); /* Verify loop limits */ assert(((i == uStor->queueIndex) && (MAXINDEX <= MAXINDEX)) || (((MAXINDEX + 1) == i) && (REAL_MALLOC == uStor->queueIndex))); /* Verify queue index */ assert(0 == ((unsigned long)uStor & (TwoTable[i] - 1))); /* Verify alignment */ assert(Free == uStor->state); /* Verify state */ assert(0 == memcmp(uStor->headMarker, markerChar, SIZEOF(uStor->headMarker))); /* Vfy metadata marker */ assert(MAXINDEX != i || extent_used > uStor->extHdrOffset); if (GDL_SmChkFreeBackfill & gtmDebugLevel) /* Use backfill check method for verifying freed storage is untouched */ assert(backfillChk((unsigned char *)uStor + hdrSize, TwoTable[i] - hdrSize)); } } } /* ** still under ifdef DEBUG ** */ /* Routine to run the allocated chains to verify that the markers are all still in place */ /* #GTM_THREAD_SAFE : The below function (verifyAllocatedStorage) is thread-safe because caller ensures serialization with locks */ void verifyAllocatedStorage(void) { storElem *eHdr, *uStor; unsigned char *trailerMarker; uint4 i; int hdrSize; if (gtmSystemMalloc) return; assert(IS_PTHREAD_LOCKED_AND_HOLDER); hdrSize = OFFSETOF(storElem, userStorage); /* Looping for MAXINDEX+1 will check the real-malloc'd chains too */ for (eHdr = &allocStorElemQs[0], i = 0; i <= (MAXINDEX + 1); ++i, ++eHdr) { for (uStor = STE_FP(eHdr); uStor->queueIndex != QUEUE_ANCHOR; uStor = STE_FP(uStor)) { assert(((MAXINDEX + 1) >= i)); /* Verify loop not going nutz */ assert(((i == uStor->queueIndex) && (MAXINDEX <= MAXINDEX)) || (((MAXINDEX + 1) == i) && (REAL_MALLOC == uStor->queueIndex))); /* Verify queue index */ if (i != MAXINDEX + 1) /* If not verifying real mallocs,*/ assert(0 == ((unsigned long)uStor & (TwoTable[i] - 1))); /* .. verify alignment */ assert(Allocated == uStor->state); /* Verify state */ assert(0 == memcmp(uStor->headMarker, markerChar, SIZEOF(uStor->headMarker))); /* Vfy metadata markers */ trailerMarker = (unsigned char *)&uStor->userStorage.userStart+uStor->allocLen;/* Where trailer was put */ assert(0 == memcmp(trailerMarker, markerChar, SIZEOF(markerChar))); assert(MAXINDEX != i || extent_used > uStor->extHdrOffset); if (GDL_SmChkAllocBackfill & gtmDebugLevel) /* Use backfill check method for after-allocation metadata */ assert(backfillChk(trailerMarker + SIZEOF(markerChar), (uStor->realLen - uStor->allocLen - hdrSize - SIZEOF(markerChar)))); } } } /* ** still under ifdef DEBUG ** */ /* Routine to print the end-of-process info -- either allocation statistics or malloc trace dump. * Note that the use of FPRINTF here instead of util_out_print is historical. The output was at one * time going to stdout and util_out_print goes to stderr. If necessary or desired, these could easily * be changed to use util_out_print instead of FPRINTF. */ /* #GTM_THREAD_SAFE : The below function (printMallocInfo) is thread-safe because caller ensures serialization with locks */ void printMallocInfo(void) { int i, j; assert(IS_PTHREAD_LOCKED_AND_HOLDER); if (GDL_SmStats & gtmDebugLevel) { FPRINTF(stderr, "\nMalloc small storage performance:\n"); FPRINTF(stderr, "Total mallocs: %d, total frees: %d, total extents: %d, total rmalloc bytes: %ld," " max rmalloc bytes: %ld\n", totalMallocs, totalFrees, totalExtents, totalRmalloc, rmallocMax); FPRINTF(stderr, "Total (currently) allocated (includes overhead): %ld, Total (currently) used (no overhead): %ld\n", totalAlloc, totalUsed); FPRINTF(stderr, "Maximum extents: %d, Current extents: %d, Released extents: %d\n", maxExtents, curExtents, (totalExtents - curExtents)); FPRINTF(stderr, "\nQueueSize Mallocs Frees Splits Combines CurCnt MaxCnt CurCnt MaxCnt\n"); FPRINTF(stderr, " Free Free Alloc Alloc\n"); FPRINTF(stderr, "-----------------------------------------------------------------------------------------\n"); { for (i = 0; i <= MAXINDEX + 1; ++i) { FPRINTF(stderr, "%9d %9d %9d %9d %9d %9d %9d %9d %9d\n", TwoTable[i], mallocCnt[i], freeCnt[i], elemSplits[i], elemCombines[i], freeElemCnt[i], freeElemMax[i], allocElemCnt[i], allocElemMax[i]); } } } if (GDL_SmDumpTrace & gtmDebugLevel) { FPRINTF(stderr, "\nMalloc Storage Traceback: gtm_malloc() addr: 0x"gmaAdr"\n", >m_malloc); FPRINTF(stderr, "TransNumber "gmaFill" AllocAddr Size "gmaFill" CallerAddr\n"); FPRINTF(stderr, "------------------------------------------------"gmaLine gmaLine"\n"); for (i = 0,j = smLastMallocIndex; i < MAXSMTRACE; ++i,--j)/* Loop through entire table, start with last elem used */ { if (0 > j) /* Wrap as necessary */ j = MAXSMTRACE - 1; if (0 != smMallocs[j].smTn) FPRINTF(stderr, "%9d 0x"gmaAdr" %10d 0x"gmaAdr"\n", smMallocs[j].smTn, smMallocs[j].smAddr, smMallocs[j].smSize, smMallocs[j].smCaller); } FPRINTF(stderr, "\n\nFree Storage Traceback:\n"); FPRINTF(stderr, "TransNumber "gmaFill" FreeAddr Size "gmaFill" CallerAddr\n"); FPRINTF(stderr, "------------------------------------------------"gmaLine gmaLine"\n"); for (i = 0, j = smLastFreeIndex; i < MAXSMTRACE; ++i, --j)/* Loop through entire table, start with last elem used */ { if (0 > j) /* Wrap as necessary */ j = MAXSMTRACE - 1; if (0 != smFrees[j].smTn) FPRINTF(stderr, "%9d 0x"gmaAdr" %10d 0x"gmaAdr"\n", smFrees[j].smTn, smFrees[j].smAddr, smFrees[j].smSize, smFrees[j].smCaller); } FPRINTF(stderr, "\n"); FFLUSH(stderr); } printMallocDump(); } /* ** still under ifdef DEBUG ** */ /* Routine to print storage dump. This is called as part of print_malloc_info but is also potentially separately called from * op_view so is a separate routine. */ /* #GTM_THREAD_SAFE : The below function (printMallocDump) is thread-safe because caller ensures serialization with locks */ void printMallocDump(void) { storElem *eHdr, *uStor; int i; assert(IS_PTHREAD_LOCKED_AND_HOLDER); if (GDL_SmDump & gtmDebugLevel) { FPRINTF(stderr, "\nMalloc Storage Dump: gtm_malloc() addr: 0x"gmaAdr"\n", >m_malloc); FPRINTF(stderr, gmaFill"Malloc Addr "gmaFill" Alloc From Malloc Size Trans Number\n"); FPRINTF(stderr, " ----------------------------------------------------------"gmaLine gmaLine"\n"); /* Looping for each allocated queue */ for (eHdr = &allocStorElemQs[0], i = 0; i <= (MAXINDEX + 1); ++i, ++eHdr) { for (uStor = STE_FP(eHdr); uStor->queueIndex != QUEUE_ANCHOR; uStor = STE_FP(uStor)) { FPRINTF(stderr, " 0x"gmaAdr" 0x"gmaAdr" %10d %10d\n", &uStor->userStorage.userStart, uStor->allocatedBy, uStor->allocLen, uStor->smTn); } } FFLUSH(stderr); } } void *system_malloc(size_t size) { void *rval; intrpt_state_t prev_intrpt_state; boolean_t was_holder; DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); rval = malloc(size); if (!rval) { PTHREAD_MUTEX_LOCK_IF_NEEDED(was_holder); /* get exclusive thread lock in case threads are in use */ raise_gtmmemory_error(); } ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); return rval; } void system_free(void *addr) { intrpt_state_t prev_intrpt_state; DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); free(addr); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); return; } #endif #ifndef GTM_MALLOC_DEBUG void *gtm_malloc(size_t size) { return gtm_malloc_main(size, TAIL_CALL_LEVEL); } void gtm_free(void *addr) { gtm_free_main(addr, TAIL_CALL_LEVEL); } #endif fis-gtm-V7.0-005/sr_port/gtm_maxstr.c0000755000032200000250000000627514342376331016411 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "gtm_string.h" #include "error.h" #include "gtm_maxstr.h" /* GT.M string buffer stack where each entry is allocated geometrically on the need basis */ GBLDEF mstr maxstr_buff[MAXSTR_STACK_SIZE]; GBLDEF int maxstr_stack_level = -1; /* This handler is to release the heap storage allocated within a function in case of errors. * The suggested protocol is that after a function is done with using the string buffer, the macro * MAXSTR_BUFF_FINI used in the function epilog releases the malloc'd storage. But if an error * occurs within the function body, the function epilog is not executed so this condition handler * should do the same so that the storage is not kept dangling. */ CONDITION_HANDLER(gtm_maxstr_ch) { START_CH(TRUE); if (maxstr_buff[maxstr_stack_level].addr) { free(maxstr_buff[maxstr_stack_level].addr); maxstr_buff[maxstr_stack_level].addr = NULL; } maxstr_buff[maxstr_stack_level].len = 0; maxstr_stack_level--; NEXTCH; } /* The routine checks if the currently available buffer (either 32K automatic or >32K malloc'd) * is sufficient for the new space requirement. If not sufficient, it keeps doubling the buffer size * and checks until a new buffer size is large enough to accommodate the incoming string. It then * allocates the new buffer and releases the previously malloc'd buffer. * Note that if strings fit within 32K, the automating buffer is used and no heap storage is used. * Parameters: * * space_needed - buffer space needed to accommodate the string that is about to be written * buff - address of the pointer to the beginning of the buffer. If reallocation occurs, buff will be * modified to point to the reallocated buffer. * space_occupied - how full is the buffer? * * Returns the new allocated buffer size. */ int gtm_maxstr_alloc(int space_needed, char** buff, int space_occupied) { int new_buff_size; if (space_needed > (maxstr_buff[maxstr_stack_level].len - space_occupied)) { /* Existing buffer is not sufficient. reallocate the buffer with the double the size */ new_buff_size = maxstr_buff[maxstr_stack_level].len; while (space_needed > new_buff_size - space_occupied) new_buff_size += new_buff_size; if (NULL != maxstr_buff[maxstr_stack_level].addr) { assert(maxstr_buff[maxstr_stack_level].addr == *buff); maxstr_buff[maxstr_stack_level].addr = (char *)malloc(new_buff_size); memcpy(maxstr_buff[maxstr_stack_level].addr, *buff, space_occupied); free(*buff); } else { maxstr_buff[maxstr_stack_level].addr = (char *)malloc(new_buff_size); memcpy(maxstr_buff[maxstr_stack_level].addr, *buff, space_occupied); } *buff = maxstr_buff[maxstr_stack_level].addr; maxstr_buff[maxstr_stack_level].len = new_buff_size; } return maxstr_buff[maxstr_stack_level].len; } fis-gtm-V7.0-005/sr_port/gtm_maxstr.h0000755000032200000250000001051014342376331016401 0ustar librarygtc/**************************************************************** * * * Copyright 2003 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GTM_MAXSTR_INCLUDED #define GTM_MAXSTR_INCLUDED /* The following macros should be used whenever an automatic string buffer * of size MAX_STRLEN needs to be declared in a function. Given that MAX_STRLEN * is enhanced to 1MB, allocating 1MB of local buffer on stack is not viable and * may lead to C stack overflows. The following macros employ an adaptive * scheme where an inital buffer of size 32K is allocated on stack and whenever * the buffer is found to be insufficient, the algorithm reallocates the buffer in * malloc'd space by doubling the previous size. * * If the string fits within 32K, there is [almost] no additional penalty since * the buffer is on stack as before. * * For any future requirement of MAX_STRLEN automatic buffers, the following macros * should be used. An example below: * * func() * { * char buffer[MAX_STRLEN]; * mstr src, dst; * * ... * dst.addr = &buffer[0]; * memcpy(dst.addr, src.addr, len) * ... * * return * } * * should be replaced something like * * #include "gtm_maxstr.h" * func() * { * MAXSTR_BUFF_DECL(buffer); * mstr src, dst; * * MAXSTR_BUFF_INIT; * ... * ... * dst.addr = &buffer[0]; * MAXSTR_BUFF_ALLOC(src.len, &dst.addr, 0); * ... * ... * MAXSTR_BUFF_FINI; * return; * } * */ /* The maximum nested depth of MAXSTR_BUFF_INIT/MAXSTR_BUFF_FINI allowed. We do not expect * many nesting levels. When the buffer stack overflows, GT.M asserts. */ #define MAXSTR_STACK_SIZE 10 GBLREF mstr maxstr_buff[]; /* Buffer stack for nested MAXSTR_BUFF_INIT/MAXSTR_BUFF_FINI */ /* Each entry in the buffer stack where each entry points to the buffer and it's size. Note that * although mstr is chosen as the entry type, it does not represent a GT.M string in the * traditional sense. The addr field point to the malloc'd buffer and is NULL if no * reallocation occured, i.e. buffer lies on the stack. The len field stores the current * buffer size which can grow geometrically. */ GBLREF int maxstr_stack_level; /* Current (0-index based) depth of nested MAXSTR_BUFF_INIT/MAXSTR_BUFF_FINI */ #define MAXSTR_BUFF_DECL(var) char var[MAX_STRBUFF_INIT]; #define MAXSTR_BUFF_INIT \ { \ ESTABLISH(gtm_maxstr_ch); \ maxstr_stack_level++; \ assert(maxstr_stack_level < MAXSTR_STACK_SIZE); \ maxstr_buff[maxstr_stack_level].len = MAX_STRBUFF_INIT; \ maxstr_buff[maxstr_stack_level].addr = NULL; \ } #define MAXSTR_BUFF_INIT_RET \ { \ ESTABLISH_RET(gtm_maxstr_ch, -1); \ maxstr_stack_level++; \ assert(maxstr_stack_level < MAXSTR_STACK_SIZE); \ maxstr_buff[maxstr_stack_level].len = MAX_STRBUFF_INIT; \ maxstr_buff[maxstr_stack_level].addr = NULL; \ } /* The following macro checks whether the existing available buffer is sufficient * and if not, it reallocates the buffer to the sufficient size. * space_needed - buffer space needed to accommodate the string that is about to be written. * buff - pointer to the beginning of the buffer. If reallocation occurs, buff will be * modified to point to the reallocated buffer. * space_occupied - how full is the buffer? * returns - size of the allocated buffer (whether reallocated or not). */ #define MAXSTR_BUFF_ALLOC(space_needed, buff, space_occupied) \ gtm_maxstr_alloc((space_needed), &(buff), (space_occupied)) #define MAXSTR_BUFF_FINI \ { \ if (maxstr_buff[maxstr_stack_level].addr) \ { \ free(maxstr_buff[maxstr_stack_level].addr); \ maxstr_buff[maxstr_stack_level].addr = NULL; \ } \ maxstr_buff[maxstr_stack_level].len = 0; \ maxstr_stack_level--; \ REVERT; /* gtm_maxstr_ch() */ \ } int gtm_maxstr_alloc(int space_needed, char** buff, int space_occupied); #endif /* GTM_MAXSTR_INCLUDED */ fis-gtm-V7.0-005/sr_port/gtm_memcmp.c0000755000032200000250000000165714342376331016350 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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. * * * ****************************************************************/ /* gtm_memcmp - GT.M interlude to C library memcmp function. * * gtm_memcmp is an interlude to the supplied C library memcmp that * prevents memory addressing errors that can occur when the length * of the items to be compared is zero and the supplied function * does not first validate its input parameters. */ #include "mdef.h" #include "gtm_string.h" #undef memcmp int gtm_memcmp (const void *a, const void *b, size_t len) { return (int)(len == 0 ? len : memcmp(a, b, len)); } fis-gtm-V7.0-005/sr_port/gtm_memcpy_validate_and_execute.c0000644000032200000250000000413314342376331022566 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2018 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" #define BYPASS_MEMCPY_OVERRIDE /* Want to run original system memcpy() after checks */ #include "gtm_string.h" #include "stringpool.h" #include "gtmdbglvl.h" #include "gtm_memcpy_validate_and_execute.h" #ifdef DEBUG /* Is only a debugging routine - nothing to see here for a production build - move along */ GBLREF uint4 gtmDebugLevel; /* Identify memcpy() invocations that should be memmove() instead. If this routine assert fails, the arguments * overlap so should be converted to memmove(). One exception to that rule which is currently bypassed is when * source and target are equal. There are no known implementation of memcpy() that would break in such a * condition so since at least two of these currently exist in GT.M (one in gtmcrypt.h on UNIX and one in * mu_cre_file on VMS), this routine does not cause an assert fail in that case. */ void *gtm_memcpy_validate_and_execute(void *target, const void *src, size_t len) { /* Unless specifically bypassed, in DEBUG, disallow memcpy() larger than max positive integer (2GB) */ assert((GDL_AllowLargeMemcpy & gtmDebugLevel) || ((0 <= (signed)len) && (MAXPOSINT4 >= len))); if (target == src) /* Allow special case to go through but avoid actual memcpy() call */ return target; assert(((char *)(target) > (char *)(src)) ? ((char *)(target) >= ((char *)(src) + (len))) : ((char *)(src) >= ((char *)(target) + (len)))); assert(((unsigned char *)target < stringpool.base) || ((unsigned char *)target > stringpool.top) || (((unsigned char *)target + len) <= stringpool.top)); return memcpy(target, src, len); } #endif fis-gtm-V7.0-005/sr_port/gtm_memcpy_validate_and_execute.h0000644000032200000250000000111614342376331022571 0ustar librarygtc/**************************************************************** * * * Copyright 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #ifndef GTM_MVAE_INCLUDED #define GTM_MVAE_INCLUDED void *gtm_memcpy_validate_and_execute(void *target, const void *src, size_t len); #endif fis-gtm-V7.0-005/sr_port/gtm_netdb.h0000755000032200000250000000617714342376331016175 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ /* gtm_netdb.h - interlude to system header file. */ #ifndef GTM_NETDBH #define GTM_NETDBH #include #define MAX_GETHOST_TRIES 8 #define SA_MAXLEN NI_MAXHOST /* NI_MAXHOST is 1025, large enough to hold any IPV6 address format * e.g.(123:567:901:345:215:0:0:0) */ #define SA_MAXLITLEN NI_MAXHOST /* large enough to hold any host name, e.g. * host name google: dfw06s16-in-x12.1e100.net */ #define USR_SA_MAXLITLEN 128 /* maximum size of host GTM user can specify * the reason why the number is so small is because the host name size * is stored as one byte in socket parameter list (refer to iosocket_use) */ #ifdef VMS #define VMS_MAX_TCP_IO_SIZE (64 * 1024 - 512) /* Hard limit for TCP send or recv size. On some implementations, the limit * is 64K - 1, on others it is 64K - 512. We take the conservative approach * and choose the lower limit */ #endif /* Macro to issue an rts_error_csa() with the results of getaddrinfo() or getnameinfo(). * Takes ERR_GETADDRINFO or ERR_GETNAMEINFO as the mnemonic. */ #define RTS_ERROR_ADDRINFO(CSA, MNEMONIC, ERRCODE) \ { \ if (EAI_SYSTEM == ERRCODE) \ rts_error_csa(CSA_ARG(CSA) VARLSTCNT(4) MNEMONIC, 0, errno, 0); \ else \ rts_error_csa(CSA_ARG(CSA) VARLSTCNT(6) MNEMONIC, 0, \ ERR_TEXT, 2, RTS_ERROR_STRING(gai_strerror(ERRCODE))); \ } /* Macro to issue an ERR_GETADDRINFO and continue (no rts_error) */ #define GTM_PUTMSG_CSA_ADDRINFO(CSA, MNEMONIC, ERRCODE, HOSTINFO) \ { \ if (EAI_SYSTEM == ERRCODE) \ gtm_putmsg_csa(CSA_ARG(CSA) VARLSTCNT(8) MNEMONIC, 0, errno, 0, \ ERR_TEXT, 2, LEN_AND_STR(HOSTINFO)); \ else \ gtm_putmsg_csa(CSA_ARG(CSA) VARLSTCNT(10) MNEMONIC, 0, \ ERR_TEXT, 2, RTS_ERROR_STRING(gai_strerror(ERRCODE)), \ ERR_TEXT, 2, LEN_AND_STR(HOSTINFO)); \ } /* Same as above, but with a literal string context description. */ #define RTS_ERROR_ADDRINFO_CTX(CSA, MNEMONIC, ERRCODE, CONTEXT) \ { \ if (EAI_SYSTEM == ERRCODE) \ rts_error_csa(CSA_ARG(CSA) VARLSTCNT(8) MNEMONIC, 0, \ ERR_TEXT, 2, RTS_ERROR_LITERAL(CONTEXT), errno, 0); \ else \ rts_error_csa(CSA_ARG(CSA) VARLSTCNT(10) MNEMONIC, 0, \ ERR_TEXT, 2, RTS_ERROR_LITERAL(CONTEXT), \ ERR_TEXT, 2, RTS_ERROR_STRING(gai_strerror(ERRCODE))); \ } /* Get either string for either system or gai error */ #define TEXT_ADDRINFO(TEXT, ERRCODE, SAVEERRNO) \ { \ if (EAI_SYSTEM == ERRCODE) \ TEXT = (char *)STRERROR(SAVEERRNO); \ else \ TEXT = (char *)gai_strerror(ERRCODE); \ } #endif fis-gtm-V7.0-005/sr_port/gtm_newintrinsic.c0000644000032200000250000001374714342376333017610 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "mv_stent.h" #include "stack_frame.h" #include "tp_frame.h" #include "gtm_string.h" #include "gtm_newintrinsic.h" #include "op.h" GBLREF mv_stent *mv_chain; GBLREF unsigned char *stackbase, *stacktop, *msp, *stackwarn; GBLREF stack_frame *frame_pointer; GBLREF tp_frame *tp_pointer; GBLREF symval *curr_symval; GBLREF uint4 dollar_tlevel; #ifdef GTM_TRIGGER GBLREF mval dollar_ztwormhole; #endif error_def(ERR_STACKCRIT); error_def(ERR_STACKOFLOW); /* Note this module follows the basic pattern of op_newvar which handles the same function except for local vars instead of intrinsic vars. */ void gtm_newintrinsic(mval *intrinsic) { mv_stent *mv_st_ent, *mvst_tmp, *mvst_prev; stack_frame *fp, *fp_prev, *fp_fix; tp_frame *tpp; unsigned char *old_sp, *top; int indx; int4 shift_size; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(intrinsic); if (frame_pointer->type & SFT_COUNT) { /* Current (youngest) frame is NOT an indirect frame. Create restore entry for intrinsic var. */ PUSH_MV_STENT(MVST_MSAV); mv_chain->mv_st_cont.mvs_msav.v = *intrinsic; mv_chain->mv_st_cont.mvs_msav.addr = intrinsic; } else { /* Current (youngest) frame IS an indirect frame. The situation is more complex because this is not a true stackframe. It has full access to the base "counted" frame's vars and any new done here must behave as if it were done in the base/counted frame. To accomplish this, we actually find the base frame we are executing in, then shift all frames younger than that up by the size of the mvstent entry we need to save/restore the value being new'd and then go into each frame modified and fixup all the addresses. */ fp = frame_pointer; fp_prev = fp->old_frame_pointer; assert(fp_prev); /* Find relevant base (counted) frame */ while (!(fp_prev->type & SFT_COUNT)) { fp = fp_prev; fp_prev = fp->old_frame_pointer; assert(fp_prev); } /* top is beginning of earliest indirect stackframe before counted base frame. It is the point where we will shift to make room to insert an mv_stent into the base frame. */ top = (unsigned char *)(fp + 1); old_sp = msp; shift_size = mvs_size[MVST_MSAV]; msp -= shift_size; if (msp <= stackwarn) { if (msp <= stacktop) { msp = old_sp; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_STACKOFLOW); } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_STACKCRIT); } /* Ready, set, shift the younger indirect frames to make room for mv_stent */ memmove(msp, old_sp, top - (unsigned char *)old_sp); mv_st_ent = (mv_stent *)(top - shift_size); mv_st_ent->mv_st_type = MVST_MSAV; ADJUST_FRAME_POINTER(frame_pointer, shift_size); /* adjust all the pointers in all the stackframes that were moved */ for (fp_fix = frame_pointer; fp_fix != fp_prev; fp_fix = fp_fix->old_frame_pointer) { if ((unsigned char *)fp_fix->l_symtab < top && (unsigned char *)fp_fix->l_symtab > stacktop) fp_fix->l_symtab = (ht_ent_mname **)((char *)fp_fix->l_symtab - shift_size); if (fp_fix->temps_ptr < top && fp_fix->temps_ptr > stacktop) fp_fix->temps_ptr -= shift_size; if (fp_fix->vartab_ptr < (char *)top && fp_fix->vartab_ptr > (char *)stacktop) fp_fix->vartab_ptr -= shift_size; if ((unsigned char *)fp_fix->old_frame_pointer < top && (char *)fp_fix->old_frame_pointer > (char *)stacktop) { ADJUST_FRAME_POINTER(fp_fix->old_frame_pointer, shift_size); } } /* Adjust stackframe and mvstent pointers in relevant tp_frame blocks */ assert(((NULL == tp_pointer) && !dollar_tlevel) || ((NULL != tp_pointer) && dollar_tlevel)); for (tpp = tp_pointer; (tpp && ((unsigned char *)tpp->fp < top)); tpp = tpp->old_tp_frame) { if ((unsigned char *)tpp->fp > stacktop) tpp->fp = (struct stack_frame_struct *)((char *)tpp->fp - shift_size); /* Note low check for < top may be superfluous here but without a test case to verify, I feel better leaving it in. SE 8/2001 */ if ((unsigned char *)tpp->mvc < top && (unsigned char *)tpp->mvc > stacktop) tpp->mvc = (struct mv_stent_struct *)((char *)tpp->mvc - shift_size); } /* Put new mvstent entry on (into) the mvstent chain */ if ((unsigned char *)mv_chain >= top) { /* Just put new entry on end of chain which preceeds our base frame */ mv_st_ent->mv_st_next = (unsigned int)((char *)mv_chain - (char *)mv_st_ent); mv_chain = mv_st_ent; } else { /* One of the indirect frames has mv_stents associated with it so we have to find the appropriate insertion point for this frame. */ fp = (stack_frame *)((char *)fp - shift_size); mv_chain = (mv_stent *)((char *)mv_chain - shift_size); mvst_tmp = mv_chain; mvst_prev = (mv_stent *)((char *)mvst_tmp + mvst_tmp->mv_st_next); while (mvst_prev < (mv_stent *)fp) { mvst_tmp = mvst_prev; mvst_prev = (mv_stent *)((char *)mvst_tmp + mvst_tmp->mv_st_next); } mvst_tmp->mv_st_next = (unsigned int)((char *)mv_st_ent - (char *)mvst_tmp); mv_st_ent->mv_st_next = (unsigned int)((char *)mvst_prev - (char *)mv_st_ent + shift_size); } /* Save current values of intrinsic var in the inserted mv_stent */ mv_st_ent->mv_st_cont.mvs_msav.v = *intrinsic; mv_st_ent->mv_st_cont.mvs_msav.addr = intrinsic; } /* Clear the intrinsic var's current value if not $ZTWORMHOLE or $ETRAP */ if ((&(TREF(dollar_etrap)) != intrinsic) GTMTRIG_ONLY(&& (&dollar_ztwormhole != intrinsic))) { intrinsic->mvtype = MV_STR; intrinsic->str.len = 0; } return; } fis-gtm-V7.0-005/sr_port/gtm_newintrinsic.h0000755000032200000250000000074614342376331017611 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ void gtm_newintrinsic(mval *intrinsic); fis-gtm-V7.0-005/sr_port/gtm_putmsg_list.h0000755000032200000250000000107714342376331017445 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ #ifndef GTM_PUTMSG_LIST_H #define GTM_PUTMSG_LIST_H void gtm_putmsg_list(void *csa, int arg_count, va_list var); #endif fis-gtm-V7.0-005/sr_port/gtm_pwd.h0000755000032200000250000000154414342376331015664 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2008 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. * * * ****************************************************************/ /* gtm_pwd.h - interlude to system header file. */ #ifndef GTM_PWDH #define GTM_PWDH #include #define GETPWUID(uid,getpwuid_res) (getpwuid_res = getpwuid(uid)) /* #define to be gtm_getpwuid to serve as a wrapper for blocking signals since getpwuid is not signal-safe. */ #define getpwuid gtm_getpwuid struct passwd *gtm_getpwuid(uid_t uid); /* Define prototype of "gtm_getpwuid" here */ #endif fis-gtm-V7.0-005/sr_port/gtm_relqueopi.c0000644000032200000250000000564414342376331017074 0ustar librarygtc/**************************************************************** * * * Copyright 2008 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. * * * ****************************************************************/ /* gtm_relqueopi - C-callable relative queue interlocked routines * * These routines perform interlocked operations on doubly-linked * relative queues. They are designed to emulate the VAX machine * instructions (and corresponding VAX C library routines) after * which they are named. * * insqhi - insert entry into queue at head, interlocked * insqti - insert entry into queue at tail, interlocked * remqhi - remove entry from queue at head, interlocked * remqti - remove entry from queue at tail, interlocked */ #include "mdef.h" #include "relqueopi.h" #ifdef DEBUG /* An element that is not present in the queue should have its fl,bl fields zero. * An element that is present in the queue should have its fl,bl fields non-zero. * Ensure/Check this property for all queue operations. * This will help catch a case when an element that is already present in the queue is re-added into the same queue. * Such operations will cause queue corruption and since the queue is in shared memory (given that the interlocked * queue operations are being done), could cause shared memory corruption and database damage (D9H03-002644). */ int gtm_insqhi(que_ent_ptr_t new, que_head_ptr_t base) { int4 status; assert(0 == new->fl); assert(0 == new->bl); status = SYS_INSQHI(new, base); /* We cannot assert that new->fl and new->bl are non-zero at this point since they * could be concurrently removed from the queue right after we inserted it above. */ return status; } int gtm_insqti(que_ent_ptr_t new, que_head_ptr_t base) { int4 status; assert(0 == new->fl); assert(0 == new->bl); status = SYS_INSQTI(new, base); /* We cannot assert that new->fl and new->bl are non-zero at this point since they * could be concurrently removed from the queue right after we inserted it above. */ return status; } void_ptr_t gtm_remqhi(que_head_ptr_t base) { que_ent_ptr_t ret; ret = SYS_REMQHI(base); if (NULL != ret) { # ifndef UNIX /* in Unix, relqueopi.c already does the rest of fl,bl to 0 just before releasing the swap lock */ ret->fl = 0; ret->bl = 0; # endif assert(0 == ret->fl); assert(0 == ret->bl); } return (void_ptr_t)ret; } void_ptr_t gtm_remqti(que_head_ptr_t base) { que_ent_ptr_t ret; ret = SYS_REMQTI(base); if (NULL != ret) { # ifndef UNIX /* in Unix, relqueopi.c already does the rest of fl,bl to 0 just before releasing the swap lock */ ret->fl = 0; ret->bl = 0; # endif assert(0 == ret->fl); assert(0 == ret->bl); } return (void_ptr_t)ret; } #endif fis-gtm-V7.0-005/sr_port/gtm_rename.h0000755000032200000250000000231214342376331016333 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2018 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. * * * ****************************************************************/ #ifndef GTM_RENAME_H_INCLUDED #define GTM_RENAME_H_INCLUDED /* gtm_rename.h */ #define RENAME_SUCCESS 0 #define RENAME_NOT_REQD 1 #define RENAME_FAILED 2 #define JNLSWITCH_TM_FMT "_%Y%j%H%M%S" /* yearjuliandayhoursminutesseconds */ #define JNLSWITCH_TM_FMT_LEN 14 /* SIZE of string produced by STRFTIME(JNLSWITCH_TM_FNT) */ int rename_file_if_exists(char *org_fn, int org_fn_len, char *rename_fn, int *rename_fn_len, uint4 *ustatus); uint4 gtm_rename(char *org_fn, int org_fn_len, char *rename_fn, int rename_len, uint4 *ustatus); uint4 prepare_unique_name(char *org_fn, int org_fn_len, char *prefix, char *suffix, char *rename_fn, int *rename_fn_len, jnl_tm_t now, uint4 *ustatus); #endif fis-gtm-V7.0-005/sr_port/gtm_repl.h0000644000032200000250000000403414342376331016026 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2021 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. * * * ****************************************************************/ #ifndef GTM_REPL_H_INCLUDED #define GTM_REPL_H_INCLUDED #ifdef GTM_TLS #include "gtm_tls.h" typedef enum { REPLTLS_RENEG_STATE_NONE, REPLTLS_WAITING_FOR_RENEG_TIMEOUT, REPLTLS_WAITING_FOR_RENEG_ACK, REPLTLS_SKIP_RENEGOTIATION, REPLTLS_WAITING_FOR_RENEG_COMPLETE /* Used only by the receiver. */ } repl_tls_reneg_state; typedef struct repl_tls_info_struct { char id[MAX_TLSID_LEN]; boolean_t plaintext_fallback; boolean_t enabled; boolean_t notls_retry; repl_tls_reneg_state renegotiate_state; gtm_tls_socket_t *sock; } repl_tls_info_t; GBLREF repl_tls_info_t repl_tls; error_def(ERR_TLSCLOSE); #define REPL_TLS_REQUESTED ('\0' != repl_tls.id[0]) #define CLEAR_REPL_TLS_REQUESTED repl_tls.id[0] = '\0' #define REPL_TLS_ENABLED (repl_tls.enabled) #define CLEAR_REPL_TLS_ENABLED repl_tls.enabled = FALSE #define PLAINTEXT_FALLBACK (repl_tls.plaintext_fallback) #define DEFAULT_RENEGOTIATE_TIMEOUT (2 * 60) /* About 2 hours between renegotiation. */ #define MIN_RENEGOTIATE_TIMEOUT 1 #define ISSUE_REPLNOTLS(ERRID, STR1, STR2) \ { \ if (!PLAINTEXT_FALLBACK) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERRID, 4, LEN_AND_LIT(STR1), LEN_AND_LIT(STR2)); \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) MAKE_MSG_WARNING(ERRID), 4, LEN_AND_LIT(STR1), LEN_AND_LIT(STR2)); \ } void repl_log_tls_info(FILE *logfp, gtm_tls_socket_t *socket); int repl_do_tls_handshake(FILE *logfp, int sock_fd, boolean_t do_accept, int *poll_direction); void repl_do_tls_init(FILE *logfp); #endif /* GTM_TLS */ #endif fis-gtm-V7.0-005/sr_port/gtm_reservedDB.h0000644000032200000250000001412314342376331017111 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2018 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. * * * ****************************************************************/ /* Header file for things relating to reserved databases (e.g. statsDB) that are created when they are opened and * are deleted when the last user closes it. */ #ifndef GTM_RESERVEDDB_H #define GTM_RESERVEDDB_H /* ReservedDB flags applicable to gd_region->reservedDBFlags */ enum { RDBF_AUTODB = 0x01, /* This DB is auto-created */ RDBF_NOSTATS = 0x02, /* This DB does not collect global stats so has no statsDB */ RDBF_STATSDB = 0x04 /* This is a statsDB (must have AUTODB and NOSTATS also) */ }; #define RDBF_STATSDB_MASK (RDBF_AUTODB | RDBF_NOSTATS | RDBF_STATSDB) /* This is a statsDB */ #define STATSDB_ERROR_RATE 99 /* a number of identical statsDB error messages to skip/throttle before the next output */ /* Possible errors we encounter that prevent us from setting baseDBnl->statsdb_fnname[.len] */ enum { FNERR_NOERR = 0, /* No error recorded */ FNERR_NOSTATS, /* BaseDB has NOSTATS set */ FNERR_STATSDIR_TRNFAIL, /* Unable to translate $gtm_statsdir (should never happen) */ FNERR_STATSDIR_TRN2LONG, /* Translation of $gtm_statsdir too long (should never happen) */ FNERR_INV_BASEDBFN, /* BaseDBfn had no '/' making parse fail (should never happen) */ FNERR_FTOK_FAIL, /* The STAT() in gtm_ftok() failed (no rc) (should never hapen) */ FNERR_FNAMEBUF_OVERFLOW /* Not enough space to add the statsdb fname to fname buffer */ }; #define FNERR_NOSTATS_TEXT "The base database has NOSTATS set - should not be trying to open statistics database" #define FNERR_STATSDIR_TRNFAIL_TEXT "Error attempting to translate $gtm_statsdir" #define FNERR_STATSDIR_TRN2LONG_TEXT "Translation of $gtm_statsdir was too long to fit in buffer" #define FNERR_INV_BASEDBFN_TEXT "Unable to parse the base database filename" #define FNERR_FTOK_FAIL_TEXT "Failure generating FTOK value for $gtm_statsdir" #define FNERR_FNAMEBUF_OVERFLOW_TEXT "Buffer overflow detected adding statistics database filename (hash..gst) to " \ "filename buffer" /* Global name used for shared global stats in statsDB MM database */ #define STATSDB_GBLNAME "%YGS" #define STATSDB_GBLNAME_LEN (SIZEOF(STATSDB_GBLNAME) - 1) #define STATSDB_FNAME_SUFFIX ".gst" #define RESERVED_NAMESPACE "%Y" #define RESERVED_NAMESPACE_LEN (SIZEOF(RESERVED_NAMESPACE) - 1) /* The maximum size of $gtm_statsdir is MAX_FN_LEN minus a minimal statsDB fname */ #define MAX_STATSDIR_LEN (MAX_FN_LEN - 8 /* hash */ - STRLEN(".a.dat") - STRLEN(STATSDB_FNAME_SUFFIX)) /* Size of the minimum additional record that can fit in a statsDB block after a record has been added to it. Use this * value to pad the first record in the block to prevent another record from being added. The '3' being added in is for * the single byte difference in the key that must exist plus the two NULL bytes that end the key following it. */ #define MIN_STATSDB_REC (SIZEOF(rec_hdr) + 3 + SIZEOF(gvstats_rec_t)) /* Macroize statsDB identification so we can change how it is defined in the future */ #define IS_STATSDB_REG(REG) (RDBF_STATSDB_MASK == (REG)->reservedDBFlags) #define IS_STATSDB_CSA(CSA) (RDBF_STATSDB_MASK == (CSA)->reservedDBFlags) #define IS_RDBF_STATSDB(X) (RDBF_STATSDB & (X)->reservedDBFlags) /* X can be REG or CSA or CSD */ /* Macroize autoDB identification */ #define IS_AUTODB_REG(REG) (RDBF_AUTODB & (REG)->reservedDBFlags) /* Identify if a region name is lower-case (and thus for a statsDB) by just checking the first character of the region. * No need to check the entire region name as a region name is always all upper or all lower. */ #define IS_STATSDB_REGNAME(REGPTR) (ISLOWER_ASCII((REGPTR)->rname[0])) #define IS_BASEDB_REGNAME(REGPTR) (ISUPPER_ASCII((REGPTR)->rname[0])) /* Macros to "find" the statsDBreg from a baseDBreg or vice versa. We use two different names for the two directions * but they are the same code because of how the indexes are set up. The name difference is just to make clear what * is happening where the macros are being used. */ #define STATSDBREG_TO_BASEDBREG(STATSDBREG, BASEDBREG) \ MBSTART { \ int dbindx; \ gd_addr *owningGd; \ \ dbindx = (STATSDBREG)->statsDB_reg_index; \ owningGd = (STATSDBREG)->owning_gd; \ assert(NULL != owningGd); \ assert(INVALID_STATSDB_REG_INDEX != dbindx); \ /* If TREF(ok_to_see_statsdb_regs) is FALSE, "gd_load" would have squished "n_regions" \ * into half of the original (by removing the stats regions) so account for that below. \ */ \ assert(dbindx < (2 * owningGd->n_regions)); \ BASEDBREG = &(STATSDBREG)->owning_gd->regions[dbindx]; \ } MBEND #define BASEDBREG_TO_STATSDBREG(BASEDBREG, STATSDBREG) STATSDBREG_TO_BASEDBREG(BASEDBREG, STATSDBREG) /* If an attempt to initialize a statsDB occurs inside a transaction (dollar_tlevel is non-zero), allocate one of these * blocks and queue it to defer initialization until the transaction (outer-most level) is committed or abandoned. */ typedef struct statsDB_diqe /* Deferred init queue element */ { struct statsDB_diqe *next; /* Next of these blocks on queue */ struct gd_region_struct *baseDBreg; /* base region ptr */ struct gd_region_struct *statsDBreg; /* statsDB region ptr */ } statsDB_deferred_init_que_elem; /* To enable debugging macro (output to console) uncomment the following #define */ /* #define DEBUG_RESERVEDDB */ #ifdef DEBUG_RESERVEDDB # define DBGRDB(x) DBGFPF(x) # define DBGRDB_ONLY(x) x #else # define DBGRDB(x) # define DBGRDB_ONLY(x) #endif /* Debugging macro that supresses unlink/recreate of statsDB file if fail to create it (orphaned file). To use this, * uncomment define below. */ /* #define BYPASS_UNLINK_RECREATE_STATSDB */ #endif fis-gtm-V7.0-005/sr_port/gtm_savetraps.c0000755000032200000250000000177614342376331017104 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_savetraps.h" #include "gtm_newintrinsic.h" GBLREF boolean_t ztrap_explicit_null; /* Routine called when we need to save the current Xtrap (etrap or ztrap) but don't know which to save. */ void gtm_savetraps(void) { mval *intrinsic; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TREF(dollar_ztrap)).str.len || ztrap_explicit_null) intrinsic = &(TREF(dollar_ztrap)); else intrinsic = &(TREF(dollar_etrap)); gtm_newintrinsic(intrinsic); return; } fis-gtm-V7.0-005/sr_port/gtm_savetraps.h0000755000032200000250000000102114342376331017070 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GTM_SAVETRAPS_H #define GTM_SAVETRAPS_H void gtm_savetraps(void); #endif fis-gtm-V7.0-005/sr_port/gtm_select.h0000755000032200000250000000172514342376331016352 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ /* gtm_select.h - include Unix system header files needed by select(). */ #if defined(_AIX) || defined(__CYGWIN__) || defined(_UWIN) #include #elif defined(__hpux) #include #elif defined(__osf__) #include #include #elif defined(__sparc) || defined(__MVS__) #include #elif defined(__linux__) #include #include #include #elif defined(VMS) #include #include #else #error UNSUPPORTED PLATFORM #endif fis-gtm-V7.0-005/sr_port/gtm_sizeof.h0000644000032200000250000000471114342376331016365 0ustar librarygtc/**************************************************************** * * * Copyright 2009 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. * * * ****************************************************************/ #ifndef GTM_SIZEOF_H_INCLUDED #define GTM_SIZEOF_H_INCLUDED /* Note: sizeof returns a "UNSIGNED long" type by default. When one of the operand in a relational operation * (<, <= etc.) is a SIGNED type and the other is UNSIGNED, both operands are typecast to UNSIGNED (NOT SIGNED) * before performing the comparison. This has unexpected consequences. For example, if a variable XYZ is signed * and has a negative value of -1, one would expect an "if (X < sizeof(char))" to return TRUE (because X is * negative and sizeof of anything is positive) but it actually returns FALSE. We have gotten bit by this at least * twice (C9E09-002635 and C9J10-003208). Therefore we define a SIZEOF macro that returns a "SIGNED long" type and * use it throughout the code base. This way "if (X < SIZEOF(char))" will evaluate correctly irrespective of * whether X is signed or unsigned (if X is signed, since SIZEOF is also signed, a signed comparison occurs; * if X is unsigned, since SIZEOF is signed, C's typecasting rules cause a unsigned comparison to occur). * * In a platform where long is 64-bits and SIZEOF is used in comparisons involving 32-bit variables, a compiler * warning is issued for every 64->32 bit auto cast (zOS compiler for now). To avoid this warning, we define the * SIZEOF macro to return a "SIGNED int" type on zOS. This will take care of the most common sizeof usages. * Whenever SIZEOF needs to be used in expressions involving 64-bit pointer quantities, use ((INTPTR_T)SIZEOF(...)). * Whenever SIZEOF needs to be used in expressions involving 64-bit integer quantities, use ((long)SIZEOF(...)). * * Some modules (like getcaps.c, gtm_tparm.c etc) do not include mdef.h for reasons stated in the corresponding * files but yet use sizeof. To enable those modules to use the safer SIZEOF macro as well, we create a separate * header file gtm_sizeof.h that is included explicitly by them as well as by mdef.h. */ #if defined(__MVS__) # define SIZEOF(X) ((int)(sizeof(X))) #else # define SIZEOF(X) ((long)sizeof(X)) #endif #endif fis-gtm-V7.0-005/sr_port/gtm_socket.h0000755000032200000250000000641714342376331016366 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001, 2015 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. * * * ****************************************************************/ /* gtm_socket.h - interlude to system header file. */ #ifndef GTM_SOCKETH #define GTM_SOCKETH #ifdef VMS #include #else #include #endif #define BIND bind #define CONNECT connect #define ACCEPT accept #define RECVFROM recvfrom #define SENDTO sendto typedef struct sockaddr *sockaddr_ptr; #if defined(__osf__) && defined(__alpha) #define GTM_SOCKLEN_TYPE size_t #elif defined(VMS) #define GTM_SOCKLEN_TYPE size_t #else #define GTM_SOCKLEN_TYPE socklen_t #endif /* define macro on platforms to determine if it is an AF_UNIX domain socket problem with getsockname() */ #if defined(__linux__) || defined(VMS) # define IS_SOCKNAME_UNIXERROR(ERR) (FALSE) #elif defined(AIX) # define IS_SOCKNAME_UNIXERROR(ERR) ((EOPNOTSUPP == ERR) || (ENOTCONN == ERR)) #elif defined(__sun) || defined(__hpux) # define IS_SOCKNAME_UNIXERROR(ERR) ((EOPNOTSUPP == ERR) || (EINVAL == ERR)) #else # define IS_SOCKNAME_UNIXERROR(ERR) (EOPNOTSUPP == ERR) #endif #ifdef GTM_FD_TRACE /* Just like open and close were noted down in gtm_fcntl.h, note down all macros which we are redefining here and could * potentially have been conflictingly defined by the system header file "socket.h". The system define will be used * in gtm_fd_trace.c within the implementation of the GT.M interlude function. Currently none of these functions (socket) * are defined by the system so it is not theoretically necessary but they could be defined in the future. */ # undef socket /* in case this is already defined by */ # define socket gtm_socket #endif int gtm_socket(int domain, int type, int protocol); int gtm_connect(int socket, struct sockaddr *address, size_t address_len); /* BYPASSOK(connect) */ #if defined(VMS) && !defined(_SS_PAD2SIZE) /* No sockaddr_storage on OpenVMS 7.2-1, but we only support AF_INET on VMS, so use sockaddr_in. */ #define sockaddr_storage sockaddr_in /* getnameinfo() inexplicably throws an ACCVIO/NOPRIV on OpenVMS 7.2-1, so revert to the old API. */ #define GETNAMEINFO(SA, SALEN, HOST, HOSTLEN, SERV, SERVLEN, FLAGS, RES) \ { \ assert(((struct sockaddr *)(SA))->sa_family == AF_INET); \ assert((FLAGS & NI_NUMERICHOST) || (NULL == HOST)); \ assert((FLAGS & NI_NUMERICSERV) || (NULL == SERV)); \ assert(FLAGS & (NI_NUMERICHOST | NI_NUMERICSERV)); \ if ((FLAGS & NI_NUMERICHOST) && (NULL != HOST)) \ STRNCPY(HOST, inet_ntoa(((struct sockaddr_in *)(SA))->sin_addr), HOSTLEN); \ if ((FLAGS & NI_NUMERICSERV) && (NULL != SERV)) \ i2asc((uchar_ptr_t)(SERV), ntohs(((struct sockaddr_in *)(SA))->sin_port)); \ RES = 0; \ } #else #define GETNAMEINFO(SA, SALEN, HOST, HOSTLEN, SERV, SERVLEN, FLAGS, RES) \ { \ RES = getnameinfo(SA, SALEN, HOST, HOSTLEN, SERV, SERVLEN, FLAGS); \ } #endif #endif fis-gtm-V7.0-005/sr_port/gtm_stat.h0000755000032200000250000000131314342376331016037 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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. * * * ****************************************************************/ /* gtm_stat.h - interlude to system header file. */ #ifndef GTM_STATH #define GTM_STATH #include #define CHMOD chmod #define FCHMOD fchmod #define MKDIR mkdir #define Stat stat #define MKNOD mknod #define LSTAT lstat #endif fis-gtm-V7.0-005/sr_port/gtm_stdlib.h0000755000032200000250000000442414342376331016353 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ /* gtm_stdlib.h - interlude to system header file. */ #ifndef GTM_STDLIBH #define GTM_STDLIBH #include #ifndef __CYGWIN__ # define GETENV getenv #else char *gtm_getenv(char *varname); # define GETENV gtm_getenv #endif #define ATOI atoi #define ATOL atol #define ATOF atof #ifdef UNIX /* If interrupted, this function has previously caused hangs to do a subsequent syslog() invocation from generic_signal_handler(), * so just defer interrupts to be safe. UNIX is a GT.M-specific compiler switch, which we expect to be undefined for any non-GT.M * compilation that might include this file. */ #define PUTENV(VAR, ARG) \ { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); \ VAR = putenv(ARG); \ ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); \ } #else # define PUTENV(VAR, ARG) \ { \ VAR = putenv(ARG); \ } #endif #define STRTOL strtol #define STRTOLL strtoll #define STRTOUL strtoul # if INT_MAX < LONG_MAX /* like Tru64 */ # define STRTO64L strtol # define STRTOU64L strtoul # else # define STRTO64L strtoll # define STRTOU64L strtoull # endif #define MKSTEMP(template,mkstemp_res) \ { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_MKSTEMP, prev_intrpt_state); \ mkstemp_res = mkstemp(template); \ ENABLE_INTERRUPTS(INTRPT_IN_MKSTEMP, prev_intrpt_state); \ } # if defined(STATIC_ANALYSIS) # define SYSTEM system # else # define SYSTEM gtm_system int gtm_system(const char *cmdline); # endif int gtm_system_internal(const char *sh, const char *opt, const char *rtn, const char *cmdline); void gtm_image_exit(int status); #define EXIT(x) gtm_image_exit(x) #endif fis-gtm-V7.0-005/sr_port/gtm_string.h0000755000032200000250000000552714342376331016405 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* If this is not the vax, define string.h. This is because the Vax has its own built-in instructions for string manipulation. */ #ifndef GTM_STRINGH #define GTM_STRINGH #include #define MAX_CHAR_LEN 4 /* bytes - e.g. translate can turn a 1 byte character into an up to 4 byte character */ #define STRERROR strerror #define STRCPY(DEST, SOURCE) strcpy((char *)(DEST), (char *)(SOURCE)) #define STRNCPY_STR(DEST, STRING, LEN) strncpy((char *)(DEST), (char *)(STRING), LEN) #define STRCMP(SOURCE, DEST) strcmp((char *)(SOURCE), (char *)(DEST)) #define STRNCMP_LIT(SOURCE, LITERAL) strncmp(SOURCE, LITERAL, SIZEOF(LITERAL) - 1) /* BYPASSOK */ /* Make sure that SIZEOF(SOURCE) > 0 or SOURCE != NULL before running. */ #define STRNCMP_LIT_FULL(SOURCE, LITERAL) strncmp(SOURCE, LITERAL, SIZEOF(LITERAL)) /* BYPASSOK */ #define STRNCMP_LIT_LEN(SOURCE, LITERAL, LENGTH) \ strncmp((const char *)(SOURCE), (const char *)(LITERAL), MIN(SIZEOF(LITERAL), LENGTH)) /* BYPASSOK */ #define STRNCMP_STR(SOURCE, STRING, LEN) strncmp(SOURCE, STRING, LEN) #define STRNCAT(DEST, SOURCE, LEN) strncat((char *)DEST, (const char *)SOURCE, (size_t)LEN) /* Ensure that our uses of STRTOK and STRTOK_R are not called inside a timer handler */ #define STRTOK_R(STR, DELIM, SAVE) (DBG_ASSERT(FALSE == timer_in_handler) strtok_r(STR, DELIM, SAVE)) /* We need to catch any memcpy() that is used when the source and target strings overlap in any fashion so we can change * them to a memmove. So in debug builds, assert fail if this is the case. */ #if defined(DEBUG) && !defined(BYPASS_MEMCPY_OVERRIDE) # include "gtm_memcpy_validate_and_execute.h" # ifdef memcpy # undef memcpy /* Some platforms like AIX create memcpy as a #define which needs removing before re-define */ # endif # define memcpy(TARGET, SRC, LEN) gtm_memcpy_validate_and_execute((void *)(TARGET), (const void *)(SRC), (LEN)) #endif /* The strnlen() function is POSIX-2008 */ #define STRNLEN(STR, MAXLEN, RSLT) RSLT = strnlen(STR, (size_t)MAXLEN) /* Returns the (int) casted result */ #define SSTRNLEN(STR, MAXLEN, RSLT) RSLT = (int)strnlen(STR, (size_t)MAXLEN) #define STRNDUP(STR, MAXLEN, DST) \ { \ size_t local_len; \ \ STRNLEN(STR, MAXLEN, local_len); \ DST = (char *) malloc(local_len + 1); \ memcpy(DST, STR, local_len); \ DST[local_len] = '\0'; \ } #endif fis-gtm-V7.0-005/sr_port/gtm_strings.h0000644000032200000250000000152014342376331016552 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2015 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. * * * ****************************************************************/ #ifndef GTM_STRINGSH #define GTM_STRINGSH #include #define STRCASECMP(SOURCE, DEST) strcasecmp((char *)(SOURCE), (char *)(DEST)) #define STRNCASECMP(SOURCE, DEST, LEN) strncasecmp(SOURCE, DEST, LEN) #define STRNCASECMP_LIT(SOURCE, LITERAL) strncasecmp(SOURCE, LITERAL, SIZEOF(LITERAL) - 1) /* BYPASSOK */ #endif fis-gtm-V7.0-005/sr_port/gtm_tempnam.c0000755000032200000250000000270514342376331016526 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_string.h" #include "gtm_stdio.h" #include "gtm_tempnam.h" /************************************************************** * gtm_tempnam() * Generates a temporary file name and places it in fullname * Note that fullname should be malloced by the caller * It is assumed to be of proper size (MAX_FN_LEN + 1) * **************************************************************/ void gtm_tempnam(char *dir, char *prefix, char *fullname) { static int temp_file_counter = 0; int len; char *ptr, def_prefix[] = "GTM_TEMP_"; if (NULL == dir) { len = SIZEOF(SCRATCH_DIR) - 1; memcpy(fullname, SCRATCH_DIR, len); } else { len = STRLEN(dir); memcpy(fullname, dir, len); } ptr = fullname + len; if (NULL == prefix) { prefix = def_prefix; len = SIZEOF(def_prefix) - 1; } else len = STRLEN(prefix); memcpy(ptr, prefix, len); ptr += len; SNPRINTF(ptr, MAX_FN_LEN - len, "_%d.tmp", temp_file_counter++); } fis-gtm-V7.0-005/sr_port/gtm_tempnam.h0000755000032200000250000000142714342376331016533 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GTM_TEMPNAM_H_INCLUDED #define GTM_TEMPNAM_H_INCLUDED void gtm_tempnam(char *dir, char *prefix, char *fullname); #if defined(UNIX) # define SCRATCH_DIR "/tmp/" #elif defined(VMS) # define SCRATCH_DIR "SYS$SCRATCH:" #else # error Unsupported Platform #endif #endif fis-gtm-V7.0-005/sr_port/gtm_text_alloc.h0000644000032200000250000000641314342376331017225 0ustar librarygtc/**************************************************************** * * * Copyright 2007, 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. * * * ****************************************************************/ #ifndef GTM_TEXT_ALLOC_H #define GTM_TEXT_ALLOC_H /* States a storage element may be in (which need to be different from malloc states). */ enum TextState {TextAllocated = 0x43, TextFree = 0x34}; /* Each allocated block (in the mmap build) has the following structure. The actual address returned to the user for allocation and supplied by the user for release is actually the storage beginning at the 'userStorage.userStart' area. This holds true even for storage that is truely mmap'd. */ typedef struct textElemStruct { /* This flavor of header is 16 bytes. This is required on IA64 and we have just adopted it for the other platforms as well as it is a minor expense given the sizes of chunks we are using */ int queueIndex; /* Index into TwoTable for this size of element */ enum TextState state; /* State of this block */ unsigned int realLen; /* Real (total) length of allocation */ int filler; union /* The links are used only when element is free */ { struct /* Free block information */ { struct textElemStruct *fPtr; /* Next storage element on free queue */ struct textElemStruct *bPtr; /* Previous storage element on free queue */ } links; unsigned char userStart; /* First byte of user useable storage */ } userStorage; } textElem; /* The below macros are used to allocate and release areas of storage that will be used to contain executable code. Traditionally, GTM has just used malloc() and free() and placed this code on the heap but some systems now protect against that (noteably Linux). On those platforms (and any others we choose to separate the executable storage pools from the regular heap storage), we use gtm_text_alloc() and gtm_text_free() to allocate and free executable storage. These modules use mmap() and munmap() for this purpose. Note that while use of the mmap() interface for executable storage does have some potential advantages for security (and as noted above, is required for some platforms to even function, because of the page aligned granularity of its requests, storage usage with gtm_text_alloc() is not as efficient as it is with regular heap based storage. For this reason, we only use this method on the required platforms rather than all. Replaceing the algorithms in gtm_text_alloc.c with ones not based on the buddy system could potentially alleviate these efficiency differences. */ #if defined(__linux__) || defined(__osf__) || defined(__MVS__) || defined(__CYGWIN__) # define GTM_TEXT_ALLOC(x) gtm_text_alloc(x) # define GTM_TEXT_FREE(x) gtm_text_free(x) void *gtm_text_alloc(size_t size); void gtm_text_free(void *addr); void printAllocInfo(void); # define COMP_GTA /* Build gtm_text_alloc() module */ # else # define GTM_TEXT_ALLOC(x) gtm_malloc(x) # define GTM_TEXT_FREE(x) gtm_free(x) #endif #endif /* GTM_TEXT_ALLOC_H */ fis-gtm-V7.0-005/sr_port/gtm_threadgbl.h0000644000032200000250000001501014342376331017014 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2015 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. * * * ****************************************************************/ #ifndef GTM_THREADGBL_included #define GTM_THREADGBL_included #ifndef NO_THREADGBL_DEFTYPES # include "gtm_threadgbl_deftypes.h" /* Avoided for gtm_threadgbl_deftypes.c which builds this header file */ #endif /* The following three macros allow access to global variables located in the thread-global array. The ultimate * goal of this array is to permit GTM to become thread-safe. That is not yet true but this framework is the first * step in taming our global variable use. In the future, these macros will change to their thread-safe counterparts * such that we can have different arrays per thread. * * The macros can be classified as declaration (DCL_THREADGBL_ACCESS), definition (SETUP_THREADGBL_ACCESS), and * usage (TADR, TREF, TAREF1, TAREF2, RFPTR, SFPTR, IVFPTR). * * DCL_THREADGBL_ACCESS - simple job is to declare the local variable used as an anchor to the thread global * array. It should be located in the declaration section of an entry point. * * SETUP_THREAD_GBL_ACCESS - Sets the value of the local variable used as an anchor. Today, this is just a * global var de-reference. In the future, it will use thread-logic to access a * thread related base var. This should be placed near the top of the executable * code for the routine - certainly before the first TREF macro. * TADR - Used to obtain the address of the element. * TREF - Used to dereference a global var (see notes at TREF macro for usage). * TAREF1/TAREF2 - Used to access array elements - TAREF1 for vectors, TAREF2 for 2 subscript access. * TLEN - Used to obtain length of total element for both one and two dimension arrays. Not * defined for non-arrays. * RFPTR - Used to reference a function pointer in an expression or conditional. * SFPTR - Used to set a new value into a function pointer. * IVFPTR - Used to invoke a function pointer. */ /* Note - access can also be made from assembler code though the mechanism differs. See $gtm_tools/gtmthreadgblasm.m.txt for * details. */ /* Declare local thread global anchor */ #define DCL_THREADGBL_ACCESS void *lcl_gtm_threadgbl /* Setup the local thread global anchor giving a value (and purpose in life) */ #define SETUP_THREADGBL_ACCESS lcl_gtm_threadgbl = gtm_threadgbl /* Reference a given global in the thread global array. There are a couple of different ways this macro can be * used. Note that its first term is a "de-reference" (aka "*"). As an example, say we have a global name defined * as follows: * * somestruct *gblname; * * and we wish to access gblname->subfield. If we code "TREF(gblname)->subfield", that would generate * * *((somestruct **)((char *)lcl_gtm_threadgbl + nnn))->subfield * * Note that the first dereference covers the entire expression instead of just the TREF expression. This is an incorrect * reference that hopefully produces a compiler error instead of a hard-to-find bug. What needs to be coded instead is * (TREF(gblname))->subfield which generates: * * (*((somestruct **)((char *)lcl_gtm_threadgbl + nnn)))->subfield * * This isolates the dereference to the expression it needs to refer to and produces an expression to correctly access the * desired field. However, if the global is not a pointer, but say, a boolean, then you CANNOT surround the TREF macro with * parens if the expression is on the left hand side of the "=" operator because that is not a valid LREF expression. So if * the field were a boolean, you could modify it as "TREF(somebool) = TRUE;". * * The general rule of thumb is if the global is a pointer being used to access a subfield, or if the global is a structure * itself and you are accessing subfields with a "." operator (e.g. (TREF(fnpca)).fnpcs[i].indx = i;) surround the TREF with (). * Otherwise* you can leave it unadorned. * * Note that function pointers present some odd constraints. You can cast something to a function pointer type but only * when you are going to actually invoke it (as the IVFPTR() macro does below). But this means that non-invoking references * (or assignments) cannot use casts to make types match. So we need 2 additional macros for reference in an expression (RFPTR) * or setting a function pointer (SFPTR) so we can control the necessary aspects of the expressions involved. See * $sr_unix/generic_signal_handler.c for uses of most of these macros used with function pointers. */ #define TREF(name) *((ggt_##name *)((char *)lcl_gtm_threadgbl + ggo_##name)) /* For address of item use TADR(name) macro */ #define TADR(name) ((ggt_##name *)((char *)lcl_gtm_threadgbl + ggo_##name)) /* For character length of character arrays (for arrays only) */ #define TLEN(name) (ggl_##name) /* For access to single dimension array (vector), use TAREF1 macro */ #define TAREF1(name, indx1) (TADR(name))[indx1] /* For access to 2 dimension array, use TAREF2 macro */ #define TAREF2(name, indx1, indx2) ((ggt_##name *)((char *)lcl_gtm_threadgbl + ggo_##name + /* Base addr of array */ \ (((indx1) - 1) * SIZEOF(ggt_##name) * ggd_##name)))[indx2] /* Row offset */ /* To set a function pointer, use SFPTR(name) macro - Used to copy an address INTO a function pointer */ #define SFPTR(name, value) *((char **)((char *)lcl_gtm_threadgbl + ggo_##name)) = (char *)(value) /* To reference a function pointer's value, use RFPTR(name) macro. Use only in rvalue RHS expressions. Does not produce * an lvalue suitable for LHS usage. See SFPTR() above if function pointer needs a new value. */ #define RFPTR(name) (ggt_##name (*)gga_##name)(*(char **)((char *)lcl_gtm_threadgbl + ggo_##name)) /* To invoke a function pointer, use IVFPTR(name) macro */ #define IVFPTR(name) ((ggf_##name)(*(char **)((char *)lcl_gtm_threadgbl + ggo_##name))) /* In the main routines for GTM and the utilities that need to initialize the thread global structure all other routines access, * this macro should be used in lieu of SETUP_THREADGBL_ACCESS. */ #define GTM_THREADGBL_INIT \ { \ gtm_threadgbl_init(); \ SETUP_THREADGBL_ACCESS; \ } #endif fis-gtm-V7.0-005/sr_port/gtm_threadgbl_defs.h0000644000032200000250000011214514342376331020024 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2022 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. * * * ****************************************************************/ /* Note since this table is included multiple times, it should not have "previously included" protection. * * When adding items to this table, please be aware of alignment issues. * * Notes on where to put added items: * * 1. Make organization defensible. Possible organizations: * a. Group like-types alphabetically. * b. Group related items (such as compiler, replication, etc. * 2. Because references will use an offset into this structure and since the "immediate offset" in * compiler generated instructions is usually limited to "smaller" values like less than 16K or * 32K or whatever (platform dependent), items near the top of the table should be reserved used for * the most commonly used items (e.g. cs_addrs, cs_data, gv_target, etc). * 3. Larger arrays/structures should go nearer the end. * 4. There are no other runtime dependencies on this order. The order of fields can be switched around * any way desired with just a rebuild. * 5. It is important for ANY DEBUG_ONLY fields to go at the VERY END. Failure to do this breaks gtmpcat. * 6. If a DEBUG_ONLY array is declared whose dimension is a macro, then it is necessary, for gtmpcat to work, * that the macro shouldn't be defined as DEBUG_ONLY. */ /* Priority access fields - commonly used fields in performance situations */ THREADGBLDEF(grabbing_crit, gd_region *) /* Region currently grabbing crit in (if any) */ /* Compiler */ THREADGBLDEF(blkmod_fail_level, int4) /* TP trace reporting element */ THREADGBLDEF(blkmod_fail_type, int4) /* TP trace reporting element */ THREADGBLDEF(block_level, int4) /* used to check embedded subroutine levels */ THREADGBLDEF(boolchain, triple) /* anchor for chain used by bx_boolop */ THREADGBLDEF(boolchain_ptr, triple *) /* pointer to anchor for chain used by bx_boolop */ THREADGBLDEF(bool_targ_anchor, tbp) /* anchor of targ chain for bool relocation */ THREADGBLDEF(bool_targ_ptr, tbp *) /* ptr->anchor of targ chain for bool relocation */ THREADGBLDEF(code_generated, boolean_t) /* flag that the compiler generated an object */ THREADGBLDEF(codegen_padlen, int4) /* used to pad code to section alignment */ THREADGBLDEF(compile_time, boolean_t) /* flag that the compiler's at work */ THREADGBLDEF(curtchain, triple *) /* pointer to anchor of current triple chain */ THREADGBLDEF(director_ident, mstr) /* look-ahead scanner mident from advancewindow */ THREADGBLDEF(director_mval, mval) /* look-ahead scanner mval from advancewindow*/ THREADGBLDEF(director_token, char) /* look-ahead scanner token from advancewindow */ THREADGBLDEF(dollar_zcstatus, int4) /* return status for zcompile and others */ THREADGBLDEF(expr_depth, unsigned int) /* expression nesting level */ THREADGBLDEF(expr_start, triple *) /* chain anchor for side effect early evaluation */ THREADGBLDEF(expr_start_orig, triple *) /* anchor used to test if there's anything hung on * expr_start */ THREADGBLDEF(defined_symbols, struct sym_table *) /* Anchor for symbol chain */ THREADGBLDEF(for_stack_ptr, oprtype **) /* part of FOR compilation nesting mechanism */ THREADGBLDEF(gtm_fullbool, unsigned int) /* controls boolean side-effect behavior defaults * to 0 (GTM_BOOL) */ THREADGBLDEF(ind_result, mval *) /* pointer to indirection return location */ THREADGBLDEF(ind_source, mval *) /* pointer to indirection source location */ THREADGBLDEF(indirection_mval, mval) /* used for parsing subscripted indirection */ THREADGBLDEF(last_source_column, int) /* parser tracker */ THREADGBLDEF(lexical_ptr, char *) /* parser character position */ THREADGBLDEF(linkage_first, struct linkage_entry *) /* Start of linkage (extern) list this routine */ THREADGBLDEF(linkage_last, struct linkage_entry *) /* Last added entry */ #ifdef USHBIN_SUPPORTED THREADGBLDEF(objhash_state, hash128_state_t) /* Seed value - progressive hash of object file */ #endif THREADGBLDEF(discard, boolean_t) /* parsing after literal FALSE postconditional */ THREADGBLDEF(pos_in_chain, triple) /* anchor used to restart after a parsing error */ THREADGBLDEF(rts_error_in_parse, boolean_t) /* flag to stop parsing current line */ THREADGBLDEF(s2n_intlit, boolean_t) /* type info from s2n for advancewindow */ THREADGBLDEF(routine_source_offset, uint4) /* offset of M source within literal text pool */ THREADGBLDEF(saw_side_effect, boolean_t) /* need side effect handling other than naked */ THREADGBLDEF(shift_side_effects, int) /* flag shifting of side-effects ahead of boolean * evaluation */ THREADGBLDEF(side_effect_base, boolean_t *) /* anchor side effect array: bin ops & func args */ THREADGBLDEF(side_effect_depth, uint4) /* current high water of side effect expr array */ THREADGBLDEF(side_effect_handling, int) /* side effect handling in actuallists, function * args & non-boolean binary operator operands */ THREADGBLDEF(source_buffer, mstr) /* source line buffer control */ THREADGBLDEF(source_error_found, int4) /* flag to partially defer compiler error */ THREADGBLDEF(temp_subs, boolean_t) /* flag temp storing of subscripts to preserve * current evaluation */ THREADGBLDEF(trigger_compile_and_link, boolean_t) /* A trigger compilation/link is active */ THREADGBLDEF(window_ident, mstr) /* current scanner mident from advancewindow */ THREADGBLDEF(window_mval, mval) /* current scanner mval from advancewindow */ THREADGBLDEF(window_token, char) /* current scanner token from advancewindow */ THREADGBLDEF(xecute_literal_parse, boolean_t) /* flag TRUE when trying what its name says */ THREADGBLDEF(fetch_control, fetch_ctrl) /* structure for managing lvn fetches */ /* Database */ THREADGBLDEF(dbinit_max_delta_secs, uint4) /* max time before we bail out in db_init */ THREADGBLDEF(dollar_zmaxtptime, int4) /* tp timeout in seconds */ THREADGBLAR1DEF(save_xfer_root, save_xfer_entry, DEFERRED_EVENTS) /* array of deferred events with the zeroth * acting as the root of a queue identified by the * _ptr item immediately following */ THREADGBLDEF(save_xfer_root_ptr, save_xfer_entry*) /* pointer to anchor; see immediately prior item */ THREADGBLDEF(ztimeout_timer_on, boolean_t) /* ztimeout has a gt_timer pending */ THREADGBLDEF(dollar_ztimeout, dollar_ztimeout_struct) /* holds ztimeout state info */ THREADGBLDEF(donot_write_inctn_in_wcs_recover, boolean_t) /* TRUE if wcs_recover should NOT write INCTN */ THREADGBLDEF(gbuff_limit, mval) /* holds a GTM_POOLLIMIT value for REORG or DBG */ THREADGBLDEF(gd_targ_tn, trans_num) /* number that is incremented for every gvcst_spr* * action. helps easily determine whether a region * has already been seen in this gvcst_spr* action. */ THREADGBLDEF(gd_targ_reg_array, trans_num *) /* Indicates which regions are already part of * current spanning-region database action. * Array is NULL if no spanning globals were * detected as part of opening global directory. */ THREADGBLDEF(gd_targ_reg_array_size, uint4) /* Size of current gd_targ_reg_array allocation. * Non-zero only if at least one spanning global * has been seen at time of gld open. * Note: the dimension is the # of regions involved * and not the # of bytes allocated in the array. */ THREADGBLDEF(gd_targ_addr, gd_addr *) /* current global directory reference. Needed by * name level $order or $zprevious to know inside * op_gvorder.c/op_zprevious.c whether the global * reference went through op_gvname/op_gvextnam. * Also needed by gvcst_spr_*.c etc. */ THREADGBLDEF(gd_targ_gvnh_reg, gvnh_reg_t *) /* Pointer to the gvnh_reg corresponding to the * unsubscripted gvn reference that last went * through op_gvname/op_gvnaked/op_gvextnam. * Set to a non-NULL value for spanning globals * and to NULL for non-spanning globals (because * it offers an easy way to check for spanning * globals in op_gv* functions before invoking * the corresponding gvcst_spr_* functions). * Needed by op_gvorder etc. to avoid having to * recompute this based on gd_header & gv_currkey. */ THREADGBLDEF(gd_targ_map, gd_binding *) /* map entry to which "gv_bind_subsname" bound * the subscripted gvn. Maintained ONLY for spanning * globals. Cannot be relied upon in case the * current reference is for a non-spanning global. * (i.e. is usable only if gd_targ_gvnh_reg->gvspan * is non-NULL). This is needed by gvcst_spr_* * functions to avoid recomputing the map (using * gv_srch_map) based on gd_header/gv_currkey. */ THREADGBLDEF(gtm_custom_errors, mstr) THREADGBLDEF(gv_extname_size, int4) /* part op_gvextname working memory mechanism */ THREADGBLDEF(gv_last_subsc_null, boolean_t) /* indicates whether the last subscript of * gv_currkey (aka $reference) is a NULL string */ THREADGBLDEF(gv_mergekey2, gv_key *) /* op_merge working memory */ THREADGBLDEF(gv_reorgkey, gv_key *) /* mu_swap_blk working memory */ THREADGBLDEF(gv_some_subsc_null, boolean_t) /* TRUE if SOME subscript other than the last is * NULL in gv_currkey (aka $REFERENCE). Note that * while "some" in var name might typically include * the last subscript, it does NOT in this case and * allows name to be kept shorter. */ THREADGBLDEF(gv_sparekey, gv_key *) /* gv_xform_key working memory */ THREADGBLDEF(gv_sparekey_mval, mval) /* gv_xform_key working memory */ THREADGBLDEF(gv_sparekey_size, int4) /* part gv_xform_key working memory mechanism */ THREADGBLDEF(gv_tporigkey_ptr, gv_key_buf *) /* copy of gv_currkey at outermost TSTART */ THREADGBLDEF(gv_tporig_extnam_str, mstr) /* copy of extnam_str at outermost TSTART */ THREADGBLDEF(in_gvcst_bmp_mark_free, boolean_t) /* May need to skip online rollback cleanup or * gvcst_redo_root_search on a restart */ THREADGBLDEF(in_gvcst_redo_root_search, boolean_t) /* TRUE if gvcst_redo_root_search is in C-stack */ THREADGBLDEF(in_op_gvget, boolean_t) /* TRUE if op_gvget() is a C-stack call ancestor */ THREADGBLDEF(issue_DBROLLEDBACK_anyways, boolean_t) /* currently set by MUPIP LOAD */ THREADGBLDEF(last_fnquery_return_subcnt, int) /* count subscript in last_fnquery_return_sub */ THREADGBLDEF(last_fnquery_return_varname, mval) /* returned varname of last $QUERY() */ THREADGBLDEF(nontprestart_count, uint4) /* non-tp restart counter */ THREADGBLDEF(nontprestart_log_first, int4) /* # of non-tp restarts logged unconditionally */ THREADGBLDEF(nontprestart_log_delta, int4) /* defines every n-th restart to be logged for * non-tp */ THREADGBLDEF(block_now_locked, cache_rec_ptr_t) /* db_csh_getn sets for secshr_sb_clnup exit */ THREADGBLDEF(ok_to_call_wcs_recover, boolean_t) /* Set to TRUE before a few wcs_recover callers. * Any call to wcs_recover in the final retry * assert to prevent cache recovery while in a * transaction and confuse things enough to cause * further restarts (which is out-of-design while * in the final retry). */ THREADGBLDEF(prev_gv_target, gv_namehead *) /* saves the last gv_target for debugging */ THREADGBLDEF(ready2signal_gvundef, boolean_t) /* TRUE if GET operation about to signal GVUNDEF */ THREADGBLDEF(redo_rootsrch_ctxt, redo_root_search_context) /* context to be saved and restored during * gvcst_redo_root_search */ THREADGBLDEF(semwait2long, volatile boolean_t) /* Waited too long for a semaphore */ THREADGBLDEF(skip_DB_exists_check, boolean_t) /* skip check for whether the DB file exists */ THREADGBLDEF(skip_file_corrupt_check, boolean_t) /* skip file_corrupt check in grab_crit */ THREADGBLDEF(tpnotacidtime, mval) /* limit for long non-ACID ops in transactions */ THREADGBLDEF(tp_restart_count, uint4) /* tp_restart counter */ THREADGBLDEF(tp_restart_dont_counts, int4) /* tp_restart count adjustment; NOTE: DEBUG only */ THREADGBLDEF(tp_restart_entryref, mval) /* tp_restart position for reporting */ THREADGBLDEF(tp_restart_failhist_indx, int4) /* tp_restart dbg restart history index */ THREADGBLDEF(tprestart_syslog_delta, int4) /* defines every n-th restart to be logged for tp */ THREADGBLDEF(tprestart_syslog_first, int4) /* # of TP restarts logged unconditionally */ THREADGBLAR1DEF(t_fail_hist_blk, block_id, (CDB_MAX_TRIES))/* array for TP tracing */ THREADGBLAR1DEF(tp_fail_bttn, trans_num, (CDB_MAX_TRIES))/* array for TP tracing */ THREADGBLAR1DEF(tp_fail_histtn, trans_num, (CDB_MAX_TRIES))/* array for TP tracing */ THREADGBLAR1DEF(tp_fail_hist, gv_namehead *, (CDB_MAX_TRIES))/* array for TP tracing */ THREADGBLAR1DEF(tp_fail_hist_reg, gd_region *, (CDB_MAX_TRIES))/* array for TP tracing */ THREADGBLDEF(transform, boolean_t) /* flag collation transform eligible */ THREADGBLDEF(wcs_recover_done, boolean_t) /* TRUE if wcs_recover was ever invoked in this * process. */ THREADGBLDEF(statsdb_fnerr_reason, int) /* Failure code for "gvcst_set_statsdb_fname" */ THREADGBLDEF(statsdb_memerr, boolean_t) /* If true, Failed to write to statsdb (SIG7)" */ THREADGBLDEF(non_tp_noiso_key_n_value, key_cum_value) /* for non-TP recompute block */ /* Local variables */ THREADGBLDEF(curr_symval_cycle, unsigned int) /* When curr_symval is changed, counter is bumped */ THREADGBLDEF(in_op_fnnext, boolean_t) /* set TRUE by op_fnnext; FALSE by op_fnorder */ THREADGBLDEF(local_collseq, collseq *) /* pointer to collation algorithm for lvns */ THREADGBLDEF(local_collseq_stdnull, boolean_t) /* flag temp controlling empty-string subscript * handling - if true, use standard null subscript * collation for local variables */ THREADGBLDEF(local_coll_nums_as_strings, boolean_t) /* flag controlling whether local variables that * evaluate to numbers are treated like numbers * (collating before strings) or like strings in * local collations */ THREADGBLDEF(lvmon_active, boolean_t) /* TRUE when local var monitoring is active */ THREADGBLDEF(lvmon_vars_anchor, lvmon_var *) /* Anchor for lv monitoring structure */ THREADGBLDEF(lvmon_vars_count, int) /* Count of lvmon_vars at lvmon_vars_anchor */ THREADGBLDEF(lv_null_subs, int) /* set in gtm_env_init_sp() */ THREADGBLDEF(max_lcl_coll_xform_bufsiz, int) /* max size of local collation buffer,which extends * from 32K each time the buffer overflows */ /* Replication variables */ THREADGBLDEF(replgbl, replgbl_t) /* set of global variables needed by the source * server */ THREADGBLDEF(tqread_nowait, boolean_t) /* avoid sleeping in t_qread if TRUE */ /* Auditing related global variables */ THREADGBLDEF(dollar_zaudit, boolean_t) /* Intrinsic that indicates whether direct mode * auditing (i.e. APD) is enabled. * TRUE => Auditing is enabled. */ THREADGBLDEF(is_zauditlog, boolean_t) /* Index for audit_conn*/ /* Miscellaneous */ THREADGBLDEF(arlink_enabled, boolean_t) /* TRUE if any zroutines segment is autorelink * enabled. */ THREADGBLDEF(arlink_loaded, uint4) /* Count of auto-relink enabled routines linked */ THREADGBLDEF(collseq_list, collseq *) /* list of pointers to currently mapped collation * algorithms - since this seems only used in * collseq.c -seems more like a STATICDEF */ THREADGBLFPTR(create_fatal_error_zshow_dmp_fptr, void, (void)) /* Fptr for gtm_fatal_error* zshow dmp routine */ THREADGBLDEF(disable_sigcont, boolean_t) /* indicates whether the SIGCONT signal * is allowed internally */ THREADGBLDEF(dollar_zcompile, mstr) /* compiler qualifiers */ THREADGBLDEF(dollar_etrap, mval) /* $etrap - standard error action */ THREADGBLDEF(dollar_zmode, mval) /* run mode indicator */ THREADGBLDEF(dollar_zonlnrlbk, int) /* ISV (incremented for every online rollback) */ THREADGBLDEF(dollar_zclose, int) /* ISV (set to close status for PIPE device) */ THREADGBLDEF(dollar_zroutines, mstr) /* routine search list */ THREADGBLDEF(dollar_zstep, mval) /* $zstep - action repository for zstep */ THREADGBLDEF(zstep_action, mval) /* $zstep - actual action for zstep */ THREADGBLDEF(dollar_ztrap, mval) /* $ztrap - recursive try error action */ THREADGBLDEF(error_on_jnl_file_lost, unsigned int) /* controls error handling done by jnl_file_lost. * 0 (default) : Turn off journaling and continue. * 1 : Keep journaling on, throw rts_error */ THREADGBLDEF(fnzsearch_lv_vars, lv_val *) /* op_fnzsearch lv tree anchor */ THREADGBLDEF(fnzsearch_sub_mval, mval) /* op_fnzsearch subscript constuctor */ THREADGBLDEF(fnzsearch_nullsubs_sav, int) /* op_fnzsearch temp for null subs control */ THREADGBLDEF(fnzsearch_globbuf_ptr, glob_t *) /* op_fnzsearch temp for pointing to glob results */ THREADGBLDEF(glvn_pool_ptr, glvn_pool *) /* Pointer to the glvn pool */ #ifdef GTMDBGFLAGS_ENABLED THREADGBLDEF(gtmdbgflags, int) THREADGBLDEF(gtmdbgflags_freq, int) THREADGBLDEF(gtmdbgflags_freq_cntr, int) #endif THREADGBLDEF(gtm_env_init_started, boolean_t) /* gtm_env_init flag envvar processing */ THREADGBLFPTR(gtm_env_xlate_entry, int, ()) /* gtm_env_xlate() function pointer */ THREADGBLDEF(gtm_environment_init, boolean_t) /* indicates GT.M development environment rather * than a production environment */ THREADGBLFPTR(gtm_sigusr1_handler, void, (void)) /* SIGUSR1 signal handler function ptr */ THREADGBLDEF(gtm_linktmpdir, mstr) /* Directory to use for relinkctl files */ THREADGBLDEF(gtm_tmpdir, mstr) /* Directory to use for tmp files */ THREADGBLDEF(gtm_trigger_etrap, mval) /* $etrap - for use in triggers */ THREADGBLDEF(gtm_trctbl_cur, trctbl_entry *) /* Current gtm trace table entry */ THREADGBLDEF(gtm_trctbl_end, trctbl_entry *) /* End of gtm trace table (last entry + 1) */ THREADGBLDEF(gtm_trctbl_groups, unsigned int) /* Trace group mask (max 31 groups) */ THREADGBLDEF(gtm_trctbl_start, trctbl_entry *) /* Start of gtm trace table */ THREADGBLDEF(gtm_waitstuck_script, mstr) /* Path to the script to be executed during waits*/ THREADGBLDEF(gtmprompt, mstr) /* mstr pointing to prombuf containing the GTM * prompt */ THREADGBLDEF(gtmsecshr_comkey, unsigned int) /* Hashed version key for gtmsecshr communications * eliminates cross-version issues */ THREADGBLDEF(gvcst_statsDB_open_ch_active, boolean_t) /* Condition handler is active */ THREADGBLDEF(in_zwrite, boolean_t) /* ZWrite is active */ THREADGBLDEF(is_socketpool, boolean_t) /* True when device-to-be-opened is socketpool */ THREADGBLDEF(gtm_socket_keepalive_idle, int) /* Initialized from $gtm_socket_keepalive_idle */ THREADGBLDEF(in_mupip_integ, boolean_t) /* To let DO_DB_HDR_CHECK skip DBFLCORRP */ THREADGBLDEF(instance_frozen_crit_skipped, boolean_t) /* To indicate Instance Freeze is on, CRIT skipped*/ THREADGBLDEF(integ_cannotskip_crit, boolean_t) /* indicates whether a SKIP CRIT is allowed */ THREADGBLDEF(retry_buffer_returned_null, boolean_t) /* If a retried block in integ returned a NULL */ THREADGBLDEF(lab_lnr, lnr_tabent **) /* Passes address from op_rhd_ext to op_extcall etc. * Points into either lab_proxy or linkage table */ THREADGBLDEF(jobexam_counter, unsigned int) /* How many times invoked $ZJOBEXAM() this proc */ #ifdef AUTORELINK_SUPPORTED THREADGBLDEF(lnk_proxy, lnk_tabent_proxy) /* Proxy linkage table for rtnhdr/labtab args for * indirect calls */ #else THREADGBLDEF(lab_proxy, lab_tabent_proxy) /* Placeholder storing lab_ln_ptr offset / lnr_adr * pointer and has_parms value, so they are * contiguous in memory */ #endif THREADGBLDEF(mprof_alloc_reclaim, boolean_t) /* Flag indicating whether the temporarily allocated * memory should be reclaimed */ THREADGBLDEF(mprof_chunk_avail_size, int) /* Number of mprof stack frames that can fit in * the current chunk */ THREADGBLDEF(mprof_env_gbl_name, mval) /* Name of global to use in mprof testing; should * be undefined to not use explicit mprof testing, * and empty or '0' if mprof data should not be * dumped into a global at the end */ THREADGBLDEF(mprof_ptr, mprof_wrapper *) /* Object containing key mprof references */ THREADGBLDEF(mprof_reclaim_addr, char *) /* Address of the memory bucket before * unw_prof_frame temporary allocations in case they * go to new bucket */ THREADGBLDEF(mprof_reclaim_cnt, int) /* Amount of mem to reclaim after unw_prof_frame */ THREADGBLDEF(mprof_stack_curr_frame, mprof_stack_frame *) /* Pointer to the last frame on the mprof stack */ THREADGBLDEF(mprof_stack_next_frame, mprof_stack_frame *) /* Pointer to the next frame to be put on the * mprof stack */ THREADGBLDEF(mu_cre_file_openrc, int) /* 0 if success otherwise holds errno after open */ #ifdef AUTORELINK_SUPPORTED THREADGBLDEF(open_relinkctl_list, open_relinkctl_sgm *) /* Anchor for open relinkctl list; similar to * open_shlib_root */ THREADGBLDEF(relinkctl_shm_min_index, int) /* Minimum size of rtnobj shared memory segment * is 2**relinkctl_shm_min_index */ THREADGBLDEF(gtm_autorelink_keeprtn, boolean_t) /* do not let go of objects in rtnobj shm */ #endif THREADGBLDEF(open_shlib_root, open_shlib *) /* Anchor for open shared library list */ THREADGBLDEF(parm_pool_ptr, parm_pool *) /* Pointer to the parameter pool */ THREADGBLDEF(parms_cnt, unsigned int) /* Parameters count */ THREADGBLAR1DEF(zpeek_regname, char, NAME_ENTRY_SZ) /* Last $ZPEEK() region specified */ THREADGBLDEF(zpeek_regname_len, int) /* Length of zpeekop_regname */ THREADGBLDEF(zpeek_reg_ptr, gd_region *) /* Resolved pointer for zpeekop_regname */ THREADGBLDEF(pipefifo_interrupt, int) /* count of number of times a pipe or fifo device is * interrupted */ THREADGBLDEF(prof_fp, mprof_stack_frame *) /* Stack frame that mprof currently operates on */ THREADGBLDEF(relink_allowed, int) /* Non-zero if recursive relink permitted */ #ifdef AUTORELINK_SUPPORTED THREADGBLDEF(save_zhist, zro_hist *) /* Temp storage for zro_hist blk so condition hndler * can get a hold of it if necessary to free it */ #endif THREADGBLDEF(set_zroutines_cycle, uint4) /* Informs us if we changed $ZROUTINES between * linking a routine and invoking it */ THREADGBLDEF(statsDB_init_defer_anchor, statsDB_deferred_init_que_elem *) /* Anchor point for deferred init of statsDBs */ THREADGBLDEF(statshare_opted_in, uint4) /* Flag controlling stats collection */ THREADGBLDEF(trans_code_pop, mval *) /* trans_code holder for $ZTRAP popping */ THREADGBLDEF(view_ydirt_str, char *) /* op_view working storage for ydir* ops */ THREADGBLDEF(view_ydirt_str_len, int4) /* Part of op_view working storage for ydir* ops */ THREADGBLDEF(view_region_list, tp_region *) /* used by view_arg_convert and op_view/view_dbop */ THREADGBLDEF(view_region_free_list, tp_region *) /* used by view_arg_convert and op_view/view_dbop */ THREADGBLDEF(zdate_form, int4) /* Control for default $zdate() format */ THREADGBLAR1DEF(zintcmd_active, zintcmd_active_info, ZINTCMD_LAST) /* Interrupted timed commands */ THREADGBLDEF(zro_root, zro_ent *) /* Anchor for zroutines structure entry array */ THREADGBLDEF(zsearch_var, lv_val *) /* UNIX $zsearch() lookup variable */ THREADGBLDEF(ztrap_form, int4) /* ztrap type indicator */ THREADGBLDEF(poll_fds_buffer, char *) /* Buffer for poll() argument */ THREADGBLDEF(poll_fds_buffer_size, size_t) /* Current allocated size of poll_fds_buffer */ THREADGBLDEF(socket_handle_counter, int) /* Counter for generated socket handles */ THREADGBLDEF(mu_set_file_noencryptable, boolean_t) /* Disabling encryption relaxes encr errors */ /* Larger structures and char strings */ THREADGBLAR1DEF(director_string, char, SIZEOF(mident_fixed)*2) /* Buffer for director_ident */ THREADGBLDEF(fnpca, fnpc_area) /* $Piece cache structure area */ THREADGBLAR1DEF(for_stack, oprtype *, MAX_FOR_STACK) /* Stacks FOR scope complete (compilation) addrs */ THREADGBLAR1DEF(for_temps, boolean_t, MAX_FOR_STACK) /* Stacked flags of FOR control value temps */ THREADGBLDEF(gtm_utfcgr_strings, int) /* Strings we can keep UTF8 parsing cache for */ THREADGBLDEF(gtm_utfcgr_string_groups, int) /* Groups of chars we can keep for each string */ THREADGBLAR1DEF(last_fnquery_return_sub, mval, MAX_LVSUBSCRIPTS)/* Returned subscripts of last $QUERY() */ THREADGBLDEF(lcl_coll_xform_buff, char *) /* This buffer is for local collation * transformations, which must not nest - i.e. * a transformation routine must not call another, * or itself. This kind of nesting would cause * overwriting of the buffer */ THREADGBLDEF(protmem_ba, mstr) /* Protected buffer */ THREADGBLAR1DEF(parm_ary, char *, MAX_PARMS) /* Parameter strings buffer */ THREADGBLAR1DEF(parm_ary_len, int, MAX_PARMS) /* Array element allocation length */ THREADGBLAR1DEF(parm_str_len, int, MAX_PARMS) /* Parameter strings lengths */ THREADGBLAR1DEF(prombuf, char, (MAX_MIDENT_LEN + 1)) /* The prompt buffer size (32) would allow at * least 8 UTF8 characters, but since most * commonly used UTF8 characters only occupy up * to 3 bytes, the buffer would at least * accommodate 10 UTF8 characters in a prompt */ THREADGBLAR1DEF(tmp_object_file_name, char, GTM_PATH_MAX) /* Hold temporary object name across routines */ THREADGBLAR1DEF(tp_restart_failhist_arry, char, FAIL_HIST_ARRAY_SIZE) /* tp_restart dbg storage of restart history */ #ifdef UTF8_SUPPORTED THREADGBLDEF(utfcgra, utfcgr_area) /* Lookaside cache for UTF8 parsing */ #endif THREADGBLDEF(utfcgr_string_lookmax, int) /* How many times to look for unreferenced slot */ THREADGBLAR1DEF(window_string, char, SIZEOF(mident_fixed)) /* Buffer for window_ident */ /* Utility I/O */ THREADGBLDEF(last_va_list_ptr, va_list) /* Last variable-length argument list used for util * out buffer management */ THREADGBLAR1DEF(util_outbuff, char, OUT_BUFF_SIZE * UTIL_OUTBUFF_STACK_SIZE) /* Util output buffer */ THREADGBLDEF(util_outbuff_ptr, char *) /* Pointer to util output buffer */ THREADGBLDEF(util_outptr, char *) /* Pointer within util output buffer */ /* GTM Call-in related globals */ THREADGBLDEF(callin_hashtab, hash_table_str *) /* Callin hash table */ THREADGBLDEF(ci_table, callin_entry_list *) /* Callin table in the form of a linked list */ THREADGBLDEF(ci_filter_hashtab, hash_table_str *) /* Filter hash table */ THREADGBLDEF(ci_filter_table, callin_entry_list *) /* Filter table list*/ THREADGBLDEF(extcall_package_root, struct extcall_package_list *) /* External call table package list */ THREADGBLDEF(gtmci_nested_level, unsigned int) /* Current nested depth of callin environments */ THREADGBLDEF(comm_filter_init, boolean_t) /* Signifying that the filter is in use */ THREADGBLDEF(temp_fgncal_stack, unsigned char *) /* Override for fgncal_stack when non-NULL */ THREADGBLDEF(zwrite_output_hook, void *) /* Really a void (*)(void), called if not NULL * when zwr_output is ready for use. */ THREADGBLDEF(want_empty_gvts, boolean_t) /* set to TRUE by MUPIP REORG when it is selecting * globals to be reorged. Need to be able to select * killed globals for effective truncate. */ THREADGBLDEF(in_mu_swap_root_state, unsigned int) /* Three states: * MUSWP_INCR_ROOT_CYCLE: MUPIP REORG moves GVT * root blocks in mu_swap_root. * MUSWP_FREE_BLK: MUPIP REORG frees blocks * MUSWP_NONE */ THREADGBLDEF(prev_t_tries, unsigned int) /* t_tries - 1, before t_retry/tp_restart */ THREADGBLDEF(rlbk_during_redo_root, boolean_t) /* set to TRUE if an online rollback which takes * the db to a different logical state occurs * during gvcst_redo_root_search in t_retry */ THREADGBLDEF(mlk_yield_pid, uint4) /* if non-zero, indicates the process id for which * we yielded a chance to get this lock. We keep * yielding as long as the process-id that is the * first in the wait queue ("d->pending") changes. * If the pid stays the same, we stop yielding. * if -1, indicates we disabled fairness mechanism * in order to avoid livelocks. */ THREADGBLDEF(jnl_extract_nocol, uint4) /* If non-zero, MUPIP JOURNAL EXTRACT will skip * reading the directory tree to find the * collation information for each global. Note * that this might result in garbled subscripts * in the extract if globals do use alternative * collation. */ THREADGBLDEF(skip_gtm_putmsg, boolean_t) /* currently needed by GDE to avoid gtm_putmsg * from happening inside map_sym.c/fgn_getinfo.c * when a VIEW "YCHKCOLL" is done as otherwise this * pollutes the current device GDE is writing to * and causes a GDECHECK error. This variable might * also be useful for others as the need arises. */ THREADGBLDEF(spangbl_seen, boolean_t) /* The process has referenced at least one global which spans * multiple regions. Used by op_gvnaked currently. */ THREADGBLDEF(no_spangbls, boolean_t) /* This process does not need to worry about spanning regions. * Examples are DSE which operates on a specific region * irrespective of whether the global spans regions or not. */ THREADGBLDEF(max_fid_index, int) /* maximum value of csa->fid_index across all open csa's */ THREADGBLDEF(is_mu_rndwn_rlnkctl, int) /* this process is MUPIP RUNDOWN -RELINKCTL */ THREADGBLDEF(expand_prev_key, boolean_t) /* Want gvcst_search_blk/gvcst_search_tail to expand prev_key * as they do the search. This avoids a later call to * "gvcst_expand_key" to determine prev_key after the search. */ THREADGBLDEF(gtm_autorelink_ctlmax, uint4) /* Maximum number of routines allowed for auterelink */ /* Each process that opens a database file with O_DIRECT (which happens if asyncio=TRUE) needs to do * writes from a buffer that is aligned at the filesystem-blocksize level. We ensure this in database shared * memory global buffers where each buffer is guaranteed aligned at OS_PAGE_SIZE as private memory buffers that are * used (e.g. dbfilop etc.). All the private memory usages will use the global variable "dio_buff.aligned". * It is guaranteed to be OS_PAGE_SIZE aligned. "dio_buff.unaligned_size" holds the size of the allocated buffer * and it has enough space to hold one GDS block (max db blocksize across ALL dbs opened by this process till now) * with OS_PAGE_SIZE padding. "dio_buff.unaligned" points to the beginning of this unaligned buffer and * "dio_buff.aligned" points to an offset within this unaligned buffer that is also OS_PAGE_SIZE aligned. */ THREADGBLDEF(dio_buff, dio_buff_t) #ifdef GTM_TRIGGER THREADGBLDEF(gvt_triggers_read_this_tn, boolean_t) /* if non-zero, indicates triggers were read for * at least one gv_target in this transaction. * A complete list of all gv_targets who had * triggers read in this transaction can be found * by going through the gvt_tp_list and checking * if gvt->trig_read_tn matches local_tn. This is * useful to invalidate all those triggers in case * of a transaction restart or rollback. */ THREADGBLDEF(op_fntext_tlevel, uint4) /* Non-zero implies $TEXT argument is a trigger * routine. * If non-zero, this is 1 + dollar_tlevel at * the time of the call to "get_src_line" in * op_fntext.c. Later used by fntext_ch.c. */ THREADGBLDEF(in_op_fntext, boolean_t) /* Denote the trigger was processed in $Text() */ THREADGBLDEF(ztrigbuff, char *) /* Buffer to hold $ztrigger/mupip-trigger output * until TCOMMIT. */ THREADGBLDEF(ztrigbuffAllocLen, int) /* Length of allocated ztrigbuff buffer */ THREADGBLDEF(ztrigbuffLen, int) /* Length of used ztrigbuff buffer */ THREADGBLDEF(ztrig_use_io_curr_device, boolean_t) /* Use current IO device instead of stderr/util_out_print */ #endif THREADGBLDEF(in_ext_call, boolean_t) /* Indicates we are in an external call */ /* linux AIO related data */ #ifdef USE_LIBAIO THREADGBLDEF(gtm_aio_nr_events, uint4) /* Indicates the value of the nr_events parameter suggested for * use by io_setup(). */ #endif THREADGBLDEF(crit_reg_count, int4) /* A count of the number of regions/jnlpools where this process * has crit */ THREADGBLDEF(ok_to_see_statsdb_regs, boolean_t) /* FALSE implies statsdb regions are hidden at "gd_load" time */ THREADGBLDEF(was_open_reg_seen, boolean_t) /* TRUE => there is at least one region with reg->was_open = TRUE */ THREADGBLDEF(nontp_jbuf_rsrv, jbuf_rsrv_struct_t *) /* Pointer to structure corresponding to reservations * on the journal buffer for current non-TP transaction. */ THREADGBLDEF(source_line, int4) /* keep track of line number in M file while compiling */ /* Debug values */ #ifdef DEBUG THREADGBLDEF(donot_commit, boolean_t) /* debug-only - see gdsfhead.h for purpose */ THREADGBLDEF(continue_proc_cnt, int) /* Used by whitebox secshr test to count time * process was continued. */ THREADGBLDEF(gtm_test_fake_enospc, boolean_t) /* DEBUG-only option to enable/disable anticipatory * freeze fake ENOSPC testing */ THREADGBLDEF(gtm_test_jnlpool_sync, uint4) /* DEBUG-only option to force the journal pool * accounting out of sync every n transactions. */ THREADGBLDEF(gtm_usesecshr, boolean_t) /* Bypass easy methods of dealing with IPCs, files, * wakeups, etc and always use gtmsecshr (testing). */ THREADGBLDEF(rts_error_unusable, boolean_t) /* Denotes the window in which an rts_error is * unusable */ THREADGBLDEF(rts_error_unusable_seen, boolean_t) THREADGBLAR1DEF(trans_restart_hist_array, trans_restart_hist_t, TRANS_RESTART_HIST_ARRAY_SZ) /* See tp.h for usage */ THREADGBLDEF(trans_restart_hist_index, uint4) THREADGBLDEF(skip_mv_num_approx_assert, boolean_t) /* TRUE if mval2subsc is invoked from op_fnview */ THREADGBLDEF(gtm_gvundef_fatal, boolean_t) /* core and die intended for testing */ THREADGBLDEF(gtm_dirtree_collhdr_always, boolean_t) /* Initialize 4-byte collation header in directory tree always. * Used by tests that are sensitive to DT leaf block layout. */ THREADGBLDEF(activelv_cycle, int) /* # of times SET_ACTIVE_LV macro has been invoked */ THREADGBLDEF(activelv_index, int) /* == (activelv_cycle % ACTIVELV_DBG_ARRAY_SIZE_DEF) */ THREADGBLDEF(activelv_dbg_array, activelv_dbg_t *) /* pointer to array holding trace details for * ACTIVELV_DBG_ARRAY_SIZE_DEF most recent * invocations of SET_ACTIVE_LV */ THREADGBLDEF(cli_get_str_max_len, uint4) # ifdef GTM_TRIGGER THREADGBLDEF(gtmio_skip_tlevel_assert, boolean_t) /* Allow for "util_out_print_gtmio" calls without TP * if this variable is TRUE. */ THREADGBLDEF(in_trigger_upgrade, boolean_t) /* caller is MUPIP TRIGGER -UPGRADE */ #endif /* #ifdef GTM_TRIGGER */ THREADGBLDEF(gtm_test_autorelink_always, boolean_t) /* DEBUG-only option to enable/disable autorelink always */ THREADGBLDEF(fork_without_child_wait, boolean_t) /* we did a FORK but did not wait for child to detach from * inherited shm so shm_nattch could be higher than we expect. */ THREADGBLDEF(in_bm_getfree_gdsfilext, boolean_t) /* bm_getfree() did a preemptive crit grab before doing a * file extension. */ #endif /* #ifdef DEBUG */ /* (DEBUG_ONLY relevant points reproduced from the comment at the top of this file) * 5. It is important for ANY DEBUG_ONLY fields to go at the VERY END. Failure to do this breaks gtmpcat. * 6. If a DEBUG_ONLY array is declared whose dimension is a macro, then it is necessary, for gtmpcat to work, * that the macro shouldn't be defined as DEBUG_ONLY. */ fis-gtm-V7.0-005/sr_port/gtm_threadgbl_deftypes.c0000644000032200000250000001556614342376333020734 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2022 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. * * * ****************************************************************/ /* Since we are about to create gtm_threadgbl_deftypes.h, signal gtm_threadgbl.h to avoid including it */ #define NO_THREADGBL_DEFTYPES #include "mdef.h" #include #include "gtm_inet.h" #include "gtm_iconv.h" #include "gtm_socket.h" #include "gtm_unistd.h" #include "gtm_limits.h" #include "gtm_signal.h" #include #include "gtm_un.h" #include #include "cache.h" #include "hashtab_addr.h" #include "hashtab_int4.h" #include "hashtab_int8.h" #include "hashtab_mname.h" #include "hashtab_str.h" #include "hashtab_objcode.h" #include "error.h" #include #include "gdsroot.h" #include "gdskill.h" #include "ccp.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "comline.h" #include "compiler.h" #include "cmd_qlf.h" #include "io.h" #include "iosp.h" #include "jnl.h" #include "lv_val.h" #include "collseq.h" #include "mdq.h" #include "mprof.h" #include "mv_stent.h" #include "find_mvstent.h" /* needed for zintcmd_active */ #include "stack_frame.h" #include "stp_parms.h" #include "stringpool.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "tp_frame.h" #include "mlkdef.h" #include "zshow.h" #include "zwrite.h" #include "zbreak.h" #include "fnpc.h" #include "utfcgr.h" #include "mmseg.h" #include "gtmimagename.h" #include "gt_timer.h" #include "iosocketdef.h" /* needed for socket_pool and MAX_N_SOCKETS*/ #include "ctrlc_handler_dummy.h" #include "unw_prof_frame_dummy.h" #include "op.h" #include "gtmsecshr.h" #include "error_trap.h" #include "patcode.h" /* for pat_everything and sizeof_pat_everything */ #include "source_file.h" /* for REV_TIME_BUFF_LEN */ #include "mupipbckup.h" #include "dpgbldir.h" #include "mmemory.h" #include "have_crit.h" #include "alias.h" #include "zroutines.h" #include "trace_table.h" #include "parm_pool.h" #include "util.h" /* for util_outbuff manipulations */ #include "nametabtyp.h" #include "localvarmonitor.h" /* FOR REPLICATION RELATED GLOBALS */ #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" #include "replgbl.h" /* FOR MERGE RELATED GLOBALS */ #include "gvname_info.h" #include "op_merge.h" #include "cli.h" #include "invocation_mode.h" #include "fgncal.h" #include "repl_sem.h" #include "gtm_zlib.h" #include "zro_shlibs.h" #include "jnl_typedef.h" #include "gds_blk_upgrade.h" /* for UPGRADE_IF_NEEDED flag */ #include "cws_insert.h" /* for CWS_REORG_ARRAYSIZE */ #ifdef UTF8_SUPPORTED # include "gtm_icu_api.h" # include "gtm_utf8.h" #endif #include "gtmcrypt.h" #include "gdsblk.h" #include "muextr.h" #ifdef GTM_TRIGGER # include "gv_trigger.h" # include "gtm_trigger.h" #endif #ifdef AUTORELINK_SUPPORTED # include "relinkctl.h" #endif #include "ztimeout_routines.h" #include "deferred_events.h" #include "deferred_events_queue.h" /* This module's purpose is to generate gtm_threadgbl_deftypes.h for a given platform. This header file * contains all the type and offset informatin needed for the TREF macro in gtm_threadgbl.h to access * any field in the global structure without having to have all the types known in every module. Only the * types used need be known. * * This is acomplished by creating the structure in this module with all types and offsets known and outputting * those values in the form of #define statements that can be used by subsequent compiles. */ /* First step, create the structure */ #define THREADGBLDEF(name, type) type name; #define THREADGBLFPTR(name, type, args) type (*name)args; #define THREADGBLAR1DEF(name, type, dim1) type name[dim1]; #define THREADGBLAR2DEF(name, type, dim1, dim2) type name[dim1][dim2]; typedef struct { #include "gtm_threadgbl_defs.h" } gtm_threadgbl_def_t; #undef THREADGBLDEF #undef THREADGBLFPTR #undef THREADGBLAR1DEF #undef THREADGBLAR2DEF /* Note this module uses regular (lower case) printf because using PRINTF calls gtm_printf which is inappropriate * since this module is not part of the GTM runtime but a standalone text generator. */ /* Define macros that will generate the type and offset #defines */ #define PRINT_TYPE_OFFSET(name, type) \ printf("# define ggo_%s %d\n", #name, (int)OFFSETOF(gtm_threadgbl_def_t, name)); \ printf("# define ggt_%s %s\n", #name, #type); /* For function pointers, we need the offset and type (which is a return type in this case since the actual type of * the item is "function pointer") but also need the argument declarations for the function declaration to be complete * Lastly, we need a function pointer typedef to make invocations work correctly. */ #define PRINT_TYPE_OFFSET_FPTR(name, type, args) \ printf("# define ggo_%s %d\n", #name, (int)OFFSETOF(gtm_threadgbl_def_t, name)); \ printf("# define ggt_%s %s\n", #name, #type); /* In this case, return type */ \ printf("# define gga_%s %s\n", #name, #args); \ printf("typedef %s (*ggf_%s)%s;\n", #type, #name, #args); /* For single dimension arrays, include the length of the entire array as it is likely needed, especially * for character types. */ #define PRINT_TYPE_OFFSET_ARY1(name, type, dim1) \ printf("# define ggo_%s %d\n", #name, (int)OFFSETOF(gtm_threadgbl_def_t, name)); \ printf("# define ggt_%s %s\n", #name, #type); \ printf("# define ggl_%s %d\n", #name, (int)SIZEOF(gtd.name)); /* For two dimensional arrays, we need to record the 2nd dimension as it is needed in the address computations */ #define PRINT_TYPE_OFFSET_ARY2(name, type, dim1, dim2) \ printf("# define ggo_%s %d\n", #name, (int)OFFSETOF(gtm_threadgbl_def_t, name)); \ printf("# define ggt_%s %s\n", #name, #type); \ printf("# define ggl_%s %d\n", #name, (int)SIZEOF(gtm_threadgbl_def_t.name)); \ printf("# define ggd_%s %d\n", #name, (int)dim2); int main() { gtm_threadgbl_def_t gtd; /* Now run through each var in the structure generating defines for the type and offset within the structure */ # define THREADGBLDEF(name, type) PRINT_TYPE_OFFSET(name, type) # define THREADGBLFPTR(name, type, args) PRINT_TYPE_OFFSET_FPTR(name, type, args) # define THREADGBLAR1DEF(name, type, dim1) PRINT_TYPE_OFFSET_ARY1(name, type, dim1) # define THREADGBLAR2DEF(name, type, dim1, dim2) PRINT_TYPE_OFFSET_ARY1(name, type, dim1, dim2) # include "gtm_threadgbl_defs.h" # undef THREADGBLDEF # undef THREADGBLFPTR # undef THREADGBLAR1DEF # undef THREADGBLAR2DEF printf("# define size_gtm_threadgbl_struct %d\n", (int)SIZEOF(gtm_threadgbl_def_t)); return 0; } fis-gtm-V7.0-005/sr_port/gtm_threadgbl_init.c0000644000032200000250000001576714342376333020057 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2021 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" #define BYPASS_MEMCPY_OVERRIDE /* Signals gtm_string.h to not override memcpy(). When this routine is linked into gtcm_pkdisp, * the assert in the routine called by memcpy macro causes the world to be pulled in. Avoid. */ /* Note that since this routine is called prior to reading environment vars or pretty much any * other initialization, we cannot use gtm_malloc() yet so care is taken to use the real system * malloc. */ #undef malloc #undef free #include #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_inet.h" #include "gtm_iconv.h" #include "gtm_socket.h" #include "gtm_unistd.h" #include "gtm_limits.h" #include "gtm_un.h" #include "gtm_signal.h" #include #include #include "cache.h" #include "gtm_multi_thread.h" #include "hashtab_addr.h" #include "hashtab_int4.h" #include "hashtab_int8.h" #include "hashtab_mname.h" #include "hashtab_str.h" #include "hashtab_objcode.h" #include "error.h" #include #include "gdsroot.h" #include "gdskill.h" #include "ccp.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "comline.h" #include "compiler.h" #include "cmd_qlf.h" #include "io.h" #include "iosp.h" #include "jnl.h" #include "lv_val.h" #include "collseq.h" #include "mdq.h" #include "mprof.h" #include "mv_stent.h" #include "find_mvstent.h" /* needed for zintcmd_active */ #include "stack_frame.h" #include "stp_parms.h" #include "stringpool.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "tp_frame.h" #include "mlkdef.h" #include "zshow.h" #include "zwrite.h" #include "zbreak.h" #include "fnpc.h" #include "mmseg.h" #include "gtmimagename.h" #include "gt_timer.h" #include "iosocketdef.h" /* needed for socket_pool and MAX_N_SOCKETS*/ #include "ctrlc_handler_dummy.h" #include "unw_prof_frame_dummy.h" #include "op.h" #include "gtmsecshr.h" #include "error_trap.h" #include "patcode.h" /* for pat_everything and sizeof_pat_everything */ #include "source_file.h" /* for REV_TIME_BUFF_LEN */ #include "mupipbckup.h" #include "dpgbldir.h" #include "mmemory.h" #include "have_crit.h" #include "alias.h" #include "zroutines.h" #include "parm_pool.h" #include "util.h" /* for util_outbuff manipulations */ #include "nametabtyp.h" #include "gtm_reservedDB.h" #include "localvarmonitor.h" /* FOR REPLICATION RELATED GLOBALS */ #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" #include "replgbl.h" #include "trace_table.h" /* FOR MERGE RELATED GLOBALS */ #include "gvname_info.h" #include "op_merge.h" #include "cli.h" #include "invocation_mode.h" #include "fgncal.h" #include "repl_sem.h" #include "gtm_zlib.h" #include "zro_shlibs.h" #include "jnl_typedef.h" #include "gds_blk_upgrade.h" /* for UPGRADE_IF_NEEDED flag */ #include "cws_insert.h" /* for CWS_REORG_ARRAYSIZE */ #ifdef UTF8_SUPPORTED # include "gtm_icu_api.h" # include "gtm_utf8.h" # include "utfcgr.h" #endif #include "gtmcrypt.h" #include "gdsblk.h" #include "muextr.h" #ifdef GTM_TRIGGER # include "gv_trigger.h" # include "gtm_trigger.h" #endif #ifdef AUTORELINK_SUPPORTED # include "relinkctl.h" #endif #include "ztimeout_routines.h" #include "deferred_events_queue.h" #include "gtm_threadgbl_init.h" #define DEFAULT_PROMPT "GTM>" GBLDEF void *gtm_threadgbl; /* Anchor for thread global for this thread */ /* Since gtm_threadgbl is type-neutral, define a structure mapping just for this routine that *does* * contain the types. This structure can be accessed through the debugger for easier type dumping. * Note we define this structure even in pro since it could be useful even in pro debugging (by gtmpcat). */ #define THREADGBLDEF(name,type) type name; #define THREADGBLFPTR(name, type, args) type (*name)args; #define THREADGBLAR1DEF(name, type, dim1) type name[dim1]; #define THREADGBLAR2DEF(name, type, dim1, dim2) type name[dim1][dim2]; typedef struct { # include "gtm_threadgbl_defs.h" } gtm_threadgbl_true_t; #undef THREADGBLDEF #undef THREADGBLFPTR #undef THREADGBLAR1DEF #undef THREADGBLAR2DEF GBLDEF gtm_threadgbl_true_t *gtm_threadgbl_true; GBLREF int4 *aligned_source_buffer; /* This routine allocates the thread global structure and for now, since GTM is not yet threaded, * anchors it in a global variable. This still improves access to global variables even in this * paradym because the 3 step global dereference only need happen once per module. */ error_def(ERR_MEMORY); error_def(ERR_VMSMEMORY); error_def(ERR_GTMASSERT); void gtm_threadgbl_init(void) { void *lcl_gtm_threadgbl; /* DCL_THREADGBL_ACCESS */ if (SIZEOF(gtm_threadgbl_true_t) != size_gtm_threadgbl_struct) { /* Size mismatch with gtm_threadgbl_deftypes.h - no error handling yet available so do * the best we can. */ FPRINTF(stderr, "GTM-F-GTMASSERT gtm_threadgbl_true_t and gtm_threadgbl_t are different sizes\n"); EXIT(ERR_GTMASSERT); } if (NULL != gtm_threadgbl) { /* has already been initialized - don't re-init */ FPRINTF(stderr, "GTM-F-GTMASSERT gtm_threadgbl is already initialized\n"); EXIT(ERR_GTMASSERT); } gtm_threadgbl = malloc(size_gtm_threadgbl_struct); if (NULL == gtm_threadgbl) { /* Storage was not allocated for some reason - no error handling yet still */ perror("GTM-F-MEMORY Unable to allocate startup thread structure"); EXIT(UNIX_ONLY(ERR_MEMORY) VMS_ONLY(ERR_VMSMEMORY)); } memset(gtm_threadgbl, 0, size_gtm_threadgbl_struct); gtm_threadgbl_true = (gtm_threadgbl_true_t *)gtm_threadgbl; lcl_gtm_threadgbl = gtm_threadgbl; /* SETUP_THREADGBL_ACCESS */ /* Add specific initializations if other than 0s here using the TREF() family of macros: */ (TREF(director_ident)).addr = TADR(director_string); TREF(for_stack_ptr) = TADR(for_stack); (TREF(gtmprompt)).addr = TADR(prombuf); (TREF(gtmprompt)).len = SIZEOF(DEFAULT_PROMPT) - 1; TREF(lv_null_subs) = LVNULLSUBS_OK; /* UNIX: set in gtm_env_init_sp(), VMS: set in gtm$startup() - init'd here * in case alternative invocation methods bypass gtm_startup() */ MEMCPY_LIT(TADR(prombuf), DEFAULT_PROMPT); (TREF(replgbl)).jnl_release_timeout = DEFAULT_JNL_RELEASE_TIMEOUT; (TREF(window_ident)).addr = TADR(window_string); ASSERT_SAFE_TO_UPDATE_THREAD_GBLS; TREF(util_outbuff_ptr) = TADR(util_outbuff); /* Point util_outbuff_ptr to the beginning of util_outbuff at first. */ TREF(util_outptr) = TREF(util_outbuff_ptr); (TREF(source_buffer)).addr = (char *)&aligned_source_buffer; (TREF(source_buffer)).len = MAX_SRCLINE; (TREF(dollar_ztimeout)).ztimeout_seconds.m[1] = -1000; } fis-gtm-V7.0-005/sr_port/gtm_threadgbl_init.h0000644000032200000250000000106014342376331020037 0ustar librarygtc/**************************************************************** * * * Copyright 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. * * * ****************************************************************/ #ifndef GTM_THREADGBL_INIT_included #define GTM_THREADGBL_INIT_included void gtm_threadgbl_init(void); #endif fis-gtm-V7.0-005/sr_port/gtm_time.h0000755000032200000250000001005314342376331016023 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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. * * * ****************************************************************/ /* gtm_time.h - interlude to system header file. */ #ifndef GTM_TIMEH #define GTM_TIMEH #include /* CTIME format also used by asctime: Fri Oct 23 13:58:14 2015 */ #define CTIME_STRFMT "%a %b %d %H:%M:%S %Y\n" #define STRFTIME(dest, maxsize, format, timeptr, res) \ { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_X_TIME_FUNCTION, prev_intrpt_state); \ res = strftime(dest, maxsize, format, timeptr); \ ENABLE_INTERRUPTS(INTRPT_IN_X_TIME_FUNCTION, prev_intrpt_state); \ } /* To use GET_CUR_TIME macro these definitions are required * now_t now; char time_str[CTIME_BEFORE_NL + 2]; */ typedef time_t now_t; #define CTIME_BEFORE_NL 24 /* #GTM_THREAD_SAFE : The below macro (GET_CUR_TIME) is thread-safe */ #define GET_CUR_TIME(time_str) \ { \ char *time_ptr = &time_str[0]; \ now_t now; \ intrpt_state_t prev_intrpt_state; \ \ if ((time_t)-1 == (now = time(NULL))) \ MEMCPY_LIT(time_ptr, "****** time failed *****\n"); /* keep string len same as CTIME_BEFORE_NL */ \ else \ { \ /* Do not use GTM_CTIME as it uses "ctime" which is not thread-safe. Use "ctime_r" instead which is thread-safe \ * We still need to disable interrupts (from external signals) to avoid hangs (e.g. SIG-15 taking us to \ * generic_signal_handler -> send_msg_csa -> syslog which in turn could deadlock due to an in-progress \ * "ctime_r" call. Note that the DEFER_INTERRUPTS and ENABLE_INTERRUPTS macro are a no-op in case \ * "multi_thread_in_use" is TRUE but external signals are anyways disabled by "gtm_multi_thread" in that case. \ */ \ DEFER_INTERRUPTS(INTRPT_IN_X_TIME_FUNCTION, prev_intrpt_state); \ time_ptr = ctime_r(&now, time_ptr); \ ENABLE_INTERRUPTS(INTRPT_IN_X_TIME_FUNCTION, prev_intrpt_state); \ if (NULL == time_ptr) \ { \ time_ptr = &time_str[0]; \ MEMCPY_LIT(time_ptr, "***** ctime failed *****\n"); /* keep string len same as CTIME_BEFORE_NL */ \ } \ /* else time_str[] already contains the filled in time */ \ } \ } #define GTM_MKTIME(VAR, TIME) \ { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_X_TIME_FUNCTION, prev_intrpt_state); \ VAR = mktime(TIME); \ ENABLE_INTERRUPTS(INTRPT_IN_X_TIME_FUNCTION, prev_intrpt_state); \ } #define GTM_GMTIME(VAR, TIME) \ { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_X_TIME_FUNCTION, prev_intrpt_state); \ VAR = gmtime(TIME); \ ENABLE_INTERRUPTS(INTRPT_IN_X_TIME_FUNCTION, prev_intrpt_state); \ } #define GTM_LOCALTIME(VAR, TIME) \ { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_X_TIME_FUNCTION, prev_intrpt_state); \ VAR = localtime(TIME); \ ENABLE_INTERRUPTS(INTRPT_IN_X_TIME_FUNCTION, prev_intrpt_state); \ } /* CTIME collides with linux define in termios */ #define GTM_CTIME(VAR, TIME) \ { \ GBLREF boolean_t multi_thread_in_use; \ intrpt_state_t prev_intrpt_state; \ \ /* "ctime" is not thread-safe. Make sure threads are not in use by callers of GTM_CTIME */ \ GTM_PTHREAD_ONLY(assert(!multi_thread_in_use)); \ DEFER_INTERRUPTS(INTRPT_IN_X_TIME_FUNCTION, prev_intrpt_state); \ VAR = ctime(TIME); \ ENABLE_INTERRUPTS(INTRPT_IN_X_TIME_FUNCTION, prev_intrpt_state); \ } #endif fis-gtm-V7.0-005/sr_port/gtm_un.h0000644000032200000250000000131014342376331015500 0ustar librarygtc/**************************************************************** * * * Copyright 2013, 2014 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. * * * ****************************************************************/ /* gtm_un.h - interlude to system header file. */ #ifndef GTM_UNH #define GTM_UNH #ifndef VMS #include #endif #ifdef __hpux #define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) + strlen ((ptr)->sun_path)) #endif #endif fis-gtm-V7.0-005/sr_port/gtm_unistd.h0000755000032200000250000001103114342376331016370 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* gtm_unistd.h - interlude to system header file. */ #ifndef GTM_UNISTDH #define GTM_UNISTDH #include #include #include "gtm_string.h" #define CHDIR chdir #define CHOWN chown #define FCHOWN fchown /* Usual convention is to uppercase the system function in the GT.M macro wrapper. But in this case, we want to macro-wrap * the _exit() function. _EXIT is ruled out because names starting with _ are reserved for system functions. * Hence naming it UNDERSCORE_EXIT instead. */ #define UNDERSCORE_EXIT(x) \ MBSTART { \ char *rname; \ \ /* Currently we dont know of any caller of UNDERSCORE_EXIT inside threaded code. So add below assert */ \ assert(!INSIDE_THREADED_CODE(rname)); /* Below code is not thread safe as it does exit() */ \ _exit(x); \ } MBEND #define INVALID_UID (uid_t)-1 #define INVALID_GID (gid_t)-1 GBLREF uid_t user_id, effective_user_id; GBLREF gid_t group_id, effective_group_id; #define GETUID() user_id #define GETEUID() ((INVALID_UID == effective_user_id) \ ? (effective_user_id = geteuid()) : effective_user_id) #define GETGID() ((INVALID_GID == group_id) ? (group_id = getgid()) : group_id) #define GETEGID() ((INVALID_GID == effective_group_id) \ ? (effective_group_id = getegid()) : effective_group_id) #if defined(VMS) #define GTM_MAX_DIR_LEN (PATH_MAX + PATH_MAX) /* DEVICE + DIRECTORY */ #define GTM_VMS_STYLE_CWD 1 #define GTM_UNIX_STYLE_CWD 0 #define GETCWD(buffer, size, getcwd_res) \ (getcwd_res = getcwd(buffer, size, GTM_VMS_STYLE_CWD)) /* force VMS style always 'cos many other parts of GT.M always * do it the VMS way */ #else /* !VMS => UNIX */ #define GTM_MAX_DIR_LEN (PATH_MAX + 1) /* DIRECTORY + terminating '\0' */ #define GETCWD(buffer, size, getcwd_res) \ { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); \ getcwd_res = getcwd(buffer, size); \ ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); \ } #define CONFSTR gtm_confstr #endif #ifndef UTF8_SUPPORTED #define GETHOSTNAME(name,namelen,gethostname_res) \ (gethostname_res = gethostname(name, namelen)) #else #include "gtm_utf8.h" GBLREF boolean_t gtm_utf8_mode; #define GETHOSTNAME(name,namelen,gethostname_res) \ (gethostname_res = gethostname(name, namelen), \ gtm_utf8_mode ? gtm_utf8_trim_invalid_tail((unsigned char *)name, namelen) : 0, \ gethostname_res) #endif #define LINK link #define UNLINK unlink #define TTYNAME ttyname #define TTYNAME_R(fd,buf,buflen,rc) \ { \ intrpt_state_t prev_intrpt_state; \ \ DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); \ rc = ttyname_r(fd, buf, buflen); \ ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); \ } #define ACCESS access #define EXECL execl #define EXECV execv #define EXECVE execve #define TRUNCATE truncate #ifdef GTM_FD_TRACE /* Just like open and close were noted down in gtm_fcntl.h, note down all macros which we are redefining here and could * potentially have been conflictingly defined by the system header file "unistd.h". The system define will be used * in gtm_fd_trace.c within the implementation of the GT.M interlude function. Currently none of these functions (close, * pipe, dup, dup2) are defined by the system so it is not theoretically necessary but they could be defined in the future. */ # undef close /* in case this is already defined by */ # undef pipe /* in case this is already defined by */ # undef dup /* in case this is already defined by */ # undef dup2 /* in case this is already defined by */ # define close gtm_close # define pipe gtm_pipe1 /* gtm_pipe is already used so using pipe1 */ # define dup gtm_dup # define dup2 gtm_dup2 #endif int gtm_close(int fd); int gtm_pipe1(int pipefd[2]); int gtm_dup(int oldfd); int gtm_dup2(int oldfd, int newfd); int gtm_confstr(char *command, unsigned int maxsize); #endif fis-gtm-V7.0-005/sr_port/gtm_utsname.h0000755000032200000250000000120714342376331016542 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ /* gtm_utsname.h - interlude to system header file. */ #ifndef GTM_UTSNAMEH #define GTM_UTSNAMEH #include #define UNAME(name,uname_res) (uname_res = uname(name)) #endif fis-gtm-V7.0-005/sr_port/gtm_wake.h0000755000032200000250000000116014342376331016013 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GTM_WAKE_H_INCLUDED #define GTM_WAKE_H_INCLUDED void gtm_wake(int4 *pidadr,char *prcnam); #endif fis-gtm-V7.0-005/sr_port/gtmctype.h0000755000032200000250000000075314342376331016060 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, Inc. * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #define PRINTABLE(X) ((X) < 127 && (X) > 31) fis-gtm-V7.0-005/sr_port/gtmdbglvl.h0000755000032200000250000000741214342376331016205 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GTMDBGLVL_H_INCLUDED #define GTMDBGLVL_H_INCLUDED /* Define GT.M debug levels. These values can be added together to turn on multiple features at the same time. Note that the cumulative value specified in the logical or environment variable must currently be specified in decimal. */ #define GDL_None 0x00000000 /* (000) No debugging is happening today */ #define GDL_Simple 0x00000001 /* (001) Regular assert checking, no special checks */ #define GDL_SmStats 0x00000002 /* (002) Print usage statistics at end of process */ #define GDL_SmTrace 0x00000004 /* (004) Trace each malloc/free (output to stderr) */ #define GDL_SmDumpTrace 0x00000008 /* (008) Dump malloc/free trace information on exit */ #define GDL_SmAllocVerf 0x00000010 /* (016) Perform verification of allocated storage chain for each call */ #define GDL_SmFreeVerf 0x00000020 /* (032) Perform simple verification of free storage chain for each call */ #define GDL_SmBackfill 0x00000040 /* (064) Backfill unused storage (cause exceptions if released storage is used */ #define GDL_SmChkAllocBackfill 0x00000080 /* (128) Verify backfilled storage in GDL_AllocVerf while verifying \ each individual queue entry */ #define GDL_SmChkFreeBackfill 0x00000100 /* (256) Verify backfilled storage in GDL_FreeVerf while verifying \ each individual queue entry */ #define GDL_SmStorHog 0x00000200 /* (512) Each piece of storage allocated is allocated in an element twice \ the desired size to provide glorious amounts of backfill for \ overrun checking. */ #define GDL_DumpOnStackOFlow 0x00000400 /* (1024) When get a stack overflow or out-of-memory error, generate a core */ #define GDL_ZSHOWDumpOnSignal 0x00000800 /* (2048) Don't supress GTM_FATAL file creation when get a signal */ #define GDL_PrintIndCacheStats 0x00001000 /* (4096) Print indirect cacheing stats */ #define GDL_PrintCacheStats 0x00002000 /* (8192) Print stats on $Piece and UTF8 cacheing (debug only) */ #define GDL_DebugCompiler 0x00004000 /* (16384) Turn on compiler debugging */ #define GDL_SmDump 0x00008000 /* (32768) Do full blown storage dump -- only useful in debug mode */ #define GDL_PrintEntryPoints 0x00010000 /* (65536) Print address of entry points when they are loaded/resolved */ #define GDL_PrintSockIntStats 0x00020000 /* (131072) Print Socket interrupt stats on exit */ #define GDL_SmInitAlloc 0x00040000 /* (262144) Initialize all storage allocated or deallocated with 0xdeadbeef */ #define GDL_PrintPipeIntStats 0x00080000 /* (524288) Print Pipe/Fifo(rm) interrupt stats on exit */ #define GDL_IgnoreAvailSpace 0x00100000 /* (1048576) Allow gdsfilext/mu_cre_file (UNIX) to ignore available space */ #define GDL_PrintPMAPStats 0x00200000 /* (2097152) Print process memory map on exit (using pmap or procmap utility) */ #define GDL_AllowLargeMemcpy 0x00400000 /* (4194304) Bypass the 1GB sanity check in gtm_memcpy_validate_and_execute() */ #define GDL_UseSystemMalloc 0x80000000 /* (2147483648) Use the system's malloc(), disabling all the above GDL_Sm options */ #define GDL_SmAllMallocDebug (GDL_Simple | GDL_SmStats | GDL_SmTrace | GDL_SmDumpTrace | GDL_SmAllocVerf \ | GDL_SmFreeVerf | GDL_SmBackfill | GDL_SmChkAllocBackfill | GDL_SmChkFreeBackfill \ | GDL_SmStorHog | GDL_SmDump | GDL_SmInitAlloc) #endif fis-gtm-V7.0-005/sr_port/gtmimagename.h0000755000032200000250000000271314342376331016655 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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. * * * ****************************************************************/ #ifndef GTMIMAGENAME_DEF #define GTMIMAGENAME_DEF typedef struct { char *imageName; int imageNameLen; } gtmImageName; enum gtmImageTypes { #define IMAGE_TABLE_ENTRY(A,B) A, #include "gtmimagetable.h" #undef IMAGE_TABLE_ENTRY n_image_types }; #define GTMIMAGENAMETXT(x) gtmImageNames[x].imageNameLen, gtmImageNames[x].imageName GBLREF enum gtmImageTypes image_type; /* needed by IS_MUMPS_IMAGE and IS_GTM_IMAGE macros */ GBLREF boolean_t run_time; /* needed by IS_MCODE_RUNNING macro */ #define IS_MCODE_RUNNING (run_time) #define IS_DSE_IMAGE (DSE_IMAGE == image_type) #define IS_GTCM_SERVER_IMAGE (GTCM_SERVER_IMAGE == image_type) #define IS_GTCM_GNP_SERVER_IMAGE (GTCM_GNP_SERVER_IMAGE == image_type) #define IS_GTMSECSHR_IMAGE (GTMSECSHR_IMAGE == image_type) #define IS_GTM_IMAGE IS_MUMPS_IMAGE #define IS_LKE_IMAGE (LKE_IMAGE == image_type) #define IS_MUMPS_IMAGE (GTM_IMAGE == image_type) #define IS_MUPIP_IMAGE (MUPIP_IMAGE == image_type) #define IS_VALID_IMAGE (INVALID_IMAGE != image_type) #endif fis-gtm-V7.0-005/sr_port/gtmimagetable.h0000755000032200000250000000175014342376331017024 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ /* * IMAGE_TABLE_ENTRY(image_type, image-name) */ IMAGE_TABLE_ENTRY (INVALID_IMAGE, "INVALID_IMAGE") IMAGE_TABLE_ENTRY (GTM_IMAGE, "GT.M") IMAGE_TABLE_ENTRY (MUPIP_IMAGE, "MUPIP") IMAGE_TABLE_ENTRY (DSE_IMAGE, "DSE") IMAGE_TABLE_ENTRY (LKE_IMAGE, "LKE") IMAGE_TABLE_ENTRY (GTMSECSHR_IMAGE, "GTMSECSHR") IMAGE_TABLE_ENTRY (GTCM_SERVER_IMAGE, "GTCM_SERVER") IMAGE_TABLE_ENTRY (GTCM_GNP_SERVER_IMAGE, "GTCM_GNP_SERVER") fis-gtm-V7.0-005/sr_port/gtmmsg.h0000644000032200000250000000377714342376331015530 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef GTMMSG_H_INCLUDED #define GTMMSG_H_INCLUDED void gtm_getmsg(int4 msgnum, mstr *msgbuf); void gtm_putmsg(int argcnt, ...); void gtm_putmsg_csa(void *, int argcnt, ...); /* Use CSA_ARG(CSA) for portability */ void gtm_putmsg_noflush(int argcnt, ...); void gtm_putmsg_noflush_csa(void *, int argcnt, ...); GBLREF boolean_t multi_thread_in_use; /* TRUE => threads are in use. FALSE => not in use */ /* If threads are in use, then do not use "gv_cur_region" as the thread could be operating on a region completely different * from the process-wide "gv_cur_region" variable. Assume a safe value of NULL as the csa. */ #define PTHREAD_CSA_FROM_GV_CUR_REGION(CSA, LCL_JNLPOOL) \ { \ if (!multi_thread_in_use) \ { \ CSA = REG2CSA(gv_cur_region); \ if (CSA && !CUSTOM_ERRORS_LOADED_CSA(CSA, LCL_JNLPOOL)) \ CSA = NULL; \ } else \ CSA = NULL; \ } # define GET_MSG_IDX(MSG_ID, CTL, IDX) \ { \ assert(NULL != CTL); \ assert(((MSG_ID & 0x0FFFFFFF) & FACMASK(CTL->facnum)) && (MSGMASK(MSG_ID, CTL->facnum) <= CTL->msg_cnt)); \ IDX = MSGMASK(MSG_ID, CTL->facnum) - 1; \ } /* Given a pointer to ctl array (merrors_ctl, gdeerrors_ctl, etc.) and a msg_id, get the structure corresponding to that msg_id */ # define GET_MSG_INFO(MSG_ID, CTL, MSG_INFO) \ { \ int idx; \ \ GET_MSG_IDX(MSG_ID, CTL, idx); \ MSG_INFO = CTL->fst_msg + idx; \ } #endif /* GTMMSG_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/gtmrecv_ch.c0000755000032200000250000000170214342376331016333 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "error.h" error_def(ERR_ASSERT); error_def(ERR_CTRLC); error_def(ERR_FORCEDHALT); error_def(ERR_GTMASSERT); error_def(ERR_GTMASSERT2); error_def(ERR_GTMCHECK); error_def(ERR_MEMORY); error_def(ERR_OUTOFSPACE); error_def(ERR_STACKOFLOW); error_def(ERR_VMSMEMORY); CONDITION_HANDLER(gtmrecv_ch) { START_CH(TRUE); if (!(IS_GTM_ERROR(SIGNAL)) || DUMPABLE || SEVERITY == ERROR) { NEXTCH; } VMS_ONLY( /* warning, info, or success */ CONTINUE; ) assertpro(FALSE); } fis-gtm-V7.0-005/sr_port/gtmrecv_changelog.c0000755000032200000250000001256014342376331017674 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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_time.h" #include "gtm_string.h" #include "gtm_inet.h" #include #ifdef VMS #include /* Required for gtmrecv.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmrecv.h" #include "repl_dbg.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "util.h" #include "repl_log.h" #include "gtm_fcntl.h" #include "gtmio.h" #include "repl_sp.h" GBLREF recvpool_addrs recvpool; GBLREF gtmrecv_options_t gtmrecv_options; error_def(ERR_REPLLOGOPN); error_def(ERR_CHANGELOGINTERVAL); int gtmrecv_changelog(void) { uint4 changelog_accepted = 0; int log_fd = 0; /*used to indicate whether the new specified log file is writable*/ int close_status = 0; /*used to indicate if log file is successfully closed*/ char* err_code; int save_errno = 0; int retry_count = 5; if (0 != recvpool.gtmrecv_local->changelog) { while (0 != retry_count--) { LONG_SLEEP(5); if (!recvpool.gtmrecv_local->changelog) break; } } if (0 != recvpool.upd_proc_local->changelog) { retry_count = 5; while (0 != retry_count--) { LONG_SLEEP(5); if (!recvpool.upd_proc_local->changelog) break; } } /* Grab the recvpool jnlpool option write lock */ if (0 > grab_sem(RECV, RECV_SERV_OPTIONS_SEM)) { util_out_print("Error grabbing recvpool option write lock. Could not initiate change log", TRUE); return (ABNORMAL_SHUTDOWN); } if (0 != recvpool.gtmrecv_local->changelog || 0 != recvpool.upd_proc_local->changelog) { util_out_print("Change log is already in progress. Not initiating change in log file or log interval", TRUE); rel_sem(RECV, RECV_SERV_OPTIONS_SEM); return (ABNORMAL_SHUTDOWN); } if ('\0' != gtmrecv_options.log_file[0]) /* trigger change in log file (for both receiver and update process) */ { if (0 != strcmp(recvpool.gtmrecv_local->log_file, gtmrecv_options.log_file)) { #ifdef UNIX /*check if the new log file is writable*/ OPENFILE3_CLOEXEC(gtmrecv_options.log_file, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, log_fd); if (log_fd < 0) { save_errno = ERRNO; err_code = STRERROR(save_errno); if ('\0' != recvpool.gtmrecv_local->log_file[0]) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_REPLLOGOPN, 6, LEN_AND_STR(gtmrecv_options.log_file), LEN_AND_STR(err_code), LEN_AND_STR(recvpool.gtmrecv_local->log_file)); else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_REPLLOGOPN, 6, LEN_AND_STR(gtmrecv_options.log_file), LEN_AND_STR(err_code), LEN_AND_STR(NULL_DEVICE)); } else { CLOSEFILE_IF_OPEN(log_fd, close_status); assert(close_status==0); changelog_accepted |= REPLIC_CHANGE_LOGFILE; strcpy(recvpool.gtmrecv_local->log_file, gtmrecv_options.log_file); util_out_print("Change log initiated with file !AD", TRUE, LEN_AND_STR(gtmrecv_options.log_file)); } #elif defined(VMS) changelog_accepted |= REPLIC_CHANGE_LOGFILE; strcpy(recvpool.gtmrecv_local->log_file, gtmrecv_options.log_file); util_out_print("Change log initiated with file !AD", TRUE, LEN_AND_STR(gtmrecv_options.log_file)); #endif } else util_out_print("Log file is already !AD. Not initiating change in log file", TRUE, LEN_AND_STR(gtmrecv_options.log_file)); } if (0 != gtmrecv_options.rcvr_log_interval) /* trigger change in receiver log interval */ { if (gtmrecv_options.rcvr_log_interval != recvpool.gtmrecv_local->log_interval) { changelog_accepted |= REPLIC_CHANGE_LOGINTERVAL; recvpool.gtmrecv_local->log_interval = gtmrecv_options.rcvr_log_interval; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_CHANGELOGINTERVAL, 5, LEN_AND_LIT("Receiver"), LEN_AND_STR(recvpool.gtmrecv_local->log_file), gtmrecv_options.rcvr_log_interval); } else util_out_print("Receiver log interval is already !UL. Not initiating change in log interval", TRUE, gtmrecv_options.rcvr_log_interval); } if (0 != gtmrecv_options.upd_log_interval) /* trigger change in update process log interval */ { if (gtmrecv_options.upd_log_interval != recvpool.upd_proc_local->log_interval) { changelog_accepted |= REPLIC_CHANGE_UPD_LOGINTERVAL; recvpool.upd_proc_local->log_interval = gtmrecv_options.upd_log_interval; util_out_print("Change initiated with update process log interval !UL", TRUE, gtmrecv_options.upd_log_interval); } else util_out_print("Update process log interval is already !UL. Not initiating change in log interval", TRUE, gtmrecv_options.upd_log_interval); } if (0 != changelog_accepted) recvpool.gtmrecv_local->changelog = changelog_accepted; else util_out_print("No change to log file or log interval", TRUE); rel_sem(RECV, RECV_SERV_OPTIONS_SEM); return (0 != save_errno) ? ABNORMAL_SHUTDOWN : NORMAL_SHUTDOWN; } fis-gtm-V7.0-005/sr_port/gtmrecv_checkhealth.c0000755000032200000250000001261114342376331020205 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "gtm_time.h" #include #include "gtm_inet.h" #include "gtm_string.h" #ifdef UNIX #include #endif #ifdef VMS #include /* Required for gtmrecv.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmrecv.h" #include "repl_dbg.h" #include "gtm_stdio.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "repl_sp.h" #include "repl_log.h" #include "is_proc_alive.h" GBLREF recvpool_addrs recvpool; GBLREF gtmrecv_options_t gtmrecv_options; int is_srv_alive(int srv_type) { int status, semval; boolean_t start_wait_logged; uint4 srv_pid; boolean_t srv_alive; srv_pid = (GTMRECV == srv_type) ? recvpool.gtmrecv_local->recv_serv_pid : recvpool.upd_proc_local->upd_proc_pid; if (0 < srv_pid) { if (srv_alive = is_proc_alive(srv_pid, 0)) semval = get_sem_info(RECV, (GTMRECV == srv_type) ? RECV_SERV_COUNT_SEM : UPD_PROC_COUNT_SEM, SEM_INFO_VAL); if (srv_alive && 1 == semval) status = SRV_ALIVE; else if (!srv_alive || 0 == semval) status = SRV_DEAD; else status = SRV_ERR; } else status = SRV_DEAD; return (status); } int is_updproc_alive(void) { return (is_srv_alive(UPDPROC)); } int is_recv_srv_alive(void) { return (is_srv_alive(GTMRECV)); } int gtmrecv_checkhealth(void) { int rcv_status, upd_status, helper_status, save_errno; uint4 gtmrecv_pid, updproc_pid, updproc_pid_prev, helper_pid; boolean_t helper_alive; upd_helper_ctl_ptr_t upd_helper_ctl; upd_helper_entry_ptr_t helper, helper_top; recvpool_user helper_type; /* Grab the recvpool option write lock */ if (0 > grab_sem(RECV, RECV_SERV_OPTIONS_SEM)) { UNIX_ONLY(save_errno = errno); repl_log(stderr, FALSE, TRUE, "Error grabbing recvpool option write lock : %s. Could not check health of Receiver" "Server/Update Process\n", UNIX_ONLY(STRERROR(save_errno)) VMS_ONLY(REPL_SEM_ERROR)); return (((SRV_ERR << 2) | SRV_ERR) + NORMAL_SHUTDOWN); } gtmrecv_pid = recvpool.gtmrecv_local->recv_serv_pid; updproc_pid = recvpool.upd_proc_local->upd_proc_pid; updproc_pid_prev = recvpool.upd_proc_local->upd_proc_pid_prev; REPL_DPRINT1("Checking health of Receiver Server/Update Process\n"); rcv_status = is_recv_srv_alive(); upd_status = is_updproc_alive(); switch(rcv_status) { case SRV_ALIVE: repl_log(stderr, FALSE, TRUE, FORMAT_STR, gtmrecv_pid, "Receiver server", ""); break; case SRV_DEAD: repl_log(stderr, FALSE, TRUE, FORMAT_STR, gtmrecv_pid, "Receiver server", " NOT"); if (0 == gtmrecv_pid) { if (NO_SHUTDOWN == recvpool.gtmrecv_local->shutdown) repl_log(stderr, FALSE, TRUE, "Receiver Server crashed during receive pool initialization\n"); else repl_log(stderr, FALSE, TRUE, "Receiver server crashed during shutdown\n"); } break; case SRV_ERR: repl_log(stderr, FALSE, TRUE, "Error finding health of receiver server\n"); break; } switch(upd_status) { case SRV_ALIVE: repl_log(stderr, FALSE, TRUE, FORMAT_STR, updproc_pid, "Update process", ""); break; case SRV_DEAD: repl_log(stderr, FALSE, TRUE, FORMAT_STR, updproc_pid ? updproc_pid : updproc_pid_prev, "Update process", " NOT"); if (0 == updproc_pid) { if (NO_SHUTDOWN == recvpool.upd_proc_local->upd_proc_shutdown) repl_log(stderr, FALSE, TRUE, "Update Process crashed during initialization\n"); else repl_log(stderr, FALSE, TRUE, "Update Process crashed during shutdown\n"); } break; case SRV_ERR: repl_log(stdout, FALSE, TRUE, "Error in finding health of update process\n"); break; } helper_status = SRV_ALIVE; if (gtmrecv_options.helpers) { upd_helper_ctl = recvpool.upd_helper_ctl; for (helper = upd_helper_ctl->helper_list, helper_top = helper + MAX_UPD_HELPERS; helper < helper_top; helper++) { if (0 != (helper_pid = helper->helper_pid_prev)) { helper_type = (recvpool_user)helper->helper_type; helper_alive = is_proc_alive(helper_pid, 0); if (helper_alive && 0 == helper->helper_pid) /* process has vacated its slot, but the rcvr hasn't */ helper_alive = FALSE; /* salvaged it yet. Unix zombies are alive,* er, half dead */ helper_status = (SRV_ALIVE == helper_status && helper_alive) ? SRV_ALIVE : SRV_DEAD; repl_log(stderr, FALSE, TRUE, FORMAT_STR, helper_pid, (UPD_HELPER_READER == helper_type) ? "Helper reader" : "Helper writer", helper_alive ? "" : " NOT"); } } if (SRV_DEAD == helper_status) { /* indicate to the receiver that it has to reap helpers */ upd_helper_ctl->reap_helpers = HELPER_REAP_NOWAIT; while (HELPER_REAP_NONE != upd_helper_ctl->reap_helpers && SRV_ALIVE == is_recv_srv_alive()) SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_SHUTDOWN); upd_helper_ctl->reap_helpers = HELPER_REAP_NONE; /* just in case recvr died */ } } rel_sem(RECV, RECV_SERV_OPTIONS_SEM); return ((rcv_status | (upd_status << 2) | (helper_status << 4)) + NORMAL_SHUTDOWN); } fis-gtm-V7.0-005/sr_port/gtmrecv_comm_init.c0000644000032200000250000001125214342376331017715 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_socket.h" #include "gtm_netdb.h" /* for NI_MAXSERV and AI_V4MAPPED */ #include "gtm_ipv6.h" #include "gtm_inet.h" #include "gtm_fcntl.h" #include #include #include "gtm_unistd.h" #ifdef VMS #include /* Required for gtmrecv.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmrecv.h" #include "repl_sp.h" #include "gtmio.h" #include "util.h" #include "repl_log.h" #include "repl_comm.h" GBLDEF int gtmrecv_listen_sock_fd = FD_INVALID; error_def(ERR_GETADDRINFO); error_def(ERR_REPLCOMM); error_def(ERR_TEXT); /* Initialize communication stuff */ int gtmrecv_comm_init(in_port_t port) { struct addrinfo *ai_ptr = NULL, hints; const int enable_reuseaddr = 1; struct linger disable_linger = {0, 0}; int rc; int errcode; char port_buffer[NI_MAXSERV]; int port_buffer_len; int temp_sock_fd; int af; char err_buffer[512]; struct sockaddr_in local; struct sockaddr *local_sa_ptr; char local_ip[SA_MAXLEN]; char local_port_buffer[NI_MAXSERV]; unsigned int save_errno; GTM_SOCKLEN_TYPE len; if (FD_INVALID != gtmrecv_listen_sock_fd) /* Initialization done already */ return (0); /* Create the socket used for communicating with primary */ af = ((GTM_IPV6_SUPPORTED && !ipv4_only) ? AF_INET6 : AF_INET); if (FD_INVALID == (temp_sock_fd = socket(af, SOCK_STREAM, IPPROTO_TCP))) { af = AF_INET; if (FD_INVALID == (temp_sock_fd = socket(af, SOCK_STREAM, IPPROTO_TCP))) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with receiver server socket create"), ERRNO); return (-1); } } /* Make it known to the world that you are ready for a Source Server */ SERVER_HINTS(hints, af); SNPRINTF(port_buffer, NI_MAXSERV, "%hu", port); if (0 != (errcode = getaddrinfo(NULL, port_buffer, &hints, &ai_ptr))) { CLOSEFILE(temp_sock_fd, rc); RTS_ERROR_ADDRINFO_CTX(NULL, ERR_GETADDRINFO, errcode, "FAILED in obtaining IP address on receiver server."); return -1; } gtmrecv_listen_sock_fd = temp_sock_fd; if (0 > setsockopt(gtmrecv_listen_sock_fd, SOL_SOCKET, SO_LINGER, (const void *)&disable_linger, SIZEOF(disable_linger))) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with receiver server listen socket disable linger"), ERRNO); if (0 > setsockopt(gtmrecv_listen_sock_fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&enable_reuseaddr, SIZEOF(enable_reuseaddr))) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error with receiver server listen socket enable reuseaddr"), ERRNO); } if ((0 > BIND(gtmrecv_listen_sock_fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen)) || (WBTEST_ENABLED(WBTEST_REPL_INIT_ERR)) ) { GTM_WHITE_BOX_TEST(WBTEST_REPL_INIT_ERR, errno, 98); SNPRINTF(err_buffer, 512, "Could not bind local address. Local Port : %hu", port); SEND_SYSMSG_REPLCOMM(LEN_AND_STR(err_buffer)); freeaddrinfo(ai_ptr); CLOSEFILE_RESET(gtmrecv_listen_sock_fd, rc); /* resets "gtmrecv_listen_sock_fd" to FD_INVALID */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(err_buffer), ERRNO); return (-1); } if ((0 > listen(gtmrecv_listen_sock_fd, 5)) || (WBTEST_ENABLED(WBTEST_REPL_INIT_ERR2))) { save_errno = ERRNO; len = SIZEOF(local); if (0 == getsockname(gtmrecv_listen_sock_fd, (struct sockaddr *)&local, (GTM_SOCKLEN_TYPE *)&len)) SNPRINTF(err_buffer, 512, "Could not listen. Local port : %hu", ntohs(local.sin_port)); else SNPRINTF(err_buffer, 512, "Could not listen. Local port : *UNKNOWN* : %s\n", strerror(errno)); GTM_WHITE_BOX_TEST(WBTEST_REPL_INIT_ERR2, save_errno, 98); SEND_SYSMSG_REPLCOMM(LEN_AND_STR(err_buffer)); freeaddrinfo(ai_ptr); CLOSEFILE_RESET(gtmrecv_listen_sock_fd, rc); /* resets "gtmrecv_listen_sock_fd" to FD_INVALID */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(err_buffer), save_errno); return (-1); } freeaddrinfo(ai_ptr); return (0); } fis-gtm-V7.0-005/sr_port/gtmrecv_end_helpers.c0000755000032200000250000000420014342376331020225 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2015 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_stdio.h" #ifdef UNIX #include #elif defined(VMS) #include /* Required for gtmrecv.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "repl_shutdcode.h" #include "gtmrecv.h" #include "is_proc_alive.h" #include "eintr_wrappers.h" #include "repl_log.h" GBLREF recvpool_addrs recvpool; int gtmrecv_end_helpers(boolean_t is_rcvr_srvr) { /* Set flag in recvpool telling the receiver server to stop all reader and writer helpers. * Wait for receiver server to complete the processs - all processes shut down, or some error occurred */ upd_helper_ctl_ptr_t upd_helper_ctl; upd_helper_entry_ptr_t helper, helper_top; repl_log(stdout, TRUE, TRUE, "Initiating shut down of Helpers\n"); upd_helper_ctl = recvpool.upd_helper_ctl; for (helper = upd_helper_ctl->helper_list, helper_top = helper + MAX_UPD_HELPERS; helper < helper_top; helper++) helper->helper_shutdown = SHUTDOWN; /* indicate to the helper to shut down */ if (is_rcvr_srvr) gtmrecv_reap_helpers(TRUE); else { upd_helper_ctl->reap_helpers = HELPER_REAP_WAIT; while (HELPER_REAP_NONE != upd_helper_ctl->reap_helpers && SRV_ALIVE == is_recv_srv_alive()) SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_SHUTDOWN); if (HELPER_REAP_WAIT == upd_helper_ctl->reap_helpers) /* No receiver to clean things up for us */ gtmrecv_reap_helpers(TRUE); /* waitpid will fail, but is_proc_alive() check will work */ else upd_helper_ctl->reap_helpers = HELPER_REAP_NONE; } return NORMAL_SHUTDOWN; } fis-gtm-V7.0-005/sr_port/gtmrecv_exit.c0000755000032200000250000000161014342376331016710 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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" /* for EXIT() */ #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "repl_shutdcode.h" #include "gtmrecv.h" #include "repl_log.h" #include "repl_dbg.h" void gtmrecv_exit(int exit_status) { EXIT(exit_status); } fis-gtm-V7.0-005/sr_port/gtmrecv_get_opt.c0000755000032200000250000002434214342376331017407 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_limits.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_unistd.h" #include "gtm_ctype.h" #include "gtm_inet.h" #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmrecv.h" #include "cli.h" #include "gtm_stdio.h" #include "util.h" #include "repl_log.h" #include "gtm_zlib.h" #ifdef GTM_TLS #include "gtm_repl.h" #endif GBLREF gtmrecv_options_t gtmrecv_options; int gtmrecv_get_opt(void) { boolean_t autorollback, cmplvl_status, filter, log, log_interval_specified, plaintext_fallback; char statslog_val[4]; /* "ON" or "OFF" */ gtm_int64_t buffsize; int status; uint4 n_readers, n_helpers; unsigned short filter_cmd_len, instfilename_len, instname_len, log_file_len, statslog_val_len, tlsid_len; gtmrecv_options.start = (CLI_PRESENT == cli_present("START")); gtmrecv_options.shut_down = (CLI_PRESENT == cli_present("SHUTDOWN")); gtmrecv_options.checkhealth = (CLI_PRESENT == cli_present("CHECKHEALTH")); gtmrecv_options.statslog = (CLI_PRESENT == cli_present("STATSLOG")); gtmrecv_options.showbacklog = (CLI_PRESENT == cli_present("SHOWBACKLOG")); gtmrecv_options.changelog = (CLI_PRESENT == cli_present("CHANGELOG")); gtmrecv_options.updateonly = (CLI_PRESENT == cli_present("UPDATEONLY")); gtmrecv_options.updateresync = (CLI_PRESENT == cli_present("UPDATERESYNC")); gtmrecv_options.reuse_specified = (CLI_PRESENT == cli_present("REUSE")); gtmrecv_options.resume_specified = (CLI_PRESENT == cli_present("RESUME")); gtmrecv_options.initialize_specified = (CLI_PRESENT == cli_present("INITIALIZE")); gtmrecv_options.stopreceiverfilter = (CLI_PRESENT == cli_present("STOPRECEIVERFILTER")); if (gtmrecv_options.updateresync) { instfilename_len = SIZEOF(gtmrecv_options.updresync_instfilename) - 1; /* keep 1 byte for trailing NULL */ /* Treat -UPDATERESYNC (with no value) as if -UPDATERESYNC="" was specified */ if (!cli_get_str("UPDATERESYNC", gtmrecv_options.updresync_instfilename, &instfilename_len)) { instfilename_len = 0; if (gtmrecv_options.reuse_specified) { util_out_print("Error: REUSE qualifier not allowed if UPDATERESYNC qualifier has no value", TRUE); return (-1); } } else if (gtmrecv_options.reuse_specified) { instname_len = SIZEOF(gtmrecv_options.reuse_instname) - 1; /* keep 1 byte for trailing NULL */ if (!cli_get_str("REUSE", gtmrecv_options.reuse_instname, &instname_len)) { util_out_print("Error parsing REUSE qualifier", TRUE); return (-1); } else { assert(SIZEOF(gtmrecv_options.reuse_instname) > instname_len); gtmrecv_options.reuse_instname[instname_len] = '\0'; } } assert(SIZEOF(gtmrecv_options.updresync_instfilename) > instfilename_len); gtmrecv_options.updresync_instfilename[instfilename_len] = '\0'; if (gtmrecv_options.resume_specified) { if (!cli_get_int("RESUME", >mrecv_options.resume_strm_num)) { util_out_print("Error parsing RESUME qualifier", TRUE); return (-1); } if ((0 >= gtmrecv_options.resume_strm_num) || (MAX_SUPPL_STRMS <= gtmrecv_options.resume_strm_num)) { util_out_print("RESUME qualifier should specify a stream number between 1 and 15 (both inclusive)", TRUE); return (-1); } } } gtmrecv_options.noresync = (CLI_PRESENT == cli_present("NORESYNC")); gtmrecv_options.helpers = (CLI_PRESENT == cli_present("HELPERS")); gtmrecv_options.listen_port = 0; /* invalid port; indicates listenport not specified */ if (gtmrecv_options.start && CLI_PRESENT == cli_present("LISTENPORT")) { if (!cli_get_int("LISTENPORT", >mrecv_options.listen_port)) { util_out_print("Error parsing LISTENPORT qualifier", TRUE); return (-1); } if (CLI_PRESENT == cli_present("BUFFSIZE")) { /* use a big conversion so we have a signed number for comparison */ if (!cli_get_int64("BUFFSIZE", &buffsize)) { util_out_print("Error parsing BUFFSIZE qualifier", TRUE); return(-1); } if (MIN_RECVPOOL_SIZE > buffsize) gtmrecv_options.buffsize = MIN_RECVPOOL_SIZE; else if ((gtm_uint64_t)MAX_RECVPOOL_SIZE < buffsize) gtmrecv_options.buffsize = (gtm_uint64_t)MAX_RECVPOOL_SIZE; else gtmrecv_options.buffsize = (gtm_uint64_t)buffsize; } else gtmrecv_options.buffsize = DEFAULT_RECVPOOL_SIZE; /* Check if -autorollback is specified (default is -noautorollback) */ autorollback = cli_present("AUTOROLLBACK"); gtmrecv_options.autorollback = autorollback ? (CLI_NEGATED != autorollback) : FALSE; if (gtmrecv_options.autorollback) gtmrecv_options.autorollback_verbose = cli_present("AUTOROLLBACK.VERBOSE"); /* Check if compression level is specified */ if (cmplvl_status = (CLI_PRESENT == cli_present("CMPLVL"))) { if (!cli_get_int("CMPLVL", >mrecv_options.cmplvl)) { util_out_print("Error parsing CMPLVL qualifier", TRUE); return(-1); } if (GTM_CMPLVL_OUT_OF_RANGE(gtmrecv_options.cmplvl)) gtmrecv_options.cmplvl = ZLIB_CMPLVL_MIN; /* no compression in this case */ /* CMPLVL qualifier should override any value specified in the environment variable gtm_zlib_cmp_level */ gtm_zlib_cmp_level = gtmrecv_options.cmplvl; } else gtmrecv_options.cmplvl = ZLIB_CMPLVL_MIN; /* no compression in this case */ if (filter = (CLI_PRESENT == cli_present("FILTER"))) { filter_cmd_len = MAX_FILTER_CMD_LEN; if (!cli_get_str("FILTER", gtmrecv_options.filter_cmd, &filter_cmd_len)) { util_out_print("Error parsing FILTER qualifier", TRUE); return (-1); } } else gtmrecv_options.filter_cmd[0] = '\0'; gtmrecv_options.stopsourcefilter = (CLI_PRESENT == cli_present("STOPSOURCEFILTER")); /* Check if SSL/TLS secure communication is requested. */ # ifdef GTM_TLS if (CLI_PRESENT == cli_present("TLSID")) { tlsid_len = MAX_TLSID_LEN; if (!cli_get_str("TLSID", repl_tls.id, &tlsid_len)) { util_out_print("Error parsing TLSID qualifier", TRUE); return -1; } assert(0 < tlsid_len); /* Check if plaintext-fallback mode is specified. Default option is NOPLAINTEXTFALLBACK. */ if (CLI_PRESENT == (plaintext_fallback = cli_present("PLAINTEXTFALLBACK"))) repl_tls.plaintext_fallback = (plaintext_fallback != CLI_NEGATED); else repl_tls.plaintext_fallback = FALSE; } # endif } if ((gtmrecv_options.start && 0 != gtmrecv_options.listen_port) || gtmrecv_options.statslog || gtmrecv_options.changelog) { log = (CLI_PRESENT == cli_present("LOG")); log_interval_specified = (CLI_PRESENT == cli_present("LOG_INTERVAL")); if (log) { log_file_len = MAX_FN_LEN + 1; if (!cli_get_str("LOG", gtmrecv_options.log_file, &log_file_len)) { util_out_print("Error parsing LOG qualifier", TRUE); return (-1); } } else gtmrecv_options.log_file[0] = '\0'; gtmrecv_options.rcvr_log_interval = gtmrecv_options.upd_log_interval = 0; if (log_interval_specified && 0 == cli_parse_two_numbers("LOG_INTERVAL", GTMRECV_LOGINTERVAL_DELIM, >mrecv_options.rcvr_log_interval, >mrecv_options.upd_log_interval)) return (-1); if (gtmrecv_options.start) { if (0 == gtmrecv_options.rcvr_log_interval) gtmrecv_options.rcvr_log_interval = LOGTRNUM_INTERVAL; if (0 == gtmrecv_options.upd_log_interval) gtmrecv_options.upd_log_interval = LOGTRNUM_INTERVAL; } /* For changelog, interval == 0 implies don't change log interval already established */ /* We ignore interval specification for statslog, Vinaya 2005/02/07 */ } if (gtmrecv_options.shut_down) { if (CLI_PRESENT == (status = cli_present("TIMEOUT"))) { if (!cli_get_int("TIMEOUT", >mrecv_options.shutdown_time)) { util_out_print("Error parsing TIMEOUT qualifier", TRUE); return (-1); } if (DEFAULT_SHUTDOWN_TIMEOUT < gtmrecv_options.shutdown_time || 0 > gtmrecv_options.shutdown_time) { gtmrecv_options.shutdown_time = DEFAULT_SHUTDOWN_TIMEOUT; util_out_print("shutdown TIMEOUT changed to !UL", TRUE, gtmrecv_options.shutdown_time); } } else if (CLI_NEGATED == status) gtmrecv_options.shutdown_time = -1; else /* TIMEOUT not specified */ gtmrecv_options.shutdown_time = DEFAULT_SHUTDOWN_TIMEOUT; } if (gtmrecv_options.statslog) { statslog_val_len = 3; /* max(strlen("ON"), strlen("OFF")) */ if (!cli_get_str("STATSLOG", statslog_val, &statslog_val_len)) { util_out_print("Error parsing STATSLOG qualifier", TRUE); return (-1); } statslog_val[statslog_val_len] = '\0'; cli_strupper(statslog_val); if (0 == STRCMP(statslog_val, "ON")) gtmrecv_options.statslog = TRUE; else if (0 == STRCMP(statslog_val, "OFF")) gtmrecv_options.statslog = FALSE; else { util_out_print("Invalid value for STATSLOG qualifier, should be either ON or OFF", TRUE); return (-1); } } gtmrecv_options.n_readers = gtmrecv_options.n_writers = 0; if (gtmrecv_options.helpers && gtmrecv_options.start) { /* parse the helpers qualifier to find out how many readers and writes have to be started */ if (0 == (status = cli_parse_two_numbers("HELPERS", UPD_HELPERS_DELIM, &n_helpers, &n_readers))) return (-1); if (!(status & CLI_2NUM_FIRST_SPECIFIED)) n_helpers = DEFAULT_UPD_HELPERS; if (MIN_UPD_HELPERS > n_helpers || MAX_UPD_HELPERS < n_helpers) { util_out_print("Invalid number of helpers; must be in the range [!UL,!UL]", TRUE, MIN_UPD_HELPERS, MAX_UPD_HELPERS); return (-1); } if (!(status & CLI_2NUM_SECOND_SPECIFIED)) n_readers = (int)(n_helpers * ((float)DEFAULT_UPD_HELP_READERS)/DEFAULT_UPD_HELPERS); /* may round down */ if (n_readers > n_helpers) { n_readers = n_helpers; util_out_print("Number of readers exceeds number of helpers, reducing number of readers to number of " "helpers", TRUE); } gtmrecv_options.n_readers = n_readers; gtmrecv_options.n_writers = n_helpers - n_readers; } return (0); } fis-gtm-V7.0-005/sr_port/gtmrecv_helpers_init.c0000644000032200000250000001554214342376331020432 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2021 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" #ifdef UNIX #include #include "gtm_unistd.h" #include "fork_init.h" #include #elif defined(VMS) #include /* Required for gtmrecv.h */ #endif #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "repl_shutdcode.h" #include "gtmrecv.h" #include "repl_dbg.h" #include "repl_errno.h" #include "gtm_stdio.h" #include "repl_sem.h" #include "io.h" #include "is_proc_alive.h" #include "gtmmsg.h" #include "trans_log_name.h" #include "repl_log.h" #include "eintr_wrappers.h" #include "memcoherency.h" #include "getjobnum.h" #include "gtmimagename.h" #include "wbox_test_init.h" #define UPDHELPER_CMD_MAXLEN GTM_PATH_MAX #define UPDHELPER_CMD "%s/mupip" #define UPDHELPER_CMD_FILE "mupip" #define UPDHELPER_CMD_ARG1 "replicate" #define UPDHELPER_CMD_ARG2 "-updhelper" #define UPDHELPER_READER_CMD_ARG3 "-reader" #define UPDHELPER_WRITER_CMD_ARG3 "-writer" #define UPDHELPER_READER_CMD_STR "REPLICATE/UPDHELPER/READER" #define UPDHELPER_WRITER_CMD_STR "REPLICATE/UPDHELPER/WRITER" #define UPDHELPER_MBX_PREFIX "GTMH" /* first three must be GTM, and only character left for uniqueness */ /* U for update process, R for receiver, S for source, H for helper */ GBLREF recvpool_addrs recvpool; GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF boolean_t gtm_dist_ok_to_use; GBLREF FILE *gtmrecv_log_fp; GBLREF uint4 process_id; LITREF gtmImageName gtmImageNames[]; error_def(ERR_GTMDISTUNVERIF); error_def(ERR_LOGTOOLONG); error_def(ERR_RECVPOOLSETUP); error_def(ERR_TEXT); error_def(ERR_HLPPROC); static int helper_init(upd_helper_entry_ptr_t helper, recvpool_user helper_type) { int save_errno, save_shutdown; char helper_cmd[UPDHELPER_CMD_MAXLEN]; int helper_cmd_len; int status; int4 i4status; pid_t helper_pid, waitpid_res; save_shutdown = helper->helper_shutdown; helper->helper_shutdown = NO_SHUTDOWN; if (!gtm_dist_ok_to_use) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_GTMDISTUNVERIF, 4, STRLEN(gtm_dist), gtm_dist, gtmImageNames[image_type].imageNameLen, gtmImageNames[image_type].imageName); if (WBTEST_ENABLED(WBTEST_MAXGTMDIST_HELPER_PROCESS)) { memset(gtm_dist, 'a', GTM_PATH_MAX-2); gtm_dist[GTM_PATH_MAX-1] = '\0'; } helper_cmd_len = SNPRINTF(helper_cmd, UPDHELPER_CMD_MAXLEN, UPDHELPER_CMD, gtm_dist); if ((-1 == helper_cmd_len) || (UPDHELPER_CMD_MAXLEN <= helper_cmd_len)) { helper->helper_shutdown = save_shutdown; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_HLPPROC, 0, ERR_TEXT, 2, LEN_AND_LIT("Could not find path of Helper Process. Check value of $gtm_dist")); repl_errno = EREPL_UPDSTART_BADPATH; return UPDPROC_START_ERR ; } FORK(helper_pid); if (0 > helper_pid) { save_errno = errno; helper->helper_shutdown = save_shutdown; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_HLPPROC, 0, ERR_TEXT, 2, LEN_AND_LIT("Could not fork Helper process"), save_errno); repl_errno = EREPL_UPDSTART_FORK; return UPDPROC_START_ERR; } if (0 == helper_pid) { /* helper */ getjobnum(); helper->helper_pid_prev = process_id; /* identify owner of slot */ if (WBTEST_ENABLED(WBTEST_BADEXEC_HELPER_PROCESS)) STRCPY(helper_cmd, "ersatz"); if (-1 == EXECL(helper_cmd, helper_cmd, UPDHELPER_CMD_ARG1, UPDHELPER_CMD_ARG2, (UPD_HELPER_READER == helper_type) ? UPDHELPER_READER_CMD_ARG3 : UPDHELPER_WRITER_CMD_ARG3, NULL)) { save_errno = errno; helper->helper_shutdown = save_shutdown; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_HLPPROC, 0, ERR_TEXT, 2, LEN_AND_LIT("Could not exec Helper Process"), save_errno); repl_errno = EREPL_UPDSTART_EXEC; UNDERSCORE_EXIT(UPDPROC_START_ERR); } } /* Wait for helper to startup */ while (helper_pid != helper->helper_pid && is_proc_alive(helper_pid, 0)) { SHORT_SLEEP(GTMRECV_WAIT_FOR_SRV_START); UNIX_ONLY(WAITPID(helper_pid, &status, WNOHANG, waitpid_res);) /* Release defunct helper process if dead */ } /* The helper has now gone far enough in the initialization, or died before initialization. Consider startup completed. */ repl_log(gtmrecv_log_fp, TRUE, TRUE, "Helper %s started. PID %d [0x%X]\n", (UPD_HELPER_READER == helper_type) ? "reader" : "writer", helper_pid, helper_pid); return UPDPROC_STARTED; } int gtmrecv_helpers_init(int n_readers, int n_writers) { /* Receiver server interface to start n_readers and n_writers helper processes */ upd_helper_ctl_ptr_t upd_helper_ctl; upd_helper_entry_ptr_t helper, helper_top; int reader_count, writer_count, error_count, avail_slots, status; assert(0 != n_readers || 0 != n_writers); upd_helper_ctl = recvpool.upd_helper_ctl; for (avail_slots = 0, helper = upd_helper_ctl->helper_list, helper_top = helper + MAX_UPD_HELPERS; helper < helper_top; helper++) { if (0 == helper->helper_pid) avail_slots++; } if (n_readers + n_writers > avail_slots) { /* adjust reader/writer count for available slots according to the percentage specified by user */ n_writers = (int)(((float)n_writers/(n_readers + n_writers)) * (float)avail_slots); /* may round down */ n_readers = avail_slots - n_writers; /* preference to readers, writer count may round down */ } /* Start helpers, readers first */ for (helper = upd_helper_ctl->helper_list, helper_top = helper + MAX_UPD_HELPERS, reader_count = 0, writer_count = 0, error_count = 0; (reader_count + writer_count + error_count) < (n_readers + n_writers); ) { for (; 0 != helper->helper_pid && helper < helper_top; helper++) /* find next vacant slot */ ; assertpro(helper != helper_top); status = helper_init(helper, ((reader_count + error_count) < n_readers) ? UPD_HELPER_READER : UPD_HELPER_WRITER); if (UPDPROC_STARTED == status) { if ((reader_count + error_count) < n_readers) reader_count++; else writer_count++; } else /* UPDPROC_START_ERR == status */ { if ((EREPL_UPDSTART_BADPATH == repl_errno) /* receiver server lost gtm_dist environment, bad situation */ || (EREPL_UPDSTART_EXEC == repl_errno)) /* in forked child, could not exec, should exit */ gtmrecv_exit(ABNORMAL_SHUTDOWN); error_count++; } } upd_helper_ctl->start_n_readers = reader_count; upd_helper_ctl->start_n_writers = writer_count; SHM_WRITE_MEMORY_BARRIER; upd_helper_ctl->start_helpers = FALSE; return ((0 == error_count) ? NORMAL_SHUTDOWN : ABNORMAL_SHUTDOWN); } fis-gtm-V7.0-005/sr_port/gtmrecv_reap_helpers.c0000755000032200000250000000374014342376331020416 0ustar librarygtc/**************************************************************** * * * Copyright 2005 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 "gtm_stdio.h" #ifdef UNIX #include #elif defined(VMS) #include /* Required for gtmrecv.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "repl_shutdcode.h" #include "gtmrecv.h" #include "is_proc_alive.h" #include "eintr_wrappers.h" #include "repl_log.h" GBLREF recvpool_addrs recvpool; void gtmrecv_reap_helpers(boolean_t wait) { upd_helper_ctl_ptr_t upd_helper_ctl; upd_helper_entry_ptr_t helper, helper_top; uint4 helper_pid; int exit_status; UNIX_ONLY(pid_t waitpid_res;) upd_helper_ctl = recvpool.upd_helper_ctl; for (helper = upd_helper_ctl->helper_list, helper_top = helper + MAX_UPD_HELPERS; helper < helper_top; helper++) { while (0 != (helper_pid = helper->helper_pid_prev)) { UNIX_ONLY(WAITPID(helper_pid, &exit_status, WNOHANG, waitpid_res);) /* release defunct helper if dead */ if (UNIX_ONLY(waitpid_res == helper_pid || ) !is_proc_alive(helper_pid, 0)) { helper->helper_pid = 0; /* release entry */ if (NORMAL_SHUTDOWN == helper->helper_shutdown) { /* zombie has been released (on Unix only). Clean-up the slot */ helper->helper_pid_prev = 0; helper->helper_shutdown = NO_SHUTDOWN; } /* else helper shutdown abnormal, continue to report in checkhealth */ break; } if (!wait) break; SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_SHUTDOWN); } } upd_helper_ctl->reap_helpers = HELPER_REAP_NONE; } fis-gtm-V7.0-005/sr_port/gtmrecv_reinit_logseqno.c0000755000032200000250000000201114342376331021134 0ustar librarygtc/**************************************************************** * * * Copyright 2005 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 "gtm_inet.h" #ifdef VMS #include #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmrecv.h" GBLREF recvpool_addrs recvpool; GBLREF seq_num lastlog_seqno; GBLREF uint4 log_interval; GBLREF qw_num trans_recvd_cnt, last_log_tr_recvd_cnt; void gtmrecv_reinit_logseqno(void) { lastlog_seqno = recvpool.recvpool_ctl->jnl_seqno - log_interval; trans_recvd_cnt = -(qw_num)(log_interval - 1); last_log_tr_recvd_cnt = 0; } fis-gtm-V7.0-005/sr_port/gtmrecv_showbacklog.c0000755000032200000250000000401414342376331020243 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2008 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" #if !defined(__MVS__) && !defined(VMS) #include #endif #include #include #include "gtm_string.h" #include "gtm_inet.h" #ifdef VMS #include /* Required for gtmrecv.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmrecv.h" #include "repl_dbg.h" #include "repl_shutdcode.h" #include "util.h" GBLREF recvpool_addrs recvpool; GBLREF gtmrecv_options_t gtmrecv_options; GBLREF seq_num seq_num_zero; GBLREF seq_num seq_num_one; int gtmrecv_showbacklog(void) { seq_num seq_num, read_jnl_seqno, jnl_seqno; QWASSIGN(jnl_seqno, recvpool.recvpool_ctl->jnl_seqno); if (QWEQ(jnl_seqno, seq_num_zero)) QWASSIGN(jnl_seqno, recvpool.recvpool_ctl->old_jnl_seqno); QWASSIGN(read_jnl_seqno, recvpool.upd_proc_local->read_jnl_seqno); QWSUB(seq_num, jnl_seqno, read_jnl_seqno); util_out_print("!@UQ : number of backlog transactions received by receiver server and yet to be processed " "by update process", TRUE, &seq_num); QWASSIGN(seq_num, jnl_seqno); if (QWNE(seq_num, seq_num_zero)) QWDECRBY(seq_num, seq_num_one); util_out_print("!@UQ : sequence number of last transaction received from Source Server and written to receive pool", TRUE, &seq_num); QWASSIGN(seq_num, read_jnl_seqno); if (QWNE(seq_num, seq_num_zero)) QWDECRBY(seq_num, seq_num_one); util_out_print("!@UQ : sequence number of last transaction processed by update process", TRUE, &seq_num); return (NORMAL_SHUTDOWN); } fis-gtm-V7.0-005/sr_port/gtmrecv_start_helpers.c0000755000032200000250000000735514342376331020632 0ustar librarygtc/**************************************************************** * * * Copyright 2005, 2009 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. * * * ****************************************************************/ #if defined(__MVS__) && !defined(_ISOC99_SOURCE) #define _ISOC99_SOURCE #endif #include "mdef.h" #include "gtm_stdio.h" #include "gtm_string.h" #ifdef VMS #include /* Required for gtmrecv.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "repl_shutdcode.h" #include "gtmrecv.h" #include "gtmmsg.h" #include "is_proc_alive.h" #include "memcoherency.h" GBLREF recvpool_addrs recvpool; int gtmrecv_start_helpers(int n_readers, int n_writers) { /* Set flag in recvpool telling the receiver server to start n_readers and n_writers helper processes. * Wait for receiver server to complete the process - completed successfully, or terminated with error */ upd_helper_ctl_ptr_t upd_helper_ctl; upd_helper_entry_ptr_t helper, helper_top; char err_str[BUFSIZ]; int avail_slots, started_readers, started_writers; error_def(ERR_REPLERR); error_def(ERR_REPLINFO); error_def(ERR_REPLWARN); assert(0 != n_readers || 0 != n_writers); upd_helper_ctl = recvpool.upd_helper_ctl; /* let's clean up dead helpers first so we get an accurate count of available slots */ upd_helper_ctl->reap_helpers = HELPER_REAP_NOWAIT; while (HELPER_REAP_NONE != upd_helper_ctl->reap_helpers && SRV_ALIVE == is_recv_srv_alive()) SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_SHUTDOWN); upd_helper_ctl->reap_helpers = HELPER_REAP_NONE; /* just in case recvr died */ /* count available slots so receiver doesn't have to */ for (avail_slots = 0, helper = upd_helper_ctl->helper_list, helper_top = helper + MAX_UPD_HELPERS; helper < helper_top; helper++) { if (0 == helper->helper_pid) { avail_slots++; helper->helper_pid_prev = 0; /* force out abnormally terminated helpers as well */ helper->helper_shutdown = NO_SHUTDOWN; /* clean state */ } } if (avail_slots < n_readers + n_writers) { SNPRINTF(err_str, SIZEOF(err_str), "%d helpers will exceed the maximum allowed (%d), limit the helpers to %d\n", n_readers + n_writers, MAX_UPD_HELPERS, avail_slots); gtm_putmsg(VARLSTCNT(4) ERR_REPLERR, 2, LEN_AND_STR(err_str)); return ABNORMAL_SHUTDOWN; } upd_helper_ctl->start_n_readers = n_readers; upd_helper_ctl->start_n_writers = n_writers; SHM_WRITE_MEMORY_BARRIER; upd_helper_ctl->start_helpers = TRUE; /* hey receiver, let's go, start 'em up */ while (upd_helper_ctl->start_helpers && SRV_ALIVE == is_recv_srv_alive()) SHORT_SLEEP(GTMRECV_WAIT_FOR_SRV_START); if (!upd_helper_ctl->start_helpers) { started_readers = upd_helper_ctl->start_n_readers; started_writers = upd_helper_ctl->start_n_writers; SNPRINTF(err_str, SIZEOF(err_str), "%s %d out of %d readers and %d out of %d writers started", ((started_readers + started_writers) == (n_readers + n_writers)) ? "All" : "Only", started_readers, n_readers, started_writers, n_writers); if ((started_readers + started_writers) == (n_readers + n_writers)) { gtm_putmsg(VARLSTCNT(4) ERR_REPLINFO, 2, LEN_AND_STR(err_str)); return NORMAL_SHUTDOWN; } gtm_putmsg(VARLSTCNT(4) ERR_REPLWARN, 2, LEN_AND_STR(err_str)); return ABNORMAL_SHUTDOWN; } gtm_putmsg(VARLSTCNT(4) ERR_REPLERR, 2, LEN_AND_LIT("Receiver server is not alive to start helpers. Start receiver server first")); return ABNORMAL_SHUTDOWN; } fis-gtm-V7.0-005/sr_port/gtmrecv_statslog.c0000644000032200000250000000416014342376331017577 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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" #if !defined(__MVS__) && !defined(VMS) #include #endif #include #include #include "gtm_string.h" #include "gtm_inet.h" #ifdef UNIX #include #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmrecv.h" #include "repl_dbg.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "util.h" #include "gtm_fcntl.h" #include "gtmio.h" #include "repl_log.h" #include "repl_sp.h" GBLREF recvpool_addrs recvpool; GBLREF gtmrecv_options_t gtmrecv_options; error_def(ERR_REPLLOGOPN); int gtmrecv_statslog(void) { /* Grab the recvpool option write lock */ if (0 > grab_sem(RECV, RECV_SERV_OPTIONS_SEM)) { util_out_print("Error grabbing recvpool option write lock. Could not initiate stats log", TRUE); return (ABNORMAL_SHUTDOWN); } if (gtmrecv_options.statslog == recvpool.gtmrecv_local->statslog) { util_out_print("STATSLOG is already !AD. Not initiating change in stats log", TRUE, gtmrecv_options.statslog ? strlen("ON") : strlen("OFF"), gtmrecv_options.statslog ? "ON" : "OFF"); rel_sem_immediate(RECV, RECV_SERV_OPTIONS_SEM); return (ABNORMAL_SHUTDOWN); } if (!gtmrecv_options.statslog) { recvpool.gtmrecv_local->statslog = FALSE; util_out_print("STATSLOG turned OFF", TRUE); rel_sem_immediate(RECV, RECV_SERV_OPTIONS_SEM); return (NORMAL_SHUTDOWN); } recvpool.gtmrecv_local->statslog = TRUE; util_out_print("Stats log turned on", TRUE); rel_sem_immediate(RECV, RECV_SERV_OPTIONS_SEM); return (NORMAL_SHUTDOWN); } fis-gtm-V7.0-005/sr_port/gtmrecv_upd_proc_init.c0000644000032200000250000002514314342376331020601 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_time.h" #include "gtm_fcntl.h" #include "gtm_string.h" #include "gtm_unistd.h" #include "gtm_inet.h" #ifdef UNIX #include #include "fork_init.h" #elif defined(VMS) #include #endif #include #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gtmrecv.h" #include "repl_dbg.h" #include "repl_errno.h" #include "iosp.h" #include "gtm_stdio.h" #include "repl_shutdcode.h" #include "repl_sem.h" #include "io.h" #include "is_proc_alive.h" #include "gtmmsg.h" #include "trans_log_name.h" #include "repl_log.h" #include "eintr_wrappers.h" #include "gtmimagename.h" #include "wbox_test_init.h" #define UPDPROC_CMD_MAXLEN GTM_PATH_MAX #define UPDPROC_CMD "%s/mupip" #define UPDPROC_CMD_FILE "mupip" #define UPDPROC_CMD_ARG1 "replicate" #define UPDPROC_CMD_ARG2 "-updateproc" #define UPDPROC_CMD_STR "REPLICATE/UPDATEPROC" GBLREF recvpool_addrs recvpool; GBLREF int recvpool_shmid; GBLREF char gtm_dist[GTM_PATH_MAX]; GBLREF boolean_t gtm_dist_ok_to_use; GBLREF int gtmrecv_log_fd; GBLREF FILE *gtmrecv_log_fp; GBLREF int updproc_log_fd; LITREF gtmImageName gtmImageNames[]; error_def(ERR_GTMDISTUNVERIF); error_def(ERR_LOGTOOLONG); error_def(ERR_RECVPOOLSETUP); error_def(ERR_REPLINFO); error_def(ERR_SYSCALL); error_def(ERR_TEXT); error_def(ERR_UPDPROC); int gtmrecv_upd_proc_init(boolean_t fresh_start) { /* Update Process initialization */ char upd_proc_cmd[UPDPROC_CMD_MAXLEN]; int upd_proc_cmd_len; int status, save_errno; int upd_status, save_upd_status; #ifdef UNIX pid_t upd_pid, waitpid_res; #elif defined(VMS) uint4 upd_pid; uint4 cmd_channel; $DESCRIPTOR(cmd_desc, UPDPROC_CMD_STR); #endif pthread_mutexattr_t write_updated_ctl_attr; pthread_condattr_t write_updated_attr; /* Check if the update process is alive */ if ((upd_status = is_updproc_alive()) == SRV_ERR) { save_errno = errno; /* errno from get_sem_info() called from is_updproc_alive() */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Receive pool semctl failure"), UNIX_ONLY(save_errno) VMS_ONLY(REPL_SEM_ERRNO)); repl_errno = EREPL_UPDSTART_SEMCTL; return(UPDPROC_START_ERR); } else if (upd_status == SRV_ALIVE && !fresh_start) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TEXT, 2, RTS_ERROR_LITERAL("Update process already exists. Not starting it")); return(UPDPROC_EXISTS); } else if (upd_status == SRV_ALIVE) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_UPDPROC, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Update process already exists. Please kill it before a fresh start")); return(UPDPROC_EXISTS); } save_upd_status = recvpool.upd_proc_local->upd_proc_shutdown; recvpool.upd_proc_local->upd_proc_shutdown = NO_SHUTDOWN; #ifdef UNIX if (!gtm_dist_ok_to_use) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_GTMDISTUNVERIF, 4, STRLEN(gtm_dist), gtm_dist, gtmImageNames[image_type].imageNameLen, gtmImageNames[image_type].imageName); if (WBTEST_ENABLED(WBTEST_MAXGTMDIST_UPDATE_PROCESS)) { memset(gtm_dist, 'a', GTM_PATH_MAX-2); gtm_dist[GTM_PATH_MAX-1] = '\0'; } upd_proc_cmd_len = SNPRINTF(upd_proc_cmd, UPDPROC_CMD_MAXLEN, UPDPROC_CMD, gtm_dist); if ((-1 == upd_proc_cmd_len) || (UPDPROC_CMD_MAXLEN <= upd_proc_cmd_len)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_UPDPROC, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Could not find path of Update Process. Check value of $gtm_dist")); repl_errno = EREPL_UPDSTART_BADPATH; return(UPDPROC_START_ERR); } /* Destroy/Reinitialize the mutex/cond. * Needed here in case the update process exited while holding the mutex, and the system didn't clean it up. * Robust mutexes should handle this case, in theory, but that does not appear to be the case in practice. */ pthread_mutex_destroy(&recvpool.recvpool_ctl->write_updated_ctl); status = pthread_mutexattr_init(&write_updated_ctl_attr); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutexattr_init"), CALLFROM, status, 0); status = pthread_mutexattr_settype(&write_updated_ctl_attr, PTHREAD_MUTEX_ERRORCHECK); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutexattr_settype"), CALLFROM, status, 0); status = pthread_mutexattr_setpshared(&write_updated_ctl_attr, PTHREAD_PROCESS_SHARED); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutexattr_setpshared"), CALLFROM, status, 0); # if PTHREAD_MUTEX_ROBUST_SUPPORTED status = pthread_mutexattr_setrobust(&write_updated_ctl_attr, PTHREAD_MUTEX_ROBUST); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutexattr_setrobust"), CALLFROM, status, 0); # endif status = pthread_mutex_init(&recvpool.recvpool_ctl->write_updated_ctl, &write_updated_ctl_attr); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_mutex_init"), CALLFROM, status, 0); memset(&recvpool.recvpool_ctl->write_updated, 0, SIZEOF(recvpool.recvpool_ctl->write_updated)); status = pthread_condattr_init(&write_updated_attr); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_condattr_init"), CALLFROM, status, 0); status = pthread_condattr_setpshared(&write_updated_attr, PTHREAD_PROCESS_SHARED); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_condattr_setpshared"), CALLFROM, status, 0); status = pthread_cond_init(&recvpool.recvpool_ctl->write_updated, &write_updated_attr); if (0 != status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("pthread_cond_init"), CALLFROM, status, 0); FORK(upd_pid); if (0 > upd_pid) { recvpool.upd_proc_local->upd_proc_shutdown = save_upd_status; gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_UPDPROC, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Could not fork update process"), errno); repl_errno = EREPL_UPDSTART_FORK; return(UPDPROC_START_ERR); } if (0 == upd_pid) { /* Update Process */ if (WBTEST_ENABLED(WBTEST_BADEXEC_UPDATE_PROCESS)) STRCPY(upd_proc_cmd, "ersatz"); if (EXECL(upd_proc_cmd, upd_proc_cmd, UPDPROC_CMD_ARG1, UPDPROC_CMD_ARG2, NULL) < 0) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_UPDPROC, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Could not exec Update Process"), errno); repl_errno = EREPL_UPDSTART_EXEC; UNDERSCORE_EXIT(UPDPROC_START_ERR); } } #elif defined(VMS) /* Create detached server and write startup commands to it */ status = repl_create_server(&cmd_desc, "GTMU", "", &cmd_channel, &upd_pid, ERR_RECVPOOLSETUP); if (SS_NORMAL != status) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to spawn Update process"), status); recvpool.upd_proc_local->upd_proc_shutdown = save_upd_status; repl_errno = EREPL_UPDSTART_FORK; return(UPDPROC_START_ERR); } #endif if (recvpool.upd_proc_local->upd_proc_pid) recvpool.upd_proc_local->upd_proc_pid_prev = recvpool.upd_proc_local->upd_proc_pid; else recvpool.upd_proc_local->upd_proc_pid_prev = upd_pid; recvpool.upd_proc_local->upd_proc_pid = upd_pid; /* Receiver Server; wait for the update process to startup */ REPL_DPRINT2("Waiting for update process %d to startup\n", upd_pid); while (get_sem_info(RECV, UPD_PROC_COUNT_SEM, SEM_INFO_VAL) == 0 && is_proc_alive(upd_pid, 0)) { /* To take care of reassignment of PIDs, the while condition should be && with the * condition (PPID of pid == process_id) */ REPL_DPRINT2("Waiting for update process %d to startup\n", upd_pid); UNIX_ONLY(WAITPID(upd_pid, &status, WNOHANG, waitpid_res);) /* Release defunct update process if dead */ SHORT_SLEEP(GTMRECV_WAIT_FOR_SRV_START); } #ifdef VMS /* Deassign the send-cmd mailbox channel */ if (SS_NORMAL != (status = sys$dassgn(cmd_channel))) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to close upd-send-cmd mbox channel"), status); recvpool.upd_proc_local->upd_proc_shutdown = save_upd_status; repl_errno = EREPL_UPDSTART_BADPATH; /* Just to make an auto-shutdown */ return(UPDPROC_START_ERR); } #endif repl_log(gtmrecv_log_fp, TRUE, FALSE, "Update Process started. PID %d [0x%X]\n", upd_pid, upd_pid); return(UPDPROC_STARTED); } int gtmrecv_start_updonly(void) { int start_status, recvr_status, upd_status; if ((upd_status = is_updproc_alive()) == SRV_ALIVE) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_UPDPROC, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Update Process exists already. New process not started")); return(UPDPROC_START_ERR); } else if (upd_status == SRV_ERR) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_UPDPROC, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error in starting up update process")); return(UPDPROC_START_ERR); } assert(upd_status == SRV_DEAD); #ifdef VMS recvpool.upd_proc_local->changelog |= REPLIC_CHANGE_LOGFILE; #endif recvpool.upd_proc_local->start_upd = UPDPROC_START; while ((start_status = recvpool.upd_proc_local->start_upd) == UPDPROC_START && (recvr_status = is_recv_srv_alive()) == SRV_ALIVE) SHORT_SLEEP(GTMRECV_WAIT_FOR_SRV_START); if (start_status == UPDPROC_STARTED) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_REPLINFO, 2, RTS_ERROR_LITERAL("Update Process started successfully")); return(UPDPROC_STARTED); } #ifdef VMS recvpool.upd_proc_local->changelog &= ~REPLIC_CHANGE_LOGFILE; #endif if (start_status == UPDPROC_START) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_UPDPROC, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Receiver server is not alive to start update process. Please start receiver server")); } else if (start_status == UPDPROC_START_ERR) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_UPDPROC, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error starting update process")); } else if (start_status == UPDPROC_EXISTS) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_UPDPROC, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Update Process exists already. New process not started")); } return(UPDPROC_START_ERR); } fis-gtm-V7.0-005/sr_port/gtmsource_ch.c0000755000032200000250000000562114342376331016700 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include "gtm_unistd.h" #include "gtm_fcntl.h" #include "gtm_inet.h" #ifdef UNIX #include "gtm_ipc.h" #include #elif defined(VMS) #include #include /* Required for gtmrecv.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "repl_shutdcode.h" #include "repl_msg.h" #include "repl_comm.h" #include "gtmsource.h" #include "error.h" #include "dpgbldir.h" #include "wbox_test_init.h" #ifdef UNIX #include "ftok_sems.h" #endif GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF boolean_t is_src_server; GBLREF gtmsource_options_t gtmsource_options; GBLREF int gtmsource_sock_fd; error_def(ERR_ASSERT); error_def(ERR_CTRLC); error_def(ERR_FORCEDHALT); error_def(ERR_GTMASSERT); error_def(ERR_GTMASSERT2); error_def(ERR_GTMCHECK); error_def(ERR_OUTOFSPACE); error_def(ERR_REPLSRCEXITERR); error_def(ERR_STACKOFLOW); error_def(ERR_MEMORY); error_def(ERR_VMSMEMORY); CONDITION_HANDLER(gtmsource_ch) { gd_addr *addr_ptr; gd_region *reg_local, *reg_top; sgmnt_addrs *csa; START_CH(TRUE); if (!(IS_GTM_ERROR(SIGNAL)) || DUMPABLE || SEVERITY == ERROR) { for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) { for (reg_local = addr_ptr->regions, reg_top = reg_local + addr_ptr->n_regions; reg_local < reg_top; reg_local++) { if (reg_local->open && !reg_local->was_open) { csa = (sgmnt_addrs *)&FILE_INFO(reg_local)->s_addrs; if (csa && (csa->now_crit)) rel_crit(reg_local); } } } if ((NULL != jnlpool) && (jnlpool->jnlpool_ctl)) { csa = (sgmnt_addrs *)&FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs; if (csa && csa->now_crit) rel_lock(jnlpool->jnlpool_dummy_reg); } if (is_src_server) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_REPLSRCEXITERR, 2, gtmsource_options.secondary_instname, gtmsource_options.log_file); /* When WBTEST_INDUCE_TLSIOERR, intentionally skip the gracefull close of the socket descriptor to * induce a TLSIOERR in the Receiver Server */ DEBUG_ONLY(if (!WBTEST_ENABLED(WBTEST_INDUCE_TLSIOERR))) if (FD_INVALID != gtmsource_sock_fd) /* Close the socket if open */ repl_close(>msource_sock_fd); NEXTCH; } VMS_ONLY ( /* warning, info, or success */ CONTINUE; ) assertpro(FALSE); } fis-gtm-V7.0-005/sr_port/gtmsource_comm_init.c0000644000032200000250000000767214342376331020271 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #if defined(__MVS__) && !defined(_ISOC99_SOURCE) #define _ISOC99_SOURCE #endif #include "mdef.h" #include #include #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_socket.h" #include "gtm_netdb.h" #include "gtm_ipv6.h" #include "gtm_inet.h" #include "gtm_fcntl.h" #include "gtm_unistd.h" #ifdef VMS #include /* Required for gtmsource.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_sp.h" #include "repl_comm.h" #include "repl_log.h" #include "util.h" /* util_out_print in GTM_PUTMSG_CSA_ADDRINFO */ #define RESOLUTION_FAILURE_PREFIX "Failure in resolving " GBLDEF int gtmsource_sock_fd = FD_INVALID; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF FILE *gtmsource_log_fp; error_def(ERR_REPLCOMM); error_def(ERR_GETADDRINFO); error_def(ERR_TEXT); int gtmsource_comm_init(boolean_t print_addresolve_error) { /* Initialize communication stuff */ struct linger disable_linger = {0, 0}; char error_string[1024]; int err_status; struct addrinfo *ai_ptr = NULL, *ai_head = NULL, hints; gtmsource_local_ptr_t gtmsource_local; char *host; char port_buffer[NI_MAXSERV], hostinfo[SIZEOF(RESOLUTION_FAILURE_PREFIX) + MAX_HOST_NAME_LEN + NI_MAXSERV]; int port_len; int errcode; if (FD_INVALID != gtmsource_sock_fd) /* Initialization done already */ return(0); gtmsource_local = jnlpool->gtmsource_local; port_len = 0; I2A(port_buffer, port_len, gtmsource_local->secondary_port); port_buffer[port_len] = '\0'; host = gtmsource_local->secondary_host; CLIENT_HINTS(hints); errcode = getaddrinfo(host, port_buffer, &hints, &ai_head); if ((0 != errcode) && print_addresolve_error) { SNPRINTF(hostinfo, SIZEOF(hostinfo), "%s%s:%s", RESOLUTION_FAILURE_PREFIX, host, port_buffer); GTM_PUTMSG_CSA_ADDRINFO(NULL, ERR_GETADDRINFO, errcode, hostinfo); } if (ai_head) { for(ai_ptr = ai_head; NULL != ai_ptr; ai_ptr = ai_ptr->ai_next) { if (FD_INVALID == (gtmsource_sock_fd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol))) err_status = errno; else { err_status = 0; break; } } if (0 != err_status) { freeaddrinfo(ai_head); /* prevent mem-leak */ SNPRINTF(error_string, SIZEOF(error_string), "Error with source server socket create : %s", STRERROR(err_status)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(error_string)); } assert(NULL != ai_ptr); assert(SIZEOF(gtmsource_local->secondary_inet_addr) >= ai_ptr->ai_addrlen); /* only save the addrinfo and address after the socket is successfuly created */ gtmsource_local->secondary_af = ai_ptr->ai_family; gtmsource_local->secondary_addrlen = ai_ptr->ai_addrlen; memcpy((struct sockaddr*)(>msource_local->secondary_inet_addr), ai_ptr->ai_addr, ai_ptr->ai_addrlen); freeaddrinfo(ai_head); /* prevent mem-leak */ /* A connection breakage should get rid of the socket */ if (-1 == setsockopt(gtmsource_sock_fd, SOL_SOCKET, SO_LINGER, (const void *)&disable_linger, SIZEOF(disable_linger))) { err_status = ERRNO; SNPRINTF(error_string, SIZEOF(error_string), "Error with source server socket disable linger : %s", STRERROR(err_status)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(error_string)); } } return(errcode); } fis-gtm-V7.0-005/sr_port/gtmsource_ctl_init.c0000644000032200000250000004212414342376331020107 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #ifdef UNIX #include "gtm_stat.h" #elif defined(VMS) #include #include #include #include #include #include /* Required for gtmsource.h */ #include #else #error Unsupported platform #endif #include #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "gtm_inet.h" #include "gtm_stdio.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_ctl.h" #include "repl_errno.h" #include "repl_dbg.h" #ifdef UNIX #include "gtmio.h" #endif #include "iosp.h" #include "eintr_wrappers.h" #include "repl_sp.h" #include "tp_change_reg.h" #include "is_file_identical.h" #include "get_fs_block_size.h" #include "gtmcrypt.h" #ifdef __MVS__ #include "gtm_zos_io.h" #endif GBLDEF repl_ctl_element *repl_ctl_list = NULL; GBLDEF repl_rctl_elem_t *repl_rctl_list = NULL; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF seq_num seq_num_zero; GBLREF gd_addr *gd_header; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF int gtmsource_log_fd; GBLREF FILE *gtmsource_log_fp; GBLREF int gtmsource_statslog_fd; GBLREF FILE *gtmsource_statslog_fp; #ifdef UNIX GBLREF gtmsource_state_t gtmsource_state; GBLREF uint4 process_id; #endif error_def(ERR_JNLFILRDOPN); error_def(ERR_JNLNOREPL); repl_buff_t *repl_buff_create(uint4 buffsize, uint4 jnl_fs_block_size); repl_buff_t *repl_buff_create(uint4 buffsize, uint4 jnl_fs_block_size) { repl_buff_t *tmp_rb; int index; unsigned char *buff_ptr; tmp_rb = (repl_buff_t *)malloc(SIZEOF(repl_buff_t)); tmp_rb->buffindex = REPL_MAINBUFF; for (index = REPL_MAINBUFF; REPL_NUMBUFF > index; index++) { tmp_rb->buff[index].reclen = 0; tmp_rb->buff[index].recaddr = JNL_FILE_FIRST_RECORD; tmp_rb->buff[index].readaddr = JNL_FILE_FIRST_RECORD; tmp_rb->buff[index].buffremaining = buffsize; buff_ptr = (unsigned char *)malloc(buffsize + jnl_fs_block_size); tmp_rb->buff[index].base_buff = buff_ptr; tmp_rb->buff[index].base = (unsigned char *)ROUND_UP2((uintszofptr_t)buff_ptr, jnl_fs_block_size); tmp_rb->buff[index].recbuff = tmp_rb->buff[index].base; } tmp_rb->fc = (repl_file_control_t *)malloc(SIZEOF(repl_file_control_t)); return (tmp_rb); } /* Given a journal file name, this function opens that file explicitly without going through "jnl_ensure_open" */ int repl_open_jnl_file_by_name(repl_ctl_element *tmp_ctl, int jnl_fn_len, char *jnl_fn, int *fd_ptr, void *stat_buf_ptr) { int tmp_fd; int status; #ifdef UNIX struct stat stat_buf; #elif defined(VMS) struct FAB fab; struct NAM stat_buf; #else #error Unsupported platform #endif tmp_ctl->jnl_fn_len = jnl_fn_len; memcpy(tmp_ctl->jnl_fn, jnl_fn, jnl_fn_len); tmp_ctl->jnl_fn[jnl_fn_len] = '\0'; status = SS_NORMAL; /* Open Journal File */ # ifdef UNIX OPENFILE_CLOEXEC(tmp_ctl->jnl_fn, O_RDONLY, tmp_fd); if (0 > tmp_fd) { status = errno; } if (SS_NORMAL == status) { FSTAT_FILE(tmp_fd, &stat_buf, status); if (0 > status) { status = errno; assert(FALSE); } # ifdef __MVS else if (-1 == gtm_zos_tag_to_policy(tmp_fd, TAG_BINARY)) { status = errno; assert(FALSE); } # endif } *((struct stat *)stat_buf_ptr) = stat_buf; # elif defined(VMS) fab = cc$rms_fab; fab.fab$l_fna = tmp_ctl->jnl_fn; fab.fab$b_fns = tmp_ctl->jnl_fn_len; fab.fab$l_fop = FAB$M_UFO; fab.fab$b_fac = FAB$M_GET | FAB$M_PUT | FAB$M_BIO; fab.fab$b_shr = FAB$M_SHRPUT | FAB$M_SHRGET | FAB$M_UPI; stat_buf = cc$rms_nam; fab.fab$l_nam = &stat_buf; fab.fab$l_dna = JNL_EXT_DEF; fab.fab$b_dns = SIZEOF(JNL_EXT_DEF) - 1; status = sys$open(&fab); if (RMS$_NORMAL == status) { status = SS_NORMAL; tmp_fd = fab.fab$l_stv; } assert(SS_NORMAL == status); *((struct NAM *)stat_buf_ptr) = stat_buf; # endif REPL_DPRINT2("CTL INIT : Direct open of file %s\n", tmp_ctl->jnl_fn); *fd_ptr = tmp_fd; return status; } int repl_ctl_create(repl_ctl_element **ctl, gd_region *reg, int jnl_fn_len, char *jnl_fn, boolean_t init) { gd_region *r_save; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; repl_ctl_element *tmp_ctl = NULL; jnl_file_header *tmp_jfh = NULL; jnl_file_header *tmp_jfh_base = NULL; jnl_private_control *jpc; int tmp_fd = NOJNL; int status; int gtmcrypt_errno; uint4 jnl_status; boolean_t did_jnl_ensure_open = FALSE, was_crit; int4 lcl_jnl_fn_len; char lcl_jnl_fn[JNL_NAME_SIZE]; #ifdef UNIX struct stat stat_buf; #elif defined(VMS) struct NAM stat_buf; short iosb[4]; /* needed by the F_READ_BLK_ALIGNED macro */ #else #error Unsupported platform #endif uint4 jnl_fs_block_size; status = SS_NORMAL; jnl_status = 0; tmp_ctl = (repl_ctl_element *)malloc(SIZEOF(repl_ctl_element)); tmp_ctl->reg = reg; csa = &FILE_INFO(reg)->s_addrs; jpc = csa->jnl; if (init) { assert((0 == jnl_fn_len) && ((NULL == jnl_fn) || ('\0' == jnl_fn[0]))); r_save = gv_cur_region; gv_cur_region = reg; tp_change_reg(); assert(csa == cs_addrs); jpc->channel = NOJNL; /* Not to close the prev gener file */ was_crit = csa->now_crit; if (!was_crit) grab_crit(reg, NOT_APPLICABLE); # ifdef UNIX if (csa->onln_rlbk_cycle != csa->nl->onln_rlbk_cycle) { /* Concurrent online rollback. Possible only if we are called from gtmsource_update_zqgblmod_seqno_and_tn * in which case we don't hold the gtmsource_srv_latch. Assert that. */ assert(process_id != jnlpool->gtmsource_local->gtmsource_srv_latch.u.parts.latch_pid); SYNC_ONLN_RLBK_CYCLES; gtmsource_onln_rlbk_clnup(); if (!was_crit) rel_crit(reg); return -1; } # endif /* Although replication may be WAS_ON, it is possible that source server has not yet sent records * that were generated when replication was ON. We have to open and read this journal file to * cover such a case. But in the WAS_ON case, do not ask for a jnl_ensure_open to be done since * it will return an error (it will try to open the latest generation journal file and that will * fail because of a lot of reasons e.g. "jpc->cycle" vs "jpc->jnl_buff->cycle" mismatch or db/jnl * tn mismatch JNLTRANSLSS error etc.). This will even cause a journal file switch which the source * server should never do (it is only supposed to READ from journal files). Open the journal file * stored in the database file header (which will be non-NULL even though journaling is currently OFF) * thereby can send as many seqnos as possible until the repl=WAS_ON/jnl=OFF state was reached. */ csd = csa->hdr; assert(REPL_ALLOWED(csd)); if (!REPL_WAS_ENABLED(csd)) { /* replication is allowed and has not gone into the WAS_ON state so journaling is expected to be ON*/ assert(JNL_ENABLED(csd)); did_jnl_ensure_open = TRUE; jnl_status = jnl_ensure_open(reg, csa); if (0 != jnl_status) { if (!was_crit) rel_crit(reg); /* jnl_status may be ERR_JNLSWITCHRETRY, which has a severity of INFO, but we want to * treat it (or any other non-zero status) as an ERROR, so force it. */ if (SS_NORMAL != jpc->status) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(7) MAKE_MSG_TYPE(jnl_status, ERROR), 4, JNL_LEN_STR(csd), DB_LEN_STR(reg), jpc->status); else RTS_ERROR_CSA_ABT(csa, VARLSTCNT(6) MAKE_MSG_TYPE(jnl_status, ERROR), 4, JNL_LEN_STR(csd), DB_LEN_STR(reg)); } else { tmp_ctl->jnl_fn_len = csd->jnl_file_len; memcpy(tmp_ctl->jnl_fn, csd->jnl_file_name, tmp_ctl->jnl_fn_len); tmp_ctl->jnl_fn[tmp_ctl->jnl_fn_len] = '\0'; } /* stash the shared fileid into private storage before rel_crit as it is used in JNL_GDID_PVT macro below */ VMS_ONLY (jpc->fileid = csa->nl->jnl_file.jnl_file_id;) UNIX_ONLY(jpc->fileid = csa->nl->jnl_file.u;) REPL_DPRINT2("CTL INIT : Open of file %s thru jnl_ensure_open\n", tmp_ctl->jnl_fn); tmp_fd = jpc->channel; } else { /* Note that we hold crit so it is safe to pass csd->jnl_file_name (no one else will be changing it) */ status = repl_open_jnl_file_by_name(tmp_ctl, csd->jnl_file_len, (char *)csd->jnl_file_name, &tmp_fd, &stat_buf); } if (!was_crit) rel_crit(reg); gv_cur_region = r_save; tp_change_reg(); assert((NOJNL != tmp_fd) || ((status != SS_NORMAL) && gtm_white_box_test_case_enabled && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number))); } else status = repl_open_jnl_file_by_name(tmp_ctl, jnl_fn_len, jnl_fn, &tmp_fd, &stat_buf); if (status == SS_NORMAL) { jnl_fs_block_size = get_fs_block_size(tmp_fd); /* Because the read below will be to the aligned buffer, and it will read an aligned size, we need * to allocate jnl_file_header + 2 * jnl_fs_block_size. There will be some throw-away before the * buffer to get the alignment in place, and then up to jnl_fs_block_size after the header in order * for the size to be a multiple of jnl_fs_block_size. */ tmp_jfh_base = (jnl_file_header *)malloc(SIZEOF(jnl_file_header) + (2 * jnl_fs_block_size)); tmp_jfh = (jnl_file_header *)(ROUND_UP2((uintszofptr_t)tmp_jfh_base, jnl_fs_block_size)); F_READ_BLK_ALIGNED(tmp_fd, 0, tmp_jfh, ROUND_UP2(REAL_JNL_HDR_LEN, jnl_fs_block_size), status); assert(SS_NORMAL == status); if (SS_NORMAL == status) CHECK_JNL_FILE_IS_USABLE(tmp_jfh, status, FALSE, 0, NULL); /* FALSE => NO gtm_putmsg even if errors */ assert(SS_NORMAL == status); } if ((SS_NORMAL != status) || (!REPL_ALLOWED(tmp_jfh))) { /* We need tmp_ctl->jnl_fn to issue the error but want to free up tmp_ctl before the error. * So copy jnl_fn into local buffer before the error. */ lcl_jnl_fn_len = tmp_ctl->jnl_fn_len; assert(ARRAYSIZE(lcl_jnl_fn) >= ARRAYSIZE(tmp_ctl->jnl_fn)); assert(lcl_jnl_fn_len < ARRAYSIZE(lcl_jnl_fn)); memcpy(lcl_jnl_fn, tmp_ctl->jnl_fn, lcl_jnl_fn_len); lcl_jnl_fn[lcl_jnl_fn_len] = '\0'; assert((NULL == tmp_jfh) || !REPL_ALLOWED(tmp_jfh)); free(tmp_ctl); tmp_ctl = NULL; if (NULL != tmp_jfh_base) free(tmp_jfh_base); tmp_jfh = NULL; tmp_jfh_base = NULL; if (SS_NORMAL != status) RTS_ERROR_CSA_ABT(csa, VARLSTCNT(7) ERR_JNLFILRDOPN, 4, lcl_jnl_fn_len, lcl_jnl_fn, DB_LEN_STR(reg), status); else RTS_ERROR_CSA_ABT(csa, VARLSTCNT(6) ERR_JNLNOREPL, 4, lcl_jnl_fn_len, lcl_jnl_fn, DB_LEN_STR(reg)); } assert(SS_NORMAL == status); /* so jnl_fs_block_size is guaranteed to have been initialized */ tmp_ctl->repl_buff = repl_buff_create(tmp_jfh->alignsize, jnl_fs_block_size); tmp_ctl->repl_buff->backctl = tmp_ctl; tmp_ctl->repl_buff->fc->eof_addr = JNL_FILE_FIRST_RECORD; tmp_ctl->repl_buff->fc->fs_block_size = jnl_fs_block_size; tmp_ctl->repl_buff->fc->jfh_base = tmp_jfh_base; tmp_ctl->repl_buff->fc->jfh = tmp_jfh; tmp_ctl->repl_buff->fc->fd = tmp_fd; if (USES_ANY_KEY(tmp_jfh)) { ASSERT_ENCRYPTION_INITIALIZED; /* should be done in db_init (gtmsource() -> gvcst_init() -> db_init()) */ INIT_DB_OR_JNL_ENCRYPTION(tmp_ctl, tmp_jfh, reg->dyn.addr->fname_len, (char *)reg->dyn.addr->fname, gtmcrypt_errno); if (0 != gtmcrypt_errno) GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, rts_error, tmp_ctl->jnl_fn_len, tmp_ctl->jnl_fn); } if (did_jnl_ensure_open) { F_COPY_GDID(tmp_ctl->repl_buff->fc->id, JNL_GDID_PVT(csa)); /* reset jpc->channel (would have been updated by jnl_ensure_open) as that corresponds to an * actively updated journal file and is only for GT.M and never for source server which only * READS from journal files for the most part (it can do a jnl_flush rarely (in case GT.M failed * to do one) using GTMSRC_DO_JNL_FLUSH_IF_POSSIBLE macro). Source server anyways has a copy of * the fd in tmp_ctl->repl_buff->fc->fd. */ jpc->channel = NOJNL; } else { F_COPY_GDID_FROM_STAT(tmp_ctl->repl_buff->fc->id, stat_buf); /* For VMS stat_buf is a NAM structure */ } QWASSIGN(tmp_ctl->min_seqno, seq_num_zero); QWASSIGN(tmp_ctl->max_seqno, seq_num_zero); QWASSIGN(tmp_ctl->seqno, seq_num_zero); tmp_ctl->tn = 1; tmp_ctl->file_state = JNL_FILE_UNREAD; tmp_ctl->lookback = FALSE; tmp_ctl->first_read_done = FALSE; tmp_ctl->eof_addr_final = FALSE; tmp_ctl->max_seqno_final = FALSE; tmp_ctl->min_seqno_dskaddr = 0; tmp_ctl->max_seqno_dskaddr = 0; tmp_ctl->next = tmp_ctl->prev = NULL; *ctl = tmp_ctl; return (SS_NORMAL); } /* Setup ctl for reading from journal files */ int gtmsource_ctl_init(void) { gd_region *region_top, *reg; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; repl_ctl_element *tmp_ctl, *prev_ctl; repl_rctl_elem_t *repl_rctl, *last_rctl; # ifdef DEBUG repl_rctl_elem_t *tmp_rctl; # endif int jnl_file_len, status; repl_ctl_list = (repl_ctl_element *)malloc(SIZEOF(repl_ctl_element)); memset((char_ptr_t)repl_ctl_list, 0, SIZEOF(*repl_ctl_list)); prev_ctl = repl_ctl_list; UNIX_ONLY(assert(GTMSOURCE_HANDLE_ONLN_RLBK != gtmsource_state)); /* can't come here without handling online rollback */ region_top = gd_header->regions + gd_header->n_regions; last_rctl = NULL; for (reg = gd_header->regions; reg < region_top; reg++) { assert(reg->open); csa = &FILE_INFO(reg)->s_addrs; csd = csa->hdr; if (REPL_ALLOWED(csd)) { status = repl_ctl_create(&tmp_ctl, reg, 0, NULL, TRUE); assert((SS_NORMAL == status) UNIX_ONLY(|| (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state))); UNIX_ONLY( if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) return -1; ) prev_ctl->next = tmp_ctl; tmp_ctl->prev = prev_ctl; tmp_ctl->next = NULL; prev_ctl = tmp_ctl; repl_rctl = (repl_rctl_elem_t *)csa->miscptr; if (NULL == repl_rctl) { # ifdef DEBUG tmp_rctl = repl_rctl_list; while ((NULL != tmp_rctl) && (NULL != tmp_rctl->next)) tmp_rctl = tmp_rctl->next; assert(last_rctl == tmp_rctl); # endif repl_rctl = (repl_rctl_elem_t *)malloc(SIZEOF(repl_rctl_elem_t)); repl_rctl->next = NULL; repl_rctl->prev = last_rctl; if (NULL == repl_rctl_list) repl_rctl_list = repl_rctl; else last_rctl->next = repl_rctl; last_rctl = repl_rctl; csa->miscptr = (void *)repl_rctl; } repl_rctl->ctl_start = tmp_ctl; /* repl_rctl->read_complete is later initialized in function "read_and_merge" */ tmp_ctl->repl_rctl = repl_rctl; } } /* This function should never be invoked unless there is at least one replicated region. */ assertpro(NULL != repl_ctl_list->next); return (SS_NORMAL); } int repl_ctl_close(repl_ctl_element *ctl) { int index; int status; if (NULL != ctl) { REPL_DPRINT2("CTL CLOSE : Close of file %s\n", ctl->jnl_fn); if (NULL != ctl->repl_buff) { for (index = REPL_MAINBUFF; REPL_NUMBUFF > index; index++) if (NULL != ctl->repl_buff->buff[index].base_buff) free(ctl->repl_buff->buff[index].base_buff); if (NULL != ctl->repl_buff->fc) { if (NULL != ctl->repl_buff->fc->jfh_base) free(ctl->repl_buff->fc->jfh_base); if (NOJNL != ctl->repl_buff->fc->fd) F_CLOSE(ctl->repl_buff->fc->fd, status); /* resets "ctl->repl_buff->fc->fd" to FD_INVALID */ free(ctl->repl_buff->fc); } free(ctl->repl_buff); } free(ctl); } return (SS_NORMAL); } int gtmsource_ctl_close(void) { repl_ctl_element *ctl; sgmnt_addrs *csa; int status; repl_rctl_elem_t *repl_rctl; UNIX_ONLY(gtmsource_stop_jnl_release_timer();) if (repl_ctl_list) { for (ctl = repl_ctl_list->next; NULL != ctl; ctl = repl_ctl_list->next) { repl_ctl_list->next = ctl->next; /* next element becomes head thereby removing this element, the current head from the list; if there is an error path that returns us to this function before all elements were freed, we won't try to free elements that have been freed already */ # ifdef DEBUG csa = &FILE_INFO(ctl->reg)->s_addrs; /* jpc->channel should never be set to a valid value outside of repl_ctl_create. The only exception is if * we were interrupted in the middle of repl_ctl_create by an external signal in which case the process * better be exiting. Assert that. */ assert(((NULL != csa->jnl) && (NOJNL == csa->jnl->channel)) || process_exiting); # endif repl_ctl_close(ctl); } ctl = repl_ctl_list; repl_ctl_list = NULL; free(ctl); } for (repl_rctl = repl_rctl_list; NULL != repl_rctl; repl_rctl = repl_rctl->next) repl_rctl->ctl_start = NULL; return (SS_NORMAL); } int gtmsource_set_lookback(void) { /* Scan all the region ctl's and set lookback to TRUE if ctl has to be repositioned for a transaction read from the past. * In all other cases, reset it to FALSE. */ repl_ctl_element *ctl; for (ctl = repl_ctl_list->next; NULL != ctl; ctl = ctl->next) { if (((JNL_FILE_OPEN == ctl->file_state) || (JNL_FILE_CLOSED == ctl->file_state)) && QWLE(jnlpool->gtmsource_local->read_jnl_seqno, ctl->seqno)) ctl->lookback = TRUE; else ctl->lookback = FALSE; } return (SS_NORMAL); } fis-gtm-V7.0-005/sr_port/gtmsource_exit.c0000755000032200000250000000237114342376331017256 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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_stdio.h" /* for FILE * in repl_comm.h */ #include "gtm_stdlib.h" /* for EXIT() */ #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_comm.h" GBLREF boolean_t is_src_server; GBLREF gtmsource_options_t gtmsource_options; #ifdef VMS error_def(ERR_REPLEXITERR); #endif error_def(ERR_REPLSRCEXITERR); void gtmsource_exit(int exit_status) { if ((0 != exit_status) && is_src_server) send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_REPLSRCEXITERR, 2, gtmsource_options.secondary_instname, gtmsource_options.log_file); EXIT(exit_status); } fis-gtm-V7.0-005/sr_port/gtmsource_heartbeat.h0000755000032200000250000000240614342376331020250 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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. * * * ****************************************************************/ #ifndef GTMSOURCE_HEARTBEAT_H #define GTMSOURCE_HEARTBEAT_H #define gtmsource_is_heartbeat_stalled (heartbeat_stalled) #ifndef REPL_DISABLE_HEARTBEAT #define GTMSOURCE_IS_HEARTBEAT_DUE(NOW) \ (0 != last_sent_time \ && difftime(*(NOW), last_sent_time) >= (double)jnlpool->gtmsource_local->connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD]) #else #define GTMSOURCE_IS_HEARTBEAT_DUE(NOW) FALSE #endif GBLREF boolean_t heartbeat_stalled; GBLREF repl_heartbeat_que_entry_t *repl_heartbeat_que_head; GBLREF repl_heartbeat_que_entry_t *repl_heartbeat_free_head; GBLREF time_t last_sent_time; GBLREF time_t earliest_sent_time; void gtmsource_heartbeat_timer(TID tid, int4 interval_len, int *interval_ptr); #endif /* GTMSOURCE_HEARTBEAT_H */ fis-gtm-V7.0-005/sr_port/gtmsource_poll_actions.c0000755000032200000250000001650314342376331020775 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_stdio.h" #include "gtm_time.h" #include "gtm_string.h" #include "gtm_unistd.h" #include "gtm_inet.h" #ifdef VMS #include /* Required for gtmsource.h */ #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" #include "repl_dbg.h" #include "repl_log.h" #include "iosp.h" #include "repl_shutdcode.h" #include "gt_timer.h" #include "gtmsource_heartbeat.h" #include "jnl.h" #include "repl_filter.h" #include "util.h" #include "repl_comm.h" #include "eintr_wrappers.h" #include "gtmio.h" #include "sgtm_putmsg.h" #include "copy.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF int gtmsource_sock_fd; GBLREF gtmsource_state_t gtmsource_state; GBLREF boolean_t gtmsource_logstats; GBLREF int gtmsource_log_fd; GBLREF FILE *gtmsource_log_fp; GBLREF int gtmsource_filter; GBLREF time_t gtmsource_last_flush_time; GBLREF volatile time_t gtmsource_now; GBLREF gtmsource_options_t gtmsource_options; GBLREF uint4 log_interval; #ifdef UNIX GBLREF boolean_t last_seen_freeze_flag; #endif error_def(ERR_REPLWARN); #ifdef UNIX error_def(ERR_REPLINSTFREEZECOMMENT); error_def(ERR_REPLINSTFROZEN); error_def(ERR_REPLINSTUNFROZEN); #endif #define OUT_LINE 1024 + 1 int gtmsource_poll_actions(boolean_t poll_secondary) { /* This function should be called only in active mode, but cannot assert for it */ gtmsource_local_ptr_t gtmsource_local; time_t now; repl_heartbeat_msg_t overdue_heartbeat; char *time_ptr; char time_str[CTIME_BEFORE_NL + 1]; char print_msg[REPL_MSG_SIZE], msg_str[OUT_LINE]; boolean_t log_switched = FALSE; int status; time_t temp_time; gtm_time4_t time4; gtmsource_local = jnlpool->gtmsource_local; if (SHUTDOWN == gtmsource_local->shutdown) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Shutdown signalled\n"); gtmsource_end(); /* Won't return */ } if (jnlpool->jnlpool_ctl->freeze != last_seen_freeze_flag) { last_seen_freeze_flag = jnlpool->jnlpool_ctl->freeze; if (last_seen_freeze_flag) { sgtm_putmsg(print_msg, REPL_MSG_SIZE, VARLSTCNT(3) ERR_REPLINSTFROZEN, 1, jnlpool->repl_inst_filehdr->inst_info.this_instname); repl_log(gtmsource_log_fp, TRUE, FALSE, print_msg); sgtm_putmsg(print_msg, REPL_MSG_SIZE, VARLSTCNT(3) ERR_REPLINSTFREEZECOMMENT, 1, jnlpool->jnlpool_ctl->freeze_comment); repl_log(gtmsource_log_fp, TRUE, TRUE, print_msg); } else { sgtm_putmsg(print_msg, REPL_MSG_SIZE, VARLSTCNT(3) ERR_REPLINSTUNFROZEN, 1, jnlpool->repl_inst_filehdr->inst_info.this_instname); repl_log(gtmsource_log_fp, TRUE, TRUE, print_msg); } } if (GTMSOURCE_START == gtmsource_state) return (SS_NORMAL); if (GTMSOURCE_CHANGING_MODE != gtmsource_state && GTMSOURCE_MODE_PASSIVE_REQUESTED == gtmsource_local->mode) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Changing mode from ACTIVE to PASSIVE\n"); gtmsource_state = GTMSOURCE_CHANGING_MODE; gtmsource_local->mode = GTMSOURCE_MODE_PASSIVE; UNIX_ONLY(gtmsource_local->gtmsource_state = gtmsource_state;) gtmsource_flush_fh(gtmsource_local->read_jnl_seqno); /* Force the update on a transition */ return (SS_NORMAL); } if (poll_secondary && GTMSOURCE_CHANGING_MODE != gtmsource_state && GTMSOURCE_WAITING_FOR_CONNECTION != gtmsource_state) { now = gtmsource_now; if (gtmsource_is_heartbeat_overdue(&now, &overdue_heartbeat)) { /* Few platforms don't allow unaligned memory access. Passing ack_time to GTM_CTIME(ctime) may * cause sig. time4 and temp_time are used as temporary variable for converting time to string.*/ GET_LONG(time4, &overdue_heartbeat.ack_time[0]); temp_time = time4; GTM_CTIME(time_ptr, &temp_time); memcpy(time_str, time_ptr, CTIME_BEFORE_NL); time_str[CTIME_BEFORE_NL] = '\0'; SNPRINTF(msg_str, OUT_LINE, "No response received for heartbeat sent at %s with SEQNO " GTM64_ONLY("%lu") NON_GTM64_ONLY("%llu") " in %0.f seconds. Closing connection\n", time_str, *(seq_num *)&overdue_heartbeat.ack_seqno[0], difftime(now, temp_time)); sgtm_putmsg(print_msg, REPL_MSG_SIZE, VARLSTCNT(4) ERR_REPLWARN, 2, LEN_AND_STR(msg_str)); repl_log(gtmsource_log_fp, TRUE, TRUE, print_msg); repl_close(>msource_sock_fd); SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; UNIX_ONLY(gtmsource_local->gtmsource_state = gtmsource_state;) return (SS_NORMAL); } if (GTMSOURCE_IS_HEARTBEAT_DUE(&now) && !heartbeat_stalled) { gtmsource_send_heartbeat(&now); if ((GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state) || (GTMSOURCE_CHANGING_MODE == gtmsource_state) || (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state)) return (SS_NORMAL); } } if ((GTMSOURCE_SENDING_JNLRECS == gtmsource_state) /* Flush the file header only with an active connection */ && (GTMSOURCE_FH_FLUSH_INTERVAL <= difftime(gtmsource_now, gtmsource_last_flush_time))) { gtmsource_flush_fh(gtmsource_local->read_jnl_seqno); if (GTMSOURCE_HANDLE_ONLN_RLBK == gtmsource_state) return (SS_NORMAL); } if (0 != gtmsource_local->changelog) { if (gtmsource_local->changelog & REPLIC_CHANGE_LOGINTERVAL) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Changing log interval from %u to %u\n", log_interval, gtmsource_local->log_interval); log_interval = gtmsource_local->log_interval; gtmsource_reinit_logseqno(); /* will force a LOG on the first send following the interval change */ } if (gtmsource_local->changelog & REPLIC_CHANGE_LOGFILE) { log_switched = TRUE; repl_log(gtmsource_log_fp, TRUE, TRUE, "Changing log file to %s\n", gtmsource_local->log_file); #ifdef UNIX repl_log_init(REPL_GENERAL_LOG, >msource_log_fd, gtmsource_local->log_file); repl_log_fd2fp(>msource_log_fp, gtmsource_log_fd); #elif defined(VMS) util_log_open(STR_AND_LEN(gtmsource_local->log_file)); #else #error unsupported platform #endif STRCPY(gtmsource_options.log_file, jnlpool->gtmsource_local->log_file); } if ( log_switched == TRUE ) repl_log(gtmsource_log_fp, TRUE, TRUE, "Change log to %s successful\n", gtmsource_local->log_file); gtmsource_local->changelog = 0; } if (!gtmsource_logstats && gtmsource_local->statslog) { #ifdef UNIX gtmsource_logstats = TRUE; repl_log(gtmsource_log_fp, TRUE, TRUE, "Begin statistics logging\n"); #else repl_log(gtmsource_log_fp, TRUE, TRUE, "Stats logging not supported on VMS\n"); #endif } else if (gtmsource_logstats && !gtmsource_local->statslog) { gtmsource_logstats = FALSE; repl_log(gtmsource_log_fp, TRUE, TRUE, "End statistics logging\n"); } if ((gtmsource_filter & EXTERNAL_FILTER) && ('\0' == gtmsource_local->filter_cmd[0])) { repl_log(gtmsource_log_fp, TRUE, TRUE, "Stopping filter\n"); repl_stop_filter(); gtmsource_filter &= ~EXTERNAL_FILTER; } return (SS_NORMAL); } fis-gtm-V7.0-005/sr_port/gtmsource_reinit_logseqno.c0000755000032200000250000000216314342376331021505 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2017 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_inet.h" #ifdef VMS #include #endif #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #include "gtmsource.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF seq_num lastlog_seqno; GBLREF uint4 log_interval; GBLREF qw_num trans_sent_cnt, last_log_tr_sent_cnt; void gtmsource_reinit_logseqno(void) { lastlog_seqno = jnlpool->gtmsource_local->read_jnl_seqno - log_interval; trans_sent_cnt = -(qw_num)(log_interval - 1); last_log_tr_sent_cnt = 0; } fis-gtm-V7.0-005/sr_port/gv_bind_name.c0000644000032200000250000001315114342376331016620 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_stdio.h" #include "gtm_string.h" #include "gtmio.h" #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "collseq.h" #include "gdsfhead.h" #include "gdscc.h" #include "copy.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "hashtab_mname.h" #include "tp.h" #include "change_reg.h" #include "targ_alloc.h" #include "gvcst_protos.h" /* for gvcst_root_search prototype */ #include "min_max.h" #include "gtmimagename.h" #include "gvnh_spanreg.h" #include "gv_trigger_common.h" /* for *HASHT* macros used inside GVNH_REG_INIT macro */ #include "io.h" GBLREF gv_namehead *gv_target; GBLREF gv_key *gv_currkey; GBLREF gd_region *gv_cur_region; error_def(ERR_KEY2BIG); error_def(ERR_GVIS); /* Map an unsubscripted global name to its corresponding region in the gld file */ gvnh_reg_t *gv_bind_name(gd_addr *addr, mname_entry *gvname) { gd_binding *map; ht_ent_mname *tabent, *tabent1; gd_region *reg; gvnh_reg_t *gvnh_reg; int keylen, count; char format_key[MAX_MIDENT_LEN + 1]; /* max key length + 1 byte for '^' */ gv_namehead *tmp_gvt; sgmnt_addrs *csa; hash_table_mname *tab_ptr; assert(MAX_MIDENT_LEN >= gvname->var_name.len); tab_ptr = addr->tab_ptr; if (NULL == (tabent = lookup_hashtab_mname((hash_table_mname *)tab_ptr, gvname))) { count = tab_ptr->count; /* Note down current # of valid entries in hash table */ map = gv_srch_map(addr, gvname->var_name.addr, gvname->var_name.len, SKIP_BASEDB_OPEN_FALSE); reg = map->reg.addr; if (!reg->open) gv_init_reg(reg, addr); if (IS_STATSDB_REG(reg)) { /* In case of a statsDB, it is possible that "gv_srch_map" or "gv_init_reg" calls above end up doing * a "op_gvname/gv_bind_name" if they in turn invoke "gvcst_init_statsDB". In that case, the hash table * could have been updated since we did the "lookup_hashtab_mname" call above. So redo the lookup. */ tabent = lookup_hashtab_mname((hash_table_mname *)tab_ptr, gvname); } else { /* If not a statsDB, then the above calls to "gv_srch_map" or "gv_init_reg" should not have changed * the hashtable status of "gvname". There is an exception in that if gvname is "%YGS" (STATSDB_GBLNAME), * then it is possible that the open of the statsDB failed (e.g. gtm_statsdir env var too long etc.) in * which case the gvname would have been dynamically remapped to the baseDB. Assert that. * Since we want to avoid a memcmp against STATSDB_GBLNAME, we check if the hashtable count has changed * since we noted it down at function entry and if so redo the lookup hashtab in pro. Since only additions * happen in this particular hashtable, it is enough to check for "count < tab_ptr->count". */ # ifdef DEBUG tabent1 = lookup_hashtab_mname((hash_table_mname *)tab_ptr, gvname); assert((tabent1 == tabent) || ((gvname->var_name.len == STATSDB_GBLNAME_LEN) && (0 == memcmp(gvname->var_name.addr, STATSDB_GBLNAME, STATSDB_GBLNAME_LEN)) && (count < tab_ptr->count))); # endif if (count < tab_ptr->count) tabent = lookup_hashtab_mname((hash_table_mname *)tab_ptr, gvname); } } if (NULL == tabent) { tmp_gvt = targ_alloc(reg->max_key_size, gvname, reg); GVNH_REG_INIT(addr, tab_ptr, map, tmp_gvt, reg, gvnh_reg, tabent); } else { gvnh_reg = (gvnh_reg_t *)tabent->value; assert(NULL != gvnh_reg); reg = gvnh_reg->gd_reg; if (!reg->open) { gv_init_reg(reg, addr); /* could modify gvnh_reg->gvt if multiple regions map to same db file */ assert((0 == gvnh_reg->gvt->clue.end) || IS_STATSDB_REG(reg)); /* A statsDB open writes to itself */ } tmp_gvt = gvnh_reg->gvt; } if (((keylen = gvname->var_name.len) + 2) > reg->max_key_size) /* caution: embedded assignment of "keylen" */ { assert(ARRAYSIZE(format_key) >= (1 + gvname->var_name.len)); format_key[0] = '^'; memcpy(&format_key[1], gvname->var_name.addr, gvname->var_name.len); csa = &FILE_INFO(reg)->s_addrs; gv_currkey->end = 0; RTS_ERROR_CSA_ABT(csa, VARLSTCNT(10) ERR_KEY2BIG, 4, keylen + 2, (int4)reg->max_key_size, REG_LEN_STR(reg), ERR_GVIS, 2, 1 + gvname->var_name.len, format_key); } gv_target = tmp_gvt; /* now that any rts_error possibilities are all past us, it is safe to set gv_target. * Setting it before could casue gv_target and gv_currkey to get out of sync in case of * an error condition and fail asserts in mdb_condition_handler (for example). */ memcpy(gv_currkey->base, gvname->var_name.addr, keylen); gv_currkey->base[keylen] = KEY_DELIMITER; keylen++; gv_currkey->base[keylen] = KEY_DELIMITER; gv_currkey->end = keylen; gv_currkey->prev = 0; if (NULL == gvnh_reg->gvspan) { /* Global does not span multiple regions. In that case, open the only region that this global maps to right here. * In case of spanning globals, the subscripted reference will be used to find the mapping region (potentially * different from "reg" computed here. And that is the region to do a "change_reg" on. Will be done later * in "gv_bind_subsname". */ gv_cur_region = reg; change_reg(); } return gvnh_reg; } fis-gtm-V7.0-005/sr_port/gv_bind_subsname.c0000644000032200000250000000300614342376331017513 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2020 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_stdio.h" #include "gtmio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "targ_alloc.h" #include "change_reg.h" #include "gvcst_protos.h" /* for gvcst_root_search prototype used in GV_BIND_SUBSREG macro */ #include "gtmimagename.h" #include "io.h" #include "gvt_inline.h" GBLREF gd_region *gv_cur_region; GBLREF gv_key *gv_currkey; /* Like gv_bind_name but this operates on a subscripted global reference. In addition, this does a gvcst_root_search too. */ void gv_bind_subsname(gd_addr *addr, gv_key *key, gvnh_reg_t *gvnh_reg) { gd_binding *map; gd_region *reg; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; map = gv_srch_map(addr, (char *)&key->base[0], key->end - 1, SKIP_BASEDB_OPEN_FALSE); TREF(gd_targ_map) = map; reg = map->reg.addr; GV_BIND_SUBSREG(addr, reg, gvnh_reg); /* sets gv_target/gv_cur_region/cs_addrs */ DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); } fis-gtm-V7.0-005/sr_port/gv_init_reg.c0000755000032200000250000000303114342376331016503 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "iosp.h" #include "gdsbt.h" #include "gdsfhead.h" #include "cryptdef.h" #include "filestruct.h" #include "targ_alloc.h" #include "gvusr.h" #include "gvcst_protos.h" /* for gvcst_init prototype */ GBLREF int4 lkid; GBLREF bool licensed ; GBLREF gv_key *gv_currkey; GBLREF gv_key *gv_altkey; GBLREF gd_region *gv_cur_region; void gv_init_reg (gd_region *reg, gd_addr *addr) { # ifdef NOLICENSE licensed = TRUE; # else CRYPT_CHKSYSTEM; # endif switch (reg->dyn.addr->acc_meth) { case dba_usr: gvusr_init (reg, &gv_cur_region, &gv_currkey, &gv_altkey); break; /* we may be left in dba_cm state for gt_cm, if we have rundown the db and again accessed the db without quitting out of gtm */ case dba_cm: case dba_mm: case dba_bg: if (!reg->open) gvcst_init(reg, addr); break; default: assertpro(reg->dyn.addr->acc_meth != reg->dyn.addr->acc_meth); } assert(reg->open); return; } fis-gtm-V7.0-005/sr_port/gv_match.c0000755000032200000250000000253714342376331016011 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2008 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "dpgbldir.h" #include "gv_match.h" /* Multiple regions from across multiple global directories might correspond to the same physical file. * This routine detects if a given input region's database file matches with the corresponding file of an already open region. * If yes it returns the FIRST matching region else it returns NULL. */ gd_region *gv_match(gd_region *reg) { gd_region *r_top, *gv_region; gd_addr *addr_ptr; for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) { for (gv_region = addr_ptr->regions, r_top = gv_region + addr_ptr->n_regions; gv_region < r_top; gv_region++) { if (!gv_region->open) continue; if (REG_EQUAL(FILE_INFO(reg), gv_region)) return gv_region; } } return NULL; } fis-gtm-V7.0-005/sr_port/gv_match.h0000755000032200000250000000107014342376331016005 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GV_MATCH_INCLUDED #define GV_MATCH_INCLUDED gd_region *gv_match(gd_region *reg); #endif /* GV_MATCH_INCLUDED */ fis-gtm-V7.0-005/sr_port/gv_rundown.c0000644000032200000250000001345614342376331016410 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_inet.h" /* Required for gtmsource.h */ #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "gdskill.h" #include "gdscc.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "ast.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtmrecv.h" #include "error.h" #include "io.h" #include "gtmsecshr.h" #include "mutex.h" #include "ftok_sems.h" #include "tp_change_reg.h" #include "gds_rundown.h" #include "dpgbldir.h" #include "gvcmy_rundown.h" #include "rc_cpt_ops.h" #include "gv_rundown.h" #include "targ_alloc.h" #ifdef DEBUG #include "anticipatory_freeze.h" #endif #include "aio_shim.h" GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF int pool_init; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnlpool_addrs_ptr_t jnlpool_head; GBLREF recvpool_addrs recvpool; #ifdef DEBUG GBLREF boolean_t is_jnlpool_creator; error_def(ERR_TEXT); #endif error_def(ERR_NOTALLDBRNDWN); void gv_rundown(void) { gd_region *r_top, *r_save, *r_local; gd_addr *addr_ptr; jnlpool_addrs_ptr_t local_jnlpool, save_jnlpool; sgm_info *si; int4 rundown_status = EXIT_NRM; /* if gds_rundown went smoothly */ unix_db_info *udi; sgmnt_addrs *csa; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; r_save = gv_cur_region; /* Save for possible core dump */ save_jnlpool = jnlpool; gvcmy_rundown(); ENABLE_AST if (pool_init) { for (local_jnlpool = jnlpool_head; local_jnlpool; local_jnlpool = local_jnlpool->next) if (local_jnlpool->pool_init) rel_lock(local_jnlpool->jnlpool_dummy_reg); } for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) { for (r_local = addr_ptr->regions, r_top = r_local + addr_ptr->n_regions; r_local < r_top; r_local++) { if (r_local->open && (dba_cm != r_local->dyn.addr->acc_meth)) { /* Rundown has already occurred for GT.CM client regions through gvcmy_rundown() above. * Hence the (dba_cm != ...) check in the if above. Note that for GT.CM client regions, * region->open is TRUE although cs_addrs is NULL. */ # ifdef DEBUG if (is_jnlpool_creator && INST_FREEZE_ON_NOSPC_ENABLED(REG2CSA(r_local), local_jnlpool) && TREF(gtm_test_fake_enospc)) { /* Clear ENOSPC faking now that we are running down */ csa = REG2CSA(r_local); assert((NULL == csa->jnlpool) || (jnlpool == csa->jnlpool)); if (csa->nl->fake_db_enospc || csa->nl->fake_jnl_enospc) { send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_TEXT, 2, DB_LEN_STR(r_local), ERR_TEXT, 2, LEN_AND_LIT("Resetting fake_db_enospc and fake_jnl_enospc")); csa->nl->fake_db_enospc = FALSE; csa->nl->fake_jnl_enospc = FALSE; } } # endif gv_cur_region = r_local; csa = REG2CSA(r_local); if (csa->jnlpool && (jnlpool != csa->jnlpool)) jnlpool = csa->jnlpool; tp_change_reg(); rundown_status |= gds_rundown(CLEANUP_UDI_TRUE); } r_local->open = r_local->was_open = FALSE; } } rc_close_section(); gv_cur_region = r_save; /* Restore value for dumps but this region is now closed and is otherwise defunct */ jnlpool = save_jnlpool; cs_addrs = NULL; gtmsecshr_sock_cleanup(CLIENT); # ifndef MUTEX_MSEM_WAKE mutex_sock_cleanup(); # endif for (jnlpool = jnlpool_head; jnlpool; jnlpool = jnlpool->next) jnlpool_detach(); /* Once upon a time this used ftok_sem_reg to release a possibly still held semaphore, but attempts to do that exploded if * the region was closed because the structures needed by FILE_INFO had been released; this could happen due to bypass. So * this approach was relegated to the mists of time on the theory that gds_rundown did the best it could with the semaphores * and, if bypass was involved, some required operational action will deal with them. We still check for possible remaining * replication journal pool semaphores and deal with those. * Note that we use FALSE for the decr_cnt parameter (2nd parameter) to "ftok_sem_release". This is to avoid incorrect * removal of the ftok semaphore in case the counter is down to 1 but there are processes which did not bump the counter * (due to the counter overflowing) that are still accessing the semaphore. Even though we don't decrement the counter, * the SEM_UNDO will take care of doing the actual decrement when this process terminates. The only consequence is * we don't remove the ftok semaphore when the last process to use it dies, requiring mupip rollback/recover/rundown to * clean it up. But that is considered okay because these are unclean exit conditions with that documented requirement. */ for (jnlpool = jnlpool_head; jnlpool; jnlpool = jnlpool->next) if (jnlpool->jnlpool_dummy_reg && jnlpool->pool_init) { udi = FILE_INFO(jnlpool->jnlpool_dummy_reg); if (udi->grabbed_ftok_sem) ftok_sem_release(jnlpool->jnlpool_dummy_reg, FALSE, TRUE); } if (NULL != recvpool.recvpool_dummy_reg) { udi = FILE_INFO(recvpool.recvpool_dummy_reg); if (udi->grabbed_ftok_sem) ftok_sem_release(recvpool.recvpool_dummy_reg, FALSE, TRUE); } if (EXIT_NRM != rundown_status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_NOTALLDBRNDWN); } fis-gtm-V7.0-005/sr_port/gv_rundown.h0000755000032200000250000000106014342376331016404 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GV_RUNDOWN_INCLUDED #define GV_RUNDOWN_INCLUDED void gv_rundown(void); #endif /* GV_RUNDOWN_INCLUDED */ fis-gtm-V7.0-005/sr_port/gv_select.c0000755000032200000250000003273314342376331016175 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_stdio.h" #include "gtm_string.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" /* needed for WCSFLU_* macros */ #include "muextr.h" #include "iosp.h" #include "cli.h" #include "util.h" #include "op.h" #include "gt_timer.h" #include "mupip_exit.h" #include "gv_select.h" #include "global_map.h" #include "gtmmsg.h" #include "wcs_flu.h" #include "min_max.h" #include "hashtab.h" /* needed for HT_VALUE_DUMMY */ #ifdef GTM64 #include "hashtab_int8.h" #endif /* GTM64 */ #include "error.h" #include "gdscc.h" #include "gdskill.h" #include "buddy_list.h" /* needed for tp.h */ #include "hashtab_int4.h" /* needed for tp.h */ #include "tp.h" #include "hashtab_mname.h" #include "gvnh_spanreg.h" #include "change_reg.h" #include "io.h" #include "gtmio.h" #ifdef EXTRACT_HASHT_GLOBAL # include "gv_trigger_common.h" /* for IS_GVKEY_HASHT_GBLNAME and HASHT_GBL_CHAR1 macros */ # ifdef GTM_TRIGGER # include "gv_trigger.h" # include "targ_alloc.h" # include "gvcst_protos.h" /* for gvcst_root_search prototype used in GVTR_SWITCH_REG_AND_HASHT_BIND_NAME macro */ # endif #endif #define MAX_GMAP_ENTRIES_PER_ITER 2 /* maximum increase (could even be negative) in gmap array size per call to global_map */ error_def(ERR_DBRDONLY); error_def(ERR_FREEZE); error_def(ERR_FREEZECTRL); error_def(ERR_MUNOACTION); error_def(ERR_MUNOFINISH); error_def(ERR_SELECTSYNTAX); GBLREF bool mu_ctrlc_occurred; GBLREF bool mu_ctrly_occurred; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_data_ptr_t cs_data; GBLREF sgmnt_addrs *cs_addrs; GBLREF tp_region *grlist; GBLREF gd_addr *gd_header; static readonly unsigned char percent_lit = '%'; static readonly unsigned char tilde_lit = '~'; STATICFNDCL void gv_select_reg(void *ext_hash, boolean_t freeze, int *reg_max_rec, int *reg_max_key, int *reg_max_blk, boolean_t restrict_reg, gvnh_reg_t *gvnh_reg, glist **gl_tail); void gv_select(char *cli_buff, int n_len, boolean_t freeze, char opname[], glist *gl_head, int *reg_max_rec, int *reg_max_key, int *reg_max_blk, boolean_t restrict_reg) { int num_quote, len, gmap_size, new_gmap_size, estimated_entries, count, rslt, hash_code; int i, mini, maxi; char *ptr, *ptr1, *c; mname_entry gvname; mstr gmap[512], *gmap_ptr, *gmap_ptr_base, gmap_beg, gmap_end; mval curr_gbl_name; gd_region *reg; gv_namehead *gvt; gvnh_reg_t *gvnh_reg; gvnh_spanreg_t *gvspan; ht_ent_mname *tabent_mname; glist *gl_tail; # ifdef GTM64 hash_table_int8 ext_hash; # else hash_table_int4 ext_hash; # endif # ifdef EXTRACT_HASHT_GLOBAL gvnh_reg_t *hashgbl_gvnh_reg = NULL; gd_region *r_top; sgmnt_addrs *csa; # endif memset(gmap, 0, SIZEOF(gmap)); gmap_size = SIZEOF(gmap) / SIZEOF(gmap[0]); gmap_ptr_base = &gmap[0]; /* "estimated_entries" is a conservative estimate of the # of entries that could be used up in the gmap array */ estimated_entries = 1; /* take into account the NULL gmap entry at the end of the array */ for (ptr = cli_buff; *ptr; ptr = ptr1) { for (ptr1 = ptr; ; ptr1++) { if (',' == *ptr1) { len = (int)(ptr1 - ptr); ptr1++; break; } else if (!*ptr1) { len = (int)(ptr1 - ptr); break; } } gmap_beg.addr = ptr; c = gmap_beg.addr + len - 1; num_quote = 0; while ('"' == *c) { len--; c--; num_quote++; } if (0 >= len) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SELECTSYNTAX, 2, LEN_AND_STR(opname)); mupip_exit(ERR_MUNOACTION); } c = gmap_beg.addr; while (0 < num_quote) { if ('"' == *c) { c++; len--; } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SELECTSYNTAX, 2, LEN_AND_STR(opname)); mupip_exit(ERR_MUNOACTION); } num_quote--; } gmap_beg.addr = c; if ('^' == *c) { gmap_beg.addr++; len--; } gmap_beg.len = len; c = mu_extr_ident(&gmap_beg); len -= INTCAST(c - gmap_beg.addr); assert(len >= 0); if (0 == len) gmap_end = gmap_beg; else if (gmap_beg.len == 1 && '*' == *c) { gmap_beg.addr = (char*)&percent_lit; gmap_beg.len = SIZEOF(percent_lit); gmap_end.addr = (char*)&tilde_lit; gmap_end.len = SIZEOF(tilde_lit); } else if (1 == len && '*' == *c) { gmap_end = gmap_beg; gmap_beg.len--; *c = '~'; } else if (':' != *c) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SELECTSYNTAX, 2, LEN_AND_STR(opname)); mupip_exit(ERR_MUNOACTION); } else { gmap_beg.len = INTCAST(c - gmap_beg.addr); c++; gmap_end.addr = c; gmap_end.len = len - 1; if ('^' == *c) { gmap_end.addr++; gmap_end.len--; } c = mu_extr_ident(&gmap_end); MSTR_CMP(gmap_beg, gmap_end, rslt); if (((c - gmap_end.addr) != gmap_end.len) || (0 < rslt)) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SELECTSYNTAX, 2, LEN_AND_STR(opname)); mupip_exit(ERR_MUNOACTION); } } /* "estimated_entries" is the maximum number of entries that could be used up in the gmap array including the * next global_map call. The actual number of used entries could be much lower than this. * But since determining the actual number would mean scanning the gmap array for the first NULL pointer (a * performance overhead), we do an approximate check instead. */ estimated_entries += MAX_GMAP_ENTRIES_PER_ITER; if (estimated_entries >= gmap_size) { /* Current gmap array does not have enough space. Double size before calling global_map */ new_gmap_size = gmap_size * 2; /* double size of gmap array */ gmap_ptr = (mstr *)malloc(SIZEOF(mstr) * new_gmap_size); memcpy(gmap_ptr, gmap_ptr_base, SIZEOF(mstr) * gmap_size); if (gmap_ptr_base != &gmap[0]) free(gmap_ptr_base); gmap_size = new_gmap_size; gmap_ptr_base = gmap_ptr; } global_map(gmap_ptr_base, &gmap_beg, &gmap_end); DEBUG_ONLY( count = 1; for (gmap_ptr = gmap_ptr_base; gmap_ptr->addr; gmap_ptr++) count++; assert(count < gmap_size); ) } if (freeze) { GTM64_ONLY(init_hashtab_int8(&ext_hash, gmap_size * (100.0 / HT_LOAD_FACTOR), HASHTAB_COMPACT, HASHTAB_SPARE_TABLE);) NON_GTM64_ONLY(init_hashtab_int4(&ext_hash, gmap_size * (100.0 / HT_LOAD_FACTOR), HASHTAB_COMPACT, HASHTAB_SPARE_TABLE);) } gl_head->next = NULL; gl_tail = gl_head; *reg_max_rec = 0; *reg_max_key = 0; *reg_max_blk = 0; for (gmap_ptr = gmap_ptr_base; gmap_ptr->addr ; gmap_ptr++) { curr_gbl_name.mvtype = MV_STR; curr_gbl_name.str = *gmap_ptr++; DEBUG_ONLY(MSTRP_CMP(&curr_gbl_name.str, gmap_ptr, rslt);) assert(0 >= rslt); do { /* User input global names could be arbitrarily large. * Truncate to max supported length before computing hash. */ if (MAX_MIDENT_LEN < curr_gbl_name.str.len) curr_gbl_name.str.len = MAX_MIDENT_LEN; # if defined(GTM_TRIGGER) && defined(EXTRACT_HASHT_GLOBAL) if (HASHT_GBL_CHAR1 == curr_gbl_name.str.addr[0]) { /* Global names starting with "#" e.g. ^#t. Consider these as spanning across * all regions of the gbldir for the purposes of the caller (MUPIP EXTRACT * or MUPIP REORG or MUPIP SIZE). */ if (NULL == hashgbl_gvnh_reg) { hashgbl_gvnh_reg = (gvnh_reg_t *)malloc(SIZEOF(gvnh_reg_t)); /* initialize gvnh_reg fields to NULL specifically gvnh_reg->gvspan * as this is used by the caller of gv_select. */ memset(hashgbl_gvnh_reg, 0, SIZEOF(gvnh_reg_t)); } /* At this time, only ^#t is supported. In the future ^#k or some such globals * might be supported for other purposes. The following code needs to change if/when * that happens. */ assert(IS_GVKEY_HASHT_GBLNAME(curr_gbl_name.str.len, curr_gbl_name.str.addr)); if (!gd_header) gvinit(); for (reg = gd_header->regions, r_top = reg + gd_header->n_regions; reg < r_top; reg++) { if (IS_STATSDB_REGNAME(reg)) continue; GVTR_SWITCH_REG_AND_HASHT_BIND_NAME(reg); csa = cs_addrs; if (NULL == csa) /* not BG or MM access method */ continue; gv_select_reg((void *)&ext_hash, freeze, reg_max_rec, reg_max_key, reg_max_blk, restrict_reg, hashgbl_gvnh_reg, &gl_tail); } break; } # endif COMPUTE_HASH_MSTR(curr_gbl_name.str, hash_code); op_gvname_fast(VARLSTCNT(2) hash_code, &curr_gbl_name); assert(IS_REG_BG_OR_MM(gv_cur_region)); /* for dba_cm or dba_usr, op_gvname_fast/gv_bind_name/gv_init_reg would have errored out */ gvname.hash_code = hash_code; gvname.var_name = curr_gbl_name.str; tabent_mname = lookup_hashtab_mname((hash_table_mname *)gd_header->tab_ptr, &gvname); assert(NULL != tabent_mname); gvnh_reg = (gvnh_reg_t *)tabent_mname->value; assert(gv_cur_region == gvnh_reg->gd_reg); gvspan = gvnh_reg->gvspan; if (NULL == gvspan) gv_select_reg((void *)&ext_hash, freeze, reg_max_rec, reg_max_key, reg_max_blk, restrict_reg, gvnh_reg, &gl_tail); else { /* If global spans multiple regions, make sure gv_targets corresponding to ALL * spanned regions are allocated and gv_target->root is also initialized * accordingly for all the spanned regions. */ gvnh_spanreg_subs_gvt_init(gvnh_reg, gd_header, NULL); maxi = gvspan->max_reg_index; mini = gvspan->min_reg_index; for (i = mini; i <= maxi; i++) { assert(i >= 0); assert(i < gd_header->n_regions); reg = gd_header->regions + i; gvt = GET_REAL_GVT(gvspan->gvt_array[i - mini]); if (NULL != gvt) { gv_target = gvt; gv_cur_region = reg; change_reg(); gv_select_reg((void *)&ext_hash, freeze, reg_max_rec, reg_max_key, reg_max_blk, restrict_reg, gvnh_reg, &gl_tail); } } } op_gvorder(&curr_gbl_name); if (0 == curr_gbl_name.str.len) { (gmap_ptr + 1)->addr = 0; break; } assert('^' == *curr_gbl_name.str.addr); curr_gbl_name.str.addr++; curr_gbl_name.str.len--; MSTRP_CMP(&curr_gbl_name.str, gmap_ptr, rslt); } while (0 >= rslt); } if (gmap_ptr_base != &gmap[0]) free(gmap_ptr_base); } /* Assumes "gv_target" and "gv_cur_region" are properly setup at function entry */ STATICFNDEF void gv_select_reg(void *ext_hash, boolean_t freeze, int *reg_max_rec, int *reg_max_key, int *reg_max_blk, boolean_t restrict_reg, gvnh_reg_t *gvnh_reg, glist **gl_tail) { tp_region *rptr; mval val; glist *gl_ptr; # ifdef GTM64 ht_ent_int8 *tabent; # else ht_ent_int4 *tabent; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(gv_target->gd_csa == cs_addrs); if (restrict_reg) { /* Only select globals in specified regions */ for (rptr = grlist; NULL != rptr; rptr = rptr->fPtr) { if (gv_cur_region == rptr->reg) break; } if (NULL == rptr) return; /* this region is not part of specified regions. return right away */ } TREF(gd_targ_gvnh_reg) = NULL; /* needed so op_gvdata goes through gvcst_data (i.e. focuses only on the current region) * and NOT through gvcst_spr_data (which would focus on all spanned regions if any). */ op_gvdata(&val); /* It is okay not to restore TREF(gd_targ_gvnh_reg) since it will be initialized again once gv_select is done * and the next op_gvname/op_gvextnam/op_gvnaked call is made. It is not needed until then anyways. */ if ((0 == val.m[1]) && ((0 == gv_target->root) || !TREF(want_empty_gvts))) return; /* global has an empty GVT in this region. return right away */ if (freeze) { /* Note: gv_cur_region pointer is 64-bits on 64-bit platforms. So use hashtable accordingly. */ GTM64_ONLY(if(add_hashtab_int8((hash_table_int8 *)ext_hash,(gtm_uint64_t *)&gv_cur_region, HT_VALUE_DUMMY, &tabent))) NON_GTM64_ONLY(if (add_hashtab_int4((hash_table_int4 *)ext_hash, (uint4 *)&gv_cur_region, HT_VALUE_DUMMY, &tabent))) { if (FROZEN(cs_addrs->hdr)) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_FREEZE, 2, REG_LEN_STR(gv_cur_region)); mupip_exit(ERR_MUNOFINISH); } /* Cannot proceed for read-only data files */ if (gv_cur_region->read_only) { util_out_print("Cannot freeze the database",TRUE); gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); mupip_exit(ERR_MUNOFINISH); } while (REG_ALREADY_FROZEN == region_freeze(gv_cur_region, TRUE, FALSE, FALSE, FALSE, TRUE)) { hiber_start(1000); if (mu_ctrly_occurred || mu_ctrlc_occurred) { gtm_putmsg_csa(CSA_ARG(cs_addrs) VARLSTCNT(1) ERR_FREEZECTRL); mupip_exit(ERR_MUNOFINISH); } } } } if (*reg_max_rec < cs_data->max_rec_size) *reg_max_rec = cs_data->max_rec_size; if (*reg_max_key < cs_data->max_key_size) *reg_max_key = cs_data->max_key_size; if (*reg_max_blk < cs_data->blk_size) *reg_max_blk = cs_data->blk_size; gl_ptr = (glist *)malloc(SIZEOF(glist)); gl_ptr->next = NULL; gl_ptr->reg = gv_cur_region; gl_ptr->gvt = gv_target; gl_ptr->gvnh_reg = gvnh_reg; (*gl_tail)->next = gl_ptr; *gl_tail = gl_ptr; } fis-gtm-V7.0-005/sr_port/gv_select.h0000755000032200000250000000131014342376331016165 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #ifndef GV_SELECT_INCLUDED #define GV_SELECT_INCLUDED void gv_select(char *cli_buff, int n_len, boolean_t freeze, char opname[], glist *gl_head, int *reg_max_rec, int *reg_max_key, int *reg_max_blk, boolean_t restrict_reg); #endif /* GV_SELECT_INCLUDED */ fis-gtm-V7.0-005/sr_port/gv_srch_gblname.c0000644000032200000250000000271114342376331017330 0ustar librarygtc/**************************************************************** * * * Copyright 2013 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 "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" /* Searches a global directory "gblnames" array for which entry an input "global-name" falls in */ gd_gblname *gv_srch_gblname(gd_addr *addr, char *name, int name_len) { int res; int low, high, mid; gd_gblname *gname_start, *gname; assert(addr->n_gblnames); /* caller should have taken care of this */ /* At all times in the loop, "low" corresponds to the smallest possible value for "gname" * and "high" corresponds to one more than the highest possible value for "gname". */ low = 0; high = addr->n_gblnames; gname_start = &addr->gblnames[low]; do { if (low == high) break; assert(low < high); mid = (low + high) / 2; assert(low <= mid); assert(mid < high); gname = &gname_start[mid]; res = memcmp(name, gname->gblname, name_len); if (0 > res) high = mid; else if (0 < res) low = mid + 1; else if ('\0' == gname->gblname[name_len]) return gname; else high = mid; } while (TRUE); return NULL; } fis-gtm-V7.0-005/sr_port/gv_srch_map.c0000644000032200000250000001343414342376331016504 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2017 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 "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gvcst_protos.h" /* needed by OPEN_BASEREG_IF_STATSREG */ /* Searches a global directory map array for which map entry an input "key" falls in. * "key" could be an unsubscripted or subscripted global reference. * "skip_basedb_open" is set to TRUE in a special case from a call in "gvcst_init_statsDB" and is FALSE otherwise. * In that special case, the caller knows to set the ^%YGS map entry to point to the appropriate statsdb region * so we do not need to do unnecessary opens of other basedb regions. But otherwise this function ensures that * if ever a map entry is returned that points to a statsdb region, the corresponding basedb region has been opened. */ gd_binding *gv_srch_map(gd_addr *addr, char *key, int key_len, boolean_t skip_basedb_open) { int res; int low, high, mid; gd_binding *map_start, *map; # ifdef DEBUG int dbg_match; # endif map_start = addr->maps; assert(('%' == map_start[1].gvkey.addr[0]) && (1 == map_start[1].gvname_len)); /* We expect all callers to search for global names that start with "^%" or higher. */ assert(0 <= memcmp(key, &(map_start[1].gvkey.addr[0]), key_len)); low = 2; /* get past local locks AND first map entry which is always "%" */ high = addr->n_maps - 1; DEBUG_ONLY(dbg_match = -1;) /* At all times in the loop, "low" corresponds to the smallest possible value for "map" * and "high" corresponds to the highest possible value for "map". */ do { if (low == high) { assert((-1 == dbg_match) || (low == dbg_match)); map = &map_start[low]; if (!skip_basedb_open) OPEN_BASEREG_IF_STATSREG(map); /* can modify map->reg.addr if statsDBReg */ return map; } assert(low < high); mid = (low + high) / 2; assert(low <= mid); assert(mid < high); map = &map_start[mid]; res = memcmp(key, &(map->gvkey.addr[0]), key_len); if (0 > res) high = mid; else if (0 < res) low = mid + 1; else if (key_len < (map->gvkey_len - 1)) high = mid; else { assert(key_len == (map->gvkey_len - 1)); low = mid + 1; # ifdef DEBUG dbg_match = low; # else map = &map_start[low]; if (!skip_basedb_open) OPEN_BASEREG_IF_STATSREG(map); /* can modify map->reg.addr if statsDBReg */ return map; # endif } } while (TRUE); } /* Similar to gv_srch_map except that it does a linear search starting at a specific map and going FORWARD. * We expect this function to be invoked in case the caller expects the target map to be found very close to the current map. * This might be faster in some cases than a binary search of the entire gld map array (done by gv_srch_map). */ gd_binding *gv_srch_map_linear(gd_binding *start_map, char *key, int key_len) { gd_binding *map; int res; # ifdef DEBUG gd_addr *addr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # endif map = start_map; DEBUG_ONLY( addr = TREF(gd_targ_addr); assert(map > addr->maps); ) for ( ; ; map++) { /* Currently, the only callers of this function are gvcst_spr_* functions (e.g. "gvcst_spr_data" etc.). * And most of them (except gvcst_spr_query/gvcst_start_queryget) start an implicit TP transaction right * after this call. And since statsDB init is deferred once in TP, it is preferable to do the init before * the TP begins. So we include the OPEN_BASEREG_IF_STATSREG macro call here instead of in each of the * caller. This can be moved back to the individual callers if new callers of this function happen which * don't need this macro. */ OPEN_BASEREG_IF_STATSREG(map); /* can modify map->reg.addr if statsDBReg */ assert(map < &addr->maps[addr->n_maps]); res = memcmp(key, &map->gvkey.addr[0], key_len); if (0 < res) continue; if (0 > res) break; /* res == 0 at this point */ if (key_len < (map->gvkey_len - 1)) break; assert(key_len == (map->gvkey_len - 1)); map++; break; } OPEN_BASEREG_IF_STATSREG(map); /* can modify map->reg.addr if statsDBReg */ return map; } /* Similar to gv_srch_map_linear except that it does the linear search going BACKWARD. */ gd_binding *gv_srch_map_linear_backward(gd_binding *start_map, char *key, int key_len) { gd_binding *map; int res; # ifdef DEBUG gd_addr *addr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # endif map = start_map; DEBUG_ONLY( addr = TREF(gd_targ_addr); assert(map < &addr->maps[addr->n_maps]); ) for ( ; ; map--) { /* Currently, the only caller of this function is "gvcst_spr_zprevious". And it starts an implicit TP transaction * right after this call. And since statsDB init is deferred once in TP, it is preferable to do the init before * the TP begins. So we include the OPEN_BASEREG_IF_STATSREG macro call here instead of in each of the * caller. This can be moved back to the individual callers if new callers of this function happen which * don't need this macro. */ OPEN_BASEREG_IF_STATSREG(map); /* can modify map->reg.addr if statsDBReg */ assert(map >= addr->maps); res = memcmp(key, &map->gvkey.addr[0], key_len); if (0 < res) break; if (0 > res) continue; /* res == 0 at this point */ if (key_len < (map->gvkey_len - 1)) continue; assert(key_len == (map->gvkey_len - 1)); break; } map++; assert(map > addr->maps); OPEN_BASEREG_IF_STATSREG(map); /* can modify map->reg.addr if statsDBReg */ return map; } fis-gtm-V7.0-005/sr_port/gv_trigger_common.h0000644000032200000250000000554314342376331017732 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2018 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. * * * ****************************************************************/ #ifndef GV_TRIGGER_COMMON_INCLUDED #define GV_TRIGGER_COMMON_INCLUDED /* Following macros, though related to trigger global (^#t), are needed in trigger non-supported platforms. * While gv_trigger.h lives placed in sr_unix, VMS needs the following macros. */ #define HASHT_GBL_CHAR1 '#' #define HASHT_GBL_CHAR2 't' #define HASHT_GBLNAME "#t" #define HASHT_FULL_GBLNAME "^#t" #define HASHT_GBLNAME_LEN STR_LIT_LEN(HASHT_GBLNAME) #define HASHT_FULL_GBLNM_LEN STR_LIT_LEN(HASHT_FULL_GBLNAME) #define HASHT_GBLNAME_FULL_LEN STR_LIT_LEN(HASHT_GBLNAME) + 1 /* including terminating '\0' subscript */ /* This macro assumes addr points to a gv_currkey like subscript representation (with potential subscripts) */ #define IS_GVKEY_HASHT_GBLNAME(LEN, ADDR) ((HASHT_GBLNAME_LEN <= LEN) \ && (KEY_DELIMITER == ADDR[HASHT_GBLNAME_LEN]) \ && (HASHT_GBL_CHAR1 == ADDR[0]) \ && (HASHT_GBL_CHAR2 == ADDR[1])) /* This macro assumes addr points to an mname (i.e. unsubscripted) */ #define IS_MNAME_HASHT_GBLNAME(MNAME) ((HASHT_GBLNAME_LEN == MNAME.len) && !MEMCMP_LIT(MNAME.addr, HASHT_GBLNAME)) /* Similar to IS_GVKEY_HASHT_GBLNAME but used in places where ADDR points to ZWR formatted KEY (includes '^') */ #define IS_GVKEY_HASHT_FULL_GBLNAME(LEN, ADDR) ((HASHT_FULL_GBLNM_LEN <= LEN) \ && ('^' == ADDR[0]) \ && (HASHT_GBL_CHAR1 == ADDR[1]) \ && (HASHT_GBL_CHAR2 == ADDR[2])) /* Currently supported ^#t global format */ #define HASHT_GBL_CURLABEL "4" /* V6.2-002 onwards, keep up to date for test/com_u/trigupgrd_test.csh */ #define HASHT_GBL_CURLABEL_INT 4 #define HASHT_GBL_CURLABEL_LEN STR_LIT_LEN(HASHT_GBL_CURLABEL) /* HASHT_GBL_CURLABEL values of prior trigger versions */ #define V25_HASHT_GBL_LABEL "3" /* V6.2-001 */ #define V25_HASHT_GBL_LABEL_INT 3 #define V21_HASHT_GBL_LABEL "2" /* V5.4-002 to V6.2-000 */ #define V21_HASHT_GBL_LABEL_INT 2 #define V19_HASHT_GBL_LABEL "1" /* V5.4-000 to V5.4-001 */ #define V19_HASHT_GBL_LABEL_INT 1 #define LITERAL_HASHLABEL "#LABEL" #define LITERAL_HASHCYCLE "#CYCLE" #define LITERAL_HASHCOUNT "#COUNT" #define LITERAL_HASHTRHASH "#TRHASH" #define LITERAL_HASHLABEL_LEN STR_LIT_LEN(LITERAL_HASHLABEL) #define LITERAL_HASHCYCLE_LEN STR_LIT_LEN(LITERAL_HASHCYCLE) #define LITERAL_HASHCOUNT_LEN STR_LIT_LEN(LITERAL_HASHCOUNT) #define LITERAL_HASHTRHASH_LEN STR_LIT_LEN(LITERAL_HASHTRHASH) #endif fis-gtm-V7.0-005/sr_port/gv_xform_key.c0000755000032200000250000000474614342376331016724 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gvsub2str.h" #include "mvalconv.h" #include "gv_xform_key.h" GBLREF int4 gv_keysize; GBLREF gv_namehead *gv_target; GBLREF gd_region *gv_cur_region; /* transform gv_currkey or gv_altkey based on collation sequence * if XBACK is true then convert from internal to external format. * if XBACK is false, convert from external to internal format */ void gv_xform_key(gv_key *keyp, boolean_t xback) { unsigned char *c0, *c1, *ctop; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TREF(gv_sparekey_size) < gv_keysize) { if (TREF(gv_sparekey)) free(TREF(gv_sparekey)); else { (TREF(gv_sparekey_mval)).str.addr = (char *)malloc(MAX_ZWR_KEY_SZ); (TREF(gv_sparekey_mval)).mvtype = MV_STR; } TREF(gv_sparekey) = (gv_key *)malloc(SIZEOF(gv_key) + gv_keysize + 1); TREF(gv_sparekey_size) = gv_keysize; } assert(keyp->top == gv_keysize); assert(keyp->end < keyp->top); memcpy(TREF(gv_sparekey), keyp, SIZEOF(gv_key) + keyp->end + 1); c1 = keyp->base; while (*c1++) ; c0 = (TREF(gv_sparekey))->base + (c1 - keyp->base); ctop = &((TREF(gv_sparekey))->base[(TREF(gv_sparekey))->end]); if (!*c0) /* no subscipts */ { assert(c0 == ctop); return; } assert(c0 < ctop); keyp->prev = 0; keyp->end = c1 - keyp->base; for (; c0 < ctop; ) { if (STR_SUB_PREFIX != *c0) { assert(!(gv_target->nct)); while (*c1++ = *c0++) ; keyp->prev = keyp->end; keyp->end = c1 - keyp->base; } else { TREF(transform) = xback; (TREF(gv_sparekey_mval)).str.len = gvsub2str(c0, &((TREF(gv_sparekey_mval)).str), FALSE) - (unsigned char *)(TREF(gv_sparekey_mval)).str.addr; TREF(transform) = !xback; mval2subsc(TADR(gv_sparekey_mval), keyp, gv_cur_region->std_null_coll); c1 = &keyp->base[keyp->end]; while (*c0++) ; } assert(keyp->end < keyp->top); } return; } fis-gtm-V7.0-005/sr_port/gv_xform_key.h0000755000032200000250000000120014342376331016707 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GV_XFORM_KEY_H_INCLUDED #define GV_XFORM_KEY_H_INCLUDED void gv_xform_key(gv_key *keyp, boolean_t xback); #endif fis-gtm-V7.0-005/sr_port/gvcmx.h0000755000032200000250000000177114342376331015351 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2004 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GVCMX_INCLUDED #define GVCMX_INCLUDED mint gvcmx_data(void); bool gvcmx_get(mval *v); bool gvcmx_order(void); bool gvcmx_query(mval *val); bool gvcmx_reqremlk(unsigned char laflag, int4 time); bool gvcmx_resremlk(unsigned char c); bool gvcmx_zprevious(void); void gvcmx_canremlk(void); void gvcmx_kill(bool so_subtree); void gvcmx_put(mval *v); void gvcmx_increment(mval *increment, mval *result); void gvcmx_susremlk(unsigned char rmv_locks); void gvcmx_unlock(unsigned char rmv_locks, bool specific, char incr); #endif /* GVCMX_INCLUDED */ fis-gtm-V7.0-005/sr_port/gvcmy_close.h0000755000032200000250000000104514342376331016531 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GVCMY_CLOSE_H_INCLUDED #define GVCMY_CLOSE_H_INCLUDED void gvcmy_close(struct CLB *); #endif fis-gtm-V7.0-005/sr_port/gvcmy_rundown.h0000755000032200000250000000115214342376331017117 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GVCMY_RUNDOWN_H_INCLUDED #define GVCMY_RUNDOWN_H_INCLUDED void gvcmy_rundown(void); #endif fis-gtm-V7.0-005/sr_port/gvcst_blk_build.c0000755000032200000250000002604414342376331017355 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gdsroot.h" #include "gdsblk.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdscc.h" #include "gdskill.h" #include "filestruct.h" #include "copy.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "gvcst_blk_build.h" #include "gtmimagename.h" #include "spec_type.h" #ifdef DEBUG GBLREF boolean_t skip_block_chain_tail_check; #endif GBLREF unsigned char cw_set_depth; GBLREF uint4 dollar_tlevel; GBLREF sgm_info *sgm_info_ptr; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF boolean_t mu_reorg_upgrd_dwngrd_in_prog, write_after_image; GBLREF unsigned int t_tries; GBLREF jnl_gbls_t jgbl; void gvcst_blk_build(cw_set_element *cse, sm_uc_ptr_t base_addr, trans_num ctn) { blk_segment *seg, *stop_ptr, *array; boolean_t long_blk_id; int4 offset, blk_id_sz, off_chain_sz; off_chain chain; v6_off_chain v6_chain; sm_uc_ptr_t ptr, ptrtop, c; sm_ulong_t n; trans_num blktn; # ifdef DEBUG boolean_t integ_error_found; rec_hdr_ptr_t rp; sm_uc_ptr_t chainptr, input_base_addr; unsigned short nRecLen; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* For a TP transaction we should reach here with crit as the only function that invokes this is bg_update_phase2 * which operates outside crit. The exceptions to this are DSE (write_after_image is TRUE) or ONLINE ROLLBACK * which holds crit for the entire duration */ assert((dba_bg != cs_data->acc_meth) || dollar_tlevel || !cs_addrs->now_crit || write_after_image || jgbl.onlnrlbk || mu_reorg_upgrd_dwngrd_in_prog || ((NULL != cse->recompute_list_head) && (gds_t_write == cse->mode))); assert((dba_mm != cs_data->acc_meth) || dollar_tlevel || cs_addrs->now_crit); assert(cse->mode != gds_t_writemap); assert(cse->ondsk_blkver); array = (blk_segment *)cse->upd_addr; long_blk_id = BLK_ID_32_VER < cse->ondsk_blkver; blk_id_sz = SIZEOF_BLK_ID(long_blk_id); off_chain_sz = blk_id_sz; /* the off_chain struct should be the same size as the block_id in the current block */ assert(array->len >= SIZEOF(blk_hdr)); assert(array->len <= cs_data->blk_size); assert((cse->ins_off + blk_id_sz) <= array->len); assert((short)cse->index >= 0); assert(!cse->undo_next_off[0] && !cse->undo_offset[0]); assert(!cse->undo_next_off[1] && !cse->undo_offset[1]); DEBUG_ONLY(input_base_addr = base_addr;) if (base_addr == NULL) { /* it's the first private TP build */ assert(dollar_tlevel); assert(cse->blk_target); base_addr = cse->new_buff = (unsigned char *)get_new_free_element(sgm_info_ptr->new_buff_list); cse->first_copy = TRUE; } else assert(0 == ((sm_ulong_t)base_addr & 3)); /* word aligned at least */ /* The block-transaction-number is modified before the contents of the block are modified. This is * done so as to allow a cdb_sc_blkmod check (done in t_qread, gvcst_search, gvcst_put and tp_hist) * to be done out-of-crit by just checking for the transaction numbers. If the contents of the block * were modified first, there is a possibility that the block-transaction number didn't get updated * although the contents of the block may have changed and basing the decision of block-modified on * just the transaction numbers may not always be correct. * Note that in mm_update and bg_update there is an else block where instead of gvcst_blk_build(), * a memcpy is done. To effect the above change, we also need to switch the order of memcpy and * block-transaction-number-updation in those places. * Note that a similar change is not needed in gvcst_map_build() because that will never be in the * search history for any key. */ if (!ctn && dollar_tlevel) { /* Subtract one so will pass concurrency control for mm databases. * This block is guaranteed to be in an earlier history from when it was first read, * so this history is superfluous for concurrency control. * The correct tn is put in the block in mm_update or bg_update when the block is copied to the database. */ ctn = cs_addrs->ti->curr_tn - 1; } /* Assert that the block's transaction number is LESS than the transaction number corresponding to the blk build. * i.e. no one else should have touched the block contents in shared memory from the time we locked this in phase1 * to the time we build it in phase2. * There are a few exceptions. * a) With DSE, it is possible to change the block transaction number and then a DSE or MUPIP command can run * on the above block with the above condition not true. * b) tp_tend calls gvcst_blk_build for cse's with mode kill_t_write/kill_t_create. For them we build a private * copy of the block for later use in phase2 of the M-kill. In this case, blktn could be * uninitialized so cannot do any checks using this value. * c) For MM, we dont have two phase commits so dont do any checks in that case. * d) For acquired blocks, it is possible that some process had read in the uninitialized block from disk * outside of crit (due to concurrency issues). Therefore the buffer could contain garbage. So we cannot * rely on the buffer contents to determine the block's transaction number. * e) If a twin is created, we explicitly set its buffer tn to be equal to ctn in phase1. * But since we are not passed the "cr" in this routine, it is not easily possible to check that. * Hence in case of twinning, we relax the check so buffertn == ctn is allowed. */ DEBUG_ONLY(blktn = ((blk_hdr_ptr_t)base_addr)->tn); assert(!IS_MCODE_RUNNING || !cs_addrs->t_commit_crit || (dba_bg != cs_data->acc_meth) || (n_gds_t_op < cse->mode) || (cse->mode == gds_t_acquired) || ((!cs_data->asyncio && (blktn < ctn)) || (cs_data->asyncio && (blktn <= ctn)))); /* With memory instruction reordering (currently possible only on AIX with the POWER architecture) it is possible * the early_tn we read in the assert below gets executed BEFORE the curr_tn read that happens a few lines above. * That could then fail this assert (GTM-8523). Account for that with the AIX_ONLY condition below. */ assert((ctn < cs_addrs->ti->early_tn) || write_after_image AIX_ONLY(|| (cs_data->acc_meth == dba_mm))); ((blk_hdr_ptr_t)base_addr)->bver = cse->ondsk_blkver; ((blk_hdr_ptr_t)base_addr)->tn = ctn; ((blk_hdr_ptr_t)base_addr)->bsiz = UINTCAST(array->len); ((blk_hdr_ptr_t)base_addr)->levl = cse->level; if (cse->forward_process) { stop_ptr = (blk_segment *)array->addr; seg = cse->first_copy ? array + 1: array + 2; ptr = base_addr + SIZEOF(blk_hdr); if (!cse->first_copy) ptr += ((blk_segment *)(array + 1))->len; for ( ; seg <= stop_ptr; ) { assert(0L <= ((INTPTR_T)seg->len)); DBG_BG_PHASE2_CHECK_CR_IS_PINNED(cs_addrs, seg); memmove(ptr, seg->addr, seg->len); ptr += seg->len; seg++; } } else { stop_ptr = cse->first_copy ? array : array + 1; seg = (blk_segment *)array->addr; ptr = base_addr + array->len; while (seg != stop_ptr) { assert(0L <= ((INTPTR_T)seg->len)); DBG_BG_PHASE2_CHECK_CR_IS_PINNED(cs_addrs, seg); ptr -= (n = seg->len); memmove(ptr, seg->addr, n); seg--; } } if (dollar_tlevel) { if (cse->ins_off) { /* if the cw set has a reference to resolve, move it to the block */ assert(cse->index < sgm_info_ptr->cw_set_depth); assert((int)cse->ins_off >= (int)(SIZEOF(blk_hdr) + SIZEOF(rec_hdr))); assert((int)(cse->next_off + cse->ins_off + blk_id_sz) <= array->len); if (cse->first_off == 0) cse->first_off = cse->ins_off; ptr = base_addr + cse->ins_off; chain.flag = 1; assert((1 << CW_INDEX_MAX_BITS) > cse->index); chain.cw_index = cse->index; assert((1LL << NEXT_OFF_MAX_BITS) > cse->next_off); chain.next_off = cse->next_off; WRITE_OFF_CHAIN(long_blk_id, &chain, &v6_chain, ptr); cse->index = 0; cse->ins_off = 0; cse->next_off = 0; } # ifdef DEBUG if (offset = cse->first_off) { /* Verify the integrity of the TP chains within a newly created block. * If it is the first TP private build, the update array could have referenced * shared memory global buffers which could have been concurrently updated. * So the integrity of the chain cannot be easily verified. If ever we find * an integ error in the chain, we check if this is the first private TP build * and if so allow it but set a debug flag donot_commit so we never ever commit * this transaction. The hope is that it will instead restart after validation. */ ptr = base_addr; ptrtop = ptr + ((blk_hdr_ptr_t)ptr)->bsiz; chainptr = ptr + offset; ptr += SIZEOF(blk_hdr); integ_error_found = FALSE; for ( ; ptr < ptrtop; ) { do { GET_USHORT(nRecLen, &((rec_hdr_ptr_t)ptr)->rsiz); if (0 == nRecLen) { assert(NULL == input_base_addr); integ_error_found = TRUE; break; } c = ptr; c += SIZEOF(rec_hdr); /* The *-key does not have a key. Everything else has one. Account for that. */ if (bstar_rec_size(long_blk_id) != nRecLen) { for ( ; (c < ptrtop) && ((*c++ != KEY_DELIMITER) || (*c != KEY_DELIMITER)); ) ; if (c >= ptrtop) { assert(NULL == input_base_addr); integ_error_found = TRUE; break; } c++; } ptr += nRecLen; if (c == chainptr) { if (((ptr - off_chain_sz) != chainptr) && ((ptr - off_chain_sz - COLL_SPEC_LEN) != chainptr)) { assert(NULL == input_base_addr); integ_error_found = TRUE; } break; } if (c > chainptr) { assert(NULL == input_base_addr); integ_error_found = TRUE; break; } READ_OFF_CHAIN(long_blk_id, &chain, &v6_chain, c); if (chain.flag) { assert(NULL == input_base_addr); integ_error_found = TRUE; break; } } while (ptr < ptrtop); if (integ_error_found) break; if (chainptr < ptrtop) { READ_OFF_CHAIN(long_blk_id, &chain, &v6_chain, chainptr); assert(1 == chain.flag || (skip_block_chain_tail_check && (0 == chain.next_off))); assert(chain.cw_index < sgm_info_ptr->cw_set_depth); offset = chain.next_off; if (0 == offset) chainptr = ptrtop; else { chainptr = chainptr + offset; assert(chainptr < ptrtop); /* ensure we have not overrun the buffer */ } } } if (integ_error_found) TREF(donot_commit) |= DONOTCOMMIT_GVCST_BLK_BUILD_TPCHAIN; else assert(0 == offset); /* ensure the chain is NULL terminated */ } # endif } else assert(dollar_tlevel || (cse->index < (int)cw_set_depth)); } fis-gtm-V7.0-005/sr_port/gvcst_blk_build.h0000755000032200000250000000117114342376331017354 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GVCST_BLK_BUILD_INCLUDED #define GVCST_BLK_BUILD_INCLUDED void gvcst_blk_build(cw_set_element *cse, sm_uc_ptr_t base_addr, trans_num ctn); #endif /* GVCST_BLK_BUILD_INCLUDED */ fis-gtm-V7.0-005/sr_port/gvcst_blk_search.c0000755000032200000250000001421114342376331017514 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_stdio.h" #include "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "copy.h" #include "cdb_sc.h" #include "gvcst_protos.h" /* for gvcst_search_tail,gvcst_search_blk prototype */ #include "min_max.h" #include "gvcst_expand_key.h" #include "send_msg.h" #include "cert_blk.h" /* * ------------------------------------------------------------------- * Search a single gvcst block * * function definition * enum cdb_sc gvcst_search_blk(pKey,pStat) * gv_key *pKey; - target key * srch_blk_status *pStat; - status block for this buffer * * function returns cdb_sc_normal if successful; otherwise, * one of the failure codes cdb_sc_badoffset or cdb_sc_blklenerr. * * function definition * enum cdb_sc gvcst_search_tail(pKey,pStat,pOldKey) * gv_key *pKey; - target key * srch_blk_status *pStat; - status block for this buffer * gv_key *pOldKey; - key for status block * * gvcst_search_tail is identical to gvcst_search_blk, * except instead of starting with the beginning * of the block, it starts where the previous gvcst_search_blk * left off, using the srch_blk_status to set-up. * * if successful, fills in srch_blk_status as follows: * * --------------------------------- * | Block number (untouched) | * --------------------------------- * | Buffer address (input param) | * --------------------------------- * | Transaction number (untouched)| * --------------------------------- * | match | offset | previous record * --------------------------------- * | match | offset | current record * --------------------------------- * * if the match is not found, or is found at the top or bottom of the * block, then the values of the return fields are as follows: * * PREVIOUS REC CURRENT REC * CONDITION MATCH OFFSET MATCH OFFSET * --------- ---- ------ ----- ------ * Buffer empty 0 0 0 7 * Hit first key in block 0 0 a 7 * Hit star key b c 0 x * Hit last key (leaf) b c a x * Went past last key (leaf) b x 0 y * * where: * a = size of target key * b = number of characters which match on the previous * key (including those which have been compressed away) * c = offset of previous record * x = offset for last record in the block * y = top of buffer (same as block bsize) * * * Block structure * block : * blk_hdr : * blk_data : record...record * record : [rec_data] * rec_hdr : * rec_data : [byte...byte] * ------------------------------------------------------------------- */ GBLREF unsigned int t_tries; GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; error_def(ERR_TEXT); #ifdef DEBUG #include "gdscc.h" #define DBG_CHECK_SRCH_HIST_AND_CSE_BUFFER_MATCH(pStat) \ { \ GBLREF uint4 dollar_tlevel; \ \ srch_blk_status *tp_srch_status; \ cw_set_element *cse; \ \ if (dollar_tlevel) \ { \ tp_srch_status = pStat->first_tp_srch_status; \ if (NULL != tp_srch_status) \ { \ cse = tp_srch_status->cse; \ if (NULL != cse) \ assert(cse->new_buff == pStat->buffaddr); \ } \ } \ } #else #define DBG_CHECK_SRCH_HIST_AND_CSE_BUFFER_MATCH(pStat) #endif #define INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat) if (CDB_STAGNATE <= t_tries) gvcst_search_fail(pStat); #define OUT_LINE 1024 + 1 static void gvcst_search_fail(srch_blk_status *pStat) { char buff[OUT_LINE], crbuff[SIZEOF(blk_hdr_ptr_t) + 1], regbuff[MAX_RN_LEN + 1]; uint4 len; assert(CDB_STAGNATE <= t_tries); assert((NULL != pStat) && ((NULL != pStat->cr) || (dba_mm == gv_cur_region->dyn.addr->acc_meth)) && (NULL != cs_addrs)); if (NULL != pStat) { if (NULL != pStat->cr) { SNPRINTF(crbuff, OUT_LINE, ": crbuff = 0x%lX", pStat->cr->buffaddr); cert_blk(gv_cur_region, pStat->cr->blk, (blk_hdr_ptr_t)GDS_ANY_REL2ABS(cs_addrs, pStat->cr->buffaddr), 0, SEND_MSG_ON_CERT_FAIL, NULL); } else crbuff[0] = '\0'; len = (6 * SIZEOF(long unsigned int)) + gv_cur_region->rname_len + 1 + STRLEN("Possible data corruption in region ") + STRLEN(" : blk = 0x : buff = 0x : cr = 0x% : csa = 0x% : csalock = 0x%"); memcpy(regbuff, gv_cur_region->rname, gv_cur_region->rname_len); regbuff[gv_cur_region->rname_len] = '\0'; SNPRINTF(buff, len, "Possible data corruption in region %s : blk = 0x%lX : buff = 0x%lX : cr = 0x%lX %s : " "csa = 0x%lX : csalock = 0x%lX", regbuff, (long unsigned int)pStat->blk_num, (long unsigned int)pStat->buffaddr, (long unsigned int)pStat->cr, crbuff, (long unsigned int)cs_addrs, (long unsigned int)cs_addrs->mlkctl); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_STR(buff)); } assert(t_tries); /* assert here so we don't have to do it when this returns */ } #define GVCST_SEARCH_EXPAND_PREVKEY #define GVCST_SEARCH_BLK #include "gvcst_blk_search.h" /* for function gvcst_search_blk_expand_prevkey() */ /* BYPASSOK : intentional duplicate include. */ #undef GVCST_SEARCH_BLK #define GVCST_SEARCH_TAIL #include "gvcst_blk_search.h" /* for function gvcst_search_tail_expand_prevkey() */ /* BYPASSOK : intentional duplicate include. */ #undef GVCST_SEARCH_TAIL #undef GVCST_SEARCH_EXPAND_PREVKEY #define GVCST_SEARCH_BLK #include "gvcst_blk_search.h" /* for function gvcst_search_blk() */ /* BYPASSOK : intentional duplicate include. */ #undef GVCST_SEARCH_BLK #define GVCST_SEARCH_TAIL #include "gvcst_blk_search.h" /* for function gvcst_search_tail() */ /* BYPASSOK : intentional duplicate include. */ #undef GVCST_SEARCH_TAIL fis-gtm-V7.0-005/sr_port/gvcst_blk_search.h0000644000032200000250000003114214342376331017520 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015-2020 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. * * * ****************************************************************/ /* * -------------------------------------------------- * Search for a key in the block * * Return: * cdb_sc_normal - success * cdb_sc_badoffset - record with 0 length encountered, * possibly a corrupt block * cdb_sc_blklenerr - end of block reached without match * -------------------------------------------------- */ GBLREF sgmnt_data_ptr_t cs_data; GBLREF gv_namehead *gv_target; GBLREF uint4 dollar_tlevel; #ifndef GVCST_SEARCH_EXPAND_PREVKEY # ifdef GVCST_SEARCH_BLK enum cdb_sc gvcst_search_blk(gv_key *pKey, srch_blk_status *pStat) # endif # ifdef GVCST_SEARCH_TAIL /* gvcst_search_tail is the "start anywhere" version of gvcst_search_blk. * Currently this is called only for level-0 blocks. The below logic is coded with this assumption. * Getting started is a bit awkward, so excuse the gotos. */ enum cdb_sc gvcst_search_tail(gv_key *pKey, srch_blk_status *pStat, gv_key *pOldKey) # endif #else # ifdef GVCST_SEARCH_BLK GBLREF gv_key *gv_altkey; /* "gvcst_search_blk_expand_prevkey" is the same as "gvcst_search_blk" except this search happens on a level0 block * and sets gv_altkey to fully expanded key corresponding to pStat->prev_rec. This avoids a later call to gvcst_expand_key, * which would imply TWO searches of the block, and instead does the job with ONE search. */ enum cdb_sc gvcst_search_blk_expand_prevkey(gv_key *pKey, srch_blk_status *pStat) # endif # ifdef GVCST_SEARCH_TAIL /* "gvcst_search_tail_expand_prevkey" is the same as "gvcst_search_tail" except this search sets gv_altkey to the fully * expanded key corresponding to pStat->prev_rec. This avoids a later call to gvcst_expand_key, which would imply * TWO searches of the block, and instead does the job with ONE search. */ enum cdb_sc gvcst_search_tail_expand_prevkey(gv_key *pKey, srch_blk_status *pStat, gv_key *pOldKey) # endif #endif { /* register variables named in perceived order of declining impact */ register int nFlg, nTargLen, nMatchCnt, nTmp; sm_uc_ptr_t pBlkBase, pRecBase, pTop, pRec, pPrevRec; unsigned char *pCurrTarg, *pTargKeyBase; boolean_t long_blk_id; # ifdef GVCST_SEARCH_TAIL unsigned char *pOldKeyBase, *pCurrTargPos; int tmp_cmpc; # ifdef GVCST_SEARCH_EXPAND_PREVKEY gv_key *prevKey; enum cdb_sc status; # endif # endif unsigned short nRecLen; # ifdef GVCST_SEARCH_BLK boolean_t level0; # endif # ifdef GVCST_SEARCH_EXPAND_PREVKEY int prevKeyCmpLen; /* length of compressed portion of prevKey stored in gv_altkey->base */ int prevKeyUnCmpLen;/* Length of uncompressed portion of prevKey */ sm_uc_ptr_t prevKeyUnCmp; /* pointer to beginning of uncompressed portion of prevKey */ unsigned char *prevKeyStart; /* pointer to &gv_altkey->base[0] */ unsigned char *prevKeyTop; /* pointer to allocated end of gv_altkey */ unsigned char *tmpPtr; # else DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # endif # if defined(GVCST_SEARCH_TAIL) && !defined(GVCST_SEARCH_EXPAND_PREVKEY) assert(0 < memcmp(pKey->base, pOldKey->base, pKey->end + 1)); /* below code assumes this is ensured by caller */ if (0 == pStat->prev_rec.offset) return gvcst_search_blk(pKey, pStat); /* nice clean start at the begining of a block */ # endif /* The following load code (and code in a few other places) is coded in a "assembler" style * in an attempt to encourage the compiler to get it efficient. * For instance, memory and non-memory instructions are interlaced to encourage pipelining. * Of course a great compiler doesn't need help, but this is portable code and ... */ DBG_CHECK_SRCH_HIST_AND_CSE_BUFFER_MATCH(pStat); pBlkBase = pStat->buffaddr; long_blk_id = IS_64_BLK_ID(pBlkBase); # ifndef GVCST_SEARCH_EXPAND_PREVKEY # ifdef GVCST_SEARCH_BLK level0 = (0 == ((blk_hdr_ptr_t)pBlkBase)->levl); if (level0 && TREF(expand_prev_key)) return gvcst_search_blk_expand_prevkey(pKey, pStat); # endif # ifdef GVCST_SEARCH_TAIL if (TREF(expand_prev_key)) return gvcst_search_tail_expand_prevkey(pKey, pStat, pOldKey); # endif # else prevKeyStart = &gv_altkey->base[0]; prevKeyTop = &gv_altkey->base[gv_altkey->top]; # ifdef GVCST_SEARCH_BLK level0 = TRUE; /* We are in "gvcst_search_blk_expand_prevkey" so we should have been called for a level0 block */ prevKeyCmpLen = 0; prevKeyUnCmp = NULL; # endif # ifdef GVCST_SEARCH_TAIL /* Note: "level0" variable is guaranteed to be TRUE since gvcst_search_tail is currently invoked only for * leaf blocks. We therefore do not compute it like we do in gvcst_search_blk. Assert this assumption. */ assert(0 == pStat->level); # endif # endif pTop = pBlkBase + MIN(((blk_hdr_ptr_t)pBlkBase)->bsiz, cs_data->blk_size); pCurrTarg = pKey->base; pTargKeyBase = pCurrTarg; # ifdef GVCST_SEARCH_BLK pRecBase = pBlkBase; nRecLen = SIZEOF(blk_hdr); nMatchCnt = 0; nTargLen = (int)pKey->end; nTargLen++; /* for the terminating NUL on the key */ # endif # ifdef GVCST_SEARCH_TAIL pRecBase = pBlkBase + pStat->curr_rec.offset; pRec = pRecBase; nMatchCnt = pStat->prev_rec.match; pOldKeyBase = pOldKey->base; pPrevRec = pBlkBase + pStat->prev_rec.offset; # ifdef GVCST_SEARCH_EXPAND_PREVKEY prevKey = pStat->blk_target->prev_key; if ((NULL == prevKey) || (PREV_KEY_NOT_COMPUTED == prevKey->end)) { status = gvcst_expand_prev_key(pStat, pOldKey, gv_altkey); if (cdb_sc_normal != status) return status; prevKey = gv_altkey; } else { /* Since gv_altkey is used elsewhere, ensure that it is in sync with prevKey before performing the search * and returning to the caller. */ memcpy(gv_altkey->base, prevKey->base, prevKey->end - 1); } assert(prevKey->end); prevKeyCmpLen = prevKey->end - 1; prevKeyUnCmp = &prevKey->base[prevKeyCmpLen]; assert(KEY_DELIMITER == prevKeyUnCmp[0]); assert(KEY_DELIMITER == prevKeyUnCmp[1]); # endif if (pRec >= pTop) { /* Terminated at end of block */ if (pRec > pTop) { INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat); return cdb_sc_blklenerr; } if (0 != (nTargLen = nMatchCnt)) { do { if (*pCurrTarg++ != *pOldKeyBase++) break; } while (--nTargLen); } nMatchCnt -= nTargLen; nTargLen = 0; # ifdef GVCST_SEARCH_EXPAND_PREVKEY /* Normally pTop points to an offset in the GDS block. But in this case, we are terminating the * search without a search of the actual block and so we need to point pTop to the same location * where prevKeyUnCmp points and that is prevKey. */ pTop = &prevKey->base[prevKey->end + 1]; /* + 1 needed to balance pTop-- done at function end */ # endif } else { nTargLen = pKey->end; nTargLen++; /* for the NUL that terminates the key */ GET_USHORT(nRecLen, &((rec_hdr_ptr_t)pRec)->rsiz); EVAL_CMPC2((rec_hdr_ptr_t)pRec, nTmp); tmp_cmpc = nTmp; nFlg = tmp_cmpc; if (0 != nFlg) { do { if (0 != (nFlg = *pCurrTarg - *pOldKeyBase++)) break; pCurrTarg++; } while (--tmp_cmpc); assert(0 <= nFlg); /* because gvcst_search_tail is called ONLY if pTarg->clue.key < pKey */ if (0 < nFlg) { nMatchCnt = (int)(pCurrTarg - pTargKeyBase); nTargLen -= nMatchCnt; } } if (0 == nFlg) { tmp_cmpc = nMatchCnt; nMatchCnt = (int)(pCurrTarg - pTargKeyBase); nTargLen -= nMatchCnt; tmp_cmpc -= nMatchCnt; if (0 < tmp_cmpc) { pCurrTargPos = pCurrTarg; do { if (*pCurrTargPos++ != *pOldKeyBase++) break; nMatchCnt++; } while (--tmp_cmpc); } goto alt_loop_entry; } # endif for (;;) { pRec = pRecBase + nRecLen; # ifdef GVCST_SEARCH_EXPAND_PREVKEY if (pRecBase != pBlkBase) { /* nTmp points to the compression count corresponding to pPrevRec */ if (nTmp > prevKeyCmpLen) { if (((prevKeyStart + nTmp) >= prevKeyTop) || (NULL == prevKeyUnCmp)) { if (dollar_tlevel) TP_TRACE_HIST_MOD(pStat->blk_num, pStat->blk_target, tp_blkmod_gvcst_srch, cs_data, pStat->tn, ((blk_hdr_ptr_t)pBlkBase)->tn, pStat->level); else NONTP_TRACE_HIST_MOD(pStat, t_blkmod_gvcst_srch); return cdb_sc_blkmod; } # ifdef GVCST_SEARCH_TAIL assert((prevKeyUnCmp > pBlkBase) || ((prevKeyUnCmp == &prevKey->base[prevKeyCmpLen]) && (prevKeyCmpLen == (prevKey->end - 1)))); # else assert(prevKeyUnCmp > pBlkBase); # endif memcpy(prevKeyStart + prevKeyCmpLen, prevKeyUnCmp, nTmp - prevKeyCmpLen); } prevKeyCmpLen = nTmp; prevKeyUnCmp = pRecBase + SIZEOF(rec_hdr); } # endif if (pRec >= pTop) { /* Terminated at end of block */ if (pRec > pTop) /* If record goes off the end, then block must be bad */ { INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat); return cdb_sc_blklenerr; } nTargLen = 0; # ifdef GVCST_SEARCH_BLK if (!level0) nMatchCnt = 0; /* star key */ else # endif { /* data block */ pPrevRec = pRecBase; pRecBase = pRec; } break; } GET_USHORT(nRecLen, &((rec_hdr_ptr_t)pRec)->rsiz); if (0 == nRecLen) /* If record length is 0, then block must be bad */ { INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat); return cdb_sc_badoffset; } pPrevRec = pRecBase; pRecBase = pRec; /* If current compression count > last match, then this record also matches on 'last match' characters. * Keep looping. */ EVAL_CMPC2((rec_hdr_ptr_t)pRec, nTmp) if (nTmp > nMatchCnt) continue; if (nTmp < nMatchCnt) { /* Terminate on compression count < previous match, this key is after the target */ # ifdef GVCST_SEARCH_BLK if ((bstar_rec_size(long_blk_id) == nRecLen) && !level0) /* Star key has size of SIZEOF(rec_hdr) + SIZEOF(block_id), make match = 0 */ nTargLen = 0; else # endif /* Data block, make match = current compression count */ nTargLen = nTmp; break; } # ifdef GVCST_SEARCH_TAIL alt_loop_entry: # endif /* Compression count == match count; Compare current target with current record */ pRec += SIZEOF(rec_hdr); do { if ((nFlg = *pCurrTarg - *pRec++) != 0) break; pCurrTarg++; } while (--nTargLen); if (0 < nFlg) nMatchCnt = (int)(pCurrTarg - pTargKeyBase); else { /* Key is after target*/ # ifdef GVCST_SEARCH_BLK if ((bstar_rec_size(long_blk_id) == nRecLen) && !level0) /* Star key has size of SIZEOF(rec_hdr) + SIZEOF(block_id), make match = 0 */ nTargLen = 0; else # endif nTargLen = (int)(pCurrTarg - pTargKeyBase); break; } } # ifdef GVCST_SEARCH_TAIL } # endif pStat->prev_rec.offset = (short)(pPrevRec - pBlkBase); pStat->prev_rec.match = (short)nMatchCnt; pStat->curr_rec.offset = (short)(pRecBase - pBlkBase); pStat->curr_rec.match = (short)nTargLen; # ifdef GVCST_SEARCH_EXPAND_PREVKEY if (NULL != (tmpPtr = prevKeyUnCmp)) /* Note: Assignment */ { /* gv_altkey->base[0] thru gv_altkey->base[prevKeyCmpLen] already holds the compressed portion of prevKey. * Copy over uncompressed portion of prevKey into gv_altkey->base and update gv_altkey->end before returning. */ pTop--; /* to check for double KEY_DELIMITER byte sequence without exceeding buffer allocation bounds */ do { if (tmpPtr >= pTop) { if (dollar_tlevel) TP_TRACE_HIST_MOD(pStat->blk_num, pStat->blk_target, tp_blkmod_gvcst_srch, cs_data, pStat->tn, ((blk_hdr_ptr_t)pBlkBase)->tn, pStat->level); else NONTP_TRACE_HIST_MOD(pStat, t_blkmod_gvcst_srch); return cdb_sc_blkmod; } /* It is now safe to do *tmpPtr and *++tmpPtr without worry about exceeding array bounds */ if ((KEY_DELIMITER == *tmpPtr++) && (KEY_DELIMITER == *tmpPtr)) break; } while (TRUE); tmpPtr++; /* go past second KEY_DELIMITER so that gets copied over to gv_altkey too */ prevKeyUnCmpLen = tmpPtr - prevKeyUnCmp; prevKeyStart += prevKeyCmpLen; if (prevKeyStart + prevKeyUnCmpLen > prevKeyTop) { if (dollar_tlevel) TP_TRACE_HIST_MOD(pStat->blk_num, pStat->blk_target, tp_blkmod_gvcst_srch, cs_data, pStat->tn, ((blk_hdr_ptr_t)pBlkBase)->tn, pStat->level); else NONTP_TRACE_HIST_MOD(pStat, t_blkmod_gvcst_srch); return cdb_sc_blkmod; } memcpy(prevKeyStart, prevKeyUnCmp, prevKeyUnCmpLen); gv_altkey->end = prevKeyCmpLen + prevKeyUnCmpLen - 1; /* remove 2nd KEY_DELIMITER from "end" calculation */ } else gv_altkey->end = 0; # endif return cdb_sc_normal; } fis-gtm-V7.0-005/sr_port/gvcst_bmp_mark_free.c0000755000032200000250000003705614342376331020224 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* gvcst_bmp_mark_free.c This marks all the blocks in kill set list to be marked free. Note ks must be already sorted */ #include "mdef.h" #include "gtm_string.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "memcoherency.h" #include "gdsblkops.h" /* for CHECK_AND_RESET_UPDATE_ARRAY macro */ /* Include prototypes */ #include "t_qread.h" #include "t_end.h" #include "t_retry.h" #include "t_begin.h" #include "t_write_map.h" #include "mm_read.h" #include "add_inter.h" #include "gvcst_bmp_mark_free.h" #include "t_busy2free.h" #include "t_abort.h" #ifdef UNIX #include "db_snapshot.h" #endif #include "muextr.h" #include "mupip_reorg.h" GBLREF char *update_array, *update_array_ptr; GBLREF cw_set_element cw_set[]; GBLREF unsigned char cw_set_depth; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF unsigned char rdfail_detail; GBLREF sgm_info *sgm_info_ptr; GBLREF boolean_t mu_reorg_process; GBLREF inctn_opcode_t inctn_opcode; GBLREF inctn_detail_t inctn_detail; /* holds detail to fill in to inctn jnl record */ GBLREF uint4 dollar_tlevel; #ifdef UNIX GBLREF unsigned int t_tries; GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; #endif GBLREF gd_region *gv_cur_region; error_def(ERR_GVKILLFAIL); error_def(ERR_IGNBMPMRKFREE); trans_num gvcst_bmp_mark_free(kill_set *ks) { block_id bit_map, next_bm, *updptr; blk_ident *blk, *blk_top, *nextblk; trans_num ctn, start_db_fmt_tn; unsigned int len; # if defined(UNIX) && defined(DEBUG) unsigned int lcl_t_tries; # endif int4 blk_prev_version; srch_hist alt_hist; trans_num ret_tn = 0; boolean_t visit_blks; srch_blk_status bmphist; cache_rec_ptr_t cr; enum db_ver ondsk_blkver; enum cdb_sc status; boolean_t mark_level_as_special; inctn_opcode_t saved_inctn_opcode; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; TREF(in_gvcst_bmp_mark_free) = TRUE; assert(inctn_bmp_mark_free_gtm == inctn_opcode || inctn_bmp_mark_free_mu_reorg == inctn_opcode); /* Note down the desired_db_format_tn before you start relying on cs_data->fully_upgraded. * If the db is fully_upgraded, take the optimal path that does not need to read each block being freed. * But in order to detect concurrent desired_db_format changes, note down the tn (when the last format change occurred) * before the fully_upgraded check and after having noted down the database current_tn. * If they are the same, then we are guaranteed no concurrent desired_db_format change occurred. * If they are not, then fall through to the non-optimal path where each to-be-killed block has to be visited. * The reason we need to visit every block in case desired_db_format changes is to take care of the case where * MUPIP REORG DOWNGRADE concurrently changes a block that we are about to free. */ start_db_fmt_tn = cs_data->desired_db_format_tn; visit_blks = (!cs_data->fully_upgraded); /* Local evaluation */ assert(!visit_blks || (visit_blks && dba_bg == cs_addrs->hdr->acc_meth)); /* must have blks_to_upgrd == 0 for non-BG */ assert(!dollar_tlevel); /* Should NOT be in TP now */ blk = &ks->blk[0]; blk_top = &ks->blk[ks->used]; if (!visit_blks) { /* Database has been completely upgraded. Free all blocks in one bitmap as part of one transaction. */ assert(cs_data->db_got_to_v5_once); /* assert all V4 fmt blocks (including RECYCLED) have space for V5 upgrade */ inctn_detail.blknum_struct.blknum = 0; /* to indicate no adjustment to "blks_to_upgrd" necessary */ /* If any of the mini transaction below restarts because of an online rollback, we don't want the application * refresh to happen (like $ZONLNRLBK++ or rts error(DBROLLEDBACK). This is because, although we are currently in * non-tp (dollar_tleve = 0), we could actually be in a TP transaction and have actually faked dollar_tlevel. In * such a case, we should NOT * be issuing a DBROLLEDBACK error as TP transactions are supposed to just restart in * case of an online rollback. So, set the global variable that gtm_onln_rlbk_clnup can check and skip doing the * application refresh, but will reset the clues. The next update will see the cycle mismatch and will accordingly * take the right action. */ for ( ; blk < blk_top; blk = nextblk) { if (0 != blk->flag) { nextblk = blk + 1; continue; } assert(0 < blk->block); assert((block_id)blk->block < cs_addrs->ti->total_blks); bit_map = ROUND_DOWN2((block_id)blk->block, BLKS_PER_LMAP); next_bm = bit_map + BLKS_PER_LMAP; CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ /* Scan for the next local bitmap */ updptr = (block_id *)update_array_ptr; for (nextblk = blk; (nextblk < blk_top) && (0 == nextblk->flag) && ((block_id)nextblk->block < next_bm); ++nextblk) { assert((block_id)nextblk->block - bit_map); *updptr++ = (block_id)nextblk->block - bit_map; } len = (unsigned int)((char *)nextblk - (char *)blk); update_array_ptr = (char *)updptr; alt_hist.h[0].blk_num = 0; /* need for calls to T_END for bitmaps */ alt_hist.h[0].blk_target = NULL; /* need to initialize for calls to T_END */ /* the following assumes SIZEOF(blk_ident) == SIZEOF(int) */ assert(SIZEOF(blk_ident) == SIZEOF(block_id)); *(block_id *)update_array_ptr = 0; t_begin(ERR_GVKILLFAIL, UPDTRNS_DB_UPDATED_MASK); for (;;) { ctn = cs_addrs->ti->curr_tn; /* Need a read fence before reading fields from cs_data as we are reading outside * of crit and relying on this value to detect desired db format state change. */ SHM_READ_MEMORY_BARRIER; if (start_db_fmt_tn != cs_data->desired_db_format_tn) { /* Concurrent db format change has occurred. Need to visit every block to be killed * to determine its block format. Fall through to the non-optimal path below */ ret_tn = 0; break; } /* if this is freeing a level-0 directory tree block, we need to transition the block to free * right away and write its before-image thereby enabling fast integ to avoid writing level-0 * block before-images altogether. It is possible the fast integ hasn't started at this stage, * so we cannot use FASTINTEG_IN_PROG in the if condition, but fast integ may already start later * at bg/mm update stage, so we always need to prepare cw_set element */ if ((MUSWP_FREE_BLK == TREF(in_mu_swap_root_state)) && blk->level) { /* blk->level was set as 1 for level-0 DIR tree block in mu_swap_root */ /* for mu_swap_root, only one block is freed during bmp_mark_free */ assert(1 == ks->used); ctn = cs_addrs->ti->curr_tn; alt_hist.h[0].cse = NULL; alt_hist.h[0].tn = ctn; alt_hist.h[0].blk_num = blk->block; alt_hist.h[1].blk_num = 0; /* this is to terminate history reading in t_end */ if (NULL == (alt_hist.h[0].buffaddr = t_qread(alt_hist.h[0].blk_num, (sm_int_ptr_t)&alt_hist.h[0].cycle, &alt_hist.h[0].cr))) { t_retry((enum cdb_sc)rdfail_detail); continue; } t_busy2free(&alt_hist.h[0]); /* The special level value will be used later in t_end to indicate * before_image of this block will be written to snapshot file */ cw_set[cw_set_depth-1].level = CSE_LEVEL_DRT_LVL0_FREE; mark_level_as_special = TRUE; } else mark_level_as_special = FALSE; bmphist.blk_num = bit_map; if (NULL == (bmphist.buffaddr = t_qread(bmphist.blk_num, (sm_int_ptr_t)&bmphist.cycle, &bmphist.cr))) { t_retry((enum cdb_sc)rdfail_detail); continue; } t_write_map(&bmphist, (uchar_ptr_t)update_array, ctn, -(int4)(nextblk - blk)); if (mark_level_as_special) { /* The special level value will be used later in gvcst_map_build to set the block to be * freed as free rather than recycled */ cw_set[cw_set_depth-1].level = CSE_LEVEL_DRT_LVL0_FREE; } DEBUG_ONLY(lcl_t_tries = t_tries); if ((trans_num)0 == (ret_tn = t_end(&alt_hist, NULL, TN_NOT_SPECIFIED))) { assert((CDB_STAGNATE == t_tries) || (lcl_t_tries == t_tries - 1)); status = LAST_RESTART_CODE; if ((cdb_sc_onln_rlbk1 == status) || (cdb_sc_onln_rlbk2 == status) || TREF(rlbk_during_redo_root)) { /* t_end restarted due to online rollback. Discard bitmap free-up and return control * to the application. But, before that reset only_reset_clues_if_onln_rlbk to FALSE */ TREF(in_gvcst_bmp_mark_free) = FALSE; send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(6) ERR_IGNBMPMRKFREE, 4, REG_LEN_STR(gv_cur_region), DB_LEN_STR(gv_cur_region)); t_abort(gv_cur_region, cs_addrs); return ret_tn; /* actually 0 */ } continue; } break; } if (0 == ret_tn) /* db format change occurred. Fall through to below for loop to visit each block */ { /* Abort any active transaction to get rid of lingering Non-TP artifacts */ saved_inctn_opcode = inctn_opcode; /* t_abort() may change inctn_opcode so save and restore it */ t_abort(gv_cur_region, cs_addrs); inctn_opcode = saved_inctn_opcode; break; } } } /* for all blocks in the kill_set */ for ( ; blk < blk_top; blk++) { /* Database has NOT been completely upgraded. Have to read every block that is going to be freed * and determine whether it has been upgraded or not. Every block will be freed as part of one * separate update to the bitmap. This will cause as many transactions as the blocks are being freed. * But this overhead will be present only as long as the database is not completely upgraded. * The reason why every block is updated separately is in order to accurately maintain the "blks_to_upgrd" * counter in the database file-header when the block-freeup phase (2nd phase) of the M-kill proceeds * concurrently with a MUPIP REORG UPGRADE/DOWNGRADE. If the bitmap is not updated for every block freeup * then MUPIP REORG UPGRADE/DOWNGRADE should also upgrade/downgrade all blocks in one bitmap as part of * one transaction (only then will we avoid double-decrement of "blks_to_upgrd" counter by the M-kill as * well as the MUPIP REORG UPGRADE/DOWNGRADE). That is a non-trivial task as potentially 512 blocks need * to be modified as part of one non-TP transaction which is unnecessarily making it heavyweight. Compared * to that, incurring a per-block bitmap update overhead in the M-kill is considered acceptable since this * will be the case only as long as we are in compatibility mode which should be hopefully not for long. */ if (0 != blk->flag) continue; assert(0 < blk->block); assert((block_id)blk->block < cs_addrs->ti->total_blks); assert(!IS_BITMAP_BLK(blk->block)); bit_map = ROUND_DOWN2((block_id)blk->block, BLKS_PER_LMAP); assert(dba_bg == cs_addrs->hdr->acc_meth); /* We need to check each block we are deleting to see if it is in the format of a previous version. * If it is, then "csd->blks_to_upgrd" needs to be correspondingly adjusted. */ alt_hist.h[0].level = 0; /* Initialize for loop below */ alt_hist.h[1].blk_num = 0; alt_hist.h[0].blk_target = NULL; /* need to initialize for calls to T_END */ CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ assert((block_id)blk->block - bit_map); assert(SIZEOF(block_id) == SIZEOF(blk_ident)); *((block_id *)update_array_ptr) = ((block_id)blk->block - bit_map); update_array_ptr += SIZEOF(blk_ident); /* the following assumes SIZEOF(blk_ident) == SIZEOF(int) */ assert(SIZEOF(blk_ident) == SIZEOF(block_id)); *(block_id *)update_array_ptr = 0; t_begin(ERR_GVKILLFAIL, UPDTRNS_DB_UPDATED_MASK); for (;;) { ctn = cs_addrs->ti->curr_tn; alt_hist.h[0].cse = NULL; alt_hist.h[0].tn = ctn; alt_hist.h[0].blk_num = blk->block; if (NULL == (alt_hist.h[0].buffaddr = t_qread(alt_hist.h[0].blk_num, (sm_int_ptr_t)&alt_hist.h[0].cycle, &alt_hist.h[0].cr))) { t_retry((enum cdb_sc)rdfail_detail); continue; } /* IF csd->db_got_to_v5_once is FALSE * a) mark the block as FREE (not RECYCLED to avoid confusing MUPIP REORG UPGRADE with a * block that was RECYCLED right at the time of MUPIP UPGRADE from a V4 to V5 version). * MUPIP REORG UPGRADE will mark all existing RECYCLED blocks as FREE. * b) need to write PBLK * ELSE * a) mark this block as RECYCLED * b) no need to write PBLK (it will be written when the block later gets reused). * ENDIF * * Create a cw-set-element with mode gds_t_busy2free that will cause a PBLK to be written in t_end * (the value csd->db_got_to_v5_once will be checked while holding crit) only in the IF case above. * At the same time bg_update will NOT be invoked for this cw-set-element so this block will not be * touched. But the corresponding bitmap block will be updated as part of the same transaction (see * t_write_map below) to mark this block as FREE or RECYCLED depending on whether csd->db_got_to_v5_once * is FALSE or TRUE (actual check done in gvcst_map_build and sec_shr_map_build). */ t_busy2free(&alt_hist.h[0]); cr = alt_hist.h[0].cr; ondsk_blkver = cr->ondsk_blkver; /* Get local copy in case cr->ondsk_blkver changes between * first and second part of the || */ assert((GDSV7 == ondsk_blkver) || (GDSV7m == ondsk_blkver) || (GDSV6 == ondsk_blkver)); if (GDSVCURR != ondsk_blkver) inctn_detail.blknum_struct.blknum = blk->block; else inctn_detail.blknum_struct.blknum = 0; /* i.e. no adjustment to "blks_to_upgrd" necessary */ bmphist.blk_num = bit_map; if (NULL == (bmphist.buffaddr = t_qread(bmphist.blk_num, (sm_int_ptr_t)&bmphist.cycle, &bmphist.cr))) { t_retry((enum cdb_sc)rdfail_detail); continue; } t_write_map(&bmphist, (uchar_ptr_t)update_array, ctn, -1); if ((MUSWP_FREE_BLK == TREF(in_mu_swap_root_state)) && blk->level) { assert(1 == ks->used); cw_set[cw_set_depth-1].level = CSE_LEVEL_DRT_LVL0_FREE; /* special level for gvcst_map_build */ cw_set[cw_set_depth-2].level = CSE_LEVEL_DRT_LVL0_FREE; /* special level for t_end */ /* Here we do not need to do BIT_SET_DIR_TREE because later the block will be always written to * snapshot file without checking whether it belongs to DIR or GV tree */ } UNIX_ONLY(DEBUG_ONLY(lcl_t_tries = t_tries)); if ((trans_num)0 == (ret_tn = t_end(&alt_hist, NULL, TN_NOT_SPECIFIED))) { assert((CDB_STAGNATE == t_tries) || (lcl_t_tries == t_tries - 1)); assert(0 < t_tries); DEBUG_ONLY(status = LAST_RESTART_CODE); /* get the recent restart code */ /* We don't expect online rollback related retries because we are here with the database NOT fully * upgraded. This means, online rollback cannot even start (it issues ORLBKNOV4BLK). Assert that. */ assert((cdb_sc_onln_rlbk1 != status) && (cdb_sc_onln_rlbk2 != status)); continue; } break; } } /* for all blocks in the kill_set */ TREF(in_gvcst_bmp_mark_free) = FALSE; return ret_tn; } fis-gtm-V7.0-005/sr_port/gvcst_bmp_mark_free.h0000755000032200000250000000317714342376331020226 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2002 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GVCST_BMP_MARK_FREE_INCLUDED #define GVCST_BMP_MARK_FREE_INCLUDED trans_num gvcst_bmp_mark_free(kill_set *ks); #define GVCST_BMP_MARK_FREE(ks, ret_tn, cur_inctn_opcode, new_inctn_opcode, inctn_opcode, cs_addrs) \ { /* inctn_opcode is set already by callers (TP/non-TP/reorg) and is not expected to change. save it \ * before modifying it. actually, the following save and reset of inctn_opcode (done before and after the \ * call to gvcst_bmp_mark_free()) needs to be done only if JNL_ENABLED(cs_addrs), but since it is not \ * easy to re-execute the save and reset of inctn_opcode in case t_end() detects a cdb_sc_jnlstatemod \ * retry code, we choose the easier approach of doing the save and reset unconditionally even though this \ * approach has an overhead of doing a few assignments even though inctn_opcode might not be used in t_end \ * (in case JNL_ENABLED is not TRUE at t_end() time). \ */ \ assert(inctn_opcode == cur_inctn_opcode); \ inctn_opcode = new_inctn_opcode; \ ret_tn = gvcst_bmp_mark_free(ks); \ inctn_opcode = cur_inctn_opcode; /* restore inctn_opcode */ \ } #endif /* GVCST_BMP_MARK_FREE_INCLUDED */ fis-gtm-V7.0-005/sr_port/gvcst_data.c0000755000032200000250000001370414342376331016336 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "cdb_sc.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "copy.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ #ifdef UNIX /* needed for frame_pointer in GVCST_ROOT_SEARCH_AND_PREP macro */ # include "repl_msg.h" # include "gtmsource.h" # include "rtnhdr.h" # include "stack_frame.h" # include "wbox_test_init.h" #endif #include "t_end.h" /* prototypes */ #include "t_retry.h" #include "t_begin.h" #include "gvcst_protos.h" /* for gvcst_rtsib,gvcst_search,gvcst_search_blk,gvcst_data prototype */ /* needed for spanning nodes */ #include "op.h" #include "op_tcommit.h" #include "error.h" #include "tp_frame.h" #include "tp_restart.h" #include "gtmimagename.h" LITREF mval literal_batch; GBLREF gv_key *gv_currkey, *gv_altkey; GBLREF gv_namehead *gv_target; GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF uint4 dollar_tlevel; GBLREF unsigned int t_tries; error_def(ERR_GVDATAFAIL); DEFINE_NSB_CONDITION_HANDLER(gvcst_data_ch) mint gvcst_data(void) { bool found, sn_tpwrapped; boolean_t est_first_pass; int oldend; mint val; int save_dollar_tlevel; DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); val = gvcst_data2(); INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_data, (gtm_uint64_t) 1); WBTEST_ONLY(WBTEST_QUERY_HANG, LONG_SLEEP(2); // we need this to let things see the incremented value ); # ifdef UNIX if (-1 != val) { assert(save_dollar_tlevel == dollar_tlevel); return val; } oldend = gv_currkey->end; if (!dollar_tlevel) { sn_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0); ESTABLISH_NORET(gvcst_data_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); WBTEST_ONLY(WBTEST_QUERY_HANG, LONG_SLEEP(2); ); val = gvcst_data2(); } else sn_tpwrapped = FALSE; if (-1 == val) { /* -1 implies node exists. Need to see if a proper descendant exists */ val = 1; /* 0 1 0 0 <-- append that to gv_currkey */ assert(oldend == gv_currkey->end); GVKEY_INCREMENT_QUERY(gv_currkey); found = gvcst_query(); /* want to save gv_altkey? */ if (found && (0 == memcmp(gv_currkey->base, gv_altkey->base, oldend))) val += 10; } if (sn_tpwrapped) { op_tcommit(); REVERT; /* remove our condition handler */ } RESTORE_CURRKEY(gv_currkey, oldend); assert(save_dollar_tlevel == dollar_tlevel); # endif return val; } mint gvcst_data2(void) { blk_hdr_ptr_t bp; boolean_t do_rtsib, is_dummy; enum cdb_sc status; mint val; rec_hdr_ptr_t rp; unsigned short match, rsiz; srch_blk_status *bh; srch_hist *rt_history; sm_uc_ptr_t b_top; int tmp_cmpc; int data_len, cur_val_offset, realval = 0; VMS_ONLY(assert((gv_target->root < cs_addrs->ti->total_blks) || dollar_tlevel)); T_BEGIN_READ_NONTP_OR_TP(ERR_GVDATAFAIL); assert(t_tries < CDB_STAGNATE || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ for (;;) { /* The following code is duplicated in gvcst_dataget. Any changes here might need to be reflected there as well */ rt_history = gv_target->alt_hist; rt_history->h[0].blk_num = 0; #if defined(DEBUG) && defined(UNIX) if (gtm_white_box_test_case_enabled && (WBTEST_ANTIFREEZE_GVDATAFAIL == gtm_white_box_test_case_number)) { t_retry(cdb_sc_blknumerr); continue; } #endif if (cdb_sc_normal != (status = gvcst_search(gv_currkey, NULL))) { t_retry(status); continue; } bh = gv_target->hist.h; bp = (blk_hdr_ptr_t)bh->buffaddr; rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); b_top = bh->buffaddr + bp->bsiz; match = bh->curr_rec.match; do_rtsib = FALSE; realval = 0; if (gv_currkey->end + 1 == match) { val = 1; GET_USHORT(rsiz, &rp->rsiz); # ifdef UNIX /* check for spanning node dummy value: a single zero byte */ cur_val_offset = SIZEOF(rec_hdr) + match - EVAL_CMPC((rec_hdr_ptr_t)rp); data_len = rsiz - cur_val_offset; is_dummy = IS_SN_DUMMY(data_len, (sm_uc_ptr_t)rp + cur_val_offset); if (is_dummy) { realval = -1; IF_SN_DISALLOWED_AND_NO_SPAN_IN_DB(realval = 0); /* resume since this is not a spanning node */ } # endif rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + rsiz); if ((sm_uc_ptr_t)rp > b_top) { t_retry(cdb_sc_rmisalign); continue; } else if ((sm_uc_ptr_t)rp == b_top) do_rtsib = TRUE; else if (EVAL_CMPC(rp) >= gv_currkey->end) val += 10; } else if (match >= gv_currkey->end) val = 10; else { val = 0; if (rp == (rec_hdr_ptr_t)b_top) do_rtsib = TRUE; } if (do_rtsib && (cdb_sc_endtree != (status = gvcst_rtsib(rt_history, 0)))) { if ((cdb_sc_normal != status) || (cdb_sc_normal != (status = gvcst_search_blk(gv_currkey, rt_history->h)))) { t_retry(status); continue; } if (rt_history->h[0].curr_rec.match >= gv_currkey->end) { assert(1 >= val); val += 10; } } if (!dollar_tlevel) { if ((trans_num)0 == t_end(&gv_target->hist, 0 == rt_history->h[0].blk_num ? NULL : rt_history, TN_NOT_SPECIFIED)) continue; } else { status = tp_hist(0 == rt_history->h[0].blk_num ? NULL : rt_history); if (cdb_sc_normal != status) { t_retry(status); continue; } } return (0 != realval) ? realval : val; } } fis-gtm-V7.0-005/sr_port/gvcst_dataget.c0000644000032200000250000002332314342376331017031 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2019 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 "stringpool.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "copy.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ #include "t_end.h" /* prototypes */ #include "t_retry.h" #include "t_begin.h" #include "gvcst_protos.h" /* for gvcst_rtsib,gvcst_search,gvcst_search_blk,gvcst_data prototype */ /* needed for spanning nodes */ #include "op.h" #include "op_tcommit.h" #include "error.h" #include "tp_frame.h" #include "tp_restart.h" #include "gtmimagename.h" LITREF mstr nsb_dummy; GBLREF gv_key *gv_currkey; GBLREF gv_namehead *gv_target; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF gd_region *gv_cur_region; GBLREF uint4 dollar_tlevel; GBLREF unsigned int t_tries; GBLREF uint4 t_err; error_def(ERR_GVDATAGETFAIL); error_def(ERR_GVKILLFAIL); error_def(ERR_GVPUTFAIL); enum cdb_sc gvcst_dataget(mint *dollar_data, mval *val) { boolean_t gotit, gotspan, gotpiece, check_rtsib; mint dollar_data_ctrl, dollar_data_piece, dollar_data_null, dg_info; mval val_ctrl, val_piece; int gblsize, i, total_len, oldend, tmp_numsubs; unsigned short numsubs; sm_uc_ptr_t sn_ptr; enum cdb_sc status; int save_dollar_tlevel; dg_info = *dollar_data; assert((DG_GETONLY == dg_info) || (DG_DATAGET == dg_info)); DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); assert(dollar_tlevel); if (cdb_sc_normal != (status = gvcst_dataget2(dollar_data, val, NULL))) { assert(save_dollar_tlevel == dollar_tlevel); return status; } # ifdef UNIX gotit = *dollar_data % 10; if (gotit && IS_SN_DUMMY(val->str.len, val->str.addr)) { /* We just found a dummy nodelet. Need to look for chunks of spanning node */ IF_SN_DISALLOWED_AND_NO_SPAN_IN_DB(return status); oldend = gv_currkey->end; APPEND_HIDDEN_SUB(gv_currkey); /* Search for control subscript */ dollar_data_ctrl = dg_info; if (cdb_sc_normal != (status = gvcst_dataget2(&dollar_data_ctrl, &val_ctrl, NULL))) { RESTORE_CURRKEY(gv_currkey, oldend); assert(save_dollar_tlevel == dollar_tlevel); return status; } gotspan = dollar_data_ctrl % 10; if (gotspan) { /* Spanning node indeed, as expected. Piece it together. Recompute dollar_data. */ if (val_ctrl.str.len == 6) { GET_NSBCTRL(val_ctrl.str.addr, numsubs, gblsize); } else { SSCANF(val_ctrl.str.addr, "%d,%d", &tmp_numsubs, &gblsize); numsubs = tmp_numsubs; } ENSURE_STP_FREE_SPACE(gblsize + cs_addrs->hdr->blk_size); /* give leeway.. think about more */ DBG_MARK_STRINGPOOL_UNUSABLE; sn_ptr = stringpool.free; total_len = 0; val->str.addr = (char *)sn_ptr; for (i = 0; i < numsubs; i++) { NEXT_HIDDEN_SUB(gv_currkey, i); /* We only need to do a rtsib on the last chunk. Only there can we check for descendants */ check_rtsib = (((i + 1) == numsubs) && (DG_DATAGET == dg_info)); dollar_data_piece = (check_rtsib) ? DG_GETSNDATA : DG_GETONLY; if (cdb_sc_normal != (status = gvcst_dataget2(&dollar_data_piece, &val_piece, sn_ptr))) { RESTORE_CURRKEY(gv_currkey, oldend); DBG_MARK_STRINGPOOL_USABLE; assert(save_dollar_tlevel == dollar_tlevel); return status; } gotpiece = dollar_data_piece % 10; if (gotpiece) { sn_ptr += val_piece.str.len; total_len += val_piece.str.len; } assert(total_len < (gblsize + cs_addrs->hdr->blk_size)); if (!gotpiece || (total_len > gblsize)) break; } if ((total_len != gblsize) || (i != numsubs)) { /* Fetched value either too small or too big compared to what control subscript says */ RESTORE_CURRKEY(gv_currkey, oldend); DBG_MARK_STRINGPOOL_USABLE; assert(save_dollar_tlevel == dollar_tlevel); return cdb_sc_spansize; } assert(val->mvtype == MV_STR); val->str.len = gblsize; stringpool.free += gblsize; DBG_MARK_STRINGPOOL_USABLE; if (check_rtsib) { *dollar_data = dollar_data_piece; if ((11 != dollar_data_piece) && cs_data->std_null_coll) { /* Check for a null-subscripted descendant. Append null sub to gv_currkey and check $data */ RESTORE_CURRKEY(gv_currkey, oldend); GVKEY_INCREMENT_QUERY(gv_currkey); dollar_data_null = DG_DATAONLY; gvcst_dataget2(&dollar_data_null, &val_piece, NULL); if (dollar_data_null) *dollar_data = 11; /* Child found */ RESTORE_CURRKEY(gv_currkey, oldend); } } } RESTORE_CURRKEY(gv_currkey, oldend); } if (DG_GETONLY == dg_info) *dollar_data = (mint)gotit; /* Just return 1 if it exists and 0 if it doesn't */ assert(save_dollar_tlevel == dollar_tlevel); # endif return status; } /* This function is the equivalent of invoking gvcst_data & gvcst_get at the same time. * One crucial difference is that this function does NOT handle restarts by automatically invoking t_retry. * Instead, it returns the restart code to the caller so that it can handle the restart accordingly. * This is important in the case of triggers because we do NOT want to call t_retry in case of a implicit tstart * wrapped gvcst_put or gvcst_kill trigger-invoking update transaction. Additionally, this function assumes * that it is called always inside of TP (i.e. dollar_tlevel is non-zero). */ enum cdb_sc gvcst_dataget2(mint *dollar_data, mval *val, unsigned char *sn_ptr) { blk_hdr_ptr_t bp; boolean_t do_rtsib; enum cdb_sc status; mint dlr_data, dg2_info; rec_hdr_ptr_t rp; unsigned short match, rsiz; srch_blk_status *bh; srch_hist *rt_history; sm_uc_ptr_t b_top; int key_size, data_len, delta; uint4 save_t_err; /* The following code is lifted from gvcst_data. Any changes here might need to be reflected there as well */ assert(dollar_tlevel); assert((CDB_STAGNATE > t_tries) || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ save_t_err = t_err; assert((ERR_GVKILLFAIL == save_t_err) || (ERR_GVPUTFAIL == save_t_err)); /* called only from gvcst_kill and gvcst_put */ t_err = ERR_GVDATAGETFAIL; /* switch t_err to reflect dataget sub-operation (under the KILL operation) */ /* In case of a failure return, it is ok to return with t_err set to ERR_GVDATAGETFAIL as that gives a better * picture of exactly where in the transaction the failure occurred. */ dg2_info = *dollar_data; assert((DG_GETONLY == dg2_info) || (DG_DATAGET == dg2_info) || (DG_DATAONLY == dg2_info) || (DG_GETSNDATA == dg2_info)); delta = (DG_GETSNDATA == dg2_info) ? 4 : 0; /* next key doesn't need to match hidden subscript */ rt_history = gv_target->alt_hist; rt_history->h[0].blk_num = 0; if (cdb_sc_normal != (status = gvcst_search(gv_currkey, NULL))) return status; bh = gv_target->hist.h; bp = (blk_hdr_ptr_t)bh->buffaddr; rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); b_top = bh->buffaddr + bp->bsiz; match = bh->curr_rec.match; key_size = gv_currkey->end + 1; do_rtsib = FALSE; /* Even if key does not exist, return null string in "val". Caller can use dollar_data to distinguish * whether the key is undefined or defined and set to the null string. */ val->mvtype = MV_STR; val->str.len = 0; if (key_size == match) { dlr_data = 1; /* the following code is lifted from gvcst_get. any changes here might need to be reflected there as well */ GET_USHORT(rsiz, &rp->rsiz); data_len = rsiz + EVAL_CMPC(rp) - SIZEOF(rec_hdr) - key_size; if ((0 > data_len) || ((sm_uc_ptr_t)rp + rsiz > b_top)) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_rmisalign; return status; } else if (DG_DATAONLY != dg2_info) { val->str.len = data_len; if (!sn_ptr) { ENSURE_STP_FREE_SPACE(data_len); memcpy(stringpool.free, (sm_uc_ptr_t)rp + rsiz - data_len, data_len); val->str.addr = (char *)stringpool.free; stringpool.free += data_len; } else { memcpy(sn_ptr, (sm_uc_ptr_t)rp + rsiz - data_len, data_len); val->str.addr = (char *)sn_ptr; } } /* --------------------- end code lifted from gvcst_get ---------------------------- */ rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + rsiz); if ((sm_uc_ptr_t)rp > b_top) { status = cdb_sc_rmisalign; return status; } else if ((sm_uc_ptr_t)rp == b_top) do_rtsib = TRUE; else if (EVAL_CMPC(rp) + delta >= gv_currkey->end) dlr_data += 10; } else if (match + delta >= gv_currkey->end) dlr_data = 10; else { dlr_data = 0; if (rp == (rec_hdr_ptr_t)b_top) do_rtsib = TRUE; } if ((DG_GETONLY != dg2_info) && do_rtsib && (cdb_sc_endtree != (status = gvcst_rtsib(rt_history, 0)))) { /* only do rtsib and search_blk if full data information is desired */ if ((cdb_sc_normal != status) || (cdb_sc_normal != (status = gvcst_search_blk(gv_currkey, rt_history->h)))) return status; if (rt_history->h[0].curr_rec.match + delta >= gv_currkey->end) { assert(1 >= dlr_data); dlr_data += 10; } } status = tp_hist(0 == rt_history->h[0].blk_num ? NULL : rt_history); if (cdb_sc_normal != status) return status; *dollar_data = (DG_GETONLY != dg2_info) ? dlr_data : (dlr_data % 10); t_err = save_t_err; /* restore t_err to what it was at function entry */ return status; } fis-gtm-V7.0-005/sr_port/gvcst_delete_blk.c0000755000032200000250000001453414342376331017521 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdscc.h" #include "copy.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "hashtab_int8.h" #include "tp.h" #include "gvcst_blk_build.h" #include "gvcst_delete_blk.h" GBLREF kill_set *kill_set_tail; GBLREF sgm_info *sgm_info_ptr; GBLREF uint4 dollar_tlevel; GBLREF gv_namehead *gv_target; GBLREF boolean_t horiz_growth; GBLREF sgmnt_addrs *cs_addrs; GBLREF unsigned int t_tries; #ifdef VMS GBLREF boolean_t tp_has_kill_t_cse; /* cse->mode of kill_t_write or kill_t_create got created in this transaction */ #endif /* This is not the ideal place to put this */ static inline cw_set_element *get_new_free_cw_set_element(buddy_list *list) /* 4SCA allocator specific to cw_set_element */ { cw_set_element *p; assert(0 < list->elemSize); assert(sizeof(cw_set_element) <= (size_t)(list->elemSize)); p = (cw_set_element *) get_new_free_element(list); assert(p); return p; } void gvcst_delete_blk(block_id blk, int level, boolean_t committed) { cw_set_element *cse, *old_cse; kill_set *ks; off_chain chain; srch_blk_status *tp_srch_status; uint4 iter; ht_ent_int8 *tabent; DEBUG_ONLY( boolean_t block_already_in_hist = FALSE; ) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; horiz_growth = FALSE; if (!dollar_tlevel) ks = kill_set_tail; else { PUT_BLK_ID(&chain, blk); tp_srch_status = NULL; if (chain.flag == 1) { assert((SIZEOF(int) * 8) >= CW_INDEX_MAX_BITS); tp_get_cw(sgm_info_ptr->first_cw_set, (int)chain.cw_index, &cse); } else { if (NULL != (tabent = lookup_hashtab_int8(sgm_info_ptr->blks_in_use, (ublock_id *)&blk))) tp_srch_status = (srch_blk_status *)tabent->value; cse = tp_srch_status ? tp_srch_status->cse : NULL; } if (cse) { if (!committed) { assert(dollar_tlevel >= cse->t_level); if (NULL != cse->high_tlevel) { /* this is possible only if this block is already part of either of the tree paths * in gv_target->hist or alt_hist (see gvcst_kill.c) and got a newer cse created as * part of a gvcst_kill_blk on this block in gvcst_kill.c a little before the call * to gvcst_kill_blk of a parent block which in turn decided to delete this block * as part of its records that are getting removed. this is a guaranteed restartable * situation. since gvcst_delete_blk does not return any status, proceed with the * newer cse and return. the restart will be later detected by tp_hist. */ cse = cse->high_tlevel; DEBUG_ONLY(TREF(donot_commit) |= DONOTCOMMIT_GVCST_DELETE_BLK_CSE_TLEVEL;) DEBUG_ONLY(block_already_in_hist = TRUE;) } assert(!cse->high_tlevel); if (cse->t_level != dollar_tlevel) { /* this part of the code is almost similar to that in t_write(), * any changes in one should be reflected in the other */ horiz_growth = TRUE; old_cse = cse; assert(SIZEOF(*cse) <= sgm_info_ptr->tlvl_cw_set_list->elemSize); cse = (cw_set_element *)get_new_free_cw_set_element(sgm_info_ptr->tlvl_cw_set_list); assert(NULL != cse); assert(SIZEOF(*cse) >= SIZEOF(cw_set_element)); /* trying tautology 4CSA */ memcpy(cse, old_cse, sizeof(*cse)); cse->low_tlevel = old_cse; cse->high_tlevel = NULL; old_cse->high_tlevel = cse; cse->t_level = dollar_tlevel; assert(2 == (SIZEOF(cse->undo_offset) / SIZEOF(cse->undo_offset[0]))); assert(2 == (SIZEOF(cse->undo_next_off) / SIZEOF(cse->undo_next_off[0]))); for (iter = 0; iter < 2; iter++) cse->undo_next_off[iter] = cse->undo_offset[iter] = 0; if (old_cse->done) { assert(NULL != old_cse->new_buff); cse->new_buff = (unsigned char *)get_new_free_element(sgm_info_ptr->new_buff_list); memcpy(cse->new_buff, old_cse->new_buff, ((blk_hdr_ptr_t)old_cse->new_buff)->bsiz); } else cse->new_buff = NULL; assert(!block_already_in_hist); /* tp_hist (called from gvcst_kill) updates "->cse" fields for all blocks that are * part of the left or right histories of the M-kill. But this block is not one of * those. Hence tp_srch_status->cse has to be updated here explicitly. */ if (tp_srch_status) tp_srch_status->cse = (void *)cse; } switch (cse->mode) { case gds_t_create: cse->mode = kill_t_create; VMS_ONLY(tp_has_kill_t_cse = TRUE;) if (level == 0) return; break; case gds_t_write: cse->mode = kill_t_write; VMS_ONLY(tp_has_kill_t_cse = TRUE;) break; default: ; } } else { switch(cse->mode) { case kill_t_create: VMS_ONLY(assert(tp_has_kill_t_cse)); if (level == 0) return; break; default: if (chain.flag) { chain.flag = 0; blk = cse->blk; } break; } } } ks = sgm_info_ptr->kill_set_tail; if (NULL == ks) /* Allocate first kill set to sgm_info_ptr block */ { ks = sgm_info_ptr->kill_set_tail = sgm_info_ptr->kill_set_head = (kill_set *)malloc(SIZEOF(kill_set)); assert(NULL == sgm_info_ptr->kip_csa); ks->used = 0; ks->next_kill_set = NULL; } } while (ks->used >= BLKS_IN_KILL_SET) { if (ks->next_kill_set == NULL) { ks->next_kill_set = (kill_set *)malloc(SIZEOF(kill_set)); ks->next_kill_set->used = 0; ks->next_kill_set->next_kill_set = NULL; } ks = kill_set_tail = ks->next_kill_set; } ks->blk[ks->used].level = (level) ? 1 : 0; if (!dollar_tlevel || !chain.flag) { assert((CDB_STAGNATE > t_tries) || (blk < cs_addrs->ti->total_blks)); ks->blk[ks->used].block = blk; ks->blk[ks->used].flag = 0; } else { ks->blk[ks->used].block = chain.cw_index; ks->blk[ks->used].flag = chain.flag; } ++ks->used; assert(ks->used <= BLKS_IN_KILL_SET); } fis-gtm-V7.0-005/sr_port/gvcst_delete_blk.h0000755000032200000250000000116014342376331017515 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GVCST_DELETE_BLK_INCLUDED #define GVCST_DELETE_BLK_INCLUDED void gvcst_delete_blk(block_id blk, int level, boolean_t committed); #endif /* GVCST_DELETE_BLK_INCLUDED */ fis-gtm-V7.0-005/sr_port/gvcst_expand_any_key.c0000755000032200000250000001320614342376331020420 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ /* ------------------------------------------------------------------------- gvcst_expand_any_key.c Expands key in a block. It can expands a *-key too. Given block base and record top of the key to be expanded, this will expand the key. Result is placed in expanded_key. Can expand *=key too. ------------------------------------------------------------------------- */ #include "mdef.h" #include "gtm_string.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "copy.h" #include "mu_reorg.h" #include "filestruct.h" /* for struct RAB type recognition by C compiler before prototype usage in muextr.h */ #include "muextr.h" #include "tp.h" /* Include prototypes */ #include "t_qread.h" #include "mupip_reorg.h" GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF gv_namehead *gv_target; GBLREF unsigned int t_tries; GBLREF unsigned char rdfail_detail; /******************************************************************************************* Input Parameter: blk_base = Block's base which has the key rec_top = record top of the record which will be expanded Output Parameter: expanded_key = expanded key rec_size = last record size which has the key keylen = key size keycmpc = key compression count hist_ptr = history of blocks read, while expanding a *-key History excludes the working block from which key is expanded and includes the blocks read below the current block to expand a *-key NOTE: hist_ptr.depth will be unchanged Return: cdb_sc_normal on success failure code on concurrency failure *******************************************************************************************/ enum cdb_sc gvcst_expand_any_key (srch_blk_status *blk_stat, sm_uc_ptr_t rec_top, sm_uc_ptr_t expanded_key, int *rec_size, int *keylen, int *keycmpc, srch_hist *hist_ptr) { block_id tblk_num; boolean_t long_blk_id; enum cdb_sc status; int cur_level; int star_keycmpc, star_keylen, star_rec_size; int tblk_size; int4 blk_id_sz; sm_uc_ptr_t blk_base, curptr, rPtr1, rPtr2; unsigned char expanded_star_key[MAX_KEY_SZ]; unsigned short temp_ushort; blk_base = blk_stat->buffaddr; long_blk_id = IS_64_BLK_ID(blk_base); blk_id_sz = SIZEOF_BLK_ID(long_blk_id); cur_level = blk_stat->level; curptr = blk_base + SIZEOF(blk_hdr); *rec_size = *keycmpc = *keylen = 0; while (curptr < rec_top) { GET_RSIZ(*rec_size, curptr); if ((0 == cur_level) || (bstar_rec_size(long_blk_id) != *rec_size)) { READ_RECORD(status, rec_size, keycmpc, keylen, expanded_key, cur_level, blk_stat, curptr); if (cdb_sc_normal != status) { assert(t_tries < CDB_STAGNATE); return status; } else { curptr += *rec_size; if (curptr >= rec_top) break; } } else /* a star record in index block */ { if ((curptr + *rec_size != rec_top) || (NULL == hist_ptr)) { assert(t_tries < CDB_STAGNATE); return cdb_sc_rmisalign; } while (0 != cur_level) { tblk_size = ((blk_hdr_ptr_t)blk_base)->bsiz; READ_BLK_ID(long_blk_id, &tblk_num, blk_base + tblk_size - blk_id_sz); if (0 == tblk_num || cs_data->trans_hist.total_blks - 1 < tblk_num) { assert(t_tries < CDB_STAGNATE); return cdb_sc_badlvl; } cur_level--; hist_ptr->h[cur_level].tn = cs_addrs->ti->curr_tn; if (!(blk_base = t_qread(tblk_num, (sm_int_ptr_t)(&(hist_ptr->h[cur_level].cycle)), &(hist_ptr->h[cur_level].cr) ))) { assert(t_tries < CDB_STAGNATE); return (enum cdb_sc)rdfail_detail; } /* Recalculating the 2 below values when a new block is read accounts for a DB with a mix of * V7 and V6 blocks which will be necessary for the V6->V7 upgrade */ long_blk_id = IS_64_BLK_ID(blk_base); blk_id_sz = SIZEOF_BLK_ID(long_blk_id); if (((blk_hdr_ptr_t)blk_base)->levl != cur_level) { assert(t_tries < CDB_STAGNATE); return cdb_sc_badlvl; } hist_ptr->h[cur_level].buffaddr = blk_base; hist_ptr->h[cur_level].blk_num = tblk_num; hist_ptr->h[cur_level].prev_rec.match = 0; hist_ptr->h[cur_level].prev_rec.offset = 0; hist_ptr->h[cur_level].curr_rec.match = 0; hist_ptr->h[cur_level].curr_rec.offset = 0; } tblk_size = ((blk_hdr_ptr_t)blk_base)->bsiz; /* expand *-key from right most leaf level block of the * sub-tree, of which, the original block is root */ if (cdb_sc_normal != (status = (gvcst_expand_any_key(&hist_ptr->h[cur_level], blk_base + tblk_size, expanded_star_key, &star_rec_size, &star_keylen, &star_keycmpc, hist_ptr)))) return status; if (*keylen + *keycmpc) /* Previous key exists */ { GET_CMPC(*keycmpc, expanded_key, expanded_star_key); } memcpy(expanded_key, expanded_star_key, star_keylen + star_keycmpc); *keylen = star_keylen + star_keycmpc - *keycmpc; *rec_size = *keylen + *keycmpc + bstar_rec_size(long_blk_id); return cdb_sc_normal; } /* end else if *-record */ }/* end of "while" loop */ if (curptr == rec_top) { return cdb_sc_normal; } else { assert(t_tries < CDB_STAGNATE); return cdb_sc_rmisalign; } } fis-gtm-V7.0-005/sr_port/gvcst_expand_free_subtree.c0000644000032200000250000001652414342376331021436 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "cdb_sc.h" /* atleast for cdb_sc_* codes */ #include "copy.h" /* atleast for the GET_USHORT macros */ #include "gdsroot.h" /* atleast for gds_file_id used by sgmnt_data in gdsfhead.h */ #include "gdskill.h" /* atleast for the kill_set and blk_ident structures */ #include "gdsblk.h" /* atleast for the blk_hdr and rec_hdr structures */ #include "gtm_facility.h" /* atleast for gdsfhead.h */ #include "fileinfo.h" /* atleast for gdsfhead.h */ #include "gdsbt.h" /* atleast for gdsfhead.h */ #include "gdsfhead.h" /* atleast for cs_addrs, cs_data etc. */ #include "filestruct.h" /* atleast for the FILE_INFO macro */ #include "gdscc.h" /* atleast for cw_set_element in tp.h */ #include "jnl.h" /* atleast for tp.h */ #include "buddy_list.h" /* atleast for tp.h */ #include "tp.h" /* atleast for off_chain */ #include "t_qread.h" #include "gvcst_bmp_mark_free.h" #include "gvcst_delete_blk.h" #include "gvcst_kill_sort.h" #include "gvcst_expand_free_subtree.h" #include "rc_cpt_ops.h" #include "wcs_phase2_commit_wait.h" #include "min_max.h" GBLREF gd_region *gv_cur_region; GBLREF inctn_opcode_t inctn_opcode; GBLREF sgm_info *sgm_info_ptr; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF uint4 bml_save_dollar_tlevel; GBLREF uint4 dollar_tlevel; GBLREF uint4 update_trans; GBLREF unsigned char rdfail_detail; error_def(ERR_GVKILLFAIL); error_def(ERR_IGNBMPMRKFREE); void gvcst_expand_free_subtree(kill_set *ks_head) { blk_hdr_ptr_t bp; blk_ident *ksb; block_id blk, temp_blk; boolean_t flush_cache = FALSE, was_crit, long_blk_id; bt_rec_ptr_t bt; cache_rec_ptr_t cr; inctn_opcode_t save_inctn_opcode; int4 cnt, cycle, kill_error, blk_id_sz; kill_set *ks; off_chain chain; rec_hdr_ptr_t rp, rp1, rtop; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; trans_num ret_tn; uint4 bsiz, level, save_update_trans; unsigned char temp_buff[MAX_DB_BLK_SIZE]; unsigned short temp_ushort; # ifdef DEBUG uint4 save_dollar_tlevel; # endif csa = cs_addrs; csd = cs_data; /* If ever the following assert is removed, "flush_cache" shouldn't be set to FALSE unconditionally as it is now */ assert(!csd->dsid); /* see related comment in gvcst_kill before the call to this routine */ assert(MAX_DB_BLK_SIZE >= cs_data->blk_size); for (ks = ks_head; NULL != ks; ks = ks->next_kill_set) { for (cnt = 0; cnt < ks->used; ++cnt) { ksb = &ks->blk[cnt]; if (0 != ksb->level) { if (!(was_crit = csa->now_crit)) /* needed so t_qread does not return NULL below */ grab_crit_encr_cycle_sync(gv_cur_region, WS_13); # ifdef UNIX if (csa->onln_rlbk_cycle != csa->nl->onln_rlbk_cycle) { /* Concurrent online rollback. We don't want to continue with rest of the logic to add more * blocks to the kill-set and do the gvcst_bmp_mark_free. Return to the caller. Since we * haven't sync'ed the cycles, the next tranasction commit will detect the online rollback * and the restart logic will handle it appropriately. */ rel_crit(gv_cur_region); send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_IGNBMPMRKFREE, 4, REG_LEN_STR(gv_cur_region), DB_LEN_STR(gv_cur_region)); return; } # endif if (dollar_tlevel && ksb->flag) { chain.flag = 1; chain.next_off = 0; assert(ksb->block < (1LL << CW_INDEX_MAX_BITS)); chain.cw_index = ksb->block; assert(SIZEOF(chain) == SIZEOF(blk)); blk = *(block_id *)&chain; } else blk = ksb->block; if (!(bp = (blk_hdr_ptr_t)t_qread(blk, (sm_int_ptr_t)&cycle, &cr))) { /* This should have worked because t_qread was done in crit */ RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_GVKILLFAIL, 2, 1, &rdfail_detail); } long_blk_id = IS_64_BLK_ID(bp); blk_id_sz = SIZEOF_BLK_ID(long_blk_id); if (NULL != cr) { /* It is possible that t_qread returned a buffer from first_tp_srch_status. * In that case, t_qread does not wait for cr->in_tend to be zero since * there is no need to wait as long as all this is done inside of the TP * transaction. But the gvcst_expand_free_subtree logic is special in that it * is done AFTER the TP transaction is committed but with dollar_tlevel still * set to non-zero. So it is possible that cr->in_tend is non-zero in this case. * Hence we need to check if cr->in_tend is non-zero and if so wait for commit * to complete before scanning the block for child-block #s to free. */ if (dollar_tlevel && cr->in_tend) wcs_phase2_commit_wait(csa, cr); assert(!cr->twin || cr->bt_index); assert((NULL == (bt = bt_get(blk))) || (CR_NOTVALID == bt->cache_index) || (cr == (cache_rec_ptr_t)GDS_REL2ABS(bt->cache_index)) && (0 == cr->in_tend)); } assert(MAX_DB_BLK_SIZE >= bp->bsiz); bsiz = MIN(bp->bsiz, MAX_DB_BLK_SIZE); /* avoid buffer overflows */ memcpy(temp_buff, bp, bsiz); if (!was_crit) rel_crit(gv_cur_region); for (rp = (rec_hdr_ptr_t)(temp_buff + SIZEOF(blk_hdr)), rtop = (rec_hdr_ptr_t)(temp_buff + bsiz); rp < rtop; rp = rp1) { GET_USHORT(temp_ushort, &rp->rsiz); rp1 = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + temp_ushort); if ((sm_uc_ptr_t)rp1 < ((sm_uc_ptr_t)(rp + 1) + blk_id_sz)) { /* This should have worked because a local copy was made while crit */ assert(FALSE); kill_error = cdb_sc_rmisalign; RTS_ERROR_CSA_ABT(csa, VARLSTCNT(4) ERR_GVKILLFAIL, 2, 1, &kill_error); } READ_BLK_ID(long_blk_id, &temp_blk, (sm_uc_ptr_t)rp1 - blk_id_sz); if (dollar_tlevel) { assert(sgm_info_ptr->tp_csa == cs_addrs); chain = *(off_chain *)&temp_blk; assert((SIZEOF(int) * 8) >= CW_INDEX_MAX_BITS); assertpro(!((1 == chain.flag) && ((int)chain.cw_index >= sgm_info_ptr->cw_set_depth))); assert(chain.flag || (temp_blk < csa->ti->total_blks)); } level = ((blk_hdr_ptr_t)temp_buff)->levl; gvcst_delete_blk(temp_blk, level - 1, TRUE); if ((1 == level) && !dollar_tlevel && cs_data->dsid && !flush_cache) rc_cpt_entry(temp_blk); /* Invalidate single block */ } ksb->level = 0; } else { if (!dollar_tlevel && cs_data->dsid && !flush_cache) rc_cpt_entry(ksb->block); } } gvcst_kill_sort(ks); assert(!bml_save_dollar_tlevel); DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); bml_save_dollar_tlevel = dollar_tlevel; /* Resetting and restoring of update_trans is necessary to avoid blowing an assert in t_begin that it is 0. */ save_update_trans = update_trans; assert(1 >= dollar_tlevel); dollar_tlevel = 0; /* temporarily for gvcst_bmp_mark_free */ update_trans = 0; GVCST_BMP_MARK_FREE(ks, ret_tn, inctn_invalid_op, inctn_bmp_mark_free_gtm, inctn_opcode, csa) update_trans = save_update_trans; dollar_tlevel = bml_save_dollar_tlevel; assert(dollar_tlevel == save_dollar_tlevel); bml_save_dollar_tlevel = 0; } } fis-gtm-V7.0-005/sr_port/gvcst_expand_free_subtree.h0000755000032200000250000000117114342376331021436 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GVCST_EXPAND_FREE_SUBTREE_INCLUDED #define GVCST_EXPAND_FREE_SUBTREE_INCLUDED void gvcst_expand_free_subtree(kill_set *ks_head); #endif /* GVCST_EXPAND_FREE_SUBTREE_INCLUDED */ fis-gtm-V7.0-005/sr_port/gvcst_expand_key.c0000755000032200000250000001350514342376331017553 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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 "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "cdb_sc.h" #include "copy.h" #include "min_max.h" #include "gvcst_expand_key.h" GBLREF gd_region *gv_cur_region; GBLREF unsigned int t_tries; GBLREF uint4 dollar_tlevel; /* Note: A lot of the code below is similar to that in gvcst_blk_search.h. * Any changes there need to be incorporated here and vice-versa. */ enum cdb_sc gvcst_expand_key(srch_blk_status *pStat, int4 rec_top, gv_key *key) { int expKeyCmpLen; /* length of compressed portion of expKey stored in key->base */ int expKeyUnCmpLen;/* Length of uncompressed portion of expKey */ int nTmp; int r_offset; rec_hdr_ptr_t rp, rtop; blk_hdr_ptr_t bp; sm_uc_ptr_t expKeyUnCmp; /* pointer to beginning of uncompressed portion of expKey */ sm_uc_ptr_t pTop; unsigned char *expKeyStart; /* pointer to &key->base[0] */ unsigned char *expKeyTop; /* pointer to allocated end of input "key" */ unsigned char *tmpPtr; unsigned short temp_ushort; assert(SIZEOF(rec_hdr) <= SIZEOF(blk_hdr)); bp = (blk_hdr_ptr_t)pStat->buffaddr; rp = (rec_hdr_ptr_t)bp; rtop = (rec_hdr_ptr_t)((sm_uc_ptr_t)bp + rec_top); expKeyCmpLen = 0; expKeyUnCmp = NULL; expKeyStart = &key->base[0]; expKeyTop = &key->base[key->top]; for (r_offset = SIZEOF(blk_hdr); ; GET_USHORT(temp_ushort, &rp->rsiz), r_offset = temp_ushort) { /* WARNING: Assumes that SIZEOF(rec_hdr) <= SIZEOF(blk_hdr) */ if (r_offset < SIZEOF(rec_hdr)) { assert(CDB_STAGNATE > t_tries); return cdb_sc_r2small; } rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + r_offset); if (rp > rtop) { assert(CDB_STAGNATE > t_tries); return cdb_sc_rmisalign; } nTmp = EVAL_CMPC(rp); if (nTmp > expKeyCmpLen) { if (((expKeyStart + nTmp) >= expKeyTop) || (NULL == expKeyUnCmp)) { if (dollar_tlevel) TP_TRACE_HIST_MOD(pStat->blk_num, pStat->blk_target, tp_blkmod_gvcst_srch, cs_data, pStat->tn, ((blk_hdr_ptr_t)bp)->tn, pStat->level); else NONTP_TRACE_HIST_MOD(pStat, t_blkmod_gvcst_expand_key); return cdb_sc_blkmod; } assert(NULL != expKeyUnCmp); memcpy(expKeyStart + expKeyCmpLen, expKeyUnCmp, nTmp - expKeyCmpLen); } expKeyCmpLen = nTmp; expKeyUnCmp = (sm_uc_ptr_t)rp + SIZEOF(rec_hdr); if (rp == rtop) break; } assert(NULL != expKeyUnCmp); tmpPtr = expKeyUnCmp; /* gv_altkey->base[0] thru gv_altkey->base[expKeyCmpLen] already holds the compressed portion of expKey. * Copy over uncompressed portion of expKey into gv_altkey->base and update gv_altkey->end before returning. */ pTop = (sm_uc_ptr_t)bp + MIN(bp->bsiz, cs_data->blk_size) - 1; /* -1 to check for double KEY_DELIMITER byte sequence * without exceeding buffer allocation bounds. */ do { if (tmpPtr >= pTop) { if (dollar_tlevel) TP_TRACE_HIST_MOD(pStat->blk_num, pStat->blk_target, tp_blkmod_gvcst_srch, cs_data, pStat->tn, ((blk_hdr_ptr_t)bp)->tn, pStat->level); else NONTP_TRACE_HIST_MOD(pStat, t_blkmod_gvcst_expand_key); return cdb_sc_blkmod; } /* It is now safe to do *tmpPtr and *++tmpPtr without worry about exceeding block bounds */ if ((KEY_DELIMITER == *tmpPtr++) && (KEY_DELIMITER == *tmpPtr)) break; } while (TRUE); tmpPtr++; /* go past second KEY_DELIMITER so that gets copied over to gv_altkey too */ expKeyUnCmpLen = tmpPtr - expKeyUnCmp; tmpPtr = expKeyStart + expKeyCmpLen; if (tmpPtr + expKeyUnCmpLen > expKeyTop) { if (dollar_tlevel) TP_TRACE_HIST_MOD(pStat->blk_num, pStat->blk_target, tp_blkmod_gvcst_srch, cs_data, pStat->tn, ((blk_hdr_ptr_t)bp)->tn, pStat->level); else NONTP_TRACE_HIST_MOD(pStat, t_blkmod_gvcst_expand_key); return cdb_sc_blkmod; } memcpy(tmpPtr, expKeyUnCmp, expKeyUnCmpLen); if (KEY_DELIMITER == *expKeyStart) { /* A valid key wouldn't start with a '\0' character. So the block must have been concurrently modified. */ return cdb_sc_mkblk; } expKeyUnCmpLen--; /* remove 2nd KEY_DELIMITER from "end" calculation */ key->end = expKeyCmpLen + expKeyUnCmpLen; /* key->prev is not initialized. Caller should not rely on this. */ /* Due to concurrency issues, it is possible "key" is not a well-formed key (e.g. it might have two successive * KEY_DELIMITER bytes in the middle of the key). So we cannot add a DBG_CHECK_GVKEY_VALID(key) here. * But we expect later validation to catch this and restart the transaction (without affecting db integrity). * So we dont worry about such keys here. */ assert(2 <= key->end); /* Ensure the key is double-null-byte terminated even if this is a restartable situation. * Callers like gvcst_put rely on this (in asserts). */ tmpPtr += expKeyUnCmpLen; *tmpPtr-- = KEY_DELIMITER; *tmpPtr-- = KEY_DELIMITER; if (KEY_DELIMITER == *tmpPtr) { /* A valid key should have a non-null byte before the terminating 2-null-bytes. * If not, the block must have been concurrently modified. So restart. */ return cdb_sc_mkblk; } return cdb_sc_normal; } #define GVCST_EXPAND_CURR_KEY #include "gvcst_expand_key.h" /* Defines the function "gvcst_expand_curr_key" */ /* BYPASSOK : intentional duplicate include. */ #undef GVCST_EXPAND_CURR_KEY #define GVCST_EXPAND_PREV_KEY #include "gvcst_expand_key.h" /* Defines the function "gvcst_expand_prev_key" */ /* BYPASSOK : intentional duplicate include. */ #undef GVCST_EXPAND_PREV_KEY fis-gtm-V7.0-005/sr_port/gvcst_expand_key.h0000755000032200000250000001420114342376331017552 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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. * * * ****************************************************************/ #ifndef GVCST_EXPAND_KEY_INCLUDED #define GVCST_EXPAND_KEY_INCLUDED enum cdb_sc gvcst_expand_key(srch_blk_status *pStat, int4 rec_top, gv_key *key); enum cdb_sc gvcst_expand_curr_key(srch_blk_status *pStat, gv_key *srch_key, gv_key *exp_key); enum cdb_sc gvcst_expand_prev_key(srch_blk_status *pStat, gv_key *srch_key, gv_key *exp_key); GBLREF sgmnt_data_ptr_t cs_data; #endif /* GVCST_EXPAND_KEY_INCLUDED */ #if (defined(GVCST_EXPAND_CURR_KEY) || defined(GVCST_EXPAND_PREV_KEY)) # ifdef GVCST_EXPAND_CURR_KEY /* Determine the uncompressed key at pStat->curr_rec.offset after a block search using srch_key has filled in * pStat->curr_rec. The uncompressed key is stored in "exp_key". */ enum cdb_sc gvcst_expand_curr_key(srch_blk_status *pStat, gv_key *srch_key, gv_key *exp_key) # endif # ifdef GVCST_EXPAND_PREV_KEY /* Determine the uncompressed key at pStat->prev_rec.offset after a block search using srch_key has filled in * pStat->prev_rec. The uncompressed key is stored in "exp_key". */ enum cdb_sc gvcst_expand_prev_key(srch_blk_status *pStat, gv_key *srch_key, gv_key *exp_key) # endif { int tmpCmpc, match, offset, keyend; rec_hdr_ptr_t rp; sm_uc_ptr_t buffaddr; unsigned char *dstBase, *dstEnd, *dstTop; /* exp_key related variables */ unsigned char *src; /* srch_key related variables */ unsigned char ch; # ifdef GVCST_EXPAND_CURR_KEY boolean_t fullmatch; # endif # ifdef DEBUG boolean_t match_adjusted = FALSE; # endif /* Since searching for "srch_key" landed us in between pStat->prev_rec and pStat->curr_rec, we are guaranteed that * pStat->curr_rec.match >= record-compression-count-at-pStat->curr_rec.offset (or else the search would not have * terminated in between prev_rec and curr_rec). This means we can get all the compressed bytes of the key at * curr_rec from srch_key and get the uncompressed bytes from the actual record. */ buffaddr = pStat->buffaddr; # ifdef GVCST_EXPAND_PREV_KEY offset = pStat->prev_rec.offset; if (SIZEOF(blk_hdr) > offset) { assert(0 == offset); return cdb_sc_badoffset; /* prev_key not in current block but in left sibling block. Return */ } match = pStat->prev_rec.match; # endif # ifdef GVCST_EXPAND_CURR_KEY offset = pStat->curr_rec.offset; match = pStat->curr_rec.match; # endif assert(SIZEOF(blk_hdr) <= offset); rp = (rec_hdr_ptr_t)(buffaddr + offset); EVAL_CMPC2(rp, tmpCmpc); if (tmpCmpc > match) { # ifdef GVCST_EXPAND_PREV_KEY /* We cannot determine the uncompressed prev_key based only on prev_rec.match and srch_key. * Need to go the full-blown route. */ return gvcst_expand_key(pStat, offset, exp_key); # endif # ifdef GVCST_EXPAND_CURR_KEY /* This means the block changed since we did the search. Return abnormal status so retry occurs. */ return cdb_sc_blkmod; # endif } /* Get all compressed bytes of exp_key from srch_key and get the uncompressed bytes from actual record */ dstBase = dstEnd = exp_key->base; dstTop = &dstBase[exp_key->top]; keyend = srch_key->end; assert(2 <= keyend); /* Need at least one non-zero byte to start and a zero byte to end key */ # ifdef GVCST_EXPAND_PREV_KEY assert(match != (keyend + 1)); /* Can have a full-match only on curr_key, not on prev_key */ # endif src = srch_key->base; # ifdef GVCST_EXPAND_CURR_KEY if (match == (keyend + 1)) { /* Full match. Return srch_key */ fullmatch = TRUE; match = keyend + 1; } else { fullmatch = FALSE; # endif assert(match <= keyend); /* If last matching byte in key is \0, back off one byte while copying from srch_key. Otherwise, the * logic to check for double KEY_DELIMITER sequence below will get confused. */ if ((match > tmpCmpc) && (KEY_DELIMITER == src[match - 1])) { match--; DEBUG_ONLY(match_adjusted = TRUE;) } # ifdef GVCST_EXPAND_CURR_KEY } # endif if (dstEnd + match >= dstTop) { assert(CDB_STAGNATE > t_tries); return cdb_sc_keyoflow; } memcpy(dstEnd, src, match); # ifdef GVCST_EXPAND_CURR_KEY if (fullmatch) match--; # endif dstEnd += match; # ifdef GVCST_EXPAND_CURR_KEY if (!fullmatch) { # endif src = ((sm_uc_ptr_t)(rp + 1)) + (match - tmpCmpc); dstTop--; /* to check for double KEY_DELIMITER byte sequence without exceeding buffer allocation bounds */ for ( ; ; ) { if (dstEnd >= dstTop) { assert(CDB_STAGNATE > t_tries); return cdb_sc_keyoflow; } *dstEnd++ = ch = *src++; if ((KEY_DELIMITER == ch) && (KEY_DELIMITER == (ch = *src))) { *dstEnd = ch; break; } } # ifdef GVCST_EXPAND_CURR_KEY } # endif if (KEY_DELIMITER == *dstBase) { /* A valid key wouldn't start with a '\0' character. So the block must have been concurrently modified. */ return cdb_sc_mkblk; } exp_key->end = dstEnd - dstBase; assert(2 <= exp_key->end); /* Ensure the key is double-null-byte terminated even if this is a restartable situation. * Callers like gvcst_put rely on this (in asserts). */ *dstEnd-- = KEY_DELIMITER; *dstEnd-- = KEY_DELIMITER; if (KEY_DELIMITER == *dstEnd) { /* A valid key should have a non-null byte before the terminating 2-null-bytes. * If not, the block must have been concurrently modified. So restart. */ return cdb_sc_mkblk; } /* exp_key->prev is not initialized. Caller should not rely on this. */ /* Due to concurrency issues, it is possible "exp_key" is not a well-formed key (e.g. it might have two successive * KEY_DELIMITER bytes in the middle of the key). So we cannot add a DBG_CHECK_GVKEY_VALID(exp_key) here. * But we expect later validation to catch this and restart the transaction (without affecting db integrity). * So we dont worry about such keys here. */ return cdb_sc_normal; } #endif /* (defined(GVCST_EXPAND_CURR_KEY) || defined(GVCST_EXPAND_PREV_KEY)) */ fis-gtm-V7.0-005/sr_port/gvcst_gblmod.c0000755000032200000250000001064014342376331016665 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ #include "t_end.h" /* prototypes */ #include "t_retry.h" #include "t_begin.h" #include "gvcst_protos.h" /* for gvcst_gblmod,gvcst_search prototype */ #include "copy.h" #include "gtmimagename.h" /* needed for spanning nodes */ GBLREF gv_namehead *gv_target; GBLREF gv_key *gv_currkey; GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF uint4 dollar_tlevel; GBLREF unsigned int t_tries; LITREF mstr nsb_dummy; error_def(ERR_GBLMODFAIL); boolean_t gvcst_gblmod(mval *v) { boolean_t gblmod, is_dummy; enum cdb_sc status; int key_size, key_size2, data_len; srch_hist *alt_history; blk_hdr_ptr_t bp; rec_hdr_ptr_t rp; unsigned short match, match2, rsiz, offset_to_value, oldend; srch_blk_status *bh; sm_uc_ptr_t b_top; trans_num tn_to_compare; T_BEGIN_READ_NONTP_OR_TP(ERR_GBLMODFAIL); assert(t_tries < CDB_STAGNATE || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ for (;;) { gblmod = TRUE; if (cdb_sc_normal == (status = gvcst_search(gv_currkey, NULL))) { alt_history = gv_target->alt_hist; alt_history->h[0].blk_num = 0; VMS_ONLY( if (cs_addrs->hdr->resync_tn >= ((blk_hdr_ptr_t)gv_target->hist.h[0].buffaddr)->tn) gblmod = FALSE; ) # ifdef UNIX tn_to_compare = ((blk_hdr_ptr_t)gv_target->hist.h[0].buffaddr)->tn; bh = gv_target->hist.h; bp = (blk_hdr_ptr_t) bh->buffaddr; rp = (rec_hdr_ptr_t) (bh->buffaddr + bh->curr_rec.offset); b_top = bh->buffaddr + bp->bsiz; GET_USHORT(rsiz, &rp->rsiz); key_size = gv_currkey->end + 1; data_len = rsiz + EVAL_CMPC(rp) - SIZEOF(rec_hdr) - key_size; match = bh->curr_rec.match; if (key_size == match) { if ((0 > data_len) || ((sm_uc_ptr_t)rp + rsiz > b_top)) { status = cdb_sc_rmisalign; t_retry(status); continue; } offset_to_value = SIZEOF(rec_hdr) + key_size - EVAL_CMPC(rp); /* If it could be a spanning node, i.e., has special value, then try to get tn from the * block that contains the first special subscript. Since dummy nodes always have the * same value, the tn number is not updated It s enough to do only the first piece * since all pieces of a spanning node are killed before an update is applied. */ if (IS_SN_DUMMY(data_len, (sm_uc_ptr_t)rp + offset_to_value)) { oldend = gv_currkey->end; APPEND_HIDDEN_SUB(gv_currkey); if (cdb_sc_normal == (status = gvcst_search(gv_currkey, alt_history))) { key_size2 = gv_currkey->end + 1; match = alt_history->h[0].curr_rec.match; if (key_size2 == match) tn_to_compare = ((blk_hdr_ptr_t)alt_history->h[0].buffaddr)->tn; } else { gv_currkey->end = oldend; gv_currkey->base[gv_currkey->end - 1] = KEY_DELIMITER; gv_currkey->base[gv_currkey->end] = KEY_DELIMITER; t_retry(status); continue; } gv_currkey->end = oldend; gv_currkey->base[gv_currkey->end - 1] = KEY_DELIMITER; gv_currkey->base[gv_currkey->end] = KEY_DELIMITER; } } if (cs_addrs->hdr->zqgblmod_tn > tn_to_compare) gblmod = FALSE; # endif if (!dollar_tlevel) { if ((trans_num)0 == t_end(&gv_target->hist, 0 == alt_history->h[0].blk_num ? NULL : alt_history, TN_NOT_SPECIFIED)) continue; } else { status = tp_hist(0 == alt_history->h[0].blk_num ? NULL : alt_history); if (cdb_sc_normal != status) { t_retry(status); continue; } } return gblmod; } t_retry(status); } } fis-gtm-V7.0-005/sr_port/gvcst_get.c0000644000032200000250000001746514342376333016213 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "stringpool.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "cdb_sc.h" #include "copy.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ #include "repl_msg.h" #include "gtmsource.h" #include #include "stack_frame.h" #include "wbox_test_init.h" #include "t_end.h" /* prototypes */ #include "t_retry.h" #include "t_begin.h" #include "gvcst_protos.h" /* for gvcst_search,gvcst_get prototype */ /* needed for spanning nodes */ #include "op.h" #include "op_tcommit.h" #include "error.h" #include "tp_frame.h" #include "tp_restart.h" #include "gtmimagename.h" LITREF mval literal_batch; LITREF mstr nsb_dummy; GBLREF gv_namehead *gv_target; GBLREF gv_key *gv_currkey; GBLREF spdesc stringpool; GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF uint4 dollar_tlevel; GBLREF unsigned int t_tries; error_def(ERR_GVGETFAIL); DEFINE_NSB_CONDITION_HANDLER(gvcst_get_ch) boolean_t gvcst_get(mval *v) { /* To avoid an extra function call, the outer if-check can be brought out into op_gvget (a final optimization) */ boolean_t gotit, gotspan, gotpiece, gotdummy, sn_tpwrapped; boolean_t est_first_pass; mval val_ctrl, val_piece; int gblsize, i, total_len, oldend, tmp_numsubs; unsigned short numsubs; sm_uc_ptr_t sn_ptr; int debug_len; int save_dollar_tlevel; DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); gotit = gvcst_get2(v, NULL); INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_get, (gtm_uint64_t) 1); WBTEST_ONLY(WBTEST_QUERY_HANG, LONG_SLEEP(2); ); DEBUG_ONLY(debug_len = (int)v->str.len); /* Ensure v isn't garbage pointer by actually accessing it */ if (gotit && IS_SN_DUMMY(v->str.len, v->str.addr)) { /* Start TP transaction to piece together value */ IF_SN_DISALLOWED_AND_NO_SPAN_IN_DB(return gotit); if (!dollar_tlevel) { sn_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0); ESTABLISH_NORET(gvcst_get_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); WBTEST_ONLY(WBTEST_QUERY_HANG, LONG_SLEEP(2); ); gotdummy = gvcst_get2(v, NULL); /* Will be returned if not currently a spanning node */ } else { sn_tpwrapped = FALSE; gotdummy = gotit; } oldend = gv_currkey->end; APPEND_HIDDEN_SUB(gv_currkey); WBTEST_ONLY(WBTEST_QUERY_HANG, LONG_SLEEP(2); ); gotspan = gvcst_get2(&val_ctrl, NULL); /* Search for control subscript */ if (gotspan) { /* Spanning node indeed, as expected. Piece it together */ if (val_ctrl.str.len == 6) { GET_NSBCTRL(val_ctrl.str.addr, numsubs, gblsize); } else { /* Temporarily account for mixture of control node formats between FT04 and FT05. * Note that this only works for block sizes greater than 1000. */ SSCANF(val_ctrl.str.addr, "%d,%d", &tmp_numsubs, &gblsize); numsubs = tmp_numsubs; } ENSURE_STP_FREE_SPACE(gblsize + cs_addrs->hdr->blk_size); /* give leeway.. think about more */ sn_ptr = stringpool.free; total_len = 0; v->str.addr = (char *)sn_ptr; for (i = 0; i < numsubs; i++) { NEXT_HIDDEN_SUB(gv_currkey, i); gotpiece = gvcst_get2(&val_piece, sn_ptr); if (gotpiece) { sn_ptr += val_piece.str.len; total_len += val_piece.str.len; } assert(total_len < (gblsize + cs_addrs->hdr->blk_size)); if (!gotpiece || (total_len > gblsize)) break; } if ((total_len != gblsize) || (i != numsubs)) /* Fetched value either too small or too big compared to what control subscript says */ t_retry(cdb_sc_spansize); } RESTORE_CURRKEY(gv_currkey, oldend); if (sn_tpwrapped) { op_tcommit(); REVERT; /* remove our condition handler */ } if (gotspan) { v->mvtype = MV_STR; /*v->str.addr = (char *)stringpool.free;*/ v->str.len = gblsize; stringpool.free += gblsize; } gotit = gotspan || gotdummy; } assert(save_dollar_tlevel == dollar_tlevel); return gotit; } boolean_t gvcst_get2(mval *v, unsigned char *sn_ptr) { blk_hdr_ptr_t bp; enum cdb_sc status; int key_size, data_len; int tmp_cmpc; rec_hdr_ptr_t rp; sm_uc_ptr_t b_top; srch_blk_status *bh; unsigned short rsiz; # ifdef DEBUG boolean_t in_op_gvget_lcl; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DEBUG_ONLY( /* Store global variable in_op_gvget in a local variable and reset the global right away to ensure that the global * value does not incorrectly get carried over to the next call of gvcst_get (e.g. it if was from "op_fngvget"). */ in_op_gvget_lcl = TREF(in_op_gvget); TREF(in_op_gvget) = FALSE; ) T_BEGIN_READ_NONTP_OR_TP(ERR_GVGETFAIL); assert(t_tries < CDB_STAGNATE || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ for (;;) { # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_ANTIFREEZE_GVGETFAIL == gtm_white_box_test_case_number) && !IS_STATSDB_REG(gv_cur_region)) { status = cdb_sc_blknumerr; t_retry(status); continue; } # endif if (cdb_sc_normal == (status = gvcst_search(gv_currkey, NULL))) { bh = gv_target->hist.h; if ((key_size = gv_currkey->end + 1) == bh->curr_rec.match) { /* The following code is duplicated in gvcst_dataget. Any changes here might need * to be reflected there as well. */ bp = (blk_hdr_ptr_t)bh->buffaddr; b_top = bh->buffaddr + bp->bsiz; rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); GET_USHORT(rsiz, &rp->rsiz); data_len = rsiz + EVAL_CMPC(rp) - SIZEOF(rec_hdr) - key_size; if ((0 > data_len) || ((sm_uc_ptr_t)rp + rsiz > b_top)) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_rmisalign; } else { if (!sn_ptr) { ENSURE_STP_FREE_SPACE(data_len); assert(stringpool.top - stringpool.free >= data_len); memcpy(stringpool.free, (sm_uc_ptr_t)rp + rsiz - data_len, data_len); } else memcpy(sn_ptr, (sm_uc_ptr_t)rp + rsiz - data_len, data_len); if (!dollar_tlevel) { if ((trans_num)0 == t_end(&gv_target->hist, NULL, TN_NOT_SPECIFIED)) continue; } else { status = tp_hist(NULL); if (cdb_sc_normal != status) { t_retry(status); continue; } } v->mvtype = MV_STR; v->str.len = data_len; if (!sn_ptr) { v->str.addr = (char *)stringpool.free; stringpool.free += data_len; } else v->str.addr = (char *)sn_ptr; return TRUE; } } else { DEBUG_ONLY(TREF(ready2signal_gvundef) = in_op_gvget_lcl;) if (!dollar_tlevel) { if ((trans_num)0 == t_end(&gv_target->hist, NULL, TN_NOT_SPECIFIED)) { assert(FALSE == TREF(ready2signal_gvundef)); /* t_end should have reset this */ continue; } } else { status = tp_hist(NULL); if (cdb_sc_normal != status) { assert(FALSE == TREF(ready2signal_gvundef)); /* tp_hist should have reset this */ t_retry(status); continue; } } assert(FALSE == TREF(ready2signal_gvundef)); /* t_end/tp_hist should have reset this up front */ return FALSE; } } t_retry(status); } } fis-gtm-V7.0-005/sr_port/gvcst_incr.c0000755000032200000250000000555614342376331016366 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2004-2019 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 "gdsroot.h" /* needed for gdsfhead.h */ #include "gdsblk.h" /* needed for gdsfhead.h */ #include "gtm_facility.h" /* needed for gdsfhead.h */ #include "fileinfo.h" /* needed for gdsfhead.h */ #include "gdsbt.h" /* needed for gdsfhead.h */ #include "gdsfhead.h" /* needed for gvcst_protos.h */ #include "gdsblkops.h" #include "filestruct.h" #include "jnl.h" #include "gdscc.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "mvalconv.h" /* for i2mval prototype for the MV_FORCE_MVAL macro */ #include "gvcst_protos.h" /* for gvcst_incr prototype */ GBLREF boolean_t in_gvcst_incr; GBLREF mval increment_delta_mval; GBLREF mval *post_incr_mval; GBLREF sgm_info *sgm_info_ptr; void gvcst_incr(mval *increment, mval *result) { assert(!in_gvcst_incr); assert(MV_IS_NUMERIC(increment)); /* op_gvincr or gtcmtr_increment should have done the MV_FORCE_NUM before calling */ in_gvcst_incr = TRUE; post_incr_mval = result; /* it is possible (due to some optimizations in the compiler) that both the input parameters "increment" and "result" * point to the same mval. if we pass "increment" and "result" as they are to gvcst_put, it is possible due to the code * flow that gvcst_put needs to read the value of "increment" after it is done modifying "result" (it can do this by * changing the global variable "post_incr_mval"). in this case it will read a bad value of "increment" (since it will * now be reading the modified value of "result"). therefore we do not pass them as they are. Instead, since we are * interested in only the numeric value of "increment", we take a copy of "increment" into an mval ("increment_delta_mval") * and force it to be numeric. It is the address of this mval that is passed to gvcst_put. Any changes to "post_incr_mval" * will not affect this mval so it is safe to read this anytime in gvcst_put. The mval "increment_delta_mval" is also * used in gvincr_recompute_upd_array. */ increment_delta_mval = *increment; /* Since we should be caring about just the numeric part, nullify the string part of the mval */ increment_delta_mval.str.len = 0; gvcst_put(&increment_delta_mval); assert(!in_gvcst_incr); /* should have been reset by gvcst_put */ in_gvcst_incr = FALSE; /* just in case it is not reset already by gvcst_put */ } fis-gtm-V7.0-005/sr_port/gvcst_init.c0000644000032200000250000020010214342376331016353 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 /* for offsetof macro */ #include "gtm_string.h" #include "gtm_time.h" #include "gtm_fcntl.h" #include "gtm_stdio.h" #include "gtm_unistd.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "db_header_conversion.h" #include "gdsblk.h" #include "gdskill.h" #include "gdscc.h" #include "min_max.h" /* needed for gdsblkops.h and MAX macro used below */ #include "gdsblkops.h" #include "filestruct.h" #include "iosp.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "gtm_stdlib.h" /* for ATOI */ #include "gtmimagename.h" #include "cryptdef.h" #include "mlkdef.h" #include "error.h" #include "gt_timer.h" #include "trans_log_name.h" #include "gtm_logicals.h" #include "dbfilop.h" #include "set_num_additional_processors.h" #include "have_crit.h" #include "t_retry.h" #include "dpgbldir.h" #include "longset.h" /* needed for cws_insert.h */ #include "cws_insert.h" /* for CWS_INIT macro */ #include "gvcst_protos.h" /* for gvcst_init,gvcst_init_sysops,gvcst_tp_init prototype */ #include "compswap.h" #include "send_msg.h" #include "targ_alloc.h" /* for "targ_free" prototype */ #include "hashtab_mname.h" /* for CWS_INIT macro */ #include "process_gvt_pending_list.h" #include "gvt_hashtab.h" #include "gtmmsg.h" #include "op.h" #include "set_gbuff_limit.h" #include "gtm_reservedDB.h" #include "anticipatory_freeze.h" #include "wbox_test_init.h" #include "ftok_sems.h" #include "util.h" #include "getzposition.h" #include "gtmio.h" #include "io.h" #include "ipcrmid.h" #include "gtm_ipc.h" #include "gtm_semutils.h" #include "gtm_sem.h" #include "is_file_identical.h" #include "gvt_inline.h" #ifdef GTM_FD_TRACE #include "gtm_dbjnl_dupfd_check.h" #endif /* Deferred database encryption initialization. Check the key handle and skip if already initialized */ #define INIT_DEFERRED_DB_ENCRYPTION_IF_NEEDED(REG, CSA, CSD) \ MBSTART { \ int init_status; \ int fn_len; \ char *fn; \ boolean_t do_crypt_init; \ boolean_t shoulda_crypt_init; \ DEBUG_ONLY(boolean_t was_gtmcrypt_initialized = gtmcrypt_initialized); \ \ do_crypt_init = ((USES_ENCRYPTION(CSD->is_encrypted)) && !IS_LKE_IMAGE && CSA->encr_ptr \ && (GTMCRYPT_INVALID_KEY_HANDLE == (CSA)->encr_key_handle) \ && !(CSA->encr_ptr->issued_db_init_crypt_warning) \ && (CSA->encr_ptr->reorg_encrypt_cycle == CSA->nl->reorg_encrypt_cycle)); \ /* Concurrent REORG ENCRYPT has invalidated previously acquired key information */ \ shoulda_crypt_init = ((USES_ENCRYPTION(CSD->is_encrypted)) && !IS_LKE_IMAGE && CSA->encr_ptr \ && (GTMCRYPT_INVALID_KEY_HANDLE == (CSA)->encr_key_handle) \ && !(CSA->encr_ptr->issued_db_init_crypt_warning)); \ if (do_crypt_init) \ { \ assert(was_gtmcrypt_initialized); \ fn = (char *)(REG->dyn.addr->fname); \ fn_len = REG->dyn.addr->fname_len; \ INIT_DB_OR_JNL_ENCRYPTION(CSA, CSD, fn_len, fn, init_status); \ if ((0 != init_status) && (CSA->encr_ptr->reorg_encrypt_cycle == CSA->nl->reorg_encrypt_cycle)) \ { \ if (IS_GTM_IMAGE || mu_reorg_encrypt_in_prog) \ { \ GTMCRYPT_REPORT_ERROR(init_status, rts_error, fn_len, fn); \ } else \ { \ GTMCRYPT_REPORT_ERROR(MAKE_MSG_WARNING(init_status), gtm_putmsg, fn_len, fn); \ CSA->encr_ptr->issued_db_init_crypt_warning = TRUE; \ } \ } else if (0 != init_status) \ { /* CSA->encr_ptr cannot be trusted */ \ memset(CSA->encr_ptr, 0, SIZEOF(enc_info_t)); \ CSA->encr_ptr->reorg_encrypt_cycle = -1; \ /* Send the warning to syslog, which does not count as issued_db_init_crypt_warning */ \ GTMCRYPT_REPORT_ERROR(MAKE_MSG_WARNING(init_status), send_msg, fn_len, fn); \ } \ } else if (shoulda_crypt_init) \ { /* CSA->encr_ptr cannot be trusted */ \ memset(CSA->encr_ptr, 0, SIZEOF(enc_info_t)); \ CSA->encr_ptr->reorg_encrypt_cycle = -1; \ assert(was_gtmcrypt_initialized); \ fn = (char *)(REG->dyn.addr->fname); \ fn_len = REG->dyn.addr->fname_len; \ /* Send the warning to syslog, which does not count as issued_db_init_crypt_warning */ \ GTMCRYPT_REPORT_ERROR(SET_CRYPTERR_MASK(ERR_ENCRYPTCONFLT2), send_msg, fn_len, fn); \ } \ } MBEND GBLREF boolean_t mu_reorg_process; GBLREF boolean_t created_core, dont_want_core; GBLREF gd_region *db_init_region, *gv_cur_region, *ftok_sem_reg; GBLREF gv_key *gv_currkey; GBLREF gv_namehead *gv_target; GBLREF sgmnt_addrs *cs_addrs_list; GBLREF sgmnt_data_ptr_t cs_data; GBLREF boolean_t gtmcrypt_initialized; GBLREF boolean_t gtcm_connection; GBLREF gd_addr *gd_header; GBLREF bool licensed; GBLREF int4 lkid; GBLREF char *update_array, *update_array_ptr; GBLREF uint4 update_array_size, cumul_update_array_size; GBLREF ua_list *first_ua, *curr_ua; GBLREF short crash_count; GBLREF uint4 dollar_tlevel; GBLREF uint4 dollar_trestart; GBLREF uint4 mu_reorg_encrypt_in_prog; GBLREF boolean_t mupip_jnl_recover; GBLREF jnl_format_buffer *non_tp_jfb_ptr; GBLREF buddy_list *global_tlvl_info_list; GBLREF tp_region *tp_reg_free_list; /* Ptr to list of tp_regions that are unused */ GBLREF tp_region *tp_reg_list; /* Ptr to list of tp_regions for this transaction */ GBLREF unsigned int t_tries; GBLREF struct_jrec_tcom tcom_record; GBLREF uint4 region_open_count; GBLREF sm_uc_ptr_t reformat_buffer; GBLREF int reformat_buffer_len; GBLREF volatile int reformat_buffer_in_use; /* used only in DEBUG mode */ GBLREF volatile int4 fast_lock_count; GBLREF boolean_t dse_running; GBLREF jnl_gbls_t jgbl; GBLREF enum gtmImageTypes image_type; GBLREF int pool_init; GBLREF boolean_t jnlpool_init_needed; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnlpool_addrs_ptr_t jnlpool_head; GBLREF uint4 process_id; GBLREF int4 pre_drvlongjmp_error_condition; LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; LITREF mval literal_statsDB_gblname; #define MAX_DBINIT_RETRY 3 #define MAX_DBFILOPN_RETRY_CNT 4 /* In code below to (re)create a statsDB file, we are doing a number of operations under lock. Rather than deal with * getting out of the lock before the error, handle the possible errors after we are out from under the lock. These * values are the possible errors we need may need to raise. */ typedef enum { STATSDB_NOERR = 0, STATSDB_NOTEEXIST, STATSDB_OPNERR, STATSDB_READERR, STATSDB_NOTSTATSDB, STATSDB_NOTOURS, STATSDB_UNLINKERR, STATSDB_RECREATEERR, STATSDB_CLOSEERR, STATSDB_SHMRMIDERR, STATSDB_SEMRMIDERR, STATSDB_FTOKSEMRMIDERR } statsdb_recreate_errors; error_def(ERR_BADDBVER); error_def(ERR_DBCREINCOMP); error_def(ERR_DBFLCORRP); error_def(ERR_DBGLDMISMATCH); error_def(ERR_DBNOTGDS); error_def(ERR_DBOPNERR); error_def(ERR_DBROLLEDBACK); error_def(ERR_DBVERPERFWARN1); error_def(ERR_DBVERPERFWARN2); error_def(ERR_DRVLONGJMP); /* Generic internal only error used to drive longjump() in a queued condition handler */ error_def(ERR_ENCRYPTCONFLT2); error_def(ERR_INVSTATSDB); error_def(ERR_MMNODYNUPGRD); error_def(ERR_REGOPENFAIL); error_def(ERR_STATSDBFNERR); error_def(ERR_STATSDBINUSE); static readonly mval literal_poollimit = DEFINE_MVAL_LITERAL(MV_STR | MV_NUM_APPROX, 0, 0, (SIZEOF("POOLLIMIT") - 1), "POOLLIMIT", 0, 0); void assert_jrec_member_offsets(void) { assert(REAL_JNL_HDR_LEN % DISK_BLOCK_SIZE == 0); assert(JNL_HDR_LEN % DISK_BLOCK_SIZE == 0); /* We currently assume that the journal file header size is aligned relative to the filesystem block size. * which is currently assumed to be a 2-power (e.g. 512 bytes, 1K, 2K, 4K etc.) but never more than 64K * (MAX_IO_BLOCK_SIZE). Given this, we keep the journal file header size at 64K for Unix. * This way any process updating the file header will hold crit and do aligned writes. Any process * writing the journal file data (journal records) on disk will hold the qio lock and can safely do so without * ever touching the journal file header area. If ever MAX_IO_BLOCK_SIZE changes (say because some filesystem * block size changes to 128K) such that JNL_HDR_LEN is no longer aligned to that, we want to know hence this assert. */ assert(JNL_HDR_LEN % MAX_IO_BLOCK_SIZE == 0); assert(REAL_JNL_HDR_LEN == SIZEOF(jnl_file_header)); assert(REAL_JNL_HDR_LEN <= JNL_HDR_LEN); assert(JNL_HDR_LEN == JNL_FILE_FIRST_RECORD); assert(DISK_BLOCK_SIZE >= PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN + EOF_RECLEN); assert((JNL_ALLOC_MIN * DISK_BLOCK_SIZE) > JNL_HDR_LEN); /* Following assert is for JNL_FILE_TAIL_PRESERVE macro in jnl.h */ assert(PINI_RECLEN >= EPOCH_RECLEN && PINI_RECLEN >= PFIN_RECLEN && PINI_RECLEN >= EOF_RECLEN); /* jnl_string structure has a 8-bit nodeflags field and a 24-bit length field. In some cases, this is * used as a 32-bit length field (e.g. in the value part of the SET record or ZTWORMHOLE or LGTRIG record). * These usages treat the 32-bits as a jnl_str_len_t type and access it directly. Hence the requirement that * jnl_str_len_t be the same size as 32-bits and also the same as the offset to the "text" member. * If this assert fails, all places that reference jnl_str_len_t need to be revisited. */ assert(SIZEOF(jnl_str_len_t) == SIZEOF(uint4)); assert(SIZEOF(jnl_str_len_t) == offsetof(jnl_string, text[0])); /* since time in jnl record is a uint4, and since JNL_SHORT_TIME expects time_t, we better ensure they are same. * A change in the size of time_t would mean a redesign of the fields. */ assert(SIZEOF(time_t) == GTM64_ONLY(SIZEOF(gtm_int8)) NON_GTM64_ONLY(SIZEOF(int4))); /* Make sure all jnl_seqno fields start at same offset. mur_output_record and others rely on this. */ assert(offsetof(struct_jrec_null, jnl_seqno) == offsetof(struct_jrec_upd, token_seq.jnl_seqno)); assert(offsetof(struct_jrec_null, jnl_seqno) == offsetof(struct_jrec_epoch, jnl_seqno)); assert(offsetof(struct_jrec_null, jnl_seqno) == offsetof(struct_jrec_eof, jnl_seqno)); assert(offsetof(struct_jrec_null, jnl_seqno) == offsetof(struct_jrec_tcom, token_seq.jnl_seqno)); assert(offsetof(struct_jrec_null, jnl_seqno) == offsetof(struct_jrec_ztworm, token_seq.jnl_seqno)); assert(offsetof(struct_jrec_null, jnl_seqno) == offsetof(struct_jrec_lgtrig, token_seq.jnl_seqno)); /* Make sure all strm_seqno fields start at same offset. Lot of modules rely on this */ assert(offsetof(struct_jrec_null, strm_seqno) == offsetof(struct_jrec_upd, strm_seqno)); assert(offsetof(struct_jrec_null, strm_seqno) == offsetof(struct_jrec_tcom, strm_seqno)); assert(offsetof(struct_jrec_null, strm_seqno) == offsetof(struct_jrec_ztworm, strm_seqno)); assert(offsetof(struct_jrec_null, strm_seqno) == offsetof(struct_jrec_lgtrig, strm_seqno)); /* EOF and EPOCH are not included in the above asserts because they have not ONE but 16 strm_seqno values each */ assert(offsetof(struct_jrec_ztcom, token) == offsetof(struct_jrec_upd, token_seq)); /* Make sure all jnl_seqno and token fields start at 8-byte boundary */ assert(offsetof(struct_jrec_upd, token_seq.jnl_seqno) == (ROUND_UP(offsetof(struct_jrec_upd, token_seq.jnl_seqno), SIZEOF(seq_num)))); assert(offsetof(struct_jrec_tcom, token_seq.jnl_seqno) == (ROUND_UP(offsetof(struct_jrec_tcom, token_seq.jnl_seqno), SIZEOF(seq_num)))); assert(offsetof(struct_jrec_null, jnl_seqno) == (ROUND_UP(offsetof(struct_jrec_null, jnl_seqno), SIZEOF(seq_num)))); assert(offsetof(struct_jrec_epoch, jnl_seqno) == (ROUND_UP(offsetof(struct_jrec_epoch, jnl_seqno), SIZEOF(seq_num)))); assert(offsetof(struct_jrec_eof, jnl_seqno) == (ROUND_UP(offsetof(struct_jrec_eof, jnl_seqno), SIZEOF(seq_num)))); /* All fixed size records must be multiple of 8-byte */ assert(TCOM_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_tcom), JNL_REC_START_BNDRY))); assert(ZTCOM_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_ztcom), JNL_REC_START_BNDRY))); assert(INCTN_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_inctn), JNL_REC_START_BNDRY))); assert(PINI_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_pini), JNL_REC_START_BNDRY))); assert(PFIN_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_pfin), JNL_REC_START_BNDRY))); assert(NULL_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_null), JNL_REC_START_BNDRY))); assert(EPOCH_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_epoch), JNL_REC_START_BNDRY))); assert(EOF_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_eof), JNL_REC_START_BNDRY))); /* Assumption about the structures in code */ assert(0 == MIN_ALIGN_RECLEN % JNL_REC_START_BNDRY); assert(SIZEOF(uint4) == SIZEOF(jrec_suffix)); assert((SIZEOF(jnl_record) + MAX_LOGI_JNL_REC_SIZE + SIZEOF(jrec_suffix)) < MAX_JNL_REC_SIZE); assert((DISK_BLOCK_SIZE * JNL_DEF_ALIGNSIZE) >= MAX_JNL_REC_SIZE);/* default alignsize supports max jnl record length */ assert(MAX_MAX_NONTP_JNL_REC_SIZE <= MAX_JNL_REC_SIZE); assert(MAX_DB_BLK_SIZE < MAX_MAX_NONTP_JNL_REC_SIZE); /* Ensure a PBLK record can accommodate a full GDS block */ assert(MAX_JNL_REC_SIZE <= (1 << 24)); /* Ensure that the 24-bit length field in the journal record can accommodate the maximum journal record size */ assert(tcom_record.prefix.forwptr == tcom_record.suffix.backptr); assert(TCOM_RECLEN == tcom_record.suffix.backptr); assert(SIZEOF(token_split_t) == SIZEOF(token_build)); /* Required for TOKEN_SET macro */ } CONDITION_HANDLER(gvcst_init_autoDB_ch) { gd_region *reg; unix_db_info *udi; START_CH(TRUE); if ((SUCCESS == SEVERITY) || (INFO == SEVERITY)) { assert(FALSE); /* don't know of any possible INFO/SUCCESS errors */ CONTINUE; /* Keep going for non-error issues */ } if (IS_STATSDB_REG(db_init_region)) STATSDBREG_TO_BASEDBREG(db_init_region, reg); else reg = db_init_region; udi = FILE_INFO(reg); if (udi->grabbed_ftok_sem && !ftok_sem_release(reg, FALSE, FALSE)) /* release ftok lock on the base region */ assert(FALSE); if (ftok_sem_reg == reg) ftok_sem_reg = NULL; /* Enable interrupts in case we are here with intrpt_ok_state == INTRPT_IN_GVCST_INIT due to an rts error. * 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 "gvcst_init" (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_GVCST_INIT, INTRPT_OK_TO_INTERRUPT); NEXTCH; } void gvcst_init(gd_region *reg, gd_addr *addr) { gd_segment *seg; sgmnt_addrs *baseDBcsa, *csa, *prevcsa, *regcsa; sgmnt_data_ptr_t csd; sgmnt_data statsDBcsd; jnlpool_addrs_ptr_t save_jnlpool; uint4 segment_update_array_size; int4 bsize, padsize; boolean_t is_statsDB, realloc_alt_buff, retry_dbinit; file_control *fc; gd_region *prev_reg, *reg_top, *baseDBreg, *statsDBreg; # ifdef DEBUG cache_rec_ptr_t cr; bt_rec_ptr_t bt; blk_ident tmp_blk; # endif int db_init_ret, loopcnt, max_fid_index, fd, rc, save_errno, errrsn_text_len, status; mstr log_nam, trans_log_nam; char trans_buff[MAX_FN_LEN + 1], statsdb_path[MAX_FN_LEN + 1], *errrsn_text; unique_file_id *reg_fid, *tmp_reg_fid; gd_id replfile_gdid, *tmp_gdid; tp_region *tr; ua_list *tmp_ua; time_t curr_time; uint4 curr_time_uint4, next_warn_uint4; gtm_uint8 minus1 = (gtm_uint8)-1; enum db_acc_method reg_acc_meth; boolean_t onln_rlbk_cycle_mismatch = FALSE; boolean_t replpool_valid = FALSE, replfilegdid_valid = FALSE, jnlpool_found = FALSE; intrpt_state_t save_intrpt_ok_state; replpool_identifier replpool_id; unsigned int full_len; int4 db_init_retry; srch_blk_status *bh; mstr *gld_str; node_local_ptr_t baseDBnl; unsigned char cstatus; statsdb_recreate_errors statsdb_rcerr; jbuf_rsrv_struct_t *nontp_jbuf_rsrv_lcl; intrpt_state_t prev_intrpt_state; key_t ftok_key; int ftok_semid; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; UNSUPPORTED_PLATFORM_CHECK; /* If this is a statsDB, then open its baseDB first (if not already open). Most of the times the baseDB would be open. * In rare cases like direct use of ^%YGS global, it is possible baseDB is not open. */ assert(!reg->open); is_statsDB = IS_STATSDB_REG(reg); if (is_statsDB) { STATSDBREG_TO_BASEDBREG(reg, baseDBreg); if (!baseDBreg->open) { DBGRDB((stderr, "gvcst_init: !baseDBreg->open (NOT open)\n")); gvcst_init(baseDBreg, addr); assert(baseDBreg->open); if (reg->open) /* statsDB was opened as part of opening baseDB. No need to do anything more here */ { DBGRDB((stderr, "gvcst_init: reg->open return\n")); return; } /* At this point, the baseDB is open but the statsDB is not automatically opened. This is possible if * a) TREF(statshare_opted_in) is not ALL. In that case, this call to "gvcst_init" is coming through * a direct reference to the statsDB (e.g. ZWR ^%YGS). OR * b) baseDBreg->was_open is TRUE. In that case, the statsDB open would have been short-circuited * in "gvcst_init". * c) Neither (a) nor (b). This means an open of the statsDB was attempted as part of the baseDB * "gvcst_init" done above. Since it did not, that means some sort of error occurred that * has sent messages to the user console or syslog so return right away to the caller which * would silently adjust gld map entries so they do not point to this statsDB anymore * (NOSTATS should already be set in the baseDB in this case, assert that). */ if ((ALL_STATS_OPTIN == TREF(statshare_opted_in)) && !baseDBreg->was_open) { assert(RDBF_NOSTATS & baseDBreg->reservedDBFlags); return; } } else if (RDBF_NOSTATS & baseDBreg->reservedDBFlags) { /* The baseDB was already open and the statsDB was NOT open. This could be because of either the baseDB * has NOSTATS set in it or it could be that ALL_STATS_OPTIN wasn't in place when we attempted before to * open the statsDB but failed for whatever reason (privs, noexistant directory, space, etc). In either * return right away (for same reason as described before the "if" block above). */ return; } baseDBcsa = &FILE_INFO(baseDBreg)->s_addrs; baseDBnl = baseDBcsa->nl; if (!baseDBnl->statsdb_created) { /* Before "dbfilopn" of a statsdb, check if it has been created. If not, create it (as it is an autodb).*/ /* Disable interrupts for the time we hold the ftok lock as it is otherwise possible we get a SIG-15 * and go to exit handling and try a nested "ftok_sem_lock" on the same basedb and that could pose * multiple issues (e.g. ftok_sem_lock starts a timer while waiting in the "semop" call and we will * have the same timer-id added twice due to the nested call). */ DBGRDB((stderr, "gvcst_init: !baseDBnl->statsdb_created\n")); if (IS_TP_AND_FINAL_RETRY && baseDBcsa->now_crit) { /* If this is a TP transaction and in the final retry, we are about to request the ftok * sem lock on baseDBreg while already holding crit on baseDBReg. That is an out-of-order * request which can lead to crit/ftok deadlocks so release crit before requesting it. * This code is similar to the TPNOTACID_CHECK macro with the below exceptions. * a) We do not want to issue the TPNOTACID syslog message since there is no ACID * violation here AND * b) We have to check for baseDBcsa->now_crit in addition to IS_TP_AND_FINAL_RETRY * as it is possible this call comes from "tp_restart -> gv_init_reg -> gvcst_init" * AND t_tries is still 3 but we do not hold crit on any region at that point * (i.e. "tp_crit_all_regions" call is not yet done) and in that case we should * not decrement t_tries (TP_FINAL_RETRY_DECREMENT_T_TRIES_IF_OK call below) * as it would result in we later starting the final retry with t_tries = 2 but * holding crit on all regions which is an out-of-design situation. */ TP_REL_CRIT_ALL_REG; assert(!baseDBcsa->now_crit); assert(!mupip_jnl_recover); TP_FINAL_RETRY_DECREMENT_T_TRIES_IF_OK; } db_init_region = baseDBreg; /* gvcst_init_autoDB_ch needs reg 4 ftok_sem_lock */ DEFER_INTERRUPTS(INTRPT_IN_GVCST_INIT, prev_intrpt_state); assert(INTRPT_OK_TO_INTERRUPT == prev_intrpt_state); /* relied upon by ENABLE_INTERRUPTS * in "gvcst_init_autoDB_ch". */ if (!ftok_sem_lock(baseDBreg, FALSE)) { /* Use FTOK of the base db as a lock, the same lock obtained when statsdb is auto deleted */ assert(FALSE); RTS_ERROR_CSA_ABT(baseDBcsa, VARLSTCNT(4) ERR_DBFILERR, 2, DB_LEN_STR(baseDBreg)); } ESTABLISH(gvcst_init_autoDB_ch); /* ch reverses the DEFER and lock in the opposite order */ if (0 == baseDBnl->statsdb_fname_len) { /* Initialize cnl->statsdb_fname from the basedb name */ baseDBnl->statsdb_fname_len = ARRAYSIZE(baseDBnl->statsdb_fname); gvcst_set_statsdb_fname(baseDBcsa->hdr, baseDBreg, baseDBnl->statsdb_fname, &baseDBnl->statsdb_fname_len); } if (0 == baseDBnl->statsdb_fname_len) { /* only true if gvcst_set_statsdb_fname had a problem and set it that way in */ switch(TREF(statsdb_fnerr_reason)) { /* turn the reason from gvcst_set_statsdb_fname into a useful error message */ case FNERR_NOSTATS: errrsn_text = FNERR_NOSTATS_TEXT; errrsn_text_len = SIZEOF(FNERR_NOSTATS_TEXT) - 1; break; case FNERR_STATSDIR_TRNFAIL: errrsn_text = FNERR_STATSDIR_TRNFAIL_TEXT; errrsn_text_len = SIZEOF(FNERR_STATSDIR_TRNFAIL_TEXT) - 1; break; case FNERR_STATSDIR_TRN2LONG: errrsn_text = FNERR_STATSDIR_TRN2LONG_TEXT; errrsn_text_len = SIZEOF(FNERR_STATSDIR_TRN2LONG_TEXT) - 1; break; case FNERR_INV_BASEDBFN: errrsn_text = FNERR_INV_BASEDBFN_TEXT; errrsn_text_len = SIZEOF(FNERR_INV_BASEDBFN_TEXT) - 1; break; case FNERR_FTOK_FAIL: errrsn_text = FNERR_FTOK_FAIL_TEXT; errrsn_text_len = SIZEOF(FNERR_FTOK_FAIL_TEXT) - 1; break; case FNERR_FNAMEBUF_OVERFLOW: errrsn_text = FNERR_FNAMEBUF_OVERFLOW_TEXT; errrsn_text_len = SIZEOF(FNERR_FNAMEBUF_OVERFLOW_TEXT) - 1; break; default: assertpro(FALSE); } assert(TREF(gvcst_statsDB_open_ch_active)); /* below error goes to syslog and not to user */ baseDBreg->reservedDBFlags |= RDBF_NOSTATS; /* Disable STATS in base DB */ baseDBcsa->reservedDBFlags |= RDBF_NOSTATS; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_DBFILERR, 2, DB_LEN_STR(baseDBreg), ERR_STATSDBFNERR, 2, errrsn_text_len, errrsn_text); } COPY_STATSDB_FNAME_INTO_STATSREG(reg, baseDBnl->statsdb_fname, baseDBnl->statsdb_fname_len); seg = reg->dyn.addr; if (!seg->blk_size) { /* Region/segment created by "mu_gv_cur_reg_init" (which sets most of reg/seg fields to 0). * Now that we need a non-zero blk_size, do what GDE did to calculate the statsdb block-size. * But since we cannot duplicate that code here, we set this to the same value effectively but * add an assert that the two are the same in "gd_load" function. * Take this opportunity to initialize other seg/reg fields for statsdbs as GDE would have done. */ seg->blk_size = STATSDB_BLK_SIZE; /* Similar code for a few other critical fields that need initialization before "mu_cre_file" */ seg->allocation = STATSDB_ALLOCATION; reg->max_key_size = STATSDB_MAX_KEY_SIZE; reg->max_rec_size = STATSDB_MAX_REC_SIZE; /* The below is directly inherited from the base db so no macro/assert like above fields */ seg->mutex_slots = NUM_CRIT_ENTRY(baseDBcsa->hdr); reg->mumps_can_bypass = TRUE; } if (0 != baseDBcsa->hdr->statsdb_allocation) { /* The statsdb allocation for this region has been extended before. * Use the extended allocation size from the file header. */ seg->allocation = baseDBcsa->hdr->statsdb_allocation; } for ( ; ; ) /* for loop only to let us break from error cases without having a deep if-then-else */ { /* with the ftok lock in place, check if the db is already created */ if (baseDBnl->statsdb_created) break; /* File still not created. Do it now under the ftok lock. */ DBGRDB((stderr, "gvcst_init: !baseDBnl->statsdb_created (under lock)\n")); cstatus = gvcst_cre_autoDB(reg); if (EXIT_NRM == cstatus) { baseDBnl->statsdb_created = TRUE; break; } /* File failed to create - if this is a case where the file exists (but was not supposed * to exist), we should remove/recreate the file. Otherwise, we have a real error - * probably a missing directory or permissions or some such. In that case, turn off stats * and unwind the failed open. */ statsdb_rcerr = STATSDB_NOERR; /* Assume no error to occur */ save_errno = TREF(mu_cre_file_openrc); /* Save the errno that stopped our recreate */ DBGRDB((stderr, "gvcst_init: Create of statsDB failed - rc = %d\n", save_errno)); for ( ; ; ) /* for loop only to handle error cases without having a deep if-then-else */ { if (EACCES == TREF(mu_cre_file_openrc)) { statsdb_rcerr = STATSDB_OPNERR; save_errno = errno; break; } if (EEXIST != TREF(mu_cre_file_openrc)) { statsdb_rcerr = STATSDB_NOTEEXIST; break; } /* See if this statsdb file is really supposed to be linked to the baseDB we * also just opened. To do this, we need to open the statsdb temporarily, read * its fileheader and then close it to get the file-header field we want. Note * this is a very quick operation so we don't use the normal DB utilities on it. * We just want to read the file header and get out. Since we have the lock, no * other processes will have this database open unless we are illegitimately * opening it. This is an infrequent issue so the overhead is not relevant. */ DBGRDB((stderr, "gvcst_init: Test to see if file is 'ours'\n")); memcpy(statsdb_path, reg->dyn.addr->fname, reg->dyn.addr->fname_len); statsdb_path[reg->dyn.addr->fname_len] = '\0'; /* Rebuffer fn to include null */ fd = OPEN(statsdb_path, O_RDONLY); if (0 > fd) { /* Some sort of open error occurred */ statsdb_rcerr = STATSDB_OPNERR; save_errno = errno; break; } /* Open worked - read file header from it*/ LSEEKREAD(fd, 0, (char *)&statsDBcsd, SIZEOF(sgmnt_data), rc); if (0 == memcmp(statsDBcsd.label, V6_GDS_LABEL, GDS_LABEL_SZ - 1)) db_header_upconv(&statsDBcsd); if (0 > rc) { /* Wasn't enough data to be a file header - not a statsDB */ statsdb_rcerr = STATSDB_NOTSTATSDB; CLOSEFILE(fd, rc); break; } if (0 < rc) { /* Unknown error while doing read */ statsdb_rcerr = STATSDB_READERR; save_errno = rc; CLOSEFILE(fd, rc); break; } /* This is the case (0 == rc) */ /* Read worked - check if these two files are a proper pair */ if ((baseDBreg->dyn.addr->fname_len != statsDBcsd.basedb_fname_len) || (0 != memcmp(baseDBreg->dyn.addr->fname, statsDBcsd.basedb_fname, statsDBcsd.basedb_fname_len))) { /* This file is in use by another database */ statsdb_rcerr = STATSDB_NOTOURS; CLOSEFILE(fd, rc); break; } /* This file was for us - unlink and recreate */ DBGRDB((stderr, "gvcst_init: File is ours - unlink and recreate it\n")); # ifdef BYPASS_UNLINK_RECREATE_STATSDB baseDBnl->statsdb_created = TRUE; # else /* Before removing a leftover statsdb file, check if it has corresponding private * semid/shmid or ftok_semid. If so, remove them too. */ if ((INVALID_SHMID != statsDBcsd.shmid) && (0 != shm_rmid(statsDBcsd.shmid))) { statsdb_rcerr = STATSDB_SHMRMIDERR; save_errno = errno; CLOSEFILE(fd, rc); break; } if ((INVALID_SEMID != statsDBcsd.semid) && (0 != sem_rmid(statsDBcsd.semid))) { statsdb_rcerr = STATSDB_SEMRMIDERR; save_errno = errno; CLOSEFILE(fd, rc); break; } if ((-1 != (ftok_key = FTOK(statsdb_path, GTM_ID))) && (INVALID_SEMID != (ftok_semid = semget(ftok_key, FTOK_SEM_PER_ID, RWDALL | IPC_CREAT))) && (0 == semctl(ftok_semid, DB_COUNTER_SEM, GETVAL)) && (0 != sem_rmid(ftok_semid))) { statsdb_rcerr = STATSDB_FTOKSEMRMIDERR; save_errno = errno; CLOSEFILE(fd, rc); break; } rc = UNLINK(statsdb_path); if (0 > rc) { /* Unlink failed - may not have permissions */ statsdb_rcerr = STATSDB_UNLINKERR; save_errno = errno; CLOSEFILE(fd, rc); break; } /* Unlink succeeded - recreate now */ cstatus = gvcst_cre_autoDB(reg); if (EXIT_NRM != cstatus) { /* Recreate failed */ statsdb_rcerr = STATSDB_RECREATEERR; save_errno = TREF(mu_cre_file_openrc); CLOSEFILE(fd, rc); break; } baseDBnl->statsdb_created = TRUE; # endif CLOSEFILE(fd, rc); if (0 < rc) { /* Close failed */ statsdb_rcerr = STATSDB_CLOSEERR; save_errno = rc; } break; } if (STATSDB_NOERR != statsdb_rcerr) { /* If we could not create or recreate the file, finish our error processing here */ assert(TREF(gvcst_statsDB_open_ch_active)); /* so the below rts_error_csa calls * go to syslog and not to user. */ baseDBreg->reservedDBFlags |= RDBF_NOSTATS; /* Disable STATS in base DB */ baseDBcsa->reservedDBFlags |= RDBF_NOSTATS; baseDBnl->statsdb_fname_len = 0; if (!ftok_sem_release(baseDBreg, FALSE, FALSE)) { /* Release the lock before unwinding back */ assert(FALSE); RTS_ERROR_CSA_ABT(baseDBcsa, VARLSTCNT(4) ERR_DBFILERR, 2, DB_LEN_STR(baseDBreg)); } /* For those errors that need a special error message, take care of that here * now that we've released the lock. */ switch(statsdb_rcerr) { case STATSDB_NOTEEXIST: /* Some error occurred handled elsewhere */ break; case STATSDB_NOTOURS: /* This statsdb already in use elsewhere */ /* We are trying to attach a statsDB to our baseDB should not be * associated with that baseDB. First check if this IS a statsdb. */ if (IS_RDBF_STATSDB(&statsDBcsd)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_STATSDBINUSE, 6, DB_LEN_STR(reg), statsDBcsd.basedb_fname_len, statsDBcsd.basedb_fname, DB_LEN_STR(baseDBreg)); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_INVSTATSDB, 4, DB_LEN_STR(reg), REG_LEN_STR(reg)); break; /* For the compiler */ case STATSDB_OPNERR: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_DBOPNERR, 2, DB_LEN_STR(reg), save_errno); break; /* For the compiler */ case STATSDB_READERR: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_DBFILERR, 2, DB_LEN_STR(reg), save_errno); break; /* For the compiler */ case STATSDB_NOTSTATSDB: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_INVSTATSDB, 4, DB_LEN_STR(reg), REG_LEN_STR(reg)); break; /* For the compiler */ case STATSDB_UNLINKERR: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("unlink()"), CALLFROM, save_errno); break; /* For the compiler */ case STATSDB_RECREATEERR: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_DBOPNERR, 2, DB_LEN_STR(reg), save_errno); break; /* For the compiler */ case STATSDB_CLOSEERR: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("close()"), CALLFROM, save_errno); break; /* For the compiler */ case STATSDB_SHMRMIDERR: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("shm_rmid()"), CALLFROM, save_errno); break; /* For the compiler */ case STATSDB_SEMRMIDERR: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("sem_rmid()"), CALLFROM, save_errno); break; /* For the compiler */ case STATSDB_FTOKSEMRMIDERR: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("ftok sem_rmid()"), CALLFROM, save_errno); break; /* For the compiler */ default: assertpro(FALSE); } if (TREF(gvcst_statsDB_open_ch_active)) { /* Unwind back to ESTABLISH_NORET where did gvcst_init() call to open this * statsDB which now won't open. */ pre_drvlongjmp_error_condition = error_condition; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DRVLONGJMP); } else { /* We are not nested so can give the appropriate error ourselves */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_DBFILERR, 2, DB_LEN_STR(reg), ERR_TEXT, 2, RTS_ERROR_TEXT("See preceding errors written to syserr" " and/or syslog for details")); } } break; } REVERT; if (!ftok_sem_release(baseDBreg, FALSE, FALSE)) { assert(FALSE); RTS_ERROR_CSA_ABT(baseDBcsa, VARLSTCNT(4) ERR_DBFILERR, 2, DB_LEN_STR(baseDBreg)); } ENABLE_INTERRUPTS(INTRPT_IN_GVCST_INIT, prev_intrpt_state); } else { COPY_STATSDB_FNAME_INTO_STATSREG(reg, baseDBnl->statsdb_fname, baseDBnl->statsdb_fname_len); seg = reg->dyn.addr; if (!seg->blk_size) { /* see comment above on why we this initialization for a statsDB */ seg->blk_size = STATSDB_BLK_SIZE; seg->allocation = STATSDB_ALLOCATION; reg->max_key_size = STATSDB_MAX_KEY_SIZE; reg->max_rec_size = STATSDB_MAX_REC_SIZE; seg->mutex_slots = NUM_CRIT_ENTRY(baseDBcsa->hdr); reg->mumps_can_bypass = TRUE; } if (0 != baseDBcsa->hdr->statsdb_allocation) { /* The statsdb allocation for this region has been extended before. * Use the extended allocation size from the file header. */ seg->allocation = baseDBcsa->hdr->statsdb_allocation; } } } # ifdef DEBUG else { BASEDBREG_TO_STATSDBREG(reg, statsDBreg); assert(!statsDBreg->open); } # endif assert(!jgbl.forw_phase_recovery); CWS_INIT; /* initialize the cw_stagnate hash-table */ /* check the header design assumptions */ assert(SIZEOF(th_rec) == (SIZEOF(bt_rec) - SIZEOF(bt->blkque))); assert(SIZEOF(cache_rec) == (SIZEOF(cache_state_rec) + SIZEOF(cr->blkque))); DEBUG_ONLY(assert_jrec_member_offsets()); assert(MAX_DB_BLK_SIZE < (1ULL << NEXT_OFF_MAX_BITS)); /* Ensure a off_chain record's next_off member * can work with all possible block sizes */ set_num_additional_processors(); # ifdef DEBUG /* Note that the "block" member in the blk_ident structure in gdskill.h has 30 bits. * Currently, the maximum number of blocks is 2**30. If ever this increases, something * has to be correspondingly done to the "block" member to increase its capacity. * The following assert checks that we always have space in the "block" member * to represent a GDS block number. */ tmp_blk.block = minus1; assert(MAXTOTALBLKS_MAX - 1 <= tmp_blk.block); # endif /* TH_BLOCK is currently a hardcoded constant as basing it on the offsetof macro does not work with the VMS compiler. * Therefore assert that TH_BLOCK points to the 512-byte block where the "trans_hist" member lies in the fileheader. */ assert(DIVIDE_ROUND_UP(offsetof(sgmnt_data, trans_hist), DISK_BLOCK_SIZE) == TH_BLOCK); /* Here's the shared memory layout: * * low address * * both * segment_data * (file_header) * MM_BLOCK * (master_map) * TH_BLOCK * BG * bt_header * (bt_buckets * bt_rec) * th_base (SIZEOF(que_ent) into an odd bt_rec) * bt_base * (n_bts * bt_rec) * cs_addrs->acc_meth.bg.cache_state * (cache_que_heads) * (bt_buckets * cache_rec) * (n_bts * cache_rec) * critical * (mutex_struct) * nl * (node_local) * [jnl_name * jnl_buffer] * LOCK_BLOCK (lock_space) * (lock_space_size) * MM * file contents * cs_addrs->acc_meth.mm.mmblk_state * (mmblk_que_heads) * (bt_buckets * mmblk_rec) * (n_bts * mmblk_rec) * critical * (mutex_struct) * nl * (node_local) * [jnl_name * jnl_buffer] * LOCK_BLOCK (lock_space) * (lock_space_size) * high address */ /* Ensure first 3 members (upto now_running) of node_local are at the same offset for any version. * * Structure ----> node_local <---- size 59392 [0xe800] * * offset = 0000 [0x0000] size = 0012 [0x000c] ----> node_local.label * offset = 0012 [0x000c] size = 0256 [0x0100] ----> node_local.fname * offset = 0268 [0x010c] size = 0036 [0x0024] ----> node_local.now_running * * This is so that the VERMISMATCH error can be successfully detected in db_init/mu_rndwn_file * and so that the db-file-name can be successfully obtained from orphaned shm by mu_rndwn_all. */ assert(0 == OFFSETOF(node_local, label[0])); assert(12 == SIZEOF(((node_local *)NULL)->label)); assert(12 == GDS_LABEL_SZ); assert(12 == OFFSETOF(node_local, fname[0])); assert(256 == SIZEOF(((node_local *)NULL)->fname)); assert(256 == (MAX_FN_LEN + 1)); assert(268 == OFFSETOF(node_local, now_running[0])); assert(36 == SIZEOF(((node_local *)NULL)->now_running)); assert(36 == MAX_REL_NAME); for (loopcnt = 0; loopcnt < MAX_DBFILOPN_RETRY_CNT; loopcnt++) { prev_reg = dbfilopn(reg); if (prev_reg != reg) { /* (gd_region *)-1 == prev_reg => cm region open attempted */ if (NULL == prev_reg || (gd_region *)-1L == prev_reg) return; /* Found same database already open - prev_reg contains addr of originally opened region */ FILE_CNTL(reg) = FILE_CNTL(prev_reg); memcpy(reg->dyn.addr->fname, prev_reg->dyn.addr->fname, prev_reg->dyn.addr->fname_len); reg->dyn.addr->fname_len = prev_reg->dyn.addr->fname_len; csa = (sgmnt_addrs *)&FILE_INFO(reg)->s_addrs; if (NULL == csa->gvt_hashtab) gvt_hashtab_init(csa); /* populate csa->gvt_hashtab; needed BEFORE PROCESS_GVT_PENDING_LIST */ PROCESS_GVT_PENDING_LIST(reg, csa); csd = csa->hdr; reg->max_rec_size = csd->max_rec_size; reg->max_key_size = csd->max_key_size; reg->null_subs = csd->null_subs; reg->std_null_coll = csd->std_null_coll; reg->jnl_state = csd->jnl_state; reg->jnl_file_len = csd->jnl_file_len; /* journal file name length */ memcpy(reg->jnl_file_name, csd->jnl_file_name, reg->jnl_file_len); /* journal file name */ reg->jnl_alq = csd->jnl_alq; reg->jnl_deq = csd->jnl_deq; reg->jnl_buffer_size = csd->jnl_buffer_size; reg->jnl_before_image = csd->jnl_before_image; reg->dyn.addr->asyncio = csd->asyncio; reg->dyn.addr->read_only = csd->read_only; assert((RDBF_NOSTATS & csd->reservedDBFlags) ? (csa->reservedDBFlags == csd->reservedDBFlags) : ((~RDBF_NOSTATS & csa->reservedDBFlags) == csd->reservedDBFlags)); /* suitably aligned*/ SYNC_RESERVEDDBFLAGS_REG_CSA_CSD(reg, csa, csd, ((node_local_ptr_t)NULL)); SET_REGION_OPEN_TRUE(reg, WAS_OPEN_TRUE); assert(1 <= csa->regcnt); csa->regcnt++; /* Increment # of regions that point to this csa */ return; } /* Note that if we are opening a statsDB, it is possible baseDBreg->was_open is TRUE at this point. */ reg->was_open = FALSE; /* We shouldn't have crit on any region unless we are in TP and in the final retry or we are in mupip_set_journal * trying to switch journals across all regions. WBTEST_HOLD_CRIT_ENABLED is an exception because it exercises a * deadlock situation so it needs to hold multiple crits at the same time. Currently, there is no fine-granular * checking for mupip_set_journal, hence a coarse MUPIP_IMAGE check for image_type. */ assert(dollar_tlevel && (CDB_STAGNATE <= t_tries) || IS_MUPIP_IMAGE || (0 == have_crit(CRIT_HAVE_ANY_REG)) || WBTEST_ENABLED(WBTEST_HOLD_CRIT_ENABLED)); if (dollar_tlevel && (0 != have_crit(CRIT_HAVE_ANY_REG))) { /* To avoid deadlocks with currently holding crits and the DLM lock request to be done in "db_init", * we should insert this region in the tp_reg_list and tp_restart should do the gvcst_init after * having released crit on all regions. */ insert_region(reg, &tp_reg_list, &tp_reg_free_list, SIZEOF(tp_region)); t_retry(cdb_sc_needcrit); assert(FALSE); /* should never reach here since t_retry should have unwound the M-stack and restarted TP */ } csa = (sgmnt_addrs *)&FILE_INFO(reg)->s_addrs; # ifdef NOLICENSE licensed = TRUE; # else CRYPT_CHKSYSTEM; # endif csa->hdr = NULL; csa->nl = NULL; csa->jnl = NULL; csa->gbuff_limit = 0; csa->our_midnite = NULL; csa->our_lru_cache_rec_off = 0; csa->persistent_freeze = FALSE; /* want secshr_db_clnup() to clear an incomplete freeze/unfreeze codepath */ csa->regcnt = 1; /* At this point, only one region points to this csa */ csa->db_addrs[0] = csa->db_addrs[1] = NULL; csa->mlkctl = NULL; csa->mlkctl_len = 0; csa->mlkhash = NULL; csa->mlkhash_shmid = INVALID_SHMID; csa->gd_ptr = addr ? addr : gd_header; if (csa->gd_ptr) csa->gd_instinfo = csa->gd_ptr->instinfo; if ((IS_GTM_IMAGE || !pool_init) && jnlpool_init_needed && CUSTOM_ERRORS_AVAILABLE && REPL_ALLOWED(csa) && REPL_INST_AVAILABLE(addr)) { replpool_valid = TRUE; jnlpool_init(GTMRELAXED, (boolean_t)FALSE, (boolean_t *)NULL, addr); status = filename_to_id(&replfile_gdid, replpool_id.instfilename); /* set by REPL_INST_AVAILABLE */ replfilegdid_valid = (SS_NORMAL == status); } else replpool_valid = replfilegdid_valid = FALSE; /* Any LSEEKWRITEs hence forth will wait if the instance is frozen. To aid in printing the region information before * and after the wait, csa->region is referenced. Since it is NULL at this point, set it to reg. This is a safe * thing to do since csa->region is anyways set in db_common_init (few lines below). */ csa->region = reg; /* Protect the db_init and the code below until we set reg->open to TRUE. This is needed as otherwise, * if a MUPIP STOP is issued to this process at a time-window when db_init is completed but reg->open * is NOT set to TRUE, will cause gds_rundown NOT to clean up the shared memory created by db_init and * thus would be left over in the system. */ DEFER_INTERRUPTS(INTRPT_IN_GVCST_INIT, prev_intrpt_state); assert(INTRPT_OK_TO_INTERRUPT == prev_intrpt_state); /* relied upon by ENABLE_INTERRUPTS * in dbinit_ch and gvcst_init_autoDB_ch */ /* Do a loop of "db_init". This is to account for the fact that "db_init" can return an error in some cases. * e.g. If DSE does "db_init" first time with OK_TO_BYPASS_TRUE and somewhere in the middle of "db_init" it * notices that a concurrent mumps process has deleted the semid/shmid since it was the last process attached * to the db. In this case we retry the "db_init" a few times and the last time we retry with OK_TO_BYPASS_FALSE * even if this is DSE. */ db_init_retry = 0; GTM_WHITE_BOX_TEST(WBTEST_HOLD_FTOK_UNTIL_BYPASS, db_init_retry, MAX_DBINIT_RETRY); do { db_init_ret = db_init(reg, (MAX_DBINIT_RETRY == db_init_retry) ? OK_TO_BYPASS_FALSE : OK_TO_BYPASS_TRUE); if (0 == db_init_ret) break; if (-1 == db_init_ret) { assert(MAX_DBINIT_RETRY > db_init_retry); retry_dbinit = TRUE; } else { /* Set "retry_dbinit" to FALSE that way "db_init_err_cleanup" call below takes care of * closing udi->fd opened in "dbfilopn" call early in this iteration. This avoids fd leak * since another call to "dbfilopn" in the next iteration would not know about the previous fd. */ retry_dbinit = FALSE; } db_init_err_cleanup(retry_dbinit); if (!retry_dbinit) break; } while (MAX_DBINIT_RETRY >= ++db_init_retry); assert(-1 != db_init_ret); if (0 == db_init_ret) break; if (ERR_DBGLDMISMATCH == db_init_ret) { /* "db_init" would have adjusted seg->asyncio to reflect the db file header's asyncio settings. * Retry but do not count this try towards the total tries as otherwise it is theoretically possible * for the db fileheader to be recreated with just the opposite asyncio setting in each try and * we might eventually error out when "loopcnt" is exhausted. Not counting this try implies we * might loop indefinitely but the chances are infinitesimally small. And since this error is never * issued, the user should never expect to see this and hence this error is not documented. */ loopcnt--; } } if (db_init_ret) { assert(FALSE); /* we don't know of a practical way to get errors in each of the for-loop attempts above */ /* "db_init" returned with an unexpected error. Issue a generic error to note this out-of-design state */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_REGOPENFAIL, 4, REG_LEN_STR(reg), DB_LEN_STR(reg)); } /* At this point, we have initialized the database, but haven't yet set reg->open to TRUE. If any rts_errors happen in * the meantime, there are no condition handlers established to handle the rts_error. More importantly, it is non-trivial * to add logic to such a condition handler to undo the effects of db_init. Also, in some cases, the rts_error can can * confuse future calls of db_init. By invoking DBG_MARK_RTS_ERROR_UNUSABLE, we can catch any rts_errors in future and * eliminate it on a case by case basis. */ DBG_MARK_RTS_ERROR_UNUSABLE; UPDATE_CRASH_COUNT(csa, crash_count); csa->regnum = ++region_open_count; csd = csa->hdr; # ifdef GTM_TRIGGER /* Take copy of db trigger cycle into csa at db startup. Any concurrent changes to the * db trigger cycle (by MUPIP TRIGGER) will be detected at tcommit (t_end/tp_tend) time. */ csa->db_trigger_cycle = csd->db_trigger_cycle; # endif /* set csd and fill in selected fields */ assert(REG_ACC_METH(reg) == csd->acc_meth); /* db_init should have made sure this assert holds good */ reg_acc_meth = csd->acc_meth; /* It is necessary that we do the pending gv_target list reallocation BEFORE db_common_init as the latter resets * reg->max_key_size to be equal to the csd->max_key_size and hence process_gvt_pending_list might wrongly conclude * that NO reallocation (since it checks reg->max_key_size with csd->max_key_size) is needed when in fact a * reallocation might be necessary (if the user changed max_key_size AFTER database creation) */ PROCESS_GVT_PENDING_LIST(reg, csa); db_common_init(reg, csa, csd); /* do initialization common to db_init() and mu_rndwn_file() */ /* If we are not fully upgraded, see if we need to send a warning to the operator console about performance. Compatibility mode is a known performance drain. Actually, we can send one of two messages. If the desired_db_format is for an earlier release than the current release, we send a performance warning that this mode degrades performance. However, if the desired_db_format is for the current version but there are blocks to convert still, we send a gengle reminder that running mupip reorg upgrade would be a good idea to get the full performance benefit of V5. */ time(&curr_time); assert(MAXUINT4 > curr_time); curr_time_uint4 = (uint4)curr_time; next_warn_uint4 = csd->next_upgrd_warn.cas_time; if (!csd->fully_upgraded && curr_time_uint4 > next_warn_uint4 && COMPSWAP_LOCK(&csd->next_upgrd_warn.time_latch, next_warn_uint4, 0, (curr_time_uint4 + UPGRD_WARN_INTERVAL), 0)) { /* The msg is due and we have successfully updated the next time interval */ if (GDSVCURR != csd->desired_db_format) /* TODO: what should this check? */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_DBVERPERFWARN1, 2, DB_LEN_STR(reg)); else send_msg_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_DBVERPERFWARN2, 2, DB_LEN_STR(reg)); } csa->lock_crit_with_db = csd->lock_crit_with_db; /* put a copy in csa where it's cheaper for mlk* to access */ /* Compute the maximum journal space requirements for a PBLK (including possible ALIGN record). * Use this variable in the TOTAL_TPJNL_REC_SIZE and TOTAL_NONTP_JNL_REC_SIZE macros instead of recomputing. */ csa->pblk_align_jrecsize = (int4)MIN_PBLK_RECLEN + csd->blk_size + (int4)MIN_ALIGN_RECLEN; segment_update_array_size = UA_SIZE(csd); assert(!reg->was_open); SET_REGION_OPEN_TRUE(reg, WAS_OPEN_FALSE); GTM_FD_TRACE_ONLY(gtm_dbjnl_dupfd_check()); /* check if any of db or jnl fds collide (D9I11-002714) */ if (NULL != csa->dir_tree) { /* It is possible that dir_tree has already been targ_alloc'ed. This is because GT.CM or VMS DAL * calls can run down regions without the process halting out. We don't want to double malloc. */ csa->dir_tree->clue.end = 0; } SET_CSA_DIR_TREE(csa, reg->max_key_size, reg); assert(reg->open); /* Now that reg->open is set to TRUE and directory tree is initialized, go ahead and set rts_error back to being usable */ DBG_MARK_RTS_ERROR_USABLE; /* Do the deferred encryption initialization now in case it needs to issue an rts_error */ INIT_DEFERRED_DB_ENCRYPTION_IF_NEEDED(reg, csa, csd); /* gds_rundown if invoked from now on will take care of cleaning up the shared memory segment */ /* The below code, until the ENABLE_INTERRUPTS(INTRPT_IN_GVCST_INIT, prev_intrpt_state), can do mallocs which in turn * can issue a GTM-E-MEMORY error which would invoke rts_error. Hence these have to be done AFTER the * DBG_MARK_RTS_ERROR_USABLE call. Since these are only private memory initializations, it is safe to * do these after reg->open is set. Any rts_errors from now on still do the needful cleanup of shared memory in * gds_rundown since reg->open is already TRUE. */ if (first_ua == NULL) { /* first open of first database - establish an update array system */ assert(update_array == NULL); assert(update_array_ptr == NULL); assert(update_array_size == 0); tmp_ua = (ua_list *)malloc(SIZEOF(ua_list)); memset(tmp_ua, 0, SIZEOF(ua_list)); /* initialize tmp_ua->update_array and tmp_ua->next_ua to NULL */ tmp_ua->update_array = (char *)malloc(segment_update_array_size); tmp_ua->update_array_size = segment_update_array_size; /* assign global variables only after malloc() succeeds */ update_array_size = cumul_update_array_size = segment_update_array_size; update_array = update_array_ptr = tmp_ua->update_array; first_ua = curr_ua = tmp_ua; } else { /* there's already an update_array system in place */ assert(update_array != NULL); assert(update_array_size != 0); if (!dollar_tlevel && segment_update_array_size > first_ua->update_array_size) { /* no transaction in progress and the current array is too small - replace it */ assert(first_ua->update_array == update_array); assert(first_ua->update_array_size == update_array_size); assert(first_ua->next_ua == NULL); tmp_ua = first_ua; first_ua = curr_ua = NULL; free(update_array); tmp_ua->update_array = update_array = update_array_ptr = NULL; tmp_ua->update_array = (char *)malloc(segment_update_array_size); tmp_ua->update_array_size = segment_update_array_size; /* assign global variables only after malloc() succeeds */ update_array_size = cumul_update_array_size = segment_update_array_size; update_array = update_array_ptr = tmp_ua->update_array; first_ua = curr_ua = tmp_ua; } } assert(global_tlvl_info_list || !csa->sgm_info_ptr); if (JNL_ALLOWED(csa)) { bsize = csd->blk_size; realloc_alt_buff = FALSE; if (NULL == non_tp_jfb_ptr) { non_tp_jfb_ptr = (jnl_format_buffer *)malloc(SIZEOF(jnl_format_buffer)); non_tp_jfb_ptr->hi_water_bsize = bsize; non_tp_jfb_ptr->buff = (char *)malloc(MAX_NONTP_JNL_REC_SIZE(bsize)); non_tp_jfb_ptr->record_size = 0; /* initialize it to 0 since TOTAL_NONTPJNL_REC_SIZE macro uses it */ non_tp_jfb_ptr->alt_buff = NULL; assert(NULL == TREF(nontp_jbuf_rsrv)); ALLOC_JBUF_RSRV_STRUCT(nontp_jbuf_rsrv_lcl, csa); TREF(nontp_jbuf_rsrv) = nontp_jbuf_rsrv_lcl; } else if (bsize > non_tp_jfb_ptr->hi_water_bsize) { /* Need a larger buffer to accommodate larger non-TP journal records */ non_tp_jfb_ptr->hi_water_bsize = bsize; free(non_tp_jfb_ptr->buff); non_tp_jfb_ptr->buff = (char *)malloc(MAX_NONTP_JNL_REC_SIZE(bsize)); if (NULL != non_tp_jfb_ptr->alt_buff) { free(non_tp_jfb_ptr->alt_buff); realloc_alt_buff = TRUE; } assert(NULL != TREF(nontp_jbuf_rsrv)); } /* If the journal records need to be encrypted in the journal file and if replication is in use, we will need access * to both the encrypted (for the journal file) and unencrypted (for the journal pool) journal record contents. * Allocate an alternative buffer if any open journaled region is encrypted. */ if (realloc_alt_buff || (USES_ENCRYPTION(csd->is_encrypted) && (NULL == non_tp_jfb_ptr->alt_buff))) non_tp_jfb_ptr->alt_buff = (char *)malloc(MAX_NONTP_JNL_REC_SIZE(non_tp_jfb_ptr->hi_water_bsize)); /* csa->min_total_tpjnl_rec_size represents the minimum journal buffer space needed for a TP transaction. * It is a conservative estimate assuming that one ALIGN record and one PINI record will be written for * one set of fixed size jnl records written. * si->total_jnl_rec_size is initialized/reinitialized to this value here and in tp_clean_up(). * The purpose of this field is to avoid recomputation of the variable in tp_clean_up(). * In addition to this, space requirements for whatever journal records get formatted as part of * jnl_format() need to be taken into account. * This is done in jnl_format() where si->total_jnl_rec_size is appropriately incremented. */ csa->min_total_tpjnl_rec_size = PINI_RECLEN + TCOM_RECLEN + MIN_ALIGN_RECLEN; /* Similarly csa->min_total_nontpjnl_rec_size represents the minimum journal buffer space needed * for a non-TP transaction. * It is a conservative estimate assuming that one ALIGN record and one PINI record will be written for * one set of fixed size jnl records written. */ csa->min_total_nontpjnl_rec_size = PINI_RECLEN + MIN_ALIGN_RECLEN; } if (!IS_GTM_IMAGE) gvcst_tp_init(reg); /* Initialize TP structures, else postpone till TP is used (only if GTM) */ if (!global_tlvl_info_list) { global_tlvl_info_list = (buddy_list *)malloc(SIZEOF(buddy_list)); initialize_list(global_tlvl_info_list, SIZEOF(global_tlvl_info), GBL_TLVL_INFO_LIST_INIT_ALLOC); } ENABLE_INTERRUPTS(INTRPT_IN_GVCST_INIT, prev_intrpt_state); if (dba_bg == reg_acc_meth) { /* Check if (a) this region has non-upgraded blocks and if so, (b) the reformat buffer exists and * (c) if it is big enough to deal with this region. If the region does not have any non-upgraded * block (blks_to_upgrd is 0) we will not allocate the buffer at this time. Note that this opens up * a small window for errors. If this buffer is not allocated and someone turns on compatibility * mode and before the process can discover this and allocate the buffer, it runs out of memory, * errors out and finds it is responsible for running down the database, it could fail on a recursive * memory error when it tries to allocate the block. This is (to me) an acceptable risk as it is * very low and compares favorably to the cost of every process allocating a database block sized * chunk of private storage that will be seldom if ever used (SE 3/2005). */ if (0 != csd->blks_to_upgrd && csd->blk_size > reformat_buffer_len) { /* Buffer not big enough (or does not exist) .. get a new one releasing old if it exists */ assert(0 == fast_lock_count); /* this is mainline (non-interrupt) code */ ++fast_lock_count; /* No interrupts across this use of reformat_buffer */ /* reformat_buffer_in_use should always be incremented only AFTER incrementing fast_lock_count * as it is the latter that prevents interrupts from using the reformat buffer. Similarly * the decrement of fast_lock_count should be done AFTER decrementing reformat_buffer_in_use. */ assert(0 == reformat_buffer_in_use); DEBUG_ONLY(reformat_buffer_in_use++); if (reformat_buffer) free(reformat_buffer); /* Different blksized databases in use .. keep only largest one */ reformat_buffer = malloc(csd->blk_size); reformat_buffer_len = csd->blk_size; DEBUG_ONLY(reformat_buffer_in_use--); assert(0 == reformat_buffer_in_use); --fast_lock_count; } assert(MV_STR & (TREF(gbuff_limit)).mvtype); if (mu_reorg_process && (0 == (TREF(gbuff_limit)).str.len)) { /* if the environment variable wasn't supplied, use the default for REORG */ (TREF(gbuff_limit)).str.len = SIZEOF(REORG_GBUFF_LIMIT); (TREF(gbuff_limit)).str.addr = malloc(SIZEOF(REORG_GBUFF_LIMIT)); memcpy((TREF(gbuff_limit)).str.addr, REORG_GBUFF_LIMIT, SIZEOF(REORG_GBUFF_LIMIT)); } if ((mu_reorg_process DEBUG_ONLY(|| IS_GTM_IMAGE)) && (0 != (TREF(gbuff_limit)).str.len)) { /* if reorg or dbg apply env var */ set_gbuff_limit(&csa, &csd, &(TREF(gbuff_limit))); # ifdef DEBUG if ((process_id & 2) && (process_id & (csd->n_bts - 1))) /* also randomize our_midnite */ { csa->our_midnite = csa->acc_meth.bg.cache_state->cache_array + csd->bt_buckets; csa->our_midnite += (process_id & (csd->n_bts - 1)); assert((csa->acc_meth.bg.cache_state->cache_array + csd->bt_buckets + csd->n_bts) > csa->our_midnite); cr = csa->our_midnite - csa->gbuff_limit; if (cr < csa->acc_meth.bg.cache_state->cache_array + csd->bt_buckets) cr += csd->n_bts; csa->our_lru_cache_rec_off = GDS_ANY_ABS2REL(csa, cr); } assert((csa->acc_meth.bg.cache_state->cache_array + csd->bt_buckets) < csa->our_midnite); # endif } } if (IS_ACC_METH_BG_OR_MM(reg_acc_meth)) { /* Determine fid_index of current region's file_id across sorted file_ids of all regions open until now. * All regions which have a file_id lesser than that of current region will have no change to their fid_index * All regions which have a file_id greater than that of current region will have their fid_index incremented by 1 * The fid_index determination algorithm below has an optimization in that if the current region's file_id is * determined to be greater than that of a particular region, then all regions whose fid_index is lesser * than that particular region's fid_index are guaranteed to have a lesser file_id than the current region * so we do not compare those against the current region's file_id. * Note that the sorting is done only on DB/MM regions. GT.CM/DDP regions should not be part of TP transactions, * hence they will not be sorted. */ prevcsa = NULL; reg_fid = &(csa->nl->unique_id); max_fid_index = 1; for (regcsa = cs_addrs_list; NULL != regcsa; regcsa = regcsa->next_csa) { onln_rlbk_cycle_mismatch |= (regcsa->db_onln_rlbkd_cycle != regcsa->nl->db_onln_rlbkd_cycle); if ((NULL != prevcsa) && (regcsa->fid_index < prevcsa->fid_index)) continue; tmp_reg_fid = &((regcsa)->nl->unique_id); if (0 < gdid_cmp(&(reg_fid->uid), &(tmp_reg_fid->uid))) { if ((NULL == prevcsa) || (regcsa->fid_index > prevcsa->fid_index)) prevcsa = regcsa; } else { regcsa->fid_index++; max_fid_index = MAX(max_fid_index, regcsa->fid_index); } } if (NULL == prevcsa) csa->fid_index = 1; else { csa->fid_index = prevcsa->fid_index + 1; max_fid_index = MAX(max_fid_index, csa->fid_index); } if (onln_rlbk_cycle_mismatch) { csa->root_search_cycle--; if (REPL_ALLOWED(csd)) { csa->onln_rlbk_cycle--; csa->db_onln_rlbkd_cycle--; } } /* Add current csa into list of open csas */ csa->next_csa = cs_addrs_list; cs_addrs_list = csa; /* Also update tp_reg_list fid_index's as insert_region relies on it */ for (tr = tp_reg_list; NULL != tr; tr = tr->fPtr) tr->fid_index = (&FILE_INFO(tr->reg)->s_addrs)->fid_index; DBG_CHECK_TP_REG_LIST_SORTING(tp_reg_list); TREF(max_fid_index) = max_fid_index; } if (pool_init && REPL_ALLOWED(csd) && jnlpool_init_needed) { /* Last parameter to VALIDATE_INITIALIZED_JNLPOOL is TRUE if the process does logical updates and FALSE otherwise. * This parameter governs whether the macro can do SCNDDBNOUPD check or not. All the utilities that sets * jnlpool_init_needed global variable don't do logical updates (REORG, EXTEND, etc.). But, for GT.M, * jnlpool_init_needed is set to TRUE unconditionally. Even though GT.M can do logical updates, we pass FALSE * unconditionally to the macro (indicating no logical updates). This is because, at this point, there is no way to * tell if this process wants to open the database for read or write operation. If it is for a read operation, we * don't want the below macro to issue SCNDDBNOUPD error. If it is for write operation, we will skip the * SCNDDBNOUPD error message here. But, eventually when this process goes to gvcst_{put,kill} or op_ztrigger, * SCNDDBNOUPD is issued. */ save_jnlpool = jnlpool; if (IS_GTM_IMAGE) { /* csa->jnlpool not set until validate jnlpool so need to compare gd_id of instance file */ if (!replfilegdid_valid) { assertpro(replpool_valid || REPL_INST_AVAILABLE(addr)); /* if any pool inited this should succeed */ status = filename_to_id(&replfile_gdid, replpool_id.instfilename); replfilegdid_valid = (SS_NORMAL == status); } if (replfilegdid_valid) { if (jnlpool && jnlpool->pool_init) { tmp_gdid = &FILE_ID(jnlpool->jnlpool_dummy_reg); if (!gdid_cmp(tmp_gdid, &replfile_gdid)) jnlpool_found = TRUE; } if (!jnlpool_found) for (jnlpool = jnlpool_head; jnlpool; jnlpool = jnlpool->next) if (jnlpool->pool_init) { tmp_gdid = &FILE_ID(jnlpool->jnlpool_dummy_reg); if (!gdid_cmp(tmp_gdid, &replfile_gdid)) { jnlpool_found = TRUE; break; } } if (!jnlpool_found) { jnlpool_init(GTMRELAXED, (boolean_t)FALSE, (boolean_t *)NULL, addr); tmp_gdid = &FILE_ID(jnlpool->jnlpool_dummy_reg); if (!gdid_cmp(tmp_gdid, &replfile_gdid)) jnlpool_found = TRUE; } } } else { assertpro(jnlpool); /* only one for utilities */ jnlpool_found = TRUE; } if (jnlpool_found) VALIDATE_INITIALIZED_JNLPOOL(csa, csa->nl, reg, GTMRELAXED, SCNDDBNOUPD_CHECK_FALSE); if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; } /* At this point, the database is officially open but one of two condition can exist here where we need to do more work: * 1. This was a normal database open and this process has opted-in for global shared stats so we need to also open * the associated statsDB (IF the baseDB does not have the NOSTATS qualifier). * 2. We are opening a statsDB in which case we are guaranteed the baseDB has already been open (assert at start of * this function). The baseDB open would have done the needed initialization for the statsDB in a lot of cases. * But in case it did not (e.g. TREF(statshare_opted_in) is FALSE), keep the statsDB open read-only until the * initialization happens later (when TREF(statshare_opted_in) becomes TRUE again). * Note since this database is officially opened and has gone through db_init(), the current reservedDBFlags in all of * region, sgmnt_addrs, and sgmnt_data are sync'd to the value that was in sgmnt_data (fileheader). */ if (!IS_DSE_IMAGE) { /* DSE does not open statsdb automatically. It does it only when asked to */ if (NO_STATS_OPTIN != TREF(statshare_opted_in)) { if (!is_statsDB) { /* This is a baseDB - so long if all in, we should initialize the statsDB */ if (!(RDBF_NOSTATS & reg->reservedDBFlags) && (ALL_STATS_OPTIN == TREF(statshare_opted_in))) { BASEDBREG_TO_STATSDBREG(reg, statsDBreg); assert(NULL != statsDBreg); if (!statsDBreg->statsDB_setup_started) { /* The associated statsDB may or may not be open but it hasn't been initialized * so take care of that now. Put a handler around it so if the open of the statsDB * file fails for whatever reason, we can ignore it and just keep going. */ statsDBreg->statsDB_setup_started = TRUE; gvcst_init_statsDB(reg, DO_STATSDB_INIT_TRUE); } } else csa->reservedDBFlags |= RDBF_NOSTATS; } } else if (is_statsDB) { /* We are opening a statsDB file but not as a statsDB (i.e. not opted-in) but we still need to set the * database to R/O mode so it is only updated by GTM's C code and never in M mode which could lead to * too-small records being added that would cause the gvstats records to move which must never happen. * For DSE, we want it to open the db R/W (in case we need some repair of the statsdb). * So skip this R/O setting for DSE. */ assert(!reg->was_open); reg->read_only = TRUE; csa->read_write = FALSE; /* Maintain read_only/read_write in parallel */ } } return; } fis-gtm-V7.0-005/sr_port/gvcst_jrt_null.c0000644000032200000250000000421714342376331017252 0ustar librarygtc/**************************************************************** * * * Copyright 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for jnl.h */ #include "jnl.h" #include "t_end.h" /* prototypes */ #include "t_retry.h" #include "t_begin.h" #include "gvcst_jrt_null.h" /* for gvcst_jrt_null prototype */ #include "jnl_get_checksum.h" GBLREF jnl_format_buffer *non_tp_jfb_ptr; GBLREF jnl_gbls_t jgbl; #ifdef DEBUG GBLREF uint4 dollar_tlevel; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF unsigned int t_tries; #endif error_def(ERR_JRTNULLFAIL); void gvcst_jrt_null() { enum cdb_sc status; struct_jrec_null *rec; assert(NULL != gv_cur_region); assert(NULL != cs_addrs); assert(cs_addrs == &FILE_INFO(gv_cur_region)->s_addrs); t_begin(ERR_JRTNULLFAIL, UPDTRNS_DB_UPDATED_MASK); assert(!dollar_tlevel); assert(t_tries < CDB_STAGNATE || cs_addrs->now_crit); /* we better hold crit in the final retry */ non_tp_jfb_ptr->rectype = JRT_NULL; non_tp_jfb_ptr->record_size = NULL_RECLEN; non_tp_jfb_ptr->checksum = INIT_CHECKSUM_SEED; rec = (struct_jrec_null *)non_tp_jfb_ptr->buff; rec->prefix.jrec_type = JRT_NULL; rec->prefix.forwptr = NULL_RECLEN; rec->prefix.checksum = INIT_CHECKSUM_SEED; rec->suffix.backptr = NULL_RECLEN; rec->suffix.suffix_code = JNL_REC_SUFFIX_CODE; rec->filler = 0; jgbl.cumul_jnl_rec_len = NULL_RECLEN; /* The rest of the initialization is taken care of by jnl_write_logical (invoked in t_end below) */ DEBUG_ONLY(jgbl.cumul_index = 1;) for (;;) { DEBUG_ONLY(jgbl.cu_jnl_index = 0;) if ((trans_num)0 == t_end(NULL, NULL, TN_NOT_SPECIFIED)) continue; return; } } fis-gtm-V7.0-005/sr_port/gvcst_jrt_null.h0000644000032200000250000000115614342376331017256 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2018 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. * * * ****************************************************************/ #ifndef GVCST_JRT_NULL_H_INCLUDED #define GVCST_JRT_NULL_H_INCLUDED void gvcst_jrt_null(void); #endif fis-gtm-V7.0-005/sr_port/gvcst_kill.c0000644000032200000250000011166714342376333016366 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_string.h" #include "gtm_stdlib.h" #include "gtm_stdio.h" #include "gtm_inet.h" /* Required for gtmsource.h */ #include "gtmio.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "jnl.h" #include "copy.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "repl_msg.h" #include "gtmsource.h" #include "interlock.h" #include #include "stack_frame.h" #include "gv_trigger.h" #include "gtm_trigger.h" #include "gv_trigger_protos.h" #include "mv_stent.h" #include "stringpool.h" #include "gtm_trigger_trc.h" #include "tp_frame.h" #include "tp_restart.h" #include "is_file_identical.h" #include "anticipatory_freeze.h" #include "gvt_inline.h" /* Include prototypes */ #include "gvcst_kill_blk.h" #include "t_qread.h" #include "t_end.h" #include "t_retry.h" #include "t_begin.h" #include "gvcst_expand_free_subtree.h" #include "gvcst_protos.h" /* for gvcst_kill,gvcst_search prototype */ #include "rc_cpt_ops.h" #include "add_inter.h" #include "sleep_cnt.h" #include "wcs_sleep.h" #include "wbox_test_init.h" #include "memcoherency.h" #include "util.h" #include "op.h" /* for op_tstart prototype */ #include "format_targ_key.h" /* for format_targ_key prototype */ #include "tp_set_sgm.h" /* for tp_set_sgm prototype */ #include "op_tcommit.h" /* for op_tcommit prototype */ #include "have_crit.h" #include "error.h" #include "gtmimagename.h" /* needed for spanning nodes */ #include "gtm_repl_multi_inst.h" /* for DISALLOW_MULTIINST_UPDATE_IN_TP */ GBLREF gd_region *gv_cur_region; GBLREF gv_key *gv_currkey, *gv_altkey; GBLREF int4 gv_keysize; GBLREF gv_namehead *gv_target; GBLREF jnl_fence_control jnl_fence_ctl; GBLREF kill_set *kill_set_tail; GBLREF uint4 dollar_tlevel; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF sgm_info *sgm_info_ptr; GBLREF sgm_info *first_sgm_info; GBLREF unsigned char cw_set_depth; GBLREF unsigned int t_tries; GBLREF boolean_t need_kip_incr; GBLREF uint4 update_trans; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnlpool_addrs_ptr_t jnlpool_head; GBLREF sgmnt_addrs *kip_csa; GBLREF boolean_t skip_dbtriggers; /* see gbldefs.c for description of this global */ GBLREF stack_frame *frame_pointer; GBLREF boolean_t gv_play_duplicate_kills; #ifdef GTM_TRIGGER GBLREF int tprestart_state; GBLREF int4 gtm_trigger_depth; GBLREF int4 tstart_trigger_depth; GBLREF boolean_t skip_INVOKE_RESTART; GBLREF boolean_t ztwormhole_used; /* TRUE if $ztwormhole was used by trigger code */ GBLREF mval dollar_ztwormhole; GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; #endif #ifdef DEBUG GBLREF char *update_array, *update_array_ptr; GBLREF uint4 update_array_size; /* needed for the ENSURE_UPDATE_ARRAY_SPACE macro */ GBLREF jnl_gbls_t jgbl; GBLREF boolean_t donot_INVOKE_MUMTSTART; #endif GBLREF boolean_t span_nodes_disallowed; error_def(ERR_TPRETRY); error_def(ERR_GVKILLFAIL); #ifdef GTM_TRIGGER LITREF mval literal_null; LITREF mval *fndata_table[2][2]; #endif LITREF mval literal_batch; #define GOTO_RETRY(CDB_STATUS, SKIP_ASSERT) \ { \ assert((CDB_STAGNATE > t_tries) || is_final_retry_code(CDB_STATUS) || SKIP_ASSERT); \ goto retry; \ } DEFINE_NSB_CONDITION_HANDLER(gvcst_kill_ch) void gvcst_kill(boolean_t do_subtree) { boolean_t spanstat; boolean_t sn_tpwrapped; boolean_t est_first_pass; int oldend; int save_dollar_tlevel; DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); if (do_subtree) { /* If we're killing the whole subtree, that includes any spanning nodes. No need to do anything special */ gvcst_kill2(TRUE, NULL, FALSE); assert(save_dollar_tlevel == dollar_tlevel); return; } else { /* Attempt to zkill node, but abort if we might have a spanning node */ spanstat = 0; gvcst_kill2(FALSE, &spanstat, FALSE); assert(save_dollar_tlevel == dollar_tlevel); if (!spanstat) return; } RTS_ERROR_IF_SN_DISALLOWED; # ifdef DEBUG /* ^%Y* should never be killed (op_gvkill/op_gvzwithdraw would have issued a ERR_PCTYRESERVED error in that case). * The only exception is if we are removing a ^%YGS record from the statsdb (caller "gvcst_remove_statsDB_linkage" * but in that case we would have reg->read_only set to FALSE for a statsdb region name. Account for that. */ assert((RESERVED_NAMESPACE_LEN > gv_currkey->end) || (0 != MEMCMP_LIT(gv_currkey->base, RESERVED_NAMESPACE)) || (IS_STATSDB_REGNAME(gv_cur_region) && !gv_cur_region->read_only)); # endif oldend = gv_currkey->end; /* Almost certainly have a spanning node to zkill. So start a TP transaction to deal with it. */ if (!dollar_tlevel) { sn_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART + IMPLICIT_TRIGGER_TSTART), TRUE, &literal_batch, 0); assert(!donot_INVOKE_MUMTSTART); DEBUG_ONLY(donot_INVOKE_MUMTSTART = TRUE); ESTABLISH_NORET(gvcst_kill_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); } else sn_tpwrapped = FALSE; /* Fire any triggers FIRST, then proceed with the kill. If we started a lcl_implicit transaction in first gvcst_kill * triggers were rolled back. So if span_status indicates TRLBKTRIG, do them again. * Otherwise, skip triggers because they were either kept or didn't happen. * What if new triggers added between above gvcst_kill and now? Should cause a restart because trigger cycle changes? */ if (sn_tpwrapped) gvcst_kill2(FALSE, NULL, FALSE); /* zkill primary dummy node <--- jnling + trigs happen here */ /* kill any existing hidden subscripts */ APPEND_HIDDEN_SUB(gv_currkey); /* append "0211" to gv_currkey */ gvcst_kill2(FALSE, NULL, TRUE); RESTORE_CURRKEY(gv_currkey, oldend); if (sn_tpwrapped) { op_tcommit(); DEBUG_ONLY(donot_INVOKE_MUMTSTART = FALSE); REVERT; /* remove our condition handler */ } assert(save_dollar_tlevel == dollar_tlevel); } void gvcst_kill2(boolean_t do_subtree, boolean_t *span_status, boolean_t killing_chunks) { block_id gvt_root; boolean_t clue, flush_cache; boolean_t next_fenced_was_null, write_logical_jnlrecs, jnl_format_done; boolean_t left_extra, right_extra; boolean_t want_root_search = FALSE, is_dummy, succeeded, key_exists; rec_hdr_ptr_t rp; uint4 lcl_onln_rlbkd_cycle; int data_len, cur_val_offset; unsigned short rec_size; cw_set_element *tp_cse; enum cdb_sc cdb_status; int lev, end, target_key_size; uint4 prev_update_trans, actual_update; jnl_format_buffer *jfb, *ztworm_jfb; jnl_action_code operation; kill_set kill_set_head, *ks, *temp_ks; node_local_ptr_t cnl; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; srch_blk_status *bh, *left, *right; srch_hist *gvt_hist, *alt_hist, *dir_hist; srch_rec_status *left_rec_stat, local_srch_rec; uint4 segment_update_array_size; unsigned char *base; int lcl_dollar_tlevel, rc; uint4 nodeflags; gv_namehead *save_targ; sgm_info *si; # ifdef GTM_TRIGGER mint dlr_data; boolean_t is_tpwrap; boolean_t lcl_implicit_tstart; /* local copy of the global variable "implicit_tstart" */ gtm_trigger_parms trigparms; gvt_trigger_t *gvt_trigger; gvtr_invoke_parms_t gvtr_parms; int gtm_trig_status, idx; unsigned char *save_msp; mv_stent *save_mv_chain; mval *ztold_mval = NULL, ztvalue_new, ztworm_val; # endif # ifdef DEBUG boolean_t is_mm, root_search_done = FALSE; uint4 dbg_research_cnt; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; csa = cs_addrs; csd = csa->hdr; cnl = csa->nl; DEBUG_ONLY(is_mm = (dba_mm == csd->acc_meth)); GTMTRIG_ONLY( TRIG_CHECK_REPLSTATE_MATCHES_EXPLICIT_UPDATE(gv_cur_region, csa); if (IS_EXPLICIT_UPDATE) { /* This is an explicit update. Set ztwormhole_used to FALSE. Note that we initialize this only at the * beginning of the transaction and not at the beginning of each try/retry. If the application used * $ztwormhole in any retsarting try of the transaction, we consider it necessary to write the * TZTWORM/UZTWORM record even though it was not used in the succeeding/committing try. */ ztwormhole_used = FALSE; } ) JNLPOOL_INIT_IF_NEEDED(csa, csd, cnl, SCNDDBNOUPD_CHECK_TRUE); if (!dollar_tlevel) { kill_set_head.next_kill_set = NULL; if (jnl_fence_ctl.level) /* next_fenced_was_null is reliable only if we are in ZTransaction */ next_fenced_was_null = (NULL == csa->next_fenced) ? TRUE : FALSE; /* In case of non-TP explicit updates that invoke triggers the kills happen inside of TP. If those kills * dont cause any actual update, we need prev_update_trans set appropriately so update_trans can be reset. */ GTMTRIG_ONLY(prev_update_trans = 0); } else prev_update_trans = sgm_info_ptr->update_trans; assert(('\0' != gv_currkey->base[0]) && gv_currkey->end); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); DISALLOW_MULTIINST_UPDATE_IN_TP(dollar_tlevel, jnlpool_head, csa, first_sgm_info, FALSE); T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_GVKILLFAIL); assert(NULL != update_array); assert(NULL != update_array_ptr); assert(0 != update_array_size); assert(update_array + update_array_size >= update_array_ptr); GTMTRIG_ONLY( lcl_implicit_tstart = FALSE; trigparms.ztvalue_new = NULL; ) operation = (do_subtree ? JNL_KILL : JNL_ZKILL); for ( ; ; ) { actual_update = 0; # ifdef GTM_TRIGGER gvtr_parms.num_triggers_invoked = 0; /* clear any leftover value */ is_tpwrap = FALSE; /* No trigger ^#t reads needed if skip_dbtriggers is TRUE (e.g. mupip load etc.) */ if (!skip_dbtriggers) { gvt_root = gv_target->root; /* store root before macro call to later compare */ GVTR_INIT_AND_TPWRAP_IF_NEEDED(csa, csd, gv_target, gvt_trigger, lcl_implicit_tstart, is_tpwrap, ERR_GVKILLFAIL); assert(gvt_trigger == gv_target->gvt_trigger); if (gvt_root && (0 == gv_target->root)) { /* gv_target->root was non-zero BEFORE call to "gvtr_init" (as part of GVTR_INIT_... macro above) * but zero AFTER the call. This is possible only if it restarted internally due to a concurrent * online rollback or reorg truncate that moved GVT root blocks. Refetch gv_target->root. */ assert(dollar_tlevel && t_tries && lcl_implicit_tstart); want_root_search = TRUE; } } assert((0 != gv_target->root) || want_root_search || gv_play_duplicate_kills); /* finish off any pending root search from previous retry */ REDO_ROOT_SEARCH_IF_NEEDED(want_root_search, cdb_status); if (cdb_sc_normal != cdb_status) { /* gvcst_root_search invoked from REDO_ROOT_SEARCH_IF_NEEDED ended up with a restart situation but did not * actually invoke t_retry. Instead, it returned control back to us asking us to restart. * Cannot enable assert (which has an assert about t_tries < CDB_STAGNATE) because it is possible for us * to get cdb_sc_gvtrootmod2 restart when t_tries == CDB_STAGNATE. Pass GOTO_RETRY parameter accordingly. */ GOTO_RETRY(cdb_status, SKIP_ASSERT_FALSE); } # endif /* Need to reinitialize gvt_hist & alt_hist for each try as it might have got set to a value in the previous * retry that is inappropriate for this try (e.g. gvt_root value changed between tries). */ gvt_hist = &gv_target->hist; gvt_root = gv_target->root; /* refetch root in case it changed due to retries in this for loop */ if (!gvt_root) { assert(gv_play_duplicate_kills); /* No GVT for this global. So nothing to kill. But since we have to play the duplicate kill * (asserted above) we cannot return at this point. There are two cases. * (1) If we hold crit on the region at this point, then there is no way a concurrent update * could create a GVT for this global. So no ned of any extra history recordkeeping. * (2) If we dont hold crit though, this scenario is possible. Handle this by searching for * the global name in the directory tree and verifying that it is still missing. * a) If YES, then pass this history on to t_end/tp_hist as part of the duplicate kill play action. * This way we make sure no other process concurrently creates the GVT while we process this * duplicate kill transaction. * b) If NO, then some other process has created a GVT for this global after we started this * transaction. In this case we will have to first get the non-zero root block number of this * GVT and then use it in the rest of this function. Use the function "gvcst_redo_root_search" * for this purpose. It is possible that the root block is set to 0 even after the call to the * above function (because of a concurrent KILL of this GVT after we saw a non-zero value but * before the "gvcst_redo_root_search" invocation). In this case, we are back to square one. * So we redo the logic in this entire comment again each time going through t_retry. * In the 3rd retry, we will get crit and that time we will break out of this block of code * and move on to the real kill. */ alt_hist = NULL; if (csa->now_crit) { /* Case (1) : Clear history in case this gets passed to t_end/tp_hist later below */ gvt_hist->h[0].blk_num = 0; } else { /* Case (2) : Search for global name in directory tree */ save_targ = gv_target; SET_GV_ALTKEY_TO_GBLNAME_FROM_GV_CURRKEY; /* set up gv_altkey to be just the gblname */ gv_target = csa->dir_tree; dir_hist = &gv_target->hist; cdb_status = gvcst_search(gv_altkey, NULL); RESET_GV_TARGET_LCL(save_targ); if (cdb_sc_normal != cdb_status) { /* Retry the transaction. But reset directory tree clue as it is suspect at this point. * Needed as t_retry only resets clue of gv_target which is not the directory tree anymore. */ csa->dir_tree->clue.end = 0; GOTO_RETRY(cdb_status, SKIP_ASSERT_FALSE); } if ((gv_altkey->end + 1) == dir_hist->h[0].curr_rec.match) { /* Case (2b) : GVT now exists for this global */ cdb_status = cdb_sc_gvtrootmod; GOTO_RETRY(cdb_status, SKIP_ASSERT_FALSE); } else { /* Case (2a) : GVT does not exist for this global */ gvt_hist = dir_hist; /* validate directory tree history in t_end/tp_hist */ } } } else alt_hist = gv_target->alt_hist; DEBUG_ONLY(dbg_research_cnt = 0;) jnl_format_done = FALSE; write_logical_jnlrecs = JNL_WRITE_LOGICAL_RECS(csa); # ifdef GTM_TRIGGER dlr_data = 0; /* No trigger invocation needed if skip_dbtriggers is TRUE (e.g. mupip load etc.). * If gvt_root is 0 (possible only if gv_play_duplicate_kills is TRUE) we want to only journal the * kill but not touch the database or invoke triggers. So skip triggers in that case too. */ if (!skip_dbtriggers && (NULL != gvt_trigger) && gvt_root && !killing_chunks) { PUSH_ZTOLDMVAL_ON_M_STACK(ztold_mval, save_msp, save_mv_chain); /* Determine $ZTOLDVAL & $ZTDATA to fill in trigparms */ dlr_data = DG_DATAGET; /* tell dataget we want full info regarding descendants */ cdb_status = gvcst_dataget(&dlr_data, ztold_mval); if (cdb_sc_normal != cdb_status) GOTO_RETRY(cdb_status, SKIP_ASSERT_FALSE); assert((11 >= dlr_data) && (1 >= (dlr_data % 10))); /* Invoke triggers for KILL as long as $data is nonzero (1 or 10 or 11). * Invoke triggers for ZKILL only if $data is 1 or 11 (for 10 case, ZKILL is a no-op). */ if (do_subtree ? dlr_data : (dlr_data & 1)) { /* Either node or its descendants exists. Invoke KILL triggers for this node. * But first write journal records (ZTWORM and/or KILL) for the triggering nupdate. * "ztworm_jfb", "jfb" and "jnl_format_done" are set by the below macro. */ JNL_FORMAT_ZTWORM_IF_NEEDED(csa, write_logical_jnlrecs, operation, gv_currkey, NULL, ztworm_jfb, jfb, jnl_format_done); /* Initialize trigger parms that dont depend on the context of the matching trigger */ trigparms.ztoldval_new = ztold_mval; trigparms.ztdata_new = fndata_table[dlr_data / 10][dlr_data & 1]; if (NULL == trigparms.ztvalue_new) { /* Do not pass literal_null directly since $ztval can be modified inside trigger * code and literal_null is in read-only segment so will not be modifiable. * Hence the need for a dummy local variable mval "ztvalue_new" in the C stack. */ ztvalue_new = literal_null; trigparms.ztvalue_new = &ztvalue_new; } gvtr_parms.gvtr_cmd = do_subtree ? GVTR_CMDTYPE_KILL : GVTR_CMDTYPE_ZKILL; gvtr_parms.gvt_trigger = gvt_trigger; /* Now that we have filled in minimal information, let "gvtr_match_n_invoke" do the rest */ gtm_trig_status = gvtr_match_n_invoke(&trigparms, &gvtr_parms); INCR_GVSTATS_COUNTER(csa, csa->nl, n_kill_trigger_fired, gvtr_parms.num_triggers_invoked); assert((0 == gtm_trig_status) || (ERR_TPRETRY == gtm_trig_status)); if (ERR_TPRETRY == gtm_trig_status) { /* A restart has been signaled that we need to handle or complete the handling of. * This restart could have occurred reading the trigger in which case no * tp_restart() has yet been done or it could have occurred in trigger code in * which case we need to finish the incomplete tp_restart. In both cases this * must be an implicitly TP wrapped transaction. Our action is to complete the * necessary tp_restart() logic (t_retry is already completed so should be skipped) * and then re-do the gvcst_kill logic. */ assert(lcl_implicit_tstart || *span_status); cdb_status = cdb_sc_normal; /* signal "retry:" to avoid t_retry call */ assert(CDB_STAGNATE >= t_tries); GOTO_RETRY(cdb_status, SKIP_ASSERT_TRUE); /* Need to skip assert because t_tries * can be == CDB_STAGNATE. */ } REMOVE_ZTWORM_JFB_IF_NEEDED(ztworm_jfb, jfb, sgm_info_ptr); } /* else : we dont invoke any KILL/ZTKILL type triggers for a node whose $data is 0 */ POP_MVALS_FROM_M_STACK_IF_NEEDED(ztold_mval, save_msp, save_mv_chain); } # endif assert(csd == cs_data); /* assert csd is in sync with cs_data even if there were MM db file extensions */ assert(csd == csa->hdr); si = sgm_info_ptr; /* Has to be AFTER the GVTR_INIT_AND_TPWRAP_IF_NEEDED macro in case that sets * sgm_info_ptr to a non-NULL value (if a non-TP transaction is tp wrapped for triggers). */ assert(t_tries < CDB_STAGNATE || csa->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ if (!dollar_tlevel) { CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ kill_set_tail = &kill_set_head; for (ks = &kill_set_head; NULL != ks; ks = ks->next_kill_set) ks->used = 0; } else { segment_update_array_size = UA_NON_BM_SIZE(csd); ENSURE_UPDATE_ARRAY_SPACE(segment_update_array_size); } clue = (0 != gv_target->clue.end); research: if (gvt_root) { #if defined(DEBUG) if (gtm_white_box_test_case_enabled && (WBTEST_ANTIFREEZE_GVKILLFAIL == gtm_white_box_test_case_number)) { cdb_status = cdb_sc_blknumerr; /* Skip assert inside GOTO_RETRY macro as the WBTEST_ANTIFREEZE_GVKILLFAIL white-box testcase * intentionally triggers a GVKILLFAIL error. */ GOTO_RETRY(cdb_status, SKIP_ASSERT_TRUE); } #endif if (cdb_sc_normal != (cdb_status = gvcst_search(gv_currkey, NULL))) GOTO_RETRY(cdb_status, SKIP_ASSERT_FALSE); assert(gv_altkey->top == gv_currkey->top); assert(gv_altkey->top == gv_keysize); end = gv_currkey->end; assert(end < gv_currkey->top); memcpy(gv_altkey, gv_currkey, SIZEOF(gv_key) + end + 1); base = &gv_altkey->base[0]; if (do_subtree) { base[end - 1] = 1; assert(KEY_DELIMITER == base[end]); base[++end] = KEY_DELIMITER; } else { target_key_size = gv_currkey->end + 1; bh = &gv_target->hist.h[0]; key_exists = (target_key_size == bh->curr_rec.match); if (key_exists) { /* check for spanning node dummy value: a single zero byte */ rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)bh->buffaddr + bh->curr_rec.offset); GET_USHORT(rec_size, &rp->rsiz); cur_val_offset = SIZEOF(rec_hdr) + target_key_size - EVAL_CMPC((rec_hdr_ptr_t)rp); data_len = rec_size - cur_val_offset; is_dummy = IS_SN_DUMMY(data_len, (sm_uc_ptr_t)rp + cur_val_offset); if (is_dummy && (NULL != span_status) && !(span_nodes_disallowed && csd->span_node_absent)) { need_kip_incr = FALSE; if (!dollar_tlevel) { update_trans = 0; succeeded = ((trans_num)0 != t_end(gvt_hist, NULL, TN_NOT_SPECIFIED)); if (!succeeded) { /* see other t_end */ assert((NULL == kip_csa) && (csd == cs_data)); update_trans = UPDTRNS_DB_UPDATED_MASK; continue; } *span_status = TRUE; return; } else { cdb_status = tp_hist(NULL); if (cdb_sc_normal != cdb_status) GOTO_RETRY(cdb_status, SKIP_ASSERT_FALSE); *span_status = TRUE; # ifdef GTM_TRIGGER if (lcl_implicit_tstart) { /* Rollback triggers */ OP_TROLLBACK(-1); return; } # endif /* do not return in case of entering with TP, still set span_status */ } } } if (killing_chunks) { /* Second call of gvcst_kill2 within TP transaction in gvcst_kill * Kill all hidden subscripts... */ base[end - 3] = STR_SUB_MAXVAL; base[end - 2] = STR_SUB_MAXVAL; base[end - 1] = KEY_DELIMITER; base[end - 0] = KEY_DELIMITER; } else { base[end] = 1; base[++end] = KEY_DELIMITER; base[++end] = KEY_DELIMITER; } } gv_altkey->end = end; if (cdb_sc_normal != (cdb_status = gvcst_search(gv_altkey, alt_hist))) GOTO_RETRY(cdb_status, SKIP_ASSERT_FALSE); if (alt_hist->depth != gvt_hist->depth) { cdb_status = cdb_sc_badlvl; GOTO_RETRY(cdb_status, SKIP_ASSERT_FALSE); } right_extra = FALSE; left_extra = TRUE; for (lev = 0; 0 != gvt_hist->h[lev].blk_num; ++lev) { left = &gvt_hist->h[lev]; right = &alt_hist->h[lev]; assert(0 != right->blk_num); left_rec_stat = left_extra ? &left->prev_rec : &left->curr_rec; if (left->blk_num == right->blk_num) { cdb_status = gvcst_kill_blk(left, lev, gv_currkey, *left_rec_stat, right->curr_rec, right_extra, &tp_cse); assert(!dollar_tlevel || (NULL == tp_cse) || (left->cse == tp_cse)); assert( dollar_tlevel || (NULL == tp_cse)); if (tp_cse) actual_update = UPDTRNS_DB_UPDATED_MASK; if (cdb_sc_normal == cdb_status) break; gv_target->clue.end = 0; /* If need to go up from leaf (or higher), history wont be valid */ if (clue) { /* Clue history valid only for data block, need to re-search */ clue = FALSE; DEBUG_ONLY(dbg_research_cnt++;) goto research; } if (cdb_sc_delete_parent != cdb_status) GOTO_RETRY(cdb_status, SKIP_ASSERT_FALSE); left_extra = right_extra = TRUE; } else { gv_target->clue.end = 0; /* If more than one block involved, history will not be valid */ if (clue) { /* Clue history valid only for data block, need to re-search */ clue = FALSE; DEBUG_ONLY(dbg_research_cnt++;) goto research; } local_srch_rec.offset = ((blk_hdr_ptr_t)left->buffaddr)->bsiz; local_srch_rec.match = 0; cdb_status = gvcst_kill_blk(left, lev, gv_currkey, *left_rec_stat, local_srch_rec, FALSE, &tp_cse); assert(!dollar_tlevel || (NULL == tp_cse) || (left->cse == tp_cse)); assert( dollar_tlevel || (NULL == tp_cse)); if (tp_cse) actual_update = UPDTRNS_DB_UPDATED_MASK; if (cdb_sc_normal == cdb_status) left_extra = FALSE; else if (cdb_sc_delete_parent == cdb_status) { left_extra = TRUE; cdb_status = cdb_sc_normal; } else GOTO_RETRY(cdb_status, SKIP_ASSERT_FALSE); local_srch_rec.offset = local_srch_rec.match = 0; cdb_status = gvcst_kill_blk(right, lev, gv_altkey, local_srch_rec, right->curr_rec, right_extra, &tp_cse); assert(!dollar_tlevel || (NULL == tp_cse) || (right->cse == tp_cse)); assert( dollar_tlevel || (NULL == tp_cse)); if (tp_cse) actual_update = UPDTRNS_DB_UPDATED_MASK; if (cdb_sc_normal == cdb_status) right_extra = FALSE; else if (cdb_sc_delete_parent == cdb_status) { right_extra = TRUE; cdb_status = cdb_sc_normal; } else GOTO_RETRY(cdb_status, SKIP_ASSERT_FALSE); } } } if (!gv_play_duplicate_kills) { /* Determine whether the kill is going to update the db or not. If not, skip the kill altogether. * The variable "actual_update" is set accordingly below. */ if (!dollar_tlevel) { assert(!jnl_format_done); assert(0 == actual_update); /* for non-TP, tp_cse is NULL even if cw_set_depth is non-zero */ if (0 != cw_set_depth) actual_update = UPDTRNS_DB_UPDATED_MASK; /* Reset update_trans (to potentially non-zero value) in case it got set to 0 in previous retry. */ update_trans = actual_update; } else { # ifdef GTM_TRIGGER if (!actual_update) /* possible only if the node we are attempting to KILL does not exist now */ { /* Note that it is possible that the node existed at the time of the "gvcst_dataget" but * got killed later when we did the gvcst_search (right after the "research:" label). This * is possible if any triggers invoked in between KILLed the node and/or all its * descendants. We still want to consider this case as an actual update to the database * as far as journaling is concerned (this is because we have already formatted the * KILL journal record) so set actual_update to UPDTRNS_DB_UPDATED_MASK in this case. * Note that it is possible that the node does not exist now due to a restartable * situation (instead of due to a KILL inside trigger code). In that case, it is safe to * set actual_update to UPDTRNS_DB_UPDATED_MASK (even though we did not do any update to * the database) since we will be restarting anyways. For ZKILL, check if dlr_data was * 1 or 11 and for KILL, check if it was 1, 10 or 11. */ # ifdef DEBUG if (!gvtr_parms.num_triggers_invoked && (do_subtree ? dlr_data : (dlr_data & 1))) { /* Triggers were not invoked but still the node that existed a few * steps above does not exist now. This is a restartable situation. * Assert that. */ assert(!skip_dbtriggers); TREF(donot_commit) |= DONOTCOMMIT_GVCST_KILL_ZERO_TRIGGERS; } # endif if (do_subtree ? dlr_data : (dlr_data & 1)) actual_update = UPDTRNS_DB_UPDATED_MASK; } # endif NON_GTMTRIG_ONLY(assert(!jnl_format_done)); assert(!actual_update || si->cw_set_depth GTMTRIG_ONLY(|| gvtr_parms.num_triggers_invoked || TREF(donot_commit))); assert(!(prev_update_trans & ~UPDTRNS_VALID_MASK)); if (!actual_update) si->update_trans = prev_update_trans; /* restore status prior to redundant KILL */ } } else { /* Since "gv_play_duplicate_kills" is set, irrespective of whether the node/subtree to be * killed exists or not in the db, consider this as a db-updating kill. */ if (!dollar_tlevel) { assert(!jnl_format_done); assert(0 == actual_update); /* for non-TP, tp_cse is NULL even if cw_set_depth is non-zero */ /* See comment above IS_OK_TO_INVOKE_GVCST_KILL macro for why the below assert is the way it is */ assert(!jgbl.forw_phase_recovery || cw_set_depth || (JS_IS_DUPLICATE & jgbl.mur_jrec_nodeflags) || jgbl.mur_options_forward); if (0 != cw_set_depth) actual_update = UPDTRNS_DB_UPDATED_MASK; /* Set update_trans to TRUE unconditionally since we want to play duplicate kills */ update_trans = UPDTRNS_DB_UPDATED_MASK; } else { /* See comment above IS_OK_TO_INVOKE_GVCST_KILL macro for why the below assert is the way it is */ /*assert(!jgbl.forw_phase_recovery || actual_update || (JS_IS_DUPLICATE & jgbl.mur_jrec_nodeflags) || jgbl.mur_options_forward);*/ assert(si->update_trans); } } if (write_logical_jnlrecs && (actual_update || gv_play_duplicate_kills) && !killing_chunks) { /* Maintain journal records only if the kill actually resulted in a database update OR if * "gv_play_duplicate_kills" is TRUE. In the latter case, even though no db blocks will be touched, * it will still increment the db curr_tn and write jnl records. * * skip_dbtriggers is set to TRUE for trigger unsupporting platforms. So, nodeflags will be set to skip * triggers on secondary. This ensures that updates happening in primary (trigger unsupporting platform) * is treated in the same order in the secondary (trigger supporting platform) irrespective of whether * the secondary has defined triggers or not for the global that is being updated. */ if (!dollar_tlevel) { nodeflags = 0; if (skip_dbtriggers) nodeflags |= JS_SKIP_TRIGGERS_MASK; if (!actual_update) { assert(gv_play_duplicate_kills); nodeflags |= JS_IS_DUPLICATE; } assert(!jnl_format_done); jfb = jnl_format(operation, gv_currkey, NULL, nodeflags); assert(NULL != jfb); } else if (!jnl_format_done) { nodeflags = 0; if (skip_dbtriggers) nodeflags |= JS_SKIP_TRIGGERS_MASK; if (!actual_update) { assert(gv_play_duplicate_kills); nodeflags |= JS_IS_DUPLICATE; } # ifdef GTM_TRIGGER /* Do not replicate implicit updates */ assert(tstart_trigger_depth <= gtm_trigger_depth); if (gtm_trigger_depth > tstart_trigger_depth) { /* Ensure that JS_SKIP_TRIGGERS_MASK and JS_NOT_REPLICATED_MASK are mutually exclusive. */ assert(!(nodeflags & JS_SKIP_TRIGGERS_MASK)); nodeflags |= JS_NOT_REPLICATED_MASK; } # endif /* Write KILL journal record */ jfb = jnl_format(operation, gv_currkey, NULL, nodeflags); assert(NULL != jfb); } } flush_cache = FALSE; if (!dollar_tlevel) { if ((0 != csd->dsid) && (0 < kill_set_head.used) && (gvt_hist->h[1].blk_num != alt_hist->h[1].blk_num)) { /* multi-level delete */ rc_cpt_inval(); flush_cache = TRUE; } if (0 < kill_set_head.used) /* increase kill_in_prog */ { need_kip_incr = TRUE; if (!csa->now_crit) /* Do not sleep while holding crit */ WAIT_ON_INHIBIT_KILLS(cnl, MAXWAIT2KILL); } if ((trans_num)0 == t_end(gvt_hist, alt_hist, TN_NOT_SPECIFIED)) { assert(csd == cs_data); /* To ensure they are the same even if MM extensions happened in between */ if (jnl_fence_ctl.level && next_fenced_was_null && actual_update && write_logical_jnlrecs) { /* If ZTransaction and first KILL and the kill resulted in an update * Note that "write_logical_jnlrecs" is used above instead of JNL_WRITE_LOGICAL_RECS(csa) * since the value of the latter macro might have changed inside the call to t_end() * (since jnl state changes could change the JNL_ENABLED check which is part of the macro). */ assert(NULL != csa->next_fenced); assert(jnl_fence_ctl.fence_list == csa); jnl_fence_ctl.fence_list = csa->next_fenced; csa->next_fenced = NULL; } need_kip_incr = FALSE; assert(NULL == kip_csa); /* We could have entered gvcst_kill trying to kill a global that does not exist but later due to a * concurrent set, we are about to retry. In such a case, update_trans could have been set to 0 * (from actual_update above). Reset update_trans to non-zero for the next retry as we expect the * kill to happen in this retry. */ update_trans = UPDTRNS_DB_UPDATED_MASK; continue; } assert(csd == cs_data); /* To ensure they are the same even if MM extensions happened in between */ } else { cdb_status = tp_hist(alt_hist); if (cdb_sc_normal != cdb_status) GOTO_RETRY(cdb_status, SKIP_ASSERT_FALSE); } /* Note down $tlevel (used later) before it is potentially changed by op_tcommit below */ lcl_dollar_tlevel = dollar_tlevel; # ifdef GTM_TRIGGER if (lcl_implicit_tstart) { assert(gvt_root); GVTR_OP_TCOMMIT(cdb_status); if (cdb_sc_normal != cdb_status) GOTO_RETRY(cdb_status, SKIP_ASSERT_FALSE); } # endif if (!killing_chunks) INCR_GVSTATS_COUNTER(csa, cnl, n_kill, 1); if (gvt_root && (0 != gv_target->clue.end)) { /* If clue is still valid, then the deletion was confined to a single block */ assert(gvt_hist->h[0].blk_num == alt_hist->h[0].blk_num); /* In this case, the "right hand" key (which was searched via gv_altkey) was the last search * and should become the clue. Furthermore, the curr.match from this last search should be * the history's curr.match. However, this record will have been shuffled to the position of * the "left hand" key, and therefore, the original curr.offset should be left untouched. */ gvt_hist->h[0].curr_rec.match = alt_hist->h[0].curr_rec.match; assert(!TREF(expand_prev_key)); /* this ensures it is safe to use EXPAND_PREV_KEY_FALSE below */ COPY_CURR_AND_PREV_KEY_TO_GVTARGET_CLUE(gv_target, gv_altkey, EXPAND_PREV_KEY_FALSE); } NON_GTMTRIG_ONLY(assert(lcl_dollar_tlevel == dollar_tlevel)); if (!lcl_dollar_tlevel) { assert(!dollar_tlevel); assert(0 < kill_set_head.used || (NULL == kip_csa)); if (0 < kill_set_head.used) /* free subtree, decrease kill_in_prog */ { /* If csd->dsid is non-zero then some rc code was exercised before the changes * to prevent pre-commit expansion of the kill subtree. Not clear on what to do now. */ assert(!csd->dsid); ENABLE_WBTEST_ABANDONEDKILL; gvcst_expand_free_subtree(&kill_set_head); assert(csd == cs_data); /* To ensure they are the same even if MM extensions happened in between */ DECR_KIP(csd, csa, kip_csa); } assert(0 < kill_set_head.used || (NULL == kip_csa)); for (ks = kill_set_head.next_kill_set; NULL != ks; ks = temp_ks) { temp_ks = ks->next_kill_set; free(ks); } assert(0 < kill_set_head.used || (NULL == kip_csa)); } GTMTRIG_ONLY(assert(NULL == ztold_mval)); return; retry: # ifdef GTM_TRIGGER if (lcl_implicit_tstart) { assert(!skip_dbtriggers); assert(!skip_INVOKE_RESTART); assert((cdb_sc_normal != cdb_status) || (ERR_TPRETRY == gtm_trig_status)); if (cdb_sc_normal != cdb_status) skip_INVOKE_RESTART = TRUE; /* causes t_retry to invoke only tp_restart without any rts_error */ /* else: t_retry has already been done by gtm_trigger so no need to do it again for this try */ } # endif assert((cdb_sc_normal != cdb_status) GTMTRIG_ONLY(|| lcl_implicit_tstart || *span_status)); if (cdb_sc_normal != cdb_status) { GTMTRIG_ONLY(POP_MVALS_FROM_M_STACK_IF_NEEDED(ztold_mval, save_msp, save_mv_chain)); t_retry(cdb_status); GTMTRIG_ONLY(skip_INVOKE_RESTART = FALSE); } else { /* else: t_retry has already been done so no need to do that again but need to still invoke tp_restart * to complete pending "tprestart_state" related work. */ # ifdef GTM_TRIGGER assert(ERR_TPRETRY == gtm_trig_status); TRIGGER_BASE_FRAME_UNWIND_IF_NOMANSLAND; POP_MVALS_FROM_M_STACK_IF_NEEDED(ztold_mval, save_msp, save_mv_chain); if (!lcl_implicit_tstart) { /* We started an implicit transaction for spanning nodes in gvcst_kill. Invoke restart to return. */ assert(*span_status && !skip_INVOKE_RESTART && (&gvcst_kill_ch == ctxt->ch)); INVOKE_RESTART; } # endif rc = tp_restart(1, !TP_RESTART_HANDLES_ERRORS); assert(0 == rc GTMTRIG_ONLY(&& TPRESTART_STATE_NORMAL == tprestart_state)); } assert(0 < t_tries); # ifdef GTM_TRIGGER if (lcl_implicit_tstart) { SET_WANT_ROOT_SEARCH(cdb_status, want_root_search); assert(!skip_INVOKE_RESTART); /* if set to TRUE above, should have been reset by t_retry */ } # endif /* At this point, we can be in TP only if we implicitly did a tstart in gvcst_kill (as part of a trigger update). * Assert that. Since the t_retry/tp_restart would have reset si->update_trans, we need to set it again. * So reinvoke the T_BEGIN call only in case of TP. For non-TP, update_trans is unaffected by t_retry. */ assert(!dollar_tlevel GTMTRIG_ONLY(|| lcl_implicit_tstart)); if (dollar_tlevel) { /* op_ztrigger has similar code and should be maintained in parallel */ tp_set_sgm(); /* set sgm_info_ptr & first_sgm_info for TP start */ T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_GVKILLFAIL); /* set update_trans and t_err for wrapped TP */ } else { /* We could have entered gvcst_kill trying to kill a global that does not exist but later due to a * concurrent set, came here for retry. In such a case, update_trans could have been set to 0 * (from actual_update). Reset update_trans to non-zero for the next retry as we expect the kill to * happen in this retry. */ update_trans = UPDTRNS_DB_UPDATED_MASK; } assert(csd == cs_data); /* To ensure they are the same even if MM extensions happened in between */ } } fis-gtm-V7.0-005/sr_port/gvcst_kill_blk.c0000755000032200000250000004065614342376331017216 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "cdb_sc.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "copy.h" #include "gdscc.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* Include prototypes */ #include "t_write.h" #include "t_create.h" #include "gvcst_delete_blk.h" #include "gvcst_kill_blk.h" GBLREF boolean_t horiz_growth; GBLREF char *update_array, *update_array_ptr; GBLREF gv_namehead *gv_target; GBLREF sgm_info *sgm_info_ptr; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF uint4 update_array_size; /* for the BLK_* macros */ GBLREF uint4 dollar_tlevel; GBLREF unsigned int t_tries; /* delete all records greater than low and less than high in blkhist->blk_num */ enum cdb_sc gvcst_kill_blk(srch_blk_status *blkhist, char level, gv_key *search_key, srch_rec_status low, srch_rec_status high, boolean_t right_extra, cw_set_element **cseptr) { blk_hdr_ptr_t old_blk_hdr; blk_segment *bs1, *bs_ptr; block_id blk, temp_blk; static readonly block_id_64 zeroes_64 = 0; static readonly block_id_32 zeroes_32 = 0; block_index new_block_index; bool kill_root, first_copy; cw_set_element *cse, *old_cse; int4 blk_size, blk_seg_cnt, lmatch, rmatch, targ_len, prev_len, targ_base, next_rec_shrink, temp_int, blkseglen, tmp_cmpc; off_chain chain1, curr_chain, prev_chain; v6_off_chain v6_chain; rec_hdr_ptr_t left_ptr; /*pointer to record before first record to delete*/ rec_hdr_ptr_t del_ptr; /*pointer to first record to delete*/ rec_hdr_ptr_t right_ptr; /*pointer to record after last record to delete*/ rec_hdr_ptr_t right_prev_ptr; rec_hdr_ptr_t rp, rp1; /*scratch record pointer*/ rec_hdr_ptr_t first_in_blk, top_of_block, new_rec_hdr, star_rec_hdr; sm_uc_ptr_t buffer, curr, prev, right_bytptr; srch_blk_status *t1; unsigned char *skb; unsigned short temp_ushort; boolean_t long_blk_id; int4 blk_id_sz, off_chain_sz; *cseptr = NULL; if (low.offset == high.offset) return cdb_sc_normal; blk = blkhist->blk_num; if (dollar_tlevel) { PUT_BLK_ID(&chain1, blk); assert((SIZEOF(int) * 8) >= CW_INDEX_MAX_BITS); if ((1 == chain1.flag) && ((int)chain1.cw_index >= sgm_info_ptr->cw_set_depth)) { assert(sgm_info_ptr->tp_csa == cs_addrs); assert(FALSE == cs_addrs->now_crit); return cdb_sc_blknumerr; } } buffer = blkhist->buffaddr; old_blk_hdr = (blk_hdr_ptr_t)buffer; kill_root = FALSE; blk_size = cs_data->blk_size; first_in_blk = (rec_hdr_ptr_t)((sm_uc_ptr_t)old_blk_hdr + SIZEOF(blk_hdr)); top_of_block = (rec_hdr_ptr_t)((sm_uc_ptr_t)old_blk_hdr + old_blk_hdr->bsiz); left_ptr = (rec_hdr_ptr_t)((sm_uc_ptr_t)old_blk_hdr + low.offset); right_ptr = (rec_hdr_ptr_t)((sm_uc_ptr_t)old_blk_hdr + high.offset); long_blk_id = IS_64_BLK_ID(buffer); blk_id_sz = SIZEOF_BLK_ID(long_blk_id); off_chain_sz = long_blk_id ? SIZEOF(off_chain) : SIZEOF(v6_off_chain); assert(blk_id_sz == off_chain_sz); /* block_id and off_chain should be the same size */ if (right_extra && right_ptr < top_of_block) { right_prev_ptr = right_ptr; GET_USHORT(temp_ushort, &right_ptr->rsiz); right_ptr = (rec_hdr_ptr_t)((sm_uc_ptr_t)right_ptr + temp_ushort); } if ((sm_uc_ptr_t)left_ptr < (sm_uc_ptr_t)old_blk_hdr || (sm_uc_ptr_t)right_ptr > (sm_uc_ptr_t)top_of_block || (sm_uc_ptr_t)left_ptr >= (sm_uc_ptr_t)right_ptr) { assert(CDB_STAGNATE > t_tries); return cdb_sc_rmisalign; } if ((sm_uc_ptr_t)left_ptr == (sm_uc_ptr_t)old_blk_hdr) { if ((sm_uc_ptr_t)right_ptr == (sm_uc_ptr_t)top_of_block) { if ((sm_uc_ptr_t)first_in_blk == (sm_uc_ptr_t)top_of_block) { if (0 != level) { assert(CDB_STAGNATE > t_tries); return cdb_sc_rmisalign; } return cdb_sc_normal; } if (!gv_target->hist.h[level + 1].blk_num) kill_root = TRUE; else { /* We are about to free up the contents of this entire block. If this block corresponded to * a global that has NOISOLATION turned on and has a non-zero recompute list (i.e. some SETs * already happened in this same TP transaction), make sure we disable the NOISOLATION * optimization in this case as that is applicable only if one or more SETs happened in this * data block and NOT if a KILL happens. Usually this is done by a t_write(GDS_WRITE_KILLTN) * call but since in this case the entire block is being freed, "t_write" wont be invoked * so we need to explicitly set GDS_WRITE_KILLTN like t_write would have (GTM-8269). * Note: blkhist->first_tp_srch_status is not reliable outside of TP. Thankfully the recompute * list is also maintained only in case of TP so a check of dollar_tlevel is enough to * dereference both "first_tp_srch_status" and "recompute_list_head". */ if (dollar_tlevel) { t1 = blkhist->first_tp_srch_status ? blkhist->first_tp_srch_status : blkhist; cse = t1->cse; if ((NULL != cse) && cse->recompute_list_head) cse->write_type |= GDS_WRITE_KILLTN; } return cdb_sc_delete_parent; } } del_ptr = first_in_blk; } else { GET_USHORT(temp_ushort, &left_ptr->rsiz); del_ptr = (rec_hdr_ptr_t)((sm_uc_ptr_t)left_ptr + temp_ushort); if ((sm_uc_ptr_t)del_ptr <= (sm_uc_ptr_t)(left_ptr + 1) || (sm_uc_ptr_t)del_ptr > (sm_uc_ptr_t)right_ptr) { assert(CDB_STAGNATE > t_tries); return cdb_sc_rmisalign; } } if ((sm_uc_ptr_t)del_ptr == (sm_uc_ptr_t)right_ptr) return cdb_sc_normal; lmatch = low.match; rmatch = high.match; if (level) { for (rp = del_ptr ; rp < right_ptr ; rp = rp1) { GET_USHORT(temp_ushort, &rp->rsiz); rp1 = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + temp_ushort); if (((sm_uc_ptr_t)rp1 < (sm_uc_ptr_t)(rp + 1) + blk_id_sz) || ((sm_uc_ptr_t)rp1 < buffer) || ((sm_uc_ptr_t)rp1 > (buffer + blk_size))) { assert(CDB_STAGNATE > t_tries); return cdb_sc_rmisalign; } READ_BLK_ID(long_blk_id, &temp_blk, (sm_uc_ptr_t)rp1 - blk_id_sz); if (dollar_tlevel) { chain1 = *(off_chain *)&temp_blk; assert((SIZEOF(int) * 8) >= CW_INDEX_MAX_BITS); if ((1 == chain1.flag) && ((int)chain1.cw_index >= sgm_info_ptr->cw_set_depth)) { assert(sgm_info_ptr->tp_csa == cs_addrs); assert(FALSE == cs_addrs->now_crit); return cdb_sc_blknumerr; } } gvcst_delete_blk(temp_blk, level - 1, FALSE); } } if (kill_root) { /* create an empty data block */ BLK_INIT(bs_ptr, bs1); if (!BLK_FINI(bs_ptr, bs1)) { assert(CDB_STAGNATE > t_tries); return cdb_sc_mkblk; } new_block_index = t_create(blk, (uchar_ptr_t)bs1, 0, 0, 0); /* create index block */ BLK_ADDR(new_rec_hdr, SIZEOF(rec_hdr), rec_hdr); new_rec_hdr->rsiz = bstar_rec_size(long_blk_id); SET_CMPC(new_rec_hdr, 0); BLK_INIT(bs_ptr, bs1); BLK_SEG(bs_ptr, (sm_uc_ptr_t)new_rec_hdr, SIZEOF(rec_hdr)); BLK_SEG(bs_ptr, long_blk_id ? ((sm_uc_ptr_t)&zeroes_64) : ((sm_uc_ptr_t)&zeroes_32), blk_id_sz); if (!BLK_FINI(bs_ptr, bs1)) { assert(CDB_STAGNATE > t_tries); return cdb_sc_mkblk; } cse = t_write(blkhist, (unsigned char *)bs1, SIZEOF(blk_hdr) + SIZEOF(rec_hdr), new_block_index, 1, TRUE, FALSE, GDS_WRITE_KILLTN); assert(!dollar_tlevel || !cse->high_tlevel); if (dollar_tlevel) { assert(cse); *cseptr = cse; cse->first_off = 0; } return cdb_sc_normal; } next_rec_shrink = (int)(old_blk_hdr->bsiz + ((sm_uc_ptr_t)del_ptr - (sm_uc_ptr_t)right_ptr)); if (SIZEOF(blk_hdr) >= next_rec_shrink) { assert(CDB_STAGNATE > t_tries); return cdb_sc_rmisalign; } if ((sm_uc_ptr_t)right_ptr == (sm_uc_ptr_t)top_of_block) { if (level) { GET_USHORT(temp_ushort, &left_ptr->rsiz); next_rec_shrink += SIZEOF(rec_hdr) + blk_id_sz - temp_ushort; } } else { targ_base = (rmatch < lmatch) ? rmatch : lmatch; prev_len = 0; if (right_extra) { EVAL_CMPC2(right_prev_ptr, tmp_cmpc); targ_len = tmp_cmpc - targ_base; if (targ_len < 0) targ_len = 0; temp_int = tmp_cmpc - EVAL_CMPC(right_ptr); if (0 >= temp_int) prev_len = - temp_int; else { if (temp_int < targ_len) targ_len -= temp_int; else targ_len = 0; } } else { targ_len = EVAL_CMPC(right_ptr) - targ_base; if (targ_len < 0) targ_len = 0; } next_rec_shrink += targ_len + prev_len; } BLK_INIT(bs_ptr, bs1); first_copy = TRUE; blkseglen = (int)((sm_uc_ptr_t)del_ptr - (sm_uc_ptr_t)first_in_blk); if (0 < blkseglen) { if (((sm_uc_ptr_t)right_ptr != (sm_uc_ptr_t)top_of_block) || (0 == level)) { BLK_SEG(bs_ptr, (sm_uc_ptr_t)first_in_blk, blkseglen); first_copy = FALSE; } else { blkseglen = (int)((sm_uc_ptr_t)left_ptr - (sm_uc_ptr_t)first_in_blk); if (0 < blkseglen) { BLK_SEG(bs_ptr, (sm_uc_ptr_t)first_in_blk, blkseglen); first_copy = FALSE; } BLK_ADDR(star_rec_hdr, SIZEOF(rec_hdr), rec_hdr); SET_CMPC(star_rec_hdr, 0); star_rec_hdr->rsiz = (unsigned short)(bstar_rec_size(long_blk_id)); BLK_SEG(bs_ptr, (sm_uc_ptr_t)star_rec_hdr, SIZEOF(rec_hdr)); GET_USHORT(temp_ushort, &left_ptr->rsiz); BLK_SEG(bs_ptr, ((sm_uc_ptr_t)left_ptr + temp_ushort - blk_id_sz), blk_id_sz); } } blkseglen = (int)((sm_uc_ptr_t)top_of_block - (sm_uc_ptr_t)right_ptr); assert(0 <= blkseglen); if (0 != blkseglen) { next_rec_shrink = targ_len + prev_len; if (0 >= next_rec_shrink) { BLK_SEG(bs_ptr, (sm_uc_ptr_t)right_ptr, blkseglen); } else { BLK_ADDR(new_rec_hdr, SIZEOF(rec_hdr), rec_hdr); SET_CMPC(new_rec_hdr, EVAL_CMPC(right_ptr) - next_rec_shrink); GET_USHORT(temp_ushort, &right_ptr->rsiz); new_rec_hdr->rsiz = temp_ushort + next_rec_shrink; BLK_SEG(bs_ptr, (sm_uc_ptr_t)new_rec_hdr, SIZEOF(rec_hdr)); if (targ_len) { BLK_ADDR(skb, targ_len, unsigned char); memcpy(skb, &search_key->base[targ_base], targ_len); BLK_SEG(bs_ptr, skb, targ_len); } if (prev_len) BLK_SEG(bs_ptr, (sm_uc_ptr_t)(right_prev_ptr + 1) , prev_len); right_bytptr = (sm_uc_ptr_t)(right_ptr + 1); blkseglen = (int)((sm_uc_ptr_t)top_of_block - right_bytptr); if (0 < blkseglen) { BLK_SEG(bs_ptr, right_bytptr, blkseglen); } else { assert(CDB_STAGNATE > t_tries); return cdb_sc_rmisalign; } } } if (!BLK_FINI(bs_ptr, bs1)) { assert(CDB_STAGNATE > t_tries); return cdb_sc_mkblk; } cse = t_write(blkhist, (unsigned char *)bs1, 0, 0, level, first_copy, TRUE, GDS_WRITE_KILLTN); assert(!dollar_tlevel || !cse->high_tlevel); if (dollar_tlevel) { assert(cse); *cseptr = cse; } if (horiz_growth) { old_cse = cse->low_tlevel; assert(old_cse && old_cse->done); assert(2 == (SIZEOF(old_cse->undo_offset) / SIZEOF(old_cse->undo_offset[0]))); assert(2 == (SIZEOF(old_cse->undo_next_off) / SIZEOF(old_cse->undo_next_off[0]))); assert(!old_cse->undo_next_off[0] && !old_cse->undo_offset[0]); assert(!old_cse->undo_next_off[1] && !old_cse->undo_offset[1]); } if ((dollar_tlevel) && (0 != cse->first_off)) { /* fix up chains in the block to account for deleted records */ prev = NULL; curr = buffer + cse->first_off; READ_OFF_CHAIN(long_blk_id, &curr_chain, &v6_chain, curr); while (curr < (sm_uc_ptr_t)del_ptr) { /* follow chain to first deleted record */ if (0 == curr_chain.next_off) break; if ((right_ptr == top_of_block) && (((sm_uc_ptr_t)del_ptr - curr) == off_chain_sz)) break; /* special case described below: stop just before the first deleted record */ prev = curr; curr += curr_chain.next_off; READ_OFF_CHAIN(long_blk_id, &curr_chain, &v6_chain, curr); } if ((right_ptr == top_of_block) && (((sm_uc_ptr_t)del_ptr - curr) == off_chain_sz)) { /* if the right side of the block is gone and our last chain is in the last record, * terminate the chain and adjust the previous entry to point at the new *-key * NOTE: this assumes there's NEVER a TP delete of records in the GVT */ assert(0 != level); /* store next_off in old_cse before actually changing it in the buffer(for rolling back) */ if (horiz_growth) { old_cse->undo_next_off[0] = curr_chain.next_off; old_cse->undo_offset[0] = (block_offset)(curr - buffer); assert(old_cse->undo_offset[0]); } curr_chain.next_off = 0; WRITE_OFF_CHAIN(long_blk_id, &curr_chain, &v6_chain, curr); if (NULL != prev) { /* adjust previous chain next_off to reflect that the record it refers to is now a *-key */ READ_OFF_CHAIN(long_blk_id, &prev_chain, &v6_chain, prev); /* store next_off in old_cse before actually changing it in the buffer(for rolling back) */ if (horiz_growth) { old_cse->undo_next_off[1] = prev_chain.next_off; old_cse->undo_offset[1] = (block_offset)(prev - buffer); assert(old_cse->undo_offset[1]); } /* next_off is of type gtm_uint8 but is constrained by a bit field to 16-bits so the following * assert is to verify that there was no precision loss. */ assert((1ULL << NEXT_OFF_MAX_BITS) > ((sm_uc_ptr_t)left_ptr - prev + (unsigned int)(SIZEOF(rec_hdr)))); prev_chain.next_off = (uint4)((sm_uc_ptr_t)left_ptr - prev + (unsigned int)(SIZEOF(rec_hdr))); WRITE_OFF_CHAIN(long_blk_id, &prev_chain, &v6_chain, prev); } else /* it's the first (and only) one */ cse->first_off = (block_offset)((sm_uc_ptr_t)left_ptr - buffer + SIZEOF(rec_hdr)); } else if (curr >= (sm_uc_ptr_t)del_ptr) { /* may be more records on the right that aren't deleted */ while (curr < (sm_uc_ptr_t)right_ptr) { /* follow chain past last deleted record */ if (0 == curr_chain.next_off) break; curr += curr_chain.next_off; READ_OFF_CHAIN(long_blk_id, &curr_chain, &v6_chain, curr); } /* prev : ptr to chain record immediately preceding the deleted area, * or 0 if none. * * curr : ptr to chain record immediately following the deleted area, * or to last chain record. */ if (curr < (sm_uc_ptr_t)right_ptr) { /* the former end of the chain is going, going, gone */ if (NULL != prev) { /* terminate the chain before the delete */ READ_OFF_CHAIN(long_blk_id, &prev_chain, &v6_chain, prev); /* store next_off in old_cse before actually changing it in the buffer(for rolling back) */ if (horiz_growth) { old_cse->undo_next_off[0] = prev_chain.next_off; old_cse->undo_offset[0] = (block_offset)(prev - buffer); assert(old_cse->undo_offset[0]); } prev_chain.next_off = 0; WRITE_OFF_CHAIN(long_blk_id, &prev_chain, &v6_chain, prev); } else cse->first_off = 0; /* the whole chain is gone */ } else { /* stitch up the left and right to account for the hole in the middle */ /* next_rec_shrink is the change in record size due to the new compression count */ if (NULL != prev) { READ_OFF_CHAIN(long_blk_id, &prev_chain, &v6_chain, prev); /* ??? new compression may be less (ie +) so why are negative shrinks ignored? */ /* store next_off in old_cse before actually changing it in the buffer(for rolling back) */ if (horiz_growth) { old_cse->undo_next_off[0] = prev_chain.next_off; old_cse->undo_offset[0] = (block_offset)(prev - buffer); assert(old_cse->undo_offset[0]); } /* next_off is of type gtm_uint8 but is constrained by a bit field to 16-bits * so the following assert is to verify that there is no precision loss. */ assert((1ULL << NEXT_OFF_MAX_BITS) > (curr - prev - ((sm_uc_ptr_t)right_ptr - (sm_uc_ptr_t)del_ptr) + (next_rec_shrink > 0 ? next_rec_shrink : 0))); prev_chain.next_off = (block_offset)(curr - prev - ((sm_uc_ptr_t)right_ptr - (sm_uc_ptr_t)del_ptr) + (next_rec_shrink > 0 ? next_rec_shrink : 0)); WRITE_OFF_CHAIN(long_blk_id, &prev_chain, &v6_chain, prev); } else /* curr remains first: adjust the head */ cse->first_off = (block_offset)(curr - buffer - ((sm_uc_ptr_t)right_ptr - (sm_uc_ptr_t)del_ptr) + (next_rec_shrink > 0 ? next_rec_shrink : 0)); } } } horiz_growth = FALSE; return cdb_sc_normal; } fis-gtm-V7.0-005/sr_port/gvcst_kill_blk.h0000755000032200000250000000140314342376331017206 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2005 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. * * * ****************************************************************/ #ifndef GVCST_KILL_BLK_DEFINED /* Declare parms for gvcst_kill_blk.c */ enum cdb_sc gvcst_kill_blk (srch_blk_status *blkhist, char level, gv_key *search_key, srch_rec_status low, srch_rec_status high, boolean_t right_extra, cw_set_element **cseptr); #define GVCST_KILL_BLK_DEFINED #endif fis-gtm-V7.0-005/sr_port/gvcst_kill_sort.c0000755000032200000250000000316514342376331017427 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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 "gdsroot.h" #include "gdskill.h" #include "gvcst_kill_sort.h" #define S_CUTOFF 15 void gvcst_kill_sort(kill_set *k) { block_id_ptr_t stack[50],*sp; block_id v,t; block_id_ptr_t l,r; block_id_ptr_t ix,jx,kx; assert(k->used <= BLKS_IN_KILL_SET); sp = stack; l = (block_id_ptr_t)(k->blk); r = l + k->used-1; for (;;) if (r - l < S_CUTOFF) { for (ix = l + 1 ; ix <= r ; ix++) { for (jx = ix , t = *ix; jx > l && *(jx-1) > t; jx--) *jx = *(jx - 1); *jx = t; } if (sp <= stack) break; else { l = *--sp; r = *--sp; } } else { ix = l; jx = r; kx = l + ((int)(r - l) / 2); kx = (*ix > *jx) ? ((*jx > *kx) ? jx: ((*ix > *kx) ? kx : ix)): ((*jx < *kx) ? jx: ((*ix > *kx) ? ix : kx)); t = *kx; *kx = *jx; *jx = t; ix--; do { do ix++; while (*ix < t); do jx--; while (*jx > t); v = *ix; *ix = *jx; *jx = v; } while (jx > ix); *jx = *ix; *ix = *r; *r = v; if (ix - l > r - ix) { *sp++ = ix - 1; *sp++ = l; l = ix + 1; } else { *sp++ = r; *sp++ = ix + 1; r = ix - 1; } } return; } fis-gtm-V7.0-005/sr_port/gvcst_kill_sort.h0000755000032200000250000000117014342376331017426 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GVCST_KILL_SORT_H_INCLUDED #define GVCST_KILL_SORT_H_INCLUDED void gvcst_kill_sort(kill_set *k); #endif fis-gtm-V7.0-005/sr_port/gvcst_lbm_check.c0000755000032200000250000000530514342376331017332 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2019 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_string.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsbml.h" #include "gvcst_lbm_check.h" /* Routines for checking status of database blocks using a given local bitmap */ /* There are two bits that denote the status of a block in the local bitmap: 0b00 busy 0b01 free 0b10 invalid 0b11 recycled (but free) The low order bit can be used to denote busy or free so that is what is actually tested. Inputs: 1) pointer to local bit map data (blk_ptr + SIZEOF(blk_hdr)). 2) bit offset to test into bitmap. */ boolean_t gvcst_blk_is_allocated(uchar_ptr_t lbmap, block_id lm_offset) { block_id uchar_offset, uchar_offset_rem; unsigned char result_byte; uchar_ptr_t map_byte; /* lm_offset is a bit offset into a local map so, * it should never be larger then (BLKS_PER_LMAP * BML_BITS_PER_BLK) */ assert((BLKS_PER_LMAP * BML_BITS_PER_BLK) > lm_offset); uchar_offset = lm_offset / BITS_PER_UCHAR; uchar_offset_rem = lm_offset % BITS_PER_UCHAR; map_byte = lbmap + uchar_offset; result_byte = (*map_byte >> uchar_offset_rem) & 0x3; switch(result_byte) { case 0x00: return TRUE; case 0x01: case 0x03: return FALSE; default: assertpro(FALSE); } return FALSE; /* Can't get here but keep compiler happy */ } /* Similarly this routine tells if the block was EVER allocated (allocated or recycled) */ boolean_t gvcst_blk_ever_allocated(uchar_ptr_t lbmap, block_id lm_offset) { block_id uchar_offset, uchar_offset_rem; unsigned char result_byte; uchar_ptr_t map_byte; /* lm_offset is a bit offset into a local map so, * it should never be larger then (BLKS_PER_LMAP * BML_BITS_PER_BLK) */ assert((BLKS_PER_LMAP * BML_BITS_PER_BLK) > lm_offset); uchar_offset = lm_offset / BITS_PER_UCHAR; uchar_offset_rem = lm_offset % BITS_PER_UCHAR; map_byte = lbmap + uchar_offset; result_byte = (*map_byte >> uchar_offset_rem) & 0x3; switch(result_byte) { case 0x00: case 0x03: return TRUE; case 0x01: return FALSE; default: assertpro(FALSE); } return FALSE; /* Can't get here but keep compiler happy */ } fis-gtm-V7.0-005/sr_port/gvcst_lbm_check.h0000755000032200000250000000135514342376331017340 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2019 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. * * * ****************************************************************/ #ifndef GVCST_LBM_CHECK_H #define GVCST_LBM_CHECK_H #include "gdsroot.h" boolean_t gvcst_blk_is_allocated(uchar_ptr_t lbmap, block_id lm_offset); boolean_t gvcst_blk_ever_allocated(uchar_ptr_t lbmap, block_id lm_offset); #endif fis-gtm-V7.0-005/sr_port/gvcst_lftsib.c0000755000032200000250000000643314342376331016711 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "cdb_sc.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "copy.h" #include "tp.h" /* Include prototypes */ #include "t_qread.h" #include "gvcst_protos.h" /* for gvcst_search_blk,gvcst_lftsib prototype */ /* WARNING: assumes that the search history for the current target is in gv_target.hist */ GBLREF sgmnt_addrs *cs_addrs; GBLREF gv_namehead *gv_target; GBLREF gv_key *gv_currkey; GBLREF unsigned char rdfail_detail; GBLREF srch_blk_status *first_tp_srch_status; /* overriding value of srch_blk_status given by t_qread in case of TP */ GBLREF unsigned int t_tries; enum cdb_sc gvcst_lftsib(srch_hist *full_hist) { srch_blk_status *old, *new, *old_base, *new_base; rec_hdr_ptr_t rp; unsigned short rec_size; enum cdb_sc ret_val; block_id blk; unsigned short rtop, temp_short; sm_uc_ptr_t buffer_address, bp; int4 cycle; boolean_t long_blk_id; new_base = &full_hist->h[0]; old = old_base = &gv_target->hist.h[0]; long_blk_id = IS_64_BLK_ID(old->buffaddr); for (;;) { buffer_address = old->buffaddr; temp_short = old->prev_rec.offset; if (temp_short > 0) break; old++; if (0 == old->blk_num) return cdb_sc_endtree; if (old->cr && (old->blk_num != old->cr->blk)) { assert(CDB_STAGNATE > t_tries); return cdb_sc_lostcr; } if (cdb_sc_normal != (ret_val = gvcst_search_blk(gv_currkey, old))) return ret_val; } /* old now points to the first block which had a non-zero prev_rec.offset */ new = new_base + (old - old_base + 1); full_hist->depth = (uint4)(old - old_base); (new--)->blk_num = 0; new->blk_num = old->blk_num; new->tn = old->tn; new->cse = NULL; new->first_tp_srch_status = old->first_tp_srch_status; assert(new->level == old->level); assert(new->blk_target == old->blk_target); new->buffaddr = old->buffaddr; new->curr_rec = old->prev_rec; new->cycle = old->cycle; new->cr = old->cr; temp_short = new->curr_rec.offset; rp = (rec_hdr_ptr_t)(temp_short + new->buffaddr); GET_USHORT(rec_size, &rp->rsiz); rtop = temp_short + rec_size; if (((blk_hdr_ptr_t)new->buffaddr)->bsiz < rtop) return cdb_sc_rmisalign; bp = new->buffaddr; while (--new >= new_base) { --old; READ_BLK_ID(long_blk_id, &blk, bp + rtop - SIZEOF_BLK_ID(long_blk_id)); new->tn = cs_addrs->ti->curr_tn; new->cse = NULL; if (NULL == (buffer_address = t_qread(blk, &new->cycle, &new->cr))) return((enum cdb_sc)rdfail_detail); long_blk_id = IS_64_BLK_ID(old->buffaddr); new->first_tp_srch_status = first_tp_srch_status; assert(new->level == old->level); assert(new->blk_target == old->blk_target); new->blk_num = blk; new->buffaddr = buffer_address; bp = new->buffaddr; rtop = ((blk_hdr_ptr_t)new->buffaddr)->bsiz; } return cdb_sc_normal; } fis-gtm-V7.0-005/sr_port/gvcst_map_build.c0000755000032200000250000000447414342376331017365 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "gdscc.h" #include "gdsbml.h" #include "send_msg.h" /* prototypes */ #include "gvcst_map_build.h" #include "min_max.h" #include "wbox_test_init.h" GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; void gvcst_map_build(block_id *array, sm_uc_ptr_t base_addr, cw_set_element *cs, trans_num ctn) { boolean_t status; uint4 (*bml_func)(); uint4 ret; block_id bitnum; #ifdef DEBUG int4 actual_cnt = 0; block_id prev_bitnum = -1; if (!gtm_white_box_test_case_enabled || (WBTEST_ANTIFREEZE_DBBMLCORRUPT != gtm_white_box_test_case_number)) { VALIDATE_BM_BLK(cs->blk, (blk_hdr_ptr_t)base_addr, cs_addrs, gv_cur_region, status); assert(status); /* assert it is a valid bitmap block */ } #endif ((blk_hdr_ptr_t)base_addr)->tn = ctn; base_addr += SIZEOF(blk_hdr); assert(cs_addrs->now_crit); /* Don't want to be messing with highest_lbm_with_busy_blk outside crit */ DETERMINE_BML_FUNC(bml_func, cs, cs_addrs); while (bitnum = *array) /* caution : intended assignment */ { assert(bitnum == (int4)bitnum); /* check that casting bitnum is valid */ assert((int4)bitnum < cs_addrs->hdr->bplmap); /* check that bitnum is positive and within 0 to bplmap */ assert(bitnum > prev_bitnum); /* assert that blocks are sorted in the update array */ ret = (* bml_func)(bitnum, base_addr); DEBUG_ONLY( if (cs->reference_cnt > 0) actual_cnt++; /* block is being marked busy */ else if (!ret) actual_cnt--; /* block is transitioning from BUSY to either RECYCLED or FREE */ /* all other state changes do not involve updates to the free_blocks count */ ) array++; DEBUG_ONLY(prev_bitnum = bitnum;) } assert(actual_cnt == cs->reference_cnt); } fis-gtm-V7.0-005/sr_port/gvcst_map_build.h0000755000032200000250000000132014342376331017355 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef GVCST_MAP_BUILD_INCLUDED #define GVCST_MAP_BUILD_INCLUDED void gvcst_map_build(block_id *array, sm_uc_ptr_t base_addr, cw_set_element *cs, trans_num ctn); #endif /* GVCST_MAP_BUILD_INCLUDED */ fis-gtm-V7.0-005/sr_port/gvcst_order.c0000755000032200000250000001544214342376331016541 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "cdb_sc.h" #include "copy.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ #ifdef UNIX /* needed for frame_pointer in GVCST_ROOT_SEARCH_AND_PREP macro */ # include "repl_msg.h" # include "gtmsource.h" # include "rtnhdr.h" # include "stack_frame.h" # include "wbox_test_init.h" #endif #include "t_end.h" /* prototypes */ #include "t_retry.h" #include "t_begin.h" #include "gvcst_protos.h" /* for gvcst_rtsib,gvcst_search,gvcst_search_blk,gvcst_order prototype */ /* needed for spanning nodes */ #include "op.h" #include "op_tcommit.h" #include "error.h" #include "tp_frame.h" #include "tp_restart.h" #include "gtmimagename.h" LITREF mval literal_batch; GBLREF sgmnt_data_ptr_t cs_data; GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF gv_namehead *gv_target; GBLREF gv_key *gv_currkey, *gv_altkey; GBLREF int4 gv_keysize; GBLREF uint4 dollar_tlevel; GBLREF unsigned int t_tries; error_def(ERR_GVORDERFAIL); DEFINE_NSB_CONDITION_HANDLER(gvcst_order_ch) boolean_t gvcst_order(void) { /* See gvcst_query.c */ boolean_t found, is_hidden, sn_tpwrapped; boolean_t est_first_pass; gv_key_buf save_currkey; int end, prev, oldend; int save_dollar_tlevel; DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); found = gvcst_order2(); INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_order, (gtm_uint64_t) 1); WBTEST_ONLY(WBTEST_QUERY_HANG, LONG_SLEEP(2); ); # ifdef UNIX assert(save_dollar_tlevel == dollar_tlevel); CHECK_HIDDEN_SUBSCRIPT_AND_RETURN(found, gv_altkey, is_hidden); assert(found && is_hidden); IF_SN_DISALLOWED_AND_NO_SPAN_IN_DB(return found); SAVE_GV_CURRKEY_LAST_SUBSCRIPT(save_currkey, prev, oldend); if (!dollar_tlevel) { sn_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0); ESTABLISH_NORET(gvcst_order_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); found = gvcst_order2(); } else sn_tpwrapped = FALSE; if (found) { CHECK_HIDDEN_SUBSCRIPT(gv_altkey, is_hidden); if (is_hidden) { /* Replace last subscript to be the highest possible hidden subscript so another * gvcst_order2 will give us the next non-hidden subscript. */ REPLACE_HIDDEN_SUB_TO_HIGHEST(gv_altkey, gv_currkey); /* uses gv_altkey to modify gv_currkey */ WBTEST_ONLY(WBTEST_QUERY_HANG, LONG_SLEEP(2); ); found = gvcst_order2(); } } if (sn_tpwrapped) { op_tcommit(); REVERT; /* remove our condition handler */ } RESTORE_GV_CURRKEY_LAST_SUBSCRIPT(save_currkey, prev, oldend); assert(save_dollar_tlevel == dollar_tlevel); # endif return found; } boolean_t gvcst_order2(void) { blk_hdr_ptr_t bp; boolean_t found, two_histories; enum cdb_sc status; rec_hdr_ptr_t rp; unsigned short rec_size; srch_blk_status *bh; srch_hist *rt_history; sm_uc_ptr_t c1, c2, ctop, alt_top; int tmp_cmpc; T_BEGIN_READ_NONTP_OR_TP(ERR_GVORDERFAIL); for (;;) { assert(t_tries < CDB_STAGNATE || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ two_histories = FALSE; #if defined(DEBUG) && defined(UNIX) if (gtm_white_box_test_case_enabled && (WBTEST_ANTIFREEZE_GVORDERFAIL == gtm_white_box_test_case_number)) { status = cdb_sc_blknumerr; t_retry(status); continue; } #endif if (cdb_sc_normal == (status = gvcst_search(gv_currkey, NULL))) { found = TRUE; bh = gv_target->hist.h; rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); bp = (blk_hdr_ptr_t)bh->buffaddr; if ((rec_hdr_ptr_t)CST_TOB(bp) <= rp) { two_histories = TRUE; rt_history = gv_target->alt_hist; status = gvcst_rtsib(rt_history, 0); if (cdb_sc_normal == status) { bh = rt_history->h; if (cdb_sc_normal != (status = gvcst_search_blk(gv_currkey, bh))) { t_retry(status); continue; } rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); bp = (blk_hdr_ptr_t)bh->buffaddr; } else { if (cdb_sc_endtree == status) { found = FALSE; two_histories = FALSE; /* second history not valid */ } else { t_retry(status); continue; } } } if (bh->curr_rec.match >= gv_currkey->end) found = FALSE; if (found) { assert(gv_altkey->top == gv_currkey->top); assert(gv_altkey->top == gv_keysize); assert(gv_altkey->end < gv_altkey->top); /* store new subscipt */ c1 = gv_altkey->base; alt_top = gv_altkey->base + gv_altkey->top - 1; /* Make alt_top one less than gv_altkey->top to allow double-null at end of a key-name */ /* 4/17/96 * HP compiler bug work-around. The original statement was * c2 = (unsigned char *)CST_BOK(rp) + bh->curr_rec.match - rp->cmpc; * * ...but this was sometimes compiled incorrectly (the lower 4 bits * of rp->cmpc, sign extended, were subtracted from bh->curr_rec.match). * I separated out the subtraction of rp->cmpc. * * -VTF. */ c2 = (sm_uc_ptr_t)CST_BOK(rp) + bh->curr_rec.match; memcpy(c1, gv_currkey->base, bh->curr_rec.match); c1 += bh->curr_rec.match; c2 -= EVAL_CMPC(rp); GET_USHORT(rec_size, &rp->rsiz); ctop = (sm_uc_ptr_t)rp + rec_size; for (;;) { if (c2 >= ctop || c1 >= alt_top) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_rmisalign; goto restart; /* goto needed because of nested FOR loop */ } if (0 == (*c1++ = *c2++)) { *c1 = 0; break; } } gv_altkey->end = c1 - gv_altkey->base; assert(gv_altkey->end < gv_altkey->top); } if (!dollar_tlevel) { if ((trans_num)0 == t_end(&gv_target->hist, two_histories ? rt_history : NULL, TN_NOT_SPECIFIED)) continue; } else { status = tp_hist(two_histories ? rt_history : NULL); if (cdb_sc_normal != status) { t_retry(status); continue; } } assert(cs_data == cs_addrs->hdr); return (found && (bh->curr_rec.match >= gv_currkey->prev)); } restart: t_retry(status); } } fis-gtm-V7.0-005/sr_port/gvcst_protos.h0000755000032200000250000001265614342376331016765 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2004-2020 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. * * * ****************************************************************/ /* collection of prototypes of gvcst_* modules that need a bare minimum of mdef.h & gdsfhead.h to be already included */ #ifndef GVCST_PROTOS_H_INCLUDED #define GVCST_PROTOS_H_INCLUDED #define OK_TO_BYPASS_FALSE FALSE #define OK_TO_BYPASS_TRUE TRUE #define DO_STATSDB_INIT_FALSE FALSE #define DO_STATSDB_INIT_TRUE TRUE void db_auto_upgrade(gd_region *reg); void v6_db_auto_upgrade(gd_region *reg); int db_init(gd_region *reg, boolean_t ok_to_bypass); void db_init_err_cleanup(boolean_t retry_dbinit); void gvcst_redo_root_search(void); gd_region *dbfilopn (gd_region *reg); void dbsecspc(gd_region *reg, sgmnt_data_ptr_t csd, gtm_uint64_t *sec_size); unsigned char gvcst_cre_autoDB(gd_region *reg); mint gvcst_data(void); mint gvcst_data2(void); enum cdb_sc gvcst_dataget(mint *dollar_data, mval *val); enum cdb_sc gvcst_dataget2(mint *dollar_data, mval *val, unsigned char *sn_ptr); void gvcst_deferred_init_statsDB(void); void gvcst_set_statsdb_fname(sgmnt_data_ptr_t csd, gd_region *baseDBreg, char *statsdb_fname, uint4 *statsdb_fname_len); boolean_t gvcst_gblmod(mval *v); boolean_t gvcst_get(mval *v); boolean_t gvcst_get2(mval *v, unsigned char *sn_ptr); void gvcst_incr(mval *increment, mval *result); void gvcst_init(gd_region *greg, gd_addr *addr); void gvcst_init_statsDB(gd_region *baseDBreg, boolean_t do_statsdb_init); void gvcst_kill(boolean_t do_subtree); void gvcst_kill2(boolean_t do_subtree, boolean_t *span_status, boolean_t killing_chunks); enum cdb_sc gvcst_lftsib(srch_hist *full_hist); boolean_t gvcst_order(void); boolean_t gvcst_order2(void); void gvcst_put(mval *v); void gvcst_put2(mval *val, span_parms *parms); boolean_t gvcst_query(void); boolean_t gvcst_query2(void); boolean_t gvcst_queryget(mval *val); boolean_t gvcst_queryget2(mval *val, unsigned char *sn_ptr); void gvcst_remove_statsDB_linkage(gd_region *baseDBreg); void gvcst_remove_statsDB_linkage_all(void); void gvcst_remove_statsDB_linkage_wrapper(gd_region *baseDBreg, gd_region *statsDBreg); enum cdb_sc gvcst_root_search(boolean_t donot_restart); enum cdb_sc gvcst_rtsib(srch_hist *full_hist, int level); enum cdb_sc gvcst_search(gv_key *pKey, srch_hist *pHist); enum cdb_sc gvcst_search_blk(gv_key *pKey, srch_blk_status *pStat); enum cdb_sc gvcst_search_blk_expand_prevkey(gv_key *pKey, srch_blk_status *pStat); enum cdb_sc gvcst_search_tail(gv_key *pKey, srch_blk_status *pStat, gv_key *pOldKey); enum cdb_sc gvcst_search_tail_expand_prevkey(gv_key *pKey, srch_blk_status *pStat, gv_key *pOldKey); mint gvcst_spr_data(void); void gvcst_spr_kill(void); boolean_t gvcst_spr_order(void); boolean_t gvcst_spr_zprevious(void); boolean_t gvcst_spr_query(void); boolean_t gvcst_spr_queryget(mval *cumul_val); # define INVOKE_GVCST_SPR_XXX(GVNH_REG, STATEMENT) \ { \ assert(NULL != GVNH_REG->gvspan); \ STATEMENT; \ } void gvcst_tp_init(gd_region *); boolean_t gvcst_zprevious(void); boolean_t gvcst_zprevious2(void); /* gvcst_dataget and gvcst_dataget2 take the following values as input */ #define DG_GETONLY 2 #define DG_DATAONLY 3 #define DG_DATAGET 4 #define DG_GETSNDATA 5 #define INCREMENT_GD_TARG_TN(LCL_TN) \ { \ (TREF(gd_targ_tn))++; \ LCL_TN = TREF(gd_targ_tn); \ if (0 == LCL_TN) \ { /* We have wrapped around after 2**64 increments. Reset gd_targ_reg_array to \ * avoid incorrect matches of tn (the pre-wrapped tn with the post-wrapped tn). \ */ \ assert(SIZEOF(trans_num) == SIZEOF((TREF(gd_targ_reg_array))[0])); \ memset(TREF(gd_targ_reg_array), 0, TREF(gd_targ_reg_array_size) * SIZEOF(trans_num)); \ LCL_TN++; \ TREF(gd_targ_tn) = LCL_TN; \ } \ } GBLREF boolean_t gv_play_duplicate_kills; /* In case "gv_play_duplicate_kills" is TRUE, invoke "gvcst_kill" even if the GVT does not exist. This is so we record * in the jnl file and the db (curr_tn wise) that such a KILL occurred. Also do this only if the update is an explicit * update. Else this update is not replicated so we dont have the need to write a journal record for the KILL. While at * this, add a few asserts that in case of backward recovery, the to-be-killed node always exists. This is because backward * recovery is supposed to mirror GT.M activity on the database and so should see the exact same state of the database * that GT.M saw. The only exception is in case of replication and the update process wrote a KILL jnl record because it * has to mirror the jnl state of the primary. In this case though nodeflags will have the JS_IS_DUPLICATE bit set. */ #define IS_OK_TO_INVOKE_GVCST_KILL(GVT) \ ( \ DBG_ASSERT(!jgbl.forw_phase_recovery || gv_play_duplicate_kills GTMTRIG_ONLY(&& IS_EXPLICIT_UPDATE_NOASSERT)) \ DBG_ASSERT(!jgbl.forw_phase_recovery || gv_target->root || (JS_IS_DUPLICATE & jgbl.mur_jrec_nodeflags) \ || jgbl.mur_options_forward) \ (GVT->root || (gv_play_duplicate_kills GTMTRIG_ONLY(&& IS_EXPLICIT_UPDATE_NOASSERT))) \ ) #endif fis-gtm-V7.0-005/sr_port/gvcst_put.c0000644000032200000250000041233714342376333016241 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "gtm_inet.h" /* Required for gtmsource.h */ #include "gtmio.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "cdb_sc.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "jnl.h" #include "gdscc.h" #include "copy.h" #include "buddy_list.h" /* needed for tp.h */ #include "hashtab_int8.h" #include "tp.h" #include "rc_oflow.h" #include "repl_msg.h" #include "gtmsource.h" #include #include "stack_frame.h" #include "mv_stent.h" #include "gv_trigger.h" #include "gtm_trigger.h" #include "gv_trigger_protos.h" #include "subscript.h" #include "stringpool.h" #include "gtm_trigger_trc.h" #include "tp_frame.h" #include "tp_restart.h" #include "is_file_identical.h" #include "anticipatory_freeze.h" /* Include prototypes */ #include "wbox_test_init.h" #include "t_write.h" #include "t_write_root.h" #include "t_end.h" #include "t_retry.h" #include "t_begin.h" #include "t_create.h" #include "gvcst_blk_build.h" #include "gvcst_expand_key.h" #include "gvcst_protos.h" /* for gvcst_search,gvcst_search_blk,gvcst_put prototype */ #include "op.h" /* for op_add & op_tstart prototype */ #include "format_targ_key.h" /* for format_targ_key prototype */ #include "gvsub2str.h" /* for gvsub2str prototype */ #include "tp_set_sgm.h" /* for tp_set_sgm prototype */ #include "op_tcommit.h" /* for op_tcommit prototype */ #include "have_crit.h" #include "error.h" #include "gtmimagename.h" /* for spanning nodes */ #include "preemptive_db_clnup.h" #include "spec_type.h" #include "collseq.h" #ifdef DEBUG #include "mvalconv.h" #endif #include "gtm_repl_multi_inst.h" /* for DISALLOW_MULTIINST_UPDATE_IN_TP */ #include "gvt_inline.h" #ifdef GTM_TRIGGER LITREF mval literal_null; LITREF mval literal_one; LITREF mval literal_zero; #endif LITREF mval literal_batch; LITREF mstr nsb_dummy; /* Globals that will not change in value across nested trigger calls of gvcst_put OR even if they might change in value, * the change is such that they dont need save/restore logic surrounding the "gtm_trigger" call. Any new GBLREFs that are * added in this module need to be examined for interference between gvcst_put and nested trigger call and any save/restore * logic (if needed) should be appropriately added surrounding the "gtm_trigger" invocation. */ GBLREF boolean_t gvdupsetnoop; /* if TRUE, duplicate SETs update journal but not database (except for curr_tn++) */ GBLREF boolean_t horiz_growth; GBLREF boolean_t in_gvcst_incr; GBLREF boolean_t mu_reorg_process; GBLREF boolean_t mu_reorg_upgrd_dwngrd_in_prog; /* TRUE if MUPIP REORG UPGRADE/DOWNGRADE is in progress */ GBLREF char *update_array, *update_array_ptr; GBLREF gv_key *gv_altkey; GBLREF gv_namehead *reset_gv_target; GBLREF inctn_opcode_t inctn_opcode; GBLREF int gv_fillfactor; GBLREF int rc_set_fragment; /* Contains offset within data at which data fragment starts */ GBLREF int4 gv_keysize; GBLREF int4 prev_first_off, prev_next_off; GBLREF uint4 update_trans; GBLREF jnl_format_buffer *non_tp_jfb_ptr; GBLREF jnl_gbls_t jgbl; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnlpool_addrs_ptr_t jnlpool_head; GBLREF uint4 dollar_tlevel; GBLREF uint4 process_id; GBLREF uint4 update_array_size, cumul_update_array_size; /* the current total size of the update array */ GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; GBLREF unsigned int t_tries; GBLREF cw_set_element cw_set[CDB_CW_SET_SIZE];/* create write set. */ GBLREF boolean_t skip_dbtriggers; /* see gbldefs.c for description of this global */ GBLREF stack_frame *frame_pointer; GBLREF mv_stent *mv_chain; #ifdef GTM_TRIGGER GBLREF int tprestart_state; GBLREF int4 gtm_trigger_depth; GBLREF int4 tstart_trigger_depth; GBLREF boolean_t skip_INVOKE_RESTART; GBLREF boolean_t ztwormhole_used; /* TRUE if $ztwormhole was used by trigger code */ #endif #ifdef DEBUG GBLREF boolean_t skip_block_chain_tail_check; GBLREF boolean_t donot_INVOKE_MUMTSTART; #endif /* Globals that could change in value across nested trigger calls of gvcst_put AND need to be saved/restored */ GBLREF boolean_t is_dollar_incr; GBLREF gd_region *gv_cur_region; GBLREF gv_key *gv_currkey; GBLREF gv_namehead *gv_target; GBLREF mval *post_incr_mval; GBLREF mval increment_delta_mval; GBLREF sgm_info *sgm_info_ptr; GBLREF sgm_info *first_sgm_info; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF enum gtmImageTypes image_type; GBLREF boolean_t span_nodes_disallowed; GBLREF boolean_t is_updproc; GBLREF uint4 dollar_trestart; error_def(ERR_DBROLLEDBACK); error_def(ERR_GVINCRISOLATION); error_def(ERR_GVIS); error_def(ERR_GVPUTFAIL); error_def(ERR_MAXBTLEVEL); error_def(ERR_REC2BIG); error_def(ERR_RSVDBYTE2HIGH); error_def(ERR_TEXT); error_def(ERR_TPRETRY); error_def(ERR_UNIMPLOP); /* Before issuing an error, add GVT to the list of known gvts in this TP transaction in case it is not already done. * This GVT addition is usually done by "tp_hist" but that function has most likely not yet been invoked in gvcst_put. * Doing this addition will ensure we remember to reset any non-zero clue in dir_tree as part of tp_clean_up when a TROLLBACK * or TRESTART (implicit or explicit) occurs. Not doing so could means if an ERR_REC2BIG happens here, control will * go to the error trap and if it does a TROLLBACK (which does a tp_clean_up) we would be left with a potentially out-of-date * clue of GVT which if used for later global references could result in db integ errors. */ #define ENSURE_VALUE_WITHIN_MAX_REC_SIZE(value, GVT) \ { \ if (dollar_tlevel) \ ADD_TO_GVT_TP_LIST(GVT, RESET_FIRST_TP_SRCH_STATUS_FALSE); /* note: macro updates read_local_tn if necessary */ \ if (value.len > gv_cur_region->max_rec_size) \ { \ if (0 == (end = format_targ_key(buff, MAX_ZWR_KEY_SZ, gv_currkey, TRUE))) \ end = &buff[MAX_ZWR_KEY_SZ - 1]; \ gv_currkey->end = 0; \ rts_error_csa(CSA_ARG(cs_addrs) VARLSTCNT(10) ERR_REC2BIG, 4, VMS_ONLY(gv_currkey->end + 1 + SIZEOF(rec_hdr) +) \ value.len, (int4)gv_cur_region->max_rec_size, \ REG_LEN_STR(gv_cur_region), ERR_GVIS, 2, end - buff, buff); \ } \ } /* See comment before ENSURE_VALUE_WITHIN_MAX_REC_SIZE macro definition for why the ADD_TO_GVT_TP_LIST call below is necessary */ #define ISSUE_RSVDBYTE2HIGH_ERROR(GVT) \ { \ if (dollar_tlevel) \ ADD_TO_GVT_TP_LIST(GVT, RESET_FIRST_TP_SRCH_STATUS_FALSE); /* note: macro updates read_local_tn if necessary */ \ /* The record that is newly inserted/updated does not fit by itself in a separate block \ * if the current reserved-bytes for this database is taken into account. Cannot go on. \ */ \ if (0 == (end = format_targ_key(buff, MAX_ZWR_KEY_SZ, gv_currkey, TRUE))) \ end = &buff[MAX_ZWR_KEY_SZ - 1]; \ rts_error_csa(CSA_ARG(cs_addrs) VARLSTCNT(11) ERR_RSVDBYTE2HIGH, 5, new_blk_size_single, \ REG_LEN_STR(gv_cur_region), blk_size, blk_reserved_bytes, \ ERR_GVIS, 2, end - buff, buff); \ } #define RESTORE_ZERO_GVT_ROOT_ON_RETRY(LCL_ROOT, GV_TARGET, DIR_HIST, DIR_TREE) \ { \ if (!LCL_ROOT) \ { \ assert(NULL != DIR_HIST); \ assert(DIR_TREE == GV_TARGET->gd_csa->dir_tree); \ /* t_retry only resets gv_target->clue and not the clue of the directory tree. \ * But DIR_HIST non-null implies the directory tree was used in a gvcst_search and hence \ * was validated (in t_end/tp_hist),so we need to reset its clue before the next try. \ */ \ DIR_TREE->clue.end = 0; \ /* We had reset the root block from zero to a non-zero value within \ * this function, but since we are restarting, we can no longer be \ * sure of the validity of the root block. Reset it to 0 so it will \ * be re-determined in the next global reference. \ */ \ GV_TARGET->root = 0; \ } \ } #define RECORD_FITS_IN_A_BLOCK(VAL, KEY, BLK_SZ, RESERVED_BYTES) \ ((VAL)->str.len <= COMPUTE_CHUNK_SIZE(KEY, BLK_SZ, RESERVED_BYTES)) #define ZKILL_NODELETS \ { \ APPEND_HIDDEN_SUB(gv_currkey); \ if (gv_target->root) \ gvcst_kill2(FALSE, NULL, TRUE); /* zkill any existing spanning nodelets.. */ \ RESTORE_CURRKEY(gv_currkey, oldend); \ } #define POP_MVALS_FROM_M_STACK_IF_REALLY_NEEDED(lcl_span_status, ztold_mval, save_msp, save_mv_chain) \ { \ if (!lcl_span_status) \ POP_MVALS_FROM_M_STACK_IF_NEEDED(ztold_mval, save_msp, save_mv_chain); \ } #ifdef DEBUG # define DBG_SAVE_VAL_AT_FUN_ENTRY \ { /* Save copy of "val" at function entry. \ * Make sure this is not touched by any nested trigger code */ \ dbg_lcl_val = val; \ dbg_vallen = val->str.len; \ memcpy(dbg_valbuff, val->str.addr, MIN(ARRAYSIZE(dbg_valbuff), dbg_vallen)); \ } # define DBG_CHECK_VAL_AT_FUN_EXIT \ { /* Check "val" is same as what it was at function entry.(i.e. was not touched by nested trigger code). \ * The only exception is if $ZTVAL changed "val" in which case gvcst_put would have been redone. */ \ assert(dbg_vallen == dbg_lcl_val->str.len); \ assert(0 == memcmp(dbg_valbuff, dbg_lcl_val->str.addr, MIN(ARRAYSIZE(dbg_valbuff), dbg_vallen))); \ } #else # define DBG_SAVE_VAL_AT_FUN_ENTRY # define DBG_CHECK_VAL_AT_FUN_EXIT #endif #define GOTO_RETRY \ { \ GTMTRIG_DBG_ONLY(dbg_trace_array[dbg_num_iters].retry_line = __LINE__); \ goto retry; \ } CONDITION_HANDLER(gvcst_put_ch) { int rc; START_CH(TRUE); if ((int)ERR_TPRETRY == SIGNAL) { /* delay tp_restart till after the long jump and some other stuff */ UNWIND(NULL, NULL); } NEXTCH; } /* Used to determine how many records in a block to count to estimate number of records in the block */ #define NUMRECSTOCOUNTTOESTIMATE 3 static inline int4 num_recs_in_blk(sm_uc_ptr_t pBlkBase); static inline int4 num_recs_in_blk(sm_uc_ptr_t pBlkBase) { sm_uc_ptr_t pBlkTop, pRec; uint4 blkLen, recLen, heuristicRecsSize; uint4 recCnt; heuristicRecsSize = 0; blkLen = ((blk_hdr_ptr_t) pBlkBase)->bsiz; if (blkLen > MAX_DB_BLK_SIZE) return -1; pBlkTop = pBlkBase + blkLen; pRec = pBlkBase + SIZEOF(blk_hdr); for (recCnt = NUMRECSTOCOUNTTOESTIMATE; (0 < recCnt) && (pRec < pBlkTop); recCnt--) { GET_USHORT(recLen, &((rec_hdr_ptr_t)pRec)->rsiz); pRec += recLen; heuristicRecsSize += recLen; } if ((pRec > pBlkTop) || (0 == heuristicRecsSize)) return -1; return recCnt ? (NUMRECSTOCOUNTTOESTIMATE - recCnt) : ((blkLen * NUMRECSTOCOUNTTOESTIMATE) / heuristicRecsSize); } void gvcst_put(mval *val) { boolean_t sn_tpwrapped, fits; boolean_t est_first_pass, found; mval val_ctrl, val_piece, val_dummy; mval *pre_incr_mval, *save_val; block_id lcl_root; int gblsize, chunk_size, i, oldend; unsigned short numsubs; unsigned char mychars[MAX_NSBCTRL_SZ]; int save_dollar_tlevel, rc; boolean_t save_in_gvcst_incr; /* gvcst_put2 sets this FALSE, so save it in case we need to back out */ span_parms parms; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; parms.span_status = FALSE; parms.blk_reserved_bytes = cs_data->reserved_bytes; /* Only want to read once for consistency */ parms.enable_trigger_read_and_fire = TRUE; parms.enable_jnl_format = TRUE; lcl_root = gv_target->root; /* deal with possibility of spanning nodes */ DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); fits = RECORD_FITS_IN_A_BLOCK(val, gv_currkey, cs_data->blk_size, parms.blk_reserved_bytes); save_in_gvcst_incr = in_gvcst_incr; if (fits) { gvcst_put2(val, &parms); if (!parms.span_status) { assert(save_dollar_tlevel == dollar_tlevel); return; /* We've successfully set a normal non-spanning global. */ } } RTS_ERROR_IF_SN_DISALLOWED; /* Either we need to create a spanning node, or kill one before resetting it */ GTMTRIG_ONLY(parms.ztold_mval = NULL); cs_data->span_node_absent = FALSE; oldend = gv_currkey->end; val_dummy.str = nsb_dummy; if (!dollar_tlevel) { sn_tpwrapped = TRUE; save_val = val; /* We pass the IMPLICIT_TRIGGER_TSTART flag because we might invoke a trigger, and in that case we want to avoid * invoking a restart from within gtm_trigger. Since we remove gvcst_put_ch before invoking the trigger * (see comment in errorsp.h), an attempt to invoke a restart returns us back to the mdb_condition_handler created * by the initial dm-start. Unwinding from there returns to the OS -- i.e., the process silently dies. * Also note that we need to ensure the retry logic at the bottom of gvcst_put2 is executed in case a restart * happens within a trigger invocation. We want to return from gvtr_match_n_invoke, so we can goto retry. */ op_tstart((IMPLICIT_TSTART + IMPLICIT_TRIGGER_TSTART), TRUE, &literal_batch, 0); frame_pointer->flags |= SSF_NORET_VIA_MUMTSTART; assert(!donot_INVOKE_MUMTSTART); DEBUG_ONLY(donot_INVOKE_MUMTSTART = TRUE); ESTABLISH_NORET(gvcst_put_ch, est_first_pass); if (est_first_pass) { /* did a long jump back to here */ val = save_val; GTMTRIG_ONLY(POP_MVALS_FROM_M_STACK_IF_NEEDED(parms.ztold_mval, parms.save_msp, ((mv_stent *)parms.save_mv_chain))); preemptive_db_clnup(ERROR); /* Bluff about SEVERITY to reset gv_target and reset_gv_target tp_restart resets * but not reset_gv_target This matches flow with non-spanning-node tp_restart, * which does preemptive_db_clnup in mdb_condition_handler */ rc = tp_restart(1, !TP_RESTART_HANDLES_ERRORS); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); RESTORE_ZERO_GVT_ROOT_ON_RETRY(lcl_root, gv_target, &cs_addrs->dir_tree->hist, cs_addrs->dir_tree); fits = RECORD_FITS_IN_A_BLOCK(val, gv_currkey, cs_data->blk_size, parms.blk_reserved_bytes); if (cdb_sc_onln_rlbk2 == LAST_RESTART_CODE) /* Database was taken back to a different logical state. We are an implicit TP transaction, and * as in the case of implicit TP for triggers, we issue a DBROLLEDBACK error that the application * programmer can catch. */ RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DBROLLEDBACK); } tp_set_sgm(); GVCST_ROOT_SEARCH; } else sn_tpwrapped = FALSE; parms.span_status = TRUE; parms.enable_trigger_read_and_fire = TRUE; parms.enable_jnl_format = TRUE; parms.ztval_gvcst_put_redo = FALSE; if (save_in_gvcst_incr) { in_gvcst_incr = FALSE; /* allow gvcst_put2 to do a regular set */ PUSH_MV_STENT(MVST_MVAL); /* protect pre_incr_mval from stp_gcol */ pre_incr_mval = &mv_chain->mv_st_cont.mvs_mval; found = gvcst_get(pre_incr_mval); /* what if it doesn't exist? needs to be treated as 0 */ if (found) pre_incr_mval->mvtype = MV_STR; else *pre_incr_mval = literal_null; op_add(pre_incr_mval, &increment_delta_mval, post_incr_mval); POP_MV_STENT(); /* pre_incr_mval */ assert(MV_IS_NUMERIC(post_incr_mval)); MV_FORCE_STR(post_incr_mval); val = post_incr_mval; /* its a number, should fit in single block.. unless ridick rsrvdbytes.. */ fits = RECORD_FITS_IN_A_BLOCK(val, gv_currkey, cs_data->blk_size, parms.blk_reserved_bytes); } /* Set the primary node. If we're setting a spanning node, that the primary node will have a dummy value, $char(0). * Journal formatting and triggers happen here. If ztval changed during trigger invocations to a large value, * we do a second gvcst_put2 to set the new value. */ parms.val_forjnl = val; gvcst_put2(((fits) ? val : &val_dummy), &parms); parms.enable_trigger_read_and_fire = FALSE; # ifdef GTM_TRIGGER if (parms.ztval_gvcst_put_redo) { val = parms.ztval_mval; parms.enable_jnl_format = TRUE; /* new val needs to be journaled */ parms.val_forjnl = val; fits = RECORD_FITS_IN_A_BLOCK(val, gv_currkey, cs_data->blk_size, parms.blk_reserved_bytes); assert(!fits); gvcst_put2(&val_dummy, &parms); } # endif ZKILL_NODELETS; /* Even if not creating a spanning node, may be replacing one */ if (!fits) { /* Need to create a spanning node. Break value up into chunks. */ parms.enable_jnl_format = FALSE; /* jnl formatting already done */ APPEND_HIDDEN_SUB(gv_currkey); chunk_size = COMPUTE_CHUNK_SIZE(gv_currkey, cs_data->blk_size, parms.blk_reserved_bytes); gblsize = val->str.len; numsubs = DIVIDE_ROUND_UP(gblsize, chunk_size); PUT_NSBCTRL(mychars, numsubs, gblsize); val_ctrl.str.addr = (char *)mychars; val_ctrl.str.len = 6; /* Count the spanning node set as one set */ INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_set, 1); gvcst_put2(&val_ctrl, &parms); /* Set control subscript, indicating glbsize and number of chunks */ for (i = 0; i < numsubs; i++) { NEXT_HIDDEN_SUB(gv_currkey, i); val_piece.str.len = MIN(chunk_size, gblsize - i * chunk_size); val_piece.str.addr = val->str.addr + i * chunk_size; gvcst_put2(&val_piece, &parms); } RESTORE_CURRKEY(gv_currkey, oldend); } GTMTRIG_ONLY(POP_MVALS_FROM_M_STACK_IF_NEEDED(parms.ztold_mval, parms.save_msp, ((mv_stent *)parms.save_mv_chain))); /* pop any stacked mvals before op_tcommit as it does its own popping */ if (sn_tpwrapped) { op_tcommit(); DEBUG_ONLY(donot_INVOKE_MUMTSTART = FALSE); REVERT; /* remove our condition handler */ } assert(save_dollar_tlevel == dollar_tlevel); } void gvcst_put2(mval *val, span_parms *parms) { blk_segment *bs1, *bs_ptr, *new_blk_bs; block_id allocation_clue, tp_root, gvt_for_root, blk_num, last_split_blk_num[MAX_BT_DEPTH]; block_id lcl_root, last_split_bnum; block_index left_hand_index, ins_chain_index, root_blk_cw_index, next_blk_index; block_offset next_offset, first_offset, ins_off1, ins_off2, old_curr_chain_next_off; boolean_t jnl_format_done, is_dummy, needfmtjnl, fits, lcl_span_status, want_root_search = FALSE; boolean_t copy_extra_record, level_0, new_rec, no_pointers, succeeded, key_exists; boolean_t make_it_null, gbl_target_was_set, duplicate_set, new_rec_goes_to_right, need_extra_block_split; boolean_t db_long_blk_id, long_blk_id; /* db_long_blk_id is based on csd->desired_db_format, * while long_blk_id is based on bp->bver */ boolean_t write_logical_jnlrecs, can_write_logical_jnlrecs, blk_match, is_split_dir_left; boolean_t split_to_right; /* FALSE if a block split creates a new block on the left of the split point. * In this case, a "t_create" is needed for the new block on the left * and a "t_write" is needed for the current block (right side of split). * TRUE if a block split creates a new block on the right of the split point. * In this case, the pre-split block is untouched (i.e. no "t_write" needed) * and serves as the left side of the split AND a new block is created on the * right ("t_create" is needed for that). */ boolean_t prev_split_to_right; /* copy of "split_to_right" after child level split (used in parent split) */ char *va, last_split_direction[MAX_BT_DEPTH]; const char zeroes_blkid[SIZEOF(block_id_64)] = {0}; const char zeroes_blkid_collhdr[SIZEOF(block_id_64) + COLL_SPEC_LEN] = {0}; const char v6_zeroes_blkid[SIZEOF(block_id_32)] = {0}; const char v6_zeroes_blkid_collhdr[SIZEOF(block_id_32) + COLL_SPEC_LEN] = {0}; enum cdb_sc status, status2; enum split_dir last_split_dir; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; node_local_ptr_t cnl; int4 blk_size, blk_fill_size, blk_reserved_bytes; cw_set_element *cse, *cse_new, *old_cse; gv_key *temp_key, *src_key; static gv_key *gv_altkey2; gv_namehead *save_targ, *split_targ, *dir_tree; ht_ent_int8 *tabent; int cur_blk_size, blk_seg_cnt, delta, i, j, left_hand_offset, n, ins_chain_offset, new_blk_size_l, new_blk_size_r, new_blk_size_single, new_blk_size, blk_reserved_size, last_possible_left_offset, new_rec_size, next_rec_shrink, next_rec_shrink1, start_len, offset_sum, rec_cmpc, tmp_cmpc, target_key_size, tp_lev, undo_index, cur_val_offset, curr_offset, bh_level, blk_id_sz, off_chain_sz; int rc; int split_depth; int4 cse_first_off; int4 data_len; jnl_action *ja; jnl_format_buffer *jfb, *ztworm_jfb; key_cum_value *tempkv; mstr value; mval *set_val; /* actual right-hand-side value of the SET or $INCR command */ mval *val_forjnl; mval *ja_val; off_chain chain1, curr_chain, prev_chain, chain2; v6_off_chain v6_chain; rec_hdr_ptr_t curr_rec_hdr, extra_rec_hdr, next_rec_hdr, new_star_hdr, rp, tmp_rp; srch_blk_status *bh, *bq, *tp_srch_status; srch_hist *dir_hist; sm_uc_ptr_t cp1, cp2, curr; sm_uc_ptr_t buffaddr; sgm_info *si; uchar_ptr_t subrec_ptr; uint4 segment_update_array_size, key_top, cp2_len, bs1_2_len, bs1_3_len; uint4 nodeflags; uint4 no_4byte_collhdr; unsigned short extra_record_blkid_off, rec_size, tmp_rsiz; unsigned int prev_rec_offset, prev_rec_match, curr_rec_offset, curr_rec_match; unsigned char buff[MAX_ZWR_KEY_SZ], *end, old_ch, new_ch; # ifdef GTM_TRIGGER boolean_t is_tpwrap; boolean_t lcl_implicit_tstart; /* local copy of the global variable "implicit_tstart" */ boolean_t lcl_is_dollar_incr; /* local copy of is_dollar_incr taken at start of module. * used to restore is_dollar_incr in case of TP restarts */ boolean_t ztval_gvcst_put_redo, skip_hasht_read; gtm_trigger_parms trigparms; gvt_trigger_t *gvt_trigger; gvtr_invoke_parms_t gvtr_parms; int gtm_trig_status; mint dlr_data; mv_stent *save_mv_chain; mval lcl_increment_delta_mval; /* local copy of "increment_delta_mval" */ mval *lcl_post_incr_mval; /* local copy of "post_incr_mval" at function entry. * used to restore "post_incr_mval" in case of TP restarts */ mval *lcl_val; /* local copy of "val" at function entry. * used to restore "val" in case of TP restarts */ mval *lcl_val_forjnl; mval *pval; /* copy of "value" (an mstr), protected from stp gcol */ mval *ztold_mval = NULL; mval *ztval_mval; unsigned char *save_msp; DEBUG_ONLY(enum cdb_sc save_cdb_status;) # endif # ifdef DEBUG /* We want to capture all pertinent information across each iteration of gvcst_put. * There are 3 things that can contribute to a new iteration. * a) restarts from the primary set. * Max of 4 iterations. * b) extra_block_split from the primary set. It can have its own set of restarts too. * Max of 4 iterations per extra_block_split. * The # of extra block splits could be arbitrary in case of non-TP but cannot be more than 1 for TP * because in TP, we would have grabbed crit in the final retry and prevent any more concurrent updates. * c) ztval_gvcst_put_redo. This in turn can have its own set of restarts and extra_block_split iterations. * Could take a max of (a) + (b) = 4 + 4 = 8 iterations. * Total of 16 max iterations. If ever a transaction goes for more than this # of iterations (theoretically * possible in non-TP if a lot of extra block splits occur), we assert fail. */ boolean_t is_fresh_tn_start; boolean_t is_mm; char dbg_valbuff[256]; int dbg_num_iters = -1; /* number of iterations through gvcst_put */ int lcl_dollar_tlevel, lcl_t_tries; mstr_len_t dbg_vallen; mval *dbg_lcl_val; mval tmpmval, *random_mval = &tmpmval; typedef struct { unsigned int t_tries; int retry_line; boolean_t is_fresh_tn_start; boolean_t is_dollar_incr; boolean_t ztval_gvcst_put_redo; boolean_t is_extra_block_split; mval *val; boolean_t lcl_implicit_tstart; } dbg_trace; dbg_trace dbg_trace_array[16]; # endif boolean_t dont_copy_extra_record = FALSE; boolean_t preemptive_split; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; is_dollar_incr = in_gvcst_incr; in_gvcst_incr = FALSE; csa = cs_addrs; csd = csa->hdr; cnl = csa->nl; assert(csd == cs_data); DEBUG_ONLY(is_mm = (dba_mm == csd->acc_meth);) # ifdef GTM_TRIGGER TRIG_CHECK_REPLSTATE_MATCHES_EXPLICIT_UPDATE(gv_cur_region, csa); if (IS_EXPLICIT_UPDATE) { /* This is an explicit update. Set ztwormhole_used to FALSE. Note that we initialize this only at the * beginning of the transaction and not at the beginning of each try/retry. If the application used * $ztwormhole in any retsarting try of the transaction, we consider it necessary to write the * TZTWORM/UZTWORM record even though it was not used in the succeeding/committing try. */ ztwormhole_used = FALSE; } # endif JNLPOOL_INIT_IF_NEEDED(csa, csd, cnl, SCNDDBNOUPD_CHECK_TRUE); db_long_blk_id = BLK_ID_32_VER < csd->desired_db_format; blk_size = csd->blk_size; blk_reserved_bytes = parms->blk_reserved_bytes; blk_fill_size = (blk_size * gv_fillfactor) / 100 - blk_reserved_bytes; lcl_span_status = parms->span_status; if (lcl_span_status) { needfmtjnl = parms->enable_jnl_format; val_forjnl = parms->val_forjnl; # ifdef GTM_TRIGGER ztold_mval = parms->ztold_mval; skip_hasht_read = !parms->enable_trigger_read_and_fire; # endif } else { needfmtjnl = TRUE; val_forjnl = val; } jnl_format_done = !needfmtjnl; /* do "jnl_format" only once per logical non-tp transaction irrespective of * number of retries or number of chunks if spanning node */ GTMTRIG_ONLY( ztval_gvcst_put_redo = FALSE; skip_hasht_read = FALSE; ) assert(('\0' != gv_currkey->base[0]) && gv_currkey->end); # ifdef DEBUG /* ^%Y* should never be set (op_gvput would have issued a ERR_PCTYRESERVED error in that case). * The only exception is if we are adding a ^%YGS record into the statsdb from "gvcst_init_statsDB" but in that * case we would have reg->read_only set to FALSE for a statsdb region name. Account for that. */ assert((RESERVED_NAMESPACE_LEN > gv_currkey->end) || (0 != MEMCMP_LIT(gv_currkey->base, RESERVED_NAMESPACE)) || (IS_STATSDB_REGNAME(gv_cur_region) && !gv_cur_region->read_only && csa->orig_read_write)); # endif DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); DISALLOW_MULTIINST_UPDATE_IN_TP(dollar_tlevel, jnlpool_head, csa, first_sgm_info, FALSE); /* this needs to be initialized before any code that does a "goto retry" since this gets used there */ save_targ = gv_target; gbl_target_was_set = (INVALID_GV_TARGET != reset_gv_target); if (INVALID_GV_TARGET != reset_gv_target) gbl_target_was_set = TRUE; else { gbl_target_was_set = FALSE; reset_gv_target = save_targ; } DBG_SAVE_VAL_AT_FUN_ENTRY; GTMTRIG_ONLY( lcl_implicit_tstart = FALSE; DEBUG_ONLY(gvtr_parms.num_triggers_invoked = -1;) /* set to an out-of-design value; checked by an assert */ ) DEBUG_ONLY( status = cdb_sc_normal; lcl_dollar_tlevel = dollar_tlevel; ) fresh_tn_start: DEBUG_ONLY(lcl_t_tries = -1;) DEBUG_ONLY(is_fresh_tn_start = TRUE;) assert(!jnl_format_done || (dollar_tlevel GTMTRIG_ONLY(&& ztval_gvcst_put_redo)) || lcl_span_status); T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_GVPUTFAIL); tn_restart: /* t_tries should never decrease - it either increases or stays the same. If should decrease we could live-lock with * an oscillating t_tries and never reach CDB_STAGNATE (go from optimistic to pessimistic concurrency). Since we * typically do a normal increment and then, for certain conditions, do a complementary decrement, we assert that * the net effect is never a decrease. */ assert(csa == cs_addrs); /* no amount of retries should change cs_addrs from what it was at entry into gvcst_put */ assert((((int)t_tries) > lcl_t_tries) || (CDB_STAGNATE == t_tries)); DEBUG_ONLY(lcl_t_tries = t_tries;) /* update lcl_t_tries */ DEBUG_ONLY( dbg_num_iters++; assert(dbg_num_iters < ARRAYSIZE(dbg_trace_array)); dbg_trace_array[dbg_num_iters].is_fresh_tn_start = is_fresh_tn_start; dbg_trace_array[dbg_num_iters].t_tries = t_tries; is_fresh_tn_start = FALSE; dbg_trace_array[dbg_num_iters].is_dollar_incr = is_dollar_incr; GTMTRIG_ONLY(dbg_trace_array[dbg_num_iters].ztval_gvcst_put_redo = ztval_gvcst_put_redo;) dbg_trace_array[dbg_num_iters].val = val; GTMTRIG_ONLY(dbg_trace_array[dbg_num_iters].lcl_implicit_tstart = lcl_implicit_tstart;) dbg_trace_array[dbg_num_iters].is_extra_block_split = FALSE; dbg_trace_array[dbg_num_iters].retry_line = 0; split_targ = NULL; ) assert(csd == cs_data); /* To ensure they are the same even if MM extensions happened in between */ # ifdef GTM_TRIGGER gvtr_parms.num_triggers_invoked = 0; /* clear any leftover value */ assert(!ztval_gvcst_put_redo || IS_PTR_INSIDE_M_STACK(val)); is_tpwrap = FALSE; if (!skip_dbtriggers && !skip_hasht_read && parms->enable_trigger_read_and_fire) { GVTR_INIT_AND_TPWRAP_IF_NEEDED(csa, csd, gv_target, gvt_trigger, lcl_implicit_tstart, is_tpwrap, ERR_GVPUTFAIL); assert(gvt_trigger == gv_target->gvt_trigger); assert(gv_target == save_targ); /* gv_target should NOT have been mutated by the trigger reads */ if (is_tpwrap) { /* The above call to GVTR_INIT* macro created a TP transaction (by invoking op_tstart). * Save all pertinent global variable information that needs to be restored in case of * a restart. Note that the restart could happen in a nested trigger so these global * variables could have changed in value from what they were at gvcst_put entry, hence * the need to save/restore them. If this is not an implicitly tp wrapped transaction, * there is no need to do this save/restore because a restart will transfer control * back to the M code corresponding to the start of the transaction which would * automatically initialize these global variables to the appropriate values. */ assert(lcl_implicit_tstart); lcl_is_dollar_incr = is_dollar_incr; lcl_val = val; lcl_val_forjnl = val_forjnl; lcl_post_incr_mval = post_incr_mval; lcl_increment_delta_mval = increment_delta_mval; jnl_format_done = FALSE; } if (NULL != gvt_trigger) { PUSH_ZTOLDMVAL_ON_M_STACK(ztold_mval, save_msp, save_mv_chain); if (lcl_span_status) { /* Need the ability to pop vals from gvcst_put. */ parms->ztold_mval = ztold_mval; parms->save_msp = save_msp; parms->save_mv_chain = (unsigned char *)save_mv_chain; } } } REDO_ROOT_SEARCH_IF_NEEDED(want_root_search, status2); if (cdb_sc_normal != status2) { /* gvcst_root_search invoked from REDO_ROOT_SEARCH_IF_NEEDED ended up with a restart situation but did not * actually invoke t_retry. Instead, it returned control back to us asking us to restart. * Cannot enable assert (which has an assert about t_tries < CDB_STAGNATE) because it is possible for us * to get cdb_sc_gvtrootnonzero restart when t_tries == CDB_STAGNATE. Pass GOTO_RETRY parameter accordingly. */ GOTO_RETRY; } # endif assert(csd == cs_data); /* assert csd is in sync with cs_data even if there were MM db file extensions */ si = sgm_info_ptr; /* Cannot be moved before GVTR_INIT_AND_TPWRAP_IF_NEEDED macro since we could enter gvcst_put * with sgm_info_ptr NULL but could tpwrap a non-tp transaction due to triggers. In that case * we want the updated sgm_info_ptr to be noted down in si and used later. */ assert((NULL == si) || (si->update_trans)); assert(NULL != update_array); assert(NULL != update_array_ptr); assert(0 != update_array_size); assert(update_array + update_array_size >= update_array_ptr); /* When the following two asserts trip, we should change the data types of prev_first_off * and prev_next_off, so they satisfy the assert. */ assert(SIZEOF(prev_first_off) >= SIZEOF(block_offset)); assert(SIZEOF(prev_next_off) >= SIZEOF(block_offset)); prev_first_off = prev_next_off = PREV_OFF_INVALID; horiz_growth = FALSE; assert(t_tries < CDB_STAGNATE || csa->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ /* level_0 == true and no_pointers == false means that this is a directory tree data block containing pointers to roots */ level_0 = no_pointers = TRUE; assert(gv_altkey->top == gv_currkey->top); assert(gv_altkey->top == gv_keysize); assert(gv_currkey->end < gv_currkey->top); assert(gv_altkey->end < gv_altkey->top); temp_key = gv_currkey; dir_hist = NULL; ins_chain_index = 0; lcl_root = gv_target->root; tp_root = lcl_root; if (!dollar_tlevel) { CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ } else { segment_update_array_size = UA_NON_BM_SIZE(csd); ENSURE_UPDATE_ARRAY_SPACE(segment_update_array_size); curr_chain = *(off_chain *)&lcl_root; if (curr_chain.flag) { assert((SIZEOF(int) * 8) >= CW_INDEX_MAX_BITS); tp_get_cw(si->first_cw_set, (int)curr_chain.cw_index, &cse); tp_root = cse->blk; assert(tp_root); } } if (0 == tp_root) { /* Global does not exist as far as we know. Creating a new one requires validating the directory tree path which * led us to this conclusion. So scan the directory tree here and validate its history at the end of this function. * If we decide to restart due to a concurrency conflict, remember to reset gv_target->root to 0 before restarting. */ gv_target = dir_tree = csa->dir_tree; SET_GV_ALTKEY_TO_GBLNAME_FROM_GV_CURRKEY; /* set up gv_altkey to be just the gblname */ dir_hist = &gv_target->hist; status = gvcst_search(gv_altkey, NULL); RESET_GV_TARGET_LCL(save_targ); if (cdb_sc_normal != status) GOTO_RETRY; if (gv_altkey->end + 1 == dir_hist->h[0].curr_rec.match) { tmp_rp = (rec_hdr_ptr_t)(dir_hist->h[0].buffaddr + dir_hist->h[0].curr_rec.offset); EVAL_CMPC2(tmp_rp, tmp_cmpc); READ_BLK_ID(IS_64_BLK_ID(dir_hist->h[0].buffaddr), &tp_root, dir_hist->h[0].buffaddr + dir_hist->h[0].curr_rec.offset + SIZEOF(rec_hdr) + gv_altkey->end + 1 - tmp_cmpc); if (dollar_tlevel) { gvt_for_root = dir_hist->h[0].blk_num; curr_chain = *(off_chain *)&gvt_for_root; if (curr_chain.flag) tp_get_cw(si->first_cw_set, curr_chain.cw_index, &cse); else { if (NULL != (tabent = lookup_hashtab_int8(si->blks_in_use, (ublock_id *)&gvt_for_root))) tp_srch_status = tabent->value; else tp_srch_status = NULL; cse = tp_srch_status ? tp_srch_status->cse : NULL; } assert(!cse || !cse->high_tlevel); } assert(0 == gv_target->root); gv_target->root = tp_root; if (tp_root) { /* root block was 0 at start of this function but became non-zero midway. * Need to do a "gvcst_root_search" to confirm all error scenarios (e.g. ACTCOLLMISMTCH) * are taken care of. Handle that by signaling a restart. The "t_retry" call * will take care of doing the GVCST_ROOT_SEARCH in case we are not in TP. * If we are in an explicit TP, a "tp_restart" will take care of redoing the transaction * which will redo the GVCST_ROOT_SEARCH (as part of the op_gvname before invoking * op_gvput/gvcst_put/gvcst_pu2 again). If we are in an implicit TP (possible with * triggers), then control will stay inside "gvcst_put2" so we need to explicitly do * the "gvcst_root_search". But we can only do a "gvcst_redo_root_search" and that too * after the "t_retry" happened and at the beginning of the next try. This is done through * setting "want_root_search" to TRUE at the end of the current try and using this variable * to invoke the "REDO_ROOT_SEARCH_IF_NEEDED" macro at the beginning of the next try. * The reason we cannot do the REDO_ROOT_SEARCH_IF_NEEDED until after the * GVTR_INIT_AND_TPWRAP_IF_NEEDED macro is invoked in the next try is because that macro * expects no other db activity to have happened until it is done. */ status = cdb_sc_gvtrootnonzero; GOTO_RETRY; } } } blk_reserved_size = blk_size - blk_reserved_bytes; if (0 == tp_root) { /* there is no entry in the GVT (and no root), so create a new empty tree and put the name in the GVT */ /* Create the data block */ key_exists = FALSE; if (is_dollar_incr) { /* The global variable that is being $INCREMENTed does not exist. * $INCREMENT() should not signal UNDEF error but proceed with an implicit $GET(). */ assert(dollar_tlevel ? si->update_trans : update_trans); *post_incr_mval = *val; MV_FORCE_NUM(post_incr_mval); post_incr_mval->mvtype &= ~MV_STR; /* needed to force any alphanumeric string to numeric */ MV_FORCE_STR(post_incr_mval); assert(post_incr_mval->str.len); value = post_incr_mval->str; /* The MAX_REC_SIZE check could not be done in op_gvincr (like is done in op_gvput) because * the post-increment value is not known until here. so do the check here. */ ENSURE_VALUE_WITHIN_MAX_REC_SIZE(value, dir_tree); } else value = val->str; /* Potential size of a GVT leaf block containing just the new/updated record */ new_blk_size_single = SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + temp_key->end + 1 + value.len; if (new_blk_size_single > blk_reserved_size) { /* The record that is newly inserted/updated does not fit by itself in a separate block * if the current reserved-bytes for this database is taken into account. Cannot go on. */ ISSUE_RSVDBYTE2HIGH_ERROR(dir_tree); } BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); curr_rec_hdr->rsiz = SIZEOF(rec_hdr) + temp_key->end + 1 + value.len; SET_CMPC(curr_rec_hdr, 0); BLK_INIT(bs_ptr, new_blk_bs); BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); BLK_ADDR(cp1, temp_key->end + 1, unsigned char); memcpy(cp1, temp_key->base, temp_key->end + 1); BLK_SEG(bs_ptr, cp1, temp_key->end + 1); if (0 != value.len) { BLK_ADDR(va, value.len, char); memcpy(va, value.addr, value.len); BLK_SEG(bs_ptr, (unsigned char *)va, value.len); } if (0 == BLK_FINI(bs_ptr, new_blk_bs)) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } assert(new_blk_bs[0].len <= blk_reserved_size); /* Assert that new block has space for reserved bytes */ /* Create the index block */ /* Since we are making a new block we are using the version indicated by the desired_db_format in sgmnt_data * to determine the block_id size */ BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); curr_rec_hdr->rsiz = bstar_rec_size(db_long_blk_id); SET_CMPC(curr_rec_hdr, 0); BLK_INIT(bs_ptr, bs1); BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); if (db_long_blk_id) { BLK_SEG(bs_ptr, (unsigned char *)&zeroes_blkid, SIZEOF(block_id_64)); } else { BLK_SEG(bs_ptr, (unsigned char *)&v6_zeroes_blkid, SIZEOF(block_id_32)); } if (0 == BLK_FINI(bs_ptr, bs1)) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } assert(bs1[0].len <= blk_reserved_size); /* Assert that new block has space for reserved bytes */ allocation_clue = ALLOCATION_CLUE(csd->trans_hist.total_blks); next_blk_index = t_create(allocation_clue, (uchar_ptr_t)new_blk_bs, 0, 0, 0); /* create GVT data block */ ++allocation_clue; ins_chain_index = t_create(allocation_clue, (uchar_ptr_t)bs1, SIZEOF(blk_hdr) + SIZEOF(rec_hdr), next_blk_index, 1); /* create GVT index block */ root_blk_cw_index = ins_chain_index; temp_key = gv_altkey; gv_target->hist.h[0].blk_num = HIST_TERMINATOR; gv_target = dir_tree; bh = &gv_target->hist.h[0]; assert(ACT_NOT_SPECIFIED != save_targ->act); /* Global directory OR db file header defined an alternative collation sequence for this global (even if it is 0). * Store it in directory tree as part of the specific global name. This way future updates to this global will * continue to use this stored collation even if the db file header collation sequence changes (using DSE) during * the lifetime of this GVT. */ if (save_targ->act) { act_in_gvt(save_targ); /* verify that collation library version is still compatible */ /* The above call sets "save_targ->collseq" to a non-NULL value. need to keep it set so it * stays set after the GVT gets created. If this transaction restarts (i.e. we did not * create the GVT, it is okay for save_targ->collseq to be set to a non-NULL value since it * will be reinitialized when save_targ->root gets set to a non-zero value in gvcst_root_search. */ no_4byte_collhdr = 0; } else if (save_targ->act_specified_in_gld) { /* If collation has been specified in gld, even though it is zero, we need to store it in * the directory tree. Or else we could get an incorrect ACTCOLLMISMTCH error later in case * the db file header has an overriding non-zero default collation field set. */ no_4byte_collhdr = 0; } else # ifdef DEBUG { /* Starting V6.1, newly created nodes in the directory tree (DT) leaf block have a 4-byte collation header * unconditionally but in order to test the code below for correctness on pre-existing DT leaf block * records which might not contain a 4-byte collation header, we randomly enable the 4-byte collation * header for now in debug mode. Note that this can be safely done only for globals that have collation #0. * When V7 is released and the directory tree leaf records are all upgraded to contain the 4-byte collation * header unconditionally, this randomization will need to be removed. * Note that if the env var "gtm_dirtree_collhdr_always" is true, then disable this behavior (this is * usually set by tests that rely on a fixed DT leaf block layout and will fail if the layout changes). */ if (TREF(gtm_dirtree_collhdr_always)) no_4byte_collhdr = 0; else { op_fnrandom(2, random_mval); no_4byte_collhdr = MV_FORCE_INT(random_mval); } } # else no_4byte_collhdr = 0; /* for pro, add the 4-byte collation header even for "act" = 0 */ # endif if (no_4byte_collhdr) { /* No 4-byte collation header */ if (db_long_blk_id) { value.addr = (char *)&zeroes_blkid; value.len = SIZEOF(block_id_64); assert(SIZEOF(zeroes_blkid) == value.len); } else { value.addr = (char *)&v6_zeroes_blkid; value.len = SIZEOF(block_id_32); assert(SIZEOF(v6_zeroes_blkid) == value.len); } } else { /* The "block_id" will be filled in later. Fill the 4-byte collation header now */ if (db_long_blk_id) { value.addr = (char *)&zeroes_blkid_collhdr; value.len = SIZEOF(block_id_64) + COLL_SPEC_LEN; assert(SIZEOF(zeroes_blkid_collhdr) == value.len); subrec_ptr = (unsigned char *)value.addr + SIZEOF(block_id_64); } else { value.addr = (char *)&v6_zeroes_blkid_collhdr; value.len = SIZEOF(block_id_32) + COLL_SPEC_LEN; assert(SIZEOF(v6_zeroes_blkid_collhdr) == value.len); subrec_ptr = (unsigned char *)value.addr + SIZEOF(block_id_32); } assert(COLL_NCT_OFFSET > 0); assert(COLL_ACT_OFFSET > 0); assert(COLL_VER_OFFSET > 0); assert(COLL_NCT_OFFSET < COLL_SPEC_LEN); assert(COLL_ACT_OFFSET < COLL_SPEC_LEN); assert(COLL_VER_OFFSET < COLL_SPEC_LEN); *subrec_ptr = COLL_SPEC; *(subrec_ptr + COLL_NCT_OFFSET) = save_targ->nct; *(subrec_ptr + COLL_ACT_OFFSET) = save_targ->act; *(subrec_ptr + COLL_VER_OFFSET) = save_targ->ver; } no_pointers = FALSE; } else { # ifdef GTM_TRIGGER if (lcl_span_status && (NULL != ztold_mval) && !skip_hasht_read && parms->enable_trigger_read_and_fire) { /* Dealing with spanning nodes, need to use a get routine to find ztold_val. Need to do this BEFORE * gvcst_search below or we'll disrupt gv_target->hist, which is used in the subsequent constructions. * Though we don't need dollar_data, we use gvcst_dataget since it returns status, allowing retry * cleanup to be done (RESET_GV_TARGET_LCL_AND_CLR_GBL in particular) before invoking a restart. */ assert(dollar_tlevel && !skip_dbtriggers); dlr_data = DG_GETONLY; /* tell dataget we just want to do a get; we don't care about descendants */ status = gvcst_dataget(&dlr_data, ztold_mval); if (cdb_sc_normal != status) GOTO_RETRY; } # endif # if defined(DEBUG) if (gtm_white_box_test_case_enabled && (WBTEST_ANTIFREEZE_GVINCRPUTFAIL == gtm_white_box_test_case_number) && !IS_STATSDB_REG(gv_cur_region)) { status = cdb_sc_blknumerr; GOTO_RETRY; } # endif if (cdb_sc_normal != (status = gvcst_search(gv_currkey, NULL))) GOTO_RETRY; target_key_size = gv_currkey->end + 1; bh = &gv_target->hist.h[0]; key_exists = (target_key_size == bh->curr_rec.match); if (key_exists) { /* check for spanning node dummy value: a single zero byte */ rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)bh->buffaddr + bh->curr_rec.offset); GET_USHORT(rec_size, &rp->rsiz); cur_val_offset = SIZEOF(rec_hdr) + target_key_size - EVAL_CMPC((rec_hdr_ptr_t)rp); data_len = rec_size - cur_val_offset; is_dummy = (1 == data_len) && ('\0' == *(sm_uc_ptr_t)((sm_uc_ptr_t)rp + cur_val_offset)); if (is_dummy && !lcl_span_status && (csa->dir_tree != gv_target) && !(span_nodes_disallowed && csd->span_node_absent)) { /* Validate that value is really $zchar(0) and either restart or back out of gvcst_put2. * Three cases: * 1) not in TP, no triggers * 2) in TP (triggers invoked or not) * 3) weren't in TP when we entered gvcst_put2, but did an op_tstart to check for triggers */ RESET_GV_TARGET_LCL_AND_CLR_GBL(save_targ, DO_GVT_GVKEY_CHECK); if (!dollar_tlevel) { update_trans = 0; succeeded = ((trans_num)0 != t_end(&gv_target->hist, dir_hist, TN_NOT_SPECIFIED)); if (!succeeded) { /* see other t_end */ RESTORE_ZERO_GVT_ROOT_ON_RETRY(lcl_root, gv_target, dir_hist, dir_tree); jnl_format_done = !needfmtjnl; GTMTRIG_DBG_ONLY(dbg_trace_array[dbg_num_iters].retry_line = __LINE__); update_trans = UPDTRNS_DB_UPDATED_MASK; goto tn_restart; } } else { status = tp_hist(dir_hist); if (NULL != dir_hist) ADD_TO_GVT_TP_LIST(dir_tree, RESET_FIRST_TP_SRCH_STATUS_FALSE); if (cdb_sc_normal != status) GOTO_RETRY; # ifdef GTM_TRIGGER if (lcl_implicit_tstart) { /* Started TP for playing triggers. Abort and try again outside after nsb op_tstart * Otherwise, we were already in TP when we came into gvcst_put. Triggers can stay, * but finish put outside. */ POP_MVALS_FROM_M_STACK_IF_NEEDED(ztold_mval, save_msp, save_mv_chain); OP_TROLLBACK(-1); } # endif } parms->span_status = TRUE; return; } } if (is_dollar_incr) { if (key_exists) { /* $INCR is being done on an existing global variable key in the database. * the value to set the key to has to be determined by adding the existing value * with the increment passed as the input parameter "val" (of type (mval *)) to gvcst_put */ if (cdb_sc_normal != (status = gvincr_compute_post_incr(bh))) { assert(CDB_STAGNATE > t_tries); GOTO_RETRY; } } else { /* The global variable that is being $INCREMENTed does not exist. $INCREMENT() should not * signal UNDEF error but proceed with an implicit $GET() */ *post_incr_mval = *val; MV_FORCE_NUM(post_incr_mval); post_incr_mval->mvtype &= ~MV_STR; /* needed to force any alphanumeric string to numeric */ MV_FORCE_STR(post_incr_mval); assert(post_incr_mval->str.len); } assert(MV_IS_STRING(post_incr_mval)); assert(dollar_tlevel ? si->update_trans : update_trans); value = post_incr_mval->str; /* The MAX_REC_SIZE check could not be done in op_gvincr (like is done in op_gvput) because * the post-increment value is not known until here. so do the check here. */ ENSURE_VALUE_WITHIN_MAX_REC_SIZE(value, gv_target); } else value = val->str; } /* -------------------------------------------------------------------------------------------- * The code for the non-block-split case is very similar to the code in recompute_upd_array. * Any changes in either place should be reflected in the other. * -------------------------------------------------------------------------------------------- */ need_extra_block_split = FALSE; /* Assume we don't require an additional block split (most common case) */ split_to_right = FALSE; duplicate_set = FALSE; /* Assume this is NOT a duplicate set (most common case) */ split_depth = 0; split_targ = gv_target; for (succeeded = FALSE; !succeeded; no_pointers = level_0 = FALSE) { buffaddr = bh->buffaddr; long_blk_id = IS_64_BLK_ID(buffaddr); blk_id_sz = SIZEOF_BLK_ID(long_blk_id); if (long_blk_id) off_chain_sz = SIZEOF(off_chain); else off_chain_sz = SIZEOF(v6_off_chain); /* The off_chain struct used should be the same as the block_id type being used, * but we will use off_chain_sz as a separate value for code readability. */ assert(off_chain_sz == blk_id_sz); cur_blk_size = ((blk_hdr_ptr_t)buffaddr)->bsiz; target_key_size = temp_key->end + 1; /* Potential size of a block containing just the new/updated record */ new_blk_size_single = SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + target_key_size + value.len; if (new_blk_size_single > blk_reserved_size) { /* The record that is newly inserted/updated does not fit by itself in a separate block * if the current reserved-bytes for this database is taken into account. If this is not a * GVT leaf block, this situation is then possible if we are not in the final retry (and hence * dont hold crit on the region) and "temp_key->end" (and in turn "target_key_size") was * computed from a stale copy (due to concurrent updates or buffer reuse) of the global buffer * (effectively a restartable situation). If so, restart. If not issue error. */ if (no_pointers || (CDB_STAGNATE <= t_tries)) { ISSUE_RSVDBYTE2HIGH_ERROR(gv_target); } else { status = cdb_sc_mkblk; GOTO_RETRY; } } curr_rec_match = bh->curr_rec.match; curr_rec_offset = bh->curr_rec.offset; new_rec = (target_key_size != curr_rec_match); if (!new_rec && !no_pointers) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_lostcr; /* will a new cdb_sc status be better */ GOTO_RETRY; } rp = (rec_hdr_ptr_t)(buffaddr + curr_rec_offset); if (curr_rec_offset == cur_blk_size) { if ((FALSE == new_rec) && dollar_tlevel) { /* why dollar_tlevel ??? */ assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } rec_cmpc = 0; rec_size = 0; } else { GET_USHORT(rec_size, &rp->rsiz); rec_cmpc = EVAL_CMPC(rp); if ((sm_uc_ptr_t)rp + rec_size > (sm_uc_ptr_t)buffaddr + cur_blk_size) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } } prev_rec_match = bh->prev_rec.match; if (new_rec) { new_rec_size = SIZEOF(rec_hdr) + target_key_size - prev_rec_match + value.len; if (cur_blk_size <= (signed int)curr_rec_offset) /* typecast necessary to enforce "signed int" comparison */ next_rec_shrink = 0; else next_rec_shrink = curr_rec_match - rec_cmpc; delta = new_rec_size - next_rec_shrink; } else { if (rec_cmpc != prev_rec_match) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } assert(target_key_size > rec_cmpc); cur_val_offset = SIZEOF(rec_hdr) + (target_key_size - rec_cmpc); # ifdef GTM_TRIGGER if (no_pointers && (NULL != ztold_mval) && !skip_hasht_read && parms->enable_trigger_read_and_fire && !lcl_span_status) { /* Complete initialization of ztold_mval */ assert(!skip_dbtriggers); data_len = rec_size - cur_val_offset; if (0 > data_len) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_rmisalign; GOTO_RETRY; } ztold_mval->str.len = data_len; if (data_len) { if (!(IS_STP_SPACE_AVAILABLE(data_len))) { PUSH_MV_STENT(MVST_MVAL); /* protect "value" mstr from stp gcol */ pval = &mv_chain->mv_st_cont.mvs_mval; pval->str = value; pval->mvtype = MV_STR; ENSURE_STP_FREE_SPACE(data_len); value = pval->str; POP_MV_STENT(); /* pval */ } ztold_mval->str.addr = (char *)stringpool.free; memcpy(ztold_mval->str.addr, (sm_uc_ptr_t)rp + cur_val_offset, data_len); stringpool.free += data_len; } ztold_mval->mvtype = MV_STR; /* ztold_mval is now completely initialized */ } # endif new_rec_size = cur_val_offset + value.len; delta = new_rec_size - rec_size; if (!delta && value.len && !memcmp(value.addr, (sm_uc_ptr_t)rp + new_rec_size - value.len, value.len)) { duplicate_set = TRUE; if (gvdupsetnoop) { /* We do not want to touch the DB Blocks in case of a duplicate set unless the * dupsetnoop optimization is disabled. Since it is enabled, let us break right away. */ succeeded = TRUE; break; /* duplicate SET */ } } next_rec_shrink = 0; } blk_num = bh->blk_num; bh_level = bh->level; if (dollar_tlevel) { if ((SIZEOF(rec_hdr) + target_key_size - prev_rec_match + value.len) != new_rec_size) { /* why dollar_tlevel??? */ assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } chain1 = *(off_chain *)&blk_num; assert((SIZEOF(int) * 8) >= CW_INDEX_MAX_BITS); if ((1 == chain1.flag) && ((int)chain1.cw_index >= si->cw_set_depth)) { assert(si->tp_csa == csa); assert(FALSE == csa->now_crit); status = cdb_sc_blknumerr; GOTO_RETRY; } } next_rec_shrink1 = next_rec_shrink; /* Potential size of the current block including the new/updated record */ new_blk_size = cur_blk_size + delta; /* It is possible due to concurrency issues (for example if the buffer that we are planning on updating * in shared memory got reused for a different block) that "new_blk_size" is lesser than "new_blk_size_single" * In those cases, we will go into the non-block-split case but eventually we will restart. */ assert((new_blk_size >= new_blk_size_single) || (CDB_STAGNATE > t_tries)); prev_split_to_right = split_to_right; /* note down "split_to_right" corresponding to one lower level */ dont_copy_extra_record = FALSE; /* will adjust later if needed */ /* Let's use restarts to determine if it might be a good idea to split blocks */ preemptive_split = FALSE; /* Preemptive split interferes with existing gvcst_put() optimizations on the final retry. */ /* This may be an area for further investigation in the future. */ if ((2 == t_tries) && (2 >= dollar_trestart) && (0 != csd->problksplit) && (0 == bh_level) && (gv_target != csa->dir_tree) && !is_updproc && !IS_STATSDB_CSA(csa)) { if (num_recs_in_blk(buffaddr) > csd->problksplit) { preemptive_split = TRUE; dont_copy_extra_record = TRUE; /* this interferes so disable */ } } if (((new_blk_size <= blk_fill_size) || (new_blk_size <= new_blk_size_single)) && !preemptive_split) { /* Update can be done without overflowing the block's fillfactor OR the record to be updated * is the only record in the new block. Do not split block in either case. This means we might * not honour the desired FillFactor if the only record in a block exceeds the blk_fill_size, * but in this case we are guaranteed the block has room for the current reserved bytes. */ if (no_pointers) /* level zero (normal) data block: no deferred pointer chains */ { assert(!prev_split_to_right); ins_chain_offset = 0; assert(0 == ins_chain_index); } else /* index or directory level block */ { /* In case a new GVT is being created, it is possible 4-byte collation information is being * added to the leaf level directory tree record for this global name (after the 8-byte block_id). * Irrespective of the collation header, make sure ins_chain_offset points to the block_id part. */ if (db_long_blk_id) { assert(((char *)&zeroes_blkid == value.addr) || ((char *)&zeroes_blkid_collhdr == value.addr)); assert((SIZEOF(zeroes_blkid) == value.len) || (SIZEOF(zeroes_blkid_collhdr) == value.len)); } else { assert(((char *)&v6_zeroes_blkid == value.addr) || ((char *)&v6_zeroes_blkid_collhdr == value.addr)); assert((SIZEOF(v6_zeroes_blkid) == value.len) || (SIZEOF(v6_zeroes_blkid_collhdr) == value.len)); } ins_chain_offset = (int)(curr_rec_offset + new_rec_size - value.len); } BLK_INIT(bs_ptr, bs1); if (0 == rc_set_fragment) { BLK_SEG(bs_ptr, buffaddr + SIZEOF(blk_hdr), curr_rec_offset - SIZEOF(blk_hdr)); BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); curr_rec_hdr->rsiz = new_rec_size; SET_CMPC(curr_rec_hdr, prev_rec_match); BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); BLK_ADDR(cp1, target_key_size - prev_rec_match, unsigned char); memcpy(cp1, temp_key->base + prev_rec_match, target_key_size - prev_rec_match); BLK_SEG(bs_ptr, cp1, target_key_size - prev_rec_match); if (0 != value.len) { BLK_ADDR(va, value.len, char); memcpy(va, value.addr, value.len); BLK_SEG(bs_ptr, (unsigned char *)va, value.len); } if (!new_rec) rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + rec_size); n = (int)(cur_blk_size - ((sm_uc_ptr_t)rp - buffaddr)); if (n > 0) { if (new_rec) { BLK_ADDR(next_rec_hdr, SIZEOF(rec_hdr), rec_hdr); tmp_rsiz = rec_size - next_rec_shrink; next_rec_hdr->rsiz = tmp_rsiz; SET_CMPC(next_rec_hdr, curr_rec_match); BLK_SEG(bs_ptr, (sm_uc_ptr_t)next_rec_hdr, SIZEOF(rec_hdr)); next_rec_shrink += SIZEOF(rec_hdr); } if (n < next_rec_shrink) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } BLK_SEG(bs_ptr, (sm_uc_ptr_t)rp + next_rec_shrink, n - next_rec_shrink); if (prev_split_to_right) { /* This is an index block where a lower level block had a block split * to the right. The newly inserted record will be added before the * split point at this level. But the new block # created at the lower * level will be inserted in next_rec and the newly inserted record * will inherit the block number already there in "next_rec". Do those * adjustments here. */ assert(new_rec); /* this is to ensure "tmp_rsiz" would have been set above * and that "rp" & rec_size are still in sync. */ ins_chain_offset += tmp_rsiz; assert(value.len); /* so "va" would be initialized by the * "BLK_ADDR(va, ...)" call above. */ assert(((sm_uc_ptr_t)rp + rec_size) <= (buffaddr + cur_blk_size)); /* or else we would have restarted above with "cdb_sc_mkblk" */ assert(blk_id_sz == value.len); memcpy(va, ((sm_uc_ptr_t)rp + rec_size) - value.len, value.len); } } } else { /* With GT.M TRIGGERS, it is not clear how the RC protocol will work. The below assert is to * be informed whenever such usage happens (expected to be really rare) and handle it right * then instead of worrying about it during the initial trigger implementation. */ assert(FALSE); curr_rec_hdr = (rec_hdr_ptr_t)(buffaddr + curr_rec_offset); EVAL_CMPC2(curr_rec_hdr, tmp_cmpc); /* First piece is block prior to record + key + data prior to fragment */ BLK_SEG(bs_ptr, buffaddr + SIZEOF(blk_hdr), curr_rec_offset - SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + rc_set_fragment + gv_currkey->end + 1 - tmp_cmpc); /* Second piece is fragment itself */ BLK_ADDR(va, value.len, char); memcpy(va, value.addr, value.len); BLK_SEG(bs_ptr, (unsigned char *)va, value.len); /* Third piece is data after fragment + rest of block after record */ n = (int)(cur_blk_size - ((sm_uc_ptr_t)curr_rec_hdr - buffaddr) - SIZEOF(rec_hdr) - (gv_currkey->end + 1 - tmp_cmpc) - rc_set_fragment - value.len); if (0 < n) BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr + gv_currkey->end + 1 - tmp_cmpc + rc_set_fragment + value.len, n); } if (0 == BLK_FINI(bs_ptr, bs1)) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } assert(bs1[0].len <= blk_reserved_size); /* Assert that new block has space for reserved bytes */ /* If we had a block split to right in a lower level, we need to disable the indexmod optimization * (see gdscc.h for a description of that optimization) at this level. Below is an example describing why. * Let us say Blk6 is a level-1 index block with a *-record pointing to a level-0 block Blk5 that has say * a record ^a(1). Let us say process P1 does a SET of ^a(2) to a value too big to fit in Blk5. And * the split_to_right scheme kicks in placing this record in a new block Blk7 (right sibling of Blk5). * Just before P1 commits this, let us say another process P2 does a SET of ^a(3) to a value that is * small enough to fit in Blk5. Since P1 has not yet committed its changes, Blk6 still contains a *-record * and so P2 descends that into Blk5 and finds ^a(3) can fit in and computes its update array based on that. * When P2 goes to commit time though, it finds Blk6 changed (by P1's commit). But P1 did not touch Blk5 * because it used the split_to_right scheme. Therefore P2 incorrectly goes ahead with the commit even * though the index block (Blk6) has changed. This is because of the indexmod optimization. This will result * in Blk5 containins ^a(1),^a(3) and a right sibling block Blk7 containing ^a(2). Effectively a * DBKEYGTIND integrity error. Hence the GDS_WRITE_KILLTN usage below. */ cse = t_write(bh, (unsigned char *)bs1, ins_chain_offset, ins_chain_index, bh_level, FALSE, FALSE, (!prev_split_to_right ? GDS_WRITE_PLAIN : GDS_WRITE_KILLTN)); assert(cse); assert(!dollar_tlevel || !cse->high_tlevel); if ((0 != ins_chain_offset) && (dollar_tlevel ? (0 != cse->first_off) : !prev_split_to_right)) { if (dollar_tlevel) { /* formerly tp_offset_chain - inserts a new_entry in the chain */ assert((NULL != cse->new_buff) || horiz_growth && cse->low_tlevel->new_buff && (buffaddr == cse->low_tlevel->new_buff)); assert(!prev_split_to_right && (0 == cse->next_off)); assert(ins_chain_offset > (signed)SIZEOF(blk_hdr)); /* we want signed comparison */ offset_sum = cse->first_off; curr = buffaddr + offset_sum; assert(new_rec); assert(offset_sum != curr_rec_offset); /* The typecast is needed below to enforce a "signed int" (versus "unsigned int") compare */ if (offset_sum >= (signed int)curr_rec_offset) { /* new record is prior to 1st existing chain record, id new one as 1st */ /* first_off-------------v--------------------v * [blk_hdr]...[new rec ( )]...[existing rec ( )]... */ cse->next_off = value.len + (offset_sum - curr_rec_offset - next_rec_shrink1); cse->first_off = ins_chain_offset; } else { if (horiz_growth) { old_cse = cse->low_tlevel; assert(old_cse->first_off); assert(old_cse && old_cse->done); assert(!old_cse->undo_next_off[0] && !old_cse->undo_offset[0]); } /* find chain records before and after the new one */ for ( ; ; curr += curr_chain.next_off) { /* try to make offset_sum identify the 1st chain entry after new record */ READ_OFF_CHAIN(long_blk_id, &curr_chain, &v6_chain, curr); assert(1 == curr_chain.flag); if (0 == curr_chain.next_off) break; offset_sum += curr_chain.next_off; assert(offset_sum != curr_rec_offset); /* The typecast is needed below to enforce a "signed int" comparison */ if (offset_sum >= (signed int)curr_rec_offset) break; } /* store next_off in old_cse before changing it in the buffer (for rolling back) */ if (horiz_growth) { old_cse->undo_next_off[0] = curr_chain.next_off; old_cse->undo_offset[0] = (block_offset)(curr - buffaddr); assert(old_cse->undo_offset[0]); } if (0 == curr_chain.next_off) { /* the last chain record precedes the new record: just update it */ /* ---|---------------v * [blk_hdr]...[existing rec ( )]...[new rec ( )]... */ curr_chain.next_off = ins_chain_offset - offset_sum; WRITE_OFF_CHAIN(long_blk_id, &curr_chain, &v6_chain, curr); } else { /* update the chain record before the new one */ /* ---|---------------v--------------------v * [blk_hdr]...[existing rec ( )]...[new rec ( )]...[existing rec ( )] */ assert((1ULL << NEXT_OFF_MAX_BITS) > (ins_chain_offset - (curr - buffaddr))); curr_chain.next_off = (unsigned int)(ins_chain_offset - (curr - buffaddr)); WRITE_OFF_CHAIN(long_blk_id, &curr_chain, &v6_chain, curr); cse->next_off = value.len + (offset_sum - curr_rec_offset - next_rec_shrink1); } } assert((ins_chain_offset + (int)cse->next_off) <= (delta + (sm_long_t)cur_blk_size - off_chain_sz)); } else if (mu_reorg_process || mu_reorg_upgrd_dwngrd_in_prog) { /* turn off t_end (non-TP) optimization while outside of crit */ cse->recompute_list_head = cse->recompute_list_tail = NULL; cse = NULL; } } succeeded = TRUE; if (level_0) { if (new_rec) { /* New record insertion at leaf level. gvcst_search would have already updated clue to * reflect the new key, but we need to fix the search history to keep it in sync with clue. * This search history (and clue) will be used by the NEXT call to gvcst_search. * Note that clue.end could be 0 at this point (see "Clue less than first rec, invalidate" * comment in gvcst_search) in which case the below assignment is unnecessary (though does * not hurt) but we want to avoid the if check (since we expect clue to be non-zero mostly). */ assert((0 == gv_target->clue.end) || (gv_target->clue.end + 1 == target_key_size)); assert(1 < target_key_size); assert(bh->curr_rec.match != target_key_size); bh->curr_rec.match = target_key_size; } /* ------------------------------------------------------------------------------------------------- * We have to maintain information for future recomputation only if the following are satisfied * 1) The block is a leaf-level block * 2) The global has NOISOLATION turned ON * 3) The cw_set_element hasn't encountered a block-split or a kill * 4) We don't need an extra_block_split * * We can also add an optimization that only cse's of mode gds_t_write need to have such an update, * but because of the belief that for a nonisolated variable, we will very rarely encounter a * situation where a created block will have some new keys added to it, and that adding * the check slows down the normal code, we don't do that check here. * ------------------------------------------------------------------------------------------------- */ if (cse && gv_target->noisolation && !cse->write_type && !need_extra_block_split && (dollar_tlevel || !is_dollar_incr)) { if (is_dollar_incr) { assert(dollar_tlevel); /* See comment in ENSURE_VALUE_WITHIN_MAX_REC_SIZE macro * definition for why the below macro call is necessary. */ ADD_TO_GVT_TP_LIST(gv_target, RESET_FIRST_TP_SRCH_STATUS_FALSE); RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_GVINCRISOLATION, 2, gv_target->gvname.var_name.len, gv_target->gvname.var_name.addr); } if (NULL == cse->recompute_list_tail || 0 != memcmp(gv_currkey->base, cse->recompute_list_tail->keybuf.split.base, gv_currkey->top)) { tempkv = (dollar_tlevel ? (key_cum_value *)get_new_element(si->recompute_list, 1) : &(TREF(non_tp_noiso_key_n_value))); tempkv->keybuf.key = *(gv_key_nobase *)gv_currkey; tempkv->next = NULL; memcpy(tempkv->keybuf.split.base, gv_currkey->base, gv_currkey->end + 1); if (NULL == cse->recompute_list_head) { assert(NULL == cse->recompute_list_tail); cse->recompute_list_head = tempkv; } else if (dollar_tlevel) cse->recompute_list_tail->next = tempkv; cse->recompute_list_tail = tempkv; } else tempkv = cse->recompute_list_tail; assert(0 == val->str.len || ((val->str.len == bs1[4].len) && 0 == memcmp(val->str.addr, bs1[4].addr, val->str.len))); tempkv->value.len = val->str.len; /* x.addr not used if val->str.len is 0 */ tempkv->value.addr = dollar_tlevel ? (char *)bs1[4].addr : val->str.addr; } } } else { /* Block split required */ split_depth++; gv_target->clue.end = 0; /* invalidate clue */ /* Potential size of the left and right blocks, including the new record */ new_blk_size_l = curr_rec_offset + new_rec_size; new_blk_size_r = new_blk_size_single + cur_blk_size - curr_rec_offset - (new_rec ? next_rec_shrink : rec_size); assert(new_blk_size_single <= blk_reserved_size); assert(blk_reserved_size >= blk_fill_size); prev_rec_offset = bh->prev_rec.offset; /* Decide which side (left or right) the new record goes. Ensure either side has at least one record. * This means we might not honor the desired FillFactor if the only record in a block exceeds the * blk_fill_size, but in this case we are guaranteed the block has room for the current reserved bytes. * The typecast of curr_rec_offset is needed below to enforce a "signed int" comparison. */ if (new_blk_size_r > blk_fill_size) { new_rec_goes_to_right = (new_blk_size_r == new_blk_size_single); last_split_dir = NEWREC_DIR_FORCED; /* no choice in split direction */ } else if (new_blk_size_l > blk_fill_size) { new_rec_goes_to_right = TRUE; last_split_dir = NEWREC_DIR_FORCED; /* no choice in split direction */ } else { /* new_rec can go in either direction without any issues of fitting in. * This is where we need to use a few heuristics to ensure good block space utilization. * We note down which direction (left or right) the new record went in after the split. * We use that as the heuristic to identify the direction of data loading and do the * splits accordingly for future updates. */ assert(!level_0 || !IS_STATSDB_CSA(csa)); last_split_dir = (enum split_dir)gv_target->last_split_direction[bh_level]; if (NEWREC_DIR_FORCED == last_split_dir) { /* dont have prior information to use heuristic. choose whichever side is less full. * if this turns out to not be the correct choice, we will correct ourselves at the * time of the next block split at the same level. */ last_split_dir = (new_blk_size_l < new_blk_size_r) ? NEWREC_DIR_LEFT : NEWREC_DIR_RIGHT; } else { /* Last block split at this level chose a specific direction for new_rec. See if * that heuristic worked. This is done by checking if the block # that new_rec went * into previously is the same block that is being split now. If so, that means the * previous choice of direction was actually not optimal. So try the other direction now. */ last_split_bnum = gv_target->last_split_blk_num[bh_level]; if (dollar_tlevel) { chain2 = *(off_chain *)&last_split_bnum; if (chain1.flag == chain2.flag) { if (!chain1.flag) blk_match = (blk_num == last_split_bnum); else { assert(chain1.cw_index < si->cw_set_depth); blk_match = (chain1.cw_index == chain2.cw_index); } } else blk_match = FALSE; } else { DEBUG_ONLY(chain1 = *(off_chain *)&last_split_bnum;) assert(!chain1.flag); blk_match = (blk_num == last_split_bnum); } is_split_dir_left = (NEWREC_DIR_LEFT == last_split_dir); if (blk_match) /* switch direction since last choice did not seem to have worked */ last_split_dir = is_split_dir_left ? NEWREC_DIR_RIGHT : NEWREC_DIR_LEFT; else { /* blk# did not match means there is a high likelihood that the current split * is happening in the OTHER sibling block from the previous block split operation * at the same level. There is no easy way of confirming this so we assume the * heuristic is doing its job, unless we see evidence otherwise. And that evidence * is IF the block sizes of the left and right halves dont match the direction of * choice (e.g. if we choose NEWREC_DIR_LEFT, we expect the right block to be * almost full and the left block to be almost empty and vice versa). * In this case too switch the direction. */ if (is_split_dir_left) { if (new_blk_size_l > new_blk_size_r) last_split_dir = NEWREC_DIR_RIGHT; } else { if (new_blk_size_l < new_blk_size_r) last_split_dir = NEWREC_DIR_LEFT; } } } new_rec_goes_to_right = (NEWREC_DIR_RIGHT == last_split_dir); } last_split_direction[bh_level] = (char)last_split_dir; assert(!prev_split_to_right || !level_0); if (new_rec_goes_to_right) { if (!new_rec || (new_blk_size_r != new_blk_size_single) || dollar_tlevel) { /* Left side of this block will be split off into a new block. * The new record and the right side of this block will remain in this block. */ split_to_right = FALSE; /* Prepare new (left-side) block */ BLK_INIT(bs_ptr, bs1); if (level_0) { /* If this is a statsdb, it means we are potentially about to move an existing * leaf-level ^%YGS node from one block to another (a no-no). Assert accordingly. */ # ifdef DEBUG if (IS_STATSDB_CSA(csa)) TREF(donot_commit) |= DONOTCOMMIT_GVCST_PUT_SPLIT_TO_RIGHT; # endif BLK_SEG(bs_ptr, buffaddr + SIZEOF(blk_hdr), curr_rec_offset - SIZEOF(blk_hdr)); } else { /* for index records, the record before the split becomes a new *-key */ /* Note: If the block split was caused by our appending the new record * to the end of the block, this code causes the record PRIOR to the * current *-key to become the new *-key. */ BLK_SEG(bs_ptr, buffaddr + SIZEOF(blk_hdr), prev_rec_offset - SIZEOF(blk_hdr)); BLK_ADDR(new_star_hdr, SIZEOF(rec_hdr), rec_hdr); new_star_hdr->rsiz = bstar_rec_size(long_blk_id); SET_CMPC(new_star_hdr, 0); BLK_SEG(bs_ptr, (sm_uc_ptr_t)new_star_hdr, SIZEOF(rec_hdr)); BLK_SEG(bs_ptr, (sm_uc_ptr_t)rp - blk_id_sz, blk_id_sz); } new_blk_bs = bs1; if (0 == BLK_FINI(bs_ptr, bs1)) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } /* We want to assert that the left block has enough space for reserved bytes but * it is possible that it DOES NOT have enough space for reserved bytes if the pre-split * block was previously populated with a very low reserved bytes setting and if the current * reserved bytes setting is much higher than what the chosen split point would free up. * This is an issue waiting to be fixed by GTM-6522. Until then the following assert * has to remain commented out. * * assert(bs1[0].len <= blk_reserved_size); */ ins_chain_offset = no_pointers ? 0 : (int)(SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + target_key_size); left_hand_offset = left_hand_index = 0; /* Prepare the right-side block (is an existing block) */ BLK_INIT(bs_ptr, bs1); } else { /* The to-be inserted key is the only one on the right side and is non-existent. * No point moving all the current content of the existing block into a new left side block * and inserting the new record in the current block. It is less node motion to create * a new block and insert the new record there and leave the current block untouched. * Note that this is necessary for statsDB where a ^%YGS node once added in a particular * block offset is assumed to stay there irrespective of block splits in surrounding * nodes/blocks by other concurrent processes. */ split_to_right = TRUE; /* Since we come here only if "new_blk_size_r == new_blk_size_single", it is not possible * to come here in case of an index block since there will always be a *-key in that * case and that will be to the right of the newly inserted record so "new_blk_size_r" * would always be greater than "new_blk_size_single" in that case. But in case of a * restartable situation it is possible to come here. Assert accordingly using the * TREF(donot_commit) scheme. */ # ifdef DEBUG if (!level_0) TREF(donot_commit) |= DONOTCOMMIT_GVCST_PUT_SPLIT_TO_RIGHT; # endif /* Since the "t_create" call (for the new block) below relies on "left_hand_index" and * "left_hand_offset" to be set, initialize them even though the newly created block * is actually the right side block (not the left side). */ left_hand_offset = no_pointers ? 0 : (int)(SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + target_key_size); left_hand_index = ins_chain_index; ins_chain_index = ins_chain_offset = 0; /* Prepare the right-side block (is a newly created block) */ BLK_INIT(bs_ptr, bs1); new_blk_bs = bs1; } if (!new_rec) rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + rec_size); BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); curr_rec_hdr->rsiz = target_key_size + SIZEOF(rec_hdr) + value.len; SET_CMPC(curr_rec_hdr, 0); BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); BLK_ADDR(cp1, target_key_size, unsigned char); memcpy(cp1, temp_key->base, target_key_size); BLK_SEG(bs_ptr, cp1, target_key_size); if (0 != value.len) { BLK_ADDR(va, value.len, char); memcpy(va, value.addr, value.len); BLK_SEG(bs_ptr, (unsigned char *)va, value.len); } if (buffaddr + cur_blk_size > (sm_uc_ptr_t)rp) { assert(!split_to_right); BLK_ADDR(next_rec_hdr, SIZEOF(rec_hdr), rec_hdr); GET_USHORT(tmp_rsiz, &rp->rsiz); tmp_rsiz -= next_rec_shrink; next_rec_hdr->rsiz = tmp_rsiz; SET_CMPC(next_rec_hdr, new_rec ? curr_rec_match : EVAL_CMPC(rp)); BLK_SEG(bs_ptr, (sm_uc_ptr_t)next_rec_hdr, SIZEOF(rec_hdr)); next_rec_shrink += SIZEOF(rec_hdr); n = cur_blk_size - INTCAST(((sm_uc_ptr_t)rp - buffaddr)) - next_rec_shrink; if (0 > n) /* want signed compare as 'n' can be negative */ { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } BLK_SEG(bs_ptr, (sm_uc_ptr_t)rp + next_rec_shrink, n); if (prev_split_to_right) { /* Do block# adjustment like done above (search for "if (split_to_right)") */ assert(new_rec); /* ensure "rp" & rec_size are still in sync */ ins_chain_offset += tmp_rsiz; assert(value.len); /* so "va" would be initialized by the * "BLK_ADDR(va, ...)" call above. */ assert(((sm_uc_ptr_t)rp + rec_size) <= (buffaddr + cur_blk_size)); /* or else we would have restarted above with "cdb_sc_mkblk" */ assert(blk_id_sz == value.len); memcpy(va, ((sm_uc_ptr_t)rp + rec_size) - value.len, value.len); } } if (0 == BLK_FINI(bs_ptr, bs1)) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } /* Assert that right block has space for reserved bytes */ assert(bs1[0].len <= blk_reserved_size); assert(gv_altkey->top == gv_currkey->top); assert(gv_altkey->end < gv_altkey->top); if (temp_key != gv_altkey) { assert(temp_key == gv_currkey); src_key = gv_currkey; temp_key = gv_altkey; } else { if (NULL == gv_altkey2) GVKEY_INIT(gv_altkey2, gv_keysize); COPY_KEY(gv_altkey2, temp_key); src_key = gv_altkey2; } if (cdb_sc_normal != (status = gvcst_expand_prev_key(bh, src_key, temp_key))) GOTO_RETRY; } else { /* Insert in left hand (new) block */ split_to_right = FALSE; if (!level_0) { /* In case of an index block, as long as the current record is not a *-record * (i.e. last record in the block) and copying an extra record into the left * block does not cause it to exceed the fill factor, copy an additional record. * Not doing the extra record copy for index blocks (was the case pre-V54002) has * been seen to create suboptimally filled index blocks (as low as 15% fillfactor) * depending on the patterns of updates. */ assert(new_rec); copy_extra_record = ((bstar_rec_size(long_blk_id) != rec_size) && ((new_blk_size_l + bstar_rec_size(long_blk_id)) <= blk_fill_size)); } else copy_extra_record = (!dont_copy_extra_record && (0 == prev_rec_offset) && (NEWREC_DIR_LEFT == last_split_dir) && new_rec && (SIZEOF(blk_hdr) < cur_blk_size)); BLK_INIT(bs_ptr, bs1); if (no_pointers) left_hand_offset = 0; else { left_hand_offset = curr_rec_offset + SIZEOF(rec_hdr); /* *-record is usually last record */ if (level_0 || copy_extra_record) left_hand_offset += target_key_size - prev_rec_match; /* exceptions for *-record */ } left_hand_index = ins_chain_index; /* copy over cw_set index from child level block create */ ins_chain_index = ins_chain_offset = 0; BLK_SEG(bs_ptr, buffaddr + SIZEOF(blk_hdr), curr_rec_offset - SIZEOF(blk_hdr)); if (level_0) { /* After the initial split, will this record fit into the new left block? * If not, this pass will make room and we will do another block split on the next pass. */ assert((blk_seg_cnt + SIZEOF(rec_hdr) + target_key_size - prev_rec_match + value.len) == new_blk_size_l); assert((new_blk_size_single <= new_blk_size_l) || (CDB_STAGNATE > t_tries)); assert((new_blk_size_single != new_blk_size_l) || ((0 == prev_rec_offset) && (SIZEOF(blk_hdr) == curr_rec_offset))); assert((new_blk_size_single >= new_blk_size_l) || ((SIZEOF(blk_hdr) <= prev_rec_offset) && (SIZEOF(blk_hdr) < curr_rec_offset))); if ((new_blk_size_l > blk_fill_size) && (new_blk_size_l > new_blk_size_single)) { /* There is at least one existing record to the left of the split point. * Do the initial split this pass and make an extra split next pass. */ need_extra_block_split = TRUE; DEBUG_ONLY(dbg_trace_array[dbg_num_iters].is_extra_block_split = TRUE;) } else { BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); curr_rec_hdr->rsiz = new_rec_size; SET_CMPC(curr_rec_hdr, prev_rec_match); BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); BLK_ADDR(cp1, target_key_size - prev_rec_match, unsigned char); memcpy(cp1, temp_key->base + prev_rec_match, target_key_size - prev_rec_match); BLK_SEG(bs_ptr, cp1, target_key_size - prev_rec_match); if (0 != value.len) { BLK_ADDR(va, value.len, char); memcpy(va, value.addr, value.len); BLK_SEG(bs_ptr, (unsigned char *)va, value.len); } if (copy_extra_record) { n = rec_size - curr_rec_match; /* typecast needed below to enforce a "signed int" comparison */ if ((n + (signed int)curr_rec_offset + new_rec_size) > blk_fill_size) copy_extra_record = FALSE; else { BLK_ADDR(extra_rec_hdr, SIZEOF(rec_hdr), rec_hdr); extra_rec_hdr->rsiz = n; SET_CMPC(extra_rec_hdr, curr_rec_match); BLK_SEG(bs_ptr, (sm_uc_ptr_t)extra_rec_hdr, SIZEOF(rec_hdr)); if (n < (signed)SIZEOF(rec_hdr)) /* want signed compare */ { /* as 'n' can be negative */ assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } BLK_SEG(bs_ptr, buffaddr + SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + curr_rec_match, n - SIZEOF(rec_hdr)); new_blk_size_l += n; } } } } else { if (copy_extra_record) { BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); curr_rec_hdr->rsiz = new_rec_size; SET_CMPC(curr_rec_hdr, prev_rec_match); BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); BLK_ADDR(cp1, target_key_size - prev_rec_match, unsigned char); memcpy(cp1, temp_key->base + prev_rec_match, target_key_size - prev_rec_match); BLK_SEG(bs_ptr, cp1, target_key_size - prev_rec_match); assert(value.len); BLK_ADDR(va, value.len, char); memcpy(va, value.addr, value.len); BLK_SEG(bs_ptr, (unsigned char *)va, value.len); /* Do "prev_split_to_right" processing for "copy_extra_record" case */ if (prev_split_to_right) { /* Do block# adjustment like done above (searchstr "if (split_to_right)") */ left_hand_offset += bstar_rec_size(long_blk_id); assert(((sm_uc_ptr_t)rp + rec_size) <= (buffaddr + cur_blk_size)); /* or else we would have restarted above with "cdb_sc_mkblk" */ assert(blk_id_sz == value.len); memcpy(va, ((sm_uc_ptr_t)rp + rec_size) - value.len, value.len); } new_blk_size_l += bstar_rec_size(long_blk_id); } else new_blk_size_l = curr_rec_offset + bstar_rec_size(long_blk_id); BLK_ADDR(new_star_hdr, SIZEOF(rec_hdr), rec_hdr); new_star_hdr->rsiz = bstar_rec_size(long_blk_id); SET_CMPC(new_star_hdr, 0); BLK_SEG(bs_ptr, (sm_uc_ptr_t)new_star_hdr, SIZEOF(rec_hdr)); if (!copy_extra_record) { /* Do "prev_split_to_right" processing for "!copy_extra_record" case */ if (prev_split_to_right) { /* Do block# adjustment like done above (searchstr "if (split_to_right)"). * The newly inserted record is going in the left block whereas the record * to its right is going into the right block after the split at this level. * Adjust accordingly. */ ins_chain_index = left_hand_index; ins_chain_offset = SIZEOF(blk_hdr) + rec_size + rec_cmpc - blk_id_sz; left_hand_index = left_hand_offset = 0; assert(blk_id_sz == value.len); BLK_ADDR(va, value.len, char); assert(((sm_uc_ptr_t)rp + rec_size) <= (buffaddr + cur_blk_size)); /* or else we would have restarted above with "cdb_sc_mkblk" */ memcpy(va, ((sm_uc_ptr_t)rp + rec_size) - value.len, value.len); BLK_SEG(bs_ptr, (unsigned char *)va, value.len); } else BLK_SEG(bs_ptr, (unsigned char *)&zeroes_blkid, blk_id_sz); } else BLK_SEG(bs_ptr, (sm_uc_ptr_t)rp + rec_size - blk_id_sz, blk_id_sz); } new_blk_bs = bs1; if (0 == BLK_FINI(bs_ptr, bs1)) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } /* We want to assert that the left block has enough space for reserved bytes but * it is possible that it DOES NOT have enough space for reserved bytes if the pre-split * block was previously populated with a very low reserved bytes setting and if the current * reserved bytes setting is much higher than what the chosen split point would free up. * This is an issue waiting to be fixed by C9K01-003221. Until then the following assert * has to remain commented out. * * assert(bs1[0].len <= blk_reserved_size); */ /* assert that both !new_rec and copy_extra_record can never be TRUE at the same time */ assert(new_rec || !copy_extra_record); if (!new_rec || copy_extra_record) { /* Should guard for empty block??? */ /* Before advancing one record, store rec_size and rec_cmpc for current record */ tmp_rp = rp; rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + rec_size); rec_cmpc = EVAL_CMPC(rp); GET_USHORT(rec_size, &rp->rsiz); } if (copy_extra_record) { assert(gv_altkey->top == gv_currkey->top); assert(gv_altkey->end < gv_altkey->top); if (temp_key != gv_altkey) { assert(temp_key == gv_currkey); src_key = gv_currkey; temp_key = gv_altkey; } else { if (NULL == gv_altkey2) GVKEY_INIT(gv_altkey2, gv_keysize); COPY_KEY(gv_altkey2, temp_key); src_key = gv_altkey2; } if (cdb_sc_normal != (status = gvcst_expand_curr_key(bh, src_key, temp_key))) GOTO_RETRY; } else if (temp_key != gv_altkey) { memcpy(gv_altkey, temp_key, SIZEOF(gv_key) + temp_key->end + 1); temp_key = gv_altkey; } BLK_INIT(bs_ptr, bs1); BLK_ADDR(next_rec_hdr, SIZEOF(rec_hdr), rec_hdr); next_rec_hdr->rsiz = rec_size + rec_cmpc; SET_CMPC(next_rec_hdr, 0); BLK_SEG(bs_ptr, (sm_uc_ptr_t)next_rec_hdr, SIZEOF(rec_hdr)); BLK_ADDR(cp1, rec_cmpc, unsigned char); memcpy(cp1, temp_key->base, rec_cmpc); BLK_SEG(bs_ptr, cp1, rec_cmpc); n = cur_blk_size - INTCAST(((sm_uc_ptr_t)rp - buffaddr)) - SIZEOF(rec_hdr); if (0 > n) /* want signed compare as 'n' can be negative */ { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } BLK_SEG(bs_ptr, (sm_uc_ptr_t)(rp + 1), n); if (0 == BLK_FINI(bs_ptr, bs1)) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } /* We want to assert that the right block has enough space for reserved bytes but * it is possible that it DOES NOT have enough space for reserved bytes if the pre-split * block was previously populated with a very low reserved bytes setting and if the current * reserved bytes setting is much higher than what the chosen split point would free up. * This is an issue waiting to be fixed by C9K01-003221. Until then the following assert * has to remain commented out. * * assert(bs1[0].len <= blk_reserved_size); */ } next_blk_index = t_create(blk_num, (uchar_ptr_t)new_blk_bs, left_hand_offset, left_hand_index, bh_level); /* If "split_to_right" is TRUE, the existing block is untouched so no need to worry about any tp * chains that could be split due to the block-split. So we can safely skip the below chain stuff. * But currently if "dollar_tlevel" is TRUE, then we are guaranteed "split_to_right" is FALSE so * we do not need a "&& !split_to_right" check below. We will need it though if/when "split_to_right" * functionality is enabled even for TP. */ assert(!dollar_tlevel || !split_to_right); if (!no_pointers && dollar_tlevel) { /* there may be chains */ assert(new_rec); curr_chain = *(off_chain *)&blk_num; if (curr_chain.flag) tp_get_cw(si->first_cw_set, curr_chain.cw_index, &cse); else { if (NULL != (tabent = lookup_hashtab_int8(si->blks_in_use, (ublock_id *)&blk_num))) tp_srch_status = tabent->value; else tp_srch_status = NULL; cse = tp_srch_status ? tp_srch_status->cse : NULL; } assert(!cse || !cse->high_tlevel); if ((NULL != cse) && (0 != cse->first_off)) { /* there is an existing chain: fix to account for the split */ assert(NULL != cse->new_buff); assert(cse->done); assert(0 == cse->next_off); cse_new = si->last_cw_set; assert(!cse_new->high_tlevel); assert(0 == cse_new->next_off); assert(0 == cse_new->first_off); assert(cse_new->ins_off == left_hand_offset); assert(cse_new->index == left_hand_index); assert(cse_new->level == cse->level); cse_first_off = (int4)cse->first_off; offset_sum = cse_first_off; curr = buffaddr + offset_sum; READ_OFF_CHAIN(long_blk_id, &curr_chain, &v6_chain, curr); assert(1 == curr_chain.flag); /* Determine "last_possible_left_offset" and "extra_record_blkid_off" */ copy_extra_record = (!new_rec_goes_to_right && copy_extra_record); if (copy_extra_record) { assert(!new_rec_goes_to_right); GET_USHORT(tmp_rsiz, &tmp_rp->rsiz); tmp_cmpc = EVAL_CMPC(tmp_rp); if (level_0) { /* Directory Tree leaf level block. Determine the offset within the * extra record where "block_id" is stored. It is guaranteed to be * the last 4/8-bytes of the record for all GVT index and DT index blocks * but not for DT leaf blocks where it is the first 4/8-bytes * (after the key) and a 4-byte collation header could optionally * follow it. */ extra_record_blkid_off = SIZEOF(rec_hdr) + temp_key->end + 1 - tmp_cmpc; assert(((tmp_rsiz - extra_record_blkid_off) == off_chain_sz) || ((tmp_rsiz - extra_record_blkid_off) == off_chain_sz + COLL_SPEC_LEN)); } else extra_record_blkid_off = tmp_rsiz - off_chain_sz; assert(extra_record_blkid_off); last_possible_left_offset = curr_rec_offset + extra_record_blkid_off; } else { extra_record_blkid_off = 0; if (level_0) { /* Directory Tree leaf level block. Find end of key in prev_rec * since block_id starts right after that. */ if (!prev_rec_offset) cp1 = buffaddr; else { cp1 = buffaddr + prev_rec_offset + SIZEOF(rec_hdr); assert(((sm_uc_ptr_t)rp - buffaddr) == curr_rec_offset); assert(cp1 < (sm_uc_ptr_t)rp); for ( ; cp1 < (sm_uc_ptr_t)rp; ) { if ((KEY_DELIMITER == *cp1++) && (KEY_DELIMITER == *cp1)) break; } if (++cp1 > ((sm_uc_ptr_t)rp - off_chain_sz)) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } assert(((cp1 + off_chain_sz) == (sm_uc_ptr_t)rp) || ((cp1 + off_chain_sz + COLL_SPEC_LEN) == (sm_uc_ptr_t)rp)); } } last_possible_left_offset = level_0 ? INTCAST(cp1 - buffaddr) : curr_rec_offset - off_chain_sz; } if (offset_sum <= last_possible_left_offset) { /* the split falls within or after the chain; otherwise entire chain stays right */ assert((cse_first_off < curr_rec_offset) || (cse_first_off == last_possible_left_offset)); if (left_hand_offset && (curr_rec_offset < cse_first_off)) { /* We are inserting the new record (with the to-be-filled child block * number) AND an extra record in the left block and the TP block * chain of the block to be split starts AFTER the new record's offset * in the current block. This means the left block (cse_new) will have a * block chain starting with the newly inserted record's block pointer. */ cse_new->first_off = left_hand_offset; } else { cse_new->first_off = cse_first_off; assert(0 == cse_new->next_off); } if (level_0) /* if no *-key issue stop after, rather than at, a match */ last_possible_left_offset += off_chain_sz; if (offset_sum < last_possible_left_offset) { /* it's not an immediate hit */ for (;;) { /* follow chain upto split point */ assert(1 == curr_chain.flag); if (0 == curr_chain.next_off) break; offset_sum += curr_chain.next_off; if (offset_sum >= last_possible_left_offset) break; /* Increment curr_chain to the next element */ curr += curr_chain.next_off; READ_OFF_CHAIN(long_blk_id, &curr_chain, &v6_chain, curr); } /* end of search chain loop */ } assert(curr >= (buffaddr + cse_first_off)); if (level_0) /* restore match point to "normal" */ last_possible_left_offset -= off_chain_sz; if ((offset_sum == last_possible_left_offset) && !level_0) { /* The last record in the left side of the pre-split block is where * the search stopped. If no extra record copy was done, then this * record will end up BEFORE the inserted record in the post-split * left block. Otherwise this will be AFTER the inserted record. * * In case of copy_extra_record, the extra record will become the *-key * ---|------------v-----------------v * [blk_hdr]...[curr rec( )][new rec ( )] [extra rec (*-key)] * * In case of no extra record copy, the new record will become the *-key * ---|-------------------v * [blk_hdr]...[curr rec( )][new rec (*-key)( )] * * Take this into account during the calculations below. */ assert(cse_first_off <= last_possible_left_offset); if (left_hand_offset) { assert(!ins_chain_offset); if (!extra_record_blkid_off && (offset_sum != cse_first_off)) { /* bring curr up to the match */ curr += curr_chain.next_off; READ_OFF_CHAIN(long_blk_id, &curr_chain, &v6_chain, curr); } curr_offset = curr - buffaddr; undo_index = 0; if (curr_offset < curr_rec_offset) { /* The chain starts before the curr_rec_offset. Fix * next_off field from the last element in the chain * before this offset. */ prev_chain = curr_chain; assert(extra_record_blkid_off || (bstar_rec_size(long_blk_id) == (left_hand_offset - curr_offset))); prev_chain.next_off = left_hand_offset - curr_offset; assert((curr_offset + prev_chain.next_off) <= (new_blk_size_l - off_chain_sz)); if (dollar_tlevel != cse->t_level) { assert(dollar_tlevel > cse->t_level); assert(!cse->undo_next_off[0] && !cse->undo_offset[0]); assert(!cse->undo_next_off[1] && !cse->undo_offset[1]); cse->undo_next_off[0] = curr_chain.next_off; cse->undo_offset[0] = (block_offset)curr_offset; undo_index = 1; } WRITE_OFF_CHAIN(long_blk_id, &prev_chain, &v6_chain, curr); } if (extra_record_blkid_off) { if (offset_sum != cse_first_off) { /* bring curr up to the match */ curr += curr_chain.next_off; curr_offset += curr_chain.next_off; READ_OFF_CHAIN(long_blk_id, &curr_chain, &v6_chain, curr); } if (dollar_tlevel != cse->t_level) { assert(dollar_tlevel > cse->t_level); assert(!cse->undo_next_off[undo_index] && !cse->undo_offset[undo_index]); cse->undo_next_off[undo_index] = curr_chain.next_off; cse->undo_offset[undo_index] = (block_offset)curr_offset; } prev_chain = curr_chain; prev_chain.next_off = 0; WRITE_OFF_CHAIN(long_blk_id, &prev_chain, &v6_chain, curr); cse_new->next_off = bstar_rec_size(long_blk_id); } offset_sum += curr_chain.next_off; } else { undo_index = 0; /* the last record turns into the *-key */ if (offset_sum == cse_first_off) { /* it's all there is */ /* first_off --------------------v * [blk_hdr]...[curr rec (*-key)( )] */ assert(prev_rec_offset >= SIZEOF(blk_hdr)); cse_new->first_off = (block_offset)(prev_rec_offset + SIZEOF(rec_hdr)); } else { /* update the next_off of the previous chain record */ /* ---|--------------------v * [blk_hdr]...[prev rec( )][curr rec (*-key)( )] */ assert((buffaddr + prev_rec_offset) > curr); prev_chain = curr_chain; assert((offset_sum - prev_chain.next_off) /* check old */ == (curr - buffaddr)); /* method equivalent */ assert(((prev_rec_offset + (unsigned int)(SIZEOF(rec_hdr)) - (curr - buffaddr))) == (uint4) ((prev_rec_offset + (unsigned int)(SIZEOF(rec_hdr)) - (curr - buffaddr)))); prev_chain.next_off = (uint4)( (prev_rec_offset + (unsigned int)(SIZEOF(rec_hdr)) - (curr - buffaddr))); assert((curr - buffaddr + prev_chain.next_off) <= ((new_blk_size_l < blk_reserved_size ? new_blk_size_l : blk_reserved_size) - off_chain_sz)); if (dollar_tlevel != cse->t_level) { assert(dollar_tlevel > cse->t_level); assert(!cse->undo_next_off[0] && !cse->undo_offset[0]); assert(!cse->undo_next_off[1] && !cse->undo_offset[1]); cse->undo_next_off[0] = curr_chain.next_off; cse->undo_offset[0] = (block_offset)(curr - buffaddr); undo_index = 1; } WRITE_OFF_CHAIN(long_blk_id, &prev_chain, &v6_chain, curr); /* bring curr up to the match */ curr += curr_chain.next_off; READ_OFF_CHAIN(long_blk_id, &curr_chain, &v6_chain, curr); } offset_sum += curr_chain.next_off; if (dollar_tlevel != cse->t_level) { assert(dollar_tlevel > cse->t_level); assert(!cse->undo_next_off[undo_index] && !cse->undo_offset[undo_index]); cse->undo_next_off[undo_index] = curr_chain.next_off; cse->undo_offset[undo_index] = (block_offset)(curr - buffaddr); } curr_chain.next_off = 0; WRITE_OFF_CHAIN(long_blk_id, &curr_chain, &v6_chain, curr); } } else { /* found the split and no *-key issue: just terminate before the split */ if (offset_sum == cse_first_off) offset_sum += curr_chain.next_off; /* put it in the lead */ old_curr_chain_next_off = curr_chain.next_off; if (left_hand_offset) { /* there's a new chain rec in left */ curr_offset = curr - buffaddr; if (extra_record_blkid_off && (curr_offset == last_possible_left_offset)) { assert(level_0); /* else *-key issues */ cse_new->next_off = value.len + extra_record_blkid_off - next_rec_shrink1; } assert(!ins_chain_offset); /* put the new one at the end of the chain */ /* ---|---------------v * [blk_hdr]...[curr rec( )]...[new rec ( )] */ /* the new rec may or may not be a *-key */ assert((offset_sum - curr_chain.next_off) == curr_offset); assert(left_hand_offset > curr_offset); curr_chain.next_off = (block_offset)(left_hand_offset - curr_offset); } else curr_chain.next_off = 0; assert((curr - buffaddr + curr_chain.next_off) <= ((new_blk_size_l < blk_reserved_size ? new_blk_size_l : blk_reserved_size) - off_chain_sz)); if (dollar_tlevel != cse->t_level) { assert(dollar_tlevel > cse->t_level); assert(!cse->undo_next_off[0] && !cse->undo_offset[0]); assert(!cse->undo_next_off[1] && !cse->undo_offset[1]); cse->undo_next_off[0] = old_curr_chain_next_off; cse->undo_offset[0] = (block_offset)(curr - buffaddr); } WRITE_OFF_CHAIN(long_blk_id, &curr_chain, &v6_chain, curr); } /* end of *-key or not alternatives */ assert((left_hand_offset + (int)cse_new->next_off) <= ((new_blk_size_l < blk_reserved_size ? new_blk_size_l : blk_reserved_size) - off_chain_sz)); } /* end of buffer and cse_new adjustments */ prev_first_off = cse_first_off; if (ins_chain_offset) { /* if there is a new chain rec in the old block, put it first */ /* first_off---------v * [blk_hdr][new rec( )]... */ assert(!left_hand_offset); assert(0 == extra_record_blkid_off); assert(ins_chain_offset >= (SIZEOF(blk_hdr) + SIZEOF(rec_hdr))); cse->first_off = ins_chain_offset; assert(0 == cse->next_off); if (offset_sum > last_possible_left_offset) { /* there are existing chain records after the split */ /* first_off---------v--------------------v * [blk_hdr][new rec( )]...[existing rec ( )] */ prev_next_off = cse->next_off; assert(offset_sum > curr_rec_offset); cse->next_off = offset_sum - curr_rec_offset - next_rec_shrink1 + value.len; assert((int)(cse->next_off + ins_chain_offset) < new_blk_size_r); } } else if (offset_sum <= last_possible_left_offset) { /* the last chain record went left with the split */ cse->first_off = 0; } else { /* just adjust the anchor for the split */ /* first_off------------------v * [blk_hdr]...[existing rec ( )] */ assert(offset_sum >= (int)cse_first_off); assert(offset_sum > curr_rec_offset); assert((curr_rec_offset == ((sm_uc_ptr_t)rp - buffaddr)) || copy_extra_record && (curr_rec_offset + tmp_rsiz == ((sm_uc_ptr_t)rp - buffaddr))); cse->first_off = (block_offset)(offset_sum - ((sm_uc_ptr_t)rp - buffaddr) + rec_cmpc + SIZEOF(blk_hdr)); assert(cse->first_off >= (SIZEOF(blk_hdr) + SIZEOF(rec_hdr))); } assert((ins_chain_offset + (int)cse->next_off) <= ((new_blk_size_r < blk_reserved_size ? new_blk_size_r : blk_reserved_size) - off_chain_sz)); } /* end of of split processing */ } /* end of tp only code */ if (!dollar_tlevel) cse = NULL; else { cse_new = si->last_cw_set; assert(!cse_new->high_tlevel); gvcst_blk_build(cse_new, NULL, 0); cse_new->done = TRUE; } /* Record block split heuristic info that will be used in next block split */ if (!new_rec_goes_to_right) { chain1.flag = 1; chain1.cw_index = next_blk_index; chain1.next_off = 0; assert(SIZEOF(gv_target->last_split_blk_num[bh_level]) == SIZEOF(off_chain)); last_split_blk_num[bh_level] = *(block_id *)&chain1; } else last_split_blk_num[bh_level] = blk_num; assert(temp_key == gv_altkey); /* If new_rec_goes_to_right is TRUE (of which "split_to_right"=TRUE is a subset), then it most likely * implies that the left side of the block is almost full (i.e. adding the new record there caused it * to exceed the fill factor) therefore direct all future updates to keys in between (which lie between * the last key of the left block and the first key of the right block) to the right block. * If not, direct those updates to the left block thereby preventing it from staying at a * low capacity for a long period of time. * * The direction of future updates is implemented by controlling what key gets passed for * record addition into the parent index block. For directing all in-between updates to the * right block, pass in the last key of the left block to the parent index block. For directing * all in-between updates to the left block, back off 1 spot from the first key of the right * block and pass that to the parent index block. * * Doing this backoff accurately would imply finding the last non-zero byte in the key and taking * 1 off from it. In case the length of the right key is less than the left key, it is possible * that this backoff causes the new key to be less than even the left key (e.g. if left side has * "C2 13 93 00" as key sequence corresponding to the number 1292 and right side has "C2 14 00" * corresponding to the number 1300, taking one off the right side would give "C2 13 00" which corresponds * to the number 12 and is lesser than the left side). In this case, we would have to start adding in * FF bytes to the key as much as possible until we reached the left key length. In the above example, * we would get "C2 13 FF 00". * * In the end, because of the complexities involved in getting an accurate backoff (see above paragraph), * we instead implement a simplified backoff by examining just the first byte that differs and the * immediately following byte (if needed). If it turns out that we cannot get a backoff with just * those 2 bytes (should be rare), we then let the left key go unmodified. In such cases, we expect * not many intervening possible keys and and therefore it does not matter that much whether we pass * the left or (right-1) key to the parent. * * There are two additional cases in which we let the left key go unmodified: 1) if the backoff would * result in a key larger than max_key_size and 2) if the left key ends in "00 00" and the right key ends * in "00 01 ... ". Backing off the 01 would give a index key with "00 00" in the middle. * * temp_key already holds the key corresponding to the last record of the left block. * bs1[2] and bs1[3] hold the key corresponding to the first record of the right block. */ if (level_0) { /* Determine key for record to pass on to parent index block */ cp1 = temp_key->base; assert(KEY_DELIMITER != *temp_key->base); cp2 = (unsigned char *)bs1[2].addr; bs1_2_len = bs1[2].len; for (i = 0; (i < bs1_2_len) && (*cp2 == *cp1); ++i) { ++cp2; ++cp1; } if (i == bs1_2_len) { cp2 = (unsigned char *)bs1[3].addr; bs1_3_len = bs1[3].len; for (j = 0; (j < bs1_3_len) && (*cp2 == *cp1); ++j) { ++cp2; ++cp1; } } n = (int)((sm_long_t)*cp2 - (sm_long_t)*cp1); if (0 > n) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } else if (1 < n) { temp_key->end = cp1 - temp_key->base + 2; if (temp_key->end < temp_key->top) { *cp1++ += (!new_rec_goes_to_right ? (n - 1) : 1); *cp1++ = 0; *cp1 = 0; } else { temp_key->end = temp_key->prev; assert(temp_key->end < temp_key->top); assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } } else if (1 == n) { cp1++; start_len = cp1 - temp_key->base + 2; if (start_len < temp_key->top) { if (i == (bs1_2_len - 1)) cp2 = (unsigned char *)bs1[3].addr; else cp2++; if (((KEY_DELIMITER != *(cp1 - 1)) || (KEY_DELIMITER != *(cp1 - 2))) && ((STR_SUB_MAXVAL != *cp1) || (KEY_DELIMITER != *cp2)) && (gv_cur_region->max_key_size > start_len)) { if (!new_rec_goes_to_right) { old_ch = *cp2; new_ch = old_ch - 1; *cp1 = new_ch; if (KEY_DELIMITER != old_ch) *(cp1 - 1) = *(cp2 - 1); } else { old_ch = *cp1; new_ch = old_ch + 1; *cp1 = new_ch; if (STR_SUB_MAXVAL == old_ch) *(cp1 - 1) = *(cp2 - 1); } cp1++; if (KEY_DELIMITER == new_ch) temp_key->end--; else *cp1++ = KEY_DELIMITER; *cp1 = KEY_DELIMITER; temp_key->end = cp1 - temp_key->base; } } else { temp_key->end = temp_key->prev; assert(temp_key->end < temp_key->top); assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } } } assert(temp_key->end < temp_key->top); assert(1 <= temp_key->end); assert(KEY_DELIMITER == temp_key->base[temp_key->end]); assert(KEY_DELIMITER == temp_key->base[temp_key->end - 1]); if ((2 <= temp_key->end) && (KEY_DELIMITER == temp_key->base[temp_key->end - 2])) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } bq = bh + 1; if (HIST_TERMINATOR != bq->blk_num) { /* Not root; write blocks and continue */ if (cdb_sc_normal != (status = gvcst_search_blk(temp_key, bq))) GOTO_RETRY; if (!split_to_right) { /* It's necessary to disable the indexmod optimization for splits of index blocks. * Hence the GDS_WRITE_KILLTN use below. Refer to GTM-7353, C9B11-001813 (GTM-3984), * and C9H12-002934 (GTM-6104) for more details. */ cse = t_write(bh, (unsigned char *)bs1, ins_chain_offset, ins_chain_index, bh_level, TRUE, FALSE, level_0 ? GDS_WRITE_PLAIN : GDS_WRITE_KILLTN); assert(!dollar_tlevel || !cse->high_tlevel); if (dollar_tlevel) cse->write_type |= GDS_WRITE_BLOCK_SPLIT; else cse = NULL; /* if non-TP, NULL == cse is later a proxy for dollar_tlevel */ } /* else left-side (i.e. current) block was untouched and new contents went into right block * so no need to invoke "t_write". */ if (long_blk_id) { value.len = SIZEOF(block_id_64); value.addr = (char *)&zeroes_blkid; } else { value.len = SIZEOF(block_id_32); value.addr = (char *)&v6_zeroes_blkid; } ++bh; ins_chain_index = next_blk_index; } else { /* Create new root */ if ((bh_level + 1) == MAX_BT_DEPTH) { if (CDB_STAGNATE > t_tries) { status = cdb_sc_maxlvl; GOTO_RETRY; } RTS_ERROR_CSA_ABT(csa, VARLSTCNT(6) ERR_MAXBTLEVEL, 4, gv_target->gvname.var_name.len, gv_target->gvname.var_name.addr, REG_LEN_STR(gv_cur_region)); } ins_chain_index = t_create(blk_num, (uchar_ptr_t)bs1, ins_chain_offset, ins_chain_index, bh_level); make_it_null = FALSE; if (NULL != cse) { /* adjust block to use the buffer and offsets worked out for the old root */ assert(cse->done); assert(NULL != cse->new_buff); cse_new = si->last_cw_set; assert(!cse_new->high_tlevel); cse_new->blk_target = cse->blk_target; cse_new->first_off = cse->first_off; cse_new->next_off = cse->next_off; /* to be able to incrementally rollback, we need another copy of new_buff, * pointer copying wouldn't suffice */ cse_new->new_buff = (unsigned char *)get_new_free_element(si->new_buff_list); memcpy(cse_new->new_buff, cse->new_buff, ((blk_hdr_ptr_t)cse->new_buff)->bsiz); cse_new->old_block = NULL; make_it_null = TRUE; } /* Build the right child of the new root right now since it is possible that before commit the * root block may have been recycled in the global buffer which wouldn't cause a restart since * it has been built already (see the gvcst_blk_build below). Otherwise, we may be relying * on incorrect data in the root block when we build this right child finally in bg_update. * Note that this needs to be done only in TP since only tp_tend allows for a block with a * cse not to be in the global buffer if a new_buff already exists. */ if (dollar_tlevel) { DEBUG_ONLY(tp_get_cw(si->first_cw_set, ins_chain_index, &cse_new);) assert(cse_new == si->last_cw_set); cse_new = si->last_cw_set; assert(FALSE == cse_new->done); assert(!cse_new->high_tlevel); gvcst_blk_build(cse_new, NULL, 0); cse_new->done = TRUE; } target_key_size = temp_key->end + 1; BLK_INIT(bs_ptr, bs1); BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); curr_rec_hdr->rsiz = target_key_size + SIZEOF(rec_hdr) + blk_id_sz; SET_CMPC(curr_rec_hdr, 0); BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); BLK_ADDR(cp1, target_key_size, unsigned char); memcpy(cp1, temp_key->base, target_key_size); BLK_SEG(bs_ptr, cp1, target_key_size); if (long_blk_id) { BLK_SEG(bs_ptr, (unsigned char *)&zeroes_blkid, SIZEOF(block_id_64)); } else { BLK_SEG(bs_ptr, (unsigned char *)&v6_zeroes_blkid, SIZEOF(block_id_32)); } BLK_ADDR(next_rec_hdr, SIZEOF(rec_hdr), rec_hdr); next_rec_hdr->rsiz = bstar_rec_size(long_blk_id); SET_CMPC(next_rec_hdr, 0); BLK_SEG(bs_ptr, (sm_uc_ptr_t)next_rec_hdr, SIZEOF(rec_hdr)); if (long_blk_id) { BLK_SEG(bs_ptr, (unsigned char *)&zeroes_blkid, SIZEOF(block_id_64)); } else { BLK_SEG(bs_ptr, (unsigned char *)&v6_zeroes_blkid, SIZEOF(block_id_32)); } if (0 == BLK_FINI(bs_ptr, bs1)) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_mkblk; GOTO_RETRY; } assert(bs1[0].len <= blk_reserved_size); /* Assert that new block has space for reserved bytes */ ins_off1 = (block_offset)(SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + target_key_size); ins_off2 = (block_offset)(SIZEOF(blk_hdr) + (2 * SIZEOF(rec_hdr)) + blk_id_sz + target_key_size); assert(ins_off1 < ins_off2); /* Since a new root block is not created but two new children are created, this update to the * root block should disable the "indexmod" optimization (C9B11-001813). */ cse = t_write(bh, (unsigned char *)bs1, ins_off1, next_blk_index, bh_level + 1, TRUE, FALSE, GDS_WRITE_KILLTN); if (make_it_null) cse->new_buff = NULL; assert(!dollar_tlevel || !cse->high_tlevel); if (!dollar_tlevel) { /* create a sibling cw-set-element to store ins_off2/ins_chain_index */ t_write_root(ins_off2, ins_chain_index); } else { cse->write_type |= GDS_WRITE_BLOCK_SPLIT; assert(NULL == cse->new_buff); cse->first_off = 0; cse->next_off = ins_off2 - ins_off1; /* the following is the only place where the buffer is not completely built by * gvcst_blk_build. this means that the block chain seen by gvcst_blk_build will * have a bad value (that is fixed below) at the end of the list. therefore the * block chain integrity checking code in gvcst_blk_build will error out normally * in this case. signal that routine to skip checking just this tail element. */ DEBUG_ONLY(skip_block_chain_tail_check = TRUE;) gvcst_blk_build(cse, NULL, 0); DEBUG_ONLY(skip_block_chain_tail_check = FALSE;) curr_chain.flag = 1; curr_chain.cw_index = ins_chain_index; curr_chain.next_off = 0; curr = cse->new_buff + ins_off2; WRITE_OFF_CHAIN(long_blk_id, &curr_chain, &v6_chain, curr); cse->done = TRUE; gv_target->clue.end = 0; } succeeded = TRUE; } } } assert(succeeded); horiz_growth = FALSE; assert((csa->dir_tree == gv_target) || tp_root); RESET_GV_TARGET_LCL_AND_CLR_GBL(save_targ, DO_GVT_GVKEY_CHECK); /* The only case where gv_target is still csa->dir_tree after the above RESET macro is if op_gvput was invoked * with gv_target being set to cs_addrs->dir_tree. In that case gbl_target_was_set would have been set to TRUE. Assert. */ assert((csa->dir_tree != gv_target) || gbl_target_was_set); /* Format the journal records only once for non-TP (irrespective of number of restarts). * We remember this through the variable "jnl_format_done". If TRUE, we do not redo the jnl_format. * The only exception is if we are in $INCREMENT in which case we need to reformat since the * current value (and hence the post-increment value) of the key might be different in different tries. * In this case, the restart code checks and resets "jnl_format_done" to FALSE. */ if (!dollar_tlevel) { nodeflags = 0; if (skip_dbtriggers) nodeflags |= JS_SKIP_TRIGGERS_MASK; if (duplicate_set) nodeflags |= JS_IS_DUPLICATE; assert(!jnl_format_done || !is_dollar_incr && (JNL_SET == non_tp_jfb_ptr->ja.operation)); if (need_extra_block_split) inctn_opcode = inctn_gvcstput_extra_blk_split; else if (JNL_WRITE_LOGICAL_RECS(csa) && !jnl_format_done) { jfb = jnl_format(JNL_SET, gv_currkey, (!is_dollar_incr ? val_forjnl : post_incr_mval), nodeflags); assert(NULL != jfb); jnl_format_done = TRUE; } succeeded = ((trans_num)0 != t_end(&gv_target->hist, dir_hist, TN_NOT_SPECIFIED)); inctn_opcode = inctn_invalid_op; if (succeeded) { if (NULL != dir_hist) { /* The Global Variable Tree was created in this transaction. So clear its gv_target to be safe. * The directory tree though will have a non-zero value and that can stay as it is since it * was validated in this transaction and was found good enough for us to commit. */ assert(dir_tree != gv_target); gv_target->clue.end = 0; } } else { /* "t_retry" would have already been invoked by "t_end". * So instead of going to "retry:", do only whatever steps from there are necessary here. */ RESTORE_ZERO_GVT_ROOT_ON_RETRY(lcl_root, gv_target, dir_hist, dir_tree); /*if (is_dollar_incr)*/ jnl_format_done = FALSE; /* need to reformat jnl records for $INCR even in case of non-TP */ GTMTRIG_DBG_ONLY(dbg_trace_array[dbg_num_iters].retry_line = __LINE__); goto tn_restart; } } else { status = tp_hist(dir_hist); if (NULL != dir_hist) { /* Note that although "tp_hist" processes the "dir_hist" history, it only adds "gv_target" to gvt_tp_list. * But csa->dir_tree might have had clue, blk-split related info etc. modified as part of this * gvcst_put invocation that might also need cleanup (just like any other gv_target) so add * csa->dir_tree to gvt_tp_list (if not already done). Therefore treat this as if tp_hist is doing * the ADD_TO_GVT_TP_LIST call for dir_tree. */ assert(dir_tree == csa->dir_tree); ADD_TO_GVT_TP_LIST(dir_tree, RESET_FIRST_TP_SRCH_STATUS_FALSE); /* note: above macro updates read_local_tn if necessary */ } if (cdb_sc_normal != status) GOTO_RETRY; jnl_format_done = !needfmtjnl; } if (succeeded) { if (0 == tp_root) { /* Fill in gv_target->root with newly created root block value. * Previously, root remained at 0 at the end of the transaction and it was left to the * NEXT transaction to do a gvcst_root_search and determine the new root block. * This was fine until recently when op_gvrectarg was reworked to NOT do a gvcst_root_search * (to avoid potential TP restarts while unwinding the M stack). This meant that gv_target->root * needed to be kept uptodate as otherwise it was possible for gv_target->root to be stale * after a op_gvrectarg causing incorrect behavior of following M code (see v52000/C9B10001765 * subtest for example where $order(^gvn,$$extrinsic) is done and extrinsic CREATES <^gvn>). */ GTMTRIG_ONLY(assert(!ztval_gvcst_put_redo);) assert(0 == gv_target->root); if (!dollar_tlevel) { tp_root = cw_set[root_blk_cw_index].blk; assert(gds_t_acquired == cw_set[root_blk_cw_index].old_mode); assert(gds_t_committed == cw_set[root_blk_cw_index].mode); assert(!IS_BITMAP_BLK(tp_root)); } else { chain1.flag = 1; chain1.cw_index = root_blk_cw_index; chain1.next_off = 0; /* does not matter what value we set this field to */ assert(SIZEOF(tp_root) == SIZEOF(chain1)); tp_root = *(block_id *)&chain1; } gv_target->root = tp_root; } if (need_extra_block_split) { /* The logical update required an extra block split operation first (which succeeded) so * get back to doing the logical update before doing any trigger invocations etc. */ GTMTRIG_ONLY(skip_hasht_read = (dollar_tlevel) ? TRUE : skip_hasht_read;) goto fresh_tn_start; } for (bh_level = 0; bh_level < split_depth; bh_level++) { blk_num = last_split_blk_num[bh_level]; assert(0 != blk_num); split_targ->last_split_blk_num[bh_level] = blk_num; assert((NEWREC_DIR_FORCED == last_split_direction[bh_level]) || (NEWREC_DIR_LEFT == last_split_direction[bh_level]) || (NEWREC_DIR_RIGHT == last_split_direction[bh_level])); split_targ->last_split_direction[bh_level] = last_split_direction[bh_level]; /* Fix blk_num if it was created in this transaction. In case of non-TP, we have the real block number * corresponding to the created block. In case of TP, we can know that only at tp_clean_up time so defer. */ chain1 = *(off_chain *)&blk_num; if (chain1.flag) { if (!dollar_tlevel) { assert(chain1.cw_index < ARRAYSIZE(cw_set)); split_targ->last_split_blk_num[bh_level] = cw_set[chain1.cw_index].blk; } else split_targ->split_cleanup_needed = TRUE;/* phantom blk# will be fixed at tp_clean_up time */ } } if (dollar_tlevel) { nodeflags = 0; if (skip_dbtriggers) nodeflags |= JS_SKIP_TRIGGERS_MASK; if (duplicate_set) nodeflags |= JS_IS_DUPLICATE; ja_val = (!is_dollar_incr ? val_forjnl : post_incr_mval); write_logical_jnlrecs = JNL_WRITE_LOGICAL_RECS(csa); # ifdef GTM_TRIGGER if (!skip_dbtriggers && parms->enable_trigger_read_and_fire) { /* Since we are about to invoke the trigger, we better have gv_target->gvt_trigger and * the local variable gvt_trigger in sync. The only exception is when we are here because * of a $ztvalue update and redoing the gvcst_put. In this case, it's possible that * the trigger code that was previously executed deleted the trigger and did an update * on the global which would have set gv_target->gvt_trigger to NULL. Assert accordingly. */ assert(ztval_gvcst_put_redo || (gvt_trigger == gv_target->gvt_trigger)); if ((NULL != gvt_trigger) && !ztval_gvcst_put_redo) { assert(dollar_tlevel); /* Format ZTWORM and SET journal records. * "ztworm_jfb", "jfb" and "jnl_format_done" are set by the below macro. */ JNL_FORMAT_ZTWORM_IF_NEEDED(csa, write_logical_jnlrecs, JNL_SET, gv_currkey, ja_val, ztworm_jfb, jfb, jnl_format_done); /* Initialize trigger parms that dont depend on the context of the matching trigger */ trigparms.ztoldval_new = key_exists ? ztold_mval : (mval *)&literal_null; PUSH_MV_STENT(MVST_MVAL); /* protect $ztval from stp_gcol */ ztval_mval = &mv_chain->mv_st_cont.mvs_mval; if (!is_dollar_incr) *ztval_mval = *val_forjnl; else { *ztval_mval = *post_incr_mval; /* Since this is pointing to malloced buffer, we need to repoint it to stringpool * to avoid a nested trigger call (that does a $INCR) from overwriting this buffer. * This way buffers corresponding to $ztvals of nested triggers can coexist. */ s2pool(&ztval_mval->str); } trigparms.ztvalue_new = ztval_mval; trigparms.ztdata_new = key_exists ? &literal_one : &literal_zero; gvtr_parms.gvtr_cmd = GVTR_CMDTYPE_SET; gvtr_parms.gvt_trigger = gvt_trigger; /* Now that we have filled in minimal information, let "gvtr_match_n_invoke" do the rest */ gtm_trig_status = gvtr_match_n_invoke(&trigparms, &gvtr_parms); INCR_GVSTATS_COUNTER(csa, csa->nl, n_set_trigger_fired, gvtr_parms.num_triggers_invoked); assert((0 == gtm_trig_status) || (ERR_TPRETRY == gtm_trig_status)); if (ERR_TPRETRY == gtm_trig_status) { /* A restart has been signaled that we need to handle or complete the handling of. * This restart could have occurred reading the trigger in which case no * tp_restart() has yet been done or it could have occurred in trigger code in * which case we need to finish the incomplete tp_restart. In both cases this * must be an implicitly TP wrapped transaction. Our action is to complete the * necessary tp_restart() logic (t_retry is already completed so should be skipped) * and then re-do the gvcst_put logic. */ assert(lcl_implicit_tstart || lcl_span_status); assert(CDB_STAGNATE >= t_tries); status = cdb_sc_normal; /* signal "retry:" to avoid t_retry call */ GOTO_RETRY; } REMOVE_ZTWORM_JFB_IF_NEEDED(ztworm_jfb, jfb, si); if (trigparms.ztvalue_changed) { /* At least one of the invoked triggers changed $ztval. * Redo the gvcst_put with $ztval as the right side of the SET. * Also make sure gtm_trigger calls are NOT done this time around. */ assert(0 < gvtr_parms.num_triggers_invoked); val = trigparms.ztvalue_new; val_forjnl = trigparms.ztvalue_new; MV_FORCE_STR(val); /* in case the updated value happens to be a numeric quantity */ fits = RECORD_FITS_IN_A_BLOCK(val, gv_currkey, blk_size, blk_reserved_bytes); if (!fits) { /* If val is now too big to fit in a block, we need to back out into * gvcst_put and try again with spanning nodes. This means we should * OP_TROLLBACK if lcl_implicit_tstart. */ if (lcl_implicit_tstart) { POP_MVALS_FROM_M_STACK_IF_NEEDED(ztold_mval, save_msp, save_mv_chain); OP_TROLLBACK(-1); assert(!lcl_span_status); parms->span_status = TRUE; } else { parms->ztval_gvcst_put_redo = TRUE; parms->ztval_mval = val; } RESET_GV_TARGET_LCL_AND_CLR_GBL(save_targ, DO_GVT_GVKEY_CHECK); return; } ztval_gvcst_put_redo = TRUE; skip_hasht_read = TRUE; /* In case, the current gvcst_put invocation was for $INCR, reset the corresponding * global variable that indicates a $INCR is in progress since the redo of the * gvcst_put is a SET command (no longer $INCR). */ is_dollar_incr = FALSE; /* Dont pop the mvals as we want ztval_mval (which points to the mval containing * "val" for the redo iteration) protected-from-stp_gcol/accessible until the * redo is complete. */ goto fresh_tn_start; } } /* We don't want to pop mvals yet if we still need to set chunks of ztval */ POP_MVALS_FROM_M_STACK_IF_REALLY_NEEDED(lcl_span_status, ztold_mval, save_msp, save_mv_chain); /* pop any stacked mvals before op_tcommit as it does its own popping */ } # endif if (write_logical_jnlrecs && !jnl_format_done) { assert(dollar_tlevel); # ifdef GTM_TRIGGER /* Do not replicate implicit update or $ztval redo update */ assert(tstart_trigger_depth <= gtm_trigger_depth); if ((gtm_trigger_depth > tstart_trigger_depth) || ztval_gvcst_put_redo) { /* Ensure that JS_SKIP_TRIGGERS_MASK and JS_NOT_REPLICATED_MASK are mutually exclusive. */ assert(!(nodeflags & JS_SKIP_TRIGGERS_MASK)); nodeflags |= JS_NOT_REPLICATED_MASK; } # endif jfb = jnl_format(JNL_SET, gv_currkey, ja_val, nodeflags); assert(NULL != jfb); jnl_format_done = TRUE; } # ifdef GTM_TRIGGER /* Go ahead with commit of any implicit TP wrapped transaction */ if (lcl_implicit_tstart) { GVTR_OP_TCOMMIT(status); if (cdb_sc_normal != status) GOTO_RETRY; } # endif } assert(!JNL_WRITE_LOGICAL_RECS(csa) || jnl_format_done); /* Now that the SET/$INCR is finally complete, increment the corresponding GVSTAT counter */ if (!lcl_span_status) INCR_GVSTATS_COUNTER(csa, cnl, n_set, 1); DBG_CHECK_VAL_AT_FUN_EXIT; assert(lcl_dollar_tlevel == dollar_tlevel); return; } retry: /* Note that it is possible cs_addrs is not equal to csa at this point in case we restarted due to trigger * invocations and in case those triggers referenced globals in different regions. But this should be fixed * by a call to t_retry/tp_restart below (it does a TP_CHANGE_REG(tp_pointer->gd_reg)). */ # ifdef GTM_TRIGGER if (lcl_implicit_tstart) { assert(!skip_dbtriggers); assert(!skip_INVOKE_RESTART); assert((cdb_sc_normal != status) || (ERR_TPRETRY == gtm_trig_status)); if (cdb_sc_normal != status) skip_INVOKE_RESTART = TRUE; /* causes t_retry to invoke only tp_restart without any rts_error */ /* else: t_retry has already been done by gtm_trigger so no need to do it again for this try */ /* If an implicitly TP wrapped transaction is restarting, restore things to what they were * at entry into gvcst_put. Note that we could have done multiple iterations of gvcst_put for * extra_block_split/retry/ztval_gvcst_put_redo. */ ztval_gvcst_put_redo = FALSE; skip_hasht_read = FALSE; val = lcl_val; val_forjnl = lcl_val_forjnl; /* $increment related fields need to be restored */ is_dollar_incr = lcl_is_dollar_incr; post_incr_mval = lcl_post_incr_mval; increment_delta_mval = lcl_increment_delta_mval; } # endif assert((cdb_sc_normal != status) GTMTRIG_ONLY(|| lcl_implicit_tstart || lcl_span_status)); if (cdb_sc_normal != status) { /* Need to restart. If directory tree was used in this transaction, nullify its clue as well (not normally * done by t_retry). The RESTORE_ZERO_GVT_ROOT_ON_RETRY macro call below takes care of that for us. */ RESET_GV_TARGET_LCL_AND_CLR_GBL(save_targ, dollar_tlevel ? DO_GVT_GVKEY_CHECK_RESTART : DO_GVT_GVKEY_CHECK); RESTORE_ZERO_GVT_ROOT_ON_RETRY(lcl_root, gv_target, dir_hist, dir_tree); GTMTRIG_ONLY(POP_MVALS_FROM_M_STACK_IF_REALLY_NEEDED(lcl_span_status, ztold_mval, save_msp, save_mv_chain)); t_retry(status); GTMTRIG_ONLY(skip_INVOKE_RESTART = FALSE); } else { /* else: t_retry has already been done so no need to do that again but need to still invoke tp_restart * to complete pending "tprestart_state" related work. * SKIP_GVT_GVKEY_CHECK allows us to skip DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC if a trigger invocation * restarted during GVCST_ROOT_SEARCH, in which case gv_currkey will correspond to ^#t. * The subsequent tp_restart restores gv_currkey/gv_target to an in-sync state. */ RESET_GV_TARGET_LCL_AND_CLR_GBL(save_targ, SKIP_GVT_GVKEY_CHECK); # ifdef GTM_TRIGGER assert(ERR_TPRETRY == gtm_trig_status); TRIGGER_BASE_FRAME_UNWIND_IF_NOMANSLAND; POP_MVALS_FROM_M_STACK_IF_REALLY_NEEDED(lcl_span_status, ztold_mval, save_msp, save_mv_chain) if (!lcl_implicit_tstart) { /* We started an implicit transaction for spanning nodes in gvcst_put. Invoke restart to return. */ assert(lcl_span_status && !skip_INVOKE_RESTART && (&gvcst_put_ch == ctxt->ch)); INVOKE_RESTART; } # endif rc = tp_restart(1, !TP_RESTART_HANDLES_ERRORS); assert(0 == rc GTMTRIG_ONLY(&& TPRESTART_STATE_NORMAL == tprestart_state)); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); /* finishs the check skipped above */ RESTORE_ZERO_GVT_ROOT_ON_RETRY(lcl_root, gv_target, dir_hist, dir_tree); } # ifdef GTM_TRIGGER assert(0 < t_tries); assert((cdb_sc_normal != status) || (ERR_TPRETRY == gtm_trig_status)); if (cdb_sc_normal == status) { DEBUG_ONLY(save_cdb_status = status); status = LAST_RESTART_CODE; } assert((cdb_sc_onln_rlbk2 != status) || TREF(dollar_zonlnrlbk)); assert(((cdb_sc_onln_rlbk1 != status) && (cdb_sc_onln_rlbk2 != status)) || !gv_target->root); if ((cdb_sc_onln_rlbk2 == status) && lcl_implicit_tstart) { /* Database was taken back to a different logical state and we are an implicit TP transaction. * Issue DBROLLEDBACK error that the application programmer can catch and do the necessary stuff. */ assert(gtm_trigger_depth == tstart_trigger_depth); RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_DBROLLEDBACK); } /* Note: In case of cdb_sc_onln_rlbk1, the restart logic will take care of doing the root search */ # endif GTMTRIG_ONLY(assert(!skip_INVOKE_RESTART);) /* if set to TRUE a few statements above, should have been reset by t_retry */ /* At this point, we can be in TP only if we implicitly did a tstart in gvcst_put (as part of a trigger update). * Assert that. Since the t_retry/tp_restart would have reset si->update_trans, we need to set it again. * So reinvoke the T_BEGIN call only in case of TP. For non-TP, update_trans is unaffected by t_retry. */ assert(!dollar_tlevel GTMTRIG_ONLY(|| lcl_implicit_tstart)); if (dollar_tlevel) { jnl_format_done = !needfmtjnl; /* need to reformat jnl records unconditionally in case of TP */ tp_set_sgm(); /* set sgm_info_ptr & first_sgm_info for TP start */ T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_GVPUTFAIL); /* set update_trans and t_err for wrapped TP */ if (cdb_sc_gvtrootnonzero == status) { /* This is an implicit TP and gv_target->root became non-zero midway in previous try but was * reset to 0 before the "t_retry" call. Read it afresh during start of next try. * Setting "want_root_search" to TRUE achieves that. */ want_root_search = TRUE; } } else if (is_dollar_incr) jnl_format_done = !needfmtjnl; /* need to reformat jnl records for $INCR even in case of non-TP */ assert(dollar_tlevel || update_trans); goto tn_restart; } fis-gtm-V7.0-005/sr_port/gvcst_query.c0000755000032200000250000001433714342376331016575 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "cdb_sc.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ #ifdef UNIX /* needed for frame_pointer in GVCST_ROOT_SEARCH_AND_PREP macro */ # include "repl_msg.h" # include "gtmsource.h" # include "rtnhdr.h" # include "stack_frame.h" # include "wbox_test_init.h" #endif #include "t_end.h" #include "t_retry.h" #include "t_begin.h" #include "gvcst_expand_key.h" #include "gvcst_protos.h" /* for gvcst_rtsib,gvcst_search,gvcst_search_blk,gvcst_query prototype */ /* needed for spanning nodes */ #include "op.h" #include "op_tcommit.h" #include "error.h" #include "tp_frame.h" #include "tp_restart.h" #include "gtmimagename.h" LITREF mval literal_batch; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF gd_region *gv_cur_region; GBLREF gv_namehead *gv_target; GBLREF gv_key *gv_currkey, *gv_altkey; GBLREF uint4 dollar_tlevel; GBLREF unsigned int t_tries; error_def(ERR_GVQUERYFAIL); DEFINE_NSB_CONDITION_HANDLER(gvcst_query_ch) boolean_t gvcst_query(void) { /* Similar to gvcst_order. In each case we skip over hidden subscripts as needed. * * 1 2 3 NULL <--- order/zprev... * 1 2 3 NULL NULL * 1 2 3 NULL NULL NULL <--- query from here... * 1 2 3 NULL NULL NULL hidden * 1 2 3 NULL NULL hidden * 1 2 3 NULL hidden * 1 2 3 hidden <--- ... skip this guy and go to bottom/top, respectively * 1 2 3 7 <--- ... needs to end up here */ boolean_t found, is_hidden, sn_tpwrapped; boolean_t est_first_pass; gv_key_buf save_currkey; int i; int save_dollar_tlevel; DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); found = gvcst_query2(); INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_query, 1); WBTEST_ONLY(WBTEST_QUERY_HANG, LONG_SLEEP(2); ); # ifdef UNIX assert(save_dollar_tlevel == dollar_tlevel); CHECK_HIDDEN_SUBSCRIPT_AND_RETURN(found, gv_altkey, is_hidden); IF_SN_DISALLOWED_AND_NO_SPAN_IN_DB(return found); assert(found && is_hidden); SAVE_GV_CURRKEY(save_currkey); if (!dollar_tlevel) { sn_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0); ESTABLISH_NORET(gvcst_query_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); } else sn_tpwrapped = FALSE; for (i = 0; i <= MAX_GVSUBSCRIPTS; i++) { WBTEST_ONLY(WBTEST_QUERY_HANG, LONG_SLEEP(2); ); found = gvcst_query2(); if (found) { CHECK_HIDDEN_SUBSCRIPT(gv_altkey, is_hidden); if (!is_hidden) break; } else break; assert(found && is_hidden); /* Replace last subscript to be the highest possible hidden subscript so another * gvcst_query2 will give us the next non-hidden subscript. */ REPLACE_HIDDEN_SUB_TO_HIGHEST(gv_altkey, gv_currkey); /* uses gv_altkey to modify gv_currkey */ } if (sn_tpwrapped) { op_tcommit(); REVERT; /* remove our condition handler */ } RESTORE_GV_CURRKEY(save_currkey); assert(save_dollar_tlevel == dollar_tlevel); # endif return found; } boolean_t gvcst_query2(void) { boolean_t found, two_histories; enum cdb_sc status; blk_hdr_ptr_t bp; rec_hdr_ptr_t rp; unsigned char *c1, *c2; srch_blk_status *bh; srch_hist *rt_history; T_BEGIN_READ_NONTP_OR_TP(ERR_GVQUERYFAIL); assert(t_tries < CDB_STAGNATE || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ for (;;) { two_histories = FALSE; # if defined(DEBUG) && defined(UNIX) if (gtm_white_box_test_case_enabled && (WBTEST_ANTIFREEZE_GVQUERYFAIL == gtm_white_box_test_case_number)) { t_retry(cdb_sc_blknumerr); continue; } # endif if (cdb_sc_normal == (status = gvcst_search(gv_currkey, 0))) { found = TRUE; bh = &gv_target->hist.h[0]; rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); bp = (blk_hdr_ptr_t)bh->buffaddr; if (rp >= (rec_hdr_ptr_t)CST_TOB(bp)) { two_histories = TRUE; rt_history = gv_target->alt_hist; status = gvcst_rtsib(rt_history, 0); if (cdb_sc_endtree == status) /* end of tree */ { found = FALSE; two_histories = FALSE; /* second history not valid */ } else if (cdb_sc_normal != status) { t_retry(status); continue; } else { bh = &rt_history->h[0]; if (cdb_sc_normal != (status = gvcst_search_blk(gv_currkey, bh))) { t_retry(status); continue; } rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); bp = (blk_hdr_ptr_t)bh->buffaddr; } } if (found) { /* !found indicates that the end of tree has been reached (see call to * gvcst_rtsib). If there is no more tree, don't bother doing expansion. */ status = gvcst_expand_curr_key(bh, gv_currkey, gv_altkey); if (cdb_sc_normal != status) { t_retry(status); continue; } } if (!dollar_tlevel) { if ((trans_num)0 == t_end(&gv_target->hist, !two_histories ? NULL : rt_history, TN_NOT_SPECIFIED)) continue; } else { status = tp_hist(!two_histories ? NULL : rt_history); if (cdb_sc_normal != status) { t_retry(status); continue; } } assert(cs_data == cs_addrs->hdr); if (found) { c1 = &gv_altkey->base[0]; c2 = &gv_currkey->base[0]; for ( ; *c2; ) { if (*c2++ != *c1++) break; } if (!*c2 && !*c1) return TRUE; } return FALSE; } t_retry(status); } } fis-gtm-V7.0-005/sr_port/gvcst_queryget.c0000755000032200000250000001463514342376331017276 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "stringpool.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "cdb_sc.h" #include "copy.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ #ifdef UNIX /* needed for frame_pointer in GVCST_ROOT_SEARCH_AND_PREP macro */ # include "repl_msg.h" # include "gtmsource.h" # include "rtnhdr.h" # include "stack_frame.h" # include "wbox_test_init.h" #endif #include "gvcst_protos.h" /* for gvcst_queryget,gvcst_search,gvcst_rtsib,gvcst_search_blk prototype */ #include "t_begin.h" #include "gvcst_expand_key.h" #include "t_retry.h" #include "t_end.h" /* needed for spanning nodes */ #include "op.h" #include "op_tcommit.h" #include "error.h" #include "tp_frame.h" #include "tp_restart.h" #include "gtmimagename.h" LITREF mval literal_batch; LITREF mstr nsb_dummy; GBLREF gd_region *gv_cur_region; GBLREF gv_namehead *gv_target; GBLREF gv_key *gv_currkey, *gv_altkey; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF spdesc stringpool; GBLREF uint4 dollar_tlevel; GBLREF unsigned int t_tries; error_def(ERR_GVQUERYGETFAIL); DEFINE_NSB_CONDITION_HANDLER(gvcst_queryget_ch) boolean_t gvcst_queryget(mval *val) { bool found, is_hidden, is_dummy = FALSE, sn_tpwrapped; boolean_t est_first_pass; gv_key_buf save_currkey; int save_dollar_tlevel; DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); found = gvcst_queryget2(val, NULL); WBTEST_ONLY(WBTEST_QUERY_HANG, LONG_SLEEP(2); ); # ifdef UNIX assert(save_dollar_tlevel == dollar_tlevel); CHECK_HIDDEN_SUBSCRIPT(gv_altkey, is_hidden); if (found) INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_get, (gtm_uint64_t) 1); if (found && IS_SN_DUMMY(val->str.len, val->str.addr)) is_dummy = TRUE; if (!found || (!is_dummy && !is_hidden)) return found; IF_SN_DISALLOWED_AND_NO_SPAN_IN_DB(return found); SAVE_GV_CURRKEY(save_currkey); if (!dollar_tlevel) { sn_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0); ESTABLISH_NORET(gvcst_queryget_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); } else sn_tpwrapped = FALSE; found = gvcst_query(); WBTEST_ONLY(WBTEST_QUERY_HANG, LONG_SLEEP(2); ); if (found) { COPY_KEY(gv_currkey, gv_altkey); /* set gv_currkey to gv_altkey */ found = gvcst_get(val); WBTEST_ONLY(WBTEST_QUERY_HANG, LONG_SLEEP(2); ); } if (sn_tpwrapped) { op_tcommit(); REVERT; /* remove our condition handler */ } RESTORE_GV_CURRKEY(save_currkey); assert(save_dollar_tlevel == dollar_tlevel); # endif return found; } boolean_t gvcst_queryget2(mval *val, unsigned char *sn_ptr) { blk_hdr_ptr_t bp; boolean_t found, two_histories; enum cdb_sc status; int rsiz, key_size, data_len; rec_hdr_ptr_t rp; srch_blk_status *bh; srch_hist *rt_history; unsigned short temp_ushort; int tmp_cmpc; DEBUG_ONLY(unsigned char *save_strp = NULL); T_BEGIN_READ_NONTP_OR_TP(ERR_GVQUERYGETFAIL); assert((CDB_STAGNATE > t_tries) || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ for (;;) { two_histories = FALSE; # if defined(DEBUG) && defined(UNIX) if (gtm_white_box_test_case_enabled && (WBTEST_ANTIFREEZE_GVQUERYGETFAIL == gtm_white_box_test_case_number)) { status = cdb_sc_blknumerr; t_retry(status); continue; } # endif if (cdb_sc_normal == (status = gvcst_search(gv_currkey, 0))) { found = TRUE; bh = &gv_target->hist.h[0]; rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); bp = (blk_hdr_ptr_t)bh->buffaddr; if (rp >= (rec_hdr_ptr_t)CST_TOB(bp)) { two_histories = TRUE; rt_history = gv_target->alt_hist; status = gvcst_rtsib(rt_history, 0); if (cdb_sc_endtree == status) /* end of tree */ { found = FALSE; two_histories = FALSE; /* second history not valid */ } else if (cdb_sc_normal != status) { t_retry(status); continue; } else { bh = &rt_history->h[0]; if (cdb_sc_normal != (status = gvcst_search_blk(gv_currkey, bh))) { t_retry(status); continue; } rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); bp = (blk_hdr_ptr_t)bh->buffaddr; } } /* !found indicates that the end of tree has been reached (see call to * gvcst_rtsib). If there is no more tree, don't bother doing expansion. */ if (found) { status = gvcst_expand_curr_key(bh, gv_currkey, gv_altkey); if (cdb_sc_normal != status) { t_retry(status); continue; } key_size = gv_altkey->end + 1; GET_RSIZ(rsiz, rp); data_len = rsiz + EVAL_CMPC(rp) - SIZEOF(rec_hdr) - key_size; if (data_len < 0 || (sm_uc_ptr_t)rp + rsiz > (sm_uc_ptr_t)bp + ((blk_hdr_ptr_t)bp)->bsiz) { assert(CDB_STAGNATE > t_tries); t_retry(cdb_sc_rmisalign); continue; } ENSURE_STP_FREE_SPACE(data_len); # ifdef DEBUG if (!save_strp) save_strp = stringpool.free; # endif memcpy(stringpool.free, (sm_uc_ptr_t)rp + rsiz - data_len, data_len); /* Assumption: t_end/tp_hist will never cause stp_gcol() call BYPASSOK */ } if (!dollar_tlevel) { if ((trans_num)0 == t_end(&gv_target->hist, !two_histories ? NULL : rt_history, TN_NOT_SPECIFIED)) continue; } else { status = tp_hist(!two_histories ? NULL : rt_history); if (cdb_sc_normal != status) { t_retry(status); continue; } } if (found) { DEBUG_ONLY(assert(save_strp == stringpool.free)); /* Process val first. Already copied to string pool. */ val->mvtype = MV_STR; val->str.addr = (char *)stringpool.free; val->str.len = data_len; stringpool.free += data_len; } return found; } t_retry(status); } } fis-gtm-V7.0-005/sr_port/gvcst_reservedDB_funcs.c0000644000032200000250000012165014342376331020645 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2022 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_stdio.h" #include "gtm_string.h" #include "gtm_ipc.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "dpgbldir.h" #include "filestruct.h" #include "gvcst_protos.h" #include "mvalconv.h" #include "op.h" #include "gdsblk.h" #include "gtm_reservedDB.h" #include "gvn2gds.h" #include "mu_cre_file.h" #include "gds_rundown.h" #include "min_max.h" #include "gtm_caseconv.h" #include "hashtab_mname.h" #include "send_msg.h" #include "error.h" #include "gtm_logicals.h" #include "trans_log_name.h" #include "iosp.h" #include "parse_file.h" #include "getzposition.h" #include "util.h" #include "repl_msg.h" /* for gtmsource.h */ #include "gtmsource.h" /* for jnlpool_addrs_ptr_t */ #include "gtm_signal.h" /* for SIGPROCMASK */ LITREF mval literal_statsDB_gblname; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data *cs_data; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF uint4 process_id; GBLREF uint4 dollar_tlevel; GBLREF gd_region *db_init_region, *gv_cur_region; GBLREF gv_key *gv_altkey, *gv_currkey; GBLREF gv_namehead *reset_gv_target; GBLREF boolean_t need_core; GBLREF boolean_t created_core; GBLREF boolean_t dont_want_core; GBLREF gd_addr *gd_header; GBLREF mstr extnam_str; GBLREF int4 pre_drvlongjmp_error_condition; DEBUG_ONLY(GBLREF boolean_t ok_to_UNWIND_in_exit_handling;) STATICDEF intrpt_state_t gvcst_statsDB_open_ch_intrpt_ok_state; STATICDEF gd_region *save_statsDBreg; /* For use in condition handler */ /* Macro to restore the values that were saved at the start of the routine */ #define RESTORE_SAVED_VALUES \ MBSTART { \ TP_CHANGE_REG(save_cur_region); \ jnlpool = save_jnlpool; \ gv_target = save_gv_target; \ reset_gv_target = save_reset_gv_target; \ RESTORE_GV_ALTKEY(save_altkey); \ RESTORE_GV_CURRKEY(save_currkey); \ TREF(gd_targ_gvnh_reg) = save_gd_targ_gvnh_reg; \ TREF(gd_targ_map) = save_gd_targ_map; \ TREF(gd_targ_addr) = save_gd_targ_addr; \ TREF(gv_last_subsc_null) = save_gv_last_subsc_null; \ TREF(gv_some_subsc_null) = save_gv_some_subsc_null; \ gd_header = save_gd_header; \ } MBEND error_def(ERR_DBPRIVERR); error_def(ERR_DRVLONGJMP); /* Generic internal only error used to drive longjump() in a queued condition handler */ error_def(ERR_RNDWNSTATSDBFAIL); error_def(ERR_STATSDBERR); /* This routine is a wrapper for mu_cre_file() when called from GTM. The issue is mu_cre_file() was written to run * largely stand-alone and then quit. So it uses stack vars for all the needed database structures and initializes only * what it needs leaving those structures largely unusable for functions that come later. That's fine when it is called * from mupip_create() but not good when called to create an auto-created DB from within mumps or other executables. * What we do here is buffer the current region (pointed to by the parameter region) and the dyn.addr segment it points * with stack variables, then call mu_cre_file() before throwing it all away when we return. * * Parameter(s): * * reg - gd_region * of region to be created * * Return value: * File creation return code */ unsigned char gvcst_cre_autoDB(gd_region *reg) { gd_region *save_cur_region; gd_region cur_region; gd_segment cur_segment; jnlpool_addrs_ptr_t save_jnlpool; unsigned char cstatus; assert(RDBF_AUTODB & reg->reservedDBFlags); save_cur_region = gv_cur_region; save_jnlpool = jnlpool; memcpy((char *)&cur_region, reg, SIZEOF(gd_region)); memcpy((char *)&cur_segment, reg->dyn.addr, SIZEOF(gd_segment)); gv_cur_region = &cur_region; gv_cur_region->dyn.addr = &cur_segment; cstatus = mu_cre_file(); TP_CHANGE_REG(save_cur_region); jnlpool = save_jnlpool; return cstatus; } /* Initialize a statsDB database. This includes the following steps: * 1. Locate and open the statsDB related to the region parameter passed in. * 2. Write the initial gvstats_rec_t record for the input region and the current process. The writing of * the stats record involves a bit of work: * a. All statsDB records are written with two things in mind: * i. Records in memory must be written such that the gvstats_rec_t part of the record is 8 byte aligned. * ii. Records must be of sufficient size in the block to prevent another record from being added to the * block that might cause the earlier record to move. Records must NOT move because they are being * updated directly (outside of database APIs). * b. So need to compute two types of padding that is written before the gvstats_rec_t structure. The M user * program accesses the gvstats_rec_t as the last (SIZEOF(gvstats_rec_t)) bytes in the record. * c. Need to copy the existing stats from cs_addrs into the shared record. * d. Write the gvstats_rec_t record. * 3. Once the stats record is written, mark the file R/O to prevent further updates (from this process). * 4. Need to point the cs_addrs pointer to the stats in the newly written record (after locating it via fields * in gv_target. * * The call to op_gvname() sets up gv_currkey for our op_gvput() call also giving us the key length we need for * the alignment pad calculation. * * Parameter(s): * baseDBreg - gd_region* address of base (not hidden) database whose statsDB database we are to initialize. */ void gvcst_init_statsDB(gd_region *baseDBreg, boolean_t do_statsdb_init) { mval pid_mval, baseDBreg_nam_mval; mval statsDBrec_mval, statsDBget_mval; gd_region *statsDBreg, *statsDBreg_located, *save_cur_region; gv_namehead *save_gv_target, *save_reset_gv_target; jnlpool_addrs_ptr_t save_jnlpool; srch_blk_status *bh; char statsDBinitrec[SIZEOF(gvstats_rec_t) * 2]; /* Gives chunk large enuf to hold pad */ int datasize, extlen, freespace, padsize, sizewkey, sizewkeyrnd, rc; gv_key_buf save_altkey, save_currkey; sgmnt_addrs *baseDBcsa, *statsDBcsa; statsDB_deferred_init_que_elem *sdiqeptr; gvnh_reg_t *save_gd_targ_gvnh_reg; gd_binding *save_gd_targ_map; gd_addr *save_gd_targ_addr, *save_gd_header; boolean_t save_gv_last_subsc_null, save_gv_some_subsc_null, longjmp_done1, longjmp_done2; boolean_t save_gvcst_statsDB_open_ch_active; gd_binding *ygs_map; mname_entry gvname; sigset_t unblock_sigbus; ht_ent_mname *tabent; gvnh_reg_t *gvnh_reg; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; BASEDBREG_TO_STATSDBREG(baseDBreg, statsDBreg_located); assert(baseDBreg->open); assert(dba_cm != baseDBreg->dyn.addr->acc_meth); baseDBcsa = &FILE_INFO(baseDBreg)->s_addrs; save_gvcst_statsDB_open_ch_active = TREF(gvcst_statsDB_open_ch_active); /* note down in case this function recurses */ TREF(gvcst_statsDB_open_ch_active) = TRUE; longjmp_done2 = FALSE; /* initialize this variable BEFORE "setjmp" done in first ESTABLISH_NORET below so it is * initialized even if we break out of the for loop due to an error even before * this variable got initialized as part of the second ESTABLISH_NORET below. */ gvcst_statsDB_open_ch_intrpt_ok_state = intrpt_ok_state; /* needed by "gvcst_statsDB_open_ch" */ ESTABLISH_NORET(gvcst_statsDB_open_ch, longjmp_done1); for ( ; !longjmp_done1; ) /* have a dummy for loop to be able to use "break" for various codepaths below */ { if (!do_statsdb_init) { /* Want to open statsDB region but do not want statsdb init (^%YGS addition etc.). This is a call from * OPEN_BASEREG_IF_STATSREG. All the macro cares about is the statsDB be open. Not that a statsDB init * happen. So do just that. The reason we need to do it here is because errors are better handled here * (user-invisible) and the caller will modify gld map entries and/or switch baseDB to NOSTATS if any * errors occur thereby preventing future access to this statsDB database file. */ gvcst_init(statsDBreg_located, NULL); if (statsDBreg_located->open) /* do the check just in case */ { statsDBcsa = &FILE_INFO(statsDBreg_located)->s_addrs; /* Database was opened read/only as this process has no privs to write to it. * Even though we do not need to do the statsdb init now, we know we cannot do the init * successfully and since the caller in this case is in a better position to turn off * stats in the gld (OPEN_BASEREG_IF_STATSREG) use this opportunity to raise an error. */ if (!statsDBcsa->orig_read_write) RTS_ERROR_CSA_ABT(statsDBcsa, VARLSTCNT(4) ERR_DBPRIVERR, 2, DB_LEN_STR(statsDBreg_located)); } break; } if (0 < dollar_tlevel) { /* We are inside a transaction. We cannot do this inside a transaction for two reasons: * * 1. Were we to write the initialization record, there's a chance the record could be unwound as * part of a TP rollback and no mechanism exists to rewrite it. * 2. We actually don't write the gvstats record to the database until the commit so there's no * mechanism to find where the record was written, locate it and set up non-DB-API access to * it as we do with non-TP initializations. * * Solution is to defer the initialization until the transaction is exited - either by commiting * or by aborting the transaction. */ sdiqeptr = malloc(SIZEOF(statsDB_deferred_init_que_elem)); sdiqeptr->baseDBreg = baseDBreg; sdiqeptr->statsDBreg = statsDBreg_located; sdiqeptr->next = TREF(statsDB_init_defer_anchor); TREF(statsDB_init_defer_anchor) = sdiqeptr; /* Although we did not open the statsDB as a statsDB, we still need to open the region and see if * there are any errors before returning to the caller. This is because callers like "gvcst_init" * and "OPEN_BASEREG_IF_STATSREG" rely on this open to happen here (and catch errors) so they * can take appropriate action (see comment in OPEN_BASEREG_IF_STATSREG for example reason). */ gvcst_init(statsDBreg_located, NULL); break; } save_cur_region = gv_cur_region; save_jnlpool = jnlpool; save_gv_target = gv_target; save_reset_gv_target = reset_gv_target; SAVE_GV_CURRKEY(save_currkey); /* It is possible we end up here with the following function call trace starting from name-level $order. * op_gvorder -> op_gvdata -> gvcst_spr_data -> op_tcommit -> gvcst_deferred_init_statsDB -> gvcst_init_statsDB * In that case, the op_gvorder would be relying on "gv_altkey" which we could modify as part of the following call * to set up the ^%YGS global name. * op_gvname -> op_gvname_common -> gv_bind_subsname -> gvcst_root_search * -> SET_GV_ALTKEY_TO_GBLNAME_FROM_GV_CURRKEY * Therefore, we need to save/restore "gv_altkey" too. */ SAVE_GV_ALTKEY(save_altkey); /* Below save is similar to that done in op_gvsavtarg/op_gvrectarg */ save_gd_targ_gvnh_reg = TREF(gd_targ_gvnh_reg); save_gd_targ_map = TREF(gd_targ_map); save_gd_targ_addr = TREF(gd_targ_addr); save_gv_last_subsc_null = TREF(gv_last_subsc_null); save_gv_some_subsc_null = TREF(gv_some_subsc_null); save_gd_header = gd_header; /* save "gd_header" before tampering with global variable */ gd_header = baseDBreg->owning_gd; /* direct "op_gvname" to search for maps in this gld */ /* Must have baseDB open and be opted in to be here */ assert(NO_STATS_OPTIN != TREF(statshare_opted_in)); assert(!IS_DSE_IMAGE); /* DSE opens a statsdb only directly (never through a base DB) */ /* Create a condition handler so the above saved items can be undone on an error to restore the environment */ ESTABLISH_NORET(gvcst_statsDB_init_ch, longjmp_done2); if (longjmp_done2) { /* We returned here due to an error encountered somewhere below. * Restore the things that were saved, then REVERT our handler. */ REVERT; RESTORE_SAVED_VALUES; break; } /* Create mvals with region name and processid in them to feed to op_gvname */ memset((char *)&baseDBreg_nam_mval, 0, SIZEOF(mval)); baseDBreg_nam_mval.mvtype = MV_STR; baseDBreg_nam_mval.str.len = baseDBreg->rname_len; baseDBreg_nam_mval.str.addr = (char *)&baseDBreg->rname; /* Init parts of the gvstats record mval not dependent on further calculations */ statsDBrec_mval.mvtype = MV_STR; statsDBrec_mval.str.addr = statsDBinitrec; /* Step 1: The very first thing that needs to happen when a statsDB is freshly opened is that we need to write * a special record ^%YGS(region,INT_MAX) that must be there to prevent left hand block splits that * can cause a process gvstats record to be moved to a new block. We can't check it until we do the * first op_gvname that opens the proper DB and makes sure cs_addrs is populated, etc. Since most of * the time, processes won't need to do this, we go ahead and do the op_gvname that most processes * need which is for the ^%YGS(region,pid) record. If we then check and find the max record not written, * we'll redo the op_gvname to setup for the max record instead. */ i2mval(&pid_mval, process_id); /* We have already saved gv_currkey at function entry and are tampering with it in this function. * The "op_gvname" call does a DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC check which would cross-check gv_currkey * & gv_target. That check could fail if this function was called when the two were not in sync (possible for * example if the call stack is "op_gvorder -> gv_init_reg -> gvcst_init -> gvcst_init_statsDB". So bypass that * check by clearing the key. */ DEBUG_ONLY(gv_currkey->base[0] = KEY_DELIMITER;) /* to bypass DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC in op_gvname */ /* Note that "op_gvname" of ^%YGS is going to first open the region corresponding to the unsubscripted name %YGS. * That is guaranteed (by GDE) to be a statsDB region. If it happens to be different from "statsDBreg_located", * then we need to open that region first and that in turn means we first need to open its associated baseDB region * (Design assumption : statsDB region is never opened until its corresponding baseDB region is opened). To avoid * these unnecessary database opens, we modify the gld map so the unsubscripted %YGS name maps to * "statsDBreg_located". This way we don't need any opens of baseDB as we know "baseDBreg" (baseDB region of * "statsDBreg_located") is already open at this point. And we avoid doing this map modification more than once * per gld using the "ygs_map_entry_changed" flag in the gld. */ if (!gd_header->ygs_map_entry_changed) { ygs_map = gv_srch_map(gd_header, STATSDB_GBLNAME, STATSDB_GBLNAME_LEN, SKIP_BASEDB_OPEN_TRUE); /* SKIP_BASEDB_OPEN_TRUE is to signal "gv_srch_map" to not invoke OPEN_BASEREG_IF_STATSREG. * See comment about "skip_basedb_open" in function prototype of "gv_srch_map" for details. */ assert(IS_STATSDB_REG(statsDBreg_located)); ygs_map->reg.addr = statsDBreg_located; gvname.var_name.addr = STATSDB_GBLNAME; gvname.var_name.len = STATSDB_GBLNAME_LEN; COMPUTE_HASH_MSTR(gvname.var_name, gvname.hash_code); tabent = lookup_hashtab_mname((hash_table_mname *)gd_header->tab_ptr, &gvname); if (NULL != tabent) { /* Repoint ^%YGS hashtable entry to point unsubscripted global name to new statsdb region */ gvnh_reg = (gvnh_reg_t *)tabent->value; assert(NULL != gvnh_reg); gvnh_reg->gd_reg = statsDBreg_located; } gd_header->ygs_map_entry_changed = TRUE; } extlen = extnam_str.len; op_gvname(3, (mval *)&literal_statsDB_gblname, &baseDBreg_nam_mval, &pid_mval); extnam_str.len = extlen; assert(NULL != gv_currkey); assert(0 != gv_currkey->end); statsDBreg = gv_cur_region; if (statsDBreg != statsDBreg_located) { /* For some reason the statsDB was not opened or is not available - mark it NOSTATS and we are done */ baseDBreg->reservedDBFlags |= RDBF_NOSTATS; baseDBcsa->reservedDBFlags |= RDBF_NOSTATS; REVERT; RESTORE_SAVED_VALUES; break; } statsDBcsa = &FILE_INFO(statsDBreg)->s_addrs; if (!statsDBcsa->orig_read_write) /* Database was opened read/only as this process has no privs to write to it - raise error */ RTS_ERROR_CSA_ABT(statsDBcsa, VARLSTCNT(4) ERR_DBPRIVERR, 2, DB_LEN_STR(statsDBreg)); if (!statsDBcsa->statsDB_setup_completed) { /* If initialization was never completed, do it now */ assert(IS_STATSDB_REG(statsDBreg)); assert(statsDBcsa->orig_read_write); /* Step 2: Now figure out the alignment pad size needed to make record fields align in memory */ sizewkey = SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + gv_currkey->end + 1; sizewkeyrnd = ROUND_UP2(sizewkey, SIZEOF(gtm_uint64_t)); padsize = sizewkeyrnd - sizewkey; /* Now to figure out the padding to prevent additional records in this block. Find how much space in this * block remains unused after this record. Use this space to increase the pad size by the needed amount. */ freespace = cs_data->blk_size - sizewkeyrnd - SIZEOF(gvstats_rec_t); padsize += (freespace >= MIN_STATSDB_REC) ? (((freespace - MIN_STATSDB_REC) / 8) + 1) * 8 : 0; if (0 < padsize) memset(statsDBinitrec, 0, padsize); /* Initialize prefix pad area with NULLs */ /* Move the current stats in cs_addrs to the shared rec we are about to write */ memcpy(statsDBinitrec + padsize, baseDBcsa->gvstats_rec_p, SIZEOF(gvstats_rec_t)); memset((char *)&statsDBrec_mval, 0, SIZEOF(mval)); statsDBrec_mval.mvtype = MV_STR; statsDBrec_mval.str.addr = statsDBinitrec; statsDBrec_mval.str.len = padsize + SIZEOF(gvstats_rec_t); /* If this statsDB was previously opened by direct references to ^%YGS, gvcst_init() will have set the DB * to R/O mode so if it was previously opened, we need to set it back to write-mode to add the needed * gvstats record. */ assert(statsDBreg_located == statsDBreg); statsDBreg->read_only = FALSE; /* Maintain read_only/read_write in parallel */ statsDBcsa->read_write = TRUE; /* Maintain reg->read_only simultaneously */ op_gvput(&statsDBrec_mval); statsDBreg->read_only = TRUE; statsDBcsa->read_write = FALSE; /* Maintain read_only/read_write in parallel */ /* Step 3: Now we have written a record - set the DB to R/O */ assert(statsDBcsa == &FILE_INFO(statsDBreg)->s_addrs); /* Step 4: Locate the newly written record and update the csa->gvstats_rec_p so new stats updates occur * in shared memory instead of process-private. */ bh = gv_target->hist.h; /* "op_gvput" could have returned a 0 value in case it was the one creating the GVT. To make sure we have * a clean history - fetch back the record we just wrote. */ gvcst_get(&statsDBget_mval); /* This should call "gvcst_search" which should fill in "bh->curr_rec" */ assert(0 < bh->curr_rec.offset); assert((gv_currkey->end + 1) == bh->curr_rec.match); assert(SIZEOF(blk_hdr) == bh->curr_rec.offset); /* Should find 1st record in blk */ baseDBcsa->gvstats_rec_p = (gvstats_rec_t *)(bh->buffaddr + sizewkey + padsize); /* ==> Start of gvstats_rec_t */ assert(0 == (((UINTPTR_T)baseDBcsa->gvstats_rec_p) & 0x7)); /* Verify 8 byte alignment */ statsDBcsa->statsDB_setup_completed = TRUE; } statsDBreg->statsDB_setup_started = FALSE; statsDBreg->statsDB_setup_completed = TRUE; REVERT; /* Restore previous region's setup */ RESTORE_SAVED_VALUES; break; } REVERT; TREF(gvcst_statsDB_open_ch_active) = save_gvcst_statsDB_open_ch_active; /* Now that we have restored the environment as it was at function entry, check if there were any errors * inside any of the two ESTABLISH_NORETs done above. If so, handle them appropriately. */ if (longjmp_done2 || longjmp_done1) { /* Check if we got a TPRETRY (an internal error) in "gvcst_init" above. If so drive parent condition handler to * trigger restart. For any other error conditions, do not do anything more as we want statsdb open to silently * switch to nostats in that case. */ assert(0 != error_condition); if (ERR_TPRETRY == error_condition) DRIVECH(error_condition); /* Drive lower level handlers with same error we had */ if (TRUE == TREF(statsdb_memerr)) { /* Case where we've received a SIGBUS trying to create the stats block. * In this case, we don't want to turn off stats, because we want the process * to continue with private updates. */ TREF(statsdb_memerr) = FALSE; /* unblock SIGBUS since we did a long jump */ sigemptyset(&unblock_sigbus); sigaddset(&unblock_sigbus, SIGBUS); SIGPROCMASK(SIG_UNBLOCK, &unblock_sigbus, NULL, rc); } else { /* Since we got an error while trying to open the statsDB, silently set NOSTATS on baseDB */ baseDBreg->reservedDBFlags |= RDBF_NOSTATS; baseDBcsa->reservedDBFlags |= RDBF_NOSTATS; } if (ERR_DRVLONGJMP == error_condition) SET_ERROR_CONDITION(pre_drvlongjmp_error_condition); } return; } /* Simplistic handler for when errors occur during open of statsDB. The ESTABLISH_NORET is a place-holder for the * setjmp/longjmp sequence used to recover quietly from errors opening* the statsDB. So if the error is the magic * ERR_DRVLONGJMP, do just that (longjmp() via the UNWIND() macro). If the error is otherwise, capture the error * and where it was raised and send the STATSDBERR to the syslog before we unwind back to the ESTABLISH_NORET. */ CONDITION_HANDLER(gvcst_statsDB_open_ch) { char buffer[OUT_BUFF_SIZE]; gd_region *reg; int msglen; mval zpos; node_local_ptr_t cnl; START_CH(TRUE); assert(ERR_DBROLLEDBACK != arg); /* A statsDB region should never participate in rollback */ if (DUMPABLE) NEXTCH; /* Bubble down till handled properly in mdb_condition_handler() */ if ((SUCCESS == SEVERITY) || (INFO == SEVERITY)) CONTINUE; /* Keep going for non-error issues */ if (ERR_DRVLONGJMP != arg) { /* Need to reflect the current error to the syslog - 1st check whether to throttle because it's a repeat */ if (IS_STATSDB_REG(db_init_region)) STATSDBREG_TO_BASEDBREG(db_init_region, reg); else reg = db_init_region; cnl = (&FILE_INFO(reg)->s_addrs)->nl; if ((cnl->statsdb_cur_error != SIGNAL) || !cnl->statsdb_error_cycle--) { /* Not a repeat or we've surpressed STATSDB_ERROR_RATE messages */ cnl->statsdb_error_cycle = STATSDB_ERROR_RATE; cnl->statsdb_cur_error = SIGNAL; msglen = TREF(util_outptr) - TREF(util_outbuff_ptr); assert(OUT_BUFF_SIZE > msglen); memcpy(buffer, TREF(util_outbuff_ptr), msglen); /* save message that got us here */ getzposition(&zpos); /* Send whole thing to syslog */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_STATSDBERR, 4, zpos.str.len, zpos.str.addr, msglen, buffer); } } intrpt_ok_state = gvcst_statsDB_open_ch_intrpt_ok_state; UNWIND(NULL, NULL); /* Return back to where ESTABLISH_NORET was done */ } /* Condition handler for gvcst_statsDB_init() - all we need to do is unwind back to where the ESTABLISH_NORET() is done * as the code at that point can do the needful in the frame of reference it needs to be done. */ CONDITION_HANDLER(gvcst_statsDB_init_ch) { START_CH(TRUE); if (DUMPABLE) NEXTCH; /* Bubble down till handled properly in mdb_condition_handler() */ if ((SUCCESS == SEVERITY) || (INFO == SEVERITY)) CONTINUE; /* Keep going for non-error issues */ DEBUG_ONLY(ok_to_UNWIND_in_exit_handling = TRUE); UNWIND(NULL, NULL); /* Return back to where ESTABLISH_NORET was done */ } /* Routine to remove the process's gvstats_rec from the statsDB database associated with a given baseDB. This also * entails copying the shared stats record back into cs_addrs of the baseDB and resetting it's stats pointer. This * "unlinks" the two databases. * * Parameter(s): * baseDBreg - gd_region* of baseDB whose stats we are "unsharing". */ void gvcst_remove_statsDB_linkage(gd_region *baseDBreg) { mval pid_mval, baseDBreg_nam_mval; mval statsDBrec_mval, statsDBget_mval; gd_region *statsDBreg, *save_cur_region; gv_namehead *save_gv_target, *save_reset_gv_target; char statsDBinitrec[SIZEOF(gvstats_rec_t) * 2]; /* Gives us a chunk large enuf to hold padding */ gv_key_buf save_currkey; sgmnt_addrs *baseDBcsa, *statsDBcsa; gvstats_rec_t *gvstats_rec_p; gvnh_reg_t *save_gd_targ_gvnh_reg; gd_binding *save_gd_targ_map; gd_addr *save_gd_targ_addr, *save_gd_header; jnlpool_addrs_ptr_t save_jnlpool; boolean_t save_gv_last_subsc_null, save_gv_some_subsc_null; # ifdef DEBUG mval stats_rec; srch_blk_status *bh; uint4 recsize; rec_hdr *recptr; gd_region *statsDBreg_located; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(dba_cm != baseDBreg->dyn.addr->acc_meth); save_cur_region = gv_cur_region; save_gv_target = gv_target; save_reset_gv_target = reset_gv_target; SAVE_GV_CURRKEY(save_currkey); save_jnlpool = jnlpool; /* Below save is similar to that done in op_gvsavtarg/op_gvrectarg */ save_gd_targ_gvnh_reg = TREF(gd_targ_gvnh_reg); save_gd_targ_map = TREF(gd_targ_map); save_gd_targ_addr = TREF(gd_targ_addr); save_gv_last_subsc_null = TREF(gv_last_subsc_null); save_gv_some_subsc_null = TREF(gv_some_subsc_null); /* Create mvals with region name and processid in them to feed to op_gvname */ memset((char *)&baseDBreg_nam_mval, 0, SIZEOF(mval)); baseDBreg_nam_mval.mvtype = MV_STR; baseDBreg_nam_mval.str.len = baseDBreg->rname_len; baseDBreg_nam_mval.str.addr = (char *)&baseDBreg->rname; i2mval(&pid_mval, process_id); /* Step 1: Locate the existing ^%YGS(region,pid) node. This locates the existing record for us so we can * copy the gvstats_rec data back to private storage in cs_addrs but also sets up the key for the * "op_gvkill" call to kill that record. */ save_gd_header = gd_header; /* save "gd_header" before tampering with global variable */ gd_header = baseDBreg->owning_gd; /* direct "op_gvname" to search for maps in this gld */ /* We have already saved gv_currkey at function entry and are tampering with it in this function. * The "op_gvname" call does a DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC check which would cross-check gv_currkey * & gv_target. That check could fail if this function was called when the two were not in sync (possible for * example if the call stack is "op_gvorder -> gv_init_reg -> gvcst_init -> gvcst_init_statsDB". So bypass that * check by clearing the key. */ DEBUG_ONLY(gv_currkey->base[0] = KEY_DELIMITER;) /* to bypass DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC in op_gvname */ op_gvname(3, (mval *)&literal_statsDB_gblname, &baseDBreg_nam_mval, &pid_mval); assert(NULL != gv_currkey); assert(0 != gv_currkey->end); statsDBreg = gv_cur_region; statsDBcsa = &FILE_INFO(statsDBreg)->s_addrs; DEBUG_ONLY(BASEDBREG_TO_STATSDBREG(baseDBreg, statsDBreg_located)); assert(statsDBreg == statsDBreg_located); assert(IS_STATSDB_REG(statsDBreg)); assert(statsDBreg->statsDB_setup_completed); /* Step 2: Need to switch the database back to R/W so we can do the KILL */ assert(statsDBcsa == &FILE_INFO(statsDBreg)->s_addrs); /* Note that if multiple statsDB regions map to the same statsDB file, it is possible to have * statsDBreg->statsDB_setup_completed TRUE for more than one such region in which case all of them * would map to the same statsDBcsa and might end up calling this function more than once for the same statsDBcsa. * In that case, do the removal of ^%YGS node only once. The below check accomplishes that. */ if (statsDBcsa->statsDB_setup_completed) { assert(statsDBcsa->orig_read_write); /* Step 3: Copy the shared gvstats_rec_t data back to private and for debug, verify record address in DEBUG but only * if the baseDB is actually still open. */ if (baseDBreg->open) { baseDBcsa = &FILE_INFO(baseDBreg)->s_addrs; # ifdef DEBUG gvcst_get(&stats_rec); /* Fetch record to set history for DEBUG mode validation */ bh = gv_target->hist.h; assert(0 != bh->curr_rec.match); /* Shouldn't be possible to create a GVT with this call */ assert((gv_currkey->end + 1) == bh->curr_rec.match); assert(SIZEOF(blk_hdr) == bh->curr_rec.offset); /* We should find 1st record in block */ recptr = (rec_hdr *)(bh->buffaddr + SIZEOF(blk_hdr)); recsize = recptr->rsiz; /* The gvstats_rec_t part of the record is the last part of the record */ gvstats_rec_p = (gvstats_rec_t *)((char *)recptr + (recsize - SIZEOF(gvstats_rec_t))); assert(gvstats_rec_p == baseDBcsa->gvstats_rec_p); # else gvstats_rec_p = baseDBcsa->gvstats_rec_p; # endif memcpy(&baseDBcsa->gvstats_rec, gvstats_rec_p, SIZEOF(gvstats_rec_t)); baseDBcsa->gvstats_rec_p = &baseDBcsa->gvstats_rec; /* ==> Reset start of gvstats_rec_t to private */ } /* Step 4: Kill the record */ statsDBreg->read_only = FALSE; statsDBcsa->read_write = TRUE; /* Maintain read_only/read_write in parallel */ op_gvkill(); statsDBreg->read_only = TRUE; statsDBcsa->read_write = FALSE; /* Maintain read_only/read_write in parallel */ statsDBcsa->statsDB_setup_completed = FALSE; } /* Restore previous region's setup */ TP_CHANGE_REG(save_cur_region); jnlpool = save_jnlpool; gv_target = save_gv_target; reset_gv_target = save_reset_gv_target; RESTORE_GV_CURRKEY(save_currkey); TREF(gd_targ_gvnh_reg) = save_gd_targ_gvnh_reg; TREF(gd_targ_map) = save_gd_targ_map; TREF(gd_targ_addr) = save_gd_targ_addr; TREF(gv_last_subsc_null) = save_gv_last_subsc_null; TREF(gv_some_subsc_null) = save_gv_some_subsc_null; gd_header = save_gd_header; } /* Routine to remove the statsDB linkages between the statsDB databases and the baseDBs for all open statsDBs. * General purpose of this routine is to run the regions, find the statsDBs, locate the associated baseDBs, * and call a routine to cleanup one baseDB/statsDB region at a time using a condition handler wrapper so * we can report (via operator log messages) but then largely ignore errors so we can process each region * appropriately. We are doing this in the exit handler so error handling capability is at a minimum. */ void gvcst_remove_statsDB_linkage_all(void) { gd_addr *gdhdr_addr; gd_region *r_top, *r_save, *statsDBreg, *baseDBreg; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(0 == dollar_tlevel); for (gdhdr_addr = get_next_gdr(NULL); gdhdr_addr; gdhdr_addr = get_next_gdr(gdhdr_addr)) { /* For each global directory */ for (statsDBreg = gdhdr_addr->regions, r_top = statsDBreg + gdhdr_addr->n_regions; statsDBreg < r_top; statsDBreg++) { if (statsDBreg->open && IS_STATSDB_REG(statsDBreg) && statsDBreg->statsDB_setup_completed) { /* We really are an OPEN and initialized statsDB - remove the link */ STATSDBREG_TO_BASEDBREG(statsDBreg, baseDBreg); assert(NULL != baseDBreg); gvcst_remove_statsDB_linkage_wrapper(baseDBreg, statsDBreg); } } } } /* Routine to provide and condition handler wrapper for gvcst_remove_statsDB_linkage() when called from the exit handler. * This is because we can't tolerate much error handling in the exit handler so any errors we do get, we report and * unwind (ignore). Since we are taking things apart here, the ramifications of ignoring an error aren't bad. Worst * risk is not deleting the process record we created. * * Parameter(s): * baseDBreg - gd_region* of baseDB we want to unlink * statsDBreg - gd_region* of statsDB we want to unlink */ void gvcst_remove_statsDB_linkage_wrapper(gd_region *baseDBreg, gd_region *statsDBreg) { ESTABLISH(gvcst_remove_statsDB_linkage_ch); save_statsDBreg = statsDBreg; /* Save for use in condition handler */ gvcst_remove_statsDB_linkage(baseDBreg); /* If fails, we unwind and go to next one */ REVERT; } /* Routine to handle deferred statsDB initializations. These are initializations that were deferred because they occurred * inside a TP transaction. We are now (or should be) free of that transaction so the initializations can be performed. Any * regions deferred are in a queue. Process them now and release the blocks that recorded them (this is a one time thing * so no reason exists to hold or queue the free blocks). */ void gvcst_deferred_init_statsDB(void) { statsDB_deferred_init_que_elem *sdiqeptr, *sdiqeptr_next; gd_region *baseDBreg, *statsDBreg; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (process_exiting) /* No sense opening anything if we are exiting. Like driven by exit handler's rollback */ return; assert(0 == dollar_tlevel); assert(NULL != TREF(statsDB_init_defer_anchor)); for (sdiqeptr = TREF(statsDB_init_defer_anchor), sdiqeptr_next = sdiqeptr->next; sdiqeptr; sdiqeptr = sdiqeptr_next) { /* For each statsDB on the queue, perform initialization and free the deferred init block */ sdiqeptr_next = sdiqeptr->next; /* Next entry on queue or NULL */ /* We wanted to initialize it earlier but couldn't because we were in a transaction. Now the baseDB * still needs to be open and not re-opened and we can't have completed initialization elsewhere. */ baseDBreg = sdiqeptr->baseDBreg; statsDBreg = sdiqeptr->statsDBreg; assert(baseDBreg->open); if (!statsDBreg->statsDB_setup_completed) gvcst_init_statsDB(sdiqeptr->baseDBreg, DO_STATSDB_INIT_TRUE); TREF(statsDB_init_defer_anchor) = sdiqeptr_next; /* Remove current entry from queue */ free(sdiqeptr); } } /* Condition handler for gvcst_remove_statsDB_linkage_wrapper */ CONDITION_HANDLER(gvcst_remove_statsDB_linkage_ch) { char buffer[OUT_BUFF_SIZE]; int msglen; mval zpos; START_CH(TRUE); /* Save error that brought us here */ msglen = TREF(util_outptr) - TREF(util_outbuff_ptr); assert(OUT_BUFF_SIZE > msglen); memcpy(buffer, TREF(util_outbuff_ptr), msglen); getzposition(&zpos); /* Find out where it occurred */ /* Send whole thing to syslog */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_RNDWNSTATSDBFAIL, 10, REG_LEN_STR(save_statsDBreg), DB_LEN_STR(save_statsDBreg), zpos.str.len, zpos.str.addr, msglen, buffer); if (DUMPABLE && !SUPPRESS_DUMP) { need_core = TRUE; gtm_fork_n_core(); } DEBUG_ONLY(ok_to_UNWIND_in_exit_handling = TRUE); UNWIND(NULL, NULL); /* This returns back to gvcst_remove_statsDB_linkage_all() to do next region */ } /* For a given input basedb file name "fname", this function determines the corresponding "statsdb" filename and sets it * in the 2nd and 3rd parameters (name & byte-length). If no statsdb file name can be determined, *statsdb_fname_len is set to 0. * *statsdb_fname_len, at function entry time, is set to the allocated length of the statsdb_fname char array. */ void gvcst_set_statsdb_fname(sgmnt_data_ptr_t csd, gd_region *baseDBreg, char *statsdb_fname, uint4 *statsdb_fname_len) { boolean_t fname_changed, statsdb_off; char *basedb_fname, *baseBuf, *baseTop, *statsBuf, *statsTop; char tmp_fname[MAX_FN_LEN + 1], save_basedb_fname[MAX_FN_LEN + 1]; int baseBufLen, save_basedb_fname_len, statsBufLen; int int_status; key_t hash_ftok; gd_segment *baseDBseg; mstr dbfile, trans, val; parse_blk pblk; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Database file name could be relative path (e.g. mupip rundown -file mumps.dat or other mupip commands * that require standalone access which specify a relative file name). Convert it to absolute path if possible. * The ftok of the path to the basedb is needed by in order to derive the statsdb file name . * Ignore any error exit status. In that case, continue with relative path. */ baseDBseg = baseDBreg->dyn.addr; basedb_fname = (char *)baseDBseg->fname; assert('\0' != basedb_fname[0]); if ('/' != basedb_fname[0]) { fname_changed = TRUE; /* We need to make baseDBreg->dyn.addr->fname an absolute path temporarily. Save a copy and restore later */ assert(ARRAYSIZE(save_basedb_fname) >= ARRAYSIZE(baseDBseg->fname)); save_basedb_fname_len = baseDBseg->fname_len; assert(ARRAYSIZE(save_basedb_fname) > save_basedb_fname_len); memcpy(save_basedb_fname, baseDBseg->fname, save_basedb_fname_len + 1); /* copy trailing '\0' too */ mupfndfil(baseDBreg, NULL, LOG_ERROR_FALSE); /* this will modify baseDBreg->dyn.addr->fname */ } else fname_changed = FALSE; assert(ARRAYSIZE(tmp_fname) <= *statsdb_fname_len); /* caller should have initialized it */ statsdb_off = FALSE; TREF(statsdb_fnerr_reason) = FNERR_NOERR; for ( ; ; ) /* have a dummy for loop to be able to use "break" for various codepaths below */ { if (RDBF_NOSTATS & csd->reservedDBFlags) { TREF(statsdb_fnerr_reason) = FNERR_NOSTATS; statsdb_off = TRUE; /* This db does not have stats turned on (e.g. statsdb) */ break; } /* This db has stats turned on. Store the full path name of the stats db when base db shm is created. * The stats db file name is of the form "$gtm_statsdir/.BASEDB-FILE-NAME.gst" where "$gtm_statsdir" * evaluates to a directory, is the ftok of the BASEDB-ABSOLUTE-PATH and BASEDB-FILE-NAME is the * file name minus the path. For example, if the basedb is "/a/a.dat", the corresponding statsdb file name * is "$gtm_statsdir/.a.dat.gst" where is the ftok of the directory "/a". * Note: The stats db will be created later when it actually needs to be opened by a process that has * opted in (VIEW STATSHARE or $gtm_statshare env var set). */ val.addr = GTM_STATSDIR; val.len = SIZEOF(GTM_STATSDIR) - 1; statsBuf = &tmp_fname[0]; /* Note: "gtm_env_init_sp" already processed GTM_STATSDIR to make it default to GTM_TMP_ENV etc. */ int_status = TRANS_LOG_NAME(&val, &trans, statsBuf, MAX_STATSDIR_LEN, do_sendmsg_on_log2long); if (SS_NORMAL != int_status) { assert(FALSE); /* Same TRANS_LOG_NAME in "gtm_env_init_sp" succeeded so this cannot fail */ TREF(statsdb_fnerr_reason) = FNERR_STATSDIR_TRNFAIL; statsdb_off = TRUE; break; } statsTop = statsBuf + MAX_FN_LEN; /* leave room for '\0' at end (hence not "MAX_FN_LEN + 1") */ statsBuf += trans.len; if ((statsBuf + 1) >= statsTop) { /* Not enough space to store full-path file name of statsdb in basedb shm */ assert(FALSE); TREF(statsdb_fnerr_reason) = FNERR_STATSDIR_TRN2LONG; statsdb_off = TRUE; break; } *statsBuf++ = '/'; /* Now find the base db file name (minus the path) */ baseBuf = strrchr(basedb_fname, '/'); if (NULL == baseBuf) { assert(FALSE); /* Since db file name is an absolute path, we should see at least one '/' */ TREF(statsdb_fnerr_reason) = FNERR_INV_BASEDBFN; statsdb_off = TRUE; break; } *baseBuf = '\0'; /* temporarily modify basedb name to include just the directory */ hash_ftok = FTOK(basedb_fname, GTM_ID); *baseBuf++ = '/'; /* restore basedb full path name */ if (-1 == hash_ftok) { assert(FALSE); /* possible only if parent dir of base db was deleted midway in db init */ TREF(statsdb_fnerr_reason) = FNERR_FTOK_FAIL; statsdb_off = TRUE; break; } /* Now add the hash to the statsdb file name */ if ((statsBuf + 9) >= statsTop) /* 8 bytes for hex display of 4-byte ftok/hash, 1 byte for '.' */ { /* Not enough space to store full-path file name of statsdb in basedb shm */ TREF(statsdb_fnerr_reason) = FNERR_FNAMEBUF_OVERFLOW; statsdb_off = TRUE; break; } SNPRINTF(statsBuf, statsTop - statsBuf, "%x", hash_ftok); statsBuf += 8; *statsBuf++ = '.'; /* Now add the basedb file name + ".gst" extension to the statsdb file name */ baseBufLen = STRLEN(baseBuf); if ((statsBuf + baseBufLen + STR_LIT_LEN(STATSDB_FNAME_SUFFIX)) > statsTop) { /* Not enough space to store full-path file name of statsdb in basedb shm */ TREF(statsdb_fnerr_reason) = FNERR_FNAMEBUF_OVERFLOW; statsdb_off = TRUE; break; } memcpy(statsBuf, baseBuf, baseBufLen); statsBuf += baseBufLen; MEMCPY_LIT(statsBuf, STATSDB_FNAME_SUFFIX); statsBuf += STR_LIT_LEN(STATSDB_FNAME_SUFFIX); *statsBuf = '\0'; assert(statsBuf <= statsTop); statsBufLen = statsBuf - tmp_fname; statsBuf = &tmp_fname[0]; /* Now call "parse_file" to translate any "." or ".." usages in the path */ memcpy(tmp_fname, statsBuf, statsBufLen); dbfile.addr = tmp_fname; dbfile.len = statsBufLen; memset(&pblk, 0, SIZEOF(pblk)); pblk.buffer = statsdb_fname; pblk.buff_size = (unsigned char)(MAX_FN_LEN);/* Pass buffersize - 1 (standard protocol for parse_file) */ pblk.def1_buf = STATSDB_FNAME_SUFFIX; pblk.def1_size = STR_LIT_LEN(STATSDB_FNAME_SUFFIX); pblk.fop = F_SYNTAXO; /* Syntax check only - bypass directory / file existence check. */ int_status = parse_file(&dbfile, &pblk); if (!(int_status & 1)) { /* Some error in "parse_file". Likely not enough space to store full-path file name of statsdb. */ assert(FALSE); TREF(statsdb_fnerr_reason) = FNERR_FNAMEBUF_OVERFLOW; statsdb_off = TRUE; break; } assert(pblk.b_esl < *statsdb_fname_len); *statsdb_fname_len = pblk.b_esl; pblk.buffer[pblk.b_esl] = 0; /* null terminate "cnl->statsdb_fname" */ break; } if (statsdb_off) { assert(0 < *statsdb_fname_len); *statsdb_fname_len = 0; statsdb_fname[0] = '\0'; /* turn off stats gathering for this base db */ } if (fname_changed) { /* Restore baseDBseg->fname and baseDBseg->fname_len */ memcpy(baseDBseg->fname, save_basedb_fname, save_basedb_fname_len + 1); baseDBseg->fname_len = save_basedb_fname_len; } return; } fis-gtm-V7.0-005/sr_port/gvcst_root_search.c0000644000032200000250000003451514342376331017735 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_stdio.h" #include "gtmio.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "cdb_sc.h" #include "copy.h" #include "spec_type.h" #include "stringpool.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ #include "t_end.h" /* prototypes */ #include "t_retry.h" #include "t_begin.h" #include "gvcst_protos.h" /* for gvcst_search,gvcst_root_search prototype */ #include "get_spec.h" #include "collseq.h" #include "gtmimagename.h" #include "error.h" #include "io.h" #include "gvt_inline.h" GBLREF gv_key *gv_currkey, *gv_altkey; GBLREF int4 gv_keysize; GBLREF gv_namehead *gv_target; GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF uint4 dollar_tlevel; GBLREF uint4 dollar_trestart; GBLREF unsigned int t_tries; GBLREF gv_namehead *reset_gv_target; GBLREF boolean_t mu_reorg_process; GBLREF boolean_t mupip_jnl_recover; # ifdef DEBUG GBLREF boolean_t is_rcvr_server; GBLREF boolean_t is_src_server; GBLREF unsigned char t_fail_hist_dbg[T_FAIL_HIST_DBG_SIZE]; GBLREF unsigned int t_tries_dbg; # endif GBLREF jnl_gbls_t jgbl; GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; GBLREF trans_num start_tn; GBLREF uint4 update_trans; GBLREF inctn_opcode_t inctn_opcode; GBLREF uint4 t_err; #ifdef GTM_TRIGGER GBLREF boolean_t skip_INVOKE_RESTART; #endif error_def(ERR_ACTCOLLMISMTCH); error_def(ERR_GVGETFAIL); error_def(ERR_NCTCOLLSPGBL); static mstr global_collation_mstr; #ifdef GTM_TRIGGER # define TRIG_TP_SET_SGM \ { \ if (dollar_tlevel) \ { \ assert(skip_INVOKE_RESTART); \ tp_set_sgm(); \ } \ } #else # define TRIG_TP_SET_SGM #endif #define T_RETRY_AND_CLEANUP(STATUS, DONOT_RESTART) \ { \ gv_target->clue.end = 0; \ RESET_GV_TARGET_LCL_AND_CLR_GBL(save_targ, DO_GVT_GVKEY_CHECK); \ if (DONOT_RESTART) \ return status; /* caller will handle the restart */ \ t_retry(STATUS); \ save_targ->root = 0; /* May have been found by gvcst_redo_root_search. \ * Reset and allow gvcst_root_search to find it itself. \ */ \ TRIG_TP_SET_SGM; \ } #define SAVE_ROOTSRCH_ENTRY_STATE \ { \ int idx; \ redo_root_search_context *rootsrch_ctxt_ptr; \ \ GBLREF gv_namehead *reorg_gv_target; \ \ rootsrch_ctxt_ptr = &(TREF(redo_rootsrch_ctxt)); \ rootsrch_ctxt_ptr->t_tries = t_tries; \ for (idx = 0; CDB_MAX_TRIES > idx; idx++) \ rootsrch_ctxt_ptr->t_fail_hist[idx] = t_fail_hist[idx]; \ rootsrch_ctxt_ptr->prev_t_tries = TREF(prev_t_tries); \ DEBUG_ONLY( \ rootsrch_ctxt_ptr->t_tries_dbg = t_tries_dbg; \ for (idx = 0; T_FAIL_HIST_DBG_SIZE > idx; idx++) \ rootsrch_ctxt_ptr->t_fail_hist_dbg[idx] = t_fail_hist_dbg[idx]; \ ) \ rootsrch_ctxt_ptr->start_tn = start_tn; \ rootsrch_ctxt_ptr->update_trans = update_trans; \ rootsrch_ctxt_ptr->inctn_opcode = inctn_opcode; \ /* Resetting and restoring of update_trans is necessary to avoid blowing an assert in \ * t_begin that it is 0. \ */ \ update_trans = 0; \ inctn_opcode = inctn_invalid_op; \ rootsrch_ctxt_ptr->t_err = t_err; \ rootsrch_ctxt_ptr->hold_onto_crit = cs_addrs->hold_onto_crit; \ if (CDB_STAGNATE <= t_tries) \ { \ assert(cs_addrs->now_crit); \ cs_addrs->hold_onto_crit = TRUE; \ } \ if (mu_reorg_process) \ { /* In case gv_currkey/gv_target are out of sync. */ \ rootsrch_ctxt_ptr->gv_currkey = (gv_key *)&rootsrch_ctxt_ptr->currkey.key; \ MEMCPY_KEY(rootsrch_ctxt_ptr->gv_currkey, gv_currkey); \ SET_GV_CURRKEY_FROM_GVT(reorg_gv_target); \ } \ } #define RESTORE_ROOTSRCH_ENTRY_STATE \ { \ int idx; \ redo_root_search_context *rootsrch_ctxt_ptr; \ \ rootsrch_ctxt_ptr = &(TREF(redo_rootsrch_ctxt)); \ t_tries = rootsrch_ctxt_ptr->t_tries; \ for (idx = 0; CDB_MAX_TRIES > idx; idx++) \ t_fail_hist[idx] = rootsrch_ctxt_ptr->t_fail_hist[idx]; \ TREF(prev_t_tries) = rootsrch_ctxt_ptr->prev_t_tries; \ DEBUG_ONLY( \ t_tries_dbg = rootsrch_ctxt_ptr->t_tries_dbg; \ for (idx = 0; T_FAIL_HIST_DBG_SIZE > idx; idx++) \ t_fail_hist_dbg[idx] = rootsrch_ctxt_ptr->t_fail_hist_dbg[idx]; \ ) \ start_tn = rootsrch_ctxt_ptr->start_tn; \ update_trans = rootsrch_ctxt_ptr->update_trans; \ inctn_opcode = rootsrch_ctxt_ptr->inctn_opcode; \ t_err = rootsrch_ctxt_ptr->t_err; \ cs_addrs->hold_onto_crit = rootsrch_ctxt_ptr->hold_onto_crit; \ TREF(in_gvcst_redo_root_search) = FALSE; \ if (mu_reorg_process) \ /* Restore gv_currkey */ \ MEMCPY_KEY(gv_currkey, rootsrch_ctxt_ptr->gv_currkey); \ } CONDITION_HANDLER(gvcst_redo_root_search_ch) { START_CH(TRUE); RESTORE_ROOTSRCH_ENTRY_STATE; NEXTCH; } /** * Called when a root block is out-of-sync with the database (such as if it has been moved during a reorg). */ void gvcst_redo_root_search() { DEBUG_ONLY(boolean_t dbg_now_crit;) uint4 lcl_onln_rlbkd_cycle; boolean_t expand_prev_key; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ESTABLISH(gvcst_redo_root_search_ch); assert(!TREF(in_gvcst_redo_root_search)); /* Should never recurse. However, can be called from non-redo gvcst_root_search, * e.g. from op_gvname. In that case, the results of gvcst_redo_root_search are * discarded and the outer root search correctly sets gv_target->root. */ TREF(in_gvcst_redo_root_search) = TRUE; /* If TREF(expand_prev_key) is true (as it is in gvcst_zprevious2), it can wreck havoc in gvcst_search in some cases; * this is put here to guard against such a thing happening. The known case of havod is in gvcst_zprevious2, * but changes more local to that file did not address all cases, so this is put in */ expand_prev_key = TREF(expand_prev_key); TREF(expand_prev_key) = FALSE; assert(0 < t_tries); assert(!is_src_server && !is_rcvr_server); assert(!jgbl.onlnrlbk); assert((NULL != gv_target) && !gv_target->root); assert(cs_addrs == gv_target->gd_csa); assert(!dollar_tlevel); DEBUG_ONLY(dbg_now_crit = cs_addrs->now_crit); /* save global variables now that we are going to do the root search in the middle of the current transaction */ SAVE_ROOTSRCH_ENTRY_STATE; lcl_onln_rlbkd_cycle = cs_addrs->db_onln_rlbkd_cycle; GVCST_ROOT_SEARCH; if (lcl_onln_rlbkd_cycle != cs_addrs->nl->db_onln_rlbkd_cycle) TREF(rlbk_during_redo_root) = TRUE; assert(cs_addrs->now_crit == dbg_now_crit); /* ensure crit state remains same AFTER gvcst_root_search */ /* restore global variables now that we are continuing with the original transaction */ RESTORE_ROOTSRCH_ENTRY_STATE; TREF(expand_prev_key) = expand_prev_key; REVERT; } /** * Searches through the database for the root block pointed to by cs_addrs->dir_tree * * @param[in] donot_restart if true, prevents gvcst_root_search from calling t_retry on a failure */ enum cdb_sc gvcst_root_search(boolean_t donot_restart) { srch_blk_status *h0; sm_uc_ptr_t rp; unsigned short rlen, hdr_len; uchar_ptr_t subrec_ptr; enum cdb_sc status; boolean_t gbl_target_was_set, long_blk_id; int4 blk_id_sz; gv_namehead *save_targ; mname_entry *gvent; int altkeylen; int tmp_cmpc; block_id lcl_root; uint4 oldact, newact, oldnct, oldver; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(IS_REG_BG_OR_MM(gv_cur_region)); SET_GV_ALTKEY_TO_GBLNAME_FROM_GV_CURRKEY; /* set up gv_altkey to be just the gblname */ save_targ = gv_target; /* Check if "gv_target->gvname" matches "gv_altkey->base". If not, there is a name mismatch (out-of-design situation). * This check is temporary until we catch the situation that caused D9H02-002641. * It's suspected the original situation has been fixed (see D9I08-002695). But the assertpro will remain until * gvcst_redo_root_search has been well-tested. */ /* --- Check BEGIN --- */ gvent = &save_targ->gvname; altkeylen = gv_altkey->end - 1; assertpro(altkeylen && (altkeylen == gvent->var_name.len) && (0 == memcmp(gv_altkey->base, gvent->var_name.addr, gvent->var_name.len))); /* --- Check END --- */ if (INVALID_GV_TARGET != reset_gv_target) gbl_target_was_set = TRUE; else { gbl_target_was_set = FALSE; reset_gv_target = save_targ; } T_BEGIN_READ_NONTP_OR_TP(ERR_GVGETFAIL); /* We better hold crit in the final retry (TP & non-TP). Only exception is journal recovery */ assert((t_tries < CDB_STAGNATE) || cs_addrs->now_crit || mupip_jnl_recover); for (;;) { /* for provides convenient mechanism to exit if failure */ lcl_root = 0; /* set lcl_root to 0 at the start of every iteration (this way even retry will get fresh value) */ hdr_len = rlen = 0; gv_target = cs_addrs->dir_tree; if (dollar_trestart) gv_target->clue.end = 0; assert(0 == save_targ->root); if (cdb_sc_normal == (status = gvcst_search(gv_altkey, NULL))) { if (gv_altkey->end + 1 == gv_target->hist.h[0].curr_rec.match) { /* End effectively represents the size of the subscript; * curr_rec.match should be that size if we found the key */ h0 = gv_target->hist.h; long_blk_id = IS_64_BLK_ID(h0->buffaddr); blk_id_sz = SIZEOF_BLK_ID(long_blk_id); rp = (h0->buffaddr + h0->curr_rec.offset); hdr_len = SIZEOF(rec_hdr) + gv_altkey->end + 1 - EVAL_CMPC((rec_hdr_ptr_t)rp); GET_USHORT(rlen, rp); if (FALSE == (CHKRECLEN(rp, h0->buffaddr, rlen)) || (rlen < hdr_len + blk_id_sz)) { T_RETRY_AND_CLEANUP(cdb_sc_rmisalign, donot_restart); continue; } READ_BLK_ID(long_blk_id, &lcl_root, rp + hdr_len); if (rlen > (hdr_len + blk_id_sz)) { assert(NULL != global_collation_mstr.addr || 0 == global_collation_mstr.len); if (global_collation_mstr.len < rlen - (hdr_len + blk_id_sz)) { if (NULL != global_collation_mstr.addr) free(global_collation_mstr.addr); global_collation_mstr.len = rlen - (hdr_len + blk_id_sz); global_collation_mstr.addr = (char *)malloc(global_collation_mstr.len); } /* the memcpy needs to be done here instead of out of for loop for * concurrency consideration. We don't use s2pool because the pointer rp is 64 bits */ memcpy(global_collation_mstr.addr, rp + hdr_len + blk_id_sz, rlen - (hdr_len + blk_id_sz)); } if (dollar_tlevel) { status = tp_hist(NULL); if (cdb_sc_normal != status) { T_RETRY_AND_CLEANUP(status, donot_restart); continue; } break; } } if (!dollar_tlevel) { if ((trans_num)0 != t_end(&gv_target->hist, NULL, TN_NOT_SPECIFIED)) break; } else { status = tp_hist(NULL); if (cdb_sc_normal == status) break; T_RETRY_AND_CLEANUP(status, donot_restart); continue; } } else { T_RETRY_AND_CLEANUP(status, donot_restart); continue; } } RESET_GV_TARGET_LCL_AND_CLR_GBL(save_targ, DO_GVT_GVKEY_CHECK); if (lcl_root) { oldact = gv_target->act; oldnct = gv_target->nct; if (rlen > hdr_len + blk_id_sz) { assert((NULL != global_collation_mstr.addr) && (uchar_ptr_t)global_collation_mstr.addr); subrec_ptr = get_spec((uchar_ptr_t)global_collation_mstr.addr, (int)(rlen - (hdr_len + blk_id_sz)), COLL_SPEC); if (subrec_ptr) { gv_target->nct = *(subrec_ptr + COLL_NCT_OFFSET); gv_target->act = *(subrec_ptr + COLL_ACT_OFFSET); gv_target->ver = *(subrec_ptr + COLL_VER_OFFSET); } else { gv_target->nct = 0; gv_target->act = 0; gv_target->ver = 0; } } else if (gv_target->act_specified_in_gld) { /* Global directory specified a collation. Directory tree did not specify any non-zero collation. * So global directory prevails. */ gv_target->nct = 0; /* gv_target->act and gv_target->ver would already have been set in COPY_ACT_FROM_GVNH_REG_TO_GVT macro */ } else { /* Global directory did not specify a collation. In that case, db file header defaults prevail. */ gv_target->nct = 0; gv_target->act = cs_addrs->hdr->def_coll; gv_target->ver = cs_addrs->hdr->def_coll_ver; } /* If DSE, a runtime global directory is created and hence $gtmgbldir is not as effective as it is for GT.M. * Hence exclude DSE from the below errors which are related to $gtmgbldir. */ if (!IS_DSE_IMAGE) { if (gv_target->nct && gv_target->nct_must_be_zero) { /* restore gv_target->act and gv_target->nct */ gv_target->act = oldact; gv_target->nct = oldnct; RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(6) ERR_NCTCOLLSPGBL, 4, DB_LEN_STR(gv_cur_region), gv_target->gvname.var_name.len, gv_target->gvname.var_name.addr); } if (gv_target->act_specified_in_gld && (oldact != gv_target->act)) { newact = gv_target->act; gv_target->act = oldact; gv_target->nct = oldnct; RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(8) ERR_ACTCOLLMISMTCH, 6, gv_target->gvname.var_name.len, gv_target->gvname.var_name.addr, oldact, DB_LEN_STR(gv_cur_region), newact); } } } else if (!gv_target->act_specified_in_gld) { /* If GLD did NOT specify an alternative collation sequence for this global name * but the db file header has a default collation defined, use it. */ gv_target->nct = 0; assert(ACT_NOT_SPECIFIED != cs_addrs->hdr->def_coll); gv_target->act = cs_addrs->hdr->def_coll; gv_target->ver = cs_addrs->hdr->def_coll_ver; } if (gv_target->act) act_in_gvt(gv_target); /* note: this could issue COLLTYPVERSION error */ gv_target->root = lcl_root; /* now that we know the transaction validated fine, set root block in gv_target */ assert(gv_target->act || NULL == gv_target->collseq); return cdb_sc_normal; } fis-gtm-V7.0-005/sr_port/gvcst_rtsib.c0000755000032200000250000001037314342376331016547 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "cdb_sc.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "copy.h" #include "tp.h" /* Include prototypes */ #include "t_qread.h" #include "gvcst_protos.h" /* for gvcst_search_blk,gvcst_rtsib prototype */ /* construct a new array which is the path to the right sibling of the leaf of the old array note: the offset's will be correct, but NOT the 'match' members */ /* WARNING: assumes that the search history for the current target is in gv_target.hist */ GBLREF sgmnt_addrs *cs_addrs; GBLREF gv_namehead *gv_target; GBLREF gv_key *gv_currkey; GBLREF unsigned char rdfail_detail; GBLREF unsigned int t_tries; GBLREF srch_blk_status *first_tp_srch_status; /* overriding value of srch_blk_status given by t_qread in case of TP */ enum cdb_sc gvcst_rtsib(srch_hist *full_hist, int level) { srch_blk_status *old, *new, *old_base, *new_base; rec_hdr_ptr_t rp; enum cdb_sc ret_val; block_id blk; unsigned short rec_size, temp_short; sm_uc_ptr_t buffer_address; int4 blk_id_sz, cycle; boolean_t long_blk_id; new_base = &full_hist->h[level]; old = old_base = &gv_target->hist.h[level]; for (;;) { old++; if (0 == old->blk_num) return cdb_sc_endtree; if (old->cr && (old->blk_num != old->cr->blk)) { assert(CDB_STAGNATE > t_tries); return cdb_sc_lostcr; } if (cdb_sc_normal != (ret_val = gvcst_search_blk(gv_currkey, old))) return ret_val; buffer_address = old->buffaddr; temp_short = old->curr_rec.offset; rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)buffer_address + temp_short); GET_USHORT(rec_size, &rp->rsiz); if ((sm_uc_ptr_t)rp + rec_size > buffer_address + (unsigned int)((blk_hdr_ptr_t)buffer_address)->bsiz) { assert(CDB_STAGNATE > t_tries); return cdb_sc_rmisalign; } if ((unsigned int)((blk_hdr_ptr_t)buffer_address)->bsiz > (temp_short + rec_size)) break; } /* old now points to the first block which did not have a star key pointer*/ new = new_base + (old - old_base + 1); full_hist->depth = (int)(level + old - old_base); (new--)->blk_num = 0; new->tn = old->tn; new->cse = NULL; new->first_tp_srch_status = old->first_tp_srch_status; assert(new->level == old->level); assert(new->blk_target == old->blk_target); new->blk_num = old->blk_num; new->buffaddr = old->buffaddr; new->prev_rec = old->curr_rec; new->cycle = old->cycle; new->cr = old->cr; temp_short = new->prev_rec.offset; rp = (rec_hdr_ptr_t)(temp_short + new->buffaddr); GET_USHORT(rec_size, &rp->rsiz); temp_short += rec_size; new->curr_rec.offset = temp_short; new->curr_rec.match = 0; if (((blk_hdr_ptr_t)old->buffaddr)->bsiz < temp_short) { assert(CDB_STAGNATE > t_tries); return cdb_sc_rmisalign; } rp = (rec_hdr_ptr_t)(old->buffaddr + temp_short); long_blk_id = IS_64_BLK_ID(old->buffaddr); while (--new >= new_base) { --old; GET_USHORT(rec_size, &rp->rsiz); if (((sm_uc_ptr_t)rp + rec_size) > (buffer_address + (unsigned int)((blk_hdr_ptr_t)buffer_address)->bsiz)) { assert(CDB_STAGNATE > t_tries); return cdb_sc_rmisalign; } READ_BLK_ID(long_blk_id, &blk, (sm_uc_ptr_t)rp + rec_size - SIZEOF_BLK_ID(long_blk_id)); new->tn = cs_addrs->ti->curr_tn; new->cse = NULL; if (NULL == (buffer_address = t_qread(blk, &new->cycle, &new->cr))) return((enum cdb_sc)rdfail_detail); long_blk_id = IS_64_BLK_ID(buffer_address); new->first_tp_srch_status = first_tp_srch_status; assert(new->level == old->level); assert(new->blk_target == old->blk_target); new->blk_num = blk; new->buffaddr = buffer_address; new->prev_rec.match = 0; new->prev_rec.offset = 0; new->curr_rec.match = 0; new->curr_rec.offset = SIZEOF(blk_hdr); rp = (rec_hdr_ptr_t)(buffer_address + SIZEOF(blk_hdr)); } return cdb_sc_normal; } fis-gtm-V7.0-005/sr_port/gvcst_search.c0000755000032200000250000005663614342376331016705 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "gdscc.h" #include "copy.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "hashtab_int8.h" #include "tp.h" #include "gvcst_blk_build.h" #include "t_qread.h" #include "longset.h" /* needed for cws_insert.h */ #include "cws_insert.h" #include "gvcst_protos.h" /* for gvcst_search_blk,gvcst_search_tail,gvcst_search prototype */ #include "min_max.h" #include "gvcst_expand_key.h" #include "gvt_inline.h" GBLREF boolean_t mu_reorg_process, mu_reorg_upgrd_dwngrd_in_prog; GBLREF char gvcst_search_clue; GBLREF gd_region *gv_cur_region; GBLREF gv_key *gv_altkey; GBLREF gv_namehead *gv_target; GBLREF sgm_info *sgm_info_ptr; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF srch_blk_status *first_tp_srch_status; /* overriding value of srch_blk_status given by t_qread in case of TP */ GBLREF trans_num local_tn; /* transaction number for THIS PROCESS */ GBLREF uint4 dollar_tlevel; GBLREF unsigned char rdfail_detail; GBLREF unsigned int t_tries; #define SET_GVCST_SEARCH_CLUE(X) gvcst_search_clue = X; /** * Searches for pKey and records the path through the b-tree in pHist * * In some cases, also fills out gv_altkey. It also uses "clues" to optimize, and a large chunk * of the code here is to handle the verification of the clues validity (~400 lines). * * @param[in] pKey The key search the database for * @param[out] pHist History buffer which will contain the list of records leading the pKey; * the first element ([0]) will contain the leaf node if NULL, clue information * is filled out in gv_target */ enum cdb_sc gvcst_search(gv_key *pKey, /* Key to search for */ srch_hist *pHist) /* History to fill in*/ { unsigned char nLevl; enum cdb_sc status; register int n1; register uchar_ptr_t c1, c2; register sm_uc_ptr_t pRec, pBlkBase; register gv_namehead *pTarg; /* Local copy of gv_target; hope it gets put into register */ register srch_blk_status *pCurr; register srch_blk_status *pNonStar; register srch_hist *pTargHist; int tmp_cmpc; block_id nBlkId; cache_rec_ptr_t cr; int cycle; unsigned short n0, nKeyLen; trans_num tn; cw_set_element *cse; off_chain chain1, chain2; srch_blk_status *tp_srch_status, *srch_status, *leaf_blk_hist; boolean_t already_built, is_mm, long_blk_id; ht_ent_int8 *tabent; sm_uc_ptr_t buffaddr; trans_num blkhdrtn, oldest_hist_tn; int hist_size; # ifdef DEBUG boolean_t save_donot_commit; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; pTarg = gv_target; assert(NULL != pTarg); assert(pTarg->root); assert(pKey != (gv_key *)&pTarg->clue); nKeyLen = pKey->end + 1; assert(!dollar_tlevel || ((NULL != sgm_info_ptr) && (cs_addrs->sgm_info_ptr == sgm_info_ptr))); SET_GVCST_SEARCH_CLUE(0); INCR_DB_CSH_COUNTER(cs_addrs, n_gvcst_srches, 1); pTargHist = ((NULL == pHist) ? &pTarg->hist : pHist); /* If FINAL RETRY and TP then we can safely use clues of gv_targets that have been referenced in this * TP transaction (read_local_tn == local_tn). While that is guaranteed to be true for all updates, it * does not hold good for READs since we allow a lot more reads to be done inside a transaction compared * to the # of updates allowed. We allow the same global to be read multiple times inside the same transaction * using different global buffers for each read. This means that we need to validate any clues from the first * read before using it for the second read even if it is in the final retry. This validation is done inside * the below IF block. As for gv_targets which are referenced for the very first time in this TP transaction, * we have no easy way of determining if their clues are still uptodate (i.e. using the clue will guarantee us * no restart) and since we are in the final retry, we dont want to take a risk. So dont use the clue in that case. * * If FINAL RETRY and Non-TP, we will be dealing with only ONE gv_target so its clue would have been reset as * part of the penultimate restart so we dont have any of the above issue in the non-tp case. The only exception * is if we are in gvcst_kill in which case, gvcst_search will be called twice and the clue could be non-zero * for the second invocation. In this case, the clue is guaranteed to be uptodate since it was set just now * as part of the first invocation. So no need to do anything about clue in final retry for Non-TP. */ if ((0 != pTarg->clue.end) && ((CDB_STAGNATE > t_tries) || !dollar_tlevel || (pTarg->read_local_tn == local_tn))) { /* Have non-zero clue. Check if it is usable for the current search key. If so validate clue then and use it. */ /* In t_end, we skipped validating the clue in case of reorg due to the assumption that reorg never uses the clue * i.e. it nullifies the clue before calling gvcst_search. However, it doesn't reset the clue for directory tree * and so continue using the clue if called for root search. Assert accordingly. */ assert(!mu_reorg_process || (pTarg->gd_csa->dir_tree == pTarg)); INCR_DB_CSH_COUNTER(cs_addrs, n_gvcst_srch_clues, 1); status = cdb_sc_normal; /* clue is usable unless proved otherwise */ DEBUG_ONLY(save_donot_commit = TREF(donot_commit);) if (NULL != pHist) { /* Copy the full srch_hist and set loop terminator flag in unused srch_blk_status entry. * If in TP and if leaf block in history has cse, we are guaranteed that it is built by the * immediately previous call to "gvcst_search" (called by gvcst_kill which does two calls to * gvcst_search of which this invocation is the second) so no need to build the block like * is done for the (NULL == pHist) case below. Assert that and some more. */ hist_size = HIST_SIZE(pTarg->hist); memcpy(pHist, &pTarg->hist, hist_size); ((srch_blk_status *)((char *)pHist + hist_size))->blk_num = 0; # ifdef DEBUG if (dollar_tlevel) { leaf_blk_hist = &pHist->h[0]; assert(0 == leaf_blk_hist->level); chain1 = *(off_chain *)&leaf_blk_hist->blk_num; if (chain1.flag == 1) { assert((SIZEOF(int) * 8) >= CW_INDEX_MAX_BITS); assert((int)chain1.cw_index < sgm_info_ptr->cw_set_depth); tp_get_cw(sgm_info_ptr->first_cw_set, (int)chain1.cw_index, &cse); } else { tp_srch_status = leaf_blk_hist->first_tp_srch_status; ASSERT_IS_WITHIN_TP_HIST_ARRAY_BOUNDS(tp_srch_status, sgm_info_ptr); cse = (NULL != tp_srch_status) ? tp_srch_status->cse : NULL; } assert((NULL == cse) || cse->done); } # endif } else if (dollar_tlevel) { /* First nullify first_tp_srch_status member in gv_target history if out-of-date. This is logically done * at tp_clean_up time but delayed until the time this gv_target is used next in a transaction. This way * it saves some CPU cycles. pTarg->read_local_tn tells us whether this is the first usage of this * gv_target in this TP transaction and if so we need to reset the out-of-date field. */ if (pTarg->read_local_tn != local_tn) GVT_CLEAR_FIRST_TP_SRCH_STATUS(pTarg); /* TP & going to use clue. check if clue path contains a leaf block with a corresponding unbuilt * cse from the previous traversal. If so build it first before gvcst_search_blk/gvcst_search_tail. */ tp_srch_status = NULL; leaf_blk_hist = &pTarg->hist.h[0]; assert(leaf_blk_hist->blk_target == pTarg); assert(0 == leaf_blk_hist->level); chain1 = *(off_chain *)&leaf_blk_hist->blk_num; if (chain1.flag == 1) { assert((SIZEOF(int) * 8) >= CW_INDEX_MAX_BITS); if ((int)chain1.cw_index >= sgm_info_ptr->cw_set_depth) { assert(sgm_info_ptr->tp_csa == cs_addrs); assert(FALSE == cs_addrs->now_crit); return cdb_sc_blknumerr; } tp_get_cw(sgm_info_ptr->first_cw_set, (int)chain1.cw_index, &cse); } else { nBlkId = leaf_blk_hist->blk_num; tp_srch_status = leaf_blk_hist->first_tp_srch_status; if ((NULL == tp_srch_status) && (NULL != (tabent = lookup_hashtab_int8(sgm_info_ptr->blks_in_use, (ublock_id *)&leaf_blk_hist->blk_num)))) tp_srch_status = tabent->value; ASSERT_IS_WITHIN_TP_HIST_ARRAY_BOUNDS(tp_srch_status, sgm_info_ptr); cse = (NULL != tp_srch_status) ? tp_srch_status->cse : NULL; } assert(!cse || !cse->high_tlevel); if ((NULL == tp_srch_status) || (tp_srch_status->blk_target == pTarg)) { /* Either the leaf level block in clue is not already present in the current TP transaction's * hashtable OR it is already present and the corresponding globals match. If they dont match * we know for sure the clue is out-of-date (i.e. using it will lead to a transaction restart) * and hence needs to be discarded. */ leaf_blk_hist->first_tp_srch_status = tp_srch_status; if (NULL != cse) { if (!cse->done) { /* there's a private copy and it's not up to date */ already_built = (NULL != cse->new_buff); gvcst_blk_build(cse, cse->new_buff, 0); /* Validate the block's search history right after building a private copy. * This is not needed in case gvcst_search is going to reuse the clue's search * history and return (because tp_hist will do the validation of this block). * But if gvcst_search decides to do a fresh traversal (because the clue does not * cover the path of the current input key etc.) the block build that happened now * will not get validated in tp_hist since it will instead be given the current * key's search history path (a totally new path) for validation. Since a private * copy of the block has been built, tp_tend would also skip validating this block * so it is necessary that we validate the block right here. Since it is tricky to * accurately differentiate between the two cases, we do the validation * unconditionally here (besides it is only a few if checks done per block build * so it is considered okay performance-wise). */ if (!already_built && !chain1.flag) { /* is_mm is calculated twice, but this is done so as to speed up the * most-frequent path, i.e. when there is a clue and either no cse or * cse->done is TRUE */ is_mm = (dba_mm == cs_data->acc_meth); buffaddr = tp_srch_status->buffaddr; cr = tp_srch_status->cr; assert(tp_srch_status && (is_mm || cr) && buffaddr); blkhdrtn = ((blk_hdr_ptr_t)buffaddr)->tn; if (TP_IS_CDB_SC_BLKMOD3(cr, tp_srch_status, blkhdrtn)) { assert(CDB_STAGNATE > t_tries); assert(0 == leaf_blk_hist->level); assert(0 == tp_srch_status->level); TP_TRACE_HIST_MOD(leaf_blk_hist->blk_num, gv_target, tp_blkmod_gvcst_srch, cs_data, tp_srch_status->tn, blkhdrtn, leaf_blk_hist->level); return cdb_sc_blkmod; } if (!is_mm && ((tp_srch_status->cycle != cr->cycle) || (tp_srch_status->blk_num != cr->blk))) { assert(CDB_STAGNATE > t_tries); return cdb_sc_lostcr; } } cse->done = TRUE; leaf_blk_hist->cr = 0; leaf_blk_hist->cycle = CYCLE_PVT_COPY; leaf_blk_hist->buffaddr = cse->new_buff; } else { /* Keep leaf_blk_hist->buffaddr and cse->new_buff in sync if they are not already. * They will be mostly in sync except for two cases. * a) If a TP transaction updates two different globals and the second update * invoked t_qread (invoked from outside gvcst_search) for a leaf block * corresponding to the first global and ended up constructing a private * block. However, the transaction validation done in tp_hist/tp_tend * should detect this and restart. * b) If a gvcst_kill happens in a TP transaction and it does a gvcst_search * call without going through tp_hist (GTM-8120). In this case there is no * restartable situation. * Since it is hard to distinguish (a) from (b), we do not set the donot_commit * variable to indicate this is a restartable situation in case of just (a). */ leaf_blk_hist->buffaddr = cse->new_buff; /* sync the buffers in pro, just in case */ } } } else { /* Two different gv_targets point to same block; discard out-of-date clue. */ # ifdef DEBUG if ((pTarg->read_local_tn >= local_tn) && (NULL != leaf_blk_hist->first_tp_srch_status)) { /* Since the clue was used in *this* transaction, it cannot successfully complete. Set * donot_commit to verify that a restart happens (either in tp_hist or tp_tend) */ assert(pTarg->read_local_tn == local_tn); TREF(donot_commit) |= DONOTCOMMIT_GVCST_SEARCH_BLKTARGET_MISMATCH; } # endif status = cdb_sc_lostcr; } } /* Validate EVERY level in the clue before using it for ALL retries. This way we avoid unnecessary restarts. * This is NECESSARY for the final retry (e.g. in a TP transaction that does LOTS of reads of different globals, * it is possible that one global's clue is invalidated by a later read of another global) and is DESIRABLE (for * performance reasons) in the other tries. The cost of a restart (particularly in TP) is very high that it is * considered okay to take the hit of validating the entire clue before using it even if it is not the final retry. */ if (cdb_sc_normal == status) { is_mm = (dba_mm == cs_data->acc_meth); if (!is_mm) oldest_hist_tn = OLDEST_HIST_TN(cs_addrs); for (srch_status = &pTargHist->h[0]; HIST_TERMINATOR != srch_status->blk_num; srch_status++) { /* Do the actual verification of each history block */ assert((srch_status->level == srch_status - &pTargHist->h[0]) || (mu_reorg_upgrd_dwngrd_in_prog && (DIR_ROOT == srch_status->blk_num))); assert(is_mm || (NULL == srch_status->cr) || (NULL != srch_status->buffaddr)); cr = srch_status->cr; assert(!is_mm || (NULL == cr)); if (TP_IS_CDB_SC_BLKMOD(cr, srch_status)) { status = cdb_sc_blkmod; break; } if (NULL != cr) { assert(NULL != srch_status->buffaddr); if (srch_status->cycle != cr->cycle) { status = cdb_sc_lostcr; break; } if ((CDB_STAGNATE <= t_tries) || mu_reorg_process) { CWS_INSERT(cr->blk); if ((CDB_STAGNATE <= t_tries) && (srch_status->tn <= oldest_hist_tn)) { /* The tn at which the history was last validated is before the earliest * transaction in the BT. The clue can no longer be relied upon. */ status = cdb_sc_losthist; break; } } cr->refer = TRUE; } } } if (cdb_sc_normal == status) { /* Now that we are ready to use the clue, put more-likely case earlier in the if then else sequence. * For sequential reads of globals, we expect the tail of the clue to be much more used than the head. * For random reads, both are equally probable and hence it doesn't matter. * The case (0 == n1) is not expected a lot (relatively) since the application may be able to optimize * a number of reads of the same key into one read by using a local-variable to store the value. */ if (0 < (n1 = memcmp(pKey->base, ((gv_key *)&(pTarg->clue))->base, nKeyLen))) { if (memcmp(pKey->base, pTarg->last_rec->base, nKeyLen) <= 0) { SET_GVCST_SEARCH_CLUE(1); status = gvcst_search_tail(pKey, pTargHist->h, (gv_key *)&pTarg->clue); if (cdb_sc_normal == status) { if (NULL == pHist) { /* Implies the search history is being filled in pTarg->hist so we can * safely update pTarg->clue to reflect the new search key. It is important * that this clue update be done AFTER the gvcst_search_tail invocation * (as that needs to pass the previous clue key). */ COPY_CURR_AND_PREV_KEY_TO_GVTARGET_CLUE(pTarg, pKey, TREF(expand_prev_key)); } INCR_DB_CSH_COUNTER(cs_addrs, n_clue_used_tail, 1); return cdb_sc_normal; } /* Else clue is not usable. Fall through to do full traversal. */ } } else if (0 > n1) { if (memcmp(pKey->base, pTarg->first_rec->base, nKeyLen) >= 0) { SET_GVCST_SEARCH_CLUE(3); status = gvcst_search_blk(pKey, pTargHist->h); if (cdb_sc_normal == status) { if (NULL == pHist) { /* Implies the search history is being filled in pTarg->hist so we can * safely update pTarg->clue to reflect the new search key. It does not * matter if we update the clue BEFORE or AFTER the gvcst_search_blk * invocation but for consistency with the gvcst_search_tail invocation * we keep it AFTER. */ COPY_CURR_AND_PREV_KEY_TO_GVTARGET_CLUE(pTarg, pKey, TREF(expand_prev_key)); } INCR_DB_CSH_COUNTER(cs_addrs, n_clue_used_head, 1); return cdb_sc_normal; } /* Else clue is not usable. Fall through to do full traversal. */ } } else { SET_GVCST_SEARCH_CLUE(2); /* If $zprevious is the current operation, and we have a clue based on a prior search but * dont have a prev_key computed as part of that search (because it was not a $zprevious * operation as well), we cannot use the clue. Instead we need to fall through. */ if (!TREF(expand_prev_key)) { INCR_DB_CSH_COUNTER(cs_addrs, n_clue_used_same, 1); return cdb_sc_normal; } if ((NULL != pTarg->prev_key) && (PREV_KEY_NOT_COMPUTED != pTarg->prev_key->end)) { COPY_KEY(gv_altkey, pTarg->prev_key); INCR_DB_CSH_COUNTER(cs_addrs, n_clue_used_same, 1); return cdb_sc_normal; } leaf_blk_hist = &pTarg->hist.h[0]; status = gvcst_expand_prev_key(leaf_blk_hist, (gv_key *)&pTarg->clue, gv_altkey); if (cdb_sc_normal == status) { COPY_PREV_KEY_TO_GVT_CLUE(pTarg, TRUE); /* gv_altkey already contains the prev_key so no need to do the following. * COPY_KEY(gv_altkey, pTarg->prev_key); */ INCR_DB_CSH_COUNTER(cs_addrs, n_clue_used_same, 1); return cdb_sc_normal; } /* Else clue is not usable. Fall through to do full traversal. */ } } # ifdef DEBUG /* If we are not going to use this clue (i.e. going to read the entire GVT afresh), * restore global variable donot_commit in case it got set to a non-zero value above. * This is needed because we are no longer relying on the out-of-date (and restart causing) clue. */ TREF(donot_commit) = save_donot_commit; # endif } nBlkId = pTarg->root; tn = cs_addrs->ti->curr_tn; if (NULL == (pBlkBase = t_qread(nBlkId, (sm_int_ptr_t)&cycle, &cr))) return (enum cdb_sc)rdfail_detail; long_blk_id = IS_64_BLK_ID(pBlkBase); nLevl = ((blk_hdr_ptr_t)pBlkBase)->levl; if (MAX_BT_DEPTH < (int)nLevl) { assert(CDB_STAGNATE > t_tries); return cdb_sc_maxlvl; } if (0 == (int)nLevl) { assert(CDB_STAGNATE > t_tries); return cdb_sc_badlvl; } is_mm = (dba_mm == cs_data->acc_meth); pTargHist->depth = (int)nLevl; pCurr = &pTargHist->h[nLevl]; (pCurr + 1)->blk_num = 0; pCurr->tn = tn; pCurr->first_tp_srch_status = first_tp_srch_status; pCurr->cycle = cycle; pCurr->cr = cr; pNonStar = NULL; for (;;) { assert(pCurr->level == nLevl); pCurr->cse = NULL; pCurr->blk_num = nBlkId; pCurr->buffaddr = pBlkBase; if (cdb_sc_normal != (status = gvcst_search_blk(pKey, pCurr))) return status; if (0 == nLevl) break; if ((n0 = pCurr->curr_rec.offset) >= ((blk_hdr_ptr_t)pBlkBase)->bsiz) n0 = pCurr->prev_rec.offset; pRec = pBlkBase + n0; GET_USHORT(n0, &((rec_hdr_ptr_t)pRec)->rsiz); if (FALSE == CHKRECLEN(pRec, pBlkBase, n0)) { assert(CDB_STAGNATE > t_tries); return cdb_sc_rmisalign; } READ_BLK_ID(long_blk_id, &nBlkId, pRec + n0 - SIZEOF_BLK_ID(long_blk_id)); if (is_mm) { PUT_BLK_ID(&chain2, nBlkId); if ((0 == chain2.flag) && (nBlkId > cs_addrs->total_blks)) { /* private copy should be taken care of by .flag */ if (cs_addrs->total_blks < cs_addrs->ti->total_blks) return cdb_sc_helpedout; else return cdb_sc_blknumerr; } } if (bstar_rec_size(long_blk_id) != n0) pNonStar = pCurr; pCurr--; pCurr->tn = cs_addrs->ti->curr_tn; if (NULL == (pBlkBase = t_qread(nBlkId, (sm_int_ptr_t)&pCurr->cycle, &pCurr->cr))) return (enum cdb_sc)rdfail_detail; /* Recalculating long_blk_id when a new block is read accounts for a DB with a mix of * V7 and V6 blocks which will be necessary for the V6->V7 upgrade */ long_blk_id = IS_64_BLK_ID(pBlkBase); pCurr->first_tp_srch_status = first_tp_srch_status; if (((blk_hdr_ptr_t)pBlkBase)->levl != --nLevl) { assert(CDB_STAGNATE > t_tries); return cdb_sc_badlvl; } } if (NULL == pHist) { if ((pCurr->curr_rec.offset < SIZEOF(blk_hdr)) || ((pCurr->curr_rec.offset == SIZEOF(blk_hdr)) && (pCurr->curr_rec.match < nKeyLen))) { /* Clue less than first rec, invalidate */ pTarg->clue.end = 0; return cdb_sc_normal; } pRec = pBlkBase + SIZEOF(blk_hdr); GET_USHORT(n0, &((rec_hdr_ptr_t)pRec)->rsiz); if (FALSE == CHKRECLEN(pRec, pBlkBase, n0)) { assert(CDB_STAGNATE > t_tries); return cdb_sc_rmisalign; } c1 = pRec + SIZEOF(rec_hdr); c2 = pTarg->first_rec->base; if (n0 > (pTarg->first_rec->top)) { n0 = pTarg->first_rec->top; status = cdb_sc_keyoflow; } else status = cdb_sc_rmisalign; if (0 != n0) { do { --n0; if ((0 == (*c2++ = *c1++)) && (0 == *c1)) break; } while (n0); } if (0 == n0) { assert(CDB_STAGNATE > t_tries); return status; } assert(c2 < &pTarg->first_rec->base[pTarg->first_rec->top]); /* make sure we don't exceed allocated bounds */ *c2 = *c1; DEBUG_ONLY(pTarg->first_rec->end = c2 - pTarg->first_rec->base;) if (NULL == pNonStar) { *((short *)pTarg->last_rec->base) = GVT_CLUE_LAST_REC_MAXKEY; DEBUG_ONLY(pTarg->last_rec->end = SIZEOF(short);) } else { pRec = pNonStar->buffaddr + pNonStar->curr_rec.offset; GET_USHORT(n0, &((rec_hdr_ptr_t)pRec)->rsiz); c1 = pNonStar->buffaddr; if (FALSE == CHKRECLEN(pRec, c1, n0)) { assert(CDB_STAGNATE > t_tries); return cdb_sc_rmisalign; } EVAL_CMPC2((rec_hdr_ptr_t)pRec, n1); if (pNonStar->curr_rec.match < n1) { assert(CDB_STAGNATE > t_tries); return cdb_sc_rmisalign; } if (n1 > (int)(pTarg->last_rec->top)) { assert(CDB_STAGNATE > t_tries); return cdb_sc_keyoflow; } c2 = pTarg->last_rec->base; if (0 != n1) memcpy(c2, pKey->base, n1); c2 = (sm_uc_ptr_t)c2 + n1; c1 = pRec + SIZEOF(rec_hdr); if ((int)n0 > (int)(pTarg->last_rec->top) - n1) { n0 = pTarg->last_rec->top - n1; status = cdb_sc_keyoflow; } else status = cdb_sc_rmisalign; if (0 != n0) { do { --n0; if ((0 == (*c2++ = *c1++)) && (0 == *c1)) break; } while (n0); } if (0 == n0) { assert(CDB_STAGNATE > t_tries); return status; } assert(c2 < &pTarg->last_rec->base[pTarg->last_rec->top]); /* make sure we don't exceed allocated bounds */ *c2 = *c1; DEBUG_ONLY(pTarg->last_rec->end = c2 - pTarg->last_rec->base;) } COPY_CURR_AND_PREV_KEY_TO_GVTARGET_CLUE(pTarg, pKey, TREF(expand_prev_key)); } return cdb_sc_normal; } fis-gtm-V7.0-005/sr_port/gvcst_tp_init.c0000644000032200000250000001116114342376331017063 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2020 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_string.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 "buddy_list.h" /* needed for tp.h */ #include "hashtab_int8.h" #include "tp.h" #include "tp_timeout.h" #include "gvcst_protos.h" /* for gvcst_tp_init prototype */ /* Initialize the TP structures we will be using for the successive TP operations */ void gvcst_tp_init(gd_region *greg) { sgm_info *si; sgmnt_addrs *csa; csa = (sgmnt_addrs *)&FILE_INFO(greg)->s_addrs; if (NULL == csa->sgm_info_ptr) { si = csa->sgm_info_ptr = (sgm_info *)malloc(SIZEOF(sgm_info)); assert(32768 > SIZEOF(sgm_info)); memset(si, 0, SIZEOF(sgm_info)); si->tp_hist_size = TP_MAX_MM_TRANSIZE; si->cur_tp_hist_size = INIT_CUR_TP_HIST_SIZE; /* should be very much less than si->tp_hist_size */ assert(si->cur_tp_hist_size <= si->tp_hist_size); si->blks_in_use = (hash_table_int8 *)malloc(SIZEOF(hash_table_int8)); init_hashtab_int8(si->blks_in_use, BLKS_IN_USE_INIT_ELEMS, HASHTAB_COMPACT, HASHTAB_SPARE_TABLE); /* See comment in tp.h about cur_tp_hist_size for details */ si->first_tp_hist = si->last_tp_hist = (srch_blk_status *)malloc(SIZEOF(srch_blk_status) * si->cur_tp_hist_size); si->cw_set_list = (buddy_list *)malloc(SIZEOF(buddy_list)); initialize_list(si->cw_set_list, SIZEOF(cw_set_element), CW_SET_LIST_INIT_ALLOC); si->tlvl_cw_set_list = (buddy_list *)malloc(SIZEOF(buddy_list)); initialize_list(si->tlvl_cw_set_list, SIZEOF(cw_set_element), TLVL_CW_SET_LIST_INIT_ALLOC); si->tlvl_info_list = (buddy_list *)malloc(SIZEOF(buddy_list)); initialize_list(si->tlvl_info_list, SIZEOF(tlevel_info), TLVL_INFO_LIST_INIT_ALLOC); si->new_buff_list = (buddy_list *)malloc(SIZEOF(buddy_list)); initialize_list(si->new_buff_list, SIZEOF(que_ent) + csa->hdr->blk_size, NEW_BUFF_LIST_INIT_ALLOC); si->recompute_list = (buddy_list *)malloc(SIZEOF(buddy_list)); initialize_list(si->recompute_list, SIZEOF(key_cum_value), RECOMPUTE_LIST_INIT_ALLOC); /* The size of the si->cr_array can go up to TP_MAX_MM_TRANSIZE, but usually is quite less. * Therefore, initially allocate a small array and expand as needed later. */ if (dba_bg == greg->dyn.addr->acc_meth) { si->cr_array_size = si->cur_tp_hist_size; si->cr_array = (cache_rec_ptr_ptr_t)malloc(SIZEOF(cache_rec_ptr_t) * si->cr_array_size); } else { si->cr_array_size = 0; si->cr_array = NULL; } si->tp_set_sgm_done = FALSE; } else si = csa->sgm_info_ptr; si->gv_cur_region = greg; si->tp_csa = csa; si->tp_csd = csa->hdr; si->start_tn = csa->ti->curr_tn; if (JNL_ALLOWED(csa)) { si->total_jnl_rec_size = csa->min_total_tpjnl_rec_size; /* Reinitialize total_jnl_rec_size */ /* Since the following jnl-mallocs are independent of any dynamically-changeable parameter of the * database, we can as well use the existing malloced jnl structures if at all they exist. */ if (NULL == si->jnl_tail) { si->jnl_tail = &si->jnl_head; si->jnl_list = (buddy_list *)malloc(SIZEOF(buddy_list)); initialize_list(si->jnl_list, SIZEOF(jnl_format_buffer), JNL_LIST_INIT_ALLOC); si->format_buff_list = (buddy_list *)malloc(SIZEOF(buddy_list)); /* Minimum value of elemSize is 8 due to alignment requirements of the returned memory location. * Therefore, we request an elemSize of 8 bytes for the format-buffer and will convert as much * bytes as we need into as many 8-byte multiple segments (see code in jnl_format). */ initialize_list(si->format_buff_list, JFB_ELE_SIZE, DIVIDE_ROUND_UP(JNL_FORMAT_BUFF_INIT_ALLOC, JFB_ELE_SIZE)); ALLOC_JBUF_RSRV_STRUCT(si->jbuf_rsrv_ptr, csa); } } else if (NULL != si->jnl_tail) { /* journaling is currently disallowed although it was allowed (non-zero si->jnl_tail) * during the prior use of this region. Free up unnecessary region-specific structures now. */ FREEUP_BUDDY_LIST(si->jnl_list); FREEUP_BUDDY_LIST(si->format_buff_list); assert(NULL != si->jbuf_rsrv_ptr); FREE_JBUF_RSRV_STRUCT(si->jbuf_rsrv_ptr); si->jnl_tail = NULL; } } fis-gtm-V7.0-005/sr_port/gvcst_zprevious.c0000755000032200000250000001333314342376331017471 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_string.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "cdb_sc.h" #include "filestruct.h" /* needed for jnl.h */ #include "gdscc.h" /* needed for tp.h */ #include "jnl.h" /* needed for tp.h */ #include "gdskill.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ #ifdef UNIX /* needed for frame_pointer in GVCST_ROOT_SEARCH_AND_PREP macro */ # include "repl_msg.h" # include "gtmsource.h" # include "rtnhdr.h" # include "stack_frame.h" #endif #include "t_end.h" /* prototypes */ #include "t_retry.h" #include "t_begin.h" #include "gvcst_protos.h" /* for gvcst_lftsib,gvcst_search,gvcst_search_blk,gvcst_zprevious prototype */ /* needed for spanning nodes */ #include "op.h" #include "op_tcommit.h" #include "error.h" #include "tp_frame.h" #include "tp_restart.h" #include "gtmimagename.h" LITREF mval literal_batch; GBLREF sgmnt_data_ptr_t cs_data; GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF gv_namehead *gv_target; GBLREF gv_key *gv_currkey, *gv_altkey; GBLREF int4 gv_keysize; GBLREF uint4 dollar_tlevel; GBLREF unsigned int t_tries; error_def(ERR_GVORDERFAIL); DEFINE_NSB_CONDITION_HANDLER(gvcst_zprevious_ch) boolean_t gvcst_zprevious(void) { boolean_t found, is_hidden; # ifdef DEBUG CHECK_HIDDEN_SUBSCRIPT(gv_currkey, is_hidden); assert(!is_hidden); # endif found = gvcst_zprevious2(); if (found) { /* If the previous subscript found is a hidden subscript, it is possible there is a null subscript * ("" in standard null collation representation) before this hidden subscript in which case that needs * to be returned. But that will require one more call to "gvcst_zprevious2". But even if the null * subscript does exist and we return "found" as TRUE with the null subscript, the caller (op_zprevious) * is going to return an empty string in both cases (whether or not a null subscript was found behind * the hidden subscript). Therefore, we avoid the second call and return "found" as FALSE in both cases. */ CHECK_HIDDEN_SUBSCRIPT(gv_altkey, is_hidden); if (is_hidden) return FALSE; else return TRUE; } else return FALSE; } boolean_t gvcst_zprevious2(void) { boolean_t found, two_histories; enum cdb_sc status; srch_blk_status *bh; srch_hist *lft_history; unsigned int currkey_prev, currkey_end, altkey_end, prev_rec_match; unsigned char *c, *ctop; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; T_BEGIN_READ_NONTP_OR_TP(ERR_GVORDERFAIL); TREF(expand_prev_key) = TRUE; /* this will cause "gv_altkey" to contain fully expanded previous key */ /* Note that "t_retry" usage below could transfer control out of this function if dollar_tlevel > 0. If so, * we need to remember to reset TREF(expand_prev_key) to FALSE since this zprevious action has terminated. * We do that reset in tp_restart. */ for (;;) { assert(t_tries < CDB_STAGNATE || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ two_histories = FALSE; if (cdb_sc_normal == (status = gvcst_search(gv_currkey, NULL))) /* will set "gv_altkey" to contain previous key */ { found = TRUE; bh = gv_target->hist.h; if (0 == bh->prev_rec.offset) { two_histories = TRUE; lft_history = gv_target->alt_hist; status = gvcst_lftsib(lft_history); if (cdb_sc_normal == status) { bh = lft_history->h; if (cdb_sc_normal != (status = gvcst_search_blk(gv_currkey, bh))) { t_retry(status); continue; } } else if (cdb_sc_endtree == status) { found = FALSE; two_histories = FALSE; /* second history not valid */ } else { t_retry(status); continue; } } assert(gv_altkey->top == gv_currkey->top); assert(gv_altkey->top == gv_keysize); assert(gv_currkey->end < gv_currkey->top); assert(gv_altkey->end < gv_altkey->top); currkey_prev = gv_currkey->prev; currkey_end = gv_currkey->end; altkey_end = gv_altkey->end; prev_rec_match = bh->prev_rec.match; if (((altkey_end < currkey_end) && (altkey_end <= currkey_prev)) || (prev_rec_match < currkey_prev)) found = FALSE; else { /* Truncate gv_altkey to same subscript level/depth as gv_currkey */ c = gv_altkey->base; ctop = c + altkey_end; c += prev_rec_match; for (;;) { if (c >= ctop) { assert(CDB_STAGNATE > t_tries); status = cdb_sc_rmisalign; break; } if (0 == *c++) { *c = 0; break; } } if (cdb_sc_normal != status) { t_retry(status); continue; } gv_altkey->end = c - gv_altkey->base; assert(gv_altkey->end < gv_altkey->top); } if (!dollar_tlevel) { if ((trans_num)0 == t_end(&gv_target->hist, two_histories ? lft_history : NULL, TN_NOT_SPECIFIED)) continue; } else { status = tp_hist(two_histories ? lft_history : NULL); if (cdb_sc_normal != status) { t_retry(status); continue; } } assert(cs_data == cs_addrs->hdr); INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_zprev, 1); TREF(expand_prev_key) = FALSE; return found; } t_retry(status); } } fis-gtm-V7.0-005/sr_port/gvincr_compute_post_incr.c0000755000032200000250000000723714342376331021327 0ustar librarygtc/**************************************************************** * * * Copyright 2004, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "gtm_string.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gtm_facility.h" #include "gdskill.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "jnl.h" #include "copy.h" #include "op.h" /* for op_add prototype */ #define GVINCR_PRE_INCR_MIN_BUFFLEN MAX_NUM_SIZE /* starting size of the malloced buffer to store pre_increment value */ /* since the increment will mostly be a number, start with MAX_NUM_SIZE */ static char *gvincr_pre_incr_buff; /* buffer to hold the pre-$INCR string value before converting to numeric */ static int gvincr_pre_incr_bufflen = 0; /* length of the currently allocated buffer, updated if expansion occurs */ GBLREF mval *post_incr_mval; GBLREF gv_key *gv_currkey; GBLREF mval increment_delta_mval; /* mval holding the increment value, set by op_gvincr */ GBLREF unsigned int t_tries; /* compute post_incr_mval from the current value of gv_currkey that was just now searched down the tree */ enum cdb_sc gvincr_compute_post_incr(srch_blk_status *bh) { int4 cur_blk_size; sm_uc_ptr_t buffaddr; rec_hdr_ptr_t rp; unsigned short rec_size; int4 target_key_size, data_len; uint4 gvincr_malloc_len; mval pre_incr_mval; int tmp_cmpc; buffaddr = bh->buffaddr; cur_blk_size = ((blk_hdr_ptr_t)buffaddr)->bsiz; rp = (rec_hdr_ptr_t)(buffaddr + bh->curr_rec.offset); GET_USHORT(rec_size, &rp->rsiz); target_key_size = bh->curr_rec.match; assert(target_key_size == gv_currkey->end + 1); data_len = rec_size + EVAL_CMPC(rp) - SIZEOF(rec_hdr) - target_key_size; if ((0 > data_len) || (((sm_uc_ptr_t)rp + rec_size) > ((sm_uc_ptr_t)buffaddr + cur_blk_size))) { assert(CDB_STAGNATE > t_tries); return cdb_sc_rmisalign; } if (data_len > gvincr_pre_incr_bufflen) { if (NULL != gvincr_pre_incr_buff) free(gvincr_pre_incr_buff); gvincr_malloc_len = (data_len > GVINCR_PRE_INCR_MIN_BUFFLEN) ? data_len : GVINCR_PRE_INCR_MIN_BUFFLEN; gvincr_pre_incr_buff = (char *)malloc(gvincr_malloc_len); gvincr_pre_incr_bufflen = gvincr_malloc_len; } /* malloced buffer is used for pre_incr_mval instead of stringpool because this is memory that is * inherently used only by $INCREMENT and is needed only during the lifetime of the increment. * keeping it in the stringpool causes it to stay until the next garbage collection which adds * to unnecessary overheads. */ pre_incr_mval.mvtype = MV_STR; pre_incr_mval.str.addr = (char *)gvincr_pre_incr_buff; pre_incr_mval.str.len = data_len; memcpy(pre_incr_mval.str.addr, (sm_uc_ptr_t)rp + rec_size - data_len, data_len); op_add(&pre_incr_mval, &increment_delta_mval, post_incr_mval); assert(MV_IS_NUMERIC(post_incr_mval)); /* "post_incr_mval" is of numeric type, convert it to a string type so it can be used by the caller to set "value" */ MV_FORCE_STR(post_incr_mval); /* will use stringpool to store string representation */ /* "post_incr_mval" is a copy of the mval pointer passed to "op_gvincr" and hence is on the M-stack * and therefore is known to the garbage collector (stp_gcol). hence it is ok for it to use the stringpool */ return cdb_sc_normal; } fis-gtm-V7.0-005/sr_port/gvincr_recompute_upd_array.c0000755000032200000250000002354114342376331021640 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2004-2016 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_string.h" #include "cdb_sc.h" #include "gdsroot.h" #include "gtm_facility.h" #include "gdskill.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "min_max.h" /* needed for gdsblkops.h */ #include "gdsblkops.h" #include "jnl.h" #include "copy.h" #include "gvcst_protos.h" /* for gvcst_search_blk prototypes */ #include "op.h" /* for add_mvals prototype */ #include "jnl_get_checksum.h" GBLREF uint4 dollar_tlevel; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF mval *post_incr_mval; /* mval pointing to the post-$INCR value */ GBLREF jnl_format_buffer *non_tp_jfb_ptr; GBLREF jnl_gbls_t jgbl; GBLREF char *update_array, *update_array_ptr; GBLREF int gv_fillfactor, rc_set_fragment; /* Contains offset within data at which data fragment starts */ GBLREF unsigned char cw_set_depth; GBLREF gv_key *gv_currkey; GBLREF unsigned int t_tries; GBLREF uint4 update_array_size; GBLREF gv_namehead *gv_target; /* -------------------------------------------------------------------------------------------- * This code is very similar to the code in gvcst_put for the non-block-split case as well as * the code in recompute_upd_array in tp_tend.c. All of these need to be maintained in sync. * -------------------------------------------------------------------------------------------- */ enum cdb_sc gvincr_recompute_upd_array(srch_blk_status *bh, struct cw_set_element_struct *cse, cache_rec_ptr_t cr) { blk_segment *bs1, *bs_ptr; char *va; enum cdb_sc status; int4 blk_size, blk_fill_size, cur_blk_size, blk_seg_cnt, delta, tail_len, new_rec_size; int4 target_key_size, data_len; int tmp_cmpc; mstr value; rec_hdr_ptr_t curr_rec_hdr, rp; sm_uc_ptr_t cp1, buffaddr; unsigned short rec_size; unsigned int bsiz; jnl_format_buffer *jfb; blk_hdr_ptr_t old_block; sgmnt_addrs *csa; csa = cs_addrs; assert(!dollar_tlevel); /* this recomputation is currently supported only for non-TP */ /* To support this for TP would require addressing a lot more issues. Examples are * a) Currently we format jnl records only for explicit updates and not for implicit updates (updates in trigger code). * All such triggers updates currently happen inside of a TP (even if the explicit update is non-TP, there * is an implicit TP wrapper). Therefore we need to record more information as to whether this update * to the database needs a corresponding format of the logical journal record or not. */ assert(0 == cse->level); /* better be a leaf-level block */ assert(csa->now_crit); assert(!cse->level && (gds_t_write == cse->mode) && (NULL == cse->new_buff) && (GDS_WRITE_PLAIN == cse->write_type)); blk_size = cs_data->blk_size; /* "blk_size" is also used by the BLK_FINI macro below */ blk_fill_size = (blk_size * gv_fillfactor) / 100 - cs_data->reserved_bytes; /* clues for gv_target involved in recomputation need not be nullified since only the value changes (not the key) */ assert(CR_NOTVALID != (sm_long_t)cr); if (NULL == cr || CR_NOTVALID == (sm_long_t)cr || (0 <= cr->read_in_progress)) { assert(CDB_STAGNATE > t_tries); return cdb_sc_lostcr; } if (cr->in_tend) { /* Possible if this cache-record is being modified concurrently by another process in bg_update_phase2. * Normally t_qread would have waited for this to complete before returning. But it is possible in some * cases to bypass t_qread (e.g. gv_target->clue.end is non-zero). In this case we have two options. * a) Signal a restart. This will cause clue.end to get reset to 0 and will now go through t_qread. * b) Wait for in_tend to become non-zero and then proceed. This will save a restart. * Since we are not in TP the overhead of restarting is not that bad. * Since we hold crit at this point, we decide not to wait. We choose (a). */ assert(CDB_STAGNATE > t_tries); return cdb_sc_blkmod; } buffaddr = bh->buffaddr; target_key_size = gv_currkey->end + 1; if (cdb_sc_normal != (status = gvcst_search_blk(gv_currkey, bh))) { assert(CDB_STAGNATE > t_tries); return status; } if (target_key_size != bh->curr_rec.match) /* key does not exist, nothing doable here, restart transaction */ { assert(CDB_STAGNATE > t_tries); return cdb_sc_blkmod; } cur_blk_size = ((blk_hdr_ptr_t)buffaddr)->bsiz; rp = (rec_hdr_ptr_t)(buffaddr + bh->curr_rec.offset); GET_USHORT(rec_size, &rp->rsiz); data_len = rec_size + EVAL_CMPC(rp) - SIZEOF(rec_hdr) - target_key_size; if (cdb_sc_normal != (status = gvincr_compute_post_incr(bh))) { assert(CDB_STAGNATE > t_tries); return status; } assert(MV_IS_STRING(post_incr_mval)); /* gvincr_recompute_post_incr should have set it to be a of type MV_STR */ value = post_incr_mval->str; new_rec_size = rec_size - data_len + value.len; delta = new_rec_size - rec_size; if ((cur_blk_size + delta) > blk_fill_size) { assert(CDB_STAGNATE > t_tries); return cdb_sc_blksplit; } if (0 != rc_set_fragment) { assert(CDB_STAGNATE > t_tries); return cdb_sc_mkblk; /* let gvcst_put do the recomputation out of crit in case of rc_set */ } /* Note that a lot of the code below relies on the fact that we are in non-TP. For TP we need to do extra stuff */ assert(NULL != update_array); assert(NULL != update_array_ptr); assert(0 != update_array_size); assert(update_array + update_array_size >= update_array_ptr); assert(1 == cw_set_depth); /* since cw_set_depth is guaranteed to be 1 (by the above assert), we can be sure that the only update array space we would * have used is for the current (and only) cw_set_element "cse" and hence can reuse the space by resetting update_array_ptr */ assert(ROUND_UP2((INTPTR_T)update_array, UPDATE_ELEMENT_ALIGN_SIZE) == (INTPTR_T)cse->upd_addr); RESET_UPDATE_ARRAY; /* do not use CHECK_AND_RESET_UPDATE_ARRAY since we are knowingly resetting an active update array */ BLK_INIT(bs_ptr, bs1); BLK_SEG(bs_ptr, buffaddr + SIZEOF(blk_hdr), bh->curr_rec.offset - SIZEOF(blk_hdr)); BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); curr_rec_hdr->rsiz = new_rec_size; SET_CMPC(curr_rec_hdr, bh->prev_rec.match); BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); BLK_ADDR(cp1, target_key_size - bh->prev_rec.match, unsigned char); memcpy(cp1, gv_currkey->base + bh->prev_rec.match, target_key_size - bh->prev_rec.match); BLK_SEG(bs_ptr, cp1, target_key_size - bh->prev_rec.match); assert(0 != value.len); BLK_ADDR(va, value.len, char); memcpy(va, value.addr, value.len); BLK_SEG(bs_ptr, (unsigned char *)va, value.len); rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + rec_size); tail_len = (int4)(cur_blk_size - ((sm_uc_ptr_t)rp - buffaddr)); assert(tail_len >= 0); /* else gvincr_recompute_post_incr would have returned cdb_sc_rmisalign and we will not be here */ if (tail_len > 0) { BLK_SEG(bs_ptr, (sm_uc_ptr_t)rp, tail_len); } if (0 == BLK_FINI(bs_ptr, bs1)) { assert(CDB_STAGNATE > t_tries); return cdb_sc_mkblk; } cse->upd_addr = (unsigned char *)bs1; /* assert that cse->old_block is indeed pointing to the buffer that the cache-record is pointing to. * this is necessary to ensure that we are copying "ondsk_blkver" from the correct cache-record. * there is a possibility that this assert might not hold true which is if we are in a restartable situation. * but in that case do the same check that t_end will perform to determine this. */ assert((cse->old_block == (sm_uc_ptr_t)GDS_REL2ABS(cr->buffaddr)) || (bh->cycle != cr->cycle) || (bh->cr != cr)); cse->ondsk_blkver = cr->ondsk_blkver; cse->done = FALSE; /* Reformat the logical SET jnl-record if we need to write logical records. But recompute checksums for PBLK record * ONLY IF journaling is enabled. Do not need to do this in the case REPL_WAS_ENABLED(csa) is TRUE as replication * only cares about logical records. Hence the separation of the code below into two "if" blocks. */ if (JNL_WRITE_LOGICAL_RECS(csa)) jfb = jnl_format(JNL_SET, gv_currkey, post_incr_mval, 0); /* Re-format the logical SET jnl-record */ if (JNL_ENABLED(csa)) { /* Recompute checksums in case necessary */ if (csa->jnl_before_image && (NULL != cse->old_block)) { old_block = (blk_hdr_ptr_t)cse->old_block; if (old_block->tn < csa->jnl->jnl_buff->epoch_tn) { bsiz = old_block->bsiz; /* Note that at this point, validation is still not complete (in caller "t_end"). So it is * possible we are looking at a block with garbage block-header (possible for example if this * is a block marked FREE in the bitmap but was read from disk due to concurrency conflicts * and was all zeroes but because this db is encrypted, the 0-block was decrypted before being * placed in the global buffer which made the block-header contain garbage. So need edit-checks * on "bsiz" field in block header. */ if (bsiz > csa->hdr->blk_size) { /* This is a restartable condition. Restart */ assert(CDB_STAGNATE > t_tries); return cdb_sc_mkblk; } cse->blk_checksum = jnl_get_checksum(old_block, csa, bsiz); } else cse->blk_checksum = 0; } } assert(NULL != gv_target); /* If clue is known to be non-zero, we have the potential for the first_rec part of it to be unreliable. * Reset it to be safe. See comment in similar section in tp_hist for details on why. */ if (gv_target->clue.end) GVT_CLUE_INVALIDATE_FIRST_REC(gv_target); return cdb_sc_normal; } fis-gtm-V7.0-005/sr_port/gvinit.c0000755000032200000250000000245314342376331015516 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "dpgbldir.h" #include "gvt_inline.h" GBLREF gd_addr *gd_header; void gvinit(void) { mval v; /* if gd_header is null then get the current one */ if (!gd_header) { v.mvtype = MV_STR; v.str.len = 0; gd_header = zgbldir(&v); } /* May get in here after an extended ref call OR in mupip journal recover forward processing (with * function call graph "mur_output_record/gvcst_put/gvtr_init/gvtr_db_tpwrap/op_tstart"). * In either case it is possible that gv_currkey has already been set up, so dont lose any preexisting keys. */ GVKEYSIZE_INIT_IF_NEEDED; /* sets "gv_keysize", "gv_currkey" and "gv_altkey" (if not already done) */ } fis-gtm-V7.0-005/sr_port/gvn.c0000755000032200000250000001243014342376331015004 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "compiler.h" #include "opcode.h" #include "toktyp.h" #include "mdq.h" #include "advancewindow.h" #include "fullbool.h" #include "show_source_line.h" #include "hashtab_mname.h" GBLREF boolean_t run_time; error_def(ERR_EXPR); error_def(ERR_EXTGBLDEL); error_def(ERR_GBLNAME); error_def(ERR_GVNAKEDEXTNM); error_def(ERR_MAXNRSUBSCRIPTS); error_def(ERR_RPARENMISSING); error_def(ERR_SIDEEFFECTEVAL); int gvn(void) { boolean_t shifting, vbar; char x; int hash_code; opctype ox; oprtype *sb1, *sb2, subscripts[MAX_GVSUBSCRIPTS + 1]; triple *oldchain, *ref, *s, tmpchain, *triptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(TK_CIRCUMFLEX == TREF(window_token)); advancewindow(); sb1 = sb2 = subscripts; ox = 0; if (shifting = (TREF(shift_side_effects) && (!TREF(saw_side_effect) || (GTM_BOOL == TREF(gtm_fullbool) && (OLD_SE == TREF(side_effect_handling)))))) { /* NOTE assignment above */ dqinit(&tmpchain, exorder); oldchain = setcurtchain(&tmpchain); } if ((TK_LBRACKET == TREF(window_token)) || (TK_VBAR == TREF(window_token))) { assert(sb2 == sb1); /* set "hash_code" as the first operand so OC_GVEXTNAM has it passed in at same spot as op_gvname */ sb1++; vbar = (TK_VBAR == TREF(window_token)); advancewindow(); if (EXPR_FAIL == (vbar ? expr(sb1++, MUMPS_EXPR) : expratom(sb1))) { stx_error(ERR_EXPR); if (shifting) setcurtchain(oldchain); return FALSE; } if (!vbar) { /* DE257948 - we need to force ex_tail() in a case we have an additional contain/square-bracket. * Correct the tree and sb1->opcode.tref and prevent an assert failure in emit_code.c */ coerce(sb1, OCT_MVAL); ex_tail(sb1++); } if (TK_COMMA == TREF(window_token)) { advancewindow(); if (EXPR_FAIL == (vbar ? expr(sb1++, MUMPS_EXPR) : expratom(sb1))) { stx_error(ERR_EXPR); if (shifting) setcurtchain(oldchain); return FALSE; } if (!vbar) { coerce(sb1, OCT_MVAL); ex_tail(sb1++); } } else *sb1++ = put_str(0,0); if ((!vbar && (TK_RBRACKET != TREF(window_token))) || (vbar && (TK_VBAR != TREF(window_token)))) { stx_error(ERR_EXTGBLDEL); if (shifting) setcurtchain(oldchain); return FALSE; } advancewindow(); ox = OC_GVEXTNAM; } if (TK_IDENT == TREF(window_token)) { COMPUTE_HASH_MSTR((TREF(window_ident)), hash_code); if (!ox) { ox = OC_GVNAME; *sb1++ = put_ilit((mint)hash_code); } else *sb2 = put_ilit((mint)hash_code); /* fill in hash_code in the space previously set aside */ *sb1++ = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); advancewindow(); } else { if (ox) { stx_error(ERR_GVNAKEDEXTNM); if (shifting) setcurtchain(oldchain); return FALSE; } if (TK_LPAREN != TREF(window_token)) { stx_error(ERR_GBLNAME); if (shifting) setcurtchain(oldchain); return FALSE; } ox = OC_GVNAKED; /* pass in a dummy hash_code in case of OC_GVNAKED. We need this so op_gvname_fast, op_gvextnam_fast and * op_gvnaked_fast have the same call interface. op_savgvn.c relies on this to replace OC_GVNAME, OC_GVEXTNAM * or OC_GVNAKED opcodes with a OC_SAVGVN opcode. */ *sb1++ = put_ilit((mint)0); } if (TK_LPAREN == TREF(window_token)) { for (;;) { if (sb1 >= ARRAYTOP(subscripts)) { stx_error(ERR_MAXNRSUBSCRIPTS); if (shifting) setcurtchain(oldchain); return FALSE; } advancewindow(); if (EXPR_FAIL == expr(sb1, MUMPS_EXPR)) { if (shifting) setcurtchain(oldchain); return FALSE; } assert(TRIP_REF == sb1->oprclass); s = sb1->oprval.tref; if (OC_LIT == s->opcode) *sb1 = make_gvsubsc(&s->operand[0].oprval.mlit->v); sb1++; if (TK_RPAREN == (x = TREF(window_token))) /* NOTE assignment */ { advancewindow(); break; } if (TK_COMMA != x) { stx_error(ERR_RPARENMISSING); if (shifting) setcurtchain(oldchain); return FALSE; } } } ref = newtriple(ox); ref->operand[0] = put_ilit((mint)(sb1 - sb2)); SUBS_ARRAY_2_TRIPLES(ref, sb1, sb2, subscripts, 0); if (shifting) { if (TREF(saw_side_effect) && ((GTM_BOOL != TREF(gtm_fullbool)) || (OLD_SE != TREF(side_effect_handling)))) { /* saw a side effect in a subscript - our reference has been superceded so no targ game on the name */ setcurtchain(oldchain); triptr = (TREF(curtchain))->exorder.bl; dqadd(triptr, &tmpchain, exorder); } else { newtriple(OC_GVSAVTARG); setcurtchain(oldchain); assert(NULL != TREF(expr_start)); assert(&tmpchain != tmpchain.exorder.bl); dqadd(TREF(expr_start), &tmpchain, exorder); TREF(expr_start) = tmpchain.exorder.bl; assert(OC_GVSAVTARG == (TREF(expr_start))->opcode); triptr = newtriple(OC_GVRECTARG); triptr->operand[0] = put_tref(TREF(expr_start)); } } return TRUE; } fis-gtm-V7.0-005/sr_port/gvn2gds.c0000644000032200000250000002671414342376331015573 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 #include "gtm_string.h" #include "gtm_stdio.h" #include "gtmio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "gdsbt.h" #include "gdsfhead.h" #include "stringpool.h" #include "collseq.h" #include "error.h" #include "op.h" #include "patcode.h" #include "mvalconv.h" #include "lv_val.h" #include "alias.h" #include "gtmimagename.h" #include "format_targ_key.h" #include "gtm_ctype.h" /* for ISDIGIT_ASCII macro */ #include "gvn2gds.h" #include "is_canonic_name.h" #include "zshow.h" #include "io.h" #include "gvt_inline.h" GBLREF gd_region *gv_cur_region; GBLREF gv_namehead *gv_target; GBLREF gv_namehead *reset_gv_target; error_def(ERR_COLLATIONUNDEF); error_def(ERR_NOCANONICNAME); STATICDEF boolean_t save_transform; STATICDEF gd_region *save_gv_cur_region; /* Restore global variables "gv_cur_region", "gv_target" and "transform" */ #define RESTORE_GBL_VARS_BEFORE_FUN_RETURN \ MBSTART { \ /* Restore global variables "gv_cur_region", "gv_target" and "transform" back to their original state */ \ gv_cur_region = save_gv_cur_region; \ RESET_GV_TARGET(DO_GVT_GVKEY_CHECK); \ TREF(transform) = save_transform; \ } MBEND CONDITION_HANDLER(gvn2gds_ch) { START_CH(TRUE); RESTORE_GBL_VARS_BEFORE_FUN_RETURN; NEXTCH; } #define MAX_LEN_FOR_CHAR_FUNC 6 boolean_t convert_key_to_db(mval *gvn, int start, int stop, gv_key *gvkey, unsigned char **key); /* * ----------------------------------------------- * gvn2gds() * Converts a global variable name (GVN) into its internal database repesentation (GDS) * * Arguments: * gvn - Pointer to Source Name string mval. Must be in GVN form. * buf - Pointer to a buffer large enough to fit the whole GDS of the passed in GVN. * col - Collation number. * Return: * unsigned char - Pointer to the end of the GDS written to gvkey. * ----------------------------------------------- */ unsigned char *gvn2gds(mval *gvn, gv_key *gvkey, int act) { boolean_t est_first_pass, retn; collseq *csp; gd_region tmpreg; gv_namehead temp_gv_target; unsigned char *key, *key_top, *key_start; int subscript, i, contains_env; int *start, *stop; gv_name_and_subscripts start_buff, stop_buff; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* determine which buffer to use */ DETERMINE_BUFFER(gvn, start_buff, stop_buff, start, stop); if (0 != act) { csp = ready_collseq(act); if (NULL == csp) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_COLLATIONUNDEF, 1, act); } else csp = NULL; /* Do not issue COLLATIONUNDEF for 0 collation */ retn = TRUE; assert(MV_IS_STRING(gvn)); key_start = &gvkey->base[0]; key = key_start; gvkey->prev = 0; gvkey->top = DBKEYSIZE(MAX_KEY_SZ); key_top = key_start + gvkey->top; /* We will parse all of the components up front. */ if (!parse_gv_name_and_subscripts(gvn, &subscript, start, stop, &contains_env)) NOCANONICNAME_ERROR(gvn); if (stop[contains_env] - start[contains_env] > gvkey->top) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_GVSUBOFLOW); memcpy(key, gvn->str.addr + start[contains_env], stop[contains_env] - start[contains_env]); key += stop[contains_env] - start[contains_env]; *key++ = KEY_DELIMITER; gvkey->end = key - key_start; /* Temporarily repoint global variables "gv_cur_region", "gv_target" and "transform". * They are needed by mval2subsc for the following * "transform", "gv_target->nct", "gv_target->collseq" and "gv_cur_region->std_null_coll" * Note that transform is usually ON, specifying that collation transformation is "enabled", * and is only shut off for minor periods when something is being critically formatted (like * we're doing here). Note that mval2subsc could issue an rts_error, so we establish a * condition handler to restore the above. */ save_transform = TREF(transform); assert(save_transform); TREF(transform) = TRUE; reset_gv_target = gv_target; gv_target = &temp_gv_target; memset(gv_target, 0, SIZEOF(gv_namehead)); gv_target->collseq = csp; memset(&tmpreg, 0, SIZEOF(gd_region)); /* Assign "gv_cur_region" only after tmpreg has been fully initialized or timer interrupts can look at inconsistent copy */ save_gv_cur_region = gv_cur_region; gv_cur_region = &tmpreg; gv_cur_region->std_null_coll = TRUE; ESTABLISH_NORET(gvn2gds_ch, est_first_pass); /* we know the number of subscripts, so we convert them all */ for (i = 1 + contains_env; i <= contains_env + subscript; ++i) { if (!(retn = convert_key_to_db(gvn, start[i], stop[i], gvkey, &key))) break; } REVERT; RESTORE_GBL_VARS_BEFORE_FUN_RETURN; if (!retn || !CAN_APPEND_HIDDEN_SUBS(gvkey)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_GVSUBOFLOW); *key++ = KEY_DELIMITER; /* add double terminating null byte */ assert(key <= key_top); return key; } /* given the bounds of a particular subscript (assumed correct), we convert the subscript into * a form that mimics the GDS representation of that subscript */ boolean_t convert_key_to_db(mval *gvn, int start, int stop, gv_key *gvkey, unsigned char **key) { mval tmpval, *mvptr, dollarcharmval; int isrc; char strbuff[MAX_KEY_SZ + 1], *str, *str_top; char fnname[MAX_LEN_FOR_CHAR_FUNC], *c; boolean_t is_zchar; int4 num; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (ISDIGIT_ASCII(gvn->str.addr[start]) || '-' == gvn->str.addr[start] || '+' == gvn->str.addr[start] || '.' == gvn->str.addr[start]) { /* convert a number */ tmpval.str.addr = &gvn->str.addr[start]; tmpval.str.len = stop - start; tmpval.mvtype = MV_STR; mvptr = &tmpval; MV_FORCE_NUM(mvptr); if (MVTYPE_IS_NUM_APPROX(tmpval.mvtype)) return FALSE; mval2subsc(&tmpval, gvkey, gv_cur_region->std_null_coll); } else { /* It's a string. We need to accept strings, $CHAR args, and $ZCHAR args. */ str = &strbuff[0]; str_top = &strbuff[0] + MAX_KEY_SZ + 1; /* MV_NUM_APPROX needed by mval2subsc to skip val_iscan call */ tmpval.mvtype = (MV_STR | MV_NUM_APPROX); for (isrc = start; isrc < stop; ) { if ('_' == gvn->str.addr[isrc]) { /* We can skip this case, since we're already "appending" * the strings on the lhs to the string on the rhs. */ isrc++; } else if ('$' == gvn->str.addr[isrc]) { /* We determine if what comes after is a Char or a ZCHar, * and copy over accordingly */ c = &fnname[0]; isrc++; /* skip the '$' */ while ('(' != gvn->str.addr[isrc]) *c++ = TOUPPER(gvn->str.addr[isrc++]); *c = '\0'; assert(strlen(c) <= MAX_LEN_FOR_CHAR_FUNC - 1); if (!MEMCMP_LIT(fnname, "ZCHAR") || !MEMCMP_LIT(fnname, "ZCH")) is_zchar = TRUE; else if (!MEMCMP_LIT(fnname, "CHAR") || !MEMCMP_LIT(fnname, "C")) is_zchar = FALSE; else assert(FALSE); /* Parse the arguments */ isrc++; /* skip the '(' */ while (TRUE) { /* Inside the argument list for $[Z]CHAR */ /* STRTOUL will stop at the ',' or ')' */ num = (int4)STRTOUL(&gvn->str.addr[isrc], NULL, 10); # ifdef UTF8_SUPPORTED if (!is_zchar && is_gtm_chset_utf8) op_fnchar(2, &dollarcharmval, num); else # endif op_fnzchar(2, &dollarcharmval, num); assert(MV_IS_STRING(&dollarcharmval)); if (dollarcharmval.str.len) { if (str + dollarcharmval.str.len > str_top) /* String overflows capacity. */ return FALSE; memcpy(str, dollarcharmval.str.addr, dollarcharmval.str.len); str += dollarcharmval.str.len; } /* move on to the next argument */ while (',' != gvn->str.addr[isrc] && ')' != gvn->str.addr[isrc]) isrc++; if (',' == gvn->str.addr[isrc]) isrc++; else { assert(')' == gvn->str.addr[isrc]); isrc++; /* skip ')' */ break; } } } else if ('"' == gvn->str.addr[isrc]) { /* Assume valid string. */ isrc++; while (isrc < stop && !('"' == gvn->str.addr[isrc] && '"' != gvn->str.addr[isrc+1])) { if (str == str_top) /* String overflows capacity. */ return FALSE; if ('"' == gvn->str.addr[isrc] && '"' == gvn->str.addr[isrc+1]) { *str++ = '"'; isrc += 2; } else *str++ = gvn->str.addr[isrc++]; } isrc++; /* skip over '"' */ } else assert(FALSE); } tmpval.str.addr = strbuff; tmpval.str.len = str - strbuff; DEBUG_ONLY(TREF(skip_mv_num_approx_assert) = TRUE;) mval2subsc(&tmpval, gvkey, gv_cur_region->std_null_coll); DEBUG_ONLY(TREF(skip_mv_num_approx_assert) = FALSE;) } assert(gvkey->end < gvkey->top); /* else GVSUBOFLOW error would have been issued */ *key = &gvkey->base[gvkey->end]; return TRUE; } /* * ----------------------------------------------- * gds2gvn() * Converts a key in internal database representation form to a global variable name (GVN). * * Arguments: * gds - Pointer to Source Name string mval. Must be in GDS form. * buf - Pointer to a buffer large enough to fit the whole GVN of the passed in GDS. * col - Collation number. * Return: * unsigned char - Pointer to the end of the GVN written to buff. * ----------------------------------------------- */ unsigned char *gds2gvn(mval *gds, unsigned char *buff, int col) { collseq *csp; unsigned char *key; gv_key_buf save_currkey; gv_key *gvkey; gd_region tmpreg, *save_gv_cur_region; gv_namehead temp_gv_target; boolean_t est_first_pass; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; key = &buff[0]; if (0 != col) { csp = ready_collseq(col); if (NULL == csp) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_COLLATIONUNDEF, 1, col); } else csp = NULL; /* Do not issue COLLATIONUNDEF for 0 collation */ /* Temporarily repoint global variables "gv_target" and "transform". * They are needed by format_targ_key/gvsub2str "transform" and "gv_target->collseq". * Note that transform is usually ON, specifying that collation transformation is "enabled", * and is only shut off for minor periods when something is being critically formatted (like * we're doing here). While there should be no need for a condition handler, there is a * a possible rts_error from gvsub2str in format_target_key, so we establish one. */ save_transform = TREF(transform); assert(save_transform); TREF(transform) = TRUE; reset_gv_target = gv_target; gv_target = &temp_gv_target; memset(gv_target, 0, SIZEOF(gv_namehead)); gv_target->collseq = csp; assert(MV_IS_STRING(gds)); gvkey = (gv_key *)&save_currkey.key; gvkey->prev = 0; gvkey->top = DBKEYSIZE(MAX_KEY_SZ); if ((gvkey->top < gds->str.len) || (2 > gds->str.len) || (KEY_DELIMITER != gds->str.addr[gds->str.len-1]) || (KEY_DELIMITER != gds->str.addr[gds->str.len-2])) *key++ = '\0'; else { memcpy(gvkey->base, gds->str.addr, gds->str.len); gvkey->end = gds->str.len - 1; ESTABLISH_NORET(gvn2gds_ch, est_first_pass); /* format_targ_key calls gvsub2str which has an rts_error */ if (0 == (key = format_targ_key(&buff[0], MAX_ZWR_KEY_SZ, gvkey, FALSE))) key = &buff[MAX_ZWR_KEY_SZ - 1]; REVERT; } /* Restore global variables "gv_target" and "transform" back to their original state */ RESET_GV_TARGET(DO_GVT_GVKEY_CHECK); TREF(transform) = save_transform; return key; } fis-gtm-V7.0-005/sr_port/gvn2gds.h0000644000032200000250000000222214342376331015564 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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. * * * ****************************************************************/ #ifndef GVN2GDS_included #define GVN2GDS_included #include "mdef.h" #include #include "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" unsigned char *gvn2gds(mval *gvn, gv_key *gvkey, int act); /* Assumes that buff is of size MAX_ZWR_KEY_SZ. Reverses the operation done by gvn2gds */ unsigned char *gds2gvn(mval *gds, unsigned char *buff, int col); #endif fis-gtm-V7.0-005/sr_port/gvname_env_restore.c0000755000032200000250000000454714342376331020114 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 /* for offsetof macro in VMS */ #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "copy.h" #include "jnl.h" #include "buddy_list.h" #include "tp.h" #include "gtm_string.h" #include "gvname_info.h" #include "gvt_inline.h" GBLREF gv_key *gv_currkey; GBLREF gd_region *gv_cur_region; GBLREF gv_namehead *gv_target; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF uint4 dollar_tlevel; GBLREF sgm_info *sgm_info_ptr; void gvname_env_restore(gvname_info *curr_gvname_info) { DEBUG_ONLY(boolean_t is_bg_or_mm;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; gv_target = curr_gvname_info->s_gv_target; gv_cur_region = curr_gvname_info->s_gv_cur_region; DEBUG_ONLY(is_bg_or_mm = IS_REG_BG_OR_MM(gv_cur_region);) cs_addrs = curr_gvname_info->s_cs_addrs; assert((is_bg_or_mm && cs_addrs) || (dba_cm == REG_ACC_METH(gv_cur_region)) || (dba_usr == REG_ACC_METH(gv_cur_region))); if (NULL != cs_addrs) /* cs_addrs might be NULL for dba_cm/dba_usr region */ cs_data = cs_addrs->hdr; COPY_KEY(gv_currkey, curr_gvname_info->s_gv_currkey); sgm_info_ptr = curr_gvname_info->s_sgm_info_ptr; assert((is_bg_or_mm && ((dollar_tlevel && sgm_info_ptr) || (!dollar_tlevel && !sgm_info_ptr))) || (dba_cm == REG_ACC_METH(gv_cur_region)) || (dba_usr == REG_ACC_METH(gv_cur_region))); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); TREF(gd_targ_gvnh_reg) = curr_gvname_info->s_gd_targ_gvnh_reg; TREF(gd_targ_map) = curr_gvname_info->s_gd_targ_map; TREF(gd_targ_addr) = curr_gvname_info->s_gd_targ_addr; assert((gv_cur_region >= &(TREF(gd_targ_addr))->regions[0]) && (gv_cur_region < &(TREF(gd_targ_addr))->regions[(TREF(gd_targ_addr))->n_regions])); } fis-gtm-V7.0-005/sr_port/gvname_env_save.c0000755000032200000250000000443314342376331017361 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 /* for offsetof macro in VMS */ #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "copy.h" #include "jnl.h" #include "buddy_list.h" #include "tp.h" #include "gtm_string.h" #include "gvname_info.h" #include "gvt_inline.h" GBLREF gv_key *gv_currkey; GBLREF gd_region *gv_cur_region; GBLREF gv_namehead *gv_target; GBLREF sgmnt_addrs *cs_addrs; GBLREF sgmnt_data_ptr_t cs_data; GBLREF uint4 dollar_tlevel; GBLREF sgm_info *sgm_info_ptr; void gvname_env_save(gvname_info *curr_gvname_info) { DEBUG_ONLY(boolean_t is_bg_or_mm;) DEBUG_ONLY(gd_addr *addr;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DEBUG_ONLY(is_bg_or_mm = IS_REG_BG_OR_MM(gv_cur_region);) curr_gvname_info->s_gv_target = gv_target; curr_gvname_info->s_gv_cur_region = gv_cur_region; assert((is_bg_or_mm && cs_addrs->hdr == cs_data) || (dba_cm == REG_ACC_METH(gv_cur_region)) || (dba_usr == REG_ACC_METH(gv_cur_region))); curr_gvname_info->s_cs_addrs = cs_addrs; DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); COPY_KEY(curr_gvname_info->s_gv_currkey, gv_currkey); curr_gvname_info->s_sgm_info_ptr = sgm_info_ptr; assert((is_bg_or_mm && ((dollar_tlevel && sgm_info_ptr) || (!dollar_tlevel && !sgm_info_ptr))) || (dba_cm == REG_ACC_METH(gv_cur_region)) || (dba_usr == REG_ACC_METH(gv_cur_region))); DEBUG_ONLY(addr = TREF(gd_targ_addr);) assert((gv_cur_region >= &addr->regions[0]) && (gv_cur_region < &addr->regions[addr->n_regions])); curr_gvname_info->s_gd_targ_gvnh_reg = TREF(gd_targ_gvnh_reg); curr_gvname_info->s_gd_targ_map = TREF(gd_targ_map); curr_gvname_info->s_gd_targ_addr = TREF(gd_targ_addr); } fis-gtm-V7.0-005/sr_port/gvname_info.h0000755000032200000250000000261214342376331016510 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ /* gvname_info.h * ------------- * * Following structure is to save result of a call to op_gvname(). * Specially in merge we do not need to call op_gvname again and again. * Just call once and save result. * Also note that it is not easy to call op_gvname with variable arguments again and again. */ #ifndef MERGE_GLOBAL_DEFINED typedef struct gvname_info_struct { gv_key *s_gv_currkey; gv_namehead *s_gv_target; gd_region *s_gv_cur_region; sgmnt_addrs *s_cs_addrs; sgm_info *s_sgm_info_ptr; gvnh_reg_t *s_gd_targ_gvnh_reg; gd_binding *s_gd_targ_map; gd_addr *s_gd_targ_addr; int gvkey_nsubs; /* # of subscripts in s_gv_currkey. Maintained only in case of MERGE LCL=^GBL */ } gvname_info; typedef gvname_info *gvname_info_ptr; /* Function Prototypes for M global variable functions of MERGE */ void gvname_env_restore(gvname_info *curr_gvname_info); void gvname_env_save(gvname_info * curr_gvname_info); #define MERGE_GLOBAL_DEFINED #endif fis-gtm-V7.0-005/sr_port/gvnh_spanreg.h0000644000032200000250000000254714342376331016705 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2018 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. * * * ****************************************************************/ #ifndef GVNH_SPANREG_INCLUDED #define GVNH_SPANREG_INCLUDED #include "view.h" /* needed for "viewparm" */ #define ADD_GVT_TO_VIEW_NOISOLATION_LIST(GVT, PARMBLK) \ { \ noisolation_element *gvnh_entry; \ \ GBLREF buddy_list *noisolation_buddy_list; \ \ assert(NULL != noisolation_buddy_list); \ gvnh_entry = (noisolation_element *)get_new_element(noisolation_buddy_list, 1); \ gvnh_entry->gvnh = GVT; \ gvnh_entry->next = PARMBLK->ni_list.gvnh_list; \ PARMBLK->ni_list.gvnh_list = gvnh_entry; \ } void gvnh_spanreg_init(gvnh_reg_t *gvnh_reg, gd_addr *addr, gd_binding *gvmap_start); void gvnh_spanreg_subs_gvt_init(gvnh_reg_t *gvnh_reg, gd_addr *addr, viewparm *parmblk); boolean_t gvnh_spanreg_ismapped(gvnh_reg_t *gvnh_reg, gd_addr *addr, gd_region *reg); #endif /* GVNH_SPANREG_INCLUDED */ fis-gtm-V7.0-005/sr_port/gvnh_spanreg_init.c0000644000032200000250000001332014342376331017712 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2017 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "targ_alloc.h" #include "gvnh_spanreg.h" #include "gvcst_protos.h" /* Initialize gvnh_reg->gvspan if input global spans multiple regions. * "gvnh_reg" is the gvnh_reg_t structure that has already been allocated for this global name. * "addr" is the corresponding gd_addr (global directory structure) whose hashtable contains "gvnh_reg" * "gvmap_start" is the map entry in the gld file where the unsubscripted global name maps to. */ void gvnh_spanreg_init(gvnh_reg_t *gvnh_reg, gd_addr *addr, gd_binding *gvmap_start) { gvnh_spanreg_t *gvspan; gd_binding *gvmap_end, *gdmap_start; mident *gvname; char *gvent_name, *c, *c_top; int gvent_len, res, reg_index, gvspan_size; unsigned int min_reg_index, max_reg_index; gd_region *reg, *reg_start; # ifdef DEBUG boolean_t min_reg_index_adjusted = FALSE; gd_region *reg_top; gd_binding *gdmap_top; trans_num gd_targ_tn, *tn_array; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* At this point "gvnh_reg->gd_reg" and "gvnh_reg->gvt" have already been initialized */ /* First check if global spans multiple regions */ gvname = &gvnh_reg->gvt->gvname.var_name; gvent_len = gvname->len; gvent_name = gvname->addr; /* Assert that gvname is not subscripted (i.e. does not contain null bytes) */ # ifdef DEBUG c = gvent_name; c_top = gvent_name + gvent_len; for ( ; c < c_top; c++) assert(KEY_DELIMITER != *c); # endif gvmap_end = gvmap_start; if (!TREF(no_spangbls)) { reg_start = addr->regions; min_reg_index = addr->n_regions; /* impossible value of index into addr->regions[] array */ DEBUG_ONLY(reg_top = reg_start + min_reg_index;) max_reg_index = 0; DEBUG_ONLY(INCREMENT_GD_TARG_TN(gd_targ_tn);) /* takes a copy of incremented "TREF(gd_targ_tn)" * into local variable "gd_targ_tn" */ DEBUG_ONLY(tn_array = TREF(gd_targ_reg_array);) /* could be NULL if no spanning globals were seen till now */ for ( ; ; gvmap_end++) { res = memcmp(gvent_name, &(gvmap_end->gvkey.addr[0]), gvent_len); assert(0 >= res); reg = gvmap_end->reg.addr; GET_REG_INDEX(addr, reg_start, reg, reg_index); /* sets "reg_index" */ # ifdef DEBUG if (NULL != tn_array) tn_array[reg_index] = gd_targ_tn; # endif if (min_reg_index > reg_index) min_reg_index = reg_index; if (max_reg_index < reg_index) max_reg_index = reg_index; assert((0 != res) || (gvent_len <= gvmap_end->gvname_len)); if ((0 > res) || ((0 == res) && (gvent_len < gvmap_end->gvname_len))) break; } } /* else : no_spangbls is TRUE which means this process does not need to worry about globals spanning multiple regions */ if (gvmap_end == gvmap_start) { /* global does not span multiple regions. */ gvnh_reg->gvspan = NULL; return; } TREF(spangbl_seen) = TRUE; /* we found at least one global that spans multiple regions */ /* If global name is ^%YGS, the map entries for ^%YGS might change with time (see OPEN_BASEREG_IF_STATSREG macro * AND ygs_map->reg.addr in "gvcst_init_statsDB"). So keep min_reg_index as lowest possible value. */ if ((gvent_len == STATSDB_GBLNAME_LEN) && (0 == memcmp(gvent_name, STATSDB_GBLNAME, STATSDB_GBLNAME_LEN))) { DEBUG_ONLY(min_reg_index_adjusted = TRUE;) min_reg_index = 0; } /* Allocate and initialize a gvnh_spanreg_t structure and link it to gvnh_reg. * Note gvt_array[] size is max_reg_index - min_reg_index + 1. */ gvspan_size = SIZEOF(gvspan->gvt_array[0]) * (max_reg_index - min_reg_index); gvspan = (gvnh_spanreg_t *)malloc(SIZEOF(gvnh_spanreg_t) + gvspan_size); gvspan->min_reg_index = min_reg_index; gvspan->max_reg_index = max_reg_index; gdmap_start = addr->maps; DEBUG_ONLY(gdmap_top = gdmap_start + addr->n_maps;) assert((gvmap_start >= gdmap_start) && (gvmap_start < gdmap_top)); assert((gvmap_end >= gdmap_start) && (gvmap_end < gdmap_top)); gvspan->start_map_index = gvmap_start - gdmap_start; gvspan->end_map_index = gvmap_end - gdmap_start; /* Initialize the array of gv_targets (corresponding to each spanned region) to NULL initially. * As and when each region is referenced, the gv_targets will get allocated. */ memset(&gvspan->gvt_array[0], 0, gvspan_size + SIZEOF(gvspan->gvt_array[0])); # ifdef DEBUG if (!min_reg_index_adjusted) { /* Initialize the region slots that are not spanned to by this global with a distinct "invalid" value */ assert(tn_array[min_reg_index] == gd_targ_tn); assert(tn_array[max_reg_index] == gd_targ_tn); for (reg_index = min_reg_index; reg_index <= max_reg_index; reg_index++) { if (tn_array[reg_index] != gd_targ_tn) gvspan->gvt_array[reg_index - min_reg_index] = INVALID_GV_TARGET; } } # endif gvnh_reg->gvspan = gvspan; /* Initialize gvt for the region that the unsubscripted global name maps to */ reg = gvmap_start->reg.addr; GET_REG_INDEX(addr, reg_start, reg, reg_index); /* sets "reg_index" */ assert((reg_index >= min_reg_index) && (reg_index <= max_reg_index)); assert(INVALID_GV_TARGET != gvspan->gvt_array[reg_index - min_reg_index]); /* Assert that this region is indeed * mapped to by the spanning global */ gvspan->gvt_array[reg_index - min_reg_index] = gvnh_reg->gvt; return; } fis-gtm-V7.0-005/sr_port/gvnh_spanreg_ismapped.c0000644000032200000250000000246414342376331020560 0ustar librarygtc/**************************************************************** * * * Copyright 2013 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gvnh_spanreg.h" /* Check if "reg" is one of the regions that the global corresponding to "gvnh_reg" maps to in the gld file pointed to by "addr" */ boolean_t gvnh_spanreg_ismapped(gvnh_reg_t *gvnh_reg, gd_addr *addr, gd_region *reg) { gvnh_spanreg_t *gvspan; gd_binding *map, *map_top; gvspan = gvnh_reg->gvspan; assert(NULL != gvspan); assert(gvspan->start_map_index > 0); assert(gvspan->end_map_index > 0); assert(gvspan->start_map_index < addr->n_maps); assert(gvspan->end_map_index < addr->n_maps); map = &addr->maps[gvspan->start_map_index]; map_top = &addr->maps[gvspan->end_map_index]; assert(map < map_top); for ( ; map <= map_top; map++) if (reg == map->reg.addr) return TRUE; return FALSE; } fis-gtm-V7.0-005/sr_port/gvnh_spanreg_subs_gvt_init.c0000644000032200000250000001137214342376331021633 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2017 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_stdio.h" #include "gtmio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "targ_alloc.h" #include "gvnh_spanreg.h" #include "buddy_list.h" #include "dpgbldir.h" #include "change_reg.h" #include "gvcst_protos.h" /* for gvcst_root_search prototype */ #include "process_gvt_pending_list.h" /* for "is_gvt_in_pending_list" prototype used in ADD_TO_GVT_PENDING_LIST_IF_REG_NOT_OPEN */ #include "gtmimagename.h" #include "io.h" GBLREF gd_region *gv_cur_region; /* This assumes the input global (whose gvnh_reg_t structure is passed in as "gvnh_reg") spans multiple regions. * This function initializes gvnh_reg->gvspan.gvt_array[] by allocating ALL the gv_targets (if not already done) * corresponding to ALL regions that are spanned by subscripted references of the parent global name. * "gvnh_reg" is the gvnh_reg_t structure that has already been allocated for this global name. * "addr" is the corresponding gd_addr (global directory structure) whose hashtable contains "gvnh_reg" * If "parmblk" is non-NULL, this function is being invoked only by view_arg_convert (to set NOISOLATION status for * all possible gv_targets for a given global name). And hence needs to allocate all the gvt_array[] entries * even if the region is not open. * If "parmblk" is NULL, initialize gv_target->root as well (by opening the region if needed and doing a gvcst_root_search). */ void gvnh_spanreg_subs_gvt_init(gvnh_reg_t *gvnh_reg, gd_addr *addr, viewparm *parmblk) { gd_binding *gd_map_start, *map, *map_top; gd_region *reg, *gd_reg_start, *save_reg; gv_namehead *gvt, *name_gvt; gvnh_spanreg_t *gvspan; int min_reg_index, reg_index; # ifdef DEBUG gd_binding *gd_map_top; # endif assert(NULL != gvnh_reg->gvt); gvspan = gvnh_reg->gvspan; /* Determine what regions are spanned across by this global and allocate gv_targets only for those. */ gd_map_start = addr->maps; map = gd_map_start + gvspan->start_map_index; map_top = gd_map_start + gvspan->end_map_index + 1; DEBUG_ONLY(gd_map_top = &addr->maps[addr->n_maps]); assert(map_top <= gd_map_top); gd_reg_start = &addr->regions[0]; min_reg_index = gvspan->min_reg_index; name_gvt = gvnh_reg->gvt; # ifdef DEBUG /* ^%Y* should never be invoked here (callers would have issued ERR_PCTYRESERVED error in that case). Assert accordingly. */ assert((RESERVED_NAMESPACE_LEN > name_gvt->gvname.var_name.len) || (0 != MEMCMP_LIT(name_gvt->gvname.var_name.addr, RESERVED_NAMESPACE))); # endif save_reg = gv_cur_region; for ( ; map < map_top; map++) { reg = map->reg.addr; GET_REG_INDEX(addr, gd_reg_start, reg, reg_index); /* sets "reg_index" */ assert(reg_index >= min_reg_index); assert(reg_index <= gvspan->max_reg_index); reg_index -= min_reg_index; gvt = gvspan->gvt_array[reg_index]; assert(INVALID_GV_TARGET != gvt); /* Assert that this region is indeed mapped to by the spanning global */ if (NULL == gvt) { /* If called from VIEW "NOISOLATION" (i.e. parmblk is non-NULL), do NOT open the region here. * as we are going to add it to the gvt_pending_list anyways. */ if ((NULL == parmblk) && !reg->open) gv_init_reg(reg, NULL); gvt = (gv_namehead *)targ_alloc(reg->max_key_size, &name_gvt->gvname, reg); COPY_ACT_FROM_GVNH_REG_TO_GVT(gvnh_reg, gvt, reg); /* See comment in GVNH_REG_INIT macro for why the below assignment is * placed AFTER all error conditions (in above macro) have passed. */ gvspan->gvt_array[reg_index] = gvt; } if (NULL != parmblk) { if (NULL != name_gvt) ADD_GVT_TO_VIEW_NOISOLATION_LIST(gvt, parmblk); /* else : view_arg_convert would have done the ADD_GVT_TO_VIEW_NOISOLATION_LIST call already */ /* Before adding to the pending list, check if this gvt is already there * (due to a previous VIEW "NOISOLATION" command. If so skip the addition. */ if (NULL == is_gvt_in_pending_list(gvt)) ADD_TO_GVT_PENDING_LIST_IF_REG_NOT_OPEN(reg, &gvspan->gvt_array[reg_index], NULL); } else GV_BIND_SUBSREG(addr, reg, gvnh_reg); /* sets gv_target/gv_cur_region/cs_addrs/gv_target->root */ } if (NULL == parmblk) { gv_cur_region = save_reg; change_reg(); /* restore gv_cur_region to what it was at function entry */ } } fis-gtm-V7.0-005/sr_port/gvstats_rec.c0000644000032200000250000000737214342376331016544 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2008-2021 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gvstats_rec.h" /* Copy header stats to shared or private */ void gvstats_rec_csd2cnl(sgmnt_addrs *csa) { memcpy(&csa->nl->gvstats_rec, &csa->hdr->gvstats_rec, SIZEOF(gvstats_rec_csd_t)); } /* Copy shared or private back to header stats. Previously we did not copy all stats back to the header * as the initial GTM-8863 stats were instantaneous state indicator toggles that it made no sense to save. * Now that they are accumulators(GTM-9422), it makes sense to save them, and we do so. This requires that * gvstats_rec_t & gvstats_rec_csd_t are now identical in size, so we assert that.* */ void gvstats_rec_cnl2csd(sgmnt_addrs *csa) { /* All stats are now copied to the header */ assert(SIZEOF(gvstats_rec_t) == SIZEOF(gvstats_rec_csd_t)); memcpy(&csa->hdr->gvstats_rec, &csa->nl->gvstats_rec, SIZEOF(gvstats_rec_csd_t)); } void gvstats_rec_upgrade(sgmnt_addrs *csa) { node_local_ptr_t cnl; sgmnt_data_ptr_t csd; int index; csd = csa->hdr; cnl = csa->nl; /* csd still contains gvstats info in old place. Copy over to new location */ cnl->gvstats_rec.n_nontp_retries_0 = csd->filler_n_retries[0]; cnl->gvstats_rec.n_nontp_retries_1 = csd->filler_n_retries[1]; cnl->gvstats_rec.n_nontp_retries_2 = csd->filler_n_retries[2]; cnl->gvstats_rec.n_nontp_retries_3 = csd->filler_n_retries[3]; cnl->gvstats_rec.n_set = csd->filler_n_puts; cnl->gvstats_rec.n_kill = csd->filler_n_kills; cnl->gvstats_rec.n_query = csd->filler_n_queries; cnl->gvstats_rec.n_get = csd->filler_n_gets; cnl->gvstats_rec.n_order = csd->filler_n_order; cnl->gvstats_rec.n_zprev = csd->filler_n_zprevs; cnl->gvstats_rec.n_data = csd->filler_n_data; /* No longer maintained : csd->filler_n_puts_duplicate */ cnl->gvstats_rec.n_tp_readwrite = csd->filler_n_tp_updates; /* No longer maintained : csd->filler_n_tp_updates_duplicate */ cnl->gvstats_rec.n_tp_tot_retries_0 = csd->filler_n_tp_retries[0]; cnl->gvstats_rec.n_tp_tot_retries_1 = csd->filler_n_tp_retries[1]; cnl->gvstats_rec.n_tp_tot_retries_2 = csd->filler_n_tp_retries[2]; cnl->gvstats_rec.n_tp_tot_retries_3 = csd->filler_n_tp_retries[3]; cnl->gvstats_rec.n_tp_tot_retries_4 = csd->filler_n_tp_retries[4]; for (index = 5; index < 12; index++) cnl->gvstats_rec.n_tp_tot_retries_4 += csd->filler_n_tp_retries[index]; cnl->gvstats_rec.n_tp_cnflct_retries_0 = csd->filler_n_tp_retries_conflicts[0]; cnl->gvstats_rec.n_tp_cnflct_retries_1 = csd->filler_n_tp_retries_conflicts[1]; cnl->gvstats_rec.n_tp_cnflct_retries_2 = csd->filler_n_tp_retries_conflicts[2]; cnl->gvstats_rec.n_tp_cnflct_retries_3 = csd->filler_n_tp_retries_conflicts[3]; cnl->gvstats_rec.n_tp_cnflct_retries_4 = csd->filler_n_tp_retries_conflicts[4]; for (index = 5; index < 12; index++) cnl->gvstats_rec.n_tp_cnflct_retries_4 += csd->filler_n_tp_retries_conflicts[index]; /* Nullify statistics that were formerly in use but no longer so */ csd->unused_dsk_reads.curr_count = 0; csd->unused_dsk_reads.cumul_count = 0; csd->unused_dsk_writes.curr_count = 0; csd->unused_dsk_writes.cumul_count = 0; } fis-gtm-V7.0-005/sr_port/gvstats_rec.h0000644000032200000250000000262614342376331016546 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2008-2021 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. * * * ****************************************************************/ #ifndef GVSTATS_REC_H_INCLUDED #define GVSTATS_REC_H_INCLUDED /* Note gvstats_rec exists in both sgmnt_data (file header) and in node_local. The reason * for this is so that gvstats can be updated by read-only processes which would not be * able to update the read-only file header. The gvstats in node_local are the ones that * get updated and are peridically copied back to the fileheader and during fileheader * flushes to keep them up to date. */ #define TAB_GVSTATS_REC(A,B,C) A, enum gvstats_rec_type { #include "tab_gvstats_rec.h" n_gvstats_rec_types }; #undef TAB_GVSTATS_REC typedef struct gvstats_rec_struct { #define TAB_GVSTATS_REC(A,B,C) gtm_uint64_t A; #include "tab_gvstats_rec.h" } gvstats_rec_t; #undef TAB_GVSTATS_REC typedef struct gvstats_rec_csd_struct { #define TAB_GVSTATS_REC(A,B,C) gtm_uint64_t A; #include "tab_gvstats_rec.h" } gvstats_rec_csd_t; #undef TAB_GVSTATS_REC #endif fis-gtm-V7.0-005/sr_port/gvstrsub.h0000755000032200000250000000112714342376331016077 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GVSTRSUB_INCLUDED #define GVSTRSUB_INCLUDED unsigned char *gvstrsub(unsigned char *src, unsigned char *target); #endif /* GVSTRSUB_INCLUDED */ fis-gtm-V7.0-005/sr_port/gvsub2str.c0000644000032200000250000001406714342376331016160 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* * ----------------------------------------------------- * Convert a string subscript to MUMPS string * ----------------------------------------------------- */ #include "mdef.h" #include "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "copy.h" #include "collseq.h" #include "do_xform.h" #include "gvsub2str.h" #include "zshow.h" #define LARGE_EXP 10000 GBLREF gv_namehead *gv_target; LITREF unsigned short dpos[], dneg[]; error_def(ERR_GVSUBOFLOW); /* * ----------------------------------------------------- * Convert a string subscript to MUMPS string * Save result in a buffer pointed by targ. * * Entry: * sub - input string in subscript format * targ - output mstr value. * xlat_flg- translate flag. * If true convert string to MUMPS format (aka ZWRITE format) * Return: * (pointer to the last char. * converted in the targ string) + 1. * ----------------------------------------------------- */ unsigned char *gvsub2str(unsigned char *sub, mstr *opstr, boolean_t xlat_flg) { unsigned char buf[MAX_KEY_SZ + 1], buf1[MAX_KEY_SZ + 1], ch, *ptr, trail_ch, *str, *targ, *targ_end; unsigned short *tbl_ptr; int num, rev_num, trail_zero; span_subs *subs_ptr; int expon, in_length, targ_len; mstr mstr_ch, mstr_targ; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ch = *sub++; targ = (unsigned char *)opstr->addr; if (STR_SUB_PREFIX == ch || (SUBSCRIPT_STDCOL_NULL == ch && KEY_DELIMITER == *sub)) { /* If this is a string */ in_length = 0; ptr = (xlat_flg ? buf : targ); while ((ch = *sub++)) { /* Copy string to ptr, xlating each char */ in_length++; if (STR_SUB_ESCAPE == ch) /* if this is an escape, demote next char */ ch = (*sub++ - 1); *ptr++ = ch; } if (TREF(transform) && gv_target && gv_target->collseq) { mstr_ch.len = in_length; mstr_ch.addr = (char *)(xlat_flg ? buf : targ); mstr_targ.len = SIZEOF(buf1); mstr_targ.addr = (char *)buf1; do_xform(gv_target->collseq, XBACK, &mstr_ch, &mstr_targ, &targ_len); if (targ_len > opstr->len) { /* The only way we know of for targ_len to be greater than opstr->len is if the collation * library allocated an external buffer greater than opstr->len */ assert(mstr_targ.addr != (char *)buf1); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_GVSUBOFLOW); } if (!xlat_flg) { memcpy(targ, mstr_targ.addr, targ_len); /* mstr_targ.addr is used just in case it is * reallocated by the XBACK routine. */ targ = targ + targ_len; } else { in_length = targ_len; ptr = (unsigned char *)mstr_targ.addr; /* mstr_targ.addr is used just in case it is * reallocated in the XBACK routine. */ } } else if (xlat_flg) ptr = &buf[0]; else targ = targ + in_length; if (xlat_flg) { targ_len = opstr->len; format2zwr((sm_uc_ptr_t)ptr, in_length, targ, &targ_len); assert(targ_len <= opstr->len); targ = targ + targ_len; } } else { /* Number */ targ_end = targ + opstr->len; assert(opstr->len >= SPAN_PREFIX_LEN); if (SUBSCRIPT_ZERO == ch) *targ++ = '0'; else if(SPANGLOB_SUB_ESCAPE == ch) { ASGN_SPAN_PREFIX(targ); targ += SPAN_PREFIX_LEN; subs_ptr = (span_subs *)(sub - 1); /* Internal to the database, the spanning node blocks counting starts with 0 i.e. first spanning * node block has ID '0' but while displaying first block of spanning node is displayed as '1' * Hence the below adjustment in the 'num'. */ num = SPAN_GVSUBS2INT(subs_ptr) + 1; sub = (sub - 1) + SPAN_SUBS_LEN; for (trail_zero = 0; (num % DECIMAL_BASE) == 0; trail_zero++, num /= DECIMAL_BASE) ; for (rev_num = 0; num > 0; rev_num = (rev_num * DECIMAL_BASE + num % DECIMAL_BASE), num /= DECIMAL_BASE) ; for (; (rev_num > 0) && (targ < targ_end); *targ++ = (rev_num % DECIMAL_BASE + ASCII_0), rev_num /= DECIMAL_BASE) ; for (; (trail_zero > 0) && (targ < targ_end); *targ++ = '0', trail_zero--); if (*sub != 0) *targ++ = '*'; } else { tbl_ptr = (unsigned short *)&dpos[0] - 1; trail_ch = KEY_DELIMITER; if ((0 <= (signed char)ch) && (targ < targ_end)) { /* Bit 7 of the exponent is set for positive numbers; must be negative */ trail_ch = NEG_MNTSSA_END; tbl_ptr = (unsigned short *)dneg; ch = ~ch; *targ++ = '-'; } ch -= (SUBSCRIPT_BIAS - 1); /* Unbias the exponent */ expon = ch; if (0 >= (signed char)ch) { /* number is a fraction */ ch = -(signed char)ch; /* Save decimal point */ *targ++ = '.'; /* generate leading 0's */ do *targ++ = '0'; while (((signed char)ch-- > 0) && (targ < targ_end)) ; /* make expon. really large to avoid * generating extra dots */ expon = LARGE_EXP; } while ((ch = *sub++) && (ch != trail_ch) && (targ < targ_end)) { /* Convert digits loop */ /* adjust dcm. point */ if (0 >= (expon -= 2)) { if (0 != expon) { *targ++ = '.'; expon = LARGE_EXP; PUT_USHORT(targ, tbl_ptr[ch]); targ += SIZEOF(short); } else { /* Insert dot between digits */ PUT_USHORT(targ, tbl_ptr[ch]); targ += SIZEOF(short); *targ = *(targ - 1); *(targ - 1) = '.'; targ++; expon = LARGE_EXP; } } else { PUT_USHORT(targ, tbl_ptr[ch]); targ += SIZEOF(short); } } if ((LARGE_EXP - 100) < expon) { if ('0' == *(targ - 1)) targ--; if ('.' == *(targ - 1)) targ--; } else while (--expon > 0 && targ < targ_end) *targ++ = '0'; } } return targ; } fis-gtm-V7.0-005/sr_port/gvsub2str.h0000755000032200000250000000115314342376331016160 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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. * * * ****************************************************************/ #ifndef GVSUB2STR_INCLUDED #define GVSUB2STR_INCLUDED unsigned char *gvsub2str(unsigned char *sub, mstr *targ, boolean_t xlat_flg); #endif /* GVSUB2STR_INCLUDED */ fis-gtm-V7.0-005/sr_port/gvt_hashtab.c0000644000032200000250000000403514342376331016503 0ustar librarygtc/**************************************************************** * * * Copyright 2013 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "dpgbldir.h" #include "process_gvt_pending_list.h" #include "hashtab_mname.h" #include "targ_alloc.h" #include "gvt_hashtab.h" GBLREF gv_namehead *gv_target_list; void gvt_hashtab_init(sgmnt_addrs *csa) { gv_namehead *gvtarg; boolean_t added; ht_ent_mname *stayent; assert(NULL == csa->gvt_hashtab); /* This is the first time a duplicate region for the same database file is being opened. * Since two regions point to the same physical file, start maintaining a list of all global variable * names whose gv_targets have already been allocated on behalf of the current database file (not including * the region that is currently being opened for which gvt->gd_csa will still be NULL). * Future targ_allocs will check this list before they allocate (to avoid duplicate allocations). */ csa->gvt_hashtab = (hash_table_mname *)malloc(SIZEOF(hash_table_mname)); init_hashtab_mname(csa->gvt_hashtab, 0, HASHTAB_NO_COMPACT, HASHTAB_NO_SPARE_TABLE); assert(1 == csa->regcnt); for (gvtarg = gv_target_list; NULL != gvtarg; gvtarg = gvtarg->next_gvnh) { /* There is one region that is "open" and has gv_targets allocated for this "csa". * Add those gv_targets into the hashtable first. */ if (gvtarg->gd_csa != csa) continue; if (DIR_ROOT == gvtarg->root) continue; /* gvt is csa->dir_tree and does not correspond to a global name */ added = add_hashtab_mname(csa->gvt_hashtab, &gvtarg->gvname, gvtarg, &stayent); assert(added && (1 == gvtarg->regcnt)); } } fis-gtm-V7.0-005/sr_port/gvt_hashtab.h0000644000032200000250000000105214342376331016504 0ustar librarygtc/**************************************************************** * * * Copyright 2013 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. * * * ****************************************************************/ #ifndef GVT_HASHTAB_DEFINED void gvt_hashtab_init(sgmnt_addrs *csa); #define GVT_HASHTAB_DEFINED #endif fis-gtm-V7.0-005/sr_port/gvt_inline.h0000644000032200000250000001744714342376331016367 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2020 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. * * * ****************************************************************/ #ifndef GVT_INLINE_INCLUDED #define GVT_INLINE_INCLUDED static inline gv_key *gvkey_init(gv_key *gvkey, int4 keysize) { GBLREF gv_key *gv_altkey; GBLREF gv_key *gv_currkey; gv_key *new_KEY, *old_KEY; int4 keySZ; DEBUG_ONLY(DCL_THREADGBL_ACCESS); DEBUG_ONLY(SETUP_THREADGBL_ACCESS); old_KEY = gvkey; keySZ = keysize; /* KEYSIZE should have been the output of a DBKEYSIZE command so * should be a multiple of 4. Assert that. */ assert(ROUND_UP2(keySZ, 4) == keySZ); new_KEY = (gv_key *)malloc(SIZEOF(gv_key) + keySZ + 1); assert((DBKEYSIZE(MAX_KEY_SZ) == keysize) || ((gvkey != gv_currkey) && (gvkey != gv_altkey))); if ((NULL != old_KEY) && (PREV_KEY_NOT_COMPUTED != old_KEY->end)) { /* Don't call GVKEY_INIT twice for same key. The only exception * is if we are called from COPY_PREV_KEY_TO_GVT_CLUE in a * restartable situation but TREF(donot_commit) should have * been set to a special value then so check that. */ assert(TREF(donot_commit) | DONOTCOMMIT_COPY_PREV_KEY_TO_GVT_CLUE); assert(keysize >= old_KEY->top); assert(old_KEY->top > old_KEY->end); memcpy(new_KEY, old_KEY, SIZEOF(gv_key) + old_KEY->end + 1); free(old_KEY); } else { new_KEY->base[0] = '\0'; new_KEY->end = 0; new_KEY->prev = 0; } new_KEY->top = keySZ; return new_KEY; } static inline void dbg_check_gvtarget_integrity(gv_namehead *gvt) { int keysize, partial_size; GBLREF boolean_t dse_running; if (NULL != gvt->gd_csa->nl) { /* csa->nl is cleared when a statsDB is closed due to opt-out so use as flag if DB is open or not */ keysize = gvt->gd_csa->hdr->max_key_size; keysize = DBKEYSIZE(keysize); partial_size = SIZEOF(gv_namehead) + 2 * SIZEOF(gv_key) + 3 * keysize; /* DSE could change the max_key_size dynamically so account for it in the below assert */ if (!dse_running) { assert(gvt->gvname.var_name.addr == (char *)gvt + partial_size); assert((char *)gvt->first_rec == ((char *)&gvt->clue + SIZEOF(gv_key) + keysize)); assert((char *)gvt->last_rec == ((char *)gvt->first_rec + SIZEOF(gv_key) + keysize)); assert(gvt->clue.top == keysize); } assert(gvt->clue.top == gvt->first_rec->top); assert(gvt->clue.top == gvt->last_rec->top); } } static inline void copy_prev_key_to_gvt_clue(gv_namehead *gvt, boolean_t expand_prev_key) { GBLREF gv_key *gv_altkey; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (expand_prev_key) { /* gv_altkey has the previous key. Store it in clue for future clue-based searches */ if (NULL == gvt->prev_key) GVKEY_INIT(gvt->prev_key, gvt->clue.top); if (gv_altkey->end >= gvt->prev_key->top) { /* Note that this is possible in case of concurrency issues (i.e. we are in * a restartable situation (see comment at bottom of gvcst_expand_key.c which * talks about a well-formed key. Since we cannot easily signal a restart here, * we reallocate to ensure the COPY_KEY does not cause a buffer overflow and * the caller will eventually do the restart. */ DEBUG_ONLY(TREF(donot_commit) |= DONOTCOMMIT_COPY_PREV_KEY_TO_GVT_CLUE;) GVKEY_INIT(gvt->prev_key, DBKEYSIZE(gv_altkey->end)); } COPY_KEY(gvt->prev_key, gv_altkey); } else if (NULL != gvt->prev_key) { assert(PREV_KEY_NOT_COMPUTED < (1 << (SIZEOF(gv_altkey->end) * 8))); gvt->prev_key->end = PREV_KEY_NOT_COMPUTED; } } static inline void copy_curr_and_prev_key_to_gvtarget_clue(gv_namehead *gvt, gv_key *gvkey, boolean_t expand_prev_key) { GBLREF gv_key *gv_altkey; int keyend; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; keyend = gvkey->end; if (gvt->clue.top <= keyend) { /* Possible only if GVT corresponds to a global that spans multiple * regions. For example, a gvcst_spr_* function could construct a * gv_currkey starting at one spanned region and might have to do a * gvcst_* operation on another spanned region with a max-key-size * that is smaller than gv_currkey->end. In that case, copy only the * portion of gv_currkey that will fit in the gvt of the target region. */ assert(TREF(spangbl_seen)); keyend = gvt->clue.top - 1; memcpy(((gv_key *)&(gvt->clue))->base, gvkey->base, keyend - 1); ((gv_key *)&(gvt->clue))->base[keyend - 1] = KEY_DELIMITER; ((gv_key *)&(gvt->clue))->base[keyend] = KEY_DELIMITER; } else { assert(KEY_DELIMITER == gvkey->base[keyend]); assert(KEY_DELIMITER == gvkey->base[keyend - 1]); memcpy(((gv_key *)&(gvt->clue))->base, gvkey->base, keyend + 1); } gvt->clue.end = keyend; /* No need to maintain unused GVT->clue.prev */ copy_prev_key_to_gvt_clue(gvt, expand_prev_key); dbg_check_gvtarget_integrity(gvt); } /* Replace a NULL subscript at the end with the maximum possible subscript * that could exist in the database for this global name. */ static inline void gvzprevious_append_max_subs_key(gv_key *gvkey, gv_namehead *gvt) { GBLREF gd_region *gv_cur_region; int lastsubslen, keysize; unsigned char *ptr; assert(gvt->clue.top || (NULL == gvt->gd_csa)); assert(!gvt->clue.top || ((NULL != gvt->gd_csa) && (gvt->gd_csa == cs_addrs))); /* keysize can be obtained from GVT->clue.top in case of GT.M. * But for GT.CM client, clue will be uninitialized. So we would need to * compute keysize from gv_cur_region->max_key_size. Since this is true for * GT.M as well, we use the same approach for both to avoid an if check and a * break in the pipeline. */ keysize = DBKEYSIZE(gv_cur_region->max_key_size); assert(!gvt->clue.top || (keysize == gvt->clue.top)); lastsubslen = keysize - gvkey->prev - 2; assertpro((0 < lastsubslen) && (gvkey->top >= keysize) && (gvkey->end > gvkey->prev)); ptr = &gvkey->base[gvkey->prev]; memset(ptr, STR_SUB_MAXVAL, lastsubslen); ptr += lastsubslen; *ptr++ = KEY_DELIMITER; /* terminator for last subscript */ *ptr = KEY_DELIMITER; /* terminator for entire key */ gvkey->end = gvkey->prev + lastsubslen + 1; assert(gvkey->end == (ptr - &gvkey->base[0])); if (NULL != gv_target->gd_csa) dbg_check_gvtarget_integrity(gvt); } static inline void dbg_check_gvtarget_gvcurrkey_in_sync(boolean_t check_csaddrs) { mname_entry *gvent; mstr *varname; int varlen; unsigned short keyend; unsigned char *keybase; GBLREF int4 gv_keysize; GBLREF gv_key *gv_currkey; GBLREF gv_namehead *reset_gv_target; assert((NULL != gv_currkey) || (NULL == gv_target)); /* Make sure gv_currkey->top always reflects the maximum keysize across all dbs that we opened until now */ assert((NULL == gv_currkey) || (gv_currkey->top == gv_keysize)); if (!process_exiting) { keybase = &gv_currkey->base[0]; if ((NULL != gv_currkey) && (0 != keybase[0]) && (0 != gv_currkey->end) && (INVALID_GV_TARGET == reset_gv_target)) { assert(NULL != gv_target); gvent = &gv_target->gvname; varname = &gvent->var_name; varlen = varname->len; assert(varlen); assert((0 != keybase[varlen]) || !memcmp(keybase, varname->addr, varlen)); keyend = gv_currkey->end; assert(!keyend || (KEY_DELIMITER == keybase[keyend])); assert(!keyend || (KEY_DELIMITER == keybase[keyend - 1])); /* Check that gv_target is part of the gv_target_list */ DBG_CHECK_GVT_IN_GVTARGETLIST(gv_target); if (check_csaddrs) DBG_CHECK_GVTARGET_CSADDRS_IN_SYNC; } /* Do gv_target sanity check too; Do not do this if it is NULL or if it is GT.CM GNP client (gd_csa is NULL) */ if ((NULL != gv_target) && (NULL != gv_target->gd_csa)) dbg_check_gvtarget_integrity(gv_target); } } #endif fis-gtm-V7.0-005/sr_port/gvusr.h0000755000032200000250000000170514342376331015370 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2002 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GVUSR_INCLUDED #define GVUSR_INCLUDED int gvusr_data(void); int gvusr_get(mval *v); int gvusr_lock(uint4 lock_len, unsigned char *lock_key, gd_region *reg); int gvusr_order(void); int gvusr_query(mval *v); int gvusr_zprevious(void); void gvusr_init(gd_region *reg, gd_region **creg, gv_key **ckey, gv_key **akey); void gvusr_kill(bool do_subtree); void gvusr_put(mval *v); void gvusr_rundown(void); void gvusr_unlock(uint4 lock_len, unsigned char *lock_key, gd_region *reg); #endif /* GVUSR_INCLUDED */ fis-gtm-V7.0-005/sr_port/gvusr_queryget.h0000755000032200000250000000111614342376331017311 0ustar librarygtc/**************************************************************** * * * Copyright 2002 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef GVUSR_QUERYGET_H_INCLUDED #define GVUSR_QUERYGET_H_INCLUDED boolean_t gvusr_queryget(mval *v); #endif /* GVUSR_QUERYGET_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/gvzwr_arg.c0000755000032200000250000000301414342376331016220 0ustar librarygtc/**************************************************************** * * * 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 "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "zwrite.h" GBLREF gvzwrite_datablk *gvzwrite_block; void gvzwr_arg(int t, mval *a1, mval *a2) { int i; i = gvzwrite_block->subsc_count++; /* it would be good to guard the array i < sizeof... */ if (a1) { MV_FORCE_DEFINED(a1); if (MV_IS_CANONICAL(a1)) MV_FORCE_NUMD(a1); MV_FORCE_STRD(a1); if ((ZWRITE_VAL != t) && (0 == a1->str.len)) /* value is real - leave it alone */ a1 = NULL; } if (a2) { MV_FORCE_DEFINED(a2); if (MV_IS_CANONICAL(a2)) MV_FORCE_NUMD(a2); MV_FORCE_STRD(a2); if (0 == a2->str.len) /* can never be value */ a2 = NULL; } ((zwr_sub_lst *)gvzwrite_block->sub)->subsc_list[i].subsc_type = t; ((zwr_sub_lst *)gvzwrite_block->sub)->subsc_list[i].first = a1; ((zwr_sub_lst *)gvzwrite_block->sub)->subsc_list[i].second = a2; if ((ZWRITE_ASTERISK != t) && (ZWRITE_ALL != t)) gvzwrite_block->mask |= 1 << i; if (ZWRITE_VAL != t) gvzwrite_block->fixed = FALSE; return; } fis-gtm-V7.0-005/sr_port/gvzwr_fini.c0000644000032200000250000000773514342376331016407 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "mlkdef.h" #include "zshow.h" #include "zwrite.h" #include "error.h" #include "op.h" #include "change_reg.h" #include "patcode.h" #include "sgnl.h" #include "gvzwrite_clnup.h" #include "mvalconv.h" #include "gtmimagename.h" #include "gvt_inline.h" GBLDEF zshow_out *zwr_output; GBLREF gv_namehead *gv_target; GBLREF gv_namehead *reset_gv_target; GBLREF gv_key *gv_currkey; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF gvzwrite_datablk *gvzwrite_block; GBLREF gd_addr *gd_header; GBLREF bool undef_inhibit; error_def(ERR_GVNAKED); void gvzwr_fini(zshow_out *out, int pat) { char m[SIZEOF(mident_fixed)]; mval local, data; gv_key *old; gvnh_reg_t *gvnh_reg; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (!gv_currkey) gvinit(); ESTABLISH(gvzwrite_ch); zwr_output = out; assert(INVALID_GV_TARGET == reset_gv_target); reset_gv_target = gv_target; DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); gvzwrite_block->gd_reg = gv_cur_region; gvzwrite_block->old_targ = (unsigned char *)gv_target; old = (gv_key *)malloc(SIZEOF(gv_key) + gv_currkey->end + 1); gvzwrite_block->old_key = (unsigned char *)old; memcpy(gvzwrite_block->old_key, gv_currkey, SIZEOF(gv_key) + gv_currkey->end + 1); gvzwrite_block->gv_last_subsc_null = TREF(gv_last_subsc_null); gvzwrite_block->gv_some_subsc_null = TREF(gv_some_subsc_null); if (!pat) { local = *gvzwrite_block->pat; if (local.str.len) /* New reference. Will get new gv_target.. */ { gv_target = NULL; gv_currkey->base[0] = '\0'; op_gvname(VARLSTCNT(1) &local); op_gvdata(&data); if (!(MV_FORCE_INTD(&data))) { if (!undef_inhibit) sgnl_gvundef(); } else { gvzwrite_block->fixed = (gvzwrite_block->fixed ? TRUE : FALSE); gvzwr_var(MV_FORCE_INTD(&data), 0); } } else /* Old (naked) reference. Keep previous gv_target reference */ { if (gv_currkey->prev == 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_GVNAKED); gv_currkey->end = gv_currkey->prev; gv_currkey->base[gv_currkey->end] = 0; gv_currkey->prev = 0; /* If gvnh_reg corresponds to a spanning global, then determine * gv_cur_region/gv_target/gd_targ_* variables based on updated gv_currkey. */ gvnh_reg = TREF(gd_targ_gvnh_reg); /* set by op_gvname in previous call */ GV_BIND_SUBSNAME_FROM_GVNH_REG_IF_GVSPAN(gvnh_reg, gd_header, gv_currkey); op_gvdata(&data); if (!(MV_FORCE_INTD(&data))) { if (!undef_inhibit) sgnl_gvundef(); } else { gvzwrite_block->fixed = (gvzwrite_block->fixed ? TRUE : FALSE); gvzwr_var((int4)MV_FORCE_INTD(&data), 0); } } } else { gv_target = NULL; gv_currkey->base[0] = '\0'; local.mvtype = MV_STR; local.str.addr = &m[0]; local.str.len = 1; m[0] = '%'; gvzwrite_block->fixed = FALSE; for (; ;) { op_gvname(VARLSTCNT(1) &local); if (do_pattern(&local, gvzwrite_block->pat)) { op_gvdata(&data); if ((MV_FORCE_INTD(&data))) { gvzwr_var((int4)MV_FORCE_INTD(&data), 0); } } op_gvorder(&local); if (0 < local.str.len) { assert(local.str.len <= (MAX_MIDENT_LEN + 1)); local.str.addr++; local.str.len--; memcpy(&m[0], local.str.addr, (size_t)local.str.len); local.str.addr = &m[0]; } else break; } } gvzwrite_clnup(); /* this routine is called by gvzwrite_ch() too */ REVERT; return; } fis-gtm-V7.0-005/sr_port/gvzwr_init.c0000755000032200000250000000221414342376331016413 0ustar librarygtc/**************************************************************** * * * 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "zwrite.h" #include "subscript.h" GBLREF gvzwrite_datablk *gvzwrite_block; void gvzwr_init(unsigned short t, mval *val, int4 pat) { if (NULL == gvzwrite_block) { gvzwrite_block = malloc(SIZEOF(gvzwrite_datablk)); memset(gvzwrite_block, 0, SIZEOF(gvzwrite_datablk)); } MV_FORCE_STR(val); gvzwrite_block->type = pat; if (NULL == gvzwrite_block->sub) gvzwrite_block->sub = (zwr_sub_lst *)malloc(SIZEOF(zwr_sub_lst) * MAX_GVSUBSCRIPTS); gvzwrite_block->pat = val; gvzwrite_block->mask = gvzwrite_block->subsc_count = 0; gvzwrite_block->fixed = TRUE; return; } fis-gtm-V7.0-005/sr_port/gvzwr_out.c0000755000032200000250000000245614342376331016267 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2008 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "zshow.h" #include "op.h" #include "format_targ_key.h" #include "mlkdef.h" #include "zwrite.h" GBLREF gv_key *gv_currkey; GBLREF zshow_out *zwr_output; void gvzwr_out(void) { int n; mval val; mval outdesc; mstr one; char buff[MAX_ZWR_KEY_SZ], *end; if ((end = (char *)format_targ_key((uchar_ptr_t)&buff[0], MAX_ZWR_KEY_SZ, gv_currkey, TRUE)) == 0) end = &buff[MAX_ZWR_KEY_SZ - 1]; op_gvget(&val); if (!MV_DEFINED(&val)) return; MV_FORCE_STRD(&val); outdesc.mvtype = MV_STR; outdesc.str.addr = &buff[0]; outdesc.str.len = INTCAST(end - outdesc.str.addr); zshow_output(zwr_output,&outdesc.str); buff[0] = '='; one.addr = &buff[0]; one.len = 1; zshow_output(zwr_output,&one); mval_write(zwr_output,&val,TRUE); } fis-gtm-V7.0-005/sr_port/gvzwr_var.c0000755000032200000250000001705414342376331016250 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "zwrite.h" #include "op.h" #include "deferred_events_queue.h" #include "numcmp.h" #include "patcode.h" #include "sgnl.h" #include "mvalconv.h" #include "follow.h" #include "gtm_string.h" #include "gtmimagename.h" #define eb_less(u, v) (numcmp(u, v) < 0) GBLREF bool undef_inhibit; GBLREF gd_addr *gd_header; GBLREF gd_region *gv_cur_region; GBLREF gv_key *gv_currkey; GBLREF gvzwrite_datablk *gvzwrite_block; GBLREF volatile int4 outofband; LITREF mval literal_null; void gvzwr_var(uint4 data, int4 n) { mval mv, subdata; unsigned short end, prev, end1, prev1; bool save_gv_last_subsc_null; boolean_t do_lev; char seen_null; zwr_sub_lst *zwr_sub; int loop_condition = 1; gvnh_reg_t *gvnh_reg; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (outofband) async_action(FALSE); zwr_sub = (zwr_sub_lst *)gvzwrite_block->sub; if ((0 == gvzwrite_block->subsc_count) && (0 == n)) zwr_sub->subsc_list[n].subsc_type = ZWRITE_ASTERISK; if ((1 == data || 11 == data) && (!gvzwrite_block->subsc_count || (ZWRITE_ASTERISK == zwr_sub->subsc_list[n].subsc_type) || (n && !(gvzwrite_block->mask >> n)))) gvzwr_out(); if ((1 >= data) || (gvzwrite_block->subsc_count && (n >= gvzwrite_block->subsc_count) && (ZWRITE_ASTERISK != zwr_sub->subsc_list[gvzwrite_block->subsc_count - 1].subsc_type))) return; assert(1 < data); end = gv_currkey->end; prev = gv_currkey->prev; gvnh_reg = TREF(gd_targ_gvnh_reg); /* set by op_gvname call done in gvzwr_fini before call to gvzwr_var */ if ((n < gvzwrite_block->subsc_count) && (ZWRITE_VAL == zwr_sub->subsc_list[n].subsc_type)) { mval2subsc(zwr_sub->subsc_list[n].first, gv_currkey, gv_cur_region->std_null_coll); /* If gvnh_reg corresponds to a spanning global, then determine * gv_cur_region/gv_target/gd_targ_* variables based on updated gv_currkey. */ GV_BIND_SUBSNAME_FROM_GVNH_REG_IF_GVSPAN(gvnh_reg, gd_header, gv_currkey); op_gvdata(&subdata); if (MV_FORCE_INTD(&subdata) && ((10 != (int4)MV_FORCE_INTD(&subdata)) || n < gvzwrite_block->subsc_count - 1)) { save_gv_last_subsc_null = TREF(gv_last_subsc_null); gvzwr_var((int4)MV_FORCE_INTD(&subdata), n + 1); TREF(gv_last_subsc_null) = save_gv_last_subsc_null; } else if (gvzwrite_block->fixed && (!undef_inhibit)) sgnl_gvundef(); } else { seen_null = 0; if (n < gvzwrite_block->subsc_count && zwr_sub->subsc_list[n].first && ZWRITE_PATTERN != zwr_sub->subsc_list[n].subsc_type) { mv = *zwr_sub->subsc_list[n].first; mval2subsc(&mv, gv_currkey, gv_cur_region->std_null_coll); if ((mv.mvtype & MV_STR) && !mv.str.len) seen_null = 1; /* If gvnh_reg corresponds to a spanning global, then determine * gv_cur_region/gv_target/gd_targ_* variables based on updated gv_currkey. */ GV_BIND_SUBSNAME_FROM_GVNH_REG_IF_GVSPAN(gvnh_reg, gd_header, gv_currkey); op_gvdata(&subdata); } else { mval2subsc((mval *)&literal_null, gv_currkey, gv_cur_region->std_null_coll); TREF(gv_last_subsc_null) = TRUE; if (0 == gv_cur_region->std_null_coll) { op_gvorder(&mv); /* This will return the first subscript */ if (0 == mv.str.len) { if (NEVER == gv_cur_region->null_subs || seen_null) loop_condition = 0; else { seen_null = 1; /* set flag to indicate processing null sub */ op_gvnaked(VARLSTCNT(1) &mv); op_gvdata(&subdata); if (!MV_FORCE_INTD(&subdata)) loop_condition = 0; } } else { op_gvnaked(VARLSTCNT(1) &mv); op_gvdata(&subdata); } } else /* for standard null collation */ { /* determine whether $data(^gbl("") == 1 or 11, if yes, first process that */ if (NEVER == gv_cur_region->null_subs) { op_gvorder(&mv); assert(0 != mv.str.len); /* We are looking for the first subscript at a given level and so, we do not expect to have hit at the end of the list */ op_gvnaked(VARLSTCNT(1) &mv); op_gvdata(&subdata); } else { /* If gvnh_reg corresponds to a spanning global, then determine * gv_cur_region/gv_target/gd_targ_* variables based on updated gv_currkey. */ GV_BIND_SUBSNAME_FROM_GVNH_REG_IF_GVSPAN(gvnh_reg, gd_header, gv_currkey); op_gvdata(&subdata); if (MV_FORCE_INTD(&subdata)) seen_null = 1; } } } while (loop_condition) { do_lev = (MV_FORCE_INTD(&subdata) ? TRUE : FALSE); if (n < gvzwrite_block->subsc_count) { if (ZWRITE_PATTERN == zwr_sub->subsc_list[n].subsc_type) { if (!do_pattern(&mv, zwr_sub->subsc_list[n].first)) do_lev = FALSE; } else if (ZWRITE_ALL != zwr_sub->subsc_list[n].subsc_type) { if (do_lev && zwr_sub->subsc_list[n].first) { if (MV_IS_CANONICAL(&mv)) { if (!MV_IS_CANONICAL(zwr_sub->subsc_list[n].first) || eb_less(&mv, zwr_sub->subsc_list[n].first)) do_lev = FALSE; } else { if (!MV_IS_CANONICAL(zwr_sub->subsc_list[n].first) && (!follow(&mv, zwr_sub->subsc_list[n].first) && (mv.str.len != zwr_sub->subsc_list[n].first->str.len || memcmp(mv.str.addr, zwr_sub->subsc_list[n].first->str.addr, mv.str.len)))) do_lev = FALSE; } } if (do_lev && zwr_sub->subsc_list[n].second) { if (MV_IS_CANONICAL(&mv)) { if (MV_IS_CANONICAL(zwr_sub->subsc_list[n].second) && eb_less(zwr_sub->subsc_list[n].second, &mv)) do_lev = FALSE; } else { if (MV_IS_CANONICAL(zwr_sub->subsc_list[n].second) || (!follow(zwr_sub->subsc_list[n].second, &mv) && (mv.str.len != zwr_sub->subsc_list[n].second->str.len || memcmp(mv.str.addr, zwr_sub->subsc_list[n].second->str.addr, mv.str.len)))) do_lev = FALSE; } if (!do_lev) break; } } } if (do_lev) { end1 = gv_currkey->end; prev1 = gv_currkey->prev; save_gv_last_subsc_null = TREF(gv_last_subsc_null); gvzwr_var((int4)MV_FORCE_INTD(&subdata), n + 1); TREF(gv_last_subsc_null) = save_gv_last_subsc_null; gv_currkey->end = end1; gv_currkey->prev = prev1; gv_currkey->base[end1] = 0; } if (1 == seen_null) { assert(TREF(gv_last_subsc_null)); TREF(gv_last_subsc_null) = FALSE; seen_null = 2; /* set flag to indicate null sub processed */ } op_gvorder(&mv); /* When null subscript is in the middle, but with standard collation null subscripts can not be in the middle, so don't need to be worried */ if (0 == mv.str.len) { if (NEVER == gv_cur_region->null_subs || seen_null || gv_cur_region->std_null_coll) break; else { seen_null = 1; /* set flag to indicate processing null sub */ op_gvnaked(VARLSTCNT(1) &mv); op_gvdata(&subdata); if (!MV_FORCE_INTD(&subdata)) break; } } else { op_gvnaked(VARLSTCNT(1) &mv); op_gvdata(&subdata); } } } gv_currkey->end = end; gv_currkey->prev = prev; gv_currkey->base[end] = 0; } fis-gtm-V7.0-005/sr_port/gvzwrite_ch.c0000755000032200000250000000121214342376331016541 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "error.h" #include "gvzwrite_clnup.h" CONDITION_HANDLER(gvzwrite_ch) { START_CH(TRUE); gvzwrite_clnup(); /* this routine is called by gvzwr_fini() too */ NEXTCH; } fis-gtm-V7.0-005/sr_port/gvzwrite_clnup.c0000755000032200000250000000347314342376331017303 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "gtm_stdio.h" #include "gtmio.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "zwrite.h" #include "error.h" #include "change_reg.h" #include "gvzwrite_clnup.h" #include "filestruct.h" #include "gdscc.h" #include "jnl.h" #include "io.h" #include "gvt_inline.h" GBLREF gv_namehead *gv_target; GBLREF gv_namehead *reset_gv_target; GBLREF gv_key *gv_currkey; GBLREF gd_region *gv_cur_region; GBLREF sgmnt_addrs *cs_addrs; GBLREF gvzwrite_datablk *gvzwrite_block; void gvzwrite_clnup(void) { gv_key *old; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; gv_cur_region = gvzwrite_block->gd_reg; change_reg(); assert(reset_gv_target == ((gv_namehead *)gvzwrite_block->old_targ)); if (NULL != gvzwrite_block->old_key) { old = (gv_key *)gvzwrite_block->old_key; memcpy(&gv_currkey->base[0], &old->base[0], old->end + 1); gv_currkey->end = old->end; gv_currkey->prev = old->prev; free(gvzwrite_block->old_key); gvzwrite_block->old_key = gvzwrite_block->old_targ = (unsigned char *)NULL; gvzwrite_block->subsc_count = 0; TREF(gv_last_subsc_null) = gvzwrite_block->gv_last_subsc_null; TREF(gv_some_subsc_null) = gvzwrite_block->gv_some_subsc_null; } RESET_GV_TARGET(DO_GVT_GVKEY_CHECK); } fis-gtm-V7.0-005/sr_port/gvzwrite_clnup.h0000755000032200000250000000115514342376331017303 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef GVZWRITE_CLNUP_H_INCLUDED #define GVZWRITE_CLNUP_H_INCLUDED void gvzwrite_clnup(void); #endif fis-gtm-V7.0-005/sr_port/h.mpt0000755000032200000250000000352714342376331015026 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 1987, 2003 Sanchez Computer Associates, 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %H ;GT.M %H utility - date and time conversions to and from $H format ;invoke with %DT in $H day format at %CDS to set %DAT mm/dd/yyyy ;invoke with %TM in $H time format at %CTS to set %TIM hh:mm:ss ;invoke with %DT in mm/dd/yy[yy] format at %CDN to set %DAT to $H form ;invoke with %TM in hh:mm:ss format at %CTN to set %TIM to $H format ;the labels without the % are corresponding functions q %CDS S %DAT=$$CDS(%DT) q CDS(dt) i dt'<0,dt<94658 q +$zd(dt,"MM")_"/"_+$zd(dt,"DD")_$zd(dt,"/YEAR") q "" ; %CTS s %TIM=$$CTS(%TM) q CTS(tm) i tm'<0,tm<86400 q $zd(","_tm,"24:60:SS") q "" ; %CDN s %DAT=$$CDN(%DT) q CDN(dt) n cc,dat,dd,mm,yy,dh,zd s mm=+dt,dd=$p(dt,"/",2),yy=$p(dt,"/",3),dat="" i mm<1 q "" i mm>13 q "" i dd<1 q "" s zd=$ZDATEFORM i $l(yy)<3 d . s dh=$H . s yy=yy+(100*$S('zd:19,(zd>1840)&($L(zd)=4):($E(zd,1,2)+$S($E(zd,3,4)'>yy:0,1:1)),1:$E($ZDATE(dh,"YEAR"),1,2))) ; 20th rolling current century i dd>$s(+mm'=2:$e(303232332323,mm)+28,yy#4:28,yy#100:29,yy#400:28,1:29) q "" s dat=yy-1841,mm=mm-1,cc=1 i dat<0 s dd=dd-1,cc=-1 s dat=dat\4*1461+(dat#4-$s(dat'<0:0,1:4)*365)+(mm*30)+$e(10112234455,mm)+dd-(yy-1800\100-(yy-1600\400)) i yy#4,mm>1 s dat=dat-cc i yy#100=0,mm<2,yy#400 s dat=dat+cc q dat ; %CTN s %TIM=$$CTN(%TM) q CTN(tm) n h,m,s s h=+tm,m=$p(tm,":",2),s=$p(tm,":",3) i h'<0,h<24,m'<0,m<60,s'<0,s<60 q h*60+m*60+s q "" fis-gtm-V7.0-005/sr_port/hashtab.h0000755000032200000250000001153114342376331015632 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef HASHTAB_COMMON_H #define HASHTAB_COMMON_H #define HASHTAB_COMPACT FALSE #define HASHTAB_NO_COMPACT TRUE #define HASHTAB_SPARE_TABLE FALSE #define HASHTAB_NO_SPARE_TABLE TRUE #define HT_VALUE_DUMMY ((void *) 1L) /* It is required not to have 0 or -1 for this macro. * This is passed when hashtable callers want to use hash table just for the keys. * Say, database code wants to create a list of blocks being read for a transactions. * There is no corresponding value associated. For that case HT_VALUE_DUMMY will be passed as "value". */ #define HT_DELETED_ENTRY ((void *) -1L) /* Note: We may need to change the above during 64-bit port */ #define HTENT_MARK_DELETED(tabent) (HT_DELETED_ENTRY == (tabent)->value) #define HT_LOAD_FACTOR 50 #define HT_REHASH_FACTOR (HT_LOAD_FACTOR/2) #define HT_REHASH_TABLE_SIZE(table) MIN(table->size, table->count * 4) #define INSERT_HTENT(table, tabent, hkey, value) \ MBSTART { \ if (HT_DELETED_ENTRY == (tabent)->value) \ (table)->del_count--; \ (tabent)->key = *hkey; \ (tabent)->value = value; \ (table)->count++; \ } MBEND #define COMPACT_NEEDED(table) ((!(table)->dont_compact) && (((table)->del_count > (table)->cmp_trigger_size) || \ (((table)->initial_size < (table)->size ) && ((table)->count < ((table)->cmp_trigger_size / 2))))) /* * This macro is used by callers outside of the hash table implementation to indicate * whether they will request the free of the hash table base at a later point in time or * if it should be released by the hash table implementation during an expansion/compaction. * They must call FREE_BASE_HASHTAB() later to release the base otherwise the memory * will be leaked. */ #define DEFER_BASE_REL_HASHTAB(table, deferit) \ MBSTART { \ (table)->defer_base_release = deferit; \ } MBEND /* * This macro is used by callers outside of the hash table implementation to indicate * that they are no longer using the hash table base. This function only provides a "hint" to the * hash table implementation, i.e., the base can now be freed when appropriate. This can * mean when this function is called if we are not keeping spare bases or at a potentially * much later time if we are using a spare base. */ #define FREE_BASE_HASHTAB(table, base) \ MBSTART { \ if ((table)->dont_keep_spare_table) \ free(base); \ } MBEND /* For string hashing, ELF hash was found to be the best during the V5.0-000 longnames project. * During V6.2-001, Murmur3 hash was found to be much better than ELF in terms of # of collisions. * So we are going with MMR hash for now. In addition, the 32-bit murmur3 hash reference implementation * we used gives different values for the same input on different endian machines which would not work * at least for triggers since we expect the trigger definition M code to hash to the same value on * different endian machines (this is needed so mupip endiancvt does not need to worry about changing * ^#t(.*TRHASH.*) nodes. Therefore we came up with a modified 32-bit murmur3 hash * implementation that is endian independent (gtmmrhash_32). See mmrhash.c for details. */ #ifdef UNIX #include "mmrhash.h" #define STR_HASH(KEY, LEN, HASH, SEED) gtmmrhash_32(KEY, LEN, SEED, (uint4 *)&HASH) /* The STR_PHASH* macros are the progressive variants of the STR_HASH macro. */ #define STR_PHASH_INIT(STATE, TOTLEN) HASH128_STATE_INIT(STATE, 0); TOTLEN = 0 #define STR_PHASH_PROCESS(STATE, TOTLEN, KEY, LEN) gtmmrhash_128_ingest(&STATE, KEY, LEN); TOTLEN += LEN #define STR_PHASH_RESULT(STATE, TOTLEN, OUT4) \ MBSTART { \ gtm_uint16 out16; \ \ gtmmrhash_128_result(&STATE, TOTLEN, &out16); \ OUT4 = (uint4)out16.one; \ } MBEND #else #define STR_HASH ELF_HASH #endif #define ELF_HASH(sptr, len, hash, init_hashval) \ MBSTART { \ uint4 tempint; \ char *curr, *top; \ uint4 hcode; \ for (hcode = init_hashval, curr = sptr, top = sptr + len; curr < top; curr++) \ { \ hcode = (hcode << 4) + *curr; \ if (tempint = (hcode & 0xF0000000)) \ hcode ^= tempint >> 24; \ hcode &= ~tempint; \ } \ hash = hcode; \ } MBEND /* Use the ended hash table size. Currently used only by MUPIP ROLLBACK/RECOVER */ #define USE_EXTENDED_HASHTAB \ MBSTART { \ ht_sizes = (int *)&extended_ht_sizes; \ } MBEND #endif fis-gtm-V7.0-005/sr_port/hashtab_addr.c0000644000032200000250000000136414342376331016617 0ustar librarygtc/**************************************************************** * * * Copyright 2009 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 "min_max.h" #include "gtm_string.h" #include "error.h" #include "send_msg.h" #include "gtmmsg.h" #include "hashtab.h" #include "hashtab_addr.h" #define ADDR_HASH /* The below include generates the hash table routines for the "addr" hash type */ #include "hashtab_implementation.h" fis-gtm-V7.0-005/sr_port/hashtab_addr.h0000644000032200000250000000710614342376331016624 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2018 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. * * * ****************************************************************/ #ifndef HASHTAB_ADDR_H #define HASHTAB_ADDR_H typedef struct { char *key; /* Note this is the actual key, not its address */ void *value; } ht_ent_addr; typedef struct hash_table_addr_struct { ht_ent_addr *base; /* base of array of hent_* entries */ ht_ent_addr *top; /* top of array of hent_* entries */ ht_ent_addr *spare_base; /* spare array of hent_* entries */ sm_uc_ptr_t entry_passed_thru;/* Bit vector used to determine whether a particular */ /* ht_ent has been involved in a collision (meaning that */ /* this value can't be marked empty on delete */ unsigned int size; /* Hash table size */ unsigned int initial_size; /* Hash table initial size */ unsigned int spare_base_size;/* size of spare array */ unsigned int count; /* Number of valid entries */ unsigned int del_count; /* Number of entries marked deleted. */ unsigned int exp_trigger_size;/* When exp_trigger_size entried are used, expand table */ unsigned int cmp_trigger_size;/* When cmp_trigger_size reached compact table */ boolean_t dont_compact; /* if set, never perform compaction */ boolean_t dont_keep_spare_table; /* if set, don't keep a spare table */ boolean_t defer_base_release; /* if set don't release base, caller will free_base...() later */ boolean_t active; /* indicates that the table "ready to use"; if not, we need to activate it */ } hash_table_addr; #define HTENT_EMPTY_ADDR(tabent, type, htvalue) (!(htvalue = (type *)(tabent)->value)) #define HTENT_MARK_EMPTY_ADDR(tabent) (tabent)->value = (void *) 0L #define HTENT_VALID_ADDR(tabent, type, htvalue) ((!HTENT_EMPTY_ADDR(tabent, type, htvalue)) && (HT_DELETED_ENTRY != htvalue)) /* Do not downcast when INT8_HASH, 8 byte int, is defined */ #ifdef INT8_HASH #define HASHTAB_UINTCAST(X) X #else #define HASHTAB_UINTCAST(X) UINTCAST(X) #endif #ifdef GTM64 # define COMPUTE_HASH_ADDR(hkey, hash) \ hash = HASHTAB_UINTCAST(((((gtm_uint64_t)(*hkey)) & 0xFFFFFFFF) ^ (((gtm_uint64_t)(*hkey)) >> 31))) #else # define COMPUTE_HASH_ADDR(hkey, hash) hash = ((uint4)(*hkey)) #endif /* Prototypes for addr hash routines. See hashtab_implementation.h for detail interface and implementation */ void init_hashtab_addr(hash_table_addr *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table); void expand_hashtab_addr(hash_table_addr *table, int minsize); boolean_t add_hashtab_addr(hash_table_addr *table, char **key, void *value, ht_ent_addr **tabentptr); void *lookup_hashtab_addr(hash_table_addr *table, char **key); void delete_hashtab_ent_addr(hash_table_addr *table, ht_ent_addr *tabent); boolean_t delete_hashtab_addr(hash_table_addr *table, char **key); void free_hashtab_addr(hash_table_addr *table); void reinitialize_hashtab_addr(hash_table_addr *table); void compact_hashtab_addr(hash_table_addr *table); sm_uc_ptr_t copy_hashtab_to_buffer_addr(hash_table_addr *table, sm_uc_ptr_t buffer, int (*copy_entry_to_buffer)(ht_ent_addr *, sm_uc_ptr_t)); hash_table_addr *activate_hashtab_in_buffer_addr(sm_uc_ptr_t buffer, int (*copy_entry_from_buffer)(ht_ent_addr *, sm_uc_ptr_t)); #endif /* HASHTAB_ADDR_H */ fis-gtm-V7.0-005/sr_port/hashtab_implementation.h0000755000032200000250000007360314342376331020747 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* Uniform Hash Implementation Hash table code supports the following data types as key: a) int4 b) int8 c) UINTPTR_T d) object code e) variable name. f) local process address (supported via define using either int4 or int8 as type Using pre-processor following four C files will expand five sets of routines. a) hashtab_int4.c b) hashtab_int8.c c) hashtab_addr.c d) hashtab_mname.c e) hashtab_objcode.c Restrictions : We assumed that no user of hash needs to add "key, value" pair where both are null. We examined that GT.M does not need to have such cases. We can add 0 as valid data for int4 and int8, however, it must always have non-zero value. We can add 0 length string as "key" in objcode, however, it must always have non-zero length value. We know object code cannot be 0 length, so even object source (key) is of 0 length, we are fine. GT.M cannot have 0 length mname. So "key" for mname cannot be 0 length. (If we want to remove above restriction, an extra field is needed for HT_ENT) */ #include "mdef.h" #include "gtm_malloc.h" /* For raise_gtmmemory_error() definition */ #include "bit_set.h" #include "gtmio.h" #include "have_crit.h" #include "caller_id.h" GBLREF int *ht_sizes; #define DEBUGHASHTABLE 0 #define HASHTAB_CALLERID ((unsigned char *)caller_id(0)) /* For manual calls to raise_gtmmemory_error() */ #define SET_GTMMEMORY_ERROR_VARS(SIZE, ERROR) \ { \ GBLREF size_t gtmMallocErrorSize; \ GBLREF unsigned char *gtmMallocErrorCallerid;\ GBLREF int gtmMallocErrorErrno; \ \ gtmMallocErrorSize = SIZE; \ gtmMallocErrorCallerid = HASHTAB_CALLERID; \ gtmMallocErrorErrno = ERROR; \ } #if defined(INT4_HASH) # define HT_KEY_T uint4 # define HT_ENT ht_ent_int4 # define HASH_TABLE hash_table_int4 # define HTENT_KEY_MATCH(tabent, hkey) ((tabent)->key == (*hkey)) # define FIND_HASH(hkey, hash) COMPUTE_HASH_INT4(hkey, hash) # define HTENT_EMPTY HTENT_EMPTY_INT4 # define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_INT4 # define HTENT_VALID HTENT_VALID_INT4 # define INIT_HASHTAB init_hashtab_int4 # define INIT_HASHTAB_INTL init_hashtab_intl_int4 # define EXPAND_HASHTAB expand_hashtab_int4 # define ADD_HASHTAB add_hashtab_int4 # define ADD_HASHTAB_INTL add_hashtab_intl_int4 # define LOOKUP_HASHTAB lookup_hashtab_int4 # define DELETE_HASHTAB_ENT delete_hashtab_ent_int4 # define DELETE_HASHTAB delete_hashtab_int4 # define FREE_HASHTAB free_hashtab_int4 # define REINITIALIZE_HASHTAB reinitialize_hashtab_int4 # define COMPACT_HASHTAB compact_hashtab_int4 # define COPY_HASHTAB_TO_BUFFER copy_hashtab_to_buffer_int4 # define ACTIVATE_HASHTAB_IN_BUFFER activate_hashtab_in_buffer_int4 #elif defined(INT8_HASH) # define HT_KEY_T gtm_uint64_t # define HT_ENT ht_ent_int8 # define HASH_TABLE hash_table_int8 # define HTENT_KEY_MATCH(tabent, hkey) ((tabent)->key == (*hkey)) # define FIND_HASH(hkey, hash) COMPUTE_HASH_INT8(hkey, hash) # define HTENT_EMPTY HTENT_EMPTY_INT8 # define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_INT8 # define HTENT_VALID HTENT_VALID_INT8 # define INIT_HASHTAB init_hashtab_int8 # define INIT_HASHTAB_INTL init_hashtab_intl_int8 # define EXPAND_HASHTAB expand_hashtab_int8 # define ADD_HASHTAB add_hashtab_int8 # define ADD_HASHTAB_INTL add_hashtab_intl_int8 # define LOOKUP_HASHTAB lookup_hashtab_int8 # define DELETE_HASHTAB_ENT delete_hashtab_ent_int8 # define DELETE_HASHTAB delete_hashtab_int8 # define FREE_HASHTAB free_hashtab_int8 # define REINITIALIZE_HASHTAB reinitialize_hashtab_int8 # define COMPACT_HASHTAB compact_hashtab_int8 # define COPY_HASHTAB_TO_BUFFER copy_hashtab_to_buffer_int8 # define ACTIVATE_HASHTAB_IN_BUFFER activate_hashtab_in_buffer_int8 #elif defined(ADDR_HASH) # define HT_KEY_T char * # define HT_ENT ht_ent_addr # define HASH_TABLE hash_table_addr # define HTENT_KEY_MATCH(tabent, hkey) ((tabent)->key == (*hkey)) # define FIND_HASH(hkey, hash) COMPUTE_HASH_ADDR(hkey, hash) # define HTENT_EMPTY HTENT_EMPTY_ADDR # define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_ADDR # define HTENT_VALID HTENT_VALID_ADDR # define INIT_HASHTAB init_hashtab_addr # define INIT_HASHTAB_INTL init_hashtab_intl_addr # define EXPAND_HASHTAB expand_hashtab_addr # define ADD_HASHTAB add_hashtab_addr # define ADD_HASHTAB_INTL add_hashtab_intl_addr # define LOOKUP_HASHTAB lookup_hashtab_addr # define DELETE_HASHTAB_ENT delete_hashtab_ent_addr # define DELETE_HASHTAB delete_hashtab_addr # define FREE_HASHTAB free_hashtab_addr # define REINITIALIZE_HASHTAB reinitialize_hashtab_addr # define COMPACT_HASHTAB compact_hashtab_addr # define COPY_HASHTAB_TO_BUFFER copy_hashtab_to_buffer_addr # define ACTIVATE_HASHTAB_IN_BUFFER activate_hashtab_in_buffer_addr #elif defined(MNAME_HASH) # define HT_KEY_T mname_entry # define HT_ENT ht_ent_mname # define HASH_TABLE hash_table_mname # define HTENT_KEY_MATCH(tabent, hkey) \ ( ((tabent)->key.hash_code == (hkey)->hash_code) \ && ((tabent)->key.var_name.len == (hkey)->var_name.len) \ && (0 == memcmp((tabent)->key.var_name.addr, (hkey)->var_name.addr, (hkey)->var_name.len)) \ ) # define FIND_HASH(hkey, hash) {assert((hkey)->hash_code); hash = (hkey)->hash_code;} /* Note: FIND_HASH for mname does not compute hash_code. Callers must make sure it is already computed. * FIND_HASH for objcode or int4 or int8 computes hash code * for every function call of add or lookup or delete. */ # define HTENT_EMPTY HTENT_EMPTY_MNAME # define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_MNAME # define HTENT_VALID HTENT_VALID_MNAME # define INIT_HASHTAB init_hashtab_mname # define INIT_HASHTAB_INTL init_hashtab_intl_mname # define EXPAND_HASHTAB expand_hashtab_mname # define ADD_HASHTAB add_hashtab_mname # define ADD_HASHTAB_INTL add_hashtab_intl_mname # define LOOKUP_HASHTAB lookup_hashtab_mname # define DELETE_HASHTAB_ENT delete_hashtab_ent_mname # define DELETE_HASHTAB delete_hashtab_mnamen # define FREE_HASHTAB free_hashtab_mname # define REINITIALIZE_HASHTAB reinitialize_hashtab_mname # define COMPACT_HASHTAB compact_hashtab_mname # define COPY_HASHTAB_TO_BUFFER copy_hashtab_to_buffer_mname # define ACTIVATE_HASHTAB_IN_BUFFER activate_hashtab_in_buffer_mname #elif defined(STRING_HASH) # define HT_KEY_T stringkey # define HT_ENT ht_ent_str # define HASH_TABLE hash_table_str # define HTENT_KEY_MATCH(tabent, hkey) \ (((tabent)->key.hash_code == (hkey)->hash_code) \ && ((tabent)->key.str.len == (hkey)->str.len) \ && (0 == memcmp((tabent)->key.str.addr, (hkey)->str.addr, (hkey)->str.len)) \ ) # define FIND_HASH(hkey, hash) hash = (hkey)->hash_code /* Note: FIND_HASH for str does not compute hash_code. Callers must make sure it is already computed */ # define HTENT_EMPTY HTENT_EMPTY_STR # define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_STR # define HTENT_VALID HTENT_VALID_STR # define INIT_HASHTAB init_hashtab_str # define INIT_HASHTAB_INTL init_hashtab_intl_str # define EXPAND_HASHTAB expand_hashtab_str # define ADD_HASHTAB add_hashtab_str # define ADD_HASHTAB_INTL add_hashtab_intl_str # define LOOKUP_HASHTAB lookup_hashtab_str # define DELETE_HASHTAB_ENT delete_hashtab_ent_str # define DELETE_HASHTAB delete_hashtab_str # define FREE_HASHTAB free_hashtab_str # define REINITIALIZE_HASHTAB reinitialize_hashtab_str # define COMPACT_HASHTAB compact_hashtab_str # define COPY_HASHTAB_TO_BUFFER copy_hashtab_to_buffer_str # define ACTIVATE_HASHTAB_IN_BUFFER activate_hashtab_in_buffer_str #elif defined (OBJCODE_HASH) # define HT_KEY_T icode_str # define HT_ENT ht_ent_objcode # define HASH_TABLE hash_table_objcode # define HTENT_KEY_MATCH(tabent, hkey) \ (((tabent)->key.code == (hkey)->code) \ && ((tabent)->key.str.len == (hkey)->str.len) \ && (0 == memcmp((tabent)->key.str.addr, (hkey)->str.addr, (hkey)->str.len)) \ ) # define FIND_HASH(hkey, hash) COMPUTE_HASH_OBJCODE(hkey, hash) # define HTENT_EMPTY HTENT_EMPTY_OBJCODE # define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_OBJCODE # define HTENT_VALID HTENT_VALID_OBJCODE # define INIT_HASHTAB init_hashtab_objcode # define INIT_HASHTAB_INTL init_hashtab_intl_objcode # define EXPAND_HASHTAB expand_hashtab_objcode # define ADD_HASHTAB add_hashtab_objcode # define ADD_HASHTAB_INTL add_hashtab_intl_objcode # define LOOKUP_HASHTAB lookup_hashtab_objcode # define DELETE_HASHTAB_ENT delete_hashtab_ent_objcode # define DELETE_HASHTAB delete_hashtab_objcode # define FREE_HASHTAB free_hashtab_objcode # define REINITIALIZE_HASHTAB reinitialize_hashtab_objcode # define COMPACT_HASHTAB compact_hashtab_objcode # define COPY_HASHTAB_TO_BUFFER copy_hashtab_to_buffer_objcode # define ACTIVATE_HASHTAB_IN_BUFFER activate_hashtab_in_buffer_objcode #else #error undefined hash #endif #if DEBUGHASHTABLE /* Debug FPRINTF with pre and post requisite flushing of appropriate streams */ #define DBGHASHTAB(x) {flush_pio(); FPRINTF x; FFLUSH(stderr);} #else #define DBGHASHTAB(x) #endif /* We use DOUBLE HASHING algorithm. After first collision we calculate * rehash factor (rhfact) that is a function of the hash value (even though for * correctness purposes any number from 1 to prime-1 should be fine) to * avoid primary and secondary clustering. * The SET_REHASH_INDEX is actually equivalent to ht_index = (ht_index + rhfact) % prime; */ #define SET_REHASH_FACTOR(rhfact, hash, prime) (rhfact) = (1 + ((hash) % ((prime) - 1))) #define SET_REHASH_INDEX(ht_index, rhfact, prime) \ assert((rhfact) < (prime)); \ assert((ht_index) < (prime)); \ (ht_index) += (rhfact); \ if ((ht_index) >= (prime)) \ (ht_index) -= (prime); #define RETURN_IF_ADDED(table, tabent, hkey, value) \ { \ void *dummy; \ if (HTENT_EMPTY(tabent, void, dummy)) \ { \ if (NULL != first_del_ent) \ tabent = first_del_ent; \ INSERT_HTENT(table, tabent, hkey, value); \ return TRUE; \ } \ if (!HTENT_MARK_DELETED(tabent)) \ { \ if (HTENT_KEY_MATCH(tabent, hkey)) \ return FALSE; /* valid and matched */ \ } else if (NULL == first_del_ent) \ first_del_ent = tabent; \ } #define RETURN_IF_LOOKUP_DONE(tabent, hkey) \ { \ void *dummy; \ if (HTENT_EMPTY(tabent, void, dummy)) \ return NULL; \ if (!HTENT_MARK_DELETED(tabent) && HTENT_KEY_MATCH(tabent, hkey)) \ return tabent; /* valid and matched */ \ } #define DELETE_HTENT(table, tabent) \ { \ uint4 entry_num; \ sm_uc_ptr_t ptr; \ \ entry_num = (uint4)((tabent) - (table)->base); \ /* Compute offset into bitmap for this entry */ \ ptr = (table)->entry_passed_thru + (entry_num / BITS_PER_UCHAR); \ if ((1 << (entry_num & 7)) & *ptr) \ { \ (tabent)->value = HT_DELETED_ENTRY; \ (table)->del_count++; \ } else \ HTENT_MARK_EMPTY(tabent); \ (table)->count--; \ assert(((table)->count + (table)->del_count) <= (table)->size); \ } #define RETURN_IF_DELETED(table, tabent, hkey) \ { \ void *dummy; \ if (HTENT_EMPTY(tabent, void, dummy)) \ return FALSE; \ if (!HTENT_MARK_DELETED(tabent) && HTENT_KEY_MATCH(tabent, hkey)) \ { \ DELETE_HTENT(table, tabent); \ if (COMPACT_NEEDED(table)) \ COMPACT_HASHTAB(table); \ return TRUE; \ } \ } #define HT_FIELDS_COMMON_INIT(table) \ table->exp_trigger_size = (double)table->size * HT_LOAD_FACTOR / 100.0; \ table->cmp_trigger_size = (double)table->size * HT_REHASH_FACTOR / 100.0; \ table->active = TRUE; \ table->count = table->del_count = 0; /* Prototypes */ void INIT_HASHTAB(HASH_TABLE *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table); STATICFNDCL void INIT_HASHTAB_INTL(HASH_TABLE *table, int minsize, HASH_TABLE *old_table); void EXPAND_HASHTAB(HASH_TABLE *table, int minsize); boolean_t ADD_HASHTAB(HASH_TABLE *table, HT_KEY_T *key, void *value, HT_ENT **tabentptr); STATICFNDCL boolean_t ADD_HASHTAB_INTL(HASH_TABLE *table, HT_KEY_T *key, void *value, HT_ENT **tabentptr, boolean_t changing_table_size); void *LOOKUP_HASHTAB(HASH_TABLE *table, HT_KEY_T *key); void DELETE_HASHTAB_ENT(HASH_TABLE *table, HT_ENT *tabent); boolean_t DELETE_HASHTAB(HASH_TABLE *table, HT_KEY_T *key); void FREE_HASHTAB(HASH_TABLE *table); void REINITIALIZE_HASHTAB(HASH_TABLE *table); void COMPACT_HASHTAB(HASH_TABLE *table); error_def(ERR_HTOFLOW); error_def(ERR_HTSHRINKFAIL); /* This is used by external callers to initially setup the hash table. */ void INIT_HASHTAB(HASH_TABLE *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table) { int index; for (index = 0, table->initial_size = ht_sizes[index]; table->initial_size && table->initial_size < minsize; index++) table->initial_size = ht_sizes[index]; table->initial_size = table->initial_size ? table->initial_size : minsize; table->dont_compact = dont_compact; table->dont_keep_spare_table = dont_keep_spare_table; table->defer_base_release = FALSE; table->active = TRUE; INIT_HASHTAB_INTL(table, minsize, NULL); } /* This routine initializes hash table. It must be called once before hashing can be used. Note that the ht_sizes array is defined in mtables.c. A NULL old_table pointer means that the table is being setup for the first time. */ STATICFNDEF void INIT_HASHTAB_INTL(HASH_TABLE *table, int minsize, HASH_TABLE *old_table) { unsigned int cur_ht_size, prior_size; int index; boolean_t dont_keep_spare_table; DBGHASHTAB((stderr, "INIT_HASHTAB:table(%lx) minsize(%d) old_table(%lx)\n", table, minsize, old_table)); /* If this is the first time the hash table is being initialized (old_table == NULL), then look up the * actual hash table size in ht_sizes based on the requested size (minsize). * * We dont want the hash table to shrink too fast so if we are changing the size of an existing hash table: * 1) if the requested size is not smaller than half of the previous size: * a) pick the actual size from ht_sizes based on the requested size. * 2) if the requested size is smaller than half of the previous size: * b) pick the previous entry (from the previous size (old_table->size) in ht_sizes. */ if ((NULL == old_table) || (minsize > (old_table->size / 2))) { for (index = 0, cur_ht_size = ht_sizes[index]; cur_ht_size && cur_ht_size < minsize; index++) cur_ht_size = ht_sizes[index]; } else /* don't shrink too fast ! */ { prior_size = ht_sizes[0]; for (index = 1, cur_ht_size = ht_sizes[index]; cur_ht_size && cur_ht_size < old_table->size; index++) { cur_ht_size = ht_sizes[index]; prior_size = ht_sizes[index-1]; } cur_ht_size = prior_size; cur_ht_size = (cur_ht_size > old_table->initial_size) ? cur_ht_size : old_table->initial_size; } if (cur_ht_size) { DBGHASHTAB((stderr, "INIT_HASHTAB:table size will be (%d) for table(%lx)\n", cur_ht_size, old_table?old_table:table)); table->base = NULL; table->spare_base = NULL; table->spare_base_size = 0; /* a fresh new hash table */ /* If this is is an initialization from a caller outside of the hash table implementation then * old_table == NULL since there is no previous hash table. In this case the external versions of * INIT_HASHTAB will setup table with values for dont_compact and dont_keep_spare_table. Otherwise, * we can use them from the old_table. */ dont_keep_spare_table = old_table ? old_table->dont_keep_spare_table:table->dont_keep_spare_table; if (!dont_keep_spare_table) { DBGHASHTAB((stderr, "INIT_HASHTAB: old_table(%lx)\n", old_table)); if (NULL != old_table) { DBGHASHTAB((stderr, "INIT_HASHTAB: cur_ht_size(%d), spare_base_size(%d)\n", cur_ht_size, old_table->spare_base_size)); if (old_table->spare_base_size == cur_ht_size) { /* We can use the spare table since it is the size we would have malloc'd */ table->base = old_table->spare_base; DBGHASHTAB((stderr, "INIT_HASHTAB: use spare table: base(%lx)\n", table->base)); /* We no longer have a spare */ old_table->spare_base = NULL; old_table->spare_base_size = 0; } else /* no luck on the reuse thing */ if (NULL != old_table->spare_base) /* so free it if it exists */ { DBGHASHTAB((stderr, "INIT_HASHTAB: table(%lx): free spare_base(%lx)\n", old_table, old_table->spare_base)); free(old_table->spare_base); old_table->spare_base = NULL; old_table->spare_base_size = 0; } } } if (NULL == table->base) { /* Let's make sure we have a HT_ENT table. We are here either thru dont_keep_spare_table, old_table == NULL, or wrong-sized spare */ table->base = (void *)malloc((cur_ht_size * SIZEOF(HT_ENT)) + ROUND_UP(cur_ht_size, BITS_PER_UCHAR)); DBGHASHTAB((stderr, "INIT_HASHTAB: malloc a new table: table(%lx) base(%lx)\n", old_table?old_table:table, table->base)); } memset((char *)table->base, 0, (cur_ht_size * SIZEOF(HT_ENT)) + ROUND_UP(cur_ht_size, BITS_PER_UCHAR)); table->size = cur_ht_size; if (NULL != old_table) { table->initial_size = old_table->initial_size; table->dont_compact = old_table->dont_compact; table->dont_keep_spare_table = old_table->dont_keep_spare_table; table->defer_base_release = old_table->defer_base_release; } table->top = table->base + cur_ht_size; table->entry_passed_thru = (sm_uc_ptr_t) table->top; DBGHASHTAB((stderr, "INIT_HASHTAB: entry_passed_thru points to (%lx)\n", table->entry_passed_thru)); HT_FIELDS_COMMON_INIT(table); } else { DBGHASHTAB((stderr, "INIT_HASHTAB:HTOFLOW: minsize(%d) cur_ht_size(%d)\n", minsize, cur_ht_size)); SET_GTMMEMORY_ERROR_VARS(minsize, ERR_HTOFLOW); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_HTOFLOW, 1, minsize); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_HTOFLOW, 1, minsize); } } /* Description: Expand the hash table with at least minsize. This can do either expansion or compaction, which depends on old table size and minsize passed. It creates a new table and move old element to new table. It deallocate old table entries */ void EXPAND_HASHTAB(HASH_TABLE *table, int minsize) { HASH_TABLE newtable, temptab; HT_ENT *tabent, *topent, *dummy; boolean_t added; void *htval; assert(TRUE == table->active); CONDITION_HANDLER(hashtab_rehash_ch); ESTABLISH(hashtab_rehash_ch); DBGHASHTAB((stderr, "EXPAND_HASHTAB:ENTER: table: table(%lx) base (%lx), spare_base(%lx), spare_base_size(%d), \n", table, table->base, table->spare_base, table->spare_base_size)); /* The next line keeps the HP-UX Itanium compiler in pro happy. This initialization is done in INIT_HASHTAB_INTL* * but this line is placed here to appease the compiler. */ newtable.dont_keep_spare_table = table->dont_keep_spare_table; INIT_HASHTAB_INTL(&newtable, minsize, table); REVERT; if (0 < table->count) /* if no active entries then nothing to move */ for (tabent = table->base, topent = table->top; tabent < topent; tabent++) { if (HTENT_VALID(tabent, void, htval)) { /* Place location of new ht_ent entry into value location of existing ht entry */ added = ADD_HASHTAB_INTL(&newtable, &tabent->key, htval, (HT_ENT **)&tabent->value, TRUE); assert(added); } } if (!table->defer_base_release && table->dont_keep_spare_table) { DBGHASHTAB((stderr, "EXPAND_HASHTAB:free base (%lx) \n", table->base)); free(table->base); /* Deallocate old table entries */ } if (!table->dont_keep_spare_table) { temptab.spare_base = table->base; /* let's keep a spare in case we just have to clear the DELETED entries */ temptab.spare_base_size = table->size; } *table = newtable; if (table->dont_keep_spare_table) { table->spare_base = NULL; table->spare_base_size = 0; } else { table->spare_base = temptab.spare_base; /* let's keep a spare in case we just have to clear the DELETED entries */ table->spare_base_size = temptab.spare_base_size; } DBGHASHTAB((stderr, "EXPAND_HASHTAB:EXIT: table: table(%lx ) base (%lx), spare_base(%lx), spare_base_size(%d) \n", table, table->base, table->spare_base, table->spare_base_size)); } /* Description: Adds a key and corresponding value in hash table. If key is already present, return false. If key is not present, it adds the entry and returns true. As a side-effect tabent points to the matched entry or added entry */ /* This flavor is used by external caller (outside of the hash table implementation */ boolean_t ADD_HASHTAB(HASH_TABLE *table, HT_KEY_T *key, void *value, HT_ENT **tabentptr) { return ADD_HASHTAB_INTL(table, key, value, tabentptr, FALSE); } /* This flavor is used by internal callers, for example when adding entries during a change of hash table size. */ STATICFNDEF boolean_t ADD_HASHTAB_INTL(HASH_TABLE *table, HT_KEY_T *key, void *value, HT_ENT **tabentptr, boolean_t changing_table_size) { #ifdef INT8_HASH gtm_uint64_t hash, ht_index, save_ht_index, prime, rhfact; #else uint4 hash, ht_index, save_ht_index, prime, rhfact; #endif /* INT8_HASH */ HT_ENT *oldbase, *first_del_ent, *tabbase; assert(TRUE == table->active); if (!changing_table_size && (table->count >= table->exp_trigger_size)) { oldbase = table->base; EXPAND_HASHTAB(table, table->size + 1); if (oldbase == table->base) /* expansion failed */ { if (table->exp_trigger_size >= table->size) /* Note this error routine will use the memory error parameters recorded when the memory error was first raised by EXPAND_HASHTAB() above so the error will appear as if it had occurred during that expansion attempt. */ raise_gtmmemory_error(); table->exp_trigger_size = table->size; } } first_del_ent = NULL; tabbase = &table->base[0]; prime = table->size; FIND_HASH(key, hash); ht_index = (int) (hash % prime); *tabentptr = tabbase + ht_index; RETURN_IF_ADDED(table, *tabentptr, key, value); /* We are here because collision happened. Do collision resolution */ # ifdef INT8_HASH assert(MAXUINT4 > ht_index); # endif bit_set(ht_index, table->entry_passed_thru); save_ht_index = ht_index; SET_REHASH_FACTOR(rhfact, hash, prime); SET_REHASH_INDEX(ht_index, rhfact, prime); do { *tabentptr = tabbase + ht_index; RETURN_IF_ADDED(table, *tabentptr, key, value); # ifdef INT8_HASH assert(MAXUINT4 > ht_index); # endif bit_set(ht_index, table->entry_passed_thru); SET_REHASH_INDEX(ht_index, rhfact, prime); } while(ht_index != save_ht_index); /* All entries either deleted or used. No empty frame found */ if (NULL != first_del_ent) { /* There was a deleted one we could use - reuse the deleted frame */ *tabentptr = first_del_ent; INSERT_HTENT(table, *tabentptr, key, value); return TRUE; } assertpro(FALSE); return FALSE; /* to prevent warnings */ } /* * Returns pointer to the value corresponding to key, if found. * Otherwise, it returns null. */ void *LOOKUP_HASHTAB(HASH_TABLE *table, HT_KEY_T *key) { # ifdef INT8_HASH gtm_uint64_t hash, ht_index, save_ht_index, prime, rhfact; # else uint4 hash, ht_index, save_ht_index, prime, rhfact; # endif /* INT8_HASH */ HT_ENT *tabent, *tabbase; assert(TRUE == table->active); tabbase = &table->base[0]; prime = table->size; FIND_HASH(key, hash); ht_index = hash % prime; tabent = tabbase + ht_index; RETURN_IF_LOOKUP_DONE(tabent, key); /* We are here because collision happened. Do collision resolution */ save_ht_index = ht_index; SET_REHASH_FACTOR(rhfact, hash, prime); SET_REHASH_INDEX(ht_index, rhfact, prime); do { tabent = tabbase + ht_index; RETURN_IF_LOOKUP_DONE(tabent, key); SET_REHASH_INDEX(ht_index, rhfact, prime); } while(ht_index != save_ht_index); return (void *)NULL; } /* Description: Deletes hash table entry from hash table (whether it was active or not). The function version is for callers outside of the hash table implementation. */ void DELETE_HASHTAB_ENT(HASH_TABLE *table, HT_ENT *tabent) { DELETE_HTENT(table, tabent); } /* * Returns TRUE if * 1) key is found and deleted successfully * or * 2) already key was marked deleted. * Otherwise, it returns FALSE * Deletion is done by marking value to HT_DELETED_ENTRY. * If there are too many deleted entry, we call expand_hashtab() to do the * compaction eliminating entries marked HT_DELETED_ENTRY * Compaction will save memory and also cause LOOKUP_HASHTAB to run faster. */ boolean_t DELETE_HASHTAB(HASH_TABLE *table, HT_KEY_T *key) { # ifdef INT8_HASH gtm_uint64_t hash, ht_index, save_ht_index, prime, rhfact; # else uint4 hash, ht_index, save_ht_index, prime, rhfact; # endif /* INT8_HASH */ HT_ENT *tabent, *tabbase; assert(TRUE == table->active); tabbase = &table->base[0]; prime = table->size; FIND_HASH(key, hash); ht_index = hash % prime; tabent = tabbase + ht_index; RETURN_IF_DELETED(table, tabent, key); /* We are here because collision happened. Do collision resolution */ save_ht_index = ht_index; SET_REHASH_FACTOR(rhfact, hash, prime); SET_REHASH_INDEX(ht_index, rhfact, prime); do { tabent = tabbase + ht_index; RETURN_IF_DELETED(table, tabent, key); SET_REHASH_INDEX(ht_index, rhfact, prime); } while(ht_index != save_ht_index); return FALSE; } /* * free memory occupied by hash table. */ void FREE_HASHTAB(HASH_TABLE *table) { assert(TRUE == table->active); if (table->base) { DBGHASHTAB((stderr, "FREE_HASHTAB:free table(%lx): base (%lx)\n", table, table->base)); free(table->base); } table->base = NULL; if (table->spare_base) { DBGHASHTAB((stderr, "FREE_HASHTAB:free table(%lx): spare_base (%lx)\n", table, table->spare_base)); free(table->spare_base); } table->spare_base = NULL; table->spare_base_size = 0; } /* * Reinitialize hash table */ void REINITIALIZE_HASHTAB(HASH_TABLE *table) { assert(TRUE == table->active); memset((char *)table->base, 0, (table->size * SIZEOF(HT_ENT)) + ((table->size / BITS_PER_UCHAR) + 1)); HT_FIELDS_COMMON_INIT(table); } /* * Compact hashtable removing entries marked deleted. Note that this is necessary because * of the search algorithm in ADD_HASHTAB which needs to find if a key exists before it can * add a new entry. It keeps searching until it either finds the key, finds an empty (never * used) entry or until it searches the entire table. So we need to replentish the supply of * never used nodes. */ void COMPACT_HASHTAB(HASH_TABLE *table) { HT_ENT *oldbase; assert(TRUE == table->active); DBGHASHTAB((stderr, "COMPACT_HASHTAB: table(%lx)\n", table)); if (!table->dont_compact) { oldbase = (table)->base; EXPAND_HASHTAB(table, HT_REHASH_TABLE_SIZE(table)); if (oldbase == (table)->base) /* rehash failed */ { /* We will continue but performance will likely suffer */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_HTSHRINKFAIL); (table)->cmp_trigger_size = (table)->size; } } } /** * Writes the hashtable to the specified buffer * @param [in] table to write * @param [out] buffer the location to write the table to; note that the caller is responsible for making sure the buffer is large * enough for everything, including the literal representation of the records when they get written to the buffer * @param [in] copy_entry_to_buffer function pointed to returns an int representing the number of bits written to the buffer * and takes the current hash table entry along with a pointer to the buffer. It should write the hash table record to * the buffer in a way that can later be read by another function */ sm_uc_ptr_t COPY_HASHTAB_TO_BUFFER(HASH_TABLE *table, sm_uc_ptr_t buffer, int (*copy_entry_to_buffer)(HT_ENT *, sm_uc_ptr_t)) { HT_ENT *cur, *top; sm_uc_ptr_t bufptr; int copied_data; /* First we write the table entry to the buffer, then copy in each entry * Lastly, we set the table in the buffer to have null pointers for the * base and top pointers; these will have to be reconstituted when it's read * from the storage */ assert(TRUE == table->active); bufptr = buffer; memcpy(bufptr, table, SIZEOF(HASH_TABLE)); bufptr += SIZEOF(HASH_TABLE); /* Write the entry_passed_thru array to the buffer */ memcpy(bufptr, table->entry_passed_thru, ROUND_UP(table->size, BITS_PER_UCHAR)); bufptr += ROUND_UP(table->size, BITS_PER_UCHAR); memcpy(bufptr, table->base, table->size * SIZEOF(HT_ENT)); bufptr += table->size * SIZEOF(HT_ENT); if (NULL != copy_entry_to_buffer) { for(cur = table->base, top = table->top; cur < top; cur++) { bufptr += (*copy_entry_to_buffer)(cur, bufptr); } } table = (HASH_TABLE*)buffer; table->base = NULL; table->top = NULL; table->spare_base = NULL; table->active = FALSE; return bufptr; } /** * Constructs a hash table from the given buffer. * * The buffer is expected to stay alive for the duration of * the life of the hastable; we don't perform an extra copy * @param [in] buffer the written hashtable value; note that the caller is responsible for making the buffer * is large enough for the hash table, hash table entries, and the values the entries point to * @param [in] copy_entry_from_buffer function pointed to returns an int representing bits used in the buffer, * and takes the current hash table entry along with a pointer to the buffer. It should fill out the hash * table entry */ HASH_TABLE *ACTIVATE_HASHTAB_IN_BUFFER(sm_uc_ptr_t buffer, int (*copy_entry_from_buffer)(HT_ENT *, sm_uc_ptr_t)) { int i; HT_ENT *cur, *top; HASH_TABLE *table; table = (HASH_TABLE *)buffer; if (TRUE == table->active) return table; buffer += SIZEOF(HASH_TABLE); table->entry_passed_thru = buffer; buffer += ROUND_UP(table->size, BITS_PER_UCHAR); table->base = (HT_ENT *)buffer; buffer += table->size * SIZEOF(HT_ENT); table->top = (HT_ENT *)buffer; table->active = TRUE; if (NULL != copy_entry_from_buffer) { for(cur = table->base, top = table->top; cur < top; cur++) { buffer += (*copy_entry_from_buffer)(cur, buffer); } } return table; } fis-gtm-V7.0-005/sr_port/hashtab_int4.c0000755000032200000250000000124414342376331016563 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2004 Sanchez Computer Associates, 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 "min_max.h" #include "gtm_string.h" #include "error.h" #include "send_msg.h" #include "gtmmsg.h" #include "hashtab.h" #include "hashtab_int4.h" #define INT4_HASH #include "hashtab_implementation.h" fis-gtm-V7.0-005/sr_port/hashtab_int4.h0000755000032200000250000000645414342376331016600 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef HASHTAB_INT4_H #define HASHTAB_INT4_H /* Note that hashtab_addr has #define references to all items in this header file. Changes/additions should be reflected there as well. */ typedef struct { uint4 key; void *value; } ht_ent_int4; typedef struct hash_table_int4_struct { ht_ent_int4 *base; /* base of array of hent_* entries */ ht_ent_int4 *top; /* top of array of hent_* entries */ ht_ent_int4 *spare_base; /* spare array of hent_* entries */ sm_uc_ptr_t entry_passed_thru;/* Bit vector used to determine whether a particular */ /* ht_ent has been involved in a collision (meaning that */ /* this value can't be marked empty on delete */ unsigned int size; /* Hash table size */ unsigned int initial_size; /* Hash table initial size */ unsigned int spare_base_size;/* size of spare array */ unsigned int count; /* Number of valid entries */ unsigned int del_count; /* Number of entries marked deleted. */ unsigned int exp_trigger_size;/* When exp_trigger_size entried are used, expand table */ unsigned int cmp_trigger_size;/* When cmp_trigger_size reached compact table */ boolean_t dont_compact; /* if set, never perform compaction */ boolean_t dont_keep_spare_table; /* if set, don't keep a spare table */ boolean_t defer_base_release; /* if set don't release base, caller will free_base...() later */ boolean_t active; /* indicates that the table "ready to use"; if not, we need to activate it */ } hash_table_int4; #define HTENT_EMPTY_INT4(tabent, type, htvalue) (!(htvalue = (type *)(tabent)->value)) #define HTENT_MARK_EMPTY_INT4(tabent) (tabent)->value = (void *)0L #define HTENT_VALID_INT4(tabent, type, htvalue) (!(HTENT_EMPTY_INT4(tabent, type, htvalue)) && (HT_DELETED_ENTRY != htvalue)) #define COMPUTE_HASH_INT4(hkey, hash) hash = (*hkey) /* Prototypes for int4 hash routines. See hashtab_implementation.h for detail interface and implementation */ void init_hashtab_int4(hash_table_int4 *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table); void expand_hashtab_int4(hash_table_int4 *table, int minsize); boolean_t add_hashtab_int4(hash_table_int4 *table, uint4 *key, void *value, ht_ent_int4 **tabentptr); void *lookup_hashtab_int4(hash_table_int4 *table, uint4 *key); void delete_hashtab_ent_int4(hash_table_int4 *table, ht_ent_int4 *tabent); boolean_t delete_hashtab_int4(hash_table_int4 *table, uint4 *key); void free_hashtab_int4(hash_table_int4 *table); void reinitialize_hashtab_int4(hash_table_int4 *table); void compact_hashtab_int4(hash_table_int4 *table); sm_uc_ptr_t copy_hashtab_to_buffer_int4(hash_table_int4 *table, sm_uc_ptr_t buffer, int (*copy_entry_to_buffer)(ht_ent_int4 *, sm_uc_ptr_t)); hash_table_int4 *activate_hashtab_in_buffer_int4(sm_uc_ptr_t buffer, int (*copy_entry_from_buffer)(ht_ent_int4 *, sm_uc_ptr_t)); #endif fis-gtm-V7.0-005/sr_port/hashtab_int8.c0000755000032200000250000000124414342376331016567 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2004 Sanchez Computer Associates, 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 "min_max.h" #include "gtm_string.h" #include "error.h" #include "send_msg.h" #include "gtmmsg.h" #include "hashtab.h" #include "hashtab_int8.h" #define INT8_HASH #include "hashtab_implementation.h" fis-gtm-V7.0-005/sr_port/hashtab_int8.h0000755000032200000250000000667514342376331016611 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef HASHTAB_INT8_H #define HASHTAB_INT8_H /* Note that hashtab_addr has #define references to all items in this header file. Changes/additions should be reflected there as well. */ typedef struct { gtm_uint64_t key; void *value; NON_GTM64_ONLY(uint4 filler;) /* To make it 16 byte for all platforms and aligned */ } ht_ent_int8; typedef struct hash_table_int8_struct { ht_ent_int8 *base; /* base of array of hent_* entries */ ht_ent_int8 *top; /* top of array of hent_* entries */ ht_ent_int8 *spare_base; /* spare array of hent_* entries */ sm_uc_ptr_t entry_passed_thru;/* Bit vector used to determine whether a particular */ /* ht_ent has been involved in a collision (meaning that */ /* this value can't be marked empty on delete */ unsigned int size; /* Hash table size */ unsigned int initial_size; /* Hash table initial size */ unsigned int spare_base_size;/* size of spare array */ unsigned int count; /* Number of valid entries */ unsigned int del_count; /* Number of entries marked deleted. */ unsigned int exp_trigger_size;/* When exp_trigger_size entried are used, expand table */ unsigned int cmp_trigger_size;/* When cmp_trigger_size reached compact table */ boolean_t dont_compact; /* if set, never perform compaction */ boolean_t dont_keep_spare_table; /* if set, don't keep a spare table */ boolean_t defer_base_release; /* if set don't release base, caller will free_base...() later */ boolean_t active; /* indicates that the table "ready to use"; if not, we need to activate it */ } hash_table_int8; #define HTENT_EMPTY_INT8(tabent, type, htvalue) (!(htvalue = (type *)(tabent)->value)) #define HTENT_MARK_EMPTY_INT8(tabent) (tabent)->value = (void *)0L #define HTENT_VALID_INT8(tabent, type, htvalue) ((!HTENT_EMPTY_INT8(tabent, type, htvalue)) && (HT_DELETED_ENTRY != htvalue)) #define COMPUTE_HASH_INT8(hkey, hash) hash = (((*hkey) & 0xFFFFFFFF) ^ (*hkey) >> 31) /* Prototypes for int8 hash routines. See hashtab_implementation.h for detail interface and implementation */ void init_hashtab_int8(hash_table_int8 *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table); void expand_hashtab_int8(hash_table_int8 *table, int minsize); boolean_t add_hashtab_int8(hash_table_int8 *table, gtm_uint64_t *key, void *value, ht_ent_int8 **tabentptr); void *lookup_hashtab_int8(hash_table_int8 *table, gtm_uint64_t *key); void delete_hashtab_ent_int8(hash_table_int8 *table, ht_ent_int8 *tabent); boolean_t delete_hashtab_int8(hash_table_int8 *table, gtm_uint64_t *key); void free_hashtab_int8(hash_table_int8 *table); void reinitialize_hashtab_int8(hash_table_int8 *table); void compact_hashtab_int8(hash_table_int8 *table); sm_uc_ptr_t copy_hashtab_to_buffer_int8(hash_table_int8 *table, sm_uc_ptr_t buffer, int (*copy_entry_to_buffer)(ht_ent_int8 *, sm_uc_ptr_t)); hash_table_int8 *activate_hashtab_in_buffer_int8(sm_uc_ptr_t buffer, int (*copy_entry_from_buffer)(ht_ent_int8 *, sm_uc_ptr_t)); #endif fis-gtm-V7.0-005/sr_port/hashtab_mname.c0000644000032200000250000000532414342376333017004 0ustar librarygtc/**************************************************************** * * * 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 "min_max.h" #include "gtm_string.h" #include "error.h" #include "send_msg.h" #include "gtmmsg.h" #include #include "hashtab_mname.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "alias.h" GBLREF symval *curr_symval; #define MNAME_HASH /* The below include generates the hash table routines for the "mname" hash type */ #include "hashtab_implementation.h" /* While the above include creates the bulk of the routines in this modules, for the mname hash type we add one more routine that is a wrapper for the add_hashtab_mname created above. The situation this wrapper (add_hashtab_mname_symval) covers is if the hash table was expanded, it goes back through the stack and fixes the stack references kept in l_symval entries in each stackframe. Note that there are currently no direct callers of expand_hashtab_mname() (except in op_view) that we don't already protect or of delete_hashtab_mname() or they would need similar wrappers here. */ boolean_t add_hashtab_mname_symval(hash_table_mname *table, mname_entry *key, void *value, ht_ent_mname **tabentptr) { boolean_t retval; int table_size_orig; ht_ent_mname *table_base_orig; /* Currently only two values we expect here shown below. If calls are added with other values, they need to be taken care of here and in EXPAND_HASHTAB in hashtab_implementation.h */ assert(table == &curr_symval->h_symtab || table == &curr_symval->last_tab->h_symtab); assert(FALSE == key->marked); /* remember table we started with */ table_base_orig = table->base; table_size_orig = table->size; /* We'll do the base release once we do the reparations */ DEFER_BASE_REL_HASHTAB(table, TRUE); /* Call real table function */ retval = add_hashtab_mname(table, key, value, tabentptr); /* If the hash table has not changed, we are done */ if (table_base_orig == table->base) { DEFER_BASE_REL_HASHTAB(table, FALSE); return retval; } /* Otherwise we have some work to do to repair the l_symtab entries in the stack */ als_lsymtab_repair(table, table_base_orig, table_size_orig); /* We're done with the previous base */ FREE_BASE_HASHTAB(table, table_base_orig); DEFER_BASE_REL_HASHTAB(table, FALSE); return retval; } fis-gtm-V7.0-005/sr_port/hashtab_mname.h0000755000032200000250000000771714342376331017022 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef HASHTAB_MNAME_H #define HASHTAB_MNAME_H #include "hashtab.h" /* needed for STR_HASH (in COMPUTE_HASH_MNAME) */ typedef struct { mname_entry key; void *value; } ht_ent_mname; typedef struct hash_table_mname_struct { ht_ent_mname *base; /* base of array of hent_* entries */ ht_ent_mname *top; /* top of array of hent_* entries */ ht_ent_mname *spare_base; /* spare array of hent_* entries */ sm_uc_ptr_t entry_passed_thru;/* Bit vector used to determine whether a particular */ /* ht_ent has been involved in a collision (meaning that */ /* this value can't be marked empty on delete */ unsigned int size; /* Hash table size */ unsigned int initial_size; /* Hash table initial size */ unsigned int spare_base_size;/* size of spare array */ unsigned int count; /* Number of valid entries */ unsigned int del_count; /* Number of entries marked deleted. */ unsigned int exp_trigger_size;/* When exp_trigger_size entried are used, expand table */ unsigned int cmp_trigger_size;/* When cmp_trigger_size reached compact table */ boolean_t dont_compact; /* if set, never perform compaction */ boolean_t dont_keep_spare_table; /* if set, don't keep a spare table */ boolean_t defer_base_release; /* if set don't release base, caller will free_base...() later */ boolean_t active; /* indicates that the table "ready to use"; if not, we need to activate it */ } hash_table_mname; #define HTENT_EMPTY_MNAME(tabent, type, htvalue) (!(tabent)->key.var_name.len) #define HTENT_MARK_EMPTY_MNAME(tabent) (tabent)->key.var_name.len = 0 #define HTENT_VALID_MNAME(tabent, type, htvalue) ((!HTENT_EMPTY_MNAME(tabent, type, htvalue)) && \ (HT_DELETED_ENTRY != (htvalue = (type *)(tabent)->value))) #define COMPUTE_HASH_MNAME(hkey) \ { \ assert((0 < (hkey)->var_name.len) && (MAX_MIDENT_LEN >= (hkey)->var_name.len)); \ STR_HASH((hkey)->var_name.addr, (hkey)->var_name.len, ((hkey)->hash_code), 0); \ } /* This is the same as the COMPUTE_HASH_MNAME macro except "hkey" is of type mstr (not mname_entry) * and target of the computed hash_code is an input parameter "hash_code" of type "int". */ #define COMPUTE_HASH_MSTR(hkey, hash_code) \ { \ assert((0 < (hkey).len) && (MAX_MIDENT_LEN >= (hkey).len)); \ STR_HASH((hkey).addr, (hkey).len, hash_code, 0); \ } /* Prototypes for mname hash routines. See hashtab_implementation.h for detail interface and implementation */ void init_hashtab_mname(hash_table_mname *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table); void expand_hashtab_mname(hash_table_mname *table, int minsize); boolean_t add_hashtab_mname(hash_table_mname *table, mname_entry *key, void *value, ht_ent_mname **tabentptr); boolean_t add_hashtab_mname_symval(hash_table_mname *table, mname_entry *key, void *value, ht_ent_mname **tabentptr); void *lookup_hashtab_mname(hash_table_mname *table, mname_entry *key); void delete_hashtab_ent_mname(hash_table_mname *table, ht_ent_mname *tabent); boolean_t delete_hashtab_mname(hash_table_mname *table, mname_entry *key); void free_hashtab_mname(hash_table_mname *table); void reinitialize_hashtab_mname(hash_table_mname *table); void compact_hashtab_mname(hash_table_mname *table); sm_uc_ptr_t copy_hashtab_to_buffer_mname(hash_table_mname *table, sm_uc_ptr_t buffer, int (*copy_entry_to_buffer)(ht_ent_mname *, sm_uc_ptr_t)); hash_table_mname *activate_hashtab_in_buffer_mname(sm_uc_ptr_t buffer, int (*copy_entry_from_buffer)(ht_ent_mname *, sm_uc_ptr_t)); #endif fis-gtm-V7.0-005/sr_port/hashtab_objcode.c0000755000032200000250000000125114342376331017310 0ustar librarygtc/**************************************************************** * * * 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 "min_max.h" #include "gtm_string.h" #include "error.h" #include "send_msg.h" #include "gtmmsg.h" #include "cache.h" #include "hashtab_objcode.h" #define OBJCODE_HASH #include "hashtab_implementation.h" fis-gtm-V7.0-005/sr_port/hashtab_objcode.h0000755000032200000250000000752614342376331017330 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef HASHTAB_OBJCODE_H #define HASHTAB_OBJCODE_H #include "hashtab.h" /* needed for STR_HASH (in COMPUTE_HASH_MNAME) */ typedef struct { icode_str key; void *value; } ht_ent_objcode; typedef struct hash_table_objcode_struct { ht_ent_objcode *base; /* base of array of hent_* entries */ ht_ent_objcode *top; /* top of array of hent_* entries */ ht_ent_objcode *spare_base; /* spare array of hent_* entries */ sm_uc_ptr_t entry_passed_thru;/* Bit vector used to determine whether a particular */ /* ht_ent has been involved in a collision (meaning that */ /* this value can't be marked empty on delete */ unsigned int size; /* Hash table size */ unsigned int initial_size; /* Hash table initial size */ unsigned int spare_base_size;/* size of spare array */ unsigned int count; /* Number of valid entries */ unsigned int del_count; /* Number of entries marked deleted. */ unsigned int exp_trigger_size;/* When exp_trigger_size entried are used, expand table */ unsigned int cmp_trigger_size;/* When cmp_trigger_size reached compact table */ boolean_t dont_compact; /* if set, never perform compaction */ boolean_t dont_keep_spare_table; /* if set, don't keep a spare table */ boolean_t defer_base_release; /* if set don't release base, caller will free_base...() later */ boolean_t active; /* indicates that the table "ready to use"; if not, we need to activate it */ } hash_table_objcode; #define HTENT_EMPTY_OBJCODE(tabent, type, htvalue) (!(htvalue = (type *)(tabent)->value) && !(tabent)->key.str.len) #define HTENT_MARK_EMPTY_OBJCODE(tabent) \ { \ (tabent)->value = NULL; \ (tabent)->key.str.len = 0; \ } #define HTENT_VALID_OBJCODE(tabent, type, htvalue) ((!HTENT_EMPTY_OBJCODE(tabent, type, htvalue)) && (HT_DELETED_ENTRY != htvalue)) #define COMPUTE_HASH_OBJCODE(hkey, hash) \ { \ char *sptr; \ sptr = (hkey)->str.addr; \ if ((hkey)->str.len < SIZEOF(mident_fixed)) \ { \ STR_HASH((sptr), (hkey)->str.len, hash, 0); \ } else \ { \ STR_HASH((sptr), (SIZEOF(mident_fixed) / 2), hash, 0); \ STR_HASH(((sptr) + (hkey)->str.len - (SIZEOF(mident_fixed) / 2)), \ (SIZEOF(mident_fixed) / 2), hash, hash); \ } \ } /* Prototypes for objcode hash routines. See hashtab_implementation.h for detail interface and implementation */ void init_hashtab_objcode(hash_table_objcode *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table); void expand_hashtab_objcode(hash_table_objcode *table, int minsize); boolean_t add_hashtab_objcode(hash_table_objcode *table, icode_str *key, void *value, ht_ent_objcode **tabentptr); void *lookup_hashtab_objcode(hash_table_objcode *table, icode_str *key); void delete_hashtab_ent_objcode(hash_table_objcode *table, ht_ent_objcode *tabent); boolean_t delete_hashtab_objcode(hash_table_objcode *table, icode_str *key); void free_hashtab_objcode(hash_table_objcode *table); void reinitialize_hashtab_objcode(hash_table_objcode *table); void compact_hashtab_objcode(hash_table_objcode *table); sm_uc_ptr_t copy_hashtab_to_buffer_objcode(hash_table_objcode *table, sm_uc_ptr_t buffer, int (*copy_entry_to_buffer)(ht_ent_objcode *, sm_uc_ptr_t)); hash_table_objcode *activate_hashtab_in_buffer_objcode(sm_uc_ptr_t buffer, int (*copy_entry_from_buffer)(ht_ent_objcode *, sm_uc_ptr_t)); #endif fis-gtm-V7.0-005/sr_port/hashtab_rehash_ch.c0000755000032200000250000000250114342376331017626 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2018 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 "error.h" #include "util.h" #include "gtm_multi_thread.h" error_def(ERR_MEMORY); error_def(ERR_MEMORYRECURSIVE); error_def(ERR_HTOFLOW); CONDITION_HANDLER(hashtab_rehash_ch) { /* If we cannot alloc memory during rehashing, just continue in normal program flow */ START_CH(TRUE); /* If we cannot allocate memory or any error while doing rehash, just abort any more rehashing. * We will continue with old table. */ if (ERR_HTOFLOW == SIGNAL || ERR_MEMORY == SIGNAL || ERR_MEMORYRECURSIVE == SIGNAL) { UNIX_ONLY(util_out_print("", RESET)); /* Prevents error message from being flushed later by rts_error() */ char *rname; if (INSIDE_THREADED_CODE(rname)) { GTM_PTHREAD_EXIT(SIGNAL); } else { UNWIND(NULL, NULL); } } else { NEXTCH; /* non memory related error */ } } fis-gtm-V7.0-005/sr_port/hashtab_str.c0000644000032200000250000000134714342376331016516 0ustar librarygtc/**************************************************************** * * * Copyright 2009, 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 "min_max.h" #include "gtm_string.h" #include "error.h" #include "send_msg.h" #include "gtmmsg.h" #include "hashtab_str.h" #define STRING_HASH /* The below include generates the hash table routines for the literal hash type */ #include "hashtab_implementation.h" fis-gtm-V7.0-005/sr_port/hashtab_str.h0000644000032200000250000000732214342376331016522 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2018 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. * * * ****************************************************************/ #ifndef HASHTAB_STR_H #define HASHTAB_STR_H #include "hashtab.h" /* needed for STR_HASH (in COMPUTE_HASH_MNAME) */ typedef struct { mstr str; uint4 hash_code; GTM64_ONLY(int4 filler;) } stringkey; typedef struct { stringkey key; void *value; } ht_ent_str; typedef struct hash_table_str_struct { ht_ent_str *base; /* base of array of hent_* entries */ ht_ent_str *top; /* top of array of hent_* entries */ ht_ent_str *spare_base; /* spare array of hent_* entries */ sm_uc_ptr_t entry_passed_thru;/* Bit vector used to determine whether a particular */ /* ht_ent has been involved in a collision (meaning that */ /* this value can't be marked empty on delete */ unsigned int size; /* Hash table size */ unsigned int initial_size; /* Hash table initial size */ unsigned int spare_base_size;/* size of spare array */ unsigned int count; /* Number of valid entries */ unsigned int del_count; /* Number of entries marked deleted. */ unsigned int exp_trigger_size;/* When exp_trigger_size entried are used, expand table */ unsigned int cmp_trigger_size;/* When cmp_trigger_size reached compact table */ boolean_t dont_compact; /* if set, never perform compaction */ boolean_t dont_keep_spare_table; /* if set, don't keep a spare table */ boolean_t defer_base_release; /* if set don't release base, caller will free_base...() later */ boolean_t active; /* indicates that the table "ready to use"; if not, we need to activate it */ } hash_table_str; #define HTENT_EMPTY_STR(tabent, type, htvalue) (!(tabent)->key.hash_code && !(tabent)->key.str.len) #define HTENT_MARK_EMPTY_STR(tabent) \ { \ (tabent)->key.hash_code = 0; \ (tabent)->key.str.len = 0; \ } #define HTENT_VALID_STR(tabent, type, htvalue) ((!HTENT_EMPTY_STR(tabent, type, htvalue)) && \ (HT_DELETED_ENTRY != (htvalue = (type *)(tabent)->value))) #define COMPUTE_HASH_STR(hkey) \ { \ assert((0 <= (hkey)->str.len) && (MAX_STRLEN >= (hkey)->str.len)); \ if (0 == (hkey)->str.len) \ /* Likely null string -- need nonzero hash code */ \ (hkey)->hash_code = 1; \ else \ STR_HASH((hkey)->str.addr, (hkey)->str.len, ((hkey)->hash_code), 0); \ } /* Prototypes for addr hash routines. See hashtab_implementation.h for detail interface and implementation */ void init_hashtab_str(hash_table_str *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table); void expand_hashtab_str(hash_table_str *table, int minsize); boolean_t add_hashtab_str(hash_table_str *table, stringkey *key, void *value, ht_ent_str **tabentptr); void *lookup_hashtab_str(hash_table_str *table, stringkey *key); void delete_hashtab_ent_str(hash_table_str *table, ht_ent_str *tabent); boolean_t delete_hashtab_str(hash_table_str *table, stringkey *key); void free_hashtab_str(hash_table_str *table); void reinitialize_hashtab_str(hash_table_str *table); void compact_hashtab_str(hash_table_str *table); sm_uc_ptr_t copy_hashtab_to_buffer_str(hash_table_str *table, sm_uc_ptr_t buffer, int (*copy_entry_to_buffer)(ht_ent_str *, sm_uc_ptr_t)); hash_table_str *activate_hashtab_in_buffer_str(sm_uc_ptr_t buffer, int (*copy_entry_from_buffer)(ht_ent_str *, sm_uc_ptr_t)); #endif /* HASHTAB_STR_H */ fis-gtm-V7.0-005/sr_port/have_crit.c0000644000032200000250000001362414342376331016161 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_inet.h" #ifdef VMS #include /* Required for gtmsource.h */ #endif #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "repl_msg.h" #include "gtmsource.h" #include "filestruct.h" #include "jnl.h" #include "dpgbldir.h" #include "have_crit.h" #include "send_msg.h" GBLREF volatile int4 crit_count; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnlpool_addrs_ptr_t jnlpool_head; GBLREF uint4 process_id; GBLREF uint4 crit_deadlock_check_cycle; GBLREF uint4 dollar_tlevel; GBLREF unsigned int t_tries; #ifdef DEBUG GBLREF jnl_gbls_t jgbl; #endif error_def(ERR_MUTEXRELEASED); /* Return number of regions (including jnlpool dummy region) if have or are aquiring crit or in_wtstart * ** NOTE ** This routine is called from signal handlers and is thus called asynchronously. * If CRIT_IN_COMMIT bit is set, we check if in middle of commit (PHASE1 inside crit or PHASE2 outside crit) on some region. * If CRIT_RELEASE bit is set AND * a) If CRIT_TRANS_NO_REG is not specified, we release crit on ALL regions that we hold crit on. * b) If CRIT_TRANS_NO_REG is specified, we release crit on ONLY those regions that are not part of the current TP transaction * (detected by their crit_check_cycle value being the same as crit_deadlock_check_cycle). * Note: CRIT_RELEASE implies CRIT_ALL_REGIONS * If CRIT_ALL_REGIONS bit is set, go through the entire list of regions */ uint4 have_crit(uint4 crit_state) { gd_region *r_top, *r_local; gd_addr *addr_ptr; jnlpool_addrs_ptr_t local_jnlpool; sgmnt_addrs *csa; uint4 crit_reg_cnt = 0; DEBUG_ONLY(uint4 crit_jnlpool_reg = 0;) /* in order to proper release the necessary regions, CRIT_RELEASE implies going through all the regions */ if (crit_state & CRIT_RELEASE) { assert(!jgbl.onlnrlbk); /* should not request crit to be released if online rollback */ crit_state |= CRIT_ALL_REGIONS; } if ((INTRPT_IN_CRIT_FUNCTION == intrpt_ok_state) && (crit_state & CRIT_HAVE_ANY_REG)) { crit_reg_cnt++; if (0 == (crit_state & CRIT_ALL_REGIONS)) return crit_reg_cnt; } for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) { for (r_local = addr_ptr->regions, r_top = r_local + addr_ptr->n_regions; r_local < r_top; r_local++) { if (r_local->open && !r_local->was_open) { csa = &FILE_INFO(r_local)->s_addrs; if (NULL != csa) { if (csa->now_crit && (crit_state & CRIT_HAVE_ANY_REG)) { crit_reg_cnt++; /* If we are releasing (all) regions with critical section or if special * TP case, release if the cycle number doesn't match meaning this is a * region we should not hold crit in (even if it is part of tp_reg_list). */ if ((0 != (crit_state & CRIT_RELEASE)) && (0 == (crit_state & CRIT_NOT_TRANS_REG) || (crit_deadlock_check_cycle != csa->crit_check_cycle))) { assert(WBTEST_HOLD_CRIT_ENABLED); assert(!csa->hold_onto_crit); /* It is possible that if DSE has done a CRIT REMOVE and stolen our crit, it * could be given to someone else which would cause this test to fail. The * current thinking is that the state DSE put this process is no longer * viable and it should die at the earliest opportunity, there being no way * to know if that is what happened anyway. This assertpro used to proceed * this block, however we found that asynchronous calls by signal handling * logic occasionally intertupt the grab_crit logic between the time we * update csa->now_crit and the time we establish ownership in nl->in_crit. * Hence we restrict the assertpro to the case where we need to not be in * that interlude, as the handler logic does not change crit, but rather * only checks it, and deals with what it finds. */ assertpro(csa->nl->in_crit == process_id); rel_crit(r_local); send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_MUTEXRELEASED, 6, process_id, process_id, DB_LEN_STR(r_local), dollar_tlevel, t_tries); } if (0 == (crit_state & CRIT_ALL_REGIONS)) return crit_reg_cnt; } /* In Commit-crit is defined as the time since when early_tn is 1 + curr_tn upto when * t_commit_crit is set to FALSE. Note that the first check should be done only if we * hold crit as otherwise we could see inconsistent values. */ if ((crit_state & CRIT_IN_COMMIT) && (csa->now_crit && (csa->ti->early_tn != csa->ti->curr_tn) || csa->t_commit_crit)) { crit_reg_cnt++; if (0 == (crit_state & CRIT_ALL_REGIONS)) return crit_reg_cnt; } if ((crit_state & CRIT_IN_WTSTART) && csa->in_wtstart) { crit_reg_cnt++; if (0 == (crit_state & CRIT_ALL_REGIONS)) return crit_reg_cnt; } } } } } for (local_jnlpool = jnlpool_head; local_jnlpool; local_jnlpool = local_jnlpool->next) if ((NULL != local_jnlpool) && (NULL != local_jnlpool->jnlpool_ctl)) { csa = &FILE_INFO(local_jnlpool->jnlpool_dummy_reg)->s_addrs; if ((NULL != csa) && csa->now_crit && (crit_state & CRIT_HAVE_ANY_REG)) { crit_reg_cnt++; # ifdef DEBUG crit_jnlpool_reg++; assert(1 >= crit_jnlpool_reg); # endif if (0 != (crit_state & CRIT_RELEASE)) { assert(!csa->hold_onto_crit); rel_lock(local_jnlpool->jnlpool_dummy_reg); } } } return crit_reg_cnt; } fis-gtm-V7.0-005/sr_port/have_crit.h0000755000032200000250000002771314342376331016175 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ #ifndef HAVE_CRIT_H_INCLUDED #define HAVE_CRIT_H_INCLUDED #include /* needed for VSIG_ATOMIC_T */ #ifdef UNIX #include #endif /* states of CRIT passed as argument to have_crit() */ #define CRIT_HAVE_ANY_REG 0x00000001 #define CRIT_IN_COMMIT 0x00000002 #define CRIT_NOT_TRANS_REG 0x00000004 #define CRIT_RELEASE 0x00000008 #define CRIT_ALL_REGIONS 0x00000010 #define CRIT_IN_WTSTART 0x00000020 /* check if csa->in_wtstart is true */ #ifdef DEBUG #include "wbox_test_init.h" #endif #include "gt_timer.h" #include "gtm_multi_thread.h" #include "gtm_multi_proc.h" #include "gtmsiginfo.h" typedef enum { INTRPT_OK_TO_INTERRUPT = 0, INTRPT_IN_GTCMTR_TERMINATE, INTRPT_IN_TP_UNWIND, INTRPT_IN_TP_CLEAN_UP, INTRPT_IN_CRYPT_SECTION, INTRPT_IN_DB_CSH_GETN, INTRPT_IN_GVCST_INIT, INTRPT_IN_GDS_RUNDOWN, INTRPT_IN_SS_INITIATE, INTRPT_IN_ZLIB_CMP_UNCMP, INTRPT_IN_TRIGGER_NOMANS_LAND, /* State where have trigger base frame but no trigger (exec) frame. */ INTRPT_IN_MUR_OPEN_FILES, INTRPT_IN_TRUNC, INTRPT_IN_SET_NUM_ADD_PROCS, INTRPT_IN_SYSCONF, INTRPT_NO_TIMER_EVENTS, /* State where primary reason for deferral is to avoid timer pops. */ INTRPT_IN_FFLUSH, /* Deferring interrupts during fflush. */ INTRPT_IN_SHMDT, /* Deferring interrupts during SHMDT. */ INTRPT_IN_WAIT_FOR_DISK_SPACE, /* Deferring interrupts during wait_for_disk_space.c. */ INTRPT_IN_WCS_WTSTART, /* Deferring interrupts until cnl->intent_wtstart is decremented and dbsync timer is * started. */ INTRPT_IN_X_TIME_FUNCTION, /* Deferring interrupts in non-nesting functions, such as localtime, ctime, and mktime. */ INTRPT_IN_FUNC_WITH_MALLOC, /* Deferring interrupts while in libc- or system functions that do a malloc internally. */ INTRPT_IN_FDOPEN, /* Deferring interrupts in fdopen. */ INTRPT_IN_LOG_FUNCTION, /* Deferring interrupts in openlog, syslog, or closelog. */ INTRPT_IN_FORK_OR_SYSTEM, /* Deferring interrupts in fork or system. */ INTRPT_IN_FSTAT, /* Deferring interrupts in fstat. */ INTRPT_IN_TLS_FUNCTION, /* Deferring interrupts in TLS functions. */ INTRPT_IN_CONDSTK, /* Deferring interrupts during condition handler stack manipulations. */ INTRPT_IN_GTMIO_CH_SET, /* Deferring interrupts during the setting of gtmio_ch condition handler. */ INTRPT_IN_OBJECT_FILE_COMPILE, /* Deferring interrupts during the creation of an object file. */ INTRPT_IN_IO_READ, /* Deferring interrupts for async signal unsafe calls in iorm_readfl.c. */ INTRPT_IN_IO_WRITE, /* Deferring interrupts for async signal unsafe calls in iorm_write.c. */ INTRPT_IN_PTHREAD_NB, /* Deferring interrupts for non-blocking async signal unsafe calls. */ INTRPT_IN_GTM_MULTI_PROC, /* Deferring interrupts while inside "gtm_multi_proc" function */ INTRPT_IN_EINTR_WRAPPERS, /* Deferring interrupts while inside "eintr_wrappers.h" macros */ INTRPT_IN_MKSTEMP, /* Deferring interrupts while in mkstemp */ INTRPT_IN_CRYPT_RECONFIG, /* Deferring interrupts during reconfiguration of the encryption state. */ INTRPT_IN_UNLINK_AND_CLEAR, /* Deferring interrupts around unlink and clearing the filename being unlinked */ INTRPT_IN_GETC, /* Deferring interrupts around GETC() call */ INTRPT_IN_AIO_ERROR, /* Deferring interrupts around aio_error() call */ INTRPT_IN_RETRY_LOOP, /* Deferring interrupts while retrying an operation while others are blocked */ INTRPT_IN_CRIT_FUNCTION, /* Deferring interrupts in crit functions, replacing crit_count. */ INTRPT_IN_DEADLOCK_CHECK, /* Deferring interrupts in crit deadlock check, replacing crit_count. */ INTRPT_IN_DB_JNL_LSEEKWRITE, /* Deferring interrupts in DB_/JNL_LSEEKWRITE() call */ INTRPT_IN_JNL_QIO, /* Deferring interrupts in journal qio, replacing jnl_qio_in_prog. */ INTRPT_IN_MLK_CLEANUP, /* Deferring interrupts around lock table cleanup code */ INTRPT_IN_JNL_FSYNC, /* Deferring interrupts in jnl_fsync() while holding the latch */ INTRPT_IN_EVENT_HANDLING, /* Deferring interrupts while managing a deferred or outofband event */ INTRPT_IN_KILL_CLEANUP, /* Deferring interrupts while performing KILL cleanup - used by REORG */ INTRPT_IN_RTN_CLEANUP, /* Deferring interrupts while cleaning up routines */ INTRPT_IN_SOCKET_CLOSE, /* Deferring interrupts while closing some sockets */ INTRPT_NUM_STATES /* Should be the *last* one in the enum. */ } intrpt_state_t; GBLREF intrpt_state_t intrpt_ok_state; GBLREF boolean_t deferred_timers_check_needed; /* Macro to check if we are in a state that is ok to interrupt (or to do deferred signal handling). We do not want to interrupt if * the global variable intrpt_ok_state indicates it is not ok to interrupt, if we are in the midst of a malloc, if we are holding * crit, or if we are in the midst of a commit. */ #define OK_TO_INTERRUPT ((INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) && (0 == gtmMallocDepth) \ && (0 == have_crit(CRIT_HAVE_ANY_REG | CRIT_IN_COMMIT))) /* Set the value of forced_exit to 1. This should indicate that we want a deferred signal handler to be invoked first upon leaving * the current deferred window. Since we do not want forced_exit state to ever regress, and there might be several signals delivered * within the same deferred window, assert that forced_exit is either 0 or 1 before setting it to 1. */ #define SET_FORCED_EXIT_STATE \ { \ char *rname; \ GBLREF VSIG_ATOMIC_T forced_exit; \ \ /* Below code is not thread safe as it modifies global variable "forced_exit" */ \ assert(!INSIDE_THREADED_CODE(rname)); \ assert((0 == forced_exit) || (1 == forced_exit)); \ forced_exit = 1; \ SET_FORCED_THREAD_EXIT; /* Signal any running threads to stop */ \ SET_FORCED_MULTI_PROC_EXIT; /* Signal any parallel processes to stop */ \ } /* Set the value of forced_exit to 2. This should indicate that we are already in the exit processing, and do not want to handle any * deferred events, from timers or other interrupts, anymore. Ensure that forced_exit state does not regress by asserting that the * current value is 0 or 1 before setting it to 2. Note that on UNIX forced_exit can progress to 2 only from 1, while on VMS it is * possible to generic_exit_handler to change forced_exit from 0 to 2 directly. This design ensures that on VMS we will not invoke * sys$exit from DEFERRED_EXIT_HANDLING_CHECK after we started exit processing; on UNIX process_exiting flag servs the same purpose * (and is also checked by DEFERRED_EXIT_HANDLING_CHECK), so it is not necessary for generic_signal_handler to set forced_exit to 2. */ #define SET_FORCED_EXIT_STATE_ALREADY_EXITING \ { \ char *rname; \ GBLREF VSIG_ATOMIC_T forced_exit; \ GBLREF boolean_t forced_thread_exit; \ \ /* Below code is not thread safe as it modifies global variable "forced_exit" */ \ assert(!INSIDE_THREADED_CODE(rname)); \ assert(1 == forced_exit); \ assert(forced_thread_exit); \ forced_exit = 2; \ } /* Macro to be used whenever we want to handle any signals that we deferred handling and exit in the process. * In VMS, we dont do any signal handling, only exit handling. */ #define DEFERRED_EXIT_HANDLING_CHECK \ deferred_exit_handling_check() GBLREF boolean_t multi_thread_in_use; /* TRUE => threads are in use. FALSE => not in use */ /* Macro to cause deferrable interrupts to be deferred, recording the cause. * If interrupt is already deferred, state is not changed. * * The normal usage of the below macros is * DEFER_INTERRUPTS * non-interruptible code * ENABLE_INTERRUPTS * We want the non-interruptible code to be executed AFTER the SAVE_INTRPT_OK_STATE macro. * To enforce this ordering, one would think a read memory barrier is needed in between. * But it is not needed. This is because we expect the non-interruptible code to have * a) pointer dereferences OR * b) function calls * Either of these will prevent the compiler from reordering the non-interruptible code. * Any non-interruptible code that does not have either of the above usages (for e.g. uses C global * variables) might be affected by compiler reordering. As of now, there is no known case of such * usage and no such usage is anticipated in the future. * * We dont need to worry about machine reordering as well since there is no shared memory variable * involved here (intrpt_ok_state is a process private variable) and even if any reordering occurs * they will all be in-flight instructions when the interrupt occurs so the hardware will guarantee * all such instructions are completely done or completely discarded before servicing the interrupt * which means the interrupt service routine will never see a reordered state of the above code. * * If we are currently executing in a thread then interrupts will not be delivered to us but instead * will go to the master process (that spawned off threads like us) and therefore the thread flow is * not expected to be interrupted. So skip the macro processing in that case. This is necessary because * it manipulates the global variable "intrpt_ok_state" which is a no-no inside threaded code. */ /* NEWSTATE is an Input parameter. OLDSTATE is an Output parameter (later used as NEWSTATE parameter to * ENABLE_INTERRUPTS macro). Callers, e.g. ESTABLISH_RET, rely on OLDSTATE being set irrespective of * whether multi_thread_in_use is set or not. */ #define DEFER_INTERRUPTS(NEWSTATE, OLDSTATE) \ { \ OLDSTATE = intrpt_ok_state; \ if (!multi_thread_in_use) \ { \ assert(INTRPT_OK_TO_INTERRUPT != NEWSTATE); \ intrpt_ok_state = NEWSTATE; \ } \ } /* Restore deferrable interrupts back to the state it was at time of corresponding DEFER_INTERRUPTS call */ #define ENABLE_INTERRUPTS(OLDSTATE, NEWSTATE) \ { \ if (!multi_thread_in_use) \ { \ assert(OLDSTATE == intrpt_ok_state); \ intrpt_ok_state = NEWSTATE; \ if (INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) \ DEFERRED_EXIT_HANDLING_CHECK; /* check if signals were deferred in deferred zone */ \ } \ } #define OK_TO_SEND_MSG ((INTRPT_IN_X_TIME_FUNCTION != intrpt_ok_state) \ && (INTRPT_IN_LOG_FUNCTION != intrpt_ok_state) \ && (INTRPT_IN_FUNC_WITH_MALLOC != intrpt_ok_state) \ && (INTRPT_IN_FORK_OR_SYSTEM != intrpt_ok_state)) uint4 have_crit(uint4 crit_state); /* Inline functions */ static inline void deferred_exit_handling_check(void) { char *rname; GBLREF int process_exiting; GBLREF VSIG_ATOMIC_T forced_exit; GBLREF volatile int4 gtmMallocDepth; GBLREF volatile int suspend_status; /* The forced_exit state of 2 indicates that the exit is already in progress, so we do not * need to process any deferred events. Note if threads are running, check if forced_exit is * non-zero and if so exit the thread (using pthread_exit) otherwise skip deferred event * processing. A similar check will happen once threads stop running. */ if (INSIDE_THREADED_CODE(rname)) { PTHREAD_EXIT_IF_FORCED_EXIT; } else if ((2 > forced_exit) && !process_exiting && (INTRPT_IN_KILL_CLEANUP != intrpt_ok_state)) { /* If forced_exit was set while in a deferred state, disregard any deferred timers and * invoke deferred_signal_handler directly. */ if (forced_exit && OK_TO_INTERRUPT) deferred_signal_handler(); else { if ((DEFER_SUSPEND == suspend_status) && OK_TO_INTERRUPT) suspend(SIGSTOP); if (deferred_timers_check_needed && OK_TO_INTERRUPT) check_for_deferred_timers(); } } } #endif /* HAVE_CRIT_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/hd.mpt0000755000032200000250000000212214342376331015160 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 1987-2019 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %HD ;GT.M %HD utility - hexadecimal to decimal conversion program ;invoke at INT with %HD in hexadecimal to return %HD in decimal ;invoke at FUNC as an extrinsic function ;if you make heavy use of this routine, consider $ZCALL ; set %HD=$$FUNC(%HD) quit INT read !,"Hexidecimal: ",%HD set %HD=$$FUNC(%HD) quit FUNC(h) quit:"-"=$extract(h) "" ; 0>h risks NUMOFLOW new c,d,dg,l set d=0,h=$translate(h,"abcdef","ABCDEF"),l=$length(h) if (15>l) for c=1:1:l set dg=$find("0123456789ABCDEF",$extract(h,c)) quit:'dg set d=(d*16)+(dg-2) else quit $$CONVERTBASE^%CONVBASEUTIL(h,16,10) quit d fis-gtm-V7.0-005/sr_port/ho.mpt0000755000032200000250000000216414342376331015201 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 1987-2019 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %HO ;GT.M %HO utility - hexadecimal to octal conversion program ;invoke at INT with %HO in hexadecimal to return %HO in octal ;invoke at FUNC as an extrinsic function ;if you make heavy use of this routine, consider $ZCALL ; set %HO=$$FUNC(%HO) quit INT read !,"Hexadecimal: ",%HO set %HO=$$FUNC(%HO) q FUNC(h) quit:"-"=$extract(h) "" ; 0>h risks NUMOFLOW new c,d,dg,l,o set l=$length(h) quit:(15= (m * 10)); n = n - (m * 10); assert((9 >= n) && (0 <= n)); n = n + '0'; assert(('9' >= n) && ('0' <= n)); assert(ar < q); *--q = (unsigned char)n; n = m; assert(NULL != q); } } assert((uintszofptr_t)q >= (uintszofptr_t)ar); len = (unsigned int)(ar + SIZEOF(ar) - q); memcpy(p, q, len); return p + len; } uchar_ptr_t i2ascl(uchar_ptr_t p, qw_num n) { unsigned char ar[MAX_DIGITS_IN_INT8], *q; uint4 len; long r; q = ar + SIZEOF(ar); if (QWEQ(n, seq_num_zero)) *--q = '0'; else { while (QWNE(n, seq_num_zero)) { QWDIVIDEBYDW(n, 10, n, r); *--q = r + '0'; } } assert((uintszofptr_t)q >= (uintszofptr_t)ar); len = (uint4)(ar + SIZEOF(ar) - q); memcpy(p, q, len); return (unsigned char *)(p + len) ; } #ifdef INT8_SUPPORTED uchar_ptr_t i2asclx(uchar_ptr_t p, qw_num n) { unsigned char ar[MAX_HEX_DIGITS_IN_INT8], *q; uint4 len; qw_num m; q = ar + SIZEOF(ar); if (!n) *--q = '0'; else { while (n) { m = n & 0xF; if (m <= 9) *--q = m + '0'; else *--q = m - 0xa + 'A'; n = n >> 4; } } assert((uintszofptr_t)q >= (uintszofptr_t)ar); len = (uint4)(ar + SIZEOF(ar) - q); memcpy(p, q, len); return p + len; } #else uchar_ptr_t i2asclx(uchar_ptr_t p, qw_num n) { unsigned char ar[24], *q; uint4 msb, lsb, len, nibble; int i; q = ar + SIZEOF(ar); lsb = n.value[lsb_index]; msb = n.value[msb_index]; if (msb) { for (i = 0; i < 8; i++) /* 8 to denote 8 nibbles per 4-byte value */ { nibble = lsb & 0xF; if (nibble <= 9) *--q = nibble + '0'; else *--q = nibble - 0xa + 'A'; lsb = lsb >> 4; } lsb = msb; } if (!lsb) *--q = '0'; else { while (lsb) { nibble = lsb & 0xF; if (nibble <= 9) *--q = nibble + '0'; else *--q = nibble - 0xa + 'A'; lsb = lsb >> 4; } } assert((uintszofptr_t)q >= (uintszofptr_t)ar); len = ar + SIZEOF(ar) - q; memcpy(p, q, len); return p + len; } #endif fis-gtm-V7.0-005/sr_port/i2hex.c0000755000032200000250000000241714342376331015235 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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" void i2hex(UINTPTR_T val, uchar_ptr_t dest, int len) { register uchar_ptr_t cp; register UINTPTR_T n; char x; n = val; cp = &dest[len]; while (cp > dest) { x = n & 0xF; n >>= 4; *--cp = x + ((x > 9) ? 'A' - 10 : '0'); } return; } #ifdef INT8_SUPPORTED void i2hexl(qw_num val, uchar_ptr_t dest, int len) { uchar_ptr_t cp; qw_num n; char x; n = val; cp = &dest[len]; while (cp > dest) { x = n & 0xF; n >>= 4; *--cp = x + ((x > 9) ? 'A' - 10 : '0'); } } #else void i2hexl(qw_num val, uchar_ptr_t dest, int len) { if (4 > len) { /* We only process some bytes of the low order word */ i2hex(val.value[msb_index], dest, len); return; } i2hex(val.value[msb_index], dest, 8); /* Process low order word */ dest += 8; len -= 8; if (len) i2hex(val.value[lsb_index], dest, len); } #endif fis-gtm-V7.0-005/sr_port/i2hex_blkfill.c0000755000032200000250000000214014342376331016725 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" void i2hex_blkfill(int num, uchar_ptr_t addr, int len) { unsigned char buff[8]; int i; assert((SIZEOF(buff) >= len) && (0 < len)); i2hex(num, buff, len); for ( i = 0; i < len - 1 ; i++) { if (buff[i] != '0') break; buff[i] = ' '; } memcpy(addr, buff, len); } void i2hexl_blkfill(qw_num num, uchar_ptr_t addr, int len) { unsigned char buff[16]; int i; assert((SIZEOF(buff) >= len) && (0 < len)); i2hexl(num, buff, len); for ( i = 0; i < len - 1 ; i++) { if (buff[i] != '0') break; buff[i] = ' '; } memcpy(addr, buff, len); } fis-gtm-V7.0-005/sr_port/i2hex_nofill.c0000755000032200000250000000211514342376331016573 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" int i2hex_nofill(int num, uchar_ptr_t addr, int len) { unsigned char buff[8]; int i; assert(SIZEOF(buff) >= len); i2hex(num, buff, len); for (i = 0; i < (len - 1); i++) { if (buff[i] != '0') break; } memcpy(addr, &buff[i], len - i); return (len - i); } int i2hexl_nofill(qw_num num, uchar_ptr_t addr, int len) { unsigned char buff[16]; int i; assert(SIZEOF(buff) >= len); i2hexl(num, buff, len); for (i = 0; i < (len - 1); i++) { if (buff[i] != '0') break; } memcpy(addr, &buff[i], len - i); return (len - i); } fis-gtm-V7.0-005/sr_port/ind_cg_var.c0000644000032200000250000000213614342376333016306 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include #include "cache.h" #include "obj_file.h" #include "cg_var.h" #include "stringpool.h" #include "hashtab_mname.h" GBLREF spdesc indr_stringpool; void ind_cg_var(mvar *v, var_tabent **p) { /* Copy mident with variable name to variable table entry */ assert((char *)indr_stringpool.base <= v->mvname.addr && v->mvname.addr < (char *)indr_stringpool.top); (*p)[v->mvidx].var_name = v->mvname; COMPUTE_HASH_MNAME(&((*p)[v->mvidx])); (*p)[v->mvidx].var_name.addr = (char *)((v->mvname.addr - (char *)indr_stringpool.base) + ROUND_UP2(SIZEOF(ihdtyp), NATIVE_WSIZE)); (*p)[v->mvidx].marked = FALSE; } fis-gtm-V7.0-005/sr_port/ind_code.c0000644000032200000250000001435414342376333015764 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "compiler.h" #include "objlabel.h" #include #include "cache.h" #include "cgp.h" #include "stringpool.h" #include "copy.h" #include "mmemory.h" #include "obj_file.h" #include "cg_var.h" GBLREF boolean_t run_time; GBLREF int4 mvmax, mlitmax; GBLREF mvar *mvartab; GBLREF int4 curr_addr, code_size; GBLREF int4 sa_temps[]; GBLREF int4 sa_temps_offset[]; GBLREF char cg_phase; /* code generation phase */ GBLREF char cg_phase_last; /* previous code generation phase */ GBLREF spdesc stringpool, indr_stringpool; #ifdef __ia64 GBLREF int4 generated_code_size, calculated_code_size; #endif GBLDEF unsigned char *runtime_base; /************** Indirect Object Code Format ************* * * +-------------------------+> aligned section boundary * | ihdtyp | * +-------------------------> aligned section boundary * | lit text pool | * +-------------------------- * | lit mval table | * +-------------------------- * | hdr_offset (4-byte) | (8-byte on GTM64) * +-------------------------- * | validation (4-byte) | (8-byte on GTM64) * +-------------------------- * | Executable Code | * +-------------------------- * | variable Table | * +-------------------------+ * ***************************************************/ void ind_code(mstr *obj) { var_tabent *vptr; ihdtyp *itext; uint4 indir_code_size; IA64_ONLY(int old_code_size;) INTPTR_T validation, hdr_offset, long_temp; unsigned char *indr_base_addr; assert(run_time); curr_addr = SIZEOF(ihdtyp); cg_phase = CGP_APPROX_ADDR; cg_phase_last = CGP_NOSTATE; IA64_ONLY(calculated_code_size = 0); code_gen(); code_size = curr_addr; cg_phase = CGP_ADDR_OPT; # if (!defined(USHBIN_SUPPORTED) && !defined(VMS)) /* non-shared binary UNIX platforms */ shrink_jmps(); # endif /* GTM64: assuming indir_code_size wont exceed MAX_INT */ indir_code_size = (uint4)((SECTION_ALIGN_BOUNDARY - 1) /* extra padding to align the beginning of the entire indirect object */ + SIZEOF(ihdtyp) + PADLEN(SIZEOF(ihdtyp), NATIVE_WSIZE) /* extra padding to align the beginning of lit text pool */ + ROUND_UP2(indr_stringpool.free - indr_stringpool.base, NATIVE_WSIZE) /* base literal strings */ + mlitmax * SIZEOF(mval) /* literal mval table aligned at NATIVE_WSIZE boundary */ + (SIZEOF(INTPTR_T) * 2) /* validation word and (neg) offset to ihdtyp */ /* SIZEOF(INTPTR_T) is used for alignment reasons */ + GTM64_ONLY((SECTION_ALIGN_BOUNDARY - 1)) /* extra padding to align the beginning of the code address */ + code_size /* code already aligned at SECTION_ALIGN_BOUNDARY boundary */ + mvmax * SIZEOF(var_tabent)); /* variable table ents */ ENSURE_STP_FREE_SPACE(indir_code_size); /* Align the beginning of the indirect object so that ihdtyp fields can be accessed normally */ stringpool.free = (unsigned char *)ROUND_UP2((UINTPTR_T)stringpool.free, SECTION_ALIGN_BOUNDARY); itext = (ihdtyp *)stringpool.free; indr_base_addr = stringpool.free; stringpool.free += SIZEOF(ihdtyp); indir_lits(itext); /* Runtime base (fp->ctxt) needs to be set to the beginning of the Executable code so that * the literal references are generated with appropriate (-ve) offsets from the base * register (fp->ctxt). On USHBIN_SUPPORTED platforms, runtime_base should be computed * before shrink_trips since it could be used in codegen of literals */ runtime_base = stringpool.free + SIZEOF(hdr_offset) + SIZEOF(validation); /* Align the begining of the code so that it can be access properly. */ GTM64_ONLY(runtime_base = (unsigned char *)ROUND_UP2((UINTPTR_T)runtime_base, SECTION_ALIGN_BOUNDARY);) IA64_ONLY(old_code_size = code_size;) # if defined(USHBIN_SUPPORTED) || defined(VMS) shrink_trips(); # endif IA64_ONLY( if (old_code_size != code_size) calculated_code_size -= ((old_code_size - code_size)/16); ) /* Alignment for the starting of code address before code_gen.*/ GTM64_ONLY(stringpool.free = (unsigned char *)ROUND_UP2((UINTPTR_T)stringpool.free, SECTION_ALIGN_BOUNDARY);) assert(0 == GTM64_ONLY(PADLEN((UINTPTR_T)stringpool.free, SECTION_ALIGN_BOUNDARY)) NON_GTM64_ONLY(PADLEN((UINTPTR_T)stringpool.free, SIZEOF(int4)))); /* Since we know stringpool is aligned atleast at 4-byte boundary, copy both offset and validation * words with integer assignments instead of copying them by emit_immed(). */ hdr_offset = indr_base_addr - stringpool.free; /* -ve offset to ihdtyp */ *(INTPTR_T *)stringpool.free = hdr_offset; stringpool.free += SIZEOF(hdr_offset); validation = MAGIC_COOKIE; /* Word to validate we are in right place */ *(UINTPTR_T *)stringpool.free = validation; stringpool.free += SIZEOF(validation); cg_phase = CGP_MACHINE; IA64_ONLY(generated_code_size = 0); code_gen(); IA64_ONLY(assert(calculated_code_size == generated_code_size)); long_temp = stringpool.free - indr_base_addr; assert(0 == PADLEN(long_temp, SIZEOF(INTPTR_T))); /* Just to make sure things are aligned for the vartab that follows */ /* variable table */ itext->vartab_off = (int4)long_temp; itext->vartab_len = mvmax; vptr = (var_tabent*)mcalloc(mvmax * SIZEOF(var_tabent)); if (mvartab) walktree(mvartab, ind_cg_var, (char *)&vptr); else assert(0 == mvmax); emit_immed((char *) vptr, mvmax * SIZEOF(var_tabent)); itext->temp_mvals = sa_temps[TVAL_REF]; itext->temp_size = sa_temps_offset[TCAD_REF]; /* indir_code_size may be greater than the actual resultant code size because expression coersion may cause some literals * to be optimized away, leaving mlitmax greater than actual. */ assert(indir_code_size >= stringpool.free - indr_base_addr); /* Return object code pointers on stack. A later cache_put will move * the code to its new home and do the necessary cleanup on it. */ obj->addr = (char *)indr_base_addr; obj->len = INTCAST(stringpool.free - indr_base_addr); } fis-gtm-V7.0-005/sr_port/indir.h0000755000032200000250000000732214342376331015330 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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. * * * ****************************************************************/ /* The third column represents the opcodes for functions to be used by op_indfun(). * The one parameter version of $name can probably be folded into op_indfun, but at a later time. * Note: *** Please add new entries to end of list so as not to cause execution problems for * compilations from previous versions. Yes, they should have recompiled but we can * avoid exploding by simply adding entries to end of list. *** * This comment and the preceeding empty lines put the first item at line 20, so adding 20 to an argcode * places you on the line with its information or subtracting 20 from a line gives the corresponding argcode */ INDIR(indir_fndata, f_data, OC_FNDATA) ,INDIR(indir_fnnext, f_next, OC_FNNEXT) ,INDIR(indir_fnorder1, f_order1, OC_FNORDER) ,INDIR(indir_get, f_get1, OC_FNGET) ,INDIR(indir_close, m_close, 0) ,INDIR(indir_hang, m_hang, 0) ,INDIR(indir_if, m_if, 0) ,INDIR(indir_kill, m_kill, 0) ,INDIR(indir_open, m_open, 0) ,INDIR(indir_read, m_read, 0) ,INDIR(indir_set, m_set, 0) ,INDIR(indir_use, m_use, 0) ,INDIR(indir_write, m_write, 0) ,INDIR(indir_xecute, m_xecute, 0) ,INDIR(indir_nref, nref, 0) ,INDIR(indir_lock, m_lock, 0) ,INDIR(indir_do, m_do, 0) ,INDIR(indir_goto, m_goto, 0) ,INDIR(indir_job, m_job, 0) ,INDIR(indir_linetail, linetail, 0) ,INDIR(indir_new, m_new, 0) ,INDIR(indir_zlink, m_zlink, 0) ,INDIR(indir_zbreak, m_zbreak, 0) ,INDIR(indir_zsystem, m_zsystem, 0) ,INDIR(indir_zedit, m_zedit, 0) ,INDIR(indir_zmess, m_zmessage, 0) ,INDIR(indir_zwatch, m_zwatch, 0) ,INDIR(indir_zgoto, m_zgoto, 0) ,INDIR(indir_zprint, m_zprint, 0) ,INDIR(indir_zwrite, m_zwrite, 0) ,INDIR(indir_glvn, indirection, 0) ,INDIR(indir_lvadr, indirection, 0) ,INDIR(indir_pattern, indirection, 0) ,INDIR(indir_iset, indirection, 0) ,INDIR(indir_text, indirection, 0) ,INDIR(indir_view, m_view, 0) ,INDIR(indir_zattach, m_zattach, 0) ,INDIR(indir_zallocate, m_zallocate, 0) ,INDIR(indir_zdeallocate, m_zdeallocate, 0) ,INDIR(indir_lvarg, indirection, 0) ,INDIR(indir_fnzprevious, f_zprevious, OC_FNZPREVIOUS) ,INDIR(indir_fnquery, f_query, OC_FNQUERY) ,INDIR(indir_zhelp, m_zhelp, 0) ,INDIR(indir_zshow, m_zshow, 0) ,INDIR(indir_lvnamadr, indirection, 0) ,INDIR(indir_zwithdraw, m_zwithdraw, 0) ,INDIR(indir_tstart, m_tstart, 0) ,INDIR(indir_fnname, f_name, 0) /* f_name is really a dummy */ ,INDIR(indir_fnorder2, f_order, 0) ,INDIR(indir_fnzqgblmod, f_zqgblmod, OC_FNZQGBLMOD) ,INDIR(indir_trollback, m_trollback, 0) ,INDIR(indir_devparms, indirection, 0) ,INDIR(indir_merge, m_merge, 0) ,INDIR(indir_merge1, m_merge, 0) ,INDIR(indir_merge2, m_merge, 0) ,INDIR(indir_fntext, f_text, OC_FNTEXT) ,INDIR(indir_quit, m_quit, 0) ,INDIR(indir_increment, f_incr, 0) ,INDIR(indir_fnzahandle, f_zahandle, OC_FNZAHANDLE) ,INDIR(indir_fnzdata, f_data, OC_FNZDATA) #ifdef GTM_TRIGGER ,INDIR(indir_ztrigger, m_ztrigger, 0) #endif ,INDIR(indir_zhalt, m_zhalt, 0) ,INDIR(indir_savglvn0, indirection, 0) /* this entry and the following use indirection as a dummy value */ ,INDIR(indir_savlvn, indirection, 0) ,INDIR(indir_savglvn1, indirection, 0) /* 0 and 1 (above) separate 2 variants of generated code */ #ifdef AUTORELINK_SUPPORTED ,INDIR(indir_zrupdate, m_zrupdate, 0) #endif fis-gtm-V7.0-005/sr_port/indir_enum.h0000755000032200000250000000101214342376331016342 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, Inc. * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #define INDIR(a,b,c) a enum indir_enum { #include "indir.h" }; #undef INDIR fis-gtm-V7.0-005/sr_port/indir_lits.c0000644000032200000250000000340714342376333016355 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "gtm_string.h" #include "compiler.h" #include "mdq.h" #include "stringpool.h" #include #include "copy.h" #include "obj_file.h" #include "cache.h" GBLREF mliteral literal_chain; GBLREF spdesc stringpool, indr_stringpool; void indir_lits(ihdtyp *ihead) { ssize_t size, lits_pad_len, hdr_pad_len; int4 long_temp; mliteral *lit; hdr_pad_len = PADLEN(SIZEOF(ihdtyp), NATIVE_WSIZE); if (hdr_pad_len) /* additional padding to ihdtyp so that the literal text pool begins at the aligned boundary */ emit_immed(PADCHARS, (uint4)hdr_pad_len); size = indr_stringpool.free - indr_stringpool.base; emit_immed((char *)indr_stringpool.base, (uint4)size); lits_pad_len = PADLEN(size, NATIVE_WSIZE); /* Length to pad to put instructions on even bdy */ if (lits_pad_len) emit_immed(PADCHARS, (uint4)lits_pad_len); /* Pad out with extraneous info */ size += (SIZEOF(ihdtyp) + hdr_pad_len + lits_pad_len); ihead->fixup_vals_off = (int4)(ROUND_UP2(size, NATIVE_WSIZE)); ihead->fixup_vals_num = 0; dqloop(&literal_chain, que, lit) { if (lit->rt_addr < 0) { ihead->fixup_vals_num++; lit->rt_addr = (INTPTR_T)stringpool.free; lit->v.str.addr = (char *)((lit->v.str.addr - (char *)indr_stringpool.base) + SIZEOF(ihdtyp) + hdr_pad_len); emit_immed((char *)&lit->v, SIZEOF(mval)); } } } fis-gtm-V7.0-005/sr_port/indirection.c0000755000032200000250000000641014342376331016522 0ustar librarygtc /**************************************************************** * * * Copyright (c) 2001-2018 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_string.h" /* needed by INCREMENT_EXPR_DEPTH */ #include "compiler.h" #include "opcode.h" #include "mdq.h" #include "toktyp.h" #include "advancewindow.h" #include "fullbool.h" #include "show_source_line.h" #include "stringpool.h" GBLREF boolean_t run_time; GBLREF spdesc stringpool; GBLREF int source_column; error_def(ERR_EXPR); error_def(ERR_LPARENMISSING); error_def(ERR_MAXNRSUBSCRIPTS); error_def(ERR_RPARENMISSING); error_def(ERR_SIDEEFFECTEVAL); int indirection(oprtype *a) { char c; oprtype *sb1, *sb2, subs[MAX_INDSUBSCRIPTS], x; triple *next, *ref; int parens, oldlen, len; char *start, *end, *oldend; boolean_t concat_athashes; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(TK_ATSIGN == TREF(window_token)); concat_athashes = (2 == source_column); INCREMENT_EXPR_DEPTH; advancewindow(); if ((TREF(xecute_literal_parse) && ! TREF(trigger_compile_and_link)) || !expratom(a)) { DECREMENT_EXPR_DEPTH; stx_error(ERR_EXPR); return FALSE; } coerce(a, OCT_MVAL); ex_tail(a); ENCOUNTERED_SIDE_EFFECT; DECREMENT_EXPR_DEPTH; if ((TK_ATSIGN == TREF(window_token)) || ((TK_ATHASH == TREF(window_token)) && concat_athashes)) { (TREF(indirection_mval)).mvtype = 0; (TREF(indirection_mval)).str.len = 0; CLEAR_MVAL_BITS(TADR(indirection_mval)); do { start = TREF(lexical_ptr); advancewindow(); if (TK_LPAREN != TREF(window_token)) { stx_error(ERR_LPARENMISSING); return FALSE; } advancewindow(); if (!parse_until_rparen_or_space() || (TK_RPAREN != TREF(window_token))) { stx_error(ERR_RPARENMISSING); return FALSE; } end = ((TREF(source_buffer)).addr + (INTPTR_T)source_column - 1); /* lexical_ptr b4 last advancewindow */ len = INTCAST(end - start); oldlen = (TREF(indirection_mval)).str.len; ENSURE_STP_FREE_SPACE(oldlen + len); /* Ok to copy from beginning each iteration because we generally expect no more than two iterations, * and that's with nested indirection. */ memcpy(stringpool.free, (TREF(indirection_mval)).str.addr, oldlen); if (oldlen) { oldend = (char *)stringpool.free + oldlen - 1; assert(*oldend == ')'); *oldend = ','; } (TREF(indirection_mval)).mvtype = MV_STR; (TREF(indirection_mval)).str.addr = (char *)stringpool.free; (TREF(indirection_mval)).str.len = oldlen + len; stringpool.free += oldlen; memcpy(stringpool.free, start, len); stringpool.free += len; advancewindow(); } while ((TK_ATHASH == TREF(window_token)) && concat_athashes); ref = newtriple(OC_INDNAME); ref->operand[0] = *a; ref->operand[1] = put_lit(&(TREF(indirection_mval))); (TREF(indirection_mval)).mvtype = 0; /* so stp_gcol (BYPASSOK) - if invoked later - can free up space */ *a = put_tref(ref); } return TRUE; } fis-gtm-V7.0-005/sr_port/ins_errtriple.c0000755000032200000250000000646314342376331017104 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "compiler.h" #include "opcode.h" #include "mdq.h" GBLREF int4 pending_errtriplecode; /* if non-zero contains the error code to invoke ins_errtriple with */ GBLREF triple t_orig; error_def(ERR_ASSERT); error_def(ERR_GTMASSERT); error_def(ERR_DIVZERO); error_def(ERR_NEGFRACPWR); error_def(ERR_NUMOFLOW); error_def(ERR_PATNOTFOUND); void ins_errtriple(int4 in_error) { boolean_t add_rterror_triple; triple *triptr, *x; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (!IS_STX_WARN(in_error) GTMTRIG_ONLY( || TREF(trigger_compile_and_link))) { /* Not a warning and not a trigger, we have a real error (warnings become errors in triggers) */ if (TREF(curtchain) != &t_orig) { /* If working with more than 1 chain defer until back to 1 because dqdelchain cannot delete across * multiple chains. Set global variable "pending_errtriplecode" and let "setcurtchain" call here again. */ if (!pending_errtriplecode) /* Give user only the first error on the line */ pending_errtriplecode = in_error; /* Save error for later insert */ return; } x = (TREF(pos_in_chain)).exorder.bl; /* If first error in the current line/cmd, delete all triples and replace them with an OC_RTERROR triple. */ add_rterror_triple = (OC_RTERROR != x->exorder.fl->opcode); if (!add_rterror_triple) { /* This is the second error in this line/cmd. Check for triples added after OC_RTERROR and remove them * as there could be dangling references amongst them which could later cause assertpro() in emit_code. */ x = x->exorder.fl; assert(OC_RTERROR == x->opcode);/* corresponds to newtriple(OC_RTERROR) in previous ins_errtriple */ x = x->exorder.fl; assert(OC_ILIT == x->opcode); /* corresponds to put_ilit(in_error) in previous ins_errtriple */ x = x->exorder.fl; assert(OC_ILIT == x->opcode); /* corresponds to put_ilit(FALSE) in previous ins_errtriple */ } /* delete all trailing triples and if the first, replace them below with an OC_RTERROR triple */ dqdelchain(x, TREF(curtchain), exorder); CHKTCHAIN(TREF(curtchain), exorder, FALSE); assert(!add_rterror_triple || ((TREF(pos_in_chain)).exorder.bl->exorder.fl == TREF(curtchain))); assert(!add_rterror_triple || ((TREF(curtchain))->exorder.bl == (TREF(pos_in_chain)).exorder.bl)); if ((ERR_DIVZERO == in_error) || (ERR_NEGFRACPWR == in_error) || (ERR_NUMOFLOW == in_error) || (ERR_PATNOTFOUND == in_error)) TREF(rts_error_in_parse) = TRUE; } else /* For IS_STX_WARN errors (if not compiling a trigger), parsing continues, so dont strip the chain */ add_rterror_triple = TRUE; if (add_rterror_triple) { /* WARN error or first error in the current line */ triptr = newtriple(OC_RTERROR); triptr->operand[0] = put_ilit(in_error); triptr->operand[1] = put_ilit(FALSE); /* not a subroutine reference */ } } fis-gtm-V7.0-005/sr_port/ins_triple.c0000755000032200000250000000132014342376331016356 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "compiler.h" #include "mdq.h" void ins_triple(triple *x) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; dqnoop(TREF(curtchain)); dqrins(TREF(curtchain), exorder, x); } fis-gtm-V7.0-005/sr_port/insert_region.c0000755000032200000250000002004614342376331017063 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ /* insert_region.c * requirement * reg initialized (gvcst_init) * parameters * reg the region to be inserted * reg_list pointer to the pointer to the list head * reg_free_list pointer to the pointer to the free list * size size of the structure of each item in the list * return * pointer to the item in the list that is corresponding to the region. * *reg_list and *reg_free_list are also updated if needed. * fid_index field in csa and tp_reg_list is maintained by gvcst_init. Maintaining tp_reg_list is * important, since the regions might be re-sorted in between insert_region() calls (i.e. new * regions opening). dse_all() opens the regions before it calls insert_region(), so maintaining * fid_index in tp_reg_list is sufficient. */ #include "mdef.h" #include "gtm_ipc.h" /* needed for FTOK */ #include "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdscc.h" #include "gdskill.h" #include "filestruct.h" #include "jnl.h" #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #include "iosp.h" #include "dbfilop.h" #include "gtmmsg.h" #include "is_file_identical.h" #include "t_retry.h" #include "wcs_mm_recover.h" #include "gtmimagename.h" GBLREF gd_region *gv_cur_region; GBLREF tp_region *tp_reg_list; /* Ptr to list of tp_regions for a TP transaction */ GBLREF uint4 dollar_tlevel; GBLREF unsigned int t_tries; error_def(ERR_DBFILOPERR); tp_region *insert_region( gd_region *reg, tp_region **reg_list, tp_region **reg_free_list, int4 size) { boolean_t t_retry_needed; enc_info_t *encr_ptr; int4 local_fid_index, match; sgmnt_addrs *csa, *tr_csa; tp_region *tr, *tr_last, *tr_new, *tr_resort; unique_file_id local_id; int save_errno; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(SIZEOF(tp_region) <= size); if (&tp_reg_list != reg_list) { /* gvcst_init does this for the tp_reg_list, but do others here because they are relatively rare */ for (tr = *reg_list; NULL != tr; tr = tr->fPtr) if (tr->reg->open) tr->fid_index = FILE_INFO(tr->reg)->s_addrs.fid_index; DBG_CHECK_TP_REG_LIST_SORTING(*reg_list); } else assert(dollar_tlevel); if (reg->open) { csa = (sgmnt_addrs *)&FILE_INFO(reg)->s_addrs; local_fid_index = csa->fid_index; } else { if (!mupfndfil(reg, NULL, LOG_ERROR_TRUE)) return NULL; if (SS_NORMAL != (save_errno = filename_to_id(&local_id.uid, (char *)reg->dyn.addr->fname))) { /* WARNING: assignment above */ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_DBFILOPERR, 2, LEN_AND_STR(reg->dyn.addr->fname), save_errno); return NULL; } } /* See if the region is already on the list or if we have to add it */ for (tr = *reg_list, tr_last = NULL; NULL != tr; tr = tr->fPtr) { if ((reg->open) && ((tr->reg->open))) { assert(tr->fid_index == FILE_INFO(tr->reg)->s_addrs.fid_index); if (local_fid_index == tr->fid_index) { /* probable find - assert not in final retry or in TP and have crit on the region already */ assert((CDB_STAGNATE > t_tries) || (dollar_tlevel && csa->now_crit)); if (reg == tr->reg) /* Region is really found */ return tr; else /* note that it is possible that "reg" and "tr->reg" are different although their * "fid_index" is the same. This is possible if both regions point to the same physical file. * in this case we return the existing "tr" instead of creating a new one. */ return tr; } if (tr->fid_index > local_fid_index) break; /* found insertion point */ } else { /* at least 1 of new region and/or existing region are not open so use slower sorting - s.b. unusual */ if (tr->reg->open) { save_errno = filename_to_id(&tr->file_id, (char *)tr->reg->dyn.addr->fname); assert(SS_NORMAL == save_errno); } if (reg->open) { save_errno = filename_to_id(&local_id.uid, (char *)reg->dyn.addr->fname); assert(SS_NORMAL == save_errno); } match = gdid_cmp(&(tr->file_id), &(local_id.uid)); if (0 == match) return tr; if (0 < match) break; /* found insertion point */ } tr_last = tr; } if ((NULL != reg_free_list) && (NULL != *reg_free_list)) /* Get a used block off our unused queue */ { tr_new = *reg_free_list; /* Get element */ *reg_free_list = tr_new->fPtr; /* Remove from queue */ } else /* get a new one */ { tr_new = (tp_region *)malloc(size); if (size > SIZEOF(tp_region)) memset(tr_new, 0, size); } tr_new->reg = reg; /* Add this region to the list */ if (!reg->open) { /* should be unusual */ tr_new->file_id = local_id.uid; if (NULL != tr) { tr_new->fid_index = tr->fid_index; for (tr_resort = tr, local_fid_index = tr_resort->fid_index; NULL != tr_resort; tr_resort = tr_resort->fPtr) { if (tr->reg->open) tr_resort->fid_index = local_fid_index = FILE_INFO(tr_resort->reg)->s_addrs.fid_index; else tr_resort->fid_index = ++local_fid_index; } } else tr_new->fid_index = 1; } else tr_new->fid_index = local_fid_index; if (NULL == tr_last) { /* First element on the list */ tr_new->fPtr = *reg_list; *reg_list = tr_new; } else { /* Insert into list */ tr_new->fPtr = tr_last->fPtr; tr_last->fPtr = tr_new; } if ((CDB_STAGNATE <= t_tries) && dollar_tlevel && reg->open && !csa->now_crit && (&tp_reg_list == reg_list)) { /* Final retry in TP and this region not locked down. Get crit on it if it is open. * reg->open needs to be checked above to take care of the case where we do an insert_region() from gvcst_init() * in the 3rd retry in TP when we have not yet opened the region. In case region is not open, * tp_restart() (invoked through t_retry from gvcst_init) will open "reg" as well as get crit on it for us. */ DEBUG_ONLY(TREF(ok_to_call_wcs_recover) = TRUE;) t_retry_needed = (FALSE == grab_crit_immediate(reg, OK_FOR_WCS_RECOVER_TRUE, NOT_APPLICABLE)); /*Try lockdown now */ if (!t_retry_needed) { /* The "grab_crit_immediate" returned successfully. Check if encryption cycles match. * If they dont, we need to do "grab_crit_encr_cycle_check" but that uses grab_crit and * is not easily possible to convert it to a "grab_crit_immediate". So assume as if * the "grab_crit_immediate" failed in this case too and do "t_retry" instead which will * do the right thing of releasing crit on all regions and fixing the encryption cycles * on all regions before getting crit on all of them. */ encr_ptr = csa->encr_ptr; if ((NULL != encr_ptr) && (csa->nl->reorg_encrypt_cycle != encr_ptr->reorg_encrypt_cycle)) { rel_crit(reg); t_retry_needed = TRUE; } } if (t_retry_needed) { DEBUG_ONLY(TREF(ok_to_call_wcs_recover) = FALSE;) t_retry(cdb_sc_needcrit); /* avoid deadlock -- restart transaction */ assert(FALSE); /* should not come here as t_retry() does not return */ } DEBUG_ONLY(TREF(ok_to_call_wcs_recover) = FALSE;) assert(csa->now_crit); /* ensure we have crit now */ CHECK_MM_DBFILEXT_REMAP_IF_NEEDED(csa, reg); # ifdef UNIX if (MISMATCH_ROOT_CYCLES(csa, csa->nl)) { /* Going into this retry, we have already checked in tp_restart for moved root blocks in tp_reg_list. * Since we haven't yet checked this region, we check it here and reset clues for an globals in the * newly inserted region. We don't want to reset ALL gvt clues because the current retry may have made * use (and valid use at that) of clues for globals in other regions. */ RESET_ALL_GVT_CLUES_REG(csa); csa->root_search_cycle = csa->nl->root_search_cycle; } # endif } DBG_CHECK_TP_REG_LIST_SORTING(*reg_list); return tr_new; } fis-gtm-V7.0-005/sr_port/inst_flush.h0000755000032200000250000000110114342376331016366 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef INST_FLUSH_INCLUDED #define INST_FLUSH_INCLUDED void inst_flush(void *start, int4 len); #endif /* INST_FLUSH_INCLUDED */ fis-gtm-V7.0-005/sr_port/int_label.c0000755000032200000250000000152114342376331016142 0ustar librarygtc/**************************************************************** * * * 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 "gtm_string.h" #include "compiler.h" #include "toktyp.h" void int_label(void) { int len; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; TREF(window_token) = TK_IDENT; len = (TREF(window_mval)).str.len; len = (len < MAX_MIDENT_LEN) ? len: MAX_MIDENT_LEN; memcpy((TREF(window_ident)).addr, (TREF(window_mval)).str.addr, len); (TREF(window_ident)).len = len; } fis-gtm-V7.0-005/sr_port/int_namelook.c0000644000032200000250000000407214342376331016671 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "compiler.h" #include "opcode.h" #include "toktyp.h" #include "nametabtyp.h" #include "gtm_caseconv.h" #include "int_namelook.h" /* This routine performs the same function as namelook() only the first parameter for this version is an array of type uint4 vs unsigned char in namelook(). This permits a much larger number of deviceparameter names. The purpose of this function is to search for the character string in in the 3rd input parameter called str. It can find either an exact match or a match which has an * as its final character. The table containing names to compare is the 2nd parameter called nametabent. If the name is found then the offset into the nametabent is returned. If not found, a -1 is returned. */ int int_namelook(const uint4 offset_tab[], const nametabent *name_tab, char *str, int strlength) { const nametabent *top, *i; uint4 find; unsigned char temp[NAME_ENTRY_SZ], x; if (strlength > NAME_ENTRY_SZ) return -1; lower_to_upper(&temp[0], (uchar_ptr_t)str, strlength); if ('%' == (x = temp[0])) /* WARNING assignment */ return -1; i = name_tab + offset_tab[x -= 'A']; top = name_tab + offset_tab[++x]; assert((i == top) || (i->name[0] >= temp[0])); for (; i < top; i++) { if ((strlength == i->len) || ((strlength > i->len) && ('*' == i->name[i->len]))) { if (!(find = memcmp(&temp[0], i->name, (int4)(i->len)))) /* WARNING assignment */ return (int)(i - name_tab); if (0 > find) return -1; /* assumes alpha ordering */ } } return -1; } fis-gtm-V7.0-005/sr_port/int_namelook.h0000644000032200000250000000116514342376331016676 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ #ifndef NAMELOOK_INCLUDED #define NAMELOOK_INCLUDED int int_namelook(const uint4 offset_tab[], const nametabent *name_tab, char *str, int len); #endif /* NAMELOOK_INCLUDED */ fis-gtm-V7.0-005/sr_port/io_dev_dispatch.h0000755000032200000250000000210214342376331017336 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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. * * * ****************************************************************/ /* mupip_io_dev_dispatch.h is a subset of this file which includes those needed only by MUPIP * so, if you need to make a change here, please keep the other one in sync. */ GBLDEF dev_dispatch_struct io_dev_dispatch[] = { iotype(iott, iott, iott, nil), ionil_dev, /* placeholder where mt once lived */ iotype(iorm, iorm, iopi, iopi), iotype(ious, ious, ious, nil), ionil_dev, /* placeholder where mb once lived */ iotype(ionl, ionl, nil, nil), iotype(ioff, iorm, iopi, nil), iotype(iosocket, iosocket, iosocket, iosocket), iotype(iopi, iorm, iopi, iopi) }; fis-gtm-V7.0-005/sr_port/io_init.c0000644000032200000250000001677114342376331015655 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #ifdef UNIX #include "gtmio.h" #endif #include "io.h" #include "iosp.h" #include "io_params.h" #include "error.h" #include "op.h" #include "term_setup.h" #include "trans_log_name.h" GBLREF boolean_t err_same_as_out, prin_in_dev_failure, prin_out_dev_failure; GBLREF int (*op_open_ptr)(mval *v, mval *p, const mval *t, mval *mspace); GBLREF io_log_name *io_root_log_name; /* root of linked list */ GBLREF io_log_name *dollar_principal; /* pointer to log name GTM$PRINCIPAL if defined */ GBLREF io_pair io_curr_device; /* current device */ GBLREF io_pair io_std_device; /* standard device */ GBLREF mstr gtm_principal, sys_input, sys_output; LITREF mval literal_zero; error_def(ERR_FILEOPENFAIL); error_def(ERR_LOGTOOLONG); error_def(ERR_SYSCALL); void io_init(boolean_t term_ctrl) { static readonly unsigned char open_params_list[2] = { (unsigned char)iop_newversion, (unsigned char)iop_eol }; static readonly unsigned char nolognam_params_list[] = { # ifdef UNIX (unsigned char)iop_stream, /* open FILEs in Unix with STREAM option by default */ # endif (unsigned char)iop_nl, (unsigned char)iop_eol }; # ifdef UNIX static readonly unsigned char nowrap_params_list[] = { (unsigned char)iop_nowrap, (unsigned char)iop_eol }; # endif static readonly unsigned char no_params = (unsigned char)iop_eol; static readonly unsigned char shr_params[] = { (unsigned char)iop_shared, (unsigned char)iop_readonly, (unsigned char)iop_eol }; int4 status; mval val; mstr tn; MSTR_CONST (gtm_netout, "GTM_NETOUT"); MSTR_CONST (sys_net, "SYS$NET"); char buf1[MAX_TRANS_NAME_LEN]; /* buffer to hold translated name */ mval pars; io_log_name *inp, *outp; io_log_name *ln; enum io_dev_type dev_type; # ifdef UNIX int fd, newfd; struct stat statbuf, out_statbuf; # endif # ifdef UNIX /* Make sure we have valid descriptors on stdin/stdout/stderr. * Otherwise we could end up "filling the hole" with a database file and writing an error message to it. */ for (fd = 0; fd < 3; fd++) { status = fstat(fd, &statbuf); if (-1 == status) { if (EBADF == errno) { OPENFILE(DEVNULL, ((0 == fd) ? O_RDONLY : O_RDWR), newfd); if (-1 == newfd) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_SYSCALL, 5, LEN_AND_LIT("open /dev/null on std descriptor"), CALLFROM, errno, 0); assert(newfd == fd); } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_SYSCALL, 5, LEN_AND_LIT("fstat of std descriptor"), CALLFROM, errno, 0); } else if (0 < fd) { if (1 == fd) out_statbuf = statbuf; else err_same_as_out = (statbuf.st_dev == out_statbuf.st_dev) && (statbuf.st_ino == out_statbuf.st_ino); } } # endif io_init_name(); /* default logical names */ io_root_log_name = (io_log_name *)malloc(SIZEOF(*io_root_log_name)); memset(io_root_log_name, 0, SIZEOF(*io_root_log_name)); val.mvtype = MV_STR; val.str.addr = "0"; val.str.len = 1; ln = get_log_name(&val.str, INSERT); assert(ln != 0); val.str = gtm_principal; status = TRANS_LOG_NAME(&val.str, &tn, buf1, SIZEOF(buf1), dont_sendmsg_on_log2long); if (SS_NOLOGNAM == status) dollar_principal = 0; else if (SS_NORMAL == status) dollar_principal = get_log_name(&tn, INSERT); # ifdef UNIX else if (SS_LOG2LONG == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_LOGTOOLONG, 3, val.str.len, val.str.addr, SIZEOF(buf1) - 1); # endif else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status); /* open devices */ val.str = sys_input; inp = get_log_name(&val.str, INSERT); pars.mvtype = MV_STR; status = TRANS_LOG_NAME(&val.str, &tn, buf1, SIZEOF(buf1), dont_sendmsg_on_log2long); if (SS_NOLOGNAM == status) { pars.str.len = SIZEOF(nolognam_params_list); pars.str.addr = (char *)nolognam_params_list; } else if (SS_NORMAL == status) { UNIX_ONLY(assert(FALSE);) if (!io_is_rm(&val.str)) { pars.str.len = SIZEOF(no_params); pars.str.addr = (char *)&no_params; } else if (io_is_sn(&val.str)) { pars.str.len = SIZEOF(open_params_list); pars.str.addr = (char *)open_params_list; } else { pars.str.len = SIZEOF(shr_params); pars.str.addr = (char *)shr_params; } } # ifdef UNIX else if (SS_LOG2LONG == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_LOGTOOLONG, 3, val.str.len, val.str.addr, SIZEOF(buf1) - 1); # endif else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status); ESTABLISH(io_init_ch); (*op_open_ptr)(&val, &pars, (mval *)&literal_zero, 0); io_curr_device.in = io_std_device.in = inp->iod; val.str = sys_output; if ((SS_NORMAL == TRANS_LOG_NAME(>m_netout, &tn, buf1, SIZEOF(buf1), do_sendmsg_on_log2long)) && (SS_NORMAL == TRANS_LOG_NAME(&sys_net, &tn, buf1, SIZEOF(buf1), do_sendmsg_on_log2long)) && io_is_sn(&sys_net)) val.str = sys_net; outp = get_log_name(&val.str, INSERT); status = TRANS_LOG_NAME(&val.str, &tn, buf1, SIZEOF(buf1), dont_sendmsg_on_log2long); if ((SS_NORMAL != status) && (SS_NOLOGNAM != status)) { # ifdef UNIX if (SS_LOG2LONG == status) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_LOGTOOLONG, 3, val.str.len, val.str.addr, SIZEOF(buf1) - 1); else # endif rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status); } if ((val.str.addr == sys_net.addr) && (pars.str.addr == (char *)open_params_list)) /* sys$net is the only input thing that uses open_params_list */ outp->iod = io_curr_device.in; /* For terminals and mailboxes and sockets, SYS$INPUT and SYS$OUTPUT may point to the same device. If input is one of those, then check translated name for output against translated name for input; in that case they should be joined by their logical names */ dev_type = io_curr_device.in->type; if (((tt == dev_type) || (mb == dev_type) || (gtmsocket == dev_type)) && same_device_check(tn, buf1)) outp->iod = io_curr_device.in; if (!outp->iod) { if (status == SS_NOLOGNAM) { pars.str.len = SIZEOF(nolognam_params_list); pars.str.addr = (char *)nolognam_params_list; } else if (status == SS_NORMAL) { pars.str.len = SIZEOF(open_params_list); pars.str.addr = (char *)open_params_list; } (*op_open_ptr)(&val, &pars, (mval *)&literal_zero, 0); } io_curr_device.out = io_std_device.out = outp->iod; term_setup(term_ctrl); io_std_device.out->pair = io_std_device; io_std_device.in->pair = io_std_device; io_std_device.out->perm = io_std_device.in->perm = TRUE; for (ln = io_root_log_name; ln; ln = ln->next) ln->iod = io_std_device.in; if (dollar_principal) dollar_principal->iod = io_std_device.in; pars.str.len = SIZEOF(no_params); pars.str.addr = (char *)&no_params; # ifdef UNIX /* If Unix and input/output device is one of rm (FILE) or ff (FIFO) or pi (PIPE) or gtmsocket (SOCKET), * open device by default with NOWRAP option. */ if ((rm == dev_type) || (ff == dev_type) || (pi == dev_type) || (gtmsocket == dev_type)) { pars.str.len = SIZEOF(nowrap_params_list); pars.str.addr = (char *)nowrap_params_list; } # endif val.str.len = io_curr_device.in->trans_name->len; val.str.addr = io_std_device.in->trans_name->dollar_io; op_use(&val, &pars); REVERT; return; } fis-gtm-V7.0-005/sr_port/io_init_ch.c0000755000032200000250000000165114342376331016321 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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 "gtm_string.h" #include "io.h" #include "iottdef.h" #include "error.h" #include "setterm.h" #include "util.h" GBLREF io_log_name *io_root_log_name; CONDITION_HANDLER(io_init_ch) { io_log_name *iol; START_CH(TRUE); # ifdef VMS if (INFO == SEVERITY) { PRN_ERROR; CONTINUE; } # endif for (iol = io_root_log_name; 0 != iol; iol = iol->next) { if (iol->iod && (iol->iod->type == tt) && iol->iod->dev_sp) resetterm(iol->iod); } NEXTCH; } fis-gtm-V7.0-005/sr_port/io_params.h0000755000032200000250000000204514342376331016172 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, Inc. * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #define MAXDEVPARLEN 1024 #define IOP_VAR_SIZE 255 #define IOP_OPEN_OK 1 #define IOP_USE_OK 2 #define IOP_CLOSE_OK 4 #define IOP_SRC_INT 1 /* source is integer */ #define IOP_SRC_STR 2 /* source is string */ #define IOP_SRC_MSK 3 /* source is character mask */ #define IOP_SRC_PRO 4 /* source is protection mask */ #define IOP_SRC_LNGMSK 5 /* source is int4 character mask */ #define IOP_SRC_TIME 6 /* source is the date-time string */ typedef struct { unsigned char valid_with; unsigned char source_type; } dev_ctl_struct; #define IOP_DESC(a,b,c,d,e) b enum io_params { #include "iop.h" }; #undef IOP_DESC fis-gtm-V7.0-005/sr_port/io_rundown.c0000644000032200000250000000474514342376331016404 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "io.h" #include "iosp.h" #include "io_params.h" #include "error.h" GBLREF boolean_t prin_in_dev_failure, prin_out_dev_failure; GBLREF io_log_name *io_root_log_name; GBLREF io_pair io_std_device; void io_dev_close(io_log_name *d); void io_rundown (int rundown_type) { io_log_name *l; /* logical name pointer */ if (NULL == io_root_log_name) return; for (l = io_root_log_name; NULL != l; free(l), l = io_root_log_name) { io_root_log_name = l->next; if ((NULL != l->iod) && (n_io_dev_types == l->iod->type)) { /* Device setup has started but did not complete (e.g. SIG-15 during device set up took us to exit handler) * Not much can be done in terms of rundown of this device so skip it. */ continue; } if (NULL != l->iod) { if ((NORMAL_RUNDOWN == rundown_type) || ((RUNDOWN_EXCEPT_STD == rundown_type) && ((l->iod->pair.in != io_std_device.in) && (l->iod->pair.out != io_std_device.out)))) io_dev_close(l); } } } void io_dev_close (io_log_name *d) { static readonly unsigned char p[] = {iop_rundown, iop_eol}; io_desc *iod; mval pp; # ifdef DEBUG int close_called; # endif iod = d->iod; if (iod->pair.in == io_std_device.in && iod->pair.out == io_std_device.out) { if (prin_out_dev_failure || (prin_in_dev_failure && (io_std_device.in == io_std_device.out))) return; } pp.mvtype = MV_STR; pp.str.addr = (char *) p; pp.str.len = SIZEOF(p); DEBUG_ONLY(close_called = 0;) if (iod->pair.in && (iod->pair.in->state == dev_open)) { DEBUG_ONLY(close_called = __LINE__;) (iod->pair.in->disp_ptr->close)(iod->pair.in, &pp); } if (iod->pair.out && (iod->pair.out->state == dev_open)) { DEBUG_ONLY(close_called = __LINE__;) (iod->pair.out->disp_ptr->close)(iod->pair.out, &pp); } if (iod->newly_created) { assert(0 == close_called); if (gtmsocket == iod->type) iosocket_destroy(iod); else if (rm == iod->type) remove_rms(iod); else assert(FALSE); /* Not sure what to cleanup */ } } fis-gtm-V7.0-005/sr_port/ionl_close.c0000755000032200000250000000105514342376331016341 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "io.h" void ionl_close(io_desc *dv, mval *p) { dv->state = dev_closed; return; } fis-gtm-V7.0-005/sr_port/ionl_dummy.c0000755000032200000250000000110614342376331016364 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "io.h" short ionl_dummy(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout) { return 0; } fis-gtm-V7.0-005/sr_port/ionl_flush.c0000755000032200000250000000101314342376331016347 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "io.h" void ionl_flush(io_desc *iod) { return; } fis-gtm-V7.0-005/sr_port/ionl_open.c0000755000032200000250000000412014342376331016171 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "errno.h" #include "gtm_unistd.h" #include "io_params.h" #include "io.h" #include "stringpool.h" #include "gtmio.h" #include "op.h" #include "indir_enum.h" #define DEF_NL_WIDTH 255 #define DEF_NL_LENGTH 66 LITREF unsigned char io_params_size[]; short ionl_open(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout) { unsigned char ch; io_desc *d_in, *d_out, *ioptr; int p_offset, status; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; p_offset = 0; /* If UNIX, then /dev/null was actually opened by io_open_try so we have to close it since we don't use the device, we just simulate it by doing nothing on writes except maintaining the appropriate pointers. We test for fd >= 0 since the less than zero values mean no device was opened. */ UNIX_ONLY( if (0 <= fd) CLOSEFILE_RESET(fd, status); /* resets "fd" to FD_INVALID */ ); ioptr = dev_name->iod; ioptr->state = dev_open; d_in = ioptr->pair.in; d_out = ioptr->pair.out; ioptr->length = DEF_NL_LENGTH; ioptr->width = DEF_NL_WIDTH; ioptr->wrap = TRUE; ioptr->dollar.za = 0; ioptr->dollar.zeof = FALSE; ioptr->dollar.x = 0; ioptr->dollar.y = 0; while (*(pp->str.addr + p_offset) != iop_eol) { switch (ch = *(pp->str.addr + p_offset++)) { case iop_wrap: d_out->wrap = TRUE; break; case iop_nowrap: d_out->wrap = FALSE; break; case iop_exception: DEF_EXCEPTION(pp, p_offset, ioptr); break; } p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); } return TRUE; } fis-gtm-V7.0-005/sr_port/ionl_rdone.c0000755000032200000250000000114714342376331016345 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2006 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. * * * ****************************************************************/ /* ionl_rdone.c */ #include "mdef.h" #include "io.h" int ionl_rdone(mint *val, int4 timeout) { mval tmp; *val = -1; return ionl_readfl(&tmp, 1, timeout); } fis-gtm-V7.0-005/sr_port/ionl_read.c0000755000032200000250000000107114342376331016145 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2006 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 "io.h" int ionl_read(mval *val, int4 timeout) { return ionl_readfl(val, 1, timeout); } fis-gtm-V7.0-005/sr_port/ionl_readfl.c0000644000032200000250000000172714342376331016474 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "io.h" GBLREF io_pair io_curr_device; error_def(ERR_IOEOF); int ionl_readfl(mval *val, int4 width, int4 timeout) { io_desc *dv; val->str.len = 0; dv = io_curr_device.in; dv->dollar.x = 0; dv->dollar.y++; if (dv->dollar.zeof || (dv->error_handler.len > 0)) { dv->dollar.zeof = TRUE; dv->dollar.za = ZA_IO_ERR; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_IOEOF); } dv->dollar.za = 0; dv->dollar.zeof = TRUE; return TRUE; } fis-gtm-V7.0-005/sr_port/ionl_use.c0000644000032200000250000000775114342376331016036 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "gtm_iconv.h" #include "gtm_stdlib.h" #include "copy.h" #include "io_params.h" #include "io.h" #include "iosp.h" #include "iottdef.h" #include "nametabtyp.h" #include "stringpool.h" #include "namelook.h" #include "op.h" #include "indir_enum.h" LITREF nametabent filter_names[]; LITREF unsigned char filter_index[27]; LITREF unsigned char io_params_size[]; error_def(ERR_TTINVFILTER); error_def(ERR_DEVPARMNEG); void ionl_use(io_desc *iod, mval *pp) { unsigned char ch, len; int fil_type; int4 width, length; io_desc *d_in, *d_out; char *tab; int p_offset; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; p_offset = 0; d_in = iod->pair.in; d_out = iod->pair.out; assert(iod->state == dev_open); while (*(pp->str.addr + p_offset) != iop_eol) { switch (ch = *(pp->str.addr + p_offset++)) { case iop_exception: DEF_EXCEPTION(pp, p_offset, iod); break; case iop_filter: len = (unsigned char)*(pp->str.addr + p_offset); tab = pp->str.addr + p_offset + 1; if ((fil_type = namelook(filter_index, filter_names, tab, len)) < 0) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_TTINVFILTER); return; } switch (fil_type) { case 0: iod->write_filter |= CHAR_FILTER; break; case 1: iod->write_filter |= ESC1; break; case 2: iod->write_filter &= ~CHAR_FILTER; break; case 3: iod->write_filter &= ~ESC1; break; } break; case iop_nofilter: iod->write_filter = 0; break; case iop_length: GET_LONG(length, pp->str.addr + p_offset); if (length < 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DEVPARMNEG); d_out->length = length; break; case iop_width: GET_LONG(width, pp->str.addr + p_offset); if (width < 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DEVPARMNEG); if (width == 0) { d_out->wrap = FALSE; d_out->width = TTDEF_PG_WIDTH; } else { d_out->width = width; d_out->wrap = TRUE; } break; case iop_wrap: d_out->wrap = TRUE; break; case iop_nowrap: d_out->wrap = FALSE; break; case iop_x: { int4 col; GET_LONG(col, pp->str.addr + p_offset); d_out->dollar.x = col; if ((int4)(d_out->dollar.x) < 0) d_out->dollar.x = 0; if (d_out->dollar.x > d_out->width && d_out->wrap) d_out->dollar.x %= d_out->width; break; } case iop_y: { int4 row; GET_LONG(row, (pp->str.addr + p_offset)); d_out->dollar.y = row; if ((int4)(d_out->dollar.y) < 0) d_out->dollar.y = 0; if (d_out->length) d_out->dollar.y %= d_out->length; break; } case iop_ipchset: { #if defined(KEEP_zOS_EBCDIC) if ((iconv_t)0 != iod->input_conv_cd) { ICONV_CLOSE_CD(iod->input_conv_cd); } SET_CODE_SET(iod->in_code_set, (char *)(pp->str.addr + p_offset + 1)); if (DEFAULT_CODE_SET != iod->in_code_set) ICONV_OPEN_CD(iod->input_conv_cd, (char *)(pp->str.addr + p_offset + 1), INSIDE_CH_SET); #endif break; } case iop_opchset: { #if defined(KEEP_zOS_EBCDIC) if ( (iconv_t) 0 != iod->output_conv_cd) { ICONV_CLOSE_CD(iod->output_conv_cd); } SET_CODE_SET(iod->out_code_set, (char *)(pp->str.addr + p_offset + 1)); if (DEFAULT_CODE_SET != iod->out_code_set) ICONV_OPEN_CD(iod->output_conv_cd, INSIDE_CH_SET, (char *)(pp->str.addr + p_offset + 1)); #endif break; } } p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); } return; } fis-gtm-V7.0-005/sr_port/ionl_write.c0000755000032200000250000000132214342376331016363 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "io.h" #include "dollarx.h" GBLREF io_pair io_curr_device; void ionl_write(mstr *v) { io_desc *io_ptr; io_ptr = io_curr_device.out; io_ptr->dollar.zeof = FALSE; dollarx(io_ptr, (uchar_ptr_t)v->addr, (uchar_ptr_t)v->addr + v->len); return; } fis-gtm-V7.0-005/sr_port/ionl_wteol.c0000755000032200000250000000313214342376331016364 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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 "gtm_string.h" #include "io.h" #include "iottdef.h" /* essentially the same as iott_wteol */ void ionl_wteol(int4 val, io_desc *io_ptr) { mstr eol; int eol_cnt; assert(val); io_ptr->esc_state = START; eol.len = STRLEN(NATIVE_TTEOL); eol.addr = (char *)NATIVE_TTEOL; for (eol_cnt = val; eol_cnt--; ) { io_ptr->dollar.x = 0; /* so that ionl_write doesn't try to wrap (based on escape state and width) */ ionl_write(&eol); } /* $X is maintained in VMS without the below assignment (resetting to 0) because the NATIVE_TTEOL is \015\012 * and the (\015) triggers appropriate maintenance of $X. In UNIX, NATIVE_TTEOL is \012, so * FILTER=CHARACTER effectively turns off all $X maintenance (except for WRAP logic). * In VMS the below assignment is not necessary, but harmless; it is always logically correct. */ io_ptr->dollar.x = 0; if (!(io_ptr->write_filter & CHAR_FILTER)) { /* If no FILTER and EOL, also maintain $Y; * If FILTER, dollarx() of the linefeed character \012 takes care of this maintenance. */ io_ptr->dollar.y += val; if (io_ptr->length) io_ptr->dollar.y %= io_ptr->length; } return; } fis-gtm-V7.0-005/sr_port/ionl_wtff.c0000755000032200000250000000127414342376331016205 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "io.h" GBLREF io_pair io_curr_device; void ionl_wtff(void) { io_curr_device.out->esc_state = START; io_curr_device.out->dollar.zeof = FALSE; io_curr_device.out->dollar.x = 0; io_curr_device.out->dollar.y = 0; return; } fis-gtm-V7.0-005/sr_port/ionl_wtone.c0000755000032200000250000000114414342376331016367 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2006 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 "io.h" void ionl_wtone(int v) { mstr temp; char p; p = (char)v; temp.len = 1; temp.addr = &p; ionl_write(&temp); return; } fis-gtm-V7.0-005/sr_port/iop.h0000755000032200000250000003221514342376331015011 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* iop.h, to be included when io parameters need enumeration */ /* structure: enum_value, enum_mnemonic, data_element_size, legal_operations , source_value_type */ /* last entry = n_iops is the size of the table */ /* Must add entries at the end in order to avoid a recompile */ IOP_DESC(0, iop_eol, 0, 0, 0), IOP_DESC(1, iop_canctlo, 0, IOP_USE_OK, 0), IOP_DESC(2, iop_cenable, 0, IOP_USE_OK, 0), IOP_DESC(3, iop_nocenable, 0, IOP_USE_OK, 0), IOP_DESC(4, iop_clearscreen, 0, IOP_USE_OK, 0), IOP_DESC(5, iop_convert, 0, IOP_USE_OK, 0), IOP_DESC(6, iop_noconvert, 0, IOP_USE_OK, 0), IOP_DESC(7, iop_downscroll, 0, IOP_USE_OK, 0), IOP_DESC(8, iop_echo, 0, IOP_USE_OK, 0), IOP_DESC(9, iop_noecho, 0, IOP_USE_OK, 0), IOP_DESC(10, iop_eraseline, 0, IOP_USE_OK, 0), IOP_DESC(11, iop_field, SIZEOF(short), IOP_USE_OK, IOP_SRC_INT), IOP_DESC(12, iop_terminator, SIZEOF(int4) * 8, IOP_USE_OK, IOP_SRC_LNGMSK), IOP_DESC(13, iop_upscroll, 0, IOP_USE_OK, 0), IOP_DESC(14, iop_width, SIZEOF(int4), IOP_USE_OK, IOP_SRC_INT), IOP_DESC(15, iop_blocksize, SIZEOF(int4), IOP_OPEN_OK, IOP_SRC_INT), IOP_DESC(16, iop_ctrap, SIZEOF(int4), IOP_USE_OK, IOP_SRC_MSK), IOP_DESC(17, iop_x, SIZEOF(int4), IOP_USE_OK, IOP_SRC_INT), IOP_DESC(18, iop_y, SIZEOF(int4), IOP_USE_OK, IOP_SRC_INT), IOP_DESC(19, iop_escape, 0, IOP_USE_OK, 0), IOP_DESC(20, iop_noescape, 0, IOP_USE_OK, 0), IOP_DESC(21, iop_allocation, SIZEOF(int4), IOP_OPEN_OK, IOP_SRC_INT), IOP_DESC(22, iop_contiguous, 0, IOP_OPEN_OK, 0), IOP_DESC(23, iop_delete, 0, IOP_CLOSE_OK, 0), IOP_DESC(24, iop_extension, SIZEOF(unsigned short), IOP_OPEN_OK, IOP_SRC_INT), IOP_DESC(25, iop_newversion, 0, IOP_OPEN_OK, 0), IOP_DESC(26, iop_nosequential, 0, IOP_OPEN_OK, 0), IOP_DESC(27, iop_s_protection, SIZEOF(char), IOP_OPEN_OK|IOP_CLOSE_OK, IOP_SRC_PRO), IOP_DESC(28, iop_w_protection, SIZEOF(char), IOP_OPEN_OK|IOP_CLOSE_OK, IOP_SRC_PRO), IOP_DESC(29, iop_g_protection, SIZEOF(char), IOP_OPEN_OK|IOP_CLOSE_OK, IOP_SRC_PRO), IOP_DESC(30, iop_o_protection, SIZEOF(char), IOP_OPEN_OK|IOP_CLOSE_OK, IOP_SRC_PRO), IOP_DESC(31, iop_readonly, 0, IOP_OPEN_OK, 0), IOP_DESC(32, iop_recordsize, SIZEOF(int4), IOP_OPEN_OK, IOP_SRC_INT), IOP_DESC(33, iop_shared, 0, IOP_OPEN_OK, 0), IOP_DESC(34, iop_spool, 0, IOP_CLOSE_OK, 0), IOP_DESC(35, iop_submit, 0, IOP_CLOSE_OK, 0), IOP_DESC(36, iop_rfa, 0, IOP_USE_OK, 0), IOP_DESC(37, iop_space, SIZEOF(int4), IOP_USE_OK|IOP_CLOSE_OK, IOP_SRC_INT), IOP_DESC(38, iop_queue, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(39, iop_rename, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(40, iop_uic, IOP_VAR_SIZE, IOP_OPEN_OK|IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(41, iop_wrap, 0, IOP_OPEN_OK|IOP_USE_OK, 0), IOP_DESC(42, iop_nowrap, 0, IOP_OPEN_OK|IOP_USE_OK, 0), IOP_DESC(43, iop_rewind, 0, IOP_OPEN_OK|IOP_USE_OK|IOP_CLOSE_OK, 0), IOP_DESC(44, iop_exception, IOP_VAR_SIZE, IOP_OPEN_OK|IOP_USE_OK|IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(45, iop_ebcdic, 0, IOP_OPEN_OK|IOP_USE_OK, 0), IOP_DESC(46, iop_label, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(47, iop_nolabel, 0, IOP_OPEN_OK | IOP_USE_OK, 0), IOP_DESC(48, iop_newtape, 0, 0, 0), IOP_DESC(49, iop_mount, 0, IOP_OPEN_OK, 0), IOP_DESC(50, iop_fixed, 0, IOP_OPEN_OK, 0), IOP_DESC(51, iop_erasetape, 0, IOP_OPEN_OK | IOP_USE_OK | IOP_CLOSE_OK, 0), IOP_DESC(52, iop_next, 0, IOP_USE_OK, 0), IOP_DESC(53, iop_writeof, 0, IOP_USE_OK|IOP_CLOSE_OK, 0), IOP_DESC(54, iop_noreadonly, 0, IOP_OPEN_OK, 0), IOP_DESC(55, iop_rdcheckdata, 0, IOP_OPEN_OK|IOP_USE_OK, 0), IOP_DESC(56, iop_nordcheckdata, 0, IOP_OPEN_OK|IOP_USE_OK, 0), IOP_DESC(57, iop_wtcheckdata, 0, IOP_OPEN_OK|IOP_USE_OK, 0), IOP_DESC(58, iop_nowtcheckdata, 0, IOP_OPEN_OK|IOP_USE_OK, 0), IOP_DESC(59, iop_inhretry, 0, IOP_OPEN_OK|IOP_USE_OK, 0), IOP_DESC(60, iop_inhextgap, 0, IOP_OPEN_OK|IOP_USE_OK, 0), IOP_DESC(61, iop_unload, 0, IOP_CLOSE_OK, 0), IOP_DESC(62, iop_skipfile, SIZEOF(int4), IOP_USE_OK, IOP_SRC_INT), IOP_DESC(63, iop_writeonly, 0, IOP_OPEN_OK, 0), IOP_DESC(64, iop_nowriteonly, 0, IOP_OPEN_OK, 0), IOP_DESC(65, iop_wait, 0, IOP_USE_OK, 0), IOP_DESC(66, iop_tmpmbx, 0, IOP_OPEN_OK, 0), IOP_DESC(67, iop_prmmbx, 0, IOP_OPEN_OK, 0), IOP_DESC(68, iop_append, 0, IOP_OPEN_OK, 0), IOP_DESC(69, iop_nowait, 0, IOP_USE_OK, 0), IOP_DESC(70, iop_nofixed, 0, IOP_OPEN_OK, 0), IOP_DESC(71, iop_stream, 0, IOP_OPEN_OK, 0), IOP_DESC(72, iop_nostream, 0, IOP_OPEN_OK, 0), IOP_DESC(73, iop_flush, 0, IOP_USE_OK, 0), IOP_DESC(74, iop_length, SIZEOF(int4), IOP_USE_OK, IOP_SRC_INT), IOP_DESC(75, iop_typeahead, 0, IOP_USE_OK, 0), IOP_DESC(76, iop_notypeahead, 0, IOP_USE_OK, 0), IOP_DESC(77, iop_editing, 0, IOP_USE_OK, 0), IOP_DESC(78, iop_noediting, 0, IOP_USE_OK, 0), IOP_DESC(79, iop_hostsync, 0, IOP_USE_OK, 0), IOP_DESC(80, iop_nohostsync, 0, IOP_USE_OK, 0), IOP_DESC(81, iop_insert, 0, IOP_USE_OK, 0), IOP_DESC(82, iop_noinsert, 0, IOP_USE_OK|IOP_CLOSE_OK, 0), IOP_DESC(83, iop_pasthru, 0, IOP_USE_OK, 0), IOP_DESC(84, iop_nopasthru, 0, IOP_USE_OK, 0), IOP_DESC(85, iop_readsync, 0, IOP_USE_OK, 0), IOP_DESC(86, iop_noreadsync, 0, IOP_USE_OK, 0), IOP_DESC(87, iop_ttsync, 0, IOP_USE_OK, 0), IOP_DESC(88, iop_nottsync, 0, IOP_USE_OK, 0), IOP_DESC(89, iop_after, SIZEOF(int4) * 2, IOP_CLOSE_OK, IOP_SRC_TIME), IOP_DESC(90, iop_burst, 0, IOP_CLOSE_OK, 0), IOP_DESC(91, iop_characteristic, SIZEOF(int4), IOP_CLOSE_OK, IOP_SRC_INT), IOP_DESC(92, iop_copies, SIZEOF(int4), IOP_CLOSE_OK, IOP_SRC_INT), IOP_DESC(93, iop_cli, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(94, iop_flag, 0, IOP_CLOSE_OK, 0), IOP_DESC(95, iop_form, SIZEOF(int4), IOP_CLOSE_OK, IOP_SRC_INT), IOP_DESC(96, iop_header, 0, IOP_CLOSE_OK, 0), IOP_DESC(97, iop_hold, 0, IOP_CLOSE_OK, 0), IOP_DESC(98, iop_lowercase, 0, IOP_CLOSE_OK, 0), IOP_DESC(99, iop_name, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(100, iop_cpulimit, SIZEOF(int4), IOP_CLOSE_OK, IOP_SRC_INT), IOP_DESC(101, iop_noburst, 0, IOP_CLOSE_OK, 0), IOP_DESC(102, iop_print, 0, IOP_CLOSE_OK, 0), IOP_DESC(103, iop_noprint, 0, IOP_CLOSE_OK, 0), IOP_DESC(104, iop_noflag, 0, IOP_CLOSE_OK, 0), IOP_DESC(105, iop_noheader, 0, IOP_CLOSE_OK, 0), IOP_DESC(106, iop_nohold, 0, IOP_CLOSE_OK, 0), IOP_DESC(107, iop_nolowercase, 0, IOP_CLOSE_OK, 0), IOP_DESC(108, iop_nonotify, 0, IOP_CLOSE_OK, 0), IOP_DESC(109, iop_nopassall, 0, IOP_CLOSE_OK, 0), IOP_DESC(110, iop_norestart, 0, IOP_CLOSE_OK, 0), IOP_DESC(111, iop_noused_1, 0, 0, 0), IOP_DESC(112, iop_note, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(113, iop_notify, 0, IOP_CLOSE_OK, 0), IOP_DESC(114, iop_notrailer, 0, IOP_CLOSE_OK, 0), IOP_DESC(115, iop_operator, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(116, iop_firstpage, SIZEOF(int4), IOP_CLOSE_OK, IOP_SRC_INT), IOP_DESC(117, iop_passall, 0, IOP_CLOSE_OK, 0), IOP_DESC(118, iop_priority, SIZEOF(int4), IOP_CLOSE_OK, IOP_SRC_INT), IOP_DESC(119, iop_remote, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(120, iop_restart, 0, IOP_CLOSE_OK, 0), IOP_DESC(121, iop_setup, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(122, iop_trailer, 0, IOP_CLOSE_OK, 0), IOP_DESC(123, iop_user, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(124, iop_logfile, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(125, iop_logqueue, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(126, iop_lastpage, SIZEOF(int4), IOP_CLOSE_OK, IOP_SRC_INT), IOP_DESC(127, iop_page, 0, IOP_CLOSE_OK, 0), IOP_DESC(128, iop_nopage, 0, IOP_CLOSE_OK, 0), IOP_DESC(129, iop_doublespace, 0, IOP_CLOSE_OK, 0), IOP_DESC(130, iop_nodoublespace, 0, IOP_CLOSE_OK, 0), IOP_DESC(131, iop_p1, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(132, iop_p2, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(133, iop_p3, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(134, iop_p4, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(135, iop_p5, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(136, iop_p6, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(137, iop_p7, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(138, iop_p8, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(139, iop_filter, IOP_VAR_SIZE, IOP_USE_OK, IOP_SRC_STR), IOP_DESC(140, iop_nofilter, 0, IOP_USE_OK, 0), IOP_DESC(141, iop_writetm, 0, IOP_USE_OK | IOP_CLOSE_OK, 0), IOP_DESC(142, iop_writelb, IOP_VAR_SIZE, IOP_USE_OK, IOP_SRC_STR), IOP_DESC(143, iop_truncate, 0, IOP_USE_OK | IOP_OPEN_OK, 0), IOP_DESC(144, iop_notruncate, 0, IOP_USE_OK | IOP_OPEN_OK, 0), IOP_DESC(145, iop_extgap, 0, IOP_USE_OK | IOP_OPEN_OK, 0), IOP_DESC(146, iop_noebcdic, 0, IOP_USE_OK | IOP_OPEN_OK, 0), IOP_DESC(147, iop_retry, 0, IOP_USE_OK | IOP_OPEN_OK, 0), IOP_DESC(148, iop_nl, 0, IOP_OPEN_OK, 0), IOP_DESC(149, iop_noterminator, 0, IOP_USE_OK, 0), IOP_DESC(150, iop_sequential, 0, IOP_OPEN_OK, 0), IOP_DESC(151, iop_fifo, 0, IOP_OPEN_OK, 0), IOP_DESC(152, iop_canonical, 0, IOP_OPEN_OK | IOP_USE_OK, 0), IOP_DESC(153, iop_nocanonical, 0, IOP_OPEN_OK | IOP_USE_OK, 0), IOP_DESC(154, iop_socket, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_CLOSE_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(155, iop_listen, 0, 0, 0), /* no longer used - see iop_zlisten */ IOP_DESC(156, iop_urgent, 0, IOP_USE_OK, 0), IOP_DESC(157, iop_nourgent, 0, IOP_USE_OK, 0), IOP_DESC(158, iop_delimiter, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(159, iop_connect, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(160, iop_ioerror, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(161, iop_attach, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(162, iop_detach, IOP_VAR_SIZE, IOP_USE_OK, IOP_SRC_STR), IOP_DESC(163, iop_zlisten, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(164, iop_ipchset, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(165, iop_opchset, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(166, iop_nodelimiter, 0, IOP_OPEN_OK | IOP_USE_OK, 0), IOP_DESC(167, iop_zdelay, 0, IOP_OPEN_OK | IOP_USE_OK, 0), IOP_DESC(168, iop_znodelay, 0, IOP_OPEN_OK | IOP_USE_OK, 0), IOP_DESC(169, iop_zbfsize, SIZEOF(int4), IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_INT), IOP_DESC(170, iop_zibfsize, SIZEOF(int4), IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_INT), IOP_DESC(171, iop_zff, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(172, iop_znoff, 0, IOP_OPEN_OK | IOP_USE_OK, 0), IOP_DESC(173, iop_zlength, SIZEOF(int4), IOP_USE_OK, IOP_SRC_INT), IOP_DESC(174, iop_zwidth, SIZEOF(int4), IOP_USE_OK, IOP_SRC_INT), IOP_DESC(175, iop_zwrap, 0, IOP_OPEN_OK|IOP_USE_OK, 0), IOP_DESC(176, iop_znowrap, 0, IOP_OPEN_OK|IOP_USE_OK, 0), IOP_DESC(177, iop_bigrecord, 0, IOP_OPEN_OK, 0), IOP_DESC(178, iop_nobigrecord, 0, IOP_OPEN_OK, 0), IOP_DESC(179, iop_rfm, IOP_VAR_SIZE, IOP_OPEN_OK, IOP_SRC_STR), IOP_DESC(180, iop_m, 0, IOP_OPEN_OK, 0), IOP_DESC(181, iop_utf8, 0, IOP_OPEN_OK, 0), IOP_DESC(182, iop_utf16, 0, IOP_OPEN_OK, 0), IOP_DESC(183, iop_utf16be, 0, IOP_OPEN_OK, 0), IOP_DESC(184, iop_utf16le, 0, IOP_OPEN_OK, 0), IOP_DESC(185, iop_pad, SIZEOF(int4), IOP_OPEN_OK, IOP_SRC_INT), IOP_DESC(186, iop_chset, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(187, iop_morereadtime, SIZEOF(int4), IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_INT), IOP_DESC(188, iop_shell, IOP_VAR_SIZE, IOP_OPEN_OK, IOP_SRC_STR), IOP_DESC(189, iop_command, IOP_VAR_SIZE, IOP_OPEN_OK, IOP_SRC_STR), IOP_DESC(190, iop_stderr, IOP_VAR_SIZE, IOP_OPEN_OK, IOP_SRC_STR), IOP_DESC(191, iop_independent, 0, IOP_OPEN_OK, 0), IOP_DESC(192, iop_parse, 0, IOP_OPEN_OK, 0), IOP_DESC(193, iop_destroy, 0, IOP_CLOSE_OK, 0), IOP_DESC(194, iop_nodestroy, 0, IOP_CLOSE_OK, 0), IOP_DESC(195, iop_rundown, 0, IOP_CLOSE_OK, 0), IOP_DESC(196, iop_follow, 0, IOP_OPEN_OK|IOP_USE_OK, 0), IOP_DESC(197, iop_nofollow, 0, IOP_OPEN_OK|IOP_USE_OK, 0), IOP_DESC(198, iop_empterm, 0, IOP_OPEN_OK|IOP_USE_OK, 0), IOP_DESC(199, iop_noempterm, 0, IOP_OPEN_OK|IOP_USE_OK, 0), IOP_DESC(200, iop_timeout, SIZEOF(int4), IOP_CLOSE_OK, IOP_SRC_INT), IOP_DESC(201, iop_seek, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(202, iop_key, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(203, iop_input_key, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(204, iop_output_key, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(205, iop_inseek, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(206, iop_outseek, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(207, iop_inrewind, 0, IOP_OPEN_OK | IOP_USE_OK, 0), IOP_DESC(208, iop_outrewind, 0, IOP_OPEN_OK | IOP_USE_OK, 0), IOP_DESC(209, iop_buffered, 0, IOP_OPEN_OK | IOP_USE_OK, 0), IOP_DESC(210, iop_fflf, 0, IOP_OPEN_OK | IOP_USE_OK, 0), IOP_DESC(211, iop_nofflf, 0, IOP_OPEN_OK | IOP_USE_OK, 0), IOP_DESC(212, iop_hupenable, 0, IOP_USE_OK, 0), IOP_DESC(213, iop_nohupenable, 0, IOP_USE_OK, 0), IOP_DESC(214, iop_replace, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), IOP_DESC(215, iop_options, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), IOP_DESC(216, n_iops, 0, 0, 0) fis-gtm-V7.0-005/sr_port/iop_parms_size.c0000755000032200000250000000127514342376331017242 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "io_params.h" /* io_params_size contains a table of argument sizes for each io_param */ /* enumerated in io_params. */ #define IOP_DESC(a,b,c,d,e) c LITDEF unsigned char io_params_size[] = { #include "iop.h" }; fis-gtm-V7.0-005/sr_port/iorm_wtff.c0000755000032200000250000000210314342376331016202 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 "io.h" #include "iormdef.h" #include "error.h" #define FORM_FEED "\014" GBLREF io_pair io_curr_device; void iorm_wtff(void) { mstr temp; io_desc *iod; boolean_t ch_set; iod = io_curr_device.out; ESTABLISH_GTMIO_CH(&io_curr_device, ch_set); iorm_cond_wteol(iod); temp.len = SIZEOF(FORM_FEED) - 1; temp.addr = FORM_FEED; iorm_write(&temp); if (iod->fflf) /* GTM-9136: If fflf is FALSE, we don't follow the FF with an NL */ iorm_wteol(1, iod); iod->dollar.x = 0; iod->dollar.y = 0; REVERT_GTMIO_CH(&io_curr_device, ch_set); } fis-gtm-V7.0-005/sr_port/iorm_wtone.c0000755000032200000250000000261314342376331016376 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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 "io.h" #ifdef UTF8_SUPPORTED #include "gtm_utf8.h" #endif GBLREF io_pair io_curr_device; GBLREF boolean_t gtm_utf8_mode; void iorm_wtone(int ch) { mstr temp; char c; #ifdef UTF8_SUPPORTED unsigned char uni_buf[GTM_MB_LEN_MAX], *endptr; #endif if (!gtm_utf8_mode || !IS_UTF_CHSET(io_curr_device.out->ochset)) { c = (char)ch; temp.len = 1; temp.addr = &c; } #ifdef UTF8_SUPPORTED else { switch (io_curr_device.out->ochset) { case CHSET_UTF8: case CHSET_UTF16: case CHSET_UTF16BE: case CHSET_UTF16LE: endptr = UTF8_WCTOMB(ch, uni_buf); break; default: assertpro(FALSE && io_curr_device.out->ochset); } temp.addr = (char *)uni_buf; temp.len = INTCAST(endptr - uni_buf); assert(0 < temp.len); /* we validated the code point already in op_wtone() */ } #endif UTF8_ONLY(temp.char_len = 1;) iorm_write(&temp); return; } fis-gtm-V7.0-005/sr_port/iormdefsp.h0000755000032200000250000000163614342376331016215 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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. * * * ****************************************************************/ #ifndef IORMDEFSP_H #define IORMDEFSP_H #define EBCDIC_RMEOL "\25" /* #pragma(suspend) not working in macros */ #define ASCII_RMEOL "\n" #if defined(KEEP_zOS_EBCDIC) || defined(VMS) #define RMEOL ((ascii != iod->out_code_set) ? EBCDIC_RMEOL : ASCII_RMEOL) #define RMEOL_LEN ((ascii != iod->out_code_set) ? SIZEOF(EBCDIC_RMEOL) - 1 : SIZEOF(ASCII_RMEOL) - 1) #else #define RMEOL ASCII_RMEOL #define RMEOL_LEN (SIZEOF(ASCII_RMEOL) - 1) #endif #endif /* IORMDEFSP_H */ fis-gtm-V7.0-005/sr_port/iosocket_bind.c0000644000032200000250000002615314342376331017032 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* iosocket_bind.c */ #include "mdef.h" #include #include "gtm_time.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_netdb.h" #include "gtm_ipv6.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gt_timer.h" #include "io.h" #include "iotimer.h" #include "iosocketdef.h" #include "gtm_stdlib.h" #include "gtm_unistd.h" #include "min_max.h" #include "gtm_stat.h" #include "eintr_wrappers.h" /* for STAT_FILE and CHG_OWNER */ #define BOUND "BOUND" #define IPV6_UNCERTAIN 2 error_def(ERR_GETNAMEINFO); error_def(ERR_GETSOCKNAMERR); error_def(ERR_GETSOCKOPTERR); error_def(ERR_SETSOCKOPTERR); error_def(ERR_SOCKBIND); error_def(ERR_SOCKINIT); error_def(ERR_TEXT); boolean_t iosocket_bind(socket_struct *socketptr, int4 msec_timeout, boolean_t update_bufsiz, boolean_t newversion) { int temp_1 = 1; char *errptr, *charptr; int4 errlen, real_errno; short len; in_port_t actual_port; boolean_t no_time_left = FALSE, ioerror; d_socket_struct *dsocketptr; struct addrinfo *ai_ptr; char port_buffer[NI_MAXSERV]; int errcode, keepalive_opt; ABS_TIME cur_time, end_time; GTM_SOCKLEN_TYPE addrlen; GTM_SOCKLEN_TYPE sockbuflen; struct stat statbuf; mode_t filemode; static readonly char action[] = "BIND"; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; dsocketptr = socketptr->dev; ai_ptr = (struct addrinfo*)(&socketptr->local.ai); assert(NULL != dsocketptr); dsocketptr->iod->dollar.key[0] = '\0'; dsocketptr->iod->dollar.device[0] = '\0'; if (dsocketptr->iod->dollar.devicebuffer) free(dsocketptr->iod->dollar.devicebuffer); dsocketptr->iod->dollar.devicebuffer = NULL; ioerror = socketptr->ioerror; if (FD_INVALID != socketptr->temp_sd) { socketptr->sd = socketptr->temp_sd; socketptr->temp_sd = FD_INVALID; } if (NO_M_TIMEOUT != msec_timeout) { sys_get_curr_time(&cur_time); add_int_to_abs_time(&cur_time, msec_timeout, &end_time); } do { temp_1 = 1; if (socket_local != socketptr->protocol) { if (-1 == setsockopt(socketptr->sd, SOL_SOCKET, SO_REUSEADDR, &temp_1, SIZEOF(temp_1))) { real_errno = errno; errptr = (char *)STRERROR(real_errno); errlen = STRLEN(errptr); close(socketptr->sd); socketptr->sd = FD_INVALID; SOCKET_FREE(socketptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, RTS_ERROR_LITERAL("SO_REUSEADDR"), real_errno, errlen, errptr); return FALSE; } # ifdef TCP_NODELAY temp_1 = socketptr->nodelay ? 1 : 0; if (-1 == setsockopt(socketptr->sd, IPPROTO_TCP, TCP_NODELAY, &temp_1, SIZEOF(temp_1))) { real_errno = errno; errptr = (char *)STRERROR(real_errno); errlen = STRLEN(errptr); close(socketptr->sd); socketptr->sd = FD_INVALID; SOCKET_FREE(socketptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, RTS_ERROR_LITERAL("TCP_NODELAY"), real_errno, errlen, errptr); return FALSE; } # endif if (update_bufsiz) { if (-1 == setsockopt(socketptr->sd, SOL_SOCKET, SO_RCVBUF, &socketptr->bufsiz, SIZEOF(socketptr->bufsiz))) { real_errno = errno; errptr = (char *)STRERROR(real_errno); errlen = STRLEN(errptr); close(socketptr->sd); socketptr->sd = FD_INVALID; SOCKET_FREE(socketptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, RTS_ERROR_LITERAL("SO_RCVBUF"), real_errno, errlen, errptr); return FALSE; } socketptr->options_state.rcvbuf |= SOCKOPTIONS_USER; } else { sockbuflen = SIZEOF(socketptr->bufsiz); if (-1 == getsockopt(socketptr->sd, SOL_SOCKET, SO_RCVBUF, &socketptr->bufsiz, &sockbuflen)) { real_errno = errno; errptr = (char *)STRERROR(real_errno); errlen = STRLEN(errptr); close(socketptr->sd); socketptr->sd = FD_INVALID; SOCKET_FREE(socketptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_GETSOCKOPTERR, 5, RTS_ERROR_LITERAL("SO_RCVBUF"), real_errno, errlen, errptr); return FALSE; } socketptr->options_state.rcvbuf |= SOCKOPTIONS_SYSTEM; } if (0 != (SOCKOPTIONS_PENDING & socketptr->options_state.sndbuf)) { if (-1 == iosocket_setsockopt(socketptr, "SO_SNDBUF", SO_SNDBUF, SOL_SOCKET, &socketptr->iobfsize, sizeof(socketptr->iobfsize), TRUE)) { return FALSE; } socketptr->options_state.sndbuf |= SOCKOPTIONS_USER; socketptr->options_state.sndbuf &= ~SOCKOPTIONS_PENDING; } } if (socket_local == socketptr->protocol) { charptr = ((struct sockaddr_un *)(socketptr->local.sa))->sun_path; STAT_FILE(charptr, &statbuf, temp_1); if (!temp_1) { if (!S_ISSOCK(statbuf.st_mode)) { temp_1 = -1; /* bypass unlink and issue error */ errno = ENOTSOCK; } } if (!temp_1) if (newversion) temp_1 = UNLINK(charptr); if (temp_1 && ENOENT != errno) { real_errno = errno; if (ioerror) { close(socketptr->sd); socketptr->sd = FD_INVALID; SOCKET_FREE(socketptr); } SET_DOLLARDEVICE_ONECOMMA_STRERROR(dsocketptr->iod, real_errno); if (ioerror) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SOCKBIND, 0, real_errno); return FALSE; } } temp_1 = bind(socketptr->sd, SOCKET_LOCAL_ADDR(socketptr), ai_ptr->ai_addrlen); if (temp_1 < 0) { real_errno = errno; no_time_left = TRUE; switch (real_errno) { case EINTR: break; case EADDRINUSE: if (NO_M_TIMEOUT != msec_timeout) { sys_get_curr_time(&cur_time); cur_time = sub_abs_time(&end_time, &cur_time); msec_timeout = (int4)(cur_time.at_sec * MILLISECS_IN_SEC + /* Round up in order to prevent premature timeouts */ DIVIDE_ROUND_UP(cur_time.at_usec, MICROSECS_IN_MSEC)); if (msec_timeout > 0) no_time_left = FALSE; } else no_time_left = FALSE; /* retry */ if (socket_local != socketptr->protocol) break; /* fall through for LOCAL sockets since it unlikely the file will go away */ default: if (ioerror) { close(socketptr->sd); socketptr->sd = FD_INVALID; SOCKET_FREE(socketptr); } SET_DOLLARDEVICE_ONECOMMA_STRERROR(dsocketptr->iod, real_errno); if (ioerror) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SOCKBIND, 0, real_errno); return FALSE; } close(socketptr->sd); socketptr->sd = FD_INVALID; if (no_time_left) { SOCKET_FREE(socketptr); return FALSE; } hiber_start(100); if (-1 == (socketptr->sd = socket(ai_ptr->ai_family,ai_ptr->ai_socktype, ai_ptr->ai_protocol))) { real_errno = errno; errptr = (char *)STRERROR(real_errno); SET_DOLLARDEVICE_ONECOMMA_ERRSTR(dsocketptr->iod, errptr, errlen); if (ioerror) { SOCKET_FREE(socketptr); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_SOCKINIT, 3, real_errno, errlen, errptr); } return FALSE; } } } while (temp_1 < 0); /* obtain actual port from the bound address if port 0 was specified */ addrlen = SOCKET_ADDRLEN(socketptr, ai_ptr, local); if (-1 == getsockname(socketptr->sd, SOCKET_LOCAL_ADDR(socketptr), &addrlen)) { real_errno = errno; errptr = (char *)STRERROR(real_errno); SET_DOLLARDEVICE_ONECOMMA_ERRSTR(dsocketptr->iod, errptr, errlen); if (ioerror) { close(socketptr->sd); socketptr->sd = FD_INVALID; SOCKET_FREE(socketptr); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(5) ERR_GETSOCKNAMERR, 3, real_errno, errlen, errptr); } return FALSE; } if (socket_local != socketptr->protocol) { assert(ai_ptr->ai_addrlen == addrlen); /* not right for local */ GETNAMEINFO(SOCKET_LOCAL_ADDR(socketptr), addrlen, NULL, 0, port_buffer, NI_MAXSERV, NI_NUMERICSERV, errcode); if (0 != errcode) { real_errno = errno; TEXT_ADDRINFO(errptr, errcode, real_errno); SET_DOLLARDEVICE_ONECOMMA_ERRSTR(dsocketptr->iod, errptr, errlen); if (ioerror) { close(socketptr->sd); socketptr->sd = FD_INVALID; SOCKET_FREE(socketptr); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_GETNAMEINFO, 0, ERR_TEXT, 2, errlen, errptr); } return FALSE; } actual_port = ATOI(port_buffer); if (0 == socketptr->local.port) socketptr->local.port = actual_port; assert(socketptr->local.port == actual_port); if ((SOCKOPTIONS_PENDING & socketptr->options_state.alive) || (SOCKOPTIONS_PENDING & socketptr->options_state.cnt) || (SOCKOPTIONS_PENDING & socketptr->options_state.intvl)) keepalive_opt = SOCKOPTIONS_FROM_STRUCT; else keepalive_opt = TREF(gtm_socket_keepalive_idle); /* deviceparameter takes precedence */ if (keepalive_opt && !iosocket_tcp_keepalive(socketptr, keepalive_opt, action, TRUE)) { return FALSE; /* iosocket_tcp_keepalive issues rts_error rather than return */ } } else { if (socketptr->filemode_mask) { charptr = ((struct sockaddr_un *)(socketptr->local.sa))->sun_path; STAT_FILE(charptr, &statbuf, temp_1); assertpro(!temp_1); /* we just created socket so it should be there */ filemode = (statbuf.st_mode & ~socketptr->filemode_mask) | socketptr->filemode; temp_1 = CHMOD(charptr, filemode); if (temp_1) { real_errno = errno; errptr = (char *)STRERROR(real_errno); SET_DOLLARDEVICE_ONECOMMA_ERRSTR(dsocketptr->iod, errptr, errlen); if (ioerror) { close(socketptr->sd); socketptr->sd = FD_INVALID; SOCKET_FREE(socketptr); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_SOCKINIT, 3, real_errno, errlen, errptr, ERR_TEXT, 2, RTS_ERROR_LITERAL("setting protection")); } return FALSE; } } if (((gid_t)-1 != socketptr->uic.grp) || ((uid_t)-1 != socketptr->uic.mem)) { CHG_OWNER(charptr, socketptr->uic.mem, socketptr->uic.grp, temp_1); if (temp_1) { real_errno = errno; errptr = (char *)STRERROR(real_errno); SET_DOLLARDEVICE_ONECOMMA_ERRSTR(dsocketptr->iod, errptr, errlen); if (ioerror) { close(socketptr->sd); socketptr->sd = FD_INVALID; SOCKET_FREE(socketptr); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(9) ERR_SOCKINIT, 3, real_errno, errlen, errptr, ERR_TEXT, 2, RTS_ERROR_LITERAL("setting ownership")); } return FALSE; } } } socketptr->state = socket_bound; len = SIZEOF(BOUND) - 1; memcpy(&dsocketptr->iod->dollar.key[0], BOUND, len); dsocketptr->iod->dollar.key[len++] = '|'; memcpy(&dsocketptr->iod->dollar.key[len], socketptr->handle, socketptr->handle_len); len += socketptr->handle_len; dsocketptr->iod->dollar.key[len++] = '|'; if (socket_local != socketptr->protocol) SNPRINTF(&dsocketptr->iod->dollar.key[len], DD_BUFLEN, "%d", socketptr->local.port); else /* path goes in $key */ strncpy(&dsocketptr->iod->dollar.key[len], ((struct sockaddr_un *)(socketptr->local.sa))->sun_path, DD_BUFLEN - len - 1); dsocketptr->iod->dollar.key[DD_BUFLEN - 1] = '\0'; return TRUE; } fis-gtm-V7.0-005/sr_port/iosocket_close.c0000644000032200000250000001757514342376331017233 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* iosocket_close.c - close a socket connection * Parameters- * iod -- I/O descriptor for the current device. * * pp -- mval that carries the device parameters * */ #include "mdef.h" #include "gtm_string.h" #include "gtm_iconv.h" #include "gtm_stat.h" #include "gtmio.h" #include "gtm_stdio.h" #include "gtm_unistd.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "copy.h" #include "io_params.h" #include "io.h" #include "gt_timer.h" #include "iosocketdef.h" #include "stringpool.h" #include "eintr_wrappers.h" #ifdef GTM_TLS #include "gtm_tls.h" #endif #include "error.h" #include "op.h" #include "indir_enum.h" GBLREF io_desc *active_device; GBLREF int process_exiting; GBLREF boolean_t gtm_pipe_child; LITREF unsigned char io_params_size[]; error_def(ERR_CLOSEFAIL); error_def(ERR_FILEOPENFAIL); error_def(ERR_SOCKNOTFND); error_def(ERR_SYSCALL); error_def(ERR_TLSIOERROR); void iosocket_close_range(d_socket_struct *dsocketptr, int start, int end, boolean_t socket_delete, boolean_t socket_specified); void iosocket_close(io_desc *iod, mval *pp) { boolean_t socket_specified = FALSE; unsigned char ch; int handle_len; d_socket_struct *dsocketptr; char sock_handle[MAX_HANDLE_LEN], *errp; int4 start, end, index; int p_offset = 0; boolean_t socket_destroy = FALSE; boolean_t socket_delete = FALSE; boolean_t ch_set; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(iod->type == gtmsocket); dsocketptr = (d_socket_struct *)iod->dev_sp; ESTABLISH_GTMIO_CH(&iod->pair, ch_set); while (iop_eol != (ch = *(pp->str.addr + p_offset++))) { switch (ch) { case iop_exception: DEF_EXCEPTION(pp, p_offset, iod); break; case iop_socket: handle_len = (int)(unsigned char)*(pp->str.addr + p_offset); assert(handle_len > 0); memcpy(sock_handle, (char *)(pp->str.addr + p_offset + 1), handle_len); socket_specified = TRUE; break; case iop_ipchset: # if defined(KEEP_zOS_EBCDIC) if ((iconv_t)0 != iod->input_conv_cd) { ICONV_CLOSE_CD(iod->input_conv_cd); } SET_CODE_SET(iod->in_code_set, (char *)(pp->str.addr + p_offset + 1)); if (DEFAULT_CODE_SET != iod->in_code_set) ICONV_OPEN_CD(iod->input_conv_cd, INSIDE_CH_SET, (char *)(pp->str.addr + p_offset + 1)); # endif break; case iop_opchset: # if defined(KEEP_zOS_EBCDIC) if ((iconv_t)0 != iod->output_conv_cd) { ICONV_CLOSE_CD(iod->output_conv_cd); } SET_CODE_SET(iod->out_code_set, (char *)(pp->str.addr + p_offset + 1)); if (DEFAULT_CODE_SET != iod->out_code_set) ICONV_OPEN_CD(iod->output_conv_cd, (char *)(pp->str.addr + p_offset + 1), INSIDE_CH_SET); # endif break; case iop_destroy: socket_destroy = TRUE; break; case iop_nodestroy: socket_destroy = FALSE; break; case iop_delete: socket_delete = TRUE; break; default: break; } p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); } if (socket_specified) { if (0 > (index = iosocket_handle(sock_handle, &handle_len, FALSE, dsocketptr))) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKNOTFND, 2, handle_len, sock_handle); return; } start = end = index; } else { start = dsocketptr->n_socket - 1; end = 0; } iosocket_close_range(dsocketptr, start, end, socket_delete, socket_specified); /* "forget" the UTF-16 CHSET variant if no sockets are connected */ if (0 >= dsocketptr->n_socket) { dsocketptr->ichset_utf16_variant = iod->ichset = 0; dsocketptr->ochset_utf16_variant = iod->ochset = 0; } if (!socket_specified) { iod->state = dev_closed; if (socket_destroy) { active_device = 0; iosocket_destroy(iod); } } REVERT_GTMIO_CH(&iod->pair, ch_set); } void iosocket_close_range(d_socket_struct *dsocketptr, int start, int end, boolean_t socket_delete, boolean_t socket_specified) { int4 ii,jj; int rc, save_fd, save_rc = 0, save_errno; int local_process_exiting; ssize_t status; intrpt_state_t prev_intrpt_state; socket_struct *socketptr; struct stat statbuf, fstatbuf; char *path; int res; int null_fd = 0; local_process_exiting = process_exiting; /* record in case changes while here */ for (ii = start; ii >= end; ii--) { /* defer interrupts each iteration */ DEFER_INTERRUPTS(INTRPT_IN_SOCKET_CLOSE, prev_intrpt_state); socketptr = dsocketptr->socket[ii]; if ((NULL != socketptr) && (FD_INVALID != socketptr->sd)) { if (!gtm_pipe_child) { /* Don't reap if in a child process creating a new job or pipe device */ if ((socket_local == socketptr->protocol) && socketptr->passive) { /* only delete if passive/listening */ assertpro(socketptr->local.sa); path = ((struct sockaddr_un *)(socketptr->local.sa))->sun_path; FSTAT_FILE(socketptr->sd, &fstatbuf, res); if (-1 == res) { ENABLE_INTERRUPTS(INTRPT_IN_SOCKET_CLOSE, prev_intrpt_state); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fstat during socket delete"), CALLFROM, errno); } STAT_FILE(path, &statbuf, res); if (-1 == res) { ENABLE_INTERRUPTS(INTRPT_IN_SOCKET_CLOSE, prev_intrpt_state); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("stat during socket delete"), CALLFROM, errno); } if (UNLINK(path) == -1) { ENABLE_INTERRUPTS(INTRPT_IN_SOCKET_CLOSE, prev_intrpt_state); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("unlink during socket delete"), CALLFROM, errno); } } /* below is similar to iosocket_flush but socketptr may not be current socket */ if (socketptr->obuffer_timer_set) { cancel_timer((TID)socketptr); socketptr->obuffer_timer_set = FALSE; } status = 1; /* OK value */ if ((0 < socketptr->obuffer_length) && (0 == socketptr->obuffer_errno)) { socketptr->obuffer_output_active = TRUE; status = iosocket_output_buffer(socketptr); socketptr->obuffer_output_active = FALSE; } if ((0 < socketptr->obuffer_size) && ((0 >= status) || (0 != socketptr->obuffer_errno))) iosocket_buffer_error(socketptr); /* pre-existing error or error flushing buffer */ # ifdef GTM_TLS if (socketptr->tlsenabled) { gtm_tls_session_close((gtm_tls_socket_t **)&socketptr->tlssocket); socketptr->tlsenabled = FALSE; } # endif } CLOSE(socketptr->sd, rc); if (-1 == rc) { save_rc = rc; save_fd = socketptr->sd; save_errno = errno; } else if (!process_exiting && (3 > socketptr->sd)) { OPENFILE(DEVNULL, O_RDWR, null_fd); if (-1 == null_fd) { save_errno = errno; } assert(socketptr->sd == null_fd); } socketptr->sd = FD_INVALID; assert(local_process_exiting == process_exiting); if (!process_exiting) SOCKET_FREE(socketptr); /* skip if on the way out */ } if (dsocketptr->current_socket >= ii) dsocketptr->current_socket--; for (jj = ii + 1; jj <= (dsocketptr->n_socket - 1); jj++) dsocketptr->socket[jj - 1] = dsocketptr->socket[jj]; dsocketptr->n_socket--; ENABLE_INTERRUPTS(INTRPT_IN_SOCKET_CLOSE, prev_intrpt_state); } if (0 != save_rc) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_CLOSEFAIL, 1, save_fd, save_errno); } else if (-1 == null_fd) { RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_FILEOPENFAIL, 2, LIT_AND_LEN(DEVNULL), save_errno, 0); } } void iosocket_close_one(d_socket_struct *dsocketptr, int index) { iosocket_close_range(dsocketptr, index, index, FALSE, TRUE); } fis-gtm-V7.0-005/sr_port/iosocket_connect.c0000644000032200000250000005560414342376333017554 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* iosocket_connect.c */ #include "mdef.h" #include #include "gtm_time.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "stringpool.h" #include "gt_timer.h" #include "io.h" #include "iotimer.h" #include "iosocketdef.h" #include #include "stack_frame.h" #include "mv_stent.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "gtm_netdb.h" #include "gtm_ipv6.h" #include "gtm_unistd.h" #include "gtm_select.h" #include "min_max.h" #define ESTABLISHED "ESTABLISHED" GBLREF d_socket_struct *newdsocket; /* in case jobinterrupt */ GBLREF int socketus_interruptus; GBLREF int4 gtm_max_sockets; GBLREF mv_stent *mv_chain; GBLREF stack_frame *frame_pointer; GBLREF unsigned char *stackbase, *stacktop, *msp, *stackwarn; GBLREF volatile int4 outofband; GBLREF volatile boolean_t dollar_zininterrupt; error_def(ERR_GETNAMEINFO); error_def(ERR_GETSOCKNAMERR); error_def(ERR_GETSOCKOPTERR); error_def(ERR_OPENCONN); error_def(ERR_SETSOCKOPTERR); error_def(ERR_SOCKINIT); error_def(ERR_TEXT); error_def(ERR_ZINTRECURSEIO); boolean_t iosocket_connect(socket_struct *sockptr, int4 msec_timeout, boolean_t update_bufsiz) { int temp_1, keepalive_opt; char *errptr; int4 errlen, last_errno, save_errno; int d_socket_struct_len, res, nfds, sockerror; fd_set writefds; boolean_t no_time_left = FALSE, first_time = TRUE, timer_started = FALSE, oneshot; boolean_t timer_done; /* for TIMEOUT_INIT */ boolean_t need_connect, need_socket, need_select; short len; io_desc *iod; d_socket_struct *dsocketptr, *real_dsocketptr; socket_interrupt *sockintr, *real_sockintr; ABS_TIME cur_time, end_time; struct timeval *sel_time; mv_stent *mv_zintdev; struct addrinfo *remote_ai_ptr, *raw_ai_ptr, *local_ai_ptr; intrpt_state_t prev_intrpt_state; int errcode, real_errno; char ipaddr[SA_MAXLEN + 1]; char port_buffer[NI_MAXSERV]; static readonly char action[] = "CONNECT"; GTM_SOCKLEN_TYPE sockbuflen, tmp_addrlen; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DBGSOCK((stdout, "socconn: ************* Entering socconn - msec_timeout: %d\n",msec_timeout)); /* check for validity */ dsocketptr = sockptr->dev; assert(NULL != dsocketptr); sockintr = &dsocketptr->sock_save_state; iod = dsocketptr->iod; real_dsocketptr = (d_socket_struct *)iod->dev_sp; /* not newdsocket which is not saved on error */ real_sockintr = &real_dsocketptr->sock_save_state; iod->dollar.key[0] = '\0'; need_socket = need_connect = TRUE; need_select = FALSE; /* Check for restart */ if (dsocketptr->mupintr) { /* We have a pending read restart of some sort - check we aren't recursing on this device */ assertpro(sockwhich_invalid != sockintr->who_saved); /* Interrupt should never have an invalid save state */ if (dollar_zininterrupt) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZINTRECURSEIO); assertpro(sockwhich_connect == sockintr->who_saved); /* ZINTRECURSEIO should have caught */ DBGSOCK((stdout, "socconn: *#*#*#*#*#*#*# Restarted interrupted connect\n")); mv_zintdev = io_find_mvstent(iod, FALSE); if (mv_zintdev) { if (sockintr->end_time_valid) /* Restore end_time for timeout */ end_time = sockintr->end_time; if ((socket_connect_inprogress == sockptr->state) && (FD_INVALID != sockptr->sd)) { need_select = TRUE; need_socket = need_connect = FALSE; /* sd still good */ } /* Done with this mv_stent. Pop it off if we can, else mark it inactive. */ if (mv_chain == mv_zintdev) POP_MV_STENT(); /* pop if top of stack */ else { /* else mark it unused, see iosocket_open for use */ mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; mv_zintdev->mv_st_cont.mvs_zintdev.io_ptr = NULL; } DBGSOCK((stdout, "socconn: mv_stent found - endtime: %d/%d\n", end_time.at_sec, end_time.at_usec)); } else DBGSOCK((stdout, "socconn: no mv_stent found !!\n")); real_dsocketptr->mupintr = dsocketptr->mupintr = FALSE; real_sockintr->who_saved = sockintr->who_saved = sockwhich_invalid; } else if (NO_M_TIMEOUT != msec_timeout) { sys_get_curr_time(&cur_time); add_int_to_abs_time(&cur_time, msec_timeout, &end_time); } real_sockintr->end_time_valid = sockintr->end_time_valid = FALSE; oneshot = (0 == msec_timeout); last_errno = 0; remote_ai_ptr = (struct addrinfo*)(&(sockptr->remote.ai)); do { /* If the connect was failed, we may have already changed the remote. * So, the remote ai_addr should be restored. */ assertpro((socket_tcpip != sockptr->protocol) || (NULL != sockptr->remote.ai_head)); if (sockptr->remote.ai_head) memcpy(remote_ai_ptr, sockptr->remote.ai_head, SIZEOF(struct addrinfo)); if (need_socket && (FD_INVALID != sockptr->sd)) { close(sockptr->sd); sockptr->sd = FD_INVALID; } assert(FD_INVALID == -1); if (need_socket) { real_errno = -2; for (raw_ai_ptr = remote_ai_ptr; NULL != raw_ai_ptr; raw_ai_ptr = raw_ai_ptr->ai_next) { if (-1 == (sockptr->sd = socket(raw_ai_ptr->ai_family, raw_ai_ptr->ai_socktype, raw_ai_ptr->ai_protocol))) { real_errno = errno; if (socket_local == sockptr->protocol) break; /* just one attempt */ } else { real_errno = 0; break; } } if (0 != real_errno) { if (NULL != sockptr->remote.ai_head) { DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); freeaddrinfo(sockptr->remote.ai_head); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); sockptr->remote.ai_head = NULL; } SOCKET_FREE(sockptr); assertpro(-2 != real_errno); errptr = (char *)STRERROR(real_errno); errlen = STRLEN(errptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_SOCKINIT, 3, errno, errlen, errptr); return FALSE; } assertpro(NULL != raw_ai_ptr); /* there is a local, IPv6, or IPv4 address */ if (remote_ai_ptr != raw_ai_ptr) memcpy(remote_ai_ptr, raw_ai_ptr, SIZEOF(struct addrinfo)); need_socket = FALSE; first_time = TRUE; /* for initial connect */ temp_1 = 1; if (socket_local != sockptr->protocol) { SOCKET_AI_TO_REMOTE_ADDR(sockptr, raw_ai_ptr); remote_ai_ptr->ai_addr = SOCKET_REMOTE_ADDR(sockptr); remote_ai_ptr->ai_addrlen = raw_ai_ptr->ai_addrlen; remote_ai_ptr->ai_next = NULL; if (-1 == setsockopt(sockptr->sd, SOL_SOCKET, SO_REUSEADDR, &temp_1, SIZEOF(temp_1))) { save_errno = errno; errptr = (char *)STRERROR(save_errno); errlen = STRLEN(errptr); close(sockptr->sd); /* Don't leave a dangling socket around */ sockptr->sd = FD_INVALID; if (NULL != sockptr->remote.ai_head) { DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); freeaddrinfo(sockptr->remote.ai_head); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); sockptr->remote.ai_head = NULL; } SOCKET_FREE(sockptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, RTS_ERROR_LITERAL("SO_REUSEADDR"), save_errno, errlen, errptr); return FALSE; } # ifdef TCP_NODELAY temp_1 = sockptr->nodelay ? 1 : 0; if (-1 == setsockopt(sockptr->sd, IPPROTO_TCP, TCP_NODELAY, &temp_1, SIZEOF(temp_1))) { save_errno = errno; errptr = (char *)STRERROR(save_errno); errlen = STRLEN(errptr); close(sockptr->sd); /* Don't leave a dangling socket around */ sockptr->sd = FD_INVALID; if (NULL != sockptr->remote.ai_head) { DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); freeaddrinfo(sockptr->remote.ai_head); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); sockptr->remote.ai_head = NULL; } SOCKET_FREE(sockptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, RTS_ERROR_LITERAL("TCP_NODELAY"), save_errno, errlen, errptr); return FALSE; } # endif if (update_bufsiz) { if (-1 == setsockopt(sockptr->sd, SOL_SOCKET, SO_RCVBUF, &sockptr->bufsiz, SIZEOF(sockptr->bufsiz))) { save_errno = errno; errptr = (char *)STRERROR(save_errno); errlen = STRLEN(errptr); close(sockptr->sd); /* Don't leave a dangling socket around */ sockptr->sd = FD_INVALID; if (NULL != sockptr->remote.ai_head) { DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); freeaddrinfo(sockptr->remote.ai_head); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); sockptr->remote.ai_head = NULL; } SOCKET_FREE(sockptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, RTS_ERROR_LITERAL("SO_RCVBUF"), save_errno, errlen, errptr); return FALSE; } sockptr->options_state.rcvbuf |= SOCKOPTIONS_USER; } else { sockbuflen = SIZEOF(sockptr->bufsiz); if (-1 == getsockopt(sockptr->sd, SOL_SOCKET, SO_RCVBUF, &sockptr->bufsiz, &sockbuflen)) { save_errno = errno; errptr = (char *)STRERROR(save_errno); errlen = STRLEN(errptr); close(sockptr->sd); /* Don't leave a dangling socket around */ sockptr->sd = FD_INVALID; if (NULL != sockptr->remote.ai_head) { DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); freeaddrinfo(sockptr->remote.ai_head); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); sockptr->remote.ai_head = NULL; } SOCKET_FREE(sockptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_GETSOCKOPTERR, 5, RTS_ERROR_LITERAL("SO_RCVBUF"), save_errno, errlen, errptr); return FALSE; } sockptr->options_state.rcvbuf |= SOCKOPTIONS_SYSTEM; } if (0 != (SOCKOPTIONS_PENDING & sockptr->options_state.sndbuf)) { if (-1 == iosocket_setsockopt(sockptr, "SO_SNDBUF", SO_SNDBUF, SOL_SOCKET, &sockptr->iobfsize, sizeof(sockptr->iobfsize), TRUE)) return FALSE; sockptr->options_state.sndbuf |= SOCKOPTIONS_USER; sockptr->options_state.sndbuf &= ~SOCKOPTIONS_PENDING; } } } save_errno = res = 0; if (need_connect) { /* Use plain connect to allow jobinterrupt */ assert(FD_INVALID != sockptr->sd); if (first_time && (NO_M_TIMEOUT != msec_timeout)) { /* start timer if not end_time, if timeout is 0 just a short wait */ DBGSOCK((stdout, "socconn: if first_time: msec_timeout = %d\n", msec_timeout)); sys_get_curr_time(&cur_time); cur_time = sub_abs_time(&end_time, &cur_time); /* calculate remaining time */ if (0 != msec_timeout) msec_timeout = (int4)((cur_time.at_sec * MILLISECS_IN_SEC) + /* Round up in order to prevent premature timeouts */ DIVIDE_ROUND_UP(cur_time.at_usec, MICROSECS_IN_MSEC)); if (0 >= msec_timeout) { no_time_left = TRUE; msec_timeout = 100 /* enough time to connect if no problems */; DBGSOCK((stdout, "socconn: first_time msec_timeout was 0, now = %d\n", msec_timeout)); } first_time = FALSE; timer_started = TRUE; TIMEOUT_INIT(timer_done, msec_timeout); } res = connect(sockptr->sd, SOCKET_REMOTE_ADDR(sockptr), remote_ai_ptr->ai_addrlen); /* BYPASSOK */ if (res < 0) { save_errno = errno; need_connect = TRUE; switch (save_errno) { case EISCONN: save_errno = 0; res = 0; /* since it is connected already, treat as if success */ need_connect = FALSE; break; case EINTR: DBGSOCK((stdout, "socconn: EINTR: outofband = %d, oneshot = %d\n", outofband, oneshot)); if (outofband && !oneshot) { /* handle outofband unless zero timeout */ save_errno = 0; need_socket = need_connect = FALSE; break; } /* else fall through */ case EINPROGRESS: case EALREADY: need_socket = need_connect = FALSE; if (!oneshot) { DBGSOCK((stdout, "socconn: EINPROGESS: errno = %d\n", save_errno)); need_select = TRUE; } /* fall through */ case ETIMEDOUT: /* the other side bound but not listening */ case ECONNREFUSED: case ENOENT: /* LOCAL socket not there */ if (!no_time_left && (!oneshot) && (NO_M_TIMEOUT != msec_timeout)) { sys_get_curr_time(&cur_time); cur_time = sub_abs_time(&end_time, &cur_time); DBGSOCK((stdout, "socconn: ENOENT: errno = %d, msec_timeout= %d\n", save_errno, msec_timeout)); msec_timeout = (int4)(cur_time.at_sec * MILLISECS_IN_SEC + /* Round up in order to prevent premature timeouts */ DIVIDE_ROUND_UP(cur_time.at_usec, MICROSECS_IN_MSEC)); if (0 >= msec_timeout) msec_timeout = 0; } if (oneshot || (0 == msec_timeout)) no_time_left = TRUE; else if (!no_time_left) { if (ETIMEDOUT == save_errno || ECONNREFUSED == save_errno || ENOENT == save_errno) need_connect = need_socket = TRUE; else if (socket_local == sockptr->protocol) { /* AF_UNIX sockets do not continue the connect in background */ need_connect = TRUE; need_socket = need_select = FALSE; } save_errno = 0; res = -1; /* do the outer loop again */ } if (no_time_left) save_errno = 0; break; default: break; } } /* if connect failed */ } if (timer_started) { timer_started = FALSE; TIMEOUT_DONE(timer_done); } if (need_select) { sockerror = 0; do { /* unless outofband loop on select if connection continuing */ if (NO_M_TIMEOUT == msec_timeout) sel_time = NULL; else { sys_get_curr_time(&cur_time); cur_time = sub_abs_time(&end_time, &cur_time); msec_timeout = (int4)(cur_time.at_sec * MILLISECS_IN_SEC + /* Round up in order to prevent premature timeouts */ DIVIDE_ROUND_UP(cur_time.at_usec, MICROSECS_IN_MSEC)); if (0 < msec_timeout) sel_time = (struct timeval *)&cur_time; else { /* timed out so done */ save_errno = res = 0; no_time_left = TRUE; break; } } assert(FD_INVALID != sockptr->sd); assertpro(FD_SETSIZE > sockptr->sd); FD_ZERO(&writefds); FD_SET(sockptr->sd, &writefds); res = select(sockptr->sd + 1, NULL, &writefds, NULL, sel_time); if (0 < res) { /* check for socket error */ sockbuflen = SIZEOF(sockerror); res = getsockopt(sockptr->sd, SOL_SOCKET, SO_ERROR, &sockerror, &sockbuflen); if (0 == res && 0 == sockerror) { /* got it */ save_errno = 0; break; } else if (0 == res && 0 != sockerror) { if (EINTR == sockerror) { /* loop on select */ save_errno = 0; continue; } else { /* return socket error */ if (ECONNREFUSED == sockerror || ETIMEDOUT == sockerror || ENOENT == sockerror) { /* try until timeout */ last_errno = sockerror; save_errno = 0; need_socket = need_connect = TRUE; need_select = FALSE; res = -1; } else save_errno = sockerror; break; } } else { save_errno = errno; /* error on getsockopt */ break; } } else if (0 == res) { /* select timed out */ save_errno = 0; no_time_left = TRUE; break; } else if (EINTR != errno) { save_errno = errno; break; } else if (outofband) { save_errno = 0; break; } } while (TRUE); /* do select */ } if (save_errno) { if (FD_INVALID != sockptr->sd) { close(sockptr->sd); /* Don't leave a dangling socket around */ sockptr->sd = FD_INVALID; } if (NULL != sockptr->remote.ai_head) { DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); freeaddrinfo(sockptr->remote.ai_head); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); sockptr->remote.ai_head = NULL; } errptr = (char *)STRERROR(save_errno); if (dev_open == iod->state) { iod->dollar.za = ZA_IO_ERR; SET_DOLLARDEVICE_ONECOMMA_ERRSTR(dsocketptr->iod, errptr, errlen); } if (sockptr->ioerror) { SOCKET_FREE(sockptr); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_OPENCONN, 0, ERR_TEXT, 2, errlen, errptr); } errno = save_errno; return FALSE; } if (no_time_left) { if (NULL != sockptr->remote.ai_head) { DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); freeaddrinfo(sockptr->remote.ai_head); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); sockptr->remote.ai_head = NULL; } if (!need_socket) { close(sockptr->sd); sockptr->sd = FD_INVALID; } SOCKET_FREE(sockptr); return FALSE; } if (res < 0 && outofband) /* if connected delay outofband */ { DBGSOCK((stdout, "socconn: outofband interrupt received (%d) -- " "queueing mv_stent for wait intr\n", outofband)); if (!OUTOFBAND_RESTARTABLE(outofband)) { /* the operation would not be resumed, no need to save socket device states */ if (!need_socket) { close(sockptr->sd); sockptr->sd = FD_INVALID; } if (NULL != sockptr->remote.ai_head) { DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); freeaddrinfo(sockptr->remote.ai_head); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); sockptr->remote.ai_head = NULL; } SOCKET_FREE(sockptr); async_action(FALSE); assertpro(FALSE); } if (need_connect) { /* no connect in progress */ close(sockptr->sd); /* Don't leave a dangling socket around */ sockptr->sd = FD_INVALID; sockptr->state = socket_created; } else sockptr->state = socket_connect_inprogress; real_sockintr->who_saved = sockintr->who_saved = sockwhich_connect; if (NO_M_TIMEOUT != msec_timeout) { real_sockintr->end_time = sockintr->end_time = end_time; real_sockintr->end_time_valid = sockintr->end_time_valid = TRUE; } else real_sockintr->end_time_valid = sockintr->end_time_valid = FALSE; real_sockintr->newdsocket = sockintr->newdsocket = newdsocket; real_dsocketptr->mupintr = dsocketptr->mupintr = TRUE; d_socket_struct_len = SIZEOF(d_socket_struct) + (SIZEOF(socket_struct) * (gtm_max_sockets - 1)); ENSURE_STP_FREE_SPACE(d_socket_struct_len); PUSH_MV_STENT(MVST_ZINTDEV); mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; mv_chain->mv_st_cont.mvs_zintdev.io_ptr = NULL; mv_chain->mv_st_cont.mvs_zintdev.socketptr = sockptr; /* for sd and to free structure */ mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = d_socket_struct_len; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)stringpool.free; memcpy (stringpool.free, (unsigned char *)newdsocket, d_socket_struct_len); stringpool.free += d_socket_struct_len; mv_chain->mv_st_cont.mvs_zintdev.io_ptr = iod; mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE; socketus_interruptus++; DBGSOCK((stdout, "socconn: mv_stent queued - endtime: %d/%d interrupts: %d\n", end_time.at_sec, end_time.at_usec, socketus_interruptus)); async_action(FALSE); assertpro(FALSE); /* Should *never* return from async_action */ return FALSE; /* For the compiler.. */ } hiber_start(100); } while (res < 0); sockptr->state = socket_connected; sockptr->first_read = sockptr->first_write = TRUE; /* update dollar_key */ len = SIZEOF(ESTABLISHED) - 1; memcpy(&iod->dollar.key[0], ESTABLISHED, len); iod->dollar.key[len++] = '|'; memcpy(&iod->dollar.key[len], sockptr->handle, sockptr->handle_len); len += sockptr->handle_len; iod->dollar.key[len++] = '|'; /* translate internal address to numeric ip address */ assert(FALSE == need_socket); if (NULL != sockptr->remote.ai_head) { DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); freeaddrinfo(sockptr->remote.ai_head); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); sockptr->remote.ai_head = NULL; } local_ai_ptr = &(sockptr->local.ai); if (socket_local != sockptr->protocol) { tmp_addrlen = SIZEOF(struct sockaddr_storage); if (-1 == getsockname(sockptr->sd, SOCKET_LOCAL_ADDR(sockptr), &tmp_addrlen)) { save_errno = errno; errptr = (char *)STRERROR(save_errno); errlen = STRLEN(errptr); if (FD_INVALID != sockptr->sd) { close(sockptr->sd); /* Don't leave a dangling socket around */ sockptr->sd = FD_INVALID; } SOCKET_FREE(sockptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_GETSOCKNAMERR, 3, save_errno, errlen, errptr); return FALSE; } local_ai_ptr->ai_addrlen = tmp_addrlen; local_ai_ptr->ai_family = SOCKET_LOCAL_ADDR(sockptr)->sa_family; GETNAMEINFO(SOCKET_LOCAL_ADDR(sockptr), local_ai_ptr->ai_addrlen, ipaddr, SA_MAXLEN, port_buffer, NI_MAXSERV, NI_NUMERICSERV | NI_NUMERICHOST, errcode); if (0 != errcode) { if (FD_INVALID != sockptr->sd) { close(sockptr->sd); /* Don't leave a dangling socket around */ sockptr->sd = FD_INVALID; } SOCKET_FREE(sockptr); RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode); return FALSE; } sockptr->local.port = ATOI(port_buffer); STRNDUP(ipaddr, SA_MAXLEN, sockptr->local.saddr_ip); GETNAMEINFO(SOCKET_REMOTE_ADDR(sockptr), remote_ai_ptr->ai_addrlen, ipaddr, SA_MAXLEN, NULL, 0 , NI_NUMERICHOST, errcode); if (0 != errcode) { if (FD_INVALID != sockptr->sd) { close(sockptr->sd); /* Don't leave a dangling socket around */ sockptr->sd = FD_INVALID; } SOCKET_FREE(sockptr); RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode); return FALSE; } STRNDUP(ipaddr, SA_MAXLEN, sockptr->remote.saddr_ip); strncpy(&iod->dollar.key[len], sockptr->remote.saddr_ip, DD_BUFLEN - 1 - len); if ((SOCKOPTIONS_PENDING & sockptr->options_state.alive) || (SOCKOPTIONS_PENDING & sockptr->options_state.cnt) || (SOCKOPTIONS_PENDING & sockptr->options_state.intvl)) keepalive_opt = SOCKOPTIONS_FROM_STRUCT; else keepalive_opt = TREF(gtm_socket_keepalive_idle); /* deviceparameter takes precedence */ if (keepalive_opt && !iosocket_tcp_keepalive(sockptr, keepalive_opt, action, TRUE)) return FALSE; /* iosocket_tcp_keepalive issues rts_error rather than return */ } else { /* getsockname does not return info for AF_UNIX connected socket so copy from remote side */ local_ai_ptr->ai_socktype = sockptr->remote.ai.ai_socktype; local_ai_ptr->ai_addrlen = sockptr->remote.ai.ai_addrlen; local_ai_ptr->ai_protocol = sockptr->remote.ai.ai_protocol; SOCKET_ADDR_COPY(sockptr->local, sockptr->remote.sa, sockptr->remote.ai.ai_addrlen); STRNCPY_STR(&iod->dollar.key[len], ((struct sockaddr_un *)(sockptr->remote.sa))->sun_path, DD_BUFLEN - len - 1); } iod->dollar.key[DD_BUFLEN - 1] = '\0'; /* In case we fill the buffer */ return TRUE; } fis-gtm-V7.0-005/sr_port/iosocket_create.c0000644000032200000250000003157114342376331017361 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* iosocket_create.c */ /* this module takes care of * 1. allocate the space * 2. for passive: local.sa & local.ai * for active : remote.sa & remote.ai * for $principal: via getsockname and getsockpeer * 3. socketptr->protocol * 4. socketptr->sd (initialized to -1) unless already open via inetd * 5. socketptr->passive * 6. socketptr->state (initialized to created) unless already open */ #include "mdef.h" #include #include "gtm_ctype.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_netdb.h" #include "gtm_socket.h" #include "gtm_un.h" #include "gtm_inet.h" #include "gtm_ipv6.h" #include "gtm_stdlib.h" #include "have_crit.h" #include "io.h" #include "gt_timer.h" #include "iosocketdef.h" #include "min_max.h" #include "gtm_caseconv.h" #include "util.h" #include "trans_log_name.h" error_def(ERR_ADDRTOOLONG); error_def(ERR_GETSOCKNAMERR); error_def(ERR_GETADDRINFO); error_def(ERR_GETNAMEINFO); error_def(ERR_INVPORTSPEC); error_def(ERR_INVADDRSPEC); error_def(ERR_PROTNOTSUP); error_def(ERR_TEXT); error_def(ERR_SOCKINIT); /* PORT_PROTO_FORMAT defines the format for : */ #define PORT_PROTO_FORMAT "%hu:%3[^:]" #define SEPARATOR ':' socket_struct *iosocket_create(char *sockaddr, uint4 bfsize, int file_des, boolean_t listen_specified) { socket_struct *socketptr; socket_struct *prev_socketptr; socket_struct *socklist_head; boolean_t passive = FALSE; unsigned short port; int ii, save_errno, tmplen, errlen, sockaddrlen; char temp_addr[SA_MAXLITLEN], protocolstr[6], *adptr; const char *errptr; struct addrinfo *ai_ptr; struct addrinfo hints, *addr_info_ptr = NULL; struct sockaddr_un *sa_un_ptr, sa_un_trans; mval localpath; mstr transpath; int trans_status; enum socket_protocol protocol; int af; int sd; int errcode; char host_buffer[NI_MAXHOST]; char port_buffer[NI_MAXSERV]; int port_buffer_len; int colon_cnt, protooffset; char *last_2colon = NULL; intrpt_state_t prev_intrpt_state; int addrlen; GTM_SOCKLEN_TYPE tmp_addrlen; if (0 > file_des) { /* no socket descriptor yet */ memset(&hints, 0, SIZEOF(hints)); protooffset = colon_cnt = 0; sockaddrlen = STRLEN(sockaddr); for (ii = sockaddrlen - 1; 0 <= ii; ii--) { if (SEPARATOR == sockaddr[ii]) { colon_cnt++; if (1 == colon_cnt) protooffset = ii + 1; else { last_2colon = &sockaddr[ii]; break; } } } if (0 == colon_cnt) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_INVPORTSPEC); return NULL; } tmplen = sockaddrlen - protooffset; if (SIZEOF(protocolstr) <= tmplen) { /* last piece just too big to be valid */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_PROTNOTSUP, 2, tmplen , &sockaddr[protooffset]); return NULL; } lower_to_upper((uchar_ptr_t)protocolstr, (uchar_ptr_t)&sockaddr[protooffset], tmplen); if (((SIZEOF("TCP") - 1) == tmplen) && (0 == MEMCMP_LIT(protocolstr, "TCP"))) protocol = socket_tcpip; else if (((SIZEOF("LOCAL") - 1) == tmplen) && (0 == MEMCMP_LIT(protocolstr, "LOCAL"))) protocol = socket_local; else { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_PROTNOTSUP, 2, tmplen , &sockaddr[protooffset]); return NULL; } if (socket_tcpip == protocol) { if (1 == colon_cnt) { /* for listening socket or broadcasting socket */ if (!listen_specified || (SSCANF(sockaddr, PORT_PROTO_FORMAT, &port, protocolstr) < 2)) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_INVPORTSPEC); return NULL; } passive = TRUE; /* We always first try using IPv6 address, if supported */ af = ((GTM_IPV6_SUPPORTED && !ipv4_only) ? AF_INET6 : AF_INET); if (-1 == (sd = socket(af, SOCK_STREAM, IPPROTO_TCP))) { /* Try creating IPv4 socket */ af = AF_INET; if (-1 == (sd = socket(af, SOCK_STREAM, IPPROTO_TCP))) { save_errno = errno; errptr = (char *)STRERROR(save_errno); errlen = STRLEN(errptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_SOCKINIT, 3, save_errno, errlen, errptr); return NULL; } } SERVER_HINTS(hints, af); port_buffer_len = 0; I2A(port_buffer, port_buffer_len, port); port_buffer[port_buffer_len]='\0'; DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); if (0 != (errcode = getaddrinfo(NULL, port_buffer, &hints, &addr_info_ptr))) { close(sd); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); RTS_ERROR_ADDRINFO(NULL, ERR_GETADDRINFO, errcode); return NULL; } ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); SOCKET_ALLOC(socketptr); socketptr->local.port = port; socketptr->temp_sd = sd; socketptr->sd = FD_INVALID; ai_ptr = &(socketptr->local.ai); memcpy(ai_ptr, addr_info_ptr, SIZEOF(struct addrinfo)); SOCKET_AI_TO_LOCAL_ADDR(socketptr, addr_info_ptr); ai_ptr->ai_addr = SOCKET_LOCAL_ADDR(socketptr); ai_ptr->ai_addrlen = addr_info_ptr->ai_addrlen; ai_ptr->ai_next = NULL; DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); freeaddrinfo(addr_info_ptr); ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); } else { /* connection socket */ assert(2 == colon_cnt); if (listen_specified || (SSCANF(last_2colon + 1, PORT_PROTO_FORMAT, &port, protocolstr) < 2)) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_INVADDRSPEC); return NULL; } /* for connection socket */ SNPRINTF(port_buffer, NI_MAXSERV, "%hu", port); assert(last_2colon > sockaddr); addrlen = last_2colon - sockaddr; if ('[' == sockaddr[0]) { if (NULL == memchr(sockaddr, ']', addrlen)) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_INVADDRSPEC); return NULL; } addrlen -= 2; memcpy(temp_addr, &sockaddr[1], addrlen); } else memcpy(temp_addr, sockaddr, addrlen); temp_addr[addrlen] = 0; CLIENT_HINTS(hints); DEFER_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); if (0 != (errcode = getaddrinfo(temp_addr, port_buffer, &hints, &addr_info_ptr))) { ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); RTS_ERROR_ADDRINFO(NULL, ERR_GETADDRINFO, errcode); return NULL; } ENABLE_INTERRUPTS(INTRPT_IN_FUNC_WITH_MALLOC, prev_intrpt_state); /* we will test all address families in iosocket_connect() */ SOCKET_ALLOC(socketptr); socketptr->remote.ai_head = addr_info_ptr; socketptr->remote.port = port; socketptr->sd = socketptr->temp_sd = FD_INVALID; /* don't mess with 0 */ } } else if (socket_local == protocol) { /* should we get_full_path first */ /* check protooffset < sizeof sun_path */ /* protooffset is after colon */ SOCKET_ALLOC(socketptr); socketptr->protocol = socket_local; MV_INIT_STRING(&localpath, protooffset - 1, sockaddr); trans_status = TRANS_LOG_NAME(&localpath.str, &transpath, sa_un_trans.sun_path, (int)SIZEOF(sa_un_trans.sun_path), dont_sendmsg_on_log2long); if (SS_LOG2LONG == trans_status) { /* if LOG2LONG, returned len not valid so report untranslated length */ SOCKET_FREE(socketptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_ADDRTOOLONG, 4, localpath.str.len, localpath.str.addr, localpath.str.len, SIZEOF(sa_un_trans.sun_path)); return NULL; } sa_un_ptr = malloc(SIZEOF(struct sockaddr_un)); sa_un_ptr->sun_family = AF_UNIX; memcpy(sa_un_ptr->sun_path, transpath.addr, transpath.len); sa_un_ptr->sun_path[transpath.len] = '\0'; if (listen_specified) { passive = TRUE; socketptr->local.sa = (struct sockaddr *)sa_un_ptr; socketptr->local.ai.ai_family = AF_UNIX; socketptr->local.ai.ai_socktype = SOCK_STREAM; socketptr->local.ai.ai_addrlen = (size_t)((struct sockaddr_un *)0)->sun_path + protooffset; if (-1 == (sd = socket(AF_UNIX, SOCK_STREAM, 0))) { save_errno = errno; SOCKET_FREE(socketptr); errptr = (char *)STRERROR(save_errno); errlen = STRLEN(errptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_SOCKINIT, 3, save_errno, errlen, errptr); return NULL; } socketptr->temp_sd = sd; socketptr->sd = FD_INVALID; } else { socketptr->remote.sa = (struct sockaddr *)sa_un_ptr; /* setup remote fields */ socketptr->remote.ai.ai_family = AF_UNIX; socketptr->remote.ai.ai_socktype = SOCK_STREAM; socketptr->remote.ai.ai_addrlen = (size_t)((struct sockaddr_un *)0)->sun_path + protooffset; socketptr->sd = socketptr->temp_sd = FD_INVALID; /* don't mess with 0 */ } } else assertpro(socket_tcpip == protocol || socket_local == protocol); /* protocol already checked */ socketptr->state = socket_created; socketptr->howcreated = passive ? creator_listen : creator_connect; SOCKET_BUFFER_INIT(socketptr, bfsize); socketptr->passive = passive; socketptr->moreread_timeout = DEFAULT_MOREREAD_TIMEOUT; return socketptr; } else { /* socket already setup by inetd or passed via JOB or LOCAL socket */ SOCKET_ALLOC(socketptr); socketptr->sd = file_des; socketptr->temp_sd = FD_INVALID; ai_ptr = &(socketptr->local.ai); tmp_addrlen = SIZEOF(struct sockaddr_storage); if (-1 == getsockname(socketptr->sd, SOCKET_LOCAL_ADDR(socketptr), &tmp_addrlen)) { save_errno = errno; if (IS_SOCKNAME_UNIXERROR(save_errno)) { SOCKET_LOCAL_ADDR(socketptr)->sa_family = AF_UNIX; ((struct sockaddr_un *)SOCKET_LOCAL_ADDR(socketptr))->sun_path[0] = '\0'; tmp_addrlen = SIZEOF(struct sockaddr_un); } else { errptr = (char *)STRERROR(save_errno); tmplen = STRLEN(errptr); SOCKET_FREE(socketptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_GETSOCKNAMERR, 3, save_errno, tmplen, errptr); return NULL; } } else if (((size_t) (((struct sockaddr *) 0)->sa_data) >= tmp_addrlen) || (0 == SOCKET_LOCAL_ADDR(socketptr)->sa_family)) { SOCKET_LOCAL_ADDR(socketptr)->sa_family = AF_UNIX; ((struct sockaddr_un *)SOCKET_LOCAL_ADDR(socketptr))->sun_path[0] = '\0'; tmp_addrlen = SIZEOF(struct sockaddr_un); } if (AF_UNIX == SOCKET_LOCAL_ADDR(socketptr)->sa_family) protocol = socket_local; else protocol = socket_tcpip; ai_ptr->ai_addrlen = tmp_addrlen; ai_ptr->ai_family = SOCKET_LOCAL_ADDR(socketptr)->sa_family; ai_ptr->ai_socktype = SOCK_STREAM; if (socket_tcpip == protocol) { /* extract port information */ GETNAMEINFO(SOCKET_LOCAL_ADDR(socketptr), tmp_addrlen, host_buffer, NI_MAXHOST, port_buffer, NI_MAXSERV, NI_NUMERICHOST|NI_NUMERICSERV, errcode); if (0 != errcode) { SOCKET_FREE(socketptr); RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode); return NULL; } STRNDUP(host_buffer, NI_MAXHOST, socketptr->local.saddr_ip); socketptr->local.port = ATOI(port_buffer); tmp_addrlen = SIZEOF(struct sockaddr_storage); if (-1 == getpeername(socketptr->sd, SOCKET_REMOTE_ADDR(socketptr), &tmp_addrlen)) { save_errno = errno; errptr = (char *)STRERROR(save_errno); tmplen = STRLEN(errptr); SOCKET_FREE(socketptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_GETSOCKNAMERR, 3, save_errno, tmplen, errptr); return NULL; } } else if (socket_local == protocol) { SOCKET_REMOTE_ADDR(socketptr)->sa_family = AF_UNIX; ((struct sockaddr_un *)SOCKET_REMOTE_ADDR(socketptr))->sun_path[0] = '\0'; tmp_addrlen = SIZEOF(struct sockaddr_un); } socketptr->remote.ai.ai_addrlen = tmp_addrlen; assert(0 != SOCKET_REMOTE_ADDR(socketptr)->sa_family); socketptr->remote.ai.ai_family = SOCKET_REMOTE_ADDR(socketptr)->sa_family; socketptr->remote.ai.ai_socktype = SOCK_STREAM; assert((socket_tcpip != protocol) || (0 != SOCKET_REMOTE_ADDR(socketptr)->sa_family)); if (socket_tcpip == protocol) { GETNAMEINFO(SOCKET_REMOTE_ADDR(socketptr), socketptr->remote.ai.ai_addrlen, host_buffer, NI_MAXHOST, port_buffer, NI_MAXSERV, NI_NUMERICHOST|NI_NUMERICSERV, errcode); if (0 != errcode) { SOCKET_FREE(socketptr); RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode); return NULL; } STRNDUP(host_buffer, NI_MAXHOST, socketptr->remote.saddr_ip); socketptr->remote.port = ATOI(port_buffer); } else assertpro(socket_tcpip == protocol || socket_local == protocol); /* protocol already checked */ socketptr->state = socket_connected; socketptr->protocol = protocol; SOCKET_BUFFER_INIT(socketptr, bfsize); socketptr->passive = passive; socketptr->howcreated = (2 >= file_des) ? creator_principal : creator_passed; socketptr->moreread_timeout = DEFAULT_MOREREAD_TIMEOUT; return socketptr; } } fis-gtm-V7.0-005/sr_port/iosocket_delimiter.c0000644000032200000250000002007514342376331020071 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* iosocket_delimiter.c */ #include "mdef.h" #include "gtm_socket.h" #include "gtm_string.h" #include "gtm_inet.h" #include "io.h" #include "iottdef.h" #include "gt_timer.h" #include "iosocketdef.h" #include "gtm_conv.h" #include "gtm_utf8.h" GBLREF boolean_t gtm_utf8_mode; GBLREF UConverter *chset_desc[]; error_def(ERR_DELIMSIZNA); boolean_t iosocket_delimiter(unsigned char *delimiter_buffer, int4 delimiter_len, socket_struct *socketptr, boolean_t rm) { int counter, ii, c_len; unsigned char *c, *top, delimiter[MAX_DELIM_LEN + 1]; /* free the previous delimiters if any */ if (0 < socketptr->n_delimiter && socketptr->odelimiter0.addr != socketptr->delimiter[0].addr) { free(socketptr->odelimiter0.addr); socketptr->odelimiter0.addr = NULL; } for (ii = 0; ii < socketptr->n_delimiter; ii++) { if (socketptr->delimiter[ii].addr) free(socketptr->delimiter[ii].addr); if (socketptr->idelimiter[ii].addr != socketptr->delimiter[ii].addr) { free(socketptr->idelimiter[ii].addr); socketptr->idelimiter[ii].addr = NULL; } socketptr->delimiter[ii].addr = NULL; } socketptr->n_delimiter = 0; socketptr->delim0containsLF = FALSE; if (rm) return TRUE; /* fill in the new delimiters */ counter = ii = 0; c = &delimiter_buffer[0]; top = c + delimiter_len; while ((c < top) && (ii < MAX_N_DELIMITER)) { switch(delimiter[counter++] = *c++) { case ':' : /* end of the previous delimiter and start the next one */ if (1 < counter) { if (gtm_utf8_mode) { /* Check if delimiter has any invalid UTF-8 characters */ c_len = utf8_len_strict(delimiter, counter - 1); } else c_len = counter - 1; socketptr->delimiter[ii].addr = (char *)malloc(counter - 1); memcpy(socketptr->delimiter[ii].addr, delimiter, counter - 1); socketptr->delimiter[ii].len = counter - 1; UTF8_ONLY(socketptr->delimiter[ii].char_len = c_len); socketptr->idelimiter[ii] = socketptr->delimiter[ii]; if (0 == ii) socketptr->odelimiter0 = socketptr->delimiter[0]; socketptr->n_delimiter++; ii++; } counter = 0; break; case '/' : /* escape */ delimiter[counter - 1] = *c++; /* Escaping a delim character doesn't appear to be documented anywhere. Nonetheless, the assumption is that the only use is to escape ':' which is a one byte character in UTF-8. So, this logic will work. However, if there is a change in what can be escaped (say, escape a character that is > 1 byte in length), this logic has to change. Vinaya, 2007/09/07 */ break; case NATIVE_LF : /* Only NATIVE_LF is accepted as line terminator although the standard defines other line terminators */ if (0 == ii) socketptr->delim0containsLF = TRUE; break; default: /* look at the next character */ break; } if ((c == top) && (0 < counter)) { if (gtm_utf8_mode) /* Check if delimiter has any invalid UTF-8 character */ c_len = utf8_len_strict(delimiter, counter); /* triggers badchar error for invalid sequence */ else c_len = counter; socketptr->delimiter[ii].addr = (char *)malloc(counter); memcpy(socketptr->delimiter[ii].addr, delimiter, counter); socketptr->delimiter[ii].len = counter; UTF8_ONLY(socketptr->delimiter[ii].char_len = c_len); socketptr->idelimiter[ii] = socketptr->delimiter[ii]; if (0 == ii) socketptr->odelimiter0 = socketptr->delimiter[0]; socketptr->n_delimiter++; ii++; } if (counter > MAX_DELIM_LEN) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_DELIMSIZNA); return FALSE; } } return TRUE; } void iosocket_idelim_conv(socket_struct *socketptr, gtm_chset_t to_chset) { static char *conv_buff = NULL; int conv_len, delim_index, new_delim_len; assert(0 != socketptr->n_delimiter); assert(gtm_utf8_mode); if (IS_UTF16_CHSET(to_chset)) { /* Convert idelimiter[] to UTF-16 */ assert(CHSET_UTF16BE == to_chset || CHSET_UTF16LE == to_chset); if (NULL == conv_buff) conv_buff = malloc(MAX_DELIM_LEN); for (delim_index = 0; delim_index < socketptr->n_delimiter; delim_index++) { conv_len = MAX_DELIM_LEN; new_delim_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[to_chset], &socketptr->delimiter[delim_index], conv_buff, &conv_len); assert(MAX_DELIM_LEN > new_delim_len); if (MAX_DELIM_LEN < new_delim_len) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_DELIMSIZNA); return; } socketptr->idelimiter[delim_index].len = new_delim_len; UTF8_ONLY(socketptr->idelimiter[delim_index].char_len = socketptr->delimiter[delim_index].char_len); socketptr->idelimiter[delim_index].addr = malloc(new_delim_len); memcpy(socketptr->idelimiter[delim_index].addr, conv_buff, new_delim_len); } } else { /* Free up the converted idelimiter[] strings */ for (delim_index = 0; delim_index < socketptr->n_delimiter; delim_index++) { assert(socketptr->idelimiter[delim_index].addr != socketptr->delimiter[delim_index].addr); if (socketptr->idelimiter[delim_index].addr != socketptr->delimiter[delim_index].addr) free(socketptr->idelimiter[delim_index].addr); socketptr->idelimiter[delim_index] = socketptr->delimiter[delim_index]; } } return; } void iosocket_odelim_conv(socket_struct *socketptr, gtm_chset_t to_chset) { static char *conv_buff = NULL; int conv_len, delim_index, new_len; assert(0 != socketptr->n_delimiter); assert(gtm_utf8_mode); if (IS_UTF16_CHSET(to_chset)) { /* Convert odelimiter0 to UTF-16 */ assert(CHSET_UTF16BE == to_chset || CHSET_UTF16LE == to_chset); if (NULL == conv_buff) conv_buff = malloc(MAX_DELIM_LEN); conv_len = MAX_DELIM_LEN; new_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[to_chset], &socketptr->delimiter[0], conv_buff, &conv_len); assert((0 < new_len) && (MAX_DELIM_LEN > new_len)); if (MAX_DELIM_LEN < new_len) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_DELIMSIZNA); return; } socketptr->odelimiter0.len = new_len; UTF8_ONLY(socketptr->odelimiter0.char_len = socketptr->delimiter[0].char_len); socketptr->odelimiter0.addr = malloc(new_len); memcpy(socketptr->odelimiter0.addr, conv_buff, new_len); } else { /* Free up the converted odelimiter0 string */ assert(socketptr->odelimiter0.addr != socketptr->delimiter[0].addr); if (socketptr->odelimiter0.addr != socketptr->delimiter[0].addr) free(socketptr->odelimiter0.addr); socketptr->odelimiter0 = socketptr->delimiter[0]; } } void iosocket_delimiter_copy(socket_struct *from, socket_struct *to) { int delim_index; if (0 == (to->n_delimiter = from->n_delimiter)) return; for (delim_index = 0; delim_index < from->n_delimiter; delim_index++) { to->delimiter[delim_index] = from->delimiter[delim_index]; /* copy all fields */ to->delimiter[delim_index].addr = malloc(from->delimiter[delim_index].len); /* re-allocate buffer */ memcpy(to->delimiter[delim_index].addr, from->delimiter[delim_index].addr, from->delimiter[delim_index].len); to->idelimiter[delim_index] = to->delimiter[delim_index]; /* copy all fields */ if (from->delimiter[delim_index].addr != from->idelimiter[delim_index].addr) { /* has been converted */ to->idelimiter[delim_index].addr = malloc(from->idelimiter[delim_index].len); /* re-allocate buffer */ memcpy(to->idelimiter[delim_index].addr, from->idelimiter[delim_index].addr, from->idelimiter[delim_index].len); } } to->odelimiter0 = to->delimiter[0]; if (from->odelimiter0.addr != from->delimiter[0].addr) { /* has been converted */ to->odelimiter0.addr = malloc(from->odelimiter0.len); memcpy(to->odelimiter0.addr, from->odelimiter0.addr, from->odelimiter0.len); } return; } fis-gtm-V7.0-005/sr_port/iosocket_destroy.c0000644000032200000250000000433614342376331017606 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2022 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_stdio.h" #include "gtm_unistd.h" #include "gtm_socket.h" #include "io.h" #include "iosocketdef.h" #include "gtmio.h" GBLREF io_log_name *io_root_log_name; void iosocket_destroy (io_desc *ciod) { io_log_name **lpp, *lp; /* logical name pointers */ d_socket_struct *dsocketptr; assert(ciod); assert(ciod->type == gtmsocket); assert(ciod->newly_created || (ciod->state != dev_open)); if ((NULL == ciod) || (gtmsocket != ciod->type)) return; /* nothing to do here */ dsocketptr = (d_socket_struct *) ciod->dev_sp; /* This routine is called from iosocket_close.c with no SOCKET= * specified and DESTROY after closing all sockets in the device. * It is also called from io_rundown.c, trans_code.c, and * mdb_condition_handler.c if the device is newly_created or not open * so there should be no sockets in the device. */ if (dsocketptr && (0 < dsocketptr->n_socket)) return; /* be safe - should not happen */ for (lpp = &io_root_log_name, lp = *lpp; lp; lp = *lpp) { if ((NULL == lp->iod) || (n_io_dev_types == lp->iod->type)) { assert(NULL == lp->iod); /* Can be NULL if we are forced to exit during device setup */ /* skip it on pro */ } else if (lp->iod->pair.in == ciod) { /* The only device that may be "split" is the principal device. Since it is permanently open, * it will never get here. */ assert(lp->iod == ciod); assert(lp->iod->pair.out == ciod); *lpp = (*lpp)->next; free(lp); continue; /* lpp already points at next, so skip the dereference below. */ } lpp = &lp->next; } if (ciod->dollar.devicebuffer) { free(ciod->dollar.devicebuffer); ciod->dollar.devicebuffer = NULL; } if (dsocketptr) free(dsocketptr); free(ciod); } fis-gtm-V7.0-005/sr_port/iosocket_flush.c0000644000032200000250000000453114342376331017233 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* iosocket_flush.c */ #include "mdef.h" #include "gtm_socket.h" #include "gtm_inet.h" #include #include "gtm_stdio.h" #include "gtm_string.h" #include "io.h" #include "gt_timer.h" #include "iosocketdef.h" #include "error.h" #include "min_max.h" error_def(ERR_CURRSOCKOFR); error_def(ERR_NOSOCKETINDEV); error_def(ERR_SOCKPASSDATAMIX); error_def(ERR_SOCKWRITE); error_def(ERR_TEXT); GBLREF io_pair io_std_device; void iosocket_flush(io_desc *iod) { d_socket_struct *dsocketptr; socket_struct *socketptr; ssize_t status; int on = 1, off = 0; char *errptr; int4 errlen; boolean_t ch_set; assert(gtmsocket == iod->type); dsocketptr = (d_socket_struct *)iod->dev_sp; socketptr = dsocketptr->socket[dsocketptr->current_socket]; ESTABLISH_GTMIO_CH(&iod->pair, ch_set); if (0 >= dsocketptr->n_socket) { if (iod == io_std_device.out) ionl_flush(iod); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_NOSOCKETINDEV); REVERT_GTMIO_CH(&iod->pair, ch_set); return; } if (dsocketptr->current_socket >= dsocketptr->n_socket) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket); return; } ENSURE_DATA_SOCKET(socketptr); if (socketptr->obuffer_timer_set) { cancel_timer((TID)socketptr); socketptr->obuffer_timer_set = FALSE; } if (!socketptr->obuffer_output_active) { /* just to be safe */ status = 1; /* OK value */ if ((0 < socketptr->obuffer_length) && (0 == socketptr->obuffer_errno)) { socketptr->obuffer_output_active = TRUE; status = iosocket_output_buffer(socketptr); socketptr->obuffer_output_active = FALSE; } if ((0 < socketptr->obuffer_size) && ((0 >= status) || (0 != socketptr->obuffer_errno))) iosocket_buffer_error(socketptr); /* pre-existing error or error flushing buffer */ } REVERT_GTMIO_CH(&iod->pair, ch_set); return; } fis-gtm-V7.0-005/sr_port/iosocket_handle.c0000755000032200000250000000366214342376331017354 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ /* iosocket_handle.c */ /* newhandle == TRUE * * create a new handle * return the old dsocketptr->n_socket * (i.e. number of socket) * (i.e. index of the new socket) * * newhandle == FALSE * * check if the handle exist * yes ==> return the index * no ==> return -1 * (return the number of sockets would provide more information, but can cause * confliction with index = 0 * 0 ==> socket exist and index is 0 * 0 ==> there are 0 sockets exist) */ #include "mdef.h" #include "gtm_time.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "io_params.h" #include "io.h" #include "gt_timer.h" #include "iosocketdef.h" int4 iosocket_handle(char *handle, int *len, boolean_t newhandle, d_socket_struct *dsocketptr) { boolean_t unique; int4 ii, loop_flag = 1; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; while(loop_flag) { if (newhandle) { SNPRINTF(handle, MAX_HANDLE_LEN, "h%ld%03d", time((time_t *)0), ((TREF(socket_handle_counter))++ % 1000)); *len = (short)strlen(handle); } ii = 0; unique = TRUE; while(ii < dsocketptr->n_socket) { if ((*len == dsocketptr->socket[ii]->handle_len) && (0 == memcmp(handle, dsocketptr->socket[ii]->handle, *len))) { unique = FALSE; break; } ii++; } if (!newhandle) return (unique ? -1 : ii); if (unique) return ii; } /* it will never reach here */ return -1; } fis-gtm-V7.0-005/sr_port/iosocket_iocontrol.c0000644000032200000250000004160614342376331020126 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* iosocket_iocontrol.c */ #include "mdef.h" #include "mvalconv.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "io.h" #include "iotimer.h" #include "gt_timer.h" #include "iosocketdef.h" #include "gtm_caseconv.h" #include "stringpool.h" #include "min_max.h" #include "error.h" #include "gdsroot.h" #include "gdskill.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "gdscc.h" #include "filestruct.h" #include "buddy_list.h" /* needed for tp.h */ #include "jnl.h" #include "tp.h" #include "send_msg.h" #include "gtmmsg.h" /* for gtm_putmsg() prototype */ #include "change_reg.h" #include "setterm.h" #include "getzposition.h" #ifdef DEBUG #include "have_crit.h" /* for the TPNOTACID_CHECK macro */ #include "wbox_test_init.h" #endif GBLREF spdesc stringpool; GBLREF io_pair io_curr_device; GBLREF int gtm_non_blocked_write_retries; /* number of retries for non_blocked write to socket */ LITREF mval skiparg; LITREF mval literal_notimeout; error_def(ERR_EXPR); error_def(ERR_INVCTLMNE); error_def(ERR_CURRSOCKOFR); error_def(ERR_NOSOCKETINDEV); error_def(ERR_SOCKBLOCKERR); error_def(ERR_ZINTRECURSEIO); error_def(ERR_GETSOCKOPTERR); error_def(ERR_SETSOCKOPTERR); /* for iosocket_dlr_zkey */ #define FORMATTIMESTR "FORMAT" #define LISTENING "LISTENING|" #define READ "READ|" #define WRITE "WRITE|" #define MAXEVENTLITLEN (SIZEOF(LISTENING)-1) #define MAXZKEYITEMLEN (MAX_HANDLE_LEN + SA_MAXLITLEN + MAXEVENTLITLEN + 2) /* 1 pipe and a semicolon */ void iosocket_block_iocontrol(io_desc *iod, mval *option, mval *returnarg); void iosocket_iocontrol(mstr *mn, int4 argcnt, va_list args) { char action[MAX_DEVCTL_LENGTH]; unsigned short depth; int length, n; int4 msec_timeout; pid_t pid; mval *arg, *handlesvar = NULL, *option, *returnarg = NULL; mval *whatop = NULL, *handle = NULL; #ifdef GTM_TLS mval *tlsid, *password, *extraarg; #endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (0 == mn->len) return; assert(MAX_DEVCTL_LENGTH > mn->len); lower_to_upper((uchar_ptr_t)action, (uchar_ptr_t)mn->addr, mn->len); length = mn->len; if (0 == memcmp(action, "LISTEN", length)) { if (1 > argcnt) depth = DEFAULT_LISTEN_DEPTH; else { arg = va_arg(args, mval *); if ((NULL == arg) || M_ARG_SKIPPED(arg) || !MV_DEFINED(arg)) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_EXPR); return; } depth = MV_FORCE_INTD(arg); /* Negative values are downcast to unsigned short */ } iosocket_listen(io_curr_device.in, depth); } else if (0 == memcmp(action, "WAIT", length)) { arg = (0 < argcnt) ? va_arg(args, mval *) : (mval *)&literal_notimeout; if ((NULL != arg) && !M_ARG_SKIPPED(arg) && MV_DEFINED(arg)) MV_FORCE_MSTIMEOUT(arg, msec_timeout, FORMATTIMESTR); else { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_EXPR); return; } if (2 <= argcnt) { /* what operation to check */ arg = va_arg(args, mval *); if ((NULL != arg) && !M_ARG_SKIPPED(arg) && MV_DEFINED(arg)) whatop = arg; } if (3 <= argcnt) { /* which handle aka socket to check */ arg = va_arg(args, mval *); if ((NULL != arg) && !M_ARG_SKIPPED(arg) && MV_DEFINED(arg)) handle = arg; } iosocket_wait(io_curr_device.in, msec_timeout, whatop, handle); } else if (0 == memcmp(action, "PASS", length)) { n = argcnt; if (1 <= argcnt) { arg = va_arg(args, mval *); n--; if ((NULL != arg) && !M_ARG_SKIPPED(arg) && MV_DEFINED(arg)) pid = MV_FORCE_INTD(arg); /* Negative values are excluded */ else pid = -1; } else pid = -1; if (2 <= argcnt) { arg = va_arg(args, mval *); n--; if ((NULL == arg) || M_ARG_SKIPPED(arg) || !MV_DEFINED(arg)) arg = (mval *)&literal_notimeout; } else arg = (mval *)&literal_notimeout; MV_FORCE_MSTIMEOUT(arg, msec_timeout, FORMATTIMESTR); iosocket_pass_local(io_curr_device.out, pid, msec_timeout, n, args); } else if (0 == memcmp(action, "ACCEPT", length)) { n = argcnt; if (1 <= argcnt) { arg = va_arg(args, mval *); n--; if ((NULL != arg) && !M_ARG_SKIPPED(arg)) handlesvar = arg; } if (2 <= argcnt) { arg = va_arg(args, mval *); n--; if ((NULL != arg) && !M_ARG_SKIPPED(arg) && MV_DEFINED(arg)) pid = MV_FORCE_INTD(arg); /* Negative values are excluded */ else pid = -1; } else pid = -1; if (3 <= argcnt) { arg = va_arg(args, mval *); n--; if ((NULL == arg) || M_ARG_SKIPPED(arg) || !MV_DEFINED(arg)) arg = (mval *)&literal_notimeout; } else arg = (mval *)&literal_notimeout; MV_FORCE_MSTIMEOUT(arg, msec_timeout, FORMATTIMESTR); iosocket_accept_local(io_curr_device.in, handlesvar, pid, msec_timeout, n, args); #ifdef GTM_TLS } else if (0 == memcmp(action, "TLS", length)) { /* WRITE /TLS(option[,[msec_timeout][,tlsid[,password]]]) */ if (1 <= argcnt) { option = va_arg(args, mval *); if ((NULL != option) && !M_ARG_SKIPPED(option) && MV_DEFINED(option)) MV_FORCE_STRD(option); else { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_EXPR); return; } } else { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_EXPR); return; } if (2 <= argcnt) { arg = va_arg(args, mval *); if ((NULL == arg) || M_ARG_SKIPPED(arg) || !MV_DEFINED(arg)) arg = (mval *)&literal_notimeout; } else arg = (mval *)&literal_notimeout; MV_FORCE_MSTIMEOUT(arg, msec_timeout, FORMATTIMESTR); if (3 <= argcnt) { tlsid = va_arg(args, mval *); if ((NULL != tlsid) && !M_ARG_SKIPPED(tlsid) && MV_DEFINED(tlsid)) MV_FORCE_STRD(tlsid); else tlsid = NULL; } else tlsid = NULL; if (4 <= argcnt) { /* password only valid if tlsid provided */ password = va_arg(args, mval *); /* need to do va_arg in case 5th arg */ if ((NULL != tlsid) && (NULL != password) && !M_ARG_SKIPPED(password) && MV_DEFINED(password)) MV_FORCE_STRD(password); else password = NULL; } else password = NULL; if (5 <= argcnt) { extraarg = va_arg(args, mval *); if ((NULL != extraarg) && !M_ARG_SKIPPED(extraarg) && MV_DEFINED(extraarg)) MV_FORCE_STRD(extraarg); else extraarg = NULL; } else extraarg = NULL; iosocket_tls(option, msec_timeout, tlsid, password, extraarg); # endif } else if (0 == memcmp(action, "BLOCK", length)) { /* OFF, CLEAR, COUNT .lvn, SENT .lvn */ if (1 <= argcnt) { option = va_arg(args, mval *); if ((NULL != option) && !M_ARG_SKIPPED(option) && MV_DEFINED(option)) MV_FORCE_STRD(option); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_EXPR); } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_SOCKBLOCKERR, 2, LEN_AND_LIT("at least one option must be provided")); if (2 <= argcnt) { arg = va_arg(args, mval *); if ((NULL != arg) && !M_ARG_SKIPPED(arg)) returnarg = arg; } iosocket_block_iocontrol(io_curr_device.out, option, returnarg); } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_INVCTLMNE); return; } void iosocket_dlr_device(mstr *d) { io_desc *iod; boolean_t ch_set; iod = io_curr_device.in; ESTABLISH_GTMIO_CH(&iod->pair, ch_set); PUT_DOLLAR_DEVICE_INTO_MSTR(iod, d); REVERT_GTMIO_CH(&iod->pair, ch_set); return; } void iosocket_dlr_key(mstr *d) { io_desc *iod; int len; boolean_t ch_set; iod = io_curr_device.in; ESTABLISH_GTMIO_CH(&iod->pair, ch_set); len = STRLEN(iod->dollar.key); /* verify internal buffer has enough space for $DEVICE string value */ assert((int)d->len > len); if (len > 0) memcpy(d->addr, iod->dollar.key, len); d->len = len; REVERT_GTMIO_CH(&iod->pair, ch_set); return; } void iosocket_dlr_zkey(mstr *d) { int4 ii; int len, thislen, totlen, totplusthislen; char *zkeyptr, *charptr; mval *dzkey = NULL; io_desc *iod; d_socket_struct *dsocketptr; socket_struct *socketptr; boolean_t ch_set; iod = io_curr_device.in; ESTABLISH_GTMIO_CH(&iod->pair, ch_set); assertpro(gtmsocket == iod->type); dsocketptr = (d_socket_struct *)iod->dev_sp; zkeyptr = (char *)stringpool.free; totlen = thislen = totplusthislen = len = 0; for (ii = 0; ii < dsocketptr->n_socket; ii++) { socketptr = dsocketptr->socket[ii]; if ((socket_listening != socketptr->state) && (socket_connected != socketptr->state)) continue; if ((socket_connected == socketptr->state) && (0 < socketptr->buffered_length)) { /* data to be read in buffer */ if (!(SOCKPEND_READ & socketptr->pendingevent)) { /* may have been cleared by partial READ */ socketptr->pendingevent |= SOCKPEND_READ; socketptr->readyforwhat |= SOCKREADY_READ; socketptr->readycycle = dsocketptr->waitcycle; } } if (socketptr->pendingevent) { thislen = len = 0; totplusthislen = totlen + MAXZKEYITEMLEN; if ((socket_connected == socketptr->state) && (socketptr->readyforwhat == (SOCKREADY_WRITE | SOCKREADY_READ))) totplusthislen += MAXZKEYITEMLEN; /* both READ and WRITE ready */ if (!IS_STP_SPACE_AVAILABLE(totplusthislen)) { if (totlen) { assert(NULL == dzkey); /* Temp MVAL to protect $zkey contents at the end of string pool from garbage collection */ PUSH_MV_STENT(MVST_MVAL); dzkey = &mv_chain->mv_st_cont.mvs_mval; dzkey->mvtype = MV_STR; dzkey->str.len = totlen; dzkey->str.addr = (char *)stringpool.free; /* Advance stringpool free so that the contents are inside of it */ stringpool.free += totlen; } INVOKE_STP_GCOL(totplusthislen); if (totlen) { assert(IS_IN_STRINGPOOL(dzkey->str.addr, totlen)); if (!IS_AT_END_OF_STRINGPOOL(dzkey->str.addr, totlen)) { /* Move to top */ memcpy(stringpool.free, dzkey->str.addr, totlen); } else stringpool.free -= totlen; /* Backup over prior items */ assert(dzkey); POP_MV_STENT(); /* Release temporary MVAL */ dzkey = NULL; } zkeyptr = (char *)stringpool.free + totlen; } if (totlen) { /* at least one item already */ *zkeyptr++ = ';'; totlen++; } /* add READ/WRITE/LISTENING|handle|remoteinfo;... */ if (socket_listening == socketptr->state) { thislen = len = STR_LIT_LEN(LISTENING); memcpy(zkeyptr, LISTENING, len); } else if (socketptr->readyforwhat & SOCKREADY_READ) { thislen = len = STR_LIT_LEN(READ); memcpy(zkeyptr, READ, len); } else if (socketptr->readyforwhat & SOCKREADY_WRITE) { /* only WRITE ready */ thislen = len = STR_LIT_LEN(WRITE); memcpy(zkeyptr, WRITE, len); } zkeyptr += len; thislen += len; totlen += len; memcpy(zkeyptr, socketptr->handle, socketptr->handle_len); zkeyptr += socketptr->handle_len; *zkeyptr++ = '|'; thislen += (socketptr->handle_len + 1); totlen += (socketptr->handle_len + 1); if (socket_local != socketptr->protocol) { if (socket_listening == socketptr->state) len = SNPRINTF(zkeyptr, MAXZKEYITEMLEN - thislen, "%d", socketptr->local.port); else { if (NULL != socketptr->remote.saddr_ip) { len = STRLEN(socketptr->remote.saddr_ip); memcpy(zkeyptr, socketptr->remote.saddr_ip, len); } else len = 0; } # ifndef VMS } else { if (NULL != socketptr->local.sa) charptr = ((struct sockaddr_un *)(socketptr->local.sa))->sun_path; else if (NULL != socketptr->remote.sa) charptr = ((struct sockaddr_un *)(socketptr->remote.sa))->sun_path; else charptr = ""; len = STRLEN(charptr); len = MIN(len, (MAXZKEYITEMLEN - thislen)); memcpy(zkeyptr, charptr, len); # endif } zkeyptr += len; totlen += len; if (socketptr->nonblocked_output && (socketptr->readyforwhat == (SOCKREADY_READ | SOCKREADY_WRITE))) { /* already have READ now add WRITE item */ assert(totplusthislen >= (totlen + MAXZKEYITEMLEN)); *zkeyptr++ = ';'; totlen++; thislen = len = STR_LIT_LEN(WRITE); memcpy(zkeyptr, WRITE, len); zkeyptr += len; thislen += len; totlen += len; memcpy(zkeyptr, socketptr->handle, socketptr->handle_len); zkeyptr += socketptr->handle_len; *zkeyptr++ = '|'; thislen += (socketptr->handle_len + 1); totlen += (socketptr->handle_len + 1); if (socket_local != socketptr->protocol) { if (NULL != socketptr->remote.saddr_ip) { len = STRLEN(socketptr->remote.saddr_ip); memcpy(zkeyptr, socketptr->remote.saddr_ip, len); } else len = 0; } else { len = STRLEN(charptr); /* same as for READ */ len = MIN(len, (MAXZKEYITEMLEN - thislen)); memcpy(zkeyptr, charptr, len); } zkeyptr += len; totlen += len; } } } d->addr = (char *)stringpool.free; d->len = totlen; stringpool.free += totlen; REVERT_GTMIO_CH(&iod->pair, ch_set); return; } #define BLOCKOFF "OFF" #define BLOCKCLEAR "CLEAR" #define BLOCKCOUNT "COUNT" #define BLOCKSENT "SENT" #define MAX_BLOCK_OPTION 5 void iosocket_block_iocontrol(io_desc *iod, mval *option, mval *returnarg) { int tmpint, save_errno; d_socket_struct *dsocketptr; socket_struct *socketptr; boolean_t ch_set; char option_buf[MAX_BLOCK_OPTION + 1], *errptr; dsocketptr = (d_socket_struct *)iod->dev_sp; ESTABLISH_GTMIO_CH(&iod->pair, ch_set); if (0 >= dsocketptr->n_socket) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOSOCKETINDEV); return; } if (dsocketptr->n_socket <= dsocketptr->current_socket) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket); return; } if (dsocketptr->mupintr) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZINTRECURSEIO); return; } socketptr = dsocketptr->socket[dsocketptr->current_socket]; if (NULL != option) { lower_to_upper((uchar_ptr_t)option_buf, (uchar_ptr_t)(option->str.addr), MIN(SIZEOF(option_buf), option->str.len)); if (0 == memcmp(option_buf, BLOCKOFF, option->str.len)) { /* check if not already set or TLS already enabled */ if (socketptr->nonblocked_output) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKBLOCKERR, 2, LEN_AND_LIT("already non blocking")); return; } if (socket_connected != socketptr->state) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKBLOCKERR, 2, LEN_AND_LIT("must be connected")); return; } if (socketptr->tlsenabled) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKBLOCKERR, 2, LEN_AND_LIT("TLS enabled before non blocking")); return; } if (NULL != returnarg) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKBLOCKERR, 2, LEN_AND_LIT("OFF does not take an argument")); return; } socketptr->nonblocked_output = TRUE; socketptr->max_output_retries = gtm_non_blocked_write_retries; socketptr->args_written = socketptr->lastarg_size = socketptr->lastarg_sent = 0; SOCKET_OBUFFER_INIT(socketptr, socketptr->buffer_size, 0, 0); socketptr->obuffer_in_use = FALSE; /* only used if actually blocked or TLS */ #ifdef DEBUG if (WBTEST_ENABLED(WBTEST_SOCKET_NONBLOCK) && (0 < gtm_white_box_test_case_count)) { if (-1 == setsockopt(socketptr->sd, SOL_SOCKET, SO_SNDBUF, >m_white_box_test_case_count, SIZEOF(gtm_white_box_test_case_count))) { save_errno = errno; errptr = (char *)STRERROR(save_errno); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("SO_SNDBUF"), save_errno, LEN_AND_STR(errptr)); return; } } #endif } else if (0 == memcmp(option_buf, BLOCKCLEAR, option->str.len)) { if (socketptr->nonblocked_output) { if (NULL != returnarg) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKBLOCKERR, 2, LEN_AND_LIT("CLEAR does not take an argument")); return; } socketptr->args_written = socketptr->lastarg_size = socketptr->lastarg_sent = 0; } } else if (0 == memcmp(option_buf, BLOCKCOUNT, option->str.len)) { if (socketptr->nonblocked_output) { if (NULL == returnarg) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKBLOCKERR, 2, LEN_AND_LIT("COUNT requires local variable passed by reference")); return; } tmpint = (int)socketptr->args_written; MV_FORCE_UMVAL(returnarg, (unsigned int)tmpint); } } else if (0 == memcmp(option_buf, BLOCKSENT, option->str.len)) { if (socketptr->nonblocked_output) { if (NULL == returnarg) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKBLOCKERR, 2, LEN_AND_LIT("SENT requires local variable passed by reference")); return; } tmpint = (int)socketptr->lastarg_sent; MV_FORCE_MVAL(returnarg, tmpint); } } else { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKBLOCKERR, 2, LEN_AND_LIT("unknown option")); return; } } REVERT_GTMIO_CH(&iod->pair, ch_set); return; } fis-gtm-V7.0-005/sr_port/iosocket_listen.c0000755000032200000250000000615514342376331017417 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ /* iosocket_listen.c */ /* checks the socket state -- socket_bind */ /* checks the socket type -- passive */ /* start listening */ #include "mdef.h" #include #include "gtm_inet.h" #include "gtm_socket.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "io_params.h" #include "io.h" #include "gt_timer.h" #include "iosocketdef.h" #include "error.h" error_def(ERR_CURRSOCKOFR); error_def(ERR_LISTENPASSBND); error_def(ERR_NOSOCKETINDEV); error_def(ERR_SOCKLISTEN); error_def(ERR_TEXT); #define LISTENING "LISTENING" boolean_t iosocket_listen(io_desc *iod, unsigned short len) { d_socket_struct *dsocketptr; socket_struct *socketptr; boolean_t result, ch_set; assert(gtmsocket == iod->type); dsocketptr = (d_socket_struct *)iod->dev_sp; if (0 >= dsocketptr->n_socket) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOSOCKETINDEV); return FALSE; } if (dsocketptr->current_socket >= dsocketptr->n_socket) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket); return FALSE; } socketptr = dsocketptr->socket[dsocketptr->current_socket]; assert(socketptr && (socketptr->dev == dsocketptr)); ESTABLISH_RET_GTMIO_CH(&iod->pair, FALSE, ch_set); result = iosocket_listen_sock(socketptr, len); REVERT_GTMIO_CH(&iod->pair, ch_set); return result; } boolean_t iosocket_listen_sock(socket_struct *socketptr, unsigned short len) { char *errptr; int4 errlen; d_socket_struct *dsocketptr; if (((socketptr->state != socket_bound) && (socketptr->state != socket_listening)) || (TRUE != socketptr->passive)) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_LISTENPASSBND); return FALSE; } dsocketptr = socketptr->dev; dsocketptr->iod->dollar.key[0] = '\0'; /* establish a queue of length len for incoming connections */ if (-1 == listen(socketptr->sd, len)) { errptr = (char *)STRERROR(errno); errlen = STRLEN(errptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_SOCKLISTEN, 0, ERR_TEXT, 2, errlen, errptr); return FALSE; } socketptr->state = socket_listening; len = SIZEOF(LISTENING) - 1; memcpy(&dsocketptr->iod->dollar.key[0], LISTENING, len); dsocketptr->iod->dollar.key[len++] = '|'; memcpy(&dsocketptr->iod->dollar.key[len], socketptr->handle, socketptr->handle_len); len += socketptr->handle_len; dsocketptr->iod->dollar.key[len++] = '|'; if (socket_local == socketptr->protocol) SNPRINTF(&dsocketptr->iod->dollar.key[len], DD_BUFLEN - len, "%s", ((struct sockaddr_un *)(socketptr->local.sa))->sun_path); else SNPRINTF(&dsocketptr->iod->dollar.key[len], DD_BUFLEN - len, "%d", socketptr->local.port); return TRUE; } fis-gtm-V7.0-005/sr_port/iosocket_open.c0000644000032200000250000006451514342376331017063 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2022 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 #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_stat.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_limits.h" #include "gtm_time.h" #include "gtm_caseconv.h" #include "gtm_conv.h" #include "gtm_utf8.h" #include "gtm_netdb.h" #include "gtm_unistd.h" #include "copy.h" #include "gt_timer.h" #include "io.h" #include "iotimer.h" #include "io_params.h" #include "iosocketdef.h" #include "iormdef.h" #include "stringpool.h" #include "error.h" #include "op.h" #include "indir_enum.h" #include "min_max.h" GBLREF boolean_t gtm_utf8_mode; GBLREF d_socket_struct *socket_pool, *newdsocket; GBLREF int4 gtm_max_sockets; GBLREF io_pair io_std_device; /* standard device */ GBLREF UConverter *chset_desc[]; GBLREF volatile boolean_t dollar_zininterrupt; LITREF unsigned char io_params_size[]; LITREF mstr chset_names[]; error_def(ERR_ABNCOMPTINC); error_def(ERR_ADDRTOOLONG); error_def(ERR_CHSETALREADY); error_def(ERR_CURRSOCKOFR); error_def(ERR_DELIMSIZNA); error_def(ERR_DELIMWIDTH); error_def(ERR_DEVPARINAP); error_def(ERR_DEVPARMNEG); error_def(ERR_ILLESOCKBFSIZE); error_def(ERR_MRTMAXEXCEEDED); error_def(ERR_SOCKETEXIST); error_def(ERR_SOCKMAX); error_def(ERR_ZFF2MANY); error_def(ERR_ZINTRECURSEIO); #define ESTABLISHED "ESTABLISHED" short iosocket_open(io_log_name *dev, mval *pp, int file_des, mval *mspace, int4 timepar) { char addr[SA_MAXLITLEN], *errptr, sockaddr[SA_MAXLITLEN], temp_addr[SA_MAXLITLEN], dev_type[MAX_DEV_TYPE_LEN]; static char *conv_buff = NULL; unsigned char ch, *c, *start, *next, *top; int new_ozff_len, conv_len, handle_len, moreread_timeout, len; unsigned short port; int4 errlen, msec_timeout, real_errno, p_offset = 0, zff_len, delimiter_len; int4 options_len = 0; int d_socket_struct_len, soc_cnt; ABS_TIME cur_time, end_time; io_desc *ioptr; fd_set tcp_fd; uint4 bfsize = DEFAULT_SOCKET_BUFFER_SIZE, ibfsize; d_socket_struct *dsocketptr; socket_struct *curr_socketptr = NULL, *socketptr = NULL, *localsocketptr = NULL; mv_stent *mv_zintdev; boolean_t zint_conn_restart = FALSE; socket_interrupt *sockintr; mstr chset_mstr, optionstr; gtm_chset_t default_chset, temp_ichset, temp_ochset; boolean_t attach_specified = FALSE, listen_specified = FALSE, connect_specified = FALSE, ioerror_specified = FALSE, delay_specified = FALSE, nodelay_specified = FALSE, ibfsize_specified = FALSE, moreread_specified = FALSE, is_principal = FALSE, /* called from inetd */ newversion = FALSE, /* for local sockets */ ichset_specified, ochset_specified; unsigned char delimiter_buffer[MAX_N_DELIMITER * (MAX_DELIM_LEN + 1)], zff_buffer[MAX_ZFF_LEN]; unsigned char options_buffer[UCHAR_MAX + 1]; char ioerror, sock_handle[MAX_HANDLE_LEN], delimiter[MAX_DELIM_LEN + 1]; int socketptr_delim_len; char ipaddr[SA_MAXLEN]; int errcode; struct addrinfo *ai_ptr, *remote_ai_ptr; uic_struct_int uic; uint filemode; uint filemode_mask; unsigned long uicvalue; boolean_t ch_set; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ioptr = dev->iod; ESTABLISH_RET_GTMIO_CH(&ioptr->pair, -1, ch_set); assert((params) *(pp->str.addr + p_offset) < (unsigned char)n_iops); assert(ioptr != 0); assert(ioptr->state >= 0 && ioptr->state < n_io_dev_states); assert(ioptr->type == gtmsocket); if ((ioptr->state == dev_closed) && mspace && mspace->str.len && mspace->str.addr) { lower_to_upper((uchar_ptr_t)dev_type, (uchar_ptr_t)mspace->str.addr, mspace->str.len); if (STR_LIT_LEN("SOCKET") != mspace->str.len || 0 != memcmp(dev_type, "SOCKET", STR_LIT_LEN("SOCKET"))) { if (ioptr->dev_sp) free(ioptr->dev_sp); ioptr->state = dev_never_opened; } } d_socket_struct_len = SIZEOF(d_socket_struct) + (SIZEOF(socket_struct) * (gtm_max_sockets - 1)); if (ioptr->state == dev_never_opened) { dsocketptr = ioptr->dev_sp = (void *)malloc(d_socket_struct_len); ioptr->newly_created = TRUE; memset(dsocketptr, 0, d_socket_struct_len); dsocketptr->iod = ioptr; } else dsocketptr = (d_socket_struct *)ioptr->dev_sp; if (ioptr->state == dev_never_opened) { ioptr->state = dev_closed; ioptr->width = TCPDEF_WIDTH; ioptr->length = TCPDEF_LENGTH; ioptr->wrap = TRUE; dsocketptr->current_socket = -1; /* 1st socket is 0 */ if ((2 > file_des) && (0 <= file_des) && (!io_std_device.in || !io_std_device.out)) /* called from io_init */ is_principal = TRUE; } if (dsocketptr->mupintr) { /* check if connect was interrupted */ sockintr = &dsocketptr->sock_save_state; assertpro(sockwhich_invalid != sockintr->who_saved); /* Interrupt should never have an invalid save state */ if (dollar_zininterrupt) { dsocketptr->mupintr = FALSE; sockintr->who_saved = sockwhich_invalid; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZINTRECURSEIO); } assertpro(sockwhich_connect == sockintr->who_saved); /* ZINTRECURSEIO should have caught */ mv_zintdev = io_find_mvstent(dsocketptr->iod, FALSE); if (mv_zintdev && mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid) { /* mupintr will be reset and mvstent popped in iosocket_connect */ connect_specified = TRUE; listen_specified = FALSE; ibfsize_specified = sockintr->ibfsize_specified; assert(newdsocket); assert(newdsocket == sockintr->newdsocket); memcpy(newdsocket, (d_socket_struct *)mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr, d_socket_struct_len); socketptr = newdsocket->socket[newdsocket->current_socket]; ichset_specified = newdsocket->ichset_specified; ochset_specified = newdsocket->ochset_specified; temp_ichset = ioptr->ichset; temp_ochset = ioptr->ochset; assert(socketptr == (socket_struct *)mv_zintdev->mv_st_cont.mvs_zintdev.socketptr); zint_conn_restart = TRUE; /* skip what we already did, state == dev_closed */ } else { ichset_specified = ochset_specified = FALSE; temp_ichset = temp_ochset = CHSET_M; } } else { ioptr->dollar.zeof = FALSE; if (NULL == newdsocket) newdsocket = (d_socket_struct *)malloc(d_socket_struct_len); memcpy(newdsocket, dsocketptr, d_socket_struct_len); memcpy(ioptr->dollar.device, "0", SIZEOF("0")); zff_len = -1; /* indicates neither ZFF nor ZNOFF specified */ delimiter_len = -1; /* indicates neither DELIM nor NODELIM specified */ filemode = filemode_mask = 0; uic.mem = (uid_t)-1; /* flag as not specified */ uic.grp = (gid_t)-1; ichset_specified = ochset_specified = FALSE; while (iop_eol != (ch = *(pp->str.addr + p_offset++))) { switch(ch) { case iop_delimiter: delimiter_len = (int4)(unsigned char)*(pp->str.addr + p_offset); if (((MAX_DELIM_LEN + 1) * MAX_N_DELIMITER) >= delimiter_len) memcpy(delimiter_buffer, (pp->str.addr + p_offset + 1), delimiter_len); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DELIMSIZNA); break; case iop_ipchset: UTF8_ONLY( if (gtm_utf8_mode) { /* Only change ipchset if in UTF8 mode */ GET_ADDR_AND_LEN(chset_mstr.addr, chset_mstr.len); SET_ENCODING(temp_ichset, &chset_mstr) ichset_specified = TRUE; } ); break; case iop_opchset: UTF8_ONLY( if (gtm_utf8_mode) { /* Only change ipchset if in UTF8 mode */ GET_ADDR_AND_LEN(chset_mstr.addr, chset_mstr.len); SET_ENCODING(temp_ochset, &chset_mstr) ochset_specified = TRUE; } ); break; case iop_chset: UTF8_ONLY( if (gtm_utf8_mode) { /* Only change ipchset/opchset if in UTF8 mode */ GET_ADDR_AND_LEN(chset_mstr.addr, chset_mstr.len); SET_ENCODING(temp_ichset, &chset_mstr) temp_ochset = temp_ichset; ichset_specified = ochset_specified = TRUE; } ); break; /* Note the following 4 cases (iop_m/utf16/utf16be/utf16le) have no corresponding device parameter but are included here because they can be easily used in internal processing. */ case iop_m: UTF8_ONLY( temp_ichset = temp_ochset = CHSET_M; ichset_specified = ochset_specified = TRUE; ); break; case iop_utf16: UTF8_ONLY( if (gtm_utf8_mode) { /* Only change chset if in UTF8 mode */ temp_ichset = temp_ochset = CHSET_UTF16; ichset_specified = ochset_specified = TRUE; } ); break; case iop_utf16be: UTF8_ONLY( if (gtm_utf8_mode) { /* Only change chset if in UTF8 mode */ temp_ichset = temp_ochset = CHSET_UTF16BE; ichset_specified = ochset_specified = TRUE; } ); break; case iop_utf16le: UTF8_ONLY( if (gtm_utf8_mode) { /* Only change chset if in UTF8 mode */ temp_ichset = temp_ochset = CHSET_UTF16LE; ichset_specified = ochset_specified = TRUE; } ); break; /**********************************/ case iop_nodelimiter: delimiter_len = 0; break; case iop_zdelay: delay_specified = TRUE; break; case iop_znodelay: nodelay_specified = TRUE; break; case iop_zbfsize: GET_ULONG(bfsize, pp->str.addr + p_offset); if ((0 == bfsize) || (MAX_SOCKET_BUFFER_SIZE < bfsize)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_ILLESOCKBFSIZE, 1, bfsize); break; case iop_zibfsize: ibfsize_specified = TRUE; GET_ULONG(ibfsize, pp->str.addr + p_offset); if (0 == ibfsize) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_ILLESOCKBFSIZE, 1, bfsize); break; case iop_zlisten: listen_specified = TRUE; len = (int)(unsigned char)*(pp->str.addr + p_offset); if (len < SA_MAXLITLEN) { memset(sockaddr, 0, SIZEOF(sockaddr)); assert((0 < len) && (SIZEOF(sockaddr) >= len)); memcpy(sockaddr, pp->str.addr + p_offset + 1, len); } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_ADDRTOOLONG, 4, len, pp->str.addr + p_offset + 1, len, SA_MAXLITLEN); break; case iop_connect: connect_specified = TRUE; len = (int)(unsigned char)*(pp->str.addr + p_offset); if (len < SA_MAXLITLEN) { memset(sockaddr, 0, SIZEOF(sockaddr)); assert((0 < len) && (SIZEOF(sockaddr) >= len)); memcpy(sockaddr, pp->str.addr + p_offset + 1, len); } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_ADDRTOOLONG, 4, len, pp->str.addr + p_offset + 1, len, SA_MAXLITLEN); break; case iop_ioerror: ioerror_specified = TRUE; ioerror = *(pp->str.addr + p_offset + 1); /* the first char decides */ break; case iop_exception: DEF_EXCEPTION(pp, p_offset, ioptr); break; case iop_attach: attach_specified = TRUE; handle_len = (int)(unsigned char)*(pp->str.addr + p_offset); if (handle_len > MAX_HANDLE_LEN) handle_len = MAX_HANDLE_LEN; assert((0 < handle_len) && (SIZEOF(sock_handle) >= handle_len)); memcpy(sock_handle, pp->str.addr + p_offset + 1, handle_len); break; case iop_socket: RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DEVPARINAP); break; case iop_zff: if (MAX_ZFF_LEN >= (zff_len = (int4)(unsigned char)*(pp->str.addr + p_offset))) memcpy(zff_buffer, (char *)(pp->str.addr + p_offset + 1), zff_len); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZFF2MANY, 2, zff_len, MAX_ZFF_LEN); break; case iop_znoff: zff_len = 0; break; case iop_wrap: ioptr->wrap = TRUE; break; case iop_nowrap: ioptr->wrap = FALSE; break; case iop_morereadtime: /* Time in milliseconds socket read will wait for more data before returning */ GET_LONG(moreread_timeout, pp->str.addr + p_offset); if (-1 == moreread_timeout) moreread_timeout = DEFAULT_MOREREAD_TIMEOUT; else if (-1 > moreread_timeout) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DEVPARMNEG); else if (MAX_MOREREAD_TIMEOUT < moreread_timeout) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_MRTMAXEXCEEDED, 1, MAX_MOREREAD_TIMEOUT); moreread_specified = TRUE; break; case iop_newversion: newversion = TRUE; break; case iop_uic: start = (unsigned char *)pp->str.addr + p_offset; top = (unsigned char *)pp->str.addr + p_offset + 1 + *start; next = ++start; /* past length */ uicvalue = 0; while ((',' != *next) && (('0' <= *next) && ('9' >= *next)) && (next < top)) uicvalue = (10 * uicvalue) + (*next++ - '0'); if (start < next) uic.mem = uicvalue; if (',' == *next) { start = ++next; uicvalue = 0; while ((',' != *next) && (('0' <= *next) && ('9' >= *next)) && (next < top)) uicvalue = (10 * uicvalue) + (*next++ - '0'); if (start < next) uic.grp = uicvalue; } break; case iop_w_protection: filemode_mask |= S_IRWXO; filemode |= (uint)(unsigned char)*(pp->str.addr + p_offset); break; case iop_g_protection: filemode_mask |= S_IRWXG; filemode |= (uint)(unsigned char)*(pp->str.addr + p_offset) << 3; break; case iop_s_protection: case iop_o_protection: filemode_mask |= S_IRWXU; filemode |= (uint)(unsigned char)*(pp->str.addr + p_offset) << 6; break; case iop_options: options_len = (int4)(unsigned char)*(pp->str.addr + p_offset); if (UCHAR_MAX >= options_len) { memcpy(options_buffer, (unsigned char *)(pp->str.addr + p_offset + 1), options_len); options_buffer[options_len] = '\0'; } break; default: break; } p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); } if (listen_specified && connect_specified) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ABNCOMPTINC, 6, LEN_AND_LIT("CONNECT"), LEN_AND_LIT("ZLISTEN"), LEN_AND_LIT("OPEN")); return FALSE; } if (delay_specified && nodelay_specified) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ABNCOMPTINC, 6, LEN_AND_LIT("DELAY"), LEN_AND_LIT("NODELAY"), LEN_AND_LIT("OPEN")); return FALSE; } if (listen_specified || connect_specified || is_principal) { /* CHSET cannot be specified when opening a new socket, if there already are open sockets. */ if (0 < dsocketptr->n_socket && ((ochset_specified && (temp_ochset != ioptr->ochset)) || (ichset_specified && (temp_ichset != ioptr->ichset)))) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_CHSETALREADY, 8, chset_names[ioptr->ichset].len, chset_names[ioptr->ichset].addr, chset_names[ioptr->ochset].len, chset_names[ioptr->ochset].addr); return FALSE; } if (NULL == (socketptr = iosocket_create(sockaddr, bfsize, is_principal ? file_des : -1, listen_specified))) { REVERT_GTMIO_CH(&ioptr->pair, ch_set); return FALSE; } assert(listen_specified == socketptr->passive); if (ioerror_specified) socketptr->ioerror = ('T' == ioerror || 't' == ioerror); socketptr->nodelay = nodelay_specified; /* defaults to DELAY */ if (ibfsize_specified) socketptr->bufsiz = ibfsize; if (moreread_specified) { socketptr->moreread_timeout = moreread_timeout; socketptr->def_moreread_timeout = TRUE; /* iosocket_readfl.c needs to know user specified */ } if (listen_specified) { /* for LOCAL sockets */ if (filemode_mask) { socketptr->filemode_mask = filemode_mask; socketptr->filemode = filemode; } socketptr->uic = uic; /* -1 is no change */ } if (attach_specified) { /* socket handle -- also check for duplication */ if (iosocket_handle(sock_handle, &handle_len, FALSE, newdsocket) >= 0) { if (FD_INVALID != socketptr->temp_sd) close(socketptr->temp_sd); SOCKET_FREE(socketptr); assert(ioptr->newly_created == FALSE); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKETEXIST, 2, handle_len, sock_handle); return FALSE; } } else iosocket_handle(sock_handle, &handle_len, TRUE, dsocketptr); assert(MAX_HANDLE_LEN > handle_len); if (MAX_HANDLE_LEN < handle_len) handle_len = MAX_HANDLE_LEN; socketptr->handle_len = handle_len; assert((0 < handle_len) && (SIZEOF(socketptr->handle) >= handle_len)); memcpy(socketptr->handle, sock_handle, handle_len); /* connects newdsocket and socketptr (the new socket) */ if (gtm_max_sockets <= newdsocket->n_socket) { assert(ioptr->newly_created == FALSE); if (FD_INVALID != socketptr->temp_sd) close(socketptr->temp_sd); SOCKET_FREE(socketptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SOCKMAX, 1, gtm_max_sockets); return FALSE; } socketptr->dev = newdsocket; newdsocket->socket[newdsocket->n_socket++] = socketptr; newdsocket->current_socket = newdsocket->n_socket - 1; } /* set curr_socketptr to the new socket, if created, or the existing current socket. * curr_socketptr will be used to set the values of delimiter, ZFF. */ if (!socketptr) { /* If new socket not created, then use the existing (current) socket from the socket device */ if (dsocketptr->n_socket <= dsocketptr->current_socket) { assert(FALSE); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket); return FALSE; } if (-1 != dsocketptr->current_socket) curr_socketptr = dsocketptr->socket[dsocketptr->current_socket]; } else /* Set to the newly created socket */ curr_socketptr = socketptr; if (curr_socketptr && (0 < options_len)) { /* parse and store options in socket struct */ optionstr.addr = (char *)options_buffer; optionstr.len = options_len; devoptions(NULL, curr_socketptr, &optionstr, "OPEN", IOP_OPEN_OK); } /* parse the delimiter: delimiter_buffer ==> socketptr->delimiter[...] */ if ((0 <= delimiter_len) && (curr_socketptr)) iosocket_delimiter(delimiter_buffer, delimiter_len, curr_socketptr, (0 == delimiter_len)); if (ioptr->wrap && curr_socketptr && (0 != curr_socketptr->n_delimiter) && (ioptr->width < curr_socketptr->delimiter[0].len)) { socketptr_delim_len = curr_socketptr->delimiter[0].len; SOCKET_FREE(curr_socketptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_DELIMWIDTH, 2, ioptr->width, socketptr_delim_len); assert(FALSE); } if (curr_socketptr && (0 <= zff_len) && /* ZFF or ZNOFF specified */ (0 < (curr_socketptr->zff.len = zff_len))) /* assign the new ZFF len, might be 0 from ZNOFF, or ZFF="" */ { /* ZFF="non-zero-len-string" specified */ if (gtm_utf8_mode) /* Check if ZFF has any invalid UTF-8 character */ { /* Note: the ZFF string originates from the source program, so is in UTF-8 mode or M mode regardless * of OCHSET of this device; ZFF is output on WRITE # command, and MUST contain valid UTF-8 sequence */ utf8_len_strict(zff_buffer, zff_len); /* triggers badchar error for invalid sequence */ } /* we rely on curr_socketptr->zff.addr being set to 0 in iosocket_create() */ if (NULL == curr_socketptr->zff.addr) curr_socketptr->zff.addr = (char *)malloc(MAX_ZFF_LEN); else if (curr_socketptr->zff.addr != curr_socketptr->ozff.addr) { assert(NULL != curr_socketptr->ozff.addr); free(curr_socketptr->ozff.addr); /* prevent leak of prior converted form */ } assert((0 < zff_len) && (MAX_ZFF_LEN >= zff_len)); memcpy(curr_socketptr->zff.addr, zff_buffer, zff_len); curr_socketptr->ozff = curr_socketptr->zff; /* will contain converted UTF-16 form if needed */ } else if (curr_socketptr && (0 == zff_len)) { /* CHSET can change as part of reOPEN, hence changing the converted zff */ if ((NULL != curr_socketptr->ozff.addr) && (curr_socketptr->ozff.addr != curr_socketptr->zff.addr)) free(curr_socketptr->ozff.addr); /* previously converted */ curr_socketptr->ozff = curr_socketptr->zff; /* will contain converted UTF-16 form if needed */ } } /* action */ if ((listen_specified && ((!iosocket_bind(socketptr, timepar, ibfsize_specified, newversion)) || (!iosocket_listen_sock(socketptr, DEFAULT_LISTEN_DEPTH)))) || (connect_specified && (!iosocket_connect(socketptr, timepar, ibfsize_specified)))) { REVERT_GTMIO_CH(&ioptr->pair, ch_set); return FALSE; } else if (is_principal) { len = SIZEOF(ESTABLISHED) - 1; memcpy(&ioptr->dollar.key[0], ESTABLISHED, len); ioptr->dollar.key[len++] = '|'; assert((DD_BUFLEN - len - 1) >= socketptr->handle_len); /* handle could be MAX_HANDLE_LEN */ memcpy(&ioptr->dollar.key[len], socketptr->handle, MIN(socketptr->handle_len, DD_BUFLEN - len - 2)); len += MIN(socketptr->handle_len, DD_BUFLEN - len - 2); /* -2 for the next delimiter and trailing null */ ioptr->dollar.key[len++] = '|'; if (socket_tcpip == socketptr->protocol) STRNCPY_STR(&ioptr->dollar.key[len], socketptr->remote.saddr_ip, DD_BUFLEN - len - 1); else if (socket_local == socketptr->protocol) { if ((NULL != socketptr->local.sa) && ('\0' != ((struct sockaddr_un *)(socketptr->local.sa))->sun_path[0])) STRNCPY_STR(&ioptr->dollar.key[len], ((struct sockaddr_un *)(socketptr->local.sa))->sun_path, DD_BUFLEN - len - 1); else if ((NULL != socketptr->remote.sa) && ('\0' != ((struct sockaddr_un *)(socketptr->remote.sa))->sun_path[0])) STRNCPY_STR(&ioptr->dollar.key[len], ((struct sockaddr_un *)(socketptr->remote.sa))->sun_path, DD_BUFLEN - len - 1); /* set default delimiter on principal local sockets to resemble rm device */ delimiter_buffer[0] = NATIVE_NL; delimiter_len = 1; iosocket_delimiter(delimiter_buffer, delimiter_len, socketptr, FALSE); } else ioptr->dollar.key[len] = '\0'; ioptr->dollar.key[DD_BUFLEN-1] = '\0'; /* In case we fill the buffer */ } /* commit the changes to the list */ if (0 >= dsocketptr->n_socket) /* before any new socket added */ { /* Set default CHSET in case none supplied */ default_chset = (gtm_utf8_mode) ? CHSET_UTF8 : CHSET_M; if (!ichset_specified && !IS_UTF16_CHSET(ioptr->ichset)) ioptr->ichset = default_chset; if (!ochset_specified && !IS_UTF16_CHSET(ioptr->ochset)) ioptr->ochset = default_chset; } /* These parameters are to be set every time CHSET changes */ if (ichset_specified) { CHECK_UTF16_VARIANT_AND_SET_CHSET_SOCKET(dsocketptr->ichset_utf16_variant, ioptr->ichset, temp_ichset, assert(!socketptr)); newdsocket->ichset_utf16_variant = dsocketptr->ichset_utf16_variant; newdsocket->ichset_specified = dsocketptr->ichset_specified = TRUE; } if (ochset_specified) { CHECK_UTF16_VARIANT_AND_SET_CHSET_SOCKET(dsocketptr->ochset_utf16_variant, ioptr->ochset, temp_ochset, assert(!socketptr)); newdsocket->ochset_utf16_variant = dsocketptr->ochset_utf16_variant; newdsocket->ochset_specified = dsocketptr->ochset_specified = TRUE; } if ((CHSET_M != ioptr->ichset) && (CHSET_UTF16 != ioptr->ichset) && (CHSET_MAX_IDX > ioptr->ichset)) get_chset_desc(&chset_names[ioptr->ichset]); if ((CHSET_M != ioptr->ochset) && (CHSET_UTF16 != ioptr->ochset) && (CHSET_MAX_IDX > ioptr->ichset)) get_chset_desc(&chset_names[ioptr->ochset]); if (gtm_utf8_mode) { /* If CHSET is being changed to UTF-16, and delimitors are not converted, convert them * But only if the UTF16 variant has already been determined. * If CHSET is being changed to non-UTF-16, and delims are converted, free them * Do this delimiter conversion for all sockets in the currect device. */ if (ichset_specified) for (soc_cnt=0; soc_cnt < dsocketptr->n_socket; soc_cnt++) { localsocketptr = dsocketptr->socket[soc_cnt]; if (!(localsocketptr && (0 < localsocketptr->n_delimiter))) continue; if (((localsocketptr->delimiter[0].addr == localsocketptr->idelimiter[0].addr) && IS_UTF16_CHSET(ioptr->ichset) && IS_UTF16_CHSET(dsocketptr->ichset_utf16_variant)) || ((localsocketptr->delimiter[0].addr != localsocketptr->idelimiter[0].addr) && !IS_UTF16_CHSET(ioptr->ichset))) iosocket_idelim_conv(localsocketptr, ioptr->ichset); } if (ochset_specified) for (soc_cnt=0; soc_cnt < dsocketptr->n_socket; soc_cnt++) { localsocketptr = dsocketptr->socket[soc_cnt]; if (!(localsocketptr && (0 < localsocketptr->n_delimiter))) continue; if (((localsocketptr->delimiter[0].addr == localsocketptr->odelimiter0.addr) && IS_UTF16_CHSET(ioptr->ochset) && IS_UTF16_CHSET(dsocketptr->ochset_utf16_variant)) || ((localsocketptr->delimiter[0].addr != localsocketptr->odelimiter0.addr) && !IS_UTF16_CHSET(ioptr->ochset))) iosocket_odelim_conv(localsocketptr, ioptr->ochset); } /* Now convert the ZFFs */ if (ochset_specified) { if (!IS_UTF16_CHSET(ioptr->ochset)) { /* Changed to a non-UTF16 CHSET. free all converted ZFFs */ for (soc_cnt=0; soc_cnt < dsocketptr->n_socket; soc_cnt++) { localsocketptr = dsocketptr->socket[soc_cnt]; if (localsocketptr && (NULL != localsocketptr->ozff.addr) && (0 < localsocketptr->zff.len) && (localsocketptr->ozff.addr != localsocketptr->zff.addr)) free(localsocketptr->ozff.addr); /* previously converted */ localsocketptr->ozff = localsocketptr->zff; /* contains converted UTF-16 form */ } } else if (IS_UTF16_CHSET(dsocketptr->ochset_utf16_variant)) { /* Changed to UTF-16 CHSET. convert all ZFFs */ conv_buff = malloc(MAX_ZFF_LEN); for (soc_cnt=0; soc_cnt < dsocketptr->n_socket; soc_cnt++) { localsocketptr = dsocketptr->socket[soc_cnt]; if (localsocketptr && (NULL != localsocketptr->zff.addr) && (0 < localsocketptr->zff.len) && (localsocketptr->ozff.addr == localsocketptr->zff.addr)) { conv_len = MAX_ZFF_LEN; new_ozff_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[ioptr->ochset], &localsocketptr->zff, conv_buff, &conv_len); assert(MAX_ZFF_LEN > new_ozff_len); localsocketptr->ozff.len = new_ozff_len; localsocketptr->ozff.addr = malloc(new_ozff_len); memcpy(localsocketptr->ozff.addr, conv_buff, new_ozff_len); memset(conv_buff, 0, MAX_ZFF_LEN); /* Reset to be reused. */ } } } } } if (listen_specified || connect_specified || is_principal) { socketptr->dev = dsocketptr; memcpy(dsocketptr, newdsocket, d_socket_struct_len); } ioptr->newly_created = FALSE; ioptr->state = dev_open; REVERT_GTMIO_CH(&ioptr->pair, ch_set); return TRUE; } fis-gtm-V7.0-005/sr_port/mlk_unpend.h0000755000032200000250000000107114342376331016352 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef MLK_UNPEND_INCLUDED #define MLK_UNPEND_INCLUDED void mlk_unpend(mlk_pvtblk *p); #endif /* MLK_UNPEND_INCLUDED */ fis-gtm-V7.0-005/sr_port/iosocket_poolinit.c0000755000032200000250000000320214342376331017744 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ /* iosocket_poolinit.c */ #include "mdef.h" #include "gtm_string.h" #include "gtm_unistd.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "io.h" #include "io_params.h" #include "iotimer.h" #include "gt_timer.h" #include "iosocketdef.h" #include "op.h" GBLREF d_socket_struct *socket_pool; LITREF mval literal_notimeout; static char socketpoolp = '\0'; void iosocket_poolinit(void) { mval sockv, sockp, sockm; io_log_name *nl; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; memset(&sockv, 0, SIZEOF(mval)); memset(&sockp, 0, SIZEOF(mval)); memset(&sockm, 0, SIZEOF(mval)); sockv.mvtype = MV_STR; sockv.str.len = SIZEOF(SOCKETPOOLNAME) - 1; sockv.str.addr = SOCKETPOOLNAME; sockp.mvtype = MV_STR; sockp.str.len = SIZEOF(socketpoolp); sockp.str.addr = &socketpoolp; sockm.mvtype = MV_STR; sockm.str.len = SIZEOF(SOCDEVTYPENAME) - 1; sockm.str.addr = SOCDEVTYPENAME; TREF(is_socketpool) = TRUE; op_open(&sockv, &sockp, (mval *)&literal_notimeout, &sockm); TREF(is_socketpool) = FALSE; nl = get_log_name(&sockv.str, NO_INSERT); assert(NULL != nl); socket_pool = (d_socket_struct *)(nl->iod->dev_sp); } fis-gtm-V7.0-005/sr_port/iosocket_rdone.c0000755000032200000250000000265214342376331017226 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ /* iosocket_rdone.c */ #include "mdef.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "io.h" #include "gt_timer.h" #include "iosocketdef.h" #include "gtm_utf8.h" GBLREF io_pair io_curr_device; int iosocket_rdone(mint *v, int4 msec_timeout) { gtm_chset_t ichset; int ret; io_desc *iod; mval tmp; uint4 codepoint; ret = iosocket_readfl(&tmp, 1, msec_timeout); if (ret) { if (0 < tmp.str.len) { ichset = io_curr_device.in->ichset; switch(ichset) { case CHSET_M: codepoint = (unsigned char)tmp.str.addr[0]; break; case CHSET_UTF8: case CHSET_UTF16BE: case CHSET_UTF16LE: UTF8_MBTOWC(tmp.str.addr, tmp.str.addr + tmp.str.len, codepoint); break; default: assertpro(ichset != ichset); } UTF8_ONLY(assert(WEOF != codepoint)); } else /* Null length string returns 0 */ codepoint = 0; *v = (mint)(codepoint); } else *v = -1; return ret; } fis-gtm-V7.0-005/sr_port/iosocket_read.c0000755000032200000250000000143214342376331017025 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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. * * * ****************************************************************/ /* iosocket_read.c */ #include "mdef.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "io.h" #include "gt_timer.h" #include "iosocketdef.h" int iosocket_read(mval *v, int4 msec_timeout) { return iosocket_readfl(v, 0, msec_timeout); /* 0 means not fixed length */ } fis-gtm-V7.0-005/sr_port/iosocket_readfl.c0000644000032200000250000011147514342376333017357 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* iosocket_readfl.c */ #include "mdef.h" #include #include "gtm_time.h" #ifdef __MVS__ #include #endif #include "gtm_socket.h" #include "gtm_stdlib.h" #include "gtm_inet.h" #include "gtm_string.h" #include "gtm_fcntl.h" #include "eintr_wrappers.h" #include "gt_timer.h" #include "io.h" #include "iotimer.h" #include "stringpool.h" #include "iosocketdef.h" #include "min_max.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "wake_alarm.h" #include "gtm_conv.h" #include "gtm_utf8.h" #include #include "stack_frame.h" #include "mv_stent.h" #include "send_msg.h" #include "error.h" #include "trace_table.h" #include "svnames.h" #include "op.h" #include "util.h" #ifdef GTM_TLS #include "gtm_tls.h" #endif GBLREF bool out_of_time; GBLREF boolean_t gtm_utf8_mode, prin_in_dev_failure, prin_out_dev_failure; GBLREF int socketus_interruptus; GBLREF io_pair io_curr_device, io_std_device; GBLREF mstr chset_names[]; GBLREF mv_stent *mv_chain; GBLREF mval dollar_zstatus; GBLREF spdesc stringpool; GBLREF stack_frame *frame_pointer; GBLREF UConverter *chset_desc[]; GBLREF unsigned char *stackbase, *stacktop, *msp, *stackwarn; GBLREF volatile boolean_t dollar_zininterrupt; GBLREF volatile int4 outofband; error_def(ERR_BOMMISMATCH); error_def(ERR_CURRSOCKOFR); error_def(ERR_GETSOCKOPTERR); error_def(ERR_IOEOF); error_def(ERR_MAXSTRLEN); error_def(ERR_NOPRINCIO); error_def(ERR_NOSOCKETINDEV); error_def(ERR_SETSOCKOPTERR); error_def(ERR_SOCKPASSDATAMIX); error_def(ERR_TEXT); error_def(ERR_ZINTRECURSEIO); #ifdef UTF8_SUPPORTED /* Maintenance of $KEY, $DEVICE and $ZB on a badchar error */ void iosocket_readfl_badchar(mval *vmvalptr, int datalen, int delimlen, unsigned char *delimptr, unsigned char *strend) { int tmplen, errlen; unsigned char *delimend; io_desc *iod; d_socket_struct *dsocketptr; iod = io_curr_device.in; dsocketptr = (d_socket_struct *)(iod->dev_sp); vmvalptr->str.len = datalen; vmvalptr->str.addr = (char *)stringpool.free; if (0 < datalen) { /* Return how much input we got */ if ((CHSET_M != iod->ichset) && (CHSET_UTF8 != iod->ichset)) { DBGSOCK2((stdout, "socrflbc: Converting UTF16xx data back to UTF8 for internal use\n")); vmvalptr->str.len = gtm_conv(chset_desc[iod->ichset], chset_desc[CHSET_UTF8], &vmvalptr->str, NULL, NULL); vmvalptr->str.addr = (char *)stringpool.free; } stringpool.free += vmvalptr->str.len; } if ((NULL != strend) && (NULL != delimptr)) { /* First find the end of the delimiter (max of 4 bytes) */ if (0 == delimlen) { for (delimend = delimptr; (GTM_MB_LEN_MAX >= delimlen) && (delimend < strend); ++delimend, ++delimlen) { if (UTF8_VALID(delimend, strend, tmplen)) break; } } if (0 < delimlen) { /* Set $KEY and $ZB with the failing badchar */ memcpy(iod->dollar.zb, delimptr, MIN(delimlen, ESC_LEN - 1)); iod->dollar.zb[MIN(delimlen, ESC_LEN - 1)] = '\0'; memcpy(iod->dollar.key, delimptr, MIN(delimlen, DD_BUFLEN - 1)); iod->dollar.key[MIN(delimlen, DD_BUFLEN - 1)] = '\0'; } } SET_DOLLARDEVICE_ONECOMMA_ERRSTR(iod, BADCHAR_DEVICE_MSG, errlen); } #endif /* VMS uses the UCX interface; should support others that emulate it * width == 0 is a flag for non-fixed length read. timeout is in milliseconds */ int iosocket_readfl(mval *v, int4 width, int4 msec_timeout) { int ret, byteperchar; boolean_t timed, vari, more_data, terminator, has_delimiter, requeue_done, do_delim_conv, need_get_chset; boolean_t zint_restart, outofband_terminate, one_read_done, utf8_active; int flags, len, real_errno, save_errno, fcntl_res, errlen, devlen, charlen, stp_need; int bytes_read, orig_bytes_read, ii, max_bufflen, bufflen, chars_read, mb_len, match_delim; io_desc *iod; d_socket_struct *dsocketptr; socket_struct *socketptr; TID timer_id; ABS_TIME cur_time, end_time, time_for_read, zero; char *errptr; unsigned char *buffptr, *c_ptr, *c_top, *inv_beg, *buffer_start, *old_stringpool_free; ssize_t status; gtm_chset_t ichset; mv_stent *mv_zintdev; socket_interrupt *sockintr; boolean_t result; boolean_t ch_set; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(stringpool.free >= stringpool.base); assert(stringpool.free <= stringpool.top); iod = io_curr_device.in; ESTABLISH_RET_GTMIO_CH(&iod->pair, -1, ch_set); TRCTBL_ENTRY(SOCKRFL_ENTRY, width, iod, (INTPTR_T)socketus_interruptus, NULL); ichset = iod->ichset; assert(dev_open == iod->state); assert(gtmsocket == iod->type); dsocketptr = (d_socket_struct *)(iod->dev_sp); if (0 >= dsocketptr->n_socket) { if (iod == io_std_device.in) { result = ionl_readfl(v, width, msec_timeout); REVERT_GTMIO_CH(&iod->pair, ch_set); return result; } else { iod->dollar.za = ZA_IO_ERR; rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOSOCKETINDEV); return 0; } } if (dsocketptr->n_socket <= dsocketptr->current_socket) { iod->dollar.za = ZA_IO_ERR; rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket); return 0; } utf8_active = NON_UTF8_ONLY(FALSE) UTF8_ONLY(gtm_utf8_mode ? IS_UTF_CHSET(ichset) : FALSE); byteperchar = UTF8_ONLY(utf8_active ? GTM_MB_LEN_MAX :) 1; if (0 == width) { /* op_readfl won't do this; must be a call from iosocket_read */ vari = TRUE; width = MAX_STRLEN; } else { vari = FALSE; width = (width < MAX_STRLEN) ? width : MAX_STRLEN; } /* If width is set to MAX_STRLEN, we might be overly generous (assuming every char is just one byte) we must check if byte * length crosses the MAX_STRLEN boundary */ socketptr = dsocketptr->socket[dsocketptr->current_socket]; assert(socketptr); ENSURE_DATA_SOCKET(socketptr); sockintr = &dsocketptr->sock_save_state; assert(sockintr); outofband_terminate = FALSE; one_read_done = FALSE; zint_restart = FALSE; /* Check if new or resumed read */ if (dsocketptr->mupintr) { /* We have a pending read restart of some sort */ assertpro(sockwhich_invalid != sockintr->who_saved); /* Interrupt should never have an invalid save state */ /* Check we aren't recursing on this device */ if (dollar_zininterrupt) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZINTRECURSEIO); assertpro(sockwhich_readfl == sockintr->who_saved); /* ZINTRECURSEIO should have caught */ DBGSOCK((stdout, "socrfl: *#*#*#*#*#*#*# Restarted interrupted read\n")); dsocketptr->mupintr = FALSE; sockintr->who_saved = sockwhich_invalid; mv_zintdev = io_find_mvstent(iod, FALSE); assert(mv_zintdev); if (mv_zintdev) { if (mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid) { bytes_read = mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.len; assert(bytes_read == sockintr->bytes_read); if (bytes_read) { buffer_start = (unsigned char *)mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr; zint_restart = TRUE; TRCTBL_ENTRY(SOCKRFL_MVS_ZINTR, bytes_read, buffer_start, stringpool.free, NULL); /* BYPASS_OK */ } else TRCTBL_ENTRY(SOCKRFL_MVS_ZINTR, 0, NULL, stringpool.free, NULL); /* BYPASS_OK found but empty */ } else { TRCTBL_ENTRY(SOCKRFL_MVS_ZINTR, 0, NULL, NULL, NULL); /* Indicate mvstent found but invalid */ assert(FALSE); DBGSOCK((stdout, "socrfl: Evidence of an interrupt, but it was invalid\n")); } /* Done with this mv_stent. Pop it off if we can, else mark it inactive. */ if (mv_chain == mv_zintdev) POP_MV_STENT(); /* pop if top of stack */ else { /* Else mark it unused */ mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = 0; mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = NULL; } } else { sockintr->end_time_valid = FALSE; DBGSOCK((stdout, "socrfl: Evidence of an interrupt, but no MV_STENT\n")); TRCTBL_ENTRY(SOCKRFL_MVS_ZINTR, -1, NULL, NULL, NULL); /* Indicater mvstent entry not found */ } } else sockintr->end_time_valid = FALSE; if (!zint_restart) { /* Simple path (not restart or nothing read,) no worries * compute the worst case byte requirement knowing that width is in chars */ max_bufflen = (vari) ? MAX_STRBUFF_INIT : ((MAX_STRLEN > (width * byteperchar)) ? (width * byteperchar) : MAX_STRLEN); ENSURE_STP_FREE_SPACE(max_bufflen); bytes_read = 0; chars_read = 0; buffer_start = NULL; socketptr->lastaction = dsocketptr->waitcycle; socketptr->readyforwhat &= ~SOCKREADY_READ; socketptr->pendingevent &= ~SOCKPEND_READ; } else { assert(bytes_read); max_bufflen = sockintr->max_bufflen; chars_read = sockintr->chars_read; assert(chars_read <= bytes_read); DBGSOCK((stdout, "socrfl: .. mv_stent found - bytes_read: %d chars_read: %d max_bufflen: %d" " interrupts: %d\n", bytes_read, chars_read, max_bufflen, socketus_interruptus)); DBGSOCK((stdout, "socrfl: .. msec_timeout: %d\n", msec_timeout)); DBGSOCK_ONLY2(if (sockintr->end_time_valid) DBGSOCK((stdout, "socrfl: .. endtime: %d/%d\n", end_time.at_sec, end_time.at_usec))); DBGSOCK((stdout, "socrfl: .. buffer address: 0x"lvaddr" stringpool: 0x"lvaddr"\n", buffer_start, stringpool.free)); if (!IS_AT_END_OF_STRINGPOOL(buffer_start, bytes_read)) { /* Not @ stringpool.free - must move what we have, so we need room for the whole anticipated message */ DBGSOCK2((stdout, "socrfl: .. Stuff put on string pool after our buffer\n")); stp_need = max_bufflen; } else { /* Previously read buffer piece is still last thing in stringpool, so we need room for the rest */ DBGSOCK2((stdout, "socrfl: .. Our buffer did not move in the stringpool\n")); stp_need = max_bufflen - bytes_read; assert(stp_need < max_bufflen); } TRCTBL_ENTRY(SOCKRFL_RESTARTED, chars_read, (INTPTR_T)max_bufflen, (INTPTR_T)stp_need, buffer_start); if (!IS_STP_SPACE_AVAILABLE(stp_need)) { /* Need more room */ DBGSOCK2((stdout, "socrfl: .. garbage collection done in restart after interrupt\n")); v->str.addr = (char *)buffer_start; /* Protect buffer from reclaim */ v->str.len = bytes_read; INVOKE_STP_GCOL(max_bufflen); buffer_start = (unsigned char *)v->str.addr; TRCTBL_ENTRY(SOCKRFL_RSTGC, 0, buffer_start, stringpool.free, NULL); } assert((buffer_start + bytes_read) <= stringpool.free); /* BYPASSOK */ if (!IS_AT_END_OF_STRINGPOOL(buffer_start, bytes_read)) { /* Now need to move it to the top */ assert(stp_need == max_bufflen); memcpy(stringpool.free, buffer_start, bytes_read); /* BYPASSOK */ buffer_start = stringpool.free; /* BYPASSOK */ } else { /* It should still be just under the used space */ stringpool.free = buffer_start; /* backup the free pointer */ } v->str.len = 0; /* Clear incase interrupt or error -- don't want to hold this buffer */ /* At this point, our previous buffer is sitting at stringpool.free and can be * expanded if necessary. This is what the rest of the code is expecting. */ } TRCTBL_ENTRY(SOCKRFL_BEGIN, chars_read, buffer_start, stringpool.free, NULL); if (iod->dollar.x && (TCP_WRITE == socketptr->lastop)) { /* Switching from write to read */ assert(!zint_restart); if (!iod->dollar.za) iosocket_flush(iod); iod->dollar.x = 0; } socketptr->lastop = TCP_READ; ret = TRUE; has_delimiter = (0 < socketptr->n_delimiter); do_delim_conv = FALSE; if (has_delimiter && gtm_utf8_mode) { /* CHSET can be switched b/w M/UTF-8/UTF-16* in UTF-8 mode. Convert the delimitor accordingly * * 1. delimiter[] == idelimiter[] (i.e. it's not been converted) && IS_UTF16_CHSET * 2. delimiter[] != idelimiter (i.e. it's been converted to UTF-16) && CHSET is NOT UTF-16 */ if (((socketptr->delimiter[0].addr == socketptr->idelimiter[0].addr) && IS_UTF16_CHSET(ichset)) || ((socketptr->delimiter[0].addr != socketptr->idelimiter[0].addr) && !IS_UTF16_CHSET(ichset))) do_delim_conv = TRUE; } need_get_chset = FALSE; /* if we change ichset, make sure converter available */ time_for_read.at_sec = 0; if (0 == msec_timeout) time_for_read.at_usec = 0; else time_for_read.at_usec = (socketptr->def_moreread_timeout ? socketptr->moreread_timeout : INITIAL_MOREREAD_TIMEOUT) * MICROSECS_IN_MSEC; DBGSOCK((stdout, "socrfl: moreread_timeout = %d def_moreread_timeout= %d time = %d \n", socketptr->moreread_timeout, socketptr->def_moreread_timeout, time_for_read.at_usec)); timer_id = (TID)iosocket_readfl; out_of_time = FALSE; if (NO_M_TIMEOUT == msec_timeout) { timed = FALSE; assert(!sockintr->end_time_valid); } else { timed = TRUE; if (0 < msec_timeout) { /* There is time to wait */ /* Set blocking I/O */ FCNTL2(socketptr->sd, F_GETFL, flags); if (flags < 0) { iod->dollar.za = ZA_IO_ERR; save_errno = errno; errptr = (char *)STRERROR(errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_GETSOCKOPTERR, 5, LEN_AND_LIT("F_GETFL FOR NON BLOCKING I/O"), save_errno, LEN_AND_STR(errptr)); } FCNTL3(socketptr->sd, F_SETFL, flags & (~(O_NDELAY | O_NONBLOCK)), fcntl_res); if (fcntl_res < 0) { iod->dollar.za = ZA_IO_ERR; save_errno = errno; errptr = (char *)STRERROR(errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("F_SETFL FOR NON BLOCKING I/O"), save_errno, LEN_AND_STR(errptr)); } socketptr->nonblocking = FALSE; sys_get_curr_time(&cur_time); if (!sockintr->end_time_valid) add_int_to_abs_time(&cur_time, msec_timeout, &end_time); else { /* End_time taken from restart data. Compute what msec_timeout should be so timeout timer * gets set correctly below. */ end_time = sockintr->end_time; /* Restore end_time for timeout */ cur_time = sub_abs_time(&end_time, &cur_time); msec_timeout = (int4)(cur_time.at_sec * MILLISECS_IN_SEC + DIVIDE_ROUND_UP(cur_time.at_usec, MICROSECS_IN_MSEC)); if (0 >= msec_timeout) { msec_timeout = -1; out_of_time = TRUE; } DBGSOCK((stdout, "socrfl: Taking timeout end time from read restart data - " "computed msec_timeout: %d\n", msec_timeout)); } if (0 < msec_timeout) start_timer(timer_id, msec_timeout, wake_alarm, 0, NULL); } else { out_of_time = TRUE; DBGSOCK((stdout, "socrfl: No timeout specified, assuming out_of_time\n")); } } sockintr->end_time_valid = FALSE; iod->dollar.key[0] = '\0'; iod->dollar.zb[0] = '\0'; more_data = TRUE; real_errno = 0; requeue_done = FALSE; DBGSOCK((stdout, "socrfl: ##################### Entering read loop ######################\n")); for (status = 0; status >= 0;) { DBGSOCK2((stdout, "socrfl: ********* Top of read loop - bytes_read: %d chars_read: %d " "buffered_length: %d\n", bytes_read, chars_read, socketptr->buffered_length)); DBGSOCK2((stdout, "socrfl: ********* read-width: %d vari: %d status: %d\n", width, vari, status)); if (bytes_read >= max_bufflen) { /* More buffer needed. Extend the stringpool buffer by doubling the size as much as we * extended previously */ DBGSOCK((stdout, "socrfl: Buffer expand1 bytes_read(%d) max_bufflen(%d)\n", bytes_read, max_bufflen)); assert(vari); max_bufflen += max_bufflen; if (MAX_STRLEN < max_bufflen) max_bufflen = MAX_STRLEN; if (!IS_STP_SPACE_AVAILABLE(max_bufflen)) { v->str.len = bytes_read; /* to keep the data read so far from being garbage collected */ v->str.addr = (char *)stringpool.free; stringpool.free += v->str.len; /* BYPASSOK */ assert(stringpool.free <= stringpool.top); /* BYPASSOK */ INVOKE_STP_GCOL(max_bufflen); old_stringpool_free = stringpool.free; /* BYPASSOK */ assert(IS_AT_END_OF_STRINGPOOL(v->str.addr, v->str.len)); stringpool.free = (unsigned char *)v->str.addr; v->str.len = 0; /* If interrupted, don't hold onto old space */ TRCTBL_ENTRY(SOCKRFL_EXPBUFGC, bytes_read, stringpool.free, old_stringpool_free, /* BYPASSOK */ (UINTPTR_T)max_bufflen); } } if (has_delimiter || requeue_done || (socketptr->first_read && (CHSET_M != ichset))) { /* Delimiter scanning needs one char at a time. Question is how big is a char? * For the UTF character sets, we have a similar issue (with the same solution) in that * we need to make sure the entire BOM we may have is in the buffer. If the last read * caused chars to be requeued, we have to make sure that we read in one full character * (buffer likely contains only a partial char) in order to make forward progess. If we * didn't do this, we would just pull the partial char out of the buffer, notice its incompleteness * and requeue it again and again. Forcing a full char read lets us continue past this point. */ requeue_done = FALSE; /* housekeeping -- We don't want to come back here for this reason * until it happens again */ DBGSOCK_ONLY2(if (socketptr->first_read && (CHSET_M != ichset)) DBGSOCK((stdout, "socrfl: Prebuffering because ichset = UTF16\n")); else DBGSOCK((stdout, "socrfl: Prebuffering because we have delimiter or requeue\n"))); if (CHSET_M == iod->ichset) bufflen = 1; /* M mode, 1 char == 1 byte */ else { /* We need to make sure the read buffer contains a complete UTF character and to make sure * we know how long that character is so we can read it completely. The issue is that if we * read a partial utf character, the utf checking code below notices this and rebuffers it * so that it gets re-read on the next iteration. However, this then re-reads the same * partial character and we have a loop. We make a call here which takes a peek at the * first byte of the next character (and reads it in if necessary), determines how long the * character is and verifies that many characters are available in the buffer and returns the * character length to us to use for bufflen. */ charlen = (int)iosocket_snr_utf_prebuffer(iod, socketptr, 0, &time_for_read, (!vari || has_delimiter || 0 == chars_read)); DBGSOCK((stdout, "socrfl: charlen from iosocket_snr_utf_prebuffer = %d\n", charlen)); if (0 < charlen) { /* We know how long the next char is. If it fits in our buffer, then it is * the correct bufflen. If it won't, our buffer is full and we need to expand. */ if ((max_bufflen - bytes_read) < charlen) { DBGSOCK((stdout, "socrfl: Buffer expand2 - max_bufflen(%d) bytes_read(%d)\n", max_bufflen, bytes_read)); assert(vari); max_bufflen += max_bufflen; if (MAX_STRLEN < max_bufflen) max_bufflen = MAX_STRLEN; if (!IS_STP_SPACE_AVAILABLE(max_bufflen)) { v->str.len = bytes_read; /* To keep the data read so far from * being garbage collected */ v->str.addr = (char *)stringpool.free; stringpool.free += bytes_read; assert(stringpool.free <= stringpool.top); INVOKE_STP_GCOL(max_bufflen); old_stringpool_free = stringpool.free; assert(IS_AT_END_OF_STRINGPOOL(v->str.addr, v->str.len)); stringpool.free = (unsigned char *)v->str.addr; v->str.len = 0; /* If interrupted, don't hold onto old space */ TRCTBL_ENTRY(SOCKRFL_EXPBUFGC, bytes_read, stringpool.free, /* BYPASSOK */ old_stringpool_free, (UINTPTR_T)max_bufflen); } } bufflen = charlen; } else if (0 == charlen) { /* No data was available or there was a timeout. We set status to -3 here * so that we end up bypassing the call to iosocket_snr below which was to * do the actual IO. No need to repeat our lack of IO issue. */ DBGSOCK((stdout, "socrfl: Timeout or end of data stream\n")); status = -3; /* To differentiate from status=0 if prebuffer bypassed */ DEBUG_ONLY(bufflen = 0); /* Triggers assert in iosocket_snr if we end up there anyway */ } else { /* Something bad happened. Feed the error to the lower levels for proper handling */ DBGSOCK((stdout, "socrfl: Read error: status: %d errno: %d\n", charlen, errno)); status = charlen; DEBUG_ONLY(bufflen = 0); /* Triggers assert in iosocket_snr if we end up there anyway */ } } } else { bufflen = max_bufflen - bytes_read; DBGSOCK((stdout, "socrfl: Regular read .. bufflen = %d\n", bufflen)); status = 0; } buffptr = (stringpool.free + bytes_read); terminator = FALSE; if (0 <= status) /* An IO above in prebuffering may have failed in which case we do not want to reset status */ status = iosocket_snr(socketptr, buffptr, bufflen, 0, &time_for_read); else { DBGSOCK((stdout, "socrfl: Data read bypassed - status: %d\n", status)); } TRCTBL_ENTRY(SOCKRFL_RDSTATUS, status, (INTPTR_T)outofband, (INTPTR_T)out_of_time, (INTPTR_T)bytes_read); if (0 == status || -3 == status) /* -3 status can happen on EOB from prebuffering */ { DBGSOCK((stdout, "socrfl: No more data available\n")); more_data = FALSE; status = 0; /* Consistent treatment of no more data */ } else if (0 < status) { if (timed && !socketptr->def_moreread_timeout && !one_read_done) { one_read_done = TRUE; DBGSOCK((stdout, "socrfl: before moreread_timeout = %d timeout = %d \n", socketptr->moreread_timeout, time_for_read.at_usec)); time_for_read.at_usec = DEFAULT_MOREREAD_TIMEOUT * MICROSECS_IN_MSEC; DBGSOCK((stdout, "socrfl: after timeout = %d \n", time_for_read.at_usec)); } DBGSOCK((stdout, "socrfl: Bytes read: %d\n", status)); bytes_read += (int)status; if (iod == io_std_device.out) /* Not clear what purpose this serves */ prin_in_dev_failure = FALSE; /* In case the CHSET changes from non-UTF-16 to UTF-16 and a read has already been done, there's no way to * read the BOM bytes & to determine the variant. So default to UTF-16BE, if not already determined. */ if (!socketptr->first_read && ((CHSET_UTF16 == ichset) && (!IS_UTF16_CHSET(dsocketptr->ichset_utf16_variant)))) { DBGSOCK2((stdout, "socrfl: UTF16BE BOM assumed\n")); iod->ichset = ichset = dsocketptr->ichset_utf16_variant = CHSET_UTF16BE; need_get_chset = TRUE; } if (socketptr->first_read && (CHSET_M != ichset)) /* May have a BOM to defuse */ { if (CHSET_UTF8 != ichset) { /* When the type is UTF16xx, we need to check for a BOM at the beginning of the file. If * found it tells us which of UTF-16BE (default if no BOM) or UTF-16LE mode the data * is being written with. The call to iosocket_snr_utf_prebuffer() above should have made * sure that there were at least two chars available in the buffer if the char is a BOM. */ if (UTF16BE_BOM_LEN <= bytes_read) /* All UTF16xx BOM lengths are the same */ { if (0 == memcmp(buffptr, UTF16BE_BOM, UTF16BE_BOM_LEN)) { if (CHSET_UTF16LE == ichset) { iod->dollar.za = ZA_IO_ERR; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_BOMMISMATCH, 4, chset_names[CHSET_UTF16BE].len, chset_names[CHSET_UTF16BE].addr, chset_names[CHSET_UTF16LE].len, chset_names[CHSET_UTF16LE].addr); } else { iod->ichset = ichset = CHSET_UTF16BE; dsocketptr->ichset_utf16_variant = CHSET_UTF16BE; need_get_chset = TRUE; bytes_read -= UTF16BE_BOM_LEN; /* Throw way BOM */ DBGSOCK2((stdout, "socrfl: UTF16BE BOM detected\n")); } } else if (0 == memcmp(buffptr, UTF16LE_BOM, UTF16LE_BOM_LEN)) { if (CHSET_UTF16BE == ichset) { iod->dollar.za = ZA_IO_ERR; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_BOMMISMATCH, 4, chset_names[CHSET_UTF16LE].len, chset_names[CHSET_UTF16LE].addr, chset_names[CHSET_UTF16BE].len, chset_names[CHSET_UTF16BE].addr); } else { iod->ichset = ichset = CHSET_UTF16LE; dsocketptr->ichset_utf16_variant = CHSET_UTF16LE; need_get_chset = TRUE; bytes_read -= UTF16BE_BOM_LEN; /* Throw away BOM */ DBGSOCK2((stdout, "socrfl: UTF16LE BOM detected\n")); } } else { /* No BOM specified. If UTF16, default BOM to UTF16BE per standard */ if (CHSET_UTF16 == ichset) { DBGSOCK2((stdout, "socrfl: UTF16BE BOM assumed\n")); iod->ichset = ichset = CHSET_UTF16BE; dsocketptr->ichset_utf16_variant = CHSET_UTF16BE; need_get_chset = TRUE; } } } else { /* Insufficient characters to form a BOM so no BOM present. Like above, if in * UTF16 mode, default to UTF16BE per the standard. */ if (CHSET_UTF16 == ichset) { DBGSOCK2((stdout, "socrfl: UTF16BE BOM assumed\n")); iod->ichset = ichset = dsocketptr->ichset_utf16_variant = CHSET_UTF16BE; need_get_chset = TRUE; } } } else { /* Check for UTF8 BOM. If found, just eliminate it. */ if ((UTF8_BOM_LEN <= bytes_read) && (0 == memcmp(buffptr, UTF8_BOM, UTF8_BOM_LEN))) { bytes_read -= UTF8_BOM_LEN; /* Throw way BOM */ DBGSOCK2((stdout, "socrfl: UTF8 BOM detected/ignored\n")); } } socketptr->first_read = FALSE; } else if (socketptr->first_read) socketptr->first_read = FALSE; if (need_get_chset) { get_chset_desc(&chset_names[ichset]); need_get_chset = FALSE; } if (do_delim_conv) { iosocket_idelim_conv(socketptr, ichset); do_delim_conv = FALSE; } if (bytes_read && has_delimiter) { /* Check to see if it is a delimiter */ DBGSOCK((stdout, "socrfl: Searching for delimiter\n")); for (ii = 0; ii < socketptr->n_delimiter; ii++) { if (bytes_read < socketptr->idelimiter[ii].len) continue; if (0 == memcmp(socketptr->idelimiter[ii].addr, stringpool.free + bytes_read - socketptr->idelimiter[ii].len, socketptr->idelimiter[ii].len)) { terminator = TRUE; match_delim = ii; /* since idelimiter contains the converted value for UTF-16, $ZB and $KEY will have * a UTF-16 value instead of UTF-8 which is the chset used for strings. * Hence use delimiter instead of idelimiter. */ memcpy(iod->dollar.zb, socketptr->delimiter[ii].addr, MIN(socketptr->delimiter[ii].len, ESC_LEN - 1)); iod->dollar.zb[MIN(socketptr->delimiter[ii].len, ESC_LEN - 1)] = '\0'; memcpy(iod->dollar.key, socketptr->delimiter[ii].addr, MIN(socketptr->delimiter[ii].len, DD_BUFLEN - 1)); iod->dollar.key[MIN(socketptr->delimiter[ii].len, DD_BUFLEN - 1)] = '\0'; break; } } DBGSOCK_ONLY2( if (terminator) DBGSOCK((stdout, "socrfl: Delimiter found - match_delim: %d\n", match_delim)); else DBGSOCK((stdout, "socrfl: Delimiter not found\n")); ); } if (!terminator) more_data = TRUE; } else if ((EINTR == errno) && !out_of_time) /* Unrelated timer popped */ { status = 0; continue; } else { real_errno = errno; break; } if (bytes_read > MAX_STRLEN) { iod->dollar.za = ZA_IO_ERR; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_MAXSTRLEN); } orig_bytes_read = bytes_read; if (0 != bytes_read) { /* Find n chars read from [buffptr, buffptr + bytes_read) */ DBGSOCK((stdout, "socrfl: Start char scan - c_ptr: 0x"lvaddr" c_top: 0x"lvaddr"\n", buffptr, (buffptr + status))); for (c_ptr = buffptr, c_top = buffptr + status; c_ptr < c_top && chars_read < width; c_ptr += mb_len, chars_read++) { mb_len = 1; /* In case of CHSET_M */ if (!((CHSET_M == ichset) ? 1 : (CHSET_UTF8 == ichset) ? UTF8_VALID(c_ptr, c_top, mb_len) : (CHSET_UTF16BE == ichset) ? UTF16BE_VALID(c_ptr, c_top, mb_len) : UTF16LE_VALID(c_ptr, c_top, mb_len))) { /* This char is not valid utf character but this is only an error if entire char is * in the buffer. Else we ignore it and it is rebuffered further down. * First, we need to find its (real) length as xx_VALID set it to one when it * was determined to be invalid. */ mb_len = (CHSET_M == ichset) ? 0 : (CHSET_UTF8 == ichset) ? UTF8_MBFOLLOW(c_ptr) : (CHSET_UTF16BE == ichset) ? UTF16BE_MBFOLLOW(c_ptr, c_top) : UTF16LE_MBFOLLOW(c_ptr, c_top); mb_len++; /* Account for first byte of char */ if ((0 == mb_len) || ((c_ptr + mb_len) <= c_top)) { /* The entire char is in the buffer.. badchar */ # ifdef UTF8_SUPPORTED if (CHSET_UTF8 == ichset) { iosocket_readfl_badchar(v, (int)((unsigned char *)c_ptr - stringpool.free), 0, c_ptr, c_top); UTF8_BADCHAR(0, c_ptr, c_top, 0, NULL); } else /* UTF16LE or UTF16BE */ { inv_beg = c_ptr; if ((c_ptr += 2) >= c_top) c_ptr = c_top; iosocket_readfl_badchar(v, (int)((unsigned char *)inv_beg - stringpool.free), (int)(c_ptr - inv_beg), inv_beg, c_top); UTF8_BADCHAR((int)(c_ptr - inv_beg), inv_beg, c_top, chset_names[ichset].len, chset_names[ichset].addr); } # endif } } if ((c_ptr + mb_len) > c_top) /* Verify entire char is in buffer */ break; } DBGSOCK((stdout, "socrfl: End char scan - c_ptr: 0x"lvaddr" c_top: 0x"lvaddr"\n", c_ptr, c_top)); if (c_ptr < c_top) /* Width size READ completed OR partial last char, push back bytes into input buffer */ { iosocket_unsnr(socketptr, c_ptr, c_top - c_ptr); bytes_read -= (int)(c_top - c_ptr); /* We will be re-reading these bytes */ requeue_done = TRUE; /* Force single (full) char read next time through */ DBGSOCK((stdout, "socrfl: Requeue of %d bytes done - adjusted bytes_read: %d\n", (c_top - c_ptr), bytes_read)); } } if (terminator) { assert(0 < bytes_read); bytes_read -= socketptr->idelimiter[match_delim].len; c_ptr -= socketptr->idelimiter[match_delim].len; UTF8_ONLY(chars_read -= socketptr->idelimiter[match_delim].char_len); NON_UTF8_ONLY(chars_read = bytes_read); DBGSOCK((stdout, "socrfl: Terminator found - bytes_read reduced by %d bytes to %d\n", socketptr->idelimiter[match_delim].len, bytes_read)); DBGSOCK((stdout, "socrfl: .. c_ptr also reduced to 0x"lvaddr"\n", c_ptr)); } /* If we read as much as we needed or if the buffer was totally full (last char or 3 might be part of an * incomplete character than can never be completed in this buffer) or if variable length, no delim with * chars available and no more data or outofband or have data including a terminator, we are then done. Note * that we are explicitly not handling jobinterrupt outofband here because it is handled above where it needs * to be done to be able to cleanly requeue any input (before delimiter procesing). */ if ((chars_read >= width) || (MAX_STRLEN <= orig_bytes_read) || (vari && !has_delimiter && (0 != chars_read) && !more_data) || ((0 < status) && terminator)) break; if (0 != outofband) { outofband_terminate = TRUE; break; } if (timed) { if (0 < msec_timeout) { sys_get_curr_time(&cur_time); cur_time = sub_abs_time(&end_time, &cur_time); if (0 > cur_time.at_sec) { out_of_time = TRUE; cancel_timer(timer_id); DBGSOCK((stdout, "socrfl: Out of time detected and set\n")); break; } } else if (!more_data) break; } } if (EINTR == real_errno) status = 0; /* Don't treat a or timeout as an error */ if (timed) { if (0 < msec_timeout) { FCNTL3(socketptr->sd, F_SETFL, flags, fcntl_res); if (fcntl_res < 0) { iod->dollar.za = ZA_IO_ERR; save_errno = errno; errptr = (char *)STRERROR(errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("F_SETFL FOR RESTORING SOCKET OPTIONS"), save_errno, LEN_AND_STR(errptr)); } if ((O_NDELAY | O_NONBLOCK) & flags) socketptr->nonblocking = TRUE; if (out_of_time) { ret = FALSE; DBGSOCK((stdout, "socrfl: Out of time to be returned (1)\n")); } else cancel_timer(timer_id); } else if ((chars_read < width) && !(has_delimiter && terminator)) { ret = FALSE; DBGSOCK((stdout, "socrfl: Out of time to be returned (2)\n")); } } /* If we terminated due to outofband, set up restart info. We may or may not restart (any outofband is capable of * restart) but set it up for at least the more common reasons (^C and job interrupt). * * Some restart info is kept in our iodesc block, but the buffer address information is kept in an mv_stent so if * the stack is garbage collected during the interrupt we don't lose track of where our stuff is saved away. */ if (outofband_terminate) { DBGSOCK((stdout, "socrfl: outofband interrupt received (%d) -- queueing mv_stent for read intr\n", outofband)); PUSH_MV_STENT(MVST_ZINTDEV); mv_chain->mv_st_cont.mvs_zintdev.io_ptr = iod; mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)stringpool.free; mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = bytes_read; sockintr->who_saved = sockwhich_readfl; if ((0 < msec_timeout) && (NO_M_TIMEOUT != msec_timeout)) { sockintr->end_time = end_time; sockintr->end_time_valid = TRUE; cancel_timer(timer_id); /* Worry about timer if/when we come back */ } sockintr->max_bufflen = max_bufflen; sockintr->bytes_read = bytes_read; sockintr->chars_read = chars_read; dsocketptr->mupintr = TRUE; stringpool.free += bytes_read; /* Don't step on our parade in the interrupt */ socketus_interruptus++; DBGSOCK((stdout, "socrfl: .. mv_stent: bytes_read: %d chars_read: %d max_bufflen: %d " "interrupts: %d buffer_start: 0x"lvaddr"\n", bytes_read, chars_read, max_bufflen, socketus_interruptus, stringpool.free)); DBGSOCK_ONLY(if (sockintr->end_time_valid) DBGSOCK((stdout, "socrfl: .. endtime: %d/%d msec_timeout: %d\n", end_time.at_sec, end_time.at_usec, msec_timeout))); TRCTBL_ENTRY(SOCKRFL_OUTOFBAND, bytes_read, (INTPTR_T)chars_read, stringpool.free, NULL); /* BYPASSOK */ REVERT_GTMIO_CH(&iod->pair, ch_set); async_action(FALSE); assertpro(FALSE); /* Should *never* return from async_action */ return FALSE; /* For the compiler.. */ } if (0 < chars_read) { /* There's something to return. Note we do not assert anything using c_ptr as it is possible in a restarted * read with nearly immediate timeout to bypass setting c_ptr so its value is not dependable at this point. */ assert(0 <= bytes_read); v->str.len = bytes_read; v->str.addr = (char *)stringpool.free; UTF8_ONLY(v->str.char_len = chars_read); DBGSOCK((stdout, "socrfl: String to return bytelen: %d charlen: %d iod-width: %d wrap: %d\n", v->str.len, chars_read, iod->width, iod->wrap)); DBGSOCK((stdout, "socrfl: x: %d y: %d\n", iod->dollar.x, iod->dollar.y)); if (((iod->dollar.x += chars_read) >= iod->width) && iod->wrap) /* Note increment/assignment */ { iod->dollar.y += (iod->dollar.x / iod->width); if (0 != iod->length) iod->dollar.y %= iod->length; iod->dollar.x %= iod->width; } if ((CHSET_M != ichset) && (CHSET_UTF8 != ichset)) { DBGSOCK((stdout, "socrfl: Converting UTF16xx data back to UTF8 for internal use\n")); v->str.len = gtm_conv(chset_desc[ichset], chset_desc[CHSET_UTF8], &v->str, NULL, NULL); v->str.addr = (char *)stringpool.free; stringpool.free += v->str.len; } } else { v->str.len = 0; v->str.addr = iod->dollar.key; } if (status >= 0) { /* No real problems */ iod->dollar.zeof = FALSE; iod->dollar.za = 0; memcpy(iod->dollar.device, "0", SIZEOF("0")); } else { /* There's a significant problem */ DBGSOCK((stdout, "socrfl: Error handling triggered - status: %d\n", status)); if (0 == chars_read) iod->dollar.x = 0; iod->dollar.za = ZA_IO_ERR; # ifdef GTM_TLS if (socketptr->tlsenabled && (0 > real_errno)) { errptr = (char *)gtm_tls_get_error((gtm_tls_socket_t *)socketptr->tlssocket); real_errno = gtm_tls_errno(); } else /* TLS not enabled or system call error */ # endif errptr = (char *)STRERROR(real_errno); DBGSOCK((stdout, "socrfl: Error string: %.*s\n", errlen, errptr)); DBGSOCK((stdout, "socrfl: Error numbers: %d %d\n", status, real_errno)); SET_DOLLARDEVICE_ONECOMMA_ERRSTR(iod, errptr, errlen); ISSUE_NOPRINCIO_IF_NEEDED(iod, FALSE, !socketptr->ioerror); /* FALSE indicates READ */ if (socketptr->ioerror && (prin_in_dev_failure || (0 < iod->error_handler.len) || iod->dollar.zeof)) { /* no delay if permitting errors and we lost the device or have and EXCEPTION handler */ iod->dollar.zeof = TRUE; RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_IOEOF, 0, ERR_TEXT, 2, errlen, errptr); } iod->dollar.zeof = TRUE; } DBGSOCK_ONLY( if (!ret && out_of_time) { DBGSOCK((stdout, "socrfl: Returning from read due to timeout\n")); } else { DBGSOCK((stdout, "socrfl: Returning from read with success indicator set to %d\n", ret)); } ); REVERT_GTMIO_CH(&iod->pair, ch_set); return (ret); } fis-gtm-V7.0-005/sr_port/iosocket_snr.c0000755000032200000250000004071414342376331016722 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* iosocket_snr.c * description: * -- takes care of the buffering of the recv for the socket device (and possible tcp device) * parameters: * -- socketptr pointer to a socket device, where we get socket descriptor, buffer and offset in buffer * -- buffer pointer to the buffer where to return stuff * -- maxlength maximum number of bytes to get * -- flags flags to be passed to recv() * -- time_for_read pointer to the timeout structure used by select() * -- extra_status reports either a timeout or a lost-of-connection * return: * -- got some stuff to return return number of bytes received * -- got nothing and timed out return 0 * -- loss-of-connection return -2 * -- error condition return -1, with errno set * side note: * -- use our own buffer if the requested size is smaller than our own buffer size * -- use the caller's buffer if the requested size is bigger than our own buffer * control flow: * -- if we already have some leftover, use it and figure out how much is still needed * -- if there is still need to read, figure out whether to use our buffer or the passed-in buffer * -- select so that this operation is timed * -- if select returns positive, recv, otherwise, return timeout * -- if recv gets nothing, return lost-of-connection * -- if the device buffer is used, move it over to the return buffer and update the device buffer pointer */ #include "mdef.h" #include #include #include "gtm_stdio.h" #include "gtm_time.h" #include "gtm_inet.h" #include "gtm_string.h" #ifdef UNIX #include "gtm_fcntl.h" static int fcntl_res; #ifdef DEBUG #include /* for gettimeofday */ #endif #ifdef GTM_USE_POLL_FOR_SUBSECOND_SELECT #include #endif #endif #include "gtm_select.h" #include "eintr_wrappers.h" #include "gt_timer.h" #include "io.h" #include "iotimer.h" #include "stringpool.h" #include "iosocketdef.h" #include "min_max.h" #include "gtm_utf8.h" #include "have_crit.h" #include "deferred_events_queue.h" #ifdef GTM_TLS #include "gtm_tls.h" #endif /* MAX_SNR_IO is for read loop in iosocket_snr_utf_prebuffer(). It is possible for a series of interrupts (one * from each active region) to interfere with this read so be generous here. */ #define MAX_SNR_IO 50 #ifdef DEBUG /* Hold gettimeofday before and after select to debug AIX spin */ static struct timespec tsbefore, tsafter; #endif GBLREF bool out_of_time; GBLREF io_pair io_curr_device; GBLREF spdesc stringpool; GBLREF volatile int4 outofband; #ifdef GTM_TLS GBLREF gtm_tls_ctx_t *tls_ctx; #endif /* Local routine we aren't making static due to increased debugging difficult static routines make */ ssize_t iosocket_snr_io(socket_struct *socketptr, void *buffer, size_t maxlength, int flags, ABS_TIME *time_for_read); /* Select aNd Receive */ ssize_t iosocket_snr(socket_struct *socketptr, void *buffer, size_t maxlength, int flags, ABS_TIME *time_for_read) { int status; ssize_t bytesread, recvsize; void *recvbuff; DBGSOCK((stdout, "socsnr: socketptr: 0x"lvaddr" buffer: 0x"lvaddr" maxlength: %d, flags: %d\n", socketptr, buffer, maxlength, flags)); /* Use leftover from the previous read, if there is any */ assert(0 < maxlength); if (0 < socketptr->buffered_length) { DBGSOCK2((stdout, "socsnr: read from buffer - buffered_length: %d\n", socketptr->buffered_length)); bytesread = MIN(socketptr->buffered_length, maxlength); memcpy(buffer, (void *)(socketptr->buffer + socketptr->buffered_offset), bytesread); socketptr->buffered_offset += bytesread; socketptr->buffered_length -= bytesread; DBGSOCK2((stdout, "socsnr: after buffer read - buffered_offset: %d buffered_length: %d\n", socketptr->buffered_offset, socketptr->buffered_length)); return bytesread; } /* Decide on which buffer to use and the size of the recv */ if (socketptr->buffer_size > maxlength) { recvbuff = socketptr->buffer; recvsize = socketptr->buffer_size; } else { recvbuff = buffer; recvsize = maxlength; } VMS_ONLY(recvsize = MIN(recvsize, VMS_MAX_TCP_IO_SIZE)); DBGSOCK2((stdout, "socsnr: recvsize set to %d\n", recvsize)); /* Select and recv */ assert(0 == socketptr->buffered_length); socketptr->buffered_length = 0; bytesread = (int)iosocket_snr_io(socketptr, recvbuff, recvsize, flags, time_for_read); DBGSOCK2((stdout, "socsnr: bytes read from recv: %d timeout: %d\n", bytesread, out_of_time)); if (0 < bytesread) { /* Got something this time */ if (recvbuff == socketptr->buffer) { if (bytesread <= maxlength) memcpy(buffer, socketptr->buffer, bytesread); else { /* Got some stuff for future recv */ memcpy(buffer, socketptr->buffer, maxlength); socketptr->buffered_length = bytesread - maxlength; bytesread = socketptr->buffered_offset = maxlength; DBGSOCK2((stdout, "socsnr: Buffer updated post read - buffered_offset: %d " "buffered_length: %d\n", socketptr->buffered_offset, socketptr->buffered_length)); } } } return bytesread; } /* Do the IO dirty work. Note the return value can be from either select() or recv(). * This would be a static routine but that just makes it harder to debug. */ ssize_t iosocket_snr_io(socket_struct *socketptr, void *buffer, size_t maxlength, int flags, ABS_TIME *time_for_read) { int status, real_errno; ssize_t bytesread; boolean_t pollread; # ifdef GTM_USE_POLL_FOR_SUBSECOND_SELECT long poll_timeout; unsigned long poll_nfds; struct pollfd poll_fdlist[1]; # else fd_set tcp_fd, *readfds, *writefds; struct timeval lcl_time_for_read; # endif # ifdef GTM_TLS int tlspolldirection = 0; # endif DBGSOCK2((stdout, "socsnrio: Socket read request - socketptr: 0x"lvaddr" buffer: 0x"lvaddr" maxlength: %d flags: %d ", socketptr, buffer, maxlength, flags)); DBGSOCK2((stdout, "time_for_read->at_sec: %d usec: %d\n", time_for_read->at_sec, time_for_read->at_usec)); DEBUG_ONLY(clock_gettime(CLOCK_REALTIME, &tsbefore);) #ifndef GTM_USE_POLL_FOR_SUBSECOND_SELECT assertpro(FD_SETSIZE > socketptr->sd); FD_ZERO(&tcp_fd); #endif while (TRUE) { status = 0; # ifdef GTM_TLS if (socketptr->tlsenabled) { pollread = (tlspolldirection == GTMTLS_WANT_WRITE) ? FALSE : TRUE; status = gtm_tls_cachedbytes((gtm_tls_socket_t *)socketptr->tlssocket); } else # endif pollread = TRUE; if (0 == status) { /* if TLS cachedbytes available no need to poll */ # ifndef GTM_USE_POLL_FOR_SUBSECOND_SELECT FD_SET(socketptr->sd, &tcp_fd); assert(0 != FD_ISSET(socketptr->sd, &tcp_fd)); lcl_time_for_read.tv_sec = time_for_read->at_sec; lcl_time_for_read.tv_usec = (gtm_tv_usec_t)time_for_read->at_usec; if (pollread) { readfds = &tcp_fd; writefds = NULL; } else { writefds = &tcp_fd; readfds = NULL; } status = select(socketptr->sd + 1, readfds, writefds, NULL, &lcl_time_for_read); # else poll_fdlist[0].fd = socketptr->sd; poll_fdlist[0].events = pollread ? POLLIN : POLLOUT; poll_nfds = 1; poll_timeout = DIVIDE_ROUND_UP(time_for_read->at_usec, MICROSECS_IN_MSEC); /* convert to millisecs */ status = poll(&poll_fdlist[0], poll_nfds, poll_timeout); # endif real_errno = errno; DEBUG_ONLY(clock_gettime(CLOCK_REALTIME, &tsafter);) DBGSOCK2((stdout, "socsnrio: Select return code: %d :: errno: %d\n", status, real_errno)); } if (0 < status) { # ifdef GTM_TLS if (socketptr->tlsenabled) { bytesread = gtm_tls_recv((gtm_tls_socket_t *)socketptr->tlssocket, buffer, maxlength); DBGSOCK2((stdout, "socsnrio: gtm_tls_recv: %d :: errno: %d\n", bytesread, errno)); if (0 < bytesread) return bytesread; /* if want read or write, need to loop */ /* after setting tlspolldirection */ DBGSOCK2((stdout, "socsnrio: TLS errno %d - %s\n", gtm_tls_errno(), gtm_tls_get_error((gtm_tls_socket_t *)socketptr->tlssocket))); switch (bytesread) { case GTMTLS_WANT_READ: tlspolldirection = GTMTLS_WANT_READ; break; case GTMTLS_WANT_WRITE: tlspolldirection = GTMTLS_WANT_WRITE; break; default: socketptr->last_recv_errno = errno = gtm_tls_errno(); if (ECONNRESET == errno) { return (ssize_t)(-2); } else return (ssize_t)(-1); } continue; } else # endif { RECV(socketptr->sd, buffer, maxlength, flags, bytesread); real_errno = errno; socketptr->last_recv_errno = (-1 != status) ? 0 : real_errno; /* Save status for dbg purposes */ DBGSOCK2((stdout, "socsnrio: aa_recv return code: %d :: errno: %d\n", bytesread, errno)); if ((0 == bytesread) || ((-1 == bytesread) && ((ECONNRESET == real_errno) || (EPIPE == real_errno) || (EINVAL == real_errno)))) { /* Lost connection */ if (0 == bytesread) errno = ECONNRESET; return (ssize_t)(-2); } DBGSOCK_ONLY2(errno = real_errno); return bytesread; } } else break; /* nothing ready */ } DBGSOCK_ONLY2(errno = real_errno); return (ssize_t)status; } /* When scanning for delimiters, we have to make sure that the next read can pull in at least one full utf char. * Failure to do this means that if a partial utf8 char is read, it will be rebuffered, reread, rebuffered, forever. * A return code of zero indicates a timeout error occured. A negative return code indicates an IO error of some sort. * A positive return code is the length in bytes of the next utf char in the buffer. */ ssize_t iosocket_snr_utf_prebuffer(io_desc *iod, socket_struct *socketptr, int flags, ABS_TIME *time_for_read, boolean_t wait_for_input) { int mblen, bytesread, real_errno; ssize_t readlen; char *readptr; assert(CHSET_M != iod->ichset); DBGSOCK((stdout, "socsnrupb: Enter prebuffer: buffered_length: %d wait_for_input: %d\n", socketptr->buffered_length, wait_for_input)); /* See if there is *anything* in the buffer */ if (0 == socketptr->buffered_length) { /* Buffer is empty, read at least one char into it so we can check how many we need */ do { bytesread = (int)iosocket_snr_io(socketptr, socketptr->buffer, socketptr->buffer_size, flags, time_for_read); DBGSOCK_ONLY2(real_errno = errno); DBGSOCK2((stdout, "socsnrupb: Buffer empty - bytes read: %d errno: %d\n", bytesread, real_errno)); DBGSOCK_ONLY2(errno = real_errno); } while ((((-1 == bytesread) && (EINTR == errno)) || (0 == bytesread && wait_for_input)) && !out_of_time && (0 == outofband)); if (out_of_time || (0 != outofband)) { DBGSOCK_ONLY(if (out_of_time) { DBGSOCK((stdout, "socsnrupb: Returning due to timeout\n")); } else { DBGSOCK((stdout, "socsnrupb: Returning due to outofband\n")); } ); if (0 < bytesread) { /* If we read anything, be sure to consider it buffered */ socketptr->buffered_length = bytesread; socketptr->buffered_offset = 0; } return 0; } if (0 >= bytesread) { DBGSOCK_ONLY2(real_errno = errno); DBGSOCK2((stdout, "socsnrupb: Returning due to error code %d errno: %d\n", bytesread, real_errno)); DBGSOCK_ONLY2(errno = real_errno); return bytesread; } socketptr->buffered_length = bytesread; socketptr->buffered_offset = 0; } /* Compute number of bytes we need for the first char in the buffer */ readptr = socketptr->buffer + socketptr->buffered_offset; switch(iod->ichset) { case CHSET_UTF8: mblen = UTF8_MBFOLLOW(readptr); if (0 > mblen) mblen = 0; /* Invalid char, just assume one char needed */ break; case CHSET_UTF16BE: mblen = UTF16BE_MBFOLLOW(readptr, readptr + socketptr->buffered_length); if (0 > mblen) mblen = 1; /* If buffer is too small we will get -1 here. Assume need 2 chars */ break; case CHSET_UTF16LE: mblen = UTF16LE_MBFOLLOW(readptr, readptr + socketptr->buffered_length); if (0 > mblen) mblen = 1; /* If buffer is too small we will get -1 here. Assume need 2 chars */ break; case CHSET_UTF16: /* Special case as we don't know which mode we are in. This should only be used when * checking for BOMs. Check if first char is 0xFF or 0xFE. If it is, return 1 as our * (follow) length. If neither, assume UTF16BE (default UTF16 codeset) and return the * length it gives. */ if ((0xFF == (unsigned char)*readptr) || (0xFE == (unsigned char)*readptr)) mblen = 1; else { mblen = UTF16BE_MBFOLLOW(readptr, readptr + socketptr->buffered_length); if (0 > mblen) mblen = 1; /* If buffer is too small we will get -1 here. Assume need 2 chars */ } break; default: assertpro(iod->ichset != iod->ichset); } mblen++; /* Include first char we were looking at in the required byte length */ DBGSOCK2((stdout, "socsnrupb: Length of char: %d\n", mblen)); if (socketptr->buffered_length < mblen) { /* Still insufficient chars in the buffer for our utf character. Read some more in. */ if ((socketptr->buffered_offset + mblen) > socketptr->buffer_size) { /* Our char won't fit in the buffer. This can only occur if the read point is * right at the end of the buffer since the minimum buffer size is 32K. Solution * is to slide the part of the char that we have down to the beginning of the * buffer so we have plenty of room. Since this is at most 3 bytes, this is not * a major performance concern. */ DBGSOCK2((stdout, "socsnrupb: Char won't fit in buffer, slide it down\n")); assert(SIZEOF(int) > socketptr->buffered_length); assert(socketptr->buffered_offset > socketptr->buffered_length); /* Assert no overlap */ memcpy(socketptr->buffer, (socketptr->buffer + socketptr->buffered_offset), socketptr->buffered_length); socketptr->buffered_offset = 0; } while (socketptr->buffered_length < mblen) { DBGSOCK2((stdout, "socsnrupb: Top of read loop for char - buffered_length: %d\n", socketptr->buffered_length)); readptr = socketptr->buffer + socketptr->buffered_offset + socketptr->buffered_length; readlen = socketptr->buffer_size - socketptr->buffered_offset - socketptr->buffered_length; assert(0 < readlen); bytesread = (int)iosocket_snr_io(socketptr, readptr, readlen, flags, time_for_read); DBGSOCK2((stdout, "socsnrupb: Read %d chars\n", bytesread)); if (0 > bytesread) { /* Some error occurred. Check for restartable condition. */ if (EINTR == errno) if (!out_of_time) continue; else return 0; /* timeout indicator */ return bytesread; } if (out_of_time) return 0; socketptr->buffered_length += bytesread; } } DBGSOCK((stdout, "socsnrupb: Returning char length %d -- buffered_length: %d\n", mblen, socketptr->buffered_length)); return mblen; } /* Place len bytes pointed by buffer back into socketptr's internal buffer * * Side effect: suppose the last snr was with a length > internal buffer size, we would not have used the internal buffer. For * that case, unsnr might move data not in the internal buffer into the internal buffer and also might result in buffer * expansion */ void iosocket_unsnr(socket_struct *socketptr, unsigned char *buffer, size_t len) { char *new_buff; DBGSOCK((stdout, "iosunsnr: ** Requeueing %d bytes\n", len)); if ((socketptr->buffered_length + len) <= socketptr->buffer_size) { if (0 < socketptr->buffered_length) { if (socketptr->buffered_offset < len) { assert((len + socketptr->buffered_length) < socketptr->buffer_size); memmove(socketptr->buffer + len, socketptr->buffer + socketptr->buffered_offset, socketptr->buffered_length); memmove(socketptr->buffer, buffer, len); } else { memmove(socketptr->buffer, buffer, len); assert((len + socketptr->buffered_length) < socketptr->buffer_size); memmove(socketptr->buffer + len, socketptr->buffer + socketptr->buffered_offset, socketptr->buffered_length); } } else memmove(socketptr->buffer, buffer, len); } else { new_buff = malloc(socketptr->buffered_length + len); memcpy(new_buff, buffer, len); if (0 < socketptr->buffered_length) memcpy(new_buff + len, socketptr->buffer + socketptr->buffered_offset, socketptr->buffered_length); free(socketptr->buffer); socketptr->buffer = new_buff; } socketptr->buffered_offset = 0; socketptr->buffered_length += len; return; } fis-gtm-V7.0-005/sr_port/iosocket_sockopt.c0000644000032200000250000000535014342376331017574 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2022 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 #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_netdb.h" #include "gtm_ipv6.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "io.h" #include "iosocketdef.h" #include "gtmio.h" error_def(ERR_GETSOCKOPTERR); error_def(ERR_SETSOCKOPTERR); int iosocket_getsockopt(socket_struct *socketptr, char *optname, int option, int level, void *optvalue, GTM_SOCKLEN_TYPE *optvaluelen, boolean_t freesocket) { boolean_t trap; char *errptr, *sockopt_errptr; d_socket_struct *dsocketptr; int4 errlen, real_errno; ssize_t status; real_errno = 0; if (-1 == (status = getsockopt(socketptr->sd, level, option, optvalue, optvaluelen))) { real_errno = errno; dsocketptr = socketptr->dev; errptr = (char *)STRERROR(real_errno); errlen = STRLEN(errptr); trap = socketptr->ioerror; SET_DOLLARDEVICE_ONECOMMA_ERRSTR(dsocketptr->iod, errptr, errlen); if (freesocket || trap) { if (FD_INVALID != socketptr->sd) { close(socketptr->sd); /* Don't leave a dangling socket around */ socketptr->sd = FD_INVALID; } SOCKET_FREE(socketptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_GETSOCKOPTERR, 5, LEN_AND_STR(optname), real_errno, errlen, errptr); } return FALSE; } return TRUE; } int iosocket_setsockopt(socket_struct *socketptr, char *optname, int option, int level, void *optvalue, GTM_SOCKLEN_TYPE optvaluelen, boolean_t freesocket) { boolean_t trap; char *errptr, *sockopt_errptr; d_socket_struct *dsocketptr; int4 errlen, real_errno; ssize_t status; real_errno = 0; if (-1 == (status = setsockopt(socketptr->sd, level, option, optvalue, optvaluelen))) { real_errno = errno; dsocketptr = socketptr->dev; errptr = (char *)STRERROR(real_errno); errlen = STRLEN(errptr); trap = socketptr->ioerror; SET_DOLLARDEVICE_ONECOMMA_ERRSTR(dsocketptr->iod, errptr, errlen); if (freesocket || trap) { if (FD_INVALID != socketptr->sd) { close(socketptr->sd); /* Don't leave a dangling socket around */ socketptr->sd = FD_INVALID; } SOCKET_FREE(socketptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_STR(optname), real_errno, errlen, errptr); } return FALSE; } return TRUE; } fis-gtm-V7.0-005/sr_port/iosocket_switch.c0000755000032200000250000000372614342376331017423 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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. * * * ****************************************************************/ /* iosocket_switch.c */ #include "mdef.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "io.h" #include "iosp.h" #include "io_params.h" #include "gt_timer.h" #include "iosocketdef.h" GBLREF int4 gtm_max_sockets; error_def(ERR_SOCKNOTFND); error_def(ERR_SOCKETEXIST); error_def(ERR_SOCKMAX); boolean_t iosocket_switch(char *handle, int handle_len, d_socket_struct *from, d_socket_struct *to) { int4 index, ii; socket_struct *socketptr; if ((NULL == from) || (0 > (index = iosocket_handle(handle, &handle_len, FALSE, from)))) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKNOTFND, 2, handle_len, handle); return FALSE; } else { /* attach the socket to "to" and set it to current */ assert(NULL != to); if (0 <= iosocket_handle(handle, &handle_len, FALSE, to)) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKETEXIST, 2, handle_len, handle); return FALSE; } if (gtm_max_sockets <= to->n_socket) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SOCKMAX, 1, gtm_max_sockets); return FALSE; } socketptr = from->socket[index]; socketptr->dev = to; to->socket[to->n_socket++] = socketptr; to->current_socket = to->n_socket - 1; /* detach it from "from" */ if (from->current_socket >= index) from->current_socket--; for(ii = index; ii < from->n_socket - 1; ii++) { from->socket[ii] = from->socket[ii + 1]; } from->n_socket--; from->socket[from->n_socket] = NULL; } return TRUE; } fis-gtm-V7.0-005/sr_port/iosocket_tcp_keepalive.c0000644000032200000250000001635614342376331020735 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_netdb.h" #include "gtm_ipv6.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "io.h" #include "iosocketdef.h" #include "gtmio.h" error_def(ERR_SETSOCKOPTERR); /* iosocket_tcp_keepalive.c */ /* * inputs: socket pointer; the keepalive_opt value used for idle; the name of the calling action; a Boolean giving need for cleanup * returns: TRUE for success, FALSE for failure if ioerror=notrap otherwise rts_error on failures if freesocket * the callers use a positive non-zero value of keepalive_opt to specify keepalive; used here to specify TCP_KEEPIDLE * or to use values from the socket struct set via the OPTIONS device parameter if keepalive_opt is negative * TCP_KEEPCNT and TCP_KEEPINTVL can only be set via OPTIONS except as part of a white box case that sets a short time */ boolean_t iosocket_tcp_keepalive(socket_struct *socketptr, int keepalive_opt, char *act, boolean_t freesocket) { boolean_t trap; char *errptr, *sockopt_errptr; d_socket_struct *dsocketptr; int keepidle_got, keepalive_value, keepidle_value; int keepintvl_got, keepintvl_value, keepcnt_got, keepcnt_value; int4 errlen, real_errno; # ifdef DEBUG socklen_t keepidle_got_len, keepintvl_got_len, keepcnt_got_len; # endif ssize_t status; real_errno = 0; DEBUG_ONLY(keepidle_got_len = SIZEOF(keepidle_got);) if (0 > keepalive_opt) { /* SOCKOPTIONS_FROM_STRUCT so use values from socket struct */ if (SOCKOPTIONS_PENDING & socketptr->options_state.alive) { keepalive_value = socketptr->keepalive; socketptr->options_state.alive &= ~SOCKOPTIONS_PENDING; } else keepalive_value = -1; /* flag to skip setsockopt */ if (SOCKOPTIONS_PENDING & socketptr->options_state.idle) { keepidle_value = socketptr->keepidle; socketptr->options_state.idle &= ~SOCKOPTIONS_PENDING; } else keepidle_value = -1; /* flag to skip */ if (SOCKOPTIONS_PENDING & socketptr->options_state.cnt) { keepcnt_value = socketptr->keepcnt; socketptr->options_state.cnt &= ~SOCKOPTIONS_PENDING; } else keepcnt_value = -1; /* flag to skip */ if (SOCKOPTIONS_PENDING & socketptr->options_state.intvl) { keepintvl_value = socketptr->keepintvl; socketptr->options_state.intvl &= ~SOCKOPTIONS_PENDING; } else keepintvl_value = -1; /* flag to skip */ } else { keepalive_value = keepidle_value = keepalive_opt; /* use environment variable for both */ keepcnt_value = keepintvl_value = -1; /* flag to skip setsockopt */ } # ifdef DEBUG # ifndef DEBUG_SOCK if ((0 <= keepalive_value) && WBTEST_ENABLED(WBTEST_SOCKET_KEEPALIVE)) { /* skip if nothing to do with keepalive */ # endif flush_pio(); if (0 < keepalive_opt) printf("%s gtm_socket_keepalive_idle: %d\n", act, keepalive_opt); else /* if only KEEPALIVE in OPTIONS, idle may be -1 */ printf("%s USE :options=keepalive: %d, idle: %d\n", act, keepalive_value, keepidle_value); FFLUSH(stdout); printf("%s %ssetting SO_KEEPALIVE\n", act, (0 < keepalive_value ? "" : "un")); FFLUSH(stdout); # ifndef DEBUG_SOCK } # endif # endif if (0 <= keepalive_value) { if (-1 == (status = setsockopt(socketptr->sd, SOL_SOCKET, SO_KEEPALIVE, &keepalive_value, SIZEOF(keepalive_value)))) { real_errno = errno; sockopt_errptr = "SO_KEEPALIVE"; } } if ((0 == real_errno) && (0 <= keepidle_value)) { sockopt_errptr = "TCP_KEEPIDLE"; /* in case of error */ if (-1 == (status = setsockopt(socketptr->sd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle_value, SIZEOF(keepidle_value)))) real_errno = errno; # ifdef DEBUG else if (-1 == (status = getsockopt(socketptr->sd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle_got, &keepidle_got_len))) real_errno = errno; if (0 == status) { errptr = (char *)STRERROR(real_errno); assert(keepidle_got == keepidle_value); real_errno = 0; } # endif } if ((0 == real_errno) && (0 <= keepcnt_value)) { if (-1 == setsockopt(socketptr->sd, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt_value, SIZEOF(keepcnt_value))) { sockopt_errptr = "TCP_KEEPCNT"; real_errno = errno; } } if ((0 == real_errno) && (0 <= keepintvl_value)) { if (-1 == setsockopt(socketptr->sd, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl_value, SIZEOF(keepintvl_value))) { sockopt_errptr = "TCP_KEEPINTVL"; real_errno = errno; } } if (real_errno) { dsocketptr = socketptr->dev; errptr = (char *)STRERROR(real_errno); errlen = STRLEN(errptr); trap = socketptr->ioerror; SET_DOLLARDEVICE_ONECOMMA_ERRSTR(dsocketptr->iod, errptr, errlen); # ifdef DEBUG # ifndef DEBUG_SOCK if (WBTEST_ENABLED(WBTEST_SOCKET_KEEPALIVE)) { # endif flush_pio(); printf("fd: %d\n",socketptr->sd); FFLUSH(stdout); # ifndef DEBUG_SOCK } # endif # endif assert(FALSE); if (freesocket && trap) { if (FD_INVALID != socketptr->sd) { close(socketptr->sd); /* Don't leave a dangling socket around */ socketptr->sd = FD_INVALID; } SOCKET_FREE(socketptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_STR(sockopt_errptr), real_errno, errlen, errptr); } return FALSE; } # ifdef DEBUG if ((0 <= keepalive_opt) && WBTEST_ENABLED(WBTEST_SOCKET_KEEPALIVE)) { /* when implementing KEEPCNT and KEEPINTVL options, set to 2 to recognize missing peer in 6 seconds */ keepcnt_value = keepintvl_value = 2; /* for now force it in white box test */ flush_pio(); printf("%s setting TCP_KEEP options\n wb enabled: %d, wb #: %d. opt: %d\n", act, gtm_white_box_test_case_enabled, gtm_white_box_test_case_number, keepcnt_value); FFLUSH(stdout); keepcnt_got_len = SIZEOF(keepcnt_got); if (-1 == setsockopt(socketptr->sd, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt_value, SIZEOF(keepcnt_value))) { real_errno = errno; assert(FALSE); /* while a white box case, we can ignore errors */ } if (-1 == getsockopt(socketptr->sd, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt_got, &keepcnt_got_len)) { real_errno = errno; assert(FALSE); /* while a white box case, we can ignore errors */ } if (keepcnt_got != keepcnt_value) { flush_pio(); printf("cnt setopt: %d, getopt: %d\n", keepcnt_value, keepcnt_got); FFLUSH(stdout); } if (-1 == setsockopt(socketptr->sd, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl_value, SIZEOF(keepintvl_value))) { real_errno = errno; assert(FALSE); /* while a white box case, we can ignore errors */ } keepintvl_got_len = SIZEOF(keepintvl_got); if (-1 == getsockopt(socketptr->sd, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl_got, &keepintvl_got_len)) { real_errno = errno; assert(FALSE); /* while a white box case, we can ignore errors */ } if (keepintvl_got != keepintvl_value) { flush_pio(); printf("intvl setopt: %d, getopt: %d\n", keepintvl_value, keepintvl_got); FFLUSH(stdout); } } # endif return TRUE; } fis-gtm-V7.0-005/sr_port/iosocket_use.c0000644000032200000250000007504414342376331016715 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2013-2022 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. * * * ****************************************************************/ /* iosocket_use.c */ #include "mdef.h" #include "gtm_limits.h" #include "gtm_socket.h" #include "gtm_unistd.h" #include "gtm_iconv.h" #include "gtm_stdio.h" #include "gtm_string.h" #include #include "gtm_inet.h" #include "copy.h" #include "io.h" #include "io_params.h" #include "iotimer.h" #include "gt_timer.h" #include "iosocketdef.h" #include "nametabtyp.h" #include "namelook.h" #include "stringpool.h" #include "gtm_conv.h" #include "error.h" #include "op.h" #include "indir_enum.h" GBLREF boolean_t gtm_utf8_mode; GBLREF d_socket_struct *newdsocket, *socket_pool; GBLREF int4 gtm_max_sockets; GBLREF io_desc *active_device; GBLREF io_pair io_curr_device, io_std_device; GBLREF spdesc stringpool; GBLREF UConverter *chset_desc[]; GBLREF volatile boolean_t dollar_zininterrupt; LITREF nametabent filter_names[]; LITREF unsigned char filter_index[27]; LITREF unsigned char io_params_size[]; LITREF mstr chset_names[]; error_def(ERR_ABNCOMPTINC); error_def(ERR_ACOMPTBINC); error_def(ERR_ADDRTOOLONG); error_def(ERR_CHSETALREADY); error_def(ERR_ANCOMPTINC); error_def(ERR_CURRSOCKOFR); error_def(ERR_DELIMSIZNA); error_def(ERR_DELIMWIDTH); error_def(ERR_DEVPARMNEG); error_def(ERR_ILLESOCKBFSIZE); error_def(ERR_MRTMAXEXCEEDED); error_def(ERR_NOSOCKETINDEV); error_def(ERR_GETSOCKOPTERR); error_def(ERR_SETSOCKOPTERR); error_def(ERR_SOCKBFNOTEMPTY); error_def(ERR_SOCKNOTFND); error_def(ERR_SOCKMAX); error_def(ERR_TTINVFILTER); error_def(ERR_ZFF2MANY); error_def(ERR_ZINTRECURSEIO); void iosocket_use(io_desc *iod, mval *pp) { unsigned char ch, len; static char *conv_buff = NULL; int new_ozff_len, conv_len, handled_len, handlea_len, handles_len, int_len, soc_cnt; int4 length, width, new_len; d_socket_struct *dsocketptr; socket_struct *socketptr, *curr_socketptr = NULL, *localsocketptr; mstr_len_t delim_len; char handlea[MAX_HANDLE_LEN], handles[MAX_HANDLE_LEN], handled[MAX_HANDLE_LEN]; char addr[SA_MAXLITLEN], *errptr, sockaddr[SA_MAXLITLEN], temp_addr[SA_MAXLITLEN], ioerror, *free_ozff = NULL; unsigned char delimiter_buffer[MAX_N_DELIMITER * (MAX_DELIM_LEN + 1)]; unsigned char zff_buffer[MAX_ZFF_LEN], options_buffer[UCHAR_MAX + 1]; boolean_t attach_specified = FALSE, detach_specified = FALSE, connect_specified = FALSE, ioerror_specified = FALSE, listen_specified = FALSE, socket_specified = FALSE, delay_specified = FALSE, nodelay_specified = FALSE, bfsize_specified = FALSE, ochset_specified = FALSE, ichset_specified = FALSE, ibfsize_specified = FALSE, moreread_specified = FALSE, flush_specified = FALSE, create_new_socket; int4 index, n_specified, zff_len, delimiter_len, moreread_timeout, options_len; int4 n_specified_dev, n_specified_socket; int4 n_incomplete_dev; /* device level not done in iop loop */ int fil_type, nodelay, p_offset = 0, newbufsiz; GTM_SOCKLEN_TYPE sockbuflen; uint4 bfsize = DEFAULT_SOCKET_BUFFER_SIZE, ibfsize; char *tab; int save_errno; mstr chset_mstr, optionstr; gtm_chset_t temp_ochset, temp_ichset; size_t d_socket_struct_len; boolean_t ch_set; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(iod->state == dev_open); assert(iod->type == gtmsocket); dsocketptr = (d_socket_struct *)(iod->dev_sp); /* ---------------------------------- parse the command line ------------------------------------ */ n_specified = n_specified_dev = n_specified_socket = n_incomplete_dev = 0; zff_len = -1; /* indicates neither ZFF nor ZNOFF specified */ delimiter_len = -1; /* indicates neither DELIM nor NODELIM specified */ options_len = 0; /* no OPTIONS yet */ ESTABLISH_GTMIO_CH(&iod->pair, ch_set); /* A read or wait was interrupted for this device. Allow only parmless use in $zinterrupt code for * and interrupted device. */ if (iop_eol != *(pp->str.addr + p_offset)) { /* Parameters were specified */ if (dsocketptr->mupintr) { /* And if we are in $zinterrupt code this is not allowed */ if (dollar_zininterrupt) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZINTRECURSEIO); /* We are not in $zinterrupt code and this device was not resumed properly * so clear its restartability. */ io_find_mvstent(iod, TRUE); dsocketptr->mupintr = FALSE; } } else if (dsocketptr->mupintr && !dollar_zininterrupt) { /* The interrupted read was not properly resumed so clear it now */ dsocketptr->mupintr = FALSE; dsocketptr->sock_save_state.who_saved = sockwhich_invalid; io_find_mvstent(iod, TRUE); } while (iop_eol != (ch = *(pp->str.addr + p_offset++))) { assert((params)ch < (params)n_iops); switch (ch) { case iop_exception: n_specified_dev++; DEF_EXCEPTION(pp, p_offset, iod); break; case iop_filter: n_specified_dev++; len = *(pp->str.addr + p_offset); tab = pp->str.addr + p_offset + 1; if ((fil_type = namelook(filter_index, filter_names, tab, len)) < 0) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_TTINVFILTER); return; } switch (fil_type) { case 0: iod->write_filter |= CHAR_FILTER; break; case 1: iod->write_filter |= ESC1; break; case 2: iod->write_filter &= ~CHAR_FILTER; break; case 3: iod->write_filter &= ~ESC1; break; } break; case iop_nofilter: n_specified_dev++; iod->write_filter = 0; break; case iop_attach: n_specified++; attach_specified = TRUE; handlea_len = (int)(unsigned char)*(pp->str.addr + p_offset); memcpy(handlea, (char *)(pp->str.addr + p_offset + 1), handlea_len); break; case iop_detach: n_specified++; detach_specified = TRUE; handled_len = (int)(unsigned char)*(pp->str.addr + p_offset); memcpy(handled, (char *)(pp->str.addr + p_offset + 1), handled_len); break; case iop_connect: n_specified++; connect_specified = TRUE; int_len = (int)(unsigned char)*(pp->str.addr + p_offset); if (int_len < USR_SA_MAXLITLEN) { memcpy(sockaddr, (char *)(pp->str.addr + p_offset + 1), int_len); sockaddr[int_len] = '\0'; } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_ADDRTOOLONG, 4, int_len, pp->str.addr + p_offset + 1, int_len, USR_SA_MAXLITLEN); break; case iop_delimiter: n_specified++; delimiter_len = (int4)(unsigned char)*(pp->str.addr + p_offset); if (((MAX_DELIM_LEN + 1) * MAX_N_DELIMITER) >= delimiter_len) memcpy(delimiter_buffer, (pp->str.addr + p_offset + 1), delimiter_len); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DELIMSIZNA); break; case iop_nodelimiter: n_specified++; delimiter_len = 0; break; case iop_zdelay: n_specified_socket++; delay_specified = TRUE; break; case iop_znodelay: n_specified_socket++; nodelay_specified = TRUE; break; case iop_zbfsize: n_specified_socket++; bfsize_specified = TRUE; GET_ULONG(bfsize, pp->str.addr + p_offset); if ((0 == bfsize) || (MAX_SOCKET_BUFFER_SIZE < bfsize)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_ILLESOCKBFSIZE, 1, bfsize); break; case iop_zibfsize: n_specified_socket++; ibfsize_specified = TRUE; GET_ULONG(ibfsize, pp->str.addr + p_offset); if (0 == ibfsize) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_ILLESOCKBFSIZE, 1, ibfsize); break; case iop_ioerror: n_specified++; ioerror_specified = TRUE; ioerror = *(char *)(pp->str.addr + p_offset + 1); break; case iop_zlisten: n_specified++; listen_specified = TRUE; int_len = (int)(unsigned char)*(pp->str.addr + p_offset); if ((USR_SA_MAXLITLEN > int_len) && (0 < int_len)) { memcpy(sockaddr, (char *)(pp->str.addr + p_offset + 1), int_len); sockaddr[int_len] = '\0'; } else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_ADDRTOOLONG, 4, int_len, pp->str.addr + p_offset + 1, int_len, USR_SA_MAXLITLEN); break; case iop_socket: n_specified++; socket_specified = TRUE; handles_len = (int)(unsigned char)*(pp->str.addr + p_offset); memcpy(handles, (char *)(pp->str.addr + p_offset + 1), handles_len); break; case iop_ipchset: n_specified_dev++; # if defined(KEEP_zOS_EBCDIC) if ((iconv_t)0 != iod->input_conv_cd) ICONV_CLOSE_CD(iod->input_conv_cd); SET_CODE_SET(iod->in_code_set, (char *)(pp->str.addr + p_offset + 1)); if (DEFAULT_CODE_SET != iod->in_code_set) ICONV_OPEN_CD(iod->input_conv_cd, INSIDE_CH_SET, (char *)(pp->str.addr + p_offset + 1)); # endif GET_ADDR_AND_LEN(chset_mstr.addr, chset_mstr.len); SET_ENCODING(temp_ichset, &chset_mstr); if (!gtm_utf8_mode && IS_UTF_CHSET(temp_ichset)) break; /* ignore UTF chsets if not utf8_mode */ ichset_specified = TRUE; n_incomplete_dev++; break; case iop_opchset: n_specified_dev++; # if defined(KEEP_zOS_EBCDIC) if ((iconv_t)0 != iod->output_conv_cd) ICONV_CLOSE_CD(iod->output_conv_cd); SET_CODE_SET(iod->out_code_set, (char *)(pp->str.addr + p_offset + 1)); if (DEFAULT_CODE_SET != iod->out_code_set) ICONV_OPEN_CD(iod->output_conv_cd, (char *)(pp->str.addr + p_offset + 1), INSIDE_CH_SET); # endif GET_ADDR_AND_LEN(chset_mstr.addr, chset_mstr.len); SET_ENCODING(temp_ochset, &chset_mstr); if (!gtm_utf8_mode && IS_UTF_CHSET(temp_ochset)) break; /* ignore UTF chsets if not utf8_mode */ ochset_specified = TRUE; n_incomplete_dev++; break; case iop_chset: n_specified_dev++; GET_ADDR_AND_LEN(chset_mstr.addr, chset_mstr.len); SET_ENCODING(temp_ochset, &chset_mstr); SET_ENCODING(temp_ichset, &chset_mstr); if (!gtm_utf8_mode && IS_UTF_CHSET(temp_ichset)) break; /* ignore UTF chsets if not utf8_mode */ ochset_specified = TRUE; ichset_specified = TRUE; n_incomplete_dev++; break; case iop_zff: n_specified_socket++; zff_len = (int4)(unsigned char)*(pp->str.addr + p_offset); if (MAX_ZFF_LEN >= zff_len) memcpy(zff_buffer, (char *)(pp->str.addr + p_offset + 1), zff_len); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZFF2MANY, 2, zff_len, MAX_ZFF_LEN); break; case iop_znoff: n_specified_socket++; zff_len = 0; break; case iop_length: n_specified_dev++; GET_LONG(length, pp->str.addr + p_offset); if (length < 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DEVPARMNEG); iod->length = length; break; case iop_width: n_specified_dev++; /* SOCKET WIDTH is handled the same way as TERMINAL WIDTH */ GET_LONG(width, pp->str.addr + p_offset); if (width < 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DEVPARMNEG); if (0 == width) { iod->width = TCPDEF_WIDTH; iod->wrap = FALSE; } else { iod->width = width; iod->wrap = TRUE; } break; case iop_wrap: n_specified_dev++; iod->wrap = TRUE; break; case iop_nowrap: n_specified_dev++; iod->wrap = FALSE; break; case iop_morereadtime: n_specified_socket++; /* Time in milliseconds socket read will wait for more data before returning */ GET_LONG(moreread_timeout, pp->str.addr + p_offset); if (-1 == moreread_timeout) moreread_timeout = DEFAULT_MOREREAD_TIMEOUT; else if (-1 > moreread_timeout) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_DEVPARMNEG); else if (MAX_MOREREAD_TIMEOUT < moreread_timeout) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_MRTMAXEXCEEDED, 1, MAX_MOREREAD_TIMEOUT); moreread_specified = TRUE; break; case iop_flush: n_specified++; flush_specified = TRUE; break; case iop_options: n_specified++; options_len = (int4)(unsigned char)*(pp->str.addr + p_offset); if (UCHAR_MAX >= options_len) { memcpy(options_buffer, (unsigned char *)(pp->str.addr + p_offset + 1), options_len); options_buffer[options_len] = '\0'; } break; default: /* ignore deviceparm */ break; } p_offset += ((io_params_size[ch] == IOP_VAR_SIZE) ? (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); } /* ------ return immediately if no flag, worth a check because it is mostly true ------------ */ if (1 == p_offset) { REVERT_GTMIO_CH(&iod->pair, ch_set); return; } /* ------------------------------ compatibility verification -------------------------------- */ if ((socket_specified) && ((n_specified > 2) || ((2 == n_specified) && (0 >= delimiter_len)))) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ACOMPTBINC, 6, LEN_AND_LIT("SOCKET"), LEN_AND_LIT("DELIMITER"), LEN_AND_LIT("USE")); return; } if (connect_specified && listen_specified) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ABNCOMPTINC, 6, LEN_AND_LIT("CONNECT"), LEN_AND_LIT("ZLISTEN"), LEN_AND_LIT("USE")); return; } if ((ichset_specified || ochset_specified) && (listen_specified || connect_specified)) { /* CHSET cannot be specified when opening a new socket, if there already are open sockets. */ if (0 < dsocketptr->n_socket && ((temp_ochset != iod->ochset) || (temp_ichset != iod->ichset))) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_CHSETALREADY, 8, chset_names[iod->ichset].len, chset_names[iod->ichset].addr, chset_names[iod->ochset].len, chset_names[iod->ochset].addr); return; } } if (delay_specified && nodelay_specified) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ABNCOMPTINC, 6, LEN_AND_LIT("DELAY"), LEN_AND_LIT("NODELAY"), LEN_AND_LIT("OPEN")); return; } /* --------------- handle the three special cases socket/attach/detach first ----------------------- */ if (socket_specified) { /* use the socket flag to identify which socket to apply changes */ if (0 > (index = iosocket_handle(handles, &handles_len, FALSE, dsocketptr))) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKNOTFND, 2, handles_len, handles); return; } dsocketptr->current_socket = index; socketptr = dsocketptr->socket[index]; if ((1 == n_specified) && (0 == n_specified_socket) && (0 == n_incomplete_dev)) { /* other device level parameters already applied so ignore */ if (socket_listening == socketptr->state) { /* accept a new connection if there is one */ socketptr->pendingevent = socketptr->current_events = 0; iod->dollar.key[0] = '\0'; save_errno = iosocket_accept(dsocketptr, socketptr, TRUE); } /* return early since nothing else to do */ REVERT_GTMIO_CH(&iod->pair, ch_set); return; } } if (detach_specified) { if ((1 < n_specified) || (0 < n_specified_socket) || (0 < n_incomplete_dev)) { /* allow device level parameters already done */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_ANCOMPTINC, 4, LEN_AND_LIT("DETACH"), LEN_AND_LIT("USE")); return; } if (NULL == socket_pool) iosocket_poolinit(); iosocket_switch(handled, handled_len, dsocketptr, socket_pool); if (0 > dsocketptr->current_socket) { io_curr_device.in = io_std_device.in; io_curr_device.out = io_std_device.out; } REVERT_GTMIO_CH(&iod->pair, ch_set); return; /* detach can only be specified by itself */ } if (attach_specified) { /* NOTE: A socket could be moved from one device to another using DETACH/ATTACH. A socket does not carry I[O]CHSET * with it while being moved. Such a socket will use the I[O]CHSET of the device it is ATTACHed to. If there is * input still buffered, this may cause unintentional consequences in the application if I[O]CHSET changes. GT.M * does not detect (or report) a change in I[O]CHSET due to DETACH/ATTACH. */ if ((1 < n_specified) || (0 < n_specified_socket) || (0 < n_incomplete_dev)) { /* allow device level parameters already done */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_ANCOMPTINC, 4, LEN_AND_LIT("ATTACH"), LEN_AND_LIT("USE")); return; } if (NULL == socket_pool) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKNOTFND, 2, handlea_len, handlea); return; } iosocket_switch(handlea, handlea_len, socket_pool, dsocketptr); REVERT_GTMIO_CH(&iod->pair, ch_set); return; /* attach can only be specified by itself */ } /* ------------------ make a local copy of device structure to play with -------------------- */ d_socket_struct_len = SIZEOF(d_socket_struct) + (SIZEOF(socket_struct) * (gtm_max_sockets - 1)); memcpy(newdsocket, dsocketptr, d_socket_struct_len); /* ------------ create/identify the socket to work on and make a local copy ----------------- */ if (create_new_socket = (listen_specified || connect_specified)) /* real "=" */ { /* allocate the structure for a new socket */ if (NULL == (socketptr = iosocket_create(sockaddr, bfsize, -1, listen_specified))) { REVERT_GTMIO_CH(&iod->pair, ch_set); return; } if (gtm_max_sockets <= newdsocket->n_socket) { if (FD_INVALID != socketptr->temp_sd) close(socketptr->temp_sd); SOCKET_FREE(socketptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SOCKMAX, 1, gtm_max_sockets); return; } /* give the new socket a handle */ iosocket_handle(handles, &handles_len, TRUE, dsocketptr); socketptr->handle_len = handles_len; memcpy(socketptr->handle, handles, handles_len); socketptr->dev = newdsocket; /* use newdsocket temporarily for the sake of bind/connect */ socketptr->filemode_mask = 0; socketptr->uic.mem = (uid_t)-1; socketptr->uic.grp = (gid_t)-1; curr_socketptr = socketptr; } else { if (!socket_specified) { if (0 >= newdsocket->n_socket) { if (iod == io_std_device.out) ionl_use(iod, pp); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_NOSOCKETINDEV); REVERT_GTMIO_CH(&iod->pair, ch_set); return; } if (newdsocket->n_socket <= newdsocket->current_socket) { assert(FALSE); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CURRSOCKOFR, 2, newdsocket->current_socket, newdsocket->n_socket); return; } socketptr = newdsocket->socket[newdsocket->current_socket]; } /* Make a copy of the socket_struct so most errors leave the previous structure as is. * SOCKET_FREE should not be used on this copy since it frees other things such as buffers * DELIMITER is an exception due to how iosocket_delimiter manages storage. */ curr_socketptr = (socket_struct *)malloc(sizeof(socket_struct)); memcpy(curr_socketptr, socketptr, sizeof(socket_struct)); socketptr->temp_sd = FD_INVALID; } assert(NULL != curr_socketptr); /* ---------------------- apply changes to the local copy of the socket --------------------- */ if (0 <= delimiter_len) { /* note that previous delimiters are freed by the following */ iosocket_delimiter(delimiter_buffer, delimiter_len, curr_socketptr, (0 == delimiter_len)); if (curr_socketptr != socketptr) socketptr->n_delimiter = 0; /* prevent double frees or use of now freed memory if error */ } if (iod->wrap && (0 != curr_socketptr->n_delimiter) && (iod->width < curr_socketptr->delimiter[0].len)) { delim_len = curr_socketptr->delimiter[0].len; if (create_new_socket) SOCKET_FREE(socketptr) else { if (0 < delimiter_len) /* free new delimiters */ iosocket_delimiter((unsigned char *)NULL, 0, curr_socketptr, TRUE); free(curr_socketptr); /* other pointers copied from socketptr so just free this */ } RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_DELIMWIDTH, 2, iod->width, delim_len); } if (0 < options_len) { /* call devoptions w/ options_buffer to update socket struct */ optionstr.addr = (char *)options_buffer; optionstr.len = options_len; devoptions(NULL, curr_socketptr, &optionstr, "USE", IOP_USE_OK); } /* Process the CHSET changes */ if (ichset_specified) { CHECK_UTF16_VARIANT_AND_SET_CHSET_SOCKET(dsocketptr->ichset_utf16_variant, iod->ichset, temp_ichset, assert(!socketptr)); newdsocket->ichset_utf16_variant = dsocketptr->ichset_utf16_variant; newdsocket->ichset_specified = dsocketptr->ichset_specified = TRUE; } if (ochset_specified) { CHECK_UTF16_VARIANT_AND_SET_CHSET_SOCKET(dsocketptr->ochset_utf16_variant, iod->ochset, temp_ochset, assert(!socketptr)); newdsocket->ochset_utf16_variant = dsocketptr->ochset_utf16_variant; newdsocket->ochset_specified = dsocketptr->ochset_specified = TRUE; } if ((CHSET_M != iod->ichset) && (CHSET_UTF16 != iod->ichset) && (CHSET_MAX_IDX > iod->ichset)) get_chset_desc(&chset_names[iod->ichset]); if ((CHSET_M != iod->ochset) && (CHSET_UTF16 != iod->ochset) && (CHSET_MAX_IDX > iod->ichset)) get_chset_desc(&chset_names[iod->ochset]); if (0 <= zff_len && /* ZFF or ZNOFF specified */ 0 < (curr_socketptr->zff.len = zff_len)) /* assign the new ZFF len, might be 0 from ZNOFF, or ZFF="" */ { /* ZFF="non-zero-len-string" specified */ if (gtm_utf8_mode) /* Check if ZFF has any invalid UTF-8 character */ { /* Note: the ZFF string originates from the source program, so is in UTF-8 mode or M mode regardless of * OCHSET of this device. ZFF is output on WRITE # command, and MUST contain valid UTF-8 sequence. */ utf8_len_strict(zff_buffer, zff_len); } if ((NULL != curr_socketptr->ozff.addr) && (socketptr->ozff.addr != socketptr->zff.addr)) free_ozff = curr_socketptr->ozff.addr; /* previously converted */ if (NULL == curr_socketptr->zff.addr) /* we rely on curr_socketptr->zff.addr being set to 0 in iosocket_create() */ { socketptr->zff.addr = curr_socketptr->zff.addr = (char *)malloc(MAX_ZFF_LEN); socketptr->zff.len = zff_len; /* in case error so SOCKET_FREE frees */ } memcpy(curr_socketptr->zff.addr, zff_buffer, zff_len); curr_socketptr->ozff = curr_socketptr->zff; } else if (0 == zff_len) { if ((NULL != curr_socketptr->ozff.addr) && (socketptr->ozff.addr != socketptr->zff.addr)) free_ozff = curr_socketptr->ozff.addr; /* previously converted */ curr_socketptr->ozff = curr_socketptr->zff; } if (gtm_utf8_mode) { /* If CHSET is being changed to UTF-16, and delimitors are not converted, convert them * But only if the UTF16 variant has already been determined. * If CHSET is being changed to non-UTF-16, and delims are converted, free them */ if (ichset_specified) for (soc_cnt=0; soc_cnt < dsocketptr->n_socket; soc_cnt++) { localsocketptr = dsocketptr->socket[soc_cnt]; if (!(localsocketptr && (0 < localsocketptr->n_delimiter))) continue; if (((localsocketptr->delimiter[0].addr == localsocketptr->idelimiter[0].addr) && IS_UTF16_CHSET(iod->ichset) && IS_UTF16_CHSET(dsocketptr->ichset_utf16_variant)) || ((localsocketptr->delimiter[0].addr != localsocketptr->idelimiter[0].addr) && !IS_UTF16_CHSET(iod->ichset))) iosocket_idelim_conv(localsocketptr, iod->ichset); } if (ochset_specified) for (soc_cnt=0; soc_cnt < dsocketptr->n_socket; soc_cnt++) { localsocketptr = dsocketptr->socket[soc_cnt]; if (!(localsocketptr && (0 < localsocketptr->n_delimiter))) continue; if (((localsocketptr->delimiter[0].addr == localsocketptr->odelimiter0.addr) && IS_UTF16_CHSET(iod->ochset) && IS_UTF16_CHSET(dsocketptr->ochset_utf16_variant)) || ((localsocketptr->delimiter[0].addr != localsocketptr->odelimiter0.addr) && !IS_UTF16_CHSET(iod->ochset))) iosocket_odelim_conv(localsocketptr, iod->ochset); } if (ochset_specified) { /* Now convert the ZFFs */ if (!IS_UTF16_CHSET(iod->ochset)) { /* Changed to a non-UTF16 CHSET. free all converted ZFFs */ for (soc_cnt=0; soc_cnt < dsocketptr->n_socket; soc_cnt++) { localsocketptr = dsocketptr->socket[soc_cnt]; if (localsocketptr && (NULL != localsocketptr->ozff.addr) && (0 < localsocketptr->zff.len) && (localsocketptr->ozff.addr != localsocketptr->zff.addr)) { if (localsocketptr->ozff.addr == free_ozff) free_ozff = NULL; /* Prevent double free of free_ozff */ free(localsocketptr->ozff.addr); /* previously converted */ } localsocketptr->ozff = localsocketptr->zff; /* contains converted UTF-16 form */ } } else if (IS_UTF16_CHSET(dsocketptr->ochset_utf16_variant)) { /* Changed to UTF-16 CHSET. convert all ZFFs */ conv_buff = malloc(MAX_ZFF_LEN); for (soc_cnt=0; soc_cnt < dsocketptr->n_socket; soc_cnt++) { localsocketptr = dsocketptr->socket[soc_cnt]; if (localsocketptr && (NULL != localsocketptr->zff.addr) && (0 < localsocketptr->zff.len) && (localsocketptr->ozff.addr == localsocketptr->zff.addr)) { conv_len = MAX_ZFF_LEN; new_ozff_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[iod->ochset], &localsocketptr->zff, conv_buff, &conv_len); assert(MAX_ZFF_LEN > new_ozff_len); localsocketptr->ozff.len = new_ozff_len; localsocketptr->ozff.addr = malloc(new_ozff_len); memcpy(localsocketptr->ozff.addr, conv_buff, new_ozff_len); memset(conv_buff, 0, MAX_ZFF_LEN); /* Reset to be reused. */ } } } } } if (ioerror_specified) curr_socketptr->ioerror = ('T' == ioerror || 't' == ioerror); if (nodelay_specified || delay_specified) curr_socketptr->nodelay = nodelay_specified; /* defaults to DELAY */ if (ibfsize_specified) curr_socketptr->bufsiz = ibfsize; if (moreread_specified) { curr_socketptr->moreread_timeout = moreread_timeout; curr_socketptr->def_moreread_timeout = TRUE; /* need to know this was user-defined in iosocket_readfl.c */ } if (!create_new_socket) { /* these changes apply to only pre-existing sockets */ if (flush_specified) iosocket_flush(iod); /* buffered output if any */ if (bfsize_specified) curr_socketptr->buffer_size = bfsize; # ifdef TCP_NODELAY if (socket_local != curr_socketptr->protocol) { nodelay = curr_socketptr->nodelay ? 1 : 0; if ((socketptr->nodelay != curr_socketptr->nodelay) && (-1 == setsockopt(curr_socketptr->sd, IPPROTO_TCP, TCP_NODELAY, &nodelay, SIZEOF(nodelay)))) { save_errno = errno; errptr = (char *)STRERROR(save_errno); assert(curr_socketptr != socketptr); /* since not create new socket */ if (0 < delimiter_len) /* new delimiters */ iosocket_delimiter((unsigned char *)NULL, 0, curr_socketptr, TRUE); free(curr_socketptr); /* SOCKET_FREE would free storage used by existing socket */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("TCP_NODELAY"), save_errno, LEN_AND_STR(errptr)); return; } } # endif if (socketptr->bufsiz != curr_socketptr->bufsiz) { if (-1 == setsockopt(curr_socketptr->sd, SOL_SOCKET, SO_RCVBUF, &curr_socketptr->bufsiz, SIZEOF(curr_socketptr->bufsiz))) { save_errno = errno; errptr = (char *)STRERROR(save_errno); assert(curr_socketptr != socketptr); /* since not create new socket */ if (0 < delimiter_len) /* new delimiters */ iosocket_delimiter((unsigned char *)NULL, 0, curr_socketptr, TRUE); free(curr_socketptr); /* SOCKET_FREE would free storage used by existing socket */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("SO_RCVBUF"), save_errno, LEN_AND_STR(errptr)); return; } curr_socketptr->options_state.rcvbuf |= SOCKOPTIONS_USER; } if (0 != (SOCKOPTIONS_PENDING & curr_socketptr->options_state.sndbuf)) { if (-1 == iosocket_setsockopt(curr_socketptr, "SO_SNDBUF", SO_SNDBUF, SOL_SOCKET, &curr_socketptr->iobfsize, sizeof(curr_socketptr->iobfsize), TRUE)) { assert(curr_socketptr != socketptr); /* since not create new socket */ if (0 < delimiter_len) /* new delimiters */ iosocket_delimiter((unsigned char *)NULL, 0, curr_socketptr, TRUE); free(curr_socketptr); /* SOCKET_FREE would free storage used by existing socket */ return; } curr_socketptr->options_state.sndbuf |= SOCKOPTIONS_USER; curr_socketptr->options_state.sndbuf &= ~SOCKOPTIONS_PENDING; } if ((0 < options_len) && ((SOCKOPTIONS_PENDING & curr_socketptr->options_state.alive) || (SOCKOPTIONS_PENDING & curr_socketptr->options_state.cnt) || (SOCKOPTIONS_PENDING & curr_socketptr->options_state.intvl))) { /* options specified and pending keepalive related value to apply */ if (!iosocket_tcp_keepalive(curr_socketptr, SOCKOPTIONS_FROM_STRUCT, "USE", TRUE)) { assert(curr_socketptr != socketptr); /* since not create new socket */ if (0 < delimiter_len) /* new delimiters */ iosocket_delimiter((unsigned char *)NULL, 0, curr_socketptr, TRUE); free(curr_socketptr); /* SOCKET_FREE would free storage used by existing socket */ return; } } if (socketptr->buffer_size != curr_socketptr->buffer_size) { if (socketptr->buffered_length > bfsize) { assert(curr_socketptr != socketptr); /* since not create new socket */ if (0 < delimiter_len) /* new delimiters */ iosocket_delimiter((unsigned char *)NULL, 0, curr_socketptr, TRUE); free(curr_socketptr); /* SOCKET_FREE would free storage used by existing socket */ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_SOCKBFNOTEMPTY, 2, bfsize, socketptr->buffered_length); } curr_socketptr->buffer = (char *)malloc(bfsize); if (0 < socketptr->buffered_length) { assert(curr_socketptr != socketptr); /* since not create new socket */ memcpy(curr_socketptr->buffer, socketptr->buffer + socketptr->buffered_offset, socketptr->buffered_length); curr_socketptr->buffered_offset = 0; } } } /* -------------------------------------- action -------------------------------------------- */ if ((listen_specified && ((!iosocket_bind(curr_socketptr, NO_M_TIMEOUT, ibfsize_specified, FALSE)) || (!iosocket_listen_sock(curr_socketptr, DEFAULT_LISTEN_DEPTH)))) || (connect_specified && (!iosocket_connect(curr_socketptr, 0, ibfsize_specified)))) { /* error message should be printed from bind/connect and SOCKET_FREE */ assert(curr_socketptr == socketptr); /* since create new socket */ REVERT_GTMIO_CH(&iod->pair, ch_set); return; } /* ------------------------------------ commit changes -------------------------------------- */ if (create_new_socket) { assert(curr_socketptr == socketptr); /* since create new socket */ /* a new socket is created. so add to the list */ curr_socketptr->dev = dsocketptr; newdsocket->socket[newdsocket->n_socket++] = socketptr; newdsocket->current_socket = newdsocket->n_socket - 1; } else { if (NULL != free_ozff) free(free_ozff); if (socketptr->buffer_size != curr_socketptr->buffer_size) free(socketptr->buffer); /* no need to free socketptr delimiters since already done if new ones specified */ memcpy(socketptr, curr_socketptr, sizeof(socket_struct)); free(curr_socketptr); /* SOCKET_FREE would free storage used by existing socket */ } memcpy(dsocketptr, newdsocket, d_socket_struct_len); REVERT_GTMIO_CH(&iod->pair, ch_set); return; } fis-gtm-V7.0-005/sr_port/iosocket_wait.c0000644000032200000250000007121114342376333017057 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* iosocket_wait.c * * return a listening socket -- create a new socket for the connection and set it to current * set it to current * set $KEY to "CONNECT" * return a connected socket -- set it to current * set $KEY to "READ" * timeout -- set $Test to 1 */ #include "mdef.h" #include #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_unistd.h" #ifdef USE_POLL #include #endif #ifdef USE_SELECT #include "gtm_select.h" #endif #ifdef DEBUG_SOCKWAIT #include "gtmio.h" #include "have_crit.h" /* DBGSOCKWAIT needs for DBGFPF */ #endif #include "io_params.h" #include "gt_timer.h" #include "io.h" #include "iotimer.h" #include "iosocketdef.h" #include "min_max.h" #include "deferred_events_queue.h" #include #include "stack_frame.h" #include "mv_stent.h" #include "gtm_netdb.h" #include "gtm_stdlib.h" #include "eintr_wrappers.h" #include "error.h" #include "gtm_caseconv.h" #define CONNECTED "CONNECT" #define READ "READ" #define WRITE "WRITE" #define WAIT_FOR_READ 1 /* or incoming connection */ #define WAIT_FOR_WRITE 2 #define WAIT_FOR_ISDEFAULT 4 #define WAIT_FOR_DEFAULT (WAIT_FOR_READ | WAIT_FOR_WRITE | WAIT_FOR_ISDEFAULT) GBLREF int dollar_truth, socketus_interruptus; GBLREF int4 gtm_max_sockets; GBLREF mv_stent *mv_chain; GBLREF stack_frame *frame_pointer; GBLREF unsigned char *stackbase, *stacktop, *msp, *stackwarn; GBLREF volatile boolean_t dollar_zininterrupt; GBLREF volatile int4 outofband; error_def(ERR_GETNAMEINFO); error_def(ERR_GETSOCKNAMERR); error_def(ERR_GETSOCKOPTERR); error_def(ERR_SOCKACPT); error_def(ERR_SOCKNOTFND); error_def(ERR_SOCKWAIT); error_def(ERR_SOCKWAITARG); error_def(ERR_TEXT); error_def(ERR_SOCKMAX); error_def(ERR_ZINTRECURSEIO); boolean_t iosocket_wait(io_desc *iod, int4 msec_timeout, mval *whatop, mval *handle) { struct timeval utimeout, *utimeoutptr; ABS_TIME cur_time, end_time; #ifdef USE_POLL nfds_t poll_nfds; struct pollfd *poll_fds; socket_struct **poll_socketptr; /* matching poll_fds */ size_t poll_fds_size; int poll_timeout, poll_fd; #endif #ifdef USE_SELECT int select_max_fd; fd_set select_fdset, selectw_fdset; boolean_t selectw_needed; #endif d_socket_struct *dsocketptr; socket_struct *socketptr, *which_socketptr = NULL, *prev_socketptr;; socket_interrupt *sockintr; char *errptr, *charptr; int4 errlen, ii, jj, handle_index; int4 nselect, nlisten, nconnected, nwrite, rlisten, rconnected, rwrite; int4 oldestconnectedcycle, oldestconnectedindex; int4 oldestwritecycle, oldestwriteindex; int4 oldestlistencycle, oldestlistenindex; int4 oldesteventcycle, oldesteventindex; int rv, max_fd, len, len1; boolean_t zint_restart, retry_accept = FALSE; mv_stent *mv_zintdev; int errcode; int wait_for_what = 0; /* bit mask */ char wait_for_string[MAX_DEVCTL_LENGTH + 1]; boolean_t ch_set; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* check for validity */ assert(gtmsocket == iod->type); dsocketptr = (d_socket_struct *)iod->dev_sp; sockintr = &dsocketptr->sock_save_state; ESTABLISH_RET_GTMIO_CH(&iod->pair, FALSE, ch_set); /* Check for restart */ if (!dsocketptr->mupintr) { /* Simple path, no worries*/ zint_restart = FALSE; if (NULL == whatop) wait_for_what = WAIT_FOR_DEFAULT; else { MV_FORCE_STR(whatop); assert(sizeof(wait_for_string) > whatop->str.len); lower_to_upper((uchar_ptr_t)wait_for_string, (uchar_ptr_t)whatop->str.addr, MIN((sizeof(wait_for_string) - 1), whatop->str.len)); wait_for_string[whatop->str.len] = '\0'; if (strstr(wait_for_string, READ)) wait_for_what |= WAIT_FOR_READ; if (strstr(wait_for_string, WRITE)) wait_for_what |= WAIT_FOR_WRITE; if (0 == wait_for_what) { /* no valid value found */ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_SOCKWAITARG, 4, RTS_ERROR_LITERAL("Second"), RTS_ERROR_LITERAL("value is not valid")); return FALSE; } } if (NULL != handle) { MV_FORCE_STR(handle); /* WARNING inline assignment below */ if (0 > (handle_index = iosocket_handle(handle->str.addr, &handle->str.len, FALSE, dsocketptr))) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_SOCKNOTFND, 2, handle->str.len, handle->str.addr); return FALSE; /* for compiler and analyzers */ } which_socketptr = dsocketptr->socket[handle_index]; } dsocketptr->waitcycle++; /* don't count restarts */ if (0 == dsocketptr->waitcycle) { /* wrapped so make it non zero */ dsocketptr->waitcycle++; } } else { /* We have a pending wait restart of some sort - check we aren't recursing on this device */ assertpro(sockwhich_invalid != sockintr->who_saved); /* Interrupt should never have an invalid save state */ if (dollar_zininterrupt) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZINTRECURSEIO); assertpro(sockwhich_wait == sockintr->who_saved); /* ZINTRECURSEIO should have caught */ DBGSOCK((stdout, "socwait: *#*#*#*#*#*#*# Restarted interrupted wait\n")); mv_zintdev = io_find_mvstent(iod, FALSE); if (mv_zintdev) { if (sockintr->end_time_valid) /* Restore end_time for timeout */ end_time = sockintr->end_time; which_socketptr = (socket_struct *)mv_zintdev->mv_st_cont.mvs_zintdev.socketptr; wait_for_what = sockintr->wait_for_what; /* Done with this mv_stent. Pop it off if we can, else mark it inactive. */ if (mv_chain == mv_zintdev) POP_MV_STENT(); /* pop if top of stack */ else { /* else mark it unused */ mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; mv_zintdev->mv_st_cont.mvs_zintdev.io_ptr = NULL; } zint_restart = TRUE; DBGSOCK((stdout, "socwait: mv_stent found - endtime: %d/%d\n", end_time.at_sec, end_time.at_usec)); } else DBGSOCK((stdout, "socwait: no mv_stent found !!\n")); dsocketptr->mupintr = FALSE; sockintr->who_saved = sockwhich_invalid; } /* check for events */ #ifdef USE_POLL poll_fds_size = dsocketptr->n_socket * (SIZEOF(struct pollfd) + SIZEOF(socket_struct *)); if (NULL == TREF(poll_fds_buffer)) { TREF(poll_fds_buffer) = malloc(poll_fds_size); TREF(poll_fds_buffer_size) = poll_fds_size; } else if (poll_fds_size > TREF(poll_fds_buffer_size)) { free(TREF(poll_fds_buffer)); TREF(poll_fds_buffer) = malloc(poll_fds_size); TREF(poll_fds_buffer_size) = poll_fds_size; } poll_fds = (struct pollfd *) TREF(poll_fds_buffer); poll_socketptr = (socket_struct **)((char *)poll_fds + (dsocketptr->n_socket * SIZEOF(struct pollfd))); #endif #ifdef USE_SELECT FD_ZERO(&select_fdset); FD_ZERO(&selectw_fdset); selectw_needed = FALSE; #endif DBGSOCKWAIT((stdout,"waitcycle= %d\n",dsocketptr->waitcycle)); while (TRUE) { DBGSOCKWAIT((stdout,"wait loop:\n")); POLL_ONLY(poll_nfds = 0); SELECT_ONLY(select_max_fd = 0); nselect = nlisten = nconnected = nwrite = rlisten = rconnected = rwrite = 0; rv = 0; for (ii = 0; ii < dsocketptr->n_socket; ii++) { if (which_socketptr) socketptr = which_socketptr; else socketptr = dsocketptr->socket[ii]; socketptr->current_events = 0; if ((socket_listening == socketptr->state) || (socket_connected == socketptr->state)) { if (socket_connected == socketptr->state) { /* if buffer not empty set flag but not FD_SET */ nconnected++; /* increment even if not in whatop */ if ((0 < socketptr->buffered_length) && (WAIT_FOR_READ & wait_for_what)) { /* something in the buffer so ready now */ if (!(SOCKPEND_READ & socketptr->pendingevent)) { socketptr->current_events |= SOCKPEND_BUFFER | SOCKPEND_READ; DBGSOCKWAIT((stdout,"socket[%d] buffer, priorreadycycle= %d, pending= %d," " current= %d\n", ii, socketptr->readycycle, socketptr->pendingevent, socketptr->current_events)); socketptr->readycycle = dsocketptr->waitcycle; } socketptr->readyforwhat |= SOCKREADY_READ; rconnected++; if (!socketptr->nonblocked_output) continue; /* no need to check if writable */ } } else if (WAIT_FOR_READ & wait_for_what) { /* increment n... even if not in whatop */ nlisten++; if (SOCKPEND_READ & socketptr->pendingevent) { rlisten++; continue; /* ready for ACCEPT now */ } } #ifdef USE_POLL poll_fds[poll_nfds].fd = socketptr->sd; poll_fds[poll_nfds].events = 0; if (WAIT_FOR_READ & wait_for_what) poll_fds[poll_nfds].events = POLLIN; if ((socket_connected == socketptr->state) && socketptr->nonblocked_output && (WAIT_FOR_WRITE & wait_for_what)) poll_fds[poll_nfds].events |= POLLOUT; poll_socketptr[poll_nfds] = socketptr; poll_nfds++; #endif #ifdef USE_SELECT assertpro(FD_SETSIZE > socketptr->sd); if (WAIT_FOR_READ & wait_for_what) FD_SET(socketptr->sd, &select_fdset); if ((socket_connected == socketptr->state) && socketptr->nonblocked_output && (WAIT_FOR_WRITE & wait_for_what)) { FD_SET(socketptr->sd, &selectw_fdset); selectw_needed = TRUE; } select_max_fd = MAX(select_max_fd, socketptr->sd); #endif nselect++; } if (which_socketptr) break; /* only check the one socket */ } if (nselect) { if (NO_M_TIMEOUT != msec_timeout) { utimeout.tv_sec = msec_timeout / MILLISECS_IN_SEC; utimeout.tv_usec = (msec_timeout % MILLISECS_IN_SEC) * MICROSECS_IN_MSEC; sys_get_curr_time(&cur_time); if (!retry_accept && (!zint_restart || !sockintr->end_time_valid)) add_int_to_abs_time(&cur_time, msec_timeout, &end_time); else { /* end_time taken from restart data. Compute what msec_timeout should be so timeout timer gets set correctly below. Or retry after failed accept. */ DBGSOCK((stdout, "socwait: Taking timeout end time from wait restart data\n")); cur_time = sub_abs_time(&end_time, &cur_time); msec_timeout = (int4)(cur_time.at_sec * MILLISECS_IN_SEC + /* Round up in order to prevent premature timeouts */ DIVIDE_ROUND_UP(cur_time.at_usec, MICROSECS_IN_MSEC)); if (0 > msec_timeout) { msec_timeout = -1; utimeout.tv_sec = 0; utimeout.tv_usec = 0; } else { utimeout.tv_sec = cur_time.at_sec; utimeout.tv_usec = (gtm_tv_usec_t)cur_time.at_usec; } } } zint_restart = sockintr->end_time_valid = FALSE; for ( ; ; ) { #ifdef USE_POLL if ((0 < rconnected) || (0 < rlisten) || (0 < rwrite)) poll_timeout = 0; else if (NO_M_TIMEOUT == msec_timeout) poll_timeout = -1; else poll_timeout = (utimeout.tv_sec * MILLISECS_IN_SEC) + DIVIDE_ROUND_UP(utimeout.tv_usec, MICROSECS_IN_MSEC); poll_fd = -1; rv = poll(poll_fds, poll_nfds, poll_timeout); #endif #ifdef USE_SELECT utimeoutptr = &utimeout; if ((0 < rconnected) || (0 < rlisten) || (0 < rwrite)) utimeout.tv_sec = utimeout.tv_usec = 0; else if (NO_M_TIMEOUT == msec_timeout) utimeoutptr = (struct timeval *)NULL; rv = select(select_max_fd + 1, (void *)&select_fdset, selectw_needed ? (void *)&selectw_fdset : NULL, NULL, utimeoutptr); #endif if (0 > rv && EINTR == errno) { if (0 != outofband) { if (OUTOFBAND_RESTARTABLE(outofband)) { DBGSOCK((stdout, "socwait: outofband interrupt received (%d) -- " "queueing mv_stent for wait intr\n", outofband)); PUSH_MV_STENT(MVST_ZINTDEV); mv_chain->mv_st_cont.mvs_zintdev.io_ptr = iod; mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; mv_chain->mv_st_cont.mvs_zintdev.socketptr = which_socketptr; sockintr->wait_for_what = wait_for_what; sockintr->who_saved = sockwhich_wait; if (NO_M_TIMEOUT != msec_timeout) { sockintr->end_time = end_time; sockintr->end_time_valid = TRUE; } else sockintr->end_time_valid = FALSE; dsocketptr->mupintr = TRUE; socketus_interruptus++; DBGSOCK((stdout, "socwait: mv_stent queued - endtime: %d/%d" " interrupts: %d\n", end_time.at_sec, end_time.at_usec, socketus_interruptus)); } REVERT_GTMIO_CH(&iod->pair, ch_set); async_action(FALSE); assertpro(FALSE); /* Should *never* return from async_action */ return FALSE; /* For the compiler.. */ } if (NO_M_TIMEOUT != msec_timeout) { sys_get_curr_time(&cur_time); cur_time = sub_abs_time(&end_time, &cur_time); msec_timeout = (int4)(cur_time.at_sec * MILLISECS_IN_SEC + /* Round up in order to prevent premature timeouts */ DIVIDE_ROUND_UP(cur_time.at_usec, MICROSECS_IN_MSEC)); if (0 >msec_timeout) { rv = 0; /* time out */ break; } utimeout.tv_sec = cur_time.at_sec; utimeout.tv_usec = (gtm_tv_usec_t)cur_time.at_usec; } } else break; /* either other error or done */ } if ((rv == 0) && (0 == rconnected) && (0 == rlisten) && (0 == rwrite)) { /* none selected or prior pending event */ iod->dollar.key[0] = '\0'; if (NO_M_TIMEOUT != msec_timeout) { dollar_truth = FALSE; REVERT_GTMIO_CH(&iod->pair, ch_set); return FALSE; } else continue; } else if (rv < 0) { errptr = (char *)STRERROR(errno); errlen = STRLEN(errptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_SOCKWAIT, 0, ERR_TEXT, 2, errlen, errptr); return FALSE; } } else if ((0 == rlisten) && (0 == rconnected) && (0 == rwrite)) { /* nothing to select and no pending events */ iod->dollar.key[0] = '\0'; if (NO_M_TIMEOUT != msec_timeout) { dollar_truth = FALSE; REVERT_GTMIO_CH(&iod->pair, ch_set); return FALSE; } else continue; } /* find out which sockets are ready */ oldestlistencycle = oldestconnectedcycle = oldestwritecycle = oldesteventcycle = 0; oldestlistenindex = oldestconnectedindex = oldestwriteindex = oldesteventindex = -1; for (ii = 0; ii < dsocketptr->n_socket; ii++) { if (which_socketptr && (which_socketptr != dsocketptr->socket[ii])) continue; socketptr = dsocketptr->socket[ii]; if ((socket_listening != socketptr->state) && (socket_connected != socketptr->state)) continue; /* not a candidate for /WAIT */ #ifdef USE_POLL for (jj = 0; jj < poll_nfds; jj++) { if (socketptr == poll_socketptr[jj]) break; } assertpro((0 == jj) || (jj <= poll_nfds)); /* equal poll_nfds if not polled */ if (nselect && (jj != poll_nfds) && (socketptr->sd == poll_fds[jj].fd) && poll_fds[jj].revents) #endif #ifdef USE_SELECT assertpro(FD_SETSIZE > socketptr->sd); if (nselect && ((0 != FD_ISSET(socketptr->sd, &select_fdset) || (selectw_needed && (0 != FD_ISSET(socketptr->sd, &selectw_fdset)))))) #endif { /* set flag in socketptr and keep going */ if (POLL_ONLY((POLLIN & poll_fds[jj].revents)) SELECT_ONLY((FD_ISSET(socketptr->sd), &select_fdset))) { socketptr->current_events |= SOCKPEND_READ; socketptr->readyforwhat |= SOCKREADY_READ; DBGSOCKWAIT((stdout,"socket[%d] pollin, priorreadycycle= %d, pending= %d, current= %d\n", ii, socketptr->readycycle, socketptr->pendingevent, socketptr->current_events)); if (!socketptr->pendingevent && !(SOCKPEND_BUFFER & socketptr->current_events)) socketptr->readycycle = dsocketptr->waitcycle; } if (socket_listening == socketptr->state) { rlisten++; socketptr->readycycle = dsocketptr->waitcycle; } else { if (SELECT_ONLY(selectw_needed && (FD_ISSET(socketptr->sd, &selectw_fdset))) POLL_ONLY(poll_fds[jj].revents & POLLOUT)) { socketptr->current_events |= SOCKPEND_WRITE; socketptr->readyforwhat |= SOCKREADY_WRITE; if (!socketptr->pendingevent) { DBGSOCKWAIT((stdout,"socket[%d] pollout, priorreadycycle= %d, " "pending= %d, current= %d\n", ii, socketptr->readycycle, socketptr->pendingevent, socketptr->current_events)); socketptr->readycycle = dsocketptr->waitcycle; /* newly ready */ } rwrite++; } rconnected++; } } if (socketptr->current_events || (SOCKPEND_READ & socketptr->pendingevent)) { /* smallest readycycle is the oldest aka longest unselected */ if (socket_listening == socketptr->state) { if (0 == oldestlistencycle) { oldestlistencycle = socketptr->readycycle; oldestlistenindex = ii; } else if (oldestlistencycle > socketptr->readycycle) { /* this socket waiting longer */ oldestlistencycle = socketptr->readycycle; assert(0 <= oldestlistenindex); prev_socketptr = dsocketptr->socket[oldestlistenindex]; prev_socketptr->pendingevent |= (SOCKPEND_READ & prev_socketptr->current_events); oldestlistenindex = ii; } else { DBGSOCKWAIT((stdout, "socket[%d] LISTEN priorpending= %d, current %d\n", ii, socketptr->pendingevent, socketptr->current_events)); socketptr->pendingevent |= (SOCKPEND_READ & socketptr->current_events); } } else { /* only select for write if ready this time */ if (SOCKPEND_WRITE & socketptr->current_events) { if (0 == oldestwritecycle) { oldestwritecycle = socketptr->readycycle; oldestwriteindex = ii; DBGSOCKWAIT((stdout,"socket[%d] oldestwrite, readycycle = %d\n", ii, socketptr->readycycle)); } else if (oldestwritecycle > socketptr->readycycle) { /* this socket waiting longer */ oldestwritecycle = socketptr->readycycle; DBGSOCKWAIT((stdout,"socket[%d] oldestwrite(replace), readycycle = %d\n", ii, socketptr->readycycle)); assert(0 <= oldestwriteindex); prev_socketptr = dsocketptr->socket[oldestwriteindex]; prev_socketptr->pendingevent |= (SOCKPEND_WRITE & prev_socketptr->current_events); oldestwriteindex = ii; } else { DBGSOCKWAIT((stdout, "socket[%d] WRITE priorpending= %d, current %d\n", ii, socketptr->pendingevent, socketptr->current_events)); socketptr->pendingevent |= (SOCKPEND_WRITE & socketptr->current_events); } } if ((SOCKPEND_READ & socketptr->current_events) || (SOCKPEND_READ & socketptr->pendingevent)) { if (0 == oldestconnectedcycle) { oldestconnectedcycle = socketptr->readycycle; DBGSOCKWAIT((stdout,"socket[%d] oldestread, readycycle = %d\n", ii, socketptr->readycycle)); oldestconnectedindex = ii; } else if (oldestconnectedcycle > socketptr->readycycle) { /* this socket waiting longer */ oldestconnectedcycle = socketptr->readycycle; DBGSOCKWAIT((stdout, "socket[%d] oldestread(replace), readycycle = %d\n", ii, socketptr->readycycle)); assert(0 <= oldestconnectedindex); prev_socketptr = dsocketptr->socket[oldestconnectedindex]; prev_socketptr->pendingevent |= (SOCKPEND_READ & prev_socketptr->current_events); oldestconnectedindex = ii; } else { DBGSOCKWAIT((stdout, "socket[%d] READ priorpending= %d, current %d\n", ii, socketptr->pendingevent, socketptr->current_events)); socketptr->pendingevent |= (SOCKPEND_READ & socketptr->current_events); } } } } } if (0 < oldestlistencycle) { oldesteventcycle = oldestlistencycle; oldesteventindex = oldestlistenindex; } else if (0 < oldestconnectedcycle) { /* something to READ has priority over a WRITE */ oldesteventcycle = oldestconnectedcycle; oldesteventindex = oldestconnectedindex; DBGSOCKWAIT((stdout,"selected read socket[%d], cycle = %d\n", oldestconnectedindex, oldestconnectedcycle)); } else if (0 < oldestwritecycle) { oldesteventcycle = oldestwritecycle; oldesteventindex = oldestwriteindex; DBGSOCKWAIT((stdout,"selected write socket[%d], cycle = %d\n", oldestwriteindex, oldestwritecycle)); } else { /* unexpected nothing to do */ assert((0 < oldestlistencycle) || (0 < oldestconnectedcycle)); iod->dollar.key[0] = '\0'; if (NO_M_TIMEOUT != msec_timeout) { dollar_truth = FALSE; REVERT_GTMIO_CH(&iod->pair, ch_set); return FALSE; } else continue; } socketptr = dsocketptr->socket[oldesteventindex]; if (socket_listening == socketptr->state) { rv = iosocket_accept(dsocketptr, socketptr, FALSE); if (0 < rv) { retry_accept = TRUE; continue; /* pending connection gone so redo */ } else if (-1 == rv) { REVERT_GTMIO_CH(&iod->pair, ch_set); return FALSE; /* error handled in iosocket_accept */ } } else { assert(socket_connected == socketptr->state); dsocketptr->current_socket = oldesteventindex; len = 0; /* $KEY can only have one item so READWRITE if both */ if (socketptr->readyforwhat & SOCKREADY_READ) { len1 = SIZEOF(READ) - 1; memcpy(&iod->dollar.key[len], READ, len1); len += len1; } if (socketptr->readyforwhat & SOCKREADY_WRITE) { len1 = SIZEOF(WRITE) - 1; memcpy(&iod->dollar.key[len], WRITE, len1); len += len1; } assert(0 != len); iod->dollar.key[len++] = '|'; memcpy(&iod->dollar.key[len], socketptr->handle, socketptr->handle_len); len += socketptr->handle_len; iod->dollar.key[len++] = '|'; if (NULL != socketptr->remote.saddr_ip) { strncpy(&iod->dollar.key[len], socketptr->remote.saddr_ip, DD_BUFLEN - 1 - len); } else { assertpro(socket_local == socketptr->protocol); if (NULL != socketptr->local.sa) charptr = ((struct sockaddr_un *)(socketptr->local.sa))->sun_path; else if (NULL != socketptr->remote.sa) charptr = ((struct sockaddr_un *)(socketptr->remote.sa))->sun_path; else charptr = (char *)""; strncpy(&dsocketptr->iod->dollar.key[len], charptr, DD_BUFLEN - len - 1); } iod->dollar.key[DD_BUFLEN - 1] = '\0'; } break; } if (NO_M_TIMEOUT != msec_timeout) dollar_truth = TRUE; REVERT_GTMIO_CH(&iod->pair, ch_set); return TRUE; } int iosocket_accept(d_socket_struct *dsocketptr, socket_struct *socketptr, boolean_t selectfirst) { char *errptr; GTM_SOCKLEN_TYPE size, addrlen, sockoptlen; int rv, len, errcode, keepalive_opt, keepalive_value, save_errno; int4 errlen; char port_buffer[NI_MAXSERV], ipaddr[SA_MAXLEN + 1]; #ifdef USE_POLL struct pollfd poll_fds; int poll_fd; #endif #ifdef USE_SELECT fd_set select_fdset; #endif socket_struct *newsocketptr; struct sockaddr *peer_sa_ptr; struct sockaddr_storage peer; /* socket address + port */ struct timeval utimeout; static readonly char action[] = "ACCEPT"; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (gtm_max_sockets <= dsocketptr->n_socket) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SOCKMAX, 1, gtm_max_sockets); return -1; } peer_sa_ptr = ((struct sockaddr *)(&peer)); if (selectfirst || (dsocketptr->waitcycle > socketptr->readycycle)) { /* if not selected this time do a select first to check if connection still there */ do { #ifdef USE_POLL poll_fds.fd = socketptr->sd; poll_fds.events = POLLIN; rv = poll(&poll_fds, 1, 0); #endif #ifdef USE_SELECT FD_ZERO(&select_fdset); FD_SET(socketptr->sd, &select_fdset); utimeout.tv_sec = utimeout.tv_usec = 0; rv = select(socketptr->sd + 1, (void *)&select_fdset, NULL, NULL, &utimeout); #endif } while ((0 > rv) && (EINTR == (save_errno = errno))); /* inline assigment */ if (0 > rv) { errptr = (char *)STRERROR(save_errno); errlen = STRLEN(errptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_SOCKWAIT, 0, ERR_TEXT, 2, errlen, errptr); return -1; } else if (0 == rv) return EWOULDBLOCK; /* signal to find another ready socket */ } size = SIZEOF(struct sockaddr_storage); ACCEPT_SOCKET(socketptr->sd, peer_sa_ptr, &size, rv); if (-1 == rv) { switch (errno) { case ENOBUFS: case ECONNABORTED: case ETIMEDOUT: case ECONNRESET: case ENOTCONN: case ENOSR: return errno; /* pending connection gone so retry */ default: errptr = (char *)STRERROR(errno); errlen = STRLEN(errptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_SOCKACPT, 0, ERR_TEXT, 2, errlen, errptr); return -1; } } SOCKET_DUP(socketptr, newsocketptr); newsocketptr->remote.ai.ai_socktype = socketptr->local.ai.ai_socktype; newsocketptr->remote.ai.ai_protocol = socketptr->local.ai.ai_protocol; newsocketptr->lastaction = newsocketptr->readycycle = 0; newsocketptr->pendingevent = 0; newsocketptr->sd = rv; if (socket_local != newsocketptr->protocol) { /* translate internal address to numeric ip address */ SOCKET_ADDR_COPY(newsocketptr->remote, peer_sa_ptr, size); /* info not set for socket_local */ GETNAMEINFO(peer_sa_ptr, size, ipaddr, SA_MAXLEN, NULL, 0, NI_NUMERICHOST, errcode); if (0 != errcode) { close(newsocketptr->sd); SOCKET_FREE(newsocketptr); RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode); return -1; } if (NULL != newsocketptr->remote.saddr_ip) free(newsocketptr->remote.saddr_ip); STRNDUP(ipaddr, SA_MAXLEN, newsocketptr->remote.saddr_ip); /* translate internal address to port number*/ GETNAMEINFO(peer_sa_ptr, size, NULL, 0, port_buffer, NI_MAXSERV, NI_NUMERICSERV, errcode); if (0 != errcode) { close(newsocketptr->sd); SOCKET_FREE(newsocketptr); RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode); return -1; } newsocketptr->remote.port = ATOI(port_buffer); newsocketptr->remote.ai.ai_addrlen = size; addrlen = SIZEOF(struct sockaddr_storage); if (-1 == getsockname(newsocketptr->sd, SOCKET_LOCAL_ADDR(newsocketptr), &addrlen)) { save_errno = errno; errptr = (char *)STRERROR(save_errno); errlen = STRLEN(errptr); close(newsocketptr->sd); SOCKET_FREE(newsocketptr); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_GETSOCKNAMERR, 3, save_errno, errlen, errptr); return -1; } newsocketptr->local.ai.ai_addrlen = addrlen; newsocketptr->local.ai.ai_family = SOCKET_LOCAL_ADDR(newsocketptr)->sa_family; GETNAMEINFO(SOCKET_LOCAL_ADDR(newsocketptr), newsocketptr->local.ai.ai_addrlen, ipaddr, SA_MAXLEN, NULL, 0, NI_NUMERICHOST, errcode); if (0 != errcode) { close(newsocketptr->sd); SOCKET_FREE(newsocketptr); RTS_ERROR_ADDRINFO(NULL, ERR_GETNAMEINFO, errcode); return -1; } if (NULL != newsocketptr->local.saddr_ip) free(newsocketptr->local.saddr_ip); STRNDUP(ipaddr, SA_MAXLEN, newsocketptr->local.saddr_ip); # ifdef DEBUG sockoptlen = sizeof(keepalive_value); keepalive_value = 0; if (-1 == getsockopt(newsocketptr->sd, SOL_SOCKET, SO_KEEPALIVE, &keepalive_value, &sockoptlen)) { save_errno = errno; errptr = (char *)STRERROR(save_errno); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_GETSOCKOPTERR, 5, LEN_AND_LIT("SO_KEEPALIVE"), save_errno, LEN_AND_STR(errptr)); return -1; } else if ((0 != newsocketptr->options_state.alive) && ((0 == keepalive_value) != (0 == newsocketptr->keepalive))) assert(keepalive_value == socketptr->keepalive); /* AIX returns cnt if on */ # endif } newsocketptr->state = socket_connected; newsocketptr->passive = FALSE; newsocketptr->howcreated = creator_accept; newsocketptr->first_read = newsocketptr->first_write = TRUE; /* put the new-born socket to the list and create a handle for it */ iosocket_handle(newsocketptr->handle, &newsocketptr->handle_len, TRUE, dsocketptr); STRNDUP(socketptr->handle, socketptr->handle_len, newsocketptr->parenthandle); socketptr->lastaction = dsocketptr->waitcycle; /* record cycle for last connect */ dsocketptr->socket[dsocketptr->n_socket++] = newsocketptr; dsocketptr->current_socket = dsocketptr->n_socket - 1; len = SIZEOF(CONNECTED) - 1; memcpy(&dsocketptr->iod->dollar.key[0], CONNECTED, len); dsocketptr->iod->dollar.key[len++] = '|'; memcpy(&dsocketptr->iod->dollar.key[len], newsocketptr->handle, newsocketptr->handle_len); len += newsocketptr->handle_len; dsocketptr->iod->dollar.key[len++] = '|'; if (socket_local != newsocketptr->protocol) strncpy(&dsocketptr->iod->dollar.key[len], newsocketptr->remote.saddr_ip, DD_BUFLEN - 1 - len); else { /* get path from listening socket local side */ assert(NULL != socketptr->local.sa); STRNCPY_STR(&dsocketptr->iod->dollar.key[len], ((struct sockaddr_un *)(socketptr->local.sa))->sun_path, DD_BUFLEN - len); SOCKET_ADDR_COPY(newsocketptr->remote, socketptr->local.sa, SIZEOF(struct sockaddr_un)); newsocketptr->remote.ai.ai_addrlen = socketptr->local.ai.ai_addrlen; } dsocketptr->iod->dollar.key[DD_BUFLEN - 1] = '\0'; /* In case we fill the buffer */ newsocketptr->remote.ai.ai_family = SOCKET_REMOTE_ADDR(newsocketptr)->sa_family; return 0; } fis-gtm-V7.0-005/sr_port/iosocket_write.c0000644000032200000250000006602614342376331017253 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* iosocket_write.c */ #include "mdef.h" #include #ifdef USE_POLL #include "gtm_poll.h" #else #include "gtm_select.h" #endif #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_stdio.h" #include "gtm_stdlib.h" #include "gtm_string.h" #include "eintr_wrappers.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "io.h" #include "gt_timer.h" #include "iosocketdef.h" #include "dollarx.h" #include "gtm_conv.h" #include "gtm_utf8.h" #include "min_max.h" #include "stringpool.h" #include "send_msg.h" #include "error.h" #include "rel_quant.h" #include "svnames.h" #include "op.h" #include "gtmio.h" #include "wbox_test_init.h" #include "util.h" #ifdef GTM_TLS #include "gtm_tls.h" #endif GBLREF boolean_t prin_in_dev_failure, prin_out_dev_failure; GBLREF io_pair io_curr_device, io_std_device; GBLREF mstr chset_names[]; GBLREF mval dollar_zstatus; GBLREF spdesc stringpool; GBLREF UConverter *chset_desc[]; GBLREF volatile int4 outofband; #ifdef GTM_TLS GBLREF gtm_tls_ctx_t *tls_ctx; #endif error_def(ERR_CURRSOCKOFR); error_def(ERR_NOPRINCIO); error_def(ERR_NOSOCKETINDEV); error_def(ERR_SOCKPASSDATAMIX); error_def(ERR_SOCKWRITE); error_def(ERR_TEXT); error_def(ERR_TLSIOERROR); error_def(ERR_ZFF2MANY); error_def(ERR_ZINTRECURSEIO); error_def(ERR_GETSOCKOPTERR); error_def(ERR_SETSOCKOPTERR); #define SOCKBLOCK_UTF_BOM "Non blocking WRITE error sending UTF-16 BOM" #define SOCKBLOCK_DELIM "Non blocking WRITE error sending DELIMITER or ZFF" #define SOCKBLOCK_WRAP "Non blocking WRITE error sending DELIMITER while wrapping" #define SOCKBLOCK_OUTOFBAND "Non blocking WRITE interrupted" #define SOCKBLOCK_BLOCKED "Non blocking WRITE blocked - no further WRITEs allowed" /* if non blocking writes, stop if non restartable outofband event */ #define SEND_OOB(SOCKETPTR, BUF, LEN, FLAGS, RC) \ { \ do \ { \ RC = send((SOCKETPTR)->sd, BUF, LEN, FLAGS); /* BYPASSOK handle EINTR here */ \ } while ((-1 == RC) && (EINTR == errno) && (!outofband || \ ((SOCKETPTR)->nonblocked_output ? (jobinterrupt == outofband) : TRUE))); \ } #define DOTCPSEND_REAL(SOCKETPTR, SBUFF, SBUFF_LEN, SFLAGS, WRITTEN, RC) \ { \ ssize_t gtmioStatus; \ size_t gtmioBuffLen; \ size_t gtmioChunk; \ size_t gtmioWritten; \ uint4 gtmioRetries; \ sm_uc_ptr_t gtmioBuff; \ \ gtmioBuffLen = SBUFF_LEN; \ gtmioBuff = (sm_uc_ptr_t)(SBUFF); \ gtmioWritten = gtmioRetries = 0; \ for (;;) \ { \ gtmioChunk = gtmioBuffLen; \ SEND_OOB(SOCKETPTR, gtmioBuff, gtmioChunk, SFLAGS, gtmioStatus); \ if ((ssize_t)-1 != gtmioStatus) \ { \ gtmioBuffLen -= gtmioStatus; \ gtmioWritten += gtmioStatus; \ if (0 == gtmioBuffLen) \ break; \ gtmioBuff += gtmioStatus; \ } else if (!(SOCKETPTR)->nonblocking) \ break; \ else if ((EAGAIN == errno) || (EWOULDBLOCK == errno)) \ { \ if (++gtmioRetries > (SOCKETPTR)->max_output_retries) \ { \ (SOCKETPTR)->output_failures = gtmioRetries; \ break; \ } \ SHORT_SLEEP(WAIT_FOR_BLOCK_TIME); \ } else \ break; \ } \ WRITTEN = gtmioWritten; \ if ((ssize_t)-1 == gtmioStatus) /* Had legitimate error - return it */ \ RC = errno; \ else if (0 == gtmioBuffLen) \ RC = 0; \ else \ RC = -1; /* Something kept us from sending what we wanted */ \ } #define DOTCPSEND(SOCKETPTR, SBUFF, SBUFF_LEN, SFLAGS, WRITTEN, RC) \ { \ ssize_t localstatus, lwritten; \ if (!(SOCKETPTR)->obuffer_in_use) \ DOTCPSEND_REAL(SOCKETPTR, SBUFF, SBUFF_LEN, SFLAGS, WRITTEN, RC) \ else \ { /* need to return and handle partial write WRITTEN */ \ localstatus = iosocket_write_buffered(SOCKETPTR, SBUFF, SBUFF_LEN, &lwritten); \ if (SBUFF_LEN == lwritten) \ { \ assert(0 == localstatus); \ RC = 0; \ } else \ RC = localstatus; \ WRITTEN = lwritten; \ } \ if ((0 != RC) && socketptr->nonblocked_output) \ socketptr->output_blocked = TRUE; \ } void iosocket_write(mstr *v) { iosocket_write_real(v, TRUE); } int iosocket_buffer_error(socket_struct *socketptr) { /* output error from obuffer_errno */ int errlen, devlen, save_obuffer_errno, msgid; io_desc *iod; d_socket_struct *dsocketptr; const char *errptr; if (0 == socketptr->obuffer_errno) return 0; /* no error */ dsocketptr = socketptr->dev; iod = dsocketptr->iod; # ifdef GTM_TLS if (socketptr->tlsenabled) { if (-1 == socketptr->obuffer_errno) { errptr = gtm_tls_get_error((gtm_tls_socket_t *)socketptr->tlssocket); } else { errptr = (char *)STRERROR(socketptr->obuffer_errno); } save_obuffer_errno = 0; msgid = ERR_TLSIOERROR; } else # endif { save_obuffer_errno = socketptr->obuffer_errno; errptr = NULL; msgid = ERR_SOCKWRITE; } socketptr->obuffer_errno = 0; SOCKWRTERROR(iod, socketptr, msgid, save_obuffer_errno, errptr, "send"); return -1; /* error issued */ } ssize_t iosocket_output(socket_struct *socketptr, char *buffer, size_t length, boolean_t resetbuffer, boolean_t timed, ssize_t *written); ssize_t iosocket_output(socket_struct *socketptr, char *buffer, size_t length, boolean_t resetbuffer, boolean_t timed, ssize_t *written) { /* note: buffer may not be socketptr->obuffer */ boolean_t pollwrite; ssize_t status, local_written; size_t llen; int bytessent, istatus, timeout, save_errno, output_retries; char *lbuffer; # ifdef GTM_TLS int tlspolldirection = 0, short_sends = 0, save_errno2 = 0; # endif # ifdef USE_POLL struct pollfd fds; # else fd_set fds, *readfds, *writefds; struct timeval timeout_spec; # endif *written = local_written = 0; if (!socketptr->obuffer_output_active) return 0; /* how did we get here */ if (timed) { if (0 != socketptr->obuffer_errno) return -1; /* unprocessed error */ timeout = 0; /* no waiting in poll */ } else { assert(0 != socketptr->obuffer_wait_time); timeout = socketptr->obuffer_wait_time; } # ifndef USE_POLL FD_ZERO(&fds); FD_SET(socketptr->sd, &fds); timeout = timeout * 1000; /* convert milli to micro seconds */ timeout_spec.tv_sec = 0; timeout_spec.tv_usec = timeout; # endif llen = length; output_retries = status = 0; lbuffer = buffer; while ((0 == status) && (0 < llen)) { /* poll/select tlspolldirection - needed if noblocking */ # ifdef GTM_TLS if (socketptr->tlsenabled) pollwrite = (tlspolldirection == GTMTLS_WANT_READ) ? FALSE : TRUE; else # endif pollwrite = TRUE; # ifdef USE_POLL fds.fd = socketptr->sd; fds.events = pollwrite ? POLLOUT : POLLIN; istatus = poll(&fds, 1, timeout); # else if (pollwrite) { writefds = &fds; readfds = NULL; } else { readfds = &fds; writefds = NULL; } istatus = select(socketptr->sd + 1, readfds, writefds, NULL, &timeout_spec); # endif if (-1 == istatus) { save_errno = errno; if (timed) { /* called from timer so only try once */ socketptr->obuffer_errno = save_errno; status = -1; break; } if ((EAGAIN == save_errno) || (EWOULDBLOCK == save_errno)) { if (socketptr->nonblocking && (++output_retries > socketptr->max_output_retries)) { socketptr->output_failures = output_retries; socketptr->obuffer_errno = save_errno; status = -1; break; } rel_quant(); /* seems like a legitimate rel_quant */ } else if (EINTR != save_errno) { socketptr->obuffer_errno = save_errno; status = -1; break; } else if (socketptr->nonblocked_output && (no_event != outofband) && (jobinterrupt != outofband)) { socketptr->obuffer_errno = save_errno; status = -1; break; } # ifndef USE_POLL timeout_spec.tv_usec = timeout; FD_SET(socketptr->sd, &fds); # endif continue; } else if (0 == istatus) { /* poll/select timedout */ if (socketptr->nonblocking && (++output_retries > socketptr->max_output_retries)) { save_errno = socketptr->obuffer_errno = EAGAIN; status = -1; break; } continue; } # ifdef GTM_TLS if (socketptr->tlsenabled) { bytessent = gtm_tls_send((gtm_tls_socket_t *)socketptr->tlssocket, lbuffer, llen); if (0 < bytessent) { /* unless partial writes enabled either none or all should have been written */ save_errno2 = gtm_tls_errno(); /* get for debug */ if (llen > bytessent) { if (!socketptr->nonblocked_output) assert(FALSE && (llen == bytessent)); ++short_sends; } llen -= bytessent; lbuffer += bytessent; tlspolldirection = 0; local_written += bytessent; } else { switch (bytessent) { case GTMTLS_WANT_READ: tlspolldirection = GTMTLS_WANT_READ; break; case GTMTLS_WANT_WRITE: tlspolldirection = GTMTLS_WANT_WRITE; break; default: socketptr->obuffer_errno = save_errno = gtm_tls_errno(); if (-1 == save_errno) { /* non errno error use gtm_tls_get_error when reporting */ status = -1; break; } if (!socketptr->nonblocked_output && (0 == bytessent) && (0 == save_errno)) { /* should not get here */ assert(socketptr->nonblocked_output || (0 != bytessent) || (0 != save_errno)); } else if (socketptr->nonblocked_output && ((EAGAIN == save_errno) || (EWOULDBLOCK == save_errno))) { if (socketptr->nonblocking && (++output_retries > socketptr->max_output_retries)) { socketptr->output_failures = output_retries; status = -1; break; } else assert(!socketptr->nonblocking || (output_retries <= socketptr->max_output_retries)); rel_quant(); break; } else if (socketptr->nonblocked_output && ((EINTR == save_errno) && (0 != outofband) && (jobinterrupt != outofband))) { /* not JOBEXAM signal */ status = -1; break; } else { assert(-1 == bytessent); if (ECONNRESET == socketptr->obuffer_errno) { return (ssize_t)(-2); } else return (ssize_t)(-1); } break; } if (-1 == status) break; } } else # endif { /* as of 2020/8/18 buffered output is TLS only so revisit this section if non TLS buffered output added by GTM-3162 buffering on socket device */ DOTCPSEND_REAL(socketptr, buffer, length, 0, local_written, status); if (0 != status) /* 0=allsent -1=partial else=errno */ { /* current callers do this check and return */ socketptr->obuffer_errno = save_errno = status; status = -1; /* next 13 lines need rework if non TLS buffered output */ if (timed) { /* called from timer so only try once */ break; } if (!socketptr->nonblocked_output && ((EAGAIN == save_errno) || (EWOULDBLOCK == save_errno))) { rel_quant(); /* seems like a legitimate rel_quant */ status = 0; continue; } else if (EINTR != save_errno) { status = -1; break; } } else status = 0; break; /* DOTCPSEND_REAL does retries so no need here */ } } if (local_written == length) { /* all output written */ socketptr->obuffer_errno = 0; if (resetbuffer) socketptr->obuffer_length = socketptr->obuffer_offset = 0; } else assert(0 != status); *written = local_written; return status; } /* prototype in iosocketdef.h since called by iosocket_flush and iosocket_close */ ssize_t iosocket_output_buffer(socket_struct *socketptr) { ssize_t status, written; status = iosocket_output(socketptr, socketptr->obuffer, socketptr->obuffer_length, TRUE, FALSE, &written); return status; } void iosocket_output_timed(socket_struct *socketptr); void iosocket_output_timed(socket_struct *socketptr) { ssize_t status, written; size_t length; socketptr->obuffer_timer_set = FALSE; if (!socketptr->obuffer_output_active && (0 < socketptr->obuffer_length)) { /* no current writer so output the buffer */ socketptr->obuffer_output_active = TRUE; length = socketptr->obuffer_length; status = iosocket_output(socketptr, socketptr->obuffer, length, TRUE, TRUE, &written); socketptr->obuffer_output_active = FALSE; assert((length == written) || (0 != socketptr->obuffer_errno)); /* short write */ } /* reschedule timer if needed for blocking socket */ if ((0 < socketptr->obuffer_length) && (0 == socketptr->obuffer_errno) && !socketptr->nonblocked_output) { assert(0 != socketptr->obuffer_flush_time); socketptr->obuffer_timer_set = TRUE; start_timer((TID)socketptr, socketptr->obuffer_flush_time, iosocket_output_timed, SIZEOF(socketptr), (char *)&socketptr); } } ssize_t iosocket_write_buffered(socket_struct *socketptr, char *buffer, size_t length, ssize_t *written); ssize_t iosocket_write_buffered(socket_struct *socketptr, char *buffer, size_t length, ssize_t *written) { ssize_t status, obuffered_len; int errlen, devlen; io_desc *iod; d_socket_struct *dsocketptr; const char *errptr; socketptr->obuffer_output_active = TRUE; /* lock out timed writes */ *written = 0; status = socketptr->obuffer_errno; if ((0 == status ) && (0 < socketptr->obuffer_length) && ((socketptr->obuffer_size - socketptr->obuffer_length) <= length)) { /* more output than space left in buffer */ status = iosocket_output_buffer(socketptr); } if ((0 == status ) && (length > (socketptr->obuffer_size - socketptr->obuffer_offset))) { /* more output than can fit in buffer so just output it now */ assert(0 == socketptr->obuffer_length); status = iosocket_output(socketptr, buffer, length, FALSE, FALSE, written); } else if (0 == status) { /* put in buffer since room is available */ memcpy((void *)(socketptr->obuffer + socketptr->obuffer_offset), buffer, length); socketptr->obuffer_offset += length; socketptr->obuffer_length += length; /* start timer if not active */ if (!socketptr->obuffer_timer_set && (0 != socketptr->obuffer_flush_time)) { status = 0; *written = length; socketptr->obuffer_timer_set = TRUE; start_timer((TID)socketptr, socketptr->obuffer_flush_time, iosocket_output_timed, SIZEOF(socketptr), (char *)&socketptr); } else if (0 == socketptr->obuffer_flush_time) { obuffered_len = socketptr->obuffer_length; status = iosocket_output_buffer(socketptr); *written = (obuffered_len - socketptr->obuffer_length); assert((length == *written) || (0 != status)); } } socketptr->obuffer_output_active = FALSE; if ((0 > status) || (0 != socketptr->obuffer_errno)) { /* report error */ status = iosocket_buffer_error(socketptr); } return status; } void iosocket_write_real(mstr *v, boolean_t convert_output) { /* convert_output is FALSE when called from wteol or wtff */ io_desc *iod; mstr tempv; char *out, *c_ptr, *c_top, *errptr, *errortext; int in_b_len, b_len, status, new_len, c_len, mb_len; int flags, fcntl_flags, fcntl_res, save_errno; size_t written, total_written; d_socket_struct *dsocketptr; socket_struct *socketptr; boolean_t ch_set; DBGSOCK2((stdout, "socwrite: ************************** Top of iosocket_write\n")); iod = io_curr_device.out; ESTABLISH_GTMIO_CH(&iod->pair, ch_set); assert(gtmsocket == iod->type); dsocketptr = (d_socket_struct *)iod->dev_sp; if (0 >= dsocketptr->n_socket) { if (iod == io_std_device.out) ionl_write(v); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_NOSOCKETINDEV); REVERT_GTMIO_CH(&iod->pair, ch_set); return; } if (dsocketptr->n_socket <= dsocketptr->current_socket) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket); if (dsocketptr->mupintr) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZINTRECURSEIO); socketptr = dsocketptr->socket[dsocketptr->current_socket]; ENSURE_DATA_SOCKET(socketptr); #ifdef MSG_NOSIGNAL flags = MSG_NOSIGNAL; /* return EPIPE instead of SIGPIPE */ #else flags = 0; #endif tempv = *v; socketptr->lastop = TCP_WRITE; if (socketptr->nonblocked_output) { socketptr->lastaction = dsocketptr->waitcycle; socketptr->readyforwhat &= ~SOCKREADY_WRITE; socketptr->pendingevent &= ~SOCKPEND_WRITE; socketptr->lastarg_sent = 0; if (socketptr->output_blocked) { SOCKWRTERROR(iod, socketptr, ERR_SOCKWRITE, 0, SOCKBLOCK_BLOCKED, ""); REVERT_GTMIO_CH(&iod->pair, ch_set); return; } if (!socketptr->nonblocking) { /* set O_NONBLOCK if needed */ FCNTL2(socketptr->sd, F_GETFL, fcntl_flags); if (fcntl_flags < 0) { iod->dollar.za = ZA_IO_ERR; save_errno = errno; errptr = (char *)STRERROR(save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_GETSOCKOPTERR, 5, LEN_AND_LIT("F_GETFL FOR NON BLOCKING WRITE"), save_errno, LEN_AND_STR(errptr)); } FCNTL3(socketptr->sd, F_SETFL, fcntl_flags | (O_NDELAY | O_NONBLOCK), fcntl_res); if (fcntl_res < 0) { iod->dollar.za = ZA_IO_ERR; save_errno = errno; errptr = (char *)STRERROR(save_errno); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("F_SETFL FOR NON BLOCKING WRITE"), save_errno, LEN_AND_STR(errptr)); } socketptr->nonblocking = TRUE; } } /* In case the CHSET changes from non-UTF-16 to UTF-16 and a read has already been done, * there's no way to read the BOM bytes & to determine the variant. So default to UTF-16BE. */ if (!socketptr->first_write && (!IS_UTF16_CHSET(dsocketptr->ochset_utf16_variant) && (CHSET_UTF16 == iod->ochset))) { iod->ochset = dsocketptr->ochset_utf16_variant = CHSET_UTF16BE; get_chset_desc(&chset_names[iod->ochset]); } if (socketptr->first_write) { /* First WRITE, do following Transition to UTF16BE if ochset is UTF16 and WRITE a BOM */ if (CHSET_UTF16 == iod->ochset) { DBGSOCK2((stdout, "socwrite: First write UTF16 -- writing BOM\n")); iod->ochset = CHSET_UTF16BE; /* per standard, assume big endian when endian format is unspecified */ dsocketptr->ochset_utf16_variant = iod->ochset; get_chset_desc(&chset_names[iod->ochset]); written = 0; DOTCPSEND(socketptr, UTF16BE_BOM, UTF16BE_BOM_LEN, flags, written, status); DBGSOCK2((stdout, "socwrite: TCP send of BOM-BE with rc %d\n", status)); if (0 != status) { if (!socketptr->obuffer_in_use || (-1 != status)) { /* if buffered and status == -1 then error was already issued */ if (socketptr->nonblocked_output && ((EAGAIN == status) || (EWOULDBLOCK == status) || (EINTR == status))) { errortext = SOCKBLOCK_UTF_BOM; status = 0; socketptr->lastarg_sent = (size_t)-1; /* to force need to /BLOCK(CLEAR) */ } else errortext = NULL; SOCKWRTERROR(iod, socketptr, ERR_SOCKWRITE, status, errortext, "send"); } REVERT_GTMIO_CH(&iod->pair, ch_set); return; } else if (iod == io_std_device.out) prin_out_dev_failure = FALSE; } socketptr->first_write = FALSE; } if (CHSET_UTF16BE == iod->ochset || CHSET_UTF16LE == iod->ochset) { if ((0 < socketptr->zff.len) && (socketptr->zff.addr == socketptr->ozff.addr)) { /* Convert ZFF into ochset format so we don't need to convert every time ZFF is output */ new_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[iod->ochset], &socketptr->zff, NULL, NULL); if (MAX_ZFF_LEN < new_len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZFF2MANY, 2, new_len, MAX_ZFF_LEN); socketptr->ozff.addr = (char *)malloc(MAX_ZFF_LEN); /* should not need */ socketptr->ozff.len = new_len; UTF8_ONLY(socketptr->ozff.char_len = 0); /* don't care */ memcpy(socketptr->ozff.addr, stringpool.free, new_len); if (tempv.addr == socketptr->zff.addr) tempv = socketptr->ozff; /* from iosocket_wtff so use converted form */ } } /* Convert DELIMITER 0 to OCHSET format to avoid repeated conversions of DELIM0 on output. * CHSET can be switched b/w M/UTF-8/UTF-16* in UTF-8 mode. Convert the odelimitor0 accordingly * 1. odelimiter0 == idelimiter[0] (i.e. it's not been converted) && IS_UTF16_CHSET * 2. odelimiter0 != idelimiter[0] (i.e. it's been converted to UTF-16) && CHSET is NOT UTF-16 */ if (gtm_utf8_mode && (0 < socketptr->n_delimiter)) { if (((socketptr->delimiter[0].addr == socketptr->odelimiter0.addr) && IS_UTF16_CHSET(iod->ochset)) || ((socketptr->delimiter[0].addr != socketptr->odelimiter0.addr) && !IS_UTF16_CHSET(iod->ochset))) iosocket_odelim_conv(socketptr, iod->ochset); if (tempv.addr == socketptr->delimiter[0].addr) tempv = socketptr->odelimiter0; /* from iosocket_wteol so use converted form */ } memcpy(iod->dollar.device, "0", SIZEOF("0")); if (CHSET_M != iod->ochset) { /* For ochset == UTF-8, validate the output, * For ochset == UTF-16[B|L]E, convert the output (and validate during conversion) */ if (CHSET_UTF8 == iod->ochset) { UTF8_LEN_STRICT(v->addr, v->len); /* triggers badchar error for invalid sequence */ tempv = *v; } else { assert(CHSET_UTF16BE == iod->ochset || CHSET_UTF16LE == iod->ochset); /* Certain types of writes (calls from iosocket_wteol or _wtff) already have their output converted. Converting again just wrecks it so avoid that when necessary. */ if (convert_output) { new_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[iod->ochset], v, NULL, NULL); tempv.addr = (char *)stringpool.free; tempv.len = new_len; /* Since there is no dependence on string pool between now and when we send the data, we won't bother "protecting" the stringpool value. This space can be used again by whomever needs it without us forcing a garbage collection due to IO reformat. */ /* stringpool.free += new_len; */ } } } total_written = 0; /* data only this argument */ if (0 != (in_b_len = tempv.len)) { DBGSOCK2((stdout, "socwrite: starting output loop (%d bytes) - iodwidth: %d wrap: %d\n", in_b_len, iod->width, iod->wrap)); for (out = tempv.addr; ; out += b_len) { DBGSOCK2((stdout, "socwrite: ---------> Top of write loop $x: %d $y: %d in_b_len: %d\n", iod->dollar.x, iod->dollar.y, in_b_len)); if (!iod->wrap) b_len = in_b_len; else { if ((iod->dollar.x >= iod->width) && (START == iod->esc_state)) { /* Should this really be iosocket_Wteol() (for FILTER)? IF we call iosocket_wteol(), * there will be recursion iosocket_Write -> iosocket_Wteol ->iosocket_Write */ if (0 < socketptr->n_delimiter) { /* delimiters from wrapping are not counted for non blocking output */ written = 0; DOTCPSEND(socketptr, socketptr->odelimiter0.addr, socketptr->odelimiter0.len, (socketptr->urgent ? MSG_OOB : 0) | flags, written, status); DBGSOCK2((stdout, "socwrite: TCP send of %d byte delimiter with rc %d\n", socketptr->odelimiter0.len, status)); if (0 != status) { if (!socketptr->obuffer_in_use || (-1 != status)) { /* if buffered and status == -1 then error was already issued */ if (socketptr->nonblocked_output && ((EAGAIN == status) || (EWOULDBLOCK == status) || (EINTR == status) || (-1 == status))) { errortext = SOCKBLOCK_WRAP; status = 0; socketptr->lastarg_sent = (size_t)-1; /* need to CLEAR */ } else errortext = NULL; SOCKWRTERROR(iod, socketptr, ERR_SOCKWRITE, status, errortext, "send"); } REVERT_GTMIO_CH(&iod->pair, ch_set); return; } else if (iod == io_std_device.out) prin_out_dev_failure = FALSE; } iod->dollar.y++; iod->dollar.x = 0; DBGSOCK2((stdout, "socwrite: $x > width - wrote delimiter: %d $x: %d $y: %d\n", (0 < socketptr->n_delimiter), iod->dollar.x, iod->dollar.y)); } if ((START != iod->esc_state) || ((int)(iod->dollar.x + in_b_len) <= (int)iod->width)) { /* enough room even in the worst case, i.e., if width - dollar.x can accommodate in_b_len chars, * it certainly can accommodate in_b_len bytes */ b_len = in_b_len; } else { c_len = iod->width - iod->dollar.x; for (c_ptr = out, c_top = out + in_b_len, b_len = 0; (c_ptr < c_top) && c_len--; b_len += mb_len, c_ptr += mb_len) { mb_len = (CHSET_M == iod->ochset) ? 0 : (CHSET_UTF8 == iod->ochset) ? UTF8_MBFOLLOW(c_ptr) : (CHSET_UTF16BE == iod->ochset) ? UTF16BE_MBFOLLOW(c_ptr, c_top) : UTF16LE_MBFOLLOW(c_ptr, c_top); assert(-1 != mb_len); mb_len++; } DBGSOCK2((stdout, "socwrite: computing string length in chars: in_b_len: %d mb_len: %d\n", in_b_len, mb_len)); } } assert(0 != b_len); written = 0; DOTCPSEND(socketptr, out, b_len, (socketptr->urgent ? MSG_OOB : 0) | flags, written, status); DBGSOCK2((stdout, "socwrite: TCP data send of %d (of %d) bytes with rc %d\n", written, b_len, status)); total_written += written; if (0 != status) { if (!socketptr->obuffer_in_use || (-1 != status)) { /* if buffered and status == -1 then error was already issued */ if (socketptr->nonblocked_output && ((EAGAIN == status) || (EWOULDBLOCK == status) || (EINTR == status) || (-1 == status))) { if (!convert_output) { /* called from iosocket_wteol or iosocket_wtff */ status = 0; errortext = SOCKBLOCK_DELIM; } else { if (EINTR == status) { status = 0; errortext = SOCKBLOCK_OUTOFBAND; } else errortext = NULL; socketptr->lastarg_sent = (0 != total_written) ? total_written : -1; if (0 < written) dollarx(iod, (uchar_ptr_t)out, (uchar_ptr_t)out + written); } } else errortext = NULL; SOCKWRTERROR(iod, socketptr, ERR_SOCKWRITE, status, errortext, "send"); } REVERT_GTMIO_CH(&iod->pair, ch_set); return; } else { /* no error */ if (iod == io_std_device.out) prin_out_dev_failure = FALSE; } if (socketptr->nonblocked_output) b_len = written; /* update to amount actually written */ dollarx(iod, (uchar_ptr_t)out, (uchar_ptr_t)out + b_len); DBGSOCK2((stdout, "socwrite: $x/$y updated by dollarx(): $x: %d $y: %d filter: %d escape: %d\n", iod->dollar.x, iod->dollar.y, iod->write_filter, iod->esc_state)); in_b_len -= b_len; if (0 >= in_b_len) break; } iod->dollar.za = 0; } if (socketptr->nonblocked_output) { socketptr->args_written++; if (tempv.len != total_written) socketptr->lastarg_sent = (0 != total_written) ? total_written : -1; /* non restartable outofband */ } DBGSOCK2((stdout, "socwrite: <--------- Leaving iosocket_write\n")); REVERT_GTMIO_CH(&iod->pair, ch_set); return; } fis-gtm-V7.0-005/sr_port/iosocket_wteol.c0000644000032200000250000000554614342376331017253 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* iosocket_wteol.c */ /* write the 0th delimiter and flush */ #include "mdef.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "gtm_stdlib.h" #include "gt_timer.h" #include "io.h" #include "iottdef.h" #include "iosocketdef.h" #include "error.h" #include "util.h" GBLREF io_pair io_std_device; error_def(ERR_CURRSOCKOFR); error_def(ERR_NOSOCKETINDEV); error_def(ERR_SOCKPASSDATAMIX); void iosocket_wteol(int4 val, io_desc *io_ptr) { d_socket_struct *dsocketptr; socket_struct *socketptr; char *ch, *top; int eol_cnt; boolean_t ch_set; assert(gtmsocket == io_ptr->type); dsocketptr = (d_socket_struct *)io_ptr->dev_sp; ESTABLISH_GTMIO_CH(&io_ptr->pair, ch_set); if (0 >= dsocketptr->n_socket) { # ifndef VMS if (io_ptr == io_std_device.out) ionl_wteol(val, io_ptr); else # endif RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_NOSOCKETINDEV); REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return; } if (dsocketptr->current_socket >= dsocketptr->n_socket) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket); return; } socketptr = dsocketptr->socket[dsocketptr->current_socket]; ENSURE_DATA_SOCKET(socketptr); assert(val); io_ptr->esc_state = START; if (socketptr->n_delimiter > 0) { for (eol_cnt = val; eol_cnt--; ) { io_ptr->dollar.x = 0; /* so that iosocket_write doesn't try to wrap (based on escape state and width) */ iosocket_write_real(&socketptr->odelimiter0, FALSE); } } else if (socketptr->nonblocked_output) { /* if no delimiter treat WRITE ! as a successful write */ socketptr->lastarg_sent = 0; socketptr->args_written += val; /* count each ! */ } /* $X is maintained in VMS without the below assignment (resetting to 0) because the NATIVE_TTEOL is \015\012 * and the (\015) triggers appropriate maintenance of $X. In UNIX, NATIVE_TTEOL is \012, so * FILTER=CHARACTER effectively turns off all $X maintenance (except for WRAP logic). * In VMS the below assignment is not necessary, but harmless; it is always logically correct. */ io_ptr->dollar.x = 0; if (!(io_ptr->write_filter & CHAR_FILTER) || !socketptr->delim0containsLF) { /* If FILTER won't do it, also maintain $Y */ io_ptr->dollar.y += val; if (io_ptr->length) io_ptr->dollar.y %= io_ptr->length; } iosocket_flush(io_ptr); REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return; } fis-gtm-V7.0-005/sr_port/iosocket_wtff.c0000644000032200000250000000373014342376331017060 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* iosocket_wtff.c */ #include "mdef.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "io.h" #include "gt_timer.h" #include "iosocketdef.h" #include "error.h" GBLREF io_pair io_curr_device; #ifndef VMS GBLREF io_pair io_std_device; #endif error_def(ERR_CURRSOCKOFR); error_def(ERR_NOSOCKETINDEV); error_def(ERR_SOCKPASSDATAMIX); void iosocket_wtff(void) { io_desc *iod; socket_struct *socketptr; d_socket_struct *dsocketptr; boolean_t ch_set; iod = io_curr_device.out; ESTABLISH_GTMIO_CH(&iod->pair, ch_set); assert(gtmsocket == iod->type); iod->esc_state = START; dsocketptr = (d_socket_struct *)iod->dev_sp; if (0 >= dsocketptr->n_socket) { # ifndef VMS if (iod == io_std_device.out) ionl_wtff(); else # endif RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_NOSOCKETINDEV); REVERT_GTMIO_CH(&iod->pair, ch_set); return; } if (dsocketptr->current_socket >= dsocketptr->n_socket) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket); return; } socketptr = dsocketptr->socket[dsocketptr->current_socket]; ENSURE_DATA_SOCKET(socketptr); if (socketptr->ozff.len) iosocket_write_real(&socketptr->ozff, FALSE); else if (socketptr->nonblocked_output) { /* treat WRITE # when zff not set as a successful write */ socketptr->lastarg_sent = 0; socketptr->args_written++; } iosocket_flush(iod); iod->dollar.x = 0; iod->dollar.y = 0; REVERT_GTMIO_CH(&iod->pair, ch_set); return; } fis-gtm-V7.0-005/sr_port/iosocket_wtone.c0000755000032200000250000000261114342376331017246 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ /* iosocket_wtone.c */ #include "mdef.h" #include "gtm_socket.h" #include "gtm_inet.h" #include "io.h" #include "gt_timer.h" #include "iosocketdef.h" #include "gtm_utf8.h" GBLREF io_pair io_curr_device; void iosocket_wtone(int ch) { mstr temp; char c, uni_c[4], *endptr; io_desc *iod; if (CHSET_M == io_curr_device.out->ochset) { c = (char)ch; temp.len = 1; temp.addr = (char *)&c; } else { switch(io_curr_device.out->ochset) { case CHSET_UTF8: case CHSET_UTF16: case CHSET_UTF16BE: case CHSET_UTF16LE: endptr = (char *)UTF8_WCTOMB(ch, uni_c); break; default: assertpro(io_curr_device.out->ochset != io_curr_device.out->ochset); } temp.addr = uni_c; temp.len = INTCAST(endptr - uni_c); assert(0 < temp.len); /* we validated the code point already in op_wtone() */ } UTF8_ONLY(temp.char_len = 1); iosocket_write_real(&temp, TRUE); return; } fis-gtm-V7.0-005/sr_port/iosocketdef.h0000644000032200000250000005001614342376331016515 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ #ifndef IOSOCKETDEF_H #define IOSOCKETDEF_H /* iosocketdef.h */ /* one socket device may have more than one socket associate with it. * one socket may have more than one delimiter associate with it. */ #include #include "gtm_inet.h" #include "gtm_netdb.h" #include "gtm_un.h" #include "min_max.h" #include "gtm_socket.h" /* for using sockaddr_storage */ #ifndef GTM_MB_LEN_MAX #include "gtm_utf8.h" #endif /* values for lastop in socket_struct */ #define TCP_NOOP 0 #define TCP_WRITE 1 #define TCP_READ 2 /* bits for readyforwhat in socket_struct */ #define SOCKREADY_READ 1 #define SOCKREADY_WRITE 2 /* bits for pendingevent in socket_struct */ #define SOCKPEND_READ 1 /* from poll */ #define SOCKPEND_WRITE 2 #define SOCKPEND_BUFFER 4 /* something in input buffer */ /* bits for options_state in socket_struct */ #define SOCKOPTIONS_SYSTEM 1 /* value from getsockopt */ #define SOCKOPTIONS_USER 2 /* value specified by user */ #define SOCKOPTIONS_PENDING 4 /* setsockopt needed */ #define SOCKOPTIONS_FROM_STRUCT -1 /* flag for iosocket_tcp_keepalive.c */ typedef struct { uid_t mem; gid_t grp; } uic_struct_int; /* Debugging notes: Some debuging calls as as below. Note that DBGSOCK2 is *always* disabled. * disabled. As parts of the code work, the DBGSOCK calls are changed to DBGSOCK2 to get * them out of the way without removing them (they may be useful in the future). * * Uncomment the define for DEBUG_SOCK below to enable DBGSOCK() debugging */ /* #define DEBUG_SOCK */ #ifdef DEBUG_SOCK # include "gtmio.h" # define DBGSOCK(X) DBGFPF(X) # define DBGSOCK_ONLY(X) X #else # define DBGSOCK(X) # define DBGSOCK_ONLY(X) #endif #ifdef DEBUG_SOCKWAIT # define DBGSOCKWAIT(X) DBGFPF(X) # define DBGSOCKWAIT_ONLY(X) X #else # define DBGSOCKWAIT(X) # define DBGSOCKWAIT_ONLY(X) #endif /* #define DEBUG_SOCK2 */ #ifdef DEBUG_SOCK2 # include "gtmio.h" # define DBGSOCK2(X) DBGFPF(X) # define DBGSOCK_ONLY2(X) X #else # define DBGSOCK2(X) # define DBGSOCK_ONLY2(X) #endif /* About the length of the delimiter string. While we are allocating lots of space here for the maximum representation * of 64 delimiters each of 64 chars MB chars, the fact is that the iop option processing actually limits the string * containing all the delimiters to 255 bytes by its nature of having single byte length field imbedded in the buffer * stream. When the iop processing is modified to handle a larger string, these options will be useful. But for right * now, they are way overkill.. */ #define MAX_N_SOCKET 64 /* Initial default for gtm_max_sockets in gbldefs.c */ #define MAX_MAX_N_SOCKET (1024 * 1024) /* Values higher than this absurd value are most likely wrong */ #define MAX_N_DELIMITER 64 #define MAX_DELIM_LEN (MAX_N_DELIMITER * GTM_MB_LEN_MAX) /* worst case byte length for 64 UTF-8 characters */ #define MAX_HANDLE_LEN (64 * GTM_MB_LEN_MAX) /* worst case byte length for 64 UTF-8 characters */ #define MAX_ZFF_LEN (64 * GTM_MB_LEN_MAX) /* worst case byte length for 64 UTF-8 characters */ #define DEFAULT_LISTEN_DEPTH 1 #define DEFAULT_SOCKET_BUFFER_SIZE 0x400 #define MAX_SOCKET_BUFFER_SIZE 0x100000 /* Next three fields relate to the time that a variable length unterminated read will wait to see * if there is more input coming in before it gives up and returns what it has to the user. This * time is specified in milliseconds. This value used to be 200ms but that was deemed too long on * modern systems yet now the user can change it if they wish to. Tradeoffs are longer waits for * variable reads versus potential CPU burner if the value gets too low. The implementation now * waits INITIAL_MOREREAD_TIMEOUT time for the first read to occur and then switches to the * DEFAULT_MOREREAD_TIMEOUT. This keeps CPU usage low during the potentially long period prior to * reading some data, while being more responsive for subsequent reads. */ #define INITIAL_MOREREAD_TIMEOUT 200 #define DEFAULT_MOREREAD_TIMEOUT 10 #define MAX_MOREREAD_TIMEOUT 999 /* For buffered output, wait this long for socket to be ready to output */ #define DEFAULT_WRITE_WAIT 200 #define SOCKWRTERROR(IOD, SOCKPTR, GTMERR, SYSERR, ERRORTEXT, TLSLIT) \ MBSTART { \ int ERRLEN; \ char *ERRPTR; \ IOD->dollar.za = ZA_IO_ERR; \ \ ISSUE_NOPRINCIO_IF_NEEDED(IOD, TRUE, !SOCKPTR->ioerror); /* TRUE indicates WRITE */ \ assert((0 != SYSERR) || (NULL != ERRORTEXT)); \ if (NULL == ERRORTEXT) \ ERRPTR = (char *)STRERROR(SYSERR); \ else \ ERRPTR = (char *)ERRORTEXT; \ SET_DOLLARDEVICE_ONECOMMA_ERRSTR(IOD, ERRPTR, ERRLEN); \ assert((ERR_SOCKWRITE == GTMERR) || (ERR_TLSIOERROR == GTMERR)); \ if (SOCKPTR->ioerror) \ { \ if (ERR_SOCKWRITE == GTMERR) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) GTMERR, 0, ERR_TEXT, 2, ERRLEN, ERRPTR); \ else \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_TLSIOERROR, 2, LEN_AND_LIT(TLSLIT), \ ERR_TEXT, 2, ERRLEN, ERRPTR); \ } \ } MBEND #define SOCKET_ALLOC(SOCKPTR) \ { \ SOCKPTR = (socket_struct *)malloc(SIZEOF(socket_struct)); \ memset(SOCKPTR, 0, SIZEOF(socket_struct)); \ } #define SOCKET_ADDR(SOCKPTR, SOCKEND) \ ((sockaddr_ptr)(SOCKPTR->SOCKEND.sa \ ? SOCKPTR->SOCKEND.sa \ : (SOCKPTR->SOCKEND.sa = (struct sockaddr *)malloc(SIZEOF(struct sockaddr_storage)), \ memset(SOCKPTR->SOCKEND.sa, 0, SIZEOF(struct sockaddr_storage)), SOCKPTR->SOCKEND.sa))) #define SOCKET_LOCAL_ADDR(SOCKPTR) SOCKET_ADDR(SOCKPTR, local) #define SOCKET_REMOTE_ADDR(SOCKPTR) SOCKET_ADDR(SOCKPTR, remote) #define SOCKET_ADDR_COPY(SOCKADDRESS, SOCKADDRPTR, SOCKADDRLEN) \ { \ if (SOCKADDRESS.sa) \ free(SOCKADDRESS.sa); \ SOCKADDRESS.sa = (struct sockaddr *)malloc(SOCKADDRLEN); \ memcpy(SOCKADDRESS.sa, SOCKADDRPTR, SOCKADDRLEN); \ } #define SOCKET_AI_TO_ADDR(SOCKPTR, AIPTR, SOCKEND) SOCKET_ADDR_COPY((SOCKPTR)->SOCKEND, (AIPTR)->ai_addr, (AIPTR)->ai_addrlen) #define SOCKET_AI_TO_LOCAL_ADDR(SOCKPTR, AIPTR) SOCKET_AI_TO_ADDR(SOCKPTR, AIPTR, local) #define SOCKET_AI_TO_REMOTE_ADDR(SOCKPTR, AIPTR) SOCKET_AI_TO_ADDR(SOCKPTR, AIPTR, remote) #define SOCKET_ADDRLEN(SOCKPTR, AIPTR, SOCKEND) \ (((SOCKPTR)->SOCKEND.sa) ? ((AIPTR)->ai_addrlen) : (SIZEOF(struct sockaddr_storage))) #define SOCKET_BUFFER_INIT(SOCKPTR, SIZE) \ { \ SOCKPTR->buffer = (char *)malloc(SIZE); \ SOCKPTR->buffer_size = SIZE; \ SOCKPTR->buffered_length = SOCKPTR->buffered_offset = 0; \ } #define SOCKET_OBUFFER_INIT(SOCKETPTR, SIZE, WAIT_TIME, FLUSH_TIME) \ { \ if (0 == SOCKETPTR->obuffer_size) \ { \ SOCKETPTR->obuffer_size = (0 != SIZE) ? SIZE : SOCKETPTR->buffer_size; \ SOCKETPTR->obuffer = (char *)malloc(SOCKETPTR->obuffer_size); \ SOCKETPTR->obuffer_length = SOCKETPTR->obuffer_offset = 0; \ } \ SOCKETPTR->obuffer_wait_time = WAIT_TIME; \ SOCKETPTR->obuffer_flush_time = FLUSH_TIME; \ } #define SOCKET_FREE(SOCKPTR) \ { \ if (NULL != SOCKPTR) \ { \ if (NULL != SOCKPTR->buffer) \ { \ free(SOCKPTR->buffer); \ SOCKPTR->buffer = NULL; \ } \ if (NULL != SOCKPTR->obuffer) \ { \ free(SOCKPTR->obuffer); \ SOCKPTR->obuffer = NULL; \ } \ if (NULL != SOCKPTR->zff.addr) \ { \ if ((NULL != SOCKPTR->ozff.addr) && (SOCKPTR->ozff.addr != SOCKPTR->zff.addr)) \ free(SOCKPTR->ozff.addr); \ free(SOCKPTR->zff.addr); \ SOCKPTR->zff.addr = SOCKPTR->ozff.addr = NULL; \ } \ if (NULL != SOCKPTR->local.sa) \ { \ free(SOCKPTR->local.sa); \ SOCKPTR->local.sa = NULL; \ } \ if (NULL != SOCKPTR->remote.sa) \ { \ free(SOCKPTR->remote.sa); \ SOCKPTR->remote.sa = NULL; \ } \ if (NULL != SOCKPTR->local.saddr_ip) \ { \ free(SOCKPTR->local.saddr_ip); \ SOCKPTR->local.saddr_ip = NULL; \ } \ if (NULL != SOCKPTR->remote.saddr_ip) \ { \ free(SOCKPTR->remote.saddr_ip); \ SOCKPTR->remote.saddr_ip = NULL; \ } \ if (NULL != SOCKPTR->parenthandle) \ { \ free(SOCKPTR->parenthandle); \ SOCKPTR->parenthandle = NULL; \ } \ iosocket_delimiter((unsigned char *)NULL, 0, SOCKPTR, TRUE); \ free(SOCKPTR); \ SOCKPTR = NULL; \ } \ } #define SOCKET_DUP(SOCKPTR, NEWSOCKPTR) \ { \ NEWSOCKPTR = (socket_struct *)malloc(SIZEOF(socket_struct)); \ *NEWSOCKPTR = *SOCKPTR; \ if (NULL != SOCKPTR->buffer) \ { \ NEWSOCKPTR->buffered_length = NEWSOCKPTR->buffered_offset = 0; \ NEWSOCKPTR->buffer = (char *)malloc(SOCKPTR->buffer_size); \ } \ assert((NULL == SOCKPTR->obuffer) && (0 == SOCKPTR->obuffer_size)); \ if ((0 != SOCKPTR->zff.len) && (NULL != SOCKPTR->zff.addr)) \ { \ NEWSOCKPTR->zff.addr = (char *)malloc(MAX_ZFF_LEN); \ memcpy(NEWSOCKPTR->zff.addr, SOCKPTR->zff.addr, SOCKPTR->zff.len); \ if ((NULL != SOCKPTR->ozff.addr) && (SOCKPTR->zff.addr != SOCKPTR->ozff.addr)) \ { \ NEWSOCKPTR->ozff.addr = (char *)malloc(MAX_ZFF_LEN); \ memcpy(NEWSOCKPTR->ozff.addr, SOCKPTR->ozff.addr, SOCKPTR->ozff.len); \ NEWSOCKPTR->ozff.len = SOCKPTR->ozff.len; \ } else \ NEWSOCKPTR->ozff = NEWSOCKPTR->zff; \ } else \ { \ NEWSOCKPTR->zff.len = NEWSOCKPTR->ozff.len = 0; \ NEWSOCKPTR->zff.addr = NEWSOCKPTR->ozff.addr = NULL; \ } \ if (NULL != SOCKPTR->local.sa) \ { \ NEWSOCKPTR->local.sa = (struct sockaddr *)malloc(SOCKPTR->local.ai.ai_addrlen); \ memcpy(NEWSOCKPTR->local.sa, SOCKPTR->local.sa, SOCKPTR->local.ai.ai_addrlen); \ NEWSOCKPTR->local.ai.ai_addr = NEWSOCKPTR->local.sa; \ } \ if (NULL != SOCKPTR->remote.sa) \ { \ NEWSOCKPTR->remote.sa = (struct sockaddr *)malloc(SOCKPTR->remote.ai.ai_addrlen); \ memcpy(NEWSOCKPTR->remote.sa, SOCKPTR->remote.sa, SOCKPTR->remote.ai.ai_addrlen); \ NEWSOCKPTR->remote.ai.ai_addr = NEWSOCKPTR->remote.sa; \ } \ if (NULL != SOCKPTR->local.saddr_ip) \ STRNDUP(SOCKPTR->local.saddr_ip, SA_MAXLEN, NEWSOCKPTR->local.saddr_ip); \ if (NULL != SOCKPTR->remote.saddr_ip) \ STRNDUP(SOCKPTR->remote.saddr_ip, SA_MAXLEN, NEWSOCKPTR->remote.saddr_ip); \ if (NULL != SOCKPTR->parenthandle) \ NEWSOCKPTR->parenthandle = NULL; \ iosocket_delimiter_copy(SOCKPTR, NEWSOCKPTR); \ } enum socket_pass_type { sockpass_new, sockpass_data, sockpass_sock }; #define ENSURE_DATA_SOCKET(SOCKPTR) \ { \ if (socket_local == (SOCKPTR)->protocol) \ { \ if (sockpass_new == (SOCKPTR)->passtype) \ (SOCKPTR)->passtype = sockpass_data; \ else if (sockpass_sock == (SOCKPTR)->passtype) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(2) ERR_SOCKPASSDATAMIX, 0); \ } \ } #define ENSURE_PASS_SOCKET(SOCKPTR) \ { \ if (socket_local == (SOCKPTR)->protocol) \ { \ if (sockpass_new == (SOCKPTR)->passtype) \ (SOCKPTR)->passtype = sockpass_sock; \ else if (sockpass_data == (SOCKPTR)->passtype) \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(2) ERR_SOCKPASSDATAMIX, 0); \ } \ } enum socket_state { socket_connected, socket_listening, socket_bound, socket_created, socket_connect_inprogress }; enum socket_creator { creator_listen, creator_accept, creator_connect, creator_principal, creator_passed }; enum socket_protocol { socket_tcpip, socket_spx, socket_local, n_socket_protocol }; enum socket_which /* which module saved the interrupted info */ { sockwhich_invalid, sockwhich_readfl, sockwhich_wait, sockwhich_connect }; typedef struct socket_address_type { struct sockaddr *sa; struct addrinfo ai; struct addrinfo *ai_head; /* store the head of addrinfo linked list */ unsigned short port; pid_t process; /* for LOCAL passfd */ char *saddr_ip; } socket_address; typedef struct socket_struct_type { int sd; /* socket descriptor */ int temp_sd; /* a temp socket descriptor only to test whether IPv6 can be created */ struct d_socket_struct_type *dev; /* point back to the driver */ boolean_t passive, ioerror, urgent, delim0containsLF; enum socket_state state; enum socket_protocol protocol; socket_address local, remote; uint4 lastop; uint4 moreread_timeout; /* timeout to see if more data available (ms) */ char handle[MAX_HANDLE_LEN]; int handle_len; int bufsiz; /* OS internal buffer size */ int n_delimiter; int last_recv_errno; mstr delimiter[MAX_N_DELIMITER]; mstr idelimiter[MAX_N_DELIMITER]; mstr odelimiter0; size_t buffer_size; /* size of the buffer for this socket */ size_t buffered_length; /* length of stuff buffered for this socket */ size_t buffered_offset; /* offset of the buffered stuff to buffer head */ char *buffer; /* pointer to the buffer of this socket */ boolean_t nodelay; boolean_t first_read; boolean_t first_write; boolean_t def_moreread_timeout; /* true if deviceparameter morereadtime defined in open or use */ enum socket_pass_type passtype; /* prevent mix of data and socket passing on LOCAL sockets */ uint filemode; /* for LOCAL */ uint filemode_mask; /* to tell which modes specified */ uic_struct_int uic; mstr zff; mstr ozff; /* UTF-16 if chset is UTF-16 else copy of zff */ uint4 lastaction; /* waitcycle count */ uint4 readycycle; /* when was ready */ uint4 pendingevent; /* bitmask, if listening, needs accept */ enum socket_creator howcreated; char *parenthandle; /* listening socket this created from */ size_t obuffer_size; /* size of the output buffer for this socket */ size_t obuffer_length; /* length of output in this buffer */ size_t obuffer_offset; /* offset of the buffered output to buffer head */ size_t obuffer_stopped_at; /* non blocking output blocked at this offset */ size_t obuffer_stopped_left; /* non blocking remaining output */ volatile boolean_t obuffer_in_use; /* obuffer should be used for TLS or non blocking */ volatile boolean_t obuffer_timer_set; /* timer scheduled to flush buffer */ volatile boolean_t obuffer_output_active; /* in buffer output now */ int obuffer_flush_time; /* flush output buffer after this many milliseconds */ int obuffer_wait_time; /* wait for output ready this many milliseconds */ int obuffer_errno; /* save status from timed output attempt */ char *obuffer; /* pointer to the output buffer of this socket */ boolean_t nonblocking; /* socket has been set O_NONBLOCK */ #ifdef GTM_TLS boolean_t tlsenabled; void *tlssocket; /* actually gtm_tls_socket_t */ boolean_t tlsreadblocked; boolean_t tlswriteblocked; short tlspolldirection; /* what TLS wants */ #endif boolean_t nonblocked_output; /* default is blocking output */ boolean_t output_blocked; /* if set error all writes */ int output_failures; /* when nonblocked output */ int max_output_retries; /* when nonblocked output */ int args_written; /* number of WRITE arguments output since CLEAR */ size_t lastarg_size; /* number of bytes in last arg processed */ ssize_t lastarg_sent; /* number of bytes actually written */ int readyforwhat; /* bit mask SOCKREADY_READ and/or SOCKREADY_WRITE */ uint4 current_events; /* bitmask SOCKPEND_ only within iosocket_wait, see pendingevent */ struct { unsigned int alive : 3; unsigned int idle : 3; unsigned int cnt : 3; unsigned int intvl : 3; unsigned int sndbuf : 3; unsigned int rcvbuf : 3; } options_state; int keepalive; /* SO_KEEPALIVE */ int keepidle; /* TCP_KEEPIDLE */ int keepcnt; /* TCP_KEEPCNT */ int keepintvl; /* TCP_KEEPINTVL */ int iobfsize; /* SO_SNDBUF */ } socket_struct; typedef struct socket_interrupt_type { ABS_TIME end_time; enum socket_which who_saved; int max_bufflen; int bytes_read; int chars_read; boolean_t end_time_valid; boolean_t ibfsize_specified; struct d_socket_struct_type *newdsocket; int wait_for_what; /* bit mask */ } socket_interrupt; typedef struct d_socket_struct_type { socket_interrupt sock_save_state; /* Saved state of interrupted IO */ boolean_t mupintr; /* We were mupip interrupted */ int4 current_socket; /* current socket index */ int4 n_socket; /* number of sockets */ uint4 waitcycle; /* count waits */ boolean_t ichset_specified; boolean_t ochset_specified; gtm_chset_t ichset_utf16_variant; /* Save the UTF-16 variant (BE/LE) for reuse */ gtm_chset_t ochset_utf16_variant; /* Save the UTF-16 variant (BE/LE) for reuse */ struct io_desc_struct *iod; /* Point back to main IO descriptor block */ struct socket_struct_type *socket[1]; /* Array size determined by gtm_max_sockets */ } d_socket_struct; boolean_t iosocket_bind(socket_struct *socketptr, int4 timepar, boolean_t update_bufsiz, boolean_t newversion); boolean_t iosocket_connect(socket_struct *socketptr, int4 timepar, boolean_t update_bufsiz); boolean_t iosocket_delimiter(unsigned char *delimiter_buffer, int4 delimiter_len, socket_struct *socketptr, boolean_t rm); void iosocket_idelim_conv(socket_struct *socketptr, gtm_chset_t to_chset); void iosocket_odelim_conv(socket_struct *socketptr, gtm_chset_t to_chset); void iosocket_delimiter_copy(socket_struct *from, socket_struct *to); boolean_t iosocket_switch(char *handle, int handle_len, d_socket_struct *from, d_socket_struct *to); int4 iosocket_handle(char *handle, int *len, boolean_t newhandle, d_socket_struct *dsocketptr); socket_struct *iosocket_create(char *sockaddr, uint4 bfsize, int file_des, boolean_t listen_specified); ssize_t iosocket_snr(socket_struct *socketptr, void *buffer, size_t maxlength, int flags, ABS_TIME *time_for_read); void iosocket_unsnr(socket_struct *socketptr, unsigned char *buffer, size_t len); ssize_t iosocket_snr_utf_prebuffer(io_desc *iod, socket_struct *socketptr, int flags, ABS_TIME *time_for_read, boolean_t wait_for_input); void iosocket_write_real(mstr *v, boolean_t convert_output); void iosocket_readfl_badchar(mval *vmvalptr, int datalen, int delimlen, unsigned char *delimptr, unsigned char *strend); boolean_t iosocket_listen_sock(socket_struct *socketptr, unsigned short len); void iosocket_close_one(d_socket_struct *dsocketptr, int index); int iosocket_accept(d_socket_struct *dsocketptr, socket_struct *socketptr, boolean_t selectfirst); ssize_t iosocket_output_buffer(socket_struct *socketptr); int iosocket_buffer_error(socket_struct *socketptr); boolean_t iosocket_tcp_keepalive(socket_struct *socketptr, int keepalive_opt, char * act, boolean_t freesocket); boolean_t iosocket_getsockopt(socket_struct *socketptr, char *optname, int option, int level, void *optvalue, GTM_SOCKLEN_TYPE *optvaluelen, boolean_t freesocket); int iosocket_setsockopt(socket_struct *socketptr, char *optname, int option, int level, void *optvalue, GTM_SOCKLEN_TYPE optvaluelen, boolean_t freesocket); #ifdef GTM_TLS void iosocket_tls(mval *optionmval, int4 timeoutarg, mval *tlsid, mval *password, mval *extraarg); #endif #endif fis-gtm-V7.0-005/sr_port/iotimer.h0000755000032200000250000000073614342376331015675 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, Inc. * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #define NO_M_TIMEOUT 0x0007FFFE fis-gtm-V7.0-005/sr_port/iott_dummy.c0000755000032200000250000000110614342376331016402 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "io.h" short iott_dummy(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout) { return 0; } fis-gtm-V7.0-005/sr_port/iott_escape.c0000644000032200000250000000471714342376331016517 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "io.h" #include "iottdef.h" #ifdef KEEP_zOS_EBCDIC #include "gtm_unistd.h" error_def(ERR_ASC2EBCDICCONV); #endif uchar_ptr_t iott_escape(uchar_ptr_t strin, uchar_ptr_t strtop, io_desc *io_ptr) { unsigned char *str; unsigned char esc_type; #ifdef KEEP_zOS_EBCDIC if ((DEFAULT_CODE_SET != io_ptr->in_code_set) && ( -1 == __etoa_l((char *)strin, strtop - strin) )) RTS_ERROR_ABT(VARLSTCNT(4) ERR_ASC2EBCDICCONV, 2, LEN_AND_LIT("__etoa_l")); #endif str = strin; esc_type = io_ptr->esc_state; while (esc_type < FINI) { switch (esc_type) { case START: assert(*str == ESC); esc_type = AFTESC; break; case AFTESC: switch(*str) { case ';': case '?': esc_type = SEQ2; break; case 'O': esc_type = SEQ4; break; case '[': esc_type = SEQ1; break; default: if (*str >= 0x30 && *str < 0x7F) esc_type = FINI; else if (*str > 0x1F && *str < 0x30) esc_type = SEQ2; else esc_type = BADESC; break; } break; case SEQ1: if (*str < 0x30 && *str > 0x1F) esc_type = SEQ3; else if (*str > 0x3F && *str < 0x7F) esc_type = FINI; else if (*str > 0x3F || *str < 0x30) esc_type = BADESC; break; case SEQ2: if (*str >= 0x30 && *str < 0x7F) esc_type = FINI; else if (*str > 0x2F || *str < 0x20) esc_type = BADESC; break; case SEQ3: if (*str > 0x3F && *str < 0x7F) esc_type = FINI; else if (*str > 0x2F || *str < 0x20) esc_type = BADESC; case SEQ4: if (*str >= 0x40 && *str < 0x7F) esc_type = FINI; else if (*str > 0x2F || *str < 0x20) esc_type = BADESC; break; default: assertpro(FALSE); } if (esc_type == BADESC || ++str >= strtop) break; } io_ptr->esc_state = esc_type; #ifdef KEEP_zOS_EBCDIC if ((DEFAULT_CODE_SET != io_ptr->in_code_set) && ( -1 == __atoe_l((char *)strin, strtop - strin) )) RTS_ERROR_ABT(VARLSTCNT(4) ERR_ASC2EBCDICCONV, 2, LEN_AND_LIT("__atoe_l")); #endif return str; } fis-gtm-V7.0-005/sr_port/iott_wteol.c0000644000032200000250000000400214342376331016374 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #ifdef UNIX #include "error.h" #endif #include "io.h" #include "iottdef.h" UNIX_ONLY(error_def(ERR_ZINTRECURSEIO);) /* essentially the same as ionl_wteol */ void iott_wteol(int4 val, io_desc *io_ptr) { mstr eol; int eol_cnt; boolean_t ch_set; UNIX_ONLY(d_tt_struct *tt_ptr;) assert(val); #ifdef UNIX tt_ptr = (d_tt_struct *)io_ptr->dev_sp; if (tt_ptr->mupintr) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZINTRECURSEIO); #endif ESTABLISH_GTMIO_CH(&io_ptr->pair, ch_set); io_ptr->esc_state = START; eol.len = STRLEN(NATIVE_TTEOL); eol.addr = (char *)NATIVE_TTEOL; for (eol_cnt = val; eol_cnt--; ) { io_ptr->dollar.x = 0; /* so that iott_write doesn't try to wrap (based on escape state and width) */ iott_write(&eol); } /* $X is maintained in VMS without the below assignment (resetting to 0) because the NATIVE_TTEOL is \015\012 * and the (\015) triggers appropriate maintenance of $X. In UNIX, NATIVE_TTEOL is \012, so * FILTER=CHARACTER effectively turns off all $X maintenance (except for WRAP logic). * In VMS the below assignment is not necessary, but harmless; it is always logically correct. */ io_ptr->dollar.x = 0; if (!(io_ptr->write_filter & CHAR_FILTER)) { /* If no FILTER and EOL, also maintain $Y; * If FILTER, dollarx() of the linefeed character \012 takes care of this maintenance. */ io_ptr->dollar.y += val; if (io_ptr->length) io_ptr->dollar.y %= io_ptr->length; } REVERT_GTMIO_CH(&io_ptr->pair, ch_set); return; } fis-gtm-V7.0-005/sr_port/iott_wtff.c0000755000032200000250000000223714342376331016223 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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 "io.h" #include "iottdef.h" #include "io_params.h" #include "error.h" GBLREF io_pair io_curr_device; static readonly unsigned char home_param_list[] = { (unsigned char)iop_x, 0, 0, 0, 0, (unsigned char)iop_y, 0, 0, 0, 0, (unsigned char)iop_clearscreen, (unsigned char)iop_eol }; static readonly mval home_params = DEFINE_MVAL_LITERAL(MV_STR, 0, 0, SIZEOF(home_param_list) - 1, (char *)home_param_list, 0, 0); void iott_wtff(void) { boolean_t ch_set; ESTABLISH_GTMIO_CH(&io_curr_device, ch_set); io_curr_device.out->esc_state = START; iott_use(io_curr_device.out, &home_params); REVERT_GTMIO_CH(&io_curr_device, ch_set); } fis-gtm-V7.0-005/sr_port/iott_wtone.c0000644000032200000250000000257214342376331016410 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "io.h" #ifdef UTF8_SUPPORTED #include "gtm_utf8.h" #endif GBLREF io_pair io_curr_device; GBLREF boolean_t gtm_utf8_mode; LITREF mstr chset_names[]; void iott_wtone(int v) { mstr temp; char p[1]; #ifdef UTF8_SUPPORTED unsigned char utf_buf[GTM_MB_LEN_MAX], *up; #endif io_desc *iod; UTF8_ONLY(error_def(ERR_BADCHSET);) if (!gtm_utf8_mode UTF8_ONLY(|| CHSET_M == io_curr_device.out->ochset)) { p[0] = (char)v; temp.len = 1; temp.addr = p; UTF8_ONLY(temp.char_len = 1;) } #ifdef UTF8_SUPPORTED else if (CHSET_UTF8 == io_curr_device.out->ochset) { up = UTF8_WCTOMB(v, utf_buf); temp.len = INTCAST(up - utf_buf); temp.addr = (char *)&utf_buf[0]; } else RTS_ERROR_ABT(VARLSTCNT(4) ERR_BADCHSET, 2, chset_names[io_curr_device.out->ochset].len, chset_names[io_curr_device.out->ochset].addr); #endif iott_write(&temp); return; } fis-gtm-V7.0-005/sr_port/ious_close.c0000755000032200000250000000123314342376331016355 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "io.h" #include "iousdef.h" void ious_close(io_desc *iod, mval *pp) { assert(iod->state == dev_open); ((void(*)())(((d_us_struct*)(iod->dev_sp))->disp->close))(); iod->state = dev_closed; } fis-gtm-V7.0-005/sr_port/ious_dummy.c0000755000032200000250000000110614342376331016402 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "io.h" short ious_dummy(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout) { return 0; } fis-gtm-V7.0-005/sr_port/ious_flush.c0000755000032200000250000000116714342376331016377 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "io.h" #include "iousdef.h" void ious_flush(io_desc *iod) { assert(iod->state == dev_open); ((void(*)())(((d_us_struct*)(iod->dev_sp))->disp->flush))(); } fis-gtm-V7.0-005/sr_port/ious_use.c0000755000032200000250000000117514342376331016051 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "io.h" #include "iousdef.h" void ious_use(io_desc *iod, mval *pp) { assert(iod->state == dev_open); ((void(*)())(((d_us_struct*)(iod->dev_sp))->disp->use))(); } fis-gtm-V7.0-005/sr_port/ious_wteol.c0000755000032200000250000000120614342376331016402 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2004 Sanchez Computer Associates, 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 "io.h" #include "iousdef.h" void ious_wteol(int4 x, io_desc *iod) { assert(iod->state == dev_open); ((void(*)())(((d_us_struct*)(iod->dev_sp))->disp->wteol))(x); } fis-gtm-V7.0-005/sr_port/ious_wtff.c0000755000032200000250000000115314342376331016217 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "io.h" #include "iousdef.h" GBLREF io_pair io_curr_device; void ious_wtff() { (((d_us_struct*)(io_curr_device.out->dev_sp))->disp->wtff)(); } fis-gtm-V7.0-005/sr_port/iousdef.h0000755000032200000250000000130714342376331015656 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, Inc. * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #define MAX_US_READ 128 /* ***************************************************** */ /* ********* structure for user device driver ********** */ /* ***************************************************** */ typedef struct { dev_dispatch_struct *disp; }d_us_struct; fis-gtm-V7.0-005/sr_port/is_canonic_name.c0000644000032200000250000003036614342376331017324 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_ctype.h" #include "is_canonic_name.h" #ifdef DEBUG #include "subscript.h" #endif #ifdef UTF8_SUPPORTED #include "gtm_utf8.h" GBLREF boolean_t badchar_inhibit; error_def(ERR_BADCHAR); #endif /* * ----------------------------------------------- * is_canonic_name() * validate a variable name (unsubscripted or subscripted). * * Arguments: * src - Pointer to Source Name string mval * subscripts - Pointer to sequence number of subscript to find & return of subscript count * start_off - Pointer offset of the component requested by op_fnqsubscript * stop_off - Pointer offset of the end of the component requested by op_fnqsubscript * Return: * boolean_t - TRUE indicates good name; FALSE indicates defective * ----------------------------------------------- */ boolean_t is_canonic_name(mval *src, int *subscripts, int *start_off, int *stop_off) { gv_name_and_subscripts start_buff, stop_buff; int seq, contains_env, i; int *start, *stop; /* determine which buffer to use */ DETERMINE_BUFFER(src, start_buff, stop_buff, start, stop); seq = *subscripts; if (parse_gv_name_and_subscripts(src, subscripts, start, stop, &contains_env)) { # ifdef DEBUG /* Check that the sequence of start[i] and stop[i] is * always increasing. There should never be a case where * a subscript starts at an offset into src greater than * where it stops. */ for (i = 0; i <= contains_env + *subscripts; i++) assert(start[i] < stop[i]); # endif /* Make sure the user isn't trying to index too far into the * array or trying to index a negative offset. * Remember contains_env is 1 if there is an environment, * and {start,stop}[0] contain the bounds for the environment. */ if ((0 > contains_env + seq) || (*subscripts < seq)) *start_off = *stop_off = 0; else { *start_off = start[contains_env + seq]; *stop_off = stop[contains_env + seq]; } if ('"' == src->str.addr[*start_off]) *start_off++; if ((0 < *stop_off) && ('"' == src->str.addr[*stop_off - 1])) *stop_off--; return TRUE; } return FALSE; } /* * ----------------------------------------------- * parse_gv_name_and_subscripts() * Validates a global variable name and returns all the corresponding subscripts (unsubscripted or subscripted). * * Arguments: * src - Pointer to Source Name string mval. * subscripts - Pointer returns the subscript count. * start - Assumed to be an array of length [MAX_[L,G]VSUBSCRIPTS + 1 + 2], returns the start * of every subscript. If contains_env is true, the index 0 corresponds to the * first environment subscript, 1 corresponds to the name, and 2 -> *subscripts * corresponds to the keys. * stop - Assumed to be an array of length [MAX_[L,G]VSUBSCRIPTS + 1 + 2], returns the end * of every subscript, corresponding to each entry in start. * contains_env - Pointer returns whether there was an environment or not. This is an integer * for ease of indexing into the start and stop arrays, i.e. start[contains_env] * will always return the start of the name. * Return: * boolean_t - TRUE indicates good name; FALSE indicates defective * ----------------------------------------------- */ boolean_t parse_gv_name_and_subscripts(mval *src, int *subscripts, int *start, int *stop, int *contains_env) { char term; int envpart; boolean_t instring, innum; int isrc; char letter; int point; char previous; int seq; int state; int subs_count; int utf8_len; int subs_max; boolean_t gvn; register char *cpt; char *lastcpt; enum { BEFORE_NAME, CHECK_FOR_ENVIRONMENT, DISPATCH_FOR_STARTING_COMPONENT, IN_STRING, IN_NUMBER, EXPECT_FIRST_LETTER_OF_NAME, EXPECT_NEXT_LETTER_OF_NAME, IN_CHAR_FUNC, END_OF_PROCESSING }; gvn = ((0 < src->str.len) && ('^' == src->str.addr[0])); MV_FORCE_STR(src); seq = *subscripts; state = BEFORE_NAME; subs_count = -1; *contains_env = 0; subs_max = gvn ? MAX_GVSUBSCRIPTS : MAX_LVSUBSCRIPTS; isrc = 0; lastcpt = src->str.addr + src->str.len; for (cpt = src->str.addr; (cpt < lastcpt) && (subs_count < subs_max);) { letter = *cpt; switch (state) { case BEFORE_NAME: /* start of name */ if ('^' == letter) /* before start of name */ { state = CHECK_FOR_ENVIRONMENT; /* check for environment */ break; } if (('%' == letter) || ISALPHA_ASCII(letter)) { *start++ = isrc; state = EXPECT_NEXT_LETTER_OF_NAME; /* rest of name */ break; } return FALSE; case CHECK_FOR_ENVIRONMENT: /* global name */ if (('%' == letter) ||ISALPHA_ASCII(letter)) /* found ^ allow environment */ { /* found ^ allow environment */ *start++ = isrc; state = EXPECT_NEXT_LETTER_OF_NAME; /* rest of name */ break; } if (('|' == letter) || ('[' == letter)) { *contains_env = 1; term = (letter == '[') ? ']' : letter; envpart = 0; state = DISPATCH_FOR_STARTING_COMPONENT; /* process environment */ break; } return FALSE; case DISPATCH_FOR_STARTING_COMPONENT: /* dispatch for starting a component */ point = 0; instring = FALSE; if (1 < envpart) return FALSE; /* too many environment components */ if (')' == term) subs_count++; /* new subscript */ else envpart++; /* next environment component */ if ((0 < subs_count) || (1 == envpart)) *start++ = isrc; if ('"' == letter) { instring = TRUE; state = IN_STRING; /* string */ break; } if ('$' ==letter) { state = IN_CHAR_FUNC; /* $[z]char() */ break; } if ('0' == letter) /* Canonic number cannot start with 0 unless is single char */ { ++isrc; if (++cpt < lastcpt) letter = *cpt; else return FALSE; /* Cannot end with "0" */ if (term == letter) /* end or name */ state = (')' == term) ? END_OF_PROCESSING : EXPECT_FIRST_LETTER_OF_NAME; else if (',' != letter) return FALSE; /* Not a single char number */ *stop++ = isrc; break; } if ((innum = ISDIGIT_ASCII(letter)) || ('-' == letter) || ('.' == letter)) /* WARNING: assignment */ { /* note: innum cannot be TRUE unless dispatch state is IN_NUMBER; can be FALSE too * order of evaluation for the if matters; extra assignment wasted if only about to error */ if ('.' == letter) point++; previous = letter; state = IN_NUMBER; /* numeric */ break; } return FALSE; case IN_STRING: /* [quoted] string */ if ('"' == letter) /* in string */ { instring = !instring; if (instring) break; if (cpt + 1 >= lastcpt) return FALSE; if ('_' != *(cpt+1)) break; isrc++; cpt++; if (++isrc, ++cpt < lastcpt) letter = *cpt; else return FALSE; if ('$' != letter) return FALSE; state = IN_CHAR_FUNC; /* $[z]char() */ break; } if (!instring) { if (',' == letter) state = DISPATCH_FOR_STARTING_COMPONENT; /* on to next */ else if (term == letter) /* end or name */ state = (')' == term) ? END_OF_PROCESSING : EXPECT_FIRST_LETTER_OF_NAME; else return FALSE; if ((0 < subs_count) || (1 == envpart)) *stop++ = isrc; } break; case IN_NUMBER: /* numeric */ if (ISDIGIT_ASCII(letter)) /* in number */ { if (('-' == previous) && ('0' == letter)) return FALSE; previous = letter; innum = TRUE; break; } if ('.' == letter) { if ((++point > 1)) return FALSE; previous = letter; break; } if (!innum || (point && ('0' == previous))) return FALSE; if (',' == letter) state = DISPATCH_FOR_STARTING_COMPONENT; /* next */ else if (term == letter) /* end or name */ state = (')' == term) ? END_OF_PROCESSING : EXPECT_FIRST_LETTER_OF_NAME; else return FALSE; if ((0 < subs_count) || (1 == envpart)) *stop++ = isrc; previous = letter; break; case EXPECT_FIRST_LETTER_OF_NAME: /* expect first letter of name */ if (('%' == letter) || ISALPHA_ASCII(letter)) { *start++ = isrc; state = EXPECT_NEXT_LETTER_OF_NAME; /* rest of name */ break; } return FALSE; case EXPECT_NEXT_LETTER_OF_NAME: /* expect next letter of name */ if ('(' == letter) { term = ')'; envpart = 1; subs_count = 0; state = DISPATCH_FOR_STARTING_COMPONENT; /* done with name */ *stop++ = isrc; } else if (!ISALNUM_ASCII(letter)) return FALSE; break; case IN_CHAR_FUNC: /* $[Z]CHAR() */ previous = letter; /* in $CHAR() - must be ASCII */ if (('Z' == letter) || ('z' == letter)) { ++isrc; if (++cpt < lastcpt) letter = *cpt; else return FALSE; previous = 'Z'; } if (!(('C' == letter) || ('c' == letter))) return FALSE; ++isrc; if (++cpt < lastcpt) letter = *cpt; else return FALSE; if (('H' == letter) || ('h' == letter)) { ++isrc; if (++cpt < lastcpt) letter = *cpt; else return FALSE; if (!(('A' == letter) || ('a' == letter) || (('(' == letter) && ('Z' == previous)))) return FALSE; } else if ('Z' == previous) return FALSE; if ('(' != letter) { ++isrc; if (++cpt < lastcpt) letter = *cpt; else return FALSE; if (!(('R' == letter) || ('r' == letter))) return FALSE; ++isrc; if (++cpt < lastcpt) letter = *cpt; else return FALSE; } if ('(' != letter) return FALSE; for (++isrc,++cpt, innum = FALSE; cpt < lastcpt; isrc++, cpt++) { letter = *cpt; if (ISDIGIT_ASCII(letter)) { if (!innum && ('0' == previous)) return FALSE; if ('0' == letter) previous = letter; else innum = TRUE; continue; } if (!((',' == letter) || (')' == letter))) return FALSE; if (!innum && ('0' != previous)) return FALSE; previous = letter; ++isrc; if (++cpt < lastcpt) letter = *cpt; else return FALSE; if (')' == previous) break; if (!ISDIGIT_ASCII(letter)) return FALSE; if ('0' == letter) { isrc--; cpt--; innum = FALSE; } else innum = TRUE; } if (cpt > lastcpt) return FALSE; if ('_' == letter) { ++isrc; if (++cpt < lastcpt) letter = *cpt; else return FALSE; if ('$' == letter) break; if ('"' != letter) return FALSE; instring = TRUE; state = IN_STRING; /* back to string */ break; } if (',' == letter) state = DISPATCH_FOR_STARTING_COMPONENT; else if (term == letter) /* end or name */ state = (')' == term) ? END_OF_PROCESSING : EXPECT_FIRST_LETTER_OF_NAME; else return FALSE; *stop++ = isrc; break; case END_OF_PROCESSING: /* end of subscript but no closing paren - ")" */ return FALSE; } # ifdef UTF8_SUPPORTED if (!gtm_utf8_mode || (0 == (letter & 0x80))) isrc++, cpt++; else if (0 < (utf8_len = UTF8_MBFOLLOW((isrc++, cpt++)))) { /* multi-byte increment */ assert(4 > utf8_len); if (0 > utf8_len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_BADCHAR, 4, cpt-1, LEN_AND_LIT(UTF8_NAME)); isrc += utf8_len; cpt += utf8_len; } # endif NON_UTF8_ONLY({cpt++; isrc++}); } if ((END_OF_PROCESSING != state) && (EXPECT_NEXT_LETTER_OF_NAME != state)) return FALSE; assert((0 < subs_count) || ((EXPECT_NEXT_LETTER_OF_NAME == state) && (-1 == subs_count))); if (EXPECT_NEXT_LETTER_OF_NAME == state) { subs_count = 0; *stop = isrc; } assert((('^' == src->str.addr[0]) ? MAX_GVSUBSCRIPTS : MAX_LVSUBSCRIPTS) > subs_count); assert((0 < isrc) && (isrc == src->str.len) && (cpt == lastcpt)); *subscripts = subs_count; return TRUE; } fis-gtm-V7.0-005/sr_port/is_canonic_name.h0000755000032200000250000000370314342376331017327 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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. * * * ****************************************************************/ /* Technically, MAX_LVSUBSCRIPTS and MAX_GVSUBSCRIPTS could be different, but we don't know * which one to use until we determine whether src is a global or local name. */ typedef union { int lv[MAX_LVSUBSCRIPTS + 1 + 2]; /* 1 for the name, 2 for the environment components */ int gv[MAX_LVSUBSCRIPTS + 1 + 2]; } gv_name_and_subscripts; #define DETERMINE_BUFFER(src, start_buff, stop_buff, start, stop) \ MBSTART { \ if ((0 < src->str.len) && ('^' == src->str.addr[0])) \ { \ start = start_buff.gv; \ stop = stop_buff.gv; \ } else \ { \ start = start_buff.lv; \ stop = stop_buff.lv; \ } \ } MBEND #define NOCANONICNAME_ERROR(MVAL) \ MBSTART { \ int error_len; \ unsigned char *error_chr; \ \ error_len = ZWR_EXP_RATIO((MVAL)->str.len); \ ENSURE_STP_FREE_SPACE(error_len); \ DBG_MARK_STRINGPOOL_UNEXPANDABLE; \ format2zwr((sm_uc_ptr_t)(MVAL)->str.addr, (MVAL)->str.len, \ (uchar_ptr_t)stringpool.free, &error_len); \ DBG_MARK_STRINGPOOL_EXPANDABLE \ error_chr = stringpool.free; \ stringpool.free += error_len; \ rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NOCANONICNAME, 2, \ error_len, error_chr); \ } MBEND boolean_t is_canonic_name(mval *src, int *subscripts, int *start_off, int *stop_off); boolean_t parse_gv_name_and_subscripts(mval *src, int *subscripts, int *start, int *stop, int *contains_env); fis-gtm-V7.0-005/sr_port/is_equ.c0000755000032200000250000000273614342376331015507 0ustar librarygtc/**************************************************************** * * * 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 "gtm_string.h" int is_equ(mval *u,mval *v) { int land, lor, utyp, vtyp; utyp = u->mvtype; vtyp = v->mvtype; land = utyp & vtyp; lor = utyp | vtyp; if ((land & MV_NM) != 0 && (lor & MV_NUM_APPROX) == 0) { /* at this point, the mval's are both exact numbers, we can do a numeric comparison */ /* If they are both integers, compare only the relevant cells */ if (land & MV_INT) return (u->m[1] == v->m[1]); /* If one is an integer and the other is not, the two values cannot be equal */ if (lor & MV_INT) return 0; /* They are both decimal floating numbers, do a full comparison */ return ((((mval_b *)u)->sgne == ((mval_b *)v)->sgne) && (u->m[1] == v->m[1]) && (u->m[0]==v->m[0])); } /* At least one of the numbers is not in numeric form or is not a cannoical number, do a string compare */ MV_FORCE_STR(u); MV_FORCE_STR(v); if ((u->str.len != v->str.len) || (u->str.len && (u->str.addr != v->str.addr) && memcmp(u->str.addr, v->str.addr, u->str.len))) return 0; else return 1; } fis-gtm-V7.0-005/sr_port/is_file_identical.h0000755000032200000250000000125214342376331017645 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #ifndef IS_FILE_IDENTICAL_H_INCLUDED #define IS_FILE_IDENTICAL_H_INCLUDED bool is_file_identical(char *filename1, char *filename2); #include "gtm_stat.h" #endif fis-gtm-V7.0-005/sr_port/is_gdid.h0000755000032200000250000000164614342376331015630 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #ifndef IS_GDID_H_INCLUDED #define IS_GDID_H_INCLUDED #include "gtm_stat.h" bool is_gdid_file_identical(gd_id_ptr_t fid, char *filename, int4 filelen); bool is_gdid_identical(gd_id_ptr_t fid1, gd_id_ptr_t fid2); bool is_gdid_stat_identical(gd_id_ptr_t fid, struct stat *stat_buf); void set_gdid_from_stat(gd_id_ptr_t fid, struct stat *stat_buf); uint4 filename_to_id(gd_id_ptr_t fid, char *filename); #endif fis-gtm-V7.0-005/sr_port/is_ident.c0000755000032200000250000000231614342376331016012 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2006 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 "toktyp.h" LITREF char ctypetab[NUM_CHARS]; char is_ident(mstr *v) { int4 y; char *c, *c_top, ret; signed char ch; bool dig_start; ret = TRUE; c = v->addr; c_top = c + v->len; if (!v->len || (ch = *c++) < 0) ret = 0; else { switch (y = ctypetab[ch]) { case TK_LOWER: case TK_PERCENT: case TK_UPPER: case TK_DIGIT: dig_start = y == TK_DIGIT; for ( ; c < c_top; c++) { ch = *c; if (ch < 0) break; y = ctypetab[ch]; if (y != TK_DIGIT && dig_start) break; if (y != TK_DIGIT && y != TK_UPPER && y != TK_LOWER) break; } if (c == c_top) { /* we have an ident */ ret = 1 + dig_start; } else { ret = 0; } break; default: ret = 0; } } return ret; } fis-gtm-V7.0-005/sr_port/is_proc_alive.h0000755000032200000250000000145014342376331017035 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001, 2015 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. * * * ****************************************************************/ #ifndef IS_PROC_ALIVE_INCLUDED #define IS_PROC_ALIVE_INCLUDED #define IMAGECNT(imagecnt) VMS_ONLY(imagecnt) UNIX_ONLY(0) #ifdef VMS bool is_proc_alive(uint4 pid, uint4 imagecnt); #elif defined(UNIX) bool is_proc_alive(int4 pid, int4 imagecnt); #endif #endif /* IS_PROC_ALIVE_INCLUDED */ fis-gtm-V7.0-005/sr_port/jfh_from_jnl_info.c0000755000032200000250000000772614342376331017676 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2016 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_string.h" #include "gtm_time.h" #include "gdsroot.h" #include "gdsbt.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "gtmcrypt.h" GBLREF jnl_process_vector *prc_vec; GBLREF jnl_gbls_t jgbl; #ifdef UNIX GBLREF int4 strm_index; #endif void jfh_from_jnl_info(jnl_create_info *info, jnl_file_header *header) { int idx; trans_num db_tn; /**** We will write journal file header, epoch and eof in order ****/ /* Write the file header */ memset((char *)header, 0, JNL_HDR_LEN); /* note: In Unix, this means we 0-fill 2K REAL_JNL_HDR_LEN + 62K padding */ memcpy(header->label, JNL_LABEL_TEXT, STR_LIT_LEN(JNL_LABEL_TEXT)); header->is_little_endian = GTM_IS_LITTLE_ENDIAN; assert(NULL != prc_vec); JNL_WHOLE_FROM_SHORT_TIME(prc_vec->jpv_time, jgbl.gbl_jrec_time); memcpy(&header->who_created, (unsigned char*)prc_vec, SIZEOF(jnl_process_vector)); memcpy(&header->who_opened, (unsigned char*)prc_vec, SIZEOF(jnl_process_vector)); /* EPOCHs are written unconditionally even for NOBEFORE_IMAGE journaling */ header->end_of_data = JNL_HDR_LEN + PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN; header->max_jrec_len = info->max_jrec_len; header->bov_timestamp = jgbl.gbl_jrec_time; header->eov_timestamp = jgbl.gbl_jrec_time; db_tn = info->csd->trans_hist.curr_tn; header->bov_tn = db_tn; header->eov_tn = db_tn; header->before_images = info->before_images; /* Note that in case of MUPIP JOURNAL -ROLLBACK, we need to set header->repl_state to repl_open although replication * is currently not ON in the database. This is so future ROLLBACKs know this journal is replication enabled. * Assert that this code can be reached only for MUPIP JOURNAL -ROLLBACK -BACKWARD. In case of MUPIP JOURNAL -ROLLBACK * -FORWARD, journaling would have been turned OFF in the database at the start. */ assert(!jgbl.mur_options_forward); header->repl_state = jgbl.mur_rollback ? repl_open : info->repl_state; header->data_file_name_length = info->fn_len; memcpy(header->data_file_name, info->fn, info->fn_len); header->data_file_name[info->fn_len] = '\0'; header->alignsize = info->alignsize; header->autoswitchlimit = info->autoswitchlimit; header->epoch_interval = info->epoch_interval; header->start_seqno = info->reg_seqno; header->end_seqno = info->reg_seqno; header->prev_jnl_file_name_length = info->prev_jnl_len; ; memcpy(header->prev_jnl_file_name, info->prev_jnl, info->prev_jnl_len); header->prev_jnl_file_name[info->prev_jnl_len] = '\0'; assert(JNL_ALLOC_MIN <= info->alloc); header->jnl_alq = info->alloc; header->virtual_size = info->alloc; header->jnl_deq = info->extend; header->checksum = info->checksum; GTMCRYPT_COPY_ENCRYPT_SETTINGS(info, header); # ifdef UNIX if (INVALID_SUPPL_STRM != strm_index) { assert(MAX_SUPPL_STRMS == ARRAYSIZE(header->strm_start_seqno)); for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) header->strm_start_seqno[idx] = info->csd->strm_reg_seqno[idx]; if (jgbl.forw_phase_recovery) { /* If MUPIP JOURNAL -ROLLBACK, might need to do additional processing. See macro definition for comments */ MUR_ADJUST_STRM_REG_SEQNO_IF_NEEDED(info->csd, header->strm_start_seqno); } for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) header->strm_end_seqno[idx] = header->strm_start_seqno[idx]; } else { memset(&header->strm_start_seqno[0], 0, SIZEOF(header->strm_start_seqno)); memset(&header->strm_end_seqno[0], 0, SIZEOF(header->strm_end_seqno)); } # endif } fis-gtm-V7.0-005/sr_port/jmp_opto.c0000755000032200000250000003004614342376331016044 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_stdio.h" #include "compiler.h" #include "opcode.h" #include "mdq.h" #include "jmp_opto.h" #include "gtmdbglvl.h" #include "cdbg_dump.h" LITREF octabstruct oc_tab[]; /* op-code table */ GBLREF triple t_orig; /* head of triples */ GBLREF uint4 gtmDebugLevel; #define IND_NOT_DEFINED ((unsigned char)-2) #define JOPT_NO_OPT 1 #define JOPT_REP_JMP 2 #define JOPT_REF_NXT_TRP 3 #define NO_ENTRY ((unsigned char)-1) #define NUM_JO_TBL_ELE 11 #define PTR_NOT_DEFINED 0 typedef struct { unsigned int opcode; unsigned int index; unsigned int opto_flag[NUM_JO_TBL_ELE]; } jump_opto_struct; LITDEF readonly jump_opto_struct jump_opto_table[NUM_JO_TBL_ELE] = { { OC_JMP, /* opcode */ 0, /* index */ { JOPT_REP_JMP, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_NO_OPT, /* OC_JMPEQU */ JOPT_NO_OPT, /* OC_JMPNEQ */ JOPT_NO_OPT, /* OC_JMPGTR */ JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_NO_OPT, /* OC_JMPLSS */ JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ JOPT_NO_OPT /* RET */ } }, { OC_JMPTSET, /* opcode */ 1, /* index */ { JOPT_REP_JMP, /* OC_JMP */ JOPT_REP_JMP, /* OC_JMPTSET */ JOPT_REF_NXT_TRP, /* OC_JMPTCLR */ JOPT_NO_OPT, /* OC_JMPEQU */ JOPT_NO_OPT, /* OC_JMPNEQ */ JOPT_NO_OPT, /* OC_JMPGTR */ JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_NO_OPT, /* OC_JMPLSS */ JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ JOPT_NO_OPT /* RET */ } }, { OC_JMPTCLR, /* opcode */ 2, /* index */ { JOPT_REP_JMP, /* OC_JMP */ JOPT_REF_NXT_TRP, /* OC_JMPTSET */ JOPT_REP_JMP, /* OC_JMPTCLR */ JOPT_NO_OPT, /* OC_JMPEQU */ JOPT_NO_OPT, /* OC_JMPNEQ */ JOPT_NO_OPT, /* OC_JMPGTR */ JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_NO_OPT, /* OC_JMPLSS */ JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ JOPT_NO_OPT /* RET */ } }, { OC_JMPEQU, /* opcode */ 3, /* index */ { JOPT_REP_JMP, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_REP_JMP, /* OC_JMPEQU */ JOPT_REF_NXT_TRP, /* OC_JMPNEQ */ JOPT_REF_NXT_TRP, /* OC_JMPGTR */ JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_REF_NXT_TRP, /* OC_JMPLSS */ JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ JOPT_NO_OPT /* RET */ } }, { OC_JMPNEQ, /* opcode */ 4, /* index */ { JOPT_REP_JMP, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_REF_NXT_TRP, /* OC_JMPEQU */ JOPT_REP_JMP, /* OC_JMPNEQ */ JOPT_NO_OPT, /* OC_JMPGTR */ JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_NO_OPT, /* OC_JMPLSS */ JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ JOPT_NO_OPT /* RET */ } }, { OC_JMPGTR, /* opcode */ 5, /* index */ { JOPT_REP_JMP, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_REF_NXT_TRP, /* OC_JMPEQU */ JOPT_REP_JMP, /* OC_JMPNEQ */ JOPT_REP_JMP, /* OC_JMPGTR */ JOPT_REF_NXT_TRP, /* OC_JMPLEQ */ JOPT_REF_NXT_TRP, /* OC_JMPLSS */ JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ JOPT_NO_OPT /* RET */ } }, { OC_JMPLEQ, /* opcode */ 6, /* index */ { JOPT_REP_JMP, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_NO_OPT, /* OC_JMPEQU */ JOPT_NO_OPT, /* OC_JMPNEQ */ JOPT_REF_NXT_TRP, /* OC_JMPGTR */ JOPT_REP_JMP, /* OC_JMPLEQ */ JOPT_NO_OPT, /* OC_JMPLSS */ JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ JOPT_NO_OPT /* RET */ } }, { OC_JMPLSS, /* opcode */ 7, /* index */ { JOPT_REP_JMP, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_REF_NXT_TRP, /* OC_JMPEQU */ JOPT_REP_JMP, /* OC_JMPNEQ */ JOPT_REF_NXT_TRP, /* OC_JMPGTR */ JOPT_REP_JMP, /* OC_JMPLEQ */ JOPT_REP_JMP, /* OC_JMPLSS */ JOPT_REF_NXT_TRP, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ JOPT_NO_OPT /* RET */ } }, { OC_JMPGEQ, /* opcode */ 8, /* index */ { JOPT_REP_JMP, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_NO_OPT, /* OC_JMPEQU */ JOPT_NO_OPT, /* OC_JMPNEQ */ JOPT_NO_OPT, /* OC_JMPGTR */ JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_REF_NXT_TRP, /* OC_JMPLSS */ JOPT_REP_JMP, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ JOPT_NO_OPT /* RET */ } }, { OC_ZHALT, /* opcode */ 9, /* index */ { JOPT_NO_OPT, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_NO_OPT, /* OC_JMPEQU */ JOPT_NO_OPT, /* OC_JMPNEQ */ JOPT_NO_OPT, /* OC_JMPGTR */ JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_NO_OPT, /* OC_JMPLSS */ JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ JOPT_NO_OPT /* RET */ } }, { OC_RET, /* opcode */ 10, /* index */ { JOPT_NO_OPT, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_NO_OPT, /* OC_JMPEQU */ JOPT_NO_OPT, /* OC_JMPNEQ */ JOPT_NO_OPT, /* OC_JMPGTR */ JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_NO_OPT, /* OC_JMPLSS */ JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ JOPT_NO_OPT /* RET */ } } }; STATICDEF unsigned int *jo_ptr_ray[OPCODE_COUNT]; STATICDEF unsigned int jo_ind_ray[OPCODE_COUNT]; STATICFNDCL void jo_get_ptrs(unsigned int op); STATICFNDEF void jo_get_ptrs(unsigned int op) { const jump_opto_struct *j, *j_top; for (j = &jump_opto_table[0], j_top = j + NUM_JO_TBL_ELE; j < j_top; j++) { if (j->opcode == op) { jo_ind_ray[op] = j->index; jo_ptr_ray[op] = (unsigned int *)&j->opto_flag[0]; return; } } jo_ind_ray[op] = NO_ENTRY; jo_ptr_ray[op] = (unsigned int *)NO_ENTRY; } STATICDEF const readonly oprtype null_operand; /************************************************************************************************************ NOTE: We may which to modify the lookup method at some point in the future. B. Shear suggests nested switch statements. Another option is to do the lookup each time. ***********************************************************************************************************/ void jmp_opto(void) { unsigned int **clrp1, *clrp2, **clrtop1, *clrtop2, i, *p; tbp *b; triple *ct, *cur_trip, *jump_trip, *next_trip, *ref_trip, *terminal_trip; void get_jo_ptrs(); for (clrp1 = &jo_ptr_ray[0], clrtop1 = clrp1 + OPCODE_COUNT; clrp1 < clrtop1; clrp1++) *clrp1 = (unsigned int *)NO_ENTRY; for (clrp2 = &jo_ind_ray[0], clrtop2 = clrp2 + OPCODE_COUNT; clrp2 < clrtop2; clrp2++) *clrp2 = NO_ENTRY; COMPDBG(PRINTF("\n\n\n***************************** Begin jmp_opto scan ******************************\n");); dqloop(&t_orig, exorder, cur_trip) { COMPDBG(PRINTF(" ************************ Triple Start **********************\n");); COMPDBG(cdbg_dump_triple(cur_trip, 0);); if (OC_GVSAVTARG == cur_trip->opcode) { /* Look for an adjacent and therefore superfluous GVRECTARG */ for (next_trip = cur_trip->exorder.fl; oc_tab[next_trip->opcode].octype & OCT_CGSKIP; next_trip = next_trip->exorder.fl) ; if ((OC_GVRECTARG == next_trip->opcode) && (next_trip->operand[0].oprval.tref == cur_trip) && (next_trip->jmplist.que.fl == &(next_trip->jmplist))) { COMPDBG(PRINTF("jmp_opto: NOOPing OC_GVRECTARG opcode at triple addres 0x"lvaddr"\n", next_trip);); next_trip->opcode = OC_NOOP; next_trip->operand[0].oprclass = next_trip->operand[1].oprclass = NO_REF; cur_trip = cur_trip->exorder.bl; /* in case there are more than one in a row */ } continue; } if (OC_GVRECTARG == cur_trip->opcode) { /* Look for a second effectively adjacent GVRECTARG that duplicates this one */ for (next_trip = cur_trip->exorder.fl; oc_tab[next_trip->opcode].octype & OCT_CGSKIP; next_trip = next_trip->exorder.fl) ; if ((OC_GVRECTARG == next_trip->opcode) && (next_trip->operand[0].oprval.tref == cur_trip->operand[0].oprval.tref) && (next_trip->jmplist.que.fl == &(next_trip->jmplist))) { COMPDBG(PRINTF("jmp_opto: NOOPing OC_GVRECTARG opcode at triple addres 0x"lvaddr"\n", next_trip);); next_trip->opcode = OC_NOOP; next_trip->operand[0].oprclass = next_trip->operand[1].oprclass = NO_REF; cur_trip = cur_trip->exorder.bl; /* in case there are more than one in a row */ } continue; } if ((oc_tab[cur_trip->opcode].octype & OCT_JUMP) && (OC_CALL != cur_trip->opcode) && (OC_CALLSP != cur_trip->opcode)) { assert(OPCODE_COUNT > cur_trip->opcode); if (PTR_NOT_DEFINED == (p = jo_ptr_ray[cur_trip->opcode])) /* note assignment */ { jo_get_ptrs(cur_trip->opcode); p = jo_ptr_ray[cur_trip->opcode]; } assert(TJMP_REF == cur_trip->operand[0].oprclass); jump_trip = cur_trip->operand[0].oprval.tref; if (IND_NOT_DEFINED == (i = jo_ind_ray[jump_trip->opcode])) /* note assignment */ { jo_get_ptrs(jump_trip->opcode); i = jo_ind_ray[jump_trip->opcode]; } while ((IND_NOT_DEFINED != i) && (NO_ENTRY != i)) { switch(p[i]) { case JOPT_NO_OPT: i = NO_ENTRY; break; case JOPT_REF_NXT_TRP: if (cur_trip->src.line == jump_trip->src.line) { dqloop(&jump_trip->jmplist, que, b) { if (b->bpt = cur_trip) { dqdel(b, que); break; } } dqins(&jump_trip->exorder.fl->jmplist, que, b); cur_trip->operand[0].oprval.tref = jump_trip->exorder.fl; jump_trip = cur_trip->operand[0].oprval.tref; if (IND_NOT_DEFINED == (i = jo_ind_ray[jump_trip->opcode])) /* assignmnt */ { jo_get_ptrs(jump_trip->opcode); i = jo_ind_ray[jump_trip->opcode]; } COMPDBG(PRINTF("jmp_opto: JOPT_REF_NXT_TRP optimization on triple " "0x"lvaddr"\n", cur_trip);); } else i = NO_ENTRY; break; case JOPT_REP_JMP: if (cur_trip->src.line == jump_trip->src.line) { assert(TJMP_REF == jump_trip->operand[0].oprclass); dqloop(&jump_trip->jmplist, que, b) { if (b->bpt = cur_trip) { dqdel(b, que); break; } } dqins(&jump_trip->operand[0].oprval.tref->jmplist, que, b); cur_trip->operand[0] = jump_trip->operand[0]; jump_trip = cur_trip->operand[0].oprval.tref; if (IND_NOT_DEFINED == (i = jo_ind_ray[jump_trip->opcode])) /* assgnmnt */ { jo_get_ptrs(jump_trip->opcode); i = jo_ind_ray[jump_trip->opcode]; } COMPDBG(PRINTF("jmp_opto: JOPT_REP_JMP optimization on triple " "0x"lvaddr"\n", cur_trip);); } else i = NO_ENTRY; break; default: assertpro(FALSE && p[i]); break; } /* switch */ } /* while */ terminal_trip = cur_trip->exorder.fl; while ((oc_tab[cur_trip->opcode].octype & OCT_JUMP) && (OC_CALL != cur_trip->opcode) && (OC_CALLSP != cur_trip->opcode) && (TJMP_REF == cur_trip->operand[0].oprclass)) { for ((ref_trip = cur_trip->operand[0].oprval.tref); (oc_tab[ref_trip->opcode].octype & OCT_CGSKIP); (ref_trip = ref_trip->exorder.fl)) ; if (ref_trip == terminal_trip) { cur_trip->opcode = OC_NOOP; cur_trip->operand[0] = null_operand; COMPDBG(PRINTF("jmp_opto: Triple removed at address 0x"lvaddr"\n", cur_trip);); cur_trip = cur_trip->exorder.bl; } else break; } cur_trip = terminal_trip->exorder.bl; } /* if */ } /* dqloop */ # ifdef DEBUG /* If debug and compiler debugging is enabled, run through the triples again to show what we * have done to them. */ if (gtmDebugLevel & GDL_DebugCompiler) { PRINTF(" \n\n\n\n****************************** After jmp_opto scan *****************************\n"); cdbg_dump_triple_all(); } # endif } fis-gtm-V7.0-005/sr_port/jmp_opto.h0000755000032200000250000000105014342376331016042 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef JMP_OPTO_INCLUDED #define JMP_OPTO_INCLUDED void jmp_opto(void); #endif /* JMP_OPTO_INCLUDED */ fis-gtm-V7.0-005/sr_port/jnl.h0000644000032200000250000026562214342376331015014 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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" #ifndef JNL_H_INCLUDED #define JNL_H_INCLUDED #ifdef DEBUG #include /* for offsetof macro (see OFFSETOF usage in assert below) */ #include "wbox_test_init.h" #endif #ifndef JNLSP_H_INCLUDED #include "jnlsp.h" #endif #include "gtmcrypt.h" #include "sleep.h" #include "is_gdid.h" error_def(ERR_JNLBADLABEL); error_def(ERR_JNLENDIANBIG); error_def(ERR_JNLENDIANLITTLE); #define TID_STR_SIZE 8 #define JPV_LEN_NODE 16 #define JPV_LEN_USER 12 #define JPV_LEN_PRCNAM 16 #define JPV_LEN_TERMINAL 15 /* Whenever JNL_LABEL_TEXT changes, also change the following * 1) Update JNL_VER_THIS * 2) Add REPL_JNL_Vxx enum to repl_jnl_t typedef AND Vxx_JNL_VER #define in repl_filter.h * 3) Add an entry each to repl_filter_old2cur & repl_filter_cur2old arrays in repl_filter.c. * 4) Bump JNL_EXTR_LABEL and JNL_DET_EXTR_LABEL as appropriate in muprec.h. * This is not needed if for example the newly added jnl record types don't get extracted at all. * 5) Add a line in the big comment block just before "repl_filter_t" is typedefed in repl_filter.h * to update the "Filter format", "Journal format" and "GT.M version" columns. * 6) Add Vxx_JNL_VER to repl_filter.h and examine usages of Vxx_JNL_VER where xx corresponds to older * journal version to see if those need to be replaced with the newer version macro. * If the FILTER format is also changing, then do the following as well * 7) Add REPL_FILTER_Vxx enum to repl_filter_t typedef in repl_filter.h * 8) Add/Edit IF_xTOy macros in repl_filter.h to transform from/to the NEW jnl format version only. * Remove all entries that don't have the new jnl format in either the from or to part of the conversion. * 9) Add/Edit prototype and implement functions jnl_xTOy() and jnl_yTOx() in repl_filter.c * 10) Enhance repl_tr_endian_convert() to endian convert journal records from previous jnl formats to new format. * This is similar to the jnl_xTOy() filter conversion functions except that lot of byte-swaps are needed. * 11) Periodically determine if the size of the array repl_filter_old2cur is huge and if so trim support of * rolling upgrade (using replication internal filters) for older GT.M versions/jnl-formats. * This would mean bumping the macro JNL_VER_EARLIEST_REPL and examining all arrays that are defined * using this macro and changing the entries in all those arrays accordingly (e.g. repl_filter_old2cur * array currently assumes earliest supported version is V15 and hence has the function named IF_curTO15 * which needs to change to say IF_curTO17 if the earliest supported version changes to V17 or so). * */ #define JNL_LABEL_TEXT "GDSJNL28" /* see above comment paragraph for todos whenever this is changed */ #define JNL_VER_THIS 28 #define JNL_VER_EARLIEST_REPL 17 /* Replication filter support starts here GDSJNL17 = GT.M V5.1-000. * (even though it should be V5.0-000, since that is pre-multisite, * the replication connection with V55000 will error out at handshake * time so V5.1-000 is the minimum that will even reach internal filter code) */ #define JRT_MAX_V17 JRT_AIMG /* Maximum jnl record type in GDSJNL17 or GDSJNL18 that can be input to replication * filter. Actually JRT_TRIPLE is a higher record type than JRT_AIMG but it is only * sent through the replication pipe and never seen by filter routines. */ #define JRT_MAX_V19 JRT_UZTWORM /* Max jnlrec type in GDSJNL19/GDSJNL20 that can be input to replication filter */ #define JRT_MAX_V21 JRT_UZTRIG /* Max jnlrec type in GDSJNL21 that can be input to replication filter */ #define JRT_MAX_V22 JRT_UZTRIG /* Max jnlrec type in GDSJNL22/GDSJNL23 that can be input to replication filter. * Actually JRT_HISTREC is a higher record type than JRT_UZTRIG but it is only * sent through the replication pipe and never seen by filter routines. */ #define JRT_MAX_V24 JRT_ULGTRIG /* Max jnlrec type in GDSJNL24/GDSJNL25 that can be input to replication filter */ #define JNL_ALLOC_DEF 2048 #define JNL_ALLOC_MIN 2048 /* The journal buffer size (specified in pages of size DISK_BLOCK_SIZE) should be large enough for one largest record, * which is equivalent to the largest possible value (of size MAX_STRLEN) and largest possible key (of size MAX_KEY_SZ), * plus the overhead of storing the journal records. */ #define JNL_BUFFER_MIN ((MAX_LOGI_JNL_REC_SIZE + ROUND_UP(2 * MAX_IO_BLOCK_SIZE, DISK_BLOCK_SIZE)) / DISK_BLOCK_SIZE + 1) #define JNL_BUFFER_MAX 1048576 /* # of 512-byte blocks = 512Mib journal buffer size */ /* JNL_EXTEND_DEF allocation size / 10 #define JNL_EXTEND_DEF_PERC 0.1 * Uncomment this section when code is ready to use extension = 10% of allocation */ #define JNL_EXTEND_MIN 0 #define JNL_EXTEND_DEF 2048 #define JNL_EXTEND_MAX 1073741823 #define JNL_MIN_WRITE 32768 #define JNL_MAX_WRITE 65536 /* FE was changed to EB because, the bit pattern there seems to vary more than the one for "FE". * Also a research in ELWOOD journal file showed that "EB" was one of the few patterns that had the least occurrences */ #define JNL_REC_SUFFIX_CODE 0xEB /* With sync_io, we do journal writes to disk at filesystem block size boundaries. */ #define JNL_WRT_START_MODULUS(jb) jb->fs_block_size #define JNL_WRT_START_MASK(jb) ~(JNL_WRT_START_MODULUS(jb) - 1) /* mask defining where the next physical write needs to * happen as follows from the size of JNL_WRT_START_MODULUS */ #define JNL_WRT_END_MODULUS 8 #define JNL_WRT_END_MASK ((gtm_uint64_t)~(JNL_WRT_END_MODULUS - 1)) #define JNL_MIN_ALIGNSIZE (1 << 12) /* 4096 disk blocks effectively 2M alignsize */ #define JNL_DEF_ALIGNSIZE (1 << 12) /* 4096 disk blocks effectively 2M alignsize */ #define JNL_MAX_ALIGNSIZE (1 << 22) /* 4194304 disk blocks effectively 2G alignsize */ #define JNL_REC_START_BNDRY 8 /* maximum logical journal record size */ #define MAX_LOGI_JNL_REC_SIZE (ROUND_UP(MAX_STRLEN, DISK_BLOCK_SIZE) + ROUND_UP(MAX_KEY_SZ, DISK_BLOCK_SIZE)) /* one more disk-block for PBLK record header/footer */ #define MAX_JNL_REC_SIZE (MAX_LOGI_JNL_REC_SIZE + DISK_BLOCK_SIZE) /* Very large records require spanning nodes, which only happen in TP. */ #define MAX_NONTP_JNL_REC_SIZE(BSIZE) ((BSIZE) + DISK_BLOCK_SIZE) #define MAX_MAX_NONTP_JNL_REC_SIZE MAX_NONTP_JNL_REC_SIZE(MAX_DB_BLK_SIZE) #ifdef GTM_TRIGGER /* Define maximum size that $ZTWORMHOLE can be. Since $ZTWORMHOLE should be able to fit in a journal record and the * minimum alignsize used to be 128K (until V55000), we did not want it to go more than 128K (that way irrespective * of whatever alignsize the user specifies for the journal file, $ZTWORMHOLE will fit in the journal record). Leaving * a max of 512 bytes for the journal record prefix/suffix (32-byte overhead) and MIN_ALIGN_RECLEN (see comment in * JNL_MAX_RECLEN macro for why this is needed) we had allowed for a max of 128K-512 bytes in $ZTWORMHOLE. The minimum * alignsize (JNL_MIN_ALIGNSIZE) has changed to 2Mb (since V60000) and so we can potentially increase the maximum * $ZTWORMHOLE length to be 1Mb but we defer doing this until there is a need for this bigger length. */ #define MAX_ZTWORMHOLE_LEN (128 * 1024) #define MAX_ZTWORMHOLE_SIZE (MAX_ZTWORMHOLE_LEN - 512) #define MAX_ZTWORM_JREC_LEN (MAX_ZTWORMHOLE_LEN - MIN_ALIGN_RECLEN) /* Define maximum size that a LGTRIG jnl record can get to. Cannot exceed minimum align size for sure */ #define MAX_LGTRIG_LEN ((1 << 20) + (1 << 15)) /* 1Mb for XECUTE string part of the trigger, * 32K for rest of trigger definition. Together this should * still be less than 2Mb which is the JNL_MIN_ALIGNSIZE */ #define MAX_LGTRIG_JREC_LEN (MAX_LGTRIG_LEN + DISK_BLOCK_SIZE) /* Give 512 more bytes for journal record related * overhead (jrec_prefix etc.) */ #endif #define MIN_YIELD_LIMIT 0 #define MAX_YIELD_LIMIT 2048 #define DEFAULT_YIELD_LIMIT 8 /* Have a minimum jnl-file-auto-switch-limit of 4 align boundaries (currently each align boundary is 2M) */ #define JNL_AUTOSWITCHLIMIT_MIN (4 * JNL_MIN_ALIGNSIZE) #define JNL_AUTOSWITCHLIMIT_DEF 8386560 /* Instead of 8388607 it is adjusted for default allocation = extension = 2048 */ /* options (4-bytes unsigned integer) to wcs_flu() (currently flush_hdr, write_epoch, sync_epoch) are bit-wise ored */ #define WCSFLU_NONE 0 #define WCSFLU_FLUSH_HDR 1 #define WCSFLU_WRITE_EPOCH 2 #define WCSFLU_SYNC_EPOCH 4 #define WCSFLU_FSYNC_DB 8 /* Currently used only in Unix wcs_flu() */ #define WCSFLU_IN_COMMIT 16 /* Set if caller is t_end or tp_tend. See wcs_flu for explanation of when this is set */ #define WCSFLU_MSYNC_DB 32 /* Force a full msync if NO_MSYNC is defined. Currently used only in Unix wcs_flu(). */ #define WCSFLU_SPEEDUP_NOBEFORE 64 /* Do not flush dirty db buffers. Just write an epoch record. * Used to speedup nobefore jnl. */ #define WCSFLU_CLEAN_DBSYNC 128 /* wcs_flu invoked by wcs_clean_dbsync (as opposed to t_end/tp_tend invocation) */ #define WCSFLU_FORCE_EPOCH 256 /* skip the optimization that writes epoch only if an epoch is not the most recently * written record. This is necessary for some callers (e.g. update process writing * an epoch for the -noresync startup case). */ /* options for error_on_jnl_file_lost */ #define JNL_FILE_LOST_TURN_OFF 0 /* Turn off journaling. */ #define JNL_FILE_LOST_ERRORS 1 /* Throw an rts_error. */ #define MAX_JNL_FILE_LOST_OPT JNL_FILE_LOST_ERRORS #ifdef DEBUG #define DEFAULT_EPOCH_INTERVAL_IN_SECONDS 30 /* exercise epoch-syncing code relatively more often in DBG */ #else #define DEFAULT_EPOCH_INTERVAL_IN_SECONDS 300 #endif #define DEFAULT_EPOCH_INTERVAL SECOND2EPOCH_SECOND(DEFAULT_EPOCH_INTERVAL_IN_SECONDS) /* ***MUST*** include math.h for VMS */ #define MAX_EPOCH_INTERVAL 32767 /* in seconds. Amounts to nearly 10 hours. Don't want to keep db stale so long */ #define JNL_ENABLED(X) ((X)->jnl_state == jnl_open) /* If TRUE, journal records are to be written */ #define JNL_ALLOWED(X) ((X)->jnl_state != jnl_notallowed) /* If TRUE, journaling is allowed for the file */ #define REPL_ENABLED(X) ((X)->repl_state == repl_open) /* If TRUE, replication records are to be written */ #define REPL_WAS_ENABLED(X) ((X)->repl_state == repl_was_open) /* If TRUE, replication is now closed, but was open earlier */ /* In this state, replication records are not written */ #define REPL_ALLOWED(X) ((X)->repl_state != repl_closed) /* If TRUE, replication records are/were written */ /* Logical records should be written if journaling is enabled in the region OR if replication state is WAS_ON (repl_was_open). * In the former case, the journal records will be written to the journal pool, journal buffer and journal file. * In the latter case, the journal records will be written to the journal pool but not to the journal buffer and journal file. * All code that generates logical journal records should use the below macro instead of JNL_ENABLED macro. * Note that replication does not care about non-logical records (PBLK/AIMG/INCTN etc.) and hence code that generates them does * not need to (and should not) use this macro. */ #define JNL_WRITE_LOGICAL_RECS(X) (JNL_ENABLED(X) || REPL_WAS_ENABLED(X)) /* The following macro should be used to invoke the function "jnl_write" for any logical record. This macro * checks if journaling is enabled and if so invokes "jnl_write" else it invokes "jnl_pool_write" which * writes only to the journal pool. */ #define JNL_WRITE_APPROPRIATE(CSA, JPC, RECTYPE, JREC, JFB) \ MBSTART { \ GBLREF boolean_t is_replicator; \ \ assert(JNL_ENABLED(CSA) || REPL_WAS_ENABLED(CSA)); \ assert((NULL == JFB) || (RECTYPE == ((jnl_record *)(((jnl_format_buffer *)JFB)->buff))->prefix.jrec_type)); \ if (JNL_ENABLED(CSA)) \ jnl_write(JPC, RECTYPE, JREC, JFB); /* write to jnlbuffer */ \ if (REPL_ALLOWED(CSA) && is_replicator) \ { \ assert(REPL_ENABLED(CSA) || REPL_WAS_ENABLED(CSA)); \ jnl_pool_write(CSA, RECTYPE, JREC, JFB); /* write to jnlpool */ \ } \ } MBEND #define MUEXTRACT_TYPE(A) (((A)[0]-'0')*10 + ((A)[1]-'0')) /* A is a character pointer */ #define PADDED PADDING /* User must enter this string to ask standard input or output. */ #define JNL_STDO_EXTR "-stdout" #ifdef BIGENDIAN #define THREE_LOW_BYTES(x) ((uchar_ptr_t)((uchar_ptr_t)&x + 1)) #else #define THREE_LOW_BYTES(x) ((uchar_ptr_t)(&x)) #endif #define EXTTIME(S) extract_len = exttime(S, murgbl.extr_buff, extract_len) /* This macro should be used to initialize jgbl.gbl_jrec_time to the system time. The reason is that it does additional checks. */ #define SET_GBL_JREC_TIME \ MBSTART { \ assert(!jgbl.dont_reset_gbl_jrec_time); \ JNL_SHORT_TIME(jgbl.gbl_jrec_time); \ } MBEND #define DO_GBL_JREC_TIME_CHECK_FALSE FALSE #define DO_GBL_JREC_TIME_CHECK_TRUE TRUE #define SET_JNLBUFF_PREV_JREC_TIME(JB, CUR_JREC_TIME, DO_GBL_JREC_TIME_CHECK) \ { \ GBLREF jnl_gbls_t jgbl; \ \ /* In case of journal rollback, time is set back to turnaround point etc. so assert accordingly */ \ assert(!DO_GBL_JREC_TIME_CHECK || (jgbl.gbl_jrec_time >= CUR_JREC_TIME)); \ assert(jgbl.onlnrlbk || (JB->prev_jrec_time <= CUR_JREC_TIME)); \ JB->prev_jrec_time = CUR_JREC_TIME; \ } /* This macro ensures that journal records are written in non-decreasing time order in each journal file. * It is passed the time field to adjust and a pointer to the journal buffer of the region. * The journal buffer holds the timestamp of the most recently written journal record. */ #define ADJUST_GBL_JREC_TIME(jgbl, jbp) \ { \ if (jgbl.gbl_jrec_time < jbp->prev_jrec_time) \ { \ assert(!jgbl.dont_reset_gbl_jrec_time); \ jgbl.gbl_jrec_time = jbp->prev_jrec_time; \ } \ } /* This macro is similar to ADJUST_GBL_JREC_TIME except that this ensures ordering of timestamps across * ALL replicated regions in a replicated environment. In VMS, we don't maintain this prev_jnlseqno_time * field. */ #define ADJUST_GBL_JREC_TIME_JNLPOOL(jgbl, jpl) \ { \ if (jgbl.gbl_jrec_time < jpl->prev_jnlseqno_time) \ { \ assert(!jgbl.dont_reset_gbl_jrec_time); \ jgbl.gbl_jrec_time = jpl->prev_jnlseqno_time; \ } \ jpl->prev_jnlseqno_time = jgbl.gbl_jrec_time; \ } /* Check if journal file is usable from the fields in the file header. * Currently, the fields tested are LABEL and ENDIANNESS. */ #define JNL_HDR_ENDIAN_OFFSET 8 #define CHECK_JNL_FILE_IS_USABLE(JFH, STATUS, DO_GTMPUTMSG, JNL_FN_LEN, JNL_FN) \ { \ assert(JNL_HDR_ENDIAN_OFFSET == OFFSETOF(jnl_file_header, is_little_endian)); \ if (0 != MEMCMP_LIT((JFH)->label, JNL_LABEL_TEXT)) \ { \ STATUS = ERR_JNLBADLABEL; \ if (DO_GTMPUTMSG) \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(8) STATUS, 6, JNL_FN_LEN, JNL_FN, \ LEN_AND_LIT(JNL_LABEL_TEXT), SIZEOF((JFH)->label), (JFH)->label); \ } \ BIGENDIAN_ONLY( \ else if ((JFH)->is_little_endian) \ { \ STATUS = ERR_JNLENDIANLITTLE; \ if (DO_GTMPUTMSG) \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) STATUS, 2, JNL_FN_LEN, JNL_FN); \ } \ ) \ LITTLEENDIAN_ONLY( \ else if (!(JFH)->is_little_endian) \ { \ STATUS = ERR_JNLENDIANBIG; \ if (DO_GTMPUTMSG) \ gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) STATUS, 2, JNL_FN_LEN, JNL_FN); \ } \ ) \ } /* Token generation used in non-replicated journaled environment. Note the assumption here that SIZEOF(token_split_t) == SIZEOF(token_build) which will be asserted in gvcst_init(). The TOKEN_SET macro below depends on this assumption. */ typedef struct token_split_t_struct { # ifdef BIGENDIAN uint4 process_id; uint4 local_tn; # else uint4 local_tn; uint4 process_id; # endif } token_split_t; typedef union { token_split_t t_piece; token_num token; } token_build; /* To assist in setting token value, the following macro is supplied to handle the two token parts */ #define TOKEN_SET(BASE, TN, PID) (((token_build_ptr_t)(BASE))->t_piece.local_tn = (uint4)(TN), \ ((token_build_ptr_t)(BASE))->t_piece.process_id = (PID)) enum jpv_types { CURR_JPV = 0, ORIG_JPV, JPV_COUNT }; /* Note we have two process verctors now for a pini record */ typedef struct jnl_process_vector_struct /* name needed since this is used in cmmdef.h for "pvec" member */ { uint4 jpv_pid; /* Process id */ int4 jpv_image_count; /* Image activations [VMS only] */ jnl_proc_time jpv_time; /* Timestamp of the process genarating this. (This could be different than the journal record timestamp) */ jnl_proc_time jpv_login_time; /* Used for process initialization time */ char jpv_node[JPV_LEN_NODE], /* Node name */ jpv_user[JPV_LEN_USER], /* User name */ jpv_prcnam[JPV_LEN_PRCNAM], /* Process name [VMS only] */ jpv_terminal[JPV_LEN_TERMINAL]; /* Login terminal */ unsigned char jpv_mode; /* a la JPI$_MODE [VMS only] */ int4 filler; /* SIZEOF(jnl_process_vector) must be a multiple of SIZEOF(int4) */ } jnl_process_vector; enum pini_rec_stat { IGNORE_PROC = 0, ACTIVE_PROC = 1, FINISHED_PROC = 2, BROKEN_PROC = 4 }; typedef struct pini_list { uint4 pini_addr; uint4 new_pini_addr; /* used in forward phase of recovery */ jnl_process_vector jpv; /* CURR_JPV. Current process's JPV. For GTCM server we also use this. */ jnl_process_vector origjpv; /* ORIG_JPV. Used for GTCM client only */ jnl_proc_time pini_jpv_time; /* Original PINI record timestamp (before forward phase of * journal recovery modifies jpv->jpv_time */ enum pini_rec_stat state; /* used for show qualifier */ } pini_list_struct; enum jnl_record_type { #define JNL_TABLE_ENTRY(rectype, extract_rtn, label, update, fixed_size, is_replicated) rectype, #include "jnl_rec_table.h" #undef JNL_TABLE_ENTRY JRT_RECTYPES /* Total number of JOURNAL record types */ }; #include "jnl_typedef.h" enum jnl_state_codes { jnl_notallowed, jnl_closed, jnl_open }; enum repl_state_codes { repl_closed, /* region not replicated, no records are written */ repl_open, /* region is replicated, and records are written */ repl_was_open /* region is currently not replicated, but it was earlier; jnl_file_lost() changes open to was_open */ }; #define JNL_PHASE2_COMMIT_ARRAY_SIZE 4096 /* Max # of jnl commits that can still be in phase2. * Note that even if a phase2 commit is complete, the slot it occupies * cannot be reused by anyone else until all phase2 commit slots that * started before this slot are also done. */ /* Individual structure describing an active phase2 jnl commit in jnl_buffer shared memory */ typedef struct { trans_num curr_tn; seq_num jnl_seqno; seq_num strm_seqno; uint4 process_id; uint4 start_freeaddr; /* jb->rsrv_freeaddr at start of this commit */ uint4 tot_jrec_len; /* total length of jnl records in jnl buffer corresponding to this tn */ uint4 pini_addr; /* jpc->pini_addr when this phase2 commit entry was allocated */ uint4 jrec_time; /* time corresponding to this transaction */ bool in_phase2; /* TRUE if the UPDATE_JBP_RSRV_FREEADDR call for this phas2 commit entry happened * through UPDATE_JRS_RSRV_FREEADDR in t_end/tp_tend. * FALSE if the call happened directly in "jnl_write" (e.g. PFIN/INCTN etc.). * This distinction helps cleanup orphaned slots (owning pid is killed prematurely). */ bool replication; /* TRUE if this transaction bumped the jnl_seqno. Note that even though a * region might have replication turned on, there are some transactions * (e.g. inctn operations from reorg, file extensions, 2nd phase of kill etc.) * which will not bump the jnl_seqno. Those will have this set to FALSE. * This is needed by "jnl_write_salvage" to know if a JRT_INCTN or JRT_NULL needs * to be written. */ bool write_complete; /* TRUE if this pid is done writing jnl records to jnlbuff */ bool filler_8byte_align[1]; } jbuf_phase2_in_prog_t; typedef struct { trans_num eov_tn; /* curr_tn is saved as eov_tn by jnl_write_epoch. Used by recover/rollback */ volatile trans_num epoch_tn; /* Transaction number for current epoch */ seq_num end_seqno; /* reg_seqno saved by jnl_write_epoch. Used by recover/rollback */ seq_num strm_end_seqno[MAX_SUPPL_STRMS]; /* used to keep jfh->strm_end_seqno up to date with each epoch. * Unused in VMS but defined so shared memory layout is similar in Unix & VMS. */ int4 min_write_size, /* if unwritten data gets to this size, write it */ max_write_size, /* maximum size of any single write */ size; /* buffer size */ int4 epoch_interval; /* Time between successive epochs in epoch-seconds */ boolean_t before_images; /* If TRUE, before-image processing is enabled */ /* end not volatile QUAD */ uintszofptr_t buff_off; /* relative offset to filesystem-block-size aligned buffer start */ volatile int4 free, /* relative index of first byte to write in buffer */ rsrv_free; /* relative index corresponding to jb->rsrv_freeaddr */ volatile uint4 freeaddr, /* virtual on-disk address which will correspond to free, when it is written */ rsrv_freeaddr, /* freeaddr when all in-progress phase2 jnl commits are complete. * rsrv_freeaddr >= freeaddr at all times. */ next_align_addr,/* rsrv_freeaddr BEFORE which next JRT_ALIGN record needs to be written */ end_of_data, /* Synched offset updated by jnl_write_epoch. Used by recover/rollback */ filesize; /* highest virtual address available in the file (units in disk-blocks) * file size in bytes limited to 4GB by autoswitchlimit, so 'filesize' <= 8MB * so filesize cannot overflow the four bytes of a uint4 */ uint4 phase2_commit_index1; uint4 phase2_commit_index2; jbuf_phase2_in_prog_t phase2_commit_array[JNL_PHASE2_COMMIT_ARRAY_SIZE]; volatile int4 blocked; volatile uint4 fsync_dskaddr; /* dskaddr upto which fsync is done */ volatile int4 dsk; /* relative index of 1st byte to write to disk; * if free == dsk, buffer is empty */ volatile int4 wrtsize; /* size of write in progress */ volatile uint4 dskaddr, /* virtual on-disk address corresponding to dsk */ now_writer, /* current owner of io_in_prog (VMS-only) */ image_count; /* for VMS is_proc_alive */ volatile struct /* must be at least word aligned for memory coherency */ { short cond; unsigned short length; int4 dev_specific; } iosb; uint4 alignsize; /* current alignsize (max value possible is 2Gb, not 4Gb) */ jnl_tm_t eov_timestamp; /* jgbl.gbl_jrec_time saved by jnl_write_epoch. Used by recover/rollback */ uint4 cycle; /* shared copy of the number of the current journal file generation */ volatile int4 qiocnt, /* Number of qio's issued */ bytcnt, /* Number of bytes written */ errcnt, /* Number of errors during writing */ reccnt[JRT_RECTYPES]; /* Number of records written per opcode */ int filler_align[42 - JRT_RECTYPES]; /* So buff below starts on even (QW) keel */ /* Note the above filler will fail if JRT_RECTYPES grows beyond 42 elements * In that case, change the start num to the next even number. */ volatile jnl_tm_t prev_jrec_time; /* to ensure that time never decreases across successive jnl records */ volatile uint4 next_epoch_time; /* Time when next epoch is to be written (in epoch-seconds) */ volatile boolean_t need_db_fsync; /* need an fsync of the db file */ volatile int4 io_in_prog; /* VMS only: write in progress indicator (NOTE: must manipulate only with interlocked instructions */ uint4 enospc_errcnt; /* number of times jb->errcnt was last incremented due to ENOSPC error * when writing to this journal file */ uint4 max_jrec_len; /* copy of max_jrec_len from journal file header */ uint4 fs_block_size; /* underlying journal file system block size */ volatile uint4 post_epoch_freeaddr; /* virtual on-disk address after last epoch */ boolean_t last_eof_written; /* No more records may be written to the file due to autoswitch */ uint4 end_of_data_at_open; /* Offset of EOF record when jnl file is first opened in shm */ uint4 re_read_dskaddr; /* If non-zero, "jnl_qio_start" will read a filesystem-block of data * corresponding to jbp->dskaddr before initiating any new writes * to the journal file. */ /* CACHELINE_PAD macros provide spacing between the following latches so that they do not interfere with each other which can happen if they fall in the same data cacheline of a processor. */ CACHELINE_PAD(SIZEOF(global_latch_t), 0) /* start next latch at a different cacheline than previous fields */ global_latch_t io_in_prog_latch; /* UNIX only: write in progress indicator */ CACHELINE_PAD(SIZEOF(global_latch_t), 1) /* start next latch at a different cacheline than previous fields */ global_latch_t fsync_in_prog_latch; /* fsync in progress indicator */ CACHELINE_PAD(SIZEOF(global_latch_t), 2) /* start next latch at a different cacheline than previous fields */ global_latch_t phase2_commit_latch; /* Used by "jnl_phase2_cleanup" to update "phase2_commit_index1" */ CACHELINE_PAD(SIZEOF(global_latch_t), 3) /* pad enough space so next non-filler byte falls in different cacheline */ /**********************************************************************************************/ /* Important: must keep header structure quadword (8 byte) aligned for buffers used in QIO's */ /**********************************************************************************************/ unsigned char buff[1]; /* Actually buff[size] */ } jnl_buffer; /* Sets jbp->freeaddr & jbp->free. They need to be kept in sync at all times */ #define SET_JBP_FREEADDR(JBP, FREEADDR) \ { \ DEBUG_ONLY(uint4 jbp_freeaddr;) \ DEBUG_ONLY(uint4 jbp_rsrv_freeaddr;) \ \ assert(JBP->phase2_commit_latch.u.parts.latch_pid == process_id); \ DEBUG_ONLY(jbp_freeaddr = JBP->freeaddr;) \ assert(FREEADDR >= jbp_freeaddr); \ DEBUG_ONLY(jbp_rsrv_freeaddr = JBP->rsrv_freeaddr;) \ assert(FREEADDR <= jbp_rsrv_freeaddr); \ JBP->freeaddr = FREEADDR; \ /* Write memory barrier here to enforce the fact that freeaddr *must* be seen to be updated before \ * free is updated. It is less important if free is stale so we do not require a 2nd barrier for that \ * and will let the lock release (crit lock required since clustering not currently supported) do the \ * 2nd memory barrier for us. This barrier takes care of this process's responsibility to broadcast \ * cache changes. It is up to readers to also specify a read memory barrier if necessary to receive \ * this broadcast. \ */ \ SHM_WRITE_MEMORY_BARRIER; \ JBP->free = (FREEADDR) % JBP->size; \ DBG_CHECK_JNL_BUFF_FREEADDR(JBP); \ } #define DBG_CHECK_JNL_BUFF_FREEADDR(JBP) \ { \ assert((JBP->freeaddr % JBP->size) == JBP->free); \ assert((JBP->freeaddr >= JBP->dskaddr) \ || (gtm_white_box_test_case_enabled \ && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number))); \ } #ifdef DB64 # ifdef __osf__ # pragma pointer_size(save) # pragma pointer_size(long) # else # error UNSUPPORTED PLATFORM # endif #endif typedef jnl_buffer *jnl_buffer_ptr_t; typedef token_build *token_build_ptr_t; #ifdef DB64 # ifdef __osf__ # pragma pointer_size(restore) # endif #endif typedef struct jnl_private_control_struct { jnl_buffer_ptr_t jnl_buff; /* pointer to shared memory */ gd_region *region; /* backpointer to region head */ fd_type channel, /* output channel, aka fd in UNIX */ old_channel; /* VMS only - for dealing with deferred deassign */ gd_id fileid; /* currently initialized and used only by source-server */ uint4 pini_addr, /* virtual on-disk address for JRT_PINI record, if journaling */ new_freeaddr; uint4 phase2_freeaddr; /* Used by "jnl_write_reserve" in phase1 to denote freeaddr * that takes into account the cumulative length of journal records * already reserved till now in phase1. * Used by JB_FREEADDR_APPROPRIATE macro in phase2 to indicate the * freeaddr corresponding to the journal record about to be written * by this process. */ uint4 phase2_free; /* jb->free to use in "jnl_write_phase2" */ boolean_t in_jnl_phase2_salvage; /* in "jnl_phase2_salvage" */ int4 status; /* A for error reporting */ volatile boolean_t qio_active; /* jnl buffer write in progress in THIS process (recursion indicator) */ boolean_t fd_mismatch; /* TRUE when jpc->channel does not point to the active journal */ volatile boolean_t sync_io; /* TRUE if the process is using O_SYNC/O_DSYNC for this jnl (UNIX) */ /* TRUE if writers open NOCACHING to bypass XFC cache (VMS) */ boolean_t error_reported; /* TRUE if jnl_file_lost already reported the journaling error */ uint4 status2; /* for secondary error status, currently used only in VMS */ uint4 cycle; /* private copy of the number of this journal file generation */ char *err_str; /* pointer to an extended error message */ trans_num curr_tn; /* db tn used by callees of "jnl_write_phase2" */ } jnl_private_control; typedef enum { JNL_KILL, JNL_SET, JNL_ZKILL, # ifdef GTM_TRIGGER JNL_ZTWORM, JNL_LGTRIG, JNL_ZTRIG, # endif JA_MAX_TYPES } jnl_action_code; typedef enum { #define MUEXT_TABLE_ENTRY(muext_rectype, code0, code1) muext_rectype, #include "muext_rec_table.h" #undef MUEXT_TABLE_ENTRY MUEXT_MAX_TYPES /* Total number of EXTRACT JOURNAL record types */ } muextract_type; typedef struct { jnl_action_code operation; uint4 nodeflags; } jnl_action; #define JNL_FENCE_LIST_END ((sgmnt_addrs *)-1L) typedef struct { sgmnt_addrs *fence_list; int level; boolean_t replication; token_num token; seq_num strm_seqno; /* valid only in case of replication. uninitialized in case of ZTP */ } jnl_fence_control; typedef struct { uint4 jrec_type : 8; /* Offset:0 :: Actually, enum jnl_record_type */ uint4 forwptr : 24; /* Offset:1 :: Offset to beginning of next record */ off_jnl_t pini_addr; /* Offset:4 :: Offset in the journal file which contains pini record */ jnl_tm_t time; /* Offset:8 :: 4-byte time stamp both for UNIX and VMS */ uint4 checksum; /* Offset:12 :: Generated from journal record */ trans_num tn; /* Offset:16 */ } jrec_prefix; /* 24-byte */ typedef struct { uint4 backptr : 24; /* Offset to beginning of current record */ uint4 suffix_code : 8; /* JNL_REC_SUFFIX_CODE */ } jrec_suffix; /* 4-byte */ typedef union { seq_num jnl_seqno; token_num token; } token_seq_t; typedef struct { char label[SIZEOF(JNL_LABEL_TEXT) - 1]; char is_little_endian; /* this field's offset (JNL_HDR_ENDIAN_OFFSET) should not change * across journal versions and is checked right after reading the header. */ char filler_align8[7]; jnl_process_vector who_created, /* Process who created */ who_opened; /* Process who last opened */ jnl_proc_time bov_timestamp, /* 8-byte time when journal was created */ eov_timestamp; /* 8-byte time when journal was last updated Updated by cre_jnl_file/jnl_file_extend/jnl_file_close */ trans_num bov_tn, /* Beginning journal record's transaction number */ eov_tn; /* End transaction number. Updated by cre_jnl_file/jnl_file_extend/jnl_file_close */ seq_num start_seqno; /* reg_seqno when this journal file was created */ seq_num end_seqno; /* reg_seqno when this journal file was closed or last extended. Updated by cre_jnl_file/jnl_file_extend/jnl_file_close */ off_jnl_t end_of_data; /* Offset of beginning of last record. Updated by cre_jnl_file/jnl_file_extend/jnl_file_close */ off_jnl_t prev_recov_end_of_data; /* Recovered/Rolled back journal's turn around point's offset. This offset was supposed to have EOF_RECORD before recover switched journal. A non-zero value means this journal was recovered and had the turn around point. */ off_jnl_t virtual_size; /* Allocation + n * Extension (in blocks). jnl_file_extend updates it */ boolean_t crash; /* crashed before "jnl_file_close" completed */ boolean_t recover_interrupted; /* true when recover creates the journal file; false after success. */ off_jnl_t turn_around_offset; /* At turn around point journal record's (EPOCH) offset */ jnl_tm_t turn_around_time; /* At turn around point journal record's timestamp */ boolean_t before_images; /* before image enabled in this journal */ uint4 alignsize; /* align size of journal (where a valid record start) */ int4 epoch_interval; /* Time between successive epochs in epoch-seconds */ int4 repl_state; /* To state whether replication is turned on for this journal file */ uint4 autoswitchlimit;/* Limit in disk blocks (max 4GBytes) when jnl should be auto switched */ uint4 jnl_alq; /* initial allocation (in blocks) */ uint4 jnl_deq; /* extension (in blocks) */ boolean_t filler_update_disabled; /* obsoleted as part of multi-site replication changes */ int4 max_jrec_len; /* Maximum length in bytes of a journal record. * Although computed from the database block size, we need this * stored as well in case database is not available */ uint4 data_file_name_length; /* Length of data_file_name */ uint4 prev_jnl_file_name_length; /* Length of prev_jnl_file_name */ uint4 next_jnl_file_name_length; /* Length of next_jnl_file_name */ uint4 checksum; /* Calculate from journal file id */ block_id prev_recov_blks_to_upgrd_adjust; /* amount to adjust filehdr "blks_to_upgrd" if ever * backward recovery goes back past this journal file */ unsigned char data_file_name[JNL_NAME_SIZE]; /* Database file name */ unsigned char prev_jnl_file_name[JNL_NAME_SIZE]; /* Previous generation journal file name */ unsigned char next_jnl_file_name[JNL_NAME_SIZE]; /* Next generation journal file name */ /* Encryption-related fields (mirror those in the database file header) */ int4 is_encrypted; char encryption_hash[GTMCRYPT_RESERVED_HASH_LEN]; char encryption_hash2[GTMCRYPT_RESERVED_HASH_LEN]; boolean_t non_null_iv; block_id encryption_hash_cutoff; trans_num encryption_hash2_start_tn; char encr_filler[80]; seq_num strm_start_seqno[MAX_SUPPL_STRMS]; seq_num strm_end_seqno[MAX_SUPPL_STRMS]; boolean_t last_eof_written; /* No more records may be written to the file due to autoswitch */ boolean_t is_not_latest_jnl; /* Set to TRUE if this jnl becomes a previous generation jnl file * at some point in time (relied upon by mur_tp_resolve_time). */ /* filler remaining */ char filler[424]; } jnl_file_header; typedef struct { int4 status, alloc, extend, buffer; sgmnt_data_ptr_t csd; seq_num reg_seqno; unsigned char jnl[JNL_NAME_SIZE], *fn; uint4 max_jrec_len; short fn_len, jnl_len, jnl_def_len; bool before_images; bool filler_bool[1]; uint4 alignsize; int4 autoswitchlimit; /* limit in disk blocks (8388607 blocks) * when jnl should be auto switched */ int4 epoch_interval; /* Time between successive epochs in epoch-seconds */ char *prev_jnl; int4 prev_jnl_len; int4 jnl_state; /* current csd->jnl_state */ int4 repl_state; uint4 status2; /* for secondary error status information in VMS */ boolean_t no_rename; boolean_t no_prev_link; block_id blks_to_upgrd; /* Blocks not at current block version level */ block_id free_blocks; /* free blocks counter at time of epoch */ block_id total_blks; /* total blocks counter at time of epoch */ uint4 checksum; int4 is_encrypted; char encryption_hash[GTMCRYPT_RESERVED_HASH_LEN]; char encryption_hash2[GTMCRYPT_RESERVED_HASH_LEN]; boolean_t non_null_iv; block_id encryption_hash_cutoff; trans_num encryption_hash2_start_tn; sgmnt_addrs *csa; } jnl_create_info; /* Journal record definitions */ #define jnl_str_len_t uint4 /* 4 byte length (which is in turn split into bit fields below) */ /* Bit masks for the "nodeflags" field. Note that there is no flag to indicate whether this update actually invoked any triggers. * That is because we have to format the journal record BEFORE invoking any triggers (that way the triggering update comes ahead * of its corresponding triggered updates in the journal file as this ordering is relied upon by the update process) and as part * of formatting, we also compute the checksum that includes the "nodeflags" field and so fixing this field AFTER trigger * invocation to reflect if any triggers were invoked would mean recomputing the checksum all over again. Currently there is no * need for the "triggers actually invoked" bit. If it is later desired, care should be taken to recompute the checksum. */ #define JS_NOT_REPLICATED_MASK (1 << 0) /* 1 if this update should NOT be replicated. * All updates done inside of a trigger and a SET redo (because of changes to $ztval) * fall in this category. */ #define JS_HAS_TRIGGER_MASK (1 << 1) /* 1 if the global being updated had at least one trigger defined (not necessarily * invoked for this particular update) */ #define JS_NULL_ZTWORM_MASK (1 << 2) /* 1 if $ZTWORMHOLE for this update should be "" string, 0 otherwise */ #define JS_SKIP_TRIGGERS_MASK (1 << 3) /* 1 if MUPIP LOAD update so triggers are not invoked on replay by update process */ #define JS_IS_DUPLICATE (1 << 4) /* 1 if this SET or KILL is a duplicate. In case of a SET, this is a duplicate set. * In case of a KILL, it is a kill of a non-existing node aka duplicate kill. * Note that the dupkill occurs only in case of the update process. In case of GT.M, * the KILL is entirely skipped. In both duplicate sets or kills, only a journal * record is written, the database is untouched. */ #define JS_MAX_MASK (1 << 8) /* max of 8 bits we have for mask */ /* Note that even though mumps_node, ztworm_str, lgtrig_str, align_str are members defined as type "jnl_string" below, * the "nodeflags" field is initialized to non-zero values ONLY in the case of the mumps_node member. * For ztworm_str, lgtrig_str and align_str, nodeflags is guaranteed to be zero so the 24-bit "length" member * can even be used as a 32-bit length (if necessary) without issues. This is why nodeflags is * defined in a different order (BEFORE or AFTER the "length" member) based on big-endian or little-endian. */ typedef struct { # ifdef BIGENDIAN unsigned int nodeflags : 8; unsigned int length : 24; # else unsigned int length : 24; unsigned int nodeflags : 8; # endif char text[1]; /* Actually text[length] */ } jnl_string; typedef struct jnl_format_buff_struct { que_ent free_que; struct jnl_format_buff_struct *next; # ifdef GTM_TRIGGER struct jnl_format_buff_struct *prev; # endif enum jnl_record_type rectype; int4 record_size; int4 hi_water_bsize; char *buff; uint4 checksum; jnl_action ja; char *alt_buff; /* for storing the unencrypted jnl *SET and *KILL records to be pushed * into the jnl pool. */ } jnl_format_buffer; /* All fixed size records are 8-byte-multiple size. * All variable size records are made 8-byte multiple size by run-time process */ /* struct_jrec_upd for non-TP, TP or ZTP. For replication we use 8-byte jnl_seqno. Otherwise we use 8-byte token. * Currently we don't support ZTP + replication. */ typedef struct /* variable length */ { jrec_prefix prefix; token_seq_t token_seq; /* must start at 8-byte boundary */ seq_num strm_seqno; /* non-zero only if this is a supplementary instance in which case this # * reflects the 60-bit sequence number corresponding to this update on the * originating primary + higher order 4-bits reflecting the stream #. */ uint4 update_num; /* 'n' where this is the nth journaled update (across all regions) in this TP * transaction. n=1 for the first update inside TP, 2 for the second update * inside TP and so on. Needed so journal recovery and update process can play * all the updates inside of one TP transaction in the exact same order as GT.M. */ unsigned short filler_short; unsigned short num_participants; /* # of regions that wrote a TCOM record in their jnl files. * Currently written only for TSET/TKILL/TZTWORM/TLGTRIG/TZTRIG records. * Uninitialized for USET/UKILL/UZTWORM/ULGTRIG/UZTRIG records. */ jnl_string mumps_node; /* For set/kill/zkill : {jnl_str_len_t key_len, char key[key_len]} */ /* For set additionally : {mstr_len_t data_len, char data[data_len]} */ } struct_jrec_upd; /* $ztwormhole record */ typedef struct /* variable length */ { jrec_prefix prefix; token_seq_t token_seq; /* must start at 8-byte boundary */ seq_num strm_seqno; /* see "struct_jrec_upd" for comment on the purpose of this field */ uint4 update_num; /* 'n' where this is the nth journaled update (across all regions) in this TP * transaction. n=1 for the first update inside TP, 2 for the second update * inside TP and so on. Needed so journal recovery and update process can play * all the updates inside of one TP transaction in the exact same order as GT.M. */ unsigned short filler_short; unsigned short num_participants; /* # of regions that wrote a TCOM record in their jnl files. * Currently written only for TSET/TKILL/TZTWORM/TLGTRIG/TZTRIG records. * Uninitialized for USET/UKILL/UZTWORM/ULGTRIG/UZTRIG records. */ jnl_string ztworm_str; /* jnl_str_len_t ztworm_str_len, char ztworm_str[ztworm_str_len]} */ } struct_jrec_ztworm; /* logical trigger jnl record (TLGTRIG or ULGTRIG record) */ typedef struct /* variable length */ { jrec_prefix prefix; token_seq_t token_seq; /* must start at 8-byte boundary */ seq_num strm_seqno; /* see "struct_jrec_upd" for comment on the purpose of this field */ uint4 update_num; /* 'n' where this is the nth journaled update (across all regions) in this TP * transaction. n=1 for the first update inside TP, 2 for the second update * inside TP and so on. Needed so journal recovery and update process can play * all the updates inside of one TP transaction in the exact same order as GT.M. */ unsigned short filler_short; unsigned short num_participants; /* # of regions that wrote a TCOM record in their jnl files. * Currently written only for TSET/TKILL/TZTWORM/TLGTRIG/TZTRIG records. * Uninitialized for USET/UKILL/UZTWORM/ULGTRIG/UZTRIG records. */ jnl_string lgtrig_str; /* jnl_str_len_t lgtrig_str_len, char lgtrig_str[lgtrig_str_len]} */ } struct_jrec_lgtrig; #define INVALID_UPDATE_NUM (uint4)-1 typedef struct /* variable length */ { jrec_prefix prefix; block_id blknum; uint4 bsiz; enum db_ver ondsk_blkver; /* Previous version of block from cache_rec */ char blk_contents[1]; /* Actually blk_contents[bsiz] */ } struct_jrec_blk; /* Note: A JRT_ALIGN is the ONLY journal record which does not have the full "jrec_prefix" prefix. This is because * we need it to be as small as 16-bytes in size (See "jnl_phase2_salvage" for a discussion on WHY). Hence the below * special definition which takes in a minimal set of fields from jrec_prefix to keep the smallest size (including * the 4-byte suffix) at 16 bytes. The "time" member is kept at the same offset as other jnl records. This lets us * use jnlrec->prefix.time to access the time even for JRT_ALIGN records. But the "checksum" member is at a * different offset since it is at offset 12 bytes in other records which is not possible for JRT_ALIGN given it * needs to be 16-bytes in size including a 4-byte suffix. A design choice was to move the checksum to offset 4 bytes * in all the other records but that meant changes to various stuff (like replication filters to transform from the new * format to old format etc.) and was not considered worth the effort. Instead we will need to ensure all code that * needs to access the checksum field uses "prefix.checksum" only if it is sure the input record type is not JRT_ALIGN. * If the caller code is not sure, it needs to use the GET_JREC_CHECKSUM macro. */ typedef struct /* variable length */ { uint4 jrec_type : 8; /* Offset:0 :: Actually, enum jnl_record_type */ uint4 forwptr : 24; /* Offset:1 :: Offset to beginning of next record */ uint4 checksum; /* Offset:4 :: Generated from journal record */ jnl_tm_t time; /* Offset:8 :: 4-byte time stamp */ /* Note: Actual string (potentially 0-length too) follows "forwptr" and then "jrec_suffix" */ } struct_jrec_align; /* Please change the "GBLDEF struct_jrec_tcom" initialization, if below is changed */ typedef struct /* fixed length */ { jrec_prefix prefix; token_seq_t token_seq; /* must start at 8-byte boundary */ seq_num strm_seqno; /* see "struct_jrec_upd" for comment on the purpose of this field */ unsigned short filler_short; unsigned short num_participants; /* # of regions that wrote a TCOM record in their jnl files */ char jnl_tid[TID_STR_SIZE]; jrec_suffix suffix; } struct_jrec_tcom; /* Please change the "static struct_jrec_ztcom" initialization in op_ztcommit.c, if below is changed */ typedef struct /* fixed length */ { jrec_prefix prefix; token_num token; /* must start at 8-byte boundary */ seq_num filler_8bytes; /* To mirror tcom layout. It is ok to waste space because ztcom is * obsoleted record. This keeps logic (e.g. MUR_TCOM_TOKEN_PROCESSING) faster * by avoiding if checks (of whether the rectype is TCOM or ZTCOM and accordingly * taking the appropriate offset). */ unsigned short filler_short; unsigned short participants; /* # of regions that wrote ZTCOM record in their jnl files for this fenced tn */ jrec_suffix suffix; } struct_jrec_ztcom; /* Below are different inctn_detail_*_t type definitions based on the inctn record opcode. * Each of them need to ensure the following. * a) SIZEOF(inctn_detail_*_t) is identical. * b) "opcode" member is at the same offset. * c) "suffix" is the last member. * Any new inctn_detail_*_t type definitions should have corresponding code changes in jnl_write_inctn_rec.c */ typedef struct { block_id blknum; /* block that got upgraded or downgraded (opcode = inctn_blk*grd) */ unsigned short filler_short; unsigned short opcode; jrec_suffix suffix; } inctn_detail_blknum_t; typedef struct { block_id blks_to_upgrd_delta; /* Delta to adjust csd->blks_to_upgrade (opcode = inctn_gdsfilext_*) */ unsigned short filler_short; unsigned short opcode; jrec_suffix suffix; } inctn_detail_blks2upgrd_t; typedef union { inctn_detail_blknum_t blknum_struct; inctn_detail_blks2upgrd_t blks2upgrd_struct; } inctn_detail_t; typedef struct /* fixed length */ { jrec_prefix prefix; inctn_detail_t detail; /* jrec_suffix is already part of inctn_detail_t */ } struct_jrec_inctn; typedef struct /* fixed length */ { jrec_prefix prefix; jnl_process_vector process_vector[JPV_COUNT]; int4 filler; jrec_suffix suffix; } struct_jrec_pini; typedef struct /* fixed length */ { jrec_prefix prefix; uint4 filler; jrec_suffix suffix; } struct_jrec_pfin; /* Following 3 are same structures. In case we change it in future, let's define them separately */ typedef struct /* fixed length */ { jrec_prefix prefix; seq_num jnl_seqno; /* must start at 8-byte boundary */ seq_num strm_seqno; /* see "struct_jrec_upd" for comment on the purpose of this field */ uint4 filler; jrec_suffix suffix; } struct_jrec_null; typedef struct /* fixed length */ { jrec_prefix prefix; seq_num jnl_seqno; /* must start at 8-byte boundary */ block_id blks_to_upgrd; /* blocks-to-upgrade counter at time of epoch */ block_id free_blocks; /* free blocks counter at time of epoch */ block_id total_blks; /* total blocks counter at time of epoch */ boolean_t fully_upgraded; /* cs_data->fully_upgraded at the time of epoch */ uint4 filler0; /* so as to make 8-byte alignment explicit */ seq_num strm_seqno[MAX_SUPPL_STRMS]; /* seqno of each possible supplementary stream at epoch time. * used by rollback to restore seqnos on the database. */ uint4 filler1; /* so as to make the EPOCH record aligned to 8 byte boundary */ jrec_suffix suffix; } struct_jrec_epoch; typedef struct /* fixed length */ { jrec_prefix prefix; seq_num jnl_seqno; /* must start at 8-byte boundary */ uint4 filler; jrec_suffix suffix; } struct_jrec_eof; typedef struct /* fixed length */ { jrec_prefix prefix; /* 24 bytes */ block_id orig_total_blks; block_id orig_free_blocks; block_id total_blks_after_trunc; uint4 filler; /* so as to make the TRUNC record aligned to 8 byte boundary */ jrec_suffix suffix; /* 4 bytes */ } struct_jrec_trunc; typedef union { jrec_prefix prefix; struct_jrec_upd jrec_set_kill; /* JRT_SET or JRT_KILL or JRT_ZTRIG record will use this format */ struct_jrec_ztworm jrec_ztworm; struct_jrec_lgtrig jrec_lgtrig; struct_jrec_blk jrec_pblk, jrec_aimg; struct_jrec_align jrec_align; /** All below are fixed size and above are variable size records */ struct_jrec_tcom jrec_tcom; struct_jrec_ztcom jrec_ztcom; struct_jrec_inctn jrec_inctn; struct_jrec_pini jrec_pini; struct_jrec_pfin jrec_pfin; struct_jrec_null jrec_null; struct_jrec_epoch jrec_epoch; struct_jrec_eof jrec_eof; struct_jrec_trunc jrec_trunc; } jnl_record; /* Macro to access fixed size record's size */ #define TCOM_RECLEN SIZEOF(struct_jrec_tcom) #define ZTCOM_RECLEN SIZEOF(struct_jrec_ztcom) #define INCTN_RECLEN SIZEOF(struct_jrec_inctn) #define PINI_RECLEN SIZEOF(struct_jrec_pini) #define PFIN_RECLEN SIZEOF(struct_jrec_pfin) #define NULL_RECLEN SIZEOF(struct_jrec_null) #define EPOCH_RECLEN SIZEOF(struct_jrec_epoch) #define EOF_RECLEN SIZEOF(struct_jrec_eof) #define TRUNC_RECLEN SIZEOF(struct_jrec_trunc) /* Macro to access variable size record's fixed part's size */ #define FIXED_ZTWORM_RECLEN OFFSETOF(struct_jrec_ztworm, ztworm_str) #define FIXED_LGTRIG_RECLEN OFFSETOF(struct_jrec_lgtrig, lgtrig_str) #define FIXED_UPD_RECLEN OFFSETOF(struct_jrec_upd, mumps_node) #define FIXED_ALIGN_RECLEN SIZEOF(struct_jrec_align) #define MIN_ALIGN_RECLEN (FIXED_ALIGN_RECLEN + JREC_SUFFIX_SIZE) #define FIXED_BLK_RECLEN OFFSETOF(struct_jrec_blk, blk_contents[0]) #define FIXED_PBLK_RECLEN OFFSETOF(struct_jrec_blk, blk_contents[0]) #define FIXED_AIMG_RECLEN OFFSETOF(struct_jrec_blk, blk_contents[0]) #define MIN_PBLK_RECLEN (OFFSETOF(struct_jrec_blk, blk_contents[0]) + JREC_SUFFIX_SIZE) #define MIN_AIMG_RECLEN (OFFSETOF(struct_jrec_blk, blk_contents[0]) + JREC_SUFFIX_SIZE) #define JREC_PREFIX_SIZE SIZEOF(jrec_prefix) #define JREC_SUFFIX_SIZE SIZEOF(jrec_suffix) #define MIN_JNLREC_SIZE (FIXED_ALIGN_RECLEN + JREC_SUFFIX_SIZE) /* An ALIGN record is the smallest possible record */ #define JREC_PREFIX_UPTO_LEN_SIZE (OFFSETOF(jrec_prefix, pini_addr)) /* JNL_FILE_TAIL_PRESERVE macro indicates maximum number of bytes to ensure allocated at the end of the journal file * to store the journal records that will be written whenever the journal file gets closed. * (i) Any process closing the journal file needs to write at most one PINI, one EPOCH, one PFIN and one EOF record * In case of wcs_recover extra INCTN will be written * (ii) We may need to give room for twice the above space to accommodate the EOF writing by a process that closes the journal * and the EOF writing by the first process that reopens it and finds no space left and switches to a new journal. * (iii) We may need to write one ALIGN record at the most since the total calculated from (i) and (ii) above is * less than the minimum alignsize that we support (asserted before using JNL_FILE_TAIL_PRESERVE in macros below) * The variable portion of this ALIGN record can get at the most equal to the maximum of the sizes of the * PINI/EPOCH/PFIN/EOF record. We know PINI_RECLEN is maximum of EPOCH_RECLEN, PFIN_RECLEN, EOF_RECLEN (this * is in fact asserted in gvcst_init.c). */ #define JNL_FILE_TAIL_PRESERVE (MIN_ALIGN_RECLEN + (PINI_RECLEN + EPOCH_RECLEN + INCTN_RECLEN + \ PFIN_RECLEN + EOF_RECLEN) * 2 + PINI_RECLEN) typedef struct set_jnl_options_struct { int cli_journal, cli_enable, cli_on, cli_replic_on; boolean_t alignsize_specified, allocation_specified, autoswitchlimit_specified, image_type_specified, /* beofre/nobefore option specified */ buffer_size_specified, epoch_interval_specified, extension_specified, filename_specified, sync_io_specified, yield_limit_specified; /* since jnl_create_info does not have following fields, we need them here */ boolean_t sync_io; int4 yield_limit; } set_jnl_options; /* rlist_state needed to be moved here to use with mu_set_reglist */ enum rlist_state { NONALLOCATED, ALLOCATED, DEALLOCATED }; /* mu_set_reglist needed to be moved here for the journal specific fields */ /* ATTN: the first four items in this structure need to be identical to those * in structure tp_region in tp.h. */ typedef struct mu_set_reglist { struct mu_set_reglist *fPtr; /* all fields after this are used for mupip_set_journal.c */ gd_region *reg; char unique_id[UNIQUE_ID_SIZE]; int4 fid_index; enum rlist_state state; sgmnt_data_ptr_t sd; bool exclusive; /* standalone access is required for this region */ int fd; enum jnl_state_codes jnl_new_state; enum repl_state_codes repl_new_state; boolean_t before_images; } mu_set_rlist; typedef void (*pini_addr_reset_fnptr)(sgmnt_addrs *csa); typedef struct { token_num mur_jrec_seqno; /* This is jnl_seqno of the current record that backward * recovery/rollback is playing in its forward phase. */ token_num mur_jrec_strm_seqno; /* This is the strm_seqno of the current record that backward * recovery/rollback is playing in its forward phase. */ unsigned short filler_short; unsigned short mur_jrec_participants; jnl_tm_t gbl_jrec_time; jnl_tm_t mur_tp_resolve_time; /* tp resolve time as determined by journal recovery. * Time of the point upto which a region will be processed for * TP token resolution for backward or forward recover. * Note : This is what prevents user to change system time. */ boolean_t forw_phase_recovery; boolean_t mur_rollback; /* a copy of mur_options.rollback to be accessible to runtime code */ boolean_t mupip_journal; /* the current command is a MUPIP JOURNAL command */ boolean_t dont_reset_gbl_jrec_time; /* Do not reset gbl_jrec_time */ pini_addr_reset_fnptr mur_pini_addr_reset_fnptr; /* function pointer to invoke "mur_pini_addr_reset" */ uint4 cumul_jnl_rec_len; /* cumulative length of the replicated journal records * for the current TP or non-TP transaction */ boolean_t wait_for_jnl_hard; uint4 tp_ztp_jnl_upd_num; /* Incremented whenever a journaled update happens inside of * TP or ZTP. Copied over to the corresponding journal record * to record the sequence of all updates inside TP/ZTP transaction. */ uint4 mur_jrec_nodeflags; /* copy of "nodeflags" from jnl record currently being played */ # ifdef GTM_TRIGGER unsigned char *prev_ztworm_ptr; /* Non-NULL if at least one ztwormhole record was successfully * formatted in this transaction. Note that ZTWORMHOLE records are * formatted ONLY in case of journaled & replicated databases. * 1. If replicated database is unencrypted, this points to * jfb->buff + FIXED_UPD_RECLEN * 2. If replicated database is encrypted, this points to * jfb->alt_buff + FIXED_UPD_RECLEN * If no ztwormhole record is yet formatted, then points to NULL */ unsigned char *save_ztworm_ptr; /* copy of prev_ztworm_ptr saved until we know for sure whether * a ZTWORMHOLE journal record will be written or not. */ # endif # ifdef DEBUG boolean_t mur_fences_none; /* a copy of mur_options.fences to be accessible to runtime code */ uint4 cumul_index; uint4 cu_jnl_index; uint4 max_tp_ztp_jnl_upd_num; /* Max of all values processed in this * potentially multi-region transaction. Used only by jnl recovery. */ boolean_t mur_options_forward; /* a copy of mur_options.forward to be accessible to GT.M runtime */ boolean_t in_mupjnl; /* TRUE if caller is a MUPIP JOURNAL command */ # endif boolean_t onlnrlbk; /* TRUE if ONLINE ROLLBACK */ boolean_t mur_extract; /* a copy of mur_options.extr[0] to be accessible to GTM runtime*/ boolean_t save_dont_reset_gbl_jrec_time; /* save a copy of dont_reset_gbl_jrec_time */ boolean_t mur_update; /* a copy of mur_options.update to be accessible to GTM runtime */ } jnl_gbls_t; #define IN_PHASE2_JNL_COMMIT(CSA) (CSA->t_commit_crit) /* If a transaction's total journal record size exceeds the journal buffer size (minus one filesystem-block-size * just to be safe) we might have to flush a part of the transaction's journal records before writing the remaining * part. For this reason, we write the entire transaction's journal records while holding crit as otherwise it gets * tricky to handle process kills after it has written a part of the journal records but not all. */ #define IS_PHASE2_JNL_COMMIT_NEEDED_IN_CRIT(JBP, JREC_LEN) ((JREC_LEN + (JBP)->fs_block_size) > (JBP)->size) #define JB_FREEADDR_APPROPRIATE(IN_PHASE2, JPC, JB) (IN_PHASE2 ? JPC->phase2_freeaddr : JB->rsrv_freeaddr) #define JB_FREE_APPROPRIATE(IN_PHASE2, JPC, JB) (IN_PHASE2 ? JPC->phase2_free : JB->rsrv_free) #define JB_CURR_TN_APPROPRIATE(IN_PHASE2, JPC, CSA) (IN_PHASE2 ? JPC->curr_tn : CSA->ti->curr_tn) #define ASSERT_JNL_PHASE2_COMMIT_INDEX_IS_VALID(IDX, MAX_INDEX) assert((0 <= IDX) && (MAX_INDEX > IDX)) #define INCR_PHASE2_COMMIT_INDEX(IDX, PHASE2_COMMIT_ARRAY_SIZE) \ MBSTART { \ if (PHASE2_COMMIT_ARRAY_SIZE == ++IDX) \ IDX = 0; \ } MBEND \ #define DECR_PHASE2_COMMIT_INDEX(IDX, PHASE2_COMMIT_ARRAY_SIZE) \ MBSTART { \ if (0 == IDX--) \ IDX = PHASE2_COMMIT_ARRAY_SIZE - 1; \ } MBEND \ /* Sets jbp->rsrv_freeaddr & jbp->rsrv_free. They need to be kept in sync at all times */ #define SET_JBP_RSRV_FREEADDR(JBP, RSRV_FREEADDR) \ { \ assert(JBP->freeaddr <= RSRV_FREEADDR); \ JBP->rsrv_freeaddr = RSRV_FREEADDR; \ JBP->rsrv_free = (RSRV_FREEADDR) % JBP->size; \ } #define SET_JPC_PHASE2_FREEADDR(JPC, JBP, FREEADDR) \ { \ JPC->phase2_freeaddr = FREEADDR; \ JPC->phase2_free = (FREEADDR) % JBP->size; \ } /* Test an edge case when a process is killed just before phase2_commit_index2 is updated at the end of UPDATE_JBP_RSRV_FREEADDR */ #ifdef DEBUG #define DECR_INDEX2_AND_KILL(IDX2, IDX1) \ MBSTART { \ /* gtm_white_box_test_case_count is set to 1/0 by test system. Then in t_end/tp_tend (if it's 0) \ * or in jnl_write (if it's 1), gtm_white_box_test_case_count is set to 2 : to kill the process. \ */ \ if (2 == gtm_white_box_test_case_count) \ { \ DECR_PHASE2_COMMIT_INDEX(IDX2, JNL_PHASE2_COMMIT_ARRAY_SIZE); \ assert(IDX2 == IDX1); \ kill(process_id, SIGKILL); \ } \ } MBEND #else /* #ifdef DEBUG */ #define DECR_INDEX2_AND_KILL(IDX2, IDX1) /* No-OP */ #endif /* #ifdef DEBUG */ #define UPDATE_JBP_RSRV_FREEADDR(CSA, JPC, JBP, JPL, RLEN, INDEX, IN_PHASE2, JNL_SEQNO, STRM_SEQNO, REPLICATION) \ MBSTART { \ uint4 rsrv_freeaddr; \ int nextIndex, endIndex; \ jbuf_phase2_in_prog_t *phs2cmt; \ \ GBLREF uint4 process_id; \ GBLREF uint4 dollar_tlevel; \ \ assert(CSA->now_crit); \ /* The following condition implies that a update/MUMPS process was killed in CMT06, right before updating \ * JBP->phase2_commit_index2. Increment index2 & call jnl_phase2_cleanup() to process it as a dead commit. \ */ \ SHM_READ_MEMORY_BARRIER; /* Ensure the indices read from memory are correct */ \ if ((JBP->phase2_commit_index2 == JBP->phase2_commit_index1) && (JBP->freeaddr < JBP->rsrv_freeaddr) \ && ((JBP->phase2_commit_array[JBP->phase2_commit_index1].start_freeaddr + \ JBP->phase2_commit_array[JBP->phase2_commit_index1].tot_jrec_len) == JBP->rsrv_freeaddr)) \ { \ INCR_PHASE2_COMMIT_INDEX(JBP->phase2_commit_index2, JNL_PHASE2_COMMIT_ARRAY_SIZE); \ jnl_phase2_cleanup(CSA, JBP); \ } \ /* Allocate a slot. But before that, check if the slot array is full. \ * endIndex + 1 == first_index implies full. Note: INCR_PHASE2_COMMIT_INDEX macro does the + 1 \ * endIndex == first_index implies empty. \ */ \ endIndex = JBP->phase2_commit_index2; \ nextIndex = endIndex; \ INCR_PHASE2_COMMIT_INDEX(nextIndex, JNL_PHASE2_COMMIT_ARRAY_SIZE); \ while (nextIndex == JBP->phase2_commit_index1) \ { /* Slot array is full. Wait for phase2 to finish. */ \ jnl_phase2_cleanup(CSA, JBP); \ if (nextIndex != JBP->phase2_commit_index1) \ break; \ BG_TRACE_PRO_ANY(CSA, jnlbuff_phs2cmt_array_full); \ SLEEP_USEC(1, FALSE); \ } \ ASSERT_JNL_PHASE2_COMMIT_INDEX_IS_VALID(endIndex, JNL_PHASE2_COMMIT_ARRAY_SIZE); \ phs2cmt = &JBP->phase2_commit_array[endIndex]; \ phs2cmt->process_id = process_id; \ assert(JPC->curr_tn == CSA->ti->curr_tn); \ phs2cmt->curr_tn = JPC->curr_tn; \ assert(!REPLICATION || JNL_SEQNO); \ phs2cmt->jnl_seqno = JNL_SEQNO; \ assert(!REPLICATION \ || !((repl_inst_hdr_ptr_t)((sm_uc_ptr_t)JPL + ((jnlpool_ctl_ptr_t)JPL)->filehdr_off))->is_supplementary \ || STRM_SEQNO); \ phs2cmt->strm_seqno = STRM_SEQNO; \ rsrv_freeaddr = JBP->rsrv_freeaddr; \ phs2cmt->start_freeaddr = rsrv_freeaddr; \ phs2cmt->tot_jrec_len = RLEN; \ /* The below assert ensures "jnl_phase2_salvage" can definitely replace this transaction's journal \ * records with a combination of JRT_NULL/JRT_INCTN/JRT_ALIGN records. See comment there for why \ * this is necessary and how this assert achieves that. \ * In case of the forward phase of MUPIP JOURNAL RECOVER or ROLLBACK, replication would be turned off \ * but it is possible it plays forward a pre-existing NULL record. Account for that below. \ */ \ assert(!IN_PHASE2 \ || (!REPLICATION && ((RLEN == INCTN_RECLEN) || (RLEN >= (INCTN_RECLEN + MIN_ALIGN_RECLEN)))) \ || ((REPLICATION || jgbl.forw_phase_recovery) \ && ((RLEN == NULL_RECLEN) || (RLEN >= (NULL_RECLEN + MIN_ALIGN_RECLEN))))); \ phs2cmt->pini_addr = JPC->pini_addr; \ phs2cmt->jrec_time = jgbl.gbl_jrec_time; \ phs2cmt->in_phase2 = IN_PHASE2; \ phs2cmt->replication = REPLICATION; \ phs2cmt->write_complete = FALSE; \ rsrv_freeaddr += RLEN; \ assert(rsrv_freeaddr <= JBP->next_align_addr); \ assert(rsrv_freeaddr > JBP->freeaddr); \ INDEX = endIndex; \ SET_JBP_RSRV_FREEADDR(JBP, rsrv_freeaddr); \ SHM_WRITE_MEMORY_BARRIER; /* see corresponding SHM_READ_MEMORY_BARRIER in "jnl_phase2_cleanup" */ \ JBP->phase2_commit_index2 = nextIndex; \ if (WBTEST_ENABLED(WBTEST_MURUNDOWN_KILLCMT06)) \ { \ DECR_INDEX2_AND_KILL(JBP->phase2_commit_index2, JBP->phase2_commit_index1); \ } \ } MBEND #define JNL_PHASE2_WRITE_COMPLETE(CSA, JBP, INDEX, NEW_FREEADDR) \ MBSTART { \ jbuf_phase2_in_prog_t *phs2cmt; \ \ GBLREF uint4 process_id; \ \ phs2cmt = &JBP->phase2_commit_array[INDEX]; \ assert(phs2cmt->process_id == process_id); \ assert(FALSE == phs2cmt->write_complete); \ assert(((phs2cmt->start_freeaddr + phs2cmt->tot_jrec_len) == NEW_FREEADDR) \ || (gtm_white_box_test_case_enabled \ && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number))); \ phs2cmt->write_complete = TRUE; \ /* Invoke "jnl_phase2_cleanup" sparingly as it calls "grab_latch". So we do it twice. \ * Once at half-way mark and once when a wrap occurs. \ */ \ if (!INDEX || ((JNL_PHASE2_COMMIT_ARRAY_SIZE / 2) == INDEX)) \ jnl_phase2_cleanup(CSA, JBP); \ } MBEND #define JNL_PHASE2_CLEANUP_IF_POSSIBLE(CSA, JBP) \ MBSTART { \ if (JBP->phase2_commit_index1 != JBP->phase2_commit_index2) \ jnl_phase2_cleanup(CSA, JBP); /* There is something to be cleaned up */ \ } MBEND #define NEED_TO_FINISH_JNL_PHASE2(JRS) ((NULL != JRS) && JRS->tot_jrec_len) #define FINISH_JNL_PHASE2_IN_JNLBUFF(CSA, JRS) \ MBSTART { \ assert(NEED_TO_FINISH_JNL_PHASE2(JRS)); \ jnl_write_phase2(CSA, JRS); /* Mark jnl record writing into jnlbuffer as complete */ \ assert(!NEED_TO_FINISH_JNL_PHASE2(JRS)); \ } MBEND #define FINISH_JNL_PHASE2_IN_JNLPOOL_IF_NEEDED(REPLICATION, JNLPOOL) \ MBSTART { \ if (REPLICATION && JNLPOOL->jrs.tot_jrec_len) \ { \ JPL_PHASE2_WRITE_COMPLETE(JNLPOOL); /* Mark jnl record writing into jnlpool as complete */ \ assert(0 == JNLPOOL->jrs.tot_jrec_len); \ } \ } MBEND #define NONTP_FINISH_JNL_PHASE2_IN_JNLBUFF_AND_JNLPOOL(CSA, JRS, REPLICATION, JNLPOOL) \ MBSTART { \ FINISH_JNL_PHASE2_IN_JNLBUFF(CSA, JRS); /* Step CMT16 (if BG), Step CMT06a (if MM) */ \ assert(!REPLICATION || (!csa->jnlpool || (csa->jnlpool == JNLPOOL))); \ FINISH_JNL_PHASE2_IN_JNLPOOL_IF_NEEDED(REPLICATION, JNLPOOL); /* Step CMT17 (if BG), Step CMT06b (if MM) */ \ } MBEND #define TP_FINISH_JNL_PHASE2_IN_JNLBUFF_AND_JNLPOOL(JNL_FENCE_CTL, REPLICATION, JNLPOOL) \ MBSTART { \ sgmnt_addrs *csa; \ sgm_info *si; \ \ for (csa = JNL_FENCE_CTL.fence_list; JNL_FENCE_LIST_END != csa; csa = csa->next_fenced) \ { \ si = csa->sgm_info_ptr; \ assert(si->update_trans); \ jrs = si->jbuf_rsrv_ptr; \ assert(JNL_ALLOWED(csa)); \ assert(!REPLICATION || (!csa->jnlpool || (csa->jnlpool == JNLPOOL))); \ if (NEED_TO_FINISH_JNL_PHASE2(jrs)) \ FINISH_JNL_PHASE2_IN_JNLBUFF(csa, jrs); /* Step CMT16 (if BG) */ \ } \ FINISH_JNL_PHASE2_IN_JNLPOOL_IF_NEEDED(replication, JNLPOOL); /* Step CMT17 (if BG) */ \ } MBEND /* BEGIN : Structures used by "jnl_write_reserve" */ typedef struct { enum jnl_record_type rectype; /* equivalent to jrec_prefix.jrec_type */ uint4 reclen; /* equivalent to jrec_prefix.forwptr */ void *param1; } jrec_rsrv_elem_t; typedef struct { uint4 alloclen; /* # of "jrec_rsrv_elem_t" structures allocated in "jrs_array" array */ uint4 usedlen; /* # of "jrec_rsrv_elem_t" structures currently used in "jrs_array" array */ uint4 tot_jrec_len; /* Total length (in bytes) of jnl records that will be used up by this array */ uint4 phase2_commit_index; /* index into corresponding jb->phase2_commit_array entry */ jrec_rsrv_elem_t *jrs_array; /* Pointer to array of "jrec_rsrv_elem_t" structures currently allocated */ } jbuf_rsrv_struct_t; #define ALLOC_JBUF_RSRV_STRUCT(JRS, CSA) \ MBSTART { \ JRS = (jbuf_rsrv_struct_t *)malloc(SIZEOF(jbuf_rsrv_struct_t)); \ (JRS)->alloclen = 0; /* initialize "alloclen" */ \ (JRS)->jrs_array = NULL; \ REINIT_JBUF_RSRV_STRUCT(JRS, CSA, CSA->jnl, NULL);/* initialize "usedlen" and "tot_jrec_len" */ \ } MBEND #define REINIT_JBUF_RSRV_STRUCT(JRS, CSA, JPC, JBP) \ MBSTART { \ uint4 freeaddr; \ \ (JRS)->usedlen = 0; \ (JRS)->tot_jrec_len = 0; \ (JPC)->curr_tn = CSA->ti->curr_tn; \ if (NULL != JBP) \ { \ freeaddr = ((jnl_buffer_ptr_t)JBP)->rsrv_freeaddr; \ JPC->phase2_freeaddr = freeaddr; \ } \ } MBEND #define UPDATE_JRS_RSRV_FREEADDR(CSA, JPC, JBP, JRS, JPL, JNL_FENCE_CTL, REPLICATION) \ MBSTART { \ SET_JNLBUFF_PREV_JREC_TIME(JBP, jgbl.gbl_jrec_time, DO_GBL_JREC_TIME_CHECK_TRUE); \ /* Keep jb->prev_jrec_time up to date */ \ assert(JNL_ENABLED(CSA)); \ assert(JBP->rsrv_freeaddr >= JBP->freeaddr); \ assert(0 < JRS->tot_jrec_len); \ UPDATE_JBP_RSRV_FREEADDR(CSA, JPC, JBP, JPL, JRS->tot_jrec_len, JRS->phase2_commit_index, TRUE, \ JNL_FENCE_CTL.token, JNL_FENCE_CTL.strm_seqno, REPLICATION); \ } MBEND #define FREE_JBUF_RSRV_STRUCT(JRS) \ MBSTART { \ assert(NULL != JRS); \ if (NULL != JRS) \ { \ if (NULL != JRS->jrs_array) \ free(JRS->jrs_array); \ free(JRS); \ JRS = NULL; \ } \ } MBEND #define INIT_NUM_JREC_RSRV_ELEMS 4 /* END : Structures used by "jnl_write_reserve" */ #define JNL_SHARE_SIZE(X) (JNL_ALLOWED(X) ? \ (ROUND_UP(JNL_NAME_EXP_SIZE + SIZEOF(jnl_buffer), OS_PAGE_SIZE) \ + ROUND_UP(((sgmnt_data_ptr_t)X)->jnl_buffer_size * DISK_BLOCK_SIZE, \ OS_PAGE_SIZE) + OS_PAGE_SIZE) : 0) /* pass address of jnl_buffer to get address of expanded jnl file name */ #define JNL_GDID_PVT(CSA) ((CSA)->jnl->fileid) #define JNL_GDID_PTR(CSA) ((gd_id_ptr_t)(&((CSA)->nl->jnl_file.u))) /* Note that since "cycle" (in jpc and jb below) can rollover the 4G limit back to 0, it should * only be used to do "!=" checks and never to do ordered checks like "<", ">", "<=" or ">=". * Use JNL_FILE_SWITCHED when only JPC is available. Use JNL_FILE_SWITCHED2 when JPC and JB (i.e. JPC->jnl_buff) * are both available. The latter saves a dereference in t_end/tp_tend (every drop helps while inside crit). */ #define JNL_FILE_SWITCHED(JPC) ((JPC)->cycle != (JPC)->jnl_buff->cycle) #define JNL_FILE_SWITCHED2(JPC, JB) ((JPC)->cycle != (JB)->cycle) /* The jrec_len of 0 causes a switch. */ #define SWITCH_JNL_FILE(JPC) jnl_file_extend(JPC, 0) #define REG_STR "region" #define FILE_STR "database file" /* Given a journal record, get_jnl_seqno returns the jnl_seqno field * Now all replication type records, EOF and EPOCH have the jnl_seqno at the same offset. * Modify the macro GET_JNL_SEQNO if offset of jnl_seqno is changed for any journal records */ #define GET_JNL_SEQNO(j) (((jnl_record *)(j))->jrec_null.jnl_seqno) #define GET_STRM_SEQNO(j) (((jnl_record *)(j))->jrec_null.strm_seqno) #define GET_REPL_JNL_SEQNO(j) (IS_REPLICATED(((jrec_prefix *)j)->jrec_type) ? GET_JNL_SEQNO(j) : 0) /* Note: JRT_ALIGN record does not have a "prefix.tn" or "prefix.pini_addr" field (all other jnl records have it) * and has "prefix.checksum" at a different offset. Hence the below GET_JREC* and SET_JREC* macros. IF the caller knows * for sure that "RECTYPE" is not a JRT_ALIGN, then it can directly use "prefix.tn" etc. ELSE it has to use the below macros. */ #define GET_JREC_TN(JNLREC, RECTYPE) ((JRT_ALIGN != RECTYPE) \ ? (DBG_ASSERT(TN_INVALID != JNLREC->prefix.tn) JNLREC->prefix.tn) : TN_INVALID) /* A JRT_ALIGN record does not have a "pini_addr" field so treat it as pointing to the first PINI record in the journal file * for the purposes of a journal extract etc. */ #define GET_JREC_PINI_ADDR(JNLREC, RECTYPE) ((JRT_ALIGN != RECTYPE) ? JNLREC->prefix.pini_addr : JNL_FILE_FIRST_RECORD) #define GET_JREC_CHECKSUM(JNLREC, RECTYPE) ((JRT_ALIGN != RECTYPE) \ ? JNLREC->prefix.checksum : ((struct_jrec_align *)JNLREC)->checksum) #define SET_JREC_CHECKSUM(JNLREC, RECTYPE, CHECKSUM) \ MBSTART { \ if (JRT_ALIGN != RECTYPE) \ JNLREC->prefix.checksum = CHECKSUM; \ else \ ((struct_jrec_align *)JNLREC)->checksum = CHECKSUM; \ } MBEND /* For MUPIP JOURNAL -ROLLBACK, getting the strm_reg_seqno from the file header is not as straightforward * as accessing csd->strm_reg_seqno[idx]. This is because it increments this field in mur_output_record even * before we reach t_end/tp_tend. That is done for convenience of the implementation. But this assumes that the * commit has actually completed. Therefore, in case we need to invoke jnl_file_extend() inside t_end/tp_tend even * before the commit, we would see an incorrect value of csd->strm_reg_seqno[idx]. In that case, we use the * global variable jgbl.mur_jrec_strm_seqno to identify if the strm_reg_seqno[idx] value is 1 more than that and * if so return 1 lesser than that as the real strm_reg_seqno[idx]. This is used by routines that write journal * records (EPOCH, jfh->strm_end_seqno etc.) to write the correct strm_seqno. Not doing so will cause the strm_seqno * to be higher than necessary and confuse everything else (including rollback) as far as replication is concerned. * Note: We check for process_exiting to differentiate between calls made from mur_close_files() to before. Once we * reach mur_close_files, we should no longer be in an active transaction and so we don't need to make any adjustments. * VMS does not support supplementary instances so the below macro does not apply there at all. */ #define MUR_ADJUST_STRM_REG_SEQNO_IF_NEEDED(CSD, DST) \ { \ int strm_num; \ seq_num strm_seqno; \ \ GBLREF int process_exiting; \ \ if (jgbl.mur_jrec_strm_seqno && !process_exiting) \ { \ assert(jgbl.mur_rollback); \ /* This macro should be called only when journal records are about to be written \ * and that is not possible in case of a FORWARD rollback. Assert that. \ */ \ assert(!jgbl.mur_options_forward); \ strm_seqno = jgbl.mur_jrec_strm_seqno; \ strm_num = GET_STRM_INDEX(strm_seqno); \ strm_seqno = GET_STRM_SEQ60(strm_seqno); \ if (CSD->strm_reg_seqno[strm_num] == (strm_seqno + 1)) \ { \ assert(DST[strm_num] == (strm_seqno + 1)); \ DST[strm_num] = strm_seqno; \ } \ } \ } /* In t_end(), we need to write the after-image if DSE or mupip recover/rollback is playing it. * But to write it out, we should have it already built before bg_update(). * Hence, we pre-build the block here itself before invoking t_end(). */ #define BUILD_AIMG_IF_JNL_ENABLED(CSD, TN) \ { \ GBLREF cw_set_element cw_set[]; \ GBLREF unsigned char cw_set_depth; \ GBLREF jnl_format_buffer *non_tp_jfb_ptr; \ \ cw_set_element *cse; \ \ if (JNL_ENABLED(CSD)) \ { \ assert(1 == cw_set_depth); /* Only DSE uses this macro and it updates one block at a time */ \ cse = (cw_set_element *)(&cw_set[0]); \ cse->new_buff = (unsigned char *)non_tp_jfb_ptr->buff; \ gvcst_blk_build(cse, (uchar_ptr_t)cse->new_buff, TN); \ cse->done = TRUE; \ } \ } /* In Unix, the journal file header size is currently set to 64K so it is aligned with any possible filesystem block size * known at this point. This will help us do aligned writes to the journal file header as well as the journal file contents * without needing to mix both of them in the same aligned disk write. In VMS, we continue with 512-byte alignment so no change. * Note that the journal_file_header structure is only 2K currently and is captured using the REAL_JNL_HDR_LEN macro while * the padded 64K file header is captured using the JNL_HDR_LEN macro. Use either one as appropriate in the code. Both of them * are identical in VMS where it is currently 2K. */ #define REAL_JNL_HDR_LEN SIZEOF(jnl_file_header) #define JNL_HDR_LEN 64 * 1024 #define JNL_FILE_FIRST_RECORD JNL_HDR_LEN /* Minimum possible journal file size */ #define MIN_JNL_FILE_SIZE (JNL_HDR_LEN + PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN + EOF_RECLEN) /* maximum required journal file size (in 512-byte blocks), if the current transaction was the only one in a fresh journal file */ #define MAX_REQD_JNL_FILE_SIZE(tot_jrec_size) DIVIDE_ROUND_UP((tot_jrec_size + MIN_JNL_FILE_SIZE), DISK_BLOCK_SIZE) /* this macro aligns the input size to account that journal file sizes can increase only in multiples of the extension size */ #define ALIGNED_ROUND_UP(tmp_tot_jrec_size, jnl_alq, jnl_deq) \ (((tmp_tot_jrec_size) <= (jnl_alq) || !(jnl_deq)) \ ? (jnl_alq) \ : ((jnl_alq) + ROUND_UP((tmp_tot_jrec_size) - (jnl_alq), (jnl_deq)))) /* this macro aligns the input size to account that journal file sizes can increase only in multiples of the extension size */ #define ALIGNED_ROUND_DOWN(tmp_tot_jrec_size, jnl_alq, jnl_deq) \ (((tmp_tot_jrec_size) <= (jnl_alq) || !(jnl_deq)) \ ? (jnl_alq) \ : ((jnl_alq) + ROUND_DOWN((tmp_tot_jrec_size) - (jnl_alq), (jnl_deq)))) /* the following macro uses 8-byte quantities (gtm_uint64_t) to perform additions that might cause a 4G overflow */ #define DISK_BLOCKS_SUM(freeaddr, jrec_size) DIVIDE_ROUND_UP((((gtm_uint64_t)(freeaddr)) + (jrec_size)), DISK_BLOCK_SIZE) /* For future portability JNLBUFF_ALLOC is defined in jnl.h instead of jnlsp.h */ #define JPC_ALLOC(csa) \ { \ csa->jnl = (jnl_private_control *)malloc(SIZEOF(*csa->jnl)); \ memset(csa->jnl, 0, SIZEOF(*csa->jnl)); \ } #define ASSERT_JNLFILEID_NOT_NULL(csa) \ { \ assert(0 != csa->nl->jnl_file.u.inode); \ assert(0 != csa->nl->jnl_file.u.device); \ } #define NULLIFY_JNL_FILE_ID(csa) \ { \ csa->nl->jnl_file.u.inode = 0; \ csa->nl->jnl_file.u.device = 0; \ } #define JNL_INIT(csa, reg, csd) \ { \ csa->jnl_state = csd->jnl_state; \ csa->jnl_before_image = csd->jnl_before_image; \ csa->repl_state = csd->repl_state; \ if JNL_ALLOWED(csa) \ { \ JPC_ALLOC(csa); \ csa->jnl->region = reg; \ csa->jnl->jnl_buff = (jnl_buffer_ptr_t)((sm_uc_ptr_t)(csa->nl) + NODE_LOCAL_SPACE(csd) + JNL_NAME_EXP_SIZE); \ csa->jnl->channel = NOJNL; \ } else \ csa->jnl = NULL; \ } #define JNL_FD_CLOSE(CHANNEL, RC) \ { \ fd_type lcl_channel; \ \ /* Reset incoming channel BEFORE closing it. This way, if we get interrupted BEFORE the close but \ * after we have reset channel, we could at most end up with a file descriptor leak. Doing it the \ * other way around could cause us to close the channel but yet have a dangling pointer to it that \ * could result in more than one close of the same file descriptor where the second close could \ * be on some other valid open file descriptor. \ */ \ lcl_channel = CHANNEL; \ CHANNEL = NOJNL; \ CLOSEFILE_RESET(lcl_channel, RC); /* resets "lcl_channel" to FD_INVALID */ \ assert(SS_NORMAL == RC); \ } #define MAX_EPOCH_DELAY 30 #define PREFIX_ROLLED_BAK "rolled_bak_" #define REC_TOKEN(jnlrec) ((struct_jrec_upd *)jnlrec)->token_seq.token #define REC_JNL_SEQNO(jnlrec) ((struct_jrec_upd *)jnlrec)->token_seq.jnl_seqno #define REC_LEN_FROM_SUFFIX(ptr, reclen) ((jrec_suffix *)((unsigned char *)ptr + reclen - JREC_SUFFIX_SIZE))->backptr /* The below macro now relies on MAX_STRLEN value rather than on CSD->blk_size used previously because * with nodes spanning blocks journal records might be comprised of several blocks, with the limit of * MAX_STRLEN for the actual database record. But that only takes care of the value part of the record. * The key can still be MAX_KEY_SZ long. So take that into account as well. */ #define JNL_MAX_SET_KILL_RECLEN(CSD) (uint4)ROUND_UP2((FIXED_UPD_RECLEN + JREC_SUFFIX_SIZE) \ + MAX_KEY_SZ + MAX_STRLEN \ + SIZEOF(jnl_str_len_t) + SIZEOF(mstr_len_t), JNL_REC_START_BNDRY) #define JNL_MAX_PBLK_RECLEN(CSD) (uint4)ROUND_UP2(MIN_PBLK_RECLEN + (CSD)->blk_size, JNL_REC_START_BNDRY) /* Macro to compute the maximum possible journal record length in the journal file. * In order to compute the maximum jnl record length, note that an align record is written whenever * a would cause the jnl file offset to move past an aligned boundary. * Therefore after computing the maximum possible non-align-jnl-record-length, we need to add MIN_ALIGN_RECLEN * as this is the maximum possible align-jnl-record-length and should be the eventual max_jrec_len. */ #define JNL_MAX_RECLEN(JINFO, CSD) \ { \ /* This macro used to compare the value returned from JNL_MAX_SET_KILL_RECLEN with that from \ * JNL_MAX_PBLK_RECLEN and, in case of triggers, MAX_ZTWORK_JREC_LEN. However, in the current design \ * max_logi_reclen includes MAX_STR_LEN as one of its summants, thus always exceeding both \ * MAX_ZTWORK_JREC_LEN and JNL_MAX_PBLK_RECLEN. \ * \ * A logical record is a SET/KILL record. The SET could be as big as (CSD)->max_rec_size, but since \ * csd->max_rec_size can be changed independent of journal file creation (through DSE), we consider \ * the max possible record size that can be ever produced. \ */ \ (JINFO)->max_jrec_len = JNL_MAX_SET_KILL_RECLEN(CSD) + MIN_ALIGN_RECLEN; \ } /* Macro that checks that the region seqno in the filehdr is never more than the seqno in the journal pool */ #define ASSERT_JNL_SEQNO_FILEHDR_JNLPOOL(CSA, JNLPOOL) \ { /* The seqno in the file header should be at most 1 greater than that in the journal pool. \ * See step (5) of of commit logic flow in secshr_db_clnup.c for why. Assert that. \ * If CSA->jnlpool is NULL it means there have been no updates to the region so no jnlpool is attached. \ */ \ assert((!CSA->jnlpool || !JNLPOOL || !JNLPOOL->jnlpool_ctl) \ || (CSA->hdr->reg_seqno <= (JNLPOOL->jnlpool_ctl->jnl_seqno + 1))); \ } /* Given the record size, construct an IV to be used for a subsequent encryption or decryption operation. Currently, the maximum IV * length our encryption plug-in supports is 16 bytes, and we only have three bytes of information suitable for an IV at the * encryption time (explained below), so just fit four copies of a three-byte integer into the IV array. */ # define PREPARE_LOGICAL_REC_IV(REC_SIZE, IV_ARRAY) \ { \ uint4 *iv_ptr, iv_val; \ \ /* Encryption happens prior to grabbing crit, when the only initialized fields of a journal \ * record prefix are jrec_type and forwptr, collectively occupying the first four bytes of the \ * jrec_prefix structure. However, if a JRT_TZTWORM-type record remains unused in a particular \ * transaction, it is removed, while the preceding record's type is modified from UUPD to TUPD, \ * via the REMOVE_ZTWORM_JFB_IF_NEEDED macro. Since we cannot be changing IV after encryption, \ * jrec_type does not qualify. Therefore, we are left with three bytes taken by forwptr. \ */ \ assert((ARRAYSIZE(IV_ARRAY) == GTM_MAX_IV_LEN) && (GTM_MAX_IV_LEN == 4 * SIZEOF(uint4))); \ iv_ptr = (uint4 *)IV_ARRAY; \ iv_val = REC_SIZE; \ *iv_ptr++ = iv_val; \ *iv_ptr++ = iv_val; \ *iv_ptr++ = iv_val; \ *iv_ptr = iv_val; \ } /* Decrypt a logical journal record. */ # define MUR_DECRYPT_LOGICAL_RECS(MUMPS_NODE_PTR, USE_NON_NULL_IV, REC_SIZE, KEY_HANDLE, RC) \ { \ int span_length; \ char iv[GTM_MAX_IV_LEN]; \ \ RC = 0; \ assert(FIXED_UPD_RECLEN == FIXED_ZTWORM_RECLEN); \ assert(FIXED_UPD_RECLEN == FIXED_LGTRIG_RECLEN); \ span_length = REC_SIZE - FIXED_UPD_RECLEN - JREC_SUFFIX_SIZE; \ if (USE_NON_NULL_IV) \ PREPARE_LOGICAL_REC_IV(REC_SIZE, iv); \ GTMCRYPT_DECRYPT(NULL, USE_NON_NULL_IV, KEY_HANDLE, (char *)MUMPS_NODE_PTR, span_length, NULL, \ iv, GTM_MAX_IV_LEN, RC); \ } /* The following define an appendix message, used along with JNLBUFFREGUPD and JNLBUFFDBUPD messages in * various places, as well as its length, allowing for six digits for both lower and upper journal buffer * size limits, even though neither is expected to have more than five in the near future. */ #define JNLBUFFUPDAPNDX "The previous value was outside the allowable range of %d to %d" #define JNLBUFFUPDAPNDX_SIZE (SIZEOF(JNLBUFFUPDAPNDX) - 4 + (2 * 6)) /* Yields a portable value for the minimum journal buffer size */ #define JNL_BUFF_PORT_MIN(CSD) (JNL_BUFFER_MIN) /* Defines the increment value for journal buffer size's rounding-up */ #define JNL_BUFF_ROUND_UP_STEP(CSD) (MIN(MAX_IO_BLOCK_SIZE, CSD->blk_size) / DISK_BLOCK_SIZE) /* Rounds up the passed journal buffer value and assigns it to the specified variable */ #define ROUND_UP_JNL_BUFF_SIZE(DEST, VALUE, CSD) \ { \ DEST = ROUND_UP(VALUE, JNL_BUFF_ROUND_UP_STEP(CSD)); \ } /* Rounds up the minimum journal buffer value and assigns it to the specified variable */ #define ROUND_UP_MIN_JNL_BUFF_SIZE(DEST, CSD) \ { \ DEST = ROUND_UP(JNL_BUFF_PORT_MIN(CSD), JNL_BUFF_ROUND_UP_STEP(CSD)); \ } /* Rounds down the maximum journal buffer value and assigns it to the specified variable */ #define ROUND_DOWN_MAX_JNL_BUFF_SIZE(DEST, CSD) \ { \ int jnl_buffer_adj_value, jnl_buffer_decr_step; \ \ jnl_buffer_decr_step = JNL_BUFF_ROUND_UP_STEP(CSD); \ jnl_buffer_adj_value = ROUND_UP(JNL_BUFFER_MAX, jnl_buffer_decr_step); \ while (JNL_BUFFER_MAX < jnl_buffer_adj_value) \ jnl_buffer_adj_value -= jnl_buffer_decr_step; \ DEST = jnl_buffer_adj_value; \ } #define CURRENT_JNL_IO_WRITER(JB) JB->io_in_prog_latch.u.parts.latch_pid #define CURRENT_JNL_FSYNC_WRITER(JB) JB->fsync_in_prog_latch.u.parts.latch_pid /* This macro is invoked by callers just before grabbing crit to check if a db fsync is needed and if so do it. * Note that we can do the db fsync only if we already have the journal file open. If we do not, we will end * up doing this later after grabbing crit. This just minimizes the # of times db fsync happens while inside crit. */ #define DO_JNL_FSYNC_OUT_OF_CRIT_IF_NEEDED(REG, CSA, JPC, JBP) \ MBSTART { \ assert(!CSA->now_crit); \ if ((NULL != JPC) && JBP->need_db_fsync) \ jnl_wait(REG); /* Try to do db fsync outside crit */ \ } MBEND /* The below macro is currently used by the source server but is placed here in case others want to avail it later. * It does the equivalent of a "jnl_flush" but without holding crit for the most part. * The only case it needs crit is if it finds we do not have access to the latest journal file. * In that case it needs to do a "jnl_ensure_open" which requires crit. * For the caller's benefit, this macro also returns whether a jnl_flush happened (in FLUSH_DONE) * and the value of jbp->dskaddr & jbp->freeaddr before the flush (in DSKADDR & FREEADDR variables). */ #define DO_JNL_FLUSH_IF_POSSIBLE(REG, CSA, FLUSH_DONE, DSKADDR, FREEADDR, RSRV_FREEADDR) \ MBSTART { \ jnl_private_control *jpc; \ jnl_buffer_ptr_t jbp; \ boolean_t was_crit; \ uint4 jnl_status; \ \ assert(JNL_ENABLED(CSA)); \ assert(CSA == &FILE_INFO(REG)->s_addrs); \ jpc = CSA->jnl; \ assert(NULL != jpc); \ jbp = jpc->jnl_buff; \ assert(NULL != jbp); \ FLUSH_DONE = FALSE; \ if (jbp->dskaddr != jbp->rsrv_freeaddr) \ { \ was_crit = CSA->now_crit; \ if (!was_crit) \ grab_crit(REG, WS_3); \ DSKADDR = jbp->dskaddr; \ FREEADDR = jbp->freeaddr; \ RSRV_FREEADDR = jbp->rsrv_freeaddr; \ if (JNL_ENABLED(CSA->hdr) && (DSKADDR != RSRV_FREEADDR)) \ { \ jnl_status = jnl_ensure_open(REG, CSA); \ assert(0 == jnl_status); \ if (0 == jnl_status) \ { \ FLUSH_DONE = TRUE; \ jnl_status = jnl_flush(REG); \ assert(SS_NORMAL == jnl_status); \ } \ /* In case of error, silently return for now. */ \ } \ if (!was_crit) \ rel_crit(REG); \ } \ } MBEND /* jnl_ prototypes */ uint4 jnl_file_extend(jnl_private_control *jpc, uint4 total_jnl_rec_size); uint4 jnl_file_lost(jnl_private_control *jpc, uint4 jnl_stat); uint4 jnl_qio_start(jnl_private_control *jpc); uint4 jnl_write_attempt(jnl_private_control *jpc, uint4 threshold); void jnl_prc_vector(jnl_process_vector *pv); void jnl_send_oper(jnl_private_control *jpc, uint4 status); uint4 cre_jnl_file(jnl_create_info *info); uint4 cre_jnl_file_common(jnl_create_info *info, char *rename_fn, int rename_fn_len); void cre_jnl_file_intrpt_rename(sgmnt_addrs *csa); void jfh_from_jnl_info (jnl_create_info *info, jnl_file_header *header); uint4 jnl_ensure_open(gd_region *reg, sgmnt_addrs *csa); void set_jnl_info(gd_region *reg, jnl_create_info *set_jnl_info); void jnl_write_epoch_rec(sgmnt_addrs *csa); void jnl_write_inctn_rec(sgmnt_addrs *csa); void jnl_write_logical(sgmnt_addrs *csa, jnl_format_buffer *jfb, uint4 com_csum); void jnl_write_ztp_logical(sgmnt_addrs *csa, jnl_format_buffer *jfb, uint4 com_csum, seq_num jnl_seqno); void jnl_write_eof_rec(sgmnt_addrs *csa, struct_jrec_eof *eof_record); void jnl_write_trunc_rec(sgmnt_addrs *csa, block_id orig_total_blks, block_id orig_free_blocks, block_id total_blks_after_trunc); void jnl_write_reserve(sgmnt_addrs *csa, jbuf_rsrv_struct_t *nontp_jbuf_rsrv, enum jnl_record_type rectype, uint4 reclen, void *param1); void jnl_write_phase2(sgmnt_addrs *csa, jbuf_rsrv_struct_t *jbuf_rsrv_ptr); void jnl_phase2_cleanup(sgmnt_addrs *csa, jnl_buffer_ptr_t jbp); void jnl_phase2_salvage(sgmnt_addrs *csa, jnl_buffer_ptr_t jbp, jbuf_phase2_in_prog_t *deadCmt); void jnl_pool_write(sgmnt_addrs *csa, enum jnl_record_type rectype, jnl_record *jnl_rec, jnl_format_buffer *jfb); void jnl_write_align_rec(sgmnt_addrs *csa, uint4 align_filler_len, jnl_tm_t time); void jnl_write_multi_align_rec(sgmnt_addrs *csa, uint4 align_filler_len, jnl_tm_t time); jnl_format_buffer *jnl_format(jnl_action_code opcode, gv_key *key, mval *val, uint4 nodeflags); void wcs_defer_wipchk_ast(jnl_private_control *jpc); uint4 set_jnl_file_close(void); uint4 jnl_file_open_common(gd_region *reg, off_jnl_t os_file_size, char *err_str, size_t err_str_len); uint4 jnl_file_open_switch(gd_region *reg, uint4 sts, char *err_str, size_t err_str_len); void jnl_file_close(gd_region *reg, boolean_t clean, boolean_t in_jnl_switch); /* Consider putting followings in a mupip only header file : Layek 2/18/2003 */ boolean_t mupip_set_journal_parse(set_jnl_options *jnl_options, jnl_create_info *jnl_info); uint4 mupip_set_journal_newstate(set_jnl_options *jnl_options, jnl_create_info *jnl_info, mu_set_rlist *rptr); void mupip_set_journal_fname(jnl_create_info *jnl_info); uint4 mupip_set_jnlfile_aux(jnl_file_header *header, char *jnl_fname); void jnl_extr_init(void); int exttime(uint4 time, char *buffer, int extract_len); unsigned char *ext2jnlcvt(char *ext_buff, int4 ext_len, unsigned char **tr, int *tr_bufsiz, seq_num saved_jnl_seqno, seq_num saved_strm_seqno); char *ext2jnl(char *ptr, jnl_record *rec, seq_num saved_jnl_seqno, seq_num saved_strm_seqno); char *jnl2extcvt(jnl_record *rec, int4 jnl_len, char **ext_buff, int *extract_bufsiz); char *jnl2ext(char *jnl_buff, char *ext_buff, char *ext_bufftop); void jnl_set_cur_prior(gd_region *reg, sgmnt_addrs *csa, sgmnt_data *csd); void jnl_set_fd_prior(int jnl_fd, sgmnt_addrs* csa, sgmnt_data* csd, jnl_file_header *jfh); #endif /* JNL_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/jnl2ext.c0000644000032200000250000002114014342376331015573 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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 /* for offsetof macro */ #include "gtm_string.h" #include "gtm_stdio.h" #include "gdsroot.h" /* for filestruct.h */ #include "gdsbt.h" /* for gdsfhead.h */ #include "gtm_facility.h" /* for fileinfo.h */ #include "fileinfo.h" /* for gdsfhead.h */ #include "gdsfhead.h" /* for filestruct.h */ #include "filestruct.h" /* for jnl.h */ #include "jnl.h" #include "copy.h" /* for REF_CHAR macro */ #include "repl_filter.h" #include "format_targ_key.h" #include "mlkdef.h" #include "zshow.h" #include "buddy_list.h" /* needed for muprec.h */ #include "hashtab_mname.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "muprec.h" #include "real_len.h" /* for real_len() prototype */ #define DELIMIT_CURR *curr++ = '\\'; #define ZERO_TIME_DELIM "0,0\\" #define PIDS_DELIM "0\\0\\" #define JNL2EXT_STRM_SEQNO(CURR, STRM_SEQNO) \ { \ seq_num lcl_strm_seqno; \ uint4 lcl_strm_num; \ \ DELIMIT_CURR; \ lcl_strm_seqno = STRM_SEQNO; \ lcl_strm_num = GET_STRM_INDEX(lcl_strm_seqno); \ CURR = (char *)i2ascl((uchar_ptr_t)CURR, lcl_strm_num); \ DELIMIT_CURR; \ lcl_strm_seqno = GET_STRM_SEQ60(lcl_strm_seqno); \ CURR = (char *)i2ascl((uchar_ptr_t)CURR, lcl_strm_seqno); \ } #ifdef DEBUG GBLREF char *jb_stop; #endif GBLREF char muext_code[][2]; static boolean_t first_tstart = FALSE; static int4 num_tstarts = 0; static int4 num_tcommits = 0; /* Generic function to convert a journal record into an extract format record. * Expects a pointer to the journal record and the length of the buffer and * returns the result in ext_buff. If there is a bad format record in between, * it does an assertpro. It sets jb_stop to the offset of the jnl_buff where * the last jnlrecord was processed. On successful conversion, it returns a value * ptr, such that ptr - ext_buff would be the length of the extracted buffer. */ char *jnl2extcvt(jnl_record *rec, int4 jnl_len, char **ext_buff, int *extract_bufsiz) { int rec_len, tmpbufsiz, tmpsize; char *extbuf, *exttop, *tmp, *origbuf; extbuf = *ext_buff; exttop = extbuf + *extract_bufsiz; for ( ; jnl_len > JREC_PREFIX_UPTO_LEN_SIZE && jnl_len >= (rec_len = rec->prefix.forwptr) && (MIN_JNLREC_SIZE <= rec_len); ) { if (MAX_ONE_JREC_EXTRACT_BUFSIZ > (exttop - extbuf)) { /* Remaining space not enough to hold the worst-case journal extract of ONE jnl record. Expand linearly */ tmpsize = *extract_bufsiz; tmpbufsiz = tmpsize + (JNL2EXTCVT_EXPAND_FACTOR * MAX_ONE_JREC_EXTRACT_BUFSIZ); tmp = malloc(tmpbufsiz); origbuf = *ext_buff; tmpsize = extbuf - origbuf; memcpy(tmp, origbuf, tmpsize); free(origbuf); *ext_buff = tmp; *extract_bufsiz = tmpbufsiz; extbuf = tmp + tmpsize; exttop = tmp + tmpbufsiz; } extbuf = jnl2ext((char *)rec, extbuf, exttop); jnl_len -= rec_len; rec = (jnl_record *)((char *)rec + rec_len); } DEBUG_ONLY(jb_stop = (char *)rec;) return extbuf; } char *jnl2ext(char *jnl_buff, char *ext_buff, char *ext_bufftop) { char *curr, *val_ptr, rectype; unsigned char *ptr; jnl_record *rec; gv_key *key; jnl_string *keystr, *ztwormstr; int val_extr_len, val_len, rec_len, tid_len; char key_buff[SIZEOF(gv_key) + MAX_KEY_SZ + 7]; rec = (jnl_record *)jnl_buff; rectype = rec->prefix.jrec_type; rec_len = rec->prefix.forwptr; if ((ROUND_DOWN2(rec_len, JNL_REC_START_BNDRY) != rec_len) || rec_len != REC_LEN_FROM_SUFFIX(jnl_buff, rec_len)) { assertpro(FALSE); return ext_buff; } if (!IS_REPLICATED(rectype)) { assertpro(FALSE); return ext_buff; } curr = ext_buff; /* The following assumes the journal extract format is "GDSJEX07". Whenever that changes (in mur_jnl_ext.c), * the below code as well as ext2jnl.c will need to change. Add an assert to let us know of that event. */ assert(!MEMCMP_LIT(JNL_EXTR_LABEL,"GDSJEX07")); if (IS_TUPD(rectype)) { if (FALSE == first_tstart) { GET_SHORTP(curr, &muext_code[MUEXT_TSTART][0]); curr += 2; DELIMIT_CURR; MEMCPY_LIT(curr, ZERO_TIME_DELIM); curr += STR_LIT_LEN(ZERO_TIME_DELIM); curr = (char *)i2ascl((uchar_ptr_t)curr, rec->jrec_set_kill.prefix.tn); DELIMIT_CURR; MEMCPY_LIT(curr, PIDS_DELIM); curr += STR_LIT_LEN(PIDS_DELIM); curr = (char *)i2ascl((uchar_ptr_t)curr, rec->jrec_set_kill.token_seq.jnl_seqno); JNL2EXT_STRM_SEQNO(curr, rec->jrec_set_kill.strm_seqno); /* Note: updates "curr" */ *curr++ = '\n'; *curr = '\0'; first_tstart = TRUE; } num_tstarts++; } else if (JRT_TCOM == rectype) { num_tcommits++; if (num_tcommits == num_tstarts) { num_tcommits = num_tstarts = 0; first_tstart = FALSE; GET_SHORTP(curr, &muext_code[MUEXT_TCOMMIT][0]); curr += 2; DELIMIT_CURR; MEMCPY_LIT(curr, ZERO_TIME_DELIM); curr += STR_LIT_LEN(ZERO_TIME_DELIM); curr = (char *)i2ascl((uchar_ptr_t)curr, rec->jrec_tcom.prefix.tn); DELIMIT_CURR; MEMCPY_LIT(curr, PIDS_DELIM); curr += STR_LIT_LEN(PIDS_DELIM); curr = (char *)i2ascl((uchar_ptr_t)curr, rec->jrec_tcom.token_seq.jnl_seqno); JNL2EXT_STRM_SEQNO(curr, rec->jrec_tcom.strm_seqno); /* Note: updates "curr" */ DELIMIT_CURR; *curr = '1'; /* Only ONE TSTART..TCOM in the external filter format */ curr++; DELIMIT_CURR; ptr = (unsigned char *)rec->jrec_tcom.jnl_tid; tid_len = real_len(SIZEOF(rec->jrec_tcom.jnl_tid), ptr); memcpy(curr, ptr, tid_len); curr += tid_len; *curr++ = '\n'; *curr = '\0'; return curr; } return ext_buff; } if (IS_SET(rectype)) GET_SHORTP(curr, &muext_code[MUEXT_SET][0]); else if (IS_KILL(rectype)) GET_SHORTP(curr, &muext_code[MUEXT_KILL][0]); else if (IS_ZKILL(rectype)) GET_SHORTP(curr, &muext_code[MUEXT_ZKILL][0]); else if (IS_ZTWORM(rectype)) GET_SHORTP(curr, &muext_code[MUEXT_ZTWORM][0]); else if (IS_LGTRIG(rectype)) GET_SHORTP(curr, &muext_code[MUEXT_LGTRIG][0]); else if (IS_ZTRIG(rectype)) GET_SHORTP(curr, &muext_code[MUEXT_ZTRIG][0]); else /* if (JRT_NULL == rectype) */ { assert(JRT_NULL == rectype); GET_SHORTP(curr, &muext_code[MUEXT_NULL][0]); } curr += 2; DELIMIT_CURR; MEMCPY_LIT(curr, ZERO_TIME_DELIM); curr += STR_LIT_LEN(ZERO_TIME_DELIM); curr = (char *)i2ascl((uchar_ptr_t)curr, rec->jrec_set_kill.prefix.tn); DELIMIT_CURR; MEMCPY_LIT(curr, PIDS_DELIM); curr += STR_LIT_LEN(PIDS_DELIM); curr = (char *)i2ascl((uchar_ptr_t)curr, rec->jrec_set_kill.token_seq.jnl_seqno); JNL2EXT_STRM_SEQNO(curr, rec->jrec_set_kill.strm_seqno); /* Note: updates "curr" */ if (rectype == JRT_NULL) { *curr++ = '\n'; *curr='\0'; return curr; } DELIMIT_CURR; /* print "update_num" */ assert(IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(rectype)); assert(&rec->jrec_set_kill.update_num == &rec->jrec_ztworm.update_num); assert(&rec->jrec_set_kill.update_num == &rec->jrec_lgtrig.update_num); curr = (char *)i2ascl((uchar_ptr_t)curr, rec->jrec_set_kill.update_num); DELIMIT_CURR; if (IS_ZTWORM(rectype) || IS_LGTRIG(rectype)) { assert(&rec->jrec_ztworm.ztworm_str == &rec->jrec_lgtrig.lgtrig_str); ztwormstr = &rec->jrec_ztworm.ztworm_str; val_len = ztwormstr->length; val_ptr = &ztwormstr->text[0]; val_extr_len = ext_bufftop - curr; format2zwr((sm_uc_ptr_t)val_ptr, val_len, (uchar_ptr_t)curr, &val_extr_len); curr += val_extr_len; *curr++ = '\n'; *curr='\0'; return curr; } /* print "nodeflags" */ keystr = (jnl_string *)&rec->jrec_set_kill.mumps_node; curr = (char *)i2ascl((uchar_ptr_t)curr, keystr->nodeflags); DELIMIT_CURR; /* print "node" */ key = (gv_key *)ROUND_UP((unsigned long)key_buff, 8); key->top = MAX_KEY_SZ; key->end = keystr->length; if (key->end > key->top) { assertpro(FALSE); return ext_buff; } memcpy(key->base, &keystr->text[0], keystr->length); key->base[key->end] = 0; curr = (char *)format_targ_key((uchar_ptr_t)curr, MAX_ZWR_KEY_SZ, key, TRUE); if (IS_SET(rectype)) { *curr++ = '='; val_ptr = &keystr->text[keystr->length]; GET_MSTR_LEN(val_len, val_ptr); val_ptr += SIZEOF(mstr_len_t); val_extr_len = ext_bufftop - curr; format2zwr((sm_uc_ptr_t)val_ptr, val_len, (uchar_ptr_t)curr, &val_extr_len); curr += val_extr_len; } *curr++ = '\n'; *curr='\0'; return curr; } fis-gtm-V7.0-005/sr_port/jnl_ensure_open.c0000644000032200000250000000572214342376331017402 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2017 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 "gdsdbver.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "iosp.h" #include "repl_sp.h" #include "gtmio.h" #include "gtmimagename.h" #include "wbox_test_init.h" #include "gtcm_jnl_switched.h" GBLREF boolean_t is_src_server; error_def(ERR_JNLFILOPN); /* make sure that the journal file is available if appropriate */ uint4 jnl_ensure_open(gd_region *reg, sgmnt_addrs *csa) { uint4 jnl_status; jnl_private_control *jpc; sgmnt_data_ptr_t csd; boolean_t first_open_of_jnl, need_to_open_jnl; int close_res; csd = csa->hdr; assert(csa->now_crit); jpc = csa->jnl; assert(&FILE_INFO(jpc->region)->s_addrs == csa); assert(&FILE_INFO(reg)->s_addrs == csa); assert(NULL != jpc); assert(JNL_ENABLED(csa->hdr)); /* The goal is to change the code below to do only one JNL_FILE_SWITCHED(jpc) check instead of the additional * (NOJNL == jpc->channel) check done below. The assert below ensures that the NOJNL check can indeed * be subsumed by the JNL_FILE_SWITCHED check (with the exception of the source-server which has a special case that * needs to be fixed in C9D02-002241). Over time, this has to be changed to one check. */ assert((NOJNL != jpc->channel) || JNL_FILE_SWITCHED(jpc) || is_src_server); need_to_open_jnl = FALSE; jnl_status = 0; if (NOJNL == jpc->channel) need_to_open_jnl = TRUE; else if (JNL_FILE_SWITCHED(jpc)) { /* The journal file has been changed "on the fly"; close the old one and open the new one */ JNL_FD_CLOSE(jpc->channel, close_res); /* sets jpc->channel to NOJNL */ need_to_open_jnl = TRUE; } if (need_to_open_jnl) { /* Whenever journal file get switch, reset the pini_addr and new_freeaddr. */ jpc->pini_addr = 0; jpc->new_freeaddr = 0; if (IS_GTCM_GNP_SERVER_IMAGE) gtcm_jnl_switched(reg); /* Reset pini_addr of all clients that had any older journal file open */ first_open_of_jnl = (0 == csa->nl->jnl_file.u.inode); jnl_status = jnl_file_open(reg, first_open_of_jnl); } # ifdef DEBUG else GTM_WHITE_BOX_TEST(WBTEST_JNL_FILE_OPEN_FAIL, jnl_status, ERR_JNLFILOPN); # endif assert((0 != jnl_status) || !JNL_FILE_SWITCHED(jpc) || (is_src_server && !JNL_ENABLED(csa) && REPL_WAS_ENABLED(csa))); return jnl_status; } fis-gtm-V7.0-005/sr_port/jnl_file_close.c0000644000032200000250000002027214342376331017161 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2021 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 /* for offsetof, needed for JNL_FILE_TAIL_PRESERVE */ #include "gtm_time.h" #include "gtm_string.h" #include "gtm_unistd.h" #include "aswp.h" #include "lockconst.h" #include "interlock.h" #include "sleep_cnt.h" #include "performcaslatchcheck.h" #include "wcs_sleep.h" #include "gt_timer.h" #include "wbox_test_init.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "jnl_write.h" #include "gtmio.h" #include "repl_sp.h" /* F_CLOSE */ #include "iosp.h" /* for SS_NORMAL */ #include "ccp.h" #include "send_msg.h" #include "eintr_wrappers.h" #include "anticipatory_freeze.h" #include "error.h" #include "wcs_clean_dbsync.h" GBLREF jnl_gbls_t jgbl; GBLREF boolean_t in_jnl_file_autoswitch; error_def(ERR_JNLCLOSE); error_def(ERR_JNLFLUSH); error_def(ERR_JNLFSYNCERR); error_def(ERR_JNLWRERR); error_def(ERR_PREMATEOF); error_def(ERR_TEXT); void jnl_file_close(gd_region *reg, boolean_t clean, boolean_t in_jnl_switch) { jnl_file_header *header; unsigned char hdr_base[REAL_JNL_HDR_LEN + MAX_IO_BLOCK_SIZE]; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; jnl_private_control *jpc; jnl_buffer_ptr_t jb; struct_jrec_eof eof_record; off_jnl_t eof_addr; uint4 status, read_write_size; int rc, save_errno, idx; uint4 jnl_fs_block_size; boolean_t was_in_jnl_file_autoswitch, write_eof, in_tail; csa = &FILE_INFO(reg)->s_addrs; csd = csa->hdr; assert(!clean || csa->now_crit || (csd->clustered && (CCST_CLOSED == csa->nl->ccp_state))); DEBUG_ONLY( if (clean) ASSERT_JNLFILEID_NOT_NULL(csa); ) jpc = csa->jnl; if (csa->dbsync_timer) CANCEL_DBSYNC_TIMER(csa); if ((NULL == jpc) || (NOJNL == jpc->channel)) return; jb = jpc->jnl_buff; jnl_fs_block_size = jb->fs_block_size; header = (jnl_file_header *)(ROUND_UP2((uintszofptr_t)hdr_base, jnl_fs_block_size)); if (clean) { write_eof = !jb->last_eof_written; if (!jgbl.mur_extract && write_eof) { was_in_jnl_file_autoswitch = in_jnl_file_autoswitch; /* We don't want to switch while closing, so set in_jnl_file_autoswitch, * which allows writing to the tail. */ in_jnl_file_autoswitch = TRUE; if (!was_in_jnl_file_autoswitch) { assert(!jgbl.save_dont_reset_gbl_jrec_time); /* Make sure the condition handler can restore the correct value */ jgbl.save_dont_reset_gbl_jrec_time = jgbl.dont_reset_gbl_jrec_time; ESTABLISH(jnl_file_autoswitch_ch); } /* Otherwise the caller which set was_in_jnl_file_autoswitch also should have * established the condition handler already. */ /* It is possible we still have not written a PINI record in this journal file * (e.g. mupip extend saw the need to do jnl_file_extend inside jnl_write while * trying to write a PINI record). Write a PINI record in that case before closing * the journal file that way the EOF record will have a non-zero pini_addr. * However, if we are already in the tail due to a prior disruption, skip the * pini/pfin and just write the eof. */ assert(jb->rsrv_freeaddr >= jb->freeaddr); if (jb->rsrv_freeaddr != jb->end_of_data_at_open) { DEBUG_ONLY(jpc->status = SS_NORMAL); in_tail = ((off_t)jb->rsrv_freeaddr > ((off_t)DISK_BLOCK_SIZE * jb->filesize) - JNL_FILE_TAIL_PRESERVE); if ((0 == jpc->pini_addr) && !in_tail) { jnl_write_pini(csa); jnl_write_pfin(csa); } jnl_write_eof_rec(csa, &eof_record); } else { /* No journal records got written since jnl file was opened in shm. No need to write EOF record */ write_eof = FALSE; } if (!was_in_jnl_file_autoswitch) { jgbl.dont_reset_gbl_jrec_time = jgbl.save_dont_reset_gbl_jrec_time; jgbl.save_dont_reset_gbl_jrec_time = FALSE; REVERT; } else /* We were called in autoswitch, so prevent further records from being * written to this file by marking it as last_eof_written. */ jb->last_eof_written = TRUE; in_jnl_file_autoswitch = was_in_jnl_file_autoswitch; } if (SS_NORMAL != (jpc->status = jnl_flush(reg))) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(9) ERR_JNLFLUSH, 2, JNL_LEN_STR(csd), ERR_TEXT, 2, RTS_ERROR_TEXT("Error with journal flush during jnl_file_close"), jpc->status); assert(FALSE); rts_error_csa(CSA_ARG(csa) VARLSTCNT(9) ERR_JNLFLUSH, 2, JNL_LEN_STR(csd), ERR_TEXT, 2, RTS_ERROR_TEXT("Error with journal flush during jnl_file_close"), jpc->status); } # ifdef DEBUG if (!gtm_white_box_test_case_enabled || (WBTEST_JNL_FILE_LOST_DSKADDR != gtm_white_box_test_case_number)) { assert(jb->rsrv_freeaddr == jb->freeaddr); assert(jb->dskaddr == jb->freeaddr); } # endif jnl_fsync(reg, jb->dskaddr); assert((jb->freeaddr == jb->fsync_dskaddr) || (gtm_white_box_test_case_enabled && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number))); read_write_size = ROUND_UP2(REAL_JNL_HDR_LEN, jnl_fs_block_size); assert((unsigned char *)header + read_write_size <= ARRAYTOP(hdr_base)); DO_FILE_READ(jpc->channel, 0, header, read_write_size, jpc->status, jpc->status2); if (SYSCALL_SUCCESS(jpc->status)) { if (!jgbl.mur_extract && write_eof) { /* If write_eof is FALSE, we didn't write an eof, so nothing to update. */ eof_addr = jb->freeaddr - EOF_RECLEN; assert(header->end_of_data <= eof_addr); header->end_of_data = eof_addr; header->eov_timestamp = eof_record.prefix.time; assert(header->eov_timestamp >= header->bov_timestamp); header->eov_tn = eof_record.prefix.tn; assert(header->eov_tn >= header->bov_tn); header->end_seqno = eof_record.jnl_seqno; } for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) header->strm_end_seqno[idx] = csd->strm_reg_seqno[idx]; if (jgbl.forw_phase_recovery) { /* If MUPIP JOURNAL -ROLLBACK, might need some adjustment. See macro definition for comments */ MUR_ADJUST_STRM_REG_SEQNO_IF_NEEDED(csd, header->strm_end_seqno); } header->last_eof_written = jb->last_eof_written; header->is_not_latest_jnl = in_jnl_switch; header->crash = FALSE; JNL_DO_FILE_WRITE(csa, csd->jnl_file_name, jpc->channel, 0, header, read_write_size, jpc->status, jpc->status2); if (SYSCALL_ERROR(jpc->status)) { assert(FALSE); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(5) ERR_JNLWRERR, 2, JNL_LEN_STR(csd), jpc->status); } GTM_JNL_FSYNC(csa, jpc->channel, rc); if (-1 == rc) { save_errno = errno; send_msg_csa(CSA_ARG(csa) VARLSTCNT(9) ERR_JNLFSYNCERR, 2, JNL_LEN_STR(csd), ERR_TEXT, 2, RTS_ERROR_TEXT("Error with fsync during jnl_file_close"), save_errno); assert(FALSE); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(9) ERR_JNLFSYNCERR, 2, JNL_LEN_STR(csd), ERR_TEXT, 2, RTS_ERROR_TEXT("Error with fsync during jnl_file_close"), save_errno); } } /* jnl_file_id should be nullified only after the jnl file header has been written to disk. * Nullifying the jnl_file_id signals that the jnl file has been switched. The replication source server * assumes that the jnl file has been completely written to disk (including the header) before the switch is * signalled. */ NULLIFY_JNL_FILE_ID(csa); jb->cycle++; /* increment shared cycle so all future callers of jnl_ensure_open recognize journal switch */ } JNL_FD_CLOSE(jpc->channel, rc); /* sets jpc->channel to NOJNL */ GTM_WHITE_BOX_TEST(WBTEST_ANTIFREEZE_JNLCLOSE, rc, EIO); jpc->cycle--; /* decrement cycle so "jnl_ensure_open" knows to reopen the journal */ jpc->pini_addr = 0; if (clean && (SS_NORMAL != jpc->status || SS_NORMAL != rc)) { status = jpc->status; /* jnl_send_oper resets jpc->status, so save it */ jnl_send_oper(jpc, ERR_JNLCLOSE); RTS_ERROR_CSA_ABT(csa, VARLSTCNT(5) ERR_JNLCLOSE, 2, JNL_LEN_STR(csd), status); } } fis-gtm-V7.0-005/sr_port/jnl_file_lost.c0000644000032200000250000000775214342376331017045 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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_inet.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "ccp.h" #include "jnl.h" #include "send_msg.h" #include "repl_msg.h" #include "gtmsource.h" #include "anticipatory_freeze.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF gd_region *gv_cur_region; GBLREF volatile boolean_t in_wcs_recover; GBLREF int process_exiting; error_def(ERR_JNLCLOSED); error_def(ERR_REPLJNLCLOSED); uint4 jnl_file_lost(jnl_private_control *jpc, uint4 jnl_stat) { /* Notify operator and terminate journaling */ unsigned int status; sgmnt_addrs *csa; jnlpool_addrs_ptr_t save_jnlpool; jnlpool_addrs_ptr_t local_jnlpool; /* needed by INST_FREEZE_ON_MSG_ENABLED */ seq_num reg_seqno, jnlseqno; boolean_t was_lockid = FALSE, instfreeze_environ; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; save_jnlpool = jnlpool; switch(jpc->region->dyn.addr->acc_meth) { case dba_mm: case dba_bg: csa = &FILE_INFO(jpc->region)->s_addrs; if (csa->jnlpool && (jnlpool != csa->jnlpool)) jnlpool = csa->jnlpool; break; default: assertpro(FALSE && jpc->region->dyn.addr->acc_meth); /* no break */ } assert(csa->now_crit); /* We issue an rts_error (instead of shutting off journaling) in the following cases : {BYPASSOK} * 1) $gtm_error_on_jnl_file_lost is set to issue runtime error (if not already issued) in case of journaling issues. * 2) The process has the given message set in $gtm_custom_errors (indicative of instance freeze on error setup) * in which case the goal is to never shut-off journaling */ assert((NULL == jnlpool) || (NULL != jnlpool->jnlpool_ctl)); instfreeze_environ = INST_FREEZE_ON_MSG_ENABLED(csa, jnl_stat, local_jnlpool); if ((JNL_FILE_LOST_ERRORS == TREF(error_on_jnl_file_lost)) || instfreeze_environ) { if (!process_exiting || instfreeze_environ || !csa->jnl->error_reported) { if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; csa->jnl->error_reported = TRUE; in_wcs_recover = FALSE; /* in case we're called in wcs_recover() */ if (SS_NORMAL != jpc->status) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(7) jnl_stat, 4, JNL_LEN_STR(csa->hdr), DB_LEN_STR(gv_cur_region), jpc->status); rts_error_csa(CSA_ARG(csa) VARLSTCNT(7) jnl_stat, 4, JNL_LEN_STR(csa->hdr), DB_LEN_STR(gv_cur_region), jpc->status); } else { send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) jnl_stat, 4, JNL_LEN_STR(csa->hdr), DB_LEN_STR(gv_cur_region)); rts_error_csa(CSA_ARG(csa) VARLSTCNT(6) jnl_stat, 4, JNL_LEN_STR(csa->hdr), DB_LEN_STR(gv_cur_region)); } } if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; return jnl_stat; } if (0 != jnl_stat) jnl_send_oper(jpc, jnl_stat); csa->hdr->jnl_state = jnl_closed; jpc->jnl_buff->cycle++; /* increment shared cycle so all future callers of jnl_ensure_open recognize journal switch */ assert(jpc->cycle < jpc->jnl_buff->cycle); if (REPL_ENABLED(csa->hdr)) { csa->hdr->repl_state = repl_was_open; reg_seqno = csa->hdr->reg_seqno; jnlseqno = (jnlpool && jnlpool->jnlpool_ctl) ? jnlpool->jnlpool_ctl->jnl_seqno : MAX_SEQNO; send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_REPLJNLCLOSED, 6, DB_LEN_STR(jpc->region), ®_seqno, ®_seqno, &jnlseqno, &jnlseqno); } else send_msg_csa(CSA_ARG(csa) VARLSTCNT(5) ERR_JNLCLOSED, 3, DB_LEN_STR(jpc->region), &csa->ti->curr_tn); jnl_file_close(jpc->region, FALSE, TRUE); if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; return EXIT_NRM; } fis-gtm-V7.0-005/sr_port/jnl_file_open_common.c0000644000032200000250000003231414342376331020365 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2019 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_stat.h" #include "gtm_string.h" #include "gtm_time.h" #include "gtm_inet.h" #include #include "gtm_fcntl.h" #include "gtm_unistd.h" #include "interlock.h" #include "lockconst.h" #include "aswp.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "gtmio.h" #include "sgtm_putmsg.h" #include "eintr_wrappers.h" #include "repl_msg.h" #include "gtmsource.h" #include "is_file_identical.h" #include "gtmmsg.h" #include "send_msg.h" #include "repl_sp.h" #include "iosp.h" /* for SS_NORMAL */ #include "get_fs_block_size.h" #include "anticipatory_freeze.h" GBLREF int pool_init; GBLREF jnl_process_vector *prc_vec; GBLREF jnl_gbls_t jgbl; GBLREF uint4 mu_reorg_encrypt_in_prog; error_def(ERR_CRYPTJNLMISMATCH); error_def(ERR_FILEIDMATCH); error_def(ERR_JNLBADRECFMT); error_def(ERR_JNLOPNERR); error_def(ERR_JNLPREVRECOV); error_def(ERR_JNLRDERR); error_def(ERR_JNLRECTYPE); error_def(ERR_JNLSWITCHRETRY); error_def(ERR_JNLTRANSGTR); error_def(ERR_JNLTRANSLSS); error_def(ERR_JNLVSIZE); error_def(ERR_JNLWRERR); error_def(ERR_PREMATEOF); #define RETURN_AND_SET_JPC(ERR, ERR2, BUF) \ MBSTART { \ jpc->status = ERR2; \ jpc->err_str = BUF; \ return ERR; \ } MBEND /* note: returns 0 on success */ uint4 jnl_file_open_common(gd_region *reg, off_jnl_t os_file_size, char *buff, size_t buff_len) { sgmnt_addrs *csa; sgmnt_data_ptr_t csd; jnl_private_control *jpc; jnl_buffer_ptr_t jb; jnl_file_header *header; unsigned char hdr_buff[REAL_JNL_HDR_LEN + MAX_IO_BLOCK_SIZE]; struct_jrec_eof eof_record; /* pointer is in an attempt to use make code portable */ unsigned char *eof_rec_buffer; unsigned char eof_rec[(DISK_BLOCK_SIZE * 2) + MAX_IO_BLOCK_SIZE]; off_jnl_t adjust; uint4 end_of_data, jnl_fs_block_size, read_write_size, read_size; int4 status; gtm_uint64_t header_virtual_size; csa = &FILE_INFO(reg)->s_addrs; csd = csa->hdr; jpc = csa->jnl; jb = jpc->jnl_buff; jpc->status = jpc->status2 = SS_NORMAL; assert(NULL != buff); jnl_fs_block_size = get_fs_block_size(jpc->channel); /* check that the filesystem block size is a power of 2 as we do a lot of calculations below assuming this is the case */ assert(!(jnl_fs_block_size & (jnl_fs_block_size - 1))); /* Ensure filesystem-block-size alignment except in case the filesystem-block-size is higher than os-page-size (not * currently seen on the popular filesystems but seen in NFS filesystems). In that case only ensure os-page-size-alignment * since processes that attach to database shared memory attach at os-page-size-aligned virtual addresses. So treat * filesystem-block-size as the os-page-size in that case. */ if (OS_PAGE_SIZE < jnl_fs_block_size) jnl_fs_block_size = OS_PAGE_SIZE; header = (jnl_file_header *)(ROUND_UP2((uintszofptr_t)hdr_buff, jnl_fs_block_size)); eof_rec_buffer = (unsigned char *)(ROUND_UP2((uintszofptr_t)eof_rec, jnl_fs_block_size)); /* Read the journal file header */ read_write_size = ROUND_UP2(REAL_JNL_HDR_LEN, jnl_fs_block_size); assert((unsigned char *)header + read_write_size <= ARRAYTOP(hdr_buff)); DO_FILE_READ(jpc->channel, 0, header, read_write_size, jpc->status, jpc->status2); if (SS_NORMAL != jpc->status) { /* A PREMATEOF error is possible in Unix if a V54001 version is trying to open a pre-V54001 journal file * This is because starting V54001, the journal file size is always maintained as a multiple of the underlying * filesystem block size. And so in case of a previous version created journal file, it is possible the * entire unaligned journal file size is lesser than the aligned journal file header size. */ assert(ERR_PREMATEOF == jpc->status); return ERR_JNLRDERR; /* Has one !AD parameter, the journal file, which jnl_send_oper() provides */ } /* Check if the header format matches our format. Cannot access any fields inside header unless this matches */ CHECK_JNL_FILE_IS_USABLE(header, jpc->status, FALSE, 0, NULL); /* FALSE => NO gtm_putmsg even if errors */ if (SS_NORMAL != jpc->status) { sgtm_putmsg(buff, buff_len, VARLSTCNT(7) ERR_JNLOPNERR, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg), jpc->status); RETURN_AND_SET_JPC(ERR_JNLOPNERR, jpc->status, buff); } end_of_data = header->end_of_data; adjust = end_of_data & (jnl_fs_block_size - 1); /* Read the journal JRT_EOF at end_of_data offset. * Make sure the buffer being read to is big enough and that as part of the read, * we never touch touch the journal file header territory. */ read_size = ROUND_UP2((EOF_RECLEN + adjust), jnl_fs_block_size); assert(eof_rec_buffer + read_size <= ARRAYTOP(eof_rec)); assert(end_of_data - adjust >= JNL_HDR_LEN); DO_FILE_READ(jpc->channel, end_of_data - adjust, eof_rec_buffer, read_size, jpc->status, jpc->status2); if (SS_NORMAL != jpc->status) return ERR_JNLRDERR; /* Has one !AD parameter, the journal file, which jnl_send_oper() provides */ if (header->prev_recov_end_of_data) { /* not possible for run time. In case it happens user must fix it */ sgtm_putmsg(buff, buff_len, VARLSTCNT(7) ERR_JNLOPNERR, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg), ERR_JNLPREVRECOV); RETURN_AND_SET_JPC(ERR_JNLOPNERR, ERR_JNLPREVRECOV, buff); } if (!is_gdid_file_identical(&FILE_ID(reg), (char *)header->data_file_name, header->data_file_name_length)) { rts_error_csa(CSA_ARG(csa) VARLSTCNT(7) ERR_JNLOPNERR, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg), ERR_FILEIDMATCH); assert(FALSE); /* we don't expect the rts_error in the line above to return */ return ERR_JNLOPNERR; } memcpy(&eof_record, (unsigned char *)eof_rec_buffer + adjust, EOF_RECLEN); if (JRT_EOF != eof_record.prefix.jrec_type) { sgtm_putmsg(buff, buff_len, VARLSTCNT(7) ERR_JNLOPNERR, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg), ERR_JNLRECTYPE); RETURN_AND_SET_JPC(ERR_JNLOPNERR, ERR_JNLRECTYPE, buff); } if (eof_record.prefix.tn != csd->trans_hist.curr_tn) { if (eof_record.prefix.tn < csd->trans_hist.curr_tn) status = ERR_JNLTRANSLSS; else status = ERR_JNLTRANSGTR; sgtm_putmsg(buff, buff_len, VARLSTCNT(10) ERR_JNLOPNERR, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg), status, 2, &eof_record.prefix.tn, &csd->trans_hist.curr_tn); RETURN_AND_SET_JPC(ERR_JNLOPNERR, status, buff); } if (eof_record.suffix.suffix_code != JNL_REC_SUFFIX_CODE || eof_record.suffix.backptr != eof_record.prefix.forwptr) { sgtm_putmsg(buff, buff_len, VARLSTCNT(11) ERR_JNLOPNERR, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg), ERR_JNLBADRECFMT, 3, JNL_LEN_STR(csd), adjust); RETURN_AND_SET_JPC(ERR_JNLOPNERR, ERR_JNLBADRECFMT, buff); } if (!mu_reorg_encrypt_in_prog && !SAME_ENCRYPTION_SETTINGS(header, csd)) { /* We expect encryption settings in the journal to be in sync with those in the file header. The only exception is * MUPIP REORG -ENCRYPT, which switches the journal file upon changing encryption-specific fields in the file * header, thus temporarily violating this expectation. */ sgtm_putmsg(buff, buff_len, VARLSTCNT(12) ERR_JNLOPNERR, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg), ERR_CRYPTJNLMISMATCH, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg)); RETURN_AND_SET_JPC(ERR_JNLOPNERR, ERR_CRYPTJNLMISMATCH, buff); } assert(header->eov_tn == eof_record.prefix.tn); header->eov_tn = eof_record.prefix.tn; assert(header->eov_timestamp == eof_record.prefix.time); header->eov_timestamp = eof_record.prefix.time; assert(header->eov_timestamp >= header->bov_timestamp); assert(((off_jnl_t)os_file_size) % JNL_REC_START_BNDRY == 0); assert(((off_jnl_t)os_file_size) % DISK_BLOCK_SIZE == 0); assert(((off_jnl_t)os_file_size) % jnl_fs_block_size == 0); header_virtual_size = header->virtual_size; /* saving in 8-byte int to avoid overflow below */ if ((ROUND_UP2((header_virtual_size * DISK_BLOCK_SIZE), jnl_fs_block_size) < os_file_size) || (header->jnl_deq && 0 != ((header_virtual_size - header->jnl_alq) % header->jnl_deq))) { sgtm_putmsg(buff, buff_len, VARLSTCNT(14) ERR_JNLOPNERR, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg), ERR_JNLVSIZE, 7, JNL_LEN_STR(csd), header->virtual_size, header->jnl_alq, header->jnl_deq, os_file_size, jnl_fs_block_size); RETURN_AND_SET_JPC(ERR_JNLOPNERR, ERR_JNLVSIZE, buff); } /* For performance reasons (to be able to do aligned writes to the journal file), we need to ensure the journal buffer * address is filesystem-block-size aligned in Unix. Although this is needed only in case of sync_io/direct-io, we ensure * this alignment unconditionally in Unix. jb->buff_off is the number of bytes to go past before getting an aligned buffer. */ jb->buff_off = (uintszofptr_t)ROUND_UP2((uintszofptr_t)&jb->buff[0], jnl_fs_block_size) - (uintszofptr_t)&jb->buff[0]; jb->size = ROUND_DOWN2(csd->jnl_buffer_size * DISK_BLOCK_SIZE - jb->buff_off, jnl_fs_block_size); /* Assert that journal buffer does NOT spill past the allocated journal buffer size in shared memory */ assert((sm_uc_ptr_t)&jb->buff[jb->buff_off + jb->size] < ((sm_uc_ptr_t)csa->nl + NODE_LOCAL_SPACE(csd) + JNL_SHARE_SIZE(csd))); assert((sm_uc_ptr_t)jb == ((sm_uc_ptr_t)csa->nl + NODE_LOCAL_SPACE(csd) + JNL_NAME_EXP_SIZE)); jb->last_eof_written = header->last_eof_written; jb->freeaddr = jb->dskaddr = jb->fsync_dskaddr = end_of_data; SET_JBP_RSRV_FREEADDR(jb, end_of_data); /* sets both jb->rsrv_freeaddr & jb->rsrv_free */ jb->post_epoch_freeaddr = jb->end_of_data_at_open = jb->freeaddr; jb->fs_block_size = jnl_fs_block_size; /* The following is to make sure that the data in jnl_buffer is aligned with the data in the * disk file on an jnl_fs_block_size boundary. Since we assert that jb->size is a multiple of jnl_fs_block_size, * alignment with respect to jb->size implies alignment with jnl_fs_block_size. */ assert(0 == (jb->size % jnl_fs_block_size)); jb->free = jb->dsk = end_of_data % jb->size; SET_LATCH_GLOBAL(&jb->fsync_in_prog_latch, LOCK_AVAILABLE); SET_LATCH_GLOBAL(&jb->io_in_prog_latch, LOCK_AVAILABLE); assert(0 == (jnl_fs_block_size % DISK_BLOCK_SIZE)); if (adjust) { /* if jb->free does not start at a filesystem-block-size aligned boundary (which is the alignment granularity used * by "jnl_output_sp" for flushing to disk), copy as much pre-existing data from the journal file as necessary into * the journal buffer to fill the gap so we do not lose this information in the next write to disk. */ memcpy(&jb->buff[ROUND_DOWN2(jb->free, jnl_fs_block_size) + jb->buff_off], eof_rec_buffer, adjust); } jb->filesize = header->virtual_size; jb->min_write_size = JNL_MIN_WRITE; jb->max_write_size = JNL_MAX_WRITE; jb->before_images = header->before_images; jb->epoch_tn = eof_record.prefix.tn; csd->jnl_checksum = header->checksum; assert(4 == SIZEOF(header->alignsize)); assert(((gtm_uint64_t)JNL_MAX_ALIGNSIZE * 512) < (gtm_uint64_t)MAXUINT4); /* ensure 4-byte "alignsize" will not overflow */ jb->alignsize = header->alignsize; /* Use "gtm_uint64_t" typecast below to avoid 4G overflow issues with the ROUND_UP2 */ jb->next_align_addr = (uint4)(ROUND_UP2(((gtm_uint64_t)jb->freeaddr + MIN_ALIGN_RECLEN), header->alignsize) - MIN_ALIGN_RECLEN); SET_LATCH_GLOBAL(&jb->phase2_commit_latch, LOCK_AVAILABLE); jb->phase2_commit_index1 = jb->phase2_commit_index2 = 0; /* The below 2 lines are relied upon by "mutex_salvage" */ jb->phase2_commit_array[JNL_PHASE2_COMMIT_ARRAY_SIZE - 1].start_freeaddr = jb->end_of_data_at_open; jb->phase2_commit_array[JNL_PHASE2_COMMIT_ARRAY_SIZE - 1].tot_jrec_len = 0; assert(header->autoswitchlimit == csd->autoswitchlimit); assert(header->jnl_alq == csd->jnl_alq); assert(header->jnl_deq == csd->jnl_deq); assert(csd->autoswitchlimit >= csd->jnl_alq); assert(ALIGNED_ROUND_UP(csd->autoswitchlimit, csd->jnl_alq, csd->jnl_deq) == csd->autoswitchlimit); assert(csd->autoswitchlimit); JNL_WHOLE_TIME(prc_vec->jpv_time); jb->epoch_interval = header->epoch_interval; jb->next_epoch_time = (uint4)(MID_TIME(prc_vec->jpv_time) + jb->epoch_interval); jb->max_jrec_len = header->max_jrec_len; memcpy(&header->who_opened, prc_vec, SIZEOF(jnl_process_vector)); if (header->is_not_latest_jnl) { /* Magic message to indicate that we should try a switch without cutting links. */ sgtm_putmsg(buff, buff_len, VARLSTCNT(6) ERR_JNLSWITCHRETRY, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg)); RETURN_AND_SET_JPC(ERR_JNLSWITCHRETRY, 0, buff); } header->crash = TRUE; /* in case this processes is crashed, this will remain TRUE */ JNL_DO_FILE_WRITE(csa, csd->jnl_file_name, jpc->channel, 0, header, read_write_size, jpc->status, jpc->status2); if (SS_NORMAL != jpc->status) { assert(WBTEST_ENABLED(WBTEST_RECOVER_ENOSPC)); return ERR_JNLWRERR; } SET_JNLBUFF_PREV_JREC_TIME(jb, eof_record.prefix.time, DO_GBL_JREC_TIME_CHECK_FALSE); /* Sets jb->prev_jrec_time to time of to-be-overwritten EOF rec */ jb->end_of_data = 0; jb->eov_tn = 0; jb->eov_timestamp = 0; jb->end_seqno = 0; return 0; } fis-gtm-V7.0-005/sr_port/jnl_file_open_switch.c0000755000032200000250000000625714342376331020410 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2019 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_string.h" #include "gtm_time.h" #include "gtm_unistd.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "send_msg.h" #include "gtmio.h" #include "sgtm_putmsg.h" #include "repl_sp.h" #include "iosp.h" /* for SS_NORMAL */ #include "wbox_test_init.h" GBLREF jnl_gbls_t jgbl; error_def(ERR_JNLFILOPN); error_def(ERR_JNLSWITCHFAIL); error_def(ERR_JNLSWITCHRETRY); error_def(ERR_PREVJNLLINKCUT); uint4 jnl_file_open_switch(gd_region *reg, uint4 sts, char *err_str, size_t err_str_len) { sgmnt_addrs *csa; sgmnt_data_ptr_t csd; jnl_private_control *jpc; jnl_create_info create; char prev_jnl_fn[JNL_NAME_SIZE]; int status; # if defined(GTM_TRIGGER) && defined(DEBUG) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # endif csa = &FILE_INFO(reg)->s_addrs; csd = csa->hdr; jpc = csa->jnl; assert(sts GTMTRIG_ONLY(|| TREF(in_trigger_upgrade))); assert(!sts || ((ERR_JNLFILOPN != sts) && (NOJNL != jpc->channel)) || ((ERR_JNLFILOPN == sts) && (NOJNL == jpc->channel)) || (ERR_JNLSWITCHRETRY == sts)); if (NOJNL != jpc->channel) JNL_FD_CLOSE(jpc->channel, status); /* sets jpc->channel to NOJNL */ if (sts) jnl_send_oper(jpc, sts); /* attempt to create a new journal file */ memset(&create, 0, SIZEOF(create)); create.status = create.status2 = SS_NORMAL; create.prev_jnl = &prev_jnl_fn[0]; set_jnl_info(reg, &create); /* ERR_JNLSWITCHRETRY indicates that jnl_file_open_common found that the current journal had been marked for a * switch, but the switch failed at a later point, so don't cut links in that case. */ create.no_prev_link = (ERR_JNLSWITCHRETRY != sts); create.no_rename = FALSE; assert(!jgbl.forw_phase_recovery || WBTEST_ENABLED(WBTEST_RECOVER_ENOSPC) || WBTEST_ENABLED(WBTEST_JNL_CREATE_FAIL)); if (!jgbl.dont_reset_gbl_jrec_time) SET_GBL_JREC_TIME; /* needed for cre_jnl_file() */ /* else mur_output_record() would have already set jgbl.gbl_jrec_time */ assert(jgbl.gbl_jrec_time); if (EXIT_NRM != cre_jnl_file(&create)) { jpc->status = create.status; jpc->status2 = create.status2; sgtm_putmsg(err_str, err_str_len, VARLSTCNT(7) ERR_JNLSWITCHFAIL, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg), jpc->status); jpc->err_str = err_str; return ERR_JNLSWITCHFAIL; } jpc->status = SS_NORMAL; csd->jnl_checksum = create.checksum; csd->jnl_eovtn = csd->trans_hist.curr_tn; if (create.no_prev_link) send_msg_csa(CSA_ARG(csa) VARLSTCNT(6) ERR_PREVJNLLINKCUT, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg)); assert(csd->jnl_file_len == create.jnl_len); assert(0 == memcmp(csd->jnl_file_name, create.jnl, create.jnl_len)); return 0; } fis-gtm-V7.0-005/sr_port/jnl_flush.c0000644000032200000250000000413014342376331016171 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "jnl.h" #include "wbox_test_init.h" #include "gtm_threadgbl.h" GBLREF uint4 process_id; uint4 jnl_flush(gd_region *reg) { sgmnt_addrs *csa; node_local_ptr_t cnl; jnl_private_control *jpc; jnl_buffer_ptr_t jb; uint4 status; # ifdef DEBUG DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # endif if (!reg || !reg->open) return SS_NORMAL; csa = &FILE_INFO(reg)->s_addrs; assert(csa->now_crit); jpc = csa->jnl; if (!JNL_ENABLED(csa->hdr) || (NULL == jpc) || (NOJNL == jpc->channel)) return SS_NORMAL; jb = jpc->jnl_buff; jb->blocked = process_id; assert(jb->rsrv_freeaddr >= jb->freeaddr); assert((jb->rsrv_freeaddr >= jb->dskaddr) || (gtm_white_box_test_case_enabled && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number))); if (jb->rsrv_freeaddr != jb->dskaddr) { status = jnl_write_attempt(jpc, jb->rsrv_freeaddr); if (SS_NORMAL == status) { assert((jb->rsrv_freeaddr == jb->freeaddr) || (gtm_white_box_test_case_enabled && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number))); cnl = csa->nl; INCR_GVSTATS_COUNTER(csa, cnl, n_jnl_flush, 1); } } else status = SS_NORMAL; assert(((SS_NORMAL == status) && (jb->dskaddr == jb->rsrv_freeaddr) && (jb->freeaddr == jb->rsrv_freeaddr)) || TREF(gtm_test_fake_enospc) || (gtm_white_box_test_case_enabled && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number))); jb->blocked = 0; return status; } fis-gtm-V7.0-005/sr_port/jnl_get_checksum.c0000755000032200000250000007354014342376331017527 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2016 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 "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "gdsblk.h" #include "jnl_get_checksum.h" /* The following four looktable are generated using following paramenters. * Generator Polynomial = ................. 0x1EDC6F41 * Generator Polynomial Length = .......... 32 bits * Reflected Bits = ....................... TRUE * Number of Slices = ..................... 4 slices */ GBLDEF uint4 csum_table[SLICE_BY][TABLE_SIZE] = { #ifdef BIGENDIAN { 0x0, 0x3836bf2, 0xf7703be1, 0xf4f35013, 0x1f979ac7, 0x1c14f135, 0xe8e7a126, 0xeb64cad4, 0xcf58d98a, 0xccdbb278, 0x3828e26b, 0x3bab8999, 0xd0cf434d, 0xd34c28bf, 0x27bf78ac, 0x243c135e, 0x6fc75e10, 0x6c4435e2, 0x98b765f1, 0x9b340e03, 0x7050c4d7, 0x73d3af25, 0x8720ff36, 0x84a394c4, 0xa09f879a, 0xa31cec68, 0x57efbc7b, 0x546cd789, 0xbf081d5d, 0xbc8b76af, 0x487826bc, 0x4bfb4d4e, 0xde8ebd20, 0xdd0dd6d2, 0x29fe86c1, 0x2a7ded33, 0xc11927e7, 0xc29a4c15, 0x36691c06, 0x35ea77f4, 0x11d664aa, 0x12550f58, 0xe6a65f4b, 0xe52534b9, 0xe41fe6d, 0xdc2959f, 0xf931c58c, 0xfab2ae7e, 0xb149e330, 0xb2ca88c2, 0x4639d8d1, 0x45bab323, 0xaede79f7, 0xad5d1205, 0x59ae4216, 0x5a2d29e4, 0x7e113aba, 0x7d925148, 0x8961015b, 0x8ae26aa9, 0x6186a07d, 0x6205cb8f, 0x96f69b9c, 0x9575f06e, 0xbc1d7b41, 0xbf9e10b3, 0x4b6d40a0, 0x48ee2b52, 0xa38ae186, 0xa0098a74, 0x54fada67, 0x5779b195, 0x7345a2cb, 0x70c6c939, 0x8435992a, 0x87b6f2d8, 0x6cd2380c, 0x6f5153fe, 0x9ba203ed, 0x9821681f, 0xd3da2551, 0xd0594ea3, 0x24aa1eb0, 0x27297542, 0xcc4dbf96, 0xcfced464, 0x3b3d8477, 0x38beef85, 0x1c82fcdb, 0x1f019729, 0xebf2c73a, 0xe871acc8, 0x315661c, 0x960dee, 0xf4655dfd, 0xf7e6360f, 0x6293c661, 0x6110ad93, 0x95e3fd80, 0x96609672, 0x7d045ca6, 0x7e873754, 0x8a746747, 0x89f70cb5, 0xadcb1feb, 0xae487419, 0x5abb240a, 0x59384ff8, 0xb25c852c, 0xb1dfeede, 0x452cbecd, 0x46afd53f, 0xd549871, 0xed7f383, 0xfa24a390, 0xf9a7c862, 0x12c302b6, 0x11406944, 0xe5b33957, 0xe63052a5, 0xc20c41fb, 0xc18f2a09, 0x357c7a1a, 0x36ff11e8, 0xdd9bdb3c, 0xde18b0ce, 0x2aebe0dd, 0x29688b2f, 0x783bf682, 0x7bb89d70, 0x8f4bcd63, 0x8cc8a691, 0x67ac6c45, 0x642f07b7, 0x90dc57a4, 0x935f3c56, 0xb7632f08, 0xb4e044fa, 0x401314e9, 0x43907f1b, 0xa8f4b5cf, 0xab77de3d, 0x5f848e2e, 0x5c07e5dc, 0x17fca892, 0x147fc360, 0xe08c9373, 0xe30ff881, 0x86b3255, 0xbe859a7, 0xff1b09b4, 0xfc986246, 0xd8a47118, 0xdb271aea, 0x2fd44af9, 0x2c57210b, 0xc733ebdf, 0xc4b0802d, 0x3043d03e, 0x33c0bbcc, 0xa6b54ba2, 0xa5362050, 0x51c57043, 0x52461bb1, 0xb922d165, 0xbaa1ba97, 0x4e52ea84, 0x4dd18176, 0x69ed9228, 0x6a6ef9da, 0x9e9da9c9, 0x9d1ec23b, 0x767a08ef, 0x75f9631d, 0x810a330e, 0x828958fc, 0xc97215b2, 0xcaf17e40, 0x3e022e53, 0x3d8145a1, 0xd6e58f75, 0xd566e487, 0x2195b494, 0x2216df66, 0x62acc38, 0x5a9a7ca, 0xf15af7d9, 0xf2d99c2b, 0x19bd56ff, 0x1a3e3d0d, 0xeecd6d1e, 0xed4e06ec, 0xc4268dc3, 0xc7a5e631, 0x3356b622, 0x30d5ddd0, 0xdbb11704, 0xd8327cf6, 0x2cc12ce5, 0x2f424717, 0xb7e5449, 0x8fd3fbb, 0xfc0e6fa8, 0xff8d045a, 0x14e9ce8e, 0x176aa57c, 0xe399f56f, 0xe01a9e9d, 0xabe1d3d3, 0xa862b821, 0x5c91e832, 0x5f1283c0, 0xb4764914, 0xb7f522e6, 0x430672f5, 0x40851907, 0x64b90a59, 0x673a61ab, 0x93c931b8, 0x904a5a4a, 0x7b2e909e, 0x78adfb6c, 0x8c5eab7f, 0x8fddc08d, 0x1aa830e3, 0x192b5b11, 0xedd80b02, 0xee5b60f0, 0x53faa24, 0x6bcc1d6, 0xf24f91c5, 0xf1ccfa37, 0xd5f0e969, 0xd673829b, 0x2280d288, 0x2103b97a, 0xca6773ae, 0xc9e4185c, 0x3d17484f, 0x3e9423bd, 0x756f6ef3, 0x76ec0501, 0x821f5512, 0x819c3ee0, 0x6af8f434, 0x697b9fc6, 0x9d88cfd5, 0x9e0ba427, 0xba37b779, 0xb9b4dc8b, 0x4d478c98, 0x4ec4e76a, 0xa5a02dbe, 0xa623464c, 0x52d0165f, 0x51537dad }, { 0x0, 0x7798a213, 0xee304527, 0x99a8e734, 0xdc618a4e, 0xabf9285d, 0x3251cf69, 0x45c96d7a, 0xb8c3149d, 0xcf5bb68e, 0x56f351ba, 0x216bf3a9, 0x64a29ed3, 0x133a3cc0, 0x8a92dbf4, 0xfd0a79e7, 0x81f1c53f, 0xf669672c, 0x6fc18018, 0x1859220b, 0x5d904f71, 0x2a08ed62, 0xb3a00a56, 0xc438a845, 0x3932d1a2, 0x4eaa73b1, 0xd7029485, 0xa09a3696, 0xe5535bec, 0x92cbf9ff, 0xb631ecb, 0x7cfbbcd8, 0x2e38b7f, 0x757b296c, 0xecd3ce58, 0x9b4b6c4b, 0xde820131, 0xa91aa322, 0x30b24416, 0x472ae605, 0xba209fe2, 0xcdb83df1, 0x5410dac5, 0x238878d6, 0x664115ac, 0x11d9b7bf, 0x8871508b, 0xffe9f298, 0x83124e40, 0xf48aec53, 0x6d220b67, 0x1abaa974, 0x5f73c40e, 0x28eb661d, 0xb1438129, 0xc6db233a, 0x3bd15add, 0x4c49f8ce, 0xd5e11ffa, 0xa279bde9, 0xe7b0d093, 0x90287280, 0x98095b4, 0x7e1837a7, 0x4c617ff, 0x735eb5ec, 0xeaf652d8, 0x9d6ef0cb, 0xd8a79db1, 0xaf3f3fa2, 0x3697d896, 0x410f7a85, 0xbc050362, 0xcb9da171, 0x52354645, 0x25ade456, 0x6064892c, 0x17fc2b3f, 0x8e54cc0b, 0xf9cc6e18, 0x8537d2c0, 0xf2af70d3, 0x6b0797e7, 0x1c9f35f4, 0x5956588e, 0x2ecefa9d, 0xb7661da9, 0xc0febfba, 0x3df4c65d, 0x4a6c644e, 0xd3c4837a, 0xa45c2169, 0xe1954c13, 0x960dee00, 0xfa50934, 0x783dab27, 0x6259c80, 0x71bd3e93, 0xe815d9a7, 0x9f8d7bb4, 0xda4416ce, 0xaddcb4dd, 0x347453e9, 0x43ecf1fa, 0xbee6881d, 0xc97e2a0e, 0x50d6cd3a, 0x274e6f29, 0x62870253, 0x151fa040, 0x8cb74774, 0xfb2fe567, 0x87d459bf, 0xf04cfbac, 0x69e41c98, 0x1e7cbe8b, 0x5bb5d3f1, 0x2c2d71e2, 0xb58596d6, 0xc21d34c5, 0x3f174d22, 0x488fef31, 0xd1270805, 0xa6bfaa16, 0xe376c76c, 0x94ee657f, 0xd46824b, 0x7ade2058, 0xf9fac3fb, 0x8e6261e8, 0x17ca86dc, 0x605224cf, 0x259b49b5, 0x5203eba6, 0xcbab0c92, 0xbc33ae81, 0x4139d766, 0x36a17575, 0xaf099241, 0xd8913052, 0x9d585d28, 0xeac0ff3b, 0x7368180f, 0x4f0ba1c, 0x780b06c4, 0xf93a4d7, 0x963b43e3, 0xe1a3e1f0, 0xa46a8c8a, 0xd3f22e99, 0x4a5ac9ad, 0x3dc26bbe, 0xc0c81259, 0xb750b04a, 0x2ef8577e, 0x5960f56d, 0x1ca99817, 0x6b313a04, 0xf299dd30, 0x85017f23, 0xfb194884, 0x8c81ea97, 0x15290da3, 0x62b1afb0, 0x2778c2ca, 0x50e060d9, 0xc94887ed, 0xbed025fe, 0x43da5c19, 0x3442fe0a, 0xadea193e, 0xda72bb2d, 0x9fbbd657, 0xe8237444, 0x718b9370, 0x6133163, 0x7ae88dbb, 0xd702fa8, 0x94d8c89c, 0xe3406a8f, 0xa68907f5, 0xd111a5e6, 0x48b942d2, 0x3f21e0c1, 0xc22b9926, 0xb5b33b35, 0x2c1bdc01, 0x5b837e12, 0x1e4a1368, 0x69d2b17b, 0xf07a564f, 0x87e2f45c, 0xfd3cd404, 0x8aa47617, 0x130c9123, 0x64943330, 0x215d5e4a, 0x56c5fc59, 0xcf6d1b6d, 0xb8f5b97e, 0x45ffc099, 0x3267628a, 0xabcf85be, 0xdc5727ad, 0x999e4ad7, 0xee06e8c4, 0x77ae0ff0, 0x36ade3, 0x7ccd113b, 0xb55b328, 0x92fd541c, 0xe565f60f, 0xa0ac9b75, 0xd7343966, 0x4e9cde52, 0x39047c41, 0xc40e05a6, 0xb396a7b5, 0x2a3e4081, 0x5da6e292, 0x186f8fe8, 0x6ff72dfb, 0xf65fcacf, 0x81c768dc, 0xffdf5f7b, 0x8847fd68, 0x11ef1a5c, 0x6677b84f, 0x23bed535, 0x54267726, 0xcd8e9012, 0xba163201, 0x471c4be6, 0x3084e9f5, 0xa92c0ec1, 0xdeb4acd2, 0x9b7dc1a8, 0xece563bb, 0x754d848f, 0x2d5269c, 0x7e2e9a44, 0x9b63857, 0x901edf63, 0xe7867d70, 0xa24f100a, 0xd5d7b219, 0x4c7f552d, 0x3be7f73e, 0xc6ed8ed9, 0xb1752cca, 0x28ddcbfe, 0x5f4569ed, 0x1a8c0497, 0x6d14a684, 0xf4bc41b0, 0x8324e3a3 }, { 0x0, 0x7e9241a5, 0xd526f4f, 0x73c02eea, 0x1aa4de9e, 0x64369f3b, 0x17f6b1d1, 0x6964f074, 0xc53e5138, 0xbbac109d, 0xc86c3e77, 0xb6fe7fd2, 0xdf9a8fa6, 0xa108ce03, 0xd2c8e0e9, 0xac5aa14c, 0x8a7da270, 0xf4efe3d5, 0x872fcd3f, 0xf9bd8c9a, 0x90d97cee, 0xee4b3d4b, 0x9d8b13a1, 0xe3195204, 0x4f43f348, 0x31d1b2ed, 0x42119c07, 0x3c83dda2, 0x55e72dd6, 0x2b756c73, 0x58b54299, 0x2627033c, 0x14fb44e1, 0x6a690544, 0x19a92bae, 0x673b6a0b, 0xe5f9a7f, 0x70cddbda, 0x30df530, 0x7d9fb495, 0xd1c515d9, 0xaf57547c, 0xdc977a96, 0xa2053b33, 0xcb61cb47, 0xb5f38ae2, 0xc633a408, 0xb8a1e5ad, 0x9e86e691, 0xe014a734, 0x93d489de, 0xed46c87b, 0x8422380f, 0xfab079aa, 0x89705740, 0xf7e216e5, 0x5bb8b7a9, 0x252af60c, 0x56ead8e6, 0x28789943, 0x411c6937, 0x3f8e2892, 0x4c4e0678, 0x32dc47dd, 0xd98065c7, 0xa7122462, 0xd4d20a88, 0xaa404b2d, 0xc324bb59, 0xbdb6fafc, 0xce76d416, 0xb0e495b3, 0x1cbe34ff, 0x622c755a, 0x11ec5bb0, 0x6f7e1a15, 0x61aea61, 0x7888abc4, 0xb48852e, 0x75dac48b, 0x53fdc7b7, 0x2d6f8612, 0x5eafa8f8, 0x203de95d, 0x49591929, 0x37cb588c, 0x440b7666, 0x3a9937c3, 0x96c3968f, 0xe851d72a, 0x9b91f9c0, 0xe503b865, 0x8c674811, 0xf2f509b4, 0x8135275e, 0xffa766fb, 0xcd7b2126, 0xb3e96083, 0xc0294e69, 0xbebb0fcc, 0xd7dfffb8, 0xa94dbe1d, 0xda8d90f7, 0xa41fd152, 0x845701e, 0x76d731bb, 0x5171f51, 0x7b855ef4, 0x12e1ae80, 0x6c73ef25, 0x1fb3c1cf, 0x6121806a, 0x47068356, 0x3994c2f3, 0x4a54ec19, 0x34c6adbc, 0x5da25dc8, 0x23301c6d, 0x50f03287, 0x2e627322, 0x8238d26e, 0xfcaa93cb, 0x8f6abd21, 0xf1f8fc84, 0x989c0cf0, 0xe60e4d55, 0x95ce63bf, 0xeb5c221a, 0x4377278b, 0x3de5662e, 0x4e2548c4, 0x30b70961, 0x59d3f915, 0x2741b8b0, 0x5481965a, 0x2a13d7ff, 0x864976b3, 0xf8db3716, 0x8b1b19fc, 0xf5895859, 0x9ceda82d, 0xe27fe988, 0x91bfc762, 0xef2d86c7, 0xc90a85fb, 0xb798c45e, 0xc458eab4, 0xbacaab11, 0xd3ae5b65, 0xad3c1ac0, 0xdefc342a, 0xa06e758f, 0xc34d4c3, 0x72a69566, 0x166bb8c, 0x7ff4fa29, 0x16900a5d, 0x68024bf8, 0x1bc26512, 0x655024b7, 0x578c636a, 0x291e22cf, 0x5ade0c25, 0x244c4d80, 0x4d28bdf4, 0x33bafc51, 0x407ad2bb, 0x3ee8931e, 0x92b23252, 0xec2073f7, 0x9fe05d1d, 0xe1721cb8, 0x8816eccc, 0xf684ad69, 0x85448383, 0xfbd6c226, 0xddf1c11a, 0xa36380bf, 0xd0a3ae55, 0xae31eff0, 0xc7551f84, 0xb9c75e21, 0xca0770cb, 0xb495316e, 0x18cf9022, 0x665dd187, 0x159dff6d, 0x6b0fbec8, 0x26b4ebc, 0x7cf90f19, 0xf3921f3, 0x71ab6056, 0x9af7424c, 0xe46503e9, 0x97a52d03, 0xe9376ca6, 0x80539cd2, 0xfec1dd77, 0x8d01f39d, 0xf393b238, 0x5fc91374, 0x215b52d1, 0x529b7c3b, 0x2c093d9e, 0x456dcdea, 0x3bff8c4f, 0x483fa2a5, 0x36ade300, 0x108ae03c, 0x6e18a199, 0x1dd88f73, 0x634aced6, 0xa2e3ea2, 0x74bc7f07, 0x77c51ed, 0x79ee1048, 0xd5b4b104, 0xab26f0a1, 0xd8e6de4b, 0xa6749fee, 0xcf106f9a, 0xb1822e3f, 0xc24200d5, 0xbcd04170, 0x8e0c06ad, 0xf09e4708, 0x835e69e2, 0xfdcc2847, 0x94a8d833, 0xea3a9996, 0x99fab77c, 0xe768f6d9, 0x4b325795, 0x35a01630, 0x466038da, 0x38f2797f, 0x5196890b, 0x2f04c8ae, 0x5cc4e644, 0x2256a7e1, 0x471a4dd, 0x7ae3e578, 0x923cb92, 0x77b18a37, 0x1ed57a43, 0x60473be6, 0x1387150c, 0x6d1554a9, 0xc14ff5e5, 0xbfddb440, 0xcc1d9aaa, 0xb28fdb0f, 0xdbeb2b7b, 0xa5796ade, 0xd6b94434, 0xa82b0591 }, { 0x0, 0xb8aa45dd, 0x812367bf, 0x39892262, 0xf331227b, 0x4b9b67a6, 0x721245c4, 0xcab80019, 0xe66344f6, 0x5ec9012b, 0x67402349, 0xdfea6694, 0x1552668d, 0xadf82350, 0x94710132, 0x2cdb44ef, 0x3db164e9, 0x851b2134, 0xbc920356, 0x438468b, 0xce804692, 0x762a034f, 0x4fa3212d, 0xf70964f0, 0xdbd2201f, 0x637865c2, 0x5af147a0, 0xe25b027d, 0x28e30264, 0x904947b9, 0xa9c065db, 0x116a2006, 0x8b1425d7, 0x33be600a, 0xa374268, 0xb29d07b5, 0x782507ac, 0xc08f4271, 0xf9066013, 0x41ac25ce, 0x6d776121, 0xd5dd24fc, 0xec54069e, 0x54fe4343, 0x9e46435a, 0x26ec0687, 0x1f6524e5, 0xa7cf6138, 0xb6a5413e, 0xe0f04e3, 0x37862681, 0x8f2c635c, 0x45946345, 0xfd3e2698, 0xc4b704fa, 0x7c1d4127, 0x50c605c8, 0xe86c4015, 0xd1e56277, 0x694f27aa, 0xa3f727b3, 0x1b5d626e, 0x22d4400c, 0x9a7e05d1, 0xe75fa6ab, 0x5ff5e376, 0x667cc114, 0xded684c9, 0x146e84d0, 0xacc4c10d, 0x954de36f, 0x2de7a6b2, 0x13ce25d, 0xb996a780, 0x801f85e2, 0x38b5c03f, 0xf20dc026, 0x4aa785fb, 0x732ea799, 0xcb84e244, 0xdaeec242, 0x6244879f, 0x5bcda5fd, 0xe367e020, 0x29dfe039, 0x9175a5e4, 0xa8fc8786, 0x1056c25b, 0x3c8d86b4, 0x8427c369, 0xbdaee10b, 0x504a4d6, 0xcfbca4cf, 0x7716e112, 0x4e9fc370, 0xf63586ad, 0x6c4b837c, 0xd4e1c6a1, 0xed68e4c3, 0x55c2a11e, 0x9f7aa107, 0x27d0e4da, 0x1e59c6b8, 0xa6f38365, 0x8a28c78a, 0x32828257, 0xb0ba035, 0xb3a1e5e8, 0x7919e5f1, 0xc1b3a02c, 0xf83a824e, 0x4090c793, 0x51fae795, 0xe950a248, 0xd0d9802a, 0x6873c5f7, 0xa2cbc5ee, 0x1a618033, 0x23e8a251, 0x9b42e78c, 0xb799a363, 0xf33e6be, 0x36bac4dc, 0x8e108101, 0x44a88118, 0xfc02c4c5, 0xc58be6a7, 0x7d21a37a, 0x3fc9a052, 0x8763e58f, 0xbeeac7ed, 0x6408230, 0xccf88229, 0x7452c7f4, 0x4ddbe596, 0xf571a04b, 0xd9aae4a4, 0x6100a179, 0x5889831b, 0xe023c6c6, 0x2a9bc6df, 0x92318302, 0xabb8a160, 0x1312e4bd, 0x278c4bb, 0xbad28166, 0x835ba304, 0x3bf1e6d9, 0xf149e6c0, 0x49e3a31d, 0x706a817f, 0xc8c0c4a2, 0xe41b804d, 0x5cb1c590, 0x6538e7f2, 0xdd92a22f, 0x172aa236, 0xaf80e7eb, 0x9609c589, 0x2ea38054, 0xb4dd8585, 0xc77c058, 0x35fee23a, 0x8d54a7e7, 0x47eca7fe, 0xff46e223, 0xc6cfc041, 0x7e65859c, 0x52bec173, 0xea1484ae, 0xd39da6cc, 0x6b37e311, 0xa18fe308, 0x1925a6d5, 0x20ac84b7, 0x9806c16a, 0x896ce16c, 0x31c6a4b1, 0x84f86d3, 0xb0e5c30e, 0x7a5dc317, 0xc2f786ca, 0xfb7ea4a8, 0x43d4e175, 0x6f0fa59a, 0xd7a5e047, 0xee2cc225, 0x568687f8, 0x9c3e87e1, 0x2494c23c, 0x1d1de05e, 0xa5b7a583, 0xd89606f9, 0x603c4324, 0x59b56146, 0xe11f249b, 0x2ba72482, 0x930d615f, 0xaa84433d, 0x122e06e0, 0x3ef5420f, 0x865f07d2, 0xbfd625b0, 0x77c606d, 0xcdc46074, 0x756e25a9, 0x4ce707cb, 0xf44d4216, 0xe5276210, 0x5d8d27cd, 0x640405af, 0xdcae4072, 0x1616406b, 0xaebc05b6, 0x973527d4, 0x2f9f6209, 0x34426e6, 0xbbee633b, 0x82674159, 0x3acd0484, 0xf075049d, 0x48df4140, 0x71566322, 0xc9fc26ff, 0x5382232e, 0xeb2866f3, 0xd2a14491, 0x6a0b014c, 0xa0b30155, 0x18194488, 0x219066ea, 0x993a2337, 0xb5e167d8, 0xd4b2205, 0x34c20067, 0x8c6845ba, 0x46d045a3, 0xfe7a007e, 0xc7f3221c, 0x7f5967c1, 0x6e3347c7, 0xd699021a, 0xef102078, 0x57ba65a5, 0x9d0265bc, 0x25a82061, 0x1c210203, 0xa48b47de, 0x88500331, 0x30fa46ec, 0x973648e, 0xb1d92153, 0x7b61214a, 0xc3cb6497, 0xfa4246f5, 0x42e80328 } #else { 0x0, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb, 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, 0x105ec76f, 0xe235446c, 0xf165b798, 0x30e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b, 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, 0xe72719c1, 0x154c9ac2, 0x61c6936, 0xf477ea35, 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x5125dad, 0x1642ae59, 0xe4292d5a, 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0xc38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198, 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0xf36e6f7, 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789, 0xeb1fcbad, 0x197448ae, 0xa24bb5a, 0xf84f3859, 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, 0xfb410cc2, 0x92a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829, 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, 0x82f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc, 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0xb21572c, 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, 0xe330a81, 0xfc588982, 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0xd3d3e1a, 0x1e6dcdee, 0xec064eed, 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f, 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x7198540, 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f, 0xe330a81a, 0x115b2b19, 0x20bd8ed, 0xf0605bee, 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, 0xf36e6f75, 0x105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e, 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351 }, { 0x0, 0x13a29877, 0x274530ee, 0x34e7a899, 0x4e8a61dc, 0x5d28f9ab, 0x69cf5132, 0x7a6dc945, 0x9d14c3b8, 0x8eb65bcf, 0xba51f356, 0xa9f36b21, 0xd39ea264, 0xc03c3a13, 0xf4db928a, 0xe7790afd, 0x3fc5f181, 0x2c6769f6, 0x1880c16f, 0xb225918, 0x714f905d, 0x62ed082a, 0x560aa0b3, 0x45a838c4, 0xa2d13239, 0xb173aa4e, 0x859402d7, 0x96369aa0, 0xec5b53e5, 0xfff9cb92, 0xcb1e630b, 0xd8bcfb7c, 0x7f8be302, 0x6c297b75, 0x58ced3ec, 0x4b6c4b9b, 0x310182de, 0x22a31aa9, 0x1644b230, 0x5e62a47, 0xe29f20ba, 0xf13db8cd, 0xc5da1054, 0xd6788823, 0xac154166, 0xbfb7d911, 0x8b507188, 0x98f2e9ff, 0x404e1283, 0x53ec8af4, 0x670b226d, 0x74a9ba1a, 0xec4735f, 0x1d66eb28, 0x298143b1, 0x3a23dbc6, 0xdd5ad13b, 0xcef8494c, 0xfa1fe1d5, 0xe9bd79a2, 0x93d0b0e7, 0x80722890, 0xb4958009, 0xa737187e, 0xff17c604, 0xecb55e73, 0xd852f6ea, 0xcbf06e9d, 0xb19da7d8, 0xa23f3faf, 0x96d89736, 0x857a0f41, 0x620305bc, 0x71a19dcb, 0x45463552, 0x56e4ad25, 0x2c896460, 0x3f2bfc17, 0xbcc548e, 0x186eccf9, 0xc0d23785, 0xd370aff2, 0xe797076b, 0xf4359f1c, 0x8e585659, 0x9dface2e, 0xa91d66b7, 0xbabffec0, 0x5dc6f43d, 0x4e646c4a, 0x7a83c4d3, 0x69215ca4, 0x134c95e1, 0xee0d96, 0x3409a50f, 0x27ab3d78, 0x809c2506, 0x933ebd71, 0xa7d915e8, 0xb47b8d9f, 0xce1644da, 0xddb4dcad, 0xe9537434, 0xfaf1ec43, 0x1d88e6be, 0xe2a7ec9, 0x3acdd650, 0x296f4e27, 0x53028762, 0x40a01f15, 0x7447b78c, 0x67e52ffb, 0xbf59d487, 0xacfb4cf0, 0x981ce469, 0x8bbe7c1e, 0xf1d3b55b, 0xe2712d2c, 0xd69685b5, 0xc5341dc2, 0x224d173f, 0x31ef8f48, 0x50827d1, 0x16aabfa6, 0x6cc776e3, 0x7f65ee94, 0x4b82460d, 0x5820de7a, 0xfbc3faf9, 0xe861628e, 0xdc86ca17, 0xcf245260, 0xb5499b25, 0xa6eb0352, 0x920cabcb, 0x81ae33bc, 0x66d73941, 0x7575a136, 0x419209af, 0x523091d8, 0x285d589d, 0x3bffc0ea, 0xf186873, 0x1cbaf004, 0xc4060b78, 0xd7a4930f, 0xe3433b96, 0xf0e1a3e1, 0x8a8c6aa4, 0x992ef2d3, 0xadc95a4a, 0xbe6bc23d, 0x5912c8c0, 0x4ab050b7, 0x7e57f82e, 0x6df56059, 0x1798a91c, 0x43a316b, 0x30dd99f2, 0x237f0185, 0x844819fb, 0x97ea818c, 0xa30d2915, 0xb0afb162, 0xcac27827, 0xd960e050, 0xed8748c9, 0xfe25d0be, 0x195cda43, 0xafe4234, 0x3e19eaad, 0x2dbb72da, 0x57d6bb9f, 0x447423e8, 0x70938b71, 0x63311306, 0xbb8de87a, 0xa82f700d, 0x9cc8d894, 0x8f6a40e3, 0xf50789a6, 0xe6a511d1, 0xd242b948, 0xc1e0213f, 0x26992bc2, 0x353bb3b5, 0x1dc1b2c, 0x127e835b, 0x68134a1e, 0x7bb1d269, 0x4f567af0, 0x5cf4e287, 0x4d43cfd, 0x1776a48a, 0x23910c13, 0x30339464, 0x4a5e5d21, 0x59fcc556, 0x6d1b6dcf, 0x7eb9f5b8, 0x99c0ff45, 0x8a626732, 0xbe85cfab, 0xad2757dc, 0xd74a9e99, 0xc4e806ee, 0xf00fae77, 0xe3ad3600, 0x3b11cd7c, 0x28b3550b, 0x1c54fd92, 0xff665e5, 0x759baca0, 0x663934d7, 0x52de9c4e, 0x417c0439, 0xa6050ec4, 0xb5a796b3, 0x81403e2a, 0x92e2a65d, 0xe88f6f18, 0xfb2df76f, 0xcfca5ff6, 0xdc68c781, 0x7b5fdfff, 0x68fd4788, 0x5c1aef11, 0x4fb87766, 0x35d5be23, 0x26772654, 0x12908ecd, 0x13216ba, 0xe64b1c47, 0xf5e98430, 0xc10e2ca9, 0xd2acb4de, 0xa8c17d9b, 0xbb63e5ec, 0x8f844d75, 0x9c26d502, 0x449a2e7e, 0x5738b609, 0x63df1e90, 0x707d86e7, 0xa104fa2, 0x19b2d7d5, 0x2d557f4c, 0x3ef7e73b, 0xd98eedc6, 0xca2c75b1, 0xfecbdd28, 0xed69455f, 0x97048c1a, 0x84a6146d, 0xb041bcf4, 0xa3e32483 }, { 0x0, 0xa541927e, 0x4f6f520d, 0xea2ec073, 0x9edea41a, 0x3b9f3664, 0xd1b1f617, 0x74f06469, 0x38513ec5, 0x9d10acbb, 0x773e6cc8, 0xd27ffeb6, 0xa68f9adf, 0x3ce08a1, 0xe9e0c8d2, 0x4ca15aac, 0x70a27d8a, 0xd5e3eff4, 0x3fcd2f87, 0x9a8cbdf9, 0xee7cd990, 0x4b3d4bee, 0xa1138b9d, 0x45219e3, 0x48f3434f, 0xedb2d131, 0x79c1142, 0xa2dd833c, 0xd62de755, 0x736c752b, 0x9942b558, 0x3c032726, 0xe144fb14, 0x4405696a, 0xae2ba919, 0xb6a3b67, 0x7f9a5f0e, 0xdadbcd70, 0x30f50d03, 0x95b49f7d, 0xd915c5d1, 0x7c5457af, 0x967a97dc, 0x333b05a2, 0x47cb61cb, 0xe28af3b5, 0x8a433c6, 0xade5a1b8, 0x91e6869e, 0x34a714e0, 0xde89d493, 0x7bc846ed, 0xf382284, 0xaa79b0fa, 0x40577089, 0xe516e2f7, 0xa9b7b85b, 0xcf62a25, 0xe6d8ea56, 0x43997828, 0x37691c41, 0x92288e3f, 0x78064e4c, 0xdd47dc32, 0xc76580d9, 0x622412a7, 0x880ad2d4, 0x2d4b40aa, 0x59bb24c3, 0xfcfab6bd, 0x16d476ce, 0xb395e4b0, 0xff34be1c, 0x5a752c62, 0xb05bec11, 0x151a7e6f, 0x61ea1a06, 0xc4ab8878, 0x2e85480b, 0x8bc4da75, 0xb7c7fd53, 0x12866f2d, 0xf8a8af5e, 0x5de93d20, 0x29195949, 0x8c58cb37, 0x66760b44, 0xc337993a, 0x8f96c396, 0x2ad751e8, 0xc0f9919b, 0x65b803e5, 0x1148678c, 0xb409f5f2, 0x5e273581, 0xfb66a7ff, 0x26217bcd, 0x8360e9b3, 0x694e29c0, 0xcc0fbbbe, 0xb8ffdfd7, 0x1dbe4da9, 0xf7908dda, 0x52d11fa4, 0x1e704508, 0xbb31d776, 0x511f1705, 0xf45e857b, 0x80aee112, 0x25ef736c, 0xcfc1b31f, 0x6a802161, 0x56830647, 0xf3c29439, 0x19ec544a, 0xbcadc634, 0xc85da25d, 0x6d1c3023, 0x8732f050, 0x2273622e, 0x6ed23882, 0xcb93aafc, 0x21bd6a8f, 0x84fcf8f1, 0xf00c9c98, 0x554d0ee6, 0xbf63ce95, 0x1a225ceb, 0x8b277743, 0x2e66e53d, 0xc448254e, 0x6109b730, 0x15f9d359, 0xb0b84127, 0x5a968154, 0xffd7132a, 0xb3764986, 0x1637dbf8, 0xfc191b8b, 0x595889f5, 0x2da8ed9c, 0x88e97fe2, 0x62c7bf91, 0xc7862def, 0xfb850ac9, 0x5ec498b7, 0xb4ea58c4, 0x11abcaba, 0x655baed3, 0xc01a3cad, 0x2a34fcde, 0x8f756ea0, 0xc3d4340c, 0x6695a672, 0x8cbb6601, 0x29faf47f, 0x5d0a9016, 0xf84b0268, 0x1265c21b, 0xb7245065, 0x6a638c57, 0xcf221e29, 0x250cde5a, 0x804d4c24, 0xf4bd284d, 0x51fcba33, 0xbbd27a40, 0x1e93e83e, 0x5232b292, 0xf77320ec, 0x1d5de09f, 0xb81c72e1, 0xccec1688, 0x69ad84f6, 0x83834485, 0x26c2d6fb, 0x1ac1f1dd, 0xbf8063a3, 0x55aea3d0, 0xf0ef31ae, 0x841f55c7, 0x215ec7b9, 0xcb7007ca, 0x6e3195b4, 0x2290cf18, 0x87d15d66, 0x6dff9d15, 0xc8be0f6b, 0xbc4e6b02, 0x190ff97c, 0xf321390f, 0x5660ab71, 0x4c42f79a, 0xe90365e4, 0x32da597, 0xa66c37e9, 0xd29c5380, 0x77ddc1fe, 0x9df3018d, 0x38b293f3, 0x7413c95f, 0xd1525b21, 0x3b7c9b52, 0x9e3d092c, 0xeacd6d45, 0x4f8cff3b, 0xa5a23f48, 0xe3ad36, 0x3ce08a10, 0x99a1186e, 0x738fd81d, 0xd6ce4a63, 0xa23e2e0a, 0x77fbc74, 0xed517c07, 0x4810ee79, 0x4b1b4d5, 0xa1f026ab, 0x4bdee6d8, 0xee9f74a6, 0x9a6f10cf, 0x3f2e82b1, 0xd50042c2, 0x7041d0bc, 0xad060c8e, 0x8479ef0, 0xe2695e83, 0x4728ccfd, 0x33d8a894, 0x96993aea, 0x7cb7fa99, 0xd9f668e7, 0x9557324b, 0x3016a035, 0xda386046, 0x7f79f238, 0xb899651, 0xaec8042f, 0x44e6c45c, 0xe1a75622, 0xdda47104, 0x78e5e37a, 0x92cb2309, 0x378ab177, 0x437ad51e, 0xe63b4760, 0xc158713, 0xa954156d, 0xe5f54fc1, 0x40b4ddbf, 0xaa9a1dcc, 0xfdb8fb2, 0x7b2bebdb, 0xde6a79a5, 0x3444b9d6, 0x91052ba8 }, { 0x0, 0xdd45aab8, 0xbf672381, 0x62228939, 0x7b2231f3, 0xa6679b4b, 0xc4451272, 0x1900b8ca, 0xf64463e6, 0x2b01c95e, 0x49234067, 0x9466eadf, 0x8d665215, 0x5023f8ad, 0x32017194, 0xef44db2c, 0xe964b13d, 0x34211b85, 0x560392bc, 0x8b463804, 0x924680ce, 0x4f032a76, 0x2d21a34f, 0xf06409f7, 0x1f20d2db, 0xc2657863, 0xa047f15a, 0x7d025be2, 0x6402e328, 0xb9474990, 0xdb65c0a9, 0x6206a11, 0xd725148b, 0xa60be33, 0x6842370a, 0xb5079db2, 0xac072578, 0x71428fc0, 0x136006f9, 0xce25ac41, 0x2161776d, 0xfc24ddd5, 0x9e0654ec, 0x4343fe54, 0x5a43469e, 0x8706ec26, 0xe524651f, 0x3861cfa7, 0x3e41a5b6, 0xe3040f0e, 0x81268637, 0x5c632c8f, 0x45639445, 0x98263efd, 0xfa04b7c4, 0x27411d7c, 0xc805c650, 0x15406ce8, 0x7762e5d1, 0xaa274f69, 0xb327f7a3, 0x6e625d1b, 0xc40d422, 0xd1057e9a, 0xaba65fe7, 0x76e3f55f, 0x14c17c66, 0xc984d6de, 0xd0846e14, 0xdc1c4ac, 0x6fe34d95, 0xb2a6e72d, 0x5de23c01, 0x80a796b9, 0xe2851f80, 0x3fc0b538, 0x26c00df2, 0xfb85a74a, 0x99a72e73, 0x44e284cb, 0x42c2eeda, 0x9f874462, 0xfda5cd5b, 0x20e067e3, 0x39e0df29, 0xe4a57591, 0x8687fca8, 0x5bc25610, 0xb4868d3c, 0x69c32784, 0xbe1aebd, 0xd6a40405, 0xcfa4bccf, 0x12e11677, 0x70c39f4e, 0xad8635f6, 0x7c834b6c, 0xa1c6e1d4, 0xc3e468ed, 0x1ea1c255, 0x7a17a9f, 0xdae4d027, 0xb8c6591e, 0x6583f3a6, 0x8ac7288a, 0x57828232, 0x35a00b0b, 0xe8e5a1b3, 0xf1e51979, 0x2ca0b3c1, 0x4e823af8, 0x93c79040, 0x95e7fa51, 0x48a250e9, 0x2a80d9d0, 0xf7c57368, 0xeec5cba2, 0x3380611a, 0x51a2e823, 0x8ce7429b, 0x63a399b7, 0xbee6330f, 0xdcc4ba36, 0x181108e, 0x1881a844, 0xc5c402fc, 0xa7e68bc5, 0x7aa3217d, 0x52a0c93f, 0x8fe56387, 0xedc7eabe, 0x30824006, 0x2982f8cc, 0xf4c75274, 0x96e5db4d, 0x4ba071f5, 0xa4e4aad9, 0x79a10061, 0x1b838958, 0xc6c623e0, 0xdfc69b2a, 0x2833192, 0x60a1b8ab, 0xbde41213, 0xbbc47802, 0x6681d2ba, 0x4a35b83, 0xd9e6f13b, 0xc0e649f1, 0x1da3e349, 0x7f816a70, 0xa2c4c0c8, 0x4d801be4, 0x90c5b15c, 0xf2e73865, 0x2fa292dd, 0x36a22a17, 0xebe780af, 0x89c50996, 0x5480a32e, 0x8585ddb4, 0x58c0770c, 0x3ae2fe35, 0xe7a7548d, 0xfea7ec47, 0x23e246ff, 0x41c0cfc6, 0x9c85657e, 0x73c1be52, 0xae8414ea, 0xcca69dd3, 0x11e3376b, 0x8e38fa1, 0xd5a62519, 0xb784ac20, 0x6ac10698, 0x6ce16c89, 0xb1a4c631, 0xd3864f08, 0xec3e5b0, 0x17c35d7a, 0xca86f7c2, 0xa8a47efb, 0x75e1d443, 0x9aa50f6f, 0x47e0a5d7, 0x25c22cee, 0xf8878656, 0xe1873e9c, 0x3cc29424, 0x5ee01d1d, 0x83a5b7a5, 0xf90696d8, 0x24433c60, 0x4661b559, 0x9b241fe1, 0x8224a72b, 0x5f610d93, 0x3d4384aa, 0xe0062e12, 0xf42f53e, 0xd2075f86, 0xb025d6bf, 0x6d607c07, 0x7460c4cd, 0xa9256e75, 0xcb07e74c, 0x16424df4, 0x106227e5, 0xcd278d5d, 0xaf050464, 0x7240aedc, 0x6b401616, 0xb605bcae, 0xd4273597, 0x9629f2f, 0xe6264403, 0x3b63eebb, 0x59416782, 0x8404cd3a, 0x9d0475f0, 0x4041df48, 0x22635671, 0xff26fcc9, 0x2e238253, 0xf36628eb, 0x9144a1d2, 0x4c010b6a, 0x5501b3a0, 0x88441918, 0xea669021, 0x37233a99, 0xd867e1b5, 0x5224b0d, 0x6700c234, 0xba45688c, 0xa345d046, 0x7e007afe, 0x1c22f3c7, 0xc167597f, 0xc747336e, 0x1a0299d6, 0x782010ef, 0xa565ba57, 0xbc65029d, 0x6120a825, 0x302211c, 0xde478ba4, 0x31035088, 0xec46fa30, 0x8e647309, 0x5321d9b1, 0x4a21617b, 0x9764cbc3, 0xf54642fa, 0x2803e842 } #endif }; /* Input : * buff : Pointer to the input buffer whose checksum needs to be computed. * sgmnt_addrs : Segment address * bufflen : Buffer size in bytes * * Returns: * Computed checksum. */ /* #GTM_THREAD_SAFE : The below function (jnl_get_checksum) is thread-safe */ uint4 jnl_get_checksum(blk_hdr_ptr_t buff, sgmnt_addrs *csa, int bufflen) { uint4 *top, *blk_base, *blk_top, blen; DEBUG_ONLY(sm_uc_ptr_t orig_buff = NULL;) /* We are not necessarily holding crit here, so we have to rely on the csa->encr_ptr copy of encryption settings. */ if ((NULL != csa) && (NULL != csa->encr_ptr) && NEEDS_ANY_KEY(csa->encr_ptr, buff->tn)) { DBG_ENSURE_PTR_IS_VALID_GLOBUFF(csa, csa->hdr, (sm_uc_ptr_t)buff); DEBUG_ONLY(orig_buff = (unsigned char *)buff;) buff = (blk_hdr_ptr_t)GDS_ANY_ENCRYPTGLOBUF(buff, csa); DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(csa, csa->hdr, (sm_uc_ptr_t)buff); } return (compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)buff, bufflen)); } /* Input : * buff : Pointer to the input buffer whose checksum needs to be computed. * bufflen : Buffer size in bytes * * Returns: * Computed checksum. * * Algorithm: * The checksum is calculated using slice-by-4 checksum calculation algorithm */ /* #GTM_THREAD_SAFE : The below function (compute_checksum) is thread-safe */ uint4 compute_checksum(uint4 init_checksum, unsigned char *buff, int bufflen) { uint4 checksum = init_checksum; unsigned char *byte; int word_cnt, i, rem_bytes; /* calculate checksum one byte at a time so that subsequent data read will be at addresses aligned to multiple of 4*/ #ifdef GTM64 for (byte = buff; ((gtm_uint8)byte & SIZEOF(gtm_uint8)) != 0; byte++, bufflen--) #else for (byte = buff; ((uint4)byte & SIZEOF(uint4)) != 0; byte++, bufflen--) #endif { #ifdef BIGENDIAN checksum = (checksum << BITS_PER_UCHAR) ^ csum_table[0][((checksum >> (3 * BITS_PER_UCHAR)) ^ *byte) & BYTEMASK]; #else checksum = (checksum >> BITS_PER_UCHAR) ^ csum_table[0][(checksum ^ *byte) & BYTEMASK]; #endif } word_cnt = bufflen / SIZEOF(uint4); rem_bytes = bufflen & (int)(SIZEOF(uint4) - 1); /* Equivalent to bufflen % SIZEOF(uint4) */ for(i = 0; i < word_cnt; i++) { checksum = checksum ^ *(uint4 *)byte; byte = byte + SIZEOF(uint4); #ifdef BIGENDIAN checksum = csum_table[LOBYTE + 0][checksum & BYTEMASK] ^ csum_table[LOBYTE + 1][(checksum >> (1 * BITS_PER_UCHAR)) & BYTEMASK] ^ csum_table[LOBYTE + 2][(checksum >> (2 * BITS_PER_UCHAR)) & BYTEMASK] ^ csum_table[LOBYTE + 3][(checksum >> (3 * BITS_PER_UCHAR)) & BYTEMASK]; #else checksum = csum_table[HIBYTE - 0][checksum & BYTEMASK] ^ csum_table[HIBYTE - 1][(checksum >> (1 * BITS_PER_UCHAR)) & BYTEMASK] ^ csum_table[HIBYTE - 2][(checksum >> (2 * BITS_PER_UCHAR)) & BYTEMASK] ^ csum_table[HIBYTE - 3][(checksum >> (3 * BITS_PER_UCHAR)) & BYTEMASK]; #endif } for(i = 0; i < rem_bytes; i++, byte++) { #ifdef BIGENDIAN checksum = (checksum << BITS_PER_UCHAR) ^ csum_table[0][((checksum >> (3 * BITS_PER_UCHAR)) ^ *byte) & BYTEMASK]; #else checksum = (checksum >> BITS_PER_UCHAR) ^ csum_table[0][(checksum ^ *byte) & BYTEMASK]; #endif } return (checksum ? checksum : INIT_CHECKSUM_SEED); } fis-gtm-V7.0-005/sr_port/jnl_get_checksum.h0000644000032200000250000001547614342376331017535 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2005-2018 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. * * * ****************************************************************/ #ifndef JNL_GET_CHECKSUM_H_INCLUDED #define JNL_GET_CHECKSUM_H_INCLUDED #define INIT_CHECKSUM_SEED 0xFFFFFFFF #define SLICE_BY 4 #define TABLE_SIZE 256 #define BYTEMASK 0xFF GBLREF uint4 csum_table[SLICE_BY][TABLE_SIZE]; #ifdef BIGENDIAN #define LOBYTE 0 #define ADJUST_CHECKSUM(cursum, num4, newsum) \ MBSTART { \ uint4 tmpsum = csum_table[LOBYTE + 0][(cursum ^ num4) & BYTEMASK] ^ \ csum_table[LOBYTE + 1][((cursum ^ num4) >> (1 * BITS_PER_UCHAR)) & BYTEMASK] ^ \ csum_table[LOBYTE + 2][((cursum ^ num4) >> (2 * BITS_PER_UCHAR)) & BYTEMASK] ^ \ csum_table[LOBYTE + 3][((cursum ^ num4) >> (3 * BITS_PER_UCHAR)) & BYTEMASK]; \ newsum = tmpsum ? tmpsum : INIT_CHECKSUM_SEED; \ } MBEND #else #define HIBYTE 3 #define ADJUST_CHECKSUM(cursum, num4, newsum) \ MBSTART { \ uint4 tmpsum = csum_table[HIBYTE - 0][(cursum ^ num4) & BYTEMASK] ^ \ csum_table[HIBYTE - 1][((cursum ^ num4) >> (1 * BITS_PER_UCHAR)) & BYTEMASK] ^ \ csum_table[HIBYTE - 2][((cursum ^ num4) >> (2 * BITS_PER_UCHAR)) & BYTEMASK] ^ \ csum_table[HIBYTE - 3][((cursum ^ num4) >> (3 * BITS_PER_UCHAR)) & BYTEMASK]; \ newsum = tmpsum ? tmpsum : INIT_CHECKSUM_SEED; \ } MBEND #endif #define ADJUST_CHECKSUM_TN(cursum, tn, newsum) \ MBSTART { \ uint4 tmpsum_tn; \ ADJUST_CHECKSUM(cursum, *(uint4 *)tn, tmpsum_tn); \ ADJUST_CHECKSUM(tmpsum_tn, *(uint4 *)((char *)tn + SIZEOF(uint4)), newsum); \ } MBEND #define COMPUTE_COMMON_CHECKSUM(common_cksum, prefix) \ MBSTART { \ ADJUST_CHECKSUM_TN(INIT_CHECKSUM_SEED, &(prefix.tn), common_cksum); \ ADJUST_CHECKSUM(common_cksum, prefix.pini_addr, common_cksum); \ ADJUST_CHECKSUM(common_cksum, prefix.time, common_cksum); \ } MBEND #define COMPUTE_PBLK_CHECKSUM(blk_checksum, pblk_rec, common_cksum, jrec_checksum) \ MBSTART { \ ADJUST_CHECKSUM(blk_checksum, (pblk_rec)->prefix.jrec_type, jrec_checksum); \ ADJUST_CHECKSUM(jrec_checksum, (pblk_rec)->blknum, jrec_checksum); \ ADJUST_CHECKSUM(jrec_checksum, (pblk_rec)->bsiz, jrec_checksum); \ ADJUST_CHECKSUM(jrec_checksum, (pblk_rec)->ondsk_blkver, jrec_checksum); \ ADJUST_CHECKSUM(jrec_checksum, common_cksum, jrec_checksum); \ } MBEND #define COMPUTE_AIMG_CHECKSUM(blk_checksum, aimg_rec, common_cksum, jrec_checksum) \ COMPUTE_PBLK_CHECKSUM(blk_checksum, aimg_rec, common_cksum, jrec_checksum); #define COMPUTE_LOGICAL_REC_CHECKSUM(jfb_checksum, jrec, common_cksum, jrec_checksum) \ MBSTART { \ ADJUST_CHECKSUM(jfb_checksum, (jrec)->prefix.jrec_type, jrec_checksum); \ ADJUST_CHECKSUM(jrec_checksum, (jrec)->update_num, jrec_checksum); \ ADJUST_CHECKSUM(jrec_checksum, (jrec)->token_seq.token, jrec_checksum); \ ADJUST_CHECKSUM(jrec_checksum, (jrec)->strm_seqno, jrec_checksum); \ ADJUST_CHECKSUM(jrec_checksum, (jrec)->num_participants, jrec_checksum); \ ADJUST_CHECKSUM(jrec_checksum, common_cksum, jrec_checksum); \ } MBEND /* This macro is to be used whenever we are computing the checksum of a block that has been acquired. */ #define JNL_GET_CHECKSUM_ACQUIRED_BLK(cse, csd, csa, old_blk, bsize) \ MBSTART { \ cache_rec_ptr_t cr; \ boolean_t cr_is_null; \ \ GBLREF uint4 dollar_tlevel; \ \ /* Record current database tn before computing checksum of acquired block. This is used \ * later by the commit logic to determine if the block contents have changed (and hence \ * if recomputation of checksum is necessary). For BG, we have two-phase commit where \ * phase2 is done outside of crit. So it is possible that we note down the current database \ * tn and then compute checksums outside of crit and then get crit and yet in the validation \ * logic find that the block header tn is LESS than the noted dbtn (even though the block \ * contents changed after the noted dbtn). This will cause us to falsely validate this block \ * as not needing checksum recomputation. To ensure the checksum is recomputed inside crit, \ * we note down a tn of 0 in case the block is locked for update (cr->in_tend is non-zero). \ */ \ assert((gds_t_acquired == cse->mode) || (gds_t_create == cse->mode) \ || (gds_t_recycled2free == cse->mode)); \ assert(cse->old_block == (sm_uc_ptr_t)(old_blk)); \ assert((bsize) <= csd->blk_size); \ /* Since this macro is invoked only in case of before-image journaling and since MM does not \ * support before-image journaling, we can safely assert that BG is the only access method. \ */ \ assert(dba_bg == csd->acc_meth); \ /* In rare cases cse->cr can be NULL even though this block is an acquired block. This is \ * possible if we are in TP and this block was part of the tree in the initial phase of the \ * transaction but was marked free (by another process concurrently) in the later phase of \ * the same TP transaction. But this case is a sure restart situation, so be safe and \ * ensure recomputation happens inside of crit just in case we do not restart. Also add \ * asserts (using donot_commit variable) to ensure we do restart this transaction. \ */ \ cr = cse->cr; \ cr_is_null = (NULL == cr); \ assert(!cr_is_null || dollar_tlevel); \ DEBUG_ONLY(if (cr_is_null) TREF(donot_commit) |= DONOTCOMMIT_JNLGETCHECKSUM_NULL_CR;) \ cse->tn = ((cr_is_null || cr->in_tend) ? 0 : csd->trans_hist.curr_tn); \ /* If cr is NULL, it is a restartable situation, so do not waste time computing checksums. \ * If the db is encrypted, we cannot get at the encryption global buffer (jnl_get_checksum \ * requires this) since we do not even have a regular global buffer corresponding to this \ * block, so there is no way jnl_get_checksum can proceed in that case. Hence it is actually \ * necessary to avoid computing checksums if cr is NULL. \ */ \ cse->blk_checksum = !cr_is_null ? jnl_get_checksum((blk_hdr_ptr_t)old_blk, csa, (bsize)) : 0; \ } MBEND #include "gdsblk.h" uint4 jnl_get_checksum(blk_hdr_ptr_t buff, sgmnt_addrs *csa, int bufflen); uint4 compute_checksum(uint4 init_sum, unsigned char *buff, int bufflen); #endif fis-gtm-V7.0-005/sr_port/jnl_phase2_cleanup.c0000644000032200000250000001467314342376331017756 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2020 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "jnl.h" #include "memcoherency.h" #include "interlock.h" #include "is_proc_alive.h" #include "gdsbgtr.h" GBLREF uint4 process_id; #define PHASE2_COMMIT_ARRAY_ITERATE_UNTIL_WRITE_COMPLETE_FALSE(csa, phs2cmt, maxCmt) \ MBSTART { \ assert(phs2cmt < maxCmt); \ do \ { \ if (!phs2cmt->write_complete) \ break; \ phs2cmt++; \ if (phs2cmt == maxCmt) \ break; \ } while (TRUE); \ } MBEND /* The below code is very similar to "repl_phase2_cleanup" */ void jnl_phase2_cleanup(sgmnt_addrs *csa, jnl_buffer_ptr_t jbp) { uint4 currFreeaddr, newFreeaddr, stuckPid; int index1, index2; jbuf_phase2_in_prog_t *phs2cmt, *deadCmt, *begCmt, *maxCmt, *topCmt; boolean_t was_latch_owner; DEBUG_ONLY(uint4 jbp_freeaddr1;) /* It is possible we already own the latch in case we are in timer-interrupt or process-exit code hence the below check */ was_latch_owner = GLOBAL_LATCH_HELD_BY_US(&jbp->phase2_commit_latch); if (!was_latch_owner) { /* Return value of "grab_latch" does not need to be checked because we pass GRAB_LATCH_INDEFINITE_WAIT as timeout */ grab_latch(&jbp->phase2_commit_latch, GRAB_LATCH_INDEFINITE_WAIT, WS_28, csa); } index1 = jbp->phase2_commit_index1; ASSERT_JNL_PHASE2_COMMIT_INDEX_IS_VALID(index1, JNL_PHASE2_COMMIT_ARRAY_SIZE); index2 = jbp->phase2_commit_index2; SHM_READ_MEMORY_BARRIER; /* see corresponding SHM_WRITE_MEMORY_BARRIER in UPDATE_JBP_RSRV_FREEADDR macro */ ASSERT_JNL_PHASE2_COMMIT_INDEX_IS_VALID(index2, JNL_PHASE2_COMMIT_ARRAY_SIZE); if (index1 != index2) { DEBUG_ONLY(jbp_freeaddr1 = jbp->freeaddr); /* note down to help with assert failure in SET_JBP_FREEADDR below */ phs2cmt = &jbp->phase2_commit_array[index1]; if (!phs2cmt->write_complete && csa->now_crit) { /* "jnl_phase2_write" not complete in earliest phase2 entry. Since that is blocking us in crit, * check if the pid is alive. If not, cleanup. If you find the first blocking pid is dead, check * the next few pids too until an alive pid or phase2-complete entry shows up. */ deadCmt = phs2cmt; assert((deadCmt->start_freeaddr <= jbp->freeaddr) && ((deadCmt->start_freeaddr + deadCmt->tot_jrec_len) > jbp->freeaddr)); if (index1 < index2) maxCmt = &jbp->phase2_commit_array[index2]; else maxCmt = &jbp->phase2_commit_array[JNL_PHASE2_COMMIT_ARRAY_SIZE]; assert(deadCmt < maxCmt); do { if (deadCmt->write_complete) break; stuckPid = deadCmt->process_id; /* Note that we can reach here with stuckPid == process_id. An example call graph follows * t_end -> jnl_write_phase2 -> jnl_write_pblk -> jnl_write -> jnl_write_attempt * -> jnl_phase2_cleanup * Therefore "break" in that case. */ if (stuckPid == process_id) break; BG_TRACE_PRO_ANY(csa, jnlbuff_phs2cmt_isprcalv); if (is_proc_alive(stuckPid, 0)) break; jnl_phase2_salvage(csa, jbp, deadCmt); assert(deadCmt->write_complete); } while (++deadCmt < maxCmt); /* If "(deadCmt == maxCmt) && (index < index2)", we do not wrap around and search for dead pids in * the beginning of the array. Instead the next call to "jnl_phase2_cleanup" will take care of * cleaning that section IF there are any deadCmt entries. */ } if (phs2cmt->write_complete) { /* There is at least one phase2 that is complete and can be cleaned up. * Note that jbp->freeaddr will mostly be equal to phs2cmt->start_freeaddr but in rare cases * it can be greater. This is possible for example if a TP transaction has total jnl records * written that is greater than the jnlbuffer size. In that case, jb->freeaddr is updated as * each jnlbuffer size is filled even though all jnl records of the transaction are not yet written. */ assert(jbp->freeaddr >= phs2cmt->start_freeaddr); begCmt = &jbp->phase2_commit_array[0]; if (index1 < index2) { /* Easier case. No wrapping needed. */ maxCmt = &jbp->phase2_commit_array[index2]; assert(begCmt < maxCmt); PHASE2_COMMIT_ARRAY_ITERATE_UNTIL_WRITE_COMPLETE_FALSE(csa, phs2cmt, maxCmt); index1 = phs2cmt - begCmt; } else { maxCmt = topCmt = &jbp->phase2_commit_array[JNL_PHASE2_COMMIT_ARRAY_SIZE]; PHASE2_COMMIT_ARRAY_ITERATE_UNTIL_WRITE_COMPLETE_FALSE(csa, phs2cmt, maxCmt); if (phs2cmt == maxCmt) { /* Wrap around and search again */ if (index2) { phs2cmt = begCmt; maxCmt = &jbp->phase2_commit_array[index2]; PHASE2_COMMIT_ARRAY_ITERATE_UNTIL_WRITE_COMPLETE_FALSE(csa, phs2cmt, maxCmt); } index1 = ((phs2cmt == maxCmt) ? index2 : (phs2cmt - begCmt)); } else index1 = phs2cmt - begCmt; } if (phs2cmt < maxCmt) newFreeaddr = phs2cmt->start_freeaddr; else { assert(phs2cmt == maxCmt); if (phs2cmt == begCmt) { /* i.e. phs2cmt == begCmt == maxCmt. That is, begCmt and maxCmt are equal. * Because of the "assert(begCmt < maxCmt)" assert in the "if (index1 < index2)" block * above, we are guaranteed, we did not go down that code-block. In the else block, * we did initialize topCmt so we can safely use that here. */ phs2cmt = topCmt; } phs2cmt--; newFreeaddr = phs2cmt->start_freeaddr + phs2cmt->tot_jrec_len; } assert(!csa->now_crit || (index1 == index2) || (newFreeaddr == jbp->phase2_commit_array[index1].start_freeaddr)); SET_JBP_FREEADDR(jbp, newFreeaddr); ASSERT_JNL_PHASE2_COMMIT_INDEX_IS_VALID(index1, JNL_PHASE2_COMMIT_ARRAY_SIZE); SHM_WRITE_MEMORY_BARRIER; /* Update phase2_commit_index1 after memory barrier since * jnl_write_attempt relies on jbp->freeaddr & jbp->free being * reliable once phase2_commit_index1 is updated. */ jbp->phase2_commit_index1 = index1; } } if (!was_latch_owner) rel_latch(&jbp->phase2_commit_latch); } fis-gtm-V7.0-005/sr_port/jnl_phase2_salvage.c0000644000032200000250000002316614342376331017746 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2022 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "jnl.h" #include "jnl_get_checksum.h" #include "jnl_write.h" #include "send_msg.h" #ifdef DEBUG #include "is_proc_alive.h" #endif error_def(ERR_JNLBUFFPHS2SALVAGE); /* The below code is very similar to "repl_phase2_salvage" */ /* This function is invoked when we find a process that had reserved space in the journal buffers for its journal records * died before it finished its transaction commit in phase2 (outside crit). Since many other reservations could have happened * after that and those could correspond to live processes, we now have a situation where there is a hole/gap in the journal * file. The below function patches those holes by filling them with JRT_NULL/JRT_INCTN/JRT_ALIGN records to indicate some * transaction happened. This keeps the jnl_seqno/curr_tn contiguity necessary for journal recovery, replication etc. * The actual transaction's journal records are lost permanently but that is okay since the process had not touched the * database for that commit (it would have started phase2 of db commit only after all phase2 jnl records were written to * the journal buffer and got shot before that finished). */ void jnl_phase2_salvage(sgmnt_addrs *csa, jnl_buffer_ptr_t jbp, jbuf_phase2_in_prog_t *deadCmt) { boolean_t write_null_record; uint4 next_align_addr, start_freeaddr, end_freeaddr, save_phase2_freeaddr; uint4 alignsize, pini_addr, rlen, tot_jrec_len; jnl_record jrec; jnl_private_control *jpc; assert(!is_proc_alive(deadCmt->process_id, 0)); if (!deadCmt->in_phase2) { /* A process in "jnl_write" got killed after it did the UPDATE_JBP_RSRV_FREEADDR call but before it did * the JNL_PHASE2_WRITE_COMPLETE call. The corresponding journal record was already written to the journal * buffer before the UPDATE_JBP_RSRV_FREEADDR call so all that is needed is to signal the phase2 as complete. * We cannot call the JNL_PHASE2_WRITE_COMPLETE macro (which does this signaling) directly as it will fail asserts. * Hence do its equivalent before returning right away. */ deadCmt->write_complete = TRUE; return; } /* This commit entry was added by UPDATE_JRS_RSRV_FREEADDR in t_end/tp_tend. And so we are guaranteed that the journal * records added are not PFIN or EOF which have sizes of 0x20 and 0x28 respectively. This means the minimum jnl rec len * of this transaction is guaranteed to be 0x30 which is the length of a JRT_NULL record. And so it is possible for us * to replace this transaction's journal records with a NULL record (if replication is turned on) OR with an INCTN * journal record (if replication is turned OFF). And pad the rest of the space with JRT_ALIGN records as needed. * * Below is an explanation of why we can always replace an existing transaction's journal records with a sequence of * JRT_NULL/JRT_INCTN and one or more JRT_ALIGN records. * * The minimum size of a JRT_SET/JRT_KILL etc. (any logical update record) is 0x40. So the minimum size of the total * journal record size of the transaction is at least 0x40. * (a) If replicating, we can then replace this with a JRT_NULL record (size = 0x30) + JRT_ALIGN (size = 0x10, which is * the minimum align record size and so 0-byte padding). If the transaction journal record size is > 0x40, we can * add enough padding in the JRT_ALIGN and add a few more of JRT_ALIGN records as required if the transaction * spans multiple align boundaries). In this case, the JRT_ALIGN is overloaded to also act as a filler journal record. * (b) If not replicating, we can then replace this with a JRT_INCTN record (size = 0x28) + JRT_ALIGN (size = 0x18, where * the align record has 8-byte of padding). If > 0x40, then add more padding just like case (a) above. * It is possible the transaction is not a logical one (e.g. inctn due to file extension, 2nd phase of kill, reorg etc.). * In that case, the transaction journal size would be just a JRT_INCTN (0x28) but in that case we should be * able to replace it with another JRT_INCTN instead. * This is asserted below and in "UPDATE_JBP_RSRV_FREEADDR" macro. */ tot_jrec_len = deadCmt->tot_jrec_len; assert((!deadCmt->replication && (INCTN_RECLEN == tot_jrec_len)) || (deadCmt->replication && (NULL_RECLEN == tot_jrec_len)) || ((tot_jrec_len >= (NULL_RECLEN + MIN_ALIGN_RECLEN)) && (tot_jrec_len >= (INCTN_RECLEN + MIN_ALIGN_RECLEN)))); start_freeaddr = deadCmt->start_freeaddr; send_msg_csa(CSA_ARG(csa) VARLSTCNT(9) ERR_JNLBUFFPHS2SALVAGE, 7, deadCmt->process_id, DB_LEN_STR(csa->region), &deadCmt->curr_tn, &deadCmt->jnl_seqno, start_freeaddr, tot_jrec_len); alignsize = jbp->alignsize; /* Use "gtm_uint64_t" typecast below to avoid 4G overflow issues with the ROUND_UP2 */ next_align_addr = (uint4)(ROUND_UP2((gtm_uint64_t)start_freeaddr, alignsize) - MIN_ALIGN_RECLEN); assert(start_freeaddr <= next_align_addr); jpc = csa->jnl; /* Temporarily adjust jpc->phase2_freeaddr to reflect the state of the dead pid so we write jnl records where it * would have written in the jnl buffer or jnl file. */ save_phase2_freeaddr = jpc->phase2_freeaddr; assert(jpc->phase2_free == jpc->phase2_freeaddr % jbp->size); assert(!jpc->in_jnl_phase2_salvage); jpc->in_jnl_phase2_salvage = TRUE; SET_JPC_PHASE2_FREEADDR(jpc, jbp, start_freeaddr); /* needed by "jnl_write_align_rec" etc. calls below */ end_freeaddr = start_freeaddr + tot_jrec_len; write_null_record = REPL_ALLOWED(csa) && deadCmt->replication; /* see comment in jnl.h (jbuf_phase2_in_prog_t.replication) * for why it is necessary to decide JRT_NULL vs JRT_INCTN. */ assert(!write_null_record || deadCmt->jnl_seqno); rlen = write_null_record ? NULL_RECLEN : INCTN_RECLEN; pini_addr = deadCmt->pini_addr; if (!pini_addr) pini_addr = JNL_FILE_FIRST_RECORD; if ((start_freeaddr + rlen) > next_align_addr) { /* Write an ALIGN record first */ jnl_write_align_rec(csa, next_align_addr - start_freeaddr, deadCmt->jrec_time); DEBUG_ONLY(start_freeaddr = jpc->phase2_freeaddr;) assert(0 == (start_freeaddr % alignsize)); assert(start_freeaddr == (next_align_addr + MIN_ALIGN_RECLEN)); next_align_addr += alignsize; assert(start_freeaddr < next_align_addr); assert(start_freeaddr <= end_freeaddr); } assert(start_freeaddr + rlen <= end_freeaddr); if (write_null_record) { /* Write JRT_NULL record */ assert(NULL_RECLEN == rlen); jrec.jrec_null.prefix.jrec_type = JRT_NULL; jrec.jrec_null.prefix.forwptr = NULL_RECLEN; jrec.jrec_null.prefix.pini_addr = pini_addr; jrec.jrec_null.prefix.time = deadCmt->jrec_time; jrec.jrec_null.prefix.checksum = INIT_CHECKSUM_SEED; jrec.jrec_null.prefix.tn = deadCmt->curr_tn; assert(deadCmt->jnl_seqno); jrec.jrec_null.jnl_seqno = deadCmt->jnl_seqno; jrec.jrec_null.strm_seqno = deadCmt->strm_seqno; jrec.jrec_null.filler = 0; jrec.jrec_null.suffix.backptr = NULL_RECLEN; jrec.jrec_null.suffix.suffix_code = JNL_REC_SUFFIX_CODE; jrec.jrec_null.prefix.checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)&jrec, NULL_RECLEN); } else { /* Write JRT_INCTN record */ jrec.jrec_inctn.prefix.jrec_type = JRT_INCTN; jrec.jrec_inctn.prefix.forwptr = INCTN_RECLEN; jrec.jrec_inctn.prefix.pini_addr = pini_addr; jrec.jrec_inctn.prefix.time = deadCmt->jrec_time; jrec.jrec_inctn.prefix.checksum = INIT_CHECKSUM_SEED; jrec.jrec_inctn.prefix.tn = deadCmt->curr_tn; jrec.jrec_inctn.detail.blknum_struct.opcode = inctn_jnlphase2salvage; jrec.jrec_inctn.detail.blknum_struct.filler_short = 0; jrec.jrec_inctn.detail.blknum_struct.suffix.backptr = INCTN_RECLEN; jrec.jrec_inctn.detail.blknum_struct.suffix.suffix_code = JNL_REC_SUFFIX_CODE; jrec.jrec_inctn.prefix.checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)&jrec, INCTN_RECLEN); deadCmt->jnl_seqno = 0; /* for the send_msg_csa call below */ } jnl_write(jpc, jrec.prefix.jrec_type, &jrec, NULL); assert(start_freeaddr + rlen == jpc->phase2_freeaddr); while (next_align_addr < end_freeaddr) { /* Write one or more JRT_ALIGN records to fill up one "alignsize" space. Note that one JRT_ALIGN record * size cannot exceed jbp->max_jrec_len which could be way less than the current "alignsize" and hence * the call to "jnl_write_multi_align_rec" instead of "jnl_write_align_rec" directly. */ jnl_write_multi_align_rec(csa, next_align_addr - jpc->phase2_freeaddr, deadCmt->jrec_time); assert(0 == (jpc->phase2_freeaddr % alignsize)); assert(jpc->phase2_freeaddr == (next_align_addr + MIN_ALIGN_RECLEN)); next_align_addr += alignsize; assert(start_freeaddr <= end_freeaddr); } if (end_freeaddr != jpc->phase2_freeaddr) { assert((end_freeaddr - jpc->phase2_freeaddr) >= MIN_ALIGN_RECLEN); /* Write one last set of JRT_ALIGN record(s) */ jnl_write_multi_align_rec(csa, end_freeaddr - jpc->phase2_freeaddr - MIN_ALIGN_RECLEN, deadCmt->jrec_time); assert(jpc->phase2_freeaddr == end_freeaddr); } jpc->in_jnl_phase2_salvage = FALSE; SET_JPC_PHASE2_FREEADDR(jpc, jbp, save_phase2_freeaddr); /* End simulation of dead pid jnl writing */ deadCmt->write_complete = TRUE; /* signal dead pid's jnl write is now complete */ } fis-gtm-V7.0-005/sr_port/jnl_pool_write.c0000644000032200000250000001557114342376331017246 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2007-2020 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_string.h" #include "gtm_inet.h" #include /* for offsetof() macro */ #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "ccp.h" #include "iosp.h" #include "jnl.h" #include "repl_msg.h" #include "gtmsource.h" #include "min_max.h" #include "sleep_cnt.h" #include "jnl_write.h" #include "copy.h" #include "sleep.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnl_gbls_t jgbl; GBLREF boolean_t is_replicator; /* This function writes the journal records ONLY TO the journal pool. * * csa : sgmnt_addrs corresponding to region * rectype : Record type * jnl_rec : This contains fixed part of a variable size record or the complete fixed size records. * jfb : For SET/KILL/ZKILL records entire record is formatted in this. */ void jnl_pool_write(sgmnt_addrs *csa, enum jnl_record_type rectype, jnl_record *jnl_rec, jnl_format_buffer *jfb) { boolean_t pool_overflow; int max_iters, num_iters, num_participants; gtm_uint64_t dstlen, rlen; gtm_uint64_t jnlpool_size, tot_jrec_len; uchar_ptr_t jnlrecptr; jnlpool_addrs_ptr_t local_jnlpool; jnlpool_ctl_ptr_t jctl; jpl_rsrv_struct_t *jrs; gtm_uint64_t write, write_total; qw_off_t cur_write_addr, end_write_addr; gtm_int64_t wait_write_addr; /* needed signed because of subtraction happening below */ assert(is_replicator); assert(NULL != csa); local_jnlpool = JNLPOOL_FROM(csa); assert(NULL != local_jnlpool); assert(NULL != jnl_rec); assert(IS_VALID_RECTYPES_RANGE(rectype) && IS_REPLICATED(rectype)); assert(JNL_ENABLED(csa) || REPL_WAS_ENABLED(csa)); jctl = local_jnlpool->jnlpool_ctl; assert(NULL != jctl); /* ensure we haven't yet detached from the jnlpool */ jrs = &local_jnlpool->jrs; jnlpool_size = jctl->jnlpool_size; tot_jrec_len = jrs->tot_jrec_len; DEBUG_ONLY(jgbl.cu_jnl_index++;) if (JRT_TCOM == rectype) { /* If this is a TCOM record, check if this is a multi-region TP transaction. In that case, tp_tend would call * "jnl_pool_write" with a sequence say TSET1, TCOM1, TSET2, TCOM2 (where 1 implies REG1, 2 implies REG2). * But we cannot write this sequence into the journal pool as replication ("repl_sort_tr_buff" etc.) relies on * all TCOMs coming at the end, i.e. the desired order is TSET1, TSET2, TCOM1, TCOM2. So adjust that by noting * down if this TCOM is not the last one and if so skip writing it to the jnlpool and write it only when it is. * Thankfully it is okay to write N copies of the last TCOM record to correspond to each of the N regions since * replication does not care about region-specific information in the TCOM record (e.g. checksum, pini_addr etc.). * All replication cares about is the time, seqno etc. which is all common across all the regions. */ num_participants = jnl_rec->jrec_tcom.num_participants; assert(jrs->num_tcoms < num_participants); if (++jrs->num_tcoms != num_participants) return; max_iters = num_participants; /* write one TCOM record per region in a loop */ } else max_iters = 1; rlen = jnl_rec->prefix.forwptr; assert(0 == rlen % JNL_REC_START_BNDRY); assert((rlen + SIZEOF(jnldata_hdr_struct)) <= tot_jrec_len); pool_overflow = (tot_jrec_len > jnlpool_size); write_total = jrs->write_total; cur_write_addr = jrs->cur_write_addr; for (num_iters = 0; num_iters < max_iters; num_iters++) { write_total += rlen; if (write_total > tot_jrec_len) { /* "tot_jrec_len" (computed in phase1) becomes lesser than "write_total" (computed in phase2). * There is not enough reserved space in the jnlpool to write the transaction's journal records. * Skip writing any more records in the reserved space. A later call to JPL_PHASE2_WRITE_COMPLETE * will know this happened by checking jrs->write_total and will take appropriate action. * But continue "write_total" accumulation (used at end to set jrs->write_total) hence * the "continue" below instead of a "break". */ assert(FALSE); continue; } assert(cur_write_addr >= jctl->write_addr); end_write_addr = cur_write_addr + rlen; assert(end_write_addr <= jctl->rsrv_write_addr); /* If we cannot fit in this whole transaction in the journal pool, source server will anyways read this * transaction from the journal files. So skip the memcpy onto the jnlpool in the interest of time. */ if (!pool_overflow) { assert(!jrs->memcpy_skipped); /* Wait for jctl->write_addr to be high so we can go ahead with write * without overflowing/underflowing the pool. */ wait_write_addr = (gtm_int64_t)end_write_addr - jnlpool_size; while ((gtm_int64_t)jctl->write_addr < wait_write_addr) { JPL_TRACE_PRO(jctl, jnl_pool_write_sleep); SLEEP_USEC(1, FALSE); /* TODO: Need to handle case of too-many "repl_phase2_cleanup" (and hence "is_proc_alive") * calls by concurrent processes at same time. If we see a lot of the "jnl_pool_write_sleep" * counter value then this will become a priority. For now we expect that counter to be ~ 0. */ repl_phase2_cleanup(local_jnlpool); } /* If the database is encrypted, then at this point jfb->buff will contain encrypted * data which we don't want to to push into the jnlpool. Instead, we make use of the * alternate alt_buff which is guaranteed to contain the original unencrypted data. */ if (jrt_fixed_size[rectype]) jnlrecptr = (uchar_ptr_t)jnl_rec; else if (IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(rectype) && USES_ANY_KEY(csa->hdr)) jnlrecptr = (uchar_ptr_t)jfb->alt_buff; else jnlrecptr = (uchar_ptr_t)jfb->buff; write = cur_write_addr % jnlpool_size; dstlen = jnlpool_size - write; assert(rlen < jnlpool_size); /* Because of "if (tot_jrec_len <= jnlpool_size)" above */ /* Inspite of the above assert, do a "rlen < jnlpool_size" check below in pro to be safe */ if (rlen <= dstlen) /* dstlen & srclen >= rlen (most frequent case) */ memcpy(local_jnlpool->jnldata_base + write, jnlrecptr, rlen); else if (rlen < jnlpool_size) /* dstlen < rlen <= jnlpool_size */ { memcpy(local_jnlpool->jnldata_base + write, jnlrecptr, dstlen); memcpy(local_jnlpool->jnldata_base, jnlrecptr + dstlen, rlen - dstlen); } } else jrs->memcpy_skipped = TRUE; cur_write_addr = end_write_addr; } assert(end_write_addr > jrs->cur_write_addr); jrs->cur_write_addr = end_write_addr; jrs->write_total = write_total; return; } fis-gtm-V7.0-005/sr_port/jnl_rec_table.h0000755000032200000250000001633614342376331017013 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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. * * * ****************************************************************/ /* New entries should be added at the end to maintain backward compatibility with previous journal files */ /* Note: This is an exception where we have 132+ characters in a line. It is needed so that from a * particular number we can find record type. */ /* JNL_TABLE_ENTRY (rectype, extract_rtn, label, update, fixed_size, is_replicated) */ JNL_TABLE_ENTRY (JRT_BAD, NULL, "*BAD* ", NA, FALSE, FALSE) /* 0: Catch-all for invalid record types (must be first) BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_PINI, mur_extract_pini, "PINI ", NA, TRUE, FALSE) /* 1: Process initialization */ JNL_TABLE_ENTRY (JRT_PFIN, mur_extract_pfin, "PFIN ", NA, TRUE, FALSE) /* 2: Process termination */ JNL_TABLE_ENTRY (JRT_ZTCOM, mur_extract_tcom, "ZTCOM ", ZTCOMREC, TRUE, FALSE) /* 3: End of "fenced" transaction */ JNL_TABLE_ENTRY (JRT_KILL, mur_extract_set, "KILL ", KILLREC, FALSE, TRUE) /* 4: After-image logical journal transaction BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_FKILL, mur_extract_set, "FKILL ", KILLREC|FUPDREC, FALSE, FALSE) /* 5: Like KILL, but the first in a "fenced" transaction BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_GKILL, mur_extract_set, "GKILL ", KILLREC|GUPDREC, FALSE, FALSE) /* 6: Like FKILL, but not the first */ JNL_TABLE_ENTRY (JRT_SET, mur_extract_set, "SET ", SETREC, FALSE, TRUE) /* 7: After-image logical journal transaction BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_FSET, mur_extract_set, "FSET ", SETREC|FUPDREC, FALSE, FALSE) /* 8: Like SET, but the first in a "fenced" transaction BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_GSET, mur_extract_set, "GSET ", SETREC|GUPDREC, FALSE, FALSE) /* 9: Like FSET, but not the first */ JNL_TABLE_ENTRY (JRT_PBLK, mur_extract_blk, "PBLK ", NA, FALSE, FALSE) /* 10: Before-image physical journal transaction BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_EPOCH, mur_extract_epoch, "EPOCH ", NA, TRUE, FALSE) /* 11: A "new epoch" */ JNL_TABLE_ENTRY (JRT_EOF, mur_extract_eof, "EOF ", NA, TRUE, FALSE) /* 12: End of file */ JNL_TABLE_ENTRY (JRT_TKILL, mur_extract_set, "TKILL ", KILLREC|TUPDREC, FALSE, TRUE) /* 13: Like KILL, but the first in a TP transaction BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_UKILL, mur_extract_set, "UKILL ", KILLREC|UUPDREC, FALSE, TRUE) /* 14: Like TKILL, but not the first BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_TSET, mur_extract_set, "TSET ", SETREC|TUPDREC, FALSE, TRUE) /* 15: Like SET, but the first in a TP transaction BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_USET, mur_extract_set, "USET ", SETREC|UUPDREC, FALSE, TRUE) /* 16: Like TSET, but not the first */ JNL_TABLE_ENTRY (JRT_TCOM, mur_extract_tcom, "TCOM ", TCOMREC, TRUE, TRUE) /* 17: End of TP transaction */ JNL_TABLE_ENTRY (JRT_ALIGN, mur_extract_align, "ALIGN ", NA, FALSE, FALSE) /* 18: Align record */ JNL_TABLE_ENTRY (JRT_NULL, mur_extract_null, "NULL ", NA, TRUE, TRUE) /* 19: Null record */ JNL_TABLE_ENTRY (JRT_ZKILL, mur_extract_set, "ZKILL ", ZKILLREC, FALSE, TRUE) /* 20: After-image logical journal transaction BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_FZKILL, mur_extract_set, "FZKILL ", ZKILLREC|FUPDREC, FALSE, FALSE) /* 21: Like ZKILL, but the first in a "fenced" transaction BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_GZKILL, mur_extract_set, "GZKILL ", ZKILLREC|GUPDREC, FALSE, FALSE) /* 22: Like FZKILL, but not the first BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_TZKILL, mur_extract_set, "TZKILL ", ZKILLREC|TUPDREC, FALSE, TRUE) /* 23: Like ZKILL, but the first in a TP transaction BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_UZKILL, mur_extract_set, "UZKILL ", ZKILLREC|UUPDREC, FALSE, TRUE) /* 24: Like TZKILL, but not the first BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_INCTN, mur_extract_inctn, "INCTN ", NA, TRUE, FALSE) /* 25: Increment curr_tn only, no logical update BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_AIMG, mur_extract_blk, "AIMG ", NA, FALSE, FALSE) /* 26: After-image physical journal transaction BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_TRIPLE, NULL, "TRIPLE ", NA, TRUE, TRUE) /* 27: A REPL_OLD_TRIPLE message minus the 8-byte message BYPASSOK (line length) * header ("type" and "len"). Only used in the BYPASSOK (line length) * replication pipe. Never part of a journal file. BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_TZTWORM, mur_extract_set, "TZTWORM", ZTWORMREC|TUPDREC, FALSE, TRUE) /* 28: If $ZTWORMHOLE is first record in TP BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_UZTWORM, mur_extract_set, "UZTWORM", ZTWORMREC|UUPDREC, FALSE, TRUE) /* 29: Like TZTWORM but not the first record in TP BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_TZTRIG, mur_extract_set, "TZTRIG ", ZTRIGREC|TUPDREC, FALSE, TRUE) /* 30: If ZTRIGGER is first record in TP BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_UZTRIG, mur_extract_set, "UZTRIG ", ZTRIGREC|UUPDREC, FALSE, TRUE) /* 31: Like TZTRIG but not the first record in TP BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_HISTREC, NULL, "HISTREC", NA, TRUE, TRUE) /* 32: A REPL_HISTREC message minus the 8-byte message BYPASSOK (line length) * header ("type" and "len"). Only used in the BYPASSOK (line length) * replication pipe. Never part of a journal file. BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_TRUNC, mur_extract_trunc, "TRUNC ", NA, TRUE, FALSE) /* 33: Record DB file truncate details BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_TLGTRIG, mur_extract_set, "TLGTRIG", LGTRIGREC|TUPDREC, FALSE, TRUE ) /* 34: A LoGical TRIGger record generated by a BYPASSOK (line length) * $ZTRIGGER or MUPIP TRIGGER action and is the BYPASSOK (line length) * first update in this region in a TP transaction BYPASSOK (line length) */ JNL_TABLE_ENTRY (JRT_ULGTRIG, mur_extract_set, "ULGTRIG", LGTRIGREC|UUPDREC, FALSE, TRUE ) /* 35: Just like TLGTRIG but is NOT the BYPASSOK (line length) * first update in this region in a TP transaction BYPASSOK (line length) */ fis-gtm-V7.0-005/sr_port/jnl_send_oper.c0000755000032200000250000000740614342376331017042 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "jnl.h" #include "error.h" #include "send_msg.h" #include "caller_id.h" #include "wbox_test_init.h" #define ENOSPC_LOGGING_PERIOD 100 /* every 100th ENOSPC error is logged to avoid flooding the operator log */ GBLREF boolean_t caller_id_flag; GBLREF uint4 process_id; error_def(ERR_CALLERID); error_def(ERR_JNLBUFINFO); error_def(ERR_JNLNOCREATE); error_def(ERR_JNLPVTINFO); error_def(ERR_JNLSENDOPER); void jnl_send_oper(jnl_private_control *jpc, uint4 status) { sgmnt_addrs *csa; sgmnt_data_ptr_t csd; jnl_buffer_ptr_t jb; uint4 now_writer, fsync_pid; int4 io_in_prog, fsync_in_prog; boolean_t ok_to_log; /* TRUE except when we avoid flooding operator log due to ENOSPC error */ switch(jpc->region->dyn.addr->acc_meth) { case dba_mm: case dba_bg: csa = &FILE_INFO(jpc->region)->s_addrs; break; default: assertpro(FALSE && jpc->region->dyn.addr->acc_meth); } csd = csa->hdr; jb = jpc->jnl_buff; assert((ENOSPC != jpc->status) || jb->enospc_errcnt || WBTEST_ENABLED(WBTEST_RECOVER_ENOSPC)); assert((SS_NORMAL == jpc->status) || (ENOSPC == jpc->status) || !jb->enospc_errcnt || (jb->enospc_errcnt && (ERR_JNLNOCREATE == jpc->status))); ok_to_log = (jb->enospc_errcnt ? (1 == (jb->enospc_errcnt % ENOSPC_LOGGING_PERIOD)) : TRUE); caller_id_flag = FALSE; if (ok_to_log) { SEND_CALLERID("jnl_send_oper()"); if (0 != status) { if (NULL != jpc->err_str) send_msg_csa(CSA_ARG(csa) VARLSTCNT(11) ERR_JNLSENDOPER, 5, process_id, status, jpc->status, jpc->status2, jb->iosb.cond, ERR_TEXT, 2, LEN_AND_STR(jpc->err_str)); else if (SS_NORMAL != jpc->status2) send_msg_csa(CSA_ARG(csa) VARLSTCNT(14) ERR_JNLSENDOPER, 5, process_id, status, jpc->status, jpc->status2, jb->iosb.cond, status, 2, JNL_LEN_STR(csd), jpc->status, 0, jpc->status2); else if (SS_NORMAL != jpc->status) send_msg_csa(CSA_ARG(csa) VARLSTCNT(12) ERR_JNLSENDOPER, 5, process_id, status, jpc->status, jpc->status2, jb->iosb.cond, status, 2, JNL_LEN_STR(csd), jpc->status); else send_msg_csa(CSA_ARG(csa) VARLSTCNT(11) ERR_JNLSENDOPER, 5, process_id, status, jpc->status, jpc->status2, jb->iosb.cond, status, 2, JNL_LEN_STR(csd)); } } jpc->status = SS_NORMAL; jpc->status2 = SS_NORMAL; io_in_prog = (jb->io_in_prog_latch.u.parts.latch_pid ? TRUE : FALSE); now_writer = jb->io_in_prog_latch.u.parts.latch_pid; fsync_in_prog = jb->fsync_in_prog_latch.u.parts.latch_pid ? TRUE : FALSE; fsync_pid = jb->fsync_in_prog_latch.u.parts.latch_pid; if (ok_to_log) { send_msg_csa(CSA_ARG(csa) VARLSTCNT(23) ERR_JNLBUFINFO, 21, process_id, jb->dsk, jb->free, jb->bytcnt, io_in_prog, fsync_in_prog, jb->dskaddr, jb->freeaddr, jb->qiocnt, now_writer, fsync_pid, jb->filesize, jb->cycle, jb->errcnt, jb->wrtsize, jb->fsync_dskaddr, jb->rsrv_free, jb->rsrv_freeaddr, jb->phase2_commit_index1, jb->phase2_commit_index2, jb->next_align_addr); send_msg_csa(CSA_ARG(csa) VARLSTCNT(10) ERR_JNLPVTINFO, 8, process_id, jpc->cycle, jpc->fd_mismatch, jpc->channel, jpc->sync_io, jpc->pini_addr, jpc->qio_active, jpc->old_channel); } caller_id_flag = TRUE; } fis-gtm-V7.0-005/sr_port/jnl_set_cur_prior.c0000644000032200000250000000522214342376331017732 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2017-2020 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 "gdsroot.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "gtmio.h" #include "anticipatory_freeze.h" #include "is_file_identical.h" /* For use when disabling journaling in standalone to mark the current journal file as being a prior journal file. * The journal file is not always present/usable/writable, in which case we can't change it, so ignore errors. */ void jnl_set_cur_prior(gd_region *reg, sgmnt_addrs *csa, sgmnt_data *csd) { int jnl_fd; unix_db_info *udi; udi = FILE_INFO(reg); assert('\0' == csd->jnl_file_name[csd->jnl_file_len]); assert(udi->grabbed_access_sem || (csa->now_crit && !csa->nl->jnl_file.u.inode)); OPENFILE((char *)csd->jnl_file_name, O_RDWR, jnl_fd); if (FD_INVALID != jnl_fd) jnl_set_fd_prior(jnl_fd, csa, csd, NULL); } /* Same as above, but for when we already have the file open. * If jfh is non-NULL, it is assumed correct, updated, and written; otherwise, a fresh copy is read from the file. */ void jnl_set_fd_prior(int jnl_fd, sgmnt_addrs* csa, sgmnt_data* csd, jnl_file_header *jfh) { int status1, status2; jnl_file_header header, *jfh_checked = NULL; if (NULL == jfh) { DO_FILE_READ(jnl_fd, 0, &header, SIZEOF(header), status1, status2); assert(SS_NORMAL == status1); if (SS_NORMAL == status1) { CHECK_JNL_FILE_IS_USABLE(&header, status1, FALSE, 0, NULL); if ((SS_NORMAL == status1) && !header.is_not_latest_jnl) jfh_checked = &header; } } else jfh_checked = jfh; /* Only do an update if we successfully read the journal header and the database file in the journal header matches * the current database file. A mismatch may occur when working with a database backup on the same machine as the * original database, as the backup will still point to the original journal file. */ if ((NULL != jfh_checked) && is_file_identical((char *)csa->region->dyn.addr->fname, (char *)jfh_checked->data_file_name)) { jfh_checked->is_not_latest_jnl = TRUE; JNL_DO_FILE_WRITE(csa, csd->jnl_file_name, jnl_fd, 0, jfh_checked, REAL_JNL_HDR_LEN, status1, status2); assert(SS_NORMAL == status1); } JNL_FD_CLOSE(jnl_fd, status1); } fis-gtm-V7.0-005/sr_port/jnl_typedef.h0000755000032200000250000001025414342376331016524 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2017 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. * * * ****************************************************************/ #ifndef JNL_TYPEDEF_H_INCLUDED #define JNL_TYPEDEF_H_INCLUDED #define TUPDREC 0x00000001 #define UUPDREC 0x00000002 #define TCOMREC 0x00000004 #define TPREC_MASK (TUPDREC | UUPDREC | TCOMREC) #define FUPDREC 0x00000010 #define GUPDREC 0x00000020 #define ZTCOMREC 0x00000040 #define ZTPREC_MASK (FUPDREC | GUPDREC | ZTCOMREC) #define NA 0 #define SETREC 0x00000100 #define KILLREC 0x00000200 #define ZKILLREC 0x00000400 #define ZTWORMREC 0x00000800 #define ZTRIGREC 0x00001000 #define LGTRIGREC 0x00002000 #define SET_KILL_ZKILL_MASK (SETREC | KILLREC | ZKILLREC) #define SET_KILL_ZKILL_ZTWORM_MASK (SETREC | KILLREC | ZKILLREC | ZTWORMREC) #define SET_KILL_ZKILL_ZTWORM_LGTRIG_MASK (SETREC | KILLREC | ZKILLREC | ZTWORMREC | LGTRIGREC) #define SET_KILL_ZKILL_ZTWORM_ZTRIG_MASK (SETREC | KILLREC | ZKILLREC | ZTWORMREC | ZTRIGREC) #define SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG_MASK (SETREC | KILLREC | ZKILLREC | ZTWORMREC | LGTRIGREC | ZTRIGREC) #define FENCE_MASK (TPREC_MASK | ZTPREC_MASK) /* the following LITREFs are needed by the below macros */ LITREF int jrt_update[JRT_RECTYPES]; LITREF boolean_t jrt_fixed_size[JRT_RECTYPES]; LITREF boolean_t jrt_is_replicated[JRT_RECTYPES]; #define IS_VALID_RECTYPES_RANGE(rectype) ((JRT_BAD < rectype) && (JRT_RECTYPES > rectype)) #define IS_REPLICATED(rectype) (jrt_is_replicated[rectype]) #define IS_FIXED_SIZE(rectype) (jrt_fixed_size[rectype]) #define IS_SET(rectype) (jrt_update[rectype] & SETREC) #define IS_KILL(rectype) (jrt_update[rectype] & KILLREC) #define IS_ZKILL(rectype) (jrt_update[rectype] & ZKILLREC) #define IS_ZTWORM(rectype) (jrt_update[rectype] & ZTWORMREC) #define IS_LGTRIG(rectype) (jrt_update[rectype] & LGTRIGREC) #define IS_ZTRIG(rectype) (jrt_update[rectype] & ZTRIGREC) #define IS_KILL_ZKILL(rectype) (jrt_update[rectype] & (KILLREC | ZKILLREC)) #define IS_KILL_ZKILL_ZTRIG(rectype) (jrt_update[rectype] & (KILLREC | ZKILLREC | ZTRIGREC)) #define IS_SET_KILL_ZKILL_ZTRIG(rectype) (jrt_update[rectype] & (SET_KILL_ZKILL_MASK | ZTRIGREC)) #define IS_SET_KILL_ZKILL_ZTWORM(rectype) (jrt_update[rectype] & SET_KILL_ZKILL_ZTWORM_MASK) #define IS_SET_KILL_ZKILL_ZTWORM_LGTRIG(rectype) (jrt_update[rectype] & SET_KILL_ZKILL_ZTWORM_LGTRIG_MASK) #define IS_SET_KILL_ZKILL_ZTWORM_ZTRIG(rectype) (jrt_update[rectype] & SET_KILL_ZKILL_ZTWORM_ZTRIG_MASK) #define IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(rectype) (jrt_update[rectype] & SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG_MASK) #define IS_FENCED(rectype) (jrt_update[rectype] & FENCE_MASK) #define IS_TP(rectype) (jrt_update[rectype] & TPREC_MASK) #define IS_ZTP(rectype) (jrt_update[rectype] & ZTPREC_MASK) #define IS_COM(rectype) (jrt_update[rectype] & (TCOMREC | ZTCOMREC)) #define IS_FUPD(rectype) (jrt_update[rectype] & FUPDREC) #define IS_GUPD(rectype) (jrt_update[rectype] & GUPDREC) #define IS_TUPD(rectype) (jrt_update[rectype] & TUPDREC) #define IS_UUPD(rectype) (jrt_update[rectype] & UUPDREC) #define IS_FUPD_TUPD(rectype) (jrt_update[rectype] & (FUPDREC | TUPDREC)) #define IS_GUPD_UUPD(rectype) (jrt_update[rectype] & (GUPDREC | UUPDREC)) #ifdef GTM_TRIGGER # define IS_VALID_JRECTYPE(rectype) IS_VALID_RECTYPES_RANGE(rectype) #else /* On trigger non-supporting platforms, it is an error if a ZTWORM or ZTRIG rectype is seen. */ # define IS_VALID_JRECTYPE(rectype) (IS_VALID_RECTYPES_RANGE(rectype) && !IS_ZTWORM(rectype) \ && !IS_LGTRIG(rectype) && !IS_ZTRIG(rectype)) #endif #define REC_HAS_TOKEN_SEQ(rectype) (IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(rectype) || IS_COM(rectype) \ || (JRT_EPOCH == (rectype)) || (JRT_EOF == (rectype)) \ || (JRT_NULL == (rectype))) #endif /* JNL_TYPEDEF_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/jnl_wait.c0000755000032200000250000000225314342376331016023 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2016 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 "gdsroot.h" #include "gtm_facility.h" #include "gdsbt.h" #include "fileinfo.h" #include "gdsfhead.h" #include "filestruct.h" #include "iosp.h" #include "jnl.h" #include "is_file_identical.h" void jnl_wait(gd_region *reg) { jnl_private_control *jpc; sgmnt_addrs *csa; uint4 status; if ((NULL != reg) && (TRUE == reg->open)) { csa = &FILE_INFO(reg)->s_addrs; jpc = csa->jnl; if ((TRUE == JNL_ENABLED(csa->hdr)) && (NULL != jpc)) { /* wait on jnl writes for region */ status = jnl_write_attempt(jpc, jpc->new_freeaddr); if (SS_NORMAL == status && !JNL_FILE_SWITCHED(jpc)) jnl_fsync(reg, jpc->new_freeaddr); } } return; } fis-gtm-V7.0-005/sr_port/jnl_write.c0000644000032200000250000005027414342376331016214 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2020 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_string.h" #include "gtm_inet.h" #include /* for offsetof() macro */ #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "ccp.h" #include "iosp.h" #include "jnl.h" #include "repl_msg.h" /* needed by gtmsource.h */ #include "gtmsource.h" #include "min_max.h" #include "sleep_cnt.h" #include "jnl_write.h" #include "copy.h" #include "jnl_get_checksum.h" #include "is_proc_alive.h" #include "wbox_test_init.h" #include "gtmimagename.h" #include "memcoherency.h" #include "interlock.h" #include "gdsbgtr.h" #ifdef DEBUG #include "gdskill.h" /* needed for tp.h */ #include "gdscc.h" /* needed for tp.h */ #include "buddy_list.h" /* needed for tp.h */ #include "tp.h" #endif GBLREF uint4 process_id; GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF boolean_t is_src_server; GBLREF boolean_t in_jnl_file_autoswitch; GBLREF uint4 dollar_tlevel; GBLREF inctn_opcode_t inctn_opcode; GBLREF jnl_gbls_t jgbl; #ifdef DEBUG #define MAX_JNL_WRITE_RECURSION_DEPTH 3 STATICDEF int jnl_write_recursion_depth; #endif error_def(ERR_JNLEXTEND); error_def(ERR_JNLWRTNOWWRTR); #define JNL_PUTSTR(lcl_free, lcl_buff, src, len, lcl_size) \ { \ uint4 size_before_wrap; \ \ assert(len <= lcl_size); \ size_before_wrap = lcl_size - lcl_free; \ if (len <= size_before_wrap) \ { \ memcpy(&lcl_buff[lcl_free], src, len); \ lcl_free += len; \ if (len == size_before_wrap) \ lcl_free = 0; \ } else \ { \ memcpy(&lcl_buff[lcl_free], src, size_before_wrap); \ lcl_free = len - size_before_wrap; \ memcpy(&lcl_buff[0], src + size_before_wrap, lcl_free); \ } \ } /* jpc : Journal private control * rectype : Record type * jnl_rec : This contains fixed part of a variable size record or the complete fixed size records. * parm1 : For JRT_PBLK and JRT_AIMG this has the block image (blk_ptr) * : For SET/KILL/ZKILL/ZTWORM/LGTRIG/ZTRIG records it contains the journal-format-buffer (jfb) */ void jnl_write(jnl_private_control *jpc, enum jnl_record_type rectype, jnl_record *jnl_rec, void *parm1) { uint4 align_filler_len, rlen, lcl_size, lcl_free, lcl_orig_free, next_align_addr; jnl_buffer_ptr_t jb; sgmnt_addrs *csa; sgmnt_data_ptr_t csd; node_local_ptr_t cnl; struct_jrec_align *jrec_align; jrec_suffix suffix; boolean_t nowrap, in_phase2, is_align; struct_jrec_blk *jrec_blk; uint4 checksum, lcl_freeaddr, new_freeaddr, start_freeaddr; gtm_int64_t min_dskaddr; sm_uc_ptr_t lcl_buff; gd_region *reg; int commit_index, index2; uint4 jnl_fs_block_size, aligned_lcl_free, padding_size; blk_hdr_ptr_t blk_ptr; jnl_format_buffer *jfb; jbuf_phase2_in_prog_t *phs2cmt; boolean_t was_latch_owner; # ifdef DEBUG uint4 tmp_csum1, tmp_csum2; uint4 mumps_node_sz; char *mumps_node_ptr; struct_jrec_align *align_rec; uint4 end_freeaddr; # endif assert(MAX_JNL_WRITE_RECURSION_DEPTH > jnl_write_recursion_depth++); reg = jpc->region; csa = &FILE_INFO(reg)->s_addrs; jb = jpc->jnl_buff; in_phase2 = (IN_PHASE2_JNL_COMMIT(csa) || jpc->in_jnl_phase2_salvage); lcl_freeaddr = JB_FREEADDR_APPROPRIATE(in_phase2, jpc, jb); assert(NULL != jnl_rec); rlen = jnl_rec->prefix.forwptr; assert(((gtm_uint64_t)lcl_freeaddr + rlen) < MAXUINT4); /* so the below + can be done without gtm_uint64_t typecast */ new_freeaddr = lcl_freeaddr + rlen; is_align = (JRT_ALIGN == rectype); if (!in_phase2 && !is_align) { next_align_addr = jb->next_align_addr; assert(lcl_freeaddr <= next_align_addr); if (new_freeaddr > next_align_addr) { /* If we have to write an ALIGN record here before writing a PINI record, thankfully there is no issue. * This is because an ALIGN record does not have a pini_addr (if it did then we would have used * JNL_FILE_FIRST_RECORD, the pini_addr field of the first PINI journal record in the journal file). */ jb->next_align_addr += jb->alignsize; assert(jb->next_align_addr < MAXUINT4); assert(new_freeaddr < jb->next_align_addr); jnl_write_align_rec(csa, next_align_addr - lcl_freeaddr, jnl_rec->prefix.time); lcl_freeaddr = jb->rsrv_freeaddr; /* can safely examine this because "in_phase2" is 0 at this point */ new_freeaddr = lcl_freeaddr + rlen; /* Now that we have written an ALIGN record, move on to write the requested "rectype" which is guaranteed * to not be an ALIGN record (since "is_align" is FALSE). */ if (JRT_PINI == rectype) { jnl_rec->prefix.pini_addr = lcl_freeaddr; /* Checksum needs to be recomputed since prefix.pini_addr is changed in above statement */ jnl_rec->prefix.checksum = INIT_CHECKSUM_SEED; jnl_rec->prefix.checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)&jnl_rec->jrec_pini, SIZEOF(struct_jrec_pini)); } } } csd = csa->hdr; # ifdef DEBUG /* Ensure that journaling is turned ON in this database file if we are about to write a journal record AND that * no replicated journal record is written by this routine if replication is in WAS_ON state. * The only exception for journaling being turned OFF is if we are in phase2 (outside crit) and had reserved * space during phase1 but journaling got turned OFF in between phase1 and phase2. In that case JNL_ENABLED(csa) * would still be TRUE and it is okay to continue writing the journal records since we are writing in the * reserved space only (no need to flush etc. for the most part). The only exception is if we need to invoke * "jnl_write_attempt" below and that invokes "jnl_file_lost". In that case, jpc->channel would be set to NOJNL * and we return right away. "jnl_write_phase2" has an assert which takes this scenario into account. */ assert((JNL_ENABLED(csd) && (!REPL_WAS_ENABLED(csd) || !jrt_is_replicated[rectype])) || (in_phase2 && JNL_ENABLED(csa))); /* Assert that the only journal records that the source server ever writes are PINI/PFIN/EPOCH/EOF * which it does at the very end when the database is about to be shut down. The only exception is * an ALIGN record which is an indirectly written record and a NULL record which it could write on behalf * of another GT.M process (in "jnl_phase2_salvage"). So account for that too. */ assert(!is_src_server || (JRT_EOF == rectype) || (JRT_PINI == rectype) || (JRT_EPOCH == rectype) || (JRT_PFIN == rectype) || (JRT_ALIGN == rectype) || (JRT_NULL == rectype)); assert(csa->now_crit || in_phase2 || (csd->clustered && (csa->nl->ccp_state == CCST_CLOSED))); assert(IS_VALID_RECTYPES_RANGE(rectype)); # endif lcl_free = JB_FREE_APPROPRIATE(in_phase2, jpc, jb); lcl_size = jb->size; lcl_buff = &jb->buff[jb->buff_off]; ++jb->reccnt[rectype]; /* Do high-level check on rlen */ assert(rlen <= jb->max_jrec_len); /* Do fine-grained checks on rlen */ GTMTRIG_ONLY(assert(!IS_ZTWORM(rectype) || (MAX_ZTWORM_JREC_LEN >= rlen));) /* ZTWORMHOLE */ GTMTRIG_ONLY(assert(!IS_LGTRIG(rectype) || (MAX_LGTRIG_JREC_LEN >= rlen));) /* LGTRIG */ assert(!IS_SET_KILL_ZKILL_ZTRIG(rectype) || (JNL_MAX_SET_KILL_RECLEN(csd) >= rlen)); /* SET, KILL, ZKILL, ZTRIG */ jb->bytcnt += rlen; assert (0 == rlen % JNL_REC_START_BNDRY); cnl = csa->nl; /* If we are currently extending the journal file and writing the closing part of journal records, * it better be the records that we expect. This is because we will skip the padding check for these * records. The macro JNL_FILE_TAIL_PRESERVE already takes into account padding space for these. */ assert(!in_jnl_file_autoswitch || (JRT_PINI == rectype) || (JRT_PFIN == rectype) || (JRT_EPOCH == rectype) || (JRT_INCTN == rectype) || (JRT_EOF == rectype) || (JRT_ALIGN == rectype)); checksum = GET_JREC_CHECKSUM(jnl_rec, rectype); assert(checksum); # ifdef DEBUG /* Ensure that the checksum computed earlier in jnl_format or jnl_write_pblk or jnl_write_aimg_rec * or fixed-sized records matches with the block's content. */ blk_ptr = NULL; jfb = NULL; if ((JRT_PBLK == rectype) || (JRT_AIMG == rectype)) { blk_ptr = (blk_hdr_ptr_t)parm1; assert(JNL_MAX_PBLK_RECLEN(csd) >= rlen); /* PBLK and AIMG */ COMPUTE_COMMON_CHECKSUM(tmp_csum2, jnl_rec->prefix); tmp_csum1 = jnl_get_checksum(blk_ptr, NULL, jnl_rec->jrec_pblk.bsiz); COMPUTE_PBLK_CHECKSUM(tmp_csum1, &jnl_rec->jrec_pblk, tmp_csum2, tmp_csum1); } else if (IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(rectype)) { jfb = (jnl_format_buffer *)parm1; COMPUTE_COMMON_CHECKSUM(tmp_csum2, jnl_rec->prefix); mumps_node_ptr = jfb->buff + FIXED_UPD_RECLEN; mumps_node_sz = jfb->record_size - (FIXED_UPD_RECLEN + JREC_SUFFIX_SIZE); tmp_csum1 = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)mumps_node_ptr, mumps_node_sz); COMPUTE_LOGICAL_REC_CHECKSUM(tmp_csum1, &jnl_rec->jrec_set_kill, tmp_csum2, tmp_csum1); } else if (jrt_fixed_size[rectype]) { jnl_rec->prefix.checksum = INIT_CHECKSUM_SEED; assert(JRT_TRIPLE != rectype); assert(JRT_HISTREC != rectype); tmp_csum1 = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)jnl_rec, jnl_rec->prefix.forwptr); jnl_rec->prefix.checksum = checksum; } else if (is_align) { /* Note: "struct_jrec_align" has a different layout (e.g. "checksum" at different offset etc.) than all * other jnl records. So handle this specially. */ align_rec = (struct_jrec_align *)jnl_rec; align_rec->checksum = INIT_CHECKSUM_SEED; tmp_csum1 = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)jnl_rec, FIXED_ALIGN_RECLEN); align_rec->checksum = checksum; } else assert(FALSE); /* It is possible "jnl_file_lost" was invoked by us during phase2 of commit in which case jpc->pini_addr would have * been reset to 0 and so the checksums might not match. Allow for that in the below assert. */ assert((checksum == tmp_csum1) || ((!JNL_ENABLED(csd)) && (JNL_ENABLED(csa)))); # endif ADJUST_CHECKSUM(checksum, lcl_freeaddr, checksum); ADJUST_CHECKSUM(checksum, csd->jnl_checksum, checksum); SET_JREC_CHECKSUM(jnl_rec, rectype, checksum); if (!in_phase2) { assert((!jb->blocked) || (FALSE == is_proc_alive(jb->blocked, 0))); jb->blocked = process_id; } jnl_fs_block_size = jb->fs_block_size; min_dskaddr = (gtm_int64_t)new_freeaddr - lcl_size + jnl_fs_block_size; /* gtm_int64_t used as result can be negative */ /* If jb->dskaddr >= min_dskaddr, we are guaranteed we can write "rlen" bytes at "lcl_freeaddr" without * overflowing the journal buffer and/or overwriting the filesystem-aligned block before "jb->dskaddr" in jnl buffer. * The check is not exact in the sense we might invoke "jnl_write_attempt" even if it is not necessary but it is * very rare and this avoids us from always having to use a ROUND_UP2 or "& ~(jb->fs_block_size - 1)" operation. */ if ((gtm_int64_t)jb->dskaddr < min_dskaddr) { if (csa->now_crit && in_phase2 && ((gtm_int64_t)jb->freeaddr < min_dskaddr)) { /* Holding crit AND phase2. Find corresponding phase2 commit entry in jb. * Check if that commit entry writes journal records more than journal buffer size. * If so, we need to adjust jb->freeaddr in the middle of the transaction thereby * "jnl_write_attempt" can flush partial transaction data and make space for the remaining data. */ index2 = jb->phase2_commit_index2; assert(jb->phase2_commit_index1 != index2); ASSERT_JNL_PHASE2_COMMIT_INDEX_IS_VALID(index2, JNL_PHASE2_COMMIT_ARRAY_SIZE); DECR_PHASE2_COMMIT_INDEX(index2, JNL_PHASE2_COMMIT_ARRAY_SIZE); phs2cmt = &jb->phase2_commit_array[index2]; assert(phs2cmt->process_id == process_id); assert(!phs2cmt->write_complete); start_freeaddr = phs2cmt->start_freeaddr; /* If min_dskaddr < start_freeaddr, all we need is to flush out any phase2 commits before the * current one so we can directly go to do a "jnl_write_attempt(min_dskaddr)" below which * automatically takes care of that. Otherwise, we need to flush all prior phase2 commits * first (i.e. "jnl_write_attempt(start_freeaddr)") and then adjust jb->freeaddr based on * the ongoing/current phase2 commit as it fills up the journal buffer with jnl records. */ if (min_dskaddr >= start_freeaddr) { assert(dollar_tlevel); assert(phs2cmt->tot_jrec_len > (lcl_size - jnl_fs_block_size)); DEBUG_ONLY(end_freeaddr = start_freeaddr + phs2cmt->tot_jrec_len); assert(end_freeaddr >= new_freeaddr); if ((jb->freeaddr < start_freeaddr) && (SS_NORMAL != jnl_write_attempt(jpc, start_freeaddr))) { assert(NOJNL == jpc->channel); /* jnl file lost */ DEBUG_ONLY(jnl_write_recursion_depth--); return; /* let the caller handle the error */ } assert(jb->freeaddr >= start_freeaddr); /* It is possible we already own the latch in case we are in timer-interrupt * or process-exit code hence the below check. */ was_latch_owner = GLOBAL_LATCH_HELD_BY_US(&jb->phase2_commit_latch); if (!was_latch_owner) { /* Return value of "grab_latch" does not need to be checked * because we pass GRAB_LATCH_INDEFINITE_WAIT as timeout. */ grab_latch(&jb->phase2_commit_latch, GRAB_LATCH_INDEFINITE_WAIT, WS_29, csa); } SET_JBP_FREEADDR(jb, lcl_freeaddr); if (!was_latch_owner) rel_latch(&jb->phase2_commit_latch); } } if (SS_NORMAL != jnl_write_attempt(jpc, min_dskaddr)) { assert(NOJNL == jpc->channel); /* jnl file lost */ DEBUG_ONLY(jnl_write_recursion_depth--); return; /* let the caller handle the error */ } } /* If we are in "phase2", the call to "jnl_write_reserve" would have done the needed "jnl_file_extend" calls in phase1 * so no need to do "jnl_write_extend_if_needed" if "in_phase2" is TRUE. */ if (!in_phase2) { jb->blocked = 0; if (0 != jnl_write_extend_if_needed(rlen, jb, lcl_freeaddr, csa, rectype, blk_ptr, jfb, reg, jpc, jnl_rec)) { DEBUG_ONLY(jnl_write_recursion_depth--); return; /* let the caller handle the error */ } } lcl_orig_free = lcl_free; nowrap = (lcl_size >= (lcl_free + rlen)); assert(jrt_fixed_size[JRT_EOF]); if (jrt_fixed_size[rectype]) { if (nowrap) { memcpy(lcl_buff + lcl_free, (uchar_ptr_t)jnl_rec, rlen); lcl_free += rlen; if (lcl_size == lcl_free) lcl_free = 0; } else JNL_PUTSTR(lcl_free, lcl_buff, (uchar_ptr_t)jnl_rec, rlen, lcl_size); /* As part of writing the EOF record into the journal buffer, add enough 0-padding needed to reach * a filesystem-block-size aligned boundary. This way later jnl_qio_start can safely do aligned * writes without having to write non-zero garbage after the EOF record. Note that this has to be * done BEFORE updating freeaddr. Otherwise, it is possible that a jnl qio timer pops after freeaddr * gets updated but before the 0-padding is done and flushes the eof record to disk without the 0-padding. */ if (JRT_EOF == rectype) { assert(!in_phase2); aligned_lcl_free = ROUND_UP2(lcl_free, jnl_fs_block_size); padding_size = aligned_lcl_free - lcl_free; if (padding_size) memset(lcl_buff + lcl_free, 0, padding_size); } if (JRT_EPOCH != rectype) INCR_GVSTATS_COUNTER(csa, cnl, n_jrec_other, 1); /* else for EPOCH, the increment of JRE or JRI is done after "jnl_write_epoch_rec" in caller */ } else { switch(rectype) { case JRT_PBLK: case JRT_AIMG: blk_ptr = (blk_hdr_ptr_t)parm1; assert(NULL != blk_ptr); /* PBLK and AIMG */ assert(FIXED_BLK_RECLEN == FIXED_PBLK_RECLEN); assert(FIXED_BLK_RECLEN == FIXED_AIMG_RECLEN); jrec_blk = (struct_jrec_blk *)jnl_rec; suffix.backptr = rlen; suffix.suffix_code = JNL_REC_SUFFIX_CODE; if (nowrap) { /* write fixed part of record before the actual gds block image */ memcpy(lcl_buff + lcl_free, (uchar_ptr_t)jnl_rec, FIXED_BLK_RECLEN); lcl_free += (int4)FIXED_BLK_RECLEN; /* write actual block */ memcpy(lcl_buff + lcl_free, (uchar_ptr_t)blk_ptr, jrec_blk->bsiz); lcl_free += jrec_blk->bsiz; } else { /* write fixed part of record before the actual gds block image */ JNL_PUTSTR(lcl_free, lcl_buff, (uchar_ptr_t)jnl_rec, (int4)FIXED_BLK_RECLEN, lcl_size); /* write actual block */ JNL_PUTSTR(lcl_free, lcl_buff, (uchar_ptr_t)blk_ptr, jrec_blk->bsiz, lcl_size); } /* Skip over a few characters for 8-bye alignment and then write suffix */ assert(lcl_free <= lcl_size); lcl_free = ROUND_UP2(lcl_free + JREC_SUFFIX_SIZE, JNL_REC_START_BNDRY); if (lcl_free > lcl_size) { assert(lcl_free == (lcl_size + JNL_REC_START_BNDRY)); lcl_free = JNL_REC_START_BNDRY; } *((jrec_suffix *)(lcl_buff + lcl_free - JREC_SUFFIX_SIZE)) = suffix; assert(lcl_free <= lcl_size); if (lcl_size == lcl_free) lcl_free = 0; INCR_GVSTATS_COUNTER(csa, cnl, n_jrec_pblk, 1); break; case JRT_ALIGN: assert(lcl_free < lcl_size); jrec_align = &jnl_rec->jrec_align; align_filler_len = rlen - MIN_ALIGN_RECLEN; /* Note: this filler section is not zeroed */ if (nowrap) { memcpy(lcl_buff + lcl_free, (uchar_ptr_t)jrec_align, FIXED_ALIGN_RECLEN); lcl_free += (int4)(FIXED_ALIGN_RECLEN + align_filler_len); } else { JNL_PUTSTR(lcl_free, lcl_buff, (uchar_ptr_t)jrec_align, (int4)FIXED_ALIGN_RECLEN, lcl_size); /* Note: JNL_PUTSTR can wrap-adjust "lcl_free" while writing FIXED_ALIGN_RECLEN bytes. * In that case, we should not wrap-adjust it (i.e. "- lcl_size"). * Hence the "if" check below. */ lcl_free += align_filler_len; if (lcl_size <= lcl_free) { lcl_free -= lcl_size; assert(lcl_free < lcl_size); } } /* Now copy suffix */ assert(0 == (UINTPTR_T)(&lcl_buff[0] + lcl_free) % SIZEOF(jrec_suffix)); suffix.backptr = rlen; suffix.suffix_code = JNL_REC_SUFFIX_CODE; *(jrec_suffix *)(lcl_buff + lcl_free) = suffix; lcl_free += SIZEOF(jrec_suffix); if (lcl_size == lcl_free) lcl_free = 0; INCR_GVSTATS_COUNTER(csa, cnl, n_jrec_other, 1); break; default: /* SET, KILL, ZKILL for TP, ZTP, non-TP */ jfb = (jnl_format_buffer *)parm1; assert(NULL != jfb); assert(IS_TP(rectype) || IS_ZTP(rectype) || (0 == ((struct_jrec_upd *)jfb->buff)->update_num)); assert((!IS_TP(rectype) && !IS_ZTP(rectype)) || (0 != ((struct_jrec_upd *)jfb->buff)->update_num)); assert(((jrec_prefix *)jfb->buff)->forwptr == jfb->record_size); if (nowrap) { memcpy(lcl_buff + lcl_free, (uchar_ptr_t)jfb->buff, rlen); lcl_free += rlen; if (lcl_size == lcl_free) lcl_free = 0; } else JNL_PUTSTR(lcl_free, lcl_buff, (uchar_ptr_t)jfb->buff, rlen, lcl_size); INCR_GVSTATS_COUNTER(csa, cnl, n_jrec_logical, 1); break; } } assert((lcl_free - lcl_orig_free + lcl_size) % lcl_size == rlen); assert(lcl_buff[lcl_orig_free] == rectype); assert(lcl_orig_free < lcl_free || lcl_free < jb->dsk); assert((lcl_freeaddr >= jb->dskaddr) || (gtm_white_box_test_case_enabled && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number))); jpc->new_freeaddr = new_freeaddr; INCR_GVSTATS_COUNTER(csa, cnl, n_jbuff_bytes, rlen); assert(lcl_free == jpc->new_freeaddr % lcl_size); if (!in_phase2) { assert(jnl_rec->prefix.time == jgbl.gbl_jrec_time); /* since latter is used in UPDATE_JBP_RSRV_FREEADDR */ SET_JNLBUFF_PREV_JREC_TIME(jb, jnl_rec->prefix.time, DO_GBL_JREC_TIME_CHECK_TRUE); /* Keep jb->prev_jrec_time up to date */ jpc->curr_tn = csa->ti->curr_tn; /* needed below by UPDATE_JBP_RSRV_FREEADDR */ #ifdef DEBUG if ((WBTEST_ENABLED(WBTEST_MURUNDOWN_KILLCMT06)) && (gtm_white_box_test_case_count == 1)) gtm_white_box_test_case_count = 2; #endif UPDATE_JBP_RSRV_FREEADDR(csa, jpc, jb, NULL, rlen, commit_index, FALSE, 0, 0, FALSE); /* sets "commit_index" */ assert(jb->phase2_commit_array[commit_index].curr_tn == jpc->curr_tn); JNL_PHASE2_WRITE_COMPLETE(csa, jb, commit_index, jpc->new_freeaddr); } else { jpc->phase2_freeaddr = jpc->new_freeaddr; jpc->phase2_free = lcl_free; } DEBUG_ONLY(jnl_write_recursion_depth--); } fis-gtm-V7.0-005/sr_port/jnl_write.h0000644000032200000250000000202114342376331016204 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef JNL_WRITE_H_INCLUDED #define JNL_WRITE_H_INCLUDED /* We do not put this in jnl.h, because it needs all including jnl.h must include gdsblk.h */ void jnl_write(jnl_private_control *jpc, enum jnl_record_type rectype, jnl_record *jnl_rec, void *parm1); int jnl_write_extend_if_needed(int4 jrec_len, jnl_buffer_ptr_t jb, uint4 lcl_freeaddr, sgmnt_addrs *csa, enum jnl_record_type rectype, blk_hdr_ptr_t blk_ptr, jnl_format_buffer *jfb, gd_region *reg, jnl_private_control *jpc, jnl_record *jnl_rec); #endif fis-gtm-V7.0-005/sr_port/jnl_write_aimg_rec.c0000644000032200000250000000671314342376331020041 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 #include "gtm_string.h" #include "gtm_time.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "jnl.h" #include "jnl_write.h" #include "jnl_write_aimg_rec.h" #include "jnl_get_checksum.h" #include "min_max.h" #include "gtmcrypt.h" GBLREF jnl_gbls_t jgbl; GBLREF mstr pvt_crypt_buf; void jnl_write_aimg_rec(sgmnt_addrs *csa, cw_set_element *cse, uint4 com_csum) { blk_hdr_ptr_t buffer, save_buffer; boolean_t in_phase2; boolean_t use_new_key; char *in, *out; gd_segment *seg; int in_len, gtmcrypt_errno; int tmp_jrec_size; jnl_private_control *jpc; sgmnt_data_ptr_t csd; struct_jrec_blk aimg_record; uint4 cursum; csd = csa->hdr; jpc = csa->jnl; assert(0 != jpc->pini_addr); aimg_record.prefix.jrec_type = JRT_AIMG; aimg_record.prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; in_phase2 = IN_PHASE2_JNL_COMMIT(csa); aimg_record.prefix.tn = JB_CURR_TN_APPROPRIATE(in_phase2, jpc, csa); /* At this point jgbl.gbl_jrec_time should be set by the caller */ assert(jgbl.gbl_jrec_time); aimg_record.prefix.time = jgbl.gbl_jrec_time; aimg_record.prefix.checksum = INIT_CHECKSUM_SEED; aimg_record.blknum = cse->blk; /* in case we have a bad block-size, we don't want to write an AIMG larger than the GDS block size (maximum block size) */ buffer = (blk_hdr_ptr_t)cse->new_buff; assert(buffer->bsiz <= csd->blk_size); assert(buffer->bsiz >= SIZEOF(blk_hdr)); aimg_record.bsiz = MIN(csd->blk_size, buffer->bsiz); aimg_record.ondsk_blkver = cse->ondsk_blkver; tmp_jrec_size = (int)FIXED_AIMG_RECLEN + aimg_record.bsiz + JREC_SUFFIX_SIZE; aimg_record.prefix.forwptr = ROUND_UP2(tmp_jrec_size, JNL_REC_START_BNDRY); assert(SIZEOF(uint4) == SIZEOF(jrec_suffix)); save_buffer = buffer; in_len = aimg_record.bsiz - SIZEOF(*buffer); if (IS_BLK_ENCRYPTED(buffer->levl, in_len) && USES_ANY_KEY(csd)) { ASSERT_ENCRYPTION_INITIALIZED; assert(aimg_record.bsiz <= csa->hdr->blk_size); REALLOC_CRYPTBUF_IF_NEEDED(csa->hdr->blk_size); memcpy(pvt_crypt_buf.addr, buffer, SIZEOF(blk_hdr)); /* copy the block header */ in = (char *)(buffer + 1); /* + 1 because `buffer' is of type blk_hdr_ptr_t */ out = pvt_crypt_buf.addr + SIZEOF(blk_hdr); use_new_key = USES_NEW_KEY(csd); GTMCRYPT_ENCRYPT(csa, (use_new_key ? TRUE : csd->non_null_iv), (use_new_key ? csa->encr_key_handle2 : csa->encr_key_handle), in, in_len, out, buffer, SIZEOF(blk_hdr), gtmcrypt_errno); if (0 != gtmcrypt_errno) { seg = csa->region->dyn.addr; GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, rts_error, seg->fname_len, seg->fname); } buffer = (blk_hdr_ptr_t)pvt_crypt_buf.addr; } cursum = jnl_get_checksum(buffer, NULL, aimg_record.bsiz); COMPUTE_AIMG_CHECKSUM(cursum, &aimg_record, com_csum, aimg_record.prefix.checksum); jnl_write(jpc, JRT_AIMG, (jnl_record *)&aimg_record, buffer); } fis-gtm-V7.0-005/sr_port/jnl_write_aimg_rec.h0000755000032200000250000000125214342376331020042 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef JNL_WRITE_AIMG_REC_H_INCLUDED #define JNL_WRITE_AIMG_REC_H_INCLUDED void jnl_write_aimg_rec(sgmnt_addrs *csa, cw_set_element *cse, uint4 com_csum); #endif fis-gtm-V7.0-005/sr_port/jnl_write_align_rec.c0000644000032200000250000000356414342376331020217 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2017 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_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "jnl_write.h" #include "jnl_get_checksum.h" void jnl_write_align_rec(sgmnt_addrs *csa, uint4 align_filler_len, jnl_tm_t time) { struct_jrec_align align_rec; jnl_private_control *jpc; uint4 align_rec_len; /* The below assert is the reason why we have a GET_JREC_CHECKSUM macro */ assert(OFFSETOF(struct_jrec_align, checksum) != OFFSETOF(jrec_prefix, checksum)); assert(OFFSETOF(struct_jrec_align, checksum) == OFFSETOF(jrec_prefix, pini_addr)); assert(SIZEOF(align_rec.checksum) == SIZEOF(((jrec_prefix *)NULL)->checksum)); assert(OFFSETOF(struct_jrec_align, time) == OFFSETOF(jrec_prefix, time)); assert(SIZEOF(align_rec.time) == SIZEOF(((jrec_prefix *)NULL)->time)); align_rec_len = MIN_ALIGN_RECLEN + align_filler_len; jpc = csa->jnl; assert(align_rec_len <= jpc->jnl_buff->max_jrec_len); assert(0 == (align_rec_len % JNL_REC_START_BNDRY)); align_rec.jrec_type = JRT_ALIGN; align_rec.forwptr = align_rec_len; align_rec.time = time; align_rec.checksum = INIT_CHECKSUM_SEED; align_rec.checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)&align_rec, FIXED_ALIGN_RECLEN); jnl_write(jpc, JRT_ALIGN, (jnl_record *)&align_rec, NULL); } fis-gtm-V7.0-005/sr_port/jnl_write_attempt.c0000644000032200000250000003727014342376331017753 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "gdsbgtr.h" #include "filestruct.h" #include "iosp.h" #include "jnl.h" #include "lockconst.h" #include "interlock.h" #include "sleep_cnt.h" #include "send_msg.h" #include "wcs_sleep.h" #include "is_proc_alive.h" #include "compswap.h" #include "is_file_identical.h" #include "have_crit.h" #include "wbox_test_init.h" #include "anticipatory_freeze.h" #include "repl_msg.h" /* needed for gtmsource.h */ #include "gtmsource.h" /* needed for jnlpool_addrs typedef */ #include "gtmmsg.h" #include "io.h" /* needed by gtmsecshr.h */ #include "gtmsecshr.h" /* for continue_proc */ #include "gtm_c_stack_trace.h" #include "sleep.h" #define ITERATIONS_100K 100000 GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF uint4 process_id; GBLREF uint4 image_count; error_def(ERR_JNLACCESS); error_def(ERR_JNLCNTRL); error_def(ERR_JNLFLUSH); error_def(ERR_JNLFLUSHNOPROG); error_def(ERR_JNLPROCSTUCK); error_def(ERR_JNLQIOSALVAGE); error_def(ERR_JNLWRTDEFER); error_def(ERR_JNLWRTNOWWRTR); error_def(ERR_TEXT); static uint4 jnl_sub_write_attempt(jnl_private_control *jpc, unsigned int *lcnt, uint4 threshold) { sgmnt_addrs *csa; jnl_buffer_ptr_t jb; unsigned int status; boolean_t was_crit, exact_check, freeze_waiter = FALSE, freeze_cleared; /**** Note static/local */ static uint4 loop_image_count, writer; /* assumes calls from one loop at a time */ uint4 new_dskaddr, new_dsk; uint4 dskaddr, freeaddr, free, rsrv_freeaddr; uint4 phase2_commit_index1; static uint4 stuck_cnt = 0; jnlpool_addrs_ptr_t local_jnlpool; intrpt_state_t prev_intrpt_state; /* Some callers of jnl_sub_write_attempt (jnl_flush->jnl_write_attempt, jnl_write->jnl_write_attempt) are in * crit, and some other (jnl_wait->jnl_write_attempt) are not. Callers in crit do not need worry about journal * buffer fields (dskaddr, freeaddr) changing underneath them, but for those not in crit, jnl_sub_write_attempt * might incorrectly return an error status when journal file is switched. Such callers should check for * journal file switched condition and terminate any loops they are in. */ jb = jpc->jnl_buff; status = ERR_JNLWRTDEFER; csa = &FILE_INFO(jpc->region)->s_addrs; was_crit = csa->now_crit; exact_check = was_crit && (threshold == jb->rsrv_freeaddr); /* see comment in jnl_write_attempt() for why this is needed */ while (exact_check ? (jb->dskaddr != threshold) : (jb->dskaddr < threshold)) { if (jb->io_in_prog_latch.u.parts.latch_pid == process_id) { /* if error condition occurred while doing jnl_qio_start(), then release the lock before waiting */ /* note that this is done only in UNIX because Unix does synchronous I/O */ jb->image_count = 0; RELEASE_SWAPLOCK(&jb->io_in_prog_latch); } if ((!jb->io_in_prog_latch.u.parts.latch_pid) DEBUG_ONLY(&& !WBTEST_ENABLED(WBTEST_JNLPROCSTUCK_FORCE))) { if (freeze_waiter) { CLEAR_ANTICIPATORY_FREEZE(freeze_cleared); /* sets freeze_cleared */ REPORT_INSTANCE_UNFROZEN(freeze_cleared); ENABLE_INTERRUPTS(INTRPT_IN_RETRY_LOOP, prev_intrpt_state); freeze_waiter = FALSE; } status = jnl_qio_start(jpc); } if (SS_NORMAL == status) break; assert(ERR_JNLWRTNOWWRTR != status); /* don't have asynchronous jnl writes in Unix */ if ((ERR_JNLWRTNOWWRTR != status) && (ERR_JNLWRTDEFER != status)) { assert(!freeze_waiter); return status; } if (freeze_waiter) { if (!IS_REPL_INST_FROZEN) { /* Somehow the freeze was lifted by someone else */ ENABLE_INTERRUPTS(INTRPT_IN_RETRY_LOOP, prev_intrpt_state); freeze_waiter = FALSE; } else { wcs_sleep(*lcnt); continue; } } if ((writer != CURRENT_JNL_IO_WRITER(jb)) || (1 == *lcnt)) { writer = CURRENT_JNL_IO_WRITER(jb); loop_image_count = jb->image_count; *lcnt = 1; /* !!! this should be detected and limited by the caller !!! */ # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_JNLPROCSTUCK_FORCE)) writer = process_id; else # endif break; } if ((*lcnt <= JNL_MAX_FLUSH_TRIES) DEBUG_ONLY(&& !(WBTEST_ENABLED(WBTEST_JNLPROCSTUCK_FORCE)))) { wcs_sleep(*lcnt); break; } if ((writer == CURRENT_JNL_IO_WRITER(jb)) DEBUG_ONLY(|| (WBTEST_ENABLED(WBTEST_JNLPROCSTUCK_FORCE)))) { /* It isn't strictly necessary to hold crit here since we are doing an atomic operation on * io_in_prog_latch, which won't have any effect if the writer changed. If things are in a bad state, * though, grabbing crit will call wcs_recover() for us. * However, a grab_crit() here may result in a deadlock, so just do a grab_crit_immediate() and proceed. */ if (!was_crit) grab_crit_immediate(jpc->region, TRUE, NOT_APPLICABLE); /* If no one home, try to clear the latch. */ if (((FALSE == is_proc_alive(writer, jb->image_count)) DEBUG_ONLY(&& !(WBTEST_ENABLED(WBTEST_JNLPROCSTUCK_FORCE)))) && COMPSWAP_UNLOCK(&jb->io_in_prog_latch, writer, jb->image_count, LOCK_AVAILABLE, 0)) { /* We cleared the latch, so report it and restart the loop. */ BG_TRACE_PRO_ANY(csa, jnl_blocked_writer_lost); jnl_send_oper(jpc, ERR_JNLQIOSALVAGE); if (!was_crit && csa->now_crit) /* Check now_crit in case grab_crit_immediate() failed */ rel_crit(jpc->region); *lcnt = 1; continue; } if (!was_crit && csa->now_crit) /* Check now_crit in case grab_crit_immediate() failed */ rel_crit(jpc->region); /* this is the interesting case: a process is stuck */ BG_TRACE_PRO_ANY(csa, jnl_blocked_writer_stuck); if (IS_REPL_INST_FROZEN) { /* Restart if instance frozen. */ *lcnt = 1; continue; } jpc->status = status; send_msg_csa(CSA_ARG(csa) VARLSTCNT(3) ERR_JNLPROCSTUCK, 1, writer); # ifdef DEBUG if (WBTEST_ENABLED(WBTEST_JNLPROCSTUCK_FORCE)) gtm_white_box_test_case_enabled = FALSE; # endif stuck_cnt++; if (IS_REPL_INST_FROZEN) { /* The instance wasn't frozen above, but it is now, so most likely we froze it. * Note the fact. * Deferring interrupts here prevents possible hangs in GET_C_STACK_FROM_SCRIPT. */ DEFER_INTERRUPTS(INTRPT_IN_RETRY_LOOP, prev_intrpt_state); freeze_waiter = TRUE; } GET_C_STACK_FROM_SCRIPT("JNLPROCSTUCK", process_id, writer, stuck_cnt); *lcnt = 1; /* ??? is it necessary to limit this, and if so, how ??? */ if (freeze_waiter) { /* We are frozen, so restart. */ continue; } status = ERR_JNLPROCSTUCK; continue_proc(writer); break; } break; } if (csa->now_crit && (jb->dskaddr > jb->freeaddr)) { /* jb->dskaddr > jb->freeaddr => out of design condition if we have crit. * If we don't have crit, a journal switch could have occurred, so not an error condition. */ status = ERR_JNLCNTRL; } if (freeze_waiter) { CLEAR_ANTICIPATORY_FREEZE(freeze_cleared); /* sets freeze_cleared */ REPORT_INSTANCE_UNFROZEN(freeze_cleared); ENABLE_INTERRUPTS(INTRPT_IN_RETRY_LOOP, prev_intrpt_state); } return status; } uint4 jnl_write_attempt(jnl_private_control *jpc, uint4 threshold) { jnl_buffer_ptr_t jb; uint4 prev_freeaddr; uint4 index1, index2; unsigned int lcnt, prev_lcnt, cnt; sgmnt_addrs *csa; jnlpool_addrs_ptr_t save_jnlpool; unsigned int status; boolean_t was_crit, jnlfile_lost, exact_check; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; jb = jpc->jnl_buff; csa = &FILE_INFO(jpc->region)->s_addrs; save_jnlpool = jnlpool; if (csa->jnlpool && (csa->jnlpool != jnlpool)) jnlpool = csa->jnlpool; was_crit = csa->now_crit; /* If holding crit and input threshold matches jb->rsrv_freeaddr, then we need to wait in the loop as long as dskaddr * is not EQUAL to threshold. This is because if dskaddr is lesser than threshold we need to wait. If ever it * becomes greater than threshold, it is an out-of-design situation (since dskaddr has effectively become > rsrv_freeaddr) * and so we need to trigger "jnl_file_lost" which is done in "jnl_sub_write_attempt" so it is important to invoke * that routine (in the for loop below). Hence the need to do an exact match instead of a < match. If not holding * crit or input threshold does not match jb->rsrv_freeaddr, then dskaddr becoming GREATER than threshold is a valid * condition so we should do a (dskaddr < threshold), not a (dskaddr != threshold) check in that case. */ exact_check = was_crit && (threshold == jb->rsrv_freeaddr); assert(!was_crit || threshold <= jb->rsrv_freeaddr); /* Check that we either own crit on the current region or we don't own crit on ANY region. This is relied upon by * the grab_crit calls (done in jnl_write_attempt and jnl_sub_write_attempt) to ensure no deadlocks are possible. */ assert(was_crit || (0 == have_crit(CRIT_HAVE_ANY_REG))); for (prev_lcnt = lcnt = cnt = 1; (was_crit || (NOJNL != jpc->channel)) && (exact_check ? jb->dskaddr != threshold : jb->dskaddr < threshold); lcnt++, prev_lcnt = lcnt, cnt++) { prev_freeaddr = jb->freeaddr; if (prev_freeaddr < threshold) { JNL_PHASE2_CLEANUP_IF_POSSIBLE(csa, jb); /* phase2 commits in progress. Clean them up if possible */ if (prev_freeaddr == jb->freeaddr) { /* No cleanup happened implies process in phase2 commit is still alive. * Give it some time to finish its job. Not sleeping here could result in a spinloop * below (due to the "continue" below under the "SS_NORMAL == status" if check). */ BG_TRACE_PRO_ANY(csa, jnl_phase2_cleanup_if_possible); SLEEP_USEC(1, FALSE); /* There are two conditions here: * 1. The process already holds crit. In this case, immediately perform the cleanup action * 2. The process has already slept 100k times for 1ms (~ 100ms) without crit. Given the wait * without crit, see if crit can be obtained that way the JNL_PHASE2_CLEANUP_IF_POSSIBLE macro * will attempt "jnl_phase2_salvage" if needed. * * An example scenario where this is needed is if a process is in "gds_rundown"->"jnl_wait" * and does not hold crit but has written journal records after those written by another * process which was kill -9ed in phase2 of its jnl commit. Not doing this check would * cause the process in gds_rundown to be indefinitely stuck in "jnl_wait". */ if (was_crit || (!was_crit && (0 == (lcnt % ITERATIONS_100K)) && (grab_crit_immediate(jpc->region, OK_FOR_WCS_RECOVER_TRUE, NOT_APPLICABLE)))) { SHM_READ_MEMORY_BARRIER; /* Ensure the indices read from memory are correct */ index1 = jb->phase2_commit_index1; index2 = jb->phase2_commit_index2; /* This condition implies that a update/MUMPS process was killed in CMT06, right before * updating jb->phase2_commit_index2. If crit is held, increment index2 & call * jnl_phase2_cleanup() to process it as a dead commit. */ if ((csa->now_crit) && (index1 == index2) && (jb->freeaddr < jb->rsrv_freeaddr) && ((jb->phase2_commit_array[index1].start_freeaddr + jb->phase2_commit_array[index1].tot_jrec_len) == jb->rsrv_freeaddr)) INCR_PHASE2_COMMIT_INDEX(jb->phase2_commit_index2, JNL_PHASE2_COMMIT_ARRAY_SIZE); /* phase2 commits in progress. Clean them up if possible. */ JNL_PHASE2_CLEANUP_IF_POSSIBLE(csa, jb); if (!was_crit) rel_crit(jpc->region); } } } status = jnl_sub_write_attempt(jpc, &lcnt, threshold); if (JNL_FILE_SWITCHED(jpc)) { /* If we are holding crit, the journal file switch could happen in the form of journaling getting * turned OFF (due to disk space issues etc.) */ jpc->status = SS_NORMAL; if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; return SS_NORMAL; } if (SS_NORMAL == status) { /* In Unix, writes are synchronous so SS_NORMAL status return implies we have completed a jnl * write and "jb->dskaddr" is closer to "threshold" than it was in the previous iteration. * A sleep at this point will only slow things down unnecessarily. Hence no sleep if Unix. */ continue; } if ((ERR_JNLCNTRL == status) || (ERR_JNLACCESS == status) || (csa->now_crit && (ERR_JNLWRTDEFER != status) && (ERR_JNLWRTNOWWRTR != status))) { /* If JNLCNTRL or if holding crit and not waiting for some other writer * better turn off journaling and proceed with database update to avoid a database hang. */ if (was_crit) jb->blocked = 0; else { assertpro(0 == have_crit(CRIT_HAVE_ANY_REG)); grab_crit(jpc->region, WS_4); /*jnl_write_attempt has assert about have_crit that this relies on */ } jnlfile_lost = FALSE; assert(TREF(gtm_test_fake_enospc) || WBTEST_ENABLED(WBTEST_JNL_FILE_LOST_DSKADDR) || WBTEST_ENABLED(WBTEST_RECOVER_ENOSPC) || (ERR_JNLPROCSTUCK == status)); if (JNL_ENABLED(csa->hdr) && (ERR_JNLPROCSTUCK != status)) { /* We ignore the return value of jnl_file_lost() since we always want to report the journal * error, whatever its error handling method is. Also, an operator log will be sent by some * callers (t_end()) only if an error is returned here, and the operator log is wanted in * those cases. */ jnl_file_lost(jpc, status); jnlfile_lost = TRUE; } /* Else journaling got closed concurrently by another process by invoking "jnl_file_lost" * just before we got crit. Do not invoke "jnl_file_lost" again on the same journal file. * Instead continue and next iteration will detect the journal file has switched and terminate. */ if (!was_crit) rel_crit(jpc->region); if (!jnlfile_lost) continue; else { if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; return status; } } if (ERR_JNLWRTDEFER == status) { /* Check if the write was deferred because the instance is frozen. * In that case, wait until the freeze is lifted instead of wasting time spinning on the latch * in jnl_qio. */ assert(!csa->jnlpool || (csa->jnlpool == jnlpool)); WAIT_FOR_REPL_INST_UNFREEZE_SAFE(csa); } if ((ERR_JNLWRTDEFER != status) && (ERR_JNLWRTNOWWRTR != status)) { /* If holding crit, then jnl_sub_write_attempt would have invoked jnl_file_lost which would have * caused the JNL_FILE_SWITCHED check at the beginning of this for loop to succeed and return from * this function so we should never have gotten here. Assert accordingly. If not holding crit, * wait for some crit holder to invoke jnl_file_lost. Until then keep sleep looping indefinitely. * The sleep in this case is not time-limited because the callers of jnl_write_attempt (particularly * jnl_wait) do not check its return value so they assume success returns from this function. It is * non-trivial to change the interface and code of all callers to handle the error situation so we * instead choose to sleep indefinitely here until some crit process encounters the same error and * triggers jnl_file_lost processing which will terminate the loop due to the JNL_FILE_SWITCHED check. */ assert(!csa->now_crit); wcs_sleep(lcnt); } else if (prev_lcnt != lcnt) { assert(1 == lcnt); if ((ERR_JNLWRTDEFER == status) && (JNL_FLUSH_PROG_TRIES <= cnt)) { /* Change of writer */ send_msg_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_JNLFLUSHNOPROG, 2, JNL_LEN_STR(csa->hdr), ERR_TEXT, 2, LEN_AND_LIT("No progress even with multiple writers")); cnt = 0; } } } if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; return SS_NORMAL; } fis-gtm-V7.0-005/sr_port/jnl_write_eof_rec.c0000644000032200000250000000661314342376331017674 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2017 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_inet.h" #include "gtm_time.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "jnl_write.h" #include "repl_msg.h" #include "gtmsource.h" #include "jnl_get_checksum.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnl_gbls_t jgbl; void jnl_write_eof_rec(sgmnt_addrs *csa, struct_jrec_eof *eof_record) { jnl_private_control *jpc; jnlpool_addrs_ptr_t save_jnlpool; assert(!IN_PHASE2_JNL_COMMIT(csa)); jpc = csa->jnl; assert((0 != jpc->pini_addr) || ((off_t)jpc->jnl_buff->rsrv_freeaddr > ((off_t)DISK_BLOCK_SIZE * jpc->jnl_buff->filesize) - JNL_FILE_TAIL_PRESERVE)); eof_record->prefix.jrec_type = JRT_EOF; eof_record->prefix.forwptr = eof_record->suffix.backptr = EOF_RECLEN; eof_record->suffix.suffix_code = JNL_REC_SUFFIX_CODE; eof_record->prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; eof_record->prefix.tn = csa->hdr->trans_hist.curr_tn; eof_record->prefix.checksum = INIT_CHECKSUM_SEED; /* At this point jgbl.gbl_jrec_time should be set by the caller */ assert(jgbl.gbl_jrec_time); eof_record->prefix.time = jgbl.gbl_jrec_time; save_jnlpool = jnlpool; if (csa->jnlpool && (csa->jnlpool == jnlpool)) jnlpool = csa->jnlpool; ASSERT_JNL_SEQNO_FILEHDR_JNLPOOL(csa, jnlpool); /* debug-only sanity check between seqno of csa->hdr and jnlpool */ /* In UNIX, mur_close_files, at the beginning sets both jgbl.mur_jrec_seqno and csa->hdr->reg_seqno to * murgbl.consist_jnl_seqno. Assert that this is indeed the case. However, csa->hdr->reg_seqno is NOT * maintained by rollback during forward phase of recovery and is set only at mur_close_files whereas * jgbl.mur_jrec_seqno is maintained all along. So, unless we are called from mur_close_files, we cannot * rely csa->hdr->reg_seqno and so we can do the equality check only if we are called from mur_close_files */ assert(!jgbl.forw_phase_recovery || !jgbl.mur_rollback || (jgbl.mur_jrec_seqno == csa->hdr->reg_seqno) || !process_exiting); /* If caller is MUPIP JOURNAL ROLLBACK, it cannot be FORWARD rollback since that runs with journaling turned off * and we are writing journal records here. Assert accordingly. */ assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); if (!jgbl.forw_phase_recovery) { if (REPL_ALLOWED(csa)) eof_record->jnl_seqno = csa->hdr->reg_seqno;/* Note we cannot use jnlpool_ctl->jnl_seqno since * we might not presently hold the journal pool lock */ else eof_record->jnl_seqno = 0; } else QWASSIGN(eof_record->jnl_seqno, jgbl.mur_jrec_seqno); eof_record->filler = 0; eof_record->prefix.checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)eof_record, SIZEOF(struct_jrec_eof)); jnl_write(jpc, JRT_EOF, (jnl_record *)eof_record, NULL); if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; } fis-gtm-V7.0-005/sr_port/jnl_write_epoch_rec.c0000644000032200000250000001511714342376331020220 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_fcntl.h" #include "gtm_unistd.h" #include "eintr_wrappers.h" #include "gtm_inet.h" #include "gtm_time.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "jnl_write.h" #include "repl_msg.h" #include "gtmsource.h" #include "gtmio.h" #include "iosp.h" #include "jnl_get_checksum.h" #include "anticipatory_freeze.h" GBLREF jnlpool_addrs_ptr_t jnlpool; GBLREF jnl_gbls_t jgbl; GBLREF seq_num seq_num_zero; GBLREF int4 strm_index; error_def (ERR_PREMATEOF); void jnl_write_epoch_rec(sgmnt_addrs *csa) { struct_jrec_epoch epoch_record; jnl_buffer_ptr_t jb; jnl_private_control *jpc; jnl_file_header *header; jnlpool_addrs_ptr_t save_jnlpool; unsigned char hdr_base[REAL_JNL_HDR_LEN + MAX_IO_BLOCK_SIZE]; sgmnt_data_ptr_t csd; uint4 jnl_fs_block_size, read_write_size; int idx; assert(!IN_PHASE2_JNL_COMMIT(csa)); jpc = csa->jnl; save_jnlpool = jnlpool; if (csa->jnlpool && (csa->jnlpool != jnlpool)) jnlpool = csa->jnlpool; jb = jpc->jnl_buff; assert((csa->ti->early_tn == csa->ti->curr_tn) || (csa->ti->early_tn == csa->ti->curr_tn + 1)); assert(0 != jpc->pini_addr); csd = csa->hdr; epoch_record.prefix.jrec_type = JRT_EPOCH; epoch_record.prefix.forwptr = epoch_record.suffix.backptr = EPOCH_RECLEN; epoch_record.blks_to_upgrd = csd->blks_to_upgrd; epoch_record.total_blks = csd->trans_hist.total_blks; epoch_record.free_blocks = csd->trans_hist.free_blocks; epoch_record.fully_upgraded = csd->fully_upgraded; epoch_record.suffix.suffix_code = JNL_REC_SUFFIX_CODE; /* in case jpc->pini_addr turns out to be zero (not clear how), we use the pini_addr field of the * first PINI journal record in the journal file which is nothing but JNL_HDR_LEN. */ epoch_record.prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; jb->epoch_tn = epoch_record.prefix.tn = csa->ti->curr_tn; /* At this point jgbl.gbl_jrec_time should be set by the caller */ assert(jgbl.gbl_jrec_time); epoch_record.prefix.time = jgbl.gbl_jrec_time; /* we need to write epochs if jgbl.forw_phase_recovery so future recovers will have a closer turnaround point */ jb->next_epoch_time = epoch_record.prefix.time + jb->epoch_interval; epoch_record.prefix.checksum = INIT_CHECKSUM_SEED; ASSERT_JNL_SEQNO_FILEHDR_JNLPOOL(csa, jnlpool); /* debug-only sanity check between seqno of csa->hdr and jnlpool */ if (jgbl.forw_phase_recovery) { /* Set jnl-seqno of epoch record from the current seqno that rollback is playing. Note that in case of -recover * we don't actually care what seqnos get assigned to the epoch record so we go ahead and set it to the same * fields even though those might be 0 or not. */ epoch_record.jnl_seqno = jgbl.mur_jrec_seqno; for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) epoch_record.strm_seqno[idx] = csd->strm_reg_seqno[idx]; /* If MUPIP JOURNAL -ROLLBACK, might need to do additional processing. See macro definition for comments */ MUR_ADJUST_STRM_REG_SEQNO_IF_NEEDED(csd, epoch_record.strm_seqno); } else if (REPL_ALLOWED(csd)) { epoch_record.jnl_seqno = csd->reg_seqno; /* Note we cannot use jnlpool_ctl->jnl_seqno since * we might not presently hold the journal pool lock */ for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) epoch_record.strm_seqno[idx] = csd->strm_reg_seqno[idx]; } else { epoch_record.jnl_seqno = seq_num_zero; for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) epoch_record.strm_seqno[idx] = 0; } if (jb->end_of_data) { jnl_fs_block_size = jb->fs_block_size; header = (jnl_file_header *)(ROUND_UP2((uintszofptr_t)hdr_base, jnl_fs_block_size)); read_write_size = ROUND_UP2(REAL_JNL_HDR_LEN, jnl_fs_block_size); assert((unsigned char *)header + read_write_size <= ARRAYTOP(hdr_base)); DO_FILE_READ(jpc->channel, 0, header, read_write_size, jpc->status, jpc->status2); assert(SS_NORMAL != jpc->status || SS_NORMAL == jpc->status2); if (SS_NORMAL == jpc->status) { header->end_of_data = jb->end_of_data; csa->hdr->jnl_eovtn = header->eov_tn; header->eov_tn = jb->eov_tn; header->eov_timestamp = jb->eov_timestamp; header->end_seqno = jb->end_seqno; /* Keep header->strm_end_seqno[] uptodate as well if applicable */ if (INVALID_SUPPL_STRM != strm_index) { for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) header->strm_end_seqno[idx] = jb->strm_end_seqno[idx]; } JNL_DO_FILE_WRITE(csa, csd->jnl_file_name, jpc->channel, 0, header, read_write_size, jpc->status, jpc->status2); /* for abnormal status do not do anything. journal file header will have previous end_of_data */ } } jb->end_of_data = jb->rsrv_freeaddr; jb->eov_tn = csa->ti->curr_tn; jb->eov_timestamp = jgbl.gbl_jrec_time; jb->end_seqno = epoch_record.jnl_seqno; /* Keep header->strm_end_seqno[] uptodate as well if applicable */ if (INVALID_SUPPL_STRM != strm_index) { /* ROLLBACK turns off replication in mur_close_files and so we should never come here with csd->repl_state * indicating replication is allowed (for ROLLBACK). The only exception is if we are done with ROLLBACK, * but invoked gds_rundown (from mur_close_files) in which case repl_state will be turned back ON by * mur_close_files (process_exiting = TRUE). Assert accordingly */ assert(!jgbl.mur_rollback || !REPL_ALLOWED(csd) || process_exiting); assert(jgbl.mur_rollback || REPL_ALLOWED(csd)); /* If caller is MUPIP JOURNAL ROLLBACK, it cannot be FORWARD rollback since that runs with journaling * turned off and we are writing journal records in this function. Assert accordingly. */ assert(!jgbl.mur_rollback || !jgbl.mur_options_forward); for (idx = 0; idx < MAX_SUPPL_STRMS; idx++) jb->strm_end_seqno[idx] = csd->strm_reg_seqno[idx]; } epoch_record.filler0 = 0; epoch_record.filler1 = 0; epoch_record.prefix.checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)&epoch_record, SIZEOF(struct_jrec_epoch)); jnl_write(jpc, JRT_EPOCH, (jnl_record *)&epoch_record, NULL); jb->post_epoch_freeaddr = jb->rsrv_freeaddr; if (save_jnlpool != jnlpool) jnlpool = save_jnlpool; } fis-gtm-V7.0-005/sr_port/jnl_write_extend_if_needed.c0000644000032200000250000001060414342376331021536 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2017 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 "gdsroot.h" #include "gtm_facility.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "jnl_write.h" #include "jnl_get_checksum.h" GBLREF boolean_t in_jnl_file_autoswitch; /* Success : returns 0 * Failure : returns -1 */ int jnl_write_extend_if_needed(int4 jrec_len, jnl_buffer_ptr_t jb, uint4 lcl_freeaddr, sgmnt_addrs *csa, enum jnl_record_type rectype, blk_hdr_ptr_t blk_ptr, jnl_format_buffer *jfb, gd_region *reg, jnl_private_control *jpc, jnl_record *jnl_rec) { int4 jrec_len_padded; int4 blocks_needed; boolean_t do_extend; /* Before writing a journal record, check if we have some padding space to close the journal file in case we * are on the verge of an autoswitch. If we are about to autoswitch the journal file at this point, don't do * the padding check since the padding space has already been checked in jnl_write calls before this autoswitch * invocation. We can safely write the input record without worrying about autoswitch limit overflow. */ jrec_len_padded = jrec_len; if (!in_jnl_file_autoswitch) jrec_len_padded = jrec_len + JNL_FILE_TAIL_PRESERVE; blocks_needed = DISK_BLOCKS_SUM(lcl_freeaddr, jrec_len_padded); do_extend = jb->last_eof_written || (jb->filesize < blocks_needed); if (do_extend) { /* not enough room in jnl file, extend it */ /* We should never reach here if we are called from t_end/tp_tend. We check that by using the fact that * early_tn is different from curr_tn in the t_end/tp_tend case. The only exception is wcs_recover which * also sets these to be different in case of writing an INCTN record. For this case though it is okay to * extend/autoswitch the file. So allow that. */ if (!jb->last_eof_written) { assertpro((csa->ti->early_tn == csa->ti->curr_tn) || (JRT_INCTN == rectype)); assert(!IS_REPLICATED(rectype)); /* all replicated jnl records should have gone through t_end/tp_tend */ assert(jrt_fixed_size[rectype]); /* this is used later in re-computing checksums */ } assert(NULL == blk_ptr); /* as otherwise it is a PBLK or AIMG record which is of variable record * length that conflicts with the immediately above assert. */ assert(NULL == jfb); /* as otherwise it is a logical record with formatted journal records which * is of variable record length (conflicts with the jrt_fixed_size assert). */ assertpro(!in_jnl_file_autoswitch); /* avoid recursion of jnl_file_extend */ if (SS_NORMAL != jnl_flush(reg)) { assert(NOJNL == jpc->channel); /* jnl file lost */ return -1; /* let the caller handle the error */ } assert(lcl_freeaddr == jb->dskaddr); if (EXIT_ERR == jnl_file_extend(jpc, jrec_len)) return -1; /* let the caller handle the error */ assert(!jb->last_eof_written); if (0 == jpc->pini_addr) { /* This can happen only if jnl got switched in jnl_file_extend above. * Write a PINI record in the new journal file and then continue writing the input record. * Basically we need to redo the processing in jnl_write because a lot of the local variables * have changed state (e.g. JB->freeaddr etc.). So we instead call "jnl_write" * recursively and then return immediately. */ jnl_write_pini(csa); assertpro(jpc->pini_addr); /* should have been set in "jnl_write_pini" */ if (JRT_PINI != rectype) { assert(JRT_ALIGN != rectype); /* need this assert so it is safe to do "prefix.pini_addr" below */ jnl_rec->prefix.pini_addr = jpc->pini_addr; /* Checksum needs to be recomputed since prefix.pini_addr is changed in above statement */ jnl_rec->prefix.checksum = INIT_CHECKSUM_SEED; jnl_rec->prefix.checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)jnl_rec, jnl_rec->prefix.forwptr); jnl_write(jpc, rectype, jnl_rec, NULL); } return -1; /* let the caller handle the error */ } } return 0; } fis-gtm-V7.0-005/sr_port/jnl_write_inctn_rec.c0000644000032200000250000000645014342376331020235 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_inet.h" #include "gtm_time.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "jnl_write.h" #include "repl_msg.h" #include "gtmsource.h" #include "jnl_get_checksum.h" GBLREF jnl_gbls_t jgbl; GBLREF inctn_opcode_t inctn_opcode; GBLREF inctn_detail_t inctn_detail; /* holds detail to fill in to inctn jnl record */ void jnl_write_inctn_rec(sgmnt_addrs *csa) { struct_jrec_inctn inctn_record; jnl_private_control *jpc; boolean_t in_phase2; DEBUG_ONLY(int inctn_detail_size;) jpc = csa->jnl; assert(0 != jpc->pini_addr); inctn_record.prefix.jrec_type = JRT_INCTN; inctn_record.prefix.forwptr = INCTN_RECLEN; assert(&inctn_detail.blknum_struct.suffix == &inctn_detail.blks2upgrd_struct.suffix); DEBUG_ONLY(inctn_detail_size = OFFSETOF(inctn_detail_blknum_t, suffix) + SIZEOF(inctn_detail.blknum_struct.suffix);) assert(0 == (inctn_detail_size % JNL_REC_START_BNDRY)); assert(SIZEOF(inctn_detail) == inctn_detail_size); inctn_detail.blknum_struct.suffix.backptr = INCTN_RECLEN; inctn_detail.blknum_struct.suffix.suffix_code = JNL_REC_SUFFIX_CODE; inctn_record.prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; /* At this point jgbl.gbl_jrec_time should be set by the caller */ assert(jgbl.gbl_jrec_time); inctn_record.prefix.time = jgbl.gbl_jrec_time; in_phase2 = IN_PHASE2_JNL_COMMIT(csa); assert(in_phase2 || (csa->ti->early_tn == csa->ti->curr_tn) || (csa->ti->early_tn == csa->ti->curr_tn + 1)); inctn_record.prefix.tn = JB_CURR_TN_APPROPRIATE(in_phase2, jpc, csa); inctn_record.prefix.checksum = INIT_CHECKSUM_SEED; assert((inctn_opcode_total > inctn_opcode) && (inctn_invalid_op < inctn_opcode)); /* Assert that the maximum inctn opcode # will fit in the "opcode" field in the inctn jnl record. * But before that, assert opcode is at same offset in all the individual inctn_detail_* structure types. */ assert(&inctn_detail.blknum_struct.opcode == &inctn_detail.blks2upgrd_struct.opcode); assert(inctn_opcode_total < (1 << (8 * SIZEOF(inctn_detail.blknum_struct.opcode)))); inctn_detail.blknum_struct.opcode = inctn_opcode; /* fill in opcode from the global variable */ /* Instead of having a multi-line switch statement that copies exactly those fields which are necessary, we * copy the entire structure (16 bytes at this point). Pipeline breaks are considered more costly than a few * unnecessary memory-to-memory copies. */ inctn_detail.blknum_struct.filler_short = 0; inctn_record.detail = inctn_detail; inctn_record.prefix.checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)&inctn_record, SIZEOF(struct_jrec_inctn)); jnl_write(jpc, JRT_INCTN, (jnl_record *)&inctn_record, NULL); } fis-gtm-V7.0-005/sr_port/jnl_write_logical.c0000644000032200000250000000752414342376331017706 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_inet.h" #ifdef VMS #include /* Required for gtmsource.h */ #endif #include "gtm_time.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "jnl_write.h" #include "repl_msg.h" #include "gtmsource.h" #include "iosp.h" #include "jnl_get_checksum.h" #ifdef DEBUG #include "jnl_typedef.h" #include "wbox_test_init.h" #endif GBLREF jnl_fence_control jnl_fence_ctl; GBLREF uint4 dollar_tlevel; GBLREF jnl_gbls_t jgbl; GBLREF seq_num seq_num_zero; /* This called for TP and non-TP, but not for ZTP */ void jnl_write_logical(sgmnt_addrs *csa, jnl_format_buffer *jfb, uint4 com_csum) { struct_jrec_upd *jrec; struct_jrec_null *jrec_null; struct_jrec_upd *jrec_alt; jnl_private_control *jpc; boolean_t in_phase2; /* If REPL_WAS_ENABLED(csa) is TRUE, then we would not have gone through the code that initializes * jgbl.gbl_jrec_time or jpc->pini_addr. But in this case, we are not writing the journal record * to the journal buffer or journal file but write it only to the journal pool from where it gets * sent across to the update process that does not care about these fields so it is ok to leave them as is. */ jpc = csa->jnl; assert((0 != jpc->pini_addr) || REPL_WAS_ENABLED(csa) || (gtm_white_box_test_case_enabled && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number))); assert(jgbl.gbl_jrec_time || REPL_WAS_ENABLED(csa)); assert(IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(jfb->rectype) || (JRT_NULL == jfb->rectype)); assert(!IS_ZTP(jfb->rectype)); jrec = (struct_jrec_upd *)jfb->buff; assert(OFFSETOF(struct_jrec_null, prefix) == OFFSETOF(struct_jrec_upd, prefix)); assert(SIZEOF(jrec_null->prefix) == SIZEOF(jrec->prefix)); jrec->prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; in_phase2 = IN_PHASE2_JNL_COMMIT(csa); jrec->prefix.tn = JB_CURR_TN_APPROPRIATE(in_phase2, jpc, csa); jrec->prefix.time = jgbl.gbl_jrec_time; /* t_end/tp_tend/mur_output_record has already set token/jnl_seqno into jnl_fence_ctl.token */ assert((0 != jnl_fence_ctl.token) || (!dollar_tlevel && !jgbl.forw_phase_recovery && !REPL_ENABLED(csa)) || (!dollar_tlevel && jgbl.forw_phase_recovery && (repl_open != csa->hdr->intrpt_recov_repl_state))); assert(OFFSETOF(struct_jrec_null, jnl_seqno) == OFFSETOF(struct_jrec_upd, token_seq)); assert(SIZEOF(jrec_null->jnl_seqno) == SIZEOF(jrec->token_seq)); jrec->token_seq.token = jnl_fence_ctl.token; assert(OFFSETOF(struct_jrec_null, strm_seqno) == OFFSETOF(struct_jrec_upd, strm_seqno)); assert(SIZEOF(jrec_null->strm_seqno) == SIZEOF(jrec->strm_seqno)); jrec->strm_seqno = jnl_fence_ctl.strm_seqno; /* update checksum below */ if (JRT_NULL != jrec->prefix.jrec_type) COMPUTE_LOGICAL_REC_CHECKSUM(jfb->checksum, jrec, com_csum, jrec->prefix.checksum); else jrec->prefix.checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)jrec, SIZEOF(struct_jrec_null)); if (REPL_ALLOWED(csa) && USES_ANY_KEY(csa->hdr)) { jrec_alt = (struct_jrec_upd *)jfb->alt_buff; jrec_alt->prefix = jrec->prefix; jrec_alt->token_seq = jrec->token_seq; jrec_alt->strm_seqno = jrec->strm_seqno; jrec_alt->num_participants = jrec->num_participants; } JNL_WRITE_APPROPRIATE(csa, jpc, jfb->rectype, (jnl_record *)jrec, jfb); } fis-gtm-V7.0-005/sr_port/jnl_write_multi_align_rec.c0000644000032200000250000000467314342376331021433 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2017 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" void jnl_write_multi_align_rec(sgmnt_addrs *csa, uint4 align_filler_len, jnl_tm_t time) { jnl_buffer_ptr_t jbp; uint4 alignsize, max_filler_len, max_jrec_len, orig_align_filler_len; uint4 filler_len1, filler_len2; jbp = csa->jnl->jnl_buff; alignsize = jbp->alignsize; max_jrec_len = jbp->max_jrec_len; assert(max_jrec_len < alignsize); max_filler_len = max_jrec_len - MIN_ALIGN_RECLEN; DEBUG_ONLY(orig_align_filler_len = align_filler_len); while (align_filler_len > max_jrec_len) { jnl_write_align_rec(csa, max_filler_len, time); /* this will write an ALIGN record of size * MIN_ALIGN_RECLEN + max_filler_len == max_jrec_len. */ align_filler_len -= max_jrec_len; } /* At this point, "align_filler_len <= max_jrec_len" */ if (max_filler_len >= align_filler_len) { /* Simple case. Write one JRT_ALIGN and return */ jnl_write_align_rec(csa, align_filler_len, time); return; } /* At this point, "max_filler_len < align_filler_len <= max_jrec_len". This is an edge case. * We need to write two ALIGN records to ensure that * a) Record size of each of the two ALIGN records stays within "max_jrec_len" AND * b) Total size of the two ALIGN records == align_filler_len + MIN_ALIGN_RECLEN; */ assert(MIN_ALIGN_RECLEN < max_filler_len); filler_len1 = max_filler_len - MIN_ALIGN_RECLEN; /* align rec_size1 = filler_len1 + MIN_ALIGN_RECLEN == max_filler_len */ filler_len2 = align_filler_len - max_filler_len; /* align rec_size2 = filler_len2 + MIN_ALIGN_RECLEN */ /* rec_size1 + rec_size2 = max_filler_len + ((align_filler_len - max_filler_len) + MIN_ALIGN_RECLEN) * == align_filler_len + MIN_ALIGN_RECLEN */ jnl_write_align_rec(csa, filler_len1, time); jnl_write_align_rec(csa, filler_len2, time); return; } fis-gtm-V7.0-005/sr_port/jnl_write_pblk.c0000644000032200000250000000733714342376331017226 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 #include "gtm_string.h" #include "gtm_time.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "jnl.h" #include "jnl_write.h" #include "jnl_write_pblk.h" #include "min_max.h" #include "jnl_get_checksum.h" GBLREF jnl_gbls_t jgbl; GBLREF boolean_t dse_running; void jnl_write_pblk(sgmnt_addrs *csa, cw_set_element *cse, uint4 com_csum) { blk_hdr_ptr_t old_block; int tmp_jrec_size; jnl_buffer_ptr_t jbp; jnl_private_control *jpc; sgmnt_data_ptr_t csd; struct_jrec_blk pblk_record; unsigned int bsiz; # ifdef DEBUG blk_hdr_ptr_t save_old_block; # endif csd = csa->hdr; assert(IN_PHASE2_JNL_COMMIT(csa)); old_block = (blk_hdr_ptr_t)cse->old_block; ASSERT_IS_WITHIN_SHM_BOUNDS((sm_uc_ptr_t)old_block, csa); /* Assert that cr corresponding to old_block is pinned in shared memory */ DBG_ENSURE_OLD_BLOCK_IS_VALID(cse, (dba_mm == csd->acc_meth), csa, csd); bsiz = old_block->bsiz; bsiz = MIN(bsiz, csd->blk_size); /* be safe in PRO */ if (!cse->blk_checksum) cse->blk_checksum = jnl_get_checksum(old_block, csa, bsiz); else assert(cse->blk_checksum == jnl_get_checksum(old_block, csa, bsiz)); if (NEEDS_ANY_KEY(csd, old_block->tn)) { DBG_ENSURE_PTR_IS_VALID_GLOBUFF(csa, csd, (sm_uc_ptr_t)old_block); DEBUG_ONLY(save_old_block = old_block;) old_block = (blk_hdr_ptr_t)GDS_ANY_ENCRYPTGLOBUF(old_block, csa); /* Ensure that unencrypted block and its twin counterpart are in sync. */ assert(save_old_block->tn == old_block->tn); assert(save_old_block->bsiz == old_block->bsiz); assert(save_old_block->levl == old_block->levl); DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(csa, csd, (sm_uc_ptr_t)old_block); } jpc = csa->jnl; assert((0 != jpc->pini_addr) || ((!JNL_ENABLED(csd)) && (JNL_ENABLED(csa)))); pblk_record.prefix.jrec_type = JRT_PBLK; pblk_record.prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; pblk_record.prefix.tn = JB_CURR_TN_APPROPRIATE(TRUE, jpc, csa); /* At this point jgbl.gbl_jrec_time should be set by the caller */ assert(jgbl.gbl_jrec_time); pblk_record.prefix.time = jgbl.gbl_jrec_time; pblk_record.blknum = cse->blk; /* in case we have a bad block-size, we don't want to write a PBLK larger than the GDS block size (maximum block size). * in addition, check that checksum computed in t_end/tp_tend did take the adjusted bsiz into consideration. */ assert(old_block->bsiz <= csd->blk_size || dse_running); pblk_record.bsiz = bsiz; assert((pblk_record.bsiz == old_block->bsiz) || (cse->blk_checksum == jnl_get_checksum(old_block, NULL, pblk_record.bsiz))); assert(pblk_record.bsiz >= SIZEOF(blk_hdr) || dse_running); pblk_record.ondsk_blkver = cse->ondsk_blkver; tmp_jrec_size = (int)FIXED_PBLK_RECLEN + pblk_record.bsiz + JREC_SUFFIX_SIZE; pblk_record.prefix.forwptr = ROUND_UP2(tmp_jrec_size, JNL_REC_START_BNDRY); COMPUTE_PBLK_CHECKSUM(cse->blk_checksum, &pblk_record, com_csum, pblk_record.prefix.checksum); assert(SIZEOF(uint4) == SIZEOF(jrec_suffix)); jnl_write(jpc, JRT_PBLK, (jnl_record *)&pblk_record, old_block); jbp = jpc->jnl_buff; cse->jnl_freeaddr = JB_FREEADDR_APPROPRIATE(TRUE, jpc, jbp); } fis-gtm-V7.0-005/sr_port/jnl_write_pblk.h0000644000032200000250000000137514342376331017227 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef JNL_WRITE_PBLK_H_INCLUDED #define JNL_WRITE_PBLK_H_INCLUDED /* We do not put this in jnl.h, because it needs all including jnl.h must include gdsblk.h */ void jnl_write_pblk(sgmnt_addrs *csa, cw_set_element *cse, uint4 com_csum); #endif fis-gtm-V7.0-005/sr_port/jnl_write_pfin.c0000644000032200000250000000345114342376331017223 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_string.h" #include "gtm_time.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "jnl_write.h" #include "jnl_get_checksum.h" GBLREF jnl_gbls_t jgbl; void jnl_write_pfin(sgmnt_addrs *csa) { struct_jrec_pfin pfin_record; jnl_private_control *jpc; assert(!IN_PHASE2_JNL_COMMIT(csa)); jpc = csa->jnl; assert((0 != jpc->pini_addr) || (gtm_white_box_test_case_enabled && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number))); pfin_record.prefix.jrec_type = JRT_PFIN; pfin_record.prefix.forwptr = pfin_record.suffix.backptr = PFIN_RECLEN; pfin_record.suffix.suffix_code = JNL_REC_SUFFIX_CODE; pfin_record.prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; pfin_record.prefix.tn = csa->ti->curr_tn; /* At this point jgbl.gbl_jrec_time should be set by the caller */ assert(jgbl.gbl_jrec_time); pfin_record.prefix.time = jgbl.gbl_jrec_time; pfin_record.prefix.checksum = INIT_CHECKSUM_SEED; pfin_record.filler = 0; pfin_record.prefix.checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)&pfin_record, SIZEOF(struct_jrec_pfin)); jnl_write(jpc, JRT_PFIN, (jnl_record *)&pfin_record, NULL); } fis-gtm-V7.0-005/sr_port/jnl_write_phase2.c0000644000032200000250000001506714342376331017457 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2017 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "jnl.h" #include "jnl_typedef.h" #include "jnl_get_checksum.h" #include "jnl_write_pblk.h" #include "jnl_write_aimg_rec.h" #include "jnl_write.h" GBLREF jnl_gbls_t jgbl; GBLREF uint4 process_id; void jnl_write_phase2(sgmnt_addrs *csa, jbuf_rsrv_struct_t *jrs) { boolean_t need_pini_adjustment; /* TRUE => first ALIGN needs to set pini_addr of JNL_FILE_FIRST_RECORD */ cw_set_element *cse; enum jnl_record_type rectype; jnl_buffer_ptr_t jbp; jnl_format_buffer *jfb; jnl_private_control *jpc; struct_jrec_tcom *tcom_record_ptr; jrec_rsrv_elem_t *first_jre, *jre, *jre_top; uint4 common_csum, pini_addr, reclen, freeaddr, prev_freeaddr, start_freeaddr; void *param1; jbuf_phase2_in_prog_t *phs2cmt; int commit_index; boolean_t write_to_jnlbuff, write_to_jnlpool; assert(JNL_ALLOWED(csa)); assert(NULL != jrs); assert(jrs->tot_jrec_len); jpc = csa->jnl; common_csum = 0; write_to_jnlbuff = JNL_ENABLED(csa); assert(write_to_jnlbuff || REPL_ALLOWED(csa)); first_jre = jrs->jrs_array; jre_top = first_jre + jrs->usedlen; /* Note: In REPL_WAS_ALLOWED case, "write_to_jnlbuff" will be FALSE and so pini_addr etc. won't be initialized in * the logical jnl records we write to the jnlpool. But that is okay since the update process does not care about * these fields in the jnl record anyways. Also see similar comment in "jnl_write_logical" */ if (write_to_jnlbuff) { jbp = jpc->jnl_buff; ADJUST_CHECKSUM_TN(INIT_CHECKSUM_SEED, &jpc->curr_tn, common_csum); pini_addr = jpc->pini_addr; need_pini_adjustment = FALSE; commit_index = jrs->phase2_commit_index; phs2cmt = &jbp->phase2_commit_array[commit_index]; start_freeaddr = phs2cmt->start_freeaddr; # ifdef DEBUG assert(start_freeaddr >= jbp->freeaddr); assert(start_freeaddr < jbp->rsrv_freeaddr); assert(process_id == phs2cmt->process_id); assert(!phs2cmt->write_complete); assert(jpc->phase2_freeaddr == start_freeaddr + phs2cmt->tot_jrec_len); # endif if (!pini_addr) { /* PINI record would have been written as the first in this transaction in that case. * Use the corresponding freeaddr as the pini_addr for the checksum computation. * There is an exception in that an ALIGN record might have been written before the PINI * record. Thankfully an ALIGN record does not have a pini_addr so no need to worry about that. */ rectype = first_jre->rectype; pini_addr = start_freeaddr; if (JRT_PINI != rectype) { assert(JRT_ALIGN == rectype); assert(&first_jre[1] < jre_top); assert(JRT_PINI == first_jre[1].rectype); pini_addr += first_jre->reclen; need_pini_adjustment = TRUE; } } assert(pini_addr); ADJUST_CHECKSUM(common_csum, pini_addr, common_csum); ADJUST_CHECKSUM(common_csum, jgbl.gbl_jrec_time, common_csum); SET_JPC_PHASE2_FREEADDR(jpc, jbp, start_freeaddr); /* needed by "jnl_write_*" calls below */ } for (jre = first_jre; jre < jre_top; jre++) { # ifdef DEBUG if (write_to_jnlbuff) prev_freeaddr = jpc->phase2_freeaddr; # endif rectype = jre->rectype; reclen = jre->reclen; param1 = jre->param1; switch (rectype) { case JRT_ALIGN: assert(write_to_jnlbuff); assert((jre == first_jre) || jpc->pini_addr); jnl_write_align_rec(csa, reclen - MIN_ALIGN_RECLEN, jgbl.gbl_jrec_time); break; case JRT_PINI: assert(write_to_jnlbuff); assert(PINI_RECLEN == reclen); jnl_write_pini(csa); break; case JRT_PBLK: assert(write_to_jnlbuff); assert(reclen <= ROUND_UP2(FIXED_PBLK_RECLEN + JREC_SUFFIX_SIZE + csa->hdr->blk_size, JNL_REC_START_BNDRY)); cse = (cw_set_element *)param1; jnl_write_pblk(csa, cse, common_csum); break; case JRT_AIMG: assert(write_to_jnlbuff); assert(reclen <= ROUND_UP2(FIXED_AIMG_RECLEN + JREC_SUFFIX_SIZE + csa->hdr->blk_size, JNL_REC_START_BNDRY)); cse = (cw_set_element *)param1; jnl_write_aimg_rec(csa, cse, common_csum); break; case JRT_INCTN: assert(write_to_jnlbuff); assert(INCTN_RECLEN == reclen); jnl_write_inctn_rec(csa); break; case JRT_TCOM: assert(TCOM_RECLEN == reclen); tcom_record_ptr = (struct_jrec_tcom *)param1; /* Note: tcom_record_ptr->prefix.time, num_participants, token_seq.token and strm_seqno are already set * in tp_tend and is common across all participating TP regions. */ tcom_record_ptr->prefix.pini_addr = jpc->pini_addr; tcom_record_ptr->prefix.tn = jpc->curr_tn; tcom_record_ptr->prefix.checksum = INIT_CHECKSUM_SEED; tcom_record_ptr->prefix.checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)tcom_record_ptr, SIZEOF(struct_jrec_tcom)); JNL_WRITE_APPROPRIATE(csa, jpc, JRT_TCOM, (jnl_record *)tcom_record_ptr, NULL); break; default: assert(IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(rectype) || (JRT_NULL == rectype)); jfb = param1; assert(rectype == jfb->rectype); assert(reclen == jfb->record_size); if (!IS_ZTP(rectype)) jnl_write_logical(csa, jfb, common_csum); else { assert(write_to_jnlbuff); jnl_write_ztp_logical(csa, jfb, common_csum, 0); } break; } /* It is possible one of the "jnl_write_*" calls above encounters a "jnl_write_attempt -> jnl_file_lost" * scenario. In that case jpc->channel would be set to NOJNL. Take that into account in the assert below. * It is okay to finish the "for" loop and make more "jnl_write_*" calls instead of adding "if" checks * to address this rare scenario. It is just wasted effort in a rare case but should be a safe invocation. */ assert(!write_to_jnlbuff || (jpc->phase2_freeaddr == (prev_freeaddr + reclen)) || (NOJNL == jpc->channel)); } if (write_to_jnlbuff) { DEBUG_ONLY(freeaddr = jpc->phase2_freeaddr;) assert((freeaddr > jbp->freeaddr) || (csa->now_crit && (freeaddr == jbp->freeaddr))); assert(freeaddr <= jbp->rsrv_freeaddr); JNL_PHASE2_WRITE_COMPLETE(csa, jbp, commit_index, freeaddr); } jrs->tot_jrec_len = 0; /* reset needed to prevent duplicate calls to "jnl_write_phase2" for same curr_tn */ } fis-gtm-V7.0-005/sr_port/jnl_write_pini.c0000644000032200000250000001041714342376331017226 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_string.h" #include "gtm_time.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "jnl_write.h" #include "gtmimagename.h" #include "jnl_get_checksum.h" #include "buddy_list.h" /* needed for muprec.h */ #include "hashtab_int4.h" /* needed for muprec.h */ #include "hashtab_int8.h" /* needed for muprec.h */ #include "hashtab_mname.h" /* needed for muprec.h */ #include "muprec.h" GBLREF jnl_fence_control jnl_fence_ctl; GBLREF jnl_process_vector *prc_vec; GBLREF jnl_process_vector *originator_prc_vec; GBLREF jnl_gbls_t jgbl; GBLREF boolean_t exit_handler_active; GBLREF int process_exiting; GBLREF boolean_t is_src_server; void jnl_write_pini(sgmnt_addrs *csa) { struct_jrec_pini pini_record; jnl_private_control *jpc; jnl_buffer_ptr_t jbp; struct pini_list *mur_plst; boolean_t in_phase2; jpc = csa->jnl; jbp = jpc->jnl_buff; assert(prc_vec); pini_record.prefix.jrec_type = JRT_PINI; pini_record.prefix.forwptr = pini_record.suffix.backptr = PINI_RECLEN; pini_record.suffix.suffix_code = JNL_REC_SUFFIX_CODE; in_phase2 = IN_PHASE2_JNL_COMMIT(csa); pini_record.prefix.pini_addr = JB_FREEADDR_APPROPRIATE(in_phase2, jpc, jbp); /* in case an ALIGN record is written before the PINI record in "jnl_write", pini_addr above is updated appropriately. */ assert(in_phase2 || (csa->ti->early_tn == csa->ti->curr_tn) || (csa->ti->early_tn == csa->ti->curr_tn + 1)); pini_record.prefix.tn = JB_CURR_TN_APPROPRIATE(in_phase2, jpc, csa); /* At this point jgbl.gbl_jrec_time should be set by the caller */ assert(jgbl.gbl_jrec_time); pini_record.prefix.time = jgbl.gbl_jrec_time; JNL_WHOLE_FROM_SHORT_TIME(prc_vec->jpv_time, jgbl.gbl_jrec_time); pini_record.prefix.checksum = INIT_CHECKSUM_SEED; /* Note that only pini_record.prefix.time is considered in mupip journal command processing. * prc_vec->jpv_time is for accounting purpose only. Usually it is kind of redundant too. */ if (!jgbl.forw_phase_recovery) { assert((NULL == jgbl.mur_pini_addr_reset_fnptr) || (IS_MUPIP_IMAGE && exit_handler_active && process_exiting)); assert((NULL == csa->miscptr) || IS_DSE_IMAGE || (IS_MUPIP_IMAGE && exit_handler_active && process_exiting) || is_src_server); mur_plst = NULL; if (IS_GTCM_GNP_SERVER_IMAGE && (NULL != originator_prc_vec)) { memcpy((unsigned char*)&pini_record.process_vector[ORIG_JPV], (unsigned char*)originator_prc_vec, SIZEOF(jnl_process_vector)); } else memset((unsigned char*)&pini_record.process_vector[ORIG_JPV], 0, SIZEOF(jnl_process_vector)); } else { assert(NULL != csa->miscptr); mur_plst = ((reg_ctl_list *)csa->miscptr)->mur_plst; if (NULL != jgbl.mur_pini_addr_reset_fnptr) { memcpy((unsigned char*)&pini_record.process_vector[ORIG_JPV], (unsigned char *)&mur_plst->origjpv, SIZEOF(jnl_process_vector)); } else { /* gdsfilext done during "mur_block_count_correct" */ assert(NULL == mur_plst); memset((unsigned char*)&pini_record.process_vector[ORIG_JPV], 0, SIZEOF(jnl_process_vector)); } } memcpy((unsigned char*)&pini_record.process_vector[CURR_JPV], (unsigned char*)prc_vec, SIZEOF(jnl_process_vector)); pini_record.filler = 0; pini_record.prefix.checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)&pini_record, SIZEOF(struct_jrec_pini)); jnl_write(jpc, JRT_PINI, (jnl_record *)&pini_record, NULL); /* Note : jpc->pini_addr should not be updated until PINI record is written [C9D08-002376] */ jpc->pini_addr = JB_FREEADDR_APPROPRIATE(in_phase2, jpc, jbp) - PINI_RECLEN; assert(jgbl.forw_phase_recovery || (NULL == mur_plst)); if (NULL != mur_plst) mur_plst->new_pini_addr = jpc->pini_addr;/* note down for future forward play logical record processing */ } fis-gtm-V7.0-005/sr_port/jnl_write_reserve.c0000644000032200000250000001157214342376331017745 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2017 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 "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "gdscc.h" #include "jnl.h" #include "jnl_typedef.h" #ifdef DEBUG #include "gdsbml.h" #endif #define ADD_JREC_RSRV_ELEM(JPC, WRITE_TO_JNLBUFF, JRS, RECTYPE, RECLEN, PARAM1) \ { \ jrec_rsrv_elem_t *jre; \ uint4 alloclen, usedlen; \ \ alloclen = JRS->alloclen; \ usedlen = JRS->usedlen; \ assert(usedlen <= alloclen); \ assert(usedlen || !JRS->tot_jrec_len); \ assert(!usedlen || JRS->tot_jrec_len); \ if (usedlen >= alloclen) \ { /* Allocated space is not enough. Expand by allocating twice as much \ * each time (to reduce # of expansion attempts). \ */ \ jre = JRS->jrs_array; \ alloclen = (!alloclen ? INIT_NUM_JREC_RSRV_ELEMS : alloclen * 2); \ JRS->jrs_array = (jrec_rsrv_elem_t *)malloc(alloclen * SIZEOF(jrec_rsrv_elem_t)); \ JRS->alloclen = alloclen; \ if (NULL != jre) \ { \ memcpy(JRS->jrs_array, jre, usedlen * SIZEOF(jrec_rsrv_elem_t)); \ free(jre); \ } \ } \ assert(usedlen < alloclen); \ jre = &JRS->jrs_array[usedlen]; \ jre->rectype = RECTYPE; \ jre->reclen = RECLEN; \ jre->param1 = PARAM1; \ usedlen++; \ JRS->usedlen = usedlen; \ JRS->tot_jrec_len += RECLEN; \ assert(0 == (JRS->tot_jrec_len % JNL_REC_START_BNDRY)); \ if (WRITE_TO_JNLBUFF) \ JPC->phase2_freeaddr += RECLEN; \ } /* This function reserves space in the journal buffer and the journal pool for the input journal record. * This is called when we hold crit from t_end/tp_tend. Once the callers release crit, they will invoke * the appropriate jnl_write_* function to copy the journal records from private buffers onto the reserved * space in shared memory. This way we minimize holding crit while doing the journal record copy. * If the database is in the WAS_ON state (REPL_WAS_ENABLED is TRUE), then the reservation happens only * in the journal pool, not in the journal buffers. */ void jnl_write_reserve(sgmnt_addrs *csa, jbuf_rsrv_struct_t *jbuf_rsrv_ptr, enum jnl_record_type rectype, uint4 reclen, void *param1) { boolean_t write_to_jnlbuff; jnl_buffer_ptr_t jbp; jnl_private_control *jpc; uint4 align_reclen, lcl_freeaddr; # ifdef DEBUG boolean_t write_to_jnlpool; # endif assert((JRT_PINI == rectype) || (JRT_PBLK == rectype) || (JRT_AIMG == rectype) || (JRT_INCTN == rectype) || (JRT_TCOM == rectype) || IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(rectype) || (JRT_NULL == rectype)); assert(JNL_ALLOWED(csa)); assert(csa->now_crit); assert(0 == (reclen % JNL_REC_START_BNDRY)); jpc = csa->jnl; jbp = jpc->jnl_buff; assert(JNL_WRITE_LOGICAL_RECS(csa)); write_to_jnlbuff = JNL_ENABLED(csa); DEBUG_ONLY(write_to_jnlpool = REPL_ALLOWED(csa);) assert(write_to_jnlbuff || write_to_jnlpool); if (write_to_jnlbuff) { lcl_freeaddr = jpc->phase2_freeaddr; if (lcl_freeaddr + reclen > jbp->next_align_addr) { assert(jbp->next_align_addr >= lcl_freeaddr); align_reclen = (jbp->next_align_addr - lcl_freeaddr) + MIN_ALIGN_RECLEN; assert(align_reclen < jbp->alignsize); ADD_JREC_RSRV_ELEM(jpc, write_to_jnlbuff, jbuf_rsrv_ptr, JRT_ALIGN, align_reclen, param1); jbp->next_align_addr += jbp->alignsize; } } ADD_JREC_RSRV_ELEM(jpc, write_to_jnlbuff, jbuf_rsrv_ptr, rectype, reclen, param1); /* Since this function reserves space for the input journal record in the current journal file, we expect * no journal file switches to happen while writing all journal records of the current transaction in this * region. If there was not enough space in the journal file, we expect t_end/tp_tend (callers of this function) * to have done a "jnl_file_extend" as appropriate and if extension/autoswitch failed (due to permissions etc.) * to have set jbp->last_eof_written to TRUE and transferred control to an error handler instead of proceeding * with the transaction commit. So assert this field is FALSE. The only exception is if we are in the WAS_ON * state in which case this function is invoked to only write journal records to the journal pool and not to * the journal file. */ assert(!jbp->last_eof_written || !write_to_jnlbuff); } fis-gtm-V7.0-005/sr_port/jnl_write_trunc_rec.c0000644000032200000250000000364714342376331020262 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2012-2020 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_inet.h" #include "gtm_time.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "jnl_write.h" #include "repl_msg.h" #include "gtmsource.h" #include "jnl_get_checksum.h" GBLREF jnl_gbls_t jgbl; GBLREF sgmnt_data_ptr_t cs_data; GBLREF gd_region *gv_cur_region; void jnl_write_trunc_rec(sgmnt_addrs *csa, block_id orig_total_blks, block_id orig_free_blocks, block_id total_blks_after_trunc) { struct_jrec_trunc trunc_rec; jnl_private_control *jpc; assert(!IN_PHASE2_JNL_COMMIT(csa)); assert(csa->now_crit); jpc = csa->jnl; trunc_rec.prefix.jrec_type = JRT_TRUNC; trunc_rec.prefix.forwptr = trunc_rec.suffix.backptr = TRUNC_RECLEN; trunc_rec.prefix.tn = csa->ti->curr_tn; trunc_rec.suffix.suffix_code = JNL_REC_SUFFIX_CODE; trunc_rec.prefix.time = jgbl.gbl_jrec_time; trunc_rec.prefix.checksum = INIT_CHECKSUM_SEED; trunc_rec.prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; trunc_rec.orig_total_blks = orig_total_blks; trunc_rec.orig_free_blocks = orig_free_blocks; trunc_rec.total_blks_after_trunc = total_blks_after_trunc; trunc_rec.filler = 0; trunc_rec.prefix.checksum = compute_checksum(INIT_CHECKSUM_SEED, (unsigned char *)&trunc_rec, SIZEOF(struct_jrec_trunc)); jnl_write(jpc, JRT_TRUNC, (jnl_record *)&trunc_rec, NULL); } fis-gtm-V7.0-005/sr_port/jnl_write_ztp_logical.c0000644000032200000250000000464614342376331020605 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2017 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_inet.h" #include "gtm_time.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsfhead.h" #include "filestruct.h" #include "jnl.h" #include "jnl_write.h" #include "repl_msg.h" #include "gtmsource.h" #include "iosp.h" #include "jnl_get_checksum.h" #ifdef DEBUG #include "jnl_typedef.h" #endif GBLREF jnl_fence_control jnl_fence_ctl; GBLREF jnl_gbls_t jgbl; GBLREF seq_num seq_num_zero; GBLREF uint4 process_id; void jnl_write_ztp_logical(sgmnt_addrs *csa, jnl_format_buffer *jfb, uint4 com_csum, seq_num jnl_seqno) { struct_jrec_upd *jrec; jnl_private_control *jpc; boolean_t in_phase2; /* If REPL_WAS_ENABLED(csa) is TRUE, then we would not have gone through the code that initializes * jgbl.gbl_jrec_time or jpc->pini_addr. But in this case, we are not writing the journal record * to the journal buffer or journal file but write it only to the journal pool from where it gets * sent across to the update process that does not care about these fields so it is ok to leave them as is. */ jpc = csa->jnl; assert((0 != jpc->pini_addr) || REPL_WAS_ENABLED(csa)); assert(jgbl.gbl_jrec_time || REPL_WAS_ENABLED(csa)); assert(IS_SET_KILL_ZKILL_ZTRIG(jfb->rectype)); assert(IS_ZTP(jfb->rectype)); jrec = (struct_jrec_upd *)jfb->buff; jrec->prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; in_phase2 = IN_PHASE2_JNL_COMMIT(csa); jrec->prefix.tn = JB_CURR_TN_APPROPRIATE(in_phase2, jpc, csa); jrec->prefix.time = jgbl.gbl_jrec_time; assert(0 != jnl_fence_ctl.token); jrec->token_seq.token = jnl_fence_ctl.token; jrec->strm_seqno = 0; /* strm_seqno is only for replication & ZTCOM does not work with replic */ COMPUTE_LOGICAL_REC_CHECKSUM(jfb->checksum, jrec, com_csum, jrec->prefix.checksum); assert(!REPL_ALLOWED(csa)); JNL_WRITE_APPROPRIATE(csa, jpc, jfb->rectype, (jnl_record *)jrec, jfb); } fis-gtm-V7.0-005/sr_port/jnlpool_hasnt_overflowed.c0000644000032200000250000000372714342376331021326 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2020 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 "memcoherency.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "repl_msg.h" #ifdef VMS #include #endif #include "gtmsource.h" boolean_t jnlpool_hasnt_overflowed(jnlpool_ctl_ptr_t jctl, gtm_uint64_t jnlpool_size, qw_num read_addr) { /* the advantage of passing the three arguments is they are likely (on most platforms) to be scratch registers that can also * be used for computation. Also, besides all callers would have already loaded jctl and jnlpool_size */ /* For systems with UNORDERED memory access (example, ALPHA, POWER4, PA-RISC 2.0), on a multi processor system, it is * possible that the source server notices the change in jnlpool_ctl->write_addr before seeing the change to the content * to be read (including the jnl_data_header). To avoid such conditions, we should commit the order of shared memory * updates before and after the content is updated (see t_end.c, tp_tend.c). To ensure the source server reads content * that is correct, it should invalidate its cache before the read. After the read, to ensure that the content is correct * (not some that may have been overwritten), it has to invalidate its cache to fetch the latest value of rsrv_write_addr. * */ SHM_READ_MEMORY_BARRIER; /* to fetch the latest rsrv_write_addr */ return (jnlpool_size >= (jctl->rsrv_write_addr - read_addr)); } fis-gtm-V7.0-005/sr_port/job.h0000755000032200000250000000120014342376331014762 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef JOB_H_INCLUDED #define JOB_H_INCLUDED int4 ojchkfs(char *addr, int4 len, bool exist); #include "jobsp.h" #endif fis-gtm-V7.0-005/sr_port/job_addr.c0000644000032200000250000000414514342376333015761 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "cmd_qlf.h" #include #include "op.h" #include "job_addr.h" #include "zbreak.h" GBLREF mident_fixed zlink_mname; error_def(ERR_JOBLABOFF); error_def(ERR_ZLINKFILE); error_def(ERR_ZLMODULE); boolean_t job_addr(mstr *rtn, mstr *label, int4 offset, char **hdr, char **labaddr) { rhdtyp *rt_hdr; int4 *lp; mval rt; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (NULL == (rt_hdr = find_rtn_hdr(rtn))) { rt.mvtype = MV_STR; rt.str = *rtn; op_zlink(&rt, NULL); rt_hdr = find_rtn_hdr(rtn); if (NULL == rt_hdr) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(8) ERR_ZLINKFILE, 2, rtn->len, rtn->addr, ERR_ZLMODULE, 2, STRLEN(&zlink_mname.c[0]), &zlink_mname); *hdr = (char *)rt_hdr; } lp = NULL; if ((rt_hdr->compiler_qlf & CQ_LINE_ENTRY) || (0 == offset)) /* Label offset with routine compiled with NOLINE_ENTRY should cause error. */ lp = find_line_addr(rt_hdr, label, offset, NULL); if (!lp) return (FALSE); /* Set the pointer to address / offset for line number entry storage in TABENT_PROXY. */ # ifdef USHBIN_SUPPORTED ARLINK_ONLY((TABENT_PROXY).rtnhdr_adr = rt_hdr); (TABENT_PROXY).lnr_adr = lp; # else /* On non-shared-binary, calculcate the offset to the corresponding lnr_tabent record by subtracting * the base address (routine header) from line number entry's address, and save the result in * lab_ln_ptr field of TABENT_PROXY structure. */ (TABENT_PROXY).lab_ln_ptr = ((int4)lp - (int4)rt_hdr); # endif if (NULL != labaddr) *labaddr = (char *)LINE_NUMBER_ADDR(rt_hdr, lp); *hdr = (char *)rt_hdr; return (TRUE); } fis-gtm-V7.0-005/sr_port/job_addr.h0000755000032200000250000000123314342376331015762 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef JOB_ADDR_H_INCLUDED #define JOB_ADDR_H_INCLUDED boolean_t job_addr(mstr *rtn, mstr *label, int4 offset, char **hdr, char **labaddr); #endif fis-gtm-V7.0-005/sr_port/jobexam_process.c0000644000032200000250000002511614342376333017401 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 #include "gtm_signal.h" #include "gtm_unistd.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_limits.h" #include "error.h" #include "io_params.h" #include "op.h" #include "io.h" #include #include "stack_frame.h" #include "jobexam_process.h" #ifdef UNIX # include "jobexam_signal_handler.h" #endif #include "send_msg.h" #include "callg.h" #include "zshow.h" #include "util.h" #include "mv_stent.h" #define DEFAULT_DUMP_FILENAME "GTM_JOBEXAM.ZSHOW_DMP" /* + 3 below is for for 2 intervening '_' chars and 1 extra */ #define DEFAULT_DUMP_FILE_TOTSIZE (SIZEOF(DEFAULT_DUMP_FILENAME) + (MAX_DIGITS_IN_INT * 2) + 3) #define NOCONCEAL_OPTION "NO_CONCEAL" static readonly mval empty_str_mval = DEFINE_MVAL_LITERAL(MV_STR, 0, 0, 0, 0, 0, 0); static readonly mval no_conceal_op = DEFINE_MVAL_LITERAL(MV_STR, 0, 0, SIZEOF(NOCONCEAL_OPTION) - 1, NOCONCEAL_OPTION, 0, 0); static unsigned char dumpable_error_dump_file_parms[2] = {iop_newversion, iop_eol}; static unsigned char dumpable_error_dump_file_noparms[1] = {iop_eol}; GBLREF int process_exiting; GBLREF uint4 process_id; GBLREF io_pair io_std_device, io_curr_device; GBLREF mv_stent *mv_chain; GBLREF unsigned char *msp, *stackwarn, *stacktop; GBLREF boolean_t created_core; UNIX_ONLY(GBLREF sigset_t blockalrm;) DEBUG_ONLY(GBLREF boolean_t ok_to_UNWIND_in_exit_handling;) LITREF mval literal_zero; error_def(ERR_GTMASSERT); error_def(ERR_GTMASSERT2); error_def(ERR_GTMCHECK); error_def(ERR_JOBEXAMDONE); error_def(ERR_JOBEXAMFAIL); error_def(ERR_MEMORY); error_def(ERR_OUTOFSPACE); error_def(ERR_STACKCRIT); error_def(ERR_STACKOFLOW); error_def(ERR_VMSMEMORY); void jobexam_process(mval *dump_file_name, mval *dump_file_spec, mval *fmt) { mval *input_dump_file_name; io_pair dev_in_use; mv_stent *new_mv_stent; boolean_t saved_mv_stent; char saved_util_outbuff[OUT_BUFF_SIZE]; int rc, saved_util_outbuff_len; char save_dump_file_name_buff[GTM_PATH_MAX]; # ifdef UNIX struct sigaction new_action, prev_action; sigset_t savemask; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* If the input file name is the result of an expression, it is likely being held in the * same temporary as the output file spec. We can tell if this is true by comparing the * address of the input and output mvals. If they are the same, make a copy of the input * filespec in a garbage collection safe mval prior to initializing the output mval * (which in this case would clear the input mval as well if it had not just been saved). */ if (dump_file_name == dump_file_spec) { /* Make saved copy of input mval */ PUSH_MV_STENT(MVST_MVAL); new_mv_stent = mv_chain; input_dump_file_name = &mv_chain->mv_st_cont.mvs_mval; *input_dump_file_name = *dump_file_name; saved_mv_stent = TRUE; } else { /* Just use input mval as-is */ input_dump_file_name = dump_file_name; saved_mv_stent = FALSE; } # ifdef UNIX /* Block out timer calls that might trigger processing that could fail. We especially want to prevent * nesting of signal handlers since the longjump() function used by the UNWIND macro is undefined on * Tru64 when signal handlers are nested. */ SIGPROCMASK(SIG_BLOCK, &blockalrm, &savemask, rc); /* Setup new signal handler to just drive condition handler which will do the right thing */ memset(&new_action, 0, SIZEOF(new_action)); sigemptyset(&new_action.sa_mask); new_action.sa_flags = SA_SIGINFO; # ifdef __sparc new_action.sa_handler = jobexam_signal_handler; # else new_action.sa_sigaction = jobexam_signal_handler; # endif sigaction(SIGBUS, &new_action, &prev_action); sigaction(SIGSEGV, &new_action, 0); # endif *dump_file_spec = empty_str_mval; dev_in_use = io_curr_device; /* Save current IO device */ /* Save text in util_outbuff which can be detrimentally overwritten by ZSHOW. * NOTE: The following code needs to be eventually moved to jobinterrupt_process.c and replaced with * SAVE/RESTORE_UTIL_OUT_BUFFER macros, as follows: * * char *save_util_outptr; * va_list save_last_va_list_ptr; * boolean_t util_copy_saved = FALSE; * DCL_THREADGBL_ACCESS; * * SETUP_THREADGBL_ACCESS; * ... * SAVE_UTIL_OUT_BUFFER(save_util_outptr, save_last_va_list_ptr, util_copy_saved); * ... * RESTORE_UTIL_OUT_BUFFER(save_util_outptr, save_last_va_list_ptr, util_copy_saved); */ ASSERT_SAFE_TO_UPDATE_THREAD_GBLS; saved_util_outbuff_len = 0; if (NULL == TREF(util_outptr)) TREF(util_outptr) = TREF(util_outbuff_ptr); if (0 != (saved_util_outbuff_len = (int)(TREF(util_outptr) - TREF(util_outbuff_ptr)))) /* Caution -- assignment */ { assert(0 <= saved_util_outbuff_len); assert(saved_util_outbuff_len <= SIZEOF(saved_util_outbuff)); memcpy(saved_util_outbuff, TREF(util_outbuff_ptr), saved_util_outbuff_len); } jobexam_dump(input_dump_file_name, dump_file_spec, save_dump_file_name_buff, fmt); /* If any errors occur in job_exam_dump, the condition handler will unwind the stack to this point and return. */ if (0 != saved_util_outbuff_len) { /* Restore util_outbuff values */ memcpy(TREF(util_outbuff_ptr), saved_util_outbuff, saved_util_outbuff_len); TREF(util_outptr) = TREF(util_outbuff_ptr) + saved_util_outbuff_len; } io_curr_device = dev_in_use; /* Restore IO device */ /* If we saved an mval on our stack, we need to pop it off. If there was an error while doing the * jobexam dump, zshow may have left some other mv_stent entries on the stack. Pop them all off with * just a regular POP_MV_STENT macro rather than unw_mv_ent() call because the mv_stent entries * created in zshow_output reference automatic storage that cannot be referenced at this stack * level without potential (C) stack corruption. */ if (saved_mv_stent) { assertpro(mv_chain <= new_mv_stent); /* This violates our assumptions that the mv_stent we pushed onto the * stack should still be there */ while (mv_chain <= new_mv_stent) { POP_MV_STENT(); } } # ifdef UNIX /* Restore the signal handlers how they were */ sigaction(SIGBUS, &prev_action, 0); sigaction(SIGSEGV, &prev_action, 0); /* Let the timers pop again.. */ SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc); # endif } /* This routine is broken out as another ep so we can do cleanup processing in jobexam_process if * we trigger the condition handler and unwind. */ void jobexam_dump(mval *dump_filename_arg, mval *dump_file_spec, char *fatal_file_name_buff, mval *fmt) { unsigned char dump_file_name[DEFAULT_DUMP_FILE_TOTSIZE], *dump_file_name_ptr; mval def_file_name, parms, zshowall; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ESTABLISH(jobexam_dump_ch); ++(TREF(jobexam_counter)); /* Setup default filename/type to use for the parse. Append processid and a counter. */ MEMCPY_LIT(dump_file_name, DEFAULT_DUMP_FILENAME); dump_file_name_ptr = dump_file_name + SIZEOF(DEFAULT_DUMP_FILENAME) - 1; *dump_file_name_ptr++ = '_'; dump_file_name_ptr = i2asc(dump_file_name_ptr, process_id); *dump_file_name_ptr++ = '_'; dump_file_name_ptr = i2asc(dump_file_name_ptr, TREF(jobexam_counter)); def_file_name.mvtype = MV_STR; def_file_name.str.addr = (char *)dump_file_name; def_file_name.str.len = INTCAST(dump_file_name_ptr - dump_file_name); assert(DEFAULT_DUMP_FILE_TOTSIZE >= def_file_name.str.len); /* Call $ZPARSE processing to fill in any blanks, expand concealed logicals, etc. It is the callers * responsibility to make sure garbage collection knows about the value in the returned filespec. */ op_fnzparse(dump_filename_arg, &empty_str_mval, &def_file_name, &empty_str_mval, &no_conceal_op, dump_file_spec); /* If this call is for the creation of a fatal error file (which would be the case if process_exiting is set), the * mval we have for dump_file_spec is not protected from potential stringpool garbage collections. In that case, * since we won't be returning the filename (as we would for a $ZJOBEXAM() function call), rebuffer the stringpool * resident dump file name so it is not vulnerable to stringpool garbage collections. */ if (process_exiting) { assert(GTM_PATH_MAX >= dump_file_spec->str.len); memcpy(fatal_file_name_buff, dump_file_spec->str.addr, dump_file_spec->str.len); dump_file_spec->str.addr = fatal_file_name_buff; } /* Parms of file to be created (newversion) */ parms.mvtype = MV_STR; parms.str.addr = (char *)dumpable_error_dump_file_parms; parms.str.len = SIZEOF(dumpable_error_dump_file_parms); /* Open, use, and zshow into new file, then close and reset current io device */ op_open(dump_file_spec, &parms, (mval *)&literal_zero, 0); op_use(dump_file_spec, &parms); zshowall.mvtype = MV_STR; if ( (NULL == fmt) || (0 == fmt->str.len) ) { zshowall.str.addr = "*"; zshowall.str.len = 1; } else { zshowall.str.addr = fmt->str.addr; zshowall.str.len = fmt->str.len; } op_zshow(&zshowall, ZSHOW_DEVICE, NULL); parms.str.addr = (char *)dumpable_error_dump_file_noparms; parms.str.len = SIZEOF(dumpable_error_dump_file_noparms); op_close(dump_file_spec, &parms); /* Notify operator dump was taken */ send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_JOBEXAMDONE, 3, process_id, dump_file_spec->str.len, dump_file_spec->str.addr); REVERT; } CONDITION_HANDLER(jobexam_dump_ch) { boolean_t save_created_core; START_CH(TRUE); /* Operation: * 1) Flush out message we came here because of to operator console * 2) Put out our message stating that we screwed up * 3) Unwind the errant frames so we can return to the user without screwing * up the task that got interrupted to do this examine. */ # if defined(DEBUG) && defined(UNIX) if (DUMPABLE) { /* For debug UNIX issues, let's make a core if we would have made one in open code */ save_created_core = created_core; gtm_fork_n_core(); created_core = save_created_core; } # endif UNIX_ONLY(util_out_print(0, OPER)); VMS_ONLY(sig->chf$l_sig_args -= 2); VMS_ONLY(callg(send_msg, &sig->chf$l_sig_args)); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_JOBEXAMFAIL, 1, process_id); /* Stop the errors here and return to caller */ UNIX_ONLY(util_out_print("", RESET)); /* Prevent rts_error from flushing this error later */ DEBUG_ONLY(ok_to_UNWIND_in_exit_handling = TRUE); UNWIND(NULL, NULL); } fis-gtm-V7.0-005/sr_port/jobexam_process.h0000755000032200000250000000140314342376331017400 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ #ifndef JOBEXAM_PROCESS_INCLUDED #define JOBEXAM_PROCESS_INCLUDED void jobexam_process(mval *dump_file_name, mval *fmt, mval *dump_file_spec); void jobexam_dump(mval *dump_file_name, mval *dump_file_spec, char *fatal_file_name_buff, mval *fmt); #endif fis-gtm-V7.0-005/sr_port/jobinterrupt_event.c0000755000032200000250000000443514342376331020150 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* job interrupt event - an interrupt has been requested. - Call xfer_set_handlers so next M instruction traps to interrupt routine - Other required housecleaning for VMS. */ #include "mdef.h" # include #ifdef GTM_PTHREAD # include #endif #include "gtm_stdio.h" #include "io.h" #include "op.h" #include "xfer_enum.h" #include "have_crit.h" #include "deferred_events_queue.h" #include "deferred_events.h" #include "jobinterrupt_process.h" #include "fix_xfer_entry.h" GBLREF intrpt_state_t intrpt_ok_state; GBLREF xfer_entry_t xfer_table[]; GBLREF volatile int4 outofband; GBLREF volatile boolean_t dollar_zininterrupt; /* Routine called when an interrupt event occurs (signaled by mupip intrpt or other future method * of signaling interrupts). This code is driven as a signal handler on Unix. */ void jobinterrupt_event(int sig, siginfo_t *info, void *context) { /* Note the (presently unused) args are to match signature for signal handlers in Unix */ FORWARD_SIG_TO_MAIN_THREAD_IF_NEEDED(sig); if (!dollar_zininterrupt) (void)xfer_set_handlers(jobinterrupt, 0, FALSE); } /* Call back routine from xfer_set_handlers to complete outofband setup */ void jobinterrupt_set(int4 dummy_val) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(INTRPT_IN_EVENT_HANDLING == intrpt_ok_state); assert(pending == TAREF1(save_xfer_root, jobinterrupt).event_state); assert(jobinterrupt == outofband); DBGDFRDEVNT((stderr, "%d %s: jobinterrupt_set - %sneeded\n", __LINE__, __FILE__, (jobinterrupt == outofband) ? "NOT " : "")); outofband = jobinterrupt; DEFER_INTO_XFER_TAB; TAREF1(save_xfer_root, jobinterrupt).event_state = active; DBGDFRDEVNT((stderr, "%d %s: jobinterrupt_set - set the xfer entries for jobinterrupt_event\n", __LINE__, __FILE__)); } fis-gtm-V7.0-005/sr_port/jobinterrupt_init.c0000755000032200000250000000416614342376331017773 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* job interrupt initialization. Accomplish following setups: - Setup handler for SIGUSR1 signal to be handled by jobinterrupt_event() (UNIX only). - Provide initial setting for $ZINTERRUPT from default or logical or environment variable if present. */ #include "mdef.h" #include "gtm_signal.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "gtm_logicals.h" #include "trans_log_name.h" #include "io.h" #include "iosp.h" #include "stringpool.h" #include "jobinterrupt_process.h" GBLREF mval dollar_zinterrupt; #define DEF_ZINTERRUPT "IF $ZJOBEXAM()" void jobinterrupt_init(void) { mstr envvar_logical; char trans_bufr[MAX_TRANS_NAME_LEN]; DCL_THREADGBL_ACCESS; struct sigaction new_action; SETUP_THREADGBL_ACCESS; /* Setup new signal handler to just drive condition handler which will do the right thing. */ memset(&new_action, 0, SIZEOF(new_action)); sigemptyset(&new_action.sa_mask); new_action.sa_flags = SA_SIGINFO; new_action.sa_sigaction = jobinterrupt_event; sigaction(SIGUSR1, &new_action, NULL); /* Provide initial setting for $ZINTERRUPT */ envvar_logical.addr = GTM_ZINTERRUPT; envvar_logical.len = SIZEOF(GTM_ZINTERRUPT) - 1; if ((SS_NORMAL != TRANS_LOG_NAME(&envvar_logical, &dollar_zinterrupt.str, trans_bufr, SIZEOF(trans_bufr), do_sendmsg_on_log2long)) || (0 == dollar_zinterrupt.str.len)) { /* Translation failed - use default */ dollar_zinterrupt.str.addr = DEF_ZINTERRUPT; dollar_zinterrupt.str.len = SIZEOF(DEF_ZINTERRUPT) - 1; } else /* put value in stringpool if translation succeeded */ s2pool(&dollar_zinterrupt.str); dollar_zinterrupt.mvtype = MV_STR; return; } fis-gtm-V7.0-005/sr_port/jobinterrupt_process.h0000755000032200000250000000220114342376331020477 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ #ifndef JOBINTR_PROCESS_INCLUDED #define JOBINTR_PROCESS_INCLUDED #define JOBINTR_TP_RETHROW \ { /* rethrow job interrupt($ZINT) if $ZTEXIT is true and not already in $ZINTR */ \ GBLREF boolean_t dollar_ztexit_bool; \ GBLREF volatile boolean_t dollar_zininterrupt; \ \ error_def(ERR_JOBINTRRETHROW); /* BYPASSOK */ \ \ if (dollar_ztexit_bool && !dollar_zininterrupt) \ RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_JOBINTRRETHROW); \ } void jobinterrupt_event(int sig, siginfo_t *info, void *context); void jobinterrupt_init(void); void jobintrpt_ztime_process(boolean_t ztimeo); #endif fis-gtm-V7.0-005/sr_port/jobinterrupt_process_cleanup.h0000755000032200000250000000111114342376331022205 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2002 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef JOBINTR_PROCESS_CLEANUP_INCLUDED #define JOBINTR_PROCESS_CLEANUP_INCLUDED void jobinterrupt_process_cleanup(void); #endif fis-gtm-V7.0-005/sr_port/jobparameters.c0000755000032200000250000000373714342376331017062 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" /* #include "opcode.h" */ #include "toktyp.h" #include "job.h" #include "advancewindow.h" error_def (ERR_RPARENMISSING); int jobparameters (oprtype *c) { char *parptr; /* This is a workaround for the moment. The former maximum size of the * parameter string was completely incapable of handling the variety * of string parameters that may be passed. To further compound things, * no checks exist to enforce upper limits on parameter string lengths. * This new maximum was reached by calculating the maximum length of * the job parameter string (presuming each possible qualifier is represented * once and only once) and tacking on a safety net. This came to * 10 255 byte strings (plus length byte), 1 longword, and 17 single byte * identifiers for each of the job keywords. The maximum of 3000 leaves * a little room for expansion in the future */ char parastr[3000]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; parptr = parastr; if (TK_LPAREN != TREF(window_token)) { if (TK_COLON != TREF(window_token)) { if (!one_job_param (&parptr)) return FALSE; } } else { advancewindow (); for (;;) { if (!one_job_param (&parptr)) return FALSE; if (TK_COLON == TREF(window_token)) advancewindow (); else if (TK_RPAREN == TREF(window_token)) { advancewindow (); break; } else { stx_error (ERR_RPARENMISSING); return FALSE; } } } *parptr++ = jp_eol; *c = put_str (parastr,(mstr_len_t)(parptr - parastr)); return TRUE; } fis-gtm-V7.0-005/sr_port/jobparams.h0000755000032200000250000000220214342376331016171 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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. * * * ****************************************************************/ JPDEF (jp_eol, jpdt_nul), JPDEF (jp_account, jpdt_nul), #ifdef UNIX JPDEF (jp_cmdline, jpdt_str), #endif JPDEF (jp_default, jpdt_str), JPDEF (jp_detached, jpdt_nul), JPDEF (jp_error, jpdt_str), JPDEF (jp_gbldir, jpdt_str), JPDEF (jp_image, jpdt_str), JPDEF (jp_input, jpdt_str), JPDEF (jp_logfile, jpdt_str), JPDEF (jp_noaccount, jpdt_nul), JPDEF (jp_nodetached, jpdt_nul), JPDEF (jp_noswapping, jpdt_nul), JPDEF (jp_output, jpdt_str), JPDEF (jp_passcurlvn, jpdt_nul), JPDEF (jp_priority, jpdt_num), JPDEF (jp_process_name, jpdt_str), JPDEF (jp_schedule, jpdt_str), JPDEF (jp_startup, jpdt_str), JPDEF (jp_swapping, jpdt_nul), JPDEF (jp_nmvals, jpdt_nul) fis-gtm-V7.0-005/sr_port/jobparamstrs.h0000755000032200000250000000353314342376331016732 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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. * * * ****************************************************************/ JPSDEF ( 3, "ACC", jp_account), JPSDEF ( 8, "ACCOUNTI*", jp_account), #ifdef UNIX JPSDEF ( 3, "CMD", jp_cmdline), JPSDEF ( 7, "CMDLINE", jp_cmdline), #endif JPSDEF ( 3, "DEF", jp_default), JPSDEF ( 7, "DEFAULT", jp_default), JPSDEF ( 3, "DET", jp_detached), JPSDEF ( 8, "DETACHED", jp_detached), JPSDEF ( 3, "ERR", jp_error), JPSDEF ( 5, "ERROR", jp_error), JPSDEF ( 3, "GBL", jp_gbldir), JPSDEF ( 6, "GBLDIR", jp_gbldir), JPSDEF ( 2, "IM", jp_image), JPSDEF ( 5, "IMAGE", jp_image), JPSDEF ( 2, "IN", jp_input), JPSDEF ( 5, "INPUT", jp_input), JPSDEF ( 3, "LOG", jp_logfile), JPSDEF ( 7, "LOGFILE", jp_logfile), JPSDEF ( 5, "NOACC", jp_noaccount), JPSDEF ( 8, "NOACCOUN*", jp_noaccount), JPSDEF ( 5, "NODET", jp_nodetached), JPSDEF ( 8, "NODETACH*", jp_nodetached), JPSDEF ( 5, "NOSWA", jp_noswapping), JPSDEF ( 8, "NOSWAPPI*", jp_noswapping), JPSDEF ( 3, "OUT", jp_output), JPSDEF ( 6, "OUTPUT", jp_output), #ifdef UNIX JPSDEF ( 4, "PASS", jp_passcurlvn), JPSDEF ( 10, "PASSCURLVN", jp_passcurlvn), #endif JPSDEF ( 3, "PRI", jp_priority), JPSDEF ( 8, "PRIORITY", jp_priority), JPSDEF ( 3, "PRO", jp_process_name), JPSDEF ( 7, "PROCESS*", jp_process_name), JPSDEF ( 3, "SCH", jp_schedule), JPSDEF ( 8, "SCHEDULE", jp_schedule), JPSDEF ( 3, "STA", jp_startup), JPSDEF ( 7, "STARTUP", jp_startup), JPSDEF ( 3, "SWA", jp_swapping), JPSDEF ( 8, "SWAPPING", jp_swapping) fis-gtm-V7.0-005/sr_port/jswrite.mpt0000644000032200000250000003763414342376331016271 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright (c) 2020 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The ^%JSWRITE utility routine converts a glv structure or a series of SET @ arguments to a string of JS objects. ; The format of the ^%JSWRITE utility is: ; ; ^%JSWRITE(glvnode,[expr1,expr2]) ; ; * glvnode specifies the string containing the subscripted/unsubscripted global or local variable name. ; When glvnode evaluates to an empty string ("") or there are no arguments, %JSWRITE considers all ; subscripted local variables in scope for conversion. ; * If expr1 specifies "#", ^%JSWRITE returns JS objects of the entire tree starting from the glvnode ; till the end of the glv. ; * If expr1 specifies "*", ^%JSWRITE returns JS objects for all nodes descending from the specified glv node. ; * Specifying "*" and "#" together produces an error. ; * Specifying [expr1], that is, with a leading "["and trailing "]", ^%JSWRITE displays the JSON objects in an ; array collection. Without [], you need to transform the object strings to the desired destination object format. ; * If expr2 specifies "noglvname", ^%JSWRITE removes the first key containing the name of the glv root from the ; JS object output. ; * The default $ETRAP for %JSWRITE is if (""=$etrap) new $etrap set $etrap="do errorobj"_"^"_$text(+0),$zstatus="". ; To override the default error handler, set $ETRAP to any non-empty value. ; * When appropriate, enclose invocations of ^%JSWRITE in a TSTART/COMMIT boundary to prevent any blurred copy of the ; data that is actively updated. ; * When appropriate, use GT.M alias containers to take appropriate local variables temporarily out of scope and then ; run the argumentless form of ^%JSWRITE. ; * As %JSWRITE relies on the ZWRITE format of the data stored in the variables, it does not produce the ; BADCHAR run time error when it encounters malformed characters during processing. ; Examples: ; ; GTM>set glvn="demodevtest(""Developer2"")" ; GTM>do ^%JSWRITE(glvn,"*") ; JS Object Strings: All descendants of demodevtest("Developer2") ; GTM>do ^%JSWRITE(glvn,"[*]") ; Array: All descendants of demodevtest("Developer2") ; GTM>do ^%JSWRITE(glvn,"#"); JS Object Strings: All descendants of demodevtest starting from demodevtest("Developer2") ; GTM>do ^%JSWRITE(glvn,"[#]"); Array: All descendants of demodevtest starting from demodevtest("Developer2") ; $ $gtm_dist/mumps -r %XCMD 'ZWRITE ^demodevtest' | $gtm_dist/mumps -r STDIN^%JSWRITE ; ; Utility Label: ; STDIN^%JSWRITE [singlesub] ; ; With the STDIN label, the %JSWRITE utility routine expects a valid SET @ argument (like the one from the ZWRITE command) ; as its standard input over a named/unnamed pipe device and returns an array of objects. This construct ensures that ; $ZUSEDSTOR remains consistently low even for processing large data for conversion. STDIN^%JSWRITE automatically terminates ; the process with a non-zero exit status when it does not receive a READ terminator for 120 seconds from standard input. ; ; When "singlesub" is specified as an argument, ^%JSWRITE expects ZWRITE lines for single subscript glvs. ; Here ^%JSWRITE implicitly removes the unsubscripted glv name and returns an array collection of objects in the ; form of [{"key1":value,"key2":value,...},{"key1":value,"key2":value,...}] where: ; ; * key1 is the subscript ; * value is the right side of the =. ; ; The subscript first received by STDIN^%JSWRITE singlesub denotes the start of the object. When ^%JSWRITE finds the ; same subscript, it ends the current object boundary and starts the boundary of a new object. ; ; Example: ; ; $ $gtm_dist/mumps -r ^RTN ; abc("firstname")="John" ; abc("lastname")="Doe" ; abc("firstname")="Jane" ; abc("lastname")="Doe" ; $ $gtm_dist/mumps -r ^RTN | $gtm_dist/mumps -r STDIN^%JSWRITE singlesub ; [{"firstname":"John","lastname":"Doe"}, ; {"firstname":"Jane","lastname":"Doe"}] %JSWRITE(glvnode,expr2,expr3) if (""=$etrap) new $etrap set $etrap="do errorobj"_"^"_$text(+0),$zstatus="" new wantarray,traversalmethod,wantglvname,exprcode,isvalidexpr2 ; exprcode : a number from 0 to 7 each denoting the presence or absence of expressions ; isvalidexpr2 : whether expr2 (when specified) is valid ; traversalmethod can be * or #. The default is *. ; # returns JS object strings of the entire tree starting from the glvnode till the end of the glv. ; * returns JS object string for all nodes descending from the specified glv node. ; wantarray : whether the request is for a JS array string ; wantglvname : whether to include the glv name in the output ; set the defaults set wantarray=1,traversalmethod="*",wantglvname=1,exprcode=0,isvalidexpr2=0 ; find the exprcode of the user specified expressions ; 0: Nothing 4: expr3 ; 1: glvnode 5: glvnode,expr3 ; 2: expr2 6: expr2, expr3 ; 3: glvnode, expr2 7: glvnode, expr2, expr3 if $data(glvnode),$increment(exprcode,1) if $data(expr2),$increment(exprcode,2) if $data(expr3),$increment(exprcode,4) if ($select(2=exprcode:1,6=exprcode:1,1:0)) do quit . write $text(+0),@$piece($text(@("U274")),";",2) if ($select(7=exprcode:1,5=exprcode:1,4=exprcode:1,1:0)),("noglvname"'=expr3),($length(expr3)) do quit . write $text(+0),@$piece($text(@("U276")),";",2),! if ($select(7=exprcode:1,5=exprcode:1,4=exprcode:1,1:0)),(expr3="noglvname") set wantglvname=0 if ((0=exprcode)!(4=exprcode)) do navlv quit set:(1=exprcode) expr2="*" set:$select("[#]"=expr2:0,"[*]"=expr2:0,"*"=expr2:0,"#"=expr2:0,1:1) isvalidexpr2=1 if ($select(3=exprcode:1,7=exprcode:1,2=exprcode:1,6=exprcode:1,1:0)),isvalidexpr2 do quit . write $text(+0),@$piece($text(@("U275")),";",2) set:("[#]"=expr2) wantarray=1,traversalmethod="#" set:("[*]"=expr2) wantarray=1,traversalmethod="*" set:("*"=expr2) wantarray=0,traversalmethod="*" set:("#"=expr2) wantarray=0,traversalmethod="#" do:("*"=traversalmethod) nodedescendants(glvnode) do:("#"=traversalmethod) glvdescendants(glvnode) quit nodedescendants(glvnode) new startcomm ; startcomm : whether to start comma; used in navvar(glvnode) when the request is for the array form set startcomm=0 write:wantarray "[" do navvar(glvnode) ; navigate the descendants of only the glvnode write:wantarray "]" quit navvar(glvnode) ; uses $order() to navigate new base,nextnode,status,type,x,y ; base : The unsubscripted name ; nextnode : The next node in glvnode to navigate ; status : Status of zdata to determine whether we need recursion ; type : g for global and s for subscript; set variables according to the type ; x & y : counter type variable ; first determine whether the name is a global or a subscript ; g for global and s for subscript; set variables according to the type if (glvnode=$qsubscript(glvnode,0)) set type="g",base=glvnode else set type="s",base=$qsubscript(glvnode,0),glvnode=$extract(glvnode,1,$length(glvnode)-1)_",",nextnode=glvnode set x="" for do setnextnode set x=$order(@nextnode) quit:x="" do . do setnextnode . set status=$data(@nextnode) . do:(status#2) . . write:startcomm&wantarray ",",! . . write:startcomm&('wantarray) ! . . write $select(wantglvname:$$fmtglv(nextnode_"="_$zwrite(@nextnode)),1:$$fmtnoglv(nextnode_"="_$zwrite(@nextnode))) . . set startcomm=1 . do:(10<=status) navvar(nextnode) ; when nodes have data and need recursion quit setnextnode ; called from navvar set:type="s" nextnode=glvnode_$zwrite(x)_")" set:type="g" nextnode=base_"("_$zwrite(x)_")" quit glvdescendants(glvnode) ; Entry point for travesal method # new startcomm,zy ; startcomm : whether to start comma ; zy : the constructed zwrite line write:wantarray "[" set startcomm=0 for set glvnode=$query(@glvnode) quit:""=glvnode do . set zy=glvnode_"="_$zwrite($get(@glvnode)) . write:startcomm&wantarray ",",! . write:startcomm&('wantarray) ! . write $select(wantglvname:$$fmtglv(zy),1:$$fmtnoglv(zy)) . set startcomm=1 write:wantarray "]" quit errorobj ; the default error handler invoked when ""=$etrap write "{'Error':"_"'"_$$escapehex($zstatus)_"'}" set $ecode="" quit navlv ; entry point for traversing across lv new counter,glvnode,lv,lvc,lvname,more,x,y ; counter : counter variable ; glvnode : the name of the variable what needs conversion to JSON object collection ; lv : target for ZSHOW:"V" ; lvc : counter for the seconds subscript of lv ; lvname : name of the glv ; more : determines whether to follow the next node for more data. zshow "V":lv ; breaks output to chunk of 8192 bytes when data exceeds 8192 bytes. ; ; x,y : counter type variables zshow "V":lv set counter=0,(glvnode(counter),x)="" for set x=$order(lv("V",x)) quit:""=x do:'(lv("V",x)["$ZWRTAC")&("*"'=$extract(lv("V",x)))&(lv("V",x)["(") . set more=x+1 . for quit:'$zdata(lv("V",more))!(lv("V",more)["=") do . . set lv("V",x)=lv("V",x)_lv("V",more) . . if $increment(more) . write $select(wantglvname:$$fmtglv(lv("V",x)),1:$$fmtnoglv(lv("V",x))),! quit fmtnoglv(zwriteline) ; For noglvname requests new findeq,i,nxtlvl,qline,quote,value,varname ; findeq : find result ; i : counter variable ; nxtlvl : next subscript ; qline : output after running $qsubscript() ; quote : quote value ; value : value of the node as per zwrite line ; varname : name of the variable as per the zwrite line set findeq=$find(zwriteline,"="),varname=$extract(zwriteline,1,findeq-2),quote="'" ; same as fmtnoglv set qline="" ; omits the glv name as there is no need for the unsubsname for i=1:1:$qlength(varname) do ;we still have to across all subscripts even if we are not including the glv name . set nxtlvl=$$escapehex($qsubscript(varname,i)) . if ('$length(qline)) set qline="{"_quote_nxtlvl_quote ; this adds the { at the start of the object str . else set qline=qline_":{"_quote_nxtlvl_quote set value=$extract(zwriteline,findeq,$length(zwriteline)) set:""""=$extract(value) value=$extract(value,2,$length(value)-1) set quote=$$toquoteornot(value) set qline=qline_":"_quote_$$escapehex(value)_quote_$tr($justify(" ",i)," ","}") ;need to remove leading and trailing characters and replace single quote with double quote quit "{"_$translate($extract(qline,2,$length(qline)-1),"'","""")_"}" fmtglv(zwriteline) ; For glvname requests. ; Both fmtglv and fmtnoglv use slightly different logic. They ; are seperate entry point to avoid unnecessary ifs. new findeq,i,nxtlvl,qline,quote,value,varname ; findeq : find the position of equal sign in zwrline ; i : counter variable ; nxtlvl : next subscript ; qline : output after running $qsubscript() ; quote : quote value ; value : value of the node as per zwrite line ; varname : name of the variable as per the zwrite line set findeq=$find(zwriteline,"="),varname=$extract(zwriteline,1,findeq-2),quote="'" ; same as fmtglv set qline="'"_$qsubscript(varname,0)_"'" ; get the master node for i=1:1:$qlength(varname) do . set nxtlvl=$$escapehex($qsubscript(varname,i)) . set qline=qline_":{"_quote_nxtlvl_quote set value=$extract(zwriteline,findeq,$length(zwriteline)) set:""""=$extract(value) value=$extract(value,2,$length(value)-1) set quote=$$toquoteornot(value) set qline=qline_":"_quote_$$escapehex(value)_quote_$translate($justify(" ",i)," ","}") quit "{"_$tr(qline,"'","""")_"}" escapehex(value) ; escape characters that have a special meaning in Javascript runtime environments. ; First check whether there is anything to escape. quit:$translate(value,$char(10,27,34,38,39,60,62,91,93,123,125),"")=value value new asciicode,c,character,counter,javascriptStr,pos,ppos ; asciicode : holds the ascii code of characters to escape ; c : character code to escape ; character : holds value of $char(asciicode) to prevent multiple invocations of $char(asciicode) ; counter : counter type variable ; javascriptStr : holds the value after escaping a character ; pos : position of the current occurrence of character in value ; ppos : position of previous occurrence of character in value for asciicode=34,38,39,60,62,91,93,123,125 set character=$char(asciicode) do:value[character . set ppos=1,pos=1,javascriptStr="" . for set pos=$find(value,character,pos) quit:'pos do . . set javascriptStr=javascriptStr_$extract(value,ppos,pos-2)_"\u"_$$FUNC^%DH(asciicode,4),ppos=pos . set javascriptStr=javascriptStr_$extract(value,ppos,$length(value)) . set value=javascriptStr ; assign the escaped string to value quit value toquoteornot(val) ; determine whether to quote a value or not.. ; quit:((""=val)!("."=val)) "'" quit:"{"=$extract(val) "" ; do not quote when the input is a {key:value} pair quit $select(val?.N1".".N:"",val?.N:"",1:"'") STDIN ; Use as $gtm_dist/mumps -r %XCMD 'zwrite ^var' | $gtm_dist/mumps -r STDIN^%JSWRITE [singlesub] new wantsinglesub,zcmdline ; wantsinglesub : whether the request is for singlesub ; zcmdline : lowercase of $zcmdline if (""=$etrap) new $etrap set $etrap="do errorobj"_"^"_$text(+0),$zstatus="" set wantsinglesub=0 set zcmdline=$$FUNC^%TRIM($$FUNC^%LCASE($zcmdline)) set:"singlesub"=zcmdline wantsinglesub=1 if "singlesub"'=zcmdline,$length(zcmdline) use $p write $text(+0),@$piece($text(@("U277")),";",2) zhalt 11 do stdinfmtglv:'wantsinglesub,stdinfmtsinglesub:wantsinglesub quit stdinfmtsinglesub new currsub,formattedstr,getsub,resetsub,start,io,zwriteline,zwrleft ; currsub : current subscript ; formattedstr : holding the result of $$fmtnoglv ; getsub : flag to find the subscript of the glv ; resetsub : the subscript that identifies the starting javascript object boundary ; start : point of start of the javascript array ; zwriteline : input value ; zwrleft : left side of zwrline set (getsub,start)=0,resetsub="",io=$io write "[" for use io do readfromstdin quit:$zeof do:$length(zwriteline) . use $principal . set zwrleft=$piece(zwriteline,"=",1) . set:'getsub resetsub=$qsubscript(zwrleft,1),currsub=resetsub,getsub=1 . set:getsub currsub=$qsubscript(zwrleft,1) . set formattedstr=$$fmtnoglv(zwriteline) . if ((currsub'=resetsub)!$qlength(zwrleft)=0) do . . write:start ",",! . . write formattedstr,! . . set start=1 . else do . . if start,(currsub=resetsub) write "}" . . write:start ",",! . . write:(currsub=resetsub) "{" . . write $extract(formattedstr,2,$length(formattedstr)-1) . set start=1 use $principal write "}]",! quit readfromstdin ; reads from stdin. READ time out after 120 seconds. read zwriteline:120 if '$test use $principal write $text(+0),@$piece($text(@("U278")),";",2) zhalt 12 quit stdinfmtglv new io,start,zwrleft,zwrright ; io : to hold the value for $io ; start : When start=1, start placing commas ; zwrleft : left side = of the zwriteline ; zwrright : right side = of the zwriteline set io=$io,start=0 use $principal write "[" for quit:$zeof do . use io do readfromstdin . if $length(zwriteline) do . . set zwrleft=$piece(zwriteline,"=",1) . . use $principal . . write:start ",",! . . if 0=$qlength(zwrleft) do . . . set zwrright=$piece(zwriteline,"=",2) . . . write "{""",$$escapehex(zwrleft),""":" . . . write:""""=$extract(zwrright) """",$$escapehex($extract(zwrright,2,$length(zwrright)-1)),"""","}" . . . write:""""'=$extract(zwrright) zwrright,"}" . . . set start=1 . . else write $$fmtglv(zwriteline) set start=1 use $principal write "]",! quit U274 ;"-E-EXPR1NOTFOUND. No first expression specified. Specify the global or local variable node as the first expression." U275 ;"-E-ILLEGALEXPR2. Illegal second expression--"_expr2_". Specify *, #, [*], or [#]." U276 ;"-E-ILLEGALEXPR3. Illegal third expression--"_expr3_". Specify either an empty string or ""noglvname""." U277 ;"-E-ILLEGALARG1. Illegal argument--"_$zcmdline_". Specify either ""singlesub"" or leave it blank." U278 ;"-E-READTIMEOUT. Timeout occurred. STDIN did not receive a read terminator after 120 seconds." fis-gtm-V7.0-005/sr_port/la_encrypt.c0000755000032200000250000000470114342376331016354 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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. * * * ****************************************************************/ /* la_encrypt.c : for given encryption function number, and an input sequence of bytes, the program computes the checksum of the sequence. used in : la_create.c,lp_licensed.c,lm_verify */ #include "mdef.h" #include "la_encrypt.h" static void la_encrypt_table (uint4 poly, uint4 tbl[]); static uint4 la_encrypt_value ( uint4 tbl[], /* polynomial coefficients */ uint4 cs, /* checksum initial value */ unsigned char *c, /* string to compute the check sum for */ int len); /* string length */ bool la_encrypt ( short n , /* encryption function number */ char *q , /* input sequence */ int len, /* sequence length */ uint4 bcs[]) /* result, binary form */ { static uint4 poly0[10] = {0xEDB88320, 0xA001A001, 0x00008408, 0x00000000, 0xA001A001, 1, 1, 1, 1, 1}; static uint4 init0[10] = {0xFFFFFFFF, 0x00000000, 0x0000FFFF, 0xFFFFFFFF, 0x00000000, 0, 0, 0, 0, 0}; static uint4 poly1[10] = {0xA001A001, 0x00008408, 0xEDB88320, 0xEDB88320, 0x0000A001, 1, 1, 1, 1, 1}; static uint4 init1[10] = {0x00000000, 0x0000FFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0, 0, 0, 0, 0}; uint4 crctbl[16]; if (n<5) { la_encrypt_table(poly0[n],crctbl) ; bcs[0] = la_encrypt_value(crctbl,init0[n],(uchar_ptr_t)q,len) ; la_encrypt_table(poly1[n],crctbl) ; bcs[1] = la_encrypt_value(crctbl,init1[n],(uchar_ptr_t)q,len) ; } return n<5 ; } static void la_encrypt_table (uint4 poly, uint4 tbl[]) { uint4 k, t, x; int i; for ( k= 0 ; k!=16 ; k++ ) { t= k ; for ( i= 0 ; i!=4 ; i++ ) { x= t & 1 ; t= t>>1 ; if (x==1) t ^= poly ; } tbl[k]= t ; } } static uint4 la_encrypt_value ( uint4 tbl[], /* polynomial coefficients */ uint4 cs, /* checksum initial value */ unsigned char *c, /* string to compute the check sum for */ int len) /* string length */ { while ( len!=0 ) { cs ^= (int4)*c ; /* least signif. byte of cs differred with *c */ cs = ( cs >> 4 ) ^ tbl[cs & 0xF] ; cs = ( cs >> 4 ) ^ tbl[cs & 0xF] ; c++ ; len-- ; } return cs ; } fis-gtm-V7.0-005/sr_port/la_encrypt.h0000755000032200000250000000141014342376331016353 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef LA_ENCRYPT_H_INCLUDED #define LA_ENCRYPT_H_INCLUDED bool la_encrypt ( short n , /* encryption function number */ char *q , /* input sequence */ int len, /* sequence length */ uint4 bcs[]); /* result, binary form */ #endif fis-gtm-V7.0-005/sr_port/lb_init.c0000755000032200000250000000656114342376331015642 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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_string.h" #include "compiler.h" #include "toktyp.h" #include "comp_esc.h" #include "advancewindow.h" #include "lb_init.h" GBLREF struct ce_sentinel_desc *ce_def_list; error_def(ERR_CETOOMANY); LITREF char ctypetab[NUM_CHARS]; #define MAX_SUBSTITUTIONS 1024 void lb_init(void) { int num_subs, y; short int sav_last_src_col, source_col; int4 skip_count; unsigned char *cp, *cp1; boolean_t possible_sentinel; struct ce_sentinel_desc *shp; # ifdef DEBUG unsigned char original_source[MAX_SRCLINE + 1]; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (NULL != ce_def_list) { # ifdef DEBUG memcpy(original_source, (TREF(source_buffer)).addr, (TREF(source_buffer)).len + 1); /* include NUL term chars */ # endif source_col = 1; num_subs = 0; possible_sentinel = TRUE; while (possible_sentinel) { possible_sentinel = FALSE; cp = (unsigned char *)((TREF(source_buffer)).addr + source_col - 1); if (('\0' == *cp) || (DEL < *cp)) break; if ('\"' == *cp) { for (cp1 = cp + 1; ; ) { if (SP > *cp1) break; if ('\"' == *cp1++) { if ('\"' == *cp1) cp1++; /* escaped quotation mark inside string */ else break; /* end of string */ } } source_col += (cp1 - cp); cp = cp1; if ('\0' == *cp) break; } else if ('?' == *cp) { for (cp1 = cp + 1; ; ) { if (NUM_ASCII_CHARS <= *cp1) break; y = ctypetab[*cp1]; if ((TK_UPPER == y) || (TK_LOWER == y) || (TK_DIGIT == y) || (TK_PERIOD == y) || (TK_ATSIGN == y)) cp1++; else if ('\"' == *cp1) /* quoted string in pattern */ { for (cp1++; ; ) { if (SP > *cp1) break; if ('\"' == *cp1) { cp1++; if ('\"' == *cp1) /* escaped quotation mark in string */ cp1++; else /* character following string */ break; } else cp1++; } } else break; } source_col += (cp1 - cp); cp = cp1; if ('\0' == *cp) break; } for (shp = ce_def_list; NULL != shp; shp = shp->next) { if (0 == memcmp(cp, shp->escape_sentinel, shp->escape_length)) { ce_substitute (shp, source_col, &skip_count); num_subs++; if (MAX_SUBSTITUTIONS >= num_subs) { if (0 < skip_count) /* a substitution occurred */ possible_sentinel = TRUE; } else { sav_last_src_col = TREF(last_source_column); TREF(last_source_column) = source_col; stx_error (ERR_CETOOMANY); TREF(last_source_column) = sav_last_src_col; } break; } } if (!possible_sentinel) { /* in this column */ source_col++; possible_sentinel = TRUE; /* next column may have sentinel */ } } } TREF(lexical_ptr) = (TREF(source_buffer)).addr; advancewindow(); advancewindow(); return; } fis-gtm-V7.0-005/sr_port/lb_init.h0000755000032200000250000000104414342376331015636 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef LB_INIT_INCLUDED #define LB_INIT_INCLUDED void lb_init(void); #endif /* LB_INIT_INCLUDED */ fis-gtm-V7.0-005/sr_port/lcase.mpt0000755000032200000250000000234714342376331015665 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 1989, 2006 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %LCASE ;GT.M %UCASE utility - convert a string to all lower case ;invoke with string in %S to return lower case string in %S ;invoke at INT to execute interactively ;invoke at FUNC as an extrinsic function ;if you make heavy use of this routine, consider $ZCALL ; s %S=$$FUNC(%S) q INT n %S r !,"String: ",%S w !,"Lower: ",$$FUNC(%S),! q FUNC(s) new returnM,returnUTF8,ret,index i ($zver["VMS")!($ZCHSET="M") do quit ret . s returnM="set ret=$tr(s,""ABCDEFGHIJKLMNOPQRSTUVWXYZ" . for index=192:1:207,209:1:221 s returnM=returnM_$char(index) . s returnM=returnM_"""," . s returnM=returnM_"""abcdefghijklmnopqrstuvwxyz" . for index=224:1:239,241:1:253 s returnM=returnM_$char(index) . s returnM=returnM_""")" . xecute returnM s returnUTF8="set ret=$zconvert(s,""L"")" xecute returnUTF8 q ret fis-gtm-V7.0-005/sr_port/lckclr.c0000755000032200000250000000131014342376331015457 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "mlkdef.h" #include "lckclr.h" GBLREF short lks_this_cmd; GBLREF mlk_pvtblk *mlk_pvt_root; void lckclr(void) { short i; mlk_pvtblk *p1; p1 = mlk_pvt_root; for (i = 0; i < lks_this_cmd; i++) { p1->trans = 0; p1 = p1->next; } } fis-gtm-V7.0-005/sr_port/lckclr.h0000755000032200000250000000104014342376331015464 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef LCKCLR_INCLUDED #define LCKCLR_INCLUDED void lckclr(void); #endif /* LCKCLR_INCLUDED */ fis-gtm-V7.0-005/sr_port/lcl_arg1_is_desc_of_arg2.c0000755000032200000250000000141414342376331020766 0ustar librarygtc/**************************************************************** * * * 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 "lv_val.h" boolean_t lcl_arg1_is_desc_of_arg2(lv_val *cur, lv_val *ref) { lv_val *lv; lvTree *lvt; if (cur == ref) return TRUE; lv = cur; while (!LV_IS_BASE_VAR(lv)) { lvt = LV_GET_PARENT_TREE(lv); lv = (lv_val *)LVT_PARENT(lvt); if (lv == ref) return TRUE; } return FALSE; } fis-gtm-V7.0-005/sr_port/lclcol.mpt0000755000032200000250000000174014342376331016042 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2001, 2014 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %lclcol ; ; ; Local Variable Collation Control ; get() quit $view("YLCT") getncol() quit $view("YLCT","ncol") ; return null collation order ; getnct() quit $view("YLCT","nct") ; return numeric collation type ; set(lct,ncol,nct) new $etrap set $etrap="set $ecode="""" quit 0" set:""=$get(lct) lct=$view("YLCT") set:""=$get(ncol) ncol=$view("YLCT","ncol") set:""=$get(nct) nct=$view("YLCT","nct") if (lct'=$view("YLCT"))!(ncol'=$view("YLCT","ncol"))!(nct'=$view("YLCT","nct")) view "YLCT":lct:ncol:nct quit 1 fis-gtm-V7.0-005/sr_port/line.c0000755000032200000250000001604414342376331015146 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "compiler.h" #include "cmd_qlf.h" #include "toktyp.h" #include "opcode.h" #include "mdq.h" #include "mmemory.h" #include "advancewindow.h" #include "show_source_line.h" #include "start_fetches.h" GBLREF command_qualifier cmd_qlf; GBLREF int mlmax; GBLREF mlabel *mlabtab; GBLREF mline mline_root; GBLREF mline *mline_tail; GBLREF triple t_orig; /* head of triples */ error_def(ERR_BLKTOODEEP); error_def(ERR_COMMAORRPAREXP); error_def(ERR_FALLINTOFLST); error_def(ERR_LSEXPECTED); error_def(ERR_MULTFORMPARM); error_def(ERR_MULTLAB); error_def(ERR_NAMEEXPECTED); error_def(ERR_NESTFORMP); boolean_t line(uint4 *lnc) { boolean_t success, embed_error = FALSE; int parmcount, varnum; short int dot_count; mlabel *x; mline *curlin; triple *first_triple, *parmbase, *parmtail, *r, *e; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; first_triple = (TREF(curtchain))->exorder.bl; dot_count = 0; parmbase = NULL; success = TRUE; curlin = (mline *)mcalloc(SIZEOF(*curlin)); curlin->line_number = 0; curlin->table = FALSE; assert(0 == TREF(expr_depth)); curlin->line_number = *lnc; *lnc = *lnc + 1; curlin->table = TRUE; curlin->block_ok = FALSE; (TREF(side_effect_base))[0] = FALSE; TREF(last_source_column) = 0; if (TK_INTLIT == TREF(window_token)) int_label(); if ((TK_IDENT == TREF(window_token)) || (cmd_qlf.qlf & CQ_LINE_ENTRY)) START_FETCHES(OC_LINEFETCH); else newtriple(OC_LINESTART); CHKTCHAIN(TREF(curtchain), exorder, FALSE); assert(&t_orig == TREF(curtchain)); TREF(pos_in_chain) = *(TREF(curtchain)); if (TK_IDENT == TREF(window_token)) { x = get_mladdr(&(TREF(window_ident))); if (x->ml) { stx_error(ERR_MULTLAB); success = FALSE; } else { assert(NO_FORMALLIST == x->formalcnt); x->ml = curlin; advancewindow(); if (TK_COLON != TREF(window_token)) mlmax++; else { x->gbl = FALSE; advancewindow(); } } if (success && (TK_LPAREN == TREF(window_token))) { advancewindow(); parmbase = parmtail = newtriple(OC_BINDPARM); /* To error out on fall-throughs to labels with a formallist, we are inserting an error immediately before * the LINESTART/LINEFETCH opcode. So, first we need to find the LINESTART/LINEFETCH preceding the * BINDPARM we just inserted. */ assert((OC_LINESTART == parmbase->exorder.bl->opcode) || (OC_LINEFETCH == parmbase->exorder.bl->opcode)); assert(0 != parmbase->exorder.bl->src.line); /* No error should be inserted before the first label of the routine. */ if ((mlabtab->rson != x) || TREF(code_generated)) { e = maketriple(OC_RTERROR); e->operand[0] = put_ilit(ERR_FALLINTOFLST); /* Not a subroutine/func reference. */ e->operand[1] = put_ilit(FALSE); r = parmbase->exorder.bl->exorder.bl; dqins(r, exorder, e); embed_error = TRUE; } if (success) { for (parmcount = 0; TK_RPAREN != TREF(window_token); parmcount++) { if (TK_IDENT != TREF(window_token)) { stx_error(ERR_NAMEEXPECTED); success = FALSE; break; } else { varnum = get_mvaddr(&(TREF(window_ident)))->mvidx; for (r = parmbase->operand[1].oprval.tref; r; r = r->operand[1].oprval.tref) { assert(TRIP_REF == r->operand[0].oprclass); assert(ILIT_REF == r->operand[0].oprval.tref->operand[0].oprclass); assert((TRIP_REF == r->operand[1].oprclass) || (NO_REF == r->operand[1].oprclass)); if (r->operand[0].oprval.tref->operand[0].oprval.ilit == varnum) { stx_error(ERR_MULTFORMPARM); success = FALSE; break; } } if (!success) break; r = newtriple(OC_PARAMETER); parmtail->operand[1] = put_tref(r); r->operand[0] = put_ilit(varnum); parmtail = r; advancewindow(); } if (TK_COMMA == TREF(window_token)) advancewindow(); else if (TK_RPAREN != TREF(window_token)) { stx_error(ERR_COMMAORRPAREXP); success = FALSE; break; } } } if (success) { advancewindow(); parmbase->operand[0] = put_ilit(parmcount); x->formalcnt = parmcount; assert(!mlabtab->lson); if ((mlabtab->rson == x) && !TREF(code_generated)) mlabtab->formalcnt = parmcount; } } } if (success && (TK_EOL != TREF(window_token))) { if (TK_SPACE != TREF(window_token)) { stx_error(ERR_LSEXPECTED); success = FALSE; } else { assert(0 == dot_count); for (;;) { if (TK_SPACE == TREF(window_token)) advancewindow(); else if (TK_PERIOD == TREF(window_token)) { dot_count++; advancewindow(); } else break; } } if ((NULL != parmbase) && dot_count) { dot_count = TREF(block_level); stx_error(ERR_NESTFORMP); /* Should be warning */ success = FALSE; } if ((TREF(block_level) + (int4)(mline_tail->block_ok)) < dot_count) { dot_count = TREF(block_level); if (!(TREF(compile_time) && !(cmd_qlf.qlf & CQ_WARNINGS))) { show_source_line(TRUE); dec_err(VARLSTCNT(1) ERR_BLKTOODEEP); } TREF(source_error_found) = ERR_BLKTOODEEP; success = FALSE; } } if ((TREF(block_level) < dot_count) || (mline_tail == &mline_root)) { mline_tail->child = curlin; curlin->parent = mline_tail; TREF(block_level) = dot_count; } else { for (; dot_count < TREF(block_level); (TREF(block_level))--) if (NULL != mline_tail->parent) mline_tail = mline_tail->parent; mline_tail->sibling = curlin; curlin->parent = mline_tail->parent; } mline_tail = curlin; if (success) { assert(TREF(for_stack_ptr) == TADR(for_stack)); *(TREF(for_stack_ptr)) = NULL; success = linetail(); if (success) { assert(TREF(for_stack_ptr) == TADR(for_stack)); if (*(TREF(for_stack_ptr))) tnxtarg(*(TREF(for_stack_ptr))); } } assert(TREF(for_stack_ptr) == TADR(for_stack)); if (first_triple->exorder.fl == TREF(curtchain)) newtriple(OC_NOOP); /* empty line (comment, blank, etc) */ if (embed_error) { /* The entry point to the label should be LINESTART/LINEFETCH, not the RTERROR. */ curlin->externalentry = e->exorder.fl; TREF(code_generated) = TRUE; } else { curlin->externalentry = first_triple->exorder.fl; /* First_triple points to the last triple before this line was processed. Its forward link will point to a * LINEFETCH or a LINESTART, or possibly a NOOP. If the line was a comment, there is only a LINESTART, and * hence no "real" code yet. */ TREF(code_generated) = TREF(code_generated) | ((OC_NOOP != first_triple->exorder.fl->opcode) && (first_triple->exorder.fl->exorder.fl != TREF(curtchain))); } return success; } fis-gtm-V7.0-005/sr_port/linetail.c0000755000032200000250000000244014342376331016013 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "compiler.h" #include "toktyp.h" #include "opcode.h" #include "advancewindow.h" #include "cmd.h" error_def(ERR_CMD); error_def(ERR_SPOREOL); int linetail(void) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; TREF(rts_error_in_parse) = FALSE; for (;;) { while (TK_SPACE == TREF(window_token)) advancewindow(); if (TK_EOL == TREF(window_token)) return TRUE; if (!cmd()) { if (!ALREADY_RTERROR) TREF(source_error_found) ? stx_error(TREF(source_error_found)) : stx_error(ERR_CMD); assert((TREF(curtchain))->exorder.bl->exorder.fl == TREF(curtchain)); assert(TREF(source_error_found)); return FALSE; } if ((TK_SPACE != TREF(window_token)) && (TK_EOL != TREF(window_token))) { stx_error(ERR_SPOREOL); return FALSE; } } } fis-gtm-V7.0-005/sr_port/linktrc.h0000644000032200000250000000212114342376331015656 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2015 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. * * * ****************************************************************/ #ifndef INDRCOMPTRC_H #define INDRCOMPTRC_H /* Provide some tracing for indirect compiles but due to the multiple rtnhdr.h files, these macros have * been moved out to this supplemental header file so can be included where needed. * * Uncomment below #define or use -DDEBUG_INDRCOMP compilation option to enable debugging macros. */ /* #define DEBUG_INDRCOMP */ #if defined(DEBUG_INDRCOMP) # define DBGINDCOMP(x) DBGFPF(x) # define DBGINDCOMP_ONLY(x) x # include "gtm_stdio.h" # include "gtmio.h" # include "io.h" #else # define DBGINDCOMP(x) # define DBGINDCOMP_ONLY(x) #endif #endif fis-gtm-V7.0-005/sr_port/list_file.h0000755000032200000250000000137514342376331016177 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef LIST_FILE_INCLUDED #define LIST_FILE_INCLUDED void open_list_file(void); void close_list_file(void); void list_chkpage(void); void list_head(bool newpage); void list_line(char *c); void list_line_number(void); void list_tab(void); #endif fis-gtm-V7.0-005/sr_port/lke.h0000755000032200000250000000245114342376331014774 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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. * * * ****************************************************************/ #ifndef LKE_H_INCLUDED #define LKE_H_INCLUDED #include "error.h" bool lke_get_answ(char *prompt); bool lke_showlock(struct CLB *lnk, mlk_shrblk_ptr_t tree, mstr *name, bool all, bool wait, bool interactive, int4 pid, mstr one_lock, boolean_t exact); void lke_formatlockname(mlk_shrblk_ptr_t node, mstr* name); bool lke_showtree(struct CLB *lnk, mlk_pvtctl_ptr_t pctl, bool all, bool wait, pid_t pid, mstr one_lock, bool memory, int *shr_sub_size); void lke_exit(void); void lke_clean(void); void lke_clear(void); void lke_growhash(void); void lke_help(void); void lke_rehash(void); void lke_show(void); void lke_show_memory(mlk_pvtctl_ptr_t pctl, mlk_shrblk_ptr_t bhead, char *prefix); void lke_show_hashtable(mlk_pvtctl_ptr_t pctl); CONDITION_HANDLER(lke_ctrlc_handler); #endif /* __LKE_H__ */ fis-gtm-V7.0-005/sr_port/lke.hlp0000644000032200000250000005333414342376331015333 0ustar librarygtc1 Introduction Introduction The M Lock Utility (LKE) is a tool for examining and changing the GT.M LOCK environment. For a description of M LOCKs, refer to the LOCKs section in the General Language Features of M chapter and the description of the LOCK command in the Commands chapter of the Programmers Guide. The two primary functions of the M Lock Utility (LKE) are: 1. SHOW all or specified LOCKs currently active 2. CLEAR all or specified LOCKs currently active When debugging an M application, you may use LKE to identify a possible deadlock situation, that is, two or more processes have LOCKs and are waiting to add resource names LOCKed by the other(s). Process 1 Process 2 LOCK A LOCK B LOCK +A LOCK +B Process 1 has A LOCKed and attempts to LOCK B. Process 2 has B LOCKed and attempts to LOCK A. Because these processes do not release their current LOCKs before adding additional LOCKs, nor do they provide a timeout to detect the problem, they are deadlocked. Neither process can proceed normally. You can use LKE to release one of the LOCKs so both processes may execute. However, because releasing a LOCK may cause the process to violate its design assumptions, terminating one process is generally a safer way to break the deadlock. **Note** When a process leaves M, GT.M normally releases any LOCKs or ZALLOCATEs held by that process. If a GT.M process terminates abnormally, or if the system "crashes" while a GT.M process is active, GT.M cannot perform normal clean-up. However, as soon as any other process waits several seconds for a LOCK owned by a process that no longer exists, GT.M automatically clears the "orphaned" LOCK. 2 Invoke Invoke GT.M installation procedure places the LKE utility package in a directory specified by the environment variable gtm_dist. LKE requires that the environment variable gtmgbldir be defined. Invoke LKE using the following command at the shell prompt. If this does not work, consult your system manager to investigate setup and file access issues. $gtm_dist/lke LKE> **Important** Always run LKE on the node where the lock is held. When LKE is ready to accept commands, it displays the LKE> prompt. To leave LKE, enter the EXIT command at the LKE> prompt. When additional information is entered on the command line after the LKE command, LKE processes the additional information as its command. $gtm_dist/lke show -all This command displays all current LOCKs and then returns to the shell prompt. If your LKE argument contains quotes, precede each quote in the argument by a back-slash (\) or enclose the entire argument in a set of quotes (matching single or double). Apply this convention only for those LKE commands that you run from the shell. $gtm_dist/lke show -lock="^Account(\"Name\")" $gtm_dist/lke show -lock='^Account("Name")' Both these commands display the status of LOCK ^Account("Name") in the default region. 1 Commands Commands The general format of LKE commands is: command [-qualifier[=qualifier-value]] LKE accepts command and qualifier abbreviations. The section describing each command provides the minimal abbreviation that can be used for that command, and the command qualifiers, if any. FIS recommends the use of a minimum of four characters for key words in scripts to ensure new keywords do not conflict with older scripts. 2 Clear Clear Use the CLEAR command to remove active LOCKs. **Caution** FIS recommends restricting the use of the LKE CLEAR facility to debugging environments; removing LOCKs in a production environment typically violates application design assumptions and can cause aberrant process behavior. GT.M automatically removes abandoned LOCKs so it is typically safer to MUPIP STOP a process that is inappropriately hanging on to a LOCK. The format of the CLEAR command is: CLE[AR] [-qualifier...] The optional qualifiers are: -A[LL] -L[OCK] -[NO]EXACT -[NO]I[NTERACTIVE] -O[UTPUT]="file-name" -P[ID]=pid -R[EGION]=region-name By default, CLEAR operates interactively(-INTERACTIVE). Qualifiers for CLEAR -A[LL] Specifies all current LOCKs. o -ALL removes all current LOCKs. o If used, CLEAR and -REGION qualifier, -ALL removes all LOCKs in that region. o Issue a CLEAR - ALL only when there are no active GT.M processes using LOCKs, or when you can predict the effect on the application. o By default, CLEAR -ALL operates interactively (-INTERACTIVE). -[NO]EXACT Limits the CLEAR command to the exact resource name specified with -LOCK=resource_name. NOEXACT (the default) treats the specified resource name as a prefix and works not only on it, but also on any of its descendants, since their existence effectively LOCK their parent tree. -L[OCK]=""resource_name"" Unless used with -EXACT, specifies the leading prefix for an implicit wild card search of all locks that start with the resource_name. o The resource_name is enclosed in two double quotation marks ("" ""). Because M resource names are formatted the same as global nodes with punctuation characters, in this context they are usually enclosed in sets of double quotation marks with string subscripts enclosed in sets of two double quotations. o When used with CLEAR, -LOCK removes the locks that start with resource_name. o When used with SHOW,-LOCK provides a precise way to examine the specified lock. -[NO]I[NTERACTIVE] Interactively clears one LOCK at a time. LKE displays each current LOCK with the PID of the owner process and prompts for verification that the LOCK should be cleared. LKE retains the LOCK for any response other than Y[ES]. o By default, CLEAR operates interactively (-INTERACTIVE). o To avoid holding a lock resource too long, LKE skips to the next matching LOCK if there is no operator response for several seconds. o -NOINTERACTIVE forces the action to take place without user confirmation of each change. Using -NOINTERACTIVE prevents the LKE operator from controlling the LOCK subsystem for potentially long periods of time when many locks are held. To do this, it limits the amount of time it waits for each response. -O[UTPUT]="file-name" Directs the reporting of all specified LOCKs to a file. o If you specify an existing file, LKE creates a new version and overwrites that file. o If file-name has permission issues, OUTPUT reports the cause of the error. o The -OUTPUT qualifier is compatible with all other qualifiers. o By default, CLEAR sends output messages to stdout. -P[ID]=pid Specifies the process identification number that holds a LOCK on a resource name. o LKE interprets pid as a decimal number. o PID clears LOCKs held by the process with the specified process identification number. o Provides a means for directing CLEAR to LOCKs held by a process that is behaving abnormally. o The -PID qualifier is compatible with all other qualifiers. -R[EGION]=region-name region-namespecifies the region that holds the locked resource names. o REGION clears LOCKs mapped by the current global directory to a region specified by the region-name. o The -REGION qualifier is compatible with all other qualifiers. o By default, CLEAR -REGION= operates interactively (-INTERACTIVE). Example: LKE>CLEAR -ALL This command clears all current LOCKs. Example: LKE>clear -pid=2325 -interactive This command presents all LOCKs held by the process with PID equal to 2325. You can choose whether or not to clear each LOCK. LKE>clear -reg=areg -interactive This command produces an output like the following: AREG ^a Owned by PID= 2083 which is an existing process Clear lock ? Type Yes or Y in response to the prompt. LKE responds with an informational message: %GTM-S-LCKGONE, Lock removed : ^a Type Yes or N or No or N until all LOCKs are displayed and acted upon. LKE> clear -pid=4208 -nointeractive This command clears the lock held by a process with PID 4208. This command produces an output like the following: DEFAULT Lock removed : ^A Note that -NOINTERACTIVE forced the action without asking for a confirmation. Example: LKE>clear -lock="^a("b") Clear lock ? y Lock removed : ^a("b") LKE> This command clears lock ^a("b") in the default region. Example: LKE>clear -lock="^a" -nointeractive This command clears all the locks that start with "^a" in the default region. -NOINTERACTIVE qualifier instructs LKE to clear these locks without further user intervention. Example: LKE>clear -lock="^a" -exact -nointeractive This command clears lock ^a in the default region. -NOINTERACTIVE instructs LKE to clear lock ^a without further user intervention. Example: LKE>CLEAR -PID=4109 -LOCK=""^A"" Clear lock ? Y Lock removed : ^A LKE> This command clears LOCK ^A held by process with PID 4109. 2 CLNup CLNup The CLNUP command initiates a cleanup operation of the lock space to remove any abandoned artifacts left by processes that exited without releasing their LOCKs. The CLNUP processing also checks for the evidence of any entry that has been misplaced by an "overflow" condition; if it finds any, it attempts to automatically repair it, and, if it cannot, it produces a MLKHASHTABERR warning message. On seeing such a message: 1. Stop all access to (at least) the affected region(s) to ensure that the database is completely quiescent. 2. Use MUPIP SET -LOCK_SPACE to set, and, if appropriate raise, the number of pages allocated to the management of M locks associated with the affected region(s) before resuming normal operations. Note that step 1 is necessary because using MUPIP SET -LOCK_SPACE is a standalone operation even with the current value. The format of the CLNUP command is: CLN[UP] [-qualifier...] The optional qualifiers are: -A[LL] -I[NTEG] -P[ERIODIC]=n -R[EGION]=region-name By default, CLNUP operates on all regions (-ALL). Qualifiers of CLNUP -A[LL] Specifies all regions. -I[NTEG] Specifies that LKE should validate the integrity of the lock space and report any issues. -P[ERIODIC]=n Specifies that LKE perform a CLNUP every n seconds, which, if you desire active cleanup, is much lighter weight than repeated invocations of LKE from a shell script. You can stop LKE CLNUP -PERIODIC with MUPIP STOP . FIS recommends running LKE CLNUP -PERIODIC=n with a value of n that appears to prevent growth in the elements in the lock space as reported by LKE SHOW over substantial periods of time. -R[EGION] Specifies that LKE restricts CLNUP operations to a region. 2 SHow SHow Use the SHOW command to get status of the LOCK mechanism and the LOCK database. The format of the SHOW command is: SH[OW] [-qualifier...] The optional qualifiers are: -A[LL] -L[OCK] -[NO]C[RITICAL] -O[UTPUT]="file-name" -P[ID]=pid -R[EGION]=region-name -W[AIT] o By default, SHOW displays -A[LL]. o The SHOW command reports active LOCKs. Information includes the LOCK resource name and the process identification (PID) of the LOCK owner. o All invocations of LKE SHOW include utilization information, in the form of available/total space, about shared subscript data space related to LOCK commands. This information appears as a message of the form: %GTM-I-LOCKSPACEINFO, Region: : processes on queue: 0/160; LOCK slots in use: lll/120; SUBSCRIPT slot bytes in use: ssss/5052. Additionally, LKE SHOW also displays a LOCKSPACEUSE message. If the lock space is full, LKE SHOW also displays a LOCKSPACEFULL error. o A LOCK command which finds no room in LOCK_SPACE to queue a waiting LOCK, does a slow poll waiting for LOCK_SPACE to become available. If LOCK does not acquire the ownership of the named resource with the specified timeout, it returns control to the application with $TEST=0. If timeout is not specified, the LOCK command continues to do a slow poll till the space becomes available. o LOCK commands which find no available lock space send a LOCKSPACEFULL message to the operator log. To prevent flooding the operator log, GT.M suppresses further such messages until the lock space usage drops below 75% full. o The results of a SHOW may be immediately "outdated" by M LOCK activity. o If the LOCK is owned by a GT.CM server on behalf of a client GT.M process, then LKE SHOW displays the client NODENAME (limited to the first 15 characters) and clientPID. The client PID (CLNTPID) is a decimal value in UNIX. For example, %GTM-I-LOCKSPACEUSE, Estimated free lock space: 50% of 40 pages shows the amount of free space along with the number of pages configured for lock space **Note** GT.CM is an RPC-like way of remotely accessing a GT.M database. -ALL Specifies all current LOCKs. o -ALL displays all current LOCKs in all regions and information about the state of processes owning these LOCKs. o The -ALL qualifier is compatible with all other qualifiers. o When -ALL is combined with -PID or -REGION, the most restrictive qualifier prevails. o SHOW -ALL and -WAIT displays both -ALL and -WAIT information. -L[OCK]=resource_name resource_name specifies a single lock. o The resource_name is enclosed in double quotation marks ("" ""). Because M resource names are formatted the same as global nodes with punctuation characters, in this context they are usually enclosed in sets of double quotation marks with string subscripts enclosed in sets of two double quotations. o When used with the CLEAR command, the LOCK qualifier removes the specified lock. o When used with the SHOW command, the LOCK qualifier provides a precise way to examine the specified lock and any descendant LOCKed resources. -[NO]C[RITICAL] Allows the SHOW command to work even if another process is holding a critical section. o By default LKE operates in CRIT mode and ensures a consistent view of LOCKs by using the database critical section(s). o -NOCRIT displays the PID of any process currently holding the LOCK critical section. -O[UTPUT]="file-name" Directs the reporting of all specified LOCKs to a file. o If you specify an existing file, LKE creates a new version and overwrites that file. o The -OUTPUT qualifier is compatible with all other qualifiers. o By default, the SHOW command send output messages to stdout. -P[ID]=pid Specifies the process identification number that holds a LOCK on a resource name. o LKE interprets pid as a decimal number. o PID displays all LOCKs owned by the specified process identification number. o The -PID qualifier is compatible with all other qualifiers; the most restrictive of the qualifiers prevails. o By default, SHOW displays the LOCKs for all PIDs. -R[EGION]=region-name Specifies the region that holds the locked resource names. o The REGION qualifier displays LOCKs of that specified region. o The REGION qualifier is compatible with all other qualifiers; the most restrictive of the qualifiers prevails. o By default, SHOW displays the LOCKs for all regions. -W[AIT] Displays the LOCK resource name and the process state information of all processes waiting for the LOCK to be granted. o SHOW -WAIT does not display the owner of the LOCK. o SHOW -ALL -WAIT displays both -ALL and -WAIT information. o When a process abandons a "wait" request, that request may continue to appear in LKE SHOW -WAIT displays. This appearance is harmless, and is automatically eliminated if the GT.M lock management requires the space which it occupies. Use the following procedure to display all LOCKs active in the database(s) defined by the current global directory. LKE> SHOW -ALL -WAIT This produces an output like the following: No locks were found in DEFAULT AREG ^a Owned by PID=2080 which is an existing process BREG ^b(2) Owned by PID= 2089 which is a nonexistent process No locks were found in CREG Example: LKE>SHOW -ALL This command displays all LOCKs mapped to all regions of the current global directory. It produces an output like the following: DEFAULT ^A Owned by PID= 5052 which is an existing process ^B Owned by PID= 5052 which is an existing process %GTM-I-LOCKSPACEUSE, Estimated free lock space: 99% of 40 pages Example: LKE>show -lock="^a"(""b"")" This command shows lock ^a("b") in the default region. Example: LKE>SHOW -CRIT This command displays all the applicable locks held by a process that is holding a critical section. Example: LKE>show -all -output="abc.lk" This command create a new file called abc.lk that contains the output of the SHOW -ALL command. Example: LKE>show -pid=4109 This command displays all locks held by process with PID 4109 and the total lock space usage. Example: LKE>show -region=DEFAULT -lock=""^A"" This command displays the lock on ^A in the region DEFAULT. It produces an output like the following: DEFAULT ^A Owned by PID= 5052 which is an existing process %GTM-I-LOCKSPACEUSE, Estimated free lock space: 99% of 40 pages 2 Exit Exit The EXIT command ends an LKE session. The format of the EXIT command is: E[XIT] 2 Help Help The HELP command explains LKE commands. The format of the HELP command is: H[ELP] [options...] Enter the LKE command for which you want information at the Topic prompt(s) and then press RETURN or to return to the LKE prompt. Example: LKE> HELP SHOW This command displays help for the SHOW command. 2 SPawn SPawn Use the SPAWN command to create a sub-process for access to the shell without terminating the current LKE environment. Use the SPAWN command to suspend a session and issue shell commands such as ls or printenv. The format of the SPAWN command is: SP[AWN] The SPAWN command has no qualifiers. Example: LKE>spawn This command creates a sub-process for access to the current shell without terminating the current LKE environment. Type exit to return to LKE. 1 Summary Summary +-------------------------------------------------------------------+ | COMMAND | QUALIFIER | COMMENTS | |---------+---------------------+-----------------------------------| | | -ALL | | | | -L[OCK] | | | | -[NO]CRIT | | | | -[NO]EXACT | | | C[LEAR] | | Use CLEAR with care and planning. | | | -[NO]I[NTERACTIVE] | | | | -O[UTPUT]=file-name | | | | -P[ID]=pid | | | | -R[EGION]=name | | |---------+---------------------+-----------------------------------| | E[XIT] | none | - | |---------+---------------------+-----------------------------------| | H[ELP] | [option] | - | |---------+---------------------+-----------------------------------| | | -ALL | | | | -L[OCK] | | | | -[NO]CRIT | | | | -N[OINTERACTIVE] | | | SH[OW] | | - | | | -O[UTPUT]=file-name | | | | -P[ID]=pid | | | | -R[EGION]=name | | | | -W[AIT] | | |---------+---------------------+-----------------------------------| | SP[AWN] | none | shellcommand | +-------------------------------------------------------------------+ 1 Copyright Copyright Copyright 2022 Fidelity National Information Services, Inc. and/or its subsidiaries. All rights reserved. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts and no Back-Cover Texts. GT.M(TM) is a trademark of Fidelity National Information Services, Inc. Other trademarks are the property of their respective owners. This document contains a description of GT.M and the operating instructions pertaining to the various functions that comprise the system. This document does not contain any commitment of FIS. FIS believes the information in this publication is accurate as of its publication date; such information is subject to change without notice. FIS is not responsible for any errors or defects. fis-gtm-V7.0-005/sr_port/lke_clean.c0000644000032200000250000003060514342376331016130 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018-2021 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 #include "mdef.h" #include "mlkdef.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "cmidef.h" /* for cmmdef.h */ #include "hashtab_mname.h" /* needed for cmmdef.h */ #include "cmmdef.h" /* for gtcmtr_protos.h */ #include "util.h" #include "lke.h" #include "lke_getcli.h" #include "mlk_shrclean.h" #include "mlk_shrsub_garbage_collect.h" #include "interlock.h" #include "mlk_garbage_collect.h" #include "sleep.h" #include "min_max.h" #include "gtmmsg.h" #include "do_shmat.h" #include "mlk_ops.h" #include "mlk_shrblk_delete_if_empty.h" #include "mlk_shrhash_find_bucket.h" GBLREF gd_addr *gd_header; GBLREF VSIG_ATOMIC_T util_interrupt; GBLREF uint4 process_id; error_def(ERR_MLKCLEANED); error_def(ERR_MLKHASHTABERR); error_def(ERR_NOREGION); error_def(ERR_MLKHASHWRONG); error_def(ERR_CTRLC); void lke_clean(void) { /* Arguments for lke_getcli */ bool locks, all = TRUE, wait = TRUE, interactive = FALSE, match = FALSE, memory = TRUE, nocrit = TRUE; boolean_t exact = TRUE, integ = FALSE; int4 pid, repeat, bc_before, bc_after; gtm_int8 sleep_time; mstr regname, node, one_lock; char regbuf[MAX_RN_LEN], nodebuf[32], one_lockbuf[MAX_KEY_SZ]; int fi, mi; uint4 ti, num_buckets, bucket_offset, loop_cnt, loop_cnt2, total_len; mlk_shrhash_map_t usedmap, usedmap2; mlk_subhash_state_t hs; mlk_subhash_res_t hashres; mlk_shrhash_ptr_t shrhash, search_bucket, check_bucket, free_bucket; mlk_shrblk_ptr_t shrblk, shr; mlk_shrsub_ptr_t sub, sub2; gd_region *reg; int n; boolean_t was_crit; mlk_pvtblk pvtblk; DEBUG_ONLY(mlk_shrblk_ptr_t blk); struct timespec start_clock, end_clock; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; regname.addr = regbuf; regname.len = SIZEOF(regbuf); node.addr = nodebuf; node.len = SIZEOF(nodebuf); one_lock.addr = one_lockbuf; one_lock.len = SIZEOF(one_lockbuf); memset(&pvtblk, 0, SIZEOF(mlk_pvtblk)); if (lke_getcli(&all, &wait, &interactive, &pid, ®name, &node, &one_lock, &memory, &nocrit, &exact, &repeat, &integ) == 0) return; do { clock_gettime(CLOCK_MONOTONIC, &start_clock); for (reg = gd_header->regions, n = 0; n != gd_header->n_regions; ++reg, ++n) { /* If region matches and is open */ if (((0 == regname.len) || ((reg->rname_len == regname.len) && !memcmp(reg->rname, regname.addr, regname.len))) && reg->open) { match = TRUE; assert(IS_REG_BG_OR_MM(reg)); if (IS_STATSDB_REGNAME(reg)) continue; /* Construct a dummy pvtblk to pass in */ MLK_PVTCTL_INIT(pvtblk.pvtctl, reg); prepare_for_gc(&pvtblk.pvtctl); assert(pvtblk.pvtctl.ctl->lock_gc_in_progress.u.parts.latch_pid == process_id); GRAB_LOCK_CRIT_AND_SYNC(pvtblk.pvtctl, was_crit); WBTEST_ONLY(WBTEST_MLOCK_HANG, SLEEP_USEC(10000ULL * MILLISECS_IN_SEC, 0);); bc_before = pvtblk.pvtctl.ctl->blkcnt; mlk_garbage_collect(&pvtblk, 0, TRUE); assert(pvtblk.pvtctl.ctl->lock_gc_in_progress.u.parts.latch_pid == process_id); RELEASE_SWAPLOCK(&pvtblk.pvtctl.ctl->lock_gc_in_progress); bc_after = pvtblk.pvtctl.ctl->blkcnt; if (bc_after > bc_before) gtm_putmsg_csa(CSA_ARG(pvtblk.pvtctl.csa) VARLSTCNT(5) ERR_MLKCLEANED, 3, (bc_after - bc_before), REG_LEN_STR(reg)); if (!integ) { REL_LOCK_CRIT(pvtblk.pvtctl, was_crit); continue; } shrhash = pvtblk.pvtctl.shrhash; num_buckets = pvtblk.pvtctl.shrhash_size; /* NOTE; this test will overwrite values currently locked, do NOT use in production * fills the hash table with junk and puts something outside of the neighborhood * to verify it is detected and reported correctly */ WBTEST_ONLY(WBTEST_TRASH_HASH_NO_RECOVER, mlk_shrblk_ptr_t newfreehead; MLK_SUBHASH_INIT(&pvtblk, hs); sub = malloc(SIZEOF(mlk_shrsub) + 23); sub->length = 24; memcpy(sub->data, "A12345670123456776543210", 24); blk = (mlk_shrblk_ptr_t)R2A(pvtblk.pvtctl.ctl->blkfree); pvtblk.pvtctl.ctl->blkcnt--; assert(0 != blk->rsib); newfreehead = (mlk_shrblk_ptr_t)R2A(blk->rsib); newfreehead->lsib = 0; A2R(pvtblk.pvtctl.ctl->blkfree, newfreehead); CHECK_SHRBLKPTR(pvtblk.pvtctl.ctl->blkfree, pvtblk.pvtctl); CHECK_SHRBLKPTR(newfreehead->rsib, pvtblk.pvtctl); memset(blk, 0, SIZEOF(mlk_shrblk)); blk->owner = 42; blk->sequence = 1; A2R(blk->value, sub); A2R(sub->backpointer, blk); total_len = 0; mlk_shrhash_val_build(blk, &total_len, &hs); MLK_SUBHASH_FINALIZE(hs, total_len, hashres); ti = MLK_SUBHASH_RES_VAL(hashres) % num_buckets; for(int foo=0; foo < MLK_SHRHASH_NEIGHBORS + 2; foo++) { check_bucket = &shrhash[(ti + foo) % num_buckets]; check_bucket->hash = MLK_SUBHASH_RES_VAL(hashres); check_bucket->shrblk_idx = MLK_SHRBLK_IDX(pvtblk.pvtctl, blk); } check_bucket = &shrhash[ti]; check_bucket->usedmap = ~(0); ); /* NOTE; this test will overwrite values currently locked, do NOT use in production * puts space in the first 10 slots of the hash table starting at some position, then fills * outside of the neighborhood to verify that the cleanup will move the misplaced value * if it can */ WBTEST_ONLY(WBTEST_TRASH_HASH_RECOVER, mlk_shrblk_ptr_t newfreehead; MLK_SUBHASH_INIT(&pvtblk, hs); sub = malloc(SIZEOF(mlk_shrsub) + 32); sub->length = 33; memcpy(sub->data, "A12345678901234567890123456789012", 33); blk = (mlk_shrblk_ptr_t)R2A(pvtblk.pvtctl.ctl->blkfree); pvtblk.pvtctl.ctl->blkcnt--; assert(0 != blk->rsib); newfreehead = (mlk_shrblk_ptr_t)R2A(blk->rsib); newfreehead->lsib = 0; A2R(pvtblk.pvtctl.ctl->blkfree, newfreehead); CHECK_SHRBLKPTR(pvtblk.pvtctl.ctl->blkfree, pvtblk.pvtctl); CHECK_SHRBLKPTR(newfreehead->rsib, pvtblk.pvtctl); memset(blk, 0, SIZEOF(mlk_shrblk)); blk->owner = 42; blk->sequence = 1; A2R(blk->lsib, blk); A2R(blk->rsib, blk); A2R(blk->value, sub); A2R(sub->backpointer, blk); total_len = 0; mlk_shrhash_val_build(blk, &total_len, &hs); MLK_SUBHASH_FINALIZE(hs, total_len, hashres); ti = MLK_SUBHASH_RES_VAL(hashres) % num_buckets; free_bucket = &shrhash[ti]; free_bucket->usedmap = 0; for(int foo=0; foo < MLK_SHRHASH_NEIGHBORS + 2; foo++) { check_bucket = &shrhash[(ti + foo) % num_buckets]; if (foo < 10) { check_bucket->hash = 0; check_bucket->shrblk_idx = 0; check_bucket->usedmap = 0; } else { check_bucket->hash = MLK_SUBHASH_RES_VAL(hashres); check_bucket->shrblk_idx = MLK_SHRBLK_IDX(pvtblk.pvtctl, blk); /* When the shift reaches the number of bits in the underlying int, * the following wraps on x86-family processors and goes to zero on * Power processors. Rather than force the wrap behavior on Power, * allow the natural behavior and adjust the expectations of the * test. */ SET_NEIGHBOR(free_bucket->usedmap, foo); } } ); /* Search through the hash table and verify that each entry marked by a bucket hashes to that * bucket and that the entry in each bucket has been marked by the bucket it should be in */ for (fi = 0; fi < num_buckets; fi++) { search_bucket = &shrhash[fi]; usedmap = search_bucket->usedmap; for (loop_cnt = 0; usedmap != 0 && loop_cnt < MLK_SHRHASH_NEIGHBORS; loop_cnt++, usedmap >>= 1) { if (1 == (usedmap & 1U)) { check_bucket = &shrhash[(fi + loop_cnt) % num_buckets]; if (0 == check_bucket->shrblk_idx) { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) MAKE_MSG_WARNING(ERR_MLKHASHWRONG), 2, LEN_AND_LIT("Correcting.")); /* This is low-risk for having concurrency issues if we get * interrupted; it should be atomic */ CLEAR_NEIGHBOR(search_bucket->usedmap, loop_cnt); continue; } shrblk = MLK_SHRHASH_SHRBLK(pvtblk.pvtctl, check_bucket); assert(0 != shrblk->value); MLK_SUBHASH_INIT(&pvtblk, hs); total_len = 0; mlk_shrhash_val_build(shrblk, &total_len, &hs); MLK_SUBHASH_FINALIZE(hs, total_len, hashres); /* If this triggers, it means a bucket was marked as used for a * particular hash value, but is in fact used by a different hash * value; we can not easily recover from this, and the safest * course of action is for the user to bring things down ASAP */ if (((MLK_SUBHASH_RES_VAL(hashres) % num_buckets) != fi) || (MLK_SUBHASH_RES_VAL(hashres) != check_bucket->hash)) { REL_LOCK_CRIT(pvtblk.pvtctl, was_crit); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_MLKHASHWRONG, 2, LEN_AND_LIT("Can't correct, exiting.")); } } } if (0 != search_bucket->shrblk_idx) { /* Verify that the value in this bucket should be in this bucket */ MLK_SUBHASH_INIT(&pvtblk, hs); total_len = 0; mlk_shrhash_val_build(MLK_SHRHASH_SHRBLK(pvtblk.pvtctl, search_bucket), &total_len, &hs); MLK_SUBHASH_FINALIZE(hs, total_len, hashres); ti = MLK_SUBHASH_RES_VAL(hashres) % num_buckets; check_bucket = &shrhash[ti]; bucket_offset = (num_buckets + fi - ti) % num_buckets; if (MLK_SHRHASH_NEIGHBORS <= bucket_offset) { /* If this triggers, it means the hash was more than 32 away from correct bucket; not in the right neighborhood -- try moving it */ mi = mlk_shrhash_find_bucket(&pvtblk.pvtctl, MLK_SUBHASH_RES_VAL(hashres)); shr = MLK_SHRHASH_SHRBLK(pvtblk.pvtctl, search_bucket); if (mi == -1) { /* If this triggers, it mean the hash table is full and things are * out of position; very bad * Emit a critical warning and carry on */ char name_buffer[MAX_ZWR_KEY_SZ + 1]; MSTR_DEF(name, 0, name_buffer); lke_formatlockname(shr, &name); gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) MAKE_MSG_WARNING(ERR_MLKHASHTABERR), 2, RTS_ERROR_MSTR(&name)); continue; } mlk_shrhash_insert(&pvtblk.pvtctl, ti, mi, MLK_SHRBLK_IDX(pvtblk.pvtctl, shr), MLK_SUBHASH_RES_VAL(hashres)); /* Clear old bucket */ search_bucket->shrblk_idx = 0; search_bucket->hash = 0; } else { usedmap = check_bucket->usedmap; /* If this triggers, it means the bucket we are at has a value that was * not marked as overflow in the correct bucket; this could be a result of * the hashtable being full */ if (!IS_NEIGHBOR(usedmap, bucket_offset)) { REL_LOCK_CRIT(pvtblk.pvtctl, was_crit); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_MLKHASHWRONG, 2, LEN_AND_LIT("Can't correct, exiting.")); } } } } WBTEST_ONLY(WBTEST_TRASH_HASH_RECOVER, lke_show_hashtable(&pvtblk.pvtctl); ); REL_LOCK_CRIT(pvtblk.pvtctl, was_crit); } } if (!match && (0 != regname.len)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_NOREGION, 2, regname.len, regname.addr); if (!repeat) break; clock_gettime(CLOCK_MONOTONIC, &end_clock); /* seconds to milliseconds, then nanoseconds to milliseconds */ sleep_time = (repeat * MICROSECS_IN_SEC); if (end_clock.tv_nsec >= start_clock.tv_nsec) { sleep_time -= (end_clock.tv_sec - start_clock.tv_sec) * MICROSECS_IN_SEC; sleep_time -= (end_clock.tv_nsec - start_clock.tv_nsec) / NANOSECS_IN_USEC; } else { sleep_time -= (end_clock.tv_sec - 1 - start_clock.tv_sec) * MICROSECS_IN_SEC; sleep_time -= (NANOSECS_IN_SEC + end_clock.tv_nsec - start_clock.tv_nsec) / NANOSECS_IN_USEC; } if (sleep_time > 0) SLEEP_USEC(sleep_time, 0); if (util_interrupt) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CTRLC); } while (TRUE); } fis-gtm-V7.0-005/sr_port/lke_clear.c0000644000032200000250000001002414342376331016125 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* * ------------------------------------------------- * lke_clear.c : removes locks for qualified regions * used in : lke.c * ------------------------------------------------- */ #include #include "mdef.h" #include "gtm_string.h" #include "mlkdef.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "cmidef.h" /* for cmmdef.h */ #include "hashtab_mname.h" /* needed for cmmdef.h */ #include "cmmdef.h" /* for gtcmtr_protos.h */ #include "util.h" #include "gtcmtr_protos.h" #include "lke.h" #include "lke_getcli.h" #include "lke_cleartree.h" #include "gtmmsg.h" #include "interlock.h" #include "rel_quant.h" #include "do_shmat.h" #include "mlk_ops.h" #include "restrict.h" #define NOFLUSH_OUT 0 #define FLUSH 1 #define RESET 2 GBLREF gd_addr *gd_header; GBLREF short crash_count; error_def(ERR_NOREGION); error_def(ERR_UNIMPLOP); error_def(ERR_TEXT); error_def(ERR_BADREGION); error_def(ERR_NOLOCKMATCH); error_def(ERR_RESTRICTEDOP); void lke_clear(void) { bool locks, all = TRUE, wait = FALSE, interactive = TRUE, match = FALSE, memory = FALSE, nocrit = FALSE; boolean_t exact = TRUE, was_crit; int4 pid; int n; char regbuf[MAX_RN_LEN], nodebuf[32], one_lockbuf[MAX_KEY_SZ]; mstr regname, node, one_lock; gd_region *reg; mlk_pvtctl pctl; /* Get all command parameters */ regname.addr = regbuf; regname.len = SIZEOF(regbuf); node.addr = nodebuf; node.len = SIZEOF(nodebuf); one_lock.addr = one_lockbuf; one_lock.len = SIZEOF(one_lockbuf); if (RESTRICTED(lkeclear)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_RESTRICTEDOP, 1, "LKECLEAR"); if (lke_getcli(&all, &wait, &interactive, &pid, ®name, &node, &one_lock, &memory, &nocrit, &exact, 0, 0) == 0) return; /* Search all regions specified on the command line */ for (reg = gd_header->regions, n = 0; n != gd_header->n_regions; ++reg, ++n) { /* If region matches and is open */ if ((regname.len == 0 || reg->rname_len == regname.len && memcmp(reg->rname, regname.addr, regname.len) == 0) && reg->open) { match = TRUE; util_out_print("!/!AD!/", NOFLUSH_OUT, REG_LEN_STR(reg)); /* If distributed database, the region is located on another node */ if (reg->dyn.addr->acc_meth == dba_cm) { # if defined(LKE_WORKS_OK_WITH_CM) /* Remote lock clears are not supported, so LKE CLEAR -EXACT qualifier * will not be supported on GT.CM.*/ locks = gtcmtr_lke_clearreq(reg->dyn.addr->cm_blk, reg->cmx_regnum, all, interactive, pid, &node); # else gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_UNIMPLOP, 0, ERR_TEXT, 2, LEN_AND_LIT("GT.CM region - locks must be cleared on the local node"), ERR_TEXT, 2, REG_LEN_STR(reg)); continue; # endif } else if (IS_REG_BG_OR_MM(reg)) { /* Local region */ MLK_PVTCTL_INIT(pctl, reg); /* Prevent any modifications of locks while we are clearing */ GRAB_LOCK_CRIT_AND_SYNC(pctl, was_crit); locks = pctl.ctl->blkroot == 0 ? FALSE : lke_cleartree(&pctl, NULL, (mlk_shrblk_ptr_t)R2A(pctl.ctl->blkroot), all, interactive, pid, one_lock, exact); REL_LOCK_CRIT(pctl, was_crit); } else { gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(2) ERR_BADREGION, 0); locks = TRUE; } if (!locks) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NOLOCKMATCH, 2, REG_LEN_STR(reg)); } } if (!match && regname.len != 0) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_NOREGION, 2, regname.len, regname.addr); } fis-gtm-V7.0-005/sr_port/lke_clearlock.c0000755000032200000250000000530414342376331017006 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ /* * ----------------------------------------------- * lke_clearlock : removes the qualified lock node * used in : lke_cleartree.c * ----------------------------------------------- */ #include "mdef.h" #include "gtm_string.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "mlkdef.h" #include "cmidef.h" #include "hashtab_mname.h" /* needed for cmmdef.h */ #include "cmmdef.h" #include "util.h" #include "lke.h" #include "cmi.h" #include "gvcmz.h" #include "lke_clearlock.h" #define FLUSH 1 error_def(ERR_LCKGONE); bool lke_clearlock( mlk_pvtctl_ptr_t pctl, struct CLB *lnk, mlk_shrblk_ptr_t node, mstr *name, bool all, bool interactive, int4 pid) { clear_confirm confirm; clear_reply reply; uint4 status; int len; bool unlock = FALSE; sgmnt_addrs *csa; if (node->owner != 0 && (pid == node->owner || pid == 0)) { if (interactive) if (lnk == NULL) unlock = lke_get_answ("Clear lock ? "); else { lnk->mbl = sizeof confirm; lnk->mbf = (unsigned char *)&confirm; lnk->ast = NULL; status = cmi_read(lnk); if ((status & 1) == 0) { ((link_info *)(lnk->usr))->neterr = TRUE; gvcmz_error(CMMS_U_LKEDELETE, status); return FALSE; } unlock = confirm.clear; } else unlock = TRUE; if (unlock) { csa = pctl->csa; node->owner = 0; node->sequence = csa->hdr->trans_hist.lock_sequence++; len = name->len - 1; if (name->addr[len] != '(') ++len; if (lnk == NULL) util_out_print("Lock removed : !AD", FLUSH, len, name->addr); else if (!interactive) { reply.code = CMMS_V_LKESHOW; reply.status = ERR_LCKGONE; reply.locknamelength = len; memcpy(reply.lockname, name->addr, len); lnk->cbl = sizeof reply - (sizeof reply.lockname - len); lnk->mbf = (unsigned char *)&reply; lnk->ast = NULL; status = cmi_write(lnk); if ((status & 1) == 0) { ((link_info *)(lnk->usr))->neterr = TRUE; gvcmz_error(CMMS_V_LKESHOW, status); return FALSE; } } } } return unlock; } fis-gtm-V7.0-005/sr_port/lke_clearlock.h0000755000032200000250000000133014342376331017006 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef LKE_CLEARLOCK_H_INCLUDED #define LKE_CLEARLOCK_H_INCLUDED bool lke_clearlock(mlk_pvtctl_ptr_t pctl, struct CLB *lnk, mlk_shrblk_ptr_t node, mstr *name, bool all, bool interactive, int4 pid); #endif fis-gtm-V7.0-005/sr_port/lke_cleartree.c0000755000032200000250000000646014342376331017021 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ /* * ---------------------------------- * lke_cleartree : clears a lock tree * used in : lke_clear.c * ---------------------------------- */ #include "mdef.h" #include "gtm_signal.h" #include "mlkdef.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "mlk_shrblk_delete_if_empty.h" #include "mlk_wake_pending.h" #include "lke.h" #include "lke_cleartree.h" #include "lke_clearlock.h" #define KDIM 64 /* max number of subscripts */ GBLREF VSIG_ATOMIC_T util_interrupt; error_def(ERR_CTRLC); mlk_shrblk_ptr_t mlk_shrblk_sort(mlk_shrblk_ptr_t head); bool lke_cleartree( mlk_pvtctl_ptr_t pctl, struct CLB *lnk, mlk_shrblk_ptr_t tree, bool all, bool interactive, int4 pid, mstr one_lock, boolean_t exact) { mlk_shrblk_ptr_t node, oldnode, start[KDIM]; unsigned char subscript_offset[KDIM]; static char name_buffer[MAX_ZWR_KEY_SZ + 1]; static MSTR_DEF(name, 0, name_buffer); int depth = 0; bool locks = FALSE, locked, deleted; node = start[0] = mlk_shrblk_sort(tree); subscript_offset[0] = 0; for (;;) { name.len = subscript_offset[depth]; /* Display the lock node */ locked = lke_showlock(lnk, node, &name, all, FALSE, interactive, pid, one_lock, exact); locks |= locked; /* If it was locked, clear it and wake up any processes waiting for it */ if (locked && lke_clearlock(pctl, lnk, node, &name, all, interactive, pid) && node->pending != 0) mlk_wake_pending(pctl, node); /* if a specific lock was requested (-EXACT and -LOCK=), then we are done */ if (exact && (0 != one_lock.len) && locked) return locks; /* Move to the next node */ if (node->children == 0) { /* This node has no children, so move to the right */ oldnode = node; node = (mlk_shrblk_ptr_t)R2A(node->rsib); while (node == start[depth]) { /* There are no more siblings to the right at this depth, so move up and then right */ if (node->parent == 0) { /* We're already at the top, so we're done */ assert(depth == 0); (void)mlk_shrblk_delete_if_empty(pctl, node); return locks; } --depth; node = (mlk_shrblk_ptr_t)R2A(node->parent); (void)mlk_shrblk_delete_if_empty(pctl, oldnode); oldnode = node; node = (mlk_shrblk_ptr_t)R2A(node->rsib); } deleted = mlk_shrblk_delete_if_empty(pctl, oldnode); if (deleted && start[depth] == oldnode) start[depth] = node; } else { /* This node has children, so move down */ ++depth; node = start[depth] = mlk_shrblk_sort((mlk_shrblk_ptr_t)R2A(node->children)); subscript_offset[depth] = name.len; } if (util_interrupt) rts_error_csa(CSA_ARG(pctl->csa) VARLSTCNT(1) ERR_CTRLC); } } fis-gtm-V7.0-005/sr_port/lke_cleartree.h0000755000032200000250000000135414342376331017023 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef LKE_CLEARTREE_H_INCLUDED #define LKE_CLEARTREE_H_INCLUDED bool lke_cleartree(mlk_pvtctl_ptr_t pctl, struct CLB *lnk, mlk_shrblk_ptr_t tree, bool all, bool interactive, int4 pid, mstr one_lock, boolean_t exact); #endif fis-gtm-V7.0-005/sr_port/lke_exit.c0000755000032200000250000000144614342376331016023 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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" /* for EXIT() */ #include "error.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "iosp.h" #include "mlkdef.h" #include "lke.h" void lke_exit(void) { EXIT(SS_NORMAL); } fis-gtm-V7.0-005/sr_port/lke_getcli.c0000755000032200000250000000645314342376331016324 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ /* lke_getcli.c : obtains CLI qualifiers, validates their values used in : lke_clear.c, lke_show.c */ #include "mdef.h" #include "gtm_string.h" #include "cli.h" #include "lke_getcli.h" #include "util.h" /* * ------------------------------------------------------ * Get command line parameters * ------------------------------------------------------ */ int4 lke_getcli(bool *all, bool *wait, bool *inta, int4 *pid, mstr *region, mstr *node, mstr *one_lock, bool *memory, bool *nocrit, boolean_t *exact, int *repeat, boolean_t *integ) { int4 status; unsigned short len; int keylen; char one_lockbuf[MAX_ZWR_KEY_SZ + 1]; status = TRUE; /* * ----------------------------------------------------------- * -INTERACTIVE overrides any defaults -NOINTERACTIVE overrides any defaults * otherwise: default is nointeractive when -ALL is set default is original value of inta without -ALL * ----------------------------------------------------------- */ *all = (*all && cli_present("ALL") == CLI_PRESENT); *inta = *inta && (cli_present("INTERACTIVE") != CLI_NEGATED); *wait = (*wait && cli_present("WAIT") == CLI_PRESENT); *memory = (*memory && cli_present("MEMORY") == CLI_PRESENT); *nocrit = (*nocrit && cli_present("CRIT") == CLI_NEGATED); *exact = (*exact && cli_present("EXACT") == CLI_PRESENT); if (integ) *integ = (cli_present("INTEG") == CLI_PRESENT); if (repeat && cli_present("PERIODIC") == CLI_PRESENT) { if (!cli_get_int("PERIODIC", (int*)repeat)) { *repeat = 0; status = FALSE; } } else if (repeat) *repeat = 0; if (cli_present("PID") == CLI_PRESENT) { #ifdef HEXPID if (!cli_get_hex("PID", pid)) #else assert(SIZEOF(*pid) == SIZEOF(int)); if (!cli_get_int("PID", (int4 *)pid)) #endif { *pid = 0; status = FALSE; } } else *pid = 0 ; if (cli_present("REGION") == CLI_PRESENT) { len = region->len; if (!cli_get_str("REGION", region->addr, &len)) { util_out_print("Error getting REGION parameter",TRUE); region->len = 0; status = FALSE; } else region->len = len; } else region->len = 0; if (cli_present("LOCK") == CLI_PRESENT) { len = one_lock->len; if (!cli_get_str("LOCK", one_lock->addr, &len) || -1 == (keylen = lke_getki(one_lock->addr, len, one_lockbuf))) { util_out_print("Error getting LOCK parameter",TRUE); one_lock->len = 0; status = FALSE; } else { one_lock->len = keylen; memcpy(one_lock->addr, one_lockbuf, keylen); } } else { one_lock->len = 0; one_lock->addr = 0; } if (cli_present("NODE") == CLI_PRESENT) { len = node->len; if (!cli_get_str("NODE", node->addr, &len)) { util_out_print("Error getting NODE parameter",TRUE); node->len = 0; status = FALSE; } else node->len = len; } else node->len = 0; return status ; } fis-gtm-V7.0-005/sr_port/lke_getcli.h0000755000032200000250000000150514342376331016322 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef LKE_GETCLI_H #define LKE_GETCLI_H int4 lke_getcli(bool *all, bool *wait, bool *inta, int4 *pid, mstr *region, mstr *node, mstr *one_lock, bool *memory, bool *nocrit, boolean_t *exact, int *repeat, boolean_t *integ); int lke_getki(char* src, int srclen, char* outptr); #ifdef VMS #define HEXPID #endif #endif fis-gtm-V7.0-005/sr_port/lke_getki.c0000755000032200000250000001146014342376331016152 0ustar librarygtc/**************************************************************** * * * Copyright 2006, 2013 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 "gtm_string.h" #include "lke_getcli.h" #include "gtm_ctype.h" /* Needed for TOUPPER() */ /* This routine performs the necessary transformation of the LOCK keys passed in * from the CLI layer and produces a canonical formatted key. This routine * * validates if the key is indeed in the correct syntax. * * adds quotes to the string subscripts (which were removed by the CLI * layer on UNIX) * * removes the redundant quotes in the key (which were passed intact by * CLI layer on VMS). * */ int lke_getki(char* src, int srclen, char* outbuff) { char *inptr, *nextptr, *intop, *outptr, *tmpptr; mval subsc = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0); char one_lockbuf[MAX_ZWR_KEY_SZ + 1], *one_char; char *valid_char = "HAR"; /* This is used for validating characters following $ZCH and $C */ if (srclen > 1 && '"' == src[0] && '"' == src[srclen - 1]) { outptr = one_lockbuf; for (inptr = ++src, intop = src + srclen - 2; inptr < intop; *outptr++ = *inptr++) { if ('"' == *inptr && (++inptr >= intop || *inptr != '"')) return -1; /* invalid (unescaped) quote within a quoted key */ } src = one_lockbuf; srclen = (int)(outptr - one_lockbuf); } inptr = memchr(src, '(', srclen); if (NULL == inptr) { memcpy(outbuff, src, srclen); return srclen; } ++inptr; outptr = outbuff; memcpy(outptr, src, inptr - src); outptr += inptr - src; for (intop = src + srclen; inptr < intop; inptr = nextptr) { if (')' == *inptr) /* Catches incomplete lists or string concatenations */ return -1; else if ('$' == *inptr) { /* the entire subscript is within $C() or $ZCH */ *outptr++ = '$'; inptr++; if (('z' == *inptr) || ('Z' == *inptr)) { /* Very likely $ZCHAR() */ *outptr++ = 'Z'; inptr++; } if (('c' == *inptr) || ('C' == *inptr)) { *outptr++ = 'C'; inptr++; if (('Z' == *(outptr - 2)) && (('h' == *inptr) || ('H' == *inptr))) { *outptr++ ='H'; inptr++; one_char = valid_char + 1; } else one_char = valid_char; /* Validate/skip letters following C so that we allow C, CH, CHA, CHAR */ while (('\0' != *one_char) && ('(' != *inptr)) if (TOUPPER(*inptr++) != *one_char++) return -1; if ('(' != *inptr) return -1; } else /* We don't support anything other than $C() or $ZCH in locks */ return -1; nextptr = memchr(inptr, ')', intop - inptr); if (NULL == nextptr) return -1; ++nextptr; memcpy(outptr, inptr, nextptr - inptr); outptr += nextptr - inptr; } else { if ('"' == *inptr) /* Is this a quoted string? */ { /*Process character by character because '_' or ',' can be used within the quotes. */ for (nextptr = inptr + 1; nextptr < intop; nextptr++) if ('"' == *nextptr && (nextptr + 1 < intop)) { nextptr++; if ('"' != *nextptr) /* This is not a two double-quote so terminate. */ break; } } else { /* Fast-forward to the next separator */ nextptr = memchr(inptr, '_', intop - inptr); if (NULL == nextptr) { /* Not a string concatineated with $C() or $ZCH() */ nextptr = memchr(inptr, ',', intop - inptr); if (NULL == nextptr) nextptr = intop - 1; } } if (intop - 1 == nextptr) { /* If it reached to the end, it had better closed the paran */ if (')' != *nextptr) return -1; } else if ((',' != *nextptr) && ('_' != *nextptr)) /* If we are not at the end, it must be a separator*/ return -1; subsc.str.len = INTCAST(nextptr - inptr); subsc.str.addr = inptr; if (val_iscan(&subsc)) { memcpy(outptr, subsc.str.addr, subsc.str.len); outptr += subsc.str.len; } else { if (nextptr - 1 > inptr && '"' == *inptr && '"' == *(nextptr - 1)) { /* The string is already enclosed by a pair of quotes */ memcpy(outptr, inptr, nextptr - inptr); outptr += nextptr - inptr; inptr += nextptr - inptr; } else { /* unquoted string: add quotes */ *outptr++ = '"'; for (tmpptr = inptr; tmpptr < nextptr; ++tmpptr) { *outptr++ = *tmpptr; if ('"' == *tmpptr) *outptr++ = '"'; if ('_' == *tmpptr) { *--outptr; nextptr = tmpptr; } } *outptr++ = '"'; } } } if ((',' != *nextptr) && (')' != *nextptr) && ('_' != *nextptr)) return -1; *outptr++ = *nextptr++; } return (int)(outptr - outbuff); } fis-gtm-V7.0-005/sr_port/lke_growhash.c0000644000032200000250000000524314342376331016670 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2018-2021 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 #include "mdef.h" #include "mlkdef.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "cmidef.h" /* for cmmdef.h */ #include "hashtab_mname.h" /* needed for cmmdef.h */ #include "cmmdef.h" /* for gtcmtr_protos.h */ #include "util.h" #include "lke.h" #include "lke_getcli.h" #include "mlk_shrhash_resize.h" #include "interlock.h" #include "sleep.h" #include "min_max.h" #include "gtmmsg.h" #include "do_shmat.h" #include "mlk_ops.h" GBLREF gd_addr *gd_header; error_def(ERR_NOREGION); void lke_growhash(void) { /* Arguments for lke_getcli */ bool locks, all = TRUE, wait = TRUE, interactive = FALSE, match = FALSE, memory = TRUE, nocrit = TRUE; boolean_t exact = TRUE, resize_status; int4 pid; mstr regname, node, one_lock; char regbuf[MAX_RN_LEN], nodebuf[32], one_lockbuf[MAX_KEY_SZ]; gd_region *reg; int regidx; boolean_t was_crit; mlk_pvtctl pctl; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; regname.addr = regbuf; regname.len = SIZEOF(regbuf); node.addr = nodebuf; node.len = SIZEOF(nodebuf); one_lock.addr = one_lockbuf; one_lock.len = SIZEOF(one_lockbuf); if (lke_getcli(&all, &wait, &interactive, &pid, ®name, &node, &one_lock, &memory, &nocrit, &exact, NULL, NULL) == 0) return; for (reg = gd_header->regions, regidx = 0; regidx != gd_header->n_regions; ++reg, ++regidx) { /* If region matches and is open */ if (((0 == regname.len) || ((reg->rname_len == regname.len) && !memcmp(reg->rname, regname.addr, regname.len))) && reg->open) { assert(IS_REG_BG_OR_MM(reg)); if (IS_STATSDB_REGNAME(reg)) continue; match = TRUE; /* Construct a dummy pctl to pass in */ MLK_PVTCTL_INIT(pctl, reg); GRAB_LOCK_CRIT_AND_SYNC(pctl, was_crit); resize_status = mlk_shrhash_resize(&pctl); REL_LOCK_CRIT(pctl, was_crit); util_out_print("Resize of lock hash memory !AZ for region !AD", TRUE, (resize_status ? "succeeded" : "FAILED"), REG_LEN_STR(reg)); } } if (!match && (0 != regname.len)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_NOREGION, 2, regname.len, regname.addr); } fis-gtm-V7.0-005/sr_port/lke_rehash.c0000644000032200000250000000514514342376331016321 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2019-2021 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 #include "mdef.h" #include "mlkdef.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "cmidef.h" /* for cmmdef.h */ #include "hashtab_mname.h" /* needed for cmmdef.h */ #include "cmmdef.h" /* for gtcmtr_protos.h */ #include "util.h" #include "lke.h" #include "lke_getcli.h" #include "mlk_rehash.h" #include "interlock.h" #include "sleep.h" #include "min_max.h" #include "gtmmsg.h" #include "do_shmat.h" #include "mlk_ops.h" GBLREF gd_addr *gd_header; error_def(ERR_NOREGION); void lke_rehash(void) { /* Arguments for lke_getcli */ bool locks, all = TRUE, wait = TRUE, interactive = FALSE, match = FALSE, memory = TRUE, nocrit = TRUE; boolean_t exact = TRUE; int4 pid; mstr regname, node, one_lock; char regbuf[MAX_RN_LEN], nodebuf[32], one_lockbuf[MAX_KEY_SZ]; gd_region *reg; int regidx; boolean_t was_crit; mlk_pvtctl pctl; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; regname.addr = regbuf; regname.len = SIZEOF(regbuf); node.addr = nodebuf; node.len = SIZEOF(nodebuf); one_lock.addr = one_lockbuf; one_lock.len = SIZEOF(one_lockbuf); if (lke_getcli(&all, &wait, &interactive, &pid, ®name, &node, &one_lock, &memory, &nocrit, &exact, NULL, NULL) == 0) return; for (reg = gd_header->regions, regidx = 0; regidx != gd_header->n_regions; ++reg, ++regidx) { /* If region matches and is open */ if (((0 == regname.len) || ((reg->rname_len == regname.len) && !memcmp(reg->rname, regname.addr, regname.len))) && reg->open) { assert(IS_REG_BG_OR_MM(reg)); if (IS_STATSDB_REGNAME(reg)) continue; match = TRUE; /* Construct a dummy pctl to pass in */ MLK_PVTCTL_INIT(pctl, reg); GRAB_LOCK_CRIT_AND_SYNC(pctl, was_crit); mlk_rehash(&pctl); REL_LOCK_CRIT(pctl, was_crit); util_out_print("Rehash of lock completed for region !AD (seed = !UJ)", TRUE, REG_LEN_STR(reg), pctl.ctl->hash_seed); } } if (!match && (0 != regname.len)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_NOREGION, 2, regname.len, regname.addr); } fis-gtm-V7.0-005/sr_port/lke_show.c0000644000032200000250000001504114342376331016023 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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. * * * ****************************************************************/ /* * ------------------------------------------------- * lke_show.c : displays locks for qualified regions * used in : lke.c * ------------------------------------------------- */ #include #include "mdef.h" #include "gtm_string.h" #include "mlkdef.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "filestruct.h" #include "cmidef.h" /* for cmmdef.h */ #include "hashtab_mname.h" /* needed for cmmdef.h */ #include "cmmdef.h" /* for gtcmtr_protos.h */ #include "util.h" #include "gtcmtr_protos.h" #include "lke.h" #include "lke_getcli.h" #include "gtmmsg.h" #include "min_max.h" #include "interlock.h" #include "rel_quant.h" #include "do_shmat.h" #include "mlk_ops.h" #define NOFLUSH_OUT 0 #define FLUSH 1 #define RESET 2 GBLREF gd_addr *gd_header; GBLREF short crash_count; error_def(ERR_UNIMPLOP); error_def(ERR_TEXT); error_def(ERR_NOREGION); error_def(ERR_BADREGION); error_def(ERR_NOLOCKMATCH); error_def(ERR_LOCKSPACEUSE); error_def(ERR_LOCKSPACEFULL); error_def(ERR_LOCKSPACEINFO); error_def(ERR_LOCKCRITOWNER); void lke_show(void) { bool locks, all = TRUE, wait = TRUE, interactive = FALSE, match = FALSE, memory = TRUE, nocrit = TRUE; boolean_t exact = FALSE, was_crit; int4 pid; size_t ls_len; int n; char regbuf[MAX_RN_LEN], nodebuf[32], one_lockbuf[MAX_KEY_SZ]; mlk_ctldata_ptr_t ctl; mstr regname, node, one_lock; gd_region *reg; sgmnt_addrs *csa; int shr_sub_len = 0; float ls_free = 0; /* Free space in bottleneck subspace */ mlk_pvtctl pctl, pctl2; /* Get all command parameters */ regname.addr = regbuf; regname.len = SIZEOF(regbuf); node.addr = nodebuf; node.len = SIZEOF(nodebuf); one_lock.addr = one_lockbuf; one_lock.len = SIZEOF(one_lockbuf); if (lke_getcli(&all, &wait, &interactive, &pid, ®name, &node, &one_lock, &memory, &nocrit, &exact, 0, 0) == 0) return; /* Search all regions specified on the command line */ for (reg = gd_header->regions, n = 0; n != gd_header->n_regions; ++reg, ++n) { /* If region matches and is open */ if (((0 == regname.len) || (reg->rname_len == regname.len) && !memcmp(reg->rname, regname.addr, regname.len)) && reg->open) { match = TRUE; util_out_print("!/!AD!/", NOFLUSH_OUT, REG_LEN_STR(reg)); /* If distributed database, the region is located on another node */ if (reg->dyn.addr->acc_meth == dba_cm) { # if defined(LKE_WORKS_OK_WITH_CM) /* Obtain lock info from the remote node */ locks = gtcmtr_lke_showreq(reg->dyn.addr->cm_blk, reg->cmx_regnum, all, wait, pid, &node); assert(FALSE); /* because "csa" is not initialized here and is used below */ # else gtm_putmsg_csa(NULL, VARLSTCNT(10) ERR_UNIMPLOP, 0, ERR_TEXT, 2, LEN_AND_LIT("GT.CM region - locks must be displayed on the local node"), ERR_TEXT, 2, REG_LEN_STR(reg)); continue; # endif } else if (IS_REG_BG_OR_MM(reg)) { /* Local region */ csa = &FILE_INFO(reg)->s_addrs; ls_len = (size_t)csa->mlkctl_len; ctl = (mlk_ctldata_ptr_t)malloc(ls_len); MLK_PVTCTL_INIT(pctl, reg); if (nocrit) { /* Set shrhash and shrhash_size here when nocrit, as they normally get set up * when grabbing lock crit. * If we have an external lock hash table, attach the shared memory. */ pctl.shrhash_size = pctl.ctl->num_blkhash; if (MLK_CTL_BLKHASH_EXT != pctl.ctl->blkhash) pctl.shrhash = (mlk_shrhash_ptr_t)R2A(pctl.ctl->blkhash); else pctl.shrhash = do_shmat(pctl.ctl->hash_shmid, NULL, 0); } /* Prevent any modification of the lock space while we make a local copy of it */ if (!nocrit) GRAB_LOCK_CRIT_AND_SYNC(pctl, was_crit); memcpy((uchar_ptr_t)ctl, (uchar_ptr_t)csa->mlkctl, ls_len); assert((ctl->max_blkcnt > 0) && (ctl->max_prccnt > 0) && ((ctl->subtop - ctl->subbase) > 0)); pctl2 = pctl; if (MLK_CTL_BLKHASH_EXT == pctl.ctl->blkhash) { pctl2.shrhash = (mlk_shrhash_ptr_t)malloc(SIZEOF(mlk_shrhash) * pctl.shrhash_size); memcpy(pctl2.shrhash, pctl.shrhash, SIZEOF(mlk_shrhash) * pctl.shrhash_size); } if (!nocrit) REL_LOCK_CRIT(pctl, was_crit); else if (MLK_CTL_BLKHASH_EXT == pctl.ctl->blkhash) SHMDT(pctl.shrhash); shr_sub_len = 0; MLK_PVTCTL_SET_CTL(pctl2, ctl); if (MLK_CTL_BLKHASH_EXT != pctl.ctl->blkhash) pctl2.shrhash = (mlk_shrhash_ptr_t)R2A(pctl2.ctl->blkhash); locks = (ctl->blkroot == 0) ? FALSE : lke_showtree(NULL, &pctl2, all, wait, pid, one_lock, memory, &shr_sub_len); /* lock space usage consists of: control_block + nodes(locks) + processes + substrings */ /* any of those subspaces can be bottleneck. * Therefore we will report the subspace which is running out. */ ls_free = MIN(((float)ctl->blkcnt) / ctl->max_blkcnt, ((float)ctl->prccnt) / ctl->max_prccnt); ls_free = MIN(1-(((float)shr_sub_len) / (ctl->subtop - ctl->subbase)), ls_free); ls_free *= 100; /* Scale to [0-100] range. (couldn't do this inside util_out_print) */ if (ls_free < 1) /* No memory? Notify user. */ gtm_putmsg_csa(NULL, VARLSTCNT(4) ERR_LOCKSPACEFULL, 2, DB_LEN_STR(reg)); gtm_putmsg_csa(NULL, VARLSTCNT(10) ERR_LOCKSPACEINFO, 8, REG_LEN_STR(reg), (ctl->max_prccnt - ctl->prccnt), ctl->max_prccnt, (ctl->max_blkcnt - ctl->blkcnt), ctl->max_blkcnt, shr_sub_len, (ctl->subtop - ctl->subbase)); if (MLK_CTL_BLKHASH_EXT == pctl.ctl->blkhash) free(pctl2.shrhash); free(ctl); } else { gtm_putmsg_csa(NULL, VARLSTCNT(2) ERR_BADREGION, 0); locks = TRUE; } if (!locks) gtm_putmsg_csa(NULL, VARLSTCNT(4) ERR_NOLOCKMATCH, 2, REG_LEN_STR(reg)); assert((ls_free <= 100) && (ls_free >= 0)); gtm_putmsg_csa(NULL, VARLSTCNT(4) ERR_LOCKSPACEUSE, 2, ((int)ls_free), csa->hdr->lock_space_size/OS_PAGELET_SIZE); if (nocrit) gtm_putmsg_csa(NULL, VARLSTCNT(3) ERR_LOCKCRITOWNER, 1, LOCK_CRIT_OWNER(csa)); } } if (!match && (0 != regname.len)) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_NOREGION, 2, regname.len, regname.addr); } fis-gtm-V7.0-005/sr_port/lke_showlock.c0000755000032200000250000001546414342376331016710 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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. * * * ****************************************************************/ /* * ---------------------------------------------------------------- * lke_showlock : displays the lock data for a given lock tree node * used in : lke_showtree.c * ---------------------------------------------------------------- */ #include "mdef.h" #include "gtm_string.h" #include "gtm_stdio.h" /* for SNPRINTF */ #ifdef VMS #include #include #include #include #endif #include "mlkdef.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "cmidef.h" #include "hashtab_mname.h" /* needed for cmmdef.h */ #include "cmmdef.h" #include "util.h" #include "lke.h" #include "is_proc_alive.h" #include "real_len.h" /* for real_len() prototype */ #include "zshow.h" #define LNAM 24 #define NDIM 32 /* max node name size */ #define CLNTNODE_LIT " : CLNTNODE = " #define CLNTPID_LIT " : CLNTPID = " #define PID_FMT_STR "!UL" #define PIDPRINT_LIT "%d" #define GNAM_FMT_STR "!AD " GBLREF int4 process_id; #define NODE_SIZE 64 + 1 void lke_formatlocknode(mlk_shrblk_ptr_t node, mstr* name); void lke_formatlocknodes(mlk_shrblk_ptr_t node, mstr* name); static char gnam[] = GNAM_FMT_STR, gnaml[] = "!AD!/!24* ", ownedby[] = "Owned by PID= " PID_FMT_STR " which is !AD!AD", request[] = "Request PID= " PID_FMT_STR " which is !AD!AD", nonexpr[] = "a nonexistent process", existpr[] = "an existing process", nonexam[] = "an inexaminable process", nopriv[] = "no privilege"; void lke_formatlocknode(mlk_shrblk_ptr_t node, mstr* name) { mlk_shrsub_ptr_t value; char save_ch; short len1; int len2; static mval subsc = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0); value = (mlk_shrsub_ptr_t) R2A(node->value); if (0 == name->len) { /* unsubscripted lock name can never have control characters, so no ZWR translation needed */ memcpy(name->addr, value->data, value->length); name->len = value->length; name->addr[name->len++] = '('; } else { /* perform ZWR translation on the subscript */ len1 = name->len - 1; if (')' == name->addr[len1]) name->addr[len1] = ','; subsc.str.len = value->length; subsc.str.addr = (char*) value->data; len2 = MAX_ZWR_KEY_SZ + 1; if (val_iscan(&subsc)) { /* avoid printing enclosed quotes for canonical numbers */ save_ch = name->addr[len1]; format2zwr((sm_uc_ptr_t) value->data, value->length, (unsigned char*) &name->addr[len1], &len2); assert(name->addr[len1 + len2 - 1] == '"'); name->addr[len1] = save_ch; len2 -= 2; /* exclude the enclosing quotes */ } else format2zwr((sm_uc_ptr_t) value->data, value->length, (unsigned char*) &name->addr[name->len], &len2); name->len += len2; name->addr[name->len++] = ')'; } } void lke_formatlocknodes(mlk_shrblk_ptr_t node, mstr* name) { if (node->parent) lke_formatlocknodes((mlk_shrblk_ptr_t)R2A(node->parent), name); lke_formatlocknode(node, name); } void lke_formatlockname(mlk_shrblk_ptr_t node, mstr* name) { lke_formatlocknodes(node, name); if ('(' == name->addr[name->len - 1]) name->len--; } bool lke_showlock( struct CLB *lnk, mlk_shrblk_ptr_t node, mstr *name, bool all, bool wait, bool interactive, int4 pid, mstr one_lock, boolean_t exact) { boolean_t lock = FALSE, owned, unsub; char format[NODE_SIZE], gtcmbuf[NODE_SIZE]; /* gtcmbuf[] holds ": CLNTNODE = %s : CLNTPID = %d" */ char *msg, save_ch; int len2; int4 gtcmbufidx, item, ret; mlk_prcblk pblk; mlk_prcblk_ptr_t r; short len1; uint4 status; UINTPTR_T f[7]; unsub = (0 == name->len); lke_formatlocknode(node, name); /* Subtract one for the lparen in unsubscripted name */ f[0] = name->len + (unsub ? -1 : 0); f[1] = (UINTPTR_T)name->addr; if (node->owner || (node->pending && wait)) { pblk.process_id = node->owner; pblk.next = (wait && node->pending) ? (ptroff_t)((uchar_ptr_t)&node->pending - (uchar_ptr_t)&pblk.next + node->pending) : 0; owned = (all || !wait) && node->owner; r = owned ? &pblk : ((0 == node->pending) ? NULL : (mlk_prcblk_ptr_t)R2A(node->pending)); while (NULL != r) { if ((0 == pid) || (pid == r->process_id)) { f[2] = r->process_id; if (is_proc_alive((int4)r->process_id, 0)) { f[3] = STR_LIT_LEN(existpr); f[4] = (INTPTR_T)existpr; } else { f[3] = STR_LIT_LEN(nonexpr); f[4] = (UINTPTR_T)nonexpr; } if (node->auxowner) { gtcmbufidx = 0; memcpy(>cmbuf[gtcmbufidx], CLNTNODE_LIT, STR_LIT_LEN(CLNTNODE_LIT)); gtcmbufidx += STR_LIT_LEN(CLNTNODE_LIT); memcpy(>cmbuf[gtcmbufidx], node->auxnode, SIZEOF(node->auxnode)); gtcmbufidx += real_len(SIZEOF(node->auxnode), (uchar_ptr_t)node->auxnode); memcpy(>cmbuf[gtcmbufidx], CLNTPID_LIT, STR_LIT_LEN(CLNTPID_LIT)); gtcmbufidx += STR_LIT_LEN(CLNTPID_LIT); SNPRINTF(>cmbuf[gtcmbufidx], NODE_SIZE - gtcmbufidx, PIDPRINT_LIT, node->auxpid); f[5] = strlen(gtcmbuf); f[6] = (UINTPTR_T)>cmbuf[0]; assert(f[5] > gtcmbufidx); assert(gtcmbufidx < SIZEOF(gtcmbuf)); } else f[5] = f[6] = 0; if (interactive) { if (LNAM >= f[0]) { msg = gnam; len1 = STR_LIT_LEN(gnam); } else { msg = gnaml; len1 = STR_LIT_LEN(gnaml); } memcpy(format, msg, len1); if (owned && !lock) { msg = ownedby; len2 = STR_LIT_LEN(ownedby); } else { msg = request; len2 = STR_LIT_LEN(request); } memcpy(format + len1, msg, len2); format[len1 + len2] = '\0'; assert((len1 + len2) < SIZEOF(format)); if (NULL == lnk) { if ((NULL == one_lock.addr) || (!memcmp(name->addr, one_lock.addr, one_lock.len) && (!exact || (one_lock.len == f[0])))) util_out_print_args(format, 7, FLUSH, f[0], f[1], f[2], f[3], f[4], f[5], f[6]); } else util_cm_print(lnk, CMMS_V_LKESHOW, format, 7, FLUSH, f[0], f[1], f[2], f[3], f[4], f[5], f[6]); } if ((NULL != one_lock.addr) && (memcmp(name->addr, one_lock.addr, one_lock.len) || (exact && (one_lock.len != f[0])))) { lock = FALSE; return lock; } lock = TRUE; } f[0] = 0; r = (0 == r->next) ? (mlk_prcblk_ptr_t)NULL : (mlk_prcblk_ptr_t)R2A(r->next); } } return lock; } fis-gtm-V7.0-005/sr_port/lke_showtree.c0000755000032200000250000001313514342376331016710 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2019 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. * * * ****************************************************************/ /* * ----------------------------------- * lke_showtree : displays a lock tree * used in : lke_show.c * ----------------------------------- */ #include "mdef.h" #include #include "gtm_signal.h" #include "gtm_string.h" #include "gtm_stdio.h" #include "mlkdef.h" #include "cmidef.h" #include "gtmio.h" #include "lke.h" #include "mlk_shrblk_delete_if_empty.h" #define KDIM 64 /* max number of subscripts */ GBLREF VSIG_ATOMIC_T util_interrupt; error_def(ERR_CTRLC); mlk_shrblk_ptr_t mlk_shrblk_sort(mlk_shrblk_ptr_t head); void lke_show_memory(mlk_pvtctl_ptr_t pctl, mlk_shrblk_ptr_t bhead, char *prefix) { mlk_shrblk_ptr_t b, bnext, parent, children; mlk_shrsub_ptr_t dsub; mlk_prcblk_ptr_t pending; char temp[MAX_ZWR_KEY_SZ + 1]; char new_prefix[KDIM + 2]; mlk_subhash_state_t hs; uint4 total_len; mlk_subhash_res_t hashres; mlk_subhash_val_t hash; SNPRINTF(new_prefix, KDIM + 2, " %s", prefix); for (b = bhead, bnext = 0; bnext != bhead; b = bnext) { dsub = (mlk_shrsub_ptr_t)R2A(b->value); memcpy(temp, dsub->data, dsub->length); temp[dsub->length] = '\0'; parent = b->parent ? (mlk_shrblk_ptr_t)R2A(b->parent) : NULL; children = b->children ? (mlk_shrblk_ptr_t)R2A(b->children) : NULL; pending = b->pending ? (mlk_prcblk_ptr_t)R2A(b->pending) : NULL; PRINTF("%s%s : [shrblk] %p : [shrsub] %p (len=%d) : [shrhash] %x : [parent] %p : [children] %p : [pending] %p : " "[owner] %u : [auxowner] %" PRIuPTR "\n", prefix, temp, b, dsub, dsub->length, b->hash, parent, children, pending, b->owner, b->auxowner); MLK_SUBHASH_INIT_PVTCTL(pctl, hs); total_len = 0; mlk_shrhash_val_build(b, &total_len, &hs); MLK_SUBHASH_FINALIZE(hs, total_len, hashres); hash = MLK_SUBHASH_RES_VAL(hashres); if (hash != b->hash) /* Should never happen; only here in case things get mangled. */ PRINTF("\t\t: [computed shrhash] %x\n", hash); FFLUSH(stdout); if (b->children) lke_show_memory(pctl, (mlk_shrblk_ptr_t)R2A(b->children), new_prefix); bnext = (mlk_shrblk_ptr_t)R2A(b->rsib); } } void lke_show_hashtable(mlk_pvtctl_ptr_t pctl) { uint4 hash, si, num_buckets; mlk_shrhash_map_t usedmap; mlk_shrhash_ptr_t shrhash, current_bucket; mlk_shrblk_ptr_t current_shrblk; shrhash = pctl->shrhash; num_buckets = pctl->shrhash_size; for (si = 0 ; si < num_buckets; si++) { current_bucket = &shrhash[si]; usedmap = current_bucket->usedmap; hash = current_bucket->hash; if ((0 == current_bucket->shrblk_idx) && (0 == usedmap)) continue; current_shrblk = MLK_SHRHASH_SHRBLK_CHECK(*pctl, current_bucket); PRINTF("%d\t: [shrblk] %p : [hash] %x : [usedmap] %" PRIUSEDMAP "\n", si, current_shrblk, hash, usedmap); FFLUSH(stdout); } PRINTF("\t: [num_buckets] %d\n", num_buckets); if (0 != pctl->ctl->hash_seed) PRINTF("\t: [seed] %" PRIu64 "\n", pctl->ctl->hash_seed); FFLUSH(stdout); } /* Note: *shr_sub_size keeps track of total subscript area in lock space. Initialize *shr_sub_size to 0 before calling this. * lke_showtree() will keep adding on previous value of shr_sub_size. If such info is not needed simply pass NULL to shr_sub_size. * Also, caller must check if the hash table is external. If so, make a copy and point ctl->blkhash at it. */ bool lke_showtree(struct CLB *lnk, mlk_pvtctl_ptr_t pctl, bool all, bool wait, pid_t pid, mstr one_lock, bool memory, int *shr_sub_size) { mlk_shrblk_ptr_t node, start[KDIM]; mlk_shrblk_ptr_t tree; mlk_ctldata_ptr_t ctl; unsigned char subscript_offset[KDIM]; static char name_buffer[MAX_ZWR_KEY_SZ + 1]; static MSTR_DEF(name, 0, name_buffer); int depth = 0; bool locks = FALSE; int string_size = 0; ctl = pctl->ctl; assert(ctl && ctl->blkroot); tree = (mlk_shrblk_ptr_t)R2A(ctl->blkroot); if (memory) { lke_show_memory(pctl, tree, " "); if (shr_sub_size) (*shr_sub_size) = ctl->subfree - ctl->subbase; lke_show_hashtable(pctl); return TRUE; } node = start[0] = mlk_shrblk_sort(tree); subscript_offset[0] = 0; for (;;) { name.len = subscript_offset[depth]; assertpro(node->value); string_size += MLK_SHRSUB_SIZE((mlk_shrsub_ptr_t)R2A(node->value)); /* Display the lock node */ locks = lke_showlock(lnk, node, &name, all, wait, TRUE, pid, one_lock, FALSE) || locks; /* Move to the next node */ if (node->children == 0) { /* This node has no children, so move to the right */ node = (mlk_shrblk_ptr_t)R2A(node->rsib); while (node == start[depth]) { /* There are no more siblings to the right at this depth, so move up and then right */ if (node->parent == 0) { /* We're already at the top, so we're done */ assert(depth == 0); if (shr_sub_size) (*shr_sub_size) = string_size; return locks; } --depth; node = (mlk_shrblk_ptr_t)R2A(((mlk_shrblk_ptr_t)R2A(node->parent))->rsib); } } else { /* This node has children, so move down */ ++depth; node = start[depth] = mlk_shrblk_sort((mlk_shrblk_ptr_t)R2A(node->children)); subscript_offset[depth] = name.len; } if (util_interrupt) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_CTRLC); } } fis-gtm-V7.0-005/sr_port/lkglvn.c0000755000032200000250000001016214342376331015507 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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_string.h" #include "compiler.h" #include "opcode.h" #include "toktyp.h" #include "mdq.h" #include "subscript.h" #include "advancewindow.h" #include "fullbool.h" #include "show_source_line.h" GBLREF boolean_t run_time; error_def(ERR_EXPR); error_def(ERR_EXTGBLDEL); error_def(ERR_LKNAMEXPECTED); error_def(ERR_MAXNRSUBSCRIPTS); error_def(ERR_RPARENMISSING); error_def(ERR_SIDEEFFECTEVAL); int lkglvn(boolean_t gblvn) { boolean_t shifting, vbar; char *lknam, lkname_buf[MAX_MIDENT_LEN + 1], x; opctype ox; oprtype *sb1, *sb2, subscripts[MAX_GVSUBSCRIPTS + 1]; triple *oldchain, *ref, tmpchain, *triptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ox = OC_LKNAME; sb1 = sb2 = subscripts; lknam = lkname_buf; if (gblvn) *lknam++ = '^'; if (shifting = (TREF(shift_side_effects) && (!TREF(saw_side_effect) || (GTM_BOOL == TREF(gtm_fullbool) && (OLD_SE == TREF(side_effect_handling)))))) { /* NOTE assignment above */ dqinit(&tmpchain, exorder); oldchain = setcurtchain(&tmpchain); } if ((TK_LBRACKET == TREF(window_token)) || (TK_VBAR == TREF(window_token))) { vbar = (TK_VBAR == TREF(window_token)); advancewindow(); if (EXPR_FAIL == (vbar ? expr(sb1++, MUMPS_EXPR) : expratom(sb1))) { stx_error(ERR_EXPR); if (shifting) setcurtchain(oldchain); return FALSE; } if (!vbar) { coerce(sb1, OCT_MVAL); ex_tail(sb1++); } if (TK_COMMA == TREF(window_token)) { advancewindow(); if (EXPR_FAIL == (vbar ? expr(sb1++, MUMPS_EXPR) : expratom(sb1))) { stx_error(ERR_EXPR); if (shifting) setcurtchain(oldchain); return FALSE; } if (!vbar) { coerce(sb1, OCT_MVAL); ex_tail(sb1++); } } else *sb1++ = put_str(0, 0); if ((!vbar && (TK_RBRACKET != TREF(window_token))) || (vbar && (TK_VBAR != TREF(window_token)))) { stx_error(ERR_EXTGBLDEL); if (shifting) setcurtchain(oldchain); return FALSE; } advancewindow(); ox = OC_LKEXTNAME; } else *sb1++ = put_ilit(0); if (TK_IDENT != TREF(window_token)) { stx_error(ERR_LKNAMEXPECTED); return FALSE; } assert(MAX_MIDENT_LEN >= (TREF(window_ident)).len); memcpy(lknam, (TREF(window_ident)).addr, (TREF(window_ident)).len); lknam += (TREF(window_ident)).len; *sb1++ = put_str(lkname_buf,(mstr_len_t)(lknam - lkname_buf)); advancewindow(); if (TK_LPAREN == TREF(window_token)) { for (;;) { if (ARRAYTOP(subscripts) <= sb1) { stx_error(ERR_MAXNRSUBSCRIPTS); if (shifting) setcurtchain(oldchain); return FALSE; } advancewindow(); if (EXPR_FAIL == expr(sb1, MUMPS_EXPR)) { if (shifting) setcurtchain(oldchain); return FALSE; } sb1++; if (TK_RPAREN == (x = TREF(window_token))) /* NOTE assignment */ { advancewindow(); break; } if (TK_COMMA != x) { stx_error(ERR_RPARENMISSING); if (shifting) setcurtchain(oldchain); return FALSE; } } } ref = newtriple(ox); ref->operand[0] = put_ilit((mint)(sb1 - sb2)); SUBS_ARRAY_2_TRIPLES(ref, sb1, sb2, subscripts, 0); if (shifting) { if (TREF(saw_side_effect) && ((GTM_BOOL != TREF(gtm_fullbool)) || (OLD_SE != TREF(side_effect_handling)))) { /* saw a side effect in a subscript - time to stop shifting */ setcurtchain(oldchain); triptr = (TREF(curtchain))->exorder.bl; dqadd(triptr, &tmpchain, exorder); } else { newtriple(OC_GVSAVTARG); setcurtchain(oldchain); assert(&tmpchain != tmpchain.exorder.bl); dqadd(TREF(expr_start), &tmpchain, exorder); TREF(expr_start) = tmpchain.exorder.bl; triptr = newtriple(OC_GVRECTARG); triptr->operand[0] = put_tref(TREF(expr_start)); } } return TRUE; } fis-gtm-V7.0-005/sr_port/loadop.m0000755000032200000250000000146714342376331015512 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Copyright 2001, 2008 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; loadop ;load op codes n (opc,opx,opcdcnt,loadh) k opc,opx,opcdcnt s opcdcnt=0 s file=loadh("opcode_def.h") o file:read u file loop r x i $zeof g fini i x?1"OPCODE_DEF"1.E s rec=x,opcdcnt=opcdcnt+1 i d proc g loop fini c file q proc s val=opcdcnt-1,cd=$p($p(rec,"(",2),",",1) s opc(cd)=val,opx(val)=cd q err u "" w rec,!,"error code=",ec," line=",opcdcnt,! u file q fis-gtm-V7.0-005/sr_port/loadvx.m0000755000032200000250000000233614342376331015525 0ustar librarygtc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; 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. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; loadvx ;load op codes n (vxi,vxx,loadh) k vxi,vxx s lnr=0 s file=loadh("vxi.h") o file:read u file loop r x i $zeof g fini s rec=x,lnr=lnr+1 d proc g loop fini c file q proc i $e(x,"1")'="#" q pr0 s x=$tr(x,$c(9)," ") i x'?1"#define"1." "1"VXI_"1.AN1." "1"(0x"1.3AN1")".E s ec=1 g err s y=$f(x,"VXI_"),x=$e(x,y,999) s cd=$p(x," ",1),cdx="VXI_"_cd f i=1:1:$l(cd) i $e(cd,i)?1U s cd=$e(cd,1,i-1)_$c($a(cd,i)+32)_$e(cd,i+1,999) s val=$p($p($p(x,"(",2),")",1),"0x",2) d hex2dec i $d(vxx(val)) s ec=2 g err i $d(vxi(cd)) s ec=3 g err s vxi(cd)=val,vxi(cd,1)=cdx,vxx(val)=cd q err u "" w rec,!,"error code=",ec," line=",lnr,! u file q hex2dec n n,x,i s n=0,i=1 h2d1 s x=$e(val,i) i x="" s val=n q i x?1U s x=$a(x)-55 e i x?1L s x=$a(x)-87 e i x'?1N b s n=n*16+x,i=i+1 g h2d1 fis-gtm-V7.0-005/sr_port/localvarmonitor.h0000644000032200000250000000542614342376331017436 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2017-2018 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. * * * ****************************************************************/ #ifndef LOCALVARMONITOR_H_DEFINED #define LOCALVARMONITOR_H_DEFINED /* Note there are two forms of local variable monitoring in GTM - both initiated by VIEW commands. The first is the lv monitoring * potentially done by aliases when #ifdef(DEBUG_ALIAS) is set during a build followed by the appropriate * VIEW LVMONSTART/LVMONSTOP/LVMONOUT * command. * * The second method is this one. There is one lvmon_var struct (in an array anchored by TREF(lvmon_vars_anchor)) for each * base variable named as a variable in. Monitoring is started with the * VIEW "LVMON":>> * command. A lack of arguments turns off monitoring and releases the trace data structures. * * There are two routines of interest: * 1. lvmon_pull_values(idx) * 2. lvmon_compare_value_slots(idx1, idx2) * * lvmon_pull_values(idx) * - Pulls the current value of all variables being monitored storing it in the index given. * * lvmon_compare_value_slots(idx1, idx2) * - Compares the values in the two given slots giving an error message and generating a core if they differ but otherwise * allowing the process to continue. */ /* Uncomment following statement to debug LVMONITOR */ /* #define DEBUG_LVMONITOR */ #ifdef DEBUG_LVMONITOR # define DBGLVMON(X) DBGFPF(X) #else # define DBGLVMON(X) #endif #define MAX_LVMON_VALUES 2 /* Enough for 1 comparison set for now - should be an even number */ #define IS_LVMON_ACTIVE (TREF(lvmon_active)) /* Structure to hold a single saved value */ typedef struct { lv_val varlvval; /* Copy of the lv_val holding value (type and numeric value ) */ mstr varvalue; /* If varlvval is string, points to malloc'd space holding value */ uint4 alloclen; /* Allocation length of var_value */ GTM64_ONLY(int filler;) } lvmon_value_ent; /* Structure used to monitor variables defined in a table made up of lvmon_var structures */ typedef struct { mname_entry lvmv; lv_val *varlvadr; /* Only need to fetch it once unless curr_symtab_cycle has changed */ gtm_int8 curr_symval_cycle; /* Copy of curr_symval_cycle when copied lv_val addr */ lvmon_value_ent values[MAX_LVMON_VALUES]; } lvmon_var; void lvmon_pull_values(int lvmon_ary_idx); void lvmon_compare_value_slots(int lvmon_idx1, int lvmon_idx2); #endif /* ifndef _LOCALVARMONITOR_H_DEFINED_ */ fis-gtm-V7.0-005/sr_port/lock_str_to_buff.c0000644000032200000250000000227014342376331017534 0ustar librarygtc/**************************************************************** * * * Copyright 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "error.h" #include "mlkdef.h" #include "lock_str_to_buff.h" #include "gtm_string.h" #include "zshow.h" /* Takes a lock as input and outputs lock name to string to buff (terminated with NULL). This is called from op_lock2. * This function consolidates "output" variable initialization and name string formatting into one function. */ void lock_str_to_buff(mlk_pvtblk *pvt_ptr, char *outbuff, int outbuff_len) { mval v; zshow_out output; memset(&output, 0, SIZEOF(output)); output.type = ZSHOW_BUFF_ONLY; /* This setting only changes out->ptr the other fileds are ignored */ output.buff = &outbuff[0]; output.size = outbuff_len; output.ptr = output.buff; zshow_format_lock(&output,pvt_ptr); *output.ptr = '\0'; return; } fis-gtm-V7.0-005/sr_port/lock_str_to_buff.h0000644000032200000250000000111414342376331017535 0ustar librarygtc/**************************************************************** * * * Copyright 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #ifndef LOCK_STR_TO_BUFF_H #define LOCK_STR_TO_BUFF_H void lock_str_to_buff(mlk_pvtblk *pvt_ptr1, char *outbuff, int outbuff_len); #endif fis-gtm-V7.0-005/sr_port/lockconst.h0000755000032200000250000000760514342376331016226 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2007 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. * * * ****************************************************************/ /* lockconst.h - define values used for interlocks */ #define LOCK_AVAILABLE 0 #define LOCK_IN_USE 1 #define REC_UNDER_PROG 2 /* CAUTION: the following two values are not currently optional on HPUX, but rather chosen for HP-PA arthitectural reasons */ #define GLOBAL_LOCK_AVAILABLE -1 #define GLOBAL_LOCK_IN_USE 0 /* Global latch note for platforms with a micro latch (currently Solaris and HPUX). The SET_LATCH_GLOBAL macro initializes both the compswap lock (major latch) and the micro latch used by compswap. The order this is done in is first the micro latch, then the major latch. The order is important so that secshr_db_clnup can (re)initialize the entire latch and not run into problems with concurrent users attempting to get the lock. The compswap routines in these modules will check the value of the major latch first before even attempting to obtain the micro latch. */ #ifdef __hppa # define alignedaddr(x) (volatile int *)((UINTPTR_T)(x) + 15 & ~0xf) /* 32-bit */ /* Given a pointer into memory, round up to the nearest 16-byte boundary by adding 15, then masking off the last four bits. Assumption: the input address is already int (4-byte) aligned. The VOLATILE keyword is essential in this macro: it ensures that the compiler does not perform certain optimizations which would compromise the integrity the spinlock logic. */ # define release_spinlock(lockarea) {if (1) { \ _flush_globals(); \ (*alignedaddr(&(lockarea)->hp_latch_space) = GLOBAL_LOCK_AVAILABLE); } else ;} /* HP white paper sets latch to 1 for available while we set it to -1 */ /* For performance, release_spinlock is a macro, rather than a function. To release or initialize a spinlock, we simply set its value to one. We must call the psuedo function "_flush_globals()" to ensure that the compiler doesn't hold any externally-visible values in registers across the lock release */ int4 load_and_clear(sm_int_ptr_t); # define GET_LATCH_GLOBAL(a) (GLOBAL_LOCK_AVAILABLE == *alignedaddr(&(a)->hp_latch_space) ? \ load_and_clear((sm_int_ptr_t)&(a)->hp_latch_space) : GLOBAL_LOCK_IN_USE) /* above tries a fast pretest before calling load_and_clear to actually get the latch */ # define RELEASE_LATCH_GLOBAL(a) release_spinlock(a) # define SET_LATCH_GLOBAL(a, b) {RELEASE_LATCH_GLOBAL(a); assert(LOCK_AVAILABLE == b); SET_LATCH(a, b);} #elif defined(__sparc) && defined(SPARCV8_NO_CAS) /* For Sun sparc, we use the extra word of the latch for a micro lock for compswap. Future iterations of this should make use of the CAS (compare and swap) instruction newly available in the Sparc Version 9 instruction set. These *_GLOBAL macros are used only from compswap.c (currently) */ # define GET_LATCH_GLOBAL(a) aswp(&(a)->u.parts.latch_word, GLOBAL_LOCK_IN_USE) # define RELEASE_LATCH_GLOBAL(a) aswp(&(a)->u.parts.latch_word, GLOBAL_LOCK_AVAILABLE) # define SET_LATCH_GLOBAL(a, b) {SET_LATCH(&(a)->u.parts.latch_word, GLOBAL_LOCK_AVAILABLE); SET_LATCH(a, b);} #elif defined(VMS) # define SET_LATCH_GLOBAL(a, b) {(a)->u.parts.latch_image_count = 0; SET_LATCH(a, b);} #else # define SET_LATCH_GLOBAL(a, b) SET_LATCH(a, b) #endif /* perhaps this should include flush so other CPUs see the change now */ #define SET_LATCH(a,b) (*((sm_int_ptr_t)a) = b) fis-gtm-V7.0-005/sr_port/locklits.h0000755000032200000250000000156114342376331016046 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001, 2015 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. * * * ****************************************************************/ #define MAX_LCKARG 253 #define LOCKED 1 #define ZALLOCATED 2 #define LCK_REQUEST 4 #define ZAL_REQUEST 8 #define PENDING 16 #define COMPLETE 32 #define INCREMENTAL 0x40 #define NEW 0x80 #define DEAD 2 #define PART_DEAD 1 #define NOT_DEAD 0 #define NOT_THERE -1 #define LOCK_SELF_WAKE 100 /* sleep 100 msec before checking if wakeup was sent by lock holder */ fis-gtm-V7.0-005/sr_port/logical_truth_value.c0000755000032200000250000000515214342376331020251 0ustar librarygtc/**************************************************************** * * * 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 "gtm_ctype.h" #include "min_max.h" #include "gtm_string.h" #include "gtm_strings.h" /* for STRNCASECMP */ #include "trans_log_name.h" #include "iosp.h" #include "logical_truth_value.h" /* returns the truth value based on the sense indicated by 'negate'. * If negate is FALSE (i.e. in regular mode), * returns TRUE if the env variable/logical log is defined and evaluates to "TRUE" (or part thereof), * or "YES" (or part thereof), or a non zero integer * returns FALSE otherwise * If negate is TRUE(i.e. in negative mode), * returns TRUE if the env variable/logical log is defined and evaluates to "FALSE" (or part thereof), * or "NO" (or part thereof), or a zero integer * returns FALSE otherwise */ boolean_t logical_truth_value(mstr *log, boolean_t negate, boolean_t *is_defined) { int4 status; mstr tn; char buf[1024]; boolean_t zero, is_num; int index; error_def(ERR_LOGTOOLONG); error_def(ERR_TRNLOGFAIL); tn.addr = buf; if (NULL != is_defined) *is_defined = FALSE; if (SS_NORMAL == (status = TRANS_LOG_NAME(log, &tn, buf, SIZEOF(buf), dont_sendmsg_on_log2long))) { if (NULL != is_defined) *is_defined = TRUE; if (tn.len <= 0) return FALSE; for (is_num = TRUE, zero = TRUE, index = 0; index < tn.len; index++) { if (!ISDIGIT_ASCII(buf[index])) { is_num = FALSE; break; } zero = (zero && ('0' == buf[index])); } if (!negate) { /* regular mode */ return (!is_num ? (0 == STRNCASECMP(buf, LOGICAL_TRUE, MIN(STR_LIT_LEN(LOGICAL_TRUE), tn.len)) || 0 == STRNCASECMP(buf, LOGICAL_YES, MIN(STR_LIT_LEN(LOGICAL_YES), tn.len))) : !zero); } else { /* negative mode */ return (!is_num ? (0 == STRNCASECMP(buf, LOGICAL_FALSE, MIN(STR_LIT_LEN(LOGICAL_FALSE), tn.len)) || 0 == STRNCASECMP(buf, LOGICAL_NO, MIN(STR_LIT_LEN(LOGICAL_NO), tn.len))) : zero); } } else if (SS_NOLOGNAM == status) return (FALSE); # ifdef UNIX else if (SS_LOG2LONG == status) { rts_error(VARLSTCNT(5) ERR_LOGTOOLONG, 3, log->len, log->addr, SIZEOF(buf) - 1); return (FALSE); } # endif else { rts_error(VARLSTCNT(5) ERR_TRNLOGFAIL, 2, log->len, log->addr, status); return (FALSE); } } fis-gtm-V7.0-005/sr_port/logical_truth_value.h0000755000032200000250000000140614342376331020254 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2006 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. * * * ****************************************************************/ #ifndef LOGICAL_TRUTH_VALUE_H_INCLUDED #define LOGICAL_TRUTH_VALUE_H_INCLUDED #define LOGICAL_TRUE "TRUE" #define LOGICAL_YES "YES" #define LOGICAL_FALSE "FALSE" #define LOGICAL_NO "NO" boolean_t logical_truth_value(mstr *logical, boolean_t negate, boolean_t *is_defined); #endif /* LOGICAL_TRUTH_VALUE_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/longset.h0000755000032200000250000000157214342376331015677 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2003 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef LONGSET_INCLUDED #define LONGSET_INCLUDED /* To be one day eliminated when usage is totally replaced by the memset calls it should be calling now instead. Since this is a stop-gap measure, it is ok that this include pulls in gtm_string.h if necessary. void longset(uchar_ptr_t ptr, int len, unsigned char fill); */ #include "gtm_string.h" #define longset(dst, len, fill) memset(dst, fill, len) #endif /* LONGSET_INCLUDED */ fis-gtm-V7.0-005/sr_port/lookup_variable_htent.h0000644000032200000250000000122514342376331020574 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2009-2018 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. * * * ****************************************************************/ #ifndef LOOKUP_VARIABLE_HTENT_H_INCLUDED #define LOOKUP_VARIABLE_HTENT_H_INCLUDED ht_ent_mname *lookup_variable_htent(unsigned int x); #endif fis-gtm-V7.0-005/sr_port/lower_to_upper.c0000755000032200000250000000127714342376331017266 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "gtm_caseconv.h" LITREF unsigned char lower_to_upper_table[]; void lower_to_upper(uchar_ptr_t d, uchar_ptr_t s, int4 len) { uchar_ptr_t d_top; d_top = d + len; for ( ; d < d_top; ) { *d++ = lower_to_upper_table[*s++]; } } fis-gtm-V7.0-005/sr_port/lref.c0000755000032200000250000000376314342376331015153 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "cmd_qlf.h" #include "advancewindow.h" #include "gtm_caseconv.h" GBLREF command_qualifier cmd_qlf; LITREF mident zero_ident; error_def(ERR_LABELEXPECTED); int lref(oprtype *label, oprtype *offset, boolean_t no_lab_ok, mint commarg_code, boolean_t commarg_ok, boolean_t *got_some) { char c; triple *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; switch (TREF(window_token)) { case TK_INTLIT: int_label(); /* caution: fall through */ case TK_IDENT: *got_some = TRUE; if (!(cmd_qlf.qlf & CQ_LOWER_LABELS)) lower_to_upper((uchar_ptr_t)(TREF(window_ident)).addr, (uchar_ptr_t)(TREF(window_ident)).addr, (TREF(window_ident)).len); *label = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); advancewindow(); break; case TK_ATSIGN: *got_some = TRUE; if (!indirection(label)) return FALSE; if (commarg_ok && (TK_COLON != (c = TREF(window_token))) && (TK_CIRCUMFLEX != c) && (TK_PLUS != c)) /* NOTE assignment */ { ref = newtriple(OC_COMMARG); ref->operand[0] = *label; ref->operand[1] = put_ilit(commarg_code); *label = put_tref(ref); return TRUE; } break; case TK_COLON: case TK_CIRCUMFLEX: return TRUE; case TK_PLUS: *label = put_str(zero_ident.addr, zero_ident.len); if (no_lab_ok) break; /* caution: fall through */ default: stx_error(ERR_LABELEXPECTED); return FALSE; } if (TK_PLUS != TREF(window_token)) return TRUE; *got_some = TRUE; advancewindow(); return expr(offset, MUMPS_INT); } fis-gtm-V7.0-005/sr_port/lv_newblock.c0000755000032200000250000000541414342376331016523 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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 "gtm_string.h" #include "gtm_stdio.h" #include "gtmio.h" #include "gtm_malloc.h" #include "lv_val.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "alias.h" void lv_newblock(symval *sym, int numElems) { register lv_blk *ptr; register int n; lv_val *lv_base; n = numElems * SIZEOF(lv_val) + SIZEOF(lv_blk); n = INTCAST(gtm_bestfitsize(n)); /* Maximize use of storage block we are going to get */ assert(DIVIDE_ROUND_DOWN(n - SIZEOF(lv_blk), SIZEOF(lv_val)) >= numElems); numElems = DIVIDE_ROUND_DOWN(n - SIZEOF(lv_blk), SIZEOF(lv_val)); ptr = (lv_blk *)malloc(n); lv_base = (lv_val *)LV_BLK_GET_BASE(ptr); memset(lv_base, 0, numElems * SIZEOF(lv_val)); ptr->next = sym->lv_first_block; sym->lv_first_block = ptr; ptr->numAlloc = numElems; ptr->numUsed = 0; DBGRFCT((stderr, "lv_newblock: New lv_blk allocated ******************\n")); } void lvtree_newblock(symval *sym, int numElems) { register lv_blk *ptr; register int n; lvTree *lvt_base; n = numElems * SIZEOF(lvTree) + SIZEOF(lv_blk); n = INTCAST(gtm_bestfitsize(n)); /* Maximize use of storage block we are going to get */ assert(DIVIDE_ROUND_DOWN(n - SIZEOF(lv_blk), SIZEOF(lvTree)) >= numElems); numElems = DIVIDE_ROUND_DOWN(n - SIZEOF(lv_blk), SIZEOF(lvTree)); ptr = (lv_blk *)malloc(n); lvt_base = (lvTree *)LV_BLK_GET_BASE(ptr); ptr->next = sym->lvtree_first_block; sym->lvtree_first_block = ptr; ptr->numAlloc = numElems; ptr->numUsed = 0; DBGRFCT((stderr, "lvtree_newblock: New lv_blk allocated ******************\n")); } void lvtreenode_newblock(symval *sym, int numElems) { register lv_blk *ptr; register int n; lvTreeNode *lv_base; n = numElems * SIZEOF(lvTreeNode) + SIZEOF(lv_blk); n = INTCAST(gtm_bestfitsize(n)); /* Maximize use of storage block we are going to get */ assert(DIVIDE_ROUND_DOWN(n - SIZEOF(lv_blk), SIZEOF(lvTreeNode)) >= numElems); numElems = DIVIDE_ROUND_DOWN(n - SIZEOF(lv_blk), SIZEOF(lvTreeNode)); ptr = (lv_blk *)malloc(n); lv_base = (lvTreeNode *)LV_BLK_GET_BASE(ptr); memset(lv_base, 0, numElems * SIZEOF(lvTreeNode)); ptr->next = sym->lvtreenode_first_block; sym->lvtreenode_first_block = ptr; ptr->numAlloc = numElems; ptr->numUsed = 0; DBGRFCT((stderr, "lvtreenode_newblock: New lv_blk allocated ******************\n")); } fis-gtm-V7.0-005/sr_port/lv_tree.h0000644000032200000250000003677114342376331015672 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2020 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. * * * ****************************************************************/ #ifndef TREE_H_INCLUDED #define TREE_H_INCLUDED #include /* The "lvTree" typedef defines an AVL tree. One such structure exists for each local variable subscript level (starting from 0) * The "lvTreeNode" typedef defines the layout of the tree node. One structure exists for each tree node in an AVL tree. * e.g. if "a" is a local variable that has the following nodes defined * a(1)=1 * a(1,"a")=2 * a(1,"b")=3 * a(2,"c")=4 * There is ONE "lv_val" structure corresponding to the base local variable "a". * There are THREE "lvTree" structures (i.e. AVL trees) underneath the base local variable "a". * 1) One for the base variable "a". This contains the keys/subscripts 1 and 2. * 2) One for the subscripted variable "a(1)". This contains the keys/subscripts "a" and "b". * 3) One for the subscripted variable "a(2)". This contains the key/subscript "c". * There are FIVE "lvTreeNode" structures underneath the base local variable "a". * 1) One for subscript 1. * 2) One for subscript 2. * 3) One for subscript "a". * 4) One for subscript "b". * 5) One for subscript "c". */ typedef mval lvTreeNodeVal; typedef mval treeKeySubscr; /* A numeric key value stored in the AVL tree is represented in a lvTreeNodeNum structure. Additionally, a string type key can * also be stored in the same AVL tree. Instead of using an "mstr" type directly, we explicitly define the necessary fields * from the mstr (no char_len field) so we can save 8 bytes in the lvTreeNode struct on 64-bit platforms). This way both the * numeric and string lvTreeNode structures require 12 bytes thus have 8-byte alignment for the lvTreeNode* typedefs (on 64-bit * platforms) without any wasted padding space. This means that any code that interprets the key in a lvTreeNode structure * needs to examine the "key_mvtype" field and accordingly use a lvTreeNodeNum or lvTreeNodeStr structure to get at the key. */ typedef struct lvTreeNodeNumStruct { lvTreeNodeVal v; /* If string, will point to stringpool location */ struct lvTreeStruct *sbs_child; /* pointer to lvTreeStruct storing next level subscripts under this node */ struct lvTreeStruct *tree_parent; /* pointer to lvTreeStruct under whose avl tree this node belongs to */ unsigned short key_mvtype; /* will be (if non-integer) or (if integer) */ signed char balance; /* height(left) - height(right). Can be -1, 0, or 1 */ unsigned char descent_dir; /* direction of descent (LEFT = 0, RIGHT = 1) */ /* the value of the numeric key is stored in the key_* members below */ union { struct { unsigned char key_sgne; /* Contains sgn & e fields - fewer instructions to move this one byte * rather than two bit fields when copying to/from an mval */ } key_bytes; struct { # ifdef BIGENDIAN unsigned int key_sgn : 1; /* same as mval layout */ unsigned int key_e : 7; /* same as mval layout */ # else unsigned int key_e : 7; /* same as mval layout */ unsigned int key_sgn : 1; /* same as mval layout */ # endif unsigned int key_iconv : 1; /* 1 if (key_mvtype & MV_INT is TRUE) && (key_m0 field stores the * mval->m[1] value of the integer before it was converted into the * float representation. */ unsigned int filler :23; } key_bits; } key_flags; int4 key_m0; int4 key_m1; struct treeNodeStruct *avl_left; /* left AVL subtree at this subscript level */ struct treeNodeStruct *avl_right; /* right AVL subtree at this subscript level */ struct treeNodeStruct *avl_parent; /* parent node within the current subscript level AVL tree */ } lvTreeNodeNum; typedef struct treeNodeStruct { lvTreeNodeVal v; /* If string, will point to stringpool location */ struct lvTreeStruct *sbs_child; /* pointer to lvTreeStruct storing next level subscripts under this node; * overloaded as the free list pointer if this "lvTreeNode" is in the free list. */ struct lvTreeStruct *tree_parent; /* pointer to lvTreeStruct under whose avl tree this node belongs */ unsigned short key_mvtype; /* will have MV_STR bit set */ signed char balance; /* height(left) - height(right). Can be -1, 0, or 1 */ unsigned char descent_dir; /* direction of descent (LEFT = 0, RIGHT = 1) */ uint4 key_len; /* byte length of string */ char *key_addr; /* pointer to string */ NON_GTM64_ONLY(uint4 filler_8byte;) /* needed to ensure lvTreeNodeNum & lvTreeNode structures have * same size & layout for 32-bit platforms also */ struct treeNodeStruct *avl_left; /* left AVL subtree at this subscript level */ struct treeNodeStruct *avl_right; /* right AVL subtree at this subscript level */ struct treeNodeStruct *avl_parent; /* parent node within the current subscript level AVL tree */ } lvTreeNode; /* Given a pointer to a "lvTreeNode" structure, the mvtype field will tell us whether it is a lvTreeNodeStr or lvTreeNodeNum type. * key_mvtype will have the MV_STR bit set in case of lvTreeNodeStr and otherwise if lvTreeNodeNum. */ typedef lvTreeNode lvTreeNodeStr; /* last lookup clue structure */ typedef struct { lvTreeNode *lastNodeLookedUp; /* pointer to the node that was last looked up using lvAvlTreeLookup function */ lvTreeNode *lastNodeMin; /* minimum value of key that can be underneath the last looked up node's subtree */ lvTreeNode *lastNodeMax; /* maximum value of key that can be underneath the last looked up node's subtree */ } treeSrchStatus; typedef struct lvTreeStruct { /* Note if first 3 fields are disturbed, make sure to fix LV_CLONE_TREE macro which references them as a group */ unsigned short ident; /* 2-byte field (same size as mvtype) set to the value MV_LV_TREE */ unsigned short sbs_depth; /* == "n" => all nodes in current avl tree represent lvns with "n" subscripts */ uint4 avl_height; /* Height of the AVL tree rooted at "avl_root" */ struct lv_val_struct *base_lv; /* back pointer to base var lv_val (subscript depth 0) */ lvTreeNode *avl_root; /* pointer to the root of the AVL tree corresponding to this subscript level; * overloaded as the free list pointer if this "lvTree" structure is in the free list. */ lvTreeNode *sbs_parent;/* pointer to parent (points to lv_val if sbs_depth==1 and lvTreeNode if sbs_depth>1) */ treeSrchStatus lastLookup; /* clue to last node lookup in the AVL tree rooted at "avl_root" */ } lvTree; /* This section defines macros for the AVL tree implementation. Note that an AVL tree maintains its log(n) height by ensuring * the left and right subtrees at any level never differ in height by more than 1. */ #define MAX_BALANCE 1 /* the maximum difference in height of the left and right subtrees of a tree, * balance > MAX_BALANCE will trigger a re-balance */ #define TREE_DESCEND_LEFT 0 #define TREE_DESCEND_RIGHT 1 /* AVL tree algorithms talk about balance factor as being -1 (left heavy), 1 (right heavy) or 0 (balanced) * but we use 0, 1 and 2 instead for this implementation. */ #define TREE_LEFT_HEAVY 0 /* should be same as TREE_DESCEND_LEFT (an implementation efficiency) */ #define TREE_RIGHT_HEAVY 1 /* should be same as TREE_DESCEND_RIGHT (an implementation efficiency) */ #define TREE_BALANCED 2 #define TREE_IS_LEFT_HEAVY(bal) (bal) == TREE_LEFT_HEAVY #define TREE_IS_RIGHT_HEAVY(bal) (bal) == TREE_RIGHT_HEAVY #define TREE_IS_BALANCED(bal) ((bal) == TREE_BALANCED) #define TREE_IS_NOT_BALANCED(bal) (!(TREE_IS_BALANCED(bal))) #define TREE_INVERT_BALANCE_FACTOR(bal) (tree_balance_invert[bal]) #define TREE_KEY_SUBSCR_IS_CANONICAL(A_MVTYPE) (A_MVTYPE & MV_CANONICAL) #define TREE_KEY_SUBSCR_SET_MV_CANONICAL_BIT(A_MVAL) (A_MVAL)->mvtype |= MV_CANONICAL #define TREE_KEY_SUBSCR_RESET_MV_CANONICAL_BIT(A_MVAL) (A_MVAL)->mvtype &= MV_CANONICAL_OFF /* Macro to get numeric valued subscript in an lv node */ #define LV_NUM_NODE_GET_KEY(NODE, KEY) \ { \ lvTreeNodeNum *lcl_flt_node; \ int lcl_mvtype; \ \ lcl_flt_node = (lvTreeNodeNum *)NODE; \ lcl_mvtype = lcl_flt_node->key_mvtype; \ assert(MVTYPE_IS_NUMERIC(lcl_mvtype)); \ if (MV_INT & lcl_mvtype) \ (KEY)->m[1] = lcl_flt_node->key_m0; \ else \ { \ assert(lcl_flt_node->key_flags.key_bits.key_e || !lcl_flt_node->key_m1); \ ((mval_b *)(KEY))->sgne = lcl_flt_node->key_flags.key_bytes.key_sgne; \ (KEY)->m[0] = lcl_flt_node->key_m0; \ (KEY)->m[1] = lcl_flt_node->key_m1; \ } \ (KEY)->mvtype = lcl_mvtype; \ } /* Macro to get string valued subscript in an lv node */ #define LV_STR_NODE_GET_KEY(NODE, KEY) \ { \ assert(MVTYPE_IS_STRING(NODE->key_mvtype)); \ (KEY)->mvtype = NODE->key_mvtype; \ (KEY)->str.len = NODE->key_len; \ (KEY)->str.addr = NODE->key_addr; \ } /* If NODE contains a numeric key, before returning the key's mval to non-lv code make sure we * reset any bit(s) that are set only in lv code. Non-lv code does not know to handle this bit. * The only one at this point in time is MV_CANONICAL. */ #define LV_NODE_GET_KEY(NODE, KEY_MVAL) \ { \ if (TREE_KEY_SUBSCR_IS_CANONICAL(NODE->key_mvtype)) \ { /* "NODE" is of type "lvTreeNodeNum *" */ \ LV_NUM_NODE_GET_KEY(NODE, KEY_MVAL); \ (KEY_MVAL)->mvtype &= MV_CANONICAL_OFF; \ } else /* "NODE" is of type "lvTreeNode *" */ \ LV_STR_NODE_GET_KEY(NODE, KEY_MVAL); \ } /* Macro to return if a given "lvTreeNode *" pointer is a null subscript string. * Input "node" could actually be any one of "lvTreeNodeNum *" or "lvTreeNode *". * A null subscript string is of actual type "lvTreeNode *". To minimize the checks, we check for the MV_STR bit in the mvtype. * This is asserted below. For "lvTreeNodeNum *", the MV_STR bit is guaranteed not to be set. That leaves us with "lvTreeNode *". */ #define LV_NODE_KEY_IS_STRING(NODE) (DBG_ASSERT(NULL != NODE) \ MVTYPE_IS_STRING(NODE->key_mvtype)) #define LV_NODE_KEY_IS_NULL_SUBS(NODE) (LV_NODE_KEY_IS_STRING(NODE) && (0 == NODE->key_len)) #ifdef DEBUG int lvAvlTreeKeySubscrCmp(treeKeySubscr *aSubscr, lvTreeNode *bNode); int lvAvlTreeNodeSubscrCmp(lvTreeNode *aNode, lvTreeNode *bNode); #endif lvTreeNode *lvAvlTreeFirst(lvTree *lvt); lvTreeNode *lvAvlTreeLast(lvTree *lvt); lvTreeNode *lvAvlTreePrev(lvTreeNode *node); lvTreeNode *lvAvlTreeKeyPrev(lvTree *lvt, treeKeySubscr *key); lvTreeNode *lvAvlTreeNext(lvTreeNode *node); lvTreeNode *lvAvlTreeKeyNext(lvTree *lvt, treeKeySubscr *key); lvTreeNode *lvAvlTreeFirstPostOrder(lvTree *lvt); lvTreeNode *lvAvlTreeNextPostOrder(lvTreeNode *node); lvTreeNode *lvAvlTreeKeyCollatedNext(lvTree *lvt, treeKeySubscr *key); lvTreeNode *lvAvlTreeNodeCollatedNext(lvTreeNode *node); lvTreeNode *lvAvlTreeCloneSubTree(lvTreeNode *node, lvTree *lvt, lvTreeNode *avl_parent, boolean_t refCntMaint); #ifdef DEBUG boolean_t lvTreeIsWellFormed(lvTree *lvt); void assert_tree_member_offsets(void); #endif lvTreeNode *lvAvlTreeLookupInt(lvTree *lvt, treeKeySubscr *key, lvTreeNode **lookupNode); lvTreeNode *lvAvlTreeLookupNum(lvTree *lvt, treeKeySubscr *key, lvTreeNode **lookupNode); lvTreeNode *lvAvlTreeLookupStr(lvTree *lvt, treeKeySubscr *key, lvTreeNode **lookupNode); lvTreeNode *lvAvlTreeLookup(lvTree *lvt, treeKeySubscr *key, lvTreeNode **lookupNode); lvTreeNode *lvAvlTreeNodeInsert(lvTree *lvt, treeKeySubscr *key, lvTreeNode *parent); void lvAvlTreeNodeDelete(lvTree *lvt, lvTreeNode *node); /* The following LV_TREE_* macros are not defined as functions for performance reasons (to avoid overhead of parameter passing * and C stack push and pop) */ #define LV_TREE_CREATE(NEWTREE, SBS_PARENT, SBS_DEPTH, BASE_LV) \ { \ DEBUG_ONLY(assert_tree_member_offsets()); \ NEWTREE = lvtree_getslot(LV_GET_SYMVAL(base_lv)); \ /* Note: the fields are initialized below in the order in which they are defined in the "lvTree" structure */ \ NEWTREE->ident = MV_LV_TREE; \ assert(0 < SBS_DEPTH); \ NEWTREE->sbs_depth = SBS_DEPTH; \ NEWTREE->avl_height = 0; \ NEWTREE->base_lv = BASE_LV; \ NEWTREE->avl_root = NULL; \ NEWTREE->sbs_parent = SBS_PARENT; /* note: LVT_PARENT macro not used as otherwise one would wonder why \ * all members of "lvTree" structure except "sbs_parent" are initialized. \ */ \ (SBS_PARENT)->sbs_child = NEWTREE; \ NEWTREE->lastLookup.lastNodeLookedUp = NULL; \ } #define LV_TREE_NODE_DELETE(LVT, NODE) \ { \ assert(NULL != LVT); \ lvAvlTreeNodeDelete(LVT, NODE); \ /* Now that "NODE" has been removed from the AVL tree, it is safe to free the slot */ \ LVTREENODE_FREESLOT(NODE); \ TREE_DEBUG_ONLY(assert(lvTreeIsWellFormed(LVT));) \ } #define LV_TREE_CLONE(LVT, SBS_PARENT, BASE_LV, REFCNTMAINT) \ { \ lvTree *cloneTree; \ lvTreeNode *avl_root; \ \ cloneTree = lvtree_getslot(LV_GET_SYMVAL(BASE_LV)); \ /* The following is optimized to do the initialization of just the needed structure members. \ * For that it assumes a particular "lvTree" structure layout. The assumed layout is asserted \ * so any changes to the layout will automatically alert us (by an assert failure) here and \ * cause the below initialization to be accordingly reworked. \ */ \ assert(8 == OFFSETOF(lvTree, base_lv)); \ assert(OFFSETOF(lvTree, base_lv) + SIZEOF(LVT->base_lv) == OFFSETOF(lvTree, avl_root)); \ assert(OFFSETOF(lvTree, avl_root) + SIZEOF(LVT->avl_root) == OFFSETOF(lvTree, sbs_parent)); \ assert(OFFSETOF(lvTree, sbs_parent) + SIZEOF(LVT->sbs_parent) == OFFSETOF(lvTree, lastLookup)); \ assert(OFFSETOF(lvTree, lastLookup) + SIZEOF(LVT->lastLookup) == SIZEOF(lvTree)); \ /* Directly copy the first 3 fields */ \ memcpy(cloneTree, (LVT), OFFSETOF(lvTree, avl_height) + SIZEOF(LVT->avl_height)); \ cloneTree->base_lv = BASE_LV; \ cloneTree->sbs_parent = SBS_PARENT; /* see comment in LV_TREE_CREATE macro (against sbs_parent \ * initialization) for why LVT_PARENT macro is not used */ \ (SBS_PARENT)->sbs_child = cloneTree; \ /* reset clue in cloned tree as source tree pointers are no longer relevant in cloned tree */ \ cloneTree->lastLookup.lastNodeLookedUp = NULL; \ if (NULL != (avl_root = (LVT)->avl_root)) \ cloneTree->avl_root = lvAvlTreeCloneSubTree(avl_root, cloneTree, NULL, (REFCNTMAINT)); \ else \ cloneTree->avl_root = NULL; \ } #ifdef TREE_DEBUG # define TREE_DEBUG1(p) {printf(p); FFLUSH(stdout);} # define TREE_DEBUG2(p, q) {printf(p, q); FFLUSH(stdout);} # define TREE_DEBUG3(p, q, r) {printf(p, q, r); FFLUSH(stdout);} # define TREE_DEBUG4(p, q, r, s) {printf(p, q, r, s); FFLUSH(stdout);} # define TREE_DEBUG5(p, q, r, s, t) {printf(p, q, r, s, t); FFLUSH(stdout);} # define TREE_DEBUG_ONLY(X) X #else # define TREE_DEBUG1(p) # define TREE_DEBUG2(p, q) # define TREE_DEBUG3(p, q, r) # define TREE_DEBUG4(p, q, r, s) # define TREE_DEBUG5(p, q, r, s, t) # define TREE_DEBUG_ONLY(X) #endif #endif fis-gtm-V7.0-005/sr_port/lv_val.h0000755000032200000250000005524414342376331015514 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef LVVAL_H_INCLUDED #define LVVAL_H_INCLUDED #include /* for OFFSETOF macro used in the IS_OFFSET_AND_SIZE_MATCH macro */ #include "hashtab_mname.h" #include "lv_tree.h" /* Define a few generic LV related macros. These macros work irrespective of whether the input is an unsubscripted * local variable (aka base variable) which is of type (lv_val *) or a subscripted local variable which is of type * (lvTreeNode *). All of these assume the layout of the two structures is very similar (asserted in "LV_TREE_CREATE" * macro). These macros will be used extensively for code centralization and to avoid code duplication. * * The macros that are only passed an LV_PTR need to ensure that it is indeed a "lv_val *" or a "lvTreeNode *". * The macros that are also passed an IS_BASE_VAR parameter can safely assume this to be the case since the IS_BASE_VAR * parameter would have been computed using the LV_IS_BAS_VAR macro which already does the ensure. * So we avoid the duplicate assert in these cases. */ #define SYM_IS_SYMVAL(SYM) (MV_SYM == (SYM)->ident) #define IS_LV_TREE(LVT) (MV_LV_TREE == ((lvTree *)LVT)->ident) #define LV_PARENT(LV) LV_AVLNODE_PARENT(LV) #define LV_AVLNODE_PARENT(PTR) (((lvTreeNode *)PTR)->tree_parent) #define LVT_PARENT(LVT) (((lvTree *)LVT)->sbs_parent) #define IS_LVAVLTREENODE(PTR) (IS_LV_TREE(LV_AVLNODE_PARENT((lv_val *)PTR))) #define IS_PARENT_MV_SYM(LV_PTR) (SYM_IS_SYMVAL(LV_SYMVAL((lv_val *)LV_PTR))) #define DBG_ASSERT_LVT(LVT) DBG_ASSERT(IS_LV_TREE(LVT)) /* is "lvTree *" */ #define DBG_ASSERT_LV_OR_TREENODE(LV_PTR) DBG_ASSERT(IS_PARENT_MV_SYM(LV_PTR) /* is "lv_val *" */ \ || IS_LVAVLTREENODE(LV_PTR)) /* is "lvTreeNode *" or "lvTreeNodeFlt *" */ #define LV_IS_BASE_VAR(LV_PTR) (DBG_ASSERT_LV_OR_TREENODE(LV_PTR) \ IS_PARENT_MV_SYM(LV_PTR)) /* Input to macro is LV_PTR which is guaranteed to be a non-base lv. Return value is the corresponding base_lv */ #define LV_GET_BASE_VAR(LV_PTR) (DBG_ASSERT(!LV_IS_BASE_VAR(LV_PTR)) \ DBG_ASSERT(NULL != LV_AVLNODE_PARENT(LV_PTR)) \ LV_AVLNODE_PARENT(LV_PTR)->base_lv) #define LVT_GET_BASE_VAR(LVT) (DBG_ASSERT_LVT(LVT) \ LVT->base_lv) #define LVT_GET_SYMVAL(LVT) (DBG_ASSERT_LVT(LVT) \ LV_GET_SYMVAL(LVT_GET_BASE_VAR(LVT))) #define LV_GET_PARENT_TREE(LV_PTR) (DBG_ASSERT(!LV_IS_BASE_VAR(LV_PTR)) \ DBG_ASSERT(NULL != LV_AVLNODE_PARENT(LV_PTR)) \ LV_AVLNODE_PARENT(LV_PTR)) /* The following 3 macros operate on the ptrs.val_ent.children field. * If the situation demands returning the children ptr, then use LV_GET_CHILD or LV_CHILD macros in that order of preference. * If the situation demands checking if any children exist, then use LV_HAS_CHILD macro. */ #define LV_CHILD(LV_PTR) (((lv_val *)LV_PTR)->ptrs.val_ent.children) /* The following macro is an enhanced version of LV_CHILD that can be used wherever you would prefer or dont mind additional * assert checking. In some cases, this cannot be used (e.g. in the left hand side of an assignment operation). But otherwise * it is preferrable to use this macro as opposed to LV_CHILD. */ #define LV_GET_CHILD(LV_PTR) (DBG_ASSERT_LV_OR_TREENODE(LV_PTR) \ LV_CHILD(LV_PTR)) \ /* if an lv_ptr's chlidren tree pointer is non-NULL, we should be guaranteed (by lv_kill) * that there is at least one child node in that subscript tree (currently only an avl tree). * assert that as well. */ #define LV_HAS_CHILD(LV_PTR) (DBG_ASSERT_LV_OR_TREENODE(LV_PTR) \ (NULL != LV_CHILD(LV_PTR)) \ DEBUG_ONLY(&& assert(MV_LV_TREE == (LV_CHILD(LV_PTR))->ident)) \ DEBUG_ONLY(&& assert((LV_CHILD(LV_PTR))->avl_height))) /* Like the LV_CHILD and LV_GET_CHILD macro variants, the below macros need to be used with preference to LV_GET_SYMVAL * unless it is needed in the left hand side of an assignment in which case use the LV_SYMVAL macro. */ #define LV_SYMVAL(BASE_VAR) ((BASE_VAR)->ptrs.val_ent.parent.sym) #define LV_GET_SYMVAL(BASE_VAR) (DBG_ASSERT(LV_IS_BASE_VAR(BASE_VAR)) \ LV_SYMVAL(BASE_VAR)) /* The below macro relies on the fact that the parent field is at the same offset in an "lv_val" as it is in a "lvTreeNode". * This is asserted in "LV_TREE_CREATE" macro. */ #define LV_GET_PARENT(LV_PTR) (DBG_ASSERT_LV_OR_TREENODE(LV_PTR) \ LV_AVLNODE_PARENT(LV_PTR)) #define LVT_GET_PARENT(LVT) (DBG_ASSERT_LVT(LVT) \ LVT_PARENT(LVT)) #define LV_IS_VAL_DEFINED(LV_PTR) (DBG_ASSERT_LV_OR_TREENODE(LV_PTR) \ MV_DEFINED(&((lv_val *)LV_PTR)->v)) #define LV_SBS_DEPTH(LV_PTR, IS_BASE_VAR, DEPTH) \ { \ assert(IS_OFFSET_AND_SIZE_MATCH(lvTree, sbs_depth, symval, sbs_depth)); \ assert(!IS_BASE_VAR || (0 == LV_PTR->ptrs.val_ent.parent.sbs_tree->sbs_depth)); \ assert(IS_BASE_VAR || (0 < LV_PTR->ptrs.val_ent.parent.sbs_tree->sbs_depth)); \ DEPTH = LV_PTR->ptrs.val_ent.parent.sbs_tree->sbs_depth; \ } /* Mark mval held by lv_ptr to be undefined. Also lets stp_gcol and lv_gcol know to NOT protect * (from garbage collection) any strings this lv_val was pointing to at the time of the free. */ #define LV_VAL_CLEAR_MVTYPE(LVPTR) (LVPTR)->v.mvtype = 0; /* note: also clears any use as MV_ALIASCONT */ /* Queue an lv_val block back on the lv_val free list at the given anchor. * Operations: * 1) Debugging aids in debug builds. * 2) Do the queueing. * 3) Clear the mv_type so it is definitely a deleted value. * * Callers should use LV_FREESLOT instead of directly invoking LV_FLIST_ENQUEUE. * There are few exceptions like unw_mv_ent.c. */ #define LV_FLIST_ENQUEUE(flist_ptr, lv_ptr) \ { \ lv_val **savflist_ptr = (flist_ptr); \ DBGRFCT((stderr, "\n<< Free list queueing of lv_val at 0x"lvaddr" by %s line %d\n", \ (lv_ptr), __FILE__, __LINE__)); \ assert(LV_IS_BASE_VAR(lv_ptr)); \ /* assert that any subtree underneath this lv_ptr has already been freed up */ \ assert(NULL == LV_CHILD(lv_ptr)); \ LV_VAL_CLEAR_MVTYPE(lv_ptr); \ DEBUG_ONLY(memset((lv_ptr), 0xfd, SIZEOF(lv_val))); \ (lv_ptr)->ptrs.free_ent.next_free = *savflist_ptr; \ *savflist_ptr = (lv_ptr); \ LV_SYMVAL(lv_ptr) = NULL; \ DBGALS_ONLY((lv_ptr)->lvmon_mark = FALSE); \ } /* Increment the cycle for tstarts. Field is compared to same name field in lv_val to signify an lv_val has been seen * during a given transaction so reference counts are kept correct. If counter wraps, clear all the counters in all * accessible lv_vals. */ #define INCR_TSTARTCYCLE \ { \ symval *lvlsymtab; \ lv_blk *lvbp; \ lv_val *lvp, *lvp_top; \ \ if (0 == ++tstartcycle) \ { /* Set tstart cycle in all active lv_vals to 0 */ \ for (lvlsymtab = curr_symval; lvlsymtab; lvlsymtab = lvlsymtab->last_tab) \ for (lvbp = curr_symval->lv_first_block; lvbp; lvbp = lvbp->next) \ for (lvp = (lv_val *)LV_BLK_GET_BASE(lvbp), lvp_top = LV_BLK_GET_FREE(lvbp, lvp); \ lvp < lvp_top; lvp++) \ lvp->stats.tstartcycle = 0; \ tstartcycle = 1; \ } \ DBGRFCT((stderr, "INCR_TSTARTCYCLE: Bumped tstartcycle to 0x%08lx\n", tstartcycle)); \ } /* Increment the cycle for misc lv tasks. Field is compared to same name field in lv_val to signify an lv_val has been seen * during a given transaction so reference counts are kept correct. If counter wraps, clear all the counters in all * accessible lv_vals. */ #define INCR_LVTASKCYCLE \ { \ symval *lvlsymtab; \ lv_blk *lvbp; \ lv_val *lvp, *lvp_top; \ \ if (0 == ++lvtaskcycle) \ { /* Set tstart cycle in all active lv_vals to 0 */ \ for (lvlsymtab = curr_symval; lvlsymtab; lvlsymtab = lvlsymtab->last_tab) \ for (lvbp = curr_symval->lv_first_block; lvbp; lvbp = lvbp->next) \ for (lvp = (lv_val *)LV_BLK_GET_BASE(lvbp), lvp_top = LV_BLK_GET_FREE(lvbp, lvp); \ lvp < lvp_top; lvp++) \ lvp->stats.lvtaskcycle = 0; \ lvtaskcycle = 1; \ } \ DBGRFCT((stderr, "INCR_LVTASKCYCLE: Bumped lvtaskcycle to 0x%08lx\n", lvtaskcycle)); \ } /* Initialize given lv_val (should be of type "lv_val *" and not "lvTreeNode *") */ #define LVVAL_INIT(lv, symvalarg) \ { \ DBGALS_ONLY(GBLREF boolean_t lvmon_enabled;) \ DBGALS_ONLY(GBLREF stack_frame *frame_pointer;) \ assert(MV_SYM == symvalarg->ident); /* ensure above macro is never used to initialize a "lvTreeNode *" */ \ (lv)->v.mvtype = 0; \ (lv)->stats.trefcnt = 1; \ (lv)->stats.crefcnt = 0; \ (lv)->stats.tstartcycle = 0; \ (lv)->stats.lvtaskcycle = 0; \ (lv)->has_aliascont = FALSE; \ DBGALS_ONLY(if (lvmon_enabled) (lv)->lvmon_mark = TRUE; else (lv)->lvmon_mark = FALSE); \ (lv)->tp_var = NULL; \ LV_CHILD(lv) = NULL; \ LV_SYMVAL(lv) = symvalarg; \ DBGALS_ONLY((lv)->stats.init_mpc = frame_pointer->mpc); \ DBGALS_ONLY((lv)->stats.init_cpc = CURRENT_PC); /* Currently only works for Linux */ \ } /* Macro to call lv_var_clone and set the cloned status in the tp_var structure. * Note that we want the cloned tree to have its base_lv point back to "lv" and not the cloned lv. * This is because in case we want to restore the lv, we can then safely move the saved tree * back to "lv" without then having to readjust the base_lv linked in the "lvTree *" structures beneath */ #define TP_VAR_CLONE(lv) \ { \ lv_val *lcl_savelv; \ tp_var *lcl_tp_var; \ \ assert(LV_IS_BASE_VAR(lv)); \ lcl_tp_var = (lv)->tp_var; \ assert(lcl_tp_var); \ assert(!lcl_tp_var->var_cloned); \ lcl_savelv = lcl_tp_var->save_value; \ assert(NULL != lcl_savelv); \ assert(NULL == LV_CHILD(lcl_savelv)); \ DBGRFCT((stderr, "\nTP_VAR_CLONE: invoked from %s at line %d\n", __FILE__, __LINE__)); \ LV_CHILD(lcl_savelv) = LV_CHILD(lv); \ lv_var_clone(lcl_savelv, lv, TRUE); \ lcl_tp_var->var_cloned = TRUE; \ DBGRFCT((stderr, "TP_VAR_CLONE: complete\n")); \ } /* Macro to indicate if a given lv_val is an alias or not */ #define IS_ALIASLV(lv) (DBG_ASSERT(LV_IS_BASE_VAR(lv)) ((1 < (lv)->stats.trefcnt) || (0 < (lv)->stats.crefcnt)) \ DEBUG_ONLY(&& assert(IS_PARENT_MV_SYM(lv)))) #define LV_NEWBLOCK_INIT_ALLOC 16 #define LV_BLK_GET_BASE(LV_BLK) (((sm_uc_ptr_t)LV_BLK) + SIZEOF(lv_blk)) #define LV_BLK_GET_FREE(LV_BLK, LVBLK_BASE) (&LVBLK_BASE[LV_BLK->numUsed]) #ifdef DEBUG_ALIAS # ifdef __linux__ /*void * __attribute ((noinline)) __builtin_return_address(unsigned int level); Currently gives a warning we don't need */ # define CURRENT_PC __builtin_return_address(0) # else # define CURRENT_PC NULL # endif #endif typedef struct lv_val_struct { mval v; /* Value associated with this lv_val */ /* Note: The offsets of "ptrs.val_ent.children" and "ptrs.val_ent.parent.sym" are relied upon by * other modules (e.g. lv_tree.h) and asserted in LV_TREE_CREATE macro. */ union { struct { lvTree *children; union { /* Note these two fields are still available when mvtype == MV_LVCOPIED * and there is code in "als_check_xnew_var_aliases" that depends on this */ struct symval_struct *sym; lvTree *sbs_tree; } parent; } val_ent; struct { struct lv_val_struct *next_free; } free_ent; struct { /* When xnew'd lv's are copied to previous symtab, their new root is * set here so multiple references can be resolved properly (mvtype == MV_LVCOPIED) */ struct lv_val_struct *newtablv; } copy_loc; } ptrs; struct { /* Note these flags are irrelevant for other than base (unsubscripted) local vars */ int4 trefcnt; /* Total refcnt (includes container vars) */ int4 crefcnt; /* Container reference count */ uint4 tstartcycle; /* Cycle of level 0 tstart command */ uint4 lvtaskcycle; /* Cycle of various lv related tasks */ # ifdef DEBUG_ALIAS /* Keep these debugging fields inside "stats" so as not to upset the asserts in tp_unwind() and also * since they don't change once set while being allocated anyway. */ unsigned char *init_mpc; /* frame_pointer->mpc when LVVAL_INIT() was done */ unsigned char *init_cpc; /* current pc so know which C routine did LVVAL_INIT() */ # endif } stats; boolean_t has_aliascont; /* This base var has or had an alias container in it */ boolean_t lvmon_mark; /* This lv_val is being monitored; Used only #ifdef DEBUG_ALIAS */ struct tp_var_struct *tp_var; } lv_val; typedef struct lv_blk_struct { struct lv_blk_struct *next; uint4 numAlloc; uint4 numUsed; } lv_blk; /* When op_xnew creates a symtab, these blocks will describe the vars that were passed through from the * previous symtab. They need special alias processing. Note we keep our own copy of the key (rather than * pointing to the hash table entry) since op_xnew processing can cause a hash table expansion and we have * no good way to update pointers so save the hash values as part of the key to eliminate another lookup. */ typedef struct lv_xnew_var_struct { struct lv_xnew_var_struct *next; mname_entry key; lv_val *lvval; /* There are two uses for this field. In op_new, it is used to hold the previous lvval addr for the 2nd pass. In unwind processing (als_check_xnew_var_aliases) it holds the lvval in the symtab being popped since it cannot be obtained once the symtab entry is deleted in the first pass (step 2). */ } lv_xnew_var; /* While lv_xnew_var_struct are the structures that were explicitly passed through, this is a list of the structures * that are pointed to by any container vars in any of the passed through vars and any of the vars those point to, etc. * The objective is to come up with a definitive list of structures to search to see if containers got created in them * that point to the structure being torn down. */ typedef struct lv_xnewref_struct { struct lv_xnewref_struct *next; lv_val *lvval; /* This structure can be addressed through the passed thru vars but is not itself one of them */ } lv_xnew_ref; typedef struct symval_struct { unsigned short ident; unsigned short sbs_depth; /* is always 0. Defined to match offset & size of lvTree->sbs_depth. * This way callers can avoid an if check depending on whether * the input pointer is a "symval *" or a "lvTree *" type. * This is also asserted in "LV_TREE_CREATE" macro. */ boolean_t tp_save_all; lv_xnew_var *xnew_var_list; lv_xnew_ref *xnew_ref_list; hash_table_mname h_symtab; lv_blk *lv_first_block; lv_blk *lvtree_first_block; lv_blk *lvtreenode_first_block; lv_val *lv_flist; lvTree *lvtree_flist; lvTreeNode *lvtreenode_flist; struct symval_struct *last_tab; int4 symvlvl; /* Level of symval struct (nesting) */ boolean_t trigr_symval; /* Symval is owned by a trigger */ boolean_t alias_activity; GTM64_ONLY(int4 filler;) } symval; /* Structure to describe the block allocated to describe a var specified on a TSTART to be restored * on a TP restart. Block moved here from tpframe.h due to the structure references it [now] makes. * Block can take two forms: (1) the standard tp_data form which marks vars to be modified or (2) the * tp_change form where a new symbol table was stacked. */ typedef struct tp_var_struct { struct tp_var_struct *next; struct lv_val_struct *current_value; struct lv_val_struct *save_value; mname_entry key; boolean_t var_cloned; GTM64_ONLY(int4 filler;) } tp_var; typedef struct lvname_info_struct { intszofptr_t total_lv_subs; /* Total subscripts + 1 for name itself */ lv_val *start_lvp; mval *lv_subs[MAX_LVSUBSCRIPTS]; lv_val *end_lvp; } lvname_info; typedef lvname_info *lvname_info_ptr; #define ASSERT_ACTIVELV_GOOD(LV) assert((NULL == LV) || (NULL == LV_AVLNODE_PARENT(LV)) \ || LV_IS_VAL_DEFINED(LV) || LV_HAS_CHILD(LV)); /* Although the below typedef is used only in DBG, define it for PRO too as gtmpcat requires this */ typedef struct activelv_dbg_struct { lv_val *active_lv; lv_val *newlv; struct stack_frame_struct *frame_pointer; symval *curr_symval; unsigned char *mpc; unsigned char *ctxt; uint4 count; uint4 type; } activelv_dbg_t; enum actlv_type { actlv_op_putindx1 = 1, /* = 1 */ actlv_op_putindx2, /* = 2 */ actlv_lv_kill, /* = 3 */ actlv_op_zshow, /* = 4 */ actlv_op_killalias, /* = 5 */ actlv_op_killaliasall, /* = 6 */ actlv_op_setals2als, /* = 7 */ actlv_op_setalsctin2als, /* = 8 */ actlv_op_setfnretin2als, /* = 9 */ actlv_op_xkill, /* = 10 */ actlv_unw_mv_ent, /* = 11 */ actlv_op_tstart, /* = 12 */ actlv_gtm_fetch, /* = 13 */ actlv_mdb_condition_handler, /* = 14 */ actlv_merge_desc_check1, /* = 15 */ actlv_merge_desc_check2, /* = 16 */ actlv_merge_desc_check3, /* = 17 */ actlv_op_merge1, /* = 18 */ actlv_op_merge2, /* = 19 */ actlv_tp_unwind_restart, /* = 20 */ actlv_tp_unwind_rollback, /* = 21 */ actlv_tp_unwind_commit /* = 22 */ }; #ifdef DEBUG void set_active_lv(lv_val *newlv, boolean_t do_assert, int type); # define SET_ACTIVE_LV(NEWLV, DO_ASSERT, TYPE) set_active_lv((lv_val *)NEWLV, DO_ASSERT, TYPE) #else # define SET_ACTIVE_LV(NEWLV, DO_ASSERT, TYPE) \ { \ GBLREF lv_val *active_lv; \ \ active_lv = (lv_val *)NEWLV; \ } #endif #define UNDO_ACTIVE_LV(ACTIVELV_CODE) \ { \ GBLREF lv_val *active_lv; \ \ if (NULL != active_lv) \ { /* If LV_AVLNODE_PARENT is NULL, it means the node has already been \ * freed. We dont know of any such case right now so assert. In PRO \ * though we want to be safe so we skip the kill in that case but \ * reset active_lv to NULL. \ */ \ assert(NULL != LV_AVLNODE_PARENT(active_lv)); \ if (NULL != LV_AVLNODE_PARENT(active_lv)) \ { \ if (!LV_IS_VAL_DEFINED(active_lv) && !LV_HAS_CHILD(active_lv)) \ op_kill(active_lv); \ } \ SET_ACTIVE_LV(NULL, FALSE, ACTIVELV_CODE); \ } \ } #define DOTPSAVE_FALSE FALSE /* macro to indicate parameter by name "dotpsave" is passed a value of "FALSE" */ #define DOTPSAVE_TRUE TRUE /* macro to indicate parameter by name "dotpsave" is passed a value of "TRUE" */ #define DO_SUBTREE_FALSE FALSE /* macro to indicate parameter by name "do_subtree" is passed a value of "FALSE" */ #define DO_SUBTREE_TRUE TRUE /* macro to indicate parameter by name "do_subtree" is passed a value of "TRUE" */ #define LV_FREESLOT(LV) \ { \ symval *sym; \ \ assert(LV_IS_BASE_VAR(LV)); \ sym = LV_GET_SYMVAL(LV); \ LV_FLIST_ENQUEUE(&sym->lv_flist, LV); \ } #define LVTREE_FREESLOT(LVT) \ { \ symval *sym; \ \ sym = LVT_GET_SYMVAL(LVT); \ assert(NULL != LVT_GET_PARENT(LVT)); \ LVT_PARENT(LVT) = NULL; /* indicates this is free */ \ /* avl_root is overloaded to store linked list in free state */ \ LVT->avl_root = (lvTreeNode *)sym->lvtree_flist; \ sym->lvtree_flist = LVT; \ } #define LVTREENODE_FREESLOT(LV) \ { \ symval *sym; \ lv_val *base_lv; \ \ assert(!LV_IS_BASE_VAR(LV)); \ base_lv = LV_GET_BASE_VAR(LV); \ sym = LV_GET_SYMVAL(base_lv); \ LV_AVLNODE_PARENT(LV) = NULL; /* indicates to stp_gcol this is free */ \ /* sbs_child is overloaded to store linked list in free state */ \ (LV)->sbs_child = (lvTree *)sym->lvtreenode_flist; \ sym->lvtreenode_flist = LV; \ } /* Call function pointer, if not NULL, to consume formatted output. * Currently only used for ojpassvar_hook(). */ #define ZWRITE_OUTPUT_HOOK() \ MBSTART { \ if (TREF(zwrite_output_hook)) \ (*(void (*)(void))TREF(zwrite_output_hook))(); \ } MBEND /* Macro used intermittently in code to debug alias code in general. Note this macro must be specified * as a compile option since it is used in macros that do not pull in this alias.h header file. */ #ifdef DEBUG_ALIAS # define DBGALS(x) DBGFPF(x) # define DBGALS_ONLY(x) x #else # define DBGALS(x) # define DBGALS_ONLY(x) #endif unsigned char *format_lvname(lv_val *start, unsigned char *buff, int size); lv_val *lv_getslot(symval *sym); lvTree *lvtree_getslot(symval *sym); lvTreeNode *lvtreenode_getslot(symval *sym); void lv_kill(lv_val *lv, boolean_t dotpsave, boolean_t do_subtree); void lv_killarray(lvTree *lvt, boolean_t dotpsave); void lv_newblock(symval *sym, int numElems); void lv_newname(ht_ent_mname *hte, symval *sym); void lvtree_newblock(symval *sym, int numElems); void lvtreenode_newblock(symval *sym, int numElems); void lv_var_clone(lv_val *clone_var, lv_val *base_lv, boolean_t refCntMaint); void lvzwr_var(lv_val *lv, int4 n); void op_clralsvars(lv_val *dst); void op_fndata(lv_val *x, mval *y); void op_fnzdata(lv_val *x, mval *y); void op_fnincr(lv_val *local_var, mval *increment, mval *result); void op_fnnext(lv_val *src, mval *key, mval *dst); void op_fno2(lv_val *src, mval *key, mval *dst, mval *direct); void op_fnorder(lv_val *src, mval *key, mval *dst); void op_fnzahandle(lv_val *src, mval *dst); void op_fnzprevious(lv_val *src, mval *key, mval *dst); void op_kill(lv_val *lv); void op_killalias(int srcindx); void op_lvzwithdraw(lv_val *lv); void op_setals2als(lv_val *src, int dstindx); void op_setalsin2alsct(lv_val *src, lv_val *dst); void op_setalsctin2als(lv_val *src, int dstindx); void op_setalsct2alsct(lv_val *src, lv_val *dst); void op_setfnretin2als(mval *srcmv, int destindx); /* not an lv_val ref but kept here with its friends so it's not lonely */ void op_setfnretin2alsct(mval *srcmv, lv_val *dstlv); void op_zshow(mval *func, int type, lv_val *lvn); lv_val *op_getindx(UNIX_ONLY_COMMA(int argcnt) lv_val *start, ...); lv_val *op_putindx(UNIX_ONLY_COMMA(int argcnt) lv_val *start, ...); lv_val *op_srchindx(UNIX_ONLY_COMMA(int argcnt_arg) lv_val *lv, ...); lv_val *op_m_srchindx(UNIX_ONLY_COMMA(int4 count) lv_val *lvarg, ...); /* Function Prototypes for local variables functions of merge */ boolean_t lcl_arg1_is_desc_of_arg2(lv_val *cur, lv_val *ref); unsigned char *format_key_mvals(unsigned char *buff, int size, lvname_info *lvnp); unsigned char *format_key_lv_val(lv_val *lvpin, unsigned char *buff, int size); #endif fis-gtm-V7.0-005/sr_port/lv_var_clone.c0000644000032200000250000000427014342376331016663 0ustar librarygtc/**************************************************************** * * * Copyright 2009, 2014 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 /* For offsetof() macro */ #include "gtm_string.h" #include "gtm_stdio.h" #include "gtmio.h" #include "lv_val.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "alias.h" /* Routine to clone the children of a tree. The input lv_val "clone_var" should be a clone of the base lv_val * owning the tree we wish to clone. The pointers in this copy will be duplicated and the new tree linked to * "clone_var". Note that the owning symval of clone_var should be set appropriately such as in the case of * xnew processing where we are cloneing a tree out of one symtab and into another. The new tree will be * created in the symtab of the input lv_val regardless of which symtab owns the processed lv_vals. * * Inputs: * - clone_var is the base variable that we are cloning into. * - base_lv is the base variable that the cloned tree should point back to. * - refCntMaint is whether we should bump refcnts for the targets of any alias containers we locate. */ void lv_var_clone(lv_val *clone_var, lv_val *base_lv, boolean_t refCntMaint) { lvTree *clone_lvt; assert(clone_var); assert(LV_IS_BASE_VAR(clone_var)); assert(base_lv); assert(LV_IS_BASE_VAR(base_lv)); DBGRFCT((stderr, "\nlv_var_clone: Cloning base lv_val tree at 0x"lvaddr" into save_lv 0x"lvaddr"\n", base_lv, clone_var)); clone_lvt = LV_GET_CHILD(clone_var); /* "clone_lvt" holds tree to be cloned as we build new tree for clone_var */ if (NULL != clone_lvt) { assert(1 == clone_lvt->sbs_depth); LV_TREE_CLONE(clone_lvt, (lvTreeNode *)clone_var, base_lv, refCntMaint); } DBGRFCT((stderr, "lv_var_clone: Cloning of base lv_val tree at 0x"lvaddr" complete\n\n", base_lv)); } fis-gtm-V7.0-005/sr_port/lvmon_compare_value_slots.c0000644000032200000250000001025114342376331021467 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2017 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_stdio.h" #include "gtm_string.h" #include "gtmio.h" #include "io.h" #include "lv_val.h" #include "send_msg.h" #include "localvarmonitor.h" #include "error.h" /* For "gtm_fork_n_core" */ GBLREF int4 gtm_trigger_depth; error_def(ERR_LVMONBADVAL); /* Routine that for each local variable being monitored compares the values saved in the two given slots. The assumption is * that these slots have been loaded across multiple calls to lvmon_pull_values() in those slots. Variables must be the same * type and the same value within that type. Note numeric values are not at this time verified so we are only looking for * string types and seeing they have not changed. Note this routine just finds the problems and sends messages to syslog but * does not force the program to stop. * * Parameters: * lvmon_idx1 - First index to be compared - assumed to be the "expected" value. * lvmon_idx2 - Second index to be compared. * * If the values saved in the given slots are not the same, an assertpro is generated. */ void lvmon_compare_value_slots(int lvmon_idx1, int lvmon_idx2) { int len, cnt; lvmon_var *lvmon_var_p; lvmon_value_ent *lvmon_val_ent1_p; lvmon_value_ent *lvmon_val_ent2_p; boolean_t error_seen; char *dummy_str; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(TREF(lvmon_active)); if ((0 != gtm_trigger_depth) || !TREF(lvmon_active)) return; /* Avoid check if no values yet or triggers are active */ error_seen = FALSE; DBGLVMON((stderr, "lvmon_compare_value_slots: Checking slot %d vs %d\n", lvmon_idx1, lvmon_idx2)); /* For each of the local variables, compare the string values in the slots looking for corruption */ for (cnt = TREF(lvmon_vars_count), lvmon_var_p = TREF(lvmon_vars_anchor); 0 < cnt; cnt--, lvmon_var_p++) { /* First, see if we have an existing lv_val for this var and if it is valid */ lvmon_val_ent1_p = &lvmon_var_p->values[lvmon_idx1 - 1]; lvmon_val_ent2_p = &lvmon_var_p->values[lvmon_idx2 - 1]; if ((MV_STR & lvmon_val_ent1_p->varlvval.v.mvtype) && (MV_STR & lvmon_val_ent2_p->varlvval.v.mvtype)) { /* Both have string values (not checking string vs number sort of thing at this time */ len = lvmon_val_ent1_p->varvalue.len; DBGLVMON((stderr, "** lvmon_compare_value_slots: Checking strings for var %.*s\n", lvmon_var_p->lvmv.var_name.len, lvmon_var_p->lvmv.var_name.addr)); if ((len != lvmon_val_ent2_p->varvalue.len) || (0 != memcmp(lvmon_val_ent1_p->varvalue.addr, lvmon_val_ent2_p->varvalue.addr, len))) { /* Something in the value changed and not for the better */ error_seen = TRUE; DBGLVMON((stderr, "!! lvmon_compare_value_slots: Strings don't match (%d vs %d). Expected value: " "%.*s Actual value: %.*s\n", lvmon_idx1, lvmon_idx2, lvmon_val_ent1_p->varvalue.len, lvmon_val_ent1_p->varvalue.addr, lvmon_val_ent2_p->varvalue.len, lvmon_val_ent2_p->varvalue.addr)); send_msg_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_LVMONBADVAL, 8, lvmon_var_p->lvmv.var_name.len, lvmon_var_p->lvmv.var_name.addr, lvmon_idx1, lvmon_idx2, lvmon_val_ent1_p->varvalue.len, lvmon_val_ent1_p->varvalue.addr, lvmon_val_ent2_p->varvalue.len, lvmon_val_ent2_p->varvalue.addr); gtm_fork_n_core(); /* Generate a core at the failure point */ } } else { DBGLVMON((stderr, "** lvmon_compare_value_slots: Comparison avoided for var %.*s - one or both of the " "values not a string - idx%d type: 0x%04lx", lvmon_var_p->lvmv.var_name.len, lvmon_var_p->lvmv.var_name.addr, lvmon_idx1, lvmon_val_ent1_p->varlvval.v.mvtype)); DBGLVMON((stderr, " idx%d type: 0x%04lx\n",lvmon_idx2, lvmon_val_ent2_p->varlvval.v.mvtype)); } } } fis-gtm-V7.0-005/sr_port/lvmon_pull_values.c0000644000032200000250000001236714342376331017766 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2017 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_stdio.h" #include "gtm_string.h" #include "gtmio.h" #include "io.h" #include "lv_val.h" #include "localvarmonitor.h" #define LVMON_WBOX_BREAK_VALUE "This is a white box test string" #define LVMON_SAVE_VALUE(TYPE, VALUELEN, VALUE) \ { \ DBGLVMON((stderr, "** lvmon_pull_values: Fetch "TYPE" string value for variable %.*s\n", \ lvmon_var_p->lvmv.var_name.len, lvmon_var_p->lvmv.var_name.addr)); \ lvmon_val_ent_p->varvalue.len = len = (VALUELEN); \ if (0 < len) \ { /* Need to copy the string to malloc'd space. If current buffer can hold it, \ * use it - else release (if exists) and malloc a new one. \ */ \ if (len > lvmon_val_ent_p->alloclen) \ { /* Length of buffer is too small, release and realloc */ \ DBGLVMON((stderr, "** lvmon_pull_values: Reallocating buffer\n")); \ if (lvmon_val_ent_p->alloclen) \ free(lvmon_val_ent_p->varvalue.addr); \ lvmon_val_ent_p->varvalue.addr = malloc(len); \ lvmon_val_ent_p->alloclen = len; \ } \ assert(len >= lvmon_val_ent_p->varvalue.len); \ memcpy(lvmon_val_ent_p->varvalue.addr, (VALUE), len); \ } \ } GBLREF int4 gtm_trigger_depth; GBLREF symval *curr_symval; /* Routine to pull the current values of a defined list of local vars and store it in one of the value slots. * * Parameters: * lvmon_p - Pointer to lvmon array for which we are grabbing values. * lvmon_value_idx - Which value index in the lvmon.varvalues[] array to use to store the values. * * Return value meanings: * 0 - All values fetched */ void lvmon_pull_values(int lvmon_ary_idx) { int cnt, size, len; lvmon_var *lvmon_var_p; ht_ent_mname *tabent; lv_val *lv_val_p; lvmon_value_ent *lvmon_val_ent_p; DEBUG_ONLY(boolean_t lvmon_wbtest_break_mstr;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(TREF(lvmon_active)); if ((0 != gtm_trigger_depth) || !TREF(lvmon_active)) return; /* Don't record trigger values as they disappear when trigger done */ DEBUG_ONLY(lvmon_wbtest_break_mstr = FALSE); DBGLVMON((stderr,"lvmon_pull_values: Pulling values into slot %d\n",lvmon_ary_idx)); /* Fetch the value of each of the local variables and store them in the values array in the specified index */ for (cnt = TREF(lvmon_vars_count), lvmon_var_p = TREF(lvmon_vars_anchor); 0 < cnt; cnt--, lvmon_var_p++) { /* First, see if we have an existing lv_val for this var and if it is valid */ lvmon_val_ent_p = &lvmon_var_p->values[lvmon_ary_idx - 1]; if ((NULL == lvmon_var_p->varlvadr) || (TREF(curr_symval_cycle) != lvmon_var_p->curr_symval_cycle)) { /* Either lv_val pointer does not exist or is no longer valid so needs to be re-located */ DBGLVMON((stderr, "** lvmon_pull_values: (Re)Pull lv_val addr for %.*s\n", lvmon_var_p->lvmv.var_name.len, lvmon_var_p->lvmv.var_name.addr)); tabent = lookup_hashtab_mname(&curr_symval->h_symtab, &lvmon_var_p->lvmv); if (NULL == tabent) { /* Variable does not exist in this symbol table so clear the array index for the value * and continue on. Note do not clear the alloclen field or the address of the value in * varvalue to avoid a memory leak. */ DBGLVMON((stderr, "** lvmon_pull_values: lv_val for %.*s not found (yet)\n", lvmon_var_p->lvmv.var_name.len, lvmon_var_p->lvmv.var_name.addr)); memset(&lvmon_val_ent_p->varlvval, 0, SIZEOF(lv_val)); lvmon_val_ent_p->varvalue.len = 0; continue; } /* Update the fields */ lvmon_var_p->curr_symval_cycle = TREF(curr_symval_cycle); lvmon_var_p->varlvadr = lv_val_p = ((lv_val *)tabent->value); } else lv_val_p = lvmon_var_p->varlvadr; lvmon_val_ent_p->varlvval = *lv_val_p; /* Save entire previous lv_val */ /* Common code if lv_val is string - update fields in the specified index of the value array */ if (MV_IS_STRING(&lv_val_p->v)) { /* We have a string, see about storing it in a malloc'd buffer. Since we are going * to be monitoring stringpool garbage collection, it is best to keep our comparison * values out of the stringpool so we can avoid having "stp_gcol" process our value * array. Note every $gtm_white_box_test_case_count times, we will copy he wrong value * to force an error to occur. */ # ifdef DEBUG GTM_WHITE_BOX_TEST(WBTEST_LVMON_PSEUDO_FAIL, lvmon_wbtest_break_mstr, TRUE); if (lvmon_wbtest_break_mstr) { LVMON_SAVE_VALUE("**FAKE**", SIZEOF(LVMON_WBOX_BREAK_VALUE) - 1, LVMON_WBOX_BREAK_VALUE); } else # endif { LVMON_SAVE_VALUE("current string", lv_val_p->v.str.len, lv_val_p->v.str.addr); } DEBUG_ONLY(lvmon_wbtest_break_mstr = FALSE); } else DBGLVMON((stderr, "** lvmon_pull_values: Value not a string: 0x%04lx\n", lv_val_p->v.mvtype)); } return; } fis-gtm-V7.0-005/sr_port/lvn.c0000755000032200000250000000426014342376331015013 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "compiler.h" #include "fullbool.h" #include "opcode.h" #include "mdq.h" #include "toktyp.h" #include "advancewindow.h" #include "show_source_line.h" GBLREF boolean_t run_time; error_def(ERR_MAXNRSUBSCRIPTS); error_def(ERR_RPARENMISSING); error_def(ERR_VAREXPECTED); error_def(ERR_SIDEEFFECTEVAL); int lvn(oprtype *a, opctype index_op, triple *parent) { char x; oprtype *sb, *sb1, *sb2, subscripts[MAX_LVSUBSCRIPTS]; triple *ref, *root; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TREF(window_token) != TK_IDENT) { stx_error(ERR_VAREXPECTED); return FALSE; } *a = put_mvar(&(TREF(window_ident))); advancewindow(); if (TK_LPAREN != TREF(window_token)) return TRUE; assert(TRIP_REF == a->oprclass); DEBUG_ONLY(ref = a->oprval.tref); assert(OC_VAR == ref->opcode); sb1 = sb2 = subscripts; *sb1++ = *a; for (;;) { if (ARRAYTOP(subscripts) <= sb1) { stx_error(ERR_MAXNRSUBSCRIPTS); return FALSE; } advancewindow(); if (EXPR_FAIL == expr(sb1++, MUMPS_EXPR)) return FALSE; if (TK_RPAREN == (x = TREF(window_token))) /* NOTE assignment */ { advancewindow(); break; } if (TK_COMMA != x) { stx_error(ERR_RPARENMISSING); return FALSE; } } if (parent) { /* only $ORDER, $NEXT, $ZPREV have parent */ sb1--; if ((sb1 - sb2) == 1) /* only name and 1 subscript */ { /* SRCHINDX not necessary if only 1 subscript */ sb = &parent->operand[1]; *sb = *sb1; return TRUE; } } root = ref = newtriple(index_op); ref->operand[0] = put_ilit((mint)(sb1 - sb2)); SUBS_ARRAY_2_TRIPLES(ref, sb1, sb2, subscripts, 0); if (parent) { parent->operand[0] = put_tref(root); sb = &parent->operand[1]; *sb = *sb1; return TRUE; } *a = put_tref(root); return TRUE; } fis-gtm-V7.0-005/sr_port/lvzwr_arg.c0000755000032200000250000000320414342376331016226 0ustar librarygtc/**************************************************************** * * * 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 "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "hashtab_mname.h" #include "hashtab_addr.h" #include "zwrite.h" GBLREF lvzwrite_datablk *lvzwrite_block; void lvzwr_arg(int t, mval *a1, mval *a2) { int sub_idx; assert(lvzwrite_block); sub_idx = lvzwrite_block->subsc_count++; /* it would be good to guard the array sub_idx < sizeof... */ if (a1) { MV_FORCE_DEFINED(a1); if (MV_IS_CANONICAL(a1)) MV_FORCE_NUMD(a1); MV_FORCE_STRD(a1); if ((ZWRITE_VAL != t) && (0 == a1->str.len)) /* value is real - leave it alone */ a1 = NULL; } if (a2) { MV_FORCE_DEFINED(a2); if (MV_IS_CANONICAL(a2)) MV_FORCE_NUMD(a2); MV_FORCE_STRD(a2); if (0 == a2->str.len) /* can never be value */ a2 = NULL; } ((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[sub_idx].subsc_type = t; ((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[sub_idx].first = a1; ((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[sub_idx].second = a2; if ((ZWRITE_ASTERISK != t) && (ZWRITE_ALL != t)) lvzwrite_block->mask |= 1 << sub_idx; if (ZWRITE_VAL != t) lvzwrite_block->fixed = FALSE; return; } fis-gtm-V7.0-005/sr_port/lvzwr_init.c0000755000032200000250000000435314342376331016426 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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 "gtm_string.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "zwrite.h" #include "subscript.h" #include "mlkdef.h" #include "zshow.h" #include "alias.h" GBLREF lvzwrite_datablk *lvzwrite_block; GBLREF int merge_args; GBLREF symval *curr_symval; GBLREF uint4 zwrtacindx; void lvzwr_init(enum zwr_init_types zwrpattyp, mval *val) { lvzwrite_datablk *prevzwrb; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Standard call at start of zwrite type functions. If this symval has aliases in it, * prep a hash table we will use to track the lv_val addrs we process (but only if not merging). */ if (!merge_args) { /* Re-initialize table even if no "aliases" defined since dotted parms are actually aliases too and * will be placed in this table by lvzwr_out(). */ als_zwrhtab_init(); zwrtacindx = 0; } if (!lvzwrite_block) { lvzwrite_block = (lvzwrite_datablk *)malloc(SIZEOF(lvzwrite_datablk)); memset(lvzwrite_block, 0, SIZEOF(lvzwrite_datablk)); } else { /* Get back to one zwrite_block if multiples were stacked (and left over) */ for (prevzwrb = lvzwrite_block->prev; prevzwrb; lvzwrite_block = prevzwrb, prevzwrb = lvzwrite_block->prev) { if (lvzwrite_block->sub) free(lvzwrite_block->sub); free(lvzwrite_block); } } lvzwrite_block->zwr_intype = zwrpattyp; if (!merge_args && val) { /* val may be null when called from gtm_startup/gtm$startup */ MV_FORCE_STR(val); lvzwrite_block->pat = val; } else lvzwrite_block->pat = NULL; lvzwrite_block->mask = lvzwrite_block->subsc_count = 0; if (!lvzwrite_block->sub) lvzwrite_block->sub = (zwr_sub_lst *)malloc(SIZEOF(zwr_sub_lst) * MAX_LVSUBSCRIPTS); lvzwrite_block->fixed = TRUE; TREF(in_zwrite) = TRUE; return; } fis-gtm-V7.0-005/sr_port/lvzwr_key.c0000755000032200000250000000274714342376331016260 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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 "min_max.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "zwrite.h" #include "gtm_string.h" GBLREF lvzwrite_datablk *lvzwrite_block; unsigned char *lvzwr_key(unsigned char *buff, int size) { int sub_idx, len; mstr sub; unsigned char *cp, *cq; assert(lvzwrite_block); len = MIN(size, lvzwrite_block->curr_name->len); assert(MAX_MIDENT_LEN >= len); memcpy(buff, lvzwrite_block->curr_name->addr, len); size -= len; buff += len; if (lvzwrite_block->subsc_count) { if (size) { *buff++ = '('; size--; } for (sub_idx = 0; ; ) { mval_lex(((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[sub_idx].actual, &sub); if (0 <= (size -= sub.len)) { memcpy(buff, sub.addr, sub.len); buff += sub.len; } else break; if (++sub_idx < lvzwrite_block->curr_subsc && size) { *buff++ = ','; size--; } else { if (size) { *buff++ = ')'; size--; } break; } } } return buff; } fis-gtm-V7.0-005/sr_port/lvzwr_var.c0000644000032200000250000003015414342376331016246 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_stdio.h" #include "lv_val.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "zwrite.h" #include "mlkdef.h" #include "zshow.h" #include "collseq.h" #include "stringpool.h" #include "op.h" #include "deferred_events_queue.h" #include "do_xform.h" #include "numcmp.h" #include "patcode.h" #include "mvalconv.h" #include "follow.h" #include "gtm_string.h" #include "alias.h" #include "promodemo.h" /* for "demote" prototype used in LV_NODE_GET_KEY */ #include "jobsp.h" #define eb_less(u, v) (numcmp(u, v) < 0) #define COMMON_STR_PROCESSING(NODE) \ { \ mstr key_mstr; \ mval tmp_sbs; \ \ assert(MV_STR & mv.mvtype); \ if (TREF(local_collseq)) \ { \ key_mstr = mv.str; \ mv.str.len = 0; /* protect from "stp_gcol", if zwr_sub->subsc_list[n].actual points to mv */ \ ALLOC_XFORM_BUFF(key_mstr.len); \ tmp_sbs.mvtype = MV_STR; \ tmp_sbs.str.len = TREF(max_lcl_coll_xform_bufsiz); \ assert(NULL != TREF(lcl_coll_xform_buff)); \ tmp_sbs.str.addr = TREF(lcl_coll_xform_buff); \ do_xform(TREF(local_collseq), XBACK, &key_mstr, &tmp_sbs.str, &length); \ tmp_sbs.str.len = length; \ s2pool(&(tmp_sbs.str)); \ mv.str = tmp_sbs.str; \ } \ do_lev = TRUE; \ if (n < lvzwrite_block->subsc_count) \ { \ if (zwr_sub->subsc_list[n].subsc_type == ZWRITE_PATTERN) \ { \ if (!do_pattern(&mv, zwr_sub->subsc_list[n].first)) \ do_lev = FALSE; \ } else if (zwr_sub->subsc_list[n].subsc_type != ZWRITE_ALL) \ { \ if (zwr_sub->subsc_list[n].first) \ { \ if (!MV_IS_CANONICAL(zwr_sub->subsc_list[n].first) && \ (!follow(&mv, zwr_sub->subsc_list[n].first) && \ (mv.str.len != zwr_sub->subsc_list[n].first->str.len || \ memcmp(mv.str.addr, zwr_sub->subsc_list[n].first->str.addr, \ mv.str.len)))) \ do_lev = FALSE; \ } \ if (do_lev && zwr_sub->subsc_list[n].second) \ { \ if (MV_IS_CANONICAL(zwr_sub->subsc_list[n].second) || \ (!follow(zwr_sub->subsc_list[n].second, &mv) && \ (mv.str.len != zwr_sub->subsc_list[n].second->str.len || \ memcmp(mv.str.addr, \ zwr_sub->subsc_list[n].second->str.addr, \ mv.str.len)))) \ do_lev = FALSE; \ } \ } \ } \ if (do_lev) \ lvzwr_var((lv_val *)NODE, n + 1); \ } #define COMMON_NUMERIC_PROCESSING(NODE) \ { \ do_lev = TRUE; \ if (n < lvzwrite_block->subsc_count) \ { \ if (zwr_sub->subsc_list[n].subsc_type == ZWRITE_PATTERN) \ { \ if (!do_pattern(&mv, zwr_sub->subsc_list[n].first)) \ do_lev = FALSE; \ } else if (zwr_sub->subsc_list[n].subsc_type != ZWRITE_ALL) \ { \ if (zwr_sub->subsc_list[n].first) \ { \ if (!MV_IS_CANONICAL(zwr_sub->subsc_list[n].first) \ || eb_less(&mv, zwr_sub->subsc_list[n].first)) \ do_lev = FALSE; \ } \ if (do_lev && zwr_sub->subsc_list[n].second) \ { \ if (MV_IS_CANONICAL(zwr_sub->subsc_list[n].second) \ && eb_less(zwr_sub->subsc_list[n].second, &mv)) \ do_lev = FALSE; \ } \ } \ } \ if (do_lev) \ lvzwr_var((lv_val *)NODE, n + 1); \ } GBLREF bool undef_inhibit; GBLREF lvzwrite_datablk *lvzwrite_block; GBLREF int merge_args; GBLREF volatile int4 outofband; GBLREF zshow_out *zwr_output; GBLREF zwr_hash_table *zwrhtab; /* How we track aliases during zwrites */ LITREF mval literal_null; error_def(ERR_UNDEF); /* lv subscript usage notes: * 1. The sub field in lvzwrite_datablk is an array allocated at MAX_LVSUBSCRIPTS. * 2. The subscripts that appear at any given time are those for the current node being processed. * 3. Nodes are setup by lvzwr_arg(). * * Example - take the following nodes: * A(1,1)=10 * A(1,2)=20 * * The simplified processing that occurs is as follows: * 1. lvzwr_fini() sets curr_name which is the base var name (A) * 2. First level lvzwr_var is called with level (aka n) == 0 * 3. Since A has no value, nothing is printed. Notices that there are children so lvzwr_arg() * is called recursively with level 1. * 4. Sets up the level 1 subscript (key = 1). * 5. Since A(1) has no value, nothing is printed. Notices that there are children so lvzwr_arg() * is called recursively withe level 2. * 6. Sets up the level 2 subscript (key = 1). * 7. A(1,1) does have a value so lvzwr_out() is called to print the current key (from these * subscripts) and its value. * 8. No more subscripts at this level so pops back to level 1. * 9. There is another child at this level so calls lvzwr_arg() recursively with level 2. * 10. Replaces the level 2 subscript with the new key value (key = 2). * 11. A(1,2) does have a value so lvzwr_out() is called to print the current key. * 12. no more children at any level so everything pops back. */ void lvzwr_var(lv_val *lv, int4 n) { mval mv; int length; lv_val *var; char *top; int4 i; boolean_t do_lev, verify_hash_add, htent_added, value_printed_pending; zwr_sub_lst *zwr_sub; ht_ent_addr *tabent_addr; zwr_alias_var *zav, *newzav; lvTree *lvt; lvTreeNode *node, *nullsubsnode, *parent; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(lvzwrite_block); if (lv == zwr_output->out_var.lv.child) return; if (outofband && (sighup != outofband)) { /* if there is no output, sighup doesn't matter - must deal with that case at the point of output */ assert(TREF(in_zwrite)); /* in_zwrite indicates properly set up for zwrite: should clear */ TREF(in_zwrite) = FALSE; /* along with below 2 because async_action may not return */ lvzwrite_block->curr_subsc = lvzwrite_block->subsc_count = 0; async_action(FALSE); TREF(in_zwrite) = TRUE; /* in case we're back because async_action turns out to be ctrlc */ } lvzwrite_block->curr_subsc = n; zwr_sub = (zwr_sub_lst *)lvzwrite_block->sub; zwr_sub->subsc_list[n].actual = (mval *)NULL; /* Before we process this var, there are some special cases to check for first when * this is a base var (0 == lvzwrite_block->subsc_count) and the var is an alias. * * 1. Check if we have seen it before (the lvval is in the zwr_alias_var hash table), then we * need to process this var with lvzwr_out NOW and we will only be processing the base * var, not any of the subscripts. This is because all those subscripts (and the value * of the base var itself) have been dealt with previously when we first saw this * lvval. So in that case, call lvzwr_out() to output the association after which we are * done with this var. * 2. If we haven't seen it before, set a flag so we verify if the base var gets processed by * lvzwr_out or not (i.e. whether it has a value and the "subscript" or lack there of is * either wildcarded or whatever so that it actually gets dumped by lvzwr_out (see conditions * below). If not, then *we* need to add the lvval to the hash table to signify we have seen * it before so the proper associations to this alias var can be printed at a later time * when/if they are encountered. */ verify_hash_add = FALSE; /* By default we don't need to verify add */ value_printed_pending = FALSE; /* Force the "value_printed" flag on if TRUE */ zav = NULL; if (!merge_args && LV_IS_BASE_VAR(lv) && IS_ALIASLV(lv)) { assert(0 == n); /* Verify base var lv_val */ if (tabent_addr = (ht_ent_addr *)lookup_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lv)) { /* We've seen it before but check if it was actually printed at that point */ zav = (zwr_alias_var *)tabent_addr->value; assert(zav); if (zav->value_printed) { lvzwr_out(lv); ZWRITE_OUTPUT_HOOK(); lvzwrite_block->curr_subsc = lvzwrite_block->subsc_count = 0; return; } else value_printed_pending = TRUE; /* We will set value_printed flag true later */ } else verify_hash_add = TRUE; } if ((0 == lvzwrite_block->subsc_count) && (0 == n)) zwr_sub->subsc_list[n].subsc_type = ZWRITE_ASTERISK; if (LV_IS_VAL_DEFINED(lv) && (!lvzwrite_block->subsc_count || ((0 == n) && ZWRITE_ASTERISK == zwr_sub->subsc_list[n].subsc_type) || ((0 != n) && !(lvzwrite_block->mask >> n)))) { /* Print value for *this* node */ lvzwr_out(lv); ZWRITE_OUTPUT_HOOK(); } if (verify_hash_add && !lvzwrite_block->zav_added) { /* lvzwr_out processing didn't add a zav for this var. Take care of that now so we * recognize it as a "dealt with" alias when/if it is encountered later. */ newzav = als_getzavslot(); newzav->zwr_var = *lvzwrite_block->curr_name; newzav->value_printed = TRUE; htent_added = add_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lv, newzav, &tabent_addr); assert(htent_added); } /* If we processed a base var above to print an alias association but it hadn't been printed yet, * we had to wait until after lvzwr_out() was called before we could set the flag that indicated * the printing had occurred. Do that now. Note that it is only when this flag is set we are * certain to have a good value in zav. */ if (value_printed_pending) { assert(zav); zav->value_printed = TRUE; } if (lvzwrite_block->subsc_count && (n >= lvzwrite_block->subsc_count) && (ZWRITE_ASTERISK != zwr_sub->subsc_list[lvzwrite_block->subsc_count - 1].subsc_type)) return; if (n < lvzwrite_block->subsc_count && ZWRITE_VAL == zwr_sub->subsc_list[n].subsc_type) { var = op_srchindx(VARLSTCNT(2) lv, zwr_sub->subsc_list[n].first); zwr_sub->subsc_list[n].actual = zwr_sub->subsc_list[n].first; if (var && (LV_IS_VAL_DEFINED(var) || n < lvzwrite_block->subsc_count -1)) { lvzwr_var(var, n + 1); zwr_sub->subsc_list[n].actual = (mval *)NULL; lvzwrite_block->curr_subsc = n; } else { if (lvzwrite_block->fixed) { unsigned char buff[512], *end; lvzwrite_block->curr_subsc++; end = lvzwr_key(buff, SIZEOF(buff)); zwr_sub->subsc_list[n].actual = (mval *)NULL; lvzwrite_block->curr_subsc = lvzwrite_block->subsc_count = 0; if (!undef_inhibit) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_UNDEF, 2, end - buff, buff); } } } else if (lvt = LV_GET_CHILD(lv)) { /* If node has children, process them now */ zwr_sub->subsc_list[n].actual = &mv; /* In case of standard null collation, first process null subscript if it exists */ if (TREF(local_collseq_stdnull)) { nullsubsnode = lvAvlTreeLookupStr(lvt, (treeKeySubscr *)&literal_null, &parent); if (NULL != nullsubsnode) { assert(MVTYPE_IS_STRING(nullsubsnode->key_mvtype) && !nullsubsnode->key_len); /* Process null subscript first */ LV_STR_NODE_GET_KEY(nullsubsnode, &mv); /* Get node key into "mv" */ COMMON_STR_PROCESSING(nullsubsnode); } } else nullsubsnode = NULL; for (node = lvAvlTreeFirst(lvt); NULL != node; node = lvAvlTreeNext(node)) { if (node == nullsubsnode) { assert(TREF(local_collseq_stdnull)); continue; /* skip null subscript as it has already been processed */ } LV_NODE_GET_KEY(node, &mv); /* Get node key into "mv" depending on the structure type of "node" */ if (!MVTYPE_IS_STRING(mv.mvtype)) { /* "node" is of type "lvTreeNodeNum *" */ COMMON_NUMERIC_PROCESSING(node); } else { /* "node" is of type "lvTreeNode *" */ COMMON_STR_PROCESSING(node); } } zwr_sub->subsc_list[n].actual = (mval *)NULL; lvzwrite_block->curr_subsc = n; } } fis-gtm-V7.0-005/sr_port/m_break.c0000755000032200000250000000173514342376331015620 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "cmd.h" #include "start_fetches.h" int m_break(void) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TK_SPACE != TREF(window_token)) && (TK_EOL != TREF(window_token))) if (!m_xecute()) return FALSE; newtriple(OC_BREAK); /* Because the activity in direct mode brought on by the BREAK can alter the environment, start a new fetch */ MID_LINE_REFETCH; return TRUE; } fis-gtm-V7.0-005/sr_port/m_close.c0000755000032200000250000000350114342376331015632 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "io_params.h" #include "indir_enum.h" #include "advancewindow.h" #include "cmd.h" #include "deviceparameters.h" int m_close(void) { static readonly unsigned char empty_plist[1] = { iop_eol }; boolean_t inddevparms; int rval; oprtype devpopr, plist, sopr; triple *indref, *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; inddevparms = FALSE; if (EXPR_FAIL == (rval = expr(&sopr, MUMPS_STR))) /* NOTE assignment */ return FALSE; if (TK_COLON != TREF(window_token)) { /* Single parameter */ if (EXPR_INDR == rval) { /* Indirect entire parameter list */ make_commarg(&sopr, indir_close); return TRUE; } else /* default device parms */ plist = put_str((char *)empty_plist, SIZEOF(empty_plist)); } else { /* Have device parms. Determine type */ advancewindow(); if (TK_ATSIGN == TREF(window_token)) { /* Have indirect device parms */ if (!indirection(&devpopr)) return FALSE; indref = newtriple(OC_INDDEVPARMS); indref->operand[0] = devpopr; indref->operand[1] = put_ilit(IOP_CLOSE_OK); inddevparms = TRUE; } else { /* Process device parameters now */ if (!deviceparameters(&plist, IOP_CLOSE_OK)) return FALSE; } } ref = newtriple(OC_CLOSE); ref->operand[0] = sopr; ref->operand[1] = !inddevparms ? plist : put_tref(indref); return TRUE; } fis-gtm-V7.0-005/sr_port/m_do.c0000755000032200000250000002114014342376331015126 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "mdq.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mmemory.h" #include "advancewindow.h" #include "cmd.h" GBLREF boolean_t run_time; GBLREF mline *mline_tail; error_def(ERR_ACTOFFSET); int m_do(void) /* compiler module for a DO command */ { int opcd; oprtype *cr; triple *calltrip, *labelref, *obp, *oldchain, *ref0, *ref1, *routineref, tmpchain, *triptr; # ifndef __i386 triple *tripsize; # endif mval *v; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TK_SPACE == TREF(window_token)) || (TK_EOL == TREF(window_token))) { if (!run_time) /* DO SP SP is a noop at run time */ { calltrip = newtriple(OC_CALLSP); calltrip->operand[0] = put_mnxl(); mline_tail->block_ok = TRUE; # ifndef __i386 calltrip->operand[1] = put_ocnt(); # endif } return TRUE; } else if (TK_AMPERSAND == TREF(window_token)) { if (!extern_func(0)) return FALSE; else return TRUE; } dqinit(&tmpchain, exorder); oldchain = setcurtchain(&tmpchain); calltrip = entryref(OC_CALL, OC_EXTCALL, (mint)indir_do, TRUE, FALSE, FALSE); setcurtchain(oldchain); if (!calltrip) return FALSE; if (TK_LPAREN == TREF(window_token)) { if (OC_CALL == calltrip->opcode) { assert(MLAB_REF == calltrip->operand[0].oprclass); calltrip->opcode = OC_EXCAL; # ifdef __i386 ref0 = calltrip; # else ref0 = newtriple(OC_PARAMETER); calltrip->operand[1] = put_tref(ref0); ref0->operand[0] = put_tsiz(); /* parm to hold size of jump codegen */ tripsize = ref0->operand[0].oprval.tref; assert(OC_TRIPSIZE == tripsize->opcode); # endif } else { if (OC_EXTCALL == calltrip->opcode) { assert(TRIP_REF == calltrip->operand[1].oprclass); # ifdef __i386 if (OC_CDLIT == calltrip->operand[1].oprval.tref->opcode) { assert(CDLT_REF == calltrip->operand[1].oprval.tref->operand[0].oprclass); # else opcd = calltrip->operand[1].oprval.tref->opcode; if ((OC_CDLIT == opcd) || (OC_CDIDX == opcd)) { DEBUG_ONLY(opcd = calltrip->operand[1].oprval.tref->operand[0].oprclass); assert((CDLT_REF == opcd) || (CDIDX_REF == opcd)); # endif } else { assert(OC_LABADDR == calltrip->operand[1].oprval.tref->opcode); assert(TRIP_REF == calltrip->operand[1].oprval.tref->operand[1].oprclass); assert(OC_PARAMETER == calltrip->operand[1].oprval.tref->operand[1].oprval.tref->opcode); assert(TRIP_REF == calltrip->operand[1].oprval.tref->operand[1].oprval.tref->operand[0].oprclass); DEBUG_ONLY(opcd = calltrip->operand[1].oprval.tref->operand[1].oprval.tref-> operand[0].oprval.tref->opcode); assert((OC_ILIT == opcd) || (OC_COMINT == opcd)); DEBUG_ONLY(opcd = calltrip->operand[1].oprval.tref->operand[1].oprval.tref-> operand[0].oprval.tref->operand[0].oprclass); assert((ILIT_REF == opcd) || (TRIP_REF == opcd)); /* The opcd references above added to allow an invalid syntax using indirect values for * offsets while specifying a parm list to get through the above asserts (invalid syntax * should not trip asserts) but it leads to the conclusion that the below test may not be * robust enough since it is looking at a literal integer value when there is none so have * added further checks mirroring the first checks done in the two most recent asserts to * make the check more robust. [Example bad code: Do @lbl+@n^artn(arg)] */ if ((0 != calltrip->operand[1].oprval.tref->operand[1].oprval.tref-> operand[0].oprval.tref->operand[0].oprval.ilit) || (OC_ILIT != calltrip->operand[1].oprval.tref->operand[1].oprval.tref-> operand[0].oprval.tref->opcode) || (ILIT_REF != calltrip->operand[1].oprval.tref->operand[1].oprval.tref-> operand[0].oprval.tref->operand[0].oprclass)) { stx_error(ERR_ACTOFFSET); return FALSE; } } } else { /* DO _ @dlabel actuallist */ assert(OC_COMMARG == calltrip->opcode); assert(TRIP_REF == calltrip->operand[1].oprclass); assert(OC_ILIT == calltrip->operand[1].oprval.tref->opcode); assert(ILIT_REF == calltrip->operand[1].oprval.tref->operand[0].oprclass); assert((mint)indir_do == calltrip->operand[1].oprval.tref->operand[0].oprval.ilit); assert(calltrip->exorder.fl == &tmpchain); routineref = maketriple(OC_CURRHD); labelref = maketriple(OC_LABADDR); ref0 = maketriple(OC_PARAMETER); dqins(calltrip->exorder.bl, exorder, routineref); dqins(calltrip->exorder.bl, exorder, labelref); dqins(calltrip->exorder.bl, exorder, ref0); labelref->operand[0] = calltrip->operand[0]; labelref->operand[1] = put_tref(ref0); ref0->operand[0] = calltrip->operand[1]; ref0->operand[0].oprval.tref->operand[0].oprval.ilit = 0; ref0->operand[1] = put_tref(routineref); calltrip->operand[0] = put_tref(routineref); calltrip->operand[1] = put_tref(labelref); } calltrip->opcode = OC_EXTEXCAL; ref0 = newtriple(OC_PARAMETER); ref0->operand[0] = calltrip->operand[1]; calltrip->operand[1] = put_tref(ref0); } if (!actuallist(&ref0->operand[1])) return FALSE; } else if (OC_CALL == calltrip->opcode) { # ifndef __i386 calltrip->operand[1] = put_ocnt(); # endif if (TREF(for_stack_ptr) != (oprtype **)TADR(for_stack)) { if (TAREF1(for_temps, (TREF(for_stack_ptr) - (oprtype **)TADR(for_stack)))) calltrip->opcode = OC_FORLCLDO; } } if (TK_COLON == TREF(window_token)) { advancewindow(); cr = (oprtype *)mcalloc(SIZEOF(oprtype)); if (!bool_expr(FALSE, cr)) return FALSE; for (triptr = (TREF(curtchain))->exorder.bl; OC_NOOP == triptr->opcode; triptr = triptr->exorder.bl) ; if (OC_LIT == triptr->opcode) { /* it's a literal so optimize it */ v = &triptr->operand[0].oprval.mlit->v; unuse_literal(v); dqdel(triptr, exorder); if (0 == MV_FORCE_BOOL(v)) { setcurtchain(oldchain); /* it's a FALSE so just discard the whole thing */ # ifndef __i386 if (OC_EXCAL == calltrip->opcode) tripsize->opcode = OC_NOOP; /* if we are abandoning this DO, clear this too */ # endif return TRUE; } /* the code below is the same as for the no postconditional case */ obp = oldchain->exorder.bl; /* it's a TRUE - just pretend it isn't even there */ dqadd(obp, &tmpchain, exorder); /* this is a violation of info hiding */ if (OC_EXCAL == calltrip->opcode) { /* this code is the same as below for no condition */ triptr = newtriple(OC_JMP); triptr->operand[0] = put_mfun(&calltrip->operand[0].oprval.lab->mvname); calltrip->operand[0].oprclass = ILIT_REF; /* dummy placeholder */ # ifndef __i386 tripsize->operand[0].oprval.tsize->ct = triptr; # endif } return TRUE; } if ((TREF(expr_start) != TREF(expr_start_orig)) && (OC_NOOP != (TREF(expr_start))->opcode)) { triptr = newtriple(OC_GVRECTARG); triptr->operand[0] = put_tref(TREF(expr_start)); } obp = oldchain->exorder.bl; dqadd(obp, &tmpchain, exorder); /* this is a violation of info hiding */ if (OC_EXCAL == calltrip->opcode) { triptr = newtriple(OC_JMP); triptr->operand[0] = put_mfun(&calltrip->operand[0].oprval.lab->mvname); calltrip->operand[0].oprclass = ILIT_REF; /* dummy placeholder */ # ifndef __i386 tripsize->operand[0].oprval.tsize->ct = triptr; # endif } if ((TREF(expr_start) != TREF(expr_start_orig)) && (OC_NOOP != (TREF(expr_start))->opcode)) { ref0 = newtriple(OC_JMP); ref1 = newtriple(OC_GVRECTARG); ref1->operand[0] = put_tref(TREF(expr_start)); *cr = put_tjmp(ref1); tnxtarg(&ref0->operand[0]); } else tnxtarg(cr); } else { /* the code below is the same as for the case of a postconditional known at compile time to be TRUE */ obp = oldchain->exorder.bl; dqadd(obp, &tmpchain, exorder); /* this is a violation of info hiding */ if (OC_EXCAL == calltrip->opcode) { triptr = newtriple(OC_JMP); triptr->operand[0] = put_mfun(&calltrip->operand[0].oprval.lab->mvname); calltrip->operand[0].oprclass = ILIT_REF; /* dummy placeholder */ # ifndef __i386 tripsize->operand[0].oprval.tsize->ct = triptr; # endif } } return TRUE; } fis-gtm-V7.0-005/sr_port/m_else.c0000755000032200000250000000207314342376331015460 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "mmemory.h" #include "cmd.h" error_def(ERR_SPOREOL); int m_else(void) { triple *jmpref, elsepos_in_chain; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; elsepos_in_chain = TREF(pos_in_chain); if (TK_EOL == TREF(window_token)) return TRUE; if (TK_SPACE != TREF(window_token)) { stx_error(ERR_SPOREOL); return FALSE; } jmpref = newtriple(OC_JMPTSET); FOR_END_OF_SCOPE(0, jmpref->operand[0]); if (!linetail()) { tnxtarg(&jmpref->operand[0]); TREF(pos_in_chain) = elsepos_in_chain; return FALSE; } else return TRUE; } fis-gtm-V7.0-005/sr_port/m_for.c0000755000032200000250000003174114342376331015322 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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 "compiler.h" #include "mdq.h" #include "opcode.h" #include "toktyp.h" #include "mmemory.h" #include "advancewindow.h" #include "cmd.h" #include "lv_val.h" error_def(ERR_EQUAL); error_def(ERR_FOROFLOW); error_def(ERR_MAXFORARGS); error_def(ERR_SPOREOL); /* The following macro checks to see if the evaluation of control variable components has done * anything that might have expose us to a messed up the control variable context. We only * have a problem when the control variable is subscripted, because if an extrinsic rearranges * the array - a KILL will do it - the op_putindx we did initially might be pointing into * never-neverland and slamming a value into it would definately not be a healthly thing. * Without indirection we know at compile time whether or not the control variable is subscripted * but with indirection we only know at run-time; we tried some contortions to skip the refresh if * it's not needed but lost the battle with the compiler's tendency to lose reference with a scope * that's not short - OC_PASSTHRU is suppose to give it a clue but having two of those in a row * seems not to work. */ #define DEAL_WITH_DANGER(CNTRL_LVN, CNTL_VAR, VAL) \ { \ triple *REF; \ \ if (need_control_rfrsh) \ { \ REF = newtriple(OC_RFRSHLVN); \ REF->operand[0] = CNTRL_LVN; \ REF->operand[1] = put_ilit(OC_PUTINDX); \ CNTL_VAR = put_tref(REF); \ newtriple(OC_PASSTHRU)->operand[0] = CNTRL_LVN; \ newtriple(OC_PASSTHRU)->operand[0] = CNTL_VAR; /* warn off optimizer */ \ } \ REF = newtriple(OC_STO); \ REF->operand[0] = CNTL_VAR; \ REF->operand[1] = VAL; \ } /* the macro below pushes the compiler FOR stack - the FOR_POP is in compiler.h 'cause stx_error uses it * there are actually two stacks - one for code references and one for temps flags; the code reference * one, for_stack, uses for_stack_ptr; the for_temps doesn't have its own global index, but instead uses * a local variable calculated from the relationship between the for_stack and for_stack_ptr */ #define FOR_PUSH() \ { \ int Level; \ \ Level = ((++(TREF(for_stack_ptr))) - (oprtype **)TADR(for_stack)); \ if (MAX_FOR_STACK > Level) \ { \ assert(TREF(for_stack_ptr) > (oprtype **)TADR(for_stack)); \ *(TREF(for_stack_ptr)) = NULL; \ TAREF1(for_temps, Level) = TAREF1(for_temps, Level - 1); \ } else \ { \ --(TREF(for_stack_ptr)); \ stx_error(ERR_FOROFLOW, 1, (MAX_FOR_STACK - 1)); \ FOR_POP(BLOWN_FOR); \ return FALSE; \ } \ } /* the macro below tucks a code reference into the for_stack so a FOR that's done can move on correctly when skipped */ #define SAVE_FOR_OVER_ADDR() \ { \ assert(TREF(for_stack_ptr) >= (oprtype **)TADR(for_stack)); \ assert(TREF(for_stack_ptr) < (oprtype **)(TADR(for_stack) + (MAX_FOR_STACK * SIZEOF(oprtype **)))); \ if (NULL == *(TREF(for_stack_ptr))) \ *(TREF(for_stack_ptr)) = (oprtype *)mcalloc(SIZEOF(oprtype)); \ tnxtarg(*(TREF(for_stack_ptr))); \ } int m_for(void) { unsigned int arg_cnt, arg_index, for_stack_level; oprtype arg_eval_addr[MAX_FORARGS], increment[MAX_FORARGS], terminate[MAX_FORARGS], arg_next_addr, arg_value, dummy, control_variable, control_slot, v, *iteration_start_addr, iteration_start_addr_indr, *not_even_once_addr; triple *eval_next_addr[MAX_FORARGS], *control_ref, *forchk1opc, forpos_in_chain, *init_ref, *push, *ref, *s, *sav, *share, *step_ref, *term_ref, *var_ref; boolean_t need_control_rfrsh = FALSE; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; forpos_in_chain = TREF(pos_in_chain); FOR_PUSH(); if (TK_SPACE == TREF(window_token)) { /* "argumentless" form */ FOR_END_OF_SCOPE(1, dummy); ref = newtriple(OC_FORCHK1); if (!linetail()) { TREF(pos_in_chain) = forpos_in_chain; assert(TREF(source_error_found)); stx_error(TREF(source_error_found)); FOR_POP(BLOWN_FOR); return FALSE; } SAVE_FOR_OVER_ADDR(); /* stash address of next op in the for_stack array */ newtriple(OC_JMP)->operand[0] = put_tjmp(ref); /* transfer back to just before the begining of the body */ FOR_POP(GOOD_FOR); /* and pop the array */ return TRUE; } for_stack_level = (TREF(for_stack_ptr) - TADR(for_stack)); if (TK_ATSIGN == TREF(window_token)) { if (!indirection(&v)) { FOR_POP(BLOWN_FOR); return FALSE; } need_control_rfrsh = TRUE; push = newtriple(OC_GLVNSLOT); push->operand[0] = put_ilit(for_stack_level); control_slot = put_tref(push); sav = newtriple(OC_INDSAVLVN); sav->operand[0] = v; sav->operand[1] = control_slot; } else { control_ref = (TREF(curtchain))->exorder.bl; if (!lvn(&control_variable, OC_SAVLVN, NULL)) { FOR_POP(BLOWN_FOR); return FALSE; } s = control_variable.oprval.tref; if (OC_SAVLVN == s->opcode) { /* Control variable has subscripts. If no subscripts, shouldn't need refreshing. */ need_control_rfrsh = TRUE; push = maketriple(OC_GLVNSLOT); push->operand[0] = put_ilit(for_stack_level); control_slot = put_tref(push); share = maketriple(OC_SHARESLOT); share->operand[0] = put_tref(push); share->operand[1] = put_ilit(OC_SAVLVN); dqins(s->exorder.bl, exorder, share); dqins(share->exorder.bl, exorder, push); } if (OC_VAR != control_ref->exorder.fl->opcode) { FOR_POP(BLOWN_FOR); return FALSE; } assert(MVAR_REF == control_ref->exorder.fl->operand[0].oprclass); } if (TK_EQUAL != TREF(window_token)) { stx_error(ERR_EQUAL); FOR_POP(BLOWN_FOR); return FALSE; } if (need_control_rfrsh) { ref = newtriple(OC_RFRSHLVN); ref->operand[0] = control_slot; ref->operand[1] = put_ilit(OC_PUTINDX); control_variable = put_tref(ref); TAREF1(for_temps, for_stack_level) = TRUE; newtriple(OC_PASSTHRU)->operand[0] = control_slot; } newtriple(OC_PASSTHRU)->operand[0] = control_variable; /* make sure optimizer doesn't ditch control_variable */ FOR_END_OF_SCOPE(1, dummy); assert((0 < for_stack_level) && (MAX_FOR_STACK >= for_stack_level)); iteration_start_addr = (oprtype *)mcalloc(SIZEOF(oprtype)); iteration_start_addr_indr = put_indr(iteration_start_addr); arg_next_addr.oprclass = NO_REF; not_even_once_addr = NULL; /* used to skip processing where the initial control exceeds the termination */ for (arg_cnt = 0; ; ++arg_cnt) { if (MAX_FORARGS <= arg_cnt) { stx_error(ERR_MAXFORARGS); FOR_POP(BLOWN_FOR); return FALSE; } assert((TK_COMMA == TREF(window_token)) || (TK_EQUAL == TREF(window_token))); advancewindow(); tnxtarg(&arg_eval_addr[arg_cnt]); /* put location of this arg eval in arg_eval_addr array */ if (NULL != not_even_once_addr) { *not_even_once_addr = arg_eval_addr[arg_cnt]; not_even_once_addr = NULL; } if (EXPR_FAIL == expr(&arg_value, MUMPS_EXPR)) /* starting (possibly only) value */ { FOR_POP(BLOWN_FOR); return FALSE; } assert(TRIP_REF == arg_value.oprclass); if (TK_COLON != TREF(window_token)) { /* list point value? */ increment[arg_cnt].oprclass = terminate[arg_cnt].oprclass = NO_REF; DEAL_WITH_DANGER(control_slot, control_variable, arg_value); } else { /* stepping value */ init_ref = newtriple(OC_STOTEMP); /* tuck it in a temp undisturbed by coming evals */ init_ref->operand[0] = arg_value; newtriple(OC_CONUM)->operand[0] = put_tref(init_ref); /* make start numeric */ advancewindow(); /* past the first colon */ var_ref = (TREF(curtchain))->exorder.bl; if (EXPR_FAIL == expr(&increment[arg_cnt], MUMPS_EXPR)) /* pick up step */ { FOR_POP(BLOWN_FOR); return FALSE; } assert(TRIP_REF == increment[arg_cnt].oprclass); ref = increment[arg_cnt].oprval.tref; if (OC_LIT != var_ref->exorder.fl->opcode) { TAREF1(for_temps, for_stack_level) = TRUE; if (OC_VAR == var_ref->exorder.fl->opcode) { /* The above relies on lvn() always generating an OC_VAR triple first - asserted earlier */ step_ref = newtriple(OC_STOTEMP); step_ref->operand[0] = put_tref(ref); increment[arg_cnt] = put_tref(step_ref); } } if (TK_COLON != TREF(window_token)) { DEAL_WITH_DANGER(control_slot, control_variable, put_tref(init_ref)); terminate[arg_cnt].oprclass = NO_REF; /* no termination on iteration for this arg */ } else { advancewindow(); /* past the second colon */ var_ref = (TREF(curtchain))->exorder.bl; if (EXPR_FAIL == expr(&terminate[arg_cnt], MUMPS_EXPR)) /* termination control value */ { FOR_POP(BLOWN_FOR); return FALSE; } assert(TRIP_REF == terminate[arg_cnt].oprclass); ref = terminate[arg_cnt].oprval.tref; if (OC_LIT != ref->opcode) { TAREF1(for_temps, for_stack_level) = TRUE; if (OC_VAR == var_ref->exorder.fl->opcode) { /* The above relies on lvn() always generating an OC_VAR triple first */ term_ref = newtriple(OC_STOTEMP); term_ref->operand[0] = put_tref(ref); terminate[arg_cnt] = put_tref(term_ref); } } DEAL_WITH_DANGER(control_slot, control_variable, put_tref(init_ref)); term_ref = newtriple(OC_PARAMETER); term_ref->operand[0] = terminate[arg_cnt]; step_ref = newtriple(OC_PARAMETER); step_ref->operand[0] = increment[arg_cnt]; step_ref->operand[1] = put_tref(term_ref); ref = newtriple(OC_FORINIT); ref->operand[0] = control_variable; ref->operand[1] = put_tref(step_ref); not_even_once_addr = newtriple(OC_JMPGTR)->operand; } } if ((0 < arg_cnt) || (TK_COMMA == TREF(window_token))) { TAREF1(for_temps, for_stack_level) = TRUE; if (NO_REF == arg_next_addr.oprclass) arg_next_addr = put_tref(newtriple(OC_CDADDR)); (eval_next_addr[arg_cnt] = newtriple(OC_LDADDR))->destination = arg_next_addr; } if (TK_COMMA != TREF(window_token)) break; newtriple(OC_JMP)->operand[0] = iteration_start_addr_indr; } forchk1opc = newtriple(OC_FORCHK1); /* FORCHK1 is a do-nothing routine used by the out-of-band mechanism */ *iteration_start_addr = put_tjmp(forchk1opc); if ((TK_EOL != TREF(window_token)) && (TK_SPACE != TREF(window_token))) { stx_error(ERR_SPOREOL); FOR_POP(BLOWN_FOR); return FALSE; } if (!linetail()) { TREF(pos_in_chain) = forpos_in_chain; assert(TREF(source_error_found)); stx_error(TREF(source_error_found)); FOR_POP(BLOWN_FOR); return FALSE; } if (not_even_once_addr) /* if above errors leave FOR remains behind, improper operval.indr explodes OC_JMPGTR */ FOR_END_OF_SCOPE(1, *not_even_once_addr); /* 1 means down a level */ SAVE_FOR_OVER_ADDR(); /* stash address of next op in the for_stack array */ if (0 < arg_cnt) newtriple(OC_JMPAT)->operand[0] = put_tref(eval_next_addr[0]); for (arg_index = 0; arg_index <= arg_cnt; ++arg_index) { if (0 < arg_cnt) tnxtarg(eval_next_addr[arg_index]->operand); if (need_control_rfrsh) { /* since it might have moved, before touching the control variable get a fix on it */ ref = newtriple(OC_RFRSHLVN); ref->operand[0] = control_slot; if (increment[arg_index].oprclass || terminate[arg_index].oprclass) ref->operand[1] = put_ilit(OC_SRCHINDX); else /* if increment rather than new value, rfrsh w/ srchindx else putindx */ ref->operand[1] = put_ilit(OC_PUTINDX); newtriple(OC_PASSTHRU)->operand[0] = control_slot; control_variable = put_tref(ref); } newtriple(OC_PASSTHRU)->operand[0] = control_variable; /* warn off optimizer */ if (terminate[arg_index].oprclass) { term_ref = newtriple(OC_PARAMETER); term_ref->operand[0] = terminate[arg_index]; step_ref = newtriple(OC_PARAMETER); step_ref->operand[0] = increment[arg_index]; step_ref->operand[1] = put_tref(term_ref); init_ref = newtriple(OC_PARAMETER); init_ref->operand[0] = control_variable; init_ref->operand[1] = put_tref(step_ref); ref = newtriple(OC_FORLOOP); /* redirects back to forchk1, which is at the beginning of new iteration */ ref->operand[0] = *iteration_start_addr; ref->operand[1] = put_tref(init_ref); } else if (increment[arg_index].oprclass) { step_ref = newtriple(OC_ADD); step_ref->operand[0] = control_variable; step_ref->operand[1] = increment[arg_index]; ref = newtriple(OC_STO); ref->operand[0] = control_variable; ref->operand[1] = put_tref(step_ref); newtriple(OC_JMP)->operand[0] = *iteration_start_addr; } if (arg_index < arg_cnt) /* go back and evaluate the next argument */ newtriple(OC_JMP)->operand[0] = arg_eval_addr[arg_index + 1]; } FOR_POP(GOOD_FOR); return TRUE; } fis-gtm-V7.0-005/sr_port/m_goto.c0000755000032200000250000000252114342376331015476 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "mdq.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mmemory.h" #include "advancewindow.h" #include "cmd.h" int m_goto(void) /* compiler module for (ugh!) GOTO */ { triple *obp, *oldchain, tmpchain; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TREF(xecute_literal_parse)) return FALSE; dqinit(&tmpchain, exorder); oldchain = setcurtchain(&tmpchain); if (!entryref(OC_JMP, OC_EXTJMP, (mint)indir_goto, TRUE, FALSE, FALSE)) { setcurtchain(oldchain); return FALSE; } setcurtchain(oldchain); if (TK_COLON == TREF(window_token)) return m_goto_postcond(oldchain, &tmpchain); /* post conditional expression */ obp = oldchain->exorder.bl; dqadd(obp, &tmpchain, exorder); /*this violates info hiding*/ return TRUE; } fis-gtm-V7.0-005/sr_port/m_goto_postcond.c0000644000032200000250000000442014342376331017404 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2016-2018 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 "mdq.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mmemory.h" #include "advancewindow.h" #include "cmd.h" int m_goto_postcond(triple *oldchain, triple *tmpchain) /* process a postconditional for m_goto and m_zgoto */ { oprtype *cr; triple *obp, *ref0, *ref1, *triptr; mval *v; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; advancewindow(); cr = (oprtype *)mcalloc(SIZEOF(oprtype)); if (!bool_expr(FALSE, cr)) return FALSE; for (triptr = (TREF(curtchain))->exorder.bl; OC_NOOP == triptr->opcode; triptr = triptr->exorder.bl) ; if (OC_LIT == triptr->opcode) { /* it's a literal so optimize it */ v = &triptr->operand[0].oprval.mlit->v; unuse_literal(v); dqdel(triptr, exorder); if (0 == MV_FORCE_BOOL(v)) setcurtchain(oldchain); /* it's a FALSE so just discard the argument */ else { /* it's TRUE so treat as if there was no argument postconditional */ while (TK_EOL != TREF(window_token)) /* but first discard the rest of the line - it's dead */ advancewindow(); obp = oldchain->exorder.bl; dqadd(obp, tmpchain, exorder); /* this is a violation of info hiding */ } return TRUE; } if ((TREF(expr_start) != TREF(expr_start_orig)) && (OC_NOOP != (TREF(expr_start))->opcode)) { triptr = newtriple(OC_GVRECTARG); triptr->operand[0] = put_tref(TREF(expr_start)); } obp = oldchain->exorder.bl; dqadd(obp, tmpchain, exorder); /* this is a violation of info hiding */ if ((TREF(expr_start) != TREF(expr_start_orig)) && (OC_NOOP != (TREF(expr_start))->opcode)) { ref0 = newtriple(OC_JMP); ref1 = newtriple(OC_GVRECTARG); ref1->operand[0] = put_tref(TREF(expr_start)); *cr = put_tjmp(ref1); tnxtarg(&ref0->operand[0]); } else tnxtarg(cr); return TRUE; } fis-gtm-V7.0-005/sr_port/m_halt.c0000755000032200000250000000153014342376331015455 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "cmd.h" int m_halt(void) { triple *triptr; triptr = newtriple(OC_ZHALT); triptr->operand[0] = put_ilit(0); /* flag as HALT rather than ZHALT */ triptr->operand[1] = put_ilit(0); /* return from HALT is always "success" */ return TRUE; } fis-gtm-V7.0-005/sr_port/m_hang.c0000755000032200000250000000175514342376331015453 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "cmd.h" int m_hang(void) { oprtype ot; triple *triptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; switch (expr(&ot, MUMPS_NUM)) { case EXPR_FAIL: return FALSE; case EXPR_GOOD: triptr = newtriple(OC_HANG); triptr->operand[0] = ot; return TRUE; case EXPR_INDR: make_commarg(&ot, indir_hang); return TRUE; default: GTMASSERT; } return FALSE; /* This should never get executed, added to make compiler happy */ } fis-gtm-V7.0-005/sr_port/m_hcmd.c0000755000032200000250000000133314342376331015441 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "cmd.h" int m_hcmd(void) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TK_SPACE == TREF(window_token)) || (TK_EOL == TREF(window_token))) return m_halt(); return m_hang(); } fis-gtm-V7.0-005/sr_port/m_if.c0000755000032200000250000001337614342376331015136 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mdq.h" #include "mmemory.h" #include "advancewindow.h" #include "cmd.h" #include "fullbool.h" GBLREF int4 pending_errtriplecode; error_def(ERR_INDEXTRACHARS); error_def(ERR_SPOREOL); typedef struct jmpchntype { struct { struct jmpchntype *fl,*bl; } link; triple *jmptrip; } jmpchn; int m_if(void) /* compiler module for IF */ { boolean_t first_time, is_commarg, t_set; int sense; jmpchn *jmpchain, *nxtjmp; oprtype *ta_opr, x, y; triple ifpos_in_chain, *jmpref, *ref1, *ref2, *oldchain, tmpchain, *triptr; mval *v; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ifpos_in_chain = TREF(pos_in_chain); jmpchain = (jmpchn *)mcalloc(SIZEOF(jmpchn)); dqinit(jmpchain, link); if (TK_EOL == TREF(window_token)) return TRUE; is_commarg = (1 == TREF(last_source_column)); FOR_END_OF_SCOPE(0, x); assert(INDR_REF == x.oprclass); oldchain = NULL; if (TK_SPACE == TREF(window_token)) { jmpref = newtriple(OC_JMPTCLR); jmpref->operand[0] = x; nxtjmp = (jmpchn *)mcalloc(SIZEOF(jmpchn)); nxtjmp->jmptrip = jmpref; dqins(jmpchain, link, nxtjmp); } else { first_time = TRUE; for (;;) { ta_opr = (oprtype *)mcalloc(SIZEOF(oprtype)); if (!bool_expr(TRUE, ta_opr)) { if (NULL != oldchain) setcurtchain(oldchain); /* reset from discard chain */ return FALSE; } for (triptr = (TREF(curtchain))->exorder.bl; OC_NOOP == triptr->opcode; triptr = triptr->exorder.bl) ; if ((OC_JMPNEQ == triptr->opcode) /* WARNING: assignments */ && (OC_COBOOL == (ref1 = triptr->exorder.bl)->opcode) && (OC_INDGLVN == (ref2 = ref1->exorder.bl)->opcode)) { /* short-circuit only optimization that turns a trailing INDGLVN COBOOL into separate indirect IF */ triptr->opcode = OC_JMPTSET; triptr->operand[0] = put_indr(ta_opr); dqdel(ref1, exorder);; ref2->opcode = OC_COMMARG; ref2->operand[1] = put_ilit((mint)indir_if); } if (OC_LIT == triptr->opcode) { /* it is a literal so we optimize it */ dqdel(triptr, exorder); v = &triptr->operand[0].oprval.mlit->v; unuse_literal(v); if (t_set = (0 == MV_FORCE_BOOL(v))) /* WARNING: assignment */ { /* it's FALSE, insert clear of $TEST */ newtriple(OC_CLRTEST); if (TK_SPACE == TREF(director_token)) /* if there are trailing spaces */ while (TK_SPACE == TREF(director_token)) /* eat them up */ advancewindow(); if (TK_EOL == TREF(director_token)) break; /* line empty: no discard needed */ if (NULL == oldchain) { /* not already discarding, so get ready to discard the rest of the line */ dqinit(&tmpchain, exorder); oldchain = setcurtchain(&tmpchain); } } else { /* it's TRUE so insert set of $TEST and step beyond the argument */ newtriple(OC_SETTEST); if (TK_COMMA != TREF(window_token)) break; advancewindow(); continue; /* leave first_time in case next arg is also a literal */ } } else t_set = (OC_JMPTSET == (TREF(curtchain))->exorder.bl->opcode); if (!t_set) newtriple(OC_CLRTEST); if (TREF(expr_start) != TREF(expr_start_orig) && (OC_NOOP != (TREF(expr_start))->opcode)) { assert((OC_GVSAVTARG == (TREF(expr_start))->opcode)); if ((OC_GVRECTARG != (TREF(curtchain))->exorder.bl->opcode) || ((TREF(curtchain))->exorder.bl->operand[0].oprval.tref != TREF(expr_start))) newtriple(OC_GVRECTARG)->operand[0] = put_tref(TREF(expr_start)); } jmpref = newtriple(OC_JMP); jmpref->operand[0] = x; nxtjmp = (jmpchn *)mcalloc(SIZEOF(jmpchn)); nxtjmp->jmptrip = jmpref; dqins(jmpchain, link, nxtjmp); tnxtarg(ta_opr); if (first_time) { if (!t_set) newtriple(OC_SETTEST); if (TREF(expr_start) != TREF(expr_start_orig) && (OC_NOOP != (TREF(expr_start))->opcode)) { assert((OC_GVSAVTARG == (TREF(expr_start))->opcode)); if ((OC_GVRECTARG != (TREF(curtchain))->exorder.bl->opcode) || ((TREF(curtchain))->exorder.bl->operand[0].oprval.tref != TREF(expr_start))) newtriple(OC_GVRECTARG)->operand[0] = put_tref(TREF(expr_start)); } first_time = FALSE; } if (TK_COMMA != TREF(window_token)) break; advancewindow(); } } if (is_commarg) { while (TK_SPACE == TREF(window_token)) /* Eat up trailing white space */ advancewindow(); if (NULL != oldchain) setcurtchain(oldchain); /* reset from discard chain */ if (TK_EOL != TREF(window_token)) { stx_error(ERR_INDEXTRACHARS); return FALSE; } return TRUE; } if ((TK_EOL != TREF(window_token)) && (TK_SPACE != TREF(window_token))) { if (NULL != oldchain) setcurtchain(oldchain); /* reset from discard chain */ stx_error(ERR_SPOREOL); return FALSE; } if (!linetail()) { tnxtarg(&x); dqloop(jmpchain,link,nxtjmp) { ref1 = nxtjmp->jmptrip; ref1->operand[0] = x; } if (NULL != oldchain) { /* for a literal 0 postconditional, we just throw the command & args away along with any pending error */ pending_errtriplecode = 0; setcurtchain(oldchain); /* reset from discard chain */ } TREF(pos_in_chain) = ifpos_in_chain; return FALSE; } if (NULL != oldchain) setcurtchain(oldchain); /* reset from discard chain */ return TRUE; } fis-gtm-V7.0-005/sr_port/m_job.c0000755000032200000250000001002114342376331015272 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "iotimer.h" #include "job.h" #include "advancewindow.h" #include "cmd.h" GBLREF boolean_t run_time; GBLREF mident routine_name; LITREF mident zero_ident; LITREF mval literal_notimeout; error_def(ERR_COMMAORRPAREXP); error_def(ERR_JOBACTREF); error_def(ERR_MAXACTARG); error_def(ERR_RTNNAME); int m_job(void) { boolean_t is_timeout, dummybool; static readonly unsigned char empty_plist[1] = { jp_eol }; int argcnt; oprtype arglst, *argptr, argval, label, offset, routine, plist, timeout; triple *next, *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; label = put_str(zero_ident.addr, zero_ident.len); offset = put_ilit((mint)0); if (!lref(&label, &offset, FALSE, indir_job, TRUE, &dummybool)) return FALSE; if ((TRIP_REF == label.oprclass) && (OC_COMMARG == label.oprval.tref->opcode)) return TRUE; if (TK_CIRCUMFLEX != TREF(window_token)) { if (!run_time) routine = put_str(routine_name.addr, routine_name.len); else routine = put_tref(newtriple(OC_CURRTN)); } else { advancewindow(); switch (TREF(window_token)) { case TK_IDENT: routine = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); advancewindow(); break; case TK_ATSIGN: if (!indirection(&routine)) return FALSE; break; default: stx_error(ERR_RTNNAME); return FALSE; } } argcnt = 0; if (TK_LPAREN == TREF(window_token)) { advancewindow(); argptr = &arglst; while (TK_RPAREN != TREF(window_token)) { if (MAX_ACTUALS < argcnt) { stx_error(ERR_MAXACTARG); return FALSE; } if (TK_PERIOD == TREF(window_token)) { stx_error(ERR_JOBACTREF); return FALSE; } if (TK_COMMA == TREF(window_token)) { ref = newtriple(OC_NULLEXP); argval = put_tref(ref); } else if (EXPR_FAIL == expr(&argval, MUMPS_EXPR)) return FALSE; ref = newtriple(OC_PARAMETER); ref->operand[0] = argval; *argptr = put_tref(ref); argptr = &ref->operand[1]; argcnt++; if (TK_COMMA == TREF(window_token)) advancewindow(); else if (TK_RPAREN != TREF(window_token)) { stx_error(ERR_COMMAORRPAREXP); return FALSE; } } advancewindow(); /* jump over close paren */ } if (TK_COLON == TREF(window_token)) { advancewindow(); if (TK_COLON == TREF(window_token)) { is_timeout = TRUE; plist = put_str((char *)empty_plist,SIZEOF(empty_plist)); } else { if (!jobparameters(&plist)) return FALSE; is_timeout = (TK_COLON == TREF(window_token)); } if (is_timeout) { advancewindow(); if (EXPR_FAIL == expr(&timeout, MUMPS_EXPR)) return FALSE; } else timeout = put_lit((mval *)&literal_notimeout); } else { is_timeout = FALSE; plist = put_str((char *)empty_plist,SIZEOF(empty_plist)); timeout = put_lit((mval *)&literal_notimeout); } ref = newtriple(OC_JOB); ref->operand[0] = put_ilit(argcnt + 5); /* parameter list + five fixed arguments */ next = newtriple(OC_PARAMETER); ref->operand[1] = put_tref(next); next->operand[0] = label; ref = newtriple(OC_PARAMETER); next->operand[1] = put_tref(ref); ref->operand[0] = offset; next = newtriple(OC_PARAMETER); ref->operand[1] = put_tref(next); next->operand[0] = routine; ref = newtriple(OC_PARAMETER); next->operand[1] = put_tref(ref); ref->operand[0] = plist; next = newtriple(OC_PARAMETER); ref->operand[1] = put_tref(next); next->operand[0] = timeout; if (argcnt) next->operand[1] = arglst; if (is_timeout) newtriple(OC_TIMTRU); return TRUE; } fis-gtm-V7.0-005/sr_port/m_kill.c0000755000032200000250000000650114342376331015463 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "mdq.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "advancewindow.h" #include "cmd.h" error_def(ERR_ALIASEXPECTED); error_def(ERR_NOALIASLIST); error_def(ERR_RPARENMISSING); error_def(ERR_VAREXPECTED); int m_kill(void) { boolean_t alias_processing; int count; mvar *mvarptr; oprtype tmparg; triple *next, *org, *ref, *s; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (alias_processing = (TK_ASTERISK == TREF(window_token))) /* NOTE assignment */ advancewindow(); switch (TREF(window_token)) { case TK_IDENT: /* If doing alias processing, we need to pass the index of the var rather than its lv_val but do the common case first. Note that a kill of an alias container is handled the same as the kill of any other regular local variable. */ if (!alias_processing || (TK_LPAREN == TREF(director_token))) { if (!lvn(&tmparg,OC_SRCHINDX,0)) return FALSE; ref = newtriple(OC_KILL); ref->operand[0] = tmparg; } else { /* alias (unsubscripted var) kill */ ref = newtriple(OC_KILLALIAS); mvarptr = get_mvaddr(&(TREF(window_ident))); ref->operand[0] = put_ilit(mvarptr->mvidx); advancewindow(); } break; case TK_CIRCUMFLEX: if (alias_processing) { stx_error(ERR_ALIASEXPECTED); return FALSE; } if (!gvn()) return FALSE; ref = newtriple(OC_GVKILL); break; case TK_ATSIGN: if (alias_processing) { stx_error(ERR_ALIASEXPECTED); return FALSE; } if (!indirection(&tmparg)) return FALSE; ref = maketriple(OC_COMMARG); ref->operand[0] = tmparg; ref->operand[1] = put_ilit((mint)indir_kill); ins_triple(ref); return TRUE; case TK_EOL: case TK_SPACE: newtriple(alias_processing ? OC_KILLALIASALL : OC_KILLALL); break; case TK_LPAREN: if (alias_processing) { stx_error(ERR_NOALIASLIST); return FALSE; } ref = org = maketriple(OC_XKILL); count = 0; do { advancewindow(); next = maketriple(OC_PARAMETER); ref->operand[1] = put_tref(next); switch (TREF(window_token)) { case TK_IDENT: next->operand[0] = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); advancewindow(); break; case TK_ATSIGN: if (!indirection(&tmparg)) return FALSE; s = newtriple(OC_INDLVARG); s->operand[0] = tmparg; next->operand[0] = put_tref(s); break; case TK_ASTERISK: stx_error(ERR_NOALIASLIST); return FALSE; default: stx_error(ERR_VAREXPECTED); return FALSE; } ins_triple(next); ref = next; count++; } while (TK_COMMA == TREF(window_token)); if (TK_RPAREN != TREF(window_token)) { stx_error(ERR_RPARENMISSING); return FALSE; } advancewindow(); org->operand[0] = put_ilit((mint)count); ins_triple(org); return TRUE; default: stx_error(alias_processing ? ERR_ALIASEXPECTED : ERR_VAREXPECTED); return FALSE; } return TRUE; } fis-gtm-V7.0-005/sr_port/m_lock.c0000755000032200000250000000472214342376331015463 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "iotimer.h" #include "indir_enum.h" #include "advancewindow.h" #include "cmd.h" LITREF mval literal_notimeout; error_def(ERR_RPARENMISSING); int m_lock(void) { boolean_t indirect; opctype ox; oprtype indopr; triple *ref, *restart; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; restart = newtriple(OC_RESTARTPC); indirect = FALSE; ox = OC_LOCK; switch (TREF(window_token)) { case TK_EOL: case TK_SPACE: ox = OC_UNLOCK; restart->opcode = OC_NOOP; newtriple(OC_UNLOCK); return TRUE; break; case TK_MINUS: case TK_PLUS: ox = (TK_MINUS == TREF(window_token)) ? OC_LCKDECR : OC_LCKINCR; advancewindow(); if (TK_ATSIGN != TREF(window_token)) break; /* WARNING possible fall-through */ case TK_ATSIGN: if (!indirection(&indopr)) return FALSE; ref = maketriple(OC_COMMARG); ref->operand[0] = indopr; if ((TK_COLON != TREF(window_token)) && (OC_LOCK == ox)) { ref->operand[1] = put_ilit((mint)indir_lock); ins_triple(ref); return TRUE; } ref->operand[1] = put_ilit((mint)indir_nref); indirect = TRUE; /* WARNING fall-through */ default: if (OC_LOCK == ox) newtriple(OC_UNLOCK); } newtriple(OC_LKINIT); if (indirect) ins_triple(ref); else { switch (TREF(window_token)) { case TK_LPAREN: do { advancewindow(); if (nref() == EXPR_FAIL) return FALSE; } while (TK_COMMA == TREF(window_token)); if (TK_RPAREN != TREF(window_token)) { stx_error(ERR_RPARENMISSING); return FALSE; } advancewindow(); break; default: if (EXPR_FAIL == nref()) return FALSE; break; } } ref = maketriple(ox); if (TK_COLON != TREF(window_token)) { ref->operand[0] = put_lit((mval *)&literal_notimeout); ins_triple(ref); } else { advancewindow(); if (EXPR_FAIL == expr(&(ref->operand[0]), MUMPS_EXPR)) return FALSE; ins_triple(ref); newtriple(OC_TIMTRU); } return TRUE; } fis-gtm-V7.0-005/sr_port/m_merge.c0000755000032200000250000001202714342376331015627 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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_string.h" #include "compiler.h" #include "mdq.h" #include "opcode.h" #include "indir_enum.h" #include "nametabtyp.h" #include "toktyp.h" #include "merge_def.h" #include "cmd.h" #include "mvalconv.h" #include "advancewindow.h" #include "glvn_pool.h" error_def(ERR_EQUAL); error_def(ERR_RPARENMISSING); error_def(ERR_VAREXPECTED); int m_merge(void) { int type; boolean_t used_glvn_slot; mval mv; opctype put_oc; oprtype mopr, control_slot; triple *obp, *ref, *restart, *s1, *sub, tmpchain; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; used_glvn_slot = FALSE; sub = NULL; restart = newtriple(OC_RESTARTPC); /* Here is where a restart should pick up */ dqinit(&tmpchain, exorder); /* Left Hand Side of EQUAL sign */ switch (TREF(window_token)) { case TK_IDENT: if (!lvn(&mopr, OC_PUTINDX, 0)) return FALSE; if (OC_PUTINDX == mopr.oprval.tref->opcode) { /* we insert left hand side argument into tmpchain. */ sub = mopr.oprval.tref; put_oc = OC_PUTINDX; dqdel(mopr.oprval.tref, exorder); dqins(tmpchain.exorder.bl, exorder, mopr.oprval.tref); } ref = maketriple(OC_MERGE_LVARG); ref->operand[0] = put_ilit(MARG1_LCL); ref->operand[1] = mopr; dqins(tmpchain.exorder.bl, exorder, ref); break; case TK_CIRCUMFLEX: s1 = (TREF(curtchain))->exorder.bl; if (!gvn()) return FALSE; assert(OC_GVRECTARG != (TREF(curtchain))->opcode); /* we count on gvn not having been shifted */ for (sub = (TREF(curtchain))->exorder.bl; sub != s1; sub = sub->exorder.bl) { put_oc = sub->opcode; if (OC_GVNAME == put_oc || OC_GVNAKED == put_oc || OC_GVEXTNAM == put_oc) break; } assert((OC_GVNAME == put_oc) || (OC_GVNAKED == put_oc) || (OC_GVEXTNAM == put_oc)); /* we insert left hand side argument into tmpchain. */ dqdel(sub, exorder); dqins(tmpchain.exorder.bl ,exorder, sub); ref = maketriple(OC_MERGE_GVARG); ref->operand[0] = put_ilit(MARG1_GBL); dqins(tmpchain.exorder.bl, exorder, ref); break; case TK_ATSIGN: if (!indirection(&mopr)) return FALSE; if (TK_EQUAL != TREF(window_token)) { ref = newtriple(OC_COMMARG); ref->operand[0] = mopr; ref->operand[1] = put_ilit((mint) indir_merge); return TRUE; } type = MARG1_LCL | MARG1_GBL; memset(&mv, 0, SIZEOF(mval)); /* Initialize so unused fields don't cause object hash differences */ MV_FORCE_MVAL(&mv, type); MV_FORCE_STRD(&mv); if (TREF(side_effect_handling)) { /* save and restore the variable lookup for true left-to-right evaluation */ used_glvn_slot = TRUE; INSERT_INDSAVGLVN(control_slot, mopr, ANY_SLOT, 0); /* 0 flag to defer global reference */ ref = maketriple(OC_INDMERGE2); ref->operand[0] = control_slot; } else { /* quick and dirty old way */ ref = maketriple(OC_INDMERGE); ref->operand[0] = put_lit(&mv); ref->operand[1] = mopr; } /* we insert left hand side argument into tmpchain. */ dqins(tmpchain.exorder.bl, exorder, ref); break; default: stx_error(ERR_VAREXPECTED); return FALSE; } if (TREF(window_token) != TK_EQUAL) { stx_error(ERR_EQUAL); return FALSE; } advancewindow(); /* Right Hand Side of EQUAL sign */ TREF(temp_subs) = FALSE; switch (TREF(window_token)) { case TK_IDENT: if (!lvn(&mopr, OC_M_SRCHINDX, 0)) return FALSE; ref = newtriple(OC_MERGE_LVARG); ref->operand[0] = put_ilit(MARG2_LCL); ref->operand[1] = mopr; break; case TK_CIRCUMFLEX: if (!gvn()) return FALSE; ref = newtriple(OC_MERGE_GVARG); ref->operand[0] = put_ilit(MARG2_GBL); break; case TK_ATSIGN: TREF(temp_subs) = TRUE; if (!indirection(&mopr)) { stx_error(ERR_VAREXPECTED); return FALSE; } type = MARG2_LCL | MARG2_GBL; memset(&mv, 0, SIZEOF(mval)); /* Initialize so unused fields don't cause object hash differences */ MV_FORCE_MVAL(&mv, type); MV_FORCE_STRD(&mv); ref = maketriple(OC_INDMERGE); ref->operand[0] = put_lit(&mv); ref->operand[1] = mopr; ins_triple(ref); break; default: stx_error(ERR_VAREXPECTED); return FALSE; } /* * Make sure that during runtime right hand side argument is processed first. * This is specially important if global naked variable is used . */ obp = (TREF(curtchain))->exorder.bl; dqadd(obp, &tmpchain, exorder); if (TREF(temp_subs) && TREF(side_effect_handling) && sub) create_temporaries(sub, put_oc); TREF(temp_subs) = FALSE; if (used_glvn_slot) { ref = newtriple(OC_GLVNPOP); ref->operand[0] = control_slot; } ref = newtriple(OC_MERGE); return TRUE; } fis-gtm-V7.0-005/sr_port/m_new.c0000755000032200000250000001055314342376331015323 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2022 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 "compiler.h" #include "mdq.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "svnames.h" #include "nametabtyp.h" #include "funsvn.h" #include "advancewindow.h" #include "cmd.h" #include "namelook.h" #include "start_fetches.h" LITREF nametabent svn_names[]; LITREF svn_data_type svn_data[]; LITREF unsigned char svn_index[]; error_def(ERR_INVSVN); error_def(ERR_RPARENMISSING); error_def(ERR_SVNEXPECTED); error_def(ERR_SVNONEW); error_def(ERR_VAREXPECTED); int m_new(void) { boolean_t parse_warn; int count, n; mvar *var; oprtype tmparg; triple *fetch, *next, *org, *ref, *s, *tmp; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TREF(xecute_literal_parse)) return FALSE; switch (TREF(window_token)) { case TK_IDENT: var = get_mvaddr(&(TREF(window_ident))); if ((var->last_fetch != (TREF(fetch_control)).curr_fetch_trip) && !TREF(discard)) { /* This block is identical to one in put_mvar */ fetch = newtriple(OC_PARAMETER); (TREF(fetch_control)).curr_fetch_opr->operand[1] = put_tref(fetch); fetch->operand[0] = put_ilit(var->mvidx); ((TREF(fetch_control)).curr_fetch_count)++; (TREF(fetch_control)).curr_fetch_opr = fetch; var->last_fetch = (TREF(fetch_control)).curr_fetch_trip; } tmp = maketriple(OC_NEWVAR); tmp->operand[0] = put_ilit(var->mvidx); ins_triple(tmp); advancewindow(); return TRUE; case TK_ATSIGN: if (!indirection(&tmparg)) return FALSE; ref = maketriple(OC_COMMARG); ref->operand[0] = tmparg; ref->operand[1] = put_ilit((mint) indir_new); ins_triple(ref); MID_LINE_REFETCH; return TRUE; case TK_DOLLAR: advancewindow(); if (TK_IDENT == TREF(window_token)) { parse_warn = FALSE; if ((0 <= (n = namelook(svn_index, svn_names, (TREF(window_ident)).addr, (TREF(window_ident)).len)))) { /* NOTE assignment above */ switch (svn_data[n].opcode) { case SV_ZTRAP: case SV_ETRAP: case SV_ESTACK: case SV_ZYERROR: case SV_ZGBLDIR: GTMTRIG_ONLY(case SV_ZTWORMHOLE:) tmp = maketriple(OC_NEWINTRINSIC); tmp->operand[0] = put_ilit(svn_data[n].opcode); break; default: STX_ERROR_WARN(ERR_SVNONEW); /* sets "parse_warn" to TRUE */ } } else { STX_ERROR_WARN(ERR_INVSVN); /* sets "parse_warn" to TRUE */ } advancewindow(); if (!parse_warn) ins_triple(tmp); else { /* OC_RTERROR triple would have been inserted in curtchain by ins_errtriple * (invoked by stx_error). No need to do anything else. */ assert(ALREADY_RTERROR); } return TRUE; } stx_error(ERR_SVNEXPECTED); return FALSE; case TK_EOL: case TK_SPACE: /* This actually a NEW all, but an XNEW with no arguments does the job */ tmp = maketriple(OC_XNEW); tmp->operand[0] = put_ilit((mint) 0); ins_triple(tmp); /* start a new fetch for whatever follows on the line */ MID_LINE_REFETCH; return TRUE; case TK_LPAREN: ref = org = maketriple(OC_XNEW); count = 0; do { advancewindow(); next = maketriple(OC_PARAMETER); ref->operand[1] = put_tref(next); switch (TREF(window_token)) { case TK_IDENT: next->operand[0] = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); advancewindow(); break; case TK_ATSIGN: if (!indirection(&tmparg)) return FALSE; s = newtriple(OC_INDLVARG); s->operand[0] = tmparg; next->operand[0] = put_tref(s); break; default: stx_error(ERR_VAREXPECTED); return FALSE; } ins_triple(next); ref = next; count++; } while (TK_COMMA == TREF(window_token)); if (TK_RPAREN != TREF(window_token)) { stx_error(ERR_RPARENMISSING); return FALSE; } advancewindow(); org->operand[0] = put_ilit((mint) count); ins_triple(org); /* start a new fetch for whatever follows on the line */ MID_LINE_REFETCH; return TRUE; default: stx_error(ERR_VAREXPECTED); return FALSE; } } fis-gtm-V7.0-005/sr_port/m_open.c0000755000032200000250000000576014342376331015477 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "iotimer.h" #include "io_params.h" #include "advancewindow.h" #include "cmd.h" #include "deviceparameters.h" #include "mdq.h" LITREF mval literal_notimeout; LITREF mval literal_null; int m_open(void) { boolean_t is_timeout, inddevparms; static readonly unsigned char empty_plist[1] = { iop_eol }; int rval; opctype opcd; oprtype devpopr, mspace, plist, sopr, timeout; triple *indref, *ref1, *ref2, tmpchain; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; inddevparms = FALSE; if (EXPR_FAIL == (rval = expr(&sopr, MUMPS_STR))) /* NOTE assignment */ return FALSE; if (TK_COLON != TREF(window_token)) { /* Single arg specified */ if (EXPR_INDR == rval) { /* All arguments indirect */ make_commarg(&sopr, indir_open); return TRUE; } else /* Only device given, default device parms */ plist = put_str((char *)empty_plist, SIZEOF(empty_plist)); } else { advancewindow(); switch (TREF(window_token)) { case TK_COLON: /* Default device parms */ plist = put_str((char *)empty_plist, SIZEOF(empty_plist)); break; case TK_ATSIGN: /* Indirect for device parms */ if (!indirection(&devpopr)) return FALSE; indref = newtriple(OC_INDDEVPARMS); indref->operand[0] = devpopr; indref->operand[1] = put_ilit(IOP_OPEN_OK); inddevparms = TRUE; break; default: /* Literal device parms specified */ if (!deviceparameters(&plist, IOP_OPEN_OK)) return FALSE; } } /* Code generation for the optional timeout parm */ is_timeout = FALSE; if (TK_COLON != TREF(window_token)) timeout = put_lit((mval *)&literal_notimeout); else { advancewindow(); if (TK_COLON == TREF(window_token)) timeout = put_lit((mval *)&literal_notimeout); else { is_timeout = TRUE; if (EXPR_FAIL == expr(&timeout, MUMPS_EXPR)) return FALSE; } } if (TK_COLON != TREF(window_token)) mspace = put_lit((mval *)&literal_null); else { advancewindow(); if (EXPR_FAIL == expr(&mspace, MUMPS_EXPR)) return FALSE; } ref1 = newtriple(OC_OPEN); ref1->operand[0] = sopr; ref2 = newtriple(OC_PARAMETER); ref1->operand[1] = put_tref(ref2); ref2->operand[0] = !inddevparms ? plist : put_tref(indref); ref1 = newtriple(OC_PARAMETER); ref2->operand[1] = put_tref(ref1); ref1->operand[0] = timeout; ref2 = newtriple(OC_PARAMETER); ref1->operand[1] = put_tref(ref2); ref2->operand[0] = mspace; if (is_timeout) newtriple(OC_TIMTRU); return TRUE; } fis-gtm-V7.0-005/sr_port/m_quit.c0000755000032200000250000000450614342376331015515 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "mmemory.h" #include "cmd.h" #include "indir_enum.h" #include "advancewindow.h" GBLREF boolean_t run_time; error_def(ERR_ALIASEXPECTED); error_def(ERR_QUITARGLST); error_def(ERR_QUITARGUSE); int m_quit(void) { boolean_t arg; int rval; mvar *mvarptr; oprtype tmparg, x; triple *r, *triptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; arg = ((TK_EOL != TREF(window_token)) && (TK_SPACE != TREF(window_token))); if (TREF(xecute_literal_parse)) return FALSE; if (TREF(for_stack_ptr) == TADR(for_stack)) { /* not FOR */ if (!arg) { newtriple((run_time) ? OC_HARDRET : OC_RET); return TRUE; } /* We now know we have an arg. See if it is an alias indicated arg */ if (TK_ASTERISK == TREF(window_token)) { /* We have QUIT * alias syntax */ advancewindow(); if (TK_IDENT == TREF(window_token)) { /* Both alias and alias container sources go through here */ if (!lvn(&tmparg, OC_GETINDX, 0)) return FALSE; r = newtriple(OC_RETARG); r->operand[0] = tmparg; r->operand[1] = put_ilit(TRUE); return TRUE; } else { /* Unexpected text after alias indicator */ stx_error(ERR_ALIASEXPECTED); return FALSE; } } else if (EXPR_FAIL != (rval = expr(&x, MUMPS_EXPR)) && (TK_COMMA != TREF(window_token))) /* NOTE assignment */ { if (EXPR_INDR != rval) { r = newtriple(OC_RETARG); r->operand[0] = x; r->operand[1] = put_ilit(FALSE); } else /* Indirect argument */ make_commarg(&x, indir_quit); return TRUE; } if (TK_COMMA == TREF(window_token)) stx_error (ERR_QUITARGLST); return FALSE; } else if (!arg) /* FOR */ { triptr = newtriple(OC_JMP); FOR_END_OF_SCOPE(1, triptr->operand[0]); return TRUE; } stx_error(ERR_QUITARGUSE); return FALSE; } fis-gtm-V7.0-005/sr_port/m_read.c0000755000032200000250000001147314342376331015447 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "iotimer.h" #include "mdq.h" #include "advancewindow.h" #include "cmd.h" #include "rwformat.h" LITREF mval literal_notimeout; error_def(ERR_RWARG); int m_read(void) { boolean_t local; opctype put_oc, read_oc; oprtype *timeout, x; triple *put, *ref, *s1, *sub, tmpchain; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; TREF(temp_subs) = FALSE; local = TRUE; dqinit(&tmpchain, exorder); switch (TREF(window_token)) { case TK_ASTERISK: advancewindow(); switch (TREF(window_token)) { default: case TK_IDENT: if (!lvn(&x, OC_PUTINDX, 0)) return FALSE; if (OC_PUTINDX == x.oprval.tref->opcode) { dqdel(x.oprval.tref, exorder); dqins(tmpchain.exorder.bl, exorder, x.oprval.tref); sub = x.oprval.tref; put_oc = OC_PUTINDX; } put = maketriple(OC_STO); put->operand[0] = x; dqins(tmpchain.exorder.bl, exorder, put); break; case TK_CIRCUMFLEX: local = FALSE; s1 = (TREF(curtchain))->exorder.bl; if (!gvn()) return FALSE; assert(OC_GVRECTARG != (TREF(curtchain))->opcode); /* we count on gvn not having been shifted */ for (sub = (TREF(curtchain))->exorder.bl; sub != s1; sub = sub->exorder.bl) { put_oc = sub->opcode; if ((OC_GVNAME == put_oc) || (OC_GVNAKED == put_oc) || (OC_GVEXTNAM == put_oc)) break; } assert((OC_GVNAME == put_oc) || (OC_GVNAKED == put_oc) || (OC_GVEXTNAM == put_oc)); dqdel(sub, exorder); dqins(tmpchain.exorder.bl, exorder, sub); put = maketriple(OC_GVPUT); dqins(tmpchain.exorder.bl, exorder, put); break; case TK_ATSIGN: if (!indirection(&x)) return FALSE; put = maketriple(OC_INDSET); put->operand[0] = x; dqins(tmpchain.exorder.bl, exorder, put); break; } if (TK_HASH == TREF(window_token)) { stx_error(ERR_RWARG); return FALSE; } read_oc = OC_RDONE; break; case TK_QUESTION: case TK_EXCLAIMATION: case TK_HASH: case TK_SLASH: return rwformat(); case TK_STRLIT: x = put_lit(&(TREF(window_mval))); advancewindow(); ref = newtriple(OC_WRITE); ref->operand[0] = x; return TRUE; case TK_IDENT: if (!lvn(&x, OC_PUTINDX, 0)) return FALSE; read_oc = OC_READ; if (OC_PUTINDX == x.oprval.tref->opcode) { dqdel(x.oprval.tref, exorder); dqins(tmpchain.exorder.bl, exorder, x.oprval.tref); sub = x.oprval.tref; put_oc = OC_PUTINDX; } put = maketriple(OC_STO); put->operand[0] = x; dqins(tmpchain.exorder.bl, exorder, put); break; case TK_CIRCUMFLEX: local = FALSE; read_oc = OC_READ; s1 = (TREF(curtchain))->exorder.bl; if (!gvn()) return FALSE; assert(OC_GVRECTARG != (TREF(curtchain))->opcode); /* we count on gvn not having been shifted */ for (sub = (TREF(curtchain))->exorder.bl; sub != s1; sub = sub->exorder.bl) { put_oc = sub->opcode; if ((OC_GVNAME == put_oc) || (OC_GVNAKED == put_oc) || (OC_GVEXTNAM == put_oc)) break; } assert((OC_GVNAME == put_oc) || (OC_GVNAKED == put_oc) || (OC_GVEXTNAM == put_oc)); dqdel(sub, exorder); dqins(tmpchain.exorder.bl, exorder, sub); put = maketriple(OC_GVPUT); dqins(tmpchain.exorder.bl, exorder, put); break; case TK_ATSIGN: if (!indirection(&x)) return FALSE; if ((TK_COLON != TREF(window_token)) && (TK_HASH != TREF(window_token))) { ref = maketriple(OC_COMMARG); ref->operand[0] = x; ref->operand[1] = put_ilit(indir_read); ins_triple(ref); return TRUE; } put = maketriple(OC_INDSET); put->operand[0] = x; dqins(tmpchain.exorder.bl, exorder, put); read_oc = OC_READ; break; default: stx_error(ERR_RWARG); return FALSE; } if (TK_HASH == TREF(window_token)) { advancewindow(); ref = maketriple(OC_READFL); if (EXPR_FAIL == expr(&ref->operand[0], MUMPS_INT)) return FALSE; timeout = &ref->operand[1]; } else { ref = maketriple(read_oc); timeout = &ref->operand[0]; } if (TK_COLON != TREF(window_token)) { *timeout = put_lit((mval *)&literal_notimeout); ins_triple(ref); } else { advancewindow(); if (EXPR_FAIL == expr(timeout, MUMPS_EXPR)) return FALSE; ins_triple(ref); newtriple(OC_TIMTRU); } put->operand[local ? 1 : 0] = put_tref(ref); ref = (TREF(curtchain))->exorder.bl; dqadd(ref, &tmpchain, exorder); /*this is a violation of info hiding*/ return TRUE; } fis-gtm-V7.0-005/sr_port/m_set.c0000755000032200000250000010604414342376331015326 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "gtm_ctype.h" #include "compiler.h" #include "mdq.h" #include "opcode.h" #include "indir_enum.h" #include "nametabtyp.h" #include "toktyp.h" #include "funsvn.h" #include "mmemory.h" #include "advancewindow.h" #include "namelook.h" #include "cmd.h" #include "svnames.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "alias.h" #include "glvn_pool.h" #ifdef UTF8_SUPPORTED #include "gtm_utf8.h" #endif GBLREF boolean_t badchar_inhibit; GBLREF boolean_t gtm_utf8_mode; error_def(ERR_ALIASEXPECTED); error_def(ERR_COMMA); error_def(ERR_DZWRNOALIAS); error_def(ERR_DZWRNOPAREN); error_def(ERR_EQUAL); error_def(ERR_INVSVN); error_def(ERR_NOALIASLIST); error_def(ERR_RPARENMISSING); error_def(ERR_SVNOSET); error_def(ERR_VAREXPECTED); LITREF unsigned char svn_index[], fun_index[]; LITREF nametabent svn_names[], fun_names[]; LITREF svn_data_type svn_data[]; LITREF fun_data_type fun_data[]; #define FIRST_SETLEFT_NOTSEEN -1 /* see comment against variable "first_setleft_invalid" for details */ /* This macro is used to insert the conditional jump triples (in SET $PIECE/$EXTRACT) ahead of the global variable * reference of the SET $PIECE target. This is to ensure the naked indicator is not touched in cases where the M-standard * says it should not be. e.g. set $piece(^x,"delim",2,1) should not touch naked indicator since 2>1. */ #define DQINSCURTARGCHAIN(curtargtriple) \ { \ dqins(curtargchain, exorder, curtargtriple); \ assert(curtargchain->exorder.fl == curtargtriple); \ curtargchain = curtargtriple; \ } #define RESTORE_CURTCHAIN_IF_NEEDED \ { \ if (curtchain_switched) \ { \ assert(NULL != save_curtchain); \ setcurtchain(save_curtchain); \ curtchain_switched = FALSE; \ } \ } #define SYNTAX_ERROR(errnum) \ { \ RESTORE_CURTCHAIN_IF_NEEDED; \ stx_error(errnum); \ return FALSE; \ } #define SYNTAX_ERROR_NOREPORT_HERE \ { \ RESTORE_CURTCHAIN_IF_NEEDED; \ return FALSE; \ } void allow_dzwrtac_as_mident(void); int m_set(void) { /* Some comment on "parse_warn". It is set to TRUE whenever the parse encounters an invalid setleft target. * * Note that even if "parse_warn" is TRUE, we should not return FALSE right away but need to continue the parse * until the end of the current SET command. This way any remaining commands in the current parse line will be * parsed and triples generated for them. This is necessary just in case the currently parsed invalid SET command * does not get executed at runtime (due to postconditionals etc.) * * Some comment on the need for "first_setleft_invalid". This variable is needed only in the * case we encounter an invalid-SVN/invalid-FCN/unsettable-SVN as a target of the SET. We need to evaluate the * right-hand-side of the SET command only if at least one valid setleft target is parsed before an invalid setleft * target is encountered. This is because we still need to execute the valid setlefts at runtime before triggering * a runtime error for the invalid setleft. If the first setleft target is an invalid one, then there is no need * to evaluate the right-hand-side. In fact, in this case, adding triples (corresponding to the right hand side) * to the execution chain could cause problems with emit_code later in the compilation as the destination * for the right hand side triples could now be undefined (for example a valid SVN on the left side of the * SET would have generated an OC_SVPUT triple with one of its operands holding the result of the right * hand side evaluation, but an invalid SVN on the left side which would have instead caused an OC_RTERROR triple * to have been generated leaving no triple to receive the result of the right hand side evaluation thus causing * emit_code to be confused and hitting an assertpro()). Therefore discard all triples generated by the right hand * side in this case. * * By the same reasoning, discard all triples generated by setleft targets AFTER this invalid one as well. * "first_setleft_invalid" is set to TRUE if the first setleft target is invalid and set to FALSE if the first setleft * target is valid. It is initialized to -1 before the start of the parse. */ boolean_t alias_processing, delim1char, first_is_lit, got_lparen, have_lh_alias, is_extract, last_is_lit, valid_char; boolean_t parse_warn; /* set to TRUE in case of an invalid SVN etc. */ boolean_t curtchain_switched; /* set to TRUE if a setcurtchain was done */ boolean_t temp_subs_was_FALSE; boolean_t used_glvn_slot; int delimlen, first_val_lit, index, last_val_lit, nakedzalias, setop; int first_setleft_invalid; /* set to TRUE if the first setleft target is invalid */ opctype put_oc; oprtype delimval, firstval, lastval, resptr, *result, v, control_slot, first_control_slot; triple *curtargchain, *delimiter, discardcurtchain, *first, *get, *jmptrp1, *jmptrp2, *last, *obp, *put; triple *s, *s0, *s1, save_targchain, *save_curtchain, *save_curtchain1, *sub, targchain, *tmp; triple *ref; mint delimlit; mval *delim_mval; mvar *mvarptr; union { uint4 unichar_val; unsigned char unibytes_val[4]; } unichar; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; TREF(temp_subs) = FALSE; used_glvn_slot = FALSE; dqinit(&targchain, exorder); result = (oprtype *)mcalloc(SIZEOF(oprtype)); resptr = put_indr(result); delimiter = sub = last = NULL; /* A SET clause must be entirely alias related or a normal set. Parenthized multiple sets of aliases are not allowed * and will trigger an error. This is because the source and targets of aliases require different values and references * than normal sets do and thus cannot be mixed. */ if (alias_processing = (TK_ASTERISK == TREF(window_token))) advancewindow(); if (got_lparen = (TK_LPAREN == TREF(window_token))) { if (alias_processing) stx_error(ERR_NOALIASLIST); advancewindow(); TREF(temp_subs) = TRUE; } /* Some explanation: The triples from the left hand side of the SET expression that are * expressly associated with fetching (in case of set $piece/$extract) and/or storing of * the target value are removed from curtchain and placed on the targchain. Later, these * triples will be added to the end of curtchain to do the finishing store of the target * after the righthand side has been evaluated. This is per the M standard. * * Note that SET $PIECE/$EXTRACT have special conditions in which the first argument is not referenced at all. * (e.g. set $piece(^a," ",3,2) in this case 3 > 2 so this should not evaluate ^a and therefore should not * modify the naked indicator). That is, the triples that do these conditional checks need to be inserted * ahead of the OC_GVNAME of ^a, all of which need to be inserted on the targchain. But the conditionalization * can be done only after parsing the first argument of the SET $PIECE and examining the remaining arguments. * Therefore we maintain the "curtargchain" variable which stores the value of the "targchain" at the beginning * of the iteration (at the start of the $PIECE parsing) and all the conditionalization will be inserted right * here which is guaranteed to be ahead of where the OC_GVNAME gets inserted. * * For example, SET $PIECE(^A(x,y),delim,first,last)=RHS will generate a final triple chain as follows * * A - Triples to evaluate subscripts (x,y) of the global ^A * A - Triples to evaluate delim * A - Triples to evaluate first * A - Triples to evaluate last * B - Triples to evaluate RHS * C - Triples to do conditional check (e.g. first > last etc.) * C - Triples to branch around if the checks indicate this is a null operation SET $PIECE * D - Triple that does OC_GVNAME of ^A * D - Triple that does OC_SETPIECE to determine the new value * D - Triple that does OC_GVPUT of the new value into ^A(x,y) * This is the point where the conditional check triples will branch around to if they chose to. * * A - triples that evaluate the arguments/subscripts in the left-hand-side of the SET command * These triples are built in "curtchain" * B - triples that evaluate the arguments/subscripts in the right-hand-side of the SET command * These triples are built in "curtchain" * C - triples that do conditional check for any $PIECE/$EXTRACT in the left side of the SET command. * These triples are built in "curtargchain" * D - triples that generate the reference to the target of the SET and the store into the target. * These triples are built in "targchain" * * Note alias processing does not support the SET *(...)=.. type syntax because the type of argument * created for RHS processing is dependent on the LHS receiver type and we do not support more than one * type of source argument in a single SET. */ first_setleft_invalid = FIRST_SETLEFT_NOTSEEN; curtchain_switched = FALSE; nakedzalias = have_lh_alias = FALSE; save_curtchain = NULL; assert(FIRST_SETLEFT_NOTSEEN != TRUE); assert(FIRST_SETLEFT_NOTSEEN != FALSE); for (parse_warn = FALSE; ; parse_warn = FALSE) { curtargchain = targchain.exorder.bl; jmptrp1 = jmptrp2 = NULL; delim1char = is_extract = FALSE; allow_dzwrtac_as_mident(); /* Allows $ZWRTACxxx as target to be treated as an mident */ switch (TREF(window_token)) { case TK_IDENT: /* A slight diversion first. If this is a $ZWRTAC set (indication of $ in first char * is currently enough to signify that), then we need to check a few conditions first. * If this is a "naked $ZWRTAC", meaning no numeric suffix, then this is a flag that * all the $ZWRTAC vars in the local variable tree need to be kill *'d which will not * be generating a SET instruction. First we need to verify that fact and make sure * we are not in PARENs and not doing alias processing. Note *any* value can be * specified as the source but while it will be evaluated, it is NOT stored anywhere. */ if ('$' == *(TREF(window_ident)).addr) { /* We have a $ZWRTAC target */ if (got_lparen) /* We don't allow $ZWRTACxxx to be specified in a parenthesized list. * Verify that first */ SYNTAX_ERROR(ERR_DZWRNOPAREN); if (STR_LIT_LEN(DOLLAR_ZWRTAC) == (TREF(window_ident)).len) { /* Ok, this is a naked $ZWRTAC targeted set */ if (alias_processing) SYNTAX_ERROR(ERR_DZWRNOALIAS); nakedzalias = TRUE; /* This opcode doesn't really need args but it is easier to fit in with the rest * of m_set processing to pass it the result arg, which there may actually be * a use for someday.. */ put = maketriple(OC_CLRALSVARS); put->operand[0] = resptr; dqrins(&targchain, exorder, put); advancewindow(); break; } } /* If we are doing alias processing, there are two possibilities: * 1) LHS is unsubscripted - it is an alias variable being created or replaced. Need to parse * the varname as if this were a regular set. * 2) LHS is subscripted - it is an alias container variable being created or replaced. The * processing here is to pass the base variable index to the store routine so bypass the * lvn() call. */ if (!alias_processing || (TK_LPAREN == TREF(director_token))) { /* Normal variable processing or we have a lh alias container */ if (!lvn(&v, OC_PUTINDX, 0)) SYNTAX_ERROR_NOREPORT_HERE; if (OC_PUTINDX == v.oprval.tref->opcode) { dqdel(v.oprval.tref, exorder); dqrins(&targchain, exorder, v.oprval.tref); sub = v.oprval.tref; put_oc = OC_PUTINDX; if (TREF(temp_subs)) create_temporaries(sub, put_oc); } } else { /* Have alias variable. Argument is index into var table rather than pointer to var */ have_lh_alias = TRUE; /* We only want the variable index in this case. Since the entire hash structure to which * this variable is going to be pointing to is changing, doing anything that calls fetch() * is somewhat pointless so we avoid it by just accessing the variable information * directly. */ mvarptr = get_mvaddr(&(TREF(window_ident))); v = put_ilit(mvarptr->mvidx); advancewindow(); } /* Determine correct storing triple */ put = maketriple((!alias_processing ? OC_STO : (have_lh_alias ? OC_SETALS2ALS : OC_SETALSIN2ALSCT))); put->operand[0] = v; put->operand[1] = resptr; dqrins(&targchain, exorder, put); break; case TK_CIRCUMFLEX: if (alias_processing) SYNTAX_ERROR(ERR_ALIASEXPECTED); s1 = (TREF(curtchain))->exorder.bl; if (!gvn()) SYNTAX_ERROR_NOREPORT_HERE; assert(OC_GVRECTARG != (TREF(curtchain))->opcode); /* we count on gvn not having been shifted */ for (sub = (TREF(curtchain))->exorder.bl; sub != s1; sub = sub->exorder.bl) { put_oc = sub->opcode; if ((OC_GVNAME == put_oc) || (OC_GVNAKED == put_oc) || (OC_GVEXTNAM == put_oc)) break; } assert((OC_GVNAME == put_oc) || (OC_GVNAKED == put_oc) || (OC_GVEXTNAM == put_oc)); dqdel(sub, exorder); dqrins(&targchain, exorder, sub); if (TREF(temp_subs)) create_temporaries(sub, put_oc); put = maketriple(OC_GVPUT); put->operand[0] = resptr; dqrins(&targchain, exorder, put); break; case TK_ATSIGN: if (alias_processing) SYNTAX_ERROR(ERR_ALIASEXPECTED); if (!indirection(&v)) SYNTAX_ERROR_NOREPORT_HERE; if (!got_lparen && (TK_EQUAL != TREF(window_token))) { assert(!curtchain_switched); put = newtriple(OC_COMMARG); put->operand[0] = v; put->operand[1] = put_ilit(indir_set); return TRUE; } /* save and restore the variable lookup for true left-to-right evaluation */ INSERT_INDSAVGLVN(control_slot, v, ANY_SLOT, 0); /* 0 flag to defer global reference */ if (!used_glvn_slot) { used_glvn_slot = TRUE; first_control_slot = control_slot; } put = maketriple(OC_STOGLVN); put->operand[0] = control_slot; put->operand[1] = resptr; dqrins(&targchain, exorder, put); break; case TK_DOLLAR: if (alias_processing) SYNTAX_ERROR(ERR_ALIASEXPECTED); advancewindow(); if (TK_IDENT != TREF(window_token)) SYNTAX_ERROR(ERR_VAREXPECTED); if (TK_LPAREN != TREF(director_token)) { /* Look for intrinsic special variables */ s1 = (TREF(curtchain))->exorder.bl; if (0 > (index = namelook(svn_index, svn_names, (TREF(window_ident)).addr, (TREF(window_ident)).len))) { /* NOTE assignment above */ STX_ERROR_WARN(ERR_INVSVN); /* sets "parse_warn" to TRUE */ } else if (!svn_data[index].can_set) { STX_ERROR_WARN(ERR_SVNOSET); /* sets "parse_warn" to TRUE */ } advancewindow(); if (!parse_warn) { if ((SV_ETRAP != svn_data[index].opcode) && (SV_ZTRAP != svn_data[index].opcode)) { /* Setting of $ZTRAP or $ETRAP must go through opp_svput because they * may affect the stack pointer. All others directly to op_svput(). */ put = maketriple(OC_SVPUT); } else put = maketriple(OC_PSVPUT); put->operand[0] = put_ilit(svn_data[index].opcode); put->operand[1] = resptr; dqrins(&targchain, exorder, put); } else { /* OC_RTERROR triple would have been inserted in curtchain by ins_errtriple * (invoked by stx_error). To maintain consistency with the "if" portion of * this code, we need to move this triple to the "targchain". */ tmp = (TREF(curtchain))->exorder.bl; /* corresponds to put_ilit(FALSE) in ins_errtriple */ tmp = tmp->exorder.bl; /* corresponds to put_ilit(in_error) in ins_errtriple */ tmp = tmp->exorder.bl; /* corresponds to newtriple(OC_RTERROR) in ins_errtriple */ assert(OC_RTERROR == tmp->opcode); dqdel(tmp, exorder); dqrins(&targchain, exorder, tmp); } break; } /* Only 4 function names allowed on left side: $[Z]Piece and $[Z]Extract */ index = namelook(fun_index, fun_names, (TREF(window_ident)).addr, (TREF(window_ident)).len); if (0 > index) { STX_ERROR_WARN(ERR_INVFCN); /* sets "parse_warn" to TRUE */ /* OC_RTERROR triple would have been inserted in "curtchain" by ins_errtriple * (invoked by stx_error). We need to switch it to "targchain" to be consistent * with every other codepath in this module. */ tmp = (TREF(curtchain))->exorder.bl; /* corresponds to put_ilit(FALSE) in ins_errtriple */ tmp = tmp->exorder.bl; /* corresponds to put_ilit(in_error) in ins_errtriple */ tmp = tmp->exorder.bl; /* corresponds to newtriple(OC_RTERROR) in ins_errtriple */ assert(OC_RTERROR == tmp->opcode); dqdel(tmp, exorder); dqrins(&targchain, exorder, tmp); advancewindow(); /* skip past the function name */ advancewindow(); /* skip past the left paren */ /* Parse the remaining arguments until corresponding RIGHT-PAREN/SPACE/EOL is reached */ if (!parse_until_rparen_or_space()) SYNTAX_ERROR_NOREPORT_HERE; } else { switch (fun_data[index].opcode) { case OC_FNPIECE: setop = OC_SETPIECE; break; case OC_FNEXTRACT: is_extract = TRUE; setop = OC_SETEXTRACT; break; case OC_FNZPIECE: setop = OC_SETZPIECE; break; case OC_FNZEXTRACT: is_extract = TRUE; setop = OC_SETZEXTRACT; break; default: SYNTAX_ERROR(ERR_VAREXPECTED); } advancewindow(); advancewindow(); /* Although we see the get (target) variable first, we need to save it's processing * on another chain -- the targchain -- because the retrieval of the target is bypassed * and the naked indicator is not reset if the first/last parameters are not set in a * logical manner (must be > 0 and first <= last). So the evaluation order is * delimiter (if $piece), first, last, RHS of the set and then the target if applicable. * Set up primary action triple now since it is ref'd by the put triples generated below. */ s = maketriple(setop); /* Even for SET[Z]PIECE and SET[Z]EXTRACT, the SETxxxxx opcodes * do not do the final store, they only create the final value TO be * stored so generate the triples that will actually do the store now. * Note we are still building triples on the original curtchain. */ switch (TREF(window_token)) { case TK_IDENT: if (!lvn(&v, OC_PUTINDX, 0)) SYNTAX_ERROR(ERR_VAREXPECTED); if (OC_PUTINDX == v.oprval.tref->opcode) { dqdel(v.oprval.tref, exorder); dqrins(&targchain, exorder, v.oprval.tref); sub = v.oprval.tref; put_oc = OC_PUTINDX; if (TREF(temp_subs)) create_temporaries(sub, put_oc); } get = maketriple(OC_FNGET); get->operand[0] = v; put = maketriple(OC_STO); put->operand[0] = v; put->operand[1] = put_tref(s); break; case TK_ATSIGN: if (!indirection(&v)) SYNTAX_ERROR(ERR_VAREXPECTED); INSERT_INDSAVGLVN(control_slot, v, ANY_SLOT, 0); if (!used_glvn_slot) { used_glvn_slot = TRUE; first_control_slot = control_slot; } get = maketriple(OC_INDGET1); get->operand[0] = control_slot; put = maketriple(OC_STOGLVN); put->operand[0] = control_slot; put->operand[1] = put_tref(s); break; case TK_CIRCUMFLEX: s1 = (TREF(curtchain))->exorder.bl; if (!gvn()) SYNTAX_ERROR_NOREPORT_HERE; assert(OC_GVRECTARG != (TREF(curtchain))->opcode); /* gvn not shifted */ for (sub = (TREF(curtchain))->exorder.bl; sub != s1 ; sub = sub->exorder.bl) { put_oc = sub->opcode; if ((OC_GVNAME == put_oc) || (OC_GVNAKED == put_oc) || (OC_GVEXTNAM == put_oc)) break; } assert((OC_GVNAME == put_oc) || (OC_GVNAKED == put_oc) || (OC_GVEXTNAM == put_oc)); dqdel(sub, exorder); dqrins(&targchain, exorder, sub); if (TREF(temp_subs)) create_temporaries(sub, put_oc); get = maketriple(OC_FNGVGET); get->operand[0] = put_str(0, 0); put = maketriple(OC_GVPUT); put->operand[0] = put_tref(s); break; default: SYNTAX_ERROR(ERR_VAREXPECTED); } s->operand[0] = put_tref(get); /* Code to fetch args for target triple are on targchain. Put get there now too. */ dqrins(&targchain, exorder, get); if (!is_extract) { /* Set $[z]piece */ delimiter = newtriple(OC_PARAMETER); s->operand[1] = put_tref(delimiter); first = newtriple(OC_PARAMETER); delimiter->operand[1] = put_tref(first); /* Process delimiter string ($[z]piece only) */ if (TK_COMMA != TREF(window_token)) SYNTAX_ERROR(ERR_COMMA); advancewindow(); if (EXPR_FAIL == expr(&delimval, MUMPS_STR)) SYNTAX_ERROR_NOREPORT_HERE; assert(TRIP_REF == delimval.oprclass); } else { /* Set $[Z]Extract */ first = newtriple(OC_PARAMETER); s->operand[1] = put_tref(first); } /* Process first integer value */ if (TK_COMMA != TREF(window_token)) firstval = put_ilit(1); else { advancewindow(); if (EXPR_FAIL == expr(&firstval, MUMPS_INT)) SYNTAX_ERROR(ERR_COMMA); assert(TRIP_REF == firstval.oprclass); } first->operand[0] = firstval; if (first_is_lit = (OC_ILIT == firstval.oprval.tref->opcode)) { assert(ILIT_REF ==firstval.oprval.tref->operand[0].oprclass); first_val_lit = firstval.oprval.tref->operand[0].oprval.ilit; } if (TK_COMMA != TREF(window_token)) { /* There is no "last" value. Only if 1 char literal delimiter and * no "last" value can we generate shortcut code to op_set[z]p1 entry * instead of op_set[z]piece. Note if UTF8 mode is in effect, then this * optimization applies if the literal is one UTF8 char which may in * fact be up to 4 bytes but will still be passed as a single unsigned * integer. */ if (!is_extract) { delim_mval = &delimval.oprval.tref->operand[0].oprval.mlit->v; valid_char = TRUE; /* Basic assumption unles proven otherwise */ if ((OC_LIT == delimval.oprval.tref->opcode) && (1 == ((gtm_utf8_mode && (OC_SETPIECE == s->opcode)) ? MV_FORCE_LEN_DEC(delim_mval) : delim_mval->str.len))) { /* Single char delimiter for set $piece */ if (valid_char || (1 == delim_mval->str.len)) { /* This reference to a one character literal or a single * byte invalid utf8 character that needs to be turned into * an explict formated integer literal instead */ unichar.unichar_val = 0; if (!gtm_utf8_mode UTF8_ONLY(|| (OC_SETZPIECE == s->opcode))) { /* Single byte delimiter */ assert(1 == delim_mval->str.len); UNIX_ONLY(s->opcode = OC_SETZP1); VMS_ONLY(s->opcode = OC_SETP1); unichar.unibytes_val[0] = *delim_mval->str.addr; } # ifdef UTF8_SUPPORTED else { /* Potentially multiple bytes in one int */ assert(SIZEOF(int) >= delim_mval->str.len); memcpy(unichar.unibytes_val, delim_mval->str.addr, delim_mval->str.len); s->opcode = OC_SETP1; } # endif delimlit = (mint)unichar.unichar_val; delimiter->operand[0] = put_ilit(delimlit); delim1char = TRUE; } } } if (!delim1char) { /* Was not handled as a single char delim by code above either bcause it * was (1) not set $piece, or (2) was not a single char delim or (3) it was * not a VALID utf8 single char delim and badchar was inhibited. */ if (!is_extract) delimiter->operand[0] = delimval; last = newtriple(OC_PARAMETER); first->operand[1] = put_tref(last); last->operand[0] = first->operand[0]; /* start = end range */ } /* Generate test sequences for first/last to bypass the set operation if * first/last are not in a usable form */ if (first_is_lit) { if (1 > first_val_lit) { jmptrp1 = maketriple(OC_JMP); DQINSCURTARGCHAIN(jmptrp1); } /* note else no test necessary since first == last and are > 0 */ } else { /* Generate test for first being <= 0 */ jmptrp1 = maketriple(OC_COBOOL); jmptrp1->operand[0] = first->operand[0]; DQINSCURTARGCHAIN(jmptrp1); jmptrp1 = maketriple(OC_JMPLEQ); DQINSCURTARGCHAIN(jmptrp1); } } else { /* There IS a last value */ if (!is_extract) delimiter->operand[0] = delimval; last = newtriple(OC_PARAMETER); first->operand[1] = put_tref(last); advancewindow(); if (EXPR_FAIL == expr(&lastval, MUMPS_INT)) SYNTAX_ERROR_NOREPORT_HERE; assert(TRIP_REF == lastval.oprclass); last->operand[0] = lastval; /* Generate inline code to test first/last for usability and if found * lacking, branch around the getchain and the actual store so we avoid * setting the naked indicator so far as the target gvn is concerned. */ if (last_is_lit = (OC_ILIT == lastval.oprval.tref->opcode)) /* NOTE assignment */ { /* Case 1: last is a literal */ assert(ILIT_REF == lastval.oprval.tref->operand[0].oprclass); last_val_lit = lastval.oprval.tref->operand[0].oprval.ilit; if ((1 > last_val_lit) || ((first_is_lit && (first_val_lit > last_val_lit)))) { /* .. and first is a literal and one or both of them is no good * so unconditionally branch around the whole thing. */ jmptrp1 = maketriple(OC_JMP); DQINSCURTARGCHAIN(jmptrp1); } /* else case actually handled at next 'if' .. */ } else { /* Last is not literal. Do test if it is greater than 0 */ jmptrp1 = maketriple(OC_COBOOL); jmptrp1->operand[0] = last->operand[0]; DQINSCURTARGCHAIN(jmptrp1); jmptrp1 = maketriple(OC_JMPLEQ); DQINSCURTARGCHAIN(jmptrp1); } if (!last_is_lit || !first_is_lit) { /* Compare to check that last >= first */ jmptrp2 = maketriple(OC_VXCMPL); jmptrp2->operand[0] = first->operand[0]; jmptrp2->operand[1] = last->operand[0]; DQINSCURTARGCHAIN(jmptrp2); jmptrp2 = maketriple(OC_JMPGTR); DQINSCURTARGCHAIN(jmptrp2); } } } if (TK_RPAREN != TREF(window_token)) SYNTAX_ERROR(ERR_RPARENMISSING); advancewindow(); if (!parse_warn) { dqrins(&targchain, exorder, s); dqrins(&targchain, exorder, put); /* Put result operand on the chain. End of chain depends on whether or not * we are calling the shortcut or the full set-piece code */ if (delim1char) first->operand[1] = resptr; else last->operand[1] = resptr; /* Set jump targets if we did tests above. The function "tnxtarg" operates on "curtchain" * but we want to set targets based on "targchain", so temporarily switch them. Should not * use "save_curtchain" here as it might already be in use. */ save_curtchain1 = setcurtchain(&targchain); if (NULL != jmptrp1) tnxtarg(&jmptrp1->operand[0]); if (NULL != jmptrp2) tnxtarg(&jmptrp2->operand[0]); setcurtchain(save_curtchain1); } break; case TK_ASTERISK: /* The only way an asterisk can be detected here is if we are inside a list so mention this is * not possible and give error. */ stx_error(ERR_NOALIASLIST); return FALSE; default: SYNTAX_ERROR(ERR_VAREXPECTED); } if (FIRST_SETLEFT_NOTSEEN == first_setleft_invalid) { first_setleft_invalid = parse_warn; if (first_setleft_invalid) { /* We are not going to evaluate the right hand side of the SET command. This means * we should not evaluate any more setleft targets (whether they parse validly or not) * as well since their source (the RHS of the set) is undefined. To achieve this, we * switch to a temporary chain (both for curtchain and targchain) that will be discarded finally. */ /* save curtchain */ dqinit(&discardcurtchain, exorder); save_curtchain = setcurtchain(&discardcurtchain); assert(!curtchain_switched); curtchain_switched = TRUE; /* save targchain */ save_targchain = targchain; dqinit(&targchain, exorder); } } assert(FIRST_SETLEFT_NOTSEEN != first_setleft_invalid); if (!got_lparen) break; if (TK_COMMA == TREF(window_token)) advancewindow(); else { if (TK_RPAREN == TREF(window_token)) { advancewindow(); break; } else SYNTAX_ERROR(ERR_RPARENMISSING); } } if (TK_EQUAL != TREF(window_token)) SYNTAX_ERROR(ERR_EQUAL); advancewindow(); assert(FIRST_SETLEFT_NOTSEEN != first_setleft_invalid); temp_subs_was_FALSE = (FALSE == TREF(temp_subs)); /* Note down if temp_subs is FALSE at this point */ /* If we are in alias processing mode, the RHS cannot be an expression but must be one of a subscripted or unsubscripted * local variable, or a $$func(..) function call. */ if (!alias_processing) { /* Normal case first - evaluate expression creating triples on the current chain */ if (EXPR_FAIL == expr(result, MUMPS_EXPR)) SYNTAX_ERROR_NOREPORT_HERE; } else { /* Alias processing -- determine which of the three types of sources we have: var, subscripted var or $$func */ allow_dzwrtac_as_mident(); /* Allow source of $ZWRTACxxx as an mident */ if (TK_IDENT != TREF(window_token)) { /* Check if we have a $$func() call source */ if ((TK_DOLLAR == TREF(window_token)) && (TK_DOLLAR == TREF(director_token))) { /* Parse the function only with exfunc(). We definitely do not want an expression */ TREF(temp_subs) = TRUE; /* RHS $$ function detected - need temporary */ advancewindow(); if (!exfunc(result, TRUE)) SYNTAX_ERROR_NOREPORT_HERE; if (OC_SETALSIN2ALSCT == put->opcode) /* Change opcode to create an alias container from the returned alias */ put->opcode = OC_SETFNRETIN2ALSCT; else { /* Change opcode to create an alias from the returned alias */ assert(OC_SETALS2ALS == put->opcode); put->opcode = OC_SETFNRETIN2ALS; } } else /* Else, only local variables allowed as aliases */ SYNTAX_ERROR(ERR_ALIASEXPECTED); } else { /* Alias var source */ if (('$' == *(TREF(window_ident)).addr) && (STR_LIT_LEN(DOLLAR_ZWRTAC) >= (TREF(window_ident)).len)) /* $ZWRTAC is not allowed as a "source" value. Must be a $ZWRTACn format */ SYNTAX_ERROR(ERR_DZWRNOALIAS); if (TK_LPAREN == TREF(director_token)) { /* Subscripted local variable - have alias container. * The storing opcode set into the "put" triple at the top of this routine was * set assuming the source was an alias. Now that we know the source is actually * an alias container (and hence a different data type), we need to adjust the * opcode accordingly. */ if (OC_SETALS2ALS == put->opcode) put->opcode = OC_SETALSCTIN2ALS; else { assert(OC_SETALSIN2ALSCT == put->opcode); put->opcode = OC_SETALSCT2ALSCT; } } /* For RHS processing, both alias var and alias container vars have their lv_val addr * passed so normal var processing applies. */ if (!lvn(result, OC_GETINDX, 0)) SYNTAX_ERROR(ERR_ALIASEXPECTED); } } if (first_setleft_invalid) { /* switch from the temporary chain back to the current execution chain */ assert(curtchain_switched); RESTORE_CURTCHAIN_IF_NEEDED; /* does a setcurtchain(save_curtchain) */ targchain = save_targchain; } /* Now add in the left-hand side triples */ assert(!curtchain_switched); obp = (TREF(curtchain))->exorder.bl; dqadd(obp, &targchain, exorder); /* this is a violation of info hiding */ /* Check if "temp_subs" was FALSE originally but got set to TRUE as part of evaluating the right hand side * (for example, if rhs had $$ or $& or $INCR usages). If so need to create temporaries. */ if (TREF(temp_subs) && temp_subs_was_FALSE && (NULL != sub)) create_temporaries(sub, put_oc); TREF(temp_subs) = FALSE; if (used_glvn_slot) { /* Free up slots we're done with. */ ref = newtriple(OC_GLVNPOP); ref->operand[0] = first_control_slot; } return TRUE; } /* Prior to Alias support, the ZWRITE command was able to dump the entire local variable environment such that it could be * reloaded by Xecuting the dumped lines. With the addition of Alias type variables, the output lines not only include the * variable content but their alias associations as well. One aspect of this is the $ZWRTAC variable which is a temporary * "variable" which we allow to hold the content of what would otherwise be "orphaned data" or data which has a container * pointing to it but is not referenced by a base variable. During the reload operation, it is this orphaned data that is * put into the $ZWRTAC environment. The first orphaned array is put into $ZWRTAC1(...), the second into $ZWRTAC2(..) and * so on. Setting the $ZWRTAC variable itself to null (or to any value actually - null only is not enforced) causes all of * the $ZWRTAC variables to be KILL *'d. The only syntactic allowances for using $ZWRTACxxx as an mident in GTM are in this * SET statement. We do not support its use in any other statement type. This routine allows us to modify the tokens so that * if the current token is a '$' and the next token is ZWRTACxxx, we can combine them into a single token and the parser * need not be further modified. */ void allow_dzwrtac_as_mident(void) { char_ptr_t chrp, chrpmin, chrplast; int movlen; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TK_DOLLAR != TREF(window_token)) return; /* Couldn't be $ZWRTACxxx without first $ */ if (TK_IDENT != TREF(director_token)) return; /* Couldn't be $ZWRTACxxx without token 2nd part */ if (STR_LIT_LEN("ZWRTAC") > (TREF(director_ident)).len) return; /* Couldn't be $ZALAISxxx without sufficient length of name */ if (0 != MEMCMP_LIT((TREF(director_ident)).addr, "ZWRTAC")) return; /* Couldn't be $ZWRTACxxx without ZWRTAC as first part of token */ /* We need to shift the existing token over 1 byte to make room for insertion of the '$' prefix. Normally, * we wouldn't want to do this as we are verifying but since if the verification fails the code path will * raise an error and since the error does not use this token buffer, we are safe in migrating while * we do the verification check. Saves us having to scan the line backwards via memmove() again below. So * verify the token suffix is all numeric while we do our shift. */ movlen = ((MAX_MIDENT_LEN) > (TREF(director_ident)).len) ? (TREF(director_ident)).len : MAX_MIDENT_LEN - 1; for (chrplast = (TREF(director_ident)).addr + movlen, chrp = chrplast - 1, chrpmin = (TREF(director_ident)).addr + STR_LIT_LEN("ZWRTAC") - 1; chrp > chrpmin; --chrp, --chrplast) { if (!ISDIGIT_ASCII((int)*chrp)) return; /* Couldn't be $ZWRTACxxx without all numeric suffix (if exists) */ *chrplast = *chrp; } /* Verification (and shift) complete -- finish modifying director token */ MEMCPY_LIT((TREF(director_ident)).addr, DOLLAR_ZWRTAC); (TREF(director_ident)).len = movlen + 1; /* Nnw forward the scan to pull director token values into window token for use by our caller */ advancewindow(); assert(TK_IDENT == TREF(window_token)); } fis-gtm-V7.0-005/sr_port/m_tcommit.c0000755000032200000250000000145014342376331016202 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "cmd.h" error_def(ERR_SPOREOL); int m_tcommit(void) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TK_EOL != (TREF(window_token)) && (TK_SPACE != TREF(window_token))) { stx_error(ERR_SPOREOL); return FALSE; } newtriple(OC_TCOMMIT); return TRUE; } fis-gtm-V7.0-005/sr_port/m_trestart.c0000755000032200000250000000153614342376331016403 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "cmd.h" error_def(ERR_SPOREOL); int m_trestart(void) { triple *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TK_EOL != TREF(window_token)) && (TK_SPACE != TREF(window_token))) { stx_error(ERR_SPOREOL); return FALSE; } ref = newtriple(OC_TRESTART); ref->operand[0] = put_ilit(1); return TRUE; } fis-gtm-V7.0-005/sr_port/m_trollback.c0000755000032200000250000000221614342376331016504 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "indir_enum.h" #include "opcode.h" #include "toktyp.h" #include "cmd.h" int m_trollback(void) { oprtype x; triple *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TK_SPACE == TREF(window_token)) || (TK_EOL == TREF(window_token))) { ref = newtriple(OC_TROLLBACK); ref->operand[0] = put_ilit(0); return TRUE; } else { switch (expr(&x, MUMPS_INT)) { case EXPR_FAIL: return FALSE; case EXPR_GOOD: ref = newtriple(OC_TROLLBACK); ref->operand[0] = x; return TRUE; case EXPR_INDR: make_commarg(&x,indir_trollback); return TRUE; } } return FALSE; /* This should never get executed, added to make compiler happy */ } fis-gtm-V7.0-005/sr_port/m_tstart.c0000755000032200000250000001133514342376331016052 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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_string.h" #include "compiler.h" #include "opcode.h" #include "toktyp.h" #include "nametabtyp.h" #include "indir_enum.h" #include "advancewindow.h" #include "cmd.h" #include "namelook.h" static readonly nametabent tst_param_names[] = { {1,"S"} ,{6,"SERIAL"} ,{1,"T"} ,{8,"TRANSACT*"} }; /* Offset of letter with dev_param_names */ static readonly unsigned char tst_param_index[27] = { /* A B C D E F G H I J K L M N */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* O P Q R S T U V W X Y Z end */ 0, 0, 0, 0, 0, 2, 4, 4, 4, 4, 4, 4, 4 }; error_def(ERR_RPARENMISSING); error_def(ERR_TSTRTPARM); error_def(ERR_VAREXPECTED); int m_tstart(void) { boolean_t bad_parse, has_lpar, has_ser, has_tid; int count, n; mval dummyid; oprtype tmparg; triple *fetch, *ref, *s, *ser, *tid, *varlst, *varnext, *varp; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; count = -1; /* indicates not restartable */ varlst = newtriple(OC_PARAMETER); switch (TREF(window_token)) { case TK_IDENT: count = 1; varlst->operand[1] = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); advancewindow(); break; case TK_ASTERISK: count = -2; /* indicates save all variables */ advancewindow(); break; case TK_ATSIGN: if (!indirection(&tmparg)) return FALSE; if (TK_COLON == TREF(window_token)) { ref = newtriple(OC_COMMARG); ref->operand[0] = tmparg; ref->operand[1] = put_ilit((mint) indir_tstart); return TRUE; } else { count = 1; fetch = newtriple(OC_INDLVARG); fetch->operand[0] = tmparg; varlst->operand[1] = put_tref(fetch); } break; case TK_EOL: case TK_SPACE: break; case TK_LPAREN: varp = varlst; for(count = 0; ;) { advancewindow(); switch (TREF(window_token)) { case TK_IDENT: varnext = newtriple(OC_PARAMETER); varnext->operand[0] = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); varp->operand[1] = put_tref(varnext); varp = varnext; advancewindow(); count++; break; case TK_ATSIGN: if (!indirection(&tmparg)) return FALSE; s = newtriple(OC_INDLVARG); s->operand[0] = tmparg; varnext = newtriple(OC_PARAMETER); varnext->operand[0] = put_tref(s); varp->operand[1] = put_tref(varnext); varp = varnext; count++; break; case TK_RPAREN: break; default: stx_error(ERR_VAREXPECTED); return FALSE; } if (TK_COMMA != TREF(window_token)) break; } if (TK_RPAREN != TREF(window_token)) { stx_error(ERR_RPARENMISSING); return FALSE; } advancewindow(); break; } varlst->operand[0] = put_ilit(count); has_ser = has_tid = FALSE; tid = newtriple(OC_PARAMETER); if (TK_COLON == TREF(window_token)) { advancewindow(); if (has_lpar = (TK_LPAREN == TREF(window_token))) advancewindow(); for(;;) { bad_parse = TRUE; if (TK_IDENT == TREF(window_token)) { if ((0 <= (n = namelook(tst_param_index, tst_param_names, (TREF(window_ident)).addr, (TREF(window_ident)).len)))) { /* NOTE assignment above */ if (n < 2 && !has_ser) { has_ser = TRUE; bad_parse = FALSE; advancewindow(); } else if (!has_tid) { advancewindow(); if (TK_EQUAL == TREF(window_token)) { advancewindow(); if (EXPR_FAIL == expr(&tid->operand[0], MUMPS_EXPR)) return FALSE; has_tid = TRUE; bad_parse = FALSE; } } } } if (bad_parse) { stx_error(ERR_TSTRTPARM); return FALSE; } if (!has_lpar || (TK_RPAREN == TREF(window_token))) break; if (TK_COLON != TREF(window_token)) { stx_error(ERR_TSTRTPARM); return FALSE; } advancewindow(); } if (has_lpar) { assert(TK_RPAREN == TREF(window_token)); advancewindow(); } } if (!has_tid) { memset(&dummyid, 0, SIZEOF(mval)); /* Initialize so unused fields don't cause object hash differences */ dummyid.mvtype = MV_STR; dummyid.str.len = 0; tid->operand[0] = put_lit(&dummyid); } tid->operand[1] = put_tref(varlst); ref = newtriple(OC_TSTART); ref->operand[0] = put_ilit(has_ser); ref->operand[1] = put_tref(tid); return TRUE; } fis-gtm-V7.0-005/sr_port/m_use.c0000755000032200000250000000346714342376331015334 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "io_params.h" #include "indir_enum.h" #include "advancewindow.h" #include "cmd.h" #include "deviceparameters.h" int m_use(void) { boolean_t inddevparms; static readonly unsigned char empty_plist[1] = { iop_eol }; int rval; oprtype sopr, plist, devpopr; triple *indref, *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; inddevparms = FALSE; if (EXPR_FAIL == (rval = expr(&sopr, MUMPS_STR))) /* NOTE assignment */ return FALSE; if (TK_COLON != TREF(window_token)) { /* Single parameter */ if (EXPR_INDR == rval) { /* Indirect entire parameter list */ make_commarg(&sopr, indir_use); return TRUE; } else /* default device parms */ plist = put_str((char *)empty_plist, SIZEOF(empty_plist)); } else { /* Have device parms. Determine type */ advancewindow(); if (TK_ATSIGN == TREF(window_token)) { /* Have indirect device parms */ if (!indirection(&devpopr)) return FALSE; indref = newtriple(OC_INDDEVPARMS); indref->operand[0] = devpopr; indref->operand[1] = put_ilit(IOP_USE_OK); inddevparms = TRUE; } else { /* Process device parameters now */ if (!deviceparameters(&plist, IOP_USE_OK)) return FALSE; } } ref = newtriple(OC_USE); ref->operand[0] = sopr; ref->operand[1] = !inddevparms ? plist : put_tref(indref); return TRUE; } fis-gtm-V7.0-005/sr_port/m_view.c0000755000032200000250000000316114342376331015501 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "toktyp.h" #include "opcode.h" #include "indir_enum.h" #include "advancewindow.h" #include "cmd.h" error_def(ERR_FCHARMAXARGS); int m_view(void) { oprtype argv[CHARMAXARGS], *argp; unsigned short count; triple *view, *parm, *parm1; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; argp = &argv[0]; count = 0; switch (expr(argp, MUMPS_EXPR)) { case EXPR_FAIL: return FALSE; case EXPR_INDR: if (TK_COLON != TREF(window_token)) { make_commarg(argp, indir_view); return TRUE; } /* caution: fall through */ case EXPR_GOOD: view = maketriple(OC_VIEW); parm = newtriple(OC_PARAMETER); view->operand[1] = put_tref(parm); parm->operand[0] = *argp; count++; argp++; break; } for (;;) { if (TK_COLON != TREF(window_token)) break; advancewindow(); if (EXPR_FAIL == expr(argp, MUMPS_EXPR)) return FALSE; parm1 = newtriple(OC_PARAMETER); parm->operand[1] = put_tref(parm1); parm1->operand[0] = *argp; parm = parm1; count++; argp++; if (CHARMAXARGS <= count) { stx_error(ERR_FCHARMAXARGS); return FALSE; } } view->operand[0] = put_ilit(count); ins_triple(view); return TRUE; } fis-gtm-V7.0-005/sr_port/m_write.c0000755000032200000250000000674514342376331015674 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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_string.h" #include "compiler.h" #include "stringpool.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "advancewindow.h" #include "cmd.h" #include "rwformat.h" GBLREF spdesc stringpool; GBLREF bool devctlexp; error_def(ERR_STRINGOFLOW); #define STO_LLPTR(X) (llptr ? *++llptr = (X) : 0) #define LITLST_TOP (&litlst[(SIZEOF(litlst) / SIZEOF(triple *)) - 2]) int m_write(void) { boolean_t concat_ok; char *cp; int lnx; mstr *msp; mval lit; oprtype *oprptr, x; triple *litlst[128], **llptr, **ltop, **ptx, *ref, *t1; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; llptr = litlst; ltop = 0; *llptr = 0; for (;;) { devctlexp = FALSE; switch (TREF(window_token)) { case TK_ASTERISK: advancewindow(); if (EXPR_FAIL == expr(&x, MUMPS_INT)) return FALSE; assert(TRIP_REF == x.oprclass); ref = newtriple(OC_WTONE); ref->operand[0] = x; STO_LLPTR((OC_ILIT == x.oprval.tref->opcode) ? ref : 0); break; case TK_QUESTION: case TK_EXCLAIMATION: case TK_HASH: case TK_SLASH: if (!rwformat()) return FALSE; STO_LLPTR(0); break; default: concat_ok = ((0 != TREF(side_effect_handling)) || (TK_LPAREN != TREF(window_token))); switch (expr(&x, MUMPS_STR)) { case EXPR_FAIL: return FALSE; case EXPR_GOOD: assert(TRIP_REF == x.oprclass); if (devctlexp) { ref = newtriple(OC_WRITE); ref->operand[0] = x; STO_LLPTR(0); } else if (concat_ok && (OC_CAT == x.oprval.tref->opcode)) wrtcatopt(x.oprval.tref, &llptr, LITLST_TOP); else { ref = newtriple(OC_WRITE); ref->operand[0] = x; STO_LLPTR((OC_LIT == x.oprval.tref->opcode) ? ref : 0); } break; case EXPR_INDR: make_commarg(&x, indir_write); STO_LLPTR(0); break; default: assert(FALSE); } break; } if (TK_COMMA != TREF(window_token)) break; advancewindow(); if (LITLST_TOP <= llptr) { *++llptr = 0; ltop = llptr; llptr = 0; } } STO_LLPTR(0); if (ltop) llptr = ltop; for (ptx = litlst ; ptx < llptr ; ptx++) { if (*ptx && *(ptx + 1)) { lit.mvtype = MV_STR; lit.str.addr = cp = (char *)stringpool.free; CLEAR_MVAL_BITS(&lit); for (t1 = ref = *ptx++ ; ref ; ref = *ptx++) { if (OC_WRITE == ref->opcode) { msp = &(ref->operand[0].oprval.tref->operand[0].oprval.mlit->v.str); lnx = msp->len; ENSURE_STP_FREE_SPACE(lnx); memcpy(cp, msp->addr, lnx); cp += lnx; } else { assert(OC_WTONE == ref->opcode); ENSURE_STP_FREE_SPACE(1); *cp++ = ref->operand[0].oprval.tref->operand[0].oprval.ilit; } ref->operand[0].oprval.tref->opcode = OC_NOOP; ref->opcode = OC_NOOP; ref->operand[0].oprval.tref->operand[0].oprclass = NO_REF; ref->operand[0].oprclass = NO_REF; } ptx--; stringpool.free = (unsigned char *) cp; lit.str.len = INTCAST(cp - lit.str.addr); t1->opcode = OC_WRITE; t1->operand[0] = put_lit(&lit); } } return TRUE; } fis-gtm-V7.0-005/sr_port/m_xecute.c0000644000032200000250000001415114342376331016022 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "compiler.h" #include "mdq.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mmemory.h" #include "advancewindow.h" #include "cmd.h" #include "lb_init.h" #include "error.h" GBLREF boolean_t run_time; GBLREF int (*indir_fcn[])(), source_column; GBLREF int4 aligned_source_buffer, pending_errtriplecode; STATICDEF parse_save_block *parse_state_ptr; STATICDEF char *local_source_buffer; error_def(ERR_INDRMAXLEN); int m_xecute(void) /* compiler module for XECUTE */ { DEBUG_ONLY(boolean_t my_run_time = run_time); int rval; mstr *src; oprtype *cr, x; triple *obp, *oldchain, *ref0, *ref1, tmpchain, *triptr; mval *v; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; dqinit(&tmpchain, exorder); oldchain = setcurtchain(&tmpchain); switch (expr(&x, MUMPS_STR)) { case EXPR_FAIL: setcurtchain(oldchain); return FALSE; case EXPR_INDR: if (TK_COLON != TREF(window_token)) { make_commarg(&x, indir_xecute); break; } /* caution: fall through ??? maybe ??? */ case EXPR_GOOD: for (ref0 = tmpchain.exorder.bl, ref1 = ref0->exorder.bl; OC_NOOP == ref1->opcode; ref1 = ref1->exorder.bl) ; /* WARNING very evil violations of information hiding above and below */ if (!run_time && (OC_LIT == ref0->opcode) && (&tmpchain == ref1)) { /* Just found a literal, and only one, and we are not already at run time. * Can't drive the parsing with the source because there may be emedded quotes, rather must use the literal * The code in this block sorta/kinda does things like comp_init and op_commarg between a save and restore * of parser state. If the parse fails or runs into a GOTO, NEW, QUIT or (nested) XECUTE it reverts to * producing the code for a run time evaluation of the literal. GOTO and QUIT must deal with the XECUTE * frame and that's why they cause it to defer to run time; they use TREF(xecute_literal_parse), as does * stc_error and BLOWN_FOR, to recognize this "trial" parse and prevent it from causing too much carnage * Should anything else call for a similar approach things might/should be considered for refactoring and * adjustments to some names. */ src = &x.oprval.tref->operand[0].oprval.mlit->v.str; dqinit(&tmpchain, exorder); if (MAX_SRCLINE < (unsigned)src->len) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(3) ERR_INDRMAXLEN, 1, MAX_SRCLINE); /* save the parse state, point the compiler at the literal and see what happens */ if (NULL == parse_state_ptr) parse_state_ptr = malloc(SIZEOF(parse_save_block)); SAVE_PARSE_STATE(parse_state_ptr); if (NULL == local_source_buffer) local_source_buffer = malloc(MAX_SRCLINE + 1); TREF(lexical_ptr) = (TREF(source_buffer)).addr = local_source_buffer; memcpy((TREF(source_buffer)).addr, src->addr, src->len); *((TREF(source_buffer)).addr + src->len) = '\0'; (TREF(source_buffer)).len = src->len + 1; TREF(block_level) = 0; lb_init(); assert(!TREF(xecute_literal_parse)); run_time = TREF(xecute_literal_parse) = TRUE; for (;;) { rval = (*indir_fcn[indir_linetail])(); if (OC_FORLOOP == tmpchain.exorder.bl->opcode) /* Evil violation of information hiding */ rval = EXPR_FAIL; /* FOR termination would jmp too far (to EOL) */ if ((EXPR_FAIL == rval) || (TK_COMMA != TREF(window_token))) break; /* Didn't work or processed all arguments */ advancewindow(); } run_time = TREF(xecute_literal_parse) = FALSE; RESTORE_PARSE_STATE(parse_state_ptr); /* restore the parse state to the original source */ if (EXPR_FAIL == rval) { /* not so good - remove the failed chain and just leave the literal for the run time form */ TREF(last_source_column) = source_column - 1; dqinit(&tmpchain, exorder); ins_triple(ref0); pending_errtriplecode = 0; /* forget the error - leave it to runtime */ TREF(source_error_found) = 0; } } else rval = EXPR_FAIL; if (EXPR_FAIL == rval) { /* either not useable literal(s) or compiling the literal(s) failed, in which case, leave it to run time */ ref0 = maketriple(OC_COMMARG); ref0->operand[0] = x; ref0->operand[1] = put_ilit(indir_linetail); ins_triple(ref0); } } setcurtchain(oldchain); if (TK_COLON == TREF(window_token)) { advancewindow(); cr = (oprtype *)mcalloc(SIZEOF(oprtype)); if (!bool_expr(FALSE, cr)) return FALSE; for (triptr = (TREF(curtchain))->exorder.bl; OC_NOOP == triptr->opcode; triptr = triptr->exorder.bl) ; if (OC_LIT == triptr->opcode) { /* it is a literal so optimize it */ v = &triptr->operand[0].oprval.mlit->v; unuse_literal(v); dqdel(triptr, exorder); if (0 == MV_FORCE_BOOL(v)) setcurtchain(oldchain); /* it's FALSE so just discard the whole thing */ else { /* it's TRUE so treat as if no argument postconditional */ obp = oldchain->exorder.bl; dqadd(obp, &tmpchain, exorder); /* violates info hiding */ } return TRUE; } if ((TREF(expr_start) != TREF(expr_start_orig)) && (OC_NOOP != (TREF(expr_start))->opcode)) { triptr = newtriple(OC_GVRECTARG); triptr->operand[0] = put_tref(TREF(expr_start)); } obp = oldchain->exorder.bl; dqadd(obp, &tmpchain, exorder); /* violates info hiding */ if ((TREF(expr_start) != TREF(expr_start_orig)) && (OC_NOOP != (TREF(expr_start))->opcode)) { ref0 = newtriple(OC_JMP); ref1 = newtriple(OC_GVRECTARG); ref1->operand[0] = put_tref(TREF(expr_start)); *cr = put_tjmp(ref1); tnxtarg(&ref0->operand[0]); } else tnxtarg(cr); return TRUE; } obp = oldchain->exorder.bl; dqadd(obp, &tmpchain, exorder); /* violates info hiding */ assert(my_run_time == run_time); return TRUE; } fis-gtm-V7.0-005/sr_port/m_zallocate.c0000755000032200000250000000363014342376331016506 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "iotimer.h" #include "indir_enum.h" #include "advancewindow.h" #include "cmd.h" error_def(ERR_RPARENMISSING); LITREF mval literal_notimeout; int m_zallocate(void) { boolean_t indirect; oprtype indopr; triple *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; newtriple(OC_RESTARTPC); indirect = FALSE; newtriple(OC_LKINIT); switch (TREF(window_token)) { case TK_ATSIGN: if (!indirection(&indopr)) return FALSE; ref = newtriple(OC_COMMARG); ref->operand[0] = indopr; if (TK_COLON != TREF(window_token)) { ref->operand[1] = put_ilit((mint)indir_zallocate); return TRUE; } ref->operand[1] = put_ilit((mint)indir_nref); indirect = TRUE; break; case TK_LPAREN: do { advancewindow(); if (EXPR_FAIL == nref()) return FALSE; } while (TK_COMMA == TREF(window_token)); if (TK_RPAREN != TREF(window_token)) { stx_error(ERR_RPARENMISSING); return FALSE; } advancewindow(); break; default: if (EXPR_FAIL == nref()) return FALSE; break; } ref = maketriple(OC_ZALLOCATE); if (TK_COLON != TREF(window_token)) { ref->operand[0] = put_lit((mval *)&literal_notimeout); ins_triple(ref); } else { advancewindow(); if (EXPR_FAIL == expr(&(ref->operand[0]), MUMPS_EXPR)) return EXPR_FAIL; ins_triple(ref); newtriple(OC_TIMTRU); } return EXPR_GOOD; } fis-gtm-V7.0-005/sr_port/m_zattach.c0000755000032200000250000000223014342376331016161 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "cmd.h" int m_zattach(void) { oprtype x; triple *triptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TK_EOL == TREF(window_token)) || (TK_SPACE == TREF(window_token))) { triptr = newtriple(OC_ZATTACH); triptr->operand[0] = put_str("",0); return TRUE; } else { switch (expr(&x, MUMPS_STR)) { case EXPR_FAIL: return FALSE; case EXPR_GOOD: triptr = newtriple(OC_ZATTACH); triptr->operand[0] = x; return TRUE; case EXPR_INDR: make_commarg(&x,indir_zattach); return TRUE; } } return FALSE; /* This should never get executed, added to make compiler happy */ } fis-gtm-V7.0-005/sr_port/m_zbreak.c0000755000032200000250000000651514342376331016013 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "advancewindow.h" #include "cmd.h" #define CANCEL_ONE -1 #define CANCEL_ALL -2 GBLREF boolean_t run_time; GBLREF mident routine_name; LITREF mident zero_ident; error_def(ERR_LABELEXPECTED); error_def(ERR_RTNNAME); int m_zbreak(void) { boolean_t cancel, cancel_all, dummybool, is_count; oprtype action, count, label, offset, routine; triple *next, *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; label = put_str((char *)zero_ident.addr, zero_ident.len); cancel_all = FALSE; action = put_str("B", 1); if (TK_MINUS == TREF(window_token)) { advancewindow(); cancel = TRUE; count = put_ilit((mint)CANCEL_ONE); } else { cancel = FALSE; count = put_ilit((mint)0); } if (TK_ASTERISK == TREF(window_token)) { if (cancel) { advancewindow(); cancel_all = TRUE; if (!run_time) routine = put_str(routine_name.addr, routine_name.len); else routine = put_tref(newtriple(OC_CURRTN)); offset = put_ilit((mint) 0); count = put_ilit((mint) CANCEL_ALL); } else { stx_error(ERR_LABELEXPECTED); return FALSE; } } else { offset = put_ilit((mint) 0); if (!lref(&label,&offset, TRUE, indir_zbreak, !cancel, &dummybool)) return FALSE; if (label.oprclass == TRIP_REF && label.oprval.tref->opcode == OC_COMMARG) return TRUE; if (TK_CIRCUMFLEX != TREF(window_token)) { /* Routine not specified, assume current routine */ routine = PUT_CURRENT_RTN; } else { advancewindow(); switch (TREF(window_token)) { case TK_IDENT: # ifdef GTM_TRIGGER if (TK_HASH == TREF(director_token)) /* Coagulate tokens as necessary (and available) to allow '#' in the rtn name */ advwindw_hash_in_mname_allowed(); # endif routine = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); advancewindow(); break; case TK_ATSIGN: if (!indirection(&routine)) return FALSE; break; default: stx_error(ERR_RTNNAME); return FALSE; } } if (!cancel && (TK_COLON == TREF(window_token))) { advancewindow(); if (TK_COLON == TREF(window_token)) { is_count = TRUE; action = put_str("B",1); } else { if (EXPR_FAIL == expr(&action, MUMPS_STR)) return FALSE; is_count = (TK_COLON == TREF(window_token)); } if (is_count) { advancewindow(); if (EXPR_FAIL == expr(&count, MUMPS_INT)) return FALSE; } } } ref = newtriple(OC_SETZBRK); ref->operand[0] = label; next = newtriple(OC_PARAMETER); ref->operand[1] = put_tref(next); next->operand[0] = offset; ref = newtriple(OC_PARAMETER); next->operand[1] = put_tref(ref); ref->operand[0] = routine; next = newtriple(OC_PARAMETER); ref->operand[1] = put_tref(next); next->operand[0] = action; ref = newtriple(OC_PARAMETER); next->operand[1] = put_tref(ref); ref->operand[0] = count; return TRUE; } fis-gtm-V7.0-005/sr_port/m_zcompile.c0000755000032200000250000000145114342376331016351 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "cmd.h" int m_zcompile(void) { triple *triptr; oprtype x; if(EXPR_FAIL == expr(&x, MUMPS_STR)) return FALSE; triptr = newtriple(OC_ZCOMPILE); triptr->operand[0] = x; triptr->operand[1] = put_ilit(FALSE); /* ignore_dollar_zcompile arg */ return TRUE; } fis-gtm-V7.0-005/sr_port/m_zcontinue.c0000755000032200000250000000115514342376331016546 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "cmd.h" int m_zcontinue(void) { oprtype x; newtriple(OC_ZCONT); return TRUE; } fis-gtm-V7.0-005/sr_port/m_zdeallocate.c0000755000032200000250000000307014342376331017015 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "iotimer.h" #include "indir_enum.h" #include "advancewindow.h" #include "cmd.h" LITREF mval literal_notimeout; error_def(ERR_RPARENMISSING); int m_zdeallocate(void) { oprtype indopr; triple *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; newtriple(OC_LKINIT); switch(TREF(window_token)) { case TK_EOL: case TK_SPACE: break; case TK_ATSIGN: if (!indirection(&indopr)) return FALSE; ref = newtriple(OC_COMMARG); ref->operand[0] = indopr; ref->operand[1] = put_ilit((mint)indir_zdeallocate); return TRUE; break; case TK_LPAREN: do { advancewindow(); if (EXPR_FAIL == nref()) return FALSE; } while (TK_COMMA == TREF(window_token)); if (TK_RPAREN != TREF(window_token)) { stx_error(ERR_RPARENMISSING); return FALSE; } advancewindow(); break; default: if (EXPR_FAIL == nref()) return FALSE; break; } ref = newtriple(OC_ZDEALLOCATE); ref->operand[0] = put_lit((mval *)&literal_notimeout); return EXPR_GOOD; } fis-gtm-V7.0-005/sr_port/m_zedit.c0000755000032200000250000000302614342376331015646 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "svnames.h" #include "advancewindow.h" #include "cmd.h" int m_zedit(void) { int rval; oprtype file,opts; triple *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TK_EOL == TREF(window_token)) || (TK_SPACE == TREF(window_token)) || (TK_COLON == TREF(window_token))) { ref = newtriple(OC_SVGET); ref->operand[0] = put_ilit(SV_ZSOURCE); file = put_tref(ref); if (TK_COLON == TREF(window_token)) { advancewindow(); if (EXPR_FAIL == expr(&opts, MUMPS_STR)) return FALSE; } else opts = put_str("",0); } else { if (EXPR_FAIL == (rval = expr(&file, MUMPS_STR))) /* NOTE assignment */ return FALSE; if (TK_COLON != TREF(window_token)) { if (EXPR_INDR == rval) { make_commarg(&file,indir_zedit); return TRUE; } opts = put_str("",0); } else { advancewindow(); if (EXPR_FAIL == expr(&opts, MUMPS_STR)) return FALSE; } } ref = newtriple(OC_ZEDIT); ref->operand[0] = file; ref->operand[1] = opts; return TRUE; } fis-gtm-V7.0-005/sr_port/m_zgoto.c0000755000032200000250000000523114342376331015671 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2010-2020 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_string.h" #include "mdq.h" #include "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mmemory.h" #include "advancewindow.h" #include "cmd.h" error_def(ERR_COLON); int m_zgoto(void) { int rval; oprtype *cr, quits; triple *obp, *oldchain, *ref0, *ref1, tmpchain, *triptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; dqinit(&tmpchain, exorder); oldchain = setcurtchain(&tmpchain); if ((TK_EOL == TREF(window_token)) || (TK_SPACE == TREF(window_token))) { /* Default zgoto level is 1 */ quits = put_ilit(1); rval = EXPR_GOOD; } else if (EXPR_FAIL == (rval = expr(&quits, MUMPS_INT))) /* NOTE assignment */ { setcurtchain(oldchain); return FALSE; } if ((EXPR_INDR != rval) && ((TK_EOL == TREF(window_token)) || (TK_SPACE == TREF(window_token)))) { /* Only level parm supplied (no entry ref) - job for op_zg1 */ setcurtchain(oldchain); obp = oldchain->exorder.bl; dqadd(obp, &tmpchain, exorder); /* this is a violation of info hiding */ ref0 = newtriple(OC_ZG1); ref0->operand[0] = quits; return TRUE; } if (TK_COLON != TREF(window_token)) { /* First arg parsed, not ending in ":". Better have been indirect */ setcurtchain(oldchain); if (EXPR_INDR != rval) { stx_error(ERR_COLON); return FALSE; } make_commarg(&quits, indir_zgoto); obp = oldchain->exorder.bl; dqadd(obp, &tmpchain, exorder); /* this is a violation of info hiding */ return TRUE; } advancewindow(); if (TK_COLON != TREF(window_token)) { if (!entryref(OC_NOOP, OC_PARAMETER, (mint)indir_goto, FALSE, FALSE, TRUE)) { setcurtchain(oldchain); return FALSE; } ref0 = maketriple(OC_ZGOTO); ref0->operand[0] = quits; ref0->operand[1] = put_tref(tmpchain.exorder.bl); ins_triple(ref0); setcurtchain(oldchain); } else { ref0 = maketriple(OC_ZG1); ref0->operand[0] = quits; ins_triple(ref0); setcurtchain(oldchain); } if (TK_COLON == TREF(window_token)) return m_goto_postcond(oldchain, &tmpchain); /* post conditional expression */ obp = oldchain->exorder.bl; dqadd(obp, &tmpchain, exorder); /* this is a violation of info hiding */ return TRUE; } fis-gtm-V7.0-005/sr_port/m_zhalt.c0000644000032200000250000000330214342376331015643 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2011-2017 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 "compiler.h" #include "indir_enum.h" #include "opcode.h" #include "cmd.h" #include "toktyp.h" /* Halt the process similar to HALT but ZHALT allows specification of a return code. If the command does not specify a return * code, this uses 0 as a default, so that case appears the same to the shell as HALT. However, they are subject to different * potential restrictions, so they use a flag that separates the two. */ int m_zhalt(void) { triple *triptr; oprtype ot; int status; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TK_SPACE == TREF(window_token)) || (TK_EOL == TREF(window_token))) { /* no argument means return 0 */ triptr = newtriple(OC_ZHALT); triptr->operand[0] = put_ilit(1); triptr->operand[1] = put_ilit(0); return TRUE; } switch (status = expr(&ot, MUMPS_INT)) /* NOTE assignment */ { case EXPR_FAIL: return FALSE; case EXPR_GOOD: triptr = newtriple(OC_ZHALT); triptr->operand[0] = put_ilit(1); triptr->operand[1] = ot; return TRUE; case EXPR_INDR: make_commarg(&ot, indir_zhalt); return TRUE; default: assertpro(FALSE); } return FALSE; /* This should never get executed, added to make compiler happy */ } fis-gtm-V7.0-005/sr_port/m_zhelp.c0000755000032200000250000000266714342376331015663 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "advancewindow.h" #include "cmd.h" int m_zhelp (void) { int rval; oprtype lib, text; triple *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TK_EOL == TREF(window_token)) || (TK_SPACE == TREF(window_token)) || (TK_COLON == TREF(window_token))) { text = put_str("",0); if (TK_COLON == TREF(window_token)) { advancewindow(); if (EXPR_FAIL == expr(&lib, MUMPS_STR)) return FALSE; } else lib = put_str("",0); } else { if (EXPR_FAIL == (rval = expr(&text, MUMPS_STR))) /* NOTE asignment */ return FALSE; if (TK_COLON != TREF(window_token)) { if (EXPR_INDR == rval) { make_commarg(&text,indir_zhelp); return TRUE; } lib = put_str("",0); } else { advancewindow(); if (EXPR_FAIL == expr(&lib, MUMPS_STR)) return FALSE; } } ref = newtriple(OC_ZHELP); ref->operand[0] = text; ref->operand[1] = lib; return TRUE; } fis-gtm-V7.0-005/sr_port/m_zinvcmd.c0000644000032200000250000000257414342376331016205 0ustar librarygtc/**************************************************************** * * * Copyright 2011, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "compiler.h" #include "opcode.h" #include "toktyp.h" #include "advancewindow.h" #include "cmd.h" #include "error.h" error_def(ERR_INVCMD); /* Consume and ignore the arguments, and insert an INVCMD error triple. * Previously, we raised an error immediately when the invalid command was * detected, but since a false postconditional may skip the error and * continue executing the line, we need to parse it properly. */ int m_zinvcmd(void) { triple *triptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; while ((TK_EOL != TREF(window_token)) && (TK_SPACE != TREF(window_token)) && (TK_ERROR != TREF(window_token))) advancewindow(); if (TK_ERROR == TREF(window_token)) return FALSE; triptr = newtriple(OC_RTERROR); triptr->operand[0] = put_ilit(MAKE_MSG_TYPE(ERR_INVCMD, ERROR)); /* switch from warning to error */ triptr->operand[1] = put_ilit(FALSE); /* not a subroutine reference */ return TRUE; } fis-gtm-V7.0-005/sr_port/m_zlink.c0000755000032200000250000000327114342376331015660 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "svnames.h" #include "advancewindow.h" #include "cmd.h" int m_zlink(void) { int rval; oprtype file, quals; triple *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TK_EOL == TREF(window_token)) || (TK_SPACE == TREF(window_token)) || (TK_COLON == TREF(window_token))) { ref = newtriple(OC_SVGET); ref->operand[0] = put_ilit(SV_ZSOURCE); file = put_tref(ref); if (TK_COLON == TREF(window_token)) { advancewindow(); if (EXPR_FAIL == expr(&quals, MUMPS_STR)) return FALSE; } else { ref = newtriple(OC_SVGET); ref->operand[0] = put_ilit(SV_ZCOMPILE); quals = put_tref(ref); } } else { if (EXPR_FAIL == (rval = expr(&file, MUMPS_STR))) /* NOTE assignment */ return FALSE; if (TK_COLON != TREF(window_token)) { if (EXPR_INDR == rval) { make_commarg(&file, indir_zlink); return TRUE; } ref = newtriple(OC_SVGET); ref->operand[0] = put_ilit(SV_ZCOMPILE); quals = put_tref(ref); } else { advancewindow(); if (EXPR_FAIL == expr(&quals, MUMPS_STR)) return FALSE; } } ref = newtriple(OC_ZLINK); ref->operand[0] = file; ref->operand[1] = quals; return TRUE; } fis-gtm-V7.0-005/sr_port/m_zmessage.c0000755000032200000250000000305014342376331016342 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "advancewindow.h" #include "cmd.h" error_def(ERR_FCHARMAXARGS); int m_zmessage (void) { int count; oprtype arg; triple *ref0, *ref1; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; switch (expr(&arg, MUMPS_INT)) { case EXPR_FAIL: return FALSE; case EXPR_INDR: if (TK_COLON != TREF(window_token)) { make_commarg(&arg, indir_zmess); return TRUE; } /* caution: fall through */ case EXPR_GOOD: coerce(&arg, OCT_MINT); ref0 = maketriple(OC_ZMESS); ref0->operand[1] = put_tref(newtriple(OC_PARAMETER)); ref1 = ref0->operand[1].oprval.tref; ref1->operand[0] = arg; break; } for (count = 1; TK_COLON == TREF(window_token); count++) { advancewindow(); if (EXPR_FAIL == expr(&arg, MUMPS_EXPR)) return FALSE; ref1->operand[1] = put_tref(newtriple(OC_PARAMETER)); ref1 = ref1->operand[1].oprval.tref; ref1->operand[0] = arg; } ref0->operand[0] = put_ilit(count); ins_triple(ref0); return TRUE; } fis-gtm-V7.0-005/sr_port/m_zprint.c0000755000032200000250000000524714342376331016064 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2013 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "advancewindow.h" #include "cmd.h" GBLREF boolean_t run_time; GBLREF mident routine_name; LITREF mident zero_ident; error_def(ERR_LABELEXPECTED); error_def(ERR_RTNNAME); int m_zprint(void) { boolean_t got_some; oprtype lab1, lab2, off1, off2, rtn; triple *next, *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; got_some = FALSE; lab1 = put_str(zero_ident.addr, zero_ident.len); off1 = put_ilit(0); if ((TK_EOL != TREF(window_token)) && (TK_SPACE != TREF(window_token)) && !lref(&lab1, &off1, TRUE, indir_zprint, TRUE, &got_some)) return FALSE; if ((TRIP_REF == lab1.oprclass) && (OC_COMMARG == lab1.oprval.tref->opcode)) return TRUE; if (TK_CIRCUMFLEX != TREF(window_token)) { /* Routine not specified, use current routine */ rtn = PUT_CURRENT_RTN; } else { got_some = TRUE; advancewindow(); switch (TREF(window_token)) { case TK_IDENT: # ifdef GTM_TRIGGER if (TK_HASH == TREF(director_token)) /* Coagulate tokens as necessary (and available) to allow '#' in the rtn name */ advwindw_hash_in_mname_allowed(); # endif rtn = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); advancewindow(); break; case TK_ATSIGN: if (!indirection(&rtn)) return FALSE; break; default: stx_error(ERR_RTNNAME); return FALSE; } } if (TK_COLON == TREF(window_token)) { if (!got_some) { stx_error(ERR_LABELEXPECTED); return FALSE; } lab2 = put_str(zero_ident.addr, zero_ident.len); off2 = put_ilit(0); advancewindow(); if (!lref(&lab2, &off2, TRUE, indir_zprint, FALSE, &got_some)) return FALSE; if (!got_some) { stx_error(ERR_LABELEXPECTED); return FALSE; } } else { lab2 = lab1; off2 = off1; } ref = newtriple(OC_ZPRINT); ref->operand[0] = rtn; next = newtriple(OC_PARAMETER); ref->operand[1] = put_tref(next); next->operand[0] = lab1; ref = newtriple(OC_PARAMETER); next->operand[1] = put_tref(ref); ref->operand[0] = off1; next = newtriple(OC_PARAMETER); ref->operand[1] = put_tref(next); next->operand[0] = lab2; ref = newtriple(OC_PARAMETER); next->operand[1] = put_tref(ref); ref->operand[0] = off2; return TRUE; } fis-gtm-V7.0-005/sr_port/m_zshow.c0000755000032200000250000000571614342376331015711 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "mlkdef.h" #include "zshow.h" #include "advancewindow.h" #include "cmd.h" error_def(ERR_VAREXPECTED); int m_zshow(void) { static readonly char def_str[]="S"; int code, rval; oprtype func, output; triple *lvar, *outtype, *r; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TK_SPACE == TREF(window_token)) || (TK_EOL == TREF(window_token)) || (TK_COLON == TREF(window_token))) code = ZSHOW_NOPARM; else { code = ZSHOW_DEVICE; switch (expr(&func, MUMPS_STR)) { case EXPR_FAIL: return FALSE; case EXPR_GOOD: break; case EXPR_INDR: if (TK_COLON != TREF(window_token)) { make_commarg(&func, indir_zshow); return TRUE; } break; default: assertpro(FALSE); } } if (TK_COLON == TREF(window_token)) { advancewindow(); switch (TREF(window_token)) { case TK_CIRCUMFLEX: if (!gvn()) return FALSE; r = maketriple(OC_ZSHOW); outtype = newtriple(OC_PARAMETER); r->operand[1] = put_tref(outtype); if (code == ZSHOW_NOPARM) r->operand[0] = put_str(&def_str[0], (SIZEOF(def_str) - 1)); else r->operand[0] = func; outtype->operand[0] = put_ilit(ZSHOW_GLOBAL); ins_triple(r); return TRUE; case TK_IDENT: if (!lvn(&output, OC_PUTINDX, 0)) { stx_error(ERR_VAREXPECTED); return FALSE; } r = maketriple(OC_ZSHOWLOC); outtype = newtriple(OC_PARAMETER); r->operand[1] = put_tref(outtype); if (code == ZSHOW_NOPARM) r->operand[0] = put_str(&def_str[0], (SIZEOF(def_str) - 1)); else r->operand[0] = func; lvar = newtriple(OC_PARAMETER); outtype->operand[1] = put_tref(lvar); lvar->operand[0] = output; outtype->operand[0] = put_ilit(ZSHOW_LOCAL); ins_triple(r); return TRUE; case TK_ATSIGN: if (!indirection(&output)) { stx_error(ERR_VAREXPECTED); return FALSE; } r = newtriple(OC_INDRZSHOW); if (code == ZSHOW_NOPARM) r->operand[0] = put_str(&def_str[0], (SIZEOF(def_str) - 1)); else r->operand[0] = func; r->operand[1] = output; return TRUE; default: stx_error(ERR_VAREXPECTED); return FALSE; } } r = maketriple(OC_ZSHOW); outtype = newtriple(OC_PARAMETER); r->operand[1] = put_tref(outtype); if (code == ZSHOW_NOPARM) r->operand[0] = put_str(&def_str[0], (SIZEOF(def_str) - 1)); else r->operand[0] = func; outtype->operand[0] = put_ilit(ZSHOW_DEVICE); ins_triple(r); return TRUE; } fis-gtm-V7.0-005/sr_port/m_zstep.c0000755000032200000250000000401114342376331015667 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2014 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 "compiler.h" #include "opcode.h" #include "zstep.h" #include "toktyp.h" #include "nametabtyp.h" #include "mdq.h" #include "advancewindow.h" #include "cmd.h" #include "namelook.h" GBLREF int source_column; error_def(ERR_INVZSTEP); error_def(ERR_ZSTEPARG); static readonly nametabent zstep_names[] = { { 1, "I"}, { 4, "INTO"} ,{ 2,"OU" }, { 5,"OUTOF" } ,{ 2,"OV" }, { 4,"OVER" } }; static readonly unsigned char zstep_index[27] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2 ,2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6 ,6, 6, 6 }; static readonly char zstep_type[]={ ZSTEP_INTO, ZSTEP_INTO, ZSTEP_OUTOF, ZSTEP_OUTOF, ZSTEP_OVER, ZSTEP_OVER }; int m_zstep(void) { char type; int x; oprtype action; opctype op; triple *head; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert((SIZEOF(zstep_names) / SIZEOF(nametabent)) == zstep_index[26]); op = OC_ZSTEP; switch (TREF(window_token)) { case TK_SPACE: case TK_EOL: type = ZSTEP_OVER; break; case TK_IDENT: if (0 > (x = namelook(zstep_index, zstep_names, (TREF(window_ident)).addr, (TREF(window_ident)).len))) { /* NOTE assignment above*/ stx_error(ERR_INVZSTEP); return FALSE; } type = zstep_type[x]; advancewindow(); break; default: stx_error(ERR_ZSTEPARG); return FALSE; break; } if (TK_COLON == TREF(window_token)) { advancewindow(); if (EXPR_FAIL == expr(&action, MUMPS_EXPR)) return FALSE; op = OC_ZSTEPACT; } head = maketriple(op); head->operand[0] = put_ilit(type); if (OC_ZSTEPACT == op) head->operand[1] = action; ins_triple(head); return TRUE; } fis-gtm-V7.0-005/sr_port/m_zsystem.c0000755000032200000250000000220514342376331016243 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "cmd.h" int m_zsystem(void) { oprtype x; triple *triptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TK_EOL == TREF(window_token)) || (TK_SPACE == TREF(window_token))) { triptr = newtriple(OC_ZSYSTEM); triptr->operand[0] = put_str("",0); return TRUE; } else switch (expr(&x, MUMPS_STR)) { case EXPR_FAIL: return FALSE; case EXPR_GOOD: triptr = newtriple(OC_ZSYSTEM); triptr->operand[0] = x; return TRUE; case EXPR_INDR: make_commarg(&x, indir_zsystem); return TRUE; } return FALSE; /* This will never get executed, added to make compiler happy */ } fis-gtm-V7.0-005/sr_port/m_ztcommit.c0000755000032200000250000000163414342376331016400 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "cmd.h" int m_ztcommit(void) { triple *head; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; head = maketriple(OC_ZTCOMMIT); if ((TK_EOL == TREF(window_token)) || (TK_SPACE == TREF(window_token))) { head->operand[0] = put_ilit(1); ins_triple(head); return TRUE; } if (EXPR_FAIL == expr(&head->operand[0], MUMPS_INT)) return FALSE; ins_triple(head); return TRUE; } fis-gtm-V7.0-005/sr_port/m_ztstart.c0000755000032200000250000000145014342376331016241 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "toktyp.h" #include "cmd.h" error_def(ERR_SPOREOL); int m_ztstart(void) { DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if ((TK_EOL != TREF(window_token)) && (TK_SPACE != TREF(window_token))) { stx_error(ERR_SPOREOL); return FALSE; } newtriple(OC_ZTSTART); return TRUE; } fis-gtm-V7.0-005/sr_port/m_zwatch.c0000755000032200000250000000534514342376331016035 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "advancewindow.h" #include "cmd.h" #define CANCEL_ONE -1 #define CANCEL_ALL -2 LITREF mident zero_ident; error_def(ERR_VAREXPECTED); int m_zwatch(void) { boolean_t is_count; opctype op; oprtype count, name,action; triple *next, *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TK_MINUS == TREF(window_token)) { advancewindow(); switch (TREF(window_token)) { case TK_ASTERISK: name = put_str(zero_ident.addr, zero_ident.len); count = put_ilit(CANCEL_ALL); advancewindow(); break; case TK_IDENT: name = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); count = put_ilit(CANCEL_ONE); advancewindow(); break; case TK_ATSIGN: if (!indirection(&name)) return FALSE; count = put_ilit(CANCEL_ONE); break; default: stx_error(ERR_VAREXPECTED); return FALSE; } action = put_str("",0); op = OC_WATCHREF; } else { if (TK_EQUAL == TREF(window_token)) { advancewindow(); op = OC_WATCHMOD; } else op = OC_WATCHREF; switch (TREF(window_token)) { case TK_IDENT: name = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); advancewindow(); break; case TK_ATSIGN: if (!indirection(&name)) return FALSE; if ((OC_WATCHREF == op) && (TK_COLON != TREF(window_token))) { ref = maketriple(OC_COMMARG); ref->operand[0] = name; ref->operand[1] = put_ilit((mint) indir_zwatch); ins_triple(ref); return TRUE; } break; default: stx_error(ERR_VAREXPECTED); return FALSE; } if (TK_COLON != TREF(window_token)) { action = put_str("",0); count = put_ilit(0); } else { advancewindow(); if (TK_COLON == TREF(window_token)) { is_count = TRUE; action = put_str("", 0); } else { if (EXPR_FAIL == expr(&action, MUMPS_STR)) return FALSE; is_count = (TK_COLON == TREF(window_token)); } if (is_count) { advancewindow(); if (EXPR_FAIL == expr(&count, MUMPS_INT)) return FALSE; } else count = put_ilit(0); } } ref = newtriple(op); ref->operand[0] = name; next = newtriple(OC_PARAMETER); ref->operand[1] = put_tref(next); next->operand[0] = action; next->operand[1] = count; return TRUE; } fis-gtm-V7.0-005/sr_port/m_zwithdraw.c0000755000032200000250000000235614342376331016557 0ustar librarygtc/**************************************************************** * * * 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 "compiler.h" #include "opcode.h" #include "indir_enum.h" #include "toktyp.h" #include "cmd.h" error_def(ERR_VAREXPECTED); int m_zwithdraw(void) { oprtype tmparg; triple *ref; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; switch (TREF(window_token)) { case TK_IDENT: if (!lvn(&tmparg,OC_SRCHINDX,0)) return FALSE; ref = newtriple(OC_LVZWITHDRAW); ref->operand[0] = tmparg; break; case TK_CIRCUMFLEX: if (!gvn()) return FALSE; ref = newtriple(OC_GVZWITHDRAW); break; case TK_ATSIGN: if (!indirection(&tmparg)) return FALSE; ref = maketriple(OC_COMMARG); ref->operand[0] = tmparg; ref->operand[1] = put_ilit((mint) indir_zwithdraw); ins_triple(ref); return TRUE; default: stx_error(ERR_VAREXPECTED); return FALSE; } return TRUE; } fis-gtm-V7.0-005/sr_port/m_zwrite.c0000755000032200000250000002141014342376331016050 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2017 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 "compiler.h" #include "mdq.h" #include "opcode.h" #include "nametabtyp.h" #include "indir_enum.h" #include "gdsroot.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "zwrite.h" #include "toktyp.h" #include "svnames.h" #include "funsvn.h" #include "advancewindow.h" #include "cmd.h" #include "compile_pattern.h" #include "mvalconv.h" #include "namelook.h" GBLREF int source_column; GBLREF uint4 pat_everything[]; GBLREF mstr_len_t sizeof_pat_everything; error_def(ERR_COMMA); error_def(ERR_INVSVN); error_def(ERR_RPARENMISSING); error_def(ERR_SVNEXPECTED); error_def(ERR_VAREXPECTED); error_def(ERR_ZWRSPONE); LITREF unsigned char svn_index[]; LITREF nametabent svn_names[]; LITREF svn_data_type svn_data[]; /****** CAUTION !!! ****** * All occurrences of put_lit should be replaced by put_ilit. In order to maintain object * code compatibility, however, this replacement has been preempted by preceding put_lit * with n2s. * -no runtime module looks at anything but nm, so call to n2s is dispensed with. -mwm */ int m_zwrite(void) { boolean_t parse_warn, pat; char c; int index; int4 pcount; /* parameter count */ triple *count, *head, *last, *ref, *ref1; mint code, subscount; mval mv; opctype op; oprtype limit, name; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; subscount = 0; count = 0; pat = FALSE; if (TK_CIRCUMFLEX == TREF(window_token)) { advancewindow(); op = OC_GVZWRITE; } else op = OC_LVZWRITE; switch (TREF(window_token)) { case TK_SPACE: case TK_EOL: if (OC_GVZWRITE == op) { stx_error(ERR_VAREXPECTED); return FALSE; } op = OC_LVPATWRITE; head = maketriple(op); head->operand[0] = put_ilit((mint)3); /* count */ ref1 = newtriple(OC_PARAMETER); head->operand[1] = put_tref(ref1); ref1->operand[0] = put_ilit(0); /* shows not from zshow */ ref = newtriple(OC_PARAMETER); ref1->operand[1] = put_tref(ref); ref->operand[0] = put_str((char *)pat_everything, sizeof_pat_everything); memset(&mv, 0, SIZEOF(mval)); /* Clear mval so unused fields don't cause objhash * differences */ MV_FORCE_MVAL(&mv, ZWRITE_ASTERISK) ; ref->operand[1] = put_lit(&mv); ins_triple(head); return TRUE; case TK_IDENT: name = put_str((TREF(window_ident)).addr, (TREF(window_ident)).len); advancewindow(); break; case TK_DOLLAR: advancewindow(); if ((TK_IDENT != TREF(window_token)) || (OC_GVZWRITE == op)) { stx_error(ERR_SVNEXPECTED); return FALSE; } parse_warn = FALSE; index = namelook(svn_index, svn_names, (TREF(window_ident)).addr, (TREF(window_ident)).len); if (0 > index) { STX_ERROR_WARN(ERR_INVSVN); /* sets "parse_warn" to TRUE */ } else { if (!VALID_SVN(index)) { STX_ERROR_WARN(ERR_FNOTONSYS); /* sets "parse_warn" to TRUE */ } } advancewindow(); switch (TREF(window_token)) { case TK_SPACE: case TK_EOL: case TK_COMMA: if (!parse_warn) { assert(SV_NUM_SV > svn_data[index].opcode); ref = maketriple(OC_ZWRITESVN); ref->operand[0] = put_ilit(svn_data[index].opcode); ins_triple(ref); } else /* ins_errtriple should have already provided an OC_RTERROR */ assert(ALREADY_RTERROR); return TRUE; default: stx_error(ERR_SVNEXPECTED); return FALSE; } break; case TK_LPAREN: if (OC_GVZWRITE != op) /* naked reference */ { stx_error(ERR_VAREXPECTED); return FALSE; } name = put_str((TREF(window_ident)).addr, 0); break; case TK_ATSIGN: if (!indirection(&name)) return FALSE; if ((OC_LVZWRITE == op) && (TK_LPAREN != TREF(window_token))) { ref = maketriple(OC_COMMARG); ref->operand[0] = name; ref->operand[1] = put_ilit(indir_zwrite); ins_triple(ref); return TRUE; } ref = newtriple(OC_INDPAT); ref->operand[0] = name; name = put_tref(ref); break; case TK_QUESTION: advancewindow(); source_column = TREF(last_source_column); if (!compile_pattern(&name, FALSE)) return FALSE; if (op == OC_LVZWRITE) op = OC_LVPATWRITE; pat = TRUE; break; default: stx_error(ERR_VAREXPECTED); return FALSE; } head = maketriple(op); last = newtriple(OC_PARAMETER); head->operand[1] = put_tref(last); pcount = 1; if ((OC_LVPATWRITE == op) || (OC_GVZWRITE == op)) { pcount++; last->operand[0] = put_ilit(((OC_GVZWRITE == op)? pat : 0)); ref = newtriple(OC_PARAMETER); last->operand[1] = put_tref(ref); last = ref; if (OC_GVZWRITE == op) { pcount++; count = last; ref = newtriple(OC_PARAMETER); last->operand[1] = put_tref(ref); last = ref; } } last->operand[0] = name; if (TK_LPAREN != TREF(window_token)) { pcount++; memset(&mv, 0, SIZEOF(mval)); /* Clear mval so unused fields don't cause objhash differences */ if (pat) { MV_FORCE_MVAL(&mv, ZWRITE_END); } else { subscount++ ; MV_FORCE_MVAL(&mv, ZWRITE_ASTERISK); } last->operand[1] = put_lit(&mv); head->operand[0] = put_ilit(pcount); if (count) count->operand[0] = put_ilit(subscount); ins_triple(head); return TRUE; } advancewindow(); for(;;) { ref = newtriple(OC_PARAMETER); last->operand[1] = put_tref(ref); memset(&mv, 0, SIZEOF(mval)); /* Clear mval so unused fields don't cause objhash differences */ switch (TREF(window_token)) { case TK_RPAREN: dqdel(ref,exorder); advancewindow(); MV_FORCE_MVAL(&mv, ZWRITE_END); last->operand[1] = put_lit(&mv); pcount++; head->operand[0] = put_ilit((mint)pcount); if (count) count->operand[0] = put_ilit(subscount); ins_triple(head); return TRUE; case TK_ASTERISK: dqdel(ref,exorder); advancewindow(); if (TK_RPAREN != TREF(window_token)) { stx_error(ERR_RPARENMISSING); return FALSE; } advancewindow(); MV_FORCE_MVAL(&mv, ZWRITE_ASTERISK); last->operand[1] = put_lit(&mv); pcount++; subscount++; head->operand[0] = put_ilit((mint)pcount); if (count) count->operand[0] = put_ilit(subscount); ins_triple(head); return TRUE; case TK_QUESTION: advancewindow(); source_column = TREF(last_source_column); if (!compile_pattern(&limit, FALSE)) return FALSE; if ((TK_COMMA != TREF(window_token)) && (TK_RPAREN != TREF(window_token))) { stx_error(ERR_ZWRSPONE); return FALSE; } if (TK_COMMA == TREF(window_token)) advancewindow(); subscount++; MV_FORCE_MVAL(&mv, ZWRITE_PATTERN); ref->operand[0] = put_lit(&mv); pcount++; ref1 = newtriple(OC_PARAMETER); ref->operand[1] = put_tref(ref1); ref1->operand[0] = limit; last = ref1; pcount++; continue; case TK_COLON: if (TK_RPAREN != (c = TREF(director_token))) /* NOTE assignment */ { if (TK_COMMA != c) { advancewindow(); MV_FORCE_MVAL(&mv, ZWRITE_UPPER); ref->operand[0] = put_lit(&mv); pcount++; subscount++; break; } advancewindow(); } /* caution: fall through */ case TK_COMMA: advancewindow(); MV_FORCE_MVAL(&mv, ZWRITE_ALL); ref->operand[0] = put_lit(&mv); pcount++; subscount++; last = ref; continue; default: if (EXPR_FAIL == expr(&limit, MUMPS_EXPR)) return FALSE; subscount++; last = newtriple(OC_PARAMETER); ref->operand[1] = put_tref(last); last->operand[0] = limit; pcount++; if (TK_COLON == (c = TREF(window_token))) /* NOTE assignment */ { code = ZWRITE_LOWER; advancewindow(); c = TREF(window_token); } else code = ZWRITE_VAL; switch (c) { case TK_COMMA: advancewindow(); /* caution: fall through */ case TK_RPAREN: MV_FORCE_MVAL(&mv, code) ; ref->operand[0] = put_lit(&mv); pcount++; continue; default: if (code == ZWRITE_VAL) { stx_error(ERR_COMMA); return FALSE; } MV_FORCE_MVAL(&mv, ZWRITE_BOTH) ; ref->operand[0] = put_lit(&mv); pcount++; ref = last; break; } break; } if (EXPR_FAIL == expr(&limit, MUMPS_EXPR)) return FALSE; last = newtriple(OC_PARAMETER); ref->operand[1] = put_tref(last); last->operand[0] = limit; pcount++; if (TK_COMMA == TREF(window_token)) advancewindow(); } } fis-gtm-V7.0-005/sr_port/main_pragma.h0000644000032200000250000000153014342376331016466 0ustar librarygtc/**************************************************************** * * * Copyright 2009 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. * * * ****************************************************************/ #ifndef MAIN_PRAGMA_included #define MAIN_PRAGMA_included #ifdef __MVS__ #pragma runopts(ENVAR(_BPXK_AUTOCVT=ON)) #pragma runopts(FILETAG(AUTOCVT,AUTOTAG)) #endif #endif /* MAIN_PRAGMA_included */ fis-gtm-V7.0-005/sr_port/make_commarg.c0000755000032200000250000000201114342376331016626 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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 "compiler.h" #include "opcode.h" #include "mdq.h" #ifdef DEBUG LITREF octabstruct oc_tab[]; #endif void make_commarg(oprtype *x,mint ind) { triple *ref; assert(x->oprclass == TRIP_REF); ref = x->oprval.tref; if (ref->opcode != OC_INDGLVN) { assert(ref->opcode == OC_COMVAL || ref->opcode == OC_COMINT || ref->opcode == OC_COBOOL); dqdel(ref,exorder); assert(ref->operand[0].oprclass == TRIP_REF); ref = ref->operand[0].oprval.tref; assert(ref->opcode == OC_INDGLVN); } ref->opcode = OC_COMMARG; ref->operand[1] = put_ilit(ind); return; } fis-gtm-V7.0-005/sr_port/make_gvsubsc.c0000755000032200000250000000262314342376331016666 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2015 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 "compiler.h" #include "stringpool.h" #include "gdsroot.h" #include "gdsblk.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "mvalconv.h" #include "collseq.h" GBLREF spdesc stringpool; oprtype make_gvsubsc(mval *v) { mval w; gv_key *gp; ENSURE_STP_FREE_SPACE(MAX_SRCLINE + SIZEOF(gv_key)); if ((INTPTR_T)stringpool.free & 1) /* BYPASSOK */ stringpool.free++; /* word align key for structure refs */ gp = (gv_key *) stringpool.free; gp->top = MAX_SRCLINE; gp->end = gp->prev = 0; mval2subsc(v, gp, STD_NULL_COLL_FALSE); memset(&w, 0, SIZEOF(mval)); /* Clear mval so ununsed fields don't cause objhash differences */ w.mvtype = MV_STR | MV_SUBLIT; w.str.addr = (char *) gp->base; w.str.len = gp->end + 1; stringpool.free = &gp->base[gp->end + 1]; assert(stringpool.free <= stringpool.top); return put_lit(&w); } fis-gtm-V7.0-005/sr_port/maketriple.c0000755000032200000250000000166614342376331016360 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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_string.h" #include "compiler.h" #include "mdq.h" #include "mmemory.h" GBLREF int source_column; triple *maketriple(opctype op) { triple *x; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; x = (triple *)mcalloc(SIZEOF(triple)); x->opcode = op; x->src.line = TREF(source_line); x->src.column = source_column; dqinit(&(x->backptr), que); dqinit(&(x->jmplist), que); return x; } fis-gtm-V7.0-005/sr_port/matchc.c0000755000032200000250000001511014342376331015447 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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 "matchc.h" #define RETURN_NOMATCH \ { \ *res = 0; \ assert(0 < numpcs_unmatched); \ *numpcs = numpcs_unmatched; \ return src_top; \ } #define RETURN_YESMATCH(RET) \ { \ *res = RET; \ assert(0 == numpcs_unmatched); \ *numpcs = 0; \ return src_ptr; \ } /* * ----------------------------------------------- * Pseudo equivalent of VAX matchc instruction * * Arguments: * del_len - delimiter length * del_str - pointer to delimiter string * src_len - length of source string * src_str - pointer to source string * res - pointer to the result * numpcs - pointer to the number of pieces that are desired to be matched. * * Return: * pointer to next character after match substring * in the source string, if found. Otherwise src_str + src_len. * * Side effects: * set res arg to: * 0 - if match not found * 1 + char_len - if match found, where char_len is the position * of the next character after the match substring. * set numpcs arg to # of pieces that could not be matched (because end of source string was reached before then) * ----------------------------------------------- */ #ifdef UTF8_SUPPORTED #include "gtm_utf8.h" GBLREF boolean_t gtm_utf8_mode; GBLREF boolean_t badchar_inhibit; /* multi-byte character-oriented substring matching */ unsigned char *matchc(int del_len, unsigned char *del_str, int src_len, unsigned char *src_str, int *res, int *numpcs) { unsigned char *src_ptr, *src_top, *src_next, *del_ptr, *del_top, *del_next, *del_next1, *restart_ptr; wint_t src_cp, del_cp, del_cp1; /* code points for the source and delimiter characters */ int char_len, restart_char_len, del_charlen, bytelen, numpcs_unmatched; if (!gtm_utf8_mode) return matchb(del_len, del_str, src_len, src_str, res, numpcs); assert(0 <= del_len); assert(0 < *numpcs); if (0 == del_len) { /* always matches a null string */ *numpcs = 0; *res = 1; return src_str; } src_ptr = src_str; src_top = src_str + src_len; del_top = del_str + del_len; /* Check UTF8 byte sequence validity of delimiter string. The following code is very similar to utf8_len() but * we dont invoke the function here for performance reasons as this piece of code is used by heavy hitters like $piece. * Also, the code below can be forked off into two cases depending on the value of "badchar_inhibit". This is a * performance enhancement that can be done later if this is found to be a bottleneck. */ if (!badchar_inhibit) { for (del_charlen = 0, del_ptr = del_str; del_ptr < del_top; del_charlen++, del_ptr += bytelen) { if (!UTF8_VALID(del_ptr, del_top, bytelen)) utf8_badchar(0, del_ptr, del_top, 0, NULL); } } numpcs_unmatched = *numpcs; /* note down # of pieces left to match */ /* compute the code point of the 1st delimiter char */ del_next1 = UTF8_MBTOWC(del_str, del_top, del_cp1); assert((WEOF != del_cp1) || badchar_inhibit); for (char_len = 0; (src_ptr < src_top) && (src_top - src_ptr) >= del_len; ) { src_next = src_ptr; do { /* find the occurrence of 1st delimiter char in the source */ src_ptr = src_next; src_next = UTF8_MBTOWC(src_ptr, src_top, src_cp); if ((WEOF == src_cp) && !badchar_inhibit) utf8_badchar(0, src_ptr, src_top, 0, NULL); ++char_len; /* maintain the source character position */ } while ((src_next < src_top) && ((src_cp != del_cp1) || ((WEOF == src_cp) && (*src_ptr != *del_str)))); if ((src_cp != del_cp1) || (WEOF == src_cp) && (*src_ptr != *del_str)) { /* could not find the 1st delimiter char in the source */ RETURN_NOMATCH; } /* 1st delimiter character match found. match the other delimiter characters */ del_ptr = del_next1; /* advance past the 1st delimiter character */ restart_ptr = src_ptr = src_next; /* advance past the 1st source character */ restart_char_len = char_len; for ( ; (src_ptr < src_top) && (del_ptr < del_top); src_ptr = src_next, del_ptr = del_next, ++char_len) { src_next = UTF8_MBTOWC(src_ptr, src_top, src_cp); if ((WEOF == src_cp) && !badchar_inhibit) utf8_badchar(0, src_ptr, src_top, 0, NULL); del_next = UTF8_MBTOWC(del_ptr, del_top, del_cp); if ((src_cp != del_cp) || ((WEOF == src_cp) && *src_ptr != *del_ptr)) { /* match lost. restart the search skipping the first delimiter character */ src_ptr = restart_ptr; char_len = restart_char_len; break; } } if (del_ptr >= del_top) { /* Match found : Return success if no more pieces to match else continue with scan */ assert(del_top == del_ptr); assert(0 < numpcs_unmatched); if (0 == --numpcs_unmatched) RETURN_YESMATCH(1 + char_len); } } RETURN_NOMATCH; } #endif /* UTF8_SUPPORTED */ /* byte-oriented substring matching */ unsigned char *matchb(int del_len, unsigned char *del_str, int src_len, unsigned char *src_str, int *res, int *numpcs) { unsigned char *src_ptr, *pdel, *src_base, *src_top, *del_top; int src_cnt, numpcs_unmatched; boolean_t match_found; assert(0 <= del_len); assert(0 < *numpcs); if (0 == del_len) { /* always matches a null string */ *numpcs = 0; *res = 1; return src_str; } numpcs_unmatched = *numpcs; /* note down # of pieces to be matched */ src_ptr = src_base = src_str; src_top = src_ptr + src_len; if (src_len < del_len) /* Input string is shorter than delimiter string so no match possible */ RETURN_NOMATCH; del_top = del_str + del_len; pdel = del_str; while (src_ptr < src_top) { /* Quick Find 1st delimiter char */ while (*src_ptr != *pdel) { src_ptr = ++src_str; if (src_ptr == src_top) RETURN_NOMATCH; } match_found = FALSE; /* Found delimiter */ while (*src_ptr++ == *pdel++) { if (pdel == del_top) { /* Found matching piece. */ match_found = TRUE; break; } if (src_ptr == src_top) RETURN_NOMATCH; } if (match_found) { /* Return success if no more pieces to match else continue with scan */ assert(0 < numpcs_unmatched); if (0 == --numpcs_unmatched) RETURN_YESMATCH(INTCAST(1 + (src_ptr - src_base))); src_str = src_ptr; } else src_ptr = ++src_str; /* Match lost, goto next source character */ pdel = del_str; } RETURN_NOMATCH; } fis-gtm-V7.0-005/sr_port/matchc.h0000755000032200000250000000150714342376331015461 0ustar librarygtc/**************************************************************** * * * Copyright 2001, 2009 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. * * * ****************************************************************/ #ifndef MATCHC_INCLUDED #define MATCHC_INCLUDED /* Character oriented match call */ unsigned char *matchc(int del_len, unsigned char *del_str, int src_len, unsigned char *src_str, int *res, int *numpcs); /* Byte oriented match call */ unsigned char *matchb(int del_len, unsigned char *del_str, int src_len, unsigned char *src_str, int *res, int *numpcs); #endif /* MATCHC_INCLUDED */ fis-gtm-V7.0-005/sr_port/mcalloc.c0000755000032200000250000000430614342376331015627 0ustar librarygtc/**************************************************************** * * * 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 "gtm_string.h" #include "mmemory.h" #include "min_max.h" GBLREF int mcavail; GBLREF mcalloc_hdr *mcavailptr, *mcavailbase; char *mcalloc(unsigned int n) { mcalloc_hdr *hdr, *nxt, *ptr; int new_size, rel_size; /* Choice of char_ptr_t made because it is a 64 bit pointer on Tru64 which * is the alignment we need there, or any other 64 bit platforms we support * in the future. */ n = ROUND_UP2(n, SIZEOF(char_ptr_t)); if (n > mcavail) { /* No sufficient space in the current block. Follow the link and check if the next block has sufficient * space. There is no next block or the next one doesn't have enough space, allocate a new block with * the requested size and insert it after the current block. */ hdr = mcavailptr->link; if (NULL == hdr || n > hdr->size) { if (NULL != hdr) { /* i.e. the next block doesn't have sufficient space for n. Release as many small blocks as * necessary to make up for the space that we are allocating for the large block. * By release several small blocks and replacing them with a large block ensures that total * memory footprint is not increased due to a rare occurence of large routine compilation. */ rel_size = 0; for (nxt = hdr; NULL != nxt && (rel_size += nxt->size) < n; nxt = ptr) { ptr = nxt->link; free(nxt); } } else nxt = NULL; new_size = (int)MAX(MC_DSBLKSIZE, (n + MCALLOC_HDR_SZ)); hdr = (mcalloc_hdr *)malloc(new_size); hdr->link = nxt; hdr->size = (int4)(new_size - MCALLOC_HDR_SZ); mcavailptr->link = hdr; } assert(n <= hdr->size); memset(&hdr->data[0], 0, hdr->size); mcavailptr = hdr; mcavail = hdr->size; } mcavail -= n; assert(mcavail >= 0); return &mcavailptr->data[mcavail]; } fis-gtm-V7.0-005/sr_port/mcfree.c0000755000032200000250000000175114342376331015457 0ustar librarygtc/**************************************************************** * * * 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 "mmemory.h" GBLREF int mcavail; GBLREF mcalloc_hdr *mcavailptr, *mcavailbase; /* This routine doesn't actually release memory at the end of compilation. For efficiency sake * (esp. during indirect code compilation), it just resets the mcavail* pointers to the beginning * of the list so that the allocated area will be reused for the next compilation. */ void mcfree(void) { mcavailptr = mcavailbase; assert((NULL != mcavailptr) || !mcavail); mcavail = (NULL != mcavailptr) ? mcavailptr->size : 0; return; } fis-gtm-V7.0-005/sr_port/md5hash.c0000644000032200000250000002200414342376331015536 0ustar librarygtc/* md5hash.c is adapted to work with FIS GT.M (http://fis-gtm.com) from * source code at http://cvsweb.xfree86.org/cvsweb/cvs/lib/md5.c?rev=1.1.1.2 * to which no claim of Copyright was made by the authors. No claim of * copyright is made by Fidelity Information Services, Inc. with respect * to the changes made to adapt it to GT.M. * * Summary of changes: * + use GT.M includes * + use SIZEOF macro * + remove ASM_MD5 and PROTO preprocessor code */ /* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. */ /* This code was modified in 1997 by Jim Kingdon of Cyclic Software to not require an integer type which is exactly 32 bits. This work draws on the changes for the same purpose by Tatu Ylonen as part of SSH, but since I didn't actually use that code, there is no copyright issue. I hereby disclaim copyright in any changes I have made; this code remains in the public domain. */ /* Note regarding cvs_* namespace: this avoids potential conflicts with libraries such as some versions of Kerberos. No particular need to worry about whether the system supplies an MD5 library, as this file is only about 3k of object code. */ #include "mdef.h" #include "gtm_string.h" #include "md5hash.h" /* Little-endian byte-swapping routines. Note that these do not depend on the size of datatypes such as cvs_uint32, nor do they require us to detect the endianness of the machine we are running on. It is possible they should be macros for speed, but I would be surprised if they were a performance bottleneck for MD5. */ static cvs_uint32 getu32 (addr) const unsigned char *addr; { return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) | addr[1]) << 8 | addr[0]; } static void putu32 (data, addr) cvs_uint32 data; unsigned char *addr; { addr[0] = (unsigned char)data; addr[1] = (unsigned char)(data >> 8); addr[2] = (unsigned char)(data >> 16); addr[3] = (unsigned char)(data >> 24); } /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ void cvs_MD5Init (ctx) struct cvs_MD5Context *ctx; { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; ctx->buf[2] = 0x98badcfe; ctx->buf[3] = 0x10325476; ctx->bits[0] = 0; ctx->bits[1] = 0; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ void cvs_MD5Update (ctx, buf, len) struct cvs_MD5Context *ctx; unsigned char const *buf; unsigned len; { cvs_uint32 t; /* Update bitcount */ t = ctx->bits[0]; if ((ctx->bits[0] = (t + ((cvs_uint32)len << 3)) & 0xffffffff) < t) ctx->bits[1]++; /* Carry from low to high */ ctx->bits[1] += len >> 29; t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ /* Handle any leading odd-sized chunks */ if ( t ) { unsigned char *p = ctx->in + t; t = 64-t; if (len < t) { memcpy(p, buf, len); return; } memcpy(p, buf, t); cvs_MD5Transform (ctx->buf, ctx->in); buf += t; len -= t; } /* Process data in 64-byte chunks */ while (len >= 64) { memcpy(ctx->in, buf, 64); cvs_MD5Transform (ctx->buf, ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ memcpy(ctx->in, buf, len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ void cvs_MD5Final (digest, ctx) unsigned char digest[16]; struct cvs_MD5Context *ctx; { unsigned count; unsigned char *p; /* Compute number of bytes mod 64 */ count = (ctx->bits[0] >> 3) & 0x3F; /* Set the first char of padding to 0x80. This is safe since there is always at least one byte free */ p = ctx->in + count; *p++ = 0x80; /* Bytes of padding needed to make 64 bytes */ count = 64 - 1 - count; /* Pad out to 56 mod 64 */ if (count < 8) { /* Two lots of padding: Pad the first block to 64 bytes */ memset(p, 0, count); cvs_MD5Transform (ctx->buf, ctx->in); /* Now fill the next block with 56 bytes */ memset(ctx->in, 0, 56); } else { /* Pad block to 56 bytes */ memset(p, 0, count-8); } /* Append length in bits and transform */ putu32(ctx->bits[0], ctx->in + 56); putu32(ctx->bits[1], ctx->in + 60); cvs_MD5Transform (ctx->buf, ctx->in); putu32(ctx->buf[0], digest); putu32(ctx->buf[1], digest + 4); putu32(ctx->buf[2], digest + 8); putu32(ctx->buf[3], digest + 12); memset(ctx, 0, SIZEOF(ctx)); /* In case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f, w, x, y, z, data, s) \ ( w += f(x, y, z) + data, w &= 0xffffffff, w = w<>(32-s), w += x ) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ void cvs_MD5Transform (buf, inraw) cvs_uint32 buf[4]; const unsigned char inraw[64]; { register cvs_uint32 a, b, c, d; cvs_uint32 in[16]; int i; for (i = 0; i < 16; ++i) in[i] = getu32 (inraw + 4 * i); a = buf[0]; b = buf[1]; c = buf[2]; d = buf[3]; MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } fis-gtm-V7.0-005/sr_port/md5hash.h0000644000032200000250000000265514342376331015555 0ustar librarygtc/* md5hash.h is adapted to work with FIS GT.M (http://fis-gtm.com) from * source code at http://cvsweb.xfree86.org/cvsweb/cvs/lib/md5.h?rev=1.6 * to which no claim of Copyright was made by the authors. No claim of * copyright is made by Fidelity Information Services, Inc. with respect * to the changes made to adapt it to GT.M. * * Summary of changes: * + add MD5_BLOCK_LENGTH and MD5_DIGEST_LENGTH macros * + add typedef cvs_MD5_CTX * + remove PROTO preprocessor code */ #ifndef MD5HASH_H_INCLUDED #define MD5HASH_H_INCLUDED #define MD5_BLOCK_LENGTH 64 #define MD5_DIGEST_LENGTH 16 /* Unlike previous versions of this code, uint32 need not be exactly 32 bits, merely 32 bits or more. Choosing a data type which is 32 bits instead of 64 is not important; speed is considerably more important. ANSI guarantees that "unsigned long" will be big enough, and always using it seems to have few disadvantages. */ typedef unsigned long cvs_uint32; typedef struct cvs_MD5Context { cvs_uint32 buf[4]; cvs_uint32 bits[2]; unsigned char in[MD5_BLOCK_LENGTH]; } cvs_MD5_CTX; void cvs_MD5Init(struct cvs_MD5Context *context); void cvs_MD5Update(struct cvs_MD5Context *context, unsigned char const *buf, unsigned len); void cvs_MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], struct cvs_MD5Context *context); void cvs_MD5Transform(cvs_uint32 buf[4], const unsigned char in[MD5_BLOCK_LENGTH]); #endif /* MD5HASH_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/mdq.h0000755000032200000250000001314014342376331014777 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2020 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. * * * ****************************************************************/ #ifndef MDQ_H_DEFINED #define HDQ_H_DEFINED /* Define basic working macros for queue management of doubly linked list is defined using elements "N.fl" and "N.bl". * The DSRINS insert at tail rather than head and so work FIFO rather than LIFO with DQLOOP and associated macros */ /* Loop through a linked list given any element as the start (Q) and a var to use as loop incrementer */ #define DQLOOP(Q, N, I) for (I = (Q)->N.fl; (I) != (Q); (I) = (I)->N.fl) /* Initialize an element */ #define DQINIT(Q, N) ((Q)->N.fl = (Q)->N.bl = (Q)) /* Delete one element "X" from the doubly linked list */ #define DQDEL(X, N) ((X)->N.bl->N.fl = (X)->N.fl, (X)->N.fl->N.bl = (X)->N.bl) /* Delete a doubly-linked list of elements from "Q->N.fl" to "X->N.bl" (i.e. everything in between "Q" and "X" excluding them) */ #define DQDELCHAIN(Q, X, N) ((Q)->N.fl = (X), (X)->N.bl = (Q)) /* Insert one element "X" in between "Q" and "Q->N.fl" */ #define DQINS(Q, N, X) ((X)->N.fl = (Q)->N.fl, (X)->N.bl = (Q), (Q)->N.fl = (X), ((X)->N.fl)->N.bl = (X)) /* Insert one element "X" in between "Q" and "Q->N.bl" */ #define DQRINS(Q, N, X) ((X)->N.bl = (Q)->N.bl, (X)->N.fl = (Q), (Q)->N.bl = (X), ((X)->N.bl)->N.fl = (X)) /* Insert a doubly-linked list of elements from "X->N.fl" to "X->N.bl" in between "Q" and "Q->N.fl" */ #define DQADD(Q, X, N) ((Q)->N.fl->N.bl = (X)->N.bl, (X)->N.bl->N.fl = (Q)->N.fl, (Q)->N.fl = (X)->N.fl, (X)->N.fl->N.bl = (Q)) /* Define macros actually used which if #define DEBUG_TRIPLES, adds debugging information. Since these macros are * used in several different queue types and since these debugging macros only work for the exorder field in triples, * the test to see if should do debugging is not elegant but it does allow decent debugging. Note those macros without * debugging are statically defined. */ # define OCQ_NOOP 0L /* This needs to align with OC_NOOP, which is 0, but 'twould make a mess to do the includes */ # define OCQ_INVALID -1L #define dqloop(Q, N, I) DQLOOP(Q, N, I) #ifdef GTM_MALLOC_BUILD /* gtm_malloc doesn't deal with triples and the #include they require makes for trouble */ #define dqinit(Q, N) DQINIT(Q, N) #define DQTRIPCHK(Q, N) #else #define dqinit(Q, N) \ MBSTART { \ if (!memcmp(#N, "exorder", 3)) \ ((triple *)(Q))->opcode = OCQ_INVALID; \ DQINIT(Q, N); \ } MBEND #define DQTRIPCHK(Q, N) assert((0 != memcmp(#N, "exorder", 3)) || (OCQ_INVALID != ((triple *)(Q))->opcode)) #ifdef DEBUG /*don't turn this on in pro builds, at least without intentionally allowing it by changing this */ /* #define DEBUG_TRIPLES / * Uncomment this to do triple debugging, which is also tied to gtmdbglvl - as of this writing: 0x4000 */ #endif #endif # define dqnoop(Q) \ MBSTART { \ triple *REF; \ \ if ((OCQ_INVALID == ((triple *)(Q))->opcode) \ && (((triple *)(Q))->exorder.bl == (Q))) \ { \ REF = maketriple(OCQ_NOOP); \ DQINIT(REF, exorder); \ DQINS((Q), exorder, REF); \ } \ CHKTCHAIN(Q, N, TRUE); \ } MBEND #ifndef DEBUG_TRIPLES # define dqdel(X, N) DQDEL(X, N) # define dqdelchain(Q, X, N) DQDELCHAIN(Q, X, N) # define dqins(Q, N, X) DQINS(Q, N, X) # define dqrins(Q, N, X) DQRINS(Q, N, X) # define dqadd(Q, X, N) DQADD(Q, X, N) # define CHKTCHAIN(Q, N, B) #else # include "gtmdbglvl.h" # include "gtm_string.h" GBLREF uint4 gtmDebugLevel; /* Q: head of queue to check; N: the name of queue; B: whether to check main exorder (from curtchain) and any expr_start queue */ # define CHKTCHAIN(Q, N, B) \ MBSTART { \ triple *c; \ DCL_THREADGBL_ACCESS; \ \ SETUP_THREADGBL_ACCESS; \ /* memcmp() is fast and 3 chars sufficient */ \ if ((GDL_DebugCompiler & gtmDebugLevel) && (0 == memcmp(#N, "exorder", 3))) \ { \ if ((triple *)-1 != (triple *)(Q)) /* to avoid post-checking deletes */ \ chktchain((triple *)(Q)); \ if (B) /* FALSE 3rd arg disables extra AST checks */ \ { \ c = TREF(curtchain); \ if (c != (triple *)(Q)) /* don't bother if redundant */ \ chktchain(c); \ c = TREF(expr_start); \ if ((NULL != c) && (c != (triple *)(Q))) \ chktchain(c); \ c = TREF(expr_start_orig); \ if ((NULL != c) && (c != TREF(expr_start)) && (c != (triple *)(Q))) \ chktchain(c); /* this extra has been rewarding */ \ } \ } \ } MBEND # define dqdel(X, N) \ MBSTART { \ CHKTCHAIN(X, N, FALSE); \ DQDEL(X, N); \ CHKTCHAIN(-1, N, TRUE); \ } MBEND # define dqdelchain(Q, X, N) \ MBSTART { \ CHKTCHAIN(Q, N, FALSE); \ DQDELCHAIN(Q, X, N); \ CHKTCHAIN(Q, N, TRUE); \ } MBEND # define dqins(Q, N, X) \ MBSTART { \ DQTRIPCHK (Q, N); \ CHKTCHAIN(Q, N, FALSE); \ DQINS(Q, N, X); \ CHKTCHAIN(Q, N, TRUE); \ } MBEND # define dqrins(Q, N, X) \ MBSTART { \ DQTRIPCHK (Q, N); \ CHKTCHAIN(Q, N, FALSE); \ DQRINS(Q, N, X); \ CHKTCHAIN(Q, N, TRUE); \ } MBEND # define dqadd(Q, X, N) \ MBSTART { \ DQTRIPCHK (Q, N); \ CHKTCHAIN(Q, N, FALSE); \ CHKTCHAIN(X, N, FALSE); \ DQADD(Q, X, N); \ CHKTCHAIN(Q, N, ((Q) != TREF(expr_start))); \ } MBEND #endif #endif fis-gtm-V7.0-005/sr_port/mem_access.h0000755000032200000250000000132214342376331016314 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2018 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. * * * ****************************************************************/ #ifndef MEM_ACCESS_H_INCLUDED #define MEM_ACCESS_H_INCLUDED void set_noaccess(unsigned char *na_page[], unsigned char *prvprt); void reset_access(unsigned char *na_page[], unsigned char oldprt); #endif fis-gtm-V7.0-005/sr_port/memcoherency.h0000755000032200000250000002152014342376331016675 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2003-2016 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. * * * ****************************************************************/ #ifndef MEMCOHERENCY_H_INCLUDED #define MEMCOHERENCY_H_INCLUDED /* for Uniprocessor systems, no need for "memory barrier" as memory is always coherent. * But almost always we expect to be running on a multi-processor system so we want to avoid the cost * of the if check and do the memory barrier ALWAYS. */ #ifdef __alpha #include /* Read Alpha Architecture Reference Manual, edited by Richard L Sites, * Chapter "System Architecture and Programming Implications" for memory * coherency issues and behavior of "mb" instruction (memory barrier) */ /* NOTES about Alpha (pp. 5-20, section 5.6.5 Implications for Hardware, Chapter 5 System Architecture and Programming * Implications, Alpha Architecture Reference Manual, Edited by Richard L Sites * * MB and IMB force all preceding writes to at least reach their respective coherency points. This does not mean that * main-memory writes have been done, just that the order of the eventual writes is committed. MB and IMB also force all * queued cache invalidates to be delivered to the local caches before starting any subsequent reads (that may otherwise * cache hit on stale data) or writes (that may otherwise write the cache, only to have the write effectively overwriiten * by a late-delivered invalidate) */ #define SHM_WRITE_MEMORY_BARRIER asm("mb") #define SHM_READ_MEMORY_BARRIER SHM_WRITE_MEMORY_BARRIER /* same MB instruction for both read and write barriers */ #define MM_WRITE_MEMORY_BARRIER #elif defined(POWER) || defined(PWRPC) /* GT.M defines POWER and PWRPC if _AIX is defined, see sr_rs6000/mdefsp.h */ /* Refer to article "POWER4 and shared memory synchronization by R. William Hay and Gary R. Hook" available at * http://www-106.ibm.com/developerworks/eserver/articles/power4_mem.html */ /* prototypes */ void do_sync(void); void do_lwsync(void); void do_eieio(void); void do_isync(void); /* The machine codes were fetched from http://www-106.ibm.com/developerworks/eserver/articles/powerpc.html */ /* sync : Creates a memory barrier. On a given processor, any load or store instructions ahead of the sync instruction * in the program sequence must complete their accesses to memory first, and then any load or store instructions after * sync can begin */ #pragma mc_func do_sync{"7c0004ac"} #pragma reg_killed_by do_sync /* lwsync : Creates a memory barrier that provides the same ordering function as the sync instruction, except that a * load caused by an instruction following the lwsync may be performed before a store caused by an instruction that * precedes the lwsync, and the ordering does not apply to accesses to I/O memory (memory-mapped I/O). * lwsync is a new variant of the sync instruction and is interpreted by older processors as a sync. The instruction, * as its name implies, has much less performance impact than sync, and is recommended for syncrhonisation of most * memory (but not I/O) references. */ #pragma mc_func do_lwsync{"7c2004ac"} #pragma reg_killed_by do_lwsync /* eieio : Creates a memory barrier that provides the same ordering function as the sync instruction except that * ordering applies only to accesses to I/O memory */ #pragma mc_func do_eieio{"7c0006ac"} #pragma reg_killed_by do_eieio /* isync : Causes the processor to discard any prefetched (and possibly speculatively executed) instructions and * refetch the next following instructions. It is used in locking code (e.g. __check_lock()) to ensure that no * loads following entry into a critical section can access data (because of aggressive out-of-order and speculative * execution in the processor) before the lock is acquired. */ #pragma mc_func do_isync{"4c00012c"} #pragma reg_killed_by do_isync #define SHM_WRITE_MEMORY_BARRIER \ { /* Ensure that code does not rely on ordering of "loads" following lwsync in programming sequence to occur */ \ /* after "stores" before lwsync. Use do_sync() if such ordering is required. Replication code (t_end.c, */ \ /* tp_end.c) do not rely on store-load order across memory barrier. Note that grab/rel_lock() perform */ \ /* "sync" (via call to _clear_lock()), and so, we are guaranteed strict ordering of loads and stores of */ \ /* code that reads/writes to journal pool in transaction logic */ \ do_lwsync(); \ } #define SHM_READ_MEMORY_BARRIER \ { \ do_isync(); \ } #define MM_WRITE_MEMORY_BARRIER \ { \ SHM_WRITE_MEMORY_BARRIER; \ do_eieio(); \ } #elif defined(__hppa) /* For _PA_RISC1_0, _PA_RISC1_1, accesses to the address space (both to memory and I/O) through load, store and * semaphore instructions are strongly ordered. This means that accesses appear to software to be done in program order * For _PA_RISC2_0, accesses could be "strongly ordered", "ordered", or "weakly ordered" (read PA-RISC 2.0 ARCHITECTURE * by Gerry Kane, appendix "Memory Ordering Model"). * * For all PA-RISC architectures, cache flush operations are weakly ordered. Flushes may be delayed or held pending, and * a sequence of flush operations may be executed in any order. * * SYNC : Enforce program order of memory references * Any load, store, semaphore, cache flush, or cache purge instructions that follow the SYNC instruction get executed * only after all such instructions prior to the SYNC instruction have completed executing. On implementations which * execute such instructions out of sequence, this instruction enforces program ordering. In sytems in which all memory * references are performed in order, this instruction executes as a null instruction. * * IMPORTANT: SYNC instruction enforces ordering of only those accesses caused by the instructions executed on the * same processor which executes the SYNC instruction. * * [Vinaya] Research results: Accesses to fields (global) that are defined volatile are ordered (compiler generates * LDW (or STW) instruction with the O (ordered) completer, i.e., instructions generated are LDW,O (STW,O). Depending * on the requirements, it may be sufficient to define shared fields as volatile to enforce ordering. With replication * though, it is important that pending cache flushes are completed so that source server sees the transaction data * in its entirety. */ #define SHM_WRITE_MEMORY_BARRIER (void)_asm("SYNC") #define SHM_READ_MEMORY_BARRIER SHM_WRITE_MEMORY_BARRIER /* same SYNC instruction for both read and write barriers. * For read, we want all cache purges to be completed before * we load shared fields */ #elif defined(__ia64) /* In the database update logic, we must order memory mapped I/O and shared memory accesses with respect to each other. * On Itanium (and on AIX, see eieio above) SHM_WRITE_MEMORY_BARRIER alone is insufficient. We also need an mf.a instruction. * * Refer to http://www.intel.com/content/dam/www/public/us/en/documents/manuals/ * itanium-architecture-software-developer-rev-2-3-vol-2-manual.pdf - from page 2:616, * "If software needs to ensure that all prior memory operations have been accepted by the platform and have been observed * by all cache coherent agents, both an mf.a and an mf instruction must be issued. The mf.a must be issued first, and the * mf must be issued second." */ #if defined(__hpux) # include # define SHM_WRITE_MEMORY_BARRIER _MF() # define MM_WRITE_MEMORY_BARRIER \ { \ _Asm_mf_a(); \ SHM_WRITE_MEMORY_BARRIER; \ } #elif defined(__linux__) && defined(__INTEL_COMPILER) # define SHM_WRITE_MEMORY_BARRIER __mf() # define MM_WRITE_MEMORY_BARRIER \ { \ __mfa(); \ SHM_WRITE_MEMORY_BARRIER; \ } #elif defined(__linux__) /* gcc */ # define SHM_WRITE_MEMORY_BARRIER __asm__ __volatile__ ("mf" ::: "memory") # define MM_WRITE_MEMORY_BARRIER \ { \ __asm__ __volatile__ ("mf.a" ::: "memory"); \ SHM_WRITE_MEMORY_BARRIER; \ } #endif /* __linux__ */ /* On IA64, cross processor notifications of write barriers are automatic so no read barrier is necessary */ #define SHM_READ_MEMORY_BARRIER #else /* SPARC, I386, S390, Itanium */ /* Although SPARC architecture allows for out-of-order memory accesses, Solaris forces strong ordering on memory accesses. * We do not need memory barrier primitives on Solaris/SPARC. */ /* Memory accesses in Intel x86 and IBM S390 archtectures are strongly ordered */ #define SHM_WRITE_MEMORY_BARRIER #define SHM_READ_MEMORY_BARRIER #define MM_WRITE_MEMORY_BARRIER #endif #endif /* MEMCOHERENCY_H_INCLUDED */ fis-gtm-V7.0-005/sr_port/memvcmp.c0000755000032200000250000000140014342376331015651 0ustar librarygtc/**************************************************************** * * * 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. * * * ****************************************************************/ /* Compare two character strings using "0" as a filler return 0 iff they are equal return < 0 if a < b and > 0 if a > b */ #include "mdef.h" #include "gtm_string.h" #include "mmemory.h" int memvcmp(void *a, int a_len, void *b, int b_len) { int retval; MEMVCMP(a, a_len, b, b_len, retval); return retval; } fis-gtm-V7.0-005/sr_port/merge_def.h0000755000032200000250000000142714342376331016140 0ustar librarygtc/**************************************************************** * * * Copyright 2001 Sanchez Computer Associates, 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. * * * ****************************************************************/ #ifndef MERGE_DEF_DEFINED #define MARG1_LCL 1 #define MARG1_GBL 2 #define MARG2_LCL 4 #define MARG2_GBL 8 #define IND1 0 #define IND2 1 #define MARG1_IS_LCL(arg) (arg & MARG1_LCL) #define MARG1_IS_GBL(arg) (arg & MARG1_GBL) #define MARG2_IS_LCL(arg) (arg & MARG2_LCL) #define MARG2_IS_GBL(arg) (arg & MARG2_GBL) #define MERGE_DEF_DEFINED #endif fis-gtm-V7.0-005/sr_port/merge_desc_check.c0000644000032200000250000002541114342376331017444 0ustar librarygtc/**************************************************************** * * * Copyright (c) 2001-2021 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_string.h" #include "min_max.h" #include "gdsroot.h" #include "gdskill.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsbt.h" #include "gdsfhead.h" #include "zshow.h" #include "zwrite.h" #include "filestruct.h" #include "gdscc.h" #include "copy.h" #include "jnl.h" #include "buddy_list.h" #include "tp.h" #include "merge_def.h" #include "gvname_info.h" #include "op_merge.h" #include "format_targ_key.h" #include "ddphdr.h" #include "mvalconv.h" #include "gvcst_protos.h" /* needed by OPEN_BASEREG_IF_STATSREG */ GBLREF int merge_args; GBLREF merge_glvn_ptr mglvnp; error_def(ERR_MERGEDESC); /* returns 1 if no descendant issues found; * returns 0 if src and dst keys of merge are identical (i.e. NOOP - merge self); * issues MERGEDESC error otherwise. */ boolean_t merge_desc_check(void) { boolean_t intersect, *is_reg_in_array, mergereg_array[256]; char *base; enum db_acc_method acc_meth1, acc_meth2; gd_addr *addr; gd_binding *end_map1, *end_map2, *map, *start_map1, *start_map2; gd_region *reg, *reg1, *reg2; gv_key *gvkey1, *gvkey2; gv_namehead *gvt1, *gvt2; gvname_info *gblp1, *gblp2; gvnh_reg_t *gvnh_reg1, *gvnh_reg2; int dollardata_src, max_fid_index; lv_val *dst, *src; mval tmp_mval; sgmnt_addrs *csa; unsigned char buff1[MAX_ZWR_KEY_SZ], buff2[MAX_ZWR_KEY_SZ], *end1, *end2; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (MARG1_IS_GBL(merge_args) && MARG2_IS_GBL(merge_args)) { gblp1 = mglvnp->gblp[IND1]; gblp2 = mglvnp->gblp[IND2]; /* Check if one global name is a descent of the other. If not, we know for sure there is no issue. * If yes, further check if the database files involved in the source and target global are identical/intersect. * If either of the globals span multiple regions, we need to check if the database files that the subscripted * global reference (involved in the merge command) span across intersect in the source and destination globals. * If intersection found issue MERGEDESC error. If not (e.g. two globals have same name but belong to different * gld/db) no error needed. */ gvkey1 = gblp1->s_gv_currkey; gvkey2 = gblp2->s_gv_currkey; if (0 != memcmp(gvkey1->base, gvkey2->base, MIN(gvkey1->end, gvkey2->end))) return 1; if (gblp1->s_gd_targ_addr != gblp2->s_gd_targ_addr) { /* both globals involved in the merge correspond to different gld. * check if corresponding db files intersect. */ reg1 = gblp1->s_gv_cur_region; reg2 = gblp2->s_gv_cur_region; gvt1 = gblp1->s_gv_target; gvt2 = gblp2->s_gv_target; acc_meth1 = REG_ACC_METH(reg1); acc_meth2 = REG_ACC_METH(reg2); assert(!IS_ACC_METH_BG_OR_MM(acc_meth1) || (NULL != gvt1->gd_csa)); assert(!IS_ACC_METH_BG_OR_MM(acc_meth2) || (NULL != gvt2->gd_csa)); assert(IS_ACC_METH_BG_OR_MM(acc_meth1) || (NULL == gvt1->gd_csa)); assert(IS_ACC_METH_BG_OR_MM(acc_meth2) || (NULL == gvt2->gd_csa)); gvnh_reg1 = gblp1->s_gd_targ_gvnh_reg; gvnh_reg2 = gblp2->s_gd_targ_gvnh_reg; /* A non-NULL value of gvnh_reg indicates a spanning global as confirmed by the asserts below */ assert((NULL == gvnh_reg1) || (NULL != gvnh_reg1->gvspan)); assert((NULL == gvnh_reg2) || (NULL != gvnh_reg2->gvspan)); if ((NULL != gvnh_reg1) || (NULL != gvnh_reg2)) { /* At least one of src OR dst global spans multiple regions, check for region/db intersections * between the two. */ assert((NULL == gvnh_reg1) || (NULL != gvt1->gd_csa)); assert((NULL == gvnh_reg2) || (NULL != gvt2->gd_csa)); if ((NULL == gvt1->gd_csa) || (NULL == gvt2->gd_csa)) { /* One global spans multiple regions, while another is remote. They cannot intersect * as a global can never span to remote regions (i.e. no dba_usr or dba_cm). */ return 1; } /* The merge command is MERGE ^gvn1(subs1)=^gvn2(subs2) where "subs1" and "subs2" are * a comma-separated list of one or more subscripts. Find out regions spanned by * ^gvn1(subs1) as well as ^gvn2(subs2) and check for intersections in these two lists. */ /* Find list of regions corresponding to ^gvn1(subs1) */ base = (char *)&gvkey1->base[0]; addr = gblp1->s_gd_targ_addr; /* -1 usage in "gv_srch_map" calls below is to remove trailing 0 */ start_map1 = gv_srch_map(addr, base, gvkey1->end - 1, SKIP_BASEDB_OPEN_FALSE); GVKEY_INCREMENT_ORDER(gvkey1); end_map1 = gv_srch_map(addr, base, gvkey1->end - 1, SKIP_BASEDB_OPEN_FALSE); BACK_OFF_ONE_MAP_ENTRY_IF_EDGECASE(gvkey1->base, gvkey1->end - 1, end_map1); GVKEY_UNDO_INCREMENT_ORDER(gvkey1); /* Find list of regions corresponding to ^gvn2(subs2) */ assert(KEY_DELIMITER == gvkey2->base[gvkey2->end - 1]); assert(KEY_DELIMITER == gvkey2->base[gvkey2->end]); base = (char *)&gvkey2->base[0]; addr = gblp2->s_gd_targ_addr; start_map2 = gv_srch_map(addr, base, gvkey2->end - 1, SKIP_BASEDB_OPEN_FALSE); GVKEY_INCREMENT_ORDER(gvkey2); end_map2 = gv_srch_map(addr, base, gvkey2->end - 1, SKIP_BASEDB_OPEN_FALSE); BACK_OFF_ONE_MAP_ENTRY_IF_EDGECASE(gvkey2->base, gvkey2->end - 1, end_map2); GVKEY_UNDO_INCREMENT_ORDER(gvkey2); /* At this point, we are sure all regions involved in ^gvn1 and ^gvn2 are dba_mm or dba_bg. * This means all the regions would have a csa and a csa->fid_index assigned to them. * We can use this to determine intersections. Note though that some regions could not yet * be open so we need to open the regions first in that case and then use max_fid_index. */ for (map = start_map1; map <= end_map1; map++) { OPEN_BASEREG_IF_STATSREG(map); reg = map->reg.addr; if (!reg->open) gv_init_reg(reg, NULL); } for (map = start_map2; map <= end_map2; map++) { OPEN_BASEREG_IF_STATSREG(map); reg = map->reg.addr; if (!reg->open) gv_init_reg(reg, NULL); } /* At this point, all regions involved in the merge ^gvn1(subs1)=^gvn2(subs2) are open * so we can use max_fid_index without issues. */ max_fid_index = TREF(max_fid_index); if (max_fid_index < ARRAYSIZE(mergereg_array)) is_reg_in_array = &mergereg_array[0]; else is_reg_in_array = (boolean_t *)malloc(SIZEOF(boolean_t) * (max_fid_index + 1)); memset(is_reg_in_array, 0, SIZEOF(boolean_t) * (max_fid_index + 1)); intersect = FALSE; for (map = start_map1; map <= end_map1; map++) { reg = map->reg.addr; csa = (sgmnt_addrs *)&FILE_INFO(reg)->s_addrs; assert(NULL != csa); assert(max_fid_index >= csa->fid_index); is_reg_in_array[csa->fid_index] = TRUE; } for (map = start_map2; map <= end_map2; map++) { reg = map->reg.addr; csa = (sgmnt_addrs *)&FILE_INFO(reg)->s_addrs; assert(NULL != csa); assert(max_fid_index >= csa->fid_index); if (is_reg_in_array[csa->fid_index]) { intersect = TRUE; break; } } if (is_reg_in_array != &mergereg_array[0]) free(is_reg_in_array); if (!intersect) return 1; } else { /* Both globals map only to one region (no spanning). * if (!(both are bg/mm regions && dbs are same) && * !(both are cm regions && on the same remote node && same region) * !(both are usr regions && in the same volume set)) * NO DESCENDANTS * endif */ assert((NULL == gvt1->gd_csa) || (gvt1->gd_csa != gvt2->gd_csa) || (gvt1->root == gvt2->root)); if (!((NULL != gvt1->gd_csa) && (gvt1->gd_csa == gvt2->gd_csa)) && !((dba_cm == acc_meth1) && (dba_cm == acc_meth2) && (reg1->dyn.addr->cm_blk == reg2->dyn.addr->cm_blk) && (reg1->cmx_regnum == reg2->cmx_regnum)) VMS_ONLY (&& !((dba_usr == acc_meth1) && (dba_usr == acc_meth2) && ((ddp_info *)(&FILE_INFO(reg1)->file_id))->volset == ((ddp_info *)(&FILE_INFO(reg2)->file_id))->volset))) { UNIX_ONLY(assert((dba_usr != acc_meth1) && (dba_usr != acc_meth2));) return 1; } } } else if (gvkey1->end == gvkey2->end) return 0; /* NOOP - merge self */ /* Else glds are identical and global names are identical and one is a descendant of other. * So need to issue MERGEDESC error for sure (does not matter whether global spans regions * or not, does not matter if region is remote or not etc.). No other checks necessary. */ if (0 == (end1 = format_targ_key(buff1, MAX_ZWR_KEY_SZ, gvkey1, TRUE))) end1 = &buff1[MAX_ZWR_KEY_SZ - 1]; if (0 == (end2 = format_targ_key(buff2, MAX_ZWR_KEY_SZ, gvkey2, TRUE))) end2 = &buff2[MAX_ZWR_KEY_SZ - 1]; if (gvkey1->end > gvkey2->end) RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_MERGEDESC, 4, end1 - buff1, buff1, end2 - buff2, buff2); else RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_MERGEDESC, 4, end2 - buff2, buff2, end1 - buff1, buff1); } else if (MARG1_IS_LCL(merge_args) && MARG2_IS_LCL(merge_args)) { dst = mglvnp->lclp[IND1]; src = mglvnp->lclp[IND2]; if ((dst == src) || (NULL == src)) { /* NOOP - merge self OR empty subscripted source */ UNDO_ACTIVE_LV(actlv_merge_desc_check1); /* kill "dst" and parents as applicable if $data(dst)=0 */ return 0; } if (lcl_arg1_is_desc_of_arg2(dst, src)) { end1 = format_key_lv_val(dst, buff1, SIZEOF(buff1)); UNDO_ACTIVE_LV(actlv_merge_desc_check2); /* kill "dst" and parents as applicable if $data(dst)=0 */ op_fndata(src, &tmp_mval); dollardata_src = MV_FORCE_INTD(&tmp_mval); if (0 == dollardata_src) return 0; /* NOOP - merge x(subs)=x, but x is undefined */ end2 = format_key_lv_val(src, buff2, SIZEOF(buff2)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_MERGEDESC, 4, end1 - buff1, buff1, end2 - buff2, buff2); } if (lcl_arg1_is_desc_of_arg2(src, dst)) { /* No need of UNDO_ACTIVE_LV since src is a descendant of dst and so $data(dst) is != 0 */ end1 = format_key_lv_val(dst, buff1, SIZEOF(buff1)); end2 = format_key_lv_val(src, buff2, SIZEOF(buff2)); RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(6) ERR_MERGEDESC, 4, end2 - buff2, buff2, end1 - buff1, buff1); } if (LV_IS_BASE_VAR(src)) { /* source is unsubscripted. check if source is empty */ op_fndata(src, &tmp_mval); dollardata_src = MV_FORCE_INTD(&tmp_mval); if (0 == dollardata_src) { /* NOOP - merge with empty unsubscripted source local variable */ UNDO_ACTIVE_LV(actlv_merge_desc_check3); /* kill "dst" and parents as applicable if $data(dst)=0 */ return 0; } } } return 1; } fis-gtm-V7.0-005/sr_port/merrors.msg0000644000032200000250000046103514342376331016255 0ustar librarygtc!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! ! Copyright (c) 2001-2022 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. ! ! ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .FACILITY GTM,246/PREFIX=ERR_ .TITLE MERRORS Error Messages for GTM ! ! The code numbers for the errors that have been ! standardized by the MUMPS Development Committee are ! listed below. ! Those error messages that have a standardized ! error code have a definition that ends in !/ansi=### ! If not specified, the value for "ansi" defaults to 0. ! (Note that the first entry in this file MUST have ! an !/ansi= specification; any subsequent ones are ! optional.) ! See the end of this file for comments on the format ! ! M1, Naked indicator undefined ! M2, Invalid combination with P _fncodatom_ ! M3, $RANDOM seed less than 1 ! M4, No true condition in $SELECT ! M5, _lineref_ less than zero ! M6, Undefined _lvn_ ! M7, Undefined _gvn_ ! M8, Undefined _svn_ ! M9, Divide by zero ! M10, Invalid pattern match range ! M11, No parameters passed ! M12, Invalid _lineref_ (negative offset) ! M13, Invalid _lineref_ (label not found) ! M14, _line_ level not 1 ! M15, Undefined index variable ! M16, Argumented QUIT not allowed ! M17, Argumented QUIT required ! M18, Fixed length READ not greater than zero ! M19, Cannot copy a tree or subtree into itself ! M20, _line_ must have _formallist_ ! M21, Formal parameter occurs multiple times (original text: Algorithm specification invalid) ! M22, SET or KILL to ^$GLOBAL when data in global ! M23, SET or KILL to ^$JOB for non-existent job number ! M24, Change to collation algorithm while subscripted local variables defined ! M25, Attempt to modify currently executing routine ! M26, Non-existent _environment_ ! M27, Attempt to rollback a transaction that is not restartable ! M28, Mathematical function, parameter out of range ! M29, SET or KILL on _ssvn_ not allowed by implementation ! M30, Reference to _glvn_ with different collating sequence within a collating algorithm ! M31, _controlmnemonic_ used for device without a _mnemonicspace_ selected ! M32, _controlmnemonic_ used in user-defined _mnemonicspace_ which has no associated line ! M33, SET or KILL to ^$ROUTINE when _routine_ exists ! M34, --- currently unassigned --- ! M35, Device does not support _mnemonicspace_ ! M36, Incompatible _mnemonicspace_s ! M37, READ from device identified by the empty string ! M38, Invalid _ssvn_ subscript ! M39, Name of variable expected (original text: Invalid $NAME argument) ! M40, Call-by-reference in JOB _actual_ ! M41, Invalid LOCK argument within a TRANSACTION ! M42, Invalid QUIT within a TRANSACTION ! M43, Invalid range ($X, $Y) ! M44, Invalid _command_ outside of a TRANSACTION ! M45, Invalid GOTO reference ! M46, Invalid attribute name ! M47, Invalid attribute value (original text: Invalid attribute name) ! M48, Nonexistent window, element or choice ! M49, Invalid attempt to set focus ! M50, Attempt to reference a non M-Term window in an OPEN command ! M51, Attempt to destroy M-Term window prior to CLOSE ! M52, Required attribute missing ! M53, Invalid argument for font function ! M54, Attempt to create non-modal child of a modal parent ! M55, Invalid nested TSTART command ! M56, Name length exceeds implementation's limit ! M57, More than one defining occurrence of label in routine ! M58, Too few formal parameters ! M59, Environment reference not permitted for this _ssvn_ ! M60, Undefined _ssvn_ ! M61, Attempt to OPEN file with conflicting ACCESS parameters ! M62, Illegal value for ACCESS parameter while attempting to OPEN file ! M63, Illegal value for DISPOSITION parameter while attempting to CLOSE file ! M64, Illegal value for RENAME parameter while attempting to CLOSE file ! M65, Illegal value for VOLUME label ! M66, Illegal value for DENSITY parameter ! M67, Illegal value for ACCESS parameter ! M68, Illegal value for MOUNT parameter ! M69, Attempted tape I/O while no tape mounted ! M70, Illegal value for BLOCKSIZE parameter ! M71, Attempt to read data block larger than buffer size ! M72, Illegal value for recordsize parameter ! M73, Invalid usage of _devicekeyword_ NEWFILE ! M74, Illegal value for TRANSLATION parameter ! M75, String length exceeds implementation's limit ! M76, TCP socket state incorrect for CONNECT or LISTEN ! M77, TCP _deviceattribute_ missing ! M78, TCP _devicekeyword_ missing ! M79, TCP socket allocated to another device ! M80, Network error not otherwise specified ! M81, Unable to establish network connection ! M82, Network connection suspended: wait to resume ! M83, Network connection lost ! M84, Network protocol error: invalid client message ! M85, Network protocol error: invalid server message ! M86, Cannot relinquish device with I/O pending ! M87, Network buffer overflow ! M88, Non-existent _routine_ ! M89, Specified pattern is not a _subpattern_ ! M90, Invalid _namevalue_ ! M91, Routine source is not available ! M92, Mathematical overflow ! M93, Mathematical underflow ! M94, Attempt to compute zero to the zero-eth power ! M95, Exponentiation returns complex number with non-zero imaginary part ! M96, Attempt to assign value to already valued write-once _ssvn_ ! M97, Routine associated with user-defined _ssvn_ does not exist ! M98, Resource unavailable ! M99, Invalid operation for context ! M100, Output time-out expired ! M101, Attempt to assign incorrect value to $ECODE ! M102, Simultaneous synchronous and asynchronous event class ! M103, Invalid event identifier ! M104, IPC event identifier is not a valid job-number ! M105, Object not currently accessible ! M106, Object does not support requested method or property ! M107, Object has no default value ! M108, Value if not of data type OREF ! M109, Undefined _devicekeyword_ ! M110, Event identifier not available ! M111, Invalid number of days for date ! M112, Invalid number of seconds for time ! ! List of messages which are not reported by GT.M standard error routines(rts_error etc.) but ! by PRINTFs. NOTE: if the value of any these messages changes in future, all its non-standard ! usages should be reflected with the new value. ! ERR_GTMDISTUNDEF 150377714 (reported in gtm.c if $gtm_dist is not defined) ! ERR_DISTPATHMAX 150377682 (reported in gtm.c if $gtm_dist buffer is insufficient) ! ERR_DLLNOOPEN 150379250 (reported in gtm.c if libgtmshr cannot be opened) ! ERR_DLLNORTN 150379258 (reported in gtm.c if gtm_main is not found in libgtmshr) ! ERR_OPCOMMISSED 150381275 (sent via $SNDOPR in util_output.c if prior errors) ! ! The following items must be followed by a tab (because they are parsed by tools/library/doc_tools/routines/diffm.m) ! List of known undocumented messages follows (along with a comment) (do not change "known undocumented" phrase) ! ERR_ACK internal error (not displayed to the users) ! ERR_ASC2EBCDICCONV not yet documented since zOS is not officially supported ! ERR_DBGLDMISMATCH used internally to rectify an gld vs database characteristic mismatch in favor of the DB ! ERR_DRVLONGJMP used internally in condition handler management ! ERR_ENQ internal error (not displayed to the users) ! ERR_FAKENOSPCLEARED for internal testing only ! ERR_FREEZEID referenced only if #define DEBUG_FREEZE is TRUE. ! ERR_INVDBGLVL referenced only if "gtmdbglvl" is set to non-zero value (which is a debugging feature) ! ERR_JNLREQUIRED message referenced only in GT.CX code (not supported currently) ! ERR_JNLWRTNOWWRTR internal error (not displayed to the users) ! ERR_JOBINTRRETHROW internal error (not displayed to users - drives rethrow of jobinterrupt) ! ERR_JOBINTRRQST internal error (not displayed to users - drives jobinterrupt) ! ERR_LKSECINIT message referenced only in GT.CX code (not supported currently) ! ERR_LVMONBADVAL message used only in an undocumented feature (VIEW "LVMON...]" used for GT.M debugging ! ERR_MUDESTROYFAIL referenced only if #define IPCRM_FOR_SANCHEZ_ONLY is TRUE. ! ERR_MUDESTROYSUC referenced only if #define IPCRM_FOR_SANCHEZ_ONLY is TRUE. ! ERR_REPEATERROR internal error (not displayed to the users) ! ERR_REPLONLNRLBK not issued to user (internal use only) ! ERR_SYSTEMVALUE code is not executed (set $system won't reach op_svput at all to signal the error) ! ERR_TPRETRY internal error (not displayed to the users) ! ERR_WILLEXPIRE error triggered by the license management code which has been since disabled. ! ERR_YDIRTSZ used by a percent utility, which is not documented intentionally ! ERR_ZDEFACTIVE functionality not documented (GT.CM related weirdness) ! ERR_ZDEFOFLOW functionality not documented (GT.CM related weirdness) ! ERR_ZLINKBYPASS to be documented when code is enabled ! ----- Buffer to introduce new undocumented error messages without affecting UNUSEDMSGnnn match with corresponding line numbers. ! ! ! ! ! In addition all messages of the LMU and GTLP message facility are not documented as Licensing is out of GT.M since V4.2. ! ACK <>/success/fao=0!/ansi=0 BREAKZST /info/fao=0!/ansi=0 BADACCMTHD /info/fao=0!/ansi=0 BADJPIPARAM /error/fao=2!/ansi=0 BADSYIPARAM /error/fao=2!/ansi=0 BITMAPSBAD /error/fao=0!/ansi=0 BREAK /info/fao=0!/ansi=0 BREAKDEA /info/fao=0!/ansi=0 BREAKZBA /info/fao=0!/ansi=0 STATCNT /info/fao=5!/ansi=0 BTFAIL /error/fao=1!/ansi=0 MUPRECFLLCK /error/fao=2!/ansi=0 CMD /error/fao=0!/ansi=0 COLON /error/fao=0!/ansi=0 COMMA /error/fao=0!/ansi=0 COMMAORRPAREXP /error/fao=0!/ansi=0 COMMENT /info/fao=0!/ansi=0 CTRAP /error/fao=1!/ansi=0 CTRLC /info/fao=0!/ansi=0 CTRLY /info/fao=0!/ansi=0 DBCCERR /error/fao=2!/ansi=0 DUPTOKEN /error/fao=5!/ansi=0 DBJNLNOTMATCH /error/fao=6!/ansi=0 DBFILERR /error/fao=2!/ansi=0 DBNOTGDS /error/fao=2!/ansi=0/A DBOPNERR /error/fao=2!/ansi=0//1 DBRDERR /error/fao=2!/ansi=0//1 CCEDUMPNOW <>/fatal/fao=0!/ansi=0 DEVPARINAP /error/fao=0!/ansi=0 RECORDSTAT /info/fao=6!/ansi=0 NOTGBL /error/fao=2!/ansi=0 DEVPARPROT /error/fao=0!/ansi=0 PREMATEOF /error/fao=0!/ansi=0 GVINVALID /error/fao=2!/ansi=0 DEVPARTOOBIG /error/fao=0!/ansi=0 DEVPARUNK /error/fao=0!/ansi=0 DEVPARVALREQ /error/fao=0!/ansi=0 DEVPARMNEG /error/fao=0!/ansi=0 DSEBLKRDFAIL /error/fao=0!/ansi=0 DSEFAIL /error/fao=2!/ansi=0 NOTALLREPLON /warning/fao=2!/ansi=0 BADLKIPARAM /error/fao=2!/ansi=0 JNLREADBOF /error/fao=2!/ansi=0 DVIKEYBAD <$ZGETDVI("!AD","!AD") contains an illegal keyword>/error/fao=4!/ansi=0 ENQ <>/success/fao=0!/ansi=0 EQUAL /error/fao=0!/ansi=0 ERRORSUMMARY /error/fao=0!/ansi=0 ERRWEXC /error/fao=0!/ansi=0 ERRWIOEXC /error/fao=0!/ansi=0 ERRWZBRK /error/fao=0!/ansi=0 ERRWZTRAP /error/fao=0!/ansi=0 NUMUNXEOR /error/fao=2!/ansi=0 EXPR /error/fao=0!/ansi=0 STRUNXEOR /error/fao=2!/ansi=0 JNLEXTEND /error/fao=2!/ansi=0 FCHARMAXARGS /error/fao=0!/ansi=0 FCNSVNEXPECTED /error/fao=0!/ansi=0 FNARGINC /error/fao=2!/ansi=2 JNLACCESS /error/fao=2!/ansi=0//1 TRANSNOSTART /error/fao=0!/ansi=44 FNUMARG <$FNUMBER format specifier "!AD" contains an illegal character: "!AD">/error/fao=4!/ansi=0 FOROFLOW /error/fao=1!/ansi=0 YDIRTSZ /error/fao=1!/ansi=0 JNLSUCCESS /success/fao=2!/ansi=0 GBLNAME /error/fao=0!/ansi=29 GBLOFLOW /error/fao=2!/ansi=0 CORRUPT /error/fao=2!/ansi=0 GTMCHECK /fatal/fao=0!/ansi=0 GVDATAFAIL /error/fao=2!/ansi=0 EORNOTFND /error/fao=2!/ansi=0 GVGETFAIL /error/fao=2!/ansi=0 GVIS /info/fao=2!/ansi=0 GVKILLFAIL /error/fao=2!/ansi=0 GVNAKED /error/fao=0!/ansi=1 BACKUPDBFILE /info/fao=4!/ansi=0 GVORDERFAIL /error/fao=2!/ansi=0 GVPUTFAIL /error/fao=2!/ansi=0 PATTABSYNTAX /error/fao=3!/ansi=0 GVSUBOFLOW /error/fao=0!/ansi=0 GVUNDEF /error/fao=2!/ansi=7 TRANSNEST /error/fao=0!/ansi=0 INDEXTRACHARS /error/fao=0!/ansi=0 CORRUPTNODE /error/fao=2!/ansi=0 INDRMAXLEN /error/fao=1!/ansi=0 INSFFBCNT /error/fao=0!/ansi=0 INTEGERRS /error/fao=0!/ansi=0 INVCMD /warning/fao=0!/ansi=0 INVFCN /error/fao=0!/ansi=0 INVOBJ /error/fao=0!/ansi=0 INVSVN /error/fao=0!/ansi=8 IOEOF /error/fao=0!/ansi=0 IONOTOPEN /error/fao=0!/ansi=0 MUPIPINFO /info/fao=2!/ansi=0 IVTIME /error/fao=2!/ansi=0 JOBFAIL /error/fao=0!/ansi=0 JOBLABOFF /error/fao=0!/ansi=0 JUSTFRACT /error/fao=0!/ansi=0 KEY2BIG /error/fao=4!/ansi=0 LABELEXPECTED /error/fao=0!/ansi=0 LVORDERARG /error/fao=0!/ansi=0 MAXFORARGS /error/fao=0!/ansi=0 TRANSMINUS /error/fao=0!/ansi=0 MAXNRSUBSCRIPTS /error/fao=0!/ansi=0 MAXSTRLEN /error/fao=0!/ansi=75 ENCRYPTCONFLT2 /warning/fao=4!/ansi=0 JNLFILOPN /error/fao=4!/ansi=0 MBXRDONLY /error/fao=0!/ansi=0//1 JNLINVALID /error/fao=4!/ansi=0 MBXWRTONLY /error/fao=0!/ansi=0//1 MEMORY /fatal/fao=2!/ansi=0 DONOBLOCK /warning/fao=0!/ansi=0 ZATRANSCOL /error/fao=0!/ansi=0 VIEWREGLIST <$VIEW() only handles the first region subparameter>/warning/fao=0!/ansi=0 NUMERR /error/fao=2!/ansi=0 NUM64ERR /error/fao=2!/ansi=0 UNUM64ERR /error/fao=2!/ansi=0 HEXERR /error/fao=2!/ansi=0 HEX64ERR /error/fao=2!/ansi=0 CMDERR /error/fao=2!/ansi=0 BACKUPSUCCESS /info/fao=0!/ansi=0 JNLTMQUAL3 /error/fao=0!/ansi=0 JNLWRTDEFER /info/fao=0!/ansi=0 SELECTFALSE /error/fao=0!/ansi=4 SPOREOL /error/fao=0!/ansi=0 SRCLIN /info/fao=4!/ansi=0 SRCLOC /info/fao=4!/ansi=0 RLNKRECNFL /warning/fao=3!/ansi=0 STACKCRIT /error/fao=0!/ansi=0 STACKOFLOW /fatal/fao=0!/ansi=0 STACKUNDERFLO /error/fao=0!/ansi=0 STRINGOFLOW /error/fao=0!/ansi=0 SVNOSET /error/fao=0!/ansi=0 VIEWFN /error/fao=2!/ansi=0 TERMASTQUOTA /error/fao=0!/ansi=0 TEXTARG /error/fao=0!/ansi=5 TMPSTOREMAX /error/fao=0!/ansi=0 VIEWCMD /error/fao=2!/ansi=0 JNI /error/fao=2!/ansi=0 TXTSRCFMT <$TEXT encountered an invalid source program file format>/error/fao=0!/ansi=0 UIDMSG /error/fao=0!/ansi=0 UIDSND /error/fao=0!/ansi=0 UNDEF /error/fao=2!/ansi=6 UNIMPLOP /error/fao=0!/ansi=0 VAREXPECTED /error/fao=0!/ansi=39 BACKUPFAIL /error/fao=0!/ansi=0 MAXARGCNT /error/fao=1!/ansi=0 GTMSECSHRSEMGET /fatal/fao=1!/ansi=0 VIEWARGCNT /error/fao=2!/ansi=0 GTMSECSHRDMNSTARTED /info/fao=5!/ansi=0 ZATTACHERR /error/fao=2!/ansi=0 ZDATEFMT <$ZDATE format string contains invalid character>/error/fao=0!/ansi=0 ZEDFILSPEC /error/fao=2!/ansi=0 ZFILENMTOOLONG /error/fao=2!/ansi=75 ZFILKEYBAD /error/fao=2!/ansi=0 ZFILNMBAD /error/fao=2!/ansi=0 ZGOTOLTZERO /error/fao=0!/ansi=0 ZGOTOTOOBIG /error/fao=0!/ansi=0 ZLINKFILE /error/fao=2!/ansi=0 ZPARSETYPE /error/fao=2!/ansi=0 ZPARSFLDBAD /error/fao=2!/ansi=0 ZPIDBADARG /error/fao=0!/ansi=0 ZPRIVARGBAD /error/fao=2!/ansi=0 ZPRIVSYNTAXERR /error/fao=0!/ansi=0 ZPRTLABNOTFND
/info/fao=2!/ansi=0 MUSECNOTDEL
/info/fao=2!/ansi=0 RPARENREQD /error/fao=2!/ansi=0 ZGBLDIRACC /error/fao=6!/ansi=26//1 GVNAKEDEXTNM /error/fao=0!/ansi=0 EXTGBLDEL /error/fao=0!/ansi=0 DSEWCINITCON /info/fao=0!/ansi=0 LASTFILCMPLD /info/fao=2!/ansi=0 NOEXCNOZTRAP /info/fao=0!/ansi=0 UNSDCLASS /error/fao=0!/ansi=0 UNSDDTYPE /error/fao=0!/ansi=0 ZCUNKTYPE /error/fao=0!/ansi=0 ZCUNKMECH /error/fao=0!/ansi=0 ZCUNKQUAL /error/fao=0!/ansi=0 JNLDBTNNOMATCH /error/fao=9!/ansi=0 ZCALLTABLE /error/fao=0!/ansi=0 ZCARGMSMTCH /error/fao=2!/ansi=58 ZCCONMSMTCH /error/fao=0!/ansi=58 ZCOPT0 /error/fao=0!/ansi=0 ZCSTATUS /error/fao=0!/ansi=0 ZCUSRRTN /error/fao=0!/ansi=0 ZCPOSOVR /error/fao=1!/ansi=0 ZCINPUTREQ /error/fao=0!/ansi=0 JNLTNOUTOFSEQ /error/fao=6!/ansi=0 ACTRANGE /error/fao=1!/ansi=0 ZCCONVERT /error/fao=2!/ansi=0 ZCRTENOTF /error/fao=2!/ansi=0 GVRUNDOWN /error/fao=0!/ansi=0 LKRUNDOWN /error/fao=0!/ansi=0 IORUNDOWN /error/fao=0!/ansi=0 FILENOTFND /error/fao=2!/ansi=0 MUFILRNDWNFL /error/fao=2!/ansi=0 JNLTMQUAL1 /error/fao=0!/ansi=0 FALLINTOFLST /error/fao=0!/ansi=11 NOTEXTRINSIC /error/fao=0!/ansi=16 GTMSECSHRREMSEMFAIL /warning/fao=1!/ansi=0 FMLLSTMISSING /error/fao=2!/ansi=20 ACTLSTTOOLONG /error/fao=2!/ansi=58 ACTOFFSET /error/fao=0!/ansi=0 MAXACTARG /error/fao=0!/ansi=0 GTMSECSHRREMSEM <[client pid !UL] Semaphore (!UL) removed>/info/fao=2!/ansi=0 JNLTMQUAL2